deploy-app

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Deploy App Workflow

部署应用工作流

End-to-end orchestration for deploying applications to the Kubernetes homelab with full monitoring integration.
用于将应用部署到Kubernetes家庭实验室的端到端编排能力,自带完整的监控集成。

Workflow Overview

工作流概览

┌─────────────────────────────────────────────────────────────────────┐
│                      /deploy-app Workflow                           │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  1. RESEARCH                                                        │
│     ├─ Invoke kubesearch skill for real-world patterns              │
│     ├─ Check if native Helm chart exists (helm search hub)          │
│     ├─ Determine: native chart vs app-template                      │
│     └─ AskUserQuestion: Present findings, confirm approach          │
│                                                                      │
│  2. SETUP                                                           │
│     └─ task wt:new -- deploy-<app-name>                             │
│        (Creates isolated worktree + branch)                         │
│                                                                      │
│  3. CONFIGURE (in worktree)                                         │
│     ├─ kubernetes/platform/versions.env (add version)               │
│     ├─ kubernetes/platform/namespaces.yaml (add namespace)          │
│     ├─ kubernetes/platform/helm-charts.yaml (add input)             │
│     ├─ kubernetes/platform/charts/<app>.yaml (create values)        │
│     ├─ kubernetes/platform/kustomization.yaml (register)            │
│     ├─ .github/renovate.json5 (add manager)                         │
│     └─ kubernetes/platform/config/<app>/ (optional extras)          │
│        ├─ route.yaml (HTTPRoute if exposed)                         │
│        ├─ canary.yaml (health checks)                               │
│        ├─ prometheus-rules.yaml (custom alerts)                     │
│        └─ dashboard.yaml (Grafana ConfigMap)                        │
│                                                                      │
│  4. VALIDATE                                                        │
│     ├─ task k8s:validate                                            │
│     └─ task renovate:validate                                       │
│                                                                      │
│  5. TEST ON DEV (bypass Flux)                                       │
│     ├─ helm install directly to dev cluster                         │
│     ├─ Wait for pods ready (kubectl wait)                           │
│     ├─ Verify ServiceMonitor discovered (Prometheus API)            │
│     ├─ Verify no new alerts firing                                  │
│     ├─ Verify canary passing (if created)                           │
│     └─ AskUserQuestion: Report status, confirm proceed              │
│                                                                      │
│  6. CLEANUP & PR                                                    │
│     ├─ helm uninstall from dev                                      │
│     ├─ git commit (conventional commit format)                      │
│     ├─ git push + gh pr create                                      │
│     └─ Report PR URL to user                                        │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────┐
│                      /deploy-app Workflow                           │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  1. RESEARCH                                                        │
│     ├─ Invoke kubesearch skill for real-world patterns              │
│     ├─ Check if native Helm chart exists (helm search hub)          │
│     ├─ Determine: native chart vs app-template                      │
│     └─ AskUserQuestion: Present findings, confirm approach          │
│                                                                      │
│  2. SETUP                                                           │
│     └─ task wt:new -- deploy-<app-name>                             │
│        (Creates isolated worktree + branch)                         │
│                                                                      │
│  3. CONFIGURE (in worktree)                                         │
│     ├─ kubernetes/platform/versions.env (add version)               │
│     ├─ kubernetes/platform/namespaces.yaml (add namespace)          │
│     ├─ kubernetes/platform/helm-charts.yaml (add input)             │
│     ├─ kubernetes/platform/charts/<app>.yaml (create values)        │
│     ├─ kubernetes/platform/kustomization.yaml (register)            │
│     ├─ .github/renovate.json5 (add manager)                         │
│     └─ kubernetes/platform/config/<app>/ (optional extras)          │
│        ├─ route.yaml (HTTPRoute if exposed)                         │
│        ├─ canary.yaml (health checks)                               │
│        ├─ prometheus-rules.yaml (custom alerts)                     │
│        └─ dashboard.yaml (Grafana ConfigMap)                        │
│                                                                      │
│  4. VALIDATE                                                        │
│     ├─ task k8s:validate                                            │
│     └─ task renovate:validate                                       │
│                                                                      │
│  5. TEST ON DEV (bypass Flux)                                       │
│     ├─ helm install directly to dev cluster                         │
│     ├─ Wait for pods ready (kubectl wait)                           │
│     ├─ Verify ServiceMonitor discovered (Prometheus API)            │
│     ├─ Verify no new alerts firing                                  │
│     ├─ Verify canary passing (if created)                           │
│     └─ AskUserQuestion: Report status, confirm proceed              │
│                                                                      │
│  6. CLEANUP & PR                                                    │
│     ├─ helm uninstall from dev                                      │
│     ├─ git commit (conventional commit format)                      │
│     ├─ git push + gh pr create                                      │
│     └─ Report PR URL to user                                        │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

Phase 1: Research

第一阶段:调研

1.1 Search Kubesearch for Real-World Examples

1.1 在Kubesearch中搜索实际落地示例

Invoke the kubesearch skill to find how other homelabs configure this chart:
/kubesearch <chart-name>
This provides:
  • Common configuration patterns
  • Values.yaml examples from production homelabs
  • Gotchas and best practices
调用kubesearch skill查找其他家庭实验室配置该Chart的方案:
/kubesearch <chart-name>
返回结果包含:
  • 通用配置模式
  • 生产级家庭实验室的values.yaml示例
  • 注意事项和最佳实践

1.2 Check for Native Helm Chart

1.2 检查是否存在原生Helm Chart

bash
helm search hub <app-name> --max-col-width=100
Decision matrix:
ScenarioApproach
Official/community chart existsUse native Helm chart
Only container image availableUse app-template
Chart is unmaintained (>1 year)Consider app-template
User preference for app-templateUse app-template
bash
helm search hub <app-name> --max-col-width=100
决策矩阵:
场景方案
存在官方/社区维护的Chart使用原生Helm Chart
仅提供容器镜像使用app-template
Chart已停止维护(超过1年)考虑使用app-template
用户偏好使用app-template使用app-template

1.3 User Confirmation

1.3 用户确认

Use AskUserQuestion to present findings and confirm:
  • Chart selection (native vs app-template)
  • Exposure type: internal, external, or none
  • Namespace selection (new or existing)
  • Persistence requirements

使用AskUserQuestion呈现调研结果并确认以下信息:
  • Chart选择(原生vs app-template)
  • 暴露类型:内网、公网或不暴露
  • 命名空间选择(新建或复用现有)
  • 持久化存储需求

Phase 2: Setup

第二阶段:环境准备

2.1 Create Worktree

2.1 创建工作树

All deployment work happens in an isolated worktree:
bash
task wt:new -- deploy-<app-name>
This creates:
  • Branch:
    deploy-<app-name>
  • Worktree:
    ../homelab-deploy-<app-name>/
所有部署工作都在隔离的工作树中完成:
bash
task wt:new -- deploy-<app-name>
该命令会创建:
  • 分支:
    deploy-<app-name>
  • 工作树:
    ../homelab-deploy-<app-name>/

2.2 Change to Worktree

2.2 切换到工作树目录

bash
cd ../homelab-deploy-<app-name>
All subsequent file operations happen in the worktree.

bash
cd ../homelab-deploy-<app-name>
后续所有文件操作都在该工作树中完成。

Phase 3: Configure

第三阶段:配置

3.1 Add Version to versions.env

3.1 在versions.env中添加版本号

Add a version entry with a Renovate annotation. For annotation syntax and datasource selection, see the versions-renovate skill.
bash
undefined
添加带Renovate注解的版本条目。注解语法和数据源选择可参考versions-renovate skill
bash
undefined

kubernetes/platform/versions.env

kubernetes/platform/versions.env

<APP>_VERSION="x.y.z"
undefined
<APP>_VERSION="x.y.z"
undefined

3.2 Add Namespace to namespaces.yaml

3.2 在namespaces.yaml中添加命名空间

Add to
kubernetes/platform/namespaces.yaml
inputs array:
yaml
- name: <namespace>
  dataplane: ambient
  security: baseline                    # Choose: restricted, baseline, privileged
  networkPolicy: false                  # Or object with profile/enforcement
PodSecurity Level Selection:
LevelUse WhenSecurity Context Required
restricted
Standard controllers, databases, simple appsFull restricted context on all containers
baseline
Apps needing elevated capabilities (e.g.,
NET_BIND_SERVICE
)
Moderate
privileged
Host access, BPF, device accessNone
If
security: restricted
: You MUST set full security context in chart values (see step 3.4a below).
Network Policy Profile Selection:
ProfileUse When
isolated
Batch jobs, workers with no inbound traffic
internal
Internal dashboards/tools (internal gateway only)
internal-egress
Internal apps that call external APIs
standard
Public-facing web apps (both gateways + HTTPS egress)
Optional Access Labels (add if app needs these):
yaml
    access.network-policy.homelab/postgres: "true"    # Database access
    access.network-policy.homelab/garage-s3: "true"   # S3 storage access
    access.network-policy.homelab/kube-api: "true"    # Kubernetes API access
For PostgreSQL provisioning patterns, see the cnpg-database skill.
添加到
kubernetes/platform/namespaces.yaml
的inputs数组中:
yaml
- name: <namespace>
  dataplane: ambient
  security: baseline                    # 可选值:restricted, baseline, privileged
  networkPolicy: false                  # 或者包含profile/enforcement的对象
PodSecurity等级选择:
等级适用场景所需安全上下文
restricted
标准控制器、数据库、简单应用所有容器都需要配置完整的受限上下文
baseline
需要提升权限的应用(例如
NET_BIND_SERVICE
中等安全要求
privileged
需要主机访问、BPF、设备访问的应用
如果配置
security: restricted
:必须在Chart values中配置完整的安全上下文(参考下方步骤3.4a)。
网络策略配置文件选择:
配置文件适用场景
isolated
批量任务、无入站流量的工作节点
internal
内部仪表盘/工具(仅允许内网网关访问)
internal-egress
需要调用外部API的内部应用
standard
对外提供服务的Web应用(支持双网关+HTTPS出站)
可选访问标签(如果应用需要可添加):
yaml
    access.network-policy.homelab/postgres: "true"    # 数据库访问权限
    access.network-policy.homelab/garage-s3: "true"   # S3存储访问权限
    access.network-policy.homelab/kube-api: "true"    # Kubernetes API访问权限
PostgreSQL配置模式可参考cnpg-database skill

3.3 Add to helm-charts.yaml

3.3 添加到helm-charts.yaml

Add to
kubernetes/platform/helm-charts.yaml
inputs array:
yaml
- name: "<app-name>"
  namespace: "<namespace>"
  chart:
    name: "<chart-name>"
    version: "${<APP>_VERSION}"
    url: "https://charts.example.com"  # or oci://registry.io/path
  dependsOn: [cilium]  # Adjust based on dependencies
For OCI registries:
yaml
    url: "oci://ghcr.io/org/helm"
添加到
kubernetes/platform/helm-charts.yaml
的inputs数组中:
yaml
- name: "<app-name>"
  namespace: "<namespace>"
  chart:
    name: "<chart-name>"
    version: "${<APP>_VERSION}"
    url: "https://charts.example.com"  # 或 oci://registry.io/path
  dependsOn: [cilium]  # 根据依赖调整
OCI镜像源配置示例:
yaml
    url: "oci://ghcr.io/org/helm"

3.4 Create Values File

3.4 创建Values文件

Create
kubernetes/platform/charts/<app-name>.yaml
:
yaml
undefined
创建
kubernetes/platform/charts/<app-name>.yaml
yaml
undefined

yaml-language-server: $schema=<schema-url-if-available>

yaml-language-server: $schema=<schema-url-if-available>



Helm values for <app-name>

Helm values for <app-name>

Based on kubesearch research and best practices

Based on kubesearch research and best practices

Enable monitoring

Enable monitoring

serviceMonitor: enabled: true
serviceMonitor: enabled: true

Use internal domain for ingress

Use internal domain for ingress

ingress: enabled: true hosts: - host: <app-name>.${internal_domain}

See [references/file-templates.md](references/file-templates.md) for complete templates.
ingress: enabled: true hosts: - host: <app-name>.${internal_domain}

完整模板可参考[references/file-templates.md](references/file-templates.md)。

3.4a Add Security Context for Restricted Namespaces

3.4a 为受限命名空间添加安全上下文

If the target namespace uses
security: restricted
, add security context to the chart values. Check the container image's default user first -- if it runs as root, set
runAsUser: 65534
.
yaml
undefined
如果目标命名空间使用
security: restricted
,需要在Chart values中添加安全上下文。先检查容器镜像的默认用户,如果默认以root运行,设置
runAsUser: 65534
yaml
undefined

Pod-level (key varies by chart: podSecurityContext, securityContext, pod.securityContext)

Pod级(不同Chart的key可能不同:podSecurityContext, securityContext, pod.securityContext)

podSecurityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault
podSecurityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault

Container-level (every container and init container)

容器级(所有容器和初始化容器都需要配置)

securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] readOnlyRootFilesystem: true runAsNonRoot: true seccompProfile: type: RuntimeDefault

