What to replace Docker Hub with in Russia
Docker Hub was recently blocked in Russia. In this post, I will tell you how to live with it and how to quickly migrate current projects using Docker Hub.
1. Access to public images
The first thing to do is to regain the ability to do docker pull
public images (for example, docker pull ubuntu:latest
). For this we will use the mirror from Google: https://mirror.gcr.io/
. There is also a mirror from Yandex cr.yandex/mirror
but there seem to be few images there (ubuntu:latest
– There is, python:3.12
— no). You can specify a mirror each time, for example docker pull mirror.gcr.io/ubuntu:latest
. But it is more convenient to specify the mirror once in the Docker settings and do docker pull
as usual: docker pull ubuntu:latest
(the mirror will be used automatically).
I wrote a script that will do everything for you. You just need to pass the mirror as the first argument. Tested on Linux Ubuntu and MacOS.
Note for MacOS: there are sleeps in the code to wait for Docker to restart. They can be increased if necessary.
Note for Windows: it is enough to just correct the code where "$os" == "CYGWIN_NT"
and it should work.
Script:
#!/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"
Script logic:
Check if the mirror is already set up
If there is no mirror, then update the config
Restart docker
Classic way to start
Create a file with a script
update_mirror
Renewing rights
chmod +x update_mirror
Let's launch
./update_mirror 'https://mirror.gcr.io/'
A method for the laziest 🙂
curl https://gist.githubusercontent.com/Deimvis/c747446725c84cf0731e82d76f7cc67b/raw/709e8fe296121fb1445fca49086ab9359ca5da67/update_mirror.sh | bash -s 'https://mirror.gcr.io/'
Result
After setting up the mirror will be visible in the output docker system info
The following lines will appear:
Registry Mirrors:
https://mirror.gcr.io/
Now you can pull public images as usual: docker pull ubuntu:latest
2. Loading and unloading personal images
In order to securely transfer your own images, you will need a private registry. I use the registry from Yandex Cloud – it is inexpensive, and I already have some automation for them.
Creating a Container Registry
Create an account in Yandex Cloud
Create a directory (folder): Creating a catalog
Open Yandex Cloud Console (see image below)
Select Container Registry (see image below)
I will create a registry with any name
Copy the id of our registry (see the picture below)
Now we substitute the registry id before our images and Docker will understand where to send/from where to pick up images. For example:
cr.yandex/crparlvq5pji2gn67f8s/pw_backend:latest
![](https://habrastorage.org/getpro/habr/upload_files/6cd/10b/06d/6cd10b06dad6a0a4b1568e73be4e3420.png)
![](https://habrastorage.org/getpro/habr/upload_files/cd0/442/be4/cd0442be48f102edaac4ab3fbc05d59a.png)
![](https://habrastorage.org/getpro/habr/upload_files/675/e67/405/675e674054d7722fb3c5204cfb155acb.png)
The problem remains that before interacting with the private registry, you need to log in to it. There are 2 ways…
Authorization without authorization
In fact, authorization can be avoided by giving everyone the rights to pull or push images (both are not recommended, the second one especially).
Open the created registry in Yandex Cloud
Open Access bindings (see image below)
Select Assign bindings (see image below)
Select Public + All users (see image below)
Add the necessary rights: puller and/or pusher (see the picture below)
![](https://habrastorage.org/getpro/habr/upload_files/7b1/10c/759/7b110c7594873df1251e6b57398568e1.png)
![](https://habrastorage.org/getpro/habr/upload_files/b6b/950/529/b6b950529d2f2afc43c199fb370b160d.png)
![](https://habrastorage.org/getpro/habr/upload_files/d1a/476/429/d1a476429fdc46b2094f2819dd214f12.png)
![](https://habrastorage.org/getpro/habr/upload_files/3a8/c1e/803/3a8c1e80333d54428291e8ad83ada252.png)
This makes the private registry semi/fully public, but for some it may be the best way to get what they want.
Authorization using keys
Using authorization keys, you can log in to Container Registry once and for all.
Create keys:
yc iam key create --service-account-name default-sa -o key.json
(cm. documentation)Let's log in:
cat key.json | docker login \ --username json_key \ --password-stdin \ cr.yandex
The main problem with this method is that you need to repeat this procedure on each virtual machine, but it can be automated.
We save the key in the project, I will use the path:
secrets/yc_key.json
(the main thing is not to commit by accident)Next, we specify in the environment variables
SSH_USER
— user for SSHSSH_HOST
— host for SSHSSH_PKEY
— path to the private key for SSH
export SSH_USER=dbrusenin export SSH_HOST=111.222.0.3 export SSH_PKEY=~/.ssh/id_rsa
Run the script:
#!/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"
Or again a method for the lazy:
curl https://gist.githubusercontent.com/Deimvis/a60df999aca23b2292f2a5d5c856618a/raw/937d846ab667b84f13a9fb59e012ec3a37afedc8/yandex_auth.sh | sh
Result
Now you can push and pull your own images by prepending the image names cr.yandex/<registry_id>/
. You can make sure that you are authorized to the registry by looking at the config ~/.docker/config.json
.
Summary
Docker Hub is not available in Russia, so it is no longer possible to pull/push images out of the box
To pull public images, you need to set up a mirror
To push/pull your own images, you need to create a private registry and work through it