LabHub
开始
学习 学习路径 课程

构建 EAI 中间层

用 nginx 搭建网关 — 密钥、配额、版本

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

nginx 로 API 게이트웨이의 원리를 만든다 — 경로 프록시, API 키 인증(401), 소비자별 쿼터(429), 버전 라우팅과 Sunset, 요청 ID 전파와 로그, 업스트림 타임아웃(504).

왜 중요한가

인증·호출량 제한·버전은 업무와 무관하게 모든 API 에 똑같이 필요하다. API 서버마다 따로 구현하면 조금씩 달라지고, 한 소비자의 폭주가 모두를 느리게 만들고, 누가 옛 버전을 쓰는지 아무도 모르게 된다. 게이트웨이는 이것을 업무 로직 없이 한 곳에서 통제한다.

단계

  1. 업스트림 두 개를 띄운다: nohup python3 /opt/lab/fixtures/eaimw/gateway/api.py --port 9601 --version v1 > /root/eaimw/gw/v1.out 2>&1 &, … --port 9602 --version v2 …. 둘을 curl 로 한 번씩 불러 응답 JSON 두 줄을 /root/eaimw/gw/up.txt 에 저장한다.
  2. /root/eaimw/gw/nginx.conf 를 쓴다(완전한 설정 파일: pid /root/eaimw/gw/nginx.pid;, 로그는 /root/eaimw/gw/logs/, listen 8090;, 업스트림 127.0.0.1:9601·127.0.0.1:9602). /v1/ 은 v1 으로, /v2/ 는 v2 로 경로 그대로 넘긴다. nginx -p /root/eaimw/gw -c /root/eaimw/gw/nginx.conf 로 띄운다.
  3. API 키 인증: /root/eaimw/gw/keys.mapkey-channel-7f3a channel;·key-partner-19c2 partner; 두 줄을 두고 map $http_x_api_key $consumer 로 include 한다. 소비자가 없으면(키 없음·틀린 키) 401 과 JSON 본문을 돌려준다. 업스트림에는 X-Consumer: <소비자> 를 넘기고 X-API-Key 는 넘기지 않는다.
  4. 쿼터: limit_req_zone $consumer … rate=5r/s, limit_req … burst=5 nodelay, limit_req_status 429. 한 소비자가 몰아 보내면 429, 다른 소비자는 영향이 없어야 한다.
  5. 버전: /api/X-API-Version: 2 면 v2, 없으면 v1 으로 넘긴다(map $http_x_api_version). v1 으로 가는 응답(/v1/·/api/ 의 v1)에는 Sunset: Thu, 31 Dec 2026 23:59:59 GMT 헤더를 붙이고 v2 에는 붙이지 않는다.
  6. 요청 ID: 들어온 X-Request-ID 가 있으면 그대로, 없으면 $request_id 를 업스트림에 X-Request-ID 로 넘긴다. 접근 로그 형식을 '<요청ID> <소비자> <상태> "<요청줄>"'(예: log_format gw '$rid $consumer $status "$request"';)로 /root/eaimw/gw/logs/access.log 에 남긴다.
  7. proxy_read_timeout 2s 로, 업스트림이 느리면(/v1/slow 는 5초) 2초 안팎에 504 를 돌려준다.

참고

업스트림 두 버전을 띄운다

api.py 로 v1(9601)·v2(9602)를 띄우고 응답 JSON 두 줄을 /root/eaimw/gw/up.txt 에 저장한다.

api.py 는 받은 요청을 JSON 으로 비춰 줍니다. curl -s 로 두 포트를 부르고 >> 로 한 파일에 모읍니다.

경로로 버전을 나눠 넘긴다

/root/eaimw/gw/nginx.conf 로 8090 에서 /v1/→9601, /v2/→9602 를 경로 그대로 넘긴다.

upstream 블록 두 개와 location 두 개입니다. proxy_pass 에 URI 부분을 붙이지 않으면 요청 경로가 그대로 넘어갑니다.

API 키로 소비자를 식별한다

/root/eaimw/gw/keys.map 을 include 한 map 으로 소비자를 정하고, 없으면 401 JSON, 있으면 X-Consumer 만 넘기고 키는 떼어 낸다.

map $http_x_api_key $consumer { default ""; include …; } 뒤 server 에서 if ($consumer = "") { return 401 '…'; }. 헤더를 떼려면 proxy_set_header 에 빈 값을 줍니다.

소비자마다 쿼터를 건다

limit_req_zone $consumer rate=5r/s, burst=5 nodelay, limit_req_status 429 로 소비자별 쿼터를 건다.

zone 의 열쇠가 무엇이냐가 전부입니다. 한도 초과의 기본 상태코드는 503 이라 429 로 바꿔야 호출자가 '내가 많이 보냈다' 를 압니다.

헤더로 버전을 고르고, 내릴 버전을 알린다

/api/ 는 X-API-Version: 2 면 v2, 없으면 v1 으로. v1 응답에는 Sunset 헤더를 붙인다.

map $http_x_api_version $api_ver 로 업스트림 이름을 고르고 proxy_pass http://$api_ver; 합니다. Sunset 도 map 으로 v1 일 때만 값을 주면, 빈 값인 add_header 는 붙지 않습니다.

요청 ID 를 잇고 로그에 남긴다

X-Request-ID 가 있으면 그대로, 없으면 $request_id 를 넘기고, '<요청ID> <소비자> <상태> "<요청줄>"' 형식으로 접근 로그를 남긴다.

map $http_x_request_id $rid { "" $request_id; default $http_x_request_id; } 처럼 map 값에 변수를 쓸 수 있습니다. log_format 을 정의하고 access_log 에 그 이름을 붙이세요.

느린 업스트림은 빨리 끊는다

proxy_read_timeout 2s 로 /v1/slow(5초)를 2초 안팎에 504 로 끊는다.

기본값은 60초입니다. 호출자가 먼저 포기하는 API 라면 게이트웨이가 더 오래 붙들 이유가 없습니다. location 마다 공통으로 넣으세요.