• 记一次神奇的 pipe 错误


    1. 写在最前面

    笔者负责的服务有一个设计是通过 pipe 在父子进程间传输请求。在物理机上时,父子进程能够正常完成 request 和 response 。但是在上 k8s 后,发现:

    • 父进程在 write 的时候只能写入 4096 字节
    • 子进程在只能读到父进程 write 的 4096 字节,后续读就会读阻塞

    如果把服务用 docker 的方式部署又会没有这个问题。简而言之,就是一个非常神奇而又让人抓狂的问题。

    2. 问题原因

    此问题由两方面导致的:

    • K8s 后,在 pod 内的 pipe 大小,被限制为只能写入 4096 字节

      • 宿主机的 pipe 大小

        在这里插入图片描述

      • 容器内的 pipe 大小

        在这里插入图片描述

    • 服务自身设计问题导致:

      • 笔者负责的服务父进程的 request 的包的大小已经超过了 4096 字节

      • 父进程 write 的时候,使用的非阻塞 write,上 k8s 后写入 pipe 的 buffer 大小为 4096 字节后 buffer 就满了,会到导致写入的数据不完整

    3. 解决问题

    本着「知其然,还有知其所以然」的精神,先从修改配置「CAP_SYS_RESOURCE」的历史说起吧。

    注:CAP_SYS_RESOURCE,忽略资源限制

    3.1 CAP 的历史说明

    内核从 2.2 开始,Linux 将传统上与超级用户 root 关联的特权划分为不同的单元,称为 CAP。CAP 作为线程的属性存在,每个单元可以独立启用和禁用。

    注:Linux 并不真正区分进程和线程

    每个进程有三个和能力有关的位图:inheritable(I)、permitted§ 和 effective(E),对应进程描述符 task_struct(include/linux/sched.h) 里的 cap_effective、cap_inheritable、cap_permitted ,所以可以查看 /proc/{PID}/status 来查看进程的能力。

    • cap_effective:当一个进程要进行某个特权操作时,操作系统会检查 cap_effective 的对应的位是否有效
    • cap_permitted:表示进程能够使用的能力,cap_effective 是 cap_permitted 的一个子集
    • cap_inheritable:表示能够被当前进程执行的程序继承的能力

    注:笔者之前在物理机上部署的方式是使用特权命令 docker run --privileged,此命令可以继承宿主机大部分的控制权限,有一定的风险性

    3.2 CAP 拆分的能力集合说明

    • CAP_CHOWN:修改文件属主的权限
    • CAP_DAC_OVERRIDE:忽略文件的 DAC 访问限制
    • CAP_DAC_READ_SEARCH:忽略文件读及目录搜索的 DAC 访问限制
    • CAP_FOWNER:忽略文件属主 ID 必须和进程用户 ID 相匹配的限制
    • CAP_FSETID:允许设置文件的 setuid 位
    • CAP_KILL:允许对不属于自己的进程发送信号
    • CAP_SETGID:允许改变进程的组 ID
    • CAP_SETUID:允许改变进程的用户 ID
    • CAP_SETPCAP:允许向其他进程转移能力以及删除其他进程的能力
    • CAP_LINUX_IMMUTABLE:允许修改文件的 IMMUTABLE 和 APPEND 属性标志
    • CAP_NET_BIND_SERVICE:允许绑定到小于 1024 的端口
    • CAP_NET_ADMIN:允许执行网络管理任务
    • CAP_NET_RAW:允许使用原始套接字
    • CAP_IPC_LOCK:允许锁定共享内存片段
    • CAP_IPC_OWNER:忽略 IPC 所有权检查
    • CAP_SYS_MODULE:允许插入和删除内核模块
    • CAP_SYS_RAWIO:允许直接访问 /devport、/dev/mem、/dev/kmem 及原始块设备
    • CAP_SYS_CHROOT:允许使用 chroot() 系统调用
    • CAP_SYS_PTRACE:允许跟踪任何进程
    • CAP_SYS_PACCT:允许执行进程的 BSD 使审计
    • CAP_SYS_ADMIN:允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等
    • CAP_SYS_BOOT:允许重新启动系统
    • CAP_SYS_NICE:允许提升优先级以及设置其他进程的优先级
    • CAP_SYS_RESOURCE:忽略资源限制
    • CAP_SYS_TIME:允许改变系统时钟
    • CAP_SYS_TTY_CONFIG:允许配置 TTY 设备
    • CAP_MKNOD:允许使用 mknod() 系统调用
    • CAP_LEASE:允许修改文件锁的 FL_LEASE 标志

    3.3 如何知道某个程序的能力集合

    通过查看 /proc/{pid}/status ,然后过滤其中的 Cap 字段属性:

    • CapInh: inheritable 表示能够被当前进程执行的程序继承的能力
    • CapPrm: permitted 表示进程能够使用的能力
    • CapEff: effective 当一个进程要进行某个特权操作时,操作系统会检查 cap_effective 的对应的位是否有效
    3.3.1 查看只能写入 4096B 大小的程序能力位图
    • 通过 ps 查询到关心的进程 id
    • /proc/{pid}/status | grep Cap 查看进程的能力位图

    在这里插入图片描述

    注:通过查看 pipe size 的 shell 工具,可以看到当前 00000000000c25fb 是这个值时,pipe 的大小为 4096B

    ​ pipe size 检验工具: M=0; while printf A; do >&2 printf “\r$((++M)) B”; done | sleep 999

    3.3.2 查看能写入 65536B 大小的能力位图
    • 通过 ps 查询到关心的进程 id
    • /proc/{pid}/status | grep Cap 查看进程的能力位图

    在这里插入图片描述

    注:注:通过查看 pipe size 的 shell 工具,可以看到当前 00000000010c25fb 是这个值时,pipe 的大小为 65536B

    3.3.3 比较两个能力位图

    capsh 命令可以将下面两个能力位图转义为可读的格式:

    • 00000000000c25fb -> 4096B

    • 00000000010c25fb -> 65536B

    在这里插入图片描述

    至此真相大白,需要为 pod 增加 CAP_SYS_RESOURCE 的能力。

    3.3.4 为 pod 增加 CAP_SYS_RESOURCE 的方法

    要为容器添加或删除 Linux 功能,请在容器的 securityContext 部分中包含 capabilities 字段,详细的 Linux capabilities 见 Linux capabilities

    apiVersion: v1
    kind: Pod
    metadata:
      name: demo
    spec:
      containers:
      - name: demo
        image: centos:7
        command: ["tail","-f", "/dev/null"]
        securityContext:
          capabilities:
            add: ["SYS_RESOUCE"]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    详细的增加参考见:Set capabilities for a Container

    6. 碎碎念

    最近真的是忙的各种心力交瘁,想抽出点时间学习新知识都感觉有写困难,希望下个月能够规划好工作和生活。「放弃当然很容易,但是坚持就会很酷吖」

    • 给自己买花 陪自己长大
    • 真正要做的事,对神明都不要讲,你一定要如此,万般如此
    • 希望你拥有想哭就哭、想笑就笑的情绪自由,也希望你不管怎样都能有面对负面情绪的能力和重新开始的勇气。

    5. 参考资料

  • 相关阅读:
    Transformer推理性能优化技术很重要的一个就是K V cache,能否通俗分析,可以结合代码?
    C++ Primer学习笔记-----附录:标准库
    接口文档下的渗透测试
    UML/SysML建模工具更新情况-截至2024年4月(1)5款-Trufun建模平台 v2024
    【C++】泛型编程 ④ ( 函数模板 与 普通函数 调用规则 | 类型自动转换 | 类型自动转换 + 显式指定泛型类型 )
    Unity插件DOTween使用指南2(简释贝塞尔曲线)
    专题 | 同步 异步
    Linux下c++串口编程
    day2_redis缓存
    ABYY FineReader PDF软件最新15个人​版安装下载步骤教程
  • 原文地址:https://blog.csdn.net/phantom_111/article/details/127973257