0. 引言

在DevOps实战笔记–2中,我们在node2中运行了SonarQube,并简单在项目与Jenkins中使用了SonarQube服务。随后借助私有仓库Harbor完成了自动化项目部署的相关配置。此时我们已掌握了自动化CI/CD的相关原理,本篇将在此基础上继续进行讲解。

1. Jenkins流水线任务

当我们采用自由风格构建项目时,项目中的每个步骤都需要进行单独配置,而且构建时每一步之间没有明确的区分,若出现问题,需要单独查找控制台,定位起来较为困难。于是我们在此引入Jenkins流水线任务,此任务类型可以使项目的执行过程可视化,使问题的定位过程变得更加方便。在使用流水线任务时,我们通过Jenkinsfile对任务进行统一的配置与管理。通常,我们为方便后期维护,会选择将Jenkinsfile放在项目中。所以,通过流水线构建项目的方式往往较其他方式的操作更为灵活与简便。

Jenkinsfile实际上是一种使用Groovypipeline脚本。pipeline支持两种模式,分别为声明式pipeline脚本式pipeline,二者都支持生成Jenkinsfile,本次在项目中维护的是声明式pipeline。脚本中的函数按层级递归的顺序包括:pipelineagentenviromentstagesstagesteps

其中,pipeline块包含脚本中所有的声明,其他的块都需要写在pipeline{}中;
agent指明了pipeline执行的节点,在本次实验中,我们使用agent any使pipeline自由选择;
enviroment块声明了全局变量,这些变量可以在脚本的任何地方使用;
stages由若干stage组成,包含了脚本实现的逻辑,且每个脚本中只应有一个stages
stage包含了脚本逻辑的某一步;
steps该步骤的具体指令写在steps中。

综上所述,结合上篇文章的分享,我们欲将先前配置好的工作流程按序加入流水线任务,需要在脚本中完成以下几个逻辑:
注意,以下编写的脚本将放置于项目中。

1.1 从Git仓库拉取项目

回到Jenkins流水线任务,在任务配置中添加Git参数化构建用于代码的版本控制,自定义参数名为tag,将默认值设为origin/master,在流水线配置中设置从仓库拉取脚本:pipeline script from SCM,填入仓库地址。

进入语法生成器,选择checkout示例,填入Git仓库地址,设置分支后生成流水线语法:

