Skip to Content
DocsCI/CDHow It Works

How It Works

এই page-এ কোনো setup নেই। এখানে শুধু বুঝবেন pipeline-টি ভেতরে কীভাবে কাজ করে — যাতে পরের page-এ YAML লেখার সময় প্রতিটি লাইনের মানে জানা থাকে।

What Is a Pipeline

এই section-এ শব্দটি বারবার আসবে, তাই আগে মানে পরিষ্কার করে নেওয়া দরকার।

Pipeline হলো কতগুলো ধাপের একটি সারি, যেগুলো একের পর এক নিজে থেকে চলে। আপনি শুরু করিয়ে দিলেন, বাকিটা নিজে হয়।

আপনি এখন হাতে যা করেন সেটিও আসলে একটি pipeline — শুধু প্রতিটি ধাপ নিজে চালান:

code লিখুন → push → ssh → git pull → docker compose up -d --build

CI/CD-র কাজ হলো এই সারিটিকে একটি file-এ লিখে রাখা, যাতে একটি machine প্রতিবার হুবহু একইভাবে চালায়।

দুটি নিয়ম মনে রাখুন:

  • ধাপগুলো উপর থেকে নিচে চলে, একটা শেষ হলে পরেরটা
  • কোনো ধাপ fail করলে সেখানেই সব থেমে যায়

দ্বিতীয় নিয়মটিই pipeline-কে নিরাপদ করে। Build fail করলে deploy-এর ধাপ পর্যন্ত পৌঁছানোই যাবে না, তাই ভাঙা code কখনো server-এ যেতে পারবে না।

CI and CD

দুটি আলাদা কাজ, একসাথে লেখা হয় বলে অনেকে গুলিয়ে ফেলেন।

CI — Continuous Integration প্রতিটি code change build করে দেখা হয় ঠিক আছে কিনা। কিছু ভাঙলে সেখানেই ধরা পড়ে। Server-এ কিছু হয় না।

CD — Continuous Deployment Build পাশ করলে সেই জিনিস নিজে থেকে server-এ চলে যায়।

CICD
কখন চলেপ্রতিটি pull request-এশুধু main-এ push হলে
কী করেbuild করে check করেserver-এ deploy করে
Server ছোঁয়নাহ্যাঁ
Fail করলেmerge আটকে যায়deploy আটকে যায়

What Is a Runner

Runner হলো একটি অস্থায়ী Ubuntu machine যা GitHub আপনাকে প্রতিবার নতুন করে দেয়। আপনার সব build এই machine-এ হবে।

তিনটি কথা মনে রাখুন:

  • প্রতিবার সম্পূর্ণ নতুন — আগের run-এর কিছুই থাকে না
  • কাজ শেষ হলে মুছে যায়
  • আপনার server-এর সাথে এর কোনো সম্পর্ক নেই

শেষ কথাটি জরুরি। Runner আপনার server চেনে না, তার কাছে কোনো password বা key নেই। আপনি না দিলে সে ঢুকতে পারবে না। এই কারণেই পরের page-এ SSH key আর GitHub Secrets নিয়ে কাজ করতে হবে।

Where the Build Runs

এটিই এই page-এর সবচেয়ে গুরুত্বপূর্ণ প্রশ্ন। উত্তরটি পুরো pipeline-এর আকার ঠিক করে দেয়।

পথ ১ — Server নিজে build করে

push → server → git pull → docker compose up -d --build

অনেকেই এভাবে করেন। কাজ করে, কিন্তু production-এ তিনটি সমস্যা:

  • Build-এ RAM আর CPU লাগে। ছোট VPS-এ Next.js build প্রায়ই Killed হয়ে যায়, আর build চলার সময় live site ধীর হয়ে থাকে
  • Server প্রতিবার নতুন করে package টানে। একই commit থেকে দুইবার build করলে দুটি আলাদা জিনিস তৈরি হতে পারে
  • Source code server-এ পড়ে থাকে। কেউ ঢুকে পড়লে আপনার পুরো codebase আর git history তার হাতে

পথ ২ — Runner build করে, server শুধু চালায়

push → runner image বানায় → registry-তে রাখে → server টেনে চালায়

Server আর কিছু বানায় না। সে একটি তৈরি image টেনে এনে চালায়, ব্যস।

