সরাসরি পর্বে যান
পর্ব ১৬ · ৮১-এর মধ্যে · মডিউল ৩

Machine image আর cloud-init — একই server বারবার বানানো

Machine Images and cloud-init — Building the Same Server Again and Again

Taught in Bangla, this lesson shows why hand-built snowflake servers are a trap and introduces pets versus cattle, what a machine image contains and which images to trust. It writes a correct #cloud-config, reads cloud-init boot stages, compares bake and bootstrap by launch time and failure with simulators, and covers keeping secrets out of user data, golden image pipelines, launch templates and image retirement.

এই পর্বে যা শিখবেন

  • Snowflake server-এর সমস্যা আর “pets vs cattle” ধারণা
  • একটি machine image-এর ভেতরে কী থাকে, আর কোন image বিশ্বাস করবেন
  • একটি সঠিক #cloud-config লেখা এবং boot-এর প্রতিটি stage পড়া
  • Launch time আর ব্যর্থতার হিসাব দিয়ে bake ও bootstrap-এর মধ্যে বেছে নেওয়া
  • Golden image, launch template, patch-এর ছন্দ আর পুরোনো image অবসরের নিয়ম

১ · হাতে বানানো server কেন ফাঁদ

পর্ব ১৪-তে আপনি হাতে একটি server সাজিয়েছিলেন: package বসানো, Nginx-এর config, firewall, certificate. একটির জন্য এটি ঠিক আছে। এবার ভাবুন, একটি কাল্পনিক অনলাইন বাজার অ্যাপে ঈদের আগে ১০টি server লাগবে, আর একটি রাত তিনটায় নষ্ট হয়ে গেছে। কেউ কি ঠিক মনে করতে পারবেন, ছয় মাস আগে কোন কমান্ডগুলো কোন ক্রমে চালানো হয়েছিল?

এভাবে হাতে বদলাতে বদলাতে একটি server অনন্য হয়ে যায়। Martin Fowler ২০১২ সালে নিজের লেখায় একে বলেছেন snowflake server, কারণ দুটি তুষারকণা হুবহু এক হয় না। এমন server-এর সমস্যা তিনটি:

  • নকল করা যায় না: নতুন server বানালে কোনো একটি ছোট setting বাদ পড়ে যায়।
  • বদলাতে ভয় লাগে: কেউ জানেন না কোন ফাইলে কী বদলালে কী ভাঙবে।
  • ধীরে ধীরে আলাদা হয়: একই কাজের server-গুলোর মধ্যে অমিল তৈরি হয়। একে বলে drift.

পাড়ার পুরোনো CNG-র কথা ভাবুন। ইঞ্জিন এক মিস্ত্রির, গিয়ার আরেকজনের, আর তার জোড়া দেওয়া আছে এমনভাবে যা শুধু একজন মিস্ত্রিই বোঝেন। তিনি ছুটিতে গেলে গাড়ি বন্ধ। কারখানার নতুন গাড়ি আলাদা: নকশা আছে, যন্ত্রাংশ এক রকম, আর একটি নষ্ট হলে হুবহু আরেকটি আনা যায়।

Pets vs cattle

এই পার্থক্য বোঝাতে cloud জগতে একটি তুলনা খুব চালু: pets vs cattle. পোষা প্রাণীর নাম থাকে, অসুখ হলে যত্ন করে সারানো হয়। খামারের পালের প্রতিটি প্রাণী নম্বরে চেনা, আর পালটিই আসল। পোষা server হলো db-rahim-01, যাকে সবাই চেনেন। পালের server হলো web-0417; সমস্যা হলে সরিয়ে নতুন একটি আনা হয়।

তুলনাটি cloud-এর আলোচনায় ছড়িয়েছেন মূলত Randy Bias, ২০১২ সালের দিকে। তিনি নিজেই লিখেছেন, ধারণাটি পেয়েছেন Microsoft-এর Bill Baker-এর একটি বক্তৃতা থেকে। একই সময়ে Martin Fowler phoenix server-এর কথা লেখেন: server-কে নিয়মিত পুড়িয়ে নতুন করে জন্ম দেওয়া। ২০১৩ সালে Chad Fowler “immutable” শব্দটি দিয়ে একই চিন্তা জনপ্রিয় করেন, যা আজ immutable infrastructure নামে পরিচিত।

লক্ষ্যটি পরিষ্কার: server-এর জ্ঞান মানুষের মাথায় নয়, একটি নকশায় থাকবে। Cloud-এ এই নকশার দুটি অংশ: machine image আর user data. তবে যে server-এ data থাকে, সেটি অনেকটা পোষাই থেকে যায়। তাকে কীভাবে সামলাবেন, তা ভাবনার প্রশ্নে দেখব।

২ · Machine image-এর ভেতরে কী থাকে

Machine image হলো একটি boot করার মতো disk-এর সংরক্ষিত ছাঁচ, সাথে সেই disk চালাতে যা জানা দরকার তার তথ্য। পর্ব ১৫-তে যে VM চালু হয়েছিল, তার disk এমন একটি ছাঁচ থেকেই নকল হয়েছিল। ছাপাখানার প্লেটের মতো ভাবুন: একবার প্লেট তৈরি হলে হাজারটি কার্ড হুবহু ছাপা যায়।

অংশকী থাকেকেন জরুরি
Root volume-এর ছবিKernel, OS ফাইল, package, config, আপনার বসানো agentএটিই server-এর শরীর
CPU architecturex86_64 বা arm64ভুল architecture-এর instance type-এ চলবে না
Boot তথ্যBoot mode (BIOS বা UEFI), virtualization-এর ধরনInstance type-এর সঙ্গে মিলতে হয়
Block device mappingকোন disk কত বড়, কোন ধরনেরInstance চালুর সময় disk তৈরি হয়
অনুমতিকে এই image দিয়ে launch করতে পারবেনব্যক্তিগত, ভাগ করা, না সবার জন্য খোলা

Image আর snapshot এক নয়। Snapshot হলো একটি disk-এর একটি মুহূর্তের ছবি। Image সেই ছবির সঙ্গে boot করার তথ্য যোগ করে। AWS-এ একটি AMI সাধারণত ভেতরে এক বা একাধিক snapshot-এর ওপর দাঁড়িয়ে থাকে। ভেতরের virtual disk কীভাবে কাজ করে, তা পড়তে পারেন Operating Systems · Virtual machine পর্বে।

ধারণাAWSAzureGoogle Cloud
নিজের imageAMIManaged image, বা Azure Compute Gallery-র image versionCustom image
Image-এর দলনামের নিয়ম, tag, parameterGallery-র image definitionImage family
পরিধি (লেখার সময়ে)Region-ভিত্তিক; অন্য region-এ copy লাগেGallery-তে বহু region-এ replicate করা যায়Global resource, project-এর ভেতরে
Boot-এর নির্দেশUser dataCustom data, user dataMetadata-র user-data বা startup-script
Launch-এর নকশাLaunch templateScale set-এর modelInstance template

AWS-এ একই AMI-এর ID প্রতিটি region-এ আলাদা। একটি region-এর ID অন্য region-এর script-এ বসালে launch ব্যর্থ হবে। তাই বহু region-এ কাজ করলে region অনুযায়ী ID-র তালিকা রাখতে হয়, অথবা নাম দিয়ে খুঁজতে হয়।

Image বানানোর আগে পরিষ্কার করা

একটি চলমান server থেকে image বানালে তার ব্যক্তিগত জিনিসও চলে যায় প্রতিটি নকলে। তাই আগে generalization লাগে, মানে একটি নির্দিষ্ট machine-এর পরিচয় মুছে ফেলা। সবচেয়ে জরুরি: SSH host key, /etc/machine-id, shell history, authorized_keys, log আর cloud-init-এর পুরোনো অবস্থা। Host key না মুছলে সব server একই পরিচয় দেখাবে, যা পর্ব ১২-র নিরাপত্তাকে দুর্বল করে।

# Run inside the build VM, just before making the image. Never on a live server.
sudo cloud-init clean --logs        # next boot behaves like a first boot
sudo rm -f /etc/ssh/ssh_host_*      # new host keys are made on first boot
sudo truncate -s 0 /etc/machine-id  # a new machine id is made on first boot
rm -f ~/.bash_history

৩ · Image কোথা থেকে নেবেন — বিশ্বাসের প্রশ্ন

Image মানে অন্য কারও disk দিয়ে আপনার server চালানো। ভেতরে যা আছে, তা root অধিকার নিয়েই চলবে। অচেনা লোকের রান্না করা খাবার আর দোকানের সিল করা প্যাকেটের মধ্যে যে পার্থক্য, এখানেও তাই।

Provider-এর public image

Provider বা OS-এর মূল প্রকাশক (Ubuntu, Debian, Red Hat ইত্যাদি) নিজে প্রকাশ করেন। নিয়মিত হালনাগাদ হয়। শুরুর জন্য সবচেয়ে নিরাপদ।

Base হিসেবে এটিই ভালো

Marketplace image

বাণিজ্যিক বিক্রেতার প্রস্তুত করা image, যেমন firewall বা database. Software-এর আলাদা খরচ থাকতে পারে।

বিক্রেতা ও মূল্য যাচাই করুন

Community image

যে কেউ প্রকাশ করতে পারেন। প্রকাশকের কোনো কঠোর যাচাই নাও হতে পারে। ভেতরে পুরোনো software বা ক্ষতিকর জিনিস থাকতে পারে।

Production-এ এড়িয়ে চলুন

আপনার নিজের image

বিশ্বস্ত base থেকে আপনার pipeline-এ তৈরি, পরীক্ষিত ও version দেওয়া। একে বলে golden image.

Production-এর লক্ষ্য

নামে নয়, প্রকাশকের পরিচয়ে খুঁজুন

