精华内容
下载资源
问答
  • Photo by William Warby on Unsplash您是否曾经想过为什么您的单应用程序Docker容器会增长到400 MB? 或者,也许为什么一个只有几十MB的应用程序二进制...Docker镜像层Docker容器镜像本质上是堆积的文件,稍后将被实...
    88348a45fc01e06d5eda649120d78c6b.png

    Photo by William Warby on Unsplash

    您是否曾经想过为什么您的单应用程序Docker容器会增长到400 MB? 或者,也许为什么一个只有几十MB的应用程序二进制文件会生成一个MB的Docker映像?

    在本文中,我们将回顾一些导致容器变胖的主要因素,以及为您的项目提供超薄Docker容器的最佳实践和技巧。

    Docker镜像层

    Docker容器镜像本质上是堆积的文件,稍后将被实例化为正在运行的容器。 Docker利用联合文件系统(UnionFS)设计,其中文件按层分组在一起。 每一层可能包含一个或多个文件,并且每一层都位于上一层的顶部。 作为最终用户,我们体验了作为统一文件系统的所有层的所有内容的虚拟运行时合并:

    431c63a7f03b101dc8edfe754081259c.png

    A simplified view of UnionFS (Image by the author)

    UnionFS的底层实现向我们提供的最终文件系统视图(Docker通过可插拔存储驱动程序支持许多不同的视图)具有其所构成的所有层的总大小。 Docker为图像创建容器时,它将以只读格式使用镜像的所有层,并在它们之上添加一个薄的读写层。 这个薄的读写层使我们能够实际修改正在运行的Docker容器中的文件:

    ed60a7a4ac3f544defc12c2908c4ca37.png

    A running container adds a read-write layer on top of an image's read-only layers.

    如果在上述第4层中删除了文件,会发生什么情况? 尽管已删除的文件不会再出现在观察到的文件系统中,但是由于该文件包含在较低的只读层中,因此它最初占用的大小仍将是容器占用空间的一部分。

    从一个小的应用程序二进制文件开始到以一个胖容器镜像结束是相对容易的。 在以下各节中,我们将探索不同的方法来使镜像的尺寸尽可能地薄。

    提防构建路径

    我们构建Docker镜像的最常见方式是什么?

    docker build .

    上面的命令告诉Docker我们将当前的工作文件夹视为构建过程的根文件系统路径。

    为了更好地理解发出上述命令时实际发生的情况,我们应该记住,Docker构建是一个客户端-服务器进程。 我们从中执行docker build命令的Docker CLI(客户端)使用基础Docker引擎(服务器)来构建容器镜像。 为了限制对客户端基础文件系统的访问,构建过程需要知道虚拟文件系统的根目录是什么。 正是在此确切路径下,Dockerifle中的任何命令都试图查找可能最终在正在生成的镜像中结束的文件资源。

    让我们考虑一下我们通常放置Dockerfile的位置。 在项目的根源中,也许吧? 好吧,将项目根目录中的Dockerfile与Docker构建相结合,我们已经有效地添加了完整的项目文件夹作为构建的潜在文件资源。 这可能会导致在构建上下文中不必要地添加多个MB和数千个文件。 如果我们不小心在Dockerfile中定义了ADD / COPY命令,则所有这些文件都可以成为最终镜像的一部分。 在大多数情况下,这不是我们所需要的,因为最终容器镜像中仅应包含一些选定的项目人工制品。

    始终检查是否为Docker构建提供了适当的构建路径,并且Dockerfile没有向镜像添加不必要的文件。 如果出于任何原因确实需要将项目的根目录定义为构建上下文,则可以通过.dockerignore有选择地包括/排除文件。

    挤压图像

    命令合并的另一种方法是使用Docker的squash命令构建镜像,尤其是在使用您不希望或无法修改的其他Dockerfile时。

    除非您使用的是非常老的Docker版本(<1.13),否则Docker允许我们将所有层压缩为一个层,从而有效地删除所有虚幻资源。 我们仍然可以将原始的,未更改的Dockerfile与许多单独的命令一起使用,但是这次我们通过--sqash选项执行构建:

    docker build --squash .

    再次对生成的镜像进行100%优化:

    1993fa5eefd1e74062fa62612c47777c.png

    A 100% optimised image with image squash (Image by the author)

    这里需要注意的有趣一点是,由于我们的Dockerfile创建了一个添加文件的层,然后创建了另一个删除该文件的层,所以squash足够聪明,以至于无需创建任何层(我们只有9ccd9…层 我们正在使用的基本图片)。 然后,额外的荣誉就可以南瓜了。 但是,请注意,挤压图层可能会阻止您或您的镜像用户利用先前缓存的图层。

    注意:使用您不想更改的第三方Dockerfile时,一种最小化任何可能浪费空间的快速简便方法是使用--squash构建它。 您可以使用潜水工具检查图像的最终效率。

    命令合并

    您是否见过带有RUN指令非常长的Dockerfile,其中多个Shell命令与&&聚合在一起? 命令合并。

    通过合并命令,我们实际上是根据此单个long命令的结果创建了一个单独的层。 由于不存在用于添加文件并随后在另一层中删除文件的中间层,因此最后一层将不会为此类幻影文件使用任何空间。 让我们通过修改上述Dockerfile来了解这一点:

    FROM alpineRUN wget http://xcal1.vodafone.co.uk/10MB.zip -P /tmp && rm /tmp/10MB.zip

    现在我们有了一个优化的镜像:

    40f85bb8c341a35a6fca440a8074949d.png

    A 100% optimised image with commands merge (Image by the author)

    当您完成构建Dockerfile时,请检查它以查看是否可以合并命令以减少可能的浪费空间。

    标准化镜像层

    如果基础存储驱动程序支持,则镜像可以具有的最大层数为127。 如果确实需要,可以增加此限制,但是随后您可以缩小构建该映像的位置的选择(即,您需要在类似修改的基础内核上运行的Docker引擎)。

    正如上面有关Docker镜像层的部分中所讨论的,由于UnionFS,进入层的任何文件资源都保留在该层中,即使您在后一层中管理该文件也是如此。 我们来看一个示例Dockerfile:

    FROM alpineRUN wget http://xcal1.vodafone.co.uk/10MB.zip -P /tmpRUN rm /tmp/10MB.zip

    构建以上镜像:

    310da35afbae87cb153753ce61116169.png

    Building a sample image with wasted space (Image by the author)

    并进行潜水检查:

    daede561309db6d0a7159d5d5fb45c9f.png

    Image is only 34% efficient (Image by the author)

    效率为34%表示图像中浪费了很多空间。 这将导致更长的图像获取时间,额外的带宽消耗和更慢的启动时间。

    我们如何摆脱这个浪费的空间?

    删除缓存

    通常,当我们将应用程序容器化时,我们需要使用软件包管理器(例如apk,yum或apt)在生成的映像上提供额外的工具,库或实用程序。

    当我们通过缓存先前获取的软件包来安装软件包时,软件包管理器试图为我们节省时间和带宽。 为了使生成的Docker映像的尺寸尽可能小,我们不需要保留程序包管理器缓存。 毕竟,如果我们的容器需要其他映像,我们总是可以使用更新的Dockerfile重建映像。

    要删除上述三个流行的软件包管理器的软件包管理器缓存,我们可以在聚合(即命令合并)命令的末尾添加以下命令,例如:

    APK: ... && rm -rf /etc/apk/cacheYUM: ... && rm -rf /var/cache/yumAPT: ... && rm -rf /var/cache/apt

    注意:在最终确定Docker镜像之前,请不要忘记删除构建期间使用的所有缓存以及容器正常运行所不需要的任何其他临时文件。

    选择基础镜像

    每个Dockerfile都以FROM指令开头。 在此定义我们将在其上创建自己的图像的基础图像。

    如Docker文档中所述:

    " FROM指令初始化一个新的构建阶段,并为后续指令设置基础映像。 因此,有效的Dockerfile必须以FROM指令开头。 该图像可以是任何有效的图像-从公共存储库中提取图像特别容易。"

    显然,有很多不同的基础图像可供选择,每个基础图像都有自己的优势和功能。 当涉及到您自己的Docker映像的最终大小时,选择一个足以提供应用程序运行所需的工具和环境的映像至关重要。

    正如您所期望的那样,不同的流行基本镜像的大小差异很大:

    5d944cb2fe5b7d4060e77a78c133541d.png

    Popular Docker base images size (Image by the author)

    实际上,使用Ubuntu 19.10基本映像对应用程序进行容器化将至少增加73 MB,而使用Alpine 3.10.3基本映像的完全相同的应用程序只会使大小增加6 MB。 随着Docker缓存图像层,下载/带宽损失仅在您第一次使用该图像启动容器时适用(或者简单地,在拉取图像时)。 但是,增加的大小仍然存在。

    此时,您可能已经得出以下(非常合逻辑的)结论:"那么,我将永远使用Alpine!"。 如果在软件中只有那么清楚的话。

    您会发现,Alpine Linux背后的家伙还没有发现Ubuntu或Debian家伙仍在寻找的特殊秘密调味料。 为了能够创建比Debian小(例如)小的数量级的Docker映像,他们必须对Alpine映像中要包含的内容和不包含的内容做出一些决定。 在选择Alpine作为默认基本映像之前,应检查它是否提供了所需的所有环境。 此外,即使Alpine随附了软件包管理器,您也可能会发现Alpine中不提供您在(例如)基于Ubuntu的开发环境中使用的特定软件包或软件包版本。 在为项目选择最合适的基础映像之前,您应该了解这些权衡并进行测试。

    最后,如果您确实需要使用一个较胖的基础镜像,则可以使用镜像最小化工具(例如免费和开源DockerSlim)来减小最终映像的大小。

    注意:在尝试减小尺寸时,为自己的镜像选择适当的基础镜像很重要。 评估您的选择并选择一张镜像,该镜像可提供您所需的工具,以确保您可以承受的尺寸。

    完全不选择基础镜像

    如果您的应用程序可以在没有基础映像提供任何其他环境的情况下运行,则可以选择完全不使用基础映像。 当然,由于FROM在Dockerfile中是强制性的,因此您仍然必须拥有它并将其指向某个内容。 在这种情况下,您应该使用什么?

    从头开始,即:

    "一个明显为空的图像,特别是对于构建图像"从零开始"。 在构建基础映像(例如debian和busybox)或超小型映像(仅包含一个二进制文件以及它所需要的任何内容,例如hello-world)的上下文中,此映像最有用。 从头开始是Dockerfile中的一项禁止操作,并且不会在映像中创建额外的层。"

    注意:如果您的应用程序包含可以以独立方式运行的自包含可执行文件,则选择暂存基础映像可以使您尽可能减少容器的占用空间。

    多阶段构建

    当Docker 17.05可用时,多阶段构建成为关注的焦点。 期待已久的功能,多阶段构建允许镜像构建器将自定义镜像构建脚本抛在后面,并将所有内容集成到众所周知的Dockerfile格式中。

    用高级术语来说,您可以将多阶段构建视为将多个Dockerfile合并在一起,或者简单地将一个具有多个FROM的Dockerfile合并。

    在进行多阶段构建之前,如果要构建项目的工件并使用Dockerfile将其分发到容器中,则可能必须遵循一个构建过程,最终以一个如下图所示的容器结束:

    109ae256cf8d3cc18c7ce8af16780a1b.png

    Building and distributing your application without multi-stage builds (Image by the author)

    尽管上述过程在技术上没有任何问题,但最终镜像和生成的容器在构建/准备项目人工制品时创建的层上都layers肿了,这些层对于项目的运行时环境不是必需的。

    多阶段构建使您可以将创建/准备阶段与运行时环境分开:

    a6e386e0d8daf3f9b8c9e3f2e88307c8.png

    Multi-stage builds, separation creation/preparation from runtime (image by author)

    您仍然可以使用单个Dockerfile定义完整的构建工作流程。 但是,您可以将文物从一个阶段复制到另一个阶段,同时将数据丢弃在不需要的层中。

    注意:多阶段构建允许您创建跨平台的可重复构建,而无需使用特定于操作系统的自定义构建脚本。 通过有选择地包括在构建的前几个阶段中生成的伪像,可以使镜像的最终大小保持最小。

    结论

    为容器创建Docker镜像是现代软件工程师必须经常处理的过程。 有大量在线资源和示例向您展示如何创建Dockerfile,但是,您应注意生成的映像的大小。

    在本文中,我们回顾了一些方法和技巧,以最大程度地减少Docker镜像的最终大小。 通过精心制作仅包含必要工件的Dockerfile,选择合适的基础映像并使用多阶段构建,可以大大减少Docker映像的最终大小。

    (本文翻译自Nassos Michas的文章《Super-Slim Docker Containers》,参考:https://medium.com/better-programming/super-slim-docker-containers-fdaddc47e560)

    展开全文
  • Photo by William Warby on Unsplash您是否曾经想过为什么您的单应用程序Docker容器会增长到400 MB? 或者,也许为什么一个只有几十MB的应用程序二进制...Docker镜像层Docker容器镜像本质上是堆积的文件,稍后将被实...
    3f106c6e5ebb997915f6d1ed958dfe35.png

    Photo by William Warby on Unsplash

    您是否曾经想过为什么您的单应用程序Docker容器会增长到400 MB? 或者,也许为什么一个只有几十MB的应用程序二进制文件会生成一个MB的Docker映像?

    在本文中,我们将回顾一些导致容器变胖的主要因素,以及为您的项目提供超薄Docker容器的最佳实践和技巧。

    Docker镜像层

    Docker容器镜像本质上是堆积的文件,稍后将被实例化为正在运行的容器。 Docker利用联合文件系统(UnionFS)设计,其中文件按层分组在一起。 每一层可能包含一个或多个文件,并且每一层都位于上一层的顶部。 作为最终用户,我们体验了作为统一文件系统的所有层的所有内容的虚拟运行时合并:

    aae0ea23dcabe4ac6782c76c237088a6.png

    A simplified view of UnionFS (Image by the author)

    UnionFS的底层实现向我们提供的最终文件系统视图(Docker通过可插拔存储驱动程序支持许多不同的视图)具有其所构成的所有层的总大小。 Docker为图像创建容器时,它将以只读格式使用镜像的所有层,并在它们之上添加一个薄的读写层。 这个薄的读写层使我们能够实际修改正在运行的Docker容器中的文件:

    a4938cfc43f4ef874ec5b658a8329a09.png

    A running container adds a read-write layer on top of an image's read-only layers.

    如果在上述第4层中删除了文件,会发生什么情况? 尽管已删除的文件不会再出现在观察到的文件系统中,但是由于该文件包含在较低的只读层中,因此它最初占用的大小仍将是容器占用空间的一部分。

    从一个小的应用程序二进制文件开始到以一个胖容器镜像结束是相对容易的。 在以下各节中,我们将探索不同的方法来使镜像的尺寸尽可能地薄。

    提防构建路径

    我们构建Docker镜像的最常见方式是什么?

    docker build .

    上面的命令告诉Docker我们将当前的工作文件夹视为构建过程的根文件系统路径。

    为了更好地理解发出上述命令时实际发生的情况,我们应该记住,Docker构建是一个客户端-服务器进程。 我们从中执行docker build命令的Docker CLI(客户端)使用基础Docker引擎(服务器)来构建容器镜像。 为了限制对客户端基础文件系统的访问,构建过程需要知道虚拟文件系统的根目录是什么。 正是在此确切路径下,Dockerifle中的任何命令都试图查找可能最终在正在生成的镜像中结束的文件资源。

    让我们考虑一下我们通常放置Dockerfile的位置。 在项目的根源中,也许吧? 好吧,将项目根目录中的Dockerfile与Docker构建相结合,我们已经有效地添加了完整的项目文件夹作为构建的潜在文件资源。 这可能会导致在构建上下文中不必要地添加多个MB和数千个文件。 如果我们不小心在Dockerfile中定义了ADD / COPY命令,则所有这些文件都可以成为最终镜像的一部分。 在大多数情况下,这不是我们所需要的,因为最终容器镜像中仅应包含一些选定的项目人工制品。

    始终检查是否为Docker构建提供了适当的构建路径,并且Dockerfile没有向镜像添加不必要的文件。 如果出于任何原因确实需要将项目的根目录定义为构建上下文,则可以通过.dockerignore有选择地包括/排除文件。

    挤压图像

    命令合并的另一种方法是使用Docker的squash命令构建镜像,尤其是在使用您不希望或无法修改的其他Dockerfile时。

    除非您使用的是非常老的Docker版本(<1.13),否则Docker允许我们将所有层压缩为一个层,从而有效地删除所有虚幻资源。 我们仍然可以将原始的,未更改的Dockerfile与许多单独的命令一起使用,但是这次我们通过--sqash选项执行构建:

    docker build --squash .

    再次对生成的镜像进行100%优化:

    626080fedb3c8dd6d87fe19a408cbd3b.png

    A 100% optimised image with image squash (Image by the author)

    这里需要注意的有趣一点是,由于我们的Dockerfile创建了一个添加文件的层,然后创建了另一个删除该文件的层,所以squash足够聪明,以至于无需创建任何层(我们只有9ccd9…层 我们正在使用的基本图片)。 然后,额外的荣誉就可以南瓜了。 但是,请注意,挤压图层可能会阻止您或您的镜像用户利用先前缓存的图层。

    注意:使用您不想更改的第三方Dockerfile时,一种最小化任何可能浪费空间的快速简便方法是使用--squash构建它。 您可以使用潜水工具检查图像的最终效率。

    命令合并

    您是否见过带有RUN指令非常长的Dockerfile,其中多个Shell命令与&&聚合在一起? 命令合并。

    通过合并命令,我们实际上是根据此单个long命令的结果创建了一个单独的层。 由于不存在用于添加文件并随后在另一层中删除文件的中间层,因此最后一层将不会为此类幻影文件使用任何空间。 让我们通过修改上述Dockerfile来了解这一点:

    FROM alpineRUN wget http://xcal1.vodafone.co.uk/10MB.zip -P /tmp && rm /tmp/10MB.zip

    现在我们有了一个优化的镜像:

    cb305d7dad6a7cbe0511c981f1f4b301.png

    A 100% optimised image with commands merge (Image by the author)

    当您完成构建Dockerfile时,请检查它以查看是否可以合并命令以减少可能的浪费空间。

    标准化镜像层

    如果基础存储驱动程序支持,则镜像可以具有的最大层数为127。 如果确实需要,可以增加此限制,但是随后您可以缩小构建该映像的位置的选择(即,您需要在类似修改的基础内核上运行的Docker引擎)。

    正如上面有关Docker镜像层的部分中所讨论的,由于UnionFS,进入层的任何文件资源都保留在该层中,即使您在后一层中管理该文件也是如此。 我们来看一个示例Dockerfile:

    FROM alpineRUN wget http://xcal1.vodafone.co.uk/10MB.zip -P /tmpRUN rm /tmp/10MB.zip

    构建以上镜像:

    9700fb60fbda5429a5958faf873287d5.png

    Building a sample image with wasted space (Image by the author)

    并进行潜水检查:

    73d98d996fc32c8262882faf363e6ef5.png

    Image is only 34% efficient (Image by the author)

    效率为34%表示图像中浪费了很多空间。 这将导致更长的图像获取时间,额外的带宽消耗和更慢的启动时间。

    我们如何摆脱这个浪费的空间?

    删除缓存

    通常,当我们将应用程序容器化时,我们需要使用软件包管理器(例如apk,yum或apt)在生成的映像上提供额外的工具,库或实用程序。

    当我们通过缓存先前获取的软件包来安装软件包时,软件包管理器试图为我们节省时间和带宽。 为了使生成的Docker映像的尺寸尽可能小,我们不需要保留程序包管理器缓存。 毕竟,如果我们的容器需要其他映像,我们总是可以使用更新的Dockerfile重建映像。

    要删除上述三个流行的软件包管理器的软件包管理器缓存,我们可以在聚合(即命令合并)命令的末尾添加以下命令,例如:

    APK: ... && rm -rf /etc/apk/cacheYUM: ... && rm -rf /var/cache/yumAPT: ... && rm -rf /var/cache/apt

    注意:在最终确定Docker镜像之前,请不要忘记删除构建期间使用的所有缓存以及容器正常运行所不需要的任何其他临时文件。

    选择基础镜像

    每个Dockerfile都以FROM指令开头。 在此定义我们将在其上创建自己的图像的基础图像。

    如Docker文档中所述:

    " FROM指令初始化一个新的构建阶段,并为后续指令设置基础映像。 因此,有效的Dockerfile必须以FROM指令开头。 该图像可以是任何有效的图像-从公共存储库中提取图像特别容易。"

    显然,有很多不同的基础图像可供选择,每个基础图像都有自己的优势和功能。 当涉及到您自己的Docker映像的最终大小时,选择一个足以提供应用程序运行所需的工具和环境的映像至关重要。

    正如您所期望的那样,不同的流行基本镜像的大小差异很大:

    fe1455a9e80b28ab3fb647911e593247.png

    Popular Docker base images size (Image by the author)

    实际上,使用Ubuntu 19.10基本映像对应用程序进行容器化将至少增加73 MB,而使用Alpine 3.10.3基本映像的完全相同的应用程序只会使大小增加6 MB。 随着Docker缓存图像层,下载/带宽损失仅在您第一次使用该图像启动容器时适用(或者简单地,在拉取图像时)。 但是,增加的大小仍然存在。

    此时,您可能已经得出以下(非常合逻辑的)结论:"那么,我将永远使用Alpine!"。 如果在软件中只有那么清楚的话。

    您会发现,Alpine Linux背后的家伙还没有发现Ubuntu或Debian家伙仍在寻找的特殊秘密调味料。 为了能够创建比Debian小(例如)小的数量级的Docker映像,他们必须对Alpine映像中要包含的内容和不包含的内容做出一些决定。 在选择Alpine作为默认基本映像之前,应检查它是否提供了所需的所有环境。 此外,即使Alpine随附了软件包管理器,您也可能会发现Alpine中不提供您在(例如)基于Ubuntu的开发环境中使用的特定软件包或软件包版本。 在为项目选择最合适的基础映像之前,您应该了解这些权衡并进行测试。

    最后,如果您确实需要使用一个较胖的基础镜像,则可以使用镜像最小化工具(例如免费和开源DockerSlim)来减小最终映像的大小。

    注意:在尝试减小尺寸时,为自己的镜像选择适当的基础镜像很重要。 评估您的选择并选择一张镜像,该镜像可提供您所需的工具,以确保您可以承受的尺寸。

    完全不选择基础镜像

    如果您的应用程序可以在没有基础映像提供任何其他环境的情况下运行,则可以选择完全不使用基础映像。 当然,由于FROM在Dockerfile中是强制性的,因此您仍然必须拥有它并将其指向某个内容。 在这种情况下,您应该使用什么?

    从头开始,即:

    "一个明显为空的图像,特别是对于构建图像"从零开始"。 在构建基础映像(例如debian和busybox)或超小型映像(仅包含一个二进制文件以及它所需要的任何内容,例如hello-world)的上下文中,此映像最有用。 从头开始是Dockerfile中的一项禁止操作,并且不会在映像中创建额外的层。"

    注意:如果您的应用程序包含可以以独立方式运行的自包含可执行文件,则选择暂存基础映像可以使您尽可能减少容器的占用空间。

    多阶段构建

    当Docker 17.05可用时,多阶段构建成为关注的焦点。 期待已久的功能,多阶段构建允许镜像构建器将自定义镜像构建脚本抛在后面,并将所有内容集成到众所周知的Dockerfile格式中。

    用高级术语来说,您可以将多阶段构建视为将多个Dockerfile合并在一起,或者简单地将一个具有多个FROM的Dockerfile合并。

    在进行多阶段构建之前,如果要构建项目的工件并使用Dockerfile将其分发到容器中,则可能必须遵循一个构建过程,最终以一个如下图所示的容器结束:

    05fcfc473c15fc6505eada1ae2cd4b16.png

    Building and distributing your application without multi-stage builds (Image by the author)

    尽管上述过程在技术上没有任何问题,但最终镜像和生成的容器在构建/准备项目人工制品时创建的层上都layers肿了,这些层对于项目的运行时环境不是必需的。

    多阶段构建使您可以将创建/准备阶段与运行时环境分开:

    51cded5c47f12b42cf08162b50e56c50.png

    Multi-stage builds, separation creation/preparation from runtime (image by author)

    您仍然可以使用单个Dockerfile定义完整的构建工作流程。 但是,您可以将文物从一个阶段复制到另一个阶段,同时将数据丢弃在不需要的层中。

    注意:多阶段构建允许您创建跨平台的可重复构建,而无需使用特定于操作系统的自定义构建脚本。 通过有选择地包括在构建的前几个阶段中生成的伪像,可以使镜像的最终大小保持最小。

    结论

    为容器创建Docker镜像是现代软件工程师必须经常处理的过程。 有大量在线资源和示例向您展示如何创建Dockerfile,但是,您应注意生成的映像的大小。

    在本文中,我们回顾了一些方法和技巧,以最大程度地减少Docker镜像的最终大小。 通过精心制作仅包含必要工件的Dockerfile,选择合适的基础映像并使用多阶段构建,可以大大减少Docker映像的最终大小。

    (本文翻译自Nassos Michas的文章《Super-Slim Docker Containers》,参考:https://medium.com/better-programming/super-slim-docker-containers-fdaddc47e560)

    展开全文
  • 阿里云容器镜像服务,是一个镜像仓库,可以是公开的,也可以是私有的。 持续集成CI 持续集成指的是,频繁地(一天多次)将代码集成到主干。 它的好处主要有两个。 (1)快速发现错误。每完成一点更新,就集成到...

    阿里云2000优惠礼包领取

    阿里云Codepipeline服务,是一套类似Jenkins的服务(其实我觉得它的核心引擎就是来自Jenkins)。

    阿里云容器镜像服务,是一个镜像仓库,可以是公开的,也可以是私有的。

    持续集成CI

    持续集成指的是,频繁地(一天多次)将代码集成到主干。
    它的好处主要有两个。

    (1)快速发现错误。每完成一点更新,就集成到主干,可以快速发现错误,定位错误也比较容易。
    
    (2)防止分支大幅偏离主干。如果不是经常集成,主干又在不断更新,会导致以后集成的难度变大,甚至难以集成。
    

    持续集成的目的,就是让产品可以快速迭代,同时还能保持高质量。它的核心措施是,代码集成到主干之前,必须通过自动化测试。只要有一个测试用例失败,就不能集成。
    Martin Fowler说过,"持续集成并不能消除Bug,而是让它们非常容易发现和改正。"

    下面就演示一下如何通过阿里云Codepipeline跟容器镜像服务来实现 .Net Core 程序的CICD。

    持续集成

    流程


    代码push后Gitee通过webhook功能触发Codepipeline构建,构建成功后自动推送镜像到容器镜像服务

    新建一个 .Net Core MVC 的程序


    新建一个 .net core mvc 程序名叫CoreCICDTest

        public class Program
        {
            public static void Main(string[] args)
            {
                CreateWebHostBuilder(args).Build().Run();
            }
    
            public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
                WebHost.CreateDefaultBuilder(args)
                    .UseStartup<Startup>()
                     .UseKestrel(options =>
                     {
                         options.Listen(IPAddress.Any, 5000);
                     });
        }
    

    修改Program的main方法,使Kestrel监听5000端口

    @{
        ViewData["Title"] = "Home Page";
    }
    
    
    <h3>
        .NET CORE CICD TEST -- V 1.0
    </h3>
    

    修改Home/index视图

    运行一下看看效果,网站正常显示 .NET CORE CICD TEST -- V 1.0

    新建一个 MSTest 项目


    在CoreCICDTest的解决方案下新建一个 MSTest 项目用来写单元测试,名叫CoreCICDTest.Tests

        [TestClass]
        public class UnitTest1
        {
            [TestMethod]
            public void TestMethod1()
            {
                string str = "00";
    
                Assert.AreEqual(str, "00");
            }
        }
    

    修改UnitTest1文件中的TestMethod1方法,使其成为一个合法的TestMethod

    运行一下单元测试,全部通过

    添加Dockerfile文件

    FROM microsoft/dotnet:latest AS build
    WORKDIR /app
    COPY /. /app
    RUN dotnet restore
    WORKDIR /app/CoreCICDTest.Tests
    RUN dotnet test CoreCICDTest.Tests.csproj
    WORKDIR /app/CoreCICDTest
    RUN dotnet publish -o ./out -c Release
    EXPOSE 5000
    ENTRYPOINT ["dotnet", "out/CoreCICDTest.dll"]
    
    

    Dockerfile注意文件名没有任何后缀,Dockerfile用来在Docker容器内自动test、build我们的代码

    在Gitee上新建一个项目,并把CoreCICDTest解决方案推送上去

    使用Gitee的免费Git服务,新建一个项目名叫CoreCICDTest,使用Git Push命令把本地代码推送上去。

    在阿里云容器镜像服务上新建项目并进行配置



    点击“创建镜像仓库”按钮,弹出创建界面。填写命名空间kklldog,仓库名称cicd_test


    点击下一步,代码源选择“本地仓库”,点击“创建镜像仓库”完成仓库的创建

    在阿里云Codepipeline上新建项目并进行配置


    点击“新建”按钮跳转至新建项目页面。这个界面跟Jenkins简直就是一模一样


    项目名称填写cicd_test,这里没有.net相关的模板,囧!项目类型选择"构建一个自由风格的软件"就可以

    点击下一步,填写项目基本信息,源码选择Gitee。构建类型默认是java,无所谓不用过它

    点击“绑定云码账号”跳转至云码授权页面进行授权,以便阿里云可以拉取云码上的代码

    进行授权后,源码管理界面就可以选择到Gitee上的项目,填写相应的分支

    点击“增加构建步骤”,选择“镜像构建与发布”

    在“镜像构建与发布”界面填写刚才创建的仓库信息

    镜像仓库名格式为namespace/镜像仓库名。如果registry为Docker hub,拉取镜像命令为docker pull docker,则本配置项填写docker;如果 registry为阿里云Docker镜像仓库,拉取镜像命令为docker pull registry.cn-hangzhou.aliyuncs.com/acs-sample/wordpress, 则本配置项填写acs-sample/wordpress。
    
      Registry地址 用来配置docker registry地址,如果为空,默认使用Docker hub registry (https://index.docker.io/v1/);如果使用阿里云registry, 请填写https://registry.cn-beijing.aliyuncs.com/v2/,其中地域(cn-beijing)根据用户实际的镜像仓库地域来修改。
      Registry证书 用来添加授权信息,请添加Registry授权类型的证书。
    


    勾选“远程触发器”,先填写分支master,然后点击“生成”会生成触发器地址,这里好像有点小bug,有的时候这个地址不起效,如果不起效,多生成几次试试

    在“构建后操作”界面填写邮件地址,用来接收邮件通知。勾选“每次不稳定的构建都发送邮件通知”

    在Gitee的CoreCICDTest项目上配置WebHook


    点击“管理>WebHook”菜单,进行WebHook的配置

    WebHook的Url填写刚才Codepipeline里的“远程触发器”里生成的url地址;密码不填;勾选Push事件,勾选“激活”;点击“添加”按钮完成Webhook的配置。这样当我们push代码的时候,Gitee会自动给配置的url发送一次post请求,里面携带了详细的项目信息,提交信息等数据

    当点击“添加”按钮后Gitee会立马往webHook配置的url地址Post一次请求,如果Codepipeline做出“Task has been scheduled to queue”的响应则说明Codepipeline开始进行自动构建了


    返回Codepipeline项目列表,可以看到cicd_test项目已经构建成功了

    这个时候构建的镜像也应该被推送到了容器镜像服务的cicd_test仓库里。

    编写shell脚本运行容器

    sudo vim publish_cicd_test.sh
    输入以下内容
    #!/bin/bash
    sudo docker stop cicd_test
    sudo docker rm cicd_test
    sudo docker rmi registry-vpc.cn-shanghai.aliyuncs.com/kklldog/cicd_test
    sudo docker login --username=xxx --password=xxx registry-vpc.cn-shanghai.aliyuncs.com
    sudo docker pull registry-vpc.cn-shanghai.aliyuncs.com/kklldog/cicd_test:latest
    sudo docker run --name cicd_test -d -p 7000:5000 -v /etc/localtime:/etc/localtime registry-vpc.cn-shanghai.aliyuncs.com/kklldog/cicd_test:latest
    

    新建一个shell脚本命名为publish_cicd_test.sh;使用docker pull从仓库中拉取最近的镜像;使用docker run运行容器

    sudo /bin/bash publish_cicd_test.sh
    


    运行一下shell脚本

    sudo docker ps -a
    

    使用docker ps命令查看一下容器的运行状态,可以看到cicd_test容器已经运行成功了

    使用浏览器访问一下对应的端口,网站已经正常运行了

    Push代码触发构建

    刚才的构建是我们配置Webhook的时候Gitee默认发送的一次请求,正常应该是用户使用git push命令后Gitee会发送一次请求,让我们模拟一下。

    修改home/index页面,把V1.0改成V2.0
    提交成功之后查看Codepipeline项目列表,等待项目构建成功之后,我们再次运行一下publis_cicd_test.sh脚本,成功之后再次使用浏览器访问一下对应的端口看看home/index是否已经变为了V2.0

    可以看到home/index已经变成V2.0了,说明我们的持续集成流程跑通了

    持续交付/部署

    我们上面演示的过程离一开始说的push一下代码就自动构建自动发布程序就差一点点了,太晚了下次再说吧。

    阿里云2000优惠礼包领取

    展开全文
  • Docker(3)-容器镜像

    2019-10-01 03:17:38
    container是image的运行的实例,相同的镜像可以创建出多个不同的容器,就像编程思想中的类与对象的关系。 image可以得很,因此我们需要对它进行分层以减小它的体积,每一层都会对image添加额外的功能和定制...

        Docker image存放在registry中,并且是只读的。以它为模版,我们可以创建自己的container。container是image的运行的实例,相同的镜像可以创建出多个不同的容器,就像编程思想中的类与对象的关系。

        image可以变得很大,因此我们需要对它进行分层以减小它的体积,每一层都会对image添加额外的功能和定制。

        Docker中管理image常用的命令:

    • docker search - 在registry中查找名字中包含特定关键字的image,例如:docker search apache
    • docker pull - 下载image到本地,例如:docker pull ubuntu:16.04
    • docker images - 列出所有设备本地registry中的image
    • docker rmi - 删除一个image,例如:docker rmi ubuntu:14:04
    • docker history - 显示image的历史记录,例如:docker history  apacheweb

     

        Docker container是image的一个运行实例。需要注意的是,container内部生成的数据的生命周期是和container绑定在一起的,如果我们停止容器,数据将会丢失。当然,Docker中有一些方法可以持久化数据,比如使用volume。

        Docker中管理container常用的命令:

    • docker run -  创建并启动容器
    • docker create - 创建一个容器但不启动它,返回容器ID作为输出
    • docker ps - 列出正在运行的容器
    • docker stop - 停止容器
    • docker start - 启动容器
    • docker restart - 重启容器
    • docker rm - 删除容器
    • docker kill - 向容器发送SIGKILL信号
    • docker attach - 连接正在运行的容器并返回其交互式的shell
    • docker logs - 列出容器中发生的事件
    • docker top - 列出容器中正在运行的进程

     

        下面列举一些使用案例:

    docker run -d --name dbserver somerepo/mysql

        基于somerepo/mysql镜像创建并启动一个名为dbserver的容器,并在后台运行它。

    docker create -t -i centos

        创建一个基于centos镜像的容器,并返回它的容器ID。

    docker ps

        列出所有运行的容器并显示其详细信息。

    docker stop d962431b81cb

        停止容器ID为d962431b81cb的容器。

    docker start d962431b81cb

        启动容器ID为d962431b81cb的容器。

    docker kill dbserver

        向名称为dbserver的容器发送SIGKILL信号。

    docker top dbserver

        列出名称为dbserver的容器中正在运行的进程。

     

        请注意:

    • 在使用容器ID作为参数的命令中可以使用容器ID或容器名称。
    • 容器中可能会包含恶意代码,因此请确保镜像的来源和运行的程序可信。
    • 可以使用Ctrl + P + Q退出容器,而不会杀死它,然后使用docker attach命令再次连接到容器。
    • 当stop/kill容器时,容器中生成的任何数据都将丢失。
    • 最好在创建容器时使用--name选项给容器命名,这会使得后续执行容器相关命令时更加地方便。

    转载于:https://www.cnblogs.com/ZachZhou/p/7442666.html

    展开全文
  • 简介题图在这里个人觉得很...不过说实在的,cobbler将Linux的部署得很简单并且快捷,非常适合运维人员对大量新设备进行批量安装的操作,而且也支持绑定mac地址进行无人值守的自动化安装,可以说是非常方便了。Co...
  • 集装箱的诞生,让货物的运输成本大大降低,也让原本混乱的运输环境,得整洁有序,整个物流体系更加高效,最终改变了世界贸易。 在 IT 世界中,容器也扮演着「集装箱」的角色,只不过「运输」内容从货物换成了应用...
  • 镜像大小其实是衡量我们容器打包技术的重要指标,我们应该在不影响应用正常运行的情况下,尽量让我们的容器镜像变得更小,这样,不管是从安全还是维护效率角度来讲,都是最佳实践。本文我们从两种情况阐述我们的问题...
  • 镜像提供了快速生成可运行容器的可能,仓库用于存储镜像,让镜像有了一个统一的聚集地,方便我们去管理和使用大量杂乱的镜像,而分层的思想让镜像变得更加强大;具体解释如下: 镜像: 镜像是什么?最早的概念是和光...
  • 根本不会小,而且会越来越。 其实,commit,顾名思义,就是把当次的修改提交。体现在docker镜像中,就是新的一层。 在 Dockerfile 中, 每一条指令都会创建一个镜像层,继而会增加整体镜像的大小。而...
  • 镜像大小其实是衡量我们容器打包技术的重要指标,我们应该在不影响应用正常运行的情况下,尽量让我们的容器镜像变得更小,这样,不管是从安全还是维护效率角度来讲,都是最佳实践。 本文我们从两种情况阐述我们的...
  • this.$refs.cropper.changeScale() 修改图片大小 正数为变大 负数变小 this.$refs.cropper.getImgAxis() 获取图片基于容器的坐标点 this.$refs.cropper.getCropAxis() 获取截图框基于容器的坐标点 this.$refs....
  • Docker注册表的主要目的是存储和分发Docker镜像,看似是一个相对简单的任务,但是如果遇到了像Uber这样的规模计算集群,就很容易成为可伸缩性的瓶颈。在多区域和混合云系统的计算环境中,镜像分发会得更具挑战性...

空空如也

空空如也

1 2 3 4 5 ... 8
收藏数 146
精华内容 58
关键字:

容器镜像变大