Advanced Techniques and Patterns
After years of managing configuration at scale, I’ve learned that the real challenges emerge when you need governance, compliance, and automation. The basic ConfigMap and Secret patterns work for small teams, but enterprise environments require sophisticated approaches to configuration management.
The turning point in my understanding came when I had to manage configuration for 200+ microservices across multiple clusters and regions. The manual approaches that worked for 10 services became impossible at that scale.
Custom Configuration Operators
When standard Kubernetes resources aren’t enough, custom operators can automate complex configuration management tasks. I built my first configuration operator after spending too many hours manually updating configuration across environments.
Here’s a simplified version of a configuration operator I use:
// ConfigTemplate represents a configuration template
type ConfigTemplate struct {
metav1.TypeMeta `json:",inline"`
metav1.ObjectMeta `json:"metadata,omitempty"`
Spec ConfigTemplateSpec `json:"spec,omitempty"`
Status ConfigTemplateStatus `json:"status,omitempty"`
}
type ConfigTemplateSpec struct {
Template string `json:"template"`
Variables map[string]string `json:"variables"`
Targets []TargetSpec `json:"targets"`
}
type TargetSpec struct {
Namespace string `json:"namespace"`
Name string `json:"name"`
Type string `json:"type"` // ConfigMap or Secret
}
The operator watches for ConfigTemplate resources and generates ConfigMaps or Secrets based on templates:
apiVersion: config.example.com/v1
kind: ConfigTemplate
metadata:
name: database-config-template
namespace: config-system
spec:
template: |
database:
host: postgres-{{.Environment}}.internal
port: 5432
database: myapp_{{.Environment}}
ssl: {{.DatabaseSSL}}
pool_size: {{.PoolSize}}
variables:
Environment: "{{ .Namespace }}"
DatabaseSSL: "true"
PoolSize: "20"
targets:
- namespace: development
name: database-config
type: ConfigMap
- namespace: staging
name: database-config
type: ConfigMap
- namespace: production
name: database-config
type: ConfigMap
The operator processes templates and creates the appropriate resources in each target namespace. This eliminates manual configuration management across environments.
Policy-Driven Configuration
As teams grow, configuration governance becomes critical. I use Open Policy Agent (OPA) to enforce configuration policies across all environments.
Here’s a policy that ensures production ConfigMaps have required metadata:
package kubernetes.configmaps
# Deny ConfigMaps in production without required labels
deny[msg] {
input.kind == "ConfigMap"
input.metadata.namespace == "production"
required_labels := ["app", "version", "environment", "owner"]
missing_label := required_labels[_]
not input.metadata.labels[missing_label]
msg := sprintf("Production ConfigMap missing required label: %v", [missing_label])
}
# Deny ConfigMaps with sensitive data in non-encrypted form
deny[msg] {
input.kind == "ConfigMap"
input.data[key]
# Check for common sensitive patterns
sensitive_patterns := ["password", "secret", "key", "token"]
pattern := sensitive_patterns[_]
contains(lower(key), pattern)
msg := sprintf("ConfigMap contains potentially sensitive key '%v' - use Secret instead", [key])
}
# Require configuration validation annotation
deny[msg] {
input.kind == "ConfigMap"
input.metadata.namespace == "production"
not input.metadata.annotations["config.kubernetes.io/validated"]
msg := "Production ConfigMaps must have validation annotation"
}
I integrate these policies with admission controllers to prevent non-compliant configuration from being deployed:
apiVersion: config.gatekeeper.sh/v1alpha1
kind: ConstraintTemplate
metadata:
name: configmappolicy
spec:
crd:
spec:
names:
kind: ConfigMapPolicy
validation:
properties:
requiredLabels:
type: array
items:
type: string
targets:
- target: admission.k8s.gatekeeper.sh
rego: |
package configmappolicy
violation[{"msg": msg}] {
input.review.object.kind == "ConfigMap"
required := input.parameters.requiredLabels[_]
not input.review.object.metadata.labels[required]
msg := sprintf("Missing required label: %v", [required])
}
This policy framework prevents configuration mistakes before they reach production.
GitOps Configuration Management
I’ve found GitOps to be the most reliable approach for managing configuration at scale. Configuration changes go through the same review process as code changes, and deployments are automated and auditable.
My GitOps configuration structure:
config-repo/
├── environments/
│ ├── development/
│ │ ├── configmaps/
│ │ ├── secrets/
│ │ └── kustomization.yaml
│ ├── staging/
│ │ ├── configmaps/
│ │ ├── secrets/
│ │ └── kustomization.yaml
│ └── production/
│ ├── configmaps/
│ ├── secrets/
│ └── kustomization.yaml
├── base/
│ ├── configmaps/
│ ├── secrets/
│ └── kustomization.yaml
└── policies/
├── configmap-policies.rego
└── secret-policies.rego
Base configuration defines common settings:
# base/configmaps/platform-config.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: platform-config
data:
platform.yaml: |
observability:
metrics_port: 9090
log_format: json
security:
rate_limit: 1000
cors_enabled: true
Environment-specific overlays customize settings:
# environments/production/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- ../../base
patchesStrategicMerge:
- configmaps/platform-config-patch.yaml
configMapGenerator:
- name: environment-config
literals:
- environment=production
- debug=false
- log_level=warn
ArgoCD automatically syncs configuration changes from Git to Kubernetes:
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: config-production
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/company/config-repo
targetRevision: main
path: environments/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
This GitOps approach provides complete audit trails and makes configuration changes as reliable as code deployments.
Configuration Encryption and Security
Protecting sensitive configuration requires encryption both at rest and in transit. I use Sealed Secrets to encrypt configuration in Git repositories:
# Create a sealed secret from a regular secret
kubectl create secret generic database-credentials \
--from-literal=username=myapp \
--from-literal=password=supersecret \
--dry-run=client -o yaml | \
kubeseal -o yaml > database-credentials-sealed.yaml
The sealed secret can be safely stored in Git:
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: database-credentials
namespace: production
spec:
encryptedData:
username: AgBy3i4OJSWK+PiTySYZZA9rO43cGDEQAx...
password: AgAKAoiQm7xFtFqSJ8TqDE9I8tnHoRqKU...
template:
metadata:
name: database-credentials
namespace: production
type: Opaque
For even higher security, I integrate with external secret management systems:
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: production
spec:
provider:
vault:
server: "https://vault.company.com"
path: "secret"
version: "v2"
auth:
kubernetes:
mountPath: "kubernetes"
role: "production-role"
This approach keeps sensitive data out of Kubernetes entirely while still making it available to applications.
Configuration Performance Optimization
Large-scale configuration management can impact cluster performance. I’ve learned to optimize configuration for both storage and retrieval performance.
Configuration compression for large ConfigMaps:
import gzip
import base64
import yaml
def compress_configmap_data(data):
"""Compress ConfigMap data to reduce etcd storage"""
compressed = gzip.compress(data.encode('utf-8'))
return base64.b64encode(compressed).decode('utf-8')
def create_compressed_configmap(name, data):
"""Create a ConfigMap with compressed data"""
compressed_data = compress_configmap_data(yaml.dump(data))
configmap = {
'apiVersion': 'v1',
'kind': 'ConfigMap',
'metadata': {
'name': name,
'annotations': {
'config.kubernetes.io/compressed': 'true'
}
},
'data': {
'config.yaml.gz': compressed_data
}
}
return configmap
Configuration caching to reduce API server load:
type ConfigCache struct {
cache map[string]*v1.ConfigMap
mutex sync.RWMutex
ttl time.Duration
}
func (c *ConfigCache) GetConfigMap(namespace, name string) (*v1.ConfigMap, error) {
c.mutex.RLock()
key := fmt.Sprintf("%s/%s", namespace, name)
cached, exists := c.cache[key]
c.mutex.RUnlock()
if exists && time.Since(cached.CreationTimestamp.Time) < c.ttl {
return cached, nil
}
// Fetch from API server and cache
configMap, err := c.client.CoreV1().ConfigMaps(namespace).Get(
context.TODO(), name, metav1.GetOptions{})
if err != nil {
return nil, err
}
c.mutex.Lock()
c.cache[key] = configMap
c.mutex.Unlock()
return configMap, nil
}
These optimizations become important when managing thousands of ConfigMaps across large clusters.
Configuration Compliance and Auditing
Enterprise environments require comprehensive auditing of configuration changes. I use this auditing system:
apiVersion: v1
kind: ConfigMap
metadata:
name: config-audit-policy
data:
audit-policy.yaml: |
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
resources:
- group: ""
resources: ["configmaps", "secrets"]
namespaces: ["production", "staging"]
- level: RequestResponse
resources:
- group: ""
resources: ["configmaps", "secrets"]
namespaces: ["production"]
verbs: ["create", "update", "patch", "delete"]
Configuration compliance scanning:
class ConfigComplianceScanner:
def __init__(self):
self.policies = self.load_policies()
def scan_namespace(self, namespace):
"""Scan all configuration in a namespace for compliance"""
violations = []
# Scan ConfigMaps
configmaps = self.get_configmaps(namespace)
for cm in configmaps:
violations.extend(self.check_configmap_compliance(cm))
# Scan Secrets
secrets = self.get_secrets(namespace)
for secret in secrets:
violations.extend(self.check_secret_compliance(secret))
return violations
def check_configmap_compliance(self, configmap):
"""Check ConfigMap against compliance policies"""
violations = []
# Check required labels
required_labels = ["app", "version", "environment"]
for label in required_labels:
if label not in configmap.metadata.labels:
violations.append(f"Missing required label: {label}")
# Check for sensitive data
for key, value in configmap.data.items():
if self.contains_sensitive_data(key, value):
violations.append(f"Potentially sensitive data in key: {key}")
return violations
This compliance framework ensures configuration meets organizational standards and regulatory requirements.
These advanced patterns have evolved from managing configuration in large, complex environments. They provide the governance, security, and automation needed for enterprise-scale Kubernetes deployments.
Next, we’ll explore best practices and optimization techniques that tie all these concepts together into a comprehensive configuration management strategy.