CI/CD Pipeline Design
Pipeline Architecture
Code Push → GitHub Actions → Build → Test → Deploy → Verify
│ │ │ │ │ │
│ │ │ │ │ └─ Health checks
│ │ │ │ └─ Staging → Production
│ │ │ └─ Unit, Integration, Functional
│ │ └─ Composer, NPM, Static deploy
│ └─ Lint, Security scan
└─ Feature branch, PR
GitHub Actions Workflow
# .github/workflows/deploy.yml
name: Magento CI/CD
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
MAGENTO_VERSION: '2.4.7'
PHP_VERSION: '8.2'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: PHP CodeSniffer
run: |
composer install
vendor/bin/phpcs --standard=PSR12 app/code/Vendor/
- name: PHPStan
run: vendor/bin/phpstan analyse app/code/Vendor/ --level=8
security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Composer Audit
run: composer audit
- name: Security Checker
run: |
composer global require sensiolabs/security-checker
security-checker security:check composer.lock
unit-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
extensions: mbstring, intl, bcmath, gd
- name: Install Dependencies
run: composer install --no-interaction
- name: Run Tests
run: vendor/bin/phpunit --configuration phpunit.xml.dist
integration-test:
runs-on: ubuntu-latest
needs: [unit-test]
services:
mysql:
image: mysql:8.0
env:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: magento_test
ports:
- 3306:3306
redis:
image: redis:7
ports:
- 6379:6306
opensearch:
image: opensearchproject/opensearch:2.11.0
ports:
- 9200:9200
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.PHP_VERSION }}
- name: Install Magento
run: |
composer create-project --repository-url=https://repo.magento.com/ magento/project-community-edition .
bin/magento setup:install \
--db-host=127.0.0.1 \
--db-name=magento_test \
--db-user=root \
--db-password=root \
--admin-firstname=admin \
--admin-lastname=admin \
--admin-email=admin@example.com \
--admin-user=admin \
--admin-password=admin123 \
--search-engine=opensearch
- name: Run Integration Tests
run: vendor/bin/phpunit --configuration dev/tests/integration/phpunit.xml.dist
deploy-staging:
runs-on: ubuntu-latest
needs: [lint, security, unit-test, integration-test]
if: github.ref == 'refs/heads/develop'
steps:
- uses: actions/checkout@v4
- name: Deploy to Staging
run: |
ssh ${{ secrets.STAGING_SSH }} "cd /var/www/magento && \
git pull origin develop && \
composer install --no-dev && \
bin/magento setup:upgrade --keep-generated && \
bin/magento setup:di:compile && \
bin/magento setup:static-content:deploy -f && \
bin/magento cache:clean"
- name: Health Check
run: |
sleep 30
curl -sf https://staging.magento.local/health || exit 1
deploy-production:
runs-on: ubuntu-latest
needs: [lint, security, unit-test, integration-test]
if: github.ref == 'refs/heads/main'
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy to Production
run: |
# Blue-green deployment
ssh ${{ secrets.PROD_SSH }} "cd /var/www/magento-green && \
git pull origin main && \
composer install --no-dev && \
bin/magento setup:upgrade --keep-generated && \
bin/magento setup:di:compile && \
bin/magento setup:static-content:deploy -f && \
bin/magento cache:clean"
- name: Switch Traffic
run: |
ssh ${{ secrets.PROD_SSH }} "sudo nginx -s reload"
- name: Verify Deployment
run: |
sleep 30
curl -sf https://magento.local/health || exit 1
- name: Rollback on Failure
if: failure()
run: |
ssh ${{ secrets.PROD_SSH }} "sudo nginx -s reload -c /etc/nginx/nginx-blue.conf"
Deployment Scripts
#!/bin/bash
# deploy.sh
set -e
ENVIRONMENT=$1
VERSION=$2
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$ENVIRONMENT] $1"
}
log "Starting deployment v$VERSION"
# Step 1: Enable maintenance mode
log "Enabling maintenance mode..."
bin/magento maintenance:enable
# Step 2: Backup current state
log "Creating backup..."
bin/magento setup:backup --code --db
# Step 3: Pull code
log "Pulling code..."
git fetch origin
git checkout $VERSION
# Step 4: Install dependencies
log "Installing dependencies..."
composer install --no-dev --prefer-dist --no-interaction
# Step 5: Run migrations
log "Running setup:upgrade..."
bin/magento setup:upgrade --keep-generated
# Step 6: Compile DI
log "Compiling dependency injection..."
bin/magento setup:di:compile
# Step 7: Deploy static content
log "Deploying static content..."
bin/magento setup:static-content:deploy -f
# Step 8: Clean cache
log "Cleaning cache..."
bin/magento cache:clean
bin/magento cache:flush
# Step 9: Reindex
log "Reindexing..."
bin/magento indexer:reindex
# Step 10: Disable maintenance mode
log "Disabling maintenance mode..."
bin/magento maintenance:disable
# Step 11: Verify deployment
log "Verifying deployment..."
bin/magento --version
log "Deployment complete!"
Rollback Script
#!/bin/bash
# rollback.sh
set -e
BACKUP_ID=$1
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] [ROLLBACK] $1"
}
log "Starting rollback to backup $BACKUP_ID"
# Enable maintenance mode
bin/magento maintenance:enable
# Restore from backup
bin/magento setup:rollback --backup-id $BACKUP_ID
# Restore database
bin/magento db:backup:restore --backup-id $BACKUP_ID
# Clean cache
bin/magento cache:clean
bin/magento cache:flush
# Disable maintenance mode
bin/magento maintenance:disable
log "Rollback complete!"
Monitoring and Alerting
Monitoring Stack Configuration
# docker-compose.monitoring.yml
version: '3.8'
services:
prometheus:
image: prom/prometheus:latest
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- '9090:9090'
grafana:
image: grafana/grafana:latest
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
ports:
- '3000:3000'
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
alertmanager:
image: prom/alertmanager:latest
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
ports:
- '9093:9093'
node-exporter:
image: prom/node-exporter:latest
ports:
- '9100:9100'
mysql-exporter:
image: prom/mysqld-exporter:latest
ports:
- '9104:9104'
redis-exporter:
image: oliver006/redis_exporter:latest
ports:
- '9121:9121'
volumes:
prometheus_data:
grafana_data:
Prometheus Configuration
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
rule_files:
- 'rules/*.yml'
scrape_configs:
- job_name: 'magento'
static_configs:
- targets: ['web-1:9100', 'web-2:9100']
- job_name: 'mysql'
static_configs:
- targets: ['mysql-exporter:9104']
- job_name: 'redis'
static_configs:
- targets: ['redis-exporter:9121']
- job_name: 'php-fpm'
static_configs:
- targets: ['web-1:9253', 'web-2:9253']
Alerting Rules
# rules/magento-alerts.yml
groups:
- name: magento-alerts
rules:
- alert: HighErrorRate
expr: rate(http_requests_total{status=~"5.."}[5m]) / rate(http_requests_total[5m]) > 0.01
for: 5m
labels:
severity: critical
annotations:
summary: 'High error rate detected'
description: 'Error rate is {{ $value | humanizePercentage }}'
- alert: HighLatency
expr: histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m])) > 2
for: 5m
labels:
severity: warning
annotations:
summary: 'High response latency'
description: 'P99 latency is {{ $value }}s'
- alert: LowCacheHitRate
expr: redis_keyspace_hits / (redis_keyspace_hits + redis_keyspace_misses) < 0.7
for: 10m
labels:
severity: warning
annotations:
summary: 'Low cache hit rate'
description: 'Cache hit rate is {{ $value | humanizePercentage }}'
- alert: HighMemoryUsage
expr: (node_memory_MemTotal - node_memory_MemAvailable) / node_memory_MemTotal > 0.9
for: 5m
labels:
severity: critical
annotations:
summary: 'High memory usage'
description: 'Memory usage is {{ $value | humanizePercentage }}'
- alert: MySQLReplicationLag
expr: mysql_slave_status_seconds_behind_master > 10
for: 5m
labels:
severity: warning
annotations:
summary: 'MySQL replication lag'
description: 'Replication lag is {{ $value }}s'
- alert: QueueBacklog
expr: rabbitmq_queue_messages > 5000
for: 10m
labels:
severity: warning
annotations:
summary: 'Queue backlog building up'
description: '{{ $labels.queue }} has {{ $value }} messages'
Grafana Dashboard JSON
{
"dashboard": {
"title": "Magento Production Overview",
"panels": [
{
"title": "Request Rate",
"type": "graph",
"targets": [{
"expr": "rate(http_requests_total[1m])",
"legendFormat": "{{method}} {{status}}"
}],
"thresholds": [{"value": 5000, "color": "red"}]
},
{
"title": "Response Time",
"type": "graph",
"targets": [{
"expr": "histogram_quantile(0.99, rate(http_request_duration_seconds_bucket[5m]))",
"legendFormat": "P99 Latency"
}],
"thresholds": [{"value": 2, "color": "red"}]
},
{
"title": "Error Rate",
"type": "singlestat",
"targets": [{
"expr": "rate(http_requests_total{status=~'5..'}[5m]) / rate(http_requests_total[5m]) * 100"
}],
"thresholds": [
{"value": 1, "color": "yellow"},
{"value": 5, "color": "red"}
]
},
{
"title": "Cache Hit Rate",
"type": "singlestat",
"targets": [{
"expr": "redis_keyspace_hits / (redis_keyspace_hits + redis_keyspace_misses) * 100"
}],
"thresholds": [
{"value": 70, "color": "yellow"},
{"value": 90, "color": "green"}
]
}
]
}
}
Incident Response
Incident Response Runbook
# Incident Response Runbook
## Severity Levels
| Level | Description | Response Time | Escalation |
|-------|--------------------------|---------------|----------------|
| SEV1 | Site down, data loss | 15 minutes | Immediate |
| SEV2 | Major feature broken | 30 minutes | 1 hour |
| SEV3 | Minor feature degraded | 4 hours | 24 hours |
| SEV4 | Cosmetic issue | 24 hours | Next sprint |
## Incident Response Process
1. **Detection** (0-5 min)
- Alert received via PagerDuty
- Acknowledge alert
- Join incident channel
2. **Triage** (5-15 min)
- Assess severity
- Identify affected systems
- Notify stakeholders
3. **Investigation** (15-60 min)
- Check monitoring dashboards
- Review logs
- Identify root cause
4. **Mitigation** (1-2 hours)
- Implement temporary fix
- Restore service
- Monitor for stability
5. **Resolution** (2-24 hours)
- Implement permanent fix
- Verify fix works
- Update documentation
6. **Post-Mortem** (24-48 hours)
- Conduct blameless post-mortem
- Document lessons learned
- Create action items
Common Incident Runbooks
# SEV1: Magento Site Down
## Symptoms
- 5xx errors on all pages
- Health check failing
- Users cannot access site
## Immediate Actions
1. Check load balancer health
2. Check web server status
3. Check database connectivity
4. Check Redis connectivity
## Diagnostic Commands
```bash
# Check web server
sudo systemctl status nginx
sudo systemctl status php8.2-fpm
# Check database
mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';"
# Check Redis
redis-cli ping
# Check disk space
df -h
# Check memory
free -m
# Check logs
tail -100 /var/log/nginx/error.log
tail -100 /var/log/php-fpm/error.log
bin/magento cache:status
Resolution Steps
- If web server down:
sudo systemctl restart nginx php8.2-fpm - If database down:
sudo systemctl restart mysql - If Redis down:
sudo systemctl restart redis - If disk full: Clear old logs, cache
- If memory full: Restart services, check for leaks
```markdown
# SEV2: Slow Performance
## Symptoms
- Response time > 5 seconds
- High CPU/Memory usage
- Users complaining about slowness
## Diagnostic Commands
```bash
# Check top processes
top -bn1 | head -20
# Check PHP-FPM status
curl http://localhost/fpm-status
# Check MySQL queries
mysql -u root -p -e "SHOW PROCESSLIST;"
# Check cache hit rate
redis-cli INFO stats | grep keyspace
# Check Varnish
curl -I http://localhost:8080
Resolution Steps
- Identify resource bottleneck
- Scale web nodes if CPU high
- Add MySQL read replica if DB slow
- Clear cache if hit rate low
- Kill long-running queries
## Post-Mortem Template
```markdown
# Post-Mortem: [Incident Title]
## Summary
- **Date:** [Date]
- **Duration:** [Duration]
- **Severity:** [SEV1/2/3]
- **Impact:** [User impact]
## Timeline
- [Time] Alert triggered
- [Time] Incident declared
- [Time] Root cause identified
- [Time] Mitigation applied
- [Time] Service restored
- [Time] Post-mortem started
## Root Cause
[Detailed root cause analysis]
## What Went Well
- [Thing 1]
- [Thing 2]
## What Went Wrong
- [Thing 1]
- [Thing 2]
## Action Items
| Action | Owner | Due Date | Status |
|--------|-------|----------|--------|
| [Action 1] | [Person] | [Date] | Open |
| [Action 2] | [Person] | [Date] | Open |
## Lessons Learned
- [Lesson 1]
- [Lesson 2]
Operational Excellence
SLA/SLO/SLI Definitions
## Service Level Objectives (SLOs)
| Metric | SLO Target | SLI Measurement |
|---------------------|---------------|------------------------------|
| Availability | 99.9% | Uptime monitoring |
| Latency (P99) | < 2 seconds | APM metrics |
| Error Rate | < 0.1% | HTTP 5xx / total requests |
| Throughput | > 1000 RPS | Load balancer metrics |
| Data Durability | 99.999% | Backup verification |
## Error Budget
- Monthly availability target: 99.9%
- Allowed downtime: 43.8 minutes/month
- Current error budget: [Calculated]
Operational Checklist
## Daily Operations
- [ ] Review monitoring dashboards
- [ ] Check error rates and alerts
- [ ] Verify backup completion
- [ ] Review security logs
- [ ] Check queue depths
## Weekly Operations
- [ ] Performance trend analysis
- [ ] Capacity planning review
- [ ] Security patch assessment
- [ ] Dependency updates
- [ ] Documentation updates
## Monthly Operations
- [ ] Disaster recovery drill
- [ ] Performance load testing
- [ ] Security audit
- [ ] Cost optimization review
- [ ] Team retrospective
## Quarterly Operations
- [ ] Architecture review
- [ ] Technology upgrade planning
- [ ] Compliance audit
- [ ] Business continuity test
- [ ] Vendor review
Runbook Automation
#!/usr/bin/env python3
# auto_scale.py
import boto3
import json
from datetime import datetime, timedelta
class AutoScaler:
def __init__(self):
self.cloudwatch = boto3.client('cloudwatch')
self.asg = boto3.client('autoscaling')
self.asg_name = 'magento-web'
def check_and_scale(self):
metrics = self.get_metrics()
if metrics['cpu_avg'] > 60:
self.scale_up(2)
elif metrics['cpu_avg'] < 30 and metrics['current'] > 2:
self.scale_down(1)
def get_metrics(self):
response = self.cloudwatch.get_metric_statistics(
Namespace='AWS/EC2',
MetricName='CPUUtilization',
Dimensions=[{'Name': 'AutoScalingGroupName', 'Value': self.asg_name}],
StartTime=datetime.utcnow() - timedelta(minutes=5),
EndTime=datetime.utcnow(),
Period=300,
Statistics=['Average']
)
asg = self.asg.describe_auto_scaling_groups(
AutoScalingGroupNames=[self.asg_name]
)
return {
'cpu_avg': response['Datapoints'][0]['Average'] if response['Datapoints'] else 0,
'current': asg['AutoScalingGroups'][0]['DesiredCapacity']
}
def scale_up(self, count):
current = self.get_metrics()['current']
new_count = min(current + count, 16)
self.asg.set_desired_capacity(
AutoScalingGroupName=self.asg_name,
DesiredCapacity=new_count
)
print(f'Scaled up: {current} -> {new_count}')
def scale_down(self, count):
current = self.get_metrics()['current']
new_count = max(current - count, 2)
self.asg.set_desired_capacity(
AutoScalingGroupName=self.asg_name,
DesiredCapacity=new_count
)
print(f'Scaled down: {current} -> {new_count}')
if __name__ == '__main__':
scaler = AutoScaler()
scaler.check_and_scale()
Cost Optimization
## Cost Optimization Strategies
### Infrastructure
- Right-size instances based on actual usage
- Use Reserved Instances for predictable workloads
- Use Spot Instances for non-critical work
- Auto-scale to match traffic patterns
### Database
- Use read replicas for read-heavy workloads
- Archive old data to reduce storage
- Optimize queries to reduce CPU usage
### Cache
- Optimize cache hit rates
- Reduce cache invalidation frequency
- Use CDN for static assets
### Storage
- lifecycle policies for old media
- Compress images automatically
- Clean up unused resources
## Monthly Cost Review
| Component | Current Cost | Optimization | Savings |
|---------------|-------------|--------------|---------|
| EC2 | $X,XXX | Reserved | $XXX |
| RDS | $X,XXX | Right-size | $XXX |
| ElastiCache | $XXX | Cluster | $XX |
| S3 | $XXX | Lifecycle | $XX |
| DataTransfer | $XXX | CDN | $XX |
Quiz
1. What is the purpose of CI/CD?
2. What is a post-mortem?
3. What is SLO?
Flashcards
Question
What is CI/CD?
Click to reveal answer
Answer
Continuous Integration / Continuous Deployment - automated build/test/deploy
Question
What is a runbook?
Click to reveal answer
Answer
Step-by-step guide for handling specific incidents
Question
What is SLO?
Click to reveal answer
Answer
Service Level Objective - target metrics for service performance
Question
What is error budget?
Click to reveal answer
Answer
Allowed downtime based on SLO (e.g., 99.9% = 43.8 min/month)
Question
What is blue-green deployment?
Click to reveal answer
Answer
Two identical environments, switch traffic between them
Revision Notes
Key Takeaways
- 1. CI/CD automates build, test, and deployment processes
- 2. Monitoring uses Prometheus/Grafana for metrics and alerting
- 3. Incident response follows detection → triage → investigation → resolution
- 4. Post-mortems are blameless and focus on improvements
- 5. Operational excellence includes SLAs, checklists, and automation
Interview Tips
- • Describe your CI/CD pipeline and deployment strategy
- • Explain monitoring and alerting setup
- • Discuss incident response process and runbooks
- • Talk about operational excellence practices
Cheat Sheet
CI/CD Pipeline:
Lint → Security → Test → Build → Deploy → Verify
Staging → Production (with approval)
Rollback on failure
Monitoring:
Prometheus → Metrics collection
Grafana → Visualization dashboards
Alertmanager → Alert routing
ELK → Centralized logging
Incident Response:
SEV1: 15min response, immediate escalation
SEV2: 30min response, 1hr escalation
SEV3: 4hr response, 24hr escalation
SEV4: 24hr response, next sprint
Post-Mortem:
Blameless analysis
Timeline, root cause, action items
Lessons learned
Operational Excellence:
SLA: 99.9% availability
SLO: <2s latency, <0.1% errors
Error budget: 43.8 min/month
Daily/weekly/monthly checklists