Wir benutzen Renovate um alle Stellen, an denen wir Container-Images verwenden (z.B. Kubernetes-Manifeste, Puppet-Code und Gitlab-Pipelines), aktuell zu halten. Renovate sammelt Image-Referenzen aus allen unseren Repos, prüft dann die entsprechende Container-Registry auf neue Versionen der jeweiligen Images und ändert die Angabe im Repo.
no matching manifest for linux/amd64
Dabei haben wir eine sehr seltsame Feststellung gemacht. Renovate hat z.B. diese Änderung gemacht:
Aber bei der Ausführung gab es einen Fehler:
ERROR: Job failed: failed to pull image "docker:27.1.0-dind@sha256:eb37f58646a901dc7727cf448cae36daaefaba79de33b5058dab79aa4c04aefb" with specified policies [if-not-present]: no matching manifest for linux/amd64 in the manifest list entries (manager.go:250:0s)
OK, es ist kein Image für linux/amd64 mehr enthalten? Wir haben ja gehört, dass ARM populärer wird für Server, aber dass Docker plötzlich ihre Images nicht mehr für AMD64 anbietet, wäre dann doch eine Überraschung. Allerdings: linux/arm64 wird auch nicht unterstützt:
docker: no matching manifest for linux/arm64/v8 in the manifest list entries.
Es wurde noch seltsamer: Der Digest des Image, eb37f58646a901dc7727cf448cae36daaefaba79de33b5058dab79aa4c04aefb, tauchte auch bei anderen Image-Versionen und völlig anderen Images auf:
- docker:27.1.1
- docker:27.1.1-dind
- wordpress:6.6.1
- rabbitmq:3.13.1-management
Was war hier los?
Aufbau von Multi-Arch-Images
Es ist wichtig zu verstehen, wie sog. Multi-Arch-Images bei Docker funktionieren: sie sind einfach nur eine Liste der konkreten Images für die verschiedenen Architekturen. Mit dem Tool crane können wir das abfragen für unser problematisches Image:
$ crane manifest docker:27.1.0-dind@sha256:eb37f58646a901dc7727cf448cae36daaefaba79de33b5058dab79aa4c04aefb | jq '[.manifests[].platform]'
[]
OK, es wird einfach gar nichts unterstützt?! Zum Vergleich ein funktionierendes Image:
$ crane manifest docker:27.0.3-dind@sha256:75f620cbf8e87543ec1fb0bf98fa2cfde8f684308dafb6c50cc75f3a235fa1fc | jq '[.manifests[].platform]'
[
{
"architecture": "amd64",
"os": "linux"
},
[…]
{
"architecture": "arm64",
"os": "linux",
"variant": "v8"
},
[…]
]
D.h. unser Verständnis des Formats ist richtig – im Manifests sollten die Architekturen aufgelistet sein.
Die Funktionsweise der Multi-Arch-Images erklärt auch, wieso wir den Digest an mehreren Stellen gesehen haben: der Digest ist nur der SHA256-Hash eines leeren Multi-Arch-Image, komplett unabhängig vom Image-Namen/Tag:
$ crane manifest docker:27.1.0-dind@sha256:eb37f58646a901dc7727cf448cae36daaefaba79de33b5058dab79aa4c04aefb | sha256sum
eb37f58646a901dc7727cf448cae36daaefaba79de33b5058dab79aa4c04aefb -
$ crane manifest rabbitmq:3.13.1-management@sha256:eb37f58646a901dc7727cf448cae36daaefaba79de33b5058dab79aa4c04aefb | sha256sum
eb37f58646a901dc7727cf448cae36daaefaba79de33b5058dab79aa4c04aefb -
Aber wieso veröffentlicht Docker Inc leere Images?
Unsere Recherche führte uns zu verwandten Problemen — Fälle, wo neue Image-Versionen nicht für alle Architekturen verfügbar waren. Wir selbst hatten schon mal das Problem, dass bei einem Update aus einem amd64-Image ein i386-Image wurde. Wie kommt das?
Der Grund ist, dass Docker (die Firma) neue Images nicht gemeinsam für alle Architekturen pusht, sondern dass die Liste schrittweise auf dem Docker Hub aufgebaut wird. Als erstes pushen sie eine leere Liste, und diese leere Liste wird auch bereits getaggt, so dass Renovate sie findet.
Erst dann laufen die Builds für die verschiedenen Architekturen, wodurch die Liste jeweils erweitert wird (und es jedes Mal einen neuen Digest gibt). Je nachdem in welcher Reihenfolge die Builds fertig werden, und wann man das Image abruft, bekommt man andere Architekturen. amd64-Maschinen können auch i386-Images ausführen – d.h. wenn man pullt, wenn es i386 schon gibt, aber noch nicht amd64, dann bekommt man die i386-Variante.
Als Workaround haben wir Renovate konfiguriert, dass es für Docker Hub einige Stunden wartet, bevor es ein neues Image verwendet:
packageRules: [
{
matchDatasources: ["docker"],
minimumReleaseAge: "4 hours",
// nur Docker Hub (Images ohne Punkt = Images ohne Hostname)
// wobei aktuell sowieso nur Docker Hub Release-Timestamps liefert.
excludePackagePatterns: ["\\."],
},
]
Eine Garantie ist das leider auch nicht — es gibt keine Möglichkeit zu erkennen, dass ein Multi-Arch-Image vollständig gepusht wurde. Alternativ könnte Renovate ein Feature bekommen, wo man seine verwendeten Architekturen konfiguriert, und es dann nur Images berücksichtigt, die diese Architekturen beinhalten. Aber die saubere Lösung wäre, dass Docker Inc ihre Images atomic updatet – d.h. alle Architekturen baut, und erst dann das Gesamtergebnis taggt. Wir hatten dieses Problem bisher mit keinen Images anderer Quellen oder auf anderen Registries.
Wenn diese Analyse dein Interesse geweckt hat, interessieren dich vielleicht auch unsere Stellenangebote. 😉