1.概述
在过去的几年中,Docker已成为Linux上容器化的事实上的标准。 Docker易于使用,并提供轻量级虚拟化,使其成为构建应用程序和微服务的理想选择,因为越来越多的服务在云中运行。
尽管创建我们的第一张图像可能相对容易,但是构建有效的图像仍需要深思熟虑。在本教程中,我们将看到有关如何编写有效的Docker映像的示例以及每个建议背后的原因的示例。
让我们从使用官方图片开始。
2.以官方形象为基础
2.1 什么是官方镜像?
官方Docker映像是由Docker赞助的团队创建或维护的,或者至少由他们批准的映像。他们在GitHub项目上公开管理Docker映像。当发现漏洞时,它们也会进行更改,并确保映象是最新的并遵循最佳实践。
让我们通过使用Nginx官方图像的示例更清楚地看到这一点。网络服务器的创建者维护此镜像。
假设我们要使用Nginx托管我们的静态网站。我们可以创建我们的Dockerfile并基于官方映像:
FROM nginx:1.19.2
COPY my-static-website/ /usr/share/nginx/html
然后我们可以建立我们的形象:
$ docker build -t my-static-website .
最后,运行它:
$ docker run -p 8080:80 -d my-static-website
我们的Dockerfile只有两行。基本官方映像负责Nginx服务器的所有详细信息,例如默认配置文件和应公开的端口。
更具体地说,基本映像阻止Nginx成为守护程序并结束初始过程。在其他环境中也会出现这种行为,但是在Docker中,这被解释为应用程序的结尾,因此容器终止。解决方案是将Nginx配置为不成为守护程序。这是官方映像中的配置:
CMD ["nginx", "-g", "daemon off;"]
当我们将图像基于官方镜像时,我们避免了难以调试的意外错误。官方的镜像维护者是Docker和我们要使用的软件的专家,因此我们将从他们的全部知识中受益,并可能节省时间。
2.2 由其创作者维护的镜像
尽管从前面解释的意义上讲不是官方的,但Docker Hub中还有其他镜像,这些镜像也由应用程序的创建者维护。
让我们用一个例子来说明这一点。 EMQX是MQTT消息代理。假设我们要将此代理用作应用程序中的微服务之一。我们可以基于他们的映像并添加我们的配置文件。甚至更好的是,我们可以使用它们的配置通过环境变量配置EMQX。
例如,要更改EMQX监听的默认端口,我们可以添加EMQX_LISTENER__TCP__EXTERNAL
环境变量:
$ docker run -d -e EMQX_LISTENER__TCP__EXTERNAL=9999 -p 9999:9999 emqx/emqx:v4.1.3
作为特定软件背后的社区,他们处于提供其软件Docker映像的最佳位置。
在某些情况下,对于我们要使用的应用程序,我们找不到任何形式的官方图像。即使在那种情况下,我们也会从Docker Hub中搜索可以用作参考的镜像而受益。
让我们以H2为例。 H2是用Java编写的轻量级关系数据库。尽管没有H2的官方图片,但第三方创建了一个并对其进行了很好的记录。我们可以使用他们的GitHub项目来学习如何将H2用作独立服务器,甚至可以协作以使项目保持最新状态。
即使我们仅将Docker映像项目用作构建映像的起点,我们也可能会从头开始学习更多。
3.尽可能避免构建新映像
在使用Docker时,即使与基本映像相比变化不大,我们仍可能养成始终创建新映像的习惯。对于这些情况,我们可以考虑将我们的配置直接添加到正在运行的容器中,而不是构建一个image 。
每次更改时,都需要重新构建自定义Docker映像。之后还需要将它们上传到注册仓库。如果镜像包含私有信息,我们可能需要将其存储在私有存储库中。在某些情况下,通过使用基本映像并动态配置它们,而不是每次都构建自定义映像,我们可能会获得更多好处。
让我们以HAProxy为例来说明这一点。与Nginx一样,HAProxy可以用作反向代理。 Docker社区维持其官方形象。
假设我们需要配置HAProxy以将请求重定向到应用程序中的适当微服务。所有这些逻辑都可以写在一个配置文件中,例如my-config.cfg
。 Docker映像要求我们将配置放置在特定路径上。
让我们看看如何通过在运行容器上安装的自定义配置来运行HAProxy:
$ docker run -d -v my-config.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro haproxy:2.2.2
这样,即使升级HAProxy,也变得更加简单,因为我们只需要更改标签即可。当然,我们还需要确认我们的配置仍适用于新版本。
如果我们要构建一个由许多容器组成的解决方案,则可能已经在使用编排器,例如Docker Swarm或Kubernetes。它们提供了存储配置并将其链接到正在运行的容器的方法。 Swarm称它们为Configs ,而Kubernetes称它们为ConfigMaps 。
编排工具已经考虑过,我们可以在我们使用的镜像之外存储一些配置。在某些情况下,最好将配置保留在映像之外。
4.创建精简版的镜像
镜像大小很重要,原因有两个。首先,较小的镜像传输速度更快。当我们在开发机器中构建映像时,它看起来可能不会改变游戏规则。但是,当我们在CI / CD管道上构建多个映像并部署到多个服务器时,每次部署节省的总时间可能是可感知的。
其次,要实现图像的精简版,我们需要删除镜像未使用的额外软件包。这将有助于我们减少攻击面,从而提高图像的安全性。
让我们看两种减小Docker映像大小的简单方法。
4.1 可用时使用精简版版本
在这里,我们有两个主要选择:精简版的Debian和Alpine Linux发行版。
Slim版本是Debian社区的一项工作,旨在从标准映像中删除不必要的文件。许多Docker映像已经是基于Debian的精简版本。
例如,HAProxy和Nginx映像基于Debian发行版debian:buster-slim
。因此,这些映像从数百MB变为只有几十MB。
在某些其他情况下,该图像提供了标准版本的标准版本和精简版本。例如,最新的Python映像提供了一个苗条的版本,当前为python:3.7.9-slim
,它几乎比标准映像小十倍。
另一方面,许多图像都提供Alpine版本,就像我们之前提到的Python图像一样。基于Alpine的图像通常约为10 MB 。
Alpine Linux在设计之初就考虑了资源效率和安全性。这使其非常适合基础Docker映像。
需要牢记的一点是,Alpine Linux几年前选择将系统库从更常见的glibc
更改为musl
。尽管大多数软件都可以正常运行,但是如果我们选择Alpine作为基础映像,那么我们可以很好地对我们的应用程序进行彻底的测试。
4.2 使用多阶段构建
多阶段构建功能允许在同一Dockerfile中的多个阶段中构建映像,通常使用下一个阶段中的上一个阶段的结果。让我们看看它如何有用。
假设我们要使用HAProxy并通过其REST API(即Data Plane API)动态地对其进行配置。由于该API二进制文件在基础HAProxy映像中不可用,因此我们需要在构建期间下载它。
我们可以在一个阶段中下载HAProxy API二进制文件,并将其提供给下一阶段:
FROM haproxy:2.2.2-alpine AS downloadapi
RUN apk add --no-cache curl
RUN curl -L https://github.com/haproxytech/dataplaneapi/releases/download/v2.1.0/dataplaneapi_2.1.0_Linux_x86_64.tar.gz --output api.tar.gz
RUN tar -xf api.tar.gz
RUN cp build/dataplaneapi /usr/local/bin/
FROM haproxy:2.2.2-alpine
COPY --from=downloadapi /usr/local/bin/dataplaneapi /usr/local/bin/dataplaneapi
...
第一阶段, downloadapi
,下载最新的API并解压缩tar文件。第二阶段复制二进制文件,以便HAProxy稍后可以使用它。我们不需要卸载curl
或删除下载的tar文件,因为第一阶段已被完全丢弃,并且不会出现在最终映像中。
在其他一些情况下,多阶段图像的好处更加明显。例如,如果我们需要从源代码进行构建,则最终映像将不需要任何构建工具。第一阶段可以安装所有构建工具并构建二进制文件,而下一阶段将仅复制那些二进制文件。
即使我们不总是使用此功能,也很高兴知道它的存在。在某些情况下,精简版我们的镜像可能是最佳选择。
5.结论
容器化将继续存在,而Docker是开始使用它的最简单方法。它的简单性可以帮助我们快速提高生产力,尽管有些经验教训只能通过经验来学习。
在本教程中,我们回顾了一些构建更强大和安全的图像的技巧。由于容器是现代应用程序的基础,因此它们越可靠,我们的应用程序就越强大。
0 评论