运维常见题-K8s容器(一)
DeepSeek V4 Flash Vision Exp 辅助生成。若您在阅读中发现存疑或矛盾之处,欢迎反馈讨论,笔者将及时核实与修正。🤔 K8s 有哪些应用场景?
K8s(Kubernetes)是“容器编排平台”,核心价值是把成百上千个应用容器自动地、有秩序地管理起来——你告诉它“我要跑多少个什么样的容器”,它负责调度到哪台机器、挂了自动重启、流量大了自动扩容。核心本质:“运维自动化 + 平台化”——让“部署、扩缩容、自愈、发布”这些重复劳动变成声明式配置交给系统。核心要解决的问题:容器多了怎么管?
- 单个容器用
docker run手动起就行;但生产是几十上百个容器(微服务),手动管根本忙不过来——谁跑哪台、谁挂了、流量来了加几个、版本怎么升级。K8s就是这套“自动化管家”,把容器的生命周期全管起来。
- 单个容器用
主要应用场景(按用途分)
- 微服务部署:把一个大系统拆成多个小服务,每个服务是独立容器,
K8s统一调度这些服务(这是最主流、最核心的场景) - 弹性伸缩:流量高峰期自动加容器(扩容),低谷自动减(缩容),业务量波动不用人工干预
- 故障自愈:容器挂了/机器宕了,
K8s自动重启/换机器,保证服务不中断(高可用) - 滚动更新/灰度发布:升级版本时不中断服务,一批批替换旧容器,出问题能回滚
- 批量任务:一次性计算任务(如数据批处理、定时任务
CronJob),跑完即收 - 有状态应用:数据库、缓存、消息队列等需要持久化数据的应用(
StatefulSet) - 混合云/多云:同一套资源清单可跨云部署,但需按目标云适配存储/网络(
CNI)/负载均衡等——不是“一键自由搬迁”,而是“可迁移 + 需适配”
- 微服务部署:把一个大系统拆成多个小服务,每个服务是独立容器,
一句话理解
- 想象一个“写字楼物业”:你租了多少间办公室(容器),物业负责水电、清洁、维修、房间分配——坏了马上修,人多了加房间。
K8s就是这个“容器物业”,你只管说“要 5 间房跑我的应用”,物业自动搞定。
- 想象一个“写字楼物业”:你租了多少间办公室(容器),物业负责水电、清洁、维修、房间分配——坏了马上修,人多了加房间。
协助记忆
K8s像“酒店前台 + 客房管家”——你订房时(提交配置)说“要 5 间标间、能加班”,前台自动给你分配楼层(调度到节点)、房卡(网络)、保洁(自愈);客人走了自动打扫(回收),临时加人要加房(扩容)。你只需“下订房单”,不用操心房间怎么分。- 口诀:“容器多了要人管,K8s 是自动管家——调度、自愈、扩容、升级全交给它”。
进阶思考
- 为什么要用 K8s,直接用 Docker 不行吗?
Docker解决“单个容器怎么跑”,K8s解决“很多容器怎么协同”。单机几个容器用docker-compose够;但上百容器跨多台机器、要高可用/弹性/灰度,docker做不到,需要K8s这样的编排平台。Docker是“单间”,K8s是“整栋楼的智能管理系统”。
- 微服务和 K8s 是什么关系?
- 微服务是“架构风格”(把系统拆成小服务),
K8s是“跑微服务的基础设施”——微服务拆出来的每个服务都是容器,K8s负责调度、服务发现、负载均衡它们。所以微服务 + 容器 + K8s 是常见组合(微服务是思想、容器是载体、K8s 是管理平台)。
- 微服务是“架构风格”(把系统拆成小服务),
- 为什么要用 K8s,直接用 Docker 不行吗?
扩展信息
- 名字由来:
Kubernetes希腊语“舵手”(掌舵人),K8s是缩写(K+ 中间 8 个字母ubernete+s)。 - 生态地位:
K8s是CNCF(云原生计算基金会)的旗舰项目,已成为容器编排的事实标准,几乎所有云厂商(阿里ACK、腾讯TKE、华为CCE、AWSEKS)都基于它。 - 云原生概念:
K8s是“云原生”(Cloud Native)的核心——“用容器打包、K8s 调度、微服务拆分、自动扩缩”,是现代化应用的标准形态。 - 学习路线建议:先理解“容器/镜像/Pod”概念 → 再学常用资源(
Deployment/Service)→ 深入调度/存储/网络 → 最后Operator/服务网格等进阶。
- 名字由来:
🤔 简述 K8s 核心组件及作用?
K8s是“主从架构”:一组控制节点(Master)管全局,一组工作节点(Node)干具体活。控制节点上的核心组件:API Server(总入口,所有操作的前台)、etcd(集群的数据库)、Scheduler(调度器:决定容器跑哪台机器)、Controller Manager(控制器:盯着实际状态符合期望);工作节点上的:kubelet(节点管家,管本机容器)、kube-proxy(网络代理)、Container Runtime(容器运行时)。核心:“主控脑 + 干活腿”。控制节点(
Master组件,集群的大脑)API Server(kube-apiserver):集群唯一入口,所有操作(创建/查询/删除)都要经过它——像酒店“总前台”,你的一切请求都先到它这,它校验、记账再给内部。etcd:集群的分布式数据库,存所有配置和状态(“哪个 Pod 在哪台机器、什么状态”)——像酒店的“总账本”,全集群的“真相”都在这里。Scheduler(kube-scheduler):调度器,决定“新 Pod 放哪台机器”——像酒店前台“分配房间”:看哪层空、哪个房间合适,把 Pod 派到合适的节点。Controller Manager(kube-controller-manager):控制器集合,持续对比“期望状态 vs 实际状态”,发现不一致就修复——像酒店“巡楼经理”:看到房间该有 5 个人只有 3 个,就补上。
工作节点(
Node组件,集群的腿)kubelet:每个节点上的“节点管家”,负责启动/监控本节点的容器 Pod,定期向API Server汇报本机状态——像每层楼的“保洁员”,管这一层的房间(Pod)死活。kube-proxy:网络代理,监听并为Service(ClusterIP,由API Server创建服务时分配)编程转发规则(iptables/IPVS)实现流量转发与负载均衡(把请求正确转发到后端Pod)——像每层楼的“传菜员”;集群内服务发现(按名字找到服务)主要由CoreDNS/Service承担。Container Runtime(容器运行时):真正跑容器的引擎(containerd/docker/CRI-O)——像“施工队”,按kubelet的指示真正把容器跑起来。
补充组件(易问)
kubectl:命令行工具(你操作集群用的),像“遥控器”,但kubectl是客户端工具、不是集群组件。DNS(CoreDNS):集群内服务发现(按名字找到服务),像“大楼内部分机表”。
一句话理解
Master像写字楼管理处(前台收单API Server、账本etcd、分房Scheduler、巡逻Controller),Node像各个楼层(保洁kubelet、分信kube-proxy、施工runtime)。你通过“游客”(kubectl)向前台下单,管理处协调各楼层干活。
协助记忆
K8s集群 = “大饭店”——API Server是前台(客人都先到这)、etcd是订房总账(记录谁住哪)、Scheduler是大堂经理(安排客人去哪间房)、Controller Manager是巡楼主管(检查每间房是否按要求到位、缺就补)、kubelet是服务员(管一个楼层房间收拾)、kube-proxy是传菜员(把点餐正确送到对应桌)、runtime是厨师(真正做菜/跑容器)。- 口诀:“主控管全局、节点干干活——前台(API)、账本(etcd)、分房(Scheduler)、巡逻(Controller),节点上管家(kubelet)、传信(proxy)、做饭(runtime)”。
进阶思考
- 为什么所有操作都走
API Server(它是单点瓶颈吗)?API Server是“统一入口 + 校验 + 记账”,好处是一致性(所有操作有记录可审计)、安全(统一认证授权)。它本身可以横向扩展(多副本负载均衡),不是瓶颈;真正全集群数据在etcd(也要etcd集群化高可用)。
etcd挂了集群会怎样?etcd是“总账本”,它挂了集群无法读写状态(API Server也访问不了),集群进入“只读/半瘫”状态——现有 Pod 继续跑(kubelet本地维持),但不能新建/变更/调度。所以etcd必须集群化(奇数节点)保证高可用(这是 K8s 高可用最关键的依赖)。
- 为什么所有操作都走
扩展信息
- 组件分类记忆:
Master管集群(API Server/etcd/Scheduler/Controller),Node管单机(kubelet/kube-proxy/runtime)——“主控管全局、节点管单机”。 kubectl的作用:你操作K8s主要用kubectl(如kubectl get pods看 Pod、kubectl create创建资源),实际都是发给API Server执行的。- 参考:
kube-scheduler/kube-controller-manager(Master组件多副本高可用)、kubelet/kube-proxy(Node组件)、CoreDNS(集群DNS)。
- 组件分类记忆:
🤔 K8s 集群如何实现高可用?
K8s高可用分两层:控制面(Master)高可用保证“集群能正常调度/管理”,工作负载高可用保证“应用不中断”。控制面高可用靠“多副本控制节点 +etcd集群 + 负载均衡”;工作负载高可用靠“多副本Pod+ 自动调度 + 故障自愈”。核心:“主控多副本、数据多节点、应用多实例”。控制面高可用(
Master组件不单点)API Server多副本:部署 3 个(或更多)kube-apiserver,前面用负载均衡(LB)分发请求——任一API Server挂,LB自动切到其他,集群管理入口不中断。etcd集群化:etcd部署奇数节点(3/5 个)做集群,用多数派(Quorum)保证一致性——挂 1 个etcd(3 节点)集群还正常,保证“总账本”永远可读可写。这是控制面高可用的最关键一环。Scheduler/Controller多实例:kube-scheduler/kube-controller-manager也可以多副本(通过leader election选主,同一时刻只有一个干活,挂了自动接管)——避免单点。
工作负载高可用(应用不中断)
- 多副本
Pod:应用(Deployment)跑多个Pod副本(如 3 个)分布在不同节点——一个Pod/节点挂了,还有 2 个顶着服务,不中断。 - 副本跨节点分散:默认调度器对同类副本倾向分散(默认评分策略
LeastAllocated倾向占用量低),但不严格保证跨节点错开——要强约束需显式配置PodAntiAffinity/TopologySpreadConstraints(NodeResourcesFit既是资源过滤(检查节点能否容纳Pod)也是评分插件(默认LeastAllocated倾向分散,bin-packing 打包需显式启用MostAllocated/RequestedToCapacityRatio)——避免副本都落一台机器。 - 故障自愈:
kubelet监控容器死活,挂了自动重启;节点宕了,Controller检测到Pod异常,会在其他节点重新创建副本(ReplicaSet维持副本数)——自动恢复。 - 健康检查:
Pod配探针(liveness/readiness),liveness检测“容器是否健康,不健康就重启”,readiness检测“是否可接入流量,不健康就剔除”——保证流量只到健康的 Pod。
- 多副本
一句话理解
- 高可用 = “一个坏了还有别的”。控制面(管理处)多几个前台+多本账本(
etcd集群)+ 前台挂了能顶替;工作负载(应用)多几个服务员(Pod副本)+ 坏了自动换人。这样不管哪层出问题,服务都不中断。
- 高可用 = “一个坏了还有别的”。控制面(管理处)多几个前台+多本账本(
协助记忆
K8s高可用 = “连锁餐厅”——不是只有一家店(单点),而是:总部(Master)有多个管理层(多前台 + 多账本 + 轮值经理),每家店(节点)都有几个服务员(Pod副本),某个服务员辞职(POD 挂)立刻换人(自愈),某个店着火(节点宕)其他店正常营业(分散)。无论哪个环节坏,餐厅都能继续营业。- 口诀:“主控多副本、
etcd凑奇数、Scheduler/Controller 轮流选主——应用多副本分散、探针保健康、挂了自动换”。
进阶思考
- 高可用集群最少要几台机器?
- 生产一般至少 3 控制节点(
etcd3 节点 +API Server3 副本,能容忍挂 1 台)+ 若干工作节点(应用副本分散)。3 控制节点是能用etcd多数派的最小规模(挂 1 台还剩 2 台 > 1.5 多数),5 节点更稳(挂 2 台)。
- 生产一般至少 3 控制节点(
etcd为什么必须是奇数节点?etcd用多数派(Quorum= 半数以上)保证一致性:3 节点挂 1 还剩 2(多数派)能正常读写;4 节点挂 2 还剩 2(正好半数不算多数派)就协议不可用(Raft失去多数派表现为“不可用”而非脑裂)。所以用奇数节点——同样机器数下容错更高(3 挂 1、5 挂 2;4 只能挂 1,浪费一台)。
- 高可用集群最少要几台机器?
扩展信息
leader election(选主):Scheduler/Controller多副本时,通过kube-controller-manager的选主机制(--leader-elect)保证同一时刻仅一个活跃(防止多个同时调度导致冲突)。Pod跨节点分散:可用Pod 反亲和(PodAntiAffinity)让副本尽量不同节点、PodDisruptionBudget(PDB)限制“同一时刻最多能停几个副本”(滚动升级/节点维护时保可用)。- 高可用 vs 容灾:
K8s高可用主要防“节点/组件级故障”;要防“整个集群/机房灾难”(如整个Master集群挂),还需跨集群/多集群方案(如联邦、多集群调度)——高可用是“集群内”,容灾是“跨集群”,层次不同。 - 参考:
HAProxy/云LB(API Server前负载均衡)、etcdquorum、ReplicaSet(维持副本数)、liveness/readiness探针、PodAntiAffinity/PDB。
🤔 Pod 解决了什么问题?
Pod是K8s的最小调度/部署单元:把一个或多个关系紧密的容器打包在一起(共享网络、存储、生命周期),一起调度、一起启动、一起销毁。它解决的核心问题:“容器不能裸奔”——K8s 管的是Pod而不是单个容器,让“一组必须协同的容器”被当作一个整体管理。Pod是什么- 一个
Pod= 一个“业务逻辑单元”:通常一个主容器(业务)+ 可能的辅助容器(如日志收集、网络代理——sidecar模式)。 - 是
K8s中最小调度单元(K8s不直接调度容器,而是调度Pod)、最小部署单元(一个Pod是部署的原子)。
- 一个
它解决的核心问题
- 容器组合:有些容器必须同生共死、共享资源(如同一个应用 + 它的日志采集容器),分开调度会断;
Pod把它们打包成一个“整体”,一起运行。 - 共享网络:
Pod内所有容器共享同一个网络命名空间(共享IP,可localhost互访)——这是“为什么一个 Pod 能有多个容器”的关键。 - 共享存储:
Pod内容器可共享挂载的存储卷(Volume),数据互通。 - 统一生命周期:一起启动、一起销毁——
Pod是一个“生物”,整个Pod存亡是整体。
- 容器组合:有些容器必须同生共死、共享资源(如同一个应用 + 它的日志采集容器),分开调度会断;
为什么不是直接管容器(关键)
- 单个容器太“碎”(一个容器一个进程/职责),而业务往往需要一组协同容器(如 web + 日志 + 探针)。
Pod是“贴合业务的一组容器”,是K8s管理的基本粒度——你管的是“Pod 群”,不是“容器个体”。
- 单个容器太“碎”(一个容器一个进程/职责),而业务往往需要一组协同容器(如 web + 日志 + 探针)。
一句话理解
Pod就像“一个房间里的几位室友”——他们必须住一起(同一间房,共享网络/存储)、同住同散(一起搬进/搬出),K8s物业(调度器)做“房间分配”,管的是“房间”(Pod)而不单独管每个室友(容器)。
协助记忆
Pod像“一房多室友”——同房(共享网络/存储)、同住同散(一起调度/销毁),K8s按“房间”(Pod)来管,不单独管每个“室友”(容器)。- 口诀:“Pod 是打包容器的最小单元——同网、同卷、同生死,K8s 管 Pod 不管裸容器”。
进阶思考
- 一个 Pod 里到底放几个容器?
- 通常一个主容器(一个
Pod一个容器最常见);多个容器仅在强协同时(sidecar模式:如业务容器 + 日志采集、业务 + 网络代理istio),且要同生命周期。关系松散、应独立扩缩的服务别塞进一个Pod(那会一起扩缩、浪费资源)。
- 通常一个主容器(一个
Pod和容器是什么关系?Pod是最小编排/调度单元,容器是最小运行单元——一个Pod里可有一个或多个容器。你写Deployment指定的是“要多少个Pod,每个Pod跑什么容器”,K8s以Pod为单位调度。
- 一个 Pod 里到底放几个容器?
扩展信息
Pod与容器网络:Pod内多个容器共享一个网络命名空间(同一IP、端口localhost可通),这是“一个 Pod 多容器能协同”的基础。Pod生命周期:Pod是“短命”的(无状态),挂了会被Deployment重建(新Pod新IP);需要持久数据用Volume、需要固定身份用StatefulSet。- 相关概念:
Pod是最小单元,上层用Deployment(无状态)/StatefulSet(有状态)/DaemonSet(每节点一个)管理Pod——理解Pod是理解K8s一切资源的基石。
🤔 K8s 有哪些工作负载资源?
K8s的“工作负载资源”是管理 Pod 的控制器,按场景分:Deployment(无状态应用,最常用)、StatefulSet(有状态应用,要固定网络 ID/数据)、DaemonSet(每个节点跑一个)、Job(一次性任务)、CronJob(定时任务)、ReplicaSet(维护副本数的底层控制器)。核心:“按应用是否有状态、是一次性还是常驻、是否每节点都要,选对应控制器”。Deployment(无状态应用,最常用)- 管理无状态应用(web/API 等,数据不存本地),声明“要多少副本”,自动维持副本数、滚动更新、回滚——最主流,日常部署用它。
- 底层由
ReplicaSet(维护副本数)+ 版本管理组成,支持滚动更新/灰度回滚。
ReplicaSet(底层控制器)- 维护指定数量的
Pod副本(始终保证“有 N 个”),Deployment内部管理它;一般你不直接建ReplicaSet,而是用Deployment(它更高级)。
- 维护指定数量的
StatefulSet(有状态应用)- 管理有状态应用(数据库/
Redis/Kafka等,要持久化数据、固定身份)——保证每个Pod有固定网络 ID(web-0/web-1)、固定存储卷,按序启动/升级。 - 适合:需要稳定标识、持久存储、有序部署的应用(数据集群)。
- 管理有状态应用(数据库/
DaemonSet(每个节点一个)- 确保每个(或指定节点都有)一个
Pod——如日志采集(Fluentd)、监控探针(node-exporter)、网络插件(CNI)——每个节点都要跑的服务。
- 确保每个(或指定节点都有)一个
Job/CronJob(任务)Job:一次性批处理任务(跑完即止,成功退出)——如数据处理、批量计算。CronJob:定时任务(cron表达式调度)——如定期备份、定时报表。
一句话理解
- 工作负载资源 = “不同任务的管家”:
Deployment管普通员工(无状态、随时可换),StatefulSet管固定工位的老员工(有状态、工号固定),DaemonSet管每个楼层的巡检员(每层一个),Job/CronJob管临时工/固定周期的保洁(一次性/定时),ReplicaSet是保证“人不少”的班表。
- 工作负载资源 = “不同任务的管家”:
协助记忆
- 按“身份 + 是否常驻”选:“无状态常驻 →
Deployment;有状态/固定身份+数据 →StatefulSet;每节点一个 →DaemonSet;一次性任务 →Job;定时任务 →CronJob;管副本数的底层 →ReplicaSet”。 - 口诀:“无状态 Deployment、有状态 StatefulSet、每节点 DaemonSet、一次性 Job、定时 CronJob——按‘有没有状态、是不是常驻、要不要每节点’挑”。
- 按“身份 + 是否常驻”选:“无状态常驻 →
进阶思考
Deployment和StatefulSet怎么选?- 应用无状态(数据不存本地、谁干都一样、挂了换新的)→
Deployment(简单、可随意扩缩);应用有状态(需要持久数据 + 稳定网络标识,如数据库集群/Kafka/Redis)→StatefulSet(保证固定Pod名、固定存储、有序启停)。
- 应用无状态(数据不存本地、谁干都一样、挂了换新的)→
DaemonSet和Deployment区别?Deployment按数量跑(默认均匀分布,副本数你定);DaemonSet按节点跑(每个(或匹配的)节点强制一个)——DaemonSet适合“每个机器都要有”的基础组件(日志/监控/网络插件),Deployment适合“要几个就跑几个”的业务应用。
扩展信息
Workload概念:这些控制 Pod 的资源统称“工作负载(Workload)”,是K8s管理应用的入口——你写 YAML 声明“我要什么”,控制器负责“持续满足它”。ReplicaSet(旧)ReplicationController:ReplicationController是ReplicaSet的前身(selector仅支持等值式key=value,不含ReplicaSet引入的集合式matchLabels/matchExpressions选择),已被ReplicaSet取代;ReplicaSet又被Deployment封装。Helm管理:生产用Helm Chart打包/发布这些工作负载资源(deployment.yaml/statefulset.yaml模板化),实现可复用部署。- 参考命令:
kubectl get deployment/statefulset/daemonset/job/cronjob查看各类工作负载。
🤔 简述创建一个 Pod 的工作流程?
创建一个
Pod的完整流程:你kubectl apply/create提交Pod定义 →API Server校验并写入etcd→Scheduler监听到新 Pod,选一台合适节点并写回绑定 → 该节点kubelet收到指令,调用容器运行时(Runtime)真正创建并启动容器 →kubelet回报状态。核心:“声明 → 记账 → 分派 → 干活 → 汇报”。第一步:提交(你的请求)
- 你用
kubectl create -f pod.yaml(或apply)提交Pod定义(YAML:镜像、资源、端口等)——请求发到API Server。
- 你用
第二步:
API Server校验 + 记账API Server校验请求(合法性/权限/资源限制),把 Pod 写入etcd(记录“要创建这个 Pod”的期望状态)——但此时还没真正创建。
第三步:
Scheduler选节点Scheduler监听到“有新的未调度 Pod”,根据资源、亲和、污点等给 Pod 选一台合适的节点(Node),调用API Server创建Binding对象(由API Server持久化到etcd)完成绑定。
第四步:
kubelet创建容器- 选中节点上的
kubelet监听到“本节点要跑这个 Pod”,调用容器运行时(containerd/CRI-O等CRI兼容运行时——自K8s v1.24起dockershim已移除,docker不能直接作为CRI运行时,需经cri-dockerd)拉取镜像、创建并启动容器(按 YAML 定义),执行容器命令。
- 选中节点上的
第五步:汇报 + 健康检查
kubelet把 Pod 状态(Running/Ready)回报给API Server/etcd;Pod配了探针会持续检查(readiness就绪才接流量)。整体流程就是“声明 → 记账 → 分派 → 干活 → 汇报”。
一句话理解
- 就像“网上下单订房”——你下单(
kubectl apply)→ 平台记账(etcd)→ 分配房间(Scheduler选节点)→ 房卡/保洁准备入住(kubelet+runtime创建容器)→ 前台回复“已入住”(汇报状态)。
- 就像“网上下单订房”——你下单(
协助记忆
Pod创建 = “下单 → 记账 → 分房 → 入住 → 回执”,对应kubectl→etcd→Scheduler→kubelet+Runtime→ 状态汇报。- 口诀:“你提交(API)、它记账(etcd)、它分房(Scheduler)、它干活(kubelet/Runtime)、它汇报——五步建一个 Pod”。
进阶思考
Scheduler选节点时看什么?- 主要看:资源够不够(
requests资源请求,节点要有足够剩余)、节点标签/亲和(nodeSelector/nodeAffinity)、污点/容忍(taint/toleration)、Pod 间亲和/反亲和(是否要同/异节点)、端口/存储要求。选中最优节点。
- 主要看:资源够不够(
- 如果所有节点都不够资源,Pod 会怎样?
Scheduler找不到合适节点,Pod 停在Pending状态(一直等)——常见原因:资源不足/污点不容忍/存储卷挂不上。所以看到Pending要查资源/污点/存储。
扩展信息
kubectl常用命令:kubectl get pod(看 Pod)、kubectl describe pod <name>(看详情/事件)、kubectl logs <pod>(看日志)、kubectl exec -it <pod> -- bash(进入容器)。- 不同创建方式:
kubectl create(创建)、kubectl apply(声明式,推荐——可重复应用、幂等),生产多用Helm/kubectl apply。 PodStatus:Pending(等待调度/创建)、Running(运行中)、ContainerCreating(拉镜像中)、CrashLoopBackOff(反复崩溃重启)、Completed(任务完成)。- 参考:
kubectl run(快速跑一个 Pod)、kubectl apply -f(从 YAML 应用)、Pod调度依赖Scheduler。
🤔 Pod 有哪几种重启策略及应用场景?
Pod 重启策略(
restartPolicy)决定“容器退出后 K8s 怎么处理”,三种:Always(总是重启,最常用)、OnFailure(失败才重启,任务场景)、Never(从不重启,一次性任务)。核心:“看这个 Pod 是常驻服务还是任务——常驻要 Always,失败重跑用 OnFailure,跑一次绝不重启用 Never”。Always(总是重启)- 容器无论正常还是异常退出,都自动重启——保证服务常驻不中断(哪怕容器是正常退出也拉起)。
- 适用:无状态常驻服务(web/API/微服务)——挂了要立刻拉起,是最常用;且
Deployment强制且默认为Always(写成OnFailure/Never会校验失败)。 - 特点:
CrashLoopBackOff(反复崩溃时 K8s 会等待片刻再重启,防无限重启);适合“必须一直在线”的应用。
OnFailure(失败才重启)- 容器**异常退出(退出码非 0)**才重启;正常退出(退出码 0)不重启——适合“做完了就算成功”的任务。
- 适用:
Job类任务(批处理/一次性计算)——失败了重试,成功了就结束(注意:Job默认restartPolicy: Never、仅允许OnFailure/Never)。
Never(从不重启)- 容器退出(无论成败)都不重启——适合“只能跑一次、结果定了就不动”的任务。
- 适用:一次性/一次性采集任务(跑完记录结果,重跑可能重复写数据);监控它退出状态(
Completed/Failed)。
一句话理解
restartPolicy像“店员的排班规则”:Always= 全天在岗(无论啥原因离开都得立刻回来);OnFailure= 只在你“犯错”(失败)时叫回来,正常下班不用回来;Never= 干完这件活就走,不管成败都不再叫。
协助记忆
- 按“常驻 vs 任务”选:“常驻服务用
Always、失败重跑用OnFailure、跑完即止用Never”。 - 口诀:“Always 常驻总重启、OnFailure 失败才重启、Never 成败都不重启——常驻选 Always,任务看要不要重试”。
- 按“常驻 vs 任务”选:“常驻服务用
进阶思考
- 为什么
Deployment默认Always,但Job用OnFailure?Deployment是无状态常驻服务,目标是“永远有副本在跑”,所以Always(挂了立刻拉起);Job是“跑完这一批就算成功”,OnFailure让它在失败时重试、成功时淡定结束(若Always成功也重启反而“完成不了”)。策略要和“控制器目标”匹配。
CrashLoopBackOff是什么?- 容器反复崩溃(
CrashLoop)时,K8s 会指数退避等待(BackOff)再重启(实际序列10s/20s/40s/80s/160s/300s,上限300s即 5 分钟;连续正常运行约 10 分钟后重置退避计数)——防止“无限快速重启”耗尽资源。看到CrashLoopBackOff说明应用一直启动失败,要去查日志找根本原因(配置错/启动依赖/资源不足)。
- 容器反复崩溃(
- 为什么
扩展信息
restartPolicy只能控制 Pod 内容器,且影响Pod是否被重建:Always/OnFailure容器重启不换Pod;但若Pod整个被删除(节点挂了/驱逐),则由上层控制器(Deployment)重建新Pod——所以“重启策略”和“控制器重建”是两回事。Job失败容错:Job配合backoffLimit(失败重试次数)和ttlSecondsAfterFinished(完成后保留时间),控制“重试几次/结果保留多久”。- 参考命令:写
PodYAML 的spec.restartPolicy(Always/OnFailure/Never);kubectl get pod看状态、kubectl describe pod看CrashLoopBackOff事件。
🤔 Pod 有哪几种探针(健康检查)及应用场景?
Pod 探针(
Probe)是健康检查,K8s 定期探测容器是否正常。三种:liveness(存活探针:不健康就重启)、readiness(就绪探针:不健康就剔除流量)、startup(启动探针:慢启动应用先探测就绪,防重启误判)。核心:“liveness 管活不活(重启)、readiness 管能不能接流量(剔除)、startup 管慢启动”。livenessProbe(存活探针,决定“重启”)- 探测容器是否活着/健康;探测失败 → K8s 重启容器(消灭僵尸容器,让它回到健康)。
- 适用:应用“活着但不响应请求/卡死”的情况(如进程在但线程死锁、内存泄漏假死)——重启让它恢复。
- 探测方式(4 种):
exec(执行命令)、httpGet(HTTP 请求)、tcpSocket(端口连接)、grpc(嵌入式gRPC健康检查,需应用实现 gRPC Health Checking 协议;v1.23alpha、v1.24beta、v1.27起 GA)。
readinessProbe(就绪探针,决定“接流量”)- 探测容器是否准备好接收流量;未就绪 → 从 Service 后端剔除(不转发流量给它),但仍可被 liveness 重启。
- 适用:应用启动慢/依赖外部(连不上数据库就别接流量)、需要“就绪才能被访问”——保证流量只到能工作的 Pod。
- 与
liveness区分:readiness 失败不重启、只暂停/恢复转发;liveness 失败重启。
startupProbe(启动探针,处理“慢启动”)- 探测容器是否完成启动;启动期间不启用 liveness/readiness,成功后才交管——防止慢启动应用被 liveness 误杀(启动慢被当“假死”反复重启)。
- 适用:启动特别慢的应用(加载大数据/初始化久)——先给它足够启动时间,避免“启动中就被重启”。
一句话理解
- 探针像“员工健康检查”:
liveness是“你还活着吗”(晕倒了叫急救=重启);readiness是“你现在能接客吗”(还没准备好就让你先歇着,不让接客=剔除流量);startup是“你热身完了吗”(刚入职给适应期,别一慢就开除=防误杀)。
- 探针像“员工健康检查”:
协助记忆
- 口诀:“liveness 管活(重启)、readiness 管接流量(剔除)、startup 管慢启动(防误杀)——活、就绪、启动三兄弟”。
- 一句话:“活死用 liveness、能不能接客用 readiness、慢启动用 startup”。
进阶思考
liveness和readiness到底啥区别(最容易混)?liveness判“容器进程还健康吗”——不健康就重启(自救);readiness判“容器能服务了吗”——未就绪就从负载均衡摘除(不转发流量,防止流量打给没准备好的容器)。一个管“活”,一个管“接客”,缺一不可(readiness防流量错,liveness防僵尸)。
- 探针用
httpGet还是exec/tcpSocket?httpGet(HTTP 状态码 2xx/3xx)最常用(应用有 HTTP 接口时);tcpSocket(能连端口)简单但只判“端口通”非业务健康;exec(执行命令退出码 0)灵活但开销大。按应用接口选——HTTP 应用用httpGet,纯 TCP 服务用tcpSocket。
扩展信息
- 探针参数:
initialDelaySeconds(启动后多久开始探测)、periodSeconds(间隔)、timeoutSeconds(超时)、failureThreshold(失败几次判定)、successThreshold(成功几次判定)。 - 探测失败的影响:
liveness失败→重启容器(restartPolicy);readiness失败→从ServiceEndpoint移除(不再转发);都配了 startup 时启动期先走 startup。 - 参考:
kubectl describe pod看探针事件、kubectl get endpoints看 Service 后端(readiness 剔除后数量变化)、写PodYAML 的spec.containers[].livenessProbe/readinessProbe/startupProbe。
- 探针参数:
🤔 Pod 有哪些状态及其原因?
Pod状态反映它的生命周期阶段:Pending(已提交、还没跑起来)、Running(运行中)、Succeeded(正常结束)、Failed(失败结束)、Unknown(状态未知/失联)。另有常见的子状态(ContainerCreating/CrashLoopBackOff/Evicted等)。核心:“看状态判断 Pod 卡在哪一步——Pending 在调度/创建、Running 在跑、异常要看具体子状态”。五大阶段状态(顶层)
Pending:Pod已被 K8s 接受(写入etcd),但还没真正运行——可能在等调度(资源不足/污点)或拉镜像。Running:Pod已调度到节点,至少一个容器在运行(或正在启动/重启)。Succeeded:Pod所有容器正常退出(退出码 0),任务完成——Job常见。Failed:Pod所有容器退出但至少一个失败(非 0)——任务失败。Unknown:无法获取Pod状态(节点失联/kubelet 挂了)——状态未知,要查节点。
常见子状态(定问题时看这个)
ContainerCreating:容器正在创建(拉镜像/初始化)——卡在这通常镜像拉不动(网络/权限/镜像不存在)。CrashLoopBackOff:容器反复崩溃,K8s 指数退避等待重启——说明应用一直启动失败,查日志(此时Pod顶层phase仍是Running,仅容器层处于Waiting/反复重启)。Evicted:Pod被驱逐(节点资源紧张/磁盘满/节点要维护)——查驱逐原因。ImagePullBackOff:拉镜像失败(镜像不存在/认证失败/私有仓库权限)。Init:0/1等:初始化容器执行中。
状态转换
- 正常:
Pending→Running→Succeeded(任务);持续服务则Running(可能反复CrashLoopBackOff);异常会落到Failed/Evicted/Unknown。
- 正常:
一句话理解
Pod状态像“快递物流状态”:Pending= 已下单但还在分拣中心(没发货=没跑起来);Running= 运输中(跑起来了);Succeeded/Failed= 签收/拒收(正常完成/失败);Unknown= 快递信息查不到了(失联)。子状态(ContainerCreating=在打包、CrashLoopBackOff=反复被打回重发、Evicted=仓库腾地方不要这单了)。
协助记忆
- 口诀:“Pending 等分配、Running 在跑、Succeeded 正常完、Failed 失败、Unknown 失联——异常看子状态(Creating/CrashLoop/Evicted)”。
- 一句话:“Pending 没跑起来、Running 在跑、Succeeded/Failed 是任务完没完、Unknown 是失联”。
进阶思考
Pending最常见的原因有哪些(怎么排查)?- ①资源不足:节点没有足够
CPU/内存(看describe事件FailedScheduling/Insufficient cpu)②污点不容忍:节点污点且 Pod 没对应容忍 ③存储卷挂载失败(PVC未绑定)④镜像拉取问题(ImagePullBackOff)。用kubectl describe pod看事件定位。
- ①资源不足:节点没有足够
CrashLoopBackOff怎么排查?- 说明容器反复崩溃(启动即失败);
kubectl logs <pod> --previous看上次崩溃日志,找启动报错(配置文件错/启动命令错/依赖服务连不上/内存不足被杀)。修好配置/依赖后自动恢复。
- 说明容器反复崩溃(启动即失败);
扩展信息
- 判断命令:
kubectl get pod(看状态)、kubectl describe pod <name>(看事件/原因,最有用)、kubectl logs <pod>(看日志)、kubectl get events(集群事件)。 Evicted常见原因:节点DiskPressure(磁盘压力)/MemoryPressure/节点维护——K8s 驱逐低优先级 Pod 保节点,被驱逐的 Pod 由Deployment去其他节点重建。Pod状态 vs 容器状态:Pod是容器组整体状态;单看容器可用kubectl get pod -o jsonpath或kubectl describe看容器状态(Waiting/Running/Terminated)。
- 判断命令:
🤔 初始化容器有哪些应用场景?
初始化容器(
initContainer)是在主容器启动前先执行完的容器:按顺序运行(每个完成后下一个才开始),完成后主容器才启动。核心用途:“主容器启动前要先做好的准备工作”——如等待依赖服务就绪、初始化配置/脚本、设置权限、下载数据。核心:“主容器开工前的‘准备工作容器’”。什么是初始化容器
- 定义在
Pod的spec.initContainers,在主容器之前运行;按顺序执行(前一个成功退出才跑下一个),全部成功后主容器才启动。 - 特点:一次性(跑完即退出,不像主容器长期运行);可配置多个按序执行;失败则整个 Pod 重试/重启。
- 定义在
典型应用场景
- 等待依赖服务就绪:主容器(如 web)要等数据库/
Redis可用才能起——初始化容器里探测/等待(until轮询)依赖,就绪后放行主容器。 - 初始化配置/数据:往共享卷写入配置文件、拉取/生成数据(如下载模型、初始化数据库结构)——主容器直接用准备好的数据。
- 权限/属主设置:调整共享卷的属主/权限(
chown/chmod)——因为Pod卷权限可能不适合主容器用户,初始化容器先设置好。 - 网络/环境准备:设置网络规则、校验镜像签名、安装依赖工具等“主容器运行前必须完成”的任务。
- 等待依赖服务就绪:主容器(如 web)要等数据库/
与主容器区别(关键)
InitContainer跑完就退出(一次性),主容器长期运行;InitContainer不参与就绪/存活探针;InitContainer失败 → Pod 重启(按restartPolicy)重新执行初始化。- 顺序执行:多个
InitContainer一个接一个,前一个成功才下一个。
一句话理解
- 初始化容器像“餐厅开业前的后厨准备”——正式营业(主容器开跑)前,先按顺序做好:开门检查食材(等依赖就绪)、写今日菜单(初始化配置)、摆好厨具(设权限),全部弄好才开门迎客(主容器启动)。
协助记忆
InitContainer= “主容器开工前的准备工作容器”——等依赖、初始化数据、设权限,跑完就退,主容器才上场。- 口诀:“先准备、后开跑——等依赖/配数据/设权限,做到位主容器才启动”。
进阶思考
InitContainer和readinessProbe都是“等就绪”,有区别吗?- 有:
readinessProbe是主容器已启动后、探测它是否就绪(未就绪剔除流量,但容器已跑);InitContainer是主容器启动前的准备工作(没做完主容器根本不启动)。一个“启动后等就绪”,一个“启动前做准备”。
- 有:
- 什么时候用
InitContainer而不是主容器自己等?- 当“准备工作”是一次性的、独立于主容器主进程的(如专门的初始化脚本、等外部依赖、下载数据)——用
InitContainer让主容器纯业务、启动快;若只是主容器启动时简单判断,可在主容器启动命令里做,不必开InitContainer。
- 当“准备工作”是一次性的、独立于主容器主进程的(如专门的初始化脚本、等外部依赖、下载数据)——用
扩展信息
InitContainer限制:普通初始化容器不能设置readinessProbe/livenessProbe(v1.28+的可重启初始化容器——sidecar模式——才支持探针/lifecycle);资源请求按max(普通容器 requests 之和, 单个初始化容器 requests 的最大值)计算(取最大值);失败按restartPolicy重启(Always下重跑初始化)。Pod生命周期:Init:N/M的 N 表示已成功完成的初始化容器数——Init:0/2:第 1 个初始化容器运行中(0 个已完成);Init:1/2:第 1 个已完成、第 2 个运行中。用kubectl get pod可看到初始化进度。- 参考:写
PodYAML 的spec.initContainers(数组、按序执行);帮助排查启动慢/依赖问题的常用手段。
🤔 Pod 超出资源限制时,K8s 会做什么动作?
Pod跨过资源限制分两层:requests(请求值,决定能不能被调度)和limits(上限值,决定运行时能不能超)。超出limits(如 CPU 超限/内存超限):CPU 超限被限流(节流),内存超限被OOMKill杀掉并可能重启。核心:“requests 定调度门槛、limits 定运行上限——CPU 超限节流、内存超限杀死”。先分清
requests和limits(关键)requests(请求值):容器最低需要的资源(CPU/内存),Scheduler用它在选节点时判断“节点够不够”——节点要能满足所有的requests才能调度,这是调度门槛。limits(上限值):容器最多能用的资源(CPU/内存),超出会被kubelet强制限制——这是运行上限(limits≥requests)。
超出
CPU限制- CPU 超
limits:容器被限流(CPU Throttling)——cgroup把 CPU 使用率限制在限额内,不会杀掉容器,但性能下降(跑变慢、节流)。 - 表现:容器还在运行,但请求超时/响应慢(CPU 被限制)。
- CPU 超
超出
内存限制- 内存超
limits:容器被OOMKill(OOM 杀死)——内核OOM Killer杀掉进程(超内存容器),容器可能崩溃重启(看restartPolicy)。 - 表现:
CrashLoopBackOff/OOMKilled(kubectl describe看到OOMKilled),容器反复被杀重启。
- 内存超
调度层面(节点资源不足)
- 若节点资源压力大(
DiskPressure/MemoryPressure),kubelet触发驱逐(按优先级从低到高杀 Pod 腾资源);若高优先级 Pod 无法调度,调度器触发抢占(preemption,抢占低优先级 Pod)——二者机制不同:驱逐因节点压力、抢占为高优先级腾位。
- 若节点资源压力大(
一句话理解
requests像“订房的最低要求”(至少多大房间,不够不给订=调度门槛);limits像“房间用电上限”(超了会被限电=CPU 节流,或跳闸断电=内存 OOM 杀掉)。
协助记忆
- 口诀:“requests 定调度门槛、limits 定运行上限——CPU 超限被限流、内存超限被 OOM 杀”。
- 一句话:“请求值(requests)管能不能住、上限值(limits)管超没超——CPU 超限变慢、内存超限被杀死”。
进阶思考
- 为什么
requests和limits要分开?requests用于调度(保证节点能承载),limits用于运行限制(防单个容器疯抢资源拖垮节点)。分开让你能“按需申请、按上限限制”——如requests: 100m CPU(只占 100 毫核)但limits: 500m(最多可冲到 500 毫核),兼顾调度效率与资源保护。
- 内存
OOMKill后容器总是重启(CrashLoopBackOff)怎么办?- 说明内存真的超了:①调大
limits(若业务确实需要)②减少应用内存占用(调 JVM 堆/缓存大小)③检查是否@Query数据倾斜/内存泄漏。否则反复 OOM(设计不当),治本是降低实际内存需求。
- 说明内存真的超了:①调大
- 为什么
扩展信息
- 资源单位:CPU 用核/毫核(
500m= 0.5 核)、内存用Mi/Gi(如512Mi)。 Pod级 vs 容器级:K8s API无Pod级resources字段——资源只能配在spec.containers[].resources;Pod的有效请求 = 所有容器requests之和(由各容器求和得出);应用层关心容器内limits(进cgroup)。QoS类:根据requests/limits组合,Pod 被分为Guaranteed(requests=limits,高优先级,最不容易被驱逐)、Burstable、BestEffort(无 limits/requests,最低优先级,易被驱逐)——影响驱逐/抢占顺序。- 参考:
kubectl describe pod看OOMKilled/Limits;kubectl top node/pod看实际资源使用。
- 资源单位:CPU 用核/毫核(
🤔 Pod 处于 Pending 状态可能是什么原因?
Pending状态 = Pod 被接受但还没真正运行起来。常见原因:①资源不足(节点 CPU/内存不够,Scheduler调度不了)②污点/容忍不匹配(节点有污点,Pod 没对应容忍)③存储卷PVC未绑定/挂载失败④镜像拉取失败(ImagePullBackOff)⑤调度器找不到合适节点(节点选择器/亲和不满足)。核心:“Pending = 卡在‘没好地方可以跑’或‘啥都没准备好’”。最常见的几个原因(排查按这个顺序)
- ① 资源不足:节点没有足够的
CPU/内存满足 Pod 的requests——Scheduler找不到能容纳的节点,kubectl describe事件显示FailedScheduling/Insufficient cpu。 - ② 污点/容忍不匹配:节点打了污点(
Taint)(如node-role.kubernetes.io/master:NoSchedule),目标 Pod 没配对应容忍(Toleration)——调度器跳过这些节点。 - ③ 存储卷问题:Pod 需要**
PVC(持久卷)**,但PVC未绑定/存储类(StorageClass)不存在/卷挂载失败——Pod 等卷。 - ④ 镜像拉取失败:镜像名错/私有仓库认证失败/镜像不存在——显示
ImagePullBackOff。 - ⑤ 节点选择/亲和不满足:
nodeSelector/nodeAffinity/podAffinity指定的节点不存在或不满足条件。 - 其他:
hostPort端口冲突(可致调度失败);ResourceQuota/LimitRange配额限制通常在准入阶段拒绝创建(表现为Deployment/ReplicaSet的FailedCreate事件,而非Pending调度失败)。
- ① 资源不足:节点没有足够的
一句话理解
Pending像“快递一直显示‘分拣中’”——不是丢了,而是:①没有运力的车(资源不足)②这个地址车不能去(污点/亲和)③要的货还没到(存储卷/镜像)④等发货(调度排队)。
协助记忆
- 口诀:“Pending 查五件事:资源够不够(Insufficient)、污点容不容忍、卷 PVC 绑没绑、镜像拉没拉(ImagePull)、亲和满不满足”。
- 一句话:“Pending = 没地方跑 / 没准备好——先 kubectl describe 看事件”。
进阶思考
- 排查
Pending最直接的方法?kubectl describe pod <name>——看Events区的报错(FailedScheduling: 0/3 nodes available、Insufficient cpu、unbound PVC、ImagePullBackOff),事件会直接告诉你卡在哪。这是排Pending的第一命令。
0/3 nodes available什么意思?- 调度器遍历了全部 3 个节点,发现都没有合适的——原因可能是资源不足、污点、标签不匹配等(describe 会列出各节点
didn't match: node(s) ...)。逐个节点看原因。
- 调度器遍历了全部 3 个节点,发现都没有合适的——原因可能是资源不足、污点、标签不匹配等(describe 会列出各节点
- 排查
扩展信息
Pending≠ 故障:是“等待中”,可能等资源/等卷/等调度,不代表 Pod 有问题;但一直Pending要查(资源扩容/污点/卷/镜像)。- 常见相关事件:
FailedScheduling(调度失败)、FailedMount(卷挂载失败)、ImagePullBackOff(拉镜像失败)、Unschedulable(节点不可调度)。 - 参考:
kubectl get events --sort-by=.metadata.creationTimestamp(看最新事件)、kubectl describe node <node>(看节点资源/污点)。
🤔 简述删除一个 Pod 的工作流程?
删除一个
Pod的核心是“优雅删除(graceful deletion)”:你kubectl delete pod发请求 →API Server给Pod打上删除标记(设deletionTimestamp,进入Terminating)→kubelet收到后先优雅停止容器(发SIGTERM,给应用时间保存/断开连接)→ 宽限期到(或容器退出)则强制SIGKILL→ 移除Pod并从etcd删除。核心:“先礼貌辞退(SIGTERM 优雅停止),超时强制清场(SIGKILL)”。完整流程
- ①你
kubectl delete pod <name>→ 请求到API Server②API Server标记Pod为删除(deletionTimestamp设置,状态转Terminating),但不立即删 ③kubelet监听到删除标记,启动优雅关闭:先执行preStop钩子(自定义清理、摘流)并从Service摘掉(并行进行;preStop常用sleep延迟等LB完成流量摘除),再向主容器发送SIGTERM(终止信号)——给应用时间收尾④等待终止宽限期(gracePeriodSeconds,默认30s)⑤宽限期到(或应用自行退出),kubelet强制SIGKILL(立即杀)⑥Pod移除、从etcd删除(API Server确认)。
- ①你
关键点
- 优雅停止(
SIGTERM):先给应用时间收尾(保存状态/断开连接/通知下游),避免直接杀导致数据不一致/请求中断。 - 宽限期(
grace period):gracePeriodSeconds(默认 30 秒)——SIGTERM后等的最大时间,超时强制SIGKILL;可kubectl delete --grace-period=0 --force强制立即删。 preStop钩子:容器停止前执行的自定义命令(如优雅摘流、通知注册中心),配合SIGTERM做平滑下线。
- 优雅停止(
一句话理解
- 删除
Pod像“正规辞退员工”:先通知他“要和你解约了”(SIGTERM,给时间交接工作/保存进度),给个交接期(宽限期 30s),期到了还没走就强制清场(SIGKILL);若老板急(--force)直接当场辞退。
- 删除
协助记忆
- 口诀:“
kubectl delete→Terminating→SIGTERM优雅退(+preStop)→ 宽限期 30s → 超时SIGKILL→ 移除”。 - 一句话:“先礼貌(SIGTERM 保存收尾)、后强制(超时 SIGKILL)——删除不丢协议”。
- 口诀:“
进阶思考
- 为什么要有优雅停止(不直接杀掉)?
- 直接
SIGKILL会让应用来不及保存状态/关闭数据库连接/通知下游——正在处理的请求被腰斩、数据可能不一致、注册中心可能还留着该实例。优雅停止给应用机会“体面退出”(SIGTERM+preStop),保证业务平滑。
- 直接
Service怎么配合 Pod 删除(避免流量打到要删的 Pod)?Pod删除时会从Service的Endpoint摘除(readiness变 false),不再转发新流量;配合preStop(摘流)让删除时新请求不再进来、旧请求处理完,实现平滑下线。
- 为什么要有优雅停止(不直接杀掉)?
扩展信息
- 强制删除:
kubectl delete pod --grace-period=0 --force(跳过优雅,立即杀);kubectl delete pod --wait=false(不等待)。 Terminating状态:删除过程中 Pod 显示Terminating(deletionTimestamp已设);若一直Terminating卡住,可能是finalizers(终结器)阻止删除——查describe的finalizer。- 参考:
kubectl delete pod <name>、kubectl describe pod(看Terminating/finalizer)、preStop钩子、gracePeriodSeconds。
- 强制删除:
🤔 K8s 出现大量 Pod 处于 Evicted 状态是什么原因?
Evicted状态 =Pod被节点驱逐(eviction)了——kubelet因节点资源压力把Pod强制驱逐(杀掉并在可能处重建)。常见原因:①磁盘DiskPressure(磁盘/inode满)②内存MemoryPressure(节点内存紧张)③节点维护/drain(cordon仅标记不可调度、不驱逐已跑 Pod,drain才驱逐迁移)④QoS优先级低(BestEffort/Burstable被kubelet驱逐;非高优抢占)。核心:“Evicted = 节点压力大,K8s 保节点清 Pod”。什么是驱逐(
eviction)kubelet持续监控节点资源(磁盘/内存等),达到驱逐阈值(DiskPressure/MemoryPressure/PIDPressure)时,主动驱逐(kill)一些Pod给节点“减负”——被驱逐的Pod标记Evicted。- 驱逐顺序:按
QoS优先级从低到高——BestEffort(无资源限制)最易被逐,其次Burstable,最后Guaranteed(requests=limits,最安全)。
大量
Evicted的常见原因DiskPressure(磁盘压力):某节点磁盘/inode快满(日志堆积/无清理)→ 驱逐 Pod 腾磁盘——最常见。MemoryPressure(内存压力):节点内存紧张 → 驱逐 Pod(注意nodefs/imagefs属磁盘压力信号,归DiskPressure)。- 节点维护/
drain:节点要维护时用kubectl drain驱逐迁移 Pod(可cordon标记不可调度——但cordon本身不清已跑 Pod,是drain才驱逐)。 - 低 QoS 优先级:
BestEffort/BurstablePod 在资源紧张时(kubelet驱逐)先被逐(保护Guaranteed)——注意:高优抢占(调度器preemption,给高优Pod腾位)状态显Preempting,与Evicted(kubelet驱逐)机制不同,勿混。
大量 Evicted 说明什么(重要)
- 不是某个 Pod 出问题,而是整个节点(集群)资源告急——磁盘或内存压力大,
kubelet在“甩包袱”。要从节点资源入手(清理磁盘/扩容/排查日志堆积),而不是单个 Pod。
- 不是某个 Pod 出问题,而是整个节点(集群)资源告急——磁盘或内存压力大,
一句话理解
Evicted像“写字楼超载,物业强制清几间办公室”——办公楼(节点)太挤/存不下货了(磁盘/内存压力),物业按“租约优先级”(QoS)先清“临时工”(BestEffort),保重要租户(Guaranteed)。
协助记忆
- 口诀:“Evicted = 节点压力大被驱逐——磁盘满(DiskPressure)/内存紧(MemoryPressure)/节点维护,QoS 从低到高先清 BestEffort”。
- 一句话:“Evicted 不是 Pod 的错,是节点快满了——去清磁盘/看节点资源”。
进阶思考
Evicted的 Pod 会被重建吗?- 会。
Evicted是Pod被节点杀掉,但若它的控制器(Deployment/StatefulSet)维护副本数,会在其他节点/本节点重建(这也是为什么Evicted的 Pod 常变成Pending后重建)。但若节点持续压力,重建的 Pod 可能再次被驱逐(循环)。
- 会。
- 怎么排查/处理大量 Evicted?
- ①
kubectl describe node <node>看Conditions(DiskPressure/MemoryPressure)②大面积清理磁盘(日志/镜像/临时文件)、扩容磁盘/节点 ③检查是否有日志堆积/镜像爆炸 ④给关键 Pod 设limits(升Guaranteed)防被优先驱逐。
- ①
扩展信息
QoS三类(决定驱逐优先级):Guaranteed(requests=limits,优先保)、Burstable(requests<limits)、BestEffort(无 requests/limits,最易被逐)。- 驱逐配置:
kubelet的eviction-hard(硬阈值,立即逐)vseviction-soft(软阈值,留缓冲)+ 回收机制(垃圾回收image/log)。 - 参考:
kubectl get pod -A | grep Evicted(看驱逐 Pod)、kubectl describe node(看节点 Conditions/压力)、df -h/kubectl top node(看磁盘/资源)。
🤔 Deployment 滚动更新是怎么实现的?
Deployment滚动更新(rolling update)是“逐步替换旧版本 Pod”而不中断服务:Deployment创建新版本ReplicaSet,一点点(按maxSurge/maxUnavailable)先启新Pod、等新Pod就绪(readiness)→ 逐步缩旧Pod,直到全部切成新版本。核心:“新老版本交替,先增后减,服务不断”。滚动更新的核心思想
- 对比“一次性全部替换”(停机/风险大):滚动更新分批渐进——先起少量新版本
Pod(超额maxSurge),就绪后删少量旧Pod(maxUnavailable),反复直到全部新——全程服务不中断,出问题可中途回滚。
- 对比“一次性全部替换”(停机/风险大):滚动更新分批渐进——先起少量新版本
实现机制(
ReplicaSet版本切换)- ①
Deployment期望新版本 → 创建新的ReplicaSet(新版本模板)②新ReplicaSet启动新Pod,旧ReplicaSet的Pod逐步缩减 ③控制**maxSurge(最多超额几台)** +maxUnavailable(最多可用少几台):如maxSurge:1, maxUnavailable:0= 先加 1 台新 Pod、就绪后再删 1 台旧 Pod(服务始终足量)④新Pod通过readiness才接流量,Service自动把流量引到就绪的新版本 ⑤全部切换完成,旧ReplicaSet缩到 0(保留可回滚)。
- ①
关键参数
maxSurge:滚动时最多超额多少副本(如maxSurge:1= 最多比期望多 1 个,先加新)——决定“先增多少”(maxSurge/maxUnavailable默认均25%,可用整数或百分比表示)。maxUnavailable:滚动时最多可用少多少副本(如maxUnavailable:0= 始终保证 100% 可用不中断,默认25%)——决定“最多允许少几个旧”。minReadySeconds:新 Pod 就绪后等多久才算稳定(防刚就绪又挂)、progressDeadlineSeconds(更新超时判定失败)。
一句话理解
- 滚动更新像“旧员工换新员工”:不一次性辞掉所有人(全停机),而是先招几个新人(新 Pod
maxSurge),新人能干活了(readiness就绪)再辞几个老人(maxUnavailable),一茬一茬换——业务一直有人值班,不会中途关门。
- 滚动更新像“旧员工换新员工”:不一次性辞掉所有人(全停机),而是先招几个新人(新 Pod
协助记忆
- 口诀:“新老交替——先加新(maxSurge),等就绪,再删旧(maxUnavailable),滚动到全新”。
- 一句话:“先招新人顶班、新人能干了再辞老人——滚动更新不断服务”。
进阶思考
maxSurge:1, maxUnavailable:0和maxSurge:0, maxUnavailable:1区别?- 前者先加后删(先多起 1 台新的,就绪再删 1 台旧的)—可用副本始终 100%,适合核心服务(但可能短期超量);后者先删后加(先缩 1 台旧的,再补新的)—可用副本会短暂低于 100%,适合资源紧/可短暂降级的服务。
- 滚动更新到一半
readiness一直不过会怎样?- 新 Pod
readiness不通过(不健康)→ 新ReplicaSet的 Pod 反复重启/卡住,更新卡住(progressDeadlineSeconds到判定失败)> 旧ReplicaSet还健全(服务仍可用);可用kubectl rollout status看进度、kubectl rollout undo回滚。
- 新 Pod
扩展信息
- 回滚:
kubectl rollout undo deployment/<name>(回退到上一个版本);kubectl rollout history(看版本历史);kubectl rollout status(看更新进度)。 ReplicaSet保留:revisionHistoryLimit(保留几个旧ReplicaSet版本用于回滚,默认 10)。- 灰度发布更精细:滚动更新是逐步替换;更精细的金丝雀/灰度用流量控制(
Istio/Argo Rollouts)按比例分流,滚动更新是“全部切新、分批”,灰度是“少量试新、比例涨”。 - 参考:
kubectl rollout status(看更新进度)/kubectl rollout history(看版本)/kubectl rollout undo(回滚)、kubectl rollout restart(强制滚动重启);查看Deployment用kubectl get deployment(rollout是命令组非资源,不可get rollout);strategy(RollingUpdate/Recreate)。
- 回滚:
🤔 Deployment 如何控制滚动更新的快慢?
Deployment滚动更新的快慢由几个参数控制:maxSurge(最多超额几台,决定“加新速度”)、maxUnavailable(最多允许少几台,决定“删旧速度”)、minReadySeconds(新 Pod 稳定多久才算就绪)、progressDeadlineSeconds(更新超时判定失败)。想快:调大这两个配额、缩短就绪校验等待(minReadySeconds);想稳(保可用):maxSurge:1, maxUnavailable:0+ 拉长就绪校验。核心:“配额定增减速度、就绪判稳不稳、超时定成败”。控制“加新/删旧”速度(两个配额)
maxSurge:最多超额多少副本(控制“一次新增多少新 Pod”)——2= 一次加 2 台新的,更快;0= 不加新先删旧(快但可能短暂降级)。maxUnavailable:更新期间允许不可用的副本上限(作为削旧的天花板,非固定批次)——0= 不删旧(先全加新的再删,最稳但慢);判断标准是可用数 ≥ 期望副本数 −maxUnavailable。- 两者组合决定节奏:
maxSurge:1,maxUnavailable:0(先加后删,保 100% 可用,慢但稳);maxSurge:0,maxUnavailable:1(先删后加,资源省,可短暂降级)。
控制“新 Pod 稳定性判断”(
minReadySeconds)minReadySeconds:新 Podreadiness通过后再等多少秒才算真正稳定(可用)——防止“刚就绪又崩”的 Pod 被计入可用,更新继续。设大(如 30s)更稳(等新 Pod 稳定观测期),设 0 更快(就绪即算)。
控制“更新是否算失败”(
progressDeadlineSeconds)progressDeadlineSeconds:整个滚动更新在多少秒内必须完成(默认600s);超时未完成,Deployment在status.conditions标记Progressing=False(reason: ProgressDeadlineExceeded)判定更新失败,可回滚。
一句话理解
- 滚动更新快慢 = “换班速度”:
maxSurge定“一次多招几个新人”,maxUnavailable定“一次赶走几个老人”,minReadySeconds定“新人要实习多久才算能顶班”,progressDeadlineSeconds定“整个换班要在多少分钟内完成,超时算失败”。
- 滚动更新快慢 = “换班速度”:
协助记忆
- 口诀:“maxSurge 定加新、maxUnavailable 定删旧、minReadySeconds 定稳不稳、progressDeadlineSeconds 定成败——想快调大配额/缩短间隔,想稳保 100% 可用”。
- 一句话:“配额定快慢、就绪定稳、超时定成败——慢而稳 vs 快而险”。
进阶思考
- 想“最快”更新但又不能影响业务,怎么配?
maxSurge:2, maxUnavailable:0(一次加 2 台新、不删旧直到新就绪)——快(批量加)且可用不降(先加后删);但如果节点资源紧(超额 2 台占资源)要权衡。核心业务建议maxUnavailable:0(不中断),想更快调大maxSurge。
- 更新卡住(
progressDeadlineSeconds超时)说明什么?- 说明新 Pod 一直没就绪/反复崩溃(
readiness不过),更新推进不下去——查新版本配置/镜像/依赖;kubectl rollout status看卡在哪一步,必要时kubectl rollout undo回滚。
- 说明新 Pod 一直没就绪/反复崩溃(
- 想“最快”更新但又不能影响业务,怎么配?
扩展信息
- 常用命令:
kubectl rollout status deployment/<name>(看进度)、kubectl rollout undo deployment/<name>(回滚)、kubectl rollout restart(强制重启)、kubectl rollout history(版本历史)。 strategy配置:strategy.type: RollingUpdate(默认,滚动)+strategy.rollingUpdate: {maxSurge, maxUnavailable}——与Recreate(先删光再建,停机)对比,RollingUpdate是不中断的优雅更新。- 灰度发布进阶:滚动更新是“逐步全量”;要“按比例灰度(先 5% 试新再放大)”用
Argo Rollouts/Istio流量分流——滚动更新适合版本升级,灰度适合要精细控流的场景。
- 常用命令:
🤔 Deployment 与 ReplicaSet 有什么联系?
Deployment和ReplicaSet是“上下级/包装”关系:Deployment管理/包装ReplicaSet,ReplicaSet管理具体Pod副本数。你定义Deployment(声明多少副本 + 镜像版本),Deployment自动创建ReplicaSet,ReplicaSet再维持Pod副本数——所以平常你只碰Deployment,ReplicaSet是它内部自动管理的“工具”。核心:“Deployment 管版本 + ReplicaSet 管副本数”。各自的职责(层次关系)
ReplicaSet(底层):维持指定数量的Pod副本(“保证永远有 N 个 Pod 在跑”)——是“副本数控制器”,只管量不管版本。Deployment(上层):管理 Pod 的“版本/声明式更新”——描述“要什么镜像、几个副本”,内部通过ReplicaSet实现;还负责滚动更新、回滚、版本历史。- 关系:
Deployment→ 创建/管理 一个或多个ReplicaSet→ 每个ReplicaSet管自己模板的Pod副本(不同版本各一个ReplicaSet)。
滚动更新时两者的配合(关键)
- 版本升级时,
Deployment新建一个ReplicaSet(新镜像版本),新ReplicaSet起新Pod,旧ReplicaSet逐渐缩到 0——新旧版本各对应一个ReplicaSet,Deployment控制它们之间的切换(这就是滚动更新的底层)。 - 回滚 = 让旧
ReplicaSet重新扩起来(Deployment保留历史ReplicaSet供回滚)。
- 版本升级时,
一句话理解
- 像“连锁店总部(Deployment)管分店,分店(ReplicaSet)管事”:总部(
Deployment)决定“要几家店、卖什么菜(版本)”,分店(ReplicaSet)负责“保证每家店的员工够(副本数)”。总部管版本和更新,分店管人数,更新就是“新开一批店、关一批旧店”。
- 像“连锁店总部(Deployment)管分店,分店(ReplicaSet)管事”:总部(
协助记忆
- 口诀:“Deployment 管版本 + 更新,ReplicaSet 管副本数——Deployment 包装 ReplicaSet,ReplicaSet 维持 Pod 数”。
- 一句话:“平时只折腾 Deployment(声明要几个 + 什么版本),ReplicaSet 是它内部自动管 Pod 数量的”。
进阶思考
- 为什么有
ReplicaSet还要Deployment(为何不直接用 ReplicaSet)?ReplicaSet只能“维持副本数”,不能做版本更新/回滚——改镜像要重建ReplicaSet,无法平滑滚动、无法记录版本回滚。Deployment在其上加了声明式版本管理(滚动更新、回滚、历史),所以生产用Deployment而不是裸ReplicaSet。
- 改镜像时
Deployment会怎么处理 ReplicaSet?- 改
Deployment的镜像 →Deployment对照当前版本,创建一个新的ReplicaSet(新模板),新ReplicaSet起新 Pod、旧ReplicaSet缩;滚动完成后旧ReplicaSet缩到 0 但保留(revisionHistoryLimit控制保留几个,供回滚)。
- 改
- 为什么有
扩展信息
revisionHistoryLimit:保留几个旧版本ReplicaSet(默认10),供rollout undo回滚到历史版本——回滚不了太早的版本(被清理)。Deployment与ReplicaSet的关系:Deployment通过OwnerReference(控制器引用)管理ReplicaSet;pod-template-hash标签用于区分版本、并被ReplicaSet的selector用来匹配自己的Pod(Deployment的spec.selector实际匹配的是Pod)。- 参考:
kubectl get rs(看ReplicaSet)、kubectl get deployment、kubectl get pods——三层关系(Deployment→ReplicaSet→Pod)。
🤔 Deployment 与 StatefulSet 有什么区别?
Deployment管无状态应用(Pod无固定身份/数据,可随意删换、并行扩缩);StatefulSet管有状态应用(Pod有固定网络标识、持久化存储、有序部署/缩容)。核心区别:Deployment的 Pod 是“无名小卒”(随时换),StatefulSet的 Pod 是“有头有脸的固定员工”(名字固定、数据固定、按顺序来)。核心区别对比
- 是否固定身份:
Deployment的 Pod 名字随机(web-xxx),无固定身份;StatefulSet的 Pod 名字固定(web-0/web-1),有稳定网络标识(便于集群内互相发现)。 - 是否持久化数据:
Deployment默认无状态(数据不持久,Pod 挂了数据丢);StatefulSet给每个 Pod 配专属存储卷(PVC),数据持久、Pod 重建后数据还在(web-0重建仍用web-0的卷)。 - 部署/缩容顺序:
Deployment并行(同时起/删,随意);StatefulSet有序(0→1→2顺序起、2→1→0顺序删),保证启动/停止有先后(适合主从/依赖关系)。 - 用途:
Deployment无状态服务(web/API),StatefulSet有状态应用(MySQL/Redis/Kafka/Elasticsearch等数据集群)。
- 是否固定身份:
StatefulSet的独特能力- 固定网络标识:
Pod名固定(web-0),配Headless Service(无ClusterIP)让集群内其他 Pod 用固定名字访问它(如web-0.nginx)——适合有固定地址的对等服务。 - 专属持久卷:每个
Pod绑定自己的PVC(volumeClaimTemplate),保证数据持久且对应固定Pod。 - 有序启停:按
replicas顺序启动/停止,配合podManagementPolicy(OrderedReady/Parallel)。
- 固定网络标识:
一句话理解
Deployment像“临时工”——没有固定工位(名字随机)、不留文件(无状态),随时换人;StatefulSet像“正式工”——有固定工号(web-0)、有自己的办公桌/资料柜(专属持久卷)、按顺序入职/离职(有序)——适合数据库/有状态集群。
协助记忆
- 口诀:“Deployment 无状态(随机名/不持久/并行),StatefulSet 有状态(固定名/持久卷/有序)”。
- 一句话:“有状态有固定名字和数据、按顺序来用 StatefulSet;无状态随便换用 Deployment”。
进阶思考
- 数据库集群为什么必须用
StatefulSet?- 数据库(如
MySQL主从/Kafka/ES)需要固定网络地址(集群内其他节点按名访问)、持久存储(数据不能丢)、有序启停(主从/副本有依赖顺序)——StatefulSet的固定Pod名 + 专属卷 + 有序正好满足;Deployment随机名/无状态会破坏数据库集群的节点识别和数据持久。
- 数据库(如
StatefulSet能不能扩缩容(有状态不方便扩)?- 能,但要有序(按
0/1/2...编号扩)、逐个(不是并行)——因为有状态应用扩缩要通知集群(加节点/移除节点);且缩容到x后原来x+1的卷要处理。所以有状态扩缩更慢、更谨慎,不像Deployment并行随意。
- 能,但要有序(按
- 数据库集群为什么必须用
扩展信息
Headless Service:StatefulSet常用它(clusterIP: None),让 Pod 有稳定的 DNS 名(如pod-0.svc)——集群内相互发现固定节点。volumeClaimTemplate:StatefulSet里给每 Pod 定义PVC模板(每个 Pod 自动申请,web-0→pvc-0、web-1→pvc-1)——数据按 Pod 隔离持久。podManagementPolicy:OrderedReady(默认,有序启停)/Parallel(并行,适合无依赖有状态)。- 参考:
kubectl get statefulset、kubectl get pvc(看每 Pod 的卷)、Gateway/Istio对有状态服务的支持。
🤔 StatefulSet 为什么用 Headless Service?
StatefulSet用Headless Service是为了让每个Pod有稳定的网络身份(DNS名):StatefulSet的Pod名字固定(web-0/web-1),配合Headless Service(clusterIP: None,不分配虚拟 IP)能按名字直接解析到具体某个Pod(web-0.nginx.svc),供有状态集群(如数据库)内相互发现/连接固定节点。核心:“普通 Service 给一组 Pod 一个总入口,Headless Service 给每个 Pod 一个固定的名字”。普通
ServicevsHeadless Service- 普通
Service:有一个虚拟ClusterIP,访问它会被负载均衡到任意一个后端Pod(不关心谁)——适合无状态应用(哪个实例都能服务)。 Headless Service:clusterIP: None(不分配ClusterIP),DNS直接解析到每个Pod的真实IP(返回所有 Pod 地址)——客户端自己选/直连某个具体 Pod。
- 普通
为什么
StatefulSet需要它(关键)- 有状态应用要求固定节点身份:数据库/
Cassandra/ZK/ES集群中,节点要知道彼此的固定地址(互相发现、主从关系、web-0就是web-0)——普通Service负载均衡返回随机节点,破坏这种固定身份。 - 按名字直连固定 Pod:
Headless Service让web-0有稳定的 DNS 名(web-0.nginx),其他 Pod/外部能按名直达某个具体节点(如访问主节点),这是有状态集群(Quorum/主从)必需的。 - 配合
StatefulSet的 stable network ID:StatefulSet保证Pod名稳定,Headless Service让这个名字可被 DNS 解析——两者配合实现了“固定名字 + 固定地址”的稳定网络身份。
- 有状态应用要求固定节点身份:数据库/
一句话理解
- 普通
Service= “小区大门”(进出都从这里,随便哪个单元都行);Headless Service= “每家的门牌号”(web-0/web-1各有专属地址,能点名找特定一家)——StatefulSet的住户(有状态节点)需要互知道门牌号,所以用Headless。
- 普通
协助记忆
- 口诀:“StatefulSet 固定 Pod 名 + Headless Service 固定 DNS 名——有状态节点按名互相发现”。
- 一句话:“普通 Service 给一群 Pod 一个入口,Headless 给每个 Pod 一个名字——有状态集群要按名找节点”。
进阶思考
Headless Service下 DNS 怎么解析(客户端怎么知道所有 Pod)?- 集群
DNS(CoreDNS)对Headless Service:查服务名返回同一A记录指向所有Pod的IP(客户端能枚举),或SRV记录;而web-0.nginx/web-1.nginx这类逐Pod的A记录由StatefulSet的serviceName单独生成——两类记录来源不同(注意区分)。
- 集群
- 有状态应用都必须要
Headless Service吗?- 有状态应用(数据库集群)通常需要(节点间固定发现);但如果应用不依赖“节点固定身份”访问(如只是每 Pod 独立服务、无集群协商),用普通
Service也行。Headless是“要按名访问具体节点”时的选择。
- 有状态应用(数据库集群)通常需要(节点间固定发现);但如果应用不依赖“节点固定身份”访问(如只是每 Pod 独立服务、无集群协商),用普通
扩展信息
clusterIP: None:Service的spec.clusterIP: None即Headless——不分配虚拟IP,DNS直达Pod。publishNotReadyAddresses:默认Headless Service不把未就绪Pod的地址发布(readiness才见),可设true发布不健康 Pod(调试用)。- 有状态服务场景:
Cassandra/Elasticsearch/ZooKeeper/etcd/MySQL主从——都需要节点按固定名字相互发现(peer发现),是Headless Service+StatefulSet的典型组合。 - 参考:
kubectl get svc看Service(Headless的CLUSTER-IP显示None)、kubectl get endpoints看后端Pod地址、nslookup web-0.nginx(DNS 解析)。
🤔 Service 有哪些功能?
Service是让一组Pod对外提供稳定访问入口的抽象,核心功能:①服务发现(给 Pod 组一个稳定ClusterIP/DNS名)②负载均衡(把请求分发到后端多个 Pod)③Pod自动关联(按标签选择器找 Pod,Pod 增减自动更新)④暴露访问(对外提供ClusterIP/NodePort/LoadBalancer等入口)。核心:“Service = Pod 组的‘总机’——稳定入口 + 负载分发 + 自动关联后端”。① 服务发现(稳定入口)
- 给一组
Pod一个固定的ClusterIP(虚拟IP)和DNS名(如my-svc)——即使后端Pod换了IP(重建),客户端始终访问同一个Service名,不受影响。
- 给一组
② 负载均衡(请求分发)
- 把访问
Service的请求分发到后端多个Pod——kube-proxy实现(默认iptables模式用随机概率分发,IPVS模式支持轮询/加权等),应用的多个副本分摊流量,不单点。 Service维护后端Pod列表(Endpoint/EndpointSlice),只把流量发给**就绪(readiness)**的 Pod。
- 把访问
③
Pod自动关联(标签选择器)Service用标签选择器(selector)匹配一组Pod(如app: nginx)——Pod 按标签自动被Service纳入/剔出,增减无需改Service。Pod挂了/扩缩,Service自动更新后端列表(Endpoint增删)——自动跟随。
④ 暴露访问(多种类型)
ClusterIP:仅集群内访问(默认)NodePort:节点端口暴露(集群外可访问)LoadBalancer:云厂商 LB(公网)ExternalName:映射外部域名——按“需不需要公开/怎么公开”选类型。
一句话理解
Service像“公司总机(前台)”——你有固定总机号(ClusterIP/DNS),打进来(请求)前台分给对应员工(Pod,负载均衡);员工离职/新来(Pod 增减),总机自动更新花名册(标签选择器);外线打进分内线/直拨(NodePort/LoadBalancer)。
协助记忆
- 口诀:“Service = 服务发现(稳定名)+ 负载均衡(分请求)+ 自动关联(按标签)+ 暴露入口(多种类型)”。
- 一句话:“给 Pod 组一个固定入口,自动分发、自动跟随 Pod”。
进阶思考
Service用Selector匹配 Pod,那Pod挂了怎么办?Service通过Endpoint(EndpointSlice)记录后端PodIP;Pod挂/readiness失败 → 从Endpoint剔除(不再分发流量),Deployment重建后新Pod自动加回——Service自动跟随Pod健康状态,无需人工干预。
Service和Ingress区别(都能暴露访问)?Service是四层(L4,TCP/UDP负载均衡,内部/同一层暴露);Ingress是七层(L7,HTTP路由:域名/路径转后端Service),是集群入口(更贴近 HTTP 应用)。常组合:Ingress(域名路由)→ 后端Service(负载均衡)→Pod。
扩展信息
Endpoint/EndpointSlice:Service关联后端的“地址列表”(Pod IP+端口);EndpointSlice是更可扩展的新版(大数据量)。kube-proxy:Service的负载均衡由每个节点上的kube-proxy实现(监听Service/Endpoint变化,配iptables/IPVS规则转发)——Service是“声明”,kube-proxy是“实现转发”。DNS(CoreDNS):集群内Service按名解析(my-svc.namespace.svc.cluster.local)——服务发现依赖DNS。- 参考:
kubectl get svc、kubectl get endpoints、ServiceYAML 的selector/ports/type。
🤔 Service 有哪几种公开类型及应用场景?
Service四种公开类型:ClusterIP(集群内访问,默认)、NodePort(节点端口暴露,集群外可访问)、LoadBalancer(云厂商LB公网)、ExternalName(映射外部域名)。核心:“按要不要公开、怎么公开选——内部用 ClusterIP、简单外露用 NodePort、生产公网用 LoadBalancer、连外部用 ExternalName”。ClusterIP(默认)- 分配集群内虚拟
IP,仅集群内可访问(其他 Pod/节点可访问,集群外不行)——最常用,内部服务间调用。 - 适用:集群内部服务(
web调用api、微服务间通信)——不需要对外暴露。
- 分配集群内虚拟
NodePort(节点端口暴露)- 在每个节点上开放一个固定端口(
30000-32767),通过**节点IP:NodePort**访问——集群外能访问(用节点端口)。 - 适用:简单对外暴露(测试/小规模)、无云 LB 的环境、临时/直连访问。
- 局限:依赖节点
IP(节点变IP变)、端口范围固定、无负载均衡器(要配外部负载)。
- 在每个节点上开放一个固定端口(
LoadBalancer(云厂商负载均衡)- 云厂商分配公网负载均衡器(
LB),自动路由到Service后端——生产对外公开,自带公网 IP/负载均衡。 - 适用:云上生产对外暴露(
web公网入口、API网关)——云的LB自动管理(阿里/AWS ELB)。 - 前提:云环境(或实现
LoadBalancer的控制器,如MetalLB私有云)。
- 云厂商分配公网负载均衡器(
ExternalName(映射外部域名)- 不分配
IP,直接映射一个外部域名(DNS别名)——让集群内访问Service名实际到外部服务(如外部API/数据库)。 - 适用:把外部服务“伪装成集群内服务”调用(访问外部
API/第三方),无需改代码。
- 不分配
一句话理解
- 四种公开类型 = “房子对外怎么联络”:
ClusterIP= 只给小区内(内网)电话(内部服务);NodePort= 每栋楼开个对外窗口(节点端口,简单外露);LoadBalancer= 请专业前台(云LB,公网入口);ExternalName= 挂个“本店电话→实际是外面餐厅”的牌子(映射外部)。
- 四种公开类型 = “房子对外怎么联络”:
协助记忆
- 口诀:“内部用 ClusterIP、简单外露 NodePort、生产公网 LoadBalancer、连外部 ExternalName”。
- 一句话:“内网 ClusterIP、外露 NodePort、云 LB LoadBalancer、映射外部 ExternalName”。
进阶思考
NodePort和LoadBalancer什么关系(不是对立)?LoadBalancer是建立在NodePort基础上的——云LB把流量转发到各节点的NodePort,再进Service。所以LoadBalancer相当于“NodePort+ 一个云LB转发到所有节点”,生产公网用LoadBalancer(省手工配LB),无云环境先用NodePort+ 外部LB。
ClusterIP: None(Headless)算一种类型吗?- 算
ClusterIP的特殊形态:clusterIP: None不分配IP,DNS直达 Pod——StatefulSet用(固定节点名)。它和ExternalName都“无IP”,但Headless是集群内按 Pod 名解析,ExternalName是映射外部域名。
- 算
扩展信息
NodePort端口范围:默认30000-32767(api-server参数配置);NodePort的Service也可配置clusterIP(通常NodePort会连带ClusterIP)。LoadBalancer与MetalLB:私有云/裸金属没云LB,用MetalLB(开源LoadBalancer实现)给Service分配LB。IngressvsService暴露:Service是L4层暴露(NodePort/LB端口级);要HTTP/域名/路径级路由用Ingress(L7,走域名到后端Service)——对外公开的首选是Ingress+Service组合。- 参考:
ServiceYAML 的type(ClusterIP/NodePort/LoadBalancer/ExternalName)、kubectl get svc看类型/端口。
🤔 kube-proxy 有几种代理模式及原理?
kube-proxy是每个节点上实现Service网络转发的组件,主要有内核态的iptables(默认,用iptables规则做概率转发)、IPVS(高性能,用IPVS内核支持轮询/加权等)、nftables(新,v1.33起GA)三种,另有已移除的旧版userspace。核心:“kube-proxy把 Service 的请求转发到后端 Pod——iptables 默认、IPVS 高性能、nftables 新、userspace 旧”。三种代理模式
iptables(默认,最常见):用内核iptables规则把访问Service的流量随机/概率转发到后端Pod。功能够用、无需额外模块;但规模大时iptables规则多、性能略降(O(n)链)。IPVS(高性能,大集群推荐):用内核IPVS模块做负载均衡,支持**轮询(rr)/加权(wrr)/最少连接(lc)/源地址哈希(sh,会话保持)/一致性哈希(maglev)**等多种调度算法,性能更高、可扩展性好(大集群/高并发用)——sh是源地址哈希、maglev才是一致性哈希。userspace(用户态,旧版):在用户态做代理转发(kube-proxy自己转发),性能差;自v1.23已弃用、v1.26从核心移除(--mode userspace直接报错)。当前内核态可用iptables(默认)/ipvs/nftables三种(nftables:v1.29alpha、v1.31beta、v1.33GA)。
工作原理(
iptables/IPVS都要做的)kube-proxy监听Service和Endpoint(EndpointSlice)变化,动态更新转发规则——Pod/Service增减时自动收集后端PodIP,维护“哪些 Pod 属于这个 Service”。- 收到访问
Service的流量 → 按转发规则分发到某个后端Pod(iptables随机概率 /IPVS按算法)→ 实现负载均衡。 - 只发给就绪 Pod:
Endpoint只记录readiness通过(就绪)的 Pod,readiness失败自动剔除(不转发)。
IPVSvsiptables怎么选- 小/中规模、默认简单 →
iptables(默认,够用);大集群、高并发、要多种调度算法/性能 →IPVS(--proxy-mode=ipvs)。
- 小/中规模、默认简单 →
一句话理解
kube-proxy像“每层楼的传菜员(分信员)”——它盯着“哪些桌(Pod)属于哪桌预约(Service)”,外卖(请求)来了按规则分给对应的桌(后端 Pod)。iptables是随机分、IPVS是更智能的“轮流分/按权重分”,userspace是慢吞吞的人工分(旧)。
协助记忆
- 口诀:“iptables 默认(随机概率)、IPVS 高性能(轮询/加权)、userspace 旧版——大集群高并发选 IPVS”。
- 一句话:“kube-proxy 把 Service 流量分给后端 Pod——默认 iptables、要性能用 IPVS”。
进阶思考
- 为什么
userspace被淘汰?- 它在用户态转发(数据包要
kube-proxy进程处理后再转),性能差、开销大;iptables/IPVS都在内核处理转发(快、少拷贝)。所以现代用内核态(iptables/IPVS),userspace成了历史。
- 它在用户态转发(数据包要
IPVS支持哪些调度算法(比 iptables 强在哪)?IPVS支持rr(轮询)、wrr(加权轮询)、lc(最少连接)、sh(源地址哈希)、maglev(一致性哈希)等——比iptables的“随机概率”语义更明确(轮询/加权/最少连接可控),且性能高、扩展好。
- 为什么
扩展信息
- 配置:
kube-proxy的--proxy-mode(iptables/ipvs/userspace);IPVS调度器--ipvs-scheduler(默认rr)。 kube-proxy与Service关系:Service是声明(期望的稳定入口 + 后端),kube-proxy是实现(在节点上把Service流量转给后端Pod)——两者配合才让Service真正可用。EndpointSlice:Endpoint的新版(分片,大数据量更可扩展),kube-proxy监听它以更新转发。- 参考:
kubectl get endpoints(看后端Pod)、kubectl describe svc(看类型/端口)、kubectl get pods -o wide(看 PodIP)。
- 配置:
🤔 Endpoint 资源有什么作用?
Endpoint记录“Service的后端Pod地址列表”——即Service实际把流量转发到哪些Pod(Pod IP+ 端口)。核心作用:Service通过Endpoint知道“后端有哪些 Pod”,并把流量只发给就绪的 Pod;Pod增减/健康变化自动更新。核心:“Endpoint是 Service 的后端通讯录”。Endpoint是什么- 一个
Service关联一个Endpoint列表(Pod IP+ 端口),是Service与Pod之间的“绑定”体现:Service按标签选择器找到Pod→ 生成Endpoint(记录这些Pod地址)。 - 结构:
Endpoints资源(name对Service)包含一组addresses(Pod IP)+ports。
- 一个
作用(关键)
- 告诉
Service后端在哪:kube-proxy读Endpoint知道访问Service的流量转发到哪些Pod(IP:Port)——没有Endpoint,Service不知道转发去哪(空Endpoint= 无后端,访问失效)。 - 自动跟随 Pod 健康:
Endpoint只含**就绪(readiness通过)**的 Pod;readiness失败/Pod挂 → 从Endpoint剔除(不再转发),恢复后加回;Pod增减自动更新——Service总是转发给健康 Pod。 - 负载均衡的依据:
Endpoint里的多个Pod地址就是kube-proxy负载均衡的分发目标(多副本分摊流量)。
- 告诉
EndpointvsEndpointSlice- 老的是
Endpoints(一个Service一个,后端Pod多时可能很大);新的是EndpointSlice(把Endpoint分片,按label/容量切分,大数据量更可扩展)——现代版本默认用EndpointSlice。
- 老的是
一句话理解
Endpoint= “前台的花名册”:前台(Service)按这本花名册知道“哪些员工(Pod)在岗、能接客”,只把电话(请求)转给在岗能接的(就绪 Pod);有人请假(readiness 失败)划掉名字、新入职(新 Pod)加上——花名册自动更新,Service永远联系到健康员工。
协助记忆
- 口诀:“Endpoint = Service 的后端通讯录——记录 Pod 地址、只含就绪 Pod、自动增减”。
- 一句话:“Service 靠 Endpoint 知道后端有哪些 Pod,只转发给健康的”。
进阶思考
Endpoint为空(kubectl get endpoints空)说明什么?- 说明
Service的标签选择器没匹配到任何就绪 Pod——可能:①Pod标签不匹配Service的selector②Pod都未就绪(readiness没过)③没有Pod运行。此时访问Service会失败(无后端可转发),用kubectl get pods/describe 排查。
- 说明
- 为什么
EndpointSlice取代了Endpoint?- 一个
Service后端Pod成千上万时,单个Endpoints资源很庞大(主要施压API Server及kube-proxy等 watcher);EndpointSlice把地址分片(默认100/片,可上调、max 1000)、按标签管理,减轻API Server负载、更可扩展——EndpointSlicev1.19起由kube-proxy默认消费(beta)、v1.21起GA(Endpoints自v1.33起正式标记废弃,此前仅是事实上弃用;新集群实际由EndpointSlice承担)。
- 一个
扩展信息
EndpointSlice特性:按address/port/label分片,默认100个地址/片(可上调、max 1000),label: kubernetes.io/managed-by=endpointslice-controller;kubectl get endpointslices查看。Service关联条件:Endpoint/EndpointSlice由EndpointSlicecontroller 根据Service的selector(标签选择器)自动生成/更新——Service定义后自动有Endpoint。- 无
selector的 Service:可手动配Endpoint(ExternalName/LoadBalancer等特殊场景),或不自动关联 Pod。 - 参考:
kubectl get endpoints、kubectl get endpointslices、kubectl describe svc(看Endpoints)。
🤔 K8s 集群安装的网络插件有什么作用?
K8s网络插件(CNI插件)负责实现集群内的 Pod 网络——让每个 Pod 有独立 IP、能相互通信、能与节点/集群外通信。K8s本身不管网络实现,需要可插拔的CNI网络插件(如Calico/Flannel/Cilium)来提供:①Pod 间通信 ②Pod 与节点/外网通信 ③网络策略(隔离/安全)。核心:“K8s 只规定网络模型,实际建网靠 CNI 插件”。为什么需要网络插件(K8s 的网络要求)
K8s要求每个 Pod 有独立的IP(Pod IP),且 Pod 间、Pod 与节点间要互通(kubelet按模型管理)——但实际“怎么把网络搭起来 / 怎么路由 / 怎么隔离”K8s不管,交给CNI(Container Network Interface)插件。- 没有网络插件,
Pod无法互相通信、无法分配 IP——集群网络不可用。
网络插件的作用
- ① 给 Pod 分配 IP + 建网:每个 Pod 分配独立
Pod IP(CNI管理 IP 池),并在节点上建虚拟网络让 Pod 互通(veth/overlay/BGP等)。 - ② Pod 间/跨节点通信:让不同节点的 Pod 能互通(覆盖网络 over 底层,或
BGP直连)——集群内全局互联。 - ③ 网络策略(安全隔离):
NetworkPolicy定义“哪些 Pod 能/不能通信”(东西向流量隔离/白名单),由CNI实现(Calico/Cilium支持)。 - ④ 与服务发现/
kube-proxy配合:Pod 的 IP/网络连通是Service/kube-proxy转发的基础。
- ① 给 Pod 分配 IP + 建网:每个 Pod 分配独立
常见 CNI 插件(选型)
Calico:BGP路由 + 网络策略强(安全隔离好),生产常用(也支持VXLAN/IPIP覆盖和eBPF数据面)。Flannel:简单(overlayVXLAN),部署快,网络策略弱/无。Cilium:eBPF高性能(现代),网络策略 + 可观测强,云原生趋势。Weave/Kube-router:其他可选(各有特点)。
一句话理解
- 网络插件像“小区的水电煤管网施工队”——
K8s规定“每户要有水有电、户与户要通”(网络模型),施工队(CNI插件)真正把管网铺起来(建网/路由/隔离)。没有施工队,家家都“断网”。
- 网络插件像“小区的水电煤管网施工队”——
协助记忆
- 口诀:“CNI 插件管 Pod 网络——分配 IP、建网互通、网络策略隔离;常见 Calico(策略强)/Flannel(简单)/Cilium(eBPF 高性能)”。
- 一句话:“K8s 规定网络模型,CNI 插件实际建网 + 隔离”。
进阶思考
Flannel和Calico怎么选?Flannel:简单(overlay,部署快、适合小集群),但网络策略弱(无/需额外支持);Calico:功能强(BGP路由 + 网络策略完善),适合生产/需要安全隔离(东西向白名单)的集群。要网络策略 →Calico/Cilium;只要通畅通信 →Flannel。
NetworkPolicy一定需要网络插件支持吗?- 是。
NetworkPolicy是声明,实现要靠 CNI(支持网络策略的:Calico/Cilium/Weave;不支持:Flannel默认无)。没用支持策略的插件,NetworkPolicy不生效(默认全通)。所以要有隔离能力要选支持的网络插件。
- 是。
扩展信息
CNI(Container Network Interface):K8s容器网络的标准接口——CNI插件(calico/flannel/cilium)实现这个接口给 Pod 建网;K8s通过CNI调用它们。- 网络模式:
overlay(覆盖网络,VXLAN,跨节点封装——Flannel默认)、BGP路由(Calico默认,直接路由)、eBPF(Cilium,内核加速)——实现方式不同,性能/复杂度不同。 kube-proxy与网络插件关系:kube-proxy做Service的L4转发(iptables/IPVS),网络插件做 Pod 底层网络互通——两者配合才通。- 参考:
kubectl get nodes -o wide(看节点/网络)、calicoctl/cilium命令、kubectl get networkpolicy(看策略)。
🤔 Calico 有哪几种工作模式及原理?
Calico是一种CNI网络插件(网络 + 网络策略),主要工作模式:BGP(路由分发协议,默认直接路由三层)、VXLAN/IPIP(可叠加在 BGP 上的覆盖封装,跨网络时用)、eBPF(独立数据面,内核加速,现代高性能)。核心:“BGP 是路由协议、IPIP/VXLAN 可叠加其上、eBPF 是独立高性能数据面”。三种工作模式(重点)
BGP模式:用BGP协议在节点间直接交换路由(三层)——Pod 间通信直接路由,性能高、延迟低;适合同网络/可路由的环境。注意默认是否封装取决于ipipMode/CALICO_IPV4POOL_IPIP(旧版 manifest 默认Always=BGP 全 mesh+IPIP封装;设Never才是纯直连路由)。VXLAN/IPIP模式(overlay 覆盖):用封装隧道(VXLAN/IPIP把 Pod 包封装后跨节点传输)——适合跨网络/不能三层互通的环境(如跨机房/云),代价是封装开销(性能略降)。eBPF模式(现代高性能):用eBPF(内核可编程)做数据面(数据转发、网络策略)——性能极高、细粒度、可观测强;Cilium也走这个方向,需要较新内核。
工作原理(
BGP模式核心)- 每个节点上运行
Calico组件(Felix数据面 agent)/BIRD(BGPdaemon)/confd——把节点的Pod 网段通过BGP通告给其他节点,每个节点都知道其他节点的 Pod 子网路由(注意kube-router是独立 CNI,非Calico组件)。 - 数据包(Pod→Pod)直接按
BGP路由在节点间转发(不封装),iptables/eBPF做网络策略(允许/拒绝哪些流)。 - 网络策略:
NetworkPolicy由Calico实现(Felix把策略转成iptables/eBPF规则)——BGP层做路由、策略层做隔离。
- 每个节点上运行
怎么选
- 网络互通好(同机房/同网络)→
BGP(快);跨网络/子网不通 →VXLAN/IPIP(overlay);追求极致高性能 + 细粒度策略/可观测 →eBPF(需新内核)。
- 网络互通好(同机房/同网络)→
一句话理解
Calico像“快递网络”:BGP模式 = 各个分站点直接互通(自己开专线,快);VXLAN/IPIP= 跨区走中转封装(打包投递,保通达但要封装费力);eBPF= 用智能机器人分拣(加速)。
协助记忆
- 口诀:“BGP 默认直连(快)、VXLAN/IPIP 走覆盖(跨网)、eBPF 内核加速(现代高性能);网络策略靠它做隔离”。
- 一句话:“同网选 BGP、跨网用覆盖、要极速上 eBPF”。
进阶思考
Calico和Flannel关键区别?Calico:BGP直连 + 网络策略(安全隔离强)——功能全、适合生产/安全要求高;Flannel:overlay(VXLAN)简单、无网络策略(或弱)——简单、适合小集群/只要通。要隔离/安全选Calico,只要网络通选Flannel。
- 为什么
BGP模式性能比overlay好?BGP直接三层路由(数据包按路由直达,不封装);overlay(VXLAN)要把 Pod 包再封装一层(UDP封装)传输、接收方解封——多了封装/解封开销(CPU/延迟/包增大)。BGP少这层,性能高但要求网络三层可路由。
扩展信息
Calico组件:Felix(数据面 agent,每节点)、BIRD(BGPdaemon)、calico-node(打包运行的 DaemonSet)、NetworkPolicycontroller。NetworkPolicy支持:Calico默认支持NetworkPolicy(Flannel默认不支持)——这是选Calico做安全隔离的重要理由。- 性能对比:
BGP(默认,性能中高)<eBPF(性能最高、可观测最强,需新内核);VXLAN/IPIP(overlay,性能略低于 BGP)。 - 参考:
calicoctl命令(配置/查看Calico)、kubectl get networkpolicy、kubectl get svc(看 Service,Service的ClusterIP实际由kube-apiserver从--service-cluster-ip-range分配,非Calico)。
🤔 Service 与 Ingress 有什么区别?
Service(L4,四层)给一组 Pod 提供稳定负载均衡入口(ClusterIP/NodePort/LoadBalancer),基于TCP/UDP端口;Ingress(L7,七层)是集群 HTTP 入口,基于域名/路径路由到后端Service。核心区别:Service管“端口负载均衡”,Ingress管“HTTP 域名/路径路由”;Ingress是七层更贴近应用的暴露方式。Service(四层L4)- 位置:集群所有服务的基础;内容:
TCP/UDP端口负载均衡,ClusterIP/NodePort/LoadBalancer。 - 能力:把一个服务(一组 Pod)暴露成一个稳定的地址/端口,请求均衡分发——不关心 HTTP 内容(域名/路径),只按 IP+端口转发。
- 局限:
Service主要在 IP+端口层转发,不按域名/路径路由(HTTP层);可配多端口、均衡到多个后端 Pod,但无法按域名/路径区分服务。
- 位置:集群所有服务的基础;内容:
Ingress(七层L7)- 位置:集群入口(
L7);内容:HTTP/HTTPS,按域名(Host)+路径(Path)路由——一个Ingress可指向多个后端Service。 - 能力:域名/路径级路由、
HTTPS终止(TLS)、按域名分发到不同服务(URL重写等由具体Ingress Controller注解实现,非Ingress内建字段)——更贴近Web应用的暴露。 - 依赖:
Ingress是一个规则声明,真正干活的是Ingress Controller(nginx/Traefik/ALB等),Ingress本身不转发流量。
- 位置:集群入口(
区别与关系
- 层级:
ServiceL4(端口)、IngressL7(HTTP);Ingress底层仍依赖Service(路由到Service再进Pod)。 - 场景:只暴露一个 TCP 服务 →
Service(NodePort/LB);要按域名/路径暴露多个 HTTP 服务 →Ingress(入口)+Service(后端)。 - 组合:
Ingress(域名路由,L7)→ 后端Service(L4负载均衡)→Pod——Ingress管进哪个门带什么 URL,Service管门里的电梯分发到哪层。
- 层级:
一句话理解
Service= 楼里的电梯(按楼层分发,TCP/UDP 端口);Ingress= 大厦前台(按找哪个部门(域名)+什么项目(路径)引导你进哪部电梯)——Ingress是更智能的入口引导,但它进来后还是靠Service(电梯)分发到具体房间(Pod)。
协助记忆
- 口诀:“Service 管四层端口均衡,Ingress 管七层 HTTP 路由(域名/路径);Ingress 底层靠 Service 分发”。
- 一句话:“一个端口暴露用 Service,按域名/路径暴露 HTTP 用 Ingress(+Service)”。
进阶思考
- 为什么不能只用
Service暴露多个 HTTP 服务?Service一个服务一个ClusterIP/端口,多个HTTP服务要么多个NodePort(每服务一个端口,丑)、要么多个LoadBalancer(贵);Ingress一个入口按域名/路径区分多个服务(一个Ingress管多个Service),省端口/资源、更规范。
LoadBalancer和Ingress什么关系?- 云
LoadBalancer(Service类型)是真公网LB;Ingress常配一个LoadBalancer型Service做入口(Ingress Controller前面挂LB)——Ingress(L7路由)在LB(公网4/7层)之后。生产常是:公网LB→Ingress Controller(域名路由)→Service→Pod。
- 云
- 为什么不能只用
扩展信息
Ingress资源:spec.rules(域名/路径到后端Service)、spec.tls(证书)、spec.ingressClassName(指定 controller)。Ingress Controller:nginx-ingress(最常用)、Traefik、Envoy、ALB Ingress(云)——Ingress规则由 controller 实现转发。Gateway API(新):Ingress的现代演进(更灵活/多协议),v1.0GA(2023),生产可选。- 参考:
kubectl get ingress、kubectl get svc、kubectl get ingressclasses(看 controller)。
🤔 Ingress 与 Ingress Controller 的关系?
Ingress(资源)是声明/规则(要求什么域名/路径怎么路由、要不要HTTPS),Ingress Controller是执行者(真正监听Ingress规则并实现转发流量)。Ingress是需求文档,Controller是施工队——你写Ingress声明,Controller(nginx/Traefik等)把规则转换成实际的反向代理配置并干活。核心:“Ingress 声明路由规则,Controller 实现转发”。Ingress(资源,声明)- 一个
K8sAPI 资源(Ingress),只描述想要什么:host(域名)、path(路径)、后端Service、TLS(证书)——是用户要的路由规则。 - 它本身不转发任何流量(
Ingress只是数据声明,存etcd),只描述期望。
- 一个
Ingress Controller(执行者)- 真正干活的组件(集群内的
Deployment/Pod,如nginx-ingress/Traefik/ALB)——监听Ingress资源变化,把Ingress规则翻译成实际的反向代理配置(如nginx的server/location块)并转发流量。 - 工作:①watch
Ingress/Service/Endpoint变化 ②生成/更新代理配置(nginx.conf)③代理转发(按域名/路径路由到后端Service)④reload生效。 - 有
Ingress没Controller:Ingress声明了但没人执行 → 不生效(访问不了)。所以必须安装Ingress Controller(默认集群不装)。
- 真正干活的组件(集群内的
关系(关键)
Ingress= 规则声明(数据),Controller= 规则执行者(程序)——类似 Service 与 kube-proxy、Deployment 与控制器 的关系(声明 vs 实现)。- 一个
Ingress Controller可执行多个Ingress;IngressClass(ingressClassName)指定用哪个Controller。 - 生产必须有
Ingress Controller才能用Ingress(默认集群无,需部署nginx-ingress/Traefik)。
一句话理解
Ingress像消防设计图(写着这里出口、那里通道),Ingress Controller像施工队(真按图纸建门/通道、指挥人流——转发流量)。光有图纸(Ingress)没人施工(无Controller)等于没有;但图纸定规则、施工队照做。
协助记忆
- 口诀:“Ingress 声明路由(域名/路径→Service),Ingress Controller 执行(监听规则、配代理、转发);同 Service 与 kube-proxy 的关系”。
- 一句话:“Ingress 说我要这样路由,Controller 说我来实现——没 Controller,Ingress 白搭”。
进阶思考
- 为什么
Ingress本身不工作,必须有Controller?Ingress只是etcd里的一段数据(描述路由规则),本身无转发能力——需要程序把它变成真实的代理配置(nginx.conf等)并监听端口转发流量,这个程序就是Ingress Controller。所以安装集群后要另装Ingress Controller(如nginx-ingress)才能用Ingress。
- 多个
Ingress Controller会怎样?- 可共存,但每个
Ingress用ingressClassName指定归哪个Controller管(nginx或Traefik)——不同Controller处理不同Ingress(按类分)。不指定则看默认IngressClass。
- 可共存,但每个
- 为什么
扩展信息
IngressClass:ingressClassName指定Ingress用哪个Controller(如nginx/traefik);kubectl get ingressclass查看。- 常见
Controller:nginx-ingress(最主流)、Traefik(轻量)、Envoy/Istio(服务网格)、AWS ALB/GCE(云)。 Gateway API(现代):Ingress+Controller的演进(更标准/多协议),v1.0GA——生产可逐步引入。- 参考:
kubectl get ingress、kubectl get ingressclass、kubectl get pods -n <ingress-ns>(看 controller Pod)。
🤔 Ingress 暴露的应用无法访问如何排查?
Ingress暴露的应用访问不了,按“从外到内层层排查”:①DNS 解析(域名能解析到入口吗)②Ingress入口(Ingress规则/Controller正常吗)③Service后端(Service有Endpoint吗)④Pod健康(后端Pod就绪吗)⑤端口/证书/防火墙。核心:“外→DNS→Ingress→Service→Pod 逐层定位”。第 1 层:域名
DNS解析dig/nslookup看域名能否解析到入口IP(Ingress Controller的LoadBalancerIP/节点IP)——解析不到说明DNS没配/没生效。- 检查:
DNS记录指向Ingress Controller的IP吗?
第 2 层:
Ingress入口本身kubectl get ingress看Ingress规则是否存在、kubectl get ingressclass看Controller是否指定;Ingress Controller的Pod是否Running(kubectl get pods -n <ingress-ns>)。- 直接访问
Ingress Controller的IP+Host头测试(curl -H 'Host: your.domain' http://<ingress-ip>)——能通说明Ingress路由 OK,不通看Controller。
第 3 层:
Service后端kubectl get svc看Ingress指向的Service存在吗、kubectl get endpoints <svc>看有没有后端Pod地址——Endpoint为空说明Service选择器没匹配到 Pod(后端没就绪)。
第 4 层:
Pod健康kubectl get pods/describe看后端Pod是否Running+Ready(readiness通过)——Pod未就绪会被剔除(Endpoint空),访问失败。kubectl exec -it <pod> -- curl localhost:port在 Pod 内测应用本身能访问吗。
其他:端口/证书/防火墙/路径
Ingress的path是否匹配请求路径、TLS证书是否有效、防火墙/安全组是否放行80/443、Host头是否正确。
一句话理解
- 排查像“快递送不到找原因”:先问收货地址对吗(
DNS解析)→ 快递站建了吗(Ingress Controller)→ 分拣表对吗(Ingress规则)→ 分到哪个门牌(Service/Endpoint)→ 那家有人吗(Pod就绪)——层层找断点。
- 排查像“快递送不到找原因”:先问收货地址对吗(
协助记忆
- 口诀:“外→DNS→Ingress(Controller/规则)→Service(Endpoint)→Pod(就绪),逐层排查,kubectl describe/get 看每层状态”。
- 一句话:“从外往里,先 DNS 再 Ingress,Service 没后端看 Pod 就绪”。
进阶思考
Ingress能通但应用仍报错,最可能是哪层?- 通常是后端
Pod没就绪(readiness不过 →Endpoint空 →Ingress转不到)或Ingress规则path/Host不匹配——用kubectl get endpoints看后端、curl -H Host测Ingress。
- 通常是后端
Ingress Controller的Pod起不来(CrashLoop),怎么查?- 看
Ingress Controller的Pod日志(kubectl logs <ingress-pod -n ingress)——常见配置错/ConfigMap问题/端口冲突;Ingress Controller挂了整个入口都通不了,是重点排查对象。
- 看
扩展信息
Ingress Controller用LoadBalancer还是NodePort:云环境LoadBalancer(公网LB)→Ingress;无云NodePort+ 外部LB——看Ingress Controller的Service类型。- 测试命令:
curl -I -H 'Host: example.com' http://<ingress-ip>(带Host头测)、kubectl get ingress -A、kubectl get endpoints -A、kubectl describe ingress。 Host头 &path:Ingress按Host(域名)+path(路径)匹配,请求必须带正确的Host头、path匹配才路由到对应Service。- 参考:
kubectl get ingress/ingressclass、kubectl describe ingress、kubectl get endpoints、kubectl exec(测试后端)。
🤔 Ingress 后端应用无法获取客户端 IP 如何解决?
Ingress后端看到的客户端 IP 是代理(Ingress Controller)的IP而非真实访客 IP,因为Ingress Controller是个反代,转发时默认不传真实 IP。解决:①让Ingress传递客户端 IP 头(X-Forwarded-For/X-Real-IP)②后端应用读这些头而非remote_addr③正确设置代理链(X-Forwarded-For拼接、可信代理)。核心:“Ingress 是反代,真实 IP 在X-Forwarded-For头里(经可信代理校验后取最右不可信 IP),后端要读它”。为什么拿不到真实 IP
Ingress Controller(nginx/Traefik)是反向代理:客户端请求到它,它转发给后端Pod——后端看到的TCP 源地址是Ingress Controller的IP,不是真实访客 IP(类似nginx反代)。
解决思路
- ①
Ingress Controller传递真实 IP 头:nginx-ingress默认会加X-Forwarded-For/X-Real-IP(use-forwarded-headers/proxy_set_header),Ingress后配置转发真实 IP。 - ② 后端应用读
X-Forwarded-For:后端应用取X-Forwarded-For头、而非remote_addr——多数框架支持(如nginx的real_ip_header/set_real_ip_from、后端应用读XFF);注意需经可信代理校验后取值(见下)。 - ③ 正确设置可信代理/反向代理链:
X-Forwarded-For可被客户端伪造(XFF: fake, real取最左就被欺骗),所以要先配可信代理(set_real_ip_from信任Ingress的IP),再从右往左跳过所有可信 IP,取第一个不在可信列表(不可信)的 IP 作为真实客户端——这是nginxrealip模块的正确规则。
- ①
X-Forwarded-ForvsX-Real-IPX-Real-IP:单值,常是最近的代理(Ingress看到的真实 IP)——简单但只有一层。X-Forwarded-For:多值(client IP, proxy1, proxy2...),记录经过的代理链——经可信代理校验后,取最右的不可信 IP 才是真实客户端(信任链全部可信时“最左第一个”才成立;可被伪造时不能直接取最左)。
一句话理解
- 像“写信给前台中间人转交”——前台(
Ingress Controller)替客人(客户端)投递,收到的信只显示“前台寄的”(remote_addr=代理 IP);真正寄信人写在“转发人”栏(X-Forwarded-For)——后端看“转发人栏”要先配可信代理(set_real_ip_from)再取从右往左第一个不可信 IP(信任链全可信时才取最左)。
- 像“写信给前台中间人转交”——前台(
协助记忆
- 口诀:“Ingress 是反代,真实 IP 在 X-Forwarded-For;配可信代理,取最右不可信 IP(可伪造时别直接取最左)”。
- 一句话:“后端拿不到真实 IP 是因为 Ingress 是反代——去读 X-Forwarded-For 头”。
进阶思考
X-Forwarded-For怎么被伪造,怎么防?- 客户端可自己在请求里塞
X-Forwarded-For: fake;代理(Ingress)转发时追加真实 IP,所以要只信任可信代理(set_real_ip_from指定Ingress的IP段),取从右往左第一个不在可信列表里的 IP 作为真实客户端(或信任链最末)。
- 客户端可自己在请求里塞
Ingress来了两层代理(LB→Ingress→Pod),真实 IP 在哪?X-Forwarded-For会累积:client先到LB(LB加XFF: client)→Ingress(追加,XFF: client, lb-ip)→ 后端**从右往左跳过可信代理(lb-ip),取第一个不可信 IP(client)**为真实访客——信任链全可信时取最左才成立。
扩展信息
nginx-ingress配置:proxy-set-headers(ConfigMap配X-Real-IP/X-Forwarded-For)、use-forwarded-headers(从X-Forwarded-For取真实 IP)、compute-full-forwarded-for。real IP模块:nginx的real_ip_header X-Forwarded-For+set_real_ip_from <ingress-cidr>——让nginx直接把remote_addr替换为真实 IP。- 应用框架:
Spring/Django等读X-Forwarded-For(需正确配置可信代理,防XFF伪造攻击)。 - 参考:
kubectl get ingress(看注解)、Ingress Controller日志(看记录的是谁的 IP)、应用request.remote_addrvsrequest.headers['X-Forwarded-For']。
🤔 Ingress 环境提示上传文件过大怎么解决?
Ingress上传文件过大(413 Request Entity Too Large)是因为Ingress Controller(nginx)默认限制了请求体大小(client_max_body_size默认1m)。解决:①调大Ingress/nginx的上传大小限制(proxy-body-size/client_max_body_size)②同时调大后端服务的body size限制 ③必要时调 LB/网关层限制。核心:“Ingress 的 nginx 限量 1m,传大文件要放大 body size,且前后端都要改”。为什么上传过大报错
Ingress Controller(nginx)默认client_max_body_size: 1m(1MB)——超过返回413 Request Entity Too Large(请求体过大),和nginx直接限制一样。Ingress转发的上传请求超过1m就被拦。
解决方法(重点)
- ① 调大
Ingress的请求体限制:通过Ingress注解nginx.ingress.kubernetes.io/proxy-body-size: 20m(或全局ConfigMap的proxy-body-size)——放大Ingress Controller(nginx)允许的上传大小。 - ② 后端应用也要放大限制:
Ingress放大后,后端Pod的应用(nginx/Spring/tomcat)还有自己的body size限制,可能仍报错——要前后端都改(如应用nginx的client_max_body_size、Spring的max-file-size/max-request-size)。 - ③ 负载均衡/网关层:若前面还有
LB(云LB/网关),它们也可能有限制,一并调大。 - ④
Ingress的large-client-header-buffers:个别大请求头也要放大(headers大也可能报错)。
- ① 调大
一句话理解
- 像“寄件门口快递柜限重 1 公斤”——
Ingress(门口快递柜)默认1m,大包裹被退回(413);要收大包裹,把门口拒重(proxy-body-size)和寄件站自己(后端应用)的限重都调大。
- 像“寄件门口快递柜限重 1 公斤”——
协助记忆
- 口诀:“Ingress 报 413 上传过大 → 放大 proxy-body-size(Ingress)+ 后端应用 body limit,前后端都要改”。
- 一句话:“上传过大先调 Ingress 的 proxy-body-size,再调后端应用的 body size”。
进阶思考
- 改了
Ingress注解还报413,还有什么原因?- ①注解没生效(
ConfigMap/注解没reload)②后端应用自己的限制没放宽 ③传的是超大文件(如GB级,除了调大还要考虑Ingress/nginx的proxy-request-buffering、超时proxy-read-timeout)④LB层限制。
- ①注解没生效(
- 上传大文件(
GB级)要注意什么?- 调大
proxy-body-size外,还要:proxy-request-buffering: 'off'(流式转发,不缓冲整个上传)、proxy-read-timeout/send-timeout(大文件传输时间长,超时放宽)、client_max_body_size——大文件上传是Ingress高频坑,要整体调参数。
- 调大
- 改了
扩展信息
nginx-ingress注解:nginx.ingress.kubernetes.io/proxy-body-size(请求体)、proxy-request-buffering(关缓冲大文件)、proxy-read-timeout(读超时)、large-client-header-buffers(头)。413状态码:Request Entity Too Large(请求体过大)——nginx返回,因client_max_body_size限制。- 后端应用限制:
Spring的spring.servlet.multipart.max-file-size/max-request-size;nginx后端client_max_body_size;tomcat的maxPostSize/maxSwallowSize——都要和Ingress对齐。 - 参考:
kubectl annotate ingress <name> nginx.ingress.kubernetes.io/proxy-body-size=20m(加注解)、kubectl get ingress(看注解)、ConfigMap(nginx-configuration,全局默认)。
🤔 如何将 Pod 分配到指定节点上?
把
Pod调度到指定节点,几种方式:①nodeSelector(按节点标签选择,最简单);②nodeAffinity(节点亲和,更灵活——硬性/软性要求,支持In/NotIn等);③nodeName(直接指定节点名,强制);④污点/容忍(Taint/Toleration,反向——节点打污点不让 Pod 上,Pod 配容忍才能上);⑤podAffinity/podAntiAffinity(Pod 亲和/反亲和,按其他 Pod 位置调度)。核心:按标签、按节点名、按污点容忍、按 Pod 位置——各有场景。①
nodeSelector(最简单,按节点标签)- 给节点打标签(
kubectl label node <node> gpu=true),Pod里配nodeSelector: {gpu: "true"}——调度器只把Pod放到带这标签的节点。 - 适用:简单按节点属性选择(如只放有 GPU 的节点/只放 SSD 节点);缺点是只能等值匹配(
key=value),不灵活。
- 给节点打标签(
②
nodeAffinity(节点亲和,灵活)- 用
nodeAffinity表达对节点的偏好/硬性要求:完整字段requiredDuringSchedulingIgnoredDuringExecution(硬性——必须满足否则不调度)、preferredDuringSchedulingIgnoredDuringExecution(软性——尽量满足,不强制);支持In/NotIn/Exists等操作符。 - 适用:需要必须放某类节点(硬性)或尽量放某类节点(软性,如尽量放 SSD,不行放 HDD)。
- 用
③
nodeName(直接指定节点名,强制)Pod.spec.nodeName: <节点名>——调度器把Pod直接指派到这个节点(跳过调度器选择)。- 适用:强制放到固定节点的特殊场景(测试/排障/
DaemonSet兜底);但不灵活(不参与正常调度、易导致资源不均)。
④
污点/容忍(反向控制)- 节点打污点(
Taint,如node-role.kubernetes.io/gpu:NoSchedule——不让普通 Pod 上),只有配了对应容忍(Toleration)的Pod才能调度上去——这是节点怎么挑 Pod(反向),而非 Pod 挑节点。 - 适用:给特殊节点(GPU/专用)打污点,只让特定
Pod上去(如显卡节点只调度 GPU 工作负载)。
- 节点打污点(
⑤
podAffinity/podAntiAffinity(Pod 亲和/反亲和)- 按其他
Pod的位置调度:podAffinity(和某类 Pod 尽量放一起,如和redis同节点)、podAntiAffinity(尽量分开,如副本不同节点)——控制 Pod 间分布。 - 适用:多副本分散(反亲和,高可用)、有依赖的 Pod 就近(亲和,性能)。
- 按其他
一句话理解
- 把
Pod分到指定节点 = 分配宿舍:①nodeSelector= 我要住带空调的楼(按楼标签);②nodeAffinity= 最好住带阳台的,没有也行(软性偏好)或必须住一层(硬性);③nodeName= 我就住 101(直接点名);④污点/容忍= 这栋楼只让有卡的人进(节点挑人);⑤podAffinity= 我要住我朋友隔壁/远离我朋友(按室友位置)。
- 把
协助记忆
- 口诀:nodeSelector 按标签、nodeAffinity 按偏好/硬性、nodeName 点名、污点容忍(节点挑 Pod)、Pod 亲和/反亲和(按邻居)。
- 一句话:让 Pod 去指定节点——标签选、偏好调、点名定、污点拦、邻居带。
进阶思考
nodeSelector和nodeAffinity怎么选?nodeSelector简单(等值匹配,够用);nodeAffinity功能强(In/NotIn/Exists操作符、硬性/软性)——需要必须/尽量或复杂匹配用nodeAffinity,简单放某标签节点用nodeSelector。
污点和nodeSelector什么关系(都管节点选择)?- 方向相反:
nodeSelector/nodeAffinity是 Pod 说我想去哪(主动挑节点);污点/容忍是节点说谁能来(被动拦节点)——一个 Pod 挑节点、一个节点挑 Pod,常配合用(如 GPU 节点打污点 + 特定 Pod 配容忍 + nodeSelector 选 GPU 节点)。
- 方向相反:
扩展信息
nodeAffinity示例:requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: [{matchExpressions: [{key: disktype, operator: In, values: [ssd]}]}](硬性要求 SSD 节点)。Taint效果:NoSchedule(不调度新 Pod)、PreferNoSchedule(尽量不调度)、NoExecute(已跑的 Pod 被驱逐)+ 容忍的tolerationSeconds。podAffinity/podAntiAffinity:topologyKey(按节点/区域/机架维度分散)、weight(软性权重)。- 参考:
kubectl label node(打节点标签)、kubectl taint node(打污点)、kubectl describe node(看标签/污点)、PodYAML 的nodeSelector/affinity/tolerations。
🤔 K8s 调度器有哪些调度算法?
K8s调度器(kube-scheduler)用两阶段调度:先过滤(Filter/Predicate)把不合适的节点筛掉(资源够不够/污点/亲和等),再打分(Score/Priority)给剩余节点排序,选分数最高的节点。核心:先筛选(能不能放),再打分(哪最好),选最高分。调度器的工作流程(两阶段)
- ① 过滤(
Filter/Predicate,节点能不能放):逐个节点检查是否满足硬性条件,不满足的剔除——节点资源够不够(NodeResourcesFit)、是否可调度(NodeUnschedulable)、污点是否可容忍(TaintToleration)、亲和是否满足(NodeAffinity/PodAffinity)、卷能否挂载(VolumeBinding)等。 - ② 打分(
Score/Priority,节点哪个更好):对过滤剩下的节点打分,选分数最高的——NodeResourcesFit(资源均衡,倾向利用率低)、ImageLocality(镜像已存在优先)、PodTopologySpread(分散)、InterPodAffinity(亲和)等。 - ③ 选出并绑定:分数最高的节点被选中,调度器把
Pod绑定到该节点(写回etcd),kubelet再执行。
- ① 过滤(
常见的调度器插件(算法)
Filter类:NodeResourcesFit(资源合适)、NodeUnschedulable(节点可调度)、NodeName(点名节点)、NodeSelector(节点选择器)、NodeAffinity(节点亲和)、InterPodAffinity(Pod 亲和)、TaintToleration(污点容忍)、VolumeBinding(卷绑定)、NodePorts(端口冲突)。Score类:NodeResourcesFit(资源均衡,LeastAllocated倾向分散/MostAllocated打包)、ImageLocality(镜像本地)、PodTopologySpread(拓扑分散)、InterPodAffinity(Pod 亲和)、NodeAffinity(节点亲和权重)。
NodeResourcesFit两种打分(易混淆)LeastAllocated(默认):倾向分散(选利用率低/剩余多的节点——负载均衡);MostAllocated:倾向打包(选已占用的节点——省资源/集中)。默认LeastAllocated(分散)。
一句话理解
- 调度器像找教室:先筛选(这间教室坐得下吗/有投影吗——过滤),再打分(哪间最合适——空座多、有空调优先——打分),选分数最高的教室安排学生(Pod)。
协助记忆
- 口诀:先过滤(能不能放)、再打分(哪最好)、选最高分——资源/污点/亲和做过滤,均衡/镜像/分散打分。
- 一句话:K8s 调度 = 先筛掉不合适节点,再给剩下的打分,选最好的。
进阶思考
- 为什么
NodeResourcesFit默认倾向分散(LeastAllocated)而不是打包?- 分散(
LeastAllocated)让Pod分布更均匀——负载均衡、单节点故障影响小、资源用得更充分;打包(MostAllocated)是省资源/集中(适合预算/资源紧张)。默认分散更利于高可用和资源利用;要打包显式配MostAllocated。
- 分散(
- 调度器节点过滤后全不合适(节点都被筛掉)会怎样?
Pod停在Pending(调度失败),kubectl describe事件显示调度器各节点被拒原因(0/3 nodes available+ 每节点为何不合适)——查资源/污点/亲和/卷,调整后重新调度。
- 为什么
扩展信息
- 调度器是可插拔的:
kube-scheduler由**调度插件(Scheduling Framework)**组成(Filter/Score等),可配置/扩展(自定义插件)。 - 调度策略:
default-scheduler(内置);kube-scheduler通过--config指向SchedulerConfiguration的profiles定制调度配置(旧Policy配置文件/--policy-config-file在v1.23已移除)。 - 调度器高可用:多副本
kube-schedulervia--leader-elect(选主,同时一个调度)。 - 参考:
kubectl describe pod(看调度事件)、kubectl get nodes、kube-scheduler日志(SchedulerConfiguration的profiles/plugins.disabled启停插件)。
- 调度器是可插拔的:
🤔 什么情况下会用到污点与容忍?
污点/容忍(Taint/Toleration)是节点怎么挑 Pod(反向选择):节点打污点(标记不让普通 Pod 上),Pod配容忍才能调度上去。核心用途:①给专用节点(GPU/高内存)打污点只让特定 Pod 用;②节点维护时排空上移 Pod(NoExecute);③隔离特定工作负载/租户。核心:污点是节点门禁,容忍是 Pod 门卡。什么是污点/容忍(先理解)
Taint(污点,节点侧):节点打上污点(如key=value:NoSchedule),表示这个节点有特殊限制,普通 Pod 别来——相当于节点挂了门禁。Toleration(容忍,Pod 侧):Pod配容忍(匹配污点的key/value/effect)才能调度到带污点的节点——相当于 Pod 有门卡。- 关系:污点 + 容忍 = 节点设限,Pod 持卡进入(
Pod没容忍 → 调度器跳过该带污点节点)。
Taint的三种效果(effect)NoSchedule:新 Pod 不调度到该节点(已跑的不管)——最常用,只拦新来。PreferNoSchedule:软性偏好,尽力避免调度——仅在没有更优节点时才可能落上(软限制,非强制阻止)。NoExecute:不仅不调度,还把已跑的 Pod 驱逐(除非有容忍)——用于节点维护/驱逐。
什么情况用(重点)
- ① 专用节点加白名单:给 GPU/高内存/特定硬件 节点打污点(
gpu:NoSchedule),只让配了容忍的 GPU 工作负载(Pod配toleration)上——普通 Pod 不占 GPU 节点。 - ② 节点维护排空:节点要维护,打
NoExecute污点 +kubectl drain,把上面 Pod 驱逐迁移(除非容忍)——常用于节点升级/下线前。 - ③ 控制面节点隔离:
master/control-plane节点默认node-role.kubernetes.io/control-plane:NoSchedule污点——防止业务 Pod 占用 / 影响控制面。 - ④ 默认节点限制:
master/control-plane污点(默认NoSchedule)防止业务 Pod 跑到控制节点。
- ① 专用节点加白名单:给 GPU/高内存/特定硬件 节点打污点(
一句话理解
污点/容忍= VIP 房间门禁:房间(节点)挂仅限 VIP 牌(Taint),只有持 VIP 卡(Toleration)的人能进——给 GPU/高内存等特殊房间设门禁,只有特定工作负载(有卡)入住。
协助记忆
- 口诀:节点打污点(门禁)、Pod 配容忍(门卡)——专用节点/维护排空/隔离用;NoSchedule 拦新、NoExecute 驱逐。
- 一句话:污点是节点门禁、容忍是 Pod 门卡——没卡进不了禁区。
进阶思考
nodeSelector/nodeAffinity和污点/容忍什么区别?- 方向相反:
nodeSelector/nodeAffinity是 Pod 主动挑节点(我想去 GPU 节点);污点/容忍是节点主动拒/放行(GPU 节点只让有卡的上)。生产常配合:GPU 节点打污点(不让普通 Pod)+Pod配容忍 +nodeSelector选 GPU 节点。
- 方向相反:
- 为什么
Master节点默认打污点?Master(控制面)节点跑kube-apiserver/etcd等,打NoSchedule污点(node-role.kubernetes.io/control-plane:NoSchedule)防止业务 Pod 占资源/影响控制面——保证Master专注管理。需要可容忍的Pod才上(如Ingress Controller指定跑 Master)。
扩展信息
- 命令:
kubectl taint node <node> key=value:NoSchedule(打污点)、kubectl taint node <node> key:NoSchedule-(移除污点)、kubectl describe node(看污点)。 Pod容忍写法:tolerations: [{key: gpu, operator: Equal, value: "true", effect: NoSchedule}](Equal/Exists操作符、tolerationSeconds对NoExecute的容忍时长)。NoExecute与tolerationSeconds:容忍NoExecute污点时设tolerationSeconds(容忍多久,到期驱逐);无tolerationSeconds则一直容忍。- 参考:
DaemonSet常配容忍(toleration让它能跑在带污点的 Master 等)、kubectl get nodes -o wide(看污点/标签)。
- 命令:
🤔 PVC 和 PV 是什么?解决了什么问题?
PV(PersistentVolume)是集群里的存储资源(一块持久化存储,如NFS/云盘/本地盘);PVC(PersistentVolumeClaim)是用户对存储的申请单(要多大、哪种访问模式)。核心价值:把"存储的创建"和"应用使用存储"解耦——Pod申请PVC,K8s自动绑定一个满足条件的PV,应用不用关心存储具体在哪。核心:“PV 是存储,PVC 是申请,Pod 用 PVC 绑 PV”。为什么需要(解决的问题)
- 普通
Volume有局限:emptyDir随Pod生灭(确切说:容器重启数据仍在,Pod被移除才清空);hostPath数据持久(存宿主节点、删 Pod 数据仍在)但绑定具体节点、不可跨节点移植、K8s不管理其生命周期。PV/PVC引入持久化 + 解耦:存储独立于Pod存在、可由K8s管理,应用按需申请。 - 解耦:开发者只写
PVC(要多大存储),不用管到底用NFS还是云盘(那是运维配PV/StorageClass的事)——存储和业务分离。
- 普通
PV(集群存储资源,运维/管理员创建)- 集群里的一块持久化存储(
capacity容量、accessModes访问模式、storageClassName、挂载方式);可由管理员静态创建或StorageClass动态供给。 - 访问模式:
ReadWriteOnce(单节点读写——同一节点可多Pod挂载,非仅一个)、ReadOnlyMany(多节点只读)、ReadWriteMany(多节点读写)。
- 集群里的一块持久化存储(
PVC(存储申请单,用户/应用创建)- 用户声明要多少存储(
resources.requests.storage)+ 访问模式,Pod通过volumes引用PVC——像"申请单"写明需求,K8s帮它绑一个合适的PV。
- 用户声明要多少存储(
PVC绑定PV的过程Pod引用PVC→K8s找一个满足PVC条件(容量够、访问模式匹配、storageClass对应)的PV绑定 →Pod用它挂载。PVC先Pending(没找到合适PV),绑定后Bound。- 静态:管理员预建
PV;动态:StorageClass自动创建PV(现在主流)。
一句话理解
PV像仓库里的储物柜(有容量/规格),PVC像领取单(写“我要个 100G 的柜子”),K8s按领取单匹配并分配一个空柜子给应用(Pod)用——你只管填单子(PVC),不用管柜子在哪个仓库、什么材质(那是PV的事)。
协助记忆
- 口诀:“PV 是存储资源、PVC 是申请单、Pod 用 PVC 绑 PV——分层解耦,存储跟业务分开”。
- 一句话:“PVC 填要多大,K8s 自动绑定合适的 PV,Pod 只管用”。
进阶思考
PVC一直Pending(绑定不了 PV)最常见原因?- ①没有满足条件的
PV(容量/访问模式/storageClass不匹配)②StorageClass动态供给没配置/没授权 ③存储后端不可用——kubectl describe pvc看事件定位(WaitForFirstConsumer/No storage class)。
- ①没有满足条件的
PV和PVC是 1:1 绑定吗?- 是。一个
PVC一次性绑定一个PV(一对一),PVC释放后PV可回收(reclaimPolicy决定:Retain保留/Delete删除/Recycle清理);PVC不能同时绑多个PV。
- 是。一个
扩展信息
PV生命周期阶段:Available(可用)→Bound(已绑定PVC)→Released(PVC释放)→Failed(失败)。reclaimPolicy(回收策略):Retain(保留数据,人工回收)、Delete(删除PV及数据,StorageClass默认)、Recycle(清空复用,已弃用)。Pod引用:Pod的volumes配persistentVolumeClaim.claimName(引用PVC),containers里volumeMounts挂载——Pod→PVC→PV三层。- 参考:
kubectl get pv/pvc(查看)、kubectl describe pvc(看绑定状态/事件)、StorageClass(动态供给)。
🤔 StorageClass 是什么?
StorageClass是存储类/存储动态供给的模板:定义“怎么动态创建PV”——指定存储提供者(provisioner,如NFS/CSI/云盘)、reclaimPolicy回收策略、访问模式、参数。应用申请PVC时,K8s按StorageClass自动创建对应的PV(无需人工预建)。核心:“StorageClass 让存储按需动态供给——写 PVC 时自动生成 PV”。解决什么问题
- 静态
PV:管理员手动预建每个PV(麻烦、不灵活);StorageClass实现动态供给——PVC声明要多大,K8s按StorageClass模板自动建PV并绑定,存储按需、自动化。
- 静态
StorageClass定义什么provisioner(供给者):谁负责创建存储(CSI驱动如ebs.csi.aws.com/rbd.csi.ceph.com/nfs-client外部供给)——真正创建存储的插件(注意kubernetes.io/aws-ebs等树内插件在v1.27已移除、kubernetes.io/nfs非内置动态供给者)。reclaimPolicy(回收策略):Delete(删PV数据)/Retain(保留)——PVC释放后怎么处理。parameters:供给参数(如type: gp2云盘类型、fsType文件系统)——传给provisioner的具体配置。allowVolumeExpansion:是否允许扩容卷。
工作原理(动态供给)
- ①用户创建
PVC(声明容量 +storageClassName指定用哪个StorageClass)②K8s看见没人绑,调用该StorageClass的provisioner动态创建PV(如创建一个EBS/NFS卷)③PVC自动绑定新PV,Pod可用——全程自动化。 - 静态 vs 动态:静态=管理员预建
PV(慢/要人管),动态=StorageClass自动供给(主流,按需快速)。
- ①用户创建
一句话理解
StorageClass像仓库的“自动补货规则”:写规则“缺 100G 就自动从 NFS 仓调 100G 过来”(provisioner是供货商、parameters是规格、reclaimPolicy是退库政策)——应用下单(PVC),仓库按规则自动补货(动态PV)。
协助记忆
- 口诀:“StorageClass 是动态供给模板——provisioner 谁造存储、reclaimPolicy 回收、parameters 参数,PVC 指定它自动建 PV”。
- 一句话:“StorageClass 让存储自动按需来——PVC 写需求,它自动造 PV”。
进阶思考
- 默认
StorageClass是什么?- 集群可设一个默认
StorageClass(storageclass.kubernetes.io/is-default-class: true),PVC不指定storageClassName时用它;云集群(EKS/ACK)默认有(如gp2/alicloud-disk),裸机要自己装StorageClass(如local-path/NFS)。
- 集群可设一个默认
PVC怎么选StorageClass?PVC的storageClassName: <xxx>指定用哪个StorageClass;不指定则用默认。不同存储(SSD/HDD/本地盘)对应不同StorageClass,PV按需选。
- 默认
扩展信息
- 常见
StorageClass:local-path(本地路径,k3s默认)、NFS、AWS gp2/gp3、Ceph RBD(rbd.csi.ceph.com)、Alibaba cloud-disk——按环境/存储后端配。 waitForFirstConsumer:volumeBindingMode——延迟到Pod调度到节点时才创建卷(解决多区域存储位置问题)。- 参考:
kubectl get sc(看StorageClass)、kubectl describe sc <name>(看配置)、PVC的storageClassName。
- 常见
🤔 PV 的生命周期是怎样的?
PV生命周期四阶段:Available(可用,未被绑定)→Bound(已被PVC绑定)→Released(PVC释放,但数据还在)→Failed(异常/回收失败)。核心:“可用→绑定→释放→失败,回收策略决定释放后数据去留”。四个阶段
Available:PV可用(空闲,还没被PVC绑定)。Bound:PV已绑定到某个PVC(配对,一对一)——存储正被用。Released:绑定的PVC被删除,PV释放(数据还在),但不能直接复用——需人工按回收策略处理。Failed:PV自动回收失败/异常(如Delete失败),需要人工介入。
reclaimPolicy(回收策略,关键)Delete:PVC释放后,PV及底层数据一并删除(动态StorageClass默认)——数据没了。Retain:PVC释放后保留数据,PV变Released,需人工手动删除/复用(数据可恢复,安全)。Recycle:PVC释放后清空数据供复用(已弃用,被Delete取代)。
生命周期流程
- ①管理员/静态创建
PV(Available)或StorageClass动态供给 ②PVC申请,PV被绑定(Bound)③Pod用它挂载 ④PVC删除,PV释放(Released)⑤按reclaimPolicy:Delete直接删除PV(数据没了);Retain停在Released(数据保留),需 人工清理claimRef后手动再绑定/重建,才回到Available。
- ①管理员/静态创建
一句话理解
PV生命周期 = “储物柜的状态”:空闲可用(Available)→ 租给某客户(Bound)→ 客户退租(Released)→ 按“退租政策”(reclaimPolicy):Delete= 清空柜子给下家(数据删)、Retain= 柜子锁着等处理(数据保留)——柜子复用/作废全看退租政策。
协助记忆
- 口诀:“Available→Bound→Released→Failed 四阶段;回收策略 Delete 删数据、Retain 保留、Recycle 弃用”。
- 一句话:“PV 可用→绑定→释放,释放后按回收策略删数据还是保留”。
进阶思考
Retain释放后的PV怎么复用(怎么让新 PVC 用上)?Retain后PV是Released,需人工手动处理:①kubectl delete pv <name>删除后重建同名PV(数据在,重新Available)②或改pv的claimRef让新PVC绑定——较繁琐,适合“数据要保留复用”场景。
Delete策略丢数据怎么办(哪些数据不能 Delete)?- 重要数据/数据库卷别用
Delete(删PVC会连带删数据);关键存储用Retain(保留数据,人工处理),或确保有备份——StorageClass的reclaimPolicy是“数据安全 vs 清理方便”的权衡,重要数据选Retain。
- 重要数据/数据库卷别用
扩展信息
PV/PVC绑定关系:一对一(claimRef记录绑定的PVC);PVC只能绑一个PV。volumeBindingMode:Immediate(立刻绑定)/WaitForFirstConsumer(Pod调度到节点才绑,解决多区域位置)。- 参考:
kubectl get pv(看阶段/reclaimPolicy)、kubectl describe pv、PVC删除看PV阶段变化。
🤔 CSI(容器存储接口)有什么作用?
CSI(Container Storage Interface)是容器与存储后端的标准接口:让K8s通过可插拔的CSI驱动接入各种存储(NFS/Ceph/云盘/本地盘),统一管理PV/PVC/卷的创建、挂载、扩容、快照。核心价值:把存储接入标准化——一个CSI标准,对接存储厂商的驱动,K8s不用为每种存储写专门插件。核心:“CSI 是存储驱动标准,K8s用 CSI 插件接入任意存储”。解决什么问题(为什么需要 CSI)
- 早期
K8s每种存储要写专门的 in-tree 插件(aws-ebs/gce-pd/nfs等硬编码进K8s代码)——存储一多就臃肿、难扩展。 CSI(v1.13GA)把存储接入外置标准化:存储厂商写一个CSI驱动(out-of-tree,独立于 K8s 代码),K8s通过CSI接口调用它——插件化、可扩展、存储厂商自己维护。
- 早期
CSI的作用/能力- 动态供给:通过
StorageClass的provisioner(CSI驱动)自动创建/删除存储卷(PV)。 - 卷管理与挂载:
CSI驱动负责实际挂载/卸载、Mount/NodeStage/NodePublish等存储操作——K8s把卷操作委托给CSI驱动。 - 卷的扩容/快照:
CSI支持在线扩容(volumeExpansion)、卷快照(snapshot,v1.20GA)、Resize——比老插件强大。 - 拓扑感知(
CSINode):CSI支持多区域/节点拓扑感知,让卷有位置感知(CSINode)。 - 访问模式:
ReadWriteOncePod(单 Pod 读写)等;VolumeAttributesClass是可变卷属性(v1.29alpha、v1.34GA),与拓扑无关。
- 动态供给:通过
CSI架构(组件)External Provisioner:监听PVC,调用CSI驱动创建/删除卷(动态供给)。External Attacher:把卷附加到节点(Attach/Detach);External Resizer(扩容)、External Snapshotter(快照)——这些 sidecar 实现扩容/快照能力。CSI 驱动:存储厂商的插件(rbd.csi.ceph.com/ebs.csi.aws.com/hostpath),实现CSI接口(CreateVolume/DeleteVolume/ControllerPublishVolume等GRPC方法)。- CSI 驱动的 Node 服务:CSI 驱动在每个节点跑的
DaemonSet(NodeStage/NodePublish做挂载/卸载),配合kubelet——注意没有独立叫“CSI Node”的组件,是驱动自带的节点端服务。
一句话理解
CSI像USB 接口标准——不管硬盘/摄像头/打印机(各种存储后端),只要符合USB标准(CSI接口),都能插上用;存储厂商(CSI驱动)按标准做个“转接头”,K8s(电脑)统一认出并管理。标准化 + 可插拔。
协助记忆
- 口诀:“CSI 是存储驱动标准——provisioner 动态供给/挂载/扩容/快照,存储厂商写 CSI 驱动,K8s 统一接入”。
- 一句话:“CSI 把存储接入标准化,K8s 用 CSI 插件对接任意存储”。
进阶思考
CSI和StorageClass什么关系?StorageClass的provisioner就指向一个CSI驱动(如ebs.csi.aws.com)——StorageClass是“配置/参数”,CSI是“真正干活的能力”。写PVC指定StorageClass→ 用它的provisioner(CSI驱动)动态造存储。
- 为什么要用
CSI而不是旧 in-tree 插件?- ①可扩展:存储厂商独立开发
CSI驱动(不用改K8s核心)②功能全:CSI支持动态供给/扩容/快照/拓扑 ③解耦:K8s核心不臃肿、新存储快速接入。旧 in-tree 插件(如aws-ebs)已在v1.27移除,迁移到CSI。
- ①可扩展:存储厂商独立开发
扩展信息
- 常见
CSI驱动:rbd.csi.ceph.com(Ceph)、ebs.csi.aws.com(AWS)、disk.csi.alibabacloud.com(阿里)、hostpath.csi.k8s.io(本地)、nfs.csi.k8s.io(NFS)。 CSI卷操作:CreateVolume/DeleteVolume(动态供给)、ControllerPublishVolume(附加节点)、NodeStageVolume/NodePublishVolume(挂载)、CreateSnapshot/DeleteSnapshot(快照)。- 参考:
kubectl get csinodes(节点CSI)、kubectl get storageclass(看provisioner指向CSI驱动)、kubectl get pvc。
- 常见
🤔 误删除 PVC,怎么恢复?
误删
PVC的恢复思路:①删 PVC 通常不会删底层数据(除非reclaimPolicy是Delete——动态供给默认,静态PV显式配Delete同样生效)②若PV是Released/Retain状态或数据还在,重建PVC重新绑定或手动重建PV(claimRef指向新 PVC) ③有快照/备份则从存储层恢复 ④PV一旦Delete删了数据,只能靠存储端快照/备份恢复(K8s 层救不回)。核心:“删 PVC 数据未必丢——Retain 保留可重建绑定,Delete 删了数据只能靠快照/备份”。先确认数据还在吗(关键)
kubectl get pv <name>看那个PV的状态:如果PV还在(Released/Retain)→ 数据没删,可恢复;如果PV已被Delete(不在了)→ 底层存储被删,K8s 层救不回。reclaimPolicy:Retain(数据保留)、Delete(数据删除,StorageClass默认)、Recycle(已弃用)。
恢复方式 1:重建
PVC重新绑定(若 PV 还在/Retain)PV状态Retain(数据在):重建一个PVC(字段匹配)让它绑定原PV;注意Released的PV不会被新PVC自动绑定,需先把原Released的PV清空claimRef变为Available,新PVC才能在Available的PV里匹配绑定。- 若
PV是Released(claimRef还指向已删PVC):需手动清理claimRef(kubectl patch pv <name> -p '{"spec":{"claimRef":null}}')让PV回到Available,新PVC就能绑上。
恢复方式 2:快照/备份恢复(数据被删/要回滚)
PVC/数据被删且无保留:用存储层快照(CSIVolumeSnapshot)或备份恢复——kubectl从snapshot恢复:PVC的dataSource: {name: <snap>, kind: VolumeSnapshot}或从备份还原。- 前提:平时有做快照/备份(
VolumeSnapshotClass/备份工具)——这是数据安全底线。
恢复方式 3:同名重建(仅重建空卷,数据不可恢复)
- 若
PVC被删但PV是动态供给的Delete策略,数据可能已删——只能靠快照/备份恢复,或接受数据丢失(警示:重要数据别用Delete策略)。
- 若
一句话理解
- 误删
PVC像“退了房但行李还在不在”:退房(删PVC)不一定丢行李——Retain(行李放那等着)能回去拿(重建绑定);Delete(行李已清理)就只能靠之前的存包/拍照(快照/备份)。关键是看退租政策(reclaimPolicy)和有没有备份。
- 误删
协助记忆
- 口诀:“删 PVC 未必丢数据——Retain 保留可重建绑定(清 claimRef 回 Available),Delete 删了数据只能靠快照/备份恢复”。
- 一句话:“误删 PVC 先看 PV 在不在——Retain 能救、Delete 靠快照、重要数据别用 Delete”。
进阶思考
- 为什么删
PVC不删PV?PVC是“申请单”,删它只是释放绑定;PV(真实存储)在不在取决于reclaimPolicy——Retain保留 PV(数据在)、Delete删除 PV(数据删)。所以删PVC前先看PV的回收策略。
- 什么场景最容易误删
PVC且难恢复?- 动态供给 +
Delete策略(主流StorageClass默认Delete)——删PVC连PV带数据一起删,无快照就丢数据。重要数据/数据库卷应改Retain或定期快照备份。
- 动态供给 +
- 为什么删
扩展信息
claimRef清理:kubectl patch pv <name> -p '{"spec":{"claimRef":null}}'让Released的PV回到Available(可被新PVC绑定)。VolumeSnapshot:CSI卷快照(v1.20GA),PVC的dataSource: {kind: VolumeSnapshot, name: <snap>}从快照恢复——防误删的利器。- 预防:重要存储用
Retain策略、定期快照/备份、开启PVC保护(kubernetes.io/pvc-protection终结器,删PVC前等Pod释放卷)、权限控制(防误删)。 - 参考:
kubectl get pv(状态/reclaimPolicy)、kubectl get volumesnapshot、kubectl patch pv(清 claimRef)。
🤔 emptyDir 和 hostPath 卷的应用场景?
emptyDir(空目录卷)随Pod生灭、Pod内容器共享;hostPath(主机路径卷)直接挂宿主节点目录、数据持久绑定节点。应用场景:emptyDir适合同 Pod 内容器共享/临时缓存(如日志暂存、sidecar共享数据);hostPath适合访问宿主机特定文件/目录(如读宿主机配置、日志、让容器跑在指定宿主机存储)。核心:“emptyDir 临时共享、hostPath 宿主机数据——前者随 Pod、后者绑节点”。emptyDir(空目录卷,临时)- 机制:
Pod创建时分配一个空目录,Pod内多个容器共享(同卷挂载);Pod删除(或被调度到别处)时目录清空删除(数据不持久;容器重启数据仍在)。 - 特点:随
Pod生灭、同Pod多容器共享、无需PV/StorageClass、默认空目录(可选emptyDir.medium: Memory用内存)。 - 应用场景:①
Pod内多个容器共享数据(如 web 容器 + 日志采集sidecar共享日志文件、业务 + 上传临时文件)②临时缓存/中间数据(短生命周期的Pod,如批处理任务中间态)③sidecar模式共享配置/临时文件。
- 机制:
hostPath(主机路径卷,宿主机目录)- 机制:把宿主节点上的指定目录/文件挂载到容器——直接访问宿主机存储(如
/var/log、/data)。 - 特点:数据持久(存宿主节点、删 Pod 数据仍在)、绑定具体节点(不可跨节点迁移)、
K8s不管理生命周期、能访问宿主机目录(有安全考虑)。 - 应用场景:①容器要读写宿主机特定文件(如访问
/etc配置、宿主机日志)②**DaemonSet采集宿主日志**(hostPath挂/var/log给容器)③单节点存储/测试(不跨节点)。
- 机制:把宿主节点上的指定目录/文件挂载到容器——直接访问宿主机存储(如
区别与应用选型
emptyDir:临时/共享(Pod内、随Pod生命)、轻量;hostPath:宿主机持久/特定文件(绑节点、数据留宿主)。生产持久化/跨节点用PV/PVC或CSI;emptyDir/hostPath是基础卷。
一句话理解
emptyDir像“同屋室友共用的小白板”——住一起(同Pod)多人用,搬走(Pod删除)就擦掉重来(临时);hostPath像“家里固定墙上的储物柜”——数据在自家(宿主节点)留着,但墙是固定的(绑节点,搬家(换节点)柜子带不走)。PV是“外面租的保险柜”(独立持久、可移动),即专门持久化。
协助记忆
- 口诀:“emptyDir 临时共享(随 Pod 生灭)、hostPath 宿主机数据(绑节点持久)——要持久跨节点用 PV/PVC”。
- 一句话:“临时同 Pod 共享用 emptyDir、访问宿主机文件用 hostPath、生产持久化用 PV”。
进阶思考
- 为什么
hostPath生产要慎用?- ①绑节点:
Pod被调度到别的节点,hostPath数据不在那(丢/错位)②安全:容器能访问宿主机目录(权限/逃逸风险)③K8s不管理、难备份/迁移——所以生产持久化多用PV/PVC(CSI/NFS),hostPath只在特定场景(如日志采集、跨节点无关的DaemonSet)。
- ①绑节点:
emptyDir能共享宿主机内存加速吗?- 能:
emptyDir.medium: Memory用节点内存做卷(快、临时),适合高速缓存;数据在内存(Pod删除即失;Memory介质在节点重启时也丢失——RAM,但容器重启仍在),占节点内存,适合轻量暂存非持久化场景。
- 能:
- 为什么
扩展信息
emptyDir与sidecar:sidecar容器(日志采集/代理)与主容器共享emptyDir(同一卷)传数据——K8s sidecar模式常用。subPath:挂载卷的子目录(mountPath映射卷内的子路径),emptyDir/hostPath都可配。- 参考:
PodYAML 的volumes(emptyDir/hostPath)+volumeMounts(挂载);kubectl exec进容器看挂载。
🤔 Secret 有哪些应用场景?
Secret是 K8s 存敏感信息(密码/令牌/密钥/证书)的对象,用 base64 编码存储、可注入到Pod使用。应用场景:①存凭据(数据库密码/API Token)②存TLS证书(HTTPS)③存镜像拉取凭证(私有仓库dockerconfigjson)④存SSH密钥/配置。核心:“Secret 存秘密,注入 Pod,避免硬编码在镜像/配置里”。什么是
SecretK8s对象,存敏感数据(键值对),值是 base64 编码(注意:base64 非加密,只是编码——真正安全靠RBAC权限 + 集群机制如加密etcd);Pod通过挂载卷/环境变量引用。- 与
ConfigMap对比:ConfigMap存非敏感配置(普通配置),Secret存敏感数据。
Secret类型与应用场景Opaque(通用):存任意敏感键值——如数据库密码/API Key/服务令牌,最常用。kubernetes.io/tls:存TLS证书(tls.crt/tls.key)——给Ingress/HTTPS服务引用(K8s不自动生成证书,只存放用户提供的证书/私钥;自动签发要cert-manager)。kubernetes.io/dockerconfigjson:存镜像仓库凭据(docker login的config.json)——Pod拉私有镜像时imagePullSecrets引用。kubernetes.io/basic-auth:Basic Auth用户名/密码(HTTP基本认证)。kubernetes.io/ssh-auth:SSH私钥(Git拉取等)。ServiceAccount令牌:SA凭证(旧版kubernetes.io/service-account-tokenSecret 已弃用;新版由TokenRequest/投影卷按需签发)。
Pod怎么用Secret(注入方式)- ① 环境变量:
env.valueFrom.secretKeyRef把Secret的值作为环境变量——应用到Pod。 - ② 挂载卷:
volumes里secret.secretName+volumeMounts,把Secret挂成文件(/etc/secret/)——应用读文件,更新Secret挂载自动刷新。 - ③
imagePullSecrets:拉私有镜像时引用dockerconfigjson类型Secret。
- ① 环境变量:
一句话理解
Secret像保险柜里的密码本——敏感信息(数据库密码/证书/镜像密钥)锁在“保险柜”(Secret),应用要用时从保险柜取(环境变量/挂载文件),不直接写在镜像/代码/,管理集中、可权限控制。
协助记忆
- 口诀:“Secret 存敏感(密码/证书/镜像凭据),base64 编码非加密,注入 Pod 用环境变量或挂载,权限管控”。
- 一句话:“敏感信息别硬编码,放 Secret——密码、证书、仓库凭据都归它”。
进阶思考
Secret是 base64 编码,安全吗?- base64 只是编码不是加密——能被解码;真正的安全靠:①
RBAC权限控制(谁有权限看/用Secret)②etcdSecret加密(kube-apiserver的--encryption-provider-config静态加密etcd里的Secret)③最小使用。裸集群要配etcd加密,Secret才真正保密。
- base64 只是编码不是加密——能被解码;真正的安全靠:①
ConfigMap和Secret什么时候用哪个?- 非敏感配置(
app.yaml/server.conf)→ConfigMap(明文、易读);敏感(密码/Token/证书)→Secret(base64 + 权限 + 可加密)——敏感数据必须Secret,普通配置用ConfigMap。
- 非敏感配置(
扩展信息
Secret最小权限:RBAC控制谁可get/watchSecret(不给普通用户/Pod过多访问)。Secret对象默认存etcd(需配etcd加密);作volume挂载时才以tmpfs落在节点内存。secrets-store是外部密钥库CSI集成,与Secret是否落etcd是两回事。SOPS/External Secrets:Secret的敏感来源可用外部管理(External Secrets Operator从Vault/AWS Secrets Manager同步Secret)——云原生安全趋势。- 参考:
kubectl create secret(创建)、kubectl get secret、kubectl describe secret、PodYAML 的env/volumes/imagePullSecrets。
🤔 RBAC 中 Role 和 ClusterRole 区别?
Role(角色,命名空间级):在某个命名空间内授权(只能管那个Namespace的资源);ClusterRole(集群角色,集群级):在整个集群授权(所有Namespace/集群级资源)。核心区别:作用范围——Role 限一个 Namespace,ClusterRole 全集群。RoleBinding 绑 Role(限命名空间),ClusterRoleBinding 绑 ClusterRole(全集群)。Role(命名空间级角色)- 定义在某个
Namespace,只能在那个命名空间内授权(get/list/create该Namespace的Pod/Deployment等资源)。 RoleBinding绑定Role(RoleBinding也限命名空间)——授权只在那个Namespace生效。- 适用:按命名空间隔离授权(如
dev用户只能管dev命名空间、ops只能管prod)。
- 定义在某个
ClusterRole(集群级角色)- 不限命名空间,可对整个集群授权:集群级资源(
Node/PersistentVolume/Namespace)、所有命名空间的资源、非资源端点(/healthz等)。 ClusterRoleBinding绑定ClusterRole——全集群生效;也可用RoleBinding绑ClusterRole(把集群角色“收窄”到某命名空间)。- 适用:管理员/全局授权(管整个集群、所有命名空间)、访问集群级资源(
Node/PV)。
- 不限命名空间,可对整个集群授权:集群级资源(
绑定关系(4 种组合)
Role+RoleBinding:命名空间级授权(最常用,按命名空间隔离)。ClusterRole+ClusterRoleBinding:集群级授权(管理员/全局)。ClusterRole+RoleBinding(同命名空间):把集群角色收窄到某命名空间(如给dev命名空间绑只读ClusterRole)。Role+ClusterRoleBinding:不合法(Role是命名空间级,不能绑集群级绑定)。
一句话理解
Role像“大堂经理”(只管这一层——命名空间);ClusterRole像“物业总部经理”(管整栋楼——全集群)。RoleBinding是“聘书”(人事任命限一层),ClusterRoleBinding是“总部任命”(全楼)。
协助记忆
- 口诀:“Role 管一个 Namespace、ClusterRole 管全集群;RoleBinding 绑 Role(限命名空间)、ClusterRoleBinding 绑 ClusterRole(全集群);ClusterRole+RoleBinding 可收窄”。
- 一句话:“Role 限命名空间、ClusterRole 全集群——按范围选绑定”。
进阶思考
- 什么时候用
ClusterRole而不是Role?- ①要管集群级资源(
Node/PV/Namespace——这些只在集群级)②要管所有命名空间的资源 ③给管理员/角色全局权限。只管某个命名空间内普通资源 → 用Role(最小权限、隔离好)。
- ①要管集群级资源(
ClusterRole用RoleBinding绑定是什么场景?- 想用一个现有的集群角色(如只读
view)但只给某个命名空间授权——RoleBinding在dev命名空间绑ClusterRole view,用户只在dev有view权限、其他Namespace没有——“复用集群角色、收窄到命名空间”。
- 想用一个现有的集群角色(如只读
- 什么时候用
扩展信息
- 内置
ClusterRole:admin(命名空间内全权,非集群级)、edit(可读写)、view(只读)、cluster-admin(集群管理员全部权限)——内置常用。 RoleBinding/ClusterRoleBinding绑定对象:可绑User(用户)、Group(组)、ServiceAccount(服务账号)——Subject决定谁获得权限。- 参考:
kubectl get role/clusterrole、kubectl get rolebinding/clusterrolebinding、kubectl describe role。
- 内置
🤔 RBAC 中 ServiceAccount 有什么作用?
ServiceAccount(SA,服务账号)是给“非人类身份”(Pod/应用/系统)用的账号,让Pod有身份去访问K8sAPI(认证 + 授权)。核心作用:①给Pod提供访问集群 API 的身份 ②配合RBAC给Pod最小权限(只给它需要的)③承载令牌/Secret 供Pod调用 API。核心:“SA 是 Pod 的身份——用它 + RBAC 给 Pod 最小访问权限”。什么是
ServiceAccount- 与
User(人类用户)相对,SA是程序/Pod的身份——Pod用它去认证K8sAPI(而不是人类登录)。 - 每个
Pod默认关联一个SA(default);SA的服务账号令牌作凭证(注意K8s 1.24起default SA不再自动创建service-account-tokenSecret,凭证改由投影卷(TokenRequest)提供)。
- 与
ServiceAccount的作用- ①
Pod访问集群 API 的身份:Pod用SA的令牌去调用K8sAPI(如读Pod状态、创建资源)——Pod有身份(SA)才能认证。 - ② 配合
RBAC最小权限:SA绑定Role/ClusterRole(RoleBinding),给Pod只需要的权限——Pod只能访问授权范围(最小权限、安全)。 - ③ 自动挂载令牌:
Pod自动挂SA的service-account-token卷(到/var/run/secrets/kubernetes.io/serviceaccount/),应用读令牌访问 API。 - ④ 镜像拉取/外部认证:
SA可关联imagePullSecrets(拉私有镜像)、Pod对外身份。
- ①
ServiceAccount与RBAC关系SA是身份(Who),Role/ClusterRole是权限(What),RoleBinding绑定——SA+RBAC组合给Pod精确授权(如“只有prometheusSA 能查Pod/Node”)。- 生产常给不同
Pod用不同SA(不是全用default)——最小权限、隔离。
一句话理解
SA像“机器人的工作证”——机器人(Pod)干活前要有工作证(SA),证上写明能进哪些门(RoleBinding绑定权限);不给工作证(用默认SA)或许可范围大,就像机器人乱闯(安全差)。给每个机器人(Pod)合适的证(最小权限SA)才安全。
协助记忆
- 口诀:“SA 是 Pod 身份——+RBAC 给最小权限,令牌自动挂载、Pod 凭它访问 API”。
- 一句话:“ServiceAccount 是 Pod 的工作证,配 RBAC 只给它该有的权限”。
进阶思考
- 为什么不用默认
defaultSA(每 Pod 一个专门 SA)?defaultSA 权限/审计不清晰、所有Pod用同一个(隔离差);专门 SA 让每个Pod有独立身份 + 最小权限(如prometheus只读监控、deploy只改Deployment)——最小权限、可审计、安全(生产最佳实践)。
SA认证怎么做的(token 是啥)?SA关联ServiceAccount令牌(JWT),Pod挂载令牌卷,调用 API 时带Bearer token——kube-apiserver用TokenAuthentication验证SA身份;新版用绑定的ServiceAccount令牌(TokenRequest/投影卷,v1.20beta、v1.22起 GA)。
- 为什么不用默认
扩展信息
- 自动挂载控制:
automountServiceAccountToken: false(某些Pod不需访问 API,关自动挂载——安全)。 SA令牌类型:长期Secret令牌(旧)/TokenRequest投影令牌(新,短时、audience限定——更安全)。- 参考:
kubectl get serviceaccount、kubectl create sa、Pod的spec.serviceAccountName、RoleBinding绑定ServiceAccount。
- 自动挂载控制: