Course-এর সূচি

14.1

Docker, Container বনাম VM, আর ECR

container কী, build-push-pull-run, আর ECR-এ image রাখা।

18 মিনিট পড়া + Animation

টিপস: ডটেড-আন্ডারলাইন করা যেকোনো শব্দে ট্যাপ বা Hover করলে তার অর্থ দেখবে।

এই লেসন শেষে তুমি পারবে

  • Docker কী, সেটা বোঝাতে, আর একটা application container হিসেবে পাঠালে “আমার machine-এ তো চলে” সমস্যাটা কেন আর থাকে না, সেটা বলতে।
  • Image (জমাট package) আর container (তার একটা চলন্ত copy) আলাদা করে চিনতে, একটা Dockerfile পড়তে, আর image layer ও build cache বোঝাতে।
  • Build → push → pull → run চক্রটা ধরে এগোতে, আর image কোন কোন registry-তে থাকতে পারে বলতে: Docker Hub, Amazon ECR private আর ECR Public।
  • Container আর virtual machine-এর তুলনা করতে: shared kernel, চালু হওয়ার সময়, এক server-এ কয়টা ধরে, আর isolation।
  • EC2-তে হাতে Docker চালাতে, আর একশোটা container-এ গিয়ে সেটা কেন ভেঙে পড়ে, কেন orchestration (ECS, EKS) লাগে, বোঝাতে।
  • ECS, EKS, Fargate, ECR আর App Runner-কে AWS-এর container service-এর একটাই মানচিত্রে বসাতে।
  • Amazon ECR ঠিকভাবে ব্যবহার করতে: repository আর URI, IAM আর repository policy, get-login-password, tag বনাম digest, tag immutability, basic বনাম enhanced scanning, lifecycle policy, replication আর pull through cache।
  • VPC endpoint (বা NAT gateway) দিয়ে private subnet থেকে image pull করতে, আর CannotPullContainerError এলে কারণ খুঁজে বের করতে।

#Container কেন: “আমার machine-এ তো চলে”

ঢাকার একটা e-commerce shop-এর একজন developer নিজের laptop-এ নতুন checkout API-টা শেষ করল: Node.js 22, একটা image library-র নির্দিষ্ট একটা version, একটা নির্দিষ্ট folder-এ config file। সব চলছে। Operations team সেটা server-এ বসাল, যেখানে Node.js 18 বসানো, আর image library-র version অন্য। সাথে সাথে crash। ঈদের sale-এর দুই দিন আগে দুই team তর্ক করছে, কার machine-টা “ঠিক”।

আসলে application কখনোই শুধু code ছিল না। ছিল code, সাথে তার runtime, তার library, তার setting, আর operating system-এর যেসব file সে আশা করে। Container এই সবকিছু একসাথে বেঁধে পাঠায়।

#Docker কী

Docker হলো container-এ application বানানো, পাঠানো আর চালানোর একটা platform। ২০১৩ সালে বের হওয়া Docker Linux kernel-এর কিছু feature (process কী দেখতে পাবে তা আলাদা করার জন্য namespace, কতটা CPU আর memory নিতে পারবে তা বেঁধে দেওয়ার জন্য cgroup) এত সহজ করে দিল যে প্রত্যেক developer সেগুলো ব্যবহার করতে পারে। ২০১৫ থেকে এর format-গুলো OCI (Open Container Initiative)-এর খোলা standard, তাই Docker দিয়ে বানানো image containerd, Podman, ECS, Kubernetes বা Lambda-তেও চলে।

তুমি যা পাও:

  • সব জায়গায় একইভাবে চলে: container runtime আছে এমন যেকোনো machine, compatibility-র ঝামেলা নেই, আচরণ আগে থেকেই জানা।
  • যেকোনো language, যেকোনো stack: Node.js, Java, Python, PHP, Go, .NET; runtime শুধু একটা container দেখে।
  • Deploy আর দেখাশোনায় কম খাটুনি: একটাই জিনিস laptop থেকে test হয়ে production পর্যন্ত না বদলে যায়।
  • দ্রুত আর হালকা: একটা container মোটামুটি এক সেকেন্ডে চালু হয়, আর এক server-এ অনেকগুলো ধরে (নিচে “Docker বনাম virtual machine” দেখো)।

সাধারণ ব্যবহার: microservice (প্রতিটা service নিজের container-এ, নিজের সময়মতো deploy), on-premises application-কে নতুন করে না লিখে AWS-এ lift-and-shift, batch job, CI-এর build environment, আর machine learning model, তার ঠিক ঠিক library version সহ package করা।

#Image আর container এক জিনিস না

Note-এ “image” আর “container” প্রায় একই অর্থে চলে এসেছে। এ দুটোকে আলাদা রাখো; পরীক্ষা আর প্রতিটা error message আলাদা রাখে।

ImageContainer
এটা কীএকটা read-only package: file system-এর কয়েকটা layer আর metadata (কোন command চালু হবে, কোন port, কোন environment variable)একটা image-এর চলন্ত (বা থামানো) instance: নিজের আলাদা file system, network আর সীমা সহ একটা process
কোথায় থাকেএকটা registry-তে (Docker Hub, ECR) আর প্রতিটা host-এর local image cache-এএকটা host-এ, যতক্ষণ সেটা আছে
বদলায়?কখনো না। বদল মানে নতুন digest সহ নতুন imageলেখা যা কিছু, যায় তার নিজের পাতলা writable layer-এ
কয়টাএকটা image থেকে……যত খুশি container চালানো যায়, অনেক host-এ
আয়ুRegistry থেকে না মোছা পর্যন্তসরিয়ে ফেলা পর্যন্ত; তারপর তার writable layer শেষ

Programmer-এর ভাষায়: image হলো class, container হলো object। রাঁধুনির ভাষায়: image হলো মাপমতো উপকরণ ভরা, সিল করা একটা রেসিপির বাক্স; container হলো সেই বাক্স থেকে রাঁধা এক হাঁড়ি বিরিয়ানি। এক বাক্স থেকে দশ হাঁড়ি রাঁধা যায়, আর এক হাঁড়ি উল্টে পড়লে বাক্সের কিছুই বদলায় না।

শেষ সারিটা production-এ খুব জরুরি: application নিজের container-এর ভেতরে যা লেখে (upload করা file, একটা local database file, disk-এ log), container বদলানোর সাথে সাথে সব উধাও। যে data টিকে থাকতে হবে, তার জায়গা একটা volume বা managed service: EFS, EBS, S3 বা database (ECS task-এর volume নিয়ে আলোচনা লেসন ১৪.২-এ)।

নিজেকে যাচাই করোপ্রশ্ন

একজন operations engineer বলছে: আমরা একই image ১২টা server-এ deploy করেছি, তাই production-এ ১২টা image আছে। সঠিক কথাটা কোনটা?

#Dockerfile

Image বানানো হয় একটা Dockerfile থেকে, ছোট্ট একটা text file, যাতে কয়েকটা নির্দেশ লেখা থাকে। Checkout API-এর Dockerfile:

# 1. Base image: Linux file + Node.js 22, একটা registry থেকে
FROM public.ecr.aws/docker/library/node:22-alpine

WORKDIR /app

# 2. আগে dependency: package file না বদলালে এই layer আবার build হয় না
COPY package.json package-lock.json ./
RUN npm ci --omit=dev

# 3. Application code: প্রতি commit-এ বদলায়
COPY src/ ./src/

# 4. Metadata: app কোন port-এ শোনে, কোন user, কী চালু হবে
EXPOSE 3000
USER node
CMD ["node", "src/server.js"]
  • FROM একটা base image বেছে নেয় (এখানে Node.js-এর official image, Docker Hub-এর বদলে ECR Public থেকে)।
  • COPY আর RUN file যোগ করে আর command চালায়; প্রত্যেকটা একটা করে layer তৈরি করে।
  • EXPOSE, USER, CMD শুধু metadata যোগ করে: কোনো file যোগ করে না।

#Layer আর build cache

File বদলায় এমন প্রতিটা নির্দেশ একটা read-only layer বানায়, আগেরটার ওপর বসানো কিছু file-এর পরিবর্তন। প্রতিটা layer চেনা যায় তার content-এর hash দিয়ে, আর তাতে তিনটা বড় সাশ্রয় হয়:

  • Build cache। শুধু src/ বদলে আবার build করলে Docker base আর dependency-র layer আগের মতোই কাজে লাগায়, নতুন করে বানায় শুধু শেষেরগুলো। এজন্যই Dockerfile code-এর আগে package.json copy করে: ধীরগতির npm ci layer বেশির ভাগ build-এ আবার কাজে লাগে।
  • যা বদলেছে, শুধু সেটাই push আর pull। যে registry-তে base আর dependency layer আগে থেকেই আছে, সে বলে “layer already exists”; যায় শুধু নতুন code layer (কয়েক MB)। পুরোনো version আছে এমন host-ও download করে শুধু ওই layer-টা।
  • Disk-এ ভাগাভাগি। একই base layer-ওয়ালা image থেকে দশটা container চললেও প্রতি host-এ ওই base জমা থাকে একবারই।

Container চালু হওয়ার সময় runtime image-এর read-only layer-গুলোর ওপর একটা পাতলা writable layer বসিয়ে দেয়। এই layer, আর শুধু এই layer-টাই, container-এর নিজের, আর container-এর সাথেই মুছে যায়।

#Build, push, pull, run

পুরো Docker-এর কাজ চারটা ক্রিয়ায় ধরে যায়:

  1. Build

    docker build Dockerfile আর তোমার source folder মিলিয়ে তোমার machine-এ (বা একটা CI pipeline-এ) একটা image বানায়।

  2. Push

    docker push image-এর layer আর manifest একটা registry-র একটা repository-তে তুলে দেয়।

  3. Pull

    যেকোনো host-এ docker pull প্রথমে manifest নামায়, তারপর host-এর কাছে যে layer-গুলো এখনো নেই, সেগুলো।

  4. Run

    docker run image থেকে একটা container বানায়: একটা writable layer, আলাদা করা process, network আর সীমা, তারপর CMD-এর command চালু করে।

Build, push, pull, run

ঢাকার একটা online shop-এর checkout API developer-এর laptop থেকে Amazon ECR হয়ে একটা EC2 host-এ পৌঁছায়। প্রতিটা ধাপে কোন byte কোথায় যায়, আর layer cache কতটা বাঁচায়, দেখো।

পরিস্থিতি

Docker-এর চারটা কাজ, Dockerfile থেকে চলন্ত container পর্যন্ত।

DeveloperDockerfile
Base imagenode:22
Amazon ECRcheckout-example
EC2 hostDocker বসানো
ধাপ 1 / 6
1

Developer একটা Dockerfile লেখে: Node.js 22-এর base image থেকে শুরু, package.json copy, dependency install, code copy, তারপর server.js চালু।

#Image কোথায় থাকে: registry

Registry হলো image রাখার server; repository তার ভেতরে নাম দেওয়া একটা সংগ্রহ (সাধারণত প্রতি application-এ একটা), যেখানে একই image-এর অনেক version থাকে, tag আর digest দিয়ে চেনা।

Registryকে ব্যবহার করেকথা
Docker Hub (hub.docker.com)সবাই; Docker CLI-এর defaultবেশির ভাগই public repository, নানা technology আর operating system-এর official base image সহ (Ubuntu, MySQL, Node.js, nginx)। Anonymous আর free user-দের pull প্রতি IP address হিসেবে rate-limited
Amazon ECR, privateতোমার AWS accountIAM দিয়ে সুরক্ষিত private repository, তোমার account-এর প্রতিটা Region-এ; ECS, EKS, Lambda, App Runner-এর সাথে জোড়া
Amazon ECR Public (gallery.ecr.aws)Pull করতে পারে যে কেউ, publish করো তুমিpublic.ecr.aws/<alias>/-এর নিচে public repository; AWS আর অনেক vendor সেখানে publish করে, Docker-এর official image-এর copy সহ (public.ecr.aws/docker/library/…)
অন্যরাGitHub Container Registry, Quay, Azure Container Registry, নিজের বসানো registry…

একটা image-এর পুরো নাম দেখলেই বোঝা যায় সেটা ঠিক কোথায় থাকে:

অংশউদাহরণ
Registry (account + Region)111122223333.dkr.ecr.ap-southeast-1.amazonaws.com
Repositorycheckout-example
Tag (সরানো যায় এমন একটা লেবেল):1.4.2
বা digest (content-এর hash)@sha256:9f86d081884c7d65…

তাহলে checkout image-এর নাম 111122223333.dkr.ecr.ap-southeast-1.amazonaws.com/checkout-example:1.4.2। Registry ছাড়া নাম, যেমন nginx, মানে Docker Hub (docker.io/library/nginx), আর tag ছাড়া নাম মানে tag latest।

#Docker বনাম virtual machine

Docker একরকম virtualization technology, কিন্তু পুরোপুরি না।

Virtual machineContainer

  • আসল server-এর ওপর একটা hypervisor কয়েকটা guest operating system চালায়, প্রত্যেকটার নিজস্ব kernel।
  • প্রতিটা VM পুরো একটা OS boot করে: চালু হতে কয়েক মিনিট, তোমার app চলার আগেই কয়েক GB disk আর কয়েকশো MB RAM।
  • Isolation শক্ত: প্রতি VM-এ আলাদা kernel।
  • EC2 instance ঠিক এটাই: AWS Nitro hypervisor-এর ওপর একটা VM।
  • একটা host OS-এর ওপর container runtime (Docker daemon) কয়েকটা আলাদা করা process চালায়, যারা host-এর kernel ভাগাভাগি করে।
  • Boot করার মতো OS নেই: container চালু হয় মোটামুটি এক সেকেন্ডে, আর image প্রায়ই কয়েক দশ MB।
  • এক server-এ অনেক container: host-এর resource ভাগাভাগি হয়, তাই ঘনত্ব অনেক বেশি।
  • Isolation দুর্বল: সব container একই kernel-এর ওপর ভরসা করে। একে অপরকে বিশ্বাস করে না এমন tenant-দের বাড়তি isolation ছাড়া এক host-এ মিশিয়ো না।

AWS-এ সাধারণত দুটোই পাও: তোমার container চলে EC2 instance-এ (VM), আর Docker প্রতিটা instance-এ অনেকগুলো container গুঁজে দেয়। Fargate আরও এক ধাপ এগোয়: প্রতিটা Fargate task চলে নিজের আলাদা micro-VM-এ, অন্য কোনো task-এর সাথে kernel ভাগ করে না, তাই container-এর সুবিধা পাও VM-এর মতো isolation সহ (লেসন ১৪.২)।

Virtual machine, container আর image layer

প্রতিটা model-এ hardware আর তোমার application-এর মাঝখানে কী কী থাকে, আর একটা image কী দিয়ে তৈরি।

পরিস্থিতি

Infrastructureআসল server: CPU, memory, disk, network
Host OSএকটা operating system, একটা kernel
ধাপ 1 / 4
1

Operating system সহ একটা আসল server। AWS-এ এই স্তরটা AWS-এর নিজের, Nitro system-এর ভেতরে।

#EC2-তে হাতে Docker

#খালি জমি

একটা instance-এ নিজে container চালাতে:

# Amazon Linux 2023 instance-এ (SSH বা Session Manager দিয়ে)
sudo dnf install -y docker
sudo systemctl enable --now docker          # Docker daemon এখনই আর প্রতি boot-এ চালু
sudo usermod -aG docker ec2-user            # sudo ছাড়াই ec2-user docker চালাতে পারবে

# ECR-এ login (instance role-কে ECR pull-এর অনুমতি দিতে হবে), তারপর pull আর run
aws ecr get-login-password --region ap-southeast-1 | docker login --username AWS \
  --password-stdin 111122223333.dkr.ecr.ap-southeast-1.amazonaws.com
docker run -d --name checkout -p 80:3000 --restart unless-stopped \
  111122223333.dkr.ecr.ap-southeast-1.amazonaws.com/checkout-example:1.4.2

-p 80:3000 host-এর port 80-কে container-এর ভেতরের port 3000-এর সাথে জুড়ে দেয়। --restart unless-stopped Docker daemon-কে বলে, container-এর process বন্ধ হয়ে গেলে আবার চালু করতে। এক instance-এ একটা ছোট application-এর জন্য এটা ঠিক আছে। (Elastic Beanstalk-এর Docker platform, লেসন ৯.৪, ঠিক এই কাজটাই automatic করে দেয়।)

#১০০টা container-এ গিয়ে কেন ভেঙে পড়ে

এবার shop-এর ১২টা microservice, ১০টা instance জুড়ে ১০০টা container, আর সামনে ঈদের sale। হাতে চালালে কাউকে না কাউকে করতে হবে:

  • কোথায় বসবে ঠিক করা: কোন instance-এ CPU আর memory খালি, কোন host port এখনো খালি?
  • Crash হলে আবার চালু করা। --restart একই host-এ বন্ধ হয়ে যাওয়া process আবার চালায়, কিন্তু app সুস্থ কিনা জানে না (আটকে যাওয়া app চলতেই থাকে), আর host নিজেই মারা গেলে কিছুই করতে পারে না: ওই instance-এর সব container শেষ, অন্য কোথাও কেউ সেগুলো চালু করে না।
  • নতুন version deploy করা downtime ছাড়া: ১০০টা container ঠিক ক্রমে থামানো আর চালানো, নতুন version ব্যর্থ হলে আগেরটায় ফেরা।
  • Traffic অনুযায়ী scale করা: sale-এর আগে বেশি container, পরে কম।
  • Load balancer-এ জোড়া: প্রতিটা নতুন container-এর host আর port register করা, মরে যাওয়াগুলো সরানো।
  • প্রতিটা container-কে secret, log আর permission বুঝিয়ে দেওয়া।

