[Kubernetes - CI/CD] Customized Jenkins 제작과 활용 - 1/2
Kubernetes 를 활용하여 CI/CD를 구현하는 방법은 여러 가지가 있다. 이번 시리즈는 커스텀 Jenkins 이미지를 사용한 컨테이너 Application 빌드 배포 자동화를 구현해 보고자 한다. 본 글은 그 첫 번 째로, 커스텀 Jenkins 이미지를 제작, 테스트 빌드를 통해 CI/CD가 정상적으로 작동하는지를 확인하는 내용이다.
Prerequisites
- Kubernetes 1.8~1.9.x 가 설치되고 정상 작동 할 것
- Application 소스 저장소로 사용할 github 계정 준비
- Persistent Volume으로 사용할 Storage는 Heketi-API로 연동된 Glusterfs(http://bryan.wiki/286 참조)
- Jenkins 빌드 작업을 위한 네임스페이스틑 ns-jenkins 로 설정하여 사용(별도 지정하지 않으면 default 네임스페이스 사용)
- 작업 디렉터리는 $HOME/jenkins-custom-k8s-cicd 로 하고, 하위에 각 상황과 용도에 맞는 서브 디렉터리를 만들어 사용
Jenkins 기본 이미지로 Jenkins Pod/Service 기동
[root@kubemaster ~]# kubectl create namespace ns-jenkins
[root@kubemaster ~]# mkdir 00-jenkins-custom-image && cd 00-jenkins-custom-image
[root@kubemaster 00-jenkins-custom-image]# vi 00-jenkins-sa-clusteradmin-rbac.yaml
---
apiVersion: v1
kind: ServiceAccount
metadata:
namespace: ns-jenkins
name: jenkins
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: cluster-admin-clusterrolebinding
subjects:
- kind: ServiceAccount
name: jenkins
namespace: ns-jenkins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: cluster-admin-clusterrolebinding-2
subjects:
- kind: ServiceAccount
name: default
namespace: ns-jenkins
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: cluster-admin
[root@kubemaster 00-jenkins-custom-image]# vi 01-jenkins-leader-pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: jenkins-leader-pvc
annotations:
volume.beta.kubernetes.io/storage-class: glusterfs-storage
labels:
app: jenkins-leader
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 500Mi
[root@kubemaster 00-jenkins-custom-image]# vi 02-jenkins-dep-svc.yaml
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: jenkins-leader
spec:
replicas: 1
template:
metadata:
labels:
app: jenkins-leader
spec:
serviceAccountName: jenkins
securityContext:
# Jenkins uid:gid=1000:1000
fsGroup: 1000
containers:
- name: jenkins-leader
image: jenkins
volumeMounts:
- name: jenkins-home
mountPath: /var/jenkins_home
ports:
- containerPort: 8080
- containerPort: 50000
volumes:
- name: jenkins-home
persistentVolumeClaim:
claimName: jenkins-leader-pvc
---
apiVersion: v1
kind: Service
metadata:
name: jenkins-leader-svc
labels:
app: jenkins-leader
spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
protocol: TCP
name: http
nodePort: 30500
- port: 50000
protocol: TCP
name: slave
nodePort: 30501
selector:
app: jenkins-leader
[root@kubemaster 00-jenkins-custom-image]# kubectl create -f 00-jenkins-sa-clusteradmin-rbac.yaml
serviceaccount "jenkins" created
clusterrolebinding "cluster-admin-clusterrolebinding" created
[root@kubemaster 00-jenkins-custom-image]# kubectl create -f 00-jenkins-leader-pvc.yaml -n ns-jenkins
persistentvolumeclaim "jenkins-leader-pvc" created
[root@kubemaster 00-jenkins-custom-image]# kubectl create -f 01-jenkins-dep-svc.yaml -n ns-jenkins
deployment "jenkins-leader" created
service "jenkins-leader-svc" created
[root@kubemaster 00-jenkins-custom-image]# kubectl get pods -n ns-jenkins
NAME READY STATUS RESTARTS AGE
jenkins-leader-75869666cc-9kq6q 1/1 Running 0 7m
[root@kubemaster 00-jenkins-custom-image]# kubectl logs -f -n ns-jenkins jenkins-leader-75869666cc-9kq6q
Running from: /usr/share/jenkins/jenkins.war
webroot: EnvVars.masterEnvVars.get("JENKINS_HOME")
...
*************************************************************
Jenkins initial setup is required. An admin user has been created and a password generated.
Please use the following password to proceed to installation:
1541910096194795a11f9b2342b24b8d
This may also be found at: /var/jenkins_home/secrets/initialAdminPassword
*************************************************************
...
Mar 20, 2018 8:35:10 AM hudson.model.AsyncPeriodicWork$1 run
INFO: Finished Download metadata. 60,791 ms
...
* RBAC 설정에서는 각종 권한 처리를 단순하게 하기 위해 ns-jenkins 네임스페이스의 ServiceAccount인 jenkins와 default cluster-admin 권한을 부여한다. 실제 서비스 환경에서는 좀더 세밀한 권한 관리에 신경을 써둘 필요가 생길 수도 있다.
* 네임스페이스를 따로 지정하지 않으면 모든 jenkins 요소들이 default 네임스페이스에 생성된다
* 설치용 임시암호를 복사해 둔다
* [Tip] 해당 Jenkins-leader container 의 초기 암호 값을 바로 알아 내려면 아래 명령을 실행해도 된다
# kubectl exec -it -n ns-jenkins `kubectl get pods -n ns-jenkins --selector=app=jenkins-leader --output=jsonpath={.items..metadata.name}` -- cat /root/.jenkins/secrets/initialAdminPassword
Jenkins 플러그인 및 커스텀 설정
- 앞 단계에서 Kubernetes 서비스를 NodePort 로 expose 한 결과를 확인
[root@kubemaster 00-jenkins-custom-image]# kubectl get svc -n ns-jenkins
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
glusterfs-dynamic-jenkins-leader-pvc ClusterIP 10.139.137.127 <none> 1/TCP 3d
jenkins-leader-svc NodePort 10.141.255.17 <none> 80:30500/TCP,50000:30501/TCP 3d
- Jenkins-leader 서비스를 실행한 yaml 파일에서 정의한 대로, 웹브라우저로 Minion(Worker Node) IP의 30500 포트로 접속하여, 앞 단계에서 확인한 설치용 임시 암호를 입력한 후 최초 설정 진행
http://10.255.10.171:30050 으로 접속, 설치용 임시 암호 입력(편의상 다음에 나타나는 관리자용 계정/암호는 admin/admin 으로 설정)
Install suggested plugins 를 선택하여 기본 설치 플러그인을 설치
자동으로 기본 Plugin 들이 설치되며, 이후에 Jenkins 관리자 계정과 암호를 등록하고 완료(Start using Jenkins)하면 Jenkins 관리 Console 로 접속
Manage Jenkins > Configure Global Security 선택
TCP port for JNLP - Fixed 50000, Prevent Cross Site ... - 체크 해제, Apply & Save
Kubernetes Plugin 설치
Manage Jenkins > Manage Plugins 선택
Available 탭 선택
Cloud Providers 항목으로 스크롤(검색), kubernetes 체크 - Install without restart 클릭 - 설치 후 Go back to top page 선택
- Kubernetes 빌드를 위한 plugin 설정
Manage Jenkins > Configure System 선택
# of executors 항목의 값을 0 으로 설정
페이지 맨 끝, Cloud 항목으로 이동, Add a new cloud 선택 > kubernetes 선택
이번 단계에서는 아래 각 항목에 대해 시스템 구성에 맞게 정확히 입력/설정한다
Name: kubernetes
Kubernetes URL: https://kubernetes.default.svc.cluster.local
Disable https certificate check: Yes
Kubernetes Namespace: ns-jenkins(Jenkins 서비스를 기동시키는 네임스페이스명)
Jenkins URL: http://jenkins-leader-svc.ns-jenkins.svc.cluster.local
Jenkins tunnel: jenkins-leader-svc.ns-jenkins.svc.cluster.local:50000
각 항목 입력 후 Credentials: Add - Jenkins 클릭
Credentials 팝업창, Kind: kubernetes service account 선택 > Add 클릭
Credentials: 좌측 None 클릭 > 새로 만들어진 Secret text 선택 > Test Connection > Save 클릭
- Kubernetes 빌드 테스트
좌측 메인 메뉴의 New Item 클릭 > Item name 입력 > Pipeline 클릭 > 하단 OK 클릭
아래로 스크롤, Script 입력 창에 아래 스크립트 내용 Copy & Paste > Save
podTemplate(label: 'pod-golang',
containers: [
containerTemplate(
name: 'golang',
image: 'golang',
ttyEnabled: true,
command: 'cat'
)
]
) {
node ('pod-golang') {
stage 'Switch to Utility Container'
container('golang') {
sh ("go version")
}
}
}
메인 메뉴 Build Now 클릭 > Build History 의 Build 아이템 우측 역삼각형(▼) 클릭 > Console Output 클릭
빌드 과정 및 결과 확인(실제로 빌드 작업이 수행되는 jenkins-slave pod는 ns-jenkins 네임스페이스에서 실행)
빌드가 수행되는 동안 ns-jenkins 네임스페이스의 pod 목록 변화를 확인
최종적으로 Jenkins를 통한 Build test를 위한 golang pod의 lifecycle은 다음 명령어로 확인 가능
커스텀 설정된 Jenkins 빌더 이미지 저장
- 기본 및 kubernetes 플러그인의 설치/설정이 완료된 Jenkins 빌더(leader) 컨테이너의 설정 정보를 추출하고 불필요한 파일을 삭제
- 최종 완성된 컨테이너를 Customized Jenkins Builder Docker 이미지로 저장, docker.io 레지스트리에 Push
import jenkins.model.*
Jenkins.instance.setNumExecutors(0)
USER root
RUN apt-get update -y
USER ${user}
COPY config.xml /usr/share/jenkins/ref/config.xml
COPY executors.groovy /usr/share/jenkins/ref/init.groovy.d/executors.groovy
COPY jobs /usr/share/jenkins/ref/jobs
COPY secrets /usr/share/jenkins/ref/secrets
COPY users /usr/share/jenkins/ref/users
COPY plugins.txt /usr/share/jenkins/plugins.txt
# Workaround for 'Lockfile creation - File not found' error
RUN xargs /usr/local/bin/install-plugins.sh < /usr/share/jenkins/plugins.txt
RUN echo 2.0 > /usr/share/jenkins/ref/jenkins.install.UpgradeWizard.state
ENTRYPOINT ["/bin/tini", "--", "/usr/local/bin/jenkins.sh"]
- Barracuda -