Deploy ও CI/CD

Server এ তোলা, GitHub Actions দিয়ে auto deploy

রোডম্যাপের মাস ৫পড়তে প্রায় ১১ মিনিট

তোমার API এখন Docker এ চলে। এবার সেটা internet এ তুলবো, যাতে তোমার Flutter app যেকোনো জায়গা থেকে, যেকোনো phone থেকে ব্যবহার করা যায়। তারপর এমন ব্যবস্থা করবো যাতে GitHub এ code push করলেই নিজে থেকে test চলে, আর সব ঠিক থাকলে নতুন version deploy হয়ে যায় (CI/CD)। অফিসে যে "deploy এর দিন" এর আতঙ্ক থাকে, সেটা দূর করার উপায় এটাই।

#কোথায় deploy করবে

ধরন উদাহরণ সুবিধা অসুবিধা কখন
PaaS (platform) Railway, Render, Fly.io, Koyeb Git push বা Docker image দিলেই চলে, HTTPS, database সহজে খরচ বড় হলে বেশি, নিয়ন্ত্রণ কম শেখা, ছোট-মাঝারি project, startup
VPS (নিজের server) DigitalOcean, Hetzner, Linode, AWS Lightsail সস্তা, পুরো নিয়ন্ত্রণ সব নিজে: security update, backup, HTTPS, monitoring Linux শিখতে চাইলে, খরচ কমাতে
বড় cloud AWS, GCP, Azure সব কিছু আছে, বিশাল scale জটিল, খরচ বোঝা কঠিন অফিসের বড় system
Serverless AWS Lambda, Cloud Run ব্যবহার না হলে খরচ প্রায় শূন্য Cold start, সীমাবদ্ধতা হঠাৎ হঠাৎ ব্যবহার হওয়া API

শেখার জন্য পরামর্শ: প্রথমে একটা PaaS এ deploy করো (দ্রুত সফলতা)। পরে একবার একটা ছোট VPS এ নিজে সব করে দেখো (Linux, Nginx, HTTPS শেখার জন্য)।

#Production এর আগে checklist