**Restricted namespaces**: cert-manager, external-secrets, system, database, kromgo.

**Validation gap**: `task k8s:validate` does NOT catch PodSecurity violations -- only server-side dry-run or actual deployment reveals them. Always verify security context manually for restricted namespaces.
securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] readOnlyRootFilesystem: true runAsNonRoot: true seccompProfile: type: RuntimeDefault

**受限命名空间列表**:cert-manager, external-secrets, system, database, kromgo。

**校验注意事项**:`task k8s:validate`不会捕获PodSecurity违规,只有服务端 dry-run 或实际部署时才会暴露问题。请始终为受限命名空间手动校验安全上下文配置。

3.5 Register in kustomization.yaml

3.5 在kustomization.yaml中注册

Add to
kubernetes/platform/kustomization.yaml
configMapGenerator:
yaml
configMapGenerator:
  - name: platform-values
    files:
      # ... existing
      - charts/<app-name>.yaml
添加到
kubernetes/platform/kustomization.yaml
的configMapGenerator中:
yaml
configMapGenerator:
  - name: platform-values
    files:
      # ... 现有配置
      - charts/<app-name>.yaml

3.6 Configure Renovate Tracking

3.6 配置Renovate版本追踪

Renovate tracks versions.env entries automatically via inline
# renovate:
annotations (added in step 3.1). No changes to
.github/renovate.json5
are needed unless you want to add grouping or automerge overrides. For the full annotation workflow, see the versions-renovate skill.
Renovate会通过步骤3.1中添加的
# renovate:
注解自动追踪versions.env中的条目。除非需要添加分组或自动合并覆盖配置,否则不需要修改
.github/renovate.json5
。完整注解工作流可参考versions-renovate skill

3.7 Optional: Additional Configuration

3.7 可选:额外配置

For apps that need extra resources, create
kubernetes/platform/config/<app-name>/
:
如果应用需要额外资源,创建
kubernetes/platform/config/<app-name>/
目录:

HTTPRoute (for exposed apps)

HTTPRoute(用于对外暴露的应用)

For detailed gateway routing and certificate configuration, see the gateway-routing skill.
yaml
undefined
详细的网关路由和证书配置可参考gateway-routing skill
yaml
undefined

config/<app-name>/route.yaml

config/<app-name>/route.yaml

apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: <app-name> spec: parentRefs: - name: internal-gateway namespace: gateway hostnames: - <app-name>.${internal_domain} rules: - backendRefs: - name: <app-name> port: 80
undefined
apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: name: <app-name> spec: parentRefs: - name: internal-gateway namespace: gateway hostnames: - <app-name>.${internal_domain} rules: - backendRefs: - name: <app-name> port: 80
undefined

Canary Health Check

金丝雀健康检查

yaml
undefined
yaml
undefined

config/<app-name>/canary.yaml

config/<app-name>/canary.yaml

apiVersion: canaries.flanksource.com/v1 kind: Canary metadata: name: http-check-<app-name> spec: schedule: "@every 1m" http: - name: <app-name>-health url: https://<app-name>.${internal_domain}/health responseCodes: [200] maxSSLExpiry: 7
undefined
apiVersion: canaries.flanksource.com/v1 kind: Canary metadata: name: http-check-<app-name> spec: schedule: "@every 1m" http: - name: <app-name>-health url: https://<app-name>.${internal_domain}/health responseCodes: [200] maxSSLExpiry: 7
undefined

PrometheusRule (custom alerts)

PrometheusRule(自定义告警)

Only create if the chart doesn't include its own alerts:
yaml
undefined
仅当Chart没有自带告警规则时创建:
yaml
undefined

config/<app-name>/prometheus-rules.yaml

config/<app-name>/prometheus-rules.yaml

apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: <app-name>-alerts spec: groups: - name: <app-name>.rules rules: - alert: <AppName>Down expr: up{job="<app-name>"} == 0 for: 5m labels: severity: critical annotations: summary: "<app-name> is down"
undefined
apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: name: <app-name>-alerts spec: groups: - name: <app-name>.rules rules: - alert: <AppName>Down expr: up{job="<app-name>"} == 0 for: 5m labels: severity: critical annotations: summary: "<app-name> is down"
undefined

Grafana Dashboard

Grafana仪表盘

  1. Search grafana.com for community dashboards
  2. Add via gnetId in grafana values, OR
  3. Create ConfigMap:
yaml
undefined
  1. 在grafana.com搜索社区仪表盘
  2. 在grafana values中通过gnetId添加,或者
  3. 创建ConfigMap:
yaml
undefined

config/<app-name>/dashboard.yaml

config/<app-name>/dashboard.yaml

apiVersion: v1 kind: ConfigMap metadata: name: grafana-dashboard-<app-name> labels: grafana_dashboard: "true" annotations: grafana_folder: "Applications" data: <app-name>.json: | { ... dashboard JSON ... }

See [references/monitoring-patterns.md](references/monitoring-patterns.md) for detailed examples.

---
apiVersion: v1 kind: ConfigMap metadata: name: grafana-dashboard-<app-name> labels: grafana_dashboard: "true" annotations: grafana_folder: "Applications" data: <app-name>.json: | { ... dashboard JSON ... }

详细示例可参考[references/monitoring-patterns.md](references/monitoring-patterns.md)。

---

Phase 4: Validate

第四阶段:校验

4.1 Kubernetes Validation

4.1 Kubernetes配置校验

bash
task k8s:validate
This runs:
  • kustomize build
  • kubeconform schema validation
  • yamllint checks
bash
task k8s:validate
该命令会执行:
  • kustomize build
  • kubeconform schema校验
  • yamllint检查

4.2 Renovate Validation

4.2 Renovate配置校验

bash
task renovate:validate
Fix any errors before proceeding.

bash
task renovate:validate
继续下一步前请修复所有错误。

Phase 5: Test on Dev

第五阶段:开发集群测试

The dev cluster is a sandbox — iterate freely until the deployment works.
开发集群是沙箱环境,可自由迭代直到部署正常运行。

5.1 Suspend Flux (if needed)

5.1 暂停Flux同步(如需要)

If Flux would reconcile over your changes, suspend the relevant Kustomization:
bash
task k8s:flux-suspend -- <kustomization-name>
如果Flux会覆盖你的修改,暂停对应的Kustomization同步:
bash
task k8s:flux-suspend -- <kustomization-name>

5.2 Deploy Directly

5.2 直接部署

Install or upgrade the chart directly on dev:
bash
undefined
在开发集群上直接安装或升级Chart:
bash
undefined

Standard Helm repo

标准Helm仓库

KUBECONFIG=~/.kube/dev.yaml helm install <app-name> <repo>/<chart>
-n <namespace> --create-namespace
-f kubernetes/platform/charts/<app-name>.yaml
--version <version>
KUBECONFIG=~/.kube/dev.yaml helm install <app-name> <repo>/<chart>
-n <namespace> --create-namespace
-f kubernetes/platform/charts/<app-name>.yaml
--version <version>

OCI chart

OCI Chart

KUBECONFIG=~/.kube/dev.yaml helm install <app-name> oci://registry/<path>/<chart>
-n <namespace> --create-namespace
-f kubernetes/platform/charts/<app-name>.yaml
--version <version>

