文章目录
-
-
- 一、前言
- 二、jenkins插件安装
- 三、jenkins配置
-
- 1、远程服务器配置
- 2、全局凭证配置
-
-
- ① git全局凭证配置
- ② docker镜像仓库全局凭证配置
-
- 四、项目配置
-
- 1、Dockerfile
- 2、Jenkinsfile
- 五、jenkins新建任务
- 六、构建测试
- 七、其它
-
一、前言
- Jenkins(7) 参数化构建配置
- Jenkins(8) 远程服务器部署
本文将通过Dockerfile
+Jenkinsfile
方案实现部署SpringCloud微服务项目到多台服务器
项目地址 https://gitee.com/zhengqingya/small-tools
温馨小提示:本文示例项目基于码云仓库和阿里云镜像仓库环境
二、jenkins插件安装
- Publish Over SSH : 远程发布
- Extended Choice Parameter :参数化构建
- Git Parameter : Git参数化
三、jenkins配置
1、远程服务器配置
Manage Jenkins
-> Configure System
Publish over SSH
配置
温馨小提示:多台服务器则配置多个…
2、全局凭证配置
注:后面Jenkinsfile中需要使用到这里配置的全局凭证
① git全局凭证配置
② docker镜像仓库全局凭证配置
配置完后如下:
四、项目配置
1、Dockerfile
#FROM openjdk:8-jre-alpine 【 注:jre中并没有携带工具文件,但arthas需要依赖lib包和bin包里面的包和工具,arthas需要jps工具和lib包里的内容 具体参考:https://www.cnblogs.com/sky-chen/p/9887777.html 】
#FROM openjdk:8-jdk-alpine
#RUN apk add --update ttf-dejavu fontconfig && rm -rf /var/cache/apk/*
# 注:此镜像支持字体!
FROM registry.cn-hangzhou.aliyuncs.com/zhengqing/openjdk:8-jdk-alpine-font# 维护者信息
MAINTAINER zhengqingya# 构建镜像时传参数据
ARG APP_NAME
ARG APP_PORT
ARG JAVA_OPTS# 设置环境变量-运行时也可传参进来耍哈
ENV APP_NAME ${APP_NAME}
ENV APP_JAR ${APP_NAME}.jar
ENV APP_PORT ${APP_PORT}
ENV JAVA_OPTS ${JAVA_OPTS}
# -XX:+UseG1GC -Xms64m -Xmx64m -Xmn16m -XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m -XX:MaxGCPauseMillis=200 -XX:ParallelGCThreads=8 -Ddefault.client.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.language=Zh -Duser.region=CN -Dspring.profiles.active=xx -Dspring.cloud.nacos.discovery.server-addr=xx -Dspring.cloud.nacos.discovery.username=nacos -Dspring.cloud.nacos.discovery.password=nacos
# 远程调试参数: -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5001# 解决时差8小时问题
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone# copy arthas
COPY --from=hengyunabc/arthas:latest /opt/arthas /opt/arthasADD ${APP_JAR} /home/
RUN sh -c 'touch /'VOLUME /tmp# 对外暴漏的端口号
# [注:EXPOSE指令只是声明容器运行时提供的服务端口,给读者看有哪些端口,在运行时只会开启程序自身的端口!!]
EXPOSE ${APP_PORT}# 让你先休息3秒再开始运动吧???
CMD echo "****** Start... " & \echo "****** APP_JAR: ${APP_JAR} " & \echo "****** APP_PORT: ${APP_PORT} " & \echo "****** JAVA_OPTS: ${JAVA_OPTS} " & \echo "****** ${APP_JAR} will run ..." & \sleep 3 & \echo "****** 运行命令:nohup java -jar ${JAVA_OPTS} /home/${APP_JAR} >> /home/${APP_NAME}.log 2>&1 &" & \nohup java -jar ${JAVA_OPTS} /home/${APP_JAR} >> /home/${APP_NAME}.log 2>&1 & \echo "****** 查看日志..." & \tail -f /home/${APP_NAME}.log
2、Jenkinsfile
流水线语法可参考:https://www.jenkins.io/zh/doc/book/pipeline/syntax
// ==================== ↓↓↓↓↓↓ docker ↓↓↓↓↓↓ ====================
// 镜像仓库地址
def DOCKER_REGISTRY_URL = "registry.cn-hangzhou.aliyuncs.com"
// 镜像仓库命名空间名称
def DOCKER_REGISTRY_NAMESPACE = "zhengqingya"
// 镜像拉取凭证 => jenkins全局凭证中配置
def DOCKER_REGISTRY_AUTH = "aliyun_docker_registry_auth"// ==================== ↓↓↓↓↓↓ git ↓↓↓↓↓↓ ====================
// git凭证 => jenkins全局凭证中配置
def GIT_AUTH = "gitee_auth"// ==================== ↓↓↓↓↓↓ project ↓↓↓↓↓↓ ====================
// 项目-git地址
def PROJECT_GIT_URL = "https://gitee.com/zhengqingya/small-tools.git"
// 项目-公共模块名称
def PROJECT_SERVICE_COMMON_NAME = "small-tools-api/common"
def PROJECT_SERVICE_API_NAME = "small-tools-api/service-api"
// 项目-基础目录
def PROJECT_BASE_HOME = "/Users/zhengqingya/IT_zhengqing/soft/small-tools"
// 项目-服务日志目录
def PROJECT_SERVICE_LOG_HOME = "${PROJECT_BASE_HOME}/logs"
// 项目-jar文件目录
def PROJECT_JAR_HOME = "${PROJECT_BASE_HOME}/target"
// 项目-docker部署的历史jar目录
def PROJECT_DOCKER_HISTORY_JAR_HOME = "${PROJECT_BASE_HOME}/docker-history-jar"node {// jenkins工作空间def JENKINS_WORKSPACE = "${WORKSPACE}"// 取哪一块网卡值def ETH_VALUE = ""// 当前时间def CURRENT_TIME = ""// 获取选择的项目服务名称def project_service_name_select = "${PROJECT_SERVICE_NAME}".split(",")// 获取选择的服务器名称def publish_ssh_server_select = "${PUBLISH_SSH_SERVER}".split(",")// app镜像tagdef app_docker_image_tag = ""stage('初始化准备') {echo '****************************** 初始化准备 ******************************'// 网卡值设置 [注:正常liunx取eth0的内网ip,局域网取enp5s0的内网ip]ETH_VALUE = sh(script: "( [[ \"${JAVA_OPTS}\" = *'-Dspring.cloud.nacos.discovery.ip'* ]] && echo enp5s0 || echo eth0 )", returnStdout: true).trim()CURRENT_TIME = sh(script: "echo `date +\"%Y-%m-%d %H:%M:%S\"`", returnStdout: true).trim()script {app_docker_image_tag = GIT_BRANCH.replaceAll('origin/', '')}echo "当前工作空间:${JENKINS_WORKSPACE}"echo "使用分支:${GIT_BRANCH}"echo "使用docker镜像tag:${app_docker_image_tag}"echo "使用网卡:${ETH_VALUE}"echo "当前时间:${CURRENT_TIME}"sh "mkdir -p ${PROJECT_SERVICE_LOG_HOME}"sh "mkdir -p ${PROJECT_JAR_HOME}"sh "mkdir -p ${PROJECT_DOCKER_HISTORY_JAR_HOME}"}stage('拉取代码') {echo '****************************** 拉取代码 ******************************'checkout([$class: 'GitSCM', branches: [[name: "${GIT_BRANCH}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${GIT_AUTH}", url: "${PROJECT_GIT_URL}"]]])sh "pwd"}stage('公共工程打包') {echo '****************************** 公共工程打包 ******************************'sh "mvn -f ${PROJECT_SERVICE_COMMON_NAME} clean install -Dmaven.test.skip=true"sh "mvn -f ${PROJECT_SERVICE_API_NAME} clean install -Dmaven.test.skip=true"}stage('微服务打包&删除旧容器和镜像&推送镜像&部署远程服务器') {echo '****************************** 微服务打包&删除旧容器和镜像&推送镜像&部署远程服务器 ******************************'for (int i = 0; i < project_service_name_select.length; i++) {// 当前待处理项目def current_app_name = project_service_name_select[i];def current_app_jar = "${current_app_name}.jar";// 父工程def current_app_parent = ""// 端口号def current_app_port = ""echo "当前操作项目: ${current_app_name}"script {switch (current_app_name) {case "demo":current_app_parent = "small-tools-api/service"current_app_port = "20040"breakcase "system":current_app_parent = "small-tools-api/service"current_app_port = "20010"breakcase "tool":current_app_parent = "small-tools-api/service"current_app_port = "20030"breakcase "gateway":current_app_port = "1218"current_app_parent = "small-tools-api"break}}echo "当前操作项目: ${current_app_name} 端口:${current_app_port}"sh "mvn -f ${current_app_parent}/${current_app_name} clean install -Dmaven.test.skip=true"sh "cp ${current_app_parent}/${current_app_name}/target/${current_app_jar} ${JENKINS_WORKSPACE}/small-tools-api/docker"// app镜像def app_docker_image = "${DOCKER_REGISTRY_URL}/${DOCKER_REGISTRY_NAMESPACE}/${current_app_name}:${app_docker_image_tag}"echo "app镜像: ${app_docker_image}"// 删除旧容器sh "docker ps -a | grep ${current_app_name} | grep ${app_docker_image_tag} | awk '{print \$1}' | xargs -I docker stop {} | xargs -I docker rm {}"// 删除旧镜像sh "docker images | grep -E ${current_app_name} | grep ${app_docker_image_tag}| awk '{print \$3}' | uniq | xargs -I {} docker rmi --force {}"// 构建新镜像 -f:指定Dockerfile文件路径sh "cd ${JENKINS_WORKSPACE}/small-tools-api/docker && docker build -f Dockerfile --build-arg JAVA_OPTS=\"${JAVA_OPTS}\" --build-arg APP_NAME=${current_app_name} --build-arg APP_PORT=${current_app_port} -t ${app_docker_image} . --no-cache"echo '****************************** 推送镜像 ******************************'withCredentials([usernamePassword(credentialsId: "${DOCKER_REGISTRY_AUTH}", passwordVariable: 'password', usernameVariable: 'username')]) {// 登录镜像仓库sh "docker login -u ${username} -p ${password} ${DOCKER_REGISTRY_URL}"// 推送镜像sh "docker push ${app_docker_image}"sh "echo 镜像推送成功:${app_docker_image}"}// 操作完后移除docker目录下的jar文件,防止下次docker将多个jar一起构建导致镜像过大问题sh "mv -f ${JENKINS_WORKSPACE}/small-tools-api/docker/*.jar ${PROJECT_DOCKER_HISTORY_JAR_HOME}/"echo '****************************** 项目部署 ******************************'// 遍历所有服务器,分别部署for (int j = 0; j < publish_ssh_server_select.length; j++) {// 获取当前服务器名称def currentServerName = publish_ssh_server_select[j]echo '开始发布远程机器'sshPublisher(publishers: [sshPublisherDesc(configName: "${currentServerName}",transfers: [sshTransfer(cleanRemote: false, excludes: '',execCommand: """// 远程服务器下依次执行如下命令// 删除旧容器docker ps -a | grep ${current_app_name} | grep ${app_docker_image_tag} | awk '{print \$1}' | xargs -i docker stop {} | xargs -i docker rm {}// 删除旧镜像docker images | grep -E ${current_app_name} | grep ${app_docker_image_tag}| awk '{print \$3}' | uniq | xargs -I {} docker rmi --force {}// 拉取新镜像docker pull ${app_docker_image}// 创建日志文件mkdir -p ${PROJECT_SERVICE_LOG_HOME}cd ${PROJECT_SERVICE_LOG_HOME}touch ${current_app_name}.log// 运行容器echo "*** ${CURRENT_TIME} docker run -d -p ${current_app_port}:${current_app_port} -e JAVA_OPTS="${JAVA_OPTS} -Dspring.cloud.nacos.discovery.ip=`ifconfig ${ETH_VALUE} | grep inet | grep -v inet6 | awk '{print \$2}'`" -v ${PROJECT_SERVICE_LOG_HOME}/${current_app_name}.log:/home/${current_app_name}.log --name ${current_app_name} ${app_docker_image}" >> ${PROJECT_SERVICE_LOG_HOME}/run.logdocker run -d -p ${current_app_port}:${current_app_port} -e JAVA_OPTS="${JAVA_OPTS} -Dspring.cloud.nacos.discovery.ip=`ifconfig ${ETH_VALUE} | grep inet | grep -v inet6 | awk '{print \$2}'`" -v ${PROJECT_SERVICE_LOG_HOME}/${current_app_name}.log:/home/${current_app_name}.log --name ${current_app_name} ${app_docker_image}""",execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false,patternSeparator: '[, ]+', remoteDirectory: '', remoteDirectorySDF: false, removePrefix: '',sourceFiles: '')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])}}}}
五、jenkins新建任务
参数化配置
GIT_BRANCH
PROJECT_SERVICE_NAME
PUBLISH_SSH_SERVER
注:value值为之前在
Manage Jenkins
->Configure System
中配置的SSH Server
Name值
JAVA_OPTS
流水线
配置完成后应用保存
六、构建测试
温馨小提示:第1次构建的时候会出现如下情况,是因为未拉取远程git仓库获取分支信息,第2次就好了…
构建成功如下
一个简单的部署方案完成 ^_^
七、其它
# 打包镜像 -f:指定Dockerfile文件路径
docker build -f Dockerfile --build-arg JAVA_OPTS="-XX:+UseG1GC -Xms64m -Xmx64m -Xmn16m -XX:MetaspaceSize=100m -XX:MaxMetaspaceSize=100m -XX:MaxGCPauseMillis=200 -XX:ParallelGCThreads=8 -Ddefault.client.encoding=UTF-8 -Dfile.encoding=UTF-8 -Duser.language=Zh -Duser.region=CN" --build-arg APP_NAME="demo" --build-arg APP_PORT="80" -t "registry.cn-hangzhou.aliyuncs.com/zhengqingya/demo:dev" . --no-cache# 推送镜像
docker push registry.cn-hangzhou.aliyuncs.com/zhengqingya/demo:dev# 拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/zhengqingya/demo:dev# 运行
docker run -d -p 80:80 -v /home/zhengqingya/demo.log:/home/demo.log --name demo registry.cn-hangzhou.aliyuncs.com/zhengqingya/demo:dev# 删除旧容器
#docker ps -a | grep demo | grep dev | awk '{print $1}' | xargs -i docker stop {} | xargs -i docker rm {}
docker ps -a | grep demo | grep dev | awk '{print $1}' | xargs -I docker stop {
} | xargs -I docker rm {
}# 删除旧镜像
docker images | grep -E demo | grep dev | awk '{print $3}' | uniq | xargs -I {
} docker rmi --force {
}
今日分享语句:
总有一天,你会明白:你的委屈要自己消化,你的故事不用逢人就讲起;真正理解你的没有几个,大多人只会站在他们自己的立场,偷看你的笑话;你能做的就是把秘密藏起来,然后一步一步变得越来越强大!