docker-libcontainer

本文详细解析了libcontainer如何协同execdriver加载容器配置,创建及管理Docker容器的全过程。介绍了进程对象Process和逻辑容器Container的概念,以及Factory类在创建容器中的作用。深入探讨了容器启动流程,包括容器配置的传递、网络设备初始化和容器内进程的管理。
libcontainer的工作方式

execdriver如何调用libcontainer加载容器配置container,继而创建真正的Docker容器?

  • 创建libcontaine构建容器需要使用的“进程”,进程对象(非真正进程),称为Process;
  • 设置容器的输出管道,这里使用的就是Docker daemon提供给libcontainer的pipes;
  • 使用名为Factory的工厂类,通过factory.Create(<容器ID>,<容器配置container>)创建一个“逻辑”上的容器,称为Container,在这个过程中,容器配置container会填充到Container对象的config项里,container的使命至此就完成了;
  • 执行Container.Start(Process)启动物理的容器;
  • execdriver执行由Docker daemon提供的startCallback完成回调动作;
  • execdriver执行Process.Wait,一直等上述Process的所有工作都完成。

可以看到,libcontainer对Docker容器做了一层更高级的抽象,它定义了Process和Container来对应Linux中“进程”与“容器”的关系。一旦“物理”的容器创建成功,其他调用者就可以通过容器ID获取这个逻辑容器Container,接着使用Container.Stats得到容器的资源使用信息,或者执行Container.Destory来销毁这个容器。
综上,libcontainer中最主要的内容是Process, Container以及Factory这3个逻辑实体的实现原理,而execdriver或者其他调用者只要依次执行“使用Factory创建逻辑容器Container",“启动逻辑容器Container”和“用逻辑容器创建物理容器”,即可完成Docker容器的创建。

libcontainer实现原理
  1. 用FactoryL}l建逻辑容器Container

    libcontainer中Factory存在的意义就是能够创建一个逻辑上的“容器对象”Container,这个逻辑上的“容器对象”并不是一个运行着的Docker容器,而是包含了容器要运行的指令及其参数、namespace和cgroups配置参数等。

    Factory的Create操作具体做的事情:

    • 验证容器运行的根目录(默认/var/lib/docker/containers)、容器ID(字母、数字和下划
      线构成,长度范围为1~1024)和容器配置这三项内容的合法性。
    • 验证上述容器ID与现有的容器不冲突。
    • 在根目录下创建以ID为名的容器工作目录(/var/lib/docker/containers/{容器ID} ).
    • 返回一个Container对象,其中的信息包括了容器ID、容器工作目录、容器配置、初始化指令和参数(dockerinit),以及cgroups管理器(这里有直接通过文件操作管理和systemd管理两个选择,默认选第一种)。
  2. 启动逻辑容器Container

    参与物理容器创建过程的Process一共有两个实例,第一个叫Process,用于物理容器内进程的配置和IO的管理;另一个叫ParentProcess,负责从物理容器外部处理物理容器启动工作,与Container对象直接进行交互。启动工作完成后,ParentProcess负责执行等待、发信号、获得容器内进程pid等管理工作。

    创建ParentProcess的过程如下:
    (1)创建一个管道(pipe ),用来与容器内未来要运行的进程通信(这个pipe不同于前面的输出流pipes,后面会做解释)。
    (2)根据逻辑容器Container中与容器内未来要运行的进程相关的信息创建一个容器内进程启动命令cmd对象,这个对象由Golang语言中的os/exec包进行声明,Docker会调用os/exec包中的内置函数,根据cmd对象来创建一个新的进程,即容器中的第一个进程dockerinit。而cmd对象则需要从Container中获得的属性包括启动命令的路径、命令参数、输人输出、执行命令的根目录以及进程管道pipe等。
    (3)为cmd添加一个环境变量_LIBCONTAINER_ INITTYPE=standard来告诉将来的容器进程(dockerinit )当前执行的是“创建”动作。设置这个标志是因为libcontainer还可以进人已有的容器执行子进程,即docker exec指令执行的效果。

    (4)将容器需要配置的namespace添加到。and的Cloneflags中,表示将来这个cmd要运行在上述namespace中。若需要加人user namespace,还要针对配置项进行用户映射,默认映射到宿主机的root用户。
    (5)将Container中的容器配置和Process中的Entrypoint信息合并为一份容器配置加人到ParentProcess当中。

    实际上,ParentProcess是一个接口,上述过程真正创建的是一个称为initProcess的具体实现对象。cmd, pipe, cgroup管理器和容器配置这4部分共同组成了一个initProcess。这个对象是用来“创建容器”所需的ParentProcess,这主要是为了同setnsProcess区分,后者的作用是进入已有容器。逻辑容器Container启动的过程实际上就是initProcess对象的构建过程,而构建initProcess则是为创建物理容器做准备。

  3. 用逻辑容器创建物理容器

    逻辑容器Container通过initProcess.start()方法新建物理容器的过程如下:

    (1) Docker daemon利用Golang的exec包执行initProcess.cmd,其效果等价于创建一个新的进程并为它设置namespace。这个cmd里指定的命令就是容器诞生时的第一个进程。对于libcontainer来说,这个命令来自于execdriver新建容器时加载daemon的initPath,即 Docked作目录下的/var/lib/docker/init/dockerinit-{version}文件。dockerinit进程}h在的namespace即用户为最终的Docker容器指定的namespace.

    (2)把容器进程dockerinit的PID加入到cgroup中管理。至此我们可以说dockerinit的容器隔离环境已经初步创建完成。

    (3)创建容器内部的网络设备,包括to和veth。

    (4)通过管道发送容器配置给容器内进程dockerinit.

    (5)通过管道等待dockerinit根据上述配置完成所有的初始化工作,或者出错返回。

    ​ dockerinit进程只有一个功能,那就是执行reexec.init(),该init方法做什么工作,是由对应的execdrive注册到reexe。当中的具体实现来决定的。对于libcontainer来说,这里要注册执行的是Factory当中的StartInitialization()。接下来的所有动作都发生在容器内部:

    • 创建pipe管道所需的文件描述符。
    • 通过管道获取ParentProcess传来的容器配置,如namespace、网络等信息。
    • 从配置信息中获取并设置容器内的环境变量,如区别新建容器和在已存在容器中执行命令的环境变量——LIBCONTAINER_INITTYPE.
    • 如果用户在docker run中指定了一ipc、-pid、-uts参数,则dockerinit还需要把自己加人到用户指定的上述namespace中。
    • 初始化网络设备,这些网络设备正是在ParentProcess中创建出来的lo和veth。这里的初始化工作包括:修改名称、分配MAC地址、设置MTU、添加IP地址和配置默认网关等。
    • 设置路由和RLIMIT参数。
    • 创建mount namespace,为挂载文件系统做准备。
    • 在上述mount namespace中设置挂载点,挂载rootfs和各类文件设备,比如/proc。然后通过
      pivot_root切换进程根路径到rootfs的根路径。
    • 写人hostname等,加载profile信息。
    • 比较当前进程的父进程ID与初始化进程一开始记录下来的父进程ID,如果不相同,说明父进程异常退出过,终止这个初始化进程。否则执行最后一步。
    • 最后一步,使用execv系统调用执行容器配置中的Args指定的命令。

    至此,容器的创建和启动过程宣告结束,上述全部过程可以通过图3-9来描述。

在这里插入图片描述

<think>好的,我需要回答用户关于Docker中IPC命名空间的配置和使用的问题。首先,我需要回顾一下Docker的namespace机制,特别是IPC命名空间的作用。根据引用[3],Docker使用namespace来实现隔离,其中IPC namespace负责隔离进程间通信的资源,比如System V IPC和POSIX消息队列。 接下来,我需要解释如何在Docker中配置IPC命名空间。默认情况下,Docker容器有自己的IPC命名空间,但用户可以通过`--ipc`参数来调整配置。例如,使用`--ipc=host`可以让容器共享宿主机的IPC命名空间,这在需要容器与宿主机或其他容器进行IPC通信时有用。不过这样做可能会带来安全风险,需要谨慎使用。 另外,用户可能想知道如何检查或管理容器的IPC资源。可以使用`docker inspect`命令查看容器的IPC配置,或者进入容器内部使用`ipcs`命令查看当前的IPC对象,比如共享内存段和消息队列。 还要提到一些常见问题,比如当容器需要访问宿主机或其他容器的共享内存时,应该如何配置。这时候可能需要使用`--ipc=container:<name>`来共享另一个容器的IPC命名空间。同时,需要注意权限问题,确保容器有足够的权限访问这些资源。 最后,根据用户提供的引用,特别是引用[3]和引用[4],需要确保引用正确,比如在解释namespace时提到libcontainer和runC的作用,以及在运行容器时使用`docker run`的选项来配置IPC参数。 总结起来,回答需要涵盖IPC命名空间的作用、配置方法、相关命令以及常见问题解决,同时正确引用提供的参考资料。</think>### Docker IPC 命名空间配置与使用 #### 1. IPC 命名空间的作用 Docker 使用 Linux 命名空间(namespace)实现资源隔离,其中 **IPC 命名空间**(Inter-Process Communication)用于隔离进程间通信资源,包括: - System V IPC 对象(如消息队列、共享内存、信号量) - POSIX 消息队列 默认情况下,每个容器拥有独立的 IPC 命名空间,确保容器内的进程间通信与其他容器或宿主机隔离[^3]。 --- #### 2. IPC 命名空间配置 通过 `docker run` 的 `--ipc` 参数可配置容器的 IPC 模式: | 模式 | 说明 | |-----------------------|--------------------------------------------------------------------| | `--ipc=private` | **默认模式**,容器拥有独立的 IPC 命名空间 | | `--ipc=host` | 共享宿主机的 IPC 命名空间(注意:可能引发安全风险)[^3] | | `--ipc=container:<name>` | 共享指定容器的 IPC 命名空间(适用于需要直接通信的容器组)[^4] | **示例**: ```bash # 启动一个使用宿主机 IPC 命名空间的容器 docker run -it --ipc=host ubuntu bash # 启动两个共享 IPC 命名空间的容器 docker run -d --name=container1 myapp docker run -it --ipc=container:container1 myapp2 ``` --- #### 3. 常见场景与问题 ##### (1) 共享内存通信 若容器需通过共享内存通信,需确保它们共享同一 IPC 命名空间: ```bash # 容器 A 创建共享内存 docker run -d --name=producer --ipc=shareable myproducer # 容器 B 共享容器 A 的 IPC 命名空间 docker run -it --ipc=container:producer myconsumer ``` ##### (2) 权限问题 共享 IPC 资源时,需注意权限配置: - 使用 `--cap-add=IPC_LOCK` 允许容器锁定内存 - 确保 SELinux/AppArmor 策略允许跨容器 IPC 访问 ##### (3) 调试 IPC 资源 查看容器内的 IPC 对象: ```bash # 进入容器 docker exec -it <container_id> bash # 查看 System V IPC 对象 ipcs -a ``` --- #### 4. 安全建议 - 避免滥用 `--ipc=host`,否则容器可能读取/修改宿主机的 IPC 资源 - 优先通过 API 或网络通信替代直接共享 IPC 命名空间 - 使用命名空间隔离实现最小权限原则 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值