Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save mau21mau/39c2bafb6cfbaa8f2a895260206aea09 to your computer and use it in GitHub Desktop.
Save mau21mau/39c2bafb6cfbaa8f2a895260206aea09 to your computer and use it in GitHub Desktop.
How to setup Nginx Ingress Controller and L7 HTTP(S) Load Balancer to work together on GKE

This steps assume we already have a Cluster created with VPC-native traffic routing enabled.

Before the need of the HTTP(S) LoadBalancer, I would just apply the manifests provided by the NGINX DOCS page for the installation of the Nginx Ingress Controller and It would create a service of type LoadBalancer which would, then, create a regional L4 LoadBalancer automatically.

But now that I need need to have Cloud Armor and WAF, the L4 Loadbalancer doesn't support it. A HTTP(S) Load Balancer is needed in order to Cloud Armor to work.

In order to have Nginx Ingress controller working with the new HTTPS(S) LoadBalancer we need to change the type: LoadBalancer on the Nginx Ingress Controller service to ClusterIP instead, and add the NEG annotation to it cloud.google.com/neg: '{"exposed_ports": {"80":{"name": "ingress-nginx-80-neg"}}}'. We do that because we don't want it to generate a L4 LoadBalancer for us. Instead, we will manually create an HTTP(S) LoadBalancer and bind it to the ingress-nginx-controller through it's NEG annotation. This binding will happen later when we set our Nginx Ingress Controller deployment as the Backend Service of our HTTPS LoadBalancer. So back to the Nginx Ingress Controller Service, it will end up like this:

apiVersion: v1
kind: Service
metadata:
  annotations:
  labels:
    helm.sh/chart: ingress-nginx-4.0.15
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/version: 1.1.1
    app.kubernetes.io/managed-by: Helm
    app.kubernetes.io/component: controller
  name: ingress-nginx-controller
  namespace: ingress-nginx
  annotations:
    cloud.google.com/neg: '{"exposed_ports": {"80":{"name": "ingress-nginx-80-neg"}}}'
spec:
  type: ClusterIP
  ipFamilyPolicy: SingleStack
  ipFamilies:
    - IPv4
  ports:
    - name: http
      port: 80
      protocol: TCP
      targetPort: http
      appProtocol: http
    - name: https
      port: 443
      protocol: TCP
      targetPort: https
      appProtocol: https
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/instance: ingress-nginx
    app.kubernetes.io/component: controller

If you install the Nginx Ingress Controller using HELM you need to overwrite the config to add the NEG annotation to the service. So the values.yaml would look something like this:

controller:
  service:
    type: ClusterIP
    annotations:
      cloud.google.com/neg: '{"exposed_ports": {"80":{"name": "ingress-nginx-80-neg"}}}'

To install it, add the ingress-nginx to the helm repository:

helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update

Then install it:

helm install -f values.yaml ingress-nginx ingress-nginx/ingress-nginx

The next steps will be:

ZONE=us-central1-a
CLUSTER_NAME=<cluster-name>
HEALTH_CHECK_NAME=nginx-ingress-controller-health-check
NETWORK_NAME=<network-name>
CERTIFICATE_NAME=self-managed-exp-<day>-<month>-<year>
NETWORK_TAGS=$(gcloud compute instances describe \
    $(kubectl get nodes -o jsonpath='{.items[0].metadata.name}') \
    --zone=$ZONE --format="value(tags.items[0])")
  1. Create an Static IP Address (skip if you already have):

Has to be Premium tier

gcloud compute addresses create ${CLUSTER_NAME}-loadbalancer-ip \
    --global \
    --ip-version IPV4
  1. Create a Firewall rule allowing the L7 HTTP(S) Load Balancer to access our cluster
gcloud compute firewall-rules create ${CLUSTER_NAME}-allow-tcp-loadbalancer \
    --allow tcp:80 \
    --source-ranges 130.211.0.0/22,35.191.0.0/16 \
    --target-tags $NETWORK_TAGS \
    --network $NETWORK_NAME
  1. Create a Health Check for our to-be-created Backend Service
gcloud compute health-checks create http ${CLUSTER_NAME}-nginx-health-check \
  --port 80 \
  --check-interval 60 \
  --unhealthy-threshold 3 \
  --healthy-threshold 1 \
  --timeout 5 \
  --request-path /healthz
  1. Create a Backend Service which is used to inform the LoadBalancer how to connect and distribute trafic to the pods.
gcloud compute backend-services create ${CLUSTER_NAME}-backend-service \
    --load-balancing-scheme=EXTERNAL \
    --timeout=3600 \
    --protocol=HTTP \
    --port-name=http \
    --health-checks=${CLUSTER_NAME}-nginx-health-check \
    --global
  1. Now it's the time we add the Nginx NEG service (the one annotated earlier) to the back end service created on the previous step:
gcloud compute backend-services add-backend ${CLUSTER_NAME}-backend-service \
  --network-endpoint-group=ingress-nginx-80-neg \
  --network-endpoint-group-zone=$ZONE \
  --balancing-mode=RATE \
  --capacity-scaler=1.0 \
  --max-rate-per-endpoint=100 \
  --global
  1. Create the load balancer itself (URL MAPS)
gcloud compute url-maps create ${CLUSTER_NAME}-loadbalancer \
    --default-service ${CLUSTER_NAME}-backend-service

  1. Create a Self Managed Certificate. (it may be a Google-managed certificate but here we will cover the self-managed).
gcloud compute ssl-certificates create $CERTIFICATE_NAME \
    --certificate=my-cert.pem \
    --private-key=my-cert-key.pem \
    --global

Finally, I will setup the Loadbalancer frontend through the Console interface which is way easier.

  1. To create the LoadBalancer front end, enter the Loadbalancer on Console and click on "Edit".

  2. The Frontend configuration tab will be incomplete. Go there image

  3. Click on "ADD FRONTEND IP AND PORT"

  4. Give it a name and select HTTPS on the field Protocol.

  5. On IP Address change from Ephemeral to your previously allocated static IP

  6. Select your certificate and mark Enable HTTP to HTTPS redirect if you want. (I did)image

  7. Save the LoadBalancer. The entering the LoadBalancer page we should see our nginx instance(s) healthy and green. In my case I've setup the Nginx Ingress Controller to have 4 replicas: image

Finally, we just need to point our domains to the LoadBalancer IP and create our Ingress file.

NOTE: The Ingress now won't handle the certificate. The certificate will now be managed by the LoadBalancer externally. So the Ingress won't have the tls definition:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/upstream-fail-timeout: "1200"
    nginx.ingress.kubernetes.io/configuration-snippet: |
      set $http_origin "${scheme}://${host}";
      more_set_headers "server: hide";
      more_set_headers "X-Content-Type-Options: nosniff";
      more_set_headers "Referrer-Policy: strict-origin";
  name: ingress-nginx
  namespace: prod

spec:
  rules:
  - host: app.mydomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: frontend
            port:
              number: 80
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment