In this HowTo I will create a 3-node Kubernetes cluster where all nodes hold all Kubernetes roles (controlplane, worker and etcd). As Linux distribution I used Fedora 38 Server edition.
I created three VMs with about 8GB memory and an extra 50+GB /data partition for later usage.
I call my VMs in this HowTo
- vkube-1 (192.168.122.51)
- vkube-2 (192.168.122.52)
- vkube-3 (192.168.122.53)
A fourth IP (192.168.122.54) will be needed for the Kubernetes-API endpoint that will be used e.g. by kubectl.
Make sure vkube-1 can ssh to the other nodes (including itself!), this VM will be the source for setting up the cluster.
KubeKey will add all needed DNS records to
/etc/hosts
Prerequisites
The OS can be normally installed with minimal setup you initially only need:
- SSH
- conntrack
- ebtables
- ipset
- ipvsadm
Disable the firewall, selinux and swap.
Additionally we will need the following packages as we want to create a HA-cluster with multiple master-nodes.
- HAproxy
- Keepalived
And we install ourselves containerd.
Install containerd
I installed the latest available containerd via the docker-ce-stable repo.
Also some additional configurations are needed and two kernel modules must be loaded:
# Load the modules
modprobe overlay
modprobe br_netfilter
# Make sure they load after a reboot
cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
Then prepare a config file for containerd:
mv /etc/containerd/config.toml /etc/containerd/config.toml.orig
containerd config default > /etc/containerd/config.toml
Configure config.toml to use the systemd cgroup driver:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
...
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
After this start and enable the daemon:
systemctl enable containerd --now
Install HAproxy and Keepalived
Keepalived
Create a /etc/keepalived/check_apiserver.sh script (do not forget to make it executable):
#!/bin/sh
# set correct VIP!
APISERVER_VIP=192.168.100.123
APISERVER_VIP_DEST_PORT=8443
APISERVER_DEST_PORT=6443
errorExit() {
echo "*** $*" 1>&2
exit 1
}
# is the local kubernetes api-server running?
curl --silent --max-time 1 --insecure https://localhost:${APISERVER_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://localhost:${APISERVER_DEST_PORT}/"
# is the local haproy running?
pgrep -x haproxy > /dev/null || errorExit "haproxy not running"
# is the current keepalived master and the backend working?
if ip addr | grep -q ${APISERVER_VIP}; then
curl --silent --max-time 1 --insecure https://${APISERVER_VIP}:${APISERVER_VIP_DEST_PORT}/ -o /dev/null || errorExit "Error GET https://${APISERVER_VIP}:${APISERVER_VIP_DEST_PORT}/"
fi
Setup for example following /etc/keepalived/keepalived.conf:
global_defs {
notification_email {
}
router_id KUBE_API
script_user nobody
enable_script_security
}
########### VRRP CHECK SCRIPT
vrrp_script check_apiserver {
script "/etc/keepalived/check_apiserver.sh"
interval 3
#weight -2 # commented so it goes into FAULT state if backend is offline
fall 10
rise 2
}
vrrp_instance haproxy-vip {
state MASTER # set to BACKUP on other nodes
priority 200 # highest value on MASTER (e.g. set to 150 on second and 100 on third node)
interface enp2s0 # network-card interface name on node
virtual_router_id 60
advert_int 1
authentication {
auth_type PASS
auth_pass kubeapi1 # only a max of 8 character are used afaik
}
unicast_src_ip 192.168.122.51 # The IP address of this machine
unicast_peer {
192.168.122.52 # The IP addresses of peer machines
192.168.122.53
}
virtual_ipaddress {
192.168.122.54/24 # The VIP address
}
track_script {
check_apiserver
}
}
Do not forget to adapt the file on each node!
HAproxy
The /etc/haproxy/haproxy.cfg file is also kept pretty simple:
global
log /dev/log local0 warning
chroot /var/lib/haproxy
pidfile /var/run/haproxy.pid
maxconn 4000
user haproxy
group haproxy
daemon
stats socket /var/lib/haproxy/stats
defaults
log global
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
frontend kube-apiserver
bind *:8443
mode tcp
option tcplog
default_backend kube-apiserver
backend kube-apiserver
option httpchk GET /healthz
http-check expect status 200
mode tcp
option ssl-hello-chk
balance roundrobin
server k8s-master-1 192.168.122.51:6443 check
server k8s-master-2 192.168.122.52:6443 check
server k8s-master-3 192.168.122.53:6443 check
You can now enable and start HAproxy and Keepalived:
systemctl enable haproxy --now
systemctl enable keepalived --now
Install KubeKey
More details at https://github.com/kubesphere/kubekey
They supply a script that will download a tar.gz file and unpack it in the folder where you execute the command. This is simply done via executing:
mkdir kk
cd kk
curl -sfL https://get-kk.kubesphere.io | sh -
BUT – at the time of my installation, the installed version was v3.0.13. A lot of Kubernetes versions were not supported and upgrading from, for example v1.26.5 to v1.27.2 did not work, even though it was listed in the supported versions. So I suggest to build
kkon your own! It’s described here how to do it. You only need to installmakeandgolangit seems.
Cluster Setup
First we check which versions are supported by KubeKey:
./kk version --show-supported-k8s
Then we create a cluster-config and edit it for our needs. I’ve chosen v1.26.5 to later try an upgrade to a newer version to checkout how upgrades work.
./kk create config --with-kubernetes v1.26.5 --filename vkube-cluster.yaml
We will create a cluster where each node holds all roles.
The file in the end may look like this:
apiVersion: kubekey.kubesphere.io/v1alpha2
kind: Cluster
metadata:
name: vkube-cluster
spec:
hosts:
- {name: vkube-1, address: 192.168.122.51, internalAddress: 192.168.122.51, user: root, privateKeyPath: "~/.ssh/id_rsa"}
- {name: vkube-2, address: 192.168.122.52, internalAddress: 192.168.122.52, user: root, privateKeyPath: "~/.ssh/id_rsa"}
- {name: vkube-3, address: 192.168.122.53, internalAddress: 192.168.122.53, user: root, privateKeyPath: "~/.ssh/id_rsa"}
roleGroups:
etcd:
- vkube-1
- vkube-2
- vkube-3
control-plane:
- vkube-1
- vkube-2
- vkube-3
worker:
- vkube-1
- vkube-2
- vkube-3
controlPlaneEndpoint:
domain: vkube-cluster
address: "192.168.122.54"
port: 8443 # pointing to port of haproxy's "frontend kube-apiserver"
kubernetes:
version: v1.26.5
clusterName: vkube-cluster
autoRenewCerts: true
containerManager: containerd
proxyMode: iptables # I prefer this mode instead of "ipvs"
etcd:
type: kubeadm # Also here I prefer a different mode instead of the default "kubekey", which installs an extra etcd-cluster outside of the Kubernetes Cluster
network:
plugin: calico
kubePodsCIDR: 10.233.64.0/18
kubeServiceCIDR: 10.233.0.0/18
## multus support. https://github.com/k8snetworkplumbingwg/multus-cni
multusCNI:
enabled: false
registry:
privateRegistry: ""
namespaceOverride: ""
registryMirrors: []
insecureRegistries: []
addons: []
Now we initialize the cluster via the command:
./kk create cluster -f vkube-cluster.yaml
The setup will first check all prerequisites and then download all the needed stuff. It will create a file structure in the same folder where kk is placed, looking like:
kubekey
├── cni
│ ├── v1.2.0
│ │ └── amd64
│ │ └── cni-plugins-linux-amd64-v1.2.0.tgz
│ └── v3.26.1
│ └── amd64
│ └── calicoctl
├── containerd
│ └── 1.6.4
│ └── amd64
│ └── containerd-1.6.4-linux-amd64.tar.gz
├── crictl
│ └── v1.24.0
│ └── amd64
│ └── crictl-v1.24.0-linux-amd64.tar.gz
├── etcd
│ └── v3.4.13
│ └── amd64
│ └── etcd-v3.4.13-linux-amd64.tar.gz
├── helm
│ └── v3.9.0
│ └── amd64
│ └── helm
├── kube
│ └── v1.27.2
│ └── amd64
│ ├── kubeadm
│ ├── kubectl
│ └── kubelet
├── logs
│ ├── kubekey.log -> kubekey.log.20231104
│ └── kubekey.log.20231104
├── runc
│ └── v1.1.1
│ └── amd64
│ └── runc.amd64
├── vkube-1
│ └── initOS.sh
├── vkube-2
│ └── initOS.sh
└── vkube-3
└── initOS.sh
If something goes wrong and you want to restart the setup you’ll have to execute
./kk delete cluster -f vkube-cluster.yamlfirst to cleanup any mess.
Cluster Upgrade
As long as kk supports it, you can simply upgrade to a later version for example with:
./kk upgrade --with-kubernetes "v1.27.7" -f vkube-cluster.yaml
As mentioned earlier you might need to build
kkon your own to get this working 😐
Conclusion
KubeKey is quite nice to setup clusters with some exceptions that the supplied release-packages seem sometimes buggy (see described workaround with building kk on my own). Also what I quite dislike, is that all Kubernetes images, like kube-apiserver, kube-controller-manager, etc. are pushed 1:1 by KubeSphere to Docker-hub/docker.io instead of just using the registry.k8s.io. So this could be a problem for larger environments if you do not have a registry-mirror/cache.
TODO: Have a look at KubeSphere itself the UI also looks promising and better than Rancher.
Kommentare