尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Kubernetes入门到实战:架构原理、核心对象与集群部署指南

Kubernetes入门到实战:架构原理、核心对象与集群部署指南 Kubernetes 这几年的热度不用我多说了从互联网大厂到传统企业的运维团队再到个人开发者的 side project几乎都在聊它。但很多人学 Kubernetes 的时候卡在同一个地方文档看了一堆术语记了一堆真到要部署、要排查问题的时候还是懵。我最早接触 K8s 的时候也有这种感觉它不像 Docker 那样“一条命令跑起来”那么直观而是一整套由多个组件组成的系统。真正把它的架构逻辑理清楚之后你会发现它其实并不神秘甚至核心思想可以用一句话概括把服务器集群当成一台计算机来用。这篇东西我打算按照自己实际学习和使用 Kubernetes 的路径来写从“容器编排到底解决什么问题”开始到核心组件拆解、常用工作负载对象、手动部署一套可用集群的完整过程最后再把那些我在实操和面试中反复遇到的坑和考点整理出来。不管你是刚接触云原生的新手还是已经写了几个 Deployment 但没系统性梳理过原理的开发者这篇内容应该都能帮你在脑子里搭起一张完整的地图。1. 容器编排到底是干什么的Kubernetes 为什么能成为默认答案1.1 只有 Docker 的时候我们缺的是什么先回顾一个很基础的问题Docker 已经能把应用打包成镜像一条docker run就能起一个容器那为什么还需要 Kubernetes早期我维护的几台服务器是这么跑的写一个 shell 脚本里面定义几十个docker run命令每个容器指定端口映射、挂载目录、环境变量。服务器重启的时候手动执行一下脚本让容器全部拉起来。这套方案在服务器数量少、服务数量少的情况下确实能跑但一旦规模上来问题就非常明显了。第一个问题是单点故障。某个容器挂了脚本不会自动把它拉起来监控告警之后还得人肉上去处理。第二个问题是资源利用率不均衡。有的服务器 CPU 空闲有的已经跑满但你没有机制把负载迁过去。第三个问题是服务发现的成本。微服务架构里 A 服务要调用 B 服务如果 B 服务部署在不同机器、端口也不固定A 服务的配置文件就得跟着环境不断改。第四个问题是滚动更新和回滚。你没法做到“一批一批停旧起新”只能粗暴地全部停掉再全部启动这个过程必然有 downtime。这些问题的本质是你需要一个系统让它代替人去管理跨越多台机器的容器生命周期、网络通信和资源分配。这就是容器编排系统的职责。1.2 为什么最终是 Kubernetes 胜出容器编排这个概念并不是 Kubernetes 首创。在它之前有 Docker Swarm 和 Apache Mesos后来还有 HashiCorp 的 Nomad。从时间线来看Docker Swarm 作为 Docker 官方的原生编排方案上手难度最低安装最方便很多中小团队早期确实选过它。Mesos 则是大数据领域用得更多它的定位偏向“数据中心的资源调度系统”要跑在线业务和离线任务调度的场景。Kubernetes 最初是 Google 内部的 Borg 系统开源出来的它的设计起点就很高Google 内部十几年的超大规模集群管理经验直接转化成了它的架构基因。它的生态扩展性太强了API 设计使得所有功能都以声明式 API 的形式暴露出来第三方开发者可以通过自定义资源CRD和 Operator 模式做任意扩展。到了今天新版 Docker 已经默认集成了 Kubernetes 集群模式云厂商的托管服务也全部兼容 Kubernetes API无论是 EKS、GKE 还是国内的 TKE、ACK说白了都是“各家云厂商帮你把 Kubernetes 控制面管起来你只负责买工作节点”。生态的力量是巨大的。当所有周边工具——日志采集、监控告警、服务网格、CI/CD——全部围绕 Kubernetes API 来做的时候它就成了事实上的标准其他方案再想追赶几乎不可能。1.3 一台机器 vs 一个集群理解 K8s 的思维转变我踩过最大的一个坑就是习惯性地用单机思维去理解 Kubernetes导致 Deployment 怎么写都觉得别扭。单机部署的思路是我要启动一个容器指定镜像、端口、数据卷然后它就跑起来了。K8s 的思路是完全反过来的你先告诉我“最终期望状态是什么”比如这个应用需要 3 个副本、每个副本用哪个镜像、暴露哪个端口、需要多少 CPU 和内存然后系统自己去决定怎么达成这个状态。这种思路叫声明式 API是 Kubernetes 最核心的设计哲学。你写的不再是“如何启动”的步骤而是“最终长什么样”的声明。系统里的各个控制器Controller会持续监听当前状态和期望状态的差异不断把实际状态向期望状态逼近。这就像你把空调设定在 26 度空调会自动制冷或制热来维持这个温度而不是你去手动开关压缩机。建立这种思维之后后续学 Pod、Deployment、Service 都会顺畅很多。因为你理解了一个前提Kubernetes 是一个自愈系统它要的不是一条条命令的执行结果而是一份描述期望状态的清单。2. 架构拆解控制面和工作节点各自在忙什么2.1 控制面集群的“大脑”Kubernetes 集群分两部分控制面Control Plane和工作节点Worker Nodes。控制面负责“决策”工作节点负责“干活”。控制面里最重要的组件是kube-apiserver它是整个集群的入口所有组件的通信都要经过它。你可以把 apiserver 理解成公司的前台所有请求都要在这里登记、鉴权、校验然后才转给后面的部门处理。你执行的kubectl命令本质上全是在向 apiserver 发送 REST API 请求。etcd是 Kubernetes 的数据库所有集群状态都存储在 etcd 里。注意这里存的是“状态”不是业务数据。一个 Pod 被创建了、被删除了、一个 Deployment 期望 3 个副本——这些信息都存在 etcd 里。etcd 是个分布式键值存储为了保证数据一致性它使用 Raft 共识算法通常部署 3 或 5 个副本。生产环境集群挂掉很多时候跟 etcd 有关因为它对磁盘 IO 要求比较高底层存储不能太慢。kube-scheduler负责决定新创建的 Pod 应该调度到哪台工作节点上。它会把节点筛选一遍先过滤掉资源不足的节点再根据预置的打分策略给候选节点打分然后挑出最优解。调度的时候会考虑的因素包括节点剩余资源、Pod 是否绑定了特定节点、是否有亲和性要求、数据卷是否在固定可用区等。kube-controller-manager是一个组合角色里面跑着一堆控制器比如 ReplicaSet 控制器负责保证副本数符合预期Node 控制器负责检测节点心跳Endpoints 控制器负责维护 Service 背后的 Pod 列表。这些控制器做的事情本质相同watch 期望状态对比当前状态然后执行动作去对齐。2.2 工作节点真正跑业务的地方工作节点上的组件相对简单核心是三个。kubelet是每个节点上的“管家”它负责和 apiserver 通信接收指令并管理本节点上的 Pod。kubelet 会定期向 apiserver 上报节点状态和资源使用情况同时通过探针判断容器是否存活、是否就绪。你在 YAML 里配置的 livenessProbe 和 readinessProbe最终就是由 kubelet 来执行的。kube-proxy负责实现 Service 的负载均衡功能。它维护着节点上的网络规则把发往 Service 虚拟 IP 的流量转发到后端的 Pod。早期的实现用的是 iptables后来逐渐演进到 IPVS性能更好、规则更灵活。注意 kube-proxy 做的是四层转发不关心 HTTP 层的东西真正的七层路由需要靠 Ingress Controller 来做。容器运行时也不只是 Docker。理论上只要实现了 Kubernetes CRIContainer Runtime Interface接口的运行时都可以接入比如 containerd、CRI-O。从 Kubernetes 1.24 开始Dockershim 被正式移除所以现在新集群默认装的基本都是 containerd。对你来说区别不大日常写 YAML 不涉及直接操作容器运行时但排查问题的时候要知道底层跑的其实是 containerdctr和crictl这两个命令要会用。2.3 一次完整 Pod 创建流程的“旅行”我习惯把创建 Pod 的过程讲成一个故事因为理解了这条链路排错的时候就有方向了。你执行kubectl apply -f deployment.yaml请求到达 apiserver经过认证鉴权、资源校验之后Deployment 对象被写入 etcd。Deployment 控制器发现“期望 3 个副本实际 0 个”于是创建 ReplicaSetReplicaSet 控制器再去创建 3 个 Pod 对象。这些都是控制面内部的响应链。新 Pod 进入 pending 状态scheduler 监听到这种情况开始调度决策选定一个合适的节点把结果写回 etcd同时通过 apiserver 通知对应节点的 kubelet。kubelet 从本地容器运行时拉取镜像、启动容器、配置网络然后把容器运行状态回报给 apiserver。最终你执行kubectl get pods看到 Pod 变成 Running 状态。整个过程里最容易出问题的环节是镜像拉取失败、调度器选不中节点、kubelet 启动容器时报错、探针检测失败导致 Pod 被反复重启。后面我会把这些实际现象和排查方法集中整理出来。3. 绕不开的核心对象从 Pod 到 PVC 的完整认知3.1 Pod容器的最小调度单位很多初学者会被一个概念搞混Kubernetes 的调度单位是 Pod 而不是容器。Pod 是可以在一个节点上运行的一组容器的集合里面的容器共享同一个网络命名空间、共享同一个数据卷。为什么需要 Pod 而不是直接调度单个容器因为有些场景里多个进程必须紧密协作比如日志采集 sidecar 要跟着主容器一起跑或者一个代理容器和业务容器需要共享 localhost 通信。把这些进程打包成一个 Pod它们就可以被调度到同一台机器上共享网络和存储方便管理。在实际使用中大多数 Pod 里只跑一个容器最常见的两个容器的场景是一个业务容器加一个日志采集 sidecar或者一个业务容器加一个网络代理 sidecar。要注意的是Pod 被认为是“可牺牲”的。某个节点挂了上面的 Pod 不会像虚拟机那样被恢复而是由控制器在其他节点重新创建。所以不要把 Pod 当宠物养要把它当牲口用——这句话在 K8s 社区里很流行。3.2 Deployment副本管理的最佳实践Deployment 是日常用得最多的工作负载类型它的职责很简单声明副本数量、管理滚动更新和回滚。看一个最小示例apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deploy spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80这里的关键是selector.matchLabels和template.metadata.labels要匹配。Deployment 通过 label selector 找到自己管理的 Pod所以这个标签是绑定关系的核心绝对不要随意修改。如果你改了模板里的标签却没改 selectorDeployment 会创建一个新的 ReplicaSet旧的 Pod 也会因为不再匹配而变成一个“孤儿”这是新手特别容易踩的坑。Deployment 还引入了 ReplicaSet 这个中间层。每次镜像版本更新Deployment 会创建一个新的 ReplicaSet然后把旧 ReplicaSet 的副本数逐渐缩减到 0。这样做的好处是天然支持回滚——只要保留旧 ReplicaSet就能一键切回去。更新的策略默认是 RollingUpdate会控制新旧副本交替保证服务不中断。3.3 Service 和 Ingress流量怎么进来Pod 的 IP 不是固定的因为 K8s 会把某个 Pod 调度到任意节点上Pod 挂了之后由新的 Pod 顶替IP 也会变。所以你需要一个稳定的访问入口这就是 Service。Service 提供的是一个集群内部的稳定虚拟 IPCluster IP它通过 label selector 匹配后端 Pod把流量转发到其中某一个。最常用的类型是 ClusterIP集群内部可以访问如果想从外部访问可以用 NodePort把节点的某个端口映射到 Service或者配合负载均衡器用 LoadBalancer 类型。Service 是四层负载均衡它不关心 HTTP 路径。如果你需要根据域名、URL 前缀来做路由就得用 Ingress。Ingress 不是真正干活的人它只是一份规则声明真正处理流量的是 Ingress Controller比如 Nginx Ingress Controller、Traefik。Ingress Controller 以 Pod 形式跑在集群里根据 Ingress 规则动态更新自己的 Nginx 配置把外部请求按域名和路径转发到对应的 Service。一个简化示例apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: my-app spec: rules: - host: app.example.com http: paths: - path: / pathType: Prefix backend: service: name: my-app-svc port: number: 80生产环境里同一个 Ingres Controller 可以管理多个域名、多套证书这也是云原生应用最常见的流量接入方式。3.4 配置、密钥和持久化ConfigMap、Secret、PV/PVC把配置写死在镜像里是很糟糕的做法因为不同环境测试、预发、生产要的配置不一样你总不能给每个环境打一个镜像。Kubernetes 用 ConfigMap 来解决这个问题。ConfigMap 可以理解成一组键值对你用 Volume 挂载的方式把它注入到 Pod 里或者通过环境变量的方式传入容器。这样镜像内容不变只需要在不同环境应用不同的 ConfigMap 即可。Secret 和 ConfigMap 用法几乎一样区别在于它专门用来放敏感信息比如数据库密码、API Token存储的时候会做 base64 编码还能结合外部密钥管理系统做加密。持久化存储是另一个重要话题。Pod 本身是无状态的数据存在容器里的话 Pod 一删就没了。要持久化数据得用 PVPersistentVolume和 PVCPersistentVolumeClaim这套抽象。PV 是集群里的存储资源比如一块云硬盘、一个 NFS 共享目录PVC 是用户对存储资源的申请。你在 Pod 里声明用某个 PVCKubernetes 就会帮你把对应的 PV 挂载进来。这套抽象的价值是解耦应用不需要关心底层存储到底用的是云盘还是 NFS它只需要声明“我要多少空间我按什么模式访问”。存储的实际提供全部由管理员和存储插件去解决。4. 实操部署用 kubeadm 从零搭建一套 Kubernetes 集群4.1 准备工作节点规划与系统配置部署一套可用的集群我推荐用 kubeadm它是最主流的标准安装工具。你可以准备 3 台机器1 台做控制面节点2 台做工作节点。机器最好是 2 核 4G 以上因为 K8s 组件的资源消耗不小等等。系统用 Ubuntu 22.04 或者 CentOS 7 都行但要注意版本兼容。在开始之前三个节点都需要做一些基础配置。关闭 swap。K8s 要求在节点上禁用 swap因为它的内存管理和调度依赖真实内存。执行swapoff -a并且把/etc/fstab里 swap 相关的行注释掉。然后加载内核模块modprobe overlay modprobe br_netfilter接着创建配置文件/etc/modules-load.d/k8s.conf写入这两个模块名字确保重启后自动加载。再设置内核参数/etc/sysctl.d/k8s.confnet.bridge.bridge-nf-call-iptables 1 net.ipv4.ip_forward 1 vm.swappiness 0执行sysctl --system让它生效。前两个参数是为了让 iptables 能正确处理桥接流量对集群网络至关重要没设的话后面 Pod 之间通讯会出很奇怪的问题。4.2 安装容器运行时和 K8s 组件先装容器运行时推荐 containerd。Ubuntu 上可以直接用命令安装然后配置镜像加速。containerd 默认的 sandbox 镜像是registry.k8s.io/pause:3.x在国内网络环境下拉取会有问题需要在/etc/containerd/config.toml里把 sandbox_image 改成国内可访问的镜像地址然后重启 containerd。装 K8s 组件的过程就是添加 apt 源然后安装kubeadm kubelet kubectl。这里有个小技巧指定版本号安装比如kubeadm1.28.2-00避免系统更新把组件升到不兼容的新版。注意三个组件的版本要一致不然会出现 API 版本不匹配的问题。安装完成之后用kubeadm init初始化控制面节点。初始化时指定 Pod 网段非常关键因为后面装网络插件比如 Calico的时候网段必须和它保持一致kubeadm init \ --apiserver-advertise-address192.168.1.10 \ --pod-network-cidr10.244.0.0/16 \ --image-repositoryregistry.aliyuncs.com/google_containers初始化过程会拉取一组控制面组件镜像如果拉不到就用--image-repository参数指向镜像站。初始化成功后会输出一串 kubeadm join 命令保存好它是工作节点加入集群的凭证。然后配置 kubectl 的访问权限具体步骤是把/etc/kubernetes/admin.conf复制到用户目录下并设置 KUBECONFIG 环境变量。这一步不做的话根用户之外没法使用 kubectl。4.3 安装网络插件与节点加入控制面初始化完成之后kubectl get nodes能看到控制面节点但状态多半是 NotReady因为节点还没装网络插件。这里我以 Calico 为例它是最常用的 CNI 插件之一kubectl create -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml装完之后 Calico 会以 DaemonSet 的形式在每个节点上运行 Pod。过一会儿再kubectl get nodes状态应该变成 Ready。如果一直是 NotReady优先检查 Calico Pod 是不是有报错特别是网络插件镜像拉取失败的情况。工作节点加入集群就一条命令在你之前保存下来的 join 命令后面执行。如果 join 命令丢了可以在控制面节点上用kubeadm token create --print-join-command重新生成。节点加入之后会自动安装 kubelet 和 containerd然后开始拉取必要的镜像。4.4 部署一个示例应用验证集群集群就绪之后可以跑一个示例验证kubectl create deployment nginx --imagenginx:1.25 --replicas3 kubectl expose deployment nginx --port80 --typeNodePort kubectl get pods -o wide kubectl get svc nginx看到 3 个 Pod 分布在不同节点上、Service 暴露了 NodePort 端口就可以用curl http://任意节点IP:NodePort访问 Nginx 了。能够返回 Welcome to nginx 页面说明这套集群的调度、网络、负载均衡都已经正常工作。如果你需要部署的是有状态应用或者更复杂的项目比如社区里很火的 AI 工具代理服务或者需要做云原生改造的开源 CMDB思路也是一致的把应用容器化写 Deployment 和 Service 声明然后 apply 到集群里。Kubernetes 并不会关心你跑的是什么业务它的核心能力就是调度、自愈、扩缩容和网络打通。5. 实操中常踩的坑和面试高频考点5.1 部署和排错的真实案例我在搭集群和日常用 K8s 的过程中踩过的坑不少挑几个典型的说出来你们碰到可以少走弯路。第一个是kubeadm init 一直卡住。这种情况多半是拉镜像失败或者等待 kubelet 启动超时。可以用kubeadm reset清掉初始化残留再systemctl status kubelet看 kubelet 有没有报错。注意 kubelet 的日志路径在/var/log/messages或journalctl -u kubelet里面有具体的失败原因。第二个是Pod 一直处于 ImagePullBackOff。这表示镜像拉取失败。可能原因包括镜像标签写错、私有仓库没配置认证、镜像仓库被墙。先kubectl describe pod看事件详情如果是认证问题提前建一个 Secret 并在 Deployment 里指定imagePullSecrets。第三个是Service 域名解析通但访问返回连接拒绝。这通常是 Service 的 selector 和 Pod 标签不匹配导致 Endpoints 列表为空。用kubectl get endpoints查看如果没有任何地址说明 Service 没找到后端 Pod去检查 labels 是不是写对了。第四个是滚动更新时出现短暂 502。如果应用启动时间比较长readinessProbe 没配或者配置的 initialDelaySeconds 太短新 Pod 还没就绪就被放入了负载均衡池流量就全打过去了。建议在 Pod 里加上 readinessProbe给它足够的启动缓冲时间readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 10 periodSeconds: 55.2 面试高频考点从命令到原理结合热词里出现的 Kubernetes 面试题我整理几个最常被问到的方向。“简述 Kubernetes 创建一个 Pod 的完整流程”——考的是对整个控制链路是否清楚要能说清楚从 kubectl 到 apiserver、etcd、scheduler、kubelet、container runtime 的完整链路。“Deployment、ReplicaSet、Pod 三者的关系”——很多人只背了“Deployment 管理 ReplicaSetReplicaSet 管理 Pod”但面试官真正想听的是Deployment 通过控制 ReplicaSet 来实现滚动更新ReplicaSet 通过控制 Pod 副本来实现自愈为什么要多一层中间抽象。“Service 的 ClusterIP 是怎么实现的”——这里可以讲到 kube-proxy 的 iptables 模式和 IPVS 模式的区别。iptables 模式用 DNAT 规则IPVS 模式用内核态的负载均衡性能更好、支持的调度算法更多。“Pod 的存活探针和就绪探针区别”——存活探针决定要不要重启容器就绪探针决定要不要把流量打过来。只要记得这个核心区别再补一个各自适合什么场景的例子就够了。“如何在不中断服务的情况下更新镜像”——RollingUpdate 策略maxUnavailable 和 maxSurge 参数怎么控制新老副本交替的比例。最好能举一个线上配置示例。5.3 云原生生态延伸从容器编排到全栈云化学会 Kubernetes 基础之后你会发现围绕它生长出来的整个云原生生态才是真正的宝藏。现在很多开源项目都在做云原生适配比如开源 CMDB 系统用 Kubernetes 做部署底座能力更强的 AI 工具代理类项目也普遍用容器编排来做多实例部署和自动扩缩容。这里有一个明确的学习路径可以参考。先掌握 Helm它是 Kubernetes 的包管理器把你写好的 Deployment、Service、ConfigMap 打包成一个 Chart。以后部署一套复杂的应用就一行命令的事升级回滚也有版本管理。然后是 Operator 模式用自定义控制器来管理有状态应用比手工管理 StatefulSet 方便很多。再往后是现代应用的可观测性建设Prometheus 负责指标采集Grafana 负责展示Loki 或 ELK 负责日志OpenTelemetry 做链路追踪。这一套搭完你才能真正做到“业务跑在 K8s 上流量可观测、故障可定位”。我个人在实际操作中的体会是Kubernetes 的学习曲线陡峭但它其实是把运维的复杂性集中收敛到了一个地方然后把接口统一暴露给你。前期最有效的学习方式不是背命令而是亲手把一个集群搭起来把常见故障排一遍再回头去看文档里的架构描述你会发现那些抽象概念全部能对应到具体的组件和日志上。后面再用 Helm 管理应用、用 Operator 跑数据库、接入普罗米修斯监控这套技能组合基本就能覆盖绝大部分日常工作了。
返回列表