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
DevOps와 자동화에 관심이 많은 개발자. 실전 경험을 바탕으로 기술을 공유한다.
Twitter Facebook URL 복사