创建一个Docker容器时最好将镜像保持最小的状态。这样通常可以使构建和部署容器更快。
每个容器应该包含应用程序代码,特定于语言的依赖项,操作系统依赖项。任何其他的多余指令不仅使镜像变得更加臃肿也创造了很多潜在的安全问题。如果在部署到生产环境的容器中有像gcc这样的工具,那么具有shell访问权限的攻击者可以轻松地构建工具来访问其他内部系统。因此需要构建多层防御层来最大限度地减少攻击造成的伤害。
我最近在研究Python网络服务器。requirements.txt文件大多是:
Flask>=0.12,=0.9.2,=0.1.5,=1.4.2,=19,
精简在程序开发中很重要,但太小也可能有害。我们可以重构所有容器,但事实上我们很难忽视时间的成本。值得尊敬的是并不是所有的程序都很臃肿,比如alpine,该映像的大小仅为5 MB。您还可以使用最小的POSIX环境来构建应用程序。
FROM python:3.7-alpineCOPY . /appWORKDIR /appRUN pip install -r requirements.txtCMD ["gunicorn", "-w 4", "main:app"]此容器占用大小为114MB。其中,基本图像为86.7MB(在写入时)。这意味着我们的应用程序负责额外的27.3MB。注意:Alpine默认使用musl而不是glibc。这意味着如果不强制重新编译,一些Python程序将无法使用。
每次我们对源代码进行更改或者重建容器时,都会重新下载并重新安装依赖项。这显然是极大地浪费,进行迭代式开发需要太多时间。所以我们需要重写Dockerfile来利用缓存。
FROM python:3.7-alpineCOPY requirements.txt /RUN pip install -r /requirements.txtCOPY src/ /appWORKDIR /appCMD ["gunicorn", "-w 4", "main:app"]这种方式重写我们的Dockerfile会使用Docker的层缓存,并且如果requirements.txt文件没有更改,则会跳过安装Python依赖。这就可以使加快我们的构建速度,但它对整体图像大小没有影响。
如果仔细观察上面的Docker构建输出,可以看到以下内容:
Building wheels for collected packages: Flask-SSLify, Flask-Admin, itsdangerous, wtforms, MarkupSafeRunning setup.py bdist_wheel for Flask-SSLify: startedRunning setup.py bdist_wheel for Flask-SSLify: finished with status ‘done’Stored in directory: /root/.cache/pip/wheels/70/14/5b/fbd15774657c5cadc661a66236d121640c60dd9382f2a28469Running setup.py bdist_wheel for Flask-Admin: startedRunning setup.py bdist_wheel for Flask-Admin: finished with status ‘done’Stored in directory: /root/.cache/pip/wheels/3f/0f/33/5e27d4e7ba9459198695c28f879659197e33be5d5338a07a1bRunning setup.py bdist_wheel for itsdangerous: startedRunning setup.py bdist_wheel for itsdangerous: finished with status ‘done’Stored in directory: /root/.cache/pip/wheels/fc/a8/66/24d655233c757e178d45dea2de22a04c6d92766abfb741129aRunning setup.py bdist_wheel for wtforms: startedRunning setup.py bdist_wheel for wtforms: finished with status ‘done’Stored in directory: /root/.cache/pip/wheels/36/35/f3/7452cd24daeeaa5ec5b2ea13755316abc94e4e7702de29ba94Running setup.py bdist_wheel for MarkupSafe: startedRunning setup.py bdist_wheel for MarkupSafe: finished with status ‘done’Stored in directory: /root/.cache/pip/wheels/88/a7/30/e39a54a87bcbe25308fa3ca64e8ddc75d9b3e5afa21ee32d57当pip install运行时,它还存储了我们下载到/root/.cache的依赖项的备份。当我们在Docker之外使用本地开发时,这些确实有用。但是,这个目录占用了我们27.3MB的空间,'app'占用7MB。。。。我们可以通过利用Docker另一个功能 - 多级构建来解决这一点。
Docker 17.05增加了对多级构建的支持。这意味着可以在一个映像中构建依赖项,然后可以将其导入另一个映像。现在重写我们的Dockerfile以使用多级构建:
FROM python:3.7-alpine as baseFROM base as builderRUN mkdir /installWORKDIR /installCOPY requirements.txt /requirements.txtRUN pip install --install-option="--prefix=/install" -r /requirements.txtFROM baseCOPY --from=builder /install /usr/localCOPY src /appWORKDIR /appCMD ["gunicorn", "-w 4", "main:app"]这个Docker容器大小为103MB,编译的Python依赖项的大小为21.9M(我通过在构建容器中运行du -h / install来解决这个问题)。现在我们仍可以进一步压缩其大小,比如在容器构建时删除无关文件,如文档,测试等。但是要做好被打的准备----手动滑稽 :huaji3: