이번 글에서는 helm
을 이용하여 jenkins
를 kubernetes
클러스터에 배포해 볼 것이다.
Jenkins
는 Master/Slave
구조로 구성해 볼 것인데 간단하게 Master
는 Job
을 구성하는 역할을 하게 되고 Slave
는 실제 Job
을 실행시키는 역할을 하게 된다. Job
이 빌드될 때 Slave Pod
가 생성되어 스크립트를 실행시키고 Job
이 완료되면 자동으로 삭제 된다.
이 글에서는 EKS 클러스터를 사용하고 있다. EKS 클러스터 구성에 대한 자세한 내용은 EKS 클러스터 구성 글 또는 AWS 문서를 참고하기 바란다.
Jenkins
를 Helm
으로 배포하면 해당 Pod
가 삭제되었을 때도 자동으로 Pod
가 다시 생성되지만 이전 Pod
안에 있던 내용은 모두 사라지게 된다. Pod
가 삭제되었다 다시 생성되어도 내용이 남아있을 수 있도록 Pod
의 Life Cycle
과는 완전히 독립적인 Persistent Volume
이 필요하다.
이 글에서는 EBS
를 Persistent Volume
으로 사용할 예정이다. EBS CSI Driver
를 클러스터에 배포하여 EBS
를 Persistent Volume
으로 사용할 수 있도록 세팅해보자.
이 부분은 AWS 문서에 잘 나와있기 때문에 해당 문서를 보고 세팅을 완료해도 무관하다. 해당 글도 AWS 문서와 과정이 동일하다.
✓ AWS 문서를 보고 세팅을 완료했다면 바로 Jenkins 배포 단계로 건너뛰기
먼저 ServiceAccount
에 필요한 권한을 정의한 IAM 정책
을 생성한다.
정책 내용 : example-iam-policy.json
다음은 IAM 역할을 만들어 앞에서 만든 정책을 연결해 준다.
역할 만들기에서 웹 ID
를 선택하고 자격 증명 공급자
를 선택해 준다. (자격 증명 공급자 생성은 이 글을 참고)
앞에서 만든 정책을 연결해 준다.
역할 생성 완료 후 해당 역할에 대한 신뢰 관계 편집
이 필요하다.
수정 부분 : aud": "sts.amazonaws.com"
→ sub": "system:serviceaccount:kube-system:ebs-csi-controller-sa"
권한 세팅이 끝났다. 이제 EBS CSI Driver
를 배포해보자.
다음 명령어를 이용하여 Git Clone
을 실행한다.
git clone https://github.com/kubernetes-sigs/aws-ebs-csi-driver.git
복제된 프로젝트의 aws-ebs-csi-driver/deploy/kubernetes/base
폴더에 있는 serviceaccount-csi-controller.yaml
파일에 annotations
를 추가하여 우리가 생성한 역할의 권한을 ServiceAccount
가 사용할 수 있도록 해준다.
---
# Source: aws-ebs-csi-driver/templates/serviceaccount-csi-controller.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: ebs-csi-controller-sa
labels:
app.kubernetes.io/name: aws-ebs-csi-driver
#Enable if EKS IAM for SA is used
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::900184409169:role/L23724-AmazonEKS_EBS_CSI_DriverRole
수정이 완료되면 다음 명령어로 EBS CSI Driver
를 클러스터에 배포해준다.
$ kubectl apply -k aws-ebs-csi-driver/deploy/kubernetes/base
이제 세팅이 잘 되었는지 확인해 보자.
Clone
했던 프로젝트의 aws-ebs-csi-driver/examples/kubernetes/dynamic-provisioning/
폴더에 간단한 샘플이 있다.
다음 명령어로 테스트 Pod
를 생성해 본다.
$ kubectl apply -f specs/
PVC
가 생성되면서 자동으로 PV
도 생성된 것을 볼 수 있다.
$ kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE
ebs-claim Bound pvc-9f9ffc56-165a-4e47-bf51-07d6c13b252d 4Gi RWO ebs-sc 40s
$ kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS REASON AGE
pvc-9f9ffc56-165a-4e47-bf51-07d6c13b252d 4Gi RWO Delete Bound default/ebs-claim ebs-sc 40s
specs/claim.yaml
에서 정의한 PVC
에 의해서 4GiB
가 할당된 볼륨이 자동으로 생성되어 있다.
테스트 Pod
에 접속해 본다.
$ kubectl exec -it po/app sh
아래와 같이 볼륨이 마운트 된 것을 볼 수 있다.
# lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
nvme0n1 259:0 0 20G 0 disk
|-nvme0n1p1 259:1 0 20G 0 part /etc/hosts
`-nvme0n1p128 259:2 0 1M 0 part
nvme1n1 259:3 0 4G 0 disk /data
test
파일을 생성해 보자.
# cd data
# ls
lost+found out.txt
# touch test
# ls
lost+found out.txt test
이제 Pod
에서 나와 Pod
를 삭제하고 새로운 Pod
를 생성한다.
새로 생성한 Pod
로 다시 들어가서 test
파일이 있는지 확인한다.
# cd data
# ls
lost+found out.txt test
이전 Pod
에서 만든 test
파일이 잘 남아 있다면 세팅은 끝났다.
드디어 Jenkins
를 배포 해보자.
이 부분도 Jenkins 문서를 참고하였기 때문에 Jenkins 문서만 보고 따라해도 충분히 가능하다.
다만, 문서에서는minikube
를 기준으로 설명하고 있다.
jenkins
네임스페이스를 생성한다.
$ kubectl create namespace jenkins
다음 yaml
파일을 apply
하여 provisioner=ebs.csi.aws.com
인 StorageClass
를 생성해주고 해당 StorageClass
를 사용하는 PersistentVolumeClaim
도 생성해준다.
# jenkins-pvc.yml
kind: StorageClass
apiVersion: storage.k8s.io/v1
metadata:
name: jenkins-sc
provisioner: ebs.csi.aws.com
volumeBindingMode: WaitForFirstConsumer
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-pvc
namespace: jenkins
spec:
accessModes:
- ReadWriteOnce
storageClassName: jenkins-sc
resources:
requests:
storage: 30Gi
생성한 SC
와 PVC
의 이름을 기억해 둔다.
Jenkins Pod
가 API 서버
와 상호작용할 수 있도록 jenkins
라는 ServiceAccount
를 생성한다.
이 부분은 Jenkins 문서에서 제공하는 jenkins-sa.yaml을 그대로 사용했다.
jenkins-sa.yaml
을 적용하여 ClusterRole
, ClusterRoleBinding
및 ServiceAccount
를 생성해준다.
Jenkins 문서에서 제공하는 values.yaml 템플릿이 있다. 이 템플릿을 먼저 jenkins-values.yml
로 저장하고 수정하도록 하겠다.
다음은 수정한 부분이다.
✓ controller.servicePort
servicePort: 80
✓ controller.serviceType
LoadBalancer
부분을 잘 모르겠다면 [EKS] ⑥ Service, Ingress 이용해서 네트워크에 노출하기를 참고바란다.
serviceType: LoadBalancer
# Jenkins controller service annotations
serviceAnnotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
service.beta.kubernetes.io/aws-load-balancer-subnets: subnet-0d8d50653b7935fee,subnet-088cab1b1e8d2966c
✓ persistence.exixtingClaim & persistence.storageClass
앞에서 생성한 StorageClass
와 PersistentVolumeClaim
이름을 넣어준다.
existingClaim: jenkins-pvc
storageClass: jenkins-sc
✓ persistence.serviceAccount
jenkins
라는 SA
를 이미 만들었기 때문에 create=false
로 수정하고 name=jenkins
serviceAccount:
create: false
# The name of the service account is autogenerated by default
name: jenkins
annotations: {}
imagePullSecretName:
jenkins
레파지토리를 추가한다.
$ helm repo add jenkinsci https://charts.jenkins.io
$ helm repo update
jenkins-values.yml
에 정의한 내용으로 클러스터에 Jenkins
를 배포한다.
$ helm install jenkins -n jenkins -f jenkins-values.yml jenkinsci/jenkins
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/aylee/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/aylee/.kube/config
NAME: jenkins
LAST DEPLOYED: Thu May 20 07:58:06 2021
NAMESPACE: jenkins
STATUS: deployed
REVISION: 1
NOTES:
1. Get your 'admin' user password by running:
kubectl exec --namespace jenkins -it svc/jenkins -c jenkins -- /bin/cat /run/secrets/chart-admin-password && echo
2. Get the Jenkins URL to visit by running these commands in the same shell:
echo http://127.0.0.1:8080
kubectl --namespace jenkins port-forward svc/jenkins 8080:8080
3. Login with the password from step 1 and the username: admin
4. Configure security realm and authorization strategy
5. Use Jenkins Configuration as Code by specifying configScripts in your values.yaml file, see documentation: http:///configuration-as-code and examples: https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos
For more information on running Jenkins on Kubernetes, visit:
https://cloud.google.com/solutions/jenkins-on-container-engine
For more information about Jenkins Configuration as Code, visit:
https://jenkins.io/projects/jcasc/
NOTE: Consider using a custom image with pre-installed plugins
AWS 콘솔
에서 LoadBalancer
를 보면 jenkins-values.yml
에서 정의한대로 NLB
가 생성되어있다.
볼륨
또한 자동으로 생성되어 마운트
되었다.
NLB
주소로 접속하면 Jenkins
가 성공적으로 배포된 것을 볼 수 있다.
사용자 이름은 admin
이고 비밀번호는 아래 명령어를 통해 얻을 수 있다.
$ kubectl exec --namespace jenkins -it svc/jenkins -c jenkins -- /bin/cat /run/secrets/chart-admin-password && echo
GVh3G7H9uJ8tTh6mNZxKul
Jenkins
콘솔에 접속해서 Job
을 생성한 후 Build
를 실행해 본다.
Master Pod
를 삭제한다.
$ kubectl delete po/jenkins-0 -n jenkins
pod "jenkins-0" deleted
자동으로 Master Pod
가 다시 생성되고
$ kubectl get po -n jenkins
NAME READY STATUS RESTARTS AGE
jenkins-0 0/2 PodInitializing 0 24s
$ kubectl get po -n jenkins
NAME READY STATUS RESTARTS AGE
jenkins-0 1/2 Running 0 29s
Jenkins
콘솔로 접속해보면 이전과 동일한 모습인 것을 볼 수 있다.
빌드했던 Job
의 내용은 다음과 같았다.
하지만 Slave Pod
는 볼륨이 마운트되어 있지 않았기 때문에 Job
을 실행할때 생성된 pv-test
파일은 남아있지 않게 된다.
해당 빌드의 Console Output
을 보아도 볼륨이 마운트 되어있지 않은 것을 볼 수 있다.
Slave Pod
가 실행 완료 후 삭제되어도 Slave Pod
에서 실행했던 내용이 남을 수 있도록 Slav Pod
에도 볼륨을 마운트 해보자.
jenkins-values.yml
에서 아래와 같은 내용을 찾아서 수정해준다.
✓ agent.workingDir
workingDir: "/var/jenkins_home"
✓ agent.volumes & agent.workspaceVolume
volumes:
- type: PVC
claimName: jenkins-pvc
mountPath: /var/jenkins_home
readOnly: false
workspaceVolume:
type: PVC
claimName: jenkins-pvc
readOnly: false
수정 완료 후 helm upgrade
명령어를 이용하여 수정된 내용을 적용해 준다.
$ helm upgrade jenkins jenkinsci/jenkins -f jenkins-values.yml -n jenkins
WARNING: Kubernetes configuration file is group-readable. This is insecure. Location: /home/aylee/.kube/config
WARNING: Kubernetes configuration file is world-readable. This is insecure. Location: /home/aylee/.kube/config
Release "jenkins" has been upgraded. Happy Helming!
NAME: jenkins
LAST DEPLOYED: Fri May 21 01:20:13 2021
NAMESPACE: jenkins
STATUS: deployed
REVISION: 3
NOTES:
1. Get your 'admin' user password by running:
kubectl exec --namespace jenkins -it svc/jenkins -c jenkins -- /bin/cat /run/secrets/chart-admin-password && echo
2. Get the Jenkins URL to visit by running these commands in the same shell:
NOTE: It may take a few minutes for the LoadBalancer IP to be available.
You can watch the status of by running 'kubectl get svc --namespace jenkins -w jenkins'
export SERVICE_IP=$(kubectl get svc --namespace jenkins jenkins --template "{{ range (index .status.loadBalancer.ingress 0) }}{{ . }}{{ end }}")
echo http://$SERVICE_IP:80/login
3. Login with the password from step 1 and the username: admin
4. Configure security realm and authorization strategy
5. Use Jenkins Configuration as Code by specifying configScripts in your values.yaml file, see documentation: http:///configuration-as-code and examples: https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos
For more information on running Jenkins on Kubernetes, visit:
https://cloud.google.com/solutions/jenkins-on-container-engine
For more information about Jenkins Configuration as Code, visit:
https://jenkins.io/projects/jcasc/
NOTE: Consider using a custom image with pre-installed plugins
다시 Job
을 빌드 하고 Console Output
을 보면 workspace
경로가 변경되었고 볼륨이 마운트 되어있다.
Master
로 들어가서 workspace
경로로 이동해보면 Job
을 빌드할 때 생성된 파일이 유지되어 있는 것을 볼 수 있다.
$ kubectl exec --namespace jenkins -it svc/jenkins -c jenkins -- /bin/sh
# -- Master에 접속 --
$ cd /var/jenkins_home/workspace/pv-test
$ cat pv-test
Jenkins Kubernetes PV TEST
정말 많은 도움이 되었습니다! 감사합니다 :)