Ночью 30.05.2024, Docker Hub заблокировал доступ для пользователей из России. Данная блокировка осуществляется по Geo IP. Если Вы пришли на работу и видите, что ваш портал положили Мидлы, а не Джуны, повод призадуматься, что проблема действительно немного странная и стоит капнуть чуть глубже.

Так, если вы еще месяц назад создавали микросервисную архитектуру, все клали в Docker-контейнеры, то теперь вы можете встретиться с особенностью, что все это вроде еще и запускается командами вида docker-compose up или docker up, но build сделать не получается.

docker build - < Dockerfile

Кстати, а ведь раньше поднимали на Хабре тему "Docker мертв") Накаркали. Но прожил чуть дольше, чем планировали.

Вот как еще может выглядеть ошибка при попытке выкачать изменения docker pull alpine:3.17:

~> docker pull alpine:3.17
Error response from daemon: pull access denied for alpine, repository does not exist or may require 'docker login': denied: <html><body><h1>403 Forbidden</h1>
Since Docker is a US company, we must comply with US export control regulations. In an effort to comply with these, we now block all IP addresses that are located in Cuba, Iran, North Korea, Republic of Crimea, Sudan, and Syria. If you are not in one of these cities, countries, or regions and are blocked, please reach out to https://hub.docker.com/support/contact/
</body></html>

Такой ответ сейчас получают пользователи из России. Что можно с этим сделать?

1. Зеркала

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

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

Быстро подсуетились вот эти ребята: https://huecker.io/use.html#config-path

Операционная система

Путь к файлу конфигурации

Linux, regular setup

/etc/docker/daemon.json

Linux, rootless mode

~/.config/docker/daemon.json

macOS

~/.docker/daemon.json

OrbStack

Settings -> Docker -> Advanced engine config

Windows

C:\ProgramData\docker\config\daemon.json

Docker Desktop

Preferences -> Docker engine

Если Docker устанавливался через Snap:

/var/snap/docker/current/etc/docker/daemon.json

или

/var/snap/docker/current/config/daemon.json

В файле конфигурации нужно добавить

{   "registry-mirrors": ["https://huecker.io"] }

После этого выполнить:

systemctl restart docker

Откроем файл, выполнив команду, указанную выше, например: sudo nano /etc/docker/daemon.json. Если у вас открылся пустой редактор, значит файла у вас не было и после сохранения, он появится.

Точно так же как и в предыдущем пункте, необходимо в JSON добавить ключ со списком. Если у вас файла не было, то вставляем следующее:

        {

  "registry-mirrors": [

        "https://mirror.gcr.io",

        "https://daocloud.io",

        "https://c.163.com",

        "https://registry.docker-cn.com"

  ]

}   

Если у вас файл конфигурации был, то добавьте новый блок.

Сохраняем файл сочетанием клавиш CTRL+S и закрываем CTRL+X.

После этого необходимо перезапустить службу Docker, выполнив следующую 

команду: sudo systemctl restart docker.

Что касается зеркал в целом. Вам всё равно придётся идти в https://hub.docker.com/ чтобы посмотреть имя образа, теги, переменные и т.д.

Публичные зеркала

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

Если хотите использовать публичные зеркала, то используйте от компаний, которым важна репутация. Сейчас это:

  • https://mirror.gcr.io - Гугл. Он и так знает уже всё, что можно было о вас знать. Теперь ещё узнает, что вы используйте полный образ debian, а не slim версию.

  • https://dockerhub.timeweb.cloud/ - Зеркало от TimeWeb. Компания не маленькая и не новая.

Думаю, скоро подобное поднимут многие компании.

Своё зеркало

Компания Docker уже очень давно предоставляет образ registry, с помощью которого любой желающий может поднять своё зеркало. В своей компании разумнее хранить исходники у себя же, и обновлять их время от времени. Знакома ситуация, когда работодатель или грантодатель удивляются, что у Вас действительно отечественное ПО? Так вот, представьте, что потеряется хотя бы одна библиотека... В России... Насовсем. Возможно, будет другая версия, но не та, с которой Вы работали. Это, разумеется, боль. Поэтому блокировка Docker hub – это повод развивать свою систему ресурсов.

Логика простая:

Вам нужен образ, например, alpine:3.17, вы просите его у зеркала, зеркало проверяет свой кэш. Если образ у него уже есть, отдаёт вам, если нет - качает и потом отдаёт вам.

Соответственно, чтоб зеркало могло само качать образы, его IP-адрес не должен быть в списках бана у Docker. Скорее всего, ему нужно находиться вне РФ, но не всегда. Проверить, что образы будут выкачиваться на виртуалке без установленного докера:

curl -I https://hub.docker.com/

403 - VPS не подходит

200 - подходит

Ниже приведем самый простой вариант поднятия зеркала через compose.

Без аутентификации через свое "публичное" зеркало

Публичное зеркало могут использовать другие люди, если узнают о его существовании. В результате будет лишняя нагрузка на канал и занятое место на диске. Но плюс в том, что без аутентификации использовать зеркало намного проще.

Ограничение возможно, например, на уровне firewall по подсетям.

Для подтягивания сертификатов Let’s Encrypt предлагаю traefik, весь его конфиг помещается в compose файл. Вся конфигурация registry хранится в одном файле и легко перетаскивается в случае чего.

$Domain – домен для вашего зеркала. Не используйте IP-адрес. Без шифрования таскать образы через магистраль идея плохая. И ещё придётся подкручивать докер, чтоб не ругался.

$EMAIL - Для Let’s Encrypt. Отправляет письма с предупреждениями, не особо полезные.

Объяснятьservices:
mirror:
container_name: "mirror"
image: registry:2.8.3
volumes:
- ./data/:/var/lib/registry
environment:
- REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io
- REGISTRY_HTTP_ADDR=0.0.0.0:80
- REGISTRY_STORAGE_DELETE_ENABLED=true
restart: always
labels:
- "traefik.enable=true"
- "traefik.http.routers.mirror.rule=Host($DOMAIN)"
- "traefik.http.routers.mirror.entrypoints=websecure"
- "traefik.http.routers.mirror.tls.certresolver=myresolver"
- "traefik.http.services.mirror.loadbalancer.server.port=80"
traefik:
image: "traefik:v2.11.3"
container_name: "traefik"
command:
- "--log.level=DEBUG"
  - "--providers.docker=true"
  - "--providers.docker.exposedbydefault=false"
  - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
  - "--certificatesresolvers.myresolver.acme.email=с"
  - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
  - "--entrypoints.web.address=:80"
  - "--entrypoints.websecure.address=:443"
  - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
  - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
ports:
  - "80:80"
  - "443:443"
volumes:
  - "/var/run/docker.sock:/var/run/docker.sock:ro"
  - "./letsencrypt:/letsencrypt"
restart: always

Прописываете А-запись для IP-адреса, помещаете compose-файл куда-нибудь на сервере. Затем docker compose up -d и всё, у вас есть своё "публичное" зеркало.

С аутентификацией

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

Registry сам умеет в Basic auth, но там нужно создавать htpasswd файл и, соответственно, отдельный bind/volume под него. Описываю самый простой вариант, поэтому за аутентификацию будет также отвечать traefik.

У traefik свой прикол: символы $ в хэше нужно экранировать ещё одним.

Хэш для пароля генерируется стандартно

htpasswd -bBn user password

Либо сразу с фиксом $

echo $(htpasswd -bBn user password) | sed -e s/\\" class="formula inline">/\/g

Полученный hash вставляется в label traefik.http.middlewares.mirror-auth.basicauth.users.

Задать можем и несколько пользователей через запятую

services:
  mirror:
    container_name: "mirror"
    image: registry:2.8.3
    volumes:
      - ./data:/var/lib/registry
      - ./auth/:/auth
    environment:
      - REGISTRY_PROXY_REMOTEURL=https://registry-1.docker.io
      - REGISTRY_HTTP_ADDR=0.0.0.0:80
      - REGISTRY_STORAGE_DELETE_ENABLED=true
    restart: always
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.mirror.rule=Host(`$DOMAIN`)"
      - "traefik.http.routers.mirror.entrypoints=websecure"
      - "traefik.http.routers.mirror.tls.certresolver=myresolver"
      - "traefik.http.services.mirror.loadbalancer.server.port=80"
      - "traefik.http.routers.mirror.middlewares=mirror-auth"
      - "traefik.http.middlewares.mirror-auth.basicauth.users=user:$$2y$$05$$D/a0AJqMRN/18eu9hD2Oxe5b9BJDgUvYaXQYSH.aQpjqhhYqswXPO"

  traefik:
    image: "traefik:v2.11.3"
    container_name: "traefik"
    command:
#      - "--log.level=DEBUG"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=$EMAIL"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entrypoint.permanent=true"
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./letsencrypt:/letsencrypt"
    restart: always

Таким относительно простым путем получили "приватное" зеркало.

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

Периодически чистить слои без тегов можно так

docker exec mirror bin/registry garbage-collect /etc/docker/registry/config.yml -m

Использование зеркал на "клиентах"

Может не запуститься с полпинка. Пытайтесь.

Linux

Для простого сервера с докером без kubernetes

Используем публичное зеркало

Добавляете в файл ~/.docker/config.json. Возможно, его будет нужно создать.

"registry-mirrors": [
    "https://$DOMAIN"
  ]
{
  "registry-mirrors": [
    "https://$DOMAIN"
  ]
}

Далее нужно выполнить перезагрузку процессов Docker

systemctl restart docker.service

Проверка, что зеркало подцепилось

docker info

В конце должно быть

 Registry Mirrors:
https://mirror-example.com/

Теперь при docker pull alpine сервис docker будет ходить в зеркало. Это можно отследить в логах поднятого ранее registry mirror.

На сервере зеркала можно также добавить в /etc/docker/daemon.json. Возможно, его будет нужно создать.

Используем приватное зеркало

У зеркалом с аутентификацией всё немного (много) по-другому.

docker login $DOMAIN

Вводите логин и пароль. И он сам создаёт запись в ~/.docker/config.json.

Но при docker pull alpine качать он будет не с зеркала. Ха-ха. Думали всё так просто?

Но зато, если явно указать путь через зеркало docker pull my-mirror.com/library/alpine, он пойдёт просить образ у зеркала.

Проблема давняя, вот issue.

Есть workround, на ваш страх и риск. Описан он в этой же issue.

После чего:

docker login mirror-example.com

в ~/.docker/config.json получаете следующий json:

{
	"auths": {
		"mirror-example.com": {
			"auth": "dXNlcjp1c2Vy"
		}
	}
}

Нужно добавить ещё один registry с url https://index.docker.io/v1/ и таким же паролем:

{
  "auths": {
          "mirror-example.com": {
                  "auth": "dXNlcjp1c2Vy"
          },
          "https://index.docker.io/v1/": {
                  "auth": "dXNlcjp1c2Vy"
          }
  }
}

Docker Desktop. Windows, MacOS

Для решения проблемы необходимо прописать дополнительные зеркала в
конфигурационный файл.
Откройте приложение Docker Desktop и перейдите в настройки. В нём выбираем
Docker Engine.
В окне будет редактор с предзаписанной конфигурацией в виде JSON. Необходимо
добавить новый ключ со списком зеркал:

"registry-mirrors": [

"https://mirror.gcr.io",

"https://daocloud.io",

"https://c.163.com",

"https://registry.docker-cn.com"
]

После чего нажимаем кнопку Apply & restart. После перезагрузки всё будет работать.

Теперь чуть подробнее. У Docker Desktop зеркало можно добавить через Settings - Docker engine. Синтаксис такой же. Пример с публичным зеркалом:

{
  "builder": {
    "gc": {
      "defaultKeepStorage": "20GB",
      "enabled": true
    }
  },
  "experimental": false,
  "registry-mirrors": [
    "https://mirror-example.com"
  ]
}

registry-mirrors добавлено после существующих настроек.

На винде можно, не лазя в GUI, поправить файл C:\Users$USER.docker\daemon.json.

В MacOS вроде в ~/.docker/daemon.json.

Но нужны ли зеркала в Docker Desktop? Скорее нет, чем да.

2. Прокси

Зеркала позволяют только пулить через себя. Просмотр образов, push и прочее через зеркало не получится сделать.

Однако Докер умеет ходить к своему же hub через прокси. Компания Docker продолжает:

Не осилил mirror? Пушить ещё надо? Ну вот через прокси тогда сходи, вот настройка. И пушить заодно сможешь ещё.

Docker Proxy. Ознакомиться с тем, как можно использовать такой прокси от Huecker.io, можно здесь: https://huecker.io/use.html#config-path

Только не рекоммендуется для Prod-версии.

Linux

Тут есть некоторая путаница. Для того чтобы пулить образы через прокси, нужно делать systemd службу, а не править ~/.docker/daemon.json.

Переменные в daemon.json применяются при сборке и в запущенных контейнерах. Они не используются для docker cli. В документации это прописано:

These settings are used to configure proxy environment variables for containers only, and not used as proxy settings for the Docker CLI or the Docker Engine itself.

Вам потребуется рабочий HTTP или SOCKS прокси. Продублирую инструкцию по настройке systemd с официального сайта:

Создаём каталог

sudo mkdir -p /etc/systemd/system/docker.service.d

Открываем /etc/systemd/system/docker.service.d/http-proxy.conf и вставляем

[Service]
Environment="HTTP_PROXY=http://proxy-example.com:3128"
Environment="HTTPS_PROXY=http://proxy-example.com:3128"

Говорим systemd, что надо найти новый сервис, рестартуем docker

sudo systemctl daemon-reload
sudo systemctl restart docker

Проверяем, что применилось. Должны показаться Environments

~> sudo systemctl show --property=Environment docker
Environment=HTTP_PROXY=http://proxy-example.com:3128 HTTPS_PROXY=http://proxy-example.com:3128

Теперь docker pull выкачивает образ через прокси

~> docker pull alpine:3.16
3.16: Pulling from library/alpine
a88dc8b54e91: Pull complete 
Digest: sha256:452e7292acee0ee16c332324d7de05fa2c99f9994ecc9f0779c602916a672ae4
Status: Downloaded newer image for alpine:3.16
docker.io/library/alpine:3.16

Docker desktop

Тут всё совсем просто.Идём в Settings - Resourses - Proxies

И в HTTP и в HTTPS вставляем одно и то же значение.

http://proxy-example.com:3128

Теперь и поиск работает

Откуда взять прокси

Это уже другая тема, вкратце с чем у меня успешно завелось:

  1. Образ 3proxy от @paramtamtam:

docker run --rm \
    -p "3128:3128/tcp" \
    -p "1080:1080/tcp" \
    ghcr.io/tarampampam/3proxy:latest

Самый простой пример для тестов без аутентификации. Использовать недолго и с осторожностью.

  1. Прикручиваем логин и пароль, помещаем в compose

services:
  3proxy:
    image: "ghcr.io/tarampampam/3proxy:1.9.1"
    container_name: "3proxy"
    ports:
      - "3128:3128"
      - "1080:1080"
    environment:
      - PROXY_LOGIN=user
      - PROXY_PASSWORD=password
    restart: always

Теперь есть свои простые HTTP (3128 порт) и SOCKS (1080 порт) прокси.

С паролем переменные выглядят так

[Service]
Environment="HTTP_PROXY=http://user:password@proxy-example.com:3128"
Environment="HTTPS_PROXY=http://user:password@proxy-example.com:3128"
  1. Приложение Nekobox подключенное к серверу по Shadowsocks2022 (либо любые другие из семейства Xray). Обычный режим прокси, без tun и "системного прокси". По дефолту слушает порт 2080.

Вбиваем везде

http://localhost:2080

3. Маршрутизация через роутер

Docker Hub не первый и не последний. Самое удобное для дома/офиса - это маршрутизация в туннель/прокси по доменам на роутере. Никаких клиентов на девайсах, всем занимается роутер.

Давайте пробовать различные варианты и смотреть, что получится. Ведь полегло в эти первые дни немало Российских сайтов, где Docker-контейнеризация являлась до 30 мая именно преимуществом. А сейчас резко стала пока еще не недостатком, но уже болью, которую приходится преодолевать.

Можем отметить, что на портале Genervis был выбран микросервисный подход к построению архитектуры, но Docker-контейнеризация еще совсем недавно казалась максимально удобным решением, под что затачивалось приложение. Теперь подход, вероятно, не изменится, просто наши проекты зарубежный народ делает все сильнее и сильнее. Будем прорываться. Возможно, что будет российский аналог Docker, но пока большой вопрос, кто и когда его выкатит. Пока проще и разумнее, кажется, использовать сам Docker с возможностью обхода блокировки и со своими корпоративными зеркалами.

Если по-прежнему не помогает совсем ничего:

В Dockerfile надо напрямую указать, откуда брать образ:

huecker.io/tiangolo/uwsgi-nginx-flask:python3.10

Комментарии (22)


  1. exTvr
    01.06.2024 12:39
    +6


  1. AndronNSK
    01.06.2024 12:39
    +3

    "капнуть чуть глубже" валерианы экстракт будем капать или валидол на сахар?


    1. exTvr
      01.06.2024 12:39
      +5

      капнуть

      Водички на Хабр.


      1. Genervis Автор
        01.06.2024 12:39

        Уж лучше водочки


        1. exTvr
          01.06.2024 12:39

          Водочка - это не сюда, а внутрь!

          Тем паче что суббота и погода шепчет:))

          Эт, блин, хотел карму вам поправить - а никак, там мой плюс уже есть.


        1. AndronNSK
          01.06.2024 12:39

          Джэк в колу норм?


          1. exTvr
            01.06.2024 12:39
            +1

            Извращение.


  1. b1rdex
    01.06.2024 12:39

    После этого необходимо перезапустить службу Docker, выполнив следующую команду: sudo systemctl restart docker.

    Для обновления без остановки контейнеров лучше использовать sudo systemctl reload docker


  1. opusmode
    01.06.2024 12:39
    +2

    1. А почему 3proxy? Он же устарел, не обновляется, да и вообще ужасен и кусок известно чего. Если надо быстро и эффективно, я бы взял dumbproxy - реально час работы, чтобы разобраться и поднять https прокси.

    2. А зачем так сложно? Зачем лезть в систему и перенаправлять весь траффик? Можно ведь слать на прокси только демона https://docs.docker.com/network/proxy/


  1. crawlingroof
    01.06.2024 12:39
    +1

    Мдя, гр "Ленинград — как же хорошо мы плохо жили"
    а не вот это вот все...


  1. subvillion
    01.06.2024 12:39
    +19

    Плагиат до совпадения скринов https://habr.com/ru/articles/818565/

    А потом дружно будем утверждать, что хабр уже не торт...


    1. Genervis Автор
      01.06.2024 12:39

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


      1. fshp
        01.06.2024 12:39
        +3

        Вы бы могли дополнить ту статью, как это сделали другие.


  1. alnidok
    01.06.2024 12:39

    А кто-то уже подсуетился - huecker.io :)


    1. Genervis Автор
      01.06.2024 12:39

      Учтено, спасибо!