DevOpsIndex

CI/CD

#5 8 pages

CI/CD: Concepts & Patterns


CI vs CD vs GitOps

graph LR
    classDef ci fill:#3498db,stroke:#2980b9,color:#fff
    classDef cd fill:#9b59b6,stroke:#8e44ad,color:#fff
    classDef gitops fill:#2ecc71,stroke:#27ae60,color:#fff
    classDef artifact fill:#e67e22,stroke:#d35400,color:#fff

    subgraph CI["CI: Continuous Integration"]
        COMMIT["Code pushed"]:::ci --> BUILD["Build + compile"]:::ci
        BUILD --> TEST["Unit + integration tests"]:::ci
        TEST --> LINT["Lint + security scan"]:::ci
        LINT --> ART["Artifact: Docker image, binary"]:::artifact
    end

    subgraph CD["CD: Continuous Delivery"]
        ART2["Artifact"]:::artifact --> STAGING["Deploy to staging"]:::cd
        STAGING --> SMOKE["Smoke tests"]:::cd
        SMOKE --> PROD["Deploy to prod"]:::cd
    end

    subgraph GitOps["GitOps: Declarative CD"]
        GIT["Git = source of truth"]:::gitops --> AGENT["ArgoCD/Flux watches repo"]:::gitops
        AGENT --> SYNC["Syncs cluster to match git"]:::gitops
        SYNC --> HEAL["Detects and corrects drift"]:::gitops
    end
CI CD push GitOps pull
Trigger Code push/PR Pipeline pushes to env Agent pulls from git
Audit Pipeline logs Pipeline logs Git commits
Rollback Re-run pipeline Re-run pipeline git revert + auto-sync

Pipeline Triggers

graph TD
    classDef pr fill:#e67e22,stroke:#d35400,color:#fff
    classDef push fill:#2ecc71,stroke:#27ae60,color:#fff
    classDef gate fill:#e74c3c,stroke:#c0392b,color:#fff

    subgraph PRTrigger["PR trigger: gates before merge"]
        PR_OPEN["Developer opens PR"]:::pr --> RUN_TESTS["Run: lint, tests, security scan, build"]:::pr
        RUN_TESTS --> GATE["Gate: must pass before merge allowed"]:::gate
    end

    subgraph PushTrigger["Push trigger: deploy after merge"]
        MERGE["Merge to main"]:::push --> DEPLOY_STG["Deploy to staging"]:::push
        DEPLOY_STG --> TAG["Git tag: v1.2.3"]:::push
        TAG --> DEPLOY_PROD["Deploy to prod"]:::push
    end

Best practice flow:

PR opened       → CI: lint + test + security scan + build
PR merged to main → CD: deploy to staging + smoke tests
Git tag v*.*.*  → CD: deploy to production (with approval gate)

Deployment Strategies

graph TD
    classDef v1 fill:#3498db,stroke:#2980b9,color:#fff
    classDef v2 fill:#2ecc71,stroke:#27ae60,color:#fff
    classDef switch fill:#e67e22,stroke:#d35400,color:#fff
    classDef info fill:#95a5a6,stroke:#7f8c8d,color:#fff

    subgraph Rolling["Rolling Update: gradual replacement"]
        R1["v1: 3 pods"]:::v1 --> R2["Replace 1 pod with v2"]:::v2
        R2 --> R3["Replace next"]:::v2
        R3 --> R4["All on v2"]:::v2
        RN["Low cost. Both versions run briefly. Slower rollback."]:::info
    end

    subgraph BlueGreen["Blue-Green: instant switch"]
        BG1["Blue: v1 100% traffic"]:::v1 --> BG2["Green: v2 idle"]:::v2
        BG2 --> BG3["Switch LB to Green"]:::switch
        BG3 --> BG4["Blue kept for instant rollback"]:::v1
        BGN["Instant rollback. Doubles cost during transition."]:::info
    end

    subgraph Canary["Canary: gradual traffic shift"]
        C1["v1: 95%"]:::v1 --> C2["v2: 5% canary"]:::v2
        C2 --> C3["Monitor error rate + p99 latency"]:::switch
        C3 --> C4["Shift: 10% to 25% to 50% to 100%"]:::v2
        CN["Real user validation. Low blast radius."]:::info
    end
Strategy Rollback Cost Risk
Rolling Slow (re-deploy) Low Mixed versions briefly
Blue-Green Instant (flip switch) High (double) None
Canary Fast (shift back) Medium Low (small blast radius)

Secrets in CI/CD

graph TD
    classDef bad fill:#e74c3c,stroke:#c0392b,color:#fff
    classDef t1 fill:#3498db,stroke:#2980b9,color:#fff
    classDef t2 fill:#9b59b6,stroke:#8e44ad,color:#fff
    classDef t3 fill:#e67e22,stroke:#d35400,color:#fff
    classDef t4 fill:#2ecc71,stroke:#27ae60,color:#fff

    BAD["Secrets in code or CI config as plaintext"]:::bad -->|"never"| GOOD

    subgraph GOOD["Secure patterns by tier"]
        T1["CI-native stores
GitHub Actions secrets, GitLab CI variables
Simple, good for non-production"]:::t1
        T2["AWS Secrets Manager with IAM role
Rotatable, audited, AWS-native"]:::t2
        T3["HashiCorp Vault: dynamic secrets
Short-lived per pipeline run
Production-grade"]:::t3
        T4["SOPS + KMS: encrypted files in git
Decrypted at pipeline runtime"]:::t4
    end

Docker Layer Caching in CI

graph TD
    classDef cached fill:#2ecc71,stroke:#27ae60,color:#fff
    classDef rebuild fill:#e74c3c,stroke:#c0392b,color:#fff
    classDef base fill:#3498db,stroke:#2980b9,color:#fff

    subgraph Good["Optimised: deps before source"]
        G1["FROM golang:1.23-alpine"]:::base --> G2["COPY go.mod go.sum + RUN go mod download"]:::cached
        G2 --> G3["COPY source code"]:::rebuild
        G3 --> G4["RUN go build"]:::rebuild
        GN["go.mod unchanged = layer 2 cached. Only layers 3-4 rebuild."]:::cached
    end

    subgraph Bad["Wrong order: source before deps"]
        B1["FROM golang:1.23-alpine"]:::base --> B2["COPY . ."]:::rebuild
        B2 --> B3["RUN go mod download"]:::rebuild
        B3 --> B4["RUN go build"]:::rebuild
        BN["Every commit invalidates the dep download layer."]:::rebuild
    end

Debugging Flaky Pipelines

graph TD
    classDef check fill:#3498db,stroke:#2980b9,color:#fff
    classDef cause fill:#e74c3c,stroke:#c0392b,color:#fff
    classDef fix fill:#2ecc71,stroke:#27ae60,color:#fff

    FLAKY["Pipeline fails intermittently"]:::cause --> P1["1. Pattern? Time of day, branch, specific stage?"]:::check
    P1 --> P2["2. Env secrets correctly configured per environment?"]:::check
    P2 --> P3["3. Race conditions? Parallel jobs competing for same resource?"]:::check
    P3 --> P4["4. Artifact transfer between stages? Flaky storage?"]:::check
    P4 --> P5["5. Deployment target K8s/ECS throwing transient errors?"]:::check
    P5 --> P6["6. Add explicit retry logic and better logging"]:::fix

Pages in this section