☸ Namespace#
je vais installer tout ce qui concerne Docker-registry dans son propre namespace
répertoire appelé docker-registry
. Nous allons donc d’abord créer cela :
kubectl create namespace docker-registry
💾 Stockage#
Étant donné que nous allons stocker les images Docker dans notre registre personnel pour les utiliser ultérieurement avec OpenFaaS, il serait dommage qu’elles disparaissent à chaque fois que le pod se reprogramme vers un autre nœud.
Nous avons besoin d’un stockage persistant qui suit notre pod partout et lui fournit les mêmes données à tout moment.
Si vous avez suivi ma configuration, vous devriez déjà avoir installé Longhorn.
⛃ Réclamation de volume persistant#
Un volume PersistentVolumeClaim
est utilisé pour monter un PersistentVolume
dans un Pod
. Les PersistentVolumeClaims permettent aux utilisateurs de « réclamer » un stockage durable (tel qu’un GCE PersistentDisk ou un volume iSCSI) sans connaître les détails de l’environnement cloud particulier.
Nous allons créer un nouveau dossier appelé docker-registry
et un nouveau fichier pvc.yaml
dedans.
cd
mkdir docker-registry
cd docker-registry
nano pvc.yaml
Dans notre pvc.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: longhorn-docker-registry-pvc
namespace: docker-registry
spec:
accessModes:
- ReadWriteOnce
storageClassName: longhorn
resources:
requests:
storage: 15Gi
Nous demandons à Kubernetes d’utiliser Longhorn comme classe de stockage et de réclamer/créer 15 Go d’espace disque pour le stockage persistant. Nous l’appellerons longhorn-docker-registry-pvc
, et nous le référencerons sous ce nom plus tard.
👉🏻 Pour en savoir plus sur les volumes, consultez la documentation officielle ici : https://kubernetes.io/docs/concepts/storage/persistent-volumes/
Appliquez nos pvc.yaml
:
kubectl apply -f pvc.yaml
Et vérifiez
root@control0:/root/docker-registry# kubectl get pvc -n docker-registry
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
longhorn-docker-registry-pvc Bound pvc-2cdadf01-06be-4702-9a75-08f5d7754f2c 15Gi RWO longhorn <unset> 19d
#longhorn devrait automatiquement créer le PV ( physical volume )
root@control00:/root/docker-registry# kubectl get pv -n docker-registry
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-2cdadf01-06be-4702-9a75-08f5d7754f2c 15Gi RWO Delete Bound docker-registry/longhorn-docker-registry-pvc longhorn
Cool, cool : maintenant nous avons du stockage ! (Le statut peut être différent, quelque chose comme : Non attaché)
📑 Création de certificats pour le registre Docker#
J’ai littéralement passé plus de 24 heures pour faire fonctionner cela avec la configuration que j’ai, y compris OpenFaaS et ainsi de suite… donc j’espère que je n’ai oublié aucune étape. 😄
Générez des certificats dans votre répertoire docker-register :
#Installez openssl si ce n'est pas déjà le cas
sudo apt-get install openssl
🔐 Générer un certificat et une clé#
openssl req -x509 -newkey rsa:4096 -sha256 -days 3650 -nodes -keyout registry.key -out registry.crt -subj "/CN=registry.cube.local" -addext "subjectAltName=DNS:registry.cube.local,DNS:*.cube.local,IP:192.168.1.202"
/etc/hosts
pour le registre sera 192.168.1.202 registry registry.cube.local
. Je sais quelle IP ce sera, car nous la définirons plus tard (vous vous souvenez de metalLB ?), et il n’y a pas de serveur DNS, donc chaque nœud devra l’avoir dans /etc/hosts. Appelez-le comme vous voulez, mais ajoutez les noms corrects dans les paramètres subjectAltName
. Sans cela, il pourrait y avoir des problèmes où certains outils se plaignent de certificats mal signés et de SAN manquant. Quelque chose comme : x509: cannot validate certificate for <IP> because it doesn't contain any IP SANs
Vous avez maintenant deux nouveaux fichiers dans votre docker-registry répertoire :
root@control00:~/docker-registry# ls | grep regis
registry.crt
registry.key
🛡️ Ajout de TLS au secret Kubernetes#
Avant, j’avais un disque séparé dans Longhorn pour les certificats et je devais copier les certificats là-bas, puis les attacher au pod. Cela fonctionnait bien, mais il existe une solution plus simple. Le secret Kubernetes est un moyen de stocker les secrets dans le cluster.
Créez un secret dans votre docker-registry
espace de noms à partir des fichiers registry.crt
et registry.key
:
kubectl create secret tls docker-registry-tls-cert -n docker-registry --cert=registry.crt --key=registry.key
tls
. Il s’agit d’un type de secret spécial utilisé pour stocker les certificats TLS. Pour en savoir plus sur les secrets, cliquez ici : https://kubernetes.io/docs/concepts/configuration/secret/En substance, considérez-le comme un stockage interne pour les secrets du cluster. Ceux-ci sont ensuite placés sous forme de fichiers à l’intérieur du pod. Vous les verrez dans les fichiers de déploiement.
Si vous voulez voir vos secrets, vous pouvez le faire comme ceci :
kubectl get secret docker-registry-tls-cert -o yaml -n docker-registry
Vous pouvez également l’enregistrer dans un fichier à partir de cette sortie et l’appliquer de la même manière que n’importe quel fichier yaml dont vous disposez. Supprimez-les simplement de metadata
:
metadata:
creationTimestamp: "2022-05-25T18:26:52Z" <-- REMOVE
name: docker-registry-tls-cert
namespace: docker-registry <-- REMOVE
resourceVersion: "55302" <-- REMOVE
uid: 35208da3-19f1-4259-bfd1-5b276bb5948c <-- REMOVE
Vous pouvez alors avoir secret.yaml
un fichier et le stocker dans git ou autre. Pas besoin de créer manuellement le secret. Cela vous aidera également si vous l’utilisez plus tard avec le déploiement gitops d’Argo CD.
🚀 Déploiement#
Nous allons maintenant créer un déploiement simple du registre Docker et le laisser libre sur notre cluster Kubernetes.
Créez un fichier dans votre répertoire de registre Docker appelé docker.yaml
:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: registry
namespace: docker-registry
spec:
replicas: 1
selector:
matchLabels:
app: registry
template:
metadata:
labels:
app: registry
name: registry
spec:
nodeSelector:
node-type: worker
containers:
- name: registry
image: registry:2
ports:
- containerPort: 5000
env:
- name: REGISTRY_HTTP_TLS_CERTIFICATE
value: "/certs/tls.crt"
- name: REGISTRY_HTTP_TLS_KEY
value: "/certs/tls.key"
volumeMounts:
- name: lv-storage
mountPath: /var/lib/registry
subPath: registry
- name: certs
mountPath: /certs
volumes:
- name: lv-storage
persistentVolumeClaim:
claimName: longhorn-docker-registry-pvc
- name: certs
secret:
secretName: docker-registry-tls-cert
À quoi faut-il faire attention :
- namespace - j’ai spécifié
docker-registry
. - replicas - j’en utilise 1, donc il n’y aura qu’un seul registre Docker en cours d’exécution.
- nodeSelector - Comme mentionné précédemment lors de la configuration de mon Kubernetes, j’ai étiqueté les nœuds de travail avec node-type=worker. Cela permettra au déploiement de privilégier ces nœuds.
- image - Cela indiquera à Kubernetes de télécharger le registre : 2 à partir du hub Docker officiel.
- containerPort - Quel port le conteneur exposera/utilisera.
- volumeMounts - Définition de l’endroit dans le pod où nous allons monter notre stockage persistant.
- volumes - Définition où nous nous référons au PVC que nous avons créé auparavant.
- env - Ceci sera transmis en tant que variables d’environnement dans le conteneur et utilisé par le registre Docker.
⛃ Montage de volume#
En bas, vous pouvez voir que nous montons notre stockage persistant, mais nous montons également nos certificats. Nous nommons nos volumes lv-storage
et certs
. Veuillez noter que le certs
volume est un secret et non une revendication de volume persistant.
⛁ volumes#
Dans cette section, nous demandons au POD de monter notre longhorn-docker-registry-pvc
sur /var/lib/registry
. Comme ici, normalement, les images en direct sont téléchargées dans le registre. Nous montons également nos certificats sur /certs
.
⚙️ env#
Le registre Docker dispose d’options que vous pouvez influencer en définissant des variables d’environnement. Dans ce cas, nous définissons les variables d’environnement pour que le registre utilise nos certificats et leurs emplacements. Donc dans /certs/tls.crt
et /certs/tls.key
.
➡️ Appliquez le déploiement et attendez un peu que tout soit en ligne.#
kubectl apply -f docker.yaml
Vérifiez avec :
# Deployment
root@control00:/root/docker-registry# kubectl get deployments -n docker-registry
NAME READY UP-TO-DATE AVAILABLE AGE
registry 1/1 1 1 21s
# Pods ( Il devrait y en avoir qu'un )
root@control00:/root/docker-registry# kubectl get pods -n docker-registry
NAME READY STATUS RESTARTS AGE
registry-6fdc5fc5d-npslq 1/1 Running 0 29s
Nous n’avons pas encore terminé. Nous devons également créer un service pour rendre le registre disponible à l’échelle du cluster, et idéalement sur la même adresse IP/le même nom à tout moment, quel que soit le nœud sur lequel il s’exécute.
🛠️ Service#
Encore une fois, si vous avez suivi mes paramètres réseau, nous avons configuré metalLB pour nous fournir des adresses IP externes pour les pods. Par conséquent, nous l’utilisons comme service LoadBalancer pour notre registre Docker.
Dans votre dossier docker-registry
, créez service.yaml
et collez ce qui suit :
apiVersion: v1
kind: Service
metadata:
name: registry-service
namespace: docker-registry
spec:
selector:
app: registry
type: LoadBalancer
ports:
- name: docker-port
protocol: TCP
port: 5000
targetPort: 5000
loadBalancerIP: 192.168.1.202
À quoi faut-il faire attention :
- kind - Service, juste pour permettre à Kubernetes de savoir ce que nous créons.
- name - Juste un nom pour notre service.
- namespace - J’ai spécifié
docker-registry
, car le déploiement que nous ciblons se trouve dans cet espace de noms. - selector and app - La valeur de ceci est extraite de notre déploiement où ceci est défini :
app: registry
. - type - Ici, nous disons à Kubernetes que nous voulons LoadBalancer (MetalLB).
- ports - Nous définissons
port
sur notre IP externe ettargetPort
(c’est le port à l’intérieur de l’application/conteneur). - loadBalancerIP - Ceci est facultatif, mais je l’ai inclus ici. Cela nous permettra de spécifier quelle IP nous voulons pour l’IP externe. Si vous supprimez cette ligne, MetalLB attribuera la prochaine IP libre du pool que nous lui avons alloué.
Appliquer le service :
kubectl apply -f service.yaml
Attendez quelques secondes pour obtenir l’IP et vérifiez :
root@control00:/root/docker-registry# kubectl get svc -n docker-registry
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
registry-service LoadBalancer 10.43.5.16 192.168.1.202 5000:32096/TCP 7m48s
Fantastique ! Le service semble être opérationnel avec le port externe 5000. Concernant le port 32096 qui se trouve derrière, cela peut être différent pour vous. Il est attribué à un nœud sur lequel le pod est en cours d’exécution. En substance, c’est comme ça : IP externe : 5000 -> Nœud où se trouve le pod/conteneur : 32096 -> conteneur à l’intérieur : 5000. J’espère que cela a du sens 😄
Pour obtenir plus d’informations sur le service, nous pouvons demander à Kubectl de nous le décrire :
root@control00:/root/docker-registry# kubectl get svc -n docker-registry
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
registry-service LoadBalancer 10.43.5.16 192.168.1.202 5000:32096/TCP 7m48s
root@control00:/root/docker-registry# kubectl describe svc registry-service -n docker-registry
Name: registry-service
Namespace: docker-registry
Labels: <none>
Annotations: <none>
Selector: app=registry
Type: LoadBalancer
IP: 10.43.5.16
IP: 192.168.1.202
LoadBalancer Ingress: 192.168.1.202
Port: docker-port 5000/TCP
TargetPort: 5000/TCP
NodePort: docker-port 32096/TCP
Endpoints: 10.42.8.13:5000
Session Affinity: None
External Traffic Policy: Cluster
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal IPAllocated 77s (x537 over 11m) metallb-controller Assigned IP "192.168.1.202"
Normal nodeAssigned 76s (x539 over 11m) metallb-speaker announcing from node "cube01"
➕ Ajouter un certificat root aux nœuds#
Actuellement, nous avons un registre Docker compatible SSL ou TLS (Fuck it, je l’appellerai désormais HTTPS) exécuté sur Kubernetes. Cependant, puisque nous créons le certificat, nous devons l’ajouter en tant que certificat racine à nos nœuds, chacun d’entre eux ! Si nous ne le faisons pas, tout service qui essaie de l’utiliser se plaindra d’un certificat signé par une autorité inconnue ou quelque chose de similaire. Nous allons donc nous faire passer pour l’autorité comme ceci :
#Sur dietpi
ansible cube -b -m copy -a "src=registry.crt dest=/usr/local/share/ca-certificates/registry.crt"
ansible cube -b -m copy -a "src=registry.key dest=/usr/local/share/ca-certificates/registry.key"
ansible all -b -m shell -a "update-ca-certificates"
En substance, sur chaque nœud, vous devez copier notre registry.crt
dans /usr/local/share/ca-certificates/
, et exécuter update-ca-certificates
, ce qui ajoutera notre certificat en tant que certificat racine. Cela trompera la vérification en lui faisant croire que nous sommes l’autorité pour le certificat (ce qui est le cas) et elle ne se plaindra pas.
Un exemple de réalisation manuelle :
root@control00:~ sudo cp registry.* /usr/local/share/ca-certificates/
root@control00:~ sudo update-ca-certificates
Updating certificates in /etc/ssl/certs...
1 added, 0 removed; done.
Running hooks in /etc/ca-certificates/update.d...
done.
🖧 Faire en sorte que K3s utilise un registre Docker privé#
Je sais, je sais. Cela prend une éternité.
Voici d’où j’ai obtenu mes informations : https://rancher.com/docs/k3s/latest/en/installation/private-registry/
Ajoutez un nom DNS à /etc/hosts
chaque nœud
, je l’ai nommé comme ceci :
192.168.1.202 registry registry.cube.local
Une bonne idée est d’avoir le /etc/hosts
joli et synchronisé entre tous les nœuds, je vais donc l’ajouter une fois dans le nœud control00 et le déplacer vers tous les nœuds à l’aide d’Ansible.
echo "192.168.1.202 registry registry.cube.local" >> /etc/hosts
ansible cube -b -m copy -a "src=https://homelab.scasse.com/etc/hosts dest=/etc/hosts"
Maintenant, dites-le à k3s. En tant que root, créez le fichier /etc/rancher/k3s/registries.yaml
:
vi /etc/rancher/k3s/registries.yaml
Ajoutez ce qui suit :
mirrors:
registry.cube.local:5000:
endpoint:
- "https://registry.cube.local:5000"
configs:
registry.cube.local:
tls:
ca_file: "/usr/local/share/ca-certificates/registry.crt"
key_file: "/usr/local/share/ca-certificates/registry.key"
Envoyez-le à chaque nœud de contrôle du cluster.
#Assurons-nous que le répertoire existe
ansible cube -b -m file -a "path=/etc/rancher/k3s state=directory"
📤 Copiez les fichiers#
ansible cube -b -m copy -a "src=https://homelab.scasse.com/etc/rancher/k3s/registries.yaml dest=/etc/rancher/k3s/registries.yaml"
🧪 Test du registre Docker#
Suivez le guide pour installer Docker à partir d’ici :
Nous allons télécharger un conteneur Ubuntu à partir du registre officiel Docker, le réétiqueter et le pousser vers notre registre.
root@control00:~# docker pull ubuntu:16.04
16.04: Pulling from library/ubuntu
3e30c5e4609a: Pull complete
be82da0c7e99: Pull complete
bdf04dffef88: Pull complete
2624f7934929: Pull complete
Digest: sha256:3355b6e4ba1b12071ba5fe9742042a2f10b257c908fbdfac81912a16eb463879
Status: Downloaded newer image for ubuntu:16.04
docker.io/library/ubuntu:16.04
root@control00:~# docker tag ubuntu:16.04 registry.cube.local:5000/my-ubuntu
root@control00:~# docker push registry.cube.local:5000/my-ubuntu
The push refers to repository [registry.cube.local:5000/my-ubuntu]
3660514ed6c6: Pushed
2f33c1b8271f: Pushed
753fcdb98fb4: Pushed
1632f6712b3f: Pushed
latest: digest: sha256:2e459e7ec895eb5f94d267fb33ff4d881699dcd6287f27d79df515573cd83d0b size: 1150
# Vérifiez avec curl
root@control00:~# curl https://registry.cube.local:5000/v2/_catalog
{"repositories":["my-ubuntu"]}
Ouais ! Ça a marché
J’espère que c’est tout ; félicitations pour être arrivé jusqu’ici. Maintenant, prends un café et offre-moi-en un aussi 🙂