최근 프로젝트에서 Gitea, AWS ECR, Jenkins, Docker-Compose, 그리고 AWS Elastic Beanstalk(EB)를 활용하여 CI/CD 파이프라인을 구성하는 작업을 진행했습니다. 이 포스팅에서는 CI/CD 환경 구축 과정과 함께 발생했던 문제 및 이를 해결한 과정을 기록하고자 합니다.
1. 프로젝트 개요
목표
Django 기반의 웹 애플리케이션과 이를 지원하는 서비스(Nginx, Redis, TensorFlow)를 Docker-Compose로 구성하고, AWS Elastic Beanstalk를 통해 배포 및 관리하며, CI/CD를 통해 코드 변경 사항이 자동으로 배포되도록 하는 환경 구축.
사용 기술 스택
- Gitea: 코드 저장소 관리
- AWS ECR: Docker 이미지를 저장
- Jenkins: CI/CD 자동화
- Docker-Compose: 컨테이너 오케스트레이션
- AWS Elastic Beanstalk: 배포 및 운영 환경
- Redis: 캐시 및 세션 관리
- TensorFlow Serving: 모델 서빙
- Nginx: Reverse Proxy와 정적 파일 제공
2. CI/CD 파이프라인 구축
2.1 Gitea 설정
Gitea를 프로젝트의 소스 코드 저장소로 사용했습니다. Gitea의 Webhook 기능을 통해 Jenkins와 통합하여 코드 변경 시 자동으로 빌드가 트리거되도록 설정했습니다.
Webhook 설정:
- Gitea에서 프로젝트 Repository 설정으로 이동
- Webhook 추가 → Jenkins URL 및 토큰 입력
2.2 AWS ECR 설정
AWS ECR을 Docker 이미지를 저장하는 레지스트리로 사용했습니다.
ECR 설정 과정:
- AWS 콘솔에서 ECR 리포지토리를 생성
- Jenkins의 빌드 스크립트에서 aws ecr get-login-password를 통해 ECR에 로그인
- Docker 이미지를 태그 및 푸시하도록 스크립트를 구성
2.3 Docker-Compose 구성
Docker-Compose를 사용해 다음 서비스를 정의:
- uwsgi (Django): 주요 애플리케이션 서버
- nginx: Reverse Proxy 및 정적 파일 서비스
- redis: Django의 캐시 및 세션 백엔드
- ml_models (TensorFlow Serving): 모델 서빙
Docker-Compose 파일 예시:
version: '3'
services:
uwsgi:
## image: ECR 경로
depends_on:
- redis
- ml_models
nginx:
## image: ECR 경로
ports:
- 80:80
depends_on:
- uwsgi
redis:
image: redis
container_name: redis_server
ports:
- 6379:6379
ml_models:
image: emacski/tensorflow-serving
restart: always
volumes:
## - 프로젝트의 model 경로
command: "--model_config_file=/models/model_config.config --model_config_file_poll_wait_seconds=60"
ports:
- 8501:8501
2.4 Jenkins 설정
Jenkins는 Gitea에서 Webhook 이벤트를 받아 CI/CD를 수행합니다.
Job 구성:
- Pipeline: 멀티 스테이지로 빌드, 테스트, 배포를 구성
- 빌드 스크립트: 아래와 같은 Shell Script를 사용
#!/bin/bash
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
set -e # Stop script on error
# AWS credentials setup
export AWS_ACCESS_KEY_ID=$AWS_ACCESS_KEY_ID_VAR
export AWS_SECRET_ACCESS_KEY=$AWS_SECRET_ACCESS_KEY_VAR
export AWS_DEFAULT_REGION=ap-northeast-2
# AWS ECR login
aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com
# Docker image build
docker build -t my-app-dev .
# Docker image tagging
docker tag my-app-dev:latest 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/my-app-dev:latest
# Push Docker image to ECR
docker push 123456789012.dkr.ecr.ap-northeast-2.amazonaws.com/my-app-dev:latest
# Prepare deployment for Elastic Beanstalk
zip -r my-app-eb-deploy.zip docker-compose.yml Dockerfile nginx/ app_models/
# Upload the zip file to S3
aws s3 cp my-app-eb-deploy.zip s3://my-app-s3/my-app-eb-deploy.zip
# Create a new Elastic Beanstalk application version
aws elasticbeanstalk create-application-version \
--application-name "my-app" \
--version-label "build-${BUILD_NUMBER}" \
--source-bundle S3Bucket="my-app-s3",S3Key="my-app-eb-deploy.zip"
# Update Elastic Beanstalk environment
aws elasticbeanstalk update-environment \
--environment-name "My-app-test" \
--version-label "build-${BUILD_NUMBER}"
3. 트러블슈팅 사례
문제 1: Jenkins에서 eb 명령어 실행 불가
원인: Jenkins 계정에 awsebcli가 설치되지 않음.
해결: Jenkins 사용자 환경에서 pip install awsebcli --user로 설치 후 PATH 환경 변수 수정.
문제 2: Elastic Beanstalk 업데이트 실패 (ascii codec 에러)
원인: 환경 변수나 출력에서 비ASCII 문자가 포함됨.
해결: LANG 및 LC_ALL을 en_US.UTF-8로 설정하여 UTF-8 환경 보장.
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8