Kubernetes 简述
版权声明:署名-非商业性使用-相同方式共享
@@ Tags: Kubernetes;k8s
@@ Date: 2025-11-04 1428
@@ Note:
Kubernetes(常简称为 K8s)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。
概念
Kubernetes 要解决的问题
在容器技术(如 Docker)普及之前,应用的部署和运维面临诸多挑战:
- 环境不一致:开发、测试、生产环境差异导致“在我这儿是好的”问题。
- 部署繁琐:手动部署应用,流程复杂且容易出错。
- 资源利用率低:每个物理机或虚拟机通常只运行一个应用,资源浪费严重。
- 扩缩容困难:流量高峰时,手动增加服务器实例速度慢,成本高。
- 故障恢复慢:应用或机器宕机后,需要人工介入恢复,停机时间长。
容器技术(如 Docker)解决了第一个问题,它通过镜像保证了环境的一致性。但后四个问题依然存在:
- 你有成百上千个容器,如何调度它们到合适的机器上?
- 如何保证容器数量始终满足用户需求?
- 容器之间如何发现和通信?
- 如何平滑地更新应用而不中断服务?
Kubernetes 就是为了解决这些容器编排和管理问题而生的。
Kubernetes 的核心架构
Kubernetes 采用经典的主从架构,分为控制平面 和工作节点。
1. 控制平面
控制平面是 Kubernetes 的“大脑”,负责管理整个集群的所有决策。它包含以下几个核心组件:
-
API Server
- 整个系统的唯一入口。所有用户、客户端和其他组件的请求都必须通过 API Server。
- 它提供了
RESTful API,是前端界面(如kubectl)与集群交互的桥梁。
-
etcd
- 一个高可用的键值对数据库。
- Kubernetes 所有集群数据(如节点信息、Pod 状态、配置信息等)都持久化存储在 etcd 中。
- 它是 Kubernetes 的“唯一信源”。
-
Scheduler
调度器。它负责监视新创建的、还未被调度到节点上的 Pod,并根据资源需求、策略等因素,选择一个最合适的 Node 来运行它。 -
Controller Manager
控制器管理器。它运行着各种控制器,这些控制器就像是集群的“自动修复系统”,不断地观察集群的状态,确保其与期望的状态一致。例如- 节点控制器:负责监测节点故障。
- 副本控制器:确保 Pod 的副本数量始终符合用户定义的数量。
2. 工作节点
工作节点是真正运行容器化应用的地方。每个节点上运行着:
-
Kubelet
节点上的“代理”,它负责与控制平面通信,管理本节点上 Pod 的生命周期,例如创建、启动、停止容器,并定期向 API Server 报告节点和 Pod 的状态。 -
Kube Proxy
网络代理,它通过在节点上维护网络规则,实现了 Kubernetes Service 的概念,负责负载均衡和将流量转发到正确的 Pod。 -
容器运行时
负责运行容器的软件,比如 Docker、containerd 或 CRI-O。Kubelet 通过容器运行时接口与它们交互来启动和停止容器。
核心概念与对象
要理解 Kubernetes 如何工作,必须先掌握以下几个核心对象(在 K8s 中,你通过创建这些对象来定义你的应用):
-
Pod:
- Kubernetes 中最小的可部署和管理单元。
- 一个 Pod 包含一个或多个容器(通常是紧密相关的容器,例如应用容器和日志收集 sidecar 容器)。
- 这些容器共享网络命名空间、存储卷和其他资源。
- Pod 是短暂的,会被频繁地创建和销毁。
-
Deployment:
- 用于定义 Pod 的期望状态(例如,需要运行 3 个副本的 Nginx Pod)。
- 它提供了声明式的更新机制。当你更新 Pod 模板时(例如,使用新的镜像版本),Deployment 会以可控的方式(滚动更新)创建新的 Pod,并逐步替换旧的 Pod,实现零停机部署。
- 它是管理无状态应用的首选对象。
-
Service:
- 因为 Pod 是短暂的,它们的 IP 地址会变化,所以不能直接依赖 Pod IP 来访问服务。
- Service 提供了一个稳定的网络端点(一个固定的 IP 地址和 DNS 名称),为一组功能相同的 Pod(通常由 Deployment 管理)提供负载均衡。
- 无论后端的 Pod 如何创建、销毁或迁移,客户端都可以通过 Service 的固定地址访问到它们。
-
Namespace:
- 在物理集群内部创建的虚拟集群,用于实现资源隔离和多租户管理。
- 可以将资源(如 Pod、Service)划分到不同的命名空间中,方便管理和权限控制。
default、kube-system是内置的命名空间。
-
Volume:
- Pod 中的容器是短暂的,其文件系统也是临时的。当容器重启时,数据会丢失。
- Volume 提供了持久化存储的能力,允许容器中的数据在重启后依然存在。Kubernetes 支持多种存储系统(如 NFS、云提供商盘等)。
-
ConfigMap & Secret:
- ConfigMap 用于将非机密的配置数据(如配置文件、环境变量)与容器镜像解耦。
- Secret 用于存储敏感信息(如密码、OAuth 令牌、ssh 密钥),并以更安全的方式传递给 Pod。
使用 .yaml 格式的文件表示 Kubernetes 对象
Kubernetes 对象是持久化的实体。 Kubernetes 使用这些实体去表示整个集群的状态。 具体而言,它们描述了如下信息:
- 哪些容器化应用正在运行(以及在哪些节点上运行)
- 可以被应用使用的资源
- 关于应用运行时行为的策略,比如重启策略、升级策略以及容错策略
几乎每个 Kubernetes 对象包含两个嵌套的对象字段,它们负责管理对象的配置:
- 对象
spec(规约)- 在创建对象时设置的内容,描述你希望对象所具有的特征: 期望状态(Desired State)。
- 对象
status(状态)- 描述了对象的当前状态(Current State),它是由 Kubernetes 系统和组件设置并更新的。
描述 Kubernetes 对象
通常会通过 清单(Manifest) 文件来描述 Kubernetes 对象, 比如对象的基本信息、spec。然后使用 kubectl 创建对象。
该清单文件,有一些必须的字段:
apiVersion- 创建该对象所使用的 Kubernetes API 的版本kind- 想要创建的对象的类别metadata- 帮助唯一标识对象的一些数据,包括一个name字符串、UID和可选的namespacespec- 你所期望的该对象的状态
例如 创建一个 Pod 的清单文件:
# pods/simple-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
可以运行这个命令创建: kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml
再比如下面的清单文件(一个包含 Redis 和多个 ComfyUI Worker 的示例应用):
# comfyui-with-redis.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: comfyui-worker
labels:
app: comfyui-worker
spec:
replicas: 3 # 启动3个Worker副本
selector:
matchLabels:
app: comfyui-worker
template: # 定义Pod模板
metadata:
labels:
app: comfyui-worker
spec:
containers:
- name: worker
image: your-comfyui-image:latest # 请替换为你的镜像
env:
- name: REDIS_URL
value: "redis://redis-service:6379"
- name: QUEUE_NAME
value: "comfyui_tasks"
---
apiVersion: v1
kind: Service
metadata:
name: comfyui-service
spec:
selector:
app: comfyui-worker
ports:
- port: 8188
targetPort: 8188
---
# 部署Redis
apiVersion: apps/v1
kind: Deployment
metadata:
name: redis
spec:
replicas: 1
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7-alpine
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
---
apiVersion: v1
kind: Service
metadata:
name: redis-service
spec:
selector:
app: redis
ports:
- port: 6379
targetPort: 6379
配置清单的字段参考:
API 对象
"Kubernetes 对象" 和 "API 对象" 基本上指的是同一组东西,但它们的侧重点略有不同。
Kubernetes 对象 = API 对象
它们是从不同角度对同一实体的称呼:
- "Kubernetes 对象":强调这些对象在 Kubernetes 系统中的作用和功能
- "API 对象":强调这些对象通过 Kubernetes API 进行创建、读取、更新和删除 的访问方式
YAML >> API >> API 对象
flowchart TD
A[用户编写 YAML 文件] --> B[YAML 文件描述了“期望状态”]
B --> C{kubectl apply 触发 API 调用}
C -- 用户动作 --> D[POST/PUT 请求<br>送达 API Server]
subgraph K [Kubernetes 内部]
D --> E[API Server<br>处理请求]
E --> F[Persistent Storage<br>保存为 API 对象]
F --> G[控制器<br>监听并协调现实状态]
end
G --> H[集群状态<br>与 YAML 描述一致]
- YAML 描述了用户期望的状态, 它内部的字段与 k8s 的 HTTP RESTful API 接口的参数可以看做是一一对应的(在apply 时被转化为JSON HTTP 请求体交给 API Server)。
- API Server 接收到用户请求后,形成一个 API 对象(创建或更新),并保存到持久存储中(etcd)。这个API 对象是资源在 k8s 系统内部持久化后的实例和表现形式,它是集群“期望状态”的真实来源。
相关组件
Ingress
是一个 API 资源对象。它定义了一套规则,用来告诉集群如何将外部的 HTTP/HTTPS 流量路由到内部的服务。你可以把它想象成 Nginx 的 nginx.conf 或者 Apache 的虚拟主机配置,但它是以 k8s 的声明式方式写的。
Ingress 不会随意公开端口或协议。 将 HTTP 和 HTTPS 以外的服务开放到 Internet 时,通常使用 Service.Type=NodePort 或 Service.Type=LoadBalancer 类型的 Service
示例: 所有访问 myapp.example.com 的流量,都会被转发到名为 my-app-service 的集群内部服务的 80 端口。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app-ingress
spec:
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: my-app-service
port:
number: 80
Ingress Controller
是 真正实现 Ingress 规则的软件。它是一个 Pod,负责监听 Kubernetes API 上 Ingress 资源的变化,然后动态地配置和重载一个实际的负载均衡器/反向代理(如 Nginx, Traefik, Envoy 等)来满足这些规则。
Traefik
Traefik 是一个开源的 Ingress Controller,它使用反向代理和负载均衡来满足 Ingress 规则。
Traefik通过与Kubernetes API服务器交互,实时感知Service、Pod或Ingress等资源的变化,并自动更新其内部配置,实现服务的动态路由。这区别于需要手动配置和重载的传统反向代理(如Nginx)。
Traefik https 与 kubernetes-dashbord 问题
当Traefik 尝试使用 HTTPS 连接到后端服务 kubernetes-dashboard:443 时,会返回错误:
$ curl -vk https://dashboard.gitlab.local
...
* issuer: CN=TRAEFIK DEFAULT CERT
* SSL certificate verify result: self signed certificate (18), continuing anyway.
...
* Using Stream ID: 1 (easy handle 0x237a5562140)
> GET / HTTP/2
> Host: dashboard.gitlab.local
> user-agent: curl/7.88.1
> accept: */*
>
* TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
< HTTP/2 500
< content-length: 21
< date: Fri, 28 Nov 2025 03:30:04 GMT
<
Internal Server Error* Connection #0 to host dashboard.gitlab.local left intact
Traefik 容器日志显示:
$ kubectl.exe logs -n kube-system traefik-66655fcf4c-84ccw -f
2025-11-28T03:27:28Z INF Starting provider *acme.ChallengeTLSALPN
2025-11-28T03:30:04Z ERR 500 Internal Server Error error="tls: failed to verify certificate: x509: cannot validate certificate for 10.42.0.131 because it doesn't contain any IP SANs"
这个错误是因为后端服务 (kubernetes-dashboard:443) 的 TLS 证书不包含 IP SAN(Subject Alternative Name),无法验证证书的有效性。
SAN (Subject Alternative Name) 是 TLS 证书中的一个扩展字段,用于指定证书有效的域名或IP地址。
当证书用于 IP 地址而不是域名时,必须在 SAN 字段中包含该 IP 地址, 但 kubernetes-dashboard 服务的 TLS 证书是自签名证书, 没有包含 IP 地址在 SAN 字段中。
解决方案: https://community.traefik.io/t/forwarding-to-https-backend-fails-with-ingress/15887/2
K8s 使用建议
标准的 Kubernetes虽然功能强大,但在某些场景下显得过于“笨重”:
- 资源消耗高
控制平面至少需要 1-2GB 内存,对于边缘设备、IoT 或小型虚拟机来说难以承受。 - 部署复杂
搭建一个 K8s 集群需要配置多个组件,步骤繁琐,容易出错。 - 外部依赖
K8s 默认的容器运行时(如 Docker)和数据存储(etcd)都需要单独部署和维护。
因此在资源紧凑的环境下,推荐使用K3s,它是一个完全兼容 Kubernetes 的轻量级发行版,易于安装,仅需要 Kubernetes 内存的一半,所有组件都在一个小于 100 MB 的二进制文件中。
参考
Minikube
minikube 是本地 Kubernetes,专注于让 Kubernetes 的学习和开发变得容易。
kubectl 命令行工具
kubectl (“Kubernetes Control”) 是使用 Kubernetes API 与 Kubernetes 集群的控制平面进行通信的命令行工具。
kubectl 工作原理
kubectl 本身并不直接管理容器,它只是一个客户端工具。它的工作流程是:
- 读取你的
kubeconfig配置文件(通常在~/.kube/config),获取要访问的集群地址和认证信息。 - 将你的命令转换为一个 API 请求。
- 将这个请求发送给 Kubernetes API Server。
- API Server 接收到请求后,会根据请求的类型与集群的当前状态进行处理,并更新
etcd数据库。 - 其他的 Kubernetes 控制平面组件(如 Scheduler, Controller Manager)会监视这些变化,并采取行动来使集群的实际状态与期望状态一致(例如,在节点上创建 Pod)。
单位
$ kubectl.exe top pod
NAME CPU(cores) MEMORY(bytes)
echo-app-74f6cd4946-6qn8m 1m 19Mi
echo-app-74f6cd4946-h7ndx 1m 19Mi
echo-app-74f6cd4946-hwljm 1m 19Mi
echo-app-74f6cd4946-s5hxr 1m 19Mi
1m = 1 millicore(毫核)也就是 0.001 CPU 核心。
Kubernetes 当中:
- 1000m = 1 CPU 核(也就是宿主机的一个逻辑核心)
- 500m = 0.5 核心
- 1m = 0.001 核心
19Mi = 19 Mebibytes(MiB)= 19 × 1024 × 1024 bytes
在计算机领域,“MB” 其实既可能是 1000×1000,也可能是 1024×1024,但严格定义上它应该是十进制(1000),而二进制的“1024”应该写成 MiB。
1998 年之后,国际标准组织(IEC)正式规定:
| 名称 | 缩写 | 进制 | 字节 |
|---|---|---|---|
| KB | Kilobyte | 10 进制 | 1 KB = 1,000 bytes |
| MB | Megabyte | 10 进制 | 1 MB = 1,000,000 bytes |
| KiB | Kibibyte | 2 进制 | 1 KiB = 1,024 bytes |
| MiB | Mebibyte | 2 进制 | 1 MiB = 1,024×1,024 = 1,048,576 bytes |
Comments ()