Server build করেRunner build করে
Build-এর খরচআপনার server-এর RAM আর CPUGitHub-এর runner-এর
Server-এ codeআছেনেই
একই commit, একই ফলনিশ্চিত নয়নিশ্চিত
Deploy-এ যা যায়codeতৈরি image
Rollbackআবার build, ৩-৫ মিনিটপুরোনো image, ১০ সেকেন্ড
Server-এ যা লাগেDocker, git, আর অনেকটা RAMশুধু Docker

এই section-এ পথ ২ শেখানো হবে। VPS-এ Docker দিয়ে deploy করার standard পদ্ধতি এটিই।

Runner-এর সময় public repository-তে সীমাহীন এবং বিনামূল্যে। Private repository-তে GitHub-এর free plan-এ মাসে ২,০০০ মিনিট পাওয়া যায়।

একটি Next.js app-এর build-এ ২-৩ মিনিট লাগে, তাই মাসে কয়েকশ deploy করলেও এতে কুলিয়ে যায়। তবু হিসাব রাখতে চাইলে দেখুন — Settings → Billing → Actions

What Is a Registry

Runner image বানাল। কিন্তু runner তো কিছুক্ষণ পরে মুছে যাবে। তাহলে image-টি রাখব কোথায়?

Registry হলো image রাখার গুদাম। GitHub-এর নিজের registry-র নাম GHCR — GitHub Container Registry। আপনার repository-র সাথেই আসে, আলাদা account লাগে না।

Runner image বানায় ↓ docker push GHCR ↓ docker pull Server image টেনে চালায়

Docker Hub-ও ব্যবহার করা যায়। কিন্তু GHCR আপনার repository-র সাথে যুক্ত থাকায় GitHub Actions নিজে থেকেই login করে নিতে পারে — আলাদা কোনো password বা secret তৈরি করতে হয় না।

Image Tags

একটি image-এর নামের শেষে একটি tag থাকে। এই tag দিয়েই কোন version চলছে তা বোঝা যায়।

ghcr.io/your-username/myapp:latest ← কোনটা? বলা যাচ্ছে না ghcr.io/your-username/myapp:a3f9c21 ← এই commit-এর, নিশ্চিত

আমরা প্রতিটি image-কে তার commit-এর নাম দিয়ে tag করব। GitHub Actions-এ এটি github.sha নামে পাওয়া যায়।

এতে তিনটি লাভ:

  • Production-এ ঠিক কোন code চলছে তা এক নজরে বলা যায়
  • পুরোনো প্রতিটি version registry-তে জমা থাকে
  • Rollback মানে শুধু পুরোনো tag-টি আবার চালানো

শুধু latest tag ব্যবহার করবেন না। latest আজ একটা জিনিস, কাল আরেকটা — কোনো নির্দিষ্ট version-কে সে বোঝায় না। তখন rollback করার কিছুই থাকে না, কারণ পুরোনো image-টির আর কোনো নাম নেই।

আমরা দুটি tag একসাথে দেব — a3f9c21 (নির্দিষ্ট) আর latest (সুবিধার জন্য)। Deploy-এ সবসময় নির্দিষ্টটি ব্যবহার হবে।

Two Kinds of Compose File

Docker section-এ আপনি compose.yaml দেখেছেন code-এর পাশে, project-এর root folder-এ। এই section-এ সেটি থাকবে server-এ, একা।

একই নামের file, কিন্তু ভেতরে একটি শব্দ আলাদা — আর ওই শব্দটিই ঠিক করে দেয় file-টি কোথায় থাকবে।

build: — compose নিজে image বানায়

services: web: build: context: ./web

build: মানে compose-কে বলা হচ্ছে “তুমি image বানাও”। বানাতে code লাগে, Dockerfile লাগে। তাই এই file-টিকে code-এর পাশে থাকতেই হবে।

image: — compose শুধু চালায়

services: web: image: ghcr.io/your-username/myapp:${TAG}

image: মানে “বানানোর দরকার নেই, registry থেকে এই তৈরি জিনিসটি এনে চালাও”। Code লাগে না, তাই file-টি server-এ একা থাকতে পারে।

build:image:
Image কে বানায়compose নিজেআগেই বানানো হয়ে গেছে
পাশে code লাগেহ্যাঁনা
কোথায় রাখা হয়code-এর folder-এযেকোনো জায়গায়
কোথায় কাজে লাগেনিজের computer-এproduction server-এ

এখানে একটি জিনিস অনেকে গুলিয়ে ফেলেন, তাই পরিষ্কার করে নিন — এই pipeline-এ compose কখনো কিছু বানায় না।

