SSD Nodes Learn Hosting plans →
নির্দেশিকা Matt Connorদ্বারা Matt Connor · আপডেট করা হয়েছে 2026-08-26

VPS-এ Podman বনাম Docker: মূল পার্থক্য কী?

Podman-এ কোনো daemon নেই এবং এটি rootless মোডে চলে। VPS-এ এই পরিবর্তনের ফলে compose ফাইল, quadlets, পোর্ট বাইন্ডিং এবং ভলিউম মালিকানার ক্ষেত্রে কী প্রভাব পড়ে তা জানুন।

Podman এবং Docker-এর মধ্যে প্রকৃত পার্থক্য কী

Podman এবং Docker উভয়ই একটি VPS-এ একই OCI (Open Container Initiative) ইমেজ চালাতে পারে, তাই কোন সফটওয়্যার চালাতে পারবেন তা নিয়ে চিন্তার কিছু নেই। মূল পার্থক্যটি হলো এদের প্রসেস মডেলে। Docker একটি root daemon চালায় যা প্রতিটি কন্টেইনার নিয়ন্ত্রণ করে এবং docker কমান্ডটি একটি ছোট ক্লায়েন্ট হিসেবে কাজ করে যা daemon-কে কাজ করার নির্দেশ দেয়। Podman-এর কোনো daemon নেই: podman run কন্টেইনারটিকে যেখান থেকে কল করা হয়েছে তার একটি চাইল্ড প্রসেস হিসেবে আপনার নিজস্ব unprivileged ব্যবহারকারীর অধীনে শুরু করে।

বাকি সবকিছু এই একটি বিষয়ের ওপর ভিত্তি করেই নির্ধারিত হয়। অটো-স্টার্টের দায়িত্ব daemon-এর পরিবর্তে systemd-এর ওপর বর্তায়। ভলিউমের মালিকানা একটি user namespace-এর মধ্য দিয়ে যায়, তাই হোস্ট মেশিনে ls -l দিয়ে আপনি যে মালিককে দেখেন, কন্টেইনারের ভেতরে তা ভিন্ন হতে পারে। আপনি কার্নেল সেটিং পরিবর্তন না করা পর্যন্ত 1024-এর নিচের পোর্টগুলো bind করতে বাধা দেবে। docker CLI (command line interface) একটি র‍্যাপারের মাধ্যমে কাজ চালিয়ে যায়, যতক্ষণ না কোনো কিছু Docker socket-এর প্রয়োজন হয়।

কোনো ডেমোন নেই: কন্টেইনার শুরু করলে আসলে কী চলে

একটি Docker host-এ, pstree -a কমান্ডটি চালালে dockerd-কে root হিসেবে, তার পাশে containerd-কে এবং প্রতিটি চলমান কন্টেইনারের জন্য একটি করে containerd-shim-runc-v2 দেখা যায়। আপনার অ্যাপ্লিকেশনটি সেই shim-এর একটি child process এবং shim-টি PID 1-এর একটি child process। কন্টেইনারটির সাথে সেই shell-এর কোনো সংযোগ থাকে না যা এটি শুরু করেছিল। ডেমোন বন্ধ করলে আপনি সার্ভারের প্রতিটি কন্টেইনারের নিয়ন্ত্রণ হারাবেন এবং ডিফল্ট live-restore সেটিং বন্ধ থাকলে, systemctl restart docker আপনার কন্টেইনারগুলোকেও রিস্টার্ট করে দেবে।

Podman-এ এর সমতুল্য কোনো প্রসেস নেই। একটি কন্টেইনার শুরু করলে আপনি একটি conmon (কন্টেইনার মনিটর) প্রসেস পাবেন যা কন্টেইনারের মূল প্রসেসটিকে ধরে রাখে এবং এর মালিক হন সেই ব্যবহারকারী যিনি কমান্ডটি চালিয়েছেন।

podman run -d --name web -p 8080:80 docker.io/library/caddy:2
ps -o user,pid,ppid,args -C conmon
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080

ps কমান্ডে আপনার লগইন ব্যবহারকারীর অধীনে চলমান conmon তালিকাভুক্ত হওয়া উচিত, root হিসেবে নয়, এবং curl কমান্ডটি 200 প্রিন্ট করা উচিত। যেহেতু কোনো কেন্দ্রীয় সার্ভিস কন্টেইনারটির মালিক নয়, তাই sudo apt upgrade podman কমান্ডটি চলমান কোনো কিছু বন্ধ করে না এবং একটি কন্টেইনারের মনিটর ক্র্যাশ করলে অন্যগুলো ক্ষতিগ্রস্ত হয় না।

এই ডেমোন না থাকার একটি অসুবিধা আছে। রিবুট হওয়ার পর আপনার কন্টেইনারগুলো নিজে থেকে শুরু হয় না। Docker-এর --restart=always হলো এমন একটি প্রতিশ্রুতি যা ডেমোন বুট হওয়ার সময় পূরণ করে, আর Podman-এর ক্ষেত্রে এর পরিবর্তে systemd ব্যবহার করা হয়, যার জন্য নিচের quadlet সেকশনটি তৈরি করা হয়েছে।

সকেট হলো এই বিষয়ের অন্য অর্ধেক। /var/run/docker.sock হলো root-এর মালিকানাধীন একটি API (application programming interface) এন্ডপয়েন্ট এবং যে কোনো প্রসেস যা এতে লিখতে পারে, সেটি একটি privileged কন্টেইনার শুরু করতে পারে যা host filesystem মাউন্ট করে। কোনো ব্যবহারকারীকে docker গ্রুপে যুক্ত করা মানে তাকে পরোক্ষভাবে root-এর ক্ষমতা দেওয়া, যা প্রতিটি সার্ভিস অ্যাকাউন্টের জন্য প্রয়োজনীয় অ্যাক্সেস নিশ্চিত করা-এর পাশাপাশি পড়া জরুরি। Podman নিজে থেকে কোনো সকেট উন্মুক্ত করে না যদি না আপনি তা চান, এবং আপনি যে সকেটটি পাবেন তা /run/user/<uid>/podman/podman.sock-এ একজন নির্দিষ্ট ব্যবহারকারীর মালিকানাধীন থাকে।

Ubuntu 24.04-এ Podman ইনস্টল করা এবং rootless মোড নিশ্চিত করা

sudo apt update
sudo apt install -y podman uidmap
podman --version
podman info | grep -i rootless

uidmap প্যাকেজটি newuidmap এবং newgidmap প্রদান করে। এগুলো হলো setuid হেল্পার যা একজন সাধারণ ব্যবহারকারীকে subordinate ID-এর একটি পরিসীমা ব্যবহারের অনুমতি দেয়; এগুলো ছাড়া rootless কন্টেইনার চালু হয় না। podman info কমান্ডটি চালালে rootless: true আউটপুট আসা উচিত।

Ubuntu 24.04-এ Podman 4.9 এবং Debian 13-এ Podman 5.x থাকে, যা আগস্ট 2026-এ যাচাই করা হয়েছে। এই সংস্করণগুলোর পার্থক্য গুরুত্বপূর্ণ, কারণ quadlet ফাইলগুলোর জন্য 4.4 বা তার নতুন সংস্করণ প্রয়োজন এবং .pod quadlet ফাইলগুলোর জন্য 5.0 সংস্করণ প্রয়োজন। আপস্ট্রিম ডকুমেন্টেশন থেকে কোনো উদাহরণ কপি করার আগে podman --version চালিয়ে সংস্করণ যাচাই করে নিন।

প্রতিটি rootless ব্যবহারকারীর জন্য একটি subordinate ID পরিসীমা প্রয়োজন:

grep "$USER" /etc/subuid /etc/subgid

Ubuntu-তে adduser দিয়ে তৈরি করা ব্যবহারকারী স্বয়ংক্রিয়ভাবে একটি পরিসীমা পেয়ে যান। useradd -M বা কোনো কনফিগারেশন টুলের মাধ্যমে তৈরি করা ব্যবহারকারীর ক্ষেত্রে সাধারণত এটি থাকে না, এবং ব্যর্থতার বার্তায় তা উল্লেখ থাকে:

Error: cannot find UID/GID for user deploy: no subuid ranges found for user "deploy" in /etc/subuid - check rootless mode in man pages.

একটি পরিসীমা বরাদ্দ করুন, তারপর সেই ব্যবহারকারীর স্টোরেজ রিসেট করুন যাতে নতুন ম্যাপিং কার্যকর হয়:

sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 deploy
podman system migrate

প্রথমবার চালানোর সময় আরেকটি চমক হতে পারে: Podman ডিফল্টভাবে Docker Hub ধরে নেয় না। একটি ছোট ইমেজের নাম /etc/containers/registries.conf-এর unqualified-search-registries-এর বিপরীতে সমাধান করা হয়, এবং টার্মিনাল ছাড়া কোনো স্ক্রিপ্টে এটি চালালে short-name resolution enforced but cannot prompt without a TTY ত্রুটির কারণে pull ব্যর্থ হয়। সবসময় ইমেজের পূর্ণ নাম লিখুন। nginx-এর পরিবর্তে docker.io/library/nginx:1.27 ব্যবহার করুন।

একটি রেন্টেড সার্ভারে rootless container আসলে কী সুবিধা দেয়

একটি rootless container একটি user namespace-এর ভেতরে চলে, যা কার্নেলের এমন একটি ফিচার যা একটি প্রসেসকে তার নিজস্ব ইউজার আইডি (UID)-এর ম্যাপ প্রদান করে। এই namespace-এর ভেতরে কন্টেইনারের সুপারইউজার হলো UID 0। কিন্তু বাইরে, আপনার VPS-এ, সেই একই প্রসেস আপনার সাধারণ লগইন ইউজার হিসেবে গণ্য হয়। কন্টেইনারের root মানেই হোস্টের root নয়।

এটিই এই ব্যবস্থার প্রকৃত সুবিধা। কোনো ইমেজ যদি root হিসেবে চলার জন্য জেদ করে, কোনো ওয়েব অ্যাপ্লিকেশনে যদি remote code execution বাগ থাকে, অথবা এমন কোনো escape যা বাইরে UID 0 হওয়ার ওপর নির্ভরশীল—এসব ক্ষেত্রেই প্রসেসটি মেশিনের পূর্ণ নিয়ন্ত্রণ পাওয়ার বদলে আপনার unprivileged ইউজারের অনুমতিতেই সীমাবদ্ধ থাকে। rootless মোড আপনাকে কার্নেল বাগ থেকে সুরক্ষা দেয় না, এবং এটি আপনার নিজের ফাইলগুলোকেও রক্ষা করে না, কারণ escape হওয়া প্রসেসটি আপনার ইউজার হিসেবেই চলে এবং আপনার পড়াযোগ্য যেকোনো ফাইল পড়তে পারে। আইসোলেশনের এককটি UID ম্যাপিংয়ের মতোই গুরুত্বপূর্ণ, যা একটি FreeBSD jail, যা একটি ছোট মেশিনের মতো আপনার পরিচালিত পুরো userland-কে ঘিরে রাখে-এর ক্ষেত্রে আরও স্পষ্টভাবে বোঝা যায়, যা কোনো রেজিস্ট্রি থেকে ডাউনলোড করা লেয়ারড ইমেজের চেয়ে আলাদা।

Docker-ও rootless মোডে চলতে পারে। dockerd-rootless-setuptool.sh install ব্যবহার করে প্রতি ইউজারের জন্য আলাদা daemon সেটআপ করা যায় এবং এটি বেশ কার্যকর। মূল পার্থক্য হলো ডিফল্ট সেটিংসের দিকে। Podman-এ আপনি চাইলেই rootless মোড পান, তাই আপনার প্রথম ব্যর্থতা হবে এমন একটি কন্টেইনার যা port 80-তে বাইন্ড হতে পারছে না, আর Docker-এর ক্ষেত্রে যা হতো—একটি সার্ভিস যা দুই বছর ধরে নীরবে root হিসেবে চলছে।

আমার ভলিউম ফাইলগুলোর মালিক UID 100999 কেন?

এটি সেই একই user namespace-এর কারণে ঘটে। কন্টেইনারের UID 0 আপনার হোস্টের UID-এর সাথে ম্যাপ হয়। কন্টেইনারের UID 1 আপনার subuid রেঞ্জের প্রথম ID-এর সাথে ম্যাপ হয় এবং সেখান থেকে ক্রমানুসারে বাড়তে থাকে। যদি রেঞ্জটি 100000 থেকে শুরু হয়, তবে কন্টেইনারের UID 1000 হোস্টের ক্ষেত্রে 100999 হিসেবে গণ্য হবে।

mkdir -p "$PWD/data"
podman run --rm --user 1000 -v "$PWD/data:/data" docker.io/library/alpine:3 sh -c 'id -u; touch /data/f'
ls -ln "$PWD/data"

কন্টেইনারটি 1000 প্রিন্ট করে। হোস্টের লিস্টিংয়ে মালিক হিসেবে 100999 দেখায়, কারণ 100000 এর সাথে 1000 যোগ করে 1 বিয়োগ করলে 100999 হয়। এতে কোনো সমস্যা নেই এবং সাধারণ chown কমান্ড দিয়ে এটি ঠিক করা যাবে না, কারণ আপনার unprivileged ইউজার namespace-এর বাইরে ফাইলের মালিকানা পরিবর্তন করতে পারে না।

সমাধানের চারটি উপায়:

  • podman unshare chown 1000:1000 "$PWD/data" কমান্ডটি একই user namespace-এর ভেতরে chown চালায়, যেখানে সংখ্যাগুলোর অর্থ কন্টেইনারের জন্য যা, তাই থাকে।
  • -v "$PWD/data:/data:U" কমান্ডটি Podman-কে সোর্স ডিরেক্টরির মালিকানা আপনার হয়ে ঠিক করে দিতে বলে। এটি নতুন কোনো ডিরেক্টরির ক্ষেত্রে ব্যবহার করুন, গুরুত্বপূর্ণ ডেটার ওপর নয়।
  • --userns=keep-id কমান্ডটি আপনার হোস্ট UID-কে কন্টেইনারের ভেতরের একই UID-এর সাথে ম্যাপ করে, ফলে নতুন ফাইলগুলোর মালিক আপনিই থাকেন।
  • -v appdata:/data-এর মতো একটি named volume ব্যবহার করলে এই প্রশ্নই আসে না, কারণ Podman আপনার নিজস্ব স্টোরেজের ভেতরেই সঠিক মালিকানা দিয়ে এটি তৈরি করে।

আপনি যদি Docker-এ এই সমস্যার সম্মুখীন হয়ে থাকেন, তবে এটি এক ধাপ উপরের একই সমস্যা। অনেক ইমেজ যে PUID এবং PGID ভেরিয়েবল ব্যবহার করে তা কন্টেইনারের ভেতরের প্রসেসের UID নির্ধারণ করে, এবং rootless Podman-এর ক্ষেত্রে সেই UID দ্বিতীয়বার ম্যাপ হয়। rootless কন্টেইনারের ভেতরে PUID=1000 কমান্ডটি চালালেও হোস্টের ফাইলগুলোর মালিক 100999-ই থেকে যায়। এই দ্বিতীয় ম্যাপিংয়ের কথা মাথায় রেখে সংখ্যাগুলো নির্বাচন করুন, অথবা ডেটা একটি named volume-এ সরিয়ে নিন এবং এই ঝামেলা থেকে মুক্তি পান।

