From 5e0b597cf4ddd79a86d0bb7abcf1a54f626b24a7 Mon Sep 17 00:00:00 2001 From: = <=> Date: Sat, 18 Apr 2026 08:22:20 +0000 Subject: [PATCH] Added kubecost templates --- kubernetes-templates/kubecost/deployment.yml | 156 ++++++++++++ kubernetes-templates/kubecost/values.yml | 235 +++++++++++++++++++ 2 files changed, 391 insertions(+) create mode 100644 kubernetes-templates/kubecost/deployment.yml create mode 100644 kubernetes-templates/kubecost/values.yml diff --git a/kubernetes-templates/kubecost/deployment.yml b/kubernetes-templates/kubecost/deployment.yml new file mode 100644 index 0000000..e019074 --- /dev/null +++ b/kubernetes-templates/kubecost/deployment.yml @@ -0,0 +1,156 @@ +{{- if .Values.hooks.clusterID.enabled }} +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ template "kubecost.fullname" . }}-hook + namespace: {{ $.Release.Namespace }} + labels: + app: {{ template "kubecost.name" . }}-kubecost +{{ include "kubecost.labels" . | indent 4 }} + annotations: + helm.sh/hook: pre-install,pre-upgrade,post-delete + helm.sh/hook-weight: "-5" + helm.sh/hook-delete-policy: before-hook-creation +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: {{ template "kubecost.fullname" . }}-hook + labels: + app: {{ template "kubecost.name" . }} +{{ include "kubecost.labels" . | indent 4 }} + annotations: + helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade,post-delete + helm.sh/hook-weight: "-4" + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation +rules: +# The following namespaces and configmaps permissions are needed to +# grab the kube-system namespace uid and write it out to a configmap. +- apiGroups: [""] + resources: + - namespaces + verbs: ["get"] +- apiGroups: [""] + resources: + - configmaps + verbs: ["get", "create", "patch", "update", "delete"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: {{ template "kubecost.fullname" . }}-hook + labels: + app: {{ template "kubecost.name" . }} +{{ include "kubecost.labels" . | indent 4 }} + annotations: + helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgrade,post-delete + helm.sh/hook-weight: "-3" + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: {{ template "kubecost.fullname" . }}-hook +subjects: + - kind: ServiceAccount + name: {{ template "kubecost.fullname" . }}-hook + namespace: {{ $.Release.Namespace }} +{{- if index .Values "cost-analyzer" "global" "prometheus" "enabled" }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-edit-prom-cluster-id + namespace: {{ .Release.Namespace }} + annotations: + helm.sh/hook: post-install,post-upgrade + helm.sh/hook-weight: "3" + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation +spec: + template: + metadata: + name: {{ .Release.Name }}-edit-prom-cluster-id + spec: + serviceAccountName: {{ template "kubecost.fullname" . }}-hook +{{- if .Values.hooks.clusterID.priorityClassName }} + priorityClassName: "{{ .Values.hooks.clusterID.priorityClassName }}" +{{- end }} + containers: + - name: kubectl + image: {{ .Values.hooks.clusterID.kubectlImage | quote }} + imagePullPolicy: IfNotPresent + command: + - sh + - "-c" + - | + /bin/bash <<'EOF' + set -o nounset + set -o errexit + set -o pipefail + CLUSTERID=$(kubectl get namespace kube-system -o jsonpath="{.metadata.uid}") + kubectl get configmap/{{ template "kubecost.prometheus-server-cm-name" . }} -oyaml > /tmp/override.yaml + sed -e 's/\$CLUSTER_ID/"'$CLUSTERID'"/g' /tmp/override.yaml > /tmp/final.yaml + kubectl apply -f /tmp/final.yaml + echo "Done replacing \$CLUSTER_ID with $CLUSTERID" + EOF + restartPolicy: OnFailure +{{- end }} +{{- if index .Values "cost-analyzer" "prometheus" "server" "clusterIDConfigmap" }} +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-cluster-id-configmap + namespace: {{ .Release.Namespace }} + annotations: + helm.sh/hook: pre-install,pre-upgrade + helm.sh/hook-weight: "3" + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation +spec: + template: + metadata: + name: {{ .Release.Name }}-cluster-id-configmap + spec: + serviceAccountName: {{ template "kubecost.fullname" . }}-hook +{{- if .Values.hooks.clusterID.priorityClassName }} + priorityClassName: "{{ .Values.hooks.clusterID.priorityClassName }}" +{{- end }} + containers: + - name: kubectl + image: {{ .Values.hooks.clusterID.kubectlImage | quote }} + imagePullPolicy: IfNotPresent + command: + - sh + - "-c" + - kubectl create configmap {{ index .Values "cost-analyzer" "prometheus" "server" "clusterIDConfigmap" }} -n {{ .Release.Namespace }} -oyaml --dry-run --save-config --from-literal=CLUSTER_ID=$(kubectl get namespace kube-system -o jsonpath="{.metadata.uid}") | kubectl apply -f - + restartPolicy: OnFailure +--- +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ .Release.Name }}-cleanup-cluster-id-configmap + namespace: {{ .Release.Namespace }} + annotations: + helm.sh/hook: post-delete + helm.sh/hook-weight: "3" + helm.sh/hook-delete-policy: hook-succeeded,before-hook-creation +spec: + template: + metadata: + name: {{ .Release.Name }}-cleanup-cluster-id-configmap + spec: + serviceAccountName: {{ template "kubecost.fullname" . }}-hook +{{- if .Values.hooks.clusterID.priorityClassName }} + priorityClassName: "{{ .Values.hooks.clusterID.priorityClassName }}" +{{- end }} + containers: + - name: kubectl + image: {{ .Values.hooks.clusterID.kubectlImage | quote }} + imagePullPolicy: IfNotPresent + command: + - /bin/sh + - -c + - kubectl delete configmap {{ index .Values "cost-analyzer" "prometheus" "server" "clusterIDConfigmap" }} -n {{ .Release.Namespace }} --ignore-not-found + restartPolicy: OnFailure +{{- end }} +{{- end }} diff --git a/kubernetes-templates/kubecost/values.yml b/kubernetes-templates/kubecost/values.yml new file mode 100644 index 0000000..85a7d84 --- /dev/null +++ b/kubernetes-templates/kubecost/values.yml @@ -0,0 +1,235 @@ +# Default values for kubecost parent chart (wraps kubecost cost-analyzer chart). +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +hooks: + # Modifies the prometheus configmap to set the prometheus cluster_id + # external label to the cluster's kube-system ns uid. + # Creates configmap to pass kube-system ns uid as envvar to kubecost. + clusterID: + enabled: true + kubectlImage: "bitnamilegacy/kubectl:1.30.5" + priorityClassName: "" + +cost-analyzer: + enabled: true + + global: + prometheus: + # If false, Prometheus will not be installed -- only actively supported on paid Kubecost plans + enabled: true + + thanos: + enabled: false + + grafana: + # If false, Grafana will not be installed + enabled: true + + notifications: + alertmanager: + # If true, allow kubecost to write to alertmanager + enabled: true + podSecurityPolicy: + enabled: false + # Define persistence volume for cost-analyzer, more information at https://github.com/kubecost/docs/blob/master/storage.md + persistentVolume: + # Upgrades from original default 0.2Gi may break if automatic disk resize is not supported + # https://github.com/kubecost/cost-analyzer-helm-chart/issues/507 + size: 32Gi + # Note that setting this to false means configurations will be wiped out on pod restart. + enabled: true + # storageClass: "-" + + prometheus: + kubeStateMetrics: + enabled: false + kube-state-metrics: + disabled: true + nodeExporter: + enabled: false + serviceAccounts: + nodeExporter: + create: false + extraScrapeConfigs: | + - job_name: kubecost + honor_labels: true + scrape_interval: 1m + scrape_timeout: 10s + metrics_path: /metrics + scheme: http + dns_sd_configs: + - names: + - {{ .Release.Name }}-cost-analyzer + type: 'A' + port: 9003 + - job_name: kubecost-networking + kubernetes_sd_configs: + - role: pod + relabel_configs: + # Scrape only the the targets matching the following metadata + - source_labels: [__meta_kubernetes_pod_label_app] + action: keep + regex: {{ .Release.Name }}-network-costs + server: + image: + tag: v2.55.0 + # If clusterIDConfigmap is defined, instead use user-generated configmap with key CLUSTER_ID + # to use as unique cluster ID in kubecost cost-analyzer deployment. + # This overrides the cluster_id set in prometheus.server.global.external_labels. + # NOTE: This does not affect the external_labels set in prometheus config. + clusterIDConfigmap: kubecost-cluster-info-configmap + extraFlags: + - web.enable-admin-api + - web.enable-lifecycle + - storage.tsdb.wal-compression + resources: + limits: + cpu: 1000m + memory: 2500Mi + requests: + cpu: 300m + memory: 1500Mi + global: + scrape_interval: 1m + scrape_timeout: 10s + evaluation_interval: 1m + external_labels: + cluster_id: $CLUSTER_ID + persistentVolume: + size: 32Gi + enabled: true + extraArgs: + log.level: info + log.format: json + storage.tsdb.min-block-duration: 2h + storage.tsdb.max-block-duration: 2h + query.max-concurrency: 1 + query.max-samples: 100000000 + enableAdminApi: true + service: + gRPC: + enabled: true + sidecarContainers: + - name: thanos-sidecar + image: quay.io/thanos/thanos:v0.36.1 + args: + - sidecar + - --log.level=debug + - --tsdb.path=/data/ + - --prometheus.url=http://127.0.0.1:9090 + - --reloader.config-file=/etc/config/prometheus.yml + # Start of time range limit to serve. Thanos sidecar will serve only metrics, which happened + # later than this value. Option can be a constant time in RFC3339 format or time duration + # relative to current time, such as -1d or 2h45m. Valid duration units are ms, s, m, h, d, w, y. + - --min-time=-3h + env: + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + ports: + - name: sidecar-http + containerPort: 10902 + - name: grpc + containerPort: 10901 + - name: cluster + containerPort: 10900 + volumeMounts: + - name: config-volume + mountPath: /etc/config + - name: storage-volume + mountPath: /data + subPath: "" + alertmanager: + enabled: true + image: + repository: quay.io/prometheus/alertmanager + tag: v0.27.0 + resources: + limits: + cpu: 50m + memory: 100Mi + requests: + cpu: 10m + memory: 50Mi + persistentVolume: + enabled: true + pushgateway: + enabled: false + persistentVolume: + enabled: false + serverFiles: + alerts: + groups: + - name: Kubecost + rules: + - alert: kubecostDown + expr: up{job="kubecost"} == 0 + annotations: + message: 'Kubecost metrics endpoint is not being scraped successfully.' + for: 10m + labels: + severity: warning + - alert: kubecostMetricsUnavailable + expr: sum(sum_over_time(node_cpu_hourly_cost[5m])) == 0 + annotations: + message: 'Kubecost metrics are not available in Prometheus.' + for: 10m + labels: + severity: warning + - alert: kubecostRecordingRulesNotEvaluated + expr: avg_over_time(kubecost_cluster_memory_working_set_bytes[5m]) == 0 + annotations: + message: 'Kubecost recording rules are not being successfully evaluated.' + for: 10m + labels: + severity: warning + grafana: + sidecar: + image: + repository: docker.io/kiwigrid/k8s-sidecar + tag: 1.28.0 + dashboards: + enabled: true + label: kubecost_grafana_dashboard + datasources: + enabled: true + defaultDatasourceEnabled: false + label: kubecost_grafana_datasource + # Enable grafana ingress with below annotations to use Konvoy traefik auth + # ingress: + # enabled: true + # annotations: + # kubernetes.io/ingress.class: kommander-traefik + # ingress.kubernetes.io/auth-response-headers: X-Forwarded-User + # traefik.ingress.kubernetes.io/router.tls: "true" + # traefik.ingress.kubernetes.io/router.middlewares: "${workspaceNamespace}-stripprefixes@kubernetescrd,${workspaceNamespace}-forwardauth@kubernetescrd" + # hosts: [""] + # path: "/dkp/kubecost/grafana" + # Configure grafana.ini server.root_url properly if ingress is enabled + # grafana.ini: + # server: + # protocol: http + # enable_gzip: true + # root_url: "%(protocol)s://%(domain)s:%(http_port)s/dkp/kubecost/grafana" + # auth.proxy: + # enabled: true + # header_name: X-Forwarded-User + # auto-sign-up: true + # auth.basic: + # enabled: false + # users: + # auto_assign_org_role: Admin + + thanos: + store: + enabled: false + query: + enabled: false + sidecar: + enabled: false + bucket: + enabled: false + compact: + enabled: false