跳到主要内容

46 篇博文 含有标签「深度解析」

深度技术文章

查看所有标签

Kubernetes 全景解析 (1):工作负载与 Pod 生命周期深度解析

· 阅读需 21 分钟
Rainy
雨落无声,代码成诗 —— 致力于技术与艺术的极致平衡

"Pods are the atomic unit of scheduling in Kubernetes — not containers."

在 Kubernetes 的世界里,工作负载 (Workload) 是你与应用交互的核心抽象。无论你是部署一个无状态的 Web 服务、一个有状态的数据库,还是在每个节点上运行监控 Agent,K8s 都提供了专门的工作负载资源来满足需求。而所有这些工作负载的基础,都建立在 Pod 之上。

本文将从 Pod 的本质出发,逐层深入解析 Kubernetes 中五大核心工作负载资源的设计理念、编排策略与最佳实践,帮助你在架构选型时做出正确的决策。


一、Pod:K8s 的原子调度单元

1.1 为什么 Pod 是最小部署单元而非容器

许多初学者会困惑:既然 Docker 已经有了容器概念,为什么 Kubernetes 还要引入 Pod?答案在于 "超亲密容器"(Hyper-privileged Containers) 的设计哲学。

在现实世界中,一个应用往往不是孤立运行的。例如:

  • 一个 Web 服务器需要配合一个日志采集 Sidecar
  • 一个主进程需要配合一个健康检查辅助进程
  • 一个数据管道需要同时运行 ingest 和 transform 两个紧密协作的进程

这些进程需要共享网络命名空间(可以通过 localhost 互相通信)、共享存储卷(可以读写同一份数据),并且需要作为一个原子单元被调度到同一个节点上。Pod 正是为了解决这一需求而诞生的。

核心原则

Pod 是 Kubernetes 中最小的可调度单元。一个 Pod 可以包含一个或多个容器,这些容器共享相同的网络和存储命名空间,始终被调度到同一个节点上,并作为一个整体进行生命周期管理。

1.2 Pod 的设计理念

Pod 的设计围绕三个核心能力展开:

能力说明典型场景
共享网络同一 Pod 内的所有容器共享同一个 IP 地址和端口空间,可以通过 localhost 互相访问主容器 + Sidecar 代理
共享存储Pod 可以声明多个 Volume,这些 Volume 可以被 Pod 内的任意容器挂载主容器写日志,Sidecar 读取并转发
原子调度Pod 内的所有容器作为一个整体被调度到同一个节点保证本地通信的低延迟

Sidecar 模式 是 Pod 多容器设计中最经典的模式。例如,Istio 服务网格通过在每个 Pod 中注入一个 Envoy Sidecar 代理来实现流量管理、安全通信和可观测性,而无需修改应用代码。

1.3 Pod 的 YAML 结构详解

下面是一个完整的 Pod YAML 示例,每个字段都附有详细注释:

apiVersion: v1 # API 版本,Pod 属于核心 v1 组
kind: Pod # 资源类型
metadata:
name: nginx-pod # Pod 名称,在同一个 Namespace 内必须唯一
namespace: default # 命名空间,不指定则使用 default
labels: # 标签,用于选择器和组织资源
app: nginx
tier: frontend
annotations: # 注解,用于存储非标识性的元数据
description: "A sample nginx pod"
spec:
# --- 重启策略 ---
restartPolicy: Always # 容器退出后的重启策略:Always / OnFailure / Never

# --- 节点选择 ---
nodeSelector: # 通过标签选择节点
disktype: ssd
tolerations: # 容忍度,用于调度到有特定 Taint 的节点
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"

# --- 容器定义 ---
containers:
- name: nginx # 容器名称
image: nginx:1.27 # 镜像地址(含标签)
imagePullPolicy: IfNotPresent # 镜像拉取策略:Always / IfNotPresent / Never
ports:
- containerPort: 80 # 容器暴露的端口(仅声明,不自动发布)
protocol: TCP
resources: # 资源请求与限制
requests: # 调度时保证的最小资源量
cpu: "100m" # 100 millicores = 0.1 核
memory: "128Mi"
limits: # 容器可使用的最大资源量
cpu: "500m"
memory: "256Mi"
env: # 环境变量
- name: ENVIRONMENT
value: "production"
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: db-secret
key: password
volumeMounts: # 挂载存储卷
- name: nginx-data
mountPath: /usr/share/nginx/html
livenessProbe: # 存活探针
httpGet:
path: /healthz
port: 80
initialDelaySeconds: 15
periodSeconds: 10
readinessProbe: # 就绪探针
httpGet:
path: /ready
port: 80
initialDelaySeconds: 5
periodSeconds: 5
startupProbe: # 启动探针(K8s 1.18+)
httpGet:
path: /startup
port: 80
failureThreshold: 30 # 最多失败 30 次(即最多等待 300 秒)

# --- Init 容器 ---
initContainers:
- name: init-db
image: busybox:1.36
command: ['sh', '-c', 'until nslookup db-service; do echo waiting for db; sleep 2; done']

# --- 存储卷 ---
volumes:
- name: nginx-data
persistentVolumeClaim:
claimName: nginx-pvc # 引用 PVC
- name: config-volume
configMap:
name: nginx-config # 引用 ConfigMap

# --- DNS 配置 ---
dnsPolicy: ClusterFirst # DNS 策略:ClusterFirst / Default / ClusterFirstWithHostNet / None
最佳实践
  1. 始终设置 resources.requestsresources.limits,避免资源争抢导致节点不稳定。
  2. 使用 imagePullPolicy: IfNotPresent 而非 Always(除非使用 :latest 标签),以减少镜像拉取延迟。
  3. 为生产环境的镜像使用明确的 digest(如 nginx@sha256:abc123...),确保部署的可重复性。

1.4 Pod 生命周期

Pod 从创建到终止会经历一系列状态变化。理解这些状态对于排查问题至关重要。

各状态说明:

状态含义
PendingPod 已被 Kubernetes 集群接受,但一个或多个容器尚未创建并就绪。包括等待调度和下载镜像的时间。
RunningPod 已绑定到节点,所有容器已创建,至少一个容器仍在运行,或正在启动/重启中。
SucceededPod 中的所有容器已成功终止,且不会重启。
FailedPod 中的所有容器已终止,且至少一个容器以失败状态退出(非零退出码或被系统终止)。
Unknown由于某种原因无法获取 Pod 状态,通常是与节点通信失败。
CrashLoopBackOff 和 Terminating 不是 Pod Phase

CrashLoopBackOffTerminating 可能会出现在 kubectl 命令的 Status 输出中,但它们不是 Pod 的 phase 值。Pod phase 是 Kubernetes 数据模型中的显式字段,只有五个值:PendingRunningSucceededFailedUnknown

  • CrashLoopBackOff:容器反复崩溃退出,K8s 在每次重启之间增加指数退避等待时间(10s → 20s → 40s → ...,上限 300s)。
  • Terminating:Pod 正在被删除,处于优雅终止过程中(默认 30 秒宽限期)。

参考文档:Pod Lifecycle - Pod phase

1.5 Probe 机制:Liveness / Readiness / Startup

Kubernetes 通过三种探针来监控容器的健康状态:

探针类型作用失败后果
Liveness Probe检测容器是否活着重启容器
Readiness Probe检测容器是否就绪(能否接收流量)从 Service Endpoints 中移除
Startup Probe检测容器是否启动完成在启动期间禁用其他探针

探针支持四种检测方式:

  • httpGet:向容器发送 HTTP GET 请求,2xx/3xx 状态码视为成功。
  • tcpSocket:尝试与容器的指定端口建立 TCP 连接。
  • exec:在容器内执行命令,返回码为 0 视为成功。
  • grpc(v1.24+ 稳定):使用 gRPC 健康检查协议,服务状态为 SERVING 视为成功。
注意事项
  • 不要省略探针。没有探针的 Pod 在容器进程僵死(如死锁)时无法被自动恢复。
  • Startup Probe 是慢启动应用的救星。如果你的应用启动需要较长时间(如 Java 应用加载类库),务必配置 Startup Probe,否则 Liveness Probe 可能在应用尚未就绪时就杀死容器。
  • Readiness Probe 不应过于严格,否则可能导致滚动更新时出现流量中断。

