Using Kubespray to Install Kubernetes
I wanted to try out kubespray to see if I can do an update all in one swoop (control plane and nodes).
Prequisites
Let’s install the prequisites (from Installing Ansible on Debian page), first let’s install ansible
:
$ echo 'deb http://ppa.launchpad.net/ansible/ansible/ubuntu focal main' | sudo tee -a /etc/apt/sources.list.d/anisble.list
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 93C4A3FD7BB9C367
$ sudo apt update
$ sudo apt install ansible
Then let’s download the source code:
$ git clone https://github.com/kubernetes-sigs/kubespray.git
$ cd kubespray
# get the version that supports your k8s version, I grabbed the latest at the time
$ git checkout v2.20.0
# or
$ git clone --branch v2.20.0 https://github.com/kubernetes-sigs/kubespray
$ cd kubespray
# let's install the python requirements
$ sudo pip3 install -U -r requirements.txt
Now let’s generate the inventory files (from Quick Start):
# Copy ``inventory/sample`` as ``inventory/mycluster``
$ cp -rfp inventory/sample inventory/home
# Update Ansible inventory file with inventory builder
$ declare -a IPS=(192.168.1.51 192.168.1.52 192.168.1.53)
$ CONFIG_FILE=inventory/home/hosts.yaml python3 contrib/inventory_builder/inventory.py ${IPS[@]}
Before running the the install I decided to enable MetalLB:
# inventory/home/group_vars/k8s_cluster/k8s-cluster.yaml
kube_proxy_strict_arp: true
# inventory/home/group_vars/k8s_cluster/addons.yml
metallb_enabled: true
metallb_speaker_enabled: true
metallb_avoid_buggy_ips: true
# this is range of IPs that is in the same subnet as the k8s nodes
metallb_ip_range:
- 192.168.1.200-192.168.1.220
Also to use cilium:
# inventory/home/group_vars/k8s_cluster/k8s-cluster.yaml
kube_network_plugin: cilium
And lastly to enable nginx-ingress:
# inventory/home/group_vars/k8s_cluster/addons.yml
ingress_nginx_enabled: true
ingress_nginx_host_network: false
ingress_publish_status_address: ""
Installing with kubespray
The install it self was pretty simple, it’s covered in Setting up your first cluster with Kubespray and Getting started:
$ ansible-playbook -i inventory/home/hosts.yaml --become --become-user=root cluster.yml
...
...
PLAY RECAP ***********************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ma : ok=766 changed=126 unreachable=0 failed=0 skipped=1226 rescued=0 ignored=5
nc : ok=486 changed=73 unreachable=0 failed=0 skipped=685 rescued=0 ignored=1
nd : ok=486 changed=73 unreachable=0 failed=0 skipped=685 rescued=0 ignored=1
Sunday 16 October 2022 21:06:30 -0600 (0:00:00.088) 0:18:29.934 ********
====================================================================
download : download_container | Download image if required ----------------------------------------------- 141.63s
network_plugin/cilium : Cilium | Wait for pods to run ------------------------------------------------------------- 93.95s
download : download_container | Download image if required ------------------------------------------------- 43.38s
download : download_file | Download item -------------------------------------------------------------------------- 37.10s
download : download_container | Download image if required-------------------------------------------------- 34.66s
kubernetes/kubeadm : Join to cluster ---------------------------------------------------------------------------------- 31.06s
download : download_container | Download image if required -------------------------------------------------- 22.52s
download : download_container | Download image if required -------------------------------------------------- 22.02s
kubernetes/control-plane : kubeadm | Initialize first master ------------------------------------------------------ 18.55s
download : download_container | Download image if required --------------------------------------------------- 17.72s
download : download_container | Download image if required --------------------------------------------------- 16.08s
download : download_container | Download image if required --------------------------------------------------- 15.96s
kubernetes-apps/ansible : Kubernetes Apps | Start Resources ---------------------------------------------------- 12.35s
download : download_container | Download image if required --------------------------------------------------- 11.96s
download : download_file | Download item ---------------------------------------------------------------------------- 11.12s
network_plugin/cilium : Cilium | Start Resources --------------------------------------------------------------------- 10.78s
kubernetes/preinstall : Update package management cache (APT) ---------------------------------------------- 10.00s
download : download_container | Download image if required ----------------------------------------------------- 9.56s
kubernetes/node : install | Copy kubelet binary from download dir ------------------------------------------------ 9.45s
download : download_container | Download image if required ------------------------------------------------------ 9.11s
After the install is finished ssh to one of the controller nodes and grab the kubeconfig
:
$ sudo cp /etc/kubernetes/admin.conf ~/.kube/config
$ sudo chown $UID:$UID ~/.kube/config
At the end here are the pods that were running on the k8s cluster:
$ k get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
ingress-nginx ingress-nginx-controller-gzs6v 1/1 Running 0 4m5s
ingress-nginx ingress-nginx-controller-z2sbc 1/1 Running 0 4m38s
ingress-nginx ingress-nginx-controller-zjwxq 1/1 Running 0 5m10s
kube-system cilium-fxx2t 1/1 Running 0 4m2s
kube-system cilium-hchk2 1/1 Running 0 4m2s
kube-system cilium-lj4jx 1/1 Running 0 4m2s
kube-system cilium-operator-57bb669bf6-7h5ff 1/1 Running 0 4m3s
kube-system cilium-operator-57bb669bf6-z4q4q 1/1 Running 0 4m3s
kube-system coredns-59d6b54d97-4jpbp 1/1 Running 0 116s
kube-system coredns-59d6b54d97-9v2br 1/1 Running 0 107s
kube-system dns-autoscaler-78676459f6-45vfs 1/1 Running 0 112s
kube-system kube-apiserver-ma 1/1 Running 1 5m32s
kube-system kube-controller-manager-ma 1/1 Running 1 5m32s
kube-system kube-proxy-5t2wl 1/1 Running 0 4m32s
kube-system kube-proxy-d4spv 1/1 Running 0 4m32s
kube-system kube-proxy-d5x9z 1/1 Running 0 4m32s
kube-system kube-scheduler-ma 1/1 Running 1 5m40s
kube-system metrics-server-65bb5dbd44-9czsz 1/1 Running 0 97s
kube-system nginx-proxy-nc 1/1 Running 0 4m34s
kube-system nginx-proxy-nd 1/1 Running 0 4m34s
kube-system nodelocaldns-m4fns 1/1 Running 0 110s
kube-system nodelocaldns-nb675 1/1 Running 0 110s
kube-system nodelocaldns-p86bk 1/1 Running 0 110s
metallb-system controller-789c9bcb6-vtlst 1/1 Running 0 88s
metallb-system speaker-p4t7v 1/1 Running 0 88s
metallb-system speaker-vhqrz 1/1 Running 0 88s
metallb-system speaker-xlgwh 1/1 Running 0 88s
Now confirm outbound traffic is working:
elatov@ma:~$ kubectl run myshell1 -it --rm --image busybox -- sh
If you don't see a command prompt, try pressing enter.
/ # ping -c 2 google.com
PING google.com (142.250.72.46): 56 data bytes
64 bytes from 142.250.72.46: seq=0 ttl=117 time=31.695 ms
64 bytes from 142.250.72.46: seq=1 ttl=117 time=31.465 ms
--- google.com ping statistics ---
2 packets transmitted, 2 packets received, 0% packet loss
round-trip min/avg/max = 31.465/31.580/31.695 ms
To perform an upgrade
Most of the instructions are laid out in Upgrades.
The safest thing to do is either modify the group_vars
file:
> grep -r kube_version *
group_vars/k8s_cluster/k8s-cluster.yml:#kube_version: v1.24.6
group_vars/k8s_cluster/k8s-cluster.yml:kube_version: v1.23.9
Or you can also pass it into as an extra variable:
$ ansible-playbook upgrade-cluster.yml -b -i inventory/home/hosts.yaml -e kube_version=v1.19.7
...
PLAY RECAP *********************************************************************************************
localhost : ok=3 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
ma : ok=752 changed=59 unreachable=0 failed=0 skipped=1526 rescued=0 ignored=1
nc : ok=478 changed=24 unreachable=0 failed=0 skipped=735 rescued=0 ignored=1
nd : ok=478 changed=24 unreachable=0 failed=0 skipped=735 rescued=0 ignored=1
Sunday 16 October 2022 21:42:15 -0600 (0:00:00.089) 0:14:45.172 ********
====================================================================
kubernetes/control-plane : kubeadm | Upgrade first master --------------------------------------------------------------- 101.72s
download : download_container | Download image if required ------------------------------------------------------------- 21.49s
download : download_container | Download image if required ------------------------------------------------------------- 16.24s
download : download_container | Download image if required ------------------------------------------------------------- 16.07s
kubernetes-apps/ansible : Kubernetes Apps | Start Resources -------------------------------------------------------------- 10.40s
kubernetes-apps/ansible : Kubernetes Apps | Start Resources -------------------------------------------------------------- 10.36s
download : download_container | Download image if required --------------------------------------------------------------- 9.44s
kubernetes-apps/ansible : Kubernetes Apps | Lay Down CoreDNS templates --------------------------------------------- 7.99s
upgrade/pre-upgrade : Drain node --------------------------------------------------------------------------------------------------- 7.46s
kubernetes-apps/ansible : Kubernetes Apps | Lay Down CoreDNS templates --------------------------------------------- 7.24s
network_plugin/cilium : Cilium | Start Resources -------------------------------------------------------------------------------- 6.95s
upgrade/pre-upgrade : Drain node --------------------------------------------------------------------------------------------------- 6.73s
network_plugin/cilium : Cilium | Create Cilium node manifests -------------------------------------------------------------- 6.67s
container-engine/validate-container-engine : Populate service facts ------------------------------------------------------- 6.46s
kubernetes-apps/metrics_server : Metrics Server | Apply manifests -------------------------------------------------------- 6.35s
etcd : reload etcd ------------------------------------------------------------------------------------------------------------------------ 6.34s
kubernetes-apps/metrics_server : Metrics Server | Apply manifests -------------------------------------------------------- 6.28s
container-engine/validate-container-engine : Populate service facts ------------------------------------------------------- 6.06s
kubernetes-apps/metrics_server : Metrics Server | Create manifests ------------------------------------------------------- 6.05s
kubernetes-apps/metrics_server : Metrics Server | Create manifests ------------------------------------------------------- 6.04s
To Reset
To reset we can follow instructions from Reset A Kubernetes Cluster Using Kubespray:
# to Reset
ansible-playbook -i inventory/home/hosts.yaml reset.yml --become --become-user=root
# to reinstall
ansible-playbook -i inventory/home/hosts.yaml cluster.yml --become --become-user=root
Exposing a simple service with an Ingress
Since I am using MetalLB
and Nginx Ingress
, first I created a service for the Ingress Controllers
of type LoadBalancer
:
apiVersion: v1
kind: Service
metadata:
labels:
app: ingress-nginx
name: ingress-nginx
namespace: ingress-nginx
spec:
type: LoadBalancer
ports:
- name: http
port: 80
protocol: TCP
targetPort: http
selector:
app.kubernetes.io/name: ingress-nginx
After creating that I saw the service created:
$ k get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx LoadBalancer 10.233.38.149 192.168.1.200 80:31742/TCP 71m
To make sure we can at least reach the LB VIP, we can use arping
on any machine within the same subnet:
> sudo arping 192.168.1.200 -c 3
ARPING 192.168.1.200 from 192.168.1.103 eno1
Unicast reply from 192.168.1.200 [00:50:56:90:30:4A] 0.813ms
Unicast reply from 192.168.1.200 [00:50:56:90:30:4A] 1.525ms
Unicast reply from 192.168.1.200 [00:50:56:90:30:4A] 0.997ms
Sent 3 probes (1 broadcast(s))
Received 3 response(s)
If you had already reached out to the host directly (via ssh
or ping
), you can check out the arp
table and
you will see which node’s MAC address which is responding (in the example below it’s 192.168.1.52
):
> arp -a | grep '4a '
? (192.168.1.200) at 00:50:56:90:30:4a [ether] on eno1
nc.kar.int (192.168.1.52) at 00:50:56:90:30:4a [ether] on eno1
Or you can also ping the VIP from another subnet:
> ping 192.168.1.200 -c 3
PING 192.168.1.200 (192.168.1.200) 56(84) bytes of data.
64 bytes from 192.168.1.200: icmp_seq=1 ttl=63 time=3.19 ms
64 bytes from 192.168.1.200: icmp_seq=2 ttl=63 time=0.431 ms
64 bytes from 192.168.1.200: icmp_seq=3 ttl=63 time=0.595 ms
--- 192.168.1.200 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 0.431/1.406/3.192/1.264 ms
Then I created an ingress:
$ cat ingress-int.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: baz-int
annotations:
kubernetes.io/ingress.class: "nginx"
spec:
rules:
- host: baz.kar.int
http:
paths:
- path: "/"
pathType: Prefix
backend:
service:
name: baz
port:
number: 6880
You will see the ingress created:
$ k get ing
NAME CLASS HOSTS ADDRESS PORTS AGE
baz-int <none> baz.kar.int 192.168.1.51,192.168.1.52,192.168.1.53 80 73s
And you will see the addresses of all the nodes. Then as as quick test let’s make sure we can reach the service:
> curl -I http://192.168.1.200 -H "Host: baz.kar.int"
HTTP/1.1 200 OK
Date: Sat, 22 Oct 2022 19:33:17 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 1633
Connection: keep-alive
Access-Control-Allow-Origin: *
Very glad that worked out.