树莓派安装 kubernetes v1.27.2

Ubuntu 22.04.2 LTS
ARM64位系统
kubernetes v1.27.2

以前写过一篇安装教程 https://blog.haohtml.com/archives/30924 ,当时安装的版本是 < v1.24.0 版本,由于k8s 从 v1.24.0 版本开始,弃用了 Dockershim 因此没有办法继续使用 Docker Engine 作为运行时,因此如果还想继续使用旧的运行时的话,则需要安装一个 cri-docker 的软件, 本文主要是介绍(版本 >=v1.24.0 )继续使用 Docker Engine 的安装方法,这里以最新版本 v1.27.1 为例。

安装环境初始化

以下内容来自:https://kubernetes.io/zh-cn/docs/setup/production-environment/container-runtimes/

执行下述指令:

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

sudo modprobe overlay
sudo modprobe br_netfilter

# 设置所需的 sysctl 参数,参数在重新启动后保持不变
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

# 应用 sysctl 参数而不重新启动
sudo sysctl --system

通过运行以下指令确认 br_netfilteroverlay 模块被加载:

lsmod | grep br_netfilter
lsmod | grep overlay

通过运行以下指令确认 net.bridge.bridge-nf-call-iptablesnet.bridge.bridge-nf-call-ip6tablesnet.ipv4.ip_forward 系统变量在你的 sysctl 配置中被设置为 1:

sysctl net.bridge.bridge-nf-call-iptables net.bridge.bridge-nf-call-ip6tables net.ipv4.ip_forward

上面添加的两个模块只有在 containerd 运行时才会用到(如果已安装过 docker 的话,则将自动安装此服务)。可以通过命令 systemctl cat containerd.service 查看对模块的引用关系

