본문으로 건너뛰기

마이그레이션 실행 전략

8분 읽기
AI용 문서 안내
정보

이 문서는 Gateway API 도입 가이드의 심화 가이드입니다. NGINX Ingress에서 Gateway API로의 실전 마이그레이션 전략을 제공합니다.

1. 사전 요구사항: CRD 설치​

Gateway API CRD 버전은 선택한 컨트롤러의 지원 버전과 맞춰야 합니다. 아래 공통 예시는 연결된 Cilium 1.19.3 가이드의 v1.4.1 기준입니다. Standard/Experimental 번들을 서로 다른 릴리스로 덮어쓰지 마세요. 다른 컨트롤러나 Cilium 가이드 9.4절의 추론 Gateway에는 각자의 호환성 기준을 적용해야 합니다.

1.1 Gateway API 표준 CRDs​

# Cilium 1.19.3 기준 Standard CRD
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/standard-install.yaml

# Experimental 리소스가 필요하면 동일 버전의 번들을 대신 선택
# kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.4.1/experimental-install.yaml

설치되는 CRDs:

  • gatewayclasses.gateway.networking.k8s.io
  • gateways.gateway.networking.k8s.io
  • httproutes.gateway.networking.k8s.io
  • referencegrants.gateway.networking.k8s.io
  • grpcroutes.gateway.networking.k8s.io (Standard)
  • tcproutes.gateway.networking.k8s.io (Experimental)
  • tlsroutes.gateway.networking.k8s.io (Experimental)
  • udproutes.gateway.networking.k8s.io (Experimental)

1.2 각 컨트롤러별 추가 설치​

아래는 컨트롤러별 설치 구성 템플릿입니다. 모든 조합을 v1.4.1에서 검증했다는 의미는 아닙니다. AWS LBC v3.0.0의 공식 가이드는 Gateway API v1.3.0을 기준으로 하며 추가 LBC Gateway CRD도 필요합니다. NGINX/Envoy 역시 선택한 릴리스의 설치·호환성 문서에 맞춰 CRD와 차트 버전을 함께 선택하세요. 서로 다른 컨트롤러 설치 명령을 순서대로 모두 실행하지 않습니다. AWS Native (ALB + NLB Gateway)

# AWS Load Balancer Controller v3.0+ 설치 (Gateway API 지원)
helm repo add eks https://aws.github.io/eks-charts
helm repo update

# IRSA (IAM Role for Service Account) 생성
eksctl create iamserviceaccount \
--cluster=<클러스터명> \
--namespace=kube-system \
--name=aws-load-balancer-controller \
--role-name AmazonEKSLoadBalancerControllerRole \
--attach-policy-arn=arn:aws:iam::<account-id>:policy/AWSLoadBalancerControllerIAMPolicy \
--approve

# 공식 IAM 정책을 계정에 먼저 생성하고 호환 차트 버전을 지정
# https://kubernetes-sigs.github.io/aws-load-balancer-controller/latest/deploy/installation/
: "${LBC_CHART_VERSION:?Set a chart version compatible with the chosen Gateway API CRDs}"
# Helm 설치
helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
--version "$LBC_CHART_VERSION" -n kube-system \
--set clusterName=<클러스터명> \
--set serviceAccount.create=false \
--set serviceAccount.name=aws-load-balancer-controller \
--set controllerConfig.featureGates.ALBGatewayAPI=true \
--set controllerConfig.featureGates.NLBGatewayAPI=true

# 설치 확인
kubectl get deployment -n kube-system aws-load-balancer-controller

NGINX Gateway Fabric

# NGINX Gateway Fabric 설치
kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.6.0/crds.yaml
kubectl apply -f https://github.com/nginxinc/nginx-gateway-fabric/releases/download/v1.6.0/nginx-gateway.yaml

# 설치 확인
kubectl get pods -n nginx-gateway
kubectl get gatewayclass nginx

Envoy Gateway

# Envoy Gateway 설치
helm install eg oci://docker.io/envoyproxy/gateway-helm \
--version v1.3.0 \
--namespace envoy-gateway-system \
--create-namespace

# 설치 확인
kubectl get pods -n envoy-gateway-system
kubectl get gatewayclass envoy-gateway

Cilium Gateway API

Cilium 설치 시 gatewayAPI.enabled=true로 이미 활성화되어 있으므로 별도 설치 불필요.

