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_TOKEN과 packages: 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로 이미지 출처 증명(공급망 보안)
이 파이프라인을 구축하면 코드 작성과 배포를 완전 분리하여 개발에만 집중할 수 있다.