惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
Arctic Wolf
量子位
J
Java Code Geeks
V
Visual Studio Blog
小众软件
小众软件
博客园_首页
V
V2EX
博客园 - 聂微东
T
Tenable Blog
The Cloudflare Blog
P
Privacy International News Feed
A
About on SuperTechFans
Google DeepMind News
Google DeepMind News
IT之家
IT之家
S
Schneier on Security
博客园 - 叶小钗
Forbes - Security
Forbes - Security
D
DataBreaches.Net
The Register - Security
The Register - Security
T
The Blog of Author Tim Ferriss
Recent Commits to openclaw:main
Recent Commits to openclaw:main
S
Security @ Cisco Blogs
罗磊的独立博客
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Last Week in AI
Last Week in AI
AWS News Blog
AWS News Blog
GbyAI
GbyAI
Schneier on Security
Schneier on Security
Stack Overflow Blog
Stack Overflow Blog
腾讯CDC
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
B
Blog
T
Threat Research - Cisco Blogs
N
News and Events Feed by Topic
Y
Y Combinator Blog
F
Fortinet All Blogs
V
Vulnerabilities – Threatpost
H
Help Net Security
Engineering at Meta
Engineering at Meta
H
Hacker News: Front Page
P
Proofpoint News Feed
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Google DeepMind News
Google DeepMind News
aimingoo的专栏
aimingoo的专栏
大猫的无限游戏
大猫的无限游戏
C
Cybersecurity and Infrastructure Security Agency CISA
G
Google Developers Blog
博客园 - 三生石上(FineUI控件)

博客园 - 左扬

Kubernetes 编程 / Operator 专题【左扬精讲】—— RESTMapper:把 Group / Version / Kind / Resource 四元组翻译成 REST 路径的"查字典"大师 Kubernetes 编程 / Operator 专题【左扬精讲】—— Converter 资源版本转换器 Kubernetes 编程 / Operator 专题【左扬精讲】—— OwnerReference / Finalizer / 准入控制:k8s 资源生命周期的三大支柱 Kubernetes 编程 / Operator 专题【左扬精讲】—— controller-runtime 框架内幕:从 Manager 到 Reconcile 的全栈拆解 Kubernetes 编程 / Operator 专题【左扬精讲】—— 生产级 Operator 最佳实践:并发安全、资源清理与高可用设计 Kubernetes 编程 / Operator 专题【左扬精讲】—— application-operator Reconcile 循环源码精讲:从 client-go Informer 到 workqueue 的全链路解剖 Kubernetes 编程 / Operator 专题【左扬精讲】—— 从零搭建一个 application-operator 新项目:脚手架、API 设计与基于原生 DeploymentStatus/ServiceStatus 的状态建模 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:生产级 Controller 实践:并发安全、资源清理与高可用设计 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析: Controller 调试与诊断工具:从日志分析到问题定位 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:DynamicClient 操作 CRD:无需代码生成的动态操作 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:控制器与 APIServer 完整交互流程:从 Watch 到缓存同步 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:错误处理与重试机制:WorkQueue 限速器详解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Leader 选举机制:高可用控制器的必备技能 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Controller 开发模式完整实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:SharedInformerFactory 与等待缓存同步 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:从认证配置到 Deployment 操作 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:版本对应、架构组件与组件关系 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Informer 源码深度解析:从底层原理到实战应用 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Reflector 源码深度解析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:ListWatcher 源码深度解析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:Indexer 与 ThreadSafeStore 核心原理与源码深度剖析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:DeltaFIFO 核心原理与源码深度剖析 Kubernetes 编程 / Operator 专题【左扬精讲】—— Client-go 源代码分析:workqueue 核心原理与实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— runtime.Codec 资源编解码:serializer 与 codec 差异、编解码数据结构、codec 核心调用链路 Kubernetes 编程 / Operator 专题【左扬精讲】—— Scheme 资源注册机制全解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 自定义资源的内部版本与外部版本:从源码看版本定义机制 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 1.36.1 核心 API 数据结构全解 Kubernetes 编程 / Operator 专题【左扬精讲】—— Kubernetes 构建过程 【AIOPS】一文读懂LLM【左扬精讲】:从诞生到普及,解锁大语言模型的核心密码 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Hooks 源码精讲:Hooks 可观测性的无侵入式实现 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Golang 配置解析源码精讲 ——SRE 自定义 Agent 核心技巧 【AIOPS】AI Agent 专题【左扬精讲】核心功能篇:MCP-VictoriaMetrics Golang 并发模型解析 ——SRE 应对高并发采集的调优思路 【AIOPS】AI Agent 专题【左扬精讲】基础架构篇:MCP-VictoriaMetrics Golang 源码整体架构拆解 ——SRE 必懂的核心模块与数据流 OpenTelemetry 开发实战【左扬精讲】—— 云原生可观测体系构建与分布式追踪二次开发 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 7 —— 基于流量预测模型的智能弹性扩缩容 Operator 实战(AIOps 模型训练与智能扩容(下篇)—— 预测式弹性扩缩容 Operator 落地实现) Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 7 —— 基于流量预测模型的智能弹性扩缩容 Operator 实战(AIOps 模型训练与智能扩容(上篇)—— 时序预测模型构建与离线训练) Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 6 —— 基于运维专家知识库的智能故障诊断与排查 Operator 实战 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 5 —— 基于大语言模型(LLM)的实时日志流智能监测 Operator 实现 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 4 —— 基于 Operator 实现大模型私有化部署与管理 Kubernetes 编程 / Operator 专题【左扬精讲】—— Operator 开发实战项目 3(上篇)—— 面向 AI / 算力调度场景:GPU 竞价实例资源池统一调度管理 Operator 开发 Kubernetes编程 / Operator专题【左扬精讲】—— Operator 开发实战项目 2 —— 面向零售 / 电商潮汐流量难题:多云多集群数据中心级全链路弹性伸缩 DataCenter Scaler Operator 从 0 到 1 全链路开发 Kubernetes编程 / Operator专题【左扬精讲】—— 深入理解Kubebuilder注解:为什么Operator开发离不开这些特殊注释 Kubernetes编程 / Operator专题【左扬精讲】—— Operator 开发实战项目1 —— Applicaion Operator(通用应用生命周期管理 Operator 实战) Pod 镜像拉取失败?kubectl edit pods修改镜像地址的底层原理与实操 (该方法仅为临时应急方案,并非长期解决方案) Kubernetes编程/Operator专题精讲—— 理解控制器模式 —— 控制器模式的核心原理与实现逻辑(从原理到实践) 【AIOPS】AI Agent 专题【左扬精讲】模型微调实战:一站式平台 LLaMA-Factory 【AIOPS】AI Agent 专题【左扬精讲】基于 k8s+vLLM+Ray 分布式部署全指南:架构设计、资源调度与性能优化 【AIOPS】AI Agent专题【左扬精讲】非量化版DeepSeek分布式部署全指南:精度保障、显存规划与Ollama/vLLM选型 【AIOPS】AI Agent 专题【左扬精讲】零开发框架实现 ReAct Agent(Go SRE友好)
Kubernetes 编程 / Operator 专题【左扬精讲】—— Application 业务扩展:从单 Deployment 到多 Workload 的复合 Operator 演进
左扬 · 2026-06-15 · via 博客园 - 左扬

