Hari 4 — Networking & Volume: Kenapa Data Bertahan (atau Hilang)
Kamis, 2 jam. Menjawab tuntas 2 pertanyaan yang sengaja dibiarkan menggantung sejak Minggu 1-3: kenapa data Postgres bisa hilang, dan kenapa host.docker.internal dibutuhkan.
Tujuan Belajar
- Menjelaskan kenapa data di dalam container hilang saat container dihapus, dan bagaimana volume mencegah itu
- Membedakan bind mount vs named volume, dan kapan masing-masing dipakai
- Menjelaskan mekanisme network default Docker (bridge network) untuk komunikasi antar container
- Menerapkan volume ke service yang benar-benar butuh data persisten di repo sendiri
Untuk Instruktur
Materi ini idealnya dibuka dengan demo langsung: jalankan container Postgres baru, INSERT 1 baris, docker rm -f container-nya, jalankan container baru dari image yang sama, lalu tunjukkan datanya hilang. Efeknya jauh lebih kuat dilihat langsung daripada dijelaskan sebagai teori — banyak peserta baru pertama kali menyadari bind mount ./data:/opt/airflow/data yang sudah mereka pakai sejak Minggu 3 sebenarnya adalah jawaban dari masalah ini.
Konsep & Sintaks
Kenapa Data di Container Hilang
Filesystem container itu terikat ke siklus hidup container-nya — dibuat saat container dibuat, dihapus saat container dihapus (docker rm). Ini konsekuensi langsung dari konsep image vs container (hari_1_container_vs_vm.md): image adalah read-only template, dan container menambahkan 1 layer writable tipis di atasnya untuk perubahan selama berjalan — layer writable ini yang lenyap begitu container-nya dihapus.
docker run postgres:16 --name test-db
-> INSERT data (ditulis ke layer writable container "test-db")
docker rm -f test-db (layer writable ikut terhapus bersama container)
docker run postgres:16 --name test-db-2
-> data tadi TIDAK ADA (container baru = layer writable baru, kosong)
Volume menyelesaikan ini dengan menyimpan data di luar siklus hidup container — data tetap ada meski container yang memakainya dihapus, dan bisa “dipasang ulang” ke container baru.
Bind Mount vs Named Volume
Dua cara menghubungkan data eksternal ke dalam container, sudah dipakai (tanpa dijelaskan bedanya) sejak Minggu 3:
volumes:
- ./dags:/opt/airflow/dags # <- BIND MOUNT: path spesifik di HOST
- pgdata:/var/lib/postgresql/data # <- NAMED VOLUME: dikelola Docker, kamu tidak perlu tahu lokasinya
| Bind Mount | Named Volume | |
|---|---|---|
| Lokasi data | Path eksplisit di host yang kamu tentukan (./dags) | Dikelola Docker, tersimpan di area internal Docker (kamu tidak perlu tahu lokasi persisnya) |
| Cocok untuk | Development — kode di host langsung ter-reflect ke container tanpa rebuild image | Production/data persisten — database, state yang tidak perlu diedit manual dari host |
| Contoh di repo sendiri | ./dags:/opt/airflow/dags, ./spark_jobs:/opt/airflow/spark_jobs (Minggu 3) — edit file .py di host, langsung “terlihat” di container tanpa rebuild | Kalau pg-belajar dikonfigurasi dengan volume (bukan cuma docker run polos), data warehouse akan bertahan lintas restart container |
| Portabilitas | Terikat struktur folder host — kurang portable antar mesin | Lebih portable, dikelola sepenuhnya oleh Docker |
Kenapa dags/spark_jobs dipasang sebagai bind mount, bukan COPY permanen ke image (Minggu 3): supaya perubahan kode selama development langsung terlihat tanpa perlu docker build ulang tiap kali edit 1 baris — trade-off kecepatan iterasi development. Untuk image yang benar-benar akan di-deploy (bukan cuma development lokal), kode biasanya di-COPY permanen ke dalam image saat build (pola yang akan dipakai di latihan_containerization_mini_project.md untuk spark_jobs/Dockerfile dan streaming-demo/Dockerfile) — image jadi self-contained, tidak bergantung folder host yang mungkin tidak ada di server production.
Docker Networking: Bridge Network
Default, Docker membuat bridge network untuk container yang perlu saling terhubung dalam 1 host — ini yang sudah dibahas mekanismenya di hari_3_docker_compose.md (Compose otomatis membuat 1 bridge network per project, service saling terhubung lewat nama).
Ini yang menjelaskan kenapa pg-belajar (Minggu 1, docker run biasa) butuh host.docker.internal: dia tidak berada di bridge network manapun yang sama dengan container Airflow — keduanya “tidak saling kenal” secara default, beda dari service-service di dalam 1 file Compose yang sama yang otomatis satu network.
Kesalahan Umum
- Menjalankan database (Postgres, dst) tanpa volume sama sekali untuk data yang **seharusnya persisten.**
pg-belajarsejak Minggu 1 sengaja tanpa volume (untuk kesederhanaan setup awal roadmap) — konsekuensinya: kalau container itu pernah dihapus (bukan cuma di-stop), seluruh data warehouse yang sudah di-load hilang dan harus dijalankan ulang dariload_to_warehouse. Ini trade-off yang layak disadari, bukan cuma kebetulan tidak pernah terjadi. - Mengira bind mount dan named volume interchangeable begitu saja. Bind mount butuh path host yang konsisten ada di semua mesin yang menjalankan compose file itu — kalau file Compose dipindah ke mesin lain dengan struktur folder beda, bind mount bisa gagal/salah lokasi. Named volume tidak punya masalah ini karena dikelola sepenuhnya oleh Docker.
- Mengira container yang berbeda network otomatis bisa saling
pingpakai nama. Cuma container dalam network Docker yang sama yang bisa saling resolve nama sebagai hostname — duadocker compose upterpisah (dari 2 file/folder Compose berbeda) menghasilkan 2 network berbeda, meski sama-sama jalan di 1 mesin yang sama. - Menghapus volume tanpa sadar lewat
docker compose down -vataudocker system prune --volumes. Flag-v/--volumesmenghapus juga data yang tersimpan di named volume — beda daridocker compose downbiasa (cuma hapus container & network, volume tetap ada). Kebiasaan baik: selalu baca ulang flag sebelum menjalankan perintah pembersihan.
Latihan
- Kalau kamu jalankan
docker compose down(tanpa-v) di setup Airflow (Minggu 3), laludocker compose uplagi — apakah DAG history & task log yang lama masih ada? Jelaskan berdasarkan apakah Postgres metadata Airflow dikonfigurasi pakai volume atau tidak (cekdocker-compose.ymlkamu sendiri). - Rancang: kalau kamu ingin
pg-belajar(warehouse) tetap menyimpan data walau container-nya dihapus & dibuat ulang, volume seperti apa yang perlu ditambahkan ke perintahdocker run-nya? Tulis contoh perintahnya. - Jelaskan ke rekan kerja yang baru belajar Docker: kenapa
./spark_jobs:/opt/airflow/spark_jobs(bind mount) dipilih untuk development, tapi untuk image yang di-deploy ke server production nanti (latihan_containerization_mini_project.md), kodespark_jobs/sebaiknya di-COPYpermanen ke dalam image, bukan tetap mengandalkan bind mount. kafkadanairflow-webserverberada di filedocker-compose.ymlyang sama sejak Minggu 4. Tanpa menjalankan apapun, prediksi: apakahairflow-webserverbisa konek kekafkacukup pakai hostnamekafka(tanpahost.docker.internal)? Jelaskan alasannya berdasarkan konsep bridge network di atas.
Kunci Jawaban & Pembahasan
1. Jawabannya tergantung isi docker-compose.yml masing-masing peserta (biasanya file Compose resmi Airflow memang sudah menyertakan named volume untuk Postgres metadata secara default) — kalau ada baris seperti postgres-db-volume:/var/lib/postgresql/data di volumes: service postgres, maka DAG history/task log tetap ada setelah down lalu up lagi (data tersimpan di named volume, terpisah dari siklus hidup container). Kalau tidak ada volume sama sekali untuk service postgres, semua history akan hilang — poin ini bagus dicek langsung di file masing-masing, bukan diasumsikan.
2.
docker run -d --name pg-belajar \
-e POSTGRES_PASSWORD=belajar \
-p 5432:5432 \
-v pg-belajar-data:/var/lib/postgresql/data \
postgres:16
-v pg-belajar-data:/var/lib/postgresql/data membuat/memakai named volume bernama pg-belajar-data, dipasang ke direktori tempat Postgres menyimpan data filesystem-nya di dalam container (/var/lib/postgresql/data, lokasi standar image resmi Postgres). Selama volume pg-belajar-data tidak dihapus eksplisit, container pg-belajar bisa dihapus & dibuat ulang dengan perintah ini kapan saja tanpa kehilangan data — cukup pasang ulang volume yang sama.
3. Bind mount cocok untuk development karena mengoptimalkan kecepatan iterasi: edit clean_transform.py di editor, langsung tersedia di container tanpa docker build ulang (yang bisa makan waktu tiap kali) — trade-off yang masuk akal saat kode masih sering berubah. Untuk image yang di-deploy (dijalankan di server lain, bukan laptop sendiri), bind mount jadi masalah: server production tidak (dan seharusnya tidak) punya struktur folder host yang identik dengan laptop development, dan kamu ingin image itu self-contained — bisa dijalankan di mesin manapun tanpa bergantung file eksternal yang harus disiapkan manual dulu. COPY kode ke dalam image saat build memastikan image itu portable: docker run image-spark-jobs jalan identik di laptop manapun atau server manapun, tanpa syarat tambahan.
4. Ya, bisa — karena kafka dan airflow-webserver didefinisikan di docker-compose.yml yang sama, keduanya otomatis masuk ke bridge network Compose yang sama, dan Docker menyediakan DNS internal yang meresolusi nama service (kafka) ke IP container yang benar di dalam network itu. Ini beda dari kasus pg-belajar, yang sengaja tidak didefinisikan di file Compose yang sama (docker run terpisah, Minggu 1) — makanya butuh host.docker.internal sebagai jalan pintas lewat host, bukan lewat DNS internal bridge network.