Let me break down CI/CD at a high level based on what I've learned and implemented in my projects.
The Branch Strategy
Whenever we create a repo, we make certain branches — typically dev for development and prod for production. This separation is crucial for maintaining stability.
How CI Pipelines Work
Whenever a user makes changes to the dev branch, the CI (Continuous Integration) pipeline gets triggered. Here's what happens:
The CI pipeline builds the repo on an ubuntu-latest machine (usually GitHub's runners). It checks for workflows and linting errors. If the PR passes all the workflows — building the repo without errors and being free from linting and formatting issues — only then is it pushed to the dev branch.
Once every two weeks or so, all the PRs in the dev branch are reviewed. If everything is correct and working as expected, they're pushed to the prod branch.
The CD Pipeline Takes Over
This is where CD (Continuous Deployment) comes into play. Here's the flow:
- The repo is cloned using
actions/checkout@v2on anubuntu-latestmachine - If everything builds correctly, a Docker image is created
- This Docker image is pushed to a Docker registry (or AWS ECR)
- Using Kubernetes clusters, containers are created from this image
- These containers are deployed to an EC2 machine and scaled up or down based on traffic
Build Workflow (build.yml)
name: Build on PR
on:
pull_request:
branches:
- master
- main
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Use Node.js
uses: actions/setup-node@v3
with:
node-version: '20'
- name: Install Dependencies
run: npm install
- name: Generate Prisma Client
run: npm run db:generate
- name: Run Build
run: npm run build
This workflow runs on every pull request. It checks out the code, installs dependencies, generates the Prisma client, and runs the build. If any step fails, the PR can't be merged.
Deploy Workflow (deploy.yml)
name: Build and Deploy to Docker Hub
on:
push:
branches:
- master
jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Check Out Repo
uses: actions/checkout@v2
- name: Log in to Docker Hub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
- name: Build and Push Docker Image
uses: docker/build-push-action@v2
with:
context: .
file: ./docker/Dockerfile.user
push: true
tags: toovinod/cicd-pipelines:latest
- name: Verify Pushed Image
run: docker pull toovinod/cicd-pipelines:latest
The Big Picture
So to summarize: code changes trigger CI pipelines that test and validate. When code hits production branches, CD pipelines build Docker images, push them to registries, and deploy them to servers where they can scale based on demand.
Understanding this flow changed how I think about shipping software. Every push isn't just pushing code — it's triggering a chain of automated processes that ensure quality before anything reaches your users.