Kubernetes 编程 / Operator 专题【左扬精讲】—— Application 业务扩展:从单 Deployment 到多 Workload 的复合 Operator 演进

前面三篇我们把"怎么写一个能跑起来的 Operator"讲完了:Reconcile 循环、controller-runtime 框架、OwnerReference / Finalizer / 准入控制。这一篇要把视角切换到业务层——我们手里的 application-operator 怎么从"创建 1 个 Deployment"演化成"管理 1 个完整应用"。

一个真正可用的应用 Operator,至少要管四类资源:① 无状态工作负载(Deployment);② 有状态工作负载(StatefulSet);③ 网络入口(Service + Ingress + HPA);④ 配置与存储(ConfigMap + Secret + PVC)。这些资源之间存在复杂的依赖和生命周期关系,不能简单堆砌,必须有清晰的设计模式。

这一篇围绕 4 个核心场景展开:① 从单 Deployment 演进到 Application 复合对象;② Spec / Status 的设计哲学与演进;③ 滚动升级与金丝雀发布;④ 多 Application 的依赖编排。每一节都配套可复用的代码模板和架构图。读完后你将能够设计一个能上生产线的 application-operator。

Kubernetes 1.36.1 Application CRD Workload 编排 滚动升级 业务建模

🔓 学习重点提示  — 建议先通读全文,再重点回顾标注内容

★ 重点掌握(必须)
   • Application CRD 的 Spec 设计原则:用户视角 vs 系统视角分离
   • 多 Workload 协作模式:Deployment + StatefulSet + Job + CronJob 何时各用哪个
   • Status 子资源精确映射:DeploymentStatus / ServiceStatus 的字段复用
   • 滚动升级三步法:扩容新版本 → 缩容旧版本 → 灰度发布

☆ 次重点(了解即可)
   • 多 Application 依赖图:Owner vs Ref 的语义差别
   • 应用模板参数化:Helm values 的 Operator 等价物
   • 应用快照与回滚:CR revision 历史机制


目录

  1. 一、Application CRD 设计哲学
  2. 二、Spec 设计:把用户需求翻译成 k8s 资源
  3. 三、Status 子资源:从原生 k8s 字段精确映射
  4. 四、多 Workload 协作模式
  5. 五、滚动升级:从 Recreate 到 Blue-Green
  6. 六、多 Application 依赖与编排
  7. 七、FAQ(20+ 高频问题)

