在RHEL上运行带有主机装载卷(来自docker
和docker-compose
)的docker容器时,在容器启动之前,我观察到大量磁盘I/O(使用dstat
)。I/O与dockerd
进程相关联,我可以通过装载或删除主机卷来明显增加或减少I/O。如果我没有装载任何主机卷,容器将立即启动。如果我装载的卷覆盖了文件系统的很大一部分,那么I/O将非常重要,在我的情况下,大约需要20 Gb,大约需要三分钟才能启动容器。在某些情况下,这会导致docker-compose up
编排简单地超时。
一个典型的运行命令看起来像这个
docker run -it --rm --name my_container
-v /host/app/src:/app:ro,z # host volume defined here
-v my_ro_data:/data/read_only/files:ro,z # external named volume
-v /host/data/write:/data/container_output/files:z # another host volume
my/image:latest
无论卷是否为预定义的命名卷,也无论是否使用语法将其标记为只读,都会发生I/O。但当定义外部命名卷时,它看起来是这样的:
docker volume create
--driver local
--opt type=none
--opt o=bind
--opt device=/host/data/files
my_ro_data
我认为I/O与覆盖文件系统有关,但我找不到任何明确的解释来说明到底在写什么,在哪里写,以及如何优化配置以在容器启动前减少I/O需求。这显然不是整本书的内容,所以它似乎是某种差异?然而,假设我有某种大规模的数据管道,并且我想将我的容器指向具有TB文件的主机源或目标目录。。。如何装载对容器启动延迟影响较小的主机卷?
更新:根据@BMitch的指导,我专注于SELinux相关的":z"
标签。
简要历史:最初(大约在发布前一年),如果没有此标签,我们的RHEL w/SELinux服务器上的docker容器将无法访问装载的卷。尽管--volumes-from
是一个不同的cli选项,但它有其他来源在解决访问问题时引用的最佳解释:
SELinux等标签系统要求在装入容器的卷内容。没有标签,安全性系统可能会阻止容器内运行的进程使用内容。默认情况下,Docker不会更改标签集通过操作系统。要更改容器上下文中的标签,可以添加卷装载的两个后缀中的任意一个:z或:z。这些后缀告诉Docker重新标记共享卷上的文件对象。z选项告诉Docker两个容器共享卷内容。作为结果,Docker使用共享内容标签来标记内容。共享卷标允许所有容器读取/写入内容。Z选项告诉Docker使用私有非共享标签来标记内容。
此说明附带警告:elswhere:
使用Z绑定挂载/home或/usr等系统目录选项会使您的主机无法运行,您可能需要手动重新标记主机文件。
所以我使用了":z"
,有时使用":ro,z"
,一切都很好。
事实证明,这个标签导致了预启动磁盘I/O。我不太了解SELinux的安全性和标签,但我认为I/O实际上是在装载卷时更改文件标签,因此文件越多,磁盘I/O就越长。
我的观察是,去掉标签,不做任何其他事情,会导致同样的行为。这意味着docker引擎现在的默认行为是将SELinux装载的卷视为标记为":z"
。我相信这是一种可能在过去一年中引入的新行为。。。或其他系统更改。。。因为现在卷可以在没有标签的情况下访问(或者标签是永久的,允许后续的docker访问)。
然而,移除:z
并不能解决I/O和长启动时间的问题。然后我发现了这个github对话,它声称:z和:z都是潜在的危险选择,并评论道:
如果您的容器确实需要对系统目录进行更广泛的访问,然后在"docker run"中使用"--security-ot-label:disable"命令是一个更好的选择。请注意,使用上述选项而是将禁用该容器的SELinux检查。
所以我添加了这个选项,事实上,卷是可以访问的,并且没有(或最小)磁盘I/O和启动延迟。
话虽如此,我确实不理解--security-opt label:disable
的影响,并欢迎任何其他建议或解释。
有几种可能性,但不清楚是哪种基于您的问题。
如果它是覆盖文件系统,那将是出乎意料的,因为没有文件拷贝来设置它。这也不能反映您的描述,因为它只发生在卷上,并且每个容器都有一个覆盖文件系统(假设图形驱动程序设置为覆盖)。
对于无根docker守护进程(dockerd不是以root身份运行),您可以看到图形驱动程序切换到本机,这意味着它为每个层和容器复制映像文件系统,这非常昂贵。但在使用了大量磁盘空间的情况下,你们都会注意到这一点,而且在没有卷的情况下也会发生这种情况。
对于命名卷,当该卷为空并且使用命名卷创建容器时,docker将使用映像的内容初始化命名卷。这包括所有文件、权限、所有权和其他元数据。当命名卷已经有数据时,将跳过此初始化步骤,而主机卷不会执行此初始化步骤。
如果您的问题是特定于主机卷的,那么我唯一能想到的就是selinux标签设置为";z";选项。否则,默认情况下,命名卷和主机卷都是Linux绑定装载,而且这些操作通常非常快速。
最后,尚不清楚";在集装箱下水之前;包括应用程序在容器内进入就绪状态所花费的时间。要将docker步骤与容器内的应用程序分离,请将应用程序更改为类似--entrypoint true
的简单程序,这将导致容器在创建后立即退出。如果需要30秒才能退出,你知道docker很慢,但如果它立即进入退出状态,那么问题与docker无关,问题是你在容器内运行的内容。