[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target

[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/bin/containerd

Type=notify
Delegate=yes
KillMode=process
Restart=always
RestartSec=5
# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity
LimitNOFILE=infinity
# Comment TasksMax if your systemd version does not supports it.
# Only systemd 226 and above support this version.
TasksMax=infinity
OOMScoreAdjust=-999

[Install]
WantedBy=multi-user.target

不过这里安装了这两个模块也没有任何影响。

安装 Docker

参考官方文档 https://docs.docker.com/engine/install/ubuntu/ 或aliyun 文档 https://developer.aliyun.com/mirror/docker-ce

修改 cgroupdriversystemd,同时为了国内访问docker镜像加速,需要设置一下aliyun的 docker 镜像地址

sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"exec-opts": ["native.cgroupdriver=systemd"],
"registry-mirrors": ["https://gfxrbz51.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

docker info 可以验证镜像是否设置成功。

安装 cri-docker

安装 cri-docker 以二进制方式安装,也可以使用官方介绍的源码编译方式安装

wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.2/cri-dockerd-0.3.2.arm64.tgz
tar -xf cri-dockerd-0.3.2.arm64.tgz
mv cri-dockerd/cri-dockerd /usr/bin/cri-dockerd
curl -L -o /etc/systemd/system/cri-docker.service https://github.com/Mirantis/cri-dockerd/raw/master/packaging/systemd/cri-docker.service
curl -L -o /etc/systemd/system/cri-docker.socket https://raw.githubusercontent.com/Mirantis/cri-dockerd/master/packaging/systemd/cri-docker.socket
systemctl daemon-reload
systemctl enable --now cri-docker.service
systemctl enable --now cri-docker.socket

启动服务后会生成一个 /var/run/cri-dockerd.sock文件,这个socket文件会在下面用到。

参考:https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/migrating-from-dockershim/migrate-dockershim-dockerd/

安装 kubernetes

$ sudo swapoff -a

建议永久关闭, 在 /etc/fstab 中注释掉 swapfile 这一行。

参考 https://developer.aliyun.com/mirror/kubernetes

安装 k8s 时,将自动安装一个 kubernetes-cni 包,这会在当前宿主机器上的 /opt/cni/bin 目录下看到一些cni插件命令

# ls /opt/cni/bin
bandwidth  bridge  dhcp  dummy  firewall  host-device  host-local  ipvlan  loopback  macvlan  portmap  ptp  sbr  static  tuning  vlan  vr

为了实现 docker 使用的 Cgroup Driver 与 kubelet 使用的 cgroup 的一致性,建议修改如下文件内容。

cat <<EOF > /etc/default/kubelet 
KUBELET_KUBEADM_ARGS="--container-runtime=remote --container-runtime-endpoint=/var/run/cri-dockerd.sock" KUBELET_EXTRA_ARGS="--cgroup-driver=systemd" 
EOF

下载k8s所需镜像

# kubeadm config images pull  --image-repository registry.aliyuncs.com/google_containers
Found multiple CRI endpoints on the host. Please define which one do you wish to use by setting the 'criSocket' field in the kubeadm configuration file: unix:///var/run/containerd/containerd.sock, unix:///var/run/cri-dockerd.sock
To see the stack trace of this error execute with --v=5 or higher

提示需要指定一个 cri, 我们这里使用cri-docker

# kubeadm config images pull  --image-repository registry.aliyuncs.com/google_containers --cri-socket unix:///var/run/cri-dockerd.sock

注意后续凡是 kubeadm 命令都要指定这个参数有点麻烦,目前没有找到合适的解决办法。

由于国内网络问题,还需要为 cri-dockerd 指定 infra 容器镜像,编辑文件 /etc/systemd/system/cri-docker.service,在 ExecStart 指令后面添加 --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.9, 这个镜像上面我们下载过的镜像,这行内容如下

ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.9

然后重启 cri-docker 服务。

systemctl daemon-reload && systemctl restart cri-docker

安装成功后,kubeadm、kubelet 和 kubectl 版本号是一样的,这里它们是写本教程时的最新版本 v1.27.1。

# kubectl version
WARNING: This version information is deprecated and will be replaced with the output from kubectl version --short. Use --output=yaml|json to get the full version.
Client Version: version.Info{Major:"1", Minor:"27", GitVersion:"v1.27.1", GitCommit:"4c9411232e10168d7b050c49a1b59f6df9d7ea4b", GitTreeState:"clean", BuildDate:"2023-04-14T13:21:19Z", GoVersion:"go1.20.3", Compiler:"gc", Platform:"linux/arm64"}
Kustomize Version: v5.0.1

注意 kubectl 还包含了 Kustomize 命令。

现在我们开始安装 k8s

# kubeadm reset --cri-socket=unix:///var/run/cri-dockerd.sock
# kubeadm init --image-repository registry.aliyuncs.com/google_containers --pod-network-cidr=10.244.0.0/16 --cri-socket unix:///var/run/cri-dockerd.sock --v=5

这里通过参数 --cri-socket指定了 CRI cri-dockerd.sock ,这个sock文件路径在上面的 cri-docker.socket 文件配置的。如果不填写则将自动检测,只有当您安装了多个 CRI 或有非标准的 CRI 套接字时才使用此选项。这正是以前 < v1.24.0 版本k8s安装的重要区别地方。

为了以防安装出错,方便排查问题,我们使用了日志等级 --v=5

默认情况下会自动安装与当前kubeadm版本号一致的k8s,另外如果你想安装指定版本号的 k8s 的话,也可以通过参数指定

--kubernetes-version v1.24.0

如果你服务器有多个IP地址的话,默认会读取每一个IP地址,你也可以通过参数指定IP

--apiserver-advertise-address=192.168.0.200

如果你想指定服务IP cidr 的话,则通过参数指定

--service-cidr=10.96.0.0/12

如果不指定此参数,则默认为 10.96.0.0./12

安装成功后,执行提示的命令

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

这时查看节点和Pods信息

# kubectl get nodes
NAME   STATUS     ROLES           AGE   VERSION
k8s   NotReady   control-plane   10m   v1.27.1

# kubectl get pods -A
NAMESPACE     NAME                         READY   STATUS   RESTARTS   AGE
kube-system   coredns-7bdc4cb885-lvlc9     0/1     Pending   0         10m
kube-system   coredns-7bdc4cb885-nxtd7     0/1     Pending   0         10m
kube-system   etcd-k8s                     1/1     Running   0         10m
kube-system   kube-apiserver-k8s           1/1     Running   0         10m
kube-system   kube-controller-manager-k8s   1/1     Running   0         10m
kube-system   kube-proxy-qbgtq             1/1     Running   0         10m
kube-system   kube-scheduler-k8s           1/1     Running   0         10m

由于没有安装网络插件,所以节点状态为 NotReady ,同时coredns两个 Pods 也不正常。

当调用 kubeadm init 时,kubelet 的配置会被写入磁盘 /var/lib/kubelet/config.yaml, 并上传到集群 kube-system 命名空间的 kubelet-config ConfigMap。 kubelet 配置信息也被写入 /etc/kubernetes/kubelet.conf,其中包含集群内所有 kubelet 的基线配置。 此配置文件指向允许 kubelet 与 API 服务器通信的客户端证书。 这解决了将集群级配置传播到每个 kubelet 的需求。

安装网络插件

这里主要介绍 Flannel 和 Calico 这两个插件,安装任何一个即可。

由于树莓派安装的 Ubuntu22.04.2 系统,而从 ubuntu 21.10版本以后默认移除了对 vxlan 的支持,因此手动安装 xvlan

sudo apt install -y linux-modules-extra-raspi

注意要保证其它加入集群的节点,支持 vxlan 命令。

安装 Flannel 网络插件

下面开始安装Flannel

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

这时发现coredns 状态仍不是 Running, 查看日志发现无法找到 flannel 生成的网络配置文件 /run/flannel/subnet.env, 我们手动创建它,内容如下

FLANNEL_NETWORK=10.244.0.0/16
FLANNEL_SUBNET=10.244.0.1/24
FLANNEL_MTU=1450
FLANNEL_IPMASQ=false

注意每行末尾没有空格,然后重新部署 flannel 即可。

安装 Calico 网络插件

除了上面提到的 flannel 外,还有 Calico 这个网络插件,我在arm64下安装 flannel 一直遇到容器频繁的重建问题,后来将flannel卸载掉安装了 Calico 解决了问题。

安装教程参考https://docs.tigera.io/calico/latest/getting-started/kubernetes/quickstart#install-calico

  1. 安装 calico operator  和 CRD
# kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.1/manifests/tigera-operator.yaml

2. 安装 calico CR

同于 calico 默认的 pod-netowkr-cidr 使用的是 192.168.0.0/16 这个配置,与我们上面指定的 cidr 不一致, 因此没有办法直接用上面的远程安装的方法安装。只能先将文件下载到本地,然后修改 cidr 配置为 10.244.0.0/16 ,然后再应用。

# wget https://raw.githubusercontent.com/projectcalico/calico/v3.25.1/manifests/custom-resources.yaml | sed 's/192.168/10.244/g' > custome-resources2.yaml
# kubectl create -f custom-resources2.yaml

3. 确认所有pod都在运行

# watch kubectl get pods -n calico-system
NAME                                       READY   STATUS    RESTARTS   AGE
calico-kube-controllers-789dc4c76b-k25kx   1/1     Running   0          6m51s
calico-node-7tf8t                          1/1     Running   0          24m
calico-typha-64b645b8b6-4nqmj              1/1     Running   0          24m
csi-node-driver-pm528                      2/2     Running   0          24m

等到每个 pod 的状态都为 Running。

如果安装完 Calico 插件后,Master 节点仍是 NotReady 状态,则重启一下 containerd 服务一般就可以解决。

安装完  Calico  后将在 /etc/cni/net.d 目录里创建两个配置文件

# ls /etc/cni/net.d
10-calico.conflist  calico-kubeconfig

其中 calico-kubeconfig 内容是与 k8s apiserver 通讯证书配置,而 10-calico-conflist 则是 pod网络配置

# cat /etc/cni/net.d/10-calico.conflist
{
  "name": "k8s-pod-network",
  "cniVersion": "0.3.1",
  "plugins": [
    {
      "type": "calico",
      "datastore_type": "kubernetes",
      "mtu": 0,
      "nodename_file_optional": false,
      "log_level": "Info",
      "log_file_path": "/var/log/calico/cni/cni.log",
      "ipam": { "type": "calico-ipam", "assign_ipv4" : "true", "assign_ipv6" : "false"},
      "container_settings": {
          "allow_ip_forwarding": false
      },
      "policy": {
          "type": "k8s"
      },
      "kubernetes": {
          "k8s_api_root":"https://10.96.0.1:443",
          "kubeconfig": "/etc/cni/net.d/calico-kubeconfig"
      }
    },
    {
      "type": "bandwidth",
      "capabilities": {"bandwidth": true}
    },
    {"type": "portmap", "snat": true, "capabilities": {"portMappings": true}}
  ]
}

这里对IP地址管理插件使用的 calico-ipam 这个二进制文件,它同样是在安装 calico 网络插件以后创建的,除此之外另外还生成了 calicoinstall 二进制文件,它位于我们上面提到的 /opt/cni/bin/ 目录。可以看一下 plugins 子对象的 type 字段配置的就是 /opt/cni/bin 目录下的二进制文件。

部署 pod 测试

现在我们整个k8s基本安装完毕,我们看一下节点与静态pod服务状态

➜  ubuntu kubectl get nodes
NAME   STATUS   ROLES           AGE     VERSION
k8s   Ready   control-plane   4d12h   v1.27.1

➜ ubuntu kubectl get pods -A
NAMESPACE     NAME                         READY   STATUS   RESTARTS       AGE
kube-flannel   kube-flannel-ds-vbzqt         1/1     Running   11 (17m ago)   32m
kube-system   coredns-7bdc4cb885-gqcgd     1/1     Running   1 (22m ago)   4d12h
kube-system   coredns-7bdc4cb885-n47g8     1/1     Running   1 (22m ago)   4d12h
kube-system   etcd-k8s                     1/1     Running   1 (22m ago)   4d12h
kube-system   kube-apiserver-k8s           1/1     Running   1 (22m ago)   4d12h
kube-system   kube-controller-manager-k8s   1/1     Running   1 (22m ago)   4d12h
kube-system   kube-proxy-qcmck             1/1     Running   1 (22m ago)   4d12h
kube-system   kube-scheduler-k8s           1/1     Running   1 (22m ago)   4d12h

➜ ubuntu kubectl get svc -A
NAMESPACE     NAME         TYPE       CLUSTER-IP   EXTERNAL-IP   PORT(S)                 AGE
default       kubernetes   ClusterIP   10.96.0.1   <none>       443/TCP                 4d12h
kube-system   kube-dns     ClusterIP   10.96.0.10   <none>       53/UDP,53/TCP,9153/TCP   4d12h

现在节点状态处于 Ready,所有pod也处于Running状态,同时还有两个 service

现在我们测试利用 deployment 来创建一些pod,由于我这里只有一台机器,为了使用pod也可以被调度到这个节点,还需对此节点进行容忍污点

# kubectl describe node k8s
Taints:             node-role.kubernetes.io/control-plane:NoSchedule

...

# kubectl taint nodes k8s node-role.kubernetes.io/control-plane:NoSchedule-

创建一个 nginx-deployment.yml文件,内容如下

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.23-alpine
        ports:
        - name: http
          containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  #loadBalancerIP: x.y.z.a  # 指定公网IP
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer

这里指定了副本 replicas:3表示创建三个pod,同时声明了一个服务 nginx。

应用配置

kubectl apply -f nginx-deployment.yml

观察pod状态

➜  ubuntu kubectl get deployment
NAME   READY   UP-TO-DATE   AVAILABLE   AGE
nginx   3/3     3           3           32m
➜ ubuntu kubectl get pods
NAME                   READY   STATUS   RESTARTS   AGE
nginx-984448cf6-bm8xx   1/1     Running   0         32m
nginx-984448cf6-kvhgf   1/1     Running   0         32m
nginx-984448cf6-xtnv5   1/1     Running   0         32m

可以看到 pod 创建成功,但此时服务并分配到任何IP地址,下面我们通过 metallb 来解决这个问题。

crictl

crictl 是一款 Kubelet 容器运行时接口 (CRI) 的 CLI 和验证工具。 crictl 和它的源代码在 cri-tools 代码库。 crictl 不是一个通用的工作流工具,而是一个对调试有用的工具。 介绍文档 https://github.com/kubernetes-sigs/cri-tools/blob/master/docs/crictl.md

首次使用时,需要创建一个默认配置文件 crictl.yaml ,对其进行一些配置

$ cat /etc/crictl.yaml
runtime-endpoint: unix:///var/run/cri-dockerd.sock
image-endpoint: unix:///var/run/cri-dockerd.sock
timeout: 2
debug: true
pull-image-on-create: false

然后我们就可以通过 crictl images 这类的命令查看当前节点的信息,它可以代替一些docker命令,如

ubuntu@k8s:~$ sudo crictl images
DEBU[0000] get image connection
DEBU[0000] ListImagesRequest: &ListImagesRequest{Filter:&ImageFilter{Image:&ImageSpec{Image:,Annotations:map[string]string{},},},}
DEBU[0000] ListImagesResponse: &ListImagesResponse{Images:[]*Image{&Image{Id:sha256:78d0a9e0b092c0dc48cd00423a41785a9ba2980208441c51b9d4adda775dd5a9,RepoTags:[registry.aliyuncs.com/google_containers/kube-apiserver:v1.27.1],RepoDigests:[registry.aliyuncs.com/google_containers/kube-apiserver@sha256:a6daed8429c54f0008910fc4ecc17aefa1dfcd7cc2ff0089570854d4f95213ed],Size_:114714350,Uid:nil,Username:,Spec:nil,Pinned:false,},&Image{Id:sha256:20654f2150dd55dcd4d6c6a7912c34e2d5bf63430f22142e2baabc4612f17d6a,RepoTags:[registry.aliyuncs.com/google_containers/kube-controller-manager:v1.27.1],RepoDigests:[registry.aliyuncs.com/google_containers/kube-controller-manager@sha256:ed43c8f8a78f7bcda715b8427da17a62b22befe236acdc90b24db411e106f481],Size_:107243539,Uid:nil,Username:,Spec:nil,Pinned:false,},&Image{Id:sha256:717e2b2b33bd0fb8a8ce56d1ed46a129e35fd7dc7a41c4079c50e1bf47e61bda,RepoTags:[registry.aliyuncs.com/google_containers/kube-scheduler:v1.27.1],RepoDigests:[registry.aliyuncs.com/google_containers/kube-scheduler@sha256:0b942e32d0d30ca47e2e133e421c7d3bb9cdb01ee972e56d3098e6aced3cdd8a],Size_:56125459,Uid:nil,Username:,Spec:nil,Pinned:false,},&Image{Id:sha256:4ecafc4f7bda115b588241af03ef4a9feb5ec204a2c939ad9bf97b67d728a018,RepoTags:[registry.aliyuncs.com/google_containers/kube-proxy:v1.27.1],RepoDigests:[registry.aliyuncs.com/google_containers/kube-proxy@sha256:958ddb03a4d4d7a567d3563c759a05f3e95aa42ca8af2964aa76867aafc43610],Size_:66499356,Uid:nil,Username:,Spec:nil,Pinned:false,},&Image{Id:sha256:829e9de338bd5fdd3f16f68f83a9fb288fbc8453e881e5d5cfd0f6f2ff72b43e,RepoTags:[registry.aliyuncs.com/google_containers/pause:3.9],RepoDigests:[registry.aliyuncs.com/google_containers/pause@sha256:7031c1b283388d2c2e09b57badb803c05ebed362dc88d84b480cc47f72a21097],Size_:514000,Uid:nil,Username:,Spec:nil,Pinned:false,},},}
IMAGE                                                             TAG                 IMAGE ID           SIZE
registry.aliyuncs.com/google_containers/kube-apiserver           v1.27.1             78d0a9e0b092c       115MB
registry.aliyuncs.com/google_containers/kube-controller-manager   v1.27.1             20654f2150dd5       107MB
registry.aliyuncs.com/google_containers/kube-proxy               v1.27.1             4ecafc4f7bda1       66.5MB
registry.aliyuncs.com/google_containers/kube-scheduler           v1.27.1             717e2b2b33bd0       56.1MB
registry.aliyuncs.com/google_containers/pause                     3.9                 829e9de338bd5       514kB

在配置文件里启用了debug,因此最上面才会输出一些信息,一般我们只需要将 debug 设置为 false

安装 MetalLB

安装教程参考:https://metallb.universe.tf/installation/

MetalLB 分别使用两个组件来实现了上述两个功能:

  • Controller:实现地址分配,以 Deployment 方式运行,用于监听 Service 的变更,分配/回收 IP 地址。
  • Speaker:实现地址对外广播,以 DaemonSet 方式运行,对外广播 Service 的 IP 地址。
  1. 安装MetalLB
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.9/config/manifests/metallb-native.yaml

2. 配置分配给 Load Balancer Services 的IP池

apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
  name: first-pool
  namespace: metallb-system
spec:
  addresses:
  - 192.168.10.0/24
  - 192.168.9.1-192.168.9.5
  - fc00:f853:0ccd:e799::/124
#  autoAssign: false

如果要指定固定的IP的话,则可以使用 192.168.100.10/32,根据需要修改配置,改成我们计划分配的IP段。

默认情况下,MetalLB将从任何配置的具有可用地址的地址池中分配IP。这可能最终会为不需要的服务使用“昂贵”的地址。

为了防止这种行为,可以通过将autoAssign标志设置为false来禁用池的自动分配。

一旦IP被分配给服务,就必须对其进行公告,主要有两种方式

对于 Layer2 这种配置比较简单,对于大型复杂的网络推荐使用BGP这种配置。

这里为了方便,使用了 Layer2 配置

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system

在 L2Advertisement 实例中设置没有 IPAddressPool 选择器被解释为该实例与所有可用的 IPAddressPools 相关联。

因此,如果有专门的IPAddressPools,并且只有其中一些必须通过L2进行广告,则必须声明我们想要广告IP的IPAddress Pools列表(或者,可以使用标签选择器)

apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
  name: example
  namespace: metallb-system
spec:
  ipAddressPools:
  - first-pool

确认安装是否成功

# kubectl get pods -n metallb-system
NAME                          READY   STATUS    RESTARTS   AGE
controller-7948676b95-xlxc7   1/1     Running   0          3m24s
speaker-nrfsj                 1/1     Running   0          3m23s

现在我们再看下对应的服务

# kubectl get svc
NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
kubernetes   ClusterIP      10.96.0.1       <none>          443/TCP        175m
nginx        LoadBalancer   10.105.216.80   192.168.3.220   80:32116/TCP   18s

可以看到此服务分配的IP地址为 `192.168.3.220`,我们用 curl 访问一下

# curl 192.168.3.220
<!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>

可以看到服务是正常的。这里需要说明一下,由是这个ip地址是虚拟IP,只体现在iptables里,因此没有办法通过ping 命令来测试服务是否正常,不过可以通过 telnet 192.168.3.220 80 来测试端口是否正常。

节点加入

如果需要将其它节点加入集群的话,则与上面一样。服务器环境需要先禁用 swap, 然后安装必须的安装,如 dockercri-dockerkubeadmkubelet 软件,不要忘记了对 cri-docker--pod-infra-container-image 配置。

执行

# sudo kubeadm join 192.168.3.200:6443 --token ulzmn3.98oc7ftd9or3zb42 --discovery-token-ca-cert-hash sha256:1087d7988af2fb1ca066af6d1be8f4ace32f1dd4323cfc756c988093a20d6276  --cri-socket=unix:///var/run/cri-dockerd.sock

[preflight] Running pre-flight checks
[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...

这里命令也需要指定 cri-docker 参数,过一段时间可以看到新节点已经从 NotReady 变为 Ready 状态,此时我们看一下新节点下载的镜像。

# docker images
REPOSITORY                                           TAG           IMAGE ID       CREATED        SIZE
registry.aliyuncs.com/google_containers/kube-proxy   v1.27.2       29921a084542   10 days ago    66.5MB
calico/typha                                         v3.25.1       1fdaa52c5843   8 weeks ago    59.1MB
calico/cni                                           v3.25.1       d314245e1354   8 weeks ago    192MB
calico/pod2daemon-flexvol                            v3.25.1       1af18ea84b4d   8 weeks ago    10.5MB
calico/node                                          v3.25.1       273313a9d5b2   8 weeks ago    254MB
quay.io/metallb/speaker                              v0.13.9       e39bc116ce9a   3 months ago   111MB
registry.aliyuncs.com/google_containers/pause        3.9           829e9de338bd   7 months ago   514kB

这里 registry.aliyuncs.com/google_containers/pause 是在 cri-docker 服务里指定的镜像。

注意有些镜像需要在每个节点都会存在,如 kube-proxycalico-nodecsi-node-drivermetallb 中的 speaker

同时每个节点也会创建一个 vxlan.calico 的网卡

# ifconfig
vxlan.calico: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1450
        inet 10.244.166.128  netmask 255.255.255.255  broadcast 0.0.0.0
        inet6 fe80::64f9:37ff:fec3:7e94  prefixlen 64  scopeid 0x20<link>
        ether 66:f9:37:c3:7e:94  txqueuelen 0  (Ethernet)
        RX packets 0  bytes 0 (0.0 B)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 0  bytes 0 (0.0 B)
        TX errors 0  dropped 12 overruns 0  carrier 0  collisions 0

一定要确保节点存在此网卡,否则可能跨节点之间的 pod 无法互通的问题,在这一块深有体会。

这里介绍一下 Calicooverlay 网络(文档),对于其实现有两种封装方式,分别为 IPIPVXLAN 。这两种都是通过对数据包进行封装实现,发送端封包而接收端解包。其中 IPIP 也称为 IP IN IP, 这种封包方式需要 BGP(文档) ,而 BGP 又分两种拓扑方式,分别为 Full-mesh (默认拓扑) 和 Route reflectors。对于节点数 <= 100 的话,一般 Full-mesh 就够用了,而对于节点数 >100 的场景一般推荐使用 Router reflectors 这种拓扑方式。

这两种类型的封装之间有一个小区别是 CalicoVXLAN 实现不使用 BGP ,而 CalicoIP-in-IP 实现在 Calico 节点之间使用 BGP

Calico 的默认 overlay 方式为 VXLAN,因此安装后,在节点会出现一个vxlan.calico 虚拟网卡

常见问题

  1. 如果遇到 kube-flannel 一直重启的问题,并用命令 journalctl -f -u kubelet.service 查看发现以下错误。
    May 21 23:57:25 k8s kubelet[753]: I0521 23:57:25.453851 753 scope.go:115] "RemoveContainer" containerID="653cde487a076855b7b37943eeda30bacd1ca02b97d58d8e9b5d3ddece9697d8"May 21 23:57:25 k8s kubelet[753]: E0521 23:57:25.461431 753 pod_workers.go:1281] "Error syncing pod, skipping" err="failed to "StartContainer" for "kube-flannel" with CrashLoopBackOff: "back-off 5m0s restarting failed container=kube-flannel pod=kube-flannel-ds-zm59x_kube-flannel(c6ac2bdf-42a8-4b5e-b09b-6adb6090c1aa)"" pod="kube-flannel/kube-flannel-ds-zm59x" podUID=c6ac2bdf-42a8-4b5e-b09b-6adb6090c1aa
    一直未找到是由什么原因引起的,因此我安装了Calico 这个网络插件
  2. 使用 calico 网络插件,发现不同节点之间的 pod 之间无法通讯。
    请确保通讯节点之间是否存在 vxlan-calico (vxlanMode)这个网卡 或 tunl0 (ipipMode)网卡。

参考文章