Chapter Memilih Jenis Mount ditutup dengan satu pola yang belum kita kunci: hampir semua mount yang kita pasang masih terbuka untuk ditulis oleh container. Padahal di dunia nyata, container tidak selalu layak dipercaya memegang pena — aplikasi yang bug bisa menimpa konfigurasi yang seharusnya tak tersentuh, proses yang diretas bisa menulis backdoor ke direktori yang dipasangkan, dan container utilitas yang iseng bisa merusak data referensi milik container lain. Docker menyediakan dua tingkat penguncian: suffix :ro yang membekukan satu mount tertentu, dan flag --read-only yang membekukan seluruh akar filesystem container. Chapter ini membedah keduanya, membuktikan bahwa read-only bekerja satu arah — container ditolak, dunia luar tetap bisa menulis — lalu merangkai kombinasi paling ketat: --read-only berdampingan dengan tmpfs sebagai satu-satunya lubang tempat aplikasi boleh mencoret.

Suffix :ro dan Bentuk Panjangnya #

Volume data-web milik kita — berisi halo.txt plus berkas bawaan nginx — masih menjadi bahan latihan utama. Kali ini kita pasang volumenya ke container nginx baru dengan satu suffix tambahan: :ro di ujung spesifikasi mount:

bash
$ docker run -d --name web-ro \
    -p 8081:80 \
    -v data-web:/usr/share/nginx/html:ro \
    nginx

Output yang Dihasilkan:

text
24685c86011fdae91da1b411adaf2aa56d7fd858a868ed6b50273f7c9cf29d0f

Mari kita telaah tiga cara menuliskan penguncian yang sama:

  1. -v data-web:/usr/share/nginx/html:ro — field ketiga pada pola -v adalah mode; kosong berarti read-write bawaan, ro berarti read-only untuk container ini.
  2. --mount type=volume,source=data-web,destination=/usr/share/nginx/html,readonly — bentuk panjang memakai kunci readonly pada daftar opsi --mount; pada tingkat API engine, opsi ini terbaca sebagai readonly=1.
  3. tmpfs tidak bisa dikunci lewat suffix — hanya volume dan bind mount yang mengenal :ro; tmpfs dikunci lewat --mount ...readonly saja bila diperlukan.

Kondisi mount tercatat rapi di inspect. Perhatikan dua field yang berubah nilainya:

bash
$ docker inspect web-ro --format '{{json .Mounts}}'

Output yang Dihasilkan:

json
[
  {
    "Type": "volume",
    "Name": "data-web",
    "Source": "/var/lib/docker/volumes/data-web/_data",
    "Destination": "/usr/share/nginx/html",
    "Mode": "ro",
    "RW": false,
    "Propagation": ""
  }
]

Dua field itulah yang dibaca daemon saat menolak tulisan: "Mode": "ro" mencatat mode yang kita minta, dan "RW": false menyatakan mount ini tidak boleh ditulis oleh container pemiliknya.

Tulisan dari Dalam Container yang Ditolak #

Membuktikan penguncian semudah mencoba menulis dari dalam container. Kita pinjam tangan touch lewat docker exec:

bash
$ docker exec web-ro touch /usr/share/nginx/html/catatan.txt

Output yang Dihasilkan:

text
touch: cannot touch '/usr/share/nginx/html/catatan.txt': Read-only file system

Pesan itu datang dari kernel, bukan dari Docker. Mari kita telaah apa yang sebenarnya terjadi:

  1. docker exec web-ro touch ... — proses baru dilahirkan di dalam container web-ro lalu mencoba membuat berkas di path yang termount :ro.
  2. Read-only file system — kernel menolak operasi tulis karena mount tersebut dipasang dengan flag read-only di level filesystem; pesan ini identik dengan yang muncul saat kita mencoba menulis ke CD-ROM atau partisi yang di-mount ro di Linux biasa.
  3. Tidak ada proses yang dihukum — container tetap hidup, nginx tetap melayani; yang ditolak hanya operasi tulisnya, bukan container-nya.

Efek Satu Arah: Dunia Luar Masih Bisa Menulis #

Kesalahpahaman paling umum tentang :ro adalah menganggapnya membekukan data itu sendiri. Kenyataannya, :ro hanyalah kacamata yang dipakai satu container — volume atau direktori sumbernya tetap read-write bagi pihak lain. Kita buktikan dengan helper container yang memasang volume yang sama tanpa suffix:

bash
$ docker run --rm -v data-web:/usr/share/nginx/html alpine:3.20 \
    sh -c "echo 'Catatan dari helper rw.' > /usr/share/nginx/html/nota.txt"

Perintah di atas selesai tanpa keluhan apa pun. Sekarang kita baca berkas itu dari container yang terkunci:

bash
$ curl localhost:8081/nota.txt

Output yang Dihasilkan:

text
Catatan dari helper rw.

Mari kita telaah bukti berjalan ini:

  1. Helper memasang data-web tanpa :ro — container helper melihat volume sebagai read-write, sehingga echo miliknya diterima kernel tanpa keberatan.
  2. web-ro membaca perubahan seketika — keduanya memasang volume yang sama; tulisan helper muncul sebagai isi situs pada container yang terkunci, karena :ro hanya mengatur arah untuk si pemilik suffix, bukan menyegel volume secara global.
  3. Pola yang sama berlaku pada bind mount dari sisi host — pada skenario situs dev Chapter Memilih Jenis Mount, editor di host bebas mengubah berkas sementara container memasangnya :ro; penolakan hanya berlaku terhadap proses yang berada di dalam container yang memasang mode itu.

