
如果你和我一样第一次在 Jenkins 里填 GitLab 仓库地址的时候卡了半个小时那你应该能明白拉代码这件事看起来只是填一个 URL 和一个账号真正跑起来之后问题永远出在你没想到的地方。这篇文章不打算讲太多的 CI/CD 大概念就围绕一个非常具体的场景——Jenkins 从 GitLab 拉取代码——把这条链路上最容易出错、也最容易被教程几句话带过的环节拆开讲清楚。内容覆盖环境准备、凭据配置、Git 拉取失败的排查、任务创建以及从代码拉取到自动部署的完整过程。适合刚搭好 Jenkins 正准备接第一个构建任务的人也适合已经在用、但经常被 “login failed” 和 “无法拉取代码” 折腾的运维和开发同学。提示全文以 Jenkins 2.x 版本为基础文中涉及的插件名称和配置路径在 Jenkins 2.3 及后续 LTS 版本中基本一致。1. 动手之前先想明白 Jenkins 和 GitLab 之间靠什么建立信任很多人配置失败不是因为操作不对而是没搞懂底层逻辑。拉代码不是简单的“填个地址点保存”Jenkins 要访问 GitLab本质上要解决两件事网络通不通以及GitLab 认不认 Jenkins 这个客户端。前者靠网络配置后者靠凭据。1.1 一个被很多人忽略的前提Jenkins 是“另一台电脑”先建立一个关键认知你在浏览器里打开 Jenkins 页面配置东西但真正执行构建的是 Jenkins 节点这个节点可能是服务器上一个 Java 进程也可能是 Docker 容器里的一个 agent它和你本地的电脑完全是两回事。这意味着三件事你的电脑能访问 GitLab不代表 Jenkins 服务器能访问。你在本地生成的 SSH Key不等于 Jenkins 服务器上有对应的私钥。你本机配置的 Git 用户信息和 Jenkins 节点没有任何关系。所以每次搭建 Jenkins 拉代码之前第一件事永远是去 Jenkins 所在的机器上自己先试一遍。这个习惯能省掉后面百分之八十的排错时间。1.2 选对凭据方式SSH 和 HTTP 的本质区别从 GitLab 拉代码协议层面只有两类选择SSH和HTTP/HTTPS。很多教程会告诉你“两个都行”但实际项目中这两个的坑完全不同。HTTP 方式在 Jenkins 里的配置是仓库地址填http://gitlab.example.com/group/project.git凭据类型选 “Username with password”里面填 GitLab 用户名和密码或 Access Token。这种方式的优点是配置直观缺点是密码或 Token 容易过期而且每次拉代码都要做一次认证稍微有点慢。SSH 方式则是仓库地址填gitgitlab.example.com:group/project.git凭据类型选 “SSH Username with private key”里面填私钥。GitLab 通过公钥识别 Jenkins 的身份不需要每次输入密码。这种方式更好用而且在服务器上配置一次后Git 命令行工具也能复用。我的建议是只要是服务器对服务器的场景一律优先 SSH。原因不只是安全更重要的是 SSH Key 不会像 Token 一样定期失效省去的维护成本非常可观。1.3 GitLab 侧的用户权限模型GitLab 对仓库的访问权限分几个档位Guest、Reporter、Developer、Maintainer、Owner。拉取代码至少需要Reporter权限如果要做 Push 或合入分支操作才需要更高的 Developer 及以上。很多人在配置的时候图省事随手建一个普通用户然后发现构建报 403 或 401。这不一定是凭据格式写错了很可能就是权限不够。如果你只是让 Jenkins 拉取代码构建我建议直接在目标项目里创建一个Deploy Key部署密钥只授权这一个项目权限只读连额外账号都不用建。这种方式后面会详细讲。还有一个容易忽略的点GitLab 在不同版本里对用户名和仓库地址的解析方式有变化。比如老的 GitLab 支持用用户 ID 作为路径前缀新版本则要求使用用户名或群组名。遇到 “Project not found” 时先别急着怀疑网络检查一下仓库路径是不是写成了机器 ID 或数字 ID。2. 环境准备把 Jenkins、GitLab 和网络这三件事一次理顺在配置任何凭据之前先把环境梳理清楚。我见过太多人凭据配了半天最后发现是 Jenkins 服务器少了git命令或者 DNS 解析不到 GitLab 域名。2.1 组件安装与版本选择Jenkins 的安装方式有几种直接下载 war 包用java -jar启动、用系统包管理器安装、用 Docker 启动。如果是生产环境我比较推荐用 Docker 或者系统服务方式方便跟随 LTS 版本升级。这里有个版本敏感点Jenkins 2.3 这个版本号本身比较老但配置思路和现在的 LTS 版本没有本质区别。如果你是用 Docker 装建议不要用latest标签直接固定jenkins/jenkins:lts-jdk11或对应 JDK 版本的镜像避免某天升级后插件不兼容。GitLab 这边的安装不在本文重点但如果你用 Docker 装 GitLab注意端口映射和external_url配置。external_url直接决定 SSH 的 clone 地址格式如果配的是http://192.168.1.10那么 Jenkins 里 SSH 方式拉代码的地址就会带着 IP 而不是域名。2.2 插件安装Git Plugin、GitLab Plugin 和国内镜像源Jenkins 拉取 Git 代码依赖两个基础插件Git Plugin提供 Git 操作核心能力源码管理里能选 “Git” 全靠它。GitLab Plugin提供 GitLab 连接、Merge Request 触发、Webhook 通知等能力。如果你只打算实现“从 GitLab 拉代码到本地”那么 Git Plugin 就够了。如果你想让 GitLab 的 Webhook 自动触发 Jenkins 构建那 GitLab Plugin 也必须装。这里要特别提一句插件安装源的问题。很多人在安装插件时卡死在 “该 Jenkins 实例似乎已离线”其实不是真离线而是默认插件源updates.jenkins.io访问不稳定。这时候去清华或华为的开源镜像站把Update Site地址改成国内镜像然后Manage Jenkins - Manage Plugins - Advanced里点一下 “Check now”再回到插件列表就能正常下载了。注意换源只解决插件下载问题不解决 GitLab 访问问题。这两个问题千万别混在一起排查。举一个我实际遇到的例子某次搭建 Jenkins插件列表一直刷不出来控制台提示连接 updates.jenkins.io 超时。我把站点地址换成https://mirrors.tuna.tsinghua.edu.cn/jenkins/updates/update-center.json以后插件瞬间就装上了。这类问题在服务器上非常常见属于基础设施问题不是 Jenkins 的 Bug。2.3 用 git ls-remote 验证“能不能拉到代码”配置好插件和环境变量后别急着去 Jenkins 页面创建任务。先在 Jenkins 服务器上用命令行验证一遍这一步能隔离掉大部分网络问题。假设你已经在 GitLab 上创建了一个 SSH Key 对并且把公钥放到了 GitLab 用户或 Deploy Key 里那么在服务器上执行git ls-remote gitgitlab.example.com:group/project.git如果输出了一串 refs说明 SSH 协议和网络都没问题。如果提示Permission denied (publickey)说明公钥没配好或者私钥路径不对。这样可以快速定位问题。HTTP 方式的验证更简单git ls-remote http://gitlab.example.com/group/project.git按提示输入用户名和密码或 Access Token能输出 refs 就说明网络通。关键是这一步做完之后要记住用的是哪个账号、哪个 Token后面在 Jenkins 里填的就是这套。3. 凭据配置SSH Key 与 Access Token 的正确用法以及 login failed 的完整排查凭据是 Jenkins 拉取 GitLab 代码最核心也最容易出问题的一环。基本上所有“拉不到代码”的疑难杂症最后都能归结到凭据上。3.1 用 Deploy Key 完成只读拉取先推荐一种相对来说最干净的方式Deploy Key。在 GitLab 项目页面进入Settings - Repository - Deploy keys把 Jenkins 服务器上的公钥内容粘贴进去。这种方式的好处是不需要额外创建 GitLab 账号。只对这一个项目生效权限范围最小。可以设置 “Write access allowed” 来决定是否允许推送日常构建建议不要开启写权限。Jenkins 服务器上生成 Key 的命令是ssh-keygen -t ed25519 -C jenkinsbuild-server -f ~/.ssh/jenkins_gitlab_ed25519然后把生成的jenkins_gitlab_ed25519.pub内容贴到 GitLab 的 Deploy Keys 里。Jenkins 侧配置时凭据类型选 “SSH Username with private key”Username 填gitPrivate key 直接粘贴私钥文件内容或者选择 “From the Jenkins master ~/.ssh”。有一点需要注意如果你用的是 Docker 方式启动 JenkinsJenkins 进程运行在容器里宿主机的~/.ssh对它是不可见的。这时候要么把私钥内容直接粘贴到凭据里要么用 volume 把宿主机的.ssh目录挂载进容器。我通常选择前者因为可维护性更好换机器也不怕丢。3.2 用 Access Token 连接 GitLab APIDeploy Key 解决的是 Git 协议层的认证问题。但如果你的 Jenkins 还需要调用 GitLab API比如创建 Merge Request、读取项目列表、触发 Webhook那还需要在 GitLab 里生成一个Personal Access Token或Project Access Token。生成位置在 GitLab 右上角头像 -Preferences - Access Tokens老版本在Settings - Access Tokens。生成时勾选api权限有效期按需设置。拿到 Token 后在 Jenkins 的Manage Jenkins - Configure System - GitLab里配置连接信息Connection name随便填比如gitlab-prodGitLab host URL填 GitLab 的外网访问地址例如http://gitlab.example.comCredentials选 “GitLab API token”粘贴 Token填完之后点 “Test Connection”正常情况下会显示Success。如果这里就报错那就是 API Token 或网络问题和后面任务里的 Git 凭据无关。3.3 “login failed, check api token or gitlab version” 根因分析这个报错非常经典原文一般长这样login failed. check api token or gitlab version. log in via git if the version is supported.先说结论这个错误出现在Test Connection或任务构建时 GitLab Plugin 调用 GitLab API 的阶段和 “git 拉取代码”本身是两回事。它说明 Jenkins 的 GitLab 插件尝试用 API Token 访问 GitLab 接口但失败了。常见原因按概率排序API Token 无效或已过期。重新生成一个 Token注意勾选api权限。网络不通。在 Jenkins 服务器上用curl手动访问 GitLab API 试试例如curl -H PRIVATE-TOKEN: 你的token http://gitlab.example.com/api/v4/version如果返回{version:xx.x.x...}说明网络正常问题在插件侧。SSL 证书问题。如果 GitLab 是自签名证书HTTP 访问没问题但 Jenkins 用 HTTPS 时可能因为证书不受信任而失败。解决方式是让 Jenkins 信任该证书或者把 GitLab 配置成 HTTP 内网访问。插件版本与 GitLab 版本不兼容。老的 GitLab Plugin 调用的 API 接口在新版 GitLab 里被废弃或反过来。升级插件或 GitLab 时这个报错很容易突然出现。排查的时候别一头扎进 Jenkins 设置里翻要给问题分层先确认 API 能通再确认 Token 有效最后才是插件兼容性。3.4 从“手动测试”到“填入 Jenkins”的验证顺序我自己的固定流程是这样的分享给你作为参考在服务器上用git ls-remote验证 Git 协议层通不通。用curl验证 API 通不通。在 GitLab 侧确认账号权限和 Token Scope。把凭据填入 Jenkins在Credentials管理页可以点 “Verify” 测试但注意这里的验证不一定等于构建时的行为。创建一个最简单的 “Freestyle project”只做 “Git 拉取” 这一个动作跑一次构建看控制台输出。我见过最多的翻车现场是有人跳过了前两步直接在 Jenkins 里配置结果报错以后分不清到底是网络、凭据、还是权限问题。先手动验证再填 Jenkins这是唯一可靠的方法。4. 创建构建任务分支、仓库地址和触发方式的最佳实践环境通了、凭据也配好了接下来才是真正的 “从 GitLab 拉代码” 环节。这里的一些参数设置直接影响构建速度和稳定性。4.1 自由风格任务还是 Pipeline实现拉代码这个动作有两种常见方式自由风格任务Freestyle project的界面比较直观适合初学者和简单的构建场景。在 “Source Code Management” 里选 Git填入 Repository URL选择凭据指定 Branches to build就完成了。优点是上手快缺点是一旦构建步骤变多难以维护。Pipeline则是把整个构建过程写成Jenkinsfile代码即配置适合复杂流程。拉代码的 Pipeline 片段大概是这样的pipeline { agent any stages { stage(Checkout) { steps { checkout([ $class: GitSCM, branches: [[name: env.BRANCH_NAME]], extensions: [], userRemoteConfigs: [[ url: gitgitlab.example.com:group/project.git, credentialsId: jenkins-gitlab-ssh-key ]] ]) } } } }如果你以后要上 “自动化部署”“多分支流水线”建议直接学 Pipeline省得以后迁移一次。4.2 分支参数化与浅克隆默认情况下Jenkins 每次构建都会执行一次git fetch把远端所有分支的引用都拉下来。仓库小还好仓库一旦大比如包含多年历史、大量二进制文件的仓库构建时间和磁盘占用会非常难看。两种优化手段很实用浅克隆Shallow clone在 Git Plugin 的 “Additional Behaviours” 里选择 “Advanced sub-modules behaviours” 或直接加 “Shallow clone” 行为设置depth为 1。这样只拉取最新一次提交构建速度能快几倍。指定分支如果项目固定发布分支为master或main直接把 Branches to build 写成*/master不要写成**。如果你需要“构建时手动选择拉取哪个分支”可以安装Git Parameter Plugin在任务参数里加一个 Git 参数类型选 “Branch”这样每次构建前端会有一个下拉框动态读取远端分支列表选择后作为拉取分支传入。这一步在很多团队里是刚需开发要测某个特性分支测试要部署稳定分支运维要随时发布 hotfix。没有 Git Parameter 的话就只能每次改配置里的分支名或者为每个分支建一个任务明显太低效。4.3 触发方式轮询与 Webhook 怎么选让 Jenkins 知道“代码更新了”有两种思路。轮询Poll SCM是最简单的方式设置一个 cron 表达式比如H/5 * * * *每 5 分钟检查一次远端仓库有变化才触发构建。缺点是检查有延迟而且每次都会向 GitLab 发请求仓库多时会给 GitLab 带来压力。Webhook是 GitLab 在代码提交或合并请求事件发生时主动通知 Jenkins。在 GitLab 项目里进入Settings - Webhooks添加 Jenkins 的 Webhook 地址例如http://jenkins.example.com/project/你的任务名但如果你的 Jenkins 在 NAT 后面或没有公网 IPGitLab 无法主动访问那就只能靠轮询。另外需要注意 Webhook 地址末尾的路径和 Jenkins 插件版本有对应关系老版本可能要加/git/notifyCommit新版本直接用project/任务名即可。我实际踩过的一个坑是配了 Webhook 之后GitLab 那边显示请求成功但 Jenkins 迟迟不触发。最后发现是 Jenkins 的 “CSRF Protection” 阻止了匿名请求需要在系统配置里把 GitLab 服务器的 IP 加入白名单或者在 Webhook 请求里带上 API Token。这个问题很隐蔽因为 Webhook 日志里看到的可能还是 200 状态码。4.4 构建环境中的“隐藏变量”拉取代码成功后构建脚本经常需要知道自己现在在哪、这次构建是第几次。Jenkins 内置了很多环境变量下面这几个在“拉代码 - 构建”场景里最常被使用变量名含义典型用途WORKSPACE工作目录代码被拉取到的位置构建脚本里切换到代码根目录JOB_NAME当前任务名日志、通知消息中标识任务BUILD_NUMBER构建序号产物版本号、镜像标签GIT_COMMIT拉取到的 commit SHA记录构建对应的代码版本GIT_BRANCH拉取的分支名区分环境部署分支建议别在生产构建脚本里写死/var/lib/jenkins/workspace/xxx这种路径直接引用$WORKSPACE这样任务迁移或者换节点不会出问题。5. 把一段构建跑通从拉取代码到 Java Web 应用自动部署光拉代码不算完整的自动化拉完之后能构建、能发布这条链路才真正跑起来。这一步以一个典型的 Java Web 应用为例给你一套可直接参考的思路。5.1 一次标准的构建过程代码已经通过 Git 插件拉取到WORKSPACE接下来构建节点上需要准备 JDK 和 Maven 等工具。在自由风格任务的 “Build” 部分添加一个 “Invoke top-level Maven targets”填写Goals: clean package -DskipTests如果你用 Pipeline对应的步骤是stage(Build) { steps { sh mvn clean package -DskipTests } }这里有一个非常容易踩的坑Jenkins 节点上装的是哪个版本的 JDK和项目要求的是否一致。很多项目用 Java 8 编译但构建节点默认 JDK 是 17结果编译报错。解决方式是安装JDK 参数插件或者在全局工具配置里定义多个 JDK让不同的任务选不同的版本。在配置 “Invoke top-level Maven targets” 时如果下拉列表里没有 Maven需要在Manage Jenkins - Global Tool Configuration里先添加 Maven 安装可以自动下载也可以指向本机已经装好的 Maven。5.2 远程发布与部署结果验证构建产物是target/xxx.war或target/xxx.jar下一步是把产物发布到目标服务器。常见的做法是使用Publish Over SSH插件在系统配置里填好一个 SSH Server然后在任务 “Post-build Actions” 里添加 “Send build artifacts over SSH”设置Source filestarget/*.warRemove prefixtargetRemote directory/opt/appExec command执行重启脚本比如systemctl restart tomcat或docker compose up -d这里有个细节确认 Jenkins 所在机器能通过 SSH 免密登录目标服务器如果不行在 Publish Over SSH 的配置里填好用户名和私钥。目标服务器的目录权限也要提前确认否则文件传过去却没权限写日志里只会显示 “Permission denied”。部署完成后的验证也很重要。发布动作本身只是把文件丢过去应用是否正常起来是另一回事。建议在 Exec command 里加一段健康检查curl -sf http://127.0.0.1:8080/health || exit 1这样 Jenkins 会认为重启失败构建标红不会出现“构建成功但应用挂了”的假象。5.3 复用与扩展把 Pipeline 沉淀成模板当你有多个项目都要走 “拉代码 - 构建 - 部署” 的流程时重复配置自由风格任务会很痛苦。更合理的做法是把构建逻辑写进项目的Jenkinsfile在代码仓库里维护。pipeline { agent any options { buildDiscarder(logRotator(numToKeepStr: 10)) disableConcurrentBuilds() } environment { APP_NAME my-web-app REGISTRY registry.example.com } stages { stage(Checkout) { steps { checkout scm } } stage(Build) { steps { sh mvn clean package -DskipTests } } stage(Archive) { steps { archiveArtifacts artifacts: target/*.war } } stage(Deploy) { when { branch main } steps { sshPublisher( publishers: [ sshPublisherDesc( configName: prod-server, transfers: [ sshTransfer( sourceFiles: target/*.war, remoteDirectory: /opt/app, execCommand: sh /opt/app/deploy.sh ) ] ) ] ) } } } }这里checkout scm会自动使用任务里配置的仓库地址和凭据不需要重复写。按分支判断是否部署是通过when { branch main }控制的这样同一个 Pipeline 既能测试环境发布特性分支也能在生产环境只发布主干逻辑清晰也不容易改错。5.4 顺手解决的常见构建问题最后列几个我在这个过程中真实遇到过、且搜索引擎里经常被搜到的问题问题一git 拉取代码的时候提示 未能顺利退出(退出码 1)这个报错本质是 Git 命令执行非零退出码。先看控制台日志中 Git 命令是哪个环节失败重点检查凭据是否正确SSH Key 还是 Token。分支名是否存在。是否把master写成了main。如果是大仓库 clone 超时在全局 Git 配置里把http.postBuffer调大或者直接用 SSH。问题二Jenkins 提示找不到 Git 仓库或项目确认仓库地址写的是“可通过 Jenkins 访问的地址”而不是你本地浏览器的地址。很多人在办公电脑上测试用的是 NAT 映射的域名Jenkins 服务器解析不了就会报 404 或 Host key verification failed。问题三GitLab CI/CD 里 Docker 镜像构建时报 daemon 错误如果你用 GitLab Runner 构建 Docker 镜像提示error response from daemon: Get ...一般是 Runner 容器没有挂载 Docker socket或者 registry 地址网络不通。这在“自动部署”场景下很常见和 Jenkins 拉代码是两条链路别混在一起排查。最后聊两句我的实际体会如果你问我做 Jenkins 拉代码这件事最值得记住的一句话是什么我会说把“手动验证”变成肌肉记忆。所有配置问题的排查都从 Jenkins 服务器上用命令行先试一次而不是在网页上反复保存测试。另一个经验是凭据尽量走到“少而稳”能用 SSH Key 就不用密码能用 Deploy Key 就不额外建账号能用一个长期有效的 Token 就不要再造第二个。这样即使团队有人离职或者 GitLab 升级也不至于让构建一夜之间全红。Jenkins 拉 GitLab 代码只是整个交付链路的起点但它也是最基础的地基。希望这篇文章能帮你把这一步走稳后面无论是做多分支流水线、镜像构建还是自动部署都会顺手很多。