Jenkins容器配合Github Webhook实现Java代码自动拉取编译部署为Docker容器

391 阅读14分钟

一、Jenkins配置

进入 Jenkins 页面,点击左侧的 Manage Jenkins 进行配置

image-20241025105904390.png

1. 工具配置

在 Jenkins 的配置管理页面,选择 Tool ,进入工具配置管理页面

image-20241025155722041.png

在工具配置管理页面可以配置 Java, Maven, Git 等工具

配置 Java

进入 Java清华镜像,选择 Java 版本,复制对应下载的 URL,进入宿主机的目录,执行命令

# 下载 OpenJDK21
$ wget https://mirrors.tuna.tsinghua.edu.cn/Adoptium/21/jdk/x64/linux/OpenJDK21U-jdk_x64_linux_hotspot_21.0.5_11.tar.gz

将下载好的文件移动到 Jenkins 挂载目录中,这里用的是 1Panel 进行挂载

# 将下载的压缩包复制到 Jenkins 容器挂载的目录中
$ mv OpenJDK21U-jdk_x64_linux_hotspot_21.0.5_11.tar.gz /opt/1panel/apps/jenkins/jenkins/data/openjd21.tar.gz

进入 Jenkins 容器内部,移动并解压压缩包

# 宿主机进入 Jenkins 容器
$ sudo docker exec -it 1panel_jenkins bash
# 进入挂载目录拿到压缩包并解压
$ cd /var/jenkins_home
$ mv openjdk21.tar.gz /opt/java/
# 将压缩包解压
$ cd /opt/java
$ tar -xvf openjdk21.tar.gz
# 重命名解压目录
$ mv jdk21xxx/ openjdk21/

在 Jenkins 页面中点击 JDK安装,新增JDK配置,输入名称和路径

image-20241025161053198.png

配置 Maven

进入 Maven清华镜像源,选择 Maven 版本(这里是Maven-3,可以切换目录选择Maven2或Maven4),复制对应的下载URL,进入宿主机,执行下载命令

# 下载 apache-maven-3.9.9
$ wget https://mirrors.tuna.tsinghua.edu.cn/apache/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz

将下载好的文件同样移动到 Jenkins 容器的挂载目录中

# 移动文件至Jenkins挂载目录
$ mv apache-maven-3.9.9-bin.tar.gz /opt/1panel/apps/jenkins/jenkins/data/apache-maven-3.9.9.tar.gz

进入 Jenkins 容器,移动并解压下载包

# 宿主机进入 Jenkins 容器
$ sudo docker exec -it 1panel_jenkins bash
# 进入 Jenkins 挂载目录,移动压缩包
$ cd /var/jenkins_home
$ mv apache-maven-3.9.9.tar.gz /opt/maven
# 将压缩包解压
$ mv /opt/maven
$ tar -xvf apache-maven-3.9.9.tar.gz

在 Jenkins 页面拖动到尾部,选择 Maven 安装,并取消勾选自动安装,输入名称和工作目录

image-20241025161749725.png

2. 凭据配置

进入 Jenkins 配置管理页面,选择 凭据管理

image-20241025161933924.png

在凭据管理页面,选择列表中域下方的 全局

image-20241025161933924.png

在全局凭据页面点击右上方的 Add Credentials 添加凭据

image-20241025162138798.png

配置 Github SSH 私钥

进入jenkins容器,使用 cd ~ 命令进入工作目录,随后使用 ssh-keygen 生成公钥和私钥

