GitHub Actions에서 커밋 해시 기반 Docker 이미지 태그로 배포 자동화하기
이번 글에서는 GitHub Actions를 이용해 Docker 이미지를 빌드하고, 커밋 해시 기반 태그로 이미지를 푸시한 뒤, Kubernetes 배포에 사용하는 Kustomize overlay 파일까지 자동으로 수정하는 과정을 정리합니다.
현재 프로젝트에서는 로컬 Kubernetes 환경에 서비스를 배포하고 있으며, 이미지는 로컬 private registry에 푸시합니다. 이후 Argo CD가 Git 저장소의 변경 사항을 감지해 클러스터에 배포하는 GitOps 흐름을 사용하고 있습니다.
전체 흐름은 다음과 같습니다.
main브랜치에 코드가 push됨- GitHub Actions가 실행됨
- 커밋 해시를 기반으로 Docker 이미지 태그 생성
- Docker Compose로 각 서비스 이미지 빌드 및 push
- Kustomize overlay의 이미지 태그 수정
- 변경된
kustomization.yaml을 다시 GitHub에 commit/push - Argo CD가 변경 사항을 감지하고 배포
현재 사용 중인 GitHub Actions 예시
name: CD Local GitOps
on:
push:
branches:
- main
workflow_dispatch:
permissions: {}
concurrency:
group: cd-local-gitops-main
cancel-in-progress: false
env:
REGISTRY: 172.25.46.10:32000
jobs:
build-push-and-update-overlay:
name: Build, push, and update local overlay
if: github.actor != 'github-actions[bot]' && github.ref_name == 'main'
permissions:
contents: write
runs-on: [self-hosted, Linux, X64]
steps:
- name: Check out repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
ref: ${{ github.ref_name }}
persist-credentials: true
- name: Check Docker access
run: docker version
- name: Set image tag
run: |
echo "IMAGE_TAG=${GITHUB_SHA::12}" >> "$GITHUB_ENV"
echo "TAG=${GITHUB_SHA::12}" >> "$GITHUB_ENV"
- name: Create Compose env placeholders
shell: bash
run: |
set -euo pipefail
services=(
api-gateway
user-service
product-service
order-service
payment-service
notification-service
)
for service in "${services[@]}"; do
env_file="services/${service}/.env"
if [ ! -f "${env_file}" ]; then
touch "${env_file}"
fi
done
- name: Build and push service images
shell: bash
run: |
set -euo pipefail
docker compose -f docker/services.yaml build
docker compose -f docker/services.yaml push
- name: Set up Kustomize
uses: imranismail/setup-kustomize@2ba527d4d055ab63514ba50a99456fc35684947f
- name: Commit and push overlay update
shell: bash
run: |
set -euo pipefail
git fetch origin main
git rebase origin/main
services=(
user-service
product-service
order-service
payment-service
notification-service
api-gateway
)
pushd k8s/services/overlays/local
for service in "${services[@]}"; do
kustomize edit set image "${service}=${REGISTRY}/${service}:${IMAGE_TAG}"
done
popd
if git diff --quiet -- k8s/services/overlays/local/kustomization.yaml; then
echo "No overlay image tag changes to commit."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add k8s/services/overlays/local/kustomization.yaml
git commit -m "chore: update local image tags ${IMAGE_TAG}"
git push
Workflow 실행 조건 설정
먼저 workflow의 실행 조건과 공통 설정은 다음과 같습니다.
on:
push:
branches:
- main
workflow_dispatch:
permissions: {}
concurrency:
group: cd-local-gitops-main
cancel-in-progress: false
env:
REGISTRY: 172.25.46.10:32000
on.push.branches는 main 브랜치에 commit이 push되었을 때 workflow를 실행한다는 의미입니다.
workflow_dispatch:
workflow_dispatch는 GitHub Actions 페이지에서 사용자가 직접 workflow를 실행할 수 있도록 하는 설정입니다. 자동 실행뿐만 아니라 필요할 때 수동으로 배포를 실행할 수 있습니다.
permissions: {}
전역 permissions는 비워두고, 실제 권한이 필요한 job에서만 필요한 권한을 부여하도록 구성했습니다. 이렇게 하면 workflow 전체에 불필요한 권한이 부여되는 것을 줄일 수 있습니다.
concurrency:
group: cd-local-gitops-main
cancel-in-progress: false
concurrency는 동시에 실행되는 workflow를 제어하기 위한 설정입니다. 동일한 concurrency group을 사용하는 workflow가 중복 실행되지 않도록 제한할 수 있습니다.
이 설정이 필요한 이유는 이미지 빌드와 배포가 동시에 여러 번 실행될 경우, 어떤 이미지 태그가 최신 상태인지 혼란스러워질 수 있기 때문입니다. 특히 GitOps 방식에서는 Git 저장소의 최종 상태가 실제 배포 상태의 기준이 되므로, 배포 관련 workflow의 동시 실행은 주의해서 제어하는 것이 좋습니다.
env:
REGISTRY: 172.25.46.10:32000
env에는 workflow에서 공통으로 사용할 환경변수를 선언했습니다. 여기서는 Docker 이미지를 push할 private registry 주소를 REGISTRY로 정의했습니다.
Job 설정
다음은 실제 배포 작업을 수행하는 job 설정입니다.
jobs:
build-push-and-update-overlay:
name: Build, push, and update local overlay
if: github.actor != 'github-actions[bot]' && github.ref_name == 'main'
permissions:
contents: write
runs-on: [self-hosted, Linux, X64]
build-push-and-update-overlay는 job의 식별자입니다. GitHub Actions 화면에서는 name에 지정한 Build, push, and update local overlay라는 이름으로 표시됩니다.
if: github.actor != 'github-actions[bot]' && github.ref_name == 'main'
이 조건은 workflow가 실행되더라도 실제 job이 실행될지 한 번 더 검사합니다.
여기서 중요한 부분은 다음 조건입니다.
github.actor != 'github-actions[bot]'
이 workflow는 마지막 단계에서 kustomization.yaml을 수정한 뒤 github-actions[bot] 계정으로 다시 commit/push합니다. 만약 이 조건이 없다면, bot이 push한 commit으로 인해 workflow가 다시 실행될 수 있습니다.
즉, 이 조건은 GitHub Actions가 만든 commit으로 인해 workflow가 반복 실행되는 상황을 막기 위한 장치입니다.
permissions:
contents: write
이 job은 저장소의 파일을 수정하고 다시 push해야 하므로 contents: write 권한이 필요합니다.
runs-on: [self-hosted, Linux, X64]
runs-on은 job을 실행할 runner를 지정합니다. 현재는 로컬 환경의 Docker와 private registry를 사용해야 하므로 GitHub-hosted runner가 아니라 self-hosted runner를 사용하도록 설정했습니다.
이 runner에는 Docker 명령어를 실행할 수 있는 환경이 미리 준비되어 있어야 합니다.
Repository checkout
- name: Check out repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5
with:
ref: ${{ github.ref_name }}
persist-credentials: true
첫 번째 단계에서는 actions/checkout을 사용해 현재 브랜치의 코드를 runner로 가져옵니다.
ref: ${{ github.ref_name }}
현재 workflow가 실행된 브랜치를 checkout합니다.
persist-credentials: true
이후 단계에서 GitHub에 commit/push를 해야 하므로 인증 정보를 유지합니다.
Docker 사용 가능 여부 확인
- name: Check Docker access
run: docker version
self-hosted runner에서 Docker 명령어를 정상적으로 사용할 수 있는지 확인합니다.
이 단계에서 실패한다면 runner 환경에서 Docker가 설치되어 있지 않거나, 현재 runner 사용자가 Docker에 접근할 권한이 없는 상태일 수 있습니다.
커밋 해시 기반 이미지 태그 생성
- name: Set image tag
run: |
echo "IMAGE_TAG=${GITHUB_SHA::12}" >> "$GITHUB_ENV"
echo "TAG=${GITHUB_SHA::12}" >> "$GITHUB_ENV"
이 단계에서는 현재 commit hash의 앞 12자리를 이미지 태그로 사용합니다.
예를 들어 commit hash가 다음과 같다면,
a1b2c3d4e5f67890...
이미지 태그는 다음처럼 생성됩니다.
a1b2c3d4e5f6
latest 태그를 사용하면 실제 어떤 commit으로 빌드된 이미지인지 추적하기 어렵습니다. 반면 commit hash 기반 태그를 사용하면 특정 이미지가 어떤 코드 상태에서 만들어졌는지 추적하기 쉬워집니다.
echo "IMAGE_TAG=${GITHUB_SHA::12}" >> "$GITHUB_ENV"
$GITHUB_ENV에 값을 기록하면 이후 step에서도 해당 환경변수를 사용할 수 있습니다.
여기서는 IMAGE_TAG와 TAG를 모두 같은 값으로 설정했습니다. docker compose 파일에서 TAG 환경변수를 참조하고 있다면, 이 값이 이미지 태그로 사용됩니다.
Docker Compose용 env 파일 생성
- name: Create Compose env placeholders
shell: bash
run: |
set -euo pipefail
services=(
api-gateway
user-service
product-service
order-service
payment-service
notification-service
)
for service in "${services[@]}"; do
env_file="services/${service}/.env"
if [ ! -f "${env_file}" ]; then
touch "${env_file}"
fi
done
현재 docker compose 설정에서는 각 서비스의 .env 파일을 참조하고 있습니다. 하지만 GitHub에 .env 파일을 올리지 않는 경우, runner 환경에서는 해당 파일이 존재하지 않아 compose 실행 중 오류가 발생할 수 있습니다.
이를 방지하기 위해 각 서비스 디렉터리에 .env 파일이 없으면 빈 파일을 생성하도록 했습니다.
set -euo pipefail
이 설정은 bash script를 더 안전하게 실행하기 위해 사용합니다.
-e: 명령어 실패 시 즉시 종료-u: 선언되지 않은 변수를 사용하면 오류 처리-o pipefail: pipe 중간에서 실패한 명령도 실패로 처리
Docker 이미지 빌드 및 Push
- name: Build and push service images
shell: bash
run: |
set -euo pipefail
docker compose -f docker/services.yaml build
docker compose -f docker/services.yaml push
이 단계가 실제 Docker 이미지를 빌드하고 registry에 push하는 부분입니다.
docker compose -f docker/services.yaml build
docker/services.yaml에 정의된 서비스 이미지를 빌드합니다.
docker compose -f docker/services.yaml push
빌드된 이미지를 registry로 push합니다.
이 workflow에서는 직접 Docker 명령어를 실행하고 있지만, 상황에 따라 docker/build-push-action 같은 미리 만들어진 GitHub Action을 사용할 수도 있습니다.
다만 현재처럼 self-hosted runner에서 로컬 private registry를 사용하고, 여러 서비스를 compose 기반으로 한 번에 빌드하는 경우에는 직접 docker compose 명령어를 실행하는 방식도 단순하고 직관적입니다.
Kustomize 설치
- name: Set up Kustomize
uses: imranismail/setup-kustomize@2ba527d4d055ab63514ba50a99456fc35684947f
이미지를 빌드하고 push한 뒤에는 Kubernetes manifest에서 사용하는 이미지 태그를 새 태그로 변경해야 합니다.
이 프로젝트에서는 Kustomize를 사용하고 있으므로, kustomize edit set image 명령어를 실행하기 위해 Kustomize를 설치합니다.
Kustomize overlay 이미지 태그 수정 및 commit
- name: Commit and push overlay update
shell: bash
run: |
set -euo pipefail
git fetch origin main
git rebase origin/main
services=(
user-service
product-service
order-service
payment-service
notification-service
api-gateway
)
pushd k8s/services/overlays/local
for service in "${services[@]}"; do
kustomize edit set image "${service}=${REGISTRY}/${service}:${IMAGE_TAG}"
done
popd
if git diff --quiet -- k8s/services/overlays/local/kustomization.yaml; then
echo "No overlay image tag changes to commit."
exit 0
fi
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add k8s/services/overlays/local/kustomization.yaml
git commit -m "chore: update local image tags ${IMAGE_TAG}"
git push
이 단계에서는 Kustomize overlay에 정의된 서비스 이미지 태그를 새로 빌드한 이미지 태그로 변경합니다.
먼저 원격 저장소의 최신 상태를 가져온 뒤 rebase합니다.
git fetch origin main
git rebase origin/main
이미 workflow가 실행되는 동안 다른 commit이 main에 추가되었을 수 있으므로, push 전에 최신 상태를 반영하기 위한 처리입니다.
이후 이미지 태그를 변경할 서비스 목록을 정의합니다.
services=(
user-service
product-service
order-service
payment-service
notification-service
api-gateway
)
그리고 Kustomize overlay 디렉터리로 이동합니다.
pushd k8s/services/overlays/local
각 서비스에 대해 kustomize edit set image를 실행합니다.
for service in "${services[@]}"; do
kustomize edit set image "${service}=${REGISTRY}/${service}:${IMAGE_TAG}"
done
이 명령어를 실행하면 kustomization.yaml의 이미지 설정이 새 태그로 변경됩니다.
작업이 끝나면 다시 기존 working directory로 돌아옵니다.
popd
변경 사항이 없을 때는 commit하지 않기
if git diff --quiet -- k8s/services/overlays/local/kustomization.yaml; then
echo "No overlay image tag changes to commit."
exit 0
fi
이 부분은 kustomization.yaml에 실제 변경 사항이 있는지 확인합니다.
변경 사항이 없다면 굳이 commit을 만들 필요가 없으므로 workflow를 정상 종료합니다.
GitHub Actions Bot 계정으로 commit/push
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add k8s/services/overlays/local/kustomization.yaml
git commit -m "chore: update local image tags ${IMAGE_TAG}"
git push
변경 사항이 있다면 GitHub Actions bot 계정으로 commit을 생성하고 push합니다.
이 commit이 Git 저장소에 반영되면, Argo CD는 변경된 kustomization.yaml을 기준으로 새로운 이미지 태그를 감지하고 Kubernetes 클러스터에 배포할 수 있습니다.
정리
이번 설정을 통해 main 브랜치에 push하거나 GitHub Actions 화면에서 workflow를 수동 실행했을 때 다음 작업을 자동화할 수 있었습니다.
- repository checkout
- commit hash 기반 이미지 태그 생성
- Docker Compose 기반 이미지 빌드
- private registry로 이미지 push
- Kustomize overlay의 이미지 태그 수정
- 변경된 manifest 파일 commit/push
- Argo CD를 통한 GitOps 배포 연계
특히 이미지 태그를 commit hash 기반으로 관리하면, 어떤 코드가 어떤 이미지로 빌드되었는지 추적하기 쉬워집니다. 또한 Kustomize overlay 파일까지 자동으로 수정하므로, 이미지 빌드 이후 Kubernetes 배포 manifest를 직접 수정하는 과정을 줄일 수 있습니다.
이 구조를 응용하면 로컬 Kubernetes 환경뿐만 아니라 개발, 스테이징, 운영 환경별 overlay를 분리해 더 체계적인 GitOps 배포 흐름으로 확장할 수 있습니다.
Reference
'Devops > GitHub Actions' 카테고리의 다른 글
| GitHub Actions Self-hosted Runner를 WSL 환경에 설치하고 서비스로 실행하기 (0) | 2026.05.25 |
|---|