Раннер CI поднимается и уничтожается за минуты, у контейнера нет постоянного диска и часто нет постоянного IP, а внешний прокси нужно подключать заново при каждом запуске — руками через десктопное приложение это не работает. Разберём, как передать прокси в контейнер и пайплайн правильно: без кредов в образе, с учётом плавающего адреса раннера и с контролем трафика и изоляции между параллельными джобами.

Передача параметров через переменные окружения

Стандартный способ передать прокси в контейнер и в шаг пайплайна — переменные HTTP_PROXY, HTTPS_PROXY и NO_PROXY (и их аналоги в нижнем регистре, которые часть библиотек читает вместо верхнего). Значение — строка подключения с логином и паролем: протокол, креды, хост, порт. Большинство HTTP-клиентов, пакетных менеджеров и утилит командной строки подхватывают эти переменные автоматически, без правки кода приложения.

NO_PROXY отдельно перечисляет адреса и домены, которые вызываются напрямую: внутренние сервисы контейнерной сети, локальный реестр образов, служебные адреса между шагами одного пайплайна. Без корректного NO_PROXY внутренний трафик тоже уходит через внешний прокси, увеличивая расход и задержку без пользы. Переменные задают на уровне шага, а не всего пайплайна, если разным задачам нужны разные адреса — сборка идёт напрямую, а шаг с внешними проверками через канал с нужным гео.

Почему креды нельзя зашивать в образ

Контейнер собирается слоями, и каждый слой попадает в реестр образов и остаётся там даже после удаления секрета в следующем слое — команда, добавившая логин и пароль в переменную окружения на этапе сборки, оставляет их читаемыми через историю слоёв, даже если следующая же команда их удаляет. Инструменты сканирования образов на секреты находят такие креды за секунды, причём как в публичных, так и в приватных реестрах — приватность реестра не эквивалентна безопасности.

Правильная схема — инъекция секрета во время выполнения, а не сборки: переменные окружения, зашифрованное хранилище пайплайна или volume, который монтируется при запуске и не попадает ни в один слой образа. Сам образ остаётся одинаковым для всех окружений, а меняется только значение, подставляемое системой CI при старте джобы.

Whitelist IP не работает для раннеров с плавающим адресом

Разрешить доступ по IP-адресу раннера — рабочая схема только для выделенной инфраструктуры с постоянным адресом. Облачный раннер получает контейнер из общего пула на каждый запуск, и внешний IP меняется от джобы к джобе, а иногда и в середине шага при пересоздании узла — закреплённый в whitelist адрес устареет уже на следующем запуске, и доступ начнёт падать без видимой причины в самом коде.

Единственная устойчивая схема здесь — авторизация по паре логин-пароль, привязанной к учётной записи, а не к адресу. Это тот же принцип, что и для десктопного клиента: строка подключения содержит креды, а не полагается на статичный список разрешённых адресов, который раннер физически не может гарантировать. Как устроена ротация выданных адресов при такой авторизации — в статье о ротации IP по API.

Расход трафика в CI и как его ограничить

Пайплайн, который дёргает внешний прокси на каждом шаге каждого запуска, накапливает трафик быстрее, чем кажется на старте: тысяча проверок страниц с изображениями — уже 3–5 ГБ, а ежедневный прогон по сотне адресов в мониторинге даёт порядка 1,5–3 ГБ в месяц на один сценарий, и это без учёта повторов при нестабильном шаге.

Ограничить расход помогают три вещи: кэширование зависимостей и артефактов сборки, чтобы наружу уходили только реально внешние вызовы, а не пересборка одного и того же на каждом запуске; отключение медиа там, где сценарию нужен лишь текст или ответ API; и датацентровый адрес с фиксированной месячной ценой для задач без гео-чувствительности — он не тарифицируется по трафику, и тестовые прогоны не увеличивают счёт за гигабайты. Правило экономии подробнее разобрано в статье дешёвые против дорогих прокси.

Изоляция между параллельными задачами

Пайплайн с параллельными джобами — типовой сценарий CI — не должен направлять все ветки через один и тот же выданный адрес: если один из шагов получает от целевого сайта блокировку по репутации или капчу, под раздачу попадают сразу все параллельные джобы с тем же IP, включая те, что ни при чём.

Практическое решение — отдельная сессия или отдельный сегмент кредов на каждую параллельную ветку, где идентификатором служит номер джобы или шага. Для каналов с посессионной выдачей адреса это значит, что каждая задача получает собственный IP автоматически, если сессионный идентификатор действительно уникален для каждого запуска, а не захардкожен один на весь пайплайн.

Частые вопросы

Можно ли использовать один и тот же прокси-аккаунт сразу в нескольких пайплайнах?

Технически да, но на канале с общим лимитом параллельных сессий это создаёт конкуренцию за сессии между несвязанными пайплайнами: один вытесняет сессии другого в моменты пиковой нагрузки. Для нескольких активных пайплайнов практичнее развести их по отдельным сегментам кредов даже в рамках одного тарифа.

Как передать прокси в контейнер, если инструмент внутри не поддерживает переменные окружения?

Часть утилит игнорирует HTTP_PROXY и требует адрес в собственном конфиге или флаге командной строки — тогда строку подключения передают явно при вызове инструмента, читая её из той же переменной на уровне обёртывающего скрипта шага.

Нужно ли ограничивать прокси только внешними доменами, к которым реально обращается пайплайн?

Да, если аккаунт поддерживает такую настройку — это не столько экономит трафик, сколько исключает случайные обращения к внутренним и служебным адресам из-за ошибки в NO_PROXY, которые иначе останутся незамеченными до разбора логов.

Прокси с авторизацией по логину и паролю для CI, готовые к передаче через переменные окружения без привязки к адресу раннера, — в разделе прокси. Документация по интеграции и лимитам — в API прокси.