LabHub
学习 学习路径 课程

CNPE — 云原生平台工程师

请求成功了,为什么应用还没就绪?

在 LabHub 中继续学习

目标

在真实 Crossplane 2.4 中创建命名空间级 App,并验证错误的就绪条件、团队权限、变更收敛和删除延迟。 不要只凭声明或单个状态标志推断成功;应同时检查真实 Pod、HTTP 响应与所有者 UID。

为什么重要

自助服务并不只是自动化一个创建按钮。它必须限制请求输入与权限、观测生成的应用,并负责变更和删除。 不能把同名新对象或其他团队的响应混作本次请求成功的证据。 实验使用个人 VM 中运行的 k3s、Crossplane、函数和真实容器,不是 KWOK 或伪造 Ready 的 Pod。 安装可能需要数分钟。预计实验时间为 55 分钟;若需要更多时间,请在到期前延长。 会话结束时 VM 与文件会被回收。请先下载所需记录,也不要把这些操作应用到 VM 外的生产集群。

已准备内容与工具

act 会应用该步骤的文件或执行指定实验;observe 执行一次查询。 capture 不会修正当前配置,而是最多观测 75 秒,再保存指定 JSON。 grade 不修改文件或资源。如果状态仍在准备中,不要重装,只需重新查询同一执行。 已经验证的观测文件即使对应状态在后续消失也会保留。不要手工把文件改成成功 JSON。

步骤

  1. 在 /root/cnpe-api/initial.json 中写入 apiVersion=platform.labhub.local/v1alpha1、kind=App、metadata.name=parcel、metadata.namespace=team-a、spec.replicas=1、spec.channel=stable。act 1 会用 developer 账号应用请求。运行 capture 1 保存 accepted.json,同时确认请求为 Synced=True、Ready=False,并检查真实 HTTP stable 响应。
  2. 把 /opt/fixtures/cnpe-crossplane-composition.json 复制到 /root/cnpe-api/composition.json。只把 spec.pipeline[0].input.resources[0].readinessChecks[0].matchCondition.type 中的 Ready 改成 Available,保留其余模板、选择器、镜像与权限。运行 act 2 应用,再用 capture 2 把真实就绪完成记录到 ready.json。
  3. 查询 team-a 的 developer 是否能够创建本团队 App,但不能创建其他团队 App,也不能直接创建 Deployment。act 3 会实际测试两次权限拒绝和 replicas=4 的模式拒绝。运行 capture 3 保存 boundaries.json。不要把管理员成功或失败的权限查询当作拒绝证据。
  4. 在 /root/cnpe-api/update.json 中为同一个 App、名称和团队写入 replicas=2、channel=preview 请求。运行 act 4 应用,再用 capture 4 保存 updated.json。不要创建新请求,应保持相同 UID,并确认当前 generation、更新副本数、可用数、Pod 所有权关系与真实 preview 响应。
  5. 运行 act 5,只把下层 Deployment 改成 3 个副本。辅助工具会保存真实 patch 响应中的副本数、generation 与 UID,App 请求仍保持为 2。运行 capture 5,在 reconciled.json 中保存它在更晚 generation 恢复为 2 的证据。不能只看到最终数字 2 就认定实验发生过。
  6. 运行 act 6,加入教学 finalizer labhub.io/handoff-hold,并请求删除 parcel。用 capture 6 保存 deleting.json。此时存在 deletionTimestamp,但仍能查询到相同 UID。不要把该标志解释成所有下层资源一定仍然存活。
  7. 运行 act 7,只移除教学保留标志,不要强制删除其他 finalizer。运行 capture 7 保存 absent.json,确认 App、Deployment、ReplicaSet、Service 与 Pod 列表查询全部成功,而且目标已经消失。team-b/sentinel 必须保持相同 UID 与真实 HTTP 正常状态。
  8. 在 /root/cnpe-api/diagnose.py 中编写 diagnose(e),按下方诊断契约返回一个字符串。必填字段缺失、使用数字或字符串代替 bool、观测失败、状态矛盾都应返回 unknown。本步骤是独立代码任务,不会重建之前的资源。

第 8 步诊断契约

输入 e 必须同时包含 observed、accepted、synced、ready、deleting、absent 六个字段,且值必须是真实 bool。 该函数接收已经对照完成的当前状态摘要。accepted 表示当前请求对象存在。

  1. 类型或必填字段错误,或 observed=False 时,返回 unknown。
  2. absent=True 时,只有 accepted、synced、ready、deleting 全为 False 才返回 absent,否则返回 unknown。
  3. synced=True 但 accepted=False,或 ready=True 但 synced=False 时,返回 unknown。
  4. 不存在上述矛盾且 deleting=True 时,accepted=True 则返回 deleting,否则返回 unknown。
  5. 其余情况按顺序判断:ready=True 返回 ready;其次 synced=True 返回 synced;其次 accepted=True 返回 accepted;全部为 False 则返回 not_accepted。

