首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
从裸机到Kubesphere:自建K8s集群与容器编排实战
📅 2026/10/22 21:41:47
✍️ 爱科研究院
👁 阅读 3,247
1. 项目缘起与整体架构设计1.1 为什么选择自建集群而不是托管方案最早接触容器编排是在一个内部测试环境里当时用单节点跑几个Demo服务觉得这东西确实方便。后来业务侧要求把一批有状态服务也迁进来单节点扛不住才开始认真考虑多节点集群的事。摆在面前的有两条路一是直接用云厂商的托管K8s服务二是自己从裸机开始搭。托管方案省心控制面不用自己维护但有几个现实问题绕不开——测试环境预算有限托管集群的控制面费用按小时计费长期跑下来成本不低另外有些内部网络策略和存储挂载方式在托管环境里受限调试起来反而更麻烦。所以最终决定自己搭一套用三台物理机做基础后续再按需扩容。这个选择背后的逻辑其实很简单可控性和成本。自建集群意味着从etcd到kubelet的每一层你都能碰出问题能查到根上而不是对着云厂商的控制台干瞪眼。代价就是运维复杂度上来了升级、备份、证书轮换都得自己管。所以这套方案更适合有一定Linux基础、愿意花时间折腾的团队纯小白或者业务压力极大的生产环境我还是建议先评估托管方案。1.2 集群拓扑与节点角色划分三台机器的规划是这样的一台做控制面Control Plane跑kube-apiserver、kube-scheduler、kube-controller-manager和etcd另外两台做工作节点Worker Node跑业务Pod。有人会问为什么不做三控制面高可用答案很直接——测试环境资源有限三台机器如果都做控制面剩下的计算资源跑不了几个Pod。单控制面在测试环境完全够用挂了重启就行生产环境再考虑堆叠式etcd或者外部etcd集群。节点角色划分上有个细节要注意控制面节点默认带一个污点taint不允许普通Pod调度上去。这个设计是为了保护控制面资源不被业务抢占。如果你资源实在紧张可以手动去掉这个污点但我不建议这么做控制面一旦被业务Pod拖垮整个集群就失联了。工作节点则要打上标签label方便后续用nodeSelector或者亲和性调度把特定业务固定到特定节点。网络规划方面我用了三个网段节点物理网络用192.168.1.0/24Pod网络用10.244.0.0/16Service网络用10.96.0.0/12。这三个网段不能重叠否则路由会出问题。Pod网段和Service网段的具体值可以改但一旦集群初始化完成就不能再动所以规划阶段就要想清楚。1.3 容器运行时与网络插件的选型考量容器运行时这块早期Docker是默认选择但从K8s 1.24开始dockershim被移除containerd成了主流。我选containerd的原因很简单它更轻量调用链更短出问题时排查路径清晰。Docker作为运行时多了一层dockerd虽然对开发者友好但在K8s场景下这层封装反而增加了不确定性。安装containerd之后需要配置SystemdCgroup驱动这一步如果漏了kubelet启动时会报cgroup不匹配的错误节点会一直NotReady。网络插件我选了Calico而不是Flannel。Flannel配置简单但只提供基本的Overlay网络没有网络策略NetworkPolicy支持。Calico用BGP协议做路由分发性能更好而且自带网络策略功能后续如果要做Pod间的访问控制不用再换插件。Calico的IPIP模式在跨网段通信时会有封装开销但在同网段内可以直接路由实测下来延迟比Flannel的VXLAN模式低不少。如果你集群节点都在同一个二层网络里Calico的BGP模式甚至不需要封装性能接近原生网络。2. 系统初始化与运行时环境准备2.1 操作系统层面的基础配置三台机器统一装的是Ubuntu 22.04 LTS内核版本5.15。选这个版本是因为它对cgroup v2支持完善containerd和kubelet都能直接用。装完系统后第一件事是关swap这不是可选项是必须项。kubelet在swap开启的情况下行为不可预测调度器计算资源时也会出错。临时关闭用swapoff -a永久关闭要注释掉/etc/fstab里的swap行两处都做才彻底。接下来是内核参数调整。br_netfilter模块要加载否则iptables看不到桥接流量Pod之间通信会断。net.bridge.bridge-nf-call-iptables和net.bridge.bridge-nf-call-ip6tables都要设为1。net.ipv4.ip_forward也要开这是Pod跨节点通信的基础。这些参数写进/etc/sysctl.d/k8s.conf用sysctl --system生效。我见过有人只改了/etc/sysctl.conf结果重启后配置丢失节点直接失联所以配置文件的位置要记牢。时间同步容易被忽略但etcd对时间敏感节点间时间偏差超过一定阈值会导致选举异常。装个chrony或者systemd-timesyncd确保三台机器时间一致。主机名也要规划好用hostnamectl set-hostname设置成有意义的名字比如k8s-master、k8s-node1、k8s-node2后面加入集群时靠主机名区分节点比记IP方便。2.2 containerd安装与关键配置项containerd的安装有两种方式apt直接装或者下载二进制包。apt装的是系统仓库里的版本可能偏旧二进制包能选最新稳定版。我用的二进制包从官方release页面下载后解压到/usr/local/bin然后写一个systemd unit文件。unit文件里ExecStart指向/usr/local/bin/containerdDelegate设为yes这样containerd能自己管理cgroup。配置文件/etc/containerd/config.toml是重点。先用containerd config default生成默认配置然后改两个地方一是SystemdCgroup设为true二是sandbox_image改成国内能拉到的pause镜像地址。SystemdCgroup不设true的话kubelet和containerd的cgroup驱动不一致节点会报failed to run Kubelet: misconfiguration: kubelet cgroup driver: cgroupfs is different from docker cgroup driver: systemd这类错误。pause镜像地址改掉是因为默认的registry.k8s.io在国内拉取不稳定换成阿里云或者腾讯云的镜像仓库地址能省很多等待时间。改完配置后systemctl restart containerd然后用ctr命令验证一下。ctr version能看到客户端和服务端版本ctr images pull拉一个测试镜像能拉下来说明运行时没问题。这里有个小坑ctr默认用default命名空间而k8s用的是k8s.io命名空间用ctr -n k8s.io images list才能看到k8s拉下来的镜像。2.3 kubelet、kubeadm、kubectl三件套安装这三个组件的版本要一致不能kubeadm用1.28而kubelet用1.27。安装方式我用的是apt仓库先加Google的apt key再加源然后apt install kubelet kubeadm kubectl。加key的时候如果网络不通可以用国内镜像源的key。装完后systemctl enable kubelet注意这时候kubelet还起不来因为还没初始化集群这是正常的不用慌。kubelet的配置文件在/var/lib/kubelet/config.yaml这个文件是kubeadm初始化时生成的不用手动改。但有一个参数值得关注cgroupDriver。如果containerd配的是systemd这里也必须是systemdkubeadm会自动检测但如果你手动改过containerd配置最好确认一下。另外resolvConf指向/run/systemd/resolve/resolv.conf如果系统用的是其他DNS管理方式这个路径要相应调整否则Pod里的DNS解析会出问题。kubectl是客户端工具装在哪台机器都行我一般装在控制面节点上方便直接操作。装完后配一下bash补全source (kubectl completion bash)能省不少敲命令的时间。kubectl的配置文件在/etc/kubernetes/admin.conf普通用户想用的话要拷贝到~/.kube/config并改权限。3. 集群初始化与网络插件部署3.1 kubeadm init参数详解与执行过程初始化命令我用的是kubeadm init配合一个配置文件而不是一长串命令行参数。配置文件的好处是可读、可版本控制、可复用。配置文件里几个关键字段kubernetesVersion指定版本号controlPlaneEndpoint填控制面节点的IP和端口默认6443podSubnet填10.244.0.0/16serviceSubnet填10.96.0.0/12。imageRepository改成国内镜像仓库地址否则拉镜像会卡住。执行kubeadm init --config kubeadm-config.yaml之后它会做一系列检查然后拉镜像、生成证书、启动控制面组件。这个过程大概三到五分钟取决于网络速度。看到Your Kubernetes control-plane has initialized successfully!就说明成功了。这时候它会输出两段重要信息一段是配置kubectl的命令一段是工作节点加入集群的token。这两段都要保存下来尤其是token默认24小时过期过期后要用kubeadm token create重新生成。初始化完成后kubectl get nodes应该能看到控制面节点状态是NotReady。这是正常的因为网络插件还没装。kubectl get pods -n kube-system能看到apiserver、scheduler、controller-manager、etcd都在跑coredns是Pending状态等网络插件装好就会起来。3.2 Calico部署与网络连通性验证Calico的部署用kubectl apply一个yaml文件就行但yaml里的Pod网段要和kubeadm配置的一致。官方提供的calico.yaml默认用192.168.0.0/16和我的10.244.0.0/16不一样所以要改。改的地方在CALICO_IPV4POOL_CIDR这个环境变量找到它把值改掉。另外IP_AUTODETECTION_METHOD可以设成interfaceeth0或者can-reach192.168.1.1让Calico自动选对网卡避免选到docker0或者别的虚拟网卡上。apply之后等一两分钟kubectl get pods -n kube-system看到calico-node和calico-kube-controllers都Runningkubectl get nodes状态变成Ready就说明网络通了。验证网络连通性可以起两个测试Pod一个在node1一个在node2互相ping一下。kubectl run test1 --imagebusybox --overrides{spec:{nodeName:k8s-node1}} -- sleep 3600类似地起test2在node2上然后kubectl exec进去ping对方的Pod IP。能ping通说明跨节点网络没问题。Calico的BGP模式在同网段下不需要IPIP封装性能好。但如果节点跨网段就要开IPIP或者VXLAN。判断方法很简单kubectl get ippool -o yaml看ipipMode字段如果是Never就是纯BGPAlways就是全封装。同网段建议用Never跨网段用CrossSubnet只在跨网段时封装。3.3 工作节点加入与集群状态检查工作节点加入集群用kubeadm init输出的kubeadm join命令带上token和discovery-token-ca-cert-hash。这个hash是控制面CA证书的指纹用来验证控制面身份不能省。join命令执行后节点会拉镜像、启动kubelet、向apiserver注册。在控制面上kubectl get nodes能看到新节点状态从NotReady变成Ready大概需要一两分钟。节点加入后有几个检查项kubectl get nodes -o wide看内网IP对不对kubectl describe node k8s-node1看Conditions里MemoryPressure、DiskPressure、PIDPressure是不是Falsekubectl get pods -n kube-system -o wide看calico-node是不是在新节点上也Running了。如果节点一直NotReady大概率是网络插件没起来或者containerd配置有问题journalctl -u kubelet -f看日志能定位到具体原因。有个常见问题是节点加入后Pod调度不上去kubectl describe pod看到0/3 nodes are available: 3 node(s) had taint。这是因为控制面有污点工作节点如果也有污点就要检查是不是kubeadm join时带了--taint参数。默认工作节点没有污点但如果之前手动加过用kubectl taint nodes k8s-node1 key:NoSchedule-去掉。4. Kubesphere安装与功能模块配置4.1 Kubesphere与K8s的版本匹配策略Kubesphere对K8s版本有要求不是随便哪个版本都能装。装之前先去官方文档查兼容性矩阵比如Kubesphere 3.4支持K8s 1.21到1.263.3支持1.19到1.24。版本不匹配的话安装过程中会报各种奇怪的错误比如CRD不兼容、API版本找不到。我用的K8s 1.26配Kubesphere 3.4这个组合是官方验证过的相对稳。安装方式有两种一是用ks-installer在K8s集群里跑一个安装器Pod通过改ConfigMap来启用各个组件二是用KubeKey从裸机开始一键装K8s加Kubesphere。我选的是ks-installer因为集群已经搭好了不想重装。ks-installer的原理是在集群里创建一堆CRD和Operator然后根据ConfigMap里的开关决定装哪些组件。这种方式对已有集群侵入性小卸载也方便。安装前要确认集群资源够用。Kubesphere最小化安装只装核心组件大概需要4核8G全功能安装加上监控、日志、DevOps、ServiceMesh要8核16G以上。我的三台机器每台4核8G只装了核心加监控DevOps和ServiceMesh没开资源刚好够。如果资源不够安装过程中Pod会一直Pending看事件是Insufficient cpu或者Insufficient memory。4.2 ks-installer部署与核心组件启用安装第一步是apply两个yaml一个是kubesphere-installer.yaml创建Namespace和Deployment另一个是cluster-configuration.yaml定义要启用哪些组件。apply之后kubectl get pods -n kubesphere-system能看到ks-installer的Pod在跑它会根据cluster-configuration里的配置去装各个组件。这个过程比较长核心组件装完大概10到15分钟如果开了监控和日志可能要20分钟以上。cluster-configuration.yaml里几个关键开关etcd.monitoring控制etcd监控local_store控制本地存储monitoring控制Prometheus监控栈logging控制日志收集devops控制DevOps流水线servicemesh控制Istio。每个开关打开都会部署对应的Operator和CR资源消耗不小。建议第一次装只开核心加monitoring跑稳了再逐步开其他。安装过程中用kubectl logs -n kubesphere-system deploy/ks-installer -f看进度看到Installation is successful就完成了。然后kubectl get svc -n kubesphere-system找到ks-console的NodePort用浏览器访问http://节点IP:30880默认账号admin密码P88w0rd。第一次登录会要求改密码改完就进控制台了。4.3 存储、监控与日志模块的落地配置Kubesphere的存储模块依赖StorageClass。测试环境没有独立存储设备我用的是local path provisioner把节点上的某个目录当存储池。装完Kubesphere后kubectl get sc应该能看到local的StorageClass。如果没有可以手动装一个local-path-provisioner或者用OpenEBS的hostpath模式。存储这块要注意的是local volume和节点绑定Pod漂移到其他节点后数据就没了所以只适合测试生产要用网络存储。监控模块装好后Kubesphere控制台里能看到集群的CPU、内存、磁盘、网络监控图表。底层是Prometheus加GrafanaKubesphere做了封装。如果监控数据出不来检查kubectl get pods -n kubesphere-monitoring-system看prometheus-k8s的Pod是不是Running。常见问题是Prometheus的PVC没绑定kubectl get pvc -n kubesphere-monitoring-system看状态是不是Bound。没绑定的话检查StorageClass和PVC的storageClassName是否匹配。日志模块用的是Fluent Bit加Elasticsearch。Fluent Bit以DaemonSet形式跑在每个节点上收集容器日志发到Elasticsearch。装完后在控制台里能按Pod、按命名空间查日志。如果日志查不到先看Fluent Bit的Pod日志kubectl logs -n kubesphere-logging-system -l app.kubernetes.io/namefluent-bit常见错误是Elasticsearch连不上或者索引创建失败。Elasticsearch的资源消耗比较大如果节点内存不够可以调小JVM堆大小在kubectl edit statefulset elasticsearch-logging-data -n kubesphere-logging-system里改ES_JAVA_OPTS。5. 常见故障排查与运维经验5.1 节点NotReady的排查路径节点NotReady是最常见的问题排查思路是从下往上先看kubelet服务状态systemctl status kubelet如果服务没跑起来journalctl -u kubelet -n 100看报错。常见原因有containerd没启动、cgroup驱动不匹配、证书过期、配置文件路径错误。containerd的问题用systemctl status containerd和journalctl -u containerd查cgroup驱动不匹配的报错信息很明确直接改配置就行。如果kubelet在跑但节点还是NotReady看kubectl describe node的Conditions。Ready为False时Message字段会说明原因比如container runtime not ready或者network plugin not ready。container runtime not ready一般是containerd的问题network plugin not ready是Calico没起来。Calico没起来的原因可能是镜像拉不下来、RBAC权限不够、或者网段配置冲突。kubectl logs -n kube-system -l k8s-appcalico-node能看到具体报错。还有一种情况是节点状态是Ready但Pod调度不上去kubectl describe pod看到node(s) had taint或者Insufficient cpu。taint的问题用kubectl describe node看Taints字段有污点就根据情况去掉。资源不足的话kubectl describe node看Allocated resources对比Capacity和Allocatable如果Allocatable很小可能是kubelet预留资源设得太大改/var/lib/kubelet/config.yaml里的systemReserved和kubeReserved。5.2 Pod启动失败的典型场景与处理Pod一直Pending先看kubectl describe pod的Events。0/3 nodes are available后面跟的原因很关键Insufficient cpu是CPU不够node(s) had taint是污点问题node(s) didnt match node selector是标签不匹配pod has unbound immediate PersistentVolumeClaims是PVC没绑定。PVC没绑定的问题kubectl get pvc看状态Pending的话kubectl describe pvc看Events常见原因是StorageClass不存在或者Provisioner没跑。Pod一直ContainerCreating看Events里有没有Failed to pull image或者Failed to create pod sandbox。拉镜像失败检查镜像地址和网络crictl pull手动拉一下试试。sandbox创建失败一般是CNI插件的问题ls /etc/cni/net.d/看有没有Calico的配置文件没有的话说明Calico没装好。还有一种情况是failed to set up sandbox container network: failed to allocate for range这是IP池耗尽kubectl get ippool -o yaml看IP池大小不够就扩。Pod CrashLoopBackOff看kubectl logs的报错。如果是应用自身的问题日志里会有明确信息。如果是OOMKilled说明内存超了调大resources.limits.memory或者优化应用内存占用。如果是Error: cannot find module之类的是镜像里缺依赖重新打镜像。CrashLoopBackOff的退避时间会越来越长从10秒到5分钟调试时可以用kubectl run起一个带sleep的临时Pod把镜像挂进去手动跑命令比反复重启快。5.3 集群证书过期与etcd备份恢复K8s的证书默认有效期一年到期后apiserver会拒绝连接kubectl报Unable to connect to the server: x509: certificate has expired。检查证书有效期用kubeadm certs check-expiration续期用kubeadm certs renew all。续期后要重启控制面组件docker ps或者crictl ps找到apiserver、scheduler、controller-manager、etcd的容器crictl rm删掉kubelet会自动重建。注意etcd的证书续期后也要重启etcd容器否则etcd还是用旧证书。etcd备份是运维的底线。备份命令用etcdctl snapshot save要指定endpoint和cacert、cert、key。这些文件在/etc/kubernetes/pki/etcd/目录下。备份文件存到集群外的机器上别放本地节点挂了备份也没了。恢复的时候用etcdctl snapshot restore恢复到一个新的data目录然后改etcd的static pod yaml里的--data-dir指向新目录重启etcd。恢复操作风险高建议先在测试环境练几遍。Kubesphere的备份主要是备份它的CRD和ConfigMap。kubectl get crd | grep kubesphere能看到一堆CRD用kubectl get导出成yaml存起来。ConfigMap在kubesphere-system和kubesphere-monitoring-system命名空间下也导出备份。Kubesphere本身没有一键备份恢复的功能所以定期手动备份是必要的。如果集群要重建先装K8s再装Kubesphere然后把备份的CRD和ConfigMap apply回去能恢复大部分配置。5.4 资源规划与性能调优的实操建议测试环境资源有限调优的目标是用最少资源跑最多东西。kubelet的--eviction-hard参数控制驱逐阈值默认memory.available100Mi磁盘nodefs.available10%。如果节点磁盘小这个阈值要调低否则Pod频繁被驱逐。--image-gc-high-threshold和--image-gc-low-threshold控制镜像回收默认85%和80%磁盘紧张的话调到70%和60%让kubelet更早清理旧镜像。Calico的FelixConfiguration可以调iptablesRefreshInterval和routeRefreshInterval默认90秒和90秒调大到300秒能减少CPU占用。prometheus的采集间隔默认30秒调到60秒能省一半的存储和CPU。Kubesphere的监控数据保留时间默认7天改kubectl edit prometheus -n kubesphere-monitoring-system里的retention字段改成3天能省不少磁盘。节点上的系统预留资源要设合理。systemReserved给系统进程留kubeReserved给kubelet和容器运行时留。4核8G的节点systemReserved设cpu: 500m, memory: 1GikubeReserved设cpu: 500m, memory: 1Gi这样Allocatable大概是3核6G能跑不少Pod。不设的话系统进程和业务Pod抢资源节点容易卡死。改完systemctl restart kubelet生效然后kubectl describe node确认Allocatable变了。6. 个人实操体会与后续扩展方向这套集群从裸机到Kubesphere跑起来前后折腾了大概两天其中大部分时间花在排查网络和存储问题上。最大的体会是日志和事件是第一手资料遇到问题先看kubectl describe和journalctl比在网上搜半天管用。另一个体会是版本匹配比什么都重要K8s、containerd、Calico、Kubesphere这四个组件的版本要互相兼容装之前花十分钟查兼容性矩阵能省后面几小时的排查时间。后续扩展的话有几个方向可以考虑。一是加节点做高可用控制面把etcd从单点变成三节点集群这样控制面挂一台不影响集群。二是接外部存储用NFS或者Ceph做StorageClass解决local volume不能漂移的问题。三是开DevOps模块把CI/CD流水线跑起来配合GitOps做应用发布。四是开ServiceMesh用Istio做流量管理和可观测性不过这个对资源要求高节点配置得跟上。最后分享一个小技巧Kubesphere的ks-installer在安装过程中如果卡住不要急着删Pod重来。先看kubectl logs -n kubesphere-system deploy/ks-installer的日志很多时候是某个镜像拉不下来或者某个Job超时手动拉一下镜像或者调大超时时间就能过。删了重来反而可能因为残留资源导致新问题。另外cluster-configuration.yaml改完之后ks-installer会自动检测变化并重新执行不用手动重启Pod等几分钟就行。
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/10/22 21:41:47
基于仿真的SOTIF验证:系统性识别自动驾驶可预见误用
2026/10/22 21:41:47
移动MAS短信发送对接HTTP接口实战:原理、步骤与排查指南
2026/10/22 21:41:47
AI目录生成:重构论文写作流程的关键节点与实操
2026/10/23 0:02:44
Rust正成为AI基础设施的底层新标准
2026/10/23 0:02:44
Rust为何成为AI基础设施的确定性选择
2026/10/23 0:02:44
FreeRTOS任务通知实战详解:内核原理、API与避坑指南
2026/10/23 0:02:44
数据服务监控运维实战:从指标体系到告警治理与故障排查
2026/10/23 0:02:44
Verilog实现FPGA密码锁:矩阵键盘与状态机设计实战
2026/10/22 23:57:43
SQL注入原理与防御实战:从数据库安全到参数化查询的完整指南
2026/10/23 0:02:44
Verilog实现FPGA密码锁:矩阵键盘与状态机设计实战
2026/10/23 0:02:44
数据服务监控运维实战:从指标体系到告警治理与故障排查
2026/10/23 0:02:44
FreeRTOS任务通知实战详解:内核原理、API与避坑指南
2026/10/21 19:05:16
OpenResearch实战:Claude Code、Codex、OpenCode、Cursor 工作流搭建与故障排查
2026/10/22 17:15:21
知识管理核弹:用Obsidian构建第二大脑的工程化方案(TaoToken 统一 Key 接入版)
2026/10/21 19:05:04
金融AI落地指南:从调研报告到技术选型与工程实践
2026/10/22 5:06:15
我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
2026/10/22 2:07:18
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026/10/21 19:01:08
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)