SSD Nodes Learn 🎉 VPS $5.50/মাস থেকে
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-08-13

Rocky Linux বা AlmaLinux-এ Docker ইনস্টল করার নিয়ম

dnf ব্যবহার করে Rocky Linux বা AlmaLinux-এ Docker Engine ইনস্টল করুন। Podman-এর সাথে কমান্ডের দ্বন্দ্ব এড়ানো এবং SELinux bind mount সমস্যা সমাধানের কার্যকর উপায় এখানে দেখুন।

Rocky Linux এবং AlmaLinux-এ Docker ইনস্টল করা

Rocky Linux বা AlmaLinux-এ Docker ইনস্টল করতে হলে আপনাকে Docker-এর নিজস্ব dnf repository যোগ করতে হবে, compose plugin সহ engine ইনস্টল করতে হবে এবং তারপর service-টি enable করতে হবে। এই প্রক্রিয়াটি চারটি কমান্ডের মাধ্যমে সম্পন্ন হয় এবং উভয় ডিস্ট্রিবিউশনের ক্ষেত্রেই এটি অভিন্ন, কারণ উভয়ই Red Hat Enterprise Linux (RHEL)-এর রিবিল্ড এবং এদের প্যাকেজ লেআউট একই। CentOS Stream-এর ক্ষেত্রেও একই পদ্ধতি কাজ করে।

ইনস্টলেশন প্রক্রিয়াটি সংক্ষিপ্ত, তাই এই গাইডের বেশিরভাগ অংশ জুড়ে Enterprise Linux (EL) এবং Ubuntu-এর মধ্যে পার্থক্যগুলো আলোচনা করা হয়েছে। আপনার ইমেজে Podman হয়তো ইতিমধ্যে docker কমান্ডটি দখল করে থাকতে পারে। SELinux সঠিক লেবেল না থাকা পর্যন্ত bind-mounted ফাইলগুলোকে ব্লক করে রাখে। Firewalld Docker-এর প্রকাশিত পোর্টগুলোকে ফিল্টার করে না, তাই একটি কন্টেইনার পোর্ট ইন্টারনেটের জন্য উন্মুক্ত থাকতে পারে, অথচ firewall-cmd রিপোর্ট করবে যে কোনো পোর্টই খোলা নেই।

get.docker.com থেকে Docker-এর convenience script ব্যবহার করবেন না। Docker-এর নিজস্ব ডকুমেন্টেশনেই বলা হয়েছে যে এটি প্রোডাকশন এনভায়রনমেন্টের জন্য সুপারিশকৃত নয়। এটি কোনো অনুমতি ছাড়াই আপনার repository কনফিগারেশন পরিবর্তন করে ফেলে এবং আপগ্রেড করার জন্য এটি পুনরায় নিরাপদে চালানো যায় না। ম্যানুয়ালি repository যোগ করলে dnf upgrade Docker-কে সার্ভারের অন্য সব প্যাকেজের মতোই বিবেচনা করবে।

Podman কি ইতিমধ্যে docker কমান্ডের উত্তর দিচ্ছে?

Rocky Linux এবং AlmaLinux-এর ডিফল্ট রিপোজিটরিতে podman থাকে এবং অনেক VPS ইমেজে এটি আগে থেকেই ইনস্টল করা থাকে। কিছু ইমেজ আরও এক ধাপ এগিয়ে podman-docker ইনস্টল করে, যা /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 চান, তবে প্রথমে কনফ্লিক্টিং প্যাকেজগুলো সরিয়ে ফেলুন। RHEL-এর জন্য Docker যে তালিকাটি দিয়েছে তা নিচে দেওয়া হলো:

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 বা এর ওপর নির্ভরশীল অন্য কোনো টুলও মুছে যেতে পারে।

তাত্ত্বিকভাবে Docker-এর পাশাপাশি podman রাখা সম্ভব: শুধুমাত্র podman-docker সরিয়ে ফেলুন যাতে docker নামটি খালি হয়, এবং runc সরিয়ে ফেলুন, যা containerd.io প্যাকেজটি প্রতিস্থাপন করে। Docker-এর ডকুমেন্টেশন podman-কে একটি কনফ্লিক্টিং প্যাকেজ হিসেবে গণ্য করে, তাই Docker এই লেআউটটি সমর্থন করে না। যদি ইনস্টলেশন এখনো কনফ্লিক্ট দেখায়, তবে উপরের সম্পূর্ণ রিমুভাল লিস্টটি ব্যবহার করুন।

