<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      二十一、DevOps:從零建設基于K8s的DevOps平臺(二)

      二十一、DevOps:從零建設基于K8s的DevOps平臺(二)

      目錄

      1、DevOps 平臺建設

      1.1 DevOps 流程

      微服務發(fā)版的自動化流水線,一般會有如下步驟:

      1、在 GitLab 中創(chuàng)建對應的項目;
      2、配置 Jenkins 集成 Kubernetes 集群,后期 Jenkins 的 Slave 將為在 Kubernetes 中動態(tài)創(chuàng)建的 Slave;
      3、Jenkins 創(chuàng)建對應的任務(Job),集成該項目的 Git 地址和 Kubernetes 集群;
      4、開發(fā)者將代碼提交到 GitLab;
      5、如有配置鉤子,推送(Push)代碼會自動觸發(fā) Jenkins 構建,如沒有配置鉤子,需要手動構建;
      6、Jenkins 控制 Kubernetes(使用的是 Kubernetes 插件)創(chuàng)建 Jenkins Slave(Pod形式);
      7、Jenkins Slave 根據(jù)流水線(Pipeline)定義的步驟執(zhí)行構建,生成交付物;
      8、通過 Dockerfile 生成鏡像;
      9、將鏡像提送(Push)到私有 Harbor(或者其它的鏡像倉庫);
      10、Jenkins 再次控制 Kubernetes 進行最新的鏡像部署;
      11、流水線結束刪除 Jenkins Slave。

      1.2 DevOps 平臺整體設計

      image.png-334.8kB

      1.3 集群規(guī)劃(學習測試環(huán)境)

      主機名稱 物理IP 系統(tǒng) 資源配置 說明
      k8s-master01 192.168.200.50 Rocky9.4 4核4g Master節(jié)點
      k8s-node01 192.168.200.51 Rocky9.4 4核4g Node01節(jié)點
      k8s-node02 192.168.200.52 Rocky9.4 4核4g Node02節(jié)點
      Harbor 192.168.200.53 Rocky9.4 2核4g 鏡像倉庫
      Jenkins 192.168.200.54 Rocky9.4 2核4g CI/CD流水線
      GitLab 192.168.200.55 Rocky9.4 2核4g 代碼倉庫

      1.4 Harbor 私有倉庫

      1.4.1 安裝docker

      # 添加docker的yum源配置文件
      [root@harbor ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
      
      # 安裝docker
      [root@harbor ~]# yum install docker-ce docker-ce-cli -y
      
      # 啟動docker
      [root@harbor ~]# systemctl daemon-reload && systemctl enable --now docker
      

      1.4.2 配置docker-compose

      # 設置軟連接
      [root@harbor ~]# ln -s /usr/libexec/docker/cli-plugins/docker-compose /usr/local/bin/
      
      [root@harbor ~]# docker-compose -v
      Dcurl -L https://github.com/docker C/compose version v2.36.2
      
      # 批量停止harbor實例
      docker-compose down
      # 批量啟動harbor實例
      docker-compose up -d
      

      1.4.3 安裝 Harbor

      # 下載到本地
      [root@harbor ~]# wget https://github.com/goharbor/harbor/releases/download/v2.13.1/harbor-offline-installer-v2.13.1.tgz
      
      # 解壓tar包
      [root@harbor ~]# tar xf harbor-offline-installer-v2.13.1.tgz 
      [root@harbor ~]# cd harbor
      
      # 加載配置
      [root@harbor harbor]# docker load -i harbor.v2.13.1.tar.gz 
      
      # 修改 Harbor 默認配置文件
      [root@harbor harbor]# cp harbor.yml.tmpl harbor.yml
      [root@harbor harbor]# vim harbor.yml
      
      [root@harbor harbor]# sed -n "5p;10p;13,18p;47p;66p" harbor.yml
      hostname: 192.168.200.53 
        port: 80
      # https:
        # https port for harbor, default is 443
        # port: 443
        # The path of cert and key files for nginx
        # certificate: /your/certificate/path
        # private_key: /your/private/key/path
      harbor_admin_password: Harbor12345
      data_volume: /data/harbor
      
      • hostname:Harbor 的訪問地址,可以是域名或者 IP,生產推薦使用域名,并且?guī)в凶C書
      • https:域名證書的配置,生產環(huán)境需要配置權威證書供 Harbor 使用,否則需要添加 insecure-registry 配置,由于是學習環(huán)境,所以本示例未配置證書
      • harbor_admin_password:賬號密碼按需修改即可,默認為 admin:Harbor12345
      • data_volume:Harbor 的數(shù)據(jù)目錄
      # 創(chuàng)建 Harbor 數(shù)據(jù)目錄并進行預配置:
      [root@harbor harbor]# mkdir /data/harbor -p
      
      # 加載配置啟動
      [root@harbor harbor]# ./prepare
      
      # 執(zhí)行安裝:
      [root@harbor harbor]# ./install.sh
      

      web界面訪問
      image.png-348.9kB

      登錄后,創(chuàng)建一個項目
      image.png-107.5kB

      1.4.4 創(chuàng)建開機自啟動腳本

      [root@harbor harbor]# vim /etc/systemd/system/harbor.service 
      [root@harbor harbor]# cat /etc/systemd/system/harbor.service 
      [Unit]
      Description=Harbor Service
      After=docker.service
      Requires=docker.service
      
      [Service]
      Type=oneshot
      RemainAfterExit=yes
      WorkingDirectory=/usr/local/harbor
      ExecStart=/usr/local/bin/docker-compose up -d
      ExecStop=/usr/local/bin/docker-compose down
      User=root
      
      [Install]
      WantedBy=multi-user.target
      
      # 加上執(zhí)行權限
      [root@harbor harbor]# chmod +x /etc/systemd/system/harbor.service 
      
      # 設置開機自啟動
      [root@harbor harbor]# systemctl daemon-reload
      [root@harbor harbor]# systemctl enable harbor
      

      1.4.5 docker 配置 insecure registry

      如果配置不是 https 協(xié)議,所有的 Kubernetes 節(jié)點的 Docker 都需要添加 insecure-registries 配置

      [root@harbor harbor]# vim /etc/docker/daemon.json
      [root@harbor harbor]# cat /etc/docker/daemon.json 
      {
           "registry-mirrors": ["https://9upbt3ho.mirror.aliyuncs.com"],
           "insecure-registries": ["192.168.200.53"]
      }
      [root@harbor harbor]# systemctl daemon-reload
      [root@harbor harbor]# systemctl restart docker
      

      1.4.6 Containerd 配置 insecure registry (k8s節(jié)點)

      如果 Kubernetes 集群采用的是 Containerd 作為的 Runtime,那么 Containerd 也需要配置 insecure registry。

      首先生成 Containerd 的配置(如果已經(jīng)生成過,請勿執(zhí)行)

      # containerd config default > /etc/containerd/config.toml
      
      # 修改配置(所有k8s節(jié)點都要執(zhí)行)
      [root@k8s-master01 ~]# vim /etc/containerd/config.toml
      [root@k8s-master01 ~]# sed -n "170,172p" /etc/containerd/config.toml 
            [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
            		[plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.200.53"]
      			endpoint = ["http://192.168.200.53"]
      
      # 重啟服務
      [root@k8s-master01 ~]# systemctl restart containerd
      
      # 需要把K8s所有的節(jié)點都需要進行更改
      # config.toml并不是直接給ctr命令去使用的
      # docker拉取的鏡像和containerd沒有任何關系
      

      1.5 Jenkins 安裝

      1.5.1 安裝 Docker

      # 添加docker的yum源配置文件
      [root@jenkins ~]# yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
      
      # 安裝docker
      [root@jenkins ~]# yum install docker-ce docker-ce-cli -y
      
      # 啟動docker
      [root@jenkins ~]# systemctl daemon-reload && systemctl enable --now docker
      

      1.5.2 安裝Jenkins

      創(chuàng)建 Jenkins 的數(shù)據(jù)目錄,防止容器重啟后數(shù)據(jù)丟失:

      [root@jenkins ~]# mkdir /data/jenkins_data -p
      [root@jenkins ~]# chmod -R 777 /data/jenkins_data
      

      啟動 Jenkins,并配置管理員賬號密碼為 admin/admin123:

      # 拉取鏡像
      [root@jenkins ~]# docker pull crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/jenkins:2.504.3-debian-12-r1
      
      # 啟動容器
      [root@jenkins ~]# docker run -d --name=jenkins --restart=always -e JENKINS_PASSWORD=admin123 -e JENKINS_USERNAME=admin -e JENKINS_HTTP_PORT_NUMBER=8080 -p 8080:8080 -p 50000:50000 -v /usr/bin/docker:/usr/bin/docker -v /var/run/docker.sock:/var/run/docker.sock -v /data/jenkins_data:/bitnami/jenkins crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/jenkins:2.504.3-debian-12-r1
      
      # 查看容器
      [root@jenkins ~]# docker ps -a
      CONTAINER ID   IMAGE                                                                                          COMMAND                  CREATED         STATUS         PORTS                                                                                                    NAMES
      8e44a0ba9584   crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/jenkins:2.504.3-debian-12-r1   "/opt/bitnami/script…"   2 minutes ago   Up 2 minutes   0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp, 0.0.0.0:50000->50000/tcp, [::]:50000->50000/tcp, 8443/tcp   jenkins
      

      其中 8080 端口為 Jenkins Web 界面的端口,50000 是 jnlp 使用的端口,后期 Jenkins Slave 需要使用 50000 端口和 Jenkins 主節(jié)點通信。

      查看 Jenkins 日志:

      # 查看到這條日志說明 Jenkins 已完成啟動
      [root@jenkins ~]# docker logs -f jenkins
      ....
      INFO	hudson.lifecycle.Lifecycle#onReady: Jenkins is fully up and running
      

      之后通過 Jenkins 宿主機的 IP+8080 即可訪問 Jenkins
      image.png-617.9kB

      1.5.3 插件安裝

      登錄后點擊 Manage Jenkins → Manage Plugins 安裝需要使用的插件:
      image.png-106.7kB

      image.png-129.5kB

      在安裝之前首先配置國內的插件源,點擊 Advanced,將插件源更改為國內插件源(https://mirrors.huaweicloud.com/jenkins/updates/update-center.json)

      image.png-85.2kB

      點擊 Submit 后在 Available 可以看到所有的可用插件:
      image.png-134.5kB

      Git
      Git Parameter
      Git Pipeline for Blue Ocean
      GitLab
      Credentials
      Credentials Binding
      Blue Ocean
      Blue Ocean Pipeline Editor
      Blue Ocean Core JS
      Web for Blue Ocean
      Pipeline SCM API for Blue Ocean
      Dashboard for Blue Ocean
      Build With Parameters
      List Git Branches Parameter
      Pipeline
      Pipeline: Declarative
      Kubernetes
      Kubernetes CLI
      Kubernetes Credentials
      Image Tag Parameter
      Docker
      Docker Slaves
      Docker Pipeline
      Role-based Authorization Strategy
      

      最后安裝完記得重啟
      image.png-91.6kB

      等待安裝后自動重啟后,就可以在 Installed 看到已經(jīng)安裝的插件:
      image.png-95.1kB

      至此 Jenkins 和 Jenkins 插件的安裝就完成了。

      1.5.4 docker配置insecure registry

      [root@jenkins ~]# vim /etc/docker/daemon.json
      [root@jenkins ~]# cat /etc/docker/daemon.json
      {
           "registry-mirrors": ["https://9upbt3ho.mirror.aliyuncs.com"],
           "insecure-registries": ["192.168.200.53"]
      }
      [root@jenkins ~]# systemctl daemon-reload
      [root@jenkins ~]# systemctl restart docker
      

      1.6 GitLab 安裝

      GitLab 在企業(yè)內經(jīng)常用于代碼的版本控制,也是 DevOps 平臺中尤為重要的一個工具。

      1.6.1 關閉機器防火墻和 selinux

      # 關閉防火墻
      [root@gitlab ~]# systemctl disable --now firewalld
      [root@gitlab ~]# systemctl disable --now dnsmasq
      
      # 關閉selinux
      [root@gitlab ~]# setenforce 0
      [root@gitlab ~]# sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/sysconfig/selinux
      [root@gitlab ~]# sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
      

      1.6.2 安裝gitlab

      [root@gitlab ~]# wget https://mirrors.tuna.tsinghua.edu.cn/gitlab-ce/yum/el9/gitlab-ce-17.9.8-ce.0.el9.x86_64.rpm
      [root@gitlab ~]# yum install gitlab-ce-17.9.8-ce.0.el9.x86_64.rpm -y
      
      # 修改配置
      [root@gitlab ~]# vim /etc/gitlab/gitlab.rb
      [root@gitlab ~]# sed -n "32p;2346p" /etc/gitlab/gitlab.rb 
      external_url 'http://192.168.200.55'    # 將 external_url 更改為自己的發(fā)布地址,可以是服務器的 IP,也可以是一個可被解析的域名
      prometheus['enable'] = false            # 關閉 Prometheus 插件(可選)
      
      # 更改完成后需要重新加載配置文件
      [root@gitlab ~]# gitlab-ctl reconfigure
      ....
      Notes:
      Default admin account has been configured with following details:
      Username: root
      Password: You didn't opt-in to print initial root password to STDOUT.
      Password stored to /etc/gitlab/initial_root_password. This file will be cleaned up in first reconfigure run after 24 hours.
      
      NOTE: Because these credentials might be present in your log files in plain text, it is highly recommended to reset the password following https://docs.gitlab.com/ee/security/reset_user_password.html#reset-your-root-password.
      
      gitlab Reconfigured!
      

      之后可以通過 瀏覽器訪 問 GitLab,賬號 root,默認密碼在/etc/gitlab/initial_root_password

      image.png-125.8kB

      登錄后,開啟 import 功能,可以從外部倉庫導入代碼到 Gitlab:
      image.png-60.2kB

      image.png-197.4kB

      最后記得點擊保存
      image.png-64.9kB

      1.6.3 創(chuàng)建項目

      1、創(chuàng)建一個測試項目,進行一些簡單的測試。首先創(chuàng)建一個組:
      image.png-136.8kB

      image.png-97.2kB

      組名為 kubernetes,類型為 Private,之后點擊 Create group 即可:
      image.png-144.4kB

      之后在該組下創(chuàng)建一個 Project:
      image.png-136.5kB

      選擇創(chuàng)建一個空的項目:
      image.png-141.5kB

      輸入項目名稱,然后點擊 Create project 即可:
      image.png-171.2kB

      1.6.4 配置密鑰

      之后可以將 k8s-master 服務器(任意機器均可)的 key 導入到 GitLab

      # 首先生成密鑰(如有可以無需生成):
      [root@k8s-master01 ~]# ssh-keygen -t rsa -C "1773464408@qq.com"
      
      # 將公鑰的內容放在 GitLab 中即可:
      [root@k8s-master01 ~]# cat .ssh/id_rsa.pub 
      ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQDJTvF8NDvX/SKxS1z7j/SdqInCB2A3kfuRsOn3c/j962H+yN/0SfRXShaSpHzvrVPNQMtiEr4gFQ4UKgpcWUyyDeaCyYRDrUxZ96IDoY2KoTser5o9dtfmnYaKeP7koewINFesPj59Ur2pATks7SYoT59o7UGKrShcedgo23dI0pvJmNaYKOlLeYocbaCY31g76SCZ44jVmv92smU9L+rVS15FT6hTjMO194vflbhLQ5p5nyaWRORYHuHxTbe5YHpMmEQquLnvMvhAr9ngzCdeMNyVtpQx2ZcbR9YmB6WiL6VsGqyPywkb2rU9G4I0qW8bg+aI1tTnjlXCEGKCNV7rxqNx3h0vRADTlYpKeeh1Ysrt+5+CibYJj3KNfY1dXLFOCCDc6YIFRX/0jFk/7RHe2r7knsqajc1ytt0BdDYb6GBeckDhIL4WGiKI3LtHI4LdXammTAf9wwIv7ZEEyuzAHd71AKx/JTKcA+jmGGy64j2i8Xzpk9UyWuhhxOwwJKc= 1773464408@qq.com
      

      在 GitLab 找到 Profile:
      image.png-39.2kB

      之后在 SSH Keys 添加公鑰:
      image.png-166.9kB

      image.png-188.2kB

      image.png-124.5kB

      添加后就可以在服務器上拉取代碼:

      [root@k8s-master01 ~]# yum install git -y
      [root@k8s-master01 ~]# git config --global user.email 1773464408@qq.com
      [root@k8s-master01 ~]# git config --global user.name "yunwei"
      [root@k8s-master01 ~]# git clone git@192.168.200.55:kubernetes/test.git
      

      1.7 Jenkins 憑證 Credentials

      Harbor 的賬號密碼、Kubernetes 的證書、GitLab 的私鑰均使用 Jenkins 的 Credentials 管理。

      1.7.1 配置 Harbor 賬號密碼

      首先點擊 Manage Jenkins,之后點擊 Credentials:
      a6db8f8b-a9ff-45ed-b649-f83e26dc641d.png-123.4kB

      image.png-45.3kB

      image.png-127.5kB

      • Username:Harbor 或者其它平臺的用戶名
      • Password:Harbor 或者其它平臺的密碼
      • ID:該憑證的 ID
      • Description:證書的描述

      1.7.2 配置 Kubernetes 證書

      首先需要找到集群中的 KUBECONFIG,一般是 kubectl 節(jié)點的~/.kube/config 文件,或者是 KUBECONFIG 環(huán)境變量所指向的文件。

      接下來只需要把證書文件放置于 Jenkins 的 Credentials 中即可,點擊 Add Credentials,類型選擇為 Secret file

      image.png-84.6kB

      image.png-96.6kB

      1.7.3 配置 GitLab Key

      前面將k8s的公鑰放到了gitlab上面,可以拉取上面的代碼;現(xiàn)在把k8s的私鑰放到jenkin上面,現(xiàn)在jenkins也就可以拉取gitlab上面的代碼

      # 私鑰查詢
      [root@k8s-master01 ~]# cat .ssh/id_rsa
      

      點擊 Add Credentials,類型選擇為 SSH Username with private key
      image.png-100.5kB

      image.png-142.1kB

      至此我們所有的憑證都添加好了
      image.png-74.7kB

      1.8 配置 Agent

      通常情況下,Jenkins Slave 會通過 Jenkins Master 節(jié)點的 50000 端口與之通信,所以需要開啟 Agent 的 50000 端口。

      點擊 Manage Jenkins,然后點擊 Security:
      image.png-126.1kB

      在安全配置下方找到 Agents,點擊 Fixed,輸入 50000 即可:
      image.png-56.6kB

      實際使用時,如果不需要把整個 Kubernetes 集群的節(jié)點都充當創(chuàng)建 Jenkins Slave Pod 的節(jié)點,可以選擇任意的一個或多個節(jié)點作為創(chuàng)建 Slave Pod 的節(jié)點。

      假設 k8s-node01 作為 Slave 節(jié)點(可選):

      [root@k8s-master01 ~]# kubectl label node k8s-node01 build=true
      

      1.9 Jenkins 配置 Kubernetes 多集群

      首先點擊 Manage Jenkins,之后點擊 Clouds:
      image.png-99.9kB

      點擊 New cloud:
      image.png-45.6kB

      之后輸入集群的名稱,一般按照可識別的名稱即可,比如現(xiàn)在是學習環(huán)境的 Kubernetes 可以叫做kubernetes-study,選擇 Kubernetes,之后點擊 Create:
      image.png-43.2kB

      在 Credentials(憑據(jù))處選擇之前添加 Kubernetes 證書,選擇后點擊 Test Connection,最后在憑證下方即可看到能否正常連接的結果:
      7fbc4f1d-180b-486c-a319-022493183b2c.png-74.8kB

      最后點擊 Save 即可,添加完 Kubernetes 后,在 Jenkinsfile 的 Agent 中,就可以選擇該集群作為創(chuàng)建 Slave 的集群。

      如果想要添加多個集群,重復上述的步驟即可。
      image.png-24.3kB

      2、自動化流水線設計

      2.1 自動化流水線統(tǒng)一模板

      針對如上的邏輯,可以設計一個流水線模板,只需要變更一些參數(shù)即可。

      首先是頂層的 Agent,定義的是 Kubernetes 的 Pod 作為 Jenkins 的 Slave:

        agent {
          kubernetes {    # 定義使用 Kubernetes 作為 agent
            cloud 'kubernetes-study'
            slaveConnectTimeout 1200
            # 將 workspace 改成 PVC,用于持久化工作目錄,claimName 為創(chuàng)建的 PVC 名稱
            workspaceVolume persistentVolumeClaimWorkspaceVolume(claimName: "pipeline-cache", readOnly: false)
            yaml '''
      apiVersion: v1
      kind: Pod
      spec:
        restartPolicy: "Never"
        nodeSelector:     # 固定節(jié)點部署,可選
          build: "true"
        securityContext: {}
        volumes:
        - name: docker-registry-config        # docker 認證信息 volume
          configMap:
            name: docker-registry-config
        - name: "localtime"
          hostPath:
            path: "/usr/share/zoneinfo/Asia/Shanghai"
        - name: cache                 # 構建緩存 PVC
          persistentVolumeClaim:
            claimName: cache
            readonly: false
        containers:
        
        # jnlp 容器,和 Jenkins 主節(jié)點通信
        - name: jnlp
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/jnlp-agent-docker:latest"
          imagePullPolicy: IfNotPresent
          args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
            
        # build 容器,包含執(zhí)行構建的命令,比如 Java 的需要 mvn 構建,就可以用一個 maven 的鏡像
        # 容器的名字,流水線的 stage 可以直接使用該名字
        - name: "build"
          # 使用 Maven 鏡像,包含 mvn 工具,NodeJS 可以用 node 的鏡像,根據(jù)實際情況修改
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/maven:3.5.3"
          imagePullPolicy: "IfNotPresent"
          command:
          - "cat"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
          # 由于 Java 編譯時,會把依賴的插件緩存到~/.m2目錄,所以把該目錄也進行緩存,如果其他語言的編譯也需要緩存,按照該方式配置即可
          - name: "cache"
            mountPath: "/root/.m2/"
            readOnly: false
            
        # 用于生成鏡像的容器,可以是 Docker 或者是其他的可以用于構建鏡像的工具
        - name: "kaniko"
          # 本示例采用 Kaniko 制作鏡像
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/kaniko-executor:debug"
          imagePullPolicy: "IfNotPresent"
          command:
          - "sleep"
          args:
          - "99d"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
          # 掛載 docker 配置文件,用于 Kaniko 鏈接鏡像倉庫
          - name: "docker-registry-config"
            mountPath: "/kaniko/.docker"
            
        # 發(fā)版容器,因為最終是發(fā)版至 Kubernetes 的,所以需要有一個 kubectl 命令或者是其他的發(fā)版工具也可以,比如 helm
        - name: "kubectl"
          # 發(fā)版的鏡像根據(jù)實際需求進行變更
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/kubectl:devops"
          imagePullPolicy: "IfNotPresent"
          command:
          - "cat"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
            '''
          }
      

      之后看一下 Jenkinsfile 最后的環(huán)境變量和 parameters 的配置:

        # 定義一些全局的環(huán)境變量
        environment {
          COMMIT_ID = ""
          HARBOR_ADDRESS = "192.168.200.53"   # Harbor 地址
          REGISTRY_DIR = "kubernetes"         # Harbor 的項目目錄
          IMAGE_NAME = "spring-boot-project"  # 鏡像的名稱
          NAMESPACE = "kubernetes"            # 該應用在 Kubernetes 中的命名空間
          TAG = ""                            # 鏡像的 Tag,在此用 BUILD_TAG+COMMIT_ID 組成
          GIT_URL = "git@192.168.200.55:kubernetes/spring-boot-project.git"   # 代碼地址
        }
        parameters {
          # 之前講過一些 choice、input 類型的參數(shù),本次使用的是 GitParameter 插件
          # 該字段會在 Jenkins 頁面生成一個選擇分支的選項
          gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
        }
      

      接下來是拉代碼的 stage,這個 stage 是一個并行的 stage,因為考慮了該流水線是手動觸發(fā)還是觸發(fā):

          stage('Pulling Code') {
            parallel {
              stage('Pulling Code by Jenkins') {
                when {
                  expression {
                    # 假如 env.gitlabBranch 為空,則該流水線為手動觸發(fā),那么就會執(zhí)行該 stage,如果不為空則會執(zhí)行同級的另外一個 stage
                    env.gitlabBranch == null
                  }
                }
                steps {
                  # 這里使用的是 git 插件拉取代碼,BRANCH 變量取自于前面介紹的 parameters 配置
                  # git@xxxxxx:root/spring-boot-project.git 代碼地址
                  # credentialsId: 'gitlab-key',之前創(chuàng)建的拉取代碼的 key
                  git(changelog: true, poll: true, url: "${GIT_URL}", branch: "${BRANCH}", credentialsId: 'gitlab-key')
                  script {
                    # 定義一些變量用于生成鏡像的 Tag
                    # 獲取最近一次提交的 Commit ID
                    COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                    # 賦值給 TAG 變量,后面的 docker build 可以取到該 TAG 的值
                    TAG = BUILD_TAG + '-' + COMMIT_ID
                    println "Current branch is ${BRANCH}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
                  }
                }
              }
              stage('Pulling Code by trigger') {
                when {
                  expression {
                    # 如果 env.gitlabBranch 不為空,說明該流水線是通過 webhook 觸發(fā),則此時執(zhí)行該 stage,上述的 stage 不再執(zhí)行。此時 BRANCH 變量為空
                    env.gitlabBranch != null
                  }
                }
                steps {
                  # 以下配置和上述一致,只是此時 branch: env.gitlabBranch 取的值為 env.gitlabBranch
                  git(url: "${GIT_URL}", branch: env.gitlabBranch, changelog: true, poll: true, credentialsId: 'gitlab-key')
                  script {
                    COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                    TAG = BUILD_TAG + '-' + COMMIT_ID
                    println "Current branch is ${env.gitlabBranch}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
                  }
                }
              }
            }
          }
      

      代碼拉下來后,就可以執(zhí)行構建命令,由于本次實驗是 Java 示例,所以需要使用 mvn 命令進行構建:

          stage('Building') {
            steps {
              container(name: 'build') {      # 使用 Pod 模板里面的 build 容器進行構建
                sh """    # 編譯命令,需要根據(jù)自己項目的實際情況進行修改,可能會不一致
                  curl repo.maven.apache.org
                  mvn clean install -DskipTests
                  ls target/*
                """
              }
            }
          }
      

      生成編譯產物后,需要根據(jù)該產物生成對應的鏡像,此時可以使用 Pod 模板的 kaniko 容器:

      stage('Build for creating image') {
            steps {
              container(name: 'kaniko') {     # 指定使用 kaniko 容器
                sh """
                  # 執(zhí)行 build 命令,Dockerfile 會在下一小節(jié)創(chuàng)建,也是放在代碼倉庫,和 Jenkinsfile 同級
                  executor -d ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -c . --insecure --skip-tls-verify
                """
              }
            }
          }
      

      最后一步就是將該鏡像發(fā)版至 Kubernetes 集群中,此時使用的是包含 kubectl 命令的容器:

      stage('Deploying to K8s') {
            environment {     # 獲取連接 Kubernetes 集群證書
              MY_KUBECONFIG = credentials('STUDY_CLUSTER_CONFIG')
            }
            steps {
              container(name: 'kubectl'){     # 指定使用 kubectl 容器
                sh """            # 直接 set 更改 Deployment 的鏡像即可
                  kubectl --kubeconfig $MY_KUBECONFIG set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -n $NAMESPACE
                """
              }
            }
          }
        }
      

      2.2 Workspace 持久化

      Jenkins 在構建時,會產生一些依賴文件,這些文件最好進行持久化存儲,防止重復下載。接下來創(chuàng)建一個 PVC 用于流水線工作目錄及依賴文件的數(shù)據(jù)持久化:

      [root@k8s-master01 ~]# vim pipeline-cache-pvc.yaml 
      [root@k8s-master01 ~]# cat pipeline-cache-pvc.yaml 
      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: pipeline-cache
        namespace: default
      spec:
        resources:
          requests:
            storage: 200Gi
        volumeMode: Filesystem
        storageClassName: nfs-csi
        accessModes:
          - ReadWriteMany
      ---
      apiVersion: v1
      kind: PersistentVolumeClaim
      metadata:
        name: cache
        namespace: default
      spec:
        resources:
          requests:
            storage: 200Gi
        volumeMode: Filesystem
        storageClassName: nfs-csi
        accessModes:
          - ReadWriteMany
      

      創(chuàng)建后查看 PVC:

      [root@k8s-master01 ~]# kubectl create -f pipeline-cache-pvc.yaml
      [root@k8s-master01 ~]# kubectl get pvc
      NAME             STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   VOLUMEATTRIBUTESCLASS   AGE
      cache            Bound    pvc-5a714f8e-3d1e-432b-8152-4f734a3c95c8   200Gi      RWX            nfs-csi        <unset>                 74s
      pipeline-cache   Bound    pvc-375e62f3-463c-449d-87e3-f215b37aedae   200Gi      RWX            nfs-csi        <unset>                 74s
      

      注意此時需要在 NFS 把該 PVC 設置為 777 權限:

      [root@habor ~]# chmod -R 777 /data/nfs/pvc-*
      

      2.3 Kaniko 配置文件處理

      首先登錄需要推送鏡像的目標倉庫,生成 docker 的配置文件:

      [root@k8s-master01 ~]# docker login 192.168.200.53
      

      查看生成的配置文件:

      [root@k8s-master01 ~]# cat ~/.docker/config.json
      {
      	"auths": {
      		"192.168.200.53": {
      			"auth": "YWRtaW46SGFyYm9yMTIzNDU="
      		}
      	}
      }
      

      創(chuàng)建 ConfigMap,用于掛載至 Jenkins Slave 的 Kaniko 容器:

      [root@k8s-master01 ~]# kubectl create cm docker-registry-config --from-file=config.json=/root/.docker/config.json
      
      [root@k8s-master01 ~]# kubectl get cm docker-registry-config
      NAME                     DATA   AGE
      docker-registry-config   1      15s
      

      3、自動化構建 Java 應用

      3.1 創(chuàng)建 Java 測試用例

      示例項目可以從 https://gitee.com/dukuan/spring-boot-project.git 找到該項目(也可以使用公司的 Java 項目也是一樣的)。

      接下來將該項目導入到自己的 GitLab 中。首先找到之前創(chuàng)建的 Kubernetes 組
      image.png-70.3kB

      然后點擊 New Project:
      image.png-91.6kB

      選擇 Import Project:
      image.png-111.2kB

      點擊 Repo by URL,在 Git repository URL 輸入示例地址,然后點擊 Create Project 即可:

      image.png-155.6kB
      image.png-75kB

      導入后,如下所示:
      image.png-180kB

      3.2 定義 Jenkinsfile

      Jenkinsfile 是用來保存 Pipeline 代碼的文件,通常用代碼倉庫管理,或者直接放置于服務的代碼倉庫中。

      Jenkinsfile 放置于代碼倉庫中,有以下好處:

      • 方便對流水線上的代碼進行復查/迭代;
      • 對管道進行審計跟蹤;
      • 流水線真正的源代碼能夠被項目的多個成員查看和編輯。

      接下來再 GitLab 的源代碼中添加 Jenkinsfile。首先點擊代碼首頁的“+”號,然后點擊 New file:
      image.png-105.4kB

      在窗口中,添加通過模板更改后的 Pipeline,并且命名為 Jenkinsfile
      image.png-137.3kB

      image.png-68.6kB

      完整配置

      pipeline {
        agent {
          kubernetes {
            cloud 'kubernetes-study'
            slaveConnectTimeout 1200
            workspaceVolume persistentVolumeClaimWorkspaceVolume(claimName: "pipeline-cache", readOnly: false)
            yaml '''
      apiVersion: v1
      kind: Pod
      spec:
        restartPolicy: "Never"
        nodeSelector:
          build: "true"
        securityContext: {}
        volumes:
        - name: docker-registry-config
          configMap:
            name: docker-registry-config
        - name: "localtime"
          hostPath:
            path: "/usr/share/zoneinfo/Asia/Shanghai"
        - name: cache
          persistentVolumeClaim:
            claimName: cache
            readonly: false
        containers:
        - name: jnlp
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/jnlp-agent-docker:latest"
          imagePullPolicy: IfNotPresent
          args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
        - name: "build"
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/maven:3.5.3"
          imagePullPolicy: "IfNotPresent"
          command:
          - "cat"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
          - name: "cache"
            mountPath: "/root/.m2/"
            readOnly: false
        - name: "kaniko"
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/kaniko-executor:debug"
          imagePullPolicy: "IfNotPresent"
          command:
          - "sleep"
          args:
          - "99d"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
          - name: "docker-registry-config"
            mountPath: "/kaniko/.docker"
        - name: "kubectl"
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/kubectl:devops"
          imagePullPolicy: "IfNotPresent"
          command:
          - "cat"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
            '''
          }
        }
        stages {
          stage('Pulling Code') {
            parallel {
              stage('Pulling Code by Jenkins') {
                when {
                  expression {
                    env.gitlabBranch == null
                  }
                }
                steps {
                  git(changelog: true, poll: true, url: "${GIT_URL}", branch: "${BRANCH}", credentialsId: 'gitlab-key')
                  script {
                    COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                    TAG = BUILD_TAG + '-' + COMMIT_ID
                    println "Current branch is ${BRANCH}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
                  }
                }
              }
              stage('Pulling Code by trigger') {
                when {
                  expression {
                    env.gitlabBranch != null
                  }
                }
                steps {
                  git(url: "${GIT_URL}", branch: env.gitlabBranch, changelog: true, poll: true, credentialsId: 'gitlab-key')
                  script {
                    COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                    TAG = BUILD_TAG + '-' + COMMIT_ID
                    println "Current branch is ${env.gitlabBranch}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
                  }
                }
              }
            }
          }
          stage('Building') {
            steps {
              container(name: 'build') {
                sh """
                  curl repo.maven.apache.org
                  mvn clean install -DskipTests
                  ls target/*
                """
              }
            }
          }
          stage('Build for creating image') {
            steps {
              container(name: 'kaniko') {
                sh """
                  executor -d ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -c . --insecure --skip-tls-verify
                """
              }
            }
          }
          stage('Deploying to K8s') {
            environment {
              MY_KUBECONFIG = credentials('STUDY_CLUSTER_CONFIG')
            }
            steps {
              container(name: 'kubectl'){
                sh """
                  kubectl --kubeconfig $MY_KUBECONFIG set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -n $NAMESPACE
                """
              }
            }
          }
        }
        environment {
          COMMIT_ID = ""
          HARBOR_ADDRESS = "192.168.200.53"
          REGISTRY_DIR = "kubernetes"
          IMAGE_NAME = "spring-boot-project"
          NAMESPACE = "kubernetes"
          TAG = ""
          GIT_URL = "git@192.168.200.55:kubernetes/spring-boot-project.git"
        }
        parameters {
          gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
        }
      }
      

      3.3 定義 Dockerfile

      在執(zhí)行流水線過程時,需要將代碼的編譯產物做成鏡像。而本次示例是 Java 項目,只需要把 Jar 包放在有 Jre 環(huán)境的鏡像中,然后啟動該 Jar 包即可:

      # 基礎鏡像可以按需修改,可以更改為公司自有鏡像
      FROM crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/jre:8u211-data
      # jar 包名稱改成實際的名稱,本示例為 spring-cloud-eureka-0.0.1-SNAPSHOT.jar
      COPY target/spring-cloud-eureka-0.0.1-SNAPSHOT.jar ./
      # 啟動 Jar 包
      CMD java -jar spring-cloud-eureka-0.0.1-SNAPSHOT.jar
      

      image.png-67.5kB

      3.4 定義 Kubernetes 資源

      本示例在 GitLab 創(chuàng)建的 Group 為 kubernetes,可以將其認為是一個項目,同一個項目可以部署至 Kubernetes 集群中同一個 Namespace 中,本示例為 kubernetes 命名空間。

      由于使用的是私有倉庫,因此也需要先配置拉取私有倉庫鏡像的密鑰:

      [root@k8s-master01 ~]# kubectl create ns kubernetes
      [root@k8s-master01 ~]# kubectl create secret docker-registry harborkey --docker-server=192.168.200.53 --docker-username=admin --docker-password=Harbor12345 --docker-email=1773464408@qq.com -n kubernetes
      
      [root@k8s-master01 ~]# vim spring-boot-project.yaml 
      [root@k8s-master01 ~]# cat spring-boot-project.yaml 
      apiVersion: v1
      kind: Service
      metadata:
        creationTimestamp: null
        labels:
          app: spring-boot-project
        name: spring-boot-project
        namespace: kubernetes
      spec:
        ports:            # 端口按照實際情況進行修改
        - name: web
          port: 8761
          protocol: TCP
          targetPort: 8761
        selector:
          app: spring-boot-project
        sessionAffinity: None
        type: ClusterIP
      status:
        loadBalancer: {}
      ---
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        creationTimestamp: null
        name: spring-boot-project
        namespace: kubernetes
      spec:
        ingressClassName: nginx
        rules:
        - host: spring-boot-project.test.com
          http:
            paths:
            - backend:
                service:
                  name: spring-boot-project
                  port:
                    number: 8761
              path: /
              pathType: ImplementationSpecific
      status:
        loadBalancer: {}
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        creationTimestamp: null
        labels:
          app: spring-boot-project        # Deployment 標簽,和流水線的 set -l 一致
        name: spring-boot-project         # Deployment 名稱
        namespace: kubernetes
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: spring-boot-project      # Pod 的標簽
        strategy:
          rollingUpdate:
            maxSurge: 1
            maxUnavailable: 0
          type: RollingUpdate
        template:
          metadata:
            creationTimestamp: null
            labels:
              app: spring-boot-project        # Pod 的標簽
          spec:
            affinity:
              podAntiAffinity:
                preferredDuringSchedulingIgnoredDuringExecution:
                - podAffinityTerm:
                    labelSelector:
                      matchExpressions:
                      - key: app
                        operator: In
                        values:
                        - spring-boot-project
                    topologyKey: kubernetes.io/hostname
                  weight: 100
            containers:
            - env:
              - name: TZ
                value: Asia/Shanghai
              - name: LANG
                value: C.UTF-8
              image: nginx        # 此處使用的 nginx 作為原始的鏡像,通過 Jenkins 構建并發(fā)版后,變成 Java 應用的鏡像
              imagePullPolicy: IfNotPresent
              lifecycle: {}
              livenessProbe:
                failureThreshold: 2
                initialDelaySeconds: 30
                periodSeconds: 10
                successThreshold: 1
                tcpSocket:
                  port: 8761      # 端口號和健康檢查按照實際情況進行修改
                timeoutSeconds: 2
              name: spring-boot-project   # 容器的名稱,需要和流水線 set 命令的容器名稱一致
              ports:
              - containerPort: 8761       # 端口號按照實際情況進行修改
                name: web
                protocol: TCP
              readinessProbe:
                failureThreshold: 2
                initialDelaySeconds: 30
                periodSeconds: 10
                successThreshold: 1
                tcpSocket:
                  port: 8761              # 端口號和健康檢查按照實際情況進行修改
                timeoutSeconds: 2
              resources:                  # 資源請求按照實際情況修改
                limits:
                  cpu: 994m
                  memory: 1170Mi
                requests:
                  cpu: 10m
                  memory: 55Mi
            dnsPolicy: ClusterFirst
            imagePullSecrets:
            - name: harborkey             # Harbor 倉庫密鑰,需要和上述創(chuàng)建的 Secret 一致
            restartPolicy: Always
            securityContext: {}
            serviceAccountName: default
      

      創(chuàng)建該資源 (Pod 無法啟動請忽略)

      [root@k8s-master01 ~]# kubectl create -f spring-boot-project.yaml
      [root@k8s-master01 ~]# kubectl get -f spring-boot-project.yaml
      NAME                          TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
      service/spring-boot-project   ClusterIP   10.107.107.79   <none>        8761/TCP   11s
      
      NAME                                            CLASS    HOSTS                          ADDRESS   PORTS   AGE
      ingress.networking.k8s.io/spring-boot-project   <none>   spring-boot-project.test.com             80      11s
      
      NAME                                  READY   UP-TO-DATE   AVAILABLE   AGE
      deployment.apps/spring-boot-project   0/1     1            0           10s
      

      3.5 創(chuàng)建 Jenkins 任務(Job)

      1、單擊首頁的創(chuàng)建任務(Job)選項(或者是 New Item)并配置任務信息,如圖所示:
      image.png-106.1kB

      2、輸入的 Job 的名稱(一般和 GitLab 的倉庫名字一致,便于區(qū)分),類型為 Pipeline,最后點擊 OK 即可:

      image.png-60.9kB
      image.png-61.8kB

      3、如果有如下報錯,關閉 Host Key 校驗:
      image.png-31.5kB

      去安全里禁用主機密鑰驗證,直接信任所有連接(盡在內網(wǎng)可信環(huán)境中使用)
      image.png-79.4kB

      4、創(chuàng)建完成后,點擊 Build Now(由于 Jenkins 參數(shù)由 Jenkinsfile 生成,所以第一次執(zhí)行流水線會失敗):
      image.png-60.5kB

      5、第一次構建結束后,可以看到 Build Now 變成了 Build with Parameters。點擊 Build with Parameters 后,可以讀取到 Git 倉庫的分支,之后可以選擇分支進行手動構建(后面的章節(jié)會介紹自動觸發(fā)):
      image.png-73.4kB

      6、選擇分支,之后點擊 Build,然后點擊進度條即可看到構建日志:
      image.png-68.7kB

      構建日志上部分為創(chuàng)建 Pod 的日志,可以看到 Pod 為 Agent 指定的 Pod
      image.png-79.9kB

      7、此時在 Kubernetes 中,會創(chuàng)建一個 Jenkins Slave 的 Pod:

      [root@k8s-master01 ~]# kubectl get po
      NAME                                      READY   STATUS              RESTARTS   AGE
      spring-boot-project-2-h155p-d68tz-jfbf2   0/4     ContainerCreating   0          84s
      

      8、待 Pod 啟動后,Pipeline 任務就會執(zhí)行,同時也可以點擊 Blue Ocean 更加直觀的流程:
      image.png-18.6kB

      9、如果是 Java 應用,同時可以看到 mvn 編譯的過程:
      image.png-213.2kB

      編譯結束后,可以看到制作鏡像的日志:
      image.png-74.6kB

      最后為發(fā)版至 Kubernetes:
      image.png-37.6kB

      10、Finished:SUCCESS 說明該流水線正常結束。接下來可以查看 Deployment 的鏡像:

      [root@k8s-master01 ~]# kubectl get deploy -n kubernetes spring-boot-project -oyaml | grep "image:"
              image: 192.168.200.53/kubernetes/spring-boot-project:jenkins-spring-boot-project-2-3ed977e
      
      [root@k8s-master01 ~]# kubectl get po -n kubernetes
      NAME                                   READY   STATUS    RESTARTS   AGE
      spring-boot-project-65b5fb956d-qrlc4   1/1     Running   0          13m
      

      image.png-65.3kB

      12、如果配置了域名,可以通過 域名訪問(或者通過 Service 訪問):
      image.png-133.1kB

      4、 自動化構建 Vue/H5 前端應用

      本節(jié)介紹自動化構建 Vue/H5 應用,其構建方式和自動化構建 Java 基本相同,重點是更改 Deployment、Jenkinsfile 和 Dockerfile 即可。

      前端應用測試項目地址:https://gitee.com/dukuan/vue-project.git,可以參考 Java 小節(jié)的方式,

      導入前端項目到 GitLab 中,當然也可以使用公司自己的項目。

      4.1 定義 Jenkinsfile

      Jenkinsfile 和 Java 項目并無太大區(qū)別,需要更改的位置如下:

      # 編譯鏡像改為 NodeJS
      name: "build"
      image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/node:lts"
      imagePullPolicy: "IfNotPresent"
      
      # 構建命令改為 npm
      npm install --registry=https://registry.npmmirror.com/
      npm run build
      
      # 代碼地址和服務名稱
      IMAGE_NAME = "vue-project"
      NAMESPACE = "kubernetes"
      TAG = ""
      GIT_URL = "git@192.168.200.55:kubernetes/vue-project.git"
      

      添加 Jenkinsfile 至項目根目錄
      image.png-137.3kB

      完整配置

      pipeline {
        agent {
          kubernetes {
            cloud 'kubernetes-study'
            slaveConnectTimeout 1200
            workspaceVolume persistentVolumeClaimWorkspaceVolume(claimName: "pipeline-cache", readOnly: false)
            yaml '''
      apiVersion: v1
      kind: Pod
      spec:
        restartPolicy: "Never"
        nodeSelector:
          build: "true"
        securityContext: {}
        volumes:
        - name: docker-registry-config
          configMap:
            name: docker-registry-config
        - name: "localtime"
          hostPath:
            path: "/usr/share/zoneinfo/Asia/Shanghai"
        - name: cache
          persistentVolumeClaim:
            claimName: cache
            readonly: false
        containers:
        - name: jnlp
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/jnlp-agent-docker:latest"
          imagePullPolicy: IfNotPresent
          args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
        - name: "build"
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/node:lts"
          imagePullPolicy: "IfNotPresent"
          command:
          - "cat"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
          - name: "cache"
            mountPath: "/root/.m2/"
            readOnly: false
        - name: "kaniko"
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/kaniko-executor:debug"
          imagePullPolicy: "IfNotPresent"
          command:
          - "sleep"
          args:
          - "99d"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
          - name: "docker-registry-config"
            mountPath: "/kaniko/.docker"
        - name: "kubectl"
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/kubectl:devops"
          imagePullPolicy: "IfNotPresent"
          command:
          - "cat"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
            '''
          }
        }
        stages {
          stage('Pulling Code') {
            parallel {
              stage('Pulling Code by Jenkins') {
                when {
                  expression {
                    env.gitlabBranch == null
                  }
                }
                steps {
                  git(changelog: true, poll: true, url: "${GIT_URL}", branch: "${BRANCH}", credentialsId: 'gitlab-key')
                  script {
                    COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                    TAG = BUILD_TAG + '-' + COMMIT_ID
                    println "Current branch is ${BRANCH}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
                  }
                }
              }
              stage('Pulling Code by trigger') {
                when {
                  expression {
                    env.gitlabBranch != null
                  }
                }
                steps {
                  git(url: "${GIT_URL}", branch: env.gitlabBranch, changelog: true, poll: true, credentialsId: 'gitlab-key')
                  script {
                    COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                    TAG = BUILD_TAG + '-' + COMMIT_ID
                    println "Current branch is ${env.gitlabBranch}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
                  }
                }
              }
            }
          }
          stage('Building') {
            steps {
              container(name: 'build') {
                sh """
                  npm install --registry=https://registry.npmmirror.com/
                  npm run build
                """
              }
            }
          }
          stage('Build for creating image') {
            steps {
              container(name: 'kaniko') {
                sh """
                  executor -d ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -c . --insecure --skip-tls-verify
                """
              }
            }
          }
          stage('Deploying to K8s') {
            environment {
              MY_KUBECONFIG = credentials('STUDY_CLUSTER_CONFIG')
            }
            steps {
              container(name: 'kubectl'){
                sh """
                  kubectl --kubeconfig $MY_KUBECONFIG set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -n $NAMESPACE
                """
              }
            }
          }
        }
        environment {
          COMMIT_ID = ""
          HARBOR_ADDRESS = "192.168.200.53"
          REGISTRY_DIR = "kubernetes"
          IMAGE_NAME = "vue-project"
          NAMESPACE = "kubernetes"
          TAG = ""
          GIT_URL = "git@192.168.200.55:kubernetes/vue-project.git"
        }
        parameters {
          gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
        }
      }
      

      4.2 定義 Dockerfile

      前端應用構建后一般會在 dist 文件下產生 html 文件,只需要拷貝到 nginx 的根目錄下即可:

      FROM crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/nginx:1.15
      COPY dist/* /usr/share/nginx/html/
      

      image.png-56.3kB

      4.3 定義 Kubernetes 資源

      對于 Kubernetes 的資源也是類似的,只需要更改資源名稱和端口號即可(加粗部分):

      [root@k8s-master01 ~]# vim vue-project.yaml 
      [root@k8s-master01 ~]# cat vue-project.yaml 
      apiVersion: v1
      kind: Service
      metadata:
        creationTimestamp: null
        labels:
          app: vue-project
        name: vue-project
        namespace: kubernetes
      spec:
        ports:
        - name: web
          port: 80
          protocol: TCP
          targetPort: 80
        selector:
          app: vue-project
        sessionAffinity: None
        type: ClusterIP
      status:
        loadBalancer: {}
      ---
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        creationTimestamp: null
        name: vue-project
        namespace: kubernetes
      spec:
        ingressClassName: nginx
        rules:
        - host: vue-project.test.com
          http:
            paths:
            - backend:
                service:
                  name: vue-project
                  port:
                    number: 80
              path: /
              pathType: ImplementationSpecific
      status:
        loadBalancer: {}
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        creationTimestamp: null
        labels:
          app: vue-project
        name: vue-project
        namespace: kubernetes
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: vue-project
        strategy:
          rollingUpdate:
            maxSurge: 1
            maxUnavailable: 0
          type: RollingUpdate
        template:
          metadata:
            creationTimestamp: null
            labels:
              app: vue-project
          spec:
            affinity:
              podAntiAffinity:
                preferredDuringSchedulingIgnoredDuringExecution:
                - podAffinityTerm:
                    labelSelector:
                      matchExpressions:
                      - key: app
                        operator: In
                        values:
                        - vue-project
                    topologyKey: kubernetes.io/hostname
                  weight: 100
            containers:
            - env:
              - name: TZ
                value: Asia/Shanghai
              - name: LANG
                value: C.UTF-8
              image: nginx
              imagePullPolicy: IfNotPresent
              lifecycle: {}
              livenessProbe:
                failureThreshold: 2
                initialDelaySeconds: 30
                periodSeconds: 10
                successThreshold: 1
                tcpSocket:
                  port: 80
                timeoutSeconds: 2
              name: vue-project
              ports:
              - containerPort: 80
                name: web
                protocol: TCP
              readinessProbe:
                failureThreshold: 2
                initialDelaySeconds: 30
                periodSeconds: 10
                successThreshold: 1
                tcpSocket:
                  port: 80
                timeoutSeconds: 2
              resources:
                limits:
                  cpu: 994m
                  memory: 1170Mi
                requests:
                  cpu: 10m
                  memory: 55Mi
            dnsPolicy: ClusterFirst
            imagePullSecrets:
            - name: harborkey
            restartPolicy: Always
            securityContext: {}
            serviceAccountName: default
      

      創(chuàng)建該資源 (Pod 無法啟動請忽略)

      [root@k8s-master01 ~]# kubectl create -f vue-project.yaml 
      [root@k8s-master01 ~]# kubectl get -f vue-project.yaml 
      NAME                  TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
      service/vue-project   ClusterIP   10.96.136.68   <none>        80/TCP    6s
      
      NAME                                    CLASS    HOSTS                  ADDRESS   PORTS   AGE
      ingress.networking.k8s.io/vue-project   <none>   vue-project.test.com             80      5s
      
      NAME                          READY   UP-TO-DATE   AVAILABLE   AGE
      deployment.apps/vue-project   0/1     1            0           5s
      

      4.4 創(chuàng)建 Jenkins Job

      創(chuàng)建和構建 Job 和 Java 步驟類似,只需要變更名稱和倉庫地址即可,并且可以通過之前的 Job 進行復制:
      image.png-82.1kB

      image.png-65.7kB

      執(zhí)行構建:
      image.png-41.4kB

      構建成功如下所示:
      image.png-23.1kB

      發(fā)布成功后可以在瀏覽器 訪問該域名(或者使用 Service 訪問):
      image.png-11.9kB

      5、自動化構建 Golang 項目

      上述演示了 Java 和前端應用的自動化,接下來演示一下對于 Golang 的自動化構建,本次示例的代碼地址:https://gitee.com/dukuan/go-project.git。

      5.1 定義 Jenkinsfile

      本次示例的 Jenkinsfile 和之前的也無太大區(qū)別,需要的更改的位置是構建容器的鏡像、緩存目錄、Git 地址和項目名稱:

      # 構建鏡像改為 golang,需要根據(jù)實際情況更改版本
      name: "build"
      image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/golang:1.15"
      imagePullPolicy: "IfNotPresent"
      ...
      volumeMounts:
      ...
      - name: "cache"
        mountPath: "/go/pkg/"  # 緩存目錄為/go/pkg/,執(zhí)行 go build 時下載的依賴包會緩存在該目錄
        readOnly: false
      
      # 構建命令
      export GO111MODULE=on
      export CGO_ENABLED=0
      go env -w GOPROXY=https://goproxy.cn,direct
      go build
      
      # 代碼地址和服務名稱
      IMAGE_NAME = "go-project"
      NAMESPACE = "kubernetes"
      TAG = ""
      GIT_URL = "git@192.168.200.55:kubernetes/go-project.git"
      

      image.png-137.8kB

      完整文件:

      pipeline {
        agent {
          kubernetes {
            cloud 'kubernetes-study'
            slaveConnectTimeout 1200
            workspaceVolume persistentVolumeClaimWorkspaceVolume(claimName: "pipeline-cache", readOnly: false)
            yaml '''
      apiVersion: v1
      kind: Pod
      spec:
        restartPolicy: "Never"
        nodeSelector:
          build: "true"
        securityContext: {}
        volumes:
        - name: docker-registry-config
          configMap:
            name: docker-registry-config
        - name: "localtime"
          hostPath:
            path: "/usr/share/zoneinfo/Asia/Shanghai"
        - name: cache
          persistentVolumeClaim:
            claimName: cache
            readonly: false
        containers:
        - name: jnlp
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/jnlp-agent-docker:latest"
          imagePullPolicy: IfNotPresent
          args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
        - name: "build"
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/golang:1.15"
          imagePullPolicy: "IfNotPresent"
          command:
          - "cat"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
          - name: "cache"
            mountPath: "/go/pkg/"
            readOnly: false
        - name: "kaniko"
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/kaniko-executor:debug"
          imagePullPolicy: "IfNotPresent"
          command:
          - "sleep"
          args:
          - "99d"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
          - name: "docker-registry-config"
            mountPath: "/kaniko/.docker"
        - name: "kubectl"
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/kubectl:devops"
          imagePullPolicy: "IfNotPresent"
          command:
          - "cat"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
          volumeMounts:
          - name: "localtime"
            mountPath: "/etc/localtime"
            readOnly: false
            '''
          }
        }
        stages {
          stage('Pulling Code') {
            parallel {
              stage('Pulling Code by Jenkins') {
                when {
                  expression {
                    env.gitlabBranch == null
                  }
                }
                steps {
                  git(changelog: true, poll: true, url: "${GIT_URL}", branch: "${BRANCH}", credentialsId: 'gitlab-key')
                  script {
                    COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                    TAG = BUILD_TAG + '-' + COMMIT_ID
                    println "Current branch is ${BRANCH}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
                  }
                }
              }
              stage('Pulling Code by trigger') {
                when {
                  expression {
                    env.gitlabBranch != null
                  }
                }
                steps {
                  git(url: "${GIT_URL}", branch: env.gitlabBranch, changelog: true, poll: true, credentialsId: 'gitlab-key')
                  script {
                    COMMIT_ID = sh(returnStdout: true, script: "git log -n 1 --pretty=format:'%h'").trim()
                    TAG = BUILD_TAG + '-' + COMMIT_ID
                    println "Current branch is ${env.gitlabBranch}, Commit ID is ${COMMIT_ID}, Image TAG is ${TAG}"
                  }
                }
              }
            }
          }
          stage('Building') {
            steps {
              container(name: 'build') {
                sh """
                  export GO111MODULE=on
                  export CGO_ENABLED=0
                  go env -w GOPROXY=https://goproxy.cn,direct
                  go build
                """
              }
            }
          }
          stage('Build for creating image') {
            steps {
              container(name: 'kaniko') {
                sh """
                  executor -d ${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -c . --insecure --skip-tls-verify
                """
              }
            }
          }
          stage('Deploying to K8s') {
            environment {
              MY_KUBECONFIG = credentials('STUDY_CLUSTER_CONFIG')
            }
            steps {
              container(name: 'kubectl'){
                sh """
                  kubectl --kubeconfig $MY_KUBECONFIG set image deploy -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME}:${TAG} -n $NAMESPACE
                """
              }
            }
          }
        }
        environment {
          COMMIT_ID = ""
          HARBOR_ADDRESS = "192.168.200.53"
          REGISTRY_DIR = "kubernetes"
          IMAGE_NAME = "go-project"
          NAMESPACE = "kubernetes"
          TAG = ""
          GIT_URL = "git@192.168.200.55:kubernetes/go-project.git"
        }
        parameters {
          gitParameter(branch: '', branchFilter: 'origin/(.*)', defaultValue: '', description: 'Branch for build and deploy', name: 'BRANCH', quickFilterEnabled: false, selectedValue: 'NONE', sortMode: 'NONE', tagFilter: '*', type: 'PT_BRANCH')
        }
      }
      

      5.2 定義 Dockerfile

      和之前不一樣的地方是,Golang 編譯后生成的是一個二進制文件,可以直接執(zhí)行,所以底層鏡像設置為 alpine 或者其它的小鏡像即可:

      FROM crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/alpine-glibc:alpine-3.9
      # 如果定義了單獨的配置文件,可能需要拷貝到鏡像中
      # COPY conf/ ./conf
      # 包名按照實際情況進行修改
      COPY ./go-project ./
      # 啟動該應用
      ENTRYPOINT [ "./go-project"]
      

      image.png-70.3kB

      5.3 定義 Kubernetes 資源

      [root@k8s-master01 ~]# vim go-project.yaml 
      [root@k8s-master01 ~]# cat go-project.yaml 
      apiVersion: v1
      kind: Service
      metadata:
        creationTimestamp: null
        labels:
          app: go-project
        name: go-project
        namespace: kubernetes
      spec:
        ports:
        - name: web
          port: 8080
          protocol: TCP
          targetPort: 8080
        selector:
          app: go-project
        sessionAffinity: None
        type: ClusterIP
      status:
        loadBalancer: {}
      ---
      apiVersion: networking.k8s.io/v1
      kind: Ingress
      metadata:
        creationTimestamp: null
        name: go-project
        namespace: kubernetes
      spec:
        ingressClassName: nginx
        rules:
        - host: go-project.test.com
          http:
            paths:
            - backend:
                service:
                  name: go-project
                  port:
                    number: 8080
              path: /
              pathType: ImplementationSpecific
      status:
        loadBalancer: {}
      ---
      apiVersion: apps/v1
      kind: Deployment
      metadata:
        creationTimestamp: null
        labels:
          app: go-project
        name: go-project
        namespace: kubernetes
      spec:
        replicas: 1
        selector:
          matchLabels:
            app: go-project
        strategy:
          rollingUpdate:
            maxSurge: 1
            maxUnavailable: 0
          type: RollingUpdate
        template:
          metadata:
            creationTimestamp: null
            labels:
              app: go-project
          spec:
            affinity:
              podAntiAffinity:
                preferredDuringSchedulingIgnoredDuringExecution:
                - podAffinityTerm:
                    labelSelector:
                      matchExpressions:
                      - key: app
                        operator: In
                        values:
                        - go-project
                    topologyKey: kubernetes.io/hostname
                  weight: 100
            containers:
            - env:
              - name: TZ
                value: Asia/Shanghai
              - name: LANG
                value: C.UTF-8
              image: nginx
              imagePullPolicy: IfNotPresent
              lifecycle: {}
              livenessProbe:
                failureThreshold: 2
                initialDelaySeconds: 30
                periodSeconds: 10
                successThreshold: 1
                tcpSocket:
                  port: 8080
                timeoutSeconds: 2
              name: go-project
              ports:
              - containerPort: 8080
                name: web
                protocol: TCP
              readinessProbe:
                failureThreshold: 2
                initialDelaySeconds: 30
                periodSeconds: 10
                successThreshold: 1
                tcpSocket:
                  port: 8080
                timeoutSeconds: 2
              resources:
                limits:
                  cpu: 994m
                  memory: 1170Mi
                requests:
                  cpu: 10m
                  memory: 55Mi
            dnsPolicy: ClusterFirst
            imagePullSecrets:
            - name: harborkey
            restartPolicy: Always
            securityContext: {}
            serviceAccountName: default
      

      創(chuàng)建該資源 (Pod 無法啟動請忽略)

      [root@k8s-master01 ~]# kubectl create -f go-project.yaml 
      [root@k8s-master01 ~]# kubectl get -f go-project.yaml 
      NAME                 TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
      service/go-project   ClusterIP   10.97.238.110   <none>        8080/TCP   8s
      
      NAME                                   CLASS    HOSTS                 ADDRESS   PORTS   AGE
      ingress.networking.k8s.io/go-project   <none>   go-project.test.com             80      8s
      
      NAME                         READY   UP-TO-DATE   AVAILABLE   AGE
      deployment.apps/go-project   0/1     1            0           8s
      

      5.4 創(chuàng)建 Jenkins Job

      創(chuàng)建和構建 Job 和 Java 步驟類似,只需要變更名稱和倉庫地址即可,并且可以通過之前的 Job 進行復制:
      image.png-85.2kB

      image.png-77.9kB

      執(zhí)行構建
      image.png-101.7kB

      構建成功如下所示:
      image.png-22kB

      之后即可 訪問該應用(通過 Ingress 和 Service 都可):
      image.png-73.3kB

      6、自動觸發(fā)構建

      之前的構建都是采用手動選擇分支進行構建的,實際使用時,項目可能有很多,如果都是手動觸發(fā)可能比較消耗人力。所以推薦可以按需配置自動觸發(fā),即提交代碼后自動觸發(fā) Jenkins 進行構建任務。

      本次用 Go 項目進行演示。首先找到 Go 項目的 Job,點擊 Configure
      image.png-55.4kB

      之后勾選 Build when
      image.png-59kB

      選擇需要觸發(fā)的分支,并生成觸發(fā) token:
      image.png-75.3kB

      最后點擊 Save 即可。
      接下來配置 GitLab,允許觸發(fā)外部接口。點擊 Gitlab 的 Admin→Settings→Network:
      image.png-83kB

      image.png-122.9kB

      保存后,找到 Go 項目,點擊 Settings→WebHooks:
      image.png-252.5kB

      新頁面輸入點擊 Add new webhook
      image.png-57.3kB

      并輸入 Jenkins 接口地址和 Token:
      image.png-111.1kB
      image.png-63.6kB

      確認無誤后,點擊 Add webhook:
      image.png-22.4kB

      之后下方會添加一個新的 Project Hooks,可以點擊 Test 進行 Push 測試:
      image.png-113.5kB

      點擊后,即可在 Jenkins 頁面看到任務被觸發(fā):
      image.png-19kB

      也可以通過 Blue Ocean 看到是自動觸發(fā)的 stage 被執(zhí)行:
      image.png-29.9kB

      以上就是通過 GitLab 的事件觸發(fā) Jenkins 任務,在實際使用時,此功能非常常用,一般會用于開發(fā)、測試等環(huán)境,省去了手動構建的過程。而在 UAT 和生產環(huán)境,一般不需要再次構建,而是選擇其它環(huán)境產生的鏡像進行發(fā)版,接下來看一下如何進行不構建進行發(fā)版。

      7、一次構建多次部署

      在企業(yè)內部署服務時,往往每個項目都有多個環(huán)境,比如 dev、sit、prod 等。但是并非每個環(huán)境部署時,都需要進行重新編譯、構建鏡像等,此時可以把 dev 的鏡像直接部署至 sit 和 prod。

      image.png-387.6kB

      創(chuàng)建一個新的 Job,名字為 go-project-uat,類型 Pipeline:
      image.png-83.7kB

      點擊頁面的 This Project is parameterized(參數(shù)化構建):
      image.png-98.3kB

      選擇參數(shù)類型為 Image Tag Parameter(需要安裝 Image Tag 插件),之后定義 Name 為變量的名稱,Iamge Name 為 Harbor 的目錄和鏡像名稱:
      image.png-54.5kB

      點擊 Advance,輸入倉庫的地址,注意如果配置了證書,需要配置 https:
      image.png-48.2kB

      點擊 Configure,添加 Pipeline 腳本:
      image.png-73.8kB

      pipeline {
        agent {
          kubernetes {
            cloud 'kubernetes-study'
            slaveConnectTimeout 1200
            yaml '''
      apiVersion: v1
      kind: Pod
      spec:
        containers:
        - name: jnlp
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/jnlp-agent-docker:latest"
          imagePullPolicy: IfNotPresent
          args: [\'$(JENKINS_SECRET)\', \'$(JENKINS_NAME)\']
        - name: "kubectl"
          image: "crpi-q1nb2n896zwtcdts.cn-beijing.personal.cr.aliyuncs.com/ywb01/kubectl:devops"
          imagePullPolicy: "IfNotPresent"
          command:
          - "cat"
          env:
          - name: "LANGUAGE"
            value: "en_US:en"
          - name: "LC_ALL"
            value: "en_US.UTF-8"
          - name: "LANG"
            value: "en_US.UTF-8"
          tty: true
        restartPolicy: "Never" 
            '''
          }
        }
        stages {
          stage('Deploy') {
            environment {
              MY_KUBECONFIG = credentials('STUDY_CLUSTER_CONFIG')
            }
            steps {
              container(name: 'kubectl'){
                sh """
                  echo ${IMAGE_TAG}
                  kubectl --kubeconfig=${MY_KUBECONFIG} set image deployment -l app=${IMAGE_NAME} ${IMAGE_NAME}=${HARBOR_ADDRESS}/${IMAGE_TAG} -n ${NAMESPACE}
                  kubectl --kubeconfig=${MY_KUBECONFIG} get po -l app=${IMAGE_NAME} -n ${NAMESPACE} -w
                """
              }
            }
          }
        }
        environment {
          HARBOR_ADDRESS = "192.168.200.53"
          IMAGE_NAME = "go-project"
          NAMESPACE = "kubernetes"
          TAG = ""
        }
      }
      

      點擊 Build with Parameters:
      image.png-74.5kB

      選擇一個鏡像,點擊 Build:
      image.png-60.3kB

      即可看到是直接將鏡像版本更新至 Kubernetes,并無構建過程,可以省下很多時間。該流水線也可以選擇之前的版本進行回滾操作。
      image.png-115kB

      8、集成 Helm 發(fā)布

      8.1 生成 Helm 模板

      雖然上述已經(jīng)借助流水線實現(xiàn)了服務的自動發(fā)版,但是針對 Kubernetes 的資源依舊是提前手動創(chuàng)建,接下來我們借助 Helm Charts 實現(xiàn)微服務資源的自動創(chuàng)建。

      首先創(chuàng)建一個 Helm Chart 的模板:

      [root@k8s-master01 ~]# helm create chart-template
      

      接下來對模板稍加修改,使其變成一個通用的模板。首先更改默認拉取鏡像的 Secret:

      [root@k8s-master01 ~]# cd chart-template
      [root@k8s-master01 chart-template]# vim values.yaml
      [root@k8s-master01 chart-template]# sed -n "19,20p;26p;28p;79,85p;98,103p" values.yaml 
      imagePullSecrets:
      - name: harborkey
      serviceAccount:
        create: false
      resources:
        limits:
          cpu: 1000m
          memory: 512Mi
        requests:
          cpu: 100m
          memory: 128Mi
      livenessProbe:
        tcpSocket:
          port: http
      readinessProbe:
        tcpSocket:
          port: http
      

      8.2 流水線集成 Helm

      接下來使用 go-project 為例,生成一個 go-project 的 Chart:

      [root@k8s-master01 ~]# git clone git@192.168.200.55:kubernetes/go-project.git
      [root@k8s-master01 ~]# cp -rp chart-template/ go-project/chart
      

      把模板上傳至該項目的 Git 倉庫,首先把代碼下載到本地,然后把模板放到代碼 chart 目錄下:

      [root@k8s-master01 ~]# cd go-project
      [root@k8s-master01 go-project]# git add .
      [root@k8s-master01 go-project]# git commit -am "添加chart模板"
      [root@k8s-master01 go-project]# git push origin master
      

      創(chuàng)建一個新的uat環(huán)境

      [root@k8s-master01 ~]# kubectl create ns kubernetes-uat
      [root@k8s-master01 ~]# kubectl create secret docker-registry harborkey --docker-server=192.168.200.53 --docker-username=admin --docker-password=Harbor12345 --docker-email=1773464408@qq.com -n kubernetes-uat
      

      接下來按需修改values模板文件
      image.png-106.8kB

      image.png-139kB

      修改Jenkinsfile模板文件
      image.png-74.5kB

      image.png-137.9kB

      cd chart
      helm upgrade \
      --kubeconfig $MY_KUBECONFIG \
      --install $IMAGE_NAME . \
      --namespace ${NAMESPACE} \
      --create-namespace \
      --set fullnameOverride=$IMAGE_NAME \
      --set nameOverride=$IMAGE_NAME \
      --set replicaCount=1 \
      --set image.repository=${HARBOR_ADDRESS}/${REGISTRY_DIR}/${IMAGE_NAME} \
      --set image.tag=${TAG}
      

      提交代碼后,重新發(fā)版:
      image.png-128.8kB

      自動觸發(fā)構建完成發(fā)布
      image.png-30.5kB

      查看資源:

      [root@k8s-master01 ~]# kubectl get po -n kubernetes-uat
      NAME                          READY   STATUS    RESTARTS   AGE
      go-project-6c7bd8d985-qx7mv   1/1     Running   0          45s
      
      [root@k8s-master01 ~]# helm list -n kubernetes-uat
      NAME      	NAMESPACE     	REVISION	UPDATED                                	STATUS  	CHART           	APP VERSION
      go-project	kubernetes-uat	1       	2025-09-24 00:06:45.503610996 +0800 CST	deployed	go-project-0.1.0	1.16.0  
      

      此博客來源于:https://edu.51cto.com/lecturer/11062970.html

      主站蜘蛛池模板: 久久久久久亚洲精品成人| 国产高清国产精品国产专区| 中文字幕日韩熟女av| 亚洲一区二区三级av| 国产福利酱国产一区二区| 国产精品成| 久治县| 免费A级毛片樱桃视频| 蜜芽久久人人超碰爱香蕉| 日韩女同在线二区三区| 亚洲香蕉免费有线视频| 中文字幕无码专区一VA亚洲V专| 99国产精品白浆在线观看免费| 改则县| 午夜高清福利在线观看| 日韩大片高清播放器| 人妻人人妻a乱人伦青椒视频| 无套内谢少妇一二三四| 久久精品国产99国产精品| 乱女乱妇熟女熟妇综合网| 亚洲国产精品无码一区二区三区 | 久久人爽人人爽人人片av| 亚洲不卡一区三区三区四| 日本一区不卡高清更新二区| 国产成人无码免费看视频软件| 亚洲女女女同性video| 狠狠躁夜夜躁人人爽天天5| 自拍偷自拍亚洲一区二区| 99国产欧美另类久久久精品| 亚洲国产日韩一区三区| 国产成人免费永久在线平台| 国产精品亚洲二区在线播放| 最新亚洲人成网站在线观看| 绥宁县| 四虎在线播放亚洲成人| 午夜精品久久久久久久爽| 丝袜国产一区av在线观看| 蜜臀av久久国产午夜福利软件| 精品人妻中文无码av在线 | 亚洲国产在一区二区三区| 欧美日本精品一本二本三区|