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

时间:2019-09-05 09:27:30

标签: docker

我有一个go应​​用程序,它取决于cgo。构建时,它需要libsodium-dev, libzmq3-dev, libczmq-dev,运行时,它也需要以上三个软件包。

当前,我使用下一个多阶段构建:a golang build environment作为第一阶段,a debian slim作为第二阶段。但是您可能会看到3个软件包安装了两次,这浪费了时间(稍后我可能会添加更多此类软件包)。

FROM golang:1.12.9-buster AS builder

WORKDIR /src/pigeon

COPY . .

RUN apt-get update && \
  apt-get install -y --no-install-recommends libsodium-dev && \
  apt-get install -y --no-install-recommends libzmq3-dev && \
  apt-get install -y --no-install-recommends libczmq-dev && \
  go build cmd/main/pgd.go


FROM debian:buster-slim

RUN apt-get update && \
  apt-get install -y --no-install-recommends libsodium-dev && \
  apt-get install -y --no-install-recommends libzmq3-dev && \
  apt-get install -y --no-install-recommends libczmq-dev && \
  apt-get install -y --no-install-recommends python3 && \
  apt-get install -y --no-install-recommends 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"]

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

我想念东西还是不得不在以上两者之间做出选择?

2 个答案:

答案 0 :(得分:1)

COPY . .步骤中,只要您的源代码发生更改,缓存就会崩溃,您将再次运行所有后续步骤。您可以重新排列步骤顺序,以允许docker缓存您的依赖项的安装。您还可以将apt-get install命令合并为一个,以减少处理程序包管理器数据库的开销。

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

https://github.com/moby/buildkit/blob/master/frontend/dockerfile/docs/experimental.md

答案 1 :(得分:1)

这是我对您的问题的看法:

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移到新位置,最后在下一阶段COPY Debs。最后,您只需使用创建的第一张图片。

顺便说一句,这些容器将以root身份运行。取决于软件的功能,这可能是一个问题,您可能要考虑使用没有“权力”的用户。

编辑:对所做的编辑感到抱歉,但我在本地运行了两个示例,但尚未准备好运行脚本。