Jenkins를 K8s위에서 설치하며 Jenkins에 대해 공부하기 위함
구성 환경 정보
- Jenkins
- Harbor (내부 사설 이미지 저장소)
- Gitlab (내부 소스 저장소)
- ArgoCD (CD용으로 사용)
Jenkins를 설치하여 총 네개의 모듈을 사용하여 CI/CD를 진행할 예정입니다.
Harbor, Gitlab, ArgoCD는 이미 설치되어 Tekton을 사용하여 CI/CD를 진행하고 있는 환경이고 추가적으로 Jenkins를 구축하여 Jenkins 파이프라인을 구성할 계획입니다.
(해당 내용에 대해서는 별도 포스팅에서 구체적으로 다룰 예정입니다.)
kubernetes 환경에 jenkins를 설치하기 위해 아래와 같은 내용이 필요합니다.
구체적으로 알아보겠습니다.
다음 yaml들을 사용해 Jenkins를 설치합니다.
## Jenkins Namespace, ConfigMap, Seceret
apiVersion: v1
kind: Namespace
metadata:
name: jenkins-namespace
---
apiVersion: v1
kind: ConfigMap
metadata:
name: docker-config
namespace: jenkins-namespace
data:
daemon.json: |
{
"insecure-registries": ["harbor.test.ehddhks.com"] ## 공인인증서 적용안된 내부 구축 하버
}
docker-config ConfigMap을 사용하는 이유
내부 사설 이미지 저장소(Harbor)를 사용하기 위해 해당 ConfigMap이 필요합니다.
insecure-registries에 사용할 Harbor 주소를 넣어주면 됩니다.
해당 내용이 없으면 docker command를 통해 build된 image를 해당 저장소에 push 할 때 tls 인증 에러가 발생합니다.
## Jenkins PVC yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: jenkins
name: jenkins-pvc
namespace: jenkins-namespace
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 30Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
labels:
app: jenkins
name: jenkins-log-pvc
namespace: jenkins-namespace
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
## Jenkins Service Yaml
apiVersion: v1
kind: Service
metadata:
name: jenkins-service
namespace: jenkins-namespace
labels:
app: jenkins
spec:
ports:
- port: 80
protocol: TCP
targetPort: 8080
name: http
- port: 443
protocol: TCP
targetPort: 8080
name: https
selector:
app: jenkins
---
apiVersion: v1
kind: Service
metadata:
name: jenkins-service-jnlp
namespace: jenkins-namespace
labels:
app: jenkins
spec:
ports:
- port: 50000
protocol: TCP
targetPort: 50000
name: jnlp
selector:
app: jenkins
## Jenkins Deployment Yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins-deployment
namespace: jenkins-namespace
spec:
progressDeadlineSeconds: 600
replicas: 1
selector:
matchLabels:
app: jenkins
revisionHistoryLimit: 10
template:
metadata:
creationTimestamp: null
labels:
app: jenkins
spec:
containers:
- image: 192.168.178.11:5000/jenkins/jenkins:lts-docker
imagePullPolicy: IfNotPresent
name: jenkins
ports:
- containerPort: 8080
name: http
protocol: TCP
- containerPort: 50000
name: jnlp
protocol: TCP
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/jenkins_home
name: jenkins-vol
- mountPath: /var/run
name: shared
- mountPath: /var/logs
name: jenkins-log
- image: docker:dind
imagePullPolicy: IfNotPresent
name: docker
securityContext:
privileged: true
volumeMounts:
- mountPath: /var/run
name: shared
- mountPath: /etc/docker
name: docker-config
dnsPolicy: ClusterFirst
restartPolicy: Always
schedulerName: default-scheduler
securityContext:
runAsUser: 0
terminationGracePeriodSeconds: 30
volumes:
- name: jenkins-vol
persistentVolumeClaim:
claimName: jenkins-pvc
- hostPath:
path: /usr/share/zoneinfo/Asia/Seoul
type: ""
name: timezone-seoul
- name: jenkins-log
persistentVolumeClaim:
claimName: jenkins-log-pvc
- name: shared
emptyDir: {}
- name: docker-config
configMap:
name: docker-config
Deployment yaml에서의 살펴 볼 부분은 크게 세 개입니다.
이 세 가지는 Jenkins 파이프라인에서 image build를 하기 위해 추가되고 수정 된 부분들입니다.
Jenkins에서는 Image를 build할 때 docker를 사용하여 진행합니다. Docker Plugin을 Jenkins에서 제공하지만 Pod 형태로 뜨는 Jenkins 내부에 Docker Server를 설치하기는 쉽지 않습니다.
따라서 Docker 이미지 중 dind(docker in docker) 이미지를 이용해 docker를 sidecar 형태로 띄워 docker server로 활용하고, Jenkins에서 docker 컨테이너의 docker.sock 파일에 접근할 수 있게 /var/run directory를 두 컨테이너의 Shared Volume으로 설정했습니다.
마지막으로 docker command를 사용하기 위해 기존 jenkins:lts 이미지에 docker command binary 파일을 추가한 Custom Image를 만들었습니다.
Custom Image 만드는 방법
## jenkins container 생성 podman run -it -d --name jenkins --privileged jenkins/jenkins:lts ## docker binary copy podman cp /usr/bin/docker jenkins:/usr/bin/docker ## commit 후 image push podman commit jenkins podman tag d864c93e011ce4760837a4ae22de76e7b0888ac1f451e784a90229091a1c00bc 192.168.178.11:5000/jenkins/jenkins:lts-docker podman push 192.168.178.11:5000/jenkins/jenkins:lts-docker
다음과 같은 과정을 통해 docker command binary가 포함된 jenkins 이미지를 생성하여 local image registry에 push합니다.
yaml들의 배포가 끝나면 Ingress를 통해 외부에 서비스를 노출시킵니다.
Ingress를 통해 접속해보면
잠시 기다리면
다음과 같은 창이 뜹니다.
초기 비밀번호는 jenkins pod의 로그를 보면 확인할 수 있습니다.
$ kubectl logs jenkins-deployment-6c6c8c5896-hjcg5 -c jenkins
2023-09-05 08:04:22.069+0000 [id=59] INFO jenkins.install.SetupWizard#init:
*************************************************************
*************************************************************
*************************************************************
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:
49516f1247a4469b800a8e93eb8c842c <= 초기 비밀번호
This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
*************************************************************
*************************************************************
*************************************************************
2023-09-05 08:04:38.538+0000 [id=59] INFO jenkins.InitReactorRunner$1#onAttained: Completed initialization
2023-09-05 08:04:38.555+0000 [id=32] INFO hudson.lifecycle.Lifecycle#onReady: Jenkins is fully up and running
비밀번호를 사용해 로그인하면
다음과 같은 창이 뜹니다. 필요한 plugin들은 접속 후에 별도로 추가 설치 가능하기 때문에 Install suggested plugins를 눌러 추천된 plugin들만 설치합니다.
Tip
폐쇄망 환경에서는 어떻게 해야할까??
이때는 외부망 통신이 가능한 환경에서 Jenkins를 설치하고 plugin들을 설치합니다. 이후 /var/jenkins_home/plugins 디렉토리 안의 plugin들을 추출하여 폐쇄망 환경에 구성된 jenkins의 동일 directory에 넣어주고 해당 directory를 pvc를 통해 mount 시켜주면 됩니다.
설치가 끝나면 Admin 생성 화면이 나옵니다. 항목들을 작성하여 Admin 계정을 생성합니다.
Jenkins url을 확인하고
Welcome to Jenkins!!!