Istio + Kubernetes Gateway API로 외부 트래픽 라우팅하기 (Gateway → HTTPRoute → Service)

dongdorrong·2025년 12월 14일

Kubernetes

목록 보기
11/13

개요

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 등) 응답을 받음

전체 구조(개념)

flowchart LR
  User[Client] -->|Host: app.example.com| LB[LoadBalancer]
  LB --> GW[Istio Gateway Envoy]
  GW --> Route[HTTPRoute]
  Route --> SVC[Service]
  SVC --> Pod[Pod]

Gateway API는 “라우팅 의도(명세)”이고, 실제 구현(컨트롤러)은 Istio가 담당합니다. 그래서 성공 시 응답 헤더에 istio-envoy가 찍히는 게 정상입니다.


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 OK
  • server: 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=ambient opt-in을 챙겨야 하고, 상태에 ResolvedWaypoints 같은 항목이 추가로 보일 수 있습니다.

다음 단계로는:

  • HTTPS(443) 리스너 구성 + 인증서(SNI) 검증
  • AuthorizationPolicy / L7 정책(ambient에서는 waypoint 전략 포함) 적용
  • 관측(메트릭/로그/트레이싱) 정리

참고

profile
DevOps 엔지니어 / 열심히 해서 잘하자

0개의 댓글