k3s is Kubernetes in one binary: the API server, the scheduler, the kubelet, a datastore and an ingress controller, installed by one script in under a minute. This page takes one Ubuntu machine to a cluster you drive from your laptop, with certificates issued automatically, and proves it with a hello-world behind HTTPS at .
Before you start
On each node, as root:
$swapoff -a$sed -i '/ swap / s/^/#/' /etc/fstab$timedatectl set-ntp true$ufw allow 22/tcp$ufw allow 80/tcp$ufw allow 443/tcp$ufw allow 6443/tcp$ufw allow from 10.42.0.0/16 to any$ufw allow from 10.43.0.0/16 to anyInstall the first server
$curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION= sh -s - server \$ --tls-san --write-kubeconfig-mode 644If the node stays NotReady
Give it a minute: the node is Ready once flannel and CoreDNS run. If it stays NotReady, journalctl -u k3s --no-pager | tail -50 usually names the cause: swap still on, a firewall dropping 8472/udp, or a leftover Docker or containerd install on the machine. Ubuntu cloud images with apparmor are fine; Raspberry Pi images need cgroups enabled in cmdline.txt, see the k3s docs.
Kubeconfig, kubectl and helm on the laptop
$mkdir -p ~/.kube$ssh root@ cat /etc/rancher/k3s/k3s.yaml | sed "s/127.0.0.1//" > ~/.kube/config$chmod 600 ~/.kube/config$curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"$sudo install -m 755 kubectl /usr/local/bin/kubectl$curl -fsSL https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash$kubectl get nodes --no-headers | awk '{print $2}' | sort -uReady
If kubectl says x509: certificate is valid for …
The API certificate does not include : the --tls-san flag was missing or given another address. Re-run the install command on the first node with the right --tls-san; the script is idempotent and only rewrites the unit and the certificate.
Ingress: Traefik and ServiceLB
$kubectl -n kube-system get svc traefik$kubectl -n kube-system rollout status deploy/traefikdeployment "traefik" successfully rolled out
cert-manager and a ClusterIssuer
$helm repo add jetstack https://charts.jetstack.io --force-update$helm upgrade --install cert-manager jetstack/cert-manager \$ --namespace cert-manager --create-namespace --set crds.enabled=true --wait$kubectl -n cert-manager rollout status deploy/cert-manager-webhookdeployment "cert-manager-webhook" successfully rolled out
apiVersion: cert-manager.io/v1kind: ClusterIssuermetadata: name: letsencryptspec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: privateKeySecretRef: name: letsencrypt-account-key solvers: - http01: ingress: ingressClassName: traefik$kubectl apply -f clusterissuer.yamlStorage: the local-path class
$kubectl get storageclassHello world behind HTTPS
apiVersion: apps/v1kind: Deploymentmetadata: name: hellospec: replicas: 1 selector: matchLabels: { app: hello } template: metadata: labels: { app: hello } spec: containers: - name: whoami image: traefik/whoami:v1.10 ports: - containerPort: 80---apiVersion: v1kind: Servicemetadata: name: hellospec: selector: { app: hello } ports: - port: 80 targetPort: 80apiVersion: networking.k8s.io/v1kind: Ingressmetadata: name: hello annotations: cert-manager.io/cluster-issuer: letsencryptspec: ingressClassName: traefik tls: - hosts: [""] secretName: hello-tls rules: - host: http: paths: - path: / pathType: Prefix backend: service: name: hello port: { number: 80 }$kubectl apply -f hello.yaml -f hello-ingress.yaml$kubectl get certificate hello-tls -w$kubectl wait --for=condition=Ready certificate/hello-tls --timeout=120scertificate.cert-manager.io/hello-tls condition met
$curl -sI https:// | head -1HTTP/2 200
If the certificate stays not Ready
In order of likelihood:
does not resolve to a node yet, or resolves to a private address Let's Encrypt cannot reach.dig +shortthe name from outside.- Port 80 is closed somewhere (ufw, the provider's firewall): HTTP-01 needs it, even though users only use 443.
- Rate limit hit after too many attempts:
kubectl describe ordersays so. Use the staging endpoint until the setup is right. - The
Challengeispendingwith awrong status code 404: Traefik is not serving the temporary Ingress, checkkubectl -n kube-system logs deploy/traefik.
Upgrade, uninstall
Upgrading k3s is re-running the install script with a newer version; uninstalling is one script per node.
Done
One node, driven from your laptop, an ingress on every node, certificates that issue and renew themselves, and a StorageClass for the database. The hello-world can go:
$kubectl delete -f hello-ingress.yaml -f hello.yamlThe next page deploys indicat on this cluster: namespace, secrets, PostgreSQL, the application with its Ingress at , and an autoscaler.