ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

生产级K8s部署全指南:集群搭建、高可用与监控排障实战

生产级K8s部署全指南:集群搭建、高可用与监控排障实战 K8s部署这话题网上教程一抓一大把但多数是“能跑起来”就结束。真正到生产环境你会发现坑全在后面证书过期、节点NotReady、GPU调度不上、监控数据丢了找谁背锅……这篇博文不聊虚的直接围绕生产级K8s交付的核心链路来拆——怎么设计、怎么装、怎么监控、怎么排障、怎么扩展把热词里涉及的集群搭建、高可用、GPU调用、Prometheus监控、Operator案例这些东西一次性讲透。前半部分偏实操后半部分偏运维建议按顺序看想跳着读也行每个章节相对独立。1. 整体思路与设计拆解部署K8s前先想清楚这三层问题很多人一上来就装K8s装完发现网络不通、存储没法用、Pod调度一塌糊涂本质是没理解K8s不是“一个软件”而是一套“控制面数据面网络存储底座”的组合体。所以在动任何命令之前必须先把设计思路理清楚。1.1 先搞清楚K8s和Docker到底啥关系这问题在面试题里出现的频率极高但在实际部署时同样重要。Docker只是容器运行时的一种负责把镜像跑成容器K8s是编排调度平台负责决定容器跑在哪台机器、挂了怎么拉起、流量怎么分发、配置怎么下发。类比一下Docker是装修队K8s是物业公司。装修队能把房间弄好但整栋楼的水电、楼道、安保、故障响应得物业来管。这也是为什么现在生产环境更推荐containerd而不是Docker作为K8s的运行时——K8s通过CRIContainer Runtime Interface和运行时通信containerd直接支持CRI不需要额外装Dockershim而Docker走的是另一套适配层多一层就多一个故障点性能损耗和排查链路都更长。后面实操部分我会直接用containerd这也是热词里“ubuntu docker k8s”这类组合现在的主流替代方案。1.2 控制面与工作节点K8s的“大脑”和“手脚”必须分开K8s集群分两类角色控制面节点Master和工作节点Worker。控制面跑核心组件——API Server、Controller Manager、Scheduler、etcd它们是集群的大脑工作节点跑业务Pod是真正干活的手脚。我见过不少小团队用单节点All-in-One部署图省事。短期看没问题但一旦这节点挂了整个集群连查询API的能力都没有业务恢复更是无从谈起。生产环境最低标准是三台控制面节点组成高可用为什么是三台——因为控制面的核心状态存储在etcd里etcd用的是Raft一致性协议要求多数节点存活才能选举出Leader并对外提供服务。三节点集群允许挂一个五节点允许挂两个再多节点对多数派的容错提升有限但同步开销和运维成本直线上升。这就是热词里“k8s三台master怎么保证高可用”这个问题的底层逻辑。1.3 部署方案选型kubeadm、Kubekey、二进制到底选哪个市面上主流的部署方式有三种各有适用场景我直接给结论kubeadm是Kubernetes官方推荐的部署工具生命周期管理完善证书续期、升级都有配套命令。适合绝大多数生产环境也是我下面实操部分采用的方式。Kubekey是青云开源的一键部署工具对中文字档场景友好自动化程度高多节点、高可用配置都能通过配置文件搞定。适合快速拉起一套标准集群但在定制化场景下不如kubeadm透明。热词里“ubuntu高可用k8s部署”很多人用Kubekey做思路没问题但出了问题你得更依赖社区而不是官方文档。二进制方式手动下载每个组件、自己写systemd服务灵活度最高但维护成本极高证书、配置文件全得手管出一次问题就要折腾半天。这个适合学习源码级机制不适合生产。我的建议是标准生产环境用kubeadm快速实验环境用Kubekey二进制方式至少手动部署一次那是理解K8s工作原理的最佳途径。2. 环境准备与组件解析内核参数、运行时选型、网络插件一个都不能少部署K8s不是只要有机器就行底层系统环境不过关后面各种诡异问题都能找上来。这里主要讲操作系统准备、运行时安装和网络插件选型三件事。2.1 操作系统与内核参数这些值不调集群早晚出问题以Ubuntu Server 22.04 LTS为例首先确保/etc/hostname每台机器不同、/etc/hosts写清所有节点IP和主机名映射。然后是要开启内核模块overlay和br_netfilter以及调整net.bridge.bridge-nf-call-iptables1等参数。用一句话解释背后的原因K8s的Service和网络插件比如Calico大量依赖iptables和IPVS做流量转发而iptables要处理的是桥接流量内核默认不把桥接流量交给iptables管所以必须显式打开bridge-nf-call-iptables否则集群内的Service访问会莫名其妙不通。具体参数我贴一份可直接用的配置cat EOF | sudo tee /etc/sysctl.d/k8s.conf net.ipv4.ip_forward 1 net.bridge.bridge-nf-call-iptables 1 net.bridge.bridge-nf-call-ip6tables 1 fs.inotify.max_user_instances 8192 fs.inotify.max_user_watches 524288 vm.max_map_count 262144 EOF sudo sysctl --system这三个inotify参数是后来补的为什么因为K8s的kubelet和容器运行时大量使用inotify监听文件变化默认值在Pod数量上来以后会触到上限表现就是Pod创建失败、日志报too many open files或inotify实例耗尽。另外要关闭swap。K8s设计上是假设节点没有swap的原因在于容器内存限制的可靠性。你不关swapkubelet启动就会报错。执行swapoff -a并把/etc/fstab里的swap行注释掉重启后依然生效。2.2 containerd作为运行时配置这几个关键项安装containerd用官方源或Docker官方源都行。安装后需要生成默认配置并修改关键参数containerd config default | sudo tee /etc/containerd/config.toml改两个地方一是SystemdCgroup true这关系到cgroup驱动和kubelet的cgroup驱动保持一致不然后面节点状态会直接异常二是sandbox_image换到国内能稳定拉取的地址避免pause容器拉取超时导致Pod一直ContainerCreating。然后启动并设置开机自启用ctr version验证安装。这块细节点比较多最容易翻车的是cgroup驱动不一致kubelet默认使用systemd作为cgroup驱动containerd如果还用cgroupfs节点状态会变成NotReady查半天日志才发现是驱动不匹配。2.3 网络插件Calico和Flannel怎么选网络插件解决的是Pod间跨节点通信问题。Flannel实现简单、配置容易就是VXLAN隧道包一层性能损耗大概在10%-20%Calico用BGP协议直接路由性能好还支持NetworkPolicy生产环境首选。K8s集群搭建后网络插件是必须安装的核心组件否则Pod网段和Service网段都无法正常工作。选Calico要注意它有两种数据转发模式iptables和eBPF。eBPF性能更好但依赖内核版本建议5.7Ubuntu 22.04默认5.15内核可以直接用。在集群初始化后执行Calico的manifest文件部署即可一般几分钟内Pod会变成Running。3. 生产级实操用kubeadm搭建多节点集群然后跑通GPU调度这部分是全文最核心的实战内容。我会完整拆解从初始化控制面到加入工作节点、再到配置GPU调度的全过程每一步都会解释参数为什么这么写。3.1 kubeadm初始化控制面关键参数与版本组合先确认版本组合我推荐一个长期稳定、信息差较小的组合同一K8s版本下控制面组件与kubelet的版本差不能超过一个minor版本组件版本建议Kubernetesv1.29.xcontainerd1.7.xCalicov3.27etcd随kubeadm内置无需单独管初始化命令里最容易被忽视的是--apiserver-advertise-address和--pod-network-cidr。前者要填控制面节点的内网IP不能填公网IP后者要记牢后续Calico的Pod网段必须和它一致。我在实际项目中习惯把Pod网段定为10.244.0.0/16Service网段定为10.96.0.0/12这是Flannel的默认风格Calico同样兼容关键是全局统一。sudo kubeadm init \ --apiserver-advertise-address10.0.0.10 \ --pod-network-cidr10.244.0.0/16 \ --service-cidr10.96.0.0/12 \ --image-repositoryregistry.aliyuncs.com/google_containers \ --control-plane-endpoint10.0.0.10:6443这里--image-repository是组件镜像源替换解决的是拉取kube-apiserver、kube-controller-manager、kube-scheduler等镜像的网络问题。有人问这算不算自主可控——更准确的说法是K8s本身是开源项目核心代码完全自主可控但默认镜像仓库在国外部署时需要配置镜像源或离线包这是部署环节的“本地化适配”问题和软件本身是否可控是两码事。初始化成功后会输出两段关键信息一段是kubeconfig配置命令一段是kubeadm join命令。把join命令存到文本文件里后面加节点全靠它。3.2 kubectl常用命令和节点管理从加入Worker到验证集群先把kubectl配置好用管理员用户执行mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config。然后安装网络插件kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.27/manifests/calico.yaml这里要注意如果网络受限拉不到这个manifest可以改为在能访问的机器上下载后传到服务器。等kubectl get pods -n kube-system里calico相关Pod都Running了节点状态才可能Ready。工作节点加入的命令需要配置好/etc/hosts确保能解析到控制面节点IP然后执行上面保存的join命令sudo kubeadm join 10.0.0.10:6443 --token xxxxx --discovery-token-ca-cert-hash sha256:xxxxx如果token过期了可以在控制面节点重新生成kubeadm token create --print-join-command。Worker节点加入后它自己会从控制面拉取kubelet和kube-proxy所需的镜像吗不会kubelet是安装kubeadm时一并装的二进制镜像则主要是pause和kube-proxykubeadm join会自动拉取。验证集群状态用这几条命令kubectl get nodes -o wide kubectl get pods -A kubectl get cskubectl get cscomponentstatuses在新版本里因为健康检查机制改版可能返回空结果这个不是异常别被误导。3.3 K8s调用GPU节点打标签、安装设备插件、配置调度三件套这个需求在实际项目中出现的频率超出想象热词“k8s调用gpu”就是典型的AI推理、模型训练上云场景。要让K8s在指定节点上调度GPU最常规的路径是三步节点打标签与污点、安装NVIDIA设备插件、在Deployment里声明GPU资源。先给GPU节点打上专属标签和污点让非GPU任务默认不调度过去kubectl label node gpu-node-01 gpupresent kubectl taint nodes gpu-node-01 nvidia.com/gpu:NoSchedule污点的作用相当于“这个节点只接GPU任务的客”普通Pod没有对应容忍度就不会被调度过来。然后安装NVIDIA的k8s-device-pluginkubectl create -f https://raw.githubusercontent.com/NVIDIA/k8s-device-plugin/v0.14.0/nvidia-device-plugin.yml这个插件会把节点上的GPU数量、型号作为可调度资源上报给API Server。等插件Pod Running后kubectl describe node gpu-node-01就能看到类似nvidia.com/gpu: 1的资源条目。最后在应用部署清单里声明需要一块GPUapiVersion: apps/v1 kind: Deployment metadata: name: gpu-inference spec: replicas: 1 selector: matchLabels: app: gpu-inference template: metadata: labels: app: gpu-inference spec: containers: - name: inference image: registry.example.com/inference:latest resources: limits: nvidia.com/gpu: 1 tolerations: - key: nvidia.com/gpu operator: Exists effect: NoSchedule注意requests可以不写GPU但limits必须声明nvidia.com/gpu只有声明了limit调度器才知道这个Pod需要绑定GPU并触发设备插件的分配逻辑。同时要写tolerations容忍度要和节点上的污点对应上否则调度器直接跳过。顺带一提监控GPU指标采集官方推荐dcgm-exporter它和device-plugin配套输出Prometheus格式指标后面接入监控栈即可。3.4 高可用部署补充三台Master的关键在于etcd和负载均衡热词里问到“三台master怎么保证高可用”这里我补充kubeadm方式下的关键设计。三台Master上的API Server是并列的对外必须有一个统一的入口——通常用HAProxy或Nginx做TCP负载均衡请求打到一个虚拟IP或域名再由负载均衡转发到三台API Server。etcd则是三节点集群数据自动同步挂了一台不影响读写。kubeadm init时--control-plane-endpoint要填负载均衡的地址不能填单台Master的IP。后续把另外两台Master通过kubeadm join --control-plane加入时它们会自动成为控制面成员。高可用真正的难点在于etcd备份和恢复策略。三台master的高可用能防单点故障但不能防误删除和灾难性误操作。etcd定期快照备份是必须的etcdctl snapshot save并把备份文件定期离线保存。这个习惯救过我的生产集群不止一次后面排障章节里展开。4. Prometheus监控K8sOperator不是魔法但它是控制器的最佳实践集群搭完第一件该做的事不是立刻跑业务而是先把监控装上。Prometheus配合K8s是标配但直接部署一套裸Prometheus在YAML里填targets的方式在K8s动态环境里维护成本极高——Pod重启IP就变了。这就是Prometheus Operator存在的意义。4.1 Operator的本质把运维经验代码化热词“k8s中operator案例”以及“k8s控制器”其实指向同一个概念——Operator是控制器Controller的一种具体实践本质是用Kubernetes的声明式API能力把某个应用的运维逻辑变成代码。控制器通过Reconcile循环不断对比“期望状态”和“实际状态”一旦有偏差就执行操作把实际拉回期望。最直观的类比是中央空调的恒温器期望温度24度实际温度是25度控制器就启动制冷到了24度就停止。写代码替代运维人员反复操作K8s里最经典的Operator案例就是Prometheus Operator、etcd Operator和MySQL Operator这类。前置知识要先理清三个概念CRD是自定义资源相当于自定义一张数据库表Controller是监听这张表的增删改查并执行动作的循环逻辑Operator是CRD加Controller加业务逻辑的整体封装。4.2 Prometheus Operator部署与关键配置部署Prometheus Operator一般都基于kube-prometheus项目它把Prometheus Operator、Prometheus、Alertmanager、Grafana以及一堆exporter打包在一起一条命令拉起来git clone https://github.com/prometheus-operator/kube-prometheus.git cd kube-prometheus kubectl create -f manifests/setup kubectl create -f manifests/这套配置里默认包含node-exporter节点指标、kube-state-metricsK8s对象状态指标、blackbox-exporter探活以及Grafana展示面板。部署完以后最需要调整的是Prometheus的存储和采集周期。默认情况下Prometheus的存储是emptyDirPod一重启数据全没。生产环境必须接入持久化存储通过PersistentVolumeClaim声明比如apiVersion: v1 kind: PersistentVolumeClaim metadata: name: prometheus-pvc namespace: monitoring spec: accessModes: - ReadWriteOnce resources: requests: storage: 100Gi采集周期默认是30秒如果追求更细粒度的指标可以改成15秒但注意这会成倍增加存储和CPU开销100GB的盘很快就存满。监控数据的保留时间建议根据机器资源做权衡一般保留15天可以覆盖绝大多数回看需求。4.3 PodMonitor和ServiceMonitor动态发现目标的核心机制在K8s里部署Prometheus监控业务应用不再需要手写static_configs去填Pod IP而是用ServiceMonitor或PodMonitor声明“我要监控哪些服务”。Prometheus Operator会把这个声明翻译成采集配置自动发现满足标签选择器的目标。给业务Pod接入监控的最小例子应用暴露了/metrics端口并定义了Service标签为app: myapp。然后创建apiVersion: monitoring.coreos.com/v1 kind: ServiceMonitor metadata: name: myapp-monitor namespace: monitoring spec: selector: matchLabels: app: myapp endpoints: - port: metrics interval: 15s这个机制就是查询热词“k8s部署prometheus”时最核心的价值——不需要一台台加targets新服务上线只需在代码里暴露metrics接口再创建对应的ServiceMonitor采集配置自动更新。5. 常见问题与排查技巧从节点NotReady到证书过期一次讲全这部分是实操价值最高的部分。我按问题场景来整理全部来自实际踩坑记录。5.1 节点NotReady的排查顺序节点NotReady几乎人人都遇到过排查思路很重要不要瞎猜。第一步看节点状态和kubeletkubectl describe node node-name journalctl -u kubelet -f第二步检查容器运行时crictl是containerd的命令行工具crictl ps crictl info第三步检查网络插件Pod状态最常见是Calico的Pod没有Running或频繁重启大概率是Pod网段和Calico配置不一致或者主机名解析有问题。上生产前把hostname -i输出的IP和/etc/hosts里对应条目搞一致能省掉很多莫名其妙的坑。5.2 证书过期K8s最容易被忽略的定时炸弹Kubeadm部署的集群控制面组件的证书有效期默认一年。一年之后API Server的client证书过期集群就只能看不能用。我见过不止一个团队在生产环境突然发现kubectl报证书过期业务直接被卡死。预防方案是定期执行证书检查并可以先一次续到更长期限kubeadm certs check-expiration kubeadm certs renew all续期后需要重启相关组件控制面组件是静态Pod重启方式cd /etc/kubernetes/manifests mv kube-apiserver.yaml .. sleep 5 mv ../kube-apiserver.yaml .这个过程有点折腾更省心的做法是把证书续期写进crontab每个月自动执行一次再在季度巡检时人工确认。这里强调一个注意点kubeadm自动续期只对“证书还没过期”的情况有效过期之后再续集群成员关系可能已经断裂恢复起来更麻烦所以监控过期时间比学会了续期命令更重要。5.3 Pod创建后处于ContainerCreating的常见原因kubectl describe pod看Events最常见三种情况一是镜像拉取失败ImagePullBackOff原因要么是镜像地址不存在、tag写错要么是私有仓库没有配置ImagePullSecret。私有仓库镜像必须在Pod模板里指定imagePullSecrets在节点上手动docker login是没有用的。二是存储卷挂载失败比如挂载了不存在的PVC或者StorageClass没有默认值。PV与PVC的绑定关系要检查kubectl get pvc的状态是否为Bound。三是探针失败导致CrashLoopBackOff这一般不是基础设施问题是应用本身的存活探针或就绪探针路径配置不对比如探活端口和实际监听端口不一致。5.4 DNS解析异常CoreDNS的坑集群内Pod访问Service域名偶尔解析失败优先看CoreDNS Pod的状态和日志。一个常见坑是CoreDNS Pod被调度到了有污点的节点上或者节点资源不足导致CoreDNS被驱逐。建议给CoreDNS的Deployment加上资源requests并考虑让CoreDNS Pod分布在多个节点避免单点。6. 从控制器到自动化交付扩展K8s能力的正确姿势会部署、会监控、会排障K8s基本能稳定运行了。但很多团队在用到一定阶段后会发现默认能力不够比如需要根据业务请求数自动扩展、需要定时清理无效资源等。这时就轮到“自定义控制器”和“Operator”上场。6.1 控制器开发的最小路径K8s官方提供了client-go库用Informer机制监听资源变化。写一个最简单的自定义控制器监听ConfigMap变化并打印日志代码结构大致是定义InformerFactory、创建ConfigMap的Informer、注册AddFunc/UpdateFunc/DeleteFunc回调、启动Informer缓存同步最后进入等待循环。国内很多面试题会问控制器原理这里的关键是理解Informer的本地缓存和DeltaFIFO。控制器不会每次都对API Server发请求而是通过list-watch机制先全量拉取一次再增量监听变化变化事件放进DeltaFIFO由Informer消费并更新本地缓存。这样查资源的性能快对API Server的压力也小回调函数里拿到的都是本地缓存数据。6.2 用Operator和定时任务让运维更省心热词里提到的Operator案例生产中最常用的模式是“用Operator封装一个需要人工介入的操作”。以MySQL Operator为例传统方式下主从切换、备份恢复、存储扩容都要人工操作而Operator把人对MySQL实例的运维经验写成控制逻辑比如当主节点失联超过阈值时自动把从节点提升为主节点。这类项目已经是比较成熟的层面不建议从零造轮子GitHub上开源的Operator项目非常多直接复用即可。对大多数团队来说学Operator不一定要自己写一个完整的Operator理解概念后先写一个轻量的“巡检脚本定时任务”也能达到类似效果只需要用一个cronJob定期检查集群状态、清理Failed Pod、触发证书续期再配合Alertmanager的通知基本就能覆盖日常运维需求。先小步快跑再逐步把高频、易错的操作沉淀成控制器逻辑这条路在实践中走得更稳。7. 聊聊实战中的选择整个部署链路走下来我个人最深刻的体会是K8s本身不难难的是围绕它的生态底座——网络、存储、监控、证书、资源规划每一样都要提前想清楚。部署方案选型不要跟风一定要结合自己团队的运维能力和业务规模。团队只有一个人运维别一上来就搞五节点加复杂网络策略先两三个节点跑稳再逐步加节点加能力。团队有专职运维二进制部署或Kubekey结合离线包也是可选路径。最后再分享一个我在实际维护中发现的小技巧给所有核心组件的数据etcd备份、Prometheus数据做本地冗余的同时千万不要忽略“恢复演练”。你可以在测试环境模拟一次节点全挂然后尝试用备份把监控数据恢复出来做过一次你就知道真正恢复时会手忙脚乱在哪里。部署完K8s不是结束而是运维的开始。这套流程跑顺了后面加节点、扩GPU、接监控都是水到渠成的事。
返回列表