Chapter Writable Container Layer menutup dengan kesimpulan yang tidak bisa dinegosiasikan: data yang berharga tidak boleh tinggal di writable layer, karena satu docker rm sudah cukup menghapus semuanya. Pertanyaannya, seberapa nyata bahaya itu dalam praktik sehari-hari? Jawabannya paling terasa pada satu jenis beban kerja yang hampir semua tim miliki: database. Chapter ini membuka dengan kisah kehilangan data yang dialami Budi, membedah akar masalahnya pada konsep umur container versus umur data, lalu memetakan tiga jenis mount yang Docker sediakan sebagai jalan keluar — lengkap dengan kriteria memilih di antara ketiganya.

Kisah Budi: Database yang Lenyap Bersama Container #

Budi penasaran mencoba PostgreSQL versi 18 dalam wujud Alpine yang ramping. Ia menjalankan container baru dengan password contoh, persis pola yang biasa ia lihat di dokumentasi:

bash
$ docker run -d --name db-latihan -e POSTGRES_PASSWORD=rahasia123 postgres:18-alpine

Output yang Dihasilkan:

text
18-alpine: Pulling from library/postgres
ce44d321fd03: Pull complete
b7c70e50ce6a: Pull complete
...
Digest: sha256:1b50fd69a38322b7f1e2f3f2c9a2f370703d5718b327738152775c8a89df45a2
Status: Downloaded newer image for postgres:18-alpine
docker.io/library/postgres:18-alpine
07179c852aa6a925b6f4cd33dfe63e68d4984074e8086437f52e211c6c5e2704

Image terunduh untuk pertama kalinya — baris-baris layer dipotong karena cukup panjang — dan container db-latihan menyala. PostgreSQL membutuhkan beberapa detik untuk inisialisasi awal sebelum siap menerima koneksi; utilitas pg_isready bawaan image memberi kabar kapan waktunya tiba:

bash
$ docker exec db-latihan pg_isready -U postgres

Output yang Dihasilkan:

text
/var/run/postgresql:5432 - accepting connections

Penuh semangat, Budi membuat tabel latihan dan mengisinya dua baris data peserta:

bash
$ docker exec db-latihan psql -U postgres -c "CREATE TABLE peserta (nama TEXT); INSERT INTO peserta VALUES ('Budi'), ('Siti');"

Output yang Dihasilkan:

text
CREATE TABLE
INSERT 0 2
bash
$ docker exec db-latihan psql -U postgres -c "SELECT * FROM peserta;"

Output yang Dihasilkan:

text
 nama
------
 Budi
 Siti
(2 rows)

Data tersimpan, koneksi berjalan, dan semuanya tampak sempurna. Beberapa jam kemudian Budi ingin mencoba konfigurasi memori yang berbeda, jadi ia mengulang pola lama yang ia kuasai dari Section Konfigurasi Container: buang container lama, jalankan yang baru — image-nya sama, jadi harusnya cepat saja:

bash
$ docker rm -f db-latihan
$ docker run -d --name db-latihan -e POSTGRES_PASSWORD=rahasia123 postgres:18-alpine
$ docker exec db-latihan psql -U postgres -c "SELECT * FROM peserta;"

Output yang Dihasilkan:

text
db-latihan
634f0093cbbe47ee6430207913ca9bd4498c839db7391dc8a7d118d5e2e26e57
ERROR:  relation "peserta" does not exist
LINE 1: SELECT * FROM peserta;
                      ^