Image-এর নাম যে কেউ নকল করতে পারেন। “ubuntu-24.04-server” নামটি দেখে বিশ্বাস করা তাই ভুল। ২০২৫ সালে Datadog-এর গবেষকেরা “whoAMI” নামে একটি ঝুঁকি প্রকাশ করেন: কোনো script যদি শুধু নামের ধরন দিয়ে “সবচেয়ে নতুন” AMI খোঁজে আর প্রকাশক না বলে, তবে একই নামের অচেনা image বেছে নেওয়া সম্ভব। প্রতিরক্ষা সহজ:

  • Owner দিন: খোঁজার সময় সবসময় প্রকাশকের account বা project বেঁধে দিন।
  • ID pin করুন: Production-এ “latest” নয়, নির্দিষ্ট image ID ব্যবহার করুন।
  • স্বাক্ষর মিলিয়ে নিন: Image ফাইল নিজে নামালে প্রকাশকের checksum আর digital signature যাচাই করুন। Ubuntu-র মতো প্রকল্প স্বাক্ষরিত checksum প্রকাশ করে।
  • নিয়মে আটকে দিন: AWS-এর Allowed AMIs-এর মতো নিয়ম বা governance policy দিয়ে অনুমোদিত প্রকাশক ঠিক করে রাখুন (পর্ব ৩৫)।
# The owner filter is the important part. 123456789012 is a fake account id.
aws ec2 describe-images \
  --owners 123456789012 \
  --filters "Name=name,Values=shop-web-*" "Name=state,Values=available" \
  --query "sort_by(Images, &CreationDate)[-1].[ImageId,Name]" \
  --output text

৪ · Instance launch: image + user data

Image সব server-এর জন্য এক। কিন্তু প্রতিটি server-এর কিছু আলাদা কথা জানা দরকার: কোন environment, কোন app version, কোন নাম। এই আলাদা কথা পাঠানো হয় user data দিয়ে। বিয়ের কার্ডের মতো ভাবুন: ছাপা এক, শুধু খামে অতিথির নাম হাতে লেখা।

Image + user data → চলমান instance

One image and one set of instructions become many running instances

Machine image আর user data থেকে তিনটি চলমান instance Machine image OS · package · agent shop-web-v42 User data environment · version #cloud-config Launch template instance type · subnet · role Control plane জায়গা বাছাই · disk নকল · boot web-1i-0a0110.0.1.11 web-2i-0a0210.0.1.12 web-3i-0a0310.0.1.13 Disk একই, পরিচয় আলাদা
ছবি ১৬.১ — Image সবার জন্য এক। User data আর launch template ঠিক করে দেয় কীভাবে চলবে। প্রতিটি instance পায় নিজের ID আর IP.

Instance নিজের user data পায় কীভাবে? প্রতিটি VM-এর ভেতর থেকে একটি বিশেষ ঠিকানায় instance metadata সেবা পাওয়া যায়, সাধারণত 169.254.169.254. এই link-local ঠিকানা VM-এর বাইরে থেকে পৌঁছানো যায় না। এখান থেকে VM জানে নিজের instance ID, region, network আর user data.

নিচে তিন provider-এ একই কাজ দেখুন। এগুলো শুধু উদাহরণ। চালালে খরচ হতে পারে, আর আপনার account না থাকলে চালানোর দরকার নেই। পরীক্ষা শেষে instance মুছে ফেলার কমান্ডও দেওয়া আছে।

aws ec2 run-instances \
  --image-id ami-0123456789abcdef0 \
  --instance-type t3.micro \
  --user-data file://cloud-config.yaml \
  --dry-run        # checks permission only; remove it for a real launch

# clean up after a real launch
aws ec2 terminate-instances --instance-ids i-0123456789abcdef0
az group create --name rg-demo --location southeastasia

az vm create \
  --resource-group rg-demo \
  --name web-1 \
  --image Ubuntu2404 \
  --custom-data cloud-config.yaml \
  --generate-ssh-keys

# clean up (deletes every resource in the group)
az group delete --name rg-demo
gcloud compute instances create web-1 \
  --zone=asia-south1-a \
  --machine-type=e2-micro \
  --image-family=ubuntu-2404-lts-amd64 \
  --image-project=ubuntu-os-cloud \
  --metadata-from-file=user-data=cloud-config.yaml

# clean up
gcloud compute instances delete web-1 --zone=asia-south1-a

নাম এক হলেও আচরণ এক নয়। Google Cloud-এর startup-script সাধারণত প্রতি boot-এ চলে। AWS-এর user data script ডিফল্টে প্রতি instance-এ একবার চলে। আর cloud-init সব image-এ থাকে না, যেমন Windows-এ আলাদা agent কাজ করে। User data-র আকারেরও সীমা আছে; AWS-এ লেখার সময়ে তা ১৬ KB, অন্য provider-এ আলাদা। বড় script নিজে না পাঠিয়ে সেটি নামিয়ে আনার নির্দেশ পাঠানোই ভালো।

৫ · cloud-init: প্রথম boot-এর ভেতরে কী ঘটে

cloud-init হলো image-এর ভেতরে থাকা একটি প্রোগ্রাম, যা boot-এর সময় চলে আর user data পড়ে কাজ করে। Canonical-এর Ubuntu দিয়ে শুরু হলেও এখন বেশিরভাগ বড় Linux distribution আর cloud-এ এটি পাওয়া যায়। User data-র প্রথম লাইন দেখে সে বোঝে কী করতে হবে: #! দিয়ে শুরু হলে shell script, আর #cloud-config হলে YAML নির্দেশ। Cloud-config-এ আপনি কমান্ড নয়, চাওয়া অবস্থা লেখেন, তাই সাধারণত এটিই ভালো।

#cloud-config
# Fictional shop web server. No passwords or keys here.
hostname: shop-web
package_update: true
packages:
  - nginx
  - curl
write_files:
  - path: /etc/nginx/conf.d/health.conf
    owner: root:root
    permissions: '0644'
    content: |
      server {
          listen 8080;
          location = /healthz {
              default_type text/plain;
              return 200 "ok\n";
          }
      }
bootcmd:
  - echo "boot seen" >> /var/log/boot-seen.log
runcmd:
  - systemctl enable --now nginx
  - curl -fsS http://127.0.0.1:8080/healthz
final_message: "cloud-init finished in $UPTIME seconds"

প্রথম লাইনের #cloud-config লেখাটি বাধ্যতামূলক; এটি বাদ দিলে পুরো ফাইলটি কাজ না করেই পড়ে থাকতে পারে। ফাইলটি পাঠানোর আগে পরীক্ষা করা যায়: cloud-init schema --config-file cloud-config.yaml. Stage-গুলো নিচের ক্রমে চলে।

Stagesystemd unit (উদাহরণ)কী ঘটে
Detectgenerator, ds-identifyকোন cloud-এ চলছে, cloud-init চালানো দরকার কি না ঠিক করা। এর উৎসকে বলে datasource.
Localcloud-init-localNetwork চালুর আগে datasource খোঁজা, network-এর config লেখা
Networkcloud-init-network (পুরোনো version-এ cloud-init)User data পড়া; bootcmd, write_files, disk বড় করা, hostname, user, SSH key
Configcloud-configNTP, locale, package-এর উৎসের মতো config; runcmd এখানে script হিসেবে শুধু লেখা হয়
Finalcloud-finalPackage install, runcmd-এর script চালানো, final_message

এই ক্রম থেকে একটি ফাঁদ আসে। write_files চলে package install-এর আগে। তাই owner: www-data লিখলে তখনো সেই user না থাকায় সমস্যা হতে পারে। সমাধান হলো ঐ entry-তে defer: true দেওয়া, যা ফাইল লেখাকে Final-এ পাঠায়। নিচে একটি instance-এর boot ধাপে ধাপে দেখুন। সময়গুলো ধরে নেওয়া, শুধু ক্রমটি বোঝার জন্য।

একটি নতুন instance (সময় ধরে নেওয়া)
Uptime
কে চলছে
Network
health.conf
Nginx
Health check
  1. Power-on. Control plane image থেকে disk বানিয়ে VM চালু করল। Firmware থেকে bootloader, তারপর kernel উঠল। cloud-init এখনো ঘুমিয়ে।
  2. Detect. systemd চালু হলো। ds-identify বুঝল এটি একটি cloud VM, তাই cloud-init-এর unit-গুলো চালু রাখল।
  3. Local. Datasource খুঁজে পেল। Instance ID নতুন, তাই এটি “প্রথম boot” বলে গণ্য হলো। Network-এর config লেখা হলো, network এখনো ওঠেনি।
  4. Network. Network উঠল। User data পড়া হলো। bootcmd চলল, write_files health.conf লিখল, disk বড় হলো, hostname আর SSH key বসল।
  5. Config. সময়, locale আর package-এর উৎস ঠিক হলো। runcmd-এর লাইনগুলো একটি script ফাইলে লেখা হলো, কিন্তু চলেনি।
  6. Final: package. apt-get update আর nginx, curl install হলো। সবচেয়ে সময় লাগল এই ধাপে, প্রায় ৪৭ সেকেন্ড। Provider এখনো দেখাচ্ছে “running”, কিন্তু app প্রস্তুত নয়।
  7. Final: runcmd. Nginx চালু হলো। curl নিজের /healthz থেকে ok পেল। Health check সফল।
  8. শেষ। final_message ছাপা হলো, আর boot-finished চিহ্ন তৈরি হলো। Load balancer এখন traffic পাঠাতে পারে।

কোনটি একবার, কোনটি প্রতি boot-এ

cloud-init প্রতিটি কাজের একটি চিহ্ন রাখে /var/lib/cloud/instance/sem/ ফোল্ডারে। পরের boot-এ চিহ্ন দেখে কাজটি বাদ দেয়। এই হিসাব instance ID-র সঙ্গে বাঁধা। তাই সাধারণ reboot বা stop-start-এ ID একই থাকে, আর নতুন instance-এ ID নতুন হওয়ায় কাজগুলো আবার চলে।

কীকতবার চলেকোন কাজে
bootcmdপ্রতি boot-এ, শুরুর দিকেখুব প্রাথমিক কাজ
write_files, packages, runcmdপ্রতি instance-এ একবারসাধারণ সেটআপ
/var/lib/cloud/scripts/per-boot/প্রতি boot-এImage-এ রাখা script, প্রতিবার চালাতে হলে
/var/lib/cloud/scripts/per-instance/প্রতি instance-এ একবারImage-এ রাখা নতুন instance-এর script
/var/lib/cloud/scripts/per-once/মোটের ওপর একবারইID বদলালেও আর চলবে না

