GVKun编程网logo

为什么 Docker 容器镜像这么大?(docker镜像为什么那么小)

9

在本文中,您将会了解到关于为什么Docker容器镜像这么大?的新资讯,同时我们还将为您解释docker镜像为什么那么小的相关在本文中,我们将带你探索为什么Docker容器镜像这么大?的奥秘,分析doc

在本文中,您将会了解到关于为什么 Docker 容器镜像这么大?的新资讯,同时我们还将为您解释docker镜像为什么那么小的相关在本文中,我们将带你探索为什么 Docker 容器镜像这么大?的奥秘,分析docker镜像为什么那么小的特点,并给出一些关于.NET Core 2.1 容器镜像将从 Docker Hub 中删除、03 docker容器镜像基础、10.Docker容器镜像体积缩小之奇技淫巧、Dive — 分析和浏览 Docker 容器镜像内部的工具的实用技巧。

本文目录一览:

为什么 Docker 容器镜像这么大?(docker镜像为什么那么小)

为什么 Docker 容器镜像这么大?(docker镜像为什么那么小)

我通过 Fedora 的 Dockerfile 制作了一个简单的图像(最初为 320 MB)。

添加了 Nano(这个 1MB 大小的小编辑器),图像大小已经上升到 530 MB。我在上面添加了 Git(30-ish MB),然后我的图像大小猛增到
830 MB。

这不是疯了吗?

我尝试导出和导入容器以删除历史/中间图像。这项工作最多节省了 25 MB,现在我的图像大小为 804
MB。我也尝试在一个上运行许多命令RUN,但我仍然得到相同的初始 830MB。

我怀疑是否值得使用 Docker。我的意思是,我几乎没有安装任何东西,而且超过了
1GB。如果我必须添加一些重要的东西,比如数据库等等,我可能会用完磁盘空间。

任何人都遭受荒谬的图像尺寸?你如何解决?

除非我的 Dockerfile 非常不正确?

FROM fedora:latestMAINTAINER Me NotYou <email@dot.com>RUN yum -y install nanoRUN yum -y install git

但很难想象这里会出什么问题。

答案1

小编典典

