ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Kubernetes Deployment 实战:从控制器机制到滚动更新与故障排查

Kubernetes Deployment 实战:从控制器机制到滚动更新与故障排查 从第一次在测试环境里kubectl apply -f deployment.yaml到后来在生产集群上处理滚动更新和故障恢复我在K8S部署这块踩过的坑应该不比你少。很多朋友拿着一个 deployment 的 YAML 就能把服务跑起来但一旦问起为什么这么写更新时发生了什么Pod 起不来怎么排查就开始含糊。这篇文章就围绕 K8S 里最核心的 workload——Deployment——把部署、更新、暴露、排错这些环节完整捋一遍适合刚接触 K8S 的运维和开发也适合想系统补一遍 K8S 部署细节的读者。先说结论Deployment 不是一个 YAML 模板那么简单它背后是一整套期望状态、控制器、滚动发布、自愈的调度机制。把机制理解了你写出来的配置才是真正可控的。1. 为什么我坚持所有无状态服务都走 Deployment 而不是裸 Pod1.1 一次凌晨被 Pod 干掉的事故刚接触 K8S 的时候我图省事直接把一个后端服务用裸 Pod 方式跑起来了kubectl run一条命令Pod 起来了接口也通了一切都挺好。直到某天凌晨节点内存告急kubelet 把那个 Pod 给驱逐了。第二天早上服务全挂我登录节点一看Pod 状态是Terminating然后消失得干干净净没有任何东西把它拉起来。那个服务没有依赖外部存储重启就能恢复可它偏偏不会自己重启。这就是不用 Deployment 的代价。裸 Pod 不受控制器管理Pod 被删除、节点故障、资源驱逐系统不会自动重建。这个教训让我把团队里的规矩改成了除调试场景外一律不许直接创建裸 Pod所有无状态服务必须走 Deployment。1.2 Deployment 到底帮我们守住了什么Deployment 的本质是一个声明式控制器。你告诉它我希望有 3 个副本镜像版本是 v1.2.0剩下的由控制器不断尝试去逼近这个期望状态。具体来说它提供了四层保障自愈能力Pod 挂掉、节点宕机、Pod 被驱逐ReplicaSet 会自动补副本。滚动更新新版本发布时按照你定义的策略逐步替换旧 Pod而不是一次性全量重启。回滚能力新版本出了问题kubectl rollout undo一键回到上一个可用版本。扩缩容能力手动kubectl scale或者配合 HPA 自动伸缩。这里有个承上启下的角色是 ReplicaSet。Deployment 本身不直接管 Pod它管理的是 ReplicaSetReplicaSet 再管理 Pod。每次更新镜像或模板Deployment 会创建新的 ReplicaSet旧的 ReplicaSet 保留历史版本这也是回滚机制的实现基础。工作负载适用场景是否自愈是否支持滚动更新典型例子Pod调试、临时任务否否kubectl run调试ReplicaSet仅需要副本数管理是否较少直接使用Deployment无状态应用是是Web 服务、API 服务StatefulSet有状态应用是是有序数据库、消息队列DaemonSet每个节点必须跑一个是是按节点日志采集、监控 agentJob / CronJob一次性任务 / 定时任务部分否数据迁移、批处理1.3 什么时候不该用 DeploymentDeployment 不是万能药。有状态服务比如 MySQL、Redis、ZooKeeper节点之间需要稳定的网络标识和独立的存储这种场景用 StatefulSet 更合适它会给每个 Pod 分配稳定的序号和 hostnamepod-0、pod-1PVC 也会按序绑定。如果你需要每个节点上恰好跑一个的组件比如日志采集器、节点监控器那应该用 DaemonSet。Deployment 的调度是全局副本数它不考虑每节点一个的拓扑约束。再就是定时任务和一次性批处理别用 Deployment 硬撑CronJob和Job才是正路。Deployment 里的 Pod 是要长期运行的任务跑完就退出Deployment 会认为这是异常反复帮你重启最后变成 CrashLoopBackOff得不偿失。提示选工作负载之前先问三个问题——服务有没有状态是需要全局副本还是每节点副本是常驻还是跑完就退出答案组合基本就能锁定正确类型。2. deployment.yaml 字段逐项拆解照着写不踩坑2.1 基础骨架与版本选择先给一个最小可用的 deployment 骨架apiVersion: apps/v1 kind: Deployment metadata: name: myapp namespace: production spec: replicas: 3 selector: matchLabels: app: myapp template: metadata: labels: app: myapp spec: containers: - name: myapp image: harbor.example.com/myapp:v1.2.0 ports: - containerPort: 8080这里第一处要注意的是apiVersion。早年间大家都在用extensions/v1beta1到了 K8S 1.16 之后这个版本就废弃了现在写 Deployment 一律用apps/v1。如果你在网上抄到apis/extensions/v1beta1的旧配置apply 的时候会直接报错API 不认识这个版本。namespace我建议每个业务一个独立名字空间。默认丢在default里时间长了全是分不清归属的资源排查问题都费劲。2.2 最容易错的 selector 与 template.labels 匹配关系这是初学者问得最多的问题为什么 Deployment 里有两组 labelsspec.selector.matchLabels是用来选择Pod 的它决定 ReplicaSet 要管理哪些 Pod。spec.template.metadata.labels是创建Pod 时打上的标签。两者的键值必须匹配否则 API Server 会拒绝创建The Deployment myapp is invalid: spec.template.metadata.labels: Invalid value: map[string]string{app:myapp-front}: selector does not match template labels我的建议是selector 里只放一个或最多两个稳定不变的标签比如app别把版本号、分支名这种每次发布都在变的标签放进 selector。因为selector在 Deployment 创建后是不可变的想改只能删了重建。版本相关的信息放在template.metadata.labels里没问题它变更时只会触发 ReplicaSet 滚动不会破坏选择逻辑。2.3 resources 的 requests 与 limits 到底怎么写很多人发布时图省事不写 resources这在生产环境是非常危险的习惯。不写 requests调度器不知道你的 Pod 需要多少资源它可能把多个大胃口应用挤到同一节点然后集体 OOM不写 limits某个应用内存泄漏时会把节点内存吃满直接拖垮同节点的其他 Pod。resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 512Mi这里的100m是 0.1 个 CPU 核心128Mi是 128 兆字节MiB。requests 是调度依据保证 Pod 至少能拿到这么多资源limits 是运行时限制超过之后 CPU 会被限流内存超过会被杀掉OOMKilled。经验值是requests 按平时的真实用量留 20% 余量limits 按峰值用量再留 20% 余量。比如服务日常 80m CPU、200Mi 内存requests 写 100m/256Milimits 写 500m/512Mi。不要把 requests 和 limits 写成一模一样那样调度难度会急剧上升集群里会多出一堆调度不掉的 Pod。2.4 探针readiness 和 liveness 的姿势Pod 起来了不等于服务可用。我们遇到过 JVM 进程起来了但是 Spring 容器还没初始化完流量一进来就 5xx。解决这个问题的关键是 readinessProbe。readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 10 livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 15 periodSeconds: 20readinessProbe探针失败Endpoints 里摘除该 Pod流量不打进来但 Pod 不重启。适合处理启动慢、暂时不能服务的情况。livenessProbe探针失败kubelet 会杀掉容器并重启。适合处理死锁、卡死的情况。startupProbe慢启动应用专用在 startup 探针成功之前不执行 liveness 探针避免启动阶段被误杀。参数含义默认值建议initialDelaySeconds容器启动后多久开始探测0按应用启动时间评估periodSeconds探测间隔10不要太频繁10-30 合理timeoutSeconds探测超时11-5 合理太短易误报failureThreshold连续失败多少次才判定失败3建议保持 3-5successThreshold连续成功多少次才判定成功1一般不用改探针的路径千万别用那种不带业务的健康检查页。我们有个服务把/healthz写成了只检查进程存活结果 JVM 老年代打满、接口大面积超时readiness 还一直返回 200等于探针白配。健康检查里至少应该包含数据库连接池、核心依赖的连通性但也不要太重免得每次探测都要查几十张表。2.5 更新策略与优雅升级Deployment 默认的更新策略是RollingUpdate但默认参数不一定适合你的业务。关键参数有两个strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0maxUnavailable更新过程中允许最多多少个 Pod 不可用。maxSurge更新过程中允许最多超出期望副本数多少个 Pod。举个例子replicas3maxSurge1maxUnavailable0。更新时系统会先创建一个新 Pod等它 ready 之后再停掉一个旧 Pod整个过程始终保证有 3 个 Pod 在服务最多同时有 4 个 Pod 存在。这种先增后减的策略适合对可用性要求高的线上服务。如果反过来maxSurge0maxUnavailable1系统会先停一个旧 Pod再建一个新 Pod。这种先减后增对资源紧张的集群更友好但更新期间会有短暂的容量下降。还有个容易被忽略的配置是terminationGracePeriodSeconds它决定 Pod 被删除时给容器多长的优雅退出时间。如果你的应用需要先把队列里的任务消费完、或者需要反注册到注册中心默认的 30 秒可能不够需要适当调大。我们有个批处理服务收到 SIGTERM 后要等当前批次落地这个值设到了 120。设得太小Pod 会被强杀可能产生脏数据。最后给一个相对完整的生产参考 YAMLapiVersion: apps/v1 kind: Deployment metadata: name: order-service namespace: production spec: replicas: 3 selector: matchLabels: app: order-service strategy: type: RollingUpdate rollingUpdate: maxSurge: 1 maxUnavailable: 0 template: metadata: labels: app: order-service spec: terminationGracePeriodSeconds: 60 containers: - name: order-service image: harbor.example.com/order-service:v2.3.1 imagePullPolicy: IfNotPresent ports: - containerPort: 8080 env: - name: JAVA_OPTS value: -Xms256m -Xmx512m resources: requests: cpu: 200m memory: 256Mi limits: cpu: 1000m memory: 768Mi readinessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 10 periodSeconds: 10 timeoutSeconds: 3 failureThreshold: 3 livenessProbe: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 30 periodSeconds: 20 timeoutSeconds: 3 failureThreshold: 33. 把 YAML 跑起来apply、滚动更新与回滚的实战细节3.1 apply 与 create 的区别以及为什么推荐 applykubectl create -f deployment.yaml和kubectl apply -f deployment.yaml都能创建资源但语义完全不同。create是命令式操作资源已存在时会直接报错而且 create 创建出来的资源不会记住 YAML 的内容。你之后改了 YAML 再执行kubectl apply会发现它没法按新的期望状态去协调因为资源的注解里没有上一次 apply 的配置记录。apply是声明式操作K8S 会在资源的kubectl.kubernetes.io/last-applied-configuration注解里记录上次提交的 YAML。之后每次 applyAPI Server 会做三方合并上次 apply 的配置、当前线上的配置、这次提交的配置算出差异后增量修改。所以团队规范里我强制所有人用 apply配合 Git 仓库管理 YAML谁改了哪一行git diff看得清清楚楚审计也方便。3.2 滚动更新过程中到底发生了什么假设我用新的镜像 tag 更新 deploymentkubectl set image deployment/order-service order-serviceharbor.example.com/order-service:v2.4.0或者更推荐的方式直接改 YAML 然后 apply。无论哪种Deployment 都会创建一个新的 ReplicaSet并按照 strategy 里的参数逐步扩新缩旧。观察更新进度用这几个命令kubectl rollout status deployment/order-service -n production kubectl get deployments -n production kubectl get rs -n production kubectl get pods -n production -wrollout status会一直阻塞到更新完成适合在发布脚本里做同步等待。get rs能看到新旧两个 ReplicaSet 的副本数变化新 RS 的副本从 0 涨到 3旧 RS 从 3 缩到 0整个过程一目了然。这里有个坑如果新 Pod 的 readiness 探针一直不通过滚动更新会被卡住。表现为新 RS 的副本数停在 1旧 RS 还剩 2kubectl rollout status一直输出Waiting for deployment order-service rollout to finish: 1 out of 3 new replicas have been updated...。这是 Deployment 的保护机制——新 Pod 不 ready 就不继续替换避免全量故障。这时候应该先查新 Pod 的日志和事件把原因解决掉而不是急着继续发布。3.3 回滚时最常见的镜像 tag 问题发布完发现新版本有问题回滚命令很简单kubectl rollout undo deployment/order-service -n production kubectl rollout history deployment/order-service -n productionrollout history能看到历次 revision。比如当前是 revision 6要回滚到 revision 4可以指定kubectl rollout undo deployment/order-service --to-revision4 -n production但这里有个团队里反复出现的坑镜像 tag 用的是 latest或者每次发布都覆盖同一个 tag。这种情况下回滚是无效的因为新旧 ReplicaSet 引用的镜像 tag 字符串是一样的undo之后 Pod 拉到的还是同一个镜像。正确的做法是每次发布使用不可变 tag比如v2.3.1、v2.4.0或者用 git commit sha 作为 tag。这样每个 ReplicaSet 都指向一个不可变的镜像回滚才能真正回到那个版本的代码。用 latest 一时爽回滚火葬场这话一点都不夸张。3.4 扩缩容这件事远没你想的那么简单手动扩缩容kubectl scale deployment/order-service --replicas5 -n production但生产环境更推荐用 HPA 做自动扩缩apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: order-service-hpa namespace: production spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: order-service minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60HPA 会周期性获取 Pod 的 CPU 用量超过 60% 就扩容低于时缩容。但 HPA 的判定依赖 metrics-server 采集的数据集群如果没有安装 metrics-serverHPA 会一直处于unknown状态不会做任何伸缩。还有一个容易踩的坑HPA 和 Deployment 的replicas字段不要同时人工修改。你手动 scale 之后HPA 下次评估可能会把你的副本数改回去两者会打架。要么让 HPA 管要么人管别混着来。4. 部署之后怎么把流量引进来Service、externalIPs 与 Ingress4.1 Service 类型到底该怎么选Deployment 创建出来的 Pod IP 是临时的Pod 重建后 IP 就变了所以必须用 Service 做稳定的访问入口。Service 类型访问方式适用场景说明ClusterIP集群内虚拟 IP服务间调用默认类型外部不可达NodePort节点IP:NodePort测试、小规模暴露端口范围 30000-32767LoadBalancer云厂商负载均衡器云上生产环境需要云厂商插件支持ExternalNameDNS CNAME集群内访问外部服务不产生 selector 和 Endpoints集群内部服务间访问用 ClusterIP 就够了配合 DNS 规则order-service.production.svc.cluster.local不需要记 IP。需要对外暴露时小规模场景可以直接 NodePort生产环境在云上就上 LoadBalancer自建机房一般走 Ingress 或者 externalIPs。4.2 externalIPs很多人忽略的稳定入口方案externalIPs 是 Service 配置里的一个字段它可以把 Service 额外绑定到指定的外部 IP 上。这个字段在网络环境相对固定的场景下非常实用比如自建机房里有固定的内网网关 IP或者有一批预留的浮动 IP。apiVersion: v1 kind: Service metadata: name: order-service namespace: production spec: type: ClusterIP clusterIP: 10.96.0.100 externalIPs: - 192.168.10.50 selector: app: order-service ports: - port: 8080 targetPort: 8080配置之后通过192.168.10.50:8080就能访问到后端 Pod流量到达该 IP 所在节点后由 kube-proxy 转发到对应 Service 的 Endpoints。它不需要额外创建负载均衡器也不占用 NodePort 端口范围在裸金属集群和部分内网环境里是成本最低的暴露方案。需要注意 externalIPs 的流量只会进到集群内某个节点再由 kube-proxy 转发。如果这个节点挂了流量就断了除非前面有负载均衡做节点探活。所以它适合前面已有 LVS/HAProxy 引流的架构把 externalIP 当作后端 Real Server 来用而不是裸奔直接对公网。4.3 Ingress 与域名路由如果服务不止一个用 NodePort 或 externalIPs 会非常难受——每个服务一个端口域名和端口割裂。这时候引入 Ingress。apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: order-ingress namespace: production spec: rules: - host: order.example.com http: paths: - path: / pathType: Prefix backend: service: name: order-service port: number: 8080Ingress 需要一个控制器实例比如 Nginx Ingress Controller 或 Traefik否则 Ingress 规则不会被真正执行。很多初学者创建完 Ingress 发现访问不了第一反应是查 Ingress查完没问题再往下查才发现集群里压根没有 Ingress Controller。4.4 流量链路里经常被忽略的几个问题Service 的核心是 selector 到 Pod 标签的选择。最常见的问题是Service 的selector写错一个字母导致 Endpoints 为空curl 报 connection refused但 Pod 明明活着。排查链路固定为kubectl get svc order-service -n production kubectl get endpoints order-service -n production kubectl describe svc order-service -n production如果 Endpoints 为空检查 Service 的 selector 和 Pod 的 labels 是否匹配。还有一个细节是targetPort要与容器的containerPort一致是容器内部端口不是 Service 的 port。再就是 Service 的sessionAffinity字段。默认情况下同一个客户端的多次请求会被负载均衡到不同后端 Pod。如果你的应用没有做分布式会话需要把会话保持打开设置sessionAffinity: ClientIP。这个字段在 Service 上是可以在创建后修改的不用重建。5. 部署过程中最折磨人的几个故障完整排查链路5.1 master 初始化报错The API server is not healthy after 4m0.00747357s很多人在 kubeadm 初始化集群的时候卡在这个报错上。这个信息的意思是kubeadm 等 API Server 就绪等了 4 分钟没等到。API Server 是集群的大脑它没起来后面所有操作都是空谈。排查链路我建议这样走# 看 API Server 容器状态如果用的是 containerd/docker crictl ps -a | grep apiserver crictl logs apiserver-container-id常见的根因就这几种容器运行时的问题kubeadm 1.24 以后默认用 containerd如果机器上同时装了 docker两个运行时在配置上互相干扰。检查 kubelet 的--container-runtime-endpoint和 containerd 配置是否一致。cgroup 驱动不一致kubelet 的 cgroup driver 和容器运行时的 cgroup driver 不一致最常见的组合是 kubelet 用systemdcontainerd 用cgroupfs。这种不一致会导致 API Server 容器启动后不断被 kill。改法是让两边的SystemdCgroup都配置为 true。镜像拉不下来kubeadm init默认使用国外镜像仓库的地址网络受限时会一直拉取失败。稳妥做法是先用kubeadm config images list列出需要的镜像然后替换成可访问的镜像仓库地址拉取到本地再执行 init。资源不足API Server 对 CPU 和内存有最低要求2 核 2G 的机器在跑满其他组件时很容易起不来。用docker stats或crictl stats看资源占用。这个报错本身只是一种现象真正的问题要结合crictl logs和journalctl -u kubelet来看。我见过有人反复kubeadm reset三四次每次都是同样报错最后发现是 containerd 的 cgroup 配置问题改一个配置就全好了。reset 是最后手段不是第一手段。5.2 ImagePullBackOff十次有八次是镜像地址或认证问题Pod 状态一直ImagePullBackOff核心原因是镜像拉取失败。先 describe 看具体错误kubectl describe pod pod-name -n production最常见的原因镜像名打错注意私有仓库的前缀比如harbor.example.com写成了harbor.example或者 tag 写错。tag 不存在v1.2.0其实只有v1.2没有v1.2.0这个 tag拉取时 404。私有仓库认证失败私有仓库需要 imagePullSecret。创建 Secretkubectl create secret docker-registry regcred \ --docker-serverharbor.example.com \ --docker-usernameuser \ --docker-passwordpassword然后在 Deployment 里引用imagePullSecrets: - name: regcred镜像拉取策略问题imagePullPolicy: Always意味着每次创建 Pod 都会去仓库校验仓库偶尔抖动会导致创建失败。内网镜像地址稳定的情况下用IfNotPresent更省事。5.3 CrashLoopBackOff先看日志别急着改配置CrashLoopBackOff表示容器启动后很快退出kubelet 在按指数退避策略反复重启。这时候第一件事是看日志而且要看的是上一次容器退出前的日志kubectl logs pod-name -n production --previous kubectl logs pod-name -n production --tail200常见根因和对应处理应用启动参数错误环境变量、命令行参数拼写错误应用启动即抛异常。检查 Deployment 的 env 和 args。探针配置过严livenessProbe 的 initialDelaySeconds 太短应用还在启动探针已经失败容器被反复杀掉。日志里通常看不到业务异常全是容器被杀的痕迹。这种情况调大 initialDelaySeconds或者加 startupProbe。资源限制导致的 OOMKilledkubectl describe pod看到Last State: Terminated, Reason: OOMKilled说明内存超过 limits 被内核杀了。调大内存 limits或者优化应用内存。健康检查接口不存在livenessProbe 指向的路径返回 404探针失败反复重启。把探针路径改成应用真实存在的健康接口。排查 CrashLoopBackOff 的固定顺序先日志再 describe 看事件和退出原因最后才回头审视 YAML 配置。很多人一看 CrashLoopBackOff 就怀疑资源问题其实最可能是启动命令写错了。5.4 Pod 一直 Pending调度器在等什么Pod 处于Pending说明还没被调度到节点上。这个状态的坑在于它不告诉你为什么需要自己从事件里挖kubectl describe pod pod-name -n production看 Events 尾部常见原因节点资源不足所有节点 CPU/内存都不满足 requests。用kubectl describe nodes查看各节点分配情况或者kubectl top nodes看实时负载。节点有污点taintPod 没有对应的 toleration。比如集群只为特定业务保留了一组专用节点其他 Pod 调度不上去是正常的。PVC 无法绑定Pod 声明了 PVC但 StorageClass 不存在或存储服务异常调度器在等待 PVC 变 Available。查kubectl get pvc看状态。Pending 类问题最忌讳的是反复删 Pod 重试。调度失败一定有原因事件里写得清清楚楚describe 是第一动作。6. 从 Deployment 往生产走Operator、GPU 调度与几个思考6.1 Operator 和 Deployment 到底是什么关系热词里有人搜k8s 中 operator 案例这里顺带说清楚。Operator 不是和 Deployment 并列的一个东西而是把 K8S 的控制逻辑扩展到业务领域的工具。它能管理 Deployment但更常做的是管理一类更复杂的应用。典型的 Operator 案例一个复杂的中间件集群比如 etcd、Prometheus、Elasticsearch它有主从节点、有备份、有扩缩容顺序。如果光用 Deployment你只能描述我要 3 个副本但谁是主、谁是从、挂了之后怎么重新选举这些领域逻辑原生 Deployment 表达不了。Operator 的思路是把这种应用封装成自定义资源CRD比如EtcdCluster然后写一个控制器去 watch 这个资源用代码实现创建各节点 Deployment/StatefulSet、处理故障、执行备份这些逻辑。简单说Operator CRD 控制器控制器内部还是会去操作 Deployment、StatefulSet、Service 这些基础资源。所以我的理解是Operator 是给Deployment 表达不了的业务逻辑准备的如果你的应用就是普通无状态服务老老实实用 Deployment别为了炫技引入 Operator。6.2 K8S 里调度 GPU从资源声明到实际分配k8s 调用 GPU也是经常被问的。GPU 在 K8S 里一般以扩展资源的形式出现。以 NVIDIA GPU 为例节点上装了 nvidia-device-plugin 之后GPU 会以nvidia.com/gpu资源名对外暴露。Pod 要申请 GPU在 resources 里写resources: limits: nvidia.com/gpu: 1注意 GPU 资源通常只在 limits 里写不写 requests。调度器看到这个字段后会把 Pod 调度到有 GPU 的节点上并给容器挂载对应的设备。实际使用中有几个限制得提前知道。第一扩展资源不支持 requests 和 limits 分开设置也不支持超卖nvidia.com/gpu在节点上是整数资源申请 1 就是独占 1 张。第二默认调度不感知 GPU 的显存大小只要节点上报了nvidia.com/gpu的可分配数量不管这张卡是 16G 还是 80G都算 1 个资源。如果集群混用了不同显存的卡调度器可能把一个大模型任务调度到显存不够的节点上Pod 启动后初始化失败。这种场景要么按机型打标签做节点池隔离要么引入 GPU 调度扩展。6.3 面试里关于 Deployment 的高频追问结合搜索热词里的k8s 面试题我把和 Deployment 相关的高频问题整理一下做技术复盘和准备面试都有用。**Deployment 的滚动更新策略有哪些参数**RollingUpdate 和 Recreate。Recreate 是先删旧 Pod 再建新 Pod会有停机窗口一般只有对同时只能有一个实例的应用才用。**maxSurge 和 maxUnavailable 的计算方式是什么**它们可以配成百分比也可以配成整数。比如 replicas4maxUnavailable25%表示 4 个副本里允许先停 1 个。**Deployment 是如何实现回滚的**每个 ReplicaSet 对应一个 revisionDeployment 会保留历史 ReplicaSet默认保留 10 个版本revisionHistoryLimit控制。**Pod 一直 CrashLoopBackOff 怎么排查**先看日志、再看事件、再检查探针和资源限制这是标准链路。**Deployment 和 StatefulSet 的区别是什么**核心是有无状态以及 Pod 的标识和存储是否稳定。面试官问这些背后真正想考察的是你对控制器如何工作的理解而不是背参数。把本文第一节的期望状态机制讲清楚再加一个真实排错案例基本就稳了。6.4 关于自主可控的一点工程体会热词里有k8s 是不是自主可控我从工程角度说下真实体会。Kubernetes 本身是开源项目源代码公开许可证允许商用和二次开发这一点意味着你可以基于任何主流发行版构建自己的私有化平台。但要真正达到可控有几个基础设施层面的前提不能跳过。第一镜像仓库必须自建并且做离线同步。我见过不少企业把容器镜像放在公共平台上一旦平台策略调整整个部署链路就断了。自建 Harbor 加定期离线同步是部署体系可控的第一步。第二K8S 组件的二进制和镜像要提前同步到内网。不管用哪家的发行版核心组件的离线部署能力必须有一旦外部网络不可达集群仍然可以重建。第三升级路线要自己管不能依赖某个厂商的自动升级。把上游版本迭代纳入内部版本管理先测试环境验证再推生产做到心里有数。所以我的看法是可控的从来不是某个工具而是你对自己基础设施的掌控能力。开源给你可控的权利但你要真的动手去搭建私有仓库、离线包、版本管理这套体系这个权利才变成能力。最后再分享一个小技巧我自己的习惯是每个 Deployment 配置都放进 Git 仓库用目录按环境区分dev/、staging/、production/每次变更走 MRCI 里跑一遍kubectl apply --dry-runclient -f deployment.yaml做语法校验再跑一遍kubectl diff -f deployment.yaml看这次改动到底改了线上哪些字段。这套流程看起来多花了两分钟但避免过无数次手滑把副本数改 0 导致线上全挂的事故。K8S 部署 Deployment 这件事难度不在写 YAML而在理解控制器的心态。Deployment 是一个永远在默默修正偏差的循环——你声明期望它负责逼近。把这条主线想通了后续看 StatefulSet、DaemonSet、Operator都会觉得是同一套思想在延伸。踩坑不可怕怕的是不记录、不复盘、不把经验沉淀成团队规范。希望这篇记录能让你在部署路上少踩几个我踩过的坑。
返回列表