সমস্যা হলে কোথায় দেখবেন

Boot-এর সমস্যা ধরতে এই কয়েকটি জায়গা মনে রাখুন। /var/log/cloud-init-output.log-এ আছে আপনার কমান্ডের আউটপুট, আর /var/log/cloud-init.log-এ cloud-init-এর নিজের বিস্তারিত log. নিচের আউটপুট নমুনা — version-ভেদে আলাদা হতে পারে।

$ cloud-init status --wait      # wait until finished, then report
status: done

$ sudo cloud-init analyze blame # which module took how long (sample output)
-- Boot Record 01 --
     46.81200s (modules-final/config-package_update_upgrade_install)
      2.34100s (init-network/config-growpart)
      1.10700s (init-local/search-Ec2Local)

$ sudo tail -n 20 /var/log/cloud-init-output.log
$ sudo cat /var/lib/cloud/instance/user-data.txt   # root only

runcmd-এর একটি কমান্ড ব্যর্থ হলেও পরের লাইনগুলো সাধারণত চলতে থাকে, কারণ সেটি একটি সাধারণ shell script. Script-এর শেষ ফলই ধরা হয়। তাই ফল নিশ্চিত হতে চাইলে নিজে set -e ব্যবহার করুন, অথবা শেষে যাচাইয়ের কমান্ড রাখুন। নতুন version-এ cloud-init status-এর exit code-ও সাহায্য করে: সফল হলে 0, ব্যর্থ হলে 1, আর আংশিক সমস্যায় 2.

৬ · User data-তে secret নয়, আর script হোক idempotent

User data দেখতে ব্যক্তিগত মনে হয়, কিন্তু আসলে তা নয়। এটি encrypted নয়; API-তে base64 আকারে থাকা মানে শুধু অন্য রকম করে লেখা। নিচের জায়গাগুলোতে এটি পড়া সম্ভব:

  • VM-এর ভেতর থেকে: Metadata সেবা থেকে; সাধারণ user বা কোনো দুর্বল app-ও এটি পড়তে পারে।
  • Cloud API থেকে: Instance-এর তথ্য পড়ার অনুমতি যাঁর আছে, তিনিও অনেক ক্ষেত্রে পড়তে পারেন।
  • Disk আর log থেকে: cloud-init user data-র কপি রাখে, আর কমান্ড ছাপালে তা log-এ যায়।
  • Launch template আর image থেকে: নকল হলে secret-ও নকল হয়।

ঠিক পথ হলো instance-কে পরিচয় দেওয়া, secret নয়। Instance-এর সঙ্গে একটি instance role যুক্ত করুন (পর্ব ৩১)। Boot-এর সময় বা app চালুর সময় সে নিজের অস্থায়ী পরিচয়ে secret manager থেকে শুধু নিজের secret পড়বে (পর্ব ৩৩)। User data-তে থাকবে শুধু secret-এর নাম, মান নয়।

# User data holds only the secret's NAME. The role gives permission at run time.
umask 077
aws secretsmanager get-secret-value \
  --secret-id shop/db-password \
  --query SecretString --output text > /run/shop/db-password
# Azure Key Vault and Google Secret Manager work the same way.
# Do not use "set -x" here: it would print commands into the log.

Metadata সেবাও একটি ঝুঁকি। কোনো web app-কে দিয়ে ভেতরের ঠিকানায় অনুরোধ পাঠানোর ত্রুটিকে বলে SSRF. এ থেকে বাঁচতে AWS ২০১৯ সালে session token-ভিত্তিক IMDSv2 আনে। Launch template-এ একে বাধ্যতামূলক করুন। অন্য provider-ও অনুরোধে বিশেষ header চায়।

Idempotent script

User-data script আবার চলতে পারে: per-boot-এ রাখলে, image build পুনরায় চালালে, বা হাতে পরীক্ষা করলে। তাই script-কে idempotent করুন, মানে একবার চালালে যা হয়, দশবার চালালেও তাই হবে। পর্ব ১০-এর নিরাপদ script-এর নিয়ম এখানে কাজে লাগে। মূল কৌশল হলো “কাজটি করা” নয়, “এই অবস্থা নিশ্চিত করা”। নিচের script একটি temp ফোল্ডারের ভেতর ভুয়া /etc বানিয়ে কাজ করে, আসল /etc ছোঁয় না।

set -euo pipefail
ROOT=$(mktemp -d)                # a fake root, not the real /etc
trap 'rm -rf "$ROOT"' EXIT
changed=0
report() { printf '%-8s %s\n' "$1" "$2"; [ "$1" = ok ] || changed=$((changed + 1)); }

ensure_dir() {                   # create the folder only if missing
  if [ -d "$ROOT$1" ]; then report ok "$1"
  else mkdir -p "$ROOT$1"; report changed "$1"; fi
}
ensure_line() {                  # add the line only if absent
  touch "$ROOT$1"
  if grep -qxF "$2" "$ROOT$1"; then report ok "$1: $2"
  else echo "$2" >> "$ROOT$1"; report changed "$1: $2"; fi
}
ensure_file() {                  # rewrite only if the content differs
  if [ -f "$ROOT$1" ] && [ "$(cat "$ROOT$1")" = "$2" ]; then report ok "$1"
  else echo "$2" > "$ROOT$1.tmp"; mv "$ROOT$1.tmp" "$ROOT$1"; report changed "$1"; fi
}
provision() {
  changed=0
  ensure_dir  /etc/shop
  ensure_line /etc/shop/limits.conf "nofile 65535"
  ensure_file /etc/shop/health.conf "listen 8080"
  echo "result: $changed changed"
}
echo "== first run"; provision
echo "== second run"; provision
echo "== someone edits one file by hand"
echo "listen 9999" > "$ROOT/etc/shop/health.conf"
provision
for run in 1 2 3; do echo "nofile 65535" >> "$ROOT/naive.conf"; done
echo "a naive append, run 3 times: $(wc -l < "$ROOT/naive.conf") lines"

প্রথম চালানোয় result: 3 changed, দ্বিতীয়বার result: 0 changed: কোনো কাজই দ্বিগুণ হয়নি। হাতে ফাইল বদলানোর পর script শুধু সেই একটি ফাইল ঠিক করে দিল, result: 1 changed. এটিই drift সারানোর ছোট রূপ। শেষ লাইনে সাধারণ >> তিনবার চলে 3 lines বানিয়েছে, যা idempotent নয়। ফাইল লেখার সময় আগে .tmp-এ লিখে mv করা হয়েছে, যাতে অর্ধেক লেখা ফাইল কখনো দেখা না যায়।

৭ · Bake বনাম bootstrap

একটি server-কে প্রস্তুত করার দুটি সময় আছে: launch-এর আগে, আর launch-এর পরে।

  • Baking: সব কিছু আগেই image-এ ভরে রাখা। Boot-এ প্রায় কিছুই বসাতে হয় না।
  • Bootstrapping: সাধারণ base image নিয়ে boot-এর সময় সব বসানো।
  • Half-baked: ধীর ও কম বদলানো অংশ (OS patch, runtime, agent) image-এ, আর app-এর নতুন version boot-এ।

বিয়েবাড়ির ক্যাটারিং ভাবুন। Baking হলো আগের রাতেই মাংস মেখে, মসলা বেটে, হাঁড়ি প্রস্তুত রাখা; অতিথি এলে শুধু চুলা জ্বালালেই হয়। Bootstrapping হলো অতিথি আসার পর বাজারে যাওয়া। তাজা জিনিস পাবেন, নতুন পদও যোগ করতে পারবেন। কিন্তু সময় বেশি, আর সেদিন বাজার বন্ধ থাকলে সবাই না খেয়ে থাকবেন।

বিষয়BakeBootstrapHalf-baked
Launch timeসবচেয়ে কমসবচেয়ে বেশিমাঝামাঝি
Boot-এ বাইরের নির্ভরতাপ্রায় নেইMirror, registry, app store সবইশুধু app store
Instance-গুলো কি এক রকম?হুবহু একLaunch-এর দিনের ওপর নির্ভরOS এক, app নির্দিষ্ট version
ছোট পরিবর্তননতুন image লাগে, সময় বেশিScript বদলালেই হয়App-এর জন্য image লাগে না
পরিচালনার খরচPipeline আর বহু imageকম, কিন্তু boot ভাঙে বেশিদুটোরই কিছুটা

হিসাব: ধীর launch-এর দাম

ধরুন ঈদের আগে চাহিদা এমনভাবে বাড়ছে যে প্রতি মিনিটে ২টি বাড়তি instance লাগে। নতুন instance প্রস্তুত হতে যতক্ষণ লাগে, ততক্ষণের চাহিদা আগে থেকেই ফাঁকা ক্ষমতা হিসেবে রাখতে হবে।

প্রয়োজনীয় ফাঁকা ক্ষমতা = চাহিদা বাড়ার হার × প্রস্তুত হওয়ার সময়
  • Bootstrap, ৪ মিনিট: ২ × ৪ = ৮টি বাড়তি instance সবসময় চালু রাখতে হবে।
  • Bake, ১ মিনিট: ২ × ১ = ২টি।
  • পার্থক্য: ৬টি। ধরে নিই প্রতিটির দাম ঘণ্টায় $০.১০, আর মাসে ৭২০ ঘণ্টা। তাহলে ৬ × ০.১০ × ৭২০ = মাসে $৪৩২ বাড়তি।

