Kubernetes HA Cluster using LXC
Architecture
Architecture used in this hands-on guide
- name.com - for domain names
- Oracle network load balancer - for load balancing request to servers
- 2 Oracle ampere instances - as provisioned servers
- HAProxy systemd daemon - in each server, to balancing requests from the load balancer to forward to the internal LXC containers
- 2 LXC containers per server - to accommodate the Kubernetes cluster
- LXC network - to provide connections between containers of all servers
- kubeadm - to setup the Kubernetes cluster
- Flannel - to setup the Kubernetes network
- Traefik - in global deploy mode to be in each LXC container, to receive requests from HAProxy to forward to the Kubernetes services
static IP network definition
internal network CIDR
from https://www.davidc.net/sites/default/subnets/subnets.html?network=10.10.0.0&mask=16&division=1.0
10.10.0.0/16 10.10.0.0 - 10.10.255.255 10.10.0.1 - 10.10.255.254 65534 Divide
note about the nomenclature scheme for the 10.10.0.0 ip address
- the first two numbers (10.10.) are the prefix under that will be contained all the internal LXC resources
- the third number will be used to identify provisioned servers (0-255, 255 max)
- and forth number will be used to identify LXC containers accommodated by servers (1-254, 253 max theoretically, however we just want a handful of them per server, we can use 10.10.x.100 for the provisioned server, so the range will be 1-99, 98 max that is more than enough)
In this way, we can chose container names that remember both the parent server as well as the static IP used by the container
- ip table:
| resource name | external network addr | internal bridge addr |
|---|---|---|
| * server1 | (unknown at this point) | 10.10.1.100 |
| c101 | 10.10.1.1 | |
| c102 | 10.10.1.2 | |
| * server2 | (unknown at this point) | 10.10.2.100 |
| c201 | 10.10.2.1 | |
| c202 | 10.10.2.2 |
System cleanup
on the hosting machines, cleanup usefulness stuffs to avoid disk waste and provider's agents resource waste
on Debian, you can use apt-mark and apt-get autoremove --purge
Example useful for Jammy cleanup:
turn off phases updates, Ubuntu 24 specific stuff, see https://ubuntu.com/server/docs/explanation/software/about-apt-upgrade-and-phased-updates/#how-do-i-turn-off-phased-updates
cat > /etc/apt/apt.conf.d/99-Phased-Updates << EOF Update-Manager::Always-Include-Phased-Updates true; APT::Get::Always-Include-Phased-Updates true; EOF
then
apt-get update apt-mark auto `apt-mark showmanual` apt-get install apt apt-utils base-files base-passwd coreutils dash debconf dpkg grep gzip hostname libc6 libc-bin nano passwd procps psmisc readline-common sed sysvinit-utils tar apt-get install bash binutils bsdutils bzip2 ca-certificates curl dialog dnsutils findutils iptables iputils-ping kmod less locales lsof man mount mutt ncal netcat-traditional nmap telnet tzdata util-linux apt-get install busybox-static command-not-found conntrack cron dmidecode friendly-recovery hwdata hdparm info iputils-tracepath irqbalance jq linux-oracle linux-headers-oracle mtr-tiny net-tools netcat-openbsd netfilter-persistent rsync rsyslog socat sudo tcl tcpdump time ubuntu-minimal ubuntu-standard unzip apt-get autoremove --purge apt-get dist-upgrade apt-get clean shutdown -r now
shutdown just to grant avoid surprises in the next future reboot
LXC install
Note, we will use the vanilla LXC, not the Ubuntu LXD one
Follow Lxc instructions
HAProxy install
HAProxy will be installed in any server to forward requests incoming from the external network load balancer to the internal LXC containers services
In the follow example, HAProxy will forward external incoming requests on 6443 port to the Kubernetes api listening at 6443 port inside each of the LXC containers of this example, roundrobin balancing the request between them:
apt-get install haproxy
apt-get clean
cat > /etc/haproxy/haproxy.cfg << 'EOF'
global
log stdout format raw local0
chroot /var/lib/haproxy
user haproxy
group haproxy
daemon
defaults
log global
mode tcp
timeout connect 10s
timeout client 300s
timeout server 300s
timeout tunnel 300s
option tcp-check
tcp-check connect
balance roundrobin
default-server downinter 5s fastinter 1s inter 3s fall 2 rise 3
frontend ft_lxc_kubeapi
bind *:6443
default_backend bk_lxc_kubeapi
backend bk_lxc_kubeapi
server c101 10.10.1.1:6443 check
server c102 10.10.1.2:6443 check
server c201 10.10.2.1:6443 check
server c202 10.10.2.2:6443 check
frontend ft_lxc_deploy
bind *:30666
default_backend bk_lxc_deploy
backend bk_lxc_deploy
server c101 10.10.1.1:30666 check
server c102 10.10.1.2:30666 check
server c201 10.10.2.1:30666 check
server c202 10.10.2.2:30666 check
frontend ft_lxc_gitolite
bind *:30022
default_backend bk_lxc_gitolite
backend bk_lxc_gitolite
server c101 10.10.1.1:30022 check
server c102 10.10.1.2:30022 check
server c201 10.10.2.1:30022 check
server c202 10.10.2.2:30022 check
frontend ft_lxc_traefik_http
bind *:30080
default_backend bk_lxc_traefik_http
backend bk_lxc_traefik_http
server c101 10.10.1.1:30080 check
server c102 10.10.1.2:30080 check
server c201 10.10.2.1:30080 check
server c202 10.10.2.2:30080 check
frontend ft_lxc_traefik_https
bind *:30443
default_backend bk_lxc_traefik_https
backend bk_lxc_traefik_https
server c101 10.10.1.1:30443 check
server c102 10.10.1.2:30443 check
server c201 10.10.2.1:30443 check
server c202 10.10.2.2:30443 check
EOF
systemctl restart haproxy
systemctl status haproxy
Kubernetes install under LXC
Follow the Install section of Kubernetes
In one of the LXC machines with Kubernetes binaries installed, do
LB_ENDPOINT=<INTERNAL_LOADBALANCER_IP>:6443
kubeadm init --v=5 --ignore-preflight-errors=NumCPU,Mem --cri-socket unix:/run/containerd/containerd.sock --pod-network-cidr=192.168.0.0/16 --control-plane-endpoint "${LB_ENDPOINT}" --upload-certs
mkdir -p $HOME/.kube; cp -a /etc/kubernetes/admin.conf $HOME/.kube/config
kubectl get nodes
Fix kube proxy error inside LXD trying to change /proc/sys/stuffs
kubectl get all -A kubectl logs -n kube-system -l k8s-app=kube-proxy kubectl -n kube-system get configmap kube-proxy -o yaml | sed 's/maxPerCore: null/maxPerCore: 0/' | sed 's/min: null/min: 0/' | kubectl apply -f - kubectl -n kube-system delete pods -l k8s-app=kube-proxy ## wait pending state kubectl get all -A
Configure Kubernetes network
mkdir flannel
VERSION="0.28.7"
curl -fsSL "https://github.com/flannel-io/flannel/releases/download/v${VERSION}/kube-flannel.yml" | sed 's:10.244.0.0/16:192.168.0.0/16:g' > flannel/flannel.yml
kubectl apply -f flannel/flannel.yml
## wait ready state
kubectl get nodes
Obtain Kubernetes join command, all the nodes will be control-plane
echo $(kubeadm token create --print-join-command) --v=5 --ignore-preflight-errors=NumCPU,Mem --control-plane --certificate-key $(kubeadm init phase upload-certs --upload-certs 2>/dev/null | sed -n '/Using certificate key/{n;p;}')
On each other nodes to configure
kubeadm join ... mkdir -p $HOME/.kube; cp -a /etc/kubernetes/admin.conf $HOME/.kube/config kubectl get nodes
Untaint manager nodes to be able to accommodate services
kubectl taint nodes --all node-role.kubernetes.io/control-plane-