Docker 如何减少多阶段构建重复步骤的时间成本问题?

Docker 如何减少多阶段构建重复步骤的时间成本问题?,docker,Docker,我有一个go应用程序,它取决于cgo。构建时,它需要libnaudev、libzmq3-dev、libczmq-dev,运行时还需要以上三个包 目前,我使用下一个多阶段构建:golang构建环境作为第一阶段&debian slim作为第二阶段。但是你可以看到3个软件包安装了两次,这浪费了时间(以后我可能会添加更多这样的软件包) 当然,我可以放弃多阶段构建,只需使用golang1.12.9-buster进行构建,然后继续运行,但这将使最终运行映像更大(这是多阶段构建的优势) 我是错过了什么,还是不

我有一个go应用程序,它取决于cgo。构建时,它需要
libnaudev、libzmq3-dev、libczmq-dev
,运行时还需要以上三个包

目前,我使用下一个多阶段构建:
golang构建环境
作为第一阶段&
debian slim
作为第二阶段。但是你可以看到3个软件包安装了两次,这浪费了时间(以后我可能会添加更多这样的软件包)

当然,我可以放弃多阶段构建,只需使用
golang1.12.9-buster
进行构建,然后继续运行,但这将使最终运行映像更大(这是多阶段构建的优势)


我是错过了什么,还是不得不在以上两者之间做出选择?

副本处步骤,只要源代码发生更改,缓存就会崩溃,您将再次运行后面的所有步骤。您可以重新排序这些步骤,以允许docker缓存依赖项的安装。您还可以将
apt get install
命令合并为一个命令,以减少处理package manager数据库的开销

FROM golang:1.12.9-buster AS builder

WORKDIR /src/pigeon

RUN apt-get update \
 && apt-get install -y --no-install-recommends \
     libsodium-dev \
     libzmq3-dev \
     libczmq-dev

COPY . .

RUN go build cmd/main/pgd.go


FROM debian:buster-slim

RUN apt-get update \
 && apt-get install -y --no-install-recommends \
     libsodium-dev \
     libzmq3-dev \
     libczmq-dev \
     python3 \
     python3-pip \
 && pip3 install jinja2

WORKDIR /root/

RUN mkdir logger

COPY --from=builder /src/pigeon/pgd .
COPY --from=builder /src/pigeon/logger logger

CMD ["./pgd"]
您仍将安装两次软件包,但现在这些安装将被缓存以供将来的版本使用。重用库的安装的方法是重新排序步骤,在公共基础映像中安装库,然后在构建阶段安装go编译器,但这几乎肯定比两次安装库的开销更大

使用BuildKit,您可以使用实验语法在构建之间共享apt缓存,但这要求所有构建都使用BuildKit(语法不向后兼容),并修改docker的Debian映像以保留apt包缓存。从BuildKit实验文档中,apt有以下示例:

# syntax = docker/dockerfile:experimental
FROM ubuntu
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt --mount=type=cache,target=/var/lib/apt \
  apt update && apt install -y gcc

副本处步骤,只要源代码发生更改,缓存就会崩溃,您将再次运行后面的所有步骤。您可以重新排序这些步骤,以允许docker缓存依赖项的安装。您还可以将
apt get install
命令合并为一个命令,以减少处理package manager数据库的开销

FROM golang:1.12.9-buster AS builder

WORKDIR /src/pigeon

RUN apt-get update \
 && apt-get install -y --no-install-recommends \
     libsodium-dev \
     libzmq3-dev \
     libczmq-dev

COPY . .

RUN go build cmd/main/pgd.go


FROM debian:buster-slim

RUN apt-get update \
 && apt-get install -y --no-install-recommends \
     libsodium-dev \
     libzmq3-dev \
     libczmq-dev \
     python3 \
     python3-pip \
 && pip3 install jinja2

WORKDIR /root/

RUN mkdir logger

COPY --from=builder /src/pigeon/pgd .
COPY --from=builder /src/pigeon/logger logger

CMD ["./pgd"]
您仍将安装两次软件包,但现在这些安装将被缓存以供将来的版本使用。重用库的安装的方法是重新排序步骤,在公共基础映像中安装库,然后在构建阶段安装go编译器,但这几乎肯定比两次安装库的开销更大