一、Application CRD 设计哲学

1.1 用户视角 vs 系统视角

Application CRD 的 Spec 设计有两种典型思路:

  • 用户视角(High-level API):Spec 直接表达"用户想要什么"——如 image、replicas、port、env,业务友好但灵活度低
  • 系统视角(Low-level passthrough):Spec 直接嵌 k8s 原生结构——如 deploymentTemplate *appsv1.DeploymentSpec,灵活度极高但用户要懂 k8s

生产推荐折中方案:High-level 字段 + k8s 透传字段。常见业务字段(image、port)作为顶层 spec,自定义高级配置作为 rawDeployment 嵌入。这样既保证简单用例的易用性,又给复杂用例留口子:

// api/v1/application_types.go

// ApplicationSpec defines the desired state of Application
type ApplicationSpec struct {
    // === High-level 字段(90% 用户的 90% 用例)===
    Image           string                       `json:"image"`
    Replicas        *int32                       `json:"replicas,omitempty"`
    Port            int32                        `json:"port,omitempty"`
    Env             []corev1.EnvVar              `json:"env,omitempty"`
    Resources       corev1.ResourceRequirements  `json:"resources,omitempty"`

    // === Advanced 字段(10% 用户的 10% 用例)===
    // 透传 k8s 字段,Operator 会 merge 到生成的 Deployment
    DeploymentOverrides *appsv1.DeploymentSpec    `json:"deploymentOverrides,omitempty"`
    ServiceOverrides    *corev1.ServiceSpec       `json:"serviceOverrides,omitempty"`
    Ingress             *networkingv1.IngressSpec  `json:"ingress,omitempty"`

    // === 业务控制字段 ===
    RolloutStrategy RolloutStrategy `json:"rolloutStrategy"`
}

// RolloutStrategy 枚举
type RolloutStrategy string
const (
    RolloutRecreate        RolloutStrategy = "Recreate"
    RolloutRollingUpdate   RolloutStrategy = "RollingUpdate"
    RolloutBlueGreen       RolloutStrategy = "BlueGreen"
    RolloutCanary          RolloutStrategy = "Canary"
)

1.2 Application Status 设计

Status 字段反映"系统实际状态"。application-operator 复用 k8s 原生的 DeploymentStatus / ServiceStatus,避免自己重新发明:

// api/v1/application_types.go

// ApplicationStatus defines the observed state of Application
type ApplicationStatus struct {
    // === 复用 k8s 原生 Status(字段含义与 DeploymentStatus 保持一致)===
    DeploymentStatus appsv1.DeploymentStatus   `json:"deploymentStatus,omitempty"`
    ServiceStatus    corev1.ServiceStatus      `json:"serviceStatus,omitempty"`

    // === 业务聚合状态 ===
    Phase   Phase   `json:"phase"`     // Pending/Running/Failed/Upgrading
    Message string  `json:"message"`   // 人类可读的状态说明
    Reason  string  `json:"reason"`    // 机器可读的状态码

    // === 子资源引用(方便用户 kubectl describe 时看到)===
    DeploymentName string `json:"deploymentName"`
    ServiceName    string `json:"serviceName"`

    // === 时间戳 ===
    LastUpdated metav1.Time `json:"lastUpdated"`
    Conditions  []metav1.Condition `json:"conditions,omitempty"`
}

type Phase string
const (
    PhasePending    Phase = "Pending"
    PhaseCreating   Phase = "Creating"
    PhaseRunning    Phase = "Running"
    PhaseUpgrading  Phase = "Upgrading"
    PhaseDegraded   Phase = "Degraded"
    PhaseFailed     Phase = "Failed"
)

复用 k8s 原生字段的好处:① Operator 用户熟悉 DeploymentStatus 字段含义;② kubectl get application -o yaml 输出与 Deployment 字段一一对应,便于诊断;③ k8s Status 标准化(Phase / Conditions)让监控、告警、面板工具通用。

1.3 完整的数据结构关系图

Application (CRD)
├── metav1.TypeMeta (apiVersion, kind)
├── metav1.ObjectMeta (name, namespace, labels, ownerReferences, finalizers)
├── Spec ──────────────────────────────────── ApplicationSpec
│ ├── Image string
│ ├── Replicas *int32
│ ├── Port int32
│ ├── Env []EnvVar
│ ├── Resources ResourceRequirements
│ ├── DeploymentOverrides *DeploymentSpec ← 透传 k8s 字段
│ ├── ServiceOverrides *ServiceSpec
│ ├── Ingress *IngressSpec
│ └── RolloutStrategy RolloutStrategy
└── Status ────────────────────────────────── ApplicationStatus
├── DeploymentStatus (复用) ── 嵌入 k8s 原生 Status
│ ├── ObservedGeneration
│ ├── Replicas, UpdatedReplicas, ReadyReplicas
│ ├── AvailableReplicas, UnavailableReplicas
│ └── Conditions[]
├── ServiceStatus (复用)
├── Phase Phase (业务聚合状态)
├── Message string (人类可读)
├── DeploymentName string (子资源引用)
└── Conditions[] (k8s 标准化 Conditions)


