Penutup Chapter Config dan Secret meninggalkan satu pertanyaan yang belum terjawab: bagaimana menambahkan service yang hanya layak menyala pada momen tertentu — misalnya utilitas untuk mengintip isi tabel produk ketika insiden terjadi — tanpa membuatnya ikut menyala pada setiap docker compose up? Menambahkannya sebagai service biasa berarti membayar biaya terus-menerus: container ekstra menyala di mesin setiap anggota tim, memakan memori, dan memperpanjang daftar yang harus dibaca pada docker compose ps. Compose menjawab kebutuhan ini lewat kunci profiles: service yang membawa profil hanya diinstansiasi bila profilnya sedang aktif, sehingga satu berkas tetap menjadi sumber kebenaran sementara wajah stack yang menyala bisa dipilih per skenario. Chapter ini membedah mekanisme itu tuntas — aktivasi lewat flag, lewat variabel environment, dan lewat penyebutan langsung; aturan ATAU untuk service beranggota banyak profil; sampai perilaku Compose yang menyalakan service ber-profil secara otomatis mengikuti benang dependensi.

Service yang Tidak Ikut Menyala #

Insiden kemarin sore menjadi pendorongnya. Koneksi antara web dan db tiba-tiba tersendat, dan Budi ingin memeriksa resolusi DNS serta jangkauan antar container dari dalam network katalog_default — persis latihan diagnosis Chapter Troubleshooting Network di Section Jaringan Container. Masalahnya, image katalog-web yang ramping tidak membawa satu pun peralatan diagnosa: tidak ada dig, tidak ada mtr, tidak ada tcpdump. Memasang peralatan itu ke image aplikasi hanya demi sesi insiden sesekali jelas menyalahi prinsip image minimal yang kita bangun sepanjang Section Image Build dan Optimisasi. Jalan yang benar adalah service terpisah yang membawa seluruh kotak peralatan, tetapi hanya menyala bila dipanggil:

yaml
  debug:
    image: nicolaka/netshoot
    command: sleep infinity
    tty: true
    profiles: ["debug"]

Mari kita telaah empat kunci service di atas:

  1. image: nicolaka/netshoot — image pihak ketiga yang mengemas puluhan peralatan diagnosa jaringan dalam satu bundle: dig, nslookup, curl, tcpdump, mtr, sampai iperf. Dengan membawanya sebagai service, seluruh peralatan itu hadir di dalam network project tanpa satu byte pun menempel pada image aplikasi.
  2. command: sleep infinity — netshoot dirancang sebagai wadah perintah sekali jalan; tanpa perintah pengganjal, container-nya selesai seketika lalu keluar. sleep infinity menahan container hidup tanpa batas waktu — padanan pola sleep 1h yang kita pakai berulang kali di Section Jaringan Container, tanpa jadwal kedaluwarsa.
  3. tty: true — mengalokasikan pseudo-TTY pada container sehingga sesi shell interaktif lewat docker compose exec terasa seperti terminal biasa, lengkap dengan baris perintah yang rapi.
  4. profiles: ["debug"] — kunci inti chapter ini: daftar profil yang harus aktif agar service diinstansiasi. Selama profil debug tidak aktif, Compose memperlakukan service ini seperti tidak ada.

Budi menambahkan blok itu ke compose.yaml lalu menjalankan perintah yang sama seperti biasa:

bash
$ docker compose up -d

Output yang Dihasilkan:

text
[+] Running 4/4
 ✔ Network katalog_default     Running
 ✔ Container katalog-db-1      Running
 ✔ Container katalog-web-1     Running
 ✔ Container katalog-proxy-1   Running

Tidak ada satu pun baris yang menyebut debug. Inventarisnya pun bersih:

bash
$ docker compose ps

Output yang Dihasilkan:

text
NAME             IMAGE               COMMAND                 SERVICE   CREATED         STATUS                    PORTS
katalog-db-1     postgres:18-alpine  "docker-entrypoint.s…"  db        4 minutes ago   Up 4 minutes (healthy)    5432/tcp
katalog-web-1    katalog-web         "node server.js"        web       4 minutes ago   Up 4 minutes (healthy)    0.0.0.0:8080->3000/tcp
katalog-proxy-1  nginx:1.29-alpine   "/docker-entrypoint.…"  proxy     4 minutes ago   Up 4 minutes              0.0.0.0:8090->80/tcp