For iterating on values, use `helm upgrade`:
```bash
KUBECONFIG=~/.kube/dev.yaml helm upgrade <app-name> <repo>/<chart> \
  -n <namespace> \
  -f kubernetes/platform/charts/<app-name>.yaml \
  --version <version>
KUBECONFIG=~/.kube/dev.yaml helm install <app-name> oci://registry/<path>/<chart>
-n <namespace> --create-namespace
-f kubernetes/platform/charts/<app-name>.yaml
--version <version>

迭代values配置时使用`helm upgrade`:
```bash
KUBECONFIG=~/.kube/dev.yaml helm upgrade <app-name> <repo>/<chart> \
  -n <namespace> \
  -f kubernetes/platform/charts/<app-name>.yaml \
  --version <version>

5.3 Wait for Pods

5.3 等待Pod启动

bash
KUBECONFIG=~/.kube/dev.yaml kubectl -n <namespace> \
  wait --for=condition=Ready pod -l app.kubernetes.io/name=<app-name> --timeout=300s
bash
KUBECONFIG=~/.kube/dev.yaml kubectl -n <namespace> \
  wait --for=condition=Ready pod -l app.kubernetes.io/name=<app-name> --timeout=300s

5.4 Verify Network Connectivity

5.4 验证网络连通性

CRITICAL: Network policies are enforced - verify traffic flows correctly:
bash
undefined
重要:网络策略已生效,请验证流量流转正常:
bash
undefined

Setup Hubble access (run once per session)

配置Hubble访问(每个会话执行一次)

KUBECONFIG=~/.kube/dev.yaml kubectl port-forward -n kube-system svc/hubble-relay 4245:80 &
KUBECONFIG=~/.kube/dev.yaml kubectl port-forward -n kube-system svc/hubble-relay 4245:80 &

Check for dropped traffic (should be empty for healthy app)

检查被丢弃的流量(应用健康时应该无输出)

hubble observe --verdict DROPPED --namespace <namespace> --since 5m
hubble observe --verdict DROPPED --namespace <namespace> --since 5m

Verify gateway can reach the app (if exposed)

验证网关可以访问应用(如果已暴露)

hubble observe --from-namespace istio-gateway --to-namespace <namespace> --since 2m
hubble observe --from-namespace istio-gateway --to-namespace <namespace> --since 2m

Verify app can reach database (if using postgres access label)

验证应用可以访问数据库(如果使用了postgres访问标签)

hubble observe --from-namespace <namespace> --to-namespace database --since 2m

**Common issues:**
- Missing profile label → gateway traffic blocked
- Missing access label → database/S3 traffic blocked
- Wrong profile → external API calls blocked (use `internal-egress` or `standard`)
hubble observe --from-namespace <namespace> --to-namespace database --since 2m

**常见问题:**
- 缺少配置文件标签 → 网关流量被拦截
- 缺少访问标签 → 数据库/S3流量被拦截
- 配置文件选择错误 → 外部API调用被拦截(使用`internal-egress`或`standard`)

5.5 Verify Monitoring

5.5 验证监控配置

Use the helper scripts:
bash
undefined
使用辅助脚本:
bash
undefined

Check deployment health

检查部署健康状态

.claude/skills/deploy-app/scripts/check-deployment-health.sh <namespace> <app-name>
.claude/skills/deploy-app/scripts/check-deployment-health.sh <namespace> <app-name>

Check ServiceMonitor discovery (requires port-forward)

检查ServiceMonitor发现状态(需要开启端口转发)

.claude/skills/deploy-app/scripts/check-servicemonitor.sh <app-name>
.claude/skills/deploy-app/scripts/check-servicemonitor.sh <app-name>

Check no new alerts

检查无新增告警

.claude/skills/deploy-app/scripts/check-alerts.sh
.claude/skills/deploy-app/scripts/check-alerts.sh

Check canary status (if created)

检查金丝雀状态(如果已创建)

.claude/skills/deploy-app/scripts/check-canary.sh <app-name>
undefined
.claude/skills/deploy-app/scripts/check-canary.sh <app-name>
undefined

5.6 Iterate

5.6 迭代优化

If something isn't right, fix the manifests/values and re-apply. This is the dev sandbox — iterate until it works. Update Helm values, ResourceSet configs, network policy labels, etc. and re-deploy.

如果存在问题,修复清单/values后重新部署。这是开发沙箱,可反复迭代直到正常运行。更新Helm values、ResourceSet配置、网络策略标签等后重新部署即可。

Phase 6: Validate GitOps & PR

第六阶段:GitOps校验与PR提交

