- Published on
Apache APISIX Ingress Controller — etcd 기반 동적 라우팅 딥다이브
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- 들어가며
- 인그레스 지형의 변화 — 왜 지금 APISIX인가
- APISIX의 정체 — 데이터 플레인과 컨트롤 플레인의 분리
- etcd 기반 동적 라우팅이 무중단인 이유
- 배포 모드 — 두 가지 동작 방식
- 핵심 커스텀 리소스 해부
- 플러그인 — 게이트웨이의 진짜 힘
- 표준 Ingress와 Gateway API 지원
- Helm으로 배포하기 — 실습
- 운영과 튜닝
- 자주 만나는 함정과 트러블슈팅
- 마치며
- 참고 자료
들어가며
쿠버네티스 환경에서 외부 트래픽을 클러스터 안으로 들여보내는 관문은 오랫동안 Ingress 리소스와 ingress-nginx 컨트롤러가 사실상 표준이었습니다. 하지만 2026년 현재의 지형은 꽤 달라졌습니다. 표준 Ingress API는 더 이상 새 기능이 추가되지 않는 동결(frozen) 상태이고, 후속 표준인 Gateway API가 GA를 지나 빠르게 자리를 잡았으며, ingress-nginx는 사실상 유지보수 모드로 접어들며 연이은 보안 이슈가 보고되는 흐름이 이어졌습니다.
이런 전환기에 다시 주목받는 것이 API 게이트웨이 성격을 겸한 인그레스 컨트롤러입니다. 단순히 "어느 호스트의 어느 경로를 어느 서비스로 보낼 것인가"를 넘어서, 인증·레이트리밋·카나리·관측성 같은 게이트웨이 기능을 데이터 플레인에서 직접 처리하려는 요구가 커졌기 때문입니다. Apache APISIX는 그중에서도 etcd를 설정 저장소로 쓰면서 무중단 동적 라우팅을 강점으로 내세우는 프로젝트입니다.
이 글에서는 APISIX Ingress Controller가 내부적으로 어떻게 동작하는지, etcd 기반 동적 라우팅이 왜 무중단 reload를 가능하게 하는지, ApisixRoute 같은 커스텀 리소스를 어떻게 설계하고 플러그인을 어떻게 붙이는지, 그리고 표준 Ingress와 Gateway API를 어떻게 함께 지원하는지를 코드와 다이어그램 중심으로 깊게 다룹니다. 마지막에는 Helm 배포 실습과 운영 현장에서 자주 마주치는 함정을 정리합니다. 기준 버전은 2026년 상반기 시점의 APISIX 3.x 계열과 APISIX Ingress Controller 1.8 이후 계열을 가정합니다.
인그레스 지형의 변화 — 왜 지금 APISIX인가
먼저 2026년의 인그레스/게이트웨이 지형을 정리하고 넘어가겠습니다. 이 맥락을 이해해야 APISIX가 어떤 빈자리를 노리는지 보입니다.
표준 Ingress API는 v1으로 안정화된 이후 의도적으로 동결되었습니다. 즉 호스트/경로 라우팅과 TLS 종료라는 최소 기능만 표준으로 두고, 그 이상은 컨트롤러별 어노테이션으로 풀게 한 것입니다. 이 어노테이션 난립은 컨트롤러 간 이식성을 사실상 없애 버렸고, ingress-nginx의 거대한 어노테이션 표면은 보안 취약점의 온상이 되기도 했습니다. 2025년 전후로 보고된 일련의 ingress-nginx 보안 이슈와 유지보수 부담은 많은 조직에 대안 검토를 강제했습니다.
그 대안의 표준 방향이 Gateway API입니다. Gateway API는 역할 분리(인프라 운영자 vs 앱 개발자), 표현력 있는 라우팅(헤더/메서드/쿼리 매칭, 트래픽 분할), 그리고 이식 가능한 코어 스펙을 목표로 설계된 후속 표준입니다. APISIX Ingress Controller는 이 세 가지 모드를 모두 지원합니다.
| 구분 | 표준 Ingress | APISIX 네이티브 CRD | Gateway API |
|---|---|---|---|
| 표준화 수준 | CNCF 표준(동결) | APISIX 전용 | CNCF 표준(활발) |
| 라우팅 표현력 | 호스트/경로 위주 | 매우 높음(메서드/헤더/우선순위) | 높음(필터/매칭/가중치) |
| 이식성 | 어노테이션 의존으로 낮음 | 없음(APISIX 종속) | 높음 |
| 게이트웨이 기능 | 어노테이션으로 우회 | CRD로 1급 표현 | 일부는 확장으로 |
| 권장 사용처 | 레거시 마이그레이션 | APISIX 고급 기능 풀활용 | 신규 표준 채택 |
요약하면 이렇습니다. 새로 시작한다면 Gateway API를 표준 경로로 삼되, APISIX의 풍부한 플러그인과 세밀한 라우팅 제어가 필요하면 네이티브 CRD를 함께 쓰고, 기존 Ingress 자산이 있다면 표준 Ingress 모드로 무리 없이 마이그레이션할 수 있습니다. APISIX는 이 세 갈래를 하나의 데이터 플레인에서 처리한다는 점이 핵심 매력입니다.
APISIX의 정체 — 데이터 플레인과 컨트롤 플레인의 분리
Apache APISIX는 OpenResty(Nginx + LuaJIT) 위에 구축된 클라우드 네이티브 API 게이트웨이입니다. 핵심 설계 철학은 "설정과 실행의 분리"이며, 이것이 무중단 동적 라우팅의 토대가 됩니다.
APISIX는 크게 두 부분으로 나뉩니다.
- 데이터 플레인(apisix): 실제 트래픽을 받아 라우팅·플러그인·업스트림 전달을 수행하는 OpenResty 기반 프로세스입니다.
- 설정 저장소(etcd): 라우트, 업스트림, 컨슈머, 플러그인 설정 같은 모든 구성 정보를 저장하는 키-값 저장소입니다.
쿠버네티스에서 여기에 한 층이 더 붙습니다.
- APISIX Ingress Controller: 쿠버네티스 API 서버를 감시(watch)하다가, ApisixRoute 같은 커스텀 리소스나 표준 Ingress, Gateway API 리소스가 바뀌면 그것을 APISIX의 설정으로 변환해 etcd(또는 APISIX Admin API)에 반영하는 컨트롤러입니다.
전체 구조를 ASCII로 그리면 다음과 같습니다.
+---------------------------------------------------------------+
| 쿠버네티스 클러스터 |
| |
| +-------------------+ watch +----------------+ |
| | kube-apiserver | <------------------ | APISIX Ingress | |
| | - ApisixRoute | | Controller | |
| | - Ingress | 변환 후 동기화 -> | (컨트롤 플레인) | |
| | - Gateway API | +-------+--------+ |
| +-------------------+ | |
| | Admin |
| | API |
| v |
| +---------------+ |
| 외부 트래픽 | etcd | |
| | | (설정 저장소) | |
| v +-------+-------+ |
| +---------+ 라우트/업스트림 watch | |
| | APISIX | <--------------------------------+ |
| | (데이터 | |
| | 플레인)| --> 백엔드 Service / Pod (업스트림) |
| +---------+ |
+---------------------------------------------------------------+
여기서 결정적인 부분은 데이터 플레인 apisix가 etcd를 직접 watch한다는 점입니다. etcd의 watch 메커니즘 덕분에, 설정이 바뀌면 apisix 워커가 그 변경을 거의 실시간으로 메모리에 반영합니다. 새 라우트가 추가되거나 업스트림 가중치가 바뀌어도 Nginx 프로세스를 리로드하거나 재시작할 필요가 없습니다. 이것이 "무중단 동적 라우팅"의 정체입니다.
etcd 기반 동적 라우팅이 무중단인 이유
전통적인 Nginx 기반 게이트웨이에서 라우팅을 바꾼다는 것은 보통 nginx.conf를 다시 쓰고 reload 신호를 보내는 일을 의미했습니다. reload는 마스터 프로세스가 새 설정을 읽어 새 워커를 띄우고 기존 워커를 우아하게 종료시키는 과정인데, 빈번한 reload는 다음과 같은 비용을 낳습니다.
- 워커 교체 과정에서 일시적으로 메모리/커넥션이 두 배가 될 수 있습니다.
- 롱리빙 커넥션(웹소켓, gRPC 스트림)이 끊기거나 드레이닝 지연이 발생합니다.
- 초당 수십 건씩 라우트가 바뀌는 환경에서는 reload 자체가 병목이 됩니다.
APISIX는 이 모델을 근본적으로 바꿉니다. 라우트 매칭 테이블, 업스트림 목록, 플러그인 설정이 nginx.conf가 아니라 etcd에 저장되고, apisix 워커는 이를 watch 하면서 LuaJIT 런타임 안의 공유 메모리(radixtree 기반 라우터)에 적재합니다. 설정이 바뀌면 그 메모리 구조만 갱신되므로 프로세스 재시작이 일어나지 않습니다.
흐름을 단계로 그리면 다음과 같습니다.
[개발자] kubectl apply -f apisixroute.yaml
|
v
[kube-apiserver] ApisixRoute 객체 저장 + watch 이벤트 발행
|
v
[APISIX Ingress Controller]
- ApisixRoute를 APISIX Route/Upstream 스키마로 변환
- 검증(서비스 존재, 포트, 플러그인 스키마)
- Admin API 또는 etcd로 PUT
|
v
[etcd] /apisix/routes/<id> 키 갱신 -> watch 이벤트
|
v
[apisix 데이터 플레인 워커]
- watch 이벤트 수신
- radixtree 라우터 메모리 갱신 (reload 없음)
|
v
[다음 요청부터 새 라우팅 적용 — 기존 커넥션 영향 없음]
이 구조의 실무적 이점은 분명합니다. 카나리 배포에서 업스트림 가중치를 5초마다 바꿔도 데이터 플레인은 흔들리지 않고, 수천 개의 라우트가 동시에 존재해도 radixtree 기반 매칭은 경로 길이에 거의 비례하는 상수에 가까운 시간으로 동작합니다. 또한 컨트롤 플레인(Ingress Controller)이 잠시 죽어도 데이터 플레인 apisix는 etcd에 이미 적재된 설정으로 계속 트래픽을 처리합니다. 컨트롤 플레인과 데이터 플레인이 장애 격리(failure isolation)된다는 점은 게이트웨이의 가용성 측면에서 매우 중요합니다.
다만 etcd가 설정의 단일 진실 공급원(source of truth)이 되므로, etcd 자체의 가용성과 백업이 운영의 핵심 관심사가 됩니다. 이 부분은 뒤의 운영 섹션에서 다시 다루겠습니다.
배포 모드 — 두 가지 동작 방식
APISIX Ingress Controller는 크게 두 가지 배포 모드를 제공합니다. 어떤 모드를 쓰느냐에 따라 etcd가 필요한지, 컨트롤러가 무엇을 동기화하는지가 달라집니다.
| 모드 | 설명 | etcd 필요 | 특징 |
|---|---|---|---|
| 표준(default) | 컨트롤러가 APISIX Admin API를 통해 설정을 푸시, APISIX는 etcd를 백엔드로 사용 | 필요 | 가장 일반적, 풀 기능 |
| etcd 직결(또는 컨트롤러 내장 동기화) | 컨트롤러가 변환한 설정을 직접 저장소에 동기화 | 모드에 따라 다름 | 단순화된 토폴로지 시도 |
2026년 시점의 권장 구성은 데이터 플레인 apisix + etcd를 함께 두고, Ingress Controller가 Admin API로 설정을 밀어 넣는 표준 모드입니다. 이 글의 실습도 이 구성을 기준으로 합니다. APISIX 진영에서 컨트롤러와 데이터 플레인의 결합 방식이 버전에 따라 진화해 왔으므로, 실제 도입 시에는 설치하려는 차트 버전의 문서에서 권장 토폴로지를 반드시 확인해야 합니다.
핵심 커스텀 리소스 해부
APISIX의 네이티브 기능을 온전히 쓰려면 표준 Ingress 대신 APISIX의 커스텀 리소스를 사용합니다. 가장 중요한 세 가지는 ApisixRoute, ApisixUpstream, ApisixPluginConfig입니다.
ApisixRoute — 라우팅의 중심
ApisixRoute는 호스트/경로/메서드 매칭과 백엔드 연결, 그리고 라우트 단위 플러그인까지 한 곳에서 표현합니다. 기본형은 다음과 같습니다.
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: orders-route
namespace: shop
spec:
http:
- name: orders
match:
hosts:
- api.example.com
paths:
- /orders/*
methods:
- GET
- POST
backends:
- serviceName: orders-svc
servicePort: 8080
plugins:
- name: limit-count
enable: true
config:
count: 100
time_window: 60
rejected_code: 429
key_type: var
key: remote_addr
여기서 주목할 점은 match 블록의 표현력입니다. 표준 Ingress가 호스트와 경로만 다룬다면, ApisixRoute는 HTTP 메서드, 우선순위(priority), 그리고 nginxVars를 통한 헤더/쿠키/쿼리 기반 매칭까지 1급 시민으로 지원합니다. 다음은 헤더 기반 라우팅 예시입니다.
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: beta-route
namespace: shop
spec:
http:
- name: beta-users
priority: 10
match:
hosts:
- api.example.com
paths:
- /orders/*
exprs:
- subject:
scope: Header
name: X-Channel
op: Equal
value: beta
backends:
- serviceName: orders-beta-svc
servicePort: 8080
priority 값이 큰 라우트가 먼저 평가되므로, 위 베타 라우트는 X-Channel 헤더가 beta인 요청을 우선 가로채고, 나머지는 기본 라우트로 흘려보냅니다. 이런 표현은 표준 Ingress로는 깔끔하게 만들기 어렵습니다.
ApisixUpstream — 백엔드 세밀 제어
ApisixUpstream은 특정 쿠버네티스 Service에 대한 업스트림 동작을 세밀하게 설정합니다. 로드밸런싱 알고리즘, 헬스체크, 재시도, 타임아웃, 패시브 헬스체크 등을 여기서 다룹니다.
apiVersion: apisix.apache.org/v2
kind: ApisixUpstream
metadata:
name: orders-svc
namespace: shop
spec:
loadbalancer:
type: ewma
retries: 2
timeout:
connect: 3s
read: 10s
send: 10s
healthCheck:
active:
type: http
httpPath: /healthz
healthy:
interval: 5
successes: 2
unhealthy:
interval: 5
httpFailures: 3
passive:
healthy:
successes: 3
unhealthy:
httpFailures: 3
tcpFailures: 3
metadata.name이 대상 Service 이름과 같아야 한다는 점이 핵심 규칙입니다. APISIX Ingress Controller는 이 매칭을 통해 해당 Service로 향하는 업스트림에 위 설정을 적용합니다. 로드밸런서 타입으로는 라운드로빈, 일관 해싱(chash), 그리고 응답 시간 가중 이동 평균인 ewma 등을 선택할 수 있어, 지연이 들쭉날쭉한 백엔드에 대해 ewma로 자동 회피를 노리는 식의 튜닝이 가능합니다.
ApisixPluginConfig — 플러그인의 재사용
여러 라우트에 같은 플러그인 묶음을 반복해서 붙이는 대신, ApisixPluginConfig로 플러그인 세트를 정의해 두고 라우트에서 참조할 수 있습니다.
apiVersion: apisix.apache.org/v2
kind: ApisixPluginConfig
metadata:
name: common-gateway-plugins
namespace: shop
spec:
plugins:
- name: cors
enable: true
config:
allow_origins: "https://app.example.com"
allow_methods: "GET,POST,PUT,DELETE"
allow_headers: "Authorization,Content-Type"
- name: prometheus
enable: true
config:
prefer_name: true
라우트에서는 다음처럼 참조합니다.
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: orders-route
namespace: shop
spec:
http:
- name: orders
match:
hosts: ["api.example.com"]
paths: ["/orders/*"]
backends:
- serviceName: orders-svc
servicePort: 8080
plugin_config_name: common-gateway-plugins
이렇게 하면 CORS와 prometheus 설정을 한 곳에서 관리하면서 수십 개의 라우트에 일관되게 적용할 수 있습니다. 이 밖에도 ApisixConsumer(컨슈머/인증 주체), ApisixTls(인증서), ApisixClusterConfig(전역 설정) 같은 리소스가 있어 게이트웨이의 거의 모든 측면을 선언적으로 다룰 수 있습니다.
플러그인 — 게이트웨이의 진짜 힘
APISIX의 차별점은 풍부한 플러그인 생태계입니다. 플러그인은 요청 처리 파이프라인의 각 단계(rewrite, access, header_filter, body_filter, log)에 후크로 끼어들며, etcd 설정 변경만으로 동적으로 켜고 끌 수 있습니다. 자주 쓰는 플러그인을 카테고리로 묶으면 다음과 같습니다.
| 카테고리 | 대표 플러그인 | 용도 |
|---|---|---|
| 인증 | key-auth, jwt-auth, basic-auth, openid-connect, hmac-auth | API 키, JWT, OIDC 기반 인증 |
| 트래픽 제어 | limit-count, limit-req, limit-conn, traffic-split | 레이트리밋, 동시성 제한, 카나리 분할 |
| 보안 | ip-restriction, cors, csrf, ua-restriction | 접근 제어, 교차 출처 정책 |
| 관측성 | prometheus, opentelemetry, zipkin, http-logger | 메트릭, 분산 추적, 로그 전송 |
| 변환 | proxy-rewrite, response-rewrite, grpc-transcode | 요청/응답 재작성, gRPC-JSON 변환 |
인증 플러그인 예시 — key-auth
API 키 인증을 붙이려면 먼저 컨슈머를 정의하고, 라우트에 key-auth를 켭니다.
apiVersion: apisix.apache.org/v2
kind: ApisixConsumer
metadata:
name: partner-acme
namespace: shop
spec:
authParameter:
keyAuth:
value:
key: acme-secret-key-value
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: partner-api
namespace: shop
spec:
http:
- name: partner
match:
hosts: ["api.example.com"]
paths: ["/partner/*"]
backends:
- serviceName: partner-svc
servicePort: 8080
plugins:
- name: key-auth
enable: true
이제 클라이언트는 apikey 헤더에 발급받은 키를 실어 보내야만 통과합니다. 실무에서는 키 값을 평문으로 두지 않고 시크릿 참조 또는 외부 비밀 관리와 연동하는 패턴을 권장합니다.
트래픽 분할 예시 — 카나리 배포
traffic-split 플러그인으로 가중치 기반 카나리를 선언적으로 구성할 수 있습니다.
apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
name: orders-canary
namespace: shop
spec:
http:
- name: orders
match:
hosts: ["api.example.com"]
paths: ["/orders/*"]
backends:
- serviceName: orders-stable
servicePort: 8080
weight: 90
- serviceName: orders-canary
servicePort: 8080
weight: 10
backends에 가중치를 주면 APISIX가 90 대 10으로 트래픽을 분배합니다. 앞서 강조한 동적 라우팅 덕분에, 이 가중치를 90/10에서 50/50으로 바꿔 apply 해도 데이터 플레인은 reload 없이 즉시 새 비율을 반영합니다. 카나리를 점진적으로 끌어올리는 자동화 파이프라인과 궁합이 좋습니다.
관측성 — prometheus와 opentelemetry
게이트웨이는 트래픽의 길목이므로 관측성의 황금 지점입니다. prometheus 플러그인을 켜면 요청 수, 지연 분포, 상태 코드별 카운트 같은 메트릭이 노출됩니다.
apiVersion: apisix.apache.org/v2
kind: ApisixClusterConfig
metadata:
name: default
spec:
monitoring:
prometheus:
enable: true
skywalking:
enable: false
분산 추적이 필요하면 opentelemetry 플러그인으로 트레이스를 OTLP 컬렉터로 보낼 수 있습니다. 게이트웨이에서 trace를 시작해 백엔드로 전파하면, 한 요청이 게이트웨이를 거쳐 여러 마이크로서비스를 통과하는 전 구간을 하나의 트레이스로 묶어 볼 수 있습니다.
표준 Ingress와 Gateway API 지원
APISIX Ingress Controller의 큰 장점은 네이티브 CRD에 갇히지 않는다는 점입니다. 같은 데이터 플레인을 표준 Ingress와 Gateway API로도 운전할 수 있습니다.
표준 Ingress 모드
기존 Ingress 자산을 그대로 가져오려면 ingressClassName을 apisix로 지정합니다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: legacy-orders
namespace: shop
annotations:
k8s.apisix.apache.org/enable-cors: "true"
spec:
ingressClassName: apisix
rules:
- host: api.example.com
http:
paths:
- path: /orders
pathType: Prefix
backend:
service:
name: orders-svc
port:
number: 8080
표준 Ingress 모드의 한계는 명확합니다. APISIX의 풍부한 기능은 어노테이션으로만 노출되고, 그 어노테이션 키는 APISIX 전용이라 이식성이 없습니다. 따라서 표준 Ingress 모드는 "기존 자산을 무리 없이 옮겨 놓고, 점진적으로 네이티브 CRD나 Gateway API로 이전"하는 마이그레이션 경로로 보는 것이 적절합니다.
Gateway API 모드
신규 표준을 채택한다면 Gateway API를 사용합니다. 인프라 운영자가 Gateway를 정의하고, 앱 팀이 HTTPRoute를 붙이는 역할 분리가 자연스럽게 표현됩니다.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: apisix-gateway
namespace: infra
spec:
gatewayClassName: apisix
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: All
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: orders-route
namespace: shop
spec:
parentRefs:
- name: apisix-gateway
namespace: infra
hostnames:
- api.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /orders
backendRefs:
- name: orders-svc
port: 8080
weight: 100
Gateway API는 트래픽 분할, 헤더 매칭, 필터 같은 표현을 표준 스펙으로 제공하므로 컨트롤러 간 이식성이 좋습니다. 2026년의 권장 방향은 신규 워크로드는 Gateway API를 기본으로 삼고, APISIX 고유 플러그인이 꼭 필요한 라우트에 한해 ApisixPluginConfig 등을 통해 보강하는 하이브리드 전략입니다.
Helm으로 배포하기 — 실습
이제 실제로 배포해 보겠습니다. APISIX, etcd, Ingress Controller를 함께 설치하는 가장 간단한 경로는 공식 Helm 차트입니다.
먼저 차트 저장소를 추가합니다.
helm repo add apisix https://charts.apiseven.com
helm repo update
kubectl create namespace apisix
다음은 운영용 values 예시입니다. 핵심은 etcd를 HA로 띄우고, Ingress Controller를 함께 활성화하며, Admin 키와 Prometheus 노출을 설정하는 부분입니다.
apisix:
replicaCount: 3
resources:
requests:
cpu: 500m
memory: 512Mi
limits:
cpu: "1"
memory: 1Gi
etcd:
enabled: true
replicaCount: 3
persistence:
enabled: true
size: 10Gi
ingress-controller:
enabled: true
config:
apisix:
serviceNamespace: apisix
resources:
requests:
cpu: 200m
memory: 256Mi
service:
type: LoadBalancer
metrics:
serviceMonitor:
enabled: true
설치 명령은 다음과 같습니다.
helm install apisix apisix/apisix \
--namespace apisix \
-f values-prod.yaml
설치 후 핵심 컴포넌트가 모두 떴는지 확인합니다.
kubectl -n apisix get pods
kubectl -n apisix get svc
kubectl -n apisix get crd | grep apisix
CRD 목록에 apisixroutes, apisixupstreams, apisixpluginconfigs 등이 보이면 컨트롤러가 정상 설치된 것입니다. 이제 앞서 다룬 ApisixRoute를 apply 하고, LoadBalancer 서비스의 외부 IP로 요청을 보내 라우팅을 검증할 수 있습니다.
EXTERNAL_IP=$(kubectl -n apisix get svc apisix-gateway -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl -H "Host: api.example.com" "http://$EXTERNAL_IP/orders/123"
운영과 튜닝
배포가 끝났다고 운영이 끝난 것은 아닙니다. APISIX를 프로덕션에서 안정적으로 돌리려면 몇 가지 핵심을 챙겨야 합니다.
etcd가 핵심 의존성이다
앞서 강조했듯 etcd는 설정의 단일 진실 공급원입니다. 따라서 etcd의 가용성이 곧 게이트웨이 설정 변경 가능성을 좌우합니다. 다음 원칙을 권장합니다.
- etcd는 항상 홀수 노드(3 또는 5)로 구성해 쿼럼을 확보합니다.
- etcd 디스크는 빠른 SSD를 쓰고 fsync 지연을 모니터링합니다. etcd는 디스크 지연에 매우 민감합니다.
- 정기 스냅샷 백업을 자동화하고, 복구 절차를 실제로 리허설합니다.
- 데이터 플레인이 잠시 etcd와 단절되어도 마지막으로 받은 설정으로 트래픽을 계속 처리한다는 점은 위안이지만, 이 상태에서 새 설정은 반영되지 않으므로 단절을 빠르게 알아채야 합니다.
리소스와 워커 튜닝
apisix는 OpenResty 기반이므로 워커 프로세스 수와 공유 메모리 크기가 성능에 직결됩니다. 노드 CPU 수에 맞춰 워커를 설정하고, 라우트/플러그인 수가 많다면 공유 딕셔너리 크기를 넉넉히 잡습니다. CPU 요청/제한을 너무 좁게 잡으면 throttle로 인해 지연이 튀므로, 부하 테스트로 적정 값을 찾는 것이 좋습니다.
메트릭으로 보는 건강 신호
prometheus 플러그인이 노출하는 메트릭 중 운영에서 특히 중요한 것들은 다음과 같습니다.
| 메트릭 성격 | 봐야 하는 이유 |
|---|---|
| 요청 지연 분포 | 게이트웨이 자체 오버헤드와 백엔드 지연을 분리해 본다 |
| 상태 코드별 카운트 | 4xx 급증은 인증/레이트리밋, 5xx 급증은 업스트림 장애 신호 |
| 업스트림 헬스 상태 | 헬스체크가 백엔드를 빼고 있는지 추적 |
| etcd 연결 상태 | 설정 동기화가 끊겼는지 조기 감지 |
무중단 변경의 실전 가치
운영을 하다 보면 라우트를 자주 바꾸게 됩니다. 신규 서비스 추가, 카나리 비율 조정, 레이트리밋 임계값 튜닝, 긴급 IP 차단 등은 모두 etcd 설정 변경만으로 즉시 반영됩니다. reload가 없으니 변경 자체가 가벼운 작업이 되고, 이는 운영 민첩성으로 이어집니다. 특히 보안 사고 대응에서 특정 출처를 ip-restriction으로 즉시 막는 것 같은 조치를 무중단으로 할 수 있다는 점은 실전에서 큰 차이를 만듭니다.
자주 만나는 함정과 트러블슈팅
마지막으로 현장에서 자주 부딪히는 문제와 해결 방향을 정리합니다.
라우트를 apply 했는데 404가 난다
가장 흔한 증상입니다. 점검 순서는 다음과 같습니다.
- ingressClassName 또는 gatewayClassName이 apisix로 정확히 지정됐는지 확인합니다. 클래스가 맞지 않으면 컨트롤러가 해당 리소스를 무시합니다.
- 컨트롤러 로그를 봅니다. 변환 과정에서 스키마 검증에 실패하면 컨트롤러가 etcd에 반영하지 않습니다.
- 대상 Service와 포트가 실제로 존재하고 엔드포인트가 채워졌는지 확인합니다.
kubectl -n apisix logs deploy/apisix-ingress-controller
kubectl -n shop get endpoints orders-svc
kubectl -n shop describe apisixroute orders-route
설정이 etcd에 반영되지 않는다
컨트롤러는 살아 있는데 변경이 데이터 플레인까지 도달하지 않는 경우입니다. Admin API 인증 키 불일치, 컨트롤러와 apisix 간 네트워크 정책 차단, etcd 쿼럼 상실이 단골 원인입니다. 컨트롤러가 Admin API 호출에 실패한 로그가 있는지부터 확인합니다.
플러그인 설정 스키마 오류
플러그인 config는 플러그인마다 스키마가 엄격합니다. 잘못된 필드나 타입을 넣으면 그 라우트 전체가 거부될 수 있습니다. 의심되면 작은 라우트에 플러그인 하나만 붙여 격리 테스트를 하고, 컨트롤러 로그의 검증 에러 메시지를 그대로 따라가면 빠르게 원인을 좁힐 수 있습니다.
Host 헤더 없이 테스트해 라우팅이 안 맞는다
hosts 매칭이 있는 라우트는 Host 헤더가 정확히 맞아야 합니다. curl로 외부 IP를 때릴 때 Host 헤더를 빠뜨리면 라우트가 매칭되지 않아 404가 납니다. 위 실습의 curl 예시처럼 Host 헤더를 반드시 지정해 검증합니다.
etcd 디스크 지연으로 인한 설정 반영 지연
etcd는 디스크 fsync에 민감해서, 느린 디스크나 과부하 상태에서는 설정 PUT의 커밋이 지연되고 결과적으로 데이터 플레인 반영도 늦어집니다. etcd 노드를 별도의 빠른 디스크에 배치하고, etcd 자체 메트릭(디스크 커밋 지연, 리더 변경 빈도)을 항상 모니터링하는 것이 예방책입니다.
마치며
Apache APISIX Ingress Controller의 핵심은 결국 두 가지로 요약됩니다. 첫째, 설정을 etcd에 두고 데이터 플레인이 이를 watch 함으로써 reload 없는 무중단 동적 라우팅을 구현한다는 점. 둘째, 컨트롤 플레인과 데이터 플레인을 분리해 장애를 격리하면서, 표준 Ingress·네이티브 CRD·Gateway API라는 세 가지 모드를 하나의 데이터 플레인으로 운전한다는 점입니다.
2026년의 인그레스 지형에서 표준 Ingress는 동결되고 Gateway API가 후속 표준으로 자리 잡았으며 ingress-nginx는 유지보수 부담이 커진 흐름을 고려하면, 풍부한 플러그인과 동적 라우팅을 한 번에 제공하면서 Gateway API까지 지원하는 APISIX는 게이트웨이를 새로 설계하는 팀에게 충분히 매력적인 선택지입니다. 다만 etcd라는 추가 의존성과 그에 따른 운영 책임을 분명히 인지하고, 처음부터 etcd HA와 백업, 그리고 관측성을 함께 설계하는 것이 성공적인 도입의 전제 조건입니다.
새로 시작한다면 Gateway API를 표준 골격으로 삼고, APISIX의 플러그인이 필요한 지점만 네이티브 리소스로 보강하는 하이브리드 전략을 권합니다. 기존 Ingress 자산이 있다면 표준 Ingress 모드로 옮겨 놓은 뒤 점진적으로 이전하는 경로가 현실적입니다.
참고 자료
- Apache APISIX 공식 사이트: https://apisix.apache.org
- APISIX Ingress Controller 시작하기: https://apisix.apache.org/docs/ingress-controller/getting-started/
- APISIX 플러그인 허브: https://apisix.apache.org/docs/apisix/plugins/limit-count/
- APISIX Ingress Controller GitHub: https://github.com/apache/apisix-ingress-controller
- 쿠버네티스 Ingress 개념 문서: https://kubernetes.io/docs/concepts/services-networking/ingress/
- Gateway API 공식 문서: https://gateway-api.sigs.k8s.io/
- etcd 공식 문서: https://etcd.io/docs/
- cert-manager 공식 문서: https://cert-manager.io/docs/
- APISIX Helm 차트 저장소: https://github.com/apache/apisix-helm-chart