{"id":16453268,"url":"https://github.com/trallnag/kubestatus2cloudwatch","last_synced_at":"2026-01-12T01:10:17.168Z","repository":{"id":65163671,"uuid":"582991925","full_name":"trallnag/kubestatus2cloudwatch","owner":"trallnag","description":"Watches the status of resources in a Kubernetes cluster and uses the results to update a metric in Amazon CloudWatch.","archived":false,"fork":false,"pushed_at":"2026-01-02T12:37:13.000Z","size":468,"stargazers_count":1,"open_issues_count":13,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-03T07:08:55.806Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"isc","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trallnag.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-12-28T12:52:33.000Z","updated_at":"2025-12-18T10:26:59.000Z","dependencies_parsed_at":"2023-11-29T11:28:31.110Z","dependency_job_id":"849d0fd9-370e-4b54-9760-394506525ea7","html_url":"https://github.com/trallnag/kubestatus2cloudwatch","commit_stats":{"total_commits":135,"total_committers":4,"mean_commits":33.75,"dds":"0.40740740740740744","last_synced_commit":"16619272cb53cc745f98ecbc960071e16f2000cf"},"previous_names":["trallnag/kubestat2cloudwatch-aggregator"],"tags_count":11,"template":false,"template_full_name":null,"purl":"pkg:github/trallnag/kubestatus2cloudwatch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trallnag%2Fkubestatus2cloudwatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trallnag%2Fkubestatus2cloudwatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trallnag%2Fkubestatus2cloudwatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trallnag%2Fkubestatus2cloudwatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trallnag","download_url":"https://codeload.github.com/trallnag/kubestatus2cloudwatch/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trallnag%2Fkubestatus2cloudwatch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28330327,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T00:36:25.062Z","status":"ssl_error","status_checked_at":"2026-01-12T00:36:15.229Z","response_time":60,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aws","cloudwatch","kubernetes"],"created_at":"2024-10-11T10:15:05.089Z","updated_at":"2026-01-12T01:10:17.134Z","avatar_url":"https://github.com/trallnag.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![status](https://img.shields.io/badge/status-active-brightgreen)](#project-status)\n[![release](https://img.shields.io/github/v/release/trallnag/kubestatus2cloudwatch)](https://github.com/trallnag/kubestatus2cloudwatch/releases)\n[![ci](https://img.shields.io/github/actions/workflow/status/trallnag/kubestatus2cloudwatch/ci.yaml?label=ci)](https://github.com/trallnag/kubestatus2cloudwatch/actions/workflows/ci.yaml)\n[![release](https://img.shields.io/github/actions/workflow/status/trallnag/kubestatus2cloudwatch/release.yaml?label=release)](https://github.com/trallnag/kubestatus2cloudwatch/actions/workflows/release.yaml)\n\n# Kubestatus2cloudwatch \u003c!-- omit from toc --\u003e\n\nSmall program written in Go that continuously watches the status of certain\nresources in a Kubernetes cluster, aggregates these into a single value, and\nuses that to update a metric in Amazon CloudWatch.\n\nThe metric will have the value 1 if all targets are healthy, the value 0 if at\nleast one target is unhealthy (according to the configuration), and missing data\nif Kubestatus2cloudwatch is unhealthy / down.\n\n## Table of contents \u003c!-- omit from toc --\u003e\n\n- [Motivation](#motivation)\n- [Use case](#use-case)\n- [Getting started](#getting-started)\n- [Configuration](#configuration)\n- [Project status](#project-status)\n- [Versioning](#versioning)\n- [Contributing](#contributing)\n- [Licensing](#licensing)\n\n## Motivation\n\nLately (2022) I've been using Amazon EKS for running and orchestrating\ncontainerized workloads. To monitor the clusters and the workloads within them\nthe popular tools Prometheus, Grafana, and friends are used. They are hosted\nwithin the clusters and they will notify my team and me if an alert fires.\n\nBut what if the observability system itself goes down? We won't get any\nnotification in that case. And since it is all self-hosted and self-contained\nthere are no SLAs or similar.\n\nWe somehow have to monitor the monitoring system. This is where\nKubestatus2cloudwatch comes in. It scans the status of the monitoring components\nin the cluster and manages a CloudWatch metric that reflects the overall status.\nNow I can go ahead and create a CloudWatch alarm and friends to monitor this one\nmetric.\n\nI am also interested in learning Go and things related to it.\n\n## Use case\n\nHere is a high-level overview of the use case described in\n[Motivation](#motivation). Kubestatus2cloudwatch is used as a bridge between\nKubernetes and CloudWatch. The alarm fires if the metric falls below 1 or is\nmissing data for a certain amount of time.\n\n[![assets/use-case-example.drawio.svg](./assets/use-case-example.drawio.svg)](./assets/use-case-example.drawio.svg)\n\nKubestatus2cloudwatch caters to a specific use case and must be combined with\nother tools to be useful.\n\n## Getting started\n\nKubestatus2cloudwatch is written in Go and the code ends up in a single\nexecutable binary. There are three approaches:\n\n1. Use the provided container images hosted on GitHub Packages\n   [here](https://github.com/trallnag/kubestatus2cloudwatch/pkgs/container/kubestatus2cloudwatch).\n1. Get the binaries from the respective release artifacts\n   [here](https://github.com/trallnag/kubestatus2cloudwatch/releases).\n1. Build the binary with `go build` as usual with Go.\n\nCreate a configuration file for Kubestatus2cloudwatch. Read\n[Configuration](#configuration) for more information.\n\nThe general approach:\n\n1. Run Kubestatus2cloudwatch locally to make sure it fits your use case. Also\n   write the configuration for Kubestatus2cloudwatch before going into the\n   cluster.\n1. Setup the IAM role in AWS. This involves a trust policy that works with IRSA\n   and a policy that gives permission to the CloudWatch API.\n1. Setup Kubernetes resources to get the deployment itself to work properly.\n   This includes service account, role, role binding, and config map.\n1. Deploy Kubestatus2cloudwatch as a deployment.\n\nBefore getting Kubestatus2cloudwatch to run in the cluster, we will first run it\nlocally. This requires AWS and Kubernetes credentials to be setup.\n\nPlace the `config.yaml` you have adjusted to your requirements next to the\nbinary. Now execute the binary. You should see in the logs that\nKubestatus2cloudwatch reads the configuration, configures things, and then\nstarts to scan the targets periodically and update the CloudWatch metric. There\nshould be no errors visible. Check the metric in CloudWatch. If everything looks\nfine, you can proceed with deploying Kubestatus2cloudwatch in Kubernetes.\n\nNote that Kubestatus2cloudwatch interacts with the Kubernetes and CloudWatch\nAPIs, which requires appropriate permissions. IAM Roles for Service Accounts\n(IRSA) is expected to be available in the cluster.\n\nWe need a **IAM role** with the following **trust policy**:\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Principal\": {\n        \"Federated\": \"arn:aws:iam::${ACCOUNT_ID}:oidc-provider/${ISSUER_URL}\"\n      },\n      \"Action\": \"sts:AssumeRoleWithWebIdentity\",\n      \"Condition\": {\n        \"StringEquals\": {\n          \"${ISSUER_URL}:aud\": \"sts.amazonaws.com\",\n          \"${ISSUER_URL}:sub\": \"system:serviceaccount:${KUBE_NAMESPACE}:kubestatus2cloudwatch\"\n        }\n      }\n    }\n  ]\n}\n```\n\nThe **inline policy** should look like this:\n\n```json\n{\n  \"Version\": \"2012-10-17\",\n  \"Statement\": [\n    {\n      \"Effect\": \"Allow\",\n      \"Action\": \"cloudwatch:PutMetricData\",\n      \"Resource\": \"*\",\n      \"Condition\": {\n        \"StringEquals\": {\n          \"cloudwatch:namespace\": \"${KUBE_NAMESPACE}\"\n        }\n      }\n    }\n  ]\n}\n```\n\nWithin Kubernetes, the required **Service Account** references the IAM role:\n\n```yaml\napiVersion: v1\nkind: ServiceAccount\nmetadata:\n  namespace: \"${KUBE_NAMESPACE}\"\n  name: kubestatus2cloudwatch\n  labels:\n    app.kubernetes.io/name: kubestatus2cloudwatch\n  annotations:\n    eks.amazonaws.com/role-arn: arn:aws:iam::${ACCOUNT_ID}:role/${IAM_ROLE_NAME}\n    eks.amazonaws.com/sts-regional-endpoints: \"true\"\n```\n\nBut a **Role** is also required:\n\n```yaml\napiVersion: rbac.authorization.k8s.io/v1\nkind: Role\nmetadata:\n  namespace: \"${KUBE_NAMESPACE}\"\n  name: kubestatus2cloudwatch\n  labels:\n    app.kubernetes.io/name: kubestatus2cloudwatch\nrules:\n  - apiGroups: [apps]\n    resources: [daemonsets, statefulsets, deployments]\n    verbs: [get]\n```\n\nA **Role Binding** is used to associate the Role with the Service Account:\n\n```yaml\napiVersion: rbac.authorization.k8s.io/v1\nkind: RoleBinding\nmetadata:\n  namespace: \"${KUBE_NAMESPACE}\"\n  name: kubestatus2cloudwatch\n  labels:\n    app.kubernetes.io/name: kubestatus2cloudwatch\nroleRef:\n  apiGroup: rbac.authorization.k8s.io\n  kind: Role\n  name: kubestatus2cloudwatch\nsubjects:\n  - kind: ServiceAccount\n    name: kubestatus2cloudwatch\n    namespace: \"${KUBE_NAMESPACE}\"\n```\n\nTo provide the configuration file to Kubestatus2cloudwatch, a **Config Map** is\nused:\n\n```yaml\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  namespace: \"${KUBE_NAMESPACE}\"\n  name: kubestatus2cloudwatch\n  labels:\n    app.kubernetes.io/name: kubestatus2cloudwatch\ndata:\n  config.yaml: |\n    ...\n```\n\nNow finish it up by creating the **Deployment**:\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  namespace: \"${KUBE_NAMESPACE}\"\n  name: kubestatus2cloudwatch\n  labels:\n    app.kubernetes.io/name: kubestatus2cloudwatch\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app.kubernetes.io/name: kubestatus2cloudwatch\n  template:\n    metadata:\n      labels:\n        app.kubernetes.io/name: kubestatus2cloudwatch\n    spec:\n      containers:\n        - image: ghcr.io/trallnag/kubestatus2cloudwatch:${VERSION}\n          name: kubestatus2cloudwatch\n          volumeMounts:\n            - name: config\n              subPath: config.yaml\n              mountPath: /app/config.yaml\n              readOnly: true\n      serviceAccountName: kubestatus2cloudwatch\n      volumes:\n        - name: config\n          configMap:\n            name: kubestatus2cloudwatch\n```\n\nCheck the container logs and the CloudWatch metric to see if things work as\nexpected.\n\n## Configuration\n\nKubestatus2cloudwatch is configured with a YAML file that is called\n`config.yaml` and placed right next to binary. The app will crash during startup\nwithout a valid configuration file.\n\nA valid exemplary configuration with extensive comments as documentation can be\nfound at [`assets/config-example.yaml`](./assets/config-example.yaml). It can be\nused as a starting point. The file\n[`assets/config-minimal.yaml`](./assets/config-minimal.yaml) contains a minimal\nconfiguration.\n\nAs a supplement the corresponding JSON schema at\n[`assets/config.schema.json`](./assets/config.schema.json) can be used as well.\n\n## Project status\n\nThe project is maintained by me, [Tim](https://github.com/trallnag), and I am\ninterested in keeping it alive as I am actively using it.\n\n## Versioning\n\nThe project follows [Semantic Versioning](https://semver.org/).\n\n## Contributing\n\nContributions are welcome. Please refer to [`CONTRIBUTE.md`](./CONTRIBUTE.md).\n\n## Licensing\n\nThis work is licensed under the\n[ISC license](https://en.wikipedia.org/wiki/ISC_license). See\n[`LICENSE`](./LICENSE) for the license text.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrallnag%2Fkubestatus2cloudwatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrallnag%2Fkubestatus2cloudwatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrallnag%2Fkubestatus2cloudwatch/lists"}