目录

  • 一、简介
  • 二、Jenkins + K8s
  • 2.1 Jenkins配置k8s-master服务器信息
  • 2.2 配置镜像仓库信息
  • 2.3 编写k8s yaml文件
  • 2.4 将yaml文件推送到k8s
  • 2.5 配置免密钥登录
  • 2.6 k8s部署yaml资源文件
  • 2.7 重新部署yaml资源文件
  • 2.8 构建注意事项
  • 2.9 完整Jenkinsfile
  • 2.10 构建成功
  • 三、Webhook
  • 源码


一、简介

上一节我们学习了Jenkins Pipeline风格的方式,Jenkins执行目标服务器编写好的shell脚本进行容器部署。但是也是有缺点的:编写的shell脚本比较复杂,容器不方便管理。这一节我们就学习一下Jenkins结合K8s技术实现项目部署。

k8s官方文档:https://kubernetes.io/zh-cn/docs/home/

Kubernetes 为你提供:

  • 服务发现和负载均衡
    Kubernetes 可以使用 DNS 名称或自己的 IP 地址来暴露容器。 如果进入容器的流量很大, Kubernetes 可以负载均衡并分配网络流量,从而使部署稳定。
  • 存储编排
    Kubernetes 允许你自动挂载你选择的存储系统,例如本地存储、公共云提供商等。
  • 自动部署和回滚
    你可以使用 Kubernetes 描述已部署容器的所需状态, 它可以以受控的速率将实际状态更改为期望状态。 例如,你可以自动化 Kubernetes 来为你的部署创建新容器, 删除现有容器并将它们的所有资源用于新容器。
  • 自动完成装箱计算
    你为 Kubernetes 提供许多节点组成的集群,在这个集群上运行容器化的任务。 你告诉 Kubernetes 每个容器需要多少 CPU 和内存 (RAM)。 Kubernetes 可以将这些容器按实际情况调度到你的节点上,以最佳方式利用你的资源。
  • 自我修复
    Kubernetes 将重新启动失败的容器、替换容器、杀死不响应用户定义的运行状况检查的容器, 并且在准备好服务之前不将其通告给客户端。
  • 密钥与配置管理
    Kubernetes 允许你存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。 你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。

二、Jenkins + K8s

2.1 Jenkins配置k8s-master服务器信息

进入Jenkins 系统管理 -》 系统配置 -》 Publish over SSH,添加k8s-master节点的服务器信息:

Remote Directory 是Jenkins服务器连接到k8s服务器时的目录,我们填写/usr/local/k8s,注意需要将该目录创建出来。

勾选Use password authentication, or use a different key,输入服务器密码。

jenkins整合k8s k8s+jenkins_jenkins整合k8s

2.2 配置镜像仓库信息

在Jenkins连接到k8s-master服务器后,需要执行k8s命令进行部署,需要从Harbor镜像仓库中拉取我们的镜像,所以需要在docker中配置Harbor的账号密码信息。
注意:k8s集群所有的节点都需要修改这个配置,因为部署的时候不一定是哪一个节点。

vim /etc/docker/daemon.json

#加入harbor镜像仓库的地址,注意Json的格式,不要少了逗号
"insecure-registries": ["http://192.168.153.131:80"]

#重启Docker服务
systemctl restart docker

jenkins整合k8s k8s+jenkins_jenkins整合k8s_02

然后使用docker login命令登录试一下

jenkins整合k8s k8s+jenkins_jenkins整合k8s_03

2.3 编写k8s yaml文件

创建pipeline.yaml文件:

pipeline.yaml文件创建了一个Deployment、一个Service、一个Ingress

Pod镜像指向Harbor镜像仓库中的镜像。

Service8886端口映射到容器的8080端口。

Ingress host配置 hello.world.com域名转发到创建的Service。

需要修改本地hosts文件,映射hello.world.com域名到k8s集群的任意IP地址。

jenkins整合k8s k8s+jenkins_jenkins_04

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: test
  name: pipeline
  labels:
    app: pipeline
spec:
  replicas: 2
  selector:
    matchLabels:
      app: pipeline
  template:
    metadata:
      labels:
        app: pipeline
    spec:
      containers:
        - name: pipeline
          image: 192.168.153.131:80/helloworld/helloworld:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
  namespace: test
  labels:
    app: pipeline
  name: pipeline
