简介Kubernetes 课件面向刚接触云原生与容器编排的开发者、运维人员及授课讲师用于建立 k8s 的整体知识框架。内容先讲集群架构与各组件分工包括 kube-apiserver、controller-manager、scheduler、Etcd、kubelet、kube-proxy 与容器引擎的职责再落到 Pod、Controllers、Service、Label、Namespace 等基础对象并延伸到监控与日志管理、应用程序生命周期管理、调度、存储、集群安全等章节。课件中给出 kubectl 常用查询命令、Deployment 的创建发布与升级回滚、YAML 清单结构、Metrics-server 配合 cAdvisor 查看节点与 Pod 资源消耗等具体条目便于课堂演示或自学对照。压缩包共 1 个 pptx 文件约 1.25MB章节清晰、篇幅紧凑可直接作为培训讲义与复习提纲。目前已有 2191 人学习适合入门阶段快速梳理要点也便于进阶者按模块查漏补缺。1. k8s 到底是什么把一堆服务器当成一台来用先做一个类比。把应用连同它的依赖一起打包成标准集装箱也就是容器那么 k8sKubernetes 的简称就是集装箱码头的调度系统它不生产货物只负责决定哪个箱子放到哪个泊位、箱子坏了怎么补、船靠岸时货该走哪条通道。用一句话说清楚k8s 是一套容器集群管理系统2014 年开源专门做容器化应用的部署、扩展和管理目标是让部署容器化应用这件事变得简单高效。搞懂它值得花点时间是因为当机器从两台变成两百台、应用从三个变成三十个之后靠人挨个登录服务器敲命令的方式迟早会翻车。k8s 把这些重复判断沉淀成规则你只要描述「我想要什么」剩下的交给它去凑。接下来从最小的积木开始一层层拆开这个黑匣子。2. Pod 与控制器k8s 里最小的积木长什么样打开任何一份 k8s 入门材料扑面而来的都是 Pod、Controller、Service、Label、Namespace 这几个词。它们不是并列的五个概念而是有层次的Pod 是真正跑东西的载体控制器是看住 Pod 的人Service 和 Label 负责在 Pod 不断变化时维持可达性Namespace 则在逻辑上划出一块地盘。把这四层关系理顺后面所有命令和配置文件都会变得顺理成章。2.1 最小部署单元 Pod为什么不直接跑容器在 k8s 里你几乎不会直接创建一个容器创建的是 Pod。Pod 是最小的部署单元是「一组容器的集合」同一个 Pod 里的容器共享同一个网络命名空间并且 Pod 本身是短暂的——这是它的三个关键属性。很多初学者会问容器已经够轻了为什么还要在容器外面再套一层原因在于有一类应用天生亲密。原文列了三种典型场景两个应用之间要发生文件交互两个应用需要通过 127.0.0.1 或者 socket 通信最典型的就是 Web 服务器加脚本解释器这种组合两个应用之间需要发生频繁调用。这三种场景里如果把两个容器硬拆到两台机器上要么走网络绕一大圈要么根本没法用本地回环地址通信。Pod 就是把「必须住在一起」的容器放进同一个房间。具体怎么实现的Pod 本身是一个逻辑概念没有对应的实体进程它的实现靠一个叫 infra container 的小容器每个 Pod 先启动这个小容器把网络命名空间建好然后让业务容器加入进来。加入之后所有容器看到的网络视图完全一样——网络设备、IP 地址、MAC 地址都相同于是 127.0.0.1 天然可用。存储共享则是通过容器挂载同一个卷来实现的。打个比方Pod 像一间合租公寓水电表共用一套网络客厅里的冰箱共用存储但每个人有自己的房间各自的文件系统。一个最简单的 Pod 配置长这样apiVersion: v1 # API 版本决定这个资源按哪套规范解析 kind: Pod # 资源类型这里声明要创建一个 Pod metadata: name: my-pod # Pod 名称同一命名空间内唯一 spec: containers: - name: java # 容器名 image: java-demo:1.0 imagePullPolicy: IfNotPresent # 本地没有再拉取这四段式结构是所有 k8s 资源的通用骨架apiVersion 说明按哪套接口规范解析kind 说明要创建什么类型的对象metadata 放名称、标签这类元数据spec 描述你期望的状态。后面换成 Deployment、Service骨架不变只是 spec 里写的东西不同。要特别注意的是「Pod 是短暂的」这句话的分量。Pod 被删掉重建后 IP 会变所在节点挂了它也不会自己搬家。正因为如此才需要下一节要讲的控制器。2.2 控制器家族谁替你看住这些短命的 Pod如果只有 Pod运维会退化成体力活应用挂了没人拉起副本少了没人补升级时新旧版本只能手动切换。控制器解决的正是这个问题——你告诉它期望状态比如「我要 3 个副本」它就不停地把实际状态往期望状态上凑。原文列了五种各有明确分工。Deployment 面向无状态应用。所谓无状态是指任意一个副本都能处理任意一个请求副本之间没有身份差别谁挂了换一个上来就行。绝大多数 Web 前端、API 服务都属于这一类滚动升级、弹性伸缩、版本回滚这些动作在 Deployment 上都有现成命令支持。StatefulSet 面向有状态应用。数据库这类应用每个实例都有自己的身份、自己的数据盘、启动顺序还有讲究不能随便替换。StatefulSet 提供的就是稳定标识和有序管理代价是操作复杂度明显上升。DaemonSet 是守护进程型保证每个节点上都跑一个副本。日志采集、监控代理这类「必须每台机器上都有一份」的东西最适合它节点增加时它会自动补上。Job 是一次性任务跑完就结束适合数据迁移、批量计算这类场景。CronJob 则是定时任务按时间表达式周期性拉起 Job。区分它们的方法其实很朴素问一句「这个应用挂了随便换一个新的上来行不行」。行就是 Deployment不行、必须还是原来那个就是 StatefulSet必须是每台机器一个就是 DaemonSet只跑一次就不管了就是 Job需要按点跑就是 CronJob。2.3 Service 与 Label给会漂移的 Pod 一个固定门牌控制器解决了「Pod 数量对」的问题但没解决「怎么找到它们」的问题。Pod 的 IP 是临时的今天在 A 节点明天可能就在 B 节点前端如果直接把后端 IP 写死在配置里几乎必然踩坑。Service 就是干这个的它防止 Pod 失联为一组 Pod 定义统一的访问策略。它自己不跑业务进程靠的是标签选择器把符合条件的 Pod 圈进来再通过节点上维护的网络规则把流量分发过去同时承担四层负载均衡的职责。对外表现的形态是一个稳定的虚拟 IP 加端口Pod 怎么换都不影响调用方。那 Service 怎么知道该圈哪些 Pod靠 Label。标签是附加到资源上的键值对作用是关联对象、查询和筛选。这是 k8s 里非常核心的一个设计对象之间不靠硬编码的 ID 绑定而靠标签匹配。一个 Pod 可以同时被打上「应用web」「环境测试」两个标签于是既能被 Web 的 Service 选中也能被测试环境的巡检规则选中同一份数据被多条链路复用。最后是 Namespace。它把对象在逻辑上分隔开比如开发、测试、生产各占一个命名空间同名资源互不干扰权限和配额也能按空间划分。要注意的是它只是逻辑隔离不是网络隔离——跨命名空间访问默认是通的别把它当成安全边界。3. 集群里谁在干活控制面与节点的分工理解了对象模型接下来要问的是这些 YAML 交上去之后到底是谁在动手k8s 集群是典型的主从结构主节点承担控制与决策工作节点承担实际运行。控制面的四件套和工作节点上的三件事构成了整个系统的骨架。把这七个组件的职责记牢后面看报错日志时你就知道该去哪个方向找线索。3.1 控制面四件套入口、调度、控制器与账本kube-apiserver 是集群的统一入口也是各组件之间的协调者。它以 RESTful API 的形式对外提供服务所有对象资源的增、删、改、查和监听操作都要先交给它处理再提交给 etcd 存储。这个设计很关键它是唯一直接读写 etcd 的角色其他组件想改数据只能通过它从而保证了并发修改的一致性。kube-controller-manager 处理集群里的常规后台任务一个资源对应一个控制器而它负责管理这些控制器。上一节讲的 Deployment、StatefulSet其背后的「把实际状态拉向期望状态」的循环就是这些控制器在跑。它们不停地通过 apiserver 监听对象变化发现偏差就发起修正。kube-scheduler 负责调度根据调度算法为新创建的 Pod 选择一个 Node 节点。这个选择可以任意部署可能落在同一个节点上也可能分散在不同节点上。原文只说到「根据调度算法」实际决策时会综合考虑节点剩余资源、亲和与反亲和规则等因素。有个常见的误解是「调度器负责启动容器」——不是的它只负责拍板「放哪个节点」真正拉起容器的是节点上的代理。etcd 是分布式键值存储系统用来保存集群状态数据比如 Pod、Service 等对象信息。可以把它理解成集群的唯一账本整份声明式配置的事实来源都在这里账本丢了或坏了集群的「记忆」也就没了。这也是为什么生产环境里 etcd 的备份是头等大事属于那种平时想不起来、出事时最想找后悔药的东西。3.2 节点侧三件事代理、网络规则与容器引擎把视线转到工作节点上这里有三样东西在跑。kubelet 是主节点在节点上的代理管理本机运行容器的生命周期包括创建容器、为 Pod 挂载数据卷、下载 secret、获取容器和节点状态等工作。它会把每个 Pod 转换成一堆具体的容器。kubelet 是节点上最忙的组件很多「Pod 一直处于等待状态」的问题追到最后都出在它身上——比如镜像拉不下来、卷挂不上、节点资源不足。kube-proxy 在节点上实现 Pod 的网络代理维护网络规则并完成四层负载均衡。上一节说的 Service 虚拟 IP落到每台机器上就是由它维护的转发规则。它不解析应用层协议只管把到达某个地址和端口的流量按规则分给后端某一个 Pod。容器引擎负责真正运行容器。kubelet 把 Pod 翻译成容器的创建请求执行者是这一层。早期常见的是 docker 或 rocket 这类引擎现在具体用哪个属于集群搭建时的选型问题对使用者来说感知不大。3.3 一次提交的完整链路把组件串起来把组件串成一条线就清楚了。你用 kubectl 提交一份 YAML请求先到 kube-apiserverapiserver 校验通过后把它写进 etcd账本上就多了一条「期望状态」。kube-controller-manager 里的对应控制器监听到新对象发现「期望 3 个副本、实际 0 个」于是创建出 Pod 对象同样经由 apiserver 写进 etcd。kube-scheduler 监听到有 Pod 还没分配节点跑一遍调度算法选定一个节点后把结果写回该 Pod 的绑定信息。目标节点上的 kubelet 一直在监听「分给我的 Pod」发现新的任务就调用容器引擎把容器拉起来同时挂载数据卷、下载 secret。容器跑起来之后kubelet 会持续把容器和节点的状态回报上去而 kube-proxy 则在网络层面把 Service 的规则维护好让流量能打到这个新 Pod 上。这条链路解释了一个现象Pod 从提交到真正跑起来是有中间状态的卡在哪个环节就去看对应组件的状态和日志。只看最终结果不看链路排查起来就是纯玄学。4. 看得见才管得住监控、日志与应用生命周期线上出问题的时候最怕的是两眼一抹黑进程还在不在、资源够不够、日志里写了什么、最近一次变更是什么。k8s 提供的能力基本围绕这几件事展开分成三个层次——看集群和资源的「状态」看资源消耗和日志的「细节」以及对应用本身做升级、伸缩、回滚这些「动作」。4.1 从集群信息到对象详情先把现状看清楚最外层的入口是集群整体状态。kubectl cluster-info给出集群的基本连通信息kubectl cluster-info dump能导出更多集群信息排查控制面异常时经常从这里开始。这个命令输出量大通常配合重定向写到文件里再看。再往下是单个资源的详情。kubectl describe 资源 名称是最常用的一个它把某个对象的完整信息摊开事件、状态、挂载、最近的重启原因都在里面。Pod 起不来的时候先看这个命令末尾的事件列表往往比猜半天有效。实时观察则依赖 watch# 持续观察某个 Pod 的状态变化CtrlC 退出 kubectl get pod Pod 名称 --watch # 看 Pod 的简要列表-o wide 可以带出所在节点和 IP kubectl get pods -o wide--watch的含义是保持连接一旦这个对象发生变化就刷新输出。发布过程中盯着它能直观看到副本从旧版本切到新版本的全过程。参数上没什么花样把 Pod 名称换成具体资源名即可如果想观察一批对象省略名称、只用资源类型则会按列表整体观察。4.2 资源利用率与日志三个层次的排查入口知道对象「是什么状态」之后接着要知道「吃了多少资源」。这一步靠 Metrics-server 配合 cAdvisor 完成后者负责在节点上采集容器级的资源消耗数据前者把这些数据汇总成 k8s 的指标接口于是就能用熟悉的命令查看# 查看某个节点的资源消耗 kubectl top node node name # 查看某个 Pod 的资源消耗 kubectl top pod pod name如果这两条命令报错说找不到指标通常不是命令写错了而是集群里没有部署指标采集组件这属于环境问题而非操作问题。看到数据之后重点比对两件事Pod 的实际用量和你给它设的配额是否匹配节点整体余量是否还够再调度新副本。日志要分两个层次看。组件日志是 k8s 自己的日志比如 kubelet如果它由 systemd 管理就用journalctl -u kubelet查看如果某个组件是以容器形式跑在集群里的那就用kubectl logs kube-proxy-btz4p -n kube-system这样按 Pod 名去捞。系统层面的日志还可能要翻/var/log/messages。应用日志则是业务容器自己输出的内容走标准输出收集# 查看某个 Pod 的标准输出日志 kubectl logs Pod 名称 # 持续跟踪相当于 tail -f kubectl logs -f Pod 名称 # Pod 内有多个容器时用 -c 指定容器名 kubectl logs -f Pod 名称 -c 容器名称-f是持续跟踪-c是在多容器 Pod 里指定看哪一个。这两个参数组合起来是排障时的手感来源一边改配置一边盯着输出。如果希望日志落到节点本地文件而不是只走标准输出可以在 Pod 里把日志目录挂载到宿主机路径上比如把容器内的日志目录挂到宿主机的某个目录这样即使容器重启文件也还在。4.3 升级、伸缩与回滚Deployment 的四个动作应用生命周期管理最日常的四个动作都能用命令完成。先创建一个 Deployment# 用镜像创建一个 Deployment默认 1 个副本 kubectl create deployment web --imagejava-demo:1.0 # 查看 Deployment 和 Pod 是否都起来了 kubectl get deploy,pods接着把它暴露出去让集群外能访问# 把 80 端口暴露为 NodePort 类型的服务转发到容器的 80 kubectl expose deployment web --port80 --typeNodePort --target-port80 --nameweb kubectl get service--port是 Service 自身对外提供的端口--target-port是容器实际监听的端口--typeNodePort表示在每个节点上开一个端口对外暴露。这两个端口不是一回事后面还会专门讲。升级镜像、观察进度、出问题回滚是发布时的标准三部曲# 把容器镜像换成新版本 kubectl set image deployment/web webjava-demo:2.0 # 查看发布进度直到完成或失败 kubectl rollout status deployment/web # 查看历史版本列表 kubectl rollout history deployment/web # 回滚到上一个版本 kubectl rollout undo deployment/web # 回滚到指定版本 kubectl rollout undo deployment/web --to-revision2rollout status会阻塞等待直到新副本全部就绪或者超时失败这是判断发布成败最直接的依据。--to-revision指定回滚到哪个历史版本版本号来自 history 的输出。回滚本身不是万能药它只回退镜像和配置不解决数据库结构变更这类不可逆的操作发布前想清楚这一点能省掉很多麻烦。伸缩则是改副本数# 手动把副本数改成 10 kubectl scale deployment web --replicas10 # 配置自动伸缩10 到 15 个副本之间CPU 平均利用率超过 80% 就扩容 kubectl autoscale deployment web --min10 --max15 --cpu-percent80手动伸缩适合提前预判的流量高峰自动伸缩适合波动不可预测的场景但前提是容器配置了 CPU 的 requests否则百分比无从计算。删除资源同样简单kubectl delete deploy/web删 Deploymentkubectl delete svc/web删 Service。注意删掉 Deployment 会连带清掉它管理的 Pod删 Service 则只影响访问入口不影响后端还在跑的业务。5. 五组最容易混淆的概念弄清楚边界比记命令重要命令记不住可以查概念混淆了会一路错下去配置写错、排查方向错、方案选型也错。下面五组是初学者最容易搞混的搭档每一组都按「混在哪、为什么混、怎么区分」说清楚。5.1 误区一Pod 就是容器最容易混的一点是把 Pod 和容器画等号。混的原因很自然——大多数时候一个 Pod 里确实只有一个容器用起来和直接跑容器没区别。区分的关键在于Pod 是一个逻辑概念本身没有对应的实体进程它是「一组容器 共享的网络命名空间 共享的存储卷」的组合。判断标志很简单当你需要两个进程通过 127.0.0.1 通信、或者频繁读写同一份文件时它们才应该被放进同一个 Pod如果两个进程之间只是普通的 HTTP 调用那它们应该是两个独立的 Pod各自独立伸缩、独立发布。把不该放一起的塞进一个 Pod最直接的后果是没法分别扩容——一个忙一个闲你只能整体加副本白白浪费资源。5.2 误区二Deployment 能管一切因为入门示例几乎都用 Deployment很多人会默认它是万能的。混的原因在于它的使用体验太顺创建、升级、回滚、伸缩一条龙几乎不需要额外操心。区分看的是应用有没有身份。Deployment 管的是无状态应用副本之间可互换Pod 名字是随机后缀重启后换一个完全没问题。StatefulSet 管的是有状态应用每个副本有稳定的序号和稳定的存储重启后还是原来那个身份。判断标志是问一句这三个副本里能不能随便挑一个来处理这个请求能用 Deployment不能用 StatefulSet。选错了的代价通常在数据层爆发而不是在部署时报错这才是它危险的地方。5.3 误区三requests 和 limits 是一回事这两个词都出现在资源配额里写法又很像混在一起几乎是必然的。它们其实是两个方向的东西。requests 是申请配额代表容器启动和调度时至少要拿到多少资源调度器就是按这个数值判断某个节点放不放得下。limits 是限制配额代表容器运行时最多能用多少超过之后 CPU 会被限流、内存可能被终止。也就是说requests 影响「能不能被调度上去」limits 影响「跑起来之后会不会被打断」。apiVersion: v1 kind: Pod metadata: name: web spec: containers: - name: java image: java-demo:1.0 resources: requests: # 调度依据至少给我这么多 memory: 64Mi cpu: 250m limits: # 运行上限最多只能用这么多 memory: 128Mi cpu: 500m这里的250m表示 0.25 个 CPU 核心Mi是内存单位。常见的错误是只写 limits 不写 requests结果调度器按默认值判断实际部署密度和预期完全不符更糟的是 limits 设得和实际用量贴太近业务稍有波动就被限流或杀掉表现为「莫名其妙变慢」。5.4 误区四镜像拉取策略设成 Always 就等于用最新镜像imagePullPolicy有三个取值看着像「总是拉最新」其实不是这个意思。IfNotPresent是默认值镜像在宿主机上不存在时才拉取Always表示每次创建 Pod 都会重新拉取一次镜像Never表示 Pod 永远不会主动拉取这个镜像。混的地方在于把「重新拉取」理解成「拿到最新版本」——如果镜像标签写的是固定版本号重新拉取拿到的还是同一个版本只是多花了一次网络时间如果标签写成latest这类浮动标签那问题更大因为不同节点上缓存的时间不同同一个 Deployment 的多个副本可能跑着不同代码排查时非常难受。合理的做法是版本号写死策略按场景选固定的正式版本用IfNotPresent减少拉取开销确实需要每次强校验的场合用Always离线环境中镜像必须预先放好才考虑Never。5.5 误区五Service 的 port 和 targetPort 是同一个端口暴露服务时写--port80 --target-port80两个数字一样于是很容易认为它们是一回事。实际上port是 Service 这一层对外提供的端口target-port是容器内部真正监听的端口两者完全可以不同。比如容器里的应用监听 8080你希望外部统一用 80 访问那就写--port80 --target-port8080。再加上NodePort类型时节点上还会另开一个端口同一个服务可能同时存在三层端口号。区分的方法是记住方向从外往里数节点端口 → Service 端口 → 容器端口每一层都可以不同。三个数字混着填最常见的现象是 Service 起来了但访问不通而这在配置里看起来毫无问题。6. 什么时候该用 k8s什么时候老实点这套东西很强但不是所有场景都值得上。判断标准其实只有几条机器数量、发布频率、可用性要求、团队有没有人能维护它。我的经验是分三档看。第一档单机就能跑完、停机十分钟也没人投诉的小项目直接一个进程加进程守护就够了上集群属于给自己找活。第二档机器有几台需要零停机发布、需要按流量扩缩、需要故障自愈这时候 k8s 的收益开始明显压过成本。第三档多环境多团队、需要资源隔离和配额管理那它几乎是绕不开的基础设施。场景特征建议理由单机可承载、无可用性要求不用引入的复杂度远大于收益需要滚动发布、快速回滚用升级与回滚有现成机制出问题能退流量波动大、需要自动扩缩用按资源指标自动增减副本有状态且强依赖单机磁盘谨慎需要额外处理存储与身份成本明显上升团队无人熟悉集群运维缓一缓控制面与 etcd 的维护需要专人盯至于「什么时候别用」我更愿意换个说法什么时候别急着用。刚接触时最典型的翻车方式是拿生产环境练手配置全用默认值requests 不写、健康检查不配、etcd 不备份跑起来一切正常直到某天节点资源耗尽才发现没有后悔药。比较稳妥的路径是先在测试环境把 Pod、控制器、Service 这三层关系摸熟把监控和日志接上再考虑承载真实流量。还有一点是排障习惯。刚上手时我遇到 Pod 起不来第一反应总是反复改 YAML改十几次都不见效后来才养成一个笨办法先kubectl describe看事件再看kubectl logs看输出最后才回头改配置。从那以后我每次遇到异常都强制走一遍这条链路先看事实再动手能省掉一大半瞎试的时间。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?