6.1 Reconcile and Validate

6.1 同步与校验

Before opening a PR, prove the manifests work through the GitOps path:
bash
undefined
提交PR前,验证清单可以通过GitOps路径正常工作:
bash
undefined

Uninstall the direct helm install

卸载直接部署的Helm应用

KUBECONFIG=~/.kube/dev.yaml helm uninstall <app-name> -n <namespace>
KUBECONFIG=~/.kube/dev.yaml helm uninstall <app-name> -n <namespace>

Resume Flux and validate clean convergence

恢复Flux同步并验证干净收敛

task k8s:reconcile-validate

If reconciliation fails, fix the manifests and try again. The goal is a clean state where Flux can deploy everything from git.
task k8s:reconcile-validate

如果同步失败,修复清单后重试。目标是达到Flux可以从Git部署所有资源的干净状态。

6.2 Commit Changes

6.2 提交变更

bash
git add -A
git commit -m "feat(k8s): deploy <app-name> to platform

- Add <app-name> HelmRelease via ResourceSet
- Configure monitoring (ServiceMonitor, alerts)
- Add Renovate manager for version updates
$([ -f kubernetes/platform/config/<app-name>/canary.yaml ] && echo "- Add canary health checks")
$([ -f kubernetes/platform/config/<app-name>/route.yaml ] && echo "- Configure HTTPRoute for ingress")"
bash
git add -A
git commit -m "feat(k8s): deploy <app-name> to platform

- Add <app-name> HelmRelease via ResourceSet
- Configure monitoring (ServiceMonitor, alerts)
- Add Renovate manager for version updates
$([ -f kubernetes/platform/config/<app-name>/canary.yaml ] && echo "- Add canary health checks")
$([ -f kubernetes/platform/config/<app-name>/route.yaml ] && echo "- Configure HTTPRoute for ingress")"

6.3 Push and Create PR

6.3 推送代码并创建PR

bash
git push -u origin deploy-<app-name>

gh pr create --title "feat(k8s): deploy <app-name>" --body "$(cat <<'EOF'
bash
git push -u origin deploy-<app-name>

gh pr create --title "feat(k8s): deploy <app-name>" --body "$(cat <<'EOF'

Summary

Summary

  • Deploy <app-name> to the Kubernetes platform
  • Full monitoring integration (ServiceMonitor + alerts)
  • Automated version updates via Renovate
  • Deploy <app-name> to the Kubernetes platform
  • Full monitoring integration (ServiceMonitor + alerts)
  • Automated version updates via Renovate

Test plan

Test plan

  • Validated with
    task k8s:validate
  • Tested on dev cluster with direct helm install
  • ServiceMonitor targets discovered by Prometheus
  • No new alerts firing
  • Canary health checks passing (if applicable)
Generated with Claude Code EOF )"
undefined
  • Validated with
    task k8s:validate
  • Tested on dev cluster with direct helm install
  • ServiceMonitor targets discovered by Prometheus
  • No new alerts firing
  • Canary health checks passing (if applicable)
Generated with Claude Code EOF )"
undefined

6.4 Report PR URL

6.4 返回PR URL

Output the PR URL for the user.
Note: The worktree is intentionally kept until PR is merged. User cleans up with:
bash
task wt:remove -- deploy-<app-name>

向用户输出PR URL。
注意:工作树会被保留到PR合并后,用户可通过以下命令清理:
bash
task wt:remove -- deploy-<app-name>

Secrets Handling

密钥处理

