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

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

Podman daemon ছাড়াই এবং ডিফল্টভাবে rootless চলে। VPS-এ এতে Compose file, Quadlet, port এবং volume ownership কীভাবে বদলায়, তা উদাহরণসহ জানুন।

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

Podman এবং Docker উভয়ই VPS-এ একই OCI (open container initiative) image চালায়। তাই কোন software চালাতে পারবেন, পার্থক্যটি তা নিয়ে নয়। পার্থক্যটি process model-এ। Docker এমন একটি root daemon চালায়, যা সব container-এর মালিকানা ধরে রাখে। `docker command-টি একটি ছোট client, যা কাজটি করার জন্য ওই daemon-কে অনুরোধ করে। Podman-এর কোনো daemon নেই। podman run` যে process এটি চালায়, তার child process হিসেবে container শুরু করে এবং আপনার নিজের unprivileged user-এর অধীনে এটি চালায়।

বাকি সব আচরণ এই একটি তথ্য থেকেই আসে। Auto-start daemon-এর পরিবর্তে systemd-এর দায়িত্ব হয়ে যায়। Volume ownership user namespace-এর মধ্য দিয়ে প্রয়োগ হয়। তাই host-এ `ls -l দিয়ে যে owner দেখেন, container যে owner দেখে তা একই নয়। Kernel setting পরিবর্তন না করা পর্যন্ত 1024-এর নিচের port bind হতে ব্যর্থ হয়। docker` CLI (command line interface) একটি wrapper-এর মাধ্যমে কাজ চালিয়ে যায়, যতক্ষণ না কোনো কাজ Docker socket ব্যবহার করতে চায়।

ডেমন নেই: container শুরু করলে আসলে কী চলে

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

Podman-এ এর সমতুল্য কোনো process নেই। একটি container শুরু করলে একটি conmon (container monitor) process পান। এই process container-এর main process ধরে রাখে এবং command চালানো user-এর মালিকানাধীন থাকে।

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-এ আপনার login user-এর নামে, root হিসেবে নয়, চলমান conmon দেখানো উচিত। curl-এর output হওয়া উচিত 200। কোনো central service container-এর মালিক না হওয়ায় sudo apt upgrade podman ইতিমধ্যে চলমান কিছু বন্ধ করে না। একটি container-এর monitor crash করলেও অন্য container-গুলো বন্ধ হয় না।

তবে daemon না থাকলে কিছু সুবিধাও থাকে না। reboot-এর পরে আপনার container-গুলো স্বয়ংক্রিয়ভাবে শুরু করার কেউ থাকে না। Docker-এর --restart=always হলো এমন একটি প্রতিশ্রুতি, যা daemon boot-এর সময় পূরণ করে। Podman এটি systemd দিয়ে প্রতিস্থাপন করে। নিচের quadlet section-এ সেটিই দেখানো হয়েছে।

Socket এই বিষয়টির অন্য অংশ। /var/run/docker.sock হলো root-এর মালিকানাধীন একটি API (application programming interface) endpoint। এতে write করতে পারে এমন যেকোনো process host filesystem mount করা একটি privileged container শুরু করতে পারে। কোনো user-কে docker group-এ যোগ করলে ধীরপথে ওই user-কে root access দেওয়া হয়। এ বিষয়ে প্রতিটি service account-কে তার প্রয়োজনীয় access দেওয়া অংশটি পাশাপাশি পড়া উচিত। Podman-এ অনুরোধ না করা পর্যন্ত কোনো socket প্রকাশ করা হয় না। প্রকাশিত socket-টি /run/user/<uid>/podman/podman.sock-এ একটি নির্দিষ্ট user-এর অধীনে থাকে।

Ubuntu 24.04-এ Podman ইনস্টল করুন এবং rootless সত্যিই কাজ করছে কি না নিশ্চিত করুন

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

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

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

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

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

Ubuntu-তে adduser দিয়ে তৈরি ব্যবহারকারী স্বয়ংক্রিয়ভাবে একটি range পায়। useradd -M দিয়ে বা কোনো configuration tool ব্যবহার করে তৈরি ব্যবহারকারী প্রায়ই এটি পায় না। তখন ব্যর্থতার বার্তায় বিষয়টি উল্লেখ থাকে:

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

