AWS 계정을 지원받으며 프로젝트를 진행하고 있는 상황에서 주최측의 AWS CodeDeploy 사용 허가가 나지않아 다른 방법을 찾아 CI/CD를 시도하게 되었습니다.
저의 목적은,
Github Repo와 EC2를 연동해 PR이 발생할 때마다 자동으로 Git Pull이 이루어지도록
배포 파이프라인을 구성하고자 합니다.
CD(Continuous Delivery)는 원래 AWS CodeDeploy를 사용하려했으나,
아쉽게 사용하지 못하는 상황이기에 appleboy/ssh-action를 이용해 EC2 인스턴스에 SSH로 직접 접근하여 Shell Script를 동작시키는 방법을 시도했습니다.
이 방법은 빌드를 EC2 인스턴스 상에서 진행하기 때문에 EC2 메모리 등의 자원을 사용하여 서비스에 영향을 줄 수 있습니다.
Github Actions를 사용하기 위해서
Github repository에 /.github/workflows/ 경로를 생성하고, Github Actions 파일을 작성하겠습니다.
# deploy.yml
name: Deploy
# PR 혹은 Push가 되면 trigger
on:
workflow_dispatch:
push:
branches:
- develop
jobs:
deploy:
runs-on: ubuntu-latest
steps:
# Github Actions의 IP를 보안 그룹에 허용하기 위해
# Github Actions의 IP를 출력하여 다음 step에서 접근할 수 있게함
- name: Get Github Actions IP
id: ip
uses: haythem/public-ip@v1.2
# AWS IAM 계정을 등록 (EC2FullAccess 필수)
# AWS CLI 사용을 위함
- 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: ap-northeast-2
# AWS CLI로 위에서 얻은 Github Actions의 IP를 EC2 보안그룹 인바운드 규칙에 등록
- name: Add Github Actions IP to Security group
run: |
aws ec2 authorize-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32
# appleboy/ssh-action를 사용해 SSH로 EC2에 직접 접근 및 명령 수행 (CD 작업 -> script)
# SSH로 Bastion host에 접속
# Baston host에서 Private EC2로 접근
- name: SSH Commands
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.AWS_BASTION_HOST_IP }}
username: ${{ secrets.AWS_BASTION_HOST_USERNAME }}
key: ${{ secrets.AWS_SECRET_KEY }}
# script 중간에 실패하는 순간 뒤에 있는 script는 실행하지 않음
script_stop: true
script: |
cd /home/ubuntu/.ssh
ssh -i team-ariel-1-private-key.pem ${{ secrets.AWS_AIRFLOW_HOST_USERNAME }}@${{ secrets.AWS_AIRFLOW_EC2_IP }} 'bash deploy.sh'
# AWS CLI로 보안그룹에 등록했던 Github Actions의 IP를 제거
- name: Remove Github Actions IP From Security Group
run: |
aws ec2 revoke-security-group-ingress --group-id ${{ secrets.AWS_SG_ID }} --protocol tcp --port 22 --cidr ${{ steps.ip.outputs.ipv4 }}/32

위 deploy.yml 파일은 develop 브랜치에 PR 혹은 Push가 발생하면 Trigger가 작동하도록 작성했습니다.
Trigger 작동했을 때, 수행하는 step들을 살펴보자면,
AWS EC2 Baston host에 접속할 수 있도록 보안 그룹에 Github Actions의 IP를 등록시켜줘야합니다.
이때, 필요한 Github Actions의 IP를 haythem/public-ip@v1.2 통해 얻어내고 "ip"란 이름의 key로 출력해줍니다.
( 이후 ${{ steps.ip.outputs.ipv4 }}로 출력한 IP를 받아올 수 있습니다. )
앞서 말했듯이 AWS 보안그룹에 IP를 등록시키기 위해선,
AWS CLI를 사용을 해야하는데 CLI를 사용하기 위해선 당연하게도 IAM 계정이 필요합니다.
EC2FullAccess 권한이 부여된 IAM 계정 정보를 github secrets에 저장하여 이를 보안 상 안전하게 불러와 aws-actions/configure-aws-credentials@v1에 등록을 해줍니다.
${{ secrets.AWS_ACCESS_KEY_ID }}${{ secrets.AWS_SECRET_ACCESS_KEY }}위 Parameter들은 문자 그대로 IAM 계정 Access_key와 Secret_Key를 등록해주면 됩니다.
이제 AWS CLI를 통해 Github Actions의 IP를
EC2 보안그룹의 인바운드 규칙에 추가해줍니다.
${{ secrets.AWS_SG_ID }}SSH를 통해 Bastion host에 접속하고
주요 명령(CD - 배포 작업)을 수행하는 단계입니다.
appleboy/ssh-action@v1.0.3를 사용하면
host, username, key 값을 통해 Public EC2 (Bastion host)에 SSH의 형태로 접속할 수 있습니다.
host : ${{ secrets.AWS_BASTION_HOST_IP }}
host는 접속 할 Bastion Host의 IP, 탄력적 IP를 설정해줬다면 EIP를 넣어주면 됩니다.
username : ${{ secrets.AWS_BASTION_HOST_USERNAME }}
username은 접속 할 EC2의 OS에 따른 계정 명을 입력해줍니다, 저의 경우엔 Ubuntu 환경이므로 ubuntu를 입력해주었습니다.
key : ${{ secrets.AWS_SECRET_KEY }}
이는 EC2를 생성할 때 적용한 Secret key .pem으로 된 파일의 내용을 그대로 복사해서 입력해주면 됩니다.
ex)
# private_key.pem
-----BEGIN RSA PRIVATE KEY-----
...
-----END RSA PRIVATE KEY-----
그 후에 script 부분에 적힌 부분을 Bastion host 내에서 shell로 실행시킵니다.
script_stop: true 옵션은 true로 설정했을 시에 script 구문이 중간에 실패했을 시 아직 실행하지 않은 script를 마저 실행하지 않도록 해줍니다.
제가 작성한 script 부분은 아래와 같습니다.
cd /home/ubuntu/.ssh
ssh -i team-ariel-1-private-key.pem ${{ secrets.AWS_AIRFLOW_HOST_USERNAME }}@${{ secrets.AWS_AIRFLOW_EC2_IP }} 'bash deploy.sh'
/home/ubuntu/.ssh 경로에 private ec2에 접근할 수 있는 .pem key 값을 저장해두었기 때문에 이 경로로 이동해주었고,
ssh -i 명령을 통해 private ec2에 접근하여,
private ec2에 만들어둔 배포용 Shell Script 파일 - deploy.sh를 실행시켜주었습니다.
Shell Script를 통한 배포가 완료되었으므로,
EC2를 빠져나와 접속할 때 등록했던 보안그룹 내의 IP를 삭제해줍니다.
즉, Github Actions의 동작 방식을 정리를 하자면,
- develop 브랜치에 PR 혹은 Push가 발생해 Trigger가 됩니다.
- Bastion Host 보안 그룹에 Github Actions의 IP를 등록합니다.
- SSH로 Bastion Host에 접근합니다.
- Bastion Host에서 Private EC2에 접근해 미리 만들어둔 배포용 Shell Script 파일을 동작시킵니다.
- 등록했던 Github Actions의 IP를 보안 그룹에서 삭제합니다.
정상 작동함을 확인할 수 있습니다.

deploy.sh에는 github repo에서 dags 폴더가 갱신되었을 때,
단순히 git pull을 통해 갱신된 내용을 받아올 수 있도록 작성해주었습니다.
# cd github repo
cd Studio-Recommendation-Service
# pull develop-branch
git pull origin develop
deploy.sh의 git pull 명령으로 갱신된 DAG들을 받아왔지만,
Airflow에서 사용하는 dags 폴더에는 이 내용이 반영되지 않고 있습니다.
그렇기 때문에 이 local github repo 내의 dags 폴더와
Airflow에서 사용하는 dags 폴더를 마운트로 연결해주었습니다.
기본적으로 ubuntu에서 디렉토리 간 Mount를 하는 방법은 아래와 같지만
mount -o bind [원본 디렉토리] [마운트 할 디렉토리]
이 방법은 OS가 종료되는 순간 마운트가 해제가 됩니다.
따라서 자동적으로 재부팅이 되는 순간 마운트가 되도록 설정을 해줄 것입니다.
먼저,
sudo vi /etc/rc.local
/etc/rc.local의 내용을 생성 혹은 수정해줍니다.
#!/bin/bash
sudo mount -o bind /home/ubuntu/git_repo/dags /var/lib/airflow/airflow/dags
마운트 명령을 입력했으면, 이제 /etc/rc.local 파일을 실행할 수 있도록 권한을 부여해줄 것입니다.
sudo chmod u+x /etc/rc.local
다음은,
sudo vi /lib/systemd/system/rc-local.service
rc-local.service에 내용을 추가해줍니다.
( 서비스를 동작시키기 위해 필요한 필수 작업 )
[Install]
WantedBy=multi-user.target
설정이 끝났으므로 서비스에 등록시켜 부팅될 때마다
자동으로 실행되도록 만들어줍니다.
sudo systemctl enable rc-local.service
sudo systemctl start rc-local.service
# 서비스 상태 확인
sudo systemctl status rc-local.service
이제 /etc/rc.local 에 작성했던 디렉토리 간 마운트 명령이
부팅이 될 때마다 자동으로 실행됩니다.