# 宿主机进入 docker 容器
$ sudo docker exec -it 1panel_jenkins bash
# 进入容器工作目录,生成 ssh-keygen
root@aed76f497e0a:/# cd ~
root@aed76f497e0a:~# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): # 这里要求输入ssh目录, 使用默认直接回车即可
Created directory '/root/.ssh'.
Enter passphrase (empty for no passphrase): # 这里同样要求输入私钥密文, 默认为空直接回车即可
Enter same passphrase again: # 再次输入私钥密文, 和上面一样直接回车
Your identification has been saved in /root/.ssh/id_rsa
Your public key has been saved in /root/.ssh/id_rsa.pub
The key fingerprint is:
SHA256:cWqus7d9DV3W6cP0v8kGABKJslR/xZvlZgF/EK8f7eg root@aed76f497e0a
The key's randomart image is:
+---[RSA 3072]----+
|    ....o .o.o.  |
|   o ..o ....oo  |
|  . o  .o.o =..oo|
|   .    .+ + +oo=|
|        S   +o++o|
|       o    ..o*o|
|        .    oo.+|
|      ....  ..o.o|
|      o+....  E+.|
+----[SHA256]-----+

登录到 Github 页面,点击头像,出现侧边栏,选择 Settings 进入设置页面,在设置页面选择 SSH and GPG keys ,点击 New SSH key 添加刚才创建的 ssh Key

image-20241030152016164.png

在 docker 容器内部进入 .ssh 目录,查看刚才生成的公钥

root@aed76f497e0a:~# cd .ssh/
root@aed76f497e0a:~/.ssh# ls
id_rsa  id_rsa.pub
root@aed76f497e0a:~/.ssh# cat id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQC41sC+SUEcwaHM9JoLYWcFNd4CrYBMcmtEyUCCbO26wbd6j56ZQdZ5x5rHUN7Att5ZZeaS3YQSOT5ApXEaqLXOmXpiXU7E+qA9UaKS/X4zRj81ipnSdOsAokqC+tqey+mVGy77sVvl6g78v+dUe6CREzT8wS5OYrabPHuZ4tZYBVcA538cWr67ALn+7F5eGVkuTzirdwucWVCk50BYb+ME6gwbRviFO4OOUW29njbiPuQr427XcYy9DslLXTwkwGRU+X6vJ2Us+mRchyTz32VaIIrR1M5EjMKs54mKE50voCok2oH07zvUGopMS8L25GfetOmMGYKZdD08GDafIkNhPXtZg+1vKOo0UxqIqU10b3FQ58w8UdLBrsX3iYaUnAdhlRAZdPYjCAtw5nWiKliG3Al9fW7dqbl2Q8Ox2OzW2NiUahSWDHDs73yJZR2H/LuxnnzOmumH0Hmdu+tTtKn+6mQk2PEByIZUkt9Z3Fig6VJgyePYknp2oAiMqn+qKxE= root@aed76f497e0a

将公钥内容复制到复制到 Key 内容框下,创建公钥

image-20241030152632917.png

然后打开 Jenkins 的系统管理页面,在凭据添加页面中,选择类型为 SSH Username with Private Key 添加 SSH 私钥凭据

输入凭据的 ID,描述,Username

image-20241025162453374.png

在私钥处选择 Enter Directly,Key点击 添加 输入私钥,这里私钥使用的是宿主机登录 Girthub 使用的私钥

# 查看 ~/.ssh 目录下私钥
root@aed76f497e0a:~/.ssh# cat id_rsa
# 以下全为私钥内容
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----

将私钥内容复制到剪贴板,并粘贴到页面上;Passphrase可置空,点击Create添加私钥

image-20241030153342178.png

配置 Github App Key

首先进入Github页面,点击 Settings 进入账户设置页面,并在左下角选择 Developer settings 开发者选项

image-20241025171749727.png

进入开发者选项页面后,选择 Personal access token 创建个人账户访问 Token,并选择 classic

image-20241025171907770.png

在 Access Token 的访问范围内,勾选 reporepo_hook,点击确认生成

image-20241025172005784.png

生成后图中的部分即为 access_token,将其复制

image-20241025172126196.png

进入 Jenkins 的配置页面,分别输入用户名和密码。

用户名输入 Github 登录账户,密码输入上方刚生成的 access_token

image-20241025172451646.png

点击 Create 创建该凭据

3. 远程发布配置

安装 ssh Publisher 插件

