☰
K8s核心概念动态解析:Pod、Deployment、Service、Namespace运行机制
2026/10/12 4:04:03 网站建设 项目流程

1. 这不是教科书,是我在三个生产环境里踩出来的K8s概念地图

你点开这个标题,大概率正被“Pod、Service、Deployment、Namespace”这些词绕得头晕。我第一次看官方文档时也这样——每个词单独看都像懂了,合在一起就发现根本串不起来:为什么一个应用要拆成Pod+Deployment+Service三层?为什么删掉Pod它自己又冒出来?Namespace到底管的是权限还是网络?这些不是术语考试题,而是你明天就要在测试环境里敲命令、改YAML、排查服务连不通的现实问题。

我带过的几个团队,新人上手K8s最卡壳的从来不是kubectl命令记不住,而是脑子里没有一张动态运行图:不知道组件之间谁调用谁、谁依赖谁、谁在什么条件下会触发什么行为。比如你执行kubectl delete pod xxx,它立刻重建,但你未必意识到背后是Deployment控制器在持续“巡检”Pod状态;你给Service配了个ClusterIP,却没想明白这个IP根本不在任何网卡上,它只是iptables或ipvs规则里的一个虚拟入口。这些“看不见的逻辑”,才是K8s真正难啃的硬骨头。

这篇内容完全跳过抽象定义,直接从真实操作现场反推概念本质。我会用你在终端里实际看到的日志、kubectl get返回的真实字段、describe输出的关键行、甚至tcpdump抓到的包流向,把每个术语还原成可观察、可验证、可打断的活体结构。比如讲Label和Selector,我不说“键值对用于标识资源”,而是告诉你:当你在Deployment里写selector: matchLabels: app: nginx,K8s实际做的就是遍历所有Pod,检查它们的metadata.labels里有没有完全匹配这一组键值——少一个、多一个、值错一位,Deployment就永远找不到Pod,副本数死在0。这种颗粒度的解释,才是能让你半夜debug时不抓瞎的干货。

适合谁读?如果你已经装好minikube或k3s,能跑起一个Nginx Pod,但面对YAML文件还像在读天书;如果你被同事问“这个Service为啥访问不了”,翻文档查了一小时却找不到关键线索;如果你觉得K8s像一堆乐高积木,知道每块叫什么,但拼不出能跑起来的完整模型——那这篇就是为你写的。它不承诺让你速成架构师,但能确保你下次打开kubectl get all -A时,眼睛扫过去就知道哪一行代表什么、哪一列藏着陷阱、哪个状态异常意味着该去查哪个日志。

2. 概念不是名词解释,是运行时的动态契约

2.1 Pod:不是容器,是容器的“共享工作间”

很多人第一反应是“Pod就是容器”,这就像说“汽车就是发动机”——错得离谱。Pod的本质,是K8s调度的最小不可分割单元,它里面可以塞1个容器,也可以塞5个紧密协作的容器(比如主应用+日志收集sidecar+配置热更新agent)。关键在于,这些容器共享同一套底层资源视图:同一个Network Namespace(所以它们localhost互通)、同一个IPC Namespace(能用System V消息队列通信)、同一个PID Namespace(能看到彼此进程)、挂载相同的Volume(数据卷对所有容器可见)。

我见过最典型的误用场景:某团队把Web服务器和数据库塞进同一个Pod。理由是“它们总是一起部署”。结果呢?数据库内存暴涨拖垮Web,或者Web被DDoS打挂导致数据库连接池全占满。K8s的设计哲学在这里很清晰:Pod内容器必须是强耦合、生命周期完全一致的伙伴。Web和DB显然不是——它们该各自独立成Pod,通过Service发现和通信。

实操验证很简单:

# 创建一个含2个容器的Pod(nginx + busybox) kubectl apply -f - <<'EOF' apiVersion: v1 kind: Pod metadata: name: multi-container-pod spec: containers: - name: nginx image: nginx:alpine - name: busybox image: busybox:1.35 command: ['sh', '-c', 'while true; do sleep 3600; done'] EOF

然后进busybox容器:

kubectl exec -it multi-container-pod -c busybox -- sh # 在busybox里直接curl localhost:80,能通!因为共享网络栈 # 查看进程,能看到nginx的worker进程(ps aux | grep nginx) # 查看网络接口,ifconfig显示的IP和nginx容器里看到的一模一样

