Skip to content

Crossplane configuration that installs the OpenCost Helm chart with a minimal, stable interface.

Notifications You must be signed in to change notification settings

hops-ops/helm-opencost

Repository files navigation

helm-opencost

Crossplane configuration that installs OpenCost for Kubernetes cost monitoring and visibility.

Why OpenCost?

Without OpenCost:

  • No visibility into resource costs by namespace, workload, or team
  • Manual spreadsheet calculations to understand Kubernetes spending
  • Difficulty justifying infrastructure costs or implementing chargeback
  • No historical cost data for capacity planning decisions
  • Blind optimization - can't identify which workloads drive costs

With OpenCost:

  • Real-time cost allocation by namespace, pod, deployment, or label
  • Historical cost tracking for trend analysis and forecasting
  • Integration with Prometheus for accurate resource usage metrics
  • Open-source alternative to cloud vendor cost tools
  • Foundation for cost-aware autoscaling and optimization

The Journey

Stage 1: Getting Started (Default Configuration)

Minimal viable configuration for cost monitoring in a single cluster.

Why start here?

  • Establishes cost visibility from day one, even for small teams
  • Default Prometheus integration works with common setups
  • No migration pain - start tracking costs immediately
  • UI included for easy exploration and reporting
apiVersion: helm.hops.ops.com.ai/v1alpha1
kind: OpenCost
metadata:
  name: opencost
  namespace: example-env
spec:
  clusterName: example-cluster

This creates an OpenCost installation with:

  • Default Prometheus endpoint: prometheus-server.prometheus-system:80
  • Default cluster ID: example-cluster (used for multi-cluster cost aggregation)
  • UI enabled at http://opencost.opencost:9090
  • Default namespace: opencost

Stage 2: Custom Prometheus Integration

Adapt OpenCost to your monitoring stack's Prometheus endpoint.

Why customize?

  • Your Prometheus might use a different service name or namespace
  • Different port configurations for security or legacy reasons
  • Need to connect to external Prometheus instances
apiVersion: helm.hops.ops.com.ai/v1alpha1
kind: OpenCost
metadata:
  name: opencost
  namespace: example-env
spec:
  clusterName: production-us-east-1
  labels:
    team: platform
    environment: production
  namespace: opencost
  values:
    opencost:
      prometheus:
        internal:
          serviceName: kube-prometheus-stack-prometheus
          namespaceName: monitoring
          port: 9090

This example:

  • Connects to a kube-prometheus-stack Prometheus installation
  • Applies custom labels for organizational tracking
  • Uses a more descriptive cluster name for multi-cluster deployments

Stage 3: Advanced Configuration

Full control over Helm values for enterprise requirements.

Why this matters at scale?

  • Custom resource limits and requests for production SLAs
  • Integration with organizational RBAC policies
  • Custom ingress, storage, or networking requirements
  • Advanced features like cloud cost integration (AWS, GCP, Azure)
apiVersion: helm.hops.ops.com.ai/v1alpha1
kind: OpenCost
metadata:
  name: opencost
  namespace: platform-prod
spec:
  clusterName: prod-us-east-1
  providerConfigRef:
    name: prod-helm-provider
    kind: ProviderConfig
  labels:
    team: platform
    environment: production
    cost-center: engineering
  namespace: opencost
  values:
    opencost:
      exporter:
        defaultClusterId: prod-us-east-1
        cloudProviderApiKey: "{{ .Values.cloudApiKey }}"
        resources:
          requests:
            cpu: 100m
            memory: 128Mi
          limits:
            cpu: 500m
            memory: 512Mi
      prometheus:
        internal:
          serviceName: prometheus-operated
          namespaceName: monitoring
          port: 9090
      ui:
        enabled: true
        ingress:
          enabled: true
          hosts:
            - cost.example.com
          tls:
            - secretName: opencost-tls
              hosts:
                - cost.example.com
      persistence:
        enabled: true
        size: 10Gi

Stage 4: Complete Override

Replace all default Helm values for specialized deployments.