Runner image বানায় সরাসরি Dockerfile দিয়ে, compose দিয়ে নয়। তাই runner-এ কোনো compose file-ই থাকে না।

তিনটি জায়গা, তিনটি আলাদা কাজ:

Runner → BUILD করে → লাগে: code + Dockerfile (compose নেই) Registry → জমা রাখে → লাগে: কিছুই না Server → RUN করে → লাগে: compose.yaml + .env (code নেই)

Server-এ কোনো Dockerfile নেই, runner-এ কোনো compose.yaml নেই। দুটি কখনো একসাথে থাকে না।

তাই এই section-এ compose-এর কাজ শুধু চালানো, কখনো বানানো নয়।

The Full Flow

সব মিলিয়ে push করার পর যা ঘটে:

আপনার Computer │ git push origin main GitHub Repository │ workflow file দেখে trigger হয় GitHub Runner (নতুন খালি Ubuntu machine) │ ১. code clone করে │ ২. docker build ← fail করলে এখানেই থেমে যায় │ ৩. docker push → GHCR GHCR Registry │ image জমা হলো, নাম: commit-এর sha আপনার Server (SSH দিয়ে ঢোকে) │ ৪. .env-এ নতুন TAG লিখে দেয় │ ৫. docker compose pull │ ৬. docker compose up -d │ ৭. curl দিয়ে check করে Site Live (fail করলে পুরোনো TAG ফিরিয়ে আনে)

The Workflow File

Pipeline-টি আসলে আপনার project-এ রাখা একটি YAML file। GitHub সেটি পড়ে কাজটি করে।

File-টি একটি নির্দিষ্ট folder-এ থাকতে হয়:

myapp/ ├── .github/ │ └── workflows/ │ ├── ci.yml ← pull request-এ build check │ └── deploy.yml ← main-এ push হলে deploy ├── Dockerfile ├── .dockerignore └── package.json

Folder-এর নাম হুবহু .github/workflows হতে হবে। ভুল হলে GitHub file-টি খুঁজেই পাবে না, আর কোনো error ও দেখাবে না।

এখানে কোনো compose.yaml নেই — এটি ইচ্ছাকৃত। Compose file আর .env শুধু server-এ থাকে, repository-তে নয়। কারণ ওই দুটিতে server-এর নিজস্ব তথ্য আর secret থাকে।

একটি ছোট workflow দেখে প্রতিটি অংশ বুঝে নিই:

.github/workflows/ci.yml
name: CI on: pull_request: branches: [main] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Build image uses: docker/build-push-action@v6 with: context: . push: false
Keyমানে
nameActions tab-এ যে নাম দেখাবে
onকখন চলবে — এখানে main-এ আসা pull request-এ
jobsকাজের group। এক workflow-এ একাধিক job থাকতে পারে
buildjob-এর নাম, আপনি নিজে দেন
runs-onকোন OS-এর runner লাগবে
stepsjob-এর ভেতরে ধাপগুলো, উপর থেকে নিচে চলে

uses and run

Step দুই ধরনের হয়।

uses — অন্যের লেখা তৈরি action চালায়:

- uses: actions/checkout@v4

actions/checkout হলো GitHub-এর নিজের লেখা একটি action, যা repo-র code runner-এ clone করে। নিজে লিখতে গেলে অনেক লাইন লাগত।

run — সরাসরি shell command চালায়, runner-এর ভেতরে:

- run: docker image ls

এই guide-এর দুটি workflow-এ প্রায় সব step-ই uses — কারণ Docker-এর কাজগুলো তৈরি action দিয়েই হয়ে যায়।

run লাগে যখন নিজের কোনো command চালাতে হয় — যেমন test চালানো, বা log-এ কিছু লিখে রাখা।

with

কোনো action-কে extra তথ্য দিতে with ব্যবহার হয়। উপরের example-এ docker/build-push-action-কে বলা হয়েছে কোথা থেকে build করবে (context) আর registry-তে পাঠাবে কিনা (push)।

Why Two Separate Jobs

Deploy workflow-এ দুটি job থাকবে, একটি নয়।

jobs: build: runs-on: ubuntu-latest steps: # image build করে registry-তে পাঠায় deploy: needs: build # ← মূল লাইন runs-on: ubuntu-latest steps: # server-এ SSH করে

needs: build লাইনটি বলছে — build job পাশ না করলে deploy job শুরুই হবে না।

