SSD Nodes Learn Hosting plans →
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-09-04

آموزش نصب Docker روی Rocky Linux و AlmaLinux

نصب Docker Engine روی Rocky Linux و AlmaLinux با dnf. در این راهنما تداخل Podman با دستور docker و تنظیمات SELinux برای bind mounts را به صورت کامل رفع می‌کنیم.

نصب Docker روی Rocky Linux و AlmaLinux

برای نصب Docker روی Rocky Linux یا AlmaLinux، باید مخزن dnf اختصاصی Docker را اضافه کنید، موتور آن را به همراه پلاگین compose نصب کرده و سپس سرویس را فعال کنید. این فرآیند شامل چهار دستور است و در هر دو توزیع یکسان عمل می‌کند، زیرا هر دو بازسازی‌هایی از Red Hat Enterprise Linux (RHEL) هستند و ساختار بسته‌های مشابهی دارند. CentOS Stream نیز به همین صورت عمل می‌کند. تمام موارد زیر برای هر دو صدق می‌کند، بنابراین اگر هنوز در حال انتخاب بین آن‌ها هستید، عوامل تعیین‌کننده، تعهد به سازگاری هر پروژه و پشتیبانی از پردازنده‌های قدیمی‌تر شماست.

نصب بسیار کوتاه است، بنابراین بخش عمده این راهنما به تفاوت‌های Enterprise Linux (EL) با Ubuntu می‌پردازد. ممکن است Podman از قبل دستور docker را در image شما در اختیار گرفته باشد. SELinux دسترسی به فایل‌های bind-mount شده را تا زمانی که برچسب (label) مناسب نداشته باشند، مسدود می‌کند. Firewalld پورت‌های منتشر شده توسط Docker را فیلتر نمی‌کند، بنابراین ممکن است پورت یک container برای اینترنت باز باشد در حالی که firewall-cmd گزارش می‌دهد هیچ پورتی باز نیست.

از اسکریپت راحتی (convenience script) موجود در get.docker.com استفاده نکنید. مستندات خود Docker توصیه می‌کند که از آن در محیط‌های production استفاده نشود. این اسکریپت بدون اجازه، پیکربندی مخازن شما را بازنویسی می‌کند و برای ارتقا نیز نمی‌توان آن را با خیال راحت دوباره اجرا کرد. اضافه کردن دستی مخزن باعث می‌شود dnf upgrade با Docker مانند سایر بسته‌های موجود در سیستم رفتار کند. این کار همچنین موتور Docker را در محدوده dnf-automatic قرار می‌دهد، اگر از آن برای اعمال خودکار به‌روزرسانی‌های امنیتی استفاده می‌کنید؛ بنابراین از ابتدا تصمیم بگیرید که آیا می‌خواهید Docker به‌صورت خودکار وصله شود یا برای بازه‌های زمانی نگهداری (maintenance window) نگه داشته شود. در هر صورت، ارتقا باعث جایگزینی فایل باینری نصب‌شده می‌شود در حالی که dockerd قدیمی همچنان در حال اجرا باقی می‌ماند، و needs-restarting دستوری است که به شما می‌گوید کدام سرویس‌ها هنوز در حال اجرای کدی هستند که به‌تازگی جایگزین کرده‌اید.

آیا podman در حال حاضر به دستور docker پاسخ می‌دهد؟

توزیع‌های Rocky Linux و AlmaLinux به‌صورت پیش‌فرض podman را در مخازن خود دارند و بسیاری از تصاویر VPS آن را برای شما نصب می‌کنند. برخی از تصاویر فراتر رفته و podman-docker را نصب می‌کنند که یک اسکریپت shell در مسیر /usr/bin/docker قرار می‌دهد و podman را فراخوانی می‌کند. در نتیجه، هر دستور docker که تایپ می‌کنید، به‌جای Docker، برنامه podman را اجرا می‌کند و راهنمایی که برای Docker نوشته شده است، خروجی غیرمنتظره‌ای به شما می‌دهد.