Why override?

  • Migration from existing OpenCost installation
  • Highly customized configurations that conflict with defaults
  • Testing or development scenarios requiring specific chart values
apiVersion: helm.hops.ops.com.ai/v1alpha1
kind: OpenCost
metadata:
  name: opencost-dev
  namespace: dev-env
spec:
  clusterName: dev-cluster
  overrideAllValues:
    fullnameOverride: opencost-dev
    opencost:
      exporter:
        defaultClusterId: dev-testing
      prometheus:
        external:
          enabled: true
          url: https://prometheus.external.example.com
      ui:
        enabled: false

Using OpenCost

Reference the OpenCost status in downstream resources or for monitoring:

apiVersion: example.com/v1alpha1
kind: Dashboard
spec:
  opencostRef:
    name: opencost
  # Wait for OpenCost to be ready before creating dashboard
  waitForRefs:
    - kind: OpenCost
      name: opencost
      statusField: status.ready

Access the OpenCost UI:

# Port-forward to the UI
kubectl port-forward -n opencost svc/opencost 9090:9090

# Open browser to http://localhost:9090

Query the OpenCost API:

# Get allocation data for the last 7 days
curl http://opencost.opencost:9003/allocation \
  -d window=7d \
  -d aggregate=namespace

Status

The OpenCost XRD exposes the following status fields:

status:
  ready: true                    # Overall readiness (true when Helm release is ready)
  release:
    name: opencost              # Name of the Helm release
    namespace: opencost         # Namespace where release is deployed
    ready: true                 # Helm release readiness status

Use status.ready in dependencies and health checks. The XRD is considered ready when the Helm release reports ready status (all pods running and healthy).

Configuration Reference

Spec Fields

Field Type Required Default Description
clusterName string Yes - Name of the target cluster. Used as default for provider config and default cluster ID.
namespace string No "opencost" Namespace for the Helm release.
name string No XR metadata.name Helm release name.
labels object No See below Custom labels applied to all managed resources. Merged with default labels.
providerConfigRef object No {name: clusterName, kind: "ProviderConfig"} Reference to the Helm ProviderConfig.
providerConfigRef.name string No Value of clusterName Name of the ProviderConfig.
providerConfigRef.kind string No "ProviderConfig" Kind of the ProviderConfig. Valid values: ProviderConfig, ClusterProviderConfig.
values object No {} Helm values merged with defaults. See Default Values below.
overrideAllValues object No - Helm values that replace all defaults. Use when you need complete control.
managementPolicies array No ["*"] Crossplane management policies for all composed resources.

Default Labels

All resources receive these labels (merged with custom labels):

hops.ops.com.ai/managed: "true"
hops.ops.com.ai/opencost: <metadata.name>

Default Helm Values

When using spec.values, these defaults are applied first, then your values are merged:

fullnameOverride: opencost
nameOverride: opencost
opencost:
  exporter:
    defaultClusterId: <spec.clusterName>
  prometheus:
    internal:
      enabled: true
      serviceName: prometheus-server
      namespaceName: prometheus-system
      port: 80
  ui:
    enabled: true

Your spec.values are merged using YAML merge semantics. To completely replace defaults, use spec.overrideAllValues instead.

Common Helm Values

Refer to the OpenCost Helm chart documentation for all available values. Common customizations:

Prometheus Configuration:

values:
  opencost:
    prometheus:
      internal:
        enabled: true
        serviceName: prometheus-server
        namespaceName: monitoring
        port: 9090

External Prometheus:

values:
  opencost:
    prometheus:
      external:
        enabled: true
        url: https://prometheus.example.com

Cloud Cost Integration:

values:
  opencost:
    exporter:
      cloudProviderApiKey: "YOUR_API_KEY"
      aws:
        accessKeyId: "AWS_ACCESS_KEY"
        secretAccessKey: "AWS_SECRET_KEY"

Resource Limits:

