随着软件开发节奏的加快,持续集成(CI)和持续部署(CD)已经成为确保软件质量和加速产品发布的不可或缺的部分。Jenkins作为一款广泛使用的开源自动化服务器,为开发者提供了一个强大的平台来实施这些实践。然而,在实际操作中,尤其是在需要跨越不同操作系统边界时——例如将基于Linux的构建服务器上的Java应用程序部署到Windows Server环境中——可能会遇到一系列挑战。本文聚焦于这一特定场景,详细介绍如何使用Jenkins来创建和管理Jobs,以便从GitLab仓库中拉取最新的Java项目源码,经过自动化的构建过程,最终将其部署到Windows Server上运行。
jenkins 部署 : [笔记] Jenkins 安装与配置全攻略:Ubuntu 从零开始搭建持续集成环境
一. 安装 jenkins 插件
1. GitLab 相关插件
用于与 GitLab 仓库集成,支持拉取代码、触发构建等。
-
GitLab
提供 GitLab 与 Jenkins 的集成,支持 GitLab Webhook、MR 触发构建等功能。 -
Git Plugin
用于从 Git 仓库(包括 GitLab)拉取代码。 -
GitLab API
提供与 GitLab API 的交互支持(可选,根据需求安装)。
2. 构建工具插件
用于构建 Java 项目。
-
Maven Integration Plugin
如果使用 Maven 构建 Java 项目,需要安装此插件。 -
Gradle Plugin
如果使用 Gradle 构建 Java 项目,需要安装此插件。
3. SSH 相关插件
用于通过 SSH 连接到 Windows Server 2019 并部署应用。
-
SSH Build Agents Plugin
允许 Jenkins 使用 SSH 密钥进行身份验证。 -
Publish Over SSH
支持通过 SSH 将构建产物传输到远程服务器(Windows Server 2019)。
二. 全局工具配置
1. Maven 配置
2. Maven 安装
三. 配置代码仓库
我的代码仓库是使用的 极狐 gitlab
1. 获取 gitlab 的访问令牌(Access tokens)
期间 read_repository 必须勾选
- 仅需拉取代码:
如果 Jenkins 只需要从 GitLab 拉取代码(例如通过 Git-over-HTTP 或 Repository Files API),那么只需授予 read_repository 权限即可。- 需要与 GitLab API 交互:
如果 Jenkins 需要调用 GitLab API 来进行其他操作(比如获取项目详细信息、触发构建、管理合并请求等),你将需要授予 read_api 权限。这包括对所有群组和项目的读访问权、容器镜像库和软件包库的访问权等。- 高级集成:
在某些情况下,你可能还需要更多的权限,例如推送代码、创建标签或分支等。这些情况通常需要更高的权限级别,如 write_repository 或自定义权限设置。
2. 配置 jenkins 凭证
- 进入凭证管理
- 配置全局凭证
- 添加凭证
- 选择类型 Username with password
三. (win服务器使用) Windows Server 2019 启动 OpenSSH Server
1. 安装 OpenSSH Server
# 以管理员身份运行 PowerShell
# 安装 OpenSSH Server
Add-WindowsCapability -Online -Name OpenSSH.Server~~~~0.0.1.0
# 设置自动启动
Set-Service sshd -StartupType Automatic
Set-Service ssh-agent -StartupType Automatic
# 启动 SSH 服务
Start-Service sshd
Start-Service ssh-agent
# 确认服务状态
Get-Service sshd
2. 配置 SSH 服务
编辑 C:\ProgramData\ssh\sshd_config 文件,添加或修改以下内容:
# 编辑 C:\ProgramData\ssh\sshd_config 文件,添加或修改以下内容:
PasswordAuthentication yes
PermitRootLogin yes
Subsystem powershell c:/progra~1/powershell/7/pwsh.exe
KexAlgorithms diffie-hellman-group14-sha1,diffie-hellman-group-exchange-sha256,ecdh-sha2-nistp256
HostKeyAlgorithms ssh-rsa,rsa-sha2-512,rsa-sha2-256
3. 重启 SSH 服务
Restart-Service sshd
4. 配置防火墙
# 添加防火墙规则
New-NetFirewallRule -Name sshd -DisplayName 'OpenSSH Server (sshd)' -Enabled True -Direction Inbound -Protocol TCP -Action Allow -LocalPort 22
四. 配置 SSH Servers
1. 使用账号密码
注意 : windows 服务器的配置 Remote Directory 只能使用基础目录 , 不可修改
2. 使用秘钥
a. 生成SSH密钥对
- 打开终端或命令提示符。
- 运行以下命令生成SSH密钥对:
# 生成 RSA 密钥对
ssh-keygen -t rsa -b 4096 -C "[email protected]"
# 密钥默认保存在:
# - 私钥:C:\Users\jenkins\.ssh\id_rsa
# - 公钥:C:\Users\jenkins\.ssh\id_rsa.pub
ssh-keygen
: 用于生成、管理和转换认证密钥对的命令行工具,主要用于SSH协议。通常位于/usr/bin/ssh-keygen
或/usr/local/bin/ssh-keygen
。-t rsa
:-t
指定要生成的密钥类型。rsa
表示生成RSA类型的密钥。RSA是一种非对称加密算法,广泛用于SSH连接。其他常见的密钥类型包括dsa
(较少使用)、ecdsa
和ed25519
。-b 4096
:-b
指定密钥的位数。4096
表示生成的RSA密钥长度为4096位。默认情况下,ssh-keygen
生成的RSA密钥长度为2048位。增加密钥长度可以提高安全性,但也会稍微增加计算开销。4096位是一个常用的、安全的选择。-C "[email protected]"
:-C
添加一个注释(comment)到生成的公钥中。"[email protected]"
是你提供的注释内容,通常是你的电子邮件地址。
-
按照提示操作,可以接受默认路径和文件名(通常是
~/.ssh/id_rsa
和~/.ssh/id_rsa.pub
)。
-
Enter passphrase (empty for no passphrase):
系统会提示你输入一个密码短语(passphrase)。如果你设置了密码短语,每次使用私钥时都需要输入它。这增加了安全性,但也增加了使用的复杂性。(如果你不希望设置密码短语,直接按回车键即可。)
b. 将公钥复制到 Windows Server
- 将生成的公钥文件(通常是
~/.ssh/id_rsa.pub
)的内容复制到剪贴板。 - 将公钥内容添加到
authorized_keys
文件
# 创建 authorized_keys 文件并添加公钥内容
# 将 Jenkins 服务器上 id_rsa.pub 的内容复制到这个文件
Add-Content -Path C:\Users\Administrator\.ssh\authorized_keys -Value "ssh-rsa AAAA..."
-Value
是你的~/.ssh/id_rsa.pub
的内容
c. 编辑 C:\ProgramData\ssh\sshd_config
# 启用公钥认证
PubkeyAuthentication yes
# 指定授权密钥文件位置
AuthorizedKeysFile .ssh/authorized_keys
# 可选:禁用密码认证(更安全)
PasswordAuthentication no
d. 设置文件权限
# 设置 .ssh 目录的权限
icacls "C:\Users\Administrator\.ssh" /inheritance:r
icacls "C:\Users\Administrator\.ssh" /grant "SYSTEM:(F)"
icacls "C:\Users\Administrator\.ssh" /grant "ADMINISTRATORS:(F)"
icacls "C:\Users\Administrator\.ssh" /grant "Administrator:(F)"
# 设置 authorized_keys 文件的权限
icacls "C:\Users\Administrator\.ssh\authorized_keys" /inheritance:r
icacls "C:\Users\Administrator\.ssh\authorized_keys" /grant "SYSTEM:(F)"
icacls "C:\Users\Administrator\.ssh\authorized_keys" /grant "ADMINISTRATORS:(F)"
icacls "C:\Users\Administrator\.ssh\authorized_keys" /grant "Administrator:(F)"
e. 重启 SSH 服务
# 重启 SSH 服务
Restart-Service sshd
f. 在 Jenkins 中配置 SSH Publisher:
path to key 与 key 只需要配置一个就可以
3. 测试连接
四. 编写 jobs
1. 配置源码管理
2. 配置 构建完成后 上传产物与运行脚本
注意:
- 可以选择 构建环境中的
Send files or execute commands over SSH after the build runs
也可以选择 构建后操作中的Send build artifacts over SSH
。Remote directory
: 我使用的是环境参数,在 Jenkins 中使用环境参数使用 必须是${FOLDER_NAME}
的格式。- windows 服务器 运行脚本的话,只能运行服务器上的脚本。
> Exec command :
> ${DEPLOY_BAT_PATH} "${FOLDER_NAME}" "${DEPLOY_LOG_PATH}" "${BASE_SOURCE_PATH}" "${BASE_TARGET_PATH}" "${JAR_NAME}"
3. 配置环境变量
在 Jenkins 中使用环境参数使用 必须是
${FOLDER_NAME}
的格式。
- name :
FOLDER_NAME
, defaultValue : test , description : 部署文件夹名称- name :
DEPLOY_LOG_PATH
, defaultValue : E:\deploy.log , description : 日志文件路径- name :
BASE_SOURCE_PATH
, defaultValue : C:\Users\Administrator , description : 源代码基础路径- name :
BASE_TARGET_PATH
, defaultValue : E:\project , description : 目标基础路径- name :
JAR_NAME
, defaultValue : central-server.jar , description : JAR文件名称- name :
DEPLOY_BAT_PATH
, defaultValue : E:\deploy.bat , description : 部署脚本路径
4. 编写脚本
部分内容声明 :
# 因为一些要求 , 要生成启动的脚本 , java -jar 换成 javaw -jar 也是一样的
echo Creating start script... >> %DEPLOY_LOG%
(
echo @echo off
echo cd /d "%TARGET_PATH%"
echo java -jar "%JAR_NAME%" ^> "%TARGET_PATH%\app.log" 2^>^&1
) > "%TARGET_PATH%\start.bat"
# 这里因为...用的盗版系统,环境变量被魔改过,start不会起作用.所以用 call 了,如果可以用start用start,call会等待。导致Jenkins超时
if exist "%TARGET_PATH%\%JAR_NAME%" (
:: 启动应用
cd /d "%TARGET_PATH%"
call start.bat
echo Application started successfully >> %DEPLOY_LOG%
) else (
echo WARNING: JAR file not found after deployment >> %DEPLOY_LOG%
)
完整脚本:
@echo off
setlocal EnableDelayedExpansion
:: 检查参数
if "%~1"=="" (
echo Error: folder_name parameter is missing
goto :usage
)
if "%~2"=="" (
echo Error: deploy_log_path parameter is missing
goto :usage
)
if "%~3"=="" (
echo Error: base_source_path parameter is missing
goto :usage
)
if "%~4"=="" (
echo Error: base_target_path parameter is missing
goto :usage
)
if "%~5"=="" (
echo Error: jar_name parameter is missing
goto :usage
)
:: 检查并创建日志文件目录
for %%I in ("%~2") do set "LOG_DIR=%%~dpI"
if not exist "!LOG_DIR!" (
echo Creating log directory: !LOG_DIR!
md "!LOG_DIR!" 2>nul
if errorlevel 1 (
echo Error: Failed to create log directory
goto :usage
)
)
:: 设置日志文件和基础变量
set "FOLDER_NAME=%~1"
set "DEPLOY_LOG=%~2"
set "SOURCE_PATH=%~3\%FOLDER_NAME%"
set "TARGET_PATH=%~4\%FOLDER_NAME%"
set "JAR_NAME=%~5"
:: 记录部署开始
echo %date% %time% - Starting Jenkins deployment > %DEPLOY_LOG%
echo Configuration: >> %DEPLOY_LOG%
echo SOURCE_PATH=%SOURCE_PATH% >> %DEPLOY_LOG%
echo TARGET_PATH=%TARGET_PATH% >> %DEPLOY_LOG%
echo JAR_NAME=%JAR_NAME% >> %DEPLOY_LOG%
:: 停止现有进程
echo %date% %time% - Attempting to stop existing process... >> %DEPLOY_LOG%
taskkill /F /IM java.exe /T >> %DEPLOY_LOG% 2>&1 || echo No Java processes found >> %DEPLOY_LOG%
echo %date% %time% - Process stop attempt completed >> %DEPLOY_LOG%
:: 等待进程完全停止
echo %date% %time% - Waiting for processes to stop... >> %DEPLOY_LOG%
ping -n 11 127.0.0.1 > nul
echo %date% %time% - Wait completed >> %DEPLOY_LOG%
:: 清理目标目录 - 即使失败也继续
echo Cleaning target directory... >> %DEPLOY_LOG%
if exist "%TARGET_PATH%" (
rd /s /q "%TARGET_PATH%" >> %DEPLOY_LOG% 2>&1
)
:: 创建必要目录
md "%~4" 2>nul
md "%TARGET_PATH%" 2>nul
:: 复制文件
echo Moving files... >> %DEPLOY_LOG%
robocopy "%SOURCE_PATH%" "%TARGET_PATH%" /E /MOVE /R:1 /W:1 >> %DEPLOY_LOG% 2>&1
:: 创建启动脚本
echo Creating start script... >> %DEPLOY_LOG%
(
echo @echo off
echo cd /d "%TARGET_PATH%"
echo java -jar "%JAR_NAME%" ^> "%TARGET_PATH%\app.log" 2^>^&1
) > "%TARGET_PATH%\start.bat"
:: 验证部署
if exist "%TARGET_PATH%\%JAR_NAME%" (
:: 启动应用
cd /d "%TARGET_PATH%"
call start.bat
echo Application started successfully >> %DEPLOY_LOG%
) else (
echo WARNING: JAR file not found after deployment >> %DEPLOY_LOG%
)
echo Deployment completed at %date% %time% >> %DEPLOY_LOG%
endlocal
exit /b 0
:usage
echo Usage: deploy.bat [folder_name] [deploy_log_path] [base_source_path] [base_target_path] [jar_name]
echo Example: %%FOLDER_NAME%% %%DEPLOY_LOG_PATH%% %%BASE_SOURCE_PATH%% %%BASE_TARGET_PATH%% %%JAR_NAME%%
exit /b 1