Arm을 사용하여 GKE의 x86 애플리케이션을 멀티 아키텍처로 마이그레이션


이 튜토리얼에서는 Google Kubernetes Engine(GKE) 클러스터의 x86(Intel 또는 AMD) 프로세서를 사용하여 노드용으로 빌드된 애플리케이션을 x86 또는 Arm 노드에서 실행되는 멀티 아키텍처(multi-arch) 애플리케이션으로 마이그레이션하는 방법을 설명합니다. 이 튜토리얼의 대상은 기존 x86 호환 워크로드를 Arm에서 실행하려는 플랫폼 관리자, 앱 운영자, 앱 개발자를 대상으로 합니다.

GKE 클러스터를 사용하면 Tau T2A Arm 머신 시리즈를 사용하여 Arm 노드에서 워크로드를 실행할 수 있습니다. T2A 노드는 x86(Intel 또는 AMD) 프로세서를 사용하는 다른 노드와 같이 GKE 클러스터에서 실행될 수 있습니다. 수평 확장 및 컴퓨팅 집약적 워크로드에 적합합니다.

자세한 내용은 GKE의 Arm 워크로드를 참조하세요.

이 튜토리얼에서는 사용자가 Kubernetes 및 Docker에 익숙하다고 가정합니다. 이 튜토리얼에서는 Google Kubernetes Engine 및 Artifact Registry를 사용합니다.

목표

이 튜토리얼에서는 다음 작업을 완료합니다.

  • Artifact Registry에 Docker로 컨테이너 이미지 저장하기
  • x86 호환 워크로드를 GKE 클러스터에 배포하기
  • Arm에서 실행되도록 x86 호환 워크로드 다시 빌드하기
  • 기존 클러스터에 Arm 노드 풀 추가하기
  • Arm 노드에서 실행할 Arm 호환 워크로드 배포하기
  • 멀티 아키텍처 이미지를 빌드하여 여러 아키텍처에서 워크로드 실행하기
  • 하나의 GKE 클러스터에 있는 여러 아키텍처에서 워크로드 실행하기

비용

이 문서에서는 비용이 청구될 수 있는 다음과 같은 Google Cloud 구성요소를 사용합니다.

프로젝트 사용량을 기준으로 예상 비용을 산출하려면 가격 계산기를 사용하세요. Google Cloud를 처음 사용하는 사용자는 무료 체험판을 사용할 수 있습니다.

이 문서에 설명된 태스크를 완료했으면 만든 리소스를 삭제하여 청구가 계속되는 것을 방지할 수 있습니다. 자세한 내용은 삭제를 참조하세요.

시작하기 전에

다음 단계에 따라 Kubernetes Engine API를 사용 설정합니다.
  1. Sign in to your Google Cloud account. If you're new to Google Cloud, create an account to evaluate how our products perform in real-world scenarios. New customers also get $300 in free credits to run, test, and deploy workloads.
  2. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  3. Make sure that billing is enabled for your Google Cloud project.

  4. Enable the Artifact Registry and Google Kubernetes Engine APIs.

    Enable the APIs

  5. In the Google Cloud console, on the project selector page, select or create a Google Cloud project.

    Go to project selector

  6. Make sure that billing is enabled for your Google Cloud project.

  7. Enable the Artifact Registry and Google Kubernetes Engine APIs.

    Enable the APIs

이 가이드를 마치면 만든 리소스를 삭제하여 비용이 계속 청구되지 않도록 할 수 있습니다. 자세한 내용은 삭제를 참조하세요.

Cloud Shell 실행

이 튜토리얼에서는 Google Cloud에서 호스팅되는 리소스를 관리하기 위한 셸 환경인 Cloud Shell을 사용합니다.

Cloud Shell에는 Google Cloud CLIkubectl 명령줄 도구가 사전 설치되어 있습니다. gcloud CLI는 Google Cloud의 기본 명령줄 인터페이스를 제공하며, kubectl은 Kubernetes 클러스터를 대상으로 명령어를 실행할 수 있는 기본 명령줄 인터페이스를 제공합니다.

Cloud Shell 시작:

  1. Google Cloud 콘솔로 이동합니다.

    Google Cloud 콘솔

  2. Console의 오른쪽 상단에서 Cloud Shell 활성화 버튼을 클릭합니다.

