Terraform과 AWS CodeSeries 기반의 기본적인 CI/CDelivery 파이프라인 구축하기 (2/4)

jb·2023년 10월 9일
0

이 글은 총 4개의 파트로 나뉘며 각 파트별 다루는 내용은 위와 같습니다.


소스 작성

로컬 PC에서 앞서 생성한 CodeCommit git 저장소를 clone하고 아래처럼 워크로드 샘플 코드, k8s 매니페스트들, CI 작업 명세서, CD 작업 명세서를 작성하자.

git clone

git clone <생성한 codecoomit의 https url>

fastapi 웹앱 작성

먼저, 간단한 fastapi 웹앱 코드를 작성하자.

app/main.py

from fastapi import FastAPI

app = FastAPI()

@app.get('/')
async def root():
    return {"message": "CI/CD Pipeline using Terraform & AWS CodeSeries"}
  • 테스트용으로 GET / 요청 시에 간단한 메시지를 출력하는 fastapi 서버.

작성한 웹앱에 필요한 파이썬 라이브러리 명세서를 작성하자.

env/build/dev/requirements.txt

fastapi
uvicorn
  • fastapi 관련 기본 라이브러리만 설치하면 됨.

💡 여기서 짚고 넘어가야할 사항으로 폴더 구조에 대해 이야기 해보자.

빌드와 관련된 폴더는 env/build 밑에 있고 딜리버리와 관련된 폴더는 env/delivery 밑에 있음.

그리고 그 안에서 또 한 번 배포 환경별로 폴더를 나눔. (dev, stg, prd)

이러한 구조는 한 번 시도해본 것으로써 복잡하다고 생각된다면 적절히 단순하게 만들면 됨.

이제 다시 본론으로 돌아가자.

Dockerfile 작성

CI에서 해당 웹앱에 대해 컨테이너 이미지로 빌드할 작업을 Dockerfile로 작성하자.

env/build/dev/Dockerfile

FROM python:3.11

WORKDIR /app

COPY env/build/dev/requirements.txt /app
RUN pip install -q -U --no-cache-dir -r requirements.txt

COPY app /app

EXPOSE 80

CMD ["/bin/bash", "-c", "uvicorn main:app --host 0.0.0.0 --port 80 --root-path /"]
  • python v3.11을 base image로 사용.
  • 워킹디렉토리를 fastapi 소스코드가 있는 /app으로 이동.
  • requirements.txt에 명시된 파이썬 라이브러리를 설치.
  • app 폴더에 있는 fastapi 소스코드를 복사.
  • 웹 서버로 열 80포트를 오픈.
  • uvicorn 명령어로 fastapi 서버를 실행.

k8s manifests 작성

앞서 작성한 Dockerfile에 의해 빌드된 컨테이너 이미지를 기반으로 k8s에 올릴 워크로드에 대한 매니페스트를 작성하자. 여기서는 간단한 Deployment와 Service만 정의하여 사용함.

/env/delivery/dev/manifests/deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: sample-pipeline-dev
  name: sample-pipeline-dev
  namespace: sample-pipeline-dev
spec:
  replicas: 1
  selector:
    matchLabels:
      app: sample-pipeline-dev
      version: v0.1
  template:
    metadata:
      labels:
        app: sample-pipeline-dev
        version: v0.1
    spec:
      containers:
      - image: <ecr registry url>:<update_here>
        imagePullPolicy: Always
        name: sample-pipeline-dev
        ports:
        - containerPort: 80
          protocol: TCP
  • 앞서 빌드한 웹앱을 단순하게 deployment로 배포하는 yaml.
  • 여기서 image tag명을 <update_here>로 했는데, 그 이유는 CD 단계에서 지정한 이미지 tag명으로 수정하여 배포하도록 하기 위함. 이게 별로라면 각자 취향에 맞게 수정하여 사용하자.
  • 그리고 <ecr registry url> 은 각자 사용하는 이미지 레지스트리의 url을 입력해주자.

/env/delivery/dev/manifests/service.yaml

apiVersion: v1
kind: Service
metadata:
  name: sample-pipeline-dev
  namespace: sample-pipeline-dev
spec:
  selector:
    app: sample-pipeline-dev
  ports:
    - protocol: TCP
      port: 80
      targetPort: 80
  type: ClusterIP
  • 간단한 Service 명세.

