Microk8s Nginx Ingress & Certbot Setup
Published on
and was updated

Today I wanted to get nginx set up on my 3 node microk8s cluster with cert-bot. If you plan on exposing anything to the public, you'll usually reach for a webserver like apache, nginx. There are more modern webservers such as Caddy or Traefik, but I chose to go with nginx.

If you plan on exposing anything to the public, this is a must. I'm on the latest stable channel of microk8s, version 1.21.

Before we get started, there are a couple things we need to do first.

NAT Port Forward Rule

You need to open up port 443 and 80 and forard them to the IP of one of your K8 nodes.

Example: ip.of.node:80 - ip.of.node:443

I use Ubiquiti networking gear. If you use the same, then you can go into Settings > Routing & Firewall > Port Forwarding and set this up. unifi-portforward

DNS Setup

I use cloudflare to manage all my DNS. I’ve set up an A record that points to my public IPv4 address. Make sure to disable Cloudflare proxy (at least for now) so that we can verify that cert-manager did in fact create the certificates.

cloudflare-dns

Now that thats out of the way, we can start to get our hands dirty with some Kubernetes deployments!

Nginx deployment

First, enable DNS and ingress for your cluster and then create the deployment yaml file

sudo microk8s enable dns ingress

nginx-service.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: webserver-depl
spec:
  selector:
    matchLabels:
      app: webserver-app
  template:
    metadata:
      labels:
        app: webserver-app
    spec:
      containers:
        - name: webserver-app
          image: nginx:1.8
---
apiVersion: v1
kind: Service
metadata:
  name: webserver-svc
spec:
  selector:
    app: webserver-app
  ports:
  - name: webserver-app
    protocol: TCP
    port: 80
    targetPort: 80

Then apply it with:

sudo microk8s kubectl apply -f

ingress deployment

An ingress in k8s exposes both HTTP and HTTPS routes from outside the cluster, to services within. Be sure to read more about kubernetes ingress objects.

ingress.yaml

apiVersion: networking.k8s.io/v1 
kind: Ingress
metadata:
  name: ingress
spec:
  rules:
  # replace with your domain
  - host: "example.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: webserver-svc
            port:
              number: 80

Apply it

sudo microk8s kubectl apply -f ingress.yaml

You should be able to see the default "welcome to nginx" splash screen when you visit your domain now. You'll get an SSL warning when visiting, but don't worry, we'll get to that!

nginx-splash

cert-manager setup

Now that nginx is routing traffic, we can set up cert-manager.

sudo microk8s kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.3.1/cert-manager.yaml

We need to create two deployments for our certificates; one for staging, and one for production.

letsencrypt-staging.yaml

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
spec:
  acme:
#change to your email
    email: [email protected]
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-staging
    solvers:
    - http01:
        ingress:
          class: public

letsencrypt-staging.yaml

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
#change to your email
    email: [email protected]
    privateKeySecretRef:
       name: letsencrypt-prod
    solvers:
    - http01:
        ingress:
          class: public

Apply both:
```bash
sudo microk8s kubectl apply -f letsencrypt-staging.yaml

sudo microk8s kubectl apply -f letsencrypt-prod.yaml

Once thats done, you need to update the ingress deployment to use the staging certificate.

ingress.yaml

apiVersion: networking.k8s.io/v1 
kind: Ingress
metadata:
  name: ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-staging"
spec:
  tls:
  - hosts:
    - example.com
    secretName: tls-secret
  rules:
  - host: "example.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: webserver-svc
            port:
              number: 80

Apply it:

sudo microk8s kubectl apply -f ingress.yaml

We can check on the status of our certificate. You'll know it worked if Ready=True. This will take a couple minutes so give it time.

If it never shows "True", then get more info on it by running: sudo microk8s kubectl describe certificate tls-secret

Finally, swap out the staging certificate for the production certficiate.

ingress.yaml

apiVersion: networking.k8s.io/v1 
kind: Ingress
metadata:
  name: ingress
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
  tls:
  - hosts:
    - example.com
    secretName: tls-secret
  rules:
  - host: "example.com"
    http:
      paths:
      - pathType: Prefix
        path: "/"
        backend:
          service:
            name: webserver-svc
            port:
              number: 80

Apply the updates:

sudo microk8s kubectl apply -f ingress.yaml`

Time to cross our fingers, we want Ready=True just like our staging certificate.

sudo microk8s kubectl get certificate

If all is well, you should now have a valid certificate when you visit your site!

Subscribe to the newsletter

Get a monthly newsletter delivered straight to your inbox! I aim to write 2 - 4 times a month, and include curated news from topics such as tech, to science. No spam, unsubscribe at any time.