top-ww头像

用 Docker 在 Anolis OS 上搭 Jenkins,自动发布 Maven 多模块和 Vue 前端

用 Docker 在 Anolis OS 上搭 Jenkins,自动发布 Maven 多模块和 Vue 前端

后端和前端放在同一个 Git 仓库里,手工打包、停服务、传包、再启动,项目一多就容易漏步骤。这篇记录怎么用 Docker 跑 Jenkins,把四个项目收成四个流水线:三个 Java 服务,一个 Vue 管理端。

Jenkins 装在一台 Anolis OS 8.10 上。业务跑在另一台部署机上。打包在容器里完成,再通过 SSH 把产物推过去。JDK、Maven、Node 都放在 Jenkins 的数据卷里,不会碰到部署机上已经在跑的进程。

仓库地址写成 https://gitee.com/example/platform.git,分支 master。部署机写成 DEPLOY_HOST。按自己的地址替换即可。

四个项目的对应关系:

项目模块目录产物部署目录构建环境
用户中心useruser.jar/home/userJDK 8
订单中心orderorder.jar/home/orderJDK 17
内容中心contentcontent.jar/home/contentJDK 17
管理端admindist//data/wwwroot/adminNode 22

用 Docker 启动 Jenkins

部署机上的 8080 通常已经有别的服务,宿主机端口改到 18080。镜像用 Jenkins LTS,时区设为上海。

/opt/jenkins/docker-compose.yml:

services:
  jenkins:
    image: docker.m.daocloud.io/jenkins/jenkins:2.580.1-lts-jdk21
    container_name: jenkins
    restart: unless-stopped
    ports:
      - "18080:8080"
    volumes:
      - jenkins_home:/var/jenkins_home
    environment:
      TZ: Asia/Shanghai
      JAVA_OPTS: "-Duser.timezone=Asia/Shanghai -Xmx1g"

volumes:
  jenkins_home:
cd /opt/jenkins
docker compose up -d

云服务器安全组放行 18080,浏览器打开 http://Jenkins机器公网IP:18080。首次解锁密码:

docker exec jenkins cat /var/jenkins_home/secrets/initialAdminPassword

创建一个管理员,例如 admin。任务、历史记录和后面安装的 JDK、Maven、Node 都在 volume jenkins_home 里,容器重启后还在。

插件源用清华镜像:

https://mirrors.tuna.tsinghua.edu.cn/jenkins/plugins/

流水线需要这些插件及其依赖:workflow-aggregator、workflow-cps、workflow-job、git。依赖没装齐时,新建任务里看不到「流水线」,配置页的 Pipeline 下拉也是空的。

准备 JDK、Maven 和 Node

在「系统管理 → 全局工具配置」里添加四项,名字要和流水线里的完全一致:

  • jdk-8
  • jdk-11
  • jdk-17
  • maven-3.9

JDK 从清华 Adoptium 镜像下载。个别镜像会返回一段 HTML,tar 解开会失败,换一个镜像源即可。

Node 直接放进数据卷,供前端流水线使用。官方包是 .tar.xz,Jenkins 镜像里没有 xz,会报 xz: Cannot exec。下载 .tar.gz:

docker exec -u root jenkins bash -lc '
set -eu
rm -rf /tmp/node-install
mkdir -p /tmp/node-install
cd /tmp/node-install
curl -fL -o node.tar.gz https://npmmirror.com/mirrors/node/v22.23.4/node-v22.23.4-linux-x64.tar.gz
tar -xzf node.tar.gz
rm -rf /var/jenkins_home/tools/node
mv node-v22.23.4-linux-x64 /var/jenkins_home/tools/node
chown -R jenkins:jenkins /var/jenkins_home/tools/node
/var/jenkins_home/tools/node/bin/node -v
'

输出 v22.23.4 即安装完成。Vite 6.4.3 接受 Node 18、20,以及 22 及以上,19 和 21 不在范围内。

配置两个凭据

拉代码,ID 为 git-gitee。 类型选 Username with password,密码填 Gitee 私人令牌,令牌需要有读取仓库的权限。流水线里的 credentialsId 必须和这个 ID 一致。令牌无效时,git fetch 返回 401。

发包,ID 为 deploy-ssh。 部署机上单独建一个 jenkins 用户,把公钥写入该用户的 ~/.ssh/authorized_keys。Jenkins 里新建「SSH Username with private key」,用户名填 jenkins,私钥整段贴进去,包括 BEGIN OPENSSH PRIVATE KEY 那两行。密钥对保存好。以后新机器继续用这把公钥,流水线不用改凭据。

后端流水线

三个服务都是根工程下的子模块。-pl 后面写仓库里的目录名。目录名写错时,Maven 报 Could not find the selected project in the reactor。

父 POM 要先装进本地仓库,否则子模块报 Non-resolvable parent POM:

mvn -B -N -DskipTests install
mvn -B -DskipTests -pl 模块目录 -am clean package

公共模块如果把编译目标设成 17,内容和订单中心就要选 jdk-17。用 8 或 11 去编它,会得到 invalid target release: 17。用户中心维持 JDK 8。

发布在部署机的项目目录里做三件事:执行 ./stop.sh,把当前 jar 复制成 原名.jar.back.当天日期,再上传新 jar 并启动。备份放在项目目录下,例如 user-center.jar.back.20261027。

远程多行命令要用 heredoc 交给对方的 bash。不加 heredoc 时,换行会被吃掉,主机名和后面的命令粘在一起。

用户中心的启动脚本是 ./start,内容中心是 ./start.sh,以部署目录里的真实文件名为准。