একটি range বরাদ্দ করুন। এরপর নতুন mapping ব্যবহার করার জন্য ওই ব্যবহারকারীর storage reset করুন:

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

প্রথমবার ব্যবহারের সময় আরেকটি বিষয় মনে রাখতে হবে: Podman Docker Hub ধরে নেয় না। একটি সংক্ষিপ্ত image name /etc/containers/registries.conf-এর unqualified-search-registries থেকে resolve করা হয়। Terminal সংযুক্ত না থাকা script-এ pull করলে short-name resolution enforced but cannot prompt without a TTY দিয়ে ব্যর্থ হয়। প্রতিবার সম্পূর্ণ name লিখুন। nginx-এর পরিবর্তে docker.io/library/nginx:1.27 ব্যবহার করুন।

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

একটি rootless container user namespace-এর মধ্যে চলে। এটি kernel-এর এমন একটি সুবিধা, যা কোনো process-কে user ID-এর নিজস্ব private map দেয়। namespace-এর ভেতরে container-এর superuser-এর UID (user ID) 0। namespace-এর বাইরে, আপনার VPS-এ, একই process আপনার সাধারণ login user হিসেবে পরিচিত। container-এর root host-এর root নয়।

সুবিধাটির প্রকৃত পরিসর এটুকুই। কোনো image যদি root হিসেবে চলার ওপর নির্ভর করে, কোনো web application-এ remote code execution bug থাকে, অথবা কোনো escape-এর জন্য namespace-এর বাইরে UID 0 প্রয়োজন হয়, তাহলে সেসব ক্ষেত্রে process মেশিনের permissions-এর বদলে আপনার unprivileged user-এর permissions পায়। তবে rootless আপনাকে kernel bug থেকে সুরক্ষা দেয় না। এটি আপনার নিজের file-ও সুরক্ষিত রাখে না, কারণ escaped process আপনার user হিসেবেই চলে এবং আপনি যে file পড়তে পারেন, সেটিই পড়তে পারে।

Docker-ও rootless হিসেবে চলতে পারে। dockerd-rootless-setuptool.sh install প্রতি user-এর জন্য একটি daemon সেট আপ করে এবং ভালোভাবে কাজ করে। পার্থক্যটি default আচরণে। Podman ব্যবহার করলে কোনো অতিরিক্ত অনুরোধ ছাড়াই rootless mode পাওয়া যায়। তাই আপনার প্রথম ব্যর্থতা হয় এমন একটি container নিয়ে, যা port 80-এ bind করতে পারে না; এমন কোনো service নিয়ে নয়, যা অজান্তে দুই বছর root হিসেবে চলেছে।

আমার volume-এর ফাইলগুলোর মালিকানা UID 100999 কেন?

কারণ একই user namespace। Container-এর UID 0 আপনার host-এর UID-তে map হয়। Container-এর UID 1 আপনার subuid range-এর প্রথম ID-তে map হয়, এবং সেখান থেকে ক্রমানুসারে বাড়ে। Range যদি 100000 থেকে শুরু হয়, তাহলে container-এর UID 1000 host-এ 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"

Container-এ 1000 দেখায়। Host-এর listing-এ মালিক হিসেবে 100999 দেখায়, কারণ 100000 plus 1000 minus 1 = 100999। কিছু নষ্ট হয়নি। সাধারণ chown এটি ঠিক করবে না, কারণ namespace-এর বাইরে আপনার unprivileged user কোনো ফাইলের মালিকানা পরিবর্তন করতে পারে না।

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

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

Docker-এ যদি এই সমস্যার সম্মুখীন হয়ে থাকেন, তাহলে এটি একই সমস্যা, শুধু এক স্তর উপরে। অনেক image যে PUID এবং PGID variable প্রকাশ করে সেগুলো container-এর ভিতরের process যে UID ব্যবহার করবে তা নির্ধারণ করে। Rootless Podman-এ সেই UID আবার দ্বিতীয়বার map হয়। Rootless container-এর ভিতরের PUID=1000 ব্যবহার করলেও host-এ 100999 মালিকানার ফাইল তৈরি হয়। দ্বিতীয় mapping বিবেচনা করে সংখ্যাগুলো নির্বাচন করুন, অথবা data named volume-এ সরিয়ে এই বিষয়টি আর বিবেচনা করবেন না।

