GitHub Actions 워크플로를 사용하여 .NET 앱을 Google Kubernetes Engine에 배포


이 튜토리얼에서는 .NET을 사용하는 ASP.NET 모델-뷰-컨트롤러(MVC) 웹 애플리케이션을 Google Kubernetes Engine(GKE)에 배포하는 워크플로인 GitHub 작업을 사용하는 방법을 설명합니다.

이 튜토리얼은 Microsoft .NET, GitHub Actions, GKE에 대한 기본 지식이 있는 개발자와 DevOps 엔지니어를 대상으로 합니다. 또한 이 튜토리얼을 실행하려면 GitHub 계정이 필요합니다.

목표

.NET 6 .0을 사용하고 Linux에서 실행되는 ASP.NET Core 웹 애플리케이션을 Google Kubernetes Engine에 배포합니다.

다음 다이어그램은 Google Kubernetes Engine(GKE)에 ASP.NET MVC 웹 애플리케이션을 배포하는 GitHub Actions 워크플로를 보여줍니다.

GitHub Actions 워크플로의 개념 다이어그램

이 튜토리얼에서는 목표를 달성하기 위해 다음 작업을 완료하는 방법을 보여줍니다.

  • GitHub 저장소 만들기
  • 인증 구성
  • GKE 클러스터 및 Artifact Registry 저장소 배포
  • GitHub Actions 워크플로 만들기

비용

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

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

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

시작하기 전에

  1. Google Cloud 계정에 로그인합니다. Google Cloud를 처음 사용하는 경우 계정을 만들고 Google 제품의 실제 성능을 평가해 보세요. 신규 고객에게는 워크로드를 실행, 테스트, 배포하는 데 사용할 수 있는 $300의 무료 크레딧이 제공됩니다.
  2. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기로 이동

  3. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  4. API Artifact Registry and Google Kubernetes Engine 사용 설정

    API 사용 설정

  5. Google Cloud Console의 프로젝트 선택기 페이지에서 Google Cloud 프로젝트를 선택하거나 만듭니다.

    프로젝트 선택기로 이동

  6. Google Cloud 프로젝트에 결제가 사용 설정되어 있는지 확인합니다.

  7. API Artifact Registry and Google Kubernetes Engine 사용 설정

    API 사용 설정

GitHub 저장소 만들기

GitHub 계정에 dotnet-docs-samples 저장소의 포크를 만듭니다. GitHub Actions로 빌드하므로 이 단계가 필요합니다.

  1. dotnet-docs-samples GitHub 저장소로 이동합니다.
  2. 포크를 클릭합니다.
  3. 새 포크 만들기 페이지에서 다음을 설정합니다.

    • 소유자 - GitHub 계정
    • 저장소 이름 - dotnet-docs-samples
  4. 포크 만들기를 클릭합니다.

인증 구성

GitHub Actions에서 Google Cloud 프로젝트의 리소스를 인증하고 액세스할 수 있도록 Google Cloud 프로젝트를 준비합니다.

워크로드 아이덴티티 제휴 풀 및 공급업체 만들기

GitHub Actions에서 Google Cloud를 인증하고 GKE에 배포하도록 허용하려면 워크로드 아이덴티티 제휴를 사용합니다. 워크로드 아이덴티티 제휴를 사용하면 GitHub Actions 워크로드에 서비스 계정 키를 저장하고 관리할 필요가 없습니다.

