Kubernetes核心概念与实战:从Pod到Service的容器编排入门

发布时间:2026/10/15 15:08:06
Kubernetes核心概念与实战:从Pod到Service的容器编排入门 1. 从零开始理解Kubernetes为什么我们需要它如果你刚开始接触容器化部署大概率会遇到这样的场景本地用Docker跑几个服务挺顺畅一旦要把这些服务放到多台机器上问题就来了——容器挂在哪台机器上挂了之后怎么自动重启多个副本之间怎么负载均衡服务之间怎么互相发现这些问题靠手工脚本和Docker Compose已经很难优雅地解决。Kubernetes后面我统一叫它K8s就是冲着这些问题来的。K8s本质上是一个容器编排系统。你可以把它想象成一个大型物流调度中心它不生产货物容器但它负责决定每个货物放在哪个仓库节点、什么时候发车调度、坏了怎么补发自愈、客户怎么找到货服务发现。它把一堆物理机或虚拟机抽象成一个统一的资源池你只需要告诉它“我要跑三个副本的Web服务每个副本需要0.5核CPU和512MB内存”剩下的调度、部署、扩缩容、故障恢复它自己会处理。这篇文章适合谁看如果你已经会用Docker打包镜像、写过Dockerfile、用过docker run或docker-compose但对K8s里那一堆名词——Pod、Deployment、Service、Namespace、ConfigMap——感到头大那这篇内容就是为你准备的。我会用从业者的视角把这些概念一个一个拆开讲清楚它们是什么、为什么存在、实际怎么用以及我踩过哪些坑。不会堆砌官方文档里的定义而是用你能听懂的话和能直接抄的操作来说明。2. 核心概念拆解K8s世界里到底有哪些角色2.1 集群与节点K8s的物理底座K8s集群由两类角色组成控制平面Control Plane和工作节点Node。控制平面是大脑负责决策和调度工作节点是手脚负责实际跑容器。控制平面里最核心的组件是API Server、etcd、Scheduler和Controller Manager。API Server是所有操作的入口你用的kubectl命令最终都是发给它etcd是集群的数据库存着所有资源的状态Scheduler决定Pod去哪个节点Controller Manager负责维持期望状态比如你声明要3个副本少了一个它就补一个。工作节点上跑的是kubelet和kube-proxy。kubelet是节点上的“工头”负责跟控制平面汇报并管理本节点上的容器kube-proxy负责网络规则让Service的流量能正确转发到Pod。这些组件你平时不需要直接操作但理解它们的分工排查问题时能快速定位是哪一层出了毛病。注意控制平面节点通常不跑业务Pod这是默认的污点Taint机制在起作用。如果你在测试环境想省机器可以手动去掉这个限制但生产环境千万别这么干。2.2 PodK8s里最小的调度单元Pod是K8s里你能创建和管理的最小单位。一个Pod里可以有一个或多个容器这些容器共享网络命名空间和存储卷。什么意思同一个Pod里的容器可以用localhost互相访问就像它们在同一台机器上一样。这是Pod最核心的价值——把紧密协作的进程放在一起。但绝大多数场景下一个Pod里只跑一个容器。为什么还要多一层Pod而不直接调度容器因为有些辅助容器需要跟主容器共享网络或文件比如日志收集sidecar、服务网格代理。Pod这层抽象让这些协作模式变得自然。实际写YAML的时候Pod的定义长这样apiVersion: v1 kind: Pod metadata: name: nginx-demo labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80这里有几个点值得注意。apiVersion和kind告诉API Server你要创建什么资源metadata里的labels是后续Service选择Pod的依据spec.containers里定义容器镜像和端口。containerPort其实只是声明不写也不影响容器启动但写上能让其他人一眼看出这个容器监听什么端口。实操心得不要直接创建裸Pod。裸Pod没有自愈能力节点挂了它就没了。生产环境一律用Deployment或StatefulSet来管理Pod。2.3 Deployment与ReplicaSet管理无状态应用的利器Deployment是你最常用的工作负载资源。它管理ReplicaSetReplicaSet管理Pod。为什么要三层因为Deployment负责滚动更新和回滚ReplicaSet负责副本数量Pod负责实际运行。当你更新Deployment的镜像版本时它会创建一个新的ReplicaSet逐步把流量切过去旧ReplicaSet保留着以便回滚。一个典型的Deployment YAMLapiVersion: apps/v1 kind: Deployment metadata: name: web-app spec: replicas: 3 selector: matchLabels: app: web-app template: metadata: labels: app: web-app spec: containers: - name: web image: my-registry/web-app:v1.2.0 resources: requests: cpu: 250m memory: 256Mi limits: cpu: 500m memory: 512Mireplicas: 3表示期望三个副本。selector告诉Deployment它管理哪些Pod必须和template里的labels匹配。resources里的requests是调度依据K8s会找有足够资源的节点limits是硬上限超过会被限制或杀掉。这里有个常见的坑requests和limits设置不合理。如果requests设得太高Pod可能一直Pending调度不上去如果limits设得太低容器会被OOMKilled。我的经验是先设一个保守的requests比如实际用量的70%limits设为requests的1.5到2倍然后根据监控数据慢慢调。2.4 Service让Pod可以被稳定访问Pod的IP是不固定的重建后就变了。Service就是来解决这个问题的——它给一组Pod提供一个固定的虚拟IP和DNS名称。Service通过标签选择器找到后端Pod然后做负载均衡。四种Service类型你需要知道类型用途特点ClusterIP集群内部访问默认类型只有集群内能访问NodePort通过节点端口暴露每个节点开一个端口范围30000-32767LoadBalancer云厂商负载均衡需要云平台支持自动创建外部LBExternalName映射到外部DNS相当于CNAME记录最常用的是ClusterIP。比如你有一个前端Deployment和一个后端Deployment前端要访问后端就创建一个ClusterIP Service前端用http://backend-service:8080就能访问不用关心后端Pod的IP。apiVersion: v1 kind: Service metadata: name: backend-service spec: selector: app: backend ports: - protocol: TCP port: 8080 targetPort: 8080 type: ClusterIPport是Service暴露的端口targetPort是Pod实际监听的端口。这两个可以不一样比如port: 80、targetPort: 8080这样外部访问80内部转发到8080。注意Service的负载均衡是四层的TCP/UDP不支持HTTP层面的路由。如果需要根据URL路径或域名转发得用Ingress。2.5 Namespace集群里的逻辑隔离Namespace用来把集群资源划分成不同的逻辑组。默认有default、kube-system、kube-public、kube-node-lease几个。你可以给不同团队、不同环境开发、测试、生产各建一个Namespace资源名字在Namespace内唯一跨Namespace可以重名。kubectl create namespace dev kubectl get pods -n devNamespace不是网络隔离不同Namespace的Pod默认可以互相访问。它更多是管理边界和资源配额的边界。你可以给Namespace设置ResourceQuota限制这个空间里最多能用多少CPU和内存。2.6 ConfigMap与Secret配置与敏感信息管理把配置硬编码在镜像里是大忌。ConfigMap用来存非敏感的配置比如配置文件、环境变量。Secret用来存密码、Token、证书这些敏感信息。apiVersion: v1 kind: ConfigMap metadata: name: app-config data: app.properties: | log.levelINFO timeout30sPod里可以这样引用spec: containers: - name: app image: my-app:v1 envFrom: - configMapRef: name: app-configSecret的用法类似但数据需要base64编码。不过要注意Secret默认只是base64编码不是加密。如果集群的etcd没有开加密Secret的内容在etcd里是明文可读的。生产环境要开启etcd加密并用RBAC限制Secret的访问权限。3. 实操过程从零部署一个完整应用3.1 环境准备与集群搭建如果你还没有K8s集群最省事的本地方案是用minikube或kind。minikube会在本地起一个虚拟机或容器kind则是用Docker容器模拟节点。我平时快速验证用kind因为它启动快、资源占用小。kind create cluster --name demo-cluster kubectl cluster-info kubectl get nodeskubectl是操作K8s的命令行工具你需要配置好kubeconfig才能连上集群。kind创建集群后会自动帮你配好。kubectl get nodes应该能看到一个节点状态是Ready。如果你是在云上各家的托管K8s服务创建集群后也会给你kubeconfig文件放到~/.kube/config就能用。3.2 部署第一个应用Nginx我们来部署一个Nginx并通过Service暴露它。先写DeploymentapiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy namespace: default spec: replicas: 2 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80 resources: requests: cpu: 100m memory: 128Mi limits: cpu: 200m memory: 256Mi保存为nginx-deploy.yaml然后kubectl apply -f nginx-deploy.yaml kubectl get deployments kubectl get pods -l appnginx你应该看到两个Pod在Running状态。如果Pod一直Pending用kubectl describe pod pod-name看Events通常是资源不够或者镜像拉不下来。接着创建ServiceapiVersion: v1 kind: Service metadata: name: nginx-svc spec: selector: app: nginx ports: - port: 80 targetPort: 80 type: NodePortkubectl apply -f nginx-svc.yaml kubectl get svc nginx-svcNodePort类型会分配一个30000-32767的端口你可以用节点IP:NodePort访问Nginx。如果是kind集群需要做端口映射或者用kubectl port-forwardkubectl port-forward svc/nginx-svc 8080:80然后浏览器访问http://localhost:8080就能看到Nginx欢迎页。3.3 滚动更新与回滚实操现在我们把Nginx版本从1.25升到1.26体验一下滚动更新kubectl set image deployment/nginx-deploy nginxnginx:1.26 kubectl rollout status deployment/nginx-deployrollout status会实时显示更新进度。K8s默认的更新策略是RollingUpdatemaxSurge25%、maxUnavailable25%意思是先多起25%的新Pod再逐步停掉旧Pod保证服务不中断。如果新版本有问题回滚kubectl rollout undo deployment/nginx-deploy kubectl rollout history deployment/nginx-deployrollout history能看到历史版本undo默认回滚到上一个版本也可以加--to-revision2回滚到指定版本。实操心得滚动更新时一定要配好readinessProbe。否则新Pod还没准备好就被加进Service流量打进去会报错。readinessProbe的initialDelaySeconds和periodSeconds要根据应用启动时间调启动慢的应用给足时间。3.4 配置管理实操ConfigMap挂载为文件假设你的应用需要读一个config.yaml可以这样创建ConfigMapkubectl create configmap app-config --from-fileconfig.yaml然后在Deployment里挂载spec: containers: - name: app image: my-app:v1 volumeMounts: - name: config-volume mountPath: /etc/app/config.yaml subPath: config.yaml volumes: - name: config-volume configMap: name: app-configsubPath的作用是只挂载这一个文件而不是覆盖整个目录。如果不写subPathmountPath目录下的其他文件会被隐藏这是个很容易踩的坑。ConfigMap更新后挂载的文件不会自动更新除非用subPath之外的挂载方式且kubelet同步周期到了才会更新。如果应用不支持热加载需要重启Pod才能生效。我一般用kubectl rollout restart deployment/name来触发滚动重启。4. 常见问题与排查技巧实录4.1 Pod一直Pending怎么办Pending说明Pod没有被调度到任何节点。先用kubectl describe pod pod-name看Events。常见原因有资源不足节点上没有满足requests的CPU或内存。要么调低requests要么加节点。节点选择器不匹配nodeSelector或affinity指定了不存在的标签。污点与容忍节点有TaintPod没有对应的Toleration。PVC未绑定如果Pod用了PersistentVolumeClaimPVC没绑上也会Pending。4.2 Pod一直CrashLoopBackOff这个状态说明容器启动了又退出反复循环。排查步骤kubectl logs pod-name --previous看上一次退出的日志。kubectl describe pod pod-name看Events和容器的退出码。常见原因启动命令写错、配置文件缺失、依赖服务连不上、内存超限被OOMKilled。如果是OOMKilleddescribe里会看到Reason: OOMKilled这时候要调大limits.memory或者优化应用内存占用。4.3 Service访问不通先确认Service的Endpoints有没有后端kubectl get endpoints service-name如果Endpoints是空的说明Service的selector没匹配到任何Pod。检查Pod的labels和Service的selector是否一致。如果Endpoints有IP但访问不通检查targetPort是否和容器实际监听端口一致。网络插件是否正常kubectl get pods -n kube-system看网络组件。网络策略NetworkPolicy是否阻止了流量。4.4 镜像拉取失败kubectl describe pod里会看到Failed to pull image。常见原因镜像名或tag写错。私有仓库需要imagePullSecrets。节点无法访问镜像仓库网络问题或仓库地址不对。创建imagePullSecretskubectl create secret docker-registry regcred \ --docker-serverregistry \ --docker-usernameuser \ --docker-passwordpass然后在Pod spec里加imagePullSecrets: - name: regcred。4.5 常见问题速查表现象可能原因排查命令Pod Pending资源不足/污点/亲和性kubectl describe podCrashLoopBackOff启动失败/OOMkubectl logs --previousImagePullBackOff镜像名错/无权限kubectl describe podService无Endpointsselector不匹配kubectl get endpointsPVC Pending无可用PV/StorageClasskubectl describe pvc节点NotReadykubelet异常/网络插件kubectl describe node避坑技巧养成用kubectl get events --sort-by.metadata.creationTimestamp的习惯按时间排序看事件能快速定位最近发生了什么。5. 资源限制与调度策略让Pod去该去的地方5.1 requests与limits的底层逻辑前面提过requests和limits这里展开说。requests是调度器用来找节点的依据节点上所有Pod的requests总和不能超过节点可分配资源。limits是cgroup层面的硬限制CPU超了会被 throttled内存超了会被OOMKilled。CPU是可压缩资源超了只是变慢内存是不可压缩资源超了直接杀。所以内存的limits要特别小心设得太低应用容易崩设得太高又浪费。一个经验公式requests 应用稳定运行时的P95用量limits 峰值用量的1.2倍。当然这需要你有监控数据没有的话先拍脑袋设再根据实际表现调。5.2 节点亲和性与反亲和性默认调度器会尽量把Pod分散到不同节点但不会强制。如果你希望某些Pod必须在一起比如缓存和计算或者必须分开比如三个副本不要在同一节点就要用亲和性规则。节点亲和性nodeAffinity指定Pod能调度到哪些节点spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: disktype operator: In values: - ssdPod反亲和性podAntiAffinity让同一应用的多个副本不要挤在一个节点spec: affinity: podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchLabels: app: web-app topologyKey: kubernetes.io/hostnamerequired是硬性要求不满足就不调度preferred是软性偏好尽量满足但不强求。生产环境建议用preferred避免节点资源碎片导致Pod无法调度。5.3 污点与容忍节点拒绝Pod的机制污点Taint是节点给Pod设的门槛容忍Toleration是Pod的通行证。比如你有一批GPU节点只想让GPU任务跑上去就给节点打污点kubectl taint nodes gpu-node-1 gputrue:NoSchedule然后在GPU任务的Pod里加容忍spec: tolerations: - key: gpu operator: Equal value: true effect: NoScheduleNoSchedule是不调度新PodNoExecute还会驱逐已存在的Pod。控制平面节点默认有node-role.kubernetes.io/control-plane:NoSchedule污点所以业务Pod不会跑上去。6. 持久化存储数据不能丢6.1 Volume、PV与PVC的关系容器里的文件是临时的容器一重启就没了。要持久化数据需要Volume。K8s的存储抽象有三层Volume、PersistentVolumePV、PersistentVolumeClaimPVC。PV是集群级别的存储资源由管理员创建或StorageClass动态供给。PVC是用户对存储的请求声明需要多大空间、什么访问模式。Pod里引用PVCK8s负责把PVC绑定到合适的PV。访问模式有三种ReadWriteOnceRWO只能被一个节点读写。ReadOnlyManyROX多个节点只读。ReadWriteManyRWX多个节点读写需要特定存储支持如NFS、CephFS。6.2 动态供给实操大多数云环境都配了默认StorageClass你只需要创建PVCapiVersion: v1 kind: PersistentVolumeClaim metadata: name: app-data spec: accessModes: - ReadWriteOnce resources: requests: storage: 10Gi然后在Pod里挂载spec: containers: - name: app image: my-app:v1 volumeMounts: - name: data mountPath: /var/lib/app volumes: - name: data persistentVolumeClaim: claimName: app-datakubectl get pvc应该看到状态是Bound。如果一直Pending检查StorageClass是否存在、是否设置为默认。注意PVC删除后底层PV是否删除取决于StorageClass的reclaimPolicy。默认是Delete数据会一起删掉。如果数据重要改成Retain删PVC后PV保留需要手动清理。7. 健康检查让K8s知道你的应用活得好不好7.1 livenessProbe与readinessProbe的区别livenessProbe是存活探针失败会重启容器。readinessProbe是就绪探针失败会把Pod从Service后端摘掉但不重启。这两个探针的用途完全不同很多人会搞混。livenessProbe用来检测死锁或卡死的情况比如应用进程还在但已经无法响应。readinessProbe用来检测应用是否准备好接收流量比如启动时需要加载大量数据。livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 10 periodSeconds: 5 failureThreshold: 3 readinessProbe: httpGet: path: /ready port: 8080 initialDelaySeconds: 5 periodSeconds: 3initialDelaySeconds是容器启动后等多久开始探测periodSeconds是探测间隔failureThreshold是连续失败几次才判定失败。7.2 探针配置的常见坑第一个坑livenessProbe的initialDelaySeconds设得太短。应用还没启动完探针就失败容器被反复重启永远起不来。启动慢的应用要么调大这个值要么用startupProbe。startupProbe是专门给慢启动应用用的它成功之前liveness和readiness都不会执行startupProbe: httpGet: path: /healthz port: 8080 failureThreshold: 30 periodSeconds: 10这样最多给应用300秒启动时间启动完成后才交给liveness和readiness。第二个坑readinessProbe的路径返回了200但实际依赖还没就绪。探针要检查关键依赖比如数据库连接、缓存连接不能只返回一个静态的OK。第三个坑探针超时时间太短。默认timeoutSeconds是1秒如果应用响应慢探针会误判。根据实际响应时间调整一般设3到5秒。8. 日常运维命令速查与经验总结8.1 高频kubectl命令# 查看资源 kubectl get pods -o wide kubectl get all -n namespace kubectl describe pod name kubectl logs pod -c container -f kubectl exec -it pod -- /bin/sh # 调试 kubectl port-forward svc/name 8080:80 kubectl top pod kubectl get events --sort-by.metadata.creationTimestamp # 操作 kubectl apply -f file kubectl delete -f file kubectl scale deployment name --replicas5 kubectl rollout restart deployment name kubectl rollout undo deployment namekubectl get pods -o wide能看到Pod在哪个节点、IP是多少。kubectl top pod需要metrics-server支持能看到CPU和内存实际用量跟requests对比就知道资源设得合不合理。8.2 我踩过的几个印象深刻的坑第一个坑ConfigMap用subPath挂载后不更新。当时改完ConfigMap以为Pod会自动加载新配置结果等了半天没反应。后来才知道subPath挂载的文件不会随ConfigMap更新而更新必须重启Pod。现在我都用kubectl rollout restart来触发。第二个坑Service的targetPort写错。容器监听8080Service的targetPort写了80结果一直连不上。kubectl get endpoints能看到后端IP但端口不对。这种问题用kubectl exec进Pod里curl localhost:8080确认端口再对照Service配置就能发现。第三个坑资源limits设得太低导致频繁OOM。一个Java应用默认堆内存就占了容器内存的大半limits设了512MiJVM一启动就超了。后来把limits调到1Gi并加了-Xmx参数限制JVM堆大小才稳定下来。Java应用一定要显式设置堆内存不能依赖默认值。第四个坑节点磁盘满了导致Pod被驱逐。K8s默认磁盘压力驱逐阈值是85%节点磁盘用到85%就开始驱逐Pod。日志没配轮转几天就把磁盘写满了。后来配了logrotate并给节点加了磁盘监控。8.3 给新手的几个建议先在小集群上把概念玩熟别一上来就上生产。用minikube或kind搭个本地环境把Pod、Deployment、Service、ConfigMap、Secret、PVC都手动创建一遍比看十篇文档都管用。YAML别手写用kubectl create生成再改。比如kubectl create deployment nginx --imagenginx --dry-runclient -o yaml会输出一个Deployment的YAML模板改改就能用避免格式错误。养成看Events的习惯。kubectl describe和kubectl get events能解决80%的常见问题。很多报错信息其实写得很清楚只是新手不知道去哪看。资源限制一定要设。不设limits的Pod在节点资源紧张时最先被驱逐而且可能把节点拖垮。哪怕先拍脑袋设一个也比不设强。最后再分享一个小技巧用kubectl explain查字段说明。比如kubectl explain deployment.spec.template.spec.containers会告诉你这个字段支持哪些子字段、类型是什么。不用每次都去翻官方文档命令行里就能查。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询