dnf config-manager ব্যবহার করে Docker-এর রিপোজিটরি যোগ করা

Docker এন্টারপ্রাইজ লিনাক্সের জন্য download.docker.com-এ RPM প্রকাশ করে। রিপোজিটরি ফাইলটি 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

dnf-এর 5 নম্বর ভার্সন থেকে --add-repo আর্গুমেন্টটি বাদ দেওয়া হয়েছে, তাই নতুন রিলিজগুলোতে দ্বিতীয় কমান্ডটি ব্যর্থ হয়। আপনার সিস্টেমে কোনটি আছে তা যাচাই করুন এবং সেই অনুযায়ী সঠিক কমান্ডটি বেছে নিন:

dnf --version

যদি এটি 5.x ভার্সন দেখায়, তবে সাব-কমান্ড ফরম্যাটটি ব্যবহার করুন:

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 আপনার রিলিজ প্যাকেজ থেকে সেই ভেরিয়েবলটিকে প্রসারিত (expand) করে। 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 দিয়ে শেষ হওয়া উচিত। যদি আপনার রিলিজ $releasever-কে 9.6-এর মতো কোনো পয়েন্ট ভার্সনে সেট করে, তবে মেটাডেটা আনার সময় dnf সেই URL-এর জন্য Status code: 404 রিপোর্ট করবে। এটি ঠিক করতে /etc/yum.repos.d/docker-ce.repo এডিট করুন এবং $releasever-কে শুধুমাত্র মেজর নম্বর দিয়ে প্রতিস্থাপন করুন।

ইঞ্জিন এবং কম্পোজ প্লাগইন ইনস্টল করা

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

এখানে পাঁচটি প্যাকেজ রয়েছে এবং প্রতিটি প্যাকেজের কাজ আলাদা। docker-ce হলো ডেমোন (daemon), dockerddocker-ce-cli হলো সেই docker কমান্ড যা আপনি টাইপ করেন। containerd.io হলো কন্টেইনার রানটাইম যা ডেমোন পরিচালনা করে। docker-buildx-plugin ইমেজ বিল্ড করে। docker-compose-plugin সাব-কমান্ড হিসেবে docker compose প্রদান করে।

এই প্যাকেজগুলো হাইফেনযুক্ত docker-compose বাইনারি ইনস্টল করে না। এটি ছিল Compose v1, যার মেয়াদ 2023 সালের জুলাই মাসে শেষ হয়েছে। হাইফেনসহ docker-compose কল করে এমন যেকোনো কিছুকে স্পেসসহ docker compose-এ আপডেট করতে হবে।

প্রথমবার ইনস্টল করার সময় Docker-এর সাইনিং কি (signing key) ইম্পোর্ট করার জন্য প্রক্রিয়াটি থামবে এবং আপনাকে এর ফিঙ্গারপ্রিন্ট দেখাবে। কি-টি আপনার যোগ করা রিপো ফাইলে থাকা gpgkey=https://download.docker.com/linux/centos/gpg থেকে আসে, তাই গ্রহণ করার আগে dnf যে ফিঙ্গারপ্রিন্টটি দেখাচ্ছে তা ওই URL-এর সাথে মিলিয়ে নিন।

একটি ব্যর্থতা প্রায়ই দেখা যায় যা উল্লেখ করা প্রয়োজন। যদি dnf রিপোর্ট করে যে containerd.io-এর জন্য container-selinux প্রয়োজন কিন্তু তা পাওয়া যাচ্ছে না, তবে বুঝতে হবে আপনার AppStream রিপোজিটরি নিষ্ক্রিয় করা আছে। dnf repolist চালান এবং নিশ্চিত করুন যে appstream তালিকায় আছে, কারণ EL 9 এবং EL 10-এ container-selinux এখান থেকেই আসে।

Docker চালু করুন এবং এটি চলছে কি না নিশ্চিত করুন

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

