如何拆分60GB的Docker层以获得更好的性能?

bzzcjhmw  于 11个月前  发布在  Docker
关注(0)|答案(1)|浏览(102)

我正在Docker镜像中安装AMD/Xilinx Vivado。即使是几乎最小的安装也是60 GB大小,导致29 GB压缩镜像。完整安装大约是150 GB...
图像由以下人员创建:
1.从Docker Hub使用Debian Bookworm(debian:bookworm-slim
1.添加Python 3.12(实际使用Docker Hub的python:3.12-slim
1.通过apt install ...添加AMD Vivado所需的必要软件包。
1.通过./xsetup ...安装AMD Vivado作为几乎最小的设置。
当安装软件时,我注意到:

*上传问题

  • dockerd在1GbE网络设置中的推送速度约为135 Mb/s。
    *下载问题
  • dockerd最多支持3个并行下载线程。与docker push相比,docker pull的速度达到了975 Mb/s(1GbE时的最高速度)。参见Docker parallel operations limit
  • 下载的图层不是动态提取的。它需要完整下载才能提取图层。

我发现了一些提示,将大层拆分为多个层将提高性能。所以我使用了一个多阶段构建过程,其中Vivado安装在stage-1中,然后stage-2用于挂载stage-1中的层,RUN --mount... cp -r SRC DEST用于创建1.5到8.8 GB之间的15层。
结果表明:

    • 对于docker push *
  • dockerd限制为最多5个并行上传线程。

参见Docker parallel operations limit

  • 并行上传的结果是大约550 Mb/s,因此是单个上传线程速度的5倍。
    • 对于docker pull *
  • 下载一个60 GB的大图层与使用3个并行下载线程下载15个图层一样慢。下载需要5分钟。此过程受到1GbE网络设置的限制。
  • 一个15层的60 GB的图像是快速的,因为完成的层是由另一个单一的(!)线程提取的,而其他层仍然在下载。总体上,它花了5分钟下载时间+ 2分钟在所有层下载后提取剩余的层。
  • 一个大的分层60 GB的图像是两倍慢,因为它在5分钟内下载相同的数据,但然后运行一个单线程提取需要8分钟。总共13对7分钟。
    我的问题是:
  • 谁来自动(脚本化)将一层(例如60 GB)拆分为更小的层(例如2到6 GB)?

我现在想到了这些第一个想法:

  • 启动镜像并在容器中执行脚本,递归遍历Vivado安装目录。通过使用du -sh,目录被分类为:
  • ≥8 GB,开始另一个递归并吐出其内容
  • ≥1 GB,将目录或单个文件添加到层列表中。
  • 否则,收集所有剩余的文件/目录的小网站在一个单一的层
  • 结果,创建了列表的列表(或列表的字典),其中外部层可以被迭代以创建图像层,而内部层指定了RUN ... cp ...的参数以将文件从先前阶段复制到新层中。
  • 由于RUN命令不能在docker文件中的LOOP中调用,因此需要编写一个脚本来编写一个辅助Dockerfile,其中包含n个RUN调用。

手动创建Dockerfile以检查性能差异:

ARG REGISTRY
ARG NAMESPACE
ARG IMAGE
ARG IMAGE_TAG
ARG VIVADO_VERSION

FROM ${REGISTRY}/${NAMESPACE}/${IMAGE}:${IMAGE_TAG} as base
ARG VIVADO_VERSION

# Install further dependencies for Vivado
RUN --mount=type=bind,target=/context \
    apt-get update \
 && xargs -a /context/Vivado.packages apt-get install -y --no-install-recommends \
 && rm -rf /var/lib/apt/lists/* \
 && apt-get clean

FROM base as monolithic
ARG VIVADO_VERSION

RUN --mount=type=bind,target=/context \
    --mount=type=bind,from=vivado,target=/Install \
    cd /Install; \
    ./xsetup --batch Install -c /context/Vivado.${VIVADO_VERSION}.cfg --agree XilinxEULA,3rdPartyEULA

FROM base
ARG VIVADO_VERSION

RUN mkdir -p /opt/Xilinx/Vivado/${VIVADO_VERSION}/data/parts/xilinx

RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/gnu                           /opt/Xilinx/Vivado/${VIVADO_VERSION}
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/ids_lite                      /opt/Xilinx/Vivado/${VIVADO_VERSION}
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/lib                           /opt/Xilinx/Vivado/${VIVADO_VERSION}
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/tps                           /opt/Xilinx/Vivado/${VIVADO_VERSION}
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/data/secureip                 /opt/Xilinx/Vivado/${VIVADO_VERSION}/data
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/data/xsim                     /opt/Xilinx/Vivado/${VIVADO_VERSION}/data
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/data/deca                     /opt/Xilinx/Vivado/${VIVADO_VERSION}/data
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/data/ip                       /opt/Xilinx/Vivado/${VIVADO_VERSION}/data
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/data/simmodels                /opt/Xilinx/Vivado/${VIVADO_VERSION}/data
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/data/parts/xilinx/zynquplus   /opt/Xilinx/Vivado/${VIVADO_VERSION}/data/parts/xilinx
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/data/parts/xilinx/virtexuplus /opt/Xilinx/Vivado/${VIVADO_VERSION}/data/parts/xilinx
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/data/parts/xilinx/kintexuplus /opt/Xilinx/Vivado/${VIVADO_VERSION}/data/parts/xilinx
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -r /Install/data/parts/xilinx/common      /opt/Xilinx/Vivado/${VIVADO_VERSION}/data/parts/xilinx
RUN --mount=type=bind,from=monolithic,source=/opt/Xilinx/Vivado/${VIVADO_VERSION},target=/Install cp -ru /Install/*                            /opt/Xilinx/Vivado/${VIVADO_VERSION}

# Configure Vivado tools
COPY FlexLM.config.sh Vivado.config.sh /tools/GitLab-CI-Scripts/

字符串

9rbhqvlz

9rbhqvlz1#

要自动化将大型Docker层拆分为较小层的过程,您可以创建一个Python脚本,该脚本遍历Vivado安装目录,根据目录的大小对其进行分类,然后使用多个RUN命令生成一个Dockerfile,以将这些目录复制到单独的层中。

+-------------------+        +-------------------+        +----------------------+
| Vivado Directory  |   -->  | Python Script     |   -->  | Generated Dockerfile |
| (60GB)            |        | (Categorization)  |        | (Multiple RUN cmds)  |
+-------------------+        +-------------------+        +----------------------+

字符串
VivadoDockerLayerSplitter.py脚本如下:

import os
import subprocess

def get_dir_size(start_path):
    total_size = 0
    for dirpath, dirnames, filenames in os.walk(start_path):
        for f in filenames:
            fp = os.path.join(dirpath, f)
            total_size += os.path.getsize(fp)
    return total_size

def create_dockerfile(vivado_dir, output_dockerfile):
    layers = {}
    for root, dirs, files in os.walk(vivado_dir):
        size = get_dir_size(root)
        if size >= 8 * 1024**3:  # Greater than 8 GB
            for dir in dirs:
                sub_dir = os.path.join(root, dir)
                layers[sub_dir] = get_dir_size(sub_dir)
        elif size >= 1 * 1024**3:  # Greater than 1 GB
            layers[root] = size

    with open(output_dockerfile, 'w') as f:
        f.write("FROM base\n")
        for layer_dir, size in layers.items():
            rel_path = os.path.relpath(layer_dir, vivado_dir)
            f.write(f"RUN --mount=type=bind,from=monolithic,source={layer_dir},target=/Install cp -r /Install/{rel_path} /opt/Xilinx/Vivado\n")

vivado_install_dir = "/path/to/vivado/installation"
output_file = "Dockerfile.generated"
create_dockerfile(vivado_install_dir, output_file)


这应该有助于自动化将大型Docker层拆分为更易于管理的大小的过程,从而可能提高上传和下载性能。
由于RUN命令不能在docker文件中的LOOP中调用,因此需要编写一个脚本来编写二级Dockerfile,其中包含n个RUN调用
对于每个需要在单独层中的已识别目录或文件,脚本会在生成的Dockerfile中写入一个不同的RUN命令。这将通过在docker build进程启动之前 * 预处理和准备所有必要的命令来规避Dockerfile限制。
这些RUN命令用于构建过程的第二阶段。它们将Vivado安装的不同部分从第一阶段(安装它的地方)复制到第二阶段,这将创建多个层。
该脚本创建了一个Dockerfile,用于多阶段构建过程的第二阶段,其中它将执行层拆分操作作为Docker构建的一部分。

相关问题