| 时间 | 标题 | 内容 |
|---|---|---|
上午 下午 |
VM部署实践 |
1. VM虚机/云主机场景下的CD流水线实现 2. 使用Shell编写服务启动脚本 3. Jenkins 集成Ansible 实现应用发布 4. 部署策略实现:蓝绿发布、灰度发布 |
本次课共享库代码: devops-library-service-day5-init-version.tar.gz
env环境库代码和服务启动脚本:anyops-env-master.tar.gz
0. 准备工作项目标准化
公司里面要使用流水线要做持续集成CI/CD的项目越来越多,这对流水线的设计和开发有不同的要求。我们经常听到用户的反馈:
各种不同语言的技术栈, 如何使流水线适配呢? 从不同技术栈维护一套流水线模版,到我们使用共享库进行统一的管理和维护。
对于不同的项目,大家管理代码的方式也不同。可能还有一部分用户在使用Svn等不同的版本控制系统。
- 不同的项目,开发模式也不太一样, 编译构建工具不同,发布的方式也有不同的地方…
等等,不止上面的问题。所以在做流水线的使用应该提前把项目团队的规范定义好, 这样后期项目改造后可以直接集成CI/CD流水线。更加便捷。
跟进项目团队信息
| 信息项 | 描述 |
|---|---|
| 业务简称/编号 | anyops |
| 开发模式 | 特性分支开发,版本分支发布,主干分支作为最新代码 |
项目类型与构建方式 |
前端: vue项目, npm打包, 制品目录 dist |
| 后端:springboot项目, maven打包, 制品目录 target | |
发布主机环境(vm) |
LB: 192.168.1.200 |
| Server: 192.168.1.230~192.168.1.232 |
制定项目CI/CD规范
通过上面的信息,我们采用如下规范:
| 工具链 | |
|---|---|
| GitLab 代码库 | 仓库组: anyops |
| 项目仓库后端 anyops-devops-service 前端 anyops-devops-ui | |
| Jenkins作业 | 文件夹: anyops |
| 作业命名: 后端 anyops-devops-service 前端 anyops-devops-ui | |
CI构建规范 |
前端项目采用npm打包后统一放到dist目录下, 静态文件以tgz打包。 |
| 后端项目采用maven打包后统一放到target目录下,以jar包。 | |
Sonar代码报告 |
前端项目: anyops/anyops-devops-ui 后端项目: anyops/anyops-devops-service |
| 项目团队可以使用anyops命名的自定义质量规则和质量阈。 | |
Nexus制品库目录 |
com/anyops/anyops-devops-service/version/anyops-devops-service-version.jar |
| com/anyops/anyops-devops-ui/version/anyops-devops-ui-version.jar | |
| 版本: 分割release分支获取版本号 | |
| 发布规范 | 用户输入版本,下载制品库,使用脚本启动服务。 |
GitLab代码库:
Jenkins作业:
Sonar代码报告:
质量规则
质量阈
Nexus制品库目录:(raw)
CI/CD流水线设计
总体目标:
我们将CI和CD分成两条流水线作业。
- CI作业: 用户输入版本分支后下载代码,进行构建扫描最终将制品上传到制品仓库, 生成版本文件。
- CD作业: 用户输入发布版本和选择要发布的主机IP后,下载制品,将制品和服务启动脚本cp到目标机器的发布目录, 远程执行启动脚本启动服务并进行健康检查。
1. CI流水线的设计与实践
此次项目我们使用了jenkins共享库来完成最佳实践。 jenkinsfile第一行就是要导入我们的共享库。@Library("devopslib@master") _
我们在共享库中编写了一些特定的处理类:
- src/org/devops/mytools.groovy 存放小工具(代码下载, 邮件通知)
- src/org/devops/builds.groovy 构建类工具
- src/org/devops/sonarqube.groovy 代码扫描工具
- src/org/devops/artifacts.groovy 制品管理工具
- src/org/devops/gitlab.groovy 版本控制系统接口操作工具
在Jenkinsfile中导入这些类
def mytools = new org.devops.mytools()def builds = new org.devops.builds()def sonar = new org.devops.sonarqube()def artifacts = new org.devops.artifacts()def gitlab = new org.devops.gitlab()
有些参数需要用户在Jenkins页面构建前填写的:
- 用户需要在jenkins作业中配置好要构建的代码仓库的地址。(一般不需要改, 可以使用选项参数)
- 每次构建需要输入此代码库要构建的代码分支。 (字符串参数)
- 选择本次构建要使用的构建工具(选项参数maven、ant、gradle、npm)
- 用户自行选择是否跳过代码扫描(选项参数 true、false)
//UI上面的参数String branchName = "${env.branchName}" // 分支名称String gitHttpURL = "${env.gitHttpURL}" // 仓库地址String buildType = "${env.buildType}" // 构建类型String skipSonar = "${env.skipSonar}" // 是否跳过扫描
为了适配不同的构建工具,专门维护了一个map用于管理不同的构建工具的path。标准化 标准化 标准好, 也就是后期所有的agent节点都要安装这些工具的时候,要使用约定好的目录位置。
def buildTools = [ "maven" : "/usr/local/apache-maven-3.8.1","gradle": "/usr/local/gradle-6.8.3/","golang": "/usr/local/go","npm" : "/usr/local/node-v14.16.1-linux-x64/","sonar" : "/usr/local/sonar-scanner-4.6.0.2311-linux/"]
我们在流水线中定义一些全局变量:
- 通过作业的名称拿到我们的制品库名称(anyops)
- 通过作业名称拿到业务简称(anyops)
- 通过作业名称拿到应用名称(anyops-devops-service/ anyops-devops-ui)
- 通过分支名称拿到对应的版本号(release-1.1.1 对应 1.1.1 )
// 业务名称、应用名称、版本String repoName = "${JOB_NAME.split('/')[0]}"String buName = "${JOB_NAME.split('/')[0]}"String appName = "${JOB_NAME.split('/')[1].split("_")[0]}"String releaseVersion = "${branchName.split('-')[1]}"
流水线运行信息
此条流水线运行在标签为”build”的节点上, 跳过默认的checkout步骤。
agent { label "build" }options {skipDefaultCheckout true}
阶段与节点

下载代码
stage("GetCode"){steps{script{mytools.GetCode("git",branchName,gitHttpURL)}}}
构建代码
stage("Build"){steps {script {builds.Build(buildTools, buildType)}}}
代码扫描
stage("SonarScan"){when {environment name: 'skipSonar', value: 'false'}steps{script{projectName = "${appName}"sonar.SonarScan(projectName, buildType, buildTools)}}}
上传制品
stage("PushArtifacts"){steps{script{if (buildType == "maven"){env.fileDir = "target/"env.fileType = "jar"env.fileName = sh returnStdout: true, script: "cd ${env.fileDir};ls *.jar"env.fileName = fileName - "\n"println(env.fileName)println("mv ${env.fileName} ${appName}-${releaseVersion}.${fileType} ")sh "cd ${env.fileDir}; mv ${env.fileName} ${appName}-${releaseVersion}.${fileType} "env.fileName = "${appName}-${releaseVersion}.${fileType}"} else if (buildType == "npm"){env.fileDir = "dist/"env.fileName = "${appName}-${releaseVersion}.tar.gz"sh "cd ${env.fileDir};tar zcf ${env.fileName} index.html static/ --warning=no-file-changed"}env.fileTargetDir = "com/${buName}/${appName}/${releaseVersion}"artifacts.Upload(repoName,env.fileName,env.fileTargetDir,env.fileDir)}}}
上传版本文件
stage("ReleaseFile"){steps {script {//下载版本库文件 anyops-devops-service/release.yamlresponse = gitlab.GetRepoFile(12,"release.yaml", "master")//println(response)//替换文件中内容yamlData = readYaml text: """${response}"""println(yamlData)yamlData.version = "${releaseVersion}"yamlData.artifact = "http://192.168.1.200:8081/repository/${repoName}/${env.fileTargetDir}/${env.fileName}"yamlData.buname = "${buName}"yamlData.appname = "${appName}"println(yamlData.toString())sh "rm -fr test.yaml"writeYaml charset: 'UTF-8', data: yamlData, file: 'test.yaml'newYaml = sh returnStdout: true, script: 'cat test.yaml'println(newYaml)//更新gitlab文件内容base64Content = newYaml.bytes.encodeBase64().toString()// 会有并行问题,同时更新报错try {gitlab.UpdateRepoFile(12,"${appName}%2f${branchName}.yaml",base64Content, "master")} catch(e){gitlab.CreateRepoFile(12,"${appName}%2f${branchName}.yaml",base64Content, "master")}}}}
构建后操作
post {always {script{echo "always......"cleanWs()}}success {script {echo "success....."}}}
2. CD流水线的设计与实践
此次CD实践项目我们使用了jenkins共享库来完成最佳实践。 jenkinsfile第一行就是要导入我们的共享库。@Library("devopslib@master") _
我们在共享库中编写了一些特定的处理类:
- src/org/devops/mytools.groovy 存放小工具(代码下载, 邮件通知)
- src/org/devops/gitlab.groovy 版本控制系统接口操作工具
定义参数:
String appName = "${JOB_NAME.split('/')[1].split("_")[0]}" //应用名String projectType = "${JOB_NAME.split('_')[0].split("-")[-1]}" //项目类型[ui/web]// UI页面定义String releaseVersion = "${env.releaseVersion}" //版本号String port = "${env.port}" // 应用启动端口String targetDir = "${env.targetDir}" //目录主机发布目录String deployHosts = "${env.deployHosts}" //发布主机
String appName = "${JOB_NAME.split('/')[1].split("_")[0]}"String releaseVersion = "${env.releaseVersion}"String port = "${env.port}"String targetDir = "${env.targetDir}"String projectType = "${JOB_NAME.split('_')[0].split("-")[-1]}"//发布主机String deployHosts = "${env.deployHosts}"
流水线运行信息
此条流水线运行在标签为”build”的节点上, 跳过默认的checkout步骤。
agent { label "build" }options {skipDefaultCheckout true}
阶段与节点

下载制品和发布脚本
stage("PullArtifact"){steps{script{//下载版本库文件 anyops-devops-service/release-1.1.1.yamlresponse = gitlab.GetRepoFile(12,"${appName}%2frelease-${releaseVersion}.yaml", "master")//读取文件中内容yamlData = readYaml text: """${response}"""println(yamlData)artifactUrl = yamlData.artifact//后端服务if ( projectType == "service"){sh " curl -u admin:admin123 ${artifactUrl} -o ${appName}-${releaseVersion}.jar "//下载服务脚本response = gitlab.GetRepoFile(12,"service.sh", "master")println(response)writeFile file: 'service.sh', text: "${response}"sh "ls -a "}// 前端服务if ( projectType == "ui"){sh " curl -u admin:admin123 ${artifactUrl} -o ${appName}-${releaseVersion}.tar.gz "}}}}
使用ansible发布主机
stage("DeployHosts"){steps {script {echo "helloworld"println("${deployHosts}")sh "rm -fr hosts"for ( host in deployHosts.split(",")){println(host)sh "echo ${host} >> hosts"}sh " cat hosts"if ( projectType == "service" ){sh """ansible "${deployHosts}" -m ping -i hostsansible "${deployHosts}" -m shell -a "rm -fr ${targetDir}/${appName}/* && mkdir -p ${targetDir}/${appName} || echo file is exists"ansible "${deployHosts}" -m copy -a "src=${appName}-${releaseVersion}.jar dest=${targetDir}/${appName}/${appName}-${releaseVersion}.jar"ansible "${deployHosts}" -m copy -a "src=service.sh dest=${targetDir}/${appName}/service.sh"ansible "${deployHosts}" -m shell -a "cd ${targetDir}/${appName}/ ;source /etc/profile && sh service.sh ${appName} ${releaseVersion} ${port} start" -u root"""}if ( projectType == "ui") {sh """ansible "${deployHosts}" -m ping -i hostsansible "${deployHosts}" -m shell -a "rm -fr ${targetDir}/* "ansible "${deployHosts}" -m copy -a "src=${appName}-${releaseVersion}.tar.gz dest=${targetDir}/${appName}-${releaseVersion}.tar.gz"ansible "${deployHosts}" -m shell -a "cd ${targetDir}/ ; tar zxf ${appName}-${releaseVersion}.tar.gz ; ls" -u rootansible "${deployHosts}" -m shell -a "nginx -s reload """"}}}}
3.流水线调试中出现的问题
## SoanrQube的项目名称不能带有特殊字符'/'{"errors":[{"msg":"Malformed key for Project: 'anyops/anyops-devops-service'. Allowed characters are alphanumeric, '-', '_', '.' and ':', with at least one non-digit."}]}## 设置质量规则时,前端项目的language应该是js或者ts, 而不是npm。{"errors":[{"msg":"Value of parameter 'language' (npm) must be one of: [java, go, js, ts]"}]}## 在Sonarqube中找不到buildTools方法, 最后发现没有传递进去。No such property: buildTools for class: org.devops.sonarqube
扩展: 如何清除工作目录? 安装Workspace Cleanup插件。在Pipeline 的Post中的always添加CleanWs()
4. 工程关键技术点讲解
4.1 Nexus制品上传和下载
NexusAPI调试方法
进入设置页面, 找到System > API , 即可进入API调试页面。 
调试API /v1/components, 点击Try it out才能填写信息。
填写参数信息

点击
执行操作, 204表示成功。 我们可以复用这里的CURL指令, 最后封装到Jenkins流水线当中。

上传制品
curl -u admin:admin123 如果Nexus开启了认证需要配置认证信息才能正常访问。
##PNGcurl -X POST "http://192.168.1.200:8081/service/rest/v1/components?repository=myrepo" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "raw.directory=/tmp" -F "raw.asset1=@默认标题_自定义px_2020-10-01-0.png;type=image/png" -F "raw.asset1.filename=默认标题_自定义px_2020-10-01-0.png"## tar.gz & ZIPcurl -X POST "http://192.168.1.200:8081/service/rest/v1/components?repository=myrepo" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "raw.directory=/tmp" -F "raw.asset1=@nexus-3.30.0-01-unix.tar.gz;type=application/x-gzip" -F "raw.asset1.filename=aaa.tar.gz"curl -X POST "http://192.168.1.200:8081/service/rest/v1/components?repository=myrepo" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "raw.directory=/tmp" -F "raw.asset1=@waypoint_0.1.5_linux_amd64.zip;type=application/x-gzip" -F "raw.asset1.filename=waypoint_0.1.5_linux_amd64.zip"## Jar filecurl -X POST "http://192.168.1.200:8081/service/rest/v1/components?repository=myrepo" -H "accept: application/json" -H "Content-Type: multipart/form-data" -F "raw.directory=/tmp" -F "raw.asset1=@aopalliance-1.0.jar;type=application/java-archive" -F "raw.asset1.filename=aopalliance-1.0.jar"
下载制品
cURL
curl -u admin:admin123 http://192.168.1.200:8081/repository/anyops/com/anyops/anyops-devops-service/1.1.1/anyops-devops-service-1.1.1.jar -o anyops-devops-service-1.1.1.jar
Wget
wget --http-user=admin --http-passwd=admin123 http://192.168.1.200:8081/repository/anyops/com/anyops/anyops-devops-service/1.1.1/anyops-devops-service-1.1.1.jar
4.2 前端后端项目发布
前端项目
下载包
[root@master html]# curl -u admin:admin123 http://192.168.1.200:8081/repository/anyops/com/anyops/anyops-devops-ui/1.1.1/anyops-devops-ui-1.1.1.tar.gz -o anyops-devops-ui-1.1.1.tar.gz % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 196k 100 196k 0 0 24.0M 0 —:—:— —:—:— —:—:— 24.0M
解压包
[root@master html]# tar zxf anyops-devops-ui-1.1.1.tar.gz [root@master html]# ls anyops-devops-ui-1.1.1.tar.gz index.html static
触发nginx重载
[root@master html]# nginx -s reload
<a name="IKvO8"></a>### 后端项目- 复制jar包到目标目录, 使用nohup java -jar 启动服务。- nohup java -jar app.jar >output 2>&1 &<a name="qhTAr"></a>###---Ansible和SaltStack都是基于**Python**开发,Ansible只需要在一台普通的服务器上运行即可,不需要在客户端服务器上安装客户端。Ansible安装使用都很简单,而且基于上千个插件和模块,实现各种软件、平台、版本的管理,支持虚拟容器多层级的部署。有时候会觉得Ansible比SaltStack执行效率慢,其实并不是软件本身的问题,而是由于SSH服务慢,可以通过优化SSH连接速度和使用Ansible加速模块提高效率。<a name="QDXCL"></a>## 4.3 Ansible工具应用Ansible是一个IT自动化工具,简单易用。使用OpenSSH进行传输,可以配置系统,部署软件以及编排更高级的IT任务,例如持续部署。官网:http://www.ansible.com/home<br />官网文档:http://docs.ansible.com/ansible/index.html<br />Github地址:https://github.com/ansible<a name="PZXSP"></a>### 安装环境```bash## yumyum –y install ansbile## rpm$ git clone git://github.com/ansible/ansible.git$ cd ./ansible$ make rpm$ sudo rpm -Uvh ~/rpmbuild/ansible-*.noarch.rpm
Ansible如何管理主机呢? 清单文件(Host Inventory)Ansible的配置文件,对主机进行分类,用来告诉ansible需要管理哪些主机。默认文件为: /etc/ansible/hosts
## 定义主机192.168.0.22devops.server.comjenkins.test.com## 定义主机组[webservers]test.server.comtest2.server.com
命令与指令
检查主机连通性
Ansible命令格式ansible <hosts> [options]检查ansible安装环境: ansible all -m ping -u root执行命令: ansible all -a "/bin/echo hello world"
复制文件
ansible webserver -m copy -a "src=/etc/passwd dest=/opt/passwd"
yum安装软件
ansible webserver -m yum -a "name=lrzsz"
添加系统用户
ansible webserver -m user -a "name=zhangsan password=123456"
服务控制
ansible webserver -m service -a "name=sshd state=started"
4.4 SaltStack工具应用(扩展)
官方文档:https://docs.saltproject.io/en/latest/contents.html
SaltStack基于Python开发的一套C/S架构配置管理工具。使用SSL证书签发的方式进行认证管理。
可以对主机进行集中管理、文件发布、数据采集、软件包管理等配置管理操作。有利于运维人员提高工作效率,规范业务配置和操作。是常见的自动化运维利器。
Saltstack组成:
- Master是服务端,用于操作调度Minion。
- Minion是客户端,接收来自Master的指令并执行。
服务端口:
- 4505 Master和Minion的认证通信端口。(当客户端启动后,会主动向Master端注册)
- 4506 Master与Minion指令交互端口。
配置环境
一台salt-master, 多台salt-minion。
sudo rpm --import https://repo.saltproject.io/py3/redhat/8/x86_64/latest/SALTSTACK-GPG-KEY.pubcurl -fsSL https://repo.saltproject.io/py3/redhat/8/x86_64/latest.repo | sudo tee /etc/yum.repos.d/salt.repoyum install salt-masteryum install salt-minion## master start[root@zeyang-nuc-service yum.repos.d]# systemctl start salt-master[root@zeyang-nuc-service yum.repos.d]# systemctl status salt-master● salt-master.service - The Salt Master ServerLoaded: loaded (/usr/lib/systemd/system/salt-master.service; disabled; vendor preset: disabled)Active: active (running) since Fri 2021-04-23 21:22:29 HKT; 5s agoDocs: man:salt-master(1)file:///usr/share/doc/salt/html/contents.htmlhttps://docs.saltstack.com/en/latest/contents.htmlMain PID: 169600 (salt-master)Tasks: 32 (limit: 203502)Memory: 252.0MCGroup: /system.slice/salt-master.service├─169600 /usr/bin/python3.6 /usr/bin/salt-master├─169614 /usr/bin/python3.6 /usr/bin/salt-master├─169619 /usr/bin/python3.6 /usr/bin/salt-master├─169622 /usr/bin/python3.6 /usr/bin/salt-master├─169623 /usr/bin/python3.6 /usr/bin/salt-master├─169624 /usr/bin/python3.6 /usr/bin/salt-master├─169625 /usr/bin/python3.6 /usr/bin/salt-master├─169632 /usr/bin/python3.6 /usr/bin/salt-master├─169633 /usr/bin/python3.6 /usr/bin/salt-master├─169634 /usr/bin/python3.6 /usr/bin/salt-master├─169635 /usr/bin/python3.6 /usr/bin/salt-master├─169636 /usr/bin/python3.6 /usr/bin/salt-master└─169637 /usr/bin/python3.6 /usr/bin/salt-masterApr 23 21:22:28 zeyang-nuc-service systemd[1]: Starting The Salt Master Server...Apr 23 21:22:29 zeyang-nuc-service systemd[1]: Started The Salt Master Server.[root@zeyang-nuc-service yum.repos.d]# netstat -anlpt | grep 450tcp 0 0 0.0.0.0:4505 0.0.0.0:* LISTEN 169619/python3.6tcp 0 0 0.0.0.0:4506 0.0.0.0:* LISTEN 169625/python3.6
salt-minion
[root@master ~]# vi /etc/salt/minionmaster: 192.168.1.200[root@master ~]# systemctl start salt-minion[root@master ~]# systemctl status salt-minion● salt-minion.service - The Salt MinionLoaded: loaded (/usr/lib/systemd/system/salt-minion.service; disabled; vendor preset: disabled)Active: active (running) since Fri 2021-04-23 09:38:59 EDT; 6s agoDocs: man:salt-minion(1)file:///usr/share/doc/salt/html/contents.htmlhttps://docs.saltstack.com/en/latest/contents.htmlMain PID: 33505 (salt-minion)Tasks: 8 (limit: 24986)Memory: 75.6MCGroup: /system.slice/salt-minion.service├─33505 /usr/bin/python3.6 /usr/bin/salt-minion├─33510 /usr/bin/python3.6 /usr/bin/salt-minion└─33514 /usr/bin/python3.6 /usr/bin/salt-minion4月 23 09:38:58 master.zy.com systemd[1]: Starting The Salt Minion...4月 23 09:38:59 master.zy.com systemd[1]: Started The Salt Minion.4月 23 09:39:00 master.zy.com salt-minion[33505]: [ERROR ] The Salt Master has cached the public key for t>
salt-master 认证minion
[root@zeyang-nuc-service yum.repos.d]# salt-key -LAccepted Keys:zeyang-nuc-serviceDenied Keys:Unaccepted Keys:master.zy.comRejected Keys:[root@zeyang-nuc-service yum.repos.d]# salt-key -a master.zy.comThe following keys are going to be accepted:Unaccepted Keys:master.zy.comProceed? [n/Y] yKey for minion master.zy.com accepted.[root@zeyang-nuc-service yum.repos.d]# salt-key -LAccepted Keys:master.zy.comzeyang-nuc-serviceDenied Keys:Unaccepted Keys:Rejected Keys:
salt-api配置(扩展)
useradd saltapipasswd saltapi ## 设置密码 123456yum -y install salt-apivi /etc/salt/master.d/api.confexternal_auth:pam:saltapi:- .*- '@wheel'- '@runner'rest_cherrypy:port: 8000disable_ssl: truehost: 0.0.0.0systemctl restart salt-mastersystemctl restart salt-api## API(success)[root@zeyang-nuc-service master.d]# curl -k http://127.0.0.1:8000/login -H "Accept: application/x-yaml" -d username='saltapi' -d password='123456' -d eauth='pam'return:- eauth: pamexpire: 1619230016.3118818perms:- .*- '@wheel'- '@runner'start: 1619186816.3118815token: 01049ff981bc7dae25fdd27875e09afd6cd34989user: saltapi
操作指令
## 模块使用salt '*' sys.doc cmd.run## 分发文件salt-cp 'node01.zy.com' /etc/hosts /tmp/hostssalt-cp -L "node01.zy.com,node02.zy.com" /etc/hosts /tmp/hosts## 执行命令salt '*' cmd.run "ls -l | awk '/foo/{print \\$2}'"
4.5 应用发布与回滚策略
蓝绿发布
环境存在两个版本,蓝版本和绿版本同时存在,部署新版本然后进行测试,将流量切到新版本,最终实际运行的只有一个版本(蓝/绿)。好处是无需停机,并且发布风险较小。
nginx upstream模块实现:
upstream webservers {server 192.168.1.253:8099 weight=100;server 192.168.1.252:8099 down;}server {listen 8017;location / {proxy_pass http://webservers;}}nginx -s reload
灰度发布
将发行版发布到一部分用户或服务器的一种模式。这个想法是首先将更改部署到一小部分服务器,进行测试,然后将更改推广到其余服务器。一旦通过所有运行状况检查,当没有问题时,所有的客户将被路由到该应用程序的新版本,而旧版本将被删除。


nginx 权重模拟:
upstream webservers {server 192.168.1.223:8099 weight=100;server 192.168.1.222:8099 weight=100;server 192.168.1.221:8099 weight=100;}server {listen 8017;location / {proxy_pass http://webservers;}}nginx -s reload
版本回滚
- 版本一直升级,则无需回滚。
- 选择旧版本文件,进行发布。
4.6 版本文件的生成
Scripts not permitted to use staticMethod org.codehaus.groovy.runtime.EncodingGroovyMethods encodeBase64 byte[]. Administrators can decide whether to approve or reject this signature.

stage("PushFile"){// when {// expression { "${env.branchName}".contains("RELEASE-") }// }steps{script{if ("${env.branchName}".contains("RELEASE-")){println("branchName = branchName")env.branchName = "master"} else {env.branchName = "feature"}for (i = 0; i < 3; i++) {//下载版本库文件response = GetRepoFile(40,"${moduleName}%2fvalues.yaml", "${env.branchName}")//println(response)//替换文件中内容yamlData = readYaml text: """${response}"""println(yamlData.image.version)println(yamlData.image.commit)yamlData.image.version = "${releaseVersion}-${env.nowDate}"yamlData.image.commit = "${commitId}"println(yamlData.toString())sh "rm -fr test.yaml"writeYaml charset: 'UTF-8', data: yamlData, file: 'test.yaml'newYaml = sh returnStdout: true, script: 'cat test.yaml'println(newYaml)//更新gitlab文件内容base64Content = newYaml.bytes.encodeBase64().toString()// 会有并行问题,同时更新报错try {UpdateRepoFile(40,"${moduleName}%2fvalues.yaml",base64Content, "${env.branchName}")break;} catch(e){sh "sleep 2"continue;}}}}}//封装HTTP请求def HttpReq(reqType,reqUrl,reqBody){def gitServer = "http://gitlab.idevops.site/api/v4"withCredentials([string(credentialsId: 'gitlab-token', variable: 'gitlabToken')]) {result = httpRequest customHeaders: [[maskValue: true, name: 'PRIVATE-TOKEN', value: "${gitlabToken}"]],httpMode: reqType,contentType: "APPLICATION_JSON",consoleLogResponseBody: true,ignoreSslErrors: true,requestBody: reqBody,url: "${gitServer}/${reqUrl}"//quiet: true}return result}//获取文件内容def GetRepoFile(projectId,filePath,branchName){apiUrl = "projects/${projectId}/repository/files/${filePath}/raw?ref=${branchName}"response = HttpReq('GET',apiUrl,'')return response.content}//更新文件内容def UpdateRepoFile(projectId,filePath,fileContent, branchName){apiUrl = "projects/${projectId}/repository/files/${filePath}"reqBody = """{"branch": "${branchName}","encoding":"base64", "content": "${fileContent}", "commit_message": "update a new file"}"""response = HttpReq('PUT',apiUrl,reqBody)println(response)}
