Kubernetes made easy: Develop and deploy on a cloud cluster


Part 3: Ingress Controller

Table of Contents
Part 1: Basis
Part 2: Kubernetes
Part 3: Ingress Controller
Part 4: Design & Conclusions

Ingress Controller

You often may want to expose multiple services/websites on a single IP (either based on the subdomain or may be on the path in the URL). This could be done manually but often far better is using an Ingress Controller. They offer L7 routing as opposed to L4 LoadBalancer.

Pro:

  • You can add a subdomain or certain URLs pointing to an entirely different Service/Pod (without changing your DNS).
  • Terminates HTTPS: All services behind your Ingress will be served using TLS/HTTPS from the Internet, while each of your service within your cluster will run on HTTP.

Con:

  • Only handle HTTPS: Some Ingress Controllers support HTTP without TLS but at the moment if your service exposes non-HTTP(s) services (e.g. SSH, UDP...) you cannot have it behind an Ingress Controller.
  • Because most require TLS, you'll need a TLS certificate.

There are multiple Ingress controllers to choose from. GCE's default Ingress Controller creates a Google Cloud Global LoadBalancer (that costs about $20/month), doesn't support WebSockets at the moment, requires your services to be of type: NodePort, forces HTTPS... Instead we'll use the Nginx Ingress Controller as follows:

ingress.yml

apiVersion: v1
kind: List
items:
  - apiVersion: v1
    kind: Service
    metadata:
      name: nginx-ingress
    spec:
      type: LoadBalancer
      loadBalancerIP: 10.10.10.10  # static IP pre-allocated.
      ports:
        - port: 80
          name: http
        - port: 443
          name: https
      selector:
        k8s-app: nginx-ingress-lb
  - apiVersion: extensions/v1beta1
    kind: Deployment
    metadata:
      name: nginx-ingress-controller
    spec:
      replicas: 1
      revisionHistoryLimit: 3
      template:
        metadata:
          labels:
            k8s-app: nginx-ingress-lb
        spec:
          terminationGracePeriodSeconds: 60
          containers:
            - name: nginx-ingress-controller
              # From https://github.com/kubernetes/contrib/blob/master/ingress/controllers/nginx/rc.yaml
              image: gcr.io/google_containers/nginx-ingress-controller:0.8.3
              imagePullPolicy: Always
              args:
                - /nginx-ingress-controller
                # Ingress controller redirects to the given server for any unknown subdomain, can be any but:
                # - Should serve a HTTP/404 on /
                # - Must serve a HTTP/200 on /healthz
                - --default-backend-service=default/frontend
              # Use downward API
              env:
                - name: POD_NAME
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.name
                - name: POD_NAMESPACE
                  valueFrom:
                    fieldRef:
                      fieldPath: metadata.namespace
              ports:
                - containerPort: 80
                - containerPort: 443
              volumeMounts:  # Optional
                - name: tls-dhparam-vol
                  mountPath: /etc/nginx-ssl/dhparam
              livenessProbe:  # Optional
                httpGet:
                  path: /healthz
                  port: 10254
                  scheme: HTTP
                initialDelaySeconds: 30
                timeoutSeconds: 5
              resources:  # Optional
                requests:
                  memory: "10Mi"
                limits:
                  memory: "100Mi"
          volumes:  # Optional
            - name: tls-dhparam-vol
              secret:
                secretName: tls-dhparam
  - apiVersion: extensions/v1beta1
    kind: Ingress
    metadata:
      name: ingress
      annotations:
        # This tells to only use the Nginx Ingress Controller
        # and avoids the creation on a Global LoadBalancer on GKE.
        kubernetes.io/ingress.class: "nginx"
    spec:
      tls:
        - secretName: tls-certificate
          # List of hosts supported by this certificate:
          hosts:
            - example.com
            - foo.example.com
      rules:
        - host: example.com
          http:
            paths:
              - path: /
                backend:
                  serviceName: frontend
                  servicePort: 80
        - host: foo.example.com
          http:
            paths:
              - path: /media
                backend:
                  serviceName: my-other-service-name
                  servicePort: 80

Update the last section with whatever rules you desire.

We also need a TLS (previously called SSL) certificate. We'll generate one but by doing so most browsers will show a big warning unless you've installed locally your certificate as being trusted. What you should do if you don't have a trusted certificate is buy one or get a free one for example on StartSSL. We will also create a DH to enable Perfect Forward Secrecy and further improve security:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj '/CN=*.example.com/O=Example.com'
kubectl create secret tls tls-certificate --key tls.key --cert tls.crt
openssl dhparam -out dhparam.pem 4096
kubectl create secret generic tls-dhparam --from-file=dhparam.pem
kubectl apply -f ingress.yml

We expose to the Internet the Nginx Ingress Controller, which retrieves the Ingress rules we defined and routes accordingly. It'll use our TLS certificate and DH.

There are quite a lot more things you can do with Kubernetes, like running a command once or periodically (cron), and a lot more. We'll just wrap up with some important choices you'll face.

Next part Design & Conclusions

Parts: Previous12, 3, 4Next