Jenkins 基于 Kubernetes 的动态和静态节点
Agent 节点
虽然我们上面提到了动态节点的好处,但是还是会有一部分人比较喜欢坚持静态节点的方式,选择静态或者动态的 Jenkins Agent 节点都是可以的。接下来我们就分别来介绍下如何在 Kubernetes 集群中为 Jenkins 提供动静态 Agent 节点。
静态节点
首先在 Jenkins 页面 http://jenkins.k8s.local/computer/new 新建一个节点:
点击创建后配置节点信息,然后点击保存:
保存后我们可以看到节点已经创建成功了:
然后点击列表中的 agent1
名称,进入节点详情页面,在详情页面我们将获取到运行该节点的一些密钥信息,
然后创建一个如下所示的资源清单文件:
# jenkins-agent.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: jenkins-agent
namespace: kube-ops
spec:
selector:
matchLabels:
app: jenkins-agent
template:
metadata:
labels:
app: jenkins-agent
spec:
containers:
- name: agent
image: jenkins/inbound-agent
securityContext:
privileged: true
imagePullPolicy: IfNotPresent
env:
- name: JENKINS_URL
value: http://jenkins.k8s.local
- name: JENKINS_SECRET
value: 9c4c5159b111083705eed5802ceb021cfad002a18dd59c692aa59a9616e6285a
- name: JENKINS_AGENT_NAME
value: agent1
- name: JENKINS_AGENT_WORKDIR
value: /home/jenkins/workspace
上面的清单文件中的 JENKINS_URL
、JENKINS_SECRET
和 JENKINS_AGENT_WORKDIR
这些环境变量的值就是上面我们在节点详情页面获取到的信息,然后我们将这个文件应用到集群中:
$ kubectl apply -f jenkins-agent.yaml
创建后正常该 agent 的 Pod 会启动报错,错误日志如下所示:
INFO: Locating server among [http://jenkins.k8s.local/]
Sep 07, 2023 7:55:51 AM hudson.remoting.jnlp.Main$CuiListener error
SEVERE: Failed to connect to http://jenkins.k8s.local/tcpSlaveAgentListener/: jenkins.k8s.local
java.io.IOException: Failed to connect to http://jenkins.k8s.local/tcpSlaveAgentListener/: jenkins.k8s.local
at org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver.resolve(JnlpAgentEndpointResolver.java:216)
at hudson.remoting.Engine.innerRun(Engine.java:760)
at hudson.remoting.Engine.run(Engine.java:543)
Caused by: java.net.UnknownHostException: jenkins.k8s.local
at java.base/java.net.AbstractPlainSocketImpl.connect(Unknown Source)
at java.base/java.net.Socket.connect(Unknown Source)
at java.base/sun.net.NetworkClient.doConnect(Unknown Source)
at java.base/sun.net.www.http.HttpClient.openServer(Unknown Source)
at java.base/sun.net.www.http.HttpClient.openServer(Unknown Source)
at java.base/sun.net.www.http.HttpClient.<init>(Unknown Source)
at java.base/sun.net.www.http.HttpClient.New(Unknown Source)
at java.base/sun.net.www.http.HttpClient.New(Unknown Source)
at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source)
at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source)
at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source)
at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source)
at org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver.resolve(JnlpAgentEndpointResolver.java:213)
... 2 more
这其实是因为我们配置的 jenkins.k8s.local
域名是一个自定义的域名,需要在 K8s 集群中解析的话,我们还需要在 CoreDNS 中去添加一条 hosts 映射:
$ kubectl edit cm coredns -n kube-system
# Please edit the object below. Lines beginning with a '#' will be ignored,
# and an empty file will abort the edit. If an error occurs while saving this file will be
# reopened with the relevant failures.
#
apiVersion: v1
data:
Corefile: |
.:53 {
errors
health {
lameduck 5s
}
hosts {
10.206.16.10 jenkins.k8s.local
fallthrough
}
# ......
kind: ConfigMap
但其实还有更简单的方式,那就是直接将 JENKINS_URL
值替换为 Jenkins 的 Service 地址 http://jenkins.kube-ops.svc.cluster.local:8080
即可,这样就不需要在 CoreDNS 中添加 hosts 映射了。
正常现在的 Jenkins Agent Pod 应该是正常运行的,我们可以通过下面的命令查看:
$ kubectl get pods -n kube-ops -l app=jenkins-agent
NAME READY STATUS RESTARTS AGE
jenkins-agent-76884cd44c-dd9ds 1/1 Running 0 2m32s
再次查看节点列表,我们可以看到节点已经在线了:
接下来我们再创建一个 Pipeline 类型的作业,然后在 Pipeline 脚本中添加下面的内容:
这里我们定义的流水线脚本中,我们使用到了 agent
关键字,这个关键字的作用就是指定这个流水线的运行环境,这里我们指定的是 build
这个标签,也就是我们上面创建的 agent1 这个节点,这样这个流水线就会在这个节点上运行。
点击保存后,我们可以点击立即构建来执行这个流水线,然后我们可以查看这个流水线的执行结果:
这样我们就可以在 Jenkins 中使用这个静态节点来构建任务了。
动态节点
除了静态节点之外,我们还可以使用动态节点的方式来构建任务,这样可以更好的利用资源,我们这里使用的是 Kubernetes 的方式来创建动态节点,这样我们就可以在 Jenkins 中使用动态节点来构建任务了。
第 1 步. 首先需要安装 kubernetes 插件。
第 2 步. 安装完毕后,进入 http://jenkins.k8s.local/manage/cloud/
页面:
在该页面点击 New cloud
新建一个 Cloud 服务:
这里注意一定要选择上 Kubernetes
这个 Type,然后点击 Create
按钮,然后我们就可以看到下面的配置页面了:
首先配置连接 Kubernetes APIServer 的地址,由于我们的 Jenkins 运行在 Kubernetes 集群中,所以可以使用 Service 的 DNS 形式进行连接 https://kubernetes.default.svc.cluster.local
:
命名空间这里填 kube-ops
,然后点击 连接测试
,如果出现 Connected to Kubernetes v1.26.2
这样的提示信息证明 Jenkins 已经可以和 Kubernetes 系统正常通信了。
然后下方的 Jenkins URL 地址为 http://jenkins.kube-ops.svc.cluster.local:8080
,根据上面创建的 jenkins 的服务名填写,包括下面的 Jenkins 通道,默认是 50000 端口(要注意是 TCP,所以不要填写 http):
然后点击最后的 save
按钮保存配置。到这里我们的 Kubernetes 插件就算配置完成了。
测试
Kubernetes 插件的配置工作完成了,接下来我们就来添加一个 Job 任务,看是否能够在 Slave Pod 中执行,任务执行完成后看 Pod 是否会被销毁。
在 Jenkins 首页点击 新建任务
,创建一个测试的任务,同样还是选择 Pipeline
类型的任务,这次我们需要使用的流水线脚本就更复杂了,如下所示:
pipeline{
agent{
kubernetes{
label "test01"
cloud 'Kubernetes'
yaml '''
---
kind: Pod
apiVersion: v1
metadata:
labels:
k8s-app: jenkins-agent
name: jenkins-agent
namespace: kube-ops
spec:
containers:
- name: jenkinsagent
image: jenkins/inbound-agent
imagePullPolicy: IfNotPresent
'''
}
}
stages{
stage("Hello"){
steps{
script{
echo "Hello Slave Pod on Kubernetes!"
}
}
}
}
}
这次的脚本中定义的执行 agent 就比较复杂了,通过一个 kubernetes
属性来指定这个流水线的运行环境,然后通过 yaml
属性来定义这个运行 Pod 的清单文件,这里我们定义的是一个简单的 Pod,然后我们将这个 Pod 部署到 kube-ops
这个命名空间中,这样我们就可以在这个 Pod 中运行我们的 Jenkins Slave 了,需要注意 cloud
后面的值需要和前面我们定义的 Cloud 服务名称一致。
最后点击保存,同样我们可以点击左侧的 立即构建
来执行这个任务,然后我们可以查看这个任务的执行结果:
虽然我们在这里的脚本中定义的 Pod 非常简单,但可以看到 Jenkins 会帮我们配置一些默认的环境变量。当任务执行的过程中我们也可以观察 Kubernetes 集群中的 Pod 变化:
$ kubectl get pods -n kube-ops -w
NAME READY STATUS RESTARTS AGE
jenkins-55c4676f4d-fhmw2 1/1 Running 3 (12m ago) 91m
jenkins-agent-76884cd44c-dd9ds 1/1 Running 0 22m
test01-jnzmb-ht0n7 0/1 Pending 0 0s
test01-jnzmb-ht0n7 0/1 Pending 0 0s
test01-jnzmb-ht0n7 0/1 ContainerCreating 0 0s
test01-jnzmb-ht0n7 1/1 Running 0 1s
test01-jnzmb-ht0n7 1/1 Terminating 0 3s
test01-jnzmb-ht0n7 0/1 Terminating 0 4s
test01-jnzmb-ht0n7 0/1 Terminating 0 4s
test01-jnzmb-ht0n7 0/1 Terminating 0 4s
我们可以看到在我们点击立刻构建的时候可以看到一个新的 Pod:test01-jnzmb-ht0n7
被创建了,这就是我们的 Jenkins Slave。当任务构建完抽这个 Slave Pod 也会自动删除。
到这里我们就完成了使用 Kubernetes 动态生成 Jenkins Slave 的方法。
文章转载自公众号:k8s技术圈