# GatewayClass 확인
kubectl get gatewayclass cilium

2. 5-Phase 마이그레이션 프로세스​

글씨가 작으면 확대해서 확인할 수 있습니다.
다이어그램

3. Phase별 상세 가이드​

Step 1.1: 현재 Ingress 인벤토리 수집

# 모든 Ingress 리소스 목록 추출
kubectl get ingress -A -o json > ingress-inventory.json

# 주요 정보 요약
cat ingress-inventory.json | jq -r '
.items[] |
{
namespace: .metadata.namespace,
name: .metadata.name,
class: .spec.ingressClassName,
hosts: [.spec.rules[].host],
paths: [.spec.rules[].http.paths[].path],
tls: (.spec.tls != null)
}
' > ingress-summary.json

# 통계 요약
echo "=== Ingress Statistics ==="
echo "Total Ingress: $(cat ingress-inventory.json | jq '.items | length')"
echo "With TLS: $(cat ingress-inventory.json | jq '[.items[] | select(.spec.tls != null)] | length')"
echo "Unique Hosts: $(cat ingress-inventory.json | jq -r '[.items[].spec.rules[].host] | unique | length')"

Step 1.2: 기능 매핑 (NGINX Ingress → Gateway API)

🔄 NGINX Ingress → Gateway API 마이그레이션 매핑
기존 Ingress 기능의 Gateway API 대안
host: example.com→HTTPRoute.spec.hostnames
직접 매핑직접 매핑
path: /api→HTTPRoute.spec.rules[].matches[].path
직접 매핑직접 매핑
pathType: Prefix→path.type: PathPrefix
동일동일
annotations: rewrite-target→filters[].type: URLRewrite
표준화됨표준화됨
annotations: rate-limit→Policy Attachment (구현체별 상이)
진행 중표준화 진행 중
annotations: cors-*→Policy Attachment
진행 중표준화 진행 중
annotations: auth-*→Policy Attachment 또는 외부 인증
외부OAuth2-Proxy 등 권장
annotations: ssl-redirect→Gateway TLS 리스너 자동 처리
자동화됨자동화됨

Step 1.3: 리스크 평가

# risk-assessment.yaml
risks:
- id: RISK-001
category: 기능 누락
description: "NGINX rate-limit 어노테이션의 직접 대안 없음"
severity: MEDIUM
mitigation: "AWS WAF 또는 Envoy Rate Limit 서비스 사용"

- id: RISK-002
category: 다운타임
description: "Cilium ENI 모드 마이그레이션 시 다운타임 발생"
severity: HIGH
mitigation: "블루-그린 클러스터 전환 또는 유지보수 창 설정"

- id: RISK-003
category: 학습 곡선
description: "팀의 Gateway API 경험 부족"
severity: LOW
mitigation: "Phase 2 PoC에서 충분한 테스트 기간 확보"

4. 검증 스크립트​

이 스크립트는 selector 기반 Service 백엔드의 HTTP/HTTPS 경로에 대한 제한된 점검입니다. Bash, jq, kubectl, curl이 필요합니다. 테스트 호스트·경로는 선택한 리스너와 Route에 맞게 명시하며, 헤더·메서드·인증 등의 추가 조건은 별도 테스트하세요. 성공 응답만으로 모든 트래픽이나 백엔드 정체성을 검증한 것은 아닙니다.