ব্যর্থতারও হিসাব আছে। Boot-এ n-টি স্বাধীন ধাপ থাকলে আর প্রতিটির ব্যর্থতার সম্ভাবনা p হলে, মোট ব্যর্থতা = ১ − (১ − p)n. প্রতিটিতে ০.৫% করে ৬টি ধাপে তা প্রায় ২.৯৬%. অর্থাৎ ১,০০০ launch-এ গড়ে প্রায় ৩০টি ব্যর্থ। আর স্বাধীনতার ধারণাটি বিপদের সময় খাটে না: Mirror বন্ধ থাকলে সব launch একসাথে ব্যর্থ হয়। নিচের সিমুলেটর দুটো প্রভাবই দেখায়। এখানে সব সংখ্যা ধরে নেওয়া, আসল সংখ্যা নয়।

// Bake vs bootstrap over 2000 launches. All numbers are assumed.
function mulberry32(a) { return () => { a = a + 0x6D2B79F5 | 0;
  let t = Math.imul(a ^ a >>> 15, 1 | a);
  t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
  return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }
const rnd = mulberry32(2026);
const between = (lo, hi) => lo + (hi - lo) * rnd();
const FAIL = 0.004, N = 2000, OUTAGE = [1200, 1300]; // mirror down: launch 1200-1299

// each step: [min s, max s, can fail?, needs package mirror?]
const boot = [35, 55, false, false], health = [10, 15, false, false];
const apt = [20, 40, true, true], pkgs = [60, 120, true, true];
const app = [10, 30, true, false], agent = [15, 35, true, true];
const plans = {
  "bootstrap ": [boot, apt, pkgs, agent, app, health],
  "half-baked": [boot, app, health],
  "full bake ": [boot, health],
};
for (const [name, steps] of Object.entries(plans)) {
  const ok = []; let fail = 0, inOutage = 0;
  for (let i = 0; i < N; i++) {
    const down = i >= OUTAGE[0] && i < OUTAGE[1];
    let t = 0, dead = false;
    for (const [lo, hi, risky, mirror] of steps) {
      t += between(lo, hi);
      if ((risky && rnd() < FAIL) || (mirror && down)) { dead = true; break; }
    }
    if (dead) { fail++; if (down) inOutage++; } else ok.push(t);
  }
  ok.sort((a, b) => a - b);
  const mean = ok.reduce((s, x) => s + x, 0) / ok.length;
  console.log(name, "failed", String(fail).padStart(3), "(" + (fail * 100 / N).toFixed(2) + "%)",
    "in outage", String(inOutage).padStart(3), " mean", mean.toFixed(0) + "s",
    " p99", ok[Math.floor(ok.length * 0.99)].toFixed(0) + "s");
}

আউটপুট: bootstrap failed 134 (6.70%) in outage 100 mean 222s p99 268s, half-baked failed 4 (0.20%) in outage 0 mean 77s p99 95s, full bake failed 0 (0.00%) in outage 0 mean 57s p99 68s. Bootstrap-এর ১৩৪টি ব্যর্থতার ১০০টি mirror বিভ্রাটের সময়ে, মানে ঐ ১০০টি launch-এর সবগুলোই। এই নির্ভরতা না থাকায় half-baked আর bake-এর গায়ে আঁচ লাগেনি। একে বলে static stability: বাইরের কিছু নষ্ট হলেও নতুন ক্ষমতা তৈরি করা যায়। তবে “০ ব্যর্থ” এসেছে কারণ মডেলে bake-এ ঝুঁকিপূর্ণ ধাপই রাখা হয়নি। বাস্তবে capacity বা boot-এর সমস্যায় এটিও ব্যর্থ হতে পারে। FAIL বা OUTAGE বদলে দেখুন।

বেছে নেওয়ার সহজ নিয়ম: যা ধীর আর কম বদলায়, তা bake করুন। যা environment-ভেদে আলাদা, তা boot-এ দিন। Secret কখনোই image-এ নয়। Autoscaling-এর পালের জন্য bake বা half-baked সাধারণত ভালো। অল্প দিন চলা পরীক্ষার server-এ bootstrap-ই যথেষ্ট।

মাঝপথে bootstrap ব্যর্থ হলে

এবার দেখা যাক boot-এর মাঝখানে package mirror বন্ধ হলে কী ঘটে। লক্ষ করুন, provider-এর চোখে VM সারাক্ষণ ঠিকই আছে। সময়গুলো ধরে নেওয়া।

Instance i-0b7 · mirror বিভ্রাট চলছে
Uptime
Provider-এর অবস্থা
কী চলছে
ফলাফল
Health check-এ ব্যর্থ
  1. Launch. Autoscaling নতুন instance চাইল। কয়েক সেকেন্ডেই provider দেখাল “running”. মনে রাখবেন, এর মানে VM চলছে, app নয়।
  2. Update সফল। Final stage-এ package-এর তালিকা ঠিকমতো এলো। বিভ্রাটটি আংশিক: তালিকার ফাইল পাওয়া গেছে, কিন্তু package-এর ফাইল নয়।
  3. Install ব্যর্থ। nginx নামাতে গিয়ে mirror দিল 503. apt-get শেষ হলো exit code 100 দিয়ে।
  4. ভুল ধারণা। runcmd তবু চলল, কারণ cloud-init পরের module-এ এগিয়ে যায়। systemctl enable --now nginx বলল, unit নেই।
  5. Error লেখা হলো। cloud-init status এখন error দেখাচ্ছে। কিন্তু কেউ না দেখলে এই তথ্য log-এই পড়ে থাকে।
  6. Health check ব্যর্থ। ধরে নিই, load balancer ৩০ সেকেন্ড পরপর দেখে, প্রথমবার ৬০ সেকেন্ডে। Port-এ সাড়া নেই। এখনো instance-টি কোনো traffic পায়নি, কিন্তু টাকা খরচ হচ্ছে।
  7. Unhealthy. পরপর ৩ বার ব্যর্থ হওয়ায় instance-টি unhealthy হলো। ৩ বারের এই সীমাটিও ধরে নেওয়া।
  8. লুপ। Autoscaling এটি সরিয়ে নতুন একটি আনল। Mirror তখনো বন্ধ, তাই সেটিও একইভাবে ব্যর্থ হবে। চাহিদা বাড়ছে, ক্ষমতা বাড়ছে না, আর log-সহ প্রমাণ মুছে যাচ্ছে। সমাধান: bake বা half-baked, boot-এ retry, log অন্য কোথাও পাঠানো, আর ব্যর্থ launch-এর ওপর alert.

এমন log পড়ার কাজে পর্ব ৯-এর হাতিয়ারই যথেষ্ট। নিচের script একটি নমুনা cloud-init-output.log নিজেই তৈরি করে, প্রথম error খুঁজে বের করে আর প্রতিটি stage কত সেকেন্ডে শুরু হয়েছে তা দেখায়।

set -euo pipefail
log=$(mktemp); trap 'rm -f "$log"' EXIT
cat > "$log" <<'EOF'
Cloud-init v. 24.4 running 'init-local' at Fri, 30 Oct 2026 04:12:03 +0000. Up 6.10 seconds.
Cloud-init v. 24.4 running 'init' at Fri, 30 Oct 2026 04:12:06 +0000. Up 9.02 seconds.
Cloud-init v. 24.4 running 'modules:config' at Fri, 30 Oct 2026 04:12:10 +0000. Up 13.21 seconds.
Cloud-init v. 24.4 running 'modules:final' at Fri, 30 Oct 2026 04:12:12 +0000. Up 15.44 seconds.
Reading package lists...
Err:1 http://mirror.example.net/ubuntu noble-updates/main amd64 nginx 1.24.0
  503  Service Unavailable [IP: 192.0.2.10 80]
E: Unable to fetch some archives, maybe run apt-get update
Failed to enable unit: Unit file nginx.service does not exist.
Cloud-init v. 24.4 finished at Fri, 30 Oct 2026 04:12:39 +0000. Up 42.07 seconds
EOF
echo "--- stage start times"
awk '/running/ { gsub(/[^a-z:-]/, "", $5); printf "%-16s %6.2f s\n", $5, $(NF-1) }' "$log"
echo "--- first real error"
grep -n -m1 -E '^(E:|Err:)' "$log" | cut -c1-60
echo "--- problem lines: $(grep -c -E '^(E:|Err:|Failed)' "$log")"
echo "--- finished at: $(awk '/finished at/ { print $(NF-1) }' "$log") s"

আউটপুট বলছে, modules:final শুরু হয়েছে 15.44 s-এ, আর 6:Err:1 http://mirror.example.net/... হলো ৬ নম্বর লাইনের প্রথম আসল error. মোট সমস্যার লাইন problem lines: 3: mirror-এর Err, apt-এর E:, আর unit না পাওয়ার Failed. শেষ লাইনটি দেখে ভুল করবেন না: finished at: 42.07 s মানে cloud-init থেমেছে, সফল হয়েছে তা নয়। সবসময় প্রথম error থেকে পড়া শুরু করুন, কারণ পরেরগুলো প্রায়ই তারই ফল। Log-এর লেখাটি নমুনা — version-ভেদে আলাদা হতে পারে।

৮ · Golden image pipeline আর versioning

Golden image হাতে বানালে আপনি আবার snowflake বানাচ্ছেন, শুধু একধাপ পিছিয়ে। তাই image-ও বানাতে হয় একটি স্বয়ংক্রিয় pipeline-এ। ধারণাটি নিচের ছবিতে; আসল হাতিয়ার Packer আসবে পর্ব ৫৬-তে। AWS-এর EC2 Image Builder আর Azure-এর VM Image Builder-ও একই কাজ করে।

Golden image-এর কারখানা

From a trusted base image to a tested, versioned image

  1. Base imageowner + ID pinবিশ্বস্ত উৎস
  2. Build VMpatch · hardenঅস্থায়ী server
  3. পরীক্ষাboot · scanব্যর্থ হলে বাতিল
  4. প্রকাশshop-web-v42version আর tag
  5. Promotetest → prodএকই image
ছবি ১৬.২ — Pipeline একটি অস্থায়ী VM চালায়, সেটি থেকে image বানায়, তারপর সেই VM মুছে ফেলে।
  1. Base: বিশ্বস্ত প্রকাশকের নির্দিষ্ট image থেকে শুরু। পর্ব ৮-এর hardening এখানেই একবার করে দিলে প্রতিটি server জন্ম থেকেই শক্ত হয়।
  2. Build: Patch, runtime, log ও monitoring agent বসানো; তারপর পরিষ্কার করা।
  3. পরীক্ষা: নতুন image থেকে সত্যিই একটি instance চালিয়ে দেখা, boot হয় কি না আর service ওঠে কি না। সাথে CVE scan.
  4. প্রকাশ: নাম, version আর উৎসের তথ্য লিখে রাখা। ব্যর্থ হলে image প্রকাশই হবে না।
  5. Promote: Test-এ যে image পাস করল, হুবহু সেটিই production-এ যাবে। Production-এর জন্য নতুন করে build নয়। পর্ব ৫৮-র “একবার build, সব জায়গায় deploy” এখানেও খাটে।

Version আর tag

Image একবার প্রকাশ হলে আর বদলাবে না। বদলাতে হলে নতুন version. প্রতিটি image-এর সাথে এমন তথ্য রাখুন, যা পরে তদন্তে কাজে লাগে:

Tagউদাহরণকোন প্রশ্নের উত্তর দেয়
versionshop-web-v42এখন কোনটি চলছে?
base-imageubuntu-24.04-20261015কোন ভিত্তি থেকে এসেছে?
git-commita1b2c3dকোন কোড থেকে বানানো?
built-on2026-10-30Image কত পুরোনো?
scan-resultcritical=0ছাড়পত্র আছে কি?

“সবচেয়ে নতুন image” বলে কিছু production-এ ব্যবহার করবেন না। Google Cloud-এর image family বা AWS-এর public parameter dev-এ সুবিধাজনক। কিন্তু production-এ নির্দিষ্ট ID থাকলে দুই সপ্তাহ পরের launch আজকের মতোই হবে, আর rollback মানে আগের ID-তে ফেরা।

৯ · Launch template, rollout আর image-এর অবসর

প্রতিবার হাতে দশটি setting দিলে ভুল হবেই। Launch template হলো instance চালুর একটি সংরক্ষিত ফর্ম: image, instance type, subnet, security group, instance role, user data, tag আর metadata-র নিয়ম। পর্ব ১৭-র autoscaling এই ফর্ম দেখেই নতুন instance বানায়। AWS-এর launch template-এ version থাকে। Google Cloud-এর instance template বদলানো যায় না, তাই নতুন একটি বানাতে হয়। Azure-এ scale set-এর model প্রায় এই কাজটিই করে।

# Version 8 = version 7 with only the image changed. Fake ids.
aws ec2 create-launch-template-version \
  --launch-template-name shop-web \
  --source-version 7 \
  --launch-template-data '{"ImageId":"ami-0fedcba9876543210"}'

aws ec2 modify-launch-template \
  --launch-template-name shop-web \
  --default-version 8

একটি গুরুত্বপূর্ণ কথা: Template বদলালে চলমান instance-এ কিছুই বদলায় না। সে শুধু পরের launch-এর ফর্ম। তাই পুরোনো server সরাতে আলাদা rollout লাগে। নিচে একটি জরুরি patch-এর পুরো যাত্রা দেখুন। ফ্লিটে আছে ৬টি instance, আর প্রতিবার ২টি করে বদলানো হবে।

shop-web ফ্লিট · লক্ষ্য ৬টি instance
Template → image
v41 instance
v42 instance
মোট চলছে
v42 image
v41 image
  1. CVE ঘোষণা। একটি গুরুত্বপূর্ণ library-তে দুর্বলতা জানা গেল। ফ্লিটের ৬টি instance চলছে v41 থেকে। কেউ server-এ ঢুকে হাতে patch করছেন না।
  2. Build. Pipeline নিজে চলল। অস্থায়ী VM-এ patch বসল, পরিষ্কার হলো, আর image তৈরি হলো। ফ্লিটে এখনো কোনো পরিবর্তন নেই।
  3. পরীক্ষা। v42 থেকে একটি পরীক্ষার instance চলল। Boot, service, CVE scan সব পাস। ব্যর্থ হলে এখানেই থামত, production ছুঁত না।
  4. Template বদল। Template-এর version 8 এখন v42-কে দেখাচ্ছে। লক্ষ করুন, v41 instance এখনো ৬টি: Template চলমান server বদলায় না।
  5. নতুন ২টি। আগে নতুন ২টি চালু হলো, মোট ৮। ক্ষমতা কখনো ৬-এর নিচে নামছে না। নতুনগুলোর health check পাস না হলে পুরোনো সরানো হবে না।
  6. পুরোনো ২টি সরল। Load balancer আগে সংযোগ শেষ হতে দিল, তারপর সরাল। মোট আবার ৬: v41 ৪টি, v42 ২টি।
  7. পরের ব্যাচ। একই নিয়মে আরও ২টি বদলাল। এখন v41 ২টি, v42 ৪টি। Error বাড়লে এখানেই থেমে আগের version-এ ফেরা যায়।
  8. শেষ ব্যাচ। সব ৬টি v42. v41 মোছা হয়নি, কারণ সমস্যা হলে সেখানেই ফিরতে হবে।
  9. অবসরের শুরু। নির্দিষ্ট সময় পর কোনো সমস্যা না থাকায় v41 deprecated হলো: নতুন launch-এ আর ব্যবহার হবে না। পরে যাচাই করে মোছা হবে।

এই পদ্ধতির নাম rolling replacement. Blue-green আর canary-র মতো অন্য কৌশল পর্ব ৫৯-এ। AWS Auto Scaling-এর instance refresh, Azure scale set-এর rolling upgrade আর Google-এর managed instance group-এর rolling update এই কাজটিই স্বয়ংক্রিয়ভাবে করে।

Patch-এর ছন্দ

  • নিয়মিত: ধরুন প্রতি মাসে একটি নতুন image, base হালনাগাদ করে। ছন্দটি ঝুঁকি আর দলের সামর্থ্য দেখে ঠিক করুন।
  • জরুরি: গুরুতর CVE-তে নিয়মিত সময়ের অপেক্ষা না করে একই pipeline চালান। সে জন্যই pipeline-টি দ্রুত আর স্বয়ংক্রিয় হওয়া দরকার।
  • বয়সের সীমা: কোনো instance বেশি দিন চললে তাকে বদলে দিন। AWS Auto Scaling-এ maximum instance lifetime ঠিক এই কাজ করে।
  • Boot-এ upgrade নয়: User data-তে package_upgrade: true দিলে মঙ্গলবারের instance আর বুধবারের instance আলাদা হতে পারে। Boot-ও ধীর হয়।

Image-এর অবসর

পুরোনো image জমতে থাকলে দুটি সমস্যা হয়: কেউ ভুলে অরক্ষিত পুরোনো image চালিয়ে দেন, আর তার storage-এর বিল আসে। ধরুন ৪০ GB-র ২৪টি image, আর প্রতি GB-র দাম মাসে ধরে নেওয়া $০.০৫। সবচেয়ে খারাপ অবস্থায় ২৪ × ৪০ × ০.০৫ = মাসে $৪৮। Snapshot incremental হলে বিল কম হতে পারে, তবে provider-এর দাম নিজে দেখে নিন।

ধাপকী হয়কী যাচাই করবেন
ActiveLaunch-এ ব্যবহার হয়Scan পরিষ্কার কি না
Deprecatedনতুন ব্যবহার নিরুৎসাহিত, rollback-এর জন্য আছেকোন template এখনো একে দেখাচ্ছে
নিষ্ক্রিয়নতুন launch বন্ধকোনো autoscaling নির্ভর করছে কি না
মোছাImage আর snapshot মুছে ফেলাAudit-এর জন্য তথ্য রাখা আছে কি না

AWS-এ AMI deregister করলে চলমান instance-এর ক্ষতি হয় না, কিন্তু পেছনের snapshot সাধারণত নিজে থেকে মোছে না, তাই বিল চলতেই থাকে। Google Cloud-এ image-এর DEPRECATED, OBSOLETE আর DELETED অবস্থা আছে। Azure Compute Gallery-তে version-এ end-of-life তারিখ দেওয়া যায়। মোছার আগে দেখে নিন, কোনো launch template সেই image-কে দেখাচ্ছে কি না; নইলে পরের autoscaling ব্যর্থ হবে।

Drift: হাতে রাখা বনাম image দিয়ে বদলানো

Image-ভিত্তিক বদলানোর আসল লাভ সময়ের সাথে বোঝা যায়। নিচের সিমুলেটরে ২০টি করে server-এর দুটি ফ্লিট, ১২ মাস। ধরে নেওয়া হয়েছে, প্রতি মাসে একটি patch আসে, হাতে রাখা ফ্লিটে প্রতিটি server-এ তা বাদ পড়ার সম্ভাবনা ১০%. আর দুই ফ্লিটেই প্রতিটি server-এ মাসে ৩৫% সম্ভাবনায় কেউ একটি জরুরি হাতের পরিবর্তন করেন। Image ফ্লিট প্রতি মাসের শেষে পুরোটা বদলে যায়।

// Drift over 12 months: 20 hand-kept servers vs 20 replaced from a monthly image.
function mulberry32(a) { return () => { a = a + 0x6D2B79F5 | 0;
  let t = Math.imul(a ^ a >>> 15, 1 | a);
  t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
  return ((t ^ t >>> 14) >>> 0) / 4294967296; }; }
const rnd = mulberry32(16);
const SERVERS = 20, HOTFIX = 0.35, MISS = 0.10; // assumed monthly chances
let hand = Array.from({ length: SERVERS }, () => new Set());
let image = Array.from({ length: SERVERS }, () => new Set());
let wanted = new Set(), next = 0;
const diff = s => [...s].filter(k => !wanted.has(k)).length
  + [...wanted].filter(k => !s.has(k)).length;
const avg = fleet => fleet.reduce((sum, s) => sum + diff(s), 0) / fleet.length;
const kinds = fleet => new Set(fleet.map(s => [...s].sort().join())).size;

console.log("month | hand: kinds avg-diff | image: before replace -> after");
for (let m = 1; m <= 12; m++) {
  const patch = "patch" + m; wanted = new Set(wanted).add(patch);
  for (const s of hand) if (rnd() >= MISS) s.add(patch);  // someone may skip one
  for (const s of image) s.add(patch);                    // patch came in the image
  for (const fleet of [hand, image]) for (const s of fleet)
    if (rnd() < HOTFIX) s.add("hotfix" + next++);         // unique hand edit
  const before = [kinds(image), avg(image).toFixed(1)];
  image = image.map(() => new Set(wanted));               // replace all from image
  if (m % 2 === 0) console.log(String(m).padStart(5), "|",
    String(kinds(hand)).padStart(5), avg(hand).toFixed(1).padStart(8), "|",
    "kinds", String(before[0]).padStart(2), "diff", before[1], "-> kinds",
    kinds(image), "diff", avg(image).toFixed(1));
}

kinds মানে ফ্লিটে কত রকম আলাদা server আছে, আর diff মানে চাওয়া অবস্থা থেকে গড়ে কত অমিল। ১২ মাসে হাতে রাখা ফ্লিট: 12 | 20 5.5, মানে ২০টি server-ই আলাদা, আর গড়ে সাড়ে ৫টি অমিল। শুরুতে মাস ২-এ ছিল 12 0.7. অমিল প্রায় সরলরেখায় বাড়ছে, কারণ কিছুই একে কমায় না। Image ফ্লিটেও মাসের মধ্যে হাতের পরিবর্তন হয় (kinds 8 diff 0.3), কিন্তু প্রতি মাসে তা শূন্যে ফেরে: kinds 1 diff 0.0. দামটিও দেখুন: হাতের জরুরি সংশোধন pipeline-এ না তুললে replacement-এ তা হারিয়ে যায়। তাই জরুরি কাজ হাতে করলেও পরে নকশায় তুলতে হয়।

আরও গভীরে: cloud-init নতুন instance চিনে datasource-এর instance ID দিয়ে, আর /var/lib/cloud/instance হলো সেই ID-র ফোল্ডারের একটি symlink. কোনো cloud ছাড়াই পরীক্ষা করতে NoCloud datasource আছে, যেখানে user data একটি স্থানীয় disk থেকে আসে। Fedora CoreOS আর Flatcar-এর মতো OS প্রথম boot-এ Ignition ব্যবহার করে, যা সফল না হলে boot-ই থামিয়ে দেয়। Snapshot থেকে বানানো disk-এর data প্রায়ই প্রথম পড়ার সময় ধীরে ধীরে আসে, তাই নতুন volume-এর প্রথম I/O আর boot ধীর হতে পারে।

পরীক্ষা ও ইন্টারভিউতে:

  • Snapshot আর image-এর পার্থক্য? Snapshot একটি disk-এর ছবি; image-এ এর সাথে boot-এর তথ্য ও অনুমতি থাকে।
  • runcmd আর bootcmd-এর পার্থক্য? bootcmd প্রতি boot-এ শুরুর দিকে; runcmd প্রতি instance-এ একবার, শেষের দিকে।
  • Bake কখন, bootstrap কখন? দ্রুত scale দরকার হলে bake; ঘন ঘন বদলানো বা অল্প দিনের server-এ bootstrap; বাস্তবে প্রায়ই দুটির মিশ্রণ।
  • Secret কোথায় রাখবেন? Secret manager-এ। Instance role দিয়ে চলার সময় পড়বেন, user data বা image-এ নয়।
  • Immutable infrastructure মানে? চলমান server-এ পরিবর্তন না করে নতুন image থেকে বদলে ফেলা।

১০ · AI-এর যুগে image আর cloud-init

AI খুব দ্রুত cloud-config লিখে দিতে পারে। কিন্তু সেটি ভুল হলে ব্যর্থতা দেখা যাবে boot-এর সময়, প্রায়ই নিঃশব্দে। Image-এর ভুল আবার একশো server-এ একসাথে নকল হয়। এই কাজে AI সাধারণত যে ভুলগুলো করে:

AI-এর ভুলফলযাচাই
User data-তে password বা API_KEYMetadata, log আর template-এ secret ফাঁসSecret manager আর instance role
Owner ছাড়া নাম দিয়ে “latest” imageঅচেনা image চলার ঝুঁকিOwner filter আর ID pin
নিজের বানানো cloud-config keyKey নিঃশব্দে উপেক্ষিত হতে পারেcloud-init schema আর official docs
SSH-এর জন্য 0.0.0.0/0Port সবার জন্য খোলাSecurity group, session manager
curl ... | bash, chmod 777অযাচাই করা কোড root হিসেবে; সবার লেখার অধিকারChecksum, নির্দিষ্ট version, কম অনুমতি
Instance বা image মোছার কমান্ড, সতর্কতা ছাড়াProduction হারানো, rollback বন্ধID নিজে পড়ুন, আগে --dry-run

AI-কে কাজ দেওয়ার সময় সীমাগুলো আগেই বলে দিন, আর শেষে তাকে দিয়ে নিজের ঝুঁকি লেখান। নিচের prompt-এ কোনো আসল account ID, key বা IP দেওয়া নেই, আপনিও দেবেন না।

তুমি একজন সতর্ক DevOps প্রকৌশলী। Ubuntu 24.04-এর জন্য একটি #cloud-config লেখো, যা nginx বসাবে আর /healthz পথে 200 দেবে। শর্ত: ১. কোনো secret, password বা key থাকবে না। Secret লাগলে শুধু তার নাম লিখবে। ২. প্রতিটি key cloud-init-এর official docs-এ থাকতে হবে। নিশ্চিত না হলে বলে দেবে। ৩. package_upgrade দেবে না। curl | bash ব্যবহার করবে না। ৪. কোন অংশ কোন stage-এ চলে আর কতবার চলে, একটি ছোট তালিকায় বলো। ৫. শেষে বলো, boot ব্যর্থ হলে কোন log দেখব, আর কীভাবে cloud-init schema দিয়ে যাচাই করব। আউটপুট শুধু YAML আর ঐ তালিকা। আমি এটি একটি throwaway instance-এ পরীক্ষা করব।

AI-এর লেখা user data হাতে পেলে — চেকলিস্ট

ভাবনার প্রশ্ন

প্রতিটি প্রশ্ন নিয়ে নিজে কিছুক্ষণ ভাবুন — তারপর “→ উত্তর” চাপুন।

প্র ০১“Pets vs cattle” কি সব server-এ খাটে? Database server কি সত্যিই ফেলে দেওয়ার মতো?

তুলনাটি চমৎকার, কিন্তু একে সর্বজনীন নিয়ম ভাবলে বিপদ। Randy Bias নিজেও পরে লিখেছেন, তুলনাটি ঠিকভাবে ব্যবহার করতে হয়: এটি মূলত নকশার ধরনের কথা বলে, মানে একটি একক machine-এর নষ্ট হওয়াকে সিস্টেম স্বাভাবিক ঘটনা হিসেবে মেনে নেয় কি না।

Web server সহজেই পালের প্রাণী, কারণ সেটি stateless: ভেতরে এমন কিছু নেই যা অন্য কোথাও নেই। Database-এ আছে data, যা নকল করা ধীর আর হারানো ভয়ংকর। তাই পুরো database server হুট করে মুছে দেওয়া যায় না।

তবে “কখনো নয়”, এই উত্তরও ঠিক নয়। আসল কৌশল হলো state আর compute আলাদা করা। Data থাকবে আলাদা disk, replica বা managed সেবায় (পর্ব ২২)। তাহলে যে machine-এ database চলছে, সেটি বদলানো যায়। Replica-কে নতুন image দিয়ে বদলে, তাকে primary করে, পুরোনোটি সরানো হলে একটি পোষা server-কেও পালের মতো সামলানো যায়। Kubernetes-এর StatefulSet-ও (পর্ব ৪৭) একই চিন্তার একটি রূপ: পরিচয় স্থায়ী, machine নয়।

আরেকটি ব্যতিক্রম হলো দামি বিশেষ হার্ডওয়্যার, লাইসেন্সের সাথে বাঁধা পুরোনো ব্যবসায়িক সিস্টেম আর legacy app. এগুলো বাস্তবে পোষা থাকবে, আর সেটি লজ্জার নয়। তাদের জন্য ভালো backup, runbook আর পরিষ্কার পুনরুদ্ধার প্রক্রিয়া দরকার (পর্ব ২৪)। শব্দটির একটি নরম দিকও আছে: অনেকে প্রাণীর তুলনাটি অপছন্দ করেন, তাই কোথাও “disposable” বা “replaceable” শব্দ ব্যবহার হয়।

মূল উপলব্ধি: প্রশ্নটি “server পোষা না পালের” নয়। প্রশ্ন হলো, “এই server-টি এখনই হারালে আমরা কত দ্রুত, কত কম ক্ষতিতে আগের অবস্থায় ফিরতে পারব?”

প্র ০২Log4Shell-এর মতো জরুরি দুর্বলতায় কোনটি দ্রুত: চলমান server-এ patch, নাকি নতুন image?

২০২১ সালের ডিসেম্বরে Log4j library-তে Log4Shell (CVE-2021-44228) প্রকাশ পেলে পৃথিবী জুড়ে দলগুলো একটি প্রশ্নের মুখে পড়ে: “আমাদের কোথায় কোথায় এটি আছে?” প্রশ্নটি শুনতে সহজ, কিন্তু হাতে রাখা ফ্লিটে এর উত্তর পাওয়াই দিনের পর দিন লাগতে পারে। কারণ প্রতিটি server-এর ইতিহাস আলাদা।

Image-ভিত্তিক ফ্লিটে উত্তরটি একটি ডেটা অনুসন্ধানের মতো: কোন image-এ এই library আছে, আর সেই image কোন কোন instance চালাচ্ছে। Tag আর software তালিকা (SBOM, পর্ব ৪১) এখানে খুব কাজে লাগে।

গতির হিসাবে দুটি পথের নিজস্ব শক্তি আছে। হাতে বা Ansible-এ চলমান server patch করা প্রথম ঘণ্টায় দ্রুত হতে পারে, কারণ build, পরীক্ষা আর rollout-এর অপেক্ষা নেই। কিন্তু সব server-এ ঠিকমতো হলো কি না নিশ্চিত হওয়া কঠিন, আর একটি ব্যর্থ মাঝপথের অবস্থা তৈরি হতে পারে। নতুন image-এর পথে শুরুতে সময় লাগে, কিন্তু একবার pipeline চললে প্রতিটি server-এর অবস্থা নিশ্চিত, আর rollback-ও সহজ।

Netflix ২০১৩ সালে নিজের ব্লগে Aminator নামে AMI বানানোর হাতিয়ার প্রকাশের কথা জানিয়েছিল, যেখানে প্রতি release-এ baked image ব্যবহারের বর্ণনা ছিল। এমন দলের কাছে জরুরি patch কোনো বিশেষ ঘটনা নয়, প্রতিদিনের একই পথ। এখানেই আসল শিক্ষা: যে পথ প্রতিদিন চলে, জরুরি সময়ে সেটিই নির্ভরযোগ্য। বছরে একবার ব্যবহার হওয়া জরুরি প্রক্রিয়া ঠিক দরকারের সময় ভেঙে যায়।

বাস্তবে অনেক দল দুটিই রাখে: খুব জরুরি হলে আগে সাময়িক ব্যবস্থা (যেমন WAF-এর নিয়ম বা config পরিবর্তন), তারপর স্থায়ী image.

মূল উপলব্ধি: নিরাপত্তার গতি নির্ভর করে জরুরি মুহূর্তের চেষ্টার ওপর নয়, স্বাভাবিক দিনের pipeline-এর ওপর। আগে জানা, পরে দ্রুত বদলানো, এই দুটোই image নকশায় পাওয়া যায়।

প্র ০৩Metadata সেবা তো শুধু VM-এর ভেতর থেকে পাওয়া যায়। তাহলে সেখানে secret থাকলে ক্ষতি কী?

“শুধু ভেতর থেকে” কথাটি শুনতে নিরাপদ। কিন্তু একটি web app যদি ব্যবহারকারীর দেওয়া URL নিজে পড়ে, তবে “ভেতরের” অনুরোধ তৈরি করছে app নিজেই। এই ধরনের ত্রুটিকে বলে Server-Side Request Forgery বা SSRF. OWASP Top 10 (২০২১)-এ এটি আলাদা একটি বিভাগ।

২০১৯ সালে Capital One-এর বড় data ফাঁসের প্রকাশ্য বিবরণে এই ধরনটি উঠে আসে: ভুলভাবে সাজানো একটি firewall-এর মাধ্যমে metadata সেবা থেকে role-এর credential পাওয়া গিয়েছিল, আর তা দিয়ে storage-এর data পড়া হয়েছিল। ঘটনাটি মার্কিন আদালতের নথি ও সংবাদে এসেছে, আর এর বিস্তারিত নিয়ে নানা বর্ণনা আছে। মূল শিক্ষাটি প্রকৌশলের: সুরক্ষার একটি স্তরকে একমাত্র ভরসা ভাবলে সেটি ভেঙে গেলেই সব শেষ।

এর কয়েক মাস পরে, ২০১৯ সালের শেষ দিকে, AWS IMDSv2 আনে। এতে metadata পড়ার আগে একটি session token নিতে হয়, যা সাধারণ SSRF-এর জন্য কঠিন। Hop limit কম রাখলে container-এর ভেতর থেকেও পৌঁছানো সীমিত হয়। Azure আর Google Cloud-ও বিশেষ header চায়, যাতে সাধারণ অনুরোধ আটকে যায়।

User data-র ক্ষেত্রে আরও একটি কথা। Role-এর credential অন্তত অস্থায়ী, আর নিজে নিজে বদলায়। User data-র password কিন্তু স্থায়ী, আর অনেক সময় বছরের পর বছর একই থাকে। তাছাড়া user data সেই instance-এর সব সাধারণ process-এর হাতের নাগালে থাকতে পারে, তাই একটি দুর্বল library-র মাধ্যমেও তা বেরিয়ে যেতে পারে।

প্রতিরক্ষা তাই স্তরে স্তরে: IMDSv2 বাধ্যতামূলক, role-এ least privilege, app-এ URL যাচাই, outbound traffic-এ সীমা, আর audit log-এ অস্বাভাবিক ব্যবহারের alert.

মূল উপলব্ধি: “কে পৌঁছাতে পারে” আর “কে নিজের ইচ্ছায় পৌঁছাতে পারে” এক কথা নয়। যে জিনিস app পড়তে পারে, app-এর দুর্বলতাও তা পড়তে পারে। তাই secret রাখুন স্বল্পমেয়াদি আর সংকীর্ণ অনুমতিতে।

প্র ০৪Docker আর Kubernetes-এর যুগে machine image কি অপ্রয়োজনীয় হয়ে গেছে?

না, শুধু তার কাজের জায়গা বদলেছে। Container image (পর্ব ৩৬–৩৭) আর machine image একই চিন্তা থেকে আসে: যা চলবে, তা আগে থেকে প্যাক করে নকল করা। পার্থক্য হলো সীমানায়। Container image-এ kernel থাকে না; সে host-এর kernel ব্যবহার করে। Machine image-এ kernel, boot-এর জিনিস আর পুরো OS থাকে।

কিন্তু container কোথায় চলে? শেষ পর্যন্ত একটি VM বা physical machine-এ। Kubernetes-এর প্রতিটি worker node একটি server, যার kernel, container runtime, kubelet আর নিরাপত্তা patch দরকার। Managed Kubernetes-এর সেবাগুলো node-এর জন্য বিশেষ image দেয়, আর node pool upgrade মানে আসলে নতুন image থেকে node বদলানো। অর্থাৎ এই পর্বের rolling replacement সেখানেও চলছে।

Container জনপ্রিয় হওয়ার পর VM image-এ কাজ কমেছে, এটি সত্য। আগে app সহ পুরো image bake করা হতো; এখন app যায় container-এ, আর image-এ থাকে শুধু OS ও runtime. এটি মূলত একটি half-baked নকশা, যেখানে পরিবর্তনের দ্রুত অংশটি অন্য স্তরে সরে গেছে। Launch-এর ধীর অংশ node-এ, আর app-এর গতি container-এ।

একই সঙ্গে node OS-গুলোও আরও ছোট আর immutable হচ্ছে। AWS-এর Bottlerocket, Flatcar বা Fedora CoreOS-এর মতো OS-এ root filesystem প্রায় read-only রাখার চেষ্টা থাকে, আর সেগুলোতে হাতে package বসানো স্বাভাবিক নিয়ম নয়। মানে snowflake হওয়ার সুযোগই কম।

তবে VM সরাসরি এখনো যথেষ্ট কারণে চলে: kernel-ঘনিষ্ঠ workload, লাইসেন্স, শক্ত isolation, পুরোনো app, GPU driver-এর জটিলতা। পর্ব ১৯-এ এই তুলনা আবার আসবে।

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

প্র ০৫Image-এর বিশ্বাস দুই দিকের: অন্যের image আপনার ক্ষতি করতে পারে, আবার আপনার image-ও অন্যের হাতে গিয়ে ক্ষতি করতে পারে। কীভাবে?

প্রথম দিকটি হলো অন্যের image নেওয়ার ঝুঁকি। Community image-এ মাঝে মাঝে পুরোনো, অরক্ষিত software পাওয়া যায়। ২০২০ সালে Mitiga নামের একটি নিরাপত্তা প্রতিষ্ঠান জানিয়েছিল, একটি community AMI-তে তারা cryptocurrency mining-এর প্রোগ্রাম পেয়েছে। আর ২০২৫ সালের whoAMI প্রকাশনা দেখিয়েছে, শুধু নাম দিয়ে image খোঁজার অভ্যাসই একটি ঝুঁকি হতে পারে। এর প্রতিকার সরল: বিশ্বস্ত প্রকাশক, owner filter, ID pin, আর নিজের scan.

দ্বিতীয় দিকটি হলো নিজের image ভাগ করার ঝুঁকি। ২০১১ সালের “AmazonIA” গবেষণায় (Bugiel ও সহলেখক, ACM CCS) আর ২০১২ সালের Balduzzi ও সহলেখকদের বিশ্লেষণে দেখা যায়, অনেক public AMI-তে রেখে যাওয়া SSH key, API credential বা shell history পাওয়া গিয়েছিল। প্রকাশকেরা হয়তো খেয়ালই করেননি। মুছে ফেলা ফাইলও সব সময় disk থেকে পুরোপুরি যায় না, কারণ block level-এ তা পড়া যেতে পারে।

একটি golden image-এর ঝুঁকির মাপও বড়। একটি ক্ষতিকর বা ভুল জিনিস image-এ ঢুকলে তা একসাথে সব instance-এ ছড়ায়, মানে এর blast radius পুরো ফ্লিট। সেজন্য image pipeline নিজেই একটি উচ্চ-মূল্যের লক্ষ্য। ২০২৪ সালের xz ঘটনা (Linux-এর একটি compression library-তে লুকানো backdoor) মনে করিয়ে দেয়, base-এ আসা জিনিসও পরীক্ষা ছাড়া নিরাপদ নয়।

করণীয়: pipeline-এর অনুমতি সীমিত রাখুন, build পরিবেশ অস্থায়ী রাখুন, উৎসের তথ্য লিখে রাখুন, আর SLSA-র মতো কাঠামোর ধারণা অনুসরণ করুন (পর্ব ৬১)। Image কখনো ভুল করে public করবেন না; AWS-এ account পর্যায়ে AMI-এর public ভাগ বন্ধ করার নিয়মও আছে। পরীক্ষার ধাপে secret scanning যোগ করা ভালো অভ্যাস।

মূল উপলব্ধি: Image শুধু একটি সুবিধা নয়, একটি সরবরাহ শৃঙ্খলের অংশ। উৎস যাচাই আর প্রকাশের আগে পরিষ্কার করা, দুটোই সমান জরুরি।

অনুশীলন

এগুলোর জন্য কোনো cloud account লাগবে না। কাগজ-কলম আর একটু ভাবনাই যথেষ্ট।

  1. হিসাব করুন: একটি bootstrap script-এ ৭টি স্বাধীন ধাপ আছে, প্রতিটির ব্যর্থতার সম্ভাবনা ০.৩%. (ক) ১,৫০০ launch-এ গড়ে কতটি ব্যর্থ হবে? (খ) চাহিদা বাড়ে প্রতি মিনিটে ৩টি instance সমান। Bootstrap-এ প্রস্তুত হতে ৫ মিনিট, half-baked-এ ১.৫ মিনিট লাগে। পূর্ণসংখ্যায় ওপরের দিকে নিয়ে ফাঁকা ক্ষমতার পার্থক্য কত? (গ) প্রতি instance-এর দাম ঘণ্টায় ধরে নেওয়া $০.০৮, মাসে ৭২০ ঘণ্টা হলে মাসিক পার্থক্য কত?

    (ক) ব্যর্থতা = ১ − ০.৯৯৭৭ ≈ ০.০২০৮, মানে প্রায় ২.০৮%. ১,৫০০ × ০.০২০৮ ≈ ৩১টি launch.

    (খ) Bootstrap: ৩ × ৫ = ১৫টি। Half-baked: ৩ × ১.৫ = ৪.৫, ওপরের দিকে ৫টি। পার্থক্য ১০টি।

    (গ) ১০ × ০.০৮ × ৭২০ = মাসে $৫৭৬। Image pipeline-এর খরচ এর চেয়ে কম হলে half-baked লাভজনক। মনে রাখবেন, সংখ্যাগুলো ধরে নেওয়া।

  2. ফলাফল অনুমান করুন: একটি Linux instance A-র cloud-config-এ আছে bootcmd, যা /data/log ফাইলে একটি লাইন B যোগ করে, আর runcmd, যা একটি লাইন R যোগ করে। ঘটনার ক্রম: A প্রথমবার চালু হলো → reboot → stop করে আবার start (ID একই)। তারপর reboot ছাড়াই, cloud-init পরিষ্কার না করে, A-র disk থেকে image বানানো হলো, আর সেই image থেকে একই user data দিয়ে নতুন instance C চালু হলো। A আর C-তে /data/log-এ লাইনের ক্রম কী?

    A: B R B B. প্রথম boot-এ দুটিই, তারপর reboot আর start-এ শুধু bootcmd.

    C: B R B B B R. Disk-এর ফাইলটি নকল হয়ে এসেছে, তাই আগের চারটি লাইন আছে। C-র instance ID নতুন, তাই per-instance কাজ আবার চলেছে।

    দুটি শিক্ষা: image-এ পুরোনো log ও state চলে আসে, আর per-instance মানে “এই ID-তে একবার”, “এই disk-এ একবার” নয়। তাই script idempotent হওয়া দরকার।

  3. সমালোচনা করুন: নিচের user data production-এর autoscaling-এর জন্য লেখা হয়েছে। অন্তত ৫টি সমস্যা ধরুন, আর ভালো নকশা প্রস্তাব করুন।
    #cloud-config
    package_update: true
    package_upgrade: true
    runcmd:
      - curl -s http://203.0.113.10/install.sh | bash
      - echo "DB_PASSWORD=Sample-Not-Real-123" >> /etc/shop.env
      - chmod 777 /etc/shop.env
      - systemctl restart shop
    • Secret: Password user data-তে, তাই metadata, log আর template-এ দেখা যাবে। Secret manager আর instance role ব্যবহার করুন।
    • chmod 777: সবাই ফাইলটি পড়তে ও লিখতে পারবে। 0600 আর নির্দিষ্ট owner দিন।
    • curl | bash over HTTP: Encrypted নয়, যাচাই নেই, version নেই। Artifact store থেকে নির্দিষ্ট version আর checksum মিলিয়ে নিন, অথবা image-এ bake করুন।
    • package_upgrade: Boot ধীর হয়, আর প্রতিটি instance আলাদা হতে পারে। Patch image-এ যাবে।
    • যাচাই নেই: এক লাইন ব্যর্থ হলেও পরেরটি চলবে। Health check আর ব্যর্থ boot-এর alert দরকার।
    • নির্ভরতা: একটি IP-র ওপর পুরো scale-out দাঁড়িয়ে। সেটি বন্ধ হলে নতুন server আসবে না।

    ভালো নকশা: Golden বা half-baked image, নির্দিষ্ট ID, ছোট user data যাতে শুধু environment আর version থাকবে, আর চলার সময় secret manager থেকে পড়া।

  4. কমান্ড লিখুন: একটি build pipeline-এ নতুন image-এর পরীক্ষার instance-এর ভেতরে চালানোর জন্য ছোট shell অংশ লিখুন। এটি cloud-init শেষ হওয়া পর্যন্ত অপেক্ষা করবে, ব্যর্থ হলে শেষ ২০ লাইন log দেখিয়ে থামবে, আর সফল হলে http://127.0.0.1:8080/healthz যাচাই করবে। ৪০টি instance-এর ফ্লিটে ২৫% ব্যাচে, প্রতি ব্যাচ ৬ মিনিটে, এরপর পুরো rollout কতক্ষণ নেবে?
    set -euo pipefail
    if ! cloud-init status --wait; then
      sudo tail -n 20 /var/log/cloud-init-output.log
      exit 1
    fi
    curl -fsS http://127.0.0.1:8080/healthz

    curl -f HTTP error-এ non-zero exit দেয়, আর set -e তখন script থামায়। Pipeline এই exit code দেখেই image প্রকাশ বাতিল করবে।

    Rollout: ৪০-এর ২৫% = ১০টি প্রতি ব্যাচে, মোট ৪টি ব্যাচ। ৪ × ৬ = ২৪ মিনিট।

নিজেকে যাচাই করুন

ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।

ক্রমএকটি নতুন Linux cloud instance-এর boot-এর ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)

  1. Firmware থেকে kernel পর্যন্ত boot
  2. Local: datasource খোঁজা, network-এর config লেখা
  3. Network: user data পড়া, write_files, hostname, SSH key
  4. Config: সময়, locale, package-এর উৎস
  5. Final: package install আর runcmd চালানো
  6. Load balancer-এর health check সফল