提示:这就是Pod的核心契约——它不是容器集合,而是一个为多个容器提供共享运行环境的抽象层。你部署时指定的是Pod,K8s调度器决定把这个Pod放在哪个Node上,然后kubelet负责在该Node上启动所有容器并注入共享上下文。

2.2 Node与Master:不是服务器角色,是控制平面与数据平面的分离协议

新手常问:“我的K8s集群有3台服务器,哪台是Master?”这个问题本身就有陷阱。K8s的架构设计刻意模糊了物理机器和逻辑角色的绑定。一个物理服务器(Node)上,既可以运行Master组件(如kubeadm init初始化的单机集群),也可以只运行Worker组件(如k3s默认模式)。真正的分界线在于组件功能,而非机器归属。

  • Control Plane(控制平面):由API Server、etcd、Scheduler、Controller Manager组成。它们不运行你的业务容器,只负责“发号施令”——接收你kubectl apply的请求,存入etcd,调度器计算该Pod放哪台Node,Controller Manager盯着Pod状态确保数量达标。
  • Data Plane(数据平面):即所有Worker Node上的kubelet + container runtime(dockerd/containerd)。它们是“执行者”——从API Server监听指令,拉镜像、启容器、上报状态。

关键细节:API Server是唯一能直接读写etcd的组件。你kubectl get pod,实际是向API Server发HTTP GET,API Server从etcd查数据后返回给你;你kubectl delete pod,API Server先删etcd里的记录,再通知对应Node的kubelet终止容器。这意味着:

  • 如果API Server宕机,你还能kubectl get(因为kubectl缓存了最近一次响应),但apply/delete全部失败;
  • 如果某个Node的kubelet挂了,API Server会很快标记该Node为NotReady,但etcd里Pod记录还在,Scheduler不会往这台机器调度新Pod。

我在线上遇到过最痛的教训:某次升级kubelet版本后,Node状态卡在NotReady,但kubectl get node显示Ready。排查发现是kubelet配置里--node-status-update-frequency=10s被误设为10m,导致它10分钟才上报一次心跳,API Server等不及就标记为NotReady。而get node显示Ready,是因为kubectl默认显示的是Node对象的conditions字段,而该字段可能因缓存延迟未刷新。真相要查kubectl describe node xxx里的Conditions时间戳。

2.3 Deployment:不是部署工具,是“状态控制器”的具象化

Deployment常被误解为“高级版kubectl run”,其实它根本不是用来“部署”的,而是用来维持某种期望状态。它的核心逻辑就一句话:持续比对当前集群中Pod的实际状态,与Deployment YAML里声明的期望状态(replicas、image、env等),一旦发现差异,立即发起修复动作。

举个血泪案例:某次发布,运维同学手动kubectl edit deployment xxx改了镜像版本,但忘了改imagePullPolicy: Always。结果新Pod启动时,Node本地有旧镜像缓存,直接用了旧版,导致线上功能异常。为什么Deployment没阻止?因为它只管“Pod数量是否达标”、“标签是否匹配”,不管镜像内容是否真的更新了——这是imagePullPolicy和镜像仓库的职责。

Deployment的修复机制分三步走:

  1. RollingUpdate策略:默认逐个替换Pod(先启新Pod,等就绪后再删旧Pod),避免服务中断;
  2. ProgressDeadlineSeconds:如果10分钟内新Pod没就绪(比如健康检查一直失败),Deployment会标记为Progressing=False,并停止滚动;
  3. Revision History:每次kubectl apply修改Deployment,K8s自动创建新ReplicaSet,并保留旧ReplicaSet(默认保留10个),方便kubectl rollout undo回滚。

验证方法:

# 查看Deployment关联的ReplicaSet kubectl get rs -l app=nginx # 查看某个ReplicaSet管理的Pod(注意OWNER REFERENCE字段) kubectl describe rs nginx-deployment-7c54d9c8d4 # 手动删掉一个Pod,观察Deployment如何秒级重建 kubectl delete pod -l app=nginx

注意:Deployment本身不直接创建Pod,它创建ReplicaSet,ReplicaSet再创建Pod。这种分层设计让K8s能支持更复杂的扩缩容策略(比如蓝绿发布用两个Deployment,金丝雀用Istio+Deployment组合)。

