[GCP]GKE 차근 차근 알아보기 5탄 — Cloud Build 를 통한 CI/CD

이정운 (Jungwoon Lee)
21 min readJan 30, 2019

--

안녕하세요 이정운 입니다.

지난번에 Kubernetes 의 configMap 과 Secrets 를 활용하여 GKE cluster 위에서 구동되는 container 화 된 애플리케이션의 환경 정보를 분리하여, 조금 더 유연하고 이식성 있게 유지할 수 있는 구조를 가져보는 방안을 살펴보고 테스트 해봤습니다. 꼭 해당 이야기에서 언급한데로 구조를 가지고 갈 필요는 없겠지만 그래도 기존 방식이 아니라 stateless 라는 kubernetes 의 특성을 잘 이해하고 살릴 수 있는 구조로 가지고 가야지 조금 더 GKE 의 장점을 잘 얻을 수 있지 않을까합니다.

그건 그렇고, 지난번까지 GKE 에 대한 다양한 이야기를 하나씩 하다보니, 결국 이야기가 진행되면서 애플리케이션을 수정하고 다시 빌드하고 배포하는 작업을 계속 반복했는데, 이런 반복작업은 굉장히 시간 소비가 클 뿐만이 아니라 수작업으로 계속 수행하기 번거로운 작업 입니다. 지금 테스트처럼 작은 단위의 테스트면 몰라도 조금 더 큰 시스템을 일일이 수작업으로 빌드, 배포하는 것은 쉽지 않은 일 입니다. 이미 GKE 환경이 아닌 다른 환경에서도 많이 사용해서 잘 알고 계시겠지만 이러한 반복 작업들을 자동화하기 위해서 CI/CD(Continuous Integration/Continuous Deploy&Deployment) 라는 도구를 활용해서 파이프라인을 만들고 자동화 작업을 수행합니다. 당연히 GKE 도 이러한 CI/CD 도구를 활용해서 파이프라인을 만들거나 자동화 하는 것이 가능하며 이때 사용할 수 있는 Google cloud 에서 제공되는 것이 Cloud Build 라는 솔루션 입니다.

GCP 의 Cloud Build 를 사용하면 특정 언어에 상관없이 소프트웨어를 신속하게 빌드 및 배포 할 수 있으며 VM, 서버리스, Kubernetes, Firebase 등 다양한 환경에서 커스텀 빌드, 테스트, 배포 워크플로를 정의해서 사용하는 것이 가능합니다. 여기에 당연히 Managed Kubernetes 환경인 GKE 도 포함되며 오늘은 Cloud Build 를 한번 살펴보고 실제로 테스트 하는 시간을 가져보면서 이해할 수 있도록 이야기를 진행해 보겠습니다.

https://cloud.google.com/cloud-build/?hl=ko

본 이야기는 하단과 같이 좋은 이야기를 기반으로 참고해서 작성되었습니다.

Building, testing, and deploying artifacts
https://cloud.google.com/cloud-build/docs/configuring-builds/build-test-deploy-artifacts

Running builds using the GitHub app
https://cloud.google.com/cloud-build/docs/run-builds-on-github

Automating builds using build triggers
https://cloud.google.com/cloud-build/docs/running-builds/automate-builds

GKE Deployments with Cloud Builder
https://github.com/GoogleCloudPlatform/container-builder-workshop

Continuous Delivery Using Google Kubernetes Engine and Google Cloud Build
http://stephenmann.io/post/continuous-delivery-using-google-kubernetes-engine-and-google-cloud-build/

Continuous Deployment with Cloud Build
https://codelabs.developers.google.com/codelabs/cloud-builder-gke-continuous-deploy/index.html?index=..%2F..cloud#0

GoogleCloudBuild/gcbapp-example
https://github.com/GoogleCloudBuild/gcbapp-example

#1) GKE 샘플 애플리케이션 구성

먼저 새로운 GKE 샘플 애플리케이션을 이용해서 테스트를 위한 구성을 해보도록 하겠습니다. 이전 이야기에서 사용한 샘플 애플리케이션을 그대로 쓰고 싶으신 분들은 그대로 활용해도 되지만 이것 저것 뒤지다가 Google cloud platform 의 공식 github 에 조금 데모가 편한 샘플 애플리케이션이 있어서 이를 활용하도록 하겠습니다. 또한, 샘플 애플리케이션 내의 deployment 나 service 의 yaml 을 보니 잘 정의되어 있어서 이 부분을 공부하실 때도 도움이 될듯 합니다.

