.github/workflows/cd.yml
name: CD
run-name: ${{ github.event.workflow_run.name }}
맨 위의 name은 workflow의 이름임, Actions탭에서 보이는 라벨 이름이 CD로 나옴
run-name은 Github Actions 실행 인스턴스의 이름을 정해줌
${{ github.event.workflow_run.name }} 은 GitHub Context의 변수를 사용한 것이다.
이 CD Workflow의 트리거인 이전 Workflow를 가져오는데, 그걸 우리가 CI로 지정한다.
따라서 이 CD라는 이름의 workflow가 실행되면 이름이 CI에서 정한 CI Workflow같은걸 그대로 가져온다.
일반적으로 CI와 CD는 나뉘어져있다.
CI는 코드를 빌드하고, 테스트 및 커버리지 확인 등을 하며 ECR 등에 Docker 이미지를 푸시하는 것까지가 CI이다.
CD는 CI가 끝나면 그걸 트리거로 실행되며, ECS 서비스를 업데이트하고 EC2 서버에 배포하는 것까지가 CD이다.
그래서 on.workflow_run을 써서 CI Workflow가 성공하면 CD Workflow를 실행시키는 구조가 일반적이라고 한다.
on:
workflow_run:
workflows: [ "CI" ]
types:
- completed
branches: [ dev ]
on : 은 GitHub Actions의 Workflow 실행조건을 지정하는 부분이라고 한다.
workflow_run은 다른 워크플로우가 실행되고 나서 트리거되는 이벤트라고 함
workflows: [ "CI" ] 에서 CI는 .github/workflows/ci.yml 안에 있는 name을 지정한다.
name: CI라고 적혀있으면 그 CI 부분을 바라보게됨
즉 "CI"라는 이름의 workflow가 실행을 끝내면, 이 워크플로우가 시작된다.
types: - completed 는 어떤 상태일때 트리거할 것인가? 를 지정한다
completed는 CI workflow가 성공하든, 실패하든 취소되든 어떤 결과로든 '끝나면' 이 워크플로우를 실행시킨다.
일반적으로는 conclusion == 'success'라는 조건을 step 레벨에 추가해서 CI가 성공할 경우에만 CD를 실행되도록 한다.
branches: [ dev ] 는 어떤 브랜치에서 workflow가 실행될 때 트리거할 지 제한한다고 한다.
우리가 컨벤션에서 dev 브랜치에서 CI가 '끝났을 때'에 CD를 실행시키기로 했으니 dev를 적었다. 일단 돌아가는지 봐야하고, 개발중이니까..
따라서 main은 영향을 주지 않으며 dev에서 개발할 수 있게 된다.
jobs:
deploy:
runs-on: ubuntu-latest
env:
ECS_CLUSTER: <ECS 클러스터>
ECS_SERVICE: <ECS 서비스>
ECS_TASK_FAMILY: <ECS 태스크>
CONTAINER_NAME: <컨테이너 이름>
IMAGE: <ECR에 올라가는 이미지명>
AWS_REGION: <지역명>
cd.yml도 마찬가지로 on: 과 jobs: 가 하나씩 필요하다
여러개의 job을 지정했으며, deploy라는 이름으로 하나만 정의했다. 실제로는 여러개의 job을 정의할 수 있다고 한다.
deploy는 job의 이름을 의미한다.
runs-on: ubuntu-latest는 job이 실행된 러너 환경을 지정한다.
ubuntu-latest는 GitHub에서 제공해주는 최신 우분트 리눅스 VM을 의미한다.
여기에서 Docker, AWS, ecs-cli 같은 명령어를 실행한다고 함
env:는 이 cd.yml job 전체에서 사용할 환경변수를 정의한다.
이후 스탭들에서 ${{ env.ECS_CLUSTER }} 와 같은 형태나 $ECS_CLUSTER 형태로 참조할 수 있다.
다른건 익숙했지만 CONTATINER_NAME에 Task Definition 안에 정의된 '컨테이너 이름'을 실수했었다.
컨테이너 이름이 아니라 태스크 정의 이름을 적어버려서 제대로 태스크 정의가 적용되지 않았었음
steps:
- uses: actions/checkout@v4
- name: AWS ECS Configure
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
job 안에 있는 step들에 대해서
uses: actions/checkout@v4는 GitHub Actions의 checkout 액션을 사용하겠다는 것임
내 로컬저장소의 코드를 runner로 가져온다.
이걸 사용해줘야 Dockerfile, taskdef.json 같은 배포에 필요한 파일들을 runner에서 사용할 수 있다.
일반적으로 모든 워크플로우의 첫번째 스탭에서 사용된다
uses: aws-actions/configure-aws-credentials@v4 는 마찬가지로 GitHub 공식 AWS Action임
이걸 사용하면 GitHub Actions runner 안에서 AWS CLI, SDK에 사용할 자격증명이 자동으로 세팅된다
이걸 해줘야 AWS와 관련된 작업을 러너가 할 수 있음
- name: Stop running ECS tasks
run: |
CLUSTER="${{ env.ECS_CLUSTER }}"
SERVICE="${{ env.ECS_SERVICE }}"
TASKS=$(aws ecs list-tasks --cluster $CLUSTER --service-name $SERVICE --desired-status RUNNING --query 'taskArns' --output text)
if [ -n "$TASKS" ]; then
echo "Stopping running tasks: $TASKS"
for TASK in $TASKS; do
aws ecs stop-task --cluster $CLUSTER --task $TASK
done
else
echo "No running tasks found."
fi
여기 꽤나 중요한데, 기존에 ECS에서 실행중인 태스크를 중지하는 단계이다.
그래야 새 태스크 정의를 배포할 때 기존 태스크와 혼선이 없어져서 ECS가 깔끔해진다.
우선 CLUSTER= , SERVICE= 명을 작성해서 클러스터명, 서비스명을 기입해준다
TASKS=$(aws ecs list-tasks
--cluster $CLUSTER
--service-name $SERVICE
--desired-status RUNNING
--query 'taskArns'
--output text)
aws ecs list-tasks 명령어를 통해서 현재 실행중인 TASK의 ARN목록을 가져온다.
-- cluster는 어떤 클러스터에서 찾을지, --service-name은 어떤 서비스의 Task에 대해서만 볼지,
--desired-status RUNNING은 '실행'중인 Task만 조회하며,
--query 'taskArns'는 쿼리로 JSON에서 task ARN만 뽑는다.
--output text는 문자열로 반환한다는 의미.
이걸 통해 조회되는게.. arn:aws:ecs: 어쩌고 저쩌고.. 하는게 조회된다
아래는 조건문, 반복문 형태로 읽으면 편하다
if [ -n "$TASKS" ]; then 만약 [-n "$TASKS"] << 변수에 값이 있다면, 즉 실행중인 태스크가 있다면?
echo "Stopping running tasks: $TASKS" << 이걸 출력하면서?
for TASK in $TASKS; do << for문을 TASK:$TASK로 돌리는데?
aws ecs stop-task --cluster $CLUSTER --task $TASK << 해당(실행중)되는 태스크를 모두 종료시킨다
done << for문 끝
else << 그 외에는?
echo "No running tasks found." << 이걸 출력시킴
fi << if문 끝
aws ecs stop-task --cluster $CLUSTER --task $TASK 이걸 통해서 자동으로 새 태스크를 띄우고 기존 태스크를 내리는게 중요하다.
- name: Task Definition file Rendering
id: render
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ecs/taskdef-base.json
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ env.IMAGE }}:latest
aws-actions/amazon-ecs-render-task-definition@v1는 GitHub에서 제공하는 ECS Helper Action이다.
JSON 형태의 ECS Task Definition 파일을 받은 후 특정 컨테이너의 이미지를 원하는 값으로 치환해서 새로운 태스크 정의 JSON을 만들어줌
여기선 우리 컨벤션에 맞게 ecs/taskdef-base.json 파일을 받게 해서 컨테이너 이름과 이미지에 맞게 태스크 정의를 만들었다.
- name: ECS Deploy
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ecs/taskdef-base.json
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true
마지막으로 ECS에 새 태스크 정의를 배포한다.
aws-actions/amazon-ecs-deploy-task-definition@v2 는 Github AWS 배포 액션임
새로운 태스크 정의를 ECS에 등록하고, 지정된 클러스터나 서비스에 적용해서 실제 배포를 진행시킨다.
배포 과정을 자동화해주기 때문에 직접 aws ecs register-task-definition + aws ecs update-service를 사용하지 않아도 된다.
wait-for-service-stability: true 이건 새 태스크 정의를 적용시키고 ECS 서비스가 안정 상태(Healty)가 될 때까지 기다린다.
이 과정에서 새 컨테이너들이 정상 가동되어 헬스체크를 통과할 때까지 워크플로우는 블로킹된다.
false라면 배포 명령만 던지고 바로 끝난다고 함..
아래는 cd.yml 전문
name: CD
run-name: ${{ github.event.workflow_run.name }}
on:
workflow_run:
workflows: [ "CI" ]
types:
- completed
branches: [ dev ]
jobs:
deploy:
runs-on: ubuntu-latest
env:
ECS_CLUSTER: <ECS 클러스터>
ECS_SERVICE: <ECS 서비스>
ECS_TASK_FAMILY: <ECS 태스크>
CONTAINER_NAME: <컨테이너 이름>
IMAGE: <ECR에 올라가는 이미지명>
AWS_REGION: <지역명>
steps:
- uses: actions/checkout@v4
- name: AWS ECS Configure
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}
- name: Stop running ECS tasks
run: |
CLUSTER="${{ env.ECS_CLUSTER }}"
SERVICE="${{ env.ECS_SERVICE }}"
TASKS=$(aws ecs list-tasks --cluster $CLUSTER --service-name $SERVICE --desired-status RUNNING --query 'taskArns' --output text)
if [ -n "$TASKS" ]; then
echo "Stopping running tasks: $TASKS"
for TASK in $TASKS; do
aws ecs stop-task --cluster $CLUSTER --task $TASK
done
else
echo "No running tasks found."
fi
- name: Task Definition file Rendering
id: render
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ecs/taskdef-base.json
container-name: ${{ env.CONTAINER_NAME }}
image: ${{ env.IMAGE }}:latest
- name: ECS Deploy
uses: aws-actions/amazon-ecs-deploy-task-definition@v2
with:
task-definition: ecs/taskdef-base.json
service: ${{ env.ECS_SERVICE }}
cluster: ${{ env.ECS_CLUSTER }}
wait-for-service-stability: true'Monew' 카테고리의 다른 글
| 개인 개발 리포트 (1) | 2025.09.22 |
|---|---|
| S3에 백업한 뉴스 기사를 '복구'하는 기능 (0) | 2025.09.19 |
| S3 버킷에 데이터를 '백업'하는 로직 (0) | 2025.09.16 |
| CI(지속적 통합) 작성 (0) | 2025.09.12 |
| build.gradle (0) | 2025.09.04 |