PodConfigMapController is a k8s controller that dynamically generates and manages ConfigMaps based on the metadata of running Pods. Its behavior is configured through a Custom Resource Definition (CRD) called PodConfigMapConfig, allowing users to specify which Pods to target and what information to include in the generated ConfigMaps.
This controller helps in scenarios where you need Pod-specific information to be available as a ConfigMap, for example, to be consumed by other applications, monitoring systems, or for easier debugging and visibility into Pod metadata.
| Metrics | Config |
|---|---|
![]() |
![]() |
| eBPF | Version |
![]() |
![]() |
- Dynamic ConfigMap Generation: Automatically creates, updates, and deletes ConfigMaps based on Pod lifecycle and
PodConfigMapConfigresources. - CRD Driven Configuration: Uses the
PodConfigMapConfigCustom Resource to define behavior per namespace. - Selective Pod Targeting: Utilizes
podSelectorwithin thePodConfigMapConfigCRD to precisely target which Pods should have ConfigMaps generated for them. - Metadata Inclusion: Allows specifying which Pod labels and annotations should be included in the ConfigMap data.
- Status Reporting: The
PodConfigMapConfigresource reports itsobservedGenerationin its status, indicating if the controller has processed the latest specification. - Namespace-Scoped: Operates within specific namespaces, allowing for different configurations across a cluster.
- eBPF Integration: Attach tiny eBPF programs per Pod for syscall monitoring and L4 network filtering with Prometheus metrics export.
- Structured Logging: Comprehensive logging with klog/v2 using structured key-value pairs, configurable log levels, and JSON output support.
- Prometheus Metrics: Built-in observability with metrics for ConfigMap operations, reconciliation duration, errors, queue depth, and PCMC status.
- Input Validation: Comprehensive validation for CRD fields, ConfigMap names/data, Pod metadata, and label/annotation keys.
- Error Handling: Typed errors with context, error aggregation, proper propagation, and retry logic with exponential backoff.
- Configuration Management: Extensive configuration via environment variables for leader election, controller settings, logging, and metrics.
- Leader Election: Multi-replica deployment support with configurable leader election using Lease locks.
- Minimal Container Image: Distroless-based image with eBPF toolchain, running as non-root with read-only filesystem.
The PodConfigMapConfig CRD is central to configuring the controller's behavior within a namespace.
Purpose: It defines rules for how ConfigMaps should be generated for Pods in the same namespace as the PodConfigMapConfig resource. Multiple PodConfigMapConfig resources can exist in a namespace, each potentially targeting different sets of Pods.
Specification (spec):
labelsToInclude: (Optional) An array of strings. Each string is a Pod label key. If a Pod has any of these labels, the key-value pair will be included in the generated ConfigMap's data, prefixed withlabel_.annotationsToInclude: (Optional) An array of strings. Each string is a Pod annotation key. If a Pod has any of these annotations, the key-value pair will be included in the generated ConfigMap's data, prefixed withannotation_.podSelector: (Optional) A standardmetav1.LabelSelector(e.g.,matchLabels,matchExpressions). If specified, thisPodConfigMapConfigwill only apply to Pods within its namespace that match this selector. If omitted or nil, it applies to all Pods in the namespace (subject to otherPodConfigMapConfigresources).ebpfConfig: (Optional) eBPF program configuration for advanced monitoring and security capabilities per Pod.configMapDataTemplate: (Planned Future Enhancement) A map where keys are ConfigMap data keys and values are Go template strings. This will allow for more complex ConfigMap data generation based on Pod metadata.
Status (status):
observedGeneration: An integer representing themetadata.generationof thePodConfigMapConfigresource that the controller has successfully processed. This helps users and other tools understand if the controller is up-to-date with the latest changes to the CRD.
Example PodConfigMapConfig:
apiVersion: podconfig.example.com/v1alpha1
kind: PodConfigMapConfig
metadata:
name: app-specific-config
namespace: my-namespace
spec:
podSelector:
matchLabels:
app: my-production-app
environment: prod
labelsToInclude:
- "app"
- "version"
- "region"
annotationsToInclude:
- "buildID"
- "commitSHA"
status:
observedGeneration: 1 # Updated by the controllerExample with eBPF Configuration:
apiVersion: podconfig.example.com/v1alpha1
kind: PodConfigMapConfig
metadata:
name: ebpf-monitoring-config
namespace: my-namespace
spec:
podSelector:
matchLabels:
app: my-production-app
tier: frontend
labelsToInclude:
- "app"
- "version"
- "tier"
annotationsToInclude:
- "deployment.kubernetes.io/revision"
# eBPF configuration for advanced monitoring and security
ebpfConfig:
# Monitor system calls per pod
syscallMonitoring:
enabled: true
syscallNames: ["read", "write", "open", "close", "connect", "accept"]
# Simple L4 firewall rules
l4Firewall:
enabled: true
allowedPorts: [80, 443, 8080, 8443]
blockedPorts: [22, 23, 3389, 1433, 3306]
defaultAction: allow
# Export metrics to Prometheus
metricsExport:
enabled: true
updateInterval: "30s"
status:
observedGeneration: 1 # Updated by the controllerThe controller operates by watching three primary types of resources:
- Pods: For additions, updates, and deletions.
PodConfigMapConfigCustom Resources: For changes in configuration that define how ConfigMaps should be generated.
Reconciliation Logic:
- Pod Events: When a Pod is added or updated, the controller evaluates it against all
PodConfigMapConfigresources in the Pod's namespace.- For each
PodConfigMapConfigwhosespec.podSelectormatches the Pod (or if thepodSelectoris nil), a ConfigMap is synchronized. - The generated ConfigMap is named
pod-<pod-name>-from-<pcmc-name>-cfgto ensure uniqueness and indicate its origin. - The ConfigMap's data includes basic Pod information (
podName,namespace,nodeName,phase) and any labels/annotations specified in thePodConfigMapConfig'slabelsToIncludeandannotationsToIncludefields. - If a Pod no longer matches a
PodConfigMapConfig's selector (e.g., Pod labels changed orpodSelectorin CRD changed), the corresponding ConfigMap is deleted.
- For each
PodConfigMapConfigEvents:- When a
PodConfigMapConfigis added or updated, the controller re-evaluates all Pods in that namespace to apply the new/updated configuration. This may involve creating new ConfigMaps, updating existing ones, or deleting ConfigMaps if Pods no longer match the selector. - The controller updates the
status.observedGenerationof thePodConfigMapConfigafter successfully processing its specification. - When a
PodConfigMapConfigis deleted, the controller attempts to delete all ConfigMaps that were generated based on it.
- When a
- ConfigMap Naming: ConfigMaps are named
pod-<pod-name>-from-<pcmc-name>-cfg, linking them to both the Pod and the specificPodConfigMapConfiginstance that triggered their creation. - Owner References: Generated ConfigMaps have an OwnerReference pointing to the Pod they represent. This ensures that when a Pod is deleted, its associated ConfigMaps are automatically garbage-collected by Kubernetes.
- Labels: Generated ConfigMaps are labeled with
podconfig.example.com/generated-by-pcmc: <pcmc-name>andpodconfig.example.com/pod-uid: <pod-uid>for easier identification and potential cleanup.
The controller now supports attaching tiny eBPF programs to pods for advanced monitoring and security capabilities. This feature enables:
- Per-pod system call counting: Track read, write, open, close, connect, accept, and other syscalls
- Granular filtering: Monitor only specific syscalls of interest
- Real-time metrics: Export syscall counts to Prometheus for observability
- Port-based filtering: Allow/block specific TCP/UDP ports per pod
- Default policies: Configure default allow/block behavior
- Traffic statistics: Monitor allowed vs blocked connections
- Minimal overhead: Efficient eBPF programs with < 1µs latency impact
podconfigmap_controller_ebpf_syscall_count_total- Syscall counts per pod/PIDpodconfigmap_controller_ebpf_l4_firewall_total- L4 firewall statistics (allowed/blocked)podconfigmap_controller_ebpf_attached_programs- Number of attached eBPF programspodconfigmap_controller_ebpf_program_errors_total- eBPF program error rates
Generated ConfigMaps automatically include eBPF metadata:
data:
podName: "my-app-12345"
namespace: "production"
ebpf_enabled: "true"
ebpf_syscall_monitoring: "true"
ebpf_l4_firewall: "true"
ebpf_l4_firewall_default_action: "allow"
ebpf_metrics_export: "true"- Leader-only management: Only the leader replica manages eBPF programs
- Fast failover: < 10s recovery time (configurable lease duration)
- State reconciliation: Automatic eBPF program re-attachment on leader change
- Zero RPO: Stateless eBPF programs with no data loss during failover
- Kernel requirements: Linux kernel >= 5.4 with eBPF support
- Required capabilities:
SYS_ADMIN,NET_ADMIN,BPF - Secure by default: Non-root user, read-only filesystem, distroless base image
- eBPF verifier: Kernel-level program verification ensures safety
# Deploy with eBPF support
kubectl apply -f examples/deployment-with-ebpf.yaml
# Create eBPF monitoring configuration
kubectl apply -f examples/ebpf-config.yaml
# View metrics
kubectl port-forward svc/podconfigmap-controller-metrics 8080:8080
curl http://localhost:8080/metrics | grep ebpfFor detailed eBPF configuration, deployment requirements, and troubleshooting, see docs/EBPF_FEATURES.md.
The controller supports extensive configuration via environment variables:
LEADER_ELECTION_ENABLED- Enable/disable leader election (default: true)LEADER_ELECTION_LEASE_DURATION- Lease duration (default: 15s)LEADER_ELECTION_RENEW_DEADLINE- Renew deadline (default: 10s)LEADER_ELECTION_RETRY_PERIOD- Retry period (default: 2s)LEADER_ELECTION_LOCK_NAME- Lock name (default: podconfigmap-controller-lock)LEADER_ELECTION_LOCK_NAMESPACE- Lock namespace (default: default)
CONTROLLER_RESYNC_PERIOD- Informer resync period (default: 10m)CONTROLLER_POD_WORKERS- Number of pod workers (default: 1)CONTROLLER_PCMC_WORKERS- Number of PCMC workers (default: 1)CONTROLLER_MAX_RETRIES- Maximum retry attempts (default: 5)CONTROLLER_RECONCILIATION_TIMEOUT- Reconciliation timeout (default: 30s)
LOG_LEVEL- Log level (default: info)LOG_FORMAT- Log format (default: text)LOG_JSON_FORMAT- Enable JSON logging (default: false)DEBUG- Enable debug logging (default: false)
METRICS_ADDR- Metrics server address (default: :8080)
The controller exposes Prometheus metrics at /metrics:
Core Controller Metrics:
podconfigmap_controller_configmap_operations_total- ConfigMap operations counterpodconfigmap_controller_reconciliation_duration_seconds- Reconciliation duration histogrampodconfigmap_controller_reconciliation_errors_total- Reconciliation errors counterpodconfigmap_controller_queue_depth- Work queue depth gaugepodconfigmap_controller_pcmc_status- PCMC status gaugepodconfigmap_controller_active_configmaps- Active ConfigMaps count gauge
eBPF-Specific Metrics:
podconfigmap_controller_ebpf_syscall_count_total- Total syscalls per pod/PIDpodconfigmap_controller_ebpf_l4_firewall_total- L4 firewall statistics (allowed/blocked/tcp/udp)podconfigmap_controller_ebpf_attached_programs- Number of attached eBPF programs per podpodconfigmap_controller_ebpf_program_errors_total- eBPF program errors (attach/detach failures)
- Uses klog/v2 with structured key-value pairs
- Supports different log levels (info, warning, error, debug)
- Configurable output format (text/JSON)
- Contextual logging with operation details
- A running Kubernetes cluster (e.g., Minikube, Kind, Docker Desktop).
kubectlcommand-line tool configured to interact with your cluster.- For eBPF features: Linux kernel >= 5.4 with eBPF support enabled on cluster nodes.
-
Apply the CustomResourceDefinition (CRD):
kubectl apply -f crd/podconfigmapconfig_crd.yaml
-
Apply RBAC Rules (ClusterRole, ClusterRoleBinding, ServiceAccount): These allow the controller to access necessary resources.
kubectl apply -f manifests/rbac.yaml
(Note: Ensure the ServiceAccount name in
manifests/rbac.yamlmatches the one used in the Deployment if they are in separate files or defined differently.) -
Deploy the Controller:
kubectl apply -f manifests/deployment.yaml
(Review
manifests/deployment.yamlto ensure the image path points to your controller's image if you've built and pushed it to a custom registry.)
-
Create a
PodConfigMapConfigresource in a namespace: Save the example YAML from the CRD section above (or your own version) to a file (e.g.,my-pcmc.yaml) and apply it:kubectl apply -f my-pcmc.yaml -n my-namespace
(Ensure
my-namespaceexists or create it:kubectl create namespace my-namespace) -
Create/Label Pods: Deploy Pods in
my-namespacethat match thepodSelectordefined in yourPodConfigMapConfig. For the example above, Pods would need the labelsapp: my-production-appandenvironment: prod.Example Pod:
apiVersion: v1 kind: Pod metadata: name: my-sample-pod namespace: my-namespace labels: app: my-production-app environment: prod version: "1.2.3" region: "us-west-1" annotations: buildID: "build-12345" commitSHA: "abcdef123456" spec: containers: - name: my-container image: nginx
Apply it:
kubectl apply -f my-sample-pod.yaml -n my-namespace -
Verify ConfigMap Creation: Check for the generated ConfigMap:
kubectl get configmap -n my-namespace pod-my-sample-pod-from-app-specific-config-cfg -o yaml
You should see data derived from the Pod's metadata as configured by
app-specific-config. -
Verify
PodConfigMapConfigStatus:kubectl get podconfigmapconfig app-specific-config -n my-namespace -o yaml
Check that
status.observedGenerationmatchesmetadata.generation.
The controller is organized into several packages for better maintainability:
api/v1alpha1/- API types and CRD definitionscontroller/- Main controller logic and reconciliationpkg/ebpf/- eBPF program management and lifecyclepkg/logging/- Structured logging with klog/v2pkg/metrics/- Prometheus metrics for observabilitypkg/validation/- Input validation for CRDs and ConfigMapspkg/errors/- Structured error handling with contextpkg/config/- Configuration management with environment variablesebpf/- eBPF C programs for syscall monitoring and L4 firewallexamples/- Example configurations and deployment manifestsdocs/- Comprehensive documentation including eBPF featuresmain.go- Entry point with leader election and graceful shutdown
This project uses Go modules and includes comprehensive testing:
# Build the controller binary
make build
# or
# go build -o bin/controller main.go
# Run tests with coverage
make test
# Format code
make fmt
# Run go vet
make vet
# Build the Docker image (see Makefile or Dockerfile for details)
make docker-build IMG=<your-registry>/podconfigmap-controller:latest
# Push the Docker image
make docker-push IMG=<your-registry>/podconfigmap-controller:latestIf you modify the API types in api/v1alpha1/types.go, you might need to regenerate CRD manifests and potentially client code (if using generated clients/listers, though this controller currently uses dynamic clients and generic informers for PCMCs).
# Generate deepcopy methods for API types
make generate
# Generate CRD manifests from API types
make manifestsDeveloping robust Kubernetes controllers requires careful consideration of API design and controller mechanics. This project aims to follow best practices with drawing inspiration from idiomatic Kubernetes controller development.
specvs.status: ThePodConfigMapConfigCRD clearly separates the desired state (spec) from the observed state (status).spec: Defined by the user, detailing what ConfigMaps to create and how (e.g.,podSelector,labelsToInclude).status: Updated by the controller, reflecting what it has observed and done. A key field here isstatus.observedGeneration. Whenstatus.observedGeneration == metadata.generation, it signifies that the controller has processed the latest changes to thespec. This is crucial for users and other automated systems to understand if the controller's actions reflect the most recent configuration.
- Reusing Kubernetes Core Types: The
spec.podSelectorfield usesmetav1.LabelSelector, a standard Kubernetes API type. This promotes consistency and allows users to leverage familiar selection mechanisms. - Field Semantics: Fields like
podSelectorare optional. If not provided, the controller has a defined default behavior (applying to all Pods in the namespace, in the context of that specificPodConfigMapConfig). Clear default behaviors are essential for predictable APIs.
- Single, Well-Defined Job: This controller has a clear responsibility: to manage ConfigMaps based on Pod metadata and
PodConfigMapConfigconfigurations. It doesn't try to manage Pod deployments, services, or other unrelated concerns. Its inputs are Pods andPodConfigMapConfigs, and its primary output is the creation/management of ConfigMaps. This follows the UNIX philosophy of tools doing one thing well.
- Informer-Based Reads: The controller uses informers to watch for changes to Pods and
PodConfigMapConfigresources. This means reads (e.g., listing Pods orPodConfigMapConfigs) are typically served from an in-memory cache, which is efficient and reduces load on the Kubernetes API server. - Event-Driven Requeues: Changes to Pods or
PodConfigMapConfigs trigger reconciliation for the relevant objects. - Status Updates: After processing a
PodConfigMapConfig, itsstatus.observedGenerationis updated to reflect the generation of the spec that was acted upon.
- Simply having a
Readycondition is often not enough. TheobservedGenerationfield in thestatusofPodConfigMapConfigis important. It tells consumers whether the current status (and by extension, the ConfigMaps managed by this CRD instance) reflects the latest version of thePodConfigMapConfig'sspec. Ifcond.observedGeneration != metadata.generation, the status information might be stale because the controller hasn't yet reconciled the most recent update to the CRD.
- The controller primarily uses cached clients provided by
controller-runtime(via informers). This means that writes (e.g., creating or updating a ConfigMap) go directly to the API server, but subsequent reads might come from the cache, which might not immediately reflect the write. The controller logic is designed with this eventual consistency in mind. For example, when creating a ConfigMap, it retries on conflict.
- An ideal controller reconciles objects that are already in their desired state very quickly and without unnecessary API calls. This controller aims for this by:
- Using
observedGenerationto avoid redundant status updates forPodConfigMapConfigs. - Performing checks (e.g., comparing data) before updating an existing ConfigMap.
- Relying on cached reads for Pods and
PodConfigMapConfigs.
- Using
- The reconciliation functions (e.g.,
reconcilePod,reconcilePcmc) are designed to:- Return an error if a transient issue occurs, causing
controller-runtimeto requeue the item with backoff. - Return
nilon success or if no further action is needed for an item (e.g., a Pod doesn't match any selectors).controller-runtimehandles requeueing if event sources change.
- Return an error if a transient issue occurs, causing
- The controller uses separate workqueues for Pod events and
PodConfigMapConfigevents. This allows for independent processing and rate limiting if necessary. It's understood that updates made by the controller (e.g., to a ConfigMap or aPodConfigMapConfigstatus) can trigger further watch events and requeues, which is a natural part of the reconciliation loop. Periodic resyncs (a default behavior incontroller-runtime) also ensure that all objects are eventually reconciled, even if some watch events are missed.
Contributions are welcome! Please feel free to submit issues, and pull requests.
This project is licensed under the Apache License 2.0.



