基于 Open Policy Agent 实现镜像拉取配额

解读

在国内金融、运营商、云厂商的容器平台面试中,“镜像拉取配额” 不仅是技术题,更是**“合规审计”** 与**“成本治理”** 的结合。面试官想确认你能否:

  1. 用 OPA 把**“谁、从哪个仓库、拉多少、多频繁”** 变成可审计的策略;
  2. 让策略**“热更新”** 而不重启 Docker Daemon;
  3. 在**“多租户命名空间”** 场景下,既防“刷流量”又防“误杀”;
  4. 给出**“降级方案”**:OPA 异常时不阻断核心业务。

一句话:把 OPA 当**“云原生配额网关”** 用,而不是简单写条 Rego。

知识点

  • OPA 架构:Bundle Server / Discovery / Status API,国内常用 Nacos 或自建 MinIO 当 Bundle 源
  • Docker AuthZ Plugin 链/usr/local/bin/docker-authz-opa 以 Unix Socket 接入,每次 docker pull 都触发 Evaluate
  • Rego 输入结构input.BodyImageActor.ID需把 Harbor 的 robot-account 映射到企业 LDAP
  • 配额维度“镜像层大小累加” 而非次数,用 OPA 的 http.send 缓存 Harbor 配额接口结果
  • 性能陷阱Rego 不支持浮点除,层大小统一用 MB 整数
  • 高可用OPA Sidecar + gRPC 健康探针kubelet 就绪探针失败则自动降级为 Audit-only
  • 合规留痕OPA Decision Logs 直接推送到企业 Kafka日志字段必须带 “employeeId” 与 “costCenter”

答案

步骤 1:部署架构

  • 在每个 Docker 节点起 opa:0.61.0-sidecar 容器,--log-level=error --set=decision_logs.console=false
  • 通过 --authorization-model=opa 把 Docker Daemon 指向 /run/docker/plugins/opa.sock
  • Bundle Server 放在**“华北-北京 2”** 对象存储,CDN 回源走内网,5 分钟热更新一次

步骤 2:Rego 策略(精简版)

package docker.authz

import rego.v1

default allow := false

# 镜像层大小配额:单位 MB
quota := 10240  # 10 GB

# 从 Harbor 获取已用额度
used := usage {
    resp := http.send({
        "method": "GET",
        "url": sprintf("%s/api/v2.0/projects/%s/quotas",
                       [input.harbor_base, input.project]),
        "headers": {"authorization": sprintf("Bearer %s", [input.harbor_token])},
        "cache": true
    })
    usage := resp.body[0].used.storage / 1024 / 1024  # 转 MB
}

# 本次拉取大小
size := s {
    s := sum([layer.size | layer := input.body.layers[_]])
}

allow if {
    input.Path == "/v1.41/images/create"
    input.Method == "POST"
    used + size <= quota
}

步骤 3:Docker Daemon 配置

{
  "authorization-plugins": ["opa"],
  "opa-endpoint": "unix:///run/docker/plugins/opa.sock"
}

重启 Daemon 后,任何超额 pull 会收到 403 Forbidden,返回体带 “QuotaExceeded: 已用 9.8 GB,本次需 1.5 GB”

步骤 4:灰度与降级

  • 按节点标签灰度:先给 test=opa-quota 的 5 台节点开启 enforce 模式;
  • 降级开关OPA 返回 5xx 或超时 200 msAuthZ Plugin 自动 fallback 到 Allow并在日志里打 “OPA_DEGRADE”
  • 审计Decision Logs 通过 Filebeat 直接送到公司 KafkaLogstash 解析后落入 ElasticsearchKibana 仪表盘按 costCenter 汇总镜像流量

步骤 5:验收指标

  • 单节点 500 并发 pull,P99 延迟 < 30 ms
  • 策略更新到 5000 节点 < 2 分钟
  • 全年因配额拦截导致的线上故障 0 次

拓展思考

  1. 跨云多活:如果镜像仓库在 “华为云-上海” 而集群在 “阿里云-深圳”OPA 的 http.send 缓存需把地域 RTT 算进去建议把配额数据同步到 Redis 而不是实时查 Harbor
  2. 镜像加速“Dragonfly P2P” 与配额冲突时,OPA 需识别 dfget 的 Range 请求只统计完整层大小一次
  3. Serverless 场景阿里云 ECI 或华为 CCI 没有 Docker Daemon需把 OPA 集成到 “虚拟节点”Kubelet Credential Provider用 ValidatingAdmissionPolicy 替代 AuthZ Plugin
  4. 成本分摊把 OPA 决策日志接入公司 FinOps 平台按 “镜像层 MD5 + 拉取字节” 算出每个部门的实际外网流出费用每月自动出账单
  5. 政策合规央行《金融容器安全规范》要求“镜像拉取需可回溯”OPA 日志必须保存 3 年且不可篡改可把 Bundle Server 与日志 Kafka 都接入 “国密版 Hashicorp Vault” 做签名