워크로드 아이덴티티 제휴를 사용하려면 워크로드 아이덴티티 풀과 제공업체가 필요합니다. 전용 프로젝트를 사용하여 워크로드 아이덴티티 풀 및 제공업체를 관리하는 것이 좋습니다. 이 튜토리얼에서는 편의상 GKE 클러스터와 동일한 프로젝트에서 풀과 공급업체를 만듭니다.

  1. Google Cloud 콘솔을 엽니다.

  2. Google Cloud 콘솔에서 Cloud Shell을 활성화합니다.

    Cloud Shell 활성화

    Google Cloud 콘솔 하단에서 Cloud Shell 세션이 시작되고 명령줄 프롬프트가 표시됩니다. Cloud Shell은 Google Cloud CLI가 사전 설치된 셸 환경으로, 현재 프로젝트의 값이 이미 설정되어 있습니다. 세션이 초기화되는 데 몇 초 정도 걸릴 수 있습니다.

  3. 새 워크로드 아이덴티티 풀을 만듭니다.

    gcloud iam workload-identity-pools create github-actions \
        --location="global" \
        --description="GitHub Actions tutorial" \
        --display-name="GitHub Actions"
    
  4. GitHub Actions를 워크로드 아이덴티티 풀 공급업체로 추가합니다.

    gcloud iam workload-identity-pools providers create-oidc github-actions-oidc \
        --location="global" \
        --workload-identity-pool=github-actions \
        --issuer-uri="https://token.actions.githubusercontent.com/" \
        --attribute-mapping="google.subject=assertion.sub"
    

서비스 계정 만들기

  1. Cloud Shell에서 GitHub Actions가 Docker 이미지를 게시하고 GKE에 배포하는 데 사용할 수 있는 서비스 계정을 만듭니다.

    SERVICE_ACCOUNT=$(gcloud iam service-accounts create github-actions-workflow \
      --display-name "GitHub Actions workflow" \
      --format "value(email)")
    
  2. 서비스 계정에 Artifact Registry 작성자 역할(roles/artifactregistry.writer)을 부여하여 GitHub Actions에서 Artifact Registry로 푸시하도록 허용합니다.

    gcloud projects add-iam-policy-binding $(gcloud config get-value core/project) \
      --member serviceAccount:$SERVICE_ACCOUNT \
      --role roles/artifactregistry.writer
    
  3. 서비스 계정에 Google Kubernetes Engine 개발자 역할(roles/container.developer)을 부여하여 GitHub Actions에서 Artifact Registry에 푸시하도록 허용합니다.

    gcloud projects add-iam-policy-binding $(gcloud config get-value core/project) \
      --member serviceAccount:$SERVICE_ACCOUNT \
      --role roles/container.developer
    

GitHub Actions 워크플로가 서비스 계정을 사용하도록 허용

GitHub Actions 워크플로가 서비스 계정을 가장하고 사용하도록 허용합니다.

  1. GitHub Actions 워크플로에서 사용하는 subject가 포함된 환경 변수를 초기화합니다. subject는 GitHub 저장소 및 브랜치를 고유하게 식별하는 사용자 이름과 비슷합니다.

    SUBJECT=repo:OWNER/dotnet-docs-samples:ref:refs/heads/main
    

    OWNER을 GitHub 사용자 이름으로 바꿉니다.

  2. subject에 서비스 계정을 가장할 수 있는 권한을 부여합니다.

    PROJECT_NUMBER=$(gcloud projects describe $(gcloud config get-value core/project) --format='value(projectNumber)')
    
    gcloud iam service-accounts add-iam-policy-binding $SERVICE_ACCOUNT \
      --role=roles/iam.workloadIdentityUser \
      --member="principal://iam.googleapis.com/projects/$PROJECT_NUMBER/locations/global/workloadIdentityPools/github-actions/subject/$SUBJECT"
    

GKE 클러스터 및 Artifact Registry 저장소 배포

  1. Docker 이미지의 저장소를 만듭니다.

    gcloud artifacts repositories create clouddemo \
      --repository-format=docker \
      --location=us-central1
    
  2. Compute Engine 기본 서비스 계정이 저장소에 액세스하도록 허용합니다.

    gcloud projects add-iam-policy-binding $(gcloud config get-value core/project) \
      --member=serviceAccount:$PROJECT_NUMBER-compute@developer.gserviceaccount.com \
      --role=roles/artifactregistry.reader
    
  3. 클러스터를 만듭니다.

    gcloud container clusters create clouddemo-linux \
      --enable-ip-alias \
      --zone us-central1-a
    

    이 작업을 완료하는 데 몇 분이 소요될 수 있습니다.

  4. 클러스터의 프로젝트 이름과 프로젝트 번호를 가져옵니다.

    echo "Project ID: $(gcloud config get-value core/project)"
    echo "Project Number: $(gcloud projects describe $(gcloud config get-value core/project) --format=value\(projectNumber\))"
    

    이후에 이 값이 필요합니다.

GitHub Actions 워크플로 만들기

이제 GitHub Actions를 사용하여 지속적 통합을 설정할 수 있습니다. GitHub Actions 워크플로는 Git 저장소로 푸시되는 커밋마다 코드를 빌드하고 빌드 아티팩트를 Docker 컨테이너로 패키징합니다. 그러면 컨테이너가 Artifact Registry에 게시됩니다.

저장소에 이미 다음 Dockerfile이 포함됩니다.

#
# Copyright 2020 Google LLC
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

FROM mcr.microsoft.com/dotnet/aspnet:6.0
EXPOSE 8080

#------------------------------------------------------------------------------
# Copy publishing artifacts.
#------------------------------------------------------------------------------

WORKDIR /app
COPY CloudDemo.MvcCore/bin/Release/net6.0/publish/ /app/

ENV ASPNETCORE_URLS=http://0.0.0.0:8080

#------------------------------------------------------------------------------
# Run application in Kestrel.
#------------------------------------------------------------------------------

ENTRYPOINT ["dotnet", "CloudDemo.MvcCore.dll"]

저장소에는 Kubernetes 매니페스트도 포함됩니다.

#
# Copyright 2020 Google LLC
#
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements.  See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership.  The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License.  You may obtain a copy of the License at
#
#   http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied.  See the License for the
# specific language governing permissions and limitations
# under the License.
#

apiVersion: v1
kind: Service
metadata:
  name: clouddemo-netcore
spec:
  ports:
  - port: 80
    targetPort: 8080
    protocol: TCP
    name: http
  selector:
    app: clouddemo-netcore
  type: NodePort

---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: clouddemo-netcore
spec:
  defaultBackend:
    service:
      name: clouddemo-netcore
      port:
        number: 80

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: clouddemo-netcore
spec:
  replicas: 2
  selector:
    matchLabels:
      app: clouddemo-netcore
  template:
    metadata:
      labels:
        app: clouddemo-netcore
    spec:
      containers:
      - name: clouddemo-netcore
        image: CLOUDDEMO_IMAGE
        ports:
          - containerPort: 8080
        livenessProbe:      # Used by deployment controller
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5
        readinessProbe:     # Used by Ingress/GCLB
          httpGet:
            path: /health
            port: 8080
          initialDelaySeconds: 3
          periodSeconds: 5
        resources:
          limits:
            memory: 1024Mi
          requests:
            memory: 256Mi

다음을 수행하는 GitHub Actions 워크플로를 만듭니다.

  • 워크로드 아이덴티티 제휴와 이전에 만든 서비스 계정을 사용하여 Google Cloud에 인증합니다.
  • Docker 이미지를 빌드하고 Artifact Registry에 배포합니다.
  • GKE에 Docker 이미지를 배포합니다.