মাউন্ট সংক্রান্ত আরও দুটি বিষয়। Fedora এবং RHEL-এর উদাহরণে যে :z এবং :Z ফ্ল্যাগগুলো দেখেন, সেগুলো হলো SELinux relabel অপশন, তাই Ubuntu-তে এগুলোর কোনো কাজ নেই। এছাড়া, rootless Podman এমন কোনো হোস্ট ডিরেক্টরি মাউন্ট করতে পারে না যা আপনার ইউজার পড়তে পারে না; এটি কোনো ত্রুটি নয়, বরং নিরাপত্তার খাতিরেই এমনটি করা হয়েছে।

rootless Podman কেন 80 নম্বর পোর্ট পাবলিশ করতে বাধা দেয়?

1024-এর নিচের কোনো পোর্টে বাইন্ড করার জন্য এমন বিশেষাধিকারের প্রয়োজন হয় যা আপনার ইউজারের নেই। এরর মেসেজেই সমাধানটি উল্লেখ থাকে:

Error: rootlessport cannot expose privileged port 80, you can add 'net.ipv4.ip_unprivileged_port_start=80' to /etc/sysctl.conf (currently 1024), or choose a larger port number (>= 1024): listen tcp 0.0.0.0:80: bind: permission denied

দুটি উপায়ে এর সমাধান করা যায়। পুরো হোস্টের জন্য থ্রেশহোল্ড কমিয়ে দিন:

echo 'net.ipv4.ip_unprivileged_port_start=80' | sudo tee /etc/sysctl.d/99-podman.conf
sudo sysctl --system
sysctl net.ipv4.ip_unprivileged_port_start

শেষ কমান্ডটি আউটপুট হিসেবে 80 প্রদর্শন করবে। এই সেটিংটি কী করে তা পরিষ্কারভাবে বুঝে নিন: এখন মেশিনের প্রতিটি ইউজার 80 এবং 443 পোর্টে বাইন্ড করতে পারবে, শুধু কন্টেইনার চালানো ইউজারই নয়। একজন অ্যাডমিন থাকা VPS-এর ক্ষেত্রে এটি গ্রহণযোগ্য। কিন্তু যেখানে অন্য ব্যবহারকারীদের অ্যাকাউন্ট আছে, সেখানে এটি করা উচিত নয়। অন্য সমাধানটি হলো 8080 পোর্টে পাবলিশ করা এবং সামনে একটি reverse proxy বসানো, যেখানে আপনি certbot দিয়ে nginx-এ সার্টিফিকেট ইস্যু ও রিনিউ করতে চাইবেন।

Rootless পাবলিশিং আপনার অ্যাপ্লিকেশনের দেখার দৃষ্টিভঙ্গিও বদলে দেয়। Podman 4.x ডিফল্টভাবে rootlesskit port handler-এর সাথে slirp4netns ব্যবহার করে, এবং ফরওয়ার্ড করা কানেকশনগুলো পরিবর্তিত সোর্স অ্যাড্রেস নিয়ে আসে, তাই access log-এ প্রতিটি ভিজিটরকে 10.0.2.100 হিসেবে দেখায়। Podman 5.0-এ ডিফল্ট হিসেবে pasta ব্যবহার করা হয়, যা আসল ক্লায়েন্ট অ্যাড্রেস বজায় রাখে। 4.x ভার্সনে, --network slirp4netns:port_handler=slirp4netns ব্যবহার করলে কিছুটা থ্রুপুট খরচ হলেও আসল সোর্স অ্যাড্রেস ফিরে পাওয়া যায়।

এখানে একটি ভালো দিক আছে। একটি rootless পাবলিশড পোর্ট হলো সাধারণ একটি লিসেনিং সকেট, যার মালিক একটি সাধারণ প্রসেস, তাই আপনার ফায়ারওয়ালের ইনপুট রুলগুলো এতে কার্যকর হয়। Docker পোর্ট পাবলিশ করার সময় NAT (network address translation) রুল এবং নিজস্ব ফরওয়ার্ডিং এক্সেপ্ট রুল লেখে, আর ঠিক এই কারণেই একটি পাবলিশড Docker পোর্ট আপনার সেট করা ufw রুলকে উপেক্ষা করে। Rootful Podman একই ধরনের মেকানিজম ব্যবহার করে এবং একই ফাঁদে পড়ে। কিন্তু rootless-এর ক্ষেত্রে এমনটি ঘটে না।

আমার Docker Compose ফাইলগুলো কি Podman-এ কাজ করবে?

বেশিরভাগ ক্ষেত্রেই করবে, তবে দুটি ভিন্ন উপায়ে। প্রথমটি হলো podman-compose, যা একটি আলাদা ইমপ্লিমেন্টেশন। এটি একই ফাইল পড়ে এবং Podman CLI পরিচালনা করে:

sudo apt install -y podman-compose
podman-compose up -d
podman ps

