IaC/CI CD Tool

7. Github Action Build and Push(with GCP Artifact Registry)

Somaz 2024. 6. 19. 17:15
728x90
반응형

Overview

오늘은 Github Actions와 GCP Workload Identity Federation을 연동하여,
빌드된 이미지를 GCP Artifact Registry에 업로드(Push) 하는 자동화 파이프라인을 구성하는 방법에 대해 알아본다.

 

기존에는 GCP 서비스 계정 키(JSON)를 직접 저장소에 저장하거나 Secret으로 관리하는 방식이 일반적이었으나,
Workload Identity Federation(WIF)을 활용하면 외부 인증 제공자(OIDC)를 통해 키리스(Keyless) 인증이 가능해지고,
보안성과 유지관리 측면에서 훨씬 유리하다.

 

 

Terraform으로 WIF 구성을 자동화하고,
Github Actions Workflow에서 GCP 인증 및 이미지 빌드, Artifact Registry Push까지의 흐름을 단계별로 구성해보자.

 

 

 

 

 

 

 

📅 관련 글

2023.04.27 - [IaC/CI CD Tool] - 1. Github Action이란?

2023.05.22 - [IaC/CI CD Tool] - 2. Github Action (With Syntax)

2023.05.22 - [IaC/CI CD Tool] - 3. Github Action (With Automate Pull Request)

2024.03.12 - [IaC/CI CD Tool] - 4. Github Action (With Matrix Strategy)

2024.03.12 - [IaC/CI CD Tool] - 5. Github Action (With Using jobs in a workflow & Choosing the runner for a job)

2024.03.15 - [IaC/CI CD Tool] - 6. Github Action (With Using Concurrency)

2024.05.28 - [IaC/CI CD Tool] - 7. Github Action Build and Push(with GCP Artifact Registry)

2024.05.28 - [IaC/CI CD Tool] - 7. Github Action Build and Push(with GCP Artifact Registry)

2024.06.20 - [IaC/CI CD Tool] - 8. Github Action Template 생성후 MarketPlace 등록하기

2024.11.10 - [IaC/CI CD Tool] - 9. Github Action Steps Context 활용법

2025.01.22 - [IaC/CI CD Tool] - 10. Github Action Hosted Runner 생성

 

 

 


 

 

 

Github Action Build and Push(with GCP Artifact Registry)

 

 

Github 에서 GCP 인증에 사용할 Service Account를 생성 후 Workload-identity-federation을 구성

 

Workload-identity-federation이란?

Google Cloud Platform (GCP)의 Workload Identity Federation은 external ID providers를 사용하여 Google Cloud 리소스에 접근 권한을 부여하는 기능이다. 이 기능을 사용하면 AWS, Azure, 혹은 어떤 OpenID Connect(OIDC) 호환 ID 제공자를 사용하는 조직도 GCP 서비스에 안전하게 액세스할 수 있다.

  • 멀티 클라우드 보안: Workload Identity Federation을 사용하면 사용자는 Google Cloud 리소스에 접근하기 위해 다른 클라우드 플랫폼의 인증 정보를 활용할 수 있다. 이는 멀티 클라우드 환경에서의 관리와 보안을 강화한다.
  • 안전한 액세스 관리: GCP의 IAM(Identity and Access Management) 정책을 사용하여 외부 시스템의 사용자에게 세밀한 액세스 제어를 제공할 수 있다. 이로 인해 보안이 강화된다.
  • 토큰 교환 및 갱신 자동화: Workload Identity Federation을 통해 외부 ID 제공자에서 Google Cloud로의 토큰 교환 및 갱신이 자동화되어, 사용자가 수동으로 크리덴셜을 관리할 필요가 없어진다.
  • 보다 간편한 통합: 외부 시스템과의 통합이 간소화되며, 이는 특히 다양한 클라우드 환경에서 작업하는 기업에 유용하다.

 

 

먼저 GCP Console 또는 Terraform으로 Artifact Registry 저장소를 생성해준다.

 

아래는 예시이다.

resource "google_artifact_registry_repository" "somaz_repo" {
  location      = "asia-northeast3"
  repository_id = "somaz"
  format        = "DOCKER"
  description   = "Docker images for somaz services"
}

 

  • 혹은 GCP Console에서 Artifact Registry > 저장소 만들기 메뉴를 통해 수동으로 생성할 수 있다.
 

 

 

테라폼을 사용해서 구성해준다.

## Service Account ##
module "service_accounts" {
  source       = "../../modules/service_accounts"
  project_id   = var.project
  names        = ["github-action"]
  display_name = "github-action"
  description  = "github-action admin"
}

## Workload Identity Federation ##

data "google_service_account" "github-action" {
  account_id = "github-action"

  depends_on = [module.service_accounts]
}

module "workload_identity_federation" {
  source      = "../../modules/workload_identity_federation"
  project_id  = var.project
  pool_id     = "pool-github-action"
  provider_id = "provider-github-action"

  attribute_mapping = {
    "google.subject"       = "assertion.sub"
    "attribute.actor"      = "assertion.actor"
    "attribute.aud"        = "assertion.aud"
    "attribute.repository" = "assertion.repository"
  }
  issuer_uri = "<https://token.actions.githubusercontent.com>"

  service_accounts = [
    {
      name           = data.google_service_account.github-action.name
      attribute      = "attribute.repository/somaz94/*" # attribute.repository/github repository/*
      all_identities = true
    },
    {
      name           = data.google_service_account.github-action.name
      attribute      = "attribute.repository/somaz94-2/*"
      all_identities = true
    }
  ]
}

 

 

Console에서 아래와 같이 확인가능하다.

 

 

 

 

 

 

아래와 같이 권한을 부여하였다.

 

 

 

 

 


 

 

 

 

 

 

 

Github Action Workflow 구성

간단하게 Build Workflow를 구성해본다.

  • Configure GCP credentials 부분에서 secrets.GCP_WORKLOAD_IDENTITY_PROVIDER 값으로는 projects/${project_id}/locations/global/workloadIdentityPools/pool-github-action/providers/provider-github-action 를 secret 으로 저장해준다.
  • secrets.GCP_SERVICE_ACCOUNT 값으로는 github-action@${project_id}.iam.gserviceaccount.com 를 secret으로 저장해준다.
name: 1.Build