在 Jenkins 系统管理页面,选择 插件管理

image-20241031165506553.png

在插件管理页面点击左侧的 Avaiable Plugin 并搜索 Publish Over SSH 插件,这里已经安装

image-20241031165731547.png

配置发布到的远程主机

在 Jenkins 系统管理页面选择 系统配置

image-20241031165846808.png

向下翻阅,找到 Publish over SSH 这个配置项,选择在 Path to Key 输入 SSH 私钥路径,或者在 Key 处输入私钥内容

image-20241031170038939.png

继续向下拖动,找到 SSH Servers 这个配置项,点击新增添加一个配置,分别输入名称,主机名,用户名,远程目录。最后可以点击右下方的 Test Configuration 来测试该配置是否生效

image-20241031170402505.png

创建远程用户

在上面配置了发布到的远程主机,这一步我们将创建远程用户

一般来说 Jenkins 容器和发布运行的容器不在同一台服务器上,不过本文中只有一台云主机,因此流程是

Jenkins 容器 --远程发布--> Host主机 ----> docker-compose 部署

这一步也可以使用当前的主机用户,如 root, ubuntu 等等,不过这里为了隔离,选择创建一个新的用户

# 切换为 root 用户
$ su
# 创建用户
root:/# useradd -s /bin/bash -m jenkins
# 修改新用户密码
root:/# passwd
New password: # 输入密码
Retype new password: # 二次输入
passwd: password updated successfully

接下来需要修改 sshd_config 文件,允许新用户通过密码和SSH登录,默认路径为 /etc/ssh/sshd_config

# 允许用户通过密码登录, 因为传输公钥需要输入密码
PasswordAuthentication yes
# 允许使用 ssh 登录的用户, 如果为 * 则不需要改动, 否则需要在末尾加入新用户
AllowUsers root jenkins

此外,由于 docker 的权限在 root 用户手中,因此其他用户使用 docker 命令时必须使用 sudo 输入密码提升权限,然而脚本执行过程中不能够输入密码,因此还需要修改 /etc/sudoers 文件,允许该用户无需密码执行 docker 命令

修改suoders文件,添加一行

jenkins ALL=(ALL) NOPASSWD: /usr/bin/docker, /var/run/docker.sock, /usr/local/bin/docker-compose

将公钥添加到远程用户

接下来在容器内部,通过 ssh-copy-id 命令将自己的公钥添加到远程用户,随后使用 ssh 进行登录

# 使用 ssh-copy-id 复制公钥到远程用户
root@aed76f497e0a:~# ssh-copy-id -i ~/.ssh/id_rsa.pub jenkins@172.18.0.1
/usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/root/.ssh/id_rsa.pub"
/usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
# 这里输入远程用户的密码
jenkins@172.18.0.1's password:
​
Number of key(s) added: 1
​
Now try logging into the machine, with:   "ssh 'jenkins@172.18.0.1'"
and check to make sure that only the key(s) you wanted were added.
​
# 使用 ssh 连接到远程用户
root@aed76f497e0a:~# ssh jenkins@172.18.0.1
​
 System information as of Fri Nov  1 09:41:07 AM CST 2024
​
  System load:  0.09               Processes:             182
  Usage of /:   31.4% of 58.76GB   Users logged in:       1
  Memory usage: 54%                IPv4 address for eth0: 172.16.0.3
  Swap usage:   0%
​
  => There is 1 zombie process.
Welcome to JDCLOUD Elastic Compute Service
Last login: Thu Oct 31 15:34:45 2024 from xxx.xxx.xxx.xxx
jenkins@xxx-xxxx:~$

二、创建流水线

1. 基础配置

进入 Jenkins 面板,点击左侧的 新建 Item

image-20241025173433648.png

在任务类型列表中选择 Pipeline(流水线)

image-20241025173517289.png

1.1 配置 Github 地址

在流水线配置中勾选Github项目,并输入项目的URL

image-20241025173721772.png

1.2 配置构建参

