OSEN-НИЙ SAAALEСкидка 50% на виртуальный хостинг и VDS
до 30.11.2025 Подробнее
Выберите продукт

Docker Registry proxy cache на VDS: ускоряем CI и экономим трафик

Разбираем практическую схему: свой docker registry proxy cache на отдельном VDS, который прозрачно проксирует Docker Hub и другие публичные реестры контейнеров. Локальный кеш ускоряет CI, экономит трафик и снижает зависимость от внешних сервисов. В статье — пошаговая настройка, конфигурации, нюансы безопасности и интеграция с GitLab CI и GitHub Actions.
Docker Registry proxy cache на VDS: ускоряем CI и экономим трафик

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

Решение — свой docker registry proxy на отдельном облачном VDS: локальный кеш, который прозрачно проксирует обращения к Docker Hub (и не только), хранит образы рядом с вашими раннерами CI и резко ускоряет повторные сборки. Пара бонусов: уменьшение трафика и меньшая зависимость от внешних сервисов.

Что такое docker registry proxy cache и зачем он CI

По сути это Docker Registry в режиме pull-through cache. Когда CI или разработчик делает docker pull образа из внешнего реестра, запрос летит на ваш VDS:

  • если образ уже есть в локальном кеше — он отдается мгновенно, без запроса наружу;
  • если нет — proxy скачивает его из внешнего реестра, складывает в кеш и отдаёт клиенту.

Для CI это превращается в ощутимый выигрыш. Большинство пайплайнов используют ограниченный набор базовых образов: python:3.12-slim, node:22-alpine, golang:1.23, alpine и т.п. Первый прогон подождёт, зато все последующие сборки будут забирать их из локального кеша за миллисекунды.

Ключевые плюсы для CI/CD и DevOps:

  • Ускорение docker build — меньше времени уходит на network I/O, больше — на реальную работу.
  • Стабильность — если Docker Hub недоступен, а образ есть в кеше, ваши сборки не ломаются.
  • Контроль — можно видеть, какие образы реально используются, и настраивать политики хранения.
  • Экономия трафика — особенно заметна, если CI-раннеры крутятся в другом дата-центре или облаке.

Важно: docker registry proxy cache не заменяет полноценный приватный реестр артефактов (Harbor, GitLab Container Registry и т.д.). Это скорее ускоритель и кэш для внешних базовых образов, а не хранилище ваших собственных релизов.

Выбор режима: простой pull-through cache или комбинированный реестр

Есть несколько базовых вариантов архитектуры.

1. Чистый docker registry proxy для Docker Hub

Самый простой случай: поднимаем официальный registry:2 в режиме proxy к registry-1.docker.io. CI и разработчики ходят только к нему.

Плюсы:

  • минимум настроек — один контейнер и конфиг;
  • простая интеграция с любым CI — достаточно переопределить адрес реестра.

Минусы:

  • работает только как зеркало Docker Hub (и ещё некоторых публичных реестров, если настроить);
  • для приватных образов из других реестров потребуется отдельная конфигурация.

2. Комбинация: приватный реестр + proxy cache

Более продвинутый вариант — единый реестр, который:

  • хранит ваши приватные образы (артефакты CI/CD, релизы);
  • одновременно является proxy cache для Docker Hub и других внешних реестров.

Такую схему удобно реализовывать через продвинутые решения (Harbor, GitLab Container Registry с mirror, Quay и т.п.) или через комбинацию нескольких registry:2 с разными proxy-секциями и reverse-proxy (например, nginx) перед ними.

Преимущество — все docker-операции проходят через единое имя хоста, а CI использует одну точку входа и для базовых образов, и для ваших артефактов.

Схема взаимодействия CI-раннеров и Docker-клиентов с VDS, на котором запущен docker registry proxy cache

Требования к VDS под docker registry proxy

Под docker registry proxy cache лучше выделить отдельный VDS, чтобы не смешивать его нагрузку с приложениями и базами. Это снизит риски деградации продакшена из-за всплесков в CI.

Главные ресурсы, которые будут нагружены:

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

Реальные рекомендации зависят от количества проектов и интенсивности CI, но как ориентир:

  • 2–4 vCPU достаточно для десятков одновременных pulls;
  • 8–16 ГБ RAM дадут запас под кеши и процесс nginx/registry;
  • SSD-диск от 100–200 ГБ чтобы начать; далее масштабировать по факту.

Отдельно обратите внимание на доступность: если ваш CI крутится в другой облачной зоне, проверьте задержки и полосу канала до VDS. Бессмысленно поднимать кеш, если он физически дальше от раннеров, чем Docker Hub.

