I use cookies in order to optimize my website and continually improve it. By continuing to use this site, you are agreeing to the use of cookies.
You can find an Opt-Out option and more details on the Privacy Page!

Advanced kubernetes ingress

In the last post I described how to install Nginx-Ingress on a kubernetes cluster. In this post I’ll show how to setup a more complex ingress example.

Path-based Routing

The application in this example has an api-server and a separate ui server. The api-server should be accessible via /api and the frontend should be accessible via /. But they shall be available via the same domain name. Therefore ingress allows you to define path based routing. In the following part you see the ingress definition for our ui:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-ui
  namespace: application
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: application.koudingspawn.de
    http:
      paths:
      - path: /
        backend:
          serviceName: ui
          servicePort: 80

First we define that all requests on domain application.koudingspawn.de should be affected. Any request on root will be proxied to the ui service on port 80. In this example I won’t show how the service looks like, because this is not important for the ingress definition.

The second ingress definition for the api-server is also very easy:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-api
  namespace: application
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
  - host: application.koudingspawn.de
    http:
      paths:
      - path: /api
        backend:
          serviceName: api
          servicePort: 8080

There we define, that every request on path /api should be proxied to the api service on port 8080. With these two ingress definitions we have managed to forward requests to the ui by default and only if the path starts with /api requests are proxied to the apiserver.

SSL Certificate

To secure our application via tls we can add a secret to the kubernetes secret resource and reverence this into the ingress definition. This allows the nginx server to serve requests for the application also on port 443. First we need to add the certificates to the cluster:

apiVersion: v1
data:
  tls.crt: BASE64(PUBLIC_KEY)
  tls.key: BASE64(PRIVATE_KEY)
kind: Secret
metadata:
  name: application-koudingspawn-tls
  namespace: application
type: kubernetes.io/tls

In the field tls.crt we place the base64 encoded public key and in the field tls.key we place the base64 encoded private key. (e.g. cat private.pem | base64) This secret will be placed in our application namespace:

$ kubectl get secret -n application
NAME                           TYPE                                  DATA      AGE
application-koudingspawn-tls   kubernetes.io/tls                     2         28m
default-token-frgtb            kubernetes.io/service-account-token   3         1h
gitlab-com                     kubernetes.io/dockercfg               1         59m

Now we can define in the ingress definition that our nginx proxy should use tls:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-api
  namespace: application
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  tls:
  - hosts:
    - application.koudingspawn.de
    secretName: application-koudingspawn-tls
  rules:
  - host: application.koudingspawn.de
    http:
      paths:
      - path: /api
        backend:
          serviceName: api
          servicePort: 8080

So here you can see we added the tls part where we specify the path to the secretName and the hosts that are in the secret certificate. We can do this again for the ui.

Another useful step is to redirect all http traffic to https. Therefore we add the ingress resources an annotation kubernetes.io/ingress.allow-http: "false". This tells the nginx proxy to redirect http traffic to https:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-ui
  namespace: application
  annotations:
    kubernetes.io/ingress.class: "nginx"
    kubernetes.io/ingress.allow-http: "false"
spec:
  tls:
  - hosts:
    - application.koudingspawn.de
    secretName: application-koudingspawn-tls
  rules:
  - host: application.koudingspawn.de
    http:
      paths:
      - path: /
        backend:
          serviceName: ui
          servicePort: 80

Kube-Lego

The problem of the last part of our tutorial is, that we need a tls certificate. Generally tls certificates are very expensive, therefore and for more security in 2016 the Let’s Encrypt initiative has launched. There you can generate valid Certificates for free and the process of registration is very easy and well documented for nginx and apache. But what about the nginx ingress used in our kubernetes cluster?

The answer is Kube-Lego. Kube-Lego can be deployed into the kubernetes cluster and work together with the nginx ingress to generate Let’s Encrypt certificates. First we need to install the Kube-Lego namespace, configmap and deployment:

apiVersion: v1
metadata:
  name: kube-lego
  namespace: kube-lego
data:
  lego.email: "[email protected]"
  lego.url: "https://acme-staging.api.letsencrypt.org/directory"
kind: ConfigMap

In the configmap you need to specify your email address used for Let’s Encrypt registration and the Let’s encrypt API. In this case we use for testing purposes the statging api of Let’s Encrypt that generates self signed certificates that are not valid, but for a short tutorial or test it is enough to see how it work.

apiVersion: v1
kind: Namespace
metadata:
  name: kube-lego
---

apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: kube-lego
  namespace: kube-lego
spec:
  replicas: 1
  template:
    metadata:
      labels:
        app: kube-lego
    spec:
      containers:
      - name: kube-lego
        image: jetstack/kube-lego:0.1.5
        imagePullPolicy: Always
        ports:
        - containerPort: 8080
        env:
        - name: LEGO_EMAIL
          valueFrom:
            configMapKeyRef:
              name: kube-lego
              key: lego.email
        - name: LEGO_URL
          valueFrom:
            configMapKeyRef:
              name: kube-lego
              key: lego.url
        - name: LEGO_NAMESPACE
          valueFrom:
            fieldRef:
              fieldPath: metadata.namespace
        - name: LEGO_POD_IP
          valueFrom:
            fieldRef:
              fieldPath: status.podIP
        readinessProbe:
          httpGet:
            path: /healthz
            port: 8080
          initialDelaySeconds: 5
          timeoutSeconds: 1

After the components are deployed the Kube-Lego deployment watches for the annotation kubernetes.io/tls-acme: "true". If it find such an annotation on an ingress it will generate a Let’s Encrypt certificate with the hosts specified in the tls section:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-api
  namespace: application
  annotations:
    kubernetes.io/ingress.class: "nginx"
    kubernetes.io/tls-acme: "true"
    kubernetes.io/ingress.allow-http: "false"
spec:
  tls:
  - hosts:
    - application.koudingspawn.de
    secretName: application-koudingspawn-tls
  rules:
  - host: application.koudingspawn.de
    http:
      paths:
      - path: /api
        backend:
          serviceName: api
          servicePort: 8080

So in this example it means that Kube-Lego will generate a kubernetes secret called application-koudingspawn-tls with a Let’s Encrypt certificate valid for application.koudingspawn.de. You don’t need to place the secret or an empty placeholder the Kube-Lego will deploy the secret for you.

Björn Wenzel

Björn Wenzel

My name is Björn Wenzel. I’m a DevOps with interests in Kubernetes, CI/CD, Spring and NodeJS.