【问题标题】:How to flatten a Docker image?如何扁平化 Docker 镜像?
【发布时间】:2014-05-07 22:58:38
【问题描述】:

我制作了一个相当大的 Docker 容器。当我提交容器以创建图像时,图像大约为 7.8 GB。但是当我将export 容器(不是save 图像!)到一个压缩包并重新导入它时,图像只有 3 GB 大。当然历史丢失了,但这对我来说没问题,因为在我看来图像已经“完成”并且可以部署了。

如何在不将其导出到磁盘并再次导入的情况下展平图像/容器?并且:这样做是一个明智的想法还是我错过了一些重要的点?

【问题讨论】:

  • 您是否使用--rm 选项构建?这将删除中间图像。还是我误解了这个问题?
  • 还有一些其他技巧可以让镜像更小:在一个RUN 中调用一堆安装命令,删除不需要的ubuntu 包,等等。 请参阅github.com/dckc/ipython-docker/blob/master/Dockerfile 了解一个很好的例子。

标签: docker


【解决方案1】:

从 Docker 1.13 开始,您可以使用 --squash 标志。


1.13 版之前:

据我所知,您不能使用 Docker api。正如您自己已经提到的,docker exportdocker import 专为这种情况而设计。

如果您不想保存到磁盘,您可以将导出的输出流通过管道传输到导入的输入流中。我没有测试过这个,但是试试

docker export red_panda | docker import - exampleimagelocal:new

【讨论】:

  • 我只是使用“Docker 版本 1.1.1,构建 bd609d2”进行此操作,生成的图像并没有小很多。它实际上有点大。但新形象的历史已经不复存在。
  • 适用于 Docker 17.03.0-ce。图像大小从 33GB 减少到 19GB
  • --squash 需要一些守护进程的实验标志。 export 适用于容器,而不是图像。您对如何将图像转换为容器而不在该图像中运行任何东西有什么建议吗?我的一个想法是运行容器中不存在的入口点。这似乎仍在创建一个新容器。
  • 在哪里可以使用--squash 标志?
  • docker build --squash -f .
【解决方案2】:

使用--squash 标志构建映像:

https://docs.docker.com/engine/reference/commandline/build/#squash-an-images-layers---squash-experimental

还可以考虑清理不需要的文件,例如 apt 缓存:

运行apt-get clean && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

【讨论】:

  • 现有图像怎么样。
【解决方案3】:

现在 Docker 已在 17.05 中发布了多阶段构建,您可以将构建重新格式化为如下所示:

FROM buildimage as build
# your existing build steps here
FROM scratch
COPY --from=build / /
CMD ["/your/start/script"]

结果将是您的构建环境层被缓存在构建服务器上,但您标记和推送的结果图像中仅存在一个扁平副本。


请注意,您通常会将其重新构造为具有复杂的构建环境并且仅复制几个目录。这是一个使用 Go 从源代码和单个构建命令生成单个二进制映像的示例,无需在主机上安装 Go 并在 docker 外部编译:

$ cat Dockerfile 
ARG GOLANG_VER=1.8
FROM golang:${GOLANG_VER} as builder
WORKDIR /go/src/app
COPY . .
RUN go-wrapper download 
RUN go-wrapper install

FROM scratch
COPY --from=builder /go/bin/app /app
CMD ["/app"]

go 文件是一个简单的 hello world:

$ cat hello.go 
package main

import "fmt"

func main() {
        fmt.Printf("Hello, world.\n")
}

构建创建了两个环境,构建环境和临时环境,然后标记临时环境:

$ docker build -t test-multi-hello .                                                                                                                              
Sending build context to Docker daemon  4.096kB
Step 1/9 : ARG GOLANG_VER=1.8
 ---> 
Step 2/9 : FROM golang:${GOLANG_VER} as builder
 ---> a0c61f0b0796
Step 3/9 : WORKDIR /go/src/app
 ---> Using cache
 ---> af5177aae437
Step 4/9 : COPY . .
 ---> Using cache
 ---> 976490d44468
Step 5/9 : RUN go-wrapper download
 ---> Using cache
 ---> e31ac3ce83c3
Step 6/9 : RUN go-wrapper install
 ---> Using cache
 ---> 2630f482fe78
Step 7/9 : FROM scratch
 ---> 
Step 8/9 : COPY --from=builder /go/bin/app /app
 ---> Using cache
 ---> 5645db256412
Step 9/9 : CMD /app
 ---> Using cache
 ---> 8d428d6f7113
Successfully built 8d428d6f7113
Successfully tagged test-multi-hello:latest

查看镜像,发货镜像中只有单个二进制文件,而构建环境超过 700MB:

$ docker images | grep 2630f482fe78
<none>                <none>              2630f482fe78        6 days ago          700MB

$ docker images | grep 8d428d6f7113
test-multi-hello      latest              8d428d6f7113        6 days ago          1.56MB

是的,它运行:

$ docker run --rm test-multi-hello 
Hello, world.

【讨论】:

  • 这应该是这个时候公认的答案了。它非常有效和灵活!
  • 请注意,使用这种方法,workdir、entrypoint、env 等将消失,您必须重复它们。除此之外,完美! :)
  • 请注意,COPY 将所有文件系统所有权重写为 root(或运行命令的用户),这可能是不可取的。
【解决方案4】:

看看docker-squash

安装方式:

pip install docker-squash

然后,如果您有图像,则可以将所有层压缩为 1

docker-squash -f <nr_layers_to_squash> -t new_image:tag existing_image:tag

一个快速的 1-liner,对我压缩所有层很有用:

docker-squash -f $(($(docker history $IMAGE_NAME | wc -l | xargs)-1)) -t ${IMAGE_NAME}:squashed $IMAGE_NAME

【讨论】:

  • 正是我想要的!!!!没有码头工人历史!!!! :D 安全第一...信任 0
猜你喜欢
  • 1970-01-01
  • 2018-05-24
  • 2022-10-15
  • 1970-01-01
  • 2017-04-26
  • 2010-11-19
  • 1970-01-01
  • 1970-01-01
  • 2020-01-20
相关资源
最近更新 更多