- InfraCoffee
- Posts
- Implementing Continuous Integration and Deployment (CI/CD) Pipelines with GitHub Actions on Linux: A Step-by-Step Secure Guide
Implementing Continuous Integration and Deployment (CI/CD) Pipelines with GitHub Actions on Linux: A Step-by-Step Secure Guide
Hey there, little buddy! Imagine you're baking cookies with your friends. You mix the dough, put it in the oven, and check if they're yummy. But what if you had a magic kitchen that automatically mixes, bakes, tests if the cookies are perfect (not too gooey or burnt), and even packs them up to share—every time you add a new ingredient? That's what CI/CD is in DevOps! "CI" means Continuous Integration, like mixing everything together without messes, and "CD" is Continuous Deployment, like auto-baking and delivering the cookies safely. We use GitHub Actions (a free tool on GitHub) to make this magic happen on Linux computers, and we'll lock it all up tight so no cookie thieves can sneak in.
This article is brand new: We're focusing on building secure CI/CD pipelines with GitHub Actions for Linux-based workflows—perfect for juniors automating code tests and deploys, while seniors can enhance with advanced secrets and compliance. Previous topics like Docker security, Terraform IaC, Kubernetes, monitoring, and Ansible are great foundations, but CI/CD ties them into automated delivery. We'll dive deep: From repo setup to multi-stage pipelines, with YAML details, security governance (e.g., OWASP for secrets, least privilege via roles), and best practices like artifact scanning and audit logs. Assume Ubuntu 22.04 LTS on your local Linux machine; we'll deploy to a sample AWS EC2 (free tier) for realism.
Why CI/CD with GitHub Actions? Essentials for Juniors
CI/CD automates building, testing, and deploying code, reducing errors and speeding up releases. GitHub Actions is serverless (no managing servers), event-driven (triggers on push/pull), and integrates with Linux tools. Benefits:
Speed & Reliability: Catch bugs early with tests.
Security: Scan for vulnerabilities automatically.
Collaboration: Team reviews via pull requests.
Governance: Enforce policies like approvals and encrypted secrets.
Best practice: Start simple, use self-hosted runners for sensitive Linux workloads. Security: Follow NIST SP 800-218 for secure software supply chain—sign commits, rotate tokens.
Prerequisites: Prep Your Linux Machine and GitHub
Linux Setup:
sudo apt update && sudo apt upgrade -y
sudo apt install git curl -y
Security: Harden with sudo apt install fail2ban ufw -y; sudo ufw allow ssh; sudo ufw enable.
GitHub Account & Repo:
Create a free GitHub account.
New repo: "ci-cd-demo" (public for learning; private in prod).
Clone locally: git clone https://github.com/yourusername/ci-cd-demo.git; cd ci-cd-demo.
AWS Setup (for Deployment Example):
Free account at aws.amazon.com.
Create IAM user with EC2 permissions (least privilege: custom policy with ec2:RunInstances, etc.).
Install AWS CLI: curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "awscliv2.zip"; unzip awscliv2.zip; sudo ./aws/install.
Configure: aws configure (use access keys; enable MFA).
Launch EC2: We'll automate this, but test manually first.
Git Config:
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
Security: Use SSH for Git: ssh-keygen -t ed25519; cat ~/.ssh/id_ed25519.pub (add to GitHub settings).
Step 1: Create a Simple Application – Your Cookie Recipe
We'll use a Node.js app for demo (easy on Linux).
Init Project:
mkdir app && cd app
npm init -y
npm install express
app.js:
const express = require('express');
const app = express();
app.get('/', (req, res) => res.send('Hello, CI/CD World!'));
app.listen(3000, () => console.log('App running on port 3000'));
Test Script (test.js with Mocha): Install: npm install --save-dev mocha chai supertest.
const request = require('supertest');
const app = require('./app');
describe('GET /', () => {
it('should return Hello', (done) => {
request(app).get('/').expect(200, 'Hello, CI/CD World!', done);
});
});
package.json scripts:
"test": "mocha test.js"
Commit and Push:
git add .
git commit -m "Initial app"
git push origin main
Best practice: Use semantic commits (e.g., feat:, fix:).
Step 2: Set Up GitHub Actions Workflow – The Magic Kitchen
Workflows are YAML in .github/workflows/.
Create .github/workflows/ci-cd.yml:
Add Secrets:
GitHub > Repo > Settings > Secrets and variables > Actions > New repository secret.
Add AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY (from IAM). Security governance: Use OIDC instead of keys for prod (configure trust in AWS IAM role). Rotate secrets quarterly.
Push Workflow: git add .github; git commit -m "Add CI/CD workflow"; git push.
Test Trigger: Push a change; check Actions tab for runs.
name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest # Linux runner
steps:
- uses: actions/checkout@v4 # Clone repo
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
deploy:
needs: build # Run after build succeeds
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main' # Only on main push
steps:
- uses: actions/checkout@v4
- name: Set up AWS CLI
uses: aws-actions/configure-aws-credentials@v4
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-east-1
- name: Deploy to EC2
run: |
# Example: SSH to EC2 and deploy (use your instance details)
echo "Deploying..." # Replace with real script
Step 3: Enhance Security in the Pipeline
Go deep on governance.
Vulnerability Scanning
Code Linting/Static Analysis
Artifact Signing
Approvals for Deployment: Use environments: Settings > Environments > New (prod), require approvals. Update deploy job: environment: prod.
Self-Hosted Runners for Linux Control:
Settings > Actions > Runners > New self-hosted runner.
On your Linux: Follow instructions (download, configure).
Update job: runs-on: self-hosted. Security: Harden runner machine (firewall, no root access). Use ephemeral runners.
- name: Scan for vulnerabilities
uses: aquasecurity/trivy-action@master
with:
scan-type: 'fs'
format: 'table'
exit-code: '1' # Fail on critical
- name: Lint code
run: npm install eslint --save-dev; npx eslint .
Governance: Enable branch protection (require passing checks). Audit with GitHub's logs; integrate with SIEM.
Step 4: Advanced Pipeline Features – Multi-Stage Magic
Matrix Builds (Test Multiple Versions):
Caching Dependencies:
Notifications: Add Slack/Email:
Deployment Rollbacks: Use tags; add rollback job triggered on failure.
Integration Testing: Spin up Docker in pipeline:
strategy:
matrix:
node-version: [18, 20]
- uses: actions/cache@v4
with:
path: node_modules
key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}
- uses: rtCamp/action-slack-notify@v2
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
- name: Build Docker image
run: docker build -t myapp .
- name: Integration test
run: docker run myapp npm test