


















前面三篇我们把"怎么写一个能跑起来的 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 历史机制
Application CRD 的 Spec 设计有两种典型思路:
生产推荐折中方案: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"
)
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)让监控、告警、面板工具通用。
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)
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"。
// 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 复用。
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
}
复用 k8s 原生 DeploymentStatus 字段有三个核心好处:
代价是紧耦合 k8s 版本:k8s 1.36 升级到 1.37 时,DeploymentStatus 加了字段,你的 CRD 也要跟着升级 schema。但这种代价值得付——k8s 升级时 Controller 通常也要重编,CRD 升级是顺带的。
| Workload | 适用场景 | 不适用场景 |
|---|---|---|
| Deployment | 无状态服务(API server、前端) | 需要稳定网络标识的 Pod |
| StatefulSet | 有状态服务(DB、MQ、Zookeeper) | 无状态可水平扩展的 |
| DaemonSet | 节点级服务(日志收集、监控 agent | 需要按副本数控制的 |
| Job | 一次性任务数据迁移、备份 | 需要常驻运行的服务 |
| CronJob | 定时任务报表、清理 | 需要精确到秒的调度 |
当一个 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 资源。
# 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 | 先全杀旧 Pod 再起新 Pod | 开发、测试、兼容性破坏的版本 |
| RollingUpdate | 逐个替换旧 Pod(k8s 默认) | 90% 场景 |
| Blue-Green | 起全套新 Pod,验完切流量 | 零停机升级 + 快速回滚 |
| Canary | 10% 流量跑新版本,逐步扩 | 高风险变更、AB 测试 |
// 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 分钟窗口,发现问题能秒回滚(流量切回蓝,重新升级绿)。
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)变更。
| 依赖机制 | 实现 | 优缺点 |
|---|---|---|
| OwnerReference | App B 的 OwnerReferences 指向 App A | ✅ 自动级联;❌ 不能跨 namespace |
| Annotation Ref | App B 的 annotation 写 App A 的名字 | ✅ 灵活;❌ Operator 自己维护一致性 |
| 独立 Operator | 专门写一个 ApplicationOrchestrator 编排 | ✅ 复杂编排;⚠️ 增加 Operator 复杂度 |
当用户需要"同一应用部署到 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",多环境编排交给上层工具。
// 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"一样简单。
▼ 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
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。