미소를뿌리는감자의 코딩

[CatchLounge] Flask를 이용한 CI/CD 구성 본문

프로젝트

[CatchLounge] Flask를 이용한 CI/CD 구성

미뿌감 2024. 9. 5. 10:25
728x90

1. 개요

이번에 Flask를 기반으로 한 프로젝트를 진행하게 되면서 CI/CD를 적용시키게 되었다.

Spring(Java)를 기반으로 한 배포가 익숙하였기에, 새로운 도전이 되었다.

 

이전에 하던 배포와 유사하게, github dev에 push가 되게 되면, github actions가 동작하도록 구성하였다.

이후, docker image를 만들고 이를 ECR에 올렸다.

EC2에서 docker-compose를 이용해서, ECR에서 이미지를 가져와서 배포를 하도록 하였다.

 

항상 헷갈리기에 또 정리해보는~! docker는 왜 사용하는 것일까?

 

docker의 정의: 개발자가 컨테이너를 구축, 배포, 실행 즉, 관리할 수 있게 해주는 오픈 소스 플랫폼이다.

 

도커 이미지로 언어와 프레임워크를 정의를 해 둔 후, 이를 컨테이너화 시켜서 프로그램을 구동시키면

로컬 환경에 간섭이 없이 배포를 진행할 수 있기 때문에 docker를 사용한다.

또한 Dockerfile 을 이용해서, package 등을 설정해둘 수 있다.

 

 

2. 적용

우선, .github/workflows 아래에 위치시킬, yml 파일을 확인하여 보자.

 

name: Deploy to EC2 using Docker and ECR

on:
  push:
    branches:
      - dev

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.12'

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

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

    - name: Login to Amazon ECR
      run: aws ecr get-login-password --region ap-northeast-2 | docker login --username AWS --password-stdin ${{ secrets.ECR_URI }}

    - name: Build Docker image
      run: docker build -t repository .

    - name: Tag Docker image
      run: docker tag repository:latest ${{ secrets.ECR_URI }}:latest

    - name: Delete latest image from ECR
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_PUBLIC_IP }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_SSH_KEY }}
        script: |
          aws ecr batch-delete-image --repository-name repository --image-ids imageTag=latest

    - name: Push Docker image to ECR
      run: docker push ${{ secrets.ECR_URI }}:latest

    - name: Login to ECR on EC2
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_PUBLIC_IP }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_SSH_KEY }}
        script: |
          aws ecr get-login-password --region ap-northeast-2 | sudo docker login --username AWS --password-stdin ${{ secrets.ECR_URI }}

    - name: Stop running containers on EC2
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_PUBLIC_IP }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_SSH_KEY }}
        script: |
          sudo docker ps -q | xargs -r sudo docker stop

    - name: Remove all containers on EC2
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_PUBLIC_IP }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_SSH_KEY }}
        script: |
          sudo docker ps -asq | xargs -r sudo docker rm

    - name: Delete images on EC2
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_PUBLIC_IP }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_SSH_KEY }}
        script: |
          sudo docker images -q | xargs -r sudo docker rmi

    - name: Prune unused Docker resources on EC2
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_PUBLIC_IP }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_SSH_KEY }}
        script: |
          sudo docker system prune -af

    - name: Deploy using Docker Compose
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: ${{ secrets.EC2_PUBLIC_IP }}
        username: ${{ secrets.EC2_USERNAME }}
        key: ${{ secrets.EC2_SSH_KEY }}
        script: |
          cd /home/ubuntu/docker-compose
          export ECR_URI=${{ secrets.ECR_URI }}
          sudo docker-compose down
          sudo docker-compose pull
          sudo docker-compose up -d

 

이는 dev에 push 가 올라오면, 실행되는 코드이다.

    - name: Set up Python
      uses: actions/setup-python@v4
      with:
        python-version: '3.12'

 

이전 코드와 달라진 부분이 있다면, Python으로 설정을 해두었다는 것이다. [ Flask 니까]

 