Server-এ login করে করে এসব সামলে ওঠা অসম্ভব, আর এটা ঠিক ভেঙে পড়ে sale-এর মধ্যে রাত ৩টায়। সমাধান orchestration: তুমি ঘোষণা করো কী চলবে (“checkout:1.4.2-এর ৯টা copy, প্রত্যেকটা ০.৫ vCPU আর ১ GiB, এই load balancer-এর পেছনে”), আর একটা orchestrator দিনরাত বাস্তবকে সেই ঘোষণার সমান রাখে। AWS দুটো orchestrator দেয়: ECS আর EKS।

১০০টা container হাতে চালানো, নাকি orchestrator দিয়ে

ঈদের sale-এর আগে shop তার checkout container-গুলো তিনটা EC2 host-এ চালায়। প্রথমে তুমি নিজে SSH দিয়ে সামলাও; তারপর ECS তোমার হয়ে এগুলো চালু রাখে।

পরিস্থিতি

এক instance-এ এক app-এর জন্য ঠিক আছে।

তুমিSSH
Host ১
Docker বসাও
ধাপ 1 / 3
1

EC2 হলো একটা খালি জমি। তুমি SSH দিয়ে ঢোকো, dnf দিয়ে Docker বসাও, daemon চালু করো, তারপর ECR-এ login করো।

#AWS-এ container: পুরো তালিকা

Serviceএটা কীকখন বেছে নেবেলেসন
Amazon ECS (Elastic Container Service)AWS-এর নিজস্ব container orchestratorAWS-এর সাথে সবচেয়ে গভীর জোড়া আর সবচেয়ে কম শেখার ঝামেলায় container চালাতে১৪.২, ১৪.৩
Amazon EKS (Elastic Kubernetes Service)Managed Kubernetes (open source)আগে থেকেই Kubernetes চালাও, তার tooling চাও, বা lock-in এড়াতে চাও১৪.৪
AWS FargateContainer-এর জন্য serverless compute: সামলানোর মতো কোনো EC2 instance নেইECS আর EKS দুটোর সাথেই চলে: task-এর মাপ তুমি দাও, চালায় AWS১৪.২, ১৪.৪
Amazon ECR (Elastic Container Registry)Container image রাখে আর scan করেওপরের সবাই এখান থেকেই pull করেএই লেসন
AWS App RunnerSource code বা একটা image → একটা HTTPS web service, cluster একদম নেইসবচেয়ে সহজ প্রান্ত; এর বর্তমান অবস্থা দেখে নিয়ো১৪.৪

আরও দুই জায়গায় container চলে: Docker platform সহ Elastic Beanstalk (লেসন ৯.৪), আর AWS Lambda, যেটা ECR থেকে টেনে আনা ১০ GB পর্যন্ত container image হিসেবে package করা function চালাতে পারে (chapter ১৫)।

স্তর হিসেবে ভাবো: ECR image রাখে; ECS বা EKS ঠিক করে কী কোথায় চলবে আর সেটা চালু রাখে; নিচে compute দেয় EC2 বা Fargate।

#Amazon ECR: AWS-এর নিজের Docker Hub

Amazon Elastic Container Registry (ECR) হলো Docker আর OCI image (আর Helm chart বা signature-এর মতো অন্য OCI artifact)-এর জন্য একটা fully managed registry। Note বলছে এটা AWS-এর নিজের Docker Hub, শুরু হিসেবে কথাটা ভালো, তবে দুটো সংশোধন: এতে private আর public দুই রকম repository-ই আছে, আর এটা শুধু জমা রাখার চেয়ে অনেক বেশি কাজ করে।

#Private registry, repository আর storage

  • প্রতিটা AWS account-এর প্রতি Region-এ একটা private registry: <account>.dkr.ecr.<region>.amazonaws.com। তার ভেতরে তুমি repository বানাও (checkout-example, payments/ledger-example…)।
  • Private repository-তে পৌঁছানো যায় শুধু IAM credential দিয়ে।
  • Public repository থাকে ECR Public-এ, আর তালিকা থাকে ECR Public Gallery-তে (gallery.ecr.aws)। যে কেউ anonymous-ভাবে pull করতে পারে; open-source tool বা public SDK image তুমি সেখানে publish করো। এগুলো বানানো হয় us-east-1-এর API দিয়ে, তোমার compute যে Region-এই থাকুক।
  • Image layer পর্দার আড়ালে Amazon S3-এ রাখা হয়, তাই S3-এর durability পায়, আর জমা অবস্থায় encrypted থাকে (default-এ AES-256, নয়তো repository বানানোর সময় বেছে নেওয়া একটা KMS key; পরে বদলানো যায় না)।
  • তুমি টাকা দাও storage-এর (private: প্রায় USD 0.10 প্রতি GB-month) আর Region-এর বাইরে যাওয়া data-র। একই Region-এ EC2, Fargate, Lambda বা App Runner pull করলে খরচ নেই। ECR Public প্রতি মাসে ৫০ GB storage বিনা খরচে দেয়।

#Access control: IAM, repository policy আর login token

Note যেমন বলে, ECR “IAM দিয়ে সুরক্ষিত”। বাস্তবে তিন রকম policy আর একটা token:

ব্যবস্থাকোথায় লাগানোসাধারণ ব্যবহার
IAM identity policyএকটা user বা role: CI role, instance role, ECS task execution role, EKS node roleCI role push করতে পারে; runtime role শুধু pull। AWS managed policy: AmazonEC2ContainerRegistryPullOnly (শুধু pull, ২০২৪ থেকে), AmazonEC2ContainerRegistryReadOnly, AmazonEC2ContainerRegistryPowerUser
Repository policy (resource-based)একটা repositoryঅন্য account-কে pull করতে দেওয়া (shared tooling account থেকে prod account pull করছে), বা Lambda-র মতো কোনো service-কে pull করতে দেওয়া
Registry policyপুরো private registryএর ভেতরে cross-account replication allow করা, বা pull through cache-এর সীমা ঠিক করা

Docker CLI IAM বোঝে না। তাই আগে aws ecr get-login-password চালাও, যেটা GetAuthorizationToken API call করে আর একটা ১২ ঘণ্টা মেয়াদের token দেয়, যে IAM principal চাইল ঠিক তার permission নিয়ে; সেটা pipe করে দাও docker login --username AWS-এ। ECS, EKS আর Lambda নিজেদের role দিয়ে এটা তোমার হয়ে করে নেয়। ১৩ ঘণ্টা ধরে চলা কোনো pipeline হঠাৎ push করতে না পারলে বুঝবে token-এর মেয়াদ ফুরিয়েছে।

একটা pull-এ তিনটা action লাগে: ecr:GetAuthorizationToken (*-এর ওপর), তারপর repository-র ওপর ecr:BatchGetImage (manifest) আর ecr:GetDownloadUrlForLayer (layer)। Push-এ upload-এর action-গুলোও লাগে। কিছু আটকে গেলে slide-এর নিয়মটাই খাটে: permission error মানে কোথাও একটা policy নেই, হয় যে call করছে তার IAM-এ, নয়তো অন্য account-এর জন্য repository policy-তে।

#Tag, digest আর tag immutability

Tag (1.4.2, latest, prod) হলো সরানো যায় এমন একটা লেবেল, যেটা একটা image-এর দিকে নির্দেশ করে। Digest (sha256:…) হলো image manifest-এর নিজের hash: এটা কখনো বদলায় না, আর অন্য কিছুর দিকে কখনো নির্দেশ করতে পারে না।

Default-এ ECR-এর tag mutable: নতুন একটা image 1.4.2 নামে push করলে tag চুপচাপ সরে যায়, আর পুরোনো image হয়ে যায় untagged। এতে বিশ্রী চমক আসে: একই service-এর দুটো task, এক ঘণ্টার ব্যবধানে চালু হওয়া, দুটোই বলছে 1.4.2, অথচ চালাচ্ছে আলাদা code।

Repository-তে tag immutability চালু করো (--image-tag-mutability IMMUTABLE): তখন কোনো existing tag আবার push করলে ImageTagAlreadyExistsException দিয়ে ব্যর্থ হয়, তাই 1.4.2 মানে চিরকাল একই byte। কাজের নিয়ম:

  • Image-এ এমন tag দাও যা একটাই: version, Git commit hash, build number। latest deploy কোরো না; এটা নিছক একটা tag, “সবচেয়ে নতুন” না।
  • পুরো নিশ্চিত হতে digest দিয়ে deploy করো (checkout-example@sha256:…)। ECS deployment শুরুর সময় tag-কে digest-এ বদলে নেয়, তাই এক deployment-এর সব task একই image চালায়।
  • নতুন একটা option দিয়ে immutable repository-তেও wildcard দিয়ে অল্প কয়েকটা tag বাদ রাখা যায় (যেমন latest সরানো যাক) IMMUTABLE_WITH_EXCLUSION দিয়ে, আর mutable repository উল্টোভাবে কয়েকটা tag রক্ষা করতে পারে।

#Image scanning: basic আর enhanced

Note-এর পরীক্ষার কৌশল: “Docker image privately রাখো আর vulnerability-র জন্য scan করো” → Amazon ECR। ECR-এ দুই রকম scanning, registry level-এ repository filter দিয়ে ঠিক করা:

Basic scanningEnhanced scanning
ইঞ্জিনECR-এর নিজের, একটা CVE database সহ AWS native technologyAmazon Inspector
কী দেখেOperating system-এর packageOS package আর programming language-এর package (npm, pip, Maven…)
কখনPush-এর সময়, বা হাতে (প্রতিটা image ২৪ ঘণ্টায় সর্বোচ্চ একবার)Push-এর সময় আর সারাক্ষণ: নতুন CVE প্রকাশ পেলে image আবার scan হয়
FindingECR-এ (console, describe-image-scan-findings), আর scan শেষ হলে একটা EventBridge eventInspector আর ECR-এ, প্রতিটা নতুন finding-এ একটা EventBridge event, Security Hub
খরচবিনা খরচেটাকা লাগে, Inspector-এর মাধ্যমে

Basic scanning একটা মুহূর্তের ছবি: push-এর দিন যে image পরিষ্কার ছিল, পরের সপ্তাহে নতুন দুর্বলতা বের হলে সেটা আর দেখা হয় না। Enhanced scanning পাহারা চালিয়েই যায়, আর মাসের পর মাস production-এ থাকা image-এর জন্য একটা payment company-র ঠিক এটাই দরকার। Inspector নিজে নিয়ে বিস্তারিত লেসন ২১.৭-এ।

Scan নিজে থেকে কিছু আটকায় না। দুর্বল image-কে production-এ যেতে না দিতে চাইলে pipeline-কে finding পড়তে দাও (বা EventBridge event-এ সাড়া দিতে দাও), আর critical বা high finding থাকলে deployment ব্যর্থ করো।

#Lifecycle policy

প্রতি commit-এ push করা একটা CI pipeline repository-তে হাজার হাজার পুরোনো image জমিয়ে ফেলে, আর storage-এর bill হয় প্রতি GB-month হিসেবে। একটা lifecycle policy নিজে থেকেই পরিষ্কার করে, এমন সব rule দিয়ে:

  • ৭ দিনের বেশি পুরোনো untagged image expire করো (সরে যাওয়া tag-এর ফেলে যাওয়া image);
  • prod-* tag-ওয়ালা image-এর শুধু শেষ ৩০টা রাখো (imageCountMoreThan);
  • dev-* tag-ওয়ালা image push-এর ১৪ দিন পর expire করো (sinceImagePushed)।

Rule-গুলো একসাথে যাচাই হয়, প্রয়োগ হয় priority অনুযায়ী (ছোট সংখ্যা জেতে); মিলে গেলে image ২৪ ঘণ্টার মধ্যে expire হয়, আর প্রতিটা কাজ CloudTrail-এ লেখা থাকে। সবসময় আগে lifecycle policy preview চালাও: অসাবধান একটা rule production-এ চলা image-টাকেই expire করে দিতে পারে।

#Replication: cross-Region আর cross-account