দ্বিতীয়টি হলো সরাসরি Docker Compose ব্যবহার করা, যা প্রতি-ব্যবহারকারী (per-user) সকেটের মাধ্যমে Podman-এর Docker-সামঞ্জস্যপূর্ণ API-এর সাথে যোগাযোগ করে:

systemctl --user enable --now podman.socket
export DOCKER_HOST="unix:///run/user/$(id -u)/podman/podman.sock"
docker compose up -d
podman ps

docker compose ps এবং podman ps কমান্ডগুলো একই কন্টেইনারের তালিকা দেখাবে, কারণ কন্টেইনারের সেট একটাই। নাম রেজোলিউশনও একইভাবে কাজ করে: Podman-এর ডিফল্ট নেটওয়ার্ক ব্যাকএন্ড, netavark, aardvark-dns চালায়, তাই ব্যবহারকারী-সংজ্ঞায়িত নেটওয়ার্কে থাকা কন্টেইনারগুলো একে অপরকে নাম দিয়ে খুঁজে পায়।

কিছু সীমাবদ্ধতা রয়েছে। যেসব ক্ষেত্রে /var/run/docker.sock মাউন্ট করা হয়, সেগুলোকে Podman সকেটের দিকে নির্দেশ করতে হবে অথবা বাদ দিতে হবে। ইউজার নেমস্পেসের অধীনে network_mode: host ভিন্নভাবে কাজ করে। condition: service_healthy সহ depends_on-এর সাপোর্ট podman-compose-এর বিভিন্ন ভার্সনে অসামঞ্জস্যপূর্ণ। restart: always নিজে থেকে রিবুট-পরবর্তী অবস্থায় টিকে থাকতে পারে না, যা পরবর্তী সেকশনে সমাধান করা হয়েছে। একটি মাল্টি-কন্টেইনার স্ট্যাককে একক ফাইলে বর্ণনা করার জন্য Compose এখনো একটি ভালো উপায়, এবং Podman-এর অধীনে এটি একটি ট্রান্সলেশন লেয়ার হিসেবে কাজ করে। যদি কোনো স্ট্যাক আপনি দীর্ঘ বছর ধরে রাখতে চান, তবে সেটিকে quadlets-এ রূপান্তর করুন এবং দুটি অ্যাবস্ট্রাকশনের পরিবর্তে একটি বজায় রাখুন।

Pods: এমন একটি ধারণা যার কোনো উত্তর Docker-এর কাছে নেই

একটি Pod হলো কন্টেইনারের একটি দল যারা একই network namespace শেয়ার করে। Podman একটি ছোট infra কন্টেইনার চালু করে সেই namespace-টিকে সচল রাখার জন্য, এবং এর সদস্যরা তখন কোনো user-defined network বা service discovery ছাড়াই 127.0.0.1-এ একে অপরের সাথে যোগাযোগ করতে পারে।

podman pod create --name app -p 8080:80
podman run -d --pod app --name app-cache docker.io/library/redis:7
podman run -d --pod app --name app-web docker.io/library/nginx:1.27
podman pod ps
podman ps --pod

podman pod ps কমান্ডটি infra কন্টেইনারসহ মোট তিনটি কন্টেইনার নিয়ে Pod-এর Running অবস্থা দেখাবে। এখন web কন্টেইনারটি Redis-কে app-cache:6379-এর পরিবর্তে 127.0.0.1:6379-এ খুঁজে পাবে। শেয়ার করা namespace থেকে দুটি নিয়ম উঠে আসে: পোর্টগুলো Pod-এর ওপর publish করতে হবে, কোনো সদস্য কন্টেইনারের ওপর নয়; এবং কোনো দুজন সদস্য একই পোর্টে listen করতে পারবে না।

এটিই Kubernetes মডেল, এবং Podman এই মডেলটিকেই অনুসরণ করে। podman kube generate app > app.yaml বর্তমানে চলমান কন্টেইনারগুলো থেকে একটি Kubernetes manifest তৈরি করে (পুরানো প্যাকেজে এটি podman generate kube নামে পরিচিত), এবং podman kube play app.yaml সেটিকে অন্য কোনো host-এ পুনরায় তৈরি করে। Quadlet-এর একটি .kube ইউনিট টাইপ আছে যা এই ধরনের ফাইলকে systemd service হিসেবে চালায়। এটি সার্ভিসগুলোকে গ্রুপ করার একটি সম্পূর্ণ ভিন্ন পদ্ধতি, এবং যদি ভবিষ্যতে আপনার Kubernetes ব্যবহারের কোনো পরিকল্পনা থাকে, তবে Podman বেছে নেওয়ার জন্য এটিই সবচেয়ে বড় কারণ।

ডিমনের সাহায্য ছাড়াই অটো-স্টার্ট: quadlet ইউনিট

Quadlet হলো একটি systemd জেনারেটর। এটি বুট হওয়ার সময় কন্টেইনারের বর্ণনা সম্বলিত একটি ছোট ফাইলকে প্রকৃত systemd সার্ভিসে রূপান্তর করে। ফাইলগুলো rootless ইউজারের জন্য ~/.config/containers/systemd/ ডিরেক্টরিতে অথবা root-এর জন্য /etc/containers/systemd/ ডিরেক্টরিতে রাখতে হয়।