spec:
  selector:
    app: pipeline
  ports:
  - port: 8886
    targetPort: 8080
  type: NodePort
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  namespace: test
  name: pipeline
spec:
  ingressClassName: ingress
  rules:
  - host: hello.world.com
    http:
      paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: pipeline
              port:
                number: 8886

2.4 将yaml文件推送到k8s

接下来修改我们的Jenkinsfile的流程
删除之前Publish Over SSH的步骤,不再需要去执行目标服务器中准备好的shell脚本,而是将Git仓库中编写好的pipeline.yaml k8s资源文件推送到k8s-master节点。然后通过k8s部署pipeline.yaml资源文件。

Jenkins将pipeline.yaml文件推送到k8s-master服务器
注意推送的目录就是之前配置的/usr/local/k8s 目录下

jenkins整合k8s k8s+jenkins_jenkins整合k8s_05

jenkins整合k8s k8s+jenkins_Jenkins_06

sshPublisher(publishers: [sshPublisherDesc(configName: 'k8s', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'pipeline.yaml')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])

jenkins整合k8s k8s+jenkins_ci/cd_07

然后开始构建,如果能够在k8s-master服务器的**/usr/local/k8s**目录看到pipeline.yaml文件,就表示我们配置的没有问题。
在k8s-master节点进行查看:

jenkins整合k8s k8s+jenkins_ci/cd_08

2.5 配置免密钥登录

Jenkins配置免密钥登录k8s-master主机
pipeline.yaml文件推送到k8s-master节点后,是无法直接执行kubectl apply命令的,因为没有权限。
所以需要配置免秘钥登录。
将Jenkins中公钥信息复制到k8s-master的~/.ssh/authorized_keysz中,保证远程连接无密码。

#jenkins
cd ~
ls -a
ssh-keygen -t rsa # 如果没有.ssh文件,用此命令生成
cd .ssh/
cat id_rsa.pub # 查看公钥

#k8s-master
cd ~
mkdir .ssh
cd .ssh/ # 如果没有.ssh文件,直接使用mkdir .ssh 命令创建即可
vi authorized_keys #将公钥直接粘贴保存

jenkins整合k8s k8s+jenkins_jenkins整合k8s_09


jenkins整合k8s k8s+jenkins_kubernetes_10


jenkins整合k8s k8s+jenkins_kubernetes_11

将Jenkins服务器的公钥信息粘贴到k8s-master服务器的.ssh/authorized_keys

jenkins整合k8s k8s+jenkins_Jenkins_12


jenkins整合k8s k8s+jenkins_jenkins_13

然后再部署Jenkins的服务器通过ssh root@192.168.153.128 ls命令,不需要输入密码执行了命令则说明配置成功。

jenkins整合k8s k8s+jenkins_Jenkins_14

2.6 k8s部署yaml资源文件

然后生成Jenkins流水线语法
ssh root@192.168.153.128 kubectl apply -f /usr/local/k8s/pipeline.yaml shell命令转换为流水线语法

jenkins整合k8s k8s+jenkins_jenkins_15

sh 'ssh root@192.168.153.128 kubectl apply -f pipeline.yaml'

我们在Jenkinsfile中在添加一个步骤:将pipeline.yaml文件推送到k8s-master节点后,执行kubectl apply命令进行部署。

jenkins整合k8s k8s+jenkins_ci/cd_16

2.7 重新部署yaml资源文件

在完成以上步骤后,看似Jenkins和K8s的整合已经完成了,但是这里还有一个问题。

当我们的代码发生了变化,但是pipeline.yaml资源文件没有改变,k8s则不会重新执行部署。

jenkins整合k8s k8s+jenkins_kubernetes_17

所以我们需要增加一条k8s命令:kubectl rollout restart Deployment pipeline -n test,就算pipeline.yaml没有发生改变,k8s也会重新部署。

jenkins整合k8s k8s+jenkins_jenkins_18

sh '''ssh root@192.168.153.128 kubectl apply -f /usr/local/k8s/pipeline.yaml
 ssh root@192.168.153.128 kubectl rollout restart Deployment pipeline -n test'''

