devops|03 使用Kubernetes作为工作节点

使用Kubernetes集群作为工作节点 在前面那个简单的pipeline中实际并没有做什么东西,同时它的工作任务也是运行在Jenkins主节点的,现在,给Jenkins添加一个Kubernetes集群作为worker节点,当有任务开始执行的时候,让他在集群中自动创建一个pod,然后再这个pod内完成所有工作,最后任务执行结束,销毁这个pod.
添加Kubernetes作为工作节点 首先安装Kubernetes插件.
系统管理 -> 节点管理
devops|03 使用Kubernetes作为工作节点
文章图片

然后配置集群apiserver等地址
devops|03 使用Kubernetes作为工作节点
文章图片

devops|03 使用Kubernetes作为工作节点
文章图片

devops|03 使用Kubernetes作为工作节点
文章图片

devops|03 使用Kubernetes作为工作节点
文章图片

开始使用 首先先来一个比较简单的操作,比如拉取代码.
首先,Git新建项目,然后上传一个名为cpu.py的文件,内容:

from flask import Flask app = Flask(__name__)@app.route("/") def main(): number = 0 for i in range(0,100000000): number+=i return {"key":number}if __name__ == '__main__': app.run(host='0.0.0.0',port="8000")

然后Jenkins添加对应的密钥,在01文章中给gitlab添加了一个拉取代码的密钥,现在Jenkins也需要添加一下.
查看密钥
cat ~/.ssh/id_rsa

devops|03 使用Kubernetes作为工作节点
文章图片

devops|03 使用Kubernetes作为工作节点
文章图片

devops|03 使用Kubernetes作为工作节点
文章图片

devops|03 使用Kubernetes作为工作节点
文章图片

保存后自动返回,填写git地址等.
devops|03 使用Kubernetes作为工作节点
文章图片