正如@rexposadas
所说,图像包括所有层,每一层都包括您安装的所有依赖项。同样重要的是要注意基础映像(例如fedora:latest往往是非常简单的。您可能会对安装的软件具有的依赖项数量感到惊讶。

yum -y clean all通过添加到每一行,我能够使您的安装显着减小:

FROM fedora:latestRUN yum -y install nano && yum -y clean allRUN yum -y install git && yum -y clean all

在层被提交之前,对每个 RUN 执行此操作很重要,否则删除实际上不会删除数据。也就是说,在 union/copy-on-write
文件系统中,最后的清理并不会真正减少文件系统的使用,因为真实数据已经提交到较低层。为了解决这个问题,您必须在每一层进行清洁。

$ docker history bf5260c6651dIMAGE               CREATED             CREATED BY                                      SIZEbf5260c6651d        4 days ago          /bin/sh -c yum -y install git; yum -y clean a   260.7 MB172743bd5d60        4 days ago          /bin/sh -c yum -y install nano; yum -y clean    12.39 MB3f2fed40e4b0        2 weeks ago         /bin/sh -c #(nop) ADD file:cee1a4fcfcd00d18da   372.7 MBfd241224e9cf        2 weeks ago         /bin/sh -c #(nop) MAINTAINER Lokesh Mandvekar   0 B511136ea3c5a        12 months ago                                                       0 B

.NET Core 2.1 容器镜像将从 Docker Hub 中删除

.NET Core 2.1 容器镜像将从 Docker Hub 中删除

.NET Core 2.1 容器镜像将从 Docker Hub 中删除

Richard 2021 年 8 月 16 日

从 8 月 21 日开始,.NET Core 2.1 Docker 容器镜像将不再在 Docker Hub 上可用,而只能在 Microsoft Container Registry (MCR) 上使用。此更改之前已通过 dotnet/dotnet-docker #2848 宣布。如果您依赖 Docker Hub 上的 .NET Core 2.1 镜像,则应立即切换到使用 MCR。如果此更改对您/您的组织造成问题,请联系 dotnet@microsoft.com。

我们于 2019 年初开始向 MCR 发布 .NET 镜像,包括 .NET Core 2.1。.NET Core 3 及更高版本专门发布到 MCR。MCR 的好处在引用的帖子中进行了讨论。

.NET Core 2.1 将于8 月 21 日停止支持。.NET Core 2.1 镜像将在 MCR 上保持可用。但是,我们鼓励您立即迁移到更高的 .NET 版本,因为从 21 日开始将不再支持 .NET Core 2.1。.NET Core 2.1自2018 年5 月发布后,从2018年8 月开始支持三年。

这篇文章的其余部分演示了从 MCR 而不是 Docker Hub 拉取 .NET 容器镜像应该进行的更改。

从 MCR 拉取镜像

MCR 上的 .NET 镜像 可从以下存储库获得:

  • mcr.microsoft.com/dotnet/runtime-deps
  • mcr.microsoft.com/dotnet/runtime
  • mcr.microsoft.com/dotnet/aspnet
  • mcr.microsoft.com/dotnet/sdk

您需要进行以下转换才能从 MCR 而不是 Docker Hub 拉取 .NET Core 2.1 镜像。

对于 SDK:

microsoft/dotnet:2.1-sdk -> mcr.microsoft.com/dotnet/sdk:2.1
microsoft/dotnet:2-sdk -> mcr.microsoft.com/dotnet/sdk:2.1
microsoft/dotnet:2.1-sdk-stretch -> mcr.microsoft.com/dotnet/sdk:2.1-stretch
microsoft/dotnet:2.1-sdk-stretch-arm32v7 -> mcr.microsoft.com/dotnet/sdk:2.1-stretch-arm32v7
microsoft/dotnet:2.1-sdk-nanoserver-1809 -> mcr.microsoft.com/dotnet/sdk:2.1-nanoserver-1809
microsoft/dotnet:2.1-sdk-alpine -> mcr.microsoft.com/dotnet/sdk:2.1-alpine
microsoft/dotnet:2.1-sdk-bionic -> mcr.microsoft.com/dotnet/sdk:2.1-bionic
microsoft/dotnet:2.1-sdk-bionic-arm32v7 -> mcr.microsoft.com/dotnet/sdk:2.1-bionic-arm32v7
microsoft/dotnet:latest -> mcr.microsoft.com/dotnet/sdk:2.1

对于 ASP.NET Core:

microsoft/dotnet:2.1-aspnetcore-runtime -> mcr.microsoft.com/dotnet/aspnet:2.1
microsoft/dotnet:2-aspnetcore-runtime-> mcr.microsoft.com/dotnet/aspnet:2.1
microsoft/dotnet:2.1-aspnetcore-runtime-stretch-slim -> mcr.microsoft.com/dotnet/aspnet:2.1-stretch-slim
microsoft/dotnet:2.1-aspnetcore-runtime-stretch-slim-arm32v7 -> mcr.microsoft.com/dotnet/aspnet:2.1-stretch-slim-arm32v7
microsoft/dotnet:2.1-aspnetcore-runtime-nanoserver-1809 -> mcr.microsoft.com/dotnet/aspnet:2.1-nanoserver-1809
microsoft/dotnet:2.1-aspnetcore-runtime-alpine -> mcr.microsoft.com/dotnet/aspnet:2.1-alpine
microsoft/dotnet:2.1-aspnetcore-runtime-bionic -> mcr.microsoft.com/dotnet/aspnet:2.1-bionic
microsoft/dotnet:2.1-aspnetcore-runtime-bionic-arm32v7 -> mcr.microsoft.com/dotnet/aspnet:2.1-bionic-arm32v7
microsoft/dotnet:aspnetcore-runtime-> mcr.microsoft.com/dotnet/aspnet:2.1

对于 .NET 运行时:

microsoft/dotnet:2.1-runtime -> mcr.microsoft.com/dotnet/runtime:2.1
microsoft/dotnet:2-runtime -> mcr.microsoft.com/dotnet/runtime:2.1
microsoft/dotnet:2.1-runtime-stretch-slim -> mcr.microsoft.com/dotnet/runtime:2.1-stretch-slim
microsoft/dotnet:2.1-runtime-stretch-slim-arm32v7 -> mcr.microsoft.com/dotnet/runtime:2.1-stretch-slim-arm32v7
microsoft/dotnet:2.1-runtime-nanoserver-1809 -> mcr.microsoft.com/dotnet/runtime:2.1-nanoserver-1809
microsoft/dotnet:2.1-runtime-alpine -> mcr.microsoft.com/dotnet/runtime:2.1-alpine
microsoft/dotnet:2.1-runtime-bionic -> mcr.microsoft.com/dotnet/runtime:2.1-bionic
microsoft/dotnet:2.1-runtime-bionic-arm32v7 -> mcr.microsoft.com/dotnet/runtime:2.1-bionic-arm32v7
microsoft/dotnet:runtime -> mcr.microsoft.com/dotnet/runtime:2.1

对于 .NET 运行时依赖项:

microsoft/dotnet:2.1-runtime-deps -> mcr.microsoft.com/dotnet/runtime-deps:2.1
microsoft/dotnet:2.1-runtime-deps-stretch-slim -> mcr.microsoft.com/dotnet/runtime-deps:2.1-stretch-slim
microsoft/dotnet:2.1-runtime-deps-stretch-slim-arm32v7 -> mcr.microsoft.com/dotnet/runtime-deps:2.1-stretch-slim-arm32v7
microsoft/dotnet:2.1-runtime-deps-alpine -> mcr.microsoft.com/dotnet/runtime-deps:2.1-alpine
microsoft/dotnet:2.1-runtime-deps-bionic -> mcr.microsoft.com/dotnet/runtime-deps:2.1-bionic
microsoft/dotnet:2.1-runtime-deps-bionic-arm32v7 -> mcr.microsoft.com/dotnet/runtime-deps:2.1-bionic-arm32v7
microsoft/dotnet:runtime-deps -> mcr.microsoft.com/dotnet/runtime-deps:2.1

总结

多年来,我们一直与 Docker 公司密切合作,将 Microsoft 容器镜像迁移到 Microsoft Container Registry。鉴于 .NET 镜像的流行,我们选择 .NET Core 2.1 支持终止日期作为在 Docker Hub 上托管 .NET 镜像的最终日期。

如前所述,.NET Core 2.1 镜像自 2019 年以来已在 MCR 上可用,而 .NET Core 3 及更高版本的镜像版本已在 MCR 上独家提供。请移步至 MCR 以提取所有 .NET 容器镜像和使用 支持的 .NET 版本。


本文分享自微信公众号 - dotNET跨平台(opendotnet)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

03 docker容器镜像基础

03 docker容器镜像基础

  本章内容

    1、docker镜像基础

    2、docker环境下使用的文件系统

    3、registry(仓库)

    4、获取镜像

    5、制作(生成)并上传镜像

---------------------------------------

  镜像是作为docker环境的核心内容,一帮情况下,我们都需要根据工作环境来定制镜像文件,这就对于运维人员提出了很高的要求;

 

1、docker镜像基础

  docker镜像含有启动容器所需要的文件系统及其内容,因此,其用于创建并启动docker容器采用分层构建机制,最底层的bootfs,其之为rootfs;

  docker镜像的分层结构,如下:

  首先,我们需要了解bootfs ,它是用于引导文件系统的,包含有bootloader和kernel,容器启动之后就会将其卸载下来;(这里就是为了去加载内核,内核加载完成就可以卸载,以节约资源)

  其次,rootfs 位于bootfs之上,它就是docker容器的根文件件系统,是每个容器的根;

    1、在传统系统模式中,系统启动时,内存挂载在rootfs“只读”模式,完整性自检完成后将其重新挂载为读写模式;(系统启动知识)

    2、docker中,rootfs由内核挂载为“只读”模式,而后通过”联合挂载“技术额外挂载一个”可写”层;

  最后再在rootfs上面去挂载编译环境(编辑器emacs),以及应用程序,而这两层都是“只读”层,如果需要修改,我们再在最上层挂载writable读写层。也就是我们的container;

【这里我们需要知道,下层是可以多次使用的,可以同时关联于多个writable层上,这样的话,下层可以重复利用】

  

  如上图:

  Apache 运行在一个纯净的 debian上面运行,在系统上使用一个编辑器 - emacs;   bootfs在内存中挂载后,之后被移除掉;   debian-emacs-apache 这三层用来启动服务,并且一起挂载--这就叫做联合挂载;这三层是可以多个用户同时共享使用的;   但是,如果想写入文件,则不能再接写在这三层中,而是再加一层 writable ,专门用来提供给各个用户来进行写入数据;

 

2、docker环境下使用的文件系统

  此时,基于docker镜像的工作原理,我们使用原有传统的数据组织方式(文件系统)是不适用的,所以我们使用了新的文件系统来满足docker容器技术;

  其中包括三种技术:aufs  overlayfs  devicemapper(dm)

  其中,devicemapper使用的是dm的模块来实现,而并不是修改了文件系统,在生产环境下,我们建议使用overlayfs的文件系统来实现docker技术;

 

  Aufs

    用于为Linux文件系统实现“联合挂载”,aufs是之前UnionFS的重新实现,2006年由Junjiro Okajima开发;docker使用之初是用aufs作为文件系统层,它目前仍然作为存储后端之一来支持;

    它的竞争产品overlayfs,后来从3.18版本开始被合并到Linux内核;docker;

    而aufs一直不是内核中自有的文件系统。它因为代码太乱了,3万行太多,没有写入内核;如果需要,则必须打补丁升级;而Ubuntu则是将aufs写入内核了;

  overlayfs

    相对于aufs,overlayfs则要优秀的多,它已经被写入到Linux内核,默认启动docker,只要支持overlayfs,则就回使用这种方式来创建docker环境;

  devicemapper

    在没有使用上面两种文件系统的系统程序中,去启动docker进程,则是使用该方式来实现的;它是借助 dm 模块(和lvm技术是同一个模块)来实现,而不是依靠文件系统;

    所以这种方式非常的不稳定,不建议使用;但是在3.18内核之前的版本,redhat系列的Linux操作系统不支持aufs和overlayfs,就会使用该方式来实现docker环境;

 

  查看我的docker环境:

docker info

 

3、registry(仓库)

  镜像文件是从registry(仓库)中获取;

  在启动容器时(run),docker daemon会同时从本地获取相关的镜像文件,如果本地没有这个镜像文件,服务器就回直接去对应的公网仓库中去下载;

  仓库的分类:

    sponsor Registry  第三方仓库,供客户和docker社区使用

    mirror Registry  第三方仓库,只让客户使用

    vendor Registry  由发布docker镜像的供应商提供的registry

    private Registry  通过设有防火墙和额外的安全层的私有实体提供的registry

  tag标签:

    对于仓库,可以定义标签tag,每个仓库可以有多个标签,但是,一个标签是不能属于多个仓库的;

  index索引:

    维护用户账号、镜像的校验以及公共命名空间的信息;

    相当于为registry(仓库)提供了一个完成用户认证等功能的检索接口;

 

4、获取镜像

  我们获取镜像,通过 docker 命令的子命令 pull 来实现;

  用法如下:

    docker pull registry[:<port>]/[namespace/]<name>:<tag>

  

  示例:下载一个镜像文件;

  我们通过第三方的仓库来下载我们所需要的镜像文件: https://quay.io

  搜索 flannel ;这是云环境下最常用的一个网络部署系统镜像文件;

  点击“coreos/flannel”进入下载界面: 在右下角可以复制镜像地址,进入镜像下载界面

  这里,我们一定要指定tag,如下图查看tag:

  下载镜像:

docker pull quay.io/coreos/flannel:v0.11.0-arm

  查看镜像:

docker images

 

5、制作(生成)并上传镜像

  镜像生成途径有两种:1、dockerfile 2、基于容器制作

  其中dockerfile是我们最常用的镜像制作方式,而且是docker容器的核心,我们需要通过dockerfile自己定义镜像,自己定义仓库;

 

  这里我们先通过容器技术来配置:

    我们通过子命令commit来通过容器制作镜像;

     docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

      -a --author 指定作者

      -c --change  应用dockerfile的指令来创建镜像

      -m --message 添加消息信息

      -p --pause  创建镜像的时候,需要暂停容器

 

  a\首先我们运行一个容器

    我们这里首先通过 pull 命令下载一个busyBox的容器,启动这个容器

docker run --name b1 -it busyBox

  b\启动busyBox的http服务

/ # mkdir -p /data/html
/ # vi /data/html/index.html
<h1>BusyBox httpd server</h1>
/ # busyBox httpd server

  查看busyBox地址:

  从新开启一个 ssh 远程接口,查看http服务:

curl 172.17.0.2

 

  c\保存镜像

docker commit -p b1  //制作新的镜像

docker images //查看制作的镜像

  我们可以为新的镜像文件添加标签:

    通过子命令 tag 来实现

docker tag 6l6b37df33a7 test/httpd:v0.1  //添加标签需要指定镜像id

  删除镜像,我们可以通过 images 子命令来实现

docker images rm test/httpd:v0.1

  我们这里就不删除了

 

  d\我们可以运行这个镜像

  操作如下: 这里,我新添加一个容器--b1.1,使用的就是我们创建的镜像,同时,我们可以 cat /data/html/index.html 文件,这样,我们就能够看到之前编辑的文件;

 

  e\指定默认容器启动的服务

  这里我们启动的容器后,还需要指定启动httpd服务,但是,如果我们默认就需要镜像启动httpd的服务,这里,我们也可以通过commit子命令来实现;

docker commit -a "test <test@admin.com>" -c 'CMD ["/bin/httpd","-f","-h","data/html"]' -p b1 test/httpd:v0.2

  此时,则创建了一个新的镜像,我们再启动这个镜像:

docker run --name b1.2 -it test/httpd:v0.2
docker inspect b1.2 //查看地址

  查看服务:

  查看容器:

    在这里,我们可以看到,我们指定的默认启动命令;

 

  f\上传镜像到公网仓库

  根据上面的内容,我们已经为自己制作了对应的镜像文件;我们可以将自己制作的镜像文件上传到公网仓库,提供给别人下载;

  首先,我们需要去dockerhub.com上面注册账号:

  注册完成以后,登录dockerhub.com网站

  在该界面下,去创建自己的仓库;

    1、指定仓库名称 2、指定仓库描述 3、指定镜像是否公开

  

  最后创建即可:

  有了自己的公有仓库,这时候,我们就可以将自己的镜像文件上传到仓库中:

    pull 子命令为下载镜像,同样的 push 命令就是上传进项;

    在上传之前,我们需要登录到自己的公网仓库,这个操作通过 login 操作来完成;

docker login -u burnov

    提示登录成功后,我们就可以上传镜像了

   注意:这里有个问题--上传镜像我们一定要让仓库名称,登录名称对应上:

    上图是我们仓库的名称,所以,我们的镜像名称也需要修改过来

docker tag test/httpd:v0.2 burnov/httpd:v0.2

    这里,burnov/httpd 中 burnov为用户名,而httpd为仓库名;v0.2为tag标记;

 

  再次上传镜像

docker push burnov/httpd:v0.2

  这样就能上传镜像到仓库;

  查看仓库:刷新一下即可

  

10.Docker容器镜像体积缩小之奇技淫巧

10.Docker容器镜像体积缩小之奇技淫巧

原文地址:https://weiyigeek.top/2020/5/docker%E5%AE%B9%E5%99%A8%E9%95%9C%E5%83%8F%E4%BD%93%E7%A7%AF%E7%BC%A9%E5%B0%8F%E6%8A%80%E5%B7%A7.html

<!-- more -->

0x00 前言简述

描述:前面我们学习并且记录了 Dockerfile 最佳实践的一些规则,但是仅仅停在理论中并不是我的风格,所以出现了本篇文章同时也加深学习; 从最佳实践原则我们知道要缩小镜像大小,与选择的基础镜像是非常有关系的,比如buysbox (工具箱)与alpine (操作系统)镜像小的您超出您的想象,需要

docker pull alpine  # 容器中最小的Linux发行版
docker pull busybox # 嵌入式以及物联网系统中最常用的Linux下的工具箱
docker images | grep -E "busybox|alpine"
busybox                              latest              c7c37e472d31        3 weeks ago         1.22MB
alpine                               latest              a24bb4013296        8 weeks ago         5.57MB

对于刚接触容器的人来说他们很容易被自己构建的 Docker 镜像体积吓到,我只需要一个几 MB 的可执行文件而已,为何镜像的体积会达到 1 GB 以上? 答:相信下面的奇技淫巧会帮助你精简镜像,同时又不牺牲开发人员和运维人员的操作便利性。

<br>

0x01 基础环境

实验(实践)环境准备:

[root@k8s-yum-server ~]# cat /etc/system-release
CentOS Linux release 7.8.2003 (Core)

[root@k8s-yum-server ~]# uname -r
5.7.0-1.el7.elrepo.x86_64

0x02 Docker 最佳实践补充

  • (1) 建立一个空白目录进行构建上下文准备,<span>切记不要在家里录下创建一个 Dockerfile 紧接着 docker build 一把梭</span>
# 正确做法是为项目建立一个文件夹,把构建镜像时所需要的资源放在这个文件夹下
mkdir /opt/project && cd !$
vi Dockerfile # 编写 Dockerfile
  • Tips:也可以通过 .dockerignore 文件来忽略不需要的文件发送到 docker 守护进程

  • (2) 使用体积较小的基础镜像,比如 alpine 或者 debian:buster-slim,像 openjdk 可以选用openjdk:8-slim;

$docker pull debian:buster-slim
$docker pull openjdk:8-slim
$docker images
REPOSITORY   TAG             SIZE
debian       buster-slim     69.2MB  
busybox      latest          1.22MB  # 不建议采用构建太过耗时以及调试
alpine       latest          5.57MB  # 推荐
centos       latest          220MB   # 不建议采用基础镜像
  • Tips:由于 openjdk 是基于 debian 的基础镜像构建的,所以向 debian 基础镜像一样后面带个 slim 就是基于 debian:xxx-slim 镜像构建的。

  • Tips:alpine 的 c 库是 musl libc 而不是正统的 glibc 库,另外对于一些依赖 glibc 的大型项目像 openjdk 、tomcat、rabbitmq 等都不建议使用 alpine 基础镜像,因为 musl libc 可能会导致 jvm 一些奇怪的问题,这也是为什么 tomcat 官方没有给出基础镜像是 alpine 的 Dockerfile 的原因。

  • (3) 更改为国内镜像软件源,提升容器构建速度目前国内稳定可靠的镜像站主要有,华为云、阿里云、腾讯云、163等。

# alpine 基础镜像修改软件源
RUN echo "http://mirrors.huaweicloud.com/alpine/latest-stable/main/" > /etc/apk/repositories ;\
echo "http://mirrors.huaweicloud.com/alpine/latest-stable/community/" >> /etc/apk/repositories ;\
apk update ;\

# debian 基础镜像修改软件源
sed -i ''s/deb.debian.org/mirrors.huaweicloud.com/g'' /etc/apt/sources.list ;\
sed -i ''s|security.debian.org/debian-security|mirrors.huaweicloud.com/debian-security|g'' /etc/apt/sources.list ;\
apt update ;\

# Ubuntu 基础镜像修改软件源
sed -i ''s/archive.ubuntu.com/mirrors.huaweicloud.com/g'' /etc/apt/sources.list
apt update ;\
  • Tips: 建议更新软件源时候RUN指令放在镜像描述后的首条,然后再update软件源以及install相应的软件包;

  • (4) 镜像时区设置由于绝大多数基础镜像都是默认采用UTC的时区与北京时间相差8个小时,将会会导致容器内的时间与北京时间不一致,因而会对一些应用造成一些影响,还会影响容器内日志和监控的数据,可以通过以下操作进行解决;

# 方式1.通过设置环境变量来设定容器内的时区。
-e TZ=Asia/Shanghai

#  debian设置两种方式
# -启动时设定环境变量指定时区
docker run --rm -it -e TZ=Asia/Shanghai debian date
# -构建镜像的时候复制时区文件设定容器内时区
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ;\
echo "Asia/shanghai" > /etc/timezone ;\

  • Tips: alpine 基础镜像无法通过 TZ 环境变量的方式设定时区,需要安装 tzdata 来配置时区也可以在 RUN 指令后面追加上时区复制到/etc/localtime命令;
# 方式1
docker run --rm -it -e TZ=Asia/Shanghai alpine date

# 方式2
apk add --no-cache tzdata ;\
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime ;\
echo "Asia/Shanghai" > /etc/timezone ;\
apk del tzdata ;\
  • (5) 使用URL添加源码,如果不采用分阶段构建对于一些需要在容器内进行编译的项目,最好通过 git 或者 wegt 的方式将源码打入到镜像内,而非采用 ADD 或者 COPY ,因为源码编译完成之后源码就不需要可以删掉了,而通过 ADD 或者 COPY 添加进去的源码已经用在下一层镜像中了是删不掉滴啦;
    • 项目官方的 Dockerfile
# centos 7
FROM centos:7
# 添加配置文件
# add profiles
ADD conf/client.conf /etc/fdfs/
ADD conf/http.conf /etc/fdfs/
ADD conf/mime.types /etc/fdfs/
ADD conf/storage.conf /etc/fdfs/
ADD conf/tracker.conf /etc/fdfs/
ADD fastdfs.sh /home
ADD conf/nginx.conf /etc/fdfs/
ADD conf/mod_fastdfs.conf /etc/fdfs

# 添加源文件
# add source code
ADD source/libfastcommon.tar.gz /usr/local/src/
ADD source/fastdfs.tar.gz /usr/local/src/
ADD source/fastdfs-nginx-module.tar.gz /usr/local/src/
ADD source/nginx-1.15.4.tar.gz /usr/local/src/

# Run
RUN yum install git gcc gcc-c++ make automake autoconf libtool pcre pcre-devel zlib zlib-devel openssl-devel wget vim -y \
  &&  mkdir /home/dfs   \
  &&  cd /usr/local/src/  \
  &&  cd libfastcommon/   \
  &&  ./make.sh && ./make.sh install  \
  &&  cd ../  \
  &&  cd fastdfs/   \
  &&  ./make.sh && ./make.sh install  \
  &&  cd ../  \
  &&  cd nginx-1.15.4/  \
  &&  ./configure --add-module=/usr/local/src/fastdfs-nginx-module/src/   \
  &&  make && make install  \
  &&  chmod +x /home/fastdfs.sh
# export config
VOLUME /etc/fdfs

EXPOSE 22122 23000 8888 80
ENTRYPOINT ["/home/fastdfs.sh"]
  • 优化后的 Dockerfile
FROM alpine:3.10
RUN set -x \
    && echo "http://mirrors.huaweicloud.com/alpine/latest-stable/main/" > /etc/apk/repositories \
    && echo "http://mirrors.huaweicloud.com/alpine/latest-stable/community/" >> /etc/apk/repositories \
    && apk update \
    && apk add --no-cache --virtual .build-deps gcc libc-dev make perl-dev openssl-dev pcre-dev zlib-dev git \
    && mkdir -p /usr/local/src \
    && cd /usr/local/src \
    && git clone https://github.com/happyfish100/libfastcommon.git --depth 1 \
    && git clone https://github.com/happyfish100/fastdfs.git --depth 1    \
    && git clone https://github.com/happyfish100/fastdfs-nginx-module.git --depth 1  \
    && wget http://nginx.org/download/nginx-1.15.4.tar.gz \
    && tar -xf nginx-1.15.4.tar.gz \
    && cd /usr/local/src/libfastcommon \
    && ./make.sh \
    && ./make.sh install \
    && cd /usr/local/src/fastdfs/ \
    && ./make.sh \
    && ./make.sh install \
    && cd /usr/local/src/nginx-1.15.4/ \
    && ./configure --add-module=/usr/local/src/fastdfs-nginx-module/src/ \
    && make && make install \
    && apk del .build-deps \
    && apk add --no-cache pcre-dev bash \
    && mkdir -p /home/dfs  \
    && mv /usr/local/src/fastdfs/docker/dockerfile_network/fastdfs.sh /home \
    && mv /usr/local/src/fastdfs/docker/dockerfile_network/conf/* /etc/fdfs \
    && chmod +x /home/fastdfs.sh \
    && rm -rf /usr/local/src*
VOLUME /home/dfs
EXPOSE 22122 23000 8888 8080
CMD ["/home/fastdfs.sh"]
  • 构建之后的对比使用项目默认的 Dockerfile 进行构建的话,镜像大小接近 500MB ,而经过一些的优化,将所有的 RUN 指令合并为一条,最终构建出来的镜像大小为 30MB 。
# 结论: 建议采用alpine进行构建只不过需要注意其alpine的c库是`musl libc`而不是正统的glibc库
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
fastdfs             alpine              e855bd197dbe        10 seconds ago      29.3MB
fastdfs             debian              e05ca1616604        20 minutes ago      103MB
fastdfs             centos              c1488537c23c        30 minutes ago      483MB
  • (6) 使用虚拟编译环境对于只在编译过程中使用到的依赖,我们可以将这些依赖安装在虚拟环境中,编译完成之后可以一并删除这些依赖
#比如 alpine 中可以使用 apk add --no-cache --virtual .build-deps ,后面加上需要安装的相关依赖。
apk add --no-cache --virtual .build-deps gcc libc-dev make perl-dev openssl-dev pcre-dev zlib-dev git

#构建完成之后可以使用 apk del .build-deps 命令,一并将这些编译依赖全部删除。
  • 注意的是.build-deps 后面接的是编译时以来的软件包,并不是所有的编译依赖都可以删除,不要把运行时的依赖包接在后面最好单独 add 一下。

  • (7) 最小化层数至docker 在 1.10 以后,只有 RUN、COPY 和 ADD 指令会创建层,其他指令会创建临时的中间镜像但是不会直接增加构建的镜像大小了;

# 如果多个文件需要添加到容器中不同的路径,每个文件使用一条 ADD 指令的话就会增加一层镜像,可以通过以下方式进行精简镜像构建时的大小;
# 此时可以通过将所有的依赖文件打包为一个文件 src.tar.gz 然后通过 ADD 的方式把文件添加到当中去,然后在 RUN 指令后使用 mv 命令把文件移动到指定的位置。
COPY src.tar.gz /usr/local/src.tar.gz
  • Tips: 其他最小化层数无非就是把构建项目的整个步骤弄成一条 RUN 指令不过多条命令合并可以使用 && 或者 ;,在学习官方的DockerFile则发现使用的;的居多;

  • (8) 采用多阶段构建镜像可以减小镜像大小,但是注意为了保证镜像正常构建运行,需要在COPY --from=0指令执行时候将上阶段的成果采用绝对路径复制防止构建出错;

    • Tips: 最好的方法是在第一阶段指定 WORKDIR,在第二阶段使用绝对路径拷贝文件,即使基础镜像修改了 WORKDIR 不会影响到镜像的构建
FROM golang   # 默认工作空间为/go
WORKDIR /src
COPY hello.go .
RUN go build hello.go
FROM ubuntu
COPY --from=0 /src/hello .
CMD ["./hello"]

0x03 镜像体积缩减实战

(1) 奇技淫巧构建镜像体积减小 99%

描述:每一个初次使用自己写好的代码指令构建Docker镜像时候都会被镜像的大小所吓倒因为确实太大,当然前提是没有进行 DockerFile 优化中;

比如: 构建一个镜像采用编译.c的C程序然后并运行容器 示例C程序:

mkdir /opt/hello && cd $_
cat > hello.c <<EOF
#include <stdio.h>
/* hello.c */
int main () {
  puts("Hello, world!");
  return 0;
}
EOF

DockerFile

cat > Dockerfile <<EOF
FROM gcc
COPY hello.c .
RUN gcc -o hello hello.c
CMD ["./hello"]
EOF

基础实践:

$docker build --tag gcc-hello:latest .
# 表示 docker cli 命令行客户端将我们当前目录(即构建上下文) build context 打包发送 Docker daemon 守护进程 (即 dockerd)的过程。
# KB: 代表当前上下文大小
Sending build context to Docker daemon  3.072kB
....
Successfully built eb9ae8b1c49c
Successfully tagged gcc-hello:latest

# 此时您会发现构建成功的镜像体积远远超过了 1 GB, 因为该镜像包含了整个 gcc 镜像的内容;
$docker images gcc-hello 
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
gcc-hello           latest              eb9ae8b1c49c        5 minutes ago       1.19GB

# 如果使用 Ubuntu 镜像安装 C 编译器,最后编译程序你会得到一个大概 300 MB 大小的镜像比上面的镜像小多了
$docker run -it --rm gcc-hello
Hello, world!
$docker run -it --rm gcc-hello ls -alh hello
-rwxr-xr-x. 1 root root 16K Jul 27 08:21 hello  #因为编译好的可执行文件还不到 20 KB

<br/>

示例Go程序:

mkdir /opt/go-hello/ && cd $_
cat > hello.go <<EOF
package main

import "fmt"
func main() {
  fmt.Println("Hello, world!")
}
EOF

Dockerfile:

cat > dockerfile <<EOF
FROM golang
COPY hello.go .
RUN go build hello.go 
CMD ["./hello","ls -alh ./hello"]
EOF

使用基础镜像 golang 构建的镜像大小是 800 MB,而编译后的可执行文件只有 2 MB 大小:

# 镜像构建
$docker build --tag go-hello .
Sending build context to Docker daemon  3.072kB
....
Successfully built d1bb1eb974f4
Successfully tagged go-hello:latest

# 运行镜像
[root@k8s-yum-server go-hello]$ docker run -it --rm go-hello
Hello, world!
[root@k8s-yum-server go-hello]$ docker run -it --rm go-hello bash
root@f610b4030f2d:/go# ls -alh hello
-rwxr-xr-x. 1 root root 2.0M Jul 27 08:43 hello  # 比gcc生成的大多了

Tips: 为了更直观地对比不同镜像的大小,所有镜像都使用相同的镜像名不同的标签,当然您需要在构建镜像时候采用--tag参数进行。

<br/>

如何缩减镜像大小? 答:要想大幅度减少镜像的体积,多阶段构建是必不可少的。 多阶段构建的想法很简单: "我不想在最终的镜像中包含一堆 C 或 Go 编译器和整个编译工具链,我只要一个编译好的可执行文件!"

多阶段构建可以由多个 FROM 指令识别,每一个 FROM 语句表示一个新的构建阶段,阶段名称可以用 AS 参数指定,例如:

本例使用基础镜像 gcc 来编译程序 hello.c,然后启动一个新的构建阶段,它以 ubuntu 作为基础镜像,将可执行文件 hello 从上一阶段拷贝到最终的镜像中

cat > dockerfile <<EOF
FROM gcc AS mybuildstage
COPY hello.c .
RUN gcc -o hello hello.c
FROM ubuntu
COPY --from=mybuildstage hello .
CMD ["./hello"]
EOF

镜像构建:

$docker build -t go-hello:stage -f Dockerfile .
Sending build context to Docker daemon  3.072kB
Step 1/6 : FROM gcc AS mybuildstage
 ---> 21f378ba43ec
Step 2/6 : COPY hello.c .
 ---> Using cache
 ---> 09252ee7e000
Step 3/6 : RUN gcc -o hello hello.c
 ---> Using cache
 ---> 9615c168cdc0
Step 4/6 : FROM ubuntu
latest: Pulling from library/ubuntu
3ff22d22a855: Pull complete
e7cb79d19722: Pull complete
323d0d660b6a: Pull complete
b7f616834fd0: Pull complete
Digest: sha256:5d1d5407f353843ecf8b16524bc5565aa332e9e6a1297c73a92d3e754b8a636d
Status: Downloaded newer image for ubuntu:latest
 ---> 1e4467b07108
Step 5/6 : COPY --from=mybuildstage hello .
 ---> 4659f572446e
Step 6/6 : CMD ["./hello"]
 ---> Running in be24d1c6b4aa
Removing intermediate container be24d1c6b4aa
 ---> 5934753f8f4f
Successfully built 5934753f8f4f
Successfully tagged go-hello:stage

最终的镜像大小是73.9 MB,比之前的 1.1 GB 减少了 95%

$docker images go-hello
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
go-hello            stage               5934753f8f4f        9 minutes ago       73.9MB
ubuntu              latest              1e4467b07108        2 days ago          73.9MB
gcc                 latest              21f378ba43ec        3 days ago          1.19GB

$docker run -it --rm go-hello:stage
Hello, world!

如果 Dockerfile 内容不是很复杂,构建阶段也不是很多可以直接使用序号表示构建阶段。一旦 Dockerfile 变复杂了,构建阶段增多了,最好还是通过关键词 AS 为每个阶段命名这样也便于后期维护。

# 在声明构建阶段时可以不必使用关键词 AS,最终阶段拷贝文件时可以直接使用序号表示之前的构建阶段(从零开始)下面两行是等效的
COPY --from=0 hello .              # 不是很复杂时候使用
COPY --from=mybuildstage hello .   # 复杂时候一定要用阶段名称而不是阶段索引;

(2).FROM scratch 的魔力

描述:回到我们的 hello world,C 语言版本的程序大小为 16 kB,Go 语言版本的程序大小为 2 MB,那么我们到底能不能将镜像缩减到这么小?能否构建一个只包含我需要的程序,没有任何多余文件的镜像? 答案:是肯定的,你只需要将多阶段构建的第二阶段的基础镜像改为 scratch (一个虚拟镜像),不能被 pull,也不能运行,因为它表示空、nothing!这就意味着新镜像的构建是从零开始,不存在其他的镜像层; 虽然它可以极大的缩小镜像大小,但是使用它 scratch 作为基础镜像时会带来很多的不便(事物往往都不是那么完美的)。

使用scratch作为基础镜像缺点:

    1. scratch 镜像的第一个不便是没有 shell意味着 CMD/RUN 语句中不能使用字符串
FROM scratch
COPY --from=0 /go/hello .

# 采用Jons格式
CMD ["./hello"]
    1. scratch 镜像不包含任何调试工具例如ls、ps、ping等命令统统没有,当然了shell 也没有(上文提过了),你无法使用·docker exec进入容器,也无法查看网络堆栈信息等等。
    • 解决办法:折中一下可以选择 busybox 或 alpine 镜像来替代 scratch;
$docker run --rm -it go-hello:scratch ls
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"ls\": executable file not found in $PATH": unknown.
    1. scratch 镜像缺少 lib 链接库这是比较难处理的问题,使用 scratch 作为基础镜像时 Go 语言版本的 hello world 跑得很欢快,C 语言版本就不行了,或者换个更复杂的 Go 程序也是跑不起来的(例如用到了网络相关的工具包),你会遇到类似于下面的错误
# 错误信息:standard_init_linux.go:211: exec user process caused "no such file or directory"
报错信息可以看出缺少文件,但没有告诉我们到底缺少哪些文件,其实这些文件就是程序运行所必需的动态库(dynamic library)。
  • Tips:回到最初的问题默认情况下C程序/Go 程序使用的是动态链接。上面的 hello world 程序使用了标准库文件 libc.so.6,所以只有镜像中包含该文件,程序才能正常运行。 使用 scratch 作为基础镜像肯定是不行的使用 busybox 和 alpine 也不行,因为 busybox 不包含标准库,而 alpine 使用的标准库是 musl libc,与大家常用的标准库 glibc 不兼容

<br>

Q:那么该如何解决标准库的问题呢? 答:有三种方案。 1.使用静态库我们可以让编译器使用静态库编译程序办法有很多,如果使用 gcc 作为编译器,只需加上一个参数 -static(推荐方式:以大小牺牲满足程序的健壮性):

$gcc -o hello hello.c -static

编译完的可执行文件大小为 760 kB相比于之前的 16kB 是大了好多,这是因为可执行文件中包含了其运行所需要的库文件,编译完的程序就可以跑在 scratch 镜像中了。 如果使用 alpine 镜像作为基础镜像来编译得到的可执行文件会更小(< 100kB)。

2.拷贝库文件到镜像中为了找出程序运行需要哪些库文件可以使用 ldd 工具:

$ldd hello
	linux-vdso.so.1 (0x00007ffdf8acb000)
	libc.so.6 => /usr/lib/libc.so.6 (0x00007ff897ef6000)
	/lib64/ld-linux-x86-64.so.2 => /usr/lib64/ld-linux-x86-64.so.2 (0x00007ff8980f7000)
# 从输出结果可知该程序只需要 libc.so.6 这一个库文件,linux-vdso.so.1 与一种叫做VDSO[3]的机制有关,用来加速某些系统调用可有可无, ld-linux-x86-64.so.2 表示动态链接器本身,包含了所有依赖的库文件的信息。
你可以选择将 ldd 列出的所有库文件拷贝到镜像中但这会很难维护,特别是当程序有大量依赖库时(不切实际了不建议使用);

对于 hello world 程序来说,拷贝库文件完全没有问题,但对于更复杂的程序(例如使用到 DNS 的程序)就会遇到令人费解的问题,glibc(GNU C library)通过一种相当复杂的机制来实现 DNS,这种机制叫 NSS(Name Service Switch, 名称服务开关)。 它需要一个配置文件 /etc/nsswitch.conf 和额外的函数库,但使用 ldd 时不会显示这些函数库,因为这些库在程序运行后才会加载。 如果想让 DNS 解析正确工作,必须要拷贝这些额外的库文件(/lib64/libnss_*)。

3.使用 busybox:glibc 作为基础镜像 有一个镜像可以完美解决所有的这些问题那就是 busybox:glibc, 因为它只有 5 MB 大小并且包含了 glibc 和各种调试工具。 如果你想选择一个合适的镜像来运行使用动态链接的程序busybox:glibc是最好的选择。

注意:如果你的程序使用到了除标准库之外的库,仍然需要将这些库文件拷贝到镜像中。

实际案例:

cat > Dockerfile<<EOF
FROM golang
WORKDIR /go
COPY hello.go .
RUN go build hello.go
FROM scratch
COPY --from=0 /go/hello .
CMD ["./hello"]
EOF

镜像构造:

$docker build -f Dockerfile -t go-hello:scratch .
Sending build context to Docker daemon  4.096kB
Step 1/7 : FROM golang
...
 ---> Running in 8eba3646dcc4
Removing intermediate container 8eba3646dcc4
 ---> b3d1964b4d47
Step 5/7 : FROM scratch
Step 6/7 : COPY --from=0 /go/hello .
 ---> 52e976b8f1f3
Step 7/7 : CMD ["./hello"]
 ---> Running in 2247a541f3c6
Removing intermediate container 2247a541f3c6
 ---> cb05b87d0012
Successfully built cb05b87d0012
Successfully tagged go-hello:scratch


$docker run --rm -it go-hello:scratch
Hello, world!

# 构建镜像的大小比对
$docker images go-hello
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
go-hello            scratch             cb05b87d0012        3 minutes ago       2.07MB #构建的镜像大小正好就是 2 MB
go-hello            stage               5934753f8f4f        5 hours ago         73.9MB
go-hello            latest              d1bb1eb974f4        6 hours ago         812MB

<br>

静态库实践:

mkdir /opt/gcc-static && cd $_
cat > Dockerfile-static<<EOF
FROM gcc AS mybuildstage
WORKDIR /src
COPY hello.c .
RUN gcc -o hello hello.c -static
FROM scratch
COPY --from=0 /src/hello .
CMD ["./hello"]
EOF

构建实践:

$docker build -f Dockerfile-static -t gcc-static:scratch .
Sending build context to Docker daemon  3.072kB
Step 1/7 : FROM gcc AS mybuildstage
 ---> 21f378ba43ec
Step 2/7 : WORKDIR /src
 ---> Running in 7055a4f90b2a
Removing intermediate container 7055a4f90b2a
 ---> a02107ce9204
Step 3/7 : COPY hello.c .
 ---> cd278ef2de60
Step 4/7 : RUN gcc -o hello hello.c -static
 ---> Running in 368cc4437ac4
Removing intermediate container 368cc4437ac4
 ---> 506e744092b2
Step 5/7 : FROM scratch
Step 6/7 : COPY --from=0 /src/hello .
 ---> 1867700fa780
Step 7/7 : CMD ["./hello"]
 ---> Running in bb19b78211bd
Removing intermediate container bb19b78211bd
 ---> a6991f9571b3
Successfully built a6991f9571b3
Successfully tagged gcc-static:scratch

# 镜像查看以及执行镜像
$docker images gcc-static
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
gcc-static          scratch             a6991f9571b3        56 seconds ago      945kB  #此种方式比golang多阶段构建生成的镜像更小

# 但是调试是真的烦
$docker run --rm -it gcc-static:scratch   
Hello, world!

<br>

总结 描述:最后来对比一下不同构建方法构建的镜像大小:

  • 原始的构建方法:1.14 GB
  • 使用 ubuntu 镜像的多阶段构建:64.2 MB
  • 使用 alpine 镜像和静态 glibc:6.5 MB
  • 使用 alpine 镜像和动态库:5.6 MB
  • 使用 scratch 镜像和静态 glibc:940 kB
  • 使用 scratch 镜像和静态 musl libc:94 kB 最终我们将镜像的体积减少了 99.99%。

0x04 补充说明

描述:记录镜像分析缩减建议工具与学习阶段所遇的一些技巧记录

openjdk镜像的tag说明(其它镜像可以类比)

描述:在进行Docker安装Tomcat前我们先简单聊到openjdk镜像的tag说明,因为Tomcat属于Java应用所以安装JDK环境是必不可少的;

将java应用作成docker镜像时,需要镜像中带有jdk或者jre环境,通常有三种情况:

  • 在Dockerfile中加入安装jdk环境的脚本;
  • 镜像中只有应用jar包,启动容器时通过数据卷映射(-v参数)将宿主机的jdk文件夹映射到容器内;
  • 使用带有jdk的镜像作为基础镜像;

三种方式各有优劣

  • 第一种,完全自己动手,可控性强,但增加了Dockerfile编写的工作量,脚本质量要自己保证;
  • 第二种,要求宿主机预先部署jdk,增加了宿主机工作量;
  • 第三种,相对工作量小一些,如果找的基础镜像不合适,会导致做成的镜像体积过大,或者多了些不需要的内容;

为了更加精简以及程序可以正常运行所以我们必须对其基础镜像选择有一个简单的了解; 在hub.docker.com上搜索jdk官方镜像关键字openjdk,点进详情页后寻找我们常用的jdk8的镜像有多个Tags例如:https://hub.docker.com/_/openjdk?tab=tags

实际上Docker大多数应用都默认采用Debian操作系统进行构建镜像,所以我们需要对debian版本号进行一个简单的了解: |debian发行版本号 | 含义 | |:- | :- | |buster|当前的稳定版| |stretch |旧的稳定版,包含了Debian官方最近一次发行的软件包,优先推荐使用的版本| |testing |测试版本,包含了哪些暂时未被收录进”稳定版“的软件包| |ubstable |不稳定版,开发版本|

Q:如此多的的tag我们又该如何选择呢? 答: 常规的应用其Docker镜像Tag往往是相互组合的版本+操作系统发行版本号+ea/slim等关键字组合

# 版本号 openjdk:<version>
# Tag : openjdk:8
基础镜像为Debain上构建的openjdk镜像;

# stretch 关键字
# Tag : 8-jdk-stretch / 8-jre-stretch 
其中的stretch表明这个镜像的操作系统是debian9在此基础上构建了Jdk8或者Jre8环境的docker镜像

# ea关键字
# Tag : 16-ea-jdk
其中的ea表示的意思是"Early Access"正是发布之前的预览版本,该版本带有新特性并且修复了若干bug,非Release版本不建议生产环境使用;

# alpine 关键字 openjdk:<version>-alpine
# Tag : 13-ea-19-jdk-alpine3.9
其中以alpine作为基础镜像构建出的openjdk镜像

# oraclelinux7关键字 openjdk:<version>-oraclelinux7
# Tag : 13-ea-oraclelinux7
其中的oraclelinux7表明镜像的操作系统是Oracle Linux 7,从jdk12开始openjdk官方开始提供基于Oracle Linux 7的jdk镜像;

# buster 关键字
# Tag : 15-jdk-buster
其中buster表明当前的是稳定的版本

# slim 关键字
# Tag : 15-jdk-slim
其中slim表明当前的jre并非标准jre版本而是headless版本,该版本的特点是去掉了UI、键盘、鼠标相关的库,因此更加精简适合服务端应用使用

# slim-buster 关键字
# Tag : 15-slim-buster
其中slim-buster表示当前镜像是精简稳定版本

# windowsservercore 关键字 openjdk:<version>-windowsservercore
# Tag : 15-windowsservercore-ltsc2016  #其大小超乎您的想象
基于Windows Server Core (microsoft/windowsservercore)。因此它只适用于镜像的位置,比如Windows 10 Professional/Enterprise(周年纪念版)或Windows Server 2016。

<br/>

辅助工具

描述:以下工具的使用可以在我的另外一篇文章Docker使用辅助工具汇总中找到它:

  • 1.Docker 镜像分析工具 Dive

WeiyiGeek Blog - 为了能到远方,脚下的每一步都不能少。

本文章来源 Blog 站点(友链交换请邮我哟):

  • https://weiyigeek.top # 国内访问较慢
  • https://blog.weiyigeek.top # 更新频繁
  • https://weiyigeek.gitee.io # 国内访问快可能会有更新不及时得情况

更多学习笔记文章请关注 WeiyiGeek 公众账号 【点击我关注】

WeiyiGeek-公众账号

Dive — 分析和浏览 Docker 容器镜像内部的工具

Dive — 分析和浏览 Docker 容器镜像内部的工具

Dive 是一个 Docker 镜像分析工具,用来分析和浏览 Docker 镜像每层的内容,此外,它还可以帮助使用者用于构建镜像,

关于为什么 Docker 容器镜像这么大?docker镜像为什么那么小的问题我们已经讲解完毕,感谢您的阅读,如果还想了解更多关于.NET Core 2.1 容器镜像将从 Docker Hub 中删除、03 docker容器镜像基础、10.Docker容器镜像体积缩小之奇技淫巧、Dive — 分析和浏览 Docker 容器镜像内部的工具等相关内容,可以在本站寻找。

本文标签:

上一篇如何对 Arduino 代码进行单元测试?(arduino测试程序)

下一篇如何使用复合可绘制对象而不是包含 ImageView 和 TextView 的 LinearLayout(复合对象图形合并)