参考文档:Configure Liveness, Readiness and Startup Probes


二、Deployment:无状态应用的编排之王

Deployment 是 Kubernetes 中最常用的工作负载资源,专门用于管理无状态应用。它提供了声明式更新、滚动发布和回滚等生产级特性。

2.1 ReplicaSet 与 Deployment 的关系

Deployment → 管理 → ReplicaSet → 管理 → Pod

ReplicaSet (RS) 是下一层的控制器,负责确保指定数量的 Pod 副本始终在运行。而 Deployment 是更高层的抽象,它在 ReplicaSet 之上增加了版本管理滚动更新能力。

实践建议

在日常使用中,几乎不需要直接操作 ReplicaSet。你应该始终通过 Deployment 来管理应用,让 K8s 自动处理 ReplicaSet 的创建和清理。

2.2 滚动更新(Rolling Update)策略

Deployment 支持两种更新策略:

策略说明适用场景
RollingUpdate(默认)逐步替换旧 Pod 为新 Pod,保证零停机生产环境
Recreate先删除所有旧 Pod,再创建新 Pod不兼容多版本共存的场景

RollingUpdate 通过两个关键参数控制更新节奏:

  • maxSurge:更新期间允许超出期望副本数的最大 Pod 数(可以是绝对数或百分比)。
  • maxUnavailable:更新期间允许不可用的最大 Pod 数(可以是绝对数或百分比)。
默认值

Deployment 默认的滚动更新策略为:maxSurge: 25%maxUnavailable: 25%。这意味着在更新期间,可用副本数至少为 75%,最多为 125%。

参考文档:Deployments - Updating a Deployment

2.3 回滚机制(Rollback)

Deployment 的每一次更新都会创建一个新的 ReplicaSet,并保留历史版本(由 revisionHistoryLimit 控制,默认为 10)。这使得回滚操作变得极其简单:

# 查看部署历史
kubectl rollout history deployment/nginx-deployment

# 回滚到上一个版本
kubectl rollout undo deployment/nginx-deployment

# 回滚到指定版本
kubectl rollout undo deployment/nginx-deployment --to-revision=2

2.4 完整 YAML 示例

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: default
labels:
app: nginx
spec:
replicas: 3 # 期望的 Pod 副本数
revisionHistoryLimit: 10 # 保留的历史 ReplicaSet 数量
strategy:
type: RollingUpdate # 更新策略:RollingUpdate / Recreate
rollingUpdate:
maxSurge: 1 # 滚动更新时最多多创建 1 个 Pod
maxUnavailable: 0 # 滚动更新时最多允许 0 个 Pod 不可用
selector:
matchLabels: # 必须与 Pod template 的 labels 匹配
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.27
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 256Mi
livenessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /
port: 80
initialDelaySeconds: 5
periodSeconds: 5
生产环境建议
  • 设置 maxUnavailable: 0 可以确保滚动更新过程中服务容量不会下降,代价是更新期间需要更多资源(maxSurge 需要大于 0)。
  • 配合 Pod Disruption Budget (PDB) 使用,可以在节点维护时确保最少可用副本数。

三、StatefulSet:有状态应用的首选

StatefulSet 专为需要稳定网络标识持久存储的有状态应用设计,如数据库(MySQL、PostgreSQL)、消息队列(Kafka、RabbitMQ)和分布式存储系统(Elasticsearch、etcd)。

3.1 与 Deployment 的核心区别

特性DeploymentStatefulSet
Pod 标识随机生成的名称(如 nginx-7b9f...固定的序号名称(如 mysql-0, mysql-1
网络标识Pod IP 随重建而变化每个 Pod 有稳定的 DNS 名称
存储所有 Pod 共享相同的 PVC每个 Pod 有独立的 PVC
部署顺序并行创建按序号顺序创建(0 → 1 → 2 → ...)
删除顺序并行删除按序号逆序删除(... → 2 → 1 → 0)
扩缩容随机选择 Pod 删除/创建严格按序号操作

3.2 有序部署/扩展/删除

StatefulSet 的核心特性之一是有序性 (Ordinality)。当创建或扩展 StatefulSet 时,Pod 会按照序号从 0 开始依次创建,且只有前一个 Pod 进入 Running 且 Ready 状态后,才会创建下一个 Pod。

3.3 稳定的网络标识和持久存储

StatefulSet 为每个 Pod 提供以下稳定性保证:

  • 稳定的网络标识:Pod 名称格式为 <statefulset-name>-<ordinal>(如 mysql-0),且关联的 Headless Service 会为每个 Pod 创建一个稳定的 DNS 记录:<pod-name>.<headless-service>.<namespace>.svc.cluster.local
  • 持久存储:通过 volumeClaimTemplates,StatefulSet 会为每个 Pod 自动创建独立的 PVC。即使 Pod 被重新调度到其他节点,只要绑定了相同的 PVC,数据就不会丢失。

3.4 完整 YAML 示例

apiVersion: v1
kind: Service # Headless Service,用于稳定的网络标识
metadata:
name: mysql
labels:
app: mysql
spec:
clusterIP: None # Headless Service:不分配 ClusterIP
selector:
app: mysql
ports:
- port: 3306
targetPort: 3306
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql
spec:
serviceName: mysql # 必须指向关联的 Headless Service
replicas: 3
selector:
matchLabels:
app: mysql
template:
metadata:
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:8.0
ports:
- containerPort: 3306
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-secret
key: root-password
volumeMounts:
- name: data
mountPath: /var/lib/mysql
livenessProbe:
exec:
command: ["mysqladmin", "ping", "-h", "localhost"]
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
exec:
command: ["mysql", "-h", "localhost", "-e", "SELECT 1"]
initialDelaySeconds: 5
periodSeconds: 5
volumeClaimTemplates: # 为每个 Pod 自动创建独立的 PVC
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: standard
resources:
requests:
storage: 10Gi
注意事项
  • StatefulSet 不会自动创建 Headless Service,你需要手动创建。
  • 删除 StatefulSet 时,默认不会删除关联的 PVC,以防止数据丢失。如需同时删除,需要手动清理。
  • StatefulSet 的滚动更新默认使用 OnDelete 策略(即手动删除 Pod 后才会重建),如需自动滚动更新,需设置 .spec.updateStrategy.type: RollingUpdate

参考文档:StatefulSets


四、DaemonSet:节点级守护进程

DaemonSet 确保集群中的每个(或特定)节点上都运行一个 Pod 副本。当节点加入集群时,DaemonSet 会自动为其创建 Pod;当节点移除时,这些 Pod 也会被自动回收。

4.1 典型使用场景

场景示例
日志收集Fluentd、Filebeat、Promtail
监控 AgentPrometheus Node Exporter、Datadog Agent
网络插件Calico、Cilium、Flannel
存储守护进程Ceph、GlusterFS 客户端
安全合规Falco(运行时安全检测)、Twistlock

4.2 滚动更新策略

DaemonSet 支持三种更新策略:

策略说明
RollingUpdate(默认)逐个节点更新 Pod,可通过 maxUnavailable 控制并发度
OnDelete手动删除旧 Pod 后才会创建新 Pod
Surging(K8s 1.22+)先创建新 Pod 再删除旧 Pod,节点上会短暂运行两个 Pod

4.3 完整 YAML 示例

apiVersion: apps/v1
kind: DaemonSet
metadata:
name: node-exporter
namespace: monitoring
labels:
app: node-exporter
spec:
selector:
matchLabels:
app: node-exporter
updateStrategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 1 # 最多允许 1 个节点上的 Pod 不可用
template:
metadata:
labels:
app: node-exporter
spec:
hostNetwork: true # 使用宿主机网络(监控场景常见)
hostPID: true # 使用宿主机 PID 命名空间
tolerations: # 容忍所有 Taint,确保在所有节点上运行
- operator: Exists
containers:
- name: node-exporter
image: prom/node-exporter:v1.8.0
args:
- "--web.listen-address=:9100"
- "--path.procfs=/host/proc"
- "--path.sysfs=/host/sys"
ports:
- containerPort: 9100
hostPort: 9100
volumeMounts:
- name: proc
mountPath: /host/proc
readOnly: true
- name: sys
mountPath: /host/sys
readOnly: true
resources:
limits:
cpu: 200m
memory: 100Mi
volumes:
- name: proc
hostPath:
path: /proc
- name: sys
hostPath:
path: /sys
最佳实践
  • DaemonSet 通常需要使用 hostNetwork: truehostPID: truehostPath 卷来访问节点级别的资源。
  • 始终配置 tolerations 以确保 DaemonSet Pod 可以被调度到带有 Taint 的节点(如 Master 节点)。
  • 为 DaemonSet Pod 设置严格的资源限制,避免它们占用过多节点资源影响业务应用。

五、Job 与 CronJob:任务编排

5.1 一次性任务(Job)

Job 用于运行一次性任务,确保 Pod 成功执行完毕后终止。Job 会持续跟踪 Pod 的完成状态,并在失败时根据重试策略重新创建 Pod。

核心字段说明:

字段说明默认值
completions需要成功完成的 Pod 数1
parallelism并行运行的 Pod 数1
backoffLimit最大重试次数6
activeDeadlineSecondsJob 超时时间(秒),超时后标记为失败无限制
ttlSecondsAfterFinishedJob 完成后的自动清理时间(K8s 1.23+)永不清理

5.2 并行任务(Parallelism + Completions)

Job 的 parallelismcompletions 字段可以组合出不同的执行模式:

parallelismcompletions模式
11单次顺序执行
N1N 个 Pod 并行竞争,任一成功即完成
NNN 个 Pod 并行工作队列模式
1N顺序执行 N 个任务

5.3 定时任务(CronJob)

CronJob 基于 Cron 表达式来定期创建 Job。它的调度规则与 Linux 的 crontab 基本一致,格式为:

# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 日 (1 - 31)
# │ │ │ ┌───────────── 月 (1 - 12)
# │ │ │ │ ┌───────────── 星期 (0 - 6, 0 = 周日)
# │ │ │ │ │
# * * * * *
CronJob 时区

从 Kubernetes v1.25 起,CronJob 支持 timeZone 字段,可以指定 Cron 表达式所使用的时区(IANA 时区格式,如 "Asia/Shanghai")。未指定时默认使用 UTC 时区。

参考文档:CronJobs

apiVersion: batch/v1
kind: CronJob
metadata:
name: database-backup
spec:
schedule: "0 2 * * *" # 每天凌晨 2 点执行
concurrencyPolicy: Forbid # 禁止并发运行
successfulJobsHistoryLimit: 3 # 保留最近 3 个成功的 Job
failedJobsHistoryLimit: 1 # 保留最近 1 个失败的 Job
jobTemplate:
spec:
backoffLimit: 2
activeDeadlineSeconds: 3600 # 超过 1 小时则标记为失败
template:
spec:
containers:
- name: backup
image: postgres:16
command:
- /bin/bash
- -c
- pg_dump -h db-service -U $DB_USER -d $DB_NAME > /backup/$(date +%Y%m%d).sql
env:
- name: DB_USER
valueFrom:
secretKeyRef:
name: db-secret
key: username
- name: DB_NAME
value: "myapp"
volumeMounts:
- name: backup-data
mountPath: /backup
restartPolicy: Never # Job 必须设置为 Never 或 OnFailure
volumes:
- name: backup-data
persistentVolumeClaim:
claimName: backup-pvc
重要提醒
  • Job 的 Pod restartPolicy 必须设置为 NeverOnFailure,不能是 Always
  • concurrencyPolicy: Forbid 确保上一次任务尚未完成时不会启动新任务,对于数据库备份等场景至关重要。
  • 务必设置 successfulJobsHistoryLimitfailedJobsHistoryLimit,避免历史 Job 堆积消耗 etcd 存储空间。

5.4 Job 完整 YAML 示例

apiVersion: batch/v1
kind: Job
metadata:
name: data-migration
spec:
completions: 1 # 需要成功完成 1 次
parallelism: 1 # 同时运行 1 个 Pod
backoffLimit: 3 # 最多重试 3 次
activeDeadlineSeconds: 600 # 超时 10 分钟
ttlSecondsAfterFinished: 86400 # 完成后 24 小时自动清理
template:
spec:
restartPolicy: Never
containers:
- name: migrate
image: myapp:v2.0
command: ["python", "manage.py", "migrate"]
env:
- name: DATABASE_URL
valueFrom:
secretKeyRef:
name: app-secrets
key: database-url

六、ReplicaSet / ReplicationController(简述)

ReplicationController (RC)

ReplicationController 是 Kubernetes 最早的副本管理机制,用于确保指定数量的 Pod 副本始终运行。它已被 ReplicaSet 取代,目前仅存在于 API 中以保持向后兼容。

ReplicaSet (RS)

ReplicaSet 是 ReplicationController 的升级版,主要改进是支持基于集合的标签选择器(Set-based Selector),使得选择逻辑更加灵活。

特性ReplicationControllerReplicaSet
标签选择器仅支持等值匹配(environment=production支持集合匹配(environment in (production, staging)
推荐使用已废弃作为 Deployment 的底层实现,不直接使用
实践建议

不要直接创建 ReplicaSet。使用 Deployment 来管理无状态应用,Deployment 会在底层自动创建和管理 ReplicaSet。只有在需要执行非常特殊的操作(如手动管理 Pod 副本)时,才考虑直接使用 ReplicaSet。


七、工作负载选择决策树

面对不同的业务需求,如何选择合适的工作负载资源?以下决策树可以帮助你快速做出判断:

快速参考表

工作负载核心特征典型场景Pod 数量
Deployment无状态、可滚动更新、可回滚Web 服务、API 服务、微服务可变(replicas)
StatefulSet有状态、稳定标识、有序部署数据库、消息队列、分布式存储可变(replicas)
DaemonSet每节点一个 Pod日志收集、监控 Agent、网络插件= 节点数
Job一次性任务,完成后终止数据迁移、批处理、CI/CD固定(completions)
CronJob定时创建 Job数据库备份、报表生成、清理任务每次执行创建新 Job

八、本章小结

本文从 Pod 的本质出发,系统地解析了 Kubernetes 中五大核心工作负载资源:

  1. Pod 是 Kubernetes 的原子调度单元,通过共享网络和存储实现了"超亲密容器"的协作模式。理解 Pod 的生命周期和探针机制是排查问题的基础。

  2. Deployment 是无状态应用的首选,通过 ReplicaSet 实现版本管理,支持零停机的滚动更新和一键回滚。

  3. StatefulSet 为有状态应用提供了稳定的网络标识、独立的持久存储和有序的部署/删除策略,是运行数据库和消息队列等场景的最佳选择。

  4. DaemonSet 确保每个节点运行一个 Pod 副本,是部署基础设施组件(日志、监控、网络插件)的标准方式。

  5. Job 与 CronJob 覆盖了一次性任务和定时任务的需求,支持灵活的并行度和重试策略。

选择工作负载资源时,核心判断依据是:应用是否有状态是否需要长期运行是否需要在每个节点上运行。掌握这些工作负载的特性与适用场景,是构建可靠 Kubernetes 应用的基石。

在下一篇文章中,我们将深入探讨 Kubernetes 的 Service 与网络模型,解析 ClusterIP、NodePort、LoadBalancer 和 Ingress 的工作原理与选型策略。


参考文档

本文内容基于 Kubernetes 官方文档校验,以下为核心参考链接:

Kubernetes 全景解析 (0):架构设计与核心概念

· 阅读需 20 分钟
Rainy
雨落无声,代码成诗 —— 致力于技术与艺术的极致平衡

"如果你觉得 Kubernetes 太复杂,那是因为它解决的问题本身就很复杂。"

在云原生(Cloud Native)的世界里,Kubernetes(简称 K8s)已经从"可选技能"变成了"基础设施常识"。无论你是后端工程师、DevOps 工程师,还是架构师,理解 K8s 的设计思想和运行机制,都是构建现代分布式系统的必修课。

然而,K8s 的学习曲线之陡峭也是出了名的。官方文档动辄数千页,概念繁多且相互关联,很容易让人陷入"见树木不见森林"的困境。

本系列文章将从架构设计出发,逐层深入到核心概念、工作负载管理、网络模型、存储体系、调度策略与安全机制,帮助你构建一套完整、系统的 K8s 认知框架。本文作为系列的第零章,将聚焦于最根本的问题:Kubernetes 是什么?它是如何设计的?它由哪些核心部分组成?


一、为什么需要 Kubernetes

1.1 容器化演进的必然之路

要理解 K8s 存在的意义,我们需要先回顾应用部署方式的演进历程:

阶段部署方式隔离性资源利用率启动速度运维复杂度
物理机时代应用直接部署在物理服务器上-
虚拟机时代Hypervisor 划分多个 VM分钟级
容器时代Docker 等容器引擎秒级
编排时代Kubernetes 等编排系统秒级低(自动化)

物理机时代,一个应用独占一台服务器,资源浪费严重。为了提高利用率,我们开始在同一个物理机上部署多个应用,但随之而来的是依赖冲突、端口争抢、一个应用崩溃拖垮整台机器等问题。

虚拟机时代,Hypervisor(如 VMware、KVM)在物理机上虚拟出多个独立的操作系统实例,实现了良好的隔离。但虚拟机本身就很重——一个 VM 动辄几个 GB,启动需要数分钟,而且携带了完整的 Guest OS,大量资源被浪费在运行重复的系统服务上。

容器时代,Docker 横空出世。容器共享宿主机的内核,不需要 Guest OS,一个镜像通常只有几十 MB,启动只需毫秒级。容器通过 Namespace 实现视图隔离,通过 Cgroups 实现资源限制,在轻量和隔离之间找到了一个绝佳的平衡点。

但容器解决的是**"如何打包和运行应用"的问题,却没有解决"如何管理成百上千个容器"**的问题。当你的应用从 3 个容器扩展到 3000 个,跨越几十台机器时,以下问题接踵而至:

  • 哪个容器应该运行在哪台机器上?
  • 容器挂了谁来重启?机器挂了谁来迁移?
  • 如何实现滚动更新而不中断服务?
  • 如何让前端服务发现后端服务的地址?
  • 如何让外部流量均匀地分发到多个实例?

这就是 Kubernetes 登场的时刻。

1.2 K8s 解决的核心问题

Kubernetes 作为一个容器编排平台(Container Orchestration Platform),本质上解决的是一个大规模自动化管理的问题。它将运维人员从手工操作中解放出来,通过声明式配置和自动化控制循环,实现了:

  • 自动部署与回滚:声明期望状态,K8s 负责将实际状态趋近期望状态
  • 服务发现与负载均衡:内置 DNS 和 Service 机制,无需外部注册中心
  • 自动扩缩容:根据 CPU/内存/自定义指标自动调整实例数量
  • 自我修复:容器崩溃自动重启,节点故障自动迁移
  • 滚动更新与蓝绿部署:零停机更新应用

1.3 K8s 的设计哲学

理解 K8s 的设计哲学,比记住一百个命令更有价值。K8s 的三个核心设计理念贯穿了它的每一个组件:

声明式(Declarative)而非命令式(Imperative)

命令式 vs 声明式
  • 命令式:你告诉系统"做什么"——docker run nginxkubectl scale deployment nginx --replicas=3
  • 声明式:你告诉系统"你要什么"——提交一个 YAML 文件描述"我需要 3 个 Nginx 实例",K8s 会持续工作直到实际状态与期望状态一致

声明式的好处是:可重复、可审计、可版本化。同一个 YAML 文件,无论执行多少次,结果都是一致的。

不可变基础设施(Immutable Infrastructure)

K8s 中的容器镜像一旦构建就不应该被修改。需要更新时,你应该构建新镜像、创建新版本,而不是 SSH 进容器里改配置。这与传统"登录服务器打补丁"的运维方式截然不同。

最终一致性(Eventual Consistency)

K8s 不保证你的请求立即生效,但保证系统最终会收敛到期望状态。这种设计牺牲了即时性,换取了极高的可靠性和容错能力。即使你同时提交了多个冲突的变更,系统也能通过控制循环最终达到一个一致的状态。


二、K8s 整体架构

Kubernetes 采用经典的主从架构(Master-Worker Architecture),分为**控制面(Control Plane)数据面(Data Plane)**两个层级。理解这两个层面的职责划分,是理解 K8s 一切行为的基础。

2.1 架构全景图

2.2 控制面组件详解

控制面是 K8s 集群的"大脑",负责全局决策和集群状态的维护。在生产环境中,为了保证高可用,控制面组件通常部署在多个独立的 Master 节点上。

kube-apiserver:集群的统一入口

kube-apiserver 是整个 K8s 系统的唯一入口。所有组件——无论是内部的 Scheduler、Controller,还是外部的 kubectl、CI/CD Pipeline——都通过 API Server 进行通信。

为什么所有通信都要经过 API Server?

这种设计被称为 "Hub-and-Spoke" 模式。它的好处是:

  1. 统一鉴权:所有请求都在一个地方进行认证(Authentication)、授权(Authorization)和准入控制(Admission Control)
  2. 解耦:各组件不需要知道彼此的存在,只需要与 API Server 交互
  3. 审计:所有操作都有统一的日志记录

API Server 本身是无状态的,它可以水平扩展。所有的状态数据都存储在 etcd 中。

etcd:集群的"真相之源"

etcd 是一个分布式的、一致的键值存储系统,基于 Raft 共识算法实现。它是 K8s 集群的唯一数据源(Single Source of Truth)——集群中所有的一切:节点信息、Pod 状态、配置数据、Secret……全部存储在 etcd 中。

etcd 是 K8s 的命脉
  • etcd 的性能直接决定了整个集群的响应速度
  • etcd 的数据丢失意味着集群状态的丢失(虽然 Pod 可以重建,但某些运行时状态无法恢复)
  • 生产环境建议部署 3 或 5 个 etcd 节点(奇数个,满足 Raft 多数派要求)
  • 必须定期备份 etcd 数据

kube-scheduler:调度决策者

当你创建一个 Pod 时,API Server 只是将这个 Pod 的信息记录到了 etcd 中——此时 Pod 还处于 Pending 状态,因为它还没有被分配到任何节点上。Scheduler 的职责就是为每个未调度的 Pod 选择一个最合适的节点。

调度过程分为三个阶段:

  1. 过滤(Filtering):排除不满足条件的节点(资源不足、端口冲突、污点容忍不匹配等)
  2. 打分(Scoring):对剩余节点进行优先级打分(亲和性、镜像本地性、负载均衡等),选择得分最高的节点
  3. 绑定(Binding):将调度决策应用到集群,将 Pod 与选定节点进行绑定
调度框架(Scheduling Framework)

从 v1.19 起,Kubernetes 调度器采用插件化架构(Scheduling Framework),上述三个阶段对应调度框架中的核心扩展点。整个调度过程分为调度周期(Scheduling Cycle)绑定周期(Binding Cycle),调度周期串行执行,绑定周期可并发执行。

参考文档:Scheduling Framework

kube-controller-manager:状态守护者

Controller Manager 运行着多个控制器(Controller),每个控制器都是一个独立的控制循环(Reconciliation Loop),负责监控集群的某一部分状态,并持续将其推向期望状态。

常见的内置控制器包括:

控制器职责
Deployment Controller确保 Deployment 管理的 Pod 副本数符合期望
ReplicaSet Controller确保 ReplicaSet 管理的 Pod 副本数符合期望
Node Controller监控节点健康状态,节点失联时触发 Pod 驱逐
Service Account Controller为命名空间创建默认 ServiceAccount
EndpointSlice Controller维护 Service 与 Pod 的映射关系(推荐使用 EndpointSlice 替代已废弃的 Endpoints)
控制器的本质:控制循环

每个控制器的工作模式都是相同的:

while true:
实际状态 = 从 API Server 获取当前状态
期望状态 = 从配置中获取期望状态
if 实际状态 != 期望状态:
执行调谐操作(创建/删除/更新)
sleep(一段时间)

这就是 K8s 所谓的**"调谐(Reconciliation)"**机制。

cloud-controller-manager:云平台桥梁

如果你在 AWS、GCP、Azure 等云平台上运行 K8s,CCM 负责与云厂商的 API 交互,管理云平台特有的资源:

  • Node Controller:调用云 API 查询节点地址和状态
  • Route Controller:配置云平台的路由规则
  • Service Controller:创建云平台的负载均衡器(如 AWS ELB)

CCM 的引入使得 K8s 的核心代码不需要耦合任何特定云厂商的 SDK,实现了云平台无关性。

2.3 数据面组件详解

数据面(也叫 Worker Node)是实际运行应用工作负载的地方。每个 Worker 节点上运行着三个核心组件:

kubelet:节点上的"车间主任"

kubelet 是运行在每个 Worker 节点上的代理,它的职责是:

  1. 接收指令:从 API Server 获取分配到本节点的 Pod 规格(Spec)
  2. 管理容器:通过 CRI(Container Runtime Interface)调用容器运行时,创建和管理 Pod 中的容器
  3. 健康检查:定期执行 Liveness Probe、Readiness Probe 和 Startup Probe
  4. 状态上报:将节点和 Pod 的状态信息上报给 API Server

你可以把 kubelet 理解为一个"车间主任"——它不制定生产计划(那是 Scheduler 的事),但负责确保分配到自己车间的生产任务被正确执行。

kube-proxy:网络规则的维护者

kube-proxy 运行在每个节点上,负责维护节点的网络规则,实现 Service 的负载均衡和网络代理。它通过操作 iptables 或 IPVS 规则,将访问 Service 的流量转发到后端的 Pod。

简单来说,当你访问一个 Service 的 ClusterIP 时,kube-proxy 维护的规则会将流量自动转发到该 Service 关联的某个 Pod 上。

kube-proxy 是可选组件(v1.34+)

根据 Kubernetes 官方文档,kube-proxy 是一个可选组件。如果你使用的网络插件(CNI)自身实现了 Service 的数据包转发功能,并提供与 kube-proxy 等效的行为,那么你不需要在集群节点上运行 kube-proxy

例如,Cilium 等现代 CNI 插件可以直接替代 kube-proxy 的功能,通过 eBPF 实现更高效的 Service 负载均衡和网络代理。

参考文档:Kubernetes Architecture - kube-proxy (optional)

Container Runtime:容器的实际执行者

kubelet 本身并不直接运行容器,而是通过 CRI(Container Runtime Interface) 标准接口调用容器运行时。常见的 CRI 兼容运行时包括:

  • containerd:目前最主流的选择,Docker 的核心运行时组件独立出来的版本
  • CRI-O:专为 Kubernetes 设计的轻量级运行时,由 Red Hat 主导
  • Kata Containers:提供虚拟机级别的隔离,适用于安全要求极高的场景
Docker 与 K8s 的"分手"

在 K8s 1.20 之前的版本中,kubelet 通过一个名为 dockershim 的内置组件直接与 Docker 通信。但从 K8s 1.24 开始,dockershim 被正式移除。这并不意味着你不能在 K8s 中运行 Docker 镜像——Docker 镜像遵循 OCI 标准,containerd 和 CRI-O 都能运行它们。移除的只是对 Docker daemon 的直接依赖。


三、核心概念模型

Kubernetes 中的一切都被抽象为 API 对象(API Object)。理解这些对象及其关系,是使用 K8s 的基础。

3.1 API 对象与声明式配置

在 K8s 中,你不需要告诉系统"如何做",而是描述"你要什么"。你通过提交 YAML(或 JSON)文件来定义 API 对象,API Server 会将你的声明持久化到 etcd,然后各个控制器会持续工作,确保实际状态趋近于你声明的期望状态。

3.2 YAML 配置结构解析

每个 K8s API 对象的 YAML 配置都遵循以下结构:

apiVersion: apps/v1 # API 版本,标识对象属于哪个 API 组
kind: Deployment # 对象类型,K8s 内置了几十种对象类型
metadata: # 对象的元数据(名称、标签、注解等)
name: nginx-deployment
namespace: default
labels:
app: nginx
tier: frontend
spec: # 对象的规格(期望状态)
replicas: 3 # 期望运行 3 个 Pod 副本
selector: # 选择器,用于关联管理的 Pod
matchLabels:
app: nginx
template: # Pod 模板,定义如何创建 Pod
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 500m
memory: 512Mi
YAML 配置的四个必填字段
  1. apiVersion:指定 API 版本。v1 是核心 API 组,apps/v1networking.k8s.io/v1 等是扩展 API 组
  2. kind:对象类型,如 Pod、Deployment、Service、ConfigMap 等
  3. metadata:对象的"身份证",至少包含 name,通常还包含 labelsannotations
  4. spec:对象的"期望状态",不同类型的对象有不同的 spec 结构

3.3 API 对象层级关系

K8s 的 API 对象之间存在着清晰的层级关系。上层对象管理下层对象,形成了一个从粗粒度到细粒度的管理链。

Pod:K8s 的最小调度单元

初学者常有一个疑问:为什么 K8s 的最小单位是 Pod 而不是 Container?

Pod 是一个逻辑概念,它可以包含一个或多个紧密耦合的容器。这些容器共享:

  • 网络命名空间:同一个 Pod 中的容器可以通过 localhost 互相访问
  • 存储卷:可以挂载共享的 Volume
  • 运行约束:总是被调度到同一个节点上

一个典型的多容器 Pod 场景是"Sidecar 模式":主容器运行应用逻辑,Sidecar 容器负责日志收集、监控数据导出或代理转发。


四、一次请求的完整旅程

理论讲了不少,现在让我们通过一个具体的场景来串联所有知识:当你执行 kubectl apply -f nginx-deployment.yaml 时,K8s 内部到底发生了什么?

4.1 完整时序图

4.2 控制循环(Reconciliation Loop)详解

时序图展示了一次性的创建流程,但 K8s 的真正威力在于其持续运行的控制循环。让我们深入理解这个机制:

第一步:感知变化(Watch 机制)

K8s 的各个组件并不是定期轮询 API Server,而是通过基于 HTTP 长连接的 Watch 机制 来感知变化。当你创建、修改或删除一个对象时,API Server 会通过 Watch 通道将事件推送给所有订阅者。事件类型包括:

  • ADDED:新对象被创建
  • MODIFIED:对象被更新
  • DELETED:对象被删除

这种基于事件驱动的设计,比轮询更高效,延迟更低。

第二步:计算差异(Diff)

控制器收到事件后,会将实际状态期望状态进行对比。例如,Deployment Controller 发现期望的副本数是 3,但当前只有 2 个 Pod 在运行。

第三步:执行调谐(Reconcile)

控制器计算出需要执行的操作后,会通过 API Server 发起调谐请求。在上面的例子中,Deployment Controller 会创建一个新的 ReplicaSet,ReplicaSet Controller 会创建一个新的 Pod。

第四步:重复

整个过程是一个无限循环。即使某个操作失败了,下一轮循环也会再次尝试。这就是 K8s 实现自愈能力的根本原因。

水平扩展:自定义控制器

K8s 的控制器模式不仅限于内置资源。通过 CRD(Custom Resource Definition)Operator 模式,你可以定义自己的 API 对象和对应的控制器。例如,Prometheus Operator 可以管理 Prometheus 实例的生命周期,Cert-Manager Operator 可以自动化 TLS 证书的申请和续期。这就是 K8s 被称为"平台的平台"的原因。


五、本章小结与下一篇预告

本文作为 Kubernetes 全景解析系列的第零章,我们从宏观视角梳理了 K8s 的全貌:

维度核心要点
设计哲学声明式配置、不可变基础设施、最终一致性
架构分层控制面(决策)+ 数据面(执行)
控制面组件API Server(入口)、etcd(存储)、Scheduler(调度)、Controller Manager(控制循环)
数据面组件kubelet(节点代理)、kube-proxy(网络代理)、CRI(容器运行时)
核心抽象一切皆 API 对象,通过 YAML 声明期望状态
运行机制Watch 事件驱动 + 控制循环持续调谐

掌握这些基础知识后,你将不再对 K8s 感到迷茫——它的每一个行为都可以追溯到上述架构和机制中。

下一篇,我们将深入 K8s 的工作负载管理: 从 Pod 的生命周期到 Deployment、StatefulSet、DaemonSet 等工作负载控制器的使用场景与最佳实践。理解了工作负载,你就能真正开始在 K8s 上部署和运行应用了。


系列导航

章节主题状态
0架构设计与核心概念✅ 已发布
1工作负载与 Pod 生命周期深度解析✅ 已发布
2网络模型与服务发现全链路解析✅ 已发布
3存储体系与配置管理深度剖析✅ 已发布
4调度器、资源管理与弹性伸缩✅ 已发布
5安全体系与可观测性全景✅ 已发布
6生产级微服务架构实战✅ 已发布
7有状态应用与 Operator 模式实战✅ 已发布

参考文档

本文内容基于 Kubernetes 官方文档校验,以下为核心参考链接:

Harness 工程深度解析:驾驭 AI 智能体与构建生产级 CI/CD 的终极指南

· 阅读需 21 分钟
Rainy
雨落无声,代码成诗 —— 致力于技术与艺术的极致平衡

在现代软件工程中,"Harness"(原意为马具/束具)一词具有强大的双重含义。一方面,它指的是驾驭 AI 智能体(AI Agents)的基础设施——通过约束、反馈循环和任务拆分策略,将模型的原始能力转化为可靠的输出。另一方面,它是 Harness Open Source 的名字——一个集源码控制、CI/CD 流水线、开发环境和制品库于一体的端到端开发者平台。

本文将深入探讨这两个维度:从智能体 Harness 设计的概念性突破,到使用 Harness 平台的生产级 CI/CD 实战。


第一部分:什么是 Harness 工程?

马与骑手的隐喻

OpenAI 的工程团队提出了一个生动的隐喻:AI 模型就像一匹马——力量强大,但不可预测且容易跑偏。而 Harness 则是工程师围绕它构建的一切——Linter、结构化测试、文档标准、反馈循环——这些装置能够有效地引导这股力量产出成果。

在这种范式下,模型是 CPU,而 Harness 是操作系统 (OS)。CPU 负责计算,但 OS 负责文件系统、内存管理、权限控制和 I/O 调度。没有 OS 的 CPU 只是一个发热的芯片;没有 Harness 的模型只是一个产生 Token 的预测器。

工程师的主要职责正在发生转变:从编写代码转变为设计环境构建反馈循环,从而让 AI 智能体能够可靠地运行。

— OpenAI, "Harness engineering: leveraging Codex in an agent-first world" (2026年2月)

三大支柱

Harness 工程建立在三个基本支柱之上:

支柱含义示例
上下文工程 (Context Engineering)确保智能体在上下文中拥有恰到好处的信息压缩 (Compaction)、即时加载 (JIT)、结构化记忆
架构约束 (Architectural Constraints)通过机械化手段强制执行“好代码”的标准Linter、依赖规则、结构化测试
熵增管理 (Entropy Management)长期保持代码库的连贯性垃圾回收、代码重构智能体

如果没有 Harness,即使是前沿模型产出的结果也往往是功能尚可但弱不禁风的——重复的代码、漏洞百出的核心逻辑、平庸的设计以及逐渐腐化的上下文。而通过精心设计的 Harness,同一个模型可以在 10 个冲刺(Sprint)中构建出一个拥有 16 个功能、视觉风格统一且完全可运行的应用程序。


第二部分:上下文工程——有限的资源

上下文窗口(Context Window)是智能体编程中最宝贵的资源。Anthropic 将其描述为一种收益递减的有限预算——每一个 Token 都至关重要,将无关的历史记录塞满窗口会主动降低模型的性能。

策略 1:上下文压缩 (Compaction)

压缩 (Compaction) 将完整的上下文窗口浓缩为高保真的摘要。当对话接近 Token 上限时,系统会原地总结之前的轮次,仅保留最关键的细节。

压缩前:系统提示词 + 4 轮对话 + 12 次工具调用 + 冗长的输出
→ 180K tokens (接近上限)

压缩后:压缩后的摘要:关键状态 + 当前任务 + 核心上下文
→ 30K tokens (留出更多工作空间)

虽然压缩保持了连贯性,但它并没有给智能体一个全新的开始,这意味着上下文焦虑 (Context Anxiety) 可能依然存在。

— Anthropic, "Harness design is key to performance at the frontier"

策略 2:上下文重置 (Context Reset)

上下文重置 (Context Reset) 采取了更彻底的方法:完全清空上下文窗口,启动一个新的智能体阶段(Session),并使用结构化的移交产物 (Handoff Artifact) 来承载上一个智能体的状态。

这解决了一个被称为上下文焦虑的关键失效模式——即当模型认为自己接近上下文极限时,会过早地结束工作。全新的上下文为智能体提供了一个没有累赘的清晰起点。

移交产物通常包含:

  • 当前进度——哪些功能已完成,哪些正在进行中
  • 文件清单——关键文件及其用途
  • 后续步骤——接下来要执行的具体任务
  • 已知问题——推迟处理的 Bug 或决策

策略 3:即时加载 (JIT Loading)

对于拥有数百万行代码的大型仓库,不可能将所有代码都塞进上下文。JIT 加载利用工具(如 grepast-grep)按需检索代码片段,并在智能体请求时将其动态注入上下文。


第三部分:被忽视的核心——结构化约束

除了上下文管理,Harness 工程中还有几个常被忽视但至关重要的概念:

1. 上下文压舱物 (Context Ballast)

智能体需要一门“通用语言”来理解项目。在仓库中加入 CLAUDE.mdAGENTS.md 不仅是为了人类开发者,更是为了给智能体提供压舱物 (Ballast)

实战示例:一个典型的 CLAUDE.md 片段

# Agent 架构规范
1. **状态管理**:严禁使用 Redux。我们统一使用 Zustand 进行状态管理。
2. **样式**:所有组件必须使用 Tailwind CSS,不要创建任何 `.css` 文件。
3. **数据库**:所有数据库查询必须通过 Prisma ORM 进行,严禁直接拼接 Raw SQL。

这为智能体提供了极其清晰的操作结界,极大地减少了产生“技术债”的可能。

2. 机械化 guardrails (硬性约束)

不要指望通过 Prompt 告诉智能体“请不要在业务层写 SQL”。哪怕你在提示词中重复三遍,一旦上下文变长,它依然可能违规。

在 Harness 中,你应该配置一个 Linter 规则(例如使用 ast-grep 或 ESLint),在智能体生成代码后强制拦截:

# 机械化拦截示例:运行于工具调用完成后
$ npm run lint
❌ Error: [no-raw-sql] Detected Prisma raw query in src/services/user.ts:42

因为模型在面对“确凿的报错堆栈信息”时的修复能力,远远强于面对“你不应该怎么做”的软性建议时的遵循能力。这就是用程序的确定性去驾驭 LLM 的混沌性。

3. LLM-as-Auditor (审计者模式)

在生成者(Generator)输出代码后,由一个更高阶的模型(或配置了不同 System Prompt 的模型)担任 Auditor (审计者)。审计者不负责写代码,只负责在 Harness 环境中寻找逻辑漏洞和架构违规。


第四部分:多智能体协作——生成与评估的分离

近期 Harness 研究中最具影响力的见解或许是生成与评估的分离。受生成对抗网络 (GAN) 的启发,Anthropic 设计了一个三智能体系统,其表现显著优于单智能体方法。

三个角色

1. 规划者智能体 (Planner Agent)

规划者接收简单的 1-4 句话的提示词,并将其扩展为完整的产品规格说明书。它的指令是在保证雄心勃勃的范围的同时,专注于产品上下文而非细碎的技术细节。

输入: "给我做一个复古视频游戏制作器"

输出: RetroForge - 2D 复古游戏制作工具
涵盖 10 个冲刺的 16 个功能:
- 项目仪表盘与管理
- 基于瓦片的地图编辑器
- 像素画精灵编辑器
- 可视化实体行为系统
- 可运行的测试模式
- AI 辅助精灵生成器
- 音效与音乐系统
- 带分享链接的游戏导出
...

关键设计决策:规划者刻意避免指定具体的颗粒化技术实现。如果它在早期就定死了技术细节且不幸定错了,这些错误会一直向下游传递。

2. 生成者智能体 (Generator Agent)

生成者按冲刺 (Sprint) 进行开发,每次实现一个功能,使用标准的技术栈(如 React + Vite + FastAPI + SQLite/PostgreSQL)。它使用 Git 进行版本控制,并在每个冲刺结束时进行自测,然后移交给 QA。

3. 评估者智能体 (Evaluator Agent)

评估者使用 Playwright MCP 像真实用户一样与运行中的程序交互——点击页面、测试 UI 功能、验证 API 端点、检查数据库状态。它从四个维度对每个冲刺进行评分:

准则权重衡量标准
设计质量它感觉像是一个连贯的整体,还是零散部件的堆砌?
原创性是否有刻意的创意选择,还是仅仅是模板默认值?
工艺 (Craft)正常排版层级、间距、色彩和谐度、对比度
功能性正常用户能否理解界面并完成任务?

每个维度都有一个硬性门槛——如果任何一个低于门槛,该冲刺即宣告失败,生成者将收到详细的反馈意见。

冲刺合同谈判

在每个冲刺开始前,生成者和评估者会协商一份合同:在编写任何代码之前,它们会对“完成”的定义达成一致——即具体的、可测试的行为和验收标准。这弥合了高层用户故事与可验证实现之间的鸿沟。

自我评估的困境

一个关键洞察:当要求智能体评估自己的工作时,它们会习惯性地倾向于给出正面评价——将平庸的输出美化为优秀。将评估者从生成者中分离出来,使得调节怀疑精神变得更加容易:

同一智能体(自我评估):
"设计简洁且功能齐全。评分:9/10" ← 过度宽容

独立的评估者(调优为怀疑立场):
"布局使用了固定高度的面板,浪费了空间。工作流过于呆板
——没有任何指引告知用户在填充关卡前应先创建精灵。
核心游戏逻辑对输入无响应。评分:4/10" ← 诚实的评估

真实结果对比

指标单个智能体3 智能体 Harness
成本$6$124
耗时30 分钟4 小时
功能实现部分完成(核心逻辑断裂)16 个功能全量完成(运行正常)
运行模式无法运行具有物理效果的可玩模式
设计连贯性通用模板一致的视觉身份

虽然 Harness 的成本高出 20 倍,但产出质量的差距是惊人的:单智能体跑出来的游戏完全无法运行——实体显示在屏幕上但对输入毫无反应。而 Harness 运行产出了一个具有物理效果、AI 辅助精灵生成和连贯设计语言的可玩游戏。


第五部分:Harness 开源平台概述

除了“作为智能体基础设施的 Harness”这一概念外,还有一个具体的开源平台也叫 Harness——它是传说中的 Drone CI 的下一代演进。

什么是 Harness 开源平台?

Harness Open Source 是一个端到端的开发者平台,集成了四大模块:

模块描述关键特性
源码控制 (Source Control)基于 Git 的代码托管仓库、PR、代码审查、分支保护
CI/CD 流水线自动化构建与部署基于 Docker 的步骤、YAML 配置、并行执行
Gitspaces托管的开发环境云原生工作区、秒级启动
制品库 (Artifact Registry)包管理支持 Docker、Maven、npm 制品

Drone 的遗产

Harness 代表了对下一代 Drone巨大投入。Drone 仅专注于持续集成,而 Harness 增加了源码托管、开发环境和制品仓库——为团队提供了一个完整的开源 DevOps 平台。

Drone 的代码库目前作为一个功能分支继续存在,供那些在迁移期间仍需其特定流水线能力的用户使用。

技术架构

  • 后端:Go 语言编写,单二进制部署
  • 数据库:开发环境使用 SQLite,生产环境使用 PostgreSQL
  • 前端:基于 React 的 Web UI
  • 流水线:在 Docker 容器内执行
  • API:提供完整的 REST API 和 Swagger 文档

第六部分:实战——部署 Harness 并构建 CI/CD 流水线

第一步:使用 Docker 部署 Harness

运行 Harness 只需要一条命令:

docker run -d \
-p 3000:3000 \
-p 3022:3022 \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /tmp/harness:/data \
--name harness \
--restart always \
harness/harness

容器启动后,在浏览器中访问 http://localhost:3000

重要提示-v /tmp/harness:/data 挂载卷是必不可少的。如果没有它,当容器停止时,所有的仓库和配置都会丢失。生产环境请使用命名卷或持久化目录。

第二步:初始设置

  1. 创建管理员账号——首次访问时,系统会提示你创建初始管理员(默认:admin / changeit
  2. 生成 PAT (个人访问令牌) 用于 API 访问:
# 登录 CLI
./gitness login

# 生成个人访问令牌(有效期 1 年)
./gitness user pat "my-pat-uid" 2592000
  1. 测试 API
curl http://localhost:3000/api/v1/user \
-H "Authorization: Bearer $TOKEN"

第三步:创建仓库

进入 Web 界面并创建一个新仓库。Harness 提供完整的 Git 托管功能,包括:

  • 分支保护规则
  • Pull Request 开发流
  • 代码审查与评论
  • Webhook 集成

第四步:配置 CI/CD 流水线

在你的仓库根目录下创建 .harness/ 目录,并添加一个流水线配置文件:

# .harness/pipeline.yaml
kind: pipeline
spec:
stages:
- type: ci
spec:
steps:
- name: clone
type: run
spec:
container: alpine/git
script: |
git clone $REPO_URL .
echo "✅ 克隆完成"

- name: install
type: run
spec:
container: node:20-alpine
script: |
npm ci
echo "✅ 依赖安装完成"

- name: build
type: run
spec:
container: node:20-alpine
script: |
npm run build
echo "✅ 构建成功"

- name: test
type: run
spec:
container: node:20-alpine
script: |
npm run test -- --coverage
echo "✅ 单元测试全量通过"

- name: agent-qa-eval
type: run
spec:
container: python:3.11-alpine
script: |
pip install deepeval
echo "🤖 启动评估者智能体进行架构审查..."
deepeval test run tests/architecture_audit.py
echo "✅ 架构合规性扫描通过"

- name: docker-build
type: plugin
spec:
name: docker
inputs:
repo: myregistry/myapp
tags: latest,${DRONE_COMMIT_SHA:0:8}
dockerfile: Dockerfile

第五步:流水线触发器

Harness 支持自动流水线触发:

# 仅在推送到 main 分支时触发
trigger:
branch:
- main
event:
- push
- pull_request

每次向 main 分支执行 git push 时,系统将自动执行:

  1. 克隆仓库
  2. 安装依赖
  3. 构建项目
  4. 运行测试并统计覆盖率
  5. 构建并推送 Docker 镜像

第六步:流水线的 Docker 配置

Harness 流水线在 Docker 容器内运行。应用程序会自动与你的守护进程协商 Docker API 版本。

针对非标准的 Docker 运行时:

# Rancher Desktop
sudo ln -sf ~/.rd/docker.sock /var/run/docker.sock

# Colima
sudo ln -sf ~/.colima/default/docker.sock /var/run/docker.sock

# 或者在 .local.env 中设置环境变量:
GITNESS_DOCKER_HOST=unix:///Users/<username>/.rd/docker.sock

第七步:从源码构建(进阶)

对于贡献者或想要尝试最新功能的用户:

# 前提条件
# - Go 1.20+, Node.js (最新稳定版), protobuf v3.21.11

# 安装 Go 工具
go install google.golang.org/protobuf/cmd/protoc-gen-go@v1.28.1
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v1.2.0

# 安装依赖
make dep
make tools

# 构建 Web 界面
pushd web && yarn install && yarn build && popd

# 构建 Go 二进制文件
make build

# 运行服务器
./gitness server .local.env # → http://localhost:3000

第八步:访问 Swagger API

Harness 提供了详尽的 API 文档:

  • 主 APIhttp://localhost:3000/swagger
  • OpenAPI 规格书http://localhost:3000/openapi.yaml
  • 制品库 APIhttp://localhost:3000/registry/swagger/

你可以自动生成 UI 所使用的客户端代码:

./gitness swagger > web/src/services/code/swagger.yaml
cd web && yarn services

第七部分:连接两个世界——智能体 Harness + 平台 Harness

"Harness" 的两种含义——智能体基础设施与 DevOps 平台——正在合流。请设想一个现代 AI 驱动的开发工作流架构:

闭环流程

  1. 智能体 Harness 通过“规划者 → 生成者 → 评估者”循环生成高质量代码。
  2. CI/CD 集成:将此测试放入 Harness CI 的步骤中。当 Agent 生成代码或内容后,自动触发评估,只有通过阈值的 PR 才能合并。

如何使用 Aider 构建你的 Harness?

Aider 是目前最接近“Harness 工程”落地且高 Start (22k+) 的工具。它不仅是一个命令行聊天机器人,更是一个利用 Git 自动管理 Harness 的“协同工程师”。

实战步骤:

  1. 自动上下文映射:Aider 启动时会读取 .gitignore,并根据你的文件树生成 REPOMAP(仓库地图)。这本质上是 JIT Loading

  2. Git 原子化提交:每当 Aider 完成一个功能的修改并运行测试后,它会自动进行 Git 提交。这为智能体提供了一个可回退的阶段检查点

  3. 内置测试 Harness

    # 让 Aider 带着测试运行
    aider --test "npm test" src/payment.ts

    如果测试失败,Aider 会自动分析报错,尝试修复并重新运行测试,直到通过为止。这就是典型的反馈循环 (Feedback Loop)

  4. Git 提交将批准后的代码推送到 Harness 托管的仓库。

  5. Harness 流水线自动执行构建、测试和部署。

  6. 监控与反馈流回系统,启发下一轮迭代。

这就是未来:AI 智能体不仅仅是在写代码,而是通过真实的 CI/CD 流水线交付生产级软件

Aider 与 Agent 框架的定位对比

在实践中,我们需要区分交互式编码辅助系统级 Agent 框架

特性维度Aider (终端交互统帅)OpenHarness (系统指挥官)
主要使用者人类开发者(终端交互)系统/微服务(API/脚本驱动)
上下文形态基于 JIT / .gitignore 动态抓取CLAUDE.md + 会话记忆归档
回退机制依赖 Git 本地提交流多节点流式循环(API Retry)
典型应用场景快速实现单个 Feature、本地 Debug搭建自动评估管线、后台群体协作

第八部分:行业标杆——OpenHarness 开源框架

在所有的开源尝试中,OpenHarness (oh) 是一个值得深入研究的范本。它由 HKUDS 团队开发,旨在提供一个工业级的、与模型无关的智能体驾驭层。

OpenHarness 的设计完全印证了我们之前讨论的“Harness 即 OS”的观点。它的核心架构由五个关键维度组成:

1. 强化的 Agent Loop

不同于简单的“提示-回复”循环,OpenHarness 实现了流式工具调用循环 (Streaming Tool-Call Cycle)。它支持:

  • 并行工具执行:同时启动多个 Shell 或 Search 任务。
  • 指数退避重试:自动处理 API 速率限制和网络抖动。
  • 消耗追踪:实时计算 Token 成本,防止智能体陷入无限递归导致的账单爆炸。

2. 动态工具箱 (Harness Toolkit)

OpenHarness 内置了 43 个核心工具,涵盖文件操作、Shell 执行、Web 搜索和 MCP(Model Context Protocol)支持。其最独特之处在于:

  • 按需加载 Skill:通过读取 .md 文档,智能体可以即时学会如何操作特定的内部系统。
  • 插件生态:支持钩子(Hooks)和自定义智能体注入。

3. 记忆与上下文管理

它原生支持 CLAUDE.md 发现机制,将项目规范自动注入初始上下文。同时:

  • Auto-Compaction:当长对话接近窗口上限时,自动触发总结。
  • MEMORY.md:将关键决策和进度持久化到磁盘,支持跨 Session 的记忆继承。

4. 治理与权限 (Governance)

这是 OpenHarness 区别于简单的“实验脚本”的关键:

  • 多层级权限:可以配置只读、受限写入或完全控制。
  • 交互式审批:在执行 rm -rf 或危险 Shell 命令前,弹出对话框请求人类确认。

5. 群体协同 (Swarm)

支持 Subagent Spawning。主智能体可以将子任务委派给专门的子智能体(如“文档专家”或“单元测试生成器”),实现高度并发的任务处理。

核心亮点:代码即 Harness (实战演示)

为了感受这种框架的力量,看看我们如何用 OpenHarness 启动一个带审批流的智能体会话:

from openharness import Agent, ToolRegistry, Memory

# 1. 加载工具箱与上下文
tools = ToolRegistry.load(["shell", "file_system", "mcp_playwright"])
memory = Memory.load_from("MEMORY.md")

# 2. 带有严格 Harness 的智能体
agent = Agent(
model="claude-3-5-sonnet",
tools=tools,
memory=memory,
governance={
"require_human_approval": ["shell.exec(rm*)", "shell.exec(docker*)"]
}
)

# 3. 启动流式调用循环
for step in agent.run("重构项目数据库层,并运行测试"):
if step.type == "tool_call":
print(f"[{step.tool}] 执行中...")
elif step.type == "approval_required":
user_input = input(f"智能体试图执行 {step.command},是否允许?(y/n)")
step.approve(user_input == 'y')

与直接调用 OpenAI 或 Anthropic 的 API 相比,OpenHarness 将开发者从繁琐的 Token 截断、工具执行状态机、报错重试逻辑中彻底解放了出来。


第九部分:最佳实践与经验教训

关于智能体 Harness 设计

  1. 寻找最简单的方案,仅在必要时增加复杂度。 Harness 中的每一个组件都编码了你对模型能力的负面假设——而随着模型进步,这些假设往往会失效。

  2. 当新模型发布时,重新审视你的 Harness。 移除那些不再起支撑作用的部件,添加以前无法实现的新能力。Anthropic 团队在从 Opus 4.5 升级到 4.6 时移除了冲刺拆分逻辑,因为新模型能够原生处理长时间的连贯会话。

  3. 独立调优评估者。 让一个独立的评估者保持怀疑态度立场的难度,远低于让生成者对自己代码进行批判。

  4. 当观察到“上下文焦虑”时,优先选择上下文重置而非压缩。 清洁的起点往往比总结后的持续更可靠。

  5. 有趣的 Harness 空间并不会随模型进步而萎缩——它在迁移。 AI 工程师的乐趣在于不断发现新的组合方式。

关于 Harness 平台部署

  1. 生产环境务必使用持久化卷 (-v /persistent/path:/data)。
  2. 多用户环境使用 PostgreSQL 替代 SQLite。
  3. 配置 Webhook 密钥以确保流水线触发的安全性。
  4. 设置分支保护规则,防止直接向 main 分支推送代码。
  5. 使用 REST API 进行编排式管理与集成。

结语

"Harness 工程" 代表了我们思考 AI 辅助软件开发的根本性转变。它不再仅仅是给模型一个提示并寄希望于最好的结果——而是关于设计环境约束反馈循环,从而有系统地引导 AI 智能体走向高质量、生产级的产出。

无论你是在构建能够自主交付全栈应用的多智能体系统,还是在部署自托管的 CI/CD 平台来自动化团队的工作流,其核心准则是通用的:AI 周边的基础设施与 AI 本身同样重要

马很有力,而 Harness 让它变得有用。


参考资料

RAG 核心基建:文本 Chunk 策略全景解析(从固定切片到 VLM 端到端解析)

· 阅读需 31 分钟
Rainy
雨落无声,代码成诗 —— 致力于技术与艺术的极致平衡

"To chunk, or not to chunk — that is the question. But how to chunk is the engineering battle."

在 RAG(检索增强生成)系统中,分块(Chunking)是整个 Pipeline 的地基。检索质量上限由 Embedding 模型决定,下限却由分块质量决定。无论你使用多么强大的 LLM 或 向量数据库,一旦 Chunk 切错了位置、割裂了语义,后续所有优化都是徒劳。

本文将带你由浅入深地走完整条 Chunk 技术发展路线图——从最原始的固定切片,一路升级到 VLM 端到端文档理解。

Apollo Router 由浅入深:从 Federation 到请求生命周期的全链路剖析

· 阅读需 9 分钟
Rainy
雨落无声,代码成诗 —— 致力于技术与艺术的极致平衡

当你的 GraphQL 服务从一个 monolith 发展到十几个甚至几十个微服务时,如何让客户端只面对一个端点、同时让后端团队各自独立迭代?Apollo 的答案是 Federation(联邦架构) 和一个用 Rust 编写的高性能入口——Apollo Router

本文将带你从最基础的概念一路走到生产级配置,完整覆盖 Apollo Router 的请求生命周期(Request Lifecycle)