#02. AWS Fargate에 앱을 배포하기 위한 CI/CD 파이프라인 구축(Codecommit)

Cloud9 IDE 준비

  • AWS Cloud9 (브라우저 내에서 코드를 작성, 실행 및 디버그하는 데 사용할 수 있는 클라우드 기반 통합 개발 환경(IDE))
  • 애플리케이션 개발에 자주 사용되는 여러 도구들은 사전 패키징되어 개발자들이 편리하게 사용할 수 있도록 지원됩니다.

Cloud9 IDE 리뷰(검토)

Why Cloud9?

AWS Cloud9은 클라우드 기반 통합 개발 환경(IDE)으로, 브라우저 내에서 코드 작성, 실행 및 디버그를 할 수 있습니다. 이 서비스는 Amazon Web Services(AWS)의 일부로 제공되며, 개발자들이 인터넷에 연결된 장치에서도 원활하게 소프트웨어 개발을 할 수 있도록 지원합니다.

AWS Cloud9은 프로그래밍 언어에 상관없이 여러 가지 언어를 지원하며, 다양한 기능을 제공합니다. 사용자는 웹 브라우저를 통해 IDE에 접속하여 코드를 작성하고 저장할 수 있습니다. 또한, 다른 개발자와 쉽게 협업하고, 코드 리뷰를 할 수 있는 기능도 제공합니다.

이 클라우드 기반 개발 환경은 서버리스 애플리케이션, 웹 애플리케이션, 마이크로서비스, 모바일 및 기타 다양한 유형의 애플리케이션을 개발하는 데 사용됩니다. AWS의 다른 서비스들과 통합이 잘 되어 있어서 개발 프로세스를 효율적으로 관리할 수 있습니다.

AWS Cloud9은 편리한 웹 기반 개발 환경을 제공하여 개발자들의 생산성을 향상시키고, 복잡한 개발 환경 설정을 줄여줍니다. 또한, 클라우드 기반으로 제공되기 때문에 개발자들은 자신의 로컬 컴퓨터에 소프트웨어를 설치할 필요 없이 인터넷에 연결된 어떤 장치에서든 접속하여 개발 작업을 수행할 수 있습니다.

environment 디렉토리 내 구성 파일 리스트

  • Dockerfile: 애플리케이션 Docker 이미지를 생성하는 데 사용되는 명령을 나열하는 파일
  • index.js: 애플리케이션 코드를 렌더링하고 라우팅을 처리하는 파일
  • package.json: 애플리케이션 빌드 단계에서 Node에 필요한 메타데이터, 종속성 및 스크립트를 나열하는 파일
  • routes: API에 요청하고 AWS 발표 목록을 가져오는 JavaScript 파일이 포함된 디렉터리
  • static: 콘텐츠를 구성하고 화면에 표시하는 방법을 정의하는 다양한 파일이 포함된 정적 디렉터리

Dockerfile

: Dockerfile은 아래와 같이 Node.js 애플리케이션을 빌드하고 컨테이너 포트 TCP 80을 노출합니다.

$ cat Dockerfile
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: MIT-0
FROM public.ecr.aws/d5z8k9g9/node18-alpine3.15:latest
# Create app directory
WORKDIR /usr/src/app
# App dependencies
COPY package.json ./
# Download the dependencies listed in the package.json file and create the node_modules directory
RUN npm install
# Copy the application source code
COPY index.js .
COPY routes ./routes
COPY static ./static
# Listen on port 80
EXPOSE 80
# Start the application
CMD [ "node", "index.js" ]

Amazon ECS에서 배포 자동화를 위한 추가 파일 생성

생성 파일 총 3개
buildspec.yaml, appspec.yaml, taskdef.json

생성 순서 (차례대로)

  1. Docker 이미지 빌드를 위해 CodeBuild는 buildspec 파일의 명령과 파라미터를 사용합니다. (buildspec.yaml)
  2. CodeDeploy는 appspec 파일을 활용하여 태스크 정의를 선택합니다. (appspec.yaml)
  3. 현재 Amazon ECS 서비스에서 실행 중인 세 가지 태스크는 모두 동일한 태스크 정의를 참조합니다. 업데이트된 애플리케이션 소스 코드를 기반으로 새 컨테이너를 빌드하고 이를 가리키는 두 번째 태스크 정의가 필요합니다. 이러한 새로운 태스크 정의를 생성하기 위해 taskdef.json 파일을 사용합니다.

buildspec.yaml 생성

