Skip to content

Multiple Nodes Installation

Local VM setting

VMWare Setting.

  • VMnet1: host-only, subnet: 192.168.150.0/24
  • VMnet8: NAT, subnet: 11.0.1.0/24

Create guest machine with VMWare Player.

  • 4 GB RAM
  • 1 CPUs with 2 Cores
  • Ubuntu Server 22.04
  • NAT

Info:

  • Kubernetes running on Containerd.

Ubuntu Post Installation

Info:

  • Log onto each VM with the account created during Ubuntu installation, and perform below tasks on every VM.

Create user vagrant on all guests.

sudo adduser vagrant
sudo usermod -aG adm,sudo,syslog,cdrom,dip,plugdev,lxd,root vagrant
sudo passwd vagrant

Set password for root on all guests.

sudo passwd root

Enable root ssh logon.

sudo vi /etc/ssh/sshd_config

Update parameter PermitRootLogin from prohibit-password to yes.

PermitRootLogin yes
#PermitRootLogin prohibit-password

Restart the sshd service.

sudo systemctl restart sshd

Change host name, e.g., ubu1.

sudo hostnamectl set-hostname ubu1
sudo hostnamectl set-hostname ubu1 --pretty

Verify if the hostname is set to expected name, e.g., ubu1.

cat /etc/machine-info

Verify if the hostname is set to expected name, e.g., ubu1.

cat /etc/hostname

Verify if the hostname of 127.0.1.1 is set to expected name, e.g., ubu1. And add all nodes into the file /etc/hosts.

sudo vi /etc/hosts

Related setting looks like below.

127.0.1.1 ubu1
11.0.1.129 ubu1
11.0.1.130 ubu2
11.0.1.131 ubu3
11.0.1.132 ubu4

Create file /etc/netplan/00-installer-config.yaml.

sudo vi /etc/netplan/00-installer-config.yaml

Update it with information below to set VM with fixed IP with actual IP address, e.g, 11.0.1.129.

network:
  ethernets:
    ens33:
      dhcp4: false
      addresses:
      - 11.0.1.129/24
      nameservers:
        addresses:
        - 11.0.1.2
      routes:
      - to: default
        via: 11.0.1.2
  version: 2

Effect above change.

sudo netplan apply

Attention:

  • The current ssh connection will be broken due to network setting change.

Disable swap and firewall on all nodes.

sudo swapoff -a
sudo ufw disable
sudo ufw status verbose

And comment the last line of swap setting in file /etc/fstab. Need reboot guest here.

sudo vi /etc/fstab

Result likes below.

/dev/disk/by-uuid/df370d2a-83e5-4895-8c7f-633f2545e3fe / ext4 defaults 0 1
# /swap.img     none    swap    sw      0       0

Setup timezone on all nodes

sudo ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

sudo cp /etc/profile /etc/profile.bak
echo 'LANG="en_US.UTF-8"' | sudo tee -a /etc/profile

source /etc/profile

Something like this after execute command ll /etc/localtime

lrwxrwxrwx 1 root root 33 Jul 15 22:00 /etc/localtime -> /usr/share/zoneinfo/Asia/Shanghai

Kernel setting.

cat <<EOF | sudo tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF

Load to kernel.

sudo modprobe overlay
sudo modprobe br_netfilter

Network setting.

cat <<EOF | sudo tee /etc/sysctl.d/99-kubernetes-cri.conf
net.bridge.bridge-nf-call-iptables  = 1
net.ipv4.ip_forward                 = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF

Effect changes above.

sudo sysctl --system

Attention:

  • Reboot the VM.

Attention:

  • Log onto the VM with account vagrant to verify if above changes were updated as expected.
  • IP address.
ip addr list
  • Hostname.
cat /etc/machine-info
cat /etc/hostname
hostname
  • Firewall.
sudo ufw status verbose
  • Kernel setting.
lsmod | grep -i overlay
lsmod | grep -i br_netfilter
  • Network setting.
sudo sysctl -a | grep -i net.bridge.bridge-nf-call-ip*
sudo sysctl -a | grep -i net.ipv4.ip_forward

Install Containerd

Install Containerd sevice on all nodes.

