chore: import upstream snapshot with attribution
Component Security Validation / Security Audit (push) Has been cancelled
Deploy to Cloudflare Pages / deploy (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 12:38:58 +08:00
commit bb5c75ce05
8824 changed files with 1946442 additions and 0 deletions
@@ -0,0 +1,695 @@
# CI/CD Pipelines & GitOps
## CI/CD Pipeline Stages
### 1. Source → 2. Build → 3. Test → 4. Deploy → 5. Monitor
```
┌──────────┐ ┌───────┐ ┌──────┐ ┌────────┐ ┌─────────┐
│ Source │──▶│ Build │──▶│ Test │──▶│ Deploy │──▶│ Monitor │
│ (Git) │ │ │ │ │ │ │ │ │
└──────────┘ └───────┘ └──────┘ └────────┘ └─────────┘
```
## GitOps Principles
### Core Concepts
1. **Git as Single Source of Truth**: All configuration in Git
2. **Declarative**: Desired state defined, not imperative steps
3. **Automated**: Continuous reconciliation of desired vs actual state
4. **Auditable**: All changes tracked in Git history
### GitOps Workflow
```
Developer ──▶ Git Push ──▶ GitOps Controller ──▶ Kubernetes Cluster
(ArgoCD/Flux)
Continuous Sync
```
## ArgoCD Setup
### Installation
```bash
# Install ArgoCD
kubectl create namespace argocd
kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
# Access ArgoCD UI
kubectl port-forward svc/argocd-server -n argocd 8080:443
# Get initial password
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 -d
```
### ArgoCD Application
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp-production
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/myorg/myapp-gitops.git
targetRevision: main
path: kubernetes/overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true # Delete resources not in Git
selfHeal: true # Sync if cluster state drifts
allowEmpty: false
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas # Ignore HPA-managed replicas
```
### ArgoCD ApplicationSet (Multi-Environment)
```yaml
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: myapp
namespace: argocd
spec:
generators:
- list:
elements:
- env: dev
cluster: https://kubernetes.default.svc
- env: staging
cluster: https://kubernetes.default.svc
- env: production
cluster: https://prod-cluster-api.example.com
template:
metadata:
name: 'myapp-{{env}}'
spec:
project: default
source:
repoURL: https://github.com/myorg/myapp-gitops.git
targetRevision: main
path: 'kubernetes/overlays/{{env}}'
destination:
server: '{{cluster}}'
namespace: '{{env}}'
syncPolicy:
automated:
prune: true
selfHeal: true
```
## Flux CD Setup
### Installation
```bash
# Install Flux CLI
brew install fluxcd/tap/flux
# Check prerequisites
flux check --pre
# Bootstrap Flux
flux bootstrap github \
--owner=myorg \
--repository=fleet-infra \
--branch=main \
--path=clusters/production \
--personal
```
### Flux GitRepository
```yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: myapp
namespace: flux-system
spec:
interval: 1m
url: https://github.com/myorg/myapp-gitops
ref:
branch: main
secretRef:
name: git-credentials
```
### Flux Kustomization
```yaml
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: myapp-production
namespace: flux-system
spec:
interval: 5m
path: ./kubernetes/overlays/production
prune: true
sourceRef:
kind: GitRepository
name: myapp
healthChecks:
- apiVersion: apps/v1
kind: Deployment
name: myapp
namespace: production
timeout: 2m
```
## Progressive Delivery
### Canary Deployment with Flagger
```yaml
apiVersion: flagger.app/v1beta1
kind: Canary
metadata:
name: myapp
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
service:
port: 80
targetPort: 8080
analysis:
interval: 1m
threshold: 5
maxWeight: 50
stepWeight: 10
metrics:
- name: request-success-rate
thresholdRange:
min: 99
interval: 1m
- name: request-duration
thresholdRange:
max: 500
interval: 1m
webhooks:
- name: load-test
url: http://flagger-loadtester/
timeout: 5s
metadata:
cmd: "hey -z 1m -q 10 -c 2 http://myapp-canary.production/"
- name: smoke-test
url: http://flagger-loadtester/
timeout: 5s
metadata:
type: smoke
cmd: "curl -s http://myapp-canary.production/healthz | grep ok"
```
### Blue/Green Deployment
```yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: production
spec:
selector:
app: myapp
version: blue # Switch to 'green' for deployment
ports:
- port: 80
targetPort: 8080
---
# Blue Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-blue
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: blue
template:
metadata:
labels:
app: myapp
version: blue
spec:
containers:
- name: myapp
image: myapp:v1.0.0
---
# Green Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp-green
namespace: production
spec:
replicas: 3
selector:
matchLabels:
app: myapp
version: green
template:
metadata:
labels:
app: myapp
version: green
spec:
containers:
- name: myapp
image: myapp:v2.0.0
```
## CI/CD Pipeline Examples
### GitHub Actions - Complete Pipeline
See [templates.md](templates.md) for full GitHub Actions pipeline
### GitLab CI Pipeline
```yaml
variables:
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
IMAGE_TAG: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
stages:
- lint
- build
- test
- security
- deploy
# Lint stage
lint:code:
stage: lint
image: node:20-alpine
script:
- npm ci
- npm run lint
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- node_modules/
lint:dockerfile:
stage: lint
image: hadolint/hadolint:latest-alpine
script:
- hadolint Dockerfile
# Build stage
build:image:
stage: build
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build --cache-from $CI_REGISTRY_IMAGE:latest -t $IMAGE_TAG .
- docker tag $IMAGE_TAG $CI_REGISTRY_IMAGE:latest
- docker push $IMAGE_TAG
- docker push $CI_REGISTRY_IMAGE:latest
only:
- main
- develop
# Test stage
test:unit:
stage: test
image: node:20-alpine
script:
- npm ci
- npm run test:unit
coverage: '/Statements\s*:\s*(\d+\.\d+)%/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
test:integration:
stage: test
image: $IMAGE_TAG
services:
- postgres:15-alpine
variables:
POSTGRES_DB: testdb
POSTGRES_USER: testuser
POSTGRES_PASSWORD: testpass
script:
- npm run test:integration
# Security stage
security:trivy:
stage: security
image: aquasec/trivy:latest
script:
- trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE_TAG
allow_failure: true
security:secrets:
stage: security
image: trufflesecurity/trufflehog:latest
script:
- trufflehog filesystem . --fail
# Deploy stage
deploy:dev:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context dev-cluster
- kubectl set image deployment/myapp myapp=$IMAGE_TAG -n development
- kubectl rollout status deployment/myapp -n development
environment:
name: development
only:
- develop
deploy:staging:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context staging-cluster
- kubectl set image deployment/myapp myapp=$IMAGE_TAG -n staging
- kubectl rollout status deployment/myapp -n staging
environment:
name: staging
only:
- main
deploy:production:
stage: deploy
image: bitnami/kubectl:latest
script:
- kubectl config use-context prod-cluster
- kubectl set image deployment/myapp myapp=$IMAGE_TAG -n production
- kubectl rollout status deployment/myapp -n production
environment:
name: production
when: manual
only:
- main
```
### Jenkins Pipeline (Jenkinsfile)
```groovy
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'ghcr.io'
IMAGE_NAME = 'myorg/myapp'
IMAGE_TAG = "${env.GIT_COMMIT.take(7)}"
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Lint') {
parallel {
stage('Lint Code') {
steps {
sh 'npm ci'
sh 'npm run lint'
}
}
stage('Lint Dockerfile') {
steps {
sh 'hadolint Dockerfile'
}
}
}
}
stage('Build') {
steps {
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-credentials') {
def app = docker.build("${IMAGE_NAME}:${IMAGE_TAG}")
app.push()
app.push('latest')
}
}
}
}
stage('Test') {
parallel {
stage('Unit Tests') {
steps {
sh 'npm run test:unit'
}
}
stage('Integration Tests') {
steps {
sh 'npm run test:integration'
}
}
}
}
stage('Security Scan') {
steps {
sh "trivy image --exit-code 1 --severity HIGH,CRITICAL ${IMAGE_NAME}:${IMAGE_TAG}"
}
}
stage('Deploy to Dev') {
when {
branch 'develop'
}
steps {
kubernetesDeploy(
configs: 'kubernetes/overlays/dev',
kubeconfigId: 'dev-kubeconfig'
)
}
}
stage('Deploy to Production') {
when {
branch 'main'
}
steps {
input message: 'Deploy to production?', ok: 'Deploy'
kubernetesDeploy(
configs: 'kubernetes/overlays/prod',
kubeconfigId: 'prod-kubeconfig'
)
}
}
}
post {
success {
slackSend(
color: 'good',
message: "✅ Build ${env.BUILD_NUMBER} succeeded: ${env.JOB_NAME}"
)
}
failure {
slackSend(
color: 'danger',
message: "❌ Build ${env.BUILD_NUMBER} failed: ${env.JOB_NAME}"
)
}
}
}
```
## Infrastructure Testing
### Terratest (Go)
```go
package test
import (
"testing"
"github.com/gruntwork-io/terratest/modules/terraform"
"github.com/stretchr/testify/assert"
)
func TestTerraformVPCModule(t *testing.T) {
t.Parallel()
terraformOptions := terraform.WithDefaultRetryableErrors(t, &terraform.Options{
TerraformDir: "../modules/vpc",
Vars: map[string]interface{}{
"name_prefix": "test",
"vpc_cidr": "10.0.0.0/16",
},
})
defer terraform.Destroy(t, terraformOptions)
terraform.InitAndApply(t, terraformOptions)
vpcID := terraform.Output(t, terraformOptions, "vpc_id")
assert.NotEmpty(t, vpcID)
}
```
### Checkov (Infrastructure Security)
```bash
# Scan Terraform
checkov -d terraform/ --framework terraform
# Scan Kubernetes
checkov -d kubernetes/ --framework kubernetes
# Output JSON
checkov -d terraform/ --framework terraform -o json > results.json
# Skip specific checks
checkov -d terraform/ --skip-check CKV_AWS_23
```
### Kitchen-Terraform
```ruby
# kitchen.yml
driver:
name: terraform
provisioner:
name: terraform
verifier:
name: terraform
systems:
- name: default
backend: aws
controls:
- vpc_test
platforms:
- name: aws
suites:
- name: default
driver:
variables:
region: us-east-1
```
## Deployment Strategies
### Rolling Update (Default in Kubernetes)
```yaml
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # Max pods above desired during update
maxUnavailable: 0 # Zero-downtime deployment
```
### Recreate (Downtime Acceptable)
```yaml
spec:
strategy:
type: Recreate # Kill all pods, then create new ones
```
### Canary (Gradual Rollout)
- Deploy new version alongside old
- Route small % of traffic to new version
- Monitor metrics
- Gradually increase traffic
- Rollback if issues detected
### Blue/Green (Instant Switch)
- Deploy new version (green) alongside old (blue)
- Test green environment
- Switch traffic from blue to green
- Keep blue for quick rollback
## Rollback Procedures
### Kubernetes Rollback
```bash
# View rollout history
kubectl rollout history deployment/myapp -n production
# Rollback to previous version
kubectl rollout undo deployment/myapp -n production
# Rollback to specific revision
kubectl rollout undo deployment/myapp -n production --to-revision=3
# Check rollout status
kubectl rollout status deployment/myapp -n production
```
### ArgoCD Rollback
```bash
# Rollback application
argocd app rollback myapp-production
# Rollback to specific revision
argocd app rollback myapp-production 123
```
## CI/CD Best Practices
1. **Automate Everything**: From code commit to production
2. **Fast Feedback**: Fail fast, fix fast
3. **Test in Production-like**: Staging mirrors production
4. **Gradual Rollout**: Canary or blue/green for production
5. **Easy Rollback**: One-click rollback capability
6. **Security Scanning**: Every stage, every pipeline
7. **Infrastructure as Code**: No manual changes
8. **GitOps**: Git as single source of truth
9. **Observability**: Monitor every deployment
10. **Post-Deployment Tests**: Smoke tests after deploy
---
## Pipeline Performance Optimization
- **Caching**: Cache dependencies between runs
- **Parallel Execution**: Run independent stages in parallel
- **Docker Layer Caching**: Use BuildKit and layer caching
- **Artifacts**: Share build artifacts between stages
- **Resource Limits**: Optimize CI runner resources
- **Skip Unnecessary**: Only run tests for changed code
@@ -0,0 +1,695 @@
# Cloud Platforms: AWS, Azure, GCP
## Cloud Platform Selection
### AWS (Amazon Web Services)
- **Strengths**: Market leader, most services, mature ecosystem
- **Best for**: Enterprise, startups, wide service selection
- **Key Services**: EC2, EKS, RDS, S3, Lambda, CloudFormation
### Azure (Microsoft Azure)
- **Strengths**: Enterprise integration, hybrid cloud, Microsoft stack
- **Best for**: Windows workloads, hybrid scenarios, Microsoft shops
- **Key Services**: VMs, AKS, SQL Database, Blob Storage, ARM Templates
### GCP (Google Cloud Platform)
- **Strengths**: Kubernetes-native, ML/AI, data analytics
- **Best for**: Kubernetes, data processing, ML workloads
- **Key Services**: Compute Engine, GKE, Cloud SQL, Cloud Storage, Deployment Manager
## AWS Architecture Patterns
### Multi-Tier Web Application
```
Internet
┌─────────────────┐
│ CloudFront │ CDN
│ (Global) │
└────────┬────────┘
┌────▼────┐
│ ALB │ Load Balancer
└────┬────┘
┌────▼──────────┐
│ ECS/EKS │ Application Layer
│ (Multi-AZ) │
└────┬──────────┘
┌────▼────┐
│ RDS │ Database Layer
│ (Multi-AZ)│
└─────────┘
```
### AWS Well-Architected Framework Pillars
**1. Operational Excellence**
- IaC (Terraform/CloudFormation)
- CI/CD automation
- Monitoring and observability
**2. Security**
- IAM least privilege
- Encryption at rest and in transit
- Network segmentation (VPC, Security Groups)
**3. Reliability**
- Multi-AZ deployment
- Auto Scaling
- Backup and disaster recovery
**4. Performance Efficiency**
- Right-sizing instances
- CloudFront for content delivery
- ElastiCache for caching
**5. Cost Optimization**
- Reserved Instances
- Spot Instances
- Auto Scaling based on demand
**6. Sustainability**
- Region selection for renewable energy
- Right-sizing to minimize waste
### AWS Core Services
#### Compute
```hcl
# EC2 Instance
resource "aws_instance" "app" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = "t3.medium"
vpc_security_group_ids = [aws_security_group.app.id]
subnet_id = aws_subnet.private[0].id
iam_instance_profile = aws_iam_instance_profile.app.name
user_data = <<-EOF
#!/bin/bash
yum update -y
yum install -y docker
systemctl start docker
EOF
tags = {
Name = "${var.name_prefix}-app-server"
}
}
# ECS Fargate
resource "aws_ecs_service" "app" {
name = "${var.name_prefix}-service"
cluster = aws_ecs_cluster.main.id
task_definition = aws_ecs_task_definition.app.arn
desired_count = 3
launch_type = "FARGATE"
network_configuration {
subnets = aws_subnet.private[*].id
security_groups = [aws_security_group.app.id]
assign_public_ip = false
}
load_balancer {
target_group_arn = aws_lb_target_group.app.arn
container_name = "app"
container_port = 8080
}
}
# Lambda Function
resource "aws_lambda_function" "processor" {
filename = "lambda.zip"
function_name = "${var.name_prefix}-processor"
role = aws_iam_role.lambda.arn
handler = "index.handler"
runtime = "nodejs20.x"
environment {
variables = {
ENV = var.environment
}
}
}
```
#### Storage
```hcl
# S3 Bucket
resource "aws_s3_bucket" "data" {
bucket = "${var.name_prefix}-data-bucket"
}
resource "aws_s3_bucket_versioning" "data" {
bucket = aws_s3_bucket.data.id
versioning_configuration {
status = "Enabled"
}
}
resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
bucket = aws_s3_bucket.data.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.s3.arn
}
}
}
# EBS Volume
resource "aws_ebs_volume" "data" {
availability_zone = var.availability_zone
size = 100
type = "gp3"
encrypted = true
kms_key_id = aws_kms_key.ebs.arn
tags = {
Name = "${var.name_prefix}-data-volume"
}
}
# EFS File System
resource "aws_efs_file_system" "shared" {
encrypted = true
kms_key_id = aws_kms_key.efs.arn
lifecycle_policy {
transition_to_ia = "AFTER_30_DAYS"
}
tags = {
Name = "${var.name_prefix}-efs"
}
}
```
#### Database
```hcl
# RDS PostgreSQL
resource "aws_db_instance" "main" {
identifier = "${var.name_prefix}-db"
engine = "postgres"
engine_version = "15.4"
instance_class = "db.t3.medium"
allocated_storage = 100
max_allocated_storage = 1000
storage_type = "gp3"
storage_encrypted = true
multi_az = true
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.db.id]
backup_retention_period = 7
backup_window = "03:00-04:00"
maintenance_window = "sun:04:00-sun:05:00"
enabled_cloudwatch_logs_exports = ["postgresql"]
deletion_protection = var.environment == "prod"
}
# DynamoDB Table
resource "aws_dynamodb_table" "sessions" {
name = "${var.name_prefix}-sessions"
billing_mode = "PAY_PER_REQUEST"
hash_key = "session_id"
attribute {
name = "session_id"
type = "S"
}
ttl {
attribute_name = "ttl"
enabled = true
}
point_in_time_recovery {
enabled = true
}
server_side_encryption {
enabled = true
}
}
```
## Azure Architecture Patterns
### Multi-Tier Application on Azure
```
Internet
┌─────────────────┐
│ Azure Front │ CDN + WAF
│ Door │
└────────┬────────┘
┌────▼────────┐
│ App GW │ Load Balancer
└────┬────────┘
┌────▼──────────┐
│ AKS │ Application Layer
│ (Multi-Zone) │
└────┬──────────┘
┌────▼──────────┐
│ Azure SQL DB │ Database Layer
│ (Geo-Replica) │
└───────────────┘
```
### Azure Core Services
#### Compute
```hcl
# Virtual Machine
resource "azurerm_linux_virtual_machine" "app" {
name = "${var.name_prefix}-vm"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
size = "Standard_D2s_v3"
network_interface_ids = [
azurerm_network_interface.app.id,
]
os_disk {
caching = "ReadWrite"
storage_account_type = "Premium_LRS"
}
source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-focal"
sku = "20_04-lts"
version = "latest"
}
admin_username = "azureuser"
admin_ssh_key {
username = "azureuser"
public_key = file("~/.ssh/id_rsa.pub")
}
}
# AKS Cluster
resource "azurerm_kubernetes_cluster" "main" {
name = "${var.name_prefix}-aks"
location = azurerm_resource_group.main.location
resource_group_name = azurerm_resource_group.main.name
dns_prefix = var.name_prefix
default_node_pool {
name = "default"
node_count = 3
vm_size = "Standard_D2s_v3"
os_disk_size_gb = 100
}
identity {
type = "SystemAssigned"
}
network_profile {
network_plugin = "azure"
network_policy = "calico"
}
}
# Azure Functions
resource "azurerm_linux_function_app" "processor" {
name = "${var.name_prefix}-func"
resource_group_name = azurerm_resource_group.main.name
location = azurerm_resource_group.main.location
storage_account_name = azurerm_storage_account.func.name
storage_account_access_key = azurerm_storage_account.func.primary_access_key
service_plan_id = azurerm_service_plan.func.id
site_config {
application_stack {
node_version = "20"
}
}
}
```
## GCP Architecture Patterns
### Multi-Tier Application on GCP
```
Internet
┌─────────────────┐
│ Cloud CDN │
└────────┬────────┘
┌────▼────────┐
│ Cloud LB │ Global Load Balancer
└────┬────────┘
┌────▼──────────┐
│ GKE │ Application Layer
│ (Multi-Zone) │
└────┬──────────┘
┌────▼──────────┐
│ Cloud SQL │ Database Layer
│ (HA) │
└───────────────┘
```
### GCP Core Services
#### Compute
```hcl
# Compute Engine Instance
resource "google_compute_instance" "app" {
name = "${var.name_prefix}-vm"
machine_type = "e2-medium"
zone = var.zone
boot_disk {
initialize_params {
image = "ubuntu-os-cloud/ubuntu-2004-lts"
size = 50
type = "pd-ssd"
}
}
network_interface {
network = google_compute_network.vpc.name
subnetwork = google_compute_subnetwork.private.name
access_config {
// Ephemeral public IP
}
}
metadata_startup_script = file("startup.sh")
service_account {
scopes = ["cloud-platform"]
}
}
# GKE Cluster
resource "google_container_cluster" "main" {
name = "${var.name_prefix}-gke"
location = var.region
# Remove default node pool
remove_default_node_pool = true
initial_node_count = 1
network = google_compute_network.vpc.name
subnetwork = google_compute_subnetwork.private.name
ip_allocation_policy {
cluster_ipv4_cidr_block = "/16"
services_ipv4_cidr_block = "/22"
}
workload_identity_config {
workload_pool = "${var.project_id}.svc.id.goog"
}
}
resource "google_container_node_pool" "main" {
name = "main-pool"
location = var.region
cluster = google_container_cluster.main.name
node_count = 1
autoscaling {
min_node_count = 1
max_node_count = 10
}
node_config {
machine_type = "e2-medium"
disk_size_gb = 100
disk_type = "pd-standard"
oauth_scopes = [
"https://www.googleapis.com/auth/cloud-platform"
]
workload_metadata_config {
mode = "GKE_METADATA"
}
}
}
```
## Multi-Cloud Strategy
### When to Use Multi-Cloud
**Avoid vendor lock-in**
**Leverage best-of-breed services**
**Geographic requirements**
**Disaster recovery**
### Multi-Cloud Challenges
✗ Increased complexity
✗ Higher operational overhead
✗ Different APIs and tools
✗ Data transfer costs
### Multi-Cloud Tools
- **Terraform**: Unified IaC across clouds
- **Kubernetes**: Consistent compute layer
- **Service Mesh**: Unified networking
- **OpenTelemetry**: Unified observability
## Cost Optimization Strategies
### AWS Cost Optimization
```hcl
# Savings Plans / Reserved Instances
# Purchase via AWS Console or API
# Spot Instances
resource "aws_autoscaling_group" "app" {
mixed_instances_policy {
instances_distribution {
on_demand_base_capacity = 1
on_demand_percentage_above_base_capacity = 20
spot_allocation_strategy = "capacity-optimized"
}
launch_template {
launch_template_specification {
launch_template_id = aws_launch_template.app.id
}
override {
instance_type = "t3.medium"
}
override {
instance_type = "t3a.medium"
}
}
}
}
# S3 Lifecycle Policy
resource "aws_s3_bucket_lifecycle_configuration" "data" {
bucket = aws_s3_bucket.data.id
rule {
id = "archive-old-data"
status = "Enabled"
transition {
days = 30
storage_class = "STANDARD_IA"
}
transition {
days = 90
storage_class = "GLACIER"
}
expiration {
days = 365
}
}
}
```
### Cost Monitoring
```hcl
# AWS Budget
resource "aws_budgets_budget" "monthly" {
name = "monthly-budget"
budget_type = "COST"
limit_amount = "1000"
limit_unit = "USD"
time_unit = "MONTHLY"
notification {
comparison_operator = "GREATER_THAN"
threshold = 80
threshold_type = "PERCENTAGE"
notification_type = "FORECASTED"
subscriber_email_addresses = ["devops@example.com"]
}
}
```
## Disaster Recovery
### RTO/RPO Targets
- **RTO (Recovery Time Objective)**: Maximum acceptable downtime
- **RPO (Recovery Point Objective)**: Maximum acceptable data loss
### DR Strategies (Lowest to Highest Cost)
**1. Backup & Restore (RPO: hours, RTO: hours)**
- Regular backups to cloud storage
- Restore when needed
**2. Pilot Light (RPO: minutes, RTO: hours)**
- Minimal infrastructure always running
- Scale up when needed
**3. Warm Standby (RPO: seconds, RTO: minutes)**
- Scaled-down version running
- Scale up for failover
**4. Multi-Site Active/Active (RPO: none, RTO: none)**
- Full capacity in multiple regions
- Traffic distributed across sites
### Multi-Region Setup (AWS)
```hcl
provider "aws" {
alias = "primary"
region = "us-east-1"
}
provider "aws" {
alias = "dr"
region = "us-west-2"
}
# Primary region resources
module "vpc_primary" {
source = "./modules/vpc"
providers = {
aws = aws.primary
}
}
# DR region resources
module "vpc_dr" {
source = "./modules/vpc"
providers = {
aws = aws.dr
}
}
# Route53 health check and failover
resource "aws_route53_health_check" "primary" {
fqdn = aws_lb.primary.dns_name
port = 443
type = "HTTPS"
resource_path = "/health"
failure_threshold = "3"
request_interval = "30"
}
resource "aws_route53_record" "app" {
zone_id = aws_route53_zone.main.id
name = "app.example.com"
type = "A"
set_identifier = "primary"
failover_routing_policy {
type = "PRIMARY"
}
alias {
name = aws_lb.primary.dns_name
zone_id = aws_lb.primary.zone_id
evaluate_target_health = true
}
health_check_id = aws_route53_health_check.primary.id
}
resource "aws_route53_record" "app_dr" {
zone_id = aws_route53_zone.main.id
name = "app.example.com"
type = "A"
set_identifier = "secondary"
failover_routing_policy {
type = "SECONDARY"
}
alias {
name = aws_lb.dr.dns_name
zone_id = aws_lb.dr.zone_id
evaluate_target_health = true
}
}
```
## Best Practices Summary
### AWS
- Use IAM roles, not access keys
- Enable CloudTrail in all regions
- Encrypt everything (S3, EBS, RDS)
- Use VPC for network isolation
- Tag all resources for cost allocation
### Azure
- Use Managed Identities
- Enable Azure Policy for governance
- Use Azure Key Vault for secrets
- Implement RBAC
- Use Resource Groups for organization
### GCP
- Use Service Accounts with least privilege
- Enable Cloud Audit Logs
- Use VPC Service Controls
- Implement Organization Policies
- Use Labels for resource management
---
## Cloud Service Comparison
| Service Type | AWS | Azure | GCP |
|--------------|-----|-------|-----|
| Compute | EC2 | Virtual Machines | Compute Engine |
| Containers | ECS, EKS | AKS | GKE |
| Serverless | Lambda | Functions | Cloud Functions |
| Storage | S3 | Blob Storage | Cloud Storage |
| Database (SQL) | RDS | SQL Database | Cloud SQL |
| Database (NoSQL) | DynamoDB | Cosmos DB | Firestore |
| Networking | VPC | Virtual Network | VPC |
| Load Balancer | ALB/NLB | App Gateway | Cloud Load Balancing |
| CDN | CloudFront | Front Door | Cloud CDN |
| IAM | IAM | Azure AD | Cloud IAM |
@@ -0,0 +1,994 @@
# Kubernetes & Container Orchestration
## Kubernetes Architecture Essentials
### Core Components
- **Control Plane**: API Server, Scheduler, Controller Manager, etcd
- **Worker Nodes**: Kubelet, Kube-proxy, Container Runtime
- **Add-ons**: CoreDNS, Metrics Server, Ingress Controller
### Key Kubernetes Resources
- **Workloads**: Pods, Deployments, StatefulSets, DaemonSets, Jobs, CronJobs
- **Networking**: Services, Ingress, NetworkPolicies
- **Configuration**: ConfigMaps, Secrets
- **Storage**: PersistentVolumes, PersistentVolumeClaims, StorageClasses
- **Access Control**: ServiceAccounts, Roles, RoleBindings, ClusterRoles, ClusterRoleBindings
## Production-Ready Deployment Pattern
### Deployment with Best Practices
```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: production
labels:
app: myapp
version: v1.0.0
environment: production
spec:
replicas: 3
revisionHistoryLimit: 10
# Deployment strategy
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0 # Zero-downtime deployment
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
version: v1.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
spec:
# Security context at pod level
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
# Service account for pod identity
serviceAccountName: myapp
# Init container for setup tasks
initContainers:
- name: init-config
image: busybox:1.36
command: ['sh', '-c', 'echo Initializing... && sleep 2']
securityContext:
allowPrivilegeEscalation: false
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
containers:
- name: myapp
image: myapp:1.0.0
imagePullPolicy: IfNotPresent
# Resource limits and requests
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
# Container security
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
# Health checks
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
timeoutSeconds: 5
failureThreshold: 3
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
timeoutSeconds: 3
successThreshold: 1
failureThreshold: 3
startupProbe:
httpGet:
path: /startup
port: 8080
initialDelaySeconds: 0
periodSeconds: 10
failureThreshold: 30 # 5 minutes max startup time
# Environment variables
env:
- name: ENV
value: "production"
- name: LOG_LEVEL
value: "info"
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
# Environment from ConfigMap
envFrom:
- configMapRef:
name: myapp-config
- secretRef:
name: myapp-secrets
# Container ports
ports:
- name: http
containerPort: 8080
protocol: TCP
- name: metrics
containerPort: 9090
protocol: TCP
# Volume mounts
volumeMounts:
- name: config
mountPath: /etc/myapp
readOnly: true
- name: secrets
mountPath: /etc/secrets
readOnly: true
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache
# Volumes
volumes:
- name: config
configMap:
name: myapp-config
- name: secrets
secret:
secretName: myapp-secrets
defaultMode: 0400
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
# Pod scheduling
affinity:
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 100
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- myapp
topologyKey: kubernetes.io/hostname
# Tolerations for node taints
tolerations:
- key: "node-role.kubernetes.io/spot"
operator: "Exists"
effect: "NoSchedule"
```
### Service Configuration
```yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: production
labels:
app: myapp
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
spec:
type: LoadBalancer
selector:
app: myapp
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
- name: https
port: 443
targetPort: 8443
protocol: TCP
# Session affinity (optional)
sessionAffinity: ClientIP
sessionAffinityConfig:
clientIP:
timeoutSeconds: 10800
---
apiVersion: v1
kind: Service
metadata:
name: myapp-headless
namespace: production
spec:
clusterIP: None # Headless service for StatefulSets
selector:
app: myapp
ports:
- name: http
port: 8080
targetPort: 8080
```
### Ingress with TLS
```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
namespace: production
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/rate-limit: "100"
nginx.ingress.kubernetes.io/proxy-body-size: "10m"
spec:
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 8080
```
## Configuration Management
### ConfigMap
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: production
data:
# Simple key-value pairs
app.env: "production"
log.level: "info"
# Multi-line configuration files
application.yaml: |
server:
port: 8080
host: 0.0.0.0
database:
max_connections: 100
timeout: 30s
cache:
ttl: 3600
max_size: 1000
```
### Secrets Management
```yaml
# ❌ NOT RECOMMENDED - Base64 is not encryption!
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
namespace: production
type: Opaque
data:
db-password: cGFzc3dvcmQxMjM= # base64 encoded
---
# ✅ BETTER - Use Sealed Secrets
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: myapp-secrets
namespace: production
spec:
encryptedData:
db-password: AgBqV7zJ8... # Encrypted with controller's public key
---
# ✅ BEST - Use External Secrets Operator with AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: myapp-secrets
creationPolicy: Owner
data:
- secretKey: db-password
remoteRef:
key: prod/myapp/db-password
- secretKey: api-key
remoteRef:
key: prod/myapp/api-key
```
## StatefulSets for Stateful Applications
```yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: postgres
namespace: production
spec:
serviceName: postgres-headless
replicas: 3
selector:
matchLabels:
app: postgres
template:
metadata:
labels:
app: postgres
spec:
securityContext:
fsGroup: 999
runAsUser: 999
containers:
- name: postgres
image: postgres:15-alpine
env:
- name: POSTGRES_DB
value: myapp
- name: POSTGRES_USER
valueFrom:
secretKeyRef:
name: postgres-secrets
key: username
- name: POSTGRES_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-secrets
key: password
- name: PGDATA
value: /var/lib/postgresql/data/pgdata
ports:
- name: postgres
containerPort: 5432
resources:
requests:
memory: "1Gi"
cpu: "500m"
limits:
memory: "2Gi"
cpu: "1000m"
livenessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U $POSTGRES_USER -d $POSTGRES_DB
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command:
- /bin/sh
- -c
- pg_isready -U $POSTGRES_USER -d $POSTGRES_DB
initialDelaySeconds: 5
periodSeconds: 5
volumeMounts:
- name: data
mountPath: /var/lib/postgresql/data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: gp3
resources:
requests:
storage: 100Gi
```
## Autoscaling
### Horizontal Pod Autoscaler (HPA)
```yaml
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp-hpa
namespace: production
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 3
maxReplicas: 20
metrics:
# CPU-based scaling
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
# Memory-based scaling
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
# Custom metrics (requires metrics adapter)
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "1000"
behavior:
scaleDown:
stabilizationWindowSeconds: 300
policies:
- type: Percent
value: 50
periodSeconds: 60
scaleUp:
stabilizationWindowSeconds: 0
policies:
- type: Percent
value: 100
periodSeconds: 15
- type: Pods
value: 4
periodSeconds: 15
selectPolicy: Max
```
### Vertical Pod Autoscaler (VPA)
```yaml
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
name: myapp-vpa
namespace: production
spec:
targetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
updatePolicy:
updateMode: "Auto" # "Off", "Initial", "Recreate", or "Auto"
resourcePolicy:
containerPolicies:
- containerName: myapp
minAllowed:
cpu: 100m
memory: 128Mi
maxAllowed:
cpu: 2000m
memory: 2Gi
controlledResources:
- cpu
- memory
```
## RBAC (Role-Based Access Control)
### ServiceAccount, Role, and RoleBinding
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp
namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: myapp-role
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: myapp-rolebinding
namespace: production
subjects:
- kind: ServiceAccount
name: myapp
namespace: production
roleRef:
kind: Role
name: myapp-role
apiGroup: rbac.authorization.k8s.io
```
### ClusterRole for Cluster-Wide Permissions
```yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-pods-global
subjects:
- kind: ServiceAccount
name: myapp
namespace: production
roleRef:
kind: ClusterRole
name: pod-reader
apiGroup: rbac.authorization.k8s.io
```
## Network Policies
### Restrict Ingress Traffic
```yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: myapp-netpol
namespace: production
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Ingress
- Egress
ingress:
# Allow traffic from nginx ingress controller
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
- podSelector:
matchLabels:
app: nginx-ingress
ports:
- protocol: TCP
port: 8080
# Allow traffic from prometheus for metrics
- from:
- namespaceSelector:
matchLabels:
name: monitoring
- podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090
egress:
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# Allow database access
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
# Allow external HTTPS
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443
```
## Jobs and CronJobs
### Job for One-Time Task
```yaml
apiVersion: batch/v1
kind: Job
metadata:
name: database-migration
namespace: production
spec:
backoffLimit: 3
activeDeadlineSeconds: 600 # 10 minutes timeout
template:
metadata:
labels:
app: migration
spec:
restartPolicy: OnFailure
containers:
- name: migrate
image: myapp:1.0.0
command: ["/app/migrate"]
args: ["--direction", "up"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: myapp-secrets
key: database-url
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
```
### CronJob for Scheduled Tasks
```yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-database
namespace: production
spec:
schedule: "0 2 * * *" # Daily at 2 AM
timeZone: "America/New_York"
concurrencyPolicy: Forbid # Don't allow concurrent runs
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 1
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 3600 # 1 hour timeout
template:
spec:
restartPolicy: OnFailure
containers:
- name: backup
image: postgres:15-alpine
command:
- /bin/sh
- -c
- |
pg_dump -h $DB_HOST -U $DB_USER -d $DB_NAME | \
gzip > /backup/backup-$(date +%Y%m%d-%H%M%S).sql.gz
envFrom:
- secretRef:
name: postgres-secrets
volumeMounts:
- name: backup
mountPath: /backup
volumes:
- name: backup
persistentVolumeClaim:
claimName: backup-pvc
```
## Helm Charts
### Chart Structure
```
myapp-chart/
├── Chart.yaml
├── values.yaml
├── values-dev.yaml
├── values-prod.yaml
├── templates/
│ ├── deployment.yaml
│ ├── service.yaml
│ ├── ingress.yaml
│ ├── configmap.yaml
│ ├── secret.yaml
│ ├── hpa.yaml
│ ├── serviceaccount.yaml
│ ├── NOTES.txt
│ └── _helpers.tpl
└── README.md
```
### Chart.yaml
```yaml
apiVersion: v2
name: myapp
description: A Helm chart for MyApp
type: application
version: 1.0.0
appVersion: "1.0.0"
keywords:
- myapp
- web
maintainers:
- name: DevOps Team
email: devops@example.com
dependencies:
- name: postgresql
version: "12.x.x"
repository: https://charts.bitnami.com/bitnami
condition: postgresql.enabled
```
### values.yaml
```yaml
replicaCount: 3
image:
repository: myapp
pullPolicy: IfNotPresent
tag: "" # Defaults to chart appVersion
imagePullSecrets: []
serviceAccount:
create: true
annotations: {}
name: ""
podAnnotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
podSecurityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
service:
type: ClusterIP
port: 80
targetPort: 8080
ingress:
enabled: true
className: nginx
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
hosts:
- host: myapp.example.com
paths:
- path: /
pathType: Prefix
tls:
- secretName: myapp-tls
hosts:
- myapp.example.com
resources:
limits:
cpu: 500m
memory: 512Mi
requests:
cpu: 250m
memory: 256Mi
autoscaling:
enabled: true
minReplicas: 3
maxReplicas: 20
targetCPUUtilizationPercentage: 70
targetMemoryUtilizationPercentage: 80
nodeSelector: {}
tolerations: []
affinity: {}
postgresql:
enabled: true
auth:
username: myapp
database: myapp
```
### Helm Commands
```bash
# Install chart
helm install myapp ./myapp-chart -n production
# Install with custom values
helm install myapp ./myapp-chart -n production -f values-prod.yaml
# Upgrade release
helm upgrade myapp ./myapp-chart -n production
# Rollback
helm rollback myapp 1 -n production
# Uninstall
helm uninstall myapp -n production
# Template rendering (dry-run)
helm template myapp ./myapp-chart -f values-prod.yaml
# Lint chart
helm lint ./myapp-chart
```
## kubectl Command Reference
```bash
# Get resources
kubectl get pods -n production
kubectl get deployments -n production -o wide
kubectl get svc -n production
# Describe resources
kubectl describe pod myapp-123 -n production
kubectl describe deployment myapp -n production
# Logs
kubectl logs myapp-123 -n production
kubectl logs -f myapp-123 -n production # Follow
kubectl logs myapp-123 -n production --previous # Previous container
# Execute commands in pod
kubectl exec -it myapp-123 -n production -- /bin/sh
kubectl exec myapp-123 -n production -- env
# Port forwarding
kubectl port-forward svc/myapp 8080:80 -n production
# Copy files
kubectl cp myapp-123:/tmp/file.txt ./file.txt -n production
# Scale deployment
kubectl scale deployment myapp --replicas=5 -n production
# Rollout management
kubectl rollout status deployment/myapp -n production
kubectl rollout history deployment/myapp -n production
kubectl rollout undo deployment/myapp -n production
# Apply/Delete manifests
kubectl apply -f deployment.yaml
kubectl delete -f deployment.yaml
# Resource usage
kubectl top nodes
kubectl top pods -n production
# Debug
kubectl run debug --image=busybox:1.36 -it --rm --restart=Never -- sh
```
---
## Best Practices Summary
1. **Always set resource requests and limits**
2. **Implement proper health checks** (liveness, readiness, startup)
3. **Use non-root containers** with security contexts
4. **Enable RBAC** and use service accounts
5. **Implement network policies** for zero-trust networking
6. **Use namespaces** for isolation
7. **Tag everything** with consistent labels
8. **Use ConfigMaps and Secrets** (never hardcode)
9. **Implement HPA** for auto-scaling
10. **Use readOnlyRootFilesystem** when possible
@@ -0,0 +1,559 @@
# Observability: Monitoring, Logging & Tracing
## The Three Pillars of Observability
### 1. Metrics (What is happening?)
- **Definition**: Numeric measurements over time
- **Examples**: CPU usage, request rate, error rate, latency
- **Tools**: Prometheus, Datadog, CloudWatch, New Relic
### 2. Logs (Why is it happening?)
- **Definition**: Timestamped event records
- **Examples**: Application logs, access logs, error logs
- **Tools**: ELK Stack, Splunk, CloudWatch Logs, Loki
### 3. Traces (Where is it happening?)
- **Definition**: Request journey through distributed system
- **Examples**: Service call chains, database queries, external API calls
- **Tools**: Jaeger, Zipkin, AWS X-Ray, Datadog APM
## SLI/SLO/SLA Framework
### Service Level Indicators (SLIs)
**Quantitative measurements of service quality**
```yaml
# Common SLIs
availability:
definition: "Percentage of successful requests"
measurement: "(successful_requests / total_requests) * 100"
latency:
definition: "Time to process request"
measurement: "p95 response time < 200ms"
error_rate:
definition: "Percentage of failed requests"
measurement: "(failed_requests / total_requests) * 100"
throughput:
definition: "Requests processed per second"
measurement: "requests_per_second"
```
### Service Level Objectives (SLOs)
**Target values for SLIs**
```yaml
# Example SLOs
availability_slo:
target: 99.9%
measurement_window: 30 days
error_budget: 0.1% (43 minutes per month)
latency_slo:
target: "95% of requests < 200ms"
measurement_window: 7 days
error_rate_slo:
target: "< 0.1%"
measurement_window: 24 hours
```
### Service Level Agreements (SLAs)
**Business contracts with consequences**
```yaml
# Example SLA
web_application_sla:
availability: 99.9%
latency_p95: 300ms
consequences:
- availability < 99.9%: 10% service credit
- availability < 99.0%: 25% service credit
- availability < 95.0%: 50% service credit
```
## Prometheus Setup
### Prometheus Configuration
```yaml
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
external_labels:
cluster: 'production'
environment: 'prod'
# Alert manager configuration
alerting:
alertmanagers:
- static_configs:
- targets:
- alertmanager:9093
# Load rules
rule_files:
- "/etc/prometheus/rules/*.yml"
# Scrape configurations
scrape_configs:
# Prometheus self-monitoring
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
# Kubernetes pods
- job_name: 'kubernetes-pods'
kubernetes_sd_configs:
- role: pod
relabel_configs:
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
action: keep
regex: true
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
action: replace
target_label: __metrics_path__
regex: (.+)
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
action: replace
regex: ([^:]+)(?::\d+)?;(\d+)
replacement: $1:$2
target_label: __address__
- action: labelmap
regex: __meta_kubernetes_pod_label_(.+)
- source_labels: [__meta_kubernetes_namespace]
action: replace
target_label: kubernetes_namespace
- source_labels: [__meta_kubernetes_pod_name]
action: replace
target_label: kubernetes_pod_name
# Node exporter
- job_name: 'node-exporter'
kubernetes_sd_configs:
- role: node
relabel_configs:
- action: labelmap
regex: __meta_kubernetes_node_label_(.+)
```
### Alert Rules
```yaml
# alert-rules.yml
groups:
- name: application_alerts
interval: 30s
rules:
# High error rate
- alert: HighErrorRate
expr: |
rate(http_requests_total{status=~"5.."}[5m]) > 0.05
for: 5m
labels:
severity: critical
team: backend
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value | humanizePercentage }} for {{ $labels.job }}"
runbook: "https://wiki.example.com/runbooks/high-error-rate"
# High latency
- alert: HighLatency
expr: |
histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m])) > 0.5
for: 10m
labels:
severity: warning
team: backend
annotations:
summary: "High latency detected"
description: "P95 latency is {{ $value | humanizeDuration }} for {{ $labels.job }}"
# Low availability
- alert: ServiceDown
expr: up == 0
for: 2m
labels:
severity: critical
team: sre
annotations:
summary: "Service is down"
description: "{{ $labels.job }} has been down for more than 2 minutes"
- name: kubernetes_alerts
interval: 30s
rules:
# Pod crash looping
- alert: PodCrashLooping
expr: |
rate(kube_pod_container_status_restarts_total[15m]) > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Pod crash looping"
description: "Pod {{ $labels.namespace }}/{{ $labels.pod }} is crash looping"
# High memory usage
- alert: HighMemoryUsage
expr: |
(container_memory_usage_bytes / container_spec_memory_limit_bytes) > 0.9
for: 10m
labels:
severity: warning
annotations:
summary: "High memory usage"
description: "Container {{ $labels.container }} in pod {{ $labels.pod }} is using {{ $value | humanizePercentage }} of memory"
# Node disk space
- alert: NodeDiskSpaceLow
expr: |
(node_filesystem_avail_bytes / node_filesystem_size_bytes) < 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "Node disk space low"
description: "Node {{ $labels.node }} has less than 10% disk space available"
```
## Structured Logging
### Best Practices
```json
{
"timestamp": "2025-10-17T10:30:45.123Z",
"level": "ERROR",
"service": "api-gateway",
"version": "v1.2.3",
"trace_id": "abc123def456",
"span_id": "789ghi012jkl",
"user_id": "user-12345",
"request_id": "req-67890",
"method": "POST",
"path": "/api/v1/orders",
"status_code": 500,
"duration_ms": 245,
"error": {
"type": "DatabaseConnectionError",
"message": "Failed to connect to database",
"stack_trace": "..."
},
"context": {
"order_id": "order-98765",
"customer_id": "cust-54321"
}
}
```
### Logging Configuration (Node.js Example)
```javascript
const winston = require('winston');
const logger = winston.createLogger({
level: process.env.LOG_LEVEL || 'info',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.json()
),
defaultMeta: {
service: process.env.SERVICE_NAME,
version: process.env.SERVICE_VERSION,
environment: process.env.ENVIRONMENT
},
transports: [
new winston.transports.Console(),
new winston.transports.File({
filename: 'error.log',
level: 'error'
}),
new winston.transports.File({
filename: 'combined.log'
})
]
});
// Usage with correlation ID
app.use((req, res, next) => {
req.id = req.headers['x-request-id'] || uuidv4();
req.logger = logger.child({
request_id: req.id,
trace_id: req.headers['x-trace-id']
});
next();
});
app.post('/api/orders', async (req, res) => {
req.logger.info('Creating order', {
customer_id: req.body.customer_id
});
try {
const order = await createOrder(req.body);
req.logger.info('Order created successfully', {
order_id: order.id
});
res.json(order);
} catch (error) {
req.logger.error('Failed to create order', {
error: error.message,
stack: error.stack
});
res.status(500).json({ error: 'Internal server error' });
}
});
```
## Distributed Tracing
### OpenTelemetry Configuration
```yaml
# otel-collector-config.yaml
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
timeout: 10s
send_batch_size: 1024
memory_limiter:
check_interval: 1s
limit_mib: 512
resource:
attributes:
- key: environment
value: production
action: insert
exporters:
jaeger:
endpoint: jaeger:14250
tls:
insecure: true
prometheus:
endpoint: 0.0.0.0:8889
logging:
loglevel: info
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, batch, resource]
exporters: [jaeger, logging]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch, resource]
exporters: [prometheus, logging]
```
### Application Instrumentation (Python Example)
```python
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.instrumentation.flask import FlaskInstrumentor
from opentelemetry.instrumentation.requests import RequestsInstrumentor
# Set up tracing
trace.set_tracer_provider(TracerProvider())
tracer = trace.get_tracer(__name__)
# Configure OTLP exporter
otlp_exporter = OTLPSpanExporter(
endpoint="otel-collector:4317",
insecure=True
)
# Add span processor
span_processor = BatchSpanProcessor(otlp_exporter)
trace.get_tracer_provider().add_span_processor(span_processor)
# Instrument Flask and requests library
app = Flask(__name__)
FlaskInstrumentor().instrument_app(app)
RequestsInstrumentor().instrument()
# Manual span creation
@app.route('/api/order/<order_id>')
def get_order(order_id):
with tracer.start_as_current_span("get_order") as span:
span.set_attribute("order.id", order_id)
span.set_attribute("user.id", request.headers.get('X-User-ID'))
# Add events
span.add_event("Fetching order from database")
order = fetch_order_from_db(order_id)
if not order:
span.set_status(Status(StatusCode.ERROR, "Order not found"))
return {"error": "Order not found"}, 404
span.add_event("Order retrieved successfully")
return order
```
## Dashboards & Visualization
### Grafana Dashboard JSON (Example)
```json
{
"dashboard": {
"title": "Application Performance",
"panels": [
{
"title": "Request Rate",
"targets": [
{
"expr": "rate(http_requests_total[5m])",
"legendFormat": "{{method}} {{status}}"
}
],
"type": "graph"
},
{
"title": "Error Rate",
"targets": [
{
"expr": "rate(http_requests_total{status=~\"5..\"}[5m]) / rate(http_requests_total[5m])",
"legendFormat": "Error Rate"
}
],
"type": "graph"
},
{
"title": "P95 Latency",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(http_request_duration_seconds_bucket[5m]))",
"legendFormat": "P95 Latency"
}
],
"type": "graph"
},
{
"title": "Active Connections",
"targets": [
{
"expr": "sum(up{job=\"myapp\"})",
"legendFormat": "Active Instances"
}
],
"type": "stat"
}
]
}
}
```
## On-Call & Incident Response
### Runbook Template
```markdown
# Runbook: High Error Rate Alert
## Alert Details
- **Alert Name**: HighErrorRate
- **Severity**: Critical
- **Team**: Backend Engineering
- **On-Call**: See PagerDuty schedule
## Symptoms
- Error rate exceeds 5% for 5 minutes
- Users experiencing 5xx errors
- Elevated p95 latency
## Investigation Steps
1. **Check service health**
```bash
kubectl get pods -n production -l app=myapp
kubectl logs -n production -l app=myapp --tail=100
```
2. **Review error logs**
- Check Grafana dashboard
- Review application logs in Kibana
- Check CloudWatch metrics
3. **Identify error patterns**
- What endpoints are failing?
- Are errors consistent across all pods?
- Is there a pattern in timing?
4. **Check dependencies**
- Database connectivity
- External API availability
- Redis/cache status
## Common Causes & Solutions
### Database Connection Issues
- **Symptoms**: Connection timeout errors
- **Solution**:
```bash
# Check database connectivity
kubectl exec -it <pod-name> -- nc -zv database-host 5432
# Check connection pool
kubectl logs <pod-name> | grep "connection pool"
```
### Memory Leaks
- **Symptoms**: Increasing memory usage, OOM kills
- **Solution**: Restart affected pods, investigate memory usage
### Deployment Issues
- **Symptoms**: Errors started after deployment
- **Solution**: Rollback deployment
```bash
kubectl rollout undo deployment/myapp -n production
```
## Escalation
- If unresolved after 15 minutes, escalate to Senior Engineer
- If service degradation > 30 minutes, notify VP Engineering
## Post-Incident
- Create incident report
- Schedule post-mortem
- Update runbook with findings
```
## Observability Best Practices
1. **Use consistent naming**: Follow naming conventions for metrics, logs, traces
2. **Add context**: Include correlation IDs in logs and traces
3. **Set meaningful alerts**: Avoid alert fatigue with actionable alerts
4. **Define SLOs**: Measure what matters to users
5. **Practice incident response**: Regular game days and fire drills
6. **Automate runbooks**: Convert manual steps to automated remediation
7. **Monitor the monitors**: Ensure observability stack is reliable
8. **Continuous improvement**: Review and refine based on incidents
---
## Tools Comparison
| Feature | Prometheus | Datadog | New Relic | CloudWatch |
|---------|-----------|---------|-----------|------------|
| Metrics | ✓✓✓ | ✓✓✓ | ✓✓✓ | ✓✓ |
| Logs | via Loki | ✓✓✓ | ✓✓✓ | ✓✓✓ |
| Traces | via Tempo | ✓✓✓ | ✓✓✓ | ✓✓ |
| Cost | Free (self-hosted) | $$$ | $$$ | $$ |
| Learning Curve | Medium | Low | Low | Low |
| Kubernetes Native | ✓✓✓ | ✓✓ | ✓✓ | ✓ |
@@ -0,0 +1,661 @@
# DevSecOps & Security Best Practices
## Security Principles
### Defense in Depth
- **Multiple layers of security controls**
- Network security, application security, data security
- No single point of failure
### Least Privilege
- **Minimum permissions necessary**
- Regular access reviews
- Time-bound elevated access
### Zero Trust
- **Never trust, always verify**
- Verify every request regardless of origin
- Micro-segmentation and strict access controls
## Secrets Management
### ❌ Never Do This
```yaml
# NEVER hardcode secrets!
apiVersion: v1
kind: Pod
metadata:
name: bad-example
spec:
containers:
- name: app
env:
- name: DATABASE_PASSWORD
value: "SuperSecret123!" # 🚨 NEVER DO THIS
- name: API_KEY
value: "sk-abc123def456" # 🚨 NEVER DO THIS
```
### ✅ Use External Secrets Operator
```yaml
# External Secrets with AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: app-secrets
namespace: production
spec:
refreshInterval: 1h
secretStoreRef:
name: aws-secrets-manager
kind: SecretStore
target:
name: app-secrets
creationPolicy: Owner
data:
- secretKey: database-password
remoteRef:
key: prod/myapp/database-password
- secretKey: api-key
remoteRef:
key: prod/myapp/api-key
```
### ✅ Use Sealed Secrets
```bash
# Install kubeseal
brew install kubeseal
# Create sealed secret
kubectl create secret generic myapp-secrets \
--from-literal=db-password='secret123' \
--dry-run=client -o yaml | \
kubeseal -o yaml > sealed-secret.yaml
# Apply sealed secret (safe to commit to Git)
kubectl apply -f sealed-secret.yaml
```
### AWS Secrets Manager with IRSA
```yaml
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp
namespace: production
annotations:
eks.amazonaws.com/role-arn: arn:aws:iam::123456789012:role/myapp-secrets-role
---
# Application retrieves secrets using AWS SDK
# No credentials in code or config!
```
## Container Security
### Secure Dockerfile
```dockerfile
# Use specific version tags, not 'latest'
FROM node:20.10.0-alpine3.19 AS builder
# Run as non-root user
RUN addgroup -g 1000 nodejs && \
adduser -u 1000 -G nodejs -s /bin/sh -D nodejs
WORKDIR /app
# Copy and install dependencies
COPY --chown=nodejs:nodejs package*.json ./
RUN npm ci --only=production && \
npm cache clean --force
# Copy application code
COPY --chown=nodejs:nodejs . .
# Build application
RUN npm run build
# Production stage
FROM node:20.10.0-alpine3.19
# Install dumb-init for signal handling
RUN apk add --no-cache dumb-init
# Create non-root user
RUN addgroup -g 1000 nodejs && \
adduser -u 1000 -G nodejs -s /bin/sh -D nodejs
WORKDIR /app
# Copy from builder
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/package*.json ./
# Switch to non-root user
USER nodejs
# Use dumb-init
ENTRYPOINT ["dumb-init", "--"]
# Run application
CMD ["node", "dist/index.js"]
# Expose port
EXPOSE 8080
# Add labels
LABEL org.opencontainers.image.source="https://github.com/myorg/myapp" \
org.opencontainers.image.version="1.0.0" \
org.opencontainers.image.vendor="MyOrg"
```
### Security Scanning
#### Trivy (Container Vulnerability Scanner)
```bash
# Scan Docker image
trivy image myapp:latest
# Scan with severity filter
trivy image --severity HIGH,CRITICAL myapp:latest
# Scan filesystem
trivy fs --security-checks vuln,config /path/to/project
# Output to JSON
trivy image -f json -o results.json myapp:latest
```
#### Grype (Vulnerability Scanner)
```bash
# Scan image
grype myapp:latest
# Scan with specific severity
grype myapp:latest --fail-on high
# Output SARIF for GitHub
grype myapp:latest -o sarif > results.sarif
```
### Pod Security Standards
```yaml
# Pod Security Admission
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
---
# Secure Pod Configuration
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
namespace: production
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:1.0.0
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE # Only if needed
resources:
limits:
memory: "512Mi"
cpu: "500m"
requests:
memory: "256Mi"
cpu: "250m"
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
```
## Network Security
### Network Policies
```yaml
# Default deny all ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
---
# Allow specific ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: app-allow-ingress
namespace: production
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Ingress
- Egress
ingress:
# Allow from ingress controller
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
ports:
- protocol: TCP
port: 8080
egress:
# Allow DNS
- to:
- namespaceSelector:
matchLabels:
name: kube-system
- podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
# Allow database
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
# Allow HTTPS egress
- to:
- namespaceSelector: {}
ports:
- protocol: TCP
port: 443
```
### AWS Security Groups (Terraform)
```hcl
# Application Security Group
resource "aws_security_group" "app" {
name = "${var.name_prefix}-app-sg"
description = "Security group for application"
vpc_id = var.vpc_id
# Allow inbound from ALB only
ingress {
from_port = 8080
to_port = 8080
protocol = "tcp"
security_groups = [aws_security_group.alb.id]
description = "HTTP from ALB"
}
# Allow outbound to database
egress {
from_port = 5432
to_port = 5432
protocol = "tcp"
security_groups = [aws_security_group.database.id]
description = "PostgreSQL to database"
}
# Allow HTTPS egress
egress {
from_port = 443
to_port = 443
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
description = "HTTPS to internet"
}
tags = var.tags
}
```
## IAM & RBAC
### Kubernetes RBAC
```yaml
# Least privilege service account
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp
namespace: production
---
# Role with minimal permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: myapp-role
namespace: production
rules:
# Only read ConfigMaps and Secrets
- apiGroups: [""]
resources: ["configmaps", "secrets"]
verbs: ["get", "list"]
resourceNames: ["myapp-config", "myapp-secrets"]
# Read own pod information
- apiGroups: [""]
resources: ["pods"]
verbs: ["get"]
---
# Bind role to service account
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: myapp-rolebinding
namespace: production
subjects:
- kind: ServiceAccount
name: myapp
namespace: production
roleRef:
kind: Role
name: myapp-role
apiGroup: rbac.authorization.k8s.io
```
### AWS IAM Policy (Terraform)
```hcl
# Least privilege IAM policy
data "aws_iam_policy_document" "app" {
# Allow reading specific secrets
statement {
actions = [
"secretsmanager:GetSecretValue",
"secretsmanager:DescribeSecret",
]
resources = [
"arn:aws:secretsmanager:${var.region}:${data.aws_caller_identity.current.account_id}:secret:prod/myapp/*"
]
}
# Allow writing to specific S3 bucket
statement {
actions = [
"s3:PutObject",
"s3:GetObject",
]
resources = [
"${aws_s3_bucket.app.arn}/*"
]
}
# Allow publishing to specific SNS topic
statement {
actions = [
"sns:Publish",
]
resources = [
aws_sns_topic.app_notifications.arn
]
}
}
resource "aws_iam_policy" "app" {
name = "${var.name_prefix}-app-policy"
policy = data.aws_iam_policy_document.app.json
}
```
## Compliance & Policy as Code
### OPA (Open Policy Agent)
```rego
# Deny pods running as root
package kubernetes.admission
deny[msg] {
input.request.kind.kind == "Pod"
input.request.object.spec.securityContext.runAsNonRoot != true
msg := "Pods must not run as root"
}
# Require resource limits
deny[msg] {
input.request.kind.kind == "Pod"
container := input.request.object.spec.containers[_]
not container.resources.limits
msg := sprintf("Container %v must specify resource limits", [container.name])
}
# Deny latest tag
deny[msg] {
input.request.kind.kind == "Pod"
container := input.request.object.spec.containers[_]
endswith(container.image, ":latest")
msg := sprintf("Container %v uses 'latest' tag", [container.name])
}
# Require specific labels
deny[msg] {
input.request.kind.kind == "Pod"
required_labels := ["app", "version", "environment"]
label := required_labels[_]
not input.request.object.metadata.labels[label]
msg := sprintf("Missing required label: %v", [label])
}
```
### Terraform Sentinel Policy
```hcl
# sentinel.hcl
policy "require-tags" {
enforcement_level = "hard-mandatory"
}
policy "restrict-instance-type" {
enforcement_level = "soft-mandatory"
}
# require-tags.sentinel
import "tfplan/v2" as tfplan
required_tags = ["Environment", "Owner", "CostCenter"]
main = rule {
all tfplan.resource_changes as _, rc {
rc.mode is "managed" and
rc.type in ["aws_instance", "aws_db_instance", "aws_s3_bucket"] and
rc.change.actions contains "create"
implies
all required_tags as tag {
rc.change.after.tags contains tag
}
}
}
```
## Security Scanning in CI/CD
### GitHub Actions Security Workflow
```yaml
name: Security Scan
on: [push, pull_request]
jobs:
secret-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: TruffleHog Secret Scan
uses: trufflesecurity/trufflehog@main
with:
path: ./
base: ${{ github.event.repository.default_branch }}
head: HEAD
container-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build image
run: docker build -t myapp:${{ github.sha }} .
- name: Run Trivy scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
- name: Upload to GitHub Security
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
terraform-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
framework: terraform
soft_fail: false
sast-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: auto
```
## Encryption
### At Rest
```hcl
# S3 bucket encryption
resource "aws_s3_bucket_server_side_encryption_configuration" "app" {
bucket = aws_s3_bucket.app.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.s3.arn
}
bucket_key_enabled = true
}
}
# RDS encryption
resource "aws_db_instance" "app" {
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
# ... other configuration
}
# EBS encryption
resource "aws_ebs_volume" "app" {
encrypted = true
kms_key_id = aws_kms_key.ebs.arn
# ... other configuration
}
```
### In Transit
```yaml
# Enforce TLS in Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
tls:
- hosts:
- app.example.com
secretName: app-tls
```
## Security Best Practices Checklist
- [ ] **Never hardcode secrets** - Use secret managers
- [ ] **Run containers as non-root** - Set securityContext
- [ ] **Use specific image tags** - Never use 'latest'
- [ ] **Scan images for vulnerabilities** - Use Trivy/Grype
- [ ] **Enable Pod Security Standards** - Use restricted profile
- [ ] **Implement network policies** - Default deny, allow specific
- [ ] **Use RBAC** - Least privilege access
- [ ] **Encrypt data at rest** - KMS for all data stores
- [ ] **Enforce TLS** - All traffic encrypted in transit
- [ ] **Scan IaC for issues** - Checkov, tfsec in CI/CD
- [ ] **Rotate credentials regularly** - Automate rotation
- [ ] **Audit and log everything** - CloudTrail, audit logs
- [ ] **Implement policy as code** - OPA, Sentinel
- [ ] **Regular security reviews** - Penetration testing, audits
- [ ] **Keep dependencies updated** - Renovate, Dependabot
---
## Security Incident Response
1. **Detect**: Automated alerting on security events
2. **Contain**: Isolate affected systems
3. **Eradicate**: Remove threat and vulnerabilities
4. **Recover**: Restore services securely
5. **Learn**: Post-incident review and improvements
@@ -0,0 +1,919 @@
# Ready-to-Use DevOps Templates
## Terraform Templates
### AWS VPC with Multi-AZ
See [terraform.md](terraform.md) for the complete VPC module implementation.
### AWS EKS Cluster
```hcl
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 19.0"
cluster_name = "${var.name_prefix}-cluster"
cluster_version = "1.28"
vpc_id = module.vpc.vpc_id
subnet_ids = module.vpc.private_subnet_ids
cluster_endpoint_public_access = false
cluster_endpoint_private_access = true
cluster_addons = {
coredns = {
most_recent = true
}
kube-proxy = {
most_recent = true
}
vpc-cni = {
most_recent = true
}
aws-ebs-csi-driver = {
most_recent = true
}
}
eks_managed_node_groups = {
general = {
min_size = 2
max_size = 10
desired_size = 3
instance_types = ["t3.large"]
capacity_type = "ON_DEMAND"
labels = {
role = "general"
}
tags = {
NodeGroup = "general"
}
}
spot = {
min_size = 0
max_size = 10
desired_size = 2
instance_types = ["t3.large", "t3a.large"]
capacity_type = "SPOT"
labels = {
role = "spot"
}
taints = [{
key = "spot"
value = "true"
effect = "NoSchedule"
}]
tags = {
NodeGroup = "spot"
}
}
}
tags = var.tags
}
```
### AWS RDS PostgreSQL
```hcl
resource "aws_db_subnet_group" "main" {
name = "${var.name_prefix}-db-subnet-group"
subnet_ids = var.private_subnet_ids
tags = merge(
var.tags,
{
Name = "${var.name_prefix}-db-subnet-group"
}
)
}
resource "aws_db_parameter_group" "postgres" {
name = "${var.name_prefix}-postgres-params"
family = "postgres15"
parameter {
name = "log_connections"
value = "1"
}
parameter {
name = "log_disconnections"
value = "1"
}
parameter {
name = "log_duration"
value = "1"
}
parameter {
name = "log_statement"
value = "all"
}
tags = var.tags
}
resource "aws_db_instance" "main" {
identifier = "${var.name_prefix}-db"
engine = "postgres"
engine_version = "15.4"
instance_class = var.instance_class
allocated_storage = var.allocated_storage
max_allocated_storage = var.max_allocated_storage
storage_type = "gp3"
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
db_name = var.database_name
username = var.master_username
password = random_password.db_password.result
db_subnet_group_name = aws_db_subnet_group.main.name
vpc_security_group_ids = [aws_security_group.db.id]
parameter_group_name = aws_db_parameter_group.postgres.name
multi_az = var.multi_az
backup_retention_period = var.backup_retention_period
backup_window = "03:00-04:00"
maintenance_window = "sun:04:00-sun:05:00"
enabled_cloudwatch_logs_exports = ["postgresql", "upgrade"]
monitoring_interval = 60
monitoring_role_arn = aws_iam_role.rds_monitoring.arn
deletion_protection = var.environment == "prod" ? true : false
skip_final_snapshot = var.environment != "prod" ? true : false
final_snapshot_identifier = var.environment == "prod" ? "${var.name_prefix}-final-snapshot-${formatdate("YYYY-MM-DD-hhmm", timestamp())}" : null
tags = merge(
var.tags,
{
Name = "${var.name_prefix}-db"
}
)
}
```
## Kubernetes Templates
### Complete Application Stack
```yaml
---
# Namespace
apiVersion: v1
kind: Namespace
metadata:
name: myapp
labels:
name: myapp
environment: production
---
# ConfigMap
apiVersion: v1
kind: ConfigMap
metadata:
name: myapp-config
namespace: myapp
data:
APP_ENV: "production"
LOG_LEVEL: "info"
PORT: "8080"
---
# Secret (use Sealed Secrets or External Secrets in production)
apiVersion: v1
kind: Secret
metadata:
name: myapp-secrets
namespace: myapp
type: Opaque
stringData:
DATABASE_URL: "postgresql://user:pass@postgres:5432/myapp"
API_KEY: "your-api-key-here"
---
# ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
name: myapp
namespace: myapp
automountServiceAccountToken: true
---
# Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
namespace: myapp
labels:
app: myapp
spec:
replicas: 3
revisionHistoryLimit: 10
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
version: v1.0.0
annotations:
prometheus.io/scrape: "true"
prometheus.io/port: "8080"
prometheus.io/path: "/metrics"
spec:
serviceAccountName: myapp
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 2000
containers:
- name: myapp
image: myapp:1.0.0
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 8080
envFrom:
- configMapRef:
name: myapp-config
- secretRef:
name: myapp-secrets
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
capabilities:
drop:
- ALL
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
---
# Service
apiVersion: v1
kind: Service
metadata:
name: myapp
namespace: myapp
labels:
app: myapp
spec:
type: ClusterIP
selector:
app: myapp
ports:
- name: http
port: 80
targetPort: 8080
protocol: TCP
---
# Ingress
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
namespace: myapp
annotations:
kubernetes.io/ingress.class: nginx
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- myapp.example.com
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 80
---
# HorizontalPodAutoscaler
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: myapp
namespace: myapp
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: myapp
minReplicas: 3
maxReplicas: 20
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
---
# PodDisruptionBudget
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: myapp
namespace: myapp
spec:
minAvailable: 2
selector:
matchLabels:
app: myapp
```
## CI/CD Pipeline Templates
### GitHub Actions - Docker Build and Push
```yaml
name: Build and Push Docker Image
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
build-and-push:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
tags: |
type=ref,event=branch
type=ref,event=pr
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=sha,prefix={{branch}}-
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max
```
### GitHub Actions - Terraform Workflow
```yaml
name: Terraform
on:
push:
branches: [main]
paths:
- 'terraform/**'
pull_request:
branches: [main]
paths:
- 'terraform/**'
env:
TF_VERSION: 1.6.0
AWS_REGION: us-east-1
jobs:
terraform:
name: Terraform Plan & Apply
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
pull-requests: write
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}
aws-region: ${{ env.AWS_REGION }}
- name: Setup Terraform
uses: hashicorp/setup-terraform@v3
with:
terraform_version: ${{ env.TF_VERSION }}
- name: Terraform Format Check
run: terraform fmt -check -recursive
working-directory: ./terraform
- name: Terraform Init
run: terraform init
working-directory: ./terraform/environments/prod
- name: Terraform Validate
run: terraform validate
working-directory: ./terraform/environments/prod
- name: Terraform Plan
id: plan
run: |
terraform plan -no-color -out=tfplan
working-directory: ./terraform/environments/prod
continue-on-error: true
- name: Post Plan to PR
if: github.event_name == 'pull_request'
uses: actions/github-script@v7
with:
script: |
const output = `#### Terraform Plan 📖
<details><summary>Show Plan</summary>
\`\`\`terraform
${{ steps.plan.outputs.stdout }}
\`\`\`
</details>`;
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});
- name: Terraform Apply
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
run: terraform apply -auto-approve tfplan
working-directory: ./terraform/environments/prod
```
### GitLab CI - Complete Pipeline
```yaml
stages:
- lint
- build
- test
- deploy
variables:
DOCKER_IMAGE: $CI_REGISTRY_IMAGE:$CI_COMMIT_SHORT_SHA
KUBE_NAMESPACE: production
# Lint stage
lint:terraform:
stage: lint
image: hashicorp/terraform:1.6
script:
- cd terraform
- terraform fmt -check -recursive
- terraform init -backend=false
- terraform validate
only:
changes:
- terraform/**/*
lint:kubernetes:
stage: lint
image: cytopia/kubeval:latest
script:
- kubeval --strict kubernetes/*.yaml
only:
changes:
- kubernetes/**/*
# Build stage
build:
stage: build
image: docker:24
services:
- docker:24-dind
before_script:
- docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
script:
- docker build -t $DOCKER_IMAGE .
- docker push $DOCKER_IMAGE
only:
- main
- develop
# Test stage
test:unit:
stage: test
image: $DOCKER_IMAGE
script:
- npm test
coverage: '/Statements\s*:\s*(\d+\.\d+)%/'
only:
- main
- develop
test:security:
stage: test
image: aquasec/trivy:latest
script:
- trivy image --severity HIGH,CRITICAL $DOCKER_IMAGE
only:
- main
- develop
# Deploy stage
deploy:production:
stage: deploy
image: bitnami/kubectl:latest
before_script:
- kubectl config use-context production
script:
- kubectl set image deployment/myapp myapp=$DOCKER_IMAGE -n $KUBE_NAMESPACE
- kubectl rollout status deployment/myapp -n $KUBE_NAMESPACE
environment:
name: production
url: https://myapp.example.com
only:
- main
when: manual
```
## ArgoCD Application Template
```yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
finalizers:
- resources-finalizer.argocd.argoproj.io
spec:
project: default
source:
repoURL: https://github.com/myorg/myapp.git
targetRevision: HEAD
path: kubernetes/overlays/production
# For Helm charts
# helm:
# valueFiles:
# - values-production.yaml
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
allowEmpty: false
syncOptions:
- CreateNamespace=true
- PrunePropagationPolicy=foreground
- PruneLast=true
retry:
limit: 5
backoff:
duration: 5s
factor: 2
maxDuration: 3m
ignoreDifferences:
- group: apps
kind: Deployment
jsonPointers:
- /spec/replicas
```
## Monitoring & Alerting Templates
### Prometheus ServiceMonitor
```yaml
apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: myapp
namespace: production
labels:
app: myapp
spec:
selector:
matchLabels:
app: myapp
endpoints:
- port: http
path: /metrics
interval: 30s
scrapeTimeout: 10s
```
### Prometheus Alert Rules
```yaml
apiVersion: monitoring.coreos.com/v1
kind: PrometheusRule
metadata:
name: myapp-alerts
namespace: production
spec:
groups:
- name: myapp
interval: 30s
rules:
- alert: HighErrorRate
expr: |
rate(http_requests_total{status=~"5.."}[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "High error rate detected"
description: "Error rate is {{ $value | humanizePercentage }}"
- alert: PodCrashLooping
expr: |
rate(kube_pod_container_status_restarts_total[15m]) > 0
for: 5m
labels:
severity: warning
annotations:
summary: "Pod {{ $labels.pod }} is crash looping"
description: "Pod has restarted {{ $value }} times in 15 minutes"
- alert: HighMemoryUsage
expr: |
container_memory_usage_bytes{pod=~"myapp-.*"} /
container_spec_memory_limit_bytes{pod=~"myapp-.*"} > 0.9
for: 10m
labels:
severity: warning
annotations:
summary: "High memory usage"
description: "Memory usage is {{ $value | humanizePercentage }}"
```
## Docker Templates
### Multi-stage Dockerfile (Node.js)
```dockerfile
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
# Copy package files
COPY package*.json ./
# Install dependencies
RUN npm ci --only=production && \
npm cache clean --force
# Copy source code
COPY . .
# Build application
RUN npm run build
# Production stage
FROM node:20-alpine
# Install dumb-init for proper signal handling
RUN apk add --no-cache dumb-init
# Create non-root user
RUN addgroup -g 1000 nodejs && \
adduser -u 1000 -G nodejs -s /bin/sh -D nodejs
WORKDIR /app
# Copy dependencies and built app from builder
COPY --from=builder --chown=nodejs:nodejs /app/node_modules ./node_modules
COPY --from=builder --chown=nodejs:nodejs /app/dist ./dist
COPY --from=builder --chown=nodejs:nodejs /app/package*.json ./
# Switch to non-root user
USER nodejs
# Expose port
EXPOSE 8080
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
CMD node healthcheck.js
# Use dumb-init to handle signals properly
ENTRYPOINT ["dumb-init", "--"]
# Start application
CMD ["node", "dist/index.js"]
```
### .dockerignore
```
# Git
.git
.gitignore
.gitattributes
# CI/CD
.github
.gitlab-ci.yml
Jenkinsfile
# Documentation
README.md
docs/
*.md
# Dependencies
node_modules
npm-debug.log
# Testing
coverage/
.nyc_output
test/
*.test.js
# IDE
.vscode
.idea
*.swp
*.swo
# OS
.DS_Store
Thumbs.db
# Environment
.env
.env.*
!.env.example
# Build artifacts
dist/
build/
*.log
```
## Makefile Template
```makefile
.PHONY: help
help: ## Show this help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-20s\033[0m %s\n", $$1, $$2}'
.PHONY: tf-init
tf-init: ## Initialize Terraform
cd terraform/environments/$(ENV) && terraform init
.PHONY: tf-plan
tf-plan: ## Plan Terraform changes
cd terraform/environments/$(ENV) && terraform plan -out=tfplan
.PHONY: tf-apply
tf-apply: ## Apply Terraform changes
cd terraform/environments/$(ENV) && terraform apply tfplan
.PHONY: k8s-apply
k8s-apply: ## Apply Kubernetes manifests
kubectl apply -f kubernetes/ -n $(NAMESPACE)
.PHONY: k8s-delete
k8s-delete: ## Delete Kubernetes resources
kubectl delete -f kubernetes/ -n $(NAMESPACE)
.PHONY: docker-build
docker-build: ## Build Docker image
docker build -t $(IMAGE_NAME):$(TAG) .
.PHONY: docker-push
docker-push: ## Push Docker image
docker push $(IMAGE_NAME):$(TAG)
.PHONY: lint
lint: ## Run linters
terraform fmt -check -recursive
kubeval --strict kubernetes/*.yaml
.PHONY: test
test: ## Run tests
go test ./... -v -cover
.PHONY: clean
clean: ## Clean build artifacts
rm -rf dist/ build/ *.log
```
---
## Template Usage Tips
1. **Always customize** - Don't use templates as-is, adapt to your needs
2. **Security first** - Never commit secrets, use proper secret management
3. **Test in dev** - Validate templates in development before production
4. **Version control** - Track all infrastructure code in Git
5. **Documentation** - Add README files explaining template usage
6. **Automation** - Use CI/CD to validate and deploy templates
@@ -0,0 +1,725 @@
# Terraform Best Practices & Patterns
## Terraform Project Structure
### Standard Module Structure
```
terraform/
├── environments/
│ ├── dev/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ ├── terraform.tfvars
│ │ └── backend.tf
│ ├── staging/
│ └── prod/
├── modules/
│ ├── vpc/
│ │ ├── main.tf
│ │ ├── variables.tf
│ │ ├── outputs.tf
│ │ └── README.md
│ ├── eks/
│ ├── rds/
│ └── s3-bucket/
└── README.md
```
**Why this structure?**
- Separates environment configurations for isolation
- Promotes module reusability across environments
- Makes state management cleaner (one state per environment)
- Enables environment-specific variable overrides
### Module Development Template
**modules/vpc/main.tf:**
```hcl
# VPC
resource "aws_vpc" "main" {
cidr_block = var.vpc_cidr
enable_dns_hostnames = var.enable_dns_hostnames
enable_dns_support = var.enable_dns_support
tags = merge(
var.tags,
{
Name = "${var.name_prefix}-vpc"
}
)
}
# Public Subnets
resource "aws_subnet" "public" {
count = length(var.public_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.public_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
map_public_ip_on_launch = true
tags = merge(
var.tags,
{
Name = "${var.name_prefix}-public-${var.availability_zones[count.index]}"
Type = "public"
}
)
}
# Private Subnets
resource "aws_subnet" "private" {
count = length(var.private_subnet_cidrs)
vpc_id = aws_vpc.main.id
cidr_block = var.private_subnet_cidrs[count.index]
availability_zone = var.availability_zones[count.index]
tags = merge(
var.tags,
{
Name = "${var.name_prefix}-private-${var.availability_zones[count.index]}"
Type = "private"
}
)
}
# Internet Gateway
resource "aws_internet_gateway" "main" {
count = length(var.public_subnet_cidrs) > 0 ? 1 : 0
vpc_id = aws_vpc.main.id
tags = merge(
var.tags,
{
Name = "${var.name_prefix}-igw"
}
)
}
# NAT Gateways
resource "aws_eip" "nat" {
count = var.enable_nat_gateway ? var.single_nat_gateway ? 1 : length(var.availability_zones) : 0
domain = "vpc"
tags = merge(
var.tags,
{
Name = "${var.name_prefix}-nat-eip-${count.index + 1}"
}
)
}
resource "aws_nat_gateway" "main" {
count = var.enable_nat_gateway ? var.single_nat_gateway ? 1 : length(var.availability_zones) : 0
allocation_id = aws_eip.nat[count.index].id
subnet_id = aws_subnet.public[count.index].id
tags = merge(
var.tags,
{
Name = "${var.name_prefix}-nat-${count.index + 1}"
}
)
depends_on = [aws_internet_gateway.main]
}
```
**modules/vpc/variables.tf:**
```hcl
variable "name_prefix" {
description = "Prefix for resource names"
type = string
}
variable "vpc_cidr" {
description = "CIDR block for VPC"
type = string
validation {
condition = can(cidrhost(var.vpc_cidr, 0))
error_message = "Must be a valid IPv4 CIDR block."
}
}
variable "availability_zones" {
description = "List of availability zones"
type = list(string)
}
variable "public_subnet_cidrs" {
description = "CIDR blocks for public subnets"
type = list(string)
default = []
}
variable "private_subnet_cidrs" {
description = "CIDR blocks for private subnets"
type = list(string)
default = []
}
variable "enable_nat_gateway" {
description = "Enable NAT Gateway for private subnets"
type = bool
default = true
}
variable "single_nat_gateway" {
description = "Use single NAT gateway for all AZs (cost optimization)"
type = bool
default = false
}
variable "enable_dns_hostnames" {
description = "Enable DNS hostnames in VPC"
type = bool
default = true
}
variable "enable_dns_support" {
description = "Enable DNS support in VPC"
type = bool
default = true
}
variable "tags" {
description = "Tags to apply to all resources"
type = map(string)
default = {}
}
```
**modules/vpc/outputs.tf:**
```hcl
output "vpc_id" {
description = "ID of the VPC"
value = aws_vpc.main.id
}
output "vpc_cidr" {
description = "CIDR block of the VPC"
value = aws_vpc.main.cidr_block
}
output "public_subnet_ids" {
description = "IDs of public subnets"
value = aws_subnet.public[*].id
}
output "private_subnet_ids" {
description = "IDs of private subnets"
value = aws_subnet.private[*].id
}
output "nat_gateway_ids" {
description = "IDs of NAT Gateways"
value = aws_nat_gateway.main[*].id
}
```
## State Management Best Practices
### Remote State Configuration (S3 + DynamoDB)
**environments/prod/backend.tf:**
```hcl
terraform {
backend "s3" {
bucket = "company-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
encrypt = true
dynamodb_table = "terraform-state-lock"
# Enable versioning on the S3 bucket for state history
# Enable server-side encryption
# Use DynamoDB for state locking
}
}
```
### State Locking Setup
```bash
# Create S3 bucket for state
aws s3api create-bucket \
--bucket company-terraform-state \
--region us-east-1
# Enable versioning
aws s3api put-bucket-versioning \
--bucket company-terraform-state \
--versioning-configuration Status=Enabled
# Enable encryption
aws s3api put-bucket-encryption \
--bucket company-terraform-state \
--server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "AES256"
}
}]
}'
# Create DynamoDB table for locking
aws dynamodb create-table \
--table-name terraform-state-lock \
--attribute-definitions AttributeName=LockID,AttributeType=S \
--key-schema AttributeName=LockID,KeyType=HASH \
--billing-mode PAY_PER_REQUEST \
--region us-east-1
```
## Advanced Terraform Patterns
### Data Sources for Dynamic Configuration
```hcl
# Get latest Amazon Linux 2 AMI
data "aws_ami" "amazon_linux_2" {
most_recent = true
owners = ["amazon"]
filter {
name = "name"
values = ["amzn2-ami-hvm-*-x86_64-gp2"]
}
filter {
name = "virtualization-type"
values = ["hvm"]
}
}
# Get current AWS account ID
data "aws_caller_identity" "current" {}
# Get current AWS region
data "aws_region" "current" {}
# Get available availability zones
data "aws_availability_zones" "available" {
state = "available"
}
```
### Dynamic Blocks
```hcl
resource "aws_security_group" "app" {
name = "app-sg"
description = "Security group for application"
vpc_id = var.vpc_id
dynamic "ingress" {
for_each = var.ingress_rules
content {
from_port = ingress.value.from_port
to_port = ingress.value.to_port
protocol = ingress.value.protocol
cidr_blocks = ingress.value.cidr_blocks
description = ingress.value.description
}
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
# Variable definition
variable "ingress_rules" {
description = "List of ingress rules"
type = list(object({
from_port = number
to_port = number
protocol = string
cidr_blocks = list(string)
description = string
}))
}
```
### Conditional Resource Creation
```hcl
# Create resource only in production
resource "aws_db_instance" "replica" {
count = var.environment == "prod" ? var.replica_count : 0
identifier = "${var.name_prefix}-replica-${count.index + 1}"
replicate_source_db = aws_db_instance.primary.identifier
instance_class = var.replica_instance_class
publicly_accessible = false
skip_final_snapshot = true
}
```
### Lifecycle Management
```hcl
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = var.instance_type
lifecycle {
# Prevent accidental deletion of critical resources
prevent_destroy = true
# Create new resource before destroying old one
create_before_destroy = true
# Ignore changes to specific attributes
ignore_changes = [
ami, # Allow AMI updates outside Terraform
tags["LastModified"],
]
}
}
```
## Security Best Practices
### Never Hardcode Credentials
```hcl
# ❌ BAD - Hardcoded credentials
resource "aws_db_instance" "bad" {
username = "admin"
password = "SuperSecret123!" # Never do this!
}
# ✅ GOOD - Use AWS Secrets Manager
data "aws_secretsmanager_secret_version" "db_password" {
secret_id = "prod/db/master-password"
}
resource "aws_db_instance" "good" {
username = "admin"
password = jsondecode(data.aws_secretsmanager_secret_version.db_password.secret_string)["password"]
}
# ✅ BETTER - Use random password and store in Secrets Manager
resource "random_password" "db_password" {
length = 32
special = true
}
resource "aws_secretsmanager_secret" "db_password" {
name = "${var.name_prefix}-db-password"
}
resource "aws_secretsmanager_secret_version" "db_password" {
secret_id = aws_secretsmanager_secret.db_password.id
secret_string = random_password.db_password.result
}
resource "aws_db_instance" "best" {
username = "admin"
password = random_password.db_password.result
}
```
### Encryption Best Practices
```hcl
# S3 Bucket with encryption
resource "aws_s3_bucket" "data" {
bucket = "${var.name_prefix}-data-bucket"
}
resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
bucket = aws_s3_bucket.data.id
rule {
apply_server_side_encryption_by_default {
sse_algorithm = "aws:kms"
kms_master_key_id = aws_kms_key.s3.arn
}
}
}
# EBS encryption
resource "aws_ebs_volume" "data" {
availability_zone = var.availability_zone
size = var.volume_size
encrypted = true
kms_key_id = aws_kms_key.ebs.arn
}
# RDS encryption
resource "aws_db_instance" "main" {
allocated_storage = var.allocated_storage
storage_encrypted = true
kms_key_id = aws_kms_key.rds.arn
}
```
## Cost Optimization Patterns
### Spot Instances with Auto Scaling
```hcl
resource "aws_launch_template" "app" {
name_prefix = "${var.name_prefix}-"
image_id = data.aws_ami.amazon_linux_2.id
instance_type = var.instance_type
instance_market_options {
market_type = "spot"
spot_options {
max_price = var.spot_max_price
spot_instance_type = "one-time"
}
}
}
resource "aws_autoscaling_group" "app" {
name = "${var.name_prefix}-asg"
vpc_zone_identifier = var.private_subnet_ids
min_size = var.min_size
max_size = var.max_size
desired_capacity = var.desired_capacity
mixed_instances_policy {
instances_distribution {
on_demand_base_capacity = 1
on_demand_percentage_above_base_capacity = 20
spot_allocation_strategy = "capacity-optimized"
}
launch_template {
launch_template_specification {
launch_template_id = aws_launch_template.app.id
version = "$Latest"
}
override {
instance_type = "t3.medium"
}
override {
instance_type = "t3a.medium"
}
}
}
}
```
### Resource Tagging for Cost Allocation
```hcl
locals {
common_tags = {
Environment = var.environment
Project = var.project_name
ManagedBy = "Terraform"
CostCenter = var.cost_center
Owner = var.owner
Application = var.application_name
CreatedDate = timestamp()
}
}
resource "aws_instance" "web" {
ami = data.aws_ami.amazon_linux_2.id
instance_type = var.instance_type
tags = merge(
local.common_tags,
{
Name = "${var.name_prefix}-web-server"
}
)
}
```
## Testing and Validation
### Input Validation
```hcl
variable "environment" {
description = "Environment name"
type = string
validation {
condition = contains(["dev", "staging", "prod"], var.environment)
error_message = "Environment must be dev, staging, or prod."
}
}
variable "instance_type" {
description = "EC2 instance type"
type = string
validation {
condition = can(regex("^[tm][3-6]\\.(nano|micro|small|medium|large|xlarge|2xlarge)$", var.instance_type))
error_message = "Instance type must be a valid t3-t6 or m3-m6 type."
}
}
```
### Pre-commit Hooks
```yaml
# .pre-commit-config.yaml
repos:
- repo: https://github.com/antonbabenko/pre-commit-terraform
rev: v1.77.0
hooks:
- id: terraform_fmt
- id: terraform_validate
- id: terraform_docs
- id: terraform_tflint
- id: terraform_checkov
```
### CI/CD Pipeline Validation
```yaml
# .github/workflows/terraform.yml
name: Terraform CI
on:
pull_request:
paths:
- 'terraform/**'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Terraform
uses: hashicorp/setup-terraform@v2
with:
terraform_version: 1.6.0
- name: Terraform Format Check
run: terraform fmt -check -recursive
- name: Terraform Init
run: terraform init -backend=false
working-directory: ./terraform/environments/dev
- name: Terraform Validate
run: terraform validate
working-directory: ./terraform/environments/dev
- name: Run Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: terraform/
framework: terraform
```
## Common Anti-Patterns to Avoid
### ❌ Don't: Manage state manually
- Never edit `.tfstate` files directly
- Never commit state files to Git
- Always use remote state with locking
### ❌ Don't: Use count for stateful resources
```hcl
# Bad - Changes in list order cause resource recreation
resource "aws_instance" "web" {
count = length(var.instance_names)
# ...
}
# Good - Use for_each for stable resource addressing
resource "aws_instance" "web" {
for_each = toset(var.instance_names)
# ...
}
```
### ❌ Don't: Create monolithic configurations
- Split large configurations into modules
- Separate concerns (networking, compute, data)
- Use module composition
### ❌ Don't: Ignore drift detection
```bash
# Run regularly to detect drift
terraform plan -out=tfplan
terraform show -json tfplan | jq '.resource_changes[] | select(.change.actions != ["no-op"])'
```
## Terraform Commands Cheat Sheet
```bash
# Initialize and download providers
terraform init
# Upgrade providers to latest versions
terraform init -upgrade
# Format code
terraform fmt -recursive
# Validate configuration
terraform validate
# Plan changes
terraform plan -out=tfplan
# Apply changes
terraform apply tfplan
# Destroy infrastructure
terraform destroy
# Import existing resource
terraform import aws_instance.example i-1234567890abcdef0
# Show current state
terraform show
# List resources in state
terraform state list
# Remove resource from state (without destroying)
terraform state rm aws_instance.example
# Refresh state
terraform refresh
# Output values
terraform output
# Workspace commands
terraform workspace new prod
terraform workspace select prod
terraform workspace list
```
## Advanced Topics
### Terraform Cloud/Enterprise
- Remote execution
- Policy as code with Sentinel
- Private module registry
- Cost estimation
- VCS integration
### Multi-Environment Strategy
1. **Workspaces**: Simple, same code, different state
2. **Directories**: More isolation, easier to understand
3. **Branches**: Git-based separation (not recommended)
**Recommended: Directory-based with shared modules**
### Dependency Management
```hcl
# Implicit dependency (Terraform detects automatically)
resource "aws_instance" "web" {
subnet_id = aws_subnet.public.id
}
# Explicit dependency (when needed)
resource "aws_instance" "web" {
# ...
depends_on = [aws_iam_role_policy_attachment.example]
}
```
---
## Resources
- [Terraform Registry](https://registry.terraform.io/)
- [Terraform AWS Provider](https://registry.terraform.io/providers/hashicorp/aws/latest/docs)
- [Terraform Best Practices](https://www.terraform-best-practices.com/)