1. 把 Jenkins 放在 Windows 上跑,先想清楚这三件事
我第一次在 Windows 上装 Jenkins 是给一个做企业内部管理系统的团队搭构建机,当时图省事,直接在一台闲置的 Windows Server 2016 上双击了安装包,结果后面连着踩了三个星期的坑。后来复盘才明白,Windows 下 Jenkins 的麻烦从来不在"装"这一步,而在"这台机器上本来还跑着别的东西"。所以正式动手前,有几个判断必须先做完,不然装完也是给自己埋雷。
1.1 先用 Windows 还是先用 Linux,取决于你团队手里的东西
选 Windows 做 Jenkins 宿主机的团队,通常有三个现实原因:代码库里有用批处理写的构建脚本、部署目标本身就是 IIS 或 Windows 服务、运维同事对 Linux 不熟。这三种情况我全都遇到过,也都是合理的理由——Jenkins 只是个 Java 程序,操作系统对它来说不是障碍,障碍在于"上下游环境能不能对齐"。
真正需要警惕的是另一种情况:团队正在往容器化走,构建产物要打成镜像推到仓库,编译环境需要大量命令行工具链。这时候宿主机选 Windows,等于每次构建都要先跨过一层翻译,Shell 脚本要改写成 bat,路径分隔符要来回转换,权限模型也对不上。我后来的做法是把这类构建拆出去,Windows 这台机器专门负责编译打包和往 Windows 目标机分发,容器相关的活交给专门的节点。
所以第一个判断是:你的构建产物最终落在哪,宿主机最好就跟目标环境同源。目标机是 Windows 服务,留在 Windows 上;目标机是 Linux 或容器,尽早把 Jenkins 迁走。这个判断花五分钟,能省后面几十个小时。
1.2 三种安装形态:MSI、war 包、解压即用
Windows 上装 Jenkins 一共有三条路,我全用过,适用场景差别很大。
| 安装形态 | 适用场景 | 优点 | 需要注意 |
|---|---|---|---|
| MSI 安装包 | 正式构建机、长期运行 | 自动注册 Windows 服务,开机自启,卸载干净 | 安装时选项必须一次选对,改起来要动 jenkins.xml |
| war 包 + java -jar | 临时验证、版本试用、频繁升级 | 换版本就是换个文件,顺手 | 关掉命令行窗口服务就停了,要自己想办法常驻 |
| 解压即用的便携目录 | 完全离线环境、多实例 | 目录即实例,复制即可迁移 | 需要自己写服务包装,或配合第三方工具 |
MSI 安装包最大的价值是把 Jenkins 变成了一个标准的 Windows 服务,日志、开机自启、崩溃重启都由操作系统的服务管理器兜着。我在给客户部署时基本都用这条路,因为接手的人不需要知道 Java 怎么启动,会用服务管理器就够了。
war 包方式我主要用来做版本对比。比如某次升级后发现插件行为变了,想确认是不是版本问题,直接下载两个版本的 war,用不同端口同时跑,改个端口号就能对比,几分钟的事。但要注意 war 方式默认把 JENKINS_HOME 放在当前用户目录下的.jenkins,跟之前 MSI 装的实例不共享,插件和任务都要重来,别以为能直接继承。
1.3 Java 版本这道门槛,别在第一步就翻车
Jenkins 对 JDK 版本的要求这些年一直在往上抬。早期版本跑在 JDK 8 上毫无问题,从 2.357 这条线开始要求 JDK 11,较新的 LTS 版本则明确要求 JDK 17 及以上。装上老 JDK 去启动新版本,报出来的是UnsupportedClassVersionError加一串版本号,看起来吓人,其实就是版本不匹配。
我建议的做法是:装之前先去看一眼准备下载的 Jenkins 版本的官方说明,确认它要求的 JDK 最低版本,然后直接装一个比要求高一级的 LTS JDK。比如要求 11,就装 17;要求 17,就装 21。这样至少能扛过两轮 Jenkins 升级,不用每次都换运行环境。
还有一个很容易忽略的点:Windows 上经常同时存在好几个 JDK。开发工具自带一个、Oracle 安装包装一个、绿色解压版又放一个。Jenkins 服务启动时用的是它自己认定的 JAVA_HOME,跟你命令行里java -version看到的不一定是同一个。我踩过一次,命令行里明明是 17,服务起来就报类版本错误,查了半天才发现服务的启动配置指向了一个老版本的 jre 目录。所以后面我在配置服务时会显式把 JDK 路径写进 jenkins.xml,不依赖系统环境变量。
2. 安装实况:从 JDK 落地到 Jenkins 注册成系统服务
2.1 JDK 安装与环境变量,重点在"服务读得到"
JDK 安装本身没什么可说,一路下一步即可,选择安装目录时我习惯放在C:\Java\jdk-17这种没有空格的短路径下。空格路径在 Windows 上不一定会出问题,但一旦出问题就极难排查,因为报错信息往往只说"找不到文件",不告诉你是哪一段被拆开了。
环境变量配置这一步,多数教程只让你配 JAVA_HOME 和 PATH,我建议再加上一条:把%JAVA_HOME%\bin放在 PATH 的最前面,而不是追加到末尾。原因是机器上如果已经装了其他 Java,追加到末尾等于让老的优先命中,命令行走的是旧版本。你可以用一个很土但有效的办法验证:开一个新的命令行窗口,敲where java,看返回列表里的第一条是不是你想用的那个。
验证完命令行还不够。Windows 服务的环境变量和交互式登录用户的环境变量是两套东西,尤其是服务以 LocalSystem 身份运行时,它读的是系统级环境变量,跟你当前登录用户配的用户级变量没关系。所以配完一定要顺手执行一下setx /M JAVA_HOME "C:\Java\jdk-17",把变量写到系统级,重启命令行再验证一次。这一步做扎实,能省掉后面至少一半的"服务起不来"问题。
2.2 MSI 安装向导里那几个必须一次选对的选项
Jenkins 的 MSI 安装向导只有几屏,但每一屏都在决定这个实例后面好不好维护。
第一屏是安装目录。默认在C:\Program Files\Jenkins。这个目录放的是程序文件(war 包、jenkins.xml、服务包装器),跟你的任务数据不是一个地方,别搞混。如果 C 盘空间紧张,可以改到数据盘,但路径尽量别带中文和空格。
第二屏是服务登录账号。默认选项是 LocalSystem,也就是本地系统账户。这个账号权限极大,能操作本机绝大多数资源,但它在访问网络共享时会以"机器账号"的身份出现,在域环境里很容易被拒绝。如果你的构建需要把产物推送到别的机器的共享目录,就得在这里换成一个有网络访问权限的域账号或本地管理员账号。这个选项装完之后改起来麻烦,因为要重新授权数据目录的权限,所以建议在这里就选对。
第三屏是端口,默认 8080。这是 Windows 上冲突率最高的端口之一,IIS、Tomcat、各种开发工具都爱占。装之前先跑一次netstat -ano | findstr :8080,有输出就说明被占了,换个端口。我用 8081 或 8090 比较多,图个记着方便。
第四屏是 JENKINS_HOME,默认在C:\ProgramData\Jenkins\.jenkins。这个目录才是真正要备份的东西——所有任务配置、插件、构建历史、凭据都在里面,体积会随着构建次数不断膨胀。我一般会把它直接指到一个独立的盘符路径下,比如D:\jenkins_home,好处是备份的时候整目录打包就行,不用去 ProgramData 这个隐藏目录里翻。
2.3 jenkins.xml:端口、内存和启动参数的最终落点
MSI 装完之后,安装目录下会有一个jenkins.xml,这才是服务真正的启动配置。向导里选的端口、JENKINS_HOME 都写在这里,后续想改也基本都在这动。
<service> <id>jenkins</id> <name>Jenkins</name> <executable>C:\Java\jdk-17\bin\java.exe</executable> <arguments>-Xrs -Xmx2048m -Dfile.encoding=UTF-8 -Dhudson.lifecycle=hudson.lifecycle.WindowsServiceLifecycle -jar "%BASE%\jenkins.war" --httpPort=8090 --webroot=%BASE%\war</arguments> <env name="JENKINS_HOME" value="D:\jenkins_home"/> <logmode>rotate</logmode> </service>这段配置里有几个我改动最频繁的地方。
-Xmx2048m是堆内存上限,默认值通常只有 256MB 或 512MB,跑几个中等规模的任务就会开始频繁 GC,界面卡顿。按机器内存给,8GB 的机器给 2GB 比较稳,16GB 可以给 4GB。给太多也没意义,Jenkins 自己消耗不了那么多。
-Dfile.encoding=UTF-8是我必加的一条。Windows 中文环境下,Jenkins 读构建日志、执行批处理都容易出现中文乱码,加上这条能让大部分场景正常。配合后面控制台里的编码设置,基本能根治。
<logmode>rotate</logmode>让日志自动轮转,避免日志文件把磁盘写满。有些老版本默认是追加模式,跑几个月能写出几个 G 的日志。
改完 jenkins.xml 必须重启服务才生效,命令是net stop jenkins再net start jenkins。服务名不区分大小写,但如果你装的时候改过服务名,按实际名字来。重启后在浏览器里访问http://localhost:8090,能出界面就说明配置生效了。
2.4 war 包方式的正确打开姿势
前面说过 war 包适合临时验证,但它的一个隐藏优势是方便做"隔离测试"。比如怀疑某个插件把构建搞坏了,可以用 war 起一个新实例,把插件装到干净环境里对比,不会污染正式实例。
启动命令很直接:
java -jar jenkins.war --httpPort=8090 --prefix=/ci--prefix这个参数值得一提。如果你的机器上前面挂了反向代理,想让 Jenkins 跑在某个子路径下,用这个参数比配代理规则省事得多,Jenkins 自己会处理链接前缀。
war 方式的坑在于它不是服务。命令行窗口一关,进程就没了。有些教程教你用start /b后台启动,这个做法在重启机器后不会自动恢复。真想让它常驻,还是要老老实实注册成服务,可以用 NSSM 这类服务包装工具把java -jar包一层。如果只是自己临时用,那就接受它不常驻,别在这上面浪费时间。
3. 第一次打开网页:解锁、插件源与中文环境
3.1 解锁密码在哪,以及初始化时该勾哪个
第一次访问 Jenkins 会要求输入初始管理员密码,文件在JENKINS_HOME/secrets/initialAdminPassword。如果你用的是默认路径,就在C:\ProgramData\Jenkins\.jenkins\secrets\initialAdminPassword,用记事本打开,复制那串字符粘进去就行。这个文件在完成初始化后会失效,不用管它。
接下来会让你选插件安装策略:安装推荐的插件,或者自己挑。我的建议是在联网环境下选推荐插件,在离线环境下直接跳过。推荐插件包里包含了 Git、Pipeline、凭据管理这些几乎必用的东西,一次装完省事。但如果网络不好,这个环节会卡很久,进度条走到一半失败的话会留下半装的插件,反而更麻烦。离线环境下直接选"不安装任何插件",进去之后手工上传,反而干净。
初始化最后一步会创建管理员账号。这里有个忠告:不要用 admin 这种通用名字,也不要把密码设成和机器登录密码一样的东西。这台机器上将来会存所有仓库的访问凭据,用弱账号等于把所有仓库的门钥匙都挂在门口。
3.2 插件下载慢甚至下不动,两条路可走
Jenkins 默认的插件更新站点在国内访问体验很不稳定。界面上的表现是"插件管理"页面转圈转很久,或者直接报连接超时。
第一条路是换更新站点。在"系统管理 → 插件管理 → Advanced(高级)"里找到 Update Site,把地址换成国内高校或云厂商维护的镜像。清华的镜像地址是https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json。换完之后要先点 Check now 让它重新拉取元数据,再回到可选插件列表,速度通常会有明显改善。
需要注意的是,这类镜像的同步是有滞后的,有的镜像甚至已经长期停更。如果你发现换了地址之后列表里找不到某个新插件,很可能就是镜像没同步,换回官方地址试一次对比一下就知道。另外镜像地址用 https 有时会报证书或重定向问题,可以试着改成 http 看是否正常,能跑通优先。
第二条路是离线安装,在完全断网的内网环境里这是唯一选择。做法有两种,效果差别很大。
第一种是单插件上传:在联网机器上从官方更新站点下载.hpi文件,然后在"插件管理 → 高级 → 上传插件"里选文件上传。这个办法的问题是依赖。一个插件往往依赖另外几个插件,你上传主插件时它会提示缺依赖,得一个一个补齐,装十几个插件可能要来回几十趟。
第二种我强烈推荐:在联网机器上装一个同版本 Jenkins,把所需插件全部装好,然后把整个JENKINS_HOME/plugins目录拷到离线机器的对应位置,重启服务。目录里除了.jpi文件还有.jpi.pinned之类的标记文件,一起拷过去,Jenkins 启动时会自动识别并加载。我做过很多次内网部署,这个方法一次成功率远高于手工补依赖。前提是两台机器的 Jenkins 主版本要基本一致,跨大版本直接拷插件目录有概率出兼容问题。
3.3 中文界面与常用插件清单
Jenkins 本身支持多语言,但需要装本地化插件。装Locale插件之后,去"系统管理 → 系统配置",找到 Locale 一栏,填zh_CN,并且勾上"忽略浏览器语言偏好",这样不管谁用什么浏览器访问都是中文界面。不勾那个选项的话,英文浏览器的同事看到的还是英文,团队里会有人以为配置没生效。
下面这份清单是我每台新机器都会装的基础组合,按优先级排:
- Git plugin 和 Git client plugin:接入代码仓库的基础,几乎是所有任务的前提。
- Pipeline:写 Jenkinsfile 用,新建任务时才会出现"流水线"这种类型。
- Credentials Binding:让凭据以变量的形式注入构建过程,避免明文写在脚本里。
- Role-based Authorization Strategy:做基于角色的权限控制,比矩阵授权好用得多,适合多团队共用一台机器的场景。
- Workspace Cleanup:构建前后清理工作区,磁盘不会被历史文件撑爆。
- Timestamper:给构建日志每一行加时间戳,排查慢构建时特别有用。
- Email Extension或DingTalk:构建结果通知,后者适合用企业办公工具做群通知的团队。
装插件这件事有个经验:一次别装太多,装完一次重启一次。插件之间的依赖关系在批量安装时出问题的概率明显更高,一次装五六个,装完看有没有报错,比一口气装三十个然后面对满屏红色错误信息要轻松得多。
4. 让 Jenkins 真正干活:凭据、Git 与第一个 Java Web 构建
4.1 凭据管理:先搞明白为什么要用它
很多人第一次用 Jenkins 拉代码,是直接把仓库地址写成http://用户名:密码@git服务器/repo.git这种形式。能跑通,但密码就明文躺在任务配置里,任何能看到这个任务配置的人都能读出来,而且日志里可能还会打印出来。这是绝对不能上生产环境的做法。
Jenkins 的凭据系统解决的就是这个问题。凭据存在"系统管理 → 凭据"里,任务配置时只引用凭据的 ID,密码本身加密存储在JENKINS_HOME下。常用的凭据类型有三种:
| 类型 | 用途 | 典型场景 |
|---|---|---|
| Username with password | 用户名加密码 | 拉 Git 代码、登录 Tomcat 管理后台 |
| SSH Username with private key | 用户名加私钥 | 通过 SSH 推送到 Linux 目标机 |
| Secret text | 单个密钥字符串 | 仓库平台的访问令牌、通知机器人的密钥 |
一个高频问题是令牌和密码该用哪个。大部分代码托管平台现在都支持个人访问令牌,用令牌代替密码有好处:权限可以单独收窄,比如只给读代码的权限;泄露之后单独吊销不影响登录;有些平台还强制要求必须用令牌。我在所有需要拉代码的凭据里都优先用令牌。
凭据 ID 这一栏建议起个有意义的名字,比如gitlab-readonly-token、tomcat-prod-manager,别用默认生成的随机串。任务多了以后,一串随机字符串根本认不出是干什么的,改起来很容易改错环境。生产环境和测试环境的凭据一定要分开建,命名上带prod、test后缀,这是最省事的防呆手段。
4.2 全局工具配置:Git 和 Maven 的路径绑定
Jenkins 本身不带 Git 和 Maven,它只是调用你机器上装好的版本。所以要去"系统管理 → 全局工具配置"里把路径告诉它。
Git 这一项,Windows 上装完 Git for Windows 之后,可执行文件通常在这个位置:C:\Program Files\Git\bin\git.exe。注意别填成cmd\git.exe,那个是给命令行窗口用的包装,Jenkins 调用时容易出现环境相关的怪问题。填完保存,去任务里试一次能拉到代码就算通了。
Maven 这一项有两种配置方式:让 Jenkins 自动下载安装,或者指向本地已装好的目录。我倾向后者,原因是公司的私服地址、镜像配置都已经写在本地conf/settings.xml里了,让 Jenkins 自己下一个新的,还得再配一遍。在"全局工具配置"里新增 Maven,取消自动安装,填上 MAVEN_HOME 路径,再在下面指定 settings.xml 的完整路径,这样构建时用的就是你已经调好的那套配置。
顺便说下 Maven 的国内仓库镜像,配一次能省大量下载时间。在settings.xml的 mirrors 节点里加上一段,把 central 指向国内镜像仓库地址,重新构建时会明显快。这个配置是在 Maven 层面做的,跟 Jenkins 没关系,但凡是做过 Java 构建的人都会做这一步。
4.3 自由风格项目:从拉代码到打出 war 包
新建任务选"构建一个自由风格的软件项目",这是最容易上手的一种。配置按顺序过一遍:
源码管理选 Git,填仓库地址,凭据选前面建好的那个,分支填*/main或*/master(按你仓库实际的主分支来)。这里有个小坑,很多教程写的是*/master,但新建的仓库默认分支叫main,填错了会报"找不到分支",报错信息还挺含糊,让人以为是权限问题。
构建触发器按需配。最简单的做法是配合代码平台的 Webhook,让代码一推就触发构建。如果用的是 GitLab,需要先装 GitLab 插件,然后在"系统管理 → 系统配置"里找到 GitLab 配置区,填上 GitLab 服务地址和一个 API 令牌,点测试连接确认能通。接着在任务的构建触发器里勾上对应选项,Jenkins 会给出一个回调地址,把它填到 GitLab 项目的 Webhook 设置里。这里最容易失败的地方是网络方向:GitLab 服务器得能主动访问到 Jenkins 的地址,很多内网是单向通的,配完一直不触发,八成是这个原因。
构建这一步,Java Web 项目用 Maven 就够:
mvn -B clean package -DskipTests -s D:\maven\conf\settings.xml-B是批处理模式,避免 Maven 输出一堆彩色控制字符,日志干净很多。-DskipTests要不要加看团队约定,我个人的习惯是构建阶段跳过测试,测试单独放在另一个任务里跑,这样任何一个环节失败都能一眼看出是哪一类问题,不用在长日志里翻。
构建完在"构建后操作"里归档产物,填target/*.war。这样每次构建的 war 包都会留档,出了问题想回滚到上一个版本,直接从归档里拿,不用重新构建。这个动作成本极低,但救急时价值极高。
4.4 用 Jenkinsfile 把构建逻辑写进代码库
自由风格任务的配置是存在 Jenkins 里的,机器崩了就没了,而且改配置只能点网页,没法走代码评审。任务一旦多起来,这种方式就很难维护。所以有一定规模之后,建议把构建逻辑写成 Jenkinsfile 放在代码库里。
pipeline { agent any tools { jdk 'jdk-17' maven 'maven-3.9' } environment { APP_NAME = 'demo-web' DEPLOY_DIR = 'D:\\tomcat\\webapps' } stages { stage('拉取代码') { steps { checkout scm } } stage('构建') { steps { bat 'mvn -B clean package -DskipTests' } } stage('归档产物') { steps { archiveArtifacts artifacts: 'target/*.war', allowEmptyArchive: false } } stage('部署') { steps { bat """ if exist "${DEPLOY_DIR}\\${APP_NAME}.war" del /q "${DEPLOY_DIR}\\${APP_NAME}.war" copy /y "target\\*.war" "${DEPLOY_DIR}\\" """ } } } post { failure { echo "构建失败,请查看日志" } } }这段脚本里有几点值得说。agent any表示任意可用节点都行,单机部署时就是这个。tools里引用的名字必须跟"全局工具配置"里定义的一致,写错了会在构建开始时直接报找不到工具。steps里用的是 bat 步骤而不是 sh,因为宿主机是 Windows。
Pipeline 最大的好处是构建逻辑进了版本库,改构建流程和改业务代码一样走合并流程,谁改的、为什么改,都有记录。而且换一台机器部署时,只要把任务指向代码库里的 Jenkinsfile,几分钟就能把整套流水线搬过去。
4.5 Windows 批处理构建脚本里那些反直觉的坑
第一坑:每一行 bat 命令都是独立的进程。这点跟 Shell 脚本的直觉完全相反。你在某一行里set BUILD_TAG=xxx,下一行echo %BUILD_TAG%是空的,因为上一行的进程已经退出了。解决办法是用call调用子脚本让环境变量传递过去,或者用&&把命令连成一条,或者干脆用 PowerShell 步骤替代。
第二坑:路径分隔符和引号。Windows 路径带反斜杠,而反斜杠在很多地方是转义字符,写 Pipeline 的 Groovy 字符串时要写双反斜杠,或者在字符串前加'''用三引号包起来。我一开始总是在这里出错,后来养成习惯,凡是路径里有反斜杠的都用三引号。
第三坑:文件被占用。Windows 上如果 Tomcat 正在运行,war 包文件处于占用状态,直接copy过去会报"另一个程序正在使用此文件"。所以部署脚本里必须先停服务、删旧文件、拷新文件、再起服务,这个顺序不能乱。而且停服务后要给几秒钟让文件句柄释放,写得急一点就会间歇性失败,表现是"有时候成功有时候失败",非常难查。
第四坑:中文乱码。除了前面说的 JVM 编码参数,批处理里还可以在脚本开头加一句chcp 65001把控制台代码页切成 UTF-8。但要注意脚本文件本身也要存成 UTF-8 无 BOM 格式,如果存成 ANSI,加了 chcp 反而会乱得更厉害。
5. 构建完然后呢:自动部署落地的几种路子
5.1 部署到 Tomcat:热部署插件还是直接拷文件
Java Web 项目部署到 Tomcat,看起来有两种选择,实际用下来我更推荐第二种。
第一种是用 Deploy to container 插件走 Tomcat 的 Manager 接口。需要在目标 Tomcat 的tomcat-users.xml里配置一个有 manager-script 角色的账号,然后在 Jenkins 任务的构建后操作里填 Tomcat 地址、凭据、war 包路径和访问路径。优点是 Jenkins 能得到部署结果反馈,成功失败一目了然。缺点是 Tomcat 的管理接口在不同版本间路径有变化,而且把一个带管理权限的账号密码存在 Jenkins 里,安全上要多留个心。
第二种是直接把 war 包拷到webapps目录,然后重启 Tomcat 服务。听起来很土,但在 Windows 上极其可靠,尤其是 Tomcat 已经注册成 Windows 服务的情况下,用net stop和net start两条命令就能控制,不依赖任何额外配置。
我后来的做法是把两种结合:部署走拷贝文件这条路,保证稳定;部署结果靠一个简单的 HTTP 探活来判断,构建脚本里拷完之后循环请求一次应用的首页,能返回 200 就算部署成功,否则让构建失败。这样既有确定的成功判据,又不用引入额外的管理接口。
需要提醒的是,直接在webapps下覆盖 war,Tomcat 的自动解压机制有延迟,有时候会出现"文件拷过去了但访问的还是老版本"的情况。稳妥做法是先删掉旧 war 和对应的解压目录,再拷新 war,最后重启服务。虽然多花十几秒,但避免了版本不一致这种最难排查的问题。
5.2 跨机器分发:共享目录、SSH 与远程执行
如果是多台目标机,就得考虑跨机器分发。Windows 上常见的三种方式:
- 共享目录:把目标机的 webapps 目录做成共享,Jenkins 用
copy命令写过去。配置最简单,但有个大坑:Jenkins 服务如果以 LocalSystem 身份运行,访问网络共享时用的是机器账号,域环境里基本会被拒绝。解决办法是把服务账号改成一个有共享访问权限的域账号,并且在目标机上把共享权限和 NTFS 权限都放开。只放开共享权限是不够的,这是我踩过好几次才记住的点。 - SCP 或 SFTP:Windows 10 和 Server 2019 之后自带 OpenSSH 客户端,直接用
scp命令推文件,配好免密登录即可。老版本可以装一个 PuTTY 自带的 pscp,用法类似。这条路的通用性最好,目标机是 Linux 也一样能用。 - PowerShell Remoting:在目标机上启用远程管理,Jenkins 用
Invoke-Command远程执行部署脚本。适合部署动作比较复杂、需要在目标机上跑一整段逻辑的场景。配置门槛比前两种高,需要处理信任关系,但一旦通了之后非常灵活。
我自己的排序是:内网且只有一两台目标机,用共享目录最省事;目标机多或者混了 Linux,直接用 SCP;部署逻辑复杂到要在目标机上做备份、改配置、切软链,才值得上 PowerShell Remoting。
5.3 容器化部署与镜像拉取失败的处理
有些团队在 Windows 上用 Docker Desktop 跑容器化部署,构建完把镜像推到仓库,再在目标机上拉取。这条路上最常见的报错就是拉取镜像超时,日志里会出现一长串带仓库地址的报错信息,本质是默认的公共仓库地址在国内访问不稳定。
处理办法有两种,按可行性排序。第一种是配置镜像加速地址,在 Docker Desktop 的设置里找到镜像仓库配置项,填上可用的加速地址,重启 Docker 使配置生效。第二种更适合企业环境:搭一个内网私有镜像仓库,所有构建产物推到自己仓库里,目标机从内网拉取。第二种前期投入大一点,但稳定性、速度、权限控制全都更好,构建量大之后必然要走到这一步。
还有一个容易忽略的点是磁盘。Windows 上 Docker 的镜像和容器默认都存在系统盘的用户目录下,构建几十次之后磁盘就红了。这个问题在构建成功时不报错,等磁盘满了才开始各种诡异失败。所以我在搭建初期就会把这个存储目录迁到数据盘,属于一次配置长期受益的操作。
6. 环境变量、权限与报错排查:把踩过的坑摊开讲
6.1 Jenkins 可用环境变量清单与读取方式
Jenkins 在每次构建时会自动注入一批环境变量,用好了能省很多传参的功夫。常用的有这些:
| 变量名 | 含义 | 常用场景 |
|---|---|---|
| BUILD_NUMBER | 当前构建序号 | 产物命名加版本号后缀 |
| BUILD_ID | 构建的时间戳标识 | 生成唯一目录名 |
| JOB_NAME | 任务名称 | 日志和通知里标明来源 |
| WORKSPACE | 当前任务的工作目录绝对路径 | 脚本里定位文件 |
| JENKINS_URL | Jenkins 服务地址 | 拼构建详情页链接发通知 |
| NODE_NAME | 执行构建的节点名 | 多节点环境区分来源 |
| GIT_COMMIT | 本次构建对应的提交哈希 | 产物标记、回滚定位 |
| GIT_BRANCH | 本次构建的分支 | 区分测试环境和生产环境 |
读取方式在不同步骤类型里写法不一样,这点特别容易搞混:
- 在批处理步骤里用
%BUILD_NUMBER% - 在 PowerShell 步骤里用
$env:BUILD_NUMBER - 在 Pipeline 的 Groovy 代码里用
${env.BUILD_NUMBER}
我自己踩得最惨的一次是在 Pipeline 的字符串里写了%BUILD_NUMBER%,结果是原样输出,找了半天才反应过来。记住一条规则:bat 步骤的内容按 Windows 批处理语法解析,Groovy 层面的变量插值要用 env 前缀,两者可以嵌套,但顺序要理清。
还有一个实用技巧:在任务里加一个临时步骤执行set(批处理)或Get-ChildItem env:(PowerShell),把所有可用变量打印出来看一眼。Jenkins 版本和插件不同,注入的变量会有差异,与其查文档不如直接打出来。
6.2 权限体系与把自己锁在门外的救急办法
单机自用的话,默认的登录用户可以做任何事,不用配置权限。但只要团队里有其他人要用,就必须考虑权限,否则任何人都能改别人的任务、看别人的凭据。
我推荐用 Role-based Authorization Strategy 插件,思路是先定义角色(管理员、开发者、只读),给角色分配权限,再把用户或用户组映射到角色上。相比矩阵授权那种给每个用户单独勾权限的方式,角色方式在人员变动时维护成本低得多,走一个人只要把他从角色里摘出来。
这里有个必须掌握的救急技巧:矩阵授权配置错了会把自己也锁在外面,表现是登录后所有菜单都看不到,甚至进不去设置页。补救方法是停掉 Jenkins 服务,打开JENKINS_HOME/config.xml,找到 authorizationStrategy 那段配置,和上面的useSecurity节点,把安全配置改成关闭状态,重启服务后用匿名管理员身份进去重新配。做完再把安全打开。这个操作我做过两次,每次都在十分钟内解决,比到处找教程快得多。所以动权限配置之前,一定先备份 config.xml。
6.3 高频报错对照表与定位思路
下面这份表是我这些年攒下来的,基本覆盖了 Windows 环境下八成的启动和构建问题。
| 现象 | 大概率原因 | 定位方法 |
|---|---|---|
| 启动即退出,服务显示已停止 | JVM 内存参数过大或 JDK 路径失效 | 看安装目录下 jenkins.err.log 的开头几行 |
| 报类版本不支持的异常 | JDK 版本低于当前 Jenkins 要求 | 确认服务实际使用的 java.exe 版本 |
| 浏览器打不开页面 | 端口被占用或防火墙未放行 | netstat -ano | findstr :端口 |
| 插件页面一直转圈 | 更新站点访问不通 | 换镜像地址或改走离线安装 |
| 拉代码提示找不到分支 | 分支名与实际不符 | 在仓库页面确认真实分支名 |
| 构建日志中文乱码 | 编码未统一 | 加 JVM 编码参数并在脚本里切代码页 |
| 拷文件提示被占用 | 目标程序正在运行 | 部署脚本改为先停服务再替换 |
| 访问共享目录被拒绝 | 服务账号权限不足 | 换有网络权限的账号或调整共享权限 |
| 拉取镜像超时 | 公共仓库访问不稳定 | 配加速地址或改用内网仓库 |
定位这类问题的通用思路是先看日志,再看配置,最后才怀疑代码。Jenkins 的日志分散在几个地方:安装目录下的jenkins.err.log和jenkins.out.log记录服务启动阶段的信息,JENKINS_HOME/logs下记录运行期的任务日志,具体某个构建的日志则在任务的构建历史页面里。启动问题看前者,构建问题看后者,这个分类能帮你少走很多弯路。
还有个小经验:改配置要一次只改一个地方。我见过太多人在一个问题上报错之后,同时改端口、改 JDK、改内存、换插件版本,然后问题消失了也不知道是哪个起的作用,下次遇到还是一头雾水。老老实实一次改一项,改完重启验证,虽然慢一点,但每次都能定位到真正的原因。
6.4 备份、升级与日常维护
Jenkins 的备份非常朴素:关掉服务,把JENKINS_HOME整个目录打包,完事。这个目录里包含任务配置、插件、凭据、构建历史,恢复的时候解压回原位再启动服务,环境就回来了。唯一的注意点是必须先停服务再打包,运行中拷贝出来的配置有可能只拷了一半,恢复后表现为任务配置损坏。
备份频率看使用强度,我的习惯是每周一次全量,另外在每次做升级、改权限、加插件之前单独备一份。文件不用留太多,保留最近四次全量就够,磁盘占用其实不大,比起重建环境的成本完全可以接受。
升级这件事,MSI 装的话直接下新版本的安装包覆盖安装,它会保留原有的 JENKINS_HOME 和配置。但升级前务必确认两件事:新版本要求的 JDK 版本你满足了没有,装的关键插件有没有兼容性问题。插件兼容性可以看插件管理页里的提示,升级完先在一个非关键任务上跑一次,确认没问题再放开。
日常维护里最容易被忽视的是磁盘。构建历史和归档产物会持续增长,几个月不动就可能把盘占满。我的做法是给每个任务设置构建历史保留策略,比如只保留最近 20 次构建和最近 30 天的记录,归档产物同理。这个配置在任务配置页的最下面,花十秒配一次,能一直省心。另外给JENKINS_HOME所在的盘留出足够空间,我个人的经验是至少留 30% 空闲,不要等到报警了才处理。
最后再分享一个我自己一直在用的小做法:在每台 Jenkins 机器的JENKINS_HOME根目录下放一个纯文本的README-运维.md,里面写清楚这台机器装的是哪个 JDK 版本、Jenkins 版本、端口、服务账号、备份路径、几个关键凭据的用途,以及历次升级改过什么。文件很土,但半年后接手的人(很可能就是你自己)打开一看就能上手,比翻聊天记录快得不是一点半点。搭建这种基础设施,真正花时间的从来不是最初那一次安装,而是后面每一次"改动之前先搞明白现状"。