🇫🇷 Kit de survie K8S pour les dévs avec K3S - Partie 4: Utiliser une registry privée (unsecure)

Avant de passer à la partie Microservices, je me suis dit que j'aimerais utiliser une Docker Registry privée pour mes développements. Ainsi je peux "embarquer" des images à l'avance, puis utiliser ma registry au lieu de passer par le Docker hub (ce qui m'évitera beaucoup d'A/R sur internet). Je n'imaginais pas que je bataillerais autant. Du coup j'ai pris du retard dans mon programme, mais j'ai appris pas mal de choses (et j'ai eu beaucoup d'échanges sur le sujet avec Louis Tournayre)

Donc voici étape par étape ce que j'ai fait.

Création de la Docker registry

J'ai utilisé une docker registry "unsecure": donc pas de https, ni de certificats, ni d'authentification

J'ai à nouveau utilisé Multipass pour créer une VM qui va contenir ma registry. J4ai donc créé un script create.docker.registry.sh pour cela:

#!/bin/sh
eval $(cat registry.config)

multipass launch --name ${registry_name} --cpus 1 --mem 1G

IP=$(multipass info ${registry_name} | grep IPv4 | awk '{print $2}')

echo "👋 Initialize 🐳 ${registry_name}..."

multipass --verbose exec ${registry_name} -- sudo -- sh -c "
  apt-get update
  apt-get install -y docker.io
  docker run -d -p 5000:5000 --restart=always --name registry registry:2
"

# run this after every start of the registry
multipass --verbose exec ${registry_name} -- sudo -- sh -c "
  usermod -a -G docker ubuntu
  chmod 666 /var/run/docker.sock
  docker start registry
"

Mon fichier de configuration registry.config est le suivant:

registry_name="registry";
registry_domain="registry.dev.test";

Et je lis les variables comme cela: eval $(cat registry.config)

vous n'êtes pas obligés de faire comme moi

Et maintenant lancez ./create.docker.registry.sh et patientez un peu

Stop/Start

Pour stopper votre VM:

eval $(cat registry.config)
multipass stop ${registry_name}
"

Pour relancer votre VM:

eval $(cat registry.config)
multipass --verbose exec ${registry_name} -- sudo -- sh -c "
  usermod -a -G docker ubuntu
  chmod 666 /var/run/docker.sock
  docker start registry
"

Une fois la registry créée et lancée

Tout d'abord, ajoutez 192.168.64.20 registry.dev.test au fichier hosts de votre ordinateur hôte (192.168.64.20 est l'IP attribuée à ma VM, donc changez cette valeur en fonction de l'IP qui a été attribuée à votre VM). Vous pouvez obtenir l'IP comme ceci:

IP=$(multipass info ${registry_name} | grep IPv4 | awk '{print $2}')

Ensuite, pour pouvoir utiliser une "registry unsecure" avec votre client Docker, vous devez aller modifier les paramètres de celui-ci. Allez dans Preferences / Docker Engine et modifiez la configurations pour déclarer/ajouter votre registry:

{
  "insecure-registries": [
  "registry.dev.test:5000"
  ]
}

Appliquez les changements, le client Docker va re-démarrer.

Tester votre registry

Maintenant vous pouvez "récupérer" une image docker du Docker Hub, la taguer et la pousser vers votre propre registry:

docker pull node:12.0-slim
docker tag node:12.0-slim registry.dev.test:5000/node:12.0-slim
docker push registry.dev.test:5000/node:12.0-slim

Si vous faites:

curl http://registry.dev.test:5000/v2/_catalog

Vous obtiendrez:

{"repositories":["node"]}

Et avec:

curl registry.dev.test:5000/v2/node/tags/list

Vous aurez:

{"name":"node","tags":["12.0-slim"]}

Rendre votre registry visible à l'intérieur de K3S

Et c'est à partir de là que c'est devenu plus dur... Et si vous avez plus simple, je vous écouterais avec intérêt.

registries.yaml

D'après la documentation de K3S, sur chaque noeud du cluster il va falloir modifier ou créer ce fichier /etc/rancher/k3s/registries.yaml avec le contenu suivant:

mirrors:
  "registry.dev.test:5000":
    endpoint:
      - "http://registry.dev.test:5000"

/etc/hosts

Ensuite sur chaque VM contenant un noeud du cluster, il va fallor modifier le fichier hosts pour ajouter l'entrée correspondant à la VM de la registry (par exemple: 192.168.64.20 registry.dev.test).

Mais attention avec Multipass, le système est configuré pour gérer automatiquement le fichier hosts et le générer automatiquement à partir d'un template à chaque re-démarrage. Dinc si vous faites une modification, vous la perdrez.

