SJSBiz SJSBiz

Docker 컨테이너 개념과 실무 배포 흐름 정리

읽는 시간 약 15분
(adsbygoogle = window.adsbygoogle || []).push({});

현대 소프트웨어 개발에서 Docker는 필수적인 도구로 자리 잡았습니다. 개발 환경과 운영 환경의 차이로 인한 문제를 해결하고, 애플리케이션을 빠르게 배포하며, 리소스를 효율적으로 활용할 수 있게 해줍니다. 이번 글에서는 Docker 컨테이너의 핵심 개념부터 실무 배포 흐름까지 상세히 정리해 드리겠습니다.

Docker란 무엇인가

Docker는 애플리케이션을 컨테이너라는 독립된 환경에 패키징하여 실행하는 오픈소스 플랫폼입니다. 2013년에 출시되었으며, 기존 가상 머신(VM)보다 가볍고 빠른 방식으로 애플리케이션 격리를 구현합니다.

Docker의 핵심 철학은 Build once, Run anywhere입니다. 한 번 빌드한 이미지는 개발자의 노트북에서부터 테스트 서버, 프로덕션 서버, 클라우드까지 동일하게 실행됩니다.

컨테이너 vs 가상 머신

컨테이너와 가상 머신은 모두 격리된 환경을 제공하지만, 아키텍처와 성능 특성이 크게 다릅니다.

항목가상 머신 (VM)컨테이너 (Docker)
격리 수준하드웨어 수준 (OS 전체 격리)OS 수준 (프로세스 격리)
게스트 OS별도 설치 필요호스트 OS 커널 공유
시작 시간수 분수 초
이미지 크기수 GB 이상수 MB~수백 MB
리소스 사용많음 (CPU, 메모리 오버헤드)적음 (프로세스 수준)
이식성상대적으로 무거움매우 가벼움

컨테이너는 호스트 OS의 커널을 공유하면서 프로세스와 파일 시스템만 격리하므로, VM에 비해 훨씬 가볍고 빠릅니다.

Docker 핵심 개념

📦 이미지 (Image)

이미지는 컨테이너 실행에 필요한 모든 것을 포함하는 읽기 전용 템플릿입니다. 애플리케이션 코드, 런타임, 라이브러리, 환경 변수, 설정 파일 등이 패키징되어 있습니다.

# 이미지 빌드
docker build -t myapp:1.0 .

# 이미지 목록 확인
docker images

# 이미지 삭제
docker rmi myapp:1.0

📦 컨테이너 (Container)

컨테이너는 이미지의 실행 인스턴스입니다. 이미지를 기반으로 생성되며, 독립된 파일 시스템과 네트워크, 프로세스 공간을 가집니다.

# 컨테이너 실행
docker run -d -p 8080:80 --name web myapp:1.0

# 실행 중인 컨테이너 목록
docker ps

# 컨테이너 중지 및 삭제
docker stop web
docker rm web

📦 Dockerfile

Dockerfile은 이미지를 자동으로 빌드하기 위한 스크립트입니다. 레이어 캐싱을 활용하여 효율적으로 이미지를 생성합니다.

# Dockerfile 예시 (Node.js 애플리케이션)
FROM node:18-alpine

WORKDIR /app

COPY package*.json ./
RUN npm ci --only=production

COPY . .

EXPOSE 3000

USER node

CMD ["node", "server.js"]

📦 Docker Hub와 레지스트리

Docker Hub는 공식 이미지 저장소입니다. 팀 내에서는 AWS ECR, Google GCR, Harbor 등 프라이빗 레지스트리를 사용하여 이미지를 관리합니다.

# Docker Hub에서 이미지 Pull
docker pull nginx:alpine

# 프라이빗 레지스트리 로그인 및 Push
docker login registry.example.com
docker tag myapp:1.0 registry.example.com/myapp:1.0
docker push registry.example.com/myapp:1.0

Docker Compose로 다중 컨테이너 관리

실무에서는 하나의 애플리케이션이 웹 서버, 데이터베이스, 캐시 서버 등 여러 컨테이너로 구성됩니다. Docker Compose는 이런 다중 컨테이너 환경을 정의하고 실행하는 도구입니다.