cat << 'EOF' > ~/environment/buildspec.yaml
# buildspec.yaml
version: 0.2
phases:
  pre_build:
    commands:
      - echo Logging in to Amazon ECR...
      - aws --version
      - ACCOUNT_ID=$(echo $CODEBUILD_BUILD_ARN | cut -f5 -d ':') && echo "The Account ID is $ACCOUNT_ID"
      - echo "The AWS Region is $AWS_DEFAULT_REGION"
      - REPOSITORY_URI=$ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$ACCOUNT_ID-application
      - echo "The Repository URI is $REPOSITORY_URI"
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $REPOSITORY_URI
      - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7)
      - IMAGE_TAG=$COMMIT_HASH
  build:
    on-failure: ABORT
    commands:
      - echo Build started on `date`
      - echo Building the Docker image...
      - docker build -t $REPOSITORY_URI:$IMAGE_TAG .
      - docker tag $REPOSITORY_URI:$IMAGE_TAG $REPOSITORY_URI:$IMAGE_TAG
  post_build:
    commands:
      - echo Build completed on `date`
      - echo Pushing the Docker image...
      - docker push $REPOSITORY_URI:$IMAGE_TAG
      - echo Writing image definitions file...
      - printf '[{"name":”myimage","imageUri":"%s"}]' $REPOSITORY_URI:$IMAGE_TAG > imagedefinitions.json
      - printf '{"ImageURI":"%s"}' $REPOSITORY_URI:$IMAGE_TAG > imageDetail.json
artifacts:
    files: 
      - imagedefinitions.json
      - imageDetail.json
      - appspec.yaml
      - taskdef.json
EOF
  • buildspec 파일의 명령은 install, pre_build, build 및 post_build 단계로 구성됩니다.
  • 파일 하단의 아티팩트 섹션은 빌드 출력의 위치 및 구성을 가리킵니다. 관리자는 buildspec 파일에 모든 단계를 포함할 필요가 없습니다. 그러나 단계 이름은 변경하면 안 됩니다.
  • 버전은 0.2로 설정되어 있습니다.
  • pre_build 단계는 애플리케이션 코드를 빌드하기 전에 명령을 실행하는 데 사용되는 선택적 단계입니다. 이 예에서 pre_build 단계는 빌드 프로세스 전체에서 사용되는 변수를 설정하고 Amazon ECR에 인증하는 데 사용됩니다. $AWS_DEFAULT_REGION와 같이 파일이 미리 구성된 CodeBuild 환경 변수를 참조하는 경우가 있습니다. 다른 경우에는 셸 명령을 사용하여 변수를 설정합니다.
  • 새로 빌드된 각 이미지에는 CodeCommit의 해당 commit ID 태그가 지정됩니다.
  • 빌드 단계에 포함된 명령은 순차적으로 실행됩니다. 이 경우 ABORT 명령은 어떤 명령이 실패할 경우 빌드를 종료하기 위해 포함되었습니다.
  • post_build 단계는 빌드 단계에서 생성된 Docker 이미지를 Amazon ECR로 푸시합니다.
  • 완료되면 빌드는 imageDetail.json 및 imagedefinitions.json이라는 아티팩트를 생성하며 둘 다 환경 루트 디렉터리에 저장됩니다. 이러한 파일은 파이프라인의 배포 단계에서 사용되며 Amazon ECS에 배포할 이미지를 나타냅니다.
  • 아티팩트 섹션은 CodeCommit 리포지토리에 업로드된 appspec.yaml 및 taskdef.json 파일이 빌드 출력으로 포함되도록 지정합니다. 이러한 파일이 없으면 배포에 실패합니다.

appspec.yaml 생성

cat << EOF > ~/environment/appspec.yaml
# appspec.yaml
version: 0.0
Resources:
  - TargetService:
      Type: AWS::ECS::Service
      Properties:
        TaskDefinition: <TASK_DEFINITION>
        LoadBalancerInfo:
          ContainerName: "application"
          ContainerPort: 80
EOF
  •  이 파일은 특정 작업 정의를 가리키지 않습니다. 대신 TaskDefinition 레이블이 자리 표시자로 채워집니다. CodeBuild는 빌드 시 이를 새 태스크 정의로 바꿉니다. Amazon ECS 배포 중 수명 주기 이벤트에 해당하는 AWS Lambda 함수와 같은 추가 정보를 appspec 파일에 선택적으로 포함할 수 있습니다.

taskdef.json 파일을 생성 전 환경 변수 설정

AWS_REGION=$(curl -s http://169.254.169.254/latest/dynamic/instance-identity/document | jq -r .region) 
FAMILY=$(aws ecs list-task-definition-families --status ACTIVE --output text | awk '{print $NF}') 
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text) 
printf "You are using $AWS_REGION region\nYour task definition family is $FAMILY\nYour account ID is $ACCOUNT_ID\n"

taskdef.json 파일 생성

