|
| 1 | +--- |
| 2 | +title: The Joy of Kubernetes 3 - Private Docker registry on NFS storage |
| 3 | +published: true |
| 4 | +--- |
| 5 | + |
| 6 | +# Welcome to the Joy of Kubernetes. |
| 7 | + |
| 8 | +Hi everyone, so glad you could join us today and enjoy deploying some useful tools. |
| 9 | + |
| 10 | +If you are painting along at home, this one might be an episode where you need to take some brave creative efforts. |
| 11 | + |
| 12 | +The goal is to set up a private container registry on our kubernetes cluster using external storage. |
| 13 | + |
| 14 | +- [Welcome to the Joy of Kubernetes.](#welcome-to-the-joy-of-kubernetes) |
| 15 | + - [Prerequisites 🎨](#prerequisites-) |
| 16 | + - [Argo CD](#argo-cd) |
| 17 | + - [Setting the scene, what our landscape will be](#setting-the-scene-what-our-landscape-will-be) |
| 18 | + - [Storage Classes: The colors on todays palette](#storage-classes-the-colors-on-todays-palette) |
| 19 | + - [NFS is not secure](#nfs-is-not-secure) |
| 20 | + - [Creation of the storage class](#creation-of-the-storage-class) |
| 21 | + - [Without Argo CD](#without-argo-cd) |
| 22 | + - [Persistent Volume Claims: Choosing the Perfect Canvas](#persistent-volume-claims-choosing-the-perfect-canvas) |
| 23 | + - [A private Docker Registry with helm: Bringing our masterpiece together](#a-private-docker-registry-with-helm-bringing-our-masterpiece-together) |
| 24 | + - [TLS and Ingress: Finishing touches](#tls-and-ingress-finishing-touches) |
| 25 | + - [Summary](#summary) |
| 26 | + - [Further things you can try](#further-things-you-can-try) |
| 27 | + - [Thank you for joining](#thank-you-for-joining) |
| 28 | + |
| 29 | +## Prerequisites 🎨 |
| 30 | + |
| 31 | +- ~~A canvas, some brushes, and some paint~~ A kubernetes cluster and kubectl. |
| 32 | +- Optional, Argo CD with the application set creation from [the first post in the series](../he-joy-of-kubernetes-1-argocd-with-private-git-repo). |
| 33 | +- The ability to set up NFS storage, or some other storage class |
| 34 | + |
| 35 | +### Argo CD |
| 36 | + |
| 37 | +This is the structure that we will create today in our Argo CD gitops structure to get the required results splattered onto our canvas. |
| 38 | + |
| 39 | +``` powershell |
| 40 | +mkdir gitops/joy-of-kubernetes-3 |
| 41 | +mkdir gitops/joy-of-kubernetes-3/nfs |
| 42 | +mkdir gitops/joy-of-kubernetes-3/docker-registry |
| 43 | +mkdir gitops/joy-of-kubernetes-3/docker-registry-helm |
| 44 | +``` |
| 45 | + |
| 46 | +I went ahead and simplified the directories generator of our appset gitops/root-appset.yaml and applied that. This is hopefully the last time we will need to make changes to it in a while. Remember that we don't make mistakes, we just have happy little accidents. 😀 |
| 47 | + |
| 48 | +Instead of individual folders, each episode will have the same structure under gitops with one folder for each application neatly under its episode number. |
| 49 | + |
| 50 | +The first app we made is renamed "hello" and put under episode 1. |
| 51 | + |
| 52 | + |
| 53 | + |
| 54 | +This allows all apps to have names that make sense, but we can still search and filter by episodes. |
| 55 | + |
| 56 | + |
| 57 | + |
| 58 | +## Setting the scene, what our landscape will be |
| 59 | + |
| 60 | +The goal as we talked about is to have a private docker registry in kubernetes. This can be used to host images that we might want faster access to than loading from docker hub. Or simply images that we build ourselves but don't want to share with docker hub. |
| 61 | + |
| 62 | +Once the installation is complete, we will take a look at connecting docker to our new registry with docker login. So we will need to generate ourselves a password in the [bcrypt format](https://en.wikipedia.org/wiki/Bcrypt). |
| 63 | + |
| 64 | +Using the same registry container image that our installation in kubernetes will use, we can generate a bcrypt password with the htpasswd executable. I think this has been removed in later versions of the registry though. There are of course a million differnet ways you can generate this hash, but this one is elegant on top of docker and I like it. Let us know if you come up with a way that you would like to share! |
| 65 | + |
| 66 | +``` bash |
| 67 | +docker run --entrypoint htpasswd registry:2.7.0 -Bbn youruser yourplaintextpassword |
| 68 | +``` |
| 69 | + |
| 70 | + |
| 71 | + |
| 72 | +## Storage Classes: The colors on todays palette |
| 73 | + |
| 74 | +Storage classes in Kubernetes are like the various colors on our palette. They define different types of storage that we can use to create our masterpiece. These storage classes provide a way for administrators to describe the various storage options available within the cluster. |
| 75 | + |
| 76 | +### NFS is not secure |
| 77 | + |
| 78 | +For my painting today, I will be using the Network File System (NFS) as a storage class example. I added support for it on my NAS and created a shared folder. |
| 79 | + |
| 80 | + |
| 81 | + |
| 82 | +I have restricted access to the hostnames of my nodes, this is about the extent to which you can protect this type of service outside of not allowing traffic to the shared network. So keep that in mind if you want to bring this beyond our artistic realm. |
| 83 | + |
| 84 | +## Creation of the storage class |
| 85 | + |
| 86 | +To create an NFS storage class, we'll use [this simple helm chart](https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/). |
| 87 | + |
| 88 | +> gitops/joy-of-kubernetes-3/nfs/Chart.yaml |
| 89 | +
|
| 90 | +``` yaml |
| 91 | +apiVersion: v2 |
| 92 | +name: nfs |
| 93 | +description: A happy little storage class installation |
| 94 | +type: application |
| 95 | +version: 0.1.0 |
| 96 | +appVersion: "1.0" |
| 97 | +dependencies: |
| 98 | + - name: nfs-subdir-external-provisioner |
| 99 | + version: 4.0.18 |
| 100 | + repository: https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/ |
| 101 | +``` |
| 102 | +
|
| 103 | +> gitops/joy-of-kubernetes-3/nfs/values.yaml |
| 104 | +
|
| 105 | +``` yaml |
| 106 | +nfs-subdir-external-provisioner: |
| 107 | + nfs: |
| 108 | + server: NAS # The IP or hostname of our NFS Server |
| 109 | + path: /volume2/ds-pi-nfs # Where we mounted our shared storage |
| 110 | +``` |
| 111 | +
|
| 112 | +Once these files are pushed to git, an app named nfs with the label of episode 3 will show up. We can see that our configuration made its way into the deployment. |
| 113 | +
|
| 114 | + |
| 115 | +
|
| 116 | + |
| 117 | +
|
| 118 | +### Without Argo CD |
| 119 | +
|
| 120 | +If you want to use this helm chart but aren't following our gitops example, you can add it to your cluster this way. |
| 121 | +
|
| 122 | +``` powershell |
| 123 | +helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/ |
| 124 | +helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner ` |
| 125 | + --set nfs.server=NAS ` # The IP or hostname of our NFS Server |
| 126 | + --set nfs.path=/volume2/ds-pi-nfs # Where we mounted our shared storage |
| 127 | +``` |
| 128 | + |
| 129 | +With this storage class defined, we can now create our Persistent Volumes and Persistent Volume Claims that use this NFS storage. |
| 130 | + |
| 131 | + |
| 132 | +## Persistent Volume Claims: Choosing the Perfect Canvas |
| 133 | +Next, let's talk about Persistent Volume Claims (PVCs). PVCs are like choosing the perfect canvas for our painting. They allow our applications to request storage of a specific size and access mode. |
| 134 | + |
| 135 | +Here's an example of a PVC that requests storage from our NFS storage class: |
| 136 | + |
| 137 | +> gitops/joy-of-kubernetes-3/docker-registry/pvc.yaml |
| 138 | +
|
| 139 | +``` yaml |
| 140 | +apiVersion: v1 |
| 141 | +kind: PersistentVolumeClaim |
| 142 | +metadata: |
| 143 | + name: docker-registry-pvc |
| 144 | +spec: |
| 145 | + accessModes: |
| 146 | + - ReadWriteOnce |
| 147 | + volumeMode: Filesystem |
| 148 | + resources: |
| 149 | + requests: |
| 150 | + storage: 100Gi |
| 151 | + storageClassName: nfs-client |
| 152 | +``` |
| 153 | +
|
| 154 | +Our application can now use this PVC to store its data. |
| 155 | +
|
| 156 | +## A private Docker Registry with helm: Bringing our masterpiece together |
| 157 | +
|
| 158 | +When synchronizing the desired state from gitops into kubernetes using Argo CD we can use either plain manifests, helm charts, kustomize or kustomized helm. Since mixing helm charts and plain manifests doesn't work, we have split them up in two different directories. |
| 159 | +
|
| 160 | +> gitops/docker-registry-helm/Chart.yaml |
| 161 | +
|
| 162 | +``` yaml |
| 163 | +apiVersion: v2 |
| 164 | +name: images |
| 165 | +description: A majestic container image hosting |
| 166 | +type: application |
| 167 | +version: 0.1.0 |
| 168 | +appVersion: "1.0" |
| 169 | +dependencies: |
| 170 | + - name: docker-registry |
| 171 | + version: 2.2.2 |
| 172 | + repository: https://helm.twun.io |
| 173 | +``` |
| 174 | +
|
| 175 | +> gitops/docker-registry-helm/values.yaml |
| 176 | +
|
| 177 | +``` yaml |
| 178 | +images: |
| 179 | + replicaCount: 1 |
| 180 | + persistence: |
| 181 | + enabled: true |
| 182 | + size: 100Gi |
| 183 | + deleteEnabled: true |
| 184 | + storageClass: nfs-client |
| 185 | + existingClaim: docker-registry-pvc |
| 186 | + secrets: |
| 187 | + htpasswd: youruser:your-bcrypt-hash |
| 188 | +``` |
| 189 | +
|
| 190 | +## TLS and Ingress: Finishing touches |
| 191 | +I am so happy we have the cert-manager available to us from [the last episode](../2023-04-14-the-joy-of-kubernetes-2---let-us-encrypt.md). |
| 192 | +
|
| 193 | +This allows us to add highlights to our image container registry with TLS. Rather than ingress annotations we will perform the steps a bit differently today. |
| 194 | +
|
| 195 | +> gitops/joy-of-kubernetes-3/docker-registry/docker-registry-images-tls.yaml |
| 196 | +
|
| 197 | +``` yaml |
| 198 | +apiVersion: cert-manager.io/v1 |
| 199 | +kind: Certificate |
| 200 | +metadata: |
| 201 | + name: images-tls |
| 202 | +spec: |
| 203 | + secretName: images-tls |
| 204 | + dnsNames: |
| 205 | + - images.k3s.dsoderlund.consulting |
| 206 | + duration: 2160h0m0s # 90d |
| 207 | + renewBefore: 360h0m0s # 15d |
| 208 | + privateKey: |
| 209 | + algorithm: RSA |
| 210 | + encoding: PKCS1 |
| 211 | + size: 2048 |
| 212 | + usages: |
| 213 | + - "digital signature" |
| 214 | + - "key encipherment" |
| 215 | + issuerRef: |
| 216 | + name: letsencrypt-prod |
| 217 | + kind: ClusterIssuer |
| 218 | + group: cert-manager.io |
| 219 | +``` |
| 220 | +
|
| 221 | +Once pushed cert-manager will resolve a tls certificate for us. |
| 222 | +
|
| 223 | + |
| 224 | +
|
| 225 | +While it is doing its thing, we will continue with the ingress route. |
| 226 | +
|
| 227 | +> gitops/joy-of-kubernetes-3/docker-registry/docker-registry-ingress-route.yaml |
| 228 | +
|
| 229 | +``` yaml |
| 230 | +kind: IngressRoute |
| 231 | +apiVersion: traefik.containo.us/v1alpha1 |
| 232 | +metadata: |
| 233 | + name: docker-registry |
| 234 | +spec: |
| 235 | + entryPoints: |
| 236 | + - websecure |
| 237 | + routes: |
| 238 | + - match: Host(`images.k3s.dsoderlund.consulting`) |
| 239 | + kind: Rule |
| 240 | + services: |
| 241 | + - name: docker-registry-helm |
| 242 | + port: 5000 |
| 243 | + tls: |
| 244 | + secretName: images-tls |
| 245 | +``` |
| 246 | +
|
| 247 | +k3s which I use comes bundled with [traefik](https://github.com/traefik/traefik) which offers among other things ingress routes, allowing for some elegant mark up of traffic management. |
| 248 | +
|
| 249 | +The down side is that unlike ingress or a virtual service in istio, there is no pretty pictures getting drawn in Argo CD. But that is OK. |
| 250 | +
|
| 251 | + |
| 252 | +
|
| 253 | +## Summary |
| 254 | +
|
| 255 | +By painting with storage classes, we can make peristent volume claims vibrant and allow us to build stateful applications that shine. Once such application is our own private container registry. |
| 256 | +
|
| 257 | +And there we have it friends, all this guy needs now is our signature. (Signing in, get it?) |
| 258 | +
|
| 259 | +``` bash |
| 260 | +docker login images.k3s.dsoderlund.consulting |
| 261 | +``` |
| 262 | + |
| 263 | + |
| 264 | + |
| 265 | + |
| 266 | +## Further things you can try |
| 267 | + |
| 268 | +Here is an example with the application from episode 1 to show that your registry is allowing you push images. |
| 269 | + |
| 270 | +``` bash |
| 271 | +docker pull nginxdemos/hello:latest |
| 272 | +docker tag nginxdemos/hello:latest images.k3s.dsoderlund.consulting/nginxdemos/hello:latest |
| 273 | +docker push images.k3s.dsoderlund.consulting/nginxdemos/hello:latest |
| 274 | +``` |
| 275 | + |
| 276 | + |
| 277 | + |
| 278 | +You should now be able to explore pulling images from it with [pull secrets in kubernetes](https://kubernetes.io/docs/tasks/configure-pod-container/pull-image-private-registry/). |
| 279 | + |
| 280 | +## Thank you for joining |
| 281 | + |
| 282 | +That's all for now folks, I hope to see you again next week. |
0 commit comments