আপনার company অনেক বড় হয়ে গেছে। মোটামুটি প্রায় ২০০ জন engineer। হঠাৎ করেই একটা pandemic চলে এল। govt থেকে lockdown দিয়ে দিলো। এখন office-এ যেয়ে কাজ করার সুযোগ নেই। এখন সবাইকেই remotely বাড়ি থেকে কাজ করতে হবে। এখানে আপনার একটা লাভ হলো যে সব engineer-এরই একটা করে computer/laptop আছে। আর ১০-১২ জনের ছিল না, আপনি তাদের জন্য সেটার ব্যবস্থা করে দিলেন।
এভাবেই office শুরু হলো। কাজ চলতেছে। ১ মাস এই ভাবেই চললো। তারপর আপনি একটা জিনিস realize করলেন কাজের productivity কমে গেছে। আগে যেখানে ১ মাসে ১৫-২০ টা feature আরামসে complete হয়ে যেত, এখন ৪-৫ feature complete করতেই অবস্থা খারাপ হয়ে গেছে। বিষয়টা খুবই concerning। সবগুলো না হোক অন্তত ১২-১৫ টা feature তো complete করতে হবে।
এই বিষয়টা address করার জন্য আপনি সকল team leader-দের নিয়ে meet করলেন। একটা ২ ঘন্টার long meeting হলো। ভালো খারাপ সব নিয়েই কথা হলো। তবে ভালোর থেকে খারাপ বিষয়গুলোই বেশি ছিল। meeting-এর summary হচ্ছে এই রকম:
- "আমার machine-এ কাজ করে": এই simple line নাকি সবার মাঝে common হয়ে গেছে। মূল সমস্যা হলো যখন কেউ কোনো feature develop করে তখন অন্য আরেকজনের computer-এ properly run হয় না, configuration-এর problem। এই problem solve করতেই অবস্থা খারাপ। এই problem solve করার পর, যে develop করেছে তার machine থেকে আবার try করলে আবার same problem। এই ভাবেই দিনের অর্ধেক time চলে যাচ্ছে।
- এছাড়াও deploy করার সময়ও problem হচ্ছে। সেখানেও critical problem দেখা দিচ্ছে। deploy-এর সময় problem হচ্ছে। properly deploy হচ্ছে না।
আপনার একটা জিনিস মাথায় আসল, আগে office থাকতে তো এই রকম কোনো problem হয়নি তাহলে এখন এত problem হচ্ছে remotely কাজ করার জন্য? না, remotely কাজ করলে তো আর তাদের skill কমে যাবে না। বিষয়টা হচ্ছে আগে যখন সবাই office-এ কাজ করত তখন সবাই same configuration-এর machine use করত। সবার machine same ছিল। আর সবাই একসাথে কাজ করার কারণে কোনো সমস্যা হলে instant solve হয়ে যেত।
এটা কীভাবে solve করা যায়। সবাইকে তো আর same machine দিয়ে দেয়া যাবে না। আর সবার কাছে তো same type computer নেই। কেউ linux, কেউ windows আবার macOS use করে। এখন চাইলেই এটা করা যাবে না। এর থেকে better solution লাগবে।
solution কি আছে??
হ্যাঁ, অবশ্যই আছে। His name is Docker।
Docker
docker হলো একটা tool বা software, এটা use করে আপনার problem easily solve করা যায়। docker containerization নামে একটা feature আছে যেটা আপনার application-কে একটা isolated environment-এ run করবে। এই environment-টায় একটা dedicated operating system থাকে। এই operating system-এ outsider-এর কোনো effect থাকে না সেটা linux হোক, বা windows হোক, বা macOS হোক।
এখানে অনেকটা ship-এর কথা চিন্তা করতে পারেন। জাহাজে কিন্তু অনেক container থাকে। সেখানে কিন্তু একটা container অন্য একটা container-কে affect করে না। যে যার মতো। ভেতরে যা আছে সবকিছুই isolated। আর ship কিন্তু অনেকগুলো container একসাথে carry করতে পারে।
এখানে docker container হলো ship-এর container আর ship-টা হচ্ছে host machine। তবে আপনার laptop, বা computer বা macOS যেটাই হোক না কেন, সেটাই basically host machine।
docker-এর সবচেয়ে বড় সুবিধা হচ্ছে, আপনার machine-এ যা চলছে যেভাবে চলছে, ঠিক সেভাবে আপনার colleague-এর machine-এ, আবার production server-এ same ভাবেই run করবে। আর কেউ কিছু change করলেও same configuration-এর কারণে same result-ই পাবে। "আমার machine-এ কাজ করে" এটা থেকে মুক্তি পেয়ে যাবেন।
আপনি যদি docker-এ নতুন হন তাহলে একটু confusion হতে পারে। আমারও হয়েছিল। আস্তে আস্তে সবকিছু clear হয়ে যাবে।
Why not Virtual Machine?
আপনি যদি কখনো virtual machine use করে থাকেন তাহলে আপনার মনে এই question-টা আসতেই পারে। VM machine করলেই তো হয়। এত complicated জিনিসের বা কী দরকার। হ্যাঁ, অবশ্যই দরকার।
বিষয়টাতে যাওয়ার আগে docker কীভাবে কাজ করে সেটা জানার প্রয়োজন। Docker কোনো আলাদা Operating System run করে না। এটি Host machine-এর kernel share করে এবং শুধুমাত্র অ্যাপ্লিকেশনের জন্য প্রয়োজনীয় library ও dependency গুলো রান করে। আর host machine এর resource by default ভাবেই use হবে। এই architecture-এর কারণেই একটা docker container-এর size খুবই lightweight হয়। একটা typical nodejs এর docker image এর size 50-100mb এর মধ্যে হয়। তবে container রান করলে সেখানে মোটামুটি ৫০০-৭০০mb ram use হতে পারে।
vm-এ সবকিছুই তার own operating system + own kernel + more isolated environment। এই সমস্ত কারণে vm-এর size খুবই বেশি হয়। অনেক ক্ষেত্রে 5-10gb হয়ে যায়। আর startup time অনেক বেশি।
এখন চিন্তা করেন, সবাইকে যদি এই 5-10gb-এর vm-কে transfer করা যায়, setup করা ঝামেলা। আর suppose কেউ কোনো feature add করলো তখন তো পুরো vm-টাই transfer করা লাগবে। কী একটা pain!
এখানে docker help করে, আপনি চাইলে আপনার code দিয়ে আপনার নিজের machine-এই docker container build করতে পারেন। আপনার 700-800mb container transfer করা লাগবে না।
এই সমস্ত কারণেই মূলত docker-টা use করাই সবচেয়ে ভালো। এছাড়াও আরও অনেক সুবিধা আছে। যেমন ধরুন deployment একদম পানির মতো easy করে দিবে। CI/CD নিয়ে আলোচনা হবে তখন deployment নিয়ে কথা হবে।
Dockerfile: Recipe লিখুন
আপনারা যারা জীবনে একবার হলেও, computer-এ windows বা linux setup করেছেন তখন দেখবেন, আপনাকে অনেক কিছু set করে দিতে হয়। windows-এর কোন version use করবেন বা linux-এর মধ্যে কোন linux, ubuntu নাকি mint নাকি অন্য কিছু। এবং এই সবকিছু ঠিক করার পরে কিন্তু মোটামুটি অনেক time লাগে setup complete হতে।
কখনো ভেবে দেখেছেন এই সময়টাতে কী হয়? এই সময়েই মূলত OS-এর জন্য file-গুলো আসে সেগুলো ready করা হয় এবং আপনার system-এর drive-এ push করা হয়। নানারকম system preference আছে সেগুলো set করা হয়। যখন সবকিছু ঠিক হয়ে যায় তারপরে যেয়ে আপনি computer use করতে পারেন।
একটু আগে আমি বললাম যে docker operating system run করে application run করে। সে তো আর জানে না যে আমার কোন operating system use করতে হবে আর use করার পর application-টাকে কীভাবে setup করে run করতে হবে। তাকে তো বলে দিতে হবে। না হলে তো কিছুই বুঝবে না। For example, আপনার nodejs application run করার জন্য আপনার system-এ nodejs install থাকতে হবে। তা নাহলে তো সেই application run হবে না।
এটার জন্য একটা recipe বা configuration লিখতে হয়, যাকে বলে dockerfile। নিচে একটা dockerfile-এর example দেখতে পারবেন। ভয় পাওয়ার দরকার নেই আস্তে আস্তে শিখে যাবেন। খুব কঠিন কিছু না।
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/main.js"]
এখানে একটা nodejs application run করার জন্য লেখা হয়েছে। Node-20 এর alpine version-টা use করা হয়েছে এবং প্রয়োজনীয় সকল dependencies-গুলো setup করা হয়েছে।
Layer Caching: Build Fast করুন
উপরের dockerfile দেখে একটা question arise হতে পারে। যে এত line লেখার কী আছে। 3-4 line-এই তো শেষ করা যেতে পারে। এখানেই বিষয়টা সে docker-এর build process। dockerfile build হওয়ার time-এ line by line data cached করে রাখে। যেই line থেকে আবার নতুন করে build হবে। For example, আপনি যদি package.json file কোনো change না করে থাকেন তাহলে সেটা COPY . . থেকে নতুন build শুরু হবে। তার আগে যেগুলো build হয়েছে সেগুলো cached data থেকে use করবে। আর যদি package.json file-এ change করে থাকেন তাহলে ঐ line থেকে আবার নতুন করে build শুরু হবে। এই জন্যই মূলত dockerfile multiple line-এ লেখা হয়। এতে container-এর size কমে।
নিচের example দেখলে আরও ভালো বুঝতে পারবেন।
# ✅ dependencies আগে — কম পাল্টায়
COPY package*.json ./
RUN npm ci
# ✅ source code পরে — বেশি পাল্টায়
COPY . .
RUN npm run build
Docker Image
আপনি যখন কোনো OS setup করেন তখন আপনার কাছে একটা .img file থাকে। এই file-টা আপনার system computer-এ setup করার পরেই কিন্তু windows run হয়। এখানে বোঝার বিষয় হচ্ছে, এখানে এই .img file-টা কিন্তু একা একা চলতে পারবে না। বা আপনার ঐ .img-টা বাদেও শুধু hardware দিয়ে কাজ হবে না। মূলত operating system-এর সব files (kernel, system files, file systems etc.) এই .img file-এর মধ্যে থাকে, অনেকটা zip file-এর মতো করে একসাথে করে রাখা হয়।
docker-এর জন্য, only application run করার জন্য needed files-গুলো একসাথে করে একটা file create করা হয়। এইগুলো কে docker image বলে। এই image-গুলোকে একটা container-এর মধ্যে run হয়। আর container কীভাবে কাজ করে উপরে মোটামুটি বলেছি বুঝে গেছেন।
Dockerfile থেকেই এই image-গুলো create করতে হয়। তারপর docker-এর মাধ্যমে এগুলো run করতে হয়।
Multi-Stage Build: প্রোডাকশন ইমেজ সাইজ ৮০% কমান ⚡
একটি সাধারণ TypeScript বা modern Node.js app-কে বিল্ড করার জন্য TypeScript কম্পাইলার tsc, linter এবং অনেক devDependencies দরকার হয়। কিন্তু প্রোডাকশনে এই app এর build run করলেই হয়ে যায়।
এগুলোকে image-এ রেখে দিলে dokcer image এর size 700-800 mb হয়ে যায়। ক্ষেত্র বিশেষ আরও বেশিও হতে পারে। এই সমস্যার সেরা সমাধান হলো Multi-Stage Build। এখানে শুধু একটা stage থেকে আরেকটা stage-এ প্রয়োজনীয় ফাইলগুলা transfer হবে।
# --- Stage 1: Build Stage ---
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# --- Stage 2: Production Runtime Stage ---
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
# শুধু প্রোডাকশন ডিপেন্ডেন্সি ইনস্টল
COPY package*.json ./
RUN npm ci --omit=dev
# স্টেজ ১ থেকে শুধু বিল্ড হওয়া dist ফোল্ডার কপি
COPY --from=builder /app/dist ./dist
# Security: Root হিসেবে রান না করে non-root user ব্যবহার
USER node
EXPOSE 3000
CMD ["node", "dist/main.js"]
এর সবচেয়ে বড় সুবিধা হচ্ছে docker image-এর size ৭০-৮০% কমে যায়। এছাড়াও production-এর জন্য প্রয়োজনীয় ফাইলগুলোই image-এর ভেতরে থাকে।
Docker Compose: Multi-Service Local Dev
একটা জিনিস, আপনার application-এ কিন্তু অনেক configuration করতে হয়। এগুলো application-এর outside থেকে provide করতে হবে। না হলে একটা security risk তৈরি হতে পারে। এখন আপনি চাইলেই আপনার docker container প্রতিবার configurations type করে run করতে পারেন। কিন্তু যদি নতুন কোনো কিছু add করা লাগে বা delete করা লাগে তখন কী করবেন? তখন কিন্তু বিষয়টা pain হয়ে যাবে। এছাড়া আমি একটু বললাম যে, docker system resource use করে, সেখানেও তো limit করা লাগবে। এগুলো তো arbitrary ভাবে করা যাবে না। একটা proper way লাগবে।
এই problem-টাকে address করার জন্য, docker introduce করে, একটা orchestrator, যেটা হলো docker compose। একটা band-এ orchestrator কী করেন সেটা তো জানেন। তিনি instruction দেন কখন কোন instrument-টা বাজাতে হবে, কতক্ষণ বাজবে এবং কখন বন্ধ হবে etc. মানে তিনিই সবকিছু control করে থাকেন। docker compose-ও same কাজটাই করে থাকে।
docker compose মূলত image file run করে। আর মজার ব্যাপার হচ্ছে এই compose file দিয়েই docker image build করা যায়। আর সেই image-গুলো compose file দিয়েই run করা যায়। এছাড়াও existing image-ও run করা যায়।
services:
app: # application container
build: . # builidng with existing Dockerfile file
ports:
- "3000:3000" # mapping port 3000 of host to container
environment: # environment variables
DATABASE_URL: postgresql://postgres:password@db:5432/mydb
depends_on: # dependencies
- db
- redis
db: # container for database
image: postgres:16-alpine # existing external postgres image
environment: # environment variables
POSTGRES_PASSWORD: password
POSTGRES_DB: mydb
volumes:
- pgdata:/var/lib/postgresql/data
redis:
image: redis:7-alpine # external redis image
volumes:
pgdata: # volume data
এখানে PostgreSQL database and redis-এর জন্য external image use করা হয়েছে।
docker compose up -d
এই একটা command-এ সবগুলো container একসাথে run হবে। কোনো কিছু করা লাগবে না। এক বারে সবকিছু ready।
.dockerignore: অপ্রয়োজনীয় বাদ দিন
এই পর্যন্ত যখন এসেছেন তাহলে git সম্পর্কে অবশ্যই জানেন। use-ও করতে পারেন। একটা জিনিস খেয়াল করবেন, আপনি কিন্তু github-এ node_modules, configurations, .env, build files এগুলো push করেন না। কেন করেন না? কারণ এগুলো আপনি চাইলেই externally install করতে পারবেন। docker-এর ক্ষেত্রেও একই rules প্রযোজ্য।
git-এর জন্য .gitignore use হয়, docker-এর জন্য .dockerignore file use হয়।
অনেকে মনে করেন এটি শুধু ইমেজ সাইজ ছোট রাখার জন্য ব্যবহার করা হয়। কিন্তু এর চেয়েও বড় একটি কারণ রয়েছে। আপনার লোকাল ল্যাপটপ যদি macOS বা Windows হয়, আপনার লোকাল node_modules-এ থাকা লাইব্রেরিগুলো (যেমন bcrypt, sharp, prisma) আপনার নিজস্ব OS-এর আর্কিটেকচারে কম্পাইল করা থাকে। .dockerignore-এ node_modules না রাখলে COPY . . কমান্ডটি সেই ফাইলগুলো ডকারের Linux কন্টেইনারে কপি করে দেবে—যার ফলে কন্টেইনারে invalid ELF header বা আর্কিটেকচার মিসম্যাচ এরর দিয়ে অ্যাপ ক্র্যাশ করবে!
node_modules
.git
.env
*.log
dist
উপরের যে list-এ যেগুলো আছে এগুলোর খুব বেশি দরকার নেই। এখানে .env file-টাও skip করা হয়েছে, security risk এর জন্য।
বটম লাইন
Docker simply আপনার development workflow smooth and easy করে দিবে। অনেক problem-এর solution করবে যেগুলো হয়তো আপনি চিন্তাও করতে পারবেন না। একটা distributed system, distributed remote team-এর জন্য docker must।
আপনার project-এ Docker ব্যবহার করছেন? কোন সমস্যায় পড়েছেন? কমেন্টে শেয়ার করুন! 👇