使用BuildKit,您可以使用实验语法在构建之间共享apt缓存,但这要求所有构建都使用BuildKit(语法不向后兼容),并修改docker的Debian映像以保留apt包缓存。从BuildKit实验文档中,apt有以下示例:

# syntax = docker/dockerfile:experimental
FROM ubuntu
RUN rm -f /etc/apt/apt.conf.d/docker-clean; echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache
RUN --mount=type=cache,target=/var/cache/apt --mount=type=cache,target=/var/lib/apt \
  apt update && apt install -y gcc

以下是我对你的问题的看法:

FROM debian:buster-slim as base

RUN mkdir /debs /debs_tmp \
    && chmod 777 /debs /debs_tmp

WORKDIR /debs
RUN apt-get update \
    && apt-get install -y -d \
            --no-install-recommends \
            -o dir::cache::archives="/debs_tmp/" \
        libsodium-dev \
        libzmq3-dev \
        libczmq-dev \
    && mv /debs_tmp/*.deb /debs \
    && rm -rf /debs_tmp \
    && apt-get install -y --no-install-recommends \
        python3 \
        python3-pip \
    && pip3 install jinja2 \
    && rm -rf /var/lib/apt/lists/*


##################

FROM golang:1.12.9-buster AS builder

COPY --from=base /debs /debs
WORKDIR /debs

RUN dpkg -i *.deb

WORKDIR /src/pigeon
COPY . .

RUN go build cmd/main/pgd.go

##################

FROM base

RUN rm -rf /debs

WORKDIR /root/
RUN mkdir logger

COPY --from=builder /src/pigeon/pgd .
COPY --from=builder /src/pigeon/logger logger

CMD ["./pgd"]
您可以在临时文件夹中下载所需的软件包,将DEB移动到新位置,最后在下一阶段复制DEB。最后,您只需使用您创建的第一个图像

顺便说一句,容器将作为根运行。这可能是一个问题,取决于软件做什么,你可能想考虑使用没有“权力”的用户。


编辑:很抱歉进行了编辑,但我在本地运行了几个示例,没有准备好go脚本。

以下是我对您的问题的看法:

FROM debian:buster-slim as base

RUN mkdir /debs /debs_tmp \
    && chmod 777 /debs /debs_tmp

WORKDIR /debs
RUN apt-get update \
    && apt-get install -y -d \
            --no-install-recommends \
            -o dir::cache::archives="/debs_tmp/" \
        libsodium-dev \
        libzmq3-dev \
        libczmq-dev \
    && mv /debs_tmp/*.deb /debs \
    && rm -rf /debs_tmp \
    && apt-get install -y --no-install-recommends \
        python3 \
        python3-pip \
    && pip3 install jinja2 \
    && rm -rf /var/lib/apt/lists/*


##################

FROM golang:1.12.9-buster AS builder

COPY --from=base /debs /debs
WORKDIR /debs

RUN dpkg -i *.deb

WORKDIR /src/pigeon
COPY . .

RUN go build cmd/main/pgd.go

##################

FROM base

RUN rm -rf /debs

WORKDIR /root/
RUN mkdir logger

COPY --from=builder /src/pigeon/pgd .
COPY --from=builder /src/pigeon/logger logger

CMD ["./pgd"]
您可以在临时文件夹中下载所需的软件包,将DEB移动到新位置,最后在下一阶段复制DEB。最后,您只需使用您创建的第一个图像

顺便说一句,容器将作为根运行。这可能是一个问题,取决于软件做什么,你可能想考虑使用没有“权力”的用户。


编辑:很抱歉进行了编辑,但我在本地运行了几个示例,没有准备好go脚本。

请看一些人给我的建议,但是一个软件包安装可能会导致其他依赖软件包安装,我很难将所有包从一个阶段复制到另一个阶段,尽管我可以使用
dpkg-L
查找所有内容。请看一些人给我的建议,但是一个包的安装可能会导致其他依赖性包的安装,虽然我可以使用
dpkg-L
查找所有内容,但将所有包从一个阶段复制到另一个阶段对我来说非常困难。类似于buildkit apt cache,但此解决方案不需要
实验性
支持。类似于buildkit apt cache,但此解决方案不需要
实验性
支持。