~/.config/containers/systemd/caddy.container:

[Unit]
Description=Caddy web server
After=network-online.target

[Container]
Image=docker.io/library/caddy:2
ContainerName=caddy
PublishPort=8080:80
Volume=caddy-data.volume:/data
Environment=TZ=UTC
AutoUpdate=registry

[Service]
Restart=always
MemoryMax=512M

[Install]
WantedBy=default.target

~/.config/containers/systemd/caddy-data.volume ফাইলটি প্রায় খালি থাকতে পারে, কারণ সেকশন হেডার থেকেই ভলিউম তৈরি হয়:

[Volume]
systemctl --user daemon-reload
systemctl --user start caddy
systemctl --user status caddy
journalctl --user -u caddy -n 50

সার্ভিসের নামটি ফাইলের নাম থেকে আসে: caddy.container ফাইলটি caddy.service সার্ভিসে পরিণত হয়। systemctl --user enable caddy কমান্ডটি চালাবেন না। জেনারেট করা ইউনিটগুলো enable করা যায় না এবং systemd তখন Failed to enable unit: Unit /run/user/1000/systemd/generator/caddy.service is transient or generated. উত্তর দেয়। [Install] সেকশনটি বুট হওয়ার সময় কন্টেইনার চালু করে এবং ফাইলটি এডিট করার পর daemon-reload কমান্ডটি ইউনিটটিকে পুনরায় জেনারেট করে।

এখন সেই সেটিংসটি দেখুন যা প্রায় সবার ক্ষেত্রেই সমস্যার কারণ হয়:

sudo loginctl enable-linger deploy
loginctl show-user deploy --property=Linger

Linger=yes কমান্ডটি প্রত্যাশিত। linger সক্রিয় না থাকলে, আপনার শেষ SSH কানেকশন বন্ধ হওয়ার সাথে সাথে systemd পুরো ইউজার সেশনটি বন্ধ করে দেয়। ফলে প্রতিটি rootless কন্টেইনারও বন্ধ হয়ে যায় এবং বুট হওয়ার সময় সেগুলো আর ফিরে আসে না। লগ আউট করার পর কন্টেইনার অদৃশ্য হয়ে যাওয়ার মূল কারণ এটিই।

যেহেতু কন্টেইনারটি একটি সাধারণ সার্ভিস ইউনিটের প্রধান প্রসেস, তাই systemd-এর নিজস্ব নিয়ন্ত্রণ ব্যবস্থা সরাসরি এখানে প্রযোজ্য। [Service] সেকশনের MemoryMax= এবং CPUQuota= ঠিক সেভাবেই কাজ করে যেভাবে systemd দ্বারা নিয়ন্ত্রিত অন্য যেকোনো সার্ভিসের ক্ষেত্রে করে। এর জন্য cgroup v2 (control group version 2) প্রয়োজন, যা Ubuntu-তে 22.04 ভার্সন থেকে ডিফল্ট হিসেবে ব্যবহৃত হচ্ছে। podman info | grep -i cgroup কমান্ড দিয়ে এটি নিশ্চিত করুন।

আপডেটের জন্য একটি ম্যাচিং মেকানিজম রয়েছে। AutoUpdate=registry এর সাথে systemctl --user enable --now podman-auto-update.timer ব্যবহার করলে এটি একই ট্যাগের নতুন ইমেজ আছে কি না তা রেজিস্ট্রি থেকে চেক করে, ইউনিটটি রিস্টার্ট করে এবং নতুন কন্টেইনার চালু হতে ব্যর্থ হলে আগের ইমেজে রোলব্যাক করে। কী কী পরিবর্তন হবে তা দেখতে প্রথমে podman auto-update --dry-run কমান্ডটি চালান। পুরনো podman generate systemd কমান্ডটি এখনো বিদ্যমান থাকলেও তা এখন আর ব্যবহারের উপযোগী নয় (deprecated), তাই নতুন যেকোনো কিছুর জন্য quadlet ব্যবহার করুন।

যেখানে docker alias কাজ করে, এবং যেখানে করে না

sudo apt install -y podman-docker
sudo touch /etc/containers/nodocker
docker ps

podman-docker একটি /usr/bin/docker র‍্যাপার ইনস্টল করে যা Podman-কে কল করে। nodocker ফাইলটি না থাকলে, প্রতিটি কল প্রথমে Emulate Docker CLI using podman. Create /etc/containers/nodocker to quiet msg. প্রিন্ট করে। এই র‍্যাপারটি আপনার দৈনন্দিন ব্যবহৃত কমান্ডগুলোকে কভার করে: run, ps, logs, exec, build, pull, push, inspect, cp, volume, network।

