FinOps: Engineering Practices for Cloud Cost Management

December 2, 2019

Cloud spending is out of control at most organizations. The promise of pay-as-you-go has become pay-way-more-than-expected. Teams provision resources without understanding costs. Bills arrive as surprises. Nobody knows who owns what.

FinOps brings engineering discipline to cloud financial management. Here’s how to implement it.

What is FinOps

FinOps is the practice of bringing financial accountability to the variable spend model of cloud. It combines systems, best practices, and culture to increase business value from cloud spending.

Core Principles

Teams take ownership: Individual teams are responsible for their cloud costs, not a central IT budget.

Everyone is accountable: Engineers make cost-aware decisions. Finance understands cloud economics.

Real-time data: Cost visibility in hours or days, not monthly invoices.

Decisions are business-driven: Sometimes spending more is right. Optimize for value, not just cost.

Continuous improvement: Cloud efficiency is never “done.”

The FinOps Lifecycle

┌─────────────────────────────────────────────────────────────────┐
│                                                                  │
│  ┌──────────┐     ┌──────────┐     ┌──────────┐                │
│  │  Inform  │────►│ Optimize │────►│  Operate │                │
│  └──────────┘     └──────────┘     └──────────┘                │
│       │                                   │                      │
│       └───────────────────────────────────┘                      │
│                    (continuous)                                   │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Inform: Visibility, allocation, benchmarking
Optimize: Right-sizing, commitment discounts, waste elimination
Operate: Governance, automation, organizational alignment

Cost Visibility

Tagging Strategy

Tags are the foundation of cost allocation:

# Required tags for all resources
tags:
  environment: production|staging|development
  team: orders|payments|platform
  service: api|worker|database
  cost-center: CC-12345
  owner: team-lead@company.com

# Optional but useful
tags:
  created-by: terraform|manual
  expiry: 2019-12-31  # For temporary resources

Enforcement

Prevent untagged resources:

# Terraform with tag enforcement
provider "aws" {
  default_tags {
    tags = {
      environment = var.environment
      team        = var.team
      service     = var.service
      managed-by  = "terraform"
    }
  }
}

# AWS Service Control Policy
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Deny",
      "Action": "ec2:RunInstances",
      "Resource": "arn:aws:ec2:*:*:instance/*",
      "Condition": {
        "Null": {
          "aws:RequestTag/team": "true"
        }
      }
    }
  ]
}

Cost Allocation

Allocate shared costs fairly:

# Shared infrastructure allocation
def allocate_shared_costs(total_shared_cost, teams):
    allocations = {}

    # Option 1: By compute usage
    total_compute = sum(t.compute_hours for t in teams)
    for team in teams:
        allocations[team.name] = (team.compute_hours / total_compute) * total_shared_cost

    # Option 2: By headcount
    total_headcount = sum(t.engineers for t in teams)
    for team in teams:
        allocations[team.name] = (team.engineers / total_headcount) * total_shared_cost

    # Option 3: Fixed percentage
    for team in teams:
        allocations[team.name] = total_shared_cost * team.cost_share_percentage

    return allocations

Dashboards

Real-time cost visibility:

# Key metrics per team
dashboards:
  - name: Team Cost Overview
    metrics:
      - total_spend_mtd
      - spend_vs_budget
      - cost_per_transaction
      - waste_percentage
      - reserved_coverage

  - name: Service Breakdown
    dimensions:
      - by_service
      - by_environment
      - by_resource_type

  - name: Trends
    charts:
      - daily_spend_30d
      - week_over_week_change
      - projected_month_end

Right-Sizing

Identifying Waste

Common waste patterns:

# Check for these
oversized_instances:
  signal: CPU < 10% average, memory < 30%
  action: Downsize or use burstable instances

idle_resources:
  signal: Zero traffic/usage for 7+ days
  action: Terminate or ask owner

unused_storage:
  signal: Unattached volumes, old snapshots
  action: Delete or archive

over_provisioned_databases:
  signal: IOPS utilization < 20%
  action: Downsize or switch to burstable

Automated Analysis

# Right-sizing recommendations
def analyze_instance(instance_id, days=14):
    metrics = cloudwatch.get_metrics(
        instance_id,
        metrics=['CPUUtilization', 'MemoryUtilization'],
        period=days * 24 * 3600
    )

    cpu_avg = statistics.mean(metrics['CPUUtilization'])
    cpu_max = max(metrics['CPUUtilization'])
    mem_avg = statistics.mean(metrics['MemoryUtilization'])

    current_type = ec2.describe_instance(instance_id).instance_type

    recommendation = None

    if cpu_avg < 10 and mem_avg < 30:
        # Significantly oversized
        recommendation = get_smaller_instance_type(current_type, target_cpu=cpu_max * 2)
    elif cpu_avg < 5:
        # Consider burstable
        recommendation = get_burstable_type(current_type)

    return {
        'instance_id': instance_id,
        'current_type': current_type,
        'cpu_avg': cpu_avg,
        'cpu_max': cpu_max,
        'mem_avg': mem_avg,
        'recommendation': recommendation,
        'estimated_savings': calculate_savings(current_type, recommendation)
    }

