Container registry adalah gudang image di internet. Kamu push (upload) image dari laptop atau CI ke sana, dan server pull (download) image itu untuk dijalankan. Bayangin gudang ekspedisi. Tiap kardus boleh ditempeli label seperti "terbaru" atau "kiriman Senin", dan label itu bisa dicopot lalu ditempel ke kardus lain kapan saja. Tapi tiap kardus juga punya sidik jari yang dihitung dari isinya: isinya berubah sebutir pun, sidik jarinya ikut berubah. Di registry, label itu tag, dan sidik jari itu digest.
🧪 Tebakan cepat:
web-1menarikcatatan-api:latestkemarin. Hari ini kamu push versi baru dan memindahkanlatestke sana. Lalu server baru,web-2, menariklatest. Apakahweb-1danweb-2menjalankan kode yang sama? Tebak dulu, jawabannya ada di bagian "Tag bisa pindah, digest enggak".
Nama lengkap sebuah image
Image yang kita bangun di lesson sebelumnya baru ada di laptop. Supaya web-1 bisa menariknya, image itu butuh nama lengkap yang menyebut gudangnya:
ghcr.io/budi/catatan-api:a41c9e2
| Bagian | Isinya | Kalau gak ditulis |
|---|---|---|
| registry | ghcr.io (GitHub Container Registry) |
docker.io (Docker Hub) |
| namespace | budi, pemilik akun atau organisasi |
library, image resmi Docker Hub |
| name | catatan-api |
(wajib ada) |
| tag | a41c9e2 |
latest |
Jadi docker pull nginx sebenarnya menarik docker.io/library/nginx:latest, dan user/app:2.0 artinya docker.io/user/app:2.0. Docker mengenali bagian pertama sebagai registry kalau berisi titik atau titik dua sebelum garis miring pertama, atau kalau namanya persis localhost: ghcr.io, registry.example.com:5000, localhost/team/app. Kalau gak ada, bagian itu namespace di Docker Hub.
Push dan pull
Satu kali login per mesin (untuk GHCR, password-nya personal access token GitHub dengan izin write:packages), lalu tag dan push:
$ echo $CR_PAT | docker login ghcr.io -u budi --password-stdin
Login Succeeded
$ docker build -t ghcr.io/budi/catatan-api:e4b2d90 .
$ docker push ghcr.io/budi/catatan-api:e4b2d90
The push refers to repository [ghcr.io/budi/catatan-api]
e8297fcb4347: Pushed
6b0cdd6de4cf: Layer already exists
eb41a8797959: Layer already exists
6613a3c187ef: Layer already exists
a0e70458d19e: Layer already exists
95cf1a2e1698: Layer already exists
77ea7eee3d80: Layer already exists
4fbec69b9411: Layer already exists
e4b2d90: digest: sha256:ea2c289cde499d66d29b0692d3780d7b0a0595ec74af2beeed51d82a3c3b2f2a size: 1993
- Tag
e4b2d90adalah 7 karakter pertama hash commit Git-nya, jadi image ini bisa dilacak ke kode persisnya. - Registry menyimpan per layer. Cuma layer
COPY . .yang baru (kodenya berubah), tujuh layer lain sudah ada dari push sebelumnya, jadi gak dikirim ulang. Ini layer yang sama dengan lesson Dockerfile. - Baris terakhir menyebut digest:
sha256:diikuti 64 karakter yang dihitung dari isi image. Isinya sama, digest-nya sama; beda satu byte, digest-nya beda total.
Di server, docker pull ghcr.io/budi/catatan-api:e4b2d90 menarik image itu. Layer yang sudah ada di server ditulis Already exists, yang baru Pull complete.
Tag bisa pindah, digest enggak
Tag cuma label yang menunjuk ke satu digest, dan bisa dipindah dengan push ulang. latest gak punya arti khusus; dia cuma tag yang dipakai kalau kamu gak menulis tag, dan menunjuk apa pun yang terakhir di-push dengan nama itu.
| Waktu | latest di registry menunjuk |
web-1 menjalankan |
web-2 menjalankan |
|---|---|---|---|
| Kemarin | a41c9e2 (sha256:2716…) |
latest = a41c9e2 |
belum ada |
Hari ini, latest dipindah |
e4b2d90 (sha256:ea2c…) |
masih a41c9e2 |
latest = e4b2d90 |
Jadi gak: dua server sama-sama menulis latest, tapi kodenya beda. web-1 gak pernah menarik ulang, jadi dia tetap memakai salinan kemarin. Itu jawaban tebakannya. Kalau ada bug, kamu bahkan gak bisa menjawab "production menjalankan versi berapa?".
Digest gak bisa dipindah. ghcr.io/budi/catatan-api@sha256:2716… selalu menghasilkan isi yang sama persis, di server mana pun, kapan pun.
Tag untuk production
- Git SHA (
a41c9e2) atau semver (v1.2.3): satu tag untuk satu versi, gak pernah di-push ulang. Kamu tahu persis apa yang jalan, dan rollback cukup dengan menjalankan tag sebelumnya. - Digest (
@sha256:...): paling pasti, karena dihitung dari isinya. Cocok untuk base image di Dockerfile atau deploy yang harus bisa diaudit. latest: boleh untuk coba-coba di laptop, jangan di production.
Bedanya: tag Git SHA gak berubah karena kesepakatan tim (registry sebenarnya mengizinkan push ulang), digest gak berubah karena matematika.
⚠️ Jebakan yang sering ketemu
- Deploy production pakai
latest. Server yang menarik di waktu berbeda menjalankan kode berbeda, dan rollback jadi tebak-tebakan. - Lupa tag dengan nama registry.
docker push catatan-api:e4b2d90dikirim kedocker.io/library/catatan-api, bukan ke GHCR, dan ditolak karena kamu gak punya akses. Tag dulu dengan nama lengkapghcr.io/budi/catatan-api:.... - Mengira pull mengganti container yang sedang jalan.
docker pullcuma mengunduh image. Container lama tetap memakai image lama sampai dibuat ulang. - Memotong nama di titik dua pertama. Di
registry.example.com:5000/app:2.0, titik dua pertama milik port registry. Tag-nya diambil dari bagian terakhir, setelah garis miring terakhir.
TL;DR: Container registry (Docker Hub, GHCR, ECR) menyimpan image; docker push mengunggah, docker pull mengunduh, dan cuma layer baru yang dikirim. Nama lengkap image: registry/namespace/name:tag, dengan default docker.io, library, dan latest, jadi nginx sama dengan docker.io/library/nginx:latest. Bagian pertama dianggap registry kalau berisi titik atau titik dua, atau namanya localhost. Tag cuma label yang bisa dipindah; digest sha256:... dihitung dari isi image dan gak pernah berubah. latest di production bikin server yang menarik di waktu berbeda menjalankan kode berbeda, jadi pakai tag Git SHA atau semver yang gak pernah di-push ulang, atau digest kalau harus pasti sampai byte terakhir.