[EKS] Helm으로 Jenkins 배포 - Master/Slave 구조 with Persistent Volume(EBS)

이아영·2021년 5월 21일
2

EKS

목록 보기
7/7

이번 글에서는 helm을 이용하여 jenkinskubernetes 클러스터에 배포해 볼 것이다.

JenkinsMaster/Slave 구조로 구성해 볼 것인데 간단하게 MasterJob을 구성하는 역할을 하게 되고 Slave는 실제 Job을 실행시키는 역할을 하게 된다. Job이 빌드될 때 Slave Pod가 생성되어 스크립트를 실행시키고 Job이 완료되면 자동으로 삭제 된다.

Prerequisite

✓ EKS 클러스터가 구성되어 있어야 한다.

이 글에서는 EKS 클러스터를 사용하고 있다. EKS 클러스터 구성에 대한 자세한 내용은 EKS 클러스터 구성 글 또는 AWS 문서를 참고하기 바란다.

AWS EBS for Persistent Volume

JenkinsHelm으로 배포하면 해당 Pod가 삭제되었을 때도 자동으로 Pod가 다시 생성되지만 이전 Pod 안에 있던 내용은 모두 사라지게 된다. Pod가 삭제되었다 다시 생성되어도 내용이 남아있을 수 있도록 PodLife Cycle과는 완전히 독립적인 Persistent Volume이 필요하다.

이 글에서는 EBSPersistent Volume으로 사용할 예정이다. EBS CSI Driver를 클러스터에 배포하여 EBSPersistent Volume으로 사용할 수 있도록 세팅해보자.

이 부분은 AWS 문서에 잘 나와있기 때문에 해당 문서를 보고 세팅을 완료해도 무관하다. 해당 글도 AWS 문서와 과정이 동일하다.

AWS 문서를 보고 세팅을 완료했다면 바로 Jenkins 배포 단계로 건너뛰기


Amazon EBS CSI Driver를 Amazon EKS 클러스터에 배포

먼저 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

EBS CSI Driver 테스트

이제 세팅이 잘 되었는지 확인해 보자.

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를 배포 해보자.


Helm으로 Jenkins 배포

이 부분도 Jenkins 문서를 참고하였기 때문에 Jenkins 문서만 보고 따라해도 충분히 가능하다.
다만, 문서에서는 minikube를 기준으로 설명하고 있다.

Jenkins Namespace

jenkins 네임스페이스를 생성한다.

$ kubectl create namespace jenkins

StorageClass & PersistentVolumeClaim

다음 yaml 파일을 apply하여 provisioner=ebs.csi.aws.comStorageClass를 생성해주고 해당 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

생성한 SCPVC의 이름을 기억해 둔다.

ServiceAccount

Jenkins PodAPI 서버와 상호작용할 수 있도록 jenkins라는 ServiceAccount를 생성한다.

이 부분은 Jenkins 문서에서 제공하는 jenkins-sa.yaml을 그대로 사용했다.

jenkins-sa.yaml을 적용하여 ClusterRole, ClusterRoleBindingServiceAccount를 생성해준다.

values.yaml 정의

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

앞에서 생성한 StorageClassPersistentVolumeClaim이름을 넣어준다.

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 배포

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

Master Pod 볼륨 마운트 테스트

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가 실행 완료 후 삭제되어도 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

👏👏👏

3개의 댓글

comment-user-thumbnail
2022년 2월 23일

정말 많은 도움이 되었습니다! 감사합니다 :)

답글 달기
comment-user-thumbnail
2024년 1월 13일

근데 helm으로 설치하는 방식이 각 개인 글마다 다 다른데...그 기준은 뭘까요?
어떤 매뉴얼을 참조로 만드나요?
어떻게 방식을 알게되는거죠? ㅠㅠ

답글 달기
comment-user-thumbnail
2024년 10월 28일

도움되는 글 잘 보았네요. 감사합니다.

답글 달기