SSD Nodes Learn 🎉 VPS از $5.50/ماه
راهنماها Matt Connorتوسط Matt Connor · به‌روزرسانی شده 2026-08-13

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

نصب اصولی Docker Engine روی Rocky Linux و AlmaLinux با dnf. این راهنما مشکل تداخل Podman با دستور docker و خطاهای SELinux در bind mount را برای همیشه حل می‌کند.

نصب Docker روی Rocky Linux و AlmaLinux

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

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

از اسکریپت راحتی (convenience script) موجود در get.docker.com استفاده نکنید. مستندات خود Docker توصیه می‌کند که از این روش برای محیط‌های عملیاتی (production) استفاده نشود. این اسکریپت بدون پرسش، پیکربندی مخازن شما را بازنویسی می‌کند و برای ارتقا نیز نمی‌توان آن را به‌صورت ایمن دوباره اجرا کرد. افزودن دستی مخزن باعث می‌شود که dnf upgrade با Docker مانند سایر بسته‌های موجود در سیستم رفتار کند.

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

توزیع‌های Rocky Linux و AlmaLinux به‌صورت پیش‌فرض podman را در مخازن خود دارند و بسیاری از ایمیج‌های VPS آن را برای شما نصب می‌کنند. برخی از ایمیج‌ها فراتر رفته و podman-docker را نصب می‌کنند که یک اسکریپت shell در مسیر /usr/bin/docker قرار می‌دهد و podman را فراخوانی می‌کند. در نتیجه، هر دستور 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 را اجرا می‌کند و انتخاب معقولی است. اگر آن را می‌خواهید، همین‌جا متوقف شوید. اگر 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 با آن تطبیق داده می‌شوند. در بررسی انجام‌شده در اوت 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 این مقدار را روی شماره نسخه اصلی تنظیم می‌کنند، بنابراین در EL 9 مقدار 9 و در EL 10 مقدار 10 خواهد بود؛ به همین دلیل است که مخزن CentOS روی یک سیستم Rocky به‌درستی کار می‌کند. پیش از نصب، بسط متغیر را تأیید کنید:

sudo dnf repoinfo docker-ce-stable

خط Repo-baseurl را بخوانید. این خط باید به /9/x86_64/stable یا /10/x86_64/stable ختم شود. اگر نسخه release شما مقدار $releasever را روی یک نسخه جزئی مانند 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 دیده نمی‌شود؛ چرا که در آنجا فایل 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 به‌درستی کار می‌کند، این پلاگین نصب نباشد:

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 مجوزها، مالک عددی و برچسب را در یک خط به شما نشان می‌دهد تا متوجه شوید با کدام‌یک درگیر هستید.

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

برنامه firewalld فایروال پیش‌فرض در Rocky Linux و AlmaLinux است. با دستور sudo systemctl is-active firewalld بررسی کنید که آیا در حال اجرا است یا خیر. اکنون یک پورت را منتشر کنید و ببینید 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) را فیلتر می‌کنند. یک پورت منتشرشده (published port) خطاب به میزبان نیست: Docker یک قانون NAT (ترجمه آدرس شبکه) مقصد نصب می‌کند که پیش از رسیدن بسته به مسیر ورودی میزبان، مقصد را به آدرس کانتینر بازنویسی می‌کند؛ بنابراین هسته سیستم‌عامل به‌جای تحویل محلی بسته، آن را فوروارد (forward) می‌کند. سپس Docker رابط‌های bridge خود را در zoneای به نام 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 با همین دیوار از طریق ابزار متفاوتی روبرو می‌شوند که در چرا پورت‌های منتشرشده Docker قوانین ufw را نادیده می‌گیرند توضیح داده شده است. مسیر 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 فرمت فایل و دستوراتی که آن را مدیریت می‌کنند، پوشش می‌دهد. اگر این اولین میزبان کانتینر شماست، اجرای 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 همچنین پل‌های (bridge) خود را در 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 جایگزینی برای زمانی است که به کانتینرها تحت یک کاربر بدون امتیاز نیاز دارید و این یک مسیر نصب جداگانه است، نه یک تنظیم ساده.