【问题标题】:how to merge Docker's layers of image and slim down the image file如何合并 Docker 的镜像层并瘦身镜像文件
【发布时间】:2021-06-29 12:09:25
【问题描述】:

docker image inspect <name> 给我 16GB

大约有 20 层

当我以 root 身份登录时,这 du -hs / 只给我看 2GB

仅供参考,Dockerfile 中已经有非常多行的 RUN 命令。

我可以将所有层压缩成一层而不接触 Dockerfile、重建等吗?

或者可能通过向 Dockerfile 添加额外的操作来清除/改进缓存

Dockerfile

FROM heroku/heroku:18

ENV PYENV_ROOT="/pyenv"
ENV PATH="/pyenv/shims:/pyenv/bin:$PATH"
ENV PYTHON_VERSION 3.5.6
ENV GPG_KEY <value>
ENV PYTHONUNBUFFERED 1
ENV TERM xterm
ENV EDITOR vim

RUN apt-get update && apt-get install -y \
    build-essential \
    gdal-bin \
    binutils \
    iputils-ping \
    libjpeg8 \
    libproj-dev \
    libjpeg8-dev \
    libtiff-dev \
    zlib1g-dev \
    libfreetype6-dev \
    liblcms2-dev \
    libxml2-dev \
    libxslt1-dev \
    libssl-dev \
    libncurses5-dev \
    virtualenv \
    python-pip \
    python3-pip \
    python-dev \
    libmysqlclient-dev \
    mysql-client-5.7 \
    libpq-dev \
    libcurl4-gnutls-dev \
    libgnutls28-dev \
    libbz2-dev \
    tig \
    git \
    vim \
    nano \
    tmux \
    tmuxinator \
    fish \
    sudo \
    libnet-ifconfig-wrapper-perl \
    ruby \
    libssl-dev \
    nodejs \
    strace \
    tcpdump \
    # npm & grunt
    && curl -L https://npmjs.com/install.sh | sh \
    && npm install -g grunt-cli grunt \
    # ruby & foreman
    && gem install foreman \
    # installing pyenv
    && curl https://raw.githubusercontent.com/yyuu/pyenv-installer/master/bin/pyenv-installer | bash

COPY . /app
COPY ./requirements /requirements
COPY ./requirements.txt /requirements.txt
COPY ./docker/docker_compose/django/foreman.sh /foreman.sh
COPY ./docker/docker_compose/django/Procfile /Procfile
COPY ./docker/docker_compose/django/entrypoint.sh /entrypoint.sh

# ADD sudoer user django with password django
RUN groupadd -r django -g 1000 && \
    useradd -ms /usr/bin/fish -p $(openssl passwd -1 django) --uid 1000 --gid 1000 -r -g django django && \
    usermod -a -G sudo django && \
    chown -R django:django /app 

COPY --chown=django:django ./docker/docker_compose/django/fish /home/django/.config/fish
COPY --chown=django:django ./docker/docker_compose/django/tmuxinator /home/django/.tmuxinator
COPY ./docker/docker_compose/django/fish /root/.config/fish

WORKDIR /app

RUN sed -i 's/\r//' /entrypoint.sh \
    && sed -i 's/\r//' /foreman.sh \
    && chmod +x /entrypoint.sh \
    && chown django /entrypoint.sh \
    && chmod +x /foreman.sh \
    && chown django /foreman.sh \
    && chown -R django:django /home/django/ \
    && pyenv install ${PYTHON_VERSION%%} \
    && mkdir -p /app/log \
    && pyenv global ${PYTHON_VERSION%%} \
    && pyenv rehash \
    && ${PYENV_ROOT%%}/versions/${PYTHON_VERSION%%}/bin/pip install -U pip \
    && ${PYENV_ROOT%%}/versions/${PYTHON_VERSION%%}/bin/pip install -r /requirements.txt \
    && chown -R django:django /pyenv/ \
    && ${PYENV_ROOT%%}/versions/${PYTHON_VERSION%%}/bin/pip install -r /requirements/dev_requirements.txt

# this user receives ENVs from the top
USER django