Mount সম্পর্কে আরও 2টি বিষয় মনে রাখুন। Fedora এবং RHEL-এর উদাহরণে দেখা :z এবং :Z flag হলো SELinux relabel option। Ubuntu-তে AppArmor ব্যবহৃত হয়, তাই সেখানে এগুলো কোনো কাজ করে না। Rootless Podman এমন কোনো host directory mount করতে পারে না, যা আপনার user পড়তে পারে না। এটিই প্রত্যাশিত আচরণ, কোনো ত্রুটি নয়।

rootless Podman কেন port 80 publish করতে অস্বীকার করে?

কারণ 1024-এর নিচের port-এ bind করতে এমন privilege প্রয়োজন, যা আপনার user-এর নেই। Error message-এ সমাধানটি উল্লেখ থাকে:

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

দুটি সমাধান কার্যকর। পুরো host-এর জন্য threshold কমিয়ে দিন:

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

শেষ command-এর output হিসেবে 80 দেখানো উচিত। এই setting কী করে তা স্পষ্টভাবে বুঝুন: এখন container চালানো user-টি নয়, মেশিনের যেকোনো user 80 এবং 443 bind করতে পারবে। শুধু একজন administrator-যুক্ত VPS-এ এটি গ্রহণযোগ্য সমঝোতা হতে পারে। অন্য user-দের account থাকা server-এ এটি গ্রহণযোগ্য নয়। অন্য সমাধান হলো 8080-এ publish করে সামনে একটি reverse proxy বসানো। যেখানেই certbot-এর মাধ্যমে nginx-এ certificate issue ও renew করা আপনার প্রয়োজন হবে।

Rootless publishing আপনার application যে তথ্য দেখতে পায়, সেটিও পরিবর্তন করে। Podman 4.x rootlesskit port handler-এর সঙ্গে ডিফল্টভাবে slirp4netns ব্যবহার করে। Forward করা connection-এ source address পুনর্লিখিত হয়। তাই access log-এ প্রতিটি visitor-এর address 10.0.2.100 হিসেবে নথিভুক্ত হয়। Podman 5.0-এ ডিফল্ট pasta করা হয়েছে। এতে প্রকৃত client address অক্ষুণ্ণ থাকে। 4.x-এ --network slirp4netns:port_handler=slirp4netns ব্যবহার করলে প্রকৃত source address ফিরিয়ে আনা যায়, তবে throughput কিছুটা কমে।

এখানে একটি ভালো দিক আছে। Rootless published port হলো একটি সাধারণ listening socket, যার মালিক একটি normal process। তাই firewall-এর input rule এই port-এর ক্ষেত্রেও প্রযোজ্য হয়। Docker port publish করার সময় NAT (network address translation) rule এবং নিজের forwarding accept rule লিখে। এ কারণেই আপনি যে ufw rule দিয়ে published Docker port block হবে ভেবেছিলেন, তা কাজ করে না। Rootful Podman একই ধরনের network plumbing ব্যবহার করে এবং একই সমস্যায় পড়ে। Rootless Podman এতে পড়ে না।

Podman-এর অধীনে কি আমার Docker Compose ফাইলগুলো এখনও কাজ করবে?

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

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

দ্বিতীয়টি হলো আসল Docker Compose ব্যবহার করা, যা প্রতি ব্যবহারকারীর socket-এর মাধ্যমে Podman-এর Docker-compatible 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-এ একই container তালিকাভুক্ত হওয়ার কথা, কারণ container-এর একটিই সেট রয়েছে। Name resolution-ও কাজ করে: Podman-এর default network backend, netavark, aardvark-dns চালায়। তাই user-defined network-এ থাকা container-গুলো নাম ব্যবহার করে একে অপরকে খুঁজে পায়।

