이 글은 총 4개의 파트로 나뉘며 각 파트별 다루는 내용은 위와 같습니다.
로컬 PC에서 앞서 생성한 CodeCommit git 저장소를 clone하고 아래처럼 워크로드 샘플 코드, k8s 매니페스트들, CI 작업 명세서, CD 작업 명세서를 작성하자.
git clone
git clone <생성한 codecoomit의 https url>
먼저, 간단한 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
💡 여기서 짚고 넘어가야할 사항으로 폴더 구조에 대해 이야기 해보자.
빌드와 관련된 폴더는 env/build
밑에 있고 딜리버리와 관련된 폴더는 env/delivery
밑에 있음.
그리고 그 안에서 또 한 번 배포 환경별로 폴더를 나눔. (dev
, stg
, prd
)
이러한 구조는 한 번 시도해본 것으로써 복잡하다고 생각된다면 적절히 단순하게 만들면 됨.
이제 다시 본론으로 돌아가자.
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 /"]
앞서 작성한 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
<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
이제, 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"
다음으로 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"
<update here>
로 되어 있음. 이 부분을 앞서 정의한 이미지 태그명으로 수정하는 것.지금까지 작성한 파일들에 대한 목록을 보자.
$ 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/build
나 env/delivery
밑에 dev
폴더 뿐만 아니라 stg
, prd
폴더도 생성해서 사용하려 함.
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으로 배포해보자.