absent 只表示通过正常查询确认对象不存在,并不代表过去请求已经成功回收。回收行为在第 7 步单独确认。

参考

请对照 App→Deployment→ReplicaSet→Pod 的所有权关系与当前响应 Pod,拒绝只因名称相同而关联的其他对象记录。 观测文件用于学习同一次实验执行的历史状态,不是密码学远程证明或防作弊装置。 第 2 步之后的评分会检查当前 Composition;第 3 步还会检查当前 XRD 与权限;第 4、5 步在删除前也会检查当前应用。 删除后必须具备第 7 步的真实回收证据,才能重新检查历史记录。步骤准备不会覆盖现有文件或后续进度,只会补齐必要的前置步骤。第 8 步不会回滚集群状态。 一个教学 finalizer 并不保证所有下层资源都得到保留。命名空间权限隔离也不能证明完整的网络策略与配额隔离。 官方文档:Composition · Finalizers

创建请求并观测就绪失败

在 /root/cnpe-api/initial.json 中写入 apiVersion=platform.labhub.local/v1alpha1、kind=App、metadata.name=parcel、metadata.namespace=team-a、spec.replicas=1、spec.channel=stable。act 1 会用 developer 账号应用请求。运行 capture 1 保存 accepted.json,同时确认请求为 Synced=True、Ready=False,并检查真实 HTTP stable 响应。

请求被接受并不表示执行完成。请分别查看 App 条件与对应 Pod 的 HTTP 响应。

恢复适合应用的就绪条件

把 /opt/fixtures/cnpe-crossplane-composition.json 复制到 /root/cnpe-api/composition.json。只把 spec.pipeline[0].input.resources[0].readinessChecks[0].matchCondition.type 中的 Ready 改成 Available,保留其余模板、选择器、镜像与权限。运行 act 2 应用,再用 capture 2 把真实就绪完成记录到 ready.json。

查询 Deployment 实际提供的条件名称。把 readiness 检查改成 None 并不算恢复。

实际测试受限账号的拒绝行为

查询 team-a 的 developer 是否能够创建本团队 App,但不能创建其他团队 App,也不能直接创建 Deployment。act 3 会实际测试两次权限拒绝和 replicas=4 的模式拒绝。运行 capture 3 保存 boundaries.json。不要把管理员成功或失败的权限查询当作拒绝证据。

在 kubectl auth can-i 中使用 --as=system:serviceaccount:team-a:developer。遭到拒绝后,还应通过管理员列表确认对象没有创建。

把同一请求切换到新 channel

在 /root/cnpe-api/update.json 中为同一个 App、名称和团队写入 replicas=2、channel=preview 请求。运行 act 4 应用,再用 capture 4 保存 updated.json。不要创建新请求,应保持相同 UID,并确认当前 generation、更新副本数、可用数、Pod 所有权关系与真实 preview 响应。

同时查看 metadata.generation 与 status.observedGeneration。正在终止的旧 Pod 可能暂时保留。

理解下层资源变更为何会被撤销

运行 act 5,只把下层 Deployment 改成 3 个副本。辅助工具会保存真实 patch 响应中的副本数、generation 与 UID,App 请求仍保持为 2。运行 capture 5,在 reconciled.json 中保存它在更晚 generation 恢复为 2 的证据。不能只看到最终数字 2 就认定实验发生过。

希望持续存在的变更来源是 App。手工修改下层资源可能在下一次收敛时被覆盖。

区分删除请求与真实不存在

运行 act 6,加入教学 finalizer labhub.io/handoff-hold,并请求删除 parcel。用 capture 6 保存 deleting.json。此时存在 deletionTimestamp,但仍能查询到相同 UID。不要把该标志解释成所有下层资源一定仍然存活。

delete --wait=false 成功只表示请求成功。请直接查询 finalizer 与 deletionTimestamp。

确认只回收了自己的资源

运行 act 7,只移除教学保留标志,不要强制删除其他 finalizer。运行 capture 7 保存 absent.json,确认 App、Deployment、ReplicaSet、Service 与 Pod 列表查询全部成功,而且目标已经消失。team-b/sentinel 必须保持相同 UID 与真实 HTTP 正常状态。

查询失败与空列表不同。为了删除自己的资源而连其他团队也删除,同样是失败答案。

保留未知状态的诊断器

在 /root/cnpe-api/diagnose.py 中编写 diagnose(e),按下方诊断契约返回一个字符串。必填字段缺失、使用数字或字符串代替 bool、观测失败、状态矛盾都应返回 unknown。本步骤是独立代码任务,不会重建之前的资源。

先检查类型、必填字段和可观测性,排除矛盾后再判定删除与就绪状态。