Login sudah beres dan repository budi/katalog-web sudah menunggu di Docker Hub — tetapi image hasil Section Docker dalam Pengembangan masih memakai nama pendek katalog-web:2.0, nama yang tidak menyebut registry tujuan maupun pemilik repository. Sebelum docker push bisa bekerja, image memerlukan nama lengkap yang sesuai dengan alamatnya di registry. Chapter ini membedah docker tag sampai ke mekanismenya: mengapa tag hanyalah alias, bagaimana satu image boleh memikul banyak nama sekaligus, ke mana nama tanpa namespace sebenarnya diarahkan, dan bagaimana latest bekerja tanpa sihir apa pun.

Tag Adalah Alias, Bukan Salinan #

Kita mulai dari fakta paling mendasar: image di penyimpanan lokal diidentifikasi lewat ID-nya, bukan lewat nama. Nama repository dan tag hanyalah referensi — alias yang menunjuk satu image tertentu. Perintah docker tag tidak menyalin data apa pun; perintah ini hanya memasang alias baru pada image yang sudah ada:

bash
$ docker tag katalog-web:2.0 budi/katalog-web:2.0

Perintah ini sukses tanpa mencetak satu baris pun — docker tag adalah salah satu perintah paling sunyi di CLI Docker. Tidak ada pesan berarti tidak ada kesalahan, dan alias baru pun terpasang. Mari kita buktikan hasilnya di tabel image lokal:

bash
$ docker images

Output yang Dihasilkan:

text
REPOSITORY           TAG      IMAGE ID       CREATED         SIZE
budi/katalog-web     2.0      4d8b2e7f1a6c   2 days ago      142MB
katalog-web          2.0      4d8b2e7f1a6c   2 days ago      142MB
katalog-web          dev      8f3a2c9e5b1d   3 days ago      189MB
...

Baris image lain seperti nginx dan postgres dipotong agar fokus. Mari kita telaah tiga baris yang tersisa:

  1. Kolom REPOSITORY dan TAG — kini ada dua baris untuk rilis yang sama: katalog-web:2.0 (nama lama, polos) dan budi/katalog-web:2.0 (nama lengkap baru).
  2. Kolom IMAGE ID — kedua baris menunjuk ID yang sama, 4d8b2e7f1a6c; ini bukti bahwa keduanya adalah image yang persis sama, bukan dua salinan.
  3. Kolom SIZE — 142MB tidak berlipat ganda karena tidak ada satu byte pun yang disalin; disk tetap menyimpan satu set layer. Baris katalog-web:dev yang gemuk tetap berdiri sendiri sebagai image development dari Chapter Development dan Production Image.

Nama Tanpa Namespace Akan Tersesat ke library #

Mengapa nama pendek katalog-web:2.0 tidak cukup untuk push? Karena Docker melengkapi nama yang tidak ditulis penuh dengan bagian bawaannya: registry docker.io dan namespace library. Nama katalog-web:2.0 sebenarnya dibaca sebagai docker.io/library/katalog-web:2.0 — baris naming to docker.io/library/katalog-web:2.0 pada output build di Section Docker dalam Pengembangan sepanjang waktu sudah memberi tahu kita hal itu.

Masalahnya, namespace library adalah wilayah khusus official images — akun biasa tidak punya hak push ke sana. Bila Budi lupa menempelkan namespace dan langsung menjalankan docker push katalog-web:2.0, registry menolak dengan galat akses karena hak (scope) akun Budi tidak mencakup namespace tersebut:

text
The push refers to repository [docker.io/library/katalog-web]
denied: requested access to the resource is denied

Menempelkan username sebagai namespace — budi/katalog-web — adalah cara memberi tahu Docker Hub bahwa image ini milik akun Budi, persis alamat repository yang kita buat pada Chapter Membuat Repository.

Bentuk nama yang sama bekerja untuk registry selain Docker Hub: cukup awali dengan hostname registry tujuan. Contoh berikut menyiapkan image yang sama untuk GitHub Container Registry:

bash
$ docker tag katalog-web:2.0 ghcr.io/budi/katalog-web:v2

Bagian ghcr.io/ mengubah nasib push: alias ini akan dikirim ke GHCR, sedangkan budi/katalog-web:2.0 tetap ditujukan ke Docker Hub. Satu image lokal, dua alamat distribusi berbeda — berguna ketika kita ingin menyebarkan image yang sama ke beberapa registry sekaligus tanpa build ulang. Aturan deteksinya sendiri sudah kita kenal dari Chapter Memahami Image Tag: segmen pertama yang mengandung titik atau titik dua dibaca sebagai hostname registry.

Menempel Semua Nama Sejak Build #

Kita tidak harus menunggu build selesai untuk mengetik docker tag satu per satu. Perintah docker build menerima flag -t beberapa kali dalam satu kali jalan — dan karena layer-layernya sudah tersedia dari build sebelumnya, BuildKit melayani semuanya dari cache:

bash
$ docker build -t katalog-web:2.0 -t budi/katalog-web:2.0 -t budi/katalog-web:latest .

Output yang Dihasilkan (dipotong):

text
[+] Building 2.1s (15/15) FINISHED
 => [internal] load build definition from Dockerfile                    0.1s
 => [internal] load metadata for docker.io/library/node:22-alpine       0.4s
...
 => CACHED [9/15] RUN npm test                                          0.0s
