Exercises — Containers — namespaces, cgroups, difference from VMs
4.2.41 · D4· Coding › Operating Systems › Containers — namespaces, cgroups, difference from VMs
Yahan sab kuch parent note ke ek sentence par tika hai: ek container ek normal process hai jise is baare mein jhooth bolaya jaata hai ki woh kya SEE kar sakta hai (namespaces) aur jo woh USE kar sakta hai usme limit lagayi jaati hai (cgroups), EK shared kernel par. Har sawaal ke liye yahi picture dimag mein rakho.
Level 1 — Recognition
Goal: identify karo ki koi scenario kis kernel feature ya concept se belong karti hai. Koi computation nahi.
Exercise L1.1
Har item ke liye batao ki yeh ek namespace ka kaam hai (controls what you SEE) ya ek cgroup ka kaam hai (controls what you USE): (a) container ki pehli process PID 1 ke roop mein appear hoti hai; (b) container ko 512 MB par OOM-kill kar diya jaata hai; (c) container ka apna hostname hai; (d) container ko ek core ke 50% par throttle kiya jaata hai; (e) container ke paas network interfaces ki apni private list hai.
Recall Solution L1.1
Rule yeh hai: SEE = namespace, USE = cgroup. (Agar sure na ho to upar wala SEE-vs-USE figure dobara dekho ki har item kis column mein aata hai.)
- (a) namespace (PID namespace — tum ek fresh numbering dekhte ho jo 1 se shuru hoti hai).
- (b) cgroup (memory controller — ek quantity limit).
- (c) namespace (UTS namespace woh hostname isolate karta hai jo tum dekhte ho).
- (d) cgroup (cpu controller — ek quantity limit).
- (e) namespace (network namespace — interfaces ka ek private view).
Score = 5 mein se kitne sahi.
Exercise L1.2
Har syscall ko uske ek-line kaam se match karo: clone, unshare, setns.
(i) current process ko brand-new namespaces mein move karo;
(ii) fresh namespaces ke saath ek child process create karo;
(iii) ek already-existing namespace mein join karo (docker exec dwara use hota hai).
Recall Solution L1.2
clone→ (ii) fresh namespaces ke saath ek child create karo (CLONE_NEW*flags ke through). Dekho Processes and the clone/fork syscall.unshare→ (i) current process ko new namespaces mein move karo.setns→ (iii) ek existing namespace mein join karo — yahi haidocker execek running container ke andar kaise step karta hai. Dekho Linux Kernel — syscalls.
Level 2 — Application
Goal: CPU-quota aur memory formulas mein numbers plug in karo.
Exercise L2.1
Ek cgroup ka cpu.max = "50000 100000" hai (microseconds). Isse ek core ka kitna fraction milta hai, aur har 100 ms window mein woh kitne milliseconds CPU run kar sakta hai?
Recall Solution L2.1
Formula (parent note se):
KYA dono numbers ka matlab hai, upar bucket figure par dekha gaya: kernel scheduler (dekho Scheduling — CFS and CPU shares) bucket mein quota microseconds ka runtime daalta hai, aur ise har period microseconds mein refill karta hai.
Plug in: cores of one core.
Milliseconds: CPU per ms window (figure mein mint stretch). 50 ms spend karne ke baad, group frozen ho jaata hai (slate stretch) jab tak agla window bucket refill nahi karta.
Exercise L2.2
Tum ek aisa container chahte ho jo 1.5 cores tak burst kar sake, har 100 ms window mein check kiya jaaye. cpu.max mein tum kya string likhoge?
Recall Solution L2.2
Hum formula ko ulta karte hain. Hum chahte hain aur .
To likho: cpu.max = "150000 100000".
YEH 1.0 exceed karna legal kyun hai: ek multi-core machine par ek group ek full core se zyada use kar sakta hai, kyunki ek 100 ms window mein woh ek saath kai CPUs par runtime accumulate kar sakta hai — bucket mein ab zyada tokens hain jitne ek CPU akele spend kar sakta.
Exercise L2.3
256 MiB ko woh exact byte value mein convert karo jo tum memory.max mein echo karoge.
Recall Solution L2.3
"MiB" (mebibyte) powers of two use karta hai, ten nahi:
To: echo 268435456 > /sys/fs/cgroup/myapp/memory.max.
Exact bytes kyun: kernel ek process ki resident memory (RSS) bytes mein count karta hai; jab running total is threshold ko cross karta hai to memory controller sirf us group ke andar OOM-killer trigger karta hai.
Level 3 — Analysis
Goal: explain karo KI koi behaviour kyun hoti hai, yeh reason karte hue ki kaunsa global table private hai (ya nahi).
Exercise L3.1
Tum unshare --pid --fork --mount-proc bash run karte ho, phir ps aux karte ho, aur sirf do ya teen processes dekhte ho — tumhari shell PID 1 hai. Host par abhi bhi 400 running processes hain. Global tables ke terms mein explain karo ki woh tumhare view se kyun gayab ho gaye lekin abhi bhi run kar rahe hain.
Recall Solution L3.1
Picture — neeche wali figure dekho. Kernel normally ek global PID table (baaya ledger) rakhta hai. Ek PID namespace tumhare process group ko us table ki apni private copy (daaya ledger) deta hai. Khas baat, neeche ki mint bar dikhati hai ki same single kernel abhi bhi har real process run kar raha hai — kuch delete nahi hua.

