裸机部署k8s集群平台

为何要裸机部署k8s?为什么不用minikube?其实本地裸机部署可以对k8s有更加深入的了解,而且k8s涉及的概念很多,如果没有实际操作基本上很难理解这些概念以及如何使用。

除此之外,minikube是主从单个节点部署在一台机器上,对于学习k8s不是特别有帮助。

裸机部署k8s

预备环境:

  • Parallel Desktop虚拟机 下载地址
  • macOS 12 M1
  • master主节点,slave1和slave2工作节点,实现一主二从
  • Ubuntu 20 Server for ARM 镜像 下载地址
  • 每个节点至少2GB内存,2核CPU,所以确保宿主主机内存 >= 16GB
  • master: 192.168.2.104
  • slave1: 192.168.2.109
  • slave2: 192.168.2.111
  • 科学上网,自备梯子

另外说一句就是在我没有购买PD之前我是在VMware Fusion上搭建的,这应该和PD搭建是一样的。另外要注意的是,如果搭建了一个虚拟机后就 克隆 这个虚拟机,之后可能网络会有问题,所以还是建议手动一个一个搭建。

配置master和所有slave

ssh 密钥登陆

1
2
3
4
# 复制公钥文件到节点
scp .ssh/id_rsa.pub [email protected]:/root/.ssh/authorized_keys
scp .ssh/id_rsa.pub [email protected]:/root/.ssh/authorized_keys
scp .ssh/id_rsa.pub [email protected]:/root/.ssh/authorized_keys
1
2
3
4
5
vim /etc/ssh/sshd_config
# 添加
PermitRootLogin yes
RSAAuthentication yes
PubkeyAuthentication yes

修改master IP地址和MAC地址

其实这里应该不需要修改的,至少我在PD上测试时不用(可能是我没有将虚拟机关闭的原因)
另外要注意 macaddress 需要和 PD/VMware上查看的一样

1
vim /etc/netplan/00-installer-config.yaml
1
2
3
4
5
6
7
8
9
10
11
12
network:
ethernets:
ens160: #配置的网卡的名称
match:
macaddress: 00:50:56:29:1C:37
addresses: [192.168.2.104/24] #配置的静态ip地址和掩码
dhcp4: no
gateway4: 192.168.2.2
nameservers:
addresses: [114.114.114.114,8.8.8.8]
version: 2
renderer: networkd

修改slave IP地址和Mac地址

对于PD来说,可修改也可不修改(除非你需要关闭虚拟机,不过一般都是暂停)

以下YAML文件中的Mac地址需要和PD/VMware虚拟机网络适配器中生成的Mac地址对应

1
vim /etc/netplan/00-installer-config.yaml

slave1

1
2
3
4
5
6
7
8
9
10
11
12
network:
ethernets:
ens160: #配置的网卡的名称
match:
macaddress: 00:0C:29:7F:AF:F6
addresses: [192.168.2.109/24] #配置的静态ip地址和掩码
dhcp4: no #关闭DHCP,如果需要打开DHCP则写yes
gateway4: 192.168.2.2
nameservers:
addresses: [114.114.114.114,8.8.8.8]
version: 2
renderer: networkd #指定后端采用systemd-networkd或者Network Manager,可不填写则默认使用systemd-workd

slave2
同上

1
2
3
4
5
6
7
8
9
10
11
12
13
network:
ethernets:
ens160: #配置的网卡的名称
match:
macaddress: 00:50:56:37:E5:AA
addresses: [192.168.2.111/24] #配置的静态ip地址和掩码
dhcp4: no #关闭DHCP,如果需要打开DHCP则写yes
gateway4: 192.168.2.2
nameservers:
addresses: [114.114.114.114,8.8.8.8]

version: 2
renderer: networkd #指定后端采用systemd-networkd或者Network Manager,可不填写则默认使用systemd-workd

使配置生效!!!

1
netplan apply

设置主机名

1
2
3
4
5
6
# master
hostnamectl set-hostname master
# slave1
hostnamectl set-hostname slave1
# slave2
hostnamectl set-hostname slave2

修改hosts

1
2
3
4
# 添加以下内容到 /etc/hosts
192.168.2.104 master
192.168.2.109 slave1
192.168.2.111 slave2

关闭swap

一定要关闭swap!!!

1
2
3
vim /etc/fstab
# 注释掉
#/swap.img none swap sw 0 0

安装docker engine

1
2
3
4
5
6
7
8
9
10
11
sudo apt-get update
sudo apt-get install 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
sudo apt-get install docker-ce docker-ce-cli containerd.io