콘솔에 Cloud Shell 세션이 표시됩니다. 이 셸을 사용하여 gcloudkubectl 명령어를 실행합니다.

개발 환경 준비하기

이 섹션에서는 튜토리얼에 따라 환경을 준비합니다.

gcloud CLI의 기본 설정을 구성합니다.

프로젝트 ID, 영역, 새 클러스터 이름의 환경 변수를 설정합니다.

export PROJECT_ID=PROJECT_ID
export ZONE=us-central1-a
export CLUSTER_NAME=my-cluster

PROJECT_ID시작하기 전에 섹션에서 이 튜토리얼에 대해 선택한 프로젝트 ID로 바꿉니다.

이 튜토리얼에서는 us-central1-a에서 리소스를 만듭니다. Tau T2A 머신 시리즈를 사용할 수 있는 위치의 전체 목록은 사용 가능한 리전 및 영역을 참조하세요.

Git 저장소 클론

  1. 저장소를 복제합니다.

    git clone https://github.com/GoogleCloudPlatform/kubernetes-engine-samples
    
  2. 현재 작업 디렉터리를 이전 단계에서 클론된 저장소로 변경합니다.

    cd kubernetes-engine-samples/workloads/migrate-x86-app-to-multi-arch/
    

GKE 클러스터 만들기 및 x86 애플리케이션 배포

이 튜토리얼의 1부에서는 x86 노드로 클러스터를 만들고 x86 애플리케이션을 배포합니다. 예시 애플리케이션은 HTTP 요청에 응답하는 서비스입니다. Golang 프로그래밍 언어로 빌드되었습니다.

이 설정은 x86 호환 애플리케이션과 x86 노드를 사용하여 일반적인 클러스터 환경을 나타냅니다.

GKE 클러스터 만들기

먼저 x86 프로세서가 있는 노드를 사용하여 GKE를 만듭니다. 이 구성에서는 x86 애플리케이션을 실행할 일반적인 클러스터 환경을 만듭니다.

클러스터를 만듭니다.

gcloud container clusters create $CLUSTER_NAME \
    --release-channel=rapid \
    --zone=$ZONE \
    --machine-type=e2-standard-2 \
    --num-nodes=1 \
    --async

이후 단계에서 특정 기능을 설명하기 위해 이 클러스터의 자동 확장은 중지되었습니다.

클러스터 생성이 완료되기까지 몇 분이 걸릴 수 있습니다. --async 플래그를 사용하면 다음 단계를 완료하는 동안 이 작업이 백그라운드에서 실행됩니다.

Arm 노드만 있는 클러스터를 만들 수 있지만 이 튜토리얼에서는 먼저 x86 노드만 있는 클러스터를 만들어 Arm과 호환되는 x86 전용 애플리케이션을 만드는 프로세스를 알아봅니다.

Artifact Registry Docker 저장소 만들기

  1. Artifact Registry에 Docker 이미지를 저장할 저장소를 만듭니다.

    gcloud artifacts repositories create docker-repo \
          --repository-format=docker \
          --location=us-central1 \
          --description="Docker repository"
    
  2. Artifact Registry에서 이 저장소를 인증하도록 Docker 명령줄 도구를 구성합니다.

    gcloud auth configure-docker us-central1-docker.pkg.dev
    

x86 이미지를 빌드하고 Artifact Registry에 푸시

  1. 애플리케이션의 x86 호환 버전을 빌드합니다.

    docker build -t us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/x86-hello:v0.0.1 . 
    
  2. 이미지를 Artifact Registry로 내보냅니다.

    docker push us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/x86-hello:v0.0.1
    

