避免使用具有某些缺陷的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