How to set up k0s Kubernetes: A quick and dirty guide

Nick Chase - November 23, 2020
- k0s
image
The k0s project is a simple and popular way to get Kubernetes up and running swiftly and smoothly.  In this quick and dirty guide, we'll give you all the background you need to get started. The Kubernetes architecture of k0s consists of a single binary that includes everything you need to run Kubernetes on any system that includes the Linux kernel.  Putting it to use is straightforward:
  1. Download the k0s binary
  2. Create a server to instantiate the Kubernetes control plane
  3. Create a Kubernetes worker
  4. Access the cluster
Of course you can add additional controllers or servers, but let's start with the very simplest version:  a single server running everything you need.

Create a single node Kubernetes cluster with k0s

The first thing we need to do is create a server that will act as the k0s controller.  The host itself doesn't have to be huge; for this guide I used an AWS t2.medium instance (2 CPUs, 4GB RAM) running Amazon Linux 2.  Just make sure that port 6443 is open so that you can contact the cluster later. Now you can install k0s with a simple one line command:
sudo curl -sSLf https://get.k0s.sh | sudo sh
Once the script downloads, all you need to do is start the server:
sudo k0s install controller --enable-worker
Wait a few moments and then start the service:
sudo k0s start
You can make sure it's running by checking its status:
sudo k0s status
Now let's access the new cluster.

Access the k0s cluster

The k0s distribution includes kubectl, so actually accessing the cluster requires no extra steps:
$ sudo k0s kubectl get nodes
NAME               STATUS   ROLES    AGE   VERSION
ip-172-31-29-164   Ready       97s   v1.20.4-k0s1
However, if you want to access the cluster from somewhere else (or if you want to use an independent install of kubectl), you're going to need the KUBECONFIG file. When you create the server, k0s creates a KUBECONFIG file for you, so copy it to your working directory and point to it:
sudo cp /var/lib/k0s/pki/admin.conf ~/admin.conf
export KUBECONFIG=~/admin.conf
Now you can access the cluster itself:
sudo k0s kubectl get namespaces
NAME              STATUS   AGE
default           Active   5m32s
kube-node-lease   Active   5m34s
kube-public       Active   5m34s
kube-system       Active   5m34s
Notice that if you look for the nodes, there is no master node:. Remember, k0s implements the control plane as naked processes.
sudo k0s kubectl get nodes
NAME             STATUS   ROLES    AGE    VERSION
ip-172-31-8-33   Ready    <none>   5m1s   v1.19.3
But what happens if we try to access the cluster from another server, using a tool such as Lens?

Accessing k0s from outside the cluster: Customizing the k0s Kubernetes cluster

Now let's look at accessing the cluster from an external server.  We can easily get the KUBECONFIG file by using the key we used to create the server to copy it to our local machine using SCP:
scp -i <SERVER_KEY> ec2-user@<SERVER_IP>:~/admin.conf .
export KUBECONFIG=admin.conf
From there, we'll want to use the public IP address of the server rather than localhost, so open the admin.conf file and edit the server address.  For example, in my case, the public IP of my server is 52.10.92.152:
apiVersion: v1
clusters:
- cluster:
    server: https://52.10.92.152:6443
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURBRENDQWVpZ0F3SUJBZ0lVRzhGakJZVVNZOFBrOWNjcTVhK3lFenNBNXAwd0RRWUpLb1pJaHZjTkFRRUwKQlFBd0dERVdNQlFHQTFVRUF4TU5hM1ZpWlhKdVpYUmxjeTFqWVRBZUZ3MHlNREV4TWpNd016TXpNREJhR...
Now if we were to test this connection, we'd see something odd.
kubectl version
Client Version: version.Info{Major:"1", Minor:"19", GitVersion:"v1.19.0", GitCommit:"e19964183377d0ec2052d1f1fa930c4d7575bd50", GitTreeState:"clean", BuildDate:"2020-08-26T14:30:33Z", GoVersion:"go1.15", Compiler:"gc", Platform:"windows/amd64"}
Unable to connect to the server: x509: certificate is valid for 127.0.0.1, 172.31.8.33, 172.31.8.33, 172.31.8.33, 10.96.0.1, not 52.10.92.152
So we're making the connection, and Kubernetes is working, but the credentials are incorrect.  To solve this problem, we need to configure k0s to include the public IP address. Note that we could have done this prior to our initial installation--usually, that will make setup easier--but we can take care of the issue now as well. To start, we can export the actual configuration file k0s will use:
sudo k0s default-config > k0s.yaml
We can then edit that file to add the public IP, and any other address at which we want to call the server:
apiVersion: k0s.k0sproject.io/v1beta1
kind: Cluster
metadata:
  name: k0s
spec:
  api:
    address: 172.31.8.33
    sans:
    - 172.31.8.33
    - 172.31.8.33
    - 52.10.92.152
    extraArgs: {}
  controllerManager:
    extraArgs: {}
  scheduler:
    extraArgs: {}
  storage:
    type: etcd
    kine: null
    etcd:
      peerAddress: 172.31.8.33
  network:
    podCIDR: 10.244.0.0/16
    serviceCIDR: 10.96.0.0/12
    provider: calico
    calico:
      mode: vxlan
      vxlanPort: 4789
      vxlanVNI: 4096
...
Next, stop the k0s service.
sudo k0s stop
Clean up the previous installation, which was made without the configuration file:
sudo k0s reset
Reinstall k0s with your new config file:
sudo k0s install controller --enable-worker -c k0s.yaml
Start the k0s service again:
sudo k0s start
If you need to modify your current configuration at some point in the future, you’re free to change the config file even while k0s is running -- but remember that you will need to restart k0s to apply the changes:
sudo k0s stop
sudo k0s start
From here, everything should Just Work. At this point, you can also access the Kubernetes cluster with Lens by importing the KUBECONFIG.

