Skip to content

CI/CD Pipeline: The 'Factory Assembly Line' Mental Model

Why does 'works on my machine' still happen with Docker? A mastery guide to CI/CD pipelines, GitHub Actions, and zero-downtime Blue/Green deployments.

5 min readTiếng Việt
CI/CD Pipeline: The 'Factory Assembly Line' Mental Model

Every team starts with the same deployment process:

  1. Write code on your laptop.
  2. Zip it up.
  3. SSH into the server.
  4. Cross your fingers and pray.
  5. Something breaks in production at 2 AM.

CI/CD exists to make Step 3–5 automatic, repeatable, and less terrifying.

This is the Mastery Guide to CI/CD. We’ll use the “Factory Assembly Line” model to understand pipelines, GitHub Actions, and the art of zero-downtime deployments.


Part 1: Foundations (The Mental Model)

The Old Way: Artisan Crafting

In a blacksmith shop, one guy does everything: heats the metal, shapes it, cools it, inspects it, packages it. If he’s sick, nothing ships. If he makes a mistake, it goes to the customer.

This is manual deployment.

The New Way: The Factory Assembly Line

A modern factory has stations: Stamping → Welding → Painting → Inspection → Packaging. Each station checks the output of the previous one. If Inspection fails, nothing reaches Packaging.

This is CI/CD:

Code Commit


[Station 1: CI — Continuous Integration]
    ├── Run Tests (Did we break anything?)
    ├── Lint Code (Is the code clean?)
    └── Build Docker Image (Does it even compile?)


[Station 2: CD — Continuous Delivery]
    ├── Push Image to Registry
    └── Deploy to Staging Environment


[Station 3: CD — Continuous Deployment]  ← (Optional: fully automated)
    └── Deploy to Production

Part 2: The Investigation (GitHub Actions Anatomy)

GitHub Actions is the most popular CI/CD tool for developers. A “workflow” is just a YAML file in .github/workflows/.

Reading a Pipeline

# .github/workflows/deploy.yml

name: "Build and Deploy"

# TRIGGER: When does this run?
on:
  push:
    branches: [main]   # Only on pushes to main

jobs:
  # JOB 1: The CI (Test & Build)
  build:
    runs-on: ubuntu-latest  # What machine to run on (GitHub provides this)
    
    steps:
      - name: "Checkout Code"
        uses: actions/checkout@v4  # Built-in Action: Pull the code

      - name: "Run Tests"
        run: |
          pip install -r requirements.txt
          pytest tests/ --tb=short

      - name: "Build Docker Image"
        run: docker build -t myapp:${{ github.sha }} .

      - name: "Push to Registry"
        run: |
          docker login ghcr.io -u ${{ github.actor }} -p ${{ secrets.GITHUB_TOKEN }}
          docker push ghcr.io/myrepo/myapp:${{ github.sha }}

  # JOB 2: The CD (Deploy) — Only runs if build succeeds
  deploy:
    needs: build  # ← This job WAITS for build to pass
    runs-on: ubuntu-latest
    
    steps:
      - name: "Deploy to Server"
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.SERVER_HOST }}
          username: deploy
          key: ${{ secrets.SSH_KEY }}
          script: |
            docker pull ghcr.io/myrepo/myapp:${{ github.sha }}
            docker stop myapp || true
            docker run -d --name myapp -p 8000:8000 ghcr.io/myrepo/myapp:${{ github.sha }}

Key Concepts to Spot:

  • on: — The trigger. (push, pull_request, schedule).
  • jobs: — Independent units of work that CAN run in parallel.
  • needs: — Creates a dependency (Job B waits for Job A).
  • secrets: — Secrets stored in GitHub settings. Never hardcode passwords in YAML!

Part 3: The Diagnosis (Common Failures)

The “Works in CI, Fails in Production” Mystery

Problem Cause Fix
Different dependencies CI installs fresh. Production has leftovers. Docker everything. Immutable artifacts.
Different environment variables CI has DEBUG=True. Prod doesn’t. Use env: in GitHub Actions and match production secrets.
Database migrations forgot Code deployed, migration not run. Add a migration step BEFORE the deploy step.
Tests pass but app crashes Tests don’t cover the startup code. Add a smoke test: curl http://localhost:8000/health after deploy.

Part 4: The Resolution (Deployment Strategies)

Not all deployments are equal. The strategy determines the risk.

Strategy 1: Rolling Deployment (Kubernetes Default)

Replace old pods one by one. Minimal risk. If one fails, stop.

Strategy 2: Blue/Green Deployment (Zero Downtime)

Run TWO identical production environments. “Blue” is live. “Green” is the new version.

Load Balancer

      ├──► Blue (v1) ← LIVE (100% traffic)

      └──► Green (v2) ← Staging (0% traffic)

After deploy:

      ├──► Blue (v1) ← Idle (0% traffic)

      └──► Green (v2) ← LIVE (100% traffic)
  • Benefit: Instant rollback. If Green fails, flip the switch back to Blue.
  • Cost: Requires double the infrastructure.

Strategy 3: Canary Deployment (Progressive)

Slowly roll out to a percentage of users. If no errors, increase to 100%.

Load Balancer

      ├──► v1 (90% traffic)

      └──► v2 (10% traffic "Canary") ← Test on real users

Final Mental Model

CI (Continuous Integration) -> The Quality Inspection. "Does this code compile and pass tests?"
CD (Continuous Delivery)    -> The Packaging. "Is this artifact ready to be deployed?"
CD (Continuous Deployment)  -> The Shipping. "Deploy automatically to production."

Blue/Green   -> Two factories. Instant switch.
Canary       -> Test on 10% of real customers first.
Rolling      -> Replace workers one by one.

Rules for Good CI/CD:

  1. Fast feedback: The pipeline should fail in under 5 minutes if there’s a problem.
  2. Everything is code: The pipeline config is in Git. No clicking in Jenkins UI.
  3. Immutable artifacts: Build once, deploy the same image everywhere. Never git pull on a production server.

Related posts

You found a tiny easter egg. Keep poking around!