您的管道在尝试访问外部 API 或下载依赖项时出现错误 403 Forbidden 或 Connection refused?很可能是因为您的 CI/CD 服务器的 IP 地址在目标资源的防火墙上被阻止。代理解决了这个问题:您通过所需的 IP 路由流量,管道可以顺利运行。本文提供了 GitHub Actions、GitLab CI 和 Jenkins 的逐步指南。
为什么在 CI/CD 中使用代理:真实场景
CI/CD 管道在具有固定 IP 地址的服务器上运行——如 GitHub、GitLab 的云运行器或自有的 Jenkins 代理。这些 IP 地址是众所周知的,许多外部服务要么阻止它们,要么限制请求数量。以下是一些必须使用代理的具体情况:
访问地理限制资源
许多企业 npm 注册表、Maven 仓库和内部 API 仅可从特定国家或 IP 范围访问。如果您的 GitHub Actions 运行器位于防火墙级别被目标服务阻止的区域,管道将无法下载依赖项或发送数据。具有所需地理位置的代理可以在不改变基础设施的情况下解决此问题。
速率限制和 IP 阻止
GitHub Actions 的云运行器使用来自 Microsoft Azure 的 IP 地址。许多公共 API 知道这些范围并对其施加严格的限制——或者完全阻止。例如,解析公共数据、在测试期间请求外部 API、从受限 CDN 下载发行版——这一切经常因为云运行器的 IP 地址而失败。通过代理进行轮换可以绕过速率限制。
与真实网站的集成测试
如果您的集成测试访问真实网站或市场(如 Wildberries、Ozon、Avito、Amazon),这些网站在每次运行时都会看到来自运行器的相同 IP,并迅速将其阻止。具有 IP 轮换的代理可以让测试稳定通过,而不会出现验证码和阻止。
访问内部企业资源
企业网络通常对外部世界关闭。如果管道需要部署到内部服务器或访问私有 API,企业网络内的代理(或 SOCKS5 隧道)成为云运行器与封闭基础设施之间的桥梁。
测试广告和营销集成
使用 Facebook Ads API、TikTok Ads API 或 Google Ads API 的团队经常通过 CI/CD 自动化创建广告活动。这些平台对 IP 有严格的规则:来自数据中心的请求可能会被阻止或需要额外的验证。管道中的住宅代理使请求看起来像普通用户流量。
选择哪种类型的代理用于管道
选择代理类型取决于任务。对于 CI/CD 管道,有三种相关的选项——每种都有其优缺点和限制:
| 代理类型 | 速度 | 网站信任度 | 最适合用于 |
|---|---|---|---|
| 数据中心代理 | 非常高 | 中等 | 下载依赖项、内部仓库、快速 API(没有严格检查) |
| 住宅代理 | 中等 | 高 | 与真实网站的集成测试、广告 API(Facebook、TikTok)、市场 |
| 移动代理 | 中等 | 最高 | 移动 API 测试、与具有最高反机器人保护的平台合作 |
实践规则:
如果任务是下载包或访问内部服务,请选择数据中心代理——它们速度快且便宜。如果任务是对真实网站进行测试或与广告平台合作,则需要住宅代理。SOCKS5 协议优于 HTTP/HTTPS,因为它在处理非标准端口和协议时更透明。
在 GitHub Actions 中设置代理
GitHub Actions 是目前最流行的 CI/CD 工具。这里的代理设置通过环境变量和存储库的秘密进行。我们逐步解析。
步骤 1:将代理数据添加到存储库的秘密中
切勿将代理的用户名和密码直接写入 YAML 工作流文件。请使用 GitHub Secrets:
- 打开存储库 → 设置 → 秘密和变量 → 操作
- 点击 新建存储库秘密
- 创建一个名为
PROXY_URL的秘密,值的格式为http://user:[email protected]:port
步骤 2:在工作流中使用环境变量
大多数工具(curl、wget、npm、pip、Maven)会自动获取标准环境变量 HTTP_PROXY、HTTPS_PROXY 和 NO_PROXY。工作流示例:
name: Build with Proxy
on: [push]
env:
HTTP_PROXY: ${{ secrets.PROXY_URL }}
HTTPS_PROXY: ${{ secrets.PROXY_URL }}
NO_PROXY: localhost,127.0.0.1,internal.company.com
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install dependencies
run: npm ci
- name: Run integration tests
run: npm test
- name: Call external API
run: |
curl -v https://api.example.com/data
步骤 3:在 GitHub Actions 中使用 SOCKS5 代理
如果您使用 SOCKS5(推荐用于大多数任务),标准环境变量是不够的——需要本地隧道。请使用 proxychains 工具或配置 microsocks:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Setup SOCKS5 proxy tunnel
run: |
sudo apt-get install -y proxychains4
echo "socks5 proxy.host 1080 user password" >> /etc/proxychains4.conf
- name: Run command through SOCKS5
run: proxychains4 curl https://restricted-resource.com/api
为特定工具配置代理
一些工具会忽略系统变量,需要单独配置:
| 工具 | 如何配置代理 |
|---|---|
| npm / yarn | npm config set proxy http://user:pass@host:port |
| pip (Python) | pip install --proxy http://user:pass@host:port package |
| Maven | 通过 settings.xml 中的 <proxies> 部分 |
| Gradle | systemProp.https.proxyHost=host 在 gradle.properties 中 |
| Git | git config --global http.proxy http://user:pass@host:port |
| Docker build | --build-arg HTTP_PROXY=http://user:pass@host:port |
在 GitLab CI 中设置代理
GitLab CI 提供了多个级别来设置环境变量:项目级、组级或实例级。这使得与 GitHub Actions 相比,代理的管理更加灵活。
步骤 1:将变量添加到 GitLab CI/CD 变量中
- 打开项目 → 设置 → CI/CD → 变量 部分
- 点击 添加变量
- 添加变量
PROXY_URL,类型为 Masked(在日志中隐藏值) - 值:
http://user:[email protected]:port
步骤 2:在 .gitlab-ci.yml 中使用变量
variables:
HTTP_PROXY: $PROXY_URL
HTTPS_PROXY: $PROXY_URL
NO_PROXY: "localhost,127.0.0.1,.internal.company.com"
stages:
- build
- test
- deploy
build:
stage: build
image: node:20-alpine
script:
- npm ci
- npm run build
integration_tests:
stage: test
image: python:3.11
script:
- pip install -r requirements.txt
- pytest tests/integration/
deploy:
stage: deploy
script:
- curl -X POST https://api.external-service.com/deploy
-H "Authorization: Bearer $DEPLOY_TOKEN"
-d '{"version": "$CI_COMMIT_SHA"}'
仅为特定作业设置代理
如果代理并不需要在每个地方使用(例如,仅用于集成测试,而不是构建),请在特定作业级别设置变量,而不是全局设置:
integration_tests:
stage: test
variables:
HTTP_PROXY: $PROXY_URL
HTTPS_PROXY: $PROXY_URL
script:
- pytest tests/integration/
build:
stage: build
# 此处未设置代理——直接连接
script:
- npm ci && npm run build
自托管 GitLab Runner:在 Runner 级别设置代理
如果您使用自有的 GitLab Runner,可以在 Runner 的配置中全局设置代理。打开文件 /etc/gitlab-runner/config.toml,并在 [runners.env] 部分添加:
[[runners]]
name = "my-runner"
url = "https://gitlab.com/"
token = "TOKEN"
executor = "docker"
environment = [
"HTTP_PROXY=http://user:[email protected]:port",
"HTTPS_PROXY=http://user:[email protected]:port",
"NO_PROXY=localhost,127.0.0.1"
]
这很方便,当该 Runner 上的所有管道都需要使用代理时——无需在每个 .gitlab-ci.yml 中指定。
在 Jenkins 中设置代理
Jenkins 是三者中最灵活的工具,但设置起来也是最复杂的。可以在多个级别设置代理:全局设置、特定 Pipeline 或单个步骤。
方法 1:Jenkins 中的全局代理设置
- 打开 管理 Jenkins → 系统
- 找到 HTTP 代理配置 部分
- 填写字段:服务器、端口、用户名、密码
- 在 无代理主机 字段中列出内部地址,以逗号分隔
- 点击 测试 URL 进行检查并保存
此设置影响 Jenkins 插件和更新的下载,但 不会自动 传递到管道的运行环境中。管道需要单独的配置。
方法 2:通过环境变量在声明性管道中设置代理
pipeline {
agent any
environment {
HTTP_PROXY = credentials('proxy-url-credential')
HTTPS_PROXY = credentials('proxy-url-credential')
NO_PROXY = 'localhost,127.0.0.1,internal.company.com'
}
stages {
stage('Build') {
steps {
sh 'npm ci'
sh 'npm run build'
}
}
stage('Integration Tests') {
steps {
sh 'pytest tests/integration/'
}
}
stage('Deploy') {
steps {
sh '''
curl -X POST https://api.external-service.com/deploy \
-H "Authorization: Bearer ${DEPLOY_TOKEN}" \
-d "version=${GIT_COMMIT}"
'''
}
}
}
}
步骤 3:将代理凭据添加到 Jenkins 凭据中
- 打开 管理 Jenkins → 凭据 → 系统 → 全局凭据
- 点击 添加凭据
- 类型:秘密文本
- ID:
proxy-url-credential - 秘密:
http://user:[email protected]:port
方法 3:通过 JVM 参数为 Java 项目设置代理
如果您的管道构建 Java 项目(Maven、Gradle),系统环境变量可能无法工作——JVM 使用自己的系统属性。将其添加到 JAVA_OPTS 中:
environment {
JAVA_OPTS = '-Dhttps.proxyHost=proxy.host -Dhttps.proxyPort=8080 -Dhttps.proxyUser=user -Dhttps.proxyPassword=password -Dhttp.nonProxyHosts=localhost|127.0.0.1|*.internal.com'
}
管道中的 Docker 容器内的代理
大多数现代 CI/CD 管道在 Docker 容器内运行步骤。将代理传递到容器是一个单独的任务,可以通过几种方式解决。
在构建镜像时通过 --build-arg 传递代理
如果代理仅在构建 Docker 镜像时需要(例如,在 Dockerfile 内安装包),请使用构建参数:
# 在 .github/workflows/build.yml 或 .gitlab-ci.yml 中
docker build \
--build-arg HTTP_PROXY=$HTTP_PROXY \
--build-arg HTTPS_PROXY=$HTTPS_PROXY \
--build-arg NO_PROXY=$NO_PROXY \
-t myapp:latest .
# 在 Dockerfile 中
ARG HTTP_PROXY
ARG HTTPS_PROXY
ARG NO_PROXY
ENV HTTP_PROXY=$HTTP_PROXY
ENV HTTPS_PROXY=$HTTPS_PROXY
ENV NO_PROXY=$NO_PROXY
RUN apt-get update && apt-get install -y curl
RUN npm ci
⚠️ 注意:构建镜像时的安全性
通过 ARG 和 ENV 在 Dockerfile 中设置的变量会保存在镜像的元数据中,并可以通过 docker inspect 查看。如果代理需要身份验证,请确保生成的镜像不会发布到公共注册表——否则凭据将暴露在公共场合。
为 Docker 守护进程全局设置代理
在自托管的运行器上,可以为整个 Docker 守护进程设置代理——这样所有容器将自动获得代理,而无需在 Dockerfile 中进行更改:
# /etc/systemd/system/docker.service.d/http-proxy.conf
[Service]
Environment="HTTP_PROXY=http://user:[email protected]:port"
Environment="HTTPS_PROXY=http://user:[email protected]:port"
Environment="NO_PROXY=localhost,127.0.0.1,registry.internal.com"
# 应用更改:
# systemctl daemon-reload
# systemctl restart docker
安全性:如何存储代理凭据
代理凭据与 API 密钥或数据库密码一样,都是秘密。它们的泄露意味着任何人都可以使用您的代理而不花费您的费用。以下是安全存储的规则:
安全检查清单
- ✅ 绝对不要 将代理的用户名/密码直接写入管道的 YAML 文件
- ✅ 在 GitLab 中使用 Masked variables,在 GitHub 中使用 Encrypted secrets——它们在日志中被隐藏
- ✅ 在 Jenkins 中使用 Secret text 或 Username with password 类型的凭据存储
- ✅ 为内部地址添加
NO_PROXY——流量到自己的基础设施不应通过代理 - ✅ 定期轮换代理密码——仅在秘密存储中更新,而不更改管道代码
- ✅ 在支持的地方使用代理的 IP 身份验证(将运行器的 IP 列入白名单)——这比密码更安全
- ✅ 检查代理日志以发现异常活动
代理 URL 格式:什么放在哪里
| 协议 | URL 格式 | 何时使用 |
|---|---|---|
| HTTP | http://user:pass@host:port |
大多数工具,如 npm、pip、curl |
| HTTPS | https://user:pass@host:port |
与代理服务器的加密连接 |
| SOCKS5 | socks5://user:pass@host:port |
非标准端口、UDP 流量、最大兼容性 |
常见错误及其解决方法
即使在正确设置后,仍可能会出现问题。以下是最常见的错误及其解决方案:
错误:需要代理身份验证 (407)
原因:用户名或密码不正确,或者工具未传递它们。
解决方案:检查 URL 格式——密码中的特殊字符需要进行 URL 编码。例如,p@ss#word → p%40ss%23word。还要确保环境变量确实传递到步骤中——通过 echo $HTTP_PROXY(前几个字符)进行检查。
错误:SSL 证书验证失败
原因:代理执行 SSL 检查(MITM)并替换证书。客户端不信任代理的证书。
解决方案:将代理的根证书添加到受信任的证书中。对于 curl: --cacert /path/to/proxy-ca.crt。对于 npm: npm config set cafile /path/to/proxy-ca.crt。或者使用不进行 SSL 检查的代理——对于 CI/CD 更可取。
错误:通过代理连接超时
原因:代理服务器无法从运行器的 IP 访问,或者端口被防火墙阻止。
解决方案:通过管道步骤中的命令 nc -zv proxy.host port 检查代理的可用性。确保运行器的 IP 已添加到代理提供商的白名单中(如果使用 IP 身份验证)。对于 GitHub Actions 的云运行器,IP 范围发布在 meta.github.com 中。
错误:工具忽略 HTTP_PROXY 变量
原因:一些工具(尤其是基于 Java 的工具)不读取系统环境变量。
解决方案:使用特定工具的原生代理设置(见上表)。对于 Java,通过 JAVA_OPTS 添加 JVM 属性。对于 curl,明确使用标志 -x http://proxy:port。
错误:内部服务也通过代理
原因:变量 NO_PROXY 未设置或设置不正确。
解决方案:在 NO_PROXY 中指定所有内部域和 IP。对域使用通配符: NO_PROXY=localhost,127.0.0.1,10.0.0.0/8,.internal.company.com。请注意:某些工具支持 CIDR 表示法,而其他工具仅支持精确域。
结论
在 CI/CD 管道中设置代理并不是一次性任务,而是自动化正确架构的一部分。我们讨论了三种主要工具:GitHub Actions(通过 Secrets 和环境变量)、GitLab CI(通过带掩码的 Variables)和 Jenkins(通过凭据存储和声明性管道)。关键原则对所有人都是相同的:绝不要在代码中存储凭据,使用 NO_PROXY 处理内部地址,并根据具体任务选择代理类型。
选择正确的代理类型对管道的稳定性至关重要。对于下载依赖项和访问标准 API,快速的 数据中心代理 足够了。如果您的管道在真实网站上进行集成测试,或与广告平台(Facebook Ads API、TikTok Ads API)或市场进行交互——请使用 住宅代理:它们的 IP 被视为普通用户流量,极少受到阻止或速率限制。
主要原则是:在管道开始时测试代理的单独步骤——这将快速诊断问题,而不会浪费时间在漫长构建的最后寻找错误。添加步骤 curl -v https://api.ipify.org 在代理设置后立即——它将显示请求发送的 IP,并确认代理工作正常。