在配置中,勾选参数化构建过程,这个允许每次构建时用户输入指定参数,并支持默认值

这里配置一个参数 JAR_VERSION,因为我们打出来的包会携带版本号,所以在这里设置一个 JAR_VERSION 参数方便更换

image-20241031100905135.png

1.3 配置 Github WebHook

下方的触发器根据个人情况勾选,这里选择了 WebHook

image-20241025173803053.png

2. 流水线脚本

接下来需要编写流水线脚本,可以点击下方的 流水线语法 辅助编写

image-20241030153726268.png

这里逐步给出一个完整的 pipeline

2.1 流水线框架

pipeline 的整个流程大致框架如下

pipeline {
   // 声明任务可以在哪种节点上运行
   agent any
​
   // 声明需要使用的工具, 运行时会将这些工具的路径添加到 PATH 中,也可以在具体的stage中声明
   tools { ... }
​
   // 声明运行时环境变量
   environment { ... }
​
   // 声明整个流水线不同阶段
   stages {
       // 声明一个阶段
       stage('Checkout') {
           // 声明阶段中的步骤
           steps {
               // 具体步骤
               sh 'echo Hello,World'
           }
       }
   }
}

2.2 声明运行时环境变量

在 pipeline 脚本中,在顶层声明运行时环境变量

pipeline {
   ...
​
   // 声明运行时环境变量
   environment {
       // 声明项目名称
       PROJECT_NAME = "test-hook"
       // 声明 JAR 包名称, ${JAR_VERSION} 为流水线的参数化变量
       JAR_NAME = "test-hook-${JAR_VERSION}.jar"
   }
}

2.3 拉取 Git 代码

接下来进入 Stage 阶段,第一步拉取 Github 代码,点击 流水线语法 进入流水线生成页面

示例步骤的类型选择 git: Git ,输入仓库的URL和分支,并选择凭据 如果URL是https类型,则选择上述配置的 Github App Key,类型为 Username and Password

上述有报错是因为 TLS handshake 不稳定,因此一般推荐使用 ssh,URL为 git@github.com:xxx 的形式

image-20241030170714068.png

如果出现上述报错,说明 git 还没有 known_host 等信息,此时只需在容器内手动 git clone 一下即可

root@aed76f497e0a:~# git clone git@github.com:<USER_NAME>/test-hook.git
Cloning into 'test-hook'...
The authenticity of host 'github.com (20.205.243.166)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes # 这里输入 yes
Warning: Permanently added 'github.com' (ED25519) to the list of known hosts.
remote: Enumerating objects: 33, done.
remote: Counting objects: 100% (33/33), done.
remote: Compressing objects: 100% (19/19), done.
remote: Total 33 (delta 10), reused 26 (delta 6), pack-reused 0 (from 0)
Receiving objects: 100% (33/33), 4.47 KiB | 508.00 KiB/s, done.
Resolving deltas: 100% (10/10), done.
root@aed76f497e0a:~#

此时重新选择凭据则不会出现问题,点击生成流水线脚本,即可生成 git 拉取的脚本

image-20241030171042529.png

此时将生成的内容复制,回到流水线脚本,继续编写

pipeline {
    ...
    
    stages {
        stage('Checkout') {
            steps {
                // 1. 从 Github 拉取代码
                git branch: 'main', credentialsId: 'jenkins-github-host-private-key', url: 'git@github.com:<USER_NAME>/test-hook.git'
            }
        }
    }
}

2.4 Maven编译打包

在 Git 拉取项目之后,接下来使用 maven 进行打包,不过在这一步首先需要声明 Java 和 Maven 这两个工具,否则运行时会报错找不到环境变量

点击 流水线语法 进入模板界面,在示例步骤中选择 tool: ,并选择具体的 tool,最后点击 生成流水线脚本 生成对应的语句

image-20241030165237778.png

使用这种方式声明jdk和maven工具,git工具看情况声明

