
이번 글에서는 GitHub Actions를 활용해서 백엔드 프로젝트를 AWS EC2에 자동 배포하는 실제 워크플로우를 공유해본다.
기본적으로 Github Action은 public 레포지토리에선 무제한, private 레포지토리에선 한달 2000분의 사용량을 무료로 제공한다.
예전 스프링으로 자바 개발을 할땐, jar파일 빌드 후 tomcat에 deploy하는 것 으로 배포가 끝나다보니 워크플로우를 생성하는 것 보다 수동으로 진행하는게 더 빠르고 간편했다.
하지만 nest.js프로젝트를 시작하면서 서버마다 달라지는 환경을 맞추기 위해 docker로 구성하려다 보니 배포과정이 이만저만이 아니었다.
그 결과 매번 이렇게 귀찮은 작업을 진행하느니 워크플로우를 잘 만들어두면 고생을 덜 수 있다고 판단해 공부하는겸 진행해보았다.
처음엔 push → build → scp → ssh 실행이 전부겠거니 했지만, 막상 구성하다 보니 보안 그룹 열고 닫는 것, IP 자동 등록, 환경변수 파일 전송까지 하나하나 직접 손봐야 할 게 많았다.
한 번 잘 구성해두면 다음부터는 커밋만 툭 올리면 자동 배포된다.
특히 팀원이 여러 명일 때나 반복 작업이 많을 때는 꼭 한 번 도입해볼 만하다.
전체 흐름 요약
이 워크플로우는 다음과 같은 순서로 실행된다:
main 브랜치에 푸시됨
→ GitHub Actions 워크플로우 시작
→ 현재 러너의 IP 확인 (curl ifconfig.me)
→ EC2 보안 그룹에 IP 등록 (22포트 허용)
→ Docker 이미지 빌드 → `.tar` 파일로 저장
→ `.env` 파일 및 `.tar` 이미지 파일 EC2로 업로드 (scp)
→ EC2에서 배포 스크립트 실행 (`deploy.sh`)
→ 보안 그룹에서 IP 제거 (SSH 접근 차단)
→ Slack으로 배포 성공/실패 메시지 전송핵심 포인트 설명
1) IP 동적 등록 (보안 그룹 열기)
GitHub Actions의 러너는 매 실행마다 IP가 달라진다.
그래서 아래처럼 curl로 현재 IP를 가져와서 보안 그룹에 등록해야 한다.
- name: Get runner IP
id: ip
run: echo "IP=$(curl -s https://ifconfig.me)" >> $GITHUB_OUTPUT
그리고 EC2에 SSH 연결을 허용하기 위해 아래처럼 authorize-security-group-ingress 명령을 실행한다.
- name: Allow SSH from GitHub Actions IP
run: |
aws ec2 authorize-security-group-ingress \
--group-id ${{ secrets.SECURITY_GROUP_ID }} \
--protocol tcp --port 22 \
--cidr ${{ steps.ip.outputs.IP }}/32 \
--region ${{ secrets.AWS_REGION }}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.AWS_REGION }}
ip를 허용했으면 다시 닫아야겠지
반드시 워크플로우를 종료하기전에 허용한 ip를 닫아주자.
- name: Remove SSH access from GitHub Actions IP
if: always()
run: |
aws ec2 revoke-security-group-ingress \
--group-id ${{ secrets.SECURITY_GROUP_ID }} \
--protocol tcp --port 22 \
--cidr ${{ steps.ip.outputs.IP }}/32 \
--region ${{ secrets.AWS_REGION }}
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
AWS_REGION: ${{ secrets.AWS_REGION }}
Docker 이미지 .tar 파일로 저장
build.sh를 구성해 스크립트 안에서 Docker 이미지를 빌드하고 .tar로 저장한 뒤, 이 파일을 EC2로 전송하는 구조다.
chmod +x ./build.sh
./build.sh
"build.sh" 이 스크립트 안에서 docker build, docker save 명령을 실행하게 된다.
scp를 통한 파일 전송
appleboy/scp-action을 활용해 .env 파일과 .tar 이미지를 EC2로 전송한다.
- name: Upload env to EC2
uses: appleboy/scp-action@v0.1.3
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_KEY }}
port: ${{ secrets.EC2_PORT || 22 }}
source: .env.dev
target: 'target/path'
- name: Upload Docker image to EC2
uses: appleboy/scp-action@v0.1.3
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_KEY }}
port: ${{ secrets.EC2_PORT || 22 }}
source: image.tar
target: '/target/path'
SSH 원격 명령 실행
EC2 내에서 .tar 파일을 불러와 컨테이너를 재시작하는 deploy.sh를 실행한다.
- name: Deploy to EC2
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.EC2_HOST }}
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_KEY }}
port: 22
script: |
chmod +x /deploy/path/deploy.sh
/deploy/path/deploy.sh
Slack 알림 전송
github action의 기본 설정은 실패했을때 email로 알림이 오게된다.
성공했을땐 알림이 안온다...
워크플로우 실행 후 github action을 들어가서 deploy창을 하염없이 바라보고 있는 내 자신을 보고 현재 사용중인 슬랙에 웹훅을 이용해
메세지를 보낼 수 있도록 추가했다.
성공했을 때와 실패했을 때 각각 Slack에 알림을 보내는 로직도 포함되어 있다.
메세지에 <@UID> 를 사용해 슬랙 사용자 mention ID로, 알림을 받는 대상을 설정할 수 있다.
- name: Notify Slack on Success
if: success()
run: |
curl -X POST -H 'Content-type: application/json' --data '{...}' ${{ secrets.SLACK_WEBHOOK_URL }}
- name: Notify Slack on Failure
if: failure()
run: |
curl -X POST -H 'Content-type: application/json' --data '{...}' ${{ secrets.SLACK_WEBHOOK_URL }}
최종 정리
이 자동화 워크플로우를 구축하면서 느낀 점은 다음과 같다:
보안 그룹 자동 등록은 매우 유용하지만 동시에 조심해야 할 부분이기도 하다. IP 등록 후 반드시 해제하도록 구성하는 게 중요하다.
scp, ssh, slack까지 연결하면 깔끔하게 하나의 흐름으로 끝낼 수 있다.
docker build만 남기고 EC2에서 돌리는 방식이 오히려 간단할 수 있지만, CI 환경에서 미리 빌드한 이미지를 전달하는 게 더 안정적이다.
커밋 메시지, 워크플로우 링크 등을 슬랙에 넣으면 기록 관리도 잘 된다.
이제 커밋만 올려도 배포가 자동으로 되니, 개발 속도도 빨라지고 실수도 줄어든 느낌이다.
'devops' 카테고리의 다른 글
| [DevOps 삽질일지] Vercel Hobby + Private Repo로 모노레포 배포하기 (1) | 2025.07.07 |
|---|