Tip di akhir chapter sebelumnya menyebut flag -p seolah ia satu trik kecil untuk memindahkan nomor port. Kenyataannya -p adalah flag dengan format paling padat di docker run: satu nilai bisa memuat alamat host, port host, port container, dan protokol sekaligus, dan setiap segmennya menyimpan aturan main sendiri. Kita juga sudah dua kali menyinggung hubungan -p dengan EXPOSE — pertama pada Section Dasar Docker CLI dan kedua pada Chapter EXPOSE dan Port — tanpa pernah membedah format lengkapnya. Chapter ini melunasi utang itu: anatomi empat segmen, pengikatan ke alamat tertentu, protokol udp, rentang port, port acak, sampai diagnosis konflik port yang paling sering menyapa pemula.
Anatomi Flag -p #
Bentuk lengkap flag -p (bentuk panjangnya --publish) adalah:
-p [alamat_host:]port_host:port_container[/proto]Mari kita telaah keempat segmennya satu per satu:
| Segmen | Wajib | Contoh | Artinya |
|---|---|---|---|
| alamat_host | tidak — bawaan mengikat semua antarmuka | 127.0.0.1 | antarmuka host tempat port dibuka |
| port_host | ya — angka 0 berarti acak | 8000 | port di sisi host yang diterbitkan |
| port_container | ya | 8000 | port tujuan di dalam container |
| proto | tidak — bawaan tcp | udp | protokol pemetaan |
Kombinasi yang paling sering dijumpai terangkum dalam tabel berikut:
| Bentuk flag | Efeknya |
|---|---|
| -p 8080:8000 | port host 8080 di semua antarmuka menuju port container 8000 tcp |
| -p 127.0.0.1:8000:8000 | hanya localhost host yang bisa menghubungi |
| -p 9000:8000/udp | pemetaan protokol udp |
| -p 8080-8082:8000 | rentang port host menuju satu port container |
Kita jalankan bentuk dasarnya pada myapp. Port host 8080 sedang diduduki container web milik nginx, jadi Budi memakai 8000:
$ docker run -d --name myapp-publik -p 8000:8000 myapp:1.0Output yang Dihasilkan:
5d8c2a9e7b4f1a6d3c8e5b2f9a7d4c1e6b3a8f5d2c9e7b4a1f6d3c8e5b2a9d7c$ docker port myapp-publikOutput yang Dihasilkan:
8000/tcp -> 0.0.0.0:8000
8000/tcp -> [::]:8000$ curl localhost:8000Output yang Dihasilkan:
{"app":"myapp","pesan":"Halo dari container"}Mari kita telaah tiga perintah di atas:
8000/tcp -> 0.0.0.0:8000— port 8000 di dalam container dipetakan ke port 8000 di semua antarmuka IPv4 host; baris kedua dengan[::]adalah pasangan IPv6-nya, karena tanpa segmen alamat, Docker mengikat keduanya sekaligus.- Arah panah dibaca dari kiri ke kanan — port container berada di kiri, pintu host di kanan; perintah
docker portmenyajikan inventaris lengkap pemetaan container yang sedang berjalan. curlmenembus sampai aplikasi — permintaan dari host masuk lewat port host, diteruskan daemon ke port container, dan dijawab server myapp; inilah penerbitan yang sesungguhnya, berbeda dariEXPOSE 8000pada Dockerfile yang hanyalah catatan metadata — pembagian tugas yang sudah kita buktikan pada Chapter EXPOSE dan Port.
Perjalanan satu permintaan dari luar sampai ke aplikasi terpetakan seperti ini:
Mari kita telaah diagram di atas: cabang kiri menggambarkan jalur yang barusan kita uji dengan curl, sedangkan cabang kanan adalah nasib yang kita saksikan pada Chapter EXPOSE dan Port ketika myapp-sunyi berjalan tanpa satu pun flag -p — koneksi langsung ditolak karena tidak ada pintu host yang mengarah ke container.
Mengikat Hanya ke Localhost #
Segmen alamat membatasi pintu host ke antarmuka tertentu. Bentuk paling bergunanya mengikat ke 127.0.0.1 sehingga hanya proses di mesin yang sama yang bisa menghubungi:
$ docker run -d --name myapp-lokal -p 127.0.0.1:8001:8000 myapp:1.0Output yang Dihasilkan:
3e7a9c1d5f8b2b6e4d8a1c3f9d2b5f7a3e1c6b8d4a2f9c7e1b3d5f7a9c2e4d6f$ docker port myapp-lokalOutput yang Dihasilkan:
8000/tcp -> 127.0.0.1:8001Mari kita telaah satu baris output itu:
- Satu baris saja, tanpa pasangan IPv6 — pemetaan hanya hidup di
127.0.0.1; mesin lain di jaringan yang mencoba menghubungi alamat LAN host akan ditolak, sementaracurl localhost:8001dari host tetap sampai ke myapp. - Port host dan port container boleh berbeda total — 8001 di host, 8000 di container; aplikasi tidak pernah tahu dan tidak perlu tahu nomor pintu yang dipakai di sisi luar.
Protokol dan Rentang Port #
Flag -p boleh diulang dalam satu perintah, dan dua kekuatan itu — pengulangan serta rentang — paling enak diperagakan bersama:
$ docker run -d --name myapp-banyak \
-p 9000:8000/udp \
-p 9001-9003:8000 \
myapp:1.0Output yang Dihasilkan:
7f2b8e4c6a1d3c9e5b2a7d4f1e6b3a8f5d2c9e7b4a1f6d3c8e5b2a9d7c4f1a3e$ docker port myapp-banyakOutput yang Dihasilkan:
8000/udp -> 0.0.0.0:9000
8000/udp -> [::]:9000
8000/tcp -> 0.0.0.0:9001
8000/tcp -> 0.0.0.0:9002
8000/tcp -> 0.0.0.0:9003
...Output di atas dipotong — tiga baris pasangan IPv6 untuk rentang tcp tidak ditampilkan. Mari kita telaah kombinasi flag-nya:
- Dua flag
-pdalam satudocker run— satu container boleh menerbitkan banyak pintu sekaligus; setiap flag menciptakan pemetaan yang independen. 9000:8000/udpmembuka pintu protokol udp — tcp dan udp adalah dua ruang nomor port yang terpisah; pemetaan udp tetap dibuat meskipun myapp tidak mendengarkan udp, sehingga klien udp yang menghubungi tidak akan dijawab. Kasus penggunaan aslinya adalah layanan berbasis udp seperti DNS pada port 53.- Rentang
9001-9003menghasilkan tiga pemetaan — setiap port host dalam rentang mengarah ke port container yang sama; pola ini berguna saat satu layanan harus dijangkau lewat beberapa pintu host sekaligus, misalnya selama migrasi nomor port bertahap.
Port Acak dengan -p 0 dan -P #
Menyebut angka port host setiap saat tidak selalu praktis — mesin uji, pekerjaan paralel, dan skrip lebih suka membiarkan daemon memilih. Port host 0 memerintahkan persis itu:
$ docker run -d --name myapp-nol -p 0:8000 myapp:1.0Output yang Dihasilkan:
9b3e7d1a5c2f4e8b2d6f1a9c7d3f5e2c9a6b8d1f3e5c7a9b2d4f6e8c0a3b5d7f$ docker port myapp-nolOutput yang Dihasilkan:
8000/tcp -> 0.0.0.0:32773
8000/tcp -> [::]:32773Mari kita telaah angka yang muncul begitu saja itu:
32773dipilih daemon dari rentang port tinggi — nilai ini berbeda pada setiap mesin dan setiapdocker run; selalu baca hasilnya lewatdocker portatau kolom PORTS, jangan berasumsi angkanya stabil.- Bentuk pendeknya serupa —
-p 8000yang hanya menyebut port container memberi efek yang sama: pemetaan ke port host acak. - Saudara tuanya adalah flag
-P— huruf besar tersebut menerbitkan seluruh daftar EXPOSE image ke port acak masing-masing, pola yang sudah kita buktikan dengan myapp-acak pada Chapter EXPOSE dan Port;-p 0memberi kendali eksplisit per port,-Pmenyerahkan seluruhnya pada daftar EXPOSE.
Ketiga alat penerbitan beserta EXPOSE kini bisa dirangkum dalam satu tabel pembanding:
| Aspek | -p | -P | EXPOSE |
|---|---|---|---|
| Siapa menetapkan port host | operator eksplisit — 0 berarti acak | daemon memilih acak | tidak ada — bukan penerbitan |
| Sumber port container | argumen flag | daftar EXPOSE pada image | instruksi Dockerfile |
| Kapan berlaku | saat container diciptakan | saat container diciptakan | saat build — metadata semata |
| Efek jaringan nyata | pemetaan dibuat | pemetaan dibuat | tidak ada |
| Terbaca lewat | docker port dan kolom PORTS | docker port dan kolom PORTS | docker image inspect |
Konflik Port #
Satu-satunya pintu host tidak boleh dipakai dua pemetaan sekaligus, dan itulah galat yang paling sering menyapa pemula. Budi mencoba bentuk kanonik 8080:8000 untuk myapp — tanpa sadar bahwa container web sudah lama menduduki 8080:
$ docker run -d --name myapp-8080 -p 8080:8000 myapp:1.0Output yang Dihasilkan:
docker: Error response from daemon: Bind for 0.0.0.0:8080 failed: port is already allocated
See 'docker run --help'.Mari kita telaah kegagalan itu beserta jalan keluarnya:
- Daemon gagal mengikat port host saat create — galat terjadi sebelum aplikasi myapp sempat menyala; konflik terdeteksi sedini mungkin karena pintu host dipegang daemon, bukan aplikasi.
- Container gagal tetap tercipta —
myapp-8080tertinggal berstatus Created tanpa pernah berjalan; bersihkan dengandocker rm myapp-8080sebelum mencoba lagi. - Pemakai port bisa container lain atau proses host — pemilik 8080 kali ini adalah container web dengan pemetaan
-p 8080:80; cari kandidat container lewatdocker psdan kolom PORTS, sedangkan layanan host di luar Docker ditelusuri dengan alat host sepertiss -tlnp.
Pembersihan dan solusinya berjalan singkat:
$ docker rm myapp-8080
$ docker run -d --name myapp-8080 -p 8002:8000 myapp:1.0Output yang Dihasilkan:
myapp-8080
2c6f9b3e7a1d5f8b2e4d6a3c9e1b7d3f5a2c8e4b1d6f3a9c7b2e5d4f1a3c6e9bMenggeser nomor port host adalah obat tercepat; alternatifnya dua — menghentikan layanan host yang memakai port tersebut, atau menyerahkan nomor tersebut dan memilih angka lain untuk container baru. Inventaris pintu yang sedang terbuka terbaca dalam satu perintah berformat:
$ docker ps --format "table {{.Names}}\t{{.Ports}}"Output yang Dihasilkan:
NAMES PORTS
web 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp
myapp-publik 0.0.0.0:8000->8000/tcp, [::]:8000->8000/tcp
myapp-lokal 127.0.0.1:8001->8000/tcp
myapp-banyak 0.0.0.0:9000->8000/udp, [::]:9000->8000/udp, 0.0.0.0:9001-9003->8000/tcp, ...
myapp-nol 0.0.0.0:32773->8000/tcp, [::]:32773->8000/tcp
myapp-8080 0.0.0.0:8002->8000/tcp, [::]:8002->8000/tcpOutput di atas dipotong pada baris myapp-banyak — pasangan IPv6 untuk rentang tcp tidak ditampilkan. Enam baris itu sekaligus merangkum seluruh wajah pemetaan yang kita bangun sepanjang chapter: pemetaan biasa, ikatan localhost, udp, rentang yang diringkas menjadi 9001-9003, dan port acak.
Catatan IPv6 #
Dua notasi alamat pada output di atas layak dikenali lebih dekat. Alamat 0.0.0.0 berarti seluruh antarmuka IPv4, sedangkan [::] berarti seluruh antarmuka IPv6 — itulah mengapa pemetaan tanpa segmen alamat selalu muncul berpasangan. Segmen alamat juga menerima alamat IPv6 dalam tanda kurung siku bila kita ingin membatasi pemetaan ke IPv6 saja:
$ docker run -d --name myapp-v6 -p "[::1]:8003:8000" myapp:1.0Output yang Dihasilkan:
6e1b4d8a2c7f9e3b5a1d6c8e4b2f7a9d1c3e5b6f8a2d4c6e1b3f5a7d9c2e4b6f$ docker port myapp-v6Output yang Dihasilkan:
8000/tcp -> [::1]:8003Notasi [::1] adalah loopback IPv6 — kebalikan langsung dari 127.0.0.1; pemetaan hanya hidup di antarmuka IPv6 localhost host, dan klien yang terhubung lewat IPv4 tidak akan menemukannya.
Budi membersihkan container latihan sebelum chapter ini ditutup, membiarkan web tetap hidup untuk latihan berikutnya:
$ docker rm -f myapp-publik myapp-lokal myapp-banyak myapp-nol myapp-8080 myapp-v6Output yang Dihasilkan:
myapp-publik
myapp-lokal
myapp-banyak
myapp-nol
myapp-8080
myapp-v6Pintu-pintu host kini terpetakan utuh — dari pemetaan eksplisit, ikatan localhost, udp, rentang, sampai port acak. Namun masih ada satu knob runtime yang kecil sekaligus sering dilupakan orang: direktori kerja tempat proses container mulai membaca dan menulis berkasnya. Chapter Working Directory menutup rangkaian lima flag dasar ini dengan -w.
Referensi #
- docker container run — CLI Reference
— flag
-p,-P, dan seluruh bentuk segmen alamat, port, serta protokol. - docker container port — CLI Reference — membaca pemetaan port container yang sedang berjalan.
- Docker Networking — konsep jaringan container yang menjadi dasar penerbitan port.