x86 애플리케이션 배포

  1. 다음 스크립트를 실행하여 클러스터가 준비되었는지 확인합니다.

    echo
    echo -ne "Waiting for GKE cluster to finish provisioning"
    gke_status=""
    while [ -z $gke_status ]; do
       sleep 2
       echo -ne '.'   
       gke_status=$(gcloud container clusters list --format="value(STATUS)" --filter="NAME=$CLUSTER_NAME AND STATUS=RUNNING")
    done
    echo
    echo "GKE Cluster '$CLUSTER_NAME' is $gke_status" 
    echo
    

    클러스터가 준비되면 다음과 비슷한 출력이 표시되어야 합니다.

    GKE Cluster 'my-cluster' is RUNNING
    
  2. kubectl이 클러스터의 Kubernetes API에 연결할 수 있도록 클러스터 사용자 인증 정보를 검색합니다.

    gcloud container clusters get-credentials $CLUSTER_NAME --zone $ZONE --project $PROJECT_ID
    
  3. kustomize를 사용하여 이미지를 업데이트하고 x86 애플리케이션을 배포합니다.

    $(cd k8s/overlays/x86 && kustomize edit set image hello=us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/x86-hello:v0.0.1) 
    kubectl apply -k k8s/overlays/x86
    
  4. 서비스를 배포하여 애플리케이션을 인터넷에 노출합니다.

    kubectl apply -f k8s/hello-service.yaml
    
  5. 서비스 hello-service의 외부 IP 주소에서 프로비저닝을 완료했는지 확인합니다.

    echo
    echo -ne "Waiting for External IP to be provisioned"
    external_ip=""
    while [ -z $external_ip ]; do
       sleep 2
       echo -ne '.'
       external_ip=$(kubectl get svc hello-service --template="{{range .status.loadBalancer.ingress}}{{.ip}}{{end}}")
    done
    echo
    echo "External IP: $external_ip"
    echo
    

    외부 IP 주소가 프로비저닝되면 다음과 비슷한 출력이 표시되어야 합니다.

    External IP: 203.0.113.0
    
  6. HTTP 요청을 수행하여 배포가 예상대로 작동하는지 테스트합니다.

    curl -w '\n' http://$external_ip
    

    출력은 다음과 비슷합니다.

    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-mwfkd, CPU PLATFORM:linux/amd64
    

    출력에서는 이 x86 호환 배포가 amd64 아키텍처의 기본 노드 풀에 있는 노드에서 실행되고 있음을 보여줍니다. 클러스터의 기본 노드 풀에 있는 노드에 x86(Intel 또는 AMD) 프로세서가 있습니다.

클러스터에 Arm 노드 추가

이 튜토리얼의 다음 부분에서는 기존 클러스터에 Arm 노드를 추가합니다. 이러한 노드는 애플리케이션의 Arm 호환 버전이 Arm에서 실행되도록 다시 빌드될 때 배포되는 위치입니다.

검문소

지금까지 다음과 같은 목표를 달성했습니다.

  • x86 노드를 사용하여 GKE 클러스터 만들기
  • Artifact Registry에 Docker를 사용하여 x86 호환 컨테이너 이미지 저장하기
  • x86 호환 워크로드를 GKE 클러스터에 배포하기

x86 노드 및 x86 호환 워크로드로 클러스터 환경을 구성했습니다. 이 구성은 현재 Arm 노드와 Arm 호환 워크로드를 사용하지 않는 경우 기존 클러스터 환경과 유사합니다.

클러스터에 Arm 노드 풀 추가

기존 클러스터에 Arm 노드 풀을 추가합니다.

gcloud container node-pools create arm-pool \
    --cluster $CLUSTER_NAME \
    --zone $ZONE \
    --machine-type=t2a-standard-2 \
    --num-nodes=1

t2a-standard-2 머신 유형은 Tau T2A 머신 시리즈(미리보기)의 Arm VM입니다.

x86 노드를 사용하여 노드 풀을 만드는 방식과 동일한 방식으로 Arm 노드를 사용하여 노드 풀을 만듭니다. 이 노드 풀이 생성되면 이 클러스터에서 x86 노드와 Arm 노드 모두 실행됩니다.

기존 클러스터에 Arm 노드 풀을 추가하는 방법에 대한 자세한 내용은 GKE 클러스터에 Arm 노드 풀 추가를 참조하세요.

x86 기반 노드에서 실행되는 기존 애플리케이션 수직 확장