যা কাজ করে না তার তালিকাটি ছোট, কিন্তু গুরুত্বপূর্ণ। Swarm mode-এর কোনো সমতুল্য ব্যবস্থা নেই, তাই Swarm stack-এর কোনো স্থান নেই। যে টুলগুলো Docker socket-এর সাথে যোগাযোগ করে, সেগুলোর জন্য Podman socket এক্সপোর্ট করতে হয় এবং কিছু টুল এখনো পার্থক্য ধরতে পারে; Traefik-এর Docker provider /run/user/<uid>/podman/podman.sock-এ নির্দেশ করলে কাজ করে, কিন্তু Watchtower-এর কোনো জায়গা নেই, কারণ podman auto-update সেই কাজটি করে। স্টোরেজ আলাদা, তাই Docker দিয়ে আগে পুল করা ইমেজগুলো Podman দেখতে পায় না, এবং একটি ব্যস্ত Docker host-এ podman images খালি অবস্থায় শুরু হয়।

চলমান স্ট্যাক মাইগ্রেট করার ধাপসমূহ

  1. কন্টেইনারগুলোর মালিকানা থাকবে এমন একটি unprivileged ব্যবহারকারী তৈরি করুন বা নির্বাচন করুন এবং নিশ্চিত করুন যে /etc/subuid-এ তার একটি রেঞ্জ বরাদ্দ আছে।
  2. রেজিস্ট্রি থেকে আসা যেকোনো ইমেজ সম্পূর্ণ কোয়ালিফাইড নাম ব্যবহার করে পুনরায় pull করুন। Podman-এর নিজস্ব ইমেজ স্টোর রয়েছে এবং এটি Docker-এর ইমেজ পড়তে পারে না।
  3. docker save app:1.4 | podman load ব্যবহার করে লোকালি তৈরি ইমেজগুলো স্থানান্তর করুন।
  4. Docker কন্টেইনারটি বন্ধ করুন, /var/lib/docker/volumes/<name>/_data থেকে প্রতিটি ভলিউমের বিষয়বস্তু কপি করুন এবং তারপর podman unshare chown -R 1000:1000 <path> ব্যবহার করে মালিকানা (ownership) ঠিক করুন।
  5. পোর্ট সংক্রান্ত বিষয়টি সমাধান করুন: একটি reverse proxy-এর পেছনে 1024-এর উপরের পোর্ট ব্যবহার করুন অথবা net.ipv4.ip_unprivileged_port_start সেট করুন।
  6. প্রতিটি কন্টেইনারের জন্য একটি করে quadlet ফাইল লিখুন, systemctl --user daemon-reload চালান এবং প্রতিটি সার্ভিস চালু করুন।
  7. sudo loginctl enable-linger <user> চালান, VPS রিবুট করুন, পুনরায় লগ ইন করুন এবং নিশ্চিত করুন যে podman ps-এ প্রতিটি সার্ভিস পুনরায় দেখা যাচ্ছে।

এই দুটি ইঞ্জিনের মধ্যে কোনো কিছুই শেয়ার করা হয় না: এদের ইমেজ স্টোরেজ এবং নেটওয়ার্ক আলাদা। তাই মাইগ্রেশনের সময় আপনি উভয়ই চালাতে পারবেন এবং এদের মধ্যে কেবল হোস্ট পোর্টের জন্য সংঘাত হতে পারে। একটি সার্ভিস স্থানান্তর করুন, একদিন পর্যবেক্ষণ করুন এবং তারপর পরবর্তীটি স্থানান্তর করুন।

Podman বনাম Docker: আপনার VPS-এ কোনটি ব্যবহার করবেন?

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

আপনার VPS-এ যদি হাতেগোনা কয়েকটি সার্ভিস থাকে যা আপনি পুরোপুরি নিয়ন্ত্রণ করেন, অথবা আপনি যদি প্রতিটি অ্যাপ্লিকেশনকে আলাদা unprivileged user-এর অধীনে চালাতে চান এবং বক্সে কোনো docker গ্রুপ রাখতে না চান, তবে Podman-এ চলে আসুন। ডিস্ট্রিবিউশন অ্যালাইনমেন্টও গুরুত্বপূর্ণ: RHEL এবং এর রিবিল্ডগুলোতে Podman-কে সমর্থিত ইঞ্জিন হিসেবে দেওয়া হয়, তাই ঐ সিস্টেমগুলোতে Podman ব্যবহার করলে ঝামেলা কম হয়। আপনি যদি তবুও ঐ হোস্টগুলোতে Docker ব্যবহার করতে চান, তবে Rocky Linux এবং AlmaLinux-এ dnf পদ্ধতি শুরু করার আগে podman-docker র‍্যাপারটি সরিয়ে ফেলতে হবে, যা সেখানে docker কমান্ডটির মালিকানা নিয়ে থাকে। আপনি যদি ইতিমধ্যে systemd ইউনিট দিয়ে সবকিছু সুপারভাইজ করেন, তবে quadlets আপনার কাছে নতুন কোনো টুল শেখার চেয়ে বরং একটি প্রয়োজনীয় সংযোজন বলে মনে হবে।

মাঝামাঝি একটি বিকল্পের কথা উল্লেখ করা যেতে পারে। Rootful Podman অনেকটা Docker-এর মতোই কাজ করে, র‍্যাপারের মাধ্যমে docker কমান্ডটি বজায় রাখে এবং সবসময় চালু থাকা ডেমনের প্রয়োজনীয়তা দূর করে। তবে এতে rootless সুবিধার অংশটি পাওয়া যায় না, যা আপনার নিরাপত্তার ক্ষেত্রে মূল পরিবর্তন আনে, তাই এটিকে একটি অন্তর্বর্তীকালীন ধাপ হিসেবে বিবেচনা করুন।

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