Docker-এর RPM প্যাকেজ ইনস্টল করার পর daemon বন্ধ এবং disabled অবস্থায় থাকে। এই কারণেই Docker-এর CentOS পৃষ্ঠায় এই ধাপটি দেওয়া হয়েছে, কিন্তু Ubuntu পৃষ্ঠায় নয়, কারণ সেখানে deb প্যাকেজটি আপনার জন্য সার্ভিসটি চালু করে দেয়। enable বাদ দিলে Docker পরবর্তী রিবুট পর্যন্ত চলবে, তারপর বন্ধ হয়ে যাবে এবং এর সাথে থাকা প্রতিটি কন্টেইনারও বন্ধ হয়ে যাবে।

systemctl status কমান্ডটি Active: active (running) প্রদর্শন করা উচিত। hello-world কন্টেইনারটি This message shows that your installation appears to be working correctly. প্রিন্ট করে বন্ধ হয়ে যাবে। যদি এর পরিবর্তে /var/run/docker.sock-এ কোনো পারমিশন এরর দেখায়, তবে আপনি sudo বাদ দিয়েছেন, যা নিচের docker group সেকশনে সমাধান করা হয়েছে।

Compose প্লাগইনটি আলাদাভাবে পরীক্ষা করুন, কারণ এটি একটি ভিন্ন প্যাকেজ এবং ইঞ্জিন ঠিক থাকলেও এটি অনুপস্থিত থাকতে পারে:

docker compose version

একটি সঠিক উত্তরের আউটপুট Docker Compose version v2.x.x-এর মতো দেখাবে। রিবুটের পর আপনার সার্ভিসগুলো ফিরে পাওয়া daemon চালু করার চেয়ে আলাদা একটি বিষয়, এবং restart policies নির্ধারণ করে যে রিবুটের পর Compose সার্ভিসগুলো আবার চালু হবে কি না

কেন bind mount-এ permission denied ত্রুটি দেখায়?

Rocky Linux এবং AlmaLinux ডিফল্টভাবে SELinux (Security-Enhanced Linux) enforcing মোডে চালায়। getenforce কমান্ড দিয়ে এটি নিশ্চিত করুন, যা Enforcing আউটপুট দেয়।

Docker কন্টেইনারগুলো container_t SELinux টাইপের অধীনে চলে এবং এই টাইপটি শুধুমাত্র container_file_t লেবেলযুক্ত ফাইলগুলোই পড়তে ও লিখতে পারে। হোস্ট মেশিনে আপনি যে ডিরেক্টরি তৈরি করেন, সেটি তার প্যারেন্ট পাথ থেকে লেবেল গ্রহণ করে, যা 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 কার্নেলের অডিট রেকর্ড দেখায়, যাতে avc: denied { read } থাকে, একটি scontext= ফিল্ডে container_t নাম থাকে এবং একটি tcontext= ফিল্ডে ডিরেক্টরিতে দেখা লেবেলটি থাকে। এই দুটি ফিল্ডের অমিলই হলো মূল সমস্যা।

এর সমাধান হলো volume আর্গুমেন্টের সাথে একটি সাফিক্স যোগ করা। Docker আপনার জন্য পাথটি পুনরায় লেবেল করে দেয়:

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 এবং unshared হিসেবে লেবেল করে, যা একটি নির্দিষ্ট কন্টেইনারের সাথে যুক্ত থাকে এবং অন্য কোনো কন্টেইনার একই পাথ পড়তে চাইলে তাকে বাধা দেওয়া হয়। সাইডকার বা ব্যাকআপ কন্টেইনারের ক্ষেত্রে :z ব্যবহার করুন। ডাটাবেস ডিরেক্টরির জন্য :Z ব্যবহার করুন যা একটি কন্টেইনারের মালিকানাধীন।

