이 문서에서는 두 GKE 클러스터에 샘플 store 애플리케이션을 블루-그린 배포하는 방법을 안내합니다. 블루/그린 배포는 위험을 최소화하면서 애플리케이션을 새 GKE 클러스터로 이전하는 효과적인 전략입니다. 현재 클러스터 (블루)에서 새 클러스터 (그린)로 트래픽을 점진적으로 이동하면 전체 전환을 커밋하기 전에 프로덕션에서 새 환경을 검증할 수 있습니다.
이 튜토리얼에서는 샘플 store 애플리케이션을 사용하여 온라인 쇼핑 서비스가 개별 팀에 의해 소유 및 운영되고 공유되는 GKE 클러스터의 Fleet 간에 배포되는 실제 시나리오를 시뮬레이션합니다.
시작하기 전에
멀티 클러스터 게이트웨이는 배포되기 전 몇 가지 환경적인 준비가 요구됩니다. 계속하기 전에 멀티 클러스터 게이트웨이 환경 준비의 단계를 따르세요.
마지막으로 환경에서 컨트롤러를 사용하기 전에 GKE Gateway Controller 제한사항 및 알려진 문제를 검토하세요.
게이트웨이를 사용한 블루-그린 멀티 클러스터 라우팅
gke-l7-global-external-managed-*, gke-l7-regional-external-managed-* 및 gke-l7-rilb-* GatewayClasses에는 트래픽 분할, 헤더 일치, 헤더 조작, 트래픽 미러링 등을 포함하여 많은 고급 트래픽 라우팅 기능이 포함되어 있습니다. 이 예시에서는 가중치에 기반한 트래픽 분할을 사용하여 2개의 GKE 클러스터 간의 트래픽 비율을 명시적으로 제어하는 방법을 살펴봅니다.
이 예시에서는 서비스 소유자가 애플리케이션을 새 GKE 클러스터로 이동하거나 확장할 때 수행하는 몇 가지 실제 단계를 수행합니다. 블루-그린 배포의 목적은 새 클러스터가 올바르게 작동하는지 확인하는 여러 검증 단계를 통해 위험을 줄이기 위한 것입니다. 이 예시는 배포의 4단계를 통과합니다.
- 100%-헤더 기반 카나리아: HTTP 헤더 라우팅을 사용하여 테스트 또는 합성 트래픽만 새 클러스터로 전송합니다.
- 100%-트래픽 미러링: 카나리아 클러스터로 사용자 트래픽을 미러링합니다. 이렇게 하면 사용자 트래픽을 100% 이 클러스터로 복사하여 카나리아 클러스터의 용량을 테스트합니다.
- 90%-10%: 10%의 분할 트래픽을 카나리아로 테스트하여 새 클러스터를 라이브 트래픽에 느리게 노출시킵니다.
- 0%-100%: 오류가 관측된 경우 다시 전환할 수 있는 옵션과 함께 새 클러스터로 완전히 컷오버합니다.
이 예시는 이전 예시와 비슷하지만 대신 내부 멀티 클러스터 게이트웨이를 배포합니다. 이렇게 하면 VPC 내에서 비공개로 액세스할 수 있는 내부 애플리케이션 부하 분산기를 배포합니다. 이전 단계에서 배포한 것과 동일한 애플리케이션 및 클러스터를 사용하지만, 다른 게이트웨이를 통해 이를 배포합니다.
기본 요건
다음 예시는 외부 멀티 클러스터 게이트웨이 배포의 단계 중 일부에 따라 빌드됩니다. 이 예시를 진행하기 전에 다음 단계를 완료했는지 확인하세요.
-
이 예시에서는 이미 설정된 상태의
gke-west-1및gke-west-2클러스터가 사용됩니다. 이러한 클러스터는gke-l7-rilb-mcGatewayClass가 리전별 클래스이고 동일한 리전의 클러스터 백엔드만 지원하기 때문에 동일한 리전에 있습니다. 각 클러스터에 필요한 서비스 및 ServiceExports를 배포합니다. 이전 예시에서 Service 및 ServiceExport를 배포한 경우 이미 일부가 배포되어 있습니다.
kubectl apply --context gke-west-1 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store-west-1-service.yaml kubectl apply --context gke-west-2 -f https://raw.githubusercontent.com/GoogleCloudPlatform/gke-networking-recipes/main/gateway/gke-gateway-controller/multi-cluster-gateway/store-west-2-service.yaml비슷한 리소스 집합을 각 클러스터에 배포합니다.
service/store created serviceexport.net.gke.io/store created service/store-west-2 created serviceexport.net.gke.io/store-west-2 created
프록시 전용 서브넷 구성
아직 수행하지 않았으면 내부 게이트웨이를 배포하려는 각 리전에서 프록시 전용 서브넷을 구성합니다. 이 서브넷은 부하 분산기 프록시에 내부 IP 주소를 제공하는 데 사용되며 --purpose가 REGIONAL_MANAGED_PROXY로만 설정된 상태로 구성되어야 합니다.
내부 애플리케이션 부하 분산기를 관리하는 게이트웨이를 만들기 전에 프록시 전용 서브넷을 만들어야 합니다. 내부 애플리케이션 부하 분산기를 사용하는 Virtual Private Cloud(VPC) 네트워크의 각 리전에는 프록시 전용 서브넷이 있어야 합니다.
gcloud compute networks subnets create 명령어는 프록시 전용 서브넷을 만듭니다.
gcloud compute networks subnets create SUBNET_NAME \
--purpose=REGIONAL_MANAGED_PROXY \
--role=ACTIVE \
--region=REGION \
--network=VPC_NETWORK_NAME \
--range=CIDR_RANGE
다음을 바꿉니다.
SUBNET_NAME: 프록시 전용 서브넷의 이름입니다.REGION: 프록시 전용 서브넷의 리전입니다.VPC_NETWORK_NAME: 서브넷이 포함된 VPC 네트워크의 이름입니다.CIDR_RANGE: 서브넷의 기본 IP 주소 범위입니다. 리전의 프록시에서 64개 이상의 IP 주소를 사용할 수 있도록/26이상의 서브넷 마스크를 사용해야 합니다. 권장 서브넷 마스크는/23입니다.
게이트웨이 배포
다음 게이트웨이는 동일한 리전의 GKE 클러스터만 타겟팅하는 리전별 내부 게이트웨이인 gke-l7-rilb-mc GatewayClass에서 생성됩니다.
다음
Gateway매니페스트를 구성 클러스터(이 예시에서는gke-west-1)에 적용합니다.cat << EOF | kubectl apply --context gke-west-1 -f - kind: Gateway apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-http namespace: store spec: gatewayClassName: gke-l7-rilb-mc listeners: - name: http protocol: HTTP port: 80 allowedRoutes: kinds: - kind: HTTPRoute EOF게이트웨이가 성공적으로 배포되었는지 확인합니다. 다음 명령어를 사용하여 이 게이트웨이에서 이벤트를 필터링할 수 있습니다.
kubectl get events --field-selector involvedObject.kind=Gateway,involvedObject.name=internal-http --context=gke-west-1 --namespace store출력이 다음과 유사하면 게이트웨이 배포가 성공한 것입니다.
LAST SEEN TYPE REASON OBJECT MESSAGE 5m18s Normal ADD gateway/internal-http store/internal-http 3m44s Normal UPDATE gateway/internal-http store/internal-http 3m9s Normal SYNC gateway/internal-http SYNC on store/internal-http was a success
헤더 기반 카나리아
헤더 기반 카나리아를 통해 서비스 소유자는 실제 사용자로부터 제공되지 않는 합성 테스트 트래픽을 찾을 수 있습니다. 이렇게 하면 사용자를 직접 노출시키지 않고도 애플리케이션의 기본 네트워킹이 작동하는지 쉽게 확인할 수 있습니다.
다음
HTTPRoute매니페스트를 구성 클러스터(이 예시에서는gke-west-1)에 적용합니다.cat << EOF | kubectl apply --context gke-west-1 -f - kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-store-route namespace: store labels: gateway: internal-http spec: parentRefs: - kind: Gateway namespace: store name: internal-http hostnames: - "store.example.internal" rules: # Matches for env=canary and sends it to store-west-2 ServiceImport - matches: - headers: - name: env value: canary backendRefs: - group: net.gke.io kind: ServiceImport name: store-west-2 port: 8080 # All other traffic goes to store-west-1 ServiceImport - backendRefs: - group: net.gke.io kind: ServiceImport name: store-west-1 port: 8080 EOF배포된 다음에는 이 HTTPRoute가 다음 라우팅 동작을 구성합니다.
env: canaryHTTP 헤더가 없는store.example.internal헤더에 대한 내부 요청은gke-west-1클러스터의store포드로 라우팅됩니다.env: canaryHTTP 헤더가 있는store.example.internal에 대한 내부 요청은gke-west-2클러스터의store포드로 라우팅됩니다.
게이트웨이 IP 주소로 트래픽을 전송하여 HTTPRoute가 올바르게 작동하는지 확인합니다.
internal-http에서 내부 IP 주소를 검색합니다.kubectl get gateways.gateway.networking.k8s.io internal-http -o=jsonpath="{.status.addresses[0].value}" --context gke-west-1 --namespace store다음 단계에서 VIP를 출력으로 수신되는 IP 주소로 바꿉니다.
env: canaryHTTP 헤더를 사용하여 요청을 게이트웨이로 전송합니다. 이렇게 하면 트래픽이gke-west-2로 라우팅되는지 확인합니다. GKE 클러스터와 동일한 VPC에서 비공개 클라이언트를 사용하여 요청이 올바르게 라우팅되는지 확인합니다. 다음 명령어는 게이트웨이 IP 주소에 대해 비공개 액세스 권한이 있는 머신에서 실행되어야 하며, 그렇지 않으면 작동하지 않습니다.curl -H "host: store.example.internal" -H "env: canary" http://VIP이 출력은 요청이
gke-west-2클러스터의 포드에서 제공되었는지 확인합니다.{ "cluster_name": "gke-west-2", "host_header": "store.example.internal", "node_name": "gke-gke-west-2-default-pool-4cde1f72-m82p.c.agmsb-k8s.internal", "pod_name": "store-5f5b954888-9kdb5", "pod_name_emoji": "😂", "project_id": "agmsb-k8s", "timestamp": "2021-05-31T01:21:55", "zone": "us-west1-a" }
트래픽 미러링
이 단계에서는 의도된 클러스터로 트래픽을 전송하지만 또한 카나리아 클러스터로 트래픽을 미러링합니다.
미러링 사용은 어떤 방식으로든 클라이언트에 대한 응답에 영향을 주지 않고 트래픽 로드가 애플리케이션 성능에 미치는 영향을 확인하는 데 유용합니다. 모든 종류의 출시에 필요하지는 않을 수 있지만 성능 또는 로드에 영향을 줄 수 있는 대규모 변경사항을 출시할 때 유용할 수 있습니다.
다음
HTTPRoute매니페스트를 구성 클러스터(이 예시에서는gke-west-1)에 적용합니다.cat << EOF | kubectl apply --context gke-west-1 -f - kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-store-route namespace: store labels: gateway: internal-http spec: parentRefs: - kind: Gateway namespace: store name: internal-http hostnames: - "store.example.internal" rules: # Sends all traffic to store-west-1 ServiceImport - backendRefs: - name: store-west-1 group: net.gke.io kind: ServiceImport port: 8080 # Also mirrors all traffic to store-west-2 ServiceImport filters: - type: RequestMirror requestMirror: backendRef: group: net.gke.io kind: ServiceImport name: store-west-2 port: 8080 EOF비공개 클라이언트를 사용하여 요청을
internal-http게이트웨이로 전송합니다. 이후 단계에서 애플리케이션 로그에서 이 요청을 고유하게 식별할 수 있도록/mirror경로를 사용합니다.curl -H "host: store.example.internal" http://VIP/mirror출력을 통해 클라이언트가
gke-west-1클러스터의 포드에서 응답을 수신했음이 확인됩니다.{ "cluster_name": "gke-west-1", "host_header": "store.example.internal", "node_name": "gke-gke-west-1-default-pool-65059399-ssfq.c.agmsb-k8s.internal", "pod_name": "store-5f5b954888-brg5w", "pod_name_emoji": "🎖", "project_id": "agmsb-k8s", "timestamp": "2021-05-31T01:24:51", "zone": "us-west1-a" }즉, 기본 클러스터가 트래픽에 응답하고 있습니다. 마이그레이션 중인 클러스터가 미러링된 트래픽을 수신하는지 확인해야 합니다.
gke-west-2클러스터에서store포드의 애플리케이션 로그를 확인합니다. 이 로그로 포드가 부하 분산기에서 미러링된 트래픽을 수신했는지 확인됩니다.kubectl logs deployment/store --context gke-west-2 -n store | grep /mirror이 출력은
gke-west-2클러스터의 포드가 동일한 요청을 수신하는지 확인합니다. 하지만 이러한 요청에 대한 응답은 클라이언트로 다시 전송되지 않습니다. 로그에 표시된 IP 주소는 해당 포드와 통신하는 부하 분산기의 내부 IP 주소입니다.Found 2 pods, using pod/store-5c65bdf74f-vpqbs [2023-10-12 21:05:20,805] INFO in _internal: 192.168.21.3 - - [12/Oct/2023 21:05:20] "GET /mirror HTTP/1.1" 200 - [2023-10-12 21:05:27,158] INFO in _internal: 192.168.21.3 - - [12/Oct/2023 21:05:27] "GET /mirror HTTP/1.1" 200 - [2023-10-12 21:05:27,805] INFO in _internal: 192.168.21.3 - - [12/Oct/2023 21:05:27] "GET /mirror HTTP/1.1" 200 -
트래픽 분할
트래픽 분할은 새 코드를 출시하거나 새 환경에 안전하게 배포하기 위한 가장 일반적인 방법 중 하나입니다. 서비스 소유자는 실제 사용자 요청에 대해 수락되는 위험 수준에 따라 출시 성공을 확인할 수 있도록 일반적으로 전체 트래픽의 극소량에 해당하는 카나리아 백엔드로 전송되는 명시적인 트래픽 백분율을 설정합니다.
트래픽의 소량에 트래픽 분할을 수행하면 서비스 소유자가 애플리케이션 및 응답 상태를 조사할 수 있습니다. 모든 신호가 정상 상태로 보이면 전체 컷오버로 진행할 수 있습니다.
다음
HTTPRoute매니페스트를 구성 클러스터(이 예시에서는gke-west-1)에 적용합니다.cat << EOF | kubectl apply --context gke-west-1 -f - kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-store-route namespace: store labels: gateway: internal-http spec: parentRefs: - kind: Gateway namespace: store name: internal-http hostnames: - "store.example.internal" rules: - backendRefs: # 90% of traffic to store-west-1 ServiceImport - name: store-west-1 group: net.gke.io kind: ServiceImport port: 8080 weight: 90 # 10% of traffic to store-west-2 ServiceImport - name: store-west-2 group: net.gke.io kind: ServiceImport port: 8080 weight: 10 EOF비공개 클라이언트를 사용하여 지속적인 curl 요청을
internal- http게이트웨이로 전송합니다.while true; do curl -H "host: store.example.internal" -s VIP | grep "cluster_name"; sleep 1; done출력은 이와 비슷하며, 90/10 트래픽 분할이 수행됨을 나타냅니다.
"cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", "cluster_name": "gke-west-2", "cluster_name": "gke-west-1", "cluster_name": "gke-west-1", ...
트래픽 컷오버
블루-그린 마이그레이션의 마지막 단계는 새 클러스터로 완전히 컷오버하고 이전 클러스터를 삭제하는 것입니다. 서비스 소유자가 실제로 두 번째 클러스터를 기존 클러스터로 온보딩하고 있었던 경우에는 최종 단계로 트래픽이 두 클러스터 모두로 전송되므로, 이 마지막 단계가 달라집니다. 이 시나리오에서는 gke-west-1 및 gke-west-2 클러스터 모두의 포드가 포함된 단일 store ServiceImport가 권장됩니다. 이렇게 하면 부하 분산기가 근접성, 상태, 용량을 기준으로 활성-활성 애플리케이션에 대해 트래픽이 전송될 위치를 결정할 수 있습니다.
다음
HTTPRoute매니페스트를 구성 클러스터(이 예시에서는gke-west-1)에 적용합니다.cat << EOF | kubectl apply --context gke-west-1 -f - kind: HTTPRoute apiVersion: gateway.networking.k8s.io/v1 metadata: name: internal-store-route namespace: store labels: gateway: internal-http spec: parentRefs: - kind: Gateway namespace: store name: internal-http hostnames: - "store.example.internal" rules: - backendRefs: # No traffic to the store-west-1 ServiceImport - name: store-west-1 group: net.gke.io kind: ServiceImport port: 8080 weight: 0 # All traffic to the store-west-2 ServiceImport - name: store-west-2 group: net.gke.io kind: ServiceImport port: 8080 weight: 100 EOF비공개 클라이언트를 사용하여 지속적인 curl 요청을
internal- http게이트웨이로 전송합니다.while true; do curl -H "host: store.example.internal" -s VIP | grep "cluster_name"; sleep 1; done출력은 다음과 비슷합니다. 이제 모든 트래픽이
gke-west-2로 전송됩니다."cluster_name": "gke-west-2", "cluster_name": "gke-west-2", "cluster_name": "gke-west-2", "cluster_name": "gke-west-2", ...
이 마지막 단계에서는 하나의 GKE 클러스터에서 다른 GKE 클러스터로 전체 블루-그린 애플리케이션 마이그레이션을 완료합니다.
삭제
이 문서의 연습을 완료한 후에는 다음 단계에 따라 자신의 계정에 원치 않는 비용이 발생하지 않도록 리소스를 삭제합니다.
다른 목적으로 등록할 필요가 없으면 Fleet에서 클러스터를 등록 취소합니다.
multiclusterservicediscovery기능을 사용 중지합니다.gcloud container fleet multi-cluster-services disable멀티 클러스터 인그레스 사용 중지
gcloud container fleet ingress disableAPI를 사용 중지합니다.
gcloud services disable \ multiclusterservicediscovery.googleapis.com \ multiclusteringress.googleapis.com \ trafficdirector.googleapis.com \ --project=PROJECT_ID
문제 해결
정상 업스트림 없음
증상:
게이트웨이를 만들 때 백엔드 서비스에 액세스할 수 없는 경우 다음 문제가 발생할 수 있습니다(503 응답 코드).
no healthy upstream
이유:
이 오류 메시지는 상태 점검 프로버가 정상 백엔드 서비스를 찾을 수 없음을 나타냅니다. 백엔드 서비스가 정상일 수 있지만 상태 점검을 맞춤설정해야 할 수도 있습니다.
해결 방법:
이 문제를 해결하려면 HealthCheckPolicy를 사용하여 애플리케이션 요구사항(예: /health)에 따라 상태 점검을 맞춤설정합니다.
다음 단계
- 게이트웨이 컨트롤러 자세히 알아보기