A Kubernetes controller that watches your cluster and exposes container image inventory as Prometheus metrics.
Sentinel monitors Kubernetes workloads across labeled namespaces and tracks which container images are running in your cluster β exposed as Prometheus metrics with support for dynamic label enrichment from workload annotations and labels.
- Sentinel
Gain real-time visibility into your cluster's container image landscape. Perfect for:
- Image Inventory Tracking β Know exactly what's running, where, and when
- Security & Compliance β Monitor image versions across namespaces
- Version Drift Detection β Spot outdated or unauthorized images quickly
- Change Tracking β Track image updates with old/new tag audit trail
- Audit & Governance β Enrich metrics with custom labels like
ownerorenvironment
- Kubernetes cluster (>= v1.28)
kubectlconfigured- KIND or Minikube for local testing
- Deploy Sentinel to your cluster:
kubectl apply -f manifests/install/sentinel.yaml- Label the namespaces you want Sentinel to watch:
kubectl label namespace my-namespace sentinel.io/controlled=enabled- Access metrics:
Sentinel exposes metrics on port 9090 at /metrics:
kubectl port-forward -n kube-system svc/sentinel-metrics 9090:9090
curl localhost:9090/metricsInfo metric (Gauge, always 1) providing a full inventory of container images running in your cluster.
Labels:
| Label | Description | Example |
|---|---|---|
workload_namespace |
Kubernetes namespace | production |
workload_type |
Kind of workload | Deployment |
workload_name |
Name of the workload | api-server |
container_name |
Container within the workload | nginx |
image |
Full image string | ghcr.io/myorg/app:v1.2.3 |
image_registry |
Parsed registry | ghcr.io |
image_repository |
Parsed repository | myorg/app |
image_tag |
Parsed tag | v1.2.3 |
| dynamic labels | From extraLabels config |
owner, env, etc. |
Example output:
sentinel_container_image_info{
workload_namespace="production",
workload_type="Deployment",
workload_name="api-server",
container_name="nginx",
image="nginx:1.28.2-alpine-slim",
image_registry="docker.io",
image_repository="nginx",
image_tag="1.28.2-alpine-slim",
owner="platform-team",
env="production"
} 1
Counter that increments every time a container's image tag changes, providing an audit trail of deployments.
Labels:
| Label | Description | Example |
|---|---|---|
workload_namespace |
Kubernetes namespace | production |
workload_type |
Kind of workload | Deployment |
workload_name |
Name of the workload | api-server |
container_name |
Container within the workload | nginx |
old_image_tag |
Previous image tag | 1.28.2-alpine-slim |
new_image_tag |
New image tag | 1.29.0-alpine-slim |
Example output:
sentinel_image_changes_total{
workload_namespace="production",
workload_type="Deployment",
workload_name="api-server",
container_name="nginx",
old_image_tag="1.28.2-alpine-slim",
new_image_tag="1.29.0-alpine-slim"
} 1
Useful PromQL queries:
# Count all image changes in the last 24 hours
sum(increase(sentinel_image_changes_total[24h]))
# Alert: too many image changes in production
sentinel_image_changes_total{workload_namespace="production"} > 5
# Find containers still using :latest
sentinel_container_image_info{image_tag="latest"}
# Count containers per registry
count by (image_registry) (sentinel_container_image_info)
Sentinel can extract annotations and labels from your workloads and expose them as Prometheus metric labels. This is configured via extraLabels:
extraLabels:
- type: "annotation" # Where to look: "annotation" or "label"
key: "sentinel.io/owner" # The key to extract
timeseriesLabelName: "owner" # The Prometheus label name
- type: "label"
key: "environment"
timeseriesLabelName: "env"If a workload doesn't have the specified annotation or label, the metric label will be set to an empty string "".
This allows each organization to enrich metrics with the labels that matter to them β team ownership, cost center, environment, or any other metadata.
Sentinel can be configured via:
namespaceSelector:
"sentinel.io/controlled": "enabled"
metricsPort: "9090"
verbosity: 2
extraLabels:
- type: "annotation"
key: "sentinel.io/owner"
timeseriesLabelName: "owner"
- type: "label"
key: "environment"
timeseriesLabelName: "env"# Note: Complex types (maps, arrays) should be configured via YAML file
# Only simple values can be overridden via environment variables
export METRICSPORT=9090
export VERBOSITY=2sentinel start -v=2| Key | Type | Default | Description |
|---|---|---|---|
namespaceSelector |
map[string]string |
{"sentinel.io/controlled": "enabled"} |
Label selector for namespaces to watch |
metricsPort |
string |
"9090" |
Port for Prometheus metrics endpoint |
verbosity |
int |
0 |
Log level: 0=Info, 1=Warn, 2=Debug |
extraLabels |
[]ExtraLabel |
[] |
Additional labels to extract from workloads |
A pre-built Grafana dashboard is included in dashboard/grafana.json. Import it into your Grafana instance for:
- Overview stats β Tracked containers, workloads, image changes, and
:latesttag usage - Image inventory table β Current container images with color-coded tags (
:latestin red) - Registry distribution β Donut chart showing image count by registry
- Change tracking log β Table of all detected image changes with old β new tags
# Initialize Go module
go mod tidy
# Build the binary
go build -o sentinel# Build Docker image
docker build -t sentinel:latest .
# Load into KIND cluster
kind load docker-image sentinel:latest --name <cluster-name>
# Deploy Sentinel
kubectl apply -f manifests/install/sentinel.yaml
# Deploy demo workloads
kubectl apply -f manifests/develop/demo-app-1.yaml
kubectl apply -f manifests/develop/demo-app-2.yaml
# Verify metrics
kubectl port-forward -n kube-system svc/sentinel-metrics 9090:9090
curl -s localhost:9090/metrics | grep sentinel_Sentinel is in active development as a learning project to master Go and Kubernetes controller patterns.
Current capabilities:
- β Namespace watching with label selectors
- β Deployment monitoring with real-time informers
- β Container image parsing (registry, repository, tag)
- β Prometheus metrics server
- β Dynamic label enrichment from annotations/labels
- β Image change tracking (old tag β new tag)
- β Grafana dashboard
- π§ StatefulSet/DaemonSet/CronJob support (planned)
- π§ Init container support (planned)
- π§ Metric cleanup on workload deletion (planned)
Built with β€οΈ and Go | Report an Issue

