kubernetes(32):持续集成(1)-k8s集群中搭建Jenkins
k8s集群中搭建Jenkins
https://www.qikqiak.com/k8s-book/docs/36.Jenkins%20Slave.html
https://help.aliyun.com/document_detail/106712.html?spm=a2c4g.11186623.6.813.626e4330lQtSFi
基于Kubernete的CI/CD,可以使用的工具有很多,比如Jenkins、Gitlab CI已经新兴的drone之类的,我还是最为熟悉的Jenkins来做CI/CD的工具。
Jenkins简介jenkins(一):持续集成和Jenkins简介
1 K8s集群安装jenkins
1.1 新建命名空间
添加创建一个 namespace:
kubectl create namespace kube-ops
1.2 创建pv-pvc
我们将容器的 /var/jenkins_home 目录挂载到了一个名为 opspvc 的 PVC 对象上面,所以我们同样还得提前创建一个对应的 PVC 对象
kubernetes(13):k8s数据持久化-pv和pvc—NFS实现
mkdir /data/k8s chown -R 1000 /data/k8s #后面有坑,Jenkins权限问题,提前执行
apiVersion: v1 kind: PersistentVolume metadata: name: opspv spec: capacity: storage: 20Gi accessModes: - ReadWriteMany persistentVolumeReclaimPolicy: Delete nfs: server: 10.6.76.25 path: /data/k8s --- kind: PersistentVolumeClaim apiVersion: v1 metadata: name: opspvc namespace: kube-ops spec: accessModes: - ReadWriteMany resources: requests: storage: 20Gi
[root@k8s-master ops]# kubectl apply -f pvc.yaml persistentvolume/opspv created persistentvolumeclaim/opspvc created [root@k8s-master ops]# [root@k8s-master ops]# kubectl get pvc -n kube-ops NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE opspvc Bound opspv 20Gi RWX 60s [root@k8s-master ops]# kubectl get pv | grep ops opspv 20Gi RWX Delete Bound kube-ops/opspvc 73s [root@k8s-master ops]# kubectl get pvc -n kube-ops NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS AGE opspvc Bound opspv 20Gi RWX 75s [root@k8s-master ops]#
1.3 创建ServiceAccount配置权限
需要使用到一个拥有相关权限的 serviceAccount:jenkins,这里只是给 jenkins 赋予了一些必要的权限,给这个 sa 绑定一个 cluster-admin 的集群角色权限也是可以的,当然这样具有一定的安全风险:(rbac.yaml)
apiVersion: v1 kind: ServiceAccount metadata: name: jenkins2 namespace: kube-ops --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1beta1 metadata: name: jenkins2 rules: - apiGroups: ["extensions", "apps"] resources: ["deployments"] verbs: ["create", "delete", "get", "list", "watch", "patch", "update"] - apiGroups: [""] resources: ["services"] verbs: ["create", "delete", "get", "list", "watch", "patch", "update"] - apiGroups: [""] resources: ["pods"] verbs: ["create","delete","get","list","patch","update","watch"] - apiGroups: [""] resources: ["pods/exec"] verbs: ["create","delete","get","list","patch","update","watch"] - apiGroups: [""] resources: ["pods/log"] verbs: ["get","list","watch"] - apiGroups: [""] resources: ["secrets"] verbs: ["get"] --- apiVersion: rbac.authorization.k8s.io/v1beta1 kind: ClusterRoleBinding metadata: name: jenkins2 namespace: kube-ops roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: jenkins2 subjects: - kind: ServiceAccount name: jenkins2 namespace: kube-ops
[root@k8s-master ops]# kubectl apply -f rbac.yaml serviceaccount/jenkins2 created clusterrole.rbac.authorization.k8s.io/jenkins2 created clusterrolebinding.rbac.authorization.k8s.io/jenkins2 created
1.4 创建Jenkins
通过 NodePort 的形式来暴露 Jenkins 的 web 服务,固定为30002端口,另外还需要暴露一个 agent 的端口,这个端口主要是用于 Jenkins 的 master 和 slave 之间通信使用的。
# cat jenkins2.yaml --- apiVersion: extensions/v1beta1 kind: Deployment metadata: name: jenkins2 namespace: kube-ops spec: template: metadata: labels: app: jenkins2 spec: terminationGracePeriodSeconds: 10 serviceAccount: jenkins2 containers: - name: jenkins image: jenkins/jenkins:lts imagePullPolicy: IfNotPresent ports: - containerPort: 8080 name: web protocol: TCP - containerPort: 50000 name: agent protocol: TCP resources: limits: cpu: 1000m memory: 1Gi requests: cpu: 500m memory: 512Mi livenessProbe: httpGet: path: /login port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 failureThreshold: 12 readinessProbe: httpGet: path: /login port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 failureThreshold: 12 volumeMounts: - name: jenkinshome subPath: jenkins2 mountPath: /var/jenkins_home securityContext: fsGroup: 1000 volumes: - name: jenkinshome persistentVolumeClaim: claimName: opspvc --- apiVersion: v1 kind: Service metadata: name: jenkins2 namespace: kube-ops labels: app: jenkins2 spec: selector: app: jenkins2 type: NodePort ports: - name: web port: 8080 targetPort: web nodePort: 30002 - name: agent port: 50000 targetPort: agent
[root@k8s-master ops]# kubectl apply -f jenkins2.yaml deployment.extensions/jenkins2 created service/jenkins2 created [root@k8s-master ops]#
创建完成后,要去拉取镜像可能需要等待一会儿,然后我们查看下 Pod 的状态
[root@k8s-master ops]# kubectl -n kube-ops get pod NAME READY STATUS RESTARTS AGE jenkins2-59764f8f65-rcvh5 0/1 CrashLoopBackOff 4 4m20s [root@k8s-master ops]# [root@k8s-master ops]# kubectl -n kube-ops describe pod jenkins2-59764f8f65-rcvh5 Liveness: http-get http://:8080/login delay=60s timeout=5s period=10s #success=1 #failure=12 Readiness: http-get http://:8080/login delay=60s timeout=5s period=10s #success=1 #failure=12 Environment: <none> Mounts: /var/jenkins_home from jenkinshome (rw,path="jenkins") /var/run/secrets/kubernetes.io/serviceaccount from jenkins2-token-97q9h (ro)
#查看日志
[root@k8s-master ops]# kubectl -n kube-ops logs jenkins2-59764f8f65-rcvh5 Can not write to /var/jenkins_home/copy_reference_file.log. Wrong volume permissions? touch: cannot touch '/var/jenkins_home/copy_reference_file.log': Permission denied [root@k8s-master ops]#
没有权限在 jenkins 的 home 目录下面创建文件,这是因为默认的镜像使用的是 jenkins 这个用户,而我们通过 PVC 挂载到 nfs 服务器的共享数据目录下面却是 root 用户的,所以没有权限访问该目录,要解决该问题,也很简单,我只需要在 nfs 共享数据目录下面把我们的目录权限重新分配下即可:
chown -R 1000 /data/k8s/jenkins2
#再次执行
[root@k8s-master ops]# kubectl delete -f jenkins2.yaml deployment.extensions "jenkins2" deleted service "jenkins2" deleted [root@k8s-master ops]# kubectl apply -f jenkins2.yaml deployment.extensions/jenkins2 created service/jenkins created [root@k8s-master ops]#
#等待个2分钟
[root@k8s-master ops]# kubectl -n kube-ops get pod NAME READY STATUS RESTARTS AGE jenkins2-59764f8f65-62r5w 1/1 Running 0 3m31s
1.5 密码登陆
任意节点的 IP:30002 端口就可以访问 jenkins 服务
初始化的密码我们可以在 jenkins 的容器的日志中进行查看,也可以直接在 nfs 的共享数据目录中查看:
[root@k8s-master ops]# cat /data/k8s/jenkins/secrets/initialAdminPassword 739b9126bc23434495a96d5d65619e70 [root@k8s-master ops]# [root@k8s-master ops]# kubectl -n kube-ops logs jenkins2-59764f8f65-62r5w ….. 739b9126bc23434495a96d5d65619e70 This may also be found at: /var/jenkins_home/secrets/initialAdminPassword ….
然后选择安装推荐的插件即可
安装完成后添加管理员帐号即可进入到 jenkins 主界面:
2 k8s环境使用Jenkins优点
传统的 Jenkins Slave 一主多从方式会存在一些痛点,比如:
- 主 Master 发生单点故障时,整个流程都不可用了
- 每个 Slave 的配置环境不一样,来完成不同语言的编译打包等操作,但是这些差异化的配置导致管理起来非常不方便,维护起来也是比较费劲
- 资源分配不均衡,有的 Slave 要运行的 job 出现排队等待,而有的 Slave 处于空闲状态
- 资源有浪费,每台 Slave 可能是物理机或者虚拟机,当 Slave 处于空闲状态时,也不会完全释放掉资源。
正因为上面的这些种种痛点,我们渴望一种更高效更可靠的方式来完成这个 CI/CD 流程,而 Docker 虚拟化容器技术能很好的解决这个痛点,又特别是在 Kubernetes 集群环境下面能够更好来解决上面的问题,下图是基于 Kubernetes 搭建 Jenkins 集群的简单示意图:
从图上可以看到 Jenkins Master 和 Jenkins Slave 以 Pod 形式运行在 Kubernetes 集群的 Node 上,Master 运行在其中一个节点,并且将其配置数据存储到一个 Volume 上去,Slave 运行在各个节点上,并且它不是一直处于运行状态,它会按照需求动态的创建并自动删除。
这种方式的工作流程大致为:当 Jenkins Master 接受到 Build 请求时,会根据配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master 上,当运行完 Job 后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态。
那么我们使用这种方式带来了哪些好处呢?
- 服务高可用,当 Jenkins Master 出现故障时,Kubernetes 会自动创建一个新的 Jenkins Master 容器,并且将 Volume 分配给新创建的容器,保证数据不丢失,从而达到集群服务高可用。
- 动态伸缩,合理使用资源,每次运行 Job 时,会自动创建一个 Jenkins Slave,Job 完成后,Slave 自动注销并删除容器,资源自动释放,而且 Kubernetes 会根据每个资源的使用情况,动态分配 Slave 到空闲的节点上创建,降低出现因某节点资源利用率高,还排队等待在该节点的情况。
- 扩展性好,当 Kubernetes 集群的资源严重不足而导致 Job 排队等待时,可以很容易的添加一个 Kubernetes Node 到集群中,从而实现扩展。
是不是以前我们面临的种种问题在 Kubernetes 集群环境下面是不是都没有了啊?看上去非常完美。