Dockerfile最佳实践

2021-01-03 16:55:23

不幸的是,编写有价值的Dockerfile并不像您想象的那么简单。大多数狂野的Docker镜像在这里都失败了,甚至专业人员[1]常常[2]错[2]错[3]。

该存储库提供了编写Dockerfile的最佳实践,这些年来,我(@slimsag)从我的个人项目和我的工作@sourcegraph中都学到了很多东西。这只是指导,而不是强制性的规定-有时可能有原因不执行此处所述的内容,但是如果您不知道,那么这可能就是您应该执行的操作。

将Dockerfile复制到您自己的项目中,然后按照注释创建您的Dockerfile。

以非root用户身份运行容器会大大降低容器“>”的风险。主机特权升​​级可能会发生。这是附加的安全性好处。 (Docker文档,Bitnami博客文章)

低于10,000的UID在多个系统上存在安全风险,因为如果有人确实设法在Docker容器外部提升特权,则其Docker容器的UID可能与特权更高的系统用户的UID重叠,从而向他们授予其他权限。为了获得最佳安全性,请始终以10,000以上的UID运行进程。

最终,与您的容器打交道的人将需要操纵容器所拥有文件的文件许可权。如果您的容器没有静态UID / GID,则必须从正在运行的容器中提取此信息,然后他们才能在主机上分配正确的文件权限。最好对所有不变的容器使用单个静态UID / GID。我们建议使用10000:10001,这样按照以下最佳做法,chown 10000:10001 files /始终适用于容器。

我们建议使用major.minor(而不是major.minor.patch)使用特定图像版本来固定图像标签,以确保您始终:

保持构建正常工作(最新版本意味着您的构建将来可能会任意中断,而major.minor应该意味着这不会发生)

SHA固定为您提供了完全可靠和可复制的版本,但这也可能意味着您没有任何明显的方法可以从使用的基本映像中提取重要的安全修复程序。如果使用major.minor标记,则在生成映像的新版本时会意外获得安全修复程序-生成的复制性较差。

考虑使用docker-lock:此工具可准确跟踪您用于构建的Docker映像SHA,同时使您使用的实际映像仍然是major.minor版本。这样,您就可以像使用SHA固定一样重现构建,而在发布重要更新时就可以像使用major.minor版本一样获得重要的安全更新。

如果您是一家大型公司/组织,并且愿意启动诸如图像安全扫描仪,自动依赖项更新等之类的基础架构,那么也请考虑采用这种方法。

即使您认为您的应用程序正确处理了信号,我们还是建议您在Dockerfile中使用tini作为ENTRYPOINT。如果您在应用程序中弄错了主机系统和其他运行在其上的容器,则可能会改变其稳定性。有关详细信息和优点,请参见tini文档:

它可以保护您免受意外创建僵尸进程的软件的侵害,因为僵尸进程可能(随着时间的推移!)使整个系统缺少PID(并使其无法使用)。

它可确保默认信号处理程序适用于您在Docker映像中运行的软件。例如,对于Tini,即使您没有显式安装信号处理程序,SIGTERM也会正确终止您的进程。

它是如此完全透明地执行!在没有Tini的情况下工作的Docker映像将在没有任何更改的情况下与Tini一起工作。

它使人们可以根据人体工程学将参数传递给您的二进制文件,而不必猜测其名称,例如他们可以写:

如果CMD包含二进制名称,那么他们必须猜测您的二进制名称是什么才能传递参数等。

如果您希望Dockerfile在旧/旧版Linux系统和Docker for Mac版本上运行,并希望避免DNS解析问题,请安装绑定工具。

不幸的是,尽管Docker确实是本地添加的,但它是可选的(您必须将--init传递给docker run命令)。另外,因为它是运行时的功能,例如Kubernetes不会使用Docker运行时,而是使用其他容器运行时,它不一定总是默认的,因此最好是在映像中提供有效的入口点(如tini)。

这取决于。我们建议在此处固定major.minor,因为我们认为创建新Docker映像的普通开发人员最有可能有效地管理提供最大安全性的日常工作。如果您是一家较大的公司/组织,则可以考虑以下方法:

使用许多自动图像漏洞扫描工具,例如GCR漏洞扫描,以使您知道图像何时存在漏洞。

使用自动图像标签更新软件(例如Renovate)来更新图像标签并得到通知。 但是,这显然需要更多的工作和基础架构,因此我们在这里不建议这样做,因为期望大多数人会锁定SHA,并且可能永远不会再对其进行更新-因此永远不会在其映像中获得安全修复程序。