news 2026/10/7 10:23:21

从Docker到Kubernetes:容器化部署到集群运维的实战排错指南

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
从Docker到Kubernetes:容器化部署到集群运维的实战排错指南

如果你已经能熟练地写 Dockerfile、能跑通docker-compose up -d,甚至习惯了把 MySQL、Redis 都塞进容器里跑,那说实话,单机容器化这一关你已经过了。但"阶段二"的挑战,恰好是从你试图把这些经验搬到 Kubernetes 集群里那一刻开始的。Docker 让你学会了"怎么把应用装进箱子",Kubernetes 则逼着你重新回答"这个箱子在成百上千台机器上怎么活"。

这篇文章我不打算再重复一遍安装教程,也不打算把 Deployment、Service、Ingress 的名词解释抄一遍。我想聊的是从一个"能跑 Docker 的人"变成"能交付 Kubernetes 集群的人"这条路上,那些真正消耗时间的问题:本地环境为什么起不来、镜像为什么拉不动、Pod 为什么一直 Pending、MySQL 为什么一重启就丢数据、网络为什么时通时不通。每个问题我都会按照实际排查链路来讲,包含命令、配置、排错路径,以及我踩过之后才明白的原理。适合已经具备 Docker 基础、正在准备或刚刚开始接触 Kubernetes 部署的读者,也适合在公司里被人推着去做容器化落地的同学。

1. 把应用塞进容器只是起点,从Compose到集群跨越的到底是什么

1.1 Compose让你觉得"部署很简单",这种错觉会付出代价

很多团队接触容器化的第一步是 Docker Compose。一个docker-compose.yml里写好services、volumes、ports,然后一条docker-compose up -d,所有服务就起来了。这个体验非常爽,但它掩盖了一个关键事实:Compose 的编排范围是一台机器,它不关心也不处理机器故障、网络分区、节点调度。

到了 Kubernetes 阶段,你面对的是同一套 YAML 但思维模型完全不同。最核心的转变有两个:

第一个转变是从"命令式"到"声明式"。你不再说"帮我启动这个容器",而是告诉集群"我希望最终有 3 个 nginx 副本在运行"。Kubernetes 的控制器循环会不断检查现实状态,如果副本数变成 2 了,它自动补一个;如果节点挂了,它把 Pod 重新调度到别的节点。这个机制叫 reconciliation loop,可以类比成公司里的目标管理:老板定了目标,各级主管不断审视差距、推进执行,而不是老板每次亲自盯着一个员工干活。

第二个转变是从"进程视角"到"API 视角"。Compose 里你关心的是容器、网络、卷这些静态资源;Kubernetes 里这一切都被抽象成 API 对象,比如 Pod、Service、Deployment、StatefulSet。你操作的不再是机器上的进程,而是通过kubectl apply往 API Server 提交期望状态。长期用下来,你会发现真正稳定的部署流程一定基于声明式文件,而不是命令。

1.2 集群规划里最容易被忽略的三个前置问题

如果你在公司里负责搭建集群,先别急着装 kubeadm,因为后面至少有三个问题会回头找你。

第一个是网络模型。Kubernetes 本身不实现 Pod 间网络,你需要选一个 CNI 插件。Flannel 简单、开销低,适合小规模集群;Calico 支持 NetworkPolicy、性能好,更适合生产,但要求内核模块和 IP 转发配置正确。无论选哪个,一定要在kubeadm init之前把net.ipv4.ip_forward开好,否则集群内部 DNS 和网络会以非常隐晦的方式坏掉。

第二个是存储。单机上你用volumes挂载宿主机目录没毛病,但在集群里 Pod 是会飘的,今天跑在 node-1,明天可能被调度到 node-2。本地目录跟着 Pod 走,数据并不会跟着走。你必须在集群层面规划存储方案:测试环境可以先用local或者 NFS,生产环境建议考虑云服务商提供的云盘或者自建分布式存储(比如基于 Rook 部署 Ceph)。这个决策越往后拖,成本越高,尤其是当你已经有几十套 MySQL 跑在本地目录上时再迁移,绝对痛苦。