একটি cloud-init চালিত instance reboot করা হলো, instance ID বদলায়নি। ডিফল্ট আচরণে আবার কোনটি চলবে?

  • শুধু runcmd
  • শুধু bootcmd
  • bootcmd, runcmd আর packages সবই
  • কোনোটিই নয়

একটি দল user data-তে database password রাখতে চায়, যুক্তি: base64 করা আছে। সবচেয়ে ভালো জবাব কোনটি?

  • Base64 যথেষ্ট, কারণ সাধারণ চোখে পড়া যায় না
  • Password image-এ bake করা ভালো
  • User data-তে শুধু secret-এর নাম রেখে, instance role দিয়ে secret manager থেকে পড়া উচিত
  • User data ঠিক আছে, শুধু cloud-init-এর log বন্ধ করলেই হয়

একটি launch template-এর ডিফল্ট version-এ নতুন image বসানো হলো। ১০টি চলমান instance-এর কী হবে?

  • কিছুই হবে না; আলাদা rollout না করলে পুরোনো image-এই চলবে
  • সবগুলো নিজে থেকে reboot হয়ে নতুন image নেবে
  • চলমান disk-এ নতুন ফাইলগুলো copy হয়ে যাবে
  • পুরোনো ১০টি সাথে সাথে বন্ধ হয়ে যাবে