pipeline {
    ...
​
    stages {
        ...
            
        stage('Compile-Package') {
            tools {
                jdk 'OpenJDK21'
                maven 'apache-maven-3.9.9'
            }
        }
    }
}

声明完工具后就可以进行编译打包工作了

stage('Compile-Package') {
    tools { ... }
    steps {
        sh 'mvn -Dmaven.test.failure.ignore=true clean package'
    }
}

在这里声明一个 post 区域,如果打包成功,会在当前工作目录生成 target 文件夹,里面存储打包好的 JAR 包

我们需要准备 docker 部署所需要的相关文件,有以下几个:

  • docker-compose.yaml:编排模板,使用占位符配合.env文件方便修改

    networks:
        1panel-network:
            external: true
    services:
        java:
            command: bash /app/run.sh
            # 容器名称
            container_name: ${CONTAINER_NAME}
            # 使用的 Java 版本
            image: bitnami/java:${JAVA_VERSION}
            labels:
                createdBy: Apps
            networks:
                - 1panel-network
            ports:
                # 暴露对外访问IP:主机使用端口:容器内部使用端口
                - ${HOST_IP}:${APP_HOST_PORT}:${APP_CONTAINER_PORT}
            restart: no
            volumes:
                # 可执行文件所在的目录
                - ${RUNNABLE_DIR}:/app/
                - ./run.sh:/app/run.sh
                - ./.env:/app/.env
            working_dir: /app
    
  • .env:提供 docker-compose.yaml 使用的环境变量,方便修改

    CODE_DIR="/opt/1panel/apps/jenkins/jenkins/data/runtime/java/"
    CONTAINER_NAME="test-hook-app"
    EXEC_SCRIPT="java -jar test-hook-1.0.jar"
    HOST_IP="0.0.0.0"
    JAVA_APP_PORT=8080
    JAVA_VERSION=21
    PANEL_APP_PORT_HTTP=18664
    
  • run.sh:容器内执行脚本,用于运行 Java 应用

    #!/bin/bashsource /app/.env
    ​
    eval $EXEC_SCRIPT
    

可以看到上面这三个文件中, docker-compose.yaml 和 run.sh 基本不用变动,只需要更改 .env 文件即可,因此该阶段就是生成对应的 .env 文件,这里使用了 env-setup.sh bash脚本来生成