তবে কিছু সীমাবদ্ধতা রয়েছে। /var/run/docker.sock mount করে এমন যেকোনো কিছুকে Podman socket ব্যবহার করার জন্য নির্দেশ করতে হবে, অথবা তা বাদ দিতে হবে। User namespace-এর অধীনে network_mode: host ভিন্নভাবে কাজ করে। condition: service_healthy-সহ depends_on podman-compose-এর বিভিন্ন version-এ সমানভাবে সমর্থিত নয়। restart: always নিজে থেকে reboot-এর পর টিকে থাকে না; পরের section-এ এর সমাধান দেখানো হয়েছে। একটি ফাইলে একটি multi-container stack বর্ণনা করার জন্য Compose এখনও কার্যকর পদ্ধতি, তবে Podman-এর অধীনে এটি একটি translation layer হিসেবে কাজ করে। কোনো stack বহু বছর চালানোর পরিকল্পনা থাকলে সেটিকে quadlet-এ রূপান্তর করুন এবং দুটি abstraction-এর বদলে একটি abstraction রক্ষণাবেক্ষণ করুন।

Pod: যে ধারণার কোনো সমাধান Docker-এ নেই

Pod হলো এমন container-গুলোর একটি group, যারা একই network namespace share করে। Podman সেই namespace চালু রাখার জন্য একটি ছোট infra container শুরু করে। এরপর সদস্য container-গুলো কোনো 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-এ তিনটি container-সহ pod Running দেখা যাওয়ার কথা। এর মধ্যে infra container-ও গণনা করা হয়। এখন web container 127.0.0.1:6379-এ Redis-এ পৌঁছায়, app-cache:6379-এ নয়। Shared namespace থেকে দুটি নিয়ম অনুসরণ করতে হয়: port pod-এ publish করতে হবে, কোনো member-এ নয়; এবং কোনো দুই member একই port-এ listen করতে পারবে না।

এটাই Kubernetes-এর model, এবং Podman এই model-কে গুরুত্ব দেয়। বর্তমানে চলমান configuration থেকে podman kube generate app > app.yaml একটি Kubernetes manifest লিখে; পুরোনো package-গুলোতে এর নাম podman generate kube লেখা থাকে। podman kube play app.yaml অন্য host-এ সেটি পুনরায় তৈরি করে। Quadlet-এ একটি .kube unit type আছে, যা এই ধরনের file-কে systemd service হিসেবে চালায়। Service group করার এটি সত্যিই ভিন্ন একটি পদ্ধতি। ভবিষ্যতে Kubernetes ব্যবহারের সম্ভাবনা থাকলে Podman বেছে নেওয়ার এটিই সবচেয়ে শক্তিশালী কারণ।

ডেমন ছাড়াই স্বয়ংক্রিয়ভাবে শুরু: quadlet unit

Quadlet হলো একটি systemd generator। এটি container বর্ণনা করা একটি সংক্ষিপ্ত file-কে boot-এর সময় প্রকৃত systemd service-এ রূপান্তর করে। rootless user-এর জন্য file-গুলো ~/.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 প্রায় খালি থাকতে পারে, কারণ section header-ই volume তৈরি করে:

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

service-এর নাম filename থেকে আসে: caddy.container হয়ে যায় caddy.servicesystemctl --user enable caddy চালাবেন না। Generated unit enable করা যায় না, এবং systemd উত্তর দেয় Failed to enable unit: Unit /run/user/1000/systemd/generator/caddy.service is transient or generated.[Install] section-ই boot-এর সময় container শুরু করে, আর file সম্পাদনা করার পরে unit পুনরায় তৈরি করে daemon-reload

এখন সেই setting, যা প্রায় সবাইকে সমস্যায় ফেলে:

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

Linger=yes প্রত্যাশা করুন। linger ছাড়া আপনার শেষ SSH connection বন্ধ হলে systemd পুরো user session বন্ধ করে দেয়। ফলে এর সঙ্গে সব rootless container-ও বন্ধ হয়ে যায় এবং boot-এর সময় কোনো container আর শুরু হয় না। আপনি logout করলে যে container বন্ধ হয়ে যায়, তার কারণ সবসময় এটাই।

