안녕하세요. Gameeye에서 deeplol.gg 서비스를 개발 중인 김철기입니다.
클라우드 서버 인프라 구축, 백엔드 개발, 딥러닝 모델 연구를 담당하고 있습니다.
이번 포스팅에서는 자동으로 소스를 빌드하고 AWS ECR에 푸쉬한 뒤 쿠버네티스에 배포하는 내용을 다루겠습니다. 쿠버네티스 환경 세팅과 도메인 관리, AWS ECR 사용법은 이전 포스팅에서 다뤘으니 참고해주시기 바랍니다.
CodeBuild에서 EKS에 접근하기 위해서는 권한이 필요합니다. CodeBuild 생성 단계에서 자동으로 생성하게 할수도 있으나 해당 포스팅에서는 미리 생성 후 진행하도록 하겠습니다.
인라인 정책과 신뢰 관계에 아래 내용을 설정하여 Role을 생성해줍니다.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": "eks:DescribeCluster",
"Resource": "*"
}
]
}
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Service": "codebuild.amazonaws.com"
},
"Action": "sts:AssumeRole"
}
]
}
이제 위에서 생성한 권한을 EKS에게 알려줘야합니다.
아래 명령어로 aws-auth를 수정합니다.
kubectl -n kube-system edit cm aws-auth
기존 내용을 아래 내용으로 수정해줍니다.
🚨 위에서 생성한 Role은 Service Role이지만 service-role을 제외한 arn을 입력해줘야합니다.
arn:aws:iam::{account_id}:role/service-role/codebuild-EKS-DEPLOY-service-role (x)
arn:aws:iam::{account_id}:role/codebuild-EKS-DEPLOY-service-role (o)
apiVersion: v1
data:
mapRoles: |
- groups:
- system:bootstrappers
- system:nodes
rolearn: arn:aws:iam::${ACCOUNT_ID}:role/myAmazonEKSNodeRole
username: system:node:{{EC2PrivateDNSName}}
- groups:
- system:masters
rolearn: 위에서 생성한 Role의 ARN
username: 위에서 생성한 Role의 NAME
kind: ConfigMap
metadata:
annotations:
name: aws-auth
namespace: kube-system
uid: xxxx-xxxx-xxxx-xxxx-xxxx
AWS CodeCommit 콘솔에서 리포지토리를 생성합니다.
buildspec.yml 파일을 작성합니다.
kubectl 설치, ecr 로그인, docker 빌드, eks deploy 과정이 포함되어 있습니다.
(해당 포스팅에서는 dockerfile을 따로 작성하지 않고 예제로 사용해온 슈퍼마리오 이미지를 사용합니다. 직접 dockerfile을 만들어서 빌드하실 분은 주석처리된 2줄을 사용하시면 됩니다.)
version: 0.2
phases:
install:
runtime-versions:
docker: 19
commands:
- echo Install Kubectl
- echo ---------------------------------
- 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 sts get-caller-identity
- aws eks --region ap-northeast-2 update-kubeconfig --name {cluster_name}
- echo ---------------------------------
pre_build:
commands:
- echo ENV Values
- echo ---------------------------------
- echo $AWS_DEFAULT_REGION
- echo $AWS_ACCOUNT_ID
- echo $IMAGE_REPO_NAME
- echo $IMAGE_TAG
- echo $CODEBUILD_BUILD_NUMBER
- echo ---------------------------------
- echo Logging 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 Build started on `date`
- echo Application Build
- echo ---------------------------------
- echo GO Build Start
- echo Go Build Code In here
- echo GO Build Stop
- echo ---------------------------------
- echo Start Docker
- docker version
- echo ---------------------------------
- echo Building the Docker image...
- echo ---------------------------------
- docker pull pengbai/docker-supermario
- docker tag pengbai/docker-supermario $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG-$CODEBUILD_BUILD_NUMBER
# - docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG-$CODEBUILD_BUILD_NUMBER .
# - docker tag $IMAGE_REPO_NAME:$IMAGE_TAG-$CODEBUILD_BUILD_NUMBER $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG-$CODEBUILD_BUILD_NUMBER
- echo ---------------------------------
- echo Pushing the Docker image...
- echo ---------------------------------
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG-$CODEBUILD_BUILD_NUMBER
post_build:
commands:
- AWS_ECR_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG-$CODEBUILD_BUILD_NUMBER
- DATE=`date`
- echo Build completed on $DATE
- sed -i 's#AWS_ECR_URI#'"$AWS_ECR_URI"'#' ./eks-app-deploy.yml
- kubectl apply -f ./eks-app-deploy.yml
eks-app-deploy.yml 파일을 작성합니다. AWS_ECR_URI 부분은 buildspec.yml 파일에서 ECR URI로 치환해줍니다.
apiVersion: apps/v1
kind: Deployment
metadata:
name: mario
labels:
app: mario
spec:
replicas: 1
selector:
matchLabels:
app: mario
template:
metadata:
labels:
app: mario
spec:
containers:
- name: mario
image: AWS_ECR_URI
---
apiVersion: v1
kind: Service
metadata:
name: mario
annotations:
external-dns.alpha.kubernetes.io/hostname: {도메인명}
spec:
selector:
app: mario
ports:
- port: 80
protocol: TCP
targetPort: 8080
type: LoadBalancer
dev 브랜치를 생성해줍니다.
CodeBuild 프로젝트를 생성해줍니다.
프로젝트 이름을 정해주고,
빌드할 소스의 위치와 브랜치 조건을 설정합니다.
환경은 관리형 이미지, 리눅스, 최신 버전이미지를 선택하고 앞에서 생성한 역할 ARN을 설정해줍니다.
🚨 이미지를 aws/codebuild/amazonlinux2-x86_64-standard:4.0으로 사용할 때 docker version을 19로 설정하지 못하는 이슈가 있었습니다. 3.0으로 바꿔 19로 사용해주시거나 4.0으로 20버전을 사용해주세요.
(buildspec.yml 파일의 docker: 19 부분입니다.)
phases:
install:
runtime-versions:
docker: 19
추가 구성에서 환경 변수를 설정해줍니다. region과 id는 본인 계정값으로 입력하시면 됩니다.
마지막으로 Buildspec 부분은 buildspec 파일 사용을 선택하시고 빌드 프로젝트 생성 버튼을 누르시면 됩니다.
콘솔에서 파이프라인 생성 버튼을 눌러 파이프라인을 생성해줍니다.
먼저 파이프라인 이름을 입력 후 다음 버튼을 클릭합니다.
소스 공급자를 CodeCommit으로 설정하고 앞서 생성한 리포지토리와 브랜치를 선택해줍니다.
빌드 스테이지에는 CodeBuild 프로젝트를 선택해줍니다.
CodeBuild에 배포 단계가 포함되어 있으므로 배포 스테이지는 건너뛰기 해줍니다.
검토 페이지에서 파이프라인 생성 버튼을 클릭하는 것으로 파이프라인 생성을 완료합니다.
배포에 성공하면 CodeBuild 프로젝트의 빌드 상태가 성공함으로 변경되고 성공 로그가 출력됩니다.
파이프라인에서도 성공이라는 표시가 출력됩니다.
설정한 도메인으로 접근하면 빌드된 이미지의 서비스가 정상적으로 출력되는 것을 확인할 수 있습니다.
드디어 CI/CD까지 해보았습니다👍 포스팅에는 테스트 단계가 누락되어서 CI까지 완벽하게 수행했다고 보기는 어렵지만 코드 병합과 디플로이 자동화는 해당 포스팅으로 충분히 따라해보실 수 있습니다.
이후로는 오토스케일링을 이용한 Node, Pod 리소스 관리와 대시보드, airflow 또는 kubeflow를 이용한 배치 관리를 정리해볼 생각입니다. 아직도 갈길이 멀지만 차근차근 정리해보도록 하겠습니다.
포스팅 내용중 문제가 있거나 궁금한 부분이 있으시면 답글 남겨주세요🙏