第三个是镜像仓库。企业里必然要有一个私有镜像仓库,不然后面所有节点拉镜像都会慢到你怀疑人生。Harbor 是目前社区里最主流的开源选择,支持镜像复制、漏洞扫描、权限控制。你需要在搭集群之前就把镜像仓库准备好,并写好 tag 规范(比如registry.internal/base/nginx:1.24.0),不要等应用部署时才发现每个节点都要登录一次仓库。

1.3 一个更接近生产的小集群长什么样

我建议的最小生产参考架构是这样:

  • 3 个 master 节点跑kube-apiserver、etcd、kube-controller-manager、kube-scheduler,每个节点至少 4C8G。
  • 3 个 worker 节点跑业务负载,每个节点至少 8C16G,并且挂载一块独立数据盘给容器运行时。
  • 一个独立的镜像仓库节点,跑 Harbor。
  • 一个独立的存储节点,先跑 NFS 给测试环境用,后续再扩分布式存储。
  • 一个负载均衡器,用来暴露kube-apiserver的 6443 端口,以及后续所有 Ingress 流量。

如果你只有两台机器,把 master 和 worker 混布也可以,但要知道 master 被业务 Pod 打挂的风险是真实存在的。集群开始阶段我不建议上全套监控,先装上metrics-server和kubectl top能看资源就够用了,后面再按需加 Prometheus。

2. 本地开发环境的高频翻车现场:桌面端、启动失败与镜像拉取

2.1 Docker Desktop 启动不了,先别急着重装

如果你用 Windows 开发,大概率会遇到 Docker Desktop 启动失败,报错里写着virtualization support not detected或者failed to start docker application container engine。这个报错字面意思是"没检测到虚拟化支持",但实际原因有好几种,我按排查顺序给你理一遍:

第一步,确认 BIOS/UEFI 里开启了虚拟化。Intel CPU 看 VT-x,AMD CPU 看 SVM,这个选项藏得比较深,一般在Advanced -> CPU Configuration里。如果之前开过 VMware/VirtualBox,大概率没问题;但有些主板更新 BIOS 后会把虚拟化重置回关闭状态,所以值得看一眼。

第二步,检查 Windows 功能里是否启用了 Hyper-V、虚拟机平台、适用于 Linux 的 Windows 子系统(WSL 2)。Docker Desktop 在 Windows 上依赖这些组件,缺一个都会起不来。你可以在 PowerShell 里跑:

dism.exe /online /get-featureinfo /featurename:Microsoft-Hyper-V dism.exe /online /get-featureinfo /featurename:VirtualMachinePlatform dism.exe /online /get-featureinfo /featurename:Microsoft-Windows-Subsystem-Linux

如果显示 Disabled,用下面的命令开启后重启:

dism.exe /online /enable-feature /featurename:Microsoft-Hyper-V-All /all /norestart dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart

第三步,确认 WSL 2 是默认版本。跑一下wsl --set-default-version 2,然后wsl --status查看内核是否正常。旧版本的 WSL 内核也会导致 Docker Desktop 无法连接进程。

第四步,如果你开了 VMware Workstation 或 VirtualBox,它们与 Hyper-V 的冲突是经典的翻车原因。要么共存时手动改 Docker Desktop 的 backend 配置,要么干脆统一用 Hyper-V。这里没有所有人都适用的答案,取决于你的其他虚拟化负载。

2.2 镜像拉取慢与 daemon.json 的潜规则

无论是 Docker Desktop 还是 Linux 上的 Docker Engine,镜像下载慢几乎是国内团队第一天必遇的问题。解决方案是配置 registry mirror,也就是镜像加速器。Docker 读的是/etc/docker/daemon.json,改动后必须重启 Docker 才生效:

{ "registry-mirrors": [ "https://docker.mirrors.example.com" ] }

改完执行:

sudo systemctl daemon-reload sudo systemctl restart docker

然后跑docker info,看输出里的Registry Mirrors一节是否列出了你配置的地址,确认生效。

这里要提醒一句:不要为了图快随便搜一个来路不明的加速地址填进去。镜像加速本质上是中间商,你的镜像内容、拉取流量都会经过它,选择正规云厂商提供的或者在团队内有统一运维管理的加速服务才靠谱。另外,很多"镜像下载慢"其实不是 Docker 配置问题,而是镜像本身太大、仓库在国外、或者本地 DNS 解析有问题。排查时可以先用docker pull hello-world这种小镜像测链路,再用实际业务镜像测速度,定位到底哪一环慢。热词里频繁出现"docker镜像下载慢"这个问题,说明它确实是个普遍痛点,但很多人一开始改错方向,浪费时间。

2.3 权限报错的真相:docker 命令背后的 Unix Domain Socket

在 Linux 上装完 Docker 后,普通用户直接跑docker ps经常会看到:

permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock

这不是你操作错了,而是权限模型的问题。Docker 客户端与服务端通过/var/run/docker.sock通信,这个 socket 文件默认属于root用户和docker组。把当前用户加入docker组:

sudo usermod -aG docker $USER newgrp docker

然后重新登录终端再跑docker ps就能用了。注意newgrp docker只是让当前会话临时生效,彻底生效需要重新登录或者重启。把用户加进docker组等同于给用户 root 权限,因为 Docker 可以直接挂载宿主机目录、执行特权操作。所以生产服务器上不要随便把人加进 docker 组,这是安全边界问题。

Ubuntu 下安装 Docker 本身不复杂,只要走官方 apt 仓库,不装 Ubuntu 自带的旧版本docker.io就行。安装完成后再确认containerd和docker-buildx-plugin都装上了,避免后面构建镜像时才发现缺少组件。

3. 用声明式YAML接管应用:从docker run到Deployment与Service的演替

3.1 从 nginx 入手:第一个 Deployment 的正确写法

热词里频繁出现"kubernetes 部署 nginx",可见这是所有人练习的第一个对象。但如果你直接把docker run -d -p 8080:80 nginx翻译成 Kubernetes 对象,方向就错了。Kubernetes 的最小调度单位是 Pod,而 Pod 通常不直接创建,而是通过 Deployment 来托管。

一个最基础的 Deployment 文件长这样:

apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment namespace: default spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.24.0 ports: - containerPort: 80 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 500m memory: 256Mi livenessProbe: httpGet: path: /healthz port: 80 initialDelaySeconds: 10 periodSeconds: 10 readinessProbe: httpGet: path: /index.html port: 80 initialDelaySeconds: 5 periodSeconds: 5

这里有几个细节值得说。requests是给调度器看的资源申请,limits是运行时限额。如果你不写 resources,集群会把 Pod 当成"无限资源"容器,调度时最容易被塞到高负载节点,然后被莫名判定为异常。这是新手最常忽视的问题,也是集群里 Pod OOM 的根源之一。

livenessProbe决定容器是否存活,失败会重启容器;readinessProbe决定容器是否接收流量,失败期间不会分配到请求。很多团队上线后发现"明明 Pod 起来了,但服务不稳定",多半是探针没配好,或者只配了 liveness 没配 readiness。

把文件保存为nginx-deployment.yaml,apply 之后用两条命令观察状态:

kubectl apply -f nginx-deployment.yaml kubectl get pods -o wide kubectl describe deployment nginx-deployment

describe是个宝藏命令,它能告诉你 Deployment 的 ReplicaSet 创建情况、事件记录、镜像拉取状态,绝大多数问题都能在这里找到线索。

3.2 Service 和 Docker -p 的端口映射逻辑完全不同

Docker 里用-p 8080:80把容器端口映射到宿主机端口,在 Kubernetes 里这个逻辑被拆开了。Pod 的 IP 是集群内网 IP,外界无法直接访问,你需要 Service 提供稳定的访问入口。

最常见的 ClusterIP 类型 Service 长这样:

apiVersion: v1 kind: Service metadata: name: nginx-service namespace: default spec: selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80

port是 Service 对外暴露的端口,targetPort是后端 Pod 上的容器端口。Service 通过selector和 Pod 的 labels 关联,这个机制替代了 Docker 里的服务名解析。集群内部其它应用访问它时,直接用http://nginx-service就能通。

如果你要让外部访问,则需要 NodePort 或者 Ingress。NodePort 会在每个节点上开一个端口范围在 30000-32767 之间的随机端口,适合临时调试,不适合生产;生产环境一般走 Ingress 统一入口。Ingress 本身也是要装一个控制器的,比如 Nginx Ingress Controller,它会根据 Ingress 规则把请求路由到对应的 Service。

这个阶段有些人会犯一个错误:直接在 Deployment 里配hostNetwork: true。这个配置确实能让 Pod 直接用宿主机网络,但代价是端口冲突无法被 Kubernetes 管理,几乎丢掉了 Service 和网络策略的全部优势。除非有特殊需求,否则不要用。

