Overview
해당 문서는 Bitbucket에서 관리하는 프로젝트를 Github로 이관하며 직면하는 배포 파이프라인 이전에 대한 해결 방법을 다루고 있습니다. 기존 Bitbucket 프로젝트 루트 경로의 bitbucket-pipelines.yml 파일의 내용이 Github Actions에서 요구되는 형태로 변환되고, ./github/workflows/github-actions.yml 로 이동되었으며, 그 외의 설정 파일(task-definition.json , task-definition-dev.json , Dockerfile)은 내용과 위치가 동일합니다.
CI/CD Scenario
- Github의 master 또는 develop 브랜치에 코드가 push 됨
- 해당 프로젝트의 최신 상황에 대한 도커 이미지 빌드
- AWS ECR(Elastic Container Registry)에 이미지 push
- AWS ECS(Elastic Container Service)에 어플리케이션 배포
Github Actions, Bitbucket Pipelines의 차이점
설정 파일의 위치
- bitbucket-pipelines.yml 파일은 프로젝트의 루트 경로에 위치
- github-actions.yml 파일은 project/.github/workflows/ 경로에 위치
- → 해당 위치에 파일이 존재해야 Github Actions의 이벤트를 감지할 수 있음
self-hosted runner 및 docker DinD 적용
- Bitbucket Pipelines 에서는 Docker-in-Docker 서비스 적용을 위해 self-hosted runner를 생성한다.
- Github Actions 에서는 Github에서 제공하는 ubuntu runner를 사용하여 현재 설정이 되어 있다.
- → 추후 배포 속도 등을 고려하여 self-hosted runner 사용 예정
AWS ECR 로그인 방식부분에서 AWS 환경 변수를 읽는 방식
- Bitbucket Pipelines 에서는 ECR 로그인을 위해 AWS 환경변수의 값을 Bitbucket Repository에서 읽어오는 것으로 추측됨
- Github Actions 에서는 파일 내부에 직접 ECR AWS 환경변수를 명시함(aws-access-key-id, aws-secret-access-key, aws-region)
bitbucket-pipelines.yml 분석
공식 문서 링크
definitions:
services:
docker:
memory: 3072
docker-custom:
type: docker
image: docker:dind
memory: 3072
push-image: &push-image
... 이하 생략...
deploy-to-ecs: &deploy-to-ecs
... 이하 생략...
설명
definitions
는 Bitbucket Pipelines에서 재사용 가능한 파이프라인 구성을 정의하는 데 사용되는 섹션입니다. 즉, 여러 파이프라인에서 사용할 수 있는 구성을 한 번 정의하고 해당 파이프라인에서 이를 참조하여 사용할 수 있습니다. 이를 통해 코드 중복을 줄이고 파이프라인 구성을 단순화할 수 있습니다.
push-image
와 deploy-to-ecs
는 실행 환경의 이름을 나타냅니다. `&push-image와
&deploy-to-ecs는 YAML에서 앵커(anchor)라는 기능을 사용하여
push-image` 블록을 참조할 수 있도록 이름을 지정하는 것입니다.
따라서 &push-image
는 push-image
블록을 나타내는 앵커이며,
step 단계에서 step: *push-image
는 push-image
앵커를 참조하여 해당 블록을 실행하는 것입니다. 이를 통해 push-image
블록을 반복해서 사용하고 코드의 중복을 줄일 수 있습니다.
pipelines:
Bitbucket Pipelines는 YAML 파일을 읽어들일 때 이 섹션을 찾습니다.
branches:
특정 브랜치에 대한 파이프라인을 정의할 수 있습니다. 이를 통해 코드가 해당 브랜치로 푸시되면 해당 브랜치에 대한 빌드 및 배포 프로세스가 실행됩니다.
저희는 master 브랜치와 develop 브랜치를 구분해서 사용합니다.
develop:
develop 브랜치가 푸시될 때 실행할 파이프라인을 정의하는 섹션입니다
- step:
파이프라인에서 실행되는 개별 단계를 정의하는 데 사용되는 키워드입니다.
step
키워드는 YAML 리스트 구조로 작성되며, 각 항목은 파이프라인의 단계를 나타내는 맵으로 구성됩니다. 이 맵은 단계의 이름, 실행될 스크립트 또는 명령어, 환경 변수, 이미지 등과 같은 속성을 포함할 수 있습니다.
# example
pipelines:
default:
- step:
name: Build
script:
- npm install
- npm run build
- step:
name: Test
script:
- npm run test
name: Build and Push Docker Image
파이프라인에서 단계를 식별하고 이해하기 쉬운 설명을 제공하기 위해 사용됩니다.
image: atlassian/pipelines-awscli
파이프라인 단계를 실행하는 데 사용할 도커 이미지를 지정하는 필드입니다. 도커 이미지는 단계의 실행 환경을 정의하며, 필요한 종속성이나 실행에 필요한 소프트웨어 등을 포함할 수 있습니다.
atlassian/pipelines-awscli
는 AWS CLI (Command Line Interface)가 미리 설치되어 있는 Docker 이미지입니다. 이 이미지를 사용하면 Bitbucket Pipelines에서 AWS CLI 명령어를 사용하여 Amazon Web Services (AWS)와 상호 작용할 수 있습니다. 예를 들어, AWS Elastic Container Service (ECS)에 배포하는 경우, atlassian/pipelines-awscli
이미지를 사용하여 Amazon ECS CLI를 실행하고, ECS 서비스 및 작업 정의를 배포할 수 있습니다.
runs-on: 'self.hosted'
runs-on
필드는 특정 러너(runner)를 지정하여 작업이 실행될 컴퓨팅 환경을 정의합니다. 러너는 파이프라인 단계를 실행하는 데 사용되는 가상 머신 또는 컨테이너를 나타냅니다.
pipelines:
default:
- step:
name: Build
runs-on: ubuntu-latest
script:
- echo "Build step"
위의 코드에서는 runs-on
필드를 사용하여 Build
단계가 ubuntu-latest
러너에서 실행되도록 지정하고 있습니다.
실제로는 'self.hosted'
라는 커스텀 러너를 사용하고 있습니다. 이와 관련된 정보는 @익명 님이 알고 계십니다.
bitbucket pipelines self hosted 생성 방법
services:
- docker-custom
services
는 bitbucket-pipelines.yml
파일에서 Bitbucket Pipelines에서 실행되는 컨테이너 서비스를 지정하는 데 사용되는 필드입니다.
서비스 컨테이너는 주요 작업 컨테이너 외부에서 실행되는 독립된 컨테이너입니다. 이를 통해 파이프라인 단계에서 작업을 수행하는 동안에도 추가적인 서비스를 활용할 수 있습니다.
docker-custom
코드는 definitions:
에 정의된 사용자 정의 Docker 컨테이너 서비스(dind)를 가리키는 것입니다. dind를 사용하기 위해서는 runs-on
에 self.hosted
레이블을 포함해야 하고 Dockerfile이 프로젝트 루트 경로에 존재해야 합니다.
자세한 내용:
- 사용자 지정 Docker-in-Docker 서비스 추가는 bitbucket-pipelines.yml에서 실행기 구성 - 사용자 지정 docker-in-docker 이미지를 참조하세요.
- Docker-in-Docker 서비스 사용은 Bitbucket Pipelines에서 Docker 명령 실행을 참조하세요.
script:
- export TARGET_ENV=development
- export BUILD_ID=$BITBUCKET_BRANCH_$BITBUCKET_COMMIT_$BITBUCKET_BUILD_NUMBER
- export DOCKER_URI="${DOCKER_IMAGE_URL}"
# Login to docker registry on AWS
- eval $(aws ecr get-login --no-include-email)
# Build image
- export DOCKER_BUILDKIT=1
- docker build -t $DOCKER_URI --build-arg BUILDKIT_INLINE_CACHE=1 --build-arg TARGET_ENV=$TARGET_ENV --cache-from $DOCKER_URI:dev .
- docker tag $DOCKER_URI:latest $DOCKER_URI:dev
# Push image to private registry
- docker push $DOCKER_URI
- docker push $DOCKER_URI:dev
script
는 각 단계(step
)에서 실행할 명령어를 정의하는 필드입니다. 이 필드 안에 적힌 명령어는 쉘 스크립트 형태로 작성됩니다. 이 명령어들은 해당 단계의 실행 컨텍스트 안에서 실행되며, 하나의 step
에 대해 하나 이상의 명령어를 포함시킬 수 있습니다.
export
는 Bash shell에서 사용되는 명령어 중 하나로, 새로운 환경 변수(variable)를 선언하거나 값을 할당합니다.
eval
은 문자열을 명령문으로 인식하고 실행하는 명령어입니다.
여기서 사용된 aws ecr get-login
명령어는 Docker 레지스트리에서 이미지를 push하거나 pull 하기 위해 사용되는 Docker CLI를 Amazon ECR와 연동하기 위해 필요한 인증 토큰을 가져오는 명령어입니다.
왼쪽의 스크립트는 Docker 이미지를 빌드하고 ECR (Elastic Container Registry)에 푸시하는 작업을 수행합니다. 스크립트는 다음 단계로 구성됩니다.
export
명령어를 사용하여TARGET_ENV
및BUILD_ID
,DOCKER_URI
환경 변수를 설정합니다. 이 변수들은 Docker 빌드 및 ECR 푸시를 위해 사용됩니다.BUILD_ID
는BITBUCKET_BRANCH
,BITBUCKET_COMMIT
및BITBUCKET_BUILD_NUMBER
를 조합하여 생성합니다.DOCKER_URI
는 Bitbucket 파이프라인의 비밀 변수로 구성되어 있고 Docker 이미지의 URL을 지정합니다.- AWS ECR에 로그인합니다.
aws ecr get-login
명령을 실행하여 Docker 클라이언트가 ECR에 로그인하도록 합니다.
🤔 aws 로그인을 위한 인증 정보는 self.hosted 러너를 생성할 때 처리된 것 같은데 관리자 권한이 없어서 직접 확인은 못했습니다. - Docker 이미지를 빌드합니다. DOCKER_BUILDKIT 환경 변수를 설정하여 BuildKit 캐시를 사용합니다. TARGET_ENV와 함께 Docker 빌드를 수행하고, 빌드 캐시를 DOCKER_URI:dev 이미지로 지정하여 이전 빌드에서 캐시를 사용합니다.
DOCKER_URI:latest
태그를DOCKER_URI:dev
로 복사합니다.- Docker 이미지를 ECR에 푸시합니다. 먼저
DOCKER_URI
이미지를 푸시하고,DOCKER_URI:dev
이미지를 푸시합니다.
script:
- export TARGET_ENV=development
- export DOCKER_BUILDKIT=1
- pipe: atlassian/aws-ecs-deploy:1.6.0
variables:
AWS_ACCESS_KEY_ID: $AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY: $AWS_SECRET_ACCESS_KEY
AWS_DEFAULT_REGION: $AWS_DEFAULT_REGION
CLUSTER_NAME: ${CLUSTER_NAME}-dev
SERVICE_NAME: "${SERVICE_NAME}"
TASK_DEFINITION: 'task-definition-dev.json'
왼쪽의 스크립트는 AWS Elastic Container Service (ECS)에 배포하기 위한 스크립트입니다. 스크립트는 다음과 같은 작업을 수행합니다.
export
명령어를 사용하여TARGET_ENV
와DOCKER_BUILDKIT
환경 변수를 설정합니다.TARGET_ENV
는 배포할 환경을 나타내며, 이 경우에는development
입니다.DOCKER_BUILDKIT
은 Docker 이미지를 빌드하기 위해 Docker BuildKit을 사용하도록 설정합니다.pipe
명령어를 사용하여 Atlassian이 개발한aws-ecs-deploy
파이프라인 액션을 실행합니다. 이 명령은 ECS에 새로운 Docker 이미지를 배포합니다. 이를 위해aws-ecs-deploy
파이프라인 액션은 AWS CLI를 사용하여 AWS ECR 레지스트리에서 Docker 이미지를 가져오고, AWS ECS에서 배포할 서비스 및 작업 정의를 업데이트합니다. 이때,variables
항목을 사용하여AWS_ACCESS_KEY_ID
,AWS_SECRET_ACCESS_KEY
,AWS_DEFAULT_REGION
,CLUSTER_NAME
,SERVICE_NAME
,TASK_DEFINITION
등의 변수를 설정합니다. 이러한 변수는 파이프라인 액션에서 사용되며, AWS 자격 증명 및 배포에 필요한 다른 정보를 지정합니다.- 이러한 작업을 통해 최종적으로 AWS ECS에서 Docker 이미지를 빌드하고, 작업 정의를 업데이트하고, 새로운 이미지로 서비스를 배포합니다
master:
- step: *push-image
- step: *deploy-to-ecs
Bitbucket Pipelines에서 master 브랜치가 push될 때 실행되는 파이프라인 스텝들을 실행합니다.
*push-image 와 *deploy-to-ecs 는 definitions
에 정의되어 있습니다.
github-actions.yml 분석
공식 문서 링크
name: Development Build and Deploy
GitHub 리포지토리의 "Actions" 탭에 표시되는 워크플로의 이름입니다.
on:
push:
branches:
- develop
on
키워드는 이 워크플로우가 어떤 이벤트를 감지하고 실행될지를 정의하는 부분입니다.
왼쪽의 코드는 develop
브랜치에 push될 때만 workflow가 실행된다는 것을 의미합니다. 따라서 develop
브랜치에 코드를 push할 때만 workflow가 실행되고, 다른 브랜치에 push하면 workflow가 실행되지 않습니다.
깃헙 액션 전략은 push 말고도 다양합니다.
자세한 내용:
Workflow syntax for GitHub Actions - GitHub Docs
jobs:
build_and_push_docker_image:
...이하 생략...
build_and_push_docker_image 워크플로우에서 실행되는 모든 작업을 함께 그룹화합니다
runs-on: ubuntu-latest
runs-on
은 GitHub Actions에서 사용되는 job의 실행 환경을 설정하는 키워드입니다.
왼쪽의 코드는 Ubuntu 운영 체제의 가장 최신 버전을 사용하여 job을 실행하도록 지정하는 것입니다.
다른 실행기를 사용하는 구문 예제는 GitHub Actions에 대한 워크플로 구문 에 있습니다.
steps:
- name: Checkout code
uses: actions/checkout@v2
steps
는 워크플로우에서 실행될 단계를 정의하는 섹션입니다. 각 단계는 하나 이상의 작업을 포함할 수 있으며, 각 작업은 GitHub Marketplace에서 가져온 사용자 지정 작업 또는 본인이 만든 스크립트가 될 수 있습니다.
uses
키워드는 해당 step에서 사용할 action을 정의합니다. 왼쪽 코드 에서는 actions/checkout@v2
라는 action을 사용하고 있습니다. 이 action은 현재 실행 중인 workflow의 깃허브 리포지토리를 복제(clone)하고 워크스페이스로 가져옵니다.
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2 # More information on this action can be found below in the 'AWS Credentials' section
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ secrets.AWS_DEFAULT_REGION }}
GitHub Actions에서 AWS 자격 증명을 구성하는 단계입니다. 이를 통해 GitHub Actions가 AWS 리소스와 상호 작용할 수 있습니다.
이 단계는 aws-actions/configure-aws-credentials
액션을 사용합니다. 이 액션은 GitHub Secrets에 저장된 AWS 액세스 키 ID 및 비밀 액세스 키를 사용하여 AWS 자격 증명을 구성합니다.
이 단계를 통해 AWS 리소스와 상호 작용하기 위한 자격 증명이 설정됩니다. 다음 단계에서는 해당 자격 증명을 사용하여 AWS와 상호 작용하는 작업을 수행합니다.
- name: Log in to Amazon ECR
uses: aws-actions/amazon-ecr-login@v1
GitHub Actions에서 사용하는 AWS Actions의 amazon-ecr-login
액션을 사용하여 Amazon ECR에 로그인하는 과정입니다.
해당 액션은 로그인 인증 정보를 생성하고 자격 증명 정보를 암호화하여 GitHub Actions 내에서 사용할 수 있도록 합니다. 이를 통해 다음 단계에서 Docker 이미지를 Amazon ECR에 푸시하거나 Amazon ECS에서 사용할 수 있습니다.
- name: Build and Push Docker Image
run: |
export TARGET_ENV=development
export BUILD_ID=${{ github.ref }}_${{ github.sha }}_${{ github.run_id }}
export DOCKER_URI=${{ secrets.DOCKER_IMAGE_URL }}
export DOCKER_BUILDKIT=1
docker build -t $DOCKER_URI --build-arg BUILDKIT_INLINE_CACHE=1 --build-arg TARGET_ENV=$TARGET_ENV --cache-from $DOCKER_URI:dev .
docker tag $DOCKER_URI:latest $DOCKER_URI:dev
docker push $DOCKER_URI
docker push $DOCKER_URI:dev
Docker 이미지를 빌드하고 Amazon ECR(EC2 Container Registry)에 푸시(push)하는 작업을 수행하는 단계입니다.
export
명령어를 사용하여 다양한 변수를 설정합니다.TARGET_ENV
: 개발환경 또는 운영환경을 구분하기 위한 변수입니다.BUILD_ID
: Docker 이미지에 대한 고유한 빌드 ID를 생성하기 위한 변수입니다.DOCKER_URI
: ECR 저장소의 URL을 포함하는 변수입니다.DOCKER_BUILDKIT
: Docker 빌드 시 BuildKit 빌더를 사용하도록 설정합니다.
docker build
명령어를 사용하여 Docker 이미지를 빌드합니다.-build-arg
옵션을 사용하여 Dockerfile에서 사용할 빌드 인수(argument)를 설정합니다.-cache-from
옵션을 사용하여 캐시된 이미지를 사용합니다.
docker tag
명령어를 사용하여 이미지를 태그합니다.docker push
명령어를 사용하여 빌드된 이미지와 태그된 이미지를 Amazon ECR에 푸시합니다.
- name: Fill in the new image ID in the Amazon ECS task definition
id: task-def
uses: aws-actions/amazon-ecs-render-task-definition@v1
with:
task-definition: ${{ secrets.TASK_DEFINITION }}
container-name: ${{ secrets.SERVICE_NAME }}
image: ${{ secrets.DOCKER_IMAGE_URL }}
AWS ECS task definition의 container 이미지 정보를 업데이트하는 작업을 합니다.
id
는 이후에 사용될 수 있는 Action의 출력을 참조하기 위해 필요한 변수 이름입니다.
여기서 task-def
는 이후 단계에서 출력을 참조할 때 사용될 변수 이름입니다. 이전 단계에서 생성된 Amazon ECS task definition을 대상으로 작업하기 때문에 출력은 이전 단계에서 task-def
로 정의됩니다. 이 변수 이름을 사용하여 이후의 단계에서 이전 단계에서 생성된 Amazon ECS task definition을 참조할 수 있습니다.
aws-actions/amazon-ecs-render-task-definition@v1
액션을 사용하며, 해당 액션을 위해secrets.TASK_DEFINITION
, secrets.SERVICE_NAME
, secrets.DOCKER_IMAGE_URL
등의 GitHub Secrets 값을 사용합니다.
이를 통해 ECS 서비스에 배포할 이미지 정보가 task definition 파일에 적용됩니다.
aws-actions/amazon-ecs-render-task-definition@v1
는 Amazon ECS 작업 정의(Task Definition)을 렌더링(rendering)하고 새 이미지 ID를 새 Amazon ECS 작업 정의에 삽입하는 GitHub Action입니다.
이 작업은 AWS Systems Manager Parameter Store에서 특정 작업 정의(Task Definition)를 가져오고 해당 작업 정의에 대해 docker image
를 업데이트하는 작업을 수행합니다. 이 작업은 새 작업 정의(Task Definition)을 생성하고, 이전 작업 정의와 비교하여 변경 사항이 있을 때만 Amazon ECS 클러스터에서 업데이트합니다.
따라서 이 작업은 새로운 이미지 ID를 Amazon ECS 작업 정의(Task Definition)에 삽입하는데 사용됩니다. 이를 통해 Amazon ECS 작업은 새로운 이미지로 업데이트됩니다.
- name: Deploy Amazon ECS task definition
uses: aws-actions/amazon-ecs-deploy-task-definition@v1
with:
task-definition: ${{ steps.task-def.outputs.task-definition }}
service: ${{ secrets.SERVICE_NAME }}
cluster: ${{ secrets.CLUSTER_NAME }}-dev
wait-for-service-stability: true
AWS ECS에 task definition을 배포하는 작업을 수행하는데, 이를 위해 aws-actions/amazon-ecs-deploy-task-definition@v1
액션을 사용합니다.
액션의 입력으로 다음과 같은 정보가 필요합니다.
task-definition
: 이전 스텝에서 생성된 task definition 정보를 전달합니다.service
: 배포할 서비스의 이름을 전달합니다.cluster
: 배포할 클러스터의 이름을 전달합니다.wait-for-service-stability
: 배포 후 서비스가 안정적인 상태가 될 때까지 대기할지 여부를 전달합니다.
액션이 성공적으로 수행되면, AWS ECS 서비스에 대한 배포 작업이 완료됩니다.