Mari kita telaah dua bukti di atas: up melaporkan seluruh keadaan tidak berubah — tiga service inti tetap tiga service inti; dan ps tidak memuat baris katalog-debug-1 apa pun karena container itu memang tidak pernah diciptakan. Service ber-profil bukan service yang berhenti atau tersembunyi — ia service yang tidak dihitung sama sekali. Aktivasi membutuhkan satu flag:

bash
$ docker compose --profile debug up -d

Output yang Dihasilkan:

text
[+] Running 1/1
 ✔ Container katalog-debug-1  Started

Satu flag --profile debug, dan Compose menambahkan wajah kedua stack tanpa menyentuh ketiga service lainnya. Kehadiran debug di dalam network bisa dibuktikan seketika:

bash
$ docker compose exec debug nslookup db

Output yang Dihasilkan:

text
Server:   127.0.0.11
Address:  127.0.0.11:53

Non-authoritative answer:
Name:     db
Address:  172.18.0.2

Jawaban itu datang dari embedded DNS di 127.0.0.11 — server DNS milik user-defined network yang kita bedah pada Chapter Container DNS. Service debug tidak mendeklarasikan networks apa pun, sehingga ia bergabung ke network bawaan project dan menyelesaikan nama db dengan cara yang persis sama seperti web atau proxy. Seluruh peralatan netshoot kini berdiri satu hop dari setiap service katalog.

Profil Ganda dan Aturan ATAU #

Satu service boleh menjadi anggota lebih dari satu profil, dan aturan keanggotaannya adalah ATAU: service menyala bila salah satu profilnya aktif. Kasusnya nyata di katalog — Budi ingin service pengisi data contoh yang berguna saat sesi debug dan saat pipeline pengujian berjalan. Ia menambahkan service seed:

yaml
  seed:
    image: postgres:18-alpine
    profiles: ["debug", "ci"]
    depends_on:
      db:
        condition: service_healthy
    environment:
      PGPASSWORD: ${POSTGRES_PASSWORD}
    command:
      - psql
      - -h
      - db
      - -U
      - katalog
      - -d
      - katalog
      - -c
      - "INSERT INTO produk SELECT 'Teh Hijau', 52000 WHERE NOT EXISTS (SELECT 1 FROM produk WHERE nama = 'Teh Hijau');"

Mari kita telaah lima bagian service di atas:

  1. profiles: ["debug", "ci"] — dua keanggotaan sekaligus; aktivasi profil debug saja, profil ci saja, atau keduanya, sama-sama menjadikan seed kandidat untuk diinstansiasi.
  2. depends_on dengan condition: service_healthy — seed menunggu db lulus healthcheck sebelum mengetuk pintunya, pola gerbang yang kita bangun pada Chapter depends_on dan Startup Order.
  3. environment dengan PGPASSWORD: ${POSTGRES_PASSWORD} — klien psql membaca password dari variabel itu; nilainya diambil dari .env lewat interpolasi — mekanisme ${...} yang kita kenal sejak Chapter Environment dan env File dan yang akan kita bedah jauh lebih dalam pada chapter berikutnya.
  4. command bentuk daftar — menimpa CMD image dengan psql beserta seluruh argumennya dalam exec form; entry point image postgres meloloskan perintah selain postgres langsung ke eksekusi, persis perilaku yang kita manfaatkan pada Chapter Command Override.
  5. WHERE NOT EXISTS pada perintah INSERT — seed dirancang idempoten: dijalankan berkali-kali pun, baris Teh Hijau hanya tercatat sekali. Service one-shot yang dipanggil ulang adalah kandidat sempurna untuk berulang, dan menulis ulang data ganda adalah jenis kecelakaan yang seharusnya mustahil.

Sekarang uji aturan ATAU-nya. Budi menghentikan debug lebih dulu agar hasil percobaan mudah dibaca — docker compose stop debug — lalu mengaktifkan profil ci saja:

bash
$ docker compose --profile ci up -d