A la place il faut aller modifier (sur chaque noeud du cluster) le fichier /etc/cloud/templates/hosts.debian.tmpl pour y ajouter 192.168.64.20 registry.dev.test (avec l'IP de votre VM).

Ensuite: CoreDNS et ConfigMap

Ce n'est pas fini, normalement il faudrait modifier la ConfigMap de CoreDNS pour ajouter l'entrée ci-dessous qui permettra de contacter la registry à partir des autres pods:

hosts registry.dev.test.hosts {
  hosts {
      192.168.64.20 registry.dev.test
      fallthrough
  }
}

Normalement il faudrait récupérer la ConfigMap comme cela:

kubectl get configmap coredns -n kube-system -o yaml > coredns.yaml

cette commande va générer un fichier coredns.yaml décrivant le configmap de coredns

Modifier l'entrée data.Corefile, faire un kubectl apply -f coredns.yaml et enfin supprimer le pod qui va être recréé automatiquement et prendre en compte les modifications (comme ceci kubectl delete pod --selector=k8s-app=kube-dns -n kube-system)

✋🐛 Mais il y a un bug, ça ne fonctionne pas (ou je ne l'ai pas fait comme il faut)

A la place, il ne faut pas ajouter une rubrique hosts mais modifier l'existante:

Corefile: |-
  .:53 {
      errors
      health
      ready
      kubernetes cluster.local in-addr.arpa ip6.arpa {
        pods insecure
        upstream
        fallthrough in-addr.arpa ip6.arpa
      }
      hosts /etc/coredns/NodeHosts {
        192.168.64.20 registry.dev.test # 👋👋👋 ajouter l'entrée ici
        reload 1s
        fallthrough
      }
      prometheus :9153
      forward . /etc/resolv.conf
      cache 30
      loop
      reload
      loadbalance
  }

puis faire kubectl apply -f coredns.yaml et enfin kubectl delete pod --selector=k8s-app=kube-dns -n kube-system

✋🐛 Mais il y a un autre bug, au redémarrage du cluster la ConfigMap redevient comme avant 😡

La solution (la grosse rustine pour le moment)

  • J'ai installé yq qui permet de manipuler du yaml via le shell https://mikefarah.gitbook.io/yq/
  • J'ai créé un fichier coredns.patch.yaml avec le contenu suivant:
data:
  Corefile: |-
    .:53 {
        errors
        health
        ready
        kubernetes cluster.local in-addr.arpa ip6.arpa {
          pods insecure
          upstream
          fallthrough in-addr.arpa ip6.arpa
        }
        hosts /etc/coredns/NodeHosts {
          192.168.64.20 registry.dev.test
          reload 1s
          fallthrough
        }
        prometheus :9153
        forward . /etc/resolv.conf
        cache 30
        loop
        reload
        loadbalance
    }

✋✋✋ Vous remarquez l'entrée correspondant à ma registry

Et j'ai ensuite créé le script suivant update-coredns.sh:

#!/bin/bash
export KUBECONFIG=$PWD/k3s.yaml
rm coredns.yaml
kubectl get configmap coredns -n kube-system -o yaml > coredns.yaml

corefilepatch=$(yq read coredns.patch.yaml  'data.Corefile')

yq delete -i coredns.yaml 'data.Corefile'
yq write -i coredns.yaml 'data.Corefile' "${corefilepatch}"

kubectl apply -f coredns.yaml
kubectl delete pod --selector=k8s-app=kube-dns -n kube-system

Donc après le re-démarrage de mon cluster, je lance ./update-coredns.sh qui va mettre à jour la ConfigMap avec les bonnes informations et appliquer les modifications.

Je sais, c'est lourd, mais ça fonctionne 😉 (suivre ceci https://github.com/rancher/k3s/pull/743 qui pourrait nous simplifier la vie).

Tout ça pourquoi?

Nous avons donc notre registry privée, et maintenant nous allons l'utiliser pour le déploiement de notre application.

Modifiez votre fichier Dockerfile:

#FROM node:12.0-slim
FROM registry.dev.test:5000/node:12.0-slim

COPY . .
RUN npm install
CMD [ "node", "index.js" ]

Et maintenant pour déployer l'application:

git add .; git commit -m "update"

export KUBECONFIG=../create-cluster/k3s.yaml

export NAMESPACE="training"
export DOCKER_USER="registry.dev.test:5000" # ✋ en fait c'est notre seul changement
export CLUSTER_IP="192.168.64.17"

export CONTAINER_PORT=${CONTAINER_PORT:-8080}
export EXPOSED_PORT=${EXPOSED_PORT:-80}
export APPLICATION_NAME=$(basename $(git rev-parse --show-toplevel))
export DOCKER_USER="${DOCKER_USER}"
export NAMESPACE="${NAMESPACE}"
export IMAGE_NAME="${APPLICATION_NAME}-img"
export TAG=$(git rev-parse --short HEAD)
export IMAGE="${DOCKER_USER}/${IMAGE_NAME}:${TAG}"
export CLUSTER_IP="${CLUSTER_IP}"
export BRANCH=$(git symbolic-ref --short HEAD)
export HOST="${APPLICATION_NAME}.${BRANCH}.${CLUSTER_IP}.nip.io"

docker build -t ${IMAGE_NAME} .
docker tag ${IMAGE_NAME} ${IMAGE}
docker push ${IMAGE}

envsubst < ./kube/deploy.template.yaml > ./kube/deploy.${TAG}.yaml

kubectl apply -f ./kube/deploy.${TAG}.yaml -n ${NAMESPACE}
echo "🌍 http://${HOST}"

Remarque: par rapport aux articles précédents, ce qui change c'est la valeur de DOCKER_USER

Si vous utilisez notre plugin kubectl easy, il suffira de faire ceci:

git add .; git commit -m "update"

export KUBECONFIG=../create-cluster/k3s.yaml
NAMESPACE="training" \
DOCKER_USER="registry.dev.test:5000" \
CLUSTER_IP="192.168.64.17" \
kubectl easy deploy

Et une fois déployée, vous pouvez vérifier qu'il y a l'image du container de votre application qui est présente dans votre registry (faites un curl http://registry.dev.test:5000/v2/_catalog).

Voilà, c'était un peu "lourd" à faire mais plutôt pratique. Si ce n'est pas clair (j'ai un peu eu de mal à écrire cet article), n'hésitez pas à commenter, où à me contacter.

A bientôt pour la suite.

Last Articles

Last Updated: 28/02/2020 à 13:16:31