DEV Community

Jorge
Jorge

Posted on • Originally published at jorge.aguilera.soy on

Deploying a K3s cluster with SSL

In this post I’ll explain how to deploy a ready to prod k8s cluster (with only 1 node) using k3s and exposing a service using https with an auto-provisioned certificate using Let’s Encrypt

As a plus we’ll install Apisix, an OpenSource ApiGateway, to allow grow up our stack with more nodes+applications

Requirement

  • A Linux server with (min) 2Gb

  • A registered DNS, for example api.jorge.io

  • kubectl installed

  • helm installed

Install k3s

K3s is a light implementation of kubernetes ready to production. You can find more information at https://docs.k3s.io/installation

curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--tls-san api.jorge.io" sh -

Install Apisix

Apisix is an ApiGateway. You can use it as standalone, with docker or integrate into your cluster. It has a lot of plugins and features ready to be used in production

We’ll use a namespace for Apisix:

kubectl create namespace ingress-apisix

We’ll install a basic/typical implementation:

helm repo add apisix https://charts.apiseven.com
helm update
KUBECONFIG=/etc/rancher/k3s/k3s.yaml helm install apisix apisix/apisix --set gateway.tls.enabled=true --set ingress-controller.enabled=true --namespace ingress-apisix
Enter fullscreen mode Exit fullscreen mode

Install CertManager

CertManager is the agent able to create and provision certificates. It allows to create diferent kinds of certificates and talk with different CAs to create and install them as secrets into our cluster. For our we’ll use it to install free SSL certificates from Let’s Encrypt

helm repo add jetstack https://charts.jetstack.io --force-update
helm update
KUBECONFIG=/etc/rancher/k3s/k3s.yaml helm install cert-manager jetstack/cert-manager --namespace cert-manager --create-namespace --version v1.14.4
Enter fullscreen mode Exit fullscreen mode

Checking

We’ll check if Apisix is ready

kubectl get all -n ingress-apisix

Pods and services are up and running

The funny part

First thing (can be also last, of course, it doesnt matter) will be to redirect all plain http requests to https

traefik-https-redirect-middleware.yaml

apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
    name: redirect-https
spec:
    redirectScheme:
        scheme: https
        permanent: true
Enter fullscreen mode Exit fullscreen mode

kubectl apply -f traefik-https-redirect-middleware.yaml

Next, we’ll create the issuer:

cluster-issuer.yaml

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
    name: letsencrypt
    namespace: ingress-apisix
spec:
    acme:
        server: https://acme-v02.api.letsencrypt.org/directory
        email: jorge@edn.es
        privateKeySecretRef:
            name: letsencrypt
        solvers:
        - selector: {}
          http01:
            ingress:
            class: traefik
Enter fullscreen mode Exit fullscreen mode

kubectl apply -f cluster-issuer.yaml

We’ve created a ClusterIssuer (so all nodes and all namespaces in the cluster will be able to use it)

It will negociate with Lets Encrypt using the http01 method via traefik and will create a secret into the cluster named letsencrypt (or whatever you specify)

Now, we’ll send all trafic to Apisix

ingress.yml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
    annotations:
        spec.ingressClassName: traefik
        cert-manager.io/cluster-issuer: letsencrypt
        traefik.ingress.kubernetes.io/router.middlewares: default-redirect-https@kubernetescrd
    name: api-jorge-ingress
    namespace: ingress-apisix
spec:
    rules:
    - host: api.jorge.io
      http:
        paths:
        - pathType: Prefix
            path: /
            backend:
                service:
                    name: apisix-gateway
                    port:
                        number: 80
    tls:
    - hosts:
        - api.jorge.io
      secretName: letsencrypt-cert
Enter fullscreen mode Exit fullscreen mode

kubectl apply -f ingress.yml

Pay attention to the annotations, this is the "trick":

  • we will use the traekif ingress

  • we’re instructing to the certificate manager to use the previous issuer created

  • we’re instructing to traefil to redirect all plain http to https

  • we’re instructing we want to use a certificate from a secret (created by cert-manager)

Now all traffic will be served from our k3s cluster using https with a certificate created by Lets’Encrypt

Obviously we’ll have a 404 as Apisix doesn’t know what to do with the request

Deploying a service

We’ll deploy a typical service (httpbin)

kubectl run httpbin --image kennethreitz/httpbin --namespace ingress-apisix

(We are creating a deploying httpbin using the public image kennethreitz/httpbin)

and exposing it

kubectl expose pod httpbin -n ingress-apisix --port 80

It only remains to "link" Apisix with the new httpbin service:

routes.yaml

apiVersion: apisix.apache.org/v2
kind: ApisixRoute
metadata:
    name: httpbin
spec:
    http:
    - name: httpbin
      match:
        paths:
        - /*
        hosts:
            - api.jorge.io
      backends:
        - serviceName: httpbin
          servicePort: 8080
Enter fullscreen mode Exit fullscreen mode

We are creating an ApisixRoute to route all requests to api.jorge.io to the httpbin service.

And this is all. Right now a request from outside will follow:

  • traefik redirecting http to https

  • cert-manager negociate, creates and store the certificate

  • traefik redirect all requests to Apisix

  • Apisix determine which service to use (httpbin in our case)

  • httpbin response to the requests

Top comments (0)