시중에는 Jenkins, Gitlab, Travis 등 CI/CD 파이프라인을 위한 다양한 툴이 존재합니다. 오늘은 그 중에서도 GitLab EE(상용 버전)을 가지고 EE버전에서만 활용할 수 있는 고급 기능들과 GitLab을 알리바바 클라우드에서 가장 모범적으로 운영할 수 있는 가이드를 설명합니다.
이번 가이드에서는 ECI 기반 Alibaba Cloud Serverless Kubernetes(ASK)과 NAS file storage를 사용합니다. GitLab은 파이프라인에 등록된 CI/CD Job을 실행할 때 별도의 runner를 호스팅하여 사용할 수 있는데, 이를 통해 리소스를 분산시켜 gitlab ecs instance의 비용을 줄일 수 있고 또 고성능의 Job을 무리없이 돌릴 수 있다는 장점을 가져갈 수 있습니다. 그 외에도 위의 아키텍처의 장점을 정리하면 아래와 같습니다.
1) k8s의 Deployment와 PVC를 통한 높은 서비스 가용성 확보
2) Serverless 기반이기 때문에 k8s master/slave 노드를 관리할 필요가 없음
3) Build될 때만 pod가 trigger되어 실행되기 때문에 비용이 절감됨 (ASK는 pod가 실행되는 시간에만 과금됨)
4) 고성능을 필요로하거나 무거운 작업을 돌릴 때 리소스 확장성이 용이
Note: Gitlab Cloud (SaaS) 버전을 사용하더라도 runner는 따로 운용 가능합니다.
이번 실습에서는 Gitlab EE 버전의 고급 기능들도 함께 살펴볼 것이기 때문에 CE가 아닌 EE 버전을 설치하도록 하겠습니다. 또한 Notification/alerm 등의 메세지를 전송하기 위해 Aliababa Cloud의 DirectMail을 사용하고, gitlab instance의 주기적인 백업을 위해 OSS를 사용합니다.
단계적 절차 가이드 문서는 "클라우드 리소스 생성 및 설정 가이드"를 참조하시되, Gitlab 설치는 Gitlab 공식 사이트의 가이드를 참조합니다.
Note: "클라우드 리소스 생성 및 설정 가이드"에서는 CE edition: 무료버전을 설치합니다. 그 부분만 건너 뛰고 공식 가이드에 따라 EE를 설치해 주시면 됩니다. CE와 EE의 기능 차이는 클릭하여 확인해 주세요.
Gitlab runner를 ASK위에 설치합니다. runner는 항시 떠 있지만 나머지 pod는 사용자가 정의한 stage안의 job 이 실행될 때마다 실행되었다 사라집니다. 즉, pipeline이 실행될 때만 리소스를 가져다 사용하는 구조이기 때문에 비용을 매우 절약할 수 있습니다. 동적으로 생성되었다 사라지는 Pod들이 공유하여 스토리지 볼륨을 바라볼 수 있도록 NAS 형태의 PV를 사용할 것입니다.
ASK 생성 가이드를 따라 cluster에 gitlab runner와 runners(kubernetes executor) 구성 설정을 deploy 합니다. 를 생성합니다.
추후 해당 클러스터와의 API 통신을 위해 [Cluster information > Connection information]의 정보를 가지고 kubeconfig file 구성합니다.
gitlab ecs instance와 ASK가 같은 VPC 안에 위치하므로
spec.nfs.server에 복사한 address를 붙여넣기 합니다. .
주의사항:
nas-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: gitlab-runner-cache-pv
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 10Pi
mountOptions:
- nolock,noresvport,noacl,hard
- vers=3
- rsize=1048576
- wsize=1048576
- proto=tcp
- timeo=600
- retrans=2
nfs:
path: /share
server: <IP Address of mount target for cache>
spec.nfs.server에 복사한 address를 붙여넣기 합니다.
mvn-pv.yaml
apiVersion: v1
kind: PersistentVolume
metadata:
name: gitlab-runner-maven-pv
spec:
accessModes:
- ReadWriteMany
capacity:
storage: 10Pi
mountOptions:
- nolock,noresvport,noacl,hard
- vers=3
- rsize=1048576
- wsize=1048576
- proto=tcp
- timeo=600
- retrans=2
nfs:
path: /share
server: <IP Address of mount target for mvn warehouse>
nas-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitlab-runner-cache-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Pi
volumeName: gitlab-runner-cache-pv
mvn-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: gitlab-runner-maven-pvc
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Pi
volumeName: gitlab-runner-maven-pv
4개의 object를 모두 생성합니다.
kubectl apply -f nas-pv.yaml
kubectl apply -f mvn-pv.yaml
kubectl apply -f nas-pvc.yaml
kubectl apply -f mvn-pvc.yaml
네트워크 통신에 문제가 없다면 [ASK cluster 이름 클릭 > Volumes > Persistent Volume Claims]에서 다음과 같이 잘 'bound' 된 것을 확인할 수 있습니다.
자세한 nas pv 생성 가이드는 클릭하여 확인합니다.
아래의 credential은 gitlab의 CI/CD variables로 정의하거나 k8s의 secret/configmap 으로 관리할 수 있습니다. 두가지 모두 방법을 보여드리기 위해 ACR authentication은 secret으로 생성하고, ACR에 최종적으로 저장된 docker image를 ASK에 deploy하기 위한 ASK 접속 정보는 gitlab의 CI/CD variables로 정의해 보겠습니다.
ACR 초기 세팅은 클릭해서 확인합니다.
kubectl create secret docker-registry registry-auth-secret --docker-server=${xxx} --docker-username=${xxx} --docker-password=${xxx}
생성된 secret은 아래 command로 확인하거나 직접 console에 들어가 [Cluster 클릭 > Configuration > Secrets]에서 확인 가능합니다.
kubectl get secret registry-auth-secret --output=yaml
여기서 secret을 쓰게 될 경우 deploy할 새로운 클러스터를 추가할 때마다 helmfile을 update 해줘야 하므로 번거로울 수 있습니다. 따라서 최종적으로 deploy할 kubernetes의 접속 정보를 복사하여 KUBE_CONFIG라고 하는 variable에 저장하여 사용하겠습니다.
base64로 kubeconfig 값을 인코딩하여 variable에 저장했습니다.
cat ~/.kube/config | base64
KUBE_CONFIG라고 하는 variable에 저장합니다.
특정 VK version 이후로 부터는 imageCache가 default로 설치되어 있습니다. imageCache를 사용하면 k8s local cache를 사용함으로써 image를 pulling하는 시간을 감소시켜 pod creation을 훨씬 빠르게 할 수 있습니다. 평균적으로 ~분 단위로 소요되는 시간이 ~초 단위로 줄어듭니다.
apiVersion: eci.alibabacloud.com/v1
kind: ImageCache
metadata:
name: gitlab-runner
spec:
## gitlab runner, CI/CD 등에 사용하는 모든 이미지
images:
- gitlab/gitlab-runner-helper:x86_64-latest
- gitlab/gitlab-runner:alpine-v13.0.0
- registry.cn-hangzhou.aliyuncs.com/eci/kaniko:1.0
- registry.cn-hangzhou.aliyuncs.com/eci/kubectl:1.0
- registry.cn-hangzhou.aliyuncs.com/eci/java-demo:1.0
이번 가이드에서는 아래 gitlab 공식 다큐먼트를 참조하여 예시 helmfile를 작성하였습니다. 예시를 참조하여 실제 환경에 맞게 config를 구성하시면 됩니다.
Reference link:
[Menu > Admin > Overview > Runners] 에서 runner를 등록하기 위한 URL과 token을 복사하여 해당 부분에 붙여넣기 합니다.
helmfile.yaml
repositories:
- name: gitlab
url: https://charts.gitlab.io/
releases:
- name: gitlab-runner
namespace: default
chart: gitlab/gitlab-runner
version: 0.33.1
values:
- image: gitlab/gitlab-runner:alpine-v13.0.0 ## alpine base image로 사용
gitlabUrl: <gitlab url> ## gitlab admin console에서 복사
runnerRegistrationToken: "<runner token>" ## gitlab admin console에서 복사
rbac: ## k8s 리소스에 대한 조작 권한 부여
create: true
resources: ["pods", "pods/exec", "secrets", "services"]
verbs: ["get", "list", "watch", "create", "patch", "delete"]
clusterWideAccess: false
metrics:
enabled: false
runners:
## [[runners.kubernetes.volumes.pvc]] section에서 사전에 생성된 nas shared volume이 마운트 될 수 있도록 지정
## [[runners.kubernetes.volumes.secret]] 를 통해 secret를 runner pod에 마운트 (Alibaba Container Registry에 push하기 위한 authentication 정보), Gitlab CICD variable로도 지정 가능 - 선택사항
## 그외 runners에 대한 config.toml 에 세팅할 수 있는 값들은 https://docs.gitlab.com/runner/executors/kubernetes.html#the-available-configtoml-settings 참조
config: |
concurrent = 2
check_interval = 0
[[runners]]
[runners.kubernetes]
namespace = "default"
pull_policy = "if-not-present"
cpu_limit = "0.5"
cpu_request = "0.5"
memory_limit = "1Gi"
memory_request = "1Gi"
helper_cpu_limit = "0.5"
helper_cpu_request = "0.5"
helper_memory_limit = "1Gi"
helper_memory_request = "1Gi"
helper_image = "gitlab/gitlab-runner-helper:x86_64-latest"
[runners.kubernetes.pod_annotations]
"k8s.aliyun.com/eci-image-cache" = "true"
[[runners.kubernetes.volumes.pvc]]
name = "gitlab-runner-cache-pvc"
mount_path = "/cache"
readonly = false
[[runners.kubernetes.volumes.pvc]]
name = "gitlab-runner-maven-pvc"
mount_path = "/root/.m2"
readonly = false
[[runners.kubernetes.volumes.secret]]
name = "registry-auth-secret"
mount_path = "/root/.docker"
read_only = false
[runners.kubernetes.volumes.secret.items]
".dockerconfigjson" = "config.json"
아래 명령어는 $HOME/.kube/config 파일을 참조하여 API로 연결된 k8s cluster에 gitlab runner와 runners(kubernetes executor) 구성 설정을 deploy 합니다. value 설정 변경이 필요할 때마다 이 helmfile 하나만 수정 후 다시 apply하면 자동으로 변경사항만 골라내어 적용되기 때문에 간편합니다.
helmfile apply
Code repository: https://github.com/rnlduaeo/GitlabOnASK/tree/main/java-demo-oct
.gitlab-ci.yml
cache:
paths:
- /cache
stages:
- package
- build
- deploy
mvn_package_job:
image: registry.cn-hangzhou.aliyuncs.com/eci/kaniko:1.0
stage: package
script:
- mvn clean package -DskipTests
- cp -f target/demo.war /cache #nas volume(/cache에 마운트)에 war artifact를 저장합니다.
build_and_publish_docker_image_job:
image: registry.cn-hangzhou.aliyuncs.com/eci/kaniko:1.0
stage: build
script:
- mkdir target
- cp /cache/demo.war target/demo.war
- echo $CI_PIPELINE_ID
- kaniko -f `pwd`/Dockerfile -c `pwd` --destination=gitlab-registry.ap-northeast-1.cr.aliyuncs.com/project1/ gitlab-repository:$CI_PIPELINE_ID # 전단계(mvn_package_job)에서 저장한 artifact 기반으로 docket image를 build하고(kaniko build를 사용) ACR에 push합니다.
deploy_k8s_job:
image: registry.cn-hangzhou.aliyuncs.com/eci/kubectl:1.0
stage: deploy
script:
- mkdir -p ~/.kube
- echo $KUBE_CONFIG | base64 -d > ~/.kube/config # gitlab CI/CD variables에 저장된 KUBE_CONFIG 를 사용하여 target server에 deploy합니다.
- sed -i "s/IMAGE_TAG/$CI_PIPELINE_ID/g" deployment.yaml
- cat deployment.yaml
- kubectl apply -f deployment.yaml
runner pod를 describe 해보면 아래와 같이 build와 helper containers로 구성된 pod임을 알 수 있으며 기타 기 정의된 gitlab CI/CD variables와 구성한 volumes 등을 확인할 수 있습니다. 위의 helmfile에서 config.toml로 구성한 runners의 secret과 기타 volume등은 여기서 확인 가능합니다.
$ kubectl describe pod runner-d1uxg4p4-project-9-concurrent-0vfwm2
Name: runner-d1uxg4p4-project-9-concurrent-0vfwm2
Namespace: default
Priority: 0
Node: virtual-kubelet-ap-northeast-1b/172.29.206.209
Start Time: Tue, 19 Oct 2021 23:33:34 +0900
Labels: pod=runner-d1uxg4p4-project-9-concurrent-0
.......
Containers:
build:
Container ID: containerd://f5059875921e668a5bdf2c7fc4844592d2572bcd0668d2d6c52f5054e2d0836b
Image: registry.cn-hangzhou.aliyuncs.com/eci/kaniko:1.0
........
helper:
Container ID: containerd://d85794bb358b0c3b525da1786f9fff094279db1944ebd7a19d0f7bbacbe0255a
Image: gitlab/gitlab-runner-helper:x86_64-latest
......
Volumes:
registry-auth-secret:
Type: Secret (a volume populated by a Secret)
SecretName: registry-auth-secret
Optional: false
gitlab-runner-cache-pvc:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: gitlab-runner-cache-pvc
ReadOnly: false
gitlab-runner-maven-pvc:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: gitlab-runner-maven-pvc
ReadOnly: false
repo:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: <unset>
default-token-mncmc:
Type: Secret (a volume populated by a Secret)
SecretName: default-token-mncmc
Optional: false
QoS Class: Guaranteed
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute
node.kubernetes.io/unreachable:NoExecute
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal SuccessfulHitImageCache 18s EciService [eci.imagecache]Successfully hit image cache imc-6weel91oaxj0az23sz6z, eci will be scheduled with this image cache.
Normal Pulled 7s kubelet, eci Container image "registry.cn-hangzhou.aliyuncs.com/eci/kaniko:1.0" already present on machine
Normal Created 7s kubelet, eci Created container build
Normal Started 7s kubelet, eci Started container build
Normal Pulled 7s kubelet, eci Container image "gitlab/gitlab-runner-helper:x86_64-latest" already present on machine
Normal Created 7s kubelet, eci Created container helper
Normal Started 7s kubelet, eci Started container helper
script의 경우 일정 시간 sleep을 걸어두고 직접 pod에 들어가여 (default container가 build 이므로 build container로 접속하게 됩니다.) 스크립트를 작성하거나 문제 발생 시 트러블 슈팅합니다.
kubectl exec --stdin --tty <runner-pod-name> -- /bin/bash
문의사항은 알리바바 클라우드 코리아 SA 팀으로 문의 주시기 바랍니다.
JJ Lim - December 31, 2021
James Lee - October 11, 2023
JJ Lim - September 23, 2021
Regional Content Hub - April 15, 2024
JJ Lim - November 1, 2021
Regional Content Hub - August 12, 2024
An enterprise-level continuous delivery tool.
Learn MoreAccelerate software development and delivery by integrating DevOps with the cloud
Learn MoreAlibaba Cloud Container Service for Kubernetes is a fully managed cloud container management service that supports native Kubernetes and integrates with other Alibaba Cloud products.
Learn MoreFully managed, locally deployed Alibaba Cloud infrastructure and services with consistent user experience and management APIs with Alibaba Cloud public cloud.
Learn More