# docker-compose.yml
version: '3.8'

services:
  web:
    build: ./web
    ports:
      - "80:80"
    depends_on:
      - api
    networks:
      - frontend

  api:
    build: ./api
    ports:
      - "8080:8080"
    environment:
      - DB_HOST=db
      - REDIS_HOST=redis
    depends_on:
      - db
      - redis
    networks:
      - frontend
      - backend

  db:
    image: postgres:15-alpine
    volumes:
      - db_data:/var/lib/postgresql/data
    environment:
      - POSTGRES_USER=app
      - POSTGRES_PASSWORD=secret
    networks:
      - backend

  redis:
    image: redis:7-alpine
    networks:
      - backend

volumes:
  db_data:

networks:
  frontend:
  backend:
# 실행 명령어
docker-compose up -d        # 백그라운드 실행
docker-compose down         # 컨테이너 중지 및 삭제
docker-compose logs -f api  # 실시간 로그 확인

실무 배포 흐름 (CI/CD)

Docker를 활용한 표준적인 CI/CD 파이프라인은 다음과 같이 구성됩니다.

(adsbygoogle = window.adsbygoogle || []).push({});
[개발자 Push]
    ↓
[GitHub/GitLab Repository]
    ↓
[CI 서버: Jenkins/GitHub Actions/GitLab CI]
    ├── 코드 빌드 및 테스트
    ├── Docker 이미지 빌드
    ├── 보안 스캔 (Trivy, Snyk)
    └── 레지스트리에 이미지 Push
    ↓
[CD 서버 / ArgoCD]
    ├── 이미지 태그 업데이트
    └── Kubernetes / Docker Swarm에 배포
    ↓
[프로덕션 환경]
    ├── 로드밸런서 (Nginx Ingress)
    ├── 애플리케이션 Pod (Container)
    └── 모니터링 (Prometheus + Grafana)

🔧 GitHub Actions CI 예시

# .github/workflows/deploy.yml
name: Build and Deploy

on:
  push:
    branches: [main]

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

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

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

      - name: Build and Push
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
          cache-from: type=gha
          cache-to: type=gha,mode=max

Docker 보안 모범 사례

컨테이너는 가볍지만, 보안 설정을 소홀히 하면 호스트 전체가 위험해질 수 있습니다. 다음 사항을 반드시 적용하세요.

  • 최소 권한 원칙: 루트 사용자 대신 전용 사용자 실행 (USER 지시어)
  • 이미지 스캔: Trivy, Clair 등으로 취약점 정기 검사
  • 멀티스테이지 빌드: 빌드 의존성을 런타임 이미지에서 분리
  • 비밀 관리: 환경 변수 대신 Docker Secrets 또는 외부 KMS 사용
  • 네트워크 분리: 프론트엔드/백엔드 네트워크를 분리하여 최소 권한 통신
  • 리소스 제한: CPU, 메모리, I/O 제한으로 DoS 방어
# 멀티스테이지 빌드 예시
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build

FROM node:18-alpine AS production
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER node
EXPOSE 3000
CMD ["node", "dist/main.js"]

Kubernetes와의 연계

단일 서버에서 Docker Compose로 충분할 수 있지만, 대규모 서비스에서는 Kubernetes가 표준입니다. Docker는 Kubernetes의 컨테이너 런타임으로 사용되며, Pod 단위로 컨테이너를 오케스트레이션합니다.

# Kubernetes Deployment 예시
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: web
        image: myapp:1.0
        ports:
        - containerPort: 8080
        resources:
          requests:
            memory: "128Mi"
            cpu: "100m"
          limits:
            memory: "256Mi"
            cpu: "200m"

자주 하는 실수와 해결책

❌ 이미지 크기가 너무 큼

Ubuntu나 Debian 기반 이미지는 불필요한 패키지가 많습니다. Alpine Linux 기반 이미지를 사용하거나, distroless 이미지를 고려하세요.

❌ FROM node:18      # 1GB+
✅ FROM node:18-alpine  # 180MB
✅ FROM gcr.io/distroless/nodejs18-debian11  # 120MB

❌ .dockerignore 미사용

