개요
Kubernetes에서 외부 트래픽을 받는 방법은 오랫동안 Ingress 중심이었지만, 지금은 Gateway API가 사실상 표준으로 자리 잡고 있습니다. Istio도 Gateway API 컨트롤러로 동작할 수 있어서, Gateway/HTTPRoute만으로 “도메인 → 라우팅 → 서비스” 흐름을 구성할 수 있습니다.
이 글은 “Istio 기반 환경에서 Gateway API로 애플리케이션이 정상 통신하는지”를 재현 가능한 체크리스트로 정리한 것입니다. (Sidecar / Ambient 모두 동일한 구조로 검증 가능)
목표(성공 조건)
HTTPRoute상태가Accepted=True,ResolvedRefs=True(Ambient면ResolvedWaypoints=True가 같이 보일 수 있음)- 외부에서 LoadBalancer 주소로 접속했을 때 응답 헤더에
server: istio-envoy가 보임(= Istio Gateway를 통과) - 최종 백엔드(샘플 nginx 등) 응답을 받음
전체 구조(개념)
Gateway API는 “라우팅 의도(명세)”이고, 실제 구현(컨트롤러)은 Istio가 담당합니다. 그래서 성공 시 응답 헤더에 istio-envoy가 찍히는 게 정상입니다.
그림 관계를 글로 읽기
- Gateway(gateway, istio-system)가 gatewayClassName: istio로 GatewayClass istio를 참조한다.
- Namespace demo의 라벨 shared-gateway-access=true가 Gateway allowedRoutes selector와 맞아야 route attach가 허용된다.
- HTTPRoute demo가 parentRefs(gateway, istio-system, sectionName: http)로 Gateway에 연결된다.
- HTTPRoute가 hostnames app.example.com, PathPrefix /, backendRefs demo:80을 정한다.
- Gateway API는 라우팅 의도이고, Istio가 이 설정을 Istio Gateway Envoy로 구현한다.
- 요청: Client가 Host: app.example.com으로 LoadBalancer에 보낸다.
- LoadBalancer에서 Istio Gateway Envoy로 간다.
- Envoy가 설정된 backend인 Service demo:80으로 보낸다.
- Service가 app: demo 라벨로 Pod(nginx)를 선택한다.
1) 준비물
- Kubernetes 클러스터
- Istio 설치(Sidecar 또는 Ambient)
- Gateway API CRD 설치
type=LoadBalancer를 만들 수 있는 환경(클라우드 LB, 온프레 MetalLB 등)- (도메인 테스트 시) DNS 설정 권한(Route53 등 어떤 DNS라도 무방)
아래 예시는 AWS 환경(NLB)에서 자주 부딪히는 포인트를 함께 적었습니다. 다른 환경은 LoadBalancer/방화벽 파트만 환경에 맞게 바꾸면 됩니다.
2) Gateway API CRD 설치
Gateway API는 CRD가 먼저 필요합니다. (운영이면 버전을 고정하는 것을 권장합니다.)
- 설치 방법은 “Gateway API 공식 문서”의
standard-install.yaml적용 흐름을 따르거나, - 이미 설치되어 있다면(예: 관리형 애드온/플랫폼 기본 제공) 이 단계는 건너뛰어도 됩니다.
3) Istio 설치 (Sidecar / Ambient)
Sidecar 모드(일반적인 구성)
helm repo add istio https://istio-release.storage.googleapis.com/charts
helm repo update
helm upgrade --install istio-base istio/base -n istio-system --create-namespace
helm upgrade --install istiod istio/istiod -n istio-system
Ambient 모드(추가 컴포넌트 필요)
Ambient는 “사이드카 대신 ztunnel/waypoint 중심”이라 설치 구성요소가 더 많습니다.
helm repo add istio https://istio-release.storage.googleapis.com/charts
helm repo update
helm upgrade --install istio-base istio/base -n istio-system --create-namespace
helm upgrade --install istiod istio/istiod -n istio-system --set profile=ambient
helm upgrade --install istio-cni istio/cni -n istio-system --set profile=ambient
helm upgrade --install ztunnel istio/ztunnel -n istio-system
4) (공유) Gateway 만들기
운영에서는 보통 팀 공용 Gateway를 하나 두고, 각 서비스는 HTTPRoute만 추가하는 패턴이 관리가 편합니다.
아래는 예시입니다.
*.example.com에 대해 리스너(http)를 열고allowedRoutes를 namespace label selector로 제한합니다.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: gateway
namespace: istio-system
annotations:
# (AWS 예시) internet-facing
service.beta.kubernetes.io/aws-load-balancer-scheme: internet-facing
spec:
gatewayClassName: istio
listeners:
- name: http
hostname: "*.example.com"
port: 80
protocol: HTTP
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
shared-gateway-access: "true"
생성 후 아래처럼 “Istio가 실제로 Gateway API를 컨트롤하고 있는지”를 먼저 확인해 두면 디버깅이 빨라집니다.
kubectl get gatewayclass
kubectl -n istio-system get gateway gateway -o wide
5) 애플리케이션 네임스페이스/워크로드 준비
공통(라우트 attach 허용 라벨)
Gateway에서 allowedRoutes를 selector로 제한했으니, 해당 네임스페이스에 라벨을 붙여 줘야 합니다.
kubectl create ns demo
kubectl label ns demo shared-gateway-access=true --overwrite
Ambient에서 추가(ambient opt-in)
Ambient는 네임스페이스 opt-in이 필요합니다.
kubectl label ns demo istio.io/dataplane-mode=ambient --overwrite
6) 샘플 애플리케이션(nginx) 배포
“Gateway → HTTPRoute → Service”를 확인하는 게 목표이므로, 백엔드는 최대한 단순하게 둡니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: demo
namespace: demo
spec:
replicas: 1
selector:
matchLabels:
app: demo
template:
metadata:
labels:
app: demo
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: demo
namespace: demo
spec:
selector:
app: demo
ports:
- name: http
port: 80
targetPort: 80
kubectl apply -f demo.yaml
kubectl -n demo rollout status deploy/demo
7) HTTPRoute로 서비스 연결
HTTPRoute는 “어떤 Host/Path 요청을 어디 서비스로 보낼지”를 정의합니다.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: demo
namespace: demo
spec:
parentRefs:
- name: gateway
namespace: istio-system
sectionName: http
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: demo
port: 80
8) DNS/접속 테스트
8-1) HTTPRoute 상태 확인
kubectl -n demo get httproute demo -o wide
kubectl -n demo describe httproute demo
여기서 Accepted=True, ResolvedRefs=True(Ambient면 ResolvedWaypoints=True도)까지 확인되면, “라우팅 선언이 유효하게 붙었다”고 볼 수 있습니다.
8-2) Gateway가 가진 주소(LoadBalancer DNS) 확인
보통 아래 둘 중 하나로 확인합니다.
kubectl -n istio-system get gateway gateway -o jsonpath='{.status.addresses[0].value}{"\n"}'
또는 (Istio가 만든) LoadBalancer Service에서:
kubectl -n istio-system get svc gateway-istio -o jsonpath='{.status.loadBalancer.ingress[0].hostname}{"\n"}'
NLB가 막 생성된 직후에는 DNS 전파가 덜 되어
Could not resolve host가 잠깐 나올 수 있습니다. 몇 분 대기 후 재시도하면 해결되는 경우가 많습니다.
8-3) curl로 “Host 기반 라우팅” 검증
export LB_DNS="<위에서 확인한 값>"
curl -i -H "Host: app.example.com" "http://${LB_DNS}/"
정상이라면:
HTTP/1.1 200 OKserver: istio-envoy- (샘플 nginx면) welcome page
8-4) (선택) 실제 도메인으로 접속하기
위 curl -H "Host: ..." 방식은 DNS 없이도 Host 라우팅이 되는지 확인하기 좋습니다.
운영에서 app.example.com 같은 실제 도메인으로 접속하려면, DNS에 아래처럼 연결합니다.
- LoadBalancer가 “DNS 이름”을 주는 경우: 보통
CNAME이 가장 무난 - LoadBalancer가 “IP”를 주는 경우:
A레코드
예시(CNAME):
app.example.com. CNAME <LB_DNS>
9) 트러블슈팅 체크리스트
A. HTTPRoute Accepted=False
가장 흔한 원인은 allowedRoutes 조건 불일치입니다.
- namespace 라벨 확인:
shared-gateway-access=true kubectl -n demo get httproute demo -o yaml에서reason/message확인
B. curl이 timeout
AWS(NLB) 조합에서 자주 나오는 이슈입니다.
type=LoadBalancer가 노드의 NodePort(30000-32767) 로 트래픽을 전달하는 구성일 수 있음- NLB는 클라이언트 Source IP를 보존하는 경우가 많아서, 워커 노드 방화벽/보안그룹에서 NodePort 인바운드를 막고 있으면 timeout이 납니다.
- 운영에서는 0.0.0.0/0가 아니라 실제 허용 CIDR로 제한하는 게 맞습니다.
C. Could not resolve host
- NLB 프로비저닝 직후 DNS 전파 지연
- 로컬/사내 DNS 환경 문제
정리
- Gateway API를 쓰면 “Ingress보다 구조가 명확한” 방식으로 인그레스 라우팅을 구성할 수 있습니다.
- Sidecar/Ambient 모두에서 Gateway(API) → HTTPRoute → Service → Pod 흐름 자체는 크게 다르지 않습니다.
- Ambient는 추가로
istio.io/dataplane-mode=ambientopt-in을 챙겨야 하고, 상태에ResolvedWaypoints같은 항목이 추가로 보일 수 있습니다.
다음 단계로는:
- HTTPS(443) 리스너 구성 + 인증서(SNI) 검증
- AuthorizationPolicy / L7 정책(ambient에서는 waypoint 전략 포함) 적용
- 관측(메트릭/로그/트레이싱) 정리