然后点击"生成流水线脚本",替换下面代码中的git branch ....等内容.
实际直接复制新建凭据的ID替换掉,然后修改对应拉取代码的地址即可
podTemplate( cloud: 'kubernetes' ) { node(POD_LABEL) { stage('pull code') { // 克隆代码 git branch: 'main', credentialsId: 'd17b1091-fa2d-4310-8a4d-0b1d7f823ea9', url: 'ssh://git@20.88.9.34:222/my_group/one_project.git' // 打印信息 echo "The first stage end" } } }

手动执行:
devops|03 使用Kubernetes作为工作节点
文章图片

第一步成功.
模拟编译和构建镜像 获取到了代码,接下来就是如何对代码进行编译,然后打包推送仓库的问题了.
Python代码可以用pyinstaller命令将其编译为一个二进制文件,通过二进制文件可以再不同的主机运行并且不需要Python环境
没有找到Jenkins有对应支持pipeline的插件,自己制作一个镜像,里面包含这个命令即可.
FROM python:3.6.15-slim RUN apt-get update -y RUN apt-get install binutils -y RUN /usr/local/bin/python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple pyinstaller ADD start.sh start.sh CMD ["sleep","99d"]

docker build -t 528909316/jenkins:pyinstaller_v1 . # 推送到远程仓库 docker push 528909316/jenkins:pyinstaller_v1

接下来,就是如何让Jenkins的slave节点在这个容器中运行了.
podTemplate( cloud: 'kubernetes', containers: [ // 定义一个pod模板 containerTemplate(name: 'build', image: '528909316/jenkins:pyinstaller_v1', command: "sleep 99d", ttyEnabled: true)] ) { node(POD_LABEL) { // 步骤1: 拉取代码 stage('pull code') { // 克隆代码 git branch: 'main', credentialsId: 'd17b1091-fa2d-4310-8a4d-0b1d7f823ea9', url: 'ssh://git@20.88.9.34:222/my_group/one_project.git' echo "The first stage end" } // 步骤2: 编译代码 stage('build code') { container('build') { stage('Build a python project') { // 编译为文件 sh ''' pyinstaller -F cpu.py ''' } } echo "The second stage end." } } }

如上的代码中定义了一个pod模板.
默认Jenkins会为slave创建一个pod,然后在里面启动一个容器来运行所有的工作任务.pod中多个容器的存储是共享的,所以这里的容器模板实际上是在这个pod中新增了一个容器,这个容器具有 pyinstaller命令.当主容器拉取到代码后,会执行步骤2的编译过程.
继续开始添加下一个过程: 制作镜像推送到远程仓库
同样新增一个具有docker命令的容器,不同的是这个容器如果想使用docker命令需要先启动服务,通过/var/run/docker.sock文件和服务通信完成.容器中没有办法再启动一个docker,但是主机上(Kubernetes容器运行时使用的docker)是有这个文件的,所以将主机文件挂载到pod中,让容器中的程序直接和主机docker进程通信.
涉及知识点: 一切皆文件
制作一个包含docker命令的镜像
FROM alpine:3.14 RUN echo 'http://mirrors.aliyun.com/alpine/v3.8/main/' >/etc/apk/repositories; echo 'http://mirrors.aliyun.com/alpine/v3.8/community/'>>/etc/apk/repositories RUN apk add docker CMD ["sleep","99d"]

执行命令
docker build -t 528909316/jenkins:rundocker_v1 .

推送到仓库,接下来还要再gitlab中也添加一个Dockerfile,来定义制作这个镜像的过程.
名称自然是Dockerfile,内容:
FROM python:3.6.15-slim RUN /usr/local/bin/python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple RUN pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask ADD cpu.py cpu.py CMD ["python","cpu.py"]

继续修改pipeline文件
podTemplate( cloud: 'kubernetes', containers: [ // 容器模板1 : 编译镜像 containerTemplate(name: 'build', image: '528909316/jenkins:pyinstaller_v1', command: "sleep 99d", ttyEnabled: true), // 容器模板2: 制作docker镜像 containerTemplate(name: 'docker', image: '528909316/jenkins:rundocker_v1', command: "sleep 99d", ttyEnabled: true)], // 挂载卷: hostPathVolume为主机目录或文件,将主机的/var/run/docker.sock文件挂载到pod的/var/run/docker.sock volumes: [hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock')] ) { node(POD_LABEL) { stage('pull code') { // 克隆代码 git branch: 'main', credentialsId: 'd17b1091-fa2d-4310-8a4d-0b1d7f823ea9', url: 'ssh://git@20.88.9.34:222/my_group/one_project.git' echo "The first stage end" } stage('build code') { container('build') { stage('Build a python project') { // 编译为文件 sh ''' pyinstaller -F cpu.py ''' } } echo "The second stage end." } // 步骤3 :构建docker镜像 stage("build image") { container("docker") { stage("build image") { // build 镜像 sh "docker build -t 528909316/jenkins:taskcpu_v1 ." // 登陆docker仓库 sh "docker login -u 528909 -p '**********'" // 推送镜像 sh "docker push 528909316/jenkins:taskcpu_v1" } } echo "push end." } } }

部署到k8s 拉取代码,编译,制作镜像都完成了,接下来只剩下一件事情: 部署应用到k8s
在此选择了使用Kubernetes Continuous Deploy组件.
官方地址
注: 不知为何我安装了最新的2.1.2版本发布一直失败.直到我将版本换为了1.0.0才成功,参考文档:Kubernetes Continuous Deploy插件的使用,文章发布于2019-9,里面提到该插件1.0.0版本比较稳定,所以我也就降低了版本.
既然是插件自然使用"流水线语法"了,选择组件填写内容,添加一个kubeconfig类型的文件(内容就是Kubernetes主节点的/etc/kubernetes/admin.conf文件内容),然后重新修改pipeline增加一个流水线步骤:
发布过程中还要用到一个Kubernetes的编排文件,继续在仓库添加一个名为deploy.yaml的文件,内容:
apiVersion: apps/v1 kind: Deployment metadata: name: python-cpu spec: selector: matchLabels: run: python-cpu replicas: 1 template: metadata: labels: run: python-cpu spec: containers: - name: python-cpu image: 528909316/version:run_cpu1 ports: - containerPort: 8000 resources: limits: cpu: 500m requests: cpu: 200m --- apiVersion: v1 kind: Service metadata: name: python-cpu labels: run: python-cpu spec: ports: - port: 8000 selector: run: python-cpu

podTemplate( cloud: 'kubernetes', containers: [ containerTemplate(name: 'jenkins', image: 'jenkins/jnlp-slave:4.9-1-alpine', command: "sleep 99d", ttyEnabled: true), containerTemplate(name: 'build', image: '528909316/jenkins:pyinstaller_v1', command: "sleep 99d", ttyEnabled: true), containerTemplate(name: 'docker', image: '528909316/jenkins:rundocker_v1', command: "sleep 99d", ttyEnabled: true)], volumes: [hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock')] ) { node(POD_LABEL) { stage('pull code') { // 克隆代码 git branch: 'main', credentialsId: 'd17b1091-fa2d-4310-8a4d-0b1d7f823ea9', url: 'ssh://git@20.88.9.34:222/my_group/one_project.git' echo "The first stage end" } stage('build code') { container('build') { stage('Build a python project') { // 编译为文件 sh ''' pyinstaller -F cpu.py ''' } } echo "The second stage end." } stage("build image") { container("docker") { stage("build image") { sh "docker build -t 528909316/jenkins:taskcpu_v1 ." sh "docker login -u 528909 -p '**********'" sh "docker push 528909316/jenkins:taskcpu_v1" } } echo "push end." } stage("deploy image") { kubernetesDeploy configs: 'deploy.yml', kubeConfig: [path: ''], kubeconfigId: 'ae92d8dc-053e-409e-ae1b-f6e3f3bbb9f4', secretName: '', ssh: [sshCredentialsId: '*', sshServer: ''], textCredentials: [certificateAuthorityData: '', clientCertificateData: '', clientKeyData: '', serverUrl: 'https://'] } } }

手动执行,到Kubernetes集群中查看默认名称空间下查看,pod被创建了!
[root@master ~]# kubectl get pod NAMEREADYSTATUSRESTARTSAGE centos-deployment-76dc4d95c9-s2gvf1/1Running010h python-cpu-cc4cfb466-dkrpd1/1Running060s

到了这一步可以确定,流程已经没有问题了,接下来就是继续完善流水线文件了,首先先把发布的deploy.yml文件中的镜像版本改对,然后继续修改对应的dockerfile将打包好的文件加入到容器中,最后运行这个新制作的镜像!
最终的Dockerfile
FROM python:3.6.15-slim ADD dist/cpu cpu CMD ["./cpu"]

最终的Jenkins.groovy(pipeline)
podTemplate( cloud: 'kubernetes', containers: [ //流水线执行过程中的环境 默认会创建一个jenkins-slave容器作为pod的主容器 // 新增一个叫做 build的容器,使用528909316/jenkins:pyinstaller_v1这个镜像,具有命令pyinstaller containerTemplate(name: 'build', image: '528909316/jenkins:pyinstaller_v1', command: "sleep 99d", ttyEnabled: true), // 新增一个叫做 docker 的容器,具有命令 docker containerTemplate(name: 'docker', image: '528909316/jenkins:rundocker_v1', command: "sleep 99d", ttyEnabled: true)], volumes: [hostPathVolume(hostPath: '/var/run/docker.sock', mountPath: '/var/run/docker.sock')] ) { node(POD_LABEL) { stage('pull code') { // 克隆代码 git branch: 'main', credentialsId: 'd17b1091-fa2d-4310-8a4d-0b1d7f823ea9', url: 'ssh://git@20.88.9.34:222/my_group/one_project.git' echo "The first stage end" } stage('build code') { container('build') { stage('Build a python project') { // 编译为文件 首先更新当前编译环境的pip 然后使用清华源安装flask组件(pyinstaller时当前主机中必须有文件中import的所有包,否则会报错) // 最后pyinstaller 制作一个叫做cpu的二进制文件,放在 ./dist/目录下 sh ''' python -m pip install --upgrade pip -i https://pypi.tuna.tsinghua.edu.cn/simple pip install -i https://pypi.tuna.tsinghua.edu.cn/simple flask pyinstaller -F cpu.py ''' } } echo "The second stage end." } stage("build image") { container("docker") { stage("build image") { // 首先docker build 制作一个镜像 然后login登陆一下 sh "docker build -t 528909316/jenkins:taskcpu_v1 ." sh "docker login -u 528909316 -p 'MyNewPass4!'" // 最后推送镜像 sh "docker push 528909316/jenkins:taskcpu_v1" } } echo "push end." } stage("deploy image") { // 部署的k8s集群 kubeconfigId为添加的配置在Jenkins的id的名字 创建时没有填写ID的话 Jenkins为为其生成一串随机字符 kubernetesDeploy configs: 'deploy.yml', kubeConfig: [path: ''], kubeconfigId: 'ae92d8dc-053e-409e-ae1b-f6e3f3bbb9f4', secretName: '', ssh: [sshCredentialsId: '*', sshServer: ''], textCredentials: [certificateAuthorityData: '', clientCertificateData: '', clientKeyData: '', serverUrl: 'https://'] } } }

最终的deploy.yml
apiVersion: apps/v1 kind: Deployment metadata: name: python-cpu spec: selector: matchLabels: run: python-cpu replicas: 3 template: metadata: labels: run: python-cpu spec: containers: - name: python-cpu imagePullPolicy: Always image: 528909316/jenkins:taskcpu_v1 ports: - containerPort: 8000 resources: limits: cpu: 500m requests: cpu: 200m --- apiVersion: v1 kind: Service metadata: name: python-cpu labels: run: python-cpu spec: ports: - port: 8000 selector: run: python-cpu type: LoadBalancer

参考文献
Pipeline Syntax
问题 提示:
Started by user user
[Pipeline] Start of Pipeline
[Pipeline] podTemplate
[Pipeline] {
[Pipeline] node
ERROR: Unable to create pod kubernetes jenkins/cluster-one-project-10-stvzm-fl2nv-r8s82.
Failure executing: POST at: https://kubernetes.default.svc.cluster.local/api/v1/namespaces/jenkins/pods. Message: Forbidden!Configured service account doesn’t have access. Service account may have been revoked. pods is forbidden: User “system:serviceaccount:jenkins:default” cannot create resource “pods” in API group “” in the namespace “jenkins”.
[Pipeline] // node
[Pipeline] }
[Pipeline] // podTemplate
[Pipeline] End of Pipeline
Queue task was cancelled
Finished: ABORTED
参考
【devops|03 使用Kubernetes作为工作节点】https://blog.csdn.net/zzb7728317/article/details/106282557/

    推荐阅读