Kubernetes Right-Sizing

# VPA (Vertical Pod Autoscaler) recommendations
apiVersion: autoscaling.k8s.io/v1
kind: VerticalPodAutoscaler
metadata:
  name: api-vpa
spec:
  targetRef:
    apiVersion: apps/v1
    kind: Deployment
    name: api
  updatePolicy:
    updateMode: "Off"  # Recommendations only, don't auto-apply

Review recommendations:

kubectl describe vpa api-vpa
# Shows recommended CPU/memory based on actual usage

Commitment Discounts

Reserved Instances vs Savings Plans

Reserved Instances:

Savings Plans:

Commitment Strategy

# Coverage analysis
def calculate_commitment_strategy():
    # Get usage patterns
    on_demand_usage = analyze_usage(days=90)

    # Baseline: always-on workloads
    baseline = on_demand_usage.percentile(10)  # Conservative

    # Variable: can use spot/on-demand
    variable = on_demand_usage.max() - baseline

    return {
        'reserved_coverage': baseline,
        'reserved_savings': baseline * 0.40,  # ~40% discount
        'spot_opportunity': variable * 0.5,   # Half could be spot
        'remaining_on_demand': variable * 0.5
    }

Reserved Instance Management

# Track RI utilization
ri_metrics:
  coverage_target: 80%  # Of steady-state compute
  utilization_target: 90%  # Of purchased RIs

alerts:
  - condition: ri_utilization < 80%
    action: Review RI portfolio, sell unused

  - condition: on_demand_spend > 20% of steady_state
    action: Consider additional commitments

Spot Instances

When to Use Spot

Good candidates:

Not good:

Spot Implementation

# Kubernetes spot node pool
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig
metadata:
  name: my-cluster
managedNodeGroups:
  - name: spot-workers
    instanceTypes: ["m5.large", "m5a.large", "m4.large"]
    spot: true
    minSize: 2
    maxSize: 20
    labels:
      node-type: spot
    taints:
      - key: spot
        value: "true"
        effect: NoSchedule
# Workload tolerating spot
apiVersion: apps/v1
kind: Deployment
metadata:
  name: batch-worker
spec:
  template:
    spec:
      tolerations:
        - key: spot
          operator: Equal
          value: "true"
          effect: NoSchedule
      nodeSelector:
        node-type: spot
      terminationGracePeriodSeconds: 120
      containers:
        - name: worker
          lifecycle:
            preStop:
              exec:
                command: ["/bin/sh", "-c", "checkpoint-work.sh"]

Governance

Budgets and Alerts

# AWS Budget
budget = {
    'BudgetName': 'team-orders-monthly',
    'BudgetLimit': {
        'Amount': '10000',
        'Unit': 'USD'
    },
    'TimeUnit': 'MONTHLY',
    'BudgetType': 'COST',
    'CostFilters': {
        'TagKeyValue': ['team$orders']
    },
    'NotificationsWithSubscribers': [
        {
            'Notification': {
                'NotificationType': 'ACTUAL',
                'ComparisonOperator': 'GREATER_THAN',
                'Threshold': 80
            },
            'Subscribers': [
                {'SubscriptionType': 'EMAIL', 'Address': 'team-orders@company.com'}
            ]
        }
    ]
}

Cost Anomaly Detection

# Simple anomaly detection
def detect_cost_anomaly(team, current_spend):
    historical = get_daily_spend(team, days=30)
    mean = statistics.mean(historical)
    std_dev = statistics.stdev(historical)

    # More than 2 standard deviations
    if current_spend > mean + (2 * std_dev):
        alert(f"Cost anomaly detected for {team}: ${current_spend} (avg: ${mean})")

Approval Workflows

# Expensive resource approval
approval_thresholds:
  - type: ec2_instance
    condition: instance_type matches 'x*' or '*.metal'
    requires: manager_approval

  - type: any
    condition: estimated_monthly_cost > 1000
    requires: finance_approval

  - type: production_database
    condition: always
    requires: architecture_review

Team Practices

Engineering Checklist

Before deploying:

Monthly Review

## Team Orders - November 2019 Cost Review

### Summary
- Total spend: $12,500 (budget: $12,000)
- +4% over budget, +8% MoM

### Top Costs
1. EC2: $6,000 (RDS needs right-sizing)
2. RDS: $3,500 (staging DB oversized)
3. S3: $2,000 (old backups not deleted)
4. Other: $1,000

### Actions
- [ ] Right-size staging RDS (savings: ~$800/mo)
- [ ] Implement S3 lifecycle policy (savings: ~$500/mo)
- [ ] Review EC2 RI coverage (current: 65%, target: 80%)

Unit Economics

Track cost per business metric:

metrics:
  cost_per_order: total_cloud_cost / orders_processed
  cost_per_user: total_cloud_cost / active_users
  cost_per_api_call: total_cloud_cost / api_requests

These metrics show if you’re scaling efficiently.

Key Takeaways

Cloud costs aren’t inevitable. With FinOps practices, teams can deliver value efficiently.