values:
  opencost:
    exporter:
      resources:
        requests:
          cpu: 100m
          memory: 128Mi
        limits:
          cpu: 500m
          memory: 512Mi

UI Ingress:

values:
  opencost:
    ui:
      enabled: true
      ingress:
        enabled: true
        hosts:
          - cost.example.com

Architecture

Composed Resources

This XRD creates a single Helm release:

  • Helm Release (helm.m.crossplane.io/v1beta1/Release) - Deploys the OpenCost chart (v2.5.5) from the official OpenCost Helm repository (https://opencost.github.io/opencost-helm-chart).

The Helm release manages:

  • Deployment - OpenCost exporter and optional UI
  • Service - Exposes OpenCost API (port 9003) and UI (port 9090)
  • ServiceAccount - RBAC for reading cluster resources
  • ClusterRole/ClusterRoleBinding - Permissions for cost allocation

Dependencies

This configuration requires:

  • provider-helm (>=v1.0.6) - Manages Helm releases
  • function-auto-ready (>=v0.6.0) - Automatic readiness detection

The Helm provider must be configured with credentials for the target cluster. Typically this is a ProviderConfig referencing a kubeconfig secret or using in-cluster authentication.

Resource Flow

OpenCost XR
    |
    v
Helm Release (opencost chart v2.5.5)
    |
    +-- Deployment (exporter + UI)
    +-- Service (API + UI ports)
    +-- ServiceAccount + RBAC
    +-- ConfigMap (Prometheus config)

Examples

Minimal Example

Deploy OpenCost with all defaults:

apiVersion: helm.hops.ops.com.ai/v1alpha1
kind: OpenCost
metadata:
  name: opencost
  namespace: example-env
spec:
  clusterName: example-cluster

See examples/opencosts/minimal.yaml

Standard Example

Deploy with custom Prometheus integration:

apiVersion: helm.hops.ops.com.ai/v1alpha1
kind: OpenCost
metadata:
  name: opencost
  namespace: example-env
spec:
  clusterName: example-cluster
  labels:
    team: platform
  namespace: opencost
  values:
    opencost:
      prometheus:
        internal:
          serviceName: kube-prometheus-stack-prometheus
          namespaceName: monitoring
          port: 9090

See examples/opencosts/standard.yaml

Development

Prerequisites

Commands

Render examples to YAML:

make render              # Render all examples
make render:minimal      # Render specific example
make render:standard

Validate rendered output:

make validate            # Validate all examples
make validate:minimal    # Validate specific example

Run unit tests:

make test                # Run KCL render tests

Run E2E tests:

make e2e                 # Run E2E tests (requires cluster)

Build package:

make build               # Build Crossplane package

Publish package:

make publish tag=v1.0.0  # Build and push to registry

Clean build artifacts:

make clean               # Remove _output and .up directories

Testing Changes

  1. Modify templates in functions/render/
  2. Run make render to see generated output
  3. Run make validate to verify schema compliance
  4. Run make test to run unit tests
  5. Apply to a test cluster and verify behavior

Package Structure

.
├── apis/
│   └── opencosts/
│       ├── composition.yaml      # Pipeline composition
│       ├── configuration.yaml    # Package metadata
│       └── definition.yaml       # XRD schema
├── examples/
│   └── opencosts/
│       ├── minimal.yaml          # Minimal example
│       └── standard.yaml         # Standard example
├── functions/
│   └── render/
│       ├── 000-state-init.yaml.gotmpl
│       ├── 001-state-observed-helm.yaml.gotmpl
│       ├── 005-state-opencost.yaml.gotmpl
│       ├── 010-state-status.yaml.gotmpl
│       ├── 200-helm-release-opencost.yaml.gotmpl
│       └── 999-status.yaml.gotmpl
├── tests/                        # KCL and E2E tests
├── Makefile                      # Build and test commands
└── upbound.yaml                  # Project configuration

License

Apache-2.0

About

Crossplane configuration that installs the OpenCost Helm chart with a minimal, stable interface.

Topics

Resources

Stars

Watchers

Forks

Packages