#PaaS এ deploy (Railway উদাহরণ, Render/Fly প্রায় একই)

  1. GitHub এ repo push করো (Dockerfile সহ)
  2. Platform এ নতুন project → "Deploy from GitHub repo"
  3. একটা PostgreSQL আর একটা Redis যোগ করো (platform এর ভেতরেই)
  4. Environment variable দাও: DATABASE_URL (platform এর দেওয়া, শুধু driver অংশ postgresql+asyncpg:// করে নাও), REDIS_URL, JWT_SECRET, ENVIRONMENT=production
  5. Migration: start command এর আগে চালাও, বা আলাদা "release command":
Terminal
alembic upgrade head && uvicorn app.main:app --host 0.0.0.0 --port $PORT --proxy-headers
  1. Platform একটা https://notes-api-production.up.railway.app এর মতো URL দেবে। নিজের domain লাগাতে CNAME record (DNS অধ্যায়)।

#VPS এ deploy: নিজের হাতে একবার

একটা Ubuntu server (মাসে ৪-৬ ডলার) নিয়ে:

Terminal
# ১. Server এ ঢোকো (SSH key দিয়ে, password দিয়ে না)
ssh root@203.0.113.50

# ২. নিরাপত্তার মৌলিক কাজ
adduser deploy && usermod -aG sudo deploy
# SSH এ password login আর root login বন্ধ করো (/etc/ssh/sshd_config)
ufw allow OpenSSH && ufw allow 80 && ufw allow 443 && ufw enable
apt update && apt upgrade -y && apt install -y unattended-upgrades

# ৩. Docker install (official script বা apt repo)
curl -fsSL https://get.docker.com | sh
usermod -aG docker deploy
Terminal
# ৪. Code আর env আনো, চালাও
git clone https://github.com/you/notes-api.git && cd notes-api
nano .env      # production এর secret
docker compose -f compose.prod.yaml up -d --build

HTTPS এর জন্য সবচেয়ে সহজ Caddy (নিজে থেকে Let's Encrypt certificate নেয় আর নবায়ন করে):

Text
# Caddyfile
api.example.com {
    reverse_proxy api:8000
    encode gzip
}

Compose এ caddy service যোগ করে port 80/443 খুলে দাও। এখন https://api.example.com চালু, certificate নিয়ে আর চিন্তা নেই।

#CI/CD: GitHub Actions

CI (Continuous Integration): প্রতিটা push এ নিজে থেকে test আর check। CD (Continuous Deployment): main branch এ সব সবুজ হলে নিজে থেকে deploy।

git push PR বা main Lint ruff, pyright Test pytest + postgres Build Docker image Deploy শুধু main এ, সব সবুজ হলে যেকোনো ধাপ ব্যর্থ হলে থামে, deploy হয় না
ভুল code production এ যাওয়ার আগেই কয়েকটা দরজায় আটকে যায়।
YAML
# .github/workflows/ci.yml
name: CI

on:
  push:
    branches: [main]
  pull_request:

jobs:
  test:
    runs-on: ubuntu-latest
    services:
      postgres:
        image: postgres:17
        env:
          POSTGRES_USER: app
          POSTGRES_PASSWORD: secret
          POSTGRES_DB: notes_test
        ports: ["5432:5432"]
        options: >-
          --health-cmd "pg_isready -U app" --health-interval 5s --health-retries 10
    env:
      DATABASE_URL: postgresql+asyncpg://app:secret@localhost:5432/notes_test
      JWT_SECRET: ci-only-secret
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v6
      - run: uv sync --frozen
      - run: uv run ruff check .
      - run: uv run ruff format --check .
      - run: uv run pyright
      - run: uv run pytest --cov=src --cov-report=term-missing

  deploy:
    needs: test                              # test সফল হলে তবেই
    if: github.ref == 'refs/heads/main'      # শুধু main branch এ
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      # Platform অনুযায়ী: Railway/Render/Fly এর deploy hook বা CLI
      - run: curl -fsS -X POST "${{ secrets.DEPLOY_HOOK_URL }}"

secrets.DEPLOY_HOOK_URL থাকে GitHub repo এর Settings → Secrets এ, code এ না।

#Branch এর নিয়ম

  • main এ সরাসরি push বন্ধ (branch protection)
  • প্রতিটা কাজ আলাদা branch এ, Pull Request দিয়ে
  • PR merge হবে শুধু CI সবুজ হলে
  • একা কাজ করলেও এই অভ্যাস রাখো। অফিসে আর interview এ এটা দেখায় তুমি পেশাদারভাবে কাজ করো।

#Deploy এর পরে

  • Smoke test: deploy এর পর /health/ready আর একটা মূল endpoint call করে দেখো
  • Rollback এর উপায় জানো: আগের image/version এ ফেরার command বা button, আগে থেকেই একবার চেষ্টা করে দেখো
  • Log আর error দেখো: deploy এর পরের ১৫ মিনিট বিশেষ নজর (Monitoring)
  • Database backup পরীক্ষা: backup নেওয়া হচ্ছে, কিন্তু সেখান থেকে restore করা যায় কিনা একবার নিজে চেষ্টা করেছো? যে backup কখনো restore করে দেখা হয়নি, সেটা backup না।

#অনুশীলন

#নিজেকে যাচাই করো

CI আর CD এর পার্থক্য কী?

CI: প্রতিটা push এ নিজে থেকে lint, type check আর test চালিয়ে code এর মান যাচাই। CD: সব যাচাই সফল হলে নিজে থেকে production (বা staging) এ deploy করা।

Production এর secret (JWT_SECRET, database password) কোথায় রাখবে?

Platform এর environment variable বা secret manager এ, আর CI এর জন্য GitHub Secrets এ। কখনো code, Git, বা Docker image এর ভেতরে না।

একাধিক container এর প্রতিটা চালু হওয়ার সময় migration চালালে কী ঝুঁকি?

একসাথে কয়েকটা migration চলে একে অপরের সাথে সংঘর্ষ করতে পারে বা আংশিক অবস্থায় আটকে যেতে পারে। ভালো উপায় হলো deploy এর একটা আলাদা ধাপে একবার migration চালানো।

"যে backup কখনো restore করে দেখা হয়নি সেটা backup না" কথাটার মানে কী?

Backup হয়তো ভাঙা, অসম্পূর্ণ, বা restore এর পদ্ধতি কেউ জানে না। আসল বিপদের সময় সেটা কাজ না করলে কোনো লাভ নেই। তাই মাঝে মাঝে backup থেকে আলাদা জায়গায় restore করে যাচাই করতে হয়।

চিহ্ন দিলে sidebar আর রোডম্যাপে এই অধ্যায়ের station ভরে যাবে।