如何减小Docker镜像的大小?

12

我有一个Node.js应用程序,正在以docker容器的形式运行。以下是该应用程序的Dockerfile。

FROM ubuntu

ARG ENVIRONMENT
ARG PORT

RUN apt-get update -qq
RUN apt-get install -y build-essential nodejs npm nodejs-legacy vim

RUN mkdir /consumer_portal
ADD . /consumer_portal
WORKDIR /consumer_portal

RUN npm install -g express
RUN npm install -g path
RUN npm cache clean
RUN npm install

EXPOSE $PORT

ENTRYPOINT [ "node",  "server.js" ]
CMD [ $PORT, $ENVIRONMENT ]

我能否在这个Dockerfile中进行修改以减小docker镜像大小?


不要安装vim?在外部构建镜像? - user933161
3
使用Alpine基础镜像。 - Aurélien
@Aurélien 你的意思是说anapsix/alpine-java吗?如果不用Java,我可以用什么来代替Node呢? - user7789560
1
Alpine是一个极简的Linux发行版。例如,https://hub.docker.com/r/mhart/alpine-node/。 - Roman Kiselenko
我发现这个链接非常有用:https://learnk8s.io/blog/smaller-docker-images 此外,这篇文章中的示例是针对Node.js镜像的。 - Ashik
7个回答

10

使用官方的node alpine映像作为基础映像,正如大多数人建议的那样,这是一个简单的解决方案,可以减小映像的总大小,因为即使是基础的alpine映像与基础的ubuntu映像相比也要小得多。

Dockerfile可以像这样:

FROM node:alpine

ARG ENVIRONMENT
ARG PORT

RUN mkdir /consumer_portal \
    && npm install -g express path

COPY . /consumer_portal
WORKDIR /consumer_portal

RUN npm cache clean \
    && npm install

EXPOSE $PORT

CMD [ "node",  "server.js" ]

这两个镜像基本相同,使用起来也应该如预期那样正常。大多数你在ubuntu镜像下用的命令也可以同样在alpine镜像下使用。

我添加mock-data以创建一个类似的项目,结果是ubuntu镜像大小为491 MB,而alpine版本仅有62.5 MB:

REPOSITORY   TAG       IMAGE ID        CREATED          SIZE
alpinefoo    latest    8ca6f338475e    5 minutes ago    62.5MB
ubuntufoo    latest    38620a1bd5a6    6 minutes ago    491MB

2
只有当图像大小的主要贡献来自基础层时才是真实的(例如,数据科学家发现通常并非如此)。 - jtlz2

6
尝试将所有RUN指令打包在一起,这将减少中间图像的数量。(但不会减小最终图像的大小)。
apt-get update之后添加rm -rf /var/lib/apt/lists/*将通过删除所有无用的apt-get内容来减小镜像大小。
您还可以在最后一个RUN指令中删除vim
FROM ubuntu

ARG ENVIRONMENT
ARG PORT

RUN apt-get update \
    && apt-get install -y build-essential nodejs npm nodejs-legacy vim \
    && rm -rf /var/lib/apt/lists/* \
    && mkdir /consumer_portal

ADD . /consumer_portal
WORKDIR /consumer_portal

RUN npm install -g express \
    && npm install -g path \
    && npm cache clean \
    && npm install

EXPOSE $PORT

ENTRYPOINT [ "node",  "server.js" ]
CMD [ $PORT, $ENVIRONMENT ]

谢谢你的回答,但对我没有用,只有少量 MB 被减少了。 - user7789560
如果您想使用Ubuntu作为基础镜像,您将无法再减少更多的MB。也许您应该考虑将基础镜像更改为Alpine或类似的东西。 - kstromeiraos
@JoséAntonioLópez 不了解alpine,但随意编辑我的帖子以改进答案 ;) - Anthony Raymond
@AnandDeshmukh 除了从Ubuntu切换到其他操作系统,您不能太大程度上减小图像尺寸。 - Anthony Raymond
1
另一个选择是使用轻量级基础容器,例如Alpine Linux。 - Ashan

4
考虑使用以下方法: 在使用apt-get安装软件包时,可以使用--no-install-recommends以减小映像文件的大小。更多信息请参见本博客文章。
有一篇很好的博客讲述了几个步骤来减小映像文件的大小。
减小Docker映像文件大小的技巧: https://hackernoon.com/tips-to-reduce-docker-image-sizes-876095da3b34

3

1)转移到Alpine可能是最佳选择。我将一个Ubuntu docker文件移植到了Alpine上,容量从1.5GB减少到了585MB。我遵循了这些指示。请注意,您将使用apk而不是apt-get,并且Alpine包名称有点不同。

2)通过合并运行命令(每个新的运行命令都会创建一个新的层),也可以减少图层。

RUN apt-get update -qq && apt-get install -y build-essential nodejs npm nodejs-legacy vim
RUN npm install -g express path && npm cache clean && npm install

3) 你可能也对多阶段构建感兴趣,其中你只复制必要的组件到最终镜像中。


1

第一步生成的图像,别名:builder

将第一步图像的产物复制到当前图像中,仅使用一个图像层,节省了上一步的图像层数。

FROM node:10-alpine as builder
WORKDIR /web-console 
ADD . /web-console
RUN npm install
RUN npm run build 

FROM node:10-alpine
WORKDIR /web-console
COPY --from=builder . /web-console
CMD ["npm", "run", "docker-start"]

这是一个使用Maven的Java示例:2个步骤。
FROM maven:3.5.2-jdk-8-alpine as BUILD
WORKDIR /app
COPY pom.xml /app/
COPY src /app/src/
RUN mvn clean package -DskipTests

FROM alpine
WORKDIR /app
COPY --from=BUILD /app/target/*.jar  application.jar
ENTRYPOINT ["java", "-jar", "/application.jar"]

0

容器的图像大小是一个需要妥善处理的问题。

有人建议使用alpine发行版来节省空间。

原则上这是一个好建议,因为有一个nodejs镜像可以在alpine上使用。

但是你必须小心,在这里,因为你必须构建所有的二进制文件。即使node_modules通常只包含javascript包,在某些情况下,你也需要构建二进制文件。

如果你的dockerfile现在正常工作,那么这不应该是你的问题,但是当你从一个ubuntu转移到另一种类型的镜像时,最好记住你将来需要使用的所有二进制文件都必须在alpine镜像中编译。

说了这么多,你应该考虑在选择缩小图像大小的位置之前如何使用你的图像。

你的应用程序是一个单独的应用程序,它独自存在于一个没有其他节点应用程序的容器中吗?

如果答案是否定的,你应该知道本地docker注册表中每个图像的大小不计入总使用大小的汇总。

相反,您必须将每个图像拆分为基本层,并对每个唯一层进行求和。

我在这里的意思是,如果您有许多在一个节点上运行的节点应用程序,则单个图像并不重要。

您可以通过将 node_modules 共享为包含所有所需依赖项的卷来节省空间。

或者更好的方法是,从官方 nodejs 图像开始创建一个中间图像,其中包含您应用程序的依赖项根。例如expressjspath。然后在每个应用程序映像中安装专用依赖项。

因此,您可以获得共享常见层的优势,从而减少本地Docker注册表的总使用大小。

小的注意事项

您不需要在容器映像内全局安装 express path

您真的需要在容器中安装 vim 吗? 请注意,即使在开发中,修改容器也不安全。您可以使用卷将资源指向服务器文件系统上的资源。 或者在运行时复制进/出文件或文件夹。 如果您只需要阅读某些内容,请使用类似less,more或cat的命令。


0
如果你使用的是Ubuntu,那么一个明智的做法是这样做。
RUN apt-get update && apt-get install -y \
    build-essential \
    cwhatever-you-want \
    vim \
 && rm -rf /var/lib/apt/lists/*

最后一行会清除很多 :) 你应该总是在同一行中使用apt-get update,否则如果你添加另一个库来安装,它将被缓存并且不会在下一次构建时触发。

是的,没错。我基于Ubuntu操作系统。 - user7789560
您能帮我纠正一下Dockerfile吗?我应该把你提供的那段代码放在哪里? - user7789560
1
当我在Dockerfile的第一行尝试使用FROM mhart/alpine-node:base-6代替ubuntu时,我的Docker镜像本身无法构建。 - user7789560
Alpine有些不同,请参见http://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management#Update_the_Package_list - user2915097
Anthony 给你展示了一个示例帖子,比我的好...不会再复制粘贴了,因为它会是一样的 :) - barat

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接