安装kubeadm

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sudo apt-get update
sudo apt-get install -y ca-certificates curl software-properties-common apt-transport-https curl

curl -s https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -

tee /etc/apt/sources.list.d/kubernetes.list <<EOF
deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main
EOF

apt-get update
apt-get install -y kubelet kubeadm kubectl

systemctl enable kubelet
systemctl start kubelet
systemctl enable docker
systemctl start docker

修改docker配置

这里可以使用阿里云的镜像源

1
2
3
4
5
6
7
8
9
10
11
# kubernetes 官方推荐 docker 等使用 systemd 作为 cgroupdriver,否则 kubelet 启动不了
cat <<EOF > daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"],
"registry-mirrors": ["https://4xorsfia.mirror.aliyuncs.com"]
}
EOF
mv daemon.json /etc/docker/

systemctl daemon-reload
systemctl restart docker

用 kubeadm 初始化集群(仅master运行)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
kubeadm init \
--apiserver-advertise-address=192.168.2.104 \
--image-repository registry.aliyuncs.com/google_containers \
--pod-network-cidr=10.244.0.0/16

# 重置
# kubeadm reset -f

# 复制授权文件,以便 kubectl 可以有权限访问集群
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config

# 记住 kubeadm init 最后输出的命令,形如 kubeadm join XXXX ,后续需要在salve工作节点上执行将其加入到集群中
# kubeadm join 192.168.2.104:6443 --token ipk042.ctnrypl0wz3d9nle --discovery-token-ca-cert-hash sha256:0066e13bc73fe2c1c596fc53d6d5660030681b4fc74b4605e09cbfa523b466c7

把salve工作节点加入集群(仅slave运行)

分别在两台工作节点上执行:

1
kubeadm join 192.168.2.104:6443 --token ipk042.ctnrypl0wz3d9nle --discovery-token-ca-cert-hash sha256:0066e13bc73fe2c1c596fc53d6d5660030681b4fc74b4605e09cbfa523b466c7 

安装网络插件flannel,否则 slave 是 NotReady 状态(master运行)

可能需要科学上网,或者在宿主主机下载后上传到master节点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 下载
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml

# 修改yml文件,执行:
vim kube-flannel.yml
# 需要在kube-flannel.yml中使用 -–iface参数 指定集群主机内网网卡的名称,否则可能会出现dns无法解析
# 找到行 "--kube-subnet-mgr" ,在其下增加如下一行:
"- --iface=enp0s5"(用实际网卡名替换,通过 ip a 查看)

# 应用插件
kubectl apply -f ./kube-flannel.yml

# 如果全部为Running说明成功
╭─root@master ~
╰─# kubectl get pods --all-namespaces
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-6d8c4cb4d-c7hxb 1/1 Running 0 6m28s
kube-system coredns-6d8c4cb4d-msp7c 1/1 Running 0 6m28s
kube-system etcd-master 1/1 Running 5 6m42s
kube-system kube-apiserver-master 1/1 Running 5 6m42s
kube-system kube-controller-manager-master 1/1 Running 1 (6m7s ago) 6m42s
kube-system kube-flannel-ds-ghjjl 1/1 Running 0 62s
kube-system kube-flannel-ds-ppsd2 1/1 Running 0 59s
kube-system kube-flannel-ds-r7wnb 1/1 Running 0 3m44s
kube-system kube-proxy-5hchr 1/1 Running 0 62s
kube-system kube-proxy-7z2bp 1/1 Running 0 59s
kube-system kube-proxy-m5lfm 1/1 Running 0 6m28s
kube-system kube-scheduler-master 1/1 Running 6 (6m9s ago) 6m42s

如果遇到了coredns的崩溃,原因是coredns配置文件中的loop参数导致,使用kubectl -n kube-system edit configmap coredns: 删除即可,然后重启服务:(我使用PD搭建时没有这个问题,也可能是因为我之前VMware克隆了虚拟机导致的)

1
kubectl get pods -n kube-system -oname |grep coredns |xargs kubectl delete -n kube-system

测试test-k8s

至此基本上算是完成部署了,下面我们来编写一个yaml清单文件测试一下

app.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: apps/v1
kind: Deployment
metadata:
# 部署名字
name: test-k8s
spec:
# 一次性定义多个副本
replicas: 5
# 用来查找关联的 Pod,所有标签都匹配才行
selector:
matchLabels:
app: test-k8s
# 定义 Pod 相关数据
template:
metadata:
labels:
app: test-k8s
spec:
# 定义容器,可以多个
containers:
- name: test-k8s # 容器名字
image: registry.cn-hangzhou.aliyuncs.com/josexy/test-k8s:1.0.0 # 镜像

上面那个test-k8s测试镜像是我用go gin写的一个简单web服务,当访问主页时打印出当前环境的IP地址和主机名。
需要注意的是该镜像是基于 linux/arm64/v8 的,可能需要替换为其他的镜像或者自行构建

1
2
# 应用
kubectl apply -f app.yaml

之后等待全部pods运行完成

1
2
3
4
5
6
7
8
╭─root@master ~/work/k8s 
╰─# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test-k8s-777b86fcb4-8mq6k 1/1 Running 0 135m 10.244.2.48 slave2 <none> <none>
test-k8s-777b86fcb4-9jvpv 1/1 Running 0 135m 10.244.1.59 slave1 <none> <none>
test-k8s-777b86fcb4-h6j64 1/1 Running 0 135m 10.244.1.57 slave1 <none> <none>
test-k8s-777b86fcb4-mm5wk 1/1 Running 0 135m 10.244.1.58 slave1 <none> <none>
test-k8s-777b86fcb4-tg279 1/1 Running 0 135m 10.244.2.49 slave2 <none> <none>

可见创建的5个pods副本被k8s调度到了不同的节点上

现在通过curl访问 10.244.2.48 服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
> curl 10.244.2.48:10086

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Test K8S</title>
</head>

<body>
<p>Hello k8s</p>
<p>HostName: test-k8s-777b86fcb4-8mq6k</p>
<p>IP: 10.244.2.48</p>
</body>

</html>

可以看到访问的服务是正确的。

kubernetes 在pod内无法ping通servicename和ClusterIP的解决方法(所有节点)

原因:kube-proxy使用了iptable模式,修改为ipvs模式则可以在pod内ping通clusterIP或servicename ,需要使用 ipvs 替换iptables。

可以通过以下命令查看是否是ipvs模式

1
2
$ kubectl get cm kube-proxy -n kube-system -o yaml| grep mode
mode: "ipvs"

开启kube-proxy ipvs模式的前提条件,由于ipvs已经加入到了内核的主干,所以为kube-proxy开启ipvs的前提需要加载以下的内核模块

1
2
3
4
5
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack_ipv4 或者 nf_conntrack

加载模块shell脚本

1
2
3
4
5
6
7
8
9
10
11
cat > ipvsset.sh <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack
EOF

chmod +x ipvsset.sh && sudo ./ipvsset.sh
lsmod | grep -e ip_vs -e nf_conntrack

然后需要在 每一个节点上 安装ipset,为了便于ipvs的代理查看,可以安装管理工具ipvsadm

1
apt install ipset ipvsadm
kube-proxy设置ipvs
1
kubectl edit cm kube-proxy -n kube-system

修改 ConfigMap 的 kube-system/kube-proxy 中的config.conf,mode: "ipvs"

之后重启各个节点上的kube-proxy pod:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
$ kubectl get pod -n kube-system | grep kube-proxy | awk '{system("kubectl delete pod "$1" -n kube-system")}'

$ kubectl get pod -n kube-system -o wide| grep kube-proxy
kube-proxy-cx4k4 1/1 Running 0 10m 192.168.2.104 master <none> <none>
kube-proxy-nnc68 1/1 Running 0 10m 192.168.2.111 slave2 <none> <none>
kube-proxy-qdg5h 1/1 Running 0 11m 192.168.2.109 slave1 <none> <none>


# 日志中显示ipvs,说明开启了ipvs模式
$ kubectl logs kube-proxy-cx4k4 -n kube-system
I0217 03:40:41.109206 1 node.go:163] Successfully retrieved node IP: 192.168.2.104
I0217 03:40:41.109237 1 server_others.go:138] "Detected node IP" address="192.168.2.104"
I0217 03:40:41.120284 1 server_others.go:269] "Using ipvs Proxier"
I0217 03:40:41.120297 1 server_others.go:271] "Creating dualStackProxier for ipvs"
I0217 03:40:41.120313 1 server_others.go:491] "Detect-local-mode set to ClusterCIDR, but no IPv6 cluster CIDR defined, , defaulting to no-op detect-local for IPv6"
I0217 03:40:41.121365 1 proxier.go:438] "IPVS scheduler not specified, use rr by default"
I0217 03:40:41.121452 1 proxier.go:438] "IPVS scheduler not specified, use rr by default"

