ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

kubeadm+Containerd+Calico 搭建K8s集群避坑指南

2026/9/9 9:43:06 拓冰建站 浏览量
kubeadm+Containerd+Calico 搭建K8s集群避坑指南 1. 大多数人搭 K8s 集群失败不是因为命令敲错先说个反直觉的现象kubeadm init跑完报错九成以上不是网络问题就是镜像拉不下来但真正让新手心态崩掉的往往是“明明照着教程敲了怎么结果不一样”。原因就一个——Kubernetes 的版本迭代太快教程之间相差三个月行为就可能完全不同而你很可能拿了一篇老教程配新版本或者反过来。这篇文章存在的意义很简单我把自己从零搭建一套可用 K8s 集群的完整过程、每一步背后的原理、以及踩过的坑全部摊开来讲。无论你是刚接触容器编排的运维新人还是想在公司内部落地一套测试环境的后端开发都可以照着这份指南一步步操作。文章会覆盖环境规划、系统初始化、容器运行时安装、控制平面初始化、工作节点接入、网络插件部署、配置验证、以及后续日常管理中最容易翻车的几个环节。整份指南基于Ubuntu 22.04 LTS Containerd kubeadm Calico这套组合是目前社区最主流、资料最全、坑相对最少的搭配。如果你用的是 CentOS 7 或别的发行版原理完全一样只是个别包管理器命令和内核参数略有差异我会在对应位置做补充说明。2. 动手之前先把集群拓扑和版本策略定死2.1 我的环境规划3 节点最小可用集群K8s 最少可以跑单节点但生产或测试至少要 3 个节点才有意义——1 个控制平面 2 个工作节点。因为控制平面挂了集群还能撑一阵但连控制平面都只剩一个的话整个集群实际上处于“能跑但不可管”的状态。我这次用的是一套 3 节点虚拟机角色主机名配置IP控制平面k8s-master4C / 8G / 50G192.168.1.10工作节点k8s-node14C / 8G / 50G192.168.1.11工作节点k8s-node24C / 8G / 50G192.168.1.12关于配置我要多说两句。网上很多教程说 2C2G 也能跑确实能跑但你会非常痛苦——etcd 本身就吃内存控制平面的几个组件kube-apiserver、kube-controller-manager、kube-scheduler加在一起轻松吃掉 2~3G再跑几个业务 Pod 内存基本就告急。如果你是拿自己电脑开虚拟机练手建议至少 4G 内存起步磁盘 50G 是最低配置因为镜像、容器日志、 etcd 数据都会持续增长。2.2 版本选择不要一味求新要选“稳定且互相兼容”的组合这一步我单独拎出来讲因为它决定了你后面 90% 的坑会不会出现。Kubernetes 的版本号规则是 v1.xx.y其中 xx 是次要版本。官方支持最近三个次要版本也就是说 v1.28 发布后v1.25 就停止维护了。但“支持”和“推荐”是两回事我个人的建议是选择比最新版本落后 1~2 个次要版本比如最新是 v1.31你就选 v1.29 或 v1.30。为什么因为新版本发布后生态组件CNI、CSI、ingress-controller的兼容性需要一段时间才能跟上你等老鸟们先把坑踩完再上不迟。更关键的是控制平面和工作节点的版本不能差太多最多允许一个次要版本的偏差。比如 master 是 v1.29node 最高只能到 v1.30否则 kubelet 和 kube-apiserver 之间的 API 协商会出问题。我这次使用的版本组合Kubernetesv1.29.xContainerd1.7.xCalicov3.28.x操作系统Ubuntu 22.04内核 5.15为什么选 Containerd 而不是 DockerK8s 在 v1.24 版本之后彻底移除了对 Docker 作为运行时dockershim的支持虽然 Docker 仍然可以通过 cri-dockerd 这个适配层来用但官方推荐的默认运行时就是 Containerd。你如果坚持用 Docker等于多引入了一层转换性能损耗且不说排错链路也变长了。听我一句劝新集群直接上 Containerd别纠结。2.3 先想清楚你的网络模型这是搭建之前最容易被忽略、但影响最深远的一个决策。K8s 本身只定义了一套网络模型CNIContainer Network Interface但具体实现要你自己选。常见的 CNI 方案有 Flannel、Calico、Cilium、Weave Net它们的核心区别在于网络策略NetworkPolicy的支持程度和转发性能。Flannel 只做连通性不支持策略Calico 基于 BGP天然支持网络策略性能也不错Cilium 基于 eBPF性能最好但学习曲线陡。我这个系列用 Calico原因很简单它既支持 NetworkPolicy又不依赖特定的内核版本Cilium 要求内核 5.8Ubuntu 22.04 默认 5.15 没问题但如果你想在 CentOS 7 上跑内核 3.10 就尴尬了而且 BGP 模式在中小规模集群里性能完全够用。3. 系统初始化把所有节点的环境“拉齐”3.1 主机名、hosts、以及关闭 swap 的真相这一步看起来基础但很多人就是在这一步偷懒导致后面 kubeadm init 时各种莫名其妙的问题。首先给每个节点设置唯一的主机名# master 节点 sudo hostnamectl set-hostname k8s-master # node1 节点 sudo hostnamectl set-hostname k8s-node1 # node2 节点 sudo hostnamectl set-hostname k8s-node2然后修改每个节点的 /etc/hosts让所有节点能通过主机名互通sudo tee -a /etc/hosts EOF 192.168.1.10 k8s-master 192.168.1.11 k8s-node1 192.168.1.12 k8s-node2 EOF接着是关闭 swap。这一步不是“建议”而是“必须”。K8s 要求禁用 swap 的原因是当节点内存不足时如果 swap 还在kubelet 会认为内存还有余量不会及时触发 Pod 驱逐eviction结果就是整个节点卡死。v1.22 之后 K8s 引入了对 swap 的实验性支持但默认仍然要求关闭别去踩这个坑。sudo swapoff -a sudo sed -i / swap / s/^/#/ /etc/fstab注意swapoff -a只是临时关闭重启就失效了所以必须同时修改 /etc/fstab 用注释方式永久关闭。3.2 内核模块与系统参数iptables 的桥接流量是关键K8s 的 Service 和 Pod 之间的通信依赖 iptables或 IPVS来做转发而 iptables 处理桥接流量需要特殊的配置。这里有两项内核模块 br_netfilter 必须加载net.bridge.bridge-nf-call-iptables 必须设为 1。# 加载 br_netfilter 模块 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 sudo sysctl --system如果不做这一步最典型的报错是 kubeadm init 时提示 “WARNING: bridge-nf-call-iptables is disabled”或者 Pod 间网络时通时不通。我见过太多人卡在这里——明明容器都起来了但 Service 就是访问不了结果一查是 bridge-nf-call-iptables 没开。3.3 同步时间比你想的更重要这个很容易被忽略但重要程度不亚于关闭 swap。K8s 集群里证书的签发与校验、日志的时序分析、etcd 的 raft 选举全都依赖节点时间的一致性。节点间时钟偏差超过几秒轻则证书校验失败重则 etcd 集群脑裂。sudo apt install -y chrony sudo systemctl enable chrony sudo systemctl start chrony # 验证时间同步状态 chronyc sources -v如果你用的是虚拟机建议同时关闭 time sync 相关的选项以避免宿主机的时间同步和客户机冲突。VMware 里可以在 VM Options 里关掉 “Time synchronization”VirtualBox 则在设置里关闭 “Guest Additions” 的时间同步。4. 安装 Containerd别直接 apt install要配置 systemd cgroup 驱动4.1 从 Docker 官方源安装或者用发行版自带源Containerd 的安装有两种常见方式一种是从 Docker 官方 apt 源装另一种是用发行版自带的源。我推荐用 Docker 官方 apt 源原因很简单版本更新快而且和 K8s 社区的兼容性测试同步。sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.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 -y containerd.io4.2 关键配置cgroup 驱动必须是 systemd这是新手翻车率最高的一个环节。K8s 在 v1.24 之后默认使用 systemd 作为 kubelet 的 cgroup 驱动而 Containerd 默认配置是 cgroupfs两者不一致就会导致 kubelet 无法启动报错信息是 kubelet cgroup driver 不匹配。安装完 Containerd 后需要先导出默认配置再修改mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml然后编辑 /etc/containerd/config.toml找到[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options]这一段把SystemdCgroup从false改为true[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] SystemdCgroup true顺手还要处理镜像加速的问题。由于众所周知的网络原因直接拉取registry.k8s.io的镜像大概率会失败。有两种加速方式一种是通过 systemd 配置镜像加速器让 Containerd 在拉取镜像时自动替换 registry 地址另一种是使用国内镜像站手动拉取镜像后重新 tag。我使用的是前者在 config.toml 中配置 mirror把registry.k8s.io指向一个可用的加速器地址这样 kubeadm init 时的镜像检查就能顺利通过。改完配置后重启 Containerdsudo systemctl restart containerd systemctl status containerd确认服务状态是 running 且没有报错再进行下一步。5. kubeadm 安装与集群初始化成功的人各有各的顺利失败的人卡在同一处5.1 kubeadm、kubelet、kubectl 的安装这里有一个版本锁定的问题。如果你直接用apt install kubelet kubeadm kubectl大概率装的是最新版。对于学习和测试来说没问题但如果你想复现我这份指南建议明确锁定版本sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gpg curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.29/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg echo deb [signed-by/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.29/deb/ / | sudo tee /etc/apt/sources.list.d/kubernetes.list sudo apt-get update sudo apt-get install -y kubelet1.29.* kubeadm1.29.* kubectl1.29.* sudo apt-mark hold kubelet kubeadm kubectlapt-mark hold非常重要它防止apt upgrade时把 K8s 组件静默升级到更高版本导致集群版本不一致。我在生产环境里见过不止一次因为没 hold 版本一次系统更新后 kubelet 和 apiserver 版本差了两个次要版本整个集群直接失控。5.2 提前解决镜像问题kubeadm config images pull 检查在正式执行kubeadm init之前先手动拉一遍镜像确认网络和镜像源都没问题kubeadm config images list这一步会列出所有需要拉取的镜像清单包括 kube-apiserver、kube-controller-manager、kube-scheduler、kube-proxy、pause、etcd、coredns。确认清单无误后执行sudo kubeadm config images pull如果你看到所有的 image 都显示 pulled successfully说明基础镜像这一关已经过了。如果这里报错千万别继续往后走——回去检查 Containerd 的 mirror 配置或者用 crictl 手动拉镜像后再重新打 tag 的方式解决。5.3 kubeadm init最重要的几个参数执行如下命令注意替换为你自己的 IP 和 Pod 网段sudo kubeadm init \ --apiserver-advertise-address192.168.1.10 \ --pod-network-cidr10.244.0.0/16 \ --service-cidr10.96.0.0/12 \ --kubernetes-versionv1.29.0 \ --control-plane-endpointk8s-master参数含义--apiserver-advertise-addressAPIServer 对外宣告的 IP通常是控制平面节点的 IP。如果节点有多网卡必须显式指定否则 kubeadm 可能选错网卡。--pod-network-cidrPod 网段这个网段必须和后续 CNI 插件配置一致。我用 10.244.0.0/16 是因为 Calico 的默认配置里包含这个网段如果你用 Flannel默认也是 10.244.0.0/16。如果你用了其他网段必须调整 CNI 配置。--service-cidrService 网段默认 10.96.0.0/12 即可。--kubernetes-version显式指定版本避免 kubeadm 拉取默认版本的镜像。--control-plane-endpoint控制平面统一入口。单控制平面可以填主机名或 IP但在搭建高可用HA集群时这个值是负载均衡器的地址非常重要。我在这一步直接写主机名可以让 kubeconfig 里记录的地址是主机名在某些场景下比裸 IP 更方便。kubeadm init执行成功后末尾会输出三块关键信息kubectl 的配置方法就是把 admin.conf 拷到 ~/.kube/config工作节点加入集群的完整命令kubeadm join 加上 tokentoken 过期时间默认 24 小时过期后需要重新生成先按提示配置 kubectlmkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config kubectl get nodes你可能看到 master 节点的状态是NotReady不用慌这是正常的CNI 网络插件还没部署呢。5.4 Pod 网段冲突与调试思路在实际操作中最容易忽略的一个坑是Pod 网段和现有网络冲突。比如你的公司内网是 10.0.0.0/8你再把 Pod 网段设成 10.244.0.0/16这里的 10.244 网段虽然不冲突但如果你用了 10.10.0.0/16 这种内网设备很可能会访问不到集群里的 Pod。这个在设计初期就得想清楚网络规划不平衡集群搭建完了再改 Pod 网段是非常痛苦的事。另外补充一个调试技巧等到 Pod 一直处于 ContainerCreating不要只盯着 kubectl get pods 看用kubectl describe pod xxx -n kube-system来看事件Events大部分情况下 Event 里会直接把真正原因写清楚镜像拉取失败、挂载失败、CNI 冲突等。会看 describe 出来的 Events比会敲命令重要得多。6. 部署 Calico 网络插件Pod 间通信的“高速公路”6.1 下载并修改 Calico 清单Calico 的安装很简单官方提供了一份完整的 YAML 清单curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.28.1/manifests/calico.yaml默认配置里已经包含了很多内容包括 CRD、RBAC、DaemonSet、Service 等。我们需要关注的是 CALICO_IPV4POOL_CIDR 这个环境变量grep -n CALICO_IPV4POOL_CIDR calico.yaml默认值通常是192.168.0.0/16如果你的--pod-network-cidr用的不是这个网段必须改掉否则 Calico 分配的 Pod IP 和 apiserver 规划的网段不匹配CNI 会一直报错。kubectl apply -f calico.yaml然后观察状态kubectl get pods -n kube-system等所有 pod 都是 Running 后再看节点状态kubectl get nodes这时候 master 应该已经是 Ready 状态了。6.2 为什么选 BGP 模式而不是 VXLAN 模式Calico 支持两种主要的数据面模式BGP 和 VXLAN。BGP 模式直接通过节点的三层网络交换路由性能最好延迟低适合网络环境简单、所有节点在同一个二层域的场景VXLAN 是基于 UDP 的隧道封装适合跨子网、跨机房的场景但会有额外的封装开销和延迟。如果是同机房、同网段的节点直接用默认的 BGP 模式不用改。如果你的节点跨网段才需要考虑 VXLAN。怎么改在 calico.yaml 中找到环境变量CALICO_IPV4POOL_VXLAN设置为Always或CrossSubnet。这个配置不当容易导致跨节点 Pod 通信不通排查起来最耗费时间。6.3 部署后验证网络我常用的验证方式是启动一个 busybox Pod然后从它去 ping 任意节点的 Pod IPkubectl run test-pod --imagebusybox --restartNever -- sleep 3600 kubectl exec -it test-pod -- ping 某个Pod的IP能通说明 CNI 转发这条链路是完好的。这里补充一句ping通只是最基础的网络连通性验证但 K8s 集群中持续在用的核心通信对象是 Service 的 ClusterIP。7. 工作节点加入集群这个环节最容易出的问题7.1 执行 kubeadm joinmaster 初始化完成后工作节点上需要执行kubeadm join命令。如果 token 过期24小时或者你忘记保存 join 命令了可以通过如下方式重新生成# 在 master 上重新生成 token kubeadm token create --print-join-command然后在 node1 和 node2 上执行注意替换成你自己的 token 和 hashsudo kubeadm join 192.168.1.10:6443 \ --token token \ --discovery-token-ca-cert-hash sha256:hash执行成功后会看到输出This node has joined the cluster。回 master 执行kubectl get nodes最尴尬的场景出现了节点状态是 NotReady。怎么办7.2 节点 NotReady 的完整排查链路先别急着到处搜答案按这个顺序一步步查第一步确认 kubelet 状态systemctl status kubelet # 或 journalctl -u kubelet -f如果 kubelet 没起来大概率是配置文件有问题或 cgroup 驱动不匹配需要重点观察启动日志中是否出现failed to run Kubelet或cgroup driver等字样。第二步查看 kubelet 日志里的关键错误journalctl -u kubelet --no-pager | grep -i error | tail -20如果看到Failed to connect to apiserver检查节点到 master 的 6443 端口是否通nc -vz 192.168.1.10 6443。如果不通先解决网络连通性。第三步检查 node 的注册信息与 CNI 是否报错kubectl describe node k8s-node1看 Conditions 字段重点看Ready是 True 还是 False以及报错信息里提到 ReadyFalse 的原因。如果报CNI plugin not initialized则是 CNI 相关的问题去节点上查看 kubelet 日志。这一套链路排查下来95% 的问题都能定位到。7.3 如果 join 后怎么都加不上从节点上彻底重置当你试了很多办法还是不行或者你改了配置想重新加入节点时最干净的方式是在节点上执行sudo kubeadm reset sudo rm -rf $HOME/.kube /etc/cni/net.d然后在 master 上删除旧节点如果是同一个节点重新加入通常 master 端会自动清理但保险起见先 delete 一次kubectl delete node k8s-node1再重新执行 join。这个方法对“节点死活加不上”有奇效但属于“最后手段”先按 7.2 的链路排查完再用。8. 集群验证与第一份工作负载验证 Pod、Service、Ingress到这里恭喜你你的集群已经可以正常工作了。但“能跑”和“能用”是两回事我习惯把 K8s 集群的验证动作做完整确认这套集群不只是表面 Ready而是真正可以承载业务。8.1 部署一个 Nginx 应用并暴露为 NodePort先创建一个简单的 Deploymentkubectl create deployment nginx-demo --imagenginx:1.25 kubectl scale deployment nginx-demo --replicas3查看 Pod 分布kubectl get pods -o wide你会看到 3 个 Pod 被调度到不同节点上IP 是 Calico 分配的 10.244.x.x 网段。这时候用curl 10.244.x.x可以直接访问 Nginx 首页如果通了说明 CNI 正常。接着创建 Servicekubectl expose deployment nginx-demo --port80 --typeNodePort kubectl get svcService 会分配一个 ClusterIP10.96.x.x和一个 NodePort30000~32767 之间随机。这时候从任意节点执行curl 10.96.x.x能通从外部用curl http://任意节点IP:NodePort也能通。8.2 验证 DNS 解析与负载均衡K8s 内置的 CoreDNS 负责集群内域名解析。在一个 Pod 里测试 Service 的域名解析kubectl run curl-test --imagecurlimages/curl --rm -it --restartNever -- sh # 在容器内 curl nginx-demo.default.svc.cluster.local能通就说明 Service 的 DNS 解析、kube-proxy 转发链路都正常。小集群下kube-proxy 默认走 iptables 模式IPVS 模式需要额外在 kube-proxy 的 ConfigMap 中显式开启并确保节点上安装了 ipvsadm。但即便用默认的 iptables 模式也能做到多副本随机轮询你多发几次 curl观察几个 Pod 的访问日志能看出请求基本要轮流分配这就算负载均衡验证过了。8.3 crictl vs docker容器运行时排错命令既然用的是 Containerd日常排错就不再是docker ps了而是 crictlcrictl ps crictl images crictl logs container-id前面我提到拉取 K8s 组件镜像时遇到问题也可以用 crictl 手动 pull 镜像crictl pull registry.k8s.io/kube-apiserver:v1.29.0crictl 和 kubelet 共用同一个 containerd.sock所以在节点上观察容器状态它比 docker 命令更贴近 K8s 的真实视角。习惯用 docker 的同学建议尽早切换到 crictl否则后面排错会走弯路。9. 踩坑实录与运维提示我能帮你省下的每一小时9.1 三个反复出现的经典问题问题一Pod 一直 ContainerCreating卡在 ContainerCreating排查命令kubectl describe pod pod-name -n namespace kubectl logs pod-name -n namespace --previous最常见原因是镜像拉取失败、存储卷挂载失败、或者 CNI 没有就绪。我见过一个典型场景是用了 hostPath 卷但没有提前创建宿主机目录导致 Pod 一直 ContainerCreating 并在 Event 里直接给出挂载报错。解决方法是先建目录或者换用 PV/PVC。问题二节点 Ready 但 Pod 之间跨节点不通这大概率是 Calico BGP peer 没有建立成功。在节点上执行calicoctl node status如果没有输出 BGP peer 信息检查节点防火墙ufw是否放行了 BGP 端口 179。另外还要确认 Calico 的 IP 池网段和你 kubeadm init 时配置的 pod-network-cidr 保持一致。问题三kubelet 起不来日志提示 kubelet cgroup driver: cgroupfs is different from kubeadm cgroup driver: systemd这个错误信息非常精准说明你的容器运行时Containerd配置的还是 cgroupfs回到第 4 节做修改然后重启 Containerd再重启 kubelet这个问题就没了。9.2 关于 CoreDNS 一直 Pending或 CrashLoopBackOff如果你是严格按照我的步骤来做的CoreDNS 大概率不会出问题。但如果你用的是某些特殊网络或汇总配置CoreDNS 可能会一直 Pending 或 CrashLoopBackOff。Pending 通常是节点资源不足或调度约束导致确认一下节点有没有污点即可CrashLoopBackOff 则和 CoreDNS 的配置文件有关——如果你改过 ClusterIP 网段但没有同步调 CoreDNS 的上游 DNS 配置CoreDNS 就会反复重启。排查的时候先看日志kubectl logs -n kube-system -l k8s-appkube-dns --tail509.3 集群日常管理里的几个实用技巧隔三差五看看事件与资源用量别等告警才知道kubectl get events --sort-by.lastTimestamp | tail -20 kubectl top node kubectl top pod -A在 master 上拿命令控全局我强烈建议日常只在 master 节点上使用 kubectl不要随意去 node 操作。master 上的 kubeconfig 默认是 admin 权限别随手让人拿去用了。知识库组件升级时先读 release notes升级集群不是一件小事。每次 K8s 的次版本升级都可能有破坏性的变更比如 v1.24 移除 dockershim、v1.25 开始不再默认启用 PodSecurityPolicy 等。不管在什么环境升级前都要先备份 etcd并对关键配置做 snapshot。10. 生产集群还远远不止这一步到这里你已经拥有了一套可以运行、可以调度 Pod 的 Kubernetes 集群。但这套集群离生产环境要求还很远——控制平面是单点、etcd 没有独立部署、没有日志监控体系、没有 ingress 控制器、没有持久化存储方案、没有命名空间隔离策略。作为起步这个状态足够你学习和验证核心概念作为学习路线图你下一步很自然地会去研究高可用控制平面kubeadm 支持多 master 外部负载均衡、ingress-nginx、metrics-server Prometheus、以及基于 NFS 或云盘的 CSI 存储方案。我最初搭第一套 K8s 集群的时候前后折腾接近两周大量时间花在版本不匹配和网络插件选型上。现在是2025年社区资料比我那时丰富太多了照着这份流程配合耐心读日志一套测试集群配下来三四个小时是比较正常的节奏。真踩到坑了不要慌先用 describe 和 journalctl 把日志完整看一遍再动手改东西很多“玄学”问题到最后都会发现是自己某个配置细节没对。希望这份指南对你有实质性的帮助。练手过程中遇到任何问题欢迎在评论区一起讨论我看到都会回复。