Un registry Docker privé, c’est pratique ! Mais s’il est en plus multiarch, c’est parfait ! Pourquoi faire ? Pour par exemple pouvoir installer avec le même docker pull une image x86 ou une image arm.

Mais encore ? Si votre cluster Kubernetes est composé de noeuds x86 et de noeuds arm et que vous faites un daemonset, il est plutôt indispensable de pouvoir pointer sur une image unique ;-) (c’est aussi valable pour du Swarm, du déploiement via du compose…).

Let’s Encrypt

Pour générer un certificat Let’s Encrypt pour votre registry, vous aurez besoin du client certbot. Vérifiez s’il existe dans les paquets de votre distribution ou clonez le repository Github :

  • Clonez le repository : git clone https://github.com/certbot/certbot.git
  • Dans le répertoire certbot, utilisez certbot-auto pour générer un certificat :

    ./certbot-auto certonly --standalone --email admin@example.com -d mon-registry.com
    
  • Votre certificat sera stocké dans /etc/letsencrypt.

Installer le registry Docker v2

  • Pour persister les données du registry sur le disque et stocker sa configuration, je prépare son homedir :

    mkdir -p /opt/registry/{auth,certs,data}
    
  • Dans le répertoire auth, je vais générer les couples user/pass, au format htpasswd, autorisés sur mon registry. Par exemple, si je reprends l’exemple de la doc docker :

    docker run --entrypoint htpasswd registry:2 -Bbn testuser1 testpassword1 >> auth/htpasswd
    docker run --entrypoint htpasswd registry:2 -Bbn testuser2 testpassword2 >> auth/htpasswd
    
  • Dans le répertoire certs, on retrouve la chaine et la clé du certificat Let’s Encrypt :

    cp /etc/letsencrypt/live/r3gistry.ddns.net/privkey.pem /opt/registry/certs/domain.key
    cp /etc/letsencrypt/live/r3gistry.ddns.net/fullchain.pem /opt/registry/certs/domain.crt
    
  • Le répertoire data contiendra les données de votre registry…

  • Je lance mon registry en ligne de commande avec :

    docker run -d -p 5000:5000 --restart=always --name registry \
    -e "REGISTRY_AUTH=htpasswd" \
    -e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \
    -e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
    -e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/domain.crt \
    -e REGISTRY_HTTP_TLS_KEY=/certs/domain.key \
    -v /opt/registry/auth:/auth \
    -v /opt/registry/certs:/certs \
    -v /opt/registry/data:/data \
    registry:2
    

Une image multiarch

Mon salut est venu a la lecture de cette doc. Dans la version 2 / schéma 2 du manifeste des images Docker, il est possible de référencer derrière une image Docker une liste d’images Docker qui peuvent être spécifiques, par exemple, a une architecture. En clair, avec ce manifeste :

image: r3gistry:5000/common/prometheus:1.1.3
manifests:
  -
    image: r3gistry:5000/common/prometheus:1.1.3-arm
    platform:
      architecture: arm
      os: linux
  -
    image: r3gistry:5000/common/prometheus:1.1.3-amd64
    platform:
      architecture: amd64
      os: linux

… je vais pouvoir faire un docker pull r3gistry:5000/common/prometheus:1.1.3.

Si je suis sur du intel, ca revient a faire un docker pull r3gistry:5000/common/prometheus:1.1.3-amd64 et mon raspberry fera lui un docker pull r3gistry:5000/common/prometheus:1.1.3-arm !

Installation de manifest-tool

Reste a injecter ce manifeste dans le registry… Et il y a un outil pour ca. Il faudra vous construire le binaire. Pour ma part, j’ai fait comme ca :

  • Je clone le repo :

    git clone https://github.com/estesp/manifest-tool /go/src/github.com/estesp/manifest-tool/
    
  • Il y a un Makefile et un Dockerfile mais j’ai fait simplement :

    docker run -it -v /go:/go golang go build -o manifest github.com/estesp/manifest-tool
    
  • Et j’installe le binaire :

    mv /go/manifest /usr/local/bin
    

Injection du manifeste

La suite est simple :

  • Je pousse mon image amd64 : docker push r3gistry:5000/common/prometheus:1.1.3-amd64
  • Je pousse mon image arm : docker push r3gistry:5000/common/prometheus:1.1.3-arm
  • Je pousse mon manifest (celui vu plus haut) : manifest pushml prometheus.yaml. J’obtiens cette sortie :
$ /usr/local/bin/manifest pushml prometheus.yaml
INFO[0000] Retrieving digests of images...
INFO[0000] Image "r3gistry:5000/common/prometheus:1.1.3-arm" is digest sha256:d6; size: 2192
INFO[0001] Image "r3gistry:5000/common/prometheus:1.1.3-amd64" is digest sha256:63; size: 1986
Digest: sha256:5da2e32fd703d4743808e76e4

Si je veux consulter la liste des manifestes de cette image :

$ /usr/local/bin/manifest inspect r3gistry:5000/common/prometheus:1.1.3
r3gistry:5000/common/prometheus:1.1.3 is a manifest list containing the following 2 manifest references:
1    Mfst Type: application/vnd.docker.distribution.manifest.v2+json
1       Digest: sha256:d6
1  Mfst Length: 2192
1     Platform:
1           -      OS: linux
1           -    Arch: arm
1           - Variant:
1           - Feature:
1     # Layers: 10
         layer 1: digest = sha256:38
         layer 2: digest = sha256:a3
         layer 3: digest = sha256:bc
         layer 4: digest = sha256:0d
         layer 5: digest = sha256:b7
         layer 6: digest = sha256:49
         layer 7: digest = sha256:3c
         layer 8: digest = sha256:46
         layer 9: digest = sha256:2c
         layer 10: digest = sha256:5e

2    Mfst Type: application/vnd.docker.distribution.manifest.v2+json
2       Digest: sha256:63
2  Mfst Length: 1986
2     Platform:
2           -      OS: linux
2           -    Arch: amd64
2           - Variant:
2           - Feature:
2     # Layers: 9
         layer 1: digest = sha256:9a
         layer 2: digest = sha256:f3
         layer 3: digest = sha256:a2
         layer 4: digest = sha256:45
         layer 5: digest = sha256:4b
         layer 6: digest = sha256:7c
         layer 7: digest = sha256:4c
         layer 8: digest = sha256:80
         layer 9: digest = sha256:35

Ce n’est pas encore parfait et il y a encore du boulot pour avoir un registry complètement compatible avec du multiarch mais cette solution m’a permis d’utiliser simplement les daemonsets Kubernetes sur mon cluster composé de noeuds arm et amd64 la ou avant, je devais jouer avec des règles iptables pour avoir le arm sur un registry et le amd64 sur un autre !