#!/bin/bash
# validate-httproute.sh: selector-backed Service routes, explicit Gateway/listener and probe.
set -euo pipefail
if [ "$#" -ne 7 ]; then
echo "Usage: $0 <route-namespace> <route-name> <gateway-namespace> <gateway-name> <listener> <probe-host> <probe-path>" >&2
exit 1
fi
NS=$1; ROUTE_NAME=$2; GW_NS=$3; GW_NAME=$4; LISTENER=$5; HOST=$6; PROBE_PATH=$7
[[ "$HOST" != *'*'* && "$PROBE_PATH" == /* ]] || { echo "Use a concrete hostname and absolute path" >&2; exit 1; }
ROUTE=$(kubectl get httproute "$ROUTE_NAME" -n "$NS" -o json)
GW=$(kubectl get gateway "$GW_NAME" -n "$GW_NS" -o json)
CLASS=$(jq -r '.spec.gatewayClassName' <<<"$GW")
CONTROLLER=$(kubectl get gatewayclass "$CLASS" -o jsonpath='{.spec.controllerName}')

# Gateway readiness is distinct from the HTTPRoute parent status; reject stale conditions.
jq -e '.metadata.generation as $g | .status.conditions as $c |
all(["Accepted", "Programmed"][]; . as $t |
any($c[]?; .type == $t and .status == "True" and .observedGeneration == $g))' <<<"$GW" >/dev/null
LISTENER_JSON=$(jq -ce --arg l "$LISTENER" '.spec.listeners[] | select(.name == $l)' <<<"$GW")
PORT=$(jq -r '.port' <<<"$LISTENER_JSON")
PROTOCOL=$(jq -r '.protocol' <<<"$LISTENER_JSON")
case "$PROTOCOL" in HTTP) SCHEME=http ;; HTTPS) SCHEME=https ;; *) echo "Only HTTP/HTTPS probes are supported" >&2; exit 1 ;; esac

# Match the intended parent and its controller, including namespace, sectionName and port.
jq -e --arg ns "$NS" --arg gn "$GW_NAME" --arg gns "$GW_NS" --arg l "$LISTENER" \
--arg c "$CONTROLLER" --argjson port "$PORT" '
def canonical: {group:(.group // "gateway.networking.k8s.io"), kind:(.kind // "Gateway"),
namespace:(.namespace // $ns), name, sectionName:(.sectionName // null), port:(.port // null)};
.metadata.generation as $g |
[.spec.parentRefs[] | canonical | select(.group == "gateway.networking.k8s.io" and .kind == "Gateway"
and .name == $gn and .namespace == $gns and (.sectionName == null or .sectionName == $l)
and (.port == null or .port == $port))] as $refs |
any(.status.parents[]?; . as $p | (.parentRef | canonical) as $ref |
.controllerName == $c and any($refs[]; . == $ref) and
all(["Accepted", "ResolvedRefs"][]; . as $t |
any($p.conditions[]?; .type == $t and .status == "True" and .observedGeneration == $g)))' <<<"$ROUTE" >/dev/null

# This focused check supports ordinary Service backends, including cross-namespace references.
jq -e '[.spec.rules[]?.backendRefs[]?] as $b | ($b | length) > 0 and
all($b[]; (.group // "") == "" and (.kind // "Service") == "Service")' <<<"$ROUTE" >/dev/null
BACKENDS=$(jq -r --arg ns "$NS" '[.spec.rules[]?.backendRefs[]? |
[(.namespace // $ns), .name]] | unique[] | @tsv' <<<"$ROUTE")
while IFS=$'\t' read -r BACKEND_NS SERVICE; do
kubectl get service "$SERVICE" -n "$BACKEND_NS" -o json >/dev/null
kubectl get endpointslices -n "$BACKEND_NS" -l "kubernetes.io/service-name=$SERVICE" -o json |
jq -e 'any(.items[]?.endpoints[]?; .conditions.ready != false and (.addresses | length) > 0)' >/dev/null
done <<<"$BACKENDS"

ADDRESS=$(jq -er '.status.addresses[0].value | select(length > 0)' <<<"$GW")
[[ "$ADDRESS" == *:* ]] && ADDRESS="[$ADDRESS]"
# --connect-to changes the destination while retaining Host, TLS SNI and certificate checks.
HTTP_CODE=$(curl --silent --show-error --output /dev/null --write-out '%{http_code}' \
--connect-to "${HOST}:${PORT}:${ADDRESS}:${PORT}" \
--max-time 10 "${SCHEME}://${HOST}:${PORT}${PROBE_PATH}")
[[ "$HTTP_CODE" =~ ^[23][0-9][0-9]$ ]] || { echo "Probe failed: HTTP $HTTP_CODE" >&2; exit 1; }
echo "Selected parent accepted/resolved; Gateway programmed; ready backends; probe HTTP $HTTP_CODE"

사용 예시:

chmod +x validate-httproute.sh
./validate-httproute.sh production api-route infra production-gateway https api.example.com /api/v1/health

5. 문제 해결​

5.1 일반적인 이슈 및 해결 방법​

일반적인 문제와 해결 방법
Gateway API 트러블슈팅 가이드
증상원인해결
HTTPRoute Accepted=False선택한 Gateway/리스너가 Route 연결을 수락하지 않음1. parentRefs와 리스너 확인 2. hostname 교집합 확인 3. allowedRoutes 네임스페이스 정책 확인
HTTPRoute ResolvedRefs=False해당 부모에 대한 백엔드 참조를 해석할 수 없음1. Service 이름·포트·kind 확인 2. 다른 네임스페이스 참조의 ReferenceGrant 확인 3. 조건의 reason/message 확인
Gateway Programmed=FalseGateway 데이터플레인 구성이 준비되지 않음1. Gateway 및 리스너 조건 확인 2. 컨트롤러 로그와 LoadBalancer 상태 확인 3. TLS 리스너의 ResolvedRefs와 인증서 Secret 확인
503 Service Unavailable백엔드 엔드포인트 없음1. Service의 Endpoints 확인 2. Pod selector 일치 여부 확인 3. Pod 상태 확인 (Ready)
TLS 인증서 오류Secret이 올바르지 않음1. Secret 타입 kubernetes.io/tls 확인 2. tls.crt, tls.key 존재 확인 3. 인증서 유효기간 확인
404 Not Found경로 매칭 실패1. PathPrefix vs Exact 타입 확인 2. 대소문자 구분 여부 확인 3. URL 인코딩 확인
Gateway 주소 없음LoadBalancer 생성 실패1. 클라우드 제공자 쿼터 확인 2. 서브넷 IP 고갈 여부 확인 3. 어노테이션 오타 확인

5.2 컨트롤러별 디버깅 명령어​

AWS Load Balancer Controller

# 컨트롤러 로그 확인
kubectl logs -n kube-system deployment/aws-load-balancer-controller --tail=100 -f

# Gateway의 실제 NLB 확인
kubectl get gateway <name> -n <namespace> -o jsonpath='{.metadata.annotations.service\.beta\.kubernetes\.io/aws-load-balancer-name}'

# NLB의 Target Group 상태 확인
aws elbv2 describe-target-health --target-group-arn <arn>

# HTTPRoute 이벤트 확인
kubectl describe httproute <name> -n <namespace>

Cilium Gateway API

# Cilium Operator 로그
kubectl logs -n kube-system deployment/cilium-operator --tail=100 -f

# Envoy 구성 덤프
kubectl exec -n kube-system ds/cilium -- cilium envoy config dump > envoy-config.json

# HTTPRoute 라우팅 테이블 확인
kubectl exec -n kube-system ds/cilium -- cilium service list

# 플로우 모니터링 (Gateway 관련)
hubble observe --protocol http --port 443

# Gateway 상태 확인
cilium status --wait

NGINX Gateway Fabric

# NGINX Gateway 로그
kubectl logs -n nginx-gateway deployment/nginx-gateway --tail=100 -f

# NGINX 구성 확인
kubectl exec -n nginx-gateway deployment/nginx-gateway -- nginx -T

# HTTPRoute 매핑 확인
kubectl describe httproute <name> -n <namespace>

# 접근 로그 실시간 확인
kubectl logs -n nginx-gateway deployment/nginx-gateway -f | grep "HTTP/1.1"

Envoy Gateway

# Envoy Gateway 컨트롤러 로그
kubectl logs -n envoy-gateway-system deployment/envoy-gateway --tail=100 -f

# Envoy Proxy 로그 (데이터플레인)
kubectl logs -n envoy-gateway-system deployment/envoy-<gateway-name> --tail=100 -f

# Envoy 관리 인터페이스 포트 포워딩
kubectl port-forward -n envoy-gateway-system deployment/envoy-<gateway-name> 19000:19000

# 브라우저에서 http://localhost:19000 접속하여 stats, config 확인

# xDS 구성 덤프
curl http://localhost:19000/config_dump > envoy-xds-config.json

공통 디버깅

# Gateway 상태 상세 확인
kubectl get gateway <name> -n <namespace> -o yaml

# HTTPRoute 상태 상세 확인
kubectl get httproute <name> -n <namespace> -o yaml

# 백엔드 Service 엔드포인트 확인
kubectl get endpoints <service-name> -n <namespace>

# Pod가 Ready 상태인지 확인
kubectl get pods -n <namespace> -l <selector>

# 네트워크 정책 확인 (트래픽 차단 여부)
kubectl get networkpolicies -n <namespace>

# 이벤트 확인 (최근 10분)
kubectl get events -n <namespace> --sort-by='.lastTimestamp' | tail -20

관련 문서​