ENTRYPOINT ["/entrypoint.sh"]

到目前为止我已经尝试过:

docker build 的实验模式中的--squash 选项不适合我。该 Dockerfile 是 docker-compose 中的多个 Dockerfile 之一。

我也检查了这个: https://github.com/jwilder/docker-squash

但似乎 docker load 无法加载压缩的图像。 另外,那个壁球给了我 8GB(距离预期的 ~2GB 还很远)

docker save <image_id> | docker-squash -t latest_tiny | docker load

回答后更新:

当我添加这个时:

&& apt-get autoremove \             # ? to consider
&& apt-get clean \                  # ? to consider
&& rm -rf /var/lib/apt/lists/*

apt-get--no-cache-dir 到每个 pip,结果是 72GB(是的,甚至更多 - docker imagespip 命令之前显示 36GB,最终大小为 72GB)。

我的工作目录很清晰(关于COPY)。 du -hs /(作为 root)仍然有 2GB。并且在重建之前删除了所有图像。

按照@Mihai 的方法,我能够将图像从 16GB 缩小到 9GB。

【问题讨论】:

  • 这可能是Dockerfile 效率低下的问题。例如,如果您在 1 层中添加文件并在另一层中删除它,这不会显示在 du 中,但它仍然是图像层的一部分。可以发Dockerfile吗?
  • 据我所知,回想起来没有办法减小尺寸
  • 您正在构建什么项目,需要多个语言运行时、多个文本编辑器、stracetcpdumpgit 来运行它?将此依赖项列表缩减为仅运行应用程序所需的内容,仅此而已,它会小得多。
  • @DavidMaze:我知道这一点,但实际成本只有 2GB,我们只讨论缓存和 docker 层中差异的大小。该 Dockerfile 是面向开发人员的版本,是的 - 为他们添加了一些好东西。

标签: docker


【解决方案1】:

有一个简单的技巧可以摆脱中间层。它也会缩小尺寸,但具体多少取决于它的构建方式。

像这样创建一个 Dockerfile:

FROM your_image as initial

FROM your_image_base

COPY --from=initial / /

your_image_base 应该类似于 'alpine' - 因此是您的图像及其父级的最小图像。

现在构建映像并检查历史记录和大小:

docker build -t your-image:2.0 .
docker image history your-image:2.0
docker image ls

这样您就可以创建一个新的 Dockerfile(如果您的进程可以接受的话),而无需触及初始 Dockerfile。

如果这能解决您的问题,请告诉我。

查看 Dockerfile 后更新:

也许我想念它,但我没有看到您在执行安装后清理 apt-get 缓存。您的大型 RUN 命令应在同一行以“&& rm -rf /var/lib/apt/lists/*”结尾,这样它就不会将整个缓存存储在图层上。

【讨论】:

    【解决方案2】:

    就像米海说的那样,一定要在你的主要运行命令的末尾添加&amp;&amp; rm -rf /var/lib/apt/lists/*。可能有帮助的另一件事(取决于您的依赖项有多大)是使用 pip 安装 --no-cache-dir 选项。另外,请确保您了解 build context 并考虑使用 .dockerignore 或将上下文发送到另一个目录(完全取决于您的目录设置方式)

    我也有幸使用dive 探索图像。老实说,这看起来像一个很大的图像,所以不确定你能把它弄下来多少

    【讨论】:

      【解决方案3】:

      很简单,用就行了

      docker 提交 YOUR_CONTAINER_ID NEW_IMAGE_ID

      docker 会扔掉中间层,你丢失了历史但大小很小

      【讨论】:

      • 实际上,对我来说,新图像仍然很大。它仍然包含所有文件系统层和历史记录。运行docker image inspect NEW_IMAGE_ID|jq '.[].RootFS'docker image history NEW_IMAGE_ID 来查看。
      猜你喜欢
      • 2021-03-09
      • 2015-03-14
      • 1970-01-01
      • 2019-06-22
      • 2019-02-12
      • 1970-01-01
      • 2020-01-28
      • 2022-10-15
      相关资源
      最近更新 更多