Mari kita telaah kejadian yang barusan menimpa Budi:

  1. docker rm -f db-latihan berjalan tanpa galat — penghapusan container memang operasi sah dan sering; inilah yang membuat bahayanya licik.
  2. Container baru menjalankan inisialisasi ulang — direktori data yang dilihat container baru masih kosong, jadi skrip entrypoint PostgreSQL menjalankan initdb dari nol dan melahirkan cluster bersih tanpa tabel peserta.
  3. Data lama menumpang di writable layer container lama — seluruh direktori data fisik PostgreSQL tinggal di writable layer yang ikut terbuang pada baris pertama; persis batasan yang dipetakan pada Chapter Writable Container Layer.
  4. Tidak ada galat, tidak ada peringatan — Docker tidak menganggap ada yang salah, karena memang tidak ada: data aplikasi bukan urusan lifecycle container.

Sebelum melanjutkan, Budi merapikan arena dengan menghapus container keduanya melalui docker rm -f db-latihan. Pelajaran utamanya sudah terbentuk: umur container dan umur data adalah dua hal yang berbeda, dan menggabungkan keduanya di satu wadah adalah kesalahan desain. Yang Budi butuhkan adalah tempat menulis data yang sengaja dipisahkan dari container — dan Docker menyediakan tiga bentuknya.

Tiga Jenis Mount sebagai Jalan Keluar #

Mekanisme pemisahan umur di Docker bernama mount: sebuah lokasi penyimpanan di luar writable layer dipasang ke sebuah path di dalam container, sehingga tulisan ke path itu tidak lagi berakhir di layer yang lenyap bersama container. Ada tepat tiga jenis mount, dan ketiganya dibedakan oleh pertanyaan sederhana: di mana data secara fisik tinggal, dan siapa yang mengelolanya.

Jenis pertama adalah volume: penyimpanan yang diciptakan dan dikelola penuh oleh Docker di area khususnya sendiri, /var/lib/docker/volumes di disk host. Kita hanya berurusan dengan nama volume; daemon yang menentukan lokasi persisnya, memasangnya ke container, dan menjaganya tetap hidup setelah container mati. Jenis kedua adalah bind mount: kita sendiri menunjuk sebuah path di filesystem host — path apa pun yang kita mau — dan kernel Linux memasang path itu apa adanya ke dalam container. Jenis ketiga adalah tmpfs mount: data tidak pernah menyinggahi disk sama sekali, melainkan tinggal di memori host dan berakhir begitu container berhenti.

AspekVolumeBind Mounttmpfs Mount
Bentuk flag -v / singkat-v nama-volume:/data-v /path/host:/data--tmpfs /data
Bentuk --mount panjang--mount type=volume,src=nama,dst=/data--mount type=bind,src=/path/host,dst=/data--mount type=tmpfs,destination=/data
Lokasi fisik dataArea khusus Docker di disk hostPath host apa pun yang kita tunjukMemori host, bukan disk
PengelolaDocker daemonKita sendiriDocker bersama kernel Linux
Umur dataSelamat dari docker rmSelama berkasnya ada di hostBerakhir saat container berhenti
PortabilitasTinggi — nama dipakai ulang lintas container dan hostRendah — bergantung struktur direktori hostTidak relevan — isinya memang sementara
Kegunaan khasDatabase, data aplikasiBerkas konfigurasi, source code developmentCache, secret sementara

Bentuk Eksplisit –mount #

Flag paling sering kita lihat di dokumentasi lama adalah -v, tetapi Docker juga menyediakan bentuk panjang --mount yang menuliskan setiap bagian secara eksplisit. Tiga jenis mount dibedakan lewat nilai type, sumber dinyatakan lewat src (alias source), dan tujuan di dalam container dinyatakan lewat dst (alias destination atau target). Bentuk umumnya:

text
--mount type=volume,src=<nama-volume>,dst=<path-di-container>
--mount type=bind,src=<path-di-host>,dst=<path-di-container>
--mount type=tmpfs,dst=<path-di-container>

Perhatikan bahwa tmpfs tidak punya src — tidak ada sumber di disk untuk dipasang, karena isinya akan lahir dan mati di memori. Perbedaan gaya penulisan kedua bentuk tersebut dirangkum tabel berikut:

