它解决什么问题
Kubernetes,简称 K8s,是用来管理一组机器上容器的系统:程序跑在哪里、跑几份、挂了怎么恢复、更新时怎么切换,都交给它协调。
容器把程序和运行环境打包起来,K8s 负责把这些容器组织成一个持续运行的服务。
核心思想是声明式管理:告诉系统「我想要什么状态」,系统持续把实际状态往这个目标上调整。
比如声明「这个服务运行 3 个副本」,少了就补,多了就减。这种不断比较期望与现实、再做修正的过程,叫调谐(Reconciliation)。
集群由什么组成
集群里的机器叫 Node,可以是物理机,也可以是虚拟机。组件分成控制面和节点侧两部分。
控制面负责管理集群:
- kube-apiserver:统一入口,kubectl 和各组件通过 API 读取、修改资源
- etcd:保存集群状态的键值数据库,存的是资源配置和状态,不是应用的业务数据库
- kube-scheduler:给还没安排节点的 Pod 选择合适的 Node
- kube-controller-manager:运行各种控制器,持续检查并修正资源状态
节点侧负责执行:
- kubelet:根据分配到本节点的 Pod 配置,协调容器运行并上报状态
- 容器运行时:比如 containerd,通过 CRI 接口与 kubelet 配合,真正创建和运行容器
- 网络组件:通常由 CNI 插件实现 Pod 联网,Service 转发通常由 kube-proxy 或网络插件提供的替代实现负责
记住分工:API 接收请求,控制器补齐状态,调度器选机器,kubelet 让容器在机器上跑起来。
Pod:最小调度单位
Pod 是 K8s 创建和调度的最小单位,一个 Pod 可以包含一个或多个紧密协作的容器。
- 同一个 Pod 里的容器总在同一个 Node 上
- 它们共享网络空间,可以通过 localhost 通信,也要避免监听端口冲突
- 它们可以通过挂载同一个 Volume 共享文件,但并不自动共享各自的根文件系统
- Pod 是可替换的:被删除后,控制器可以创建新的 Pod,新 Pod 的名字和 IP 都可能变化
容器崩溃时,kubelet 可以根据重启策略在原 Pod 内重启容器;Pod 被删除或节点故障时,通常由控制器创建替代 Pod。不是把原来的 Pod 搬到另一台机器上。
工作负载:谁来管理 Pod
一般不会直接创建一个裸 Pod,而是声明更高层的工作负载:
- Deployment:管理通常可互相替代的无状态副本,支持滚动更新和回滚
- StatefulSet:给 Pod 提供稳定的身份和持久卷关联,常用于有状态服务;数据库复制和容灾仍需要应用自己实现
- DaemonSet:让每个符合条件的节点运行一个 Pod,适合日志采集、监控和网络代理
- Job:执行一次任务,追踪任务是否成功完成
- CronJob:按时间计划创建 Job
Deployment 不直接管理 Pod,中间还有 ReplicaSet:
Deployment → ReplicaSet → Pod
Deployment 管版本和发布过程,ReplicaSet 维持指定数量的 Pod。更新 Pod 模板时,Deployment 创建新的 ReplicaSet,再逐步扩新缩旧。
一次部署是怎么发生的
执行 kubectl apply -f app.yaml 之后:
- kubectl 把资源声明提交给 API Server,经过认证、授权和准入等处理后保存到 etcd
- Deployment 控制器发现目标配置,创建或调整 ReplicaSet
- ReplicaSet 控制器创建缺少的 Pod
- Scheduler 根据资源需求和调度约束,为 Pod 选择 Node
- 对应节点的 kubelet 协调运行时、网络和存储组件,准备环境并启动容器
- Pod 就绪后,通常会被纳入对应 Service 的可用后端,接收流量
这些组件通过 API 观察变化、分别调谐,不是一次同步调用就把所有事情做完。所以 apply 成功只说明声明被接受,不代表应用已经可用。
网络:怎么找到服务
Pod IP 会变化,所以客户端通常不直接依赖 Pod IP,而是访问 Service。
Service 给一组后端提供稳定的访问入口,常见类型有:
- ClusterIP:默认类型,提供集群内部可访问的虚拟 IP
- NodePort:在节点上开放端口,把请求转发给后端
- LoadBalancer:对接云平台或其他负载均衡实现,提供外部入口;需要对应基础设施支持
Label 是资源上的键值标签,Selector 是按标签筛选资源的条件。Service 通常通过 Selector 找 Pod,由 EndpointSlice 记录后端地址和就绪情况。
同一 Namespace 内通常可以直接用 Service 名访问,比如 http://web;跨 Namespace 可以用 web.demo,分别表示服务名和命名空间名。名字解析通常由集群中的 CoreDNS 提供。
HTTP/HTTPS 的域名、路径路由通常通过 Ingress 或 Gateway API 描述,需要安装相应的控制器或实现,光创建规则不会自动产生入口。
几个容易混淆的端口:
- containerPort:声明容器预期监听的端口,不会让应用自动监听,也不会自动对外开放
- Service 的 port:客户端访问 Service 时使用的端口
- Service 的 targetPort:流量最终发往 Pod 的端口
- nodePort:使用 NodePort 时,节点上对外提供的端口
配置、存储与权限
- Namespace:给资源分组,适合区分团队和环境;它本身不是完整的网络或安全隔离边界
- ConfigMap:保存普通配置,可以作为环境变量或文件提供给容器
- Secret:保存密码、令牌等敏感配置;清单里的 base64 是编码,不是加密
- Volume:给容器挂载存储;比如 emptyDir 可以跨容器重启保留,但随 Pod 删除而消失
- PV / PVC:PV 表示存储资源,PVC 表示应用对存储的申请,可以通过 StorageClass 动态供应
- ServiceAccount / RBAC:前者表示工作负载使用的身份,后者规定这个身份能对哪些资源执行哪些操作
持久卷可以让数据独立于 Pod 生命周期存在,但实际能挂载到哪些节点、删除后是否保留,取决于存储类型、访问模式和回收策略。
ConfigMap 或 Secret 作为环境变量注入时,更新资源不会直接改变已有进程的环境变量,通常需要重建 Pod。
资源与调度
容器的资源配置里,最重要的是 requests 和 limits:
- requests:声明资源需求,调度器主要按它判断节点的可分配资源是否足够
- limits:限制容器的资源使用;CPU 达到限制通常会被节流,内存超限可能触发 OOM Kill
CPU 的 1000m 等于 1 个 CPU 单位,100m 就是 0.1 个;内存常用 Mi、Gi。
requests 不是使用上限,limits 也不会凭空变出资源。节点当前 CPU 很空闲,不代表还能调度一个 requests 很大的 Pod,因为已分配的资源需求可能已经占满。
其他常见调度条件:
- nodeSelector / 节点亲和性:希望或要求 Pod 放到某类节点
- Pod 亲和性 / 反亲和性:希望或要求某些 Pod 靠近或分开
- 污点与容忍:节点用污点排斥某些 Pod,容忍表示允许接受对应污点,不保证一定调度到那里
GPU 通常需要驱动和设备插件,以 nvidia.com/gpu 等扩展资源暴露给 K8s。默认申请的是设备数量,并不是「申请多少 GB 显存」;切分和共享需要额外机制。
健康检查与扩缩容
三个探针分别回答三个问题:
- startupProbe:应用启动完成了吗?配置后,在它成功前不会执行存活和就绪探针;连续失败达到阈值会触发容器重启处理
- readinessProbe:现在能接流量吗?失败后通常会从 Service 的就绪后端中移除,但不会因此重启容器
- livenessProbe:应用是否失去正常运行能力,需要重启?连续失败达到阈值后,kubelet 会终止容器,并按重启策略处理
进程在运行,不等于服务已就绪;服务暂时不能接流量,也不一定应该重启。
扩缩容也分两层:
- HPA:根据 CPU、内存或自定义指标调整工作负载副本数,需要相应的指标服务;按 CPU 利用率计算时,还需要对应容器设置 CPU requests
- 节点自动扩缩容:增加或减少集群里的机器,需要云平台或对应基础设施支持
Pod 变多不代表机器也会变多。资源不够时,新 Pod 会 Pending,等待有节点满足条件。
一个最小部署示例
下面声明 2 个 Nginx 副本,并用一个集群内部的 Service 访问它们。保存为 app.yaml,在已配置好 kubectl 的集群上执行。
apiVersion: apps/v1
kind: Deployment
metadata:
name: web
spec:
replicas: 2
selector:
matchLabels:
app: web
template:
metadata:
labels:
app: web
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 64Mi
limits:
cpu: 500m
memory: 128Mi
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 2
periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
name: web
spec:
selector:
app: web
ports:
- port: 80
targetPort: 80
Deployment 的 Selector、Pod 模板上的 Label、Service 的 Selector,要能对应起来。这里连接它们的就是 app: web。
# 创建独立的示例命名空间并部署
kubectl create namespace k8s-demo
kubectl apply -n k8s-demo -f app.yaml
# 等待部署就绪,查看副本和服务
kubectl rollout status deployment/web -n k8s-demo
kubectl get deployments,pods,services -n k8s-demo
# 将本机端口转发到服务,保持这个命令运行
kubectl port-forward -n k8s-demo service/web 8080:80
然后在运行 kubectl 的机器上访问 http://localhost:8080。这个端口转发适合临时调试,不是正式的对外入口。实际发布时通常固定镜像版本或 digest,避免可变标签带来版本漂移。
常用排障思路
先看状态,再看事件,再看日志,最后检查应用内部:
# Pod 在哪个节点、是什么状态
kubectl get pods -n k8s-demo -o wide
# 把 <pod-name> 替换为实际 Pod 名称
kubectl describe pod <pod-name> -n k8s-demo
kubectl logs <pod-name> -n k8s-demo -c nginx
# 查看同一 Pod 中上一次退出的容器日志
kubectl logs <pod-name> -n k8s-demo -c nginx --previous
# 查看命名空间中的近期事件
kubectl get events -n k8s-demo --sort-by=.metadata.creationTimestamp
# 检查服务后端是否生成,以及后端的就绪状态
kubectl get endpointslices -n k8s-demo -l kubernetes.io/service-name=web -o yaml
# 进入容器检查,需要镜像中存在对应 shell
kubectl exec -it <pod-name> -n k8s-demo -c nginx -- /bin/sh
几种常见现象:
- Pending:可能没有节点满足资源或调度条件,也可能在等待存储等启动准备,先看事件
- ImagePullBackOff:镜像拉取失败,检查镜像地址、仓库凭据和网络
- CrashLoopBackOff:容器反复退出,系统正在退避重试,重点看退出原因和
--previous日志 - OOMKilled:容器因内存问题被终止,检查内存限制、应用峰值和节点内存情况
- Running 但不 Ready:进程已经启动,但就绪条件未满足,检查探针和应用依赖
- Service 访问不通:按 Selector、EndpointSlice、targetPort、应用监听地址、网络策略的顺序检查
把 K8s 串起来看:工作负载声明要跑什么,调度器决定跑在哪里,节点负责运行,Service 负责找到它,控制器持续让现实接近期望。