📄Jenkins pod插件相关
2023-4-21
| 2023-5-17
0  |  0 分钟
type
status
date
slug
summary
tags
category
icon
password
本文的前提是已配置好Jenkins master,关键信息:
  • Jenkins master 使用deployment安装 docker.io/jenkins/jenkins:lts
  • k8s v1.24.3
注意安装的形式和k8s版本,因为会影响后续的配置。如果你一步步按照博主的教程来,可总是出问题,要考虑上述关键信息的差异。
Jenkins master 相关的yaml:
tree . . ├── jenkins-dp.yml ├── jenkins-secret.yaml (可能不需要,和k8s版本有关) ├── pvc.yml ├── service-account.yml └── service.yml
service-account.yml
有的博主apiVersion: rbac.authorization.k8s.io/v1beta1 ,说明他们的k8s版本比较低。具体api版本可以这样查询:
kubectl api-versions |grep rbac rbac.authorization.k8s.io/v1
apiVersion: v1 kind: ServiceAccount metadata: name: jenkins namespace: demo --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: jenkins 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/v1 kind: ClusterRoleBinding metadata: name: jenkins roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: jenkins subjects: - kind: ServiceAccount name: jenkins namespace: demo
jenkins-dp.yml
kind: Deployment apiVersion: apps/v1 metadata: name: jenkins namespace: demo labels: name: jenkins spec: replicas: 1 selector: matchLabels: name: jenkins template: metadata: labels: app: jenkins name: jenkins spec: serviceAccount: jenkins volumes: - name: data persistentVolumeClaim: claimName: jenkins-home - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai containers: - name: jenkins image: jenkins/jenkins:lts imagePullPolicy: Always ports: - containerPort: 8080 name: web protocol: TCP - containerPort: 50000 # agent连接端口 name: agent protocol: TCP resources: limits: cpu: 1000m memory: 2048Mi requests: cpu: 500m memory: 1024Mi livenessProbe: httpGet: path: /login port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 failureThreshold: 5 readinessProbe: httpGet: path: /login port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 failureThreshold: 5 env: - name: JAVA_OPTS value: "-Xms1G -Xmx1G -Duser.timezone=Asia/Shanghai" - name: TRY_UPGRADE_IF_NO_MARKER value: "true" volumeMounts: - name: data mountPath: /var/jenkins_home - name: timezone mountPath: /etc/localtime securityContext: # root权限 runAsUser: 0
pvc.yml
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: jenkins-home namespace: demo spec: accessModes: - ReadWriteMany resources: requests: storage: 1Gi storageClassName: nfs volumeMode: Filesystem --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: jenkins-agent namespace: demo spec: accessModes: - ReadWriteMany resources: requests: storage: 1Gi storageClassName: nfs volumeMode: Filesystem --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: jenkins-m2 namespace: demo spec: accessModes: - ReadWriteMany resources: requests: storage: 1Gi storageClassName: nfs volumeMode: Filesystem
service.yml
kind: Service apiVersion: v1 metadata: name: jenkins namespace: demo spec: ports: - name: web port: 8080 targetPort: 8080 protocol: TCP - name: agent port: 50000 targetPort: 50000 protocol: TCP selector: app: jenkins type: NodePort
jenkins-secret.yaml(可能不需要,和k8s版本有关)
apiVersion: v1 kind: Secret type: kubernetes.io/service-account-token metadata: name: jenkinssecret annotations: kubernetes.io/service-account.name: jenkins #SA的name

1. 替换插件源

非必须,替换后速度更快,清华大学的源https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json

2. 安装Kubernetes plugin插件

别的插件暂且不说,本文主要记录kubernetes插件

2.1 kubernetes插件的菜单位置

正常情况下,此插件的位置是 系统管理 → 系统设置 → 云/cloud,但是我的版本找不到这个菜单。确认插件已安装,Jenkins已重启多次。避免弯路,可以直接在浏览器地址栏输入http://$nodeip:$jenkins-nodeport/configureClouds/ ,直达配置页面。
后续发现了插件的位置😢:系统管理 → 节点列表 → configure clouds

2.2 配置k8s集群信息

2.2.1 集群名称

建议使用默认的kubernetes ,后续pod template会引用。

2.2.2 Kubernetes 地址