GKE Deployments with Cloud Builder
https://github.com/GoogleCloudPlatform/container-builder-workshop

참고적으로 다음 파트에서 실제 배포 작업을 수행하게되면 소스 변경을 하고 commit 등의 작업을 해야 하므로 Google cloud platform 의 공식 github 에서 샘플소스를 개인 계정의 github 의 저장소로 Fork 하는 작업을 먼저 수행해 둡니다. (해당 github 에서 Fork 버튼을 클릭하면 됩니다- 본 이야기가 github 강의는 아니기때문에 github 설명은 가급적 최소로 할 예정입니다.)

테스트할 github 저장소가 준비되었다면 GKE 샘플 애플리케이션을 실제로 배포하기 위해서 GKE 의 해당 cluster 로 가서 상단 메뉴에 있는 ‘CONNECT’ 를 클릭하여 kubectl 수행을 위한 credential 을 가지고 옵니다.

복사한 명령을 Cloud Shell 에서 수행하면 하단과 같이 GKE cluster 환경에 접속하여 자동으로 필요한 credential 을 가지고 오게 되고 GKE 차원에서 필요한 기본 준비가 된 것입니다.

이제 자신의 github 로 Fork 한 샘플 애플리케이션을 직접 빌드하고 배포하기 위해서 해당 소스를 로컬 환경으로 clone 합니다.

git clone https://github.com/jwlee98/container-builder-workshop.git
cd ./container-builder-workshop

샘플 애플리케이션 준비가 되었으니 실제 배포를 위해서 kubernetes 환경에서 새로운 namespace 인 production 을 하단과 같이 kubectl 을 통해서 만듭니다.(여기서 namespace 를 별도로 만든것은 같은 GKE cluster 를 사용하면서 환경을 구분하고 관리를 편하게 하기 위한 목적이며 반드시 필요한 것은 아닙니다. 나중에 기회가 되면 설명하겠지만 kubernetes 의 특징중의 하나는 namespace 를 이용해서 물리적인 자원을 논리적으로 구분해서 사용 가능하다는 점 입니다. – https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/)

kubectl create ns production

Namespace 가 생성되었다면 github 에서 다운 받은 샘플 애플리케이션의 deployments 와 services 를 생성합니다. 제가 이전 이야기에 일부 yaml 을 설명했기 때문에 이번 이야기에서 상세하게 yaml 을 설명하지는 않지만 가급적 kubernetes/deployments/prod 에 있는 두 개의 yaml 파일과 kubernetes/services 에 있는 두 개의 yaml 파일은 직접 확인해보고 그 의미를 이해해보는 시간을 갖는것을 추천드립니다.

kubectl apply -f kubernetes/deployments/prod -n production
kubectl apply -f kubernetes/services -n production

deployment 와 service 가 정상적으로 배포되었다면 하단과 같이 노출된 service 의 external-IP 를 확인해서 서비스가 정상적으로 나오는지 테스트 해볼 수 있습니다.

export FRONTEND_SERVICE_IP=$(kubectl get -o jsonpath="{.status.loadBalancer.ingress[0].ip}"  --namespace=production services gceme-frontend)curl http://$FRONTEND_SERVICE_IP/version

실제 브라우저를 이용해서 접속하면 하단과 같이 두 부분으로 구성된 간단한 웹 페이지가 나오는 결과를 확인할 수 있습니다.

해당 github 에서 기공유된 아키텍처를 보셨겠지만 저희가 사용한 샘플 애플리케이션은 kubernetes 에서 frontend pod 와 backend pod 라는 2 개의 pod 로 분리된 하단과 같은 구조를 통해서 간단하게 서비스를 보여주는 애플리케이션 입니다.

실제 GKE 환경에서도 미리 지정된 label 을 통해서 하단과 같이 frontend 와 backend 로 명명된 pod 가 구동중인 것을 직접 확인 가능합니다.

kubectl get pods -n production -l app=gceme -l role=frontend
kubectl get pods -n production -l app=gceme -l role=backend

#2) Cloud Build 를 통한 CI/CD

지난 파트까지 간단하게 새로운 샘플 애플리케이션을 GKE 환경으로 다시 배포해보고 정상적으로 서비스가 되는지 테스트를 해봤습니다. 이제는 이번 이야기의 본 게임인 Cloud Build 를 통한 CI/CD 구성을 해보도록 하겠습니다.

우선 가장 먼저 GKE 작업을 위한 권한에 문제가 없도록 하기 위하여 Cloud IAM 을 통해서 cloudbuild 의 service account 에 conatiner.developer 권한을 매핑하는 작업을 하단과 같이 수행합니다.

export PROJECT=$(gcloud info --format='value(config.project)')
export PROJECT_NUMBER="$(gcloud projects describe \
$(gcloud config get-value core/project -q) --format='get(projectNumber)')"
gcloud projects add-iam-policy-binding ${PROJECT} \
--member=serviceAccount:${PROJECT_NUMBER}@cloudbuild.gserviceaccount.com \
--role=roles/container.developer

다음으로 샘플 애플리케이션의 Dockerfile 을 수정하여 golang 을 최신 1.8 버전의 환경을 가져오도록 변경합니다. (테스트 해보니 github 의 소스를 그대로 사용하면 빌드시에 문제가 발생하여 빌드할 golang 버전을 올려주어야 합니다.)

FROM golang:1.8-onbuild

이제 본격적으로 하단의 링크를 참고하여 Cloud Build 를 위한 설정 파일인 cloudbuild.yaml 파일을 작성합니다. 가이드를 보시면 아시겠지만 결국 해당 설정 파일에서 빌드/배포를 위한 파이프라인 구성이 가능하다는 것을 확인 가능합니다. (설명과 함께 다양한 예제가 포함되어 있으니 꼭 확인해보시기 바라겠습니다.)

Build configuration overview
https://cloud.google.com/cloud-build/docs/build-config

좀 편하게 이미 다운 받아 놓은 샘플 애플리케이션의 builder/cloudbuild-prod.yaml 파일을 그대로 쓰고 싶었는데 약간의 이슈가 있어서 그걸 그대로 복사해다가 CLUSTER, ZONE 등의 변수를 받아오는 부분만 살짝 변경하여 사용하도록 하겠습니다. cloudbuild.yaml 의 구성은 각 단계에서 수행할 entrypoint: ‘bash’ 와 args 로 이루어진 조합이며 보시는 것처럼 bash shell 명령을 사용가능하므로 원하는 다양한 액션을 직접 만들어서 변경할 수 있습니다.

