If your docker swarm deployment uses images from publicly accessible image registries such as docker hub, you will not need this. But, what if your cluster uses customized build? You can of course build the custom image on each node so that all of them uses the same custom copy of the image. You can also create a private repository on Docker Hub. Or, as this article will guide you, establish a private local registry
So, creating a local registry is as easy as running
sudo docker run -d -p 8008:8008 --name registry registry:2
You should now have a working local image registry. Of course this would not work for our usecase. What we need is a local image registry, secured with TLS and user credential.
Prereqs
We will need a proper Fully Qualified Domain Name (FQDN) and a working SSL certificate to go along with it. Either get one from reputable CA, or follow this bit on getting it with Let’s Encrypt. We will also need to make sure that all of the nodes on the cluster can resolve the registry server’s domain name, either with proper DNS server configuration, or adding the record on the host file. For the purpose of this article, let say the FQDN that I’ll be using is regs.mach5.web.id
We will also need to pick a port that is currently not in use to serve as the gateway into the registry. For the purpose of this article, I’ll be using 8443
In this post, we will be using the most basic authentication method that is supported by Docker registry, which is htpasswd. Prepare a directory to store the file, let say:
sudo mkdir /data/docker/auth/
To create a pair of user and password, do:
sudo docker run --entrypoint htpasswd registry:2 -Bbn awn1 securePassawn1 >> /data/docker/auth/htpasswd
The next step is to prepare a directory to store docker permanent storage on the host, for example:
sudo mkdir /data/docker/registry/
Now, let’s deploy our Registry container.
Establish Registry Container
To deploy the container, do
sudo docker run -d --restart=always --name registry -v /data/docker/auth:/auth -v /data/docker/registry:/var/lib/registry -v /data/docker/certs:/certs -e "REGISTRY_AUTH=htpasswd" -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd -e REGISTRY_HTTP_ADDR=0.0.0.0:8443 -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/cert.pem -e REGISTRY_HTTP_TLS_KEY=/certs/privkey.pem -p 8443:8443 registry:2
We should now have a running local docker image registry and we can access it from the following URL:
https://regs.mach5.web.id:8443/v2/
Now, let see what we can do with it
Building Docker custom image and populating the registry
Let say that I need a custom php-apache container that has all the necessary modules required for Laravel deployment. Since no image on docker hub can cater this requirement, I need to build a customized image. This is how my Dockerfile looks like:
FROM php:7.3-apache MAINTAINER ikhsan demonspeedster at gmail.com RUN apt-get update && apt-get install -y --fix-missing \ git \ openssl \ apt-utils \ zlib1g-dev \ libzip-dev \ libbz2-dev \ libxml2 \ libxml2-dev \ bzip2 \ libpng-dev \ gnupg RUN apt-get update -y && apt-get install -y libmcrypt-dev openssl RUN docker-php-ext-install ctype xml json bcmath tokenizer mysqli bz2 pdo pdo_mysql gd mbstring zip && docker-php-ext-enable ctype xml json bc math tokenizer bz2 mysqli pdo pdo_mysql gd mbstring zip RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer RUN cp /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/ RUN cp /etc/apache2/mods-available/authz_groupfile.load /etc/apache2/mods-enabled/ RUN cp /etc/apache2/mods-available/headers.load /etc/apache2/mods-enabled/
Building the custom image based on the Dockerfile above can be done by running the command below on the directory where the Dockerfile is located:
surfer@AWN1:/data/docker/scripts/builds$ sudo docker build -t my-laravel . [sudo] password for surfer: Sending build context to Docker daemon 6.144kB Step 1/11 : FROM php:7.3-apache ---> dfe3bb4436a9 Step 2/11 : MAINTAINER surfer whoareyou at gmail.com ---> Using cache ---> add2787dffad Step 3/11 : RUN apt-get update && apt-get install -y --fix-missing git openssl apt-utils zlib1g-dev libzip-dev libbz2-dev libxml2 libxml2-dev bzip2 libpng-dev gnupg ---> Using cache ---> 246c98c954ff Step 4/11 : RUN apt-get update -y && apt-get install -y libmcrypt-dev openssl ---> Using cache ---> f260c7091943 Step 5/11 : RUN docker-php-ext-install ctype xml json bcmath tokenizer mysqli bz2 pdo pdo_mysql gd mbstring zip && docker-php-ext-enable ctype xml json bcmath tokenizer bz2 mysqli pdo pdo_mysql gd mbstring zip ---> Using cache ---> de999c0e1358 Step 6/11 : RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer ---> Using cache ---> 87dd79d4cbf7 Step 7/11 : RUN cp /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-enabled/ ---> Using cache ---> 22012168ea6d Step 8/11 : RUN cp /etc/apache2/mods-available/authz_groupfile.load /etc/apache2/mods-enabled/ ---> Using cache ---> f210b628fea9 Step 9/11 : RUN cp /etc/apache2/mods-available/headers.load /etc/apache2/mods-enabled/ ---> Using cache ---> 2974a8a41445 Successfully built 2974a8a41445 Successfully tagged my-laravel:latest
We now have the perfect docker image for our project. The problem is, there let say 2 other swarm nodes that need access to this image so we can deploy the containers across all nodes in the swarm
Tagging and pushing image to Registry
To push our recently created custom image to our recently created local registry, first we need to log into the local registry using the user and password that we have created before by doing:
surfer@AWN1:~$ sudo docker login regs.mach5.web.id:8443 Username: awn1 Password: WARNING! Your password will be stored unencrypted in /home/surfer/.docker/config.json. Configure a credential helper to remove this warning. See https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Once we are logged it, tag the image by doing
sudo docker tag my-laravel regs.mach5.web.id:8443/webappAnd push it to the local registry with:
surfer@AWN1:/data/docker/scripts/builds$ sudo docker push regs.mach5.web.id:8443/webapp The push refers to repository [regs.mach5.web.id:8443/webapp] 97300f38cc34: Mounted from app 2b80b9056019: Mounted from app aea8add70e90: Mounted from app 97c5752e4057: Mounted from app 046465b1360f: Mounted from app 75e7ef1cd667: Mounted from app d4071c655272: Mounted from app 679f9d7ab128: Mounted from app 9349e9686881: Mounted from app 18c63cd06302: Mounted from app 98eebde476f8: Mounted from app 9f56628995bb: Mounted from app de06f6a1de3c: Mounted from app 7dcc21fa0198: Mounted from app 91921db962d2: Mounted from app 387f6270e938: Mounted from app 448f57bab35d: Mounted from app 66d3e4af6e09: Mounted from app 389b7b5f40f8: Mounted from app 4b017cb89a0f: Mounted from app 9512a01c3d20: Mounted from app a619f62b61d0: Mounted from app 1c95c77433e8: Mounted from app latest: digest: sha256:5d288c145f068b7769df34b5ba1feac8455bc4fbdef6d55932caaefc8593e13f size: 5120
Congrats, we just populated our local registry with our first custom image.
Alternatively, let say you want to make a generic traefik image available from your local registry, you can pull the image from Docker-hub:
surfer@AWN1:~$ sudo docker pull php:apache apache: Pulling from library/php Digest: sha256:1961e48f78c27f76ddcff0956155dd62419cc4e74cd03f3a812589c4e9b423a9 Status: Downloaded newer image for php:apache
tag the image
sudo docker tag php:apache regs.mach5.web.id:8443/webapp
And push it to the registry
surfer@AWN1:~$ sudo docker push regs.mach5.web.id:8443/webapp The push refers to repository [regs.mach5.web.id:8443/webapp] 29075c48c151: Pushed afb5b0d685c2: Pushed 88b92e4e5fea: Pushed latest: digest: sha256:99a4e99e0b5f9be2fc9a5be5b83fab4a732a7892b35371504aa55bf2f036beaa size: 949
Getting custom image from local registry
Just like when pushing an image to the local registry, to get an image off from the local registry, we need to log into the server by doing
surfer@AWN1:~$ sudo docker login regs.mach5.web.id:8443
After we have successfully log into the server, we can either pull the image just like this
surfer@AWN1:~$ sudo docker pull regs.mach5.web.id:8443/webapp Using default tag: latest latest: Pulling from traefik Digest: sha256:99a4e99e0b5f9be2fc9a5be5b83fab4a732a7892b35371504aa55bf2f036beaa Status: Downloaded newer image for regs.mach5.web.id:8443/webapp:latest
Or, to use it in service on a docker swarm:
sudo docker service create --with-registry-auth --name webaps regs.mach5.web.id:8443/webapp
The –with-registry-auth informs the master node to forward the credential used to log into the local private registry to all the nodes in the swarm so each of them can independently pull the image from the local registry.