CI 명세서 작성

이제, CI 작업 명세서를 작성하자. 내용은 앞서 작성한 웹앱을 빌드 명세서에 따라 빌드를 수행하고 ECR에 푸시하는 것.

env/build/dev/buildspec.yml

version: 0.2
env:
  variables:
    APP_ENV: "dev"
    AWS_REGION: "<ECR을 위한 내 리전명>"
    AWS_ACCOUNT_ID: "<ECR을 위한 내 aws account id>"
phases:
  pre_build:
    commands:
      - echo "export some envs and printenv"
      - GIT_TAG=$(git describe --tags --abbrev=0)
      - COMMIT_SHA_SIX=$(git rev-parse $GIT_TAG | cut -c 1-6)
      - ECR_REPO_NAME="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/sample-pipeline"
      - IMAGE_TAG="fastapiv0.100.0-py3.11-$GIT_TAG-$COMMIT_SHA_SIX"
      - printenv | sort
      - echo "log in to Amazon ECR"
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
  build:
    commands:
      - echo "add testing jobs here"  # pytest, pylint, unittest, etc
      - echo "build the docker image"
      - docker build . -f env/build/$APP_ENV/Dockerfile -t $ECR_REPO_NAME:$IMAGE_TAG
  post_build:
    commands:
      - docker push $ECR_REPO_NAME:$IMAGE_TAG
      - echo "completed this action"
  • env
    • variables
      • 기본 환경변수 선언 : 배포 환경, ecr 관련 aws 리전과 aws account id.
  • phases
    • pre_build
      • 추가적인 환경변수 선언 :
        • 최신 git tag명
        • 해당 git tag의 hash값 앞에 6자리
        • 이미지 registry명. 여기서 aws account id 변수와 aws region 변수를 사용함.
        • 이미지 tag명. 여기서 이미지 tag명은 git tag와 hash값 앞에 6자리를 suffix로 활용함. 이것도 각자 취향에 맞게 수정 가능함.
        • 이렇게 추가적인 환경변수를 선언하는 이유는 앞서 env.variables의 경우, 값을 할당할 때, 다른 변수나 bash 명령어를 사용할 수 없기 때문.
      • 최종 환경변수를 출력.
      • ECR에 로그인.
    • build
      • 코드에 대한 테스트를 먼저 수행. 여기서는 생략. pytest, pylint, unittest 등을 수행하면 될 것으로 보임.
      • 도커 이미지 빌드.
    • post_build
      • 빌드 작업 완료 후 수행할 것. 예를 들어, 작업이 완료됐으므로 완료 상태를 메신저나 메일에 푸시하는 등의 기능을 추가할 수 있음. 여기서는 빌드한 이미지를 ECR에 push하기만 하자.

CD 명세서 작성

다음으로 CDelivery 명세서를 작성하자. 내용은 kubectl을 설치하고 kubeconfig를 업데이트한 뒤 최신 git tag와 해시값 6자리를 가져와 앞서 빌드한 태그명을 변수로 만들고 deployment 매니페스트에 이미지 tag를 이 변수로 수정한 뒤 배포하는 것.

env/delivery/dev/buildspec.yml

version: 0.2
env:
  variables:
    APP_ENV: "dev"
    AWS_REGION: "<ECR을 위한 내 리전명>"
    AWS_ACCOUNT_ID: "<ECR을 위한 내 aws account id>"
    EKS_CLUSTER_NAME: "<워크로드를 배포할 eks 클러스터 이름>"
