生产形态是单机 local(DEFAULT_DEPLOY_MODE=local,env 未覆盖)⇒ K8s 分支
在 local 下本来不可达;且该形态与官方 dsh 基座、插件体系均无关
=> 整体下线,并移除该形态唯一的第三方依赖。
移除(备份在 D:/github/_dsh_shenxian_K8s后端备份_20260915/,含还原命令与
「集群化方案要复用的模板清单」):
src/supervisor/{k8s-spawner,leader,reconcile}.ts
src/fs/k8s-user-fs.ts · src/tcp-bridge.ts · src/web/file-service.ts
test/{k8s-spawner,leader}.test.mjs · scripts/smoke-file-service.mjs
改写调用方:cli.ts(5 条 import / 选主块 / file-service 与 tcp-bridge 两个
子命令 / dispatch / HELP)、web/server.ts(改为 fail-loud 守卫 + 恒用
LocalSpawner)、fs/provider.ts(只留 LocalUserFs)、package.json(测试与
smoke 入口),外加 3 处指向已删类型的悬空 JSDoc。
保留(集群化方案列为未来可选):deploy/ · poc/01-04 · Dockerfile.dsh ·
docs/k8s*.md(已加「代码已下线」状态横幅)· config.ts 的 K8s 配置字段与
DeployMode 联合类型。
验证:tsc --noEmit exit 0;npm test 36 测试 / 35 通过 / 0 失败 / 1 跳过;
npm run verify exit 0;依赖与被删符号全仓 0 命中。
21 KiB
K8s 踩坑记录(模式 B)— 部署流程 + 根因排查
⚠️ 2026-09-15:模式 B 的 K8s 后端代码已从本仓下线(
k8s-spawner/leader/reconcile/k8s-user-fs/tcp-bridge/web/file-service六个模块 + 2 个测试 + 1 个脚本)。备份:D:\github\_dsh_shenxian_K8s后端备份_20260915\。本文按「历史设计稿 + 未来可选路线」保留;文中的dshs file-service/dshs tcp-bridge两条子命令现已不存在,照着敲会command not found。> ⚠️ 2026-09-15:模式 B 的 K8s 后端代码已从本仓下线(k8s-spawner/leader/reconcile/k8s-user-fs/tcp-bridge/web/file-service六个模块 + 2 个测试 + 1 个脚本)。备份:D:\github\_dsh_shenxian_K8s后端备份_20260915\。本文按「历史设计稿 + 未来可选路线」保留;文中的dshs file-service/dshs tcp-bridge两条子命令现已不存在,照着敲会command not found。 🧭 ← 返回 README · 分步教程:K8s 部署教程
记录模式 B 实机部署踩到的坑与根因:先在阿里云 2C2G 单机 k3s 上 PoC 验证,后在 ACK 智能托管上完整落地(§5–§8 含部署流程实录)。 想直接照着部署,从 k8s-deployment.md 开始;本文按「症状 → 根因 → 修复」组织,用于卡住时对查。
1. 环境 / k3s 安装
1.1 cgroup v1 导致 k3s v1.36+ kubelet 拒启
- 症状:k3s v1.36 启动几秒后退出,
systemctl反复 auto-restart。日志:Shutdown request received: "kubelet exited: ... kubelet is configured to not run on a host using cgroup v1 ..." - 根因:K8s 1.36 移除了 cgroup v1 支持;阿里云 Linux 3(RHEL8 系)默认跑 cgroup v1。
- 修复(二选一):
- 启用 cgroup v2(改内核参数 + 重启):
grubby --update-kernel=ALL --args="systemd.unified_cgroup_hierarchy=1" grubby --info=ALL | grep args # 确认参数已写入 reboot # 重启后验证 stat -fc %T /sys/fs/cgroup/ # 输出 cgroup2fs 才对 - 或降级 k3s v1.31(还支持 cgroup v1)。
- 启用 cgroup v2(改内核参数 + 重启):
1.2 swap 未关
- 症状:kubelet 起不来(
fail-swap-on默认 true)。 - 修复:
swapoff -a # 持久化(可选):注释 /etc/fstab 里的 swap 行 sed -i '/[[:space:]]swap[[:space:]]/s/^/#/' /etc/fstab
1.3 SELinux 依赖缺失(RHEL8 系)
- 症状:k3s 安装脚本报:
nothing provides container-selinux >= 3:2.191.0-1 needed by k3s-selinux-... - 修复:跳过 SELinux RPM(PoC 不需要):
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \ INSTALL_K3S_MIRROR=cn INSTALL_K3S_SKIP_SELINUX_RPM=true sh -s - --disable traefik --disable metrics-server --disable servicelb --disable local-storage
2. 镜像源(中国区)
2.1 docker hub 被墙
- 症状:
registry-1.docker.io403 / 超时,busybox/node/socat 镜像拉不动。 - 修复:给 k3s 的 containerd 配国内镜像
/etc/rancher/k3s/registries.yaml:然后mirrors: docker.io: endpoint: - "https://docker.m.daocloud.io" - "https://docker.1ms.run"systemctl restart k3s。 - 注意:镜像拉取极慢(一个 29MB 镜像拉了 13 分钟),批量拉取要有耐心或换更快的镜像源。
2.2 raw.githubusercontent 被墙
- 症状:
kubectl apply -f https://raw.githubusercontent.com/longhorn/...拉不到 manifest。 - 修复:加 ghproxy 前缀:
curl -sL "https://ghfast.top/https://raw.githubusercontent.com/longhorn/longhorn/v1.7.2/deploy/longhorn.yaml" -o /tmp/longhorn.yaml
2.3 alpine/socat tag 写错
- 症状:
alpine/socat:1.8.0.0-r0拉取 403。 - 根因:该 tag 不存在。
- 修复:用
alpine/socat:1.8.0.0(或latest)。 ⚠️ 内部设计稿 §4.3 里写的1.8.0.0-r0需按此改正;本项目已改用自带的 Nodetcp-bridge(见 §7.5),不再依赖 socat 镜像。
2.4 ghcr.io 直连被墙(CNPG/Longhorn 镜像)
- 症状:CloudNativePG operator 镜像
ghcr.io/cloudnative-pg/cloudnative-pg卡 "Pulling" 十几分钟不动。 - 修复:
registries.yaml里给ghcr.io也配镜像:ghcr.io: endpoint: - "https://ghcr.m.daocloud.io"
2.5 CloudNativePG poolers CRD apply 报 annotation 超长
- 症状:
kubectl apply -f cnpg.yaml报CRD poolers.postgresql.cnpg.io is invalid: metadata.annotations: Too long,operator 崩溃no matches for kind "Pooler"。 - 根因:client-side apply 写的
last-applied-configurationannotation 超过 256KB。 - 修复:
kubectl apply --server-side --force-conflicts -f cnpg.yaml(server-side 不写那个超长 annotation)。
3. Longhorn
3.1 磁盘保留比例过高
- 症状:卷副本创建失败
No available disk candidates,卷状态faulted。 - 根因:Longhorn 默认保留 30% 磁盘;磁盘 88% 满时「可用 < 保留量」,拒绝调度副本。
- 修复:把保留/最小可用降到 5%:
kubectl -n longhorn-system patch settings.longhorn.io storage-reserved-percentage-for-default-disk --type=merge -p '{"value":"5"}' kubectl -n longhorn-system patch settings.longhorn.io storage-minimal-available-percentage --type=merge -p '{"value":"5"}'
3.2 iscsid 未启动
- 症状:卷卡在
attaching,消费 Pod 一直ContainerCreating。 - 修复(装完
iscsi-initiator-utils后记得启动):dnf install -y iscsi-initiator-utils systemctl enable --now iscsid
3.3 单节点 3 副本调度不了
- 症状:2 个副本
Failed to schedule replica,卷卡attaching。 - 根因:Longhorn 默认 hard anti-affinity,同一卷的副本必须在不同节点;单节点只能调度 1 个。
- 修复:副本降到 1:
kubectl -n longhorn-system patch volumes.longhorn.io <volume-name> --type=merge -p '{"spec":{"numberOfReplicas":1}}' kubectl -n longhorn-system patch settings.longhorn.io default-replica-count --type=merge -p '{"value":"1"}'
3.4 RWX 卷需 nfs-utils
- 症状:RWX 卷挂载失败(RWX 走 share-manager / NFS ganesha)。
- 修复:装
nfs-utils(提供mount.nfs):dnf install -y nfs-utils
3.5 内存预算(重要)
- Longhorn 自身 ~600M。2C2G 上 k3s + Longhorn 已占 ~1.4G,无法再跑 CloudNativePG 3 实例(需 ~1.5G)。
- PoC item 4(CNPG + pgbench)需 ≥4G 机器,2G 必 OOM。
4. 方案修正(PoC 实测推翻/修正的结论)
4.1 flannel 实际强制 NetworkPolicy(§3.5 修正)
- 实测 k3s v1.31 flannel 下 NetworkPolicy 开箱即用(k3s 内嵌 kube-router netpol 控制器,不再以 DaemonSet 形式;attacker 被拒,删策略后立刻恢复连通)。
- §3.5「k3s 默认 flannel 不强制」不成立。
- Cilium 的选型理由应改为:L7 策略 / eBPF 性能 / Hubble 可观测(这些 kube-router 没有),而不是「基础强制」。
4.2 gid ≠ fsGroup(§6.0 措辞)
- 实测 init Job 建目录
0700属主 uid 正确,但 gid=0(不是 fsGroup=100001)——因 §4.3 的 Pod 只设runAsUser+fsGroup、没设runAsGroup,进程默认 gid=0。 - 不影响 0700 安全边界(0700 已把 group 权限关成
---)。若要对齐 gid,需加runAsGroup:<uid>。
4.3 socat tag(§4.3 修正)
alpine/socat:1.8.0.0-r0→alpine/socat:1.8.0.0。见 §2.3。
5. 完整部署流程(ACK 实跑记录,2026-08-23)
在阿里云 ACK 智能托管模式 上跑通控制面 Phase 2 的完整步骤。k8s 方案里的 「3 server HA / kube-vip / MetalLB / Cilium」在 ACK 上由托管能力替代(§5.5 映射)。 每用户 DSH Pod(Phase 3)、cert-manager、Helm chart 尚未落地,仍待补。
5.1 集群与基础组件
- 建 ACK 集群(智能托管模式):网络插件 DataPath V2(eBPF)+ 勾选 NetworkPolicy;服务转发模式 IPVS。
- 装 CNPG operator:manifest 用 ghproxy 下载(§2.2),
kubectl apply --server-side --force-conflicts(§2.5 annotation 超长坑)。 - ghcr.io 换源:CNPG operator 与 Postgres 镜像在阿里云拉不动(§2.4),换
ghcr.m.daocloud.io。
5.2 部署步骤
kubectl apply -f deploy/00-namespace.yamlkubectl apply -f deploy/01-dsh-pg.yaml(Postgres 集群;storageClass 用拓扑感知alicloud-disk-topology-alltype,size ≥20GiB)- 等
dsh-pg进入Cluster in healthy state;读连接串:kubectl -n dsh get secret dsh-pg-app -o jsonpath='{.data.uri}' | base64 -d - 推镜像到 ACR:CI master push 自动推(需
ACR_USERNAME/ACR_PASSWORDsecret),或 workflow_dispatch。 - 建三个 secret(不在 deploy/ YAML 里,因含动态值):
dsh-acr-pull(docker-registry类型,ACR 凭证)dsh-secret(共享加密密钥,key)dsh-pg(url= 上面读到的 URI)
kubectl apply -f deploy/02-control-plane.yaml- bootstrap admin:
kubectl -n dsh exec deploy/dsh-orchestrator -- node lib/cli.js bootstrap-admin --username admin --password '<p>' - 按 §5.3 验收。
5.3 验收清单(Phase 2)
- 注册 → 审核 → 登录 → 管理台/域名 API 全通。
- 访问控制:普通用户访问管理台 403、域名 API 200。
- kill 一个控制面副本 → Deployment 自动重建(3/3)、Service 不中断。
- 数据在 Postgres,删副本/重启不丢。
5.4 踩坑记录(ACK 实测新增)
| 坑 | 修复 |
|---|---|
| ghcr.io 被墙:CNPG operator / Postgres 镜像拉不动 | 换 ghcr.m.daocloud.io(§2.4) |
ESSD 最小 20GiB:size: 10Gi 报 less than minimum 20GiB |
storage size: 20Gi |
CNPG 重建集群密码漂移:dsh-pg-app 重新生成密码 |
读最新 URI 重建 dsh-pg secret;生产用固定密码 |
| 控制面启动依赖 Postgres 就绪:ECONNREFUSED 崩 | 等 Postgres healthy 再部署,或接受 CrashLoopBackOff 重试 |
| Auto Mode 节点有 taint,CNPG pod 调度失败 | GOATScaler 自动扩出无 taint 节点 |
5.5 ACK 与 k8s 方案的映射
| k8s.md 定案 | ACK 等价 |
|---|---|
| 3 server HA + kube-vip | 托管控制面(免费) |
| MetalLB(L2 ARP 云上不生效) | SLB / ALB |
| Cilium | Terway DataPath V2(eBPF + NetworkPolicy) |
| Longhorn RWX / RWO | NAS / ESSD 云盘 |
| CloudNativePG | CloudNativePG(自建)或 RDS PG 高可用版 |
6. PoC 实测基线(item 4:CNPG on Longhorn)
机器:阿里云 4C16G,Ubuntu 24.04,SSD 40G。k3s v1.31 + Longhorn v1.7.2(副本 1)+ CNPG v1.24.1,3 实例 healthy。
pgbench(scale 5,30s,4 clients / 2 threads):
| 负载 | tps | latency average | 说明 |
|---|---|---|---|
select-only(-S) |
19164.7 | 0.209 ms | 纯读,数据 ~70MB 全进内存缓冲,未打到磁盘 |
| TPC-B 混合(含写) | 705.3 | 5.671 ms | 写落到 Longhorn 磁盘,这才是 I/O 基线 |
结论:CNPG on Longhorn 跑通(3 实例、pgbench 正常)。写路径 latency ~5.6ms / tps ~705,是 Longhorn 单副本的 I/O 成本。要对照「节点本地 NVMe + PG 主备」判断是否可接受,需换 NVMe 盘 + 更大 scale(让数据集超过内存)再压一次。
另:pgbench 手动跑法(kubectl run --rm -i 在后台/非 tty 会话会卡 stdin,别用):
kubectl run pgbench-init --restart=Never --image=postgres:16 -n <ns> \
--env PGHOST=<cluster>-rw --env PGUSER=dsh --env PGPASSWORD=<pw> --env PGDATABASE=dsh \
-- pgbench -i -s 5
# 等 pod phase=Succeeded 后 kubectl logs;跑完 kubectl delete pod
7. Phase 3 集群联调踩坑(2026-08-23,第二阶段)
控制面
deployMode=k8s+ leader election + file sidecar + 每用户 Pod 在 ACK 上联调的实跑记录。前一个阶段(§5)只把控制面 3 副本 + CNPG 跑通了。
7.1 部署顺序(缺一步就挂,按序执行)
deploy/00-namespace.yaml→01-dsh-pg.yaml(Postgres healthy)→ 读dsh-pg-appURI 建dsh-pg/dsh-secret/dsh-acr-pullsecret。deploy/03-rbac.yaml(SA + Role + lease 权限)。漏了它,控制面 Deployment 报FailedCreate: serviceaccount "dsh-orchestrator" not found,滚动更新卡死。deploy/02-control-plane.yaml(含imagePullPolicy: Always,同名 tag 否则节点 缓存旧镜像)。- NAS:控制台建 CNFS(容器网络文件系统)→ 应用
deploy/05-storage.yaml引 CNFS →04-pvc.yaml→08-bootstrap.yaml(chmod 1777 PVC 根)→ 最后07-psa.yaml。 - 每用户 Pod 依赖
dsh-acr-pull(§7.3)。
7.2 NAS:用 CNFS,别手写 server 参数
- 手写
alicloud-nasStorageClass 的server必须是挂载目标域 +:/,且 不含 FileSystemId 前缀。我给错成<FSID>.<NAS_MOUNT_TARGET>(带 FSID 前缀)→ provisioner 报CreateDir: IllegalCharacters;去掉 FSID 前缀 后 PVC 才 Bound。 - 但挂载目标还会变(重建 NAS 后后缀漂移),正确姿势是控制台建 CNFS
(
storage.alibabacloud.com/v1beta1 ContainerNetworkFileSystem),StorageClass 用containerNetworkFileSystem: nas参数引用,provisioner 自动拿到当前挂载目标。 - bootstrap Job 第一次卡
ContainerCreating无事件 = NFS 挂载挂起(挂载目标错/VPC 不通); 目标对了会报mount.nfs: Connection reset by peer(通常是安全组/访问组或目标刚就绪)。
7.3 每用户 Pod 镜像拉取与资源
- 控制面镜像在私有 ACR,生成的 files Pod / DSH Pod / watchdog Job 必须带
imagePullSecrets: [dsh-acr-pull]——控制面 Deployment 有,但生成的 Pod 不继承。 漏了就是Init:ImagePullBackOff insufficient_scope。代码里 configimagePullSecret默认dsh-acr-pull,已在 K8sSpawner 生成的三类 Pod/Job 全部带上。 - bootstrap Job 也需
imagePullSecrets+ 用控制面镜像(busybox 从 docker.io 拉不动)。 - files sidecar 内存 64Mi 跑不起 node:22-slim + Fastify(OOMKilled → Pod 不 Ready → Headless DNS 无 A 记录 → 控制面 ENOTFOUND),提到 256Mi。
- files sidecar 以用户 uid 跑且无 home,
homedir()落到/,resolveConfig会尝试mkdir /.dshs→ EACCES 崩。runFileService已钉dataRoot=/tmp+ 占位encryptionSecret跳过写文件。
7.5 docker.io 被墙 → socat sidecar 换成 Node tcp-bridge
- ACK 节点拉不动 docker.io(busybox/alpine/socat 都超时),而
alpine/socat正是 每用户 DSH Pod 的 sidecar 镜像。改为控制面镜像里的tcp-bridge子命令(net纯转发 8081→8080),DSH Pod 只依赖 ACR 里已有的dsh+dshs两个镜像 +imagePullSecret,彻底去掉 docker.io 依赖。 - 测鉴权/带 cookie 的接口用
--resolve走域名,别用kubectl port-forward:设了cookieDomain=.dsh.example.com后 cookie 不会发到127.0.0.1。
7.6 每用户 Pod 的 uid 与就绪探针
- uid 错位:注册/建 admin 若先
initUserRoot(建 files Pod)再createUser,此时 用户不存在,resolveUid回退 hash uid,而createUser给的是baseUid+row_id—— files Pod 用 hash uid 建 0700 目录,DSH Pod 用 row_id uid 访问 → EACCES。已改成先 建用户再 initUserRoot。 - 就绪探针:DSH 只监听 loopback,
tcpSocket 8080探的是 Pod IP、永远不 Ready → Headless 无 A 记录 → 子域 502。改成容器内node -e http.get(127.0.0.1:8080)exec 探针。 - 子域代理端口:Headless Service(clusterIP: None)没有 kube-proxy 做 DNAT,代理
必须直连 Pod 的 targetPort(sidecar 8081),不能连 Service port 80——否则 502。
endpointFor返回 8081。 - cookie 跨子域:
dsh.<domain>登录后跳<user>.dsh.<domain>,SameSite=Lax实测 不带 cookie → 401;改SameSite=None; Secure(App 已 HTTPS-only)+secureCookies=true。 - 代理 502:控制面 keep-alive 池缓存旧 DSH Pod IP,重建 Pod 后命中旧 IP 副本 → 502;
连接出错时
agent.destroy()+agent:false重试一次重新 DNS。
7.4 域名入口被阿里云备案拦截
- 腾讯云入口机 nginx 反代到 ACK 公网 SLB(
<SLB_IP>)时,Host: dsh.example.com被阿里云返回403 Non-compliance ICP Filing(Server: Beaver)——域名在腾讯云备案、 未在阿里云备案,走阿里云公网 SLB 的 80/443 被备案校验拦。直连 SLB IP(不带域名 Host) 则 200 正常。 - 解法:给域名在阿里云做 ICP 备案,或入口走 NodePort/专线绕开公网 SLB 备案层,或 控制面域名解析子域改用「SLB 直连 + 腾讯云 nginx 透传 Host」之外的方案(待定)。
8. Phase 4 加固 / 可观测(2026-08-23)
8.1 代码加固(已落地,随镜像生效)
readOnlyRootFilesystem: true+emptyDir/tmp:DSH dsh/sidecar、file sidecar、 watchdog、控制面容器全部只读 rootfs,/tmp走 emptyDir(PSA restricted 纵深)。- 空闲回收:reconcile(仅 leader)对每个期望 main 检查
hasActiveSession,会话全过期 →stopPod + 删期望态,不自动拉起(复用sessionTtlSeconds,默认 7 天)。 - egress 收敛:
DSHS_EGRESS_CIDRS(逗号分隔 CIDR)非空时,每用户 DSH Pod 的 443 egress 从0.0.0.0/0收敛为白名单(仍 except 内网)。默认空 = 保持现状。 ⚠️ DeepSeek API 走 CDN,IP 会漂移,收敛前先dig +short api.deepseek.com核对并定期重查。
8.2 etcd encryption-at-rest(ACK 托管,控制台操作)
- ACK 控制台 → 集群 → 托管控制面 → 开启 etcd 加密(encryption-at-rest)。
- 开启后必须全量重写存量 Secret,否则旧 Secret 仍明文:
kubectl get secrets -A -o json | kubectl replace -f - - 验证(应只剩
k8s:enc:aescbc:v1:前缀的密文):覆盖kubectl get secrets --all-namespaces -o json | grep -v 'k8s:enc:aescbc' || echo "all encrypted"dsh-key-*(每用户 API key)、dsh-pg(DB DSN)、dsh-secret(共享加密密钥)。
8.3 可观测(Prometheus + Loki + 告警)
- 指标:控制面已可暴露
/metrics(未接入则用 prom-client 加一个);ACK 托管 Prometheus (ARMS)在控制台开通后,用 ServiceMonitor 抓dsh-orchestrator的 3080。 关键指标:控制面副本数、Postgres 连接数、每用户 Pod 数、崩溃计数。 - 日志:Loki + Promtail(或 ACK SLS)接入控制面与每用户 Pod 日志。
- 告警规则(Prometheus):控制面副本 < 3、Postgres 不可写、DSH Pod 崩溃率、NAS 容量水位。
示例(自建 Prometheus 时用):
注:托管 Prometheus/Loki 实际接入需在 ACK 控制台开通,本轮交付清单与步骤。
apiVersion: monitoring.coreos.com/v1 kind: PrometheusRule metadata: { name: dsh-alerts, namespace: dsh } spec: groups: - name: dsh rules: - alert: DshControlPlaneDown expr: count(up{app="dsh-orchestrator"}) < 3 for: 5m - alert: DshPostgresUnavailable expr: pg_up == 0 for: 2m
8.4 Web 安全审计(代码审计结论,2026-08-23)
- XSS(已修):
desktop.html/admin.html把文件名e.name、插件名/描述plugin.name/plugin.description、密钥名k.name、用户名u.username直接拼进innerHTML/insertAdjacentHTML。文件名与插件描述不受字符集约束 → 存储型 XSS。 已加esc()转义<>&"'后再拼接。 - 输入校验(已确认安全):用户名
^[a-zA-Z0-9_-]+$、域名isValidDomain(小写字母数字+连字符,防 nginx 注入)、密钥名^[A-Za-z0-9\-_ .]{1,32}$、路径resolveWithinRoot/safeFilename。 - CSRF(残余低风险,未修):会话 cookie 为
SameSite=None后跨站请求也会带 cookie,但所有状态变更端点都是application/json+ 无 CORS 头,跨站 fetch 会触发 preflight 被浏览器拦;真正暴露的是无 body 的POST /api/auth/logout(logout CSRF, 低危)。后续若要加固,给状态变更端点加 CSRF token / 自定义头校验。 - OWASP ZAP 基线扫描:未在本轮执行(需 ZAP 工具/容器)。已定位的 XSS 已人工修, ZAP 可作为 CI 门禁后续接入。
8.5 联调后期修的 4 个根因 bug(2026-08-23)
| 症状 | 根因 | 修复 |
|---|---|---|
| leader 不接管,lease 挂旧 pod 数小时 | client-node patchNamespacedLease 走 JSON Patch(要数组),传对象被 Go 拒 400 被静默吞 |
改 replaceNamespacedLease 全量 PUT + RV 乐观并发 |
| admin DSH 反复 502 | endpointFor 返回 Service DNS,A 记录 ~30s TTL,Pod 重启后这 30s 内仍解析旧 IP |
endpointFor 直接返回 podIP,每次请求实时读 Pod |
| 模型无法请求(EAI_AGAIN) | ACK DNS 是 node-local-dns(label k8s-app=node-local-dns),NP 规则却选 k8s-app=kube-dns → DNS 全被拦 |
NP DNS egress 改 0.0.0.0/0(可移植) |
| keepAlive 池销毁后不可复用 | agent.destroy() 只销毁不替换,后续请求首跳全失败 |
destroy 后 new Agent() 替换,再短连接重试一次 |