For detailed secret management workflows including persistent SSM-backed secrets, see the secrets skill.
┌─────────────────────────────────────────────────────────────────────┐
│                      Secrets Decision Tree                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  App needs a secret?                                                │
│      │                                                              │
│      ├─ Random/generated (password, API key, encryption key)        │
│      │   └─ Use secret-generator annotation:                        │
│      │      secret-generator.v1.mittwald.de/autogenerate: "key"     │
│      │                                                              │
│      ├─ External service (OAuth, third-party API)                   │
│      │   └─ Create ExternalSecret → AWS SSM                         │
│      │      Instruct user to add secret to Parameter Store          │
│      │                                                              │
│      └─ Unclear which type?                                         │
│          └─ AskUserQuestion: "Can this be randomly generated?"      │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘
包含持久化SSM-backed密钥的详细密钥管理工作流可参考secrets skill
┌─────────────────────────────────────────────────────────────────────┐
│                      Secrets Decision Tree                          │
├─────────────────────────────────────────────────────────────────────┤
│                                                                      │
│  App needs a secret?                                                │
│      │                                                              │
│      ├─ Random/generated (password, API key, encryption key)        │
│      │   └─ Use secret-generator annotation:                        │
│      │      secret-generator.v1.mittwald.de/autogenerate: "key"     │
│      │                                                              │
│      ├─ External service (OAuth, third-party API)                   │
│      │   └─ Create ExternalSecret → AWS SSM                         │
│      │      Instruct user to add secret to Parameter Store          │
│      │                                                              │
│      └─ Unclear which type?                                         │
│          └─ AskUserQuestion: "Can this be randomly generated?"      │
│                                                                      │
└─────────────────────────────────────────────────────────────────────┘

Auto-Generated Secrets

自动生成密钥

yaml
apiVersion: v1
kind: Secret
metadata:
  name: <app-name>-secret
  annotations:
    secret-generator.v1.mittwald.de/autogenerate: "password,api-key"
type: Opaque
yaml
apiVersion: v1
kind: Secret
metadata:
  name: <app-name>-secret
  annotations:
    secret-generator.v1.mittwald.de/autogenerate: "password,api-key"
type: Opaque

External Secrets

外部密钥

yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: <app-name>-secret
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: aws-parameter-store
  target:
    name: <app-name>-secret
  data:
    - secretKey: api-token
      remoteRef:
        key: /homelab/kubernetes/${cluster_name}/<app-name>/api-token

yaml
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: <app-name>-secret
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: ClusterSecretStore
    name: aws-parameter-store
  target:
    name: <app-name>-secret
  data:
    - secretKey: api-token
      remoteRef:
        key: /homelab/kubernetes/${cluster_name}/<app-name>/api-token

Error Handling

错误处理

ErrorResponse
No chart foundSuggest app-template, ask user
Validation failsShow error, fix, retry
CrashLoopBackOffShow logs, propose fix, ask user
Alerts firingShow alerts, determine if related, ask user
Namespace existsAsk user: reuse or new name
Secret neededApply decision tree above
Port-forward failsCheck if Prometheus is running in dev
Pods rejected by PodSecurityMissing security context for restricted namespace

错误处理方案
未找到Chart建议使用app-template,询问用户
校验失败展示错误,修复后重试
CrashLoopBackOff展示日志,提出修复方案,询问用户
告警触发展示告警,判断是否相关,询问用户
命名空间已存在询问用户:复用还是重命名
需要密钥参考上方决策树处理
端口转发失败检查Prometheus是否在开发集群运行
Pod被PodSecurity拒绝受限命名空间缺少安全上下文配置

User Interaction Points

用户交互节点

PhaseInteractionPurpose
ResearchAskUserQuestionPresent kubesearch findings, confirm chart choice
ResearchAskUserQuestionNative helm vs app-template decision
ResearchAskUserQuestionExposure type (internal/external/none)
Dev TestAskUserQuestionReport test results, confirm PR creation
FailureAskUserQuestionReport error, propose fix, ask to retry

阶段交互目的
调研AskUserQuestion呈现kubesearch结果,确认Chart选择
调研AskUserQuestion选择原生Helm还是app-template
调研AskUserQuestion确认暴露类型(内网/公网/不暴露)
开发测试AskUserQuestion上报测试结果,确认创建PR
失败AskUserQuestion上报错误,提出修复方案,询问是否重试

References

参考文档

  • File Templates - Copy-paste templates for all config files
  • Monitoring Patterns - ServiceMonitor, PrometheusRule, Canary examples
  • flux-gitops skill - ResourceSet patterns
  • app-template skill - For apps without native charts
  • kubesearch skill - Research workflow
  • 文件模板 - 所有配置文件的可复用模板
  • 监控模式 - ServiceMonitor、PrometheusRule、Canary示例
  • flux-gitops skill - ResourceSet模式
  • app-template skill - 无原生Chart的应用处理方案
  • kubesearch skill - 调研工作流