Container တွေအကြောင်းကို ပြီးခဲ့တဲ့ article မှာ မိတ်ဆက်အနေနဲ့ ပြောပြခဲ့ပြီးဖြစ်ပါတယ်။ ဒီတစ်ခေါက်မှာတော့ Container တွေရဲ့ Resource Isolation ကို လုပ်ပေးတဲ့ အရာတွေကို လက်တွေ့လေ့လာသွားမှာဖြစ်ပါတယ်။
Containers
Container တွေကို Linux Kernel မှာ ပါဝင်တဲ့
- chroot
- namespaces
- cgroups ဆိုတဲ့ feature ၃ ခုပေါ်အခြေခံပြီး တည်ဆောက်ထားတာဖြစ်ပါတယ်။
Chroot (Change Root)
ပထမဆုံးအနေနဲ့ File System အားဖြင့်ကန့်သတ်ဖို့လိုပါတယ်။ ဒါမှသာ Container တွေဟာ တစ်ခုနဲ့တစ်ခုရဲ့ File တွေကို မတွေ့ရဘူး ၊ access လုပ်ခွင့်မရှိဘူး ဖြစ်မှာပါ။ Linux မှာပါတဲ့ Chroot ဆိုတဲ့ feature ကို ဒီလိုမျိုးအတွက်သုံးလို့ရပါတယ်။
Chroot က ဘာကိုလုပ်ပေးတာလဲဆိုတော့ Main Operating System ကနေခွဲထွက်ပြီး virtual environment တစ်ခုအနေနဲ့ ဖြစ်သွားအောင်လုပ်ပေးတာပါ။ သူ့မှာ ကိုယ်ပိုင် root directory ရှိပြီးတော့ ဒီ environment ထဲမှာ run တဲ့ program မှန်သမျှဟာ သူ့ရဲ့ root directory tree အတွင်းမှာပဲရှိတဲ့ file တွေကို access လုပ်ခွင့်ရှိတာဖြစ်ပါတယ်။ လက်တွေ့တချက် ကြည့်ကြရအောင်
လက်ရှိမှာ ကျနော်တို့ဟာ Linux OS တစ်ခုပေါ်မှာရှိနေပါမယ်။ အရင်ဆုံး ကိုယ်ရောက်နေတဲ့နေရာရဲ့ directory structure ကို တချက် ls
ကြည့်လိုက်ပါ။ ပြီးရင်တော့ ကျနော်တို့ chroot ဝင်ဖို့အတွက် root folder အသစ်တစ်ခု လုပ်လိုက်ပါမယ်။
mkdir /new-root/bin
ပြီးရင်တော့ root အသစ်မှာ command တွေရိုက်ဖို့အတွက် bash shell နဲ့ command တစ်ခုဖြစ်တဲ့ ls တို့ကို လက်ရှိ root ကနေ copy ကူးပါမယ်။
cp /bin/bash /bin/ls /new-root/bin
bash နဲ့ ls တွေမှာ link ချိတ်ထားတဲ့ library တွေရှိတာဆိုတော့ အဲဒါတွေကိုလည်း copy ကူးဖို့လိုပါတယ်။ ldd
command ကိုသုံးပြီး ဘာတွေ copy ကူးဖို့လိုလဲဆိုတာကြည့်လိုက်ပါမယ်။
ldd /bin/bash
ဒီလိုမျိုး output ထွက်လာပါလိမ့်မယ်
linux-vdso.so.1 (0x00007fffa89d8000)
libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007f6fb8a07000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f6fb8803000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6fb8412000)
/lib64/ld-linux-x86-64.so.2 (0x00007f6fb8f4b000)
ကြည့်ကြည့်မယ်ဆိုရင် ကူးဖိုလိုတာတွေကိုတွေ့ရမှာဖြစ်ပြီးတော့ သူတို့ရှိရမယ့် path ကိုလည်းမြင်ရမှာပါ။
mkdir /new-root/lib{,64}
အပေါ်က command ကတော့ new root ထဲမှာ lib နဲ့ lib64 ဆိုတဲ့ folder နှစ်ခုကို ဆောက်လိုက်တာပဲဖြစ်ပါတယ်။ ပြီးရင်တော့ copy ကူးပါမယ်
cp /lib/x86_64-linux-gnu/libtinfo.so.5 /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libc.so.6 /new-root/lib
cp /lib64/ld-linux-x86-64.so.2 /new-root/lib64
ls အတွက်လည်း အခုအတိုင်း လုပ်ဆောင်ရမှာဖြစ်ပါတယ်။
ပြီးသွားရင်တော့ ကျနော်တို့ရဲ့ root အသစ်ထဲဝင်လို့ရပါပြီ။ အောက်က command က ဆိုလိုတာကတော့ new-root ထဲကို chroot ဝင်ပြီးတော့ bash ဆိုတဲ့ program ကို run မယ်လို့ဆိုလိုတာဖြစ်ပါတယ်။
chroot /new-root bash
ဒါဆိုရင်တော့ chroot လုပ်ထားတဲ့ confined space တစ်ခုထဲရောက်သွားမှာဖြစ်ပြီးတော့ အခုနေ ls ထုတ်ကြည့်ရင် ကျနော်တို့အစတုန်းက ထုတ်ကြည့်ခဲ့တာနဲ့တူမှာမဟုတ်တော့ပါဘူး။ ဒီ chroot ထားတဲ့ environment ကနေထွက်ပြီးတော့ အပြင်မှာရှိတဲ့ directory တွေ file တွေကို access လုပ်လို့ရတော့မှာမဟုတ်ပါဘူး။ ဒါဆိုရင် file system အားဖြင့်တော့ separated ဖြစ်သွားပြီဖြစ်ပါတယ်။
(Chroot’d environment ကနေ ထွက်ဖို့ဆိုရင် exit command ကိုသုံးနိုင်ပါတယ်။)
Namespaces
Isolate ဖြစ်ဖို့ဆိုတာ File System တစ်ခုပဲ separate ဖြစ်နေရုံနဲ့မရပါဘူး။ Chroot ကြောင့် File တွေကို access လုပ်လို့မရတော့တာမှန်ပေမယ့် process အားတော့ access လုပ်လို့ရနေသေးတာဖြစ်တဲ့အတွက် မလုံလောက်သေးပါဘူး။ ဒီလို process တွေကို isolate လုပ်ဖို့အတွက်ကတော့ namespaces ကိုသုံးပါတယ်။ Namespaces တွေသာမရှိဘူးဆိုရင် ဥပမာ - container A မှာ run နေတဲ့ process တစ်ခုက container B မှာရှိတဲ့ network interface တွေကို remove တာမျိုး ၊ hostname တွေ ချိန်း file system တွေ unmount လုပ်တာမျိုးတွေ လုပ်လို့ရနေမှာဖြစ်ပါတယ်။ အရင် shared hosting ဥပမာနဲ့ပဲပြန်ပြောရမယ်ဆိုရင် Company A နဲ့ B နဲ့ က သီးသန့်စီ ကိုယ်ပိုင် chroot environment တွေမှာရှိနေမယ်ဆိုပါတော့။ ဒါပေမယ့်လည်း Company A နဲ့ B က တစ်ယောက်နဲ့တစ်ယောက်ရဲ့ process တွေကို တွေ့နေရပါသေးတာဖြစ်တဲ့အတွက် အကယ်၍ Company A ကသာ “ya know what? Kill B’s web server” ဆိုပြီး လုပ်ချလိုက်လို့ရနေတာဖြစ်ပါတယ်။ demostrate ပြရရင်တော့ -
အရင်ဆုံး terminal window နှစ်ခုဖွင့်ပြီးတော့ ပထမတစ်ခုမှာတော့ ခုနက ဆောက်ခဲ့တဲ့ new root ထဲကို chroot နဲ့ဝင်ပါမယ်။
chroot /new-root bash
ဒုတိယ window မှာတော့ file တစ်ခု create လုပ်ပြီးတော့ tail နဲ့ ဖတ်တဲ့ process တစ်ခု create ထားပါမယ်။
touch gg.txt
tail -f /new-root/gg.txt
ပြီးရင်တော့ ps ဆိုတဲ့ command နဲ့ လက်ရှိ run နေတဲ့ process တွေကိုကြည့်ပါမယ်။
ps aux
USER PID ... TIME COMMAND
root 1 00:00 /bin/bash
root 102 ... 00:00 tail -f
...
tail process ရဲ့ process id ဖြစ်တဲ့ PID ကို copy ကူးလိုက်ပါမယ်။
ပြီးရင်တော့ chroot ဝင်ထားတဲ့ ပထမ terminal ကနေပြီးတော့
kill 102
ဆိုပြီး kill လိုက်လို့ရှိရင် terminal two မှာရှိတဲ့ process ဟာရပ်သွားမှာဖြစ်ပါတယ်။ ဒါ့ကြောင့် process တွေကိုပါ isolate လုပ်ဖို့က လိုလာပါတယ်။
Namespace တွေဟာ process တစ်ခုကမြင်နိုင်တာကို limit လုပ်ပေးတာဖြစ်တဲ့ အတွက် ဥပမာ - Container A မှာ ရှိတဲ့ process က တခြား Container တွေမှာရှိတဲ့ process တွေကို ရှိမှန်းတောင် သိနိုင်မှာမဟုတ်တော့ပါဘူး။ လက်တွေ့ကြည့်ကြရအောင်
အရင်ဆုံး ခုနတုန်းလိုပဲ chroot ဝင်လို့ရနိုင်တဲ့ enviornment တစ်ခုဆောက်ဖို့လိုပါမယ်။ ဒါပေမယ့် ဒီတစ်ခေါက်မှာတော့ debootstrap လို့ခေါ်တဲ့ tool တစ်ခုကိုသုံးပြီးဆောက်ပါမယ်။ deboostrap ကတော့ debian boostrap လို့ခေါ်ပြီး basic debian OS တစ်ခုရအောင်လုပ်ပေးတာဖြစ်ပါတယ်။ အရင်ဆုံး update လုပ်ပြီးတော့ debootstrap ကို install လုပ်ပါမယ်။
apt-get update -y
apt-get install debootstrap -y
ပြီးရင်တော့ debootstrap ကိုသုံးပြီးတော့ Ubuntu Bionic flavour ရှိတဲ့ environment တစ်ခုကိုဆောက်ပါမယ်။ better-root လို့ နာမည်ပေးထားပါမယ်။ 150 MB လောက်တော့ internet ကနေ ဆွဲမှာဖြစ်ပါတယ်။
debootstrap --variant=minbase bionic /better-root
I: Retrieving InRelease
I: Checking Release signature
...
...
...
...
I: Configuring apt...
I: Configuring libc-bin...
I: Base system installed successfully.
ပြီးရင်တော့ ကျနော်တို့ unshare ဆိုတဲ့ command တစ်ခုကိုသုံးပြီးတော့ namespace လုပ်ပါမယ်။
unshare --mount --uts --ipc --net --pid --fork --user --map-root-user chroot /better-root bash
ဒီ command ကဆိုလိုတာကတော့ mount points တွေ hostname တွေ network တွေ user တွေစတာတွေအကုန်လုံးကို share မလုပ်တော့ပဲနဲ့ better-root ဆိုတဲ့ folder ကို chroot ဝင်ပြီး bash ကို run မယ်လို့ဆိုလိုတာဖြစ်ပါတယ်။
ပြီးရင်တော့ ဒီ chroot အတွက် လိုအပ်တာလေးတွေကို တချက်ပြန် mount ပါမယ်။
mount -t proc none /proc # process namespace
mount -t sysfs none /sys # filesystem
mount -t tmpfs none /tmp # filesystem
ပြီးသွားရင်တော့ ခုနကလို process တွေကို terminal နှစ်ခုဖွင့်ပြီး ပြန်စမ်းကြည့်ကြည့်လိုရှိရင် ရတော့မှာမဟုတ်ပါဘူး။
ဒါဆိုရင်တော့ ကျနော်တို့ရဲ့ container က အခုဆိုရင် file system အရရော ၊ process တွေအရရော တစ်ခုနဲ့တစ်ခု မမြင်နိုင်တော့တဲ့အတွက် တော်တော်လေး isolate လုပ်နိုင်တဲ့အထဲပါသွားပါပြီ။ ဒါပေမယ့် Namespace တွေက physical resource တွေဖြစ်တဲ့ CPU, memory, disk စတာတွေကို restrict မလုပ်နိုင်ပါဘူး။ ဒါလို physical resource တွေကို restrict လုပ်ဖို့အတွက်ကိုတော့ နောက်ထပ် kernel feature တစ်ခုဖြစ်တဲ့ C-groups ကိုသုံးပါတယ်။
Cgroups
File System အရရော Process အရရော isolate ဖြစ်သွားပြီဖြစ်သော်ငြားလည်း ကျနော်တို့ရဲ့ server တွေက Physical Resource တွေ exhaust ဖြစ်သွားရင် down နေနိုင်ပါသေးတယ်။ ဥပမာ - Company A ရဲ့ site က ရုတ်တရက်ကြီး သုံးတဲ့ user တွေများသွားခဲ့လို့ ပဲဖြစ်ဖြစ် ၊ memory leak တွေရှိနေလို့ပဲဖြစ်ဖြစ် တစ်ခုခုက CPU peak ဖြစ်သွားတာမျိုး ၊ allocate လုပ်စရာ memory မရှိတော့တာမျိုးတွေဖြစ်ခဲ့လို့ရှိရင် Company B အပါအဝင် တခြားဟာတွေပါ အကုန်လိုက် down သွားဦးမှာပဲဖြစ်ပါတယ်။ ဒါဆို ဒါကိုဘယ်လိုကာလို့ရမလဲပေါ့။
ဒီလိုပြဿနာတွေအတွက်ကို Google က Cgroups (Control Group) ကို လုပ်ပြီး သူတို့ရဲ့ infrastructure မှာသုံးခဲ့ပါတယ်။ နည်းလမ်းက ဘယ်လိုမျိုးလဲဆိုတော့ Company A က CPU ဘယ်လောက် ၊ memory ဘယ်လောက် ၊ disk ဘယ်လောက် ၊ (Company B ကိုလည်း ထိုနည်းလည်းကောင်း) ပဲသုံးလို့ရမယ်လို့ အစောတည်းက ကန့်သတ်ထားပြီးတော့ အကယ်၍ Company A က ပေးထားတာထက် ပိုသုံးဖို့ကြံလာတယ်ဆိုရင် ထပ်မရဘူး ၊ ကိုယ့်ကံနဲ့ကိုယ်ဆိုပြီး ထားလိုက်တာမျိုးပဲဖြစ်ပါတယ်။
လက်တွေ့လုပ်ကြည့်မယ်ဆိုရင် အရင်ဆုံး unshare လုပ်ထားတဲ့ environment ထဲကနေ exit command နဲ့ ထွက်လိုက်ပါ။ ပြီးရင်တော့ cgroups tool ကို download လုပ်ပါမယ်။
apt-get install -y cgroup-tools
Control group တွေ ဆောက်ပါမယ်။
# create new cgroups
cgcreate -g cpu,memory,blkio,devices,freezer:/sandbox
ပြီးရင်တော့ unshare လုပ်ထားတဲ့ environment ရဲ့ Process Id ကို ps aux နဲ့ ရှာ ၊ copy ကူးပြီးတော့ cgroup ထဲကို ထည့်လိုက််ပါမယ်။
ps aux
cgclassify -g cpu,memory,blkio,devices,freezer:sandbox <PID>
ပြီးရင်တော့ limit လုပ်ပါမယ်။
# CPU ကို ၅ ရာခိုင်နှုန်းပဲ သုံးခွင့်ပေးပါမယ်
cgset -r cpu.cfs_period_us=100000 -r cpu.cfs_quota_us=$[ 5000 * $(getconf _NPROCESSORS_ONLN) ] sandbox
# Memory ကို 80MB ပဲ သုံးခွင့်ပေးပါမယ်
cgset -r memory.limit_in_bytes=80M sandbox
ဒုတိယ terminal မှာ htop နဲ့ ဘာတွေ ဘယ်လောက်သုံးနေလဲဆိုတာကြည့်ပါမယ်။
htop
ပထမ terminal မှာတော့ CPU စားစေမယ့် command တစ်ခု run ကြည့်ပြီး စမ်းကြည့်ရအောင်
yes > /dev/null
htop မှာကြည့်ရင် CPU က 5% ပဲ ယူနေတာကိုတွေ့ရမှာပါ။
Memory 80MB ထက်ကျော်ပြီးယူလားဆိုတာကိုလည်း အောက်က command နဲ့စမ်းကြည့်လို့ရပါတယ်။
yes | tr \\\\n x | head -c 1048576000 | grep n
Conclusion
ဒီတစ်ခေါက်ပြောပြသွားတာကတော့ Container တွေရဲ့ နောက်ကွယ်က backbone နည်းပညာတွေအကြောင်းနဲ့ ဘယ်လိုပြဿနာတွေကြောင့် ဘာလို့လုပ်ရလဲဆိုတာကိုပြောပြသွားတာဖြစ်ပါတယ်။ အခုလောက်ဆိုရင်တော့ Container ဆိုတာကြီးကို magic တစ်ခုအနေနဲ့ မမြင်လောက်တော့ဘူးလို့ထင်ပါတယ်။ Container တွေဟာ ဒီဟာတွေပေါ်မှာ အခြေခံထားတာဖြစ်ပြီးတော့ လက်ရှိမှာတော့ တခြားသော feature မျိုးစုံကိုလည်းလုပ်ပေးပါသေးတယ်။ ဒါ့ကြောင့်ဆက်လေ့လာစရာအနေနဲ့ နောက်အခေါက်မှာတော့ Docker အကြောင်း ပြောပြသွားမှာဖြစ်ပါတယ်။