Figure padhte hue:
- 400 host processes abhi bhi exist karte hain aur abhi bhi CPU/RAM consume karte hain — kernel (mint bar) unhe abhi bhi run karta hai. Woh baaye ledger ko fill karte hain.
- Lekin
ps/procpadhta hai, jise--mount-procne tumhare PID namespace ko reflect karne ke liye remount kiya — isliye woh daaya ledger padhta hai. Us ledger mein sirf woh entries hain jo tumne create ki hain, 1 se renumber hokar. - Tumhari shell PID 1 hai kyunki ek fresh table 1 se counting shuru karti hai (daaye ledger ka top).
Key idea: yahan isolation bookkeeping hai, deletion nahi. Dono ledgers same running processes describe karte hain; tumhe bas padhne ke liye alag ledger de diya gaya. Yahi parent note ka "SEE karne ke baare mein jhooth bolaya gaya" hai.
Exercise L3.2
Ek container ke andar, uname -r 6.5.0-14-generic print karta hai. Host par, uname -r same 6.5.0-14-generic print karta hai. Same host par ek VM ke andar, uname -r 5.15.0-89-generic print karta hai. Yeh ek command container aur VM ke beech difference ke baare mein kya prove karti hai?
Recall Solution L3.2
uname -r running kernel se uska version poochhta hai.
- Container = host: identical strings prove karti hain ki exactly ek kernel hai. Container host kernel par ek process hai; jab woh poochhta hai "main kis kernel par hoon?", to use host ka answer milta hai kyunki wahi uska kernel hai. Isolation namespaces + cgroups (software bookkeeping) thi, doosra kernel nahi.
- VM ≠ host: alag version prove karta hai ki VM ne hypervisor dwara provide ki gayi virtual hardware par apna separate guest kernel boot kiya (dekho Virtualization and Hypervisors (Type 1 vs Type 2)).
Ek-line rule proved: same kernel version ⇒ container; different kernel version ⇒ VM.
Exercise L3.3
Ek colleague ek fork-bomb-prone service ko pids.max = 100 wale cgroup mein add karta hai lekin use koi namespace nahi deta. Service misbehave karti hai aur ek host database process par kill -9 run karti hai — aur woh kaam karta hai. Kaunsa protection present tha, kaunsa missing tha, aur kill kyun succeed hui?
Recall Solution L3.3
- Present:
pidscgroup controller. Yeh processes ki number 100 par cap karta hai, isliye ek fork bomb us count se zyada explode nahi kar sakta. Yeh ek USE limit hai. - Missing: PID namespace. Iske bina, service host ke ek global PID table share karti hai — isliye woh database ka PID SEE kar sakti hai aur use target ke roop mein naam de sakti hai.
- Kill kyun succeed hui:
killko (1) target ka PID visible hona chahiye, aur (2) sufficient privilege chahiye. Cgroup ne quantity limit ki lekin visibility ke baare mein kuch nahi kiya, isliye condition (1) hold ki. Kill through ho gayi.
Lesson: cgroups ≠ isolation. Tumhe host PIDs ko invisible karne ke liye ek PID namespace chahiye tha (aur privilege drop karne ke liye ek user namespace).
Level 4 — Synthesis
Goal: namespaces, cgroups, filesystems, aur networking ko combine karke ek working design banao.
Exercise L4.1
Ek untrusted web service run karne ke liye minimum namespaces + cgroups + filesystem setup design karo taaki woh: (a) host processes nahi dekh sake, (b) apna root / ho, (c) apna IP ho, (d) 512 MiB RAM ya 1 core se zyada use na kar sake, (e) container-root host-root na ho. Har piece aur woh ek cheez jो woh provide karta hai list karo.
Recall Solution L4.1
Ise feature by feature build karo — har requirement exactly ek mechanism se map hoti hai:
| Requirement | Mechanism | Kya provide karta hai |
|---|---|---|
| (a) host processes nahi dekh sakta | PID namespace (CLONE_NEWPID) |
private process table |
(b) apna root / |
Mount namespace + pivot_root/chroot ke through ek new root |
private mount table — dekho Filesystems — mount and chroot |
| (c) apna IP | Network namespace (CLONE_NEWNET) + ek virtual interface pair jo host se bridge ho |
private interfaces/routes — dekho Networking — virtual interfaces and bridges |
| (d) ≤512 MiB, ≤1 core | cgroup: memory.max = 536870912, cpu.max = "100000 100000" |
resource caps |
| (e) container-root ≠ host-root | User namespace (CLONE_NEWUSER) jo UID 0 → e.g. 100000 map kare |
privilege drop |
Synthesis idea: clone() exactly un flags ke saath jo requirements demand karti hain — CLONE_NEWPID | CLONE_NEWNS | CLONE_NEWNET | CLONE_NEWUSER — in paanch requirements ke name liye har global table ko privatise karta hai; cgroup woh meter karta hai jo woh consume karta hai. Saath mein = isolation (SEE) + limits (USE). Yahi hai ek container. (Note: humne CLONE_NEWUTS add nahi kiya, kyunki koi requirement hostname isolate karne ki nahi maangti — sirf wahi namespace flags add karo jo ek requirement actually maangti hai.) Root filesystem typically ek overlayfs image hogi.
Byte check for (d): bytes; cpu.max "100000 100000" = core.
Exercise L4.2
"Docker for Windows Linux containers run kar sakta hai." Woh poora chain explain karo jo actually hota hai, aur kyun ek Linux binary ke syscalls directly Windows NT kernel par execute nahi ho sakte.
Recall Solution L4.2
Core fact: ek container sirf host kernel par processes hai, aur woh processes us kernel ko syscalls karti hain. Ek Linux binary Linux syscall numbers issue karti hai (e.g. clone, openat). Windows NT kernel ek alag syscall interface implement karta hai, isliye woh numbers uske liye kuch nahi mean karte — binary immediately crash ho jaayegi.
To chain yeh hai:
- Docker Desktop quietly ek lightweight VM boot karta hai (ek hypervisor ke through) jo ek real Linux kernel run karta hai.
- Tumhara "Linux container" us Linux VM ke andar namespaced+cgrouped processes ka ek set hai, Linux kernel par.
- Tumhare Windows desktop se yeh seamless dikhta hai, lekin wahan do layers hain: ek VM (Linux kernel paane ke liye) aur ek container (uske andar).
Conclusion: "tum ek foreign OS kernel container mein nahi run kar sakte" wala rule break nahi hua — Docker ne pehle ek VM ke through ek real Linux kernel smuggle kiya. Har container ke liye ek kernel, aur woh sahi kernel hona chahiye.
Level 5 — Mastery
Goal: limiting behaviour, edge cases, aur degenerate inputs ko debug aur reason karo.
Exercise L5.1
Ek container ko cpu.max = "0 100000" set kiya gaya hai. Usmein processes ka kya hota hai, aur kyun? Phir cpu.max = "max 100000" se contrast karo.
Recall Solution L5.1
ko boundaries par apply karo (Level 2 se refill bucket picture karo — ab bucket ya to empty hai ya bottomless).
"0 100000": , isliye cores. Scheduler har window mein zero runtime ka ek bucket refill karta hai → group ko bilkul CPU nahi milta aur permanently frozen (starved) ho jaata hai. Yeh ek degenerate input hai: ek valid-looking string jo container halt kar deti hai. Kabhi kabhar yahi chahte hain."max 100000": literal tokenmaxka matlab koi limit nahi hai. effectively, isliye group machine mein jitne cores hain utne use kar sakta hai — unthrottled.
Mastery point: same file poore range ko cover karti hai "totally frozen" (0) se "unlimited" (max) tak. Hamesha edges sanity-check karo, sirf koi mid value jaise 50000 nahi.
Exercise L5.2
Ek host par do containers mein se har ek ka cpu.max = "150000 100000" (1.5 cores each) hai, lekin machine mein sirf 2 physical cores hain. Dono ek saath CPU-bound kaam run karte hain. Kya unhe dono ko 1.5 cores milte hain? Actually kya hota hai, aur kya enforce karta hai?
Recall Solution L5.2
cores requested, lekin sirf exist karte hain. cpu.max ek ceiling hai, reservation nahi — yeh cap karta hai ki har ek kitna le sakta hai, lekin aise cores manufacture nahi kar sakta jo exist hi nahi karte.
- Kyunki koi bhi individually 2 cores se zyada nahi maangta, har ek 1.5 tak burst karne ki koshish karta hai. Lekin total demand (3.0) supply (2.0) se zyada hai, isliye CFS scheduler (dekho Scheduling — CFS and CPU shares) 2 available cores ko dono groups ke beech fairly time-slice karta hai.
- Actually kya hota hai: equal CPU weight aur dono fully CPU-bound hone par, CFS 2 cores ko evenly split karta hai, isliye har container approximately core par converge karta hai — apni 1.5 ceiling se neeche. Kisi bhi container ka
cpu.maxceiling kabhi nahi bites, kyunki hardware limit (total 2 cores) pehle bite karta hai. Agar ek container ka load drop ho jaaye, doosra apni 1.5 ceiling ki taraf rise kar sakta hai, kyunki spare cores available ho jaate. - Kya enforce karta hai: do layers order mein act karti hain — pehle CFS scarce real cores ko fairly share karta hai; per-group
cpu.maxthrottle tabhi kick in karta agar koi group apni 1.5-core ceiling exceed karne ki koshish karta, jo yahan hota hi nahi.
Mastery point: limit (cpu.max — ek upper bound) aur weight/share (cpu.weight — contention mein proportional split) mein distinguish karo. Ceilings hogging rokti hain; weights decide karti hain ki contention mein kaun jeetta hai. Koi bhi extra cores create nahi karta.
Exercise L5.3
Ek process ko memory.max = 268435456 (256 MiB) wale cgroup mein daala jaata hai lekin use koi mount namespace aur koi PID namespace nahi diya jaata. Woh /proc/meminfo padhta hai aur host ki total RAM report karta hai, maan lo 32 GiB — phir 1 GiB allocate karta hai. App ko kya lagta hai ho raha hai, kernel actually kya karta hai, aur yeh classic bug kyun hai?
Recall Solution L5.3
- App ko kya lagta hai:
/proc/meminfohost ka 32 GiB dikhata hai (koi mount/PID namespace nahi matlab woh real host/procpadhta hai). Isliye app khushi se apna cache, maan lo, 8 GiB size karta hai, yeh sochte hue ki room hai. - Kernel actually kya karta hai: memory cgroup is group ka RSS count karta hai. Jis waqt group 256 MiB cross karta hai, OOM-killer us group ke andar fire karta hai aur app ko maar deta hai — 1 GiB se bahut pehle.
- Yeh classic kyun hai: visibility (
/proc) aur limits (cgroup) alag hain. Namespace ke bina jo/procko cgroup reflect karaye, app host numbers dekhta hai aur galat plan karta hai, phir mar jaata hai. Yahi real-world "container reports host RAM" bug hai (JVMs aur Node pehle heaps host/proc/meminfose size karte the).
Fix in practice: proper namespaces ke andar run karo aur runtime ko cgroup limits (memory.max) padhne do /proc/meminfo ki jagah. Ek baar phir: SEE (namespace) aur USE (cgroup) independent hain — dono set karo, aur app ko cgroup par trust karo.
Recall Self-test checklist (finish karne ke baad reveal karo)
Ek command mein container vs VM decide karne wala rule ::: uname -r — same version = container (ek kernel), different = VM (apna kernel)
Har container ke do independent legs ::: namespaces (isolate karo what you SEE) + cgroups (limit karo what you USE)
cpu.max "0 100000" kya karta hai ::: quota 0 → 0 cores → process permanently starved/frozen
memory.max ke liye "256 MB" ≠ "256 MiB" kyun ::: MB = 256×10^6 = 256000000; MiB = 256×2^20 = 268435456 bytes
Docker-for-Windows Linux containers kyun run kar sakta hai ::: woh ek hidden lightweight Linux-kernel VM boot karta hai; containers us mein run karte hain