여러 아키텍처 유형의 노드가 클러스터 하나에서 원활하게 함께 작동할 수 있습니다. taint는 자동으로 Arm 노드에 배치되므로 GKE는 x86 노드에서 실행되는 기존 워크로드를 클러스터의 Arm 노드로 예약하지 않습니다. 기존 애플리케이션을 수직 확장하면 이를 확인할 수 있습니다.

  1. 워크로드를 업데이트하여 복제본 최대 6개으로 확장합니다.

    $(cd k8s/overlays/x86_increase_replicas && kustomize edit set image hello=us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/x86-hello:v0.0.1) 
    kubectl apply -k k8s/overlays/x86_increase_replicas/
    
  2. 30초 동안 기다린 후 다음 명령어를 실행하여 배포 상태를 확인합니다.

    kubectl get pods -l="app=hello" --field-selector="status.phase=Pending"
    

    출력은 다음과 비슷하게 표시됩니다.

    NAME                                    READY   STATUS    RESTARTS   AGE
    x86-hello-deployment-6b7b456dd5-6tkxd   0/1     Pending   0          40s
    x86-hello-deployment-6b7b456dd5-k95b7   0/1     Pending   0          40s
    x86-hello-deployment-6b7b456dd5-kc876   0/1     Pending   0          40s
    

    이 출력에는 x86 기반 노드에 남아 있는 공간이 없으므로 대기 중 상태의 포드가 표시됩니다. 클러스터 자동 확장 처리가 중지되고 Arm 노드가 taint되므로 워크로드는 사용 가능한 Arm 노드에 배포되지 않습니다. 이 taint는 GKE가 Arm 노드에서 x86 워크로드를 예약하지 못하게 합니다. Arm 노드에 배포하려면 배포가 Arm 노드와 호환됨을 나타내야 합니다.

  3. 실행 중 상태인 포드를 확인합니다.

    kubectl get pods -l="app=hello" --field-selector="status.phase=Running" -o wide
    

    출력은 다음과 비슷하게 표시됩니다.

    NAME                                    READY   STATUS    RESTARTS   AGE   IP            NODE                                        NOMINATED NODE   READINESS GATES
    x86-hello-deployment-6b7b456dd5-cjclz   1/1     Running   0          62s   10.100.0.17   gke-my-cluster-default-pool-32019863-b41t   <none>           <none>
    x86-hello-deployment-6b7b456dd5-mwfkd   1/1     Running   0          34m   10.100.0.11   gke-my-cluster-default-pool-32019863-b41t   <none>           <none>
    x86-hello-deployment-6b7b456dd5-n56rg   1/1     Running   0          62s   10.100.0.16   gke-my-cluster-default-pool-32019863-b41t   <none>           <none>
    

    이 출력에서 NODE 열은 배포의 모든 포드가 기본 풀에서만 실행 중임을 나타냅니다. 즉, x86 호환 포드는 x86 노드에만 예약됩니다. Arm 노드 풀을 만들기 전에 이미 예약된 원래 포드는 동일한 노드에서 계속 실행됩니다.

  4. 다음 명령어를 실행하여 서비스에 액세스하고 출력을 확인합니다.

    for i in $(seq 1 6); do curl -w '\n' http://$external_ip; done
    

    출력은 다음과 비슷합니다.

    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-cjclz, CPU PLATFORM:linux/amd64
    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-cjclz, CPU PLATFORM:linux/amd64
    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-n56rg, CPU PLATFORM:linux/amd64
    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-n56rg, CPU PLATFORM:linux/amd64
    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-cjclz, CPU PLATFORM:linux/amd64
    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-cjclz, CPU PLATFORM:linux/amd64
    

    이 출력은 모든 포드 제공 요청이 x86 노드에서 실행 중임을 보여줍니다. 일부 포드는 기존 x86 노드에 공간이 없고 Arm 노드에 예약되지 않아서 아직 대기중 상태이므로 응답할 수 없습니다.

Arm에서 실행되도록 애플리케이션 다시 빌드

이전 섹션에서는 Arm 노드 풀을 기존 클러스터에 추가했습니다. 그러나 기존 x86 애플리케이션을 수직 확장할 때 워크로드를 Arm 노드에 예약하지 않았습니다. 이 섹션에서는 이 애플리케이션이 클러스터의 Arm 노드에서 실행될 수 있도록 애플리케이션을 Arm과 호환되도록 다시 빌드합니다.

이 예시에서는 docker build를 사용하여 이 단계를 수행합니다. 이 2단계 방식에는 다음이 포함됩니다.

  • 1단계: 코드를 Arm에 빌드합니다.
  • 2단계: 실행 파일을 린 컨테이너에 복사합니다.

이러한 단계를 수행하면 x86 호환 이미지 이외의 Arm 호환 이미지가 생성됩니다.

