K8s HTTPRoute 트래픽 라우팅

K8s Gateway API HTTPRoute란?

Kubernetes Gateway API는 Ingress의 후속 표준으로, HTTPRoute는 HTTP 트래픽 라우팅 규칙을 정의하는 핵심 리소스입니다. Ingress에서 어노테이션으로 처리하던 트래픽 분할, 헤더 기반 라우팅, URL 재작성 등을 표준 스펙으로 선언적 관리할 수 있습니다. Gateway API 1.0이 2023년 GA되었으며, Istio, Envoy, Cilium, NGINX 등 주요 구현체가 지원합니다.

Gateway + HTTPRoute 기본 구조

# 1. GatewayClass: 구현체 선택 (클러스터 관리자)
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: istio
spec:
  controllerName: istio.io/gateway-controller

---
# 2. Gateway: 리스너 정의 (인프라 팀)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: api-gateway
  namespace: infra
spec:
  gatewayClassName: istio
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: api-tls-cert
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            gateway-access: "true"  # 허용된 네임스페이스만
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: Same               # 같은 네임스페이스만

---
# 3. HTTPRoute: 라우팅 규칙 (개발팀)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-routes
  namespace: production
spec:
  parentRefs:
  - name: api-gateway
    namespace: infra
    sectionName: https           # 특정 리스너에 바인딩
  hostnames:
  - "api.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api/v1
    backendRefs:
    - name: api-v1-service
      port: 8080

경로 매칭: PathPrefix · Exact · RegularExpression

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: path-routing
  namespace: production
spec:
  parentRefs:
  - name: api-gateway
    namespace: infra
  hostnames:
  - "api.example.com"
  rules:
  # Exact: 정확한 경로 매칭
  - matches:
    - path:
        type: Exact
        value: /healthz
    backendRefs:
    - name: health-service
      port: 8080

  # PathPrefix: 접두어 매칭
  - matches:
    - path:
        type: PathPrefix
        value: /api/v2/orders
    backendRefs:
    - name: order-service-v2
      port: 8080

  - matches:
    - path:
        type: PathPrefix
        value: /api/v2/users
    backendRefs:
    - name: user-service
      port: 8080

  # RegularExpression: 정규식 매칭 (구현체 지원 필요)
  - matches:
    - path:
        type: RegularExpression
        value: "/api/v[0-9]+/products"
    backendRefs:
    - name: product-service
      port: 8080

헤더·쿼리 기반 라우팅

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: header-routing
  namespace: production
spec:
  parentRefs:
  - name: api-gateway
    namespace: infra
  hostnames:
  - "api.example.com"
  rules:
  # 헤더 기반 라우팅: 모바일 전용 백엔드
  - matches:
    - headers:
      - name: X-Client-Type
        value: mobile
      path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: mobile-api-service
      port: 8080

  # 헤더 기반: A/B 테스트 그룹
  - matches:
    - headers:
      - name: X-Experiment-Group
        value: beta
        type: Exact
      path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: beta-api-service
      port: 8080

  # 쿼리 파라미터 기반 (구현체 지원 필요)
  - matches:
    - queryParams:
      - name: version
        value: canary
        type: Exact
      path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: canary-service
      port: 8080

  # 기본 라우트 (매칭 없으면 여기로)
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: stable-api-service
      port: 8080

트래픽 분할: 카나리 배포

HTTPRoute의 backendRefs.weight로 트래픽을 비율로 분할합니다:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-deploy
  namespace: production
spec:
  parentRefs:
  - name: api-gateway
    namespace: infra
  hostnames:
  - "api.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    # 90% → 안정 버전
    - name: api-service-stable
      port: 8080
      weight: 90
    # 10% → 카나리 버전
    - name: api-service-canary
      port: 8080
      weight: 10
# 점진적 카나리 롤아웃 스크립트
#!/bin/bash
# 10% → 30% → 50% → 100% 단계적 전환

for WEIGHT in 10 30 50 100; do
  STABLE=$((100 - WEIGHT))
  
  kubectl patch httproute canary-deploy -n production --type=merge -p "
spec:
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: api-service-stable
      port: 8080
      weight: ${STABLE}
    - name: api-service-canary
      port: 8080
      weight: ${WEIGHT}
"
  
  echo "Canary weight: ${WEIGHT}%, Stable: ${STABLE}%"
  echo "Monitoring error rate for 5 minutes..."
  sleep 300
  
  # 에러율 확인 (Prometheus)
  ERROR_RATE=$(curl -s "http://prometheus:9090/api/v1/query?query=rate(http_errors_total{service='canary'}[5m])" | jq '.data.result[0].value[1]')
  
  if (( $(echo "$ERROR_RATE > 0.05" | bc -l) )); then
    echo "Error rate too high (${ERROR_RATE}), rolling back!"
    kubectl patch httproute canary-deploy -n production --type=merge -p '
spec:
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: api-service-stable
      port: 8080
      weight: 100
    - name: api-service-canary
      port: 8080
      weight: 0
'
    exit 1
  fi
done

echo "Canary promotion complete!"

