Section Image Build dan Optimisasi ditutup dengan pengakuan yang perlu kita pegang sejak sekarang: image yang baik hanyalah separuh kebenaran Docker, karena image mendeskripsikan apa yang dijalankan, bukan bagaimana container menjalankannya. Section ini mengurus belahan kedua — seluruh knob runtime yang dibaca docker run pada saat container diciptakan, dari perintah start sampai siklus hidupnya. Knob pertama yang kita bedah bahkan bukan flag: segala argumen yang diketik setelah nama image secara diam-diam menggantikan perintah bawaan container. Budi menabrak kebutuhan ini begitu myapp mulai dipakai tim — ia ingin memeriksa versi Node.js yang terpasang di dalam image tanpa memasang apa pun di host, lalu menjalankan pekerjaan singkat yang selesai dalam hitungan detik memakai image yang sama persis.

Argumen Setelah Nama Image #

Bentuk lengkap docker run selama ini kita pakai tanpa dua bagian ekornya. Sintaks resminya adalah docker run [OPTIONS] IMAGE [COMMAND] [ARG...] — segala sesuatu yang diketik setelah nama image menjadi COMMAND beserta argumennya. Image myapp:1.0 membawa perintah bawaan CMD ["node", "server.js"] sejak dibangun pada Chapter Membangun Image dengan Dockerfile, dan perilaku CMD serta ENTRYPOINT sudah kita bedah tuntas pada Chapter CMD dan ENTRYPOINT. Bila COMMAND disebut saat docker run, daemon memakai COMMAND itu sebagai proses utama container dan mengabaikan CMD image sepenuhnya — hanya untuk container yang sedang diciptakan itu, tanpa menyentuh image aslinya.

Mari kita buktikan dengan menanyakan versi Node.js yang hidup di dalam image:

bash
$ docker run --rm myapp:1.0 node --version

Output yang Dihasilkan:

text
v22.20.0

Mari kita telaah satu baris output di atas:

  1. node --version menggantikan node server.js — daemon tidak menjalankan server HTTP myapp sama sekali; proses utama container kali ini adalah node dengan argumen --version, yang mencetak versi lalu langsung selesai.
  2. v22.20.0 — versi Node.js yang terpasang di dalam image node:22-alpine; angka mayor 22-lah yang dipatok tag, sedangkan angka minor dan patch mengikuti build basis yang tersimpan di mesinmu, jadi nilainya bisa berbeda di lingkungan lain.
  3. --rm membuang bekasnya — perintah sekali jalan meninggalkan container berstatus Exited (0) bila flag ini ditiadakan; --rm membersihkan container begitu prosesnya selesai, kebiasaan yang kita bawa sejak Section Dasar Docker CLI.
  4. Image tidak berubah — pemanggilan docker run myapp:1.0 berikutnya tanpa args kembali menjalankan node server.js; penimpaan menempel pada container, bukan pada image.

Bagaimana daemon memutuskan proses apa yang akhirnya dijalankan? Diagram berikut merangkum satu-satunya percabangan yang relevan:

Mari kita telaah jalur pada diagram di atas: cabang kiri berlaku untuk image yang hanya mendefinisikan CMD seperti myapp dan alpine, sedangkan cabang kanan mengurus image yang memasangkan ENTRYPOINT dengan CMD — keluarga besar image resmi seperti redis dan postgres. Kedua cabangnya kita bedah satu per satu sepanjang chapter ini.

Tanpa Shell, Tanpa Fitur Shell #

Args yang diketik setelah image dieksekusi langsung oleh daemon tanpa perantara shell. Konsekuensinya lurus: penggabungan perintah dengan &&, pipe, pengalihan berkas, dan penilaian variabel $VAR tidak bekerja, karena fitur-fitur itu milik shell, bukan milik proses. Perintah sh -c menghadirkan shell di dalam container kapan pun diperlukan — bentuk paling sederhananya menjalankan satu perintah di dalamnya:

bash
$ docker run --rm myapp:1.0 sh -c "echo tes"

Output yang Dihasilkan:

text
tes

Shell yang hadir lewat sh -c membawa seluruh fiturnya, dan perbedaannya paling terasa saat variabel ikut bermain. Mari kita bandingkan dua cara mencetak nilai HOME:

bash
$ docker run --rm myapp:1.0 echo $HOME

Output yang Dihasilkan:

text
/home/budi
bash
$ docker run --rm myapp:1.0 sh -c 'echo $HOME'

Output yang Dihasilkan:

text
/root

Mari kita telaah dua jawaban yang berbeda itu:

  1. $HOME pada perintah pertama diganti oleh shell host — sebelum docker run menerima argumen apa pun, shell di laptop Budi sudah mengganti $HOME menjadi /home/budi; container menerima args mentah echo /home/budi dan mencetaknya apa adanya.
  2. Kutip tunggal menunda penilaian sampai container — string echo $HOME sampai utuh ke sh di dalam container, dan shell itulah yang menilai $HOME menjadi /root, direktori rumah root — user bawaan myapp:1.0 yang akan kita ganti pembahasannya pada Chapter User dan Permissions.
  3. sh -c "echo tes" bekerja dengan cara yang sama — bentuk kutip ganda sah selama tidak ada variabel yang ingin ditunda penilaiannya; kutip tunggal adalah kebiasaan yang lebih aman karena isinya tidak pernah disentuh shell host.

