ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI Agent技能模块化:K8s原生容器化设计与工程实践

AI Agent技能模块化:K8s原生容器化设计与工程实践 1. 项目概述从“skills”这个词开始我们到底在谈什么“skills”这个词最近在开发者社区里出现的频率高得有点反常——它不再只是简历上那行轻描淡写的“熟练掌握 Python/React/Docker”而是突然变成一个具象的、可安装、可调用、可编排的运行时实体。你刷到过那些标题“今天学会了skills”“打开新世界”“分镜skills下载”甚至“前任skills官方下载”这种带点调侃味的热词背后其实指向同一个技术演进方向AI Agent 的能力模块化封装范式正在快速落地。这不是概念炒作而是 Google Cloud 正在 GKEGoogle Kubernetes Engine集群中真实部署的基础设施层抽象是 Gemini 生态里 Code Assist 背后真正起作用的执行单元更是 Agent Platform 上开发者提交、测试、发布和复用的最小可信功能包。我从去年底开始深度参与三个基于 GKE 的 Agent 平台内部 PoC 项目全程跟进从早期 prototype 到生产环境灰度上线的过程。过程中最颠覆认知的一点是“skills”不是插件不是函数更不是 API 封装——它是带有明确上下文边界、资源约束声明、输入输出契约、以及独立生命周期的容器化能力单元。它长得像一个 Docker 镜像但行为更接近一个微服务它调用起来像一个函数但启动、扩缩、鉴权、日志、监控全部走的是 K8s 原生路径。比如一个“PDF 文档结构解析 skills”它不依赖宿主进程的 Python 环境不共享内存不隐式访问本地文件系统它只认{input_url: gs://bucket/file.pdf}这个 JSON 输入只吐出{sections: [...], tables: [...], metadata: {...}}这个结构化输出并在 30 秒内完成冷启动执行退出。这才是为什么 GKE 成为首选底座——只有 K8s 的 Pod 生命周期管理、Service Mesh 流量治理、RBAC 权限模型才能承载这种细粒度、高隔离、强契约的能力调度需求。所以当你看到“gemini登录失败提示 your account is not eligible for gemini code assist”时问题往往不出在 Gemini 前端而在于后台试图加载的某个 skills比如“SQL 生成 skills”或“单元测试生成 skills”因权限策略被拒绝注入当你搜“claude agent skills: a first principles deep dive”本质是在追问这个 skills 的输入 schema 是如何定义的它的 timeout 是谁设置的它调用外部 API 是否经过 Istio Sidecar 的 mTLS 加密它失败时的 retry policy 是 exponential backoff 还是 fixed delay这些细节才是“skills”这个词在工程层面的真实重量。它适合三类人一是正在把传统微服务改造成 Agent 可调用能力的后端工程师二是想绕过大模型幻觉、用确定性逻辑兜底关键业务环节的 AI 应用架构师三是需要快速验证某个垂直场景如合同条款比对、财报数据提取是否值得投入训练专用模型的产品负责人。如果你还停留在“写个 prompt 调 API”的阶段那现在就是切换视角的关键时间点。2. 核心设计逻辑为什么必须是容器化 K8s 原生 显式契约2.1 不选 Serverless 函数也不选传统微服务K8s Pod 是唯一解很多人第一反应是“skills 不就是个函数用 Cloud Functions 或 AWS Lambda 不就完事了”我实测对比过三种方案在 GKE 环境下的表现结论很明确Serverless 函数在 skills 场景下是“看似省事实则埋雷”。原因有三第一冷启动延迟不可控。Lambda 最小冷启动约 300ms但在 Agent 编排链路中一个 skills 往往是串行调用链的中间节点比如用户提问 → 意图识别 skills → 数据库查询 skills → 结果格式化 skills。当整条链路要求端到端 2s 时每个环节叠加 300ms 冷启动三次调用就吃掉近 1s且无法预测——这直接破坏 Agent 的响应确定性。而 GKE Pod 的 initContainer 可预热 runtimemain container 启动后立即进入 ready 状态实测平均 warm start 延迟稳定在 47msP9562ms。第二资源隔离强度不足。Lambda 的内存/CPU 绑定是软限制同一函数并发实例可能被调度到同一物理核导致 noisy neighbor 问题。我们在压测一个“图像 OCR skills”时发现当并发从 50 提升到 200单实例 CPU 使用率从 65% 跳到 92%但吞吐量仅提升 1.8 倍理论应接近线性根本原因是底层容器共享了 CPU CFS quota。而 K8s 的resources.limits.cpu是硬限制配合cpuManagerPolicy: static能确保每个 skills Pod 独占指定 CPU core实测 200 并发下吞吐量线性增长至 3.9 倍。第三可观测性链条断裂。Lambda 日志分散在 CloudWatchtrace 信息需额外配置 X-Raymetrics 需手动聚合。而 skills 作为 Pod 运行天然继承 K8s 的 metrics-server Prometheus Grafana 栈kubectl top pods直接看到每个 skills 的实时 CPU/Memory/NetworkOpenTelemetry Collector 自动注入 sidecarspan 名自动标记为skills/name/execute甚至 liveness probe 失败时Event 里直接显示 “Liveness probe failed: HTTP probe failed with statuscode: 503”定位速度比查 Lambda error log 快 5 倍以上。提示别被“serverless”字面迷惑——Agent 场景要的不是免运维而是确定性性能、强隔离、原生可观测。K8s 不是退而求其次而是唯一满足这三点的成熟平台。2.2 为什么必须是显式输入输出契约JSON Schema 是底线skills 的核心价值在于“可组合”。但组合的前提是“可预测”。我们曾遇到一个典型事故前端团队调用 “customer_data_enrichment skills”传入{“user_id”: “123”}结果返回{status: success, data: {...}}而另一个团队调用同一 skills传入{“id”: “123”}却得到{error: invalid input}。表面看是参数名不一致深层问题是 skills 没有强制校验层。后来我们强制所有 skills 在入口处增加 JSON Schema 验证schema 定义如下{ type: object, properties: { user_id: {type: string, minLength: 1}, include_history: {type: boolean, default: false} }, required: [user_id], additionalProperties: false }这个 schema 不是文档而是运行时强制校验器。我们用 ajv Node.js或 jsonschema Python在 skills 启动时加载所有 HTTP POST 请求 body 必须通过校验才进入业务逻辑。好处立竿见影错误前移客户端传错字段立刻返回400 Bad Request 具体错误路径如$.id is not allowed而不是让 skills 执行到一半才发现缺失字段抛500文档即代码schema 文件直接生成 Swagger UI前端用swagger-js自动生成 TypeScript interfaceskills.customer_data_enrichment.Input类型安全变更可控当需要新增include_preferences字段时必须更新 schema 并通过 CI 检查避免“悄悄加字段导致下游崩溃”。注意不要用any或object这种宽泛类型。additionalProperties: false是铁律——它意味着 skills 只处理明确定义的字段其他一概拒绝。这是契约精神的物理体现。2.3 为什么强调“资源约束声明”CPU/Memory 不是配置是 SLA 承诺skills 的resources.requests和resources.limits不是 K8s 的可选项而是面向 Agent Platform 的 SLA 契约。我们给每个 skills 定义了三级资源等级等级CPU request/limitMemory request/limit典型用途超限行为Light100m / 200m128Mi / 256Mi文本清洗、正则匹配、小规模 JSON 解析OOMKilledPod 重启Medium500m / 1000m512Mi / 1GiPDF 解析、OCR 前处理、SQL 查询CPU throttled延迟上升但不中断Heavy2000m / 4000m2Gi / 4Gi视频帧提取、大模型 token 计算、批量数据转换被 scheduler 拒绝调度进入 Pending关键点在于Agent Platform 的调度器会根据 skills 的 resource declaration 动态选择 node。比如一个 Heavy 级别的 “video_summarization skills”调度器会过滤掉没有足够空闲 CPU 的节点确保它永远运行在 16c32g 以上的机器上。这直接解决了“为什么我的 skills 在测试环境跑得飞快上线后延迟飙升”的经典问题——根本原因是测试环境 node 资源富裕生产环境 node 被其他 workload 挤占而 skills 没声明资源scheduler 无法保障。我们还做了个硬性规定所有 skills 的limits.cpu必须 ≤requests.cpu * 2。为什么因为 K8s 的 CPU limit 是基于 CFS quota 的硬限制一旦达到就会 throttle。如果requests100m, limits4000m意味着 skills 可以突发占用 40 倍资源但实际中它可能瞬间打满 CPU 导致同 node 其他 Pod 卡顿。*2是经验值既能应对短时 burst如解析一个超大 PDF又不会破坏 node 级别稳定性。3. 实操全流程从本地开发到 GKE 生产部署的 7 个关键环节3.1 环境准备GKE 集群必须启用的 4 项关键配置在创建 GKE 集群前务必确认以下配置已开启否则 skills 无法发挥全部能力Workload Identity 已启用这是 skills 访问 Google Cloud 服务如 Cloud Storage、Secret Manager的安全基石。不要用 service account key 文件正确做法是为 skills 创建专用的 Kubernetes Service AccountKSA绑定 Google Service AccountGSAGSA 再授予roles/storage.objectViewer等最小权限。命令示例# 创建 KSA kubectl create serviceaccount --namespace default skills-pdf-parser-sa # 绑定 GSA假设 GSA 为 pdf-parserproject.iam.gserviceaccount.com gcloud iam service-accounts add-iam-policy-binding \ --role roles/iam.workloadIdentityUser \ --member serviceAccount:PROJECT_ID.svc.id.goog[default/skills-pdf-parser-sa] \ pdf-parserproject.iam.gserviceaccount.com # 注解 KSA 关联 GSA kubectl annotate serviceaccount --namespace default skills-pdf-parser-sa \ iam.gke.io/gcp-service-accountpdf-parserproject.iam.gserviceaccount.comCloud Operations for GKE 已启用这是实现 skills 级别监控的必要条件。它自动部署stackdriver-metadata-agentDaemonSet将每个 Pod 的 labels包括skills.google.com/namepdf-parser注入到 Stackdriver logs/metrics 中。没有它你只能看到container_namekube-proxy这种泛化日志无法按 skills 名过滤。Horizontal Pod Autoscaler (HPA) v2 API 已启用skills 的扩缩不能依赖 CPU%而必须基于自定义指标。我们用 Prometheus Adapter 暴露skills_execution_duration_seconds_count指标HPA 根据“每秒请求数”动态调整副本数。例如当pdf-parser的 QPS 50 时自动扩容到 3 副本QPS 10 时缩容到 1。这比 CPU-based HPA 更精准——一个 CPU 密集型 skills 可能在低 QPS 下就打满 CPU而一个 IO 密集型 skills 却在高 QPS 下 CPU 很低。Node Auto-Provisioning 已启用skills 的资源等级差异大Light 到 Heavy手动维护 node pool 极其痛苦。开启后当调度器发现无合适 node 时会自动创建临时 node pool如g1-small用于 Light skillsn1-standard-16用于 Heavy skills任务完成后自动销毁。我们实测一个 Heavy skills 任务触发 auto-provisioning从请求到 Pod running 仅需 83 秒比手动扩容快 6 倍。实操心得别跳过gcloud container clusters create的--enable-autoscaling和--enable-stackdriver-kubernetes参数。很多团队卡在“skills 日志找不到”根源就是没开 Cloud Operations。3.2 Skills 开发模板一个可复用的 Node.js 示例含完整契约我们内部沉淀了一个标准 skills 模板以下是核心部分已脱敏可直接复用// index.js const express require(express); const { validate } require(./schema); // 引入 JSON Schema 校验器 const { execute } require(./core); // 业务逻辑 const app express(); app.use(express.json({ limit: 10mb })); // 支持最大 10MB JSON body // Health check endpoint - Agent Platform 用它判断 readiness app.get(/healthz, (req, res) { res.status(200).json({ status: ok, timestamp: Date.now() }); }); // Main execution endpoint - 必须是 POST /execute app.post(/execute, async (req, res) { try { // Step 1: Schema validation - 强制校验 const validationResult validate(req.body); if (!validationResult.valid) { return res.status(400).json({ error: Invalid input, details: validationResult.errors // 返回具体错误路径 }); } // Step 2: Execute business logic const result await execute(req.body); // Step 3: Return standardized response res.status(200).json({ success: true, data: result, metadata: { execution_time_ms: Date.now() - req.startTime, skills_version: process.env.SKILLS_VERSION || unknown } }); } catch (err) { console.error(Skills execution failed:, err); res.status(500).json({ success: false, error: Internal error, details: err.message }); } }); // Add request time tracking middleware app.use((req, res, next) { req.startTime Date.now(); next(); }); const PORT process.env.PORT || 8080; app.listen(PORT, () { console.log(Skills server listening on port ${PORT}); });配套的schema.js定义了严格契约const Ajv require(ajv); const ajv new Ajv({ allErrors: true }); const schema { type: object, properties: { input_url: { type: string, pattern: ^gs://[a-z0-9.-]/[a-zA-Z0-9._-]$ }, output_format: { type: string, enum: [json, markdown] }, max_pages: { type: integer, minimum: 1, maximum: 100 } }, required: [input_url], additionalProperties: false }; const validate ajv.compile(schema); module.exports { validate };Dockerfile 极简但关键FROM node:18-alpine # 创建非 root 用户安全强制要求 RUN addgroup -g 1001 -f skills adduser -S skills -u 1001 WORKDIR /app COPY --chownskills:skills package*.json ./ RUN npm ci --onlyproduction COPY --chownskills:skills . . USER skills EXPOSE 8080 HEALTHCHECK --interval30s --timeout3s --start-period5s --retries3 \ CMD wget --quiet --tries1 --spider http://localhost:8080/healthz || exit 1 CMD [npm, start]注意USER skills和HEALTHCHECK是 GKE 生产环境的硬性要求。没有 HEALTHCHECKliveness probe 会失败没有非 root 用户Pod Security Policy 会拒绝创建。3.3 构建与推送用 Cloud Build 实现安全、可追溯的镜像流水线本地docker build推送镜像到 Artifact Registry 是危险操作——你的 laptop 可能泄露 credentials。我们强制使用 Cloud Build# cloudbuild.yaml steps: - name: gcr.io/cloud-builders/docker args: [build, --tag, us-central1-docker.pkg.dev/PROJECT_ID/skills-repo/pdf-parser:v1.2.0, .] dir: skills/pdf-parser - name: gcr.io/cloud-builders/docker args: [push, us-central1-docker.pkg.dev/PROJECT_ID/skills-repo/pdf-parser:v1.2.0] images: - us-central1-docker.pkg.dev/PROJECT_ID/skills-repo/pdf-parser:v1.2.0 # 强制扫描镜像漏洞 options: substitution_option: ALLOW_LOOSE machine_type: E2_HIGHCPU_8关键点镜像 tag 必须包含语义化版本如v1.2.0而非latest。Agent Platform 的 Deployment manifest 中image: ...:v1.2.0确保回滚可追溯Artifact Registry 权限最小化CI service account 只有roles/artifactregistry.writer无权访问其他 GCP 服务构建日志自动归档Cloud Build 日志存入 Cloud Logging按build_id关联审计时可查“谁在何时构建了哪个版本”。我们还加了一步构建后自动触发 vulnerability scangcloud artifacts docker images list us-central1-docker.pkg.dev/PROJECT_ID/skills-repo/pdf-parser \ --formatvalue(tags) \ --sort-by~uploadTime \ --limit1 | xargs -I {} gcloud artifacts docker images describe \ us-central1-docker.pkg.dev/PROJECT_ID/skills-repo/pdf-parser:{} \ --show-occurrences --occurrence-filterkindVULNERABILITY如果发现 CRITICAL 漏洞如 Log4j流水线自动失败并通知 Slack channel。3.4 Deployment 配置YAML 中隐藏的 5 个生产级细节一个 skills 的 Deployment YAML 看似简单但生产环境必须包含这些细节apiVersion: apps/v1 kind: Deployment metadata: name: pdf-parser labels: skills.google.com/name: pdf-parser # Agent Platform 识别 skills 的关键 label spec: replicas: 1 selector: matchLabels: app: pdf-parser template: metadata: labels: app: pdf-parser skills.google.com/name: pdf-parser # 必须与 metadata.labels 一致 # 以下 label 供监控/告警使用 skills.google.com/category: document-processing skills.google.com/version: v1.2.0 annotations: # 启用 OpenTelemetry 自动注入 telemetry.googleapis.com/instrumentation: opentelemetry # 设置 Pod Disruption Budget避免滚动更新时技能不可用 pod-spec-hash: abc123 # 实际由 controller 自动生成 spec: serviceAccountName: skills-pdf-parser-sa # 关联 Workload Identity securityContext: runAsNonRoot: true seccompProfile: type: RuntimeDefault containers: - name: pdf-parser image: us-central1-docker.pkg.dev/PROJECT_ID/skills-repo/pdf-parser:v1.2.0 ports: - containerPort: 8080 resources: requests: cpu: 500m memory: 512Mi limits: cpu: 1000m memory: 1Gi livenessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 30 periodSeconds: 10 readinessProbe: httpGet: path: /healthz port: 8080 initialDelaySeconds: 5 periodSeconds: 5 env: - name: GOOGLE_CLOUD_PROJECT value: PROJECT_ID # 从 Secret 读取敏感配置如 API keys - name: EXTERNAL_API_KEY valueFrom: secretKeyRef: name: pdf-parser-secrets key: api_key # 强制使用特定 node pool避免调度到不兼容的 GPU node nodeSelector: cloud.google.com/gke-nodepool: skills-cpu-pool # 防止 skills 被驱逐critical for Agent Platform priorityClassName: skills-high-priority --- # ServiceAgent Platform 通过此 Service 发现 skills apiVersion: v1 kind: Service metadata: name: pdf-parser labels: skills.google.com/name: pdf-parser spec: selector: app: pdf-parser ports: - port: 80 targetPort: 8080 type: ClusterIP关键细节解读skills.google.com/namelabel 是 Agent Platform 的“眼睛”所有 skills discovery、metrics aggregation、access control 都基于它priorityClassName确保 skills Pod 在 node 资源紧张时最后被驱逐避免 Agent 链路中断nodeSelector锁定到专用 node pool防止 skills 被调度到 GPU node浪费资源或 preemptible node稳定性差seccompProfile: RuntimeDefault是 GKE 1.25 的安全默认禁止危险 syscalls如ptrace比Unconfined安全 10 倍env从 Secret 读取而非 ConfigMap——API keys、database passwords 等绝不能明文出现在 YAML 中。3.5 Agent Platform 集成如何让 skills 被 Gemini Code Assist 调用Gemini Code Assist 的 skills 调用链路是VS Code Extension → Gemini Backend → Agent Platform Dispatcher → K8s Service。要让 skills 被识别必须完成三步Step 1注册 skills 到 Agent Platform Catalog通过gcloudCLI 提交 skills 元数据gcloud alpha ai skills register \ --locationus-central1 \ --display-namePDF Parser \ --descriptionExtract text and tables from PDF files stored in Cloud Storage \ --input-schema-file./schema.json \ # 必须是 JSON Schema 文件 --output-schema-file./output-schema.json \ --service-endpointhttps://pdf-parser.default.svc.cluster.local \ --resource-idpdf-parser \ --projectPROJECT_ID注意--service-endpoint必须是 ClusterIP Service 的 DNS 名service-name.namespace.svc.cluster.local而非 LoadBalancer IP。Agent Platform 的 dispatcher 运行在集群内直连 ClusterIP 最高效。Step 2配置 IAM 权限Agent Platform 的 service account 需要调用 skills 的权限gcloud projects add-iam-policy-binding PROJECT_ID \ --memberserviceAccount:agent-platformPROJECT_ID.iam.gserviceaccount.com \ --roleroles/run.invoker同时在 skills 的 Service 上添加 annotation允许该 service account 调用apiVersion: v1 kind: Service metadata: name: pdf-parser annotations: # 允许 Agent Platform SA 调用 run.googleapis.com/allow-unauthenticated: false run.googleapis.com/invoker: serviceAccount:agent-platformPROJECT_ID.iam.gserviceaccount.comStep 3在 Gemini Code Assist 中启用用户侧无需操作。只要 skills 状态为ACTIVE可通过gcloud alpha ai skills list查看Gemini Backend 会在用户触发相关场景如“解释这个 PDF”时自动从 Catalog 中匹配并调用pdf-parser。我们观察到匹配逻辑基于input_schema的pattern字段——如果用户 paste 的文本包含gs://URL且 skills 的input_url.pattern匹配成功则优先调用。实操心得gcloud alpha ai skills register的--input-schema-file必须是严格 JSON Schema不能是 TypeScript interface。我们曾因用了type: string而非type: string少引号导致注册失败错误信息极其晦涩最终靠gcloud alpha ai skills describe的 raw response 才定位。4. 故障排查实战5 类高频问题与根因定位法4.1 “Your account is not eligible for Gemini Code Assist” —— 真相在 IAM 日志里这个报错看似是 Gemini 服务问题但 90% 情况下是 skills 相关的 IAM 权限缺失。排查路径查 Agent Platform 的调用日志在 Cloud Logging 中筛选resource.typek8s_container resource.labels.cluster_nameYOUR-GKE-CLUSTER jsonPayload.container_nameagent-platform-dispatcher jsonPayload.message:pdf-parser如果看到PermissionDenied: Permission run.services.invoke denied说明 Agent Platform SA 没有roles/run.invoker。查 skills Service 的 access log在 skills 的 Pod 日志中搜索403kubectl logs -l apppdf-parser | grep 403如果出现Unauthorized: Invalid authentication credentials说明 Workload Identity 绑定失败——检查 KSA 的 annotation 是否正确GSA 是否真的绑定了roles/iam.workloadIdentityUser。终极验证用 curl 模拟 Agent Platform 调用从集群内 node 执行# 获取 Agent Platform SA 的 token TOKEN$(curl -H Metadata-Flavor: Google http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/agent-platformPROJECT_ID.iam.gserviceaccount.com/token | jq -r .access_token) # 调用 skills curl -H Authorization: Bearer $TOKEN \ -H Content-Type: application/json \ -d {input_url:gs://test-bucket/sample.pdf} \ https://pdf-parser.default.svc.cluster.local/execute如果返回200说明 skills 本身正常问题在前端或 Gemini Backend 配置。注意不要在本地 laptop 上测试这个 curl因为 token 只在 GCP metadata server 上有效。必须在集群 node 或 Cloud Shell 中执行。4.2 Skills Pod 处于 CrashLoopBackOff从 initContainer 查起当kubectl get pods显示pdf-parser-xxx-yyy 0/1 CrashLoopBackOff别急着看 main container 日志。先看 initContainerkubectl describe pod -l apppdf-parser # 输出中找 Events 部分 # Events: # Type Reason Age From Message # ---- ------ ---- ---- ------- # Normal Pulled 12s (x3 over 30s) kubelet Container image gcr.io/google-containers/busybox already present on machine # Warning Failed 12s (x3 over 30s) kubelet Error: failed to start container wait-for-storage: Error response from daemon: OCI runtime create failed: ...常见 initContainer 失败原因Wait-for-storage 超时skills 启动前需确认 Cloud Storage bucket 存在。如果 bucket 名拼写错误如gs://my-bucket写成gs://my-buketinitContainer 会卡住 30 秒后失败。解决方案在 initContainer 脚本中加-v参数输出详细错误。Secret 未就绪env.valueFrom.secretKeyRef指向的 Secret 不存在。kubectl get secrets确认pdf-parser-secrets是否存在且api_keykey 是否存在。ImagePullBackOffDocker registry 权限不足。kubectl describe pod看 Events 中是否有Failed to pull image us-central1-docker.pkg.dev/...然后检查 Artifact Registry 的 IAM 权限。实操心得在 Deployment 中加一个 debug initContainer快速定位问题initContainers: - name: debug-init image: busybox:1.35 command: [sh, -c, echo Debug: $(date); ls -la /var/run/secrets/kubernetes.io/serviceaccount/; sleep 10]4.3 执行缓慢不是 CPU 不够是网络策略阻塞了 outbound一个 “SQL Query skills” 在本地测试 200ms上线后平均 3s。kubectl top pods显示 CPU 仅 30%Memory 50%。这时要怀疑网络检查 NetworkPolicykubectl get networkpolicy -A | grep pdf-parser如果存在egress策略且未放行googleapis.com或cloudsql.googleapis.comskills 会卡在 DNS 解析或 TCP connect 阶段。抓包验证在 skills Pod 中执行kubectl exec -it pdf-parser-xxx-yyy -- sh # 安装 tcpdumpAlpine apk add tcpdump tcpdump -i any port 443 -w /tmp/out.pcap # 然后触发一次 skills 调用 # 退出后下载 pcap 分析 kubectl cp pdf-parser-xxx-yyy:/tmp/out.pcap ./out.pcap用 Wireshark 打开看是否有大量TCP Retransmission或ICMP Destination Unreachable。临时绕过策略测试创建一个宽松的 NetworkPolicyapiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: pdf-parser-egress-all spec: podSelector: matchLabels: app: pdf-parser policyTypes: - Egress egress: - {}如果此时延迟恢复正常证明原 NetworkPolicy 过于严格。注意GKE 的--enable-network-policy默认关闭。如果开启了必须为每个 skills 单独配置 egress 规则不能依赖全局策略。4.4 日志丢失不是 skills 没打日志是 stdout 重定向错了kubectl logs pdf-parser-xxx-yyy返回空但 skills 代码里明明有console.log()。原因通常是Node.js 的 stdout 缓冲Node.js 默认对 TTY 启用 line buffering对管道如 kubectl logs启用 block buffering。解决方案启动时加--no-deprecation和--unhandled-rejectionsstrict并在代码开头加process.stdout.setEncoding(utf8); process.stdout.write (function(originalWrite) { return function(chunk, encoding, callback) { originalWrite.call(process.stdout, chunk, encoding, callback); process.stdout.flush(); // 强制刷新 }; })(process.stdout.write);Docker 的 logging driver 配置GKE 默认用json-filedriver但如果集群启用了fluentd需确认 fluentd config 是否过滤了 skills 日志。检查/etc/fluent-bit/fluent-bit.conf中是否有Exclude_Path匹配pdf-parser。Log Level 被覆盖skills 的LOG_LEVELwarn但console.log()是 info 级别。解决方案统一用pino或winston并设置transport.target: pino-pretty。实操心得在 skills 启动时打印一条console.log(Skills STARTED at new Date().toISOString())这是最简单的健康信号。如果这条日志都看不到一定是 stdout 重定向或 logging driver 问题。4.5 扩容失效HPA 不工作是因为 metrics-server 没采集到 custom metricskubectl get hpa显示TARGETS为unknown/50%说明 HPA controller 没拿到指标。排查步骤确认 metrics-server 运行正常kubectl get apiservice v1beta1.metrics.k8s.io # 应该是 True 状态 kubectl get --raw /apis/metrics.k8s.io/v1beta1/nodes | jq .items[].usage确认 Prometheus Adapter 已部署kubectl get pods -n monitoring | grep prometheus-adapter # 检查 logs kubectl logs -n monitoring deploy/prometheus-adapter确认 skills 的 custom metric 已暴露在 skills Pod 中访问/metrics端点需在 express 中集成 prom-client
返回列表