定西市建设网站费用全渠道营销的概念
因为项目需要,在GKE的集群上需要创建一个CICD的环境,记录一下安装部署一个分布式Jenkins集群的过程。
分布式Jenkins由一个主服务器和多个Agent组成,Agent可以执行主服务器分派的任务。如下图所示:
如上图,Jenkins Agent可以运行不同的操作系统,执行主服务器分派的编译打包或测试等任务。
在Jenkins的官网上介绍了在K8S上安装的几种方式,包括了Helm, operator等。但是我直接用Helm最新版的Jenkins安装始终遇到问题,日志报错信息是Google的OAUTH插件编译有问题,如果用旧版本的可以安装成功,但是我不想用旧的版本,而新版本的问题暂时没找到解决的思路,另外Helm安装的values配置过于繁杂。我考虑用最传统的Yaml manifest的方式来部署这个Jenkins。
创建Jenkin主服务器
首先是创建namespace的manifest,配置如下:
apiVersion: v1
kind: Namespace
metadata:name: "jenkins"labels:name: "jenkins"
定义Storage class的manifest
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: storage-jenkins
provisioner: kubernetes.io/gce-pd
parameters:type: pd-standardfstype: ext4
volumeBindingMode: WaitForFirstConsumer
定义PVC,保存Jenkins的相关数据
kind: PersistentVolumeClaim
apiVersion: v1
metadata:name: jenkins-pvc namespace: jenkins
spec:accessModes:- ReadWriteOncestorageClassName: "storage-jenkins" resources:requests:storage: 30Gi
然后就是创建一个deployment,部署Jenkins pod
apiVersion: apps/v1
kind: Deployment
metadata:name: jenkinsnamespace: jenkins
spec:selector:matchLabels:app: jenkinsreplicas: 1template:metadata:labels:app: jenkinsspec:volumes:- name: pvc-jenkins persistentVolumeClaim:claimName: jenkins-pvc containers:- name: jenkinsimage: "jenkins/jenkins:lts"securityContext:runAsUser: 0volumeMounts:- mountPath: "/var/jenkins_home/"name: pvc-jenkins ports:- containerPort: 8080- containerPort: 50000 resources:limits:cpu: "2"memory: "4Gi"requests:cpu: "1"memory: "2Gi"livenessProbe:httpGet:path: "/login"port: 8080initialDelaySeconds: 90periodSeconds: 10timeoutSeconds: 5failureThreshold: 5readinessProbe:httpGet:path: "/login"port: 8080initialDelaySeconds: 60periodSeconds: 10timeoutSeconds: 5failureThreshold: 3
创建一个service来暴露Jenkins的地址和端口,注意这里加了GCP的neg注解,使得可以使用ingress来暴露
apiVersion: v1
kind: Service
metadata:name: jenkins-svcnamespace: jenkinsannotations:cloud.google.com/neg: '{"ingress": true}'labels:app: jenkins
spec:ports:- name: jenkinsportport: 8080targetPort: 8080- name: slaveconnectorsport: 50000targetPort: 50000selector:app: jenkins
再创建一个ingress,使得可以从公网访问
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: jenkins-ingressnamespace: jenkins
spec:rules:- http:paths:- path: /*pathType: ImplementationSpecificbackend:service:name: jenkins-svcport:number: 8080
最后创建一个kustomization.yaml,把以上部分组合起来
resources:- namespace.yaml- storage-class.yaml- pvc.yaml- deployment.yaml- service.yaml- ingress.yaml
然后运行命令kubectl apply -k kustomization_dir/即可部署。等待ingress创建完成后,在gcp console的网页上找到相应的地址,我们还需要在Firewall里面增加一条规则,放开ingress相应的端口,然后在浏览器上访问即可进入jenkins的配置页面。
初次访问配置页面,需要输入admin password,这个可以从Jenkins pod的日志查到。
kubectl logs <jenkins_pod_name> -n jenkins
之后我们可以安装建议的插件,然后创建一个Jenkins的用户
安装Jenkins动态Agent
有了主服务器之后,我们就可以配置动态Agent了。以下是Jenkins创建动态Agent的流程图
- 当触发Jenkins任务的时候,Jenkins kubernetes插件会给K8S服务器发起一个API调用,创建一个Agent pod.
- 这个Jenkins agent pod会运行在K8S上来运行Jenkins任务,当任务完成时会自动中止。
- 当Jenkins agent pod启动时,会读取环境变量配置并和Jenkins主服务器通过JNLP的方式沟通。
安装Jenkins kubernetes插件
在Jenkins的Manage Jenkins页面,选择Plugin,搜索Kubernetes并安装。安装完成后重启Jenkins
1. 连接Kubernetes
在Jenkins的Manage Jenkins->Clouds->New Cloud,类型选择Kubernetes.
然后在配置项里面,Kubernetes url和Kubernetes server certificate key都不用输入,因为我们的Jenkins master server是在同一个Kubernetes里面。Namespace输入jenkins。点击Test Connection,如果正常就会显示连接成功。
2. 配置Jenkins URL
输入Jenkins service的internal DNS的名称,即http://jenkins-svc.jenkins.svc.cluster.local:8080
3. 创建Pod和Container template
Pod label key设置为jenkins,value设置为slave。Pod template name输入jenkins-slave,namespace输入Jenkins,labels输入slave。Containers里面先不加container template,Jenkins默认是用Jenkins/inbound-agent这个镜像
然后需要Add volume detail,因为我们的Jenkins agent是docker in docker,所以需要选择Host path volume,然后把kubernetes节点的docker daemon socket mount到Jenkins agent pod. 在Host path和mount path分别输入/var/run/docker.sock
4. 配置Service Account
因为Jenkins agent pod需要权限来调用GCP的服务,例如推送image到repository等。需要创建一个service account来给agent使用。
首先在K8S的jenkins namespace创建一个service account
kubectl create serviceaccount jenkins-sa --namespace jenkins
在GCP IAM里面创建一个service account
gcloud iam service-accounts create jenkins-sa --project=PROJECT_ID
给这个service account赋予需要的role,可以用以下命令
gcloud projects add-iam-policy-binding PROJECT_ID --member "serviceAccount:jenkins-sa@PROJECT_ID.iam.gserviceaccount.com" --role "ROLE_NAME"
把这两个service account绑定起来,使得K8S的service account可以模仿GCP的service account
gcloud iam service-accounts add-iam-policy-binding jenkins-
sa@PROJECT_ID.iam.gserviceaccount.com --role roles/iam.workloadIdentityUser --member
"serviceAccount:PROJECT_ID.svc.id.goog[jenkins/jenkins-sa]"
Annotate K8S的service account,加上email地址
kubectl annotate serviceaccount jenkins-sa --namespace jenkins
iam.gke.io/gcp-service-account=jenkins-sa@PROJECT_ID-
v1.iam.gserviceaccount.com
最后还要把Run as user id设为0,使得jenkins agent pod以root用户来运行,以避免权限问题。最后Save即可完成配置。
运行Jenkins CICD pipeline
现在可以构建一个CICD pipeline了。下图是构建CICD的一个流程图
这里面包括了几个步骤
- 往Git仓库提交代码,通过hook触发jenkins任务
- Jenkins启动agent pod,运行打包任务
- 把打包的镜像推送到registry
- 更新部署的manifest,更新镜像的标签
- 把应用部署到GKE
我这里将扩展一下这些步骤,增加Jenkin和Github webhook的集成,使得用户提交PR的时候自动触发UT检查,然后当用户执行merge操作的时候来触发打包任务。因为篇幅所限,我将在下一篇博客中记录具体的操作过程。