ECR তোমার private registry থেকে image অন্য Region আর অন্য account-এ replicate করতে পারে। Registry level-এ একবার ঠিক করে দাও (২৫টা পর্যন্ত গন্তব্য, চাইলে শুধু নির্দিষ্ট নামের prefix-ওয়ালা repository):

  • Cross-Region: Singapore-এর registry প্রতিটা নতুন image Mumbai-তে copy করে, তাই সেখানকার disaster-recovery cluster নিজের Region থেকে দ্রুত pull করে, চালু হওয়ার সময় অন্য Region থেকে কিছু টানতে হয় না।
  • Cross-account: যে tooling account-এ CI push করে, সেটা image copy করে production account-এ। গন্তব্যের registry-তে একটা registry policy থাকতে হবে, যেটা source account-কে replicate করতে দেয় (ecr:ReplicateImage, আর repository নিজে থেকে বানাতে চাইলে ecr:CreateRepository)।

মনে রাখার কথা: copy হয় শুধু replication চালুর পরে push হওয়া image (পুরোনোগুলোর জন্য backfill লাগে); repository-র নাম একই থাকে; বেশির ভাগ image ৩০ মিনিটের মধ্যে পৌঁছায়; repository policy আর lifecycle policy replicate হয় না (নতুন repository-কে ঠিক setting দিতে repository creation template ব্যবহার করো); আর replication গন্তব্যে কখনো কিছু মোছে না।

#Pull through cache

একটা pull through cache rule তোমার private registry-কে একটা upstream registry-র caching proxy বানিয়ে দেয়। তুমি pull করো 111122223333.dkr.ecr.ap-southeast-1.amazonaws.com/docker-hub/library/nginx:1.27; প্রথমবার ECR image-টা Docker Hub থেকে আনে, তোমার জন্য বানানো একটা repository-তে রাখে, তারপর ফেরত দেয়; পরের pull-গুলো আসে তোমার registry থেকেই। ওই tag-এর নতুন version আছে কিনা, ECR upstream-এ দেখে ২৪ ঘণ্টায় সর্বোচ্চ একবার, আর upstream-এ পৌঁছাতে না পারলেও cache-এর copy দিয়ে যায়।

যেসব upstream চলে: ECR Public, Kubernetes registry আর Quay (credential লাগে না), Docker Hub, GitHub Container Registry, Azure Container Registry আর আরও কয়েকটা (credential থাকে এমন একটা Secrets Manager secret-এ, যার নাম শুরু হয় ecr-pullthroughcache/ দিয়ে), আর আরেকটা ECR registry। কেন ঝামেলা করবে:

  • Docker Hub-এর rate limit। একটা NAT gateway-র পেছনে কয়েকশো Fargate task একটাই public IP ভাগাভাগি করে, আর Docker Hub anonymous pull গোনে প্রতি IP হিসেবে: sale-এর সময় একটা scale-out limit-এ ধাক্কা খেয়ে toomanyrequests দিয়ে ব্যর্থ হতে পারে। Cache-এর মধ্য দিয়ে গেলে Docker Hub দিনে একটা pull দেখে।
  • Private subnet। প্রথম pull-এর পর task-রা internet ছাড়াই ECR-এর VPC endpoint দিয়ে pull করে (একদম প্রথম pull-এর জন্য internet-এ একটা পথ লাগে)।
  • Scanning আর নিয়ন্ত্রণ। Cache করা image তোমার registry-তেই থাকে, তাই তোমার নিজের image-এর মতোই scan হয়, নিয়মে বাঁধা থাকে।

Amazon ECR একটা image নিয়ে কী কী করে

একটা CI pipeline checkout image-টা Singapore-এর একটা private repository-তে push করে। Scanning, tag immutability, replication, pull through cache আর lifecycle দিয়ে পরিষ্কার করা, একে একে দেখো।

পরিস্থিতি

OS package-এর এককালীন check।

CI pipeline
Docker Hubupstream
Amazon ECRSingapore
push 1.4.2
ধাপ 1 / 4
1

Pipeline checkout:1.4.2 push করল। Repository-তে scan on push চালু, তাই image পৌঁছানো মাত্র ECR সেটা scan করে।

#Private subnet থেকে image pull

Container সাধারণত চলে private subnet-এ, public IP ছাড়া। Image pull করতে তবুও তাদের তিন জায়গায় পৌঁছাতে হয়:

ধাপAPIকোথায় পৌঁছাতে হয়
১. Login token নেওয়াGetAuthorizationTokenECR API, com.amazonaws.<region>.ecr.api
২. Image manifest আনাBatchGetImageDocker registry API, com.amazonaws.<region>.ecr.dkr
৩. Layer downloadGetDownloadUrlForLayer একটা Amazon S3 URL দেয়S3

Private subnet-কে এই নাগাল দেওয়ার দুটো উপায় (লেসন ৩.৪ আর লেসন ২২.২):

  • একটা public subnet-এ NAT gateway। সহজ, আর Docker Hub বা অন্য registry-তেও পৌঁছায়। কিন্তু প্রতিটা layer-এর প্রতিটা byte এর ভেতর দিয়ে যায়, প্রতি GB processed হিসেবে bill হয়, আর subnet বাইরের internet-এ যেতে পারে, যেটা অনেক security team মানে না।
  • VPC endpoint, internet একদমই না: দুটো interface endpoint, ecr.api আর ecr.dkr (private DNS চালু করে, আর একটা security group যেটা task থেকে আসা HTTPS, port 443, allow করে), সাথে subnet-গুলোর route table-এ একটা S3 gateway endpoint, কারণ layer আসে S3 থেকে। S3 gateway endpoint বিনা খরচে, আর বেশির ভাগ byte এটাই বয়।

ECS আরও কয়েকটা যোগ করে: awslogs driver দিয়ে log পাঠানো Fargate task-এর লাগে CloudWatch Logs endpoint (logs), secret পড়া task-এর লাগে Secrets Manager বা SSM endpoint; আর EC2 container instance-এর ECS service-এর সাথে কথা বলতে ECS endpoint-ও লাগে (লেসন ১৪.২)।

চেনা ভুলটা: কেউ দুটো ECR interface endpoint বানায়, NAT gateway সরিয়ে দেয়, আর S3 gateway endpoint-এর কথা ভুলে যায়। Login আর manifest কাজ করে, তারপর প্রতিটা layer download timeout হয়, আর task থামে CannotPullContainerError দিয়ে।