checkout scmGit(branches: [[name: '*/master']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.140.110:8929/root/springtest.git']])

注意此时生成的语法中,默认拉取的是最新的版本,我们需要根据先前设定好的构建参数tag来控制拉取代码的版本:

checkout scmGit(branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.140.110:8929/root/springtest.git']])

将生成好的语法加入脚本中。

1.2 使用Maven构建项目

在语法生成器中选择shell范例,输入Maven构建命令,此时的命令需要指明命令所在的目录,以命令目录/命令的形式写入,此时使用的是Jenkins容器内部的Maven:

sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'

1.3 使用SonarQube检测代码质量

仍然使用语法生成器,使用Jenkins容器内部的sonar-scanner进行检测:

注意,检测指令中应当指明projectnameprojectKey(K大写)、java.binarieslogin,密钥仍在SonarQube中生成,填入login

sh '/var/jenkins_home/sonar-scanner/bin/sonar-scanner -Dsonar.source=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectkey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=11a2c19d7fe09394daf29578df2c5379787a77b3'

1.4 使用Docker制作镜像

回到语法生成器,输入命令进行docker镜像的制作:

注意:镜像命名不能使用大写,且在此之前,修改用户组并赋予权限的docker相关文件在容器内部的映射是必需的

sh '''mv ./target/*.jar ./docker/
docker build -t ${JOB_NAME}:${tag} ./docker/'''

1.5 推送镜像至Harbor

根据操作属性,我们仍需要使用语法生成器,在其中完成Harbor服务的登录,镜像的规范化命名并进行镜像的推送:

sh '''docker login -u ${harbor_username} -p ${harbor_passwd} ${harborURL}
docker tag ${JOB_NAME}:${tag} ${harborURL}/${harbor_repo}/${JOB_NAME}:${tag}
docker push ${harborURL}/${harbor_repo}/${JOB_NAME}:${tag}'''

1.6 SSH发布

在语法生成器中选择sshPublisher工具,输入执行脚本命令即可。

sshPublisher(publishers: [sshPublisherDesc(configName: 'test', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "deploy.sh $harborURL $harbor_repo $JOB_NAME $tag $container_port $host_port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])

此时,一个完备的pipeline任务就完成了。

完整的pipeline脚本如下:

pipeline{
    agent any
    
    environment {
        harbor_username='admin'
        harbor_passwd='Harbor12345'
        harborURL='192.168.140.120:80'
        harbor_repo='repo'
    }

    stages{
        stage("Pulling code from git repository"){
            steps{
                checkout scmGit(branches: [[name: '${tag}']], extensions: [], userRemoteConfigs: [[url: 'http://192.168.140.110:8929/root/springtest.git']])
            }
        }

        stage("Maven build the project"){
            steps{
                sh '/var/jenkins_home/maven/bin/mvn clean package -DskipTests'
            }
        }

        stage("Sonarqube detect the project"){
            steps{
                sh '/var/jenkins_home/sonar_scanner/bin/sonar-scanner -Dsonar.source=./ -Dsonar.projectname=${JOB_NAME} -Dsonar.projectKey=${JOB_NAME} -Dsonar.java.binaries=target/ -Dsonar.login=120db8b4f1ec457b9a9a6765f123a3540fac1707'
            }
        }

        stage("Docker make image"){
            steps{
                sh '''mv ./target/*.jar ./docker/
docker build -t springtest:${tag} ./docker/'''
            }
        }

        stage("Push image to harbor"){
            steps{
                sh '''docker login -u ${harbor_username} -p ${harbor_passwd} ${harborURL}
docker tag ${JOB_NAME}:${tag} ${harborURL}/${harbor_repo}/${JOB_NAME}:${tag}
docker push ${harborURL}/${harbor_repo}/${JOB_NAME}:${tag}'''
            }
        }

        stage("Publish over SSH"){
            steps{
                sshPublisher(publishers: [sshPublisherDesc(configName: 'test', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: "deploy.sh $harborURL $harbor_repo $JOB_NAME $tag $container_port $host_port", execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
            }
        }
    }

}

除此之外,我们仍可以在pipeline脚本中追加更多的信息,例如,我们可以配置项目构建完成后使用钉钉来发送通知。具体操作如下:

1.在钉钉工作群中新建一个群机器人,获得钉钉服务的Webhook;
2.在Jenkins插件库中下载Dingtalk插件;
3.在Jenkins全局设置中找到钉钉,配置刚刚获取到的Webhook;
4.在语法生成器中,选择dingtalk,查看当前版本Dingtalk插件使用的函数;
5.回到项目pipeline脚本中,在整体stages外部添加一个post请求,并指明分情况指明构建信息,Dingtalk v2.4.4版本的post示例如下:

post {
        success {
            dingtalk (
                robot: 'Jenkins-DingDing',
                type:'MARKDOWN',
                title: "success: ${JOB_NAME}",
                text: ["- 成功构建:${JOB_NAME}项目!\n- 版本:${tag}\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"]
            )
        }
        failure {
            dingtalk (
                robot: 'Jenkins-DingDing',
                type:'MARKDOWN',
                title: "fail: ${JOB_NAME}",
                text: ["- 失败构建:${JOB_NAME}项目!\n- 版本:${tag}\n- 持续时间:${currentBuild.durationString}\n- 任务:#${JOB_NAME}"]
            )
        }
    }

!!!注意!!! 题主在实验过程中遇到了如下问题:

devops流水线时序图 devops流水线部署_devops流水线时序图


在pipeline项目执行至最后一步stage时,即执行至Publish Over SSH时,会遇到Jenkins + Harbor容器崩溃并不复存在的问题,题主在排错时初步检查:
1.崩溃时虚拟机内存占用80%左右,并未崩内存;
2.在项目pipeline脚本以及全局SSH设置中修改相应超时时间无效
除此之外,崩溃后偶尔遇到/var/run/docker.sock权限被覆写的情况,以上问题均无解决方案,希望广大网友若有思路请及时联系uweiqingnian@126.com或在评论区指出.

2. K8S

2.1 K8S简介

Kubernetes是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes的目标是让部署容器化的应用简单并且高效(powerful),Kubernetes提供了应用部署,规划,更新,维护的一种机制。

Kubernetes一个核心的特点就是能够自主的管理容器来保证云平台中的容器按照用户的期望状态运行着,管理员可以加载一个微型服务,让规划器来找到合适的位置,同时,Kubernetes也系统提升工具以及人性化方面,让用户能够方便的部署自己的应用。

Kubernetes主要能帮助我们完成:

服务发现和负载均衡:
Kubernetes可以使用 DNS 名称或自己的 IP 地址公开容器,如果进入容器的流量很大, Kubernetes可以负载均衡并分配网络流量,从而使部署稳定。

存储编排:
Kubernetes允许你自动挂载你选择的存储系统,比如本地存储,类似Docker的数据卷。

自动部署和回滚:
你可以使用Kubernetes描述已部署容器的所需状态,它可以以受控的速率将实际状态 更改为期望状态。Kubernetes会自动帮你根据情况部署创建新容器,并删除现有容器给新容器提供资源。

自动完成装箱计算:
Kubernetes允许你设置每个容器的资源,比如CPU和内存。

自我修复:
Kubernetes重新启动失败的容器、替换容器、杀死不响应用户定义的容器,并运行状况检查的容器。

秘钥与配置管理:
Kubernetes允许你存储和管理敏感信息,例如密码、OAuth 令牌和 ssh 密钥。你可以在不重建容器镜像的情况下部署和更新密钥和应用程序配置,也无需在堆栈配置中暴露密钥。

2.2 Kubernetes架构

Kubernetes搭建需要至少两个节点,一个Master负责管理,一个Slave搭建在工作服务器上负责分配。

devops流水线时序图 devops流水线部署_运维_02


从图中可以看到各个组件的基本功能:

API Server:作为K8s通讯的核心组件,K8s内部交互以及接收发送指令的组件。
controller-manager:作为K8s的核心组件,主要做资源调度,根据集群情况分配资源
etcd:一个key-value的数据库,存储存储集群的状态信息
scheduler:负责调度每个工作节点
cloud-controller-manager:负责调度其他云服务产品
kubelet:管理Pods上面的容器。
kube-proxy:负责处理其他Slave或客户端的请求。
Pod:可以理解为就是运行的容器

以上资料均参考互联网.