এর মানে আপনার server তখনই ছোঁয়া হবে যখন image ইতিমধ্যে তৈরি হয়ে registry-তে পৌঁছে গেছে। Build fail করলে server আগের image নিয়েই চলতে থাকবে, কেউ টেরও পাবে না।

needs ছাড়া লিখলে দুটি job একসাথে চলে। তখন server এমন একটি image টানতে যাবে যা এখনো তৈরিই হয়নি — deploy fail করবে, আর কেন করল তা log দেখে বোঝা কঠিন।

Reading a Failed Run

Pipeline fail করলে GitHub-এ Actions tab-এ যাবেন।

  • লাল X মানে কোনো job fail করেছে
  • সেটিতে click করলে step-গুলো, আবার click করলে পুরো log খুলবে
  • Log-এর শেষ ২০ লাইন পড়ুন — আসল error প্রায় সবসময় সেখানেই

একটি step fail করলে পরেরগুলো চলেই না। তাই প্রথম লাল X-টিই আসল সমস্যা।

Real-World Note

Third-party action মানে অন্যের code আপনার pipeline-এ চলছে।

uses: someone/some-action@main লিখলে আপনি একজন অচেনা মানুষের code-কে আপনার secrets-এর কাছে যাওয়ার অনুমতি দিচ্ছেন। @main মানে “সবসময় সর্বশেষ version” — অর্থাৎ সে যখন খুশি code বদলাতে পারে, আর আপনার pipeline সেই বদলানো code চালাবে।

এটি একটি সত্যিকারের attack path। ২০২৫ সালের ১৪ মার্চ tj-actions/changed-files নামের একটি action-এ ঠিক এই ঘটনা ঘটে। Action-টি ২৩,০০০-এরও বেশি repository ব্যবহার করত। Attacker ভেতরে একটি malicious script ঢুকিয়ে দেয় যা runner-এর memory থেকে secrets টেনে build log-এ লিখে দিত। Public repository-র log সবাই পড়তে পারে, তাই সেগুলোর secrets প্রকাশ্যে চলে আসে।

সবচেয়ে জরুরি শিক্ষাটি অনেকে বুঝতে ভুল করেন। Attacker শুধু code বদলায়নি — সে পুরোনো version tag গুলোও ওই malicious commit-এর দিকে ঘুরিয়ে দিয়েছিল। অর্থাৎ যাঁরা @v44 লিখে রেখেছিলেন, তাঁদের কোনো লাভ হয়নি।

যাঁরা বেঁচে গিয়েছিলেন তাঁরা commit SHA দিয়ে pin করেছিলেন। SHA বদলানো যায় না — সেটিই একমাত্র আসল সুরক্ষা।

তিনটি নিয়ম:

  • @main কখনো নয়। এখানে attack করারও দরকার নেই — maintainer নিজেই যেকোনো সময় code বদলাতে পারেন
  • @v4 সর্বনিম্ন মান। বেশিরভাগ ক্ষেত্রে যথেষ্ট, তবে উপরের ঘটনা দেখাল যে tag-ও সরানো যায়
  • সত্যিকারের সুরক্ষা commit SHA। সম্পূর্ণ ৪০ অক্ষর লিখতে হবে, ছোট করে লিখলে GitHub চিনতে পারে না:
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

পাশে comment হিসেবে version লিখে রাখুন, না হলে ছয় মাস পরে বোঝা যাবে না কোনটা কী।

actions/ এবং docker/ দিয়ে শুরু হওয়া action-গুলো যথাক্রমে GitHub আর Docker-এর নিজের — ঝুঁকি তুলনামূলক কম, তাই এই guide-এ @v4 ধরনের tag ব্যবহার করা হয়েছে।

Quick Check

  • Pipeline-এর কোনো ধাপ fail করলে পরেরগুলোর কী হয়?
  • Build কেন server-এ না করে runner-এ করা উচিত?
  • Registry কী এবং কেন লাগে?
  • Image-এ শুধু latest tag দিলে কী হারাবেন?
  • compose-এ build: আর image:-এর পার্থক্য কী?
  • এই pipeline-এ image কে বানায় — compose, নাকি Dockerfile?
  • needs: build না থাকলে কী সমস্যা হয়?

পরবর্তী → Setup

github actions workflow bangla, ci cd kake bole, github runner bangla, container registry bangla, ghcr bangla, docker image tag bangla, workflow yml bangla, ci vs cd bangla, docker compose build vs image bangla, compose yaml kothay rakhbo

Last updated on