2.4 Service:不是负载均衡器,是集群内部的“服务发现协议”

Service最常被当成“K8s版Nginx”,这是巨大误区。它根本不处理HTTP流量,也不做SSL终止,甚至不解析域名——它就是一个集群内服务发现和负载均衡的抽象层,底层靠kube-proxy(或CNI插件)实现。

Service的三种类型本质区别:

  • ClusterIP(默认):只在集群内可达,分配一个虚拟IP(如10.96.1.100),这个IP不存在于任何网卡,而是通过iptables规则将发往该IP:Port的包重定向到后端Pod的IP:Port。kubectl get service看到的CLUSTER-IP列就是它。
  • NodePort:在ClusterIP基础上,把服务暴露到每个Node的固定端口(30000-32767)。外部通过<NodeIP>:<NodePort>访问,流量经NodePort→ClusterIP→Pod。
  • LoadBalancer:云厂商专用,自动创建云负载均衡器,将流量转发到NodePort。

关键原理:Service的selector匹配Pod的labels,但匹配发生在Service创建时,而非请求时。这意味着:

  • 你先创建Service,再创建带匹配label的Pod,Service能立刻发现;
  • 你先创建Pod,再创建Service,同样能发现(kube-proxy会监听Pod事件);
  • 但如果你修改Pod的label,使其不再匹配Service selector,该Pod会立即从Endpoint列表中消失,后续请求不会再打到它。

实操验证Endpoint:

# 创建Service kubectl expose deployment nginx-deployment --port=80 --target-port=80 --name=nginx-svc # 查看Endpoints(这才是真实后端地址) kubectl get endpoints nginx-svc # 修改一个Pod的label,观察Endpoints变化 kubectl label pod <pod-name> app=nginx-new --overwrite # 再查endpoints,那个Pod的IP已消失

提示:kubectl get endpoints是诊断Service不通的黄金命令。如果ENDPOINTS为空,说明selector没匹配到任何Pod;如果非空但服务仍不通,问题大概率出在Pod自身(端口没监听、健康检查失败、防火墙拦截)。

2.5 Namespace:不是文件夹,是API资源的“逻辑隔离域”

Namespace常被类比为Linux的chroot或Docker的--network,但它的作用远不止隔离。它是K8s API Server对资源进行分组、配额、权限控制的基础单元。同一个物理集群,可以同时运行开发、测试、生产环境,互不干扰——不是靠网络隔离,而是靠API层面的访问控制。

Namespace的隔离边界非常明确:

  • ✅ 隔离资源对象:Pod、Service、ConfigMap在不同Namespace下同名无冲突;
  • ✅ 隔离RBAC权限:RoleBinding只能绑定本Namespace内的Role;
  • ✅ 隔离ResourceQuota:可以给dev Namespace限制最多10个CPU、20Gi内存;
  • ❌ 不隔离网络:跨Namespace的Pod默认能互相ping通(除非CNI插件启用NetworkPolicy);
  • ❌ 不隔离存储:PVC/PV是集群级资源,跨Namespace可共享(需StorageClass支持)。

我见过最危险的操作:某团队为“省事”,把所有环境都放在default Namespace。结果一次误删kubectl delete all --all-namespaces(本意是删test ns),default里几十个生产Pod瞬间蒸发。后来强制推行命名规范:prod-app1,staging-api,dev-db,并用ResourceQuota锁死每个Namespace的资源上限。

验证Namespace效果:

# 创建两个同名Service在不同Namespace kubectl create namespace ns-a kubectl create namespace ns-b kubectl run nginx-a --image=nginx -n ns-a kubectl run nginx-b --image=nginx -n ns-b kubectl expose pod nginx-a --port=80 -n ns-a kubectl expose pod nginx-b --port=80 -n ns-b # 在ns-a里curl nginx-b.default.svc.cluster.local?不行!必须用完整域名 # 正确方式:curl nginx-b.ns-b.svc.cluster.local

注意:K8s内置DNS服务为每个Service生成域名:<service-name>.<namespace>.svc.cluster.local。跨Namespace访问必须用全限定域名,这是Namespace网络隔离的体现。

3. 核心概念联动:从YAML到真实世界的映射链

3.1 一个典型YAML文件,如何在集群里活起来?

我们以最简Deployment为例,逐行解剖它在K8s世界里的“生命旅程”:

apiVersion: apps/v1 # 告诉API Server:这是apps组的v1版本资源 kind: Deployment # 资源类型是Deployment metadata: name: nginx-deployment # Deployment对象在etcd里的唯一ID labels: app: nginx # Deployment自身的标签(用于kubectl get -l筛选) spec: replicas: 3 # 期望保持3个Pod副本 selector: # 关键!定义Deployment“认谁当孩子” matchLabels: # 必须和下面template.metadata.labels完全一致 app: nginx # 否则Deployment永远找不到Pod,replicas卡在0 template: # Pod模板,Deployment用它来创建Pod metadata: labels: app: nginx # 注意:这里必须和selector.matchLabels一致! spec: containers: - name: nginx image: nginx:1.21 # 镜像名,kubelet从仓库拉取 ports: - containerPort: 80 # 告诉K8s:这个容器监听80端口(Service targetPort依据此)

执行kubectl apply -f nginx.yaml后,发生了什么?

  1. API Server校验:检查YAML语法、字段合法性(如replicas必须是正整数)、权限(你是否有apps/v1 Deployment的create权限);
  2. 写入etcd:将Deployment对象序列化为JSON存入etcd/registry/deployments/default/nginx-deployment;
  3. Controller Manager介入:Deployment Controller监听etcd,发现新Deployment,立即创建一个ReplicaSet(名字带哈希值,如nginx-deployment-7c54d9c8d4),并设置其replicas=3;
  4. ReplicaSet Controller介入:监听到新ReplicaSet,开始创建3个Pod,每个Pod的metadata.ownerReferences指向该ReplicaSet;
  5. Scheduler调度:为每个Pod选择合适的Node(根据资源、亲和性、污点等),更新Pod的spec.nodeName字段;
  6. kubelet执行:各Node上kubelet发现有Pod分配给自己,调用containerd拉取nginx镜像、启动容器、上报状态;
  7. Service关联:如果存在匹配label的Service,kube-proxy更新iptables规则,将ClusterIP:80流量转发到3个Pod的PodIP:80。

整个过程里,etcd是唯一真相源,所有组件都从etcd读取状态、向etcd写入变更。这也是为什么K8s强调“声明式API”——你声明“我要3个nginx Pod”,K8s保证最终状态收敛到这个目标,中间过程(重启、节点故障、网络抖动)全由控制器自动修复。

3.2 Label与Annotation:一个管“找得到”,一个管“看得懂”

Label和Annotation长得像(都是key-value),但使命截然不同:

  • Label(标签):是K8s的索引字段,用于资源分组、选择、关联。所有控制器(Deployment、Service、Job)都依赖Label Selector工作。它必须符合严格格式:key不能超过63字符,只能含字母、数字、-、_、.;value同理。且Label会被频繁查询,过多Label会拖慢etcd性能。
  • Annotation(注解):是K8s的元数据仓库,存任意非索引信息。比如build-date: "2023-10-01",git-commit: "a1b2c3d",contact: "dev-team@company.com"。它不参与任何调度或匹配逻辑,纯粹给人看或给工具解析。

实战教训:曾有个团队把Git提交ID、构建时间、负责人邮箱全塞进Label。结果kubectl get pod --show-labels输出密密麻麻几百行,kubectl get pod -l "git-commit=a1b2c3d"查询极慢。后来全部迁移到Annotation,Label只留app,env,version三个核心维度。

正确用法示例:

metadata: labels: app: nginx # 用于Service选择、Deployment管理 env: prod # 用于Namespace隔离、RBAC授权 annotations: build-time: "2023-10-01T14:23:00Z" git-repo: "https://git.example.com/app/nginx" owner: "backend-team"

提示:kubectl get默认不显示Annotation,要用-o wide或kubectl describe。而kubectl get --show-labels会显示所有Label,是排查Selector匹配问题的第一步。

3.3 ConfigMap与Secret:配置与密钥的“安全传递通道”

很多人以为ConfigMap就是“把配置文件挂进容器”,其实它解决的是配置与镜像解耦的根本问题。传统方式:Dockerfile里COPY config.yml /app/config.yml,改配置就得重新构建镜像、推送仓库、更新部署——慢且易错。ConfigMap让配置变成独立API对象,可热更新、可复用、可版本化。