Private subnet থেকে image pull করা

Private subnet-এর একটা Fargate task-কে চালু হতে তিন জায়গায় পৌঁছাতে হয়: token-এর জন্য ECR API, manifest-এর জন্য registry, আর layer-এর জন্য S3। কোন পথে কাজ হয়, আর কোন পথ CannotPullContainerError-এ গিয়ে শেষ হয়, দেখো।

পরিস্থিতি

কাজ হয়, কিন্তু layer-এর প্রতিটা byte NAT দিয়ে যায়, bill-ও হয়।

ap-southeast-1
VPC
private subnet
Fargate task
NAT gateway
Amazon ECR
Amazon S3image layer
GetAuthorizationTokentoken
ধাপ 1 / 3
1

Task চালু হচ্ছে। Task execution role দিয়ে সে ECR API-এর কাছে login token চায়। Private subnet-এর default route একটা NAT gateway-র দিকে, তাই call-টা সেটা দিয়েই বের হয়।

নিজে নানা combination চালিয়ে দেখো:

এই task কি তার image pull করতে পারবে?

Checkout service-এর একটা Fargate task-কে 111122223333.dkr.ecr.ap-southeast-1.amazonaws.com/checkout-example:1.4.2 pull করতে হবে। তার network আর permission ঠিক করো, তারপর pull করে দেখো।

Subnet

NAT gateway-র route

ecr.api interface endpoint

ecr.dkr interface endpoint

S3 gateway endpoint

Execution role ECR থেকে pull করতে পারে

Repository-তে 1.4.2 tag আছে

তিনটা ধাপ ধরে এগোতে “Pull করে দেখো” চাপো।

#কোথায় গোলমাল হয়

লক্ষণসম্ভাব্য কারণসমাধান
CannotPullContainerError … i/o timeoutECR বা S3-এ যাওয়ার পথ নেই: NAT বা endpoint ছাড়া private subnet, বা interface endpoint-এর security group 443 আটকাচ্ছেNAT gateway, নয়তো ecr.api + ecr.dkr interface endpoint আর S3 gateway endpoint
Login আর manifest কাজ করে, layer download timeout হয়S3 gateway endpoint নেই (layer থাকে S3-এ)Private route table-এ S3 gateway endpoint যোগ করো
CannotPullContainerError … AccessDenied / 403যে role pull করছে (execution role, node role, instance role) তার GetAuthorizationToken, BatchGetImage বা GetDownloadUrlForLayer নেই; বা অন্য account-এর জন্য repository policy নেইএকটা pull policy লাগাও; অন্য account-এর জন্য repository policy যোগ করো
CannotPullContainerError … not foundRepository-র নাম, Region বা tag ভুল; বা lifecycle policy image-টা expire করে দিয়েছেURI ঠিক করো; lifecycle preview দেখো; immutable tag বা digest দিয়ে deploy করো
no matching manifest for linux/arm64 বা exec format errorশুধু x86_64-এর জন্য বানানো image Graviton (arm64)-এ চলছে, বা উল্টোটাMulti-architecture image বানাও (docker buildx --platform linux/amd64,linux/arm64)
Scale-out-এর সময় Docker Hub থেকে toomanyrequestsAnonymous pull-এর rate limit, সব task একটাই NAT IP ভাগ করছেECR pull through cache, নয়তো ECR Public থেকে base image
Push-এ no basic auth credentials, বা ১২ ঘণ্টা পর pipeline ব্যর্থLogin করা হয়নি, বা ১২ ঘণ্টার token-এর মেয়াদ শেষপ্রতিটা push-এর আগে get-login-password; ECR credential helper ব্যবহার করো
Push ব্যর্থ, ImageTagAlreadyExistsExceptionTag immutability চালু, আর tag-টা আগে থেকেই আছেপ্রতি build-এ নতুন tag (ঠিক এটাই উদ্দেশ্য)
এক service-এর দুটো task একই tag নিয়ে আলাদা code চালাচ্ছেদুই launch-এর মাঝখানে mutable tag overwrite হয়েছেTag immutability; একক tag; digest দিয়ে deploy
Deploy-এর পর upload করা file উধাওData লেখা হয়েছিল container-এর writable layer-এVolume বা managed storage (EFS, S3, একটা database)
পরিষ্কার scan-এর এক মাস পরেও production image দুর্বলBasic scan একবারের একটা ছবিEnhanced scanning (Inspector, সারাক্ষণ); নিয়মিত rebuild আর redeploy
Registry-র bill বেড়েই চলেছেপ্রতিটা CI build চিরকাল রাখা হচ্ছেLifecycle policy: untagged আর পুরোনো build expire (বা archive) করো

#System design-এ container আর ECR

  • একটাই জিনিস, অনেক environment। একবার build, একবার push, তারপর tag যোগ করে বা production account-এ replicate করে একই digest test থেকে staging হয়ে production-এ তোলো। যা test করেছ, ঠিক তাই চলে।
  • Build আর run-এর জন্য আলাদা account। CI push করে একটা tooling account-এ; production pull করে cross-account replication দিয়ে, বা শুধু-পড়ার অনুমতিওয়ালা একটা repository policy দিয়ে। Production-এর কোনো role হাতছাড়া হলেও সে কখনো push করতে পারে না।
  • Multi-Region। যে যে Region-এ চালাও (বা তোমার DR Region), সবখানে image replicate করো, যাতে scale-out বা failover কখনো অন্য Region-এর registry-র ওপর নির্ভর না করে।
  • Default-এ private। Task-রা private subnet-এ, ECR আর S3 endpoint সহ, image pull-এর জন্য NAT না, আর base image আসে pull through cache দিয়ে।
  • নিরাপদ supply chain। Push-এ scan, যা চলছে তার জন্য enhanced scanning, immutable tag, signing, আর একটা lifecycle policy, যাতে পুরোনো দুর্বল image পড়ে না থাকে।
  • কখন container বানাবে না। কোনো event-এ চলা একটা ছোট script প্রায়ই Lambda function হিসেবে সহজ; static website-এর জায়গা S3 আর CloudFront।

#নিজে করে দেখো

