GKE에서 멀티 클러스터 메시 설정
이 가이드에서는 Mesh CA 또는 Istio CA를 사용하여 두 클러스터를 단일 Cloud Service Mesh로 조인하고 클러스터 간 부하 분산을 사용 설정하는 방법을 설명합니다. 이 프로세스를 손쉽게 확장하여 여러 개의 클러스터를 메시에 조인할 수 있습니다.
멀티 클러스터 Cloud Service Mesh 구성은 확장, 위치, 격리와 같은 중요한 기업 문제를 해결할 수 있습니다. 자세한 내용은 멀티 클러스터 사용 사례를 참조하세요.
기본 요건
이 가이드에서는 다음 요구사항을 충족하는 Google Cloud GKE 클러스터가 2개 이상 있다고 가정합니다.
Cloud Service Mesh 버전 1.11 이상이
asmcli install
을 사용하여 클러스터에 설치되어 있어야 합니다.asmcli install
을 실행할 때asmcli
에서--output_dir
에 지정된 디렉터리에 다운로드하는asmcli
,istioctl
도구, 샘플이 필요합니다. 설정하려면 종속 도구 설치 및 클러스터 검증의 단계를 따라 다음 작업을 수행하세요.Cloud Service Mesh를 구성하려면 먼저 메시의 클러스터가 모든 포드 간에 연결되어 있어야 합니다. 또한 동일한 프로젝트에 없는 클러스터를 조인하는 경우 클러스터는 동일한 Fleet 호스트 프로젝트에 등록되어야 하며 클러스터가 동일한 네트워크의 공유 VPC 구성에 함께 있어야 합니다. 또한 공유 VPC를 호스팅할 하나의 프로젝트와 클러스터를 만들기 위한 2개의 서비스 프로젝트를 두는 것이 좋습니다. 자세한 내용은 공유 VPC를 사용하여 클러스터 설정을 참조하세요.
Istio CA를 사용하는 경우 두 클러스터에 동일한 커스텀 루트 인증서를 사용합니다.
Cloud Service Mesh가 비공개 클러스터에 빌드된 경우 동일한 VPC에 단일 서브넷을 생성하는 것이 좋습니다. 그렇지 않으면 다음을 확인합니다.
- 컨트롤 플레인은 클러스터 비공개 IP를 통해 원격 비공개 클러스터 컨트롤 플레인에 연결할 수 있습니다.
- 호출 컨트롤 플레인의 IP 범위를 원격 비공개 클러스터의 승인된 네트워크에 추가할 수 있습니다. 자세한 내용은 비공개 클러스터 간 엔드포인트 디스커버리 구성을 참조하세요.
멀티 클러스터 메시에 있는 Cloud Service Mesh 컨트롤 플레인의 다른 인스턴스에서 API 서버에 연결할 수 있어야 합니다.
- 클러스터에 전역 액세스가 사용 설정되어 있는지 확인합니다.
- 마스터 승인 네트워크에서 허용 목록을 통해 Cloud Service Mesh 컨트롤 플레인 IP가 적절하게 허용되었는지 확인합니다.
프로젝트 및 클러스터 변수 설정
다음과 같은 프로젝트 ID, 클러스터 영역 또는 리전, 클러스터 이름, 컨텍스트의 환경 변수를 만듭니다.
export PROJECT_1=PROJECT_ID_1 export LOCATION_1=CLUSTER_LOCATION_1 export CLUSTER_1=CLUSTER_NAME_1 export CTX_1="gke_${PROJECT_1}_${LOCATION_1}_${CLUSTER_1}" export PROJECT_2=PROJECT_ID_2 export LOCATION_2=CLUSTER_LOCATION_2 export CLUSTER_2=CLUSTER_NAME_2 export CTX_2="gke_${PROJECT_2}_${LOCATION_2}_${CLUSTER_2}"
새로 생성된 클러스터가 있으면 다음
gcloud
명령어로 각 클러스터에 대해 사용자 인증 정보를 가져와야 합니다. 그렇지 않으면 연결된context
를 이 가이드의 다음 단계에서 사용할 수 없습니다.명령어는 클러스터 유형(리전 또는 영역)에 따라 달라집니다.
리전
gcloud container clusters get-credentials ${CLUSTER_1} --region ${LOCATION_1} gcloud container clusters get-credentials ${CLUSTER_2} --region ${LOCATION_2}
영역
gcloud container clusters get-credentials ${CLUSTER_1} --zone ${LOCATION_1} gcloud container clusters get-credentials ${CLUSTER_2} --zone ${LOCATION_2}
방화벽 규칙 만들기
일부 경우에는 클러스터 간 트래픽이 허용되도록 방화벽 규칙을 만들어야 합니다. 예를 들어 다음과 같은 경우 방화벽 규칙을 만들어야 합니다.
- 메시의 클러스터에 다른 서브넷을 사용하는 경우
- pod가 443 및 15002 외의 포트를 여는 경우
GKE는 각 노드에 방화벽 규칙을 자동으로 추가하여 동일한 서브넷 내의 트래픽을 허용합니다. 메시에 여러 서브넷이 포함된 경우, 서브넷 간 트래픽을 허용하도록 방화벽 규칙을 명시적으로 설정해야 합니다. 각 서브넷에 대해 새 방화벽 규칙을 추가하여 소스 IP CIDR 블록과 모든 수신 트래픽의 타겟 포트를 허용해야 합니다.
다음 안내를 따라 프로젝트의 모든 클러스터 간 통신 또는 $CLUSTER_1
및 $CLUSTER_2
간 통신만 허용할 수 있습니다.
클러스터 네트워크에 대한 정보를 수집합니다.
모든 프로젝트 클러스터
클러스터가 같은 프로젝트에 있으면 다음 명령어를 사용하여 프로젝트의 모든 클러스터 간에 통신할 수 있습니다. 프로젝트에서 노출하지 않으려는 클러스터가 있으면 특정 클러스터 탭의 명령어를 사용합니다.
function join_by { local IFS="$1"; shift; echo "$*"; } ALL_CLUSTER_CIDRS=$(gcloud container clusters list --project $PROJECT_1 --format='value(clusterIpv4Cidr)' | sort | uniq) ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}")) ALL_CLUSTER_NETTAGS=$(gcloud compute instances list --project $PROJECT_1 --format='value(tags.items.[0])' | sort | uniq) ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
특정 클러스터
다음 명령어는
$CLUSTER_1
및$CLUSTER_2
간 통신을 허용하며 프로젝트의 다른 클러스터를 노출하지 않습니다.function join_by { local IFS="$1"; shift; echo "$*"; } ALL_CLUSTER_CIDRS=$(for P in $PROJECT_1 $PROJECT_2; do gcloud --project $P container clusters list --filter="name:($CLUSTER_1,$CLUSTER_2)" --format='value(clusterIpv4Cidr)'; done | sort | uniq) ALL_CLUSTER_CIDRS=$(join_by , $(echo "${ALL_CLUSTER_CIDRS}")) ALL_CLUSTER_NETTAGS=$(for P in $PROJECT_1 $PROJECT_2; do gcloud --project $P compute instances list --filter="name:($CLUSTER_1,$CLUSTER_2)" --format='value(tags.items.[0])' ; done | sort | uniq) ALL_CLUSTER_NETTAGS=$(join_by , $(echo "${ALL_CLUSTER_NETTAGS}"))
방화벽 규칙 만들기
GKE
gcloud compute firewall-rules create istio-multicluster-pods \ --allow=tcp,udp,icmp,esp,ah,sctp \ --direction=INGRESS \ --priority=900 \ --source-ranges="${ALL_CLUSTER_CIDRS}" \ --target-tags="${ALL_CLUSTER_NETTAGS}" --quiet \ --network=YOUR_NETWORK
Autopilot
TAGS="" for CLUSTER in ${CLUSTER_1} ${CLUSTER_2} do TAGS+=$(gcloud compute firewall-rules list --filter="Name:$CLUSTER*" --format="value(targetTags)" | uniq) && TAGS+="," done TAGS=${TAGS::-1} echo "Network tags for pod ranges are $TAGS" gcloud compute firewall-rules create asm-multicluster-pods \ --allow=tcp,udp,icmp,esp,ah,sctp \ --network=gke-cluster-vpc \ --direction=INGRESS \ --priority=900 --network=VPC_NAME \ --source-ranges="${ALL_CLUSTER_CIDRS}" \ --target-tags=$TAGS
엔드포인트 검색 구성
엔드포인트 검색을 구성하는 데 필요한 단계는 Fleet의 클러스터에서 선언적 API를 사용하는 것과 공개 클러스터 또는비공개 클러스터에 이를 수동으로 사용 설정하는 것 중 무엇을 선호하는지에 따라 다릅니다.
공개 클러스터 간 엔드포인트 검색 구성
GKE 클러스터 간 엔드포인트 검색을 구성하려면 asmcli create-mesh
를 실행합니다. 이 명령어는 다음을 수행합니다.
- 모든 클러스터를 동일한 Fleet에 등록합니다.
- Fleet 워크로드 아이덴티티를 신뢰하도록 메시를 구성합니다.
- 원격 보안 비밀을 만듭니다.
각 클러스터에 대한 URI 또는 kubeconfig 파일 경로를 지정할 수 있습니다.
클러스터 URI
다음 명령어에서 FLEET_PROJECT_ID
를 Fleet 호스트 프로젝트의 프로젝트 ID로 바꾸고 클러스터 URI를 각 클러스터의 클러스터 이름, 영역 또는 리전, 프로젝트 ID로 바꿉니다.
이 예시에서는 클러스터 2개만 표시하지만 명령어를 실행하면 Fleet에 추가할 수 있는 최대 허용 클러스터 수에 따라 추가 클러스터에 엔드포인트 검색을 사용 설정할 수 있습니다.
./asmcli create-mesh \
FLEET_PROJECT_ID \
${PROJECT_1}/${LOCATION_1}/${CLUSTER_1} \
${PROJECT_2}/${LOCATION_2}/${CLUSTER_2}
kubeconfig 파일
다음 명령어에서 FLEET_PROJECT_ID
를 Fleet 호스트 프로젝트의 프로젝트 ID로, PATH_TO_KUBECONFIG
를 각 kubeconfig
파일 경로로 바꿉니다. 이 예시에서는 클러스터 2개만 표시하지만 Fleet에 추가할 수 있는 최대 허용 클러스터 수에 따라 추가 클러스터에서 엔드포인트 검색을 사용 설정할 수 있습니다.
./asmcli create-mesh \
FLEET_PROJECT_ID \
PATH_TO_KUBECONFIG_1 \
PATH_TO_KUBECONFIG_2
비공개 클러스터 간 엔드포인트 디스커버리 구성
API 서버가 다른 클러스터의 Cloud Service Mesh 컨트롤 플레인에 대한 클러스터에 액세스할 수 있도록 원격 보안 비밀을 구성합니다. 명령어는 Cloud Service Mesh 유형(클러스터 내 또는 관리형)에 따라 다릅니다.
A. 클러스터 내 Cloud Service Mesh의 경우 공개 IP에 액세스할 수 없으므로 공개 IP 대신 비공개 IP를 구성해야 합니다.
PRIV_IP=`gcloud container clusters describe "${CLUSTER_1}" --project "${PROJECT_1}" \ --zone "${LOCATION_1}" --format "value(privateClusterConfig.privateEndpoint)"` ./istioctl x create-remote-secret --context=${CTX_1} --name=${CLUSTER_1} --server=https://${PRIV_IP} > ${CTX_1}.secret
PRIV_IP=`gcloud container clusters describe "${CLUSTER_2}" --project "${PROJECT_2}" \ --zone "${LOCATION_2}" --format "value(privateClusterConfig.privateEndpoint)"` ./istioctl x create-remote-secret --context=${CTX_2} --name=${CLUSTER_2} --server=https://${PRIV_IP} > ${CTX_2}.secret
B. 관리형 Cloud Service Mesh의 경우:
PUBLIC_IP=`gcloud container clusters describe "${CLUSTER_1}" --project "${PROJECT_1}" \ --zone "${LOCATION_1}" --format "value(privateClusterConfig.publicEndpoint)"` ./istioctl x create-remote-secret --context=${CTX_1} --name=${CLUSTER_1} --server=https://${PUBLIC_IP} > ${CTX_1}.secret
PUBLIC_IP=`gcloud container clusters describe "${CLUSTER_2}" --project "${PROJECT_2}" \ --zone "${LOCATION_2}" --format "value(privateClusterConfig.publicEndpoint)"` ./istioctl x create-remote-secret --context=${CTX_2} --name=${CLUSTER_2} --server=https://${PUBLIC_IP} > ${CTX_2}.secret
클러스터에 새 보안 비밀을 적용합니다.
kubectl apply -f ${CTX_1}.secret --context=${CTX_2}
kubectl apply -f ${CTX_2}.secret --context=${CTX_1}
비공개 클러스터의 승인된 네트워크 구성
다음 조건이 모두 메시에 적용되는 경우에만 이 섹션을 따르세요.
여러 비공개 클러스터를 배포할 때는 각 클러스터의 Cloud Service Mesh 컨트롤 플레인에서 원격 클러스터의 GKE 컨트롤 플레인을 호출해야 합니다. 트래픽을 허용하려면 호출 클러스터의 포드 주소 범위를 원격 클러스터의 승인된 네트워크에 추가해야 합니다.
각 클러스터의 pod IP CIDR 블록을 가져옵니다.
POD_IP_CIDR_1=`gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \ --format "value(ipAllocationPolicy.clusterIpv4CidrBlock)"`
POD_IP_CIDR_2=`gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \ --format "value(ipAllocationPolicy.clusterIpv4CidrBlock)"`
Kubernetes 클러스터 pod IP CIDR 블록을 원격 클러스터에 추가합니다.
EXISTING_CIDR_1=`gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \ --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"` gcloud container clusters update ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \ --enable-master-authorized-networks \ --master-authorized-networks ${POD_IP_CIDR_2},${EXISTING_CIDR_1//;/,}
EXISTING_CIDR_2=`gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \ --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"` gcloud container clusters update ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \ --enable-master-authorized-networks \ --master-authorized-networks ${POD_IP_CIDR_1},${EXISTING_CIDR_2//;/,}
자세한 내용은 승인된 네트워크로 클러스터 만들기를 참조하세요.
승인된 네트워크가 업데이트되었는지 확인합니다.
gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \ --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"
gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \ --format "value(masterAuthorizedNetworksConfig.cidrBlocks.cidrBlock)"
컨트롤 플레인 전역 액세스 사용 설정
다음 조건이 모두 메시에 적용되는 경우에만 이 섹션을 따르세요.
- 비공개 클러스터를 사용 중임
- 메시의 클러스터에 다른 리전을 사용함
원격 클러스터의 GKE 컨트롤 플레인을 호출하려면 각 클러스터에서 Cloud Service Mesh 컨트롤 플레인을 허용하도록 컨트롤 플레인 전역 액세스를 사용 설정해야 합니다.
컨트롤 플레인 전역 액세스 사용 설정
gcloud container clusters update ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1} \ --enable-master-global-access
gcloud container clusters update ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2} \ --enable-master-global-access
컨트롤 플레인 전역 액세스가 사용 설정되었는지 확인합니다.
gcloud container clusters describe ${CLUSTER_1} --project ${PROJECT_1} --zone ${LOCATION_1}
gcloud container clusters describe ${CLUSTER_2} --project ${PROJECT_2} --zone ${LOCATION_2}
출력의
privateClusterConfig
섹션에는masterGlobalAccessConfig
의 상태가 표시됩니다.
멀티 클러스터 연결 확인
이 섹션에서는 샘플 HelloWorld
및 Sleep
서비스를 멀티 클러스터 환경에 배포하여 클러스터 간 부하 분산 작동 방식을 확인하는 방법을 설명합니다.
샘플 디렉터리의 변수 설정
asmcli
가 다운로드된 위치로 이동하고 다음 명령어를 실행하여ASM_VERSION
을 설정합니다.export ASM_VERSION="$(./asmcli --version)"
작업 폴더를 클러스터 간 부하 분산이 작동하는지 확인하는 데 사용하는 샘플로 설정합니다. 샘플은
asmcli install
명령어에 지정된--output_dir
디렉터리의 하위 디렉터리에 있습니다. 다음 명령어에서OUTPUT_DIR
를--output_dir
에 지정한 디렉터리로 변경합니다.export SAMPLES_DIR=OUTPUT_DIR/istio-${ASM_VERSION%+*}
사이드카 삽입 사용 설정
각 클러스터에 샘플 네임스페이스를 만듭니다.
for CTX in ${CTX_1} ${CTX_2} do kubectl create --context=${CTX} namespace sample done
생성된 네임스페이스에서 사이드카 삽입을 사용 설정합니다.
권장: 다음 명령어를 실행하여 네임스페이스에 기본 삽입 라벨을 적용합니다.
for CTX in ${CTX_1} ${CTX_2} do kubectl label --context=${CTX} namespace sample \ istio.io/rev- istio-injection=enabled --overwrite done
기본 삽입을 사용하는 것이 좋지만 버전 기반 삽입이 지원됩니다. 다음 안내를 따르세요.
다음 명령어를 사용하여
istiod
에서 버전 라벨을 찾습니다.kubectl get deploy -n istio-system -l app=istiod -o \ jsonpath={.items[*].metadata.labels.'istio\.io\/rev'}'{"\n"}'
네임스페이스에 버전 라벨을 적용합니다. 다음 명령어에서
REVISION_LABEL
은 이전 단계에서 확인한istiod
버전 라벨의 값입니다.for CTX in ${CTX_1} ${CTX_2} do kubectl label --context=${CTX} namespace sample \ istio-injection- istio.io/rev=REVISION_LABEL --overwrite done
HelloWorld 서비스 설치
두 클러스터에서 HelloWorld 서비스를 만듭니다.
kubectl create --context=${CTX_1} \ -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \ -l service=helloworld -n sample
kubectl create --context=${CTX_2} \ -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \ -l service=helloworld -n sample
HelloWorld v1 및 v2를 각 클러스터에 배포
나중에 클러스터 간 부하 분산을 확인하는 데 도움이 되도록
HelloWorld v1
을CLUSTER_1
에,v2
를CLUSTER_2
에 배포합니다.kubectl create --context=${CTX_1} \ -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \ -l version=v1 -n sample
kubectl create --context=${CTX_2} \ -f ${SAMPLES_DIR}/samples/helloworld/helloworld.yaml \ -l version=v2 -n sample
다음 명령어를 사용하여
HelloWorld v1
및v2
가 실행 중인지 확인합니다. 출력이 다음과 비슷한지 확인합니다.kubectl get pod --context=${CTX_1} -n sample
NAME READY STATUS RESTARTS AGE helloworld-v1-86f77cd7bd-cpxhv 2/2 Running 0 40s
kubectl get pod --context=${CTX_2} -n sample
NAME READY STATUS RESTARTS AGE helloworld-v2-758dd55874-6x4t8 2/2 Running 0 40s
Sleep 서비스 배포
두 클러스터에
Sleep
서비스를 배포합니다. 이 포드는 데모용으로 인위적인 네트워크 트래픽을 생성합니다.for CTX in ${CTX_1} ${CTX_2} do kubectl apply --context=${CTX} \ -f ${SAMPLES_DIR}/samples/sleep/sleep.yaml -n sample done
각 클러스터에서
Sleep
서비스가 시작될 때까지 기다립니다. 출력이 다음과 비슷한지 확인합니다.kubectl get pod --context=${CTX_1} -n sample -l app=sleep
NAME READY STATUS RESTARTS AGE sleep-754684654f-n6bzf 2/2 Running 0 5s
kubectl get pod --context=${CTX_2} -n sample -l app=sleep
NAME READY STATUS RESTARTS AGE sleep-754684654f-dzl9j 2/2 Running 0 5s
클러스터 간 부하 분산 확인
HelloWorld
서비스를 여러 번 호출하고 출력을 확인하여 v1과 v2에서 번갈아 응답을 보내는지 확인합니다.
HelloWorld
서비스를 호출합니다.kubectl exec --context="${CTX_1}" -n sample -c sleep \ "$(kubectl get pod --context="${CTX_1}" -n sample -l \ app=sleep -o jsonpath='{.items[0].metadata.name}')" \ -- /bin/sh -c 'for i in $(seq 1 20); do curl -sS helloworld.sample:5000/hello; done'
출력은 다음과 비슷합니다.
Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8 Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv ...
HelloWorld
서비스를 다시 호출합니다.kubectl exec --context="${CTX_2}" -n sample -c sleep \ "$(kubectl get pod --context="${CTX_2}" -n sample -l \ app=sleep -o jsonpath='{.items[0].metadata.name}')" \ -- /bin/sh -c 'for i in $(seq 1 20); do curl -sS helloworld.sample:5000/hello; done'
출력은 다음과 비슷합니다.
Hello version: v2, instance: helloworld-v2-758dd55874-6x4t8 Hello version: v1, instance: helloworld-v1-86f77cd7bd-cpxhv ...
수고하셨습니다. 멀티 클러스터 Cloud Service Mesh의 부하 분산이 성공적으로 완료되었습니다.
HelloWorld 서비스 삭제
부하 분산이 확인되면 클러스터에서 HelloWorld
및 Sleep
모드를 삭제합니다.
kubectl delete ns sample --context ${CTX_1} kubectl delete ns sample --context ${CTX_2}