실행 파일을 다른 컨테이너에 복사하는 두 번째 단계는 가능한 최소 이미지를 빌드하는 컨테이너 빌드 권장사항 중 하나를 따릅니다.

이 튜토리얼에서는 Golang 프로그래밍 언어로 빌드된 예시 애플리케이션을 사용합니다. Golang을 사용하면 각각 GOOSGOARCH 환경 변수를 제공하여 여러 운영체제 및 CPU 플랫폼으로 애플리케이션을 크로스 컴파일할 수 있습니다.

  1. cat Dockerfile_arm을 실행하여 Arm용으로 작성된 Dockerfile을 확인합니다.

    #
    # Build: 1st stage
    #
    FROM golang:1.18-alpine as builder 
    WORKDIR /app
    COPY go.mod .
    COPY hello.go .
    RUN GOARCH=arm64 go build -o /hello && \
       apk add --update --no-cache file && \
       file /hello
    

    여기에 표시된 스니펫에서는 첫 번째 단계만 보여줍니다. 파일에는 두 단계가 모두 포함됩니다.

    이 파일에서 GOARCH=arm64를 설정하면 Go 컴파일러에서 Arm 명령 집합용 애플리케이션을 빌드하도록 지시합니다. 첫 번째 단계의 기본 이미지는 Linux Alpine 이미지이므로 GOOS를 설정할 필요가 없습니다.

  2. Arm 코드를 빌드하고 Artifact Registry에 푸시합니다.

    docker build -t us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/arm-hello:v0.0.1 -f Dockerfile_arm .
    docker push us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/arm-hello:v0.0.1
    

애플리케이션의 Arm 버전 배포

이제 애플리케이션이 Arm 노드에서 실행되도록 빌드되었으므로 클러스터의 Arm 노드에 애플리케이션을 배포할 수 있습니다.

  1. cat k8s/overlays/arm/add_arm_support.yaml을 실행하여 add_arm_support.yaml을 검사합니다.

    출력은 다음과 비슷합니다.

       nodeSelector:
          kubernetes.io/arch: arm64
    

    nodeSelector는 워크로드가 Arm 노드에서만 실행되도록 지정합니다. nodeSelector를 사용하면 GKE가 Arm 노드의 taint와 일치하는 톨러레이션(toleration)을 추가하여 GKE가 이러한 노드에서 워크로드를 예약할 수 있도록 합니다. 이 필드 설정에 대해 자세히 알아보려면 배포용 Arm 워크로드 준비를 참조하세요.

  2. Arm 호환 버전의 애플리케이션 복제본 하나를 배포합니다.

    $(cd k8s/overlays/arm && kustomize edit set image hello=us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/arm-hello:v0.0.1) 
    kubectl apply -k k8s/overlays/arm
    
  3. 5초 동안 기다린 후 Arm 배포가 curl 요청에 응답하는지 확인합니다.

    for i in $(seq 1 6); do curl -w '\n' http://$external_ip; done
    

    출력은 다음과 비슷합니다.

    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-n56rg, CPU PLATFORM:linux/amd64
    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-n56rg, CPU PLATFORM:linux/amd64
    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-mwfkd, CPU PLATFORM:linux/amd64
    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-mwfkd, CPU PLATFORM:linux/amd64
    Hello from NODE:gke-my-cluster-arm-pool-e172cff7-shwc, POD:arm-hello-deployment-69b4b6bdcc-n5l28, CPU PLATFORM:linux/arm64
    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:x86-hello-deployment-6b7b456dd5-n56rg, CPU PLATFORM:linux/amd64
    

    이 출력에는 curl 요청에 응답하는 x86 호환 애플리케이션과 Arm 호환 애플리케이션 모두의 응답이 포함되어야 합니다.

멀티 아키텍처 이미지를 빌드하여 아키텍처 전반에서 워크로드 실행

이전 섹션에서 설명한 전략을 사용하고 x86 및 Arm에 대해 별도의 워크로드를 배포할 수 있지만 이렇게 하려면 빌드 프로세스 두 개와 컨테이너 이미지 두 개를 유지하고 체계적으로 관리해야 합니다.