二、Spec 设计:把用户需求翻译成 k8s 资源

2.1 Builder 模式:把 Spec 翻译成子资源

Builder 模式是 Operator 业务层的核心模式:每个子资源(Deployment、Service、Ingress)有一个 builder 函数,接收 ApplicationSpec 输出 k8s 资源。这样的好处是 ① 业务逻辑可测;② 升级时只改 builder;③ 多个 Reconciler 共享。

// pkg/resources/deployment.go

// BuildDeployment 从 ApplicationSpec 构造 Deployment
func BuildDeployment(app *appv1.Application) *appsv1.Deployment {
    labels := map[string]string{
        "app.kubernetes.io/name":       app.Name,
        "app.kubernetes.io/managed-by": "application-operator",
    }
    replicas := int32(3)
    if app.Spec.Replicas != nil {
        replicas = *app.Spec.Replicas
    }

    dep := &appsv1.Deployment{
        ObjectMeta: metav1.ObjectMeta{
            Name:      app.Name + "-deployment",
            Namespace: app.Namespace,
            Labels:    labels,
        },
        Spec: appsv1.DeploymentSpec{
            Replicas: &replicas,
            Selector: &metav1.LabelSelector{MatchLabels: labels},
            Template: corev1.PodTemplateSpec{
                ObjectMeta: metav1.ObjectMeta{Labels: labels},
                Spec: corev1.PodSpec{
                    Containers: []corev1.Container{{
                        Name:  app.Name,
                        Image: app.Spec.Image,
                        Ports: []corev1.ContainerPort{{ContainerPort: app.Spec.Port}},
                        Env:   app.Spec.Env,
                        Resources: app.Spec.Resources,
                    }},
                },
            },
        },
    }

    // 透传字段:用户写的 overrides 直接覆盖
    if app.Spec.DeploymentOverrides != nil {
        mergeDeploymentSpec(&dep.Spec, app.Spec.DeploymentOverrides)
    }
    return dep
}

Builder 函数 纯函数(无副作用),单元测试容易:testApp.Spec.Image = "nginx:1.25" → BuildDeployment → 断言 dep.Spec.Template.Spec.Containers[0].Image == "nginx:1.25"。

2.2 Service / Ingress Builder 模式

// pkg/resources/service.go

// BuildService 从 ApplicationSpec 构造 Service
func BuildService(app *appv1.Application) *corev1.Service {
    labels := map[string]string{
        "app.kubernetes.io/name":       app.Name,
        "app.kubernetes.io/managed-by": "application-operator",
    }
    return &corev1.Service{
        ObjectMeta: metav1.ObjectMeta{
            Name:      app.Name + "-service",
            Namespace: app.Namespace,
            Labels:    labels,
        },
        Spec: corev1.ServiceSpec{
            Selector: labels,
            Ports: []corev1.ServicePort{{
                Port:       app.Spec.Port,
                TargetPort: intstr.FromInt(int(app.Spec.Port)),
                Protocol:   corev1.ProtocolTCP,
            }},
            Type: corev1.ServiceTypeClusterIP,
        },
    }
}

生产中 Service / Ingress 的 Builder 模式值得套用。一个常见的进阶做法是把 Builder 函数独立成包(pkg/resources),方便单测和后续跨多个 Operator 复用。


三、Status 子资源:从原生 k8s 字段精确映射

3.1 Status 收集器模式

Reconcile 中收集子资源状态、聚合成 ApplicationStatus 是一段固定模式:

// pkg/controller/application_controller.go

func (r *ApplicationReconciler) collectStatus(ctx context.Context, app *appv1.Application) (appv1.ApplicationStatus, error) {
    status := appv1.ApplicationStatus{
        Phase:           appv1.PhasePending,
        DeploymentName:  app.Name + "-deployment",
        ServiceName:     app.Name + "-service",
        LastUpdated:     metav1.NewTime(time.Now()),
    }

    // 1) 收集 Deployment Status
    deploy := &appsv1.Deployment{}
    if err := r.Get(ctx, types.NamespacedName{Name: status.DeploymentName, Namespace: app.Namespace}, deploy); err == nil {
        status.DeploymentStatus = deploy.Status
    } else if !errors.IsNotFound(err) {
        return status, err
    }

    // 2) 收集 Service Status
    svc := &corev1.Service{}
    if err := r.Get(ctx, types.NamespacedName{Name: status.ServiceName, Namespace: app.Namespace}, svc); err == nil {
        status.ServiceStatus = svc.Status
    } else if !errors.IsNotFound(err) {
        return status, err
    }

    // 3) 聚合 Phase
    status.Phase = aggregatePhase(&status.DeploymentStatus)
    status.Message = computeMessage(&status.DeploymentStatus)
    return status, nil
}

