现代软件交付的瓶颈早已不在应用代码本身——它卡在跑代码的平台上。团队写好了服务,却要花大量时间处理集群配置、权限审批、镜像签名、环境漂移。内部开发者平台(Internal Developer Platform,IDP)的目标就是把这些摩擦抹掉:开发者声明"我要一个生产环境的前端服务",平台自动完成从集群分配到镜像构建到安全校验到部署的全链路。
下面拆解一个基于 Kubernetes + GitOps + 供应链安全的 IDP 设计,并给出可直接改造的配置示例。
平台的核心抽象:从"申请机器"到"声明工作负载"
传统模式下,开发者提工单申请虚拟机、配网络、装运行时。云原生 IDP 把这一切收进 Kubernetes 的声明式 API 里:
- 工作负载:Deployment / StatefulSet / CronJob,开发者只关心容器镜像与资源需求。
- 环境边界:Namespace + RBAC + NetworkPolicy,平台自动生成,开发者不需要手动划分。
- 交付流程:GitOps 仓库里的 manifest 即最终状态,集群只是执行者。
关键转变——开发者不再操作集群,而是操作 Git 仓库里的声明文件。平台负责把声明翻译成集群状态并持续校验。
GitOps 交付:ArgoCD + 多仓库分层
GitOps 的核心承诺:Git 仓库是唯一可信源,集群状态必须与仓库内容一致。在 IDP 中,推荐把仓库拆成两层:
| 层级 | 仓库内容 | 谁修改 |
|---|---|---|
| 应用层 | 服务镜像版本、环境变量、副本数 | 开发者 / CI |
| 平台层 | Namespace 模板、RBAC、限流策略、安全策略 | 平台团队 |
ArgoCD 监听两个仓库,用 AppSet 或 Application 把它们组合起来推送到集群。这样做的好处:平台团队改安全策略,不需要逐个通知应用团队——ArgoCD 自动同步。
一个最小化的 ArgoCD ApplicationSet 示例:
# argocd-appset.yaml — 按集群+环境自动生成 Application
apiVersion: argoproj.io/v1alpha1
kind: ApplicationSet
metadata:
name: frontend-services
namespace: argocd
spec:
generators:
- matrix:
generators:
- list:
elements:
- cluster: dev
url: https://k8s-dev.example.com
- cluster: staging
url: https://k8s-staging.example.com
- cluster: prod
url: https://k8s-prod.example.com
- git:
repoURL: https://git.example.com/platform/env-templates
files:
- path: "frontend/**/*.yaml"
template:
metadata:
name: '{{cluster}}-frontend-{{path.basename}}'
spec:
project: frontend
source:
repoURL: https://git.example.com/apps/frontend
targetRevision: main
path: '{{path.basename}}'
destination:
server: '{{url}}'
namespace: frontend-{{cluster}}
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=true
部署这个 ApplicationSet:
# 在已安装 ArgoCD 的集群上执行
kubectl apply -f argocd-appset.yaml
# 查看生成的 Applications
argocd app list --project frontend
开发者只需在应用仓库更新镜像标签,ArgoCD 就会把变更同步到对应集群。平台团队在 env-templates 仓库里加一条 NetworkPolicy,所有环境自动生效。
供应链安全:从镜像构建到集群准入的三道门
GitOps 解决了"怎么部署",但没回答"部署的东西是否可信"。供应链安全在 IDP 里至少要覆盖三个环节:
第一道门:镜像构建时签名
CI 流程用 Cosign 对镜像签名,证明"这个镜像由这条流水线产出":
# CI 中构建并推送镜像后,用 Cosign 签名
export COSIGN_EXPERIMENTAL=1
cosign sign \
--key cosign.key \
registry.example.com/frontend:v1.2.3
# 签名记录会存入 OCI registry 的 referrers tag
cosign verify \
--key cosign.pub \
registry.example.com/frontend:v1.2.3
把 cosign.pub 放进平台层的 GitOps 仓库,集群准入策略引用这个公钥。
第二道门:集群准入控制(Kyverno / OPA Gatekeeper)
用 Kyverno 策略强制:只有经过可信签名且来自允许 registry 的镜像才能运行:
# kyverno-verify-image.yaml — 阻止未签名镜像启动
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-frontend-images
spec:
validationFailureAction: Enforce
background: false
rules:
- name: verify-signature
match:
any:
- resources:
kinds:
- Deployment
- StatefulSet
namespaces:
- frontend-dev
- frontend-staging
- frontend-prod
verifyImages:
- imageReferences:
- "registry.example.com/frontend/*"
attestors:
- entries:
- keys:
public: |-
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE...
-----END PUBLIC KEY-----
attestations:
- type: https://example.com/provenance
conditions:
- all:
- key: "{{ ci_pipeline }}"
operator: Equals
value: "frontend-ci"
# 安装 Kyverno(如果集群尚未安装)
kubectl create namespace kyverno
helm repo add kyverno https://kyverno.github.io/kyverno/
helm repo update
helm install kyverno kyverno/kyverno -n kyverno
# 部署准入策略
kubectl apply -f kyverno-verify-image.yaml
# 测试:部署一个未签名镜像,应被拒绝
kubectl run test-unsigned --image=registry.example.com/frontend:v0.0.1 -n frontend-dev
# 预期输出:Error from server: admission webhook "verify-frontend-images.kyverno.svc" denied the request
第三道门:SBOM 与漏洞扫描准入
在 CI 中生成 SBOM 并用 Cosign atttach 为镜像附加 provenance attestation。集群准入策略可以进一步校验 attestation 里的漏洞扫描结果是否在阈值内。这一层实现成本较高,建议先落地签名准入,再逐步叠加 SBOM 校验。
开发者自助:Backstage 统一入口
GitOps 仓库 + Kyverno 策略构成了平台的"后端"。开发者需要一个不碰 YAML 的入口——Backstage(Spotify 开源的开发者门户)在这里出场:
- 软件模板:开发者点"创建新服务",Backstage 调用模板引擎在 GitOps 应用仓库里生成骨架项目 + ArgoCD Application。
- 服务目录:展示所有服务在 dev / staging / prod 的运行状态,直接从 ArgoCD API 拉取。
- TechDocs:每个服务的文档随仓库走,Backstage 自动聚合。
一个 Backstage 软件模板的简化定义(放在 templates/ 目录):
# backstage-template.yaml — 创建新前端服务的模板
apiVersion: scaffolder.backstage.io/v1
kind: Template
metadata:
name: frontend-service
title: Frontend Service Template
spec:
owner: platform-team
type: service
parameters:
- title: Service Info
required:
- serviceName
- owner
properties:
serviceName:
title: Service Name
type: string
pattern: '^[a-z][a-z0-9-]{4,20}$'
owner:
title: Team
type: string
ui:field: OwnerPicker
steps:
- id: scaffold
name: Generate Skeleton
action: fetch:template
input:
url: ./skeleton
values:
serviceName: '{{parameters.serviceName}}'
owner: '{{parameters.owner}}'
- id: publish
name: Push to Git
action: publish:gitlab
input:
repoUrl: 'gitlab.example.com?owner=apps&repo={{parameters.serviceName}}'
- id: register
name: Register in Backstage
action: catalog:register
input:
catalogInfoUrl: 'https://gitlab.example.com/apps/{{parameters.serviceName}}/catalog-info.yaml'
output:
links:
- title: Open Repo
url: 'https://gitlab.example.com/apps/{{parameters.serviceName}}'
开发者填表 → Backstage 生成代码骨架 → 推到 GitOps 仓库 → ArgoCD 检测到新路径自动同步 → Kyverno 校验镜像签名 → 服务上线。全程开发者没有碰 kubectl。
落地路线与取舍
| 阶段 | 做什么 | 先不做什么 |
|---|---|---|
| 第一阶段 | ArgoCD + 双仓库分层 + Namespace 自动创建 | SBOM 准入、复杂 RBAC 自动化 |
| 第二阶段 | Cosign 签名 + Kyverno 签名准入 | 多集群联邦、跨云迁移 |
| 第三阶段 | Backstage 软件模板 + TechDocs | 自定义 UI、插件生态 |
| 第四阶段 | SBOM attestation + 漏洞阈值准入 | 全链路 SLSA Level 3 |
几个容易踩的坑:
- ArgoCD selfHeal 与手动操作的冲突:如果开发者绕过 GitOps 直接
kubectl edit,ArgoCD 的 selfHeal 会把手动改动回滚。要么禁止直接操作集群,要么在特定 Namespace 关闭 selfHeal。 - 签名密钥管理:Cosign 的私钥不能放在 CI 环境变量里。用 KMS 后端(HashiCorp Vault / AWS KMS)托管,CI 通过 KMS API 签名。
- Kyverno 策略过严:初期建议
validationFailureAction: Audit,观察一段时间再切换到Enforce。直接拦截会导致开发者无法快速调试。
云原生 IDP 不是买一个产品装上就完事——它是 Kubernetes 声明式能力 + GitOps 同步机制 + 供应链安全校验的组合拳。先从 ArgoCD 双仓库分层起步,让开发者习惯"改 Git 而不是改集群";再叠加镜像签名与准入策略,把信任链从 CI 延伸到运行时;最后用 Backstage 把自助能力暴露给团队。每一层都有可复制的配置可以上手,不需要一步到位。