namespace، cgroup و کانتینر بدون root
در این درس ۲ بخش
محیط بخش مشاهده: یک VM شخصی Ubuntu 24.04 LTS، Bash و دسترسی به proc و sysfs. آزمایش اختیاری اجرا به Podman نصبشده و آمادهٔ rootless، حساب معمولی، فضای ذخیرهسازی محلی و پیکربندی معتبر شناسههای فرعی توسط مدیر نیاز دارد. برای محدودیت منابع، پشتیبانی cgroup و واگذاری controllerها نیز مهم است. آمادهسازی مدیر را در این درس تغییر نمیدهیم. بخش اصلی اینترنت نمیخواهد؛ بخش دریافت اختیاری تصویر، دانلود عمومی و مصرف فضا دارد و در شبکهٔ مسدود قابل حذف است.
جداسازی دید، کنترل مصرف و مرز اعتماد
Namespace بخشی از منابع سراسری هسته را با یک دید جدا عرضه میکند: PID برای شمارهٔ فرایندها، mount برای نقاط اتصال، network برای پشتهٔ شبکه و user برای شناسهها و قابلیتها. دو فرایند میتوانند در بعضی namespaceها شریک و در بعضی جدا باشند. کانتینر مجموعهای از این سازوکارها همراه فایلسیستم تصویر و تنظیمات اجرای فرایند است. تصویر، الگوی ذخیرهشدهٔ لایهها و پیکربندی است؛ کانتینر یک نمونهٔ اجرا با وضعیت خودش است. چند نمونه میتوانند از یک تصویر ساخته شوند.
cgroup فرایندها را برای حسابداری و کنترل منابع گروهبندی میکند؛ مثلاً محدودیت حافظه، سهم پردازنده و تعداد فرایندها. namespace بهخودیخود سقف حافظه نمیسازد. cgroup namespace نیز بیشتر دید مسیرهای گروه را جدا میکند و با خودِ اعمال محدودیت برابر نیست. در cgroup v2 کنترل از یک سلسلهمراتب هماهنگ انجام میشود؛ کاربر معمولی تنها کنترلهایی را دارد که مدیر به او واگذار کرده است. پذیرش گزینهٔ حافظه یا CPU را از روی نسخه و پیکربندی واقعی بررسی میکنیم.
Rootless Podman با user namespace کار میکند؛ root داخل کانتینر معمولاً به هویت بیامتیاز کاربر روی میزبان نگاشت میشود و root عمومیِ میزبان نیست. این روش دامنهٔ امتیاز را کاهش میدهد، اما کانتینر ماشین مجازی نیست: فرایندها هستهٔ میزبان را شریکاند و جداسازی تضمین مطلق برای کد نامطمئن نیست. آسیبپذیری هسته یا تنظیم نامناسب میتواند مرز را تضعیف کند. اتصال مسیر حساس میزبان یا سوکت مدیریت کانتینرها، اختیار بیشتری میدهد؛ آزمایش ما هیچ مسیر میزبان و پورتی را منتشر نمیکند.
readlink /proc/self/ns/pid /proc/self/ns/mnt /proc/self/ns/user
cat /proc/self/cgroup
stat -fc %T /sys/fs/cgroup
# Optional, only with Podman already configured:
podman version
podman info --format '{{.Host.Security.Rootless}} {{.Host.CgroupsVersion}}'شناسههای namespace و مسیر cgroup خروجی محلیاند؛ عدد نمونه را معیار قبولی نگیرید. cgroup2fs نشانهٔ فایلسیستم cgroup v2 است، نه تضمین واگذاری همهٔ controllerها. در خروجی Podman، rootless باید برای این آزمایش true باشد. اگر ابزار آماده نیست، rootless برابر false است یا خطای پیکربندی رخ داد، آزمایش اجرا را کنار بگذارید و مسئلهٔ کاغذی را حل کنید؛ اجرای دوباره با sudo راهحل این تمرین نیست.
دریافت اختیاری و تثبیت تصویر واقعی
پروژهٔ Podman تصویر کوچک hello را در حساب رسمی خود روی Quay معرفی میکند. latest برچسبی تغییرپذیر است و فقط برای دریافت اولیه استفاده میشود. پس از دانلود، RepoDigests همان تصویر محلی را میخوانیم و مرجع واقعی شامل sha256 را نگه میداریم؛ هیچ مقدار ساختگی را جای digest نمیگذاریم. Digest هویت محتوا را تثبیت میکند، ولی بهتنهایی گواه بیخطر بودن محتوا نیست. خروجی دریافت، بسته به معماری و زمان تغییر میکند.
podman pull quay.io/podman/hello:latest || exit 1
podman image inspect quay.io/podman/hello:latest \
--format '{{range .RepoDigests}}{{println .}}{{end}}'
image_ref=$(podman image inspect quay.io/podman/hello:latest \
--format '{{index .RepoDigests 0}}') || exit 1
case "$image_ref" in
quay.io/podman/hello@sha256:*) ;;
*) printf 'unexpected image reference\n' >&2; exit 1 ;;
esac
printf '%s\n' "$image_ref"# Continue only if image_ref is the inspected quay.io/podman/hello@sha256:...
lab_name="linux2-12-${UID}-$(date +%s)-$$"
podman run --name "$lab_name" --label course=linux2-12 \
--pull=never --network=none --read-only --cap-drop=all \
--security-opt=no-new-privileges "$image_ref"پیش از اجرا، مرجع چاپشده باید همان مخزنِ معرفیشده و digest موجود در inspect باشد؛ اگر خالی یا نامرتبط است ادامه ندهید. pull=never دریافت ناخواسته را هنگام اجرا منع میکند. برنامهٔ hello پیام کوتاهی چاپ میکند و معمولاً پایان مییابد؛ پوستهٔ عمومی یا سرویس ماندگار نیست. network=none شبکهٔ کانتینر را جدا و بیاتصال نگه میدارد. read-only لایهٔ ریشه را نوشتنی نمیکند؛ این گزینه تضمین خواندنیبودن تمام منابع احتمالیِ یک کانتینر دلخواه نیست.
podman container inspect "$lab_name" \
--format '{{.State.Status}} {{.State.ExitCode}} {{index .Config.Labels "course"}}'
podman ps -a --filter "name=$lab_name" --filter label=course=linux2-12
# Only if this exact, owned lab container is still running:
# podman stop "$lab_name"
# After confirming the exact name and course label:
podman rm "$lab_name"تمرین و پاسخ: همکار میگوید چون id داخل کانتینر عدد صفر میدهد، میتوان همهٔ فایلهای میزبان را خواند و مصرف حافظه هم خودکار محدود است. هر دو استنتاج نادرستاند: UID داخل باید با نگاشت user namespace و مجوزهای میزبان تفسیر شود؛ محدودیت مصرف نیز به cgroup و تنظیم صریح وابسته است. گزینههایی مثل memory و cpus در rootless، به نسخهٔ cgroup و واگذاری کنترل وابستهاند و بعضی حالتها پشتیبانی نمیشوند. پاکسازی بالا فقط همان نمونهٔ نامدارِ خودتان را حذف میکند؛ از حذف گروهی همهٔ کانتینرها استفاده نکنید. تصویر دانلودشده برای استفادهٔ بعدی باقی میماند.
منابع رسمی انگلیسی:
Namespaces — Linux man-pages: https://man7.org/linux/man-pages/man7/namespaces.7.html
Control Group v2 — Linux kernel: https://www.kernel.org/doc/html/latest/admin-guide/cgroup-v2.html
Rootless mode — Podman: https://docs.podman.io/en/latest/markdown/podman.1.html
Podman run: https://docs.podman.io/en/latest/markdown/podman-run.1.html
Image inspect — Podman: https://docs.podman.io/en/latest/markdown/podman-image-inspect.1.html
Podman Hello image — Podman project: https://github.com/containers/PodmanHello
تألیف و مثالهای اختصاصی: تحریریه درسآوند. منابع رسمی انگلیسی برای مطالعهٔ تکمیلی معرفی شدهاند.
متن درسهای عمومی را بدون ورود بخوانید.