최근에 작업하고 있는 프로젝트에 API용 django가 하나있다. 이 django를 프론트가 사용할 수 있도록 EC2에 호스팅을 하는 중이다. 개발 중간중간에 변경사항들을 매번 인스턴스에 접속해서 재시작해주는 과정이 번거로워 간단하게 Github Actions를 이용해 code가 push되면 그 내용을 반영시켜 바로 배포되도록 구성을 해보았다.
이번에는 django 프로젝트를 EC2 인스턴스에 배포하는 Github Actions 파이프라인을 구성해보도록 한다.
EC2에 배포되는 django 서버는 Docker로 컨테이너화 시켜서 docker-compose를 이용해 배포하도록 한다.
이 글에서 다루는 전체적인 배포 파이프라인은 다음과 같다.
AWS Credential 설정
Github Actions를 실행하는 Runner는 호스팅된 가상머신이다. 다만, 이 Runner의 IP는 고정되어있지 않고 실행마다 계속 변경되는 부분이라 Runner가 EC2에 접근하기 위한 설정을 해주어야 한다. 배포되는 EC2가 보안그룹으로 관리되고 있다는 가정하에 파이프라인을 작성해보도록 한다.
물론 Self-hosted Runner나 고정된 Jenkins 등의 경우 이러한 작업이 필요없다.
먼저, 인스턴스의 보안그룹을 수정할 권한이 담긴 Policy를 생성한다. Security group의 Ingress에 Rule을 추가하는 AuthorizeSecurityGroupIngress와 Ingress에 Rule을 삭제하는 RevokeSecurityGroupIngress를 Policy에 추가해준다
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ec2:RevokeSecurityGroupIngress",
"ec2:AuthorizeSecurityGroupIngress"
],
"Resource": [
"arn:aws:ec2:*:[account-id]:security-group-rule/*",
"arn:aws:ec2:ap-northeast-2:[account-id]:security-group/[해당 인스턴스의 sg id]"
]
}
]
}
생성한 Policy를 연결한 user를 생성한 뒤, Access key Credential까지 발급받는다.
Dockerfile/docker-compose 작성
# Dockerfile
FROM python:3.10
WORKDIR /app
COPY . /app # .env 파일 관리 주의
RUN pip3 install -r requirements.txt
# docker-compose.yml
version: "3"
services:
web:
container_name: web
image: [hub username]/[image명]
volumes:
- .env:/app/.env # Application에 사용할 환경변수가 등록된 .env
command: python manage.py runserver 0.0.0.0:8000
ports:
- 8000:8000
이 django 프로젝트에는 API 키와 DB 관련 정보들을 .env 파일로 관리하고있다.
docker 이미지를 빌드해 hub에 push하기 전에 .env 파일은 빌드된 이미지에 말려 들어가지 않도록 확인해주어야한다.
해당 Dockerfile은 디렉토리 상에 .env파일을 잠시 지워두고 빌드를 진행했다.
.env 파일은 docker-compose를 이용해 이미지를 가져와 컨테이너를 up해줄때에 volume으로 연결해주었다.
Workflow 작성
프로젝트 Repo에 적용할 Workflow 파일을 다음과 같이 작성한다.
name: django deploy
on:
push:
branches:
- main
jobs:
deploy:
name: Deploy django project to EC2
runs-on: ubuntu-latest
steps:
- name: Checkout Repository # Repo checkout
uses: actions/checkout@v2
- name: Get Github Actions IP # Actions Runner IP확인
id: ip
uses: haythem/public-ip@v1.3
- name: Log in to Docker Hub # docker hub 로그인
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build Docker image # docker image 빌드
run: docker build . -t ${{ secrets.DOCKERHUB_USERNAME }}/gamst-web:latest
- name: Push Docker image to docker hub # 빌드한 이미지 hub에 push
run: docker push ${{ secrets.DOCKERHUB_USERNAME }}/gamst-web:latest
- name: Configure AWS Credentials # 보안그룹 변경을 위한 AWS Credential 설정
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: ap-northeast-2
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
- name: Add Ip to SG # Runner IP를 보안그룹에 추가
run: |
aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32
- name: Connect to EC2 # Runner가 해당 인스턴스에 접근
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.REMOTE_IP }}
username: ${{ secrets.REMOTE_SSH_USERNAME }}
key: ${{ secrets.REMOTE_SSH_PRIVKEY }}
script_stop: true
script: |
cd /home/ubuntu/GAMST
docker pull ${{ secrets.DOCKERHUB_USERNAME }}/gamst-web:latest
docker-compose up -d
- name: Revoke IP from SG # 작업이 끝나면, 추가해주었던 Runner IP 삭제
run: |
aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32
이 deploy Job의 Step은 크게 다음과 같다.
- Repository 코드 내려받기(checkout)
- 코드를 바탕으로 docker 이미지 build & hub에 push
- Runner가 인스턴스에 접근하기 위해 IP를 보안그룹에 추가
- 인스턴스에 SSH 접근 후, hub로부터 이미지 내려받아 재시작
- 작업 종료 후, 추가한 IP 삭제를 위한 보안그룹 수정
Secrets 등록
다음으로 Workflow가 동작할때 필요한 몇몇 Secret 값을 등록해주어야한다. docker hub password(token)이나 EC2 보안그룹을 변경하기 위한 IAM Credential과 같은 값들은 외부로 노출되지 않도록 Secret으로 저장하여 사용한다.
Repository의 Setting > Security > Secrets and variables > Actions 에서 해당 Repo의 Action workflow에서 사용할 Secret들을 관리할 수 있다.
New repository secret으로 새로운 Secret 값을 등록할 수 있고, 이렇게 등록한 Secret은 workflow 내에서 secret Context 형식으로 사용할 수 있다.
코드 push
workflow에 작성한 이벤트 트리거(on 절)에 따라 main branch에 push하면 Actions가 동작하게 되고, Workflow가 성공적으로 수행되면
실행된 Workflow의 step별로 진행과정도 살펴볼 수 있다.
이렇게 EC2 인스턴스에 Github Actions를 이용해 작은 프로젝트를 배포해보았다. 보통 이렇게 배포될 환경이 EC2 인스턴스 같은 상황에는 CodeDeploy같은 서비스를 통해 배포쪽 파이프라인을 구성한다고 한다.
그도 그럴 것이 인스턴스가 외부와 차단된 Private Subnet에 위치하면 이번에 작성한 Workflow와 같이 Actions Runner가 접근할 수 없다.
VPC와 CodeDeploy의 연결을 통해 퍼블릭 인터넷을 통하지 않고 VPC에 위치한 리소스에 배포할 수 있다고 한다.
다음은 빌드에는 Github Actions와 배포에는 CodeDeploy를 사용해 CI/CD 파이프라인을 구성해보도록 한다.