백엔드

Docker 멀티 컨테이너와 Compose, CI/CD 배포

지식소 채움이 2025. 9. 2. 08:21

1. 멀티 컨테이너 구성의 필요성

컨테이너는 “하나의 컨테이너 = 하나의 프로세스”라는 원칙을 따르는 것이 이상적입니다. 즉, 하나의 컨테이너에 웹 서버, DB, 캐시를 모두 넣기보다 역할에 따라 분리하는 것이 바람직합니다.

예시:

  • web: Django 또는 Flask와 같은 애플리케이션 서버
  • db: PostgreSQL, MySQL 등 데이터베이스 서버
  • cache: Redis, Memcached
  • proxy: Nginx, Caddy와 같은 리버스 프록시/로드밸런서

이렇게 분리하면 얻는 장점은 다음과 같습니다.

  1. 유지보수성: 특정 서비스에 문제가 생겨도 해당 컨테이너만 재시작 가능
  2. 확장성: 트래픽이 몰리면 웹 컨테이너만 수평 확장 가능
  3. 표준화: 각 컨테이너가 독립적이므로 다른 프로젝트에도 쉽게 재활용 가능

2. Docker Compose 기본 개념

멀티 컨테이너 환경을 일일이 docker run 명령어로 관리하는 것은 번거롭습니다. 이를 해결해주는 도구가 Docker Compose입니다.
docker-compose.yml 파일 하나로 서비스 정의, 네트워크, 볼륨을 함께 관리할 수 있습니다.

예시: Django + PostgreSQL + Nginx

version: '3.8'

services:
  web:
    build: ./app
    container_name: django_app
    command: gunicorn myproject.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - ./app:/usr/src/app
    ports:
      - "8000:8000"
    depends_on:
      - db
    environment:
      - DATABASE_URL=postgres://user:password@db:5432/mydb

  db:
    image: postgres:14
    container_name: postgres_db
    restart: always
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: mydb
    volumes:
      - postgres_data:/var/lib/postgresql/data

  nginx:
    image: nginx:latest
    container_name: nginx_proxy
    volumes:
      - ./nginx/conf:/etc/nginx/conf.d
    ports:
      - "80:80"
    depends_on:
      - web

volumes:
  postgres_data:

핵심 포인트

  • depends_on: 컨테이너 간 실행 순서 보장
  • volumes: 데이터 영속성 확보
  • environment: 환경변수를 통해 유연하게 설정
  • ports: 외부와 연결할 포트 지정

이렇게 정의된 Compose 파일은 docker-compose up -d 한 줄로 실행 가능합니다.


3. 로컬 개발 vs 운영 환경 분리

Compose는 환경에 따라 설정을 다르게 가져갈 수 있습니다.

  • 로컬 개발: 코드 볼륨 마운트, 디버깅을 위한 포트 개방
  • 운영 환경: 보안 강화, 불필요한 포트 차단, 로그 관리

예를 들어 docker-compose.override.yml을 두어 로컬에서는 디버깅용 설정을 추가하고, 서버에서는 배포용 설정을 적용할 수 있습니다.


4. 배포 파이프라인(CI/CD) 구성하기

멀티 컨테이너 구성을 마쳤다면 이제 남은 과제는 자동 배포입니다. 개발자가 코드를 푸시할 때마다 자동으로 빌드, 테스트, 배포가 이루어지면 생산성이 크게 향상됩니다.

CI/CD 기본 흐름

  1. 코드 푸시 (GitHub, GitLab 등)
  2. CI 서버 (GitHub Actions, GitLab CI, Jenkins 등)이 Docker 이미지를 빌드
  3. 테스트 실행 (단위 테스트, 통합 테스트)
  4. 이미지 레지스트리에 푸시 (Docker Hub, AWS ECR 등)
  5. 운영 서버에서 pull & 재배포

GitHub Actions 예시

name: CI/CD Pipeline

on:
  push:
    branches: [ "main" ]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3

      - name: Build Docker image
        run: docker build -t myrepo/myapp:${{ github.sha }} .

      - name: Login to DockerHub
        run: echo ${{ secrets.DOCKER_PASSWORD }} | docker login -u ${{ secrets.DOCKER_USERNAME }} --password-stdin

      - name: Push image
        run: docker push myrepo/myapp:${{ github.sha }}

  deploy:
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: SSH to server and deploy
        uses: appleboy/ssh-action@v0.1.7
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: ${{ secrets.SERVER_USER }}
          key: ${{ secrets.SERVER_SSH_KEY }}
          script: |
            docker pull myrepo/myapp:${{ github.sha }}
            docker-compose down
            docker-compose up -d

5. 실무에서의 고려사항

  • 보안: 환경 변수 관리 시 .env 파일과 Secret Manager 활용
  • 모니터링: Prometheus, Grafana, ELK 스택 연동
  • 무중단 배포: Blue-Green Deployment, Rolling Update 전략 적용
  • 스케일링: 필요하다면 Docker Swarm이나 Kubernetes로 확장 고려

마무리

멀티 컨테이너 구성은 현대 애플리케이션에서 사실상 필수적인 패턴입니다. Docker Compose를 통해 개발/운영 환경을 표준화하고, CI/CD 파이프라인까지 구축하면 프로젝트 생산성과 안정성이 크게 향상됩니다. 작은 사이드 프로젝트라도 초기부터 이 구조를 도입하면, 추후 확장과 협업이 훨씬 수월해집니다.

앞으로는 Compose에서 한 단계 나아가 Kubernetes 같은 오케스트레이션 도구로 확장하는 것도 고려할 수 있습니다. 그러나 그 출발점은 멀티 컨테이너와 Compose라는 점을 기억하면 좋겠습니다.