:latest は「新しい」を保証しない
:latest が保証するのは「メンテナが最後に push したもの」だけ。
メンテナが手を止めたら、タグはそこで凍る。同じサーバーで2件見つかった。
ダッシュボードのコンテナが起動しなくなっていた。イメージを見たら ビルド日が4年前だった。
調べたら、プロジェクトが別の組織アカウントに移籍していて、
旧リポジトリは放棄されていた。新しいリリースは全部新しい場所に出ている。
:latest を指したまま4年放置されていたことになる。
old-namespace/app:latest ビルド 4年前 ← 参照していたもの
ghcr.io/new-org/app:v1.76.2 ビルド 8日前 ← 実際の最新
チャット UI のイメージが :0 という浮動タグだった。
Docker Hub の説明文を読んで気づいた。
Automated (unofficial) Docker Hub mirror of tagged images on ... GHCR repo
非公式ミラーで、5ヶ月前に更新が止まっていた。 本家は GHCR にあり、その間に3マイナーバージョン進んでいた。
厄介なのは、ミラーの中では :0 は「最新」で、ダイジェスト比較でも
「更新なし」と出ること。ミラーごと死んでいるので、ミラー内部の整合性は保たれている。
ダイジェスト比較だけでは足りない。この2つを両方見る。
| 見るもの | わかること |
|---|---|
| ローカルとレジストリのダイジェスト | pull すれば新しくなるか |
| バージョンタグの最終更新日 | リポジトリが生きているか |
浮動タグ(:latest, :0 など)を使っていて、
かつバージョンタグが数ヶ月増えていないものは、放棄を疑う。
最初 docker manifest inspect --verbose の結果を使ったら、
ほぼ全部が「更新あり」になった。誤りだった。
--verbose はマルチアーキイメージだとアーキ別マニフェストの配列を返す。
その先頭の digest と、ローカルの RepoDigests(マニフェストリストの digest)は
そもそも別物なので、比較しても意味がない。
レジストリ API に HEAD して Docker-Content-Digest を見るのが確実。
curl -sI \
-H 'Accept: application/vnd.oci.image.index.v1+json, application/vnd.docker.distribution.manifest.list.v2+json' \
https://<registry>/v2/<repo>/manifests/<tag> \
| grep -i '^docker-content-digest'
「今動いているバージョン」に固定する。ここで大事なのは、 固定先タグのダイジェストが今のものと同一か、書き換える前に確認すること。
ローカル prom/prometheus:latest の digest
レジストリ prom/prometheus:v3.13.2 の digest
→ 一致すれば、書き換えても再DLもバージョン変化も起きない
一致を確認してから書き換えれば、固定作業そのものは無変更の操作になる。 バージョンを上げるのはその後、別の作業としてやる。混ぜると切り分けができなくなる。
止まっていたミラーの件では、2段階に分けた。
1 では何も起きないことを確認でき、2 では DB マイグレーションだけを単独で観測できた。 まとめてやると、問題が出たときにレジストリ移行のせいかバージョン更新のせいか分からなくなる。
docker image prune -a は使わないほうがいい。
コンテナとして常駐していないが、スクリプトが都度 docker run するイメージ
(alpine、restic/restic など)まで消える。
バックアップスクリプトが次に走るとき、静かに再ダウンロードが始まる。
消すときはイメージ名を明示する。