How To Install A Private Docker Container Registry In Kubernetes
IMPORTANT
I recreated this tutorial in a simple way to work with a newer version of Traefik. The updated tutorial can be found here:
Introduction
Hosting a Docker private registry gives you full control over the storage location of your images and how you can access them. This is especially useful if you develop your own private projects and don't want to host them publicly on Docker Hub.
In this tutorial, you will learn how to install a private Docker registry in any Kubernetes cluster. It is a follow-up of a tutorial that was previously released on this blog because it uses a Traefik Ingress Controller to expose the Docker registry.
Preparation
Create Kubernetes Namespace
The first step is to create a Kubernetes namespace where all resources will be applied during this tutorial:
kubectl create namespace docker-registry
PersistentVolumeClaim
In this section, you will mount a volume into a dedicated Kubernetes Pod using a PersistentVolumeClaim. A PersistentVolumeClaim (PVC
) is a Kubernetes resource to use a pre-defined abstract PersistentVolume (PV
) storage without exposing how those volumes are implemented.
PV
) is a storage definition that was either manually provisioned by a user or dynamically provisioned using a storage class. See this link for further information.To set up the PersistentVolumeClaim usable in the private Docker registry this tutorial will assume that you already have a preconfigured PersistentVolume called csi-cinder-classic
.
1. Create a new file (registry-pvc.yaml
) containing the PersistentVolumeClaim using the csi-cinder-classic
storage class:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: docker-registry-pv-claim
namespace: docker-registry
spec:
accessModes:
- ReadWriteOnce
volumeMode: Filesystem
resources:
requests:
storage: 60Gi
storageClassName: csi-cinder-classic
2. Apply it to the Kubernetes Cluster:
kubectl apply -f registry-pvc.yaml
3. Verify if the persistent volume claim was successfully created:
kubectl get pvc docker-registry-pv-claim -n docker-registry
# NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS
# docker-registry-pv-claim Bound *** 60Gi RWO csi-cinder-classic
Set Up Docker Registry
Now, that you set up the namespace and the PersistentVolumeClaim you can deploy a Docker registry using the external storage and make the registry available across the entire Kubernetes cluster.
Generate User & Password
To set up the Docker registry you need to generate a user and password which will be used to push and pull images to the registry.
Create a new file gen-pass.sh
that contains the following content:
export REGISTRY_USER=admin
export REGISTRY_PASS=registryPass
export DESTINATION_FOLDER=./registry-creds
# Backup credentials to local files (in case you'll forget them later on)
mkdir -p ${DESTINATION_FOLDER}
echo ${REGISTRY_USER} >> ${DESTINATION_FOLDER}/registry-user.txt
echo ${REGISTRY_PASS} >> ${DESTINATION_FOLDER}/registry-pass.txt
docker run --entrypoint htpasswd registry:2.7.0 \
-Bbn ${REGISTRY_USER} ${REGISTRY_PASS} \
> ${DESTINATION_FOLDER}/htpasswd
unset REGISTRY_USER REGISTRY_PASS DESTINATION_FOLDER
htpasswd
was removed from the latest Docker images we will be using version 2.7.0 to generate user and password. For more information have a look at this stack overflow post.Execute the script which will create the credentials within the subfolder ./registry-creds
:
sh gen-pass.sh
Install Docker Registry Using Helm
In the following steps, you will use Helm to install the Docker registry.
1. Add the twuni/docker-registry
Helm repository which is a successor of the official Docker registry:
helm repo add twuni https://helm.twun.io
2. Update local Helm chart repository cache:
helm repo update
3. Search for the latest version of twuni/docker-registry
Helm chart version:
helm search repo docker-registry
# NAME CHART VERSION APP VERSION DESCRIPTION
# twuni/docker-registry 2.2.2 2.8.1 A Helm chart for Docker Registry
4. Create a registry-chart.yaml
which will be used to install the Docker registry in our Kubernetes cluster:
---
replicaCount: 1
persistence:
enabled: true
size: 60Gi
deleteEnabled: true
storageClass: csi-cinder-classic
existingClaim: docker-registry-pv-claim
secrets:
htpasswd: admin:$2y$05$Gh/3ppmkuIXJIVyBBtHf0ug.wnnJvbtSEzlXz6z/7oO7XvF/xq7Ni
htpasswd
string with the content of the file which was generated by the gen-pass.sh
script (./registry-cred/htpasswd
)5. Install the Docker registry Helm chart using the registry-chart.yaml
:
helm install -f .\registry-chart.yaml docker-registry --namespace docker-registry twuni/docker-registry
6. Verify installation:
kubectl get pods -n docker-registry
# NAME READY STATUS RESTARTS
# docker-registry-9fa1234ba-gaf16 1/1 Running 0
If you want to change something like the replicaCount
, the htpasswd
, or the storage you can do this by adjusting the registry-chart.yaml
and reapply it by executing:
helm upgrade -f .\registry-chart.yaml docker-registry --namespace docker-registry twuni/docker-registry
How To Uninstall?
To uninstall the Docker registry you have to remove it from the Kubernetes cluster with Helm:
helm uninstall docker-registry --namespace docker-registry
Then, remove the Docker registry namespace from the Kubernetes cluster:
kubectl delete namespace docker-registry
Add Docker Registry Ingress
To expose the Docker registry you will use Traefik Ingress Controller which will allow access to the registry through HTTPS with a proper TLS certificate.
1. Create a new file (ingressroute.yaml
) and make sure to replace YOUR_DOMAIN with your Docker registry domain:
---
kind: IngressRoute
apiVersion: traefik.containo.us/v1alpha1
metadata:
name: docker-registry
namespace: docker-registry
spec:
entryPoints:
- websecure
routes:
- match: Host(`YOUR_DOMAIN`)
kind: Rule
services:
- name: docker-registry
port: 5000
2. Apply the IngressRoute by executing:
kubectl apply -f ingressroute.yaml
3. Verify if the Kubernetes resource was successfully created:
kubectl describe ingressroute docker-registry -n docker-registry
Push Images To Private Registry In Kubernetes Cluster
To show how to push a Docker Image to our new Docker registry this tutorial show how you can pull a public Docker Hub image, tag it and then push it to your registry.
1. Log in to the Docker registry by using the previously created credentials
docker login \
-u $(cat ./registry-creds/registry-user.txt) \
-p $(cat ./registry-creds/registry-pass.txt) \
YOUR_DOMAIN
2. Pull the nginx:latest
Docker image:
docker pull nginx
3. Tag the image with a custom name and prepend the private Docker registry domain name
docker tag nginx YOUR_DOMAIN/my-nginx
4. Push the Docker Image to the registry
docker push YOUR_DOMAIN/my-nginx
Use Docker Registry To Pull Images In Your Kubernetes Cluster
As you have a Docker registry deployed in your Kubernetes cluster you can start using it by pulling previously pushed images for your Kubernetes Pods.
To learn how a private Docker registry can be used for pulling images you will create a simple Kubernetes pod in a newly created test
namespace. This Kubernetes Pod will use the previously pushed image YOUR_DOMAIN/my-nginx
.
First, you have to create the test
Kubernetes namespace:
kubectl create namespace test
Create Docker Registry Secret
This is the most important step.
You have to create a Docker Secret which will be used for accessing the Docker registry from your Kubernetes cluster. To do this use the credentials from the previous step and create a Kubernetes Docker Secret in the test
namespace:
kubectl create secret docker-registry regcred --docker-server=YOUR_DOMAIN --docker-username=admin --docker-password=registryPass -n test
Create Kubernetes Pod With Image From Docker Registry
After the Kubernetes secret containing the credentials for the Docker registry is created, you create a new Kubernetes Deployment (test-nginx.yaml
) that uses your registry:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: test
spec:
selector:
matchLabels:
app: nginx
replicas: 1
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: YOUR_DOMAIN/my-nginx
ports:
- containerPort: 80
imagePullSecrets:
- name: regcred
If taking a close look at this file you will see that it contains three essential differences from a normal nginx
deployment.
image: YOUR_DOMAIN/my-nginx
imagePullSecrets:
- name: regred
Both changes are important in order to use the Docker registry. The first one selects the image to use in the pod (registry URL as a prefix + image name). The second option sets which Docker secret will be used to pull the image. If you changed credentials or names you have to update both values.
Now, deploy the Kubernetes Deployment in your Cluster:
kubectl apply -f test-nginx.yaml
Use kubectl describe podname -n test
to see if the image will be pulled and the container gets started correctly. The output should look like this:
Closing Notes
Hopefully, this article gave you a good overview of how to set up a private Docker registry in your Kubernetes cluster.
Keep in mind that having a Private Docker registry is essential if deploying Docker services that are not Open Source
To have an easy repeatable procedure that you can run on any Kubernetes cluster I created a GitHub Gist containing all necessary files. If you already have a Traefik Ingress Controller running in your Kubernetes cluster you can simply download all files, adjust them to your needs, and then apply them by executing:
kubectl create namespace docker-registry
kubectl apply -f registry-pvc.yaml
helm repo add twuni https://helm.twun.io
helm repo update
helm search repo docker-registry
helm install -f values.yaml docker-registry --namespace docker-registry twuni/docker-registry
kubectl apply -f registry-ingressroute.yaml
I would love to hear your feedback about this tutorial. Furthermore, if you also have set up a Docker registry and use a different approach, please comment here and explain what you have done differently. Also, if you have any questions, please ask them in the comments. I try to answer them if possible.
Feel free to connect with me on Medium, LinkedIn, Twitter, and GitHub.