3.3 探针、优雅下线与滚动更新的实际问题

上线一次发布后,你会发现滚动更新的过程很有意思。Deployment 默认是 RollingUpdate 策略,集群会先创建新 Pod,等它 ready 后,才销毁旧 Pod。如果你只更新镜像 tag,命令如下:

kubectl set image deployment/nginx-deployment nginx=nginx:1.26.0

或者更推荐的方式:修改 YAML 文件后重新kubectl apply -f。后者符合 GitOps 理念,每一次变更都留痕,可追溯。

但这里有一个很容易被忽略的坑:如果你的应用在收到 SIGTERM 信号时没有优雅处理,滚动更新时会出现大量 502。Kubernetes 在销毁旧 Pod 前会发送 SIGTERM,等待terminationGracePeriodSeconds(默认 30 秒)后强制杀死。Java 应用、长连接服务、消息队列消费者尤其要注意,必须实现优雅停机逻辑,否则每次发布都伴随报错告警。

4. 有状态服务是阶段二真正的硬骨头:MySQL与Redis的容器化持久生存

4.1 为什么 MySQL 在容器里"跑起来了",却让你在深夜加班

Docker 里安装 MySQL 有多简单,生产环境里维护 MySQL 容器就有多头疼。热词里频繁出现"docker安装mysql失败""docker安装mysql8.0并使用",说明大家都在容器化数据层上摔过跟头。核心问题在于数据持久化。

如果你在 Docker 阶段用:

docker run -d -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=123456 \ mysql:8.0

容器删除后数据直接消失。很多人在 Docker 阶段会挂载宿主机目录:

docker run -d -p 3306:3306 \ -v /data/mysql:/var/lib/mysql \ -e MYSQL_ROOT_PASSWORD=123456 \ mysql:8.0

这个思路在单机上没问题。但在 Kubernetes 里,Pod 重建后不一定调度回原节点,挂在 node-1 的/data/mysql目录对 node-2 上的新 Pod 完全没有意义。正确的做法是用 PersistentVolume 和 PersistentVolumeClaim,把"存储"和"节点"解耦。

一个使用 NFS 作为底层存储的 PV/PVC 长这样:

apiVersion: v1 kind: PersistentVolume metadata: name: mysql-pv spec: capacity: storage: 20Gi accessModes: - ReadWriteOnce nfs: server: 192.168.1.100 path: /data/k8s/mysql --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc namespace: database spec: accessModes: - ReadWriteOnce resources: requests: storage: 20Gi

然后在 Deployment 的 volume 配置里引用这个 PVC,MySQL 的 data 目录就不依赖于 Pod 所在节点了。这个机制是我建议所有有状态服务在集群上的第一步改造。

4.2 redis 主从在 Kubernetes 里的正确姿势

Redis 主从部署在 Docker 时代不复杂:启动一个主节点,再启动一个从节点,用slaveof或者配置 replicaof 指向主节点的 IP。但到了 Kubernetes,主节点 Pod 重启后 IP 会变,你必须用稳定的方式访问它。

对于有状态服务,Kubernetes 提供了 StatefulSet。它和 Deployment 最大的区别是:Pod 有稳定的网络标识(如redis-0.redis-headless.database.svc.cluster.local)和稳定的存储标识。StatefulSet 按序创建、按序删除,配合 Headless Service,每个副本都有固定 DNS 名称,从节点可以直接配置:

replicaof redis-0.redis-headless.database.svc.cluster.local 6379

这样即使 Pod 重建,DNS 名称不变,主从关系也不会断。

要注意的是 Redis 的持久化配置。如果只配置了 RDB 快照,宕机可能丢几十秒数据;如果开了 AOF,文件写入频率高,对存储性能要求也随之上升。我的建议是测试环境用 RDB 够用,生产环境必须 AOF,并同时把appendfsync设置为everysec,在数据安全与性能之间取平衡。

4.3 ConfigMap 和 Secret:别再把密码写死在镜像里

我见过太多团队,把 MySQL 密码、Redis 密码直接写进 Dockerfile 的 ENV 里,或者写进 application.yml 然后打进镜像。这个习惯在 Kubernetes 阶段必须改掉,因为镜像一旦构建,里面的敏感信息就会被固化,后续无法安全轮换密钥。