Output yang Dihasilkan:

text
[+] Running 2/2
 ✔ Container katalog-db-1    Running
 ✔ Container katalog-seed-1  Started
bash
$ docker compose exec db psql -U katalog -d katalog -c "SELECT * FROM produk;"

Output yang Dihasilkan:

text
       nama        | harga
-------------------+-------
 Kopi Arabika Gayo | 85000
 Teh Melati        | 45000
 Teh Hijau         | 52000
(3 rows)

Mari kita telaah hasil percobaan tersebut:

  1. seed menyala karena profil ci aktif — padahal flag --profile debug tidak ditulis; keanggotaan ganda berarti satu jalur saja sudah cukup.
  2. debug tetap berhenti — profil ci bukan anggota debug, sehingga service itu tidak tersentuh; inilah bukti bahwa aturan ATAU dievaluasi per service, bukan per berkas.
  3. db dilaporkan Running — Compose menampilkan benang dependensi dari service yang dinyalakan; gerbang service_healthy sudah terlewati karena db memang sedang sehat.
  4. Baris Teh Hijau hadir di tabel — seed selesai bekerja lalu keluar dengan exit code 0; docker compose ps akan mencatat container itu sebagai Exited (0), status wajar bagi service sekali jalan.
  5. Inilah utilitas mengintip isi tabel yang dijanjikan penutup chapter sebelumnya — query SELECT di atas dijalankan dari jalur seed, dan pola yang sama bisa dipakai untuk memeriksa data kapan pun insiden butuh bukti.

Daftar seluruh profil yang dikenal sebuah berkas bisa ditanya langsung:

bash
$ docker compose config --profiles

Output yang Dihasilkan:

text
debug
ci

COMPOSE_PROFILES Tanpa Flag #

Flag --profile menempel pada satu pemanggilan perintah. Untuk sesi kerja panjang yang selalu membutuhkan profil tertentu, mengetik ulang flag pada setiap perintah jelas membosankan, dan Compose menyediakan pintu keduanya: variabel environment COMPOSE_PROFILES. Nilainya berupa daftar profil dipisah koma, dan ia boleh didefinisikan di shell maupun di dalam .env. Budi menghentikan debug dengan docker compose stop debug, menambahkan satu baris pada .env:

text
POSTGRES_PASSWORD=rahasia123
LOG_LEVEL=info
COMPOSE_PROFILES=debug

lalu menjalankan perintah paling polos yang ada:

bash
$ docker compose up -d

Output yang Dihasilkan:

text
[+] Running 1/1
 ✔ Container katalog-debug-1  Started

Tanpa satu flag pun, profil debug aktif dan service-nya menyala. Nilai yang diekspor di shell bekerja dengan cara yang sama — COMPOSE_PROFILES=debug docker compose up -d — dan bila keduanya hadir bersamaan, nilai dari shell menimpa nilai .env, mengikuti urutan sumber yang sama dengan interpolasi yang kita bedah nanti. Setelah percobaan selesai Budi membuang baris itu dari .env: kalau dibiarkan, debug akan ikut menyala pada setiap up di mesinnya — dan profil yang selalu aktif memang sebaiknya tidak dibuat profil.

Service yang Menyala Sendiri #

Dua aturan pelengkap membuat profiles tidak menggantungkan diri pada flag. Aturan pertama: menyebut nama service ber-profil secara langsung mengaktifkan profilnya. Perintah docker compose up -d debug sama sahnya dengan --profile debug up -d — Compose melihat target eksplisit, mengaktifkan profil yang dibutuhkan target itu, lalu baru bekerja. Aturan kedua: service yang bergantung pada service ber-profil ikut menyalakan profil dependensinya. Ilustrasinya paling jelas lewat pola berikut:

yaml
  laporan:
    image: postgres:18-alpine
    depends_on:
      - db-arsip

  db-arsip:
    image: postgres:18-alpine
    profiles: ["arsip"]