pipeline {
  agent any
  tools {
    jdk 'jdk-8'
    maven 'maven-3.9'
  }
  options {
    timestamps()
    disableConcurrentBuilds()
    buildDiscarder(logRotator(numToKeepStr: '20'))
  }
  stages {
    stage('拉取') {
      steps {
        git branch: 'master',
            url: 'https://gitee.com/example/platform.git',
            credentialsId: 'git-gitee'
      }
    }
    stage('打包') {
      steps {
        sh '''
          set -eu
          mvn -B -N -DskipTests install
          mvn -B -DskipTests -pl user -am clean package
          jar=$(ls user/target/*.jar | grep -v sources | grep -v javadoc | grep -v '.original' | head -1)
          cp "$jar" user/target/user.jar
          ls -l user/target/user.jar
        '''
      }
    }
    stage('发布') {
      steps {
        withCredentials([sshUserPrivateKey(credentialsId: 'deploy-ssh', keyFileVariable: 'KEY')]) {
          sh '''
            set -eu
            ssh -i "$KEY" -o StrictHostKeyChecking=accept-new jenkins@DEPLOY_HOST bash -s << 'REMOTE'
cd /home/user
./stop.sh || true
if [ -f user.jar ]; then
  cp -a user.jar user.jar.back.$(date +%Y%m%d)
fi
REMOTE
            scp -i "$KEY" -o StrictHostKeyChecking=accept-new user/target/user.jar jenkins@DEPLOY_HOST:/home/user/user.jar
            ssh -i "$KEY" -o StrictHostKeyChecking=accept-new jenkins@DEPLOY_HOST bash -s << 'REMOTE'
cd /home/user
./start
REMOTE
          '''
        }
      }
    }
  }
  post {
    success {
      archiveArtifacts artifacts: 'user/target/user.jar', fingerprint: true
    }
  }
}

订单中心把模块、jar、目录换成 order、order.jar、/home/order,工具换成 jdk-17。内容中心换成 content、content.jar、/home/content,JDK 同样是 17,启动命令写成 ./start.sh`。

第一次构建要下载 Maven 依赖,会比以后慢。成功后,构建记录里可以直接下载 jar。容器内还有两份:

/var/jenkins_home/workspace/任务名/模块/target/xxx.jar
/var/jenkins_home/jobs/任务名/builds/构建号/archive/...

把目录交给 jenkins 用户

服务如果原来是 root 启动的,jenkins 停不掉进程,也覆盖不了 jar。日志里会看到 kill: Operation not permitted,以及 scp: Permission denied。

在部署机上用 root 做一次交接:

cd /home/content && ./stop.sh || true
cd /home/order && ./stop.sh || true
ps -ef | grep -E 'content|order' | grep -v grep

chown -R jenkins:jenkins /home/content /home/order
chmod 755 /home/content/stop.sh /home/content/start.sh
chmod 755 /home/order/stop.sh /home/order/start.sh

ps 里如果还有进程,用 root 杀掉,再执行一次 chown。然后回到 Jenkins 重新构建。新进程的属主是 jenkins,以后的 stop.sh 才能生效。交接之后,不要再在部署机上用 root 启动这些服务。

前端流水线

frontend-admin 是 Vue 3 + Vite 6,产物是静态文件,发布时不用停进程。先把整个站点复制到同级目录 /data/wwwroot/admin.back.当天日期,再清空原目录,写入新的 dist。同一天多次发布,只保留当天最后一份备份。

部署机上先授权:

mkdir -p /data/wwwroot/admin
chown jenkins:jenkins /data/wwwroot
chown -R jenkins:jenkins /data/wwwroot/admin
pipeline {
  agent any
  options {
    timestamps()
    disableConcurrentBuilds()
    buildDiscarder(logRotator(numToKeepStr: '20'))
  }
  stages {
    stage('拉取') {
      steps {
        git branch: 'master',
            url: 'https://gitee.com/example/platform.git',
            credentialsId: 'git-gitee'
      }
    }
    stage('打包') {
      steps {
        sh '''
          set -eu
          export PATH="/var/jenkins_home/tools/node/bin:$PATH"
          node -v
          npm -v
          cd frontend-admin
          npm config set registry https://registry.npmmirror.com
          if [ -f package-lock.json ]; then
            npm ci
          else
            npm install
          fi
          npm run build
          test -d dist
          ls dist
        '''
      }
    }
    stage('发布') {
      steps {
        withCredentials([sshUserPrivateKey(credentialsId: 'deploy-ssh', keyFileVariable: 'KEY')]) {
          sh '''
            set -eu
            ssh -i "$KEY" -o StrictHostKeyChecking=accept-new jenkins@DEPLOY_HOST bash -s << 'REMOTE'
set -eu
mkdir -p /data/wwwroot/admin
if [ -n "$(ls -A /data/wwwroot/admin 2>/dev/null)" ]; then
  rm -rf /data/wwwroot/admin.back.$(date +%Y%m%d)
  cp -a /data/wwwroot/admin /data/wwwroot/admin.back.$(date +%Y%m%d)
fi
find /data/wwwroot/admin -mindepth 1 -maxdepth 1 -exec rm -rf {} +
REMOTE
            tar -C frontend-admin/dist -czf - . | ssh -i "$KEY" -o StrictHostKeyChecking=accept-new jenkins@DEPLOY_HOST 'tar -C /data/wwwroot/admin -xzf -'
          '''
        }
      }
    }
  }
}

有 package-lock.json 时用 npm ci。npm 走 registry.npmmirror.com。构建完成后,部署目录里应有 index.html。

日常发布

打开 Jenkins,进入对应任务,点击「立即构建」。后端流水线会停掉旧进程、按日期备份 jar、上传新包并启动。前端流水线会备份整个站点目录,再覆盖静态文件。每个任务保留最近 20 次构建。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/wen_3370/article/details/169263665

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--