前言 本文介绍使用Jenkins部署项目,使用gitea、registry配合,分别用作代码管理、镜像存储仓库
注:一般公司的发布方式有3种
直接用jar包发布(在开发环境构建jar包,将jar包上传到目标服务器,并在该服务器运行jar包)
使用docker发布(利用Jenkins从gitea拉取对应分支代码,构建jar包,将jar包和dockerfile构建为docker镜像,然后将该镜像上传的目标服务器,在目标服务器用docker运行该镜像)
使用k8s发布(利用Jenkins从gitea拉取对应分支代码,构建jar包,将jar包和dockerfile构建为docker镜像,然后将该镜像上传到私有镜像仓库(registry、harbor等),然后由k8s从私有镜像仓库拉取docker镜像,并进行部署(k8s一般进行集群部署,将镜像部署到多台服务器))
本文采取循序渐进的方式,先讲述如何实现docker部署方式(第二种方式),再慢慢补充k8s的部署方式
一般初创企业、中小企业使用第二种方式已经能够满足需求
发布流程(docker方式)
设备拓扑(docker方式)
注:我用gitea代替了gitlab,因为gitea的对设备的性能要求更小一些;另外我将gitea和Jenkins都部署在本地(Win11)的DockerDesktop中,因为网络的问题(我在虚拟机中使用macvlan,但是Jenkins很难访问外网),我利用frp转接(Jenkins访问云服务器,云服务利用frp将访问请求转发到本机的gitea),但是原理是一样的,我们开发后将代码推送到gitea服务,Jenkins从gitea拉取代码,编译打包后将jar包推送到需要部署的目标服务器,将jar包和Dockerfile构建为docker镜像,再启动服务
基础服务安装部署 gitea服务安装部署
在适当的地方建立gitea文件夹
在gitea文件夹中建立docker-compose.yml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 version: '3' services: gitea: image: gitea/gitea:1.22.2 container_name: gitea restart: always environment: - USER_UID=1000 - USER_GID=1000 ports: - "3030:3000" - "2222:22" volumes: - ./data:/data - /etc/timezone:/etc/timezone:ro - /etc/localtime:/etc/localtime:ro
运行命令安装并运行gitea
运行命令查看gitea是否成功启动
访问http://localhost:3030
(用frp转接后可以从外网访问,http://{云服务器公网ip}:{frp配置的外网接口}
), 设置用户名密码后就可以像使用github、gitee一样使用这个git服务器
创建一个仓库,将本地代码推送到服务器
使用git命令查看本地git远程连接
到新建仓库获取仓库URL(这里建议使用frp转接后的公网url,remote默认名称为origin),使用git命令添加一条remote链接
1 git remote add remote名称 仓库URL
现在可以使用git push命令将本地代码推送到搭建好的gitea服务器
1 git push remote名称 仓库分支名称
Jenkins服务安装部署
在适当的地方创建Jenkins文件夹
在Jenkins文件夹中创建docker-compose.yml文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 version: '3.6' services: jenkins: image: jenkins/jenkins:lts container_name: jenkins restart: on-failure privileged: true user: root environment: - JAVA_OPTS=-Xmx1024m ports: - "8888:8080" - "50000:50000" volumes: - ./jenkins_home:/var/jenkins_home - ./tool:/tool
运行命令安装并启动Jenkins
验证Jenkins已经启动
查看初始密码
1 cat /jenkins_home/secrets/initialAdminPassword
修改’/jenkins_home/hudson.model.UpdateCenter.xml’文件中的url参数为国内的镜像源(如清华镜像源)
1 2 3 4 5 6 7 <?xml version='1.1' encoding='UTF-8'?> <sites > <site > <id > default</id > <url > https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json</url > </site > </sites >
设置账号密码后进入并额外安装两款插件‘Git Parameter’、‘Publish Over SSH’
将linux版的jdk和maven下载并解压到tool文件夹
进入Jenkins页面(http://localhost:8888
),进入‘系统管理’-‘全局工具配置(tool)’,点击‘JDK安装’和‘MAVEN安装’,名称可以自己取,路径则填写‘/tool/jdk’和’/tool/maven‘
注意:路径名称可以不固定的,但是填写的路径要一直到’bin‘目录的上一级;解压文件时windows系统要用管理员模式解压;
在tool下新建repo目录,进入刚才的’/tool/maven/conf’文件夹,修改settings.xml文件中的’localRepository’的值为’/tool/repo‘,修改’mirror‘的值为阿里云镜像
1 2 3 4 5 6 7 8 <localRepository > /tool/repo</localRepository > <mirror > <id > aliyunmaven</id > <mirrorOf > *</mirrorOf > <name > 阿里云公共仓库</name > <url > https://maven.aliyun.com/repository/public</url > </mirror >
registry服务安装部署
在适当位置新建’registry‘文件夹,在其中新建’docker-compose.yml‘文件,运行命令启动容器,查看容器是否启动成功
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 services: registry: privileged: true image: registry restart: no container_name: registry ports: - 5000 :5000 volumes: - ./data:/var/lib/registry - ./certs:/certs networks: - registry-net registry-web: image: konradkleine/docker-registry-frontend:v2 restart: no ports: - 8035 :80 environment: - ENV_DOCKER_REGISTRY_HOST=139.155.229.99 - ENV_DOCKER_REGISTRY_PORT=5000 networks: - registry-net networks: registry-net: driver: bridge
1 2 docker compose up -d docker ps -a
修改’/etc/docker/daemon.json’文件,添加连接项后重启docker
1 2 3 { "insecure-registries" : [ "139.155.229.99:5000" ] }
1 2 systemctl daemon-reload systemctl restart docker
如果是使用docker-desktop,则在’设置‘-’docker enigine‘中添加连接项,然后点击‘Apply & restart’
注:这里设置的‘insecure-registries’是docker访问registry服务器的链接;在生产环境中一般会使用https和SSL证书来保证安全
使用方式
(1)为镜像打标签
1 2 3 docker tag 镜像名称 registry-ip:port/组织名称/镜像名称 docker tag mysql:8.0 192.168.144.140:5000/qiuli/mysql:8.0
(2)上传镜像到registry仓库
1 2 3 docker push registry-ip:port/组织名称/镜像名称 docker push 192.168.144.140:5000/qiuli/mysql:8.0
(3)访问registry-web可以查看到上传的镜像
(4)拉取镜像
进入镜像页面,使用命令拉取镜像即可
1 docker pull 139.155.229.99:5000/qiuli/hello-world:1.0.0
部署单体项目 使用Jenkins部署Java项目 新建目标服务器 从Jenkins主页进入’系统管理‘-’系统配置‘-’Publish Over SSH‘-‘SSH Servers’,添加‘SSH Server’;自定义name,hostname为目标服务器ip,username为目标服务器用户名(默认为’root‘),Remote Directory是发布路径(即将推送来的jar包放在哪里,这里用‘/usr/local’);点击’高级‘,选择’use password‘,然后填写密码;点击’Test Configuration‘,如果显示‘success’则点击’应用‘-’保存‘
新建部署任务 点击’新建任务‘,输入任务名称,先选择’构建一个自由风格的软件项目‘,点击’确定‘
在’源码管理‘中选择’Git‘,填入gitea的仓库URL,选择需要部署的分支
在’Build Steps‘中点击’增加构建步骤‘,选择’执行Shell‘,添加命令
1 sh /tool/maven/bin/mvn clean package -Dmaven.test.skip=true
注意:因为我们没有在容器内添加全局环境变量,所以这里没有办法使用’mvn‘,而必须使用包含具体执行目录的’’/tool/maven/bin/mvn’,‘/tool/maven’是我们安装是yml文件中配置的挂载目录
点击’应用‘-’保存‘回到任务界面,点击’立即构建‘,等待其构建,在’控制台输出‘中可以看到日志
构建完毕后进入docker-compose.yml文件中挂载的’jenkins_home‘文件夹,在’workspace‘-’项目名称‘-’target‘中可以看到编译打包完毕的jar包
添加构建脚本 这里我们需要更进一步,将打包好的jar包推送到目标服务器并编写shell脚本运行它
进入该项目配置页面,来到’构建后操作‘-’增加构建后操作步骤‘-’Send build artifacts over SSH‘;
选择刚才配置的云服务器,在’Transfer Set‘-’Source files‘中填写’target/*.jar‘(即打包完毕的jar包的本地路径);在’Exec command‘中填写在目标服务器中将要执行的部署脚本:
1 2 3 4 # 首先关闭原先在运行中的java程序(如果有的话) pkill java # 运行java程序并且在运行结束1s后退出 nohup java -jar /usr/local/target/jenkins-deploy-demo-1.0-SNAPSHOT.jar & sleep 1
再选择’高级‘,点击’Exec in pty‘;点击’应用‘-’保存‘
构建并部署 来到项目界面,再次点击‘立即构建’,等待构建完成查看日志结尾显示‘success’,来到目标服务器,可以查看‘nohup.out’文件,里面是项目的启动日志;这时可以确认项目启动成功
从浏览器访问’http://目标服务器ip:项目端口port/‘,就可以看到’hello world‘页面
使用Jenkins部署Java项目(构建docker镜像并运行容器) 修改代码 在Java项目中新建’/docker/Dockerfile’,并将修改后的代码推送到gitea服务器
1 2 3 4 5 6 7 8 9 10 11 12 # 使用openjdk8的镜像 FROM openjdk:8-jre # 设置工作目录 WORKDIR /build # 将jar包复制到容器中 # (注意,这里因为我们在第3步设置了消除多余前缀目录,所以jar包直接放在目标服务器中设置的‘Remote Directory’中,前面不会再有‘target’) COPY ./jenkins-deploy-demo-1.0-SNAPSHOT.jar ./jenkins-deploy-demo.jar # 运行jar包 ENTRYPOINT ["sh","-c","java -jar $JAVA_OPTS jenkins-deploy-demo.jar $PARAMS"]
使用git推送到gitea服务器,这里不再赘述
新建目标服务器 按照‘使用Jenkins部署Java项目’中的步骤创建即可,这里’Remote Directory’设置为‘/usr/local/jenkins-deploy-demo’,也可以使用已经创建好的目标服务器进行修改
新建部署任务 按照‘使用Jenkins部署Java项目’中的步骤创建即可,也可以使用原来已经创建好的任务进行修改
重写构建脚本并推送文件到目标服务器 这里我使用原来的部署任务进行修改,在‘构建后操作’-’Send build artifacts over SSH‘,’Transfers’-‘Transfer Set’中添加‘Remove prefix’的值为‘target’,并在‘Exec command’中删除原来的构建脚本
注:‘Remove prefix’的意思是移除文件前缀,目的是将多余的文件夹去除,让上传的文件保存到同一个目录下;
再添加一项‘Transfer Set’,‘Source files’中添加‘docker/*’,‘Remove prefix’中添加‘docker’(去除前缀目录),’Exec command’中添加构建脚本:
1 2 3 4 5 6 docker build -t qiuli/jenkins-deploy-demo:1.0 /usr/local/project/jenkins-deploy-demo docker rm -f jenkins-deploy-demo docker run -d -p 10001:10001 --name=jenkins-deploy-demo qiuli/jenkins-deploy-demo:1.0
注:这里因为我重新设置了‘SSH Server’-‘Remote Directory’为‘/usr/local/project/jenkins-deploy-demo’,所以上传的两个文件保存在了‘/usr/local/project/jenkins-deploy-demo’中
构建并部署 点击‘应用’-‘保存’后回到项目界面,点击‘立即构建’(这里如果报错,可以到目标服务器,将构建脚本一步步运行,查看报错);
构建成功后来到目标服务器,输入“docker images”、“docker ps -a”可以看到构建后的镜像和运行的容器
根据运行的docker容器的端口号,从浏览器访问“http://124.71.82.231:10001/demo/helloworld
”,成功返回信息
使用Jenkins流水线部署Java项目(Jenkins-pipeline) Jenkins流水线任务示例 新建Jenkins流水线任务
填写简单的pipeline脚本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 pipeline { agent any # 执行阶段 stages { # 阶段1,拉取代码 stage('拉取代码') { # 执行步骤 steps { echo '拉取成功' } } # 阶段2,执行构建 stage('执行构建') { # 执行步骤 steps { echo '构建完成' } } } }
取消勾选‘使用 Groovy 沙盒’,点击‘Approve script’(审批脚本)
看到‘The script is already approved’(脚本已经审批),再点击‘应用’-‘保存’
来到该流水线任务页面,点击‘立即构建’,可以看到执行成功
注:可以在Jenkins中安装’Blue Ocean’插件,为流水线提供了更多功能
流水线语法
1 2 3 4 5 pipeline:整条流水线 agent:指定执行器 stages:所有阶段 stage:某一阶段,可有多个 steps:阶段内的每一步,可执行命令
pipeline语法基础框架:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 pipeline { agent any stages { stage('拉取代码') { steps { echo '拉取代码完成' } } stage('执行构建') { steps { echo '执行构建完成' } } } post { always { echo "完成" } failure { echo "失败" } } }
使用流水线部署Java项目 (1)新建流水线任务
见前文
(2)代码生成器的使用方式
(3)’拉取代码‘片段
(4)’执行构建‘片段
1 mvn clean package -Dmaven.test.skip=true
(5)’清理目标服务器前次部署时创建的容器和镜像‘片段
1 2 3 4 5 rm -rf *docker stop jenkins-deploy-demo docker rm -f jenkins-deploy-demo docker rmi -f qiuli/jenkins-deploy-demo:1.0
(6)’推送到目标服务器、生成镜像并运行容器‘片段
1 2 3 docker build -t qiuli/jenkins-deploy-demo:1.0 /usr/local/project/jenkins-deploy-demo docker run -d -p 10001:10001 --name=jenkins-deploy-demo qiuli/jenkins-deploy-demo:1.0
(7)全部脚本如下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 pipeline { agent any // 导入maven,下文就可以使用’mvn‘构建jar包(“maven”的名称与全局工具配置(tool)-maven安装中的一致) tools { maven "maven" } stages { stage('拉取代码') { steps { git 'http://139.155.229.99:3030/lkd7736241/Jenkins-deploy-demo.git' echo '拉取成功' } } stage('执行构建') { steps { // 测试mvn命令能否正确运行 // sh "mvn --version" sh "mvn clean package -Dmaven.test.skip=true" echo '构建完成' } } stage('清理目标服务器前次部署时创建的容器和镜像') { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'target-huaweiyun-jenkinsdeploydemo', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''rm -rf * docker stop jenkins-deploy-demo docker rm -f jenkins-deploy-demo docker rmi -f qiuli/jenkins-deploy-demo:1.0''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) echo '清理完成' } } stage('推送到目标服务器、生成镜像并运行容器') { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'target-huaweiyun-jenkinsdeploydemo', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'jenkins-deploy-demo/target', sourceFiles: 'jenkins-deploy-demo/target/*.jar'), sshTransfer(cleanRemote: false, excludes: '', execCommand: '''docker build -t qiuli/jenkins-deploy-demo:1.0 /usr/local/project/jenkins-deploy-demo docker run -d -p 10001:10001 --name=jenkins-deploy-demo qiuli/jenkins-deploy-demo:1.0''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'jenkins-deploy-demo/docker', sourceFiles: 'jenkins-deploy-demo/docker/*')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) echo '部署完成' } } } }
(8)将脚本粘贴并审批后,点击’应用‘-’保存‘,来到该流水线项目的页面,点击’立即构建‘
使用多分支流水线部署Java项目 (1)在Java项目中创建一个新的分支test,修改后推送到gitea代码仓库
1 2 git checkout -b test git push origin test
(2)在两个分支中新建Jenkinsfile文件
该文件中即是构建脚本;本示例项目中,master分支可以直接使用上文中的构建脚本,test分支需要调整的地方是:a.git拉取代码的语句 b.目标服务器的ip、端口、目录等(例如测试服务器和生产服务是分开的,那就需要更新目标服务器脚本片段,这不是必须,根据实际情况调整);脚本还是由脚本片段生成器生成
1 2 # git片段 git branch: 'test', url: 'http://139.155.229.99:3030/lkd7736241/Jenkins-deploy-demo.git'
master分支构建脚本全文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 pipeline { agent any // 导入maven,下文就可以使用’mvn‘构建jar包(“maven”的名称与全局工具配置(tool)-maven安装中的一致) tools { maven "maven" } stages { stage('拉取master分支代码') { steps { git 'http://139.155.229.99:3030/lkd7736241/Jenkins-deploy-demo.git' echo '拉取成功' } } stage('执行构建') { steps { // 测试mvn命令能否正确运行 // sh "mvn --version" sh "mvn clean package -Dmaven.test.skip=true" echo '构建完成' } } stage('清理目标服务器前次部署时创建的容器和镜像') { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'target-huaweiyun-jenkinsdeploydemo', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''rm -rf * docker stop jenkins-deploy-demo docker rm -f jenkins-deploy-demo docker rmi -f qiuli/jenkins-deploy-demo:1.0''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) echo '清理完成' } } stage('推送到目标服务器、生成镜像并运行容器') { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'target-huaweiyun-jenkinsdeploydemo', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'jenkins-deploy-demo/target', sourceFiles: 'jenkins-deploy-demo/target/*.jar'), sshTransfer(cleanRemote: false, excludes: '', execCommand: '''docker build -t qiuli/jenkins-deploy-demo:1.0 /usr/local/project/jenkins-deploy-demo docker run -d -p 10001:10001 --name=jenkins-deploy-demo qiuli/jenkins-deploy-demo:1.0''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'jenkins-deploy-demo/docker', sourceFiles: 'jenkins-deploy-demo/docker/*')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) echo '部署完成' } } } }
test分支构建脚本全文:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 pipeline { agent any tools { maven "maven" } stages { stage('拉取test分支代码' ) { steps { git branch: 'test' , url: 'http://139.155.229.99:3030/lkd7736241/Jenkins-deploy-demo.git' echo '拉取成功' } } stage('执行构建' ) { steps { sh "mvn clean package -Dmaven.test.skip=true" echo '构建完成' } } stage('清理目标服务器前次部署时创建的容器和镜像' ) { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'target-huaweiyun-jenkinsdeploydemo' , transfers: [sshTransfer(cleanRemote: false , excludes: '' , execCommand: '''rm -rf * docker stop jenkins-deploy-demo docker rm -f jenkins-deploy-demo docker rmi -f qiuli/jenkins-deploy-demo:1.0''' , execTimeout: 120000 , flatten: false , makeEmptyDirs: false , noDefaultExcludes: false , patternSeparator: '[, ]+' , remoteDirectory: '' , remoteDirectorySDF: false , removePrefix: '' , sourceFiles: '' )], usePromotionTimestamp: false , useWorkspaceInPromotion: false , verbose: false )]) echo '清理完成' } } stage('推送到目标服务器、生成镜像并运行容器' ) { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'target-huaweiyun-jenkinsdeploydemo' , transfers: [sshTransfer(cleanRemote: false , excludes: '' , execCommand: '' , execTimeout: 120000 , flatten: false , makeEmptyDirs: false , noDefaultExcludes: false , patternSeparator: '[, ]+' , remoteDirectory: '' , remoteDirectorySDF: false , removePrefix: 'jenkins-deploy-demo/target' , sourceFiles: 'jenkins-deploy-demo/target/*.jar' ), sshTransfer(cleanRemote: false , excludes: '' , execCommand: '''docker build -t qiuli/jenkins-deploy-demo:1.0 /usr/local/project/jenkins-deploy-demo docker run -d -p 10001:10001 --name=jenkins-deploy-demo qiuli/jenkins-deploy-demo:1.0''' , execTimeout: 120000 , flatten: false , makeEmptyDirs: false , noDefaultExcludes: false , patternSeparator: '[, ]+' , remoteDirectory: '' , remoteDirectorySDF: false , removePrefix: 'jenkins-deploy-demo/docker' , sourceFiles: 'jenkins-deploy-demo/docker/*' )], usePromotionTimestamp: false , useWorkspaceInPromotion: false , verbose: false )]) echo '部署完成' } } } }
(3)新建多分支流水线任务(需要安装multi-pipeline插件),添加git仓库后保存
(4)来到该项目页面,点击’立刻扫描 多分支流水线‘即可执行;点击’状态‘,再点击右手边构建符号即可以对某分支进行单独执行
部署集群项目(K8S方式部署) 参考文章kubernetes集群部署 、kubernetes部署Nginx集群 、kubernetes部署Redis集群
// TODO
项目部署实战 部署hades项目 Jenkins自由风格部署 将代码推送到gitea服务器
新建目标服务器 即服务将被部署的服务器,见‘使用Jenkins部署Java项目’-’新建目标服务器‘
新建自由风格任务
填写相关配置 配置代码源(gitea服务器地址)、构建步骤(Maven构建)、构建后操作(将Dockerfile文件和jar包推送到目标服务器,然后使用docker构建镜像、运行容器)
配置代码源(gitea服务器)
构建步骤(Maven构建)
1 sh /tool/maven/bin/mvn clean package -Dmaven.test.skip=true
构建后操作(将jar包推送到目标服务器)
构建后操作(将Dockerfile文件推送到目标服务器,然后使用docker构建镜像、运行容器)
1 2 3 docker build -t qiuli/hades-web:1.0 /usr/local/project/hades-qiuli docker rm -f hades-web docker run -d -p 7890:7890 --name=hades-web qiuli/hades-web:1.0
注:如果目标服务器是云服务器,记得打开端口(见’虚拟机的初始准备‘-’网络设置‘)
Jenkins多分支流水线部署 新建目标服务器
新建多分支流水线任务
配置分支源 点击‘配置’-‘分支源’-‘git’,填写代码仓库的地址
生成Jenkinsfile文件 进入项目的流水线语法页面,选择‘sshPublisher’,填写各项配置
注:在需要进行部署的代码分支新建文件Jenkinsfile,该文件中包含部署脚本,使用Jenkins的“流水线语法 ”
生成脚本片段,本次部署脚本共4个片段
(1)片段一:拉取deploy分支代码
1 git branch: 'deploy' , url: 'http://139.155.229.99:3030/lkd7736241/hades-qiuli.git'
(2)片段二:利用maven构建jar包
1 sh "mvn clean package -Dmaven.test.skip=true"
(3)片段三:清理目标服务器前次部署时创建的容器和镜像
1 2 3 4 rm -rf *docker stop hades-web docker rm -f hades-web docker rmi -f qiuli/hades-web:1.0
1 2 3 4 5 steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'huaweiyun-hades-qiuli', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''rm -rf * docker stop hades-web docker rm -f hades-web docker rmi -f qiuli/hades-web:1.0''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
(4)片段四:推送文件到目标服务器、生成镜像并运行容器
1 2 docker build -t qiuli/hades-web:1.0 /usr/local/project/hades-qiuli docker run -d -p 7890:7890 --name=hades-web qiuli/hades-web:1.0
1 2 sshPublisher(publishers: [sshPublisherDesc(configName: 'huaweiyun-hades-qiuli', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'hades-qiuli/target', sourceFiles: 'hades-qiuli/target/*.jar'), sshTransfer(cleanRemote: false, excludes: '', execCommand: '''docker build -t qiuli/hades-web:1.0 /usr/local/project/hades-qiuli docker run -d -p 7890:7890 --name=hades-web qiuli/hades-web:1.0''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'hades-qiuli/docker', sourceFiles: 'hades-qiuli/docker/*')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
(5)将4个片段粘贴到流水线部署脚本中,本次部署脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 pipeline { agent any // 导入maven,下文就可以使用’mvn‘构建jar包(“maven”的名称与全局工具配置(tool)-maven安装中的一致) tools { maven "maven" } stages { stage('拉取deploy分支代码') { steps { git branch: 'deploy', url: 'http://139.155.229.99:3030/lkd7736241/hades-qiuli.git' echo '拉取成功' } } stage('构建jar包') { steps { // 测试mvn命令能否正确运行 // sh "mvn --version" sh "mvn clean package -Dmaven.test.skip=true" echo '构建完成' } } stage('清理目标服务器前次部署时创建的容器和镜像') { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'huaweiyun-hades-qiuli', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''rm -rf * docker stop hades-web docker rm -f hades-web docker rmi -f qiuli/hades-web:1.0''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '', sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) echo '清理完成' } } stage('推送文件到目标服务器、生成镜像并运行容器') { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'huaweiyun-hades-qiuli', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'hades-qiuli/target', sourceFiles: 'hades-qiuli/target/*.jar'), sshTransfer(cleanRemote: false, excludes: '', execCommand: '''docker build -t qiuli/hades-web:1.0 /usr/local/project/hades-qiuli docker run -d -p 7890:7890 --name=hades-web qiuli/hades-web:1.0''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: 'hades-qiuli/docker', sourceFiles: 'hades-qiuli/docker/*')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)]) echo '部署完成' } } } }
将Jenkinsfile文件推送动到对应分支,启动部署 将编辑好的部署脚本推送到deploy分支,在Jenkins对应项目页面点击’立即扫描多分支流水线‘,则会扫描出含有Jenkinsfile的分支,点击启动按钮就可以进行部署
部署hades-admin项目 前置安装的服务:node、npm、git、pm2
使用git克隆hades-admin代码到目标服务器适当的文件夹
1 2 git clone -b deploy http://139.155.229.99:3030/lkd7736241/hades-admin.git
进入hades-admin文件夹,使用npm安装依赖
使用pm2启动hades-admin服务、查看服务状态
1 2 pm2 start hades-server.js pm2 status
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 const express = require ('express' );const http = require ('http' );const path = require ('path' );const reload = require ('reload' );const bodyParser = require ('body-parser' );const logger = require ('morgan' );const app = express ();app.set ('port' , process.env .PORT || 3100 ); app.use (logger ('deploy' )); app.use (bodyParser.json ()); app.use ('/public' , express.static ('public' )); app.use ('/pages' , express.static ('pages' )); app.use ('/sdk' , express.static ('sdk' )); app.get ('/*' , function (req, res ) { res.sendFile (path.join (__dirname, 'index.html' )); }); const server = http.createServer (app);reload (app) .then (function (reloadReturned ) { server.listen (app.get ('port' ), function ( ) { console .log ( 'Web server listening on port http://ip:' + app.get ('port' ) ); }); }) .catch (function (err ) { console .error ( 'Reload could not start, could not start server/sample app' , err ); });
部署austin项目 Jenkins自由风格部署 修改代码并上传到gitea服务器
新建目标服务器 - 注:目标服务器确定项目部署的服务器、文件夹等,记得在高级中配置服务器ssh登录密码
1 2 3 4 5 6 7 8 huaweiyun-austin-qiuli 124.71.82.231 root /usr/local/project/austin-qiuli
新建自由风格服务
填写相关配置 (1)配置代码源(gitea仓库地址)
1 2 3 4 http://139.155.229.99:3030/lkd7736241/austin_qiuli.git */deploy
(2)配置构架操作(构建jar包)
“增加构建步骤”-“执行Shell”
1 sh /tool/maven/bin/mvn clean package -Dmaven.test.skip=true
注:jdk和Maven已经配置够了,见“基础服务安装部署”-“Jenkins服务安装部署”
(3)配置构建后操作
将Dockerfile文件和jar包推送到目标服务器,停止之前部署的服务,清理之前的容器、镜像,然后使用docker构建镜像、运行容器
1 2 3 4 austin-web/target/*.jar austin-web/target
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 rm -rf *.jarrm -rf Dockerfiledocker stop austin-qiuli docker rm -f austin-qiuli docker rmi -f qiuli/austin-qiuli:1.0 docker build -t qiuli/austin-qiuli:1.0 /usr/local/project/austin-qiuli mkdir /usr/local/project/austin-qiuli/logsmkdir /usr/local/project/austin-qiuli/mailinglistdocker run -d -p 8899:8899 -p 6666:6666 \ -v /usr/local/project/austin-qiuli/logs:/build/logs \ -v /usr/local/project/austin-qiuli/mailinglist:/home/austin/mailinglist \ --name=austin-qiuli \ -e SET_CONTAINER_TIMEZONE=true \ -e CONTAINER_TIMEZONE=Asia/Shanghai \ -e TZ=Asia/Shanghai \ qiuli/austin-qiuli:1.0
注意:将项目打包为docker镜像并在docker中运行,需要将所有涉及到的接口映射到主机(例如作为xxl-job执行器需要暴露单独的端口6666);
要想项目运行的时间正常,需要设置时区的环境变量:
1 2 3 -e SET_CONTAINER_TIMEZONE=true -e CONTAINER_TIMEZONE=Asia/Shanghai -e TZ=Asia/Shanghai
Jenkins多分支流水线部署 新建目标服务器
新建多分支流水线任务
配置分支源 点击‘配置’-‘分支源’-‘git’,填写代码仓库的地址
生成Jenkinsfile文件 进入项目的流水线语法页面,选择‘sshPublisher’,填写各项配置
注:在需要进行部署的代码分支新建文件Jenkinsfile,该文件中包含部署脚本,使用Jenkins的“流水线语法 ”
生成脚本片段,本次部署脚本共4个片段
(1)片段一:拉取deploy分支代码
1 git branch: 'deploy' , url: 'http://139.155.229.99:3030/lkd7736241/austin_qiuli.git'
(2)片段二:利用maven构建jar包
1 sh /tool/maven/bin/mvn clean package -Dmaven.test.skip=true
1 sh 'sh /tool/maven/bin/mvn clean package -Dmaven.test.skip=true'
(3)片段三:清理目标服务器前次部署时创建的容器和镜像
1 2 3 4 5 6 rm -rf *.jar rm -rf Dockerfile docker stop austin-qiuli docker rm -f austin-qiuli docker rmi -f qiuli/austin-qiuli:1.0
1 2 3 4 5 6 sshPublisher(publishers: [sshPublisherDesc(configName: 'centos7-05-austin-qiuli' , transfers: [sshTransfer(cleanRemote: false , excludes: '' , execCommand: '''rm -rf *.jar rm -rf Dockerfile docker stop austin-qiuli docker rm -f austin-qiuli docker rmi -f qiuli/austin-qiuli:1.0''' , execTimeout: 120000 , flatten: false , makeEmptyDirs: false , noDefaultExcludes: false , patternSeparator: '[, ]+' , remoteDirectory: '' , remoteDirectorySDF: false , removePrefix: 'austin-web/target' , sourceFiles: 'austin-web/target/*.jar' )], usePromotionTimestamp: false , useWorkspaceInPromotion: false , verbose: false )])
(4)片段四:推送文件到目标服务器、生成镜像并运行容器
1 2 3 4 5 6 7 8 9 10 11 12 13 docker build -t qiuli/austin-qiuli:1.0 /usr/local/project/austin-qiuli mkdir /usr/local/project/austin-qiuli/logs mkdir /usr/local/project/austin-qiuli/mailinglist docker run -d -p 8899:8899 -p 6666:6666 \ -v /usr/local/project/austin-qiuli/logs:/build/logs \ -v /usr/local/project/austin-qiuli/mailinglist:/home/austin/mailinglist \ --name=austin-qiuli \ -e SET_CONTAINER_TIMEZONE=true \ -e CONTAINER_TIMEZONE=Asia/Shanghai \ -e TZ=Asia/Shanghai \ qiuli/austin-qiuli:1.0
1 2 3 4 5 6 7 8 9 10 11 12 13 sshPublisher(publishers: [sshPublisherDesc(configName: 'centos7-05-austin-qiuli' , transfers: [sshTransfer(cleanRemote: false , excludes: '' , execCommand: '''docker build -t qiuli/austin-qiuli:1.0 /usr/local/project/austin-qiuli mkdir /usr/local/project/austin-qiuli/logs mkdir /usr/local/project/austin-qiuli/mailinglist docker run -d -p 8899:8899 -p 6666:6666 \\ -v /usr/local/project/austin-qiuli/logs:/build/logs \\ -v /usr/local/project/austin-qiuli/mailinglist:/home/austin/mailinglist \\ --name=austin-qiuli \\ -e SET_CONTAINER_TIMEZONE=true \\ -e CONTAINER_TIMEZONE=Asia/Shanghai \\ -e TZ=Asia/Shanghai \\ qiuli/austin-qiuli:1.0''' , execTimeout: 120000 , flatten: false , makeEmptyDirs: false , noDefaultExcludes: false , patternSeparator: '[, ]+' , remoteDirectory: '' , remoteDirectorySDF: false , removePrefix: 'austin-web/target' , sourceFiles: 'austin-web/target/*.jar' )], usePromotionTimestamp: false , useWorkspaceInPromotion: false , verbose: false )])
(5)将4个片段粘贴到流水线部署脚本中,本次部署脚本如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 pipeline { agent any tools { maven "maven" } stages { stage('拉取deploy分支代码' ) { steps { git branch: 'deploy' , url: 'http://139.155.229.99:3030/lkd7736241/austin_qiuli.git' echo '拉取成功' } } stage('执行构建' ) { steps { sh 'sh /tool/maven/bin/mvn clean package -Dmaven.test.skip=true' echo '构建完成' } } stage('清理目标服务器前次部署时创建的容器和镜像' ) { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'centos7-05-austin-qiuli' , transfers: [sshTransfer(cleanRemote: false , excludes: '' , execCommand: '''rm -rf *.jar rm -rf Dockerfile docker stop austin-qiuli docker rm -f austin-qiuli docker rmi -f qiuli/austin-qiuli:1.0''' , execTimeout: 120000 , flatten: false , makeEmptyDirs: false , noDefaultExcludes: false , patternSeparator: '[, ]+' , remoteDirectory: '' , remoteDirectorySDF: false , removePrefix: 'austin-web/target' , sourceFiles: 'austin-web/target/*.jar' )], usePromotionTimestamp: false , useWorkspaceInPromotion: false , verbose: false )]) echo '清理完成' } } stage('推送到目标服务器、生成镜像并运行容器' ) { steps { sshPublisher(publishers: [sshPublisherDesc(configName: 'centos7-05-austin-qiuli' , transfers: [sshTransfer(cleanRemote: false , excludes: '' , execCommand: '''docker build -t qiuli/austin-qiuli:1.0 /usr/local/project/austin-qiuli mkdir /usr/local/project/austin-qiuli/logs mkdir /usr/local/project/austin-qiuli/mailinglist docker run -d -p 8899:8899 -p 6666:6666 \\ -v /usr/local/project/austin-qiuli/logs:/build/logs \\ -v /usr/local/project/austin-qiuli/mailinglist:/home/austin/mailinglist \\ --name=austin-qiuli \\ -e SET_CONTAINER_TIMEZONE=true \\ -e CONTAINER_TIMEZONE=Asia/Shanghai \\ -e TZ=Asia/Shanghai \\ qiuli/austin-qiuli:1.0''' , execTimeout: 120000 , flatten: false , makeEmptyDirs: false , noDefaultExcludes: false , patternSeparator: '[, ]+' , remoteDirectory: '' , remoteDirectorySDF: false , removePrefix: 'austin-web/target' , sourceFiles: 'austin-web/target/*.jar' )], usePromotionTimestamp: false , useWorkspaceInPromotion: false , verbose: false )]) echo '部署完成' } } } }
将Jenkinsfile文件推送动到对应分支,启动部署 将编辑好的部署脚本推送到deploy分支,在Jenkins对应项目页面点击’立即扫描多分支流水线‘,则会扫描出含有Jenkinsfile的分支,点击启动按钮就可以进行部署
部署austin-admin项目
前置安装的服务:node、npm、git、pm2
使用git克隆austin-admin代码到目标服务器适当的文件夹
1 2 git clone -b deploy http://139.155.229.99:3030/lkd7736241/austin-admin.git
进入austin-admin文件夹,使用npm安装依赖
使用pm2启动austin-admin服务、查看服务状态
1 2 pm2 start austin-server.js pm2 status
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 const express = require ('express' );const http = require ('http' );const path = require ('path' );const reload = require ('reload' );const bodyParser = require ('body-parser' );const logger = require ('morgan' );const app = express ();app.set ('port' , process.env .PORT || 3000 ); app.use (logger ('deploy' )); app.use (bodyParser.json ()); app.use ('/public' , express.static ('public' )); app.use ('/pages' , express.static ('pages' )); app.use ('/sdk' , express.static ('sdk' )); app.get ('/*' , function (req, res ) { res.sendFile (path.join (__dirname, 'index.html' )); }); const server = http.createServer (app);reload (app) .then (function (reloadReturned ) { server.listen (app.get ('port' ), function ( ) { console .log ( 'Web server listening on port http://ip:' + app.get ('port' ) ); }); }) .catch (function (err ) { console .error ( 'Reload could not start, could not start server/sample app' , err ); });