불필요한 파일이 이미지에 포함되면 빌드 시간이 길어지고 보안 취약점이 증가합니다. .dockerignore를 반드시 작성하세요.

# .dockerignore
node_modules
.git
.env
*.log
Dockerfile
.dockerignore

실무 체크리스트

  • Dockerfile은 멀티스테이지 빌드로 작성할 것
  • 베이스 이미지는 Alpine 또는 distroless를 우선 검토할 것
  • 루트 사용자 실행을 피하고 전용 사용자를 설정할 것
  • .dockerignore로 불필요한 파일 제외할 것
  • 이미지 보안 스캔을 CI 파이프라인에 포함할 것
  • 비밀 정보는 환경 변수 대신 Secrets로 관리할 것
  • 컨테이너 리소스에 CPU/메모리 제한을 설정할 것
  • 헬스체크(HEALTHCHECK)를 Dockerfile에 추가할 것
  • 로그는 표준 출력/에러로 내보내 외부 집계 시스템과 연동할 것
  • 이미지 태그는 latest 대신 구체적인 버전을 사용할 것

자주 묻는 질문

Docker와 Kubernetes는 무슨 차이인가요?

Docker는 컨테이너를 생성하고 실행하는 도구입니다. Kubernetes는 여러 대의 서버에서 컨테이너를 자동으로 배포, 확장, 관리하는 오케스트레이션 플랫폼입니다. Docker 없이 Kubernetes를 사용할 수 없으며, Kubernetes는 Docker를 컨테이너 런타임으로 활용합니다.

Docker Desktop은 유료인가요?

2021년부터 대기업(직원 250명 이상 또는 연매출 1,000만 달러 이상)에서의 Docker Desktop 사용은 유료 라이선스가 필요합니다. 개인 사용자나 중소기업, 교육 목적은 여전히 무료입니다. 대안으로 Rancher Desktop, Podman 등을 고려할 수 있습니다.

컨테이너 데이터는 어떻게 백업하나요?

컨테이너 자체는 상태가 없어야 합니다(Stateless). 데이터는 볼륨에 저장하고, 볼륨 데이터는 호스트의 파일 시스템이나 클라우드 스토리지(AWS EBS, NFS)에 백업합니다. 데이터베이스의 경우 컨테이너 외부의 관리형 서비스(AWS RDS)를 사용하는 것이 안전합니다.

# 볼륨 백업
docker run --rm -v db_data:/data -v $(pwd):/backup alpine tar cvf /backup/backup.tar /data

# 볼륨 복원
docker run --rm -v db_data:/data -v $(pwd):/backup alpine tar xvf /backup/backup.tar -C /

마무리

이번 글에서는 Docker 컨테이너 개념과 실무 배포 흐름을 정리해 드렸습니다. Docker는 개발과 운영의 간극을 메우는 강력한 도구이며, CI/CD 파이프라인과 결합하면 완전한 자동화 배포 환경을 구축할 수 있습니다.

핵심을 정리하면 다음과 같습니다: 컨테이너는 가벼운 격리 환경이며, 이미지는 불변의 배포 단위이고, Dockerfile로 선언적 빌드를 하며, Compose로 다중 컨테이너를 관리하고, CI/CD로 자동화하며, 보안은 최소 권한과 이미지 스캔으로 확보하세요.

Docker 컨테이너와 배포 관련 궁금한 점이나 문제가 있으시면 댓글로 남겨 주세요. 도움이 되셨다면 이 글을 주변에 공유해 주시는 것도 잊지 마세요!

(adsbygoogle = window.adsbygoogle || []).push({});
깨비

함께 보면 좋은 글

댓글 0

첫 댓글을 남겨보세요.

error: Content is protected !!
광고보고 콘텐츠 계속 읽기
원치않으시면 뒤로가기를 해주세요

광고 차단 알림

광고 클릭 제한을 초과하여 광고가 차단되었습니다.

단시간에 반복적인 광고 클릭은 시스템에 의해 감지되며, IP가 수집되어 사이트 관리자가 확인 가능합니다.

광고보고 콘텐츠 계속 읽기
원치않으시면 뒤로가기를 해주세요