Talos 节点因 page_table_check BUG 重启:一次从误判到内核栈的调查

自动化测试使用 Playwright、Testcontainers 和 Docker-in-Docker。测试运行一段时间后,talos2 会从 Kubernetes 中消失,iDRAC 报告 System CPU Resetting,对应的测试分片失败。 这次调查最终拿到了完整的内核 panic 栈。直接原因不是硬件 reset,也不是 pid_max 耗尽,而是 Linux 6.18 中 PAGE_TABLE_CHECK 对 time namespace 的 VVAR 页面错误记账,触发了 BUG_ON()。 最初看到的现象 高并发 testcontainers 场景下,监控 Pod 观察到: loadavg=90+ pids=6890 threads=12513 dstate_count=6 eBPF 还记录到一分钟内大量进程和网络命名空间操作: fork_parent[runc]: 1597 fork_parent[containerd-shim]: 1209 fork_parent[dockerd]: 779 netns operations: 1303 unshare: 38 同时出现两类 D-state 栈。一类在网络命名空间清理路径: rcu_barrier netdev_run_todo default_device_exit_batch ops_undo_list cleanup_net process_scheduled_works worker_thread 另一类在容器文件清理路径: folio_wait_bit_common folio_wait_writeback truncate_inode_partial_folio truncate_inode_pages_range xfs_fs_evict_inode evict do_unlinkat __x64_sys_unlinkat 这些现象很容易让人得出一个合理但不完整的结论:高并发容器创建、删除导致 netns、workqueue、XFS 和 NVMe 同时拥塞,最后触发 hard lockup。 ...

August 31, 2026

避免挂载失败写入本地目录:给 Linux 挂载点加不可变保护

给程序配置一个目录来保存上传文件、备份或日志,是很常见的做法。目录可能位于外置硬盘、NFS 或其他网络存储上,例如: /srv/nas 但“目录存在”不代表“存储已经挂载”。如果网络中断、NFS 服务没有启动,或者开机时挂载单元失败,/srv/nas 仍然只是系统盘上的普通目录。程序并不知道这一点,照常读写后会发现读取到的状态与挂载的存储不同步。 这个问题的关键不是“如何让挂载更可靠”,而是要让挂载失败时的写入尽早失败。 挂载点为什么会接收写入 挂载点只是一个目录。挂载成功前,路径解析到的是父文件系统;挂载成功后,Linux VFS 才把这个目录覆盖成另一个文件系统的入口: 挂载前:/srv/nas -> 根文件系统上的目录 挂载后:/srv/nas -> 外置磁盘或 NFS 文件系统 因此,挂载失败不会让路径自动消失,应用仍然可以对它执行 open、mkdir 和 rename。只要应用有权限,数据就会落到根文件系统。 用不可变属性让错误暴露出来 Linux 的 chattr 可以修改文件系统属性。对目录设置 i(immutable,不可变)属性后,目录内容不能被创建、删除、重命名或修改,即使调用者是 root 也不能直接绕过: sudo chattr +i /srv/nas 这正好符合挂载点的保护需求: 挂载还没有发生时,写入会收到 Operation not permitted,而不是悄悄占用系统盘; 挂载成功后,路径进入外部文件系统,外部文件系统自己的权限和属性生效; 外部存储掉线后,应用得到 I/O 错误,问题可以被监控发现。 i 属性作用在挂载点下面那个“被覆盖的目录”上。挂载成功后,这个目录暂时不可见,所以不会把它的权限带到新挂载的文件系统中。 一次完整的验证 下面以 /srv/nas 和 NFS 为例。生产环境请替换为自己的地址,并先在测试机验证。 1. 创建一个空挂载点 sudo install -d -o app -g app -m 0755 /srv/nas 挂载点最好保持为空。否则外部文件系统挂载后,目录中的原有文件会被隐藏;卸载后它们又会出现,容易造成“文件凭空消失”的误判。 2. 确认当前没有挂载 findmnt -T /srv/nas mountpoint -q /srv/nas && echo '已经挂载,请先确认' || echo '当前未挂载' 如果 findmnt 显示的来源仍是根文件系统,说明此时可以继续设置属性。不要在没有确认的情况下对一个已挂载的目录运行 chattr,否则可能修改到外部文件系统根目录的属性。 ...

August 4, 2026

用 LD_PRELOAD 解决容器环境中的 Chrome ERR_NETWORK_CHANGED

运行 Playwright 自动化测试时,我们遇到过一种很难稳定复现的失败:页面正在正常加载,Chrome 却突然报错: net::ERR_NETWORK_CHANGED 它看起来像目标服务断网,但实际检查后发现: 被访问的服务没有重启 Pod 本身的网络仍然可用 同一时刻,Pod 内的容器引擎正在创建或销毁其他容器 重试通常又能成功 真正变化的不是 Chrome 正在使用的网络,而是同一网络命名空间中的虚拟网卡。 为什么启动容器会影响 Chrome 在服务器运行 Docker 时,每次创建或销毁容器,都可能伴随这些操作: 创建或删除 veth 设备 给虚拟网卡增加或移除 IP 地址 更新链路状态和路由信息 这些变化会通过 Linux Netlink 机制通知用户空间。Chrome 恰好也是这些通知的订阅者。 Chromium 在 Linux 上使用 AddressTrackerLinux 跟踪网络状态。初始化时,它会创建一个路由 Netlink 套接字: netlink_fd_.reset(socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE)); 随后订阅多组网络事件: addr.nl_groups = RTMGRP_IPV4_IFADDR | RTMGRP_IPV6_IFADDR | RTMGRP_NOTIFY | RTMGRP_LINK; 因此,同一网络命名空间里任何网卡的地址或链路发生变化,都可能被 Chrome 观察到。事件再经由 NetworkChangeNotifierLinux 向网络栈传播,最终让正在进行的请求以 ERR_NETWORK_CHANGED 结束。 整个链路可以简化成: 创建或销毁容器 ↓ 虚拟网卡、IP 或链路状态变化 ↓ 内核发送 NETLINK_ROUTE 消息 ↓ Chrome 的 AddressTrackerLinux 收到变化 ↓ NetworkChangeNotifier 通知网络栈 ↓ 正在进行的请求被中断 Chrome 的行为对桌面用户是合理的。例如从 Wi-Fi 切换到有线网络后,旧连接确实可能已经失效。但在 CI 中,其他容器产生的虚拟网卡变化通常与浏览器访问的目标毫无关系。 ...

July 23, 2026