jenkins整合k8s k8s+jenkins_jenkins整合k8s_19

jenkins整合k8s k8s+jenkins_ci/cd_20

2.8 构建注意事项

我们使用了k8s部署,不再需要参数化构建

jenkins整合k8s k8s+jenkins_kubernetes_21

所以可以把之前需要的参数全部删除了,对应需要修改我们的Jenkinsfile中的tag变量:

jenkins整合k8s k8s+jenkins_jenkins_22

jenkins整合k8s k8s+jenkins_jenkins_23

在pipeline.yaml中的镜像版本也修改为latest

jenkins整合k8s k8s+jenkins_kubernetes_24

2.9 完整Jenkinsfile

pipeline {
    agent any
    environment{
        harborHost = '192.168.153.131:80'
        harborRepo = 'helloworld'
        harborUser = 'admin'
        harborPasswd = 'Harbor12345'
    }

    // 存放所有任务的合集
    stages {

        stage('Pull Code') {
            steps {
            checkout([$class: 'GitSCM', branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[url: 'https://gitee.com/L1692312138/jenkins-demo.git']]])
            }
        }

        stage('Maven Build') {
            steps {
                sh '/var/jenkins_home/apache-maven-3.8.6/bin/mvn clean package -DskipTests'
            }
        }

        stage('Push Harbor') {
            steps {
                sh '''cp ./target/*.jar ./deploy/
                cd ./deploy
                docker build -t ${JOB_NAME}:latest .'''

                sh '''docker login -u ${harborUser} -p ${harborPasswd} ${harborHost}
                docker tag ${JOB_NAME}:latest ${harborHost}/${harborRepo}/${JOB_NAME}:latest
                docker push ${harborHost}/${harborRepo}/${JOB_NAME}:latest'''
            }
        }

        stage('Publish Over SSH') {
            steps {
            sshPublisher(publishers: [sshPublisherDesc(configName: 'k8s', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: 'pipeline.yaml')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
        stage('Deployment k8s') {
            steps {
            sh '''ssh root@192.168.153.128 kubectl apply -f /usr/local/k8s/pipeline.yaml
            ssh root@192.168.153.128 kubectl rollout restart Deployment pipeline -n test'''
            }
        }
    }
    post {
      always {
        emailext body: '${FILE,path="email.html"}', subject: '【构建通知】:$PROJECT_NAME - Build # $BUILD_NUMBER - $BUILD_STATUS!', to: 'liush99@foxmail.com'
      }
    }
}

2.10 构建成功

Deployment

jenkins整合k8s k8s+jenkins_ci/cd_25


jenkins整合k8s k8s+jenkins_ci/cd_26

Pod

jenkins整合k8s k8s+jenkins_jenkins_27


Service

jenkins整合k8s k8s+jenkins_kubernetes_28

Ingress

jenkins整合k8s k8s+jenkins_Jenkins_29


访问服务

jenkins整合k8s k8s+jenkins_kubernetes_30


jenkins整合k8s k8s+jenkins_jenkins_31

三、Webhook

Jenkins下载Gitee插件

如果你使用的不是Gitee,是其他的Github、GitLab、Gogs仓库则自行下载对应的Jenkins插件。

jenkins整合k8s k8s+jenkins_ci/cd_32


然后进入项目中配置 构建触发器,勾选 Gitee webhook触发构建

jenkins整合k8s k8s+jenkins_jenkins整合k8s_33


点击生成Gitee WebHook密码

jenkins整合k8s k8s+jenkins_kubernetes_34

在Gitee仓库 管理 --》 WebHooks --》 添加WebHooks
输入回调Jenkins的URL和密码,点击添加

jenkins整合k8s k8s+jenkins_ci/cd_35


出现了一个错误,Gitee WebHooks的URL需要是公网可以访问的IP,由于博主是搭建的虚拟机,所以这一步就无法实现了。

jenkins整合k8s k8s+jenkins_jenkins整合k8s_36

到此为止,Jenkins 整合 K8s构建项目的方式就完成了!

源码

Gitee:https://gitee.com/L1692312138/jenkins-demo.git

Github:https://github.com/Liu-Shihao/helloworld.git