Aspek-v singkat--mount panjang
Tata tulisSatu string dipisah titik duaPasangan key=value dipisah koma
KeterbacaanRingkas dan cepat diketikEksplisit — setiap bagian berlabel
Perilaku pada path host yang belum adaMembuat direktori otomatisMenolak dengan galat yang jelas
Paling tepat untukPerintah cepat di terminalSkrip, dokumentasi, dan berkas compose.yaml

Satu catatan kecil tentang baris terakhir: berkas compose.yaml yang akan kita kenal pada Section Docker Compose memakai gaya penulisan panjang yang serumpun dengan --mount — biasakan membaca bentuk eksplisit sejak sekarang akan memudahkan transisi ke sana.

Ketiga jenis mount itu masing-masing mendapat chapter penuh di section ini. Volume kita bedah berikutnya: cara membuatnya, memeriksa isinya, dan membuktikan datanya selamat dari docker rm. Bind Mount menyusul setelahnya dengan kekuatan khasnya — perubahan di host langsung terlihat di container tanpa rebuild, fondasi alur kerja development. tmpfs Mount menutup trio dengan perannya menyimpan data yang justru tidak boleh bertahan: cache dan secret sementara. Dua chapter lanjutan melengkapi perawatan data berkelanjutan: pola mencadangkan dan memulihkan volume di Backup dan Restore Volume, serta pindah data antar host dan antar jenis mount di Migrasi Data Container.

Kriteria Memilih Jenis Mount #

Dengan tiga pilihan di tangan, keputusan sebaiknya tidak diambil berdasarkan kebiasaan, melainkan kriteria. Empat pertanyaan berikut menutup hampir semua kasus:

  1. Portabilitas — apakah data ini kelak harus berpindah host, atau dipakai container lain di mesin yang sama? Bila ya, volume adalah kandidat terkuat karena identitasnya hanyalah sebuah nama, bukan path di host tertentu.
  2. Backup — bagaimana data ini dicadangkan secara rutin? Volume punya pola pencadangan yang teruji lewat container bantu, materi Backup dan Restore Volume; bind mount dicadangkan seperti berkas host biasa, yang praktis justru bila sudah ada infrastruktur backup host.
  3. Performa — bagaimana pola tulis aplikasinya? Database menulis secara acak dan intensif, dan volume menghindari biaya copy-on-write yang membebani writable layer; beban seperti ini tidak boleh menumpang writable layer.
  4. Kepemilikan berkas — siapa pemilik numerik berkasnya di host dan di container? Bind mount mempertemukan UID/GID host dengan UID/GID container secara langsung, dan pertemuan ini kerap melahirkan konflik izin yang kita bedah tuntas di Permission dan Ownership Volume.

Alur keputusan lengkapnya dapat diringkas menjadi diagram berikut.

Diagram di atas dibaca dari atas ke bawah seperti rangkaian pertanyaan. Mari kita telaah tiap cabangnya:

  1. Pertanyaan pertama menyaring masalahnya — data yang memang sementara, seperti berkas scratch satu sesi, tidak butuh mount apa pun dan cukup menumpang writable layer.
  2. Cabang kiri menangkap kebutuhan akses host — konfigurasi yang diedit editor dan source code yang diawasi tool development menginginkan berkas yang bisa disentuh langsung dari host; di situlah bind mount unggul.
  3. Cabang kanan memilah data fana dari data abadi — cache yang boleh dibangun ulang dari nol cocok di tmpfs, sementara data yang harus tetap ada menjawab volume.

Kerangka pemilihan ini akan terus kita pakai sepanjang section, dan chapter penutup section — Memilih Jenis Mount — menguliknya dengan skenario-skenario nyata yang lebih lengkap. Namun teori berhenti di sini: langkah paling meyakinkan adalah melihat data benar-benar selamat dari docker rm dengan mata kepala sendiri. Kita lakukan itu pada Chapter Volume.

Referensi #