Как настроить параметры прокси в контейнерах Docker

Как настроить параметры прокси в контейнерах Docker

Docker и прокси нередко встречаются в самых неожиданных местах. Сборка может успешно пройти на одном ноутбуке, а на CI-раннере — сломаться, и часто причина сводится к одной пропущенной переменной прокси.

Это руководство показывает, как настроить параметры прокси в контейнерах Docker для одного docker run, для сборки образов и для постоянного использования на хосте. Точный способ зависит от области применения, а не от догадок. Если вам нужно быстро настроить прокси в Docker, начните с того сценария, который у вас действительно используется.

Сразу предупреждение: настройки прокси внутри контейнеров — не магия. Они помогают только если контейнер действительно может достучаться до хоста прокси и если прокси разрешает нужный вам адрес назначения.

1. Определите контекст контейнера и область действия прокси

Сначала ответьте на один вопрос: прокси нужен для одного запущенного контейнера, для docker build или для каждого контейнера, запускаемого разработчиком либо CI-задачей? От этого зависит, куда именно помещать настройку, ведь одни и те же прокси для docker build и docker run настраиваются по-разному.

Один docker run — самый простой случай. Вы один раз передаёте переменные окружения, контейнер ими пользуется, а после удаления контейнера изменения исчезают.

Прокси на этапе сборки — это другое. Менеджерам пакетов внутри образа, таким как apt, apk, pip или npm, может понадобиться прокси во время создания слоёв, даже если финальному контейнеру он не нужен.

Глобальные настройки на уровне хоста менее заметны. Они экономят время на ввод, но при этом скрывают тот факт, что контейнер наследует поведение прокси из конфигурации Docker-клиента, а не из самого образа.

Если вы выбираете между сетевыми путями для автоматизации, наше руководство о том, как выбрать VPN, поможет понять, почему одной среде может понадобиться другой способ выхода в сеть, чем другой.

2. Определите значения прокси, которые нужны Docker

Прежде чем писать команду, вам нужен точный URL прокси, порт и любые учётные данные. Запросите у сетевой команды полную форму, а не просто имя хоста, брошенное в чат.

Значение прокси часто выглядит как http://proxy.example.com:3128 или https://proxy.example.com:8443. Если нужна аутентификация, включите имя пользователя и пароль в формате, который указала ваша команда.

Сохраните список внутренних адресов, которые должны обходить прокси. В него могут входить имена сервисов, частные домены, хосты баз данных или суффиксы кластера.

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

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

3. Передайте переменные прокси в запущенный контейнер

Для одной сессии контейнера используйте docker run с переменными окружения. Docker и большинство популярных инструментов внутри контейнера распознают HTTP_PROXY, HTTPS_PROXY и NO_PROXY, а во многих образах — ещё и их варианты в нижнем регистре. Именно так обычно задают HTTP_PROXY HTTPS_PROXY NO_PROXY Docker понимает без дополнительных костылей.

Простой пример выглядит так:

  1. docker run -e HTTP_PROXY=http://proxy.example.com:3128 -e HTTPS_PROXY=http://proxy.example.com:3128 myimage
  2. Добавьте -e NO_PROXY=localhost,127.0.0.1,.internal.example.com, если внутренние сервисы должны ходить напрямую.
  3. Повторите с именами в нижнем регистре, если инструменты в образе ожидают именно их.

Некоторые базовые образы читают только имена в верхнем регистре. Некоторые инструменты поддерживают оба варианта. Это раздражает, но это реальность, поэтому тестируйте именно тот образ, который вы поставляете.

Переменные можно также поместить в файл Compose или файл окружения и дать Docker прочитать их оттуда, но принцип остаётся тем же: контейнер должен получить значения прокси при запуске.

Одна небольшая деталь важна. Если контейнер стартует без переменной, приложение внутри него не сможет потом «догадаться» о прокси.

4. Настройте прокси для сборки образов

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

Используйте docker build с передачей переменных прокси как аргументов сборки или значений окружения — в зависимости от инструментов и версии Docker. Цель в том, чтобы прокси был виден именно во время сборки, а не только при запуске.

Обычно используют такой шаблон:

  1. docker build --build-arg HTTP_PROXY=http://proxy.example.com:3128 --build-arg HTTPS_PROXY=http://proxy.example.com:3128 -t myimage .
  2. Скопируйте те же значения в NO_PROXY, если шагам сборки нужно напрямую обращаться к внутренним зеркалам.
  3. Не тащите значения прокси в финальное runtime-окружение, если приложению они действительно не нужны.

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

