Why a GKE Ingress can look fine in kubectl and still have no IP — and how that differs from EKS and ALB
If you have used EKS, HTTP Ingress usually means an Application Load Balancer. Someone installs the AWS Load Balancer Controller, you set ingressClassName: alb, and AWS creates an ALB. Certificates often come from ACM. HTTP and HTTPS are listeners on that ALB.
GKE can look the same in YAML (kind: Ingress) and behave totally differently. On GKE, external Ingress with class gce is handled by GLBC (the GCE L7 / Ingress controller). It does not create an AWS-style ALB. It creates a Google HTTP(S) load balancer: forwarding rules, a URL map, backends, and often NEGs (network endpoint groups) instead of instance groups.
Same Kubernetes API. Different cloud object. That is why copying an EKS Ingress into GKE and hoping is a bad idea. We learned that the hard way: Ingress objects applied fine, Helm said success (or timed out), and there was still no IP.
The one-line map
HTTP(S) to pods
| AWS / EKS ALB plus target groups |
GCP / GKE GCE Ingress plus URL map plus backends |
Ingress class
AWS / EKSalb — controller you install |
GCP / GKEgce — GLBC; the class must exist in the cluster |
TLS certificate
| AWS / EKS ACM ARN on the Ingress or annotations |
GCP / GKE Pre-shared Google cert name on the Ingress |
HTTPS only
| AWS / EKS No HTTP listener, or redirect 80 to 443 |
GCP / GKE Annotation so GLBC does not create HTTP port 80 |
TLS strength
| AWS / EKS ALB SSL policy |
GCP / GKE GCP SSL policy via FrontendConfig, not only an annotation |
Point at pods
| AWS / EKS Target group to instances or IPs |
GCP / GKE NEG on the Service ( ingress: true) |
Internal only
| AWS / EKS Internal ALB or NLB |
GCP / GKE Class gce-internal (regional IP) |
Nginx is a third option. A controller Deployment plus its own LoadBalancer Service can show CLASS <none> and still work. That is not GCE Ingress. Mixing them in your head is how you debug the wrong IP.
What we had
Each app Helm chart could create its own Ingress. Every CD run did helm upgrade. So:
- The load balancer was the same Google LB family, not a fresh one each time.
- SSL settings we fixed in Console vanished on the next deploy.
- Port 80 stayed up even though we only wanted HTTPS.
- Path routing (
/api,/mobile) lived in the UI chart. Two charts owning one hostname caused conflicts.
We moved hosts, certs, HTTPS-only settings, and SSL policy into a shared ingress repo. App charts only ship Deployment and Service. On the Service we kept:
cloud.google.com/neg: ‘{“ingress”: true}’
On AWS that is closer to “this Service should be a target group for the ALB.” On GKE it means “use container-native load balancing.” It is not an Ingress. Leave it on the app Service.
Failure 1: Ingress in the cluster, no address
kubectl get ingress -n app-ns showed the resource. No IP in status.loadBalancer.
On AWS, if the ALB controller is not installed, you notice quickly. On GKE the controller is usually already running, but it only binds Ingresses whose IngressClass it owns.
kubectl get ingressclass
If gce is missing, ingressClassName: gce is a dangling pointer. No VIP.
We used a Helm option like ensureGceIngressClass: true on the cluster that had no gce class. It creates:
- IngressClass named
gce - controller
k8s.io/ingress-gce(the official GCE IngressClass controller string) helm.sh/resource-policy: keepso uninstall does not delete a cluster-wide class
Do not enable that flag on a cluster where gce already exists. You will fight apply conflicts. Also do not mix gce and gce-internal on one Ingress — that is like putting internet-facing and internal scheme on the same ALB.
Failure 2: TLS policy reset after Helm
On AWS you set an SSL policy on the ALB listener. Terraform or Helm usually owns it.
On GKE we set an SSL-policy annotation on the Ingress. It looked right in Console, then fell back after the next Helm upgrade. Google’s supported way to keep the policy is a FrontendConfig, not that annotation alone. What stuck:
apiVersion: networking.gke.io/v1beta1
kind: FrontendConfig
metadata:
name: example-app-frontend
spec:
sslPolicy: example-tls-policy
Hook that FrontendConfig on the Ingress. The policy itself is a GCP resource in the same project as the load balancer. Same idea as ACM and ALB in the same account and region.
Failure 3: HTTP still listening
On AWS: no HTTP listener, or a listener that only redirects to 443.
GKE default is still to create HTTP port 80 unless you set:
kubernetes.io/ingress.allow-http: “false”
Then it is HTTPS only. There is no magic redirect on port 80 if port 80 is not there.
Failure 4: certificates
AWS: ACM cert ARN, often alb.ingress.kubernetes.io/certificate-arn.
GKE (our setup): Google-managed or pre-shared cert resource name:
ingress.gcp.kubernetes.io/pre-shared-cert: “<cert-resource-name>”
The cert must exist in that project. The hostname on the Ingress must be on the cert. dev.app.example.com is not the same as test.app.example.com. Changing a string in YAML does not create a cert.
First create of cert plus Google HTTPS LB is slow. Helm --wait can hit context deadline exceeded while the Deployment is fine. Check the Ingress IP before you blame the app. Same class of problem as waiting for an ALB to become active, except Google’s first LB is often slower.
Helm –atomic made a mess
We pulled Ingress out of the app chart. A failed upgrade rolled back to an old revision that still had an Ingress with class gce. The cluster had no IngressClass named gce, so rollback failed with: no IngressClass with the name “gce”. The first error was often just a wait timeout. Fix the class (or do not roll back onto old Ingress YAML) before you chase Helm.
How we run it now
| APP REPO Deployment, Service, NEG annotation, secrets. No Ingress. |
INGRESS REPO Hostname, cert, HTTPS-only, SSL policy, paths. |
IngressClass gce: once per cluster, if missing. App CD should not render Ingress. A leftover template that reads .Values.ingress.version when ingress is not set will fail Helm with a nil pointer. Delete the template or default the value.