“You’re either the one that creates the automation or you’re getting automated.” — Tom Preston-Werner
Не так давно все наблюдали блокировку Docker Hub в РФ, которая длилась с 30 мая по 3 июня. Хотя сейчас Docker Hub вновь доступен, я успел разработать некоторую автоматизацию для переноса всех своих проектов и решил ею поделиться (пускай и очень поздно). В этом посте я расскажу как жить с блокировкой и как быстро перевести текущие проекты, использующие Docker Hub.
1. Доступ к публичным образам
Первое, что нужно сделать, это вернуть возможность делать docker pull
публичных образов (например, docker pull ubuntu:latest
). Для этого будем использовать зеркало от Google: https://mirror.gcr.io/
. Еще есть зеркало от Яндекса cr.yandex/mirror
, но там как будто мало образов (ubuntu:latest
— есть, python:3.12
— нет). Можно каждый раз указывать зеркало, например docker pull mirror.gcr.io/ubuntu:latest
. Но удобнее один раз указать зеркало в настройках Docker и делать docker pull
как обычно: docker pull ubuntu:latest
(зеркало будет использоваться автоматически).
Я написал скрипт, который все сделает за вас. Достаточно лишь передать зеркало первым аргументом. Проверено на Linux Ubuntu и MacOS.
Примечание для MacOS: в коде стоят sleep’ы, чтобы дождаться рестарта Docker. Их можно увеличить при необходимости.
Примечание для Windows: достаточно лишь поправить код, где "$os" == "CYGWIN_NT"
и должно заработать.
Скрипт:
#!/usr/bin/env bash set -e DOCKER_REGISTRY_MIRROR="$1" # utils__str_strip takes input from stdin and strips space characters function utils__str_strip() { cat | tr -d '[:space:]' } # utils__is_true takes single argument and # returns 0 if argument equals "true" (case-insensitive) # returns 1 otherwise function utils__is_true() { bool="$(echo "$1" | tr '[:upper:]' '[:lower:]' | utils__str_strip)" if [ "$bool" == "true" ]; then return 0 fi return 1 } function _get_docker_daemon_config_path() { os="$(uname -s)" local path if [ "$os" == "Linux" ]; then path="/etc/docker/daemon.json" elif [ "$os" == "Darwin" ]; then # path="~/.config/docker/daemon.json" # https://docs.docker.com/config/daemon/ path="$HOME/.docker/daemon.json" elif [ "$os" == "CYGWIN_NT" ] || [ "$os" == "MINGW32_NT" ] || [ "$os" == "MSYS_NT" ]; then # NOTE: not tested # https://docs.docker.com/config/daemon/ path="C:\ProgramData\docker\config\daemon.json" else echo "Error: unsupported operating system" exit 1 fi echo $path } function _restart_docker() { os="$(uname -s)" local path if [ "$os" == "Linux" ]; then sudo systemctl restart docker elif [ "$os" == "Darwin" ]; then pkill 'Docker' || true sleep 3 open -a Docker sleep 3 elif [ "$os" == "CYGWIN_NT" ] || [ "$os" == "MINGW32_NT" ] || [ "$os" == "MSYS_NT" ]; then # NOTE: not tested # https://forums.docker.com/t/restart-docker-service-from-command-line/27331/3 restart-service *docker* else echo "Error: unsupported operating system" exit 1 fi } function dr__has_mirror() { local mirror="$1" sudo docker system info --format json | jq -r ".RegistryConfig.Mirrors | if index(\"${mirror}\") == null then \"false\" else \"true\" end" } function dr__add_mirror() { local mirror="$1" local config_path=$(_get_docker_daemon_config_path) (cat "$config_path" 2>/dev/null || echo "{}") | jq ". + {\"registry-mirrors\": [\"${mirror}\"]}" > /tmp/daemon.json && sudo mv /tmp/daemon.json "$config_path" _restart_docker } function DR_UPDATE_MIRROR() { mirror="$1" if ! utils__is_true $(dr__has_mirror "$mirror"); then echo "Target docker registry mirror ('$mirror') not found" echo "Start configuring docker registry mirror" dr__add_mirror "$mirror" if [ "$(dr__has_mirror "$mirror")" == "false" ]; then echo "Failed to configure docker registry mirror" exit 1; fi; echo "Successfully configured docker registry mirror" else echo "Mirror is already configured (look at "docker system info")" fi } DR_UPDATE_MIRROR "$DOCKER_REGISTRY_MIRROR"
Логика скрипта:
-
Проверить может зеркало уже настроено
-
Если зеркала нет, то обновить конфиг
-
Перезапустить докер
Классический способ запуска
-
Создаем файл со скриптом
update_mirror
-
Обновляем права
chmod +x update_mirror
-
Запускаем
./update_mirror 'https://mirror.gcr.io/'
Способ для самых ленивых 🙂
curl https://gist.githubusercontent.com/Deimvis/c747446725c84cf0731e82d76f7cc67b/raw/709e8fe296121fb1445fca49086ab9359ca5da67/update_mirror.sh | bash -s 'https://mirror.gcr.io/'
Результат
После настройки зеркало будет видно в выводе docker system info
. Появятся строчки:
Registry Mirrors: https://mirror.gcr.io/
Теперь можно пуллить публичные образы как обычно: docker pull ubuntu:latest
2. Загрузка и выгрузка личных образов
Для того, чтобы безопасно передавать собственные образы, потребуется приватный registry. Я использую registry от Яндекс Облака — это недорого, и у меня уже есть некоторая автоматизация под них.
Создание Container Registry
-
Создаем аккаунт в Yandex Cloud
-
Создаем каталог (folder): Создание каталога
-
Открываем Yandex Cloud Console (см. картинку ниже)
-
Выбираем Container Registry (см. картинку ниже)
-
Создам registry с любым названием
-
Копируем id нашего registry (см. картинку ниже)
-
Теперь подставляем registry id перед нашими образами и докер поймет, что куда нужно отправлять/откуда нужно забирать образы. Например:
cr.yandex/crparlvq5pji2gn67f8s/pw_backend:latest
При этом остается проблема, что перед тем как взаимодействовать с приватным registry, нужно к нему авторизоваться. Есть 2 способа…
Авторизация без авторизации
На самом деле авторизации можно избежать, если выдать всем права на pull или push образов (оба не рекомендуются, второе особенно).
-
Открываем созданный registry в Яндекс Облаке
-
Открываем Access bindings (см. картинку ниже)
-
Выбираем Assign bindings (см. картинку ниже)
-
Выбираем Public + All users (см. картинку ниже)
-
Добавляем нужные права: puller и/или pusher (см. картинку ниже)
Таким образом приватный registry становится полу/полностью публичным, но для кого-то это может быть наилучший способ получить желаемое.
Авторизация с помощью ключей
С помощью авторизационных ключей можно раз и навсегда залогиниться к Container Registry.
-
Создаем ключи:
yc iam key create --service-account-name default-sa -o key.json
(см. документацию) -
Логинимся:
cat key.json | docker login \ --username json_key \ --password-stdin \ cr.yandex
Главная проблема этого способа в том, что требуется повторять эту процедуру на каждой виртуалке, но можно автоматизировать.
-
Сохраняем ключ в проекте, я буду использовать путь:
secrets/yc_key.json
(главное случайно не закомитьте) -
Далее указываем в переменных окружения
-
SSH_USER
— юзер для SSH -
SSH_HOST
— хост для SSH -
SSH_PKEY
— путь к приватному ключу для SSH
export SSH_USER=dbrusenin export SSH_HOST=111.222.0.3 export SSH_PKEY=~/.ssh/id_rsa
-
-
Запускаем скрипт:
#!/usr/bin/env bash set -e function SSH() { local cmd="$1" ssh -t -o LogLevel=QUIET -o "StrictHostKeyChecking no" -i $SSH_PKEY $SSH_USER@$SSH_HOST "$cmd" } function SCP { local src=$1 local dst_dir=$2 SSH "mkdir -p \"$(dirname $dst_dir)\"" scp -i $SSH_PKEY -r $src $SSH_USER@$SSH_HOST:$dst_dir } function DR_YANDEX_AUTH() { local auth_keys="$1" SCP "$auth_keys" /tmp/yc_key.json SSH "cat /tmp/yc_key.json | sudo docker login \ --username json_key \ --password-stdin \ cr.yandex" > /dev/null SSH "rm /tmp/yc_key.json" } DR_YANDEX_AUTH "./secrets/yc_key.json"
Или снова способ для ленивых:
curl https://gist.githubusercontent.com/Deimvis/a60df999aca23b2292f2a5d5c856618a/raw/937d846ab667b84f13a9fb59e012ec3a37afedc8/yandex_auth.sh | sh
Результат
Теперь вы можете пушить и пуллить собственные образы, подставляя перед названиями образом cr.yandex/<registry_id>/
. Убедиться, что вы авторизованы к registry по идее можно взглянув на конфиг ~/.docker/config.json
.
Итог
-
Если Docker Hub недоступен, то нельзя из коробки делать pull/push образов
-
Чтобы делать pull публичных образов, нужно настроить зеркало
-
Чтобы делать push/pull собственных образов, нужно создать приватный registry и работать через него
ссылка на оригинал статьи https://habr.com/ru/articles/831608/
Добавить комментарий