// aggregatePhase 把 Deployment Status 转成业务 Phase
func aggregatePhase(d *appsv1.DeploymentStatus) appv1.Phase {
    if d.Replicas == 0 {
        return appv1.PhasePending
    }
    if d.AvailableReplicas == d.Replicas && d.ReadyReplicas == d.Replicas {
        return appv1.PhaseRunning
    }
    if d.UnavailableReplicas > 0 {
        return appv1.PhaseDegraded
    }
    return appv1.PhasePending
}

3.2 为什么复用 k8s 原生 Status 字段

复用 k8s 原生 DeploymentStatus 字段有三个核心好处:

  1. 1用户零学习成本:用过 Deployment 的人立刻懂 AvailableReplicas、Conditions 含义
  2. 2kubectl describe 友好:直接显示 k8s 标准字段,无须自定义解析
  3. 3监控通用:Prometheus 抓取规则、Service Monitor、Alert rule 通用

代价是紧耦合 k8s 版本:k8s 1.36 升级到 1.37 时,DeploymentStatus 加了字段,你的 CRD 也要跟着升级 schema。但这种代价值得付——k8s 升级时 Controller 通常也要重编,CRD 升级是顺带的。


四、多 Workload 协作模式

4.1 何时用哪个 Workload

Workload适用场景不适用场景
Deployment 无状态服务(API server、前端) 需要稳定网络标识的 Pod
StatefulSet 有状态服务(DB、MQ、Zookeeper) 无状态可水平扩展的
DaemonSet 节点级服务(日志收集、监控 agent 需要按副本数控制的
Job 一次性任务数据迁移、备份 需要常驻运行的服务
CronJob 定时任务报表、清理 需要精确到秒的调度

4.2 复合 Workload:Spec.workloads 数组

当一个 Application 包含多种类型 Workload 时,用数组表达:

// api/v1/application_types.go

type ApplicationSpec struct {
    // 简单应用的快捷字段
    Image    string
    Replicas *int32
    Port     int32

    // 复合应用的扩展
    Workloads []WorkloadSpec `json:"workloads,omitempty"`
}

type WorkloadSpec struct {
    // 工作负载类型
    Type WorkloadType `json:"type"`  // Deployment/StatefulSet/Job/CronJob

    // 通用字段
    Name      string                  `json:"name"`     // 工作负载名称(在 Application 内唯一)
    Image     string                  `json:"image"`
    Replicas  *int32                  `json:"replicas,omitempty"`
    Env       []corev1.EnvVar         `json:"env,omitempty"`

    // 类型特定字段
    JobSpec      *JobSpec      `json:"jobSpec,omitempty"`
    StatefulSpec *StatefulSpec `json:"statefulSpec,omitempty"`
}

type JobSpec struct {
    Completions  *int32
    Parallelism  *int32
    BackoffLimit *int32
    RestartPolicy corev1.RestartPolicy
}

type StatefulSpec struct {
    ServiceName       string
    VolumeClaimTemplates []corev1.PersistentVolumeClaim
}

这种设计让一个 CR 能描述"包含 1 个 Deployment + 1 个 StatefulSet + 1 个 CronJob"的复合应用。每个 Workload 有自己的名字(同一 Application 内唯一),Operator 按 name 创建对应的 k8s 资源。

4.3 一个生产 Application YAML 范例

# config/samples/application_v1_application.yaml

apiVersion: application.example.com/v1
kind: Application
metadata:
  name: my-ecommerce-app
  namespace: production
  labels:
    tier: backend
    env: prod
spec:
  # === 简单应用字段 ===
  image: registry.example.com/ecommerce:v1.2.3
  port: 8080
  replicas: 5

  env:
  - name: DB_HOST
    value: my-ecommerce-app-db
  - name: REDIS_URL
    valueFrom:
      secretKeyRef:
        name: my-ecommerce-secrets
        key: redis-url

  resources:
    requests:
      cpu: 500m
      memory: 512Mi
    limits:
      cpu: 2000m
      memory: 2Gi

  # === 滚动升级策略 ===
  rolloutStrategy: RollingUpdate
  rolloutConfig:
    maxSurge: 1
    maxUnavailable: 0

  # === 复合 Workload(数据库 + 报表 Job) ===
  workloads:
  - name: db
    type: StatefulSet
    image: postgres:15
    replicas: 1
    statefulSpec:
      serviceName: my-ecommerce-app-db
      volumeClaimTemplates:
      - metadata:
          name: data
        spec:
          accessModes: [ReadWriteOnce]
          resources:
            requests:
              storage: 100Gi

  - name: daily-report
    type: CronJob
    image: registry.example.com/report:v1
    schedule: "0 2 * * *"
    jobSpec:
      completions: 1
      backoffLimit: 3

  # === Ingress ===
  ingress:
    className: nginx
    rules:
    - host: ecommerce.example.com
      http:
        paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: my-ecommerce-app-service
              port: 8080

五、滚动升级:从 Recreate 到 Blue-Green

5.1 4 种发布策略

策略发布模式适用场景
Recreate 先全杀旧 Pod 再起新 Pod 开发、测试、兼容性破坏的版本
RollingUpdate 逐个替换旧 Pod(k8s 默认) 90% 场景
Blue-Green 起全套新 Pod,验完切流量 零停机升级 + 快速回滚
Canary 10% 流量跑新版本,逐步扩 高风险变更、AB 测试

5.2 Blue-Green 实战

// pkg/rollout/bluegreen.go

// BlueGreen 三步发布法
func (r *ApplicationReconciler) reconcileBlueGreen(ctx context.Context, app *appv1.Application) error {
    // 1) 构造绿色(新版本)Deployment
    green := BuildDeployment(app)
    green.Name = app.Name + "-green"
    green.Spec.Template.Labels["app.kubernetes.io/version"] = "green"

    // 2) 创建绿色 Deployment(如果不存在)
    if err := r.CreateOrUpdate(ctx, green); err != nil {
        return err
    }

    // 3) 等绿色就绪
    if !isReady(ctx, green) {
        return errors.New("green deployment not ready yet")
    }

    // 4) 切流量:把 Service Selector 切到绿色
    svc := BuildService(app)
    svc.Spec.Selector = green.Spec.Template.Labels
    if err := r.Update(ctx, svc); err != nil {
        return err
    }

    // 5) 删蓝色(旧版本)
    blue := &appsv1.Deployment{}
    if err := r.Get(ctx, types.NamespacedName{Name: app.Name + "-blue", Namespace: app.Namespace}, blue); err == nil {
        // 延迟删除,给监控留窗口
        if time.Since(blue.CreationTimestamp.Time) > 5*time.Minute {
            r.Delete(ctx, blue)
        }
    }
    return nil
}

Blue-Green 核心:绿起好 → 流量切绿 → 延迟删蓝。延迟删蓝给监控留 5 分钟窗口,发现问题能秒回滚(流量切回蓝,重新升级绿)。

5.3 Canary 实战

Canary 实现的本质是两套 Deployment 共存,按权重分配流量。k8s 1.18+ 提供了 Ingress 的 canary annotation 简化实现:

// Canary 阶段定义

# 旧版本(稳定)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-stable
spec:
  replicas: 9
  template:
    metadata:
      labels:
        version: stable
---
# 新版本(canary,10% 流量)
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-app-canary
spec:
  replicas: 1
  template:
    metadata:
      labels:
        version: canary
---
# Ingress 按权重路由
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "10"
spec:
  rules:
  - host: my-app.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: my-app-canary
            port:
              number: 8080

canary-weight 从 10 → 30 → 50 → 100,每次切换都要看监控(错误率、延迟、QPS)。生产中通常配合 Prometheus 自动判断:error_rate > 1% 自动回滚。

🌟 实用技巧
application-operator 不应把发布策略写死成"只支持 RollingUpdate"。生产中应让用户通过 spec.rolloutStrategy 选 Recreate / RollingUpdate / BlueGreen / Canary。Operator 把策略翻译成对应的 k8s 资源(Deployment、Ingress、Service)变更。


六、多 Application 依赖与编排

6.1 Application 间依赖的 3 种实现

依赖机制实现优缺点
OwnerReference App B 的 OwnerReferences 指向 App A ✅ 自动级联;❌ 不能跨 namespace
Annotation Ref App B 的 annotation 写 App A 的名字 ✅ 灵活;❌ Operator 自己维护一致性
独立 Operator 专门写一个 ApplicationOrchestrator 编排 ✅ 复杂编排;⚠️ 增加 Operator 复杂度

6.2 ApplicationSet:多环境多集群部署

当用户需要"同一应用部署到 dev/staging/prod 三个环境,每个环境 5 副本"时,单 Application 表达不了。k8s 1.27+ 提供了 ApplicationSet(更准确是 Kustomize / ArgoCD ApplicationSet)的语义:

// 推荐的实现:用 Kustomize 或 Helm 模板生成多环境 Application

$ kubectl kustomize config/overlays/production | kubectl apply -f -
application.application.example.com/my-ecommerce-app configured
$ for env in dev staging production; do
    kubectl kustomize config/overlays/$env | kubectl apply -f -
done

也可以用 application-operator 自己写一个 ApplicationSet CRD,但 80% 场景下 Kustomize 模板更轻量。Operator 的角色是"管理单个 Application",多环境编排交给上层工具。

6.3 应用版本与回滚

// pkg/rollout/revision.go

// 记录 Application 的每次变更(Revision)
type ApplicationRevision struct {
    metav1.TypeMeta
    metav1.ObjectMeta
    Spec   ApplicationSpec  // 当时的 Spec
    Status ApplicationStatus
    Revision int64  // 自增 ID
    CreatedAt metav1.Time
}

// 每次 Application Spec 变化时创建 Revision
func (r *ApplicationReconciler) recordRevision(ctx context.Context, app *appv1.Application) error {
    rev := &ApplicationRevision{
        ObjectMeta: metav1.ObjectMeta{
            Name: fmt.Sprintf("%s-%d", app.Name, time.Now().Unix()),
            Namespace: app.Namespace,
            OwnerReferences: []metav1.OwnerReference{{
                APIVersion: app.APIVersion,
                Kind: app.Kind,
                Name: app.Name,
                UID: app.UID,
            }},
        },
        Spec: app.Spec,
        Revision: r.revisionCounter.Add(1),
        CreatedAt: metav1.NewTime(time.Now()),
    }
    return r.Create(ctx, rev)
}

// 用户回滚:把 CR Spec 改成某个历史版本
$ kubectl get applicationrev -l app=my-ecommerce-app
NAME                    REVISION   CREATED
my-ecommerce-app-1001   1          2026-06-01T10:00
my-ecommerce-app-1002   2          2026-06-10T15:00
$ kubectl patch application my-ecommerce-app --type=merge -p '
spec: { ... }  # 复制 revision 1001 的 spec
'

这种 Revision 模式在生产中非常重要——"出问题回滚到上一版"是 OPs 的高频需求。把 ApplicationSpec 每次变化都打成快照存到独立的 ApplicationRevision CR,让回滚变成"kubectl apply 旧 spec"一样简单。


七、FAQ(20+ 高频问题)

▼ Q1: 我应该用 Application 这个名字吗?会不会和 k8s 未来原生 Application 冲突?

A: k8s 至今没有原生的 Application 资源(kubectl api-resources 中没有)。使用 Application + 自己的 group(如 application.example.com)是常见做法。生产中如果担心未来冲突,名字可以叫 AppDeployment 或 Workload,功能不变。


▼ Q2: Spec 设计时,env 这种应该 inline 还是引用 ConfigMap?

A: 关键区分"值"和"引用"。简单业务字段(image、port、replicas)放在 Application.Spec 里直接填;敏感信息(密码、token)必须 用 envFrom.configMapRef / secretKeyRef 引用,不进 Spec。


▼ Q3: 一个 Application 创建 10 个子资源,会不会 k8s 性能有问题?

A: 不会。k8s 1.36 单 namespace 支持 10万+ 对象,一个 Application 的 10 个子资源是小 case。Operator 唯一要注意的是 Reconcile 限速,避免 CR 数量上来后 APIServer 被打爆。


▼ Q4: 为什么我 spec.image 改了,Status 不立刻变?

A: Deployment 滚动升级有时间窗(默认 1 个 Pod 等到 ready 再起下一个)。在 100 副本 + 慢启动场景,升级完可能需要 5-10 分钟 Status 才全 Available。生产中可以加 ProgressDeadlineSeconds 控制最大等待时间。


▼ Q5: 复用的 DeploymentStatus 字段在 k8s 升级时会变吗?

A: 会,但兼容性很强。k8s 1.36 → 1.37 升级时,DeploymentStatus 可能加 1-2 个字段(alpha),老字段一般不会变。CRD schema 用 preserveUnknownFields: true 可让老 CR 兼容新字段。


▼ Q6: 我用 ConfigMap 存配置,每次改 ConfigMap Pod 不会自动 reload 怎么办?

A: 三种方案:① ConfigMap 用 hash 后缀(configmap-abc123)+ Deployment 引用 hash,触发 Pod 重启;② Sidecar 容器 watch ConfigMap,检测到变化发 SIGHUP;③ k8s 1.28+ 用 MutableSharedIndexInformer 监听 ConfigMap,应用 reload。


▼ Q7: Application 怎么和 Helm chart 配合?

A: 三种方案:① Application Operator 内部用 helm.sh/helm/v3/pkg/action SDK 渲染 chart;② Operator 只管 Workload,chart 由用户用 Helm 部署、Operator 监听其状态;③ 用 helm-operator(Operator SDK 子项目)专门做 Helm 集成。


▼ Q8: Spec.replicas=0 和 Paused 有什么区别?

A: Replicas=0 把 Pod 数量调到 0(资源释放),Deployment.Spec.Paused=true 不调度新 Pod 但保留旧 Pod(资源还在)。生产中 Paused 适合"保留环境暂时不消费流量",Replicas=0 适合"停机省资源"。


▼ Q9: Application 怎么和 k8s 1.36 的 DRA 配合?

A: k8s 1.36 GA 的 Dynamic Resource Allocation(DRA)允许 Pod 申请 GPU、FPGA 等扩展资源。Application Spec 加 resourceClaims 字段透传到 Pod Spec 即可。Operator 不需要做特殊处理。


▼ Q10: Operator 创建的子资源能不能被用户手动改?

A: 技术上可以。生产经验:让 Operator 加 metadata.annotations["app.kubernetes.io/managed-by"]: "application-operator",但不强制用户不能改。k8s 设计是"用户永远有最终决定权",Operator 是"持续对账"不是"独裁"。


▼ Q11: Application 包含 StatefulSet,PVC 怎么命名?

A: StatefulSet 自身有 PVC 模板(volumeClaimTemplates),命名格式是 <volumeClaimTemplate.name>-<statefulset.name>-<ordinal>,如 data-my-app-db-0。Operator 把 workloads[].statefulSpec.volumeClaimTemplates 直接传给 StatefulSet 即可。


▼ Q12: 我怎么在 Reconcile 中区分 CREATE 和 UPDATE?

A: 区分不了。Reconcile 是"对账"语义,看到当前状态去和期望状态对账。区分 CREATE/UPDATE 是 Webhook 的工作(看 request.operation)。生产中如果要在第一次创建时做特殊处理,可以用 annotations["app.kubernetes.io/created-at"] 字段。


▼ Q13: Operator 重启会丢失对 Application 的状态收集吗?

A: 不会。Operator 重启后 Informer 会重新 List 所有 Application,下一次 Reconcile 会重新收集 Status。状态信息全在 APIServer / etcd 里,Operator 无状态。


▼ Q14: spec.image 改了后多久会触发滚动升级?

A: 取决于:① Watch 事件从 APIServer 到 Operator 内存:< 1 秒;② Reconcile 触发到 BuildDeployment 完成:< 100ms;③ Deployment 滚动升级到全部新 Pod ready:默认策略下 5-30 秒(受 maxSurge / maxUnavailable 影响)。整体从改 spec 到升级完成通常 10-60 秒。


▼ Q15: HPA 怎么和 Application 配合?

A: Application Spec 加 hpa: { minReplicas, maxReplicas, metrics }。Operator 创建 HPA 资源,targetRef 指向 Deployment。注意:HPA 和 Application.Spec.Replicas 是冲突的——HPA 启动后它会改 Deployment.Replicas,Application.Spec.Replicas 失效。生产中通常让用户用 HPA 时不填 Replicas。


▼ Q16: 我能禁止 Application 修改某些字段吗?

A: 用 CEL 校验:rule: "!has(oldSelf.spec.image) || self.spec.image == oldSelf.spec.image"。或者在 Validating Webhook 中检查 oldObject.spec.image == object.spec.image。生产中通常immutable 字段是 image、storage class、access mode 等。


▼ Q17: Application 怎么对接外部数据库?

A: 两种思路:① 外部 RDS——Application 不管 DB,DB_HOST 写 RDS 地址;② 内嵌 StatefulSet——Operator 创建 PostgreSQL StatefulSet + Service,App 容器连 Service 名。生产中 ① 占多数,② 适合测试环境。


▼ Q18: 为什么我创建 Application 后子资源没有立即出现?

A: Reconcile 是异步的。从 kubectl apply 到第一个子资源出现,通常 1-3 秒。如果超过 30 秒,① 看 Operator 日志有没有报错;② 看 Application Status.Phase 是不是 Failed;③ 看 OwnerReferences 是否设置正确(不设 OwnerReference 子资源会被孤立)。


▼ Q19: Application 内部需要多个 Service(如 frontend/backend/db)怎么处理?

A: Spec 加 services: []ServiceSpec 数组,每个 Service 有自己的 name 和 selector。生产中常见 3 个:app-frontend(对外,LoadBalancer)+ app-backend(ClusterIP)+ app-db(Headless,StatefulSet 用)。


▼ Q20: Application 怎么配置 NetworkPolicy?

A: Spec 加 networkPolicy: *networkingv1.NetworkPolicySpec,Operator 创建 NetworkPolicy 资源。注意:NetworkPolicy 是命名空间级生效,需要 CNI 插件支持(Calico、Cilium 等)。生产中通常用 CiliumNetworkPolicy 替代,功能更强。


▼ Q21: Application 怎么和 Service Mesh 集成?

A: Istio/Linkerd 等 Service Mesh 通过 Sidecar 注入 + DestinationRule + VirtualService 工作。Application Spec 加 mesh: { injectSidecar, trafficPolicy },Operator 在 Pod Spec 加 sidecar.istio.io/inject: "true" annotation 触发注入。

Kubernetes 编程 / Operator 专题【左扬精讲】—— Application 业务扩展 · 基于 k8s 1.36.1 + application-operator