← PROJECT
#kubernetes#helm#go#observability#colima

k8s-container-monitor-helm: Helm Charts for the Container Monitor

This post covers k8s-container-monitor-helm, a companion repo that packages the exporter and a private in-cluster registry as Helm charts. It picks up where the previous post left off.

The problem

The previous post covered building the exporter and running it locally. That’s enough for development. Deploying to a Kubernetes cluster adds two requirements.

First, the image needs somewhere to live that the cluster can pull from. Pushing to a public registry works, but adds an external dependency and an authentication step on every pull. An in-cluster private registry keeps pulls local and removes the dependency.

Second, config and credentials need to be injected as Kubernetes Secrets and ConfigMaps rather than environment variables set by hand.

k8s-container-monitor-helm packages both: a registry chart that deploys the image store, and a container-monitor chart that deploys the exporter. A justfile wires them together.

Two-chart layout

Two charts, two namespaces:

namespace: registry
+--------------------------+
|  registry chart          |
|  docker-registry or zot  |
|  NodePort 30500          |
+----------+---------------+
           | in-cluster image pull
           | registry.registry.svc.cluster.local:5000
           v
namespace: monitoring
+--------------------------+
|  container-monitor chart |
|  Deployment + ClusterIP  |
|  ConfigMap: config       |
|  Secret: credentials     |
+--------------------------+

The address shown uses port 5000 (docker-registry default). When using zot, the port is 5080 — update image.repository accordingly.

The registry chart deploys the image store in the registry namespace and exposes it as a NodePort on 30500 for pushing from outside the cluster. The container-monitor chart deploys the exporter in the monitoring namespace, pulling its image from the in-cluster service address registry.registry.svc.cluster.local:5000.

No cross-namespace RBAC is needed. docker-registry accepts unauthenticated pulls by default.

Registry choice

The registry chart supports two backends, selected by registry.type in values:

docker-registryzot
Imageregistry:2ghcr.io/project-zot/zot-linux-amd64
Port50005080
AuthNone by defaultConfigurable
PersistenceOptionalRecommended
OCI specPartialFull (1.1.0)
Service typeNodePortClusterIP
When to useColima, local devProduction cluster

docker-registry is the default. Switching to zot requires one values change:

registry:
  type: zot

But the port changes from 5000 to 5080 — if you switch to zot, update image.repository in the container-monitor chart to use port 5080:

image:
  repository: registry.registry.svc.cluster.local:5080/container-monitor-go
  tag: latest

Tutorial: Deploy on Colima

Prerequisites

  • Colima installed
  • Helm 3, kubectl, just, Docker

1. Start Colima with Kubernetes

just colima-start

Runs colima start --kubernetes --cpu 2 --memory 4, starting k3s inside the Colima VM.

2. Configure the insecure registry

just colima-configure
colima stop && just colima-start

This writes /etc/rancher/k3s/registries.yaml inside the Colima VM, configuring k3s to allow unauthenticated HTTP pulls from localhost:30500. Without this, image pulls fail with a TLS error — k3s rejects plain HTTP from any address unless it is explicitly listed as a mirror.

mirrors:
  "localhost:30500":
    endpoint:
      - "http://localhost:30500"

The restart is required. k3s reads registries.yaml only at startup.

3. Build the app image

just build

Runs just docker-build in the k8s-container-monitor-go directory (configurable via APP_DIR env var). Produces a local image tagged container-monitor-go:local.

4. Install the registry and push

just install-registry
just push

install-registry deploys the registry chart and waits for the pod to be ready. push tags the local image as localhost:30500/container-monitor-go:latest and pushes it through the NodePort.

5. Configure and install the monitor

Edit charts/container-monitor/values.yaml with the hosts and registries to monitor, then:

just install-monitor

Minimal config:

config:
  websites:
    hosts:
      - example.com
  artifacts:
    catalog:
      url: git/your-catalog-repo
    registry:
      prod:
        host: yourregistry.example.com

secrets:
  catalogToken: "ghp_yourtoken"
  registries:
    prod:
      user: "robot$account"
      token: "yourtoken"

Registry config keys (prod above) are uppercased to build env var names — prod becomes PROD_USER and PROD_TOKEN. Keys with dots produce invalid env var names that Kubernetes rejects. Use underscores or single words.

6. Verify

just status        # pods and services in both namespaces
just check-metrics # port-forward to :8000 and curl /metrics

check-metrics port-forwards the monitor service, curls /metrics, and prints the first 40 lines. You should see status_code, loading_time, and missing_artifacts metrics.

Moving to a real cluster

Switch to zot and enable persistence. A values-prod.yaml is included in the registry chart:

# charts/registry/values-prod.yaml
registry:
  type: zot

persistence:
  enabled: true
  size: 50Gi
  storageClass: standard

service:
  type: ClusterIP

Install the registry with the prod values:

helm upgrade --install registry charts/registry \
    --namespace registry --create-namespace \
    -f charts/registry/values-prod.yaml

Update image.repository in the container-monitor values to the cluster’s registry service address, using port 5080 for zot:

image:
  repository: registry.registry.svc.cluster.local:5080/container-monitor-go
  tag: latest

Then install the monitor:

helm upgrade --install monitor charts/container-monitor \
    --namespace monitoring --create-namespace \
    -f your-values.yaml

No template changes are needed in either chart — the only differences from the Colima setup are registry.type, persistence.enabled, and the port in image.repository.

What’s next

  • Prometheus ServiceMonitor — the monitor exposes /metrics on port 8000. A ServiceMonitor CR would let Prometheus scrape it automatically without a manual port-forward.
  • Grafana dashboard — panels for missing_artifacts (gauge by registry) and status_code (status by host) would surface the data without querying the endpoint directly.