phases:
  install:
    commands:
      - echo "install kubectl and set config"
      - curl -o kubectl https://amazon-eks.s3.us-west-2.amazonaws.com/1.19.6/2021-01-05/bin/linux/amd64/kubectl
      - chmod +x ./kubectl
      - mv ./kubectl /usr/local/bin/kubectl
      - mkdir ~/.kube
      - aws eks --region $AWS_REGION update-kubeconfig --name $EKS_CLUSTER_NAME
  pre_build:
    commands:
      - echo "export some envs and printenv"
      - GIT_TAG=$(git describe --tags --abbrev=0)
      - COMMIT_SHA_SIX=$(git rev-parse $GIT_TAG | cut -c 1-6)
      - ECR_REPO_NAME="$AWS_ACCOUNT_ID.dkr.ecr.$AWS_REGION.amazonaws.com/sample-pipeline"
      - IMAGE_TAG="fastapiv0.100.0-py3.11-$GIT_TAG-$COMMIT_SHA_SIX"
      - EKS_NAMESPACE="sample-pipeline-$APP_ENV"
      - MANIFEST_PATH="env/delivery/$APP_ENV/manifests"
      - printenv | sort
      - echo "check namespace exists and create if not"
      - kubectl get ns | grep -q "$EKS_NAMESPACE" || kubectl create ns $EKS_NAMESPACE
      - echo "log in to Amazon ECR"
      - aws ecr get-login-password --region $AWS_DEFAULT_REGION | docker login --username AWS --password-stdin $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com
  build:
    commands:
      - echo "change image tag in deployment.yaml"
      - sed -i "s/<update_here>/$IMAGE_TAG/" "$MANIFEST_PATH/deployment.yaml"
      - echo "apply manifests"
      - kubectl apply -f $MANIFEST_PATH/.
  post_build:
    commands:
      - echo "completed this action"
  • env
    • variables
      • 기본 환경변수 선언 : 배포 환경, ecr 관련 aws 리전과 aws account id. eks cluster 이름.
  • phases
    • install
      • kubectl 설치.
      • aws 명령어로 해당 eks 클러스터에 대해 자격증명을 kubeconfig에 불러오기.
    • pre_build
      • 추가적인 환경변수 선언 :
        • 최신 git tag명
        • 해당 git tag의 hash값 앞에 6자리
        • ECR registry명. 여기서 aws account id 변수와 aws region 변수를 사용함. (사실상 위 예제에서는 미사용)
        • 이미지 tag명. 여기서 이미지 tag명은 git tag와 hash값 앞에 6자리를 suffix로 활용함. 이것도 각자 취향에 맞게 수정 가능함.
        • 배포할 eks의 namespace명. 여기서 앞서 정의한 배포 환경에 대한 변수를 suffix로 활용함.
        • 배포할 매니페스트 경로. 여기서도 배포 환경에 대한 변수를 경로 중간에 활용함.
        • 이렇게 추가적인 환경변수를 선언하는 이유는 앞서 env.variables의 경우, 값을 할당할 때, 다른 변수를 사용할 수 없기 때문.
      • 최종 환경변수를 출력.
      • 워크로드가 배포될 namespace가 있는지 확인하고 없다면 생성.
      • ECR에 로그인.
    • build
      • 배포할 일부 매니페스트 수정. 예를 들어, 이미지 tag명에 최신 git tag명을 적용. 구체적으로 매니페스트에는 이미지 태그명이 <update here>로 되어 있음. 이 부분을 앞서 정의한 이미지 태그명으로 수정하는 것.
      • 매니페스트들을 적용.
    • post_build
      • delivery 작업 완료 후 수행할 것. 여기서는 생략.

작성한 소스들 확인

지금까지 작성한 파일들에 대한 목록을 보자.

$ tree .                                                                                  
.
├── app
│   └── main.py
└── env
    ├── build
    │   └── dev
    │       ├── Dockerfile
    │       ├── buildspec.yml
    │       └── requirements.txt
    └── delivery
        └── dev
            ├── buildspec.yml
            └── manifests
                ├── deployment.yaml
                └── service.yaml

8 directories, 8 files

앞서 말했듯이 나는 배포 환경별로 git tag를 push할 것임. 예를 들어, dev-0.1, stg-0.1, prd-0.1 이런 식임. 그러므로 실제로는 env/buildenv/delivery 밑에 dev 폴더 뿐만 아니라 stg, prd 폴더도 생성해서 사용하려 함.

작성한 소스들 push

master 브랜치 생성

### 워킹디렉토리 확인 : git 저장소 루트 경로
$ pwd
sample-pipeline

### master 브랜치 생성
$ git checkout -b master
  git add .
  git commit -am "add source code"
  git push --set-upstream origin master

### 확인
$ git log

이제 소스는 모두 준비 됐음. AWS 리소스만 배포되어 있다면 git tag를 push 했을 때, CI/CD 파이프라인을 트리거할 수 있음. 다음으로 CI/CD 관련 aws 리소스들을 terraform으로 배포해보자.

profile
기록하는 엔지니어 되기 💪

0개의 댓글