اولین نشانه، یک بنر است. اسکریپت /usr/bin/docker وجود فایل /etc/containers/nodocker را بررسی می‌کند و در صورتی که آن فایل موجود نباشد، پیش از اجرای هر دستوری، یک خط متن چاپ می‌کند:

Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg.

ممکن است شخصی آن فایل را برای غیرفعال کردن بنر ایجاد کرده باشد، بنابراین تنها به آن تکیه نکنید. از دیتابیس بسته‌ها بپرسید که کدام بسته مالک این باینری است:

command -v docker
rpm -qf "$(command -v docker)"

پاسخی که با podman-docker شروع شود به این معنی است که podman در حال پاسخگویی است. پاسخی که با docker-ce-cli شروع شود به این معنی است که Docker واقعی نصب شده است. اگر rpm -qf گزارش دهد که هیچ بسته‌ای مالک این فایل نیست، یعنی شخصی آن را به‌صورت دستی نصب کرده است و باید پیش از اعتماد به آن، اسکریپت را مطالعه کنید.

برنامه Podman همان تصاویر OCI را اجرا می‌کند و انتخاب معقولی است. اگر آن را می‌خواهید، همین‌جا متوقف شوید. هر دو موتورهای کانتینر لینوکس هستند، بنابراین اگر هنوز در مورد پلتفرم تصمیم نگرفته‌اید، دانستن این نکته مفید است که FreeBSD jails به جای اجرای تصاویر لایه‌بندی شده که از یک registry دریافت می‌شوند، یک userland کامل را ایزوله می‌کنند. اگر Docker Engine را می‌خواهید، ابتدا بسته‌های تداخلی را حذف کنید. این لیستی است که Docker برای RHEL مستند کرده است:

sudo dnf remove docker docker-client docker-client-latest docker-common \
  docker-latest docker-latest-logrotate docker-logrotate docker-engine podman runc

پیش از تأیید، بررسی کنید که dnf قصد دارد چه مواردی را حذف کند. در یک تصویر VPS تازه، این لیست کوتاه است. در سیستمی که قبلاً استفاده شده است، حذف podman ممکن است باعث حذف cockpit-podman یا ابزار دیگری شود که به آن وابسته است.

نگهداری podman در کنار Docker در اصل ممکن است: فقط podman-docker را حذف کنید تا نام docker آزاد شود، و همچنین runc که بسته containerd.io جایگزین آن می‌شود. مستندات Docker، برنامه podman را یک بسته تداخلی در نظر می‌گیرد، بنابراین این چیدمان توسط Docker پشتیبانی نمی‌شود. اگر نصب همچنان تداخلی گزارش کرد، از لیست حذف کامل در بالا استفاده کنید.

افزودن مخزن Docker با استفاده از dnf config-manager

Docker بسته‌های RPM خود را برای Enterprise Linux در download.docker.com منتشر می‌کند. فایل مخزن به شاخه CentOS اشاره دارد که همان شاخه‌ای است که Rocky Linux و AlmaLinux با آن تطبیق داده می‌شوند. اشاره کردن یک سیستم Rocky به مخزن CentOS ممکن است اشتباه به نظر برسد، مگر اینکه بدانید چگونه هر دو توزیع پس از تبدیل CentOS به Stream توسط Red Hat در سال 2020، از تبار CentOS رشد کردند. در بررسی انجام‌شده در اوت 2026، Docker این مخزن را برای CentOS Stream 9 و CentOS Stream 10 مستند کرده است.

sudo dnf -y install dnf-plugins-core
sudo dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

نسخه 5 از dnf آرگومان --add-repo را حذف کرده است، بنابراین دستور دوم در نسخه‌های جدیدتر با خطا مواجه می‌شود. بررسی کنید کدام نسخه را دارید و سپس فرمت مناسب را انتخاب کنید:

dnf --version

اگر خروجی نسخه 5.x را نشان داد، از فرمت زیرمجموعه (subcommand) استفاده کنید:

sudo dnf config-manager addrepo --from-repofile=https://download.docker.com/linux/centos/docker-ce.repo

هر دو دستور فایل یکسانی را در /etc/yum.repos.d/docker-ce.repo می‌نویسند. استفاده از فرمت اشتباه باعث بروز خطای unknown-argument می‌شود و به جای انجام عملیات نادرست به‌صورت خاموش، شما را مطلع می‌کند؛ بنابراین متوجه آن خواهید شد.

آن فایل مخزن، baseurl را روی مسیری شامل $releasever تنظیم می‌کند و dnf این متغیر را از بسته release شما بسط می‌دهد. Rocky Linux و AlmaLinux این متغیر را روی شماره نسخه اصلی (major version) تنظیم می‌کنند، بنابراین در EL 9 مقدار 9 و در EL 10 مقدار 10 خواهد بود؛ به همین دلیل است که مخزن CentOS روی یک سیستم Rocky به‌درستی کار می‌کند. پیش از نصب، بسط متغیر را تأیید کنید:

sudo dnf repoinfo docker-ce-stable

خط Repo-baseurl را بخوانید. این خط باید به /9/x86_64/stable یا /10/x86_64/stable ختم شود. اگر نسخه سیستم شما $releasever را روی یک نسخه جزئی (point version) مانند 9.6 تنظیم کرده باشد، dnf هنگام دریافت متادیتا برای آن URL خطای Status code: 404 گزارش می‌دهد. این مشکل را با ویرایش /etc/yum.repos.d/docker-ce.repo و جایگزینی $releasever با شماره اصلی نسخه برطرف کنید.

نصب موتور و افزونه compose

sudo dnf install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

پنج بسته وجود دارد که هر کدام وظیفه مشخصی دارند. docker-ce همان دیمون (daemon) است، dockerd. docker-ce-cli همان دستور docker است که تایپ می‌کنید. containerd.io محیط اجرای کانتینر است که دیمون آن را هدایت می‌کند. docker-buildx-plugin وظیفه ساخت ایمیج‌ها را بر عهده دارد. docker-compose-plugin قابلیت docker compose را به عنوان یک زیردستور فراهم می‌کند.

این بسته‌ها فایل اجرایی docker-compose که با خط تیره جدا شده باشد را نصب نمی‌کنند. آن مربوط به نسخه Compose v1 بود که در ژوئیه 2023 به پایان عمر خود رسید. هر چیزی که docker-compose را با خط تیره فراخوانی می‌کند، باید به docker compose با یک فاصله به‌روزرسانی شود.

اولین نصب برای وارد کردن کلید امضای Docker متوقف می‌شود و اثر انگشت (fingerprint) آن را به شما نشان می‌دهد. این کلید از gpgkey=https://download.docker.com/linux/centos/gpg در فایل مخزنی که به‌تازگی اضافه کرده‌اید می‌آید، بنابراین پیش از پذیرش، اثر انگشتی که dnf چاپ می‌کند را با آن URL مقایسه کنید.

یک خطا به اندازه کافی رایج است که باید به آن اشاره کرد. اگر dnf گزارش دهد که containerd.io به container-selinux نیاز دارد و هیچ بسته‌ای آن را فراهم نمی‌کند، مخزن AppStream شما غیرفعال است. دستور dnf repolist را اجرا کنید و تأیید کنید که appstream در لیست وجود دارد، زیرا این همان جایی است که container-selinux در EL 9 و EL 10 عرضه می‌شود.

راه‌اندازی Docker و تأیید اجرای آن

sudo systemctl enable --now docker
sudo systemctl status docker --no-pager
sudo docker run --rm hello-world

بسته‌های RPM در Docker پس از نصب، daemon را متوقف و غیرفعال باقی می‌گذارند. به همین دلیل است که این مرحله در صفحه CentOS مربوط به Docker وجود دارد، اما در صفحه Ubuntu دیده نمی‌شود؛ چرا که در Ubuntu، بسته deb سرویس را به‌طور خودکار برای شما اجرا می‌کند. اگر از enable صرف‌نظر کنید، Docker فقط تا ریبوت بعدی اجرا می‌شود و پس از آن متوقف شده و تمام کانتینرها را نیز با خود از دسترس خارج می‌کند.

دستور systemctl status باید خروجی Active: active (running) را نمایش دهد. کانتینر hello-world باید عبارت This message shows that your installation appears to be working correctly. را چاپ کرده و خارج شود. اگر به‌جای آن خطای دسترسی (permission error) روی /var/run/docker.sock دریافت کردید، به این معناست که sudo را انجام نداده‌اید؛ بخشی که در ادامه در مورد گروه docker توضیح داده شده، این مشکل را برطرف می‌کند.

پلاگین compose را به‌صورت جداگانه بررسی کنید، زیرا این یک بسته متفاوت است و ممکن است در حالی که موتور اصلی به‌درستی کار می‌کند، این پلاگین نصب نشده باشد:

docker compose version

یک خروجی سالم شبیه به Docker Compose version v2.x.x است. بازگرداندن سرویس‌ها پس از ریبوت، موضوعی جدا از فعال‌سازی daemon است و سیاست‌های restart تعیین می‌کنند که آیا سرویس‌های Compose هنگام بوت بالا بیایند یا خیر.

چرا bind mount خطای permission denied می‌دهد؟

سیستم‌عامل‌های Rocky Linux و AlmaLinux به‌صورت پیش‌فرض SELinux (Security-Enhanced Linux) را در حالت enforcing اجرا می‌کنند. این موضوع را با getenforce تأیید کنید که خروجی آن Enforcing است.

کانتینرهای Docker تحت نوع SELinux با نام container_t اجرا می‌شوند و این نوع فقط اجازه خواندن و نوشتن فایل‌هایی را دارد که با برچسب container_file_t مشخص شده‌اند. دایرکتوری که شما روی میزبان (host) ایجاد کرده‌اید، برچسب مسیر والد خود را می‌گیرد که container_file_t نیست. با وجود اینکه مالک، گروه و دسترسی‌ها از دید میزبان صحیح به نظر می‌رسند، کانتینر اجازه دسترسی ندارد. این وضعیت را با سه دستور زیر بازتولید کنید:

sudo mkdir -p /srv/site
echo hello | sudo tee /srv/site/index.html
sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro nginx:alpine cat /usr/share/nginx/html/index.html

کانتینر این پیام را چاپ می‌کند:

cat: can't open '/usr/share/nginx/html/index.html': Permission denied

دو دستور علت را به شما نشان می‌دهند. ls -ldZ /srv/site برچسب را چاپ می‌کند که برای مسیری زیر /srv برابر با system_u:object_r:var_t:s0 است، نه container_file_t. سپس sudo ausearch -m avc -ts recent رکورد audit هسته را چاپ می‌کند که شامل avc: denied { read }، یک فیلد scontext= با نام container_t و یک فیلد tcontext= با نام برچسبی است که به‌تازگی روی دایرکتوری دیدید. عدم تطابق بین این دو فیلد، کل ماجراست.

راه‌حل، استفاده از یک پسوند در آرگومان volume است. Docker مسیر را برای شما بازنشانی (relabel) می‌کند:

sudo docker run --rm -v /srv/site:/usr/share/nginx/html:ro,z nginx:alpine cat /usr/share/nginx/html/index.html

:z با حروف کوچک، محتوا را به‌عنوان shared بازنشانی می‌کند تا چندین کانتینر بتوانند از یک دایرکتوری استفاده کنند. :Z با حروف بزرگ، آن را به‌عنوان private و غیرمشترک بازنشانی می‌کند که به یک کانتینر محدود است و کانتینر دوم در صورت خواندن همان مسیر، با خطا مواجه می‌شود. برای هر چیزی که یک sidecar یا کانتینر پشتیبان (backup) نیز با آن در تماس است، از :z استفاده کنید. برای دایرکتوری دیتابیس که متعلق به یک کانتینر است، از :Z استفاده کنید.