একটা repository বানাও, একটা image build করে push করো, scan করো, আর একটা lifecycle policy যোগ করো। Output ছোট করে দেখানো।

# ১. Immutable tag আর scan on push সহ একটা private repository
aws ecr create-repository --repository-name checkout-example \
  --image-tag-mutability IMMUTABLE \
  --image-scanning-configuration scanOnPush=true --region ap-southeast-1
# "repositoryUri": "111122223333.dkr.ecr.ap-southeast-1.amazonaws.com/checkout-example"

REG=111122223333.dkr.ecr.ap-southeast-1.amazonaws.com

# ২. Docker-কে registry-তে login করাও (token ১২ ঘণ্টা চলে)
aws ecr get-login-password --region ap-southeast-1 | \
  docker login --username AWS --password-stdin $REG
# Login Succeeded

# ৩. দুই CPU architecture-এর জন্য build, version দিয়ে tag, তারপর push
docker buildx build --platform linux/amd64,linux/arm64 \
  -t $REG/checkout-example:1.4.2 --push .
# => pushing layers ... done
# => pushing manifest for .../checkout-example:1.4.2@sha256:9f86d081...

# ৪. একই tag আবার push করলে ফিরিয়ে দেয় (immutability)
docker push $REG/checkout-example:1.4.2
# tag invalid: The image tag '1.4.2' already exists in the 'checkout-example' repository
#   and cannot be overwritten because the repository is immutable.

# ৫. Scan-এর finding পড়ো
aws ecr describe-image-scan-findings --repository-name checkout-example \
  --image-id imageTag=1.4.2 --region ap-southeast-1
# "imageScanStatus": { "status": "COMPLETE" },
# "findingSeverityCounts": { "HIGH": 1, "MEDIUM": 3 }

# ৬. Lifecycle: untagged ৭ দিন পর expire, শেষ ৩০টা release রাখো
aws ecr put-lifecycle-policy --repository-name checkout-example \
  --lifecycle-policy-text file://lifecycle.json --region ap-southeast-1

# ৭. Local-এ চালাও, তারপর এর layer-গুলো দেখো
docker run -d -p 8080:3000 $REG/checkout-example:1.4.2
docker image history $REG/checkout-example:1.4.2

lifecycle.json:

{
  "rules": [
    { "rulePriority": 1, "description": "Expire untagged after 7 days",
      "selection": { "tagStatus": "untagged", "countType": "sinceImagePushed",
                     "countUnit": "days", "countNumber": 7 },
      "action": { "type": "expire" } },
    { "rulePriority": 2, "description": "Keep the last 30 releases",
      "selection": { "tagStatus": "tagged", "tagPatternList": ["1.*"],
                     "countType": "imageCountMoreThan", "countNumber": 30 },
      "action": { "type": "expire" } }
  ]
}

Private subnet-এর জন্য তিনটা endpoint (ID-গুলো নিজেরগুলো দিয়ে বদলে নিয়ো):

for svc in ecr.api ecr.dkr; do
  aws ec2 create-vpc-endpoint --vpc-id vpc-0abc1234example --vpc-endpoint-type Interface \
    --service-name com.amazonaws.ap-southeast-1.$svc --private-dns-enabled \
    --subnet-ids subnet-0aaa1111example subnet-0bbb2222example \
    --security-group-ids sg-0ccc3333example
done
aws ec2 create-vpc-endpoint --vpc-id vpc-0abc1234example --vpc-endpoint-type Gateway \
  --service-name com.amazonaws.ap-southeast-1.s3 --route-table-ids rtb-0ddd4444example

#সারসংক্ষেপ

মূল কথা

  • Docker code-কে তার runtime, library আর setting সহ এমনভাবে বাঁধে যে সেটা যেকোনো machine-এ একইভাবে চলে: shipping container-এর ভেতরের restaurant। Format-গুলো খোলা OCI standard।
  • Image হলো Dockerfile থেকে বানানো read-only, স্তরে স্তরে সাজানো একটা package; container তার চলন্ত instance, নিজের writable layer সহ, যেটা container সরালেই হারায়।
  • Build → push → pull → run। Registry: Docker Hub (public, rate-limited), ECR private (IAM), ECR Public (gallery.ecr.aws)। Layer cache হয়, তাই push আর pull হয় শুধু পরিবর্তনটুকু।
  • Container বনাম VM: container host-এর kernel ভাগ করে, মোটামুটি এক সেকেন্ডে চালু হয়, ঘন হয়ে বসে; প্রতিটা VM নিজের OS চালায়। EC2 একটা VM; Fargate প্রতিটা task-কে নিজের micro-VM দেয়।
  • EC2-তে হাতে Docker (খালি জমি) এক app-এর জন্য চলে, ১০০টা container-এর জন্য না: কোথায় বসবে ঠিক করার কেউ নেই, host মরলে আবার চালু করার কেউ নেই, rolling deploy নেই, scaling নেই। Orchestration = ECS (AWS-এর নিজের) বা EKS (Kubernetes), EC2 বা Fargate-এর ওপর, ECR থেকে pull করে; সবচেয়ে সহজ বিকল্প App Runner।
  • ECR: private আর public repository, S3-এ রাখা, encrypted, IAM identity policy, সাথে repository policy (অন্য account) আর registry policy (replication); get-login-password দেয় ১২ ঘণ্টার token; permission error মানে কোনো policy নেই।
  • Tag সরে, digest সরে না। Tag immutability আর একক tag ব্যবহার করো; নিশ্চিত হতে digest দিয়ে deploy করো।
  • Scanning: basic (OS package, push-এ বা হাতে) বনাম enhanced (Amazon Inspector, OS আর language package, সারাক্ষণ)। “Image privately রাখো আর vulnerability-র জন্য scan করো” → ECR।
  • Lifecycle policy পুরোনো image expire (বা archive) করে; replication নতুন image cross-Region আর cross-account copy করে; pull through cache Docker Hub আর অন্যদের proxy হয়।
  • Private subnet থেকে pull-এ লাগে ecr.api + ecr.dkr interface endpoint আর একটা S3 gateway endpoint (বা একটা NAT gateway)। না হলে: CannotPullContainerError।