DEVOPS

Docker + GitHub Actions CI/CD 완전 가이드 -- Dockerfile부터 자동 배포까지

junetapa 2026. 2. 19 업데이트 2026. 6. 6 12 min read

Docker 개념과 설치부터 Dockerfile 작성, Docker Compose로 멀티 컨테이너 구성, GitHub Actions 워크플로 작성, 자동 빌드와 테스트, Docker Hub 푸시, 서버 자동 배포까지 -- 실무에서 바로 복사해 쓸 수 있는 설정 파일 위주로 정리했다.

2026년 6월 업데이트

2026년 6월 업데이트 — GitHub Actions 러너가 2026-06-02부터 Node 24를 기본으로 강제한다. 이에 맞춰 모든 액션 핀을 최신 안정 버전으로 갱신했다(checkout@v5, setup-node@v5, build-push-action@v6). 또한 Docker Hub 시크릿 대신 GHCR(GitHub Container Registry)와 내장 GITHUB_TOKEN을 쓰는 방식, AWS 배포는 장기 자격증명 없이 OIDC 키리스 인증으로, 이미지에는 빌드 프로버넌스 서명까지 적용하는 실동작 워크플로로 교체했다.

왜 Docker가 필요한가

개발 환경에서는 잘 되던 코드가 서버에서 안 돌아가는 경험, 한 번쯤 있을 것이다. "내 컴퓨터에서는 되는데" -- Docker는 이 문제를 근본적으로 해결한다.

01

환경 일관성 보장

개발 PC, 테스트 서버, 프로덕션 서버 어디서나 동일한 환경에서 실행된다.

02

빠른 배포와 롤백

이미지 빌드 후 어디서든 동일하게 실행된다. 문제가 생겨도 이전 이미지 태그로 즉시 롤백 가능하다.

03

격리된 실행 환경

각 서비스가 독립된 컨테이너에서 실행되어 의존성 충돌이 없다. Node.js 22와 24를 동시에 운영할 수 있다.

04

CI/CD 파이프라인의 기반

GitHub Actions와 결합하면 코드 푸시에서 자동 테스트, 자동 배포까지 완전히 자동화된다.

Docker 핵심 개념 정리

용어설명비유
Image컨테이너 실행에 필요한 모든 파일 묶음 (읽기 전용)요리 레시피
Container이미지로 실행 중인 인스턴스 (여러 개 가능)레시피로 만든 요리
Dockerfile이미지 빌드 지침서레시피 작성
Docker Hub이미지 저장/공유 레지스트리레시피 책 도서관
Compose여러 컨테이너를 한 번에 관리하는 도구풀코스 요리 관리

자주 쓰는 Docker 명령어

bash
# 이미지 관련
docker build -t myapp:1.0 .          # 이미지 빌드
docker images                         # 이미지 목록
docker pull node:24-alpine            # 이미지 다운로드

# 컨테이너 관련
docker run -d -p 3000:3000 myapp:1.0  # 백그라운드 실행 + 포트 매핑
docker ps                             # 실행 중 컨테이너 목록
docker logs -f container_id           # 로그 실시간 출력
docker exec -it container_id bash     # 컨테이너 내부 접속
도커파일과 이미지, 컨테이너로 이어지는 도커 핵심 개념을 나타낸 도해
설계도 → 만들어진 것 → 돌아가는 것 — 셋의 관계다.

Dockerfile 작성 실전

Dockerfile은 이미지를 만드는 설계도다. 레이어(layer) 구조로 되어 있어 변경된 부분만 다시 빌드하므로, 순서 설계가 빌드 성능을 좌우한다.

Node.js 앱 Dockerfile (프로덕션 최적화)

dockerfile
# 1단계: 의존성 설치 (devDependencies 포함 -- 빌드 도구 필요)
FROM node:24-alpine AS deps
WORKDIR /app
COPY package*.json ./
RUN npm ci

# 2단계: 빌드 스테이지
FROM node:24-alpine AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
RUN npm prune --omit=dev   # 런타임용 production 의존성만 남김

# 3단계: 런타임 스테이지 (production 의존성만)
FROM node:24-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup -S appgroup && adduser -S appuser -G appgroup
USER appuser
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules
COPY --from=builder --chown=appuser:appgroup /app/package.json ./
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
    CMD wget -qO- http://localhost:3000/health || exit 1
