Kubernetes 入门:掀开云原生时代的神秘面纱
在当今飞速发展的数字化浪潮中,企业正面临着如何高效、弹性、可靠地部署、扩展和管理复杂应用程序的挑战。从传统的单体应用到微服务架构的演进,从物理机到虚拟机再到容器化的普及,应用部署和管理的方式发生了天翻地覆的变化。在这个背景下,一个名字变得越来越响亮,它被誉为云原生时代的操作系统——Kubernetes。
对于许多初入这个领域的开发者、运维工程师乃至技术管理者来说,Kubernetes 似乎是一个既强大又复杂的巨兽。它拥有庞大的生态系统、众多的概念和命令行工具,让人望而却步。然而,一旦掌握了它的核心思想和基础概念,你就会发现它为现代应用管理带来的巨大便利和威力。
本文旨在为 Kubernetes 的初学者提供一个全面而详细的入门指南。我们将从理解它解决的问题出发,逐步深入其核心概念,帮助你构建对 Kubernetes 的整体认知框架。
1. 为什么需要 Kubernetes?它解决了什么问题?
在深入 Kubernetes 之前,我们首先需要理解它出现的背景和意义。回顾一下没有容器编排工具的日子:
- 单体应用时代: 应用部署相对简单,但扩展困难,任何部分出现问题都可能导致整个应用宕机。更新发布是高风险操作。
- 虚拟机时代: 虚拟机提供了更好的隔离和资源利用率,但部署和管理虚拟机本身依然繁重,启动慢,资源开销大。应用依然需要打包、配置、依赖管理等步骤。
- 容器化时代 (如 Docker): 容器彻底革新了应用的打包和部署方式。它将应用及其所有依赖(库、配置文件等)打包到一个轻量级、可移植的镜像中,确保应用在任何环境中都能一致运行。这极大地解决了“在我机器上可以运行”的问题。
容器化是应用交付的巨大进步,但随着应用规模扩大、微服务数量增多,问题随之而来:
- 如何部署大量容器? 手动在几十几百台服务器上启动、停止、配置容器是不可行的。
- 如何确保容器持续运行? 如果一个容器崩溃了,谁来重启它?如果所在的服务器故障了,谁来转移容器?
- 如何扩展应用? 当流量增加时,如何快速增加容器副本?流量减少时如何缩减?
- 如何管理容器间的通信? 不同的微服务容器如何互相发现和访问?
- 如何进行版本更新和回滚? 如何实现不停机更新(滚动更新)?更新出问题了如何快速回滚到旧版本?
- 如何管理存储和配置? 应用数据如何持久化?如何分发配置文件和敏感信息(如数据库密码)?
- 如何实现负载均衡? 如何将外部请求分发到多个容器副本上?
这些问题指向了一个核心需求:我们需要一个自动化平台,能够编排 (Orchestrate)、管理 (Manage)、扩展 (Scale) 和自动化部署 (Automate Deployment) 容器化应用。
这就是 Kubernetes 诞生的原因。它不是一个 PaaS (Platform as a Service),而是一个容器编排和管理的开源平台。它源自 Google 内部使用了十多年的容器管理系统 Borg,于 2014 年开源,并迅速成为容器编排领域的标准。
简单来说,Kubernetes 解决的核心问题是:自动化容器化应用的部署、扩展和管理。它将繁琐的手动操作变成了声明式配置和自动化流程。
2. Kubernetes 的核心思想:声明式配置与控制回路
理解 Kubernetes 的强大之处,首先要理解其核心思想:声明式配置 (Declarative Configuration) 和 控制回路 (Control Loop)。
与传统的命令式 (Imperative) 方式不同(例如,启动容器X
,如果容器Y停止则重启
,当CPU超过80%时启动容器Z
),Kubernetes 采用声明式方式。你不需要告诉 Kubernetes 如何做,你只需要告诉它你想要什么状态 (Desired State)。
例如,你不是告诉 Kubernetes “启动 3 个 Nginx 容器,如果某个容器死了就重启”,而是通过一个 YAML 文件告诉 Kubernetes:”我希望有一个名为 nginx-deployment
的部署,它应该始终运行 3 个 Nginx 容器的副本,使用 nginx:latest
镜像”。
Kubernetes 内部有一系列的控制回路 (Control Loops) 在不停地工作。每个控制回路都负责监控集群的当前状态 (Current State),并将其与用户定义的期望状态 (Desired State) 进行比较。如果发现当前状态与期望状态不符,控制回路就会采取行动,使当前状态逐渐逼近期望状态。
例如,ReplicaSet 控制器(我们后面会详细介绍)会持续检查当前正在运行的 Pod(Kubernetes 中最小的部署单元)数量。如果你声明了需要 3 个 Nginx Pod,但它只发现了 2 个,它就会自动启动一个新的 Pod。如果发现了 4 个,它就会终止一个。这个过程是自动化的、持续进行的,这就是 Kubernetes 实现自我修复和自动扩展的基础。
这种声明式 API 和控制回路的设计,使得 Kubernetes 具有强大的自愈能力 (Self-healing)、自动化能力 (Automation) 和弹性伸缩能力 (Elastic Scaling)。
3. Kubernetes 基础概念:构建你的知识地图
现在,让我们来详细剖析 Kubernetes 中那些最基础、最重要的概念。理解了它们,你就掌握了 Kubernetes 的基石。
3.1 Cluster (集群)
Kubernetes 的核心是一个集群 (Cluster)。一个集群由一组协同工作的机器组成,这些机器负责运行你的容器化应用。一个标准的 Kubernetes 集群主要包含两种类型的节点:
- Control Plane (控制平面 / Master 节点): 负责管理整个集群,维护集群的状态,并响应用户的操作。它不直接运行用户应用。
- Worker Nodes (工作节点 / Node): 负责运行实际的应用容器。控制平面将工作负载分配给工作节点。
Control Plane (控制平面) 的关键组件:
- API Server (kube-apiserver): 这是 Kubernetes 控制平面的前端。它是所有其他组件和外部用户(通过
kubectl
命令)进行交互的入口。所有对集群状态的修改和查询都通过 API Server 进行。它负责身份认证、授权、验证,并将状态存储到 etcd 中。 - etcd: 一个高可用、强一致性的键值存储系统。Kubernetes 使用 etcd 来存储集群的所有状态数据,包括 Pod、Service、Deployment 等对象的定义和当前状态。它是集群的大脑和真相的来源。
- Scheduler (kube-scheduler): 负责监听新创建的、但尚未分配到节点上的 Pod。它会根据 Pod 的资源需求、节点资源、节点亲和性/反亲和性、污点/容忍度等一系列规则,选择一个最合适的工作节点来运行这个 Pod。
- Controller Manager (kube-controller-manager): 运行着多个控制回路(Controllers),每个控制器负责一种资源类型。例如:
- Node Controller: 负责监控节点的状态,发现节点故障时及时处理。
- ReplicaSet Controller: 负责维护指定数量的 Pod 副本。
- Deployment Controller: 负责更高层次的 Deployment 对象,管理 ReplicaSet,实现滚动更新、回滚等。
- Service Account & Token Controllers: 负责管理 Service Accounts 和 API 访问令牌。
- Cloud Controller Manager (kube-cloud-controller-manager): (可选) 这个组件与特定的云提供商(如 AWS, GCP, Azure)集成,管理云平台特有的资源,例如负载均衡器、持久化存储卷、节点路由等。如果你在本地搭建 Kubernetes,可能不会有这个组件。
Worker Nodes (工作节点) 的关键组件:
- Kubelet: 运行在每个工作节点上的代理。它与控制平面通信,接收 API Server 下发的指令(例如,启动或停止一个 Pod),并确保 Pod 中描述的容器在节点上运行,并向控制平面报告节点和 Pod 的状态。
- Kube-proxy: 运行在每个工作节点上的网络代理。它维护节点上的网络规则,负责实现 Service 的抽象,将 Service 的请求转发到正确的 Pod 上。它通常通过 iptables 或 IPVS 来实现服务发现和负载均衡。
- Container Runtime: 负责运行容器的软件。Kubernetes 支持多种容器运行时,如 Docker、containerd、CRI-O 等。Kubelet 通过 CRI (Container Runtime Interface) 接口与容器运行时交互。
3.2 Pod
Pod 是 Kubernetes 中最小的可部署计算单元。它不是直接部署容器,而是部署 Pod。一个 Pod 封装了:
- 一个或多个容器: 这些容器共享 Pod 的网络命名空间、IPC 命名空间、主机名以及存储卷。
- 存储资源 (Volumes): Pod 可以挂载一个或多个存储卷,供 Pod 内的容器共享或访问持久化数据。
- 唯一的网络 IP: Pod 在集群内部拥有一个唯一的 IP 地址,Pod 内的所有容器共享这个 IP 和端口空间。
- 有关如何运行容器的信息: 如启动命令、端口、环境变量等。
为什么需要 Pod?
一个 Pod 通常代表一个应用程序的实例。虽然大多数 Pod 只包含一个容器(常见情况),但对于那些紧密耦合、需要共享资源(如共享卷、共享网络)的辅助容器(如日志收集器、服务网格 Sidecar 代理等),可以将它们打包在同一个 Pod 中。 Pod 提供了一个比单个容器更高层次的抽象,简化了复杂应用的部署和管理。Pod 是临时的,如果 Pod 所在的节点故障或 Pod 被驱逐,它会被销毁,并由控制器在其他地方重新创建。
3.3 Controllers (控制器)
如前所述,控制器是 Kubernetes 实现声明式模型和自愈能力的关键。它们监视特定类型的资源,并采取行动使其达到期望状态。以下是一些最重要的控制器:
- ReplicaSet (RS): 负责确保在任何时候都有指定数量的 Pod 副本正在运行。如果 Pod 数量低于期望值,它会创建新 Pod;如果高于期望值,它会终止多余的 Pod。ReplicaSet 主要用于维护 Pod 的高可用性。通常不直接创建 ReplicaSet,而是通过 Deployment 来间接管理 ReplicaSet。
- Deployment (部署): 这是管理无状态应用 (Stateless Applications) 的首选方式。Deployment 是一个更高级别的控制器,它管理 ReplicaSet。通过 Deployment,你可以声明你想要运行的 Pod 数量、使用的容器镜像、更新策略(如滚动更新)、回滚策略等。Deployment 控制器会根据你的声明创建和更新 ReplicaSet,而 ReplicaSet 则负责维持 Pod 的数量。Deployment 极大地简化了应用的升级、回滚和扩缩容。
- StatefulSet (有状态集): 用于管理有状态应用 (Stateful Applications),如数据库、消息队列等。与 Deployment 管理的无状态 Pod 不同,StatefulSet 为其管理的 Pod 提供:
- 稳定的网络标识 (Stable Network Identity): Pod 拥有固定的、可预测的主机名。
- 稳定的持久化存储 (Stable Persistent Storage): Pod 可以关联特定的持久化存储卷,即使 Pod 被重新调度到其他节点,也能访问相同的数据。
- 有序的部署和伸缩 (Ordered Deployment and Scaling): Pod 按照有序的索引创建和删除(例如
web-0
,web-1
,web-2
)。
StatefulSet 适用于需要维护状态、具有固定标识和需要有序操作的应用。
- DaemonSet (守护进程集): 确保一个 Pod 的副本在所有(或指定的一部分)工作节点上运行。DaemonSet 通常用于部署集群级别的日志收集代理 (如 Fluentd, Logstash)、监控代理 (如 Prometheus Node Exporter)、集群存储守护进程等。每当有新节点加入集群,DaemonSet 控制器会自动在该节点上启动相应的 Pod。节点被移除时,Pod 会被垃圾回收。
- Job (作业): 运行一个 Pod(或一组 Pod)来执行一个一次性任务 (One-off Task) 并将其运行至完成。Job 会确保指定数量的 Pod 成功完成其任务。例如,运行一个批处理脚本、一个数据迁移任务等。
- CronJob (定时作业): 基于时间调度 Job。它类似于 Linux 系统中的 Cron 任务,允许你按照预定的时间表创建 Job。例如,每天凌晨运行一次数据备份 Job。
3.4 Service (服务)
Pod 是临时的,它们的 IP 地址会在被创建或重新调度时改变。如果你直接访问 Pod 的 IP 地址,那么当 Pod 发生变化时,连接就会中断。为了解决这个问题,Kubernetes 引入了 Service (服务) 的概念。
Service 是一组 Pod 的抽象,它为这组 Pod 提供了一个稳定的网络访问方式(通常是一个固定的 IP 地址和端口),而不管后端 Pod 的生命周期如何变化。Service 通过 标签选择器 (Label Selector) 来关联它所服务的 Pod。无论 Pod 如何创建、删除或替换,只要它们的标签与 Service 的选择器匹配,它们就会被纳入到 Service 的后端 Endpoint 列表中。
Kubernetes 支持几种不同类型的 Service:
- ClusterIP (默认类型): 为 Service 分配一个仅在集群内部可访问的 IP 地址。这是最常见的 Service 类型,用于集群内部的服务间通信。
- NodePort: 在每个工作节点的 IP 上开放一个静态端口 (NodePort),所有发往该 NodePort 的请求都会被转发到 Service 的 ClusterIP,进而转发到后端的 Pod。这允许从集群外部通过
NodeIP:NodePort
访问 Service。通常用于开发测试或作为其他负载均衡器(如云提供商的 LoadBalancer)的后端。 - LoadBalancer: (通常在云环境中) 在云提供商(如 AWS, GCP, Azure)中创建一个外部负载均衡器。这个负载均衡器会有一个外部 IP 地址,并将流量转发到 Service 的 NodePort 上,进而到达 Pod。这是将 Service 暴露到互联网的标准方式。
- ExternalName: 将 Service 映射到外部 DNS 名称,而不是内部 ClusterIP。它通过返回 CNAME 记录来实现,不涉及代理。
3.5 Volume (存储卷)
容器的文件系统是临时的。当 Pod 终止时,容器文件系统中的数据也会丢失。为了解决应用数据的持久化问题,Kubernetes 引入了 Volume (存储卷) 的概念。
Volume 提供了 Pod 中容器可以访问的持久化存储。Volume 的生命周期与 Pod 相关联(但某些类型的 Volume 的数据生命周期可以独立于 Pod)。Volume 可以在 Pod 内的多个容器之间共享。
Kubernetes 支持许多不同类型的 Volume,包括:
- emptyDir: 当 Pod 被分配给一个节点时,会创建这样一个卷,其生命周期与 Pod 相同。Pod 中的所有容器都可以读写其中的数据。当 Pod 被删除时,emptyDir 中的数据也会被删除。通常用于临时存储、共享文件等。
- hostPath: 将节点文件系统中的一个文件或目录挂载到 Pod 中。这使得 Pod 可以访问节点上的文件。使用时需谨慎,因为它打破了 Pod 的可移植性(Pod 依赖于特定的节点路径),并且有安全风险。
- 各种云提供商特定的存储类型: 如 AWS EBS, GCE Persistent Disk, Azure Disk 等,这些存储类型提供了高可靠和持久的数据存储。
- NFS, iSCSI 等网络存储类型。
- PersistentVolume (PV) 和 PersistentVolumeClaim (PVC): 这是 Kubernetes 中管理持久化存储的标准和推荐方式。
- PersistentVolume (PV): 集群中的一块网络存储资源,由集群管理员 provision(预置)或由 StorageClass 动态 provision。它独立于 Pod 的生命周期存在。PV 包含了存储的类型、大小、访问模式(读写单副本、读写多副本、只读多副本)等信息。
- PersistentVolumeClaim (PVC): 是用户对存储资源的请求。用户声明所需的存储大小、访问模式等,而不需要关心底层的存储实现细节。PVC 会绑定到一个匹配需求的 PV。
Pod 通过引用 PVC 来使用存储。这种解耦使得应用开发者可以只关心存储的需求(通过 PVC),而集群管理员可以负责提供不同类型和性能的存储(通过 PV)。
3.6 Namespace (命名空间)
Namespace (命名空间) 提供了在同一个 Kubernetes 集群中划分资源的一种机制。它们用于将集群资源(如 Pod, Service, Deployment 等)划分为相互隔离的逻辑组。
命名空间的作用:
- 资源隔离: 不同的命名空间中的资源名称可以重复,它们是相互独立的。例如,你可以在
dev
命名空间和prod
命名空间中都有一个名为my-app
的 Deployment。 - 访问控制: 可以为不同的用户或团队设置权限,限制他们只能访问或管理特定命名空间下的资源。
- 资源配额 (Resource Quotas): 可以为命名空间设置资源限制,例如 CPU、内存、存储、Pod 数量等,防止某个命名空间消耗过多集群资源。
默认情况下,Kubernetes 集群启动时会创建几个命名空间:
* default
: 用户在没有指定命名空间时使用的默认命名空间。
* kube-system
: 用于 Kubernetes 系统组件(如 API Server, Scheduler 等)运行的命名空间。通常不要在这个命名空间中部署自己的应用。
* kube-public
: 用于存放集群中所有用户都可以读取的对象,例如集群信息的 ConfigMap。
* kube-node-lease
: 用于存放与节点健康状况相关的 Lease 对象。
最佳实践是为不同的环境(开发、测试、生产)、不同的团队或不同的应用创建独立的命名空间。
3.7 ConfigMap 和 Secret
在容器化应用中,将配置信息和敏感数据与应用镜像分离是一种良好的实践。Kubernetes 提供了两种资源来处理这个问题:
- ConfigMap: 用于存储非敏感的配置数据,如环境变量、命令行参数、配置文件等。ConfigMap 可以通过环境变量、命令行参数或作为文件挂载到 Pod 中供容器使用。
- Secret: 用于存储敏感数据,如密码、令牌、密钥等。Secret 的使用方式与 ConfigMap 类似,可以通过环境变量或文件挂载到 Pod 中。Kubernetes 提供了一些机制来保护 Secret 数据(如 Etcd 加密),但默认情况下,Secret 存储在 Etcd 中是未加密的,需要额外的安全措施。
将配置和敏感数据外部化,使得应用镜像更加通用,便于在不同环境中部署,提高了安全性和灵活性。
3.8 Ingress
前面提到的 Service 类型 LoadBalancer 可以将 Service 暴露到外部网络,但它通常只能处理一层网络协议(如 TCP 或 HTTP),并且为每个需要暴露的服务创建一个 LoadBalancer 成本较高。对于 HTTP/S 流量,我们通常需要更灵活的路由规则,例如根据请求的主机名 (Host) 或路径 (Path) 将请求路由到不同的后端 Service。
Ingress 就是用于管理集群中外部访问 Service 的规则集合。它不是 Service 的一种类型,而是一个独立的 API 对象。Ingress 定义了 HTTP/S 流量如何到达集群内部 Service 的规则。
Ingress 需要一个 Ingress Controller 来实际实现这些规则。Ingress Controller 是一个在集群中运行的 Pod,它监听 Ingress 资源的变化,并根据规则配置底层的负载均衡器(如 Nginx, HAProxy, Traefik 或云提供商的 HTTP(S) 负载均衡器)。
通过 Ingress,你可以实现:
- 基于域名的路由 (Name-based virtual hosting)
- 基于 URL 路径的路由 (Path-based routing)
- SSL/TLS 终止
- 将多个 Service 通过一个外部 IP 地址暴露出去
Ingress 提供了一种集中管理外部访问的机制,比为每个 Service 创建 LoadBalancer 更经济和灵活。
4. Kubernetes 的工作流程概览
理解了这些基本概念后,我们简单梳理一下一个应用部署到 Kubernetes 的大致流程:
- 用户定义期望状态: 开发者或运维人员编写 YAML 文件,声明他们希望集群达到的状态。例如,一个 Deployment 对象,描述了想要运行的应用(容器镜像)、副本数量、更新策略、Service 对象(如何访问这些 Pod)、PersistentVolumeClaim 对象(如果需要持久化存储)等。
- 通过 Kubectl 提交到 API Server: 用户使用
kubectl apply -f your-app.yaml
命令,将这些声明提交给 Kubernetes API Server。 - API Server 接收并验证: API Server 接收到请求后,进行身份认证、授权和数据格式验证,然后将这些对象的定义存储到 etcd 中。此时,集群的“期望状态”就被更新了。
- 控制器感知变化: 相关的控制器(如 Deployment Controller)会通过 API Server 感知到新的 Deployment 对象被创建。Deployment Controller 会根据 Deployment 的定义,创建或更新一个 ReplicaSet 对象,并将其状态也存储到 etcd。
- ReplicaSet Controller 创建 Pod: ReplicaSet Controller 感知到新的或更新的 ReplicaSet,发现当前运行的 Pod 数量不符合期望(例如,期望 3 个但当前为 0),它会通过 API Server 创建多个 Pod 对象,并将其状态存储到 etcd。
- Scheduler 分配 Pod: Scheduler 监听 API Server,发现有新的、尚未分配节点 (Node) 的 Pod。它根据预定的调度算法和策略,为每个 Pod 选择一个合适的工作节点,并将 Pod 与该节点进行绑定(更新 Pod 对象的状态到 etcd)。
- Kubelet 启动 Pod: 被选中的工作节点上的 Kubelet 通过 API Server 了解到有一个 Pod 被分配给了自己。Kubelet 随后与节点上的容器运行时通信(如 containerd 或 Docker),拉取 Pod 中定义的容器镜像,配置 Volume,并启动容器。
- Kubelet 报告状态: Kubelet 持续监控其节点上 Pod 的运行状态,并将状态更新报告给 API Server,最终存储在 etcd 中。
- Service & Kube-proxy: 如果定义了 Service,Kube-proxy 会根据 Service 的标签选择器,找到匹配的 Pod 的 IP 地址列表,并在节点的网络规则中配置转发规则,使得对 Service IP 的访问能够被正确地负载均衡到后端的 Pod。
- 控制回路持续运行: 所有的控制器都在持续监控实际状态与期望状态是否一致。如果一个 Pod 崩溃了,ReplicaSet Controller 会发现 Pod 数量减少,并创建新的 Pod。如果节点故障了,Node Controller 会标记节点为 Unhealthy,Scheduler 会将运行在该节点上的 Pod 重新调度到其他健康的节点。
这个循环往复、自动调整的过程,就是 Kubernetes 实现自动化运维、自愈和弹性的秘密所在。
5. 如何开始学习 Kubernetes?
对于新手来说,最好的入门方式是动手实践。
- 安装 kubectl: 这是与 Kubernetes 集群交互的命令行工具。
- 搭建一个本地单节点集群: 有很多轻量级的工具可以选择,例如:
- Minikube: 专门为本地开发和学习设计的单节点 Kubernetes 集群工具。
- Kind (Kubernetes in Docker): 使用 Docker 容器作为节点来运行本地 Kubernetes 集群。
- K3s: 轻量级的 Kubernetes 发行版,适合边缘计算和资源受限环境,也可以用于本地学习。
选择其中一个,按照官方文档进行安装。
- 学习 Kubectl 基本命令: 学习如何使用
kubectl get
,kubectl describe
,kubectl apply
,kubectl delete
,kubectl logs
,kubectl exec
等命令来查看和操作集群资源。 - 动手实践核心概念: 编写简单的 YAML 文件,创建 Pod, Deployment, Service, Volume 等资源,观察它们在集群中的行为。尝试滚动更新一个 Deployment,删除一个 Pod 看它如何被 ReplicaSet 自动重建。
- 阅读官方文档: Kubernetes 官方文档非常详尽,是学习的最佳资源。从“概念”部分入手,深入了解每个资源的详细信息。
这是一个循序渐进的过程。不要试图一次性掌握所有东西。从核心概念开始,通过实践加深理解,逐步探索更高级的特性。
6. 总结
Kubernetes 是一个复杂但极其强大的容器编排平台。它通过声明式 API 和持续运行的控制回路,彻底改变了我们构建、部署和管理分布式应用的方式。它提供的 Pod、Controller (Deployment, StatefulSet 等)、Service、Volume、Namespace、ConfigMap/Secret、Ingress 等核心概念,共同构成了其强大的功能基础。
理解 Kubernetes 的核心价值在于自动化、弹性、自愈和标准化。虽然入门曲线可能有点陡峭,但投入时间和精力去学习它,对于希望在云原生时代构建和管理现代化应用的技术人员来说,绝对是值得的。
希望本文为你掀开了 Kubernetes 的神秘面纱,为你探索这个充满活力的生态系统提供了坚实的基础和清晰的路径。祝你在 Kubernetes 的学习旅程中一切顺利!