ConfigMap的两种挂载方式:

  • Volume挂载:将ConfigMap内容以文件形式挂载到容器目录(如/etc/nginx/conf.d/),适合Nginx配置、Spring Boot application.yml;
  • 环境变量注入:将ConfigMap的key映射为容器环境变量(如DB_HOST=10.0.1.100),适合简单参数。

Secret本质是ConfigMap的加密版,但加密仅限etcd存储层(base64编码,非AES加密)。它的真正价值在于:

  • RBAC可单独控制Secret访问权限(kubectl auth can-i get secret -n prod);
  • K8s组件(如imagePullSecret)原生支持Secret类型;
  • 避免密钥硬编码在YAML里(虽然base64可逆,但至少不裸奔)。

安全红线:

  • ❌ 绝对不要在YAML里写password: "my-secret",而要用kubectl create secret generic db-secret --from-literal=password=my-secret;
  • ❌ 不要将Secret挂载为subPath(如subPath: password),因为subPath挂载不会热更新;
  • ✅ 用volumeMounts整体挂载,配合fsGroup让容器进程有读取权限。

验证ConfigMap热更新:

# 创建ConfigMap kubectl create configmap nginx-conf --from-file=nginx.conf=./nginx.conf # 挂载到Deployment kubectl set volume deployment/nginx-deployment --add --name=nginx-conf --mount-path=/etc/nginx/nginx.conf --sub-path=nginx.conf --configmap-name=nginx-conf # 修改ConfigMap内容 kubectl create configmap nginx-conf --from-file=nginx.conf=./new-nginx.conf --dry-run=client -o yaml | kubectl replace -f - # 观察Pod日志:Nginx会自动reload(需配置nginx -s reload)

4. 实操避坑指南:那些文档里不会写的血泪经验

4.1 “kubectl get all”为什么有时看不到你的Pod?

kubectl get all是个便捷命令,但它只查询特定API组的资源:pods,services,deployments,replicasets,statefulsets,daemonsets,jobs,cronjobs。如果你的资源不在这个列表里,它就隐身了。

常见失踪原因:

  • Namespace错位:kubectl get all默认查default Namespace,而你的Pod在prod里。解决方案:kubectl get all -n prod;
  • 资源类型不匹配:你创建的是kind: Pod(非控制器管理的裸Pod),但get all会显示它;如果你创建的是kind: CronJob,它会被包含;但kind: Ingress或kind: PersistentVolumeClaim就不会出现——它们属于networking.k8s.io/v1或storage.k8s.io/v1组,需单独kubectl get ingress或kubectl get pvc;
  • API版本过期:老集群用extensions/v1beta1的Deployment,新kubectl默认查apps/v1,导致get all不显示。解决方案:kubectl api-versions查支持版本,或kubectl get deployments.apps显式指定组。

终极排查法:

# 查看所有API组和资源 kubectl api-resources --namespaced=true | grep -E "(pod|service|deploy)" # 查看某资源在哪个组 kubectl explain pod | head -5 # 显示KIND: Pod, VERSION: v1, GROUP: "" kubectl explain deployment | head -5 # 显示GROUP: apps, VERSION: v1

4.2 Service无法访问?先查这五层

服务不通是最高频问题,按优先级逐层排查:

层级检查命令关键指标常见陷阱
1. Service是否存在且Endpoint非空kubectl get svc nginx-svc
kubectl get endpoints nginx-svc
ENDPOINTS列应有<pod-ip>:<port>selector不匹配、Pod未就绪(kubectl get pod看STATUS)、Pod在错误Namespace
2. Pod自身是否正常kubectl describe pod <pod-name>
kubectl logs <pod-name>
Events里无CrashLoopBackOff、容器端口监听(kubectl exec -it <pod> -- netstat -tuln | grep :80)容器进程未启动、健康检查失败、端口绑定localhost而非0.0.0.0
3. 网络连通性kubectl exec -it <test-pod> -- curl -v http://<service-name>.<ns>.svc.cluster.local:80HTTP 200或连接拒绝(Connection refused)DNS解析失败(nslookup nginx-svc.default.svc.cluster.local)、跨Namespace未用全限定域名
4. kube-proxy状态kubectl get pods -n kube-system | grep kube-proxy
kubectl logs -n kube-system <kube-proxy-pod>
Pod Running、日志无Failed to sync iptableskube-proxy CrashLoop、iptables规则损坏(iptables -t nat -L KUBE-SERVICES查规则)
5. Node网络kubectl get nodes -o wide
ping <node-ip>
Node Ready、能ping通Node NotReady、防火墙拦截NodePort(ufw status)、云安全组未开放

我处理过最诡异的案例:Service Endpoint显示正常,Pod日志一切OK,但从其他Pod curl却超时。最后发现是CNI插件(Calico)的FelixConfiguration里ipv6support: true被误开启,而集群实际是IPv4-only,导致iptables规则生成异常。解决方案:kubectl edit felixconfiguration default关掉IPv6。

4.3 “ImagePullBackOff”不是镜像问题,是认证问题

这个报错90%不是镜像不存在,而是K8s节点无法登录私有仓库。kubectl describe pod xxx里Events会显示:
Failed to pull image "harbor.example.com/proj/app:v1": rpc error: code = Unknown desc = failed to resolve reference "harbor.example.com/proj/app:v1": failed to authorize: failed to fetch anonymous token: unexpected status: 401 Unauthorized

解决方案只有两个:

  • 方案1(推荐):创建imagePullSecret,绑定到ServiceAccount
    kubectl create secret docker-registry regcred \ --docker-server=harbor.example.com \ --docker-username=admin \ --docker-password=xxx \ --docker-email=not@used.com kubectl patch serviceaccount default -p '{"imagePullSecrets": [{"name": "regcred"}]}'
  • 方案2:在Node上手动docker login harbor.example.com(不推荐,维护成本高)

注意:imagePullSecret必须和Pod在同一个Namespace,且ServiceAccount必须显式引用它。很多团队漏掉patch serviceaccount这步,导致Secret创建了但Pod用不上。

4.4 RollingUpdate卡住?看这两个超时参数

Deployment滚动更新失败,最常见的原因是ProgressDeadlineSeconds(默认600秒)和timeoutSeconds(Probe里)冲突。比如:

  • 你设了livenessProbe.initialDelaySeconds=300(容器启动后5分钟才开始健康检查);
  • 但progressDeadlineSeconds=600(10分钟内必须完成滚动);
  • 结果新Pod启动后,前5分钟不接受健康检查,Deployment等不及就标记Progressing=False,停止滚动。

解决方案:

  • 延长deadline:kubectl patch deployment nginx-deployment -p '{"spec":{"progressDeadlineSeconds":1800}}'(30分钟);
  • 优化probe:initialDelaySeconds设小(如30秒),用startupProbe替代(K8s 1.16+),它专为长启动应用设计:
    startupProbe: httpGet: path: /healthz port: 8080 failureThreshold: 30 # 失败30次才判失败(30*10s=5分钟) periodSeconds: 10

4.5 Namespace删除卡在Terminating?手动清理Finalizer

kubectl delete namespace stuck-ns后,kubectl get ns显示Terminating,但永远删不掉。这是因为Namespace里还有资源未清理完,而K8s的垃圾回收器(Garbage Collector)卡住了。

手动解法(谨慎!):

# 查看Namespace详情,找finalizers字段 kubectl get namespace stuck-ns -o json > tmp.json # 编辑tmp.json,将"finalizers": ["kubernetes"] 改为 "finalizers": [] # 强制替换 kubectl replace --raw "/api/v1/namespaces/stuck-ns/finalize" -f ./tmp.json

警告:此操作绕过K8s GC,可能导致残留资源。优先尝试kubectl api-resources --namespaced=true --verbs=list | xargs -n 1 -I {} sh -c "kubectl get {} -n stuck-ns"查出所有资源,逐个删除。

5. 概念延伸:从基础到生产就绪的必经之路

5.1 StatefulSet:当你的应用需要“有身份”的Pod

Deployment适合无状态应用(如Web服务器),但数据库、消息队列、分布式存储需要稳定的网络标识、有序部署、持久化存储。这时StatefulSet登场。

StatefulSet的三大特性:

  • 稳定唯一网络标识:Pod名为<statefulset-name>-0,<statefulset-name>-1,DNS域名固定为<pod-name>.<headless-service>.<ns>.svc.cluster.local(如mysql-0.mysql.default.svc.cluster.local);
  • 有序部署/删除:mysql-0启动成功后,才启mysql-1;删除时倒序(先删mysql-1);
  • 持久化存储绑定:每个Pod独享PVC,即使Pod被删,PVC和PV数据仍在,重建Pod时自动挂载原PVC。