这样就可以在Pod内(docker)ping通ClusterIP或者servicename了:

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
╭─root@master ~ 
╰─# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 12h
test-k8s ClusterIP 10.107.42.94 <none> 10086/TCP 38m

# 进入Pod内docker容器
╭─root@master ~
╰─# kubectl exec -it test-k8s-d4ddc8449-lkgg4 -- bash

root@test-k8s-d4ddc8449-lkgg4:/code/go/src# curl http://10.107.42.94:10086

<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Test K8S</title>
</head>

<body>
<p>Hello k8s</p>
<p>HostName: test-k8s-d4ddc8449-lkgg4</p>
<p>IP: 10.244.2.10</p>
</body>

</html>

Pod 报错解决

如果你运行 kubectl describe pod/pod-name 发现 Events 中有下面这个错误

1
networkPlugin cni failed to set up pod "test-k8s-68bb74d654-mc6b9_default" network: open /run/flannel/subnet.env: no such file or directory

在每个节点创建文件/run/flannel/subnet.env写入以下内容即可解决

1
2
3
4
FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=true

然后重新部署

1
kubectl rollout restart deployment test-k8s

查看节点(master运行)

1
2
3
4
5
6
╭─root@master ~ 
╰─# kubectl get nodes -o wide
NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME
master Ready control-plane,master 6m1s v1.23.3 192.168.2.104 <none> Ubuntu 20.04.3 LTS 5.4.0-99-generic docker://20.10.12
slave1 Ready <none> 18s v1.23.3 192.168.2.109 <none> Ubuntu 20.04.3 LTS 5.4.0-99-generic docker://20.10.12
slave2 Ready <none> 15s v1.23.3 192.168.2.111 <none> Ubuntu 20.04.3 LTS 5.4.0-99-generic docker://20.10.12

主节点

1
2
3
4
5
6
7
8
9
10
11
12
╭─root@master ~ 
╰─# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
rancher/mirrored-flannelcni-flannel v0.16.3 2830f9802082 2 weeks ago 61.7MB
registry.aliyuncs.com/google_containers/kube-apiserver v1.23.3 8e7422f73cf3 3 weeks ago 132MB
registry.aliyuncs.com/google_containers/kube-proxy v1.23.3 d36a89daa194 3 weeks ago 109MB
registry.aliyuncs.com/google_containers/kube-controller-manager v1.23.3 3e63a2140741 3 weeks ago 122MB
registry.aliyuncs.com/google_containers/kube-scheduler v1.23.3 4bad79a8953b 3 weeks ago 53MB
rancher/mirrored-flannelcni-flannel-cni-plugin v1.0.1 05932afd0a64 3 weeks ago 7.76MB
registry.aliyuncs.com/google_containers/etcd 3.5.1-0 1040f7790951 3 months ago 132MB
registry.aliyuncs.com/google_containers/coredns v1.8.6 edaa71f2aee8 4 months ago 46.8MB
registry.aliyuncs.com/google_containers/pause 3.6 7d46a07936af 5 months ago 484kB

工作节点

1
2
3
4
5
6
7
╭─root@slave1 ~ 
╰─# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
rancher/mirrored-flannelcni-flannel v0.16.3 2830f9802082 2 weeks ago 61.7MB
registry.aliyuncs.com/google_containers/kube-proxy v1.23.3 d36a89daa194 3 weeks ago 109MB
rancher/mirrored-flannelcni-flannel-cni-plugin v1.0.1 05932afd0a64 3 weeks ago 7.76MB
registry.aliyuncs.com/google_containers/pause 3.6 7d46a07936af 5 months ago 484kB

zsh 自动补全 kubectl

kubectl 通过命令 kubectl completion zsh 生成 Zsh 自动补全脚本。 在 shell 中导入(Sourcing)该自动补全脚本,将启动 kubectl 自动补全功能。

为了在所有的 shell 会话中实现此功能,请将下面内容加入到文件 ~/.zshrc 中。

1
source <(kubectl completion zsh)

部署和访问 Kubernetes 仪表板Dashboard(不完整)

在Master节点上运行以下命令安装dashboard插件(可能被墙)

这部分不是完整的,因此可以忽略

1
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.4.0/aio/deploy/recommended.yaml

未完待续

虽然从最开始到现在算是成功在PD上部署了k8s集群环境,但是有一些功能是没有的。比如裸机环境下不支持 LoadBalancer 负载均衡器,需要而外安装 MetalLB(裸金属负载均衡器),这将在下一篇继续介绍。

参考