How to Assign a Static External IP to GKE LoadBalancer Service in Kubernetes


10 views

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
  }
}