Satu volume, dua pasang kacamata — inilah pola yang memungkinkan arsitektur produsen-konsumen: satu container bertugas menulis (job impor data, helper pembaruan), container lain cukup membaca (web server yang menyajikan). Tiga use case paling umum untuk :ro:

Use caseContohMengapa :ro
Konfigurasi produksinginx.conf, app.ini yang dipasang ke containerPerubahan hanya lewat alur deploy di host, container tidak bisa mengubah konfigurasinya sendiri
Source referensiKode yang dipasang ke container pemindai atau pengujiContainer hanya perlu membaca; tak ada alasan memberinya pena
Data referensi statisKamus, daftar harga, geo-IP databaseData dibaca banyak container; menutup jalur tulis mencegah salinan rusak diam-diam

Kombinasi Keras: –read-only plus tmpfs #

Suffix :ro membekukan satu mount. Flag --read-only jauh lebih radikal: seluruh rootfs container — image beserta writable layer-nya — dipasang read-only, sehingga tidak ada satu path pun di akar filesystem yang boleh ditulis. Aplikasi nyata biasanya masih butuh menggores di dua tempat: berkas sementara di /tmp dan kadang cache di jalur tertentu; untuk itulah tmpfs dipasang sebagai lubang tulis yang diberikan secara sengaja. Kita rangkai keduanya dalam satu simulasi container api yang paling ketat:

bash
$ mkdir -p config && printf 'mode=produksi\n' > config/app.ini
$ docker run --rm --name api-ketat \
    --read-only \
    --mount type=bind,source="$(pwd)/config",target=/etc/myapp,readonly \
    --mount type=tmpfs,destination=/tmp,tmpfs-size=16m \
    alpine:3.20 \
    sh -c 'cat /etc/myapp/app.ini; touch /berkas-baru; touch /tmp/berkas-oke && ls -l /tmp/berkas-oke'

Output yang Dihasilkan:

text
mode=produksi
touch: can't create '/berkas-baru': Read-only file system
-rw-r--r--    1 root     root             0 Oct 13 15:52 /tmp/berkas-oke

Tiga baris output itu menceritakan seluruh arsitektur penguncian. Mari kita telaahnya satu per satu:

  1. mode=produksi — bind mount config terbaca sempurna di /etc/myapp; konfigurasi datang dari host dan dipasang readonly dua lapis: flag --read-only yang membekukan akar, plus opsi readonly pada bind mount itu sendiri.
  2. touch: can't create '/berkas-baru': ... — menulis ke akar filesystem ditolak mentah-mentah; dengan --read-only, writable layer yang biasanya tempat container mencoret ikut terkunci, jadi /berkas-baru gagal dibuat meski container berjalan sebagai root.
  3. /tmp/berkas-oke tercipta — tmpfs dengan batas tmpfs-size=16m menjadi satu-satunya wilayah yang boleh ditulis; penulisan itu hidup di RAM dan lenyap saat container berhenti, persis perilaku tmpfs yang kita kenal dari Chapter Tmpfs Mount.
  4. Batas memori sebagai pengaman ekstra — tmpfs-size=16m memastikan aplikasi yang liar menulis berkas sementara tidak melahap RAM host; habisnya kuota tmpfs akan menolak tulisan berikutnya alih-alih memperlambat seluruh mesin.

:ro versus –read-only #

Dua tingkat penguncian itu sering tertukar karena namanya mirip. Tabel berikut memisahkan keduanya:

Aspek:ro pada satu mount--read-only pada container
CakupanHanya path yang di-mount dengan suffix ituSeluruh rootfs container, termasuk writable layer
Tulisan yang masih lolosWritable layer dan mount lain yang tidak dikunciHanya mount rw atau tmpfs yang dipasang setelahnya
Yang perlu disiapkanTidak ada — aplikasi tidak menulis ke path ituAplikasi harus tahu satu-satunya jalur tulisnya, biasanya /tmp
Beban operatorRingan — tambahkan suffix saat docker runSedang — setiap kebutuhan tulis harus dipetakan ke tmpfs
Cocok untukMelindungi config, source, dan data referensiHardening ketat dan audit jejak tulis

Sebelum menutup, kita rapikan panggung latihan: container web-ro sudah menuntaskan tugasnya, sementara nota.txt yang ditulis helper sengaja kita biarkan di dalam data-web — berkas itu akan menjadi saksi bahwa volume terus mengingat seluruh riwayat isinya:

bash
$ docker rm -f web-ro

Output yang Dihasilkan:

text
web-ro

Data kini terlindungi dari tangan-tangan yang tidak seharusnya menulis — tetapi penguncian tidak menolong apa pun terhadap kegagalan disk, kesalahan docker volume rm yang keliru sasaran, atau serangan ransomware yang menyapu seluruh host. Perlindungan terhadap bencana semacam itu hanya ada satu kata: backup. Chapter Backup dan Restore Volume memperkenalkan pola resmi helper container yang membungkus isi volume menjadi arsip tar.gz dan menyuapkannya kembali ke volume baru — keterampilan yang sekaligus menjadi fondasi seluruh strategi migrasi data.

Referensi #

  • docker container run — referensi flag --read-only, --mount, dan mode mount.
  • Bind Mounts — contoh resmi bind mount read-only untuk konfigurasi.
  • Volumes — perilaku volume yang dipasang ro di satu container dan rw di container lain.