和Jenkins master的安装形式相关,如果是传统部署,这里可以写https://nodeip:6443 或者 https://nodehostname:6443 。如果Jenkins是deployment,已经存在于k8s集群里,可以用https://kubernetes.default.svc.cluster.local 或者https://$KUBERNETES_SERVICE_HOST
  • $KUBERNETES_SERVICE_HOST 怎么得到?可以在集群的pod查询env,譬如
kubectl -n demo exec jenkins-776d6f7d88-pbxr4 -- env|grep KUBERNETES_SERVICE_HOST KUBERNETES_SERVICE_HOST=10.96.0.1
  • https://kubernetes.default.svc.cluster.local 怎么来的?目前我只知道和dns相关,暂时不知道怎么来的。

2.2.3 Kubernetes 服务证书 key

此项和下面的凭据强相关,测试下来,不管凭据的形式是secret text还是certificate,这里都要填写。如果为空,测试集群连接可能会报错。如果凭据的形式是secret file,key可以为空。
见节点的/root/.kube/config中的certificate-authority-data部分,并通过base64加密。
echo $certificate-authority-data |base64 -d

2.2.4 凭据

Kubernetes plugin插件中的凭据是给Jenkins master使用的,Jenkins master通过凭据调用k8s api 完成slave pod等操作。Jenkins master是k8s集群中的一个服务,理论上应该通过sa调用k8s api,当然你可以简单粗暴给Jenkins master最高权限的certificate或者secret。
notion image
如图,凭证有多种形式,下面是几个形式的说明:
  • 凭据形式:secret file
secret file可以直接上传kubeconfig文件,kubeconfig可以使用admin权限的$home/.kube/config ,也可以通过自定义权限生成特定的config。
💡
如果使用secret file,Kubernetes 服务证书 key 可以为空,因为kubeconfig是完整的权限文件。
💡
如果Jenkins master是pod形式,由于kubeconfig的集群连接方式通常是https://node-masterip:6443或者https://node-masterhostname:6443,故连接测试可能会报错(比如通过hostname,coredns插件可能无法解析),可以改连接串为集群内连接串:https://kubernetes.default.svc.cluster.local 或者https://$KUBERNETES_SERVICE_HOST ,这里的连接串指的是kubeconfig文件中的server配置项。
一个典型的kubeconfig配置
apiVersion: v1 kind: Config clusters: - cluster: #proxy-url: http://proxy.example.org:3128 server: https://192.168.10.10:6443 name: development users: - name: developer contexts: - context: name: development
  • 凭据形式:certificate
打开~/.kube/config文件
复制certificate-authority-data的内容,运行以下命令生成client.crt # echo "<certificate-authority-data>" | base64 -d > ca.crt 复制client-certificate-data的内容,运行以下命令生成client.crt # echo "<client-certificate-data>" | base64 -d > client.crt 复制client-key-data的内容,运行以下命令生成client.key # echo "<client-key-data>" | base64 -d > client.key 再根据前面步骤生成的ca.crt, client.crt和client.key来生成PKCS12格式的cert.pfx 以下命令运行时,需要输入4位以上的密码 # openssl pkcs12 -export -out cert.pfx -inkey client.key -in client.crt -certfile ca.crt Enter Export Password: Verifying - Enter Export Password:
notion image
  • 凭据形式:secret text,适用于低版本k8s的说明
💡
此token有效期是永久。
下面是例子是常见的绑定cluster-admin 角色得到高权限的token,且因为是低版本k8s,创建sa和权限设置、权限绑定后,集群自动生成secret。
# kubectl create sa jenkins-test serviceaccount/jenkins-test created # kubectl create clusterrolebinding jenkins-test --clusterrole cluster-admin --serviceaccount=default:jenkins-test clusterrolebinding.rbac.authorization.k8s.io/jenkins-test created # 获取token # kubectl describe sa jenkins-test -n default Name: jenkins-test Namespace: default Labels: <none> Annotations: <none> Image pull secrets: <none> Mountable secrets: jenkins-test-token-cklc4 Tokens: jenkins-test-token-cklc4 Events: <none> # kubectl describe secrets jenkins-test-token-cklc4 -n default Name: jenkins-test-token-cklc4 Namespace: default Labels: <none> Annotations: kubernetes.io/service-account.name: jenkins-test kubernetes.io/service-account.uid: 9daba370-3b74-4f6a-903f-f60e3399d741 Type: kubernetes.io/service-account-token Data ==== ca.crt: 1025 bytes namespace: 7 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6InYtS2VkR1VvSUNwVlJjMmdBeXd3VHozZzB0NTByWjJyOE1SUUdvR1h2NlEifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZWZhdWx0Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImplbmtpbnMtdGVzdC10b2tlbi1ja2xjNCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5
notion image
  • 凭据形式:secret text,适用于高版本k8s的说明
Kubernetes 在 v1.22 版本之前自动创建用来访问 Kubernetes API 的长期凭据。 这一较老的机制是基于创建令牌 Secret 对象来实现的,Secret 对象可被挂载到运行中的 Pod 内。 在最近的版本中,包括 Kubernetes v1.27,API 凭据可以直接使用 TokenRequest API 来获得,并使用一个投射卷挂载到 Pod 中。使用此方法获得的令牌具有受限的生命期长度,并且能够在挂载它们的 Pod 被删除时自动被废弃。
你仍然可以通过手动方式来创建服务账号令牌 Secret 对象,例如你需要一个永远不过期的令牌时。 不过,使用 TokenRequest 子资源来获得访问 API 的令牌的做法仍然是推荐的方式。
我们可以简单总结下不同版本的 K8s 集群下面的 ServiceAccount Token 是如何工作的。
  • 1.20(含 1.20)之前的版本,在创建 sa 时会自动创建一个 secret,然后这个会把这个 secret 通过投射卷挂载到 pod 里,该 secret 里面包含的 token 是永久有效的。
  • 1.21~1.23 版本,在创建 sa 时也会自动创建 secret,但是在 pod 里并不会使用 secret 里的 token,而是由 kubelet 到 TokenRequest API 去申请一个 token,该 token 默认有效期为一年,但是 pod 每一个小时会更新一次 token。
  • 1.24 版本及以上,在创建 sa 时不再自动创建 secret 了,只保留由 kubelet 到 TokenRequest API 去申请 token。
当然我们仍然可以手动创建 Secret 来保存 ServiceAccount 令牌,例如在你需要一个永不过期的令牌的时候。一旦手动创建一个 Secret 并将其关联到 ServiceAccount,Kubernetes 控制平面就会自动将令牌填充到该 Secret 中。
本次实验k8s版本是v1.24.3,创建sa后直接kubectl -n demo get secrets查询不到secret,而describe可以看到Jenkins master挂载了/var/run/secrets/kubernetes.io/serviceaccount/token ,拿到jwt.io 解密,发现此token一年后过期。
notion image
kubectl -n demo describe pod jenkins-776d6f7d88-pbxr4 |grep -A4 Mounts: Mounts: /etc/localtime from timezone (rw) /var/jenkins_home from data (rw) /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-sqcpl (ro)
我做了测试,上一步填写base64加密的certificate-authority-data后,凭据填写此token,连接测试成功。但是此token和pod强相关,即/var/run/secrets/kubernetes.io/serviceaccount的token随pod删除而失效,这将非常不利于管理。
怎么生成常规的secret(token)?more
<1>. 通过 TokenRequest API 来获得临时token,为什么说是临时的,因为kubectl create token 是直接创建token,跳过了secret,故创建后kubectl -n demo get secrets 仍查不到secret,且token只会出现在当前TTY窗口。
kubectl create token $sa-name --duration=31536000s //365天,--duration不写的话,默认1小时
notion image
<2>. 手动为 ServiceAccount 创建长期有效的 API 令牌
如果你需要为 ServiceAccount 获得一个 API 令牌,你可以创建一个新的、带有特殊注解 kubernetes.io/service-account.name的 Secret 对象。
kubectl apply -f - <<EOF apiVersion: v1 kind: Secret metadata: name: build-robot-secret //secret的名字 annotations: kubernetes.io/service-account.name: build-robot //sa的名字 type: kubernetes.io/service-account-token EOF
或者先创建一个yaml文件,再create,kubectl create -f jenkins-secret.yaml
cat jenkins-secret.yaml1 apiVersion: v1 kind: Secret type: kubernetes.io/service-account-token metadata: name: jenkinssecret //secret的名字 annotations: kubernetes.io/service-account.name: jenkins //sa的名字
最后查询token kubectl -n demo describe secrets jenkinssecret
Name: jenkinssecret Namespace: demo Labels: <none> Annotations: kubernetes.io/service-account.name: jenkins kubernetes.io/service-account.uid: 3821508c-8647-4319-90e8-192afdd15fd1 Type: kubernetes.io/service-account-token Data ==== ca.crt: 1099 bytes namespace: 4 bytes token: eyJhbGciOiJSUzI1NiIsImtpZCI6Ikdjajl4UWtQZFJFVlE1QmNGR19wTmdFVXY2X0ZxZFZzLU5RNjFfUVVQek0ifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZW1vIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZWNyZXQubmFtZSI6ImplbmtpbnNzZWNyZXQiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiamVua2lucyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50LnVpZCI6IjM4MjE1MDhjLTg2NDctNDMxOS05MGU4LTE5MmFmZGQxNWZkMSIsInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZW1vOmplbmtpbnMifQ.6gzq6_GwsBPVcdbRDdZ_5-fR3OwPbx1So2DP-TRYJURYi-WVqIW84sTQeBgiUaKT1N1K_pg-rkdXPjmVD6YvQ2vjndiK7ybSDsjFqwtlhR_3w1lPVpUWSU_FpwdSw0lUdiB2NswDLqKGuI-gOgjRM14ca9Khl2R2eh6fIKSu0ttM1N8iGAyaoPkr7UuiDHGx8sIDmdu9AD-8540Hyq38dRnr9g8z8_pgAyym9mMII635TYytajzIYLVL_6nHmgAehRJOCEdO-UCEmOiw7_k8iwXel7-uUt3B2fkh4D3l_

