In my last blog post I talked about setting up your own Kubernetes cluster at home. Since we don’t run our cluster on a big cloud platform, we have no access to comfortable tools like a built-in load balancer.

Time to go ashore

But, don’t despair! We can deploy our own load balancer using the Kubernetes Ingress object. It allows us to define routing rules and use a backend of our choice that uses these rules to route traffic to our cluster. And the most exiting thing: to build an Ingress controller is really simple.

But, since I don’t want to get into building your own controller yet (at least this time), we will use a proven ecosystem. Luckily, the nginx-ingress is maintained by the Kubernetes project.

cURL and apply!

Setting up the basic infrastructure is once again copying and pasting commands.

# For more information, see https://kubernetes.github.io/ingress-nginx/deploy/#mandatory-command
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/master/deploy/mandatory.yaml

Now our backend should be configured successfully. Maybe you already noticed that we did not expose anything yet, no IP to access, nothing. That is mostly because the frontend service is custom for each cluster deployment.

echo > ingress-nginx.yaml <<EOF
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
spec:
  externalTrafficPolicy: Cluster
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  - name: https
    port: 443
    protocol: TCP
    targetPort: 443
  selector:
    app: ingress-nginx
  sessionAffinity: None
  type: NodePort
  externalIPs:
    - <your-master-ip>
EOF
kubectl apply -f ingress-nginx.yaml

Remember to replace the external IP with your own master node IP. If you have your own DNS running (or this is a public cluster), I recommend to point add a A record to it.

Exposing our blog

Let’s assume you have your blog running on Kubernetes and exposed it using a Service called blog. To expose this application using your new nginx-ingress, you just need to add the routing rules to your cluster.

echo > blog-ingress.yaml <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: blog
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  backend:
    serviceName: blog
    servicePort: 80
EOF
kubectl apply -f blog-ingress.yaml

In the case, you have a domain name pointing to your Ingress, you can also use host-based routing.

echo > blog-ingress.yaml <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: blog
  annotations:
    kubernetes.io/ingress.class: nginx
spec:
  rules:
  - host: <your-blog-domain>
    http:
      paths:
      - backend:
          serviceName: blog
          servicePort: 80
        path: /
EOF
kubectl apply -f blog-ingress.yaml

Adding certificates using Let’s Encrypt

Of couse, if you publish content on the web, you want to serve it encrypted to your users. Once upon a time, using HTTPS was hard and expensive because you had to buy a certificate from a trusted company. Luckily, again, times have changed, and there is this wonderful initiative called Let’s Encrypt and a cluster component called certmanager.

To install certmanager and setup our ClusterIssuers, we have to run a few commands again.

# install certmanager
kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/master/contrib/manifests/cert-manager/with-rbac.yaml
# setup ClusterIssuer for letsencrypt-staging
echo > letsencrypt-staging.yaml <<EOF
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
    server: https://acme-staging.api.letsencrypt.org/directory
    email: <your-email-address>
    privateKeySecretRef:
      name: letsencrypt-staging
    http01: {}
EOF
kubectl apply -f letsencrypt-staging.yaml
# setup ClusterIssuer for letsencrypt-prod
echo > letsencrypt-prod.yaml <<EOF
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: <your-email-address>
    privateKeySecretRef:
      name: letsencrypt-prod
    http01: {}
EOF
kubectl apply -f letsencrypt-prod.yaml

Remember to replace <your-email-address> with your own email. When you want to test your deployments, you can use letsencrypt-staging as a test issuer to check out if things work out as expected (because you do not have to deal with rate limits and such). Of couse, when you have a stable version running you should move to use letsencrypt-prod because the staging issuer is not trusted by default.

We finally have to update our Ingress, so we can test if our auto-encryption works well!

echo > blog-ingress.yaml <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: blog
  annotations:
    kubernetes.io/ingress.class: nginx
    certmanager.k8s.io/cluster-issuer: letsencrypt-staging
spec:
  tls:
  - hosts:
    - <your-blog-domain>
    secretName: blog-tls
  rules:
  - host: <your-blog-domain>
    http:
      paths:
      - backend:
          serviceName: blog
          servicePort: 80
        path: /
EOF
kubectl apply -f blog-ingress.yaml

Finally, our blog should be exposed using HTTPS! Remember to replace letsencrypt-staging with letsencrypt-prod when you are going public.