이번 글에서는 kops로 구축한 AWS 쿠버네티스 클러스터에 AWS CodeCommit, AWS CodeBuild, AWS CodePipeline, AWS ECR을 활용하여 CI/CD 파이프라인(Pipeline)을 만들어보도록 하겠다.
이 글의 순서는 다음과 같다.
- 실습 전 준비사항
- CI/CD 파이프라인 만들기
- CI/CD 파이프라인 테스트
1. 실습 전 준비사항
CI/CD 파이프라인 실습을 하기 위해서는 사전에 AWS에서 kops를 활용해서 쿠버네티스 클러스터 구축이 필요하다.(https://twofootdog.tistory.com/43 참고)
2. CI/CD 파이프라인 만들기
그럼 본격적으로 CI/CD Pipeline을 만들어보도록 하자.
우선 CI/CD Pipeline을 만드는 순서는 다음과 같다.
- AWS ECR 레파지토리 생성
- AWS CodeCommit 생성 후 로컬PC 스프링부트 프로젝트와 연결
- AWS CodeBuild 생성
- AWS CodeBuild IAM역할에 ECR 관련 정책 & AmazonS3FullAccess 정책 연결
- AWS CodePipeline 생성
- 쿠버네티스 컨피그파일(~/.kube/config) 관리용 S3 버킷 생성 후 config파일 업로드
- 로컬PC 스프링부트 서비스 수정(buildspec.yml, Dockerfile, Deployment.yaml, Service.yaml 파일 생성)
2-1. AWS ECR 레파지토리 생성
[AWS Management Console] -> [ECR] -> [Repositories] -> [리포지토리 생성] 선택
[리포지토리 구성]에서 [리포지토리 이름] 작성. 그 밑에 옵션값은 본인이 필요한 값만 활성화 시킴. 작성이 완료되면 [리포지토리 생성] 클릭.
그러면 다음과 같이 레파지토리가 생성되면서 URI가 나옴. URI는 [AWS Account ID].dkr.ecr.[AWS DEFAULT REGION].amazonaws.com/[REPOSITORY NAME] 순으로 나옴. 해당 내용은 기억해뒀다가 CodeBuild 생성 시 환경변수로 지정할 것임.
2-2. AWS CodeCommit 생성 후 로컬PC 스프링부트 프로젝트와 연결
해당 내용은 https://twofootdog.tistory.com/33 를 참고하고 진행하면 된다.
2-3. AWS CodeBuild 생성
다음으로 AWS Codebuild를 생성해보자.
[AWS Management Console] -> [CodeBuild] -> [프로젝트 빌드] -> [빌드 프로젝트 생성]을 선택한다.
[빌드 프로젝트 생성]에서 [프로젝트 이름]을 작성하고, [소스]에서 [소스 공급자] 및 [리포지토리]를 미리 만들어놨던 CodeCommit을 선택한다.
[환경]에서 다음과 같이 선택한다.
선택 시 [역할 이름]을 기억해두자. CodeBuild를 셋팅한 후 해당 역할 이름에 IAM 정책을 연결할 것이기 때문이다.
맨 아래 [추가 구성]을 선택한다.
[환경 변수]에서 프로젝트 빌드 시 사용할 환경변수를 작성한다. 해당 환경변수는 프로젝트 빌드 시 buildspec.yml 파일 내 변수로 사용된다. AWS_DEFAULT_REGION은 서울리전 사용중이니 ap-northeast-2, IMAGE_REPO_NAME은 조금 전 생성한 AWS ECR의 레파지토리명, IMAGE TAG는 임의로 넣고, AWS_ACCOUNT_ID는 AWS ECR 생성시 만들어진 URI의 제일 앞 문자열을 넣는다(환경변수를 작성할 때 이름과 값 맨 끝에 띄워쓰기가 있는지 조심하도록 하자)
[Buildspec]에서 파일은 buildspec.yml로 입력하고(빌드 스탭이 명시된 파일로 스프링부트 프로젝트 내에 생성할 예정), [아티팩트]의 [유형]은 아티팩트 없음을 선택한다. 원래 스프링부트의 JAR파일을 그대로 배포할 때는 아티팩트 파일로 만들어서 S3에 올려놓은 후, S3에 있는 JAR파일을 EC2 인스턴스로 올려서 실행을 시켰지만, 쿠버네티스 클러스터에 배포할 때는 S3에 올려놓을 필요 없이 도커 이미지를 AWS ECR에 올려놓기 때문이다. 설정이 완료되었으면 [빌드 프로젝트 생성] 버튼을 누른다.
2-4. AWS CodeBuild IAM역할에 ECR 관련 정책 & AmazonS3FullAccess 정책 연결
AWS CodeBuild만 생성했다고 해서 AWS ECR에서 바로 도커 이미지를 가져올 순 없다. AWS CodeBuild IAM 역할에 ECR 접근 관련 정책이 없기 때문이다. 때문에 조금 전 CodeBuild 생성 시 같이 생성된 IAM 역할에 ECR 접근을 위한 정책을 연결할 것이다.
우선 [AWS Management Console] -> [IAM] -> [정책] -> [정책 생성] -> [JSON]을 선택한 후 다음과 같이 입력한다.
{
"Statement": [
{
"Action": [
"ecr:BatchCheckLayerAvailability",
"ecr:CompleteLayerUpload",
"ecr:GetAuthorizationToken",
"ecr:InitiateLayerUpload",
"ecr:PutImage",
"ecr:UploadLayerPart"
],
"Resource": "*",
"Effect": "Allow"
}
],
"Version": "2012-10-17"
}
정책 검토 시 [이름]을 입력한 후 [정책 생성]을 클릭한다.
그 다음 [AWS Management Console] -> [IAM] -> [역할]로 가서 CodeBuild 생성 시 만들어진 IAM 역할을 선택한다.
그리고 [정책 연결] 버튼을 클릭한 후 조금 전 생성한 ECR 정책을 선택하고 [정책 연결]을 클릭한다.
ECR 정책 연결 후 AmazonS3FullAccess 정책도 연결해준다. 왜냐하면 S3에 업로드된 쿠버네티스 컨피그파일(~/.kube/config)를 CodeBuild를 수행하는 호스트로 가져와야 하기 때문이다.
2-5. AWS CodePipeline 생성
다음으로 AWS CodePipeline을 생성해보도록 하자.
사실 CodeCommit과 CodeBuild를 이미 생성해 놓았으니 그 두개를 CodePipeline에 등록만 시키면 된다.
[AWS Management Console] -> [CodePipeline] -> [파이프라인 생성]을 선택한다.
[파이프라인 이름]을 작성하고 [다음]으로 넘어간다.
[소스 스테이지 추가]에서는 조금 전 생성했던 AWS CodeCommit 레파지토리를 선택한다.
[빌드 스테이지 추가]에서는 조금 전 생성했던 AWS CodeBuild 프로젝트를 선택한다. buildspec.yml에서 사용되는 환경변수는 CodeBuild 말고 이곳에서 설정할 수도 있다.
[배포 스테이지 추가]에서는 [배포 스테이지 건너뛰기]를 선택한다(빌드 스테이지에서 이미 도커 이미지를 ECR로 올린 후 kubectl 로 쿠버네티스 클러스터에 배포하기 때문에 배포스테이지는 별도로 필요하지 않다).
2-6. 쿠버네티스 컨피그파일(~/.kube/config) 관리용 S3 버킷 생성 후 config파일 업로드
다음으로 AWS S3에 버킷 한개를 만들어 볼 것이다. 버킷을 만드는 이유는 쿠버네티스 config파일을 업로드 해 놓은 후 CodeBuild로 빌드 수행 시 해당 config 파일을 Build하는 서버로 가져오기 위함이다. 왜 config 파일을 가져오냐면, CodeBuild로 빌드를 할 때 "kubectl apply" 명령어를 통해서 빌드를 하게 되는데 해당 명령어를 수행하는 서버는 쿠버네티스 클러스터와 연결된 서버가 아니다. 때문에 kubectl apply 명령어 수행 자체가 되지 않는다(사실 kops로 쿠버네티스 CI/CD 파이프라인을 만들 때 CodeBuild를 활용하는 레퍼런스가 별로 없었기 때문에 이부분에서 제일 많이 헤맨 듯 싶다). 때문에 kops가 설치된 서버에 있는 쿠버네티스 컨피그파일(~/.kube/config)를 S3 버킷에 올려놓은 후 CodeBuild로 빌드르 할 때 해당 config파일을 빌드서버로 옮겨놓아서 kubectl apply 수행 시 쿠버네티스 클러스터와 연결되게끔 할 것이다.
우선 [AWS Management Console] -> [S3] -> [버킷 만들기]를 선택한 후 Default 셋팅값으로 버킷 한개를 만든다.
그 다음 kops가 설치된 EC2 인스턴스로 가서 S3로 config 파일을 업로드 시킨다.
# aws s3 cp ~/.kube/config s3://gajoa-bucket
2-7. 로컬PC 스프링부트 서비스 수정(buildspec.yml, Dockerfile, Deployment.yaml, Service.yaml 파일 생성)
다음으로 로컬PC의 스프링부트 프로젝트 내에 쿠버네티스 클러스터 배포에 필요한 파일을 생성해보자.
- buildspec.yml : CodeBuild가 수행하는 빌드 스탭을 정의해 놓은 파일
- Dockerfile : 도커라이징(도커 빌드. 컨테이너 이미지 생성)을 정의 해 놓은 파일
- Deployment.yaml : 쿠버네티스 클러스터에 올라가는 Deployment 정보를 정의해 놓은 파일
- Service.yaml : 쿠버네티스 클러스터에 올라가는 Service 정보를 정의해 놓은 파일
우선 생성하는 파일의 위치는 다음과 같다.
파일 내용은 다음과 같다.
buildspec.yml :
version: 0.2
phases:
install:
runtime-versions:
docker: 18
commands:
- wget -O kubectl https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl
- chmod +x ./kubectl
- mv ./kubectl /usr/local/bin/kubectl
- mkdir ~/.kube
- aws s3 cp s3://gajoa-bucket/config ~/.kube
pre_build:
commands:
- echo Logging in to Amazon ECR...
- $(aws ecr get-login --no-include-email --region $AWS_DEFAULT_REGION)
build:
commands:
- echo Build Starting on `date`
- echo Building with gradle...
- chmod +x ./gradlew
- ./gradlew build
- echo Building the Docker image...
- docker build -t $IMAGE_REPO_NAME:$IMAGE_TAG .
- docker tag $IMAGE_REPO_NAME:$IMAGE_TAG $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- docker push $AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
post_build:
commands:
- AWS_ECR_URI=$AWS_ACCOUNT_ID.dkr.ecr.$AWS_DEFAULT_REGION.amazonaws.com/$IMAGE_REPO_NAME:$IMAGE_TAG
- DATE=`date`
- echo Build completed on $DATE
- sed -i.bak 's#AWS_ECR_URI#'"$AWS_ECR_URI"'#' ./k8s/deployment.yaml
- sed -i.bak 's#DATE_STRING#'"$DATE"'#' ./k8s/deployment.yaml
- kubectl apply -f ./k8s/deployment.yaml
- kubectl apply -f ./k8s/service.yaml
Dockerfile :
FROM openjdk:8-jdk
VOLUME /tmp
ADD ./build/libs/toy-project-0.0.1-SNAPSHOT.jar app.jar
ENV JAVA_OPTS=""
ENTRYPOINT ["java","-jar","/app.jar"]
~/k8s/deployment.yml :
#apiVersion: extensions/v1beta1
apiVersion: apps/v1
kind: Deployment
metadata:
name: toy-project-deployment
spec:
replicas: 1
selector:
matchLabels:
app: toy-project
template:
metadata:
labels:
app: toy-project
spec:
containers:
- name: toy-project
image: AWS_ECR_URI #buildspec.yml에서 ECR의 URI:TAG 로 치환해줌
ports:
- containerPort: 8080
imagePullPolicy: Always
env:
- name: DATE
value: 'DATE_STRING'
~/k8s/service.yml :
apiVersion: v1
kind: Service
metadata:
name: toy-project-service
spec:
ports:
- name: "8080"
port: 8082
targetPort: 8080
selector:
app: toy-project
type: LoadBalancer
3. CI/CD 파이프라인 테스트
파일 생성이 완료되었으면 CI/CD 파이프라인 테스트를 진행해보자.
로컬PC의 Git-Bash를 실행시킨 후 소스코드를 AWS CodeCommit으로 Push해보도록 하자.
# cd [스프링부트 프로젝트 디렉토리]
# git add .
# git commit -m "test"
# git push origin master
Enter passphrase for key '/c/Users/minkyu/.ssh/codecommit_rsa.pem': [SSH 키페어 비밀번호]
AWS Pipeline을 확인해보면 배포가 성공적으로 수행된 것을 확인할 수 있다.
AWS ECR 레파지토리를 확인해보면 도커 이미지가 업로드 된 것을 확인할 수 있다.
EC2 인스턴스를 실행해서 쿠버네티스 클러스터에 정상적으로 배포되었는지 확인해보자.
# kubectl get deployment
# kubectl get pod
# kubectl get svc
마지막으로 스프링부트 서비스를 실행시켜보자. 접속방법은 조금 전 쿠버네티스 클러스터에 올린 서비스의 EXTERNAL-IP:PORT로 접속하면 된다. 서비스도 정상적으로 수행되는것을 확인할 수 있다. 성공!!
마치며
구글링을 조금 해보면 EKS로 구축한 클러스터에 CodeCommit/CodeBuild를 활용한 CI/CD 파이프라인을 만드는 내용은 참 많은데 이상하게 kops로 구축한 클러스터에 CodeCommit/CodeBuild를 활용한 CI/CD 파이프라인을 만드는 내용은 찾기가 어렵다. 그래서 여러 글을 찾아보고 분석해보고 시행착오도 겪느라 고생을 조금 한 것 같다. 다음에는 AWS Lamda와 SMS를 접목시켜 알람기능도 만들어보고, 현재 진행하는 프로젝트와는 별개로 EKS로 써볼 생각이다.
참고
https://docs.aws.amazon.com/ko_kr/codebuild/latest/userguide/sample-docker.html
https://github.com/ministryofjustice/k8s-nonprod-environments/blob/master/buildspec.yml
https://github.com/khayer117/kubernetes/blob/master/buildspec.yml
https://yunsangjun.github.io/blog/cloud/2019/06/21/aws-cicd03.html
https://blog.frugalops.com/continuous-deployment-kubernetes-platform-using-aws-codepipeline/
'IT > AWS' 카테고리의 다른 글
AWS CodePipeline에 Slack 알람 적용(Lambda, CloudWatch Events 연동) (0) | 2020.03.18 |
---|---|
AWS kops 쿠버네티스 클러스터에 Ingress 적용하기 (0) | 2020.03.08 |
AWS kops 쿠버네티스 관련 에러 정리(진행 중) (0) | 2020.03.06 |
AWS에 kops로 쿠버네티스 클러스터 구축하기 (1) | 2020.02.29 |
AWS RDS 생성 후 EC2 인스턴스 내 서비스와 연동 (0) | 2020.02.20 |
댓글