避免使用具有某些缺陷的Dockerfile案例-Python

当需要把你的Python应用打包成Docker镜像的时候,随时从网络中搜索一些例子是一件很自然的事情,并且能快速的为你提供大量简单易用的示例。

但很不幸的是,这些简单易用的示例经常会由于各种或明显、或隐式的方式出现缺陷。为了展示这些缺陷,我将通过下面步骤进行:

1.取得一个在Google搜索中出现频率较高的Dockerfile案例
2.展示可能导致应用缺陷的问题
3.在如何尽可能减少缺陷的方法上提供几点建议

注意:在特定讨论案例中,这些Dockerfile例子并不是最佳实践。

预留的缺陷

思考下面通过搜索Python镜像化案例得到的Dockerfile内容,做了一些小改动,但是功能是一致的。

# DO NOT USE THIS DOCKERFILE AS AN EXAMPLE, IT IS BROKEN
FROM python:3

COPY yourscript.py /

RUN pip install flask

CMD [ "python", "./yourscript.py" ]

Dockerfile内容存在的缺陷

你能从这个镜像中发现多少不同的缺陷或问题?

缺陷#1: 非稳定重建的版本号

第一个能注意到的事实是这个Dockerfile是基于python:3镜像,在书写内容的时候,可能python最新版本是3.7,所以将3.7作为基础镜像。然而当python更新的时候,它将可能切换至3.8来作为基础镜像。

这时重新构建镜像将会导致切换到不同的有可能导致应用故障的Python版本:可能一个微小的版本改变就导致出现部署故障

方案: 使用python:3.7作为基础镜像,精确指定需要的版本号

缺陷#2: 非稳定重建的依赖版本号

同样的,依赖库flask也没有指定一个具体的版本号,每次重建应用镜像的时候,将存在安装新版本的潜在可能性。如果他们能够兼容,那没问题,但是并不能保证一定兼容。

方案: 创建一个包含所有固定版本号依赖项的requirements.txt文件,可使用pip-tools工具来获得。

缺陷#3: 文件的拷贝改变导致构建缓存失效

如果你想快速的构建镜像,最好重复使用Docker的层缓存。但是在运行pip install之前进行拷贝文件的操作,将会导致所有之后的层缓存失效-每次都将重新拉取资源进行构建。

方案: 仅在文件第一次被需要的时候进行拷贝动作

缺陷#4: 运行在root用户是不安全的

默认Docker容器是运行在root下,这有安全风险的。

方案: 最好的方式是运行在非root(non-root)用户下,例如在不使用低于1024的端口监听或者不使用需要root权限的一些操作的情况下就可以这样做。

经过更改的内容

以下是更改后但仍不一定理想的Dockerfile来解决上述的相关问题

FROM python:3.7

COPY requirements.txt /tmp/

RUN pip install -r /tmp/requirements.txt

RUN useradd --create-home appuser
WORKDIR /home/appuser
USER appuser

COPY yourscript.py .

CMD [ "python", "./yourscript.py" ]

尽管这个生成的镜像是你希望在生产环境中运行的,但它仍然不建议使用-它也还存在不足

例如你想以受控的方式来定期更新requirements.txt文件内容,以便于获得安全更新和错误修复。

请参考另一篇文章[定期以非缓存方式构建Docker镜像-以便于获得安全更新和错误修复]

你应该从本文知道的内容

一个有缺陷的Docker镜像能够导致生产故障,而且很多时候构建一个具有最佳实践的镜像是很困难的。所以不要随意从网络中找到一些案例来复制使用:在你搜索这些的时候,建议花一些时间阅读关于最佳实践方面的文章。

作为起始点,我推荐官方关于最佳实践的文档,等等。

结束

links:
docker-cache-insecure-images
Broken by default: why you should avoid most Dockerfile examples