کانتینرها از صفر با ابزارهای لینوکس (اختیاری)

نکته: این درس کاملاً اختیاری و با هدف آموزشی طراحی شده است. برای کار کردن با Docker نیازی به اجرای روزمره‌ی این دستورات ندارید؛ اما درک این مفاهیم به شناخت دقیق‌تر سازوکار کانتینرها و عملکرد هسته‌ی لینوکس کمک می‌کند.

در درس قبل اشاره کردیم که کانتینرها سیستم‌عامل جداگانه‌ای نیستند، بلکه پردازش‌های لینوکس هستند که با قابلیت‌های هسته محدود شده‌اند. در این درس با ابزارهای پیش‌فرض لینوکس و بدون نصب ابزارهایی مانند Docker، یک محیط ایزوله‌ی ساده می‌سازیم.


ارکان سه‌گانه‌ی کانتینر در هسته‌ی لینوکس

هر سیستم کانتینری از ۳ مکانیزم بنیادین هسته‌ی لینوکس برای ایزولاسیون استفاده می‌کند:

NamespacesControl Groups (cgroups)Chroot / Pivot Root
دیدگاه و ایزولاسیون پردازشمحدودسازی و سنجش منابعایزولاسیون فایل‌سیستم
پردازش چه بخش‌هایی از سیستم را می‌بیند؟پردازش چقدر CPU، RAM و I/O مصرف می‌کند؟پردازش به کدام مسیرها و فایل‌ها دسترسی دارد؟

۱. فضاهای نام (Linux Namespaces)

فضای نام زاویه‌ی دید پردازش را محدود می‌کند. لینوکس چند فضای نام مجزا دارد:

  • uts (UNIX Timesharing System): جداسازی نام هاست (Hostname) و Domain Name.
  • pid (Process ID): پردازش داخل فضای نام فقط پردازش‌های فرزند خود را می‌بیند و شماره‌ی ۱ می‌گیرد.
  • net (Network): اختصاص اینترفیس‌های کارت شبکه، جدول مسیربندی (Routing Table) و پورت‌های مستقل.
  • mnt (Mount): جداسازی مسیرها و Mount Pointهای سیستم فایل.
  • ipc (Inter-Process Communication): جداسازی حافظه‌ی اشتراکی و صف‌های پیام لینوکس.
  • user (User IDs): نگاشت یوزر Root داخل کانتینر به یک یوزر غیر-Root در سیستم میزبان.

۲. گروه‌های کنترل (Control Groups یا Cgroups)

اگر کانتینری دچار نشت حافظه (Memory Leak) شود، نباید کل سرور از کار بیفتد. Cgroups (به‌ویژه نسخه مدرن cgroups v2 در /sys/fs/cgroup) تعیین می‌کند که پردازش‌ها حداکثر چقدر RAM، CPU یا سرعت خواندن/نوشتن از دیسک می‌توانند مصرف کنند. هسته‌ی لینوکس پردازشی که بیش از حد مجاز رم مصرف کند را با خطای معروف OOM-Killed (Out of Memory) متوقف می‌کند.

۳. ایزولاسیون فایل‌سیستم (chroot و pivot_root)

دستور chroot (مخفف Change Root) دایرکتوری ریشه‌ی (/) سیستم را برای پردازش مشخصی تغییر می‌دهد تا نتواند به فایل‌های حساس سرور میزبان سرک بکشد.


آزمایش عملی: ساخت کانتینر با دستور unshare

ابزار unshare در لینوکس به شما اجازه می‌دهد تا بدون نیاز به کدنویسی در زبان C، پردازشی را با فضاهای نام جدید اجرا کنید.

بیایید روی سرور اوبونتو این را امتحان کنیم:

گام ۱: اجرای پردازش در فضای نام مجزا

دستور زیر را با دسترسی sudo اجرا کنید:

1sudo unshare --uts --pid --mount --fork /bin/bash

پارامترها:

  • --uts: نام هاست اختصاصی
  • --pid: فضای نام پردازش مستقل
  • --mount: فضای نام Mount مستقل
  • --fork: اجرای شل در قالب یک پردازش فرزند جداگانه

گام ۲: بررسی ایزولاسیون نام هاست (UTS)

اکنون در شل جدید هستید. بیایید نام هاست را عوض کنیم:

1hostname isolated-box

حالا اگر در یک پنجره یا تب ترمینال دیگر نام هاست سرور اصلی را چک کنید:

1hostname

نام هاست سرور اصلی دست‌نخورده باقی مانده است، زیرا پردازش اول در یک UTS Namespace جداگانه قرار دارد!

گام ۳: بررسی ایزولاسیون پردازش‌ها (PID Namespace)

داخل شل ایزوله‌شده، برای اینکه دستور ps پردازش‌های سیستم را از این دیدگاه جدید بخواند، باید فایل‌سیستم /proc را مجدداً mount کنیم:

1mount -t proc proc /proc
2ps aux

خروجی شبیه به این خواهد بود:

1USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
2root         1  0.0  0.0   7456  4128 pts/0    S    10:20   0:00 /bin/bash
3root         4  0.0  0.0   8888  3112 pts/0    R+   10:21   0:00 ps aux

می‌بینید که داخل این فضا، هیچ اثری از پردازش‌های سرور اصلی نیست! شل ما پردازش شماره‌ی ۱ (PID 1) شده است و جز خودش و دستور ps پردازش دیگری در سیستم وجود ندارد.

با زدن دستور exit از این فضای ایزوله خارج شوید:

1exit

چرا مستقیماً از این دستورها استفاده نمی‌کنیم و به Docker نیاز داریم؟

همان‌طور که دیدید، ساختن یک پردازش ایزوله با دستورات هسته امکان‌پذیر است، اما در مقیاس صنعتی بسیار دشوار و پرخطاست:

  • چگونه فایل‌سیستم یک برنامه و کتابخانه‌هایش را دانلود و بسته‌بندی کنیم؟
  • چگونه ترافیک شبکه، پورت‌ها و پل‌های مجازی (Bridge) را خودکار پیکربندی کنیم؟
  • چگونه ایمیج‌ها را به صورت لایه‌ای (Layered Filesystem) ذخیره کنیم تا فضای دیسک هدر نرود؟
  • چگونه چرخه‌ی حیات و لاگ‌های صدها پردازش را مدیریت کنیم؟

Docker و ابزارهای مشابه، مدیریت خودکار Namespaces، Cgroups، فایل‌سیستم لایه‌ای (OverlayFS) و پیکربندی شبکه‌ها را در قالب دستوراتی یکپارچه و استاندارد مانند docker run انجام می‌دهند تا نیازی به اجرای دستی این فرایندها نباشد.


منابع یادگیری

ویدیو