...
 => exporting to image                                                  0.1s
 => => exporting layers                                                 0.0s
 => => writing image sha256:4d8b2e7f1a6c9e3b5d7f1a3c5e7b9d1f3a5c7e9b1d3f5a7c9e1b3d5f7a9c1e3b 0.1s
 => => naming to docker.io/library/katalog-web:2.0                      0.0s
 => => naming to docker.io/budi/katalog-web:2.0                         0.0s
 => => naming to docker.io/budi/katalog-web:latest                      0.0s

Mari kita telaah ekor output tersebut:

  1. writing image sha256:4d8b2e7f... — hanya satu image yang diekspor; seluruh langkah dilayani CACHED, sehingga identitasnya persis dengan hasil build rilis kemarin.
  2. Tiga baris naming to — satu baris untuk setiap flag -t; ketiga nama menunjuk IMAGE ID yang sama persis.
  3. naming to docker.io/library/katalog-web:2.0 — flag pertama yang polos kini tampak jelas arah sebenarnya: namespace library. Pola multi-tag inilah fondasi strategi versioning yang kita susun tuntas pada Chapter Strategi Versioning Image — menempel nomor versi, penanda commit, dan latest sekaligus pada rilis yang sama.

Tabel image lokal kini memuat empat nama untuk satu IMAGE ID:

text
REPOSITORY           TAG      IMAGE ID       CREATED         SIZE
budi/katalog-web     2.0      4d8b2e7f1a6c   2 days ago      142MB
budi/katalog-web     latest   4d8b2e7f1a6c   2 days ago      142MB
katalog-web          2.0      4d8b2e7f1a6c   2 days ago      142MB
katalog-web          dev      8f3a2c9e5b1d   3 days ago      189MB
...

Mencabut Alias dengan docker rmi #

Karena tag hanyalah alias, menghapusnya tidak serta-merta menghapus image. Perintah docker rmi pada satu nama hanya mencabut referensi tersebut — di sini kita pakai alias GHCR yang barusan dibuat dan memang tidak akan kita terbitkan:

bash
$ docker rmi ghcr.io/budi/katalog-web:v2

Output yang Dihasilkan:

text
Untagged: ghcr.io/budi/katalog-web:v2

Kata Untagged — bukan Deleted — menandakan bahwa hanya alias yang dicabut; image tetap hidup karena tiga nama lain masih menunjuk kepadanya, dan tidak satu layer pun lepas dari disk. Baru ketika alias terakhir juga dicabut, image beserta layer-layernya benar-benar dihapus dari penyimpanan lokal — perilaku yang sudah kita bahas tuntas pada Chapter Menghapus Image. Perilaku ini membuat kita bisa bermain dengan banyak nama tanpa takut merusak image aslinya.

latest Hanyalah Tag Biasa #

Banyak pemula mengira latest punya perilaku khusus: otomatis menunjuk versi terbaru, otomatis diperbarui, atau otomatis dipilih saat pull. Kenyataannya tidak ada satu pun keajaiban seperti itu. latest hanyalah string tag biasa yang kebetulan menjadi nilai bawaan: bila kita pull tanpa menulis tag, daemon melengkapinya dengan latest; bila kita build tanpa -t bertag, nama hasil build diberi tag latest.

Konsekuensinya penting dipahami dua arah, sebagaimana sudah kita bedah pada Chapter Memahami Image Tag. Image budi/katalog-web:latest tidak akan pernah bergeser sendiri ke versi baru — nilai pada nama itu hanya berubah bila kita secara eksplisit membangun atau menandai ulang dengan tag latest, seperti yang baru saja kita lakukan pada build multi-tag di atas. Sebaliknya, docker run budi/katalog-web pada image yang sudah tersimpan lokal tidak mengecek registry sama sekali — latest milik sebuah mesin bisa saja berarti versi paling lawas di ruangan. Nilai bawaan yang sama bekerja di Compose: image: budi/katalog-web:${IMAGE_TAG:-latest} pada compose.prod.yaml kita sejak Chapter Compose Override dan Environment memakai latest hanya karena variabel IMAGE_TAG tidak diset — bukan karena tag tersebut punya perilaku khusus.

Pola Nama per Tujuan #

Menutup chapter ini, tabel berikut merangkum pola nama yang layak menjadi kebiasaan, lengkap dengan tujuan masing-masing:

Pola namaContohTujuan
Nama pendekkatalog-web:2.0latihan lokal; untuk push akan tersesat ke namespace library
Namespace pemilikbudi/katalog-web:2.0push dan pull pada repository milik akun di Docker Hub
Registry eksplisitghcr.io/budi/katalog-web:v2menyebarkan image yang sama ke registry kedua tanpa build ulang
Tag mengapungbudi/katalog-web:2penunjuk rilis minor terbaru — hanya untuk lingkungan development
latestbudi/katalog-web:latestpenunjuk rilis stabil terbaru — kenyamanan, bukan identitas

Referensi #

Image katalog-web kini memikul nama lengkap budi/katalog-web:2.0 — paspor yang dibutuhkan untuk menyeberang ke Docker Hub. Langkah berikutnya adalah perjalanan itu sendiri: mengunggah manifest dan layer ke repository, menyaksikan penghematan saat push rilis kedua, lalu menarik semuanya kembali dari mesin Siti. Seluruh mekanisme docker push dan docker pull itulah yang kita praktikkan pada chapter berikutnya.