GitHub Actions로 Django + Docker 배포 자동화하기

GitHub Actions로 Django + Docker 배포 자동화하기

안녕하세요! 오늘은 GitHub Actions를 사용하여 Django 애플리케이션을 Docker 컨테이너로 자동 배포하는 방법에 대해 알아보겠습니다.

1. 프로젝트 구조

project/
├── .github/
│   └── workflows/
│       └── deploy.yml
├── app/
│   ├── Dockerfile
│   ├── requirements.txt
│   └── manage.py
├── docker-compose.yml
└── .env.example

2. Docker 설정

2.1 Dockerfile

# Python 3.9 기반 이미지 사용
FROM python:3.9-slim

# 작업 디렉토리 설정
WORKDIR /app

# 시스템 패키지 설치
RUN apt-get update && apt-get install -y \
    build-essential \
    libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# Python 의존성 설치
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# 애플리케이션 코드 복사
COPY . .

# 환경 변수 설정
ENV PYTHONUNBUFFERED=1
ENV DJANGO_SETTINGS_MODULE=project.settings.production

# Gunicorn 실행
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "project.wsgi:application"]

2.2 docker-compose.yml

version: '3.8'

services:
  web:
    build: .
    command: gunicorn project.wsgi:application --bind 0.0.0.0:8000
    volumes:
      - .:/app
    env_file:
      - .env
    depends_on:
      - db
    ports:
      - "8000:8000"

  db:
    image: postgres:13
    volumes:
      - postgres_data:/var/lib/postgresql/data/
    environment:
      - POSTGRES_DB=${POSTGRES_DB}
      - POSTGRES_USER=${POSTGRES_USER}
      - POSTGRES_PASSWORD=${POSTGRES_PASSWORD}

volumes:
  postgres_data:

3. GitHub Actions 워크플로우 설정

3.1 기본 워크플로우

# .github/workflows/deploy.yml
name: Deploy Django App

on:
  push:
    branches: [ main ]
  pull_request:
    branches: [ main ]

env:
  DOCKER_IMAGE: your-docker-image
  AWS_REGION: ap-northeast-2
  ECR_REPOSITORY: your-repository

jobs:
  test:
    runs-on: ubuntu-latest

    services:
      postgres:
        image: postgres:13
        env:
          POSTGRES_DB: test_db
          POSTGRES_USER: test_user
          POSTGRES_PASSWORD: test_password
        ports:
          - 5432:5432
        options: >-
          --health-cmd pg_isready
          --health-interval 10s
          --health-timeout 5s
          --health-retries 5

    steps:
    - uses: actions/checkout@v2

    - name: Set up Python
      uses: actions/setup-python@v2
      with:
        python-version: '3.9'

    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt

    - name: Run tests
      env:
        DATABASE_URL: postgres://test_user:test_password@localhost:5432/test_db
      run: |
        python manage.py test

  build-and-push:
    needs: test
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
    - uses: actions/checkout@v2

    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ env.AWS_REGION }}

    - name: Login to Amazon ECR
      id: login-ecr
      uses: aws-actions/amazon-ecr-login@v1

    - name: Build, tag, and push image to Amazon ECR
      env:
        ECR_REGISTRY: ${{ steps.login-ecr.outputs.registry }}
        IMAGE_TAG: ${{ github.sha }}
      run: |
        docker build -t $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG .
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG
        docker tag $ECR_REGISTRY/$ECR_REPOSITORY:$IMAGE_TAG $ECR_REGISTRY/$ECR_REPOSITORY:latest
        docker push $ECR_REGISTRY/$ECR_REPOSITORY:latest

  deploy:
    needs: build-and-push
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'

    steps:
    - name: Configure AWS credentials
      uses: aws-actions/configure-aws-credentials@v1
      with:
        aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
        aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
        aws-region: ${{ env.AWS_REGION }}

    - name: Update ECS service
      run: |
        aws ecs update-service --cluster your-cluster --service your-service --force-new-deployment

4. 환경 변수 설정

4.1 .env.example

# Django 설정
DEBUG=False
SECRET_KEY=your-secret-key
ALLOWED_HOSTS=your-domain.com

# 데이터베이스 설정
POSTGRES_DB=yourdb
POSTGRES_USER=youruser
POSTGRES_PASSWORD=yourpassword
DATABASE_URL=postgres://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}

# AWS 설정
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_STORAGE_BUCKET_NAME=your-bucket

5. 보안 설정

5.1 GitHub Secrets 설정

# GitHub 저장소의 Settings > Secrets에서 설정
AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
DJANGO_SECRET_KEY=your-secret-key

5.2 IAM 역할 설정

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ecr:GetAuthorizationToken",
                "ecr:BatchCheckLayerAvailability",
                "ecr:GetDownloadUrlForLayer",
                "ecr:BatchGetImage",
                "ecr:PutImage",
                "ecr:InitiateLayerUpload",
                "ecr:UploadLayerPart",
                "ecr:CompleteLayerUpload"
            ],
            "Resource": "*"
        },
        {
            "Effect": "Allow",
            "Action": [
                "ecs:UpdateService",
                "ecs:DescribeServices"
            ],
            "Resource": "arn:aws:ecs:region:account-id:service/your-cluster/your-service"
        }
    ]
}

6. 배포 후 작업

6.1 데이터베이스 마이그레이션

# deploy.yml에 추가
- name: Run database migrations
  run: |
    aws ecs run-task \
      --cluster your-cluster \
      --task-definition your-task-definition \
      --network-configuration "awsvpcConfiguration={subnets=[subnet-12345678],securityGroups=[sg-12345678]}" \
      --overrides '{"containerOverrides":[{"name":"web","command":["python","manage.py","migrate"]}]}'

6.2 정적 파일 수집

# deploy.yml에 추가
- name: Collect static files
  run: |
    aws ecs run-task \
      --cluster your-cluster \
      --task-definition your-task-definition \
      --network-configuration "awsvpcConfiguration={subnets=[subnet-12345678],securityGroups=[sg-12345678]}" \
      --overrides '{"containerOverrides":[{"name":"web","command":["python","manage.py","collectstatic","--noinput"]}]}'

7. 모니터링 및 알림

7.1 배포 상태 알림

# deploy.yml에 추가
- name: Notify deployment status
  if: always()
  run: |
    if [ ${{ job.status }} == 'success' ]; then
      echo "Deployment successful"
    else
      echo "Deployment failed"
    fi

7.2 로그 확인

# deploy.yml에 추가
- name: Check deployment logs
  run: |
    aws logs get-log-events \
      --log-group-name /ecs/your-service \
      --log-stream-name ecs/your-service/$(aws ecs describe-services --cluster your-cluster --services your-service --query 'services[0].deployments[0].id' --output text)

결론

GitHub Actions를 사용하여 Django 애플리케이션을 Docker 컨테이너로 자동 배포하는 방법을 알아보았습니다. 이 설정을 통해 지속적인 통합과 배포를 자동화할 수 있습니다. 추가적인 질문이나 궁금한 점이 있으시면 댓글로 남겨주세요!

댓글