Kubernetes(K8s)容器编排
Kubernetes(K8s)是容器编排系统:管理一组服务器上的容器,决定容器跑哪、跑几个、死了怎么办、怎么更新、流量怎么进。
为什么需要它。 一台机器上 Docker Compose 就够了,容器跑哪全靠手写。机器和容器一多,手写就失效了:机器一死,它的容器要找新家;流量一涨,要加副本;没人能靠手跟踪每一个容器。Kubernetes 用声明的期望状态加对账循环代替手写:你声明想要什么,系统持续把现实推向期望状态。
集群全景
一个集群就是一个控制面加一组服务器。用户流量和管理流量走两条线:用户流量不经过 API Server,管理流量不经过 Ingress。
容器
容器就是 Docker 那种容器:一个打包好的进程,带自己的文件系统,用 Linux 命名空间和 cgroup 跟别的进程隔开。 Kubernetes 自己不跑 Docker daemon,它通过容器运行时跑容器,默认是 containerd。Docker 仍是做镜像最常见的工具。
Pod
Pod 是部署的最小单位:一组容器,共享同一个 IP 和可共享的存储卷,调度时整组一起落到一台节点上,不跨节点。 调度单位必须是 Pod 而不是单个容器,因为调度器是按整组算资源账的:两个容器各要 1 核,分开看都放得下,合起来 2 核可能放不下。必须共享网络或存储、同生共死的容器才放进同一个 Pod,典型形状是边车模式:应用容器加一个日志或代理边车。能独立扩缩的服务留在各自的 Pod 里。
Pod 和节点是多对一,不是一对一。 一个 Pod 只落在一台节点上,一台节点上跑很多个 Pod。
节点、kubelet、kube-proxy、CNI
节点就是集群里的一台机器,物理机或虚拟机。 每台节点上跑三个节点级组件。kubelet 是节点上的系统进程,节点的管家:它不是 Pod,也不是容器。 它替控制面在节点上起停容器、跑探针、上报状态。kube-proxy 负责节点上 Service 的转发规则。 容器网络接口(Container Network Interface,CNI)插件给 Pod 分 IP、打通跨节点 Pod 网络。
控制面
控制面是集群的大脑。API Server 是唯一入口:所有读写都经过它,组件之间不直接通信。 etcd 是存整个集群期望状态的分布式键值数据库;控制面其他组件本身无状态,挂了重启能从 etcd 恢复。 调度器决定新 Pod 落哪台节点。 控制器管理器是一组控制器,各管一摊对账:Deployment 控制器管 ReplicaSet、ReplicaSet 控制器管 Pod 数、节点控制器管节点健康、Endpoints 控制器维护那张表。 云环境下还有云控制器,管负载均衡器、节点生命周期这些云厂商相关的活。
期望状态与对账
Kubernetes 里一切都是声明式的:你提交 Deployment、Service 这些对象,控制器跑对账循环,把现实推向声明的期望状态。Deployment 不直接管 Pod,它管 ReplicaSet,滚动更新就是一个 ReplicaSet 缩下去、另一个 ReplicaSet 升上来。
调度
调度是一次性决策。 Pod 一旦被调度到某台节点,正常情况下就待在那儿,Kubernetes 默认不会为了均衡去搬运行中的 Pod。placement 只在 Pod 重建时变:节点故障、驱逐、滚动更新、扩容产生新 Pod。流量本身不搬已运行的 Pod,流量通过扩缩容改变 Pod 数量,新 Pod 才有新的调度决策。
调度器读 Pod 定义,定义里一切都提前写清楚了:每个容器的资源需求、卷、节点选择、亲和性、容忍、端口。它把 Pod 里所有普通容器的 request 加起来作为整组要占的资源,过滤掉不符合条件的节点(资源不够、卷挂不上、有污点、亲和性不合),再打分挑最合适的。临时卷跟 Pod 走,不影响调度;持久卷才影响,因为盘可能只存在于某个可用区、只能挂到特定节点上。两个边界要记住:调度器按声明的 request 算账,不看容器运行时实际用了多少,request 写小了就会把太多 Pod 塞到一台节点上;limit 只管运行时不超用,不管调度。
探针
探针配在容器身上,写在 Pod 定义里每个容器自己的配置中,由所在节点的 kubelet 执行。 就绪探针决定容器进不进 Endpoints 表:起了但还没就绪的容器不接流量。存活探针决定挂了的容器重不重启:它只重启容器,不碰表。就绪管流量,存活管重启。
Service 与寻址
Service 对象就是全部要写的寻址配置,一个服务写一次:名字、对应哪组 Pod 的选择器、调用方用的端口、容器实际监听的目标端口。 默认的 Service 类型就是 ClusterIP,只在集群内有效。调用方代码里写的就是这个名字,不是 IP、不是节点、不是 Pod,这个名字永远不变。
名字背后跑三步:集群域名系统(Domain Name System,DNS)服务 CoreDNS 把名字解析到 ClusterIP;ClusterIP 是虚拟 IP,不对应任何真实网卡,所以不同服务都用 80 端口也不冲突,端口空间是按 IP 分的;kube-proxy 按 Endpoints 表把流量转发到就绪的 Pod。Pod 死了换节点、IP 变了,Endpoints 表自动更新,只有就绪的 Pod 才进表,调用方只认名字,一行代码不用改。
工作负载对象
ReplicaSet 管数量:保证指定数量的 Pod 副本一直跑着,死一个补一个,多了删掉。 它的副本是平等副本,个个都干活,没有主、没有备。高可用和容量是同一机制的两个效果:死一个还有剩下的接流量,N 个副本一起扛流量。ReplicaSet 只管数量、不管版本,一般不直接建,建 Deployment,Deployment 会替你建它、管着它。StatefulSet 管有状态服务:固定名字、固定盘、有序启停,换 Pod 不换盘。 DaemonSet 管每节点一份:每个符合条件的节点上恰好一个 Pod,跟着节点数走,不跟着副本数走,监控 agent、日志收集是典型用途。 Job 是跑完即停的一次性任务,CronJob 是定时版。
Ingress 与内外之分
Ingress 对象是纯配置:一条规则,写清楚哪个域名、哪条路径对应哪个 Service,它自己不收流量、不转发流量。 Ingress Controller 是真正干活的网关:现成的组件,一般是 Nginx、Traefik 跑成的 Pod,里面的控制循环盯着 Ingress 对象、规则一变就重新生成自己的网关配置并 reload,数据面真正收外部流量、按规则转发。 全新集群默认没有 Ingress Controller,得装一次,装好之后它就是集群里的普通工作负载、由 Kubernetes 自己管着。
对外暴露是 opt-in,内网是默认。 没有 Ingress 规则的 Service,外面根本进不来,不用额外配内网。服务和服务之间的调用不走 Ingress:东西向直连,服务 A 直接叫服务 B 的名字,集群 DNS 解析、kube-proxy 转发,Ingress 规则对内网调用完全不起作用。两点诚实补充:默认 Pod 之间是互通的,想真正隔离得加 NetworkPolicy(需要网络插件支持);管理流量(kubectl)不走 Ingress,走跳板机、VPN 或者云厂商的私有端点。
加节点
加节点是热操作:集群一直跑着,API Server、etcd、调度器都不重启,已有的 Pod 也不受影响。 自建集群用 kubeadm(Kubernetes 自带的集群搭建工具),控制面生成一条加入命令,里面有三样东西:API Server 的地址、一个短期邀请码(bootstrap token,有有效期,像一次性邀请码)、控制面证书的哈希(让新节点验明正身,确认自己连的是真的控制面、不是冒充的)。新节点的 kubelet 拿邀请码连上 API Server,做传输层安全协议(Transport Layer Security,TLS)bootstrap:提交证书申请,批下来后领到属于自己的节点证书,以后就用证书认证,邀请码就作废了。节点身份只有节点权限,不是管理员权限。托管集群更简单:调云厂商的 API,给节点池加节点,云厂商的自动化替你跑完加入流程。
新节点一注册(Node 对象进 API Server,kubelet 开始上报心跳),调度器马上把它当成可选节点,空节点在打分上占优,新 Pod 优先往那儿落。但已有 Pod 不自动搬家:SaaS 吃紧的正确操作是加节点加扩副本,手动扩或者配水平扩缩容(HorizontalPodAutoscaler,简称 HPA:按 CPU、内存或自定义指标自动调副本数),新副本调度到新节点上,负载就分流过去了。
全球与边缘
Kubernetes 只管一个集群以内,全球流量调度在它之上。 用户先到全球入口,全球入口选一个集群,然后才是 Kubernetes 的路由。全球层用地理或延迟 DNS 路由、Anycast、全球负载均衡器。两种声明不要混:放置声明(比如节点亲和性说这个服务只跑在欧洲节点上)是 Kubernetes 的事;用户调度声明(比如欧洲用户去欧洲集群)写在全球层(DNS 规则、负载均衡器规则),不在 Kubernetes YAML 里。边缘设备上的 Kubernetes(KubeEdge、k3s 这类轻量发行版)能跑在远端小节点上,但用户流量调度还是外层的事。多集群 Service API 管的是集群之间互相发现服务,不是给最终用户做流量调度的。
平台的平台
Kubernetes 故意做成可编程的:任何软件只要会调 API Server,就能控制 Kubernetes,不用改 Kubernetes 本身。 Operator 是最直接的形态:在集群里跑的控制器,监听自己定义的资源、操作 Kubernetes 对象,比如数据库 Operator 声明一个数据库集群就自动建 StatefulSet、配主从、做备份、故障切换,cert-manager 自动申请、续期 TLS 证书,Argo Rollouts 做金丝雀、蓝绿这些高级发布。第二层是人用的管理工具:kubectl、Dashboard、Lens、k9s,Helm 是给 YAML 打包的包管理器,Argo CD、Flux 是 GitOps 工具,盯着 Git 仓库、仓库里的 YAML 变了就自动同步到集群。第三层是平台层:Rancher、OpenShift、云厂商的托管 Kubernetes,给开发者更简单的界面,底下调 Kubernetes API 干活。
全景补齐
生产还要补几个机制,各一句话。发布旋钮:maxSurge 规定滚动更新最多多建几个,maxUnavailable 规定最多停几个,新版有问题一键回滚。StorageClass:声明要一块盘就自动建,不用管理员预建。自动扩缩三档:水平扩缩容(HorizontalPodAutoscaler,HPA)管副本数,垂直 Pod 扩缩容(Vertical Pod Autoscaler,VPA)管单个 Pod 的 request,Cluster Autoscaler 管节点数。可观测性不是内置的:容器标准输出之外的日志、指标、链路追踪得挂 Prometheus 加 Grafana、Loki 这类外挂。安全补三件:NetworkPolicy 做真正的隔离、Pod 安全上下文、Secret 静态加密(Secret 默认只是 base64 编码,不是加密)。规模再大一档,服务网格(Istio、Linkerd)补灰度、双向 TLS(mTLS)、熔断这些 plain Service 表达不了的东西。
什么时候不用 Kubernetes
Kubernetes 不是默认答案。 服务少、流量小,Docker Compose、Coolify 更划算。升级、运维、排障的复杂度只有规模到了才值。里面也一样:它是一套机制,一个需求对应一个机制,按需组合,不是整套照搬。
一句话收住:每个需求对应一个机制,机制组合起来用。