Если вы только строите инфраструктуру и выбираете, где разместить registry cache и CI, имеет смысл сразу подбирать тарифы на VDS для docker-инфраструктуры в том же регионе, где находятся раннеры и основные сервисы.

Базовая установка Docker Registry в режиме proxy

Рассмотрим типичный сценарий: на VDS установлен Docker, и мы хотим быстро поднять registry-подписку на Docker Hub.

Создаём конфиг docker registry

Создадим каталог под конфигурацию и данные:

mkdir -p /opt/registry/data
mkdir -p /opt/registry/config

Создадим файл /opt/registry/config/config.yml со следующим содержимым (минимальный пример pull-through cache для Docker Hub):

version: 0.1
log:
  level: info
  fields:
    service: registry
storage:
  filesystem:
    rootdirectory: /var/lib/registry
http:
  addr: :5000
proxy:
  remoteurl: https://registry-1.docker.io
  # Важно: ttl управляет временем жизни кеша метаданных, а не слоёв
  ttl: 168h

Обратите внимание на несколько моментов:

  • storage.filesystem.rootdirectory — путь к каталогу, где будут храниться образы;
  • http.addr — порт, на котором слушает registry (5000 по умолчанию);
  • proxy.remoteurl — адрес внешнего реестра (Docker Hub V2 API).

Каталог /var/lib/registry лучше заранее вынести на отдельный диск или раздел с достаточным запасом места.

Запускаем контейнер registry

Запускаем docker registry с монтированием конфигурации и данных:

docker run -d --name registry-proxy -p 5000:5000 -v /opt/registry/config:/etc/docker/registry -v /opt/registry/data:/var/lib/registry registry:2

Проверяем логи:

docker logs -f registry-proxy

Если всё поднялось без ошибок, можно делать первый тестовый pull.

Проверка работы кеша

На той же машине или с другого хоста (который видит VDS по сети) попробуем скачать образ через proxy:

docker pull localhost:5000/library/alpine:latest

Первый pull займёт время — registry скачает все слои с Docker Hub. Логи покажут обращения к registry-1.docker.io. Второй pull того же образа (даже с другого хоста, но через proxy) будет уже быстрым — слои будут отданы из локального кеша.

Добавляем TLS и reverse-proxy (nginx)

В продакшене раздавать docker registry по обычному HTTP на 5000 порту не стоит. Docker-клиент по умолчанию требует HTTPS (кроме localhost), и нам нужно спрятать registry за nginx c TLS.

Базовая схема

Схема проста:

  • nginx слушает 443 порт и принимает запросы от docker-клиентов;
  • nginx терминит TLS и проксирует запросы на localhost:5000, где крутится registry:2;
  • в docker-клиентах мы настраиваем реестр как registry.example.com.

Пример конфигурации nginx для docker registry proxy

Ниже пример простого виртуального хоста nginx (без специфичных для какого-либо провайдера директив):

