Why This Article?

Most tutorials show the happy path – but in reality, getting a Kubernetes service exposed to the internet via a subdomain with SSL (e.g. with Google Managed Certificates on GKE) can be a headache if you miss the right sequence or make changes too early.

Let’s do it right – and learn from real-world war stories.


Scenario:

  • You want to deploy a web service on GKE.

  • You want to access it via a subdomain (e.g. app.yourdomain.com).

  • You want HTTPS with a Google Managed Certificate (no Let’s Encrypt hassle).

  • You want to avoid all the common pitfalls and “it works, but not really” situations.


1. The Correct Sequence (TL;DR)

  1. (Optional, but highly recommended): Reserve a Static IP

    • gcloud compute addresses create my-static-ip --global

    • This ensures your A record and the GCP resources always point to the same IP, even if you recreate things.

  2. Create the Kubernetes Service

    • Type: LoadBalancer

    • Set the static IP (if you reserved one) via the annotation:

      yaml
      service.beta.kubernetes.io/gcp-global-load-balancer-static-ip: "my-static-ip"
    • Don’t use ClusterIP if you plan to expose the app externally!

  3. Create the Ingress Resource

    • Reference your Service by name/port.

    • Annotate for:

      • BackendConfig (for things like timeouts, health checks, etc.)

      • SSL certificate (see below)

      • Health check path (e.g., /healthz)

  4. Create the ManagedCertificate resource

    • Example:

      yaml
      apiVersion: networking.gke.io/v1
      kind: ManagedCertificate
      metadata:
      name: my-cert
      spec:
      domains:
      - app.yourdomain.com
    • Reference this cert in your Ingress via annotation:

      yaml
      networking.gke.io/managed-certificates: my-cert
  5. Update your DNS A Record

    • Once your Service/Ingress has an external IP, set your DNS A record for app.yourdomain.com to this IP.

    • Tip: This is why using a static IP is best – it never changes.

  6. Wait for DNS and SSL Propagation

    • DNS propagation can take minutes to hours.

    • The Google Managed Certificate will show Provisioning and only become Active once:

      • DNS is set up and

      • Google can validate domain ownership.


2. Common Pitfalls

  • Mixing up Service Types:
    If you start with ClusterIP and later switch to LoadBalancer, GKE may struggle to set up the necessary cloud resources (NEG, Load Balancer, etc.), or even “ghost” resources can linger.

  • Creating Ingress before LoadBalancer Service is Ready:
    If you create Ingress too soon, Google tries to wire up the backend before the Service is ready, leading to broken or stuck state.

  • DNS A Record points to the wrong IP or not yet set:
    The ManagedCertificate will never become Active unless DNS is correct and public.

  • Changing resources too quickly (impatience!):
    GCP is slow to update cloud resources. Constantly deleting/recreating (while tempting) often makes things worse (stale resources, failed cleanups, race conditions).

  • Forgetting to check kubectl get events or describe resources:
    Most real errors and their causes are only visible in the Kubernetes events or resource descriptions, not in the GCP console.


3. Real-Life Workflow (the “Don’t Get Burned” Checklist)

  1. Reserve a static IP (optional, but best).

  2. Apply Service.yaml (type: LoadBalancer), wait for EXTERNAL-IP.

  3. Apply ManagedCertificate.yaml.

  4. Apply Ingress.yaml (with all proper annotations).

  5. Update DNS A record to EXTERNAL-IP.

  6. Wait. And then wait more.

  7. Check:

    • kubectl get managedcertificate (should say Active)

    • kubectl describe ingress (should show your IP, backends)

    • kubectl get events --sort-by=.lastTimestamp

  8. Test via DNS (not just via raw IP).


4. Debugging Tips

  • Certificate stuck in “Provisioning”?

    • Check if your DNS A record is set and propagated globally.

    • Make sure the certificate domain matches your Ingress host exactly.

  • Ingress shows 502/404?

    • Check if Service endpoints are healthy.

    • Health check paths must be accessible (no auth!).

  • Everything looks “stuck”?

    • Try deleting both Ingress and Service, then recreating, but only if you’ve waited a while and checked events for errors.

    • Always check for orphaned cloud resources (static IP, BackendConfigs, NEGs) in the GCP console.


5. Final Notes

  • Order matters.
    Most issues come from steps done out-of-order, or from “fixing” something before the previous change was really ready.

  • GKE takes time.
    Most resources, especially ManagedCertificates, take several minutes to become ready.

  • Don’t panic!
    Everyone has struggled with this setup. The more you practice, the better you get at reading events/logs.


Summary Table

Step What to Do Why
Reserve Static IP (Optional) Reserve for DNS stability Avoids IP changes
Create Service Use type: LoadBalancer Exposes the app
Create Ingress Reference Service and Cert, add annots Routing/SSL setup
Create Certificate ManagedCertificate for the subdomain HTTPS
Set DNS A Record Point subdomain to IP Domain routing
Wait & Check Let GCP propagate, use kubectl to check Sanity check