CMD ["node", "dist/index.js"]
이미지 크기 최소화 팁

Alpine 기반 이미지 사용, 멀티 스테이지 빌드, .dockerignore 철저히 설정, RUN 명령어 체이닝으로 레이어 수 감소 (RUN cmd1 && cmd2 && cmd3)

Docker Compose -- 멀티 컨테이너 구성

실제 서비스는 앱 서버 + 데이터베이스 + 캐시 등 여러 컨테이너로 구성된다. Docker Compose는 이를 하나의 파일로 관리한다.

docker-compose.yml
version: '3.9'

services:
  app:
    build:
      context: .
      target: runner
    image: myapp:latest
    restart: unless-stopped
    ports:
      - "3000:3000"
    depends_on:
      db:
        condition: service_healthy
    networks:
      - app-net

  db:
    image: postgres:16-alpine
    restart: unless-stopped
    volumes:
      - postgres-data:/var/lib/postgresql/data
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U user -d mydb"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - app-net

  cache:
    image: redis:7-alpine
    restart: unless-stopped
    networks:
      - app-net

volumes:
  postgres-data:

networks:
  app-net:
    driver: bridge

GitHub Actions CI/CD 파이프라인

GitHub Actions는 코드 저장소와 완벽하게 통합된 CI/CD 플랫폼이다. main 브랜치에 Push하면 테스트, Docker 이미지 빌드, 레지스트리 푸시, 서버 배포까지 완전 자동화된다. 2026-06-02부터 러너의 기본 런타임이 Node 24로 강제됐으므로 actions/* 핀은 최신 메이저(@v5 이상)로 맞춰야 경고/실패가 없다.

CI 워크플로 -- PR 린트 & 테스트

yaml -- .github/workflows/ci.yml
name: CI - 린트 & 테스트

on:
  pull_request:
    branches: [main]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v5
      - name: Node.js 설정 (러너 기본 Node 24)
        uses: actions/setup-node@v5
        with:
          node-version: 24
          cache: npm
      - run: npm ci
      - run: npm run lint
      - run: npm test

CD 워크플로 -- GHCR 빌드 & 키리스 배포

아래는 실제로 동작하는 배포 워크플로다. Docker Hub 자격증명 대신 GHCR + 내장 GITHUB_TOKEN으로 로그인하므로 별도 시크릿이 필요 없고, docker/metadata-action이 태그/라벨을 자동 생성하며, 빌드 후 프로버넌스 서명으로 이미지 출처를 증명한다.

yaml -- .github/workflows/deploy.yml
name: CD - 빌드 & 배포

on:
  push:
    branches: [main]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build-and-push:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write        # GHCR 푸시
      id-token: write         # OIDC 키리스 + 프로버넌스 서명
      attestations: write     # 빌드 프로버넌스 attestation
    outputs:
      digest: ${{ steps.build.outputs.digest }}
    steps:
      - uses: actions/checkout@v5

      - name: Docker Buildx 설정
        uses: docker/setup-buildx-action@v3

      - name: GHCR 로그인 (내장 GITHUB_TOKEN)
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: 태그 & 라벨 메타데이터
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=sha
            type=ref,event=branch
            type=raw,value=latest,enable={{is_default_branch}}

      - name: 빌드 & 푸시 (멀티 아키텍처 + GHA 캐시)
        id: build
        uses: docker/build-push-action@v6
        with:
          context: .
          platforms: linux/amd64,linux/arm64
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

      - name: 빌드 프로버넌스 서명
        uses: actions/attest-build-provenance@v2
        with:
          subject-name: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          subject-digest: ${{ steps.build.outputs.digest }}
          push-to-registry: true

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    permissions:
      id-token: write   # AWS OIDC 키리스 인증
      contents: read
    steps:
      - name: AWS 자격증명 (OIDC, 장기 키 없음)
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ secrets.AWS_DEPLOY_ROLE_ARN }}
          aws-region: ap-northeast-2

      - name: ECS 서비스 강제 재배포
        run: |
          aws ecs update-service \
            --cluster prod \
            --service myapp \
            --force-new-deployment
왜 GHCR + GITHUB_TOKEN인가

GHCR로 푸시하면 DOCKERHUB_USERNAME/DOCKERHUB_TOKEN 같은 외부 시크릿이 필요 없다. 워크플로에 자동 주입되는 GITHUB_TOKENpackages: write 권한만으로 인증이 끝난다.

OIDC 키리스 -- 시크릿 1개면 충분

AWS 배포는 액세스 키를 시크릿에 저장하지 않는다. IAM에 GitHub OIDC 신뢰 관계를 등록한 뒤, 잡에 id-token: write 권한과 role-to-assume(역할 ARN, AWS_DEPLOY_ROLE_ARN 시크릿)만 주면 매 실행마다 단기 자격증명을 발급받는다. 유출 위험이 있는 장기 키가 사라진다.

푸시와 테스트, 이미지 빌드, 배포로 이어지는 깃허브 액션스 파이프라인을 나타낸 도해
코드를 밀어 넣으면 나머지가 자동으로 돈다.

자동 서버 배포 구성

위 CD 워크플로는 ECS로 무중단 배포하지만, 직접 운영하는 VM/온프레미스 서버라면 GHCR에서 새 이미지를 받아 교체하는 방식이 가장 단순하다. 서버에서 GHCR에 로그인할 때도 사용자 PAT(read:packages) 한 개면 충분하다.

bash -- 서버 측 배포 스크립트 (deploy.sh)
#!/usr/bin/env bash
set -euo pipefail

# GHCR에서 최신 이미지 받기 (사전 docker login ghcr.io 완료 상태)
docker compose pull app

# 무중단 교체: 새 컨테이너 기동 후 헬스체크 통과 시 전환
docker compose up -d --no-deps --wait app

# 오래된 이미지 정리
docker image prune -f
--wait로 헬스체크 연동

docker compose up --wait는 서비스가 healthcheck를 통과할 때까지 기다린 뒤 종료 코드를 반환한다. Dockerfile의 HEALTHCHECK와 맞물려 진짜 무중단 배포가 된다.

최적화와 실전 팁

빌드 시간 단축 -- 레이어 캐시 활용

핵심 원칙

자주 변경되는 파일일수록 Dockerfile의 아래쪽에 배치하자. Docker는 변경된 레이어 이하를 모두 다시 빌드한다.

GitHub Actions 실전 체크리스트

파이프라인 구성 체크리스트

PR 시 자동 테스트 (branch protection rules), GHA 캐시(type=gha,mode=max)로 빌드 시간 50~70% 단축, 멀티 아키텍처 빌드 (linux/amd64,arm64), 이미지 취약점 스캔 (trivy-action), 빌드 프로버넌스/SBOM 서명으로 공급망 보안 강화, 배포 후 슬랙/디스코드 알림 연동, 오래된 이미지 자동 정리 (docker image prune)

정리

  • Dockerfile -- 멀티스테이지 빌드 + 레이어 캐시 전략으로 이미지 최소화
  • .dockerignore -- node_modules, .env, .git 반드시 제외
  • Docker Compose -- depends_on + healthcheck으로 서비스 시작 순서 보장
  • CI (ci.yml) -- PR마다 Node 24 기준 린트/테스트 자동 실행
  • CD (deploy.yml) -- main 푸시 시 GHCR 빌드/푸시/배포 자동화 (액션 핀 @v5 이상)
  • GHCR + GITHUB_TOKEN -- 내장 토큰으로 푸시, Docker Hub 시크릿 불필요
  • OIDC 키리스 -- 장기 AWS 키 없이 role-to-assume으로 단기 자격증명 발급
  • 프로버넌스 서명 -- attest-build-provenance로 이미지 출처 증명(공급망 보안)

이 파이프라인을 구축하면 코드 작성과 배포를 완전 분리하여 개발에만 집중할 수 있다.

레이어 캐시를 살리는 도커파일 작성 순서를 정리한 도해
자주 바뀌는 것을 뒤로 미루면 빌드가 크게 빨라진다.
DockerGitHub ActionsCI/CDDockerfileDocker Compose자동 배포DevOps
junetapa
junetapa
개발자가 아니라 쓰는 사람. IT 현장 27년의 기준으로 AI 도구를 고르고 기록한다.
Twitter Facebook URL 복사