2022. 10. 03.
This post continues from k8s via proxmox (1) - Creating an Ubuntu server template.
Using the template created earlier, generate the k8s-ctrlr and k8s-node.
Wait a moment after booting until the cloud-init configuration is complete.
Set static IPs for k8s-ctrlr and k8s-node.
As always, use netplan.
Depending on your setup, it will likely be similar if you're running a Proxmox server with a typical router and a single computer.
Before editing, make a backup just in case:
sudo cp /etc/netplan/01-netcfg.yaml{,.bak}Edit the /etc/netplan/50-cloud-init.yaml file as follows:
version: 2
ethernets:
eth0:
addresses: [192.168.0.180/24]
routes:
- to: default
via: 192.168.0.1
nameservers:
addresses: [192.168.0.120, 1.1.1.1]In my case, I’m running a local DNS server using pi.hole on 192.168.0.120, so I added it as a nameserver.
Set the static IPs as 192.168.0.180 for k8s-ctrlr and 192.168.0.185 for k8s-node.
Apply the changes with:
sudo netplan applyInstall containerd on both k8s-ctrlr and k8s-node.
sudo apt-get update && sudo apt-get install -y containerdSave the default configuration with the following command:
sudo mkdir /etc/containerd
containerd config default | sudo tee /etc/containerd/config.tomlThen, edit the /etc/containerd/config.toml file as follows:
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
BinaryName = ""
CriuImagePath = ""
CriuPath = ""
CriuWorkPath = ""
IoGid = 0
IoUid = 0
NoNewKeyring = false
NoPivotRoot = false
Root = ""
ShimCgroup = ""
SystemdCgroup = falseChange SystemdCgroup = false to true.
Open the /etc/sysctl.conf file and modify it as follows:
# Uncomment the next line to enable packet forwarding for IPv4
# sysctl -w net.ipv4.ip_forward=1Find the second line above and remove the # comment.
Add the following to the /etc/modules-load.d/k8s.conf file:
br_netfilterReboot to apply the updated network settings.
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectlThe above commands fetch the key, add the Kubernetes apt repository, and install Kubernetes packages.
Check the official documentation for updates and modify accordingly if needed.
Let’s create a k8s-node template to generate as many workers as needed.
The following commands resolve the issue where cloned VMs have the same IP due to duplicate machine IDs.
sudo cloud-init clean
sudo truncate -s 0 /etc/machine-id
sudo rm /var/lib/dbus/machine-id
sudo ln -s /etc/machine-id /var/lib/dbus/machine-idThen, power off the VM and click Convert to Template in the web UI.
Once converted to a template, you can create as many workers as needed.
Here, we will create two workers: k8s-node-1 and k8s-node-2.
Adjust the specifications of k8s-ctrlr, k8s-node-1, and k8s-node-2.
Power off all VMs and modify their specifications in the web UI.
Depending on the server running Proxmox, the recommended specs are:
k8s-ctrlr: 2 CPUs, 4 GB RAMk8s-node-1, k8s-node-2: 2 CPUs, 2 GB RAMIf the server lacks sufficient resources, set all to 2 CPUs and 2 GB RAM, which are the minimum requirements.
Even with high specs, increasing the number of workers is preferred over allocating more RAM.
Once done, power on all VMs.
For VMs cloned from a template, static IPs may revert to DHCP. Change them back to static IPs if desired.
Log in to k8s-ctrlr and create a cluster using kubeadm.
sudo kubeadm init --control-plane-endpoint=<vm-ip> \
--node-name <vm-hostname> --pod-network-cidr=10.244.0.0/16For example, <vm-ip> could be 192.168.0.180, and <vm-hostname> could be k8s-ctrlr.
Next, run the following commands:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/configThen, copy the join command generated below and execute it on other nodes.
kubeadm join 192.168.0.180:6443 --token ~~~~~~If the command is unavailable, execute the following on k8s-ctrlr to generate a new one:
kubeadm token create --print-join-commandOn k8s-ctrlr, execute the following command:
kubectl apply -f \
https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.ymlFinally, verify that all nodes are successfully created:
kubectl get nodesIf all nodes show as Ready, the setup is complete:
NAME STATUS ROLES AGE VERSION
k8s-ctrlr Ready control-plane,master 10m v1.21.1
k8s-node-1 Ready <none> 10m v1.21.1
k8s-node-2 Ready <none> 10m v1.21.1The cluster creation process ends here.
We've created a cluster with some effort, but what should we do now? Let's deploy a web server using Kubernetes.
Let's create and deploy a single nginx pod. On k8s-ctrlr, create a pod.yaml file with the following content:
apiVersion: v1
kind: Pod
metadata:
name: nginx-example
labels:
app: nginx
spec:
containers:
- name: nginx
image: linuxserver/nginx
ports:
- containerPort: 80
name: "nginx-http"Then deploy it using the following command:
kubectl apply -f pod.yamlLet's verify if the pod was created successfully:
kubectl get pods -o wideOn k8s-node-1, we can confirm that nginx is running on the pod network at 10.244.1.2:80.
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-example 1/1 Running 0 3h8m 10.244.1.2 k8s-node-1 <none>You can verify the connection from k8s-ctrlr, k8s-node-1, and k8s-node-2 using the following command:
curl 10.244.1.2:80However, directly accessing pods is not recommended. This is because pods get new IPs when they restart or are deleted. Also, while cluster internal access is possible from any node, external access is not. To solve these issues, we create a service. On k8s-ctrlr, create a service-nodeport.yaml file with the following content:
apiVersion: v1
kind: Service
metadata:
name: nginx-example
spec:
type: NodePort
ports:
- name: http
port: 80
nodePort: 30080
targetPort: nginx-http
selector:
app: nginxThen deploy it using the following command:
kubectl apply -f service-nodeport.yamlLet's verify if the service was created successfully:
kubectl get svcIf you see the following output, it was created successfully:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 3h19m
nginx-example NodePort 10.109.7.138 <none> 80:30080/TCP 3h16mYou can verify the connection from k8s-ctrlr, k8s-node-1, and k8s-node-2 using the following command:
curl localhost:30080Additionally, you can access it externally using the IP of any cluster member:
curl 192.168.0.180:30080
curl 192.168.0.185:30080
curl 192.168.0.190:30080While on AWS you can use loadbalancer type instead of nodeport type to access through a single address, we used nodeport type here because loadbalancer type is not available locally.
In this post, we learned how to install Kubernetes and deploy nginx. In the next post, we'll explore other Kubernetes features. We'll look at ingress, configmap, secret, volume, deployment, statefulset, etc. Also, if possible, we'll try to set up CI/CD using Jenkins.