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.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://gist.github.com/palewire/12c4b2b974ef735d22da7493cf7f4d37
'IaC > CI CD Tool' 카테고리의 다른 글
7. Gitlab CI Template 활용 (0) | 2024.06.27 |
---|---|
6. Gitlab CI Build(with GCP Artifact Registry, Harbor) (0) | 2024.06.24 |
ArgoCD SSO 구성 가이드(GCP Oauth) (0) | 2024.04.14 |
6. Github Action (With Using Concurrency) (0) | 2024.03.20 |
5. Github Action (With Using jobs in a workflow & Choosing the runner for a job) (0) | 2024.03.15 |