Image katalog-web kini hidup di Docker Hub dan bisa ditarik dari mana saja — setidaknya dari mana saja yang tersambung internet dan bersedia berbagi kuota dengan publik. Namun Docker Hub bukan satu-satunya alamat di dunia: ada tim yang bekerja di jaringan terisolasi tanpa akses internet, ada perusahaan yang regulasinya melarang image internal keluar pusat data, dan ada kantor yang muak menunggu pull ratusan megabyte melewati jalur publik. Untuk semua skenario itu Docker menyediakan jawaban yang mengejutkan ringkas: registry sendiri berjalan sebagai — apa lagi — sebuah container. Chapter ini menjalankan registry privat dengan image resmi registry:2, menguji siklus tag-push-pull ke localhost:5000, lalu menaklukkan dua urusan operasionalnya yang paling sering menggigit: TLS dan autentikasi.
Mengapa Menjalankan Registry Sendiri #
Empat motivasi berikut paling sering menjadi alasan tim memutuskan bahwa image tidak boleh lagi menumpang registry publik:
| Motivasi | Cerita di lapangan |
|---|---|
| Jaringan terisolasi (air-gapped) | lingkungan tanpa internet tetap bisa mendistribusikan image antar mesin |
| Latensi rendah | pull berukuran gigabyte berjalan melalui LAN, bukan jalur publik |
| Kepatuhan (compliance) | image yang membawa data terbatas tidak pernah meninggalkan pusat data |
| Kendali penuh | kuota, retensi, dan kebijakan akses ditentukan sendiri |
Registry self-host juga punya sisi yang perlu jujur diakui sejak awal: tanpa TLS dan autentikasi ia terbuka lebar, tanpa perawatan volumenya bisa penuh oleh blob lama, dan tanpa infrastruktur pendukung ia tidak membawa replikasi maupun pemindaian kerentanan bawaan. Tim besar biasanya berakhir di registry terkelola — Amazon ECR, Artifact Registry, Quay — di tengah jalan; registry:2 tetap unggul untuk belajar, lab, dan skenario terisolasi.
Menjalankan registry:2 #
Satu perintah docker run sudah cukup untuk menghidupkan registry penuh fitur:
$ docker run -d -p 5000:5000 --restart always --name registry -v registry-data:/var/lib/registry registry:2Perintah ini menarik image registry:2 dari Docker Hub lalu menjalankannya. Kita verifikasi kehidupannya lewat daftar container:
$ docker psOutput yang Dihasilkan:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
dfb41e0f3f6a registry:2 "/entrypoint.sh /etc…" 8 seconds ago Up 7 seconds 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp registryMari kita telaah enam bagian perintah pembangunnya:
-d— registry berjalan di background sebagai layanan jangka panjang, bukan sesi interaktif.-p 5000:5000— port 5000 host diteruskan ke port 5000 container; port standar tempat Registry HTTP API V2 mendengarkan, API yang endpoint/v2/-nya kita kenal dari Chapter Pengantar Container Registry.--restart always— kebijakan restart memastikan registry bangkit sendiri setelah daemon restart atau host direboot; tanpa ini, satu reboot server melumpuhkan seluruh tim.--name registry— nama tetap supaya mudah dikelola lewatdocker logs registryataudocker exec registry.-v registry-data:/var/lib/registry— named volume menyimpan seluruh blob dan manifest di luar filesystem container; container boleh dihapus dan dibuat ulang, isi registry tetap aman — pola persistensi dari Section Penyimpanan Data.registry:2— image resmi open source dari proyek Distribution; tag2menandai garis rilis mayor yang aktif dipelihara.
Sanity check terakhir: endpoint /v2/ adalah akar API yang wajib dijawab setiap registry sehat:
$ curl http://localhost:5000/v2/Output yang Dihasilkan:
{}Jawaban JSON kosong berarti registry hidup, berbicara API V2, dan siap menerima push.
Tag, Push, dan Pull dari Registry Lokal #
Mekanisme distribusinya persis sama dengan Docker Hub — yang berubah hanya alamat. Kita pasang prefiks hostname pada nama image, lalu dorong:
$ docker tag katalog-web:2.0 localhost:5000/katalog-web:2.0
$ docker push localhost:5000/katalog-web:2.0Output yang Dihasilkan:
The push refers to repository [localhost:5000/katalog-web]
7a3e9c1f5d24: Pushed
c48e2b6d90af: Pushed
5f1d8a3c7e29: Pushed
9b2e6fa8d41c: Pushed
e6c417f2a89b: Pushed
2.0: digest: sha256:e3a47392fbb44a7fec80ab4002e269f8c6e71c769411fc9e102d59529137e8c6 size: 1570Perhatikan satu detail yang sering membuat mata terbelalak: baris digest identik dengan hasil push ke Docker Hub pada Chapter Push dan Pull Image. Digest dihitung dari isi manifest dan layer, bukan dari lokasi penyimpanan — image yang sama selalu berdigest sama di registry mana pun. Inilah bukti content-addressable lintas registry: dua server yang bahkan tidak saling mengenal menyimpan objek yang dapat dibuktikan identik. Kita uji arah sebaliknya:
$ docker pull localhost:5000/katalog-web:2.0Output yang Dihasilkan:
2.0: Pulling from katalog-web
Digest: sha256:e3a47392fbb44a7fec80ab4002e269f8c6e71c769411fc9e102d59529137e8c6
Status: Image is up to date for localhost:5000/katalog-web:2.0Status Image is up to date muncul karena push barusan turut mencatat referensi localhost:5000/katalog-web:2.0 pada penyimpanan lokal — pull dari registry lokal berhasil, hanya saja memang tidak ada yang perlu diunduh.
Saat Daemon Menolak HTTP Biasa #
Cerita berjalan mulus selama alamatnya localhost. Masalah muncul begitu registry diakses dari mesin lain — Budi membiarkan registrynya hidup di workstation 192.168.1.20, lalu Siti menarik image lewat LAN:
$ docker pull 192.168.1.20:5000/katalog-web:2.0Output yang Dihasilkan:
Error response from daemon: Get "https://192.168.1.20:5000/v2/": http: server gave HTTP response to HTTPS clientPesan ini jujur menceritakan kejadiannya: daemon Siti mencoba bicara HTTPS — protokol bawaan untuk registry selain localhost — sedangkan registry kita baru berbicara HTTP polos. Alamat localhost dan 127.0.0.1 memang dikecualikan dari kebijakan ini secara bawaan; itulah sebabnya latihan kita tadi lancar. Alamat lain tidak mendapat pengecualian apa pun.
Solusi cepat untuk laboratorium adalah mendaftarkan registry ke daftar insecure-registries pada berkas daemon.json milik mesin klien, lalu memulai ulang daemon:
{
"insecure-registries": ["192.168.1.20:5000"]
}Pada Linux kita restart layanan dengan sudo systemctl restart docker; pada Docker Desktop, berkas yang sama diedit melalui Settings → Docker Engine. Setelah daemon bangkit, docker info menampilkan bukti konfigurasi terbaca:
...
Insecure Registries:
127.0.0.0/8
192.168.1.20:5000
...Dan pull Siti kini berjalan lancar — dengan hasil yang layak direnungkan:
2.0: Pulling from katalog-web
7a3e9c1f5d24: Already exists
c48e2b6d90af: Already exists
5f1d8a3c7e29: Already exists
9b2e6fa8d41c: Already exists
e6c417f2a89b: Already exists
Digest: sha256:e3a47392fbb44a7fec80ab4002e269f8c6e71c769411fc9e102d59529137e8c6
Status: Downloaded newer image for 192.168.1.20:5000/katalog-web:2.0Lima baris Already exists muncul karena laptop Siti sudah menyimpan seluruh layer itu — bukan dari registry ini, melainkan dari tarikan Docker Hub pada Chapter Push dan Pull Image. Digest yang sama dikenali lintas registry, sehingga nol byte pun tidak diunduh ulang; baris Status: Downloaded newer image hanya mencatat bahwa nama referensi 192.168.1.20:5000/katalog-web:2.0 baru pertama kali terpasang di mesin itu.
Jalan yang Benar: TLS dengan Sertifikat #
Solusi insecure-registries meneguk kenyamanan dengan menuju risiko; jalan yang benar untuk produksi adalah TLS. Prosesnya ringkas dan berlangkah:
- Siapkan sertifikat beserta kuncinya dari certificate authority — CA internal perusahaan atau CA publik — untuk nama host registry, misalnya
registry.example.com. - Mount kedua berkas tersebut ke container registry, lalu arahkan dua environment variable padanya.
- Distribusikan sertifikat CA ke setiap mesin klien: salin ke
/etc/docker/certs.d/registry.example.com:5000/ca.crt, atau tambahkan CA ke trust store sistem operasi. - Uji dengan
docker login registry.example.com:5000— bila berhasil tanpa konfigurasi insecure apa pun, TLS bekerja.
Bentuk lengkap langkah keduanya:
$ docker rm -f registry
$ docker run -d -p 5000:5000 --restart always --name registry \
-v registry-data:/var/lib/registry \
-v /srv/registry/certs:/certs:ro \
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/registry.example.com.crt \
-e REGISTRY_HTTP_TLS_KEY=/certs/registry.example.com.key \
registry:2Variabel REGISTRY_HTTP_TLS_CERTIFICATE dan REGISTRY_HTTP_TLS_KEY memerintahkan registry menyajikan TLS pada port yang sama, sementara volume lama registry-data tetap terpasang sehingga blob yang sudah tersimpan tidak kehilangan rumah. Sejak saat itu docker login, push, dan pull ke registry.example.com:5000 bekerja normal — daemon memverifikasi sertifikat, lalu lintas terenkripsi, dan tidak ada satu pun entri insecure yang dibutuhkan.
Autentikasi dengan htpasswd #
Registry tanpa autentikasi terbuka bagi siapa saja yang bisa menjangkaunya — dan registry yang baru saja kita bekali TLS kini siap menerima kredensial dengan aman. Mode autentikasi paling sederhana yang didukung registry:2 adalah berkas htpasswd. Kita menghasilkannya tanpa memasang tool apa pun di host, cukup meminjam image registry yang entripoint-nya kita ganti sementara:
$ mkdir -p /srv/registry/auth
$ docker run --rm --entrypoint htpasswd registry:2 -Bbn budi rahasia123 > /srv/registry/auth/htpasswdFlag -B meminta hash bcrypt, -b membaca password dari argumen, dan -n menulis hasilnya ke stdout yang kita alihkan ke berkas. Isinya satu baris:
$ cat /srv/registry/auth/htpasswdOutput yang Dihasilkan:
budi:$2y$05$4Wn6KzEcHAaLdOX38RDPaVnwzp66614wdl/QzcLD3G63QB0BzYtXbLalu registry dibangkitkan ulang dengan tiga variable autentikasi:
$ docker rm -f registry
$ docker run -d -p 5000:5000 --restart always --name registry \
-v registry-data:/var/lib/registry \
-v /srv/registry/auth:/auth:ro \
-e REGISTRY_AUTH=htpasswd \
-e REGISTRY_AUTH_HTPASSWD_REALM="Registry Privat" \
-e REGISTRY_AUTH_HTPASSWD_PATH=/auth/htpasswd \
registry:2Sejak saat itu setiap push dan pull harus didahului docker login registry.example.com:5000 -u budi — pola login per registry yang persis kita latih pada Chapter Login dan Authentication, karena registry mana pun berbicara protokol yang sama. Outputnya kini bersih berkat credential helper yang sudah aktif sejak chapter itu:
$ docker login registry.example.com:5000 -u budiOutput yang Dihasilkan:
Password:
Login SucceededKredensial hanya layak dikirim melalui TLS — itulah urutan bab ini: sertifikat lebih dulu, baru htpasswd. Menggabungkan htpasswd dengan HTTP polos berarti mengirim password terenkripsi-hash tetapi menampilkannya di jalur yang bisa disadap.
Memilih Bentuk Deployment #
Seluruh perjalanan tadi bisa diringkas menjadi tabel keputusan:
| Skenario | Konfigurasi | Catatan |
|---|---|---|
| Lab dan jaringan terpercaya | HTTP + insecure-registries di klien | paling cepat dirakit; lalu lintas polos |
| Produksi | TLS + htpasswd + named volume | kredensial terenkripsi; CA didistribusikan ke klien |
| Skala besar | registry terkelola (ECR, Artifact Registry, Quay) | replikasi, pemindaian, dan kuota bawaan |
Referensi #
- Docker Registry — dokumentasi resmi image registry dan arsitekturnya.
- Deploying a registry — konfigurasi TLS dan autentikasi htpasswd secara resmi.
- Test an insecure registry — perilaku daemon terhadap registry tanpa TLS.
- Garbage collection — pembersihan blob yatim dan jadwalnya.
Registry privat menjawab kebutuhan menyimpan dan membagikan image milik kita sendiri. Namun satu pekerjaan distribusi masih tersisa: menarik image publik seperti nginx dan node berulang kali dari Docker Hub — lengkap dengan jarak jaringan dan rate limit-nya. Untuk itu Docker punya konsep registry mirror: cache perantara yang menyimpan image publik di dekat kita, topik chapter berikutnya.