Setup Single-node Kubernetes Cluster on a Home Lab server using k0s

Reducing your cloud bill and make use of your PC at home

Photo by Erin Doering from Unsplash

TL;DR

This blog guides you how to setup a single-node Kubernetes cluster with k0s on a miniPC running Ubuntu 20.04 OS and install ingress-nginx and cert-manager and a sample whoami application that can be publicly accessed over the Internet.

Prerequisites

  • Broadband internet with a good download/upload bandwidth — in my case it is 500/500Mbps
  • A home lab PC, at least 2 CPU cores, 2GB of RAM, connected to the your home broadband network
  • A public IP from your internet service provider — this may incur additional cost, check with your ISP

Install Ubuntu Server 20.04 on Home Lab server

Download Ubuntu Server 20.04 ISO file from its official site.

Screenshot captured by the author from Ubuntu
Screenshot captured by the author from Ubuntu

Configure Ubuntu

Update System

Once the installation complete, log on and update the system.

apt-get update && apt-get -y upgrade

Configure IP Address

Configure your DHCP server in your router to issue a fixed IP address to your server. Try to assign an IP address that outside of your DHCP range to avoid collision. For example, your router may have DHCP range from 192.168.1.100 to 192.168.1.255. Then you may assign 192.168.1.10 to your server.

# Reboot
sudo reboot
# Show local IP address
hostname -I | awk ‘{print $1}’

Setup SSH

Image from Saddhost
ssh-copy-id yourusername@ip_address
sudo nano /etc/ssh/sshd_config
PermitRootLogin no
PasswordAuthentication no
systemctl restart sshd
ssh yourusername@ip_address

Configure SSH for Remote Access

Image by Stephen Phillips from Unsplash
ssh your_username@external_ip -p external_port
curl ipv4.icanhazip.com
Host youralias
User your_username
Port external_port
Hostname your_domain_name
ssh youralias

Unattended Upgrade

You can also configure the system to automatically upgrade and restart by following steps I wrote in this blog. Look for Setup Unattended Upgrades title.

Install k0s

Image from Morioh
curl -sSLf https://get.k0s.sh | sudo sh
$ k0s version
v0.11.0

Setup the Cluster

Building Config

Generate default config YAML file to a folder that make it accessible by root .

sudo k0s default-config > /root/k0s.yaml
  1. The email address in the cluster-issuer section.

Start the Cluster

Install the cluster from our configuration file and start the server.

sudo k0s install controller -c /root/k0s.yaml — enable-worker
sudo systemctl start k0scontroller.service
$ sudo k0s status
Version: v0.11.0
Process ID: 1472
Parent Process ID: 1
Role: controller+worker
Init System: linux-systemd
$sudo k0s kubectl get nodes
NAME STATUS ROLES AGE VERSION
urserver1 Ready <none> 6d v1.20.4-k0s1
sudo systemctl restart k0scontroller.service

Reset the Cluster

In case your cluster go wrongly and you want to resetup the cluster then use this command to reset the cluster and restart the server before repeating the steps.

sudo k0s reset
sudo reboot

Remote Access to the Cluster

To access the cluster with kubectl, you need to copy the kubeconfig file.

sudo cp /var/lib/k0s/pki/admin.conf ~/admin.conf
scp your_username@your_server:~/admin.conf ~
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: ***
server: https://your_server:port
name: local
...
$ export KUBECONFIG=~/admin.conf
$ kubectl version
Client Version: version.Info{Major:"1", Minor:"20", GitVersion:"v1.20.2", GitCommit:"faecb196815e248d3ecfb03c680a4507229c2a56", GitTreeState:"clean", BuildDate:"2021-01-13T13:28:09Z", GoVersion:"go1.15.5", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"20+", GitVersion:"v1.20.4-k0s1", GitCommit:"e87da0bd6e03ec3fea7933c4b5263d151aafd07c", GitTreeState:"clean", BuildDate:"2021-03-03T07:31:16Z", GoVersion:"go1.15.8", Compiler:"gc", Platform:"linux/amd64"}

Verify Cluster

Check if all pod are up and running. You may need to wait for a while until all pods are up.

kubectl get pods --all-namespaces
helm list --all-namespaces
kubectl get services --namespaces kube-system

Forward Ports

To make your web application accessible from the internet, you need to forward the port 80 and 443. The configuration steps should be the same as you did for SSH (port 22) and kubectl (port 6443).

Ext.Port   Int.Port   Server IP
-------- -------- ---------
12322 22 192.168.1.10
12343 6443 192.168.1.10
80 80 192.168.1.20
443 443 192.168.1.20

Test Deploying whoami

Prepare Configuration File

Create a new namespace

kubectl create namespace whoami

Deploy Application and Test

Apply the YAML file to the namespace

$ kubectl apply -f whoami.yml --namespace whomai
deployment.apps/whoami-deployment created
service/whoami-service created
ingress.networking.k8s.io/whoami-ingress created
kubectl get all --namespace whoami
Screenshot captured by the author

Configure HTTPS

To have a proper SSL certificate for HTTPS connection, you need a valid domain name. Then create a DNS A record pointing to your public IP address. The instruction is vary by DNS service provider and won’t be included here.

$ nslookup whoami.yourdomain.com 8.8.8.8
Server: 8.8.8.8
Address: 8.8.8.8#53
Non-authoritative answer:
Name: whoami.yourdomain.com
Address: xxx.xxx.xxx.xxx
$ kubectl apply -f ingress.yml --namespace whoami
ingress.networking.k8s.io/whoami-ingress configured
kubectl get pod --watch --namespace whoami
$ kubectl get certificates --namespace whoami
NAME READY SECRET AGE
whoami True whoami 2m56s
Screenshot captured by the author

Clean up

Once you finished testing, you can delete whoami.

kubectl delete all --all --namespace whoami
kubectl delete namespace/whoami

Caveats

k0s worker node is sometimes not up and running

when you start the cluster first time, sometimes the worker node is not up even after some mount of time. In this case, restarting the service is usually help.

sudo systemctl restart k0scontroller.service

Cannot use CNAME record with cert-manager

In my environment, my public IP is dynamic and mapped to a DNS using Dynamic DNS (DDNS) service. I have my own custom domain that I try to map to DDNS domain using CNAME record. But that doesn’t seem to work for cert-manager.