#!/bin/bash# 运行目录
RUNNABLE_DIR=""
# 容器名称
CONTAINER_NAME=""
# 执行脚本
EXEC_SCRIPT=""
# 主机IP(默认对外访问)
HOST_IP="0.0.0.0"
# 应用主机端口
APP_HOST_PORT=0
# 应用容器端口
APP_CONTAINER_PORT=0
# JAVA版本
JAVA_VERSION=0
​
function help() {
    echo "Usage: env-setup.sh [OPTION]
​
      -d, --run-dir
        指定可执行文件在主机的位置
      -n, --con-name
        指定容器名称
      -e, --exec-script
        指定容器运行脚本
      -i, --host-ip
        指定容器对外允许的IP (默认为 0.0.0.0)
      -p, --host-port
        指定主机占用端口
      -q, --con-port
        指定容器内部端口
      -v, --java-version
        指定使用的JDK版本"
}
​
function verifyVariables() {
  # 判断参数
  local errors=()  # 用于收集错误信息
​
  [ -z "$RUNNABLE_DIR" ] && errors+=("run-dir unset")
  [ -z "$CONTAINER_NAME" ] && errors+=("con-name unset")
  [ -z "$EXEC_SCRIPT" ] && errors+=("exec-script unset")
  [ "$APP_HOST_PORT" -le 0 ] && errors+=("host-port invalid")
  [ "$APP_CONTAINER_PORT" -le 0 ] && errors+=("con-port invalid")
  [ "$JAVA_VERSION" -le 0 ] && errors+=("java-version unset")
​
  # 如果有错误,则输出并返回非零状态
  if [ ${#errors[@]} -ne 0 ]; then
    printf "%s\n" "${errors[@]}"
    printf "%s\n" "$(help)"
    return 1
  fireturn 0  # 所有检查通过
}
​
function printVariables() {
    file="$1"
    echo -n "" > "$file"
    
    # 这里输出时对于字符串文本, 需要手动加上单引号, 否则解析时可能报错
    {
      echo "RUNNABLE_DIR='$RUNNABLE_DIR'"
      echo "CONTAINER_NAME='$CONTAINER_NAME'"
      echo "EXEC_SCRIPT='$EXEC_SCRIPT'"
      echo "HOST_IP='$HOST_IP'"
      echo "APP_HOST_PORT=$APP_HOST_PORT"
      echo "APP_CONTAINER_PORT=$APP_CONTAINER_PORT"
      echo "JAVA_VERSION=$JAVA_VERSION"
    } >> "$file"
}
​
parameters=$(getopt -o d:n:e:i:p:q:v: \
-l run-dir:,con-name:,exec-script:,host-ip:,host-port:,con-port:,java-version: \
-n "$0" -- "$@")
​
if [ $? != 0 ]; then
  echo "parse parameters failed, terminating...";
  exit 1;
fi# 将$parameters设置为位置参数
eval set -- "$parameters"# 循环解析位置参数
while true ; do
    case "$1" in
        -d|--run-dir) RUNNABLE_DIR="$2"; shift 2;;
        -n|--con-name) CONTAINER_NAME="$2"; shift 2;;
        -e|--exec-script) EXEC_SCRIPT="$2"; shift 2;;
        -i|--host-ip) HOST_IP="$2"; shift 2;;
        -p|--host-port) APP_HOST_PORT="$2"; shift 2;;
        -q|--con-port) APP_CONTAINER_PORT="$2"; shift 2;;
        -v|--java-version) JAVA_VERSION="$2"; shift 2;;
        --) shift; break;;
        *) echo -e "invalid option\n$(help)";exit 1;;
    esac
done# 校验参数
verifyVariables
if [ $? != 0 ]; then
  exit 1;
fi
​
printVariables ".env"

在使用这个脚本时,只需要指定相关参数即可,如:

$ bash ./env-setup.sh \
       --run-dir="./" \
       --con-name=test-hook \
       --exec-script="java -jar test-hook-1.0.jar" \
       --host-port=8192 --con-port=8080 --java-version=21

至此容器部署所需要的工具已全部具备,这些工具全部放在 ${JENKINS_HOME}/runtime/java/.script 目录下,可以根据个人需要进行动态调整

接下来我们需要编写 post success 脚本,将这些工具复制过来,并执行脚本生成 .env 文件,这些生成的文件全部放在 target 目录下

stage('Compile-Package') {
    tools { ... }
    steps { ... }
    post {
        success {
            sh '''
                cd target
                cp ${JENKINS_HOME}/runtime/java/.script/docker-compose.yaml ./
                cp ${JENKINS_HOME}/runtime/java/.script/run.sh ./
                cp ${JENKINS_HOME}/runtime/java/.script/env-setup.sh ./
​
                bash ./env-setup.sh \
                --run-dir="./" --con-name=test-hook \
                --exec-script="java -jar ${JAR_NAME}" \
                --host-port=8192 --con-port=8080 --java-version=21
            '''
        }
    }
}
​

2.5 远程部署

现在程序包和docker相关文件已经准备完毕,接下来需要将其部署到远程主机,点击流水线左下方的 流水线语法 进入流水线生成页面

示例步骤处选择 sshPublisher

image-20241101102739289.png

image-20241101140925045.png

在配置完这些之后可根据个人需求,点击高级下拉菜单,进行修改。最后点击下方的 生成流水线脚本 生成相关脚本,并填入 pipeline 中