সাধারণ service unit-এর প্রধান process যেহেতু container, তাই systemd-এর নিজস্ব control সরাসরি প্রযোজ্য হয়। [Service] section-এর MemoryMax= এবং CPUQuota= systemd দিয়ে নিয়ন্ত্রণ করা অন্য যেকোনো service-এর মতোই আচরণ করে। এর জন্য cgroup v2 (control group version 2) প্রয়োজন। Ubuntu 22.04 থেকে এটি default হিসেবে ব্যবহার করছে। podman info | grep -i cgroup দিয়ে নিশ্চিত করুন।

Update-এর জন্যও একটি matching mechanism আছে। AutoUpdate=registrysystemctl --user enable --now podman-auto-update.timer একসঙ্গে registry-তে একই tag-এর নতুন image আছে কি না পরীক্ষা করে, unit restart করে এবং নতুন container শুরু হতে ব্যর্থ হলে আগের image-এ rollback করে। কী পরিবর্তন হবে তা আগে দেখতে podman auto-update --dry-run চালান। পুরোনো podman generate systemd command এখনও আছে, তবে এটি deprecated। তাই নতুন কাজের জন্য quadlet লিখুন।

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

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

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

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

চলমান stack ধাপে ধাপে স্থানান্তর

  1. Container-এর মালিক হবে এমন unprivileged user তৈরি বা নির্বাচন করুন এবং নিশ্চিত করুন যে /etc/subuid-এ তার জন্য একটি range নির্ধারিত আছে।
  2. Registry থেকে আসা সব image fully qualified name ব্যবহার করে আবার pull করুন। Podman-এর নিজস্ব image store আছে; এটি Docker-এর image store পড়বে না।
  3. স্থানীয়ভাবে build করা image-গুলো docker save app:1.4 | podman load ব্যবহার করে স্থানান্তর করুন।
  4. Docker container বন্ধ করুন, প্রতিটি volume-এর বিষয়বস্তু /var/lib/docker/volumes/<name>/_data থেকে বাইরে copy করুন, তারপর podman unshare chown -R 1000:1000 <path> ব্যবহার করে ownership ঠিক করুন।
  5. Port ব্যবহারের সিদ্ধান্ত নিন: reverse proxy-এর পেছনে 1024-এর বেশি port publish করুন, অথবা net.ipv4.ip_unprivileged_port_start নির্ধারণ করুন।
  6. প্রতিটি container-এর জন্য একটি করে quadlet file লিখুন, systemctl --user daemon-reload চালান এবং প্রতিটি service start করুন।
  7. sudo loginctl enable-linger <user> চালান, VPS reboot করুন, আবার login করে পরীক্ষা করুন যে podman ps-এ সব service আবার তালিকাভুক্ত হয়েছে।

দুটি engine কোনো resource ভাগ করে না: image storage এবং network আলাদা। তাই স্থানান্তরের সময় উভয় engine একসঙ্গে চালাতে পারবেন। তাদের মধ্যে একমাত্র সংঘাত হতে পারে host port number নিয়ে। একটি service স্থানান্তর করে এক দিন monitor করুন, তারপর পরেরটি স্থানান্তর করুন।

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

আপনার stack যদি এমন compose file-এ থাকে, যেগুলো অন্যরাও রক্ষণাবেক্ষণ করে, অথবা Docker socket-এর সঙ্গে যোগাযোগকারী tooling-এর ওপর নির্ভর করে, তাহলে Docker ব্যবহার করতেই থাকুন। অন্য সবাই যে format লেখে, তার সঙ্গে compatibility একটি বাস্তব সুবিধা, এবং Docker-এ এই সুবিধা বেশি। কোনো দলের সব laptop-এ Docker চললে production-এও একই engine ব্যবহার করার মাধ্যমে তারা সরাসরি সুবিধা পায়।