URL 재작성과 리다이렉트

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: url-rewrite
  namespace: production
spec:
  parentRefs:
  - name: api-gateway
    namespace: infra
  hostnames:
  - "api.example.com"
  rules:
  # URL 재작성: /api/v1/* → 백엔드에서는 /* 로 수신
  - matches:
    - path:
        type: PathPrefix
        value: /api/v1
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
    backendRefs:
    - name: api-v1-service
      port: 8080

  # 호스트명 재작성
  - matches:
    - path:
        type: PathPrefix
        value: /legacy
    filters:
    - type: URLRewrite
      urlRewrite:
        hostname: legacy-internal.example.com
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
    backendRefs:
    - name: legacy-service
      port: 8080

---
# HTTP → HTTPS 리다이렉트
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: https-redirect
  namespace: production
spec:
  parentRefs:
  - name: api-gateway
    namespace: infra
    sectionName: http              # HTTP 리스너에 바인딩
  hostnames:
  - "api.example.com"
  rules:
  - filters:
    - type: RequestRedirect
      requestRedirect:
        scheme: https
        statusCode: 301

요청·응답 헤더 수정

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: header-modify
  namespace: production
spec:
  parentRefs:
  - name: api-gateway
    namespace: infra
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    filters:
    # 요청 헤더 수정 (백엔드로 전달)
    - type: RequestHeaderModifier
      requestHeaderModifier:
        set:
        - name: X-Forwarded-Proto
          value: https
        add:
        - name: X-Request-Start
          value: "t=${msec}"
        remove:
        - X-Debug-Internal

    # 응답 헤더 수정 (클라이언트로 반환)
    - type: ResponseHeaderModifier
      responseHeaderModifier:
        set:
        - name: X-Content-Type-Options
          value: nosniff
        - name: Strict-Transport-Security
          value: "max-age=31536000; includeSubDomains"
        add:
        - name: X-Served-By
          value: gateway-api
        remove:
        - Server
        - X-Powered-By

    backendRefs:
    - name: api-service
      port: 8080

멀티 호스트·멀티 네임스페이스

# 여러 도메인을 하나의 Gateway에서 처리
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: multi-host
  namespace: production
spec:
  parentRefs:
  - name: api-gateway
    namespace: infra
  hostnames:
  - "api.example.com"
  - "api.example.co.kr"           # 다국어 도메인
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: api-service
      port: 8080

---
# 다른 네임스페이스의 서비스 참조 (ReferenceGrant 필요)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: cross-ns-route
  namespace: frontend
spec:
  parentRefs:
  - name: api-gateway
    namespace: infra
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    backendRefs:
    - name: api-service
      namespace: backend          # 다른 네임스페이스
      port: 8080

---
# ReferenceGrant: 크로스 네임스페이스 참조 허용
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-frontend-to-backend
  namespace: backend              # 참조 대상 네임스페이스
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: frontend           # 참조하는 네임스페이스
  to:
  - group: ""
    kind: Service

HTTPRoute 상태 확인과 디버깅

# HTTPRoute 상태 확인
kubectl get httproute -A
kubectl describe httproute api-routes -n production

# 상태 조건 확인
kubectl get httproute api-routes -n production -o jsonpath='{.status.parents[*].conditions}' | jq .
# [
#   { "type": "Accepted", "status": "True" },
#   { "type": "ResolvedRefs", "status": "True" }
# ]

# Gateway 상태
kubectl get gateway api-gateway -n infra -o jsonpath='{.status.conditions}' | jq .

# 일반적인 에러:
# Accepted=False → parentRef가 잘못되었거나 네임스페이스 허용 안 됨
# ResolvedRefs=False → backendRef 서비스가 존재하지 않음
# BackendNotFound → 서비스 또는 포트 확인

# 이벤트 확인
kubectl get events -n production --field-selector involvedObject.kind=HTTPRoute

Ingress에서 Gateway API로 마이그레이션

Ingress Gateway API
IngressClass GatewayClass
Ingress (단일 리소스) Gateway + HTTPRoute (분리)
어노테이션 (비표준) filters (표준 스펙)
트래픽 분할 불가 backendRefs.weight
크로스 네임스페이스 제한 ReferenceGrant로 명시적 허용

핵심 정리

기능 필드/필터
경로 매칭 matches.path (Exact/PathPrefix/RegularExpression)
헤더 라우팅 matches.headers
트래픽 분할 backendRefs.weight
URL 재작성 filters.URLRewrite
리다이렉트 filters.RequestRedirect
헤더 수정 filters.RequestHeaderModifier

Gateway API HTTPRoute는 Ingress의 어노테이션 지옥에서 벗어나 표준 스펙으로 트래픽 라우팅을 선언적 관리하는 K8s 차세대 표준입니다. 카나리 배포의 weight 기반 분할, 헤더 기반 A/B 테스트, ReferenceGrant를 활용한 멀티 네임스페이스 아키텍처를 적극 활용하세요. K8s Gateway API vs Ingress 비교K8s Ingress NGINX 심화도 함께 참고하세요.

위로 스크롤
WordPress Appliance - Powered by TurnKey Linux