# 多数 Dockerfile 示例可能都不够严谨

原文：[Broken by default: why you should avoid most Dockerfile examples](https://pythonspeed.com/articles/dockerizing-python-is-hard/)

作者：[Itamar Turner-Trauring](mailto://itamar@pythonspeed.com)

想把 Python 应用打包成 Docker 镜像，很自然的行为就是上网搜个例子。简单的一搜，就能得出大量简单易懂的结果。

不幸的是这些简单方便的例子经常是有一些这样那样的缺陷，有的显而易见，有的可能就不那么明显了。为了发掘这些问题，本文中将要：

1. 用一个在 Google 搜索结果中常见的 Dockerfile 开始。
1. 展示其中的问题。
1. 给出一些修复问题的建议。

> 本文的 Docerfile 仅用于解决这里发现的问题，不能算作最佳实践。

## 先天不足

看看下面的 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：Python 版本的不确定性

这里第一个需要注意的问题是，基础镜像是：`python:3`。在编写这个文件的时候，会安装 Python 3.7，但是可能未来某一天的重新构建，可能会变成 Python 3.8。这种版本切换，可能会让这一应用完全无法运行，从而打断了产品的交付过程。

**建议**：使用 `python:3.7` 作为基镜像。

### 问题 2：依赖库版本的不确定性

这里的 `pip install flask`，没有包含版本信息，所以每次重新构建，都可能升级成最新的 flask（或者 flask 的依赖，又或者 flask 的依赖的依赖）。保持兼容自然没问题，否则的话麻烦就大了。

**建议**：创建 `requirements.txt`，其中记载所有依赖的版本号，可以用 [pip-tools](https://hynek.me/articles/python-app-deps-2018/) 完成这一任务。

### 问题 3：代码的变更会让构建缓存失效

Docker 的层缓存对提高构件速度很有帮助。但是如果把 `COPY` 操作放在 `pip install` 前面，所有后续的层就都失效了，也就是说这一镜像会完全重新构建。

**建议**：在合适的时机进行文件复制。

### 问题 4：用 root 身份运行

缺省情况下，Docker 容器是用 root 身份运行的，这[并不安全](http://canihaznonprivilegedcontainers.info/)。

**建议**：如果不是有特定需要，例如监听 1024 以下的端口或者完成一些必须 root 身份的操作，建议使用非 root 账号。

## 改良版本

为了解决上面发现的几个问题，对 Dockerfile 做出如下修改：

~~~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` 进行常规更新，以便进行安全更新和 Bug 修复，可能还要[禁用缓存对镜像进行周期性重建](https://pythonspeed.com/articles/docker-cache-insecure-images/)，来获取安全加固。

## 参考链接

- https://pythonspeed.com/articles/docker-cache-insecure-images/
- https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
- https://hynek.me/articles/python-app-deps-2018/ 


