Many Kubernetes engineers face this dilemma - we need static IPs for LoadBalancer services, but hardcoding them in deployment manifests creates security and maintainability issues. The original YAML example shows the problem clearly:
spec:
type: LoadBalancer
loadBalancerIP: 192.0.2.42 # Hardcoded value in Git
Kubernetes doesn't natively support shell-style environment variable expansion in YAML files. This syntax won't work:
spec:
loadBalancerIP: ${SERVICE_ADDRESS} # Won't expand
Option 1: Using kustomize for Templating
Kustomize provides a clean way to inject values without additional tools:
# kustomization.yaml
resources:
- deployment.yaml
configMapGenerator:
- name: network-config
literals:
- SERVICE_ADDRESS=192.0.2.42
# Then reference in your deployment:
spec:
loadBalancerIP: $(SERVICE_ADDRESS)
Option 2: Helm Values Injection
For Helm users, this becomes straightforward:
# values.yaml
service:
address: ""
# templates/service.yaml
spec:
loadBalancerIP: {{ .Values.service.address }}
# Deploy with:
helm install myapp --set service.address=192.0.2.42
Option 3: CI/CD Pipeline Processing
Many teams use sed or envsubst in their pipelines:
# In your deployment script
export SERVICE_ADDRESS=192.0.2.42
envsubst < deployment.yaml.template > deployment.yaml
kubectl apply -f deployment.yaml
For GCP users wanting direct integration:
# Reserve the IP first
gcloud compute addresses create my-service-ip --global
# Then reference by name
annotations:
networking.gke.io/load-balancer-ip-address: my-service-ip
Remember that IP addresses in ConfigMaps are still visible in plaintext. For maximum security:
- Use secret management systems like Vault
- Restrict access to namespace-level secrets
- Rotate IPs through automation when possible
Hardcoding sensitive information like static IP addresses in Kubernetes deployment manifests poses several issues:
- Security risks when committing to version control
- Environment-specific configurations that shouldn't be baked into the deployment spec
- Reduced portability across different clusters/environments
# Problematic hardcoded example
spec:
type: LoadBalancer
loadBalancerIP: 192.168.1.100
While Kubernetes offers ConfigMaps and Secrets, they aren't directly usable for loadBalancerIP fields. Here are the limitations:
- loadBalancerIP must be specified at spec level, not as environment variables
- ConfigMap values can't be directly interpolated into the spec
- Some cloud providers require pre-provisioned IPs
Here are practical approaches to solve this problem:
1. Templating with kustomize
# kustomization.yaml
configMapGenerator:
- name: service-config
literals:
- SERVICE_ADDRESS=192.168.1.100
# deployment.yaml
spec:
type: LoadBalancer
loadBalancerIP: # Will be replaced by kustomize
Apply with:
kustomize build . | kubectl apply -f -
2. Helm Templating
# values.yaml
service:
address: "192.168.1.100"
# templates/deployment.yaml
spec:
type: LoadBalancer
loadBalancerIP: {{ .Values.service.address }}
3. CI/CD Pipeline Substitution
Many CI systems support environment variable substitution:
# deployment.yaml
spec:
type: LoadBalancer
loadBalancerIP: __SERVICE_ADDRESS__
# In your CI script
sed -i "s/__SERVICE_ADDRESS__/$SERVICE_ADDRESS/g" deployment.yaml
kubectl apply -f deployment.yaml
For Google Cloud, you can directly reference named addresses:
# First create the address
gcloud compute addresses create my-service-ip --global
# Then reference it in your YAML
spec:
type: LoadBalancer
loadBalancerIP: $(gcloud compute addresses describe my-service-ip --global --format='value(address)')
- Never commit actual IPs to source control
- Use infrastructure-as-code tools (Terraform, Pulumi) to manage IP allocation
- Consider using DNS names instead of IPs when possible
- Implement proper secret management for all sensitive configuration