stages {
    ...
    
    stage('Remote-Deploy') {
        steps {
            // 这里使用脚本区域,判断 .env 文件是否正确生成,如果没问题则进行传输
            script {
                if (fileExists("./target/.env")) {
sshPublisher(publishers: [sshPublisherDesc(configName: 'Host_Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''cd /home/jenkins/docker/runtime/java/test-hook
sudo docker-compose up -d''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '${PROJECT_NAME}', remoteDirectorySDF: false, removePrefix: 'target', sourceFiles: 'target/${JAR_NAME}, target/docker-compose.yaml, target/.env, target/run.sh')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                } else {
                    sh 'echo ".env file not generated"'
                }
            }
        }
    }
}

2.6 汇总

pipeline {
    agent any
​
    environment {
        PROJECT_NAME = "test-hook"
        JAR_NAME = "test-hook-${JAR_VERSION}.jar"
    }
​
    stages {
        stage('Checkout') {
            steps {
                git branch: 'main', credentialsId: 'jenkins-github-host-private-key', poll: false, url: 'git@github.com:EmyiaEllen/test-hook.git'
            }
        }
        stage('Compile-Package') {
            tools {
                jdk 'OpenJDK21'
                maven 'apache-maven-3.9.9'
            }
            steps {
                sh 'mvn -Dmaven.test.failure.ignore=true clean package'
            }
            post {
                success {
                    sh '''
                        cd target
                        cp ${JENKINS_HOME}/runtime/java/.script/docker-compose.yaml ./
                        cp ${JENKINS_HOME}/runtime/java/.script/run.sh ./
                        cp ${JENKINS_HOME}/runtime/java/.script/env-setup.sh ./
                        
                        bash ./env-setup.sh \
                        --run-dir="./" --con-name=test-hook \
                        --exec-script="java -jar ${JAR_NAME}" \
                        --host-port=8192 --con-port=8080 --java-version=21
                    '''
                }
            }
        }
        stage('Remote-Deploy') {
            steps {
                script {
                    if (fileExists("./target/.env")) {
                        sshPublisher(publishers: [sshPublisherDesc(configName: 'Host_Server', transfers: [sshTransfer(cleanRemote: false, excludes: '', execCommand: '''cd "/home/jenkins/docker/runtime/java/test-hook"
sudo docker-compose up -d''', execTimeout: 120000, flatten: false, makeEmptyDirs: false, noDefaultExcludes: false, patternSeparator: '[, ]+', remoteDirectory: '${PROJECT_NAME}', remoteDirectorySDF: false, removePrefix: 'target', sourceFiles: 'target/${JAR_NAME},target/.env,target/docker-compose.yaml,target/run.sh')], usePromotionTimestamp: false, useWorkspaceInPromotion: false, verbose: false)])
                    } else {
                        sh 'echo ".env file not generated"'
                    }
                }
            }
        }
    }
}

3. 构建

1. 手动构建

点击 test-hook-pipeline 条目,并点击 Build With Parameters 构建

image-20241101110626289.png

image-20241101110646883.png

image-20241101110713704.png

image-20241101110734584.png

最后进入远程主机,使用 sudo docker ps -asudo docker logs -f <CONTAINER_ID> 验证容器是否构建成功

image-20241101111102631.png

2. Github WebHook 构建

进入 Github 仓库,点击上方的 Setting 进入设置页面

image-20241101145303008.png

点击左侧的 Webhooks 进入 Webhooks 页面,并点击上方的 Add Webhook

image-20241101145350016.png

在 Payload URL 这里,输入主机IP和 Jenkins 容器占用的端口,路径为 /github-webhook/

image-20241101145621373.png

点击下方的 Add Webhook 确认添加,等待一会后刷新,如果出现对勾则说明验证成功

image-20241101145712816.png

在 Windows 上尝试修改并 push 到 Github,验证 Github Webhook 是否可用

image-20241101145920014.png

随后来到 Jenkins 页面,可以看到已经触发了一个新的任务

image-20241101150011982.png

等待构建完毕后,通过 HTTP 访问进行验证

image-20241101150055029.png

image-20241101151757013.png