이상적으로는 x86 플랫폼과 Arm 플랫폼 모두에서 애플리케이션을 원활하게 빌드하고 실행하려고 합니다. 이러한 경우 이 방법을 사용하는 것이 좋습니다. 여러 아키텍처 플랫폼에서 매니페스트 하나로 애플리케이션을 실행하려면 멀티 아키텍처 이미지를 사용해야 합니다. 멀티 아키텍처 이미지에 대한 자세한 내용은 Aarm 워크로드를 위한 멀티 아키텍처 이미지 빌드를 참조하세요.

멀티 아키텍처 이미지를 사용하려면 애플리케이션이 다음 기본 요건을 충족해야 합니다.

  • 애플리케이션에 아키텍처 플랫폼별 종속 항목이 없습니다.
  • 모든 종속 항목은 멀티 아키텍처 또는 최소한 대상 플랫폼에 맞게 빌드되어야 합니다.

이 튜토리얼에 사용된 예시 애플리케이션은 이러한 두 기본 요건을 모두 충족합니다. 하지만 멀티 아키텍처 이미지를 프로덕션에 배포하기 전에 빌드할 때 고유한 애플리케이션을 테스트하는 것이 좋습니다.

멀티 아키텍처 이미지 빌드 및 푸시

워크로드가 다음 기본 요건을 충족하면 Docker Buildx로 멀티 아키텍처 이미지를 빌드할 수 있습니다.

  • 기본 이미지에서는 아키텍처 여러 개를 지원합니다. 이를 확인하려면 기본 이미지에서 docker manifest inspect를 실행하고 아키텍처 플랫폼 목록을 확인합니다. 이 섹션의 끝부분에서 이미지를 검사하는 방법의 예시를 참조하세요.
  • 애플리케이션에는 각 아키텍처 플랫폼에 대한 특별한 빌드 단계가 필요하지 않습니다. 특별한 단계가 필요한 경우 Buildx로 부족할 수 있습니다. 플랫폼마다 별도의 Dockerfile이 있어야 하고 docker manifest create를 사용하여 매니페스트를 수동으로 만들어야 합니다.