একটি bootstrap-এ ৫টি স্বাধীন ধাপ আছে, প্রতিটিতে ব্যর্থতার সম্ভাবনা ১%. একটি launch ব্যর্থ হওয়ার সম্ভাবনা প্রায় কত?

  • ১%
  • ০.০১%
  • ১০%
  • প্রায় ৪.৯%

একটি deployment script শুধু “ubuntu-24.04-*” নামের ধরন দিয়ে সবচেয়ে নতুন public image বেছে নেয়। প্রধান ঝুঁকি কী?

  • Boot একটু ধীর হতে পারে
  • একই নামের অচেনা প্রকাশকের image চলে যেতে পারে
  • অন্য region-এ এটি কখনো চলবে না
  • SSH key দেওয়া যাবে না

Package mirror বন্ধ থাকায় user data-র install ব্যর্থ হলো। তবু cloud console-এ instance “running” দেখাচ্ছে। কেন?

  • Console-এর তথ্য সবসময় ভুল
  • cloud-init ব্যর্থ হলে নিজে থেকে আবার চলতে থাকে
  • “running” শুধু VM-এর অবস্থা বলে, app প্রস্তুত কি না তা বলে না
  • Provider ধরে নিয়েছে install সফল হয়েছে, কারণ user data সঠিক ছিল

