尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

K8s集群搭建与Web服务部署:kubeadm实战全流程指南

发布时间:2026/9/17 2:37:12

资讯中心
01
ARTICLE

K8s集群搭建与Web服务部署:kubeadm实战全流程指南

K8s集群搭建与Web服务部署:kubeadm实战全流程指南
直接开写。这篇博文围绕K8s 集群搭建与 Web 服务部署这个经典实操场景展开所有内容都限定在技术落地层面。1. 搭一套集群前先想清楚这三件事先说结论很多人搭 K8s 集群翻车根本不是执行命令出了问题而是动手之前没把目标想清楚。我之前带过几个刚接触容器编排的同事他们一上来就急着找 kubeadm 的初始化命令结果有的卡在镜像拉取有的把单节点集群当生产环境用后面加节点时折腾半天最后只能推翻重建。这次我把整个K8s 集群搭建 Web 服务部署的过程完整走了一遍从服务器规划、kubeadm 初始化、网络插件选型到把 Web 应用容器化、编写 Deployment 和 Service、再到配置探针和自动伸缩整套流程有踩坑也有验证整理出来给正准备上手的人一个可复现的路径。动手之前建议先确认三件事第一集群规模到底搞多大很多人觉得学习嘛一台机器就够了。我的建议是至少三台节点一个 master、两个 worker。理由很直接K8s 的调度器、控制器、etcd 这些核心组件设计上就是为多节点协作服务的单节点虽然能跑但你永远体会不到 Pod 在不同节点间调度、Node 宕机后工作负载被重新拉起这些核心场景。如果你是拿它做生产前的验证三节点是性价比最高的起点。第二发行版和版本怎么选目前社区用得最集中的就是 kubeadm它把 K8s 各组件的部署过程封装成了标准流程可控性强也方便你理解底层原理。至于版本一定要选当前稳定版本里最新的 patch 版本。别问为什么问就是 K8s 的 bug 修复非常频繁选太老的版本等于把已知的坑全踩一遍。第三网络方案选哪个这可能是除了容器运行时之外集群搭建中最容易出问题的环节。目前社区用得最多的是 Calico 和 Flannel。Flannel 简单vxlan 模式在大多数网络环境都能跑通适合入门Calico 功能更强支持 NetworkPolicy性能也好一些但配置复杂度稍高。如果只部署 Web 服务不做复杂的网络安全策略Flannel 完全够用如果想深入玩策略直接上 Calico。下面我写的这套流程用的是 Calico因为它的 BGP 模式在跨节点通信上更稳定。硬件配置这块参考我的实测数据节点角色CPU内存磁盘操作系统master2 核4G40GUbuntu 22.04 LTSworker12 核4G40GUbuntu 22.04 LTSworker22 核4G40GUbuntu 22.04 LTS这套配置跑一个 Web 应用加数据库再用 JMeter 做压测整体是很稳的。如果你的应用本身很吃资源建议把 worker 的内存提到 8GK8s 对内存的需求比 CPU 敏感得多。2. kubeadm 初始化阶段最容易翻车的几个细节2.1 基础环境配置swap、转发和 cgroup 驱动我见过太多人忽略基础环境配置直接跳去执行 kubeadm init然后在各种诡异报错里折腾半天。这部分的两个关键点一定要先处理干净。第一个是关闭 swap。Kubernetes 从设计上就假设节点上没有 swap因为一旦允许使用 swapPod 的内存配额和节点压力驱逐就失去意义了。执行命令# 立即关闭 sudo swapoff -a # 永久关闭注释掉 /etc/fstab 中 swap 相关的那行 sudo sed -i /swap/s/^/#/ /etc/fstab第二个是加载内核模块并配置网络转发。K8s 的 Pod 网络通信依赖 iptables 转发而 kube-proxy 的很多模式也需要相应内核模块支持。需要让 br_netfilter 模块生效同时把 IPv4 转发参数打开cat EOF | sudo tee /etc/modules-load.d/k8s.conf overlay br_netfilter EOF sudo modprobe overlay sudo modprobe br_netfilter 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这里有个小坑有些云厂商的安全组或者本机防火墙会拦截 netfilter 相关的流量如果你发现集群搭建后 Pod 之间网络不通优先查这两个模块是不是都正常加载了。2.2 容器运行时containerd 的配置坑K8s 从 1.24 版本开始彻底移除了 dockershim所有节点都必须用实现了 CRI 规范的运行时比如 containerd、CRI-O。containerd 是目前最主流的选型它能直接运行 Docker 构建出来的镜像兼容性也最好。安装 containerd 很简单但配置里有个关键点经常让人掉坑默认的 config.toml 里没有启用 SystemdCgroup。K8s 官方推荐的容器运行时配置要求 cgroup 驱动设置为 systemd这是因为 kubelet 本身用 systemd 管理进程如果容器运行时用 cgroupfs双驱动会导致资源统计和 Pod 驱逐逻辑出错。正确的配置方式# 生成默认配置 sudo mkdir -p /etc/containerd containerd config default | sudo tee /etc/containerd/config.toml然后编辑 config.toml找到 SystemdCgroup 参数把它改成 true[plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc] ... [plugins.io.containerd.grpc.v1.cri.containerd.runtimes.runc.options] SystemdCgroup true如果你在国内网络环境推荐在这个配置文件里把 sandbox_image 参数同步替换一下这个后面会专门说。改完配置后重启 containerdsudo systemctl restart containerd还有一个细节不要同时安装 Docker 和 containerd。因为 Docker 也内置了 containerd如果两边版本不一致会导致 kubelet 连不上正确的 CRI socket报错信息还不明显排查起来非常浪费时间。要么只装 containerd要么只装 DockerDocker 安装后自带 containerd直接用它的 CRI socket 即可。2.3 kubeadm init 的完整参数与镜像拉取问题这一步是整个集群搭建的核心。在 master 节点上执行初始化命令我给的参数是实测可用的sudo kubeadm init \ --kubernetes-versionv1.29.2 \ --pod-network-cidr10.244.0.0/16 \ --apiserver-advertise-address192.168.1.10三个参数分别说明一下--kubernetes-version指定版本确保和 kubeadm 的版本一致。--pod-network-cidrPod 网段必须和你后续选择的网络插件相匹配。Flannel 默认用 10.244.0.0/16Calico 默认也可以用这个网段但如果你自定义了网段Calico 配置也要同步改。这里踩过的坑是有人随手填了个 192.168.0.0/16结果和宿主机的局域网冲突Pod 之间通信全乱套。--apiserver-advertise-addressmaster 节点的内网 IP记得别绑错网卡。初始化过程中kubelet 需要从 gcr.io 拉取一堆核心镜像kube-apiserver、kube-controller-manager、kube-scheduler、etcd、pause。如果网络无法直接访问这些镜像源这一步会卡住很久然后报错。解决思路是先把镜像改个标签从能访问的镜像仓库拉下来再 retag 成 kubeadm 期望的镜像名。用一条脚本就能搞定kubeadm config images list --kubernetes-versionv1.29.2先看看到底需要哪些镜像然后从国内源拉取。以阿里云为例for IMAGE in kube-apiserver:v1.29.2 kube-controller-manager:v1.29.2 kube-scheduler:v1.29.2 kube-proxy:v1.29.2 pause:3.9 etcd:3.5.12-0 coredns:v1.11.1 do sudo ctr -n k8s.io images pull registry.cn-hangzhou.aliyuncs.com/google_containers/$IMAGE sudo ctr -n k8s.io images tag registry.cn-hangzhou.aliyuncs.com/google_containers/$IMAGE registry.k8s.io/$IMAGE done如果你的 containerd 版本比较新也可以用ctr -n k8s.io这个命名空间这个是 containerd 为 CRI 预留的目录kubelet 默认从这个命名空间拉镜像。初始化成功后会有一段提示文字按它给的命令配置 kubeconfigmkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config然后保存 worker 节点加入集群的 token 命令一般长这样kubeadm join 192.168.1.10:6443 --token xxx --discovery-token-ca-cert-hash sha256:xxx注意 token 有效期默认是 24 小时如果过期了在 master 上执行kubeadm token create --print-join-command重新生成一条。2.4 安装 Calico 网络插件的顺序问题很多人在 kubeadm init 之后马上执行kubectl get nodes发现节点还是 NotReady就慌了。其实这是正常的集群节点从 NotReady 变成 Ready依赖网络插件正常工作。Calico 的安装方式很直接curl -O https://raw.githubusercontent.com/projectcalico/calico/v3.27.0/manifests/calico.yaml kubectl apply -f calico.yaml等待几十秒后检查 Pod 状态kubectl get pods -n kube-system重点看 calico-node 是否成功运行。如果卡在 ImagePullBackOff大概率是国内网络拉不了 docker.io 的镜像需要把 calico.yaml 里的 image 地址改成国内镜像源。还有个容易忽视的点Calico 默认把 Pod 网段定为 192.168.0.0/16但上面 kubeadm init 我用了 10.244.0.0/16所以需要修改 calico.yaml 里 CALICO_IPV4POOL_CIDR 的配置让它和 pod-network-cidr 保持一致。这是 flannel 和 calico 最容易因为网段不一致导致跨节点通信失败的经典问题。3. 从一个 Web 镜像到集群内可访问的服务3.1 为什么用 Deployment 而不是直接跑一个 Pod网络插件就绪后集群基础环境就通了。下一步是把 Web 服务部署上去。这里先明确一个设计原则生产环境下绝不直接创建 Pod而是通过 Deployment 这类工作负载资源管理 Pod。原因很简单Deployment 帮你管好了 Pod 的整个生命周期。比如 Deployment 会保证指定数量的副本始终运行某个节点挂了它会自动在其他节点重建 Pod升级镜像版本时它可以滚动更新而不中断服务。这些能力对 Web 服务来说几乎是必须的。这里用 Nginx 作为 Web 服务示例因为它足够轻量、无论你是前端项目还是反向代理需求它都是最常出现的身影。先写一个 Deployment 的 YAMLapiVersion: apps/v1 kind: Deployment metadata: name: web-nginx labels: app: web spec: replicas: 3 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 readinessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 3 periodSeconds: 5 livenessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 10 periodSeconds: 10创建命令kubectl apply -f web-nginx.yaml这里有三个关键点值得展开副本数设置replicas 设为 3意味着 3 个 Pod 会被调度到至少 2 个 Worker 节点上如果节点资源充足通常分布在 3 个节点。这样即使某个节点宕机另外两个节点的 Pod 仍然在服务再配合 Service 做负载均衡Web 服务的可用性就立起来了。readinessProbe 和 livenessProbe 的区别通俗地解释readinessProbe 决定这个 Pod 是否应该接收流量。如果检查失败kubelet 会把 Pod 从 Service 的 Endpoints 里摘掉请求不会转发给它但容器不会重启。livenessProbe 则决定要不要重启容器。如果接口彻底卡死没有任何响应livenessProbe 检查失败就会强制重启容器让服务恢复。这两个探针的配合是 K8s 自愈能力的关键建议写 Web 服务 YAML 时都配好。为什么探针路径要设计成 /healthz这是工程上的习惯。让应用提供独立的健康检查接口和业务接口分开避免健康检查被业务逻辑污染。比如你有一个接口偶发耗时较长如果探针走的是它就可能误判 Pod 不健康。3.2 Service 实现负载均衡和服务发现Deployment 管理好了 Pod但 Pod 的 IP 地址是动态变化的容器重启后 IP 就换了。这时候需要一个稳定的访问入口就是 Service。Service 通过 selector 关联到一组 Pod然后自动把流量负载均衡到这些 Pod 的 IP 上。对应的 YAMLapiVersion: v1 kind: Service metadata: name: web-nginx-service spec: selector: app: web ports: - protocol: TCP port: 80 targetPort: 80 type: ClusterIPService 的 type 有几种根据访问场景选择Service 类型适用场景访问方式ClusterIP集群内部访问通过 Service 名或 ClusterIPNodePort外部访问适合测试环境通过节点IP:NodePortLoadBalancer公有云环境通过云负载均衡器IngressHTTP/HTTPS 域名访问生产常用通过 Ingress Controller如果你只是想快速验证服务是否工作可以先改成 NodePortkubectl edit service web-nginx-service # 把 type 改成 NodePort然后查看分配到的高端口kubectl get svc web-nginx-service浏览器访问任意节点IP:30080就能看到 Nginx 的欢迎页。但说实话NodePort 只适合临时验证。生产环境对外暴露 HTTP 服务几乎都是走 Ingress。不仅是功能更丰富域名路由、TLS 终止、限流而且不用给每个服务单独分配一个端口维护成本低很多。下面第三节里我用 Ingress 做了一个完整的对外访问链路说明。4. Ingress 对外访问链路从域名到 Pod 的完整路径4.1 为什么集群内通信不代表对外可用Service 的 ClusterIP 只能在集群内部访问外部用户没法直接用这个 IP。NodePort 虽然开了一个端口给外部但它有几个天然缺陷NodePort 范围有限默认 30000-32767、端口和服务的绑定关系靠人工记忆、无法实现基于域名的路由。这时候 Ingress 的价值就出来了。它工作在七层可以根据域名和路径把请求路由到不同的 Service。比如同一个 IP 下api.example.com路由到后端 API 服务www.example.com路由到前端页面服务。注意一个重要概念Ingress 本身不处理流量它只是一堆路由规则。真正干活的是 Ingress Controller常见的有 Nginx Ingress Controller、Traefik、HAProxy Ingress。Controller 会读取 Ingress 规则然后动态更新 Nginx 的配置实现流量转发。4.2 Nginx Ingress Controller 的部署与配置安装 Nginx Ingress Controller官方提供 Helm 包这里直接用 manifests 方式方便理解组件构成kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.10.1/deploy/static/provider/cloud/deploy.yaml部署完成后查看 Podkubectl get pods -n ingress-nginx然后写一个 Ingress 资源apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: web-ingress spec: ingressClassName: nginx rules: - host: web.example.com http: paths: - path: / pathType: Prefix backend: service: name: web-nginx-service port: number: 80创建后查看 Ingress 是否生效kubectl get ingress注意web.example.com是个示例域名实际部署时改成你自己的域名同时在 DNS 解析里把它指向 Nginx Ingress Controller 所在节点的 IP如果 controller 部署为 LoadBalancer则指向负载均衡器 IP。配置完成后本地想快速验证可以临时改下 hosts 文件echo 192.168.1.20 web.example.com /etc/hosts然后访问http://web.example.com流量路径是浏览器 - DNS解析 - 节点IP - Nginx Ingress Controller - Service - Pod这个链路中最容易出问题的是 Ingress Controller 的 Pod 访问方式。如果 controller 部署方式是 NodePort你需要访问 NodePort 对应端口如果 LoadBalancer则需要一个外部负载均衡器分配 IP。云环境一般用 LoadBalancer自建集群为了方便可以改成 NodePort 或者直接 hostNetwork。4.3 实测流量转发和常见故障排查我用 curl 实测了一下curl -I http://web.example.com返回 200 说明链路通。如果返回 404先查 Ingress Controller 的日志kubectl logs -n ingress-nginx -l app.kubernetes.io/nameingress-nginx常见的排查思路是先确认 Pod 正常再确认 Service 的 Endpoints 里有 Pod 的 IP最后确认 Ingress 规则的 host 和 path 匹配。从底层往上层查效率最高。另外一个容易踩的坑是Ingress Controller 部署在 master 节点且有 Taint如果集群只有 master 节点能部署 controller需要给节点去掉 Taint 或者给 controller 单独加 Toleration。前几年有过版本需要额外配置新版 Ingress Controller 默认加了容忍度至少不会卡在这一步。5. 压测之前必须做好的稳定性验证5.1 探针和资源配置让 Pod 更抗造集群部署好了Web 服务也通了但先别看压测工具开跑有几件基础工作一定要先做否则压测一上去Pod 就会被 OOMKilled 或者频繁重启你根本分不清是应用问题还是平台问题。第一给容器设置资源请求和限制。这是 K8s 使用中最重要的配置之一没有资源限制的容器可能把节点内存吃满影响同节点其他 Pod。给 Nginx 容器加配置resources: requests: memory: 64Mi cpu: 100m limits: memory: 128Mi cpu: 200m这里要理解 requests 和 limits 的区别。requests 是调度时的依据告诉调度器这个 Pod 至少需要这么多资源limits 是运行时上限超过这个上限CPU 会被限流内存如果超了直接 OOMKilled。在写 Nginx 部署时如果你没有配置 resourcesPod 的内存占用会一直增长节点出现压力时最先被驱逐的就是这种无限资源的 Pod。第二加上优雅终止时间。默认情况下kubelet 在 Pod 需要终止时会先发送 SIGTERM 信号等 30 秒terminationGracePeriodSeconds如果没退出再发 SIGKILL。对于 Web 服务30 秒通常够完成存量请求的处理。如果你的应用本身有优雅停机逻辑比如主动关闭数据库连接池可以适当缩短这个时间但一般保持默认即可。Nginx 的优雅停机其实天然支持当 kubelet 发来 SIGTERM它会停止接收新连接等现有的连接处理完再退出。所以 Nginx 容器基本不会出现请求中断的问题。第三检查 Pod 是否跨节点分布。用命令看下kubectl get pods -o wide如果发现 3 个 Pod 都挤在同一个节点说明调度策略需要调整。比如加上podAntiAffinity让相同应用的 Pod 尽量分散到不同节点避免单点故障affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: web topologyKey: kubernetes.io/hostname5.2 手动故障演练node 宕机和 Pod 被杀稳定性验证不只是看看配置手动故障演练是我的习惯。在压测之前做两个实验实验一杀掉一个 Web Pod看 Deployment 是否自动恢复。kubectl delete pod -l appweb kubectl get pods -w可以看到旧的 Pod Terminating新的 Pod Pending - ContainerCreating - Running整个过程大概几秒到几十秒。期间因为有 3 个副本剩余 2 个仍然在服务外部访问不受影响。实验二直接停掉一个 worker 节点。在节点上执行sudo shutdown -h now等一两分钟后在 master 上执行kubectl get nodes kubectl get pods -o wideK8s 有一个判定节点失联的时间窗口默认 40 秒左右才把节点标记为 NotReady然后再过一段时间会将其上的 Pod 标记为终止并重新调度到可用节点。这个时间平台上是自动处理的但在自建集群如果你希望更快地触发故障转移可以调低 Node controller 的判定参数比如--node-monitor-grace-period从默认 40 秒改成 20 秒不过这属于集群级参数修改不建议一开始就动。我实测过杀掉一个 worker 节点后部署在它上面的 Pod 大概在 1-2 分钟内就被重新调度到存活节点上运行。这期间服务因为有其他副本在支撑可用性没有中断。对 Web 服务来说这个自愈能力就是 K8s 能承载高并发压测的底气。5.3 水平自动伸缩高并发下的扩容机制压测必然会带来流量高峰光靠固定副本数抗压要么浪费资源要么扛不住。这时候就轮到 HPAHorizontalPodAutoscaler登场。HPA 根据资源指标动态调整副本数。比如配置一个基于 CPU 利用率的伸缩策略kubectl autoscale deployment web-nginx --cpu-percent50 --min3 --max10这条命令的意思是当 Pod 的平均 CPU 使用率超过 50% 时自动增加副本数最多增加到 10 个低于 50% 时逐渐缩回最少保持 3 个。但要注意HPA 要工作必须要有 metrics-server 提供指标数据。如果没装执行kubectl get hpa会看到failed to get cpu utilization之类的错误。安装 metrics-serverkubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml有几个老版本会在 TLS 证书校验上出问题新版本基本都处理好了。安装完成后等一两分钟执行kubectl top nodes kubectl top pods能看到 CPU 和内存数据说明 metrics-server 正常了。实测 HPA 的效果用 JMeter 发一波压力请求CPU 飙升超过 50% 后执行kubectl get pods -w能看到 Pod 数量逐渐增加从 3 个升到 6 个、8 个最终稳定在 10 个以内。压力下去后Pod 数量又会慢慢缩回 3 个。有两点值得注意HPA 的伸缩有冷却时间防止指标抖动导致副本数频繁变化所以不会立刻缩容一般默认 5 分钟左右。压测时不要看到 CPU 降了就以为 HPA 失效。如果压测脚本本身的并发量足够大HPA 扩容速度可能赶不上流量增长这时需要结合应用的实际承载能力做预热或者设置最小副本数不能完全依赖自动伸缩。6. 这趟实操下来我对高可用部署的三点体会集群跑了一轮Web 服务也承载了压测流量这套部署的稳定性得到了验证。最后记录一下这段时间实操下来最深的三个体会。第一K8s 的复杂度是分散的但最有价值的恰恰是这些分散的细节。从系统参数、容器运行时配置、网络插件选型、镜像加速到探针、资源限制、HPA每个环节单独看都不难但串起来就是一个完整的可靠性链条。很多人觉得 K8s 难难的不是某个技术点而是这些点之间的关联。所以我一直建议就算有云厂商的一键集群也要自己完整用 kubeadm 搭一次把底层机制跑通。第二Web 服务部署应该先画架构图再写 YAML。我当时在部署时先明确了访问链路用户 - Ingress - Service - Pod - 容器然后才动手写文件。这样每个资源的定位就很清晰不会一个 YAML 里堆砌大量字段出问题时定位也好找。对于服务化的应用这个思路尤其重要。第三压测之前一定要先做故障演练。我在部署后模拟过节点宕机、Pod 被杀、网络波动等情况看着 K8s 自动恢复心里才有底。如果没有这个步骤直接在压测时遇到 Pod 异常很难判断是平台不稳定还是应用扛不住压力。实际压测下来这套集群在高并发下表现稳定Web 服务通过 HPA 自动扩容扛住了流量冲击整个链路没有出现连接中断或大量超时。如果你正准备做类似的集群部署可以按这套流程走一遍。中间遇到任何问题优先查 kubelet 和 containerd 的日志大部分坑都能在日志里找到答案。祝你一次搭通。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。