이후, aws에서 자격 증명을 획득하고, ECR에 접근한다.

도커 이미지를 build 해서 container에서 실행시킬 환경을 이미지화 해준다.

이후, EC2에서 ECR에 접근한 후 이미지를 가져와서 컨터이너를 실행시켜 준다.

이후 ec2에서 Dockerfile을 열고, 이를 

마지막 단계로 미리 ec2에 저장을 시켜둔 docker-compose.yml파일을 실행시켜, 배포를 완료해 준다.

 

아래는 Dockerfile이다.

FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .

RUN pip install --upgrade pip
RUN pip install -r requirements.txt

COPY . .

EXPOSE 8080
ENV FLASK_ENV=development
CMD ["flask", "run", "--host=0.0.0.0", "--port=8080"]

 

ec2에서 실행시킬 파일이라고 할 수 있다.

requirements.txt를 복사하고 실행시켜서, module과 환경 설정 등을 해줄 수 있도록 하였다.

Flask==3.0.3                # 웹 프레임워크
pymongo==4.8.0              # MongoDB 연결 라이브러리
APScheduler==3.10.4         # 스케줄링 라이브러리
bcrypt==3.2.0               # 비밀번호 해싱
blinker==1.8.2              # Flask에서 신호/이벤트 관련
certifi==2020.6.20          # SSL 인증서
chardet==4.0.0              # 텍스트 인코딩 탐지
click==8.1.7                # 명령줄 인터페이스 생성 도구
colorama==0.4.4             # 콘솔 출력 색상 처리
cryptography==3.4.8         # 암호화 관련 기능 (JWT 생성 시 사용)
dnspython==2.6.1            # DNS 처리 라이브러리 (MongoDB의 DNS 연결 시 필요할 수 있음)
idna==2.10                   # 국제화 도메인 이름 지원
itsdangerous==2.2.0         # 안전한 데이터 서명 (Flask에서 사용)
Jinja2==3.1.4               # Flask 템플릿 엔진
MarkupSafe==2.1.5           # 템플릿에서 안전한 HTML 처리
PyJWT==2.3.0                # JSON Web Token (JWT) 생성 및 검증
requests==2.25.1            # HTTP 요청 처리
six==1.16.0                 # Python 2 및 3 호환성 지원
tzlocal==5.2                # 로컬 타임존 처리
urllib3==1.26.5             # HTTP 클라이언트
Werkzeug==3.0.3             # WSGI 유틸리티 (Flask 내부에서 사용)
zipp==1.0.0                 # 패키징 관련

Flask-SocketIO==5.3.4       # Flask용 SocketIO 확장
python-socketio==5.7.2      # SocketIO 프로토콜 지원
python-engineio==4.3.4      # 엔진IO 프로토콜 지원

 

pip freeze > requirements.txt

로 배포에 필요한 모듈을 txt 파일화 시켜서 루트 폴더에 위치시켜 주었다.

 

아래는 docker-compose.yml 파일이다.

version: '3.8'

services:
  app:
    image: 00000000.dkr.ecr.ap-northeast-2.amazonaws.com/repository:latest
    container_name: app_container
    ports:
      - "8080:8080"
    environment:
      MONGO_URI: mongodb://mongo:27017/db
    depends_on:
      - mongo


  mongo:
    image: mongo:latest
    container_name: mongo_container
    ports:
      - "27017:27017"
    volumes:
      - mongo_data:/data/db

volumes:
  mongo_data:

container로 구동시킬 서비스는 2개가 있다.

하나는 python 프로젝트이며, 다른 하나는 mongodb가 있다.

 

app: 에서, 환경으로 mongodb를 설정해 두어 mongodb와 연관되어서 프로젝트가 실행될 수 있도록 하였다.

 

mongodb의 경우 ec2에서 접근을 통해 collection들의 내용 또한 확인할 수 있는데, 이는

mongosh mongodb://localhost:27017/mydatabase

 

이 코드로 접근해서 collection 내용들을 확인할 수 있다.

728x90