jenkins通过pipeline部署springboot项目
部署方案
1、springboot项目不保存部署的pipeline或dockerfile构建脚本等与部署相关的问文件,业务项目只需关心业务,能够正常构建为jar包即可
2、新建一个代码仓库,用于保存项目需要构建的jenkinsfile
3、jenkins配置pipeline地址,从仓库拉取要构建的项目进行构建和部署
构建文件仓库示例结构如下:
4、jenkins配置
5、springboot项目镜像构建文件
# 指定基础镜像,这是分阶段构建的前期阶段 from eclipse-temurin:21-jre-alpine as builder # 设定时区、中文 env tz=asia/shanghai # 安装chrony包 run apk add --no-cache chrony # 配置chrony run echo "server 0.pool.ntp.org iburst" >> /etc/chrony/chrony.conf run echo "server 1.pool.ntp.org iburst" >> /etc/chrony/chrony.conf run echo "server 2.pool.ntp.org iburst" >> /etc/chrony/chrony.conf run echo "server 3.pool.ntp.org iburst" >> /etc/chrony/chrony.conf # 执行工作目录 workdir application # 配置参数 arg jar_file=target/*.jar # 将编译构建得到的jar文件复制到镜像空间中 copy ${jar_file} application.jar # 通过工具spring-boot-jarmode-layertools从application.jar中提取拆分后的构建结果 run java -djarmode=layertools -jar application.jar extract # 启动chronyd服务 cmd ["chronyd"] # 正式构建镜像 from builder workdir application # 前一阶段从jar中提取除了多个文件,这里分别执行copy命令复制到镜像空间中,每次copy都是一个layer copy --from=builder application/dependencies/ ./ copy --from=builder application/spring-boot-loader/ ./ copy --from=builder application/snapshot-dependencies/ ./ copy --from=builder application/application/ ./ # entrypoint ["java", "org.springframework.boot.loader.jarlauncher"] # 分层构建传递参数写法 entrypoint ["sh","-c","java $java_opts org.springframework.boot.loader.jarlauncher $params"] # 新新 # 例如: docker run -d -p 21991:2199 --name demo3 -e java_opts="-xmx128m" -e params="--spring.application.name=test-demo" docker-demo:1.3 #镜像放在最后,所传的java参数和覆盖配置文件参数写在docker镜像之前不然会导致传递失败
基础镜像可选择:
eclipse-temurin:21-jre-alpine eclipse-temurin:21-jdk-alpine openjdk:21 openjdk:21-slim # 基于dibian构建 bitnami/minideb debian:bullseye-slim
6、demo项目docker-compose.yml文件
services: demo: # 启动时传入镜像tag示例:build_tag=20240406-57 docker-compose up -d image: registry.cn-guangzhou.aliyuncs.com/lyr-test/demo:${build_tag} container_name: demo restart: always network_mode: host deploy: resources: limits: cpus: '1.00' memory: 1g reservations: cpus: '0.10' memory: 256m environment: - java_opts= -xx:+usecontainersupport -xx:initialrampercentage=75.0 -xx:maxrampercentage=75.0 -xx:minrampercentage=75.0 # 当network_mode使用hots模式时,端口号设置不生效 - params = --server.port=8080
7、jenkinsfile构建文件
// 获取当前日期 def current_date = new date().format('yyyymmdd') // 获取当前构建号 def build_number = env.build_number.tointeger() // 服务器集合 def server_list = [] // 所有的脚本命令放在pipeline中 pipeline { // 指定任务在哪个集群节点中执行,any表示任意节点 agent any parameters { string(description: '代码分支', name: 'code_branch_param', defaultvalue: 'master', trim: true) // 这在jenkins的凭据里设置的待部署服务器的名称就是服务器的ip;用docker-compose部署一般只会部署几台服务器,如果量大,建议上k8s booleanparam defaultvalue: true, description: '10.0.24.8', name: 'server_1' booleanparam description: '10.0.24.3', name: 'server_2' } tools { git 'default' } // 声明全局变量,方便后面修改使用 environment { git_config_branch = "master" git_config_address = "https://*******/demo-jenkins.git" code_address = "https://********/demo.git" // jenkins中创建的代码仓库密钥id credentials_id = 'git-credentials-id' img_repo_credentials_id = 'img-repo-credentials-id' img_repo = "registry.cn-guangzhou.aliyuncs.com" repo_namespace = 'lyr-test' default_build_tag = "${current_date}-${build_number}" } stages { stage('环境检测') { steps { // 构建环境检测 sh ''' cat /proc/version free -m df -h docker -v git -v mvn -v java -version ''' echo '环境检测完成' } } stage('拉取配置文件') { steps { echo "拉取配置文件代码分支:${git_config_branch}" sh "pwd" dir('/var/jenkins_home/workspace/pipeline/') { sh "pwd" echo "${credentials_id}" checkout scmgit(branches: [[name: "${git_config_branch}"]], extensions: [], userremoteconfigs: [[credentialsid: "${credentials_id}", url: "${git_config_address}"]]) } sh "pwd" } } stage('拉取代码') { steps { echo pwd // branch为构建分支参数 git branch: "${code_branch_param}", credentialsid: "${credentials_id}", url: "${code_address}" } } stage('maven构建') { steps { echo pwd sh """ mvn clean package -u -dmaven.test.skip=true """ } } stage('生成镜像') { steps { echo pwd // job_name为项目名变量(内置的环境变量) tag为设置的变量标签 sh ''' cp /var/jenkins_home/workspace/pipeline/${job_name}/dockerfile /var/jenkins_home/workspace/${job_name} ''' script { echo "当前镜像tag:${default_build_tag}" sh "docker build -f dockerfile -t ${img_repo}/${repo_namespace}/${job_name}:${default_build_tag} ." } } } stage('推送镜像') { steps { withcredentials([usernamepassword(credentialsid: 'img-repo-credentials-id', passwordvariable: 'img_pwd', usernamevariable: 'img_user')]) { sh ''' echo "${img_pwd}" | docker login --username ${img_user} --password-stdin ${img_repo} docker image prune -f docker push ${img_repo}/${repo_namespace}/${job_name}:${default_build_tag} ''' } } } stage('清理') { steps { sh ''' # 退出镜像仓库 # docker logout ${img_repo} # 清理前镜像 # docker images # 删除指定镜像 # docker rmi ${img_repo}/${repo_namespace}/${job_name}:${pre_build_tag} # 命令删除,删除最早一个 # docker images | grep "demo" | sort -r | tail -n 1 | awk '{print $3}' | xargs docker rmi # 清理后镜像 docker images ''' } } stage('部署至服务器') { steps { script { script { echo "server_1:" + server_1 if (server_1=="true") { server_list.add('10.0.24.8') } echo "server_2:" + server_2 if (server_2=="true") { server_list.add('10.0.24.3') } for (server_ip in server_list) { echo "当前部署的服务器id:${server_ip}" withcredentials([usernamepassword(credentialsid: server_ip, passwordvariable: 'server_pwd', usernamevariable: 'server_user')]) { node { def remote = [:] remote.name = "deploy" remote.host = server_ip remote.user = "${server_user}" remote.password = "${server_pwd}" remote.allowanyhosts = true stage('远程ssh部署') { echo "当前远程ssh部署的项目名:${job_name}" sshcommand remote: remote, command: "mkdir -p /data/${job_name}" sshput remote: remote, from: """/var/jenkins_home/workspace/pipeline/${job_name}/docker-compose.yaml""", into: """/data/${job_name}""" sshcommand remote: remote, command: """ cd /data/${job_name}/ build_tag=${default_build_tag} docker-compose up -d docker-compose ps """ echo "ssh部署脚本执行完成" } } } } } } } } } // 通知内容 post { success { //成功通知 echo "成功通知" } failure { // 失败通知 echo "失败通知" } } }
8、jenkins中配置jenkinsfile中使用到的代码仓库凭据,镜像仓库凭据和服务器密码凭据
9、配置完成后,点击构建就行
10、当首次部署到新服务器时,需要登录镜像仓库,可以手动登录,也可以在jenkins中进行配置,每次发布都要登录,不然会拉取镜像错误
// 服务器集合 def server_list = [] // 所有的脚本命令放在pipeline中 pipeline { // 指定任务在哪个集群节点中执行,any表示任意节点 agent any parameters { choice(description: '服务名', name: 'service_name', choices: ["demo"]) string(description: '镜像tag', name: 'build_tag_param', defaultvalue: '20240405-01', trim: true) booleanparam defaultvalue: true, description: '10.0.24.8', name: 'server_1' booleanparam description: '10.0.24.3', name: 'server_2' } tools { git 'default' } // 声明全局变量,方便后面修改使用 environment { git_config_branch = "master" git_config_address = "https://******/demo-jenkins.git" // jenkins中创建的代码仓库密钥id credentials_id = 'git-credentials-id' img_repo_credentials_id = 'img-repo-credentials-id' img_repo = "registry.cn-guangzhou.aliyuncs.com" repo_namespace = 'lyr-test' } stages { stage('环境检测') { steps { // 构建环境检测 sh ''' cat /proc/version free -m df -h docker -v git -v mvn -v java -version ''' echo '环境检测完成' } } stage('拉取配置文件') { steps { echo "拉取配置文件代码分支:${git_config_branch}" sh "pwd" dir('/var/jenkins_home/workspace/pipeline/') { sh "pwd" echo "${credentials_id}" checkout scmgit(branches: [[name: "${git_config_branch}"]], extensions: [], userremoteconfigs: [[credentialsid: "${credentials_id}", url: "${git_config_address}"]]) } sh "pwd" } } stage('登录镜像') { steps { withcredentials([usernamepassword(credentialsid: 'img-repo-credentials-id', passwordvariable: 'img_pwd', usernamevariable: 'img_user')]) { script { echo "server_1:" + server_1 if (server_1=="true") { server_list.add('10.0.24.8') } echo "server_2:" + server_2 if (server_2=="true") { server_list.add('10.0.24.3') } for (server_ip in server_list) { echo "当前部署的服务器id:${server_ip}" withcredentials([usernamepassword(credentialsid: server_ip, passwordvariable: 'server_pwd', usernamevariable: 'server_user')]) { node { def remote = [:] remote.name = "deploy" remote.host = server_ip remote.user = "${server_user}" remote.password = "${server_pwd}" remote.allowanyhosts = true stage('远程ssh部署') { echo "当前远程ssh登录的服务器ip:${server_ip}" sshcommand remote: remote, command: """ echo "${img_pwd}" | docker login --username ${img_user} --password-stdin ${img_repo} """ echo "镜像ssh部署脚本执行完成" } } } } } } } } stage('部署至服务器') { steps { script { script { echo "server_1:" + server_1 if (server_1=="true") { server_list.add('10.0.24.8') } echo "server_2:" + server_2 if (server_2=="true") { server_list.add('10.0.24.3') } for (server_ip in server_list) { echo "当前部署的服务器id:${server_ip}" withcredentials([usernamepassword(credentialsid: server_ip, passwordvariable: 'server_pwd', usernamevariable: 'server_user')]) { node { def remote = [:] remote.name = "deploy" remote.host = server_ip remote.user = "${server_user}" remote.password = "${server_pwd}" remote.allowanyhosts = true stage('远程ssh部署') { echo "当前远程ssh部署的项目名:${service_name}" sshcommand remote: remote, command: "mkdir -p /data/${service_name}" sshput remote: remote, from: """/var/jenkins_home/workspace/pipeline/${service_name}/docker-compose.yaml""", into: """/data/${service_name}""" sshcommand remote: remote, command: """ cd /data/${service_name}/ build_tag=${build_tag_param} docker-compose up -d docker-compose ps """ echo "ssh部署脚本执行完成" } } } } } } } } } // 通知内容 post { success { //成功通知 echo "成功通知" } failure { // 失败通知 echo "失败通知" } } }
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。
发表评论