Pola Container Sekali Jalan #

Kemampuan menimpa perintah melahirkan pola yang sangat produktif: container one-shot — dinyalakan untuk satu pekerjaan, selesai, lalu dibuang. Tabel berikut memetakan kebutuhan yang paling sering muncul sehari-hari:

KebutuhanPola perintahAlasannya
Memeriksa tool di dalam imagedocker run –rm image tool –versiversi tercetak tanpa memasang tool di host
Migrasi databasedocker run –rm image-app perintah-migrasipekerjaan sekali jalan; jejak container dibersihkan
Skrip diagnosa atau laporandocker run –rm image skrip.shhasil tercetak ke terminal, container langsung dibuang
Shell interaktif singkatdocker run -it image shmenjelajah filesystem image sambil hidup

Image alpine memberikan demo paling ringkas untuk pola ini. Argumen echo halo menimpa CMD bawaan alpine — yang merupakan sh — sehingga container mencetak sapaan lalu selesai:

bash
$ docker run --rm alpine:3.20 echo halo

Output yang Dihasilkan:

text
halo

Tanpa args, container alpine menjalankan CMD aslinya dan berperilaku sangat berbeda:

bash
$ docker run --rm alpine:3.20
$ echo "exit code: $?"

Output yang Dihasilkan:

text
exit code: 0

Mari kita telaah keheningan pada perintah pertama itu:

  1. CMD alpine adalah sh — container menjalankan shell sebagai proses utama, tetapi tanpa flag -i saluran masuknya tertutup, sehingga sh tidak menerima satu ketikan pun dan langsung mengakhiri dirinya.
  2. exit code: 0 — kepergian yang bersih; proses utama selesai dengan sukses sehingga container berhenti normal, persis klasifikasi yang kita latih pada Chapter Memahami Exit Code.
  3. Inilah sebabnya docker run -it alpine sh terasa hidup — flag -it membuka masukan dan menyiapkan terminal semu, mengubah shell yang sama menjadi sesi interaktif:
bash
$ docker run -it alpine:3.20 sh

Output yang Dihasilkan:

text
/ # echo di dalam shell container
di dalam shell container
/ # exit

Prompt / # adalah wajah busybox sh di Alpine. Perintah exit mengakhiri sh — proses utama container — sehingga container ikut berhenti; ini berbeda dari docker exec pada Chapter Menjalankan Perintah dalam Container yang menambahkan proses baru tanpa mengganggu proses utama container yang hidup.

Ketika Image Memakai ENTRYPOINT #

Image resmi besar umumnya memasangkan ENTRYPOINT dengan CMD, dan di sinilah miskonsepsi paling sering lahir: args setelah image tidak menggantikan ENTRYPOINT. Args tetap menggantikan CMD — tetapi karena ENTRYPOINT-lah yang dijalankan, args berubah peran menjadi parameter untuk ENTRYPOINT. Kita baca dulu konfigurasi image redis yang sudah tersimpan lokal sejak Section Dasar Docker CLI:

bash
$ docker image inspect --format 'Entrypoint: {{.Config.Entrypoint}}' redis:7

Output yang Dihasilkan:

text
Entrypoint: [docker-entrypoint.sh]
bash
$ docker image inspect --format 'Cmd: {{.Config.Cmd}}' redis:7

Output yang Dihasilkan:

text
Cmd: [redis-server]

Mari kita telaah konsekuensi kedua baris metadata itu:

  1. Tanpa args, container redis menjalankan docker-entrypoint.sh redis-server — ENTRYPOINT menerima CMD sebagai parameter; skrip entrypoint menyiapkan berkas-berkas yang dibutuhkan, lalu menyerahkan kendali ke redis-server.
  2. Dengan args, CMD digantikan — perintah docker run -d redis:7 redis-server --requirepass rahasia123 membuat entrypoint menerima parameter redis-server --requirepass rahasia123; inilah cara resmi image redis dikonfigurasi kata sandinya, dan args-nya memang sengaja berbentuk perintah lengkap.
  3. ENTRYPOINT tetap tak tersentuh — flag --entrypoint adalah satu-satunya cara menimpa ENTRYPOINT saat runtime, jarang dibutuhkan, dan pembahasan lengkapnya menunggu Chapter CMD dan ENTRYPOINT.

Dua wajah image berikut merangkum seluruh perilaku penimpaan:

Bentuk imageTanpa argsDengan args setelah imageContoh
CMD sajaCMD dijalankan sebagai proses utamaargs menggantikan CMD sepenuhnyamyapp:1.0, alpine
ENTRYPOINT dan CMDENTRYPOINT dijalankan dengan CMD sebagai parameterENTRYPOINT tetap; args mengambil peran CMD sebagai parameterredis:7, postgres, nginx

Command override memberi kita kuasa mengubah apa yang dijalankan container untuk satu kali jalan. Kebutuhan yang jauh lebih sering muncul justru lebih halus: aplikasi yang sama harus berperilaku berbeda antar lingkungan tanpa satu baris kode pun berubah — mode production di server, mode uji dengan basis data lain di laptop Siti. Konfigurasi semacam itulah wilayah environment variable, knob runtime kedua yang kita bedah pada Chapter Environment Variable.

Referensi #