Membatasi Penggunaan Disk (Quota) di Linux dengan Sparse File

Pada saat awal membuat "VPS hosting" sendiri berbasis Docker Container pertama di Indonesia, https://cloud.ngadem.in/, Saya membagikan sebuah partisi fisik sebagai mountpoint "/var/lib/docker". Meskipun partisi tersebut penuh, tidak akan mengganggu host OS.

Tetapi akhir-akhir ini beberapa pengguna mencoba melakukan abuse dengan menggunakan space hingga penuh, sehingga mengganggu kenyamanan pengguna lain. Mengingat space yang dibagikan tidak ada hardlimit untuk masing-masing pengguna.

Saya pernah mencoba menggunakan storage-driver "devicemapper", tetapi itu tidak bisa dibatasi lebih kecil dari 10GB. Sedangkan pembatasan yang diinginkan sekitar 0.5 - 2GB.

Pilihan terakhir saya yang bisa saya pikirkan adalah penggunaan block device (/dev/loopX).

Saya bertanya di Github Docker dan melakukan beberapa percobaan, dan rupanya Docker dapat me-mount block device secara langsung. Artinya tidak harus me-mount /dev/loopX di suatu folder di host, sebelum share folder tsb ke dalam container.

Ini seharusnya memang memungkinkan, karena dikatakan --mount di Docker memiliki dukungan sama dengan host. Sedangkan saya yakin dengan host yang saya gunakan.

Buat file user123.img 500MB

  1. # dd if=/dev/zero of=/user123.img bs=1M count=500 

Buat device node /dev/loop123

  1. # mknod -m 660 /dev/loop123 b 7 123 

b adalah "block", "7" adalah nomor major untuk loop device, "123" adalah nomor minor yang kita tetapkan

Info: https://www.kernel.org/doc/Documentation/admin-guide/devices.txt

Tautkan user123.img di /dev/loop123

  1. # losetup /dev/loop123 /user123.img 

Format

  1. # mkfs.ext4 /dev/loop123 

Lalu buat container dengan opsi seperti berikut:

  1. --mount type=volume,volume-driver=local,dst=/home,volume-opt=type=ext4,volume-opt=device=/dev/loop123 

Masalah selesai?

Tidak.

Docker tidak membolehkan "dst" atau "target" untuk mountpoint di dalam container pada puncak "/". Hanya bisa pada semisal "/home".

Saya mempunyai requirement untuk membatasi pada "/", meskipun menggunakan storage-driver overlay2.

Setelah mengobservasi behavior container ketika dibuat dan dijalankan, akhirnya menemukan solusi walau harus berurusan dengan /var/lib/docker/overlay2 secara langsung. Saya tahu ini tidak direkomendasikan.

Kembali ke judul. Lalu apa itu sparse file?

Pada contoh di atas user123.img bukan sparse file. Jika ada 100 pengguna @500M, maka akan memakan kapasitas 50GB.

Ini bisa diakali dengan sparse file. Sparse file hanya akan memakan space yang benar-benar terpakai, meskipun sama-sama memiliki batas, misal 500M.

Pada file img biasa, jika 100M dari 500M terpakai, sisanya berupa "zero" dan tetap memakan space pada disk. Sedangkan pada sparse file, zero dicatat sebagai metadata.

Keuntungan pada kasus ini penggunaan disk lebih efisien, dan meminimalisir abuse.

Teknik sparse file ini juga digunakan oleh Docker pada storage-driver "devicemapper" dengan mode "loop-lvm". Yang juga menjadi inspirasi saya untuk menerapkan hal semacam itu tetapi pada storage-driver "overlay2".

Karena sifat unik sparse file yang hanya mencatat zero tetapi tidak mengalokasikannya, maka tidak aneh jika kita juga bisa membuat file img lebih besar dari kapasitas disk sebenarnya!

Cara membuat sparse file dengan dd

  1. dd if=/dev/zero of=file.img bs=1M count=0 seek=500 

Cara membuat sparse file dengan truncate

  1. truncate -s 500M file.img 

Kekurangan sparse file

Cenderung terfragmentasi.

Catatan:

Untuk mendefrag bisa memanfaatkan "cp"

  1. cp --sparse=always file.img file_baru.img 

akan secara alami mendefrag karena tersalin secara contiguous menjadi file_baru.img.

Jika melakukan cp tanpa --sparse=always, file_baru.img akan hilang sparseness-nya dan memiliki zero yang memenuhi disk, menjadi file img biasa.