cat << EOF > ~/environment/taskdef.json
# taskdef.json
{
    "containerDefinitions": [
        {
            "name": "application",
            "image": "<IMAGE_NAME>",
            "portMappings": [
                {
                    "containerPort": 80,
                    "hostPort": 80,
                    "protocol": "tcp"
                }
            ],
            "essential": true,
            "logConfiguration": {
                "logDriver": "awslogs",
                "options": {
                    "awslogs-group": "cicd-logs",
                    "awslogs-region": "$AWS_REGION",
                    "awslogs-stream-prefix": "ecs"
                },
            },
        }
    ],
    "family": "$FAMILY",
    "taskRoleArn": "arn:aws:iam::$ACCOUNT_ID:role/ecsTaskExecutionRole",
    "executionRoleArn": "arn:aws:iam::$ACCOUNT_ID:role/ecsTaskExecutionRole",
    "networkMode": "awsvpc",
    "status": "ACTIVE",
    "compatibilities": [
        "EC2",
        "FARGATE"
    ],
    "requiresCompatibilities": [
        "FARGATE"
    ],
    "cpu": "256",
    "memory": "512",
    "tags": [
        {
            "key": "Name",
            "value": "GreenTaskDefinition"
        }
    ]
}
EOF
  • 이 태스크 정의는 현재 클러스터에서 실행 중인 작업 정의와 동일하지만 한 가지 중요한 차이점은 이미지 레이블이 “<IMAGE_NAME>”을 자리 표시자로 사용한다는 것입니다. 파이프라인이 실행되면 CodeDeploy는 배포 시 올바른 이미지 URI로 이 값을 업데이트합니다. 또한 파일은 포함되어야 하는 Amazon CloudWatch log group을 참조합니다. 그렇지 않으면 태스크가 실패합니다.

👩NOTE
애플리케이션을 열었을 때 웹 사이트 배경이 파란색이었다는 것을 기억하십시오. 동료들로부터 웹 사이트가 너무 어둡다는 피드백을 받았고 배경을 변경해 달라는 요청을 받았습니다. 이제 애플리케이션 코드를 수정하여 배경을 녹색으로 만듭니다. 후속 태스크에서는 그린 환경에서 이 수정된 애플리케이션 코드를 사용하는 블루/그린 배포를 빌드합니다.

AS-IS는 아래와 같습니다. 이부분의 배경을 Blue/Green Deploy 로 Build 할 예정입니다.

아래와 같이 변경을 app.css의 수정을 진행합니다.

sed -i 's/282F3D/1D8102/g' ~/environment/static/css/app.css
  • 이제 애플리케이션 코드를 업데이트했고, 파이프라인을 정의하는 파일을 생성했음
  • 새로운 CodeCommit 리포지토리를 생성하고 여기에 모든 파일을 푸시하겠습니다. 파이프라인은 소스 제어를 위해 이 리포지토리를 사용

새로운 Codecommit Repository를 생성하고, SSH 연결 URL을 환경 변수에 저장하기

: CodeCommit 서비스를 이용하여 새로운 리포지토리를 생성하는 Bash shell 스크립트를 실행합니다.

export SRC_REPO_URL=$( \
    aws codecommit create-repository \
        --repository-name pipeline-source-code \
        --repository-description "Repository for AWS News application source code" \
        | jq -r '.[].cloneUrlSsh'
)
echo "Repo successfully created. Use $SRC_REPO_URL to clone the repository"
  1. aws codecommit create-repository 명령어를 사용하여 “pipeline-source-code”라는 이름의 새로운 CodeCommit 리포지토리를 생성합니다.
  2. 이 리포지토리의 Description은 “Repository for AWS News application source code”로 설정됩니다.
  3. 이 명령의 출력은 JSON 형식으로 반환됩니다. jq -r ‘.[].cloneUrlSsh’ 부분은 이 JSON 출력에서 cloneUrlSsh 필드의 값을 추출합니다. 이 값은 리포지토리를 SSH를 통해 복제하는 데 사용되는 URL입니다.
    (이 URL은 SRC_REPO_URL라는 환경 변수에 저장됩니다. 이는 export SRC_REPO_URL=$(…) 부분에서 이루어집니다.)
  4. “Repo successfully created. Use $SRC_REPO_URL to clone the repository”라는 메시지를 출력합니다. 이 메시지는 리포지토리가 성공적으로 생성되었음을 알려줍니다.
  5. 사용자가 SRC_REPO_URL 환경 변수에 저장된 URL을 사용하여 리포지토리를 복제할 수 있음을 알립니다.

  • 위와 같이 Codecommit Repository 가 생성되었습니다.
  • Name: pipeline-source-code

아래의 명령어로 git 구성을 업데이트합니다.

git config --global init.defaultBranch main

~/environment 디렉터리를 로컬 Git 리포지토리로 초기화합니다.

cd ~/environment
git init

애플리케이션 파일을 준비하고 업데이트된 파일을 로컬 리포지토리에 커밋하고 애플리케이션 코드를 AWS CodeCommit 리포지토리의 기본 분기로 푸시하려면 다음 명령을 입력합니다. git-codecommit.[AWS_REGION].amazonaws.com에 연결할 것인지 묻는 메시지가 표시되면 yes를 입력합니다.

git add .
git commit -m "initial commit"
git push --set-upstream $SRC_REPO_URL main
  • CodeCommit 리포지토리를 성공적으로 생성하고 여기에 애플리케이션 코드를 업로드했습니다. 다음 태스크에서는 이 리포지토리를 CI/CD 파이프라인의 소스로 사용합니다.

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x