Setting Up a Private Docker Registry and Using it in NAT Network
You can find these commands in my Gitlab repo.
1. Creating workspace directory
registry_volume=/registry
rm -rf ${registry_volume}; \
mkdir -p ${registry_volume}/{auth,certs,repository,redis}
We will create auth,certs,repository,redis
directories.
auth
Authorization file for loging in to our registry. This file looks like /etc/shadow.
cert
Certificate and key files of registry.repository
Private registry images.redis
Redis Cache files. We will mount Redis container's data directory here.
2. Creating a Certificate
openssl req \
-newkey rsa:4096 \
-x509 \
-nodes \
-sha256 \
-keyout ${registry_volume}/certs/docker-registry.key \
-out ${registry_volume}/certs/docker-registry.crt
Just answer the questions.
3. Creating a Network Interface in Docker
docker_network_name=docker_nat
docker_network_subnet="10.0.2.0/24"
test -n "$(docker network ls \
| grep ${docker_network_name})" \
|| docker network create \
--subnet=${docker_network_subnet} \
${docker_network_name}
This is not necessary, but this can make easier to use it in Kubernetes etc.
4. Standing Up Redis Cache Container
redis_address=10.0.2.63
redis_port=6379
redis_password=top_secret
docker run -d \
--restart=always \
--net ${docker_network_name} \
--ip ${redis_address} \
--name redis \
-p ${redis_port}:6379 \
-v ${registry_volume}/redis:/data \
redis \
--appendonly yes \
--requirepass ${redis_password}
After container stands up,
docker exec redis \
redis-cli -h 127.0.0.1 -p ${redis_port} AUTH ${redis_password}
If you get 'OK' response, that means it's working.
5. Standing Up Private Registry Container
registry_domain=10.0.3.1 # NAT IP address of private registry
registry_port=5000
registry_name=localregistry
registry_address=10.0.2.22 # IP address of private registry container
echo -e "{\n\t\"insecure-registries\" : [ \"${registry_domain}:${registry_port}\" ]\n}" > /etc/docker/daemon.json
systemctl daemon-reload
systemctl restart docker
sleep 1
docker run -d \
--restart=always \
-p ${registry_port}:5000 \
--name ${registry_name} \
--net ${docker_network_name} \
--ip ${registry_address} \
-v ${registry_volume}/repository:/var/lib/registry \
-v ${registry_volume}/auth:/auth \
-v ${registry_volume}/certs:/certs \
-e "REGISTRY_AUTH=htpasswd" \
-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
-e "REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd" \
-e "REGISTRY_HTTP_TLS_CERTIFICATE=/certs/docker-registry.crt" \
-e "REGISTRY_HTTP_TLS_KEY=/certs/docker-registry.key" \
-e "REGISTRY_STORAGE_CACHE_BLOBDESCRIPTOR=redis" \
-e "REGISTRY_REDIS_ADDR=${redis_address}:${redis_port}" \
-e "REGISTRY_REDIS_PASSWORD=${redis_password}" \
-e "REGISTRY_REDIS_POOL_MAXIDLE=16" \
-e "REGISTRY_REDIS_POOL_MAXACTIVE=64" \
-e "REGISTRY_REDIS_POOL_IDLETIMEOUT=300s" \
registry:2
Descriptions of parameters:
-p ${registry_port}:5000
For assigning external port of our registry.
--name ${registry_name}
For reaching to our registry container without ID.
--net ${docker_network_name}
The name of the Docker network interface we just created.
--ip ${registry_address}
IP address of private registry container.
-v ${registry_volume}/repository:/var/lib/registry
Location of our private images.
-v ${registry_volume}/auth:/auth
Location of credentials to login our private registry.
-v ${registry_volume}/certs:/certs
Location of certificate and key files of our private registry.
Usage of environment variables (from docs.docker.com)
In a typical setup where you run your Registry from the official image, you can specify a configuration variable from the environment by passing -e arguments to your docker run stanza or from within a Dockerfile using the ENV instruction.
To override a configuration option, create an environment variable named REGISTRYvariable where variable is the name of the configuration option and the (underscore) represents indention levels. For example, you can configure the rootdirectory of the filesystem storage backend:
storage:
filesystem:
rootdirectory: /var/lib/registry
To override this value, set an environment variable like this:
REGISTRY_STORAGE_FILESYSTEM_ROOTDIRECTORY=/somewhere
This variable overrides the /var/lib/registry value to the /somewhere directory.
I suggest to look at Docker Documents for detailed information.
6. Creating Credentials to Login Private Registry
registry_username=user
registry_password=top_secret
docker run --rm \
--entrypoint htpasswd \
httpd:2 -Bbn \
${registry_username} \
${registry_password} \
> ${registry_volume}/auth/htpasswd; \
chmod 0600 -R ${registry_volume}/auth/htpasswd; \
cat ${registry_volume}/auth/htpasswd
Here we create the username and the password of our private registry.
7. To Login Private Registry
rm ~/.docker/config.json &>/dev/null; \
docker login \
-u ${registry_username} \
-p ${registry_password} \
https://${registry_domain}:${registry_port}
8. To Pull, Tag and Push to Private Registry an Image
Pulling an image
docker pull alpine && docker images alpine:latest
Tagging the image
docker tag \
docker.io/alpine:latest \
${registry_domain}:${registry_port}/alpine:local; \
docker images ${registry_domain}:${registry_port}/alpine:local
Pushing the tagged image
docker push ${registry_domain}:${registry_port}/alpine:local; \
docker images ${registry_domain}:${registry_port}/*
You can see files of image in ${registry_volume}/repository/docker/registry/v2/repositories
ls -1tr ${registry_volume}/repository/docker/registry/v2/repositories
or you can look into the running container
docker exec ${registry_name} \
sh -c "ls -1tr /var/lib/registry/docker/registry/v2/repositories"
9. Looking into Redis Cache
We should login to Redis Cache
docker exec -ti redis \
/usr/local/bin/redis-cli \
-h 127.0.0.1 \
-p ${redis_port} \
-a ${redis_password}
And list keys
keys *
Now our Private Registry is running and we can login and push our images. What about other NAT clients? You will probably get some security and certificate related errors when you try to login to this registry from other machines.
The shortest way to solve this problem is to set all other clients to this machine can be used as an "Insecure Registry".
You can run these commands on each machine individually
echo -e "{\n\t\"insecure-registries\" : [ \"${registry_domain}:${registry_port}\" ]\n}" > /etc/docker/daemon.json
systemctl daemon-reload && systemctl restart docker && sleep 1
Or you can run it all at once with a "For Loop"
NAT_list=( \
10.0.3.100 \
10.0.3.101 \
10.0.3.102 \
10.0.3.103 \
10.0.3.104 \
)
echo -e "{\n\t\"insecure-registries\" : [ \"${registry_domain}:${registry_port}\" ]\n}" >> /etc/docker/daemon.json
systemctl daemon-reload && systemctl restart docker && sleep 1
for i in ${NAT_list[@]}
do
scp /etc/docker/daemon.json $(echo root@${i}):/etc/docker/daemon.json
ssh -T $(echo root@${i}) 'systemctl daemon-reload; systemctl restart docker && echo OK.'
done
10. You Can Try Pulling "alpine:local" Image From Another NAT Client
Connecting to a client from NAT_list
ssh $(echo root@${NAT_list[-1]})
To Login private registry
docker login \
-u user \
-p top_secret \
https://10.0.3.1:5000 # Edit here as your configuration
Pulling "alpine:local" image
docker pull 10.0.3.1:5000/alpine:local
If you want to list images in private registry
curl -s \
--insecure \
-X GET \
-u user:top_secret \
"https://10.0.3.1:5000/v2/_catalog" \
| jq -r '.repositories'
If you want to list tags of an image in private registry
curl -s \
--insecure \
-X GET \
-u user:top_secret \
"https://10.0.3.1:5000/v2/alpine/tags/list" \
| jq -r '.tags'