Kubernetes 简述

版权声明:署名-非商业性使用-相同方式共享

@@ Tags: Kubernetes;k8s
@@ Date: 2025-11-04 1428
@@ Note:

Kubernetes(常简称为 K8s)是一个开源的容器编排平台,用于自动化部署、扩展和管理容器化应用程序。

概念

Kubernetes 要解决的问题

在容器技术(如 Docker)普及之前,应用的部署和运维面临诸多挑战:

  1. 环境不一致:开发、测试、生产环境差异导致“在我这儿是好的”问题。
  2. 部署繁琐:手动部署应用,流程复杂且容易出错。
  3. 资源利用率低:每个物理机或虚拟机通常只运行一个应用,资源浪费严重。
  4. 扩缩容困难:流量高峰时,手动增加服务器实例速度慢,成本高。
  5. 故障恢复慢:应用或机器宕机后,需要人工介入恢复,停机时间长。

容器技术(如 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 中,你通过创建这些对象来定义你的应用):

  1. Pod:

    • Kubernetes 中最小的可部署和管理单元
    • 一个 Pod 包含一个或多个容器(通常是紧密相关的容器,例如应用容器和日志收集 sidecar 容器)。
    • 这些容器共享网络命名空间、存储卷和其他资源。
    • Pod 是短暂的,会被频繁地创建和销毁。
  2. Deployment

    • 用于定义 Pod 的期望状态(例如,需要运行 3 个副本的 Nginx Pod)。
    • 它提供了声明式的更新机制。当你更新 Pod 模板时(例如,使用新的镜像版本),Deployment 会以可控的方式(滚动更新)创建新的 Pod,并逐步替换旧的 Pod,实现零停机部署。
    • 它是管理无状态应用的首选对象。
  3. Service

    • 因为 Pod 是短暂的,它们的 IP 地址会变化,所以不能直接依赖 Pod IP 来访问服务。
    • Service 提供了一个稳定的网络端点(一个固定的 IP 地址和 DNS 名称),为一组功能相同的 Pod(通常由 Deployment 管理)提供负载均衡。
    • 无论后端的 Pod 如何创建、销毁或迁移,客户端都可以通过 Service 的固定地址访问到它们。
  4. Namespace

    • 在物理集群内部创建的虚拟集群,用于实现资源隔离和多租户管理。
    • 可以将资源(如 Pod、Service)划分到不同的命名空间中,方便管理和权限控制。defaultkube-system 是内置的命名空间。
  5. Volume

    • Pod 中的容器是短暂的,其文件系统也是临时的。当容器重启时,数据会丢失。
    • Volume 提供了持久化存储的能力,允许容器中的数据在重启后依然存在。Kubernetes 支持多种存储系统(如 NFS、云提供商盘等)。
  6. 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 和可选的 namespace
  • spec - 你所期望的该对象的状态

例如 创建一个 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.svg

Ingress 不会随意公开端口或协议。 将 HTTP 和 HTTPS 以外的服务开放到 Internet 时,通常使用 Service.Type=NodePortService.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 本身并不直接管理容器,它只是一个客户端工具。它的工作流程是:

  1. 读取你的 kubeconfig 配置文件(通常在 ~/.kube/config),获取要访问的集群地址和认证信息。
  2. 将你的命令转换为一个 API 请求。
  3. 将这个请求发送给 Kubernetes API Server。
  4. API Server 接收到请求后,会根据请求的类型与集群的当前状态进行处理,并更新 etcd 数据库。
  5. 其他的 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