Docker-এর ডকুমেন্টেশনে একটি সতর্কবার্তা রয়েছে যা পুনরায় উল্লেখ করা প্রয়োজন, কারণ এই relabel প্রক্রিয়াটি রিকার্সিভ। /home বা /usr-এর মতো সিস্টেম ডিরেক্টরিগুলোকে :Z দিয়ে bind-mount করলে "আপনার হোস্ট মেশিন অকেজো হয়ে যেতে পারে এবং আপনাকে ম্যানুয়ালি হোস্ট মেশিনের ফাইলগুলো পুনরায় লেবেল করতে হতে পারে"। এই সাফিক্সগুলো শুধুমাত্র কন্টেইনারের জন্য আপনার তৈরি করা ডিরেক্টরিতে ব্যবহার করুন, কখনোই সিস্টেম পাথে নয়।

Compose-এর ক্ষেত্রে সাফিক্সটি একই স্ট্রিংয়ে যোগ করতে হয়:

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

দুটি সীমাবদ্ধতার কারণে প্রায়ই সমস্যা হয়। --mount ফ্ল্যাগটি কোনো SELinux লেবেল সেট করতে পারে না, তাই লেবেল প্রয়োজন হলে -v ব্যবহার করুন। Named volume-এর ক্ষেত্রে কোনো সাফিক্সের প্রয়োজন নেই, কারণ Docker নিজেই /var/lib/docker/volumes-এর অধীনে তৈরি ডিরেক্টরিগুলোকে লেবেল করে দেয়।

SELinux বন্ধ করবেন না। sudo setenforce 0 শুধুমাত্র এক মিনিটের পরীক্ষার জন্য ব্যবহার করুন: যদি কন্টেইনারটি কাজ করে, তবে সমস্যাটি লেবেলের এবং :z হলো এর সমাধান। পরীক্ষা শেষে অবিলম্বে sudo setenforce 1 দিয়ে এটি পুনরায় চালু করুন। Enterprise Linux-এ bind mount-এর ক্ষেত্রে permission denied ত্রুটির দুটি ভিন্ন কারণ থাকতে পারে যা কন্টেইনারের ভেতর থেকে একই রকম মনে হয়। একটি হলো SELinux লেবেল। অন্যটি হলো সাধারণ সংখ্যাসূচক ইউজার এবং গ্রুপ মালিকানা, যা PUID এবং PGID ভেরিয়েবলগুলো সমাধানের জন্য তৈরিls -lnZ কমান্ডটি মোড, সংখ্যাসূচক মালিক এবং লেবেল এক লাইনে দেখায়, যাতে আপনি বুঝতে পারেন আপনি কোনটির সাথে লড়াই করছেন।

firewalld বন্ধ দেখালেও কেন একটি published port অ্যাক্সেস করা যায়?

Rocky Linux এবং AlmaLinux-এ firewalld ডিফল্ট ফায়ারওয়াল হিসেবে থাকে। sudo systemctl is-active firewalld কমান্ডটি চালিয়ে এটি সচল আছে কি না তা নিশ্চিত করুন। এখন একটি পোর্ট publish করুন এবং 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 রেসপন্স পাওয়া যাবে। অর্থাৎ পোর্টটি ইন্টারনেটের জন্য উন্মুক্ত, অথচ আপনার ফায়ারওয়াল বলছে সেটি বন্ধ।

এর কারণ হলো প্যাকেটের যাত্রাপথ। Firewalld-এর zone rule শুধুমাত্র হোস্টের উদ্দেশ্যে আসা ট্রাফিক ফিল্টার করে। একটি published port সরাসরি হোস্টের উদ্দেশ্যে আসে না: Docker একটি destination NAT (network address translation) রুল তৈরি করে, যা প্যাকেটটি হোস্টের input path-এ পৌঁছানোর আগেই সেটিকে কন্টেইনারের ঠিকানায় রিডাইরেক্ট করে। ফলে কার্নেল প্যাকেটটিকে লোকালি ডেলিভার না করে সরাসরি ফরোয়ার্ড করে দেয়। Docker তার ব্রিজ ইন্টারফেসগুলোকে docker নামক একটি firewalld zone-এ রাখে, যার target হলো ACCEPT। এছাড়া এটি docker-forwarding নামক একটি ফরোয়ার্ডিং পলিসি যোগ করে, যা যেকোনো জোন থেকে docker জোনে ট্রাফিক ফরোয়ার্ড করার অনুমতি দেয়। আপনার জোনের রুলগুলো তাই এই প্যাকেট দেখতে পায় না।

সবচেয়ে পরিচ্ছন্ন সমাধান হলো কোনো ফায়ারওয়াল রুল ব্যবহার না করা। Publish করার সময় হোস্টের দিকটিকে loopback-এ bind করুন এবং সামনে একটি 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 আর্গুমেন্টে কোনো হোস্ট অ্যাড্রেস না থাকলে তা প্রতিটি ইন্টারফেসে publish হয়, তাই খালি -p 8080:80 ব্যবহার করা মানেই সার্ভিসটিকে পাবলিকলি উন্মুক্ত করা।

যখন আপনার কোনো সার্ভিস নির্দিষ্ট কিছু আইপি থেকে অ্যাক্সেসযোগ্য করার প্রয়োজন হয়, তখন Docker আপনার জন্য একটি চেইন বরাদ্দ রাখে। DOCKER-USER চেইনটি Docker-এর নিজস্ব accept রুলের আগেই প্রসেস হয়, তাই এখানে কোনো রুল যোগ করলে Docker রিস্টার্ট বা চেইন রিরাইট করলেও তা টিকে থাকে:

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

ইন্টারফেসের নামের জন্য eth0 ধরে না নিয়ে ip route show default থেকে নাম নিন, কারণ বর্তমান EL ইমেজগুলোতে enp1s0 বা ens3-এর মতো নাম ব্যবহার করা হয়। Rocky এবং AlmaLinux-এ iptables কমান্ডটি nftables-এর ওপর একটি কম্প্যাটিবিলিটি লেয়ার হিসেবে কাজ করে এবং Docker-এর চেইনগুলো এর মাধ্যমে দেখা যায়। এভাবে যোগ করা রুলগুলো রিবুট করলে মুছে যায়, তাই একবার নিশ্চিত হয়ে গেলে সেগুলোকে একটি systemd unit-এ লিখে রাখুন।

2025 সালে রিলিজ হওয়া Docker Engine 28.0 একটি নিরাপত্তা ত্রুটি বন্ধ করেছে: যেসব কন্টেইনার পোর্ট publish করা হয়নি, সেগুলোতে সরাসরি routed access এখন DOCKER চেইনে ব্লক করা থাকে। এই পরিবর্তনটি published পোর্টগুলোর ক্ষেত্রে প্রযোজ্য নয়, তাই বর্তমান ভার্সনেও উপরের নিয়মগুলো কার্যকর। একটি কাজের অভ্যাস গড়ে তোলা ভালো: যেকোনো sudo firewall-cmd --reload-এর পর একটি published পোর্ট পুনরায় পরীক্ষা করুন। যদি সেটি রেসপন্স করা বন্ধ করে দেয়, তবে sudo systemctl restart docker কমান্ডটি Docker-এর রুলগুলো পুনরায় ইনস্টল করবে।

Ubuntu অ্যাডমিনিস্ট্রেটররা ভিন্ন একটি টুলের মাধ্যমে একই সমস্যার সম্মুখীন হন, যা কেন published Docker পোর্টগুলো ufw রুল উপেক্ষা করে লিঙ্কে বিস্তারিত আলোচনা করা হয়েছে। উভয় ক্ষেত্রেই NAT পাথ হলো মূল কারণ, শুধু ফায়ারওয়ালের টুলটি ভিন্ন।

ডকার গ্রুপে একজন নন-রুট ইউজার যোগ করা

প্রতিটি docker কমান্ডের আগে sudo টাইপ করা বিরক্তিকর, আর ডকার গ্রুপ এই সমস্যার সমাধান করে:

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

usermod -aG কমান্ডটি /etc/group ফাইলটি এডিট করে, কিন্তু আপনার বর্তমান শেলের কাছে ইতিমধ্যে গ্রুপের তালিকা রয়েছে, তাই নতুন শেল না খোলা পর্যন্ত এই পরিবর্তন কার্যকর হবে না। newgrp docker কমান্ডটি নতুন একটি শেল চালু করে যাতে গ্রুপটি যুক্ত থাকে, ফলে আপনি তাৎক্ষণিকভাবে পরীক্ষা করতে পারেন। নতুন SSH সেশনগুলো নিজে থেকেই এই পরিবর্তন গ্রহণ করে।