-----cloudbuild-prod2.yaml----steps:### Build
- id: 'build'
name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- '-c'
- |
docker build -t gcr.io/$PROJECT_ID/gceme:$TAG_NAME .
### Test
### Publish
- id: 'publish'
name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- '-c'
- |
docker push gcr.io/$PROJECT_ID/gceme:$TAG_NAME
### Deploy
- id: 'deploy'
name: 'gcr.io/cloud-builders/gcloud'
env:
- 'CLOUDSDK_COMPUTE_ZONE=${_CLOUDSDK_COMPUTE_ZONE}'
- 'CLOUDSDK_CONTAINER_CLUSTER=${_CLOUDSDK_CONTAINER_CLUSTER}'
- 'KUBECONFIG=/kube/config'
entrypoint: 'bash'
args:
- '-c'
- |
CLUSTER=${_CLOUDSDK_CONTAINER_CLUSTER}
PROJECT=$$(gcloud config get-value core/project)
ZONE=${_CLOUDSDK_COMPUTE_ZONE}
gcloud container clusters get-credentials "$${CLUSTER}" \
--project "$${PROJECT}" \
--zone "$${ZONE}"
sed -i 's|gcr.io/cloud-solutions-images/gceme:.*|gcr.io/$PROJECT_ID/gceme:$TAG_NAME|' ./kubernetes/deployments/prod/*.yaml

kubectl get ns production || kubectl create ns production
kubectl apply --namespace production --recursive -f kubernetes/deployments/prod
kubectl apply --namespace production --recursive -f kubernetes/services
-----cloudbuild-prod2.yaml----

이렇게 Cloud Build 를 위한 설정 파일에 대한 준비가 되었다면 먼저 정상적으로 잘 수행되는지 파악하기 위해서 로컬 테스트를 수행해 보도록 합니다. 로컬 테스트의 경우에는 gcloud builds 명령을 사용하며 TAG_NAME 이나 cloudbuild.yaml 파일에서 내부적으로 사용된 변수를 직접 넣어주어야 한다는 점을 유념하시기 바라겠습니다.

export PROJECT_ID=$(gcloud info --format='value(config.project)')
export CLUSTER=kub-standard-cluster-01
export ZONE=asia-northeast1-c
gcloud builds submit --config builder/cloudbuild-prod2.yaml --substitutions=TAG_NAME=1.0.1,_CLOUDSDK_COMPUTE_ZONE=${ZONE},_CLOUDSDK_CONTAINER_CLUSTER=${CLUSTER} .

로컬 테스트 후에 정상적으로 배포가 되었는지 확인합니다. 기본적으로 큰 변화가 없다면 추가 테스트를 하지 않고도 해당 pod 의 AGE 가 변경되었는지 여부를 통해서 반영 여부를 확인할 수도 있습니다.

kubectl get pods -n production -o wide

로컬 테스트에 큰 이슈가 없다면 git add 및 commit 해서 해당 내용을 내 github 의 저장소에 반영합니다. (향후 Cloud Build 의 trigger 를 사용할때 cloudbuild.yaml 을 활용하기 위한 목적입니다. 따라서, 만드신 cloudbuild.yaml 파일의 경로는 숙지하고 있어야 합니다.)

#3) Github 에서 App 을 통한 Cloud Build 의 trigger 연동

로컬 테스트를 수행해서 정상적으로 결과가 나오는 것을 확인했으면 이제 본격적으로 좀 더 CI/CD 스럽게 github 연동 작업을 수행하도록 하겠습니다. GCP 의 Cloud Build 는 github 의 App 을 이용해서 저장소 변경시에 자동으로 Cloud Build 의 trigger 를 동작 시킬 수 있습니다. 이를 다시 정리하면 github 의 저장소가 변경되는 이벤트 만으로도 자동으로 Cloud Build 를 수행해서 애플리케이션을 빌드/배포 할 수 있습니다.

이를 위해서 Google Cloud Build 라는 github application 에 대한 설치가 필요합니다.(설치해보시면 아시겠지만 로컬에 실제 설치되는 것은 아니며 개인 github 계정에 등록되는 것입니다.) 하단의 링크로 접속하여 install 버튼을 클릭합니다.

https://github.com/apps/google-cloud-build

그러면, 하단과 같이 어떤 저장소를 쓸지를 선택하고 Install 을 클릭하여 설치하면 됩니다. (당연히 상단에서 Fork 한 본인 github 의 저장소를 선택하면 됩니다.)

github 의 Google Cloud Build App 설치가 마무리 되었다면 이제 Cloud Build 의 Trigger 를 생성해보도록 하겠습니다. 관리 콘솔에서 Cloud Build > Triggers 메뉴에서 new 를 클릭합니다. 처음에 소스 위치가 나오는데 Github 를 선택합니다.

그러면 하단과 같이 github 페이지로 redirect 되어서 권한 확인을 위한 github 계정정보를 입력하게 되어 있습니다. 각자 자신이 가지고 있는 github 계정을 넣어주면 됩니다 .

계정 확인이 잘 되었으면 이제 github 내의 어떤 저장소랑 연결할 건지 지정해주고 continue 를 클릭하면 됩니다.

마지막으로 trigger 를 위한 세부 설정을 합니다. 예를 들어 Trigger type 을 어떤 것으로 할 것인지(Branch 가 변경될 때 반응 or Tag 가 변경될 때 반응) 나 cloudbuild.yaml 파일의 위치(github 기준으로 폴더 위치를 지정 — builder/cloudbuild-prod2.yaml 이 되어야 합니다.), 맨 앞에 ‘_’ 로 시작하는 변수(cloudbuild.yaml 파일 내에서 사용한 변수)의 값 등을 넣을 수 있습니다. 크게 이슈가 없다면 해당 설정은 하단과 같이 해주고 Create trigger 버튼을 클릭하면 trigger 가 생성됩니다.

해당 작업이 정상적으로 수행되면 하단과 같이 Trigger 가 생성된 것을 확인할 수 있습니다. 특히, 해당 Trigger 는 ‘Run a trigger’ 명령을 클릭하여 바로 테스트 해보실 수 있습니다.

언급한데로 ‘Run a trigger’ 명령을 클릭하여 메뉴얼로 테스트를 해보면 하단과 같이 History 에서 자동으로 Build 가 수행된 것을 확인할 수 있습니다.

해당 빌드를 클릭하면 로그와 같이 좀 더 자세한 내용을 확인해 보실 수 있습니다.

당연하겠지만 이 작업 중에 kubectl 명령으로 pods 를 확인해보면 하단과 같이 새로운 버전의 배포작업이 일어나서 pods 가 변경되는 것을 확인할 수 있습니다.

#4) Github 에서 소스 변경을 통한 테스트

자 이제 거의 다 왔습니다. ^^& 모든 구성이 다 되었으므로 마지막으로 github 의 내 저장소에서 직접 소스를 변경하여 Cloud Build 를 거쳐 실제 GKE 환경까지 애플리케이션이 배포되는 작업을 테스트 해보도록 하겠습니다.

Github 의 샘플 애플리케이션이 있는 내 저장소에 가서 루트에 있는 html.go 파일의 <div class=”card blue”> 구문을 <div class=”card green”> 로 모두 변경 합니다. (github 에서 연필 모양 아이콘을 통해서 바로 수정 가능하며 보시면 직관적으로 아시겠지만 파란색을 녹색으로 변경하는 것 입니다.) 그리고 하단과 같이 해당 변경 사항을 commit 합니다.

현재 Cloud Build 의 trigger 변경 기준을 Tag 로 했기 때문에 commit 한다고 해서 변경이 발생되지는 않으며 github 에서 버전이 변경되었다는 의미로 tag 를 업데이트 해줍니다.

해당 작업을 수행하면 자동으로 github 내에서 Webhook 이 발생되고 App 을 통해 만들어 둔 Cloud Build 의 Trigger 가 작동합니다.

미리 지정된 Cloud Build 의 설정 파일인 cloudbuild.yaml 에서 정의한 순서대로 빌드/배포 작업이 진행됩니다. (여기서는 github 내에 배포된 builder/cloudbuild-prod2.yaml)

이 작업이 완료되고 테스트를 해보면 github 의 소스 변경 및 commit 과 tag 업데이트 만으로 자동으로 소스 빌드 및 배포작업이 수행되고 GKE 샘플 애플리케이션의 배경 카드가 파란색에서 녹색으로 변경된 것을 하단과 같이 확인할 수 있습니다.

여기까지 잘 따라오셨다면 조금 길긴 하였지만 GCP 의 CI/CD 도구인 Cloud Build 를 활용하여 GKE 환경에 대한 자동화된 빌드/배포 구성을 수행하였고 실지 테스트까지 문제 없이 수행하신 것 입니다. 특히나, Cloud Build 뿐만 아니라 개발자들이 많이 사용하는 소스 저장소인 github 의 변경에 대한 부분을 시작점으로 Trigger 를 통해서 이벤트 발생을 캐치하는 작업도 같이 잘 수행해보신 것 입니다. 반드시 GKE 환경의 CI/CD 도구를 GCP 의 Cloud Build 를 사용할 필요는 없을 수도 있겠지만 테스트 해보셔서 느낀 것처럼 쉽게 사용할 수 있게 구성되어 있으며 비용도 거의 무료수준에 가까울정도로 저렴해서 큰 부담없이 사용하기에는 간편하고 좋지 않을까 합니다.

긴 이야기 였지만 여기까지 따라와 주신 점 감사드리며 이번 이야기는 여기서 마무리 하고 다음 이야기로 다시 돌아오도록 하겠습니다. 그럼 이만 휘리릭~~~

* Disclaimer: 본 글의 작성자는 Google 직원이지만 Google cloud 를 공부하는 한 개인으로서 작성된 글입니다. 본 글의 내용, 입장은 Google 을 대변하지 않으며 Google 이 해당 콘텐츠를 보장하지 않습니다.

--

--

이정운 (Jungwoon Lee)

Technical engineer who dreams better future. (These thoughts are my own personal opinions, and do not reflect or represent Google’s opinions or plans.)