مستندات Docker هشداری دارد که تکرار آن ضروری است، زیرا بازنشانی به‌صورت بازگشتی (recursive) انجام می‌شود. Bind-mount کردن یک دایرکتوری سیستمی مانند /home یا /usr با :Z، «ماشین میزبان شما را غیرقابل استفاده می‌کند و ممکن است مجبور شوید فایل‌های سیستم میزبان را به‌صورت دستی بازنشانی کنید». این پسوندها را فقط به دایرکتوری‌هایی که خودتان برای کانتینر ایجاد کرده‌اید اختصاص دهید، هرگز از آن‌ها برای مسیرهای سیستمی استفاده نکنید.

در Compose، پسوند در همان رشته قرار می‌گیرد:

services:
  web:
    image: nginx:alpine
    volumes:
      - /srv/site:/usr/share/nginx/html:ro,z

دو محدودیت وجود دارد که به‌راحتی ممکن است با آن‌ها برخورد کنید. فلگ --mount اصلاً نمی‌تواند برچسب SELinux تنظیم کند، بنابراین وقتی به آن نیاز دارید از -v استفاده کنید. Volumeهای نام‌گذاری شده (Named volumes) نیازی به پسوند ندارند، زیرا Docker دایرکتوری‌هایی را که تحت /var/lib/docker/volumes ایجاد می‌کند، خودش برچسب‌گذاری می‌کند.

SELinux را خاموش نکنید. از sudo setenforce 0 فقط به‌عنوان یک تست یک‌دقیقه‌ای استفاده کنید: اگر کانتینر پس از آن کار کرد، مشکل از برچسب است و :z پاسخ آن است. بلافاصله آن را با sudo setenforce 1 به حالت قبل برگردانید. در Enterprise Linux، خطای permission denied در یک bind mount دو علت مجزا دارد که از داخل کانتینر یکسان به نظر می‌رسند. یکی برچسب SELinux است. دیگری مالکیت عددی کاربر و گروه است که متغیرهای PUID و PGID برای حل آن وجود دارند. ls -lnZ حالت، مالک عددی و برچسب را در یک خط به شما نشان می‌دهد تا بتوانید تشخیص دهید با کدام‌یک در حال مبارزه هستید.

چرا یک پورت منتشرشده (published) در حالی که firewalld آن را بسته نشان می‌دهد، در دسترس است؟

Firewalld فایروال پیش‌فرض در Rocky Linux و AlmaLinux است. با دستور sudo systemctl is-active firewalld بررسی کنید که آیا در حال اجرا است یا خیر. اگر هنوز آن را روی این سیستم پیکربندی نکرده‌اید، ابتدا باز کردن SSH و یک پورت وب با firewalld را انجام دهید، زیرا نکتهٔ غافلگیرکنندهٔ زیر تنها زمانی معنا پیدا می‌کند که یک مجموعه قوانین (ruleset) فعال برای مقایسه داشته باشید. اکنون یک پورت را منتشر کنید و ببینید firewalld چه پورت‌هایی را باز می‌داند:

sudo docker run -d --name web -p 8080:80 nginx:alpine
sudo firewall-cmd --list-ports

دستور firewall-cmd یک خط خالی چاپ می‌کند. از یک ماشین دیگر، دستور curl -I http://YOUR_SERVER_IP:8080/ مقدار HTTP/1.1 200 OK را برمی‌گرداند. پورت برای اینترنت باز است اما فایروال شما هیچ موردی را گزارش نمی‌کند.

علت این موضوع، مسیری است که بسته (packet) طی می‌کند. قوانین zone در firewalld ترافیکِ مقصدِ خودِ میزبان (host) را فیلتر می‌کنند. یک پورت منتشرشده، خطاب به میزبان نیست: Docker یک قانون NAT (ترجمه آدرس شبکه) مقصد نصب می‌کند که پیش از رسیدن بسته به مسیر ورودی میزبان، مقصد را به آدرس کانتینر تغییر می‌دهد؛ بنابراین هسته (kernel) به جای تحویل محلی، بسته را فوروارد (forward) می‌کند. سپس Docker رابط‌های bridge خود را در یک zone از firewalld به نام docker قرار می‌دهد که target آن ACCEPT است و یک سیاست فوروارد به نام docker-forwarding اضافه می‌کند که اجازه فوروارد از هر zone به zone docker را می‌دهد. قوانین zone شما هرگز این بسته را نمی‌بینند.

تمیزترین راه حل، نیازی به قانون فایروال ندارد. سمت میزبانِ پورت منتشرشده را به loopback متصل کنید و یک reverse proxy در مقابل آن قرار دهید:

sudo docker rm -f web
sudo docker run -d --name web -p 127.0.0.1:8080:80 nginx:alpine
curl -I http://127.0.0.1:8080/

دستور محلی curl مقدار HTTP/1.1 200 OK را برمی‌گرداند و همان درخواست از ماشین دیگر دیگر متصل نمی‌شود. هر چیزی که در آرگومان -p آدرس میزبان نداشته باشد، روی تمام رابط‌ها منتشر می‌شود؛ بنابراین یک -p 8080:80 خالی را به عنوان تصمیمی برای عمومی کردن آن سرویس در نظر بگیرید.

زمانی که نیاز دارید یک سرویس فقط از برخی آدرس‌ها در دسترس باشد و از برخی دیگر نه، Docker یک chain برای شما رزرو می‌کند. DOCKER-USER پیش از قوانین accept خودِ Docker پردازش می‌شود، بنابراین قانونی که در آنجا قرار می‌دهید پس از راه‌اندازی مجدد Docker و بازنویسی chainها توسط آن، باقی می‌ماند:

sudo iptables -I DOCKER-USER -i enp1s0 ! -s 203.0.113.10 -j DROP
sudo iptables -S DOCKER-USER

نام رابط را از ip route show default بگیرید و فرض را بر eth0 نگذارید، زیرا ایمیج‌های فعلی EL از نام‌هایی مانند enp1s0 یا ens3 استفاده می‌کنند. در Rocky و AlmaLinux، دستور iptables یک لایه سازگاری روی nftables است و chainهای Docker از طریق آن قابل مشاهده هستند. قوانینی که به این روش اضافه می‌شوند پس از reboot از بین می‌روند مگر اینکه آن‌ها را ذخیره کنید، بنابراین پس از اطمینان از عملکرد آن‌ها، آن‌ها را در یک systemd unit بنویسید.

Docker Engine 28.0 که در سال 2025 منتشر شد، یک حفره مشابه را بست: دسترسی مستقیم مسیریابی‌شده به پورت‌های کانتینری که هرگز منتشر نشده‌اند، اکنون در chain DOCKER مسدود شده است. آن تغییر بر پورت‌های منتشرشده تأثیری ندارد، بنابراین تمام موارد بالا همچنان در نسخه‌های فعلی صدق می‌کند. یک عادت عملیاتی ارزش ایجاد کردن دارد: پس از هر sudo firewall-cmd --reload، یک پورت منتشرشده را دوباره تست کنید. اگر پاسخ‌دهی آن متوقف شد، sudo systemctl restart docker قوانین Docker را دوباره نصب می‌کند.

مدیران Ubuntu با ابزار متفاوتی با همین دیوار مواجه می‌شوند که دلیل آن نادیده گرفتن قوانین ufw توسط پورت‌های منتشرشده Docker است. مسیر NAT در هر دو مورد علت اصلی است. تنها فایروالی که در مقابل آن قرار دارد تغییر می‌کند.

افزودن یک کاربر غیر-root به گروه docker

تایپ کردن sudo پیش از هر دستور docker به‌مرور زمان خسته‌کننده می‌شود و گروه docker این نیاز را برطرف می‌کند:

