K3s part 3: Traefik proxy
Traefik is a proxy service that will allow HTTP and TCP services running inside your cluster to be exposed to the public internet. It follows the standard Kubernetes Ingress API. Traefik handles automatic and transparent TLS (HTTPS) with ACME certificates (Let’s Encrypt).
Create Traefik YAML manifests
Configure the git repository directory you created in part 1 along with other config variables:
## Git directory for cluster config
FLUX_INFRA_DIR=${HOME}/git/flux-infra
## Domain name of the cluster
CLUSTER=k3s.example.com
## Traefik version
TRAEFIK_VERSION=v2.10.4
Configure Let’s Encrypt as your ACME provider. This will generate free TLS certificates.
## For staging use https://acme-staging-v02.api.letsencrypt.org/directory
## This is the production ACME server for Let's Encrypt:
ACME_SERVER=https://acme-v02.api.letsencrypt.org/directory
## This is your real email address:
ACME_EMAIL=you@example.com
To create traefik.rbac.yaml
, which creates a
ServiceAccount
and ClusterRole
for traefik:
mkdir -p ${FLUX_INFRA_DIR}/${CLUSTER}/kube-system
cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/kube-system/traefik.rbac.yaml
kind: ServiceAccount
apiVersion: v1
metadata:
name: traefik-ingress-controller
namespace: kube-system
labels:
app.kubernetes.io/name: traefik
app.kubernetes.io/instance: traefik
annotations:
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: kube-system
name: traefik-ingress-controller
rules:
- apiGroups:
- ""
resources:
- services
- endpoints
- secrets
verbs:
- get
- list
- watch
- apiGroups:
- extensions
- networking.k8s.io
resources:
- ingresses
- ingressclasses
verbs:
- get
- list
- watch
- apiGroups:
- extensions
resources:
- ingresses/status
verbs:
- update
- apiGroups:
- traefik.containo.us
resources:
- middlewares
- ingressroutes
- traefikservices
- ingressroutetcps
- ingressrouteudps
- tlsoptions
- tlsstores
verbs:
- get
- list
- watch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: traefik-ingress-controller
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: traefik-ingress-controller
subjects:
- kind: ServiceAccount
name: traefik-ingress-controller
namespace: kube-system
EOF
To create traefik.pvc.yaml
, which creates a
PersistentVolumeClaim
to request a volume to store the ACME certificates generated by Traefik:
cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/kube-system/traefik.pvc.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: traefik-data
namespace: kube-system
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 100M
storageClassName: local-path
EOF
To create traefik.yaml
, which creates the Traefik
DaemonSet:
cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/kube-system/traefik.yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik
namespace: kube-system
spec:
selector:
matchLabels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
template:
metadata:
labels:
k8s-app: traefik-ingress-lb
name: traefik-ingress-lb
spec:
containers:
- args:
- --api
- --log.level=INFO
- --api.insecure=false
- --api.dashboard=false
- --accesslog
- --global.checknewversion=true
- --entryPoints.web.address=:80
- --entryPoints.websecure.address=:443
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.websecure.http.tls.certResolver=default
- --ping=true
- --providers.kubernetescrd=true
- --providers.kubernetesingress=true
- --certificatesresolvers.default.acme.storage=/traefik-data/acme.json
- --certificatesresolvers.default.acme.tlschallenge=true
- --certificatesresolvers.default.acme.caserver=${ACME_SERVER}
- --certificatesresolvers.default.acme.email=${ACME_EMAIL}
- --entrypoints.ssh.address=:2222
image: traefik:${TRAEFIK_VERSION}
name: traefik-ingress-lb
volumeMounts:
- name: traefik-data
mountPath: /traefik-data
ports:
- containerPort: 80
hostPort: 80
name: web
- containerPort: 443
hostPort: 443
name: websecure
- containerPort: 2222
hostPort: 2222
name: ssh
securityContext:
capabilities:
add:
- NET_BIND_SERVICE
drop:
- ALL
serviceAccountName: traefik-ingress-controller
terminationGracePeriodSeconds: 60
volumes:
- name: traefik-data
persistentVolumeClaim:
claimName: traefik-data
EOF
The Traefik project distributes an http service called whoami, useful for testing that the proxy functions and that certificate generation is working. The IngressRoute connects the internal service to the outside network, through Traefik.
To create whoami.yaml
:
cat <<EOF | sed 's/@@@/`/g' > ${FLUX_INFRA_DIR}/${CLUSTER}/kube-system/whoami.yaml
apiVersion: v1
kind: Service
metadata:
name: whoami
spec:
ports:
- name: web
port: 80
protocol: TCP
selector:
app: whoami
---
apiVersion: traefik.containo.us/v1alpha1
kind: TraefikService
metadata:
name: whoami
namespace: default
spec:
weighted:
services:
- name: whoami
weight: 1
port: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: whoami
name: whoami
namespace: default
spec:
replicas: 1
selector:
matchLabels:
app: whoami
template:
metadata:
labels:
app: whoami
spec:
containers:
- image: containous/whoami
name: whoami
ports:
- containerPort: 80
name: web
---
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: whoami
namespace: default
annotations:
traefik.ingress.kubernetes.io/router.entrypoints: websecure
traefik.ingress.kubernetes.io/router.tls: "true"
spec:
entryPoints:
- websecure
routes:
- kind: Rule
match: Host(@@@whoami.${CLUSTER}@@@)
services:
- name: whoami
port: 80
tls:
certResolver: default
EOF
To create kustomization.yaml
, which lists all the resource files, including
one from the upstream Traefik distribution URL.
cat <<EOF > ${FLUX_INFRA_DIR}/${CLUSTER}/kube-system/kustomization.yaml
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://raw.githubusercontent.com/traefik/traefik/${TRAEFIK_VERSION}/docs/content/reference/dynamic-configuration/kubernetes-crd-definition-v1.yml
- traefik.rbac.yaml
- traefik.pvc.yaml
- traefik.yaml
- whoami.yaml
EOF
Apply configuration
Apply the configuration to your cluster:
kustomize build ${FLUX_INFRA_DIR}/${CLUSTER}/kube-system | kubectl apply -f -
You may see errors like this:
no matches for kind "IngressRoute" in version "traefik.containo.us/v1alpha1"
no matches for kind "TraefikService" in version "traefik.containo.us/v1alpha1"
Just run the command again:
kustomize build ${FLUX_INFRA_DIR}/${CLUSTER}/kube-system | kubectl apply -f -
The second time will resolve the dependency disorder.
Test it
You can examine the whoami service in your web browser, open https://whoami.k3s.example.com
You now know how to expose any pod to the internet, just follow the example of
whoami.yaml
, for your own services.
You can discuss this blog on Matrix (Element): #blog-rymcg-tech:enigmacurry.com
This blog is copyright EnigmaCurry and dual-licensed CC-BY-SA and MIT. The source is on github: enigmacurry/blog.rymcg.tech and PRs are welcome. ❤️