GitHub Actions 워크플로를 만들려면 다음을 수행합니다.

  1. GitHub에서 dotnet-docs-samples 저장소의 포크로 이동합니다.
  2. 파일 추가 > 새 파일 만들기를 클릭합니다.
  3. 파일 이름 지정 텍스트 필드에 다음 이름을 입력합니다.

    .github/workflows/deploy-gke.yaml
    
  4. 다음 코드를 파일에 복사합니다.

    name: Build and Deploy to GKE
    
    on:
      push:
        branches:
          - main
    
    env:
      PROJECT_ID: PROJECT_ID
      PROJECT_NUMBER: PROJECT_NUMBER
    
      CLUSTER: clouddemo-linux
      CLUSTER_ZONE: us-central1-a
    
      REPOSITORY: clouddemo
      REPOSITORY_REGION: us-central1
    
      IMAGE: clouddemo
    
    jobs:
      build:
        runs-on: ubuntu-latest
        permissions:
          id-token: write
          contents: read
    
        steps:
        - name: Checkout
          uses: actions/checkout@v3
    
        #
        # Authenticate to Google Cloud using workload identity federation
        #
        - id: 'auth'
          name: 'Obtain access token by using workload identity federation'
          uses: 'google-github-actions/auth@v0'
          with:
            create_credentials_file: true
            token_format: access_token
            workload_identity_provider: projects/${{ env.PROJECT_NUMBER }}/locations/global/workloadIdentityPools/github-actions/providers/github-actions-oidc
            service_account: github-actions-workflow@${{ env.PROJECT_ID }}.iam.gserviceaccount.com
    
        - name: Connect to Artifact Registry
          run: |-
            echo ${{ steps.auth.outputs.access_token }} | docker login -u oauth2accesstoken --password-stdin https://${{ env.REPOSITORY_REGION }}-docker.pkg.dev
    
        - name: Connect to GKE
          uses: google-github-actions/get-gke-credentials@v0
          with:
            cluster_name: ${{ env.CLUSTER }}
            location: ${{ env.CLUSTER_ZONE }}
    
        #
        # Build the .NET code
        #
        - name: Build solution
          run: |-
            dotnet publish applications/clouddemo/netcore/CloudDemo.MvcCore.sln \
                --configuration Release \
                --framework net6.0
    
        #
        # Build the Docker image and push it to Artifact Registry
        #
        - name: Create image tag
          run: echo "IMAGE_TAG=${{ env.REPOSITORY_REGION }}-docker.pkg.dev/${{ env.PROJECT_ID }}/${{ env.REPOSITORY }}/${{ env.IMAGE }}:$GITHUB_SHA" >> $GITHUB_ENV
    
        - name: Lock image version in deployment.yaml
          run: sed -i 's|CLOUDDEMO_IMAGE|${{ env.IMAGE_TAG }}|g' applications/clouddemo/netcore/deployment.yaml
    
        - name: Build Docker image
          run: docker build --tag "${{ env.IMAGE_TAG }}" applications/clouddemo/netcore
    
        - name: Publish Docker image to Google Artifact Registry
          run: docker push "${{ env.IMAGE_TAG }}"
    
        #
        # Deploy to GKE
        #
        - name: Deploy to GKE
          run: kubectl apply -f applications/clouddemo/netcore/deployment.yaml
        

    다음 값을 바꿉니다.

    • PROJECT_ID: GKE 클러스터가 포함된 프로젝트의 프로젝트 ID입니다.
    • PROJECT_NUMBER: GKE 클러스터가 포함된 프로젝트의 프로젝트 번호입니다.
  5. 새 파일 커밋 섹션에서 커밋 메시지(예: Add workflow)를 입력하고 새 파일 커밋을 클릭합니다.

  6. 메뉴에서 작업을 클릭하고 워크플로가 완료될 때까지 기다립니다.

    워크플로를 완료하는 데 몇 분 정도 걸릴 수 있습니다.

  7. Google Cloud 콘솔에서 서비스 및 인그레스 페이지를 새로고침합니다.

    서비스 및 인그레스로 이동

  8. 인그레스 탭으로 이동합니다.

  9. clouddemo 클러스터의 인그레스 서비스를 찾고 상태가 확인으로 전환될 때까지 기다립니다. 이 작업은 몇 분 정도 걸릴 수 있습니다.

  10. 같은 행의 프런트엔드 열에서 링크를 엽니다. 부하 분산기를 사용할 수 있게 될 때까지 몇 분이 걸리므로 CloudDemo 앱이 처음에는 로드되지 않을 수 있습니다. 부하 분산기가 준비되었으면 프로덕션 클러스터에서 실행되는 CloudDemo 앱이 커스텀 제목과 함께 표시됩니다.

삭제

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

GitHub 저장소 삭제

GitHub 저장소를 삭제하려면 저장소 삭제를 참조하세요. 저장소를 삭제하면 모든 소스 코드 변경사항도 손실됩니다.

프로젝트 삭제

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

    Google Cloud 프로젝트를 삭제합니다.

    gcloud projects delete PROJECT_ID

다음 단계