kubeadm 简介
kubeadm 是 Kubernetes(以下简称 k8s)官方提供的用于快速安装部署 k8s 集群的工具,伴随 k8s 每个版本的发布都会同步更新,kubeadm 会对集群配置方面的一些实践做调整,通过实验 kubeadm 可以学习到 k8s 官方在集群配置上一些新的最佳实践。
这个工具能通过两条指令完成一个 k8s 集群的部署:
# 创建一个 Master 节点
$ kubeadm init# 将一个 Node 节点加入到当前集群中
$ kubeadm join <Master节点的IP和端口 >
使用 kubeadm 工具大大简化了 k8s 的部署操作。
k8s 的部署方式
通常使用 kubeadm 部署和二进制方式部署,他们之间的区别:
kubeadm 方式部署,组件容器化部署,只有 kubelet 没有被容器化
二进制方式部署,传统的守护进程(systemd)管理服务 systemctl
实现目标
基于 华为 openEuler 22.03 LTS 系统,使用 kubeadm v1.24.1 搭建一套由单 Master node 和两个 Worker node 组成的 k8s v1.24.1 版本的集群环境。
系统环境准备
部署要求
在开始部署之前,部署 k8s 集群的宿主机(vm 或 物理机)需要满足以下几个条件:
宿主机 3 台,操作系统 CentOS7.x/8.x-86_x64 系列(此处使用 openEuler 22.03 LTS)
硬件配置:RAM 4GB或更多,CPU 核数 2c 或更多,硬盘 60GB 或更多
集群中所有机器之间网络互通
可以访问外网,需要拉取镜像
关闭防火墙,禁止 swap 分区
所有集群节点同步系统时间(使用 ntpdate 工具)
注意:以上部署规格的配置要求为最小化集群规模要求,生产环境的集群部署要求按实际情况扩展配置,为了保障集群环境的高可用性,搭建集群环境的宿主机通常以奇数( ≥ 3、5、7...)节点最佳。
部署规划
此处以单 master node 和两 worker node 集群模式为例,使用 kubeadm 部署 Kubernetes v1.24.1 版本集群环境。
1. 单 master 集群模式
2. vm 资源编排
此处使用 VMware Workstation Pro v16.2.2 虚拟机搭建 vm 环境,规划如下:
k8s 集群角色 | ip 地址 | hostname 主机名称 | 资源规格 | 操作系统 | 安装组件 |
---|---|---|---|---|---|
master | 192.168.18.130 | k8s-master-01 | 2c4g/60g | openEuler 22.03 LTS | kube-apiserver、kube-controller-manager、kube-scheduler、etcd、containerd、kubelet、kube-proxy、keepalived、nginx、calico、metrics-server、dashboard |
worker node | 192.168.18.131 | k8s-node-01 | 2c4g/60g | openEuler 22.03 LTS | containerd、kubelet、kube-proxy、ingress-controller、calico,coredns |
worker node | 192.168.18.132 | k8s-node-02 | 2c4g/60g | openEuler 22.03 LTS | containerd、kubelet、kube-proxy、ingress-controller、calico,coredns |
关于 VMware Workstation Pro v16.2.2 虚拟机自行下载,并安装配置好,vm 系统使用华为 openEuler 22.03 LTS ISO 镜像。
注意:VMware 中网络配置选择【NAT 模式】,确保 vm 内部网络环境可以访问到外部网络环境。
3. k8s 组件版本
关于 k8s 的常用资源组件版本信息规划如下:
名称 | 版本 | 下载地址 |
---|---|---|
kubernetes | v1.24.1 | https://github.com/kubernetes/kubernetes/releases/tag/v1.24.1 |
kubelet、kubeadm、kubectl | v1.24.1 | yum install -y kubelet-1.24.1 kubeadm-1.24.1 kubectl-1.24.1 |
containerd | v1.6.4,cni v0.3.1 | https://github.com/containerd/containerd/releases/tag/v1.6.4 |
flannel | v0.18.0 | https://github.com/flannel-io/flannel/releases/tag/v0.18.0 |
calico | v3.23.1 | https://github.com/projectcalico/calico/releases/tag/v3.23.1 |
kube-state-metrics | v2.4.2 | https://github.com/kubernetes/kube-state-metrics/releases/tag/v2.4.2 |
metrics-server-helm-chart | v3.8.2 | https://github.com/kubernetes-sigs/metrics-server/releases/tag/metrics-server-helm-chart-3.8.2 |
Kong Ingress Controller for Kubernetes (KIC) | v2.3.1 | https://github.com/Kong/kubernetes-ingress-controller/releases/tag/v2.3.1 |
dashboard | v2.5.1 | https://github.com/kubernetes/dashboard/releases/tag/v2.5.1 |
4. 关于 openEuler
基于华为 openEuler 系统环境部署,推荐使用 openEuler 22.03 LTS 和 openEuler 20.03 LTS SP3,以 root 身份执行下面命令。
为了方便操作,vm 中的 openEuler 系统网络 ip 可以按照上面的编排规划,设置静态 ip 地址。
关于 openEuler 系统的安装,请自行参考官方文档,此处不是重点,接下来介绍 openEuler 系统安装后,我们需要设置的相关事项。
openEuler 资源地址:
ISO下载,https://www.openeuler.org/zh/download/
安装指南,https://docs.openeuler.org/zh/docs/22.03_LTS/docs/Installation/installation.html
5. shell 终端
以下是一些比较常用的 shell 终端,选择自己喜欢的一个安装配置即可。
Xshell 5/6/7
Windows PowerShell / PowerShell
Windows Terminal
PuTTY
Visual Studio Code
VM 系统部署操作事项(所有节点)
注意:下面命令在 k8s 所有节点(master + worker)执行。
1. 关闭防火墙 Firewalld
防火墙 firewalld 先 stop 再 disable ,操作如下:
systemctl stop firewalld #停止 $
systemctl disable firewalld #开机禁用 $
查看防火墙状态
systemctl status firewalld #查看状态
输出如下信息,说明已经关闭
[root@k8s-master-01 ~]# systemctl status firewalld
● firewalld.service - firewalld - dynamic firewall daemonLoaded: loaded (/usr/lib/systemd/system/firewalld.service; disabled; vendor preset: enabled)Active: inactive (dead)Docs: man:firewalld(1)
[Linux防火墙操作命令,开放或关闭端口] =》 https://zhuanlan.zhihu.com/p/161196711
2. 关闭 SELinux
安全增强型 Linux(SELinux)是一个 Linux 内核的功能,它提供支持访问控制的安全政策保护机制。
# 临时关闭 SELinux。
setenforce 0 # 永久关闭 SELinux。
vi /etc/selinux/config
SELINUX=disabled
验证 selinux 状态
getenforce
输出如下信息,说明已经关闭
[root@k8s-node-01 ~]# getenforce
Disabled
SELinux 状态说明
SELinux 状态为 disabled,表明 SELinux 已关闭
SELinux 状态为 enforcing 或者 permissive,表明 SELinux 在运行
3. 关闭 Swap
swap 的用途 ?
swap 分区就是交换分区,(windows 平台叫虚拟内存) 在物理内存不够用时,操作系统会从物理内存中把部分暂时不被使用的数据转移到交换分区,从而为当前运行的程序留出足够的物理内存空间。
为什么要关闭 swap ?
swap 启用后,在使用磁盘空间和内存交换数据时,性能表现会较差,会减慢程序执行的速度。
有的软件的设计师不想使用交换分区,例如:kubelet 在 v1.8 版本以后强制要求 swap 必须关闭,否则会报错:
Running with swap on is not supported, please disable swap! or set --fail-swap-on flag to false
或者 kubeadm init 时会报错:
[ERROR Swap]: running with swap on is not supported. Please disable swap
关闭 swap
swapoff -a # 临时关闭
vi /etc/fstab # 永久关闭,注释掉 swap 这行
查看 swap 是否关闭
[root@k8s-master-01 ~]# free -mtotal used free shared buff/cache available
Mem: 1454 881 147 73 425 179
Swap: 0 0 0
显示 total/used/free 为 0,说明已经关闭。
4. 设置宿主机名称
依据宿主机(vm 或物理机)资源编排情况,使用 systemd 里面的 hostnamectl 设置主机名。
# 临时
hostnamectl set-hostname k8s-master-01
hostnamectl set-hostname k8s-node-01
hostnamectl set-hostname k8s-node-02# 永久,编写对应的 hostname
vi /etc/hostname
设置完成后,重新进入下 shell 终端,使配置生效。
bash
5. 在 Master node 和 Worker node 添加 hosts
修改 hosts 文件,配置主机名称和 ip 之间的映射。
$ cat > /etc/hosts << EOF
192.168.18.130 k8s-master-01
192.168.18.131 k8s-node-01
192.168.18.132 k8s-node-02
EOF
进入 bash 访问测试 node 节点网络是否连通
bash
...
ping k8s-master-01
ping k8s-node-01
ping k8s-node-02
6. 创建 containerd.conf 配置文件
在路径 "/etc/modules-load.d/containerd.conf" 创建配置文件。
cat << EOF > /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
执行以下命令使配置生效
modprobe overlay
modprobe br_netfilter
7. 将桥接的 IPv4 流量传递到 IPTABLES 链
IPTABLES 规则实现 Docker 或 K8s 的网络通信,非常关键。查看 "/etc/sysctl.d/99-xxx.conf" 配置文件。
[root@k8s-master-01 /]# ls /etc/sysctl.d/
99-kubernetes-cri.conf 99-sysctl.conf
[root@k8s-master-01 /]# cat /etc/sysctl.d/99-sysctl.conf
# sysctl settings are defined through files in
# /usr/lib/sysctl.d/, /run/sysctl.d/, and /etc/sysctl.d/.
#
# Vendors settings live in /usr/lib/sysctl.d/.
# To override a whole file, create a new file with the same in
# /etc/sysctl.d/ and put new settings there. To override
# only specific settings, add a file with a lexically later
# name in /etc/sysctl.d/ and put new settings there.
#
# For more information, see sysctl.conf(5) and sysctl.d(5).
kernel.sysrq=0
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
user.max_user_namespaces=28633
net.ipv4.ip_forward=1
net.ipv4.conf.all.send_redirects=0
net.ipv4.conf.default.send_redirects=0
net.ipv4.conf.all.accept_source_route=0
net.ipv4.conf.default.accept_source_route=0
net.ipv4.conf.all.accept_redirects=0
net.ipv4.conf.default.accept_redirects=0
net.ipv4.conf.all.secure_redirects=0
net.ipv4.conf.default.secure_redirects=0
net.ipv4.icmp_echo_ignore_broadcasts=1
net.ipv4.icmp_ignore_bogus_error_responses=1
net.ipv4.conf.all.rp_filter=1
net.ipv4.conf.default.rp_filter=1
net.ipv4.tcp_syncookies=1
kernel.dmesg_restrict=1
net.ipv6.conf.all.accept_redirects=0
net.ipv6.conf.default.accept_redirects=0
修改内核参数(上面的 99-sysctl.conf 配置文件已经修)
# 1、加载br_netfilter模块
modprobe br_netfilter# 2、验证模块是否加载成功
lsmod | grep br_netfilter# 3、修改内核参数
cat > /etc/sysctl.d/99-sysctl.conf <<EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
user.max_user_namespaces=28633
net.ipv4.ip_forward = 1
EOF# 4、使刚才修改的内核参数生效,此处使用的是 99-sysctl.conf 配置文件。
sysctl -p /etc/sysctl.d/99-sysctl.conf
sysctl 命令用于运行时配置内核参数,这些参数位于 "/proc/sys" 目录下。sysctl 配置与显示在 "/proc/sys" 目录中的内核参数。可以用 sysctl 来设置或重新设置联网功能,如 IP 转发、IP 碎片去除以及源路由检查等。用户只需要编辑 "/etc/sysctl.conf" 文件,即可手工或自动执行由 sysctl 控制的功能。
8. 配置服务器支持开启 IPVS 的前提条件(K8s 推荐配置)
IPVS 称之为 IP虚拟服务器(IP Virtual Server,简写为 IPVS)。是运行在 LVS 下的提供负载平衡功能的一种技术。
IPVS 基本上是一种高效的 Layer-4 交换机,它提供负载平衡的功能。
由于 IPVS 已经加入到了 Linux 内核的主干,所以为 kube-proxy(Kubernetes Service) 开启 IPVS 的前提需要加载以下的 Linux 内核模块:
ip_vs
ip_vs_rr
ip_vs_wrr
ip_vs_sh
nf_conntrack_ipv4 # 或 nf_conntrack
在所有服务器集群节点上执行以下脚本
cat > /etc/sysconfig/modules/ipvs.modules <<EOF
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4 # 若提示在内核中找不到 nf_conntrack_ipv4, 可以尝试切换 nf_conntrack
EOF# 或者 grep -e ip_vs -e nf_conntrack
chmod 755 /etc/sysconfig/modules/ipvs.modules && bash /etc/sysconfig/modules/ipvs.modules && lsmod | grep -e ip_vs -e nf_conntrack_ipv4
上面脚本创建了的 "/etc/sysconfig/modules/ipvs.modules" 文件,保证在节点重启后能自动加载所需模块。可执行命令 【lsmod | grep -e ip_vs -e nf_conntrack_ipv4】查看所需内核模块是否正确加载。
接下来还需要确保各个节点上已经安装了 ipset 软件包,为了便于查看 ipvs 的代理规则,最好安装一下管理工具 ipvsadm。
yum install -y ipset ipvsadm
如果不满足以上前提条件,则即使 kube-proxy 的配置开启了 ipvs 模式,也会退回到 iptables 模式。
9. 同步系统时间
ntpdate 指令通过轮询指定为服务器参数的 网络时间协议(NTP) 服务器来设置本地日期和时间,从而确定正确的时间。
此命令的适用范围:RedHat、RHEL、Ubuntu、CentOS、Fedora。
# 安装 ntpdate
yum install ntpdate -y# 执行同步命令
ntpdate time.windows.com
# 跟网络源做同步
ntpdate cn.pool.ntp.org# 把时间同步做成计划任务
crontab -e
* */1 * * * /usr/sbin/ntpdate cn.pool.ntp.org# 重启crond服务
service crond restart# 查看当前时区
date -R
同步系统时间输出如下信息
[root@k8s-master-01 /]# ntpdate time.windows.com
29 May 16:19:17 ntpdate[7873]: adjust time server 20.189.79.72 offset +0.001081 sec
IPVS 和 IPTABLES 对比分析
IPVS 是什么?
IPVS (IP Virtual Server) 实现了传输层负载均衡,也就是我们常说的 4 层局域网交换机(LAN Switches),作为 Linux 内核的一部分。IPVS 运行在主机上,在真实服务器集群前充当负载均衡器。IPVS 可以将基于 TCP 和 UDP 的服务请求转发到真实服务器上,并使真实服务器的服务在单个 IP 地址上显示为虚拟服务。
IPVS 和 IPTABLES 对比分析
kube-proxy 支持 iptables 和 ipvs 两种模式, 在 kubernetes v1.8 中引入了 ipvs 模式,在 v1.9 中处于 beta 阶段,在 v1.11 中已经正式可用了。iptables 模式在 v1.1 中就添加支持了,从 v1.2 版本开始 iptables 就是 kube-proxy 默认的操作模式,ipvs 和 iptables 都是基于 netfilter 的,但是 ipvs 采用的是 hash 表,因此当 service 数量达到一定规模时,hash 查表的速度优势就会显现出来,从而提高 service 的服务性能。
那么 ipvs 模式和 iptables 模式之间有哪些差异呢?
ipvs 为大型集群提供了更好的可扩展性和性能
ipvs 支持比 iptables 更复杂的复制均衡算法(最小负载、最少连接、加权等等)
ipvs 支持服务器健康检查和连接重试等功能
在 k8s 的集群环境中,推荐配置 ipvs ,为一定数量规模的 service 提高服务性能。
安装 containerd/kubeadm/kubelet(所有节点)
Containerd 简介
Containerd 是一个工业级标准的容器运行时,它强调简单性、健壮性和可移植性,具备如下功能:
管理容器的生命周期(从创建容器到销毁容器)
拉取/推送容器镜像
存储管理(管理镜像及容器数据的存储)
调用 runc 运行容器(与 runc 等容器运行时交互)
管理容器网络接口及网络
自 Kubernetes v1.24 起,Dockershim 已被删除,由于社区的关注,Docker 和 Mirantis 共同决定继续以 [cri-dockerd] 的形式支持 dockershim 代码(https://www.mirantis.com/blog/the-future-of-dockershim-is -cri-dockerd/), 允许你在需要时继续使用 Docker Engine 作为容器运行时。对于想要尝试其他运行时(如 containerd 或 cri-o) 的用户,已编写迁移文档(https://kubernetes.io/zh/docs/tasks/administer-cluster/migrating-from-dockershim/change-runtime-containerd/)。
查看删除 Dockershim 的原因
Dockershim:历史背景 =》 https://kubernetes.io/zh/blog/2022/05/03/dockershim-historical-context/
如果试图将链从最终用户(user)绘制到实际的容器进程(runc),它可能如下所示:runc 是一个命令行客户端,用于运行根据 OCI(开放容器计划,Open Container Initiative/OCI) 格式打包的应用程序,并且是 OCI 规范的兼容实现。
使用 Containerd 的理由
Kubernetes 在 v1.23 版本及以后版本不再默认采用 Docker/Dockershim ,而建议采用 Containerd;
Containerd 比 Docker/Dockershim 更加轻量级,在生产环境中使用更佳合适(稳定,性能);
Containerd 安装
下面我们进行 containerd 容器运行时的安装,操作如下:
1. 安装 wget (可选)
由于 openEuler 系统本身集成了 curl ,安装 wget 不是必须项。
# 安装 wget
sudo yum install wget -y# 此处安装的 wget 版本信息
...
Installed:wget-1.20.3-2.oe1.x86_64
2. 下载 containerd
分别使用 wget 和 curl 下载 containerd v1.6.4 版本。
# 使用 wget 下载 containerd v1.6.4
sudo wget https://github.com/containerd/containerd/releases/download/v1.6.4/cri-containerd-cni-1.6.4-linux-amd64.tar.gz# 使用 curl 下载 containerd v1.6.4
curl -L https://github.com/containerd/containerd/releases/download/v1.6.4/cri-containerd-cni-1.6.4-linux-amd64.tar.gz -O cri-containerd-cni-1.6.4-linux-amd64.tar.gz
3. 安装 tar
tar 命令简介
Linux tar(英文全拼:tape archive )命令用于备份文件。
tar 是用来建立,还原备份文件的工具程序,它可以加入,解开备份文件内的文件。
# 安装 tar
sudo yum install -y tar# 查看压缩包包含哪些文件
sudo tar -tf cri-containerd-cni-1.6.4-linux-amd64.tar.gz # 将压缩包解压至 cri-containerd-cni 文件夹(这里用命令创建 cri-containerd-cni文件夹,因为没有实现创建该文件夹),防止将文件都解压缩到当前文件夹(可以看到有etc、opt、usr三个子文件夹)。
sudo tar xzf cri-containerd-cni-1.6.4-linux-amd64.tar.gz -C cri-containerd-cni | mkdir cri-containerd-cni
cri-containerd-cni-1.6.4-linux-amd64.tar.gz 文件中包含三个文件夹,分别是【etc、opt、usr】,如下图所示:
4. 安装 containerd
# 解压 containerd 到根目录
tar zxvf cri-containerd-cni-1.6.0-linux-amd64.tar.gz -C /# 生成 containerd 默认配置
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
注意:确认 containerd 可执行文件所在目录在 PATH 环境变量中。
5. 配置 containerd 软件源
参考配置文件 "/etc/containerd/config.toml" 如下:
disabled_plugins = []
imports = []
oom_score = 0
plugin_dir = ""
required_plugins = []
root = "/var/lib/containerd"
state = "/run/containerd"
version = 2[cgroup]path = ""[debug]address = ""format = ""gid = 0level = ""uid = 0[grpc]address = "/run/containerd/containerd.sock"gid = 0max_recv_message_size = 16777216max_send_message_size = 16777216tcp_address = ""tcp_tls_cert = ""tcp_tls_key = ""uid = 0[metrics]address = ""grpc_histogram = false[plugins][plugins."io.containerd.gc.v1.scheduler"]deletion_threshold = 0mutation_threshold = 100pause_threshold = 0.02schedule_delay = "0s"startup_delay = "100ms"[plugins."io.containerd.grpc.v1.cri"]disable_apparmor = falsedisable_cgroup = falsedisable_hugetlb_controller = truedisable_proc_mount = falsedisable_tcp_service = trueenable_selinux = falseenable_tls_streaming = falseignore_image_defined_volumes = falsemax_concurrent_downloads = 3max_container_log_line_size = 16384netns_mounts_under_state_dir = falserestrict_oom_score_adj = false#sandbox_image = "k8s.gcr.io/pause:3.6"
# 1. 修改基础镜像地址(此处以阿里云为例)#sandbox_image = "registry.cn-hangzhou.aliyuncs.com/google_containers/pause:3.6"sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.7"selinux_category_range = 1024stats_collect_period = 10stream_idle_timeout = "4h0m0s"stream_server_address = "127.0.0.1"stream_server_port = "0"systemd_cgroup = falsetolerate_missing_hugetlb_controller = trueunset_seccomp_profile = ""[plugins."io.containerd.grpc.v1.cri".cni]bin_dir = "/opt/cni/bin"conf_dir = "/etc/cni/net.d"conf_template = ""max_conf_num = 1[plugins."io.containerd.grpc.v1.cri".containerd]default_runtime_name = "runc"disable_snapshot_annotations = truediscard_unpacked_layers = falseno_pivot = falsesnapshotter = "overlayfs"[plugins."io.containerd.grpc.v1.cri".containerd.default_runtime]base_runtime_spec = ""container_annotations = []pod_annotations = []privileged_without_host_devices = falseruntime_engine = ""runtime_root = ""runtime_type = ""[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]base_runtime_spec = ""container_annotations = []pod_annotations = []privileged_without_host_devices = falseruntime_engine = ""runtime_root = ""runtime_type = "io.containerd.runc.v2"[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]BinaryName = ""CriuImagePath = ""CriuPath = ""CriuWorkPath = ""IoGid = 0IoUid = 0NoNewKeyring = falseNoPivotRoot = falseRoot = ""ShimCgroup = ""SystemdCgroup = false[plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime]base_runtime_spec = ""container_annotations = []pod_annotations = []privileged_without_host_devices = falseruntime_engine = ""runtime_root = ""runtime_type = ""[plugins."io.containerd.grpc.v1.cri".containerd.untrusted_workload_runtime.options][plugins."io.containerd.grpc.v1.cri".image_decryption]key_model = "node"[plugins."io.containerd.grpc.v1.cri".registry]config_path = ""[plugins."io.containerd.grpc.v1.cri".registry.auths][plugins."io.containerd.grpc.v1.cri".registry.configs][plugins."io.containerd.grpc.v1.cri".registry.headers][plugins."io.containerd.grpc.v1.cri".registry.mirrors]
# 2. 设置仓库地址(镜像源)[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]endpoint = ["https://usydjf4t.mirror.aliyuncs.com","https://mirror.ccs.tencentyun.com","https://registry.docker-cn.com","http://hub-mirror.c.163.com"][plugins."io.containerd.grpc.v1.cri".registry.mirrors."k8s.gcr.io"]endpoint = ["https://registry.cn-hangzhou.aliyuncs.com/google_containers"]
# [plugins."io.containerd.grpc.v1.cri".registry.mirrors."192.168.0.187:5000"]
# endpoint = ["http://192.168.0.187:5000"]
# [plugins."io.containerd.grpc.v1.cri".registry.configs]
# [plugins."io.containerd.grpc.v1.cri".registry.configs."192.168.0.187:5000".tls]
# insecure_skip_verify = true
# [plugins."io.containerd.grpc.v1.cri".registry.configs."harbor.creditgogogo.com".auth]
# username = "admin"
# password = "Harbor12345"[plugins."io.containerd.grpc.v1.cri".x509_key_pair_streaming]tls_cert_file = ""tls_key_file = ""[plugins."io.containerd.internal.v1.opt"]path = "/opt/containerd"[plugins."io.containerd.internal.v1.restart"]interval = "10s"[plugins."io.containerd.metadata.v1.bolt"]content_sharing_policy = "shared"[plugins."io.containerd.monitor.v1.cgroups"]no_prometheus = false[plugins."io.containerd.runtime.v1.linux"]no_shim = falseruntime = "runc"runtime_root = ""shim = "containerd-shim"shim_debug = false[plugins."io.containerd.runtime.v2.task"]platforms = ["linux/amd64"][plugins."io.containerd.service.v1.diff-service"]default = ["walking"][plugins."io.containerd.snapshotter.v1.aufs"]root_path = ""[plugins."io.containerd.snapshotter.v1.btrfs"]root_path = ""[plugins."io.containerd.snapshotter.v1.devmapper"]async_remove = falsebase_image_size = ""pool_name = ""root_path = ""[plugins."io.containerd.snapshotter.v1.native"]root_path = ""[plugins."io.containerd.snapshotter.v1.overlayfs"]root_path = ""[plugins."io.containerd.snapshotter.v1.zfs"]root_path = ""[proxy_plugins][stream_processors][stream_processors."io.containerd.ocicrypt.decoder.v1.tar"]accepts = ["application/vnd.oci.image.layer.v1.tar+encrypted"]args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]path = "ctd-decoder"returns = "application/vnd.oci.image.layer.v1.tar"[stream_processors."io.containerd.ocicrypt.decoder.v1.tar.gzip"]accepts = ["application/vnd.oci.image.layer.v1.tar+gzip+encrypted"]args = ["--decryption-keys-path", "/etc/containerd/ocicrypt/keys"]env = ["OCICRYPT_KEYPROVIDER_CONFIG=/etc/containerd/ocicrypt/ocicrypt_keyprovider.conf"]path = "ctd-decoder"returns = "application/vnd.oci.image.layer.v1.tar+gzip"[timeouts]"io.containerd.timeout.shim.cleanup" = "5s""io.containerd.timeout.shim.load" = "5s""io.containerd.timeout.shim.shutdown" = "3s""io.containerd.timeout.task.state" = "2s"[ttrpc]address = ""gid = 0uid = 0
被修改的 "/etc/containerd/config.toml" 文件配置说明:
1、基础镜像设置
sandbox_image 设置国内基础镜像源地址。
2、镜像仓库设置
"docker.io" 配置 docker 公共镜像仓库源。
"k8s.gcr.io" 配置 k8s 仓库源。
其中 “192.168.0.187:5000” 是私人仓库地址(没有可以配置)。
insecure_skip_verify = true 意为跳过 tls 证书认证。
"harbor.creditgogogo.com".auth 设置仓库用户名和密码。
6. 启动 containerd 并设置为开机启动
由于上面下载的 containerd 压缩包中包含一个 "etc/systemd/system/containerd.service" 的文件,这样我们就可以通过 systemd 来配置 containerd 作为守护进程运行。
[root@k8s-master-01 /]# ls etc/systemd/system/
bluetooth.target.wants cron.service dbus-org.bluez.service default.target multi-user.target.wants sockets.target.wants timedatex.service
containerd.service ctrl-alt-del.target dbus-org.freedesktop.nm-dispatcher.service getty.target.wants network-online.target.wants sysinit.target.wants timers.target.wants
启动 containerd
# 启动 containerd,并设置为开机启动
systemctl daemon-reload && systemctl enable containerd && systemctl start containerd
# 查看 containerd 状态
systemctl status containerd
# 重启 containerd
systemctl restart containerd
此处启动 containerd 可能会显示如下信息:
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down
因为 wsl 子系统不能使用 systemd(基于 systemctl 或 service 命令的服务无法运行)。了解更多信息,请查看【WSL2 中使用 systemctl 命令】。
WSL2 中使用 systemctl 命令 =》https://www.isolves.com/it/qt/2020-12-10/34360.html
查看 "etc/systemd/system/containerd.service" 文件
cat etc/systemd/system/containerd.service
输出配置信息如下:
# Copyright The containerd Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.[Unit]
Description=containerd container runtime
Documentation=https://containerd.io
After=network.target local-fs.target[Service]
ExecStartPre=-/sbin/modprobe overlay
ExecStart=/usr/local/bin/containerdType=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
这里有两个重要的参数:
Delegate:这个选项允许 containerd 以及运行时自己管理自己创建容器的 cgroups。如果不设置这个选项,systemd 就会将进程移到自己的 cgroups 中,从而导致 containerd 无法正确获取容器的资源使用情况。
KillMode:这个选项用来处理 containerd 进程被杀死的方式。默认情况下,systemd 会在进程的 cgroup 中查找并杀死 containerd 的所有子进程。
KillMode 字段可以设置的值如下:
control-group:当前控制组里面的所有子进程,都会被杀掉
process:只杀主进程
mixed:主进程将收到 SIGTERM 信号,子进程收到 SIGKILL 信号
none:没有进程会被杀掉,只是执行服务的 stop 命令
注意:需要将 KillMode 的值设置为 process,这样可以确保升级或重启 containerd 时不杀死现有的容器。
7. 查看 containerd 信息
crictl info
# 更多使用
crictl --help
K8s 配置阿里云 repo 文件(yum 软件源)
默认配置是国外的镜像源,由于国内网络原因无法访问,所以配置阿里云的 yum 软件源。
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
阿里云 Kubernetes 镜像:
【容器】 https://developer.aliyun.com/mirror/?spm=a2c6h.13651102.0.0.3e221b11RAcHlc&serviceType=mirror&tag=%E5%AE%B9%E5%99%A8
【Kubernetes 镜像源】 https://developer.aliyun.com/mirror/kubernetes
安装 kubeadm、kubelet 和 kubectl(所有节点)
由于版本更新频繁,因此这里指定版本号部署。
yum install -y kubelet-1.24.1 kubeadm-1.24.1 kubectl-1.24.1
启动 kubelet 并设置开机启动
systemctl enable kubelet && systemctl start kubelet
ps: 由于官网未开放同步方式,可能会有索引 gpg 检查失败的情况,这时请用【yum install -y --nogpgcheck kubelet kubeadm kubectl 】安装。
部署 K8s Master 节点
1. K8s 集群初始化(kubeadm init)
依据上面的 vm 资源规划,在 master 节点(192.168.18.130)执行如下命令:
kubeadm init \--apiserver-advertise-address=192.168.18.130 \--image-repository registry.aliyuncs.com/google_containers \--kubernetes-version v1.24.1 \--service-cidr=10.96.0.0/12 \--pod-network-cidr=10.244.0.0/16 \--ignore-preflight-errors=all
参数说明:
--apiserver-advertise-address 集群通告地址
--image-repository 由于默认拉取镜像地址 k8s.gcr.io 国内无法访问,这里指定阿里云镜像仓库地址
--kubernetes-version K8s 版本,与上面安装的一致
--service-cidr 集群内部虚拟网络,Pod 统一访问入口
--pod-network-cidr Pod网络,与下面部署的 CNI 网络组件 yaml 中保持一致
--ignore-preflight-errors 忽略所有预检项的警告信息
或者使用配置文件引导:
$ vi kubeadm.conf
apiVersion: kubeadm.k8s.io/v1beta2
kind: ClusterConfiguration
kubernetesVersion: v1.24.1
imageRepository: registry.aliyuncs.com/google_containers
networking:podSubnet: 10.244.0.0/16 serviceSubnet: 10.96.0.0/12 $ kubeadm init --config kubeadm.conf --ignore-preflight-errors=all
kubeadm init 初始化成功,输出如下信息:
Your Kubernetes control-plane has initialized successfully!To start using your cluster, you need to run the following as a regular user:mkdir -p $HOME/.kubesudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/configsudo chown $(id -u):$(id -g) $HOME/.kube/configAlternatively, if you are the root user, you can run:export KUBECONFIG=/etc/kubernetes/admin.confYou should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:https://kubernetes.io/docs/concepts/cluster-administration/addons/Then you can join any number of worker nodes by running the following on each as root:kubeadm join 192.168.18.130:6443 --token pc5d3x.9ccv3m5y1llljk90 \--discovery-token-ca-cert-hash sha256:3f7b37c18a5ec21c3e225025e13a0ac53e7abdf717859808b24f9bc909b32b5b
此处注意保存下 kubeadm init 初始化产生的信息,方便下面环节的部署操作使用。
2. 拷贝文件到默认路径
拷贝 kubectl 使用的连接 k8s 认证文件到默认路径
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
3. 查看 k8s 集群节点信息
[root@k8s-master-01 ~]# kubectl get node
NAME STATUS ROLES AGE VERSION
k8s-master-01 Ready control-plane 4h8m v1.24.1
k8s-node-01 Ready <none> 3h14m v1.24.1
k8s-node-02 Ready <none> 3h14m v1.24.1
4. 查看 containerd 拉取的镜像
[root@k8s-master-01 /]# crictl image ls
IMAGE TAG IMAGE ID SIZE
docker.io/calico/cni v3.23.1 90d97aa939bbf 111MB
docker.io/calico/node v3.23.1 fbfd04bbb7f47 76.6MB
registry.aliyuncs.com/google_containers/coredns v1.8.6 a4ca41631cc7a 13.6MB
registry.aliyuncs.com/google_containers/etcd 3.5.3-0 aebe758cef4cd 102MB
registry.aliyuncs.com/google_containers/kube-apiserver v1.24.1 e9f4b425f9192 33.8MB
registry.aliyuncs.com/google_containers/kube-controller-manager v1.24.1 b4ea7e648530d 31MB
registry.aliyuncs.com/google_containers/kube-proxy v1.24.1 beb86f5d8e6cd 39.5MB
registry.aliyuncs.com/google_containers/kube-scheduler v1.24.1 18688a72645c5 15.5MB
registry.aliyuncs.com/google_containers/pause 3.7 221177c6082a8 311kB
注意:上面的镜像除了 calico 相关的(docker.io/calico/cni 和 docker.io/calico/node) 之外,其他都是 kubectl int 初始化拉取的镜像资源。
Worker 节点加入 K8s 集群
1. k8s 集群环境加入新的 worker node
向集群添加新 node 节点,执行在 kubeadm init 输出的 kubeadm join 命令,分别在 worker node 执行如下命令:
kubeadm join 192.168.18.130:6443 --token pc5d3x.9ccv3m5y1llljk90 \--discovery-token-ca-cert-hash sha256:3f7b37c18a5ec21c3e225025e13a0ac53e7abdf717859808b24f9bc909b32b5b
2. 生成加入 k8s 集群环境的 token
默认 token 有效期为 24 小时,当过期之后,该 token 就不可用了。这时就需要重新创建 token,操作如下:
$ kubeadm token create
$ kubeadm token list
$ openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | openssl dgst -sha256 -hex | sed 's/^.* //'
63bca849e0e01691ae14eab449570284f0c3ddeea590f8da988c07fe2729e924$ kubeadm join 192.168.31.61:6443 --token nuja6n.o3jrhsffiqs9swnu --discovery-token-ca-cert-hash sha256:63bca849e0e01691ae14eab449570284f0c3ddeea590f8da988c07fe2729e924
或者直接命令快捷生成
kubeadm token create --print-join-command
参考文档 =》https://kubernetes.io/docs/reference/setup-tools/kubeadm/kubeadm-join/
部署容器网络/CNI(所有节点)
1. CNI 简介
CNI 全称是 Container Network Interface,即容器网络的 API 接口。
它是 K8s 中标准的一个调用网络实现的接口。Kubelet 通过这个标准的 API 来调用不同的网络插件以实现不同的网络配置方式。
CNI 插件就是实现了一系列的 CNI API 接口。常见的 CNI 插件包括 Calico、Flannel、Terway、Weave Net 以及 Contiv。
2. CNI 分类及选型参考
CNI 插件可以分为三种:Overlay、路由及 Underlay。
【Overlay 模式】的典型特征是容器独立于主机的 IP 段,这个 IP 段进行跨主机网络通信时是通过在主机之间创建隧道的方式,将整个容器网段的包全都封装成底层的物理网络中主机之间的包。该方式的好处在于它不依赖于底层网络;
【路由模式】中主机和容器也分属不同的网段,它与 Overlay 模式的主要区别在于它的跨主机通信是通过路由打通,无需在不同主机之间做一个隧道封包。但路由打通就需要部分依赖于底层网络,比如说要求底层网络有二层可达的一个能力;
【Underlay 模式】中容器和宿主机位于同一层网络,两者拥有相同的地位。容器之间网络的打通主要依靠于底层网络。因此该模式是强依赖于底层能力的。
对于 CNI 插件的选择,有以下几个维度参考:
环境限制,不同环境中所支持的底层能力是不同的。
功能需求
性能需求
了解更多可以参考 =》https://www.kubernetes.org.cn/6908.html
注意:CNI 插件只需要部署其中一个即可,这里推荐 Calico 或 Flannel。
3. Calico 简介
Calico 是一个纯三层的数据中心网络方案,Calico 支持广泛的平台,包括 Kubernetes、OpenStack 等。
Calico 在每一个计算节点利用 Linux Kernel 实现了一个高效的虚拟路由器( vRouter) 来负责数据转发,而每个 vRouter 通过 BGP 协议负责把自己上运行的 workload 的路由信息向整个 Calico 网络内传播。
此外,Calico 项目还实现了 Kubernetes 网络策略,提供 ACL 功能。
4. 下载 Calico 插件
wget https://docs.projectcalico.org/manifests/calico.yaml
5. 修改里面定义的 Pod 网络
下载完后还需要修改里面定义 Pod 网络(CALICO_IPV4POOL_CIDR),与前面 kubeadm init 指定的一样【--pod-network-cidr=10.244.0.0/16】
# The default IPv4 pool to create on startup if none exists. Pod IPs will be
# chosen from this range. Changing this value after installation will have
# no effect. This should fall within `--cluster-cidr`.
# - name: CALICO_IPV4POOL_CIDR
# value: "192.168.0.0/16"
- name: CALICO_IPV4POOL_CIDRvalue: "10.244.0.0/16"
查找 CALICO_IPV4POOL_CIDR
vi calico.yaml
...
/CALICO_IPV4POOL_CIDR
6. 应用 calico.yaml 配置
修改完后应用 calico.yaml 清单
kubectl apply -f calico.yaml
输出如下信息:
[root@k8s-master-01 /]# ls
bin boot calico.yaml cri-containerd-cni-1.6.4-linux-amd64.tar.gz dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
[root@k8s-master-01 /]# kubectl apply -f calico.yaml
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
poddisruptionbudget.policy/calico-kube-controllers created
7. 查看命名空间 kube-system 下的 pod
kubectl get pods -n kube-system
输出如下信息:
[root@k8s-master-01 /]# kubectl get pods -n kube-system
NAME READY STATUS RESTARTS AGE
calico-kube-controllers-56cdb7c587-mfl9d 1/1 Running 22 (21m ago) 126m
calico-node-2dtr8 0/1 Running 35 (5s ago) 126m
calico-node-hmdkf 0/1 CrashLoopBackOff 35 (2m6s ago) 126m
calico-node-l6448 0/1 Error 38 (70s ago) 126m
coredns-74586cf9b6-b5r68 1/1 Running 0 7h5m
coredns-74586cf9b6-jndhp 1/1 Running 0 7h6m
etcd-k8s-master-01 1/1 Running 0 7h9m
kube-apiserver-k8s-master-01 1/1 Running 6 (20m ago) 7h9m
kube-controller-manager-k8s-master-01 0/1 CrashLoopBackOff 23 (119s ago) 7h9m
kube-proxy-ctx7q 1/1 Running 0 7h6m
kube-proxy-l72sj 1/1 Running 0 6h15m
kube-proxy-s65x9 1/1 Running 0 6h15m
kube-scheduler-k8s-master-01 0/1 CrashLoopBackOff 21 (119s ago) 7h9m
测试 K8s 集群
验证 Pod 工作
验证 Pod 网络通信
验证 DNS 解析
在 K8s 集群中创建一个 Pod,验证是否正常运行:
kubectl create deployment nginx --image=nginx:latest -p 80:8080
kubectl expose deployment nginx --port=8080 --type=NodePort
kubectl get pod,svc
访问 nginx 的 svc 地址:
http://NodeIP:Port
关于 K8s nodePort、port、targetPort、hostPort 端口的介绍 =》https://www.jianshu.com/p/8275f2031c83
在 Master 节点部署 Dashboard
1. 下载 dashboard 的 recommended.yaml 配置文件
wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.5.1/aio/deploy/recommended.yaml
2. recommended.yaml 文件重命名(可选)
把下载的 recommended.yaml 文件重命名为 dashboard-v2.5.1.yaml (可选,方便文件记录)
mv recommended.yaml dashboard-v2.5.1.yaml
3. 修改 Dashboard 配置文件暴露到外部访问
默认 Dashboard 只能集群内部访问,修改 Service 为 NodePort 类型,暴露到外部访问:
$ vi dashboard-v2.5.1.yaml
---
kind: Service
apiVersion: v1
metadata:labels:k8s-app: kubernetes-dashboardname: kubernetes-dashboardnamespace: kubernetes-dashboard
spec:ports:- port: 443targetPort: 8443nodePort: 30001selector:k8s-app: kubernetes-dashboardtype: NodePort
---
4. 执行 dashboard-v2.5.1.yaml 配置文件
kubectl apply -f dashboard-v2.5.1.yaml
输出如下信息:
[root@k8s-master-01 /]# kubectl apply -f /root/dashboard-v2.5.1.yaml
namespace/kubernetes-dashboard created
serviceaccount/kubernetes-dashboard created
service/kubernetes-dashboard created
secret/kubernetes-dashboard-certs created
secret/kubernetes-dashboard-csrf created
secret/kubernetes-dashboard-key-holder created
configmap/kubernetes-dashboard-settings created
role.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrole.rbac.authorization.k8s.io/kubernetes-dashboard created
rolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
clusterrolebinding.rbac.authorization.k8s.io/kubernetes-dashboard created
deployment.apps/kubernetes-dashboard created
service/dashboard-metrics-scraper created
deployment.apps/dashboard-metrics-scraper created
5. 查看 kubernetes-dashboard
kubectl get pods -n kubernetes-dashboard
输出如下信息:
[root@k8s-master-01 /]# kubectl get pods -n kubernetes-dashboard
NAME READY STATUS RESTARTS AGE
dashboard-metrics-scraper-7bfdf779ff-q9tsk 0/1 ContainerCreating 0 74s
kubernetes-dashboard-6465b7f54c-frf2b 0/1 ContainerCreating 0 74s[root@k8s-master-01 /]# kubectl get pods -n kubernetes-dashboard
NAME READY STATUS RESTARTS AGE
dashboard-metrics-scraper-7bfdf779ff-q9tsk 1/1 Running 0 32m
kubernetes-dashboard-6465b7f54c-frf2b 1/1 Running 0 32m
6. 访问 kubernetes-dashboard
访问 kubernetes-dashboard 地址
https://NodeIP:30001
创建 ServiceAccount 并绑定默认 cluster-admin 管理员集群角色:
# 创建用户
$ kubectl create serviceaccount dashboard-admin -n kube-system# 用户授权
$ kubectl create clusterrolebinding dashboard-admin --clusterrole=cluster-admin --serviceaccount=kube-system:dashboard-admin# 获取用户 Token
$ kubectl describe secrets -n kube-system $(kubectl -n kube-system get secret | awk '/dashboard-admin/{print $1}')
此处 master 节点的 ip 是 192.168.18.130, 浏览器访问地址 =》https://192.168.18.130:30001/#/login
最后使用输出的 token 登录 Dashboard。
总结
搭建 k8s 集群环境时,事先要规划编排好相关的资源环境以及各个组件的版本信息,开始部署之前务必保障系统环境准备事项的相关设置操作,此处我们采用的容器运行时是 containerd,由于默认的 yum 镜像源是国外环境,在国内网络环境无法访问,可配置国内的 yum 源(比如:阿里云 yum 镜像源)保障网络资源的连通性和可访问性,方便后面环节的相关操作(比如:containerd/kubeadm/kubelet 的安装、k8s master 节点的部署、容器网络 CNI 安装及网络编排)。
每一个环节都很重要,中途遇到异常,多查看资料分析下原因,关键在于理解。