When working with Google Kubernetes Engine (GKE), the default LoadBalancer service automatically provisions an ephemeral external IP. However, production scenarios often require binding to a pre-existing static IP address, particularly when:
- DNS records already point to specific IPs
- Firewall rules are IP-based
- Third-party services whitelist specific IPs
The proper approach involves three GCP resources working together:
Static IP (reserved) → Forwarding Rule → Target Pool → GKE Nodes
1. Reserve a Static IP
First, ensure your static IP exists in the same region as your GKE cluster:
gcloud compute addresses create my-static-ip \
--region=us-central1 \
--project=my-project
2. Create the Service with Annotation
Modify your Service manifest to include the networking.gke.io/load-balancer-type annotation:
apiVersion: v1
kind: Service
metadata:
name: my-lb-service
annotations:
networking.gke.io/load-balancer-type: "Internal"
networking.gke.io/static-ip: "my-static-ip"
spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 80
selector:
app: my-app
3. Verify the Binding
After applying the service, check the assignment:
kubectl get service my-lb-service -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
my-lb-service LoadBalancer 10.0.12.345 203.0.113.45 80:32456/TCP 2m
Error: "Failed to ensure load balancer"
Solution: Ensure:
- The IP exists in the same region as your cluster
- You have compute.addresses.get permission
- The IP isn't already in use
For complete control, create the GCP resources manually:
# Create health check
gcloud compute health-checks create http my-health-check \
--port=80
# Create backend service
gcloud compute backend-services create my-backend-service \
--protocol=HTTP \
--health-checks=my-health-check \
--global
# Add instance groups to backend
gcloud compute backend-services add-backend my-backend-service \
--instance-group=my-ig \
--global
When creating a LoadBalancer service in GKE, Kubernetes automatically provisions an ephemeral external IP. This becomes problematic when:
- You need DNS records pointing to a specific IP
- Your organization has firewall rules tied to specific IPs
- You want predictable IPs for whitelisting purposes
Before implementation, ensure you have:
# Check existing static IPs
gcloud compute addresses list
# Reserve new static IP (if needed)
gcloud compute addresses create my-static-ip --region=us-central1
The cleanest method uses Kubernetes annotations:
apiVersion: v1
kind: Service
metadata:
name: my-lb-service
annotations:
networking.gke.io/load-balancer-type: "Internal"
networking.gke.io/internal-load-balancer-allow-global-access: "true"
spec:
loadBalancerIP: "YOUR_STATIC_IP_ADDRESS"
ports:
- port: 80
targetPort: 80
selector:
app: my-app
type: LoadBalancer
For advanced scenarios where you need to reuse existing GCP resources:
apiVersion: v1
kind: Service
metadata:
name: my-lb-service
annotations:
networking.gke.io/load-balancer-type: "External"
networking.gke.io/existing-forwarding-rule: "projects/PROJECT_ID/regions/REGION/forwardingRules/EXISTING_RULE_NAME"
spec:
ports:
- port: 80
targetPort: 80
selector:
app: my-app
type: LoadBalancer
Connection Refused Errors: Verify your backend pods are healthy and the targetPort matches your container port.
IP Not Assigned: Check IAM permissions for compute.addresses.use and ensure the IP isn't already in use.
- Always reserve regional IPs matching your cluster's region
- Use separate IPs for staging and production environments
- Monitor IP quota limits in your GCP project
For infrastructure-as-code deployments:
resource "google_compute_address" "static_ip" {
name = "my-static-ip"
region = "us-central1"
}
resource "kubernetes_service" "lb_service" {
metadata {
name = "my-lb-service"
annotations = {
"networking.gke.io/load-balancer-type" = "External"
}
}
spec {
load_balancer_ip = google_compute_address.static_ip.address
// ... rest of service spec
}
}