on:
  push:
    branches:
      # - dev
      - '*'
      - '!main'
      - 'release/*'
    paths:
      - apps/game/**
    
  # Allows you to run this workflow manually from the Actions tab
  workflow_dispatch:
    inputs:
      environment:
        type: environment
        description: Select the environment
        required: true
      service:
        description: Which service to be built. game or admin or etc...
        required: true
        type: choice
        options:
          - game
  workflow_call:
    inputs:
      branch:
        description: Source branch name
        required: true
        type: string
      environment:
        description: Target environment
        required: true
        type: string
      service:
        description: Service to be built. game or admin or etc...
        required: true
        type: string

# A workflow run is made up of one or more jobs that can run sequentially or in parallel
jobs:
  build:
    name: Build Image
    runs-on: ubuntu-20.04
    env:
      PROJECT: somaz
      SERVICE: ${{ github.event.inputs.service || inputs.service }}
      ENVIRONMENT: ${{ github.event.inputs.environment || inputs.environment }}
    # Add "id-token" with the intended permissions.
    permissions:
      contents: read
      id-token: write

    steps:
      - name: Check out branch for workflow call
        id: checkout-for-workflow-call
        if: ${{ inputs.branch }}
        uses: actions/checkout@v4
        with:
          ref: ${{ inputs.branch }}

      - name: Check out branch for workflow dispatch
        if: ${{ steps.checkout-for-workflow-call.outcome == 'skipped' }}
        uses: actions/checkout@v4

      - name: Set GCP region JP for prod environment
        if: ${{ env.ENVIRONMENT == 'stage' || env.ENVIRONMENT == 'prod' }}
        id: set-prod-region
        run: |
          echo "GCP_REGION=asia-northeast1" >> $GITHUB_ENV

      - name: Set GCP region KR
        if: ${{ steps.set-prod-region.outcome == 'skipped' }}
        run: |
          echo "GCP_REGION=asia-northeast3" >> $GITHUB_ENV

      - name: Extract most recent environment from the last 50 commits
        if: github.event_name == 'push' 
        run: |
          git log -50 --pretty=%B
          COMMIT_MESSAGES=$(git log -50 --pretty=%B)
          ENVIRONMENT=$(echo "$COMMIT_MESSAGES" | grep -oP 'server\\(\\K[^)]*' | head -1 | sed 's/gcp_//')
          echo "ENVIRONMENT=$ENVIRONMENT" >> $GITHUB_ENV

      - name: Use input environment if provided
        if: github.event_name == 'workflow_dispatch' && inputs.environment
        run: |
          echo "ENVIRONMENT=${{ inputs.environment }}" >> $GITHUB_ENV

      - name: Use Default Service Name if push event
        if: github.event_name == 'push' 
        run: |
          echo "SERVICE=${{ inputs.service || 'game' }}" >> $GITHUB_ENV

      - name: Setup environment
        id: env_output
        run: |
          echo "Environment set to: ${{ env.ENVIRONMENT }}"
          echo "Service set to: ${{ env.SERVICE }}"

      - name: Configure GCP credentials
        id: auth         
        uses: google-github-actions/auth@v2
        with:
          token_format: access_token
          workload_identity_provider: ${{ secrets.GCP_WORKLOAD_IDENTITY_PROVIDER }}
          service_account: ${{ secrets.GCP_SERVICE_ACCOUNT }}
          access_token_lifetime: 500s     

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      - name: Login to Artifact Registry
        id : artifact
        uses: docker/login-action@v3
        with:
          registry: ${{ env.GCP_REGION }}-docker.pkg.dev
          username: oauth2accesstoken
          password: ${{ steps.auth.outputs.access_token }}

      - name: Set short sha
        id: vars
        run: |
          echo "short_sha=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Build, tag, and push image to GCP Artifact Registry
        uses: docker/build-push-action@v5
        env:
          GCP_SOMAZ_MGMT_PROJECT: ${{ secrets.GCP_SOMAZ_MGMT_PROJECT }}
          GAR_REGISTRY: ${{ env.GCP_REGION }}-docker.pkg.dev
          # SERVICE_NAME: ${{ inputs.service }}
          SERVICE_NAME: ${{ env.SERVICE }}
          GAR_REPOSITORY: ${{ env.PROJECT }}
          IMAGE_TAG: ${{ steps.vars.outputs.short_sha }}
        with:
          context: .
          file: ${{ env.SERVICE_NAME }}.Dockerfile
          push: true
          tags: ${{ env.GAR_REGISTRY }}/${{ env.GCP_SOMAZ_MGMT_PROJECT }}/${{ env.SERVICE }}-${{ env.PROJECT }}/${{ env.SERVICE }}-${{ env.PROJECT }}:${{ steps.vars.outputs.short_sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: Set branch var for workflow_dispatch
        if: github.event_name == 'workflow_dispatch'
        run: |
          echo "IS_BUILD_TRIGGERED_BY_WORKFLOW_DISPATCH=true" >> $GITHUB_ENV

      - name: Outputs build results
        run: |
          echo "target service: ${{ env.SERVICE }}"
          echo "resource branch for workflow dispatch: ${{ github.ref_name }}"
          echo "resource branch for workflow call: ${{ inputs.branch }}"
          echo "deploy environment: ${{ env.ENVIRONMENT }}"
          echo "gar region: ${{ env.GCP_REGION }}"
          echo "image tags: ${{ steps.vars.outputs.short_sha }}"

    outputs:
      tag: ${{ steps.vars.outputs.short_sha }}
      is_build_triggered_by_workflow_dispatch: ${{ env.IS_BUILD_TRIGGERED_BY_WORKFLOW_DISPATCH }}
      service: ${{ env.SERVICE }}
      environment: ${{ env.ENVIRONMENT }}
  • 이렇게 쉽게 Github Action과 GCP Workload Federation을 사용해서, Image Build 후 Push 과정을 구성할 수 있다.

 

 

 

 

 

이미지 태그 전략

  • short_sha: 커밋 SHA 기반으로 유니크한 태그 (e.g. 2f8a6c7)
  • 환경별 latest: dev-latest, stage-latest 와 같이 환경마다 latest 태그 관리
  • version 태그: 배포 Release를 위한 v1.2.3 태그 등

 

Workflow 내에서 short_sha 를 기본으로 사용하고, 추가적인 job 에서 latest 태그를 관리하도록 확장할 수 있다.

 

 

 

 

 

자주 발생하는 이슈 해결 팁

  • 403 PERMISSION_DENIED: Workload Identity Federation의 IAM 권한이 부족하거나 repository attribute가 잘못된 경우.
  • no such file or directory: Dockerfile: 서비스 이름과 Dockerfile 명명이 일치하는지 확인 필요.
  • denied: Permission denied for "xxx": GCP Artifact Registry 저장소의 권한 또는 OIDC 토큰 문제일 수 있음.

 

 

 

 

 

 

 

 

 


 

 

 

 

마무리

이번 글에서는 Github Actions에서 GCP Artifact Registry로 이미지를 Push 하기 위한 전체적인 과정을 살펴보았다.

  • Terraform으로 GCP Service Account 및 Workload Identity Federation 구성
  • Github Actions에서 WIF 기반으로 GCP 인증 수행
  • Buildx와 Docker build-push-action을 통해 이미지 빌드 & 푸시 자동화

 

이 방식을 활용하면,
서비스 계정 키를 직접 저장하거나 주기적으로 갱신하지 않아도 되는
보다 안전하고 현대적인 CI/CD 파이프라인을 구성할 수 있다.

 

또한, 입력된 service, environment, branch에 따라 다양한 컨텍스트로 이미지를 자동 빌드하고 푸시할 수 있어
멀티 서비스와 멀티 환경에 대한 유연한 대응이 가능하다.

 

 

앞으로 배포 워크플로우와도 연계하여,
Build → Push → Deploy로 이어지는 완전 자동화된 클라우드 네이티브 배포 체계를 구성해보자.

 

 

 

 

 

 

 

 


Reference

https://cloud.google.com/blog/products/devops-sre/using-github-actions-with-google-cloud-deploy?hl=en

https://gist.github.com/palewire/12c4b2b974ef735d22da7493cf7f4d37

https://cloud.google.com/iam/docs/workload-identity-federation?hl=ko&_gl=1*19geff0*_ga*MTk0NzM5NzU1My4xNjkwNDM2ODEz*_ga_WH2QY8WWF5*MTcxNjg3MjUwNi43NDkuMS4xNzE2ODc1MTYxLjAuMC4w&_ga=2.224028763.-1947397553.1690436813#impersonation

728x90
반응형