Сборка также может загружать базовые образы через Docker daemon, который работает отдельно от самого контейнерного процесса. Это значит, что одна настройка прокси может помочь на шаге Dockerfile, а другая — понадобиться на хосте Docker, если извлечение образов ломается ещё до старта сборки.

Для более широкого взгляда на поведение прокси в задачах скрапинга и автоматизации см. руководство по ротации прокси для веб-скрапинга. Механика там другая, но необходимость управлять адресами назначения и режимами отказа вполне знакома.

5. Сделайте настройки прокси постоянными через конфигурацию Docker-клиента

Постоянно указывать одни и те же флаги быстро надоедает. Если вы каждый день работаете через один и тот же прокси, конфигурация Docker-клиента может хранить значения по умолчанию, чтобы новые контейнеры наследовали их без повторного ввода.

Обычно это удобство на стороне клиента, а не трюк в Dockerfile. Это помогает разработчикам и CI-раннерам запускать контейнеры с одинаковыми значениями прокси, но не переписывает сам образ.

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

Один практический эффект — предсказуемость. Разработчик запускает тестовый контейнер в 9:00, и тот уже знает, куда должен идти исходящий трафик.

Если перед правкой конфигурационных файлов вам нужно освежить термины, глоссарий VPN и прокси поможет разобраться в них без лишней теории ради самой теории.

6. Настройте исключения no-proxy для внутренних сервисов

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

Составляйте список на основе реальных адресов. Включайте localhost, 127.0.0.1, имена сервисов, используемые Compose, суффиксы частных доменов и конечные точки кластера, находящиеся внутри вашей сети.

Не предполагаете, что точки везде работают одинаково. Некоторые парсеры считают .example.com совпадением для всех поддоменов, а другие предпочитают example.com. Проверьте точный формат, который использует ваш образ.

Для имён в стиле Kubernetes контейнеру может понадобиться обход прокси для service.namespace.svc.cluster.local или более широкого суффикса кластера. Одна такая строка способна предотвратить неделю непонятных тайм-аутов.

Хорошая привычка — записывать список обхода рядом со значением прокси в одном и том же месте. Разнесённая по разным местам конфигурация провоцирует ошибки.

7. Проверьте поведение внутри контейнера

Никогда не доверяйте флагу, пока не проверите контейнер изнутри. Сначала откройте shell в контейнере и выведите переменные окружения, затем протестируйте запрос к внешнему и внутреннему хосту.

Например, выполните внутри контейнера env | grep -i proxy и убедитесь, что значения действительно присутствуют. Затем используйте инструмент вроде curl или сам менеджер пакетов, чтобы сделать один исходящий запрос.

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

Проверьте как минимум два адреса: один публичный сайт и один внутренний сервис. Разница покажет, работают ли и путь через прокси, и путь обхода так, как ожидается.

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

8. Подстройте параметры прокси под распространённые рабочие сценарии Docker

Compose, многостадийные сборки и CI-задачи добавляют каждый свой слой. Одного флага docker run может хватить на ноутбуке, но конвейеру, который собирает, тестирует и публикует образ, прокси часто нужен в нескольких местах.

Docker Compose может передавать значения окружения в сервисы, но секции сборки всё равно могут потребовать отдельных аргументов build. Это означает, что одно и то же значение прокси может появиться один раз для этапа сборки и один раз для runtime.

CI-задачи часто хранят значения прокси в маскированных переменных или секретах пайплайна. Затем задача подставляет их в шаги docker build и docker run, чтобы контейнер мог обращаться к зеркалам пакетов и внешним API в рамках одного рабочего процесса.

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

Системы оркестрации контейнеров могут добавлять и собственные правила. Определение сервиса, sidecar и агент сборки могут указывать на разные пути, поэтому нельзя просто предполагать, что один и тот же URL прокси будет работать везде без проверки.

Для трафика, похожего на браузерный, или для задач с разными протоколами вопрос не только в том, есть ли прокси, но и в том, какой тип подходит задаче. Сравнение в статье о SOCKS5-прокси и HTTP-прокси полезно, когда контейнеру нужно общаться сразу с несколькими типами конечных точек.

Практические шаблоны команд

Эти шаблоны покрывают самые распространённые случаи. Названия могут отличаться в зависимости от образа, но логика остаётся прежней.

Случай Что задать Куда это помещается
Один контейнер HTTP_PROXY, HTTPS_PROXY, NO_PROXY docker run -e ...
Сборка образа Аргументы сборки для значений прокси docker build --build-arg ...
Повторное локальное использование Настройки прокси по умолчанию Конфигурация Docker-клиента
Внутренние сервисы Список NO_PROXY с хостами и суффиксами То же место, что и значения прокси
CI-пайплайн Маскированные переменные прокси Секреты пайплайна или окружение задачи

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

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