创建StatefulSet必须配Headless Service(clusterIP: None),它不分配ClusterIP,只提供DNS记录,让Pod能互相发现。

5.2 Ingress:集群对外的“七层网关”

Service的NodePort只能暴露单个端口,LoadBalancer成本高。Ingress是K8s的七层路由抽象,通过Ingress Controller(如Nginx Ingress、Traefik)实现:

  • 基于Host和Path路由:example.com/api→ Service A,example.com/web→ Service B;
  • TLS终止:自动加载Secret里的证书;
  • 负载均衡策略:加权轮询、最少连接等。

关键点:Ingress本身只是YAML规则,必须有Ingress Controller(作为DaemonSet或Deployment运行)来监听并生效。没有Controller,Ingress YAML毫无作用。

5.3 NetworkPolicy:微服务间的“防火墙”

默认K8s集群内Pod全通(All-to-All),这在生产环境极度危险。NetworkPolicy是K8s的网络策略引擎,基于Label控制Pod间流量:

apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: db-policy spec: podSelector: matchLabels: app: mysql ingress: - from: - podSelector: matchLabels: app: web ports: - protocol: TCP port: 3306

此策略表示:只允许app=web的Pod访问app=mysql的Pod的3306端口。但注意:NetworkPolicy需CNI插件支持(Calico、Cilium),Flannel默认不支持。

5.4 Helm:K8s的“包管理器”

YAML文件多了,管理就成了噩梦。Helm用Chart(模板包)解决:

  • Chart是预配置的YAML集合(如WordPress Chart含Deployment、Service、Ingress、Secret);
  • helm install wordpress bitnami/wordpress一键部署;
  • helm upgrade热更新配置;
  • helm repo add bitnami https://charts.bitnami.com/bitnami接入公共仓库。

Helm不是必需品,但当你管理10+微服务时,它能减少80%的YAML重复劳动。

6. 我的实操心得:从概念迷雾到肌肉记忆的三步转化

刚学K8s时,我对着kubectl get输出的几十列字段发懵:READY,STATUS,RESTARTS,AGE,哪个代表Pod真正在跑?后来悟到一个笨办法:把每个命令当成“照妖镜”,只照一个维度。

第一步:建立“状态反射弧”。

  • 看kubectl get pod,只盯READY列(如1/1表示1个容器就绪)和STATUS(Running是活着,Pending是没调度,ContainerCreating是拉镜像中,CrashLoopBackOff是容器反复崩溃);
  • 看kubectl get deploy,只盯AVAILABLE(真正在服务的副本数)和UP-TO-DATE(已更新到最新版本的副本数);
  • 看kubectl get svc,只盯CLUSTER-IP(虚拟IP)和EXTERNAL-IP(云LB地址)。

第二步:用describe深挖根因。
get只给快照,describe给病理报告。比如STATUS是Pending,describe pod的Events里会写0/3 nodes are available: 2 node(s) had taint {node-role.kubernetes.io/master: }, 1 node(s) didn't match pod affinity rules.——直接告诉你:节点有污点,或亲和性规则不满足。

第三步:动手破坏,再修复。
在测试环境故意制造故障:

  • kubectl delete pod -l app=nginx(验证Deployment自愈);
  • kubectl scale deployment nginx-deployment --replicas=0(验证副本归零);
  • kubectl edit svc nginx-svc改clusterIP: None(验证Headless Service DNS);
  • kubectl label node <node-name> type=database再kubectl edit deploy加nodeSelector(验证调度)。

每一次“破坏-观察-修复”,都比读十页文档记得牢。K8s不是靠背概念学会的,而是靠在终端里一次次敲命令、看输出、查日志、改配置,把抽象术语变成肌肉记忆。

最后分享一个小技巧:把常用命令做成别名。.bashrc里加:

alias kgp='kubectl get pods' alias kgs='kubectl get services' alias kgn='kubectl get nodes' alias kdf='kubectl describe pod' alias klf='kubectl logs -f'

别小看这几个字母,每天省下30秒,一个月就是15分钟——而这15分钟,足够你多查一次describe,多理解一个字段含义。K8s的门槛不在技术多难,而在你愿不愿意把每个术语,都亲手在终端里跑一遍、看一眼、改一回。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询