예시 애플리케이션의 기본 이미지는 Alpine이며 아키텍처 여러 개를 지원합니다. 아키텍처 플랫폼별 단계도 없으므로 Buildx를 사용하여 멀티 아키텍처 이미지를 빌드할 수 있습니다.

  1. cat Dockerfile을 실행하여 Dockerfile을 검사합니다.

    # This is a multi-stage Dockerfile. 
    # 1st stage builds the app in the target platform
    # 2nd stage create a lean image coping the binary from the 1st stage
    
    #
    # Build: 1st stage
    #
    FROM golang:1.18-alpine as builder 
    ARG BUILDPLATFORM    
    ARG TARGETPLATFORM
    RUN echo "I am running on $BUILDPLATFORM, building for $TARGETPLATFORM"  
    WORKDIR /app
    COPY go.mod .
    COPY hello.go .
    RUN go build -o /hello && \
       apk add --update --no-cache file && \
       file /hello   
    
    #
    # Release: 2nd stage
    #
    FROM alpine
    WORKDIR /
    COPY --from=builder /hello /hello
    CMD [ "/hello" ]
    

    이 Dockerfile은 빌드 단계와 출시 단계라는 두 가지 단계를 정의합니다. x86 애플리케이션을 빌드하는 데 사용된 Dockerfile과 동일한 Dockerfile을 사용합니다.

  2. 다음 명령어를 실행하여 새 docker buildx 빌더를 만들고 사용합니다.

    docker buildx create --name multiarch --use --bootstrap
    

    이제 이 새 빌더를 만들었으므로 --platform 플래그를 사용하여 linux/amd64linux/arm64 모두와 호환되는 이미지를 빌드하고 푸시할 수 있습니다. 플래그와 함께 제공된 각 플랫폼의 경우 Buildx는 대상 플랫폼에 이미지를 빌드합니다. Buildx에서 linux/arm64 이미지를 빌드할 때 arm64 기본 이미지를 다운로드합니다. 1단계에서는 arm64arm64 golang:1.18-alpine 이미지에 바이너리를 빌드합니다. 2단계에서는 arm64 Alpine Linux 이미지가 다운로드되고 바이너리가 해당 이미지의 레이어에 복사됩니다.

  3. 이미지를 빌드하고 푸시합니다.

    docker buildx build -t us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/multiarch-hello:v0.0.1 -f Dockerfile --platform linux/amd64,linux/arm64 --push .
    

    출력은 다음과 비슷합니다.

    => [linux/arm64 builder x/x] ..
    => [linux/amd64 builder x/x] ..
    

    이 출력은 linux/arm64linux/amd64를 위한 2개의 이미지가 생성되었음을 보여줍니다.

  4. 새 멀티 아키텍처 이미지의 매니페스트를 검사합니다.

    docker manifest inspect us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/multiarch-hello:v0.0.1
    

    출력은 다음과 비슷합니다.

    {
       "schemaVersion": 2,
       "mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
       "manifests": [
          {
             "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
             "size": 739,
             "digest": "sha256:dfcf8febd94d61809bca8313850a5af9113ad7d4741edec1362099c9b7d423fc",
             "platform": {
                "architecture": "amd64",
                "os": "linux"
             }
          },
          {
             "mediaType": "application/vnd.docker.distribution.manifest.v2+json",
             "size": 739,
             "digest": "sha256:90b637d85a93c3dc03fc7a97d1fd640013c3f98c7c362d1156560bbd01f6a419",
             "platform": {
                "architecture": "arm64",
                "os": "linux"
             }
          }
       ]
    

    이 출력에는 manifests 섹션에 2개의 매니페스트가 있으며 하나는 amd64 플랫폼 아키텍처에, 다른 하나는 arm64 플랫폼 아키텍처에 적용됩니다.

    이 컨테이너 이미지를 클러스터에 배포하면 GKE는 노드의 아키텍처와 일치하는 이미지만 자동으로 다운로드합니다.

애플리케이션의 멀티 아키텍처 버전 배포

  1. 멀티 아키텍처 이미지를 배포하기 전에 원래 워크로드를 삭제합니다.

    kubectl delete deploy x86-hello-deployment arm-hello-deployment
    
  2. cat k8s/overlays/multiarch/add_multiarch_support.yaml을 실행하여 add_multiarch_support.yaml kustomize 오버레이를 검사합니다.

    출력에는 다음 톨러레이션(toleration) 집합이 포함됩니다.

       tolerations:
          - key: kubernetes.io/arch
             operator: Equal
             value: arm64
             effect: NoSchedule
    

    톨러레이션(toleration)은 모든 Arm 노드에 설정된 taint와 일치하므로 이 톨러레이션(toleration)을 통해 클러스터의 Arm 노드에서 워크로드를 실행할 수 있습니다. 이제 이 워크로드가 클러스터의 모든 노드에서 실행될 수 있으므로 톨러레이션(toleration)만 필요합니다. GKE는 톨러레이션(toleration)을 사용하여 워크로드를 x86 노드와 Arm 노드 모두로 예약할 수 있습니다. GKE가 워크로드를 예약할 수 있는 위치를 지정하려면 노드 선택기와 노드 어피니티 규칙을 사용합니다. 이러한 필드 설정에 대한 자세한 내용은 배포용 Arm 워크로드 준비를 참조하세요.

  3. 복제본이 6개 있는 멀티 아키텍처 컨테이너 이미지를 배포합니다.

    $(cd k8s/overlays/multiarch && kustomize edit set image hello=us-central1-docker.pkg.dev/$PROJECT_ID/docker-repo/multiarch-hello:v0.0.1) 
    kubectl apply -k k8s/overlays/multiarch
    
  4. 10초 간 기다린 후 애플리케이션의 모든 복제본이 실행 중인지 확인합니다.

    kubectl get pods -l="app=hello" -o wide
    

    출력은 다음과 비슷합니다.

    NAME                                         READY   STATUS    RESTARTS   AGE   IP            NODE                                        NOMINATED NODE   READINESS GATES
    multiarch-hello-deployment-65bfd784d-5xrrr   1/1     Running   0          95s   10.100.1.5    gke-my-cluster-arm-pool-e172cff7-shwc       <none>           <none>
    multiarch-hello-deployment-65bfd784d-7h94b   1/1     Running   0          95s   10.100.1.4    gke-my-cluster-arm-pool-e172cff7-shwc       <none>           <none>
    multiarch-hello-deployment-65bfd784d-7qbkz   1/1     Running   0          95s   10.100.1.7    gke-my-cluster-arm-pool-e172cff7-shwc       <none>           <none>
    multiarch-hello-deployment-65bfd784d-7wqb6   1/1     Running   0          95s   10.100.1.6    gke-my-cluster-arm-pool-e172cff7-shwc       <none>           <none>
    multiarch-hello-deployment-65bfd784d-h2g2k   1/1     Running   0          95s   10.100.0.19   gke-my-cluster-default-pool-32019863-b41t   <none>           <none>
    multiarch-hello-deployment-65bfd784d-lc9dc   1/1     Running   0          95s   10.100.0.18   gke-my-cluster-default-pool-32019863-b41t   <none>           <none>
    

    이 출력에는 포드가 Arm 노드 풀의 노드와 기본(x86) 노드 풀의 다른 노드에서 실행 중임을 나타내는 NODE 열이 포함됩니다.

  5. 다음 명령어를 실행하여 서비스에 액세스하고 출력을 확인합니다.

    for i in $(seq 1 6); do curl -w '\n' http://$external_ip; done
    

    출력은 다음과 비슷합니다.

    Hello from NODE:gke-my-cluster-arm-pool-e172cff7-shwc, POD:multiarch-hello-deployment-65bfd784d-7qbkz, CPU PLATFORM:linux/arm64
    Hello from NODE:gke-my-cluster-default-pool-32019863-b41t, POD:multiarch-hello-deployment-65bfd784d-lc9dc, CPU PLATFORM:linux/amd64
    Hello from NODE:gke-my-cluster-arm-pool-e172cff7-shwc, POD:multiarch-hello-deployment-65bfd784d-5xrrr, CPU PLATFORM:linux/arm64
    Hello from NODE:gke-my-cluster-arm-pool-e172cff7-shwc, POD:multiarch-hello-deployment-65bfd784d-7wqb6, CPU PLATFORM:linux/arm64
    Hello from NODE:gke-my-cluster-arm-pool-e172cff7-shwc, POD:multiarch-hello-deployment-65bfd784d-7h94b, CPU PLATFORM:linux/arm64
    Hello from NODE:gke-my-cluster-arm-pool-e172cff7-shwc, POD:multiarch-hello-deployment-65bfd784d-7wqb6, CPU PLATFORM:linux/arm64
    

    아키텍처 플랫폼 전반에서 실행되는 포드가 요청에 응답하는 것을 확인해야 합니다.

여러 아키텍처에서 워크로드를 원활하게 실행하도록 멀티 아키텍처 이미지를 빌드하고 배포했습니다.

삭제

이 튜토리얼에서 사용된 리소스 비용이 Google Cloud 계정에 청구되지 않도록 하려면 리소스가 포함된 프로젝트를 삭제하거나 프로젝트를 유지하고 개별 리소스를 삭제하세요.

튜토리얼을 완료한 후에는 할당량 사용을 줄이고 요금 청구를 중지하기 위해 만든 리소스를 삭제할 수 있습니다. 다음 섹션은 이러한 리소스를 삭제하거나 사용 중지하는 방법을 설명합니다.

프로젝트 삭제

비용이 청구되지 않도록 하는 가장 쉬운 방법은 튜토리얼에서 만든 프로젝트를 삭제하는 것입니다.

프로젝트를 삭제하는 방법은 다음과 같습니다.

  1. In the Google Cloud console, go to the Manage resources page.

    Go to Manage resources

  2. In the project list, select the project that you want to delete, and then click Delete.
  3. In the dialog, type the project ID, and then click Shut down to delete the project.

서비스, 클러스터, 저장소 삭제

전체 프로젝트를 삭제하지 않으려면 튜토리얼을 위해 만든 클러스터와 저장소를 삭제합니다.

  1. kubectl delete를 실행하여 애플리케이션의 서비스를 삭제합니다.

    kubectl delete service hello-service
    

    이 명령어는 배포를 노출할 때 만든 Compute Engine 부하 분산기를 삭제합니다.

  2. gcloud container clusters delete를 실행하여 클러스터를 삭제합니다.

    gcloud container clusters delete $CLUSTER_NAME --zone $ZONE
    
  3. 저장소를 삭제합니다.

    gcloud artifacts repositories delete docker-repo —location=us-central1 --async
    

다음 단계