এই গ্রুপটি কী ধরনের সুবিধা দেয় তা পরিষ্কারভাবে বোঝা জরুরি। এই গ্রুপের সদস্যপদ থাকলে /var/run/docker.sock-এ রাইট অ্যাক্সেস পাওয়া যায়। আর যে কোনো কিছু যা এই সকেটের সাথে যোগাযোগ করতে পারে, তা ডেমোনকে এমন একটি কন্টেইনার চালু করতে বলতে পারে যা হোস্ট ফাইলসিস্টেমকে মাউন্ট করে। একটি কমান্ডের মাধ্যমেই বোঝা যায় এর অর্থ কী:

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

এটি এমন একটি ফাইল পড়ে যা শুধুমাত্র রুট পড়তে পারে, অথচ এটি এমন একটি অ্যাকাউন্ট থেকে করা হচ্ছে যার কোনো sudo অধিকার নেই। ডকারের নিজস্ব পোস্ট-ইনস্টল ডকুমেন্টেশনেও একই কথা বলা হয়েছে: ডকার গ্রুপ রুট-এর সমতুল্য প্রিভিলেজ প্রদান করে। কোনো অ্যাকাউন্টে এই সুবিধা তখনই দিন যদি আপনি সেই অ্যাকাউন্টটিকে sudo ব্যবহারের অনুমতি দিতে প্রস্তুত থাকেন। আপনি যদি নতুন সার্ভারে অ্যাকাউন্ট সেটআপ করেন, তবে তা পরে না করে VPS-এ ন্যূনতম প্রিভিলেজসহ ইউজার সেটআপ করার সময় এই সিদ্ধান্ত নিন।

ডকারে একটি রুটলেস মোডও রয়েছে যা ডেমোনকে একজন আনপ্রিভিলেজড ইউজার হিসেবে চালায়। এটি একটি আলাদা ইনস্টলেশন পাথ এবং এটি স্টোরেজ ড্রাইভার ও 1024-এর নিচের পোর্টগুলোর আচরণ পরিবর্তন করে। তাই এটিকে পরে যোগ করা কোনো ফ্ল্যাগ হিসেবে না দেখে একটি আলাদা প্রজেক্ট হিসেবে পরিকল্পনা করুন।

পরবর্তী পদক্ষেপ

আপনার কাছে এখন ইঞ্জিন, compose প্লাগইন, রিবুট-পরবর্তী সময়ে সচল থাকে এমন একটি সার্ভিস এবং উপরে বর্ণিত EL-এর তিনটি সুনির্দিষ্ট আচরণ রয়েছে। পরবর্তী ধাপ হলো প্রতিটি সার্ভিসের জন্য একটি compose.yaml তৈরি করা, এবং Compose ফাইলের গঠন অংশে ফাইলের ফরম্যাট ও এটি পরিচালনার কমান্ডগুলো নিয়ে আলোচনা করা হয়েছে। যদি এটি আপনার প্রথম কন্টেইনার হোস্ট হয়, তবে VPS-এ Docker চালানো অংশে সাইজিং, স্টোরেজ এবং ইমেজ-হাইজিন সংক্রান্ত বিষয়গুলো নিয়ে বিস্তারিত আলোচনা করা হয়েছে যা এই গাইডে বাদ দেওয়া হয়েছে।

FAQ

Docker-এর CentOS রিপোজিটরি কি Rocky Linux এবং AlmaLinux-এ কাজ করে?

হ্যাঁ। https://download.docker.com/linux/centos/docker-ce.repo ব্যবহার করে dnf config-manager যোগ করুন। সেই ফাইলের baseurl-এ $releasever থাকে এবং Rocky Linux ও AlmaLinux এটিকে মেজর ভার্সন নম্বরে প্রসারিত করে। ফলে একটি EL 9 বক্স CentOS 9 ট্রিতে এবং একটি EL 10 বক্স CentOS 10 ট্রিতে রেজলভ হয়। sudo dnf repoinfo docker-ce-stable দিয়ে প্রসারণটি নিশ্চিত করুন এবং Repo-baseurl লাইনটি পড়ুন। dnf যখন মেটাডেটা ফেচ করে তখন Status code: 404 আসার অর্থ হলো ভেরিয়েবলটি একটি পয়েন্ট রিলিজে প্রসারিত হয়েছে; সেক্ষেত্রে /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 দেখায়?