Backup source file.

sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak

Install Containered.

sudo apt-get update && sudo apt-get install -y containerd

Configure Containerd. Modify file /etc/containerd/config.toml.

sudo mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sudo vi /etc/containerd/config.toml

Update sandbox_image with new value "registry.aliyuncs.com/google_containers/pause:3.6". Update SystemdCgroup with new value true.

[plugins]
  [plugins."io.containerd.gc.v1.scheduler"]

  [plugins."io.containerd.grpc.v1.cri"]
    sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.6"

    [plugins."io.containerd.grpc.v1.cri".cni]
    [plugins."io.containerd.grpc.v1.cri".containerd]
      [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]
        [plugins."io.containerd.grpc.v1.cri".containerd.default_runtime.options]
      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]

          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
            SystemdCgroup = true

Restart Containerd service.

sudo systemctl restart containerd
sudo systemctl status containerd

Install nerdctl

Install nerdctl sevice on all nodes.

The goal of nerdctl is to facilitate experimenting the cutting-edge features of containerd that are not present in Docker.

Binaries are available here: https://github.com/containerd/nerdctl/releases

wget https://github.com/containerd/nerdctl/releases/download/v0.22.2/nerdctl-0.22.2-linux-amd64.tar.gz
tar -zxvf nerdctl-0.22.2-linux-amd64.tar.gz
sudo cp nerdctl /usr/bin/

Verify nerdctl.

sudo nerdctl --help

To list local Kubernetes containers.

sudo nerdctl -n k8s.io ps

Install Kubernetes

Install Kubernetes on all nodes.

Install dependencied packages.

sudo apt-get update && sudo apt-get install -y apt-transport-https ca-certificates curl

Install gpg certificate.

sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg

Add Kubernetes repo.

echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list

Update and install dependencied packages.

sudo apt-get update
sudo apt-get install ebtables
sudo apt-get install libxtables12
sudo apt-get upgrade iptables

Check available versions of kubeadm.

apt policy kubeadm

Install 1.24.1-00 version.

sudo apt-get -y install kubelet=1.24.1-00 kubeadm=1.24.1-00 kubectl=1.24.1-00 --allow-downgrades

Setup Master Node

kubeadm init

Set up Control Plane on VM playing master node.

Check kubeadm default parameters for initialization.

kubeadm config print init-defaults

Reuslt:

apiVersion: kubeadm.k8s.io/v1beta3
bootstrapTokens:
- groups:
  - system:bootstrappers:kubeadm:default-node-token
  token: abcdef.0123456789abcdef
  ttl: 24h0m0s
  usages:
  - signing
  - authentication
kind: InitConfiguration
localAPIEndpoint:
  advertiseAddress: 1.2.3.4
  bindPort: 6443
nodeRegistration:
  criSocket: unix:///var/run/containerd/containerd.sock
  imagePullPolicy: IfNotPresent
  name: node
  taints: null
---
apiServer:
  timeoutForControlPlane: 4m0s
apiVersion: kubeadm.k8s.io/v1beta3
certificatesDir: /etc/kubernetes/pki
clusterName: kubernetes
controllerManager: {}
dns: {}
etcd:
  local:
    dataDir: /var/lib/etcd
imageRepository: k8s.gcr.io
kind: ClusterConfiguration
kubernetesVersion: 1.24.0
networking:
  dnsDomain: cluster.local
  serviceSubnet: 10.96.0.0/12
scheduler: {}

Dry rune and run. Save the output, which will be used later on work nodes.

With kubeadm init to initiate cluster, we need understand below three options about network.

  • --pod-network-cidr:
  • Specify range of IP addresses for the pod network. If set, the control plane will automatically allocate CIDRs for every node.
  • Be noted that 10.244.0.0/16 is default range of flannel. If it's changed here, please do change the same when deploy Flannel.
  • --apiserver-bind-port:
  • Port for the API Server to bind to. (default 6443)
  • --service-cidr:
  • Use alternative range of IP address for service VIPs. (default "10.96.0.0/12")

Note:

  • service VIPs (a.k.a. Cluster IP), specified by option --service-cidr.
  • podCIDR (a.k.a. endpoint IP),specified by option --pod-network-cidr.

There are 4 distinct networking problems to address:

  • Highly-coupled container-to-container communications: this is solved by Pods (podCIDR) and localhost communications.
  • Pod-to-Pod communications:
  • a.k.a. container-to-container.
  • Example with Flannel, the flow is: Pod → veth pair → cni0 → flannel.1 → host eth0 → host eth0 → flannel.1 → cni0 → veth pair → Pod.
  • Pod-to-Service communications:
  • Flow: Pod → Kernel → Servive iptables → service → Pod iptables → Pod
  • External-to-Service communications:
  • LoadBalancer: SLB → NodePort → Service → Pod

kube-proxy is responsible for iptables, not traffic.

sudo kubeadm init \
  --dry-run \
  --pod-network-cidr=10.244.0.0/16 \
  --service-cidr=192.244.0.0/16 \
  --image-repository=registry.aliyuncs.com/google_containers \
  --kubernetes-version=v1.24.1

sudo kubeadm init \
  --pod-network-cidr=10.244.0.0/16 \
  --service-cidr=192.244.0.0/16 \
  --image-repository=registry.aliyuncs.com/google_containers \
  --kubernetes-version=v1.24.1

kubeconfig file

Set kubeconfig file for current user (here it's vagrant).

mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

Kubernetes provides a command line tool kubectl for communicating with a Kubernetes cluster's control plane, using the Kubernetes API.

kubectl controls the Kubernetes cluster manager.

For configuration, kubectl looks for a file named config in the $HOME/.kube directory, which is a copy of file /etc/kubernetes/admin.conf generated by kubeadm init.

We can specify other kubeconfig files by setting the KUBECONFIG environment variable or by setting the --kubeconfig flag. If the KUBECONFIG environment variable doesn't exist, kubectl uses the default kubeconfig file, $HOME/.kube/config.

A context element in a kubeconfig file is used to group access parameters under a convenient name. Each context has three parameters: cluster, namespace, and user. By default, the kubectl command-line tool uses parameters from the current context to communicate with the cluster.

A sample of .kube/config.

apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: <certificate string>
    server: https://<eth0 ip>:6443
  name: <cluster name>
contexts:
- context:
    cluster: <cluster name>
    namespace: <namespace name>
    user: <user name>
  name: <context user>@<context name>
current-context: <context name>
kind: Config
preferences: {}
users:
- name: <user name>
  user:
    client-certificate-data: <certificate string>
    client-key-data: <certificate string>

To get the current context:

kubectl config get-contexts

Result

CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
*         kubernetes-admin@kubernetes   kubernetes   kubernetes-admin   

Install Calico

Here is guidance of End-to-end Calico installation. Detail practice demo, can be found in section "Install Calico" of "A1.Discussion" below.

Install Calico

curl https://docs.projectcalico.org/manifests/calico.yaml -O
kubectl apply -f calico.yaml

Result

configmap/calico-config created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/blockaffinities.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/caliconodestatuses.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamblocks.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamconfigs.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipamhandles.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ipreservations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/kubecontrollersconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networksets.crd.projectcalico.org created
clusterrole.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrolebinding.rbac.authorization.k8s.io/calico-kube-controllers created
clusterrole.rbac.authorization.k8s.io/calico-node created
clusterrolebinding.rbac.authorization.k8s.io/calico-node created
daemonset.apps/calico-node created
serviceaccount/calico-node created
deployment.apps/calico-kube-controllers created
serviceaccount/calico-kube-controllers created
poddisruptionbudget.policy/calico-kube-controllers created

Verify status of Calico. It may take minutes to complete initialization.

kubectl get pod -n kube-system | grep calico

Result

calico-kube-controllers-555bc4b957-l8bn2   0/1     Pending    0          28s
calico-node-255pc                          0/1     Init:1/3   0          29s
calico-node-7tmnb                          0/1     Init:1/3   0          29s
calico-node-w8nvl                          0/1     Init:1/3   0          29s

Verify network status.

sudo nerdctl network ls

Result

NETWORK ID      NAME               FILE
                k8s-pod-network    /etc/cni/net.d/10-calico.conflist
17f29b073143    bridge             /etc/cni/net.d/nerdctl-bridge.conflist
                host               
                none 

Setup Work Nodes

Use kubeadm token to generate the join token and hash value.

kubeadm token create --print-join-command

Command usage:

sudo kubeadm join <your master node eth0 ip>:6443 --token <token generated by kubeadm init> --discovery-token-ca-cert-hash <hash key generated by kubeadm init>

Result looks like below.

[preflight] Running pre-flight checks
        [WARNING SystemVerification]: missing optional cgroups: blkio
[preflight] Reading configuration from the cluster...
[preflight] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -o yaml'
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Starting the kubelet
[kubelet-start] Waiting for the kubelet to perform the TLS Bootstrap...

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.

Check Cluster Status

Cluster info:

kubectl cluster-info

Output

Kubernetes control plane is running at https://11.0.1.129:6443
CoreDNS is running at https://11.0.1.129:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

Node info:

kubectl get nodes -owide

Pod info:

kubectl get pod -A

Post Installation

Bash Autocomplete

On each node.

Set kubectl auto-completion following the guideline.

apt install -y bash-completion

source /usr/share/bash-completion/bash_completion
source <(kubectl completion bash)

echo "source <(kubectl completion bash)" >> ~/.bashrc
source ~/.bashrc

Alias

If we set an alias for kubectl, we can extend shell completion to work with that alias:

echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -o default -F __start_kubectl k' >>~/.bashrc

Update Default Context

Get current context.

kubectl config get-contexts 

In below result, we know:

  • Contenxt name is kubernetes-admin@kubernetes.
  • Cluster name is kubernetes.
  • User is kubernetes-admin.
  • No namespace explicitly defined.
CURRENT   NAME                          CLUSTER      AUTHINFO           NAMESPACE
*         kubernetes-admin@kubernetes   kubernetes   kubernetes-admin 

To set a context with new update, e.g, update default namespace, etc..

# Usage:
kubectl config set-context <context name> --cluster=<cluster name> --namespace=<namespace name> --user=<user name> 

# Set default namespace
kubectl config set-context kubernetes-admin@kubernetes --cluster=kubernetes --namespace=default --user=kubernetes-admin

To switch to a new context.

kubectl config use-context <context name>
kubectl config use-context kubernetes-admin@kubernetes

Reference:

Install Helm

Helm is the Kubernetes package manager. It doesn't come with Kubernetes.

Three concepts of helm:

  • A Chart is a Helm package.
  • It contains all of the resource definitions necessary to run an application, tool, or service inside of a Kubernetes cluster.
  • Think of it like the Kubernetes equivalent of a Homebrew formula, an Apt dpkg, or a Yum RPM file.
  • A Repository is the place where charts can be collected and shared.
  • It's like Perl's CPAN archive or the Fedora Package Database, but for Kubernetes packages.
  • A Release is an instance of a chart running in a Kubernetes cluster.
  • One chart can often be installed many times into the same cluster. And each time it is installed, a new release is created.
  • Consider a MySQL chart. If you want two databases running in your cluster, you can install that chart twice. Each one will have its own release, which will in turn have its own release name.

Reference:

Helm Client Installation:

curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
chmod 700 get_helm.sh
./get_helm.sh

Output:

Downloading https://get.helm.sh/helm-v3.9.1-linux-amd64.tar.gz
Verifying checksum... Done.
Preparing to install helm into /usr/local/bin
helm installed into /usr/local/bin/helm

Note:

  • helm init does not exist in Helm 3, following the removal of Tiller. You no longer need to install Tiller in your cluster in order to use Helm.
  • helm search can be used to search two different types of source:
  • helm search hub searches the Artifact Hub, which lists helm charts from dozens of different repositories.
  • helm search repo searches the repositories that you have added to your local helm client (with helm repo add). This search is done over local data, and no public network connection is needed.

Reference:

Reset cluster

Caution: below steps will destroy current cluster.

Delete all nodes in the cluster.

kubeadm reset

Clean up rule of iptables.

iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X

Clean up rule of IPVS if using IPVS.

ipvsadm --clear