Работаете с Docker в корпоративной сети, на сервере с ограниченным доступом или хотите, чтобы ваши контейнеры выходили в интернет через конкретный IP? Без правильной настройки прокси Docker просто не сможет скачать образы с Docker Hub или любого другого реестра. В этой статье разберём все уровни проксирования — от демона до отдельного контейнера.
Зачем Docker вообще нужен прокси
Docker — это инструмент, который постоянно обращается к внешним ресурсам. При каждом docker pull или docker build демон лезет в Docker Hub, GitHub Container Registry, Google Container Registry или в ваш приватный реестр. И вот тут начинаются проблемы.
Ситуации, когда без прокси не обойтись:
- Корпоративная сеть с файрволом — весь трафик обязан идти через корпоративный прокси-сервер, иначе соединение блокируется.
- Гео-ограничения — Docker Hub или отдельные реестры недоступны из вашей страны или датацентра.
- Ограниченный сервер без прямого выхода в интернет — VPS в закрытом контуре, где интернет доступен только через шлюз.
- Контроль исходящего трафика контейнеров — вы хотите, чтобы приложения внутри контейнеров выходили в сеть через конкретный IP, например для парсинга, API-запросов или тестирования геозависимого контента.
- Анонимизация запросов — скрыть реальный IP сервера при обращении к внешним сервисам из контейнера.
- Rate limiting — Docker Hub ограничивает количество pull-запросов для анонимных пользователей (100 pull за 6 часов). Через прокси с ротацией IP можно обойти это ограничение.
Важно понимать, что Docker — это не одно приложение, а система из нескольких компонентов. Демон (dockerd) живёт на хосте и делает pull образов. Контейнеры — это изолированные процессы со своими сетевыми настройками. Поэтому настройка прокси для демона и для контейнеров — это две разные задачи, которые решаются по-разному.
Три уровня проксирования в Docker
Прежде чем лезть в конфиги, нужно понять архитектуру. В Docker есть три независимых уровня, каждый из которых требует отдельной настройки прокси:
| Уровень | Что делает | Где настраивается |
|---|---|---|
| Docker daemon | Скачивает образы (docker pull), обращается к реестрам | systemd override или daemon.json |
| Docker build | Выполняет команды при сборке образа (RUN apt-get, pip install и т.д.) | ARG и ENV в Dockerfile или --build-arg |
| Runtime контейнер | Запущенное приложение, которое делает HTTP-запросы | ENV при docker run или в docker-compose.yml |
Типичная ошибка — настроить прокси только на одном уровне и удивляться, почему образы всё равно не тянутся или приложение не видит прокси. Давайте разберём каждый уровень подробно.
Настройка прокси для Docker daemon (pull образов)
Это самая важная настройка, если вы хотите тянуть образы через прокси. Docker daemon — это системный сервис, который запускается через systemd. Переменные окружения вашего пользователя или даже root он не видит. Нужно передавать прокси именно в окружение сервиса.
Способ 1: через systemd override (рекомендуется)
Создайте директорию для переопределения настроек сервиса:
sudo mkdir -p /etc/systemd/system/docker.service.d
Создайте файл /etc/systemd/system/docker.service.d/http-proxy.conf со следующим содержимым:
[Service] Environment="HTTP_PROXY=http://username:password@proxy-host:port" Environment="HTTPS_PROXY=http://username:password@proxy-host:port" Environment="NO_PROXY=localhost,127.0.0.1,::1,your-private-registry.example.com"
Если прокси без аутентификации — просто уберите username:password@. После создания файла перезагрузите конфигурацию systemd и перезапустите Docker:
sudo systemctl daemon-reload sudo systemctl restart docker
Проверьте, что настройки применились:
sudo systemctl show --property=Environment docker
В выводе должны появиться ваши переменные HTTP_PROXY и HTTPS_PROXY.
Способ 2: через ~/.docker/config.json (Docker Desktop и новые версии)
Начиная с Docker Engine 23.0 появился более удобный способ — настройка прокси через конфигурационный файл клиента. Создайте или отредактируйте файл ~/.docker/config.json:
{
"proxies": {
"default": {
"httpProxy": "http://username:password@proxy-host:port",
"httpsProxy": "http://username:password@proxy-host:port",
"noProxy": "localhost,127.0.0.1,::1"
}
}
}
Этот способ удобен тем, что не требует прав root и перезапуска демона. Настройки автоматически передаются в контейнеры при сборке как build-аргументы. Однако для управления pull-запросами самого демона всё равно нужен способ через systemd.
💡 Важно про NO_PROXY
Всегда добавляйте в NO_PROXY адреса ваших внутренних реестров и сервисов. Иначе Docker будет пытаться достучаться до них через прокси и получать ошибки соединения. Типичный список: localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
Прокси внутри контейнера при сборке (build-time)
Когда Docker собирает образ и выполняет команды типа RUN apt-get install, RUN pip install или RUN npm install — эти команды выполняются внутри временного контейнера. У него нет доступа к прокси хоста по умолчанию. Нужно явно передать прокси через build-аргументы.
Передача прокси через --build-arg
docker build \ --build-arg HTTP_PROXY=http://proxy-host:port \ --build-arg HTTPS_PROXY=http://proxy-host:port \ --build-arg NO_PROXY=localhost,127.0.0.1 \ -t my-image .
Настройка в Dockerfile
Если прокси нужен только на этапе сборки и вы не хотите, чтобы он попал в финальный образ — используйте ARG, а не ENV:
FROM ubuntu:22.04 # Объявляем аргументы сборки ARG HTTP_PROXY ARG HTTPS_PROXY ARG NO_PROXY # Используем их в командах RUN apt-get update && apt-get install -y curl wget # После этого блока прокси не будет в ENV контейнера
Если же прокси нужен и в запущенном контейнере — используйте ENV:
FROM python:3.11 ENV HTTP_PROXY=http://proxy-host:port ENV HTTPS_PROXY=http://proxy-host:port ENV NO_PROXY=localhost,127.0.0.1 RUN pip install requests beautifulsoup4 CMD ["python", "app.py"]
⚠️ Внимание: безопасность
Не хардкодьте логин и пароль прокси прямо в Dockerfile, если образ будет публичным или попадёт в репозиторий. Используйте --build-arg и передавайте значения из переменных окружения CI/CD системы. Команда docker history может показать значения ARG, поэтому для секретов используйте Docker BuildKit secrets.
Прокси для запущенного контейнера (runtime)
Это самый распространённый сценарий для приложений, которые делают HTTP-запросы во время работы — парсеры, боты, микросервисы, которым нужен выход через конкретный IP. Здесь настройка максимально простая: нужно передать переменные окружения при запуске контейнера.
Через docker run
docker run \ -e HTTP_PROXY=http://username:password@proxy-host:port \ -e HTTPS_PROXY=http://username:password@proxy-host:port \ -e NO_PROXY=localhost,127.0.0.1 \ my-image
Большинство популярных HTTP-библиотек автоматически подхватывают переменные HTTP_PROXY и HTTPS_PROXY: Python requests, curl, wget, Go's net/http, Node.js https-proxy-agent и другие. Ничего дополнительно в коде прописывать не нужно.
Через файл .env
Удобнее хранить настройки в файле .env и передавать его целиком:
# .env файл HTTP_PROXY=http://username:password@proxy-host:port HTTPS_PROXY=http://username:password@proxy-host:port NO_PROXY=localhost,127.0.0.1
docker run --env-file .env my-image
SOCKS5 прокси в контейнере
Если вы используете SOCKS5 прокси (например, резидентные прокси чаще всего поддерживают именно этот протокол), синтаксис немного отличается:
docker run \ -e ALL_PROXY=socks5://username:password@proxy-host:port \ my-image
Обратите внимание: не все библиотеки поддерживают SOCKS5 через переменную окружения без дополнительных зависимостей. Python requests требует установки requests[socks], curl должен быть собран с поддержкой libcurl-socks.
Прокси в Docker Compose
В реальных проектах редко используют голый docker run. Чаще всего работают с Docker Compose, где несколько сервисов описаны в одном файле. Настройка прокси здесь делается на уровне каждого сервиса или через файл переменных.
Вариант 1: environment в docker-compose.yml
version: '3.8'
services:
scraper:
image: my-scraper:latest
environment:
- HTTP_PROXY=http://username:password@proxy-host:port
- HTTPS_PROXY=http://username:password@proxy-host:port
- NO_PROXY=localhost,127.0.0.1
restart: unless-stopped
api:
image: my-api:latest
# Этот сервис без прокси — обращается только к внутренним ресурсам
ports:
- "8080:8080"
Вариант 2: через .env файл (рекомендуется)
Создайте .env в директории с docker-compose.yml. Docker Compose автоматически подхватывает этот файл:
# .env PROXY_HOST=proxy-host PROXY_PORT=8080 PROXY_USER=username PROXY_PASS=password
version: '3.8'
services:
scraper:
image: my-scraper:latest
environment:
- HTTP_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
- HTTPS_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
- NO_PROXY=localhost,127.0.0.1
Прокси при сборке в Compose
Если в Compose есть секция build и нужно передать прокси на этапе сборки:
version: '3.8'
services:
app:
build:
context: .
dockerfile: Dockerfile
args:
HTTP_PROXY: http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
HTTPS_PROXY: http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
environment:
- HTTP_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
- HTTPS_PROXY=http://${PROXY_USER}:${PROXY_PASS}@${PROXY_HOST}:${PROXY_PORT}
Какой тип прокси выбрать для Docker
Выбор типа прокси зависит от задачи. Для Docker-окружений актуальны три основных типа, у каждого — своя ниша:
| Тип прокси | Скорость | Анонимность | Лучший сценарий для Docker |
|---|---|---|---|
| Датацентр | ⚡ Высокая | Средняя | Pull образов, CI/CD пайплайны, обход rate limiting Docker Hub |
| Резидентные | 🔄 Средняя | Высокая | Парсинг сайтов с защитой, API с гео-ограничениями, тестирование |
| Мобильные | 🔄 Средняя | Максимальная | Приложения, работающие с мобильными API (Instagram, TikTok) |
Для pull образов и CI/CD оптимальны прокси дата-центров — они дают максимальную скорость при скачивании больших образов (несколько гигабайт) и стабильное соединение для долгих сборок.
Для контейнеров-парсеров, которым нужно обходить защиту сайтов и выглядеть как обычный пользователь, лучше подходят резидентные прокси. Они имеют IP реальных домашних пользователей, что снижает вероятность блокировки даже при интенсивных запросах.
Для приложений, работающих с мобильными платформами — Instagram Graph API, TikTok API, мобильными версиями сервисов — стоит рассмотреть мобильные прокси. Они используют IP операторов сотовой связи и вызывают минимум подозрений у антибот-систем.
Протоколы: HTTP vs SOCKS5
Docker daemon поддерживает только HTTP/HTTPS прокси — SOCKS5 для pull образов не работает. Если у вас SOCKS5 прокси и нужно скачивать образы, придётся использовать локальный конвертер типа privoxy или microsocks, который будет принимать HTTP и проксировать через SOCKS5.
Для runtime контейнеров ситуация лучше: большинство HTTP-библиотек поддерживают SOCKS5 напрямую через переменную ALL_PROXY=socks5://....
Частые ошибки и как их исправить
Разберём самые распространённые проблемы, с которыми сталкиваются при настройке прокси в Docker:
Ошибка 1: Прокси настроен на хосте, но Docker его не видит
Симптом:
Error response from daemon: Get "https://registry-1.docker.io/v2/": dial tcp: connection refused
Причина: Docker daemon запускается как системный сервис и не наследует переменные окружения текущего пользователя, даже если вы установили export HTTPS_PROXY=... в терминале.
Решение: Настройте прокси через systemd override (способ 1 из раздела выше). Обязательно выполните systemctl daemon-reload && systemctl restart docker.
Ошибка 2: apt-get / pip / npm не работают при сборке
Симптом:
Err:1 http://archive.ubuntu.com/ubuntu focal InRelease
Could not connect to archive.ubuntu.com:80
Причина: Настройка прокси для демона не распространяется автоматически на команды внутри RUN в Dockerfile. Это два разных контекста.
Решение: Передавайте прокси через --build-arg или используйте ~/.docker/config.json с секцией proxies (Docker 23.0+, автоматически передаёт аргументы при сборке).
Ошибка 3: Внутренние сервисы недоступны через прокси
Симптом:
curl: (7) Failed to connect to internal-service.local port 8080: Connection refused
Причина: Прокси пытается проксировать запросы к внутренним адресам, которые недоступны снаружи.
Решение: Добавьте все внутренние домены и подсети в NO_PROXY. Для корпоративных сетей типичный список: localhost,127.0.0.1,::1,.internal,.local,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16
Ошибка 4: Аутентификация прокси не работает — специальные символы в пароле
Если пароль содержит специальные символы (@, #, %), URL парсится некорректно.
Решение: Закодируйте пароль в URL-encoding. Например, p@ss#word → p%40ss%23word. В Python можно быстро получить encoded-версию:
python3 -c "from urllib.parse import quote; print(quote('p@ss#word', safe=''))"
# Вывод: p%40ss%23word
Ошибка 5: Прокси работает, но SSL-сертификат не проверяется
Корпоративные прокси часто выполняют SSL-инспекцию (MITM) и подменяют сертификаты. Docker при этом выдаёт ошибку:
x509: certificate signed by unknown authority
Решение: Добавьте корпоративный корневой сертификат в доверенные на хосте и перезапустите Docker. На Ubuntu/Debian:
sudo cp corporate-ca.crt /usr/local/share/ca-certificates/ sudo update-ca-certificates sudo systemctl restart docker
Ошибка 6: Kubernetes / Docker Swarm — прокси не работает на всех нодах
В кластерных средах нужно настраивать прокси на каждой ноде отдельно. Для Kubernetes дополнительно нужно настроить переменные окружения в манифестах подов или через ConfigMap. Автоматизировать это можно через Ansible или другие инструменты конфигурационного управления.
Заключение
Настройка прокси в Docker — это не одна настройка, а три независимых уровня: демон для pull образов, build-time для сборки и runtime для работающих контейнеров. Разобравшись с этой архитектурой, вы сможете гибко управлять тем, какой трафик идёт через прокси, а какой — напрямую.
Краткий чек-лист правильной настройки:
- ✅ Для pull образов — настройте systemd override с переменными HTTP_PROXY и HTTPS_PROXY
- ✅ Для сборки — передавайте прокси через
--build-argили~/.docker/config.json - ✅ Для runtime — используйте переменные окружения через
-eили--env-file - ✅ Всегда настраивайте NO_PROXY для внутренних адресов
- ✅ Не храните пароли прокси в Dockerfile — используйте build-args и .env файлы
- ✅ Для CI/CD пайплайнов выбирайте быстрые прокси дата-центров
- ✅ Для контейнеров-парсеров — резидентные или мобильные прокси
Если ваши контейнерные приложения делают запросы к внешним сервисам, парсят данные или работают с API, которые имеют гео-ограничения, рекомендуем использовать резидентные прокси — они обеспечивают высокий уровень доверия со стороны сервисов и минимальный риск блокировок даже при интенсивной работе.