Kubernetes 的解决方案是 ConfigMap 管普通配置,Secret 管敏感信息。例如把 MySQL 链接信息放进 ConfigMap:

apiVersion: v1 kind: ConfigMap metadata: name: mysql-config namespace: database data: MYSQL_HOST: mysql-0.mysql-headless.database.svc.cluster.local MYSQL_PORT: "3306" MYSQL_DATABASE: business --- apiVersion: v1 kind: Secret metadata: name: mysql-secret namespace: database type: Opaque stringData: MYSQL_PASSWORD: your-strong-password

然后在应用 Deployment 里通过envFrom注入:

spec: containers: - name: app image: your-app:1.2.0 envFrom: - configMapRef: name: mysql-config - secretRef: name: mysql-secret

这样做的好处是:换密码只需要更新 Secret 然后滚动重启应用,不用重新构建镜像。密码不会出现在镜像层里,也不会被推到镜像仓库。

5. 故障排查链路复盘:服务起不来、网络不通、镜像拉不动时我在查什么

5.1 Docker 服务启动失败:从 journalctl 到 daemon.json

故障排查最忌乱猜。Docker 服务起不来的时候,第一件事是看服务状态和日志:

systemctl status docker journalctl -u docker.service -n 100 --no-pager

日志里常见的原因有三类:

第一类是daemon.json写坏了。JSON 语法错误、字段名不对,Docker 直接拒绝启动。排查方法:用dockerd --validate或者干脆把daemon.json备份后清空,再启动试试。能启动就说明配置有问题,用二分法把配置项逐个加回去。

第二类是磁盘空间满了。Docker 镜像和容器日志默认存在/var/lib/docker,日志无限增长很容易把分区撑满。你用df -h一看就知道。清理手段:docker system prune清理悬空镜像、docker system df查看占用、日志可以通过配置 log rotation 来控制。

第三类是服务之间端口或 iptables 规则冲突。如果你之前手动配置过 iptables,清理了 FORWARD 链规则或者 docker 自定义链被破坏,会导致容器之间、容器到外网的连接全部异常。最快速的办法是重启 Docker 让 iptables 规则重建,但更重要的是搞清楚是谁在改 iptables,比如 firewalld 和 Docker 的冲突就是云服务器上非常常见的组合。

5.2 容器网络不通的完整排查链路

容器网络不通的情况我遇到过太多次。错误的解决方式是反复重启容器,正确的解决方式是沿着网络路径逐层排查。

第一层,确认容器状态:

docker ps -a docker inspect <container_id> | grep -A 10 "NetworkSettings"

第二层,确认容器能访问自己:

docker exec -it <container_id> ping 127.0.0.1 docker exec -it <container_id> ping <网关IP>

如果容器自己 ping 不通,说明容器内部网络栈有问题;如果能通网关,说明桥接网络正常。

第三层,确认宿主机能不能访问容器:

ping <容器IP> docker network inspect bridge

如果宿主机 ping 不通容器,多半是 bridge 网桥异常或 iptables 规则被清空。

第四层,确认容器能不能出外网:

docker exec -it <container_id> ping 8.8.8.8 docker exec -it <container_id> nslookup baidu.com

能 ping 通 IP 但解析不了域名,是 DNS 问题,检查宿主机/etc/resolv.conf和 Docker daemon 的dns配置。IP 都不通,就是 NAT 或路由问题,检查iptables -t nat -L POSTROUTING有没有 MASQUERADE 规则。

最后一层才考虑跨主机通信。Kubernetes 里的跨节点 Pod 通信依赖 CNI 插件,如果 Pod A 能访问自己节点上的 Pod B,但访问不了另一节点的 Pod C,优先检查 CNI 的隧道接口或路由表,而不是业务代码。这个排查思路写下来,基本能覆盖"docker网络不通"这个热词背后 80% 的场景。

5.3 Kubernetes 侧高频故障:ImagePullBackOff、CrashLoopBackOff、Pending

到了 Kubernetes 阶段,你遇到最多的三个 Pod 状态就是 ImagePullBackOff、CrashLoopBackOff 和 Pending。

