개요
- 현재 CD 는 어떤 구성으로 되어있는가?
- 잘 돌아가나?
- 문제점 파악 및 효율적인 방안 고민
- CodeBuild + Github Action 구성 방안
- 현재는?
현재 회사에서는 Github Action을 Self Hosted 로 사용하고있다 (2024.8 ~ 현재까지)
조금은 특이? 하게 사용하고 있다.
원래의 Action을 Self Hosted로 사용하는 대부분의 활용예제는 EC2 Computing 1대 = Action 방식으로 사용하고 있었고,
내가 생각했을때는 좀 비효율적인 방식이라고 생각하여, Computing Spec 하나에 Github Action Image를 여러개 둬서 병렬 처리로 하는 방식을 활용했다.
현재 CD는 어떤구성으로 되어있는가?
대략적인 구조도는 이런 형태로 되어있다. 순서대로 CD 구성을 보면
- 개발자는 Git Repository에 Merge / Tag 배포를 진행하게 된다.
- 자체 Action 내에서 Lambda를 호출하고 해당 Lambda 함수는 Self Hosted로 된 EC2를 (Stopping -> Running) 상태로 변경한다.
- 이 후 Running 상태의 EC2는 각각의 action 이미지를 빌드하여 CD를 진행하게 된다.
잘 돌아가나?
6~7 개월 사용하다보니, 잘 돌아가긴 하지만 살짝살짝 애매한 부분이 하나씩 들어났다.
1. 동작의 불확실 성
아무래도 람다 - EC2 형태로 구성하다보니, 가끔 Ec2가 안뜨거나, Lambda 함수가 호출이 안되는 등
꽤 여러문제가 발생하였다. 사실 비용문제때문에 Action이 안돌때는 저렇게 한건데....
저 형태를 떼어내려 하니, 결국 사용하지 않을때는 EC2는 Stop 형태로 만들어야 한다 이런 결론이 나왔다.
2. EC2 관리
Self hosted로 된 EC2를 관리하다보니, 처음 EC2 + Action로 관리를 하니 아래파일들을 만들게되었다.
파일이름 | 내용 |
Dockerfile | Docker 환경에서 Action runner 실행을 위한 파일 |
runner-install.sh | Github Action Runner 추가 스크립트 |
runner-remove.sh | Github Action Runner 삭제 스크립트 |
runner-docker-up.sh | 람다에서 호출하는 Action Runner 추가 스크립트 |
runner-docker-down.sh | 람다에서 호출하는 Action Runner 제거 스크립트 |
runner-instance-update.sh | EC2 최신상태 유지 (리눅스 패키니, Action Runner, Image) |
relaim-disk-space.sh | 오래된 리소스 정리 (Disk, Docker System, Volume...) |
slack-noti-sh | Slack Noti |
사실 이런 문제 말고도, EC2를 관리하는 유지보수는 적지 않았다.
Multi Account로 관리하고 있는 만큼 환경 별로 EC2를 1개씩 둬야 했고,
그에따라 디스크 정리 및 여러부분을 관리하였다 (cron이나 여러부분에서 자동화를 하더라도... 문제는 존재함)
문제점 파악 및 효율적인 방안 고민
그럼 총 2가지 측면에서 고려해봐야 한다고 생각했다.
- 동작의 불확실성을 최대한 피하고, 사용할때만 CD 구성이 필요함
- EC2 자체의 관리를 최소화 하며, 설치 및 유지보수 비용이 간단해야 한다.
그 과정에서 고민한 결과들을 보면 하나로 대락적으로 모아졌다. -> Serverless
CodeBuild + Github Action 구성
언젠가 본적이 있었다 (2024년 인가...)
그 당시에는 굉장히 experiment 한 기능으로 생각했는데, 어쩌면 우리가 찾는 기능일지 몰라 테스트를 진행하고
관련해서 정말 간단하게 구성해보자고 팀원들과 PoC를 진행하였다.
PoC 및 실제 환경에 적용하기 위한 단계를 만들었는데 아래와 같다.
- CodeBuild 구성을 어떻게 관리할 것인가?
- 각 환경별로 Action 호출을 어떻게 할 것 인가?
- Action 스크립트는 어떻게 관리할 것 인가?
CodeBuild 구성을 어떻게 관리할 것인가?
이건 꽤 간단하였다.
우리 우리팀은 Terraform으로 대부분의 AWS 인프라를 관리하고 있었고,
개발 / 스테이징 / 운영 별로 하나하나씩 만들되 구성 자체는 Terraform 으로 관리하는 것을 목표로 하였다.
또한 CodeBuild의 경우 buildspec 을 Update를 해줘야 하는데, 그 부분도 Terraform 으로 관리하는것으로 진행하였다.
version: 0.2
env:
parameter-store:
DOCKERHUB_PASS: /common/DOCKERHUB_PWD
DOCKERHUB_USERNAME: /common/DOCKERHUB_USERNAME
phases:
install:
commands:
- echo $CODEBUILD_BUILD_ID
- echo "Installing AWS Copilot CLI for ARM64..."
- aws s3 cp s3://[s3-bucket]/ssm ssm
- chmod +x ssm
- sudo mv ssm /usr/local/bin/ssm
pre_build:
commands:
- docker login --username $DOCKERHUB_USERNAME --password $DOCKERHUB_PASS
post_build:
commands:
- export CODEBUILD_PROJECT_NAME=$(echo $CODEBUILD_BUILD_ID | cut -d":" -f1)
- S3_ARTIFACT_FILE="s3://codebuild-${CODEBUILD_PROJECT_NAME}/results/failure_build_${CODEBUILD_BUILD_ID}.txt"
- export CODEBUILD_BUILD_SUCCEEDING=2
- aws s3 ls "$S3_ARTIFACT_FILE" > /dev/null 2>&1 || (echo "No error artifacts."; export CODEBUILD_BUILD_SUCCEEDING=1;)
- if [ "${CODEBUILD_BUILD_SUCCEEDING}" -ne 1 ]; then
aws s3 cp "$S3_ARTIFACT_FILE" "build-status.txt";
if grep -q "BUILD_FAILED=true" ./build-status.txt; then
echo "Build failed in GitHub Actions.";
export CODEBUILD_BUILD_SUCCEEDING=0;
else
echo "Build succeeded in GitHub Actions.";
fi
fi
- if [ "${CODEBUILD_BUILD_SUCCEEDING}" -eq 0 ]; then exit 1; fi
각 환경별로 Action 호출을 어떻게 관리할 것 인가?
개발 / 스테이징 / 운영별로 Action을 만들고 러너의 대한 호출은 Action에 Workflow 이름 패턴으로 구분하였다.
code build 에서는 필터그룹을 활용하여, 실제 부합하는 조건에 Action만 호출할 수 있다.
- ^(action.*dev-deploy)$
- ^(action.*stg-deploy)$
- ^(action.*prd-deploy)$
Action 스크립트는 어떻게 관리할 것인가?
기본적으로 CD 구성은 Service Repository 와 CI/CD 에필요한 파일들은 구분해놨기 때문에,
해당 부분은 아래 그림과 같이 구성하였다. (그림으로 대체)
name: action-[service]-dev-deploy
on:
push:
tags:
- '[service]-dev-20[2-3][0-9][0-1][0-9][0-3][0-9]-[0-9][0-9]'
workflow_dispatch:
## [Required]
env:
ENVIRONMENT: dev
SERVICE_NAME: [service]
SLACK_CHANNEL: [slack-channel]
S3_BUCKET: [s3-bucket]
DOCKERFILE_PATH: [dockerfile-path]
## [Require Change runs-on]
jobs:
build:
runs-on:
- [codebuild-name]-${{ github.run_id }}-${{ github.run_attempt }}
- buildspec-override:true
strategy:
matrix:
node-version: [20.x]
steps:
- name: Checkout tag
uses: actions/checkout@v4
with:
ref: ${{ github.ref }}
- name: Get package version
run: |
echo "CURRENT_TAG=$(node -p "require('./apps/[service]/package.json').version")" >> $GITHUB_ENV
echo tag $CURRENT_TAG
- name: Noti Workflow start
uses: 8398a7/action-slack@v3
with:
icon_emoji: ":fire:"
channel: ${{ env.SLACK_CHANNEL }}
status: ${{ job.status }}
author_name: "[Devops] ${{ env.SERVICE_NAME }}-${{ env.ENVIRONMENT }}"
fields: repo,message,commit,author,action
text: "(1 / 2) Action Start - Tag: ${{ env.CURRENT_TAG }}"
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_DEVOPS_NOTI }}
if: always()
# [Require Change] ref ...
- name: Checkout build script repository
uses: actions/checkout@v4
with:
repository: [build repostiroy name]
token: ${{ secrets.ACCESS_TOKEN }}
path: devops-repo
ref: main
- name: Run build script
run: |
$SHELL devops-repo/repository/${{ github.repository }}/${{ env.SERVICE_NAME }}/build-deploy.sh $SERVICE_NAME $ENVIRONMENT $CURRENT_TAG $S3_BUCKET $DOCKERFILE_PATH
- name: Get and upload failed build status
if: ${{ failure() }}
run: |
CODEBUILD_PROJECT_NAME=$(echo $CODEBUILD_BUILD_ID | cut -d":" -f1)
echo "BUILD_FAILED=true" > build-status.txt
aws s3 cp build-status.txt s3://codebuild-${CODEBUILD_PROJECT_NAME}/results/failure_build_${CODEBUILD_BUILD_ID}.txt
## Action Success
- name: Noti Workflow Success
if: success()
uses: 8398a7/action-slack@v3
with:
icon_emoji: ":white_check_mark:"
channel: ${{ env.SLACK_CHANNEL }}
status: ${{ job.status }}
author_name: "[Devops] ${{ env.SERVICE_NAME }}-${{ env.ENVIRONMENT }}"
fields: repo,message,commit,author,action
text: "(2 / 2) ✅ *Action* - Tag: ${{ env.CURRENT_TAG }}"
env:
SLACK_WEBHOOK_URL: ${{ secrets.SLACK_DEVOPS_NOTI }}
현재는? 잘 돌아가나...
아직까진 문제는 없었다.
기본적으로 Serverless 형태로 돌아가 문제로 삼았던 부분은 해결되었다.
BootStrapping 시간도 1~2분 대에 끝나기 때문에 Build 시간이 문제가 되진 않았다.
캐시같은 경우에는 Local Cache가 아닌, --cache-from 로 해결하였다.
'Architecture > 회고 및 경험' 카테고리의 다른 글
GPU 인스턴스 구성을 구성해보자 - 2 (0) | 2025.03.29 |
---|---|
GPU 인스턴스 구성을 구성해보자 - 1 (0) | 2025.03.24 |
Platform Engineer의 대한 나의 생각 (0) | 2025.02.16 |
Database Dump 방법 (0) | 2025.01.12 |
Devops Engineer가 Database 관리하면서 느낀점... (0) | 2024.12.26 |