Demo how to build up multi nodes k8s cluster on hyper-v with Vagrant.
Will address few pain points:
- NAT static ip adaptor in hyper-v, needed for permanent k8s API server
- reset static ip in vagrant provision before installation and configuration
Docker installation script
Following up Docker official Ubuntu installation procedure, https://docs.docker.com/engine/install/.
Run below docker-install.sh to install docker server, client and enable cgroupdriver to systemd.
echo === $(date) Provisioning - docker-install.sh by $(whoami) start
sudo apt-get update -y
sudo apt-get install -y ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update -y
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo groupadd docker
sudo usermod -aG docker $USER
# turn off swap
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab
sudo mkdir /etc/docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl restart docker
echo === $(date) Provisioning - docker-install.sh by $(whoami) end
k8s installation script
Following up k8s kubeadm installation guide, run below k8s-install.sh to quickly install kubeadm, kubectl and kubelet latest packages.
https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/install-kubeadm/
echo === $(date) Provisioning - k8s-install.sh by $(whoami) start sudo apt-get update -y sudo apt-get install -y apt-transport-https ca-certificates curl 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 -y sudo apt-get install -y kubelet kubeadm kubectl sudo apt-mark hold kubelet kubeadm kubectl # cli completion sudo apt-get install bash-completion -y source <(kubectl completion bash) echo "source <(kubectl completion bash)" >> ~/.bashrc echo 'alias k=kubectl' >>~/.bashrc echo 'complete -F __start_kubectl k' >>~/.bashrc echo "export do='--dry-run=client -o yaml'" >>~/.bashrc echo === $(date) Provisioning - k8s-install.sh $1 by $(whoami) end
k8s cluster init script
Assume we use calico network plugin for k8s cluster, skip cpu and memory check for test.
After kubeadm init success, saves the token info to /tmp/kubeadm.log, to quickly join worker node to cluster later.
Put all in handy script k8s-init.sh for cluster init.
echo === $(date) Provisioning - k8s-init.sh $1 by $(whoami) start
if [ -z "$1" ];then
sudo kubeadm init --pod-network-cidr=192.168.0.0/16 --ignore-preflight-errors=NumCPU --ignore-preflight-errors=Mem | tee /tmp/kubeadm.log
else
sudo kubeadm init --pod-network-cidr=192.168.0.0/16 --apiserver-advertise-address=$1 --ignore-preflight-errors=NumCPU --ignore-preflight-errors=Mem | tee /tmp/kubeadm.log
fi
# allow normal user to run kubectl
if [ -d $HOME/.kube ]; then
rm -r $HOME/.kube
fi
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# install calico network addon
kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml
kubectl create -f https://docs.projectcalico.org/manifests/custom-resources.yaml
# allow run on master
kubectl taint nodes --all node-role.kubernetes.io/master-
echo === $(date) Provisioning - k8s-init.sh $1 by $(whoami) end
k8s cluster reset script
Apply below handy script k8s-reset.sh to reset cluster when whatever reason cluster mess up.
echo === $(date) Provisioning - k8s-reset.sh $1 by $(whoami) start
sudo kubeadm reset -f
date
if [ -z "$1" ];then
sudo kubeadm init --pod-network-cidr=192.168.0.0/16 --ignore-preflight-errors=NumCPU --ignore-preflight-errors=Mem | tee /tmp/kubeadm.log
else
sudo kubeadm init --pod-network-cidr=192.168.0.0/16 --apiserver-advertise-address=$1 --ignore-preflight-errors=NumCPU --ignore-preflight-errors=Mem | tee /tmp/kubeadm.log
fi
# allow normal user to run kubectl
if [ -d $HOME/.kube ]; then
rm -r $HOME/.kube
fi
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# install calico network addon
kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml
kubectl create -f https://docs.projectcalico.org/manifests/custom-resources.yaml
# allow run on master
kubectl taint nodes --all node-role.kubernetes.io/master-
echo === $(date) Provisioning - k8s-reset.sh $1 by $(whoami) end
k8s worker join script
It is lazy way to get join token if worker join soon after cluster init by copying kubeadm init log from master, it is fair enough for dev test purpose.
We need to tell master ip $1 to join script, k8s-join.sh.
The trick to make scp working in vagrant provision is to setup passwordless ssh using default vagrant key pair.
echo === $(date) Provisioning - k8s-join.sh $1 by $(whoami) start
sudo sed -i '/master/d' /etc/hosts
sudo sed -i "1i$1 master" /etc/hosts
# add private key
curl -Lo $HOME/.ssh/vagrant https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant
chmod 0600 $HOME/.ssh/vagrant
# join cluster
scp -q -o "StrictHostKeyChecking no" -i $HOME/.ssh/vagrant master:/tmp/kubeadm.log /tmp/kubeadm.log
token=$(cat /tmp/kubeadm.log |grep "kubeadm join"|head -1 |awk -Ftoken '{print $2}'|awk '{print $1}')
certhash=$(cat /tmp/kubeadm.log |grep discovery-token-ca-cert-hash|tail -1|awk '{print $2}')
sudo kubeadm join master:6443 --token $token \
--discovery-token-ca-cert-hash $certhash
# allow normal user to run kubectl
if [ -d $HOME/.kube ]; then
rm -r $HOME/.kube
fi
mkdir -p $HOME/.kube
scp -q -o "StrictHostKeyChecking no" -i $HOME/.ssh/vagrant master:$HOME/.kube/config $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
echo === $(date) Provisioning - k8s-join.sh $1 by $(whoami) end
k8s worker rejoin script
need to reset kubeadm config when mess up before join to cluster, otherwise has similar error as below,
[preflight] Running pre-flight checks
error execution phase preflight: [preflight] Some fatal errors occurred:
[ERROR FileAvailable--etc-kubernetes-kubelet.conf]: /etc/kubernetes/kubelet.conf already exists
[ERROR Port-10250]: Port 10250 is in use
[ERROR FileAvailable--etc-kubernetes-pki-ca.crt]: /etc/kubernetes/pki/ca.crt already exists
run k8s-rejoin.sh to reset kubeadm and join to cluster on worker node,
echo === $(date) Provisioning - k8s-rejoin.sh $1 by $(whoami) start
sudo sed -i '/master/d' /etc/hosts
sudo sed -i '1i$1 master' /etc/hosts
sudo kubeadm reset -f
# join cluster
scp -q -o "StrictHostKeyChecking no" -i $HOME/.ssh/vagrant master:/tmp/kubeadm.log /tmp/kubeadm.log
token=$(cat /tmp/kubeadm.log |grep "kubeadm join"|head -1 |awk -Ftoken '{print $2}'|awk '{print $1}')
certhash=$(cat /tmp/kubeadm.log |grep discovery-token-ca-cert-hash|tail -1|awk '{print $2}')
sudo kubeadm join master:6443 --token $token \
--discovery-token-ca-cert-hash $certhash
# allow normal user to run kubectl
if [ -d $HOME/.kube ]; then
rm -r $HOME/.kube
fi
mkdir -p $HOME/.kube
scp -q -o "StrictHostKeyChecking no" -i $HOME/.ssh/vagrant master:$HOME/.kube/config $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
echo === $(date) Provisioning - k8s-rejoin.sh $1 by $(whoami) end
setup NAT static ip for hyper-v in vagrant
first for all, create own NAT virtual switch on host Powershell,
New-VMSwitch -SwitchName "NAT" -SwitchType Internal New-NetIPAddress -IPAddress 192.168.120.1 -PrefixLength 24 -InterfaceAlias 'vEthernet (NAT)' New-NetNat -Name netNAT -InternalIPInterfaceAddressPrefix 192.168.120.0/24
Vagrant not supports well for hyper-v network, have to choice hyper-v virtual switch and setup static ip, routing by own for NAT, here is example of Vagrantfile,
$nic = <<SCRIPT
echo === $(date) Provisioning - nic $1 by $(whoami) start
cat <<EOF | sudo tee /etc/netplan/01-netcfg.yaml
network:
version: 2
renderer: networkd
ethernets:
eth0:
dhcp4: no
dhcp6: no
addresses: [$1/24]
gateway4: 192.168.120.1
nameservers:
addresses: [4.2.2.1, 4.2.2.2, 208.67.220.220]
EOF
cat /etc/netplan/01-netcfg.yaml
sudo netplan apply
echo eth0 setting
ip addr
ip route
ping -c 2 google.ca
echo === $(date) Provisioning - nic $1 by $(whoami) end
SCRIPT
Vagrant.configure("2") do |config|
config.vm.box = "generic/ubuntu1804"
config.vm.define "master" do |master|
master.vm.hostname = "master"
master.vm.provider "hyperv" do |v|
v.vmname="master"
v.memory=2048
v.cpus=1
end
master.vm.provision "shell", inline: $nic, args: "192.168.120.2", privileged: false
end
config.vm.define "worker" do |worker|
worker.vm.hostname = "worker"
worker.vm.provider "hyperv" do |v|
v.vmname="worker"
v.memory=1024
v.cpus=1
end
worker.vm.provision "shell", inline: $nic, args: "192.168.120.3", privileged: false
end
end
cluster test
hyper-v vm can communicate each other and to/from Host, also access to Internet
PS C:\Users\oldhorse\vagrant\master> vagrant ssh master vagrant@master:~$ ip addr 1: lo:mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:15:5d:c3:82:1e brd ff:ff:ff:ff:ff:ff inet 192.168.120.2/24 brd 192.168.120.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::215:5dff:fec3:821e/64 scope link valid_lft forever preferred_lft forever vagrant@master:~$ ping 192.168.120.3 PING 192.168.120.3 (192.168.120.3) 56(84) bytes of data. 64 bytes from 192.168.120.3: icmp_seq=1 ttl=64 time=1.72 ms 64 bytes from 192.168.120.3: icmp_seq=2 ttl=64 time=0.685 ms ^C --- 192.168.120.3 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1002ms rtt min/avg/max/mdev = 0.685/1.205/1.725/0.520 ms vagrant@master:~$ ping google.ca PING google.ca (172.217.1.195) 56(84) bytes of data. 64 bytes from 172.217.1.195: icmp_seq=1 ttl=127 time=31.3 ms 64 bytes from 172.217.1.195: icmp_seq=2 ttl=127 time=29.7 ms 64 bytes from 172.217.1.195: icmp_seq=3 ttl=127 time=27.5 ms vagrant@worker:~$ ip addr 1: lo: mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000 link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00 inet 127.0.0.1/8 scope host lo valid_lft forever preferred_lft forever 2: eth0: mtu 1500 qdisc mq state UP group default qlen 1000 link/ether 00:15:5d:c3:82:1f brd ff:ff:ff:ff:ff:ff inet 192.168.120.3/24 brd 192.168.120.255 scope global eth0 valid_lft forever preferred_lft forever inet6 fe80::215:5dff:fec3:821f/64 scope link valid_lft forever preferred_lft forever vagrant@worker:~$ ip route default via 192.168.120.1 dev eth0 proto static 192.168.120.0/24 dev eth0 proto kernel scope link src 192.168.120.3 vagrant@worker:~$ ping 192.168.120.2 PING 192.168.120.2 (192.168.120.2) 56(84) bytes of data. 64 bytes from 192.168.120.2: icmp_seq=1 ttl=64 time=0.681 ms ^C --- 192.168.120.2 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms rtt min/avg/max/mdev = 0.681/0.681/0.681/0.000 ms vagrant@worker:~$ ping 192.168.120.1 PING 192.168.120.1 (192.168.120.1) 56(84) bytes of data. 64 bytes from 192.168.120.1: icmp_seq=1 ttl=128 time=0.529 ms 64 bytes from 192.168.120.1: icmp_seq=2 ttl=128 time=0.430 ms ^C --- 192.168.120.1 ping statistics --- 2 packets transmitted, 2 received, 0% packet loss, time 1015ms rtt min/avg/max/mdev = 0.430/0.479/0.529/0.054 ms vagrant@worker:~$ ping google.ca PING google.ca (142.251.45.99) 56(84) bytes of data. 64 bytes from 142.251.45.99: icmp_seq=1 ttl=127 time=31.6 ms 64 bytes from 142.251.45.99: icmp_seq=2 ttl=127 time=94.4 ms 64 bytes from 142.251.45.99: icmp_seq=3 ttl=127 time=31.8 ms
full k8s cluster with vagrant
We put all together in final Vagrantfile,
$nic = <<SCRIPT
echo === $(date) Provisioning - nic $1 by $(whoami) start
cat <<EOF | sudo tee /etc/netplan/01-netcfg.yaml
network:
version: 2
renderer: networkd
ethernets:
eth0:
dhcp4: no
dhcp6: no
addresses: [$1/24]
gateway4: 192.168.120.1
nameservers:
addresses: [4.2.2.1, 4.2.2.2, 208.67.220.220]
EOF
cat /etc/netplan/01-netcfg.yaml
sudo netplan apply
echo eth0 setting
ip addr
ip route
ping -c 2 google.ca
echo === $(date) Provisioning - nic $1 by $(whoami) end
SCRIPT
$dockerInstall = <<SCRIPT
echo === $(date) Provisioning - dockerInstall by $(whoami) start
sudo apt-get update -y
sudo apt-get install -y ca-certificates curl gnupg lsb-release
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update -y
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
sudo groupadd docker
sudo usermod -aG docker $USER
# turn off swap
sudo swapoff -a
sudo sed -i '/swap/d' /etc/fstab
sudo mkdir /etc/docker
cat <<EOF | sudo tee /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2"
}
EOF
sudo systemctl enable docker
sudo systemctl daemon-reload
sudo systemctl restart docker
echo === $(date) Provisioning - dockerInstall by $(whoami) end
SCRIPT
$k8sInstall = <<SCRIPT
echo === $(date) Provisioning - k8sInstall by $(whoami) start
sudo apt-get update -y
sudo apt-get install -y apt-transport-https ca-certificates curl
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 -y
sudo apt-get install -y kubelet kubeadm kubectl
sudo apt-mark hold kubelet kubeadm kubectl
# cli completion
sudo apt-get install bash-completion -y
source <(kubectl completion bash)
echo "source <(kubectl completion bash)" >> ~/.bashrc
echo 'alias k=kubectl' >>~/.bashrc
echo 'complete -F __start_kubectl k' >>~/.bashrc
echo "export do='--dry-run=client -o yaml'" >>~/.bashrc
echo === $(date) Provisioning - k8sInstall by $(whoami) end
SCRIPT
$k8sInit = <<SCRIPT
# $1 - master/api server ip
echo === $(date) Provisioning - k8sInit $1 by $(whoami) start
if [ -z "$1" ];then
sudo kubeadm init --pod-network-cidr=192.168.0.0/16 --ignore-preflight-errors=NumCPU --ignore-preflight-errors=Mem | tee /tmp/kubeadm.log
else
sudo kubeadm init --pod-network-cidr=192.168.0.0/16 --apiserver-advertise-address=$1 --ignore-preflight-errors=NumCPU --ignore-preflight-errors=Mem | tee /tmp/kubeadm.log
fi
# allow normal user to run kubectl
if [ -d $HOME/.kube ]; then
rm -r $HOME/.kube
fi
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
# install calico network addon
kubectl create -f https://docs.projectcalico.org/manifests/tigera-operator.yaml
kubectl create -f https://docs.projectcalico.org/manifests/custom-resources.yaml
# allow run on master
kubectl taint nodes --all node-role.kubernetes.io/master-
echo === $(date) Provisioning - k8sInit $1 by $(whoami) end
SCRIPT
$k8sJoin = <<SCRIPT
# $1 - master/api server ip
echo === $(date) Provisioning - k8sJoin $1 by $(whoami) start
sudo sed -i '/master/d' /etc/hosts
sudo sed -i "1i$1 master" /etc/hosts
# add private key
curl -Lo $HOME/.ssh/vagrant https://raw.githubusercontent.com/hashicorp/vagrant/master/keys/vagrant
chmod 0600 $HOME/.ssh/vagrant
# join cluster
scp -q -o "StrictHostKeyChecking no" -i $HOME/.ssh/vagrant master:/tmp/kubeadm.log /tmp/kubeadm.log
token=$(cat /tmp/kubeadm.log |grep "kubeadm join"|head -1 |awk -Ftoken '{print $2}'|awk '{print $1}')
certhash=$(cat /tmp/kubeadm.log |grep discovery-token-ca-cert-hash|tail -1|awk '{print $2}')
sudo kubeadm join master:6443 --token $token \
--discovery-token-ca-cert-hash $certhash
# allow normal user to run kubectl
if [ -d $HOME/.kube ]; then
rm -r $HOME/.kube
fi
mkdir -p $HOME/.kube
scp -q -o "StrictHostKeyChecking no" -i $HOME/.ssh/vagrant master:$HOME/.kube/config $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
echo === $(date) Provisioning - k8sJoin $1 by $(whoami) end
SCRIPT
Vagrant.configure("2") do |config|
config.vm.box = "generic/ubuntu1804"
config.ssh.insert_key = false
config.vm.box_check_update = false
config.vm.define "master" do |master|
master.vm.hostname = "master"
master.vm.provider "hyperv" do |v|
v.vmname = "master"
v.memory = 2048
v.cpus = 1
end
master.vm.provision "shell", inline: $nic, args: "192.168.120.2", privileged: false
master.vm.provision "shell", inline: $dockerInstall, privileged: false
master.vm.provision "shell", inline: $k8sInstall, privileged: false
master.vm.provision "shell", inline: $k8sInit, args: "192.168.120.2", privileged: false
end
config.vm.define "worker" do |worker|
worker.vm.hostname = "worker"
worker.vm.provider "hyperv" do |v|
v.vmname = "worker"
v.memory = 1024
v.cpus = 1
end
worker.vm.provision "shell", inline: $nic, args: "192.168.120.3", privileged: false
worker.vm.provision "shell", inline: $dockerInstall, privileged: false
worker.vm.provision "shell", inline: $k8sInstall, privileged: false
worker.vm.provision "shell", inline: $k8sJoin, args: "192.168.120.2", privileged: false
end
end
k8s cluster test
vagrant@master:~$ k cluster-info
Kubernetes control plane is running at https://192.168.120.2:6443
CoreDNS is running at https://192.168.120.2:6443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy
To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.
vagrant@master:~$
vagrant@master:~$ k run test --image=nginx
pod/test created
vagrant@master:~$ k get po
NAME READY STATUS RESTARTS AGE
test 0/1 ContainerCreating 0 3s
vagrant@master:~$ k describe pod test
Name: test
Namespace: default
Priority: 0
Node: worker/192.168.120.3
Start Time: Sat, 30 Oct 2021 14:29:57 +0000
Labels: run=test
Annotations: cni.projectcalico.org/containerID: 177f5a3b56ed4a0e499956247b078712b4e7fa57e5cae1e67270e6e7c7aaf7dd
cni.projectcalico.org/podIP: 192.168.171.65/32
cni.projectcalico.org/podIPs: 192.168.171.65/32
Status: Running
IP: 192.168.171.65
IPs:
IP: 192.168.171.65
Containers:
test:
Container ID: docker://f76551b24d4cc2cb96788b4737df50f8431b5fc0efaaacfa521b136e03250c21
Image: nginx
Image ID: docker-pullable://nginx@sha256:644a70516a26004c97d0d85c7fe1d0c3a67ea8ab7ddf4aff193d9f301670cf36
Port: <none>
Host Port: <none>
State: Running
Started: Sat, 30 Oct 2021 14:30:21 +0000
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-dwxcw (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-dwxcw:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 3m4s default-scheduler Successfully assigned default/test to worker
Normal Pulling 3m3s kubelet Pulling image "nginx"
Normal Pulled 2m40s kubelet Successfully pulled image "nginx" in 22.319837942s
Normal Created 2m40s kubelet Created container test
Normal Started 2m40s kubelet Started container test
vagrant@master:~$ k get po
NAME READY STATUS RESTARTS AGE
test 1/1 Running 0 3m8s
vagrant@master:~$ curl 192.168.171.65
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
common issues
kubeadm error
+ sudo kubeadm init --pod-network-cidr=192.168.0.0/16
[init] Using Kubernetes version: v1.22.2
[preflight] Running pre-flight checks
error execution phase preflight: [preflight] Some fatal errors occurred:
[ERROR NumCPU]: the number of available CPUs 1 is less than the required 2
[ERROR Mem]: the system RAM (1255 MB) is less than the minimum 1700 MB
[ERROR Swap]: running with swap on is not supported. Please disable swap
[preflight] If you know what you are doing, you can make a check non-fatal with --ignore-preflight-errors=...
To see the stack trace of this error execute with --v=5 or higher
For test purpose, you can skip memory and cpu requirement warning,
sudo kubeadm init --pod-network-cidr=192.168.0.0/16 --ignore-preflight-errors=NumCPU --ignore-preflight-errors=Mem