Skip to content

Yaon-C2H8N2/k8s-stretched

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 

Repository files navigation

Introduction

Description

The project involves creating a Kubernetes cluster stretched across multiple sites. It will consist of 3 nodes:

  • 2 nodes on a private network
  • 1 node on a publicly accessible network
    A VPN server will be hosted on the publicly accessible node to create a common private network for all 3 nodes on which the cluster will be based.

Topology

The topology is as follows:

  • eagle: the VPS. Hosts the VPN, publicly accessible. Must access k8s-node1 and k8s-node2 to expose services hosted on these nodes.
  • k8s-node1: one of the machines on the private network. Must access the VPS and services hosted on it via the VPN rather than via the public IP.
  • k8s-node2: same as above
    The machines k8s-node1 and k8s-node2 must remain accessible via the private network on which they are hosted but must also be able to communicate with each other via the VPN.

In summary

We would theoretically have 3 networks:

  • Public (Eagle only)
  • VPN (Eagle, node1 and node2)
  • Private (node1 and node2)

k8s_streched_cluster.png

Implementation

VPN

The VPN chosen for this project is Wireguard for its simplicity of configuration and installation. Ideal for adding other nodes to the cluster later.

Installation and generation of public/private keys on each node:

apt update
apt install wireguard
cd /etc/wireguard
wg genkey > private && cat private | wg pubkey > pubkey

Server node configuration

[Interface]  
Address = 10.10.0.1/24  
ListenPort = 51820  
PrivateKey = <eagle's private key>  
PostUp = sysctl -w net.ipv4.ip_forward=1  
  
[Peer]  
PublicKey = <k8s-node1's public key>  
AllowedIPs = 10.10.0.2/32  
  
[Peer]  
PublicKey = <k8s-node2's public key>  
AllowedIPs = 10.10.0.3/32  

Configuration of the other nodes

[Interface]  
Address = 10.10.0.2/24  
PrivateKey = <k8s-nodeX's private key>  
  
[Peer]  
PublicKey = <eagle's public key>  
Endpoint = <eagle's public ip>:51820  
AllowedIPs = 10.10.0.0/24  
PersistentKeepalive = 25  

Enable routing on eagle

Since k8s-node1 and k8s-node2 need to be mutually accessible via the VPN, IP routing must be enabled:

echo 'net.ipv4.ip_forward = 1' >> /etc/sysctl.conf

The 3 nodes should now be able to communicate with each other via the 10.10.0.0 network

Kubernetes

The creation of the k8s cluster will be done via kubeadm. The plugins used for networking and ingress control will be flannel and ingress-nginx.

Initialization

Kubeadm uses a kubeadm-config.yaml configuration file to set up the cluster. The configuration is initiated from eagle:

apiVersion: kubeadm.k8s.io/v1beta4
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 10.10.0.1 # We specify to use the interface on the VPN
nodeRegistration:
  criSocket: unix:///var/run/cri-dockerd.sock # We'll use docker's CRI
  taints: null
---
apiVersion: kubeadm.k8s.io/v1beta4
imageRepository: registry.k8s.io
kind: ClusterConfiguration
kubernetesVersion: 1.33.1
networking:
  podSubnet: "10.244.0.0/16" # Required for flannel
proxy: { }
---
kind: KubeletConfiguration
apiVersion: kubelet.config.k8s.io/v1beta1
cgroupDriver: systemd

Once the configuration is done, we initialize the cluster:

kubeadm init --config ./kubeadm-config.yaml

Once the configuration is complete, a command allowing other nodes to join the cluster is displayed. In our case, we also specify the CRI to use:

kubeadm join 10.10.0.1:6443 --token <token> --discovery-token-ca-cert-hash <hash> --cri-socket unix:///var/run/cri-dockerd.sock

The nodes may not have the correct IP address:

kubectl get nodes -o wide

NAME      STATUS   ROLES         AGE   VERSION INTERNAL-IP
eagle     NotReady control-plane 5m25s v1.33.1 <eagle's public ip>
k8s-node1 NotReady <none>        4m36s v1.33.1 172.16.0.163
k8s-node2 NotReady <none>        4m27s v1.33.1 172.16.0.164

Each node needs to be configured to use the correct address:

echo "KUBELET_EXTRA_ARGS=--node-ip=10.10.0.X" > /etc/default/kubelet
systemctl daemon-reexec
systemctl restart kubelet

# once configuration is done on all nodes
kubectl get nodes -o wide

NAME      STATUS   ROLES         AGE   VERSION INTERNAL-IP
eagle     NotReady control-plane 5m45s v1.33.1 10.10.0.1
k8s-node1 NotReady <none>        4m56s v1.33.1 10.10.0.2
k8s-node2 NotReady <none>        4m47s v1.33.1 10.10.0.3

Networking & Ingress

Networking will be done via Flannel. The plugin is added with the following command:

kubectl apply -f https://github.com/flannel-io/flannel/releases/latest/download/kube-flannel.yml  

Once all pods are running, we can configure the ingress controller. We'll use ingress-nginx for this. We download the manifest before applying it because we're going to modify it:

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.12.2/deploy/static/provider/baremetal/deploy.yaml

In the Deployment section, we'll add a rule in nodeSelector to deploy the ingress controller on the publicly exposed node. Since the node is also part of the control-plane, a tolerations rule will also be added to allow deployment on the control-plane in this case:

...
---
apiVersion: apps/v1
kind: Deployment
...

spec:
    ...
    template:
        ...
        spec:
            ...
            tolerations:
                - effect: NoSchedule
                  key: node-role.kubernetes.io/control-plane
                  operator: Exists
            nodeSelector:
                kubernetes.io/hostname: eagle
                kubernetes.io/os: linux
            ... 

We now apply the ingress-nginx manifest:

kubectl -f ./deploy.yaml

Now we need to modify the VPN configuration to allow routing IPs from the networks created by Flannel through the VPN.

On Eagle:

[Peer]  
PublicKey = <k8s-node1's public key>  
AllowedIPs = 10.10.0.2/32, 10.244.1.0/24  
  
[Peer]  
PublicKey = <k8s-node2's public key>  
AllowedIPs = 10.10.0.3/32, 10.244.2.0/24  

On the private nodes:

[Peer]  
PublicKey = <eagle's public key>  
Endpoint = <eagle's public ip>:51820  
# We allow the VPN IPs and all other Flannel IPs  
AllowedIPs = 10.10.0.0/24, 10.244.0.0/24, 10.244.2.0/24  
PersistentKeepalive = 25  

After restarting the VPN, it should now be possible to reach each node, either via the VPN IP or via the IP created by Flannel. There may already be routing rules for these IPs as shown here:

ip route
# returns
default via XXX.XXX.XXX.XXX dev eth0 onlink
10.244.0.0/24 dev cni0 proto kernel scope link src 10.244.0.1
10.244.1.0/24 via 10.244.1.0 dev flannel.1 onlink
10.244.2.0/24 via 10.244.2.0 dev flannel.1 onlink
...

# we remove them and restart the VPN
ip route del 10.244.1.0/24
ip route del 10.244.2.0/24
systemctl restart wg-quick@wg0

The cluster is now fully configured and ready to host services!

About

Simple multi-site k8s setup

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors