Peta anatomi pada chapter sebelumnya menempatkan services sebagai satu-satunya blok wajib — dan memang di sanalah seluruh kehidupan stack berlangsung. Dua baris web dan db pada berkas katalog kita sejauh ini masih memakai dua kunci saja, padahal sebuah service sanggup menerima delapan belas kunci konfigurasi yang paling sering dipakai sehari-hari. Chapter ini membedah konsep service itu sendiri — mengapa ia disebut cetakbiru dan bukan container, bagaimana pola penamaan <project>-<service>-<index> bekerja — lalu menumbuhkan berkas katalog dengan kebijakan restart dan trio variabel PostgreSQL, memperkenalkan docker compose ps beserta kolom khasnya, dan menutup dengan dua alat identitas: container_name untuk nama tetap serta --scale untuk banyak replika sekaligus.
Service: Cetakbiru yang Diinstansiasi #
Kata service pada Compose punya makna yang tepat dan sering disalahpahami. Sebuah service bukanlah container: service adalah cetakbiru — deklarasi lengkap tentang bagaimana container miliknya harus diciptakan, dari image apa, dengan environment apa, dan port mana yang diterbitkan. Container yang berjalan di daemon adalah instansiasi dari cetakbiru itu, hubungan yang persis seperti hubungan kelas dengan objek pada pemrograman berorientasi objek.
Bukti hubungan itu terlihat langsung pada nama container. Ketika Compose menciptakan container untuk service web pada project katalog, container tersebut bernama katalog-web-1 — pola <project>-<service>-<index> — tanpa satu pun flag --name yang kita tulis pada berkas. Angka 1 di ujung bukan hiasan: ia adalah slot replika yang akan terisi 2, 3, dan seterusnya ketika kita menskalakan service di akhir chapter ini. Nama container yang dulu kita tentukan manual lewat --name kini dihasilkan dari tiga identitas yang sudah kita pahami: project dari blok name, service dari kunci di bawah services, dan index dari jumlah replika.
Karena container hanyalah instansiasi, menghapusnya tidak pernah mengubah cetakbiru. Perintah docker compose up selalu membaca berkas, membandingkan deklarasi dengan keadaan daemon, lalu menciptakan ulang container yang diperlukan — keandalan yang akan kita saksikan berulang kali sepanjang section ini.
Delapan Belas Kunci yang Paling Sering Dipakai #
Sebelum menumbuhkan berkas katalog, mari kita petakan dulu seluruh kunci yang akan kita jumpai berulang kali. Tabel berikut merangkum delapan belas kunci konfigurasi service yang paling sering dipakai beserta chapter yang membahasnya tuntas:
| Kunci | Peran | Dibahas di |
|---|---|---|
image | image siap pakai dari registry | Image, Build, dan Pull |
build | membangun image sendiri dari Dockerfile | Image, Build, dan Pull |
container_name | nama tetap pengganti pola otomatis | chapter ini |
command | menimpa CMD image | Command Override |
entrypoint | menimpa ENTRYPOINT image | Command Override |
environment | variabel lingkungan untuk service | Environment dan env File |
env_file | memuat variabel dari berkas terpisah | Environment dan env File |
ports | menerbitkan port ke host | Port dan Network |
expose | mendokumentasikan port antar service | Port dan Network |
volumes | menempel volume atau bind mount | Volume dan Bind Mount |
networks | network yang dimasuki service | Port dan Network |
depends_on | urutan dan syarat start antar service | depends_on dan Startup Order |
restart | kebijakan bangkit otomatis | Restart Policy |
working_dir | direktori kerja proses container | Working Directory |
user | identitas UID/GID proses | User dan Permissions |
healthcheck | perintah pemeriksaan kesehatan | Healthcheck dan Service Readiness |
labels | label metadata pada container | VOLUME dan LABEL |
deploy | batas resource dan replika lanjutan | Resource di Compose |
Sebagian besar baris tabel itu adalah padanan langsung flag docker run yang sudah kita kuasai dari Section Konfigurasi Container — environment menggantikan -e, working_dir menggantikan -w, user menggantikan --user. Perbedaan besarnya satu: seluruh nilai kini hidup di berkas yang bisa dibaca, dibedah, dan di-commit, bukan di riwayat shell.
Menumbuhkan Berkas katalog #
Berkas katalog kita tumbuh dua langkah pada chapter ini. Service db menerima trio variabel yang dulu kita buktikan efeknya lewat container db-katalog pada Chapter Environment Variable, dan kedua service menerima kebijakan restart:
name: katalog
services:
web:
image: nginx:1.29-alpine
ports:
- "8080:80"
restart: unless-stopped
db:
image: postgres:18-alpine
environment:
- POSTGRES_USER=katalog
- POSTGRES_PASSWORD=rahasia123
- POSTGRES_DB=katalog
restart: unless-stoppedMari kita telaah pertumbuhan tersebut:
- Trio
POSTGRES_USER,POSTGRES_PASSWORD, danPOSTGRES_DB— ketiganya dibaca entrypoint postgres saat direktori data masih kosong: superuser bernamakatalogdengan kata sandirahasia123dan basis data awal bernamakatalog; nilai yang sama persis dengan demodb-katalogpada Chapter Environment Variable, kini tertulis sebagai deklarasi dan bukan flag yang hilang di riwayat shell. restart: unless-stoppedpada kedua service — padanan flag--restartdari Chapter Restart Policy: container yang mati mendadak dibangkitkan lagi, container yang kita hentikan manual tetap berhenti, dan keduanya ikut hidup kembali saat daemon menyala setelah server reboot.- Kebijakan menempel per service —
restartditulis padawebdandbsecara terpisah karena keduanya berhak memilih kebijakan berbeda; tidak ada satu kunci yang berlaku untuk seluruh berkas kecuali kita memakaix-fragment dari chapter sebelumnya. - Bentuk daftar pada
environment— kita tetap memakai bentuk daftar- KUNCI=nilaikarena mudah dibaca berurutan; rendererdocker compose configakan menyeragamkannya menjadi pemetaan, persis bukti pada Chapter Struktur compose.yaml.
Menjalankan dalam Latar dan Membaca ps #
Berkas yang lebih kaya ini kita nyalakan dalam mode latar dengan flag -d — padanan -d pada docker run. Sejak menghapusnya di Chapter Menghapus Image, image nginx:1.29-alpine tidak lagi tersimpan di mesin Budi, sehingga Compose menariknya kembali lebih dulu:
$ docker compose up -dOutput yang Dihasilkan:
[+] Pulling 1/1
✔ web Pulled 4.9s
[+] Running 5/5
✔ Network katalog_default Created 0.2s
✔ Container katalog-db-1 Created 0.1s
✔ Container katalog-db-1 Started 0.5s
✔ Container katalog-web-1 Created 0.0s
✔ Container katalog-web-1 Started 0.4sInventaris stack kini dibaca lewat perintah milik Compose sendiri:
$ docker compose psOutput yang Dihasilkan:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
katalog-db-1 postgres:18-alpine "docker-entrypoint.s…" db About a minute ago Up About a minute 5432/tcp
katalog-web-1 nginx:1.29-alpine "/docker-entrypoint.…" web About a minute ago Up About a minute 0.0.0.0:8080->80/tcp, [::]:8080->80/tcpMari kita telaah isi tabel tersebut:
- Kolom
SERVICE— kolom yang tidak dimilikidocker psbawaan; kolom inilah tanda tangan Compose. Satu image yang sama boleh dipakai banyak service sekaligus, sehingga identitas container yang sesungguhnya penting bukan imagenya, melainkan service cetakbirunya — dan saat kita menskalakan service di bawah, kolom inilah yang membedakan asal tiga container kembar. NAMEmengikuti pola<project>-<service>-<index>—katalog-db-1dankatalog-web-1, persis pola yang kita bedah di awal chapter.PORTSuntuk db hanya mencantumkan5432/tcp— tanpa pemetaan ke host, karena service db tidak punya kunciports; angka itu sekadar metadata EXPOSE milik image postgres yang kita kenal dari Chapter EXPOSE dan Port.COMMANDmenampilkan perintah bawaan image dalam bentuk terpotong — entrypoint postgres dan nginx yang sudah kita bedah pada Chapter CMD dan ENTRYPOINT.
Compose juga bisa memandang lebih tinggi: menampilkan daftar seluruh project yang sedang berjalan di daemon, apa pun foldernya:
$ docker compose lsOutput yang Dihasilkan:
NAME STATUS FILES
katalog running(2) /home/budi/katalog/compose.yamlDua kolom terakhirnya langsung menjelaskan diri: status running(2) berarti dua container hidup di bawah project itu, dan FILES menunjuk berkas deklarasi yang menjadi sumber kebenarannya.
Project Kedua dengan -p #
Chapter sebelumnya menjanjikan peragaan flag -p, dan skenarionya nyata: Budi ingin menjalankan stack yang sama berdampingan sebagai lingkungan uji. Perintah berikut ia coba dari folder katalog tempat project pertama masih menyala:
$ docker compose -p katalog-uji up -dOutput yang Dihasilkan:
[+] Running 3/3
✔ Network katalog-uji_default Created 0.2s
✔ Container katalog-uji-db-1 Started 0.5s
✔ Container katalog-uji-web-1 Created 0.1s
Error response from daemon: driver failed programming external connectivity on endpoint katalog-uji-web-1 (9a3f2c8e4b1d): Bind for 0.0.0.0:8080 failed: port is already allocatedMari kita telaah kegagalan yang setengah jalan itu:
- Flag
-p katalog-ujiberhasil mengganti identitas project — networkkatalog-uji_defaultdan containerkatalog-uji-db-1lahir dengan prefix baru, bukti bahwa nama project memang bisa dipaksakan perintah tanpa menyentuh berkas. - db sempat menyala sepenuhnya — service db tidak menerbitkan port host apa pun, sehingga tidak ada yang bisa konflik; kegagalan hanya menimpa web.
- Galatnya adalah konflik port lama kita —
Bind for 0.0.0.0:8080 failed: port is already allocated, persis wujud yang kita bedah pada Chapter Port Publishing; pemegang port 8080 adalahkatalog-web-1milik project pertama, dan satu pintu host tidak boleh dipakai dua pemetaan sekaligus. - Satu berkas memang hanya punya satu nilai
ports— selama kedua project membaca berkas yang sama, keduanya menuntut port yang sama; lingkungan kedua menuntut berkas dengan nomor port berbeda.
Budi membersihkan sisa kegagalan itu, lalu menyiapkan berkas uji sebagai salinan dengan satu angka yang digeser:
$ docker compose -p katalog-uji down
$ mkdir -p ../katalog-uji
$ cp compose.yaml ../katalog-uji/compose.yaml
$ sed -i 's/"8080:80"/"8081:80"/' ../katalog-uji/compose.yaml
$ cd ../katalog-uji
$ docker compose -p katalog-uji up -dOutput yang Dihasilkan:
[+] Running 5/5
✔ Network katalog-uji_default Created 0.2s
✔ Container katalog-uji-db-1 Created 0.1s
✔ Container katalog-uji-db-1 Started 0.5s
✔ Container katalog-uji-web-1 Created 0.0s
✔ Container katalog-uji-web-1 Started 0.4sPenulisan -p katalog-uji pada perintah terakhir sebenarnya mubazir — nama folder katalog-uji sudah otomatis menjadi nama project — tetapi menuliskannya eksplisit membuat identitas project tidak lagi bergantung pada kebetulan nama folder tempat berkas berada. Kedua project kini berdiri berdampingan, terbaca dalam satu inventaris:
$ docker compose lsOutput yang Dihasilkan:
NAME STATUS FILES
katalog running(2) /home/budi/katalog/compose.yaml
katalog-uji running(2) /home/budi/katalog-uji/compose.yamlLingkungan uji selesai dipakai dan dibongkar dari dalam foldernya sendiri, lalu Budi kembali ke folder utama:
$ docker compose down
$ cd ../katalogOutput yang Dihasilkan:
[+] Running 3/3
✔ Container katalog-uji-web-1 Removed 0.3s
✔ Container katalog-uji-db-1 Removed 0.5s
✔ Network katalog-uji_default Removed 0.2scontainer_name: Nama Tetap #
Pola <project>-<service>-<index> bisa diganti dengan nama pilihan kita lewat kunci container_name:
services:
db:
image: postgres:18-alpine
container_name: katalog-dbDengan deklarasi itu, container akan bernama katalog-db — tanpa prefix project dan tanpa index. Nama yang tetap memang nyaman bagi skrip luar yang memanggil docker logs katalog-db atau docker exec katalog-db psql ..., tetapi kenyamanannya dibayar mahal:
Menskalakan Replika dengan –scale #
Ujung dari cerita index adalah kemampuan yang dibuatnya mungkin: satu service, banyak container. Perintah berikut meminta tiga replika web:
$ docker compose up -d --scale web=3Output yang Dihasilkan:
[+] Running 4/4
✔ Container katalog-db-1 Running 0.0s
✔ Container katalog-web-1 Running 0.0s
✔ Container katalog-web-2 Created 0.1s
✔ Container katalog-web-3 Created 0.1s
Error response from daemon: driver failed programming external connectivity on endpoint katalog-web-2 (4b7e2d9c8a1f): Bind for 0.0.0.0:8080 failed: port is already allocatedAritmetikanya memang tidak bisa menipu. Pemetaan "8080:80" menyediakan tepat satu pintu host, sedangkan --scale web=3 menghadirkan tiga container yang masing-masing menuntut pintu sendiri; katalog-web-1 sudah memegang 8080, sehingga katalog-web-2 gugur saat Compose mencoba mengikatkan port yang sama — dan katalog-web-2 serta katalog-web-3 tertinggal berstatus Created tanpa pernah menyala. Solusinya adalah menyediakan rentang pintu yang sama banyaknya dengan replika:
$ docker compose down
$ sed -i 's/"8080:80"/"8080-8082:80"/' compose.yaml
$ docker compose up -d --scale web=3Output yang Dihasilkan:
[+] Running 5/5
✔ Network katalog_default Created 0.2s
✔ Container katalog-db-1 Started 0.5s
✔ Container katalog-web-1 Started 0.4s
✔ Container katalog-web-2 Started 0.3s
✔ Container katalog-web-3 Started 0.3sBaris-baris Created dipotong agar ringkas — keduanya hadir di antara network dan baris Started seperti pada output-output sebelumnya. Rentang 8080-8082 dibagi rata: replika pertama menerima 8080, kedua 8081, ketiga 8082. Inventarisnya terbaca dalam satu tabel:
$ docker compose psOutput yang Dihasilkan:
NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
katalog-db-1 postgres:18-alpine "docker-entrypoint.s…" db About a minute ago Up About a minute 5432/tcp
katalog-web-1 nginx:1.29-alpine "/docker-entrypoint.…" web About a minute ago Up About a minute 0.0.0.0:8080->80/tcp, [::]:8080->80/tcp
katalog-web-2 nginx:1.29-alpine "/docker-entrypoint.…" web About a minute ago Up About a minute 0.0.0.0:8081->80/tcp, [::]:8081->80/tcp
katalog-web-3 nginx:1.29-alpine "/docker-entrypoint.…" web About a minute ago Up About a minute 0.0.0.0:8082->80/tcp, [::]:8082->80/tcpKetiga pintu benar-benar hidup dan melayani halaman yang sama:
$ curl -s -o /dev/null -w "%{http_code}\n" localhost:8080 localhost:8081 localhost:8082Output yang Dihasilkan:
200
200
200Kembali ke satu replika adalah satu perintah — replika kelebihan dibongkar otomatis:
$ docker compose up -d --scale web=1Output yang Dihasilkan:
[+] Running 3/3
✔ Container katalog-web-3 Removed 0.2s
✔ Container katalog-web-2 Removed 0.2s
✔ Container katalog-web-1 Running 0.0sBudi mengembalikan pemetaan port berkas ke bentuk semula dengan sed -i 's/"8080-8082:80"/"8080:80"/' compose.yaml, karena katalog hanya membutuhkan satu pintu web. Satu batas penting perlu dicatat sebelum chapter ditutup: skala cocok untuk service yang stateless seperti web, tetapi --scale db=3 bukan cara membuat cluster database. Tiga replika postgres akan berjalan sebagai tiga server yang saling asing dengan writable layer masing-masing — data tercecer tiga tempat tanpa mekanisme replikasi; database adalah service stateful, dan pola pengelolaannya yang benar menanti di Section Docker dan Database.
Cetakbiru web pada berkas katalog kini memakai satu image siap pakai nginx:1.29-alpine — pengganti sementara yang kita janjikan sejak chapter pembuka. Service yang sama bisa saja membangun imagenya sendiri dari Dockerfile di dalam proyek, menarik versi terkunci dengan digest, atau mengatur kapan image harus ditarik ulang. Chapter Image, Build, dan Pull mengganti pengganti sementara itu dengan aplikasi katalog yang sesungguhnya.
Referensi #
- services — Compose file reference — definisi blok services beserta seluruh kunci konfigurasinya.
- docker compose ps — referensi perintah Compose untuk memeriksa service yang berjalan.
- Docker Compose how-tos — resep praktis resmi, termasuk menskalakan service.