Rocky Linux এবং AlmaLinux-এ ডিফল্টভাবে SELinux এনফোর্সিং মোডে থাকে। কন্টেইনারগুলো container_t টাইপ হিসেবে চলে এবং শুধুমাত্র container_file_t লেবেলযুক্ত ফাইলগুলোই অ্যাক্সেস করতে পারে। তাই আপনার তৈরি করা ডিরেক্টরির লেবেল ভুল হলে মালিকানা বা মোড যাই হোক না কেন, অ্যাক্সেস অস্বীকার করা হবে। হোস্ট পাথে ls -ldZ এবং sudo ausearch -m avc -ts recent চালিয়ে এটি নিশ্চিত করুন, যা দুটি অমিল কনটেক্সটসহ avc: denied প্রদর্শন করবে। কন্টেইনারগুলোর মধ্যে শেয়ার করা কন্টেন্টের জন্য ভলিউম আর্গুমেন্টে :z যোগ করুন, অথবা একটির জন্য ব্যক্তিগত কন্টেন্টের ক্ষেত্রে :Z ব্যবহার করুন। কখনোই :Z-কে /home বা /usr-এর দিকে নির্দেশ করবেন না, কারণ রিল্যাবেল প্রক্রিয়াটি রিকার্সিভ এবং এটি হোস্ট সিস্টেমকে অকেজো করে দিতে পারে।

কন্টেইনার পোর্ট পাবলিশ করার জন্য কি firewalld-এ পোর্ট খোলা প্রয়োজন?

না, এবং এটাই সমস্যা। Docker-এর NAT রুল প্যাকেট হোস্টের ইনপুট পাথে পৌঁছানোর আগেই গন্তব্য ঠিকানা পরিবর্তন করে ফেলে, তাই firewalld-এর জোন রুলগুলো এটি পরীক্ষা করতে পারে না। Docker তার ব্রিজগুলোকে docker নামক একটি firewalld জোনে রাখে যার টার্গেট ACCEPT-p 8080:80 দিয়ে শুরু করা একটি কন্টেইনার ইন্টারনেট থেকে অ্যাক্সেসযোগ্য, যদিও sudo firewall-cmd --list-ports কোনো আউটপুট দেয় না। শুধুমাত্র হোস্ট থেকে সার্ভিসটি অ্যাক্সেস করতে চাইলে -p 127.0.0.1:8080:80 ব্যবহার করে নির্দিষ্ট ঠিকানায় পাবলিশ করুন, অথবা DOCKER-USER চেইনে ফিল্টারিং রুল যুক্ত করুন, যা Docker তার নিজস্ব অ্যাকসেপ্ট রুলের আগেই প্রসেস করে।

আমার ইউজারকে docker গ্রুপে যোগ করা কি নিরাপদ?

এটি root অ্যাক্সেস প্রদান করে। docker গ্রুপের একজন সদস্য /var/run/docker.sock-এ লিখতে পারে এবং docker run --rm -v /:/host alpine wc -l /host/etc/shadow তখন sudo অধিকারহীন অ্যাকাউন্ট থেকে একটি রুট-অনলি ফাইল পড়তে পারে। Docker-এর পোস্ট-ইনস্টল ডকুমেন্টেশনেও এই সমতুল্যতার কথা উল্লেখ আছে। শুধুমাত্র সেই অ্যাকাউন্টগুলোকেই যোগ করুন যাদের আপনি ইতিমধ্যে sudo-এর জন্য বিশ্বাস করেন এবং শেয়ারড বা সার্ভিস অ্যাকাউন্টের ক্ষেত্রে sudo docker ব্যবহার করা চালিয়ে যান। আনপ্রিভিলেজড ইউজারের অধীনে কন্টেইনার চালানোর প্রয়োজন হলে Rootless মোড ব্যবহার করুন, এটি একটি আলাদা ইনস্টলেশন পাথ, কোনো সাধারণ সেটিং নয়।