FAQ

Podman কি Docker-এর সরাসরি বিকল্প?

আপনি যে কমান্ডগুলো টাইপ করেন, তার ক্ষেত্রে এটি প্রায় কাছাকাছি। podman-docker ইনস্টল করলে আপনি একটি /usr/bin/docker র‍্যাপার পাবেন এবং run, ps, build, logs ও exec একইভাবে কাজ করবে। তবে এটি ডেমনের (daemon) কোনো বিকল্প নয়। Swarm-এর কোনো সমতুল্য ব্যবস্থা এতে নেই, যে টুলগুলো /var/run/docker.sock-এর সাথে সংযুক্ত হয় সেগুলোকে প্রতি-ব্যবহারকারী (per-user) Podman সকেটের দিকে নির্দেশ করতে হবে এবং Docker দ্বারা পুল করা ইমেজগুলো Podman-এর কাছে অদৃশ্য থাকে কারণ উভয়ই আলাদা স্টোরেজ ব্যবহার করে।

SSH থেকে লগ আউট করলে আমার rootless Podman কন্টেইনারগুলো কেন বন্ধ হয়ে যায়?

কারণ আপনার শেষ লগইন সেশন বন্ধ হওয়ার সাথে সাথে systemd ব্যবহারকারীর সেশন এবং সেই সাথে সমস্ত ব্যবহারকারী সার্ভিস বন্ধ করে দেয়। sudo loginctl enable-linger <user> কমান্ডটি চালান এবং তারপর নিশ্চিত করুন যে loginctl show-user <user> --property=Linger কমান্ডটি Linger=yes আউটপুট দিচ্ছে। Linger সক্রিয় থাকলে কোনো সক্রিয় সেশন ছাড়াই ব্যবহারকারীর systemd ইনস্ট্যান্স চলতে থাকে, যা রিবুট হওয়ার পরে কন্টেইনারগুলোকে পুনরায় চালু করতে সাহায্য করে।

আমার ভলিউমের ফাইলগুলোর মালিকানা কেন UID 100999 দেখাচ্ছে?

Rootless Podman কন্টেইনারের UID 0-কে আপনার হোস্ট ব্যবহারকারীর সাথে ম্যাপ করে এবং কন্টেইনারের UID 1 থেকে শুরু করে উপরের দিকে আপনার subuid রেঞ্জের সাথে ম্যাপ করে। যদি রেঞ্জটি 100000 থেকে শুরু হয়, তবে কন্টেইনারের UID 1000 হোস্ট মেশিনে 100999 হিসেবে দেখাবে। এটি ঠিক করতে নেমস্পেসের ভেতর থেকে podman unshare chown 1000:1000 /path/to/data ব্যবহার করুন, প্রথমবার চালানোর সময় :U ফ্ল্যাগ দিয়ে মাউন্ট করুন অথবা --userns=keep-id ব্যবহার করুন যাতে কন্টেইনারের UID আপনার নিজের UID-এর সাথে মিলে যায়।

আমি কি Podman-এর সাথে docker-compose.yml ব্যবহার চালিয়ে যেতে পারি?

হ্যাঁ, দুটি উপায়ে। podman-compose ফাইলটি পড়ে এবং সরাসরি Podman CLI নিয়ন্ত্রণ করে। অথবা systemctl --user enable --now podman.socket দিয়ে কম্প্যাটিবিলিটি সকেট সক্রিয় করুন, DOCKER_HOST=unix:///run/user/$(id -u)/podman/podman.sock সেট করুন এবং এর বিপরীতে আসল docker compose চালান। তবে network_mode: host-এর ক্ষেত্রে, যে সার্ভিসগুলো Docker সকেট মাউন্ট করে সেগুলোর ক্ষেত্রে এবং restart: always-এর ক্ষেত্রে কিছু সমস্যা হতে পারে, কারণ রিবুটের পরে টিকে থাকার জন্য এর একটি quadlet ইউনিট এবং linger প্রয়োজন।

Rootless কি সত্যিই কন্টেইনারকে আরও নিরাপদ করে?

এটি একটি নির্দিষ্ট ঝুঁকি দূর করে: যদি কোনো প্রসেস rootless কন্টেইনার থেকে বেরিয়ে আসে, তবে সেটি রুট (root) ব্যবহারকারীর পরিবর্তে আপনার সাধারণ ব্যবহারকারীর অনুমতি পাবে। এটি একটি গুরুত্বপূর্ণ সুবিধা এবং এই কারণেই rootless Podman-এ root-এর সমতুল্য docker গ্রুপের কোনো অস্তিত্ব নেই। এটি কার্নেল ভালনারেবিলিটি (kernel vulnerabilities) প্রতিরোধ করে না এবং আপনার নিজের ব্যবহারকারীর পড়া ফাইলগুলোকে রক্ষা করে না, তাই যেকোনো সার্ভারে আপনি যে ধরনের হার্ডেনিং (hardening) করেন, তা বজায় রাখুন।