Как мы уже знаем любой контейнер при создании использует какой-то образ. И образы эти где-то нужно хранить, по умолчанию образы хранятся в DockerHub. Но рано или поздно вы всё равно столкнётесь с тем что придётся использовать локальный репозиторий образов Docker, т.е. репозиторий размещённый только в вашей локальной сети. На то много причин начиная с банальной безопасности заканчивая простой экономией трафика и скоростью создания контейнеров.
На что стоит обратить внимание при создании своего локального репозитория:
- Всегда используйте ssl для прода. По умолчанию Docker всегда будет пытаться скачать образы с репозитория используя протокол
https. Но эта статья носит характер лишь ознакомления с локальным репозиторием, поэтому здесь я буду использоватьhttp. - Удаление старых неиспользуемых образов. Если у вас бывают ситуации что в день происходит сборка или скачивание образов не один раз, то через какое-то время образы могут занимать очень много дискового пространства. Поэтому если эти образы более не используются рекомендуется их удалять.
Вариант 1️⃣: Используем образ registry
Суть идеи в том, что создаётся контейнер, внутри которого репозиторий. Далее вы выкачиваете (pull) образ с DockerHub или любого другого репозитория себе на локальное устройство, можно даже импортировать образ из архива локально. Главное, чтобы локально у вас был тот самый образ, который вы хотите разместить в своём репозитории. После того как образ скачан можно его отправить (push) в локальный репозиторий.
1️⃣ Разворачиваем сам контейнер
Для создания локального репозитория будем использовать образ registry. Но так как это пример я не буду сейчас тонко настраивать этот репозиторий. Я лишь покажу что можно использовать для хранения образов локально и поэтому сейчас не буду настраивать https. Если вдруг это вам необходимо напишите об этом в комментарии.
mkdir /opt/docker-registry
docker run -d -p 5000:5000 -v /opt/docker-registry:/var/lib/registry --name my-registry registry:2
docker ps
b5d2f73cb0aa registry "/entrypoint.sh /etc…" 39 seconds ago Up 38 seconds 0.0.0.0:5000->5000/tcp, [::]:5000->5000/tcp my-registry
2️⃣Скачиваем образ
Для примера я загружу образ nginx:1.29.4, и после загрузки проверю что локально на моём устройстве он присутствует.
docker pull nginx:1.29.4
docker image ls nginx
nginx:1.29.4 4af177a024eb 161MB 0B
3️⃣Тэгируем скачанный образ
Добавляем новый тэг для загруженного образа. Дело в том, что если вы используете любой другой репозиторий, не DockerHub то и наименование вашего образа должно содержать dns имя этого репозитория, ну или ip-адресс если нет dns имени. Для примера у того же Microsoft есть свой публичный репозиторий и если мы хотим скачать что-то оттуда то имя образа уже будет содержать не только привычное нам имя приложения (nginx) а еще и полный путь образа (mcr.microsoft.com/dotnet/framework/runtime), т.е. при тэгировании образа для репозитория вы должны придерживаться следующему наименованию [HOST[:PORT]/][NAMESPACE/]REPOSITORY[:TAG].
docker tag nginx:1.29.4 172.31.144.9:5000/nginx:1.29.4
docker image ls | grep nginx
172.31.144.9:5000/nginx:1.29.4 4af177a024eb 161MB 0B
Во многих примерах вы можете встретить записи localhost/127.0.0.1 но я рекомендую прописывать сразу локальный ip-адресс хоста, тем более если репозиторий вы развернул на другом устройстве в сети, а push/pull делаете с другого.
4️⃣Загрузка образа в локальный репозиторий
Теперь самое простое, так как по умолчанию нет никакой авторизации для загрузки образа в репозиторий созданный из образа registry мы просто прописываем push.
docker push 172.31.144.9:5000/nginx:1.29.4
Get "https://172.31.144.9:5000/v2/": http: server gave HTTP response to HTTPS client
Но при попытке сделать push я получаю ошибку, потому что по умолчанию Docker пытается использовать https. Для того чтобы он не пытался это делать при обращении к 172.31.144.9, нужно прописать этот ip-адресс в файле /etc/docker/daemon.json.
⚠️ Внимание: Перезапуск Docker daemon остановит все запущенные контейнеры, включая my-registry. После перезапуска нужно запустить контейнер заново.
sudo vim /etc/docker/daemon.json
{
"insecure-registries": ["172.31.144.9:5000"]
}
sudo systemctl restart docker
docker info
docker start my-registry
docker push 172.31.144.9:5000/nginx:1.29.4
1.29.4: digest: sha256:a6dd519f4cc2f69a8f049f35b56aec2e30b7ddfedee12976c9e289c07b421804 size: 1778
Так как я использовал volume при создании контейнера, то и все файлы загружаемые в репозиторий теперь должны быть в локальной директории хоста.
sudo tree /opt/docker-registry/docker/registry/v2/repositories/nginx/_manifests
/opt/docker-registry/docker/registry/v2/repositories/
├── _manifests
│ ├── revisions
│ │ └── sha256
│ │ └── a6dd519f4cc2f69a8f049f35b56aec2e30b7ddfedee12976c9e289c07b421804
│ │ └── link
│ └── tags
│ └── 1.29.4
│ ├── current
│ │ └── link
│ └── index
│ └── sha256
│ └── a6dd519f4cc2f69a8f049f35b56aec2e30b7ddfedee12976c9e289c07b421804
│ └── link
5️⃣Скачиваем образ уже с локального репозитория
Теперь, когда я захочу загрузить образ не с репозитория DockerHub а с локального репозитория я также выполняя команду docker pull указываю полный путь образа. Но опять же не забываем добавить исключение в daemon.json.
docker pull 172.31.144.9:5000/nginx:1.29.4
6️⃣Создаём кэширующий репозиторий
И теперь представим ситуацию что вдень нужно загрузить около 5-10 образов и каждый раз так выкачивать образы с DockerHub в локальный репозиторий делать вручную проблематично. Поэтому проще сделать кэширующий registry.
sudo vim /opt/docker-registry/config.yml
version: 0.1
log:
level: info
storage:
filesystem:
rootdirectory: /var/lib/registry
http:
addr: :5000
proxy:
remoteurl: https://registry-1.docker.io
docker rm -f my-registry
docker run -d \
--name registry-mirror \
-p 5000:5000 \
-v /opt/docker-registry/config.yml:/etc/docker/registry/config.yml \
-v /opt/docker-registry:/var/lib/registry \
registry:2
Теперь можно делать pull напрямую через registry, он сам перенаправит запрос на https://registry-1.docker.io и выкачает образ себе, после чего отдаст нам. И если при другой компьютер в сети сделает тот же самый pull то registry сперва проверит есть ли какие-то обновления в образе на самом DockerHub и если их нет он отдаст образ, который есть у него локально. Т.е. не будет постоянно заново выкачивать образы при каждом обращении.
docker pull 172.31.144.9:5000/library/nginx:1.28.1-alpine-otel
sudo find /opt/docker-registry/ -name 1.28.1-alpine-otel
/opt/docker-registry/docker/registry/v2/repositories/library/nginx/_manifests/tags/1.28.1-alpine-otel
Также чтобы постоянно не писать полный путь до репозитория (172.31.144.9:5000/library/) при выполнении команды docker pull можно прописать зеркало в файле /etc/docker/daemon.json.
sudo vim /etc/docker/daemon.json
{
"registry-mirrors": ["http://172.31.144.9:5000"]
}
sudo systemctl restart docker
docker pull library/nginx:1.28-alpine3.23
Вариант 2️⃣: Используем nexus sonatype
Я рекомендую использовать именно этот вариант, только держите в уме что здесь я просто обозреваю продукт и настраиваю именно для демонстрации, а не продакшена. Опять же если будет интересно подробная настройка пишите в комментариях.
Для начала создаём контейнер из образа sonatype/nexus3. После того как контейнер поднимется можно переходить в браузере по ссылке http://172.31.144.9:8081/.
docker run -d -p 8081:8081 -v nexus-data:/nexus-data --name nexus sonatype/nexus3
При первом входе необходимо вбить автоматически сгенерированный пароль и после поменять его на свой. имя пользователя admin.
docker exec -it nexus bash
cat /nexus-data/admin.password
25689260-a833-4193-adf5-1b210be3ab30
Далее переходим к настройке, наша основная цель - это создание прокси репозитория для DockerHub и включение возможности анонимного docker pull.
- Переходим по ссылке и создаём прокси репозиторий для Docker.

- Не забываем включить
Enable anonymous access. - Дальше идём http://172.31.144.9:8081/#admin/security/realms и делаем так.

Теперь всё готово для использования только что созданного локального репозитория и магия тут в том, что не нужно ничего выкачивать с DockerHub я напрямую обращусь к локальному репозиторию дальше сам Nexus пойдёт на DockerHub и скачает образ, разместит его у себя и уже после отдаст мне образ с локального репозитория. Т.е. вместо 3 запросов как в примере ранее тут достаточно выполнить один запрос. При этом если образ на DockerHub не менялся, то и при последующем запросе сам Nexus не будет его скачивать заново.
docker pull 172.31.144.9:8081/dockerhub-proxy/nginx:1.28.1-alpine
Если же вам нужно загружать свои образы в репозиторий то необходимо создай другой docker репозиторий (hosted).


Комментарии