This document is meant to collect information from various sources to serve as a quick and dirty reminder to myself before ending up on lisa.dev
$ k get node -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
kube2 Ready <none> 2y338d v1.22.8 192.168.86.11 <none> Ubuntu 18.04.6 LTS 5.1.0-rockchip64 containerd://1.5.5
kube3 Ready <none> 2y338d v1.22.8 192.168.86.12 <none> Ubuntu 18.04.6 LTS 5.1.0-rockchip64 containerd://1.5.5
kube4 Ready control-plane 8m45s v1.24.1 192.168.86.13 <none> Debian GNU/Linux 11 (bullseye) 5.15.32-v8+ containerd://1.4.13
kubemaster Ready control-plane,master 2y338d v1.23.7 192.168.86.10 <none> Ubuntu 18.04.6 LTS 5.1.0-rockchip64 containerd://1.5.5
- A single-control-plane ("master") node cluster
- Root/privileged access
- DNS access
This guide is not targeted at any cloud provider, your mileage may vary.
(Note: This section is based on upstream's ha-considerations)
In your DNS provider, add a new A
(and/or AAAA
) record for your cluster's highly available endpoint. Ex
kube-api.example.com A 192.168.86.10
kube-api.example.com A 192.168.86.13
(Note: This section is based on upstream's ha-considerations)
This will configure haproxy to proxy port 8443/tcp
to 6443/tcp
.
Create /etc/haproxy/haproxy.cfg
:
# /etc/haproxy/haproxy.cfg
#---------------------------------------------------------------------
# Global settings
#---------------------------------------------------------------------
global
log /dev/log local0
log /dev/log local1 notice
daemon
#---------------------------------------------------------------------
# common defaults that all the 'listen' and 'backend' sections will
# use if not designated in their block
#---------------------------------------------------------------------
defaults
mode http
log global
option httplog
option dontlognull
option http-server-close
option forwardfor except 127.0.0.0/8
option redispatch
retries 1
timeout http-request 10s
timeout queue 20s
timeout connect 5s
timeout client 20s
timeout server 20s
timeout http-keep-alive 10s
timeout check 10s
#---------------------------------------------------------------------
# apiserver frontend which proxys to the control plane nodes
#---------------------------------------------------------------------
frontend apiserver
bind *:8443
mode tcp
option tcplog
default_backend apiserver
#---------------------------------------------------------------------
# round robin balancing for apiserver
#---------------------------------------------------------------------
backend apiserver
option httpchk GET /healthz
http-check expect status 200
mode tcp
option ssl-hello-chk
balance roundrobin
server kubemaster 192.168.86.10:6443 check
server kube4 192.168.86.13:6443 check
On each control-plane node, create /etc/kubernetes/manifests/haproxy.yaml
:
apiVersion: v1
kind: Pod
metadata:
name: haproxy
namespace: kube-system
spec:
containers:
- image: haproxy:2.1.4
name: haproxy
livenessProbe:
failureThreshold: 8
httpGet:
host: localhost
path: /healthz
port: 6443
scheme: HTTPS
volumeMounts:
- mountPath: /usr/local/etc/haproxy/haproxy.cfg
name: haproxyconf
readOnly: true
hostNetwork: true
volumes:
- hostPath:
path: /etc/haproxy/haproxy.cfg
type: FileOrCreate
name: haproxyconf
status: {}
Ensure your kube-system/configmaps/kubeadm-config
ConfigMap has a controlPlaneEndpoint
for your DNS setup: ex controlPlaneEndpoint: kube-api.example.com:8443
. While you're in there, add a apiServer.certSANs
list with the same controlPlaneEndpoint
name (sans port) IP addresses of your control-plane nodes in a list.
Use kubeadm to renew your certs:
rm -f /etc/kubernetes/pki/apiserver.{crt,key}
kubeadm certs renew all
kubeadm init phase upload-certs --upload-certs
Take note of the key from the previous kubeadm init phase upload-certs --upload-certs
output.
Restart kube-apiserver
, kube-controller-manager
, kube-scheduler
(and haproxy
for good measure) pods:
kubectl -n kube-system delete pod/kube-apiserver-kubemaster pod/kube-controller-manager-kubemaster pod/kube-scheduler-kubemaster pod/haproxy-kubemaster
kubeadm token create --certificate-key kube4 --print-join-command --description "join kube4"
kubeadm join 192.168.86.10:6443 --token r36anl.wdeqvrvdpervn6cq --discovery-token-ca-cert-hash sha256:c5f9959f650b9d61245bec9ae31eab990db3e353af1b687b28c95ae97bd519a1 --control-plane --certificate-key <output from kubeadm init phase upload-certs --upload-certs> --control-plane --apiserver-advertise-address 192.168.86.13 --node-name kube4
In my case I must provide --apiserver-advertise-address
because Debian is dumb and is multihoming this interface. The command is based on the output from the previous kubeadm token create
.
Edit your ~/.kube/config
to change the server to the new HA address
The remainder here is TODO, but don't forget that some cluster components will want to take advantage of the HA API Server now, including kube-proxy
.
Edit kube-system/configmaps/kube-proxy
:
apiVersion: v1
data:
config.conf: |-
apiVersion: kubeproxy.config.k8s.io/v1alpha1
# ...
kubeconfig.conf: |-
apiVersion: v1
kind: Config
clusters:
- cluster:
certificate-authority: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
server: https://kube-api.example.com:8443
name: default
# ...
- Edit metallb speaker
DaemonSet
to addnode-role.kubernetes.io/control-plane:NoSchedule
toleration - Add a
Service
for haproxy (since it otherwise seems to use the locally installed haproxy sigh)