2.2.5 Jenkins 地址/通道

本文Jenkins master是pod形式,故地址可以填写http://$jenkins-svc:8080,通道填写http://$jenkins-svc:50000 ,两个端口怎么来的?在deployment定义了:
containers: - name: jenkins image: jenkins/jenkins:lts imagePullPolicy: Always ports: - containerPort: 8080 name: web protocol: TCP - containerPort: 50000 # agent连接端口 name: agent protocol: TCP

3. pod template

kubernetes云插件最后有pod template 配置项,这里是可视化配置。建议留空,用pipeline的方式配置pod template ,便于管理和维护。
notion image

4. Jenkins slave pod的特权secret

💡
在没有kubernetes形态的slave pod之前,要使用秘钥等敏感信息基本靠Credentials插件的withcredentials。现在有了k8s插件,完全可以利用secret挂载这些敏感信息,供slave pod使用。
上面云插件配置的secret/token等都是针对Jenkins master连接k8s集群的,此集群是Jenkins slave pod的承载体。Jenkins slave pod在流水线任务通常需要使用kubectl调用业务集群api,从而创建、更新pod等。kubectl是需要调用kubeconfig的,不然就无权限。kubeconfig可以使用admin权限的$home/.kube/config ,也可以通过自定义权限生成特定的config。无论那种方式拿到了kubeconfig,怎么使用?可以参考下面的方式:
  • kubectl create secret generic 生成secret
# kubectl create secret generic jenkins-k8s-cfg -n demo --from-file=/root/.kube/config secret/jenkins-k8s-cfg created
  • pod template 挂载这个secret secret/jenkins-k8s-cfg
可以用可视化挂载,如下图,也可以在pipeline配置
notion image
volumes: [ persistentVolumeClaim(mountPath: '/root/.m2', claimName: 'jenkins-m2'), persistentVolumeClaim(mountPath: '/home/jenkins/agent/workspace', claimName: 'jenkins-agent'), secretVolume(secretName: 'jenkins-k8s-cfg', mountPath: '/home/jenkins/agent/.kube')] )
secretVolume挂载secret
  • 最后slave pod调用/home/jenkins/agent/.kube/config
stage('更新服务') { container('kubectl') { sh "kubectl --kubeconfig=/home/jenkins/agent/.kube/config apply -f xx.yaml" } }
同理,别的文件可以采用secret方式挂载,便于slave pod引用,比如docker的配置文件.docker/config.json
# docker login -u <用户名> -p <密码> 10.166.33.110 # kubectl create secret generic jenkins-docker-cfg -n demo --from-file=/root/.docker/config.json secret/jenkins-docker-cfg created

5. 参考

技术
  • k8s
  • devops
  • Jenkins pipeline实验Jenkins pod的存储和网络
    目录