VPS-এ আপনার নিয়ন্ত্রণাধীন অল্প কয়েকটি service চললে, অথবা প্রতিটি application-কে আলাদা unprivileged user-এর অধীনে চালাতে চাইলে Podman-এ পরিবর্তন করুন। এতে server-এ কোনো docker group রাখার প্রয়োজন হয় না। Distribution-এর সঙ্গে সামঞ্জস্যও গুরুত্বপূর্ণ। RHEL এবং তার rebuild-গুলো supported engine হিসেবে Podman release করে। তাই এসব system-এ Podman ব্যবহার করলে কম অপ্রত্যাশিত সমস্যা হয়। আপনি যদি ইতিমধ্যে অন্য সবকিছু systemd unit দিয়ে পরিচালনা করেন, তাহলে quadlet নতুন কোনো tool শেখার মতো মনে হবে না; বরং বাদ পড়া একটি অংশ ফিরে পাওয়ার মতো লাগবে।

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

আপনি যদি এখনও প্রথম container host তৈরি করে থাকেন, তাহলে নতুন VPS-এ Docker setup ও hardening-এর পদ্ধতি তুলনামূলকভাবে সংক্ষিপ্ত পথ। এই জ্ঞান পরে কোনো কাজেই অপচয় হবে না। উভয় engine-এ image ও volume একই object। তাই পরে পরিবর্তন করলে মূলত আপনার service কীভাবে পরিচালিত হবে সেটিই বদলাবে; অন্য প্রায় কিছুই বদলাবে না।

FAQ

Podman কি Docker-এর drop-in replacement?

আপনি যে command টাইপ করেন, তার দিক থেকে এটি অনেকটাই একই রকম। podman-docker install করলে /usr/bin/docker wrapper পাওয়া যায়, এবং run, ps, build, logsexec একইভাবে কাজ করে। তবে এটি daemon-এর replacement নয়। Swarm-এর কোনো সমতুল্য নেই, /var/run/docker.sock-এর সঙ্গে সংযোগকারী tool-গুলোকে per-user Podman socket ব্যবহারের জন্য নির্দেশ করতে হয়, এবং Docker দিয়ে pull করা image Podman-এ দেখা যায় না, কারণ দুটি আলাদা storage ব্যবহার করে।

SSH থেকে log out করলে আমার rootless Podman container বন্ধ হয়ে যায় কেন?

কারণ আপনার শেষ login session বন্ধ হলে systemd user session এবং এর সঙ্গে সব user service বন্ধ করে দেয়। sudo loginctl enable-linger <user> চালান। এরপর দেখুন loginctl show-user <user> --property=Linger চালালে Linger=yes প্রদর্শিত হয় কি না। Linger সক্রিয় session না থাকলেও ওই user-এর systemd instance চালু রাখে। এর ফলে reboot-এর পর container-গুলোও আবার start হয়।

আমার volume-এর file-গুলোর owner UID 100999 কেন?

Rootless Podman container-এর UID 0-কে আপনার host user-এর সঙ্গে map করে। এরপর container-এর UID 1 এবং পরের UID-গুলোকে আপনার subuid range-এর ওপর map করে। 100000 থেকে শুরু হওয়া range-এ container UID 1000 host-এ 100999 হয়ে যায়। podman unshare chown 1000:1000 /path/to/data ব্যবহার করে namespace-এর ভেতর থেকে এটি ঠিক করুন। প্রথমবার run করার সময় :U flag দিয়ে mount করুন। অথবা --userns=keep-id ব্যবহার করুন, যাতে container UID আপনার নিজের UID-এর সঙ্গে মিলে যায়।

Podman-এর সঙ্গে কি আমি docker-compose.yml ব্যবহার করতে পারি?

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

Rootless ব্যবহার করলে কি container সত্যিই বেশি নিরাপদ হয়?

এটি একটি নির্দিষ্ট ঝুঁকি দূর করে। কোনো process rootless container থেকে বেরিয়ে এলে সেটি root-এর পরিবর্তে আপনার unprivileged user-এর permission পায়। এই সুরক্ষা গুরুত্বপূর্ণ। এ কারণেই root-equivalent docker group-এর rootless Podman-এ কোনো সমতুল্য নেই। তবে এটি kernel vulnerability প্রতিরোধ করে না এবং আপনার নিজের user যে file পড়তে পারে, সেগুলোও সুরক্ষিত করে না। তাই যেকোনো server-এ যে বাকি hardening করবেন, সেগুলোও চালু রাখুন।