Docker Compose বুটের সময় auto-start করার নিয়ম
reboot-এর পর Docker Compose পরিষেবা চালু রাখতে restart policy সেট করুন। on-failure কেন একবারের reboot টিকিয়ে রাখে না এবং কখন systemd unit দরকার, তা জানুন।
সংক্ষিপ্ত উত্তর
Docker Compose পরিষেবাগুলো বুটের সময় শুরু হয়, যখন একই সঙ্গে দুটি শর্ত পূরণ হয়। Docker daemon-কে system service হিসেবে enabled থাকতে হবে, এবং ফাইলের প্রতিটি পরিষেবায় unless-stopped অথবা always restart policy থাকতে হবে। প্রতিটি পরিষেবায় restart: unless-stopped যোগ করুন, একবার docker compose up -d চালান, এবং reboot-এর পর container-গুলো স্বয়ংক্রিয়ভাবে আবার চালু হবে। সাধারণ ক্ষেত্রে আর কিছু প্রয়োজন নেই।
শুধু তখনই systemd unit প্রয়োজন হয়, যখন ক্রম গুরুত্বপূর্ণ: যেমন কোনো stack এমন একটি mounted disk, VPN interface, অথবা network share-এর ওপর নির্ভর করে, যা Docker daemon চালুর সময় এখনও প্রস্তুত নয়। এই পরিস্থিতি বাস্তব, এবং এই guide-এর দ্বিতীয় অংশে তা আলোচনা করা হয়েছে। আপনি যদি এখনও service definition এবং volume সম্পর্কে ধারণা নেওয়ার পর্যায়ে থাকেন, তাহলে VPS-এ Docker Compose-এর প্রাথমিক বিষয়গুলো দিয়ে শুরু করে পরে ফিরে আসুন।
compose.yaml-এ পুনরারম্ভ নীতি সেট করা
নীতিটি প্রতিটি সার্ভিসের জন্য এক লাইনে নির্ধারণ করা হয়। কোনো গ্লোবাল সুইচ নেই। তাই কোনো সার্ভিসের নীতি সেট করতে ভুললে রিবুটের পরে সেটি বন্ধ থাকবে, আর স্ট্যাকের বাকি অংশ চালু হবে।
services:
app:
image: nginx:1.27
restart: unless-stopped
ports:
- "8080:80"
db:
image: postgres:16
restart: unless-stopped
environment:
POSTGRES_PASSWORD: change-me
volumes:
- dbdata:/var/lib/postgresql/data
volumes:
dbdata:এটি প্রয়োগ করুন। তারপর চলমান কনটেইনার থেকে নীতিটি পড়ে দেখুন:
docker compose up -d
docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app)এতে unless-stopped প্রদর্শিত হবে। যদি no প্রদর্শিত হয়, তাহলে ফাইলটি সম্পাদনা করা হয়েছে, কিন্তু কনটেইনারটি পুনরায় তৈরি করা হয়নি।
এটিই সবচেয়ে সাধারণ ব্যর্থতার কারণ। পুনরারম্ভ নীতি YAML ফাইলে নয়, কনটেইনারে সংরক্ষিত থাকে। ইতিমধ্যে থাকা কনটেইনারের ক্ষেত্রে compose.yaml সম্পাদনা করলে কোনো পরিবর্তন হয় না। docker compose restart ব্যবহার করেও সমাধান হবে না, কারণ এটি একই কনটেইনার অবজেক্ট বন্ধ করে আবার চালু করে, কিন্তু এর কনফিগারেশন পরিবর্তন করে না। শুধু docker compose up -d ফাইলটির সঙ্গে চলমান কনটেইনারগুলোর তুলনা করে, নীতি পরিবর্তিত হয়েছে তা শনাক্ত করে এবং কনটেইনারগুলো পুনরায় তৈরি করে।
কোনো কনটেইনার এখনই পুনরায় তৈরি করতে না চাইলে, সেটির নীতি সরাসরি পরিবর্তন করুন:
docker update --restart unless-stopped my-containerYAML ফাইলটিও সম্পাদনা করুন। docker update চলমান কনটেইনার পরিবর্তন করে, আর পরবর্তী docker compose up -d ফাইলটি পড়ে পুরোনো মান পুনরায় সেট করবে।
প্রতিটি restart মান আসলে যা করে
Docker চারটি মান নির্ধারণ করে। এই মানগুলোর পার্থক্য কেবল machine reboot হলে বা daemon restart হলে দেখা যায়।
noহল default। কোনো পরিস্থিতিতেই container স্বয়ংক্রিয়ভাবে restart হয় না।alwayscontainer বন্ধ হলেই সেটিকে restart করে। আপনি নিজে এটি বন্ধ করলেও, পরেরবার Docker daemon চালু হলে এটি আবার চালু হয়। এটি প্রায়ই অপ্রত্যাশিত: আপনি গত সপ্তাহে ইচ্ছাকৃতভাবে বন্ধ করা container reboot-এর পরে আবার চলছে।unless-stopped,always-এর মতো আচরণ করে। তবে নিজে বন্ধ করা container daemon restart-এর পরেও বন্ধ থাকে। যে service আপনি মাঝে মাঝে maintenance-এর জন্য বন্ধ করেন, তার জন্য এই মানটি ব্যবহার করুন।on-failurecontainer কেবল non-zero exit code নিয়ে বন্ধ হলে সেটিকে restart করে। আপনিrestart: on-failure:3-এর মতো retry-এর সংখ্যা সীমিত করতে পারেন।
যে stack server চালু থাকলেই চালু থাকা উচিত, তার জন্য unless-stopped সঠিক default। কোনো container যেন বন্ধ অবস্থায় পড়ে না থাকে, তা নিশ্চিত করতে চাইলে কেবল always বেছে নিন।
কেন restart: on-failure reboot-এর পর কার্যকর থাকে না
অনেকে on-failure বেছে নেন, কারণ এটি সতর্ক পছন্দ বলে মনে হয়। পরে দেখা যায়, প্রথম reboot-এর পর প্রতিটি container বন্ধ হয়ে আছে। কারণটি সংজ্ঞাতেই আছে। on-failure শুধুমাত্র একটি বিষয়ের প্রতিক্রিয়া জানায়: container process-এর error code সহ বন্ধ হয়ে যাওয়া।
reboot কোনো error নয়। host বন্ধ হওয়ার সময় systemd docker.service বন্ধ করে, এবং daemon ইচ্ছাকৃতভাবে প্রতিটি container বন্ধ করে। container ব্যর্থ হয়নি, তাই policy-এর প্রতিক্রিয়া জানানোর মতো কিছু নেই। host আবার চালু হলে daemon যেসব container পুনরায় চালু করা প্রয়োজন সেগুলো পরীক্ষা করে। পরিষ্কারভাবে বন্ধ হওয়া on-failure container সেই তালিকায় থাকে না। এটি exited state-এই থাকে।
আপনি সরাসরি বিষয়টি দেখতে পারেন। একটি service-এ restart: on-failure সেট করুন, docker compose up -d চালান, reboot করুন, তারপর চালান:
docker compose ps -aservice-টি Exited state এবং Exited (0) 2 minutes ago-এর মতো status সহ তালিকাভুক্ত হবে। কিছু নষ্ট হয়নি এবং কোনো error log-ও লেখা হয়নি। তাই সমস্যাটি নির্ণয় করা কঠিন। policy ঠিক তার সংজ্ঞা অনুযায়ী কাজ করেছে।
on-failure এখনও কার্যকর। এমন container-এর জন্য এটি উপযোগী, যা একটি job চালায় এবং crash করতে পারে; সেখানে আপনি সীমিত সংখ্যক retry চান এবং restart loop চান না। reboot-এর পর দীর্ঘ সময় চলা service চালু রাখার জন্য এটি সঠিক উপায় নয়।
পুনরায় চালুর নীতিগুলো কেবল তখনই কাজ করে, যখন বুটের সময় Docker service চালু হয়
পুনরায় চালুর নীতিগুলো Docker daemon প্রয়োগ করে। daemon চালু না হলে কোনো কিছুই প্রয়োগ হয় না। এটি পরীক্ষা করুন:
systemctl is-enabled docker
systemctl is-enabled containerdউভয় কমান্ডের ফলাফল enabled হওয়া উচিত। Docker-এর official repository-এর প্যাকেজগুলো ইনস্টলের সময় এগুলো সক্রিয় করে, তাই নতুন server-এ সাধারণত এই পরীক্ষা সফল হয়। কোনো একটি কমান্ড disabled প্রদর্শন করলে এটি ঠিক করুন:
sudo systemctl enable --now docker containerdএখানে একটি গুরুত্বপূর্ণ বিষয় বুঝে নেওয়া দরকার। Ubuntu-এর সঙ্গে docker.socket-ও থাকে, যা কোনো কিছু প্রথমবার Docker API-তে সংযোগ করলে চাহিদা অনুযায়ী daemon চালু করে। docker.socket সক্রিয় দেখে অনেকে ধরে নেন যে daemon সুরক্ষিত আছে এবং memory বাঁচাতে docker.service নিষ্ক্রিয় করে দেন। বুটের সময় API-তে কোনো কল হয় না। তাই socket-এ কোনো সংযোগ হয় না, daemon চালু হয় না, এবং আপনি প্রথম docker command না দেওয়া পর্যন্ত কোনো container চালু হয় না। Socket activation, docker.service সক্রিয় রাখার বিকল্প নয়।
যখন একটি systemd unit-ই ভালো সমাধান
Restart policy-তে সিস্টেমের অন্যান্য অংশের সঙ্গে ক্রম নির্ধারণের কোনো ধারণা নেই। Daemon শুরু হয় এবং যত দ্রুত সম্ভব আপনার container চালু করে। আপনার stack যদি আলাদা volume, NFS (network file system) share, অথবা encrypted disk থেকে কোনো directory bind-mount করে, সেই path তৈরি হওয়ার আগেই container শুরু হতে পারে। Docker mount point-এ সহজেই একটি খালি directory তৈরি করে এবং সেটির ওপর container চালু করে। ফলে আপনার database কোনো data ছাড়াই চালু হয়।
নিচের যেকোনোটি প্রযোজ্য হলে একটি systemd unit লিখুন। Stack চালুর আগে কোনো mount, VPN interface, অথবা অন্য কোনো unit প্রস্তুত থাকা প্রয়োজন। আপনি চান systemctl stop myapp এবং systemctl start myapp সিস্টেমের অন্য সব service-এর মতো কাজ করুক। অথবা shutdown-এর সময় daemon-এর সঙ্গে জোর করে বন্ধ না হয়ে stack যেন সুশৃঙ্খলভাবে বন্ধ হয়। systemd unit আপনার কাছে নতুন হলে, একটি systemd service এবং timer লেখা-এ file format সম্পর্কে আরও বিস্তারিত ব্যাখ্যা রয়েছে।
systemd ইউনিট লেখা
স্ট্যাকটি একটি home directory-এর বাইরে নির্দিষ্ট path-এ রাখুন। /srv/myapp একটি ভালো পছন্দ, কারণ কেউ লগ ইন করার আগে চলা কোনো ইউনিটের /home পড়ার প্রয়োজন নেই।
/etc/systemd/system/myapp.service তৈরি করুন:
[Unit]
Description=myapp docker compose stack
Requires=docker.service
After=docker.service network-online.target
Wants=network-online.target
RequiresMountsFor=/srv/myapp/data
[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/srv/myapp
ExecStart=/usr/bin/docker compose up -d --remove-orphans
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0
[Install]
WantedBy=multi-user.targetএটি enable এবং start করুন:
sudo systemctl daemon-reload
sudo systemctl enable --now myapp.service
systemctl status myapp.serviceসুস্থ ইউনিটে Active: active (exited) দেখা যায়। প্রথমবার দেখলে এটি ভুল মনে হতে পারে। এটিই সঠিক: Type=oneshot-এর সঙ্গে RemainAfterExit=yes থাকার অর্থ হলো ইউনিট তার command চালিয়েছে, command শেষ হয়েছে, এবং systemd ইউনিটটিকে active হিসেবে চিহ্নিত রাখছে, যাতে shutdown-এর সময় ExecStop চলে।
প্রতিটি লাইনের নির্দিষ্ট কারণ আছে। Requires=docker.service নির্ধারণ করে যে dead socket-এর বিরুদ্ধে docker compose চালানোর বদলে ইউনিট দ্রুত ব্যর্থ হবে। After= execution order নির্ধারণ করে, কারণ শুধু Requires= তা করে না। RequiresMountsFor= systemd-কে ওই path-এর mount unit অন্তর্ভুক্ত করতে এবং সেটির জন্য অপেক্ষা করতে বাধ্য করে। restart policy-এর বদলে unit ব্যবহারের মূল কারণ এটিই। বড় image pull চলার সময় TimeoutStartSec=0 systemd-কে start job বন্ধ করা থেকে বিরত রাখে।
দুটি mechanism একসঙ্গে ব্যবহারের বিষয়ে একটি কথা। Docker-এর documentation host process manager-এর সঙ্গে restart policy মেশানোর পরামর্শ দেয় না। এই সতর্কতা এমন process manager সম্পর্কে, যা নিজেই container process-কে supervise করে এবং daemon একই কাজ করার সময় সেটিকে restart করে। একটি Type=oneshot unit কোনো কিছু supervise করে না। তাই এই unit-এর পাশাপাশি compose file-এ restart: unless-stopped রাখা ঠিক আছে, এবং এটিই আপনার প্রয়োজন। boot-এর সময় ordering systemd পরিচালনা করবে, আর ভোর 3টার সময় কোনো container crash করলে daemon সেটি পরিচালনা করবে।
প্রকৃত রিবুট দিয়ে যাচাই
প্রকৃত পরীক্ষা করার বিকল্প নেই। systemctl restart docker mount-এর ক্রম যাচাই করে না, এবং docker compose down-এর পরে docker compose up -d চালালেও boot-সংক্রান্ত কিছুই যাচাই হয় না।
sudo rebootঅপেক্ষা করুন, পুনরায় সংযোগ করুন এবং এই ক্রমে পরীক্ষা করুন:
uptime
systemctl is-active docker
docker compose psuptime নিশ্চিত করে যে আপনি সত্যিই reboot হওয়া একটি মেশিনে কাজ করছেন। stack directory থেকে চালানো docker compose ps-এ প্রতিটি service-এর অবস্থা running হিসেবে এবং uptime মেশিনের uptime-এর কাছাকাছি দেখানোর কথা। কোনো service-এ Exited দেখা গেলে সেটিই পরীক্ষা করুন।
কোনো কিছু চালু না হলে daemon log-এ boot window-এর তথ্য থাকে:
journalctl -u docker.service -b --no-pager | tail -50unit দ্বারা পরিচালিত stack-এর ক্ষেত্রে journalctl -u myapp.service -b --no-pager boot-এর সময়ের সঠিক docker compose output দেখায়। এর মধ্যে ব্যর্থ image pull বা অনুপস্থিত .env file-ও থাকতে পারে।
যে বিষয়গুলো নীরবে স্বয়ংক্রিয় স্টার্ট নষ্ট করে
docker compose run দিয়ে তৈরি করা container কখনোই file থেকে restart policy পায় না। Compose এগুলোকে একবারের জন্য চালানো container হিসেবে বিবেচনা করে। কোনো service তার policy উপেক্ষা করছে বলে মনে হলে, সেটি up-এর পরিবর্তে run দিয়ে চালু করা হয়েছিল কি না পরীক্ষা করুন।
কোনো volume বা env_file entry-র relative path compose file-এর directory অনুযায়ী নির্ধারিত হয়। এটি আপনার shell থেকে কাজ করে এবং WorkingDirectory সেট করা কোনো unit থেকেও কাজ করে। WorkingDirectory ছাড়া কোনো unit থেকে এটি ব্যর্থ হয়, কারণ তখন working directory হলো /।
Rootless Docker একটি আলাদা বিষয়। daemon একটি user service হিসেবে চলে, এবং সেই user-এর শেষ session বন্ধ হলে user service-ও বন্ধ হয়ে যায়। user-এর জন্য এটি enable করুন এবং কোনো user লগ ইন না থাকলেও এটি চালু রাখার অনুমতি দিন:
systemctl --user enable docker
sudo loginctl enable-linger $USERenable-linger ছাড়া আপনি log out করলে rootless daemon বন্ধ হয়ে যায় এবং container-গুলোও বন্ধ হয়। এতে restart policy নষ্ট হয়েছে বলে মনে হয়।
আরও একটি বিষয় আছে। স্বয়ংক্রিয় security update নির্দিষ্ট সময়ে server reboot করতে পারে। আপনার stack নিজে থেকে আবার চালু হলে তবেই এটি উপকারী। নতুন machine-এ এটি কনফিগার করা নতুন VPS-এ প্রথম দশ মিনিটের কাজ-এর অংশ হওয়া উচিত।
FAQ
restart: always এবং restart: unless-stopped-এর মধ্যে পার্থক্য কী?
উভয় নীতিই কনটেইনার নিজে থেকে বন্ধ হলে সেটি পুনরায় চালু করে। আপনি হাতে কনটেইনার বন্ধ করার পর তাদের আচরণ ভিন্ন হয়। always ব্যবহার করলে Docker daemon পরেরবার চালু হওয়ার সময় কনটেইনারটি আবার চালু হয়। তাই রিবুট করলে আপনার করা ম্যানুয়াল stop কার্যকর থাকে না। unless-stopped ব্যবহার করলে daemon মনে রাখে যে কনটেইনারটি ইচ্ছাকৃতভাবে বন্ধ করা হয়েছিল এবং সেটিকে চালু করে না। নির্দিষ্টভাবে এমন কনটেইনার দরকার না হলে, যা বন্ধ অবস্থায় থাকবে না, unless-stopped ব্যবহার করুন।
restart: unless-stopped যোগ করার পরও রিবুটের পরে কনটেইনার চালু হয় না কেন?
নীতিটি file-এ নয়, কনটেইনারে সংরক্ষিত থাকে। বিদ্যমান কনটেইনারের YAML সম্পাদনা করলে সেটি আপডেট হয় না। Compose যাতে কনটেইনারটি পুনরায় তৈরি করে, সে জন্য docker compose up -d চালান। এরপর docker inspect -f '{{.HostConfig.RestartPolicy.Name}}' $(docker compose ps -q app) দিয়ে নিশ্চিত করুন। সেটি যদি no দেখায়, তাহলে কনটেইনারটি আপনার সম্পাদনার আগেই তৈরি হয়েছিল। আরেকটি সাধারণ কারণ হলো docker.service সক্রিয় না থাকা। systemctl is-enabled docker দিয়ে এটি পরীক্ষা করতে পারেন।
আমি যদি restart policy ব্যবহার করি, তাহলে কি systemd unit-ও প্রয়োজন?
সাধারণত প্রয়োজন হয় না। যে stack-এর শুধু network প্রয়োজন, তার জন্য restart policy যথেষ্ট। অধিকাংশ stack এই ধরনের। কনটেইনারগুলো এমন কোনো নির্ভরতার ওপর নির্ভর করলে unit যোগ করুন, যা Docker daemon চালু হওয়ার সময় প্রস্তুত থাকে না। উদাহরণ হিসেবে external disk, encrypted volume, NFS share অথবা VPN interface উল্লেখ করা যায়। restart policy দিয়ে যে নির্ভরতা প্রকাশ করা যায় না, unit আপনাকে After= এবং RequiresMountsFor=-এর মাধ্যমে তার ordering নির্ধারণ করতে দেয়।
পরবর্তী রিবুটে stack আবার চালু না হয়ে স্থায়ীভাবে বন্ধ রাখব কীভাবে?
unless-stopped ব্যবহার করলে docker compose stop যথেষ্ট। কারণ হাতে বন্ধ করা কনটেইনার daemon পুনরায় চালু হলে আবার চালু হয় না। always ব্যবহার করলে শুধু stop করা যথেষ্ট নয়। রিবুটের পরে কনটেইনারটি আবার চালু হবে। হয় কনটেইনারগুলো সরাতে docker compose down চালান, অথবা প্রথমে docker update --restart no my-container দিয়ে policy পরিবর্তন করুন। systemd unit যদি stack পরিচালনা করে, তাহলে sudo systemctl disable myapp.service-ও চালান। তা না হলে unit আবার stack চালু করবে।