server {
    listen 443 ssl http2;
    server_name registry.example.com;

    ssl_certificate     /etc/ssl/certs/registry.crt;
    ssl_certificate_key /etc/ssl/private/registry.key;

    client_max_body_size 0;
    chunked_transfer_encoding on;

    location / {
        proxy_pass http://127.0.0.1:5000;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Важно выставить client_max_body_size 0, иначе push больших слоёв будет падать по ограничению размера тела запроса.

Для публичного registry proxy имеет смысл использовать валидные SSL-сертификаты для контейнерных реестров. Самоподписанные сертификаты потребуют настройки доверия на всех раннерах и developer-хостах, проще сразу купить нормальный сертификат — пригодится и для других сервисов.

Интеграция docker registry proxy с CI

Самая интересная часть — как заставить ваши пайплайны использовать локальный кеш. Рассмотрим два популярных варианта: GitLab CI и GitHub Actions. Принципы для других систем (Jenkins, Drone, TeamCity) аналогичны.

GitLab CI

В GitLab CI типичный билд выглядит так:

image: docker:git

services:
  - docker:dind

variables:
  DOCKER_HOST: tcp://docker:2375
  DOCKER_TLS_CERTDIR: ""

build:
  stage: build
  script:
    - docker pull node:22-alpine
    - docker build -t app:latest .

Чтобы использовать docker registry proxy, есть два подхода.

1. Прямое указание реестра в имени образа

Меняем имя образа с:

docker pull node:22-alpine

на:

docker pull registry.example.com/library/node:22-alpine

Проблема в том, что стандартные образы Docker Hub вида node:22-alpine обычно резолвятся как docker.io/library/node:22-alpine. Чтобы proxy корректно работал в режиме pull-through, он ожидает именно такой путь. В простейшем варианте он автоматически подхватит /library/..., но вам придётся следить за путями.

2. Настройка daemon.json на раннерах (предпочтительный)

Лучший вариант — не переписывать все docker pull в CI, а научить сам Docker-демон использовать ваш proxy для конкретных реестров.

На хосте, где крутится Docker (dind или host-docker), в /etc/docker/daemon.json можно настроить registry-mirrors:

{
  "registry-mirrors": [
    "https://registry.example.com"
  ]
}

После перезапуска Docker все обращения к Docker Hub пойдут через ваш mirror. Это самый прозрачный для CI способ: конфигурация меняется на уровне раннера, скрипты в пайплайнах остаются прежними.

В случае GitLab, если вы используете docker:dind, то нужно создать свой образ dind, в который уже включен daemon.json с registry-mirrors, и использовать его вместо стандартного.

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

GitHub Actions

В GitHub Actions часто используют actions, работающие с Docker (docker/build-push-action и др.). Там тоже можно управлять тем, через какой реестр идут pulls.

Если вы запускаете self-hosted runner с Docker в режиме host, настройка аналогична: меняете /etc/docker/daemon.json на самом раннере, добавляя mirror, и перезапускаете Docker.

Пример фрагмента workflow, который будет прозрачно использовать mirror (если он настроен на уровне демона):

jobs:
  build:
    runs-on: self-hosted
    steps:
      - uses: actions/checkout@v4
      - name: Build image
        run: |
          docker pull node:22-alpine
          docker build -t app:latest .

Если же у вас нет доступа к конфигурации Docker-демона (например, GitHub-hosted runners), тогда остаётся либо прямое указание вашего реестра в именах образов, либо использование собственного self-hosted runner в окружении, где вы полностью контролируете Docker.

build cache и влияние registry proxy на скорость сборок

Важно не путать кеш слоёв образов в реестре и build cache Docker/BuildKit, который живёт там, где выполняется сборка.

docker registry proxy cache ускоряет:

  • скачивание базовых образов (FROM ...);
  • получение слоёв при docker pull и частично при docker build, если используются уже существующие слои.

Но основной build cache (кеш слоёв внутри хоста сборки) всё равно находится на CI-раннере. Если runner одноразовый, build cache после каждого джоба теряется, и вам приходится:

  • либо использовать remote cache (BuildKit с --cache-to/--cache-from в реестре);
  • либо завязываться на постоянные runnerы с сохранением локального состояния.

registry proxy здесь помогает тем, что:

  • ускоряет первичную и повторную загрузку базовых образов для кеша;
  • может выступать как хранилище для BuildKit cache (через отдельные теги с type=registry).

Пример использования BuildKit с registry cache

В расширенном сценарии вы можете хранить build cache слоёв в том же docker registry (включая ваш proxy, если он умеет принимать push от BuildKit). Тогда даже одноразовые runnerы будут реже пересобирать всё с нуля.

Пример запуска docker buildx с кешем в реестре:

docker buildx build --cache-to=type=registry,ref=registry.example.com/org/app:buildcache,mode=max --cache-from=type=registry,ref=registry.example.com/org/app:buildcache -t registry.example.com/org/app:latest .

При таком подходе ваш docker registry VDS становится не просто proxy cache, но и репозиторием build cache. Это дополнительно нагружает диск и сеть, но значительно ускоряет повторные сборки в CI, особенно при сложных Dockerfile.

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

CI-пайплайны с ускоренными docker-сборками за счёт локального кеша в docker registry proxy

Очистка и политики хранения кеша

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

Нужно планировать:

  • политику retention (какие образы и как долго храним);
  • регулярную очистку старых и невостребованных слоёв;
  • мониторинг свободного места.

Классический registry:2 не имеет идеального встроенного GC по обращаемости (частоте pull). Есть garbage-collect, но он работает по принципу «удалить все объекты, не привязанные к манифестам» и требует определённой процедуры (остановка или режим readonly).

Типичная стратегия:

  1. Периодически (раз в N недель) останавливать registry.
  2. Запускать registry garbage-collect с конфигом.
  3. Снова запускать registry.

Пример запуска GC (для понимания, как это делается; детали зависят от вашего процесса):

docker stop registry-proxy
registry garbage-collect /etc/docker/registry/config.yml
docker start registry-proxy

Если вам нужна более интеллектуальная политика, обычно рассматривают:

  • переход на продвинутые решения (Harbor, GitLab Registry) с более гибкими политиками retention;
  • или набор своих скриптов, которые анализируют каталоги, смотрят логи и удаляют редко использующиеся образы.

Для общего понимания подходов к TTL и кеш-ключам может быть полезна статья про использование secure-link и времени жизни кэша в nginx: ограничение доступа и управление сроком жизни кеша в nginx.

Безопасность docker registry proxy на VDS

Несмотря на то что proxy cache чаще всего хранит публичные образы, к безопасности стоит отнестись серьёзно: не хочется превратить VDS в открытую свалку образов и потенциальный вектор атаки.

Базовые практики:

  • TLS по умолчанию — не раздавайте registry по HTTP наружу;
  • Firewall — закройте порт 5000 снаружи, оставив доступ только к 443 порту nginx;
  • Аутентификация — если вы планируете отдавать или принимать приватные образы, настройте basic auth или токены;
  • Разделение ролей — CI и разработчикам не обязательно иметь права на push во все репозитории;
  • Обновления — регулярно обновляйте образ registry:2 и базовую ОС на VDS.

Отдельно подумайте об изоляции самого Docker-демона на VDS: не стоит запускать на той же машине потенциально недоверенные контейнеры рядом с registry. Лучше пусть эта нода отвечает только за кеш и, возможно, за сопутствующий reverse-proxy.

Типичные проблемы и отладка

При внедрении docker registry proxy cache в CI чаще всего встречаются следующие проблемы.

1. Docker не использует mirror

Симптом: несмотря на настройки, docker продолжает ходить напрямую на Docker Hub.

Проверяем:

  • правильно ли прописан registry-mirrors в daemon.json;
  • перезапущен ли Docker-демон после изменения конфигурации;
  • нет ли локального кеша DNS, который резолвит старый адрес;
  • смотрим трафик и логи nginx и registry: есть ли вообще запросы с CI.

2. Ошибки авторизации / 401 Unauthorized

Если вы используете proxy не только для публичных, но и для приватных образов, начинаются истории с токенами и basic auth. Классический registry:2 умеет работать с auth backend, но это уже отдельная тема. Для простого proxy к публичному Docker Hub лучше вообще не включать авторизацию.

3. Ошибки push больших образов

Симптом: push валится по таймауту или ошибкам 413 Request Entity Too Large.

Чаще всего виноват nginx:

  • не выставлен или мал client_max_body_size;
  • слишком маленькие proxy_read_timeout/proxy_send_timeout при медленной сети и больших слоях.

Добавьте или проверьте в конфиге:

client_max_body_size 0;
proxy_read_timeout 300s;
proxy_send_timeout 300s;

4. Неожиданный рост диска

Симптом: раздел с /var/lib/registry быстро заполняется.

Проверьте:

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

Итог: когда docker registry proxy cache на VDS действительно нужен

Подведём итоги, в каких случаях имеет смысл заморачиваться с docker registry proxy cache на отдельном VDS и тащить CI через него:

  • у вас много docker-based CI-сборок (десятки–сотни pipelines в день);
  • используется один и тот же набор базовых образов, и каждый раз их скачивание заметно бьёт по времени;
  • есть проблемы с сетевой доступностью Docker Hub (ограничения, нестабильный канал, rate limits);
  • вы хотите централизовать контроль над тем, какие базовые образы реально используются в проектах;
  • вам нужен фундамент для дальнейшего развития: приватный реестр, хранение build cache, сложные политики retention.

Если же CI у вас запускается раз в день для пары проектов и образы небольшие, выгода может быть не так заметна. Но как только команда и количество сервисов растут, локальный docker registry proxy cache становится почти обязательной частью DevOps-инфраструктуры.

Главное — относиться к нему как к полноценному сервису: продуманная конфигурация, мониторинг, политики хранения и безопасность. Тогда он действительно ускорит ваши docker build и разгрузит внешние реестры, а не превратится в «чёрную дыру» дискового пространства.

Поделиться статьей

Вам будет интересно

PHP и Node.js на одном VDS: аккуратный запуск под systemd и cgroup v2 OpenAI Статья написана AI (GPT 5)

PHP и Node.js на одном VDS: аккуратный запуск под systemd и cgroup v2

Разбираем, как на одном VDS безопасно собрать PHP-бэкенд, Node.js-воркеры и WebSocket-сервисы, очереди на RabbitMQ или Redis, наст ...
Composer на VDS: быстрый install через packagist mirror и shm-подход к autoloader OpenAI Статья написана AI (GPT 5)

Composer на VDS: быстрый install через packagist mirror и shm-подход к autoloader

В реальных проектах Composer крутится в каждом CI job и на каждом деплое, а vendor раздувается до сотен мегабайт. На PHP VDS с SSD ...
TCP, HTTPS и API на VDS: как уменьшить latency и настроить sysctl OpenAI Статья написана AI (GPT 5)

TCP, HTTPS и API на VDS: как уменьшить latency и настроить sysctl

Разбираем, из чего складывается задержка API на VDS и как на неё влияют TCP, HTTPS и параметры ядра Linux через sysctl. Покажу, ка ...