Nama lengkap budi/katalog-web:2.0 sudah menempel pada image, dan tibalah momen yang dinanti sejak section ini dibuka: mengirim image keluar dari laptop Budi. docker push mengunggah manifest dan layer ke repository di Docker Hub, sementara docker pull menariknya kembali ke mesin mana pun yang membutuhkan. Chapter ini mengikuti image katalog-web menempuh perjalanan bolak-balik tersebut — mengamati output per-layer, menyaksikan penghematan bandwith saat rilis kedua naik, menjalankan image di laptop Siti, lalu mengenali dua kejutan klasik yang menunggu di tengah jalan: pesan pull access denied dan rate limit 429.
Push Image ke Docker Hub #
Bentuk dasarnya sesederhana namanya:
$ docker push budi/katalog-web:2.0Output yang Dihasilkan:
The push refers to repository [docker.io/budi/katalog-web]
7a3e9c1f5d24: Pushed
c48e2b6d90af: Pushed
5f1d8a3c7e29: Pushed
9b2e6fa8d41c: Pushed
e6c417f2a89b: Pushed
2.0: digest: sha256:e3a47392fbb44a7fec80ab4002e269f8c6e71c769411fc9e102d59529137e8c6 size: 1570Mari kita telaah output di atas:
The push refers to repository [...]— baris pertama mengonfirmasi alamat lengkap tujuan: registrydocker.io, repositorybudi/katalog-web, tag2.0.- Lima baris per-layer — daemon mengunggah blob layer satu per satu; awalan 12 karakter pada tiap baris adalah potongan digest layer, angka yang sama dengan yang tampil pada output pull di Chapter Pengantar Container Registry.
- Baris terakhir — manifest image yang baru saja terdaftar di registry, lengkap dengan digest dan ukuran manifest dalam byte. Nilai digest ini identik dengan yang akan tercetak ketika image ditarik di mesin lain — digest dihitung dari isi manifest dan layer, bukan dari lokasi penyimpanan, sehingga identitasnya berlaku lintas mesin.
Setelah baris terakhir tercetak, repository budi/katalog-web di Docker Hub resmi berisi satu tag — dan tab Tags pada halaman repository mulai hidup.
Rilis Kedua dan Penghematan Bandwidth #
Seminggu kemudian Budi memperbaiki bug kecil pada format harga di src/rupiah.js, lalu membangun rilis 2.1 dengan kebiasaan multi-tag dari Chapter Tagging Image:
$ docker build -t katalog-web:2.1 -t budi/katalog-web:2.1 .Output yang Dihasilkan (dipotong):
[+] Building 5.8s (15/15) FINISHED
...
=> CACHED [4/8] RUN npm install 0.0s
=> [5/8] COPY src/ ./src/ 0.1s
...
=> [9/15] RUN npm test 3.2s
=> CACHED [11/15] RUN npm install --omit=dev 0.0s
=> [12/15] COPY --from=test /app/src ./src 0.1s
...
=> exporting to image 0.4s
=> => writing image sha256:c3e91f6a2b7d4e8f1a3c5d7e9b0f2a4c6d8e0b2d4f6a8c1e3b5d7f0a2c4e6b8d 0.1s
=> => naming to docker.io/library/katalog-web:2.1 0.0s
=> => naming to docker.io/budi/katalog-web:2.1 0.0sKarena hanya berkas di src/ yang berubah, layer dependensi npm install --omit=dev masih dilayani cache — persis tabel keputusan rebuild pada Chapter Development dan Production Image. Sekarang kita dorong rilis keduanya:
$ docker push budi/katalog-web:2.1Output yang Dihasilkan:
The push refers to repository [docker.io/budi/katalog-web]
7a3e9c1f5d24: Layer already exists
c48e2b6d90af: Layer already exists
5f1d8a3c7e29: Layer already exists
9b2e6fa8d41c: Layer already exists
41d8f9c2e7a1: Pushed
2.1: digest: sha256:af3d84c454a9da3d229ffa4eaab2507467b615caff4e77ae23a9149d49d7e67f size: 1570Empat layer pertama dijawab Layer already exists karena registry sudah menyimpan blob dengan digest yang sama dari push sebelumnya — sifat content-addressable membuat registry tahu persis blob mana yang belum dimilikinya. Hanya layer kode aplikasi (41d8f9c2e7a1) yang benar-benar dikirim: unduhan berjuta-juta byte menyusut menjadi beberapa megabyte. Efek ini adalah cermin dari Already exists pada docker pull yang kita saksikan di pembuka section.
Push Beberapa Tag Sekaligus #
Docker menyediakan flag untuk mengunggah semua tag lokal milik satu repository sekaligus:
$ docker push --all-tags budi/katalog-webPerintah ini memindai penyimpanan lokal dan mengirim setiap tag budi/katalog-web yang ditemukan. Kemudahannya nyata, tetapi ada satu jebakan: tag eksperimen yang lupa dibersihkan di mesin lokal ikut terpublikasi tanpa kita sadari. Untuk rilis resmi, kebiasaan yang lebih sehat adalah push secara eksplisit per tag — daftar yang kita tulis adalah daftar yang benar-benar diterbitkan, dan audit docker push mana menimpa apa menjadi jelas:
$ docker push budi/katalog-web:2.0
$ docker push budi/katalog-web:latestPull di Mesin Siti #
Perjalanan dua arah ini baru terasa berguna ketika image ditarik di mesin lain. Siti — yang baru ditugaskan membantu proyek katalog — cukup mengetik satu perintah tanpa login karena repository kita publik:
$ docker pull budi/katalog-web:2.0Output yang Dihasilkan:
2.0: Pulling from budi/katalog-web
7a3e9c1f5d24: Pull complete
c48e2b6d90af: Pull complete
5f1d8a3c7e29: Pull complete
9b2e6fa8d41c: Pull complete
e6c417f2a89b: Pull complete
Digest: sha256:e3a47392fbb44a7fec80ab4002e269f8c6e71c769411fc9e102d59529137e8c6
Status: Downloaded newer image for budi/katalog-web:2.0
docker.io/budi/katalog-web:2.0Perhatikan baris Digest: — karakter demi karakter identik dengan yang tercetak saat Budi push dari laptopnya. Dua mesin yang belum pernah saling mengenal kini menyimpan image yang terbukti identik byte-per-byte; itulah janji content-addressable yang kita buktikan teoretis di Chapter Image Digest, kini terlihat dalam satu baris output.
Siti tidak berhenti di pull — ia menjalankan varian produksi yang resepnya sudah dibawa repository: clone kode, naikkan stack dengan IMAGE_TAG menunjuk rilis yang barusan ditarik:
$ git clone https://git.example.com/budi/katalog-web.git
$ cd katalog-web
$ IMAGE_TAG=2.0 docker compose -f compose.yaml -f compose.prod.yaml -p katalog-prod up -d webOutput yang Dihasilkan:
[+] Running 3/3
✔ Network katalog-prod_default Created
✔ Container katalog-prod-db-1 Healthy
✔ Container katalog-prod-web-1 StartedMari kita telaah alur tersebut:
IMAGE_TAG=2.0— variabel interpolasi yang kita rancang pada Chapter Compose Override dan Environment kini menunaikan tugasnya: kunciimage: budi/katalog-web:${IMAGE_TAG:-latest}melebur menjadibudi/katalog-web:2.0— image yang baru saja ditarik.- Kunci
buildtidak bekerja di sini —compose.prod.yamlmeresetnya; servicewebmurni bergantung pada image dari registry, danpostgres:18-alpineuntukdbikut ditarik pada langkah pertama. katalog-prod-db-1 Healthysebelumweb-1 Started— gerbangdepends_onmenuntut database sehat lebih dulu, rantai yang sama dengan saat Budi menguji varian produksi di laptopnya sendiri.- Prefix
-p katalog-prod— project terpisah dengan nama container dan network sendiri, persis pola yang kita pilih agar environment Siti tidak bertabrakan dengan milik siapa pun.
Hasilnya bisa diuji dari luar:
$ curl localhost:80Output yang Dihasilkan:
<h1>Katalog Produk</h1>
<ul>
</ul>Halaman katalog yang sama dengan milik Budi kini lahir dari laptop Siti tanpa satu proses instalasi pun — hanya pull dan satu berkas compose.
Alur untuk Repository Privat #
Alurnya berubah sedikit bila repository disetel Private — misalnya ketika image berisi kode yang belum boleh dilihat publik. Mesin tujuan harus berkenalan dulu sebelum menarik: login, pull, lalu jalankan:
docker login
docker pull budi/katalog-web:2.0
docker run -d --name katalog-web budi/katalog-web:2.0Mekanisme docker login dan penyimpanan kredensialnya sudah kita bahas tuntas pada Chapter Login dan Authentication; sisanya alur pull-run sama persis. Akun yang menarik harus menjadi anggota dengan permission Read — pemberian yang diatur lewat organization dan team pada Chapter Membuat Repository.
Ketika Pull Ditolak #
Apa yang terjadi jika seseorang menarik repository privat tanpa login? Registry menjawab dengan pesan yang bagi pemula sering terdengar menakutkan:
$ docker pull budi/katalog-web:2.0Output yang Dihasilkan:
Error response from daemon: pull access denied for budi/katalog-web, repository does not exist or may require 'docker login': denied: requested access to the resource is deniedPesan ini sengaja merangkap dua kemungkinan: repository memang tidak ada, atau repository privat dan peminta belum login. Registry tidak membocorkan keberadaan repository privat kepada orang yang tidak berhak, sehingga dua kondisi tampak sama dari luar. Diagnosisnya cepat: periksa ejaan nama image lebih dulu, lalu jalankan docker login bila nama sudah benar.
Musuh kedua datang dari arah kuota: rate limit yang tabel lengkapnya sudah kita bahas pada Chapter Docker Hub. Ketika batas 100 pull per 6 jam per IP terlampaui, pull gagal dengan kode HTTP 429 dan galat toomanyrequests — bukan karena image hilang. Jalan keluarnya bertingkat: login agar dihitung pada kuota akun, atau memakai registry mirror yang kita bangun dua chapter lagi.
Push dari Mesin CI #
Terakhir, perlu dipahami bahwa push tidak harus dilakukan manusia di depan laptop. Praktik modern menjalankan build dan push dari mesin CI: pipeline login memakai kredensial yang disimpan sebagai secret, membangun image, menandainya dengan nomor versi, lalu push ke registry. Seluruh materi section ini — login, tagging, push — adalah bata yang sama persis yang disusun otomatis oleh pipeline. Kita akan merakit pipeline semacam itu pada Section CI/CD dan Automation.
Referensi #
- docker image push — CLI Reference
— perilaku push per layer, flag
--all-tags, dan pencatatan digest. - docker image pull — CLI Reference — alur pull dan verifikasi digest terhadap manifest.
- Docker Hub rate limits dan usage — kuota pull yang melatarbelakangi galat 429.
Image katalog-web kini hidup di Docker Hub dan bisa ditarik dari mana saja — setidaknya dari mana saja yang punya akses internet ke docker.io. Namun Docker Hub bukan satu-satunya alamat di dunia: ada saatnya kita ingin registry milik sendiri, entah demi kecepatan jaringan lokal, regulasi data, atau lingkungan yang sama sekali tidak tersambung internet. Membangun private registry dengan image registry:2 adalah topik chapter berikutnya.