শব্দকোষ

লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব নতুন শব্দ এখানে একসাথে:

মনে রাখুন

  1. হাতে বানানো server snowflake হয়ে যায়। জ্ঞান রাখুন নকশায়, মানুষের মাথায় নয়।
  2. Image সবার জন্য এক ছাঁচ; user data প্রতি launch-এর নির্দেশ। বিশ্বাস করুন প্রকাশককে, নামকে নয়।
  3. cloud-init-এর ক্রম: Local → Network → Config → Final. bootcmd প্রতি boot-এ, runcmd প্রতি instance-এ একবার।
  4. যা ধীর আর কম বদলায় তা bake করুন; যা environment-ভেদে আলাদা তা boot-এ দিন; secret কখনোই নয়, role দিয়ে পড়ুন।
  5. Image-এ version দিন, pipeline-এ বানান, template বদলের পর rollout করুন, আর পুরোনো image অবসরে পাঠান।

পরের পর্বে: এখন আপনি একই server যত খুশি বানাতে পারেন। কিন্তু কখন কয়টি বানাবেন? পর্ব ১৭-তে autoscaling, health check আর instance group নিয়ে কথা হবে।

আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ

এই পর্বটি শেয়ার করুন:
এই পর্বে ভুল পেয়েছেন? জানান