GitHub Actions 강좌 #6 Docker 빌드와 GHCR 푸시: 태그 전략과 빌드 캐시

4 분 소요

이번 편부터 세 편은 조립 실전입니다. 첫 파이프라인은 CI의 대표 산출물인 컨테이너 이미지입니다. 코드가 main에 병합되면 이미지를 빌드해 레지스트리에 올리는 흐름을 만들며, 레지스트리는 GitHub에 내장된 GHCR(GitHub Container Registry)를 씁니다. GHCR가 무엇이고 Docker Hub와 어떻게 다른지는 GHCR 소개 글에 정리해 두었고, Dockerfile 작성 자체는 도커 기초 강좌를 전제합니다. 여기서는 GitHub Actions 관점의 조립에 집중하겠습니다.

전체 워크플로 먼저 #

완성형을 먼저 보고 조각을 해설하는 순서로 가겠습니다.

.github/workflows/image.yml
# .github/workflows/image.yml
name: build image

on:
  push:
    branches: [main]
    tags: ["v*"]

permissions:
  contents: read
  packages: write        # GHCR 푸시에 필요

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - uses: docker/metadata-action@v5
        id: meta
        with:
          images: ghcr.io/${{ github.repository }}

      - uses: docker/build-push-action@v6
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

#2와 #3에서 배운 문법이 전부 실전 투입됐습니다. 스텝 출력(steps.meta.outputs), GITHUB_TOKEN, 그리고 처음 등장한 permissions 블록입니다.

로그인: 별도 시크릿이 필요 없습니다 #

GHCR의 실무적 장점이 로그인 스텝에 그대로 드러납니다. Docker Hub라면 계정 토큰을 만들어 secrets에 등록해야 하지만, GHCR는 #3에서 본 자동 발급 토큰 GITHUB_TOKEN으로 로그인이 끝납니다. 관리할 시크릿이 하나도 늘지 않는 것입니다. 대신 워크플로에 permissions: packages: write를 선언해 토큰에 패키지 쓰기 권한을 줘야 하며, 이 선언 방식 자체가 #9에서 다룰 최소 권한 원칙의 실례입니다. 필요한 권한을 필요한 워크플로에만 명시하는 형태이기 때문입니다.

태그 전략: metadata-action에 맡기기 #

이미지 태그를 무엇으로 할지는 손으로 정하면 금방 무너지는 영역입니다. docker/metadata-action은 실행을 깨운 이벤트에 따라 태그를 자동 결정합니다. 기본 동작만으로 다음이 나옵니다.

이벤트만들어지는 태그
main 푸시main (+ 커밋 SHA 태그 옵션)
v1.4.0 태그 푸시1.4.0, 1.4, latest
PRpr-123

즉 워크플로 하나가 “개발 최신은 main 태그, 릴리스는 버전 태그” 정책을 자동으로 수행합니다. 태그 규칙을 바꾸고 싶으면 metadata-action의 tags 입력으로 규칙을 선언하는 방식이라, 태그 정책이 YAML에 코드로 남습니다. 운영에서 중요한 습관은 배포는 latest가 아니라 고정 태그(버전 또는 SHA)로 하는 것입니다. latest는 어제와 오늘이 다른 이미지일 수 있어, 어떤 코드가 떠 있는지 답할 수 없게 만들기 때문입니다.

빌드 캐시: gha 타입 한 쌍 #

도커 빌드는 레이어 캐시가 있어야 빠른데, 러너는 매번 깨끗한 머신이라 그대로는 캐시가 없습니다(#4의 문제가 도커에서 재현되는 것입니다). build-push-action의 두 줄이 그 답입니다.

.github/workflows/image.yml
          cache-from: type=gha
          cache-to: type=gha,mode=max

빌드 레이어 캐시를 GitHub Actions의 캐시 저장소에 밀어 넣고 다음 실행에서 꺼내 쓰는 설정입니다. mode=max는 중간 레이어까지 전부 저장해 적중률을 높입니다. Dockerfile이 캐시 친화적으로 작성되어 있을수록(의존성 설치 레이어와 코드 복사 레이어의 분리) 효과가 커지는데, 그 작성법은 도커 중급 강좌의 주제이므로 여기서는 연결만 해 둡니다. 멀티 아키텍처 빌드(amd64와 arm64 동시)가 필요하면 docker/setup-qemu-action과 platforms 입력을 추가하는 확장 경로도 같은 액션 위에 있습니다.

만든 이미지는 어디로 가는가 #

푸시된 이미지는 저장소의 Packages 탭에 나타나고, 주소는 ghcr.io/<owner>/<repo>입니다. 이 이미지를 실제 인프라에 배포하는 것이 다음 편의 주제이며, 테라폼 실전 강좌 #8에서 “이미지는 준비되어 있다고 전제"하고 미뤄 둔 그 앞 단계가 오늘로 채워진 셈입니다.

정리 #

이번 글에서 다룬 내용입니다.

  • GHCR 로그인은 GITHUB_TOKEN으로 끝나며, permissions에 packages write 선언이 짝입니다
  • 태그는 손으로 정하지 않고 metadata-action에 맡깁니다. 이벤트별 태그 정책이 YAML 코드로 남습니다
  • 배포는 latest가 아니라 버전·SHA 고정 태그로 합니다
  • 빌드 캐시는 cache-from/to의 gha 타입 한 쌍이고, 효과는 Dockerfile의 캐시 친화성에 비례합니다
  • 산출물은 ghcr.io/owner/repo로 올라가며, 이것을 인프라에 배포하는 것이 다음 편입니다

다음 글(#7 AWS 배포와 OIDC)에서는 액세스 키 없이 워크플로가 AWS 권한을 얻는 OIDC 연동을 완성하고, 빌드한 것을 실제로 배포합니다.

X