Add additional nodes to the Kubernetes cluster

Scaling the cluster is just a matter of adding additional worker nodes or control planes. To do that, you're going to need a token so the new server knows where to "phone home." To generate that token, go to the control plane:
k0s token create --role=worker
Obviously, in this case we're creating a new worker node.  You'll wind up with a really long string of text such as:
H4sIAAAAAAAC/2yV0Y7iOhKG7/speIGZYycwpxtpLyZgBwIxY8dlJ74LcYZAnBBCGtKs9t1XzcxIu9K5K1f9+n7Lsup/ybujKvvr8dzOJzf8Urj361D21/nLl8nvev4ymUwm17K/lf18Ug1Dd53/9Rf+2/vq46+vX31//m069Z+iouyH489jkQ/ll/x9qM79cfj4YvMhn0+2CRq2CV4IsJE8BkuhIkjARBxREM8ZGhY1jhIQgSBsybXqDKJ+AlFgkFPiUYV5HRmlmNnRoN9pdiqkqja+o2XLApZ+v1skhIZuu8ddlK/MSdZUCLhvuKOBRZYIZRl3dMUlVQLoVMKsirHptKs2VnUZNOOplPSilQgMGD9eOSaImkrdMYvwN5l2TJCoEm1xLw/dnxn935mEqC2JuXClFgbNNK8pU0SkdknNXplZ1mAd0y6TqTBxrXzjWZoDDmJil+DT1cKxKDsxAqAWXFHFgaAEImIRfWoTRG+fbwIgNuJUj4k2Yo/GSwzFQ5Ea21ZV3DdqJ6000rV5YxIFh41Br57Ba79MFTXSabuk5zxUZ5nG9xyGkyDTMVHfe1YrY0IcMXe4JSSiuXIKlhHOkIEyZKBSg8BxnD/Mujyc77BSjx3N+iKluVTnj3gVxBqmvvGpA1dgvXRLvYp7mYohTxnX4YjzU7RV3ut9j88986b3Ag0CMGNlas+2ji6LpvA2XpUomX2opTE2HJZlSo86XE/F8TruqHvfEZpmzYzJZjzHYOKSBlJoK/K22pQy7uNavPNH5vPU1SDXnsDFJoHDNCe4YvUbk+HhpkI+TaRI9aprdaN2GV57WetcDEWfLzOUeW871bzds1MQ5pDdWWqrzUPFWw/PRBtFW4+J/HsHVkbpHhSTsJ7tidMljQabKmN0NLNt8MOc3FWmNMlQtEjUYcz8SNnQcBMKynyC42X0zrVlvKaB8DqR11GwqHHAiA1ipWqxspQf33wAFVjkFrzpAiBRK51ZQ40XXGdTARFwEAHA4SZhfReIjEoLYjBNeR2B1vG0COtNvhIQO3HM0niqaJerlE/L5hWXZNorQne8sX2hqz6HYmYfwecffIiaBhKx4NM/98+ocGvPtsGuOA5Ek1mjDt2Ce+NHhkRrH8zFyjUK22P2MXgQ2ladTMZTty5OgnKotCbDKFJz2hM1JqvgaFD30ErdsjS7m4fd7pYCWczWi5MZEvJm2GIIslZxtjSyeAhPhfHNYuILNDttUYUV5ahsA1FqGPWK+rIRIDxbs1asi1YEpol6CKuLaSgkTbbJfSvLpR2s300zn8LeZzf5cLdd6pgO6WVP7h97sKMljoJUs7zmD4nED+1oLGp6grDok6UxQNmHQviy02tPfe9kTsa7BJtlaTHdNxneK/deoA52cL1tvegae2+UUbvereBum8IT8HaL26CRtUmVDC5GsiYmHS1klTJZjDtpr4vm9RajyN/iIGLp4WFOxlmCRrMUUxsO25KwXqRUJ83IchJhaCqyRdW3QkcO2i4FhO7xyhL14A+r3yIZWpw0fLMPVZVj5f+QaPN7N1NZ8wNHKlHEhQmwQBF47uUtP//rZTJp86acT2p0fSnO7VCOw6+Y+FX/iok/mfFUfTber8/T+7505fBlfz4P16HPu/+nvfd92Q5f/pCezfrY2vlkcW5/Hg8vXV/+LPuyLcrrfPLv/7x8Up/mvyH/gH8aP68wnOuynU9qN15n+Ovl56OyaLj8ffC+9f3x9PLfAAAA////I+m0AwcAAA==
This may seem excessive, but this is actually just a KUBECONFIG that's been BASE64-encoded. The benefit here is that you can put the worker node anywhere, as long as it can access the control plane over the network. To create the worker, instantiate a new server (if necessary) and download k0s:
sudo curl -sSLf https://get.k0s.sh | sudo sh
On your new worker host, create a text file to store the long join token you just generated. Then go ahead and install the worker with the join token:
sudo k0s install worker --token-file /path/to/token/file
sudo k0s start
Now if you were to go back to kubectl and check for nodes, you'd see the new node in your list, as in:
kubectl get nodes
NAME               STATUS   ROLES    AGE   VERSION
ip-172-31-14-157   Ready    <none>   81s   v1.19.3
ip-172-31-8-33     Ready    <none>   11h   v1.19.3
You can also increase the robustness of the cluster by creating additional controller nodes. For more details on control plane high availability, see the k0s documentation.

Where to go from here

k0s is exciting, but it’s still pretty young. Work on the project is moving quickly, and the community would very much appreciate any feedback or contributions. Meanwhile, we’d like to hear when you’re doing with k0s, and what you’d like to see us talk about, so let us know in the comments!