CI/CD and Automation
In this chapter, we'll explore CI/CD pipelines and automation techniques essential for modern DevOps practices.
GitHub Actions
Basic Workflow
name: CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build-and-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up Node.js
uses: actions/setup-node@v2
with:
node-version: '16'
- name: Install dependencies
run: npm install
- name: Run tests
run: npm test
- name: Build
run: npm run build
Docker Build and Push
name: Docker Build
jobs:
docker:
runs-on: ubuntu-latest
steps:
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v2
with:
push: true
tags: user/app:latest
Jenkins Pipeline
Jenkinsfile Example
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'registry.example.com'
IMAGE_NAME = 'my-app'
IMAGE_TAG = 'latest'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Build') {
steps {
sh 'npm install'
sh 'npm run build'
}
}
stage('Test') {
steps {
sh 'npm test'
}
}
stage('Docker Build') {
steps {
script {
docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${IMAGE_TAG}")
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh """
kubectl apply -f k8s/
kubectl rollout status deployment/${IMAGE_NAME}
"""
}
}
}
post {
success {
slackSend channel: '#deployments',
color: 'good',
message: "Deployment successful: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
}
failure {
slackSend channel: '#deployments',
color: 'danger',
message: "Deployment failed: ${env.JOB_NAME} ${env.BUILD_NUMBER}"
}
}
}
Infrastructure as Code
Terraform Configuration
provider "aws" {
region = "us-west-2"
}
module "vpc" {
source = "terraform-aws-modules/vpc/aws"
name = "my-vpc"
cidr = "10.0.0.0/16"
azs = ["us-west-2a", "us-west-2b"]
private_subnets = ["10.0.1.0/24", "10.0.2.0/24"]
public_subnets = ["10.0.101.0/24", "10.0.102.0/24"]
enable_nat_gateway = true
single_nat_gateway = true
tags = {
Environment = "production"
Project = "my-app"
}
}
resource "aws_ecs_cluster" "main" {
name = "my-app-cluster"
}
resource "aws_ecs_task_definition" "app" {
family = "my-app"
network_mode = "awsvpc"
requires_compatibilities = ["FARGATE"]
cpu = 256
memory = 512
container_definitions = jsonencode([
{
name = "my-app"
image = "my-app:latest"
portMappings = [
{
containerPort = 80
hostPort = 80
}
]
}
])
}
Ansible Playbook
---
- name: Configure Application Servers
hosts: app_servers
become: yes
vars:
app_name: my-app
app_version: latest
tasks:
- name: Update apt cache
apt:
update_cache: yes
- name: Install required packages
apt:
name: "{{ packages }}"
state: present
vars:
packages:
- docker.io
- python3-pip
- name: Start Docker service
service:
name: docker
state: started
enabled: yes
- name: Pull application image
docker_image:
name: "{{ app_name }}"
tag: "{{ app_version }}"
source: pull
- name: Run application container
docker_container:
name: "{{ app_name }}"
image: "{{ app_name }}:{{ app_version }}"
state: started
restart_policy: always
ports:
- "80:80"
Monitoring and Logging
Prometheus Configuration
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'node'
static_configs:
- targets: ['node-exporter:9100']
- job_name: 'application'
metrics_path: '/metrics'
static_configs:
- targets: ['app:8080']
Grafana Dashboard (JSON)
{
"dashboard": {
"id": null,
"title": "Application Metrics",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"datasource": "Prometheus",
"targets": [
{
"expr": "rate(http_requests_total[5m])",
"legendFormat": "{{method}} {{path}}"
}
]
},
{
"title": "Response Time",
"type": "graph",
"datasource": "Prometheus",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
"legendFormat": "95th percentile"
}
]
}
]
}
}
Hands-on Exercise: Setting up a Complete CI/CD Pipeline
- Create GitHub Actions Workflow
name: Complete CI/CD Pipeline
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run Tests
run: |
npm install
npm test
build:
needs: test
runs-on: ubuntu-latest
steps:
- name: Build Docker Image
run: docker build -t myapp:${{ github.sha }} .
deploy:
needs: build
runs-on: ubuntu-latest
steps:
- name: Deploy to Production
run: |
echo ${{ secrets.KUBECONFIG }} | base64 -d > kubeconfig
export KUBECONFIG=./kubeconfig
kubectl set image deployment/myapp myapp=myapp:${{ github.sha }}
- Set up Monitoring
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: myapp-monitor
spec:
selector:
matchLabels:
app: myapp
endpoints:
- port: metrics
- Configure Alerts
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: myapp-alerts
spec:
groups:
- name: myapp
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) > 1
for: 5m
labels:
severity: critical
annotations:
description: High error rate detected
In the next chapter, we'll explore advanced deployment strategies and service mesh implementations.