Kubernetes에서 외부 트래픽을 받는 방법은 오랫동안 Ingress 중심이었지만, 지금은 Gateway API가 사실상 표준으로 자리 잡고 있습니다. Istio도 Gateway API 컨트롤러로 동작할 수 있어서, Gateway/HTTPRoute만으로 “도메인 → 라우팅 → 서비스” 흐름을 구성할 수 있습니다.
이 글은 “Istio 기반 환경에서 Gateway API로 애플리케이션이 정상 통신하는지”를 재현 가능한 체크리스트로 정리한 것입니다. (Sidecar / Ambient 모두 동일한 구조로 검증 가능)
HTTPRoute 상태가 Accepted=True, ResolvedRefs=True (Ambient면 ResolvedWaypoints=True가 같이 보일 수 있음)server: istio-envoy가 보임(= Istio Gateway를 통과)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가 찍히는 게 정상입니다.
type=LoadBalancer를 만들 수 있는 환경(클라우드 LB, 온프레 MetalLB 등)아래 예시는 AWS 환경(NLB)에서 자주 부딪히는 포인트를 함께 적었습니다. 다른 환경은 LoadBalancer/방화벽 파트만 환경에 맞게 바꾸면 됩니다.
Gateway API는 CRD가 먼저 필요합니다. (운영이면 버전을 고정하는 것을 권장합니다.)
standard-install.yaml 적용 흐름을 따르거나,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는 “사이드카 대신 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
운영에서는 보통 팀 공용 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
Gateway에서 allowedRoutes를 selector로 제한했으니, 해당 네임스페이스에 라벨을 붙여줘야 합니다.
kubectl create ns demo
kubectl label ns demo shared-gateway-access=true --overwrite
Ambient는 네임스페이스 opt-in이 필요합니다.
kubectl label ns demo istio.io/dataplane-mode=ambient --overwrite
“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
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
kubectl -n demo get httproute demo -o wide
kubectl -n demo describe httproute demo
여기서 Accepted=True, ResolvedRefs=True(Ambient면 ResolvedWaypoints=True도)까지 확인되면, “라우팅 선언이 유효하게 붙었다”고 볼 수 있습니다.
보통 아래 둘 중 하나로 확인합니다.
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가 잠깐 나올 수 있습니다. 몇 분 대기 후 재시도하면 해결되는 경우가 많습니다.
export LB_DNS="<위에서 확인한 값>"
curl -i -H "Host: app.example.com" "http://${LB_DNS}/"
정상이라면:
HTTP/1.1 200 OKserver: istio-envoy위 curl -H "Host: ..." 방식은 DNS 없이도 Host 라우팅이 되는지 확인하기 좋습니다.
운영에서 app.example.com 같은 실제 도메인으로 접속하려면, DNS에 아래처럼 연결합니다.
CNAME이 가장 무난A 레코드예시(CNAME):
app.example.com. CNAME <LB_DNS>
HTTPRoute Accepted=False가장 흔한 원인은 allowedRoutes 조건 불일치입니다.
shared-gateway-access=truekubectl -n demo get httproute demo -o yaml에서 reason/message 확인curl이 timeoutAWS(NLB) 조합에서 자주 나오는 이슈입니다.
type=LoadBalancer가 노드의 NodePort(30000-32767) 로 트래픽을 전달하는 구성일 수 있음Could not resolve hostistio.io/dataplane-mode=ambient opt-in을 챙겨야 하고, 상태에 ResolvedWaypoints 같은 항목이 추가로 보일 수 있습니다.다음 단계로는: