The world’s leading publication for data science, AI, and ML professionals.

How To Install A Private Docker Container Registry In Kubernetes

Get full control of where your images are stored

Photo by Growtika on Unsplash
Photo by Growtika on Unsplash

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 private projects that shouldn’t be 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 previously released on this blog because it will use 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.

💡 Prerequisite: A PersistentVolume should exist within the Kubernetes cluster. A PersistentVolume (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 that will be used 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
  1. Apply it to the Kubernetes Cluster:
kubectl apply -f registry-pvc.yamlb
  1. 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 have created 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 that 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

💡 Note: _Change REGISTRY_USER and REGISTRYPASS to your needs. Also, since htpasswd was removed from the latest Docker images we will be using version 2.7.0 to generate the 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.

💡 Note: Helm is the package manager for Kubernetes, focused on automating the installation of all kinds of Kubernetes applications.

  1. Add the twuni/docker-registry Helm repository that is a successor of the official Docker registry:
helm repo add twuni https://helm.twun.io
  1. Update local Helm chart repository cache:
helm repo update
  1. 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
  1. 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

💡 Note: Replace the htpasswd string with the content of the file that was generated by the gen-pass.sh script (./registry-cred/htpasswd)

  1. 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
  1. 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 to allow access to the registry through HTTPS with a proper TLS certificate.

💡 Note: Read about how to install Traefik Ingress Controller in any Kubernetes cluster within this tutorial: https://www.paulsblog.dev/how-to-install-traefik-ingress-controller-in-kubernetes/

  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
  1. Apply the IngressRoute by executing:
kubectl apply -f ingressroute.yaml
  1. 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 will show how to 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
  1. Pull the nginx:latest Docker image:
docker pull nginx
  1. Tag the image with a custom name and prepend the private Docker registry domain name
docker tag nginx YOUR_DOMAIN/my-nginx
  1. 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 new 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 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

💡 Note: This Kubernetes Docker secret resource must be created within the correct namespace!

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.

  1. image: YOUR_DOMAIN/my-nginx
  2. `imagePullSecrets:

    • name: regred`

Both changes are necessary to use the Docker registry. The first option selects the image to use in the pod (registry URL as a prefix + image name). The second option sets which Docker secret is 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:

Pod Pull Image Events after deployment my-nginx Kubernetes Deployment
Pod Pull Image Events after deployment my-nginx Kubernetes Deployment

Closing Notes

Hopefully, this article gave you a good overview of how to set up a private Docker registry in your Kubernetes cluster.

Remember, 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 will answer them if possible.

Feel free to connect with me on my blog, LinkedIn, Twitter, and GitHub.


This article was initially published on my blog at https://www.paulsblog.dev/how-to-install-a-private-docker-container-registry-in-kubernetes/


Related Articles