Mari kita telaah interaksi kedua service di atas:

  1. laporan tidak memiliki profil — ia selalu dihitung sebagai kandidat service setiap up.
  2. db-arsip berprofil arsip — bila dibiarkan sendiri, ia hanya menyala saat profil itu aktif.
  3. depends_on menyatukan keduanya — begitu docker compose up -d laporan dijalankan, Compose menelusuri dependensi laporan, menemukan db-arsip ber-profil, dan mengaktifkan profil arsip secara otomatis; dependensi yang menggantung lebih berbahaya daripada profil yang ikut menyala.
  4. Pola ini bukan tambahan baru pada katalog — blok di atas adalah ilustrasi pola untuk stack yang membutuhkan pasangan seperti aplikasi laporan dan arsip datanya; katalog kita tetap pada debug dan seed.

Kapan Profiles Layak Dipakai #

Kebutuhan “service yang kadang perlu” ternyata punya banyak wujud. Tabel berikut memetakan skenario yang paling sering dijumpai bersama nama profil yang lazim dipakai komunitas:

SkenarioProfil khasIsi service-nya
Kotak peralatan saat insidendebugimage netshoot dengan sleep infinity, siap dimasuki exec
Pengujian beban pra-rilisloadtestrunner k6 beserta skrip ujinya, menyala hanya saat jendela uji
Tugas sekali jalanci, backuppg_dump, seed data, atau migrasi yang bekerja lalu keluar
Versi database alternatifujiimage postgres dengan tag major berbeda untuk latihan migrasi

Pola umumnya sama pada keempat baris: service itu mahal, berisiko, atau tidak bermakna bila menyala terus-menerus, tetapi sangat berharga pada momen tertentu. Profiles memungkinkan service seperti itu hidup di dalam berkas yang sama dengan stack utama — terdokumentasi, ter-version-control, dan tetap satu perintah untuk dipanggil.

Profiles Bukan Lingkungan Terpisah #

Batas pemakaian yang paling sering disalahpahami perlu ditegaskan sebelum chapter ini ditutup. Profiles memilih service mana yang menyala; ia tidak menyetel nilai konfigurasi apa pun.

AspekprofilesBerkas override
Yang dipilihservice mana yang diinstansiasinilai konfigurasi tiap service
Cara mengaktifkan--profile, COMPOSE_PROFILES, atau target eksplisitberkas dengan nama khusus dimuat otomatis, atau -f eksplisit
Cocok untukservice opsional per skenarioselisih kebutuhan antar orang atau antar lingkungan
Dibedah tuntas dichapter iniCompose Override dan Environment

Tutup chapter dengan siklus penuh. docker compose down membongkar seluruh container milik project tanpa peduli profil — container yang sedang berhenti pun ikut dijemput:

bash
$ docker compose down

Output yang Dihasilkan:

text
[+] Running 9/9
 ✔ Container katalog-proxy-1   Stopped
 ✔ Container katalog-proxy-1   Removed
 ✔ Container katalog-web-1     Stopped
 ✔ Container katalog-web-1     Removed
 ✔ Container katalog-db-1      Stopped
 ✔ Container katalog-db-1      Removed
 ✔ Container katalog-debug-1   Removed
 ✔ Container katalog-seed-1    Removed
 ✔ Network katalog_default     Removed
bash
$ docker compose up -d

Output yang Dihasilkan:

text
[+] Running 4/4
 ✔ Network katalog_default     Created
 ✔ Container katalog-db-1      Healthy
 ✔ Container katalog-web-1     Started
 ✔ Container katalog-proxy-1   Started

Siklus itu memperlihatkan janji profiles utuhnya: satu berkas, dua wajah — up biasa selalu kembali ke tiga service inti, sementara debug dan seed tidur nyenyak di dalam teks compose.yaml sampai dipanggil pada saat dibutuhkan. Namun perhatikan kembali satu baris kecil pada service seed: PGPASSWORD: ${POSTGRES_PASSWORD} — nilai yang dicari Compose dari luar berkas, dari .env atau dari shell. Tanda ${...} itu selama ini kita pakai tanpa membedah peta lengkapnya: dari mana saja nilai dicari, dalam urutan apa, bagaimana memberi nilai cadangan, dan bagaimana membuat Compose menolak bekerja dengan pesan yang jelas ketika nilai yang wajib ternyata tidak ada. Keseluruhan mekanisme itu adalah materi Chapter Variable Interpolation.

Referensi #