ImagePullBackOff 就是镜像拉取失败。先用kubectl describe pod <name>看事件,常见原因有:镜像名或 tag 写错、私有仓库未登录(可以使用 imagePullSecrets 解决)、镜像仓库地址从节点访问不了、磁盘空间不足。注意kubectl get events --sort-by=.lastTimestamp能按时间排序看所有事件,比逐个 pod describe 效率高很多。

CrashLoopBackOff 表示容器启动后崩溃被不断重启。这个状态只告诉你"容器起不来",但不告诉你原因。核心排查步骤是看日志:

kubectl logs <pod-name> --tail=200 kubectl logs <pod-name> --previous

--previous很重要,因为容器崩溃后当前日志可能已经丢失,上一条日志里往往才有真正的错误堆栈。常见原因集中在启动命令找不到、配置依赖缺失、探针配置过激进导致存活判定为失败、或者连接不上外部的数据库/中间件导致应用启动即失败。

Pending 状态通常意味着资源不足或者调度失败。kubectl describe pod <name>最下面会写清楚调度失败的原因,比如0/3 nodes are available: 1 Insufficient cpu, 2 Insufficient memory。很多人直接加资源,但更好的办法是先kubectl top nodes看集群整体资源水位,再决定是扩容节点、压缩现有 Pod 请求,还是调整调度策略。

6. 规模化运维的三件必须做的事:配额、自动伸缩与日志可观测

6.1 ResourceQuota 和 LimitRange:别等互相挤爆了才后悔

集群一旦开始有多个团队共享,资源配额就不是可选项了。没有 ResourceQuota 的集群就像一个没有物业的小区,谁家装修吵到别人、谁家用料占了公共区域,最后一定出矛盾。

给命名空间设置配额:

apiVersion: v1 kind: ResourceQuota metadata: name: dev-quota namespace: dev spec: hard: requests.cpu: "8" requests.memory: "16Gi" limits.cpu: "16" limits.memory: "32Gi" persistentvolumeclaims: "10" pods: "50"

LimitRange 则是约束单个 Pod 的最小和最大资源申请,防止有人写requests.cpu: 100m但实际上限把整个节点吃干。配上之后,团队写 YAML 时偷懒不写 resources 就不让你创建,这招能从源头掐掉大量不规范部署。

6.2 HPA:从"手动扩容"到"按指标扩容"

Kubernetes 的自动伸缩是很多人学它最初的吸引力之一。HorizontalPodAutoscaler 可以根据 CPU 使用率或自定义指标调整副本数,前提是集群里装了metrics-server。

一个基于 CPU 的 HPA 配置:

apiVersion: autoscaling/v2 kind: HorizontalPodAutoscaler metadata: name: myapp-hpa namespace: production spec: scaleTargetRef: apiVersion: apps/v1 kind: Deployment name: myapp minReplicas: 3 maxReplicas: 10 metrics: - type: Resource resource: name: cpu target: type: Utilization averageUtilization: 60

设置完后,用kubectl get hpa可以看到当前指标值和目标值,用kubectl describe hpa能看到伸缩事件。实测里有两点要注意:一是应用启动时间较长时,扩容会被新 Pod 还没 ready 拖慢,需要合理的探针配合;二是缩容速度很保守,因为默认的--horizontal-pod-autoscaler-downscale-stabilization是 5 分钟,这是为了防止指标抖动导致频繁伸缩。

6.3 日志和监控:没有可观测性的集群等于盲飞

容器化部署带来的一个麻烦是日志不再是落盘文件,而是标准输出。K8s 里用kubectl logs只能看单个 Pod,Pod 一重建日志就没了,这在生产环境完全不可接受。

常规做法是让应用把日志写到 stdout 和 stderr,然后由节点上的日志采集组件统一收集。主流的方案是 EFK:Elasticsearch 存日志,Filebeat/Fluentd 采日志,Kibana 做可视化。更轻量的替代是 Loki + Promtail + Grafana,资源占用小很多,中小团队够用。

监控层面,Prometheus + Grafana 是事实标准。部署方式可以直接用 kube-prometheus-stack,装了之后能拿到节点、Pod、容器的 CPU/内存/网络/磁盘指标,配合告警规则能第一时间发现异常。我个人的经验是:告警宁可少配也不要乱配,一条凌晨 3 点触发但完全不是问题的告警,会让大家以后都不看告警,疲倦感会毁掉监控体系。

