如何将你的Python项目全面自动化?
每个项目——无论你是在从事 Web 应用程序、数据科学还是 AI 开发——都可以从配置良好的 CI/CD、Docker 镜像或一些额外的代码质量工具(如 CodeClimate 或 SonarCloud)中获益。所有这些都是本文要讨论的内容,我们将看看如何将它们添加到 Python 项目中!
- 在写这篇文章之前,我还写了一篇“Python 项目终极设置”,读者感兴趣的话,可以先读下那一篇:https://martinheinz.dev/blog/14。
GitHub 库中提供了完整的源代码和文档:https://github.com/MartinHeinz/python-project-blueprint。
开发环境中可调试的 Docker 容器
有些人不喜欢 Docker,因为容器很难调试,或者构建镜像需要花很长的时间。那么,就让我们从这里开始,构建适合开发的镜像——构建速度快且易于调试。
为了使镜像易于调试,我们需要一个基础镜像,包括所有调试时可能用到的工具,像bash
、vim
、netcat
、wget
、cat
、find
、grep
等。它默认包含很多工具,没有的也很容易安装。这个镜像很笨重,但这不要紧,因为它只用于开发。你可能也注意到了,我选择了非常具体的映像——锁定了 Python 和 Debian 的版本——我是故意这么做的,因为我们希望最小化 Python 或 Debian 版本更新(可能不兼容)导致“破坏”的可能性。
作为替代方案,你也可以使用基于 Alpine 的镜像。然而,这可能会导致一些问题,因为它使用musl libc
而不是 Python 所依赖的glibc
。所以,如果决定选择这条路线,请记住这一点。至于构建速度,我们将利用多阶段构建以便可以缓存尽可能多的层。通过这种方式,我们可以避免下载诸如gcc
之类的依赖项和工具,以及应用程序所需的所有库(来自requirements.txt
)。
为了进一步提高速度,我们将从前面提到的python:3.8.1-buster
创建自定义基础镜像,这将包括我们需要的所有工具,因为我们无法将下载和安装这些工具所需的步骤缓存到最终的runner
镜像中。说的够多了,让我们看看Dockerfile
:
从上面可以看到,在创建最后的runner
镜像之前,我们要经历 3 个中间镜像。首先是名为builder
的镜像,它下载构建最终应用所需的所有必要的库,其中包括gcc
和 Python 虚拟环境。安装完成后,它还创建了实际的虚拟环境,供接下来的镜像使用。接下来是build -venv
镜像,它将依赖项列表(requirements.txt
)复制到镜像中,然后安装它。缓存会用到这个中间镜像,因为我们只希望在requirement .txt
更改时安装库,否则我们就使用缓存。
在创建最终镜像之前,我们首先要针对应用程序运行测试。这发生在tester
镜像中。我们将源代码复制到镜像中并运行测试。如果测试通过,我们就继续构建runner
。
对于runner
镜像,我们使用自定义镜像,其中包括一些额外的工具,如vim
或netcat
,这些功能在正常的 Debian 镜像中是不存在的。
- 你可以在 Docker Hub 中找到这个镜像:
https://hub.docker.com/repository/docker/martinheinz/python-3.8.1-buster-tools 你也可以在
base.Dockerfile 中查看其非常简单的`Dockerfile`
:
https://github.com/MartinHeinz/python-project-blueprint/blob/master/base.Dockerfile
那么,我们在这个最终镜像中要做的是——首先我们从tester
镜像中复制虚拟环境,其中包含所有已安装的依赖项,接下来我们复制经过测试的应用程序。现在,我们的镜像中已经有了所有的资源,我们进入应用程序所在的目录,然后设置ENTRYPOINT
,以便它在启动镜像时运行我们的应用程序。出于安全原因,我们还将USER
设置为1001
,因为最佳实践告诉我们,永远不要在root
用户下运行容器。最后两行设置镜像标签。它们将在使用make
目标运行构建时被替换 / 填充,稍后我们将看到。
针对生产环境优化过的 Docker 容器
当涉及到生产级镜像时,我们会希望确保它们小而安全且速度快。对于这个任务,我个人最喜欢的是来自 Distroless 项目的 Python 镜像。可是,Distroless 是什么呢?
这么说吧——在一个理想的世界里,每个人都可以使用FROM scratch
构建他们的镜像,然后作为基础镜像(也就是空镜像)。然而,大多数人不愿意这样做,因为那需要静态链接二进制文件,等等。这就是 Distroless 的用途——它让每个人都可以FROM scratch
。
好了,现在让我们具体描述一下 Distroless 是什么。它是由谷歌生成的一组镜像,其中包含应用程序所需的最低条件,这意味着没有 shell、包管理器或任何其他工具,这些工具会使镜像膨胀,干扰安全扫描器(如 CVE),增加建立遵从性的难度。
现在,我们知道我们在干什么了,让我们看看生产环境的Dockerfile
……实际上,这里我们不会做太大改变,它只有两行:
我们需要更改的只是用于构建和运行应用程序的基础镜像!但区别相当大——我们的开发镜像是 1.03GB,而这个只有 103MB,这就是区别!我知道,我已经能听到你说:“但是 Alpine 可以更小!”是的,没错,但是大小没那么重要。你只会在下载 / 上传时注意到镜像的大小,这并不经常发生。当镜像运行时,大小根本不重要。
比大小更重要的是安全性,从这个意义上说,Distroless 肯定更有优势,因为 Alpine(一个很好的替代选项)有很多额外的包,增加了攻击面。关于 Distroless,最后值得一提的是镜像调试。考虑到 Distroless 不包含任何 shell(甚至不包含sh
),当你需要调试和查找时,就变得非常棘手。为此,所有 Distroless 镜像都有调试版本。
因此,当遇到问题时,你可以使用debug
扫描关注公众号