Jenkins CI 插件:Kubernetes
Jenkins CI 插件:Kubernetes
Jenkins Kubernetes 插件实现了 当 Jenkins Master 接受到 Build 请求时,会根据配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master 上,当运行完 Job 后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态。
一.Jenkins 安装插件:Kubernetes
Jenkins 选择-插件管理,搜索并安装 Kubernetes 插件
二.Kubernetes 插件配置:Configure Clouds
安装后添加和配置 Kubernetes Cloud 作为 Jenkins 配置的一部分,依次进入:
Manage Jenkins -> Manage Nodes and Clouds -> Configure Clouds
2.1 Configure Clouds: Configure Kubernetes Clouds
- 配置情况一:本地集群
本地集群,即 Jenkins Master 托管在同一个 Kubernetes 集群上,那么只需要为本地集群提供 Kubernetes apiserver 的机器内部域名地址,如:https://kubernetes:6443
其他配置参考下图:
- 配置情况二:远程集群
远程集群是指 Jenkins Master 位于 Kubernetes 集群外部,一般为自建的 Jenkins 服务。
我们先来填写下图红色标识部分的信息:
名称:任意添加
- Kubernetes 地址:kube-apiserver 地址和端口
- Kubernetes 命名空间:default 或者自定义的名称空间
- Jenkins 地址:外部独立的 Jenkins Server 地址
接下来配置蓝色标识部分信息:
- Kubernetes 服务证书 key:获取 cluster-admin 管理员用户 kubeconfig 文件 /root/.kube/config 下的 certificate-authority-data 字段的内容转换成 base64 编码的结果填入
1 | $ echo 'LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JS78978UR2akNDQXFhZ0F3SUJBZ0lVVnVjRWJ1dkZQVTlGUldUQ3Fqb3c1VW02YWtrd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1pURUxNQWtHQTFVRUJoTUNRMDR4RURBT0JnTlZCQWdUQjBKbGFXcHBibWN4RURB112T0JnTlZCQWNUQjBKbAphV3BwYm1jwNVpoaVRXaSt3N0hSRnFvM3hjT2MwWldjSlRMbCtIUloxcjdGdjYKSXVzUG9rVDBPN2lMaHNrRE11UWZqR21SWGVwd2haUUZOMUZkcG5iSmhhUUlNZFVUakZpWThvT21JMWUxVTd78EUAo5ekU9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K' | base64 -d |
- 凭据:凭据类型选择 “Secret file”,cluster-admin 管理员用户 kubeconfig 文件内容保存为 kubeconfig 附件上传。 /root/.kube/config
- 最后 K8S集群连接测试:出现 Kubernetes 集群版本号表示 Jenkins 连接外部 Kubernetes 集群成功!
- 报错证书错误,看k8s大于2.3 会有这个错误, 低版本的不会。
1 | Error testing connection https://XXX:6443: javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target |
解决禁用HTTPS证书检查
2.2 Configure Clouds: Pod Templates
接下来开始配置 Pod Templates
2.2.1 Pod Template 区块配置:
- 名称:jnlp
- 命名空间:这里我使用 jenkins
- 标签列表:jnlp-slave, 这个标签非常重要,其作用是在 Jenkins Pipeline 指定 agent 完成 Job 时用到
- 用法:选择 “尽可能使用这个节点”
- 运行命令:对于Windows容器,powershell是一个很好的默认值。sleep,正常是设置为空,比较少用window容器
- 命令参数:对于Windows容器,参数Start Sleep 999999是搭配powershell的合理选择。
2.2.2 Container Template:区块配置:
- 名称:必须为 jnlp
- Docker 镜像:指定临时 Pod 使用的镜像,默认会使用 jenkins/inbound-agent 作为镜像来完成。
配置私有镜像仓库
我们在Jenkins上配置了镜像地址,但这个地址必须是要公开的。否则我们拉去不到,那怎么样才可以设置私有仓库呢?
在高级下面有配置秘钥
这边配置的秘钥,是k8s 启动 jnlp pod 起来之后在里面pull拉群镜像的秘钥。
这个是秘钥不是 Jenkins 的连接镜像仓库的凭据, 而是k8s里面配置的秘钥,我们这边只需要在相应的namespace
1 | $kubectl create secret docker-registry <secret name> --namespace=jenkins --docker-server=<your-registry-server> --docker-username=<your-name> --docker-password=<your-pword> --docker-email=<your-email> |
或者 kubesphere 上操作
2.2.3 工作目录:根据你实际使用的 docker镜像来写,或者保持默认
挂载卷:把二进制的 docker 命令和 .sock 文件挂载进 JOB POD,使得其能够在Pod内完成 docker镜像的构建。
1.23版本之前是docker 之后是 containerd
三.授权 docker 二进制命令权限
因为我们 Jenkins JOB 分发到 k8s-node 主要用于 docker image build 构建业务镜像,所以我们需要把二进制命令 /usr/bin/docker 与 /var/run/docker.sock 挂载进 Pod 供其能够使用。
相同的,如果需要构建 nodejs, Java, go 等需要先构建再发布的项目,思路也是如此:把构建过程要到的命令挂载进 Pod
3.1 授权方式1
Jenkins Agent 默认会以 jenkins 1000:1000 这个用户随机在某个 k8s-node 节点上创建临时 Pod 来完成 JOB的构建。
如果需要把宿主机的二进制命令 /usr/bin/docker 挂载进 Pod 来执行构建 docker image build,那么需要对二进制的 docker 命令和 /var/run/docker.sock 文件授权 1000:1000 用户的权限。
1 | # 所有k8s-node执行 |
3.2 授权方式2
直接为宿主机的 docker systemd脚本配置 ExecStartPost
1 | $ vim /etc/systemd/system/docker.service |
….此处省略
重启 dockerd 后验证
1 | $ ll /var/run/docker.sock |
3.3 授权方式3(推荐)
这个功能在新版本的Jenkins 才会有,旧版本推荐2的方式(具体版本没去查,使用的时候注意下就好)
Jenkins 在配置k8s集群中 - Pod Template - Pod Template details - Container Template - 高级中,运行权限设置成root 使用命令 id root
查看root 用户的id gid 填入进去
四、k8s 运行空间
创建Kubernetes Namespace与Service Account
4.1 创建Kubernetes Namespace jenkins
在Kubenates的上创建jenkins命名空间,用于Jenkins使用
1 | kubectl create namespace jenkins |
4.2 创建Service Account (可选)
这个是需要在Jenkins 命名空间上调用部署其他业务空间的业务时用到,如果我们只是调用k8s 进行编译,部署k8s 还是Jenkins master 上就不需要这个权限。(没去验证)
在Kubernetes上为Jenkins构建创建有Cluster Admin权限的Service Account jenkins:
1 | kubectl create clusterrolebinding jenkins --clusterrole cluster-admin --serviceaccount=jenkins:jenkins |
五、Jenkins Pipeline
Jenkins On Kubernetes—Jenkins上Kubernetes Plugin的使用
我们给 node 添加了一个 jnlp-slave Label 标签,指定这个 Pipeline 的 4个 stage 步骤都使用这个 Label 标签下的 Pod 来构建。
1 | node('jnlp-slave') { |
验证
1 | # Job 被下发给 dev-k8s-node1 节点 |
至此 Kubernetes + Jenkins 的 CI 阶段完成,我们可以在 Pipeline 脚本编写各式各样的构建任务,如拉取git代码、docker build、npm build 等等。
CI阶段的完成目标主要是:构建业务镜像 docker image push 推到到私有镜像仓库。
六.后记避坑
6.1 Q1.构建用的基础镜像
一开始使用的是 jenkins/inbound-agent:latest,才 360多M ,里面的工具命令很全能够满足基本的构建场景。jenkins/inbound-agent:latest 没有curl wget rsync,并且容器默认用户为 root
坚持镜像越小,效率越高,太重的东西可以安装在k8s node节点上,挂载进去使用。
根据这个镜像作为基础镜像,从新生成一个自己的镜像,用于以后业务,自己也根据自己的业务去制作这个镜像
具体的制作过程,看另外一篇文档jenkins-jnlp-镜像制作
6.2 Q2.二进制挂载构建过程需要使用的工具
我的业务镜像构建过程会用到 Python3,Nodejs,docker,kubectl,因此这些工具建议使用二进制部署于每一个 Node的宿主机,再采用 Host Path 的方式挂载进构建 Pod
注意配置 Pod 的环境变量,以供 Pod 内的 Container 能够正常使用二进制命令。不能使用$PATH 美元符号,需要填写完整 PATH
6.3 Q3.构建 Pod 的名称空间变更为 default
上文构建的名称空间开始为 jenkins,而在实际CD发布到 K8S上后发现,APP被部署到远端K8S的 jenkins 名称空间下了。而后改回 default 名称空间正常。
由于我这边并没有在yaml定义名称空间的字段,那如果在 yaml 内定义,可能可以指定名称空间发布。(猜测没有实践过)
上面也说到了如果我们发布不用k8s node 来部署业务,这个问题就不存在。
6.4 Q4 Jenkins 构建报错无法执行下去
排错进入容器
1 | kubectl exec -it jnlp-2562q -- /bin/bash |
java 版本不对,我们拉的镜像java 8的 但我们Jenkins 需要java 11
一开始用的镜像是 jenkins/inbound-agent,自己原先有做过修改的,所以里面java版本不对是java8 ,后面修改成 jenkins/inbound-agent:latest 从新做镜像。
6.5 Q5 java.lang.NoSuchMethodError: No such DSL method ‘withKubeConfig’ found among steps [approveReceivedEvent,
测试发布,调研k8s cli插件部署业务的时候报错
看另外一篇文章 安装 Kubernetes CLI