Kubernetes 集群的运维不是靠人肉盯出来的,而是靠信息流:日志给上下文、指标给趋势、事件给变化。这三样配齐了,你才算有资格说自己做的是企业级容器化部署。

最后再分享一点个人的真实体会。阶段二真正的门槛不是 Docker 和 Kubernetes 的命令行熟练度,而是你能不能接受"基础设施是代码、运行环境是声明出来的"这套哲学。我见过不少团队,Docker 玩得飞起,但到了 Kubernetes 部署阶段,遇到 Pod 起不来就习惯性上服务器手动敲命令救火,最后反而比传统虚拟机部署更痛苦。原因很简单:你想用 Kubernetes 的弹性、自愈、声明式能力,却仍然用"人肉运维"的习惯去操作它,两套思维打架。

如果你正在搭建自己的集群,我建议你先在一个可以随时重建的环境里练习,把集群搞坏再恢复,反复几次,你会对 etcd、CNI、kubelet 这些组件之间的关系产生真正的体感。等你体会到"一切皆 API 对象"带来的确定性,再看之前 Docker 阶段那些手工操作,你会发现自己已经不太愿意回到那样做事的方式了。集群继续跑着,后面还会有更复杂的坑,但至少你已经知道去哪里看日志,去哪里查事件,往哪个方向找答案。这就是从"会部署"到"能运维"的分水岭。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/7 10:23:20

SpringBoot+Vue问卷系统实战:从表设计到部署避坑指南

做一个基于SpringBoot的调查问卷系统&#xff0c;听起来像是毕业设计里最经典的那类选题&#xff0c;但实际上手之后你会发现&#xff0c;它远没有题目看起来那么“标准”。问卷要支持多少种题型、答案怎么存才能方便统计、如何防止同一个人重复提交、前端怎么和一个后端工程打…

作者头像 李华
网站建设 2026/10/7 10:23:05

冬月廿六感怀:平日里的复盘与生活整理术

晨光透过窗帘的时候&#xff0c;我翻开手机日历&#xff0c;上面写着“乙巳年冬月廿六”&#xff0c;下面一行小字备注“平日”。冬月是农历十一月&#xff0c;一年中最冷的一段日子&#xff1b;平日&#xff0c;在老黄历里是普通的一天&#xff0c;没有特别的宜忌&#xff0c;…

作者头像 李华
网站建设 2026/10/7 10:21:57

AI-IDE-Agent多角色协同开发:原理、选型与实战避坑

1. 这件事到底在解决什么最近这段时间&#xff0c;AI-IDE-Agent 这个方向肉眼可见地火了起来。很多人一开始以为它只是一个“能帮你补全代码”的插件&#xff0c;真正上手之后才发现&#xff0c;这东西的想象空间远比自动补全大得多——它已经能扮演一个小型开发团队里的多个角…

作者头像 李华
网站建设 2026/10/7 10:21:24

Java医院预约挂号系统实战:从排班建模到并发防重

在医院排队挂号的场景每个人都不陌生&#xff0c;凌晨抢号、窗口排长队、医生临时停诊、患者跑冤枉路……这些矛盾最终都指向一个问题&#xff1a;号源这种稀缺医疗资源&#xff0c;缺乏一个公开、可控、可追溯的分配机制。基于JAVA的医院预约挂号管理系统&#xff0c;就是把科…

作者头像 李华
网站建设 2026/10/7 10:18:59

JavaWeb药品管理系统实战:Servlet+JSP+JDBC全流程部署指南

简介&#xff1a;本资源是一套面向Java初学者与高校实训学生的医院药品管理系统实战项目&#xff0c;适用于JavaWeb课程设计、期末大作业及本科毕业设计。系统采用B/S架构&#xff0c;基于Java语言开发&#xff0c;融合JSP前端展示、Servlet业务逻辑与MySQL数据库持久化&#x…

作者头像 李华
网站建设 2026/10/7 10:18:24

扩散模型原理详解:从加噪去噪到图像与视频生成

在生成图片、生成视频的 AI 工具集中爆发的 2023-2025 年&#xff0c; 扩散模型&#xff08;Diffusion Model&#xff09; 几乎成了所有主流图像生成产品的共同底座。从 Midjourney 的绘图质量&#xff0c;到 Stable Diffusion 的开源生态&#xff0c;再到各类可控视频生成工…

作者头像 李华