Di penghujung Section Konfigurasi Container, Chapter Lifecycle Container menutup perjalanan dengan satu pengingat yang menjadi titik berangkat section ini: semua tulisan yang dilakukan di dalam container — berkas baru, suntingan konfigurasi, hasil kerja aplikasi — mengendap di writable layer, dan layer itu lenyap begitu container dihapus dengan docker rm. Selama ini kita menerima fakta tersebut sebagai keterangan penutup; sekarang kita berhenti dan membedahnya sampai ke mekanismenya. Ke mana persisnya tulisan itu mendarat, bagaimana Docker menulis di atas image yang read-only, mengapa menghapus berkas di dalam container tidak benar-benar menghapusnya dari image, dan sampai batas mana writable layer layak dipercaya sebagai rumah data — semuanya kita urai satu per satu di chapter pembuka Section Penyimpanan Data ini.
Semua Tulisan Mendarat di Satu Layer #
Kita mulai dari struktur yang sudah dikenal pada Chapter Konsep Image Layer: image adalah tumpukan layer read-only yang tersusun dari bawah ke atas, dan seluruh tumpukan tersebut bersifat tetap — tidak ada satu layer pun yang boleh diubah setelah image dibangun. Ketika docker run dijalankan, Docker tidak menyalin image; daemon cukup menumpuk satu writable layer tipis di puncak tumpukan read-only milik image tersebut. Container, dengan demikian, adalah gabungan tumpukan image read-only plus satu layer tertulis yang menjadi milik eksklusifnya.
Mekanisme ini menjawab pertanyaan yang mungkin sudah lama mengendap di benak kita. Berkas baru yang dibuat aplikasi di dalam container mendarat di layer teratas. Suntingan atas berkas milik image juga berakhir di layer teratas, lewat proses penyalinan yang kita bedah sebentar lagi. Penghapusan berkas milik image pun dicatat di layer teratas, bukan di imagenya. Tulisan apa pun arahnya, writable layer adalah satu-satunya tujuan — image di bawahnya tidak pernah tersentuh.
Copy-on-Write: Menyalin Sebelum Menulis #
Berkas yang berasal dari layer image tidak bisa ditulis langsung karena layer tersebut read-only. Docker mengatasinya dengan strategi copy-on-write: saat sebuah proses di container menulis berkas yang asalnya dari layer image, daemon menyalin berkas itu lebih dulu ke writable layer, kemudian menulis perubahan pada salinannya. Berkas asli di layer image tetap utuh, dan sejak salinan berada di puncak tumpukan, proses di container hanya melihat versi baru itu. Mari kita saksikan dengan contoh paling klasik: menyunting halaman utama nginx.
$ docker run -d --name web -p 8080:80 nginxOutput yang Dihasilkan:
f7c2a91b3e55913c4a5f1c6c0a4d8e2b3f4a5b6c7d8e9f0a1b2c3d4e5f60718aImage nginx:latest sudah tersimpan di mesin latihan dari section-section sebelumnya, jadi docker run langsung melahirkan container tanpa proses unduh. Sekarang kita timpa index.html milik image dari dalam container — perintah docker exec ini polanya sama dengan Chapter Menjalankan Perintah dalam Container:
$ docker exec web sh -c 'echo "<h1>Halo dari writable layer</h1>" > /usr/share/nginx/html/index.html'
$ curl localhost:8080Output yang Dihasilkan:
<h1>Halo dari writable layer</h1>Mari kita telaah apa yang barusan terjadi di balik layar:
docker exec web sh -c '...'— daemon menjalankan shellshdi dalam containerwebsebagai proses tambahan; shell itulah yang mengeksekusi pengalihan keluaran>menimpa/usr/share/nginx/html/index.html.index.htmlberasal dari layer image — berkas ini dikirim oleh image nginx dan tersimpan di salah satu layer read-only, sehingga tidak mungkin ditulis di tempatnya.- Copy-up terjadi lebih dulu — daemon menyalin
index.htmldari layer image ke writable layer, baru kemudian menimpanya dengan keluaranecho; proses penyalinan ini disebut copy-up, jantung dari strategi copy-on-write. curlmembaca hasil akhirnya — permintaan HTTP diarahkan ke port 8080 host yang diteruskan ke container, dan nginx membacaindex.htmlversi writable layer karena versi itu kini berada paling atas dalam tumpukan.- Layer image tidak berubah sama sekali — container lain yang berjalan dari image yang sama tidak melihat suntingan ini, dan
nginx:latestdi penyimpanan lokal tetap memuat berkas aslinya.
Alur lengkap pengambilan keputusan saat sebuah proses menulis berkas dapat dirangkum dalam diagram berikut.
Diagram di atas mempertegas satu fakta: tidak ada jalur tulis yang menuju layer image. Mari kita baca tiga cabangnya:
- Cabang kiri — berkas yang sudah ada di layer image harus melewati copy-up sebelum ditulis; inilah jalur yang dilalui
index.htmlpada demo kita. - Cabang kanan — berkas yang sama sekali baru, misalnya log aplikasi, langsung ditulis ke writable layer tanpa proses salin.
- Konsekuensi ukuran — copy-up menyalin seluruh berkas sekaligus, bukan hanya bagian yang diubah; menimpa satu baris dari berkas 500 MB tetap menyalin 500 MB penuh ke writable layer.
Whiteout: Menghapus Tanpa Membuang Berkasnya #
Sekarang giliran operasi yang lebih mengejutkan: menghapus berkas milik image dari dalam container. Kita hapus 50x.html, berkas halaman galat bawaan nginx yang tinggal satu direktori dengan index.html:
$ docker exec web rm /usr/share/nginx/html/50x.html
$ docker exec web ls /usr/share/nginx/htmlOutput yang Dihasilkan:
index.htmlDari sudut pandang proses di dalam web, 50x.html sudah tidak ada. Namun berkas itu tidak benar-benar terbuang — layer image yang memuatnya tetap utuh dan read-only, jadi yang dilakukan Docker hanyalah menulis penanda khusus bernama whiteout di writable layer: berkas kecil yang menyembunyikan 50x.html saat seluruh tumpukan digabungkan. Persis mekanisme yang sudah kita kenal pada proses build di Chapter Konsep Image Layer, kali ini terjadi di ranah runtime. Bukti paling jujurnya datang dari container baru yang dijalankan dari image yang sama:
$ docker run --rm nginx ls /usr/share/nginx/htmlOutput yang Dihasilkan:
50x.html
index.htmlMari kita telaah makna kedua output di atas:
- Container baru masih melihat
50x.html— penghapusan padawebhanya tercatat di writable layer milikweb; imagenginx:latesttidak pernah kehilangan berkasnya. - Berkas “terhapus” tetap memakan tempat — whiteout menyembunyikan berkas, bukan membuang isinya; menghapus berkas besar di dalam container tidak mengembalikan satu byte pun ruang disk.
--rmmembersihkan container sekali pakai — flag ini membuang container beserta writable layer-nya begitu prosesnya selesai, sehingga container uji di atas tidak meninggalkan jejak.
Membedah Perubahan dengan docker diff #
Semua perubahan tadi — salinan yang ditimpa, whiteout, berkas baru — sebenarnya bisa kita baca dalam satu tabel ringkas. Perintah docker diff membandingkan filesystem container terhadap image asalnya dan mencetak daftar perubahan pada writable layer. Kita tambahkan dulu satu perubahan jenis ketiga, yakni berkas baru di path yang sama sekali belum ada:
$ docker exec web sh -c 'mkdir /data && echo tes > /data/tes.txt'
$ docker diff webOutput yang Dihasilkan:
A /data
A /data/tes.txt
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html
D /usr/share/nginx/html/50x.htmlSetiap baris diawali satu huruf yang menyatakan jenis perubahannya. Mari kita bedah huruf demi huruf:
A(Added) — path yang ditambahkan:/datadan/data/tes.txtsama sekali tidak ada di image, keduanya lahir murni di writable layer.C(Changed) — path yang isinya atau metadatanya berubah:/usr/share/nginx/html/index.htmladalah hasil copy-up tadi, sementara dua induknya ikut bertandaCkarena isi direktori mereka memang berubah — satu berkas di dalamnya telah diganti versinya.D(Deleted) — path yang disembunyikan whiteout:/usr/share/nginx/html/50x.htmltampil dengan hurufDmeskipun berkas fisiknya masih utuh di layer image.- Daftar ini persis isi writable layer —
docker diffadalah cara termurah mengaudit jejak sebuah container sebelum dipercaya, di-commit, atau dibuang.
Menyalin Berkas Dua Arah dengan docker cp #
Writable layer memang fana, tetapi isinya tetap bisa dipindahkan ke luar sebelum container dibuang. Perintah docker cp menyalin berkas atau direktori antara host dan container dengan tata cara mirip scp: sumber dulu, tujuan kemudian, dan salah satu sisi ditulis dengan awalan nama-container:. Arah pertama adalah dari host ke container:
$ echo "banner latihan" > banner.txt
$ docker cp banner.txt web:/usr/share/nginx/html/banner.txt
$ curl localhost:8080/banner.txtOutput yang Dihasilkan:
banner latihanArah kedua membalik posisinya — berkas milik image disalin keluar ke host:
$ docker cp web:/etc/nginx/nginx.conf ./nginx.conf.host
$ ls -l nginx.conf.hostOutput yang Dihasilkan:
-rw-r--r-- 1 budi budi 1097 Okt 13 10:02 nginx.conf.hostMari kita telaah kedua arah tersebut:
- Host ke container —
banner.txtmendarat di writable layer container; jalankandocker diff websekali lagi dan berkas itu kini muncul sebagai barisAbaru, bukti bahwadocker cpmenulis melalui jalur yang sama dengan proses di dalam container. - Container ke host —
docker cpmembaca filesystem gabungan container, sehingganginx.confyang kita salin adalah berkas milik image, bukan writable layer; berkas dari container yang sudah berhenti pun tetap bisa disalin dengan cara yang sama. - Kepemilikan berkas hasil salin — berkas yang tiba di host menjadi milik user yang menjalankan
docker cp, sementara permission aslinya dipertahankan; itulah mengapanginx.conf.hosttercatat milikbudidengan moderw-r--r--. - Keterbatasan tetap sama —
docker cpadalah operasi salin sekali jalan, bukan sinkronisasi; setiap salinan berdiri sendiri dan tidak mengikuti perubahan berikutnya.
Batasan Writable Layer sebagai Rumah Data #
Sekarang kita rangkum mengapa writable layer buruk sebagai tempat menyimpan data, lewat demonstrasi penutup yang paling jujur. Container web kita bawa ke penghujung umurnya:
$ docker rm -f web
$ docker run -d --name web -p 8080:80 nginx
$ curl localhost:8080Output yang Dihasilkan:
f7c2a91b3e55913c4a5f1c6c0a4d8e2b3f4a5b6c7d8e9f0a1b2c3d4e5f60718a
<!doctype html>
<html>
<head>
<title>Welcome to nginx!</title>
...Tulisan-tulisan kita lenyap tanpa jejak: index.html kembali ke versi bawaan image, sementara banner.txt, /data/tes.txt, dan seluruh whiteout ikut terbuang bersama writable layer lama. Inilah batas pertama dan paling mendasar — writable layer berumur persis selama container, sebagaimana ditegaskan pada peta state di Chapter Lifecycle Container. Dua batas lain menyertainya. Pertama, writable layer tidak portabel: ia melekat pada container tertentu, tidak bisa dipasang ke container lain, dan satu-satunya jalan memindahkannya adalah membekukannya menjadi image baru — praktik yang cepat menggemukkan image dan melanggar prinsip image yang terkontrol dari Dockerfile. Kedua, kinerjanya lemah untuk beban tulis berat: setiap suntingan pertama atas berkas image memicu copy-up penuh, dan seluruh lalu lintas tulis harus melewati backend graph driver yang mengelola tumpukan layer. Menyimpan data penting di writable layer dengan demikian adalah jalan pintas yang hampir selalu berakhir kehilangan data — satu docker rm yang disadari terlambat sudah cukup menghapus semuanya.
Tabel berikut merangkum posisi writable layer dibandingkan tempat penyimpanan yang akan kita kenal sepanjang section ini:
| Aspek | Writable layer | Volume |
|---|---|---|
| Umur data | Selama container hidup — lenyap bersama docker rm | Terpisah dari container — selamat dari docker rm |
| Kinerja tulis | Terbebani copy-on-write di atas backend graph driver | Langsung ke filesystem volume tanpa copy-up |
| Portabilitas | Melekat pada satu container, sulit dipindahkan | Dipasang ulang ke container mana pun lewat nama |
| Kecocokan | Eksperimen dan perubahan sementara | Data aplikasi yang layak hidup lebih lama |
Fakta-fakta di atas membawa kita ke pertanyaan inti section ini: bagaimana caranya menyimpan data agar hidup lebih lama daripada container yang menulisnya? Docker menjawab dengan satu keluarga mekanisme bernama mount, dan peta besarnya — mengapa persistensi dibutuhkan dan bentuk-bentuk mount apa saja yang tersedia — kita susun pada Chapter Konsep Persistence.
Referensi #
- Container Storage — penjelasan resmi writable layer, copy-on-write, dan whiteout di balik storage driver.
- docker container diff — cara membaca perubahan filesystem container bertanda A/C/D.
- docker container cp — penyalinan berkas dua arah antara host dan container.