sudo usermod -aG docker $USER
newgrp docker
docker run --rm hello-world

usermod -aG فایل /etc/group را ویرایش می‌کند، اما shell فعلی شما لیست گروه‌های خود را از قبل بارگذاری کرده است، بنابراین تغییرات تا زمانی که یک shell جدید باز نکنید اعمال نمی‌شوند. دستور newgrp docker یک shell جدید با دسترسی گروهی مربوطه باز می‌کند تا بتوانید بلافاصله آن را تست کنید. نشست‌های SSH جدید به‌طور خودکار این تغییر را دریافت می‌کنند.

دربارهٔ آنچه این گروه اعطا می‌کند شفاف باشید. عضویت در این گروه، دسترسی نوشتن به /var/run/docker.sock را فراهم می‌کند و هر چیزی که بتواند با آن socket ارتباط برقرار کند، می‌تواند از daemon بخواهد که یک container را اجرا کند که فایل‌سیستم میزبان را mount کرده است. یک دستور نشان می‌دهد که این یعنی چه:

docker run --rm -v /:/host alpine wc -l /host/etc/shadow

این دستور فایلی را می‌خواند که فقط root قادر به خواندن آن است، آن هم از حسابی که هیچ دسترسی sudo ندارد. مستندات رسمی Docker پس از نصب نیز همین موضوع را تأیید می‌کند: گروه docker امتیازاتی معادل root اعطا می‌کند. تنها در صورتی یک حساب کاربری را به این گروه اضافه کنید که مایل باشید به آن حساب دسترسی sudo بدهید. اگر در حال تنظیم حساب‌ها روی یک سرور جدید هستید، این تصمیم را در کنار سایر مراحل تنظیم کاربر با حداقل دسترسی روی VPS بگیرید، نه پس از آن.

Docker همچنین یک حالت rootless ارائه می‌دهد که daemon را به‌عنوان یک کاربر بدون امتیاز اجرا می‌کند. این یک مسیر نصب جداگانه است و نحوهٔ عملکرد درایورهای ذخیره‌سازی و پورت‌های زیر 1024 را تغییر می‌دهد، بنابراین آن را به‌عنوان یک پروژهٔ مستقل در نظر بگیرید، نه صرفاً یک flag که بعداً اضافه می‌کنید.

گام‌های بعدی

شما اکنون موتور Docker، افزونه compose، سرویسی که پس از reboot فعال می‌ماند و سه رفتار اختصاصی EL که در بالا مستند شده است را در اختیار دارید. گام بعدی، ایجاد یک compose.yaml برای هر سرویس است و ساختار فایل Compose فرمت فایل و دستوراتی که آن را مدیریت می‌کنند را پوشش می‌دهد. اگر این اولین میزبان container شماست، اجرای Docker روی VPS به پرسش‌های مربوط به تعیین ابعاد، فضای ذخیره‌سازی و بهداشت image که در این راهنما به آن‌ها پرداخته نشده است، پاسخ می‌دهد.

FAQ

آیا مخزن CentOS برای Docker روی Rocky Linux و AlmaLinux کار می‌کند؟

بله. با استفاده از https://download.docker.com/linux/centos/docker-ce.repo و dnf config-manager آن را اضافه کنید. baseurl در آن فایل حاوی $releasever است و Rocky Linux و AlmaLinux آن را به شماره نسخه اصلی (major version) بسط می‌دهند؛ بنابراین یک سیستم EL 9 به شاخه CentOS 9 و یک سیستم EL 10 به شاخه CentOS 10 متصل می‌شود. بسط صحیح را با sudo dnf repoinfo docker-ce-stable تایید کنید و خط Repo-baseurl را بخوانید. بروز Status code: 404 هنگام دریافت متادیتا توسط dnf به این معنی است که متغیر به یک نسخه فرعی (point release) بسط یافته است؛ ویرایش /etc/yum.repos.d/docker-ce.repo برای استفاده از شماره نسخه اصلی به‌تنهایی، این مشکل را حل می‌کند.

آیا می‌توان Docker و podman را روی یک سرور نصب کرد؟

مستندات Docker از podman و runc به‌عنوان بسته‌های ناسازگار یاد می‌کند و توصیه می‌کند پیش از نصب Docker Engine هر دو را حذف کنید. تداخل اصلی مربوط به بسته podman-docker است که مالک /usr/bin/docker محسوب می‌شود و هر دستور docker را به یک دستور podman تبدیل می‌کند. برای مشاهده اینکه کدام بسته مالک این مسیر است، rpm -qf "$(command -v docker)" را اجرا کنید. اگر خروجی با podman-docker شروع شود، یعنی podman پاسخگو است. نگهداری هر دو موتور در کنار هم توسط Docker پشتیبانی نمی‌شود، بنابراین در سرورهای حساس، یکی را انتخاب کنید.

چرا کانتینر من هنگام استفاده از bind mount با خطای permission denied مواجه می‌شود؟

SELinux به‌صورت پیش‌فرض روی Rocky Linux و AlmaLinux فعال است. کانتینرها با نوع container_t اجرا می‌شوند و فقط اجازه دسترسی به فایل‌هایی با برچسب container_file_t را دارند؛ بنابراین دایرکتوری که شما ایجاد کرده‌اید برچسب اشتباهی دارد و بدون توجه به مالک یا مجوزهای فایل، دسترسی رد می‌شود. این موضوع را با ls -ldZ روی مسیر میزبان و sudo ausearch -m avc -ts recent که avc: denied را با دو کانتکست نامنطبق چاپ می‌کند، تایید کنید. برای محتوای اشتراکی بین کانتینرها :z و برای محتوای اختصاصی یک کانتینر :Z را به آرگومان volume اضافه کنید. هرگز :Z را به /home یا /usr اشاره ندهید، زیرا تغییر برچسب به‌صورت بازگشتی (recursive) انجام شده و سیستم میزبان را مختل می‌کند.

آیا برای انتشار پورت کانتینر نیاز به باز کردن پورت در firewalld دارم؟

خیر، و دقیقاً همین موضوع مشکل‌ساز است. قانون NAT در Docker آدرس مقصد را پیش از رسیدن بسته به مسیر ورودی میزبان بازنویسی می‌کند، بنابراین قوانین zone در firewalld هرگز آن را بررسی نمی‌کنند. Docker همچنین پل‌های خود را در zoneای از firewalld به نام docker با هدف ACCEPT قرار می‌دهد. کانتینری که با -p 8080:80 شروع شده باشد از اینترنت در دسترس است، در حالی که sudo firewall-cmd --list-ports چیزی را نشان نمی‌دهد. اگر فقط میزبان باید به سرویس دسترسی داشته باشد، از -p 127.0.0.1:8080:80 برای انتشار روی یک آدرس خاص استفاده کنید یا قوانین فیلترینگ را در زنجیره DOCKER-USER وارد کنید، چرا که Docker این زنجیره را پیش از قوانین پذیرش (accept) خود پردازش می‌کند.

آیا افزودن کاربر به گروه docker امن است؟

این کار دسترسی root اعطا می‌کند. عضو گروه docker می‌تواند در /var/run/docker.sock بنویسد و docker run --rm -v /:/host alpine wc -l /host/etc/shadow سپس یک فایل محدود به root را از حسابی که هیچ دسترسی sudo ندارد، می‌خواند. مستندات پس از نصب Docker نیز به همین برابری اشاره دارد. فقط حساب‌هایی را اضافه کنید که از قبل به آن‌ها برای sudo اعتماد دارید و برای حساب‌های اشتراکی یا سرویس‌ها همچنان از sudo docker استفاده کنید. حالت Rootless جایگزینی برای زمانی است که به کانتینرها تحت یک کاربر بدون امتیاز نیاز دارید و این یک مسیر نصب جداگانه است، نه صرفاً یک تنظیم ساده.