天天看点

使用 Docker 容器化 Python 应用程序的最佳实践(译文-来自:snyk)

作者:闪念基因

通过阅读许多 Python Docker 容器博客,我们发现大多数帖子都提供了如何独立于其框架(Django、Flask、Falcon 等)容器化 Python 应用程序的示例。例如,您可能会看到如下内容:

1FROM python
2WORKDIR /usr/app
3COPY . .
4RUN pip install -r requirements.txt
5CMD [ "python", "app.py" ]           

使用此 Dockerfile,我们可以构建并运行 Python Flask 应用程序:

1docker build -t flask-application .
2docker run -p 8080:5000 flask-application           

两个简单的步骤,它工作得很好,对吧?

虽然此示例对于演示和入门教程来说简单且有用,但它遗漏了许多重要的问题。因此,考虑到这一点,在这篇文章中,我们将关注这些问题,并了解使用 Docker 容器化 Python 应用程序时的 6 条最佳实践。我们将探讨为什么您应该:

  1. 为容器化的 Python 应用程序使用明确且确定的 Docker 基础映像标签。
  2. 将依赖项与源代码分开。
  3. 使用 Python WSGI 进行生产。
  4. 以尽可能低的权限运行容器(永远不要以 root 用户身份)。
  5. 处理应用程序的不健康状态。
  6. 查找并修复 Python Docker 应用程序映像中的安全漏洞。

1. 为容器化的 Python 应用程序使用显式和确定性的 Docker 基础镜像标签

虽然将其用作python我们的 Dockerized Python 应用程序的基础映像似乎合乎逻辑,但它留下了使用哪个版本的 Python 的问题。

在撰写本文时,上述基础镜像是指Dockerfile使用 Python 3.10 的基础镜像。原因?由于我们没有添加特定的标签,它默认为该基础镜像的版本,如果我们查看Docker Hub 的官方镜像页面,:latest它恰好是3.10

由于我们想控制我们容器化的 Python 版本,因此我们应该始终在 Dockerfile 中提供该版本信息。

所以既然我想使用 Python 3.10 版,我只需要将标签添加:3.10到我的 Dockerfile 中,对吧?

好吧……不完全是。

的 Docker 基础镜像标签 :3.10是一个成熟的操作系统,安装了 Python 3.10,其中包含大量您可能永远不会使用的库。此外,它包含如此大量的软件这一事实有一个副作用:由于这些库中存在安全漏洞,它会增加您的安全攻击面。

如果你引入了一个大的 Python Docker 镜像,你将很难维护和保持所有这些库版本是最新的。

如果我们使用Snyk Advisor 工具检查 Python 基础镜像,我们可以看到 Docker 基础镜像python:3.10有 12 个高严重性问题、27 个中等严重性问题和 132 个低严重性问题。因此,默认情况下,Python Docker 映像将以至少 171 个安全漏洞开始——我们甚至还没有添加任何内容!

此外,由于我们有效地引入了一个完整的操作系统,因此对于我们的 Python 应用程序服务器而言,基础映像的大小将非常大,这将导致构建速度变慢并且需要更大的空间。通常,在选择基础镜像时,规则很少,但有两个是关键。

选择 Python Docker 镜像的最佳实践

  1. 选择满足您所有要求的最小基础映像,然后在此基础上进行构建。较小的图像包含较少的漏洞、较少的资源占用和较少的不必要的包。
  2. 使用命名标签不足以确保您始终使用相同的基础图像。确保这一点的唯一方法是使用图像摘要。

有了这些知识,让我们重新访问Snyk Advisor并检查它推荐的替代标签。该工具对基础镜像的漏洞和大小进行了很好的概述,这将极大地帮助我们做出决定。

由于我们有兴趣在我们的应用程序中使用 Python 3.10,因此我们将查找该特定标记。

使用 Docker 容器化 Python 应用程序的最佳实践(译文-来自:snyk)

除了上述漏洞外,我们还可以看到该镜像的基本大小约为 350 MB,它基于 Debian 11,并安装了 427 个软件包。我们会认为这张图片对于我们的小 Python 应用程序来说有点太多了。

另一方面,还有用于 Python 的 Docker 基础映像:3.10-slim有 1 个高严重性问题、1 个中等严重性问题和 35 个低严重性问题。Docker 基础大小为 46.2 MB,也是基于 Debian 11 的操作系统,并安装了 106 个软件包。简单地为我们的 Python 应用程序服务器选择这个 Docker 基础镜像而不是默认镜像将减少安全漏洞的数量、磁盘大小和安装的库数量——同时满足我们对 :3.10.

就这样吧!我只需添加标签 :3.10-slim 就可以了!

几乎!我们满足了第一条规则——我们有一个小的 Docker 基础镜像来满足我们的要求——但我们仍然需要解决第二个问题。我们需要确保每次构建我们的 Python 应用程序服务器时,我们都会实际使用准确的 Docker 基础镜像。

为此,我们有几种选择:

  1. 从 Docker Hub 获取 Docker 基础镜像摘要。
  2. 使用 将 Docker 镜像下载到我们的计算机上docker pull python:3.10-slim,这会显示 Docker 镜像摘要:
13.10-slim: Pulling from library/python
27d63c13d9b9b: Pull complete
36ad2a11ca37b: Pull complete
41d79bc863ed3: Pull complete
5c72b5f03bec8: Pull complete
60c3b0c5ce69b: Pull complete
7Digest: sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
8Status: Downloaded newer image for python:3.10-slim
9docker.io/library/python:3.10-slim           

如果我们的计算机上已经有 Python Docker 镜像,我们可以使用以下命令从磁盘上当前存在的镜像中获取镜像摘要docker images --digests | grep python:

1python    3.10-slim    sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d           

一旦我们有了基础镜像摘要,我们就可以将它添加到前面提到的 Dockerfile 中:

文件

1FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
2WORKDIR /usr/app
3COPY . .
4RUN pip install -r requirements.txt
5CMD [ "python", "app.py" ]           

这种做法确保每次我们为这个 Python 应用程序重建 Docker 镜像时,都使用相同的底层操作系统和库版本。这提供了一个确定性的构建。

2.将依赖与源码分离

第二个最佳实践可以防止涉及具有依赖项的项目的任何类型的 Docker 映像中最常见的错误之一。首先,这是不好的做法:

  1. 将项目文件夹中的所有内容复制到图像上下文中。
  2. 安装依赖项。
  3. 运行应用程序。

嗯,它有效,但还有很多需要改进的地方。对于初学者来说,当你在本地开发一个项目时,你只在依赖项发生变化时才安装它们,对吧?因此,不要每次更改最细微的代码行时都强制您的 Docker 映像下载并安装它们。

这个最佳实践是关于优化 Docker 镜像中的层。如果我们想在 Docker 构建期间利用缓存系统,我们在编写 Dockerfile 时应该始终牢记一件事:层应该始终根据它们更改的可能性来排序。

让我们来看看我们一直携带到现在的Dockerfile。每次我们构建该 Python 应用程序映像时,Docker 软件都会检查不同的层并回答以下问题:是否发生了某些变化,或者我可以只使用我以前做过的吗?

在我们当前形式的 Dockerfile 内容中,对我们的项目文件夹的任何更改都将重新触发指令COPY- 以及随后 - 其余构建层。这真的没有意义,它为优化和加速留下了很大的空间。

让我们使用以下 Dockerfile 改进它:

1FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
2WORKDIR /usr/app
3
4COPY requirements.txt .
5RUN pip install -r requirements.txt
6
7COPY . .
8CMD [ "python", "app.py" ]           

有了这个新的 Dockerfile,下次 Docker 检查层是否可以重用时,如果发现文件没有变化requirements.txt,它会“跳转”直达指令COPY,几秒钟内解决。通过这个微小的变化,我们加快了很多构建过程——每次我们修改代码中的某些内容时,构建之间不再需要等待几分钟。

请务必注意,并非所有依赖项都打包为wheels ,这一点很重要。在这种情况下,需要在您的映像上安装编译器。

但是您告诉我们要使用尽可能小的图像来运行应用程序!

你是对的,这就是为什么我们要向你展示另一个惊人的 Docker 特性:多阶段构建。

多阶段构建

多阶段构建意味着我们使用带有更多工具的 Docker 镜像来编译那些所需的依赖项,然后我们只需将所需的工件复制到实际使用的 Python Docker 镜像中。

基于 Node.js 的应用程序示例如下:

1FROM node:latest AS build
2ARG NPM_TOKEN
3WORKDIR /usr/src/app
4COPY package*.json /usr/src/app/
5RUN npm install
6
7FROM node:lts-alpine@sha256:b2da3316acdc2bec442190a1fe10dc094e7ba4121d029cb32075ff59bb27390a
8WORKDIR /usr/src/app
9COPY --from=build /usr/src/app/node_modules /usr/src/app/node_modules
10COPY . .
11CMD ["node", "server.js"]           

注意:这是一个简单的示例,仅用于演示多阶段的功能。如果您想学习正确容器化 Node.js 应用程序的最佳实践,请参阅 Liran Tal(这个名字看起来很熟悉……)和 Yoni Goldberg 的这篇文章。

Node.js 的多阶段构建非常容易处理,因为node_modules文件夹将与实际项目位于同一文件夹中,但是,当我们处理 Python 应用程序时情况并非如此。

如果我们只是运行pip install,我们会在很多地方安装很多东西,导致无法执行多阶段构建。为了解决这个问题,我们有两个可能的解决方案:

  1. 使用pip install --user
  2. 用一个virtualenv

使用pip install --user似乎是一个不错的选择,因为所有包都将安装在~/.local目录中,因此将它们从一个阶段复制到另一个阶段非常容易。但这会产生另一个问题:您会将我们用于编译依赖项的映像中的所有系统级依赖项添加到最终的 Docker 基础映像中——我们不希望这种情况发生(记住我们的最佳实践是尽可能小的 Docker 基础镜像)。

排除第一个选项后,让我们探讨第二个选项:使用virtualenv. 如果我们这样做,我们最终会得到以下 Dockerfile。

1FROM python:3.10-slim as build
2RUN apt-get update
3RUN apt-get install -y --no-install-recommends \
4	      build-essential gcc 
5
6WORKDIR /usr/app
7RUN python -m venv /usr/app/venv
8ENV PATH="/usr/app/venv/bin:$PATH"
9
10COPY requirements.txt .
11RUN pip install -r requirements.txt
12
13FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
14WORKDIR /usr/app/venv
15COPY --from=build /usr/app/venv ./venv
16COPY . .
17
18ENV PATH="/usr/app/venv/bin:$PATH"
19CMD [ "python", "app.py" ]           

现在,我们拥有所有必需的依赖项,但没有编译它们所需的额外包。

容器化 Python 应用程序的多阶段构建的已知问题

目前,有一个已知的问题,pre-stages not being cached。解决此问题的最简单方法是使用BuildKit并将参数添加BUILDKIT_INLINE_CACHE=1到构建过程。

我们将在第一次正常构建,但后续构建将使用以下命令:

1export DOCKER_BUILDKIT=1
2docker build -t flask-application --cache-from flask-application --build-arg BUILDKIT_INLINE_CACHE=1 .           

您将需要环境变量,DOCKER_BUILDKIT=1以便 Docker 知道在构建过程中使用 BuildKit。也可以通过在 Docker 软件配置文件中添加以下设置来启用它/etc/docker/daemon.json(这样就不需要环境变量):

1{ "features": { "buildkit": true } }           

3. 使用 Python WSGI 进行生产

为生产而构建的启用调试模式的 Python 应用程序是一个很大的禁忌- 并且等待发生的安全事件。不幸的是,这是我们在许多关于容器化 Python Flask 应用程序和其他 WSGI Python 应用程序框架的博客文章中看到的一个常见错误。

调试器允许从浏览器执行任意 Python 代码,这会带来巨大的安全风险。这可以在一定程度上得到保护,但它始终是一个漏洞。为了安全起见,再说一次:不要在生产环境中运行开发服务器或调试器!这也适用于您的容器化 Python 应用程序。

我们知道在调试模式下部署 Python Flask 应用程序比设置 WSGI 服务器和 Web/代理更容易,但只有在您必须解释为什么您的应用程序被黑客入侵时才会更容易。

那么如何解决呢?首先,您应该决定要使用哪个 WSGI 服务器实现。Python应用常用的主要有这四种:

  • Green Unicorn (Gunicorn) — 从 Ruby 的 Unicorn 项目移植的预分叉工人模型。
  • uWSGI — 一种多功能、高性能和低资源使用的 WSGI 服务器实现。
  • mod_wsgi — 一个 Apache 模块,可以托管任何支持 Python WSGI 规范的 Python Web 应用程序。
  • CherryPy — Pythonic,面向对象的 HTTP 框架,也可用作 WSGI 服务器。

在本文中,我们将使用Gunicorn作为示例,但请随意阅读所有这些工具的文档和信息,以便您可以选择最适合您需要的工具。在这篇文章中,我们不会查看配置,因为它完全取决于用例。

对于容器化部分,我们只需要:

  1. 将gunicorn依赖项包含在requirements.txt
  2. 更改 Python 应用程序容器的入口点(请参阅CMD此更改的说明):
1FROM python:3.10-slim as build
2RUN apt-get update
3RUN apt-get install -y --no-install-recommends \
4	build-essential gcc 
5
6WORKDIR /usr/app
7RUN python -m venv /usr/app/venv
8ENV PATH="/usr/app/venv/bin:$PATH"
9
10COPY requirements.txt .
11RUN pip install -r requirements.txt
12
13FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
14WORKDIR /usr/app
15COPY --from=build /usr/app/venv ./venv
16COPY . .
17
18ENV PATH="/usr/app/venv/bin:$PATH"
19CMD [ "gunicorn", "--bind", "0.0.0.0:5000", "manage:app" ]           

在我们基于这个新的 Dockerfile 重建 Python 应用程序之后,我们可以运行并测试 Flask 应用程序是否已准备好处理请求:

1docker run -p 8080:5000 flask-application           

注意:我们建议对于实际的生产就绪部署,您不应直接将Gunicorn公开的端口绑定到主机。相反,我们建议您在处理所有 HTTP 请求并提供静态文件的同一网络中部署反向代理服务器。

4. 以尽可能少的权限运行容器化的 Python 应用程序(永远不要root)

最小权限原则是 Unix 早期的一个长期安全控制——当我们运行容器化的 Python 应用程序时,我们应该始终遵循这一点。

默认情况下,官方pythonDocker 镜像不包含特权用户,因此我们需要创建它以便能够以最低特权用户运行进程。

为此,我们将以下groupadd命令添加到实际运行该过程的最终映像(多阶段构建的第二阶段)中的 Dockerfile gunicorn:

1FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
2
3RUN groupadd -g 999 python && \
4    useradd -r -u 999 -g python python
5USER 999
6WORKDIR /usr/app
7
8COPY --from=build /usr/app/venv ./venv
9COPY . .
10
11ENV PATH="/usr/app/venv/bin:$PATH"
12CMD [ "gunicorn", "--bind", "0.0.0.0:5000", "manage:app" ]           

但是,问题在于,通过先前的修改,用户python拥有由 Docker 执行的系统进程……复制文件或WORKDIR目录的文件所有权如何?默认情况下,WORKDIR如果目录不存在,Docker 编译器将创建该目录,但它会以root系统用户作为其所有者来创建它,因此任何包括写入该目录的操作都可能导致我们的应用程序出现致命错误。root此外,如果我们不更改它们的行为,则默认情况下将拥有复制的文件,即使我们已经更改了用户。

让我们修复它:

1FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
2
3RUN groupadd -g 999 python && \
4    useradd -r -u 999 -g python python
5
6RUN mkdir /usr/app && chown python:python /usr/app
7WORKDIR /usr/app
8
9COPY --chown=python:python --from=build /usr/app/venv ./venv
10COPY --chown=python:python . .
11
12USER 999
13
14ENV PATH="/usr/app/venv/bin:$PATH"
15CMD [ "gunicorn", "--bind", "0.0.0.0:5000", "manage:app" ]           

通过以上更新的COPY说明,我们可以防止目录在文件所有权方面出现意外行为WORKDIR,并确保所有文件都属于将要运行该进程的同一用户。

5. 处理容器化 Python 应用程序的不健康状态

在部署应用程序时,您需要注意可能导致应用程序处于不健康状态的未处理事件或问题:1) 它不再工作,但 2) 它不会终止进程。如果发生这种情况,您的容器将不会收到通知,并且您将有一个正在运行的 Python 应用程序服务器不再响应 HTTP 请求。

为避免这种情况,我们要实施健康检查端点。为了检查容器化 Python 应用程序的运行状况,我们始终建议包含一个monitoring或healthHTTP 端点,以确保应用程序仍然能够成功处理用户请求。使用一些 Python 的 Web 应用程序框架,如 Flask,这将是一项简单的任务(下面的 Flask 示例),并且结合 Docker 的HEALTHCHECK指令,您将确保您的应用程序得到良好的健康状态监控。

以下是添加/healthHTTP 端点的 Python Flask 应用程序片段:

[email protected]('/health', methods=['GET'])
2def health():
3	# Handle here any business logic for ensuring you're application is healthy (DB connections, etc...)
4    return "Healthy: OK"           

“一旦你在你的应用程序中有了那个端点,你只需要HEALTHCHECK在你的 Dockerfile 中包含指令:

1FROM python:3.10-slim@sha256:2bac43769ace90ebd3ad83e5392295e25dfc58e58543d3ab326c3330b505283d
2
3RUN groupadd -g 999 python && \
4    useradd -r -u 999 -g python python
5
6RUN mkdir /usr/app && chown python:python /usr/app
7WORKDIR /usr/app
8
9COPY --chown=python:python --from=build /usr/app/venv ./venv
10COPY --chown=python:python . .
11
12USER 999
13
14ENV PATH="/usr/app/venv/bin:$PATH"
15CMD [ "gunicorn", "--bind", "0.0.0.0:5000", "manage:app" ]
16HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 CMD curl -f http://localhost:5000/health           

如果您在 Kubernetes 上进行部署,那么您应该知道 DockerfileHEALTHCHECK指令会被忽略。YAML 中需要相应的Kubernetes 活性、就绪性和启动探测。

因此,要解决上述HEALTHCHECK指令的 Kubernetes 部署问题:

1...
2   livenessProbe:
3     httpGet:
4       path: /health
5       port: 5000
6     initialDelaySeconds: 5
7     periodSeconds: 30
8     timeoutSeconds: 30
9     failureThreshold: 3
10...           

请注意,此配置将嵌套在container specpod 或部署 YAML 文件的一部分中。always现在,使用或 的重启策略unless_stopped,如果我们的 Python 应用程序容器进入不健康状态,它将始终重启。

6. 查找并修复 Python Docker 应用程序映像中的安全漏洞

我们已经确定,更大的 Docker 基础映像会带来许多问题,例如我们需要维护的大型软件堆栈、与安全修复程序保持同步等等。

我们还探索了使用Snyk Advisor来确定不同基础映像中的大小和漏洞指标。但 Advisor 只是冰山一角。Snyk 是一个免费的开发人员安全平台,您可以使用它来测试任何东西,从您自己的 Python 代码到您的 Python 依赖项(例如 中的依赖项requirements.txt)、运行您的应用程序的 Python 容器映像,甚至是编排这一切的 Terraform 或 Kubernetes 配置。

Snyk 最好的一点是它为它发现的漏洞提供了建议的修复。因此,它不仅会告诉您存在哪些安全漏洞,还会自动创建修复 PR 或提供补救建议。这一切都是在您现有的工具(IDE、CLI、Docker 等)和工作流(Git、CI/CD 等)中完成的。

示例:使用 Snyk 扫描我们的容器化 Python 应用程序

python:3.8当我们使用 的 Python Docker 映像构建此示例 Python Flask 应用程序时,让我们看看 Snyk CLI 是如何工作的。

Snyk 已经集成到 Windows、macOS 和 Linux 操作系统(通过 docker-ce)上的 Docker Desktop 中,并且它有一个内置命令——你可以docker scan运行它来扫描你的 Docker 镜像。

如果您安装了 Snyk CLI,您可以使用它来扫描您的 Python 项目依赖项、您的 Python 代码等。因此,首先,我们安装 Snyk CLI。如果你有一个 Node.js 环境,你可以使用npm包管理器来完成它,如下所示:

1npm install -g snyk           

或者如果你在 macOS 或 Linux 上,并且正在使用 Homebrew,你可以像这样安装它:

1brew tap snyk/tap
2brew install snyk           

对于其他安装方法,请参阅我们关于如何安装 Snyk CLI 的指南。

接下来,我们需要从 CLI 进行身份验证,以获取有效的 API 令牌来查询漏洞数据库:

1snyk auth           

以上完成后,我们就可以继续用基础镜像构建Python应用的本地Docker镜像了python:3.8:

1❯ docker build . -t python-flask-app
2FROM python:3.8 as build
3[+] Building 5.2s (8/13)
4 => [internal] load build definition from Dockerfile
5 => => transferring dockerfile: 513B
6 => [internal] load .dockerignore
7 => => transferring context: 2B
8 => [internal] load metadata for docker.io/library/python:3.8
9 => [auth] library/python:pull token for registry-1.docker.io
10 => [internal] load build context
11...           

现在,让我们通过运行以下命令使用 Snyk 对其进行扫描:

1snyk container test python-flask-app           

输出产生以下结果(故意缩短,因为输出很大):

1Testing python-flask-app...
2
3✗ Low severity vulnerability found in tiff/libtiff5
4  Description: Out-of-bounds Read
5  Info: https://snyk.io/vuln/SNYK-DEBIAN11-TIFF-514595
6  Introduced through: imagemagick@8:6.9.11.60+dfsg-1.3, imagemagick/libmagickcore-dev@8:6.9.11.60+dfsg-1.3
7  From: imagemagick@8:6.9.11.60+dfsg-1.3 > imagemagick/imagemagick-6.q16@8:6.9.11.60+dfsg-1.3 > imagemagick/libmagickcore-6.q16-6@8:6.9.11.60+dfsg-1.3 > tiff/[email protected]
8  From: imagemagick/libmagickcore-dev@8:6.9.11.60+dfsg-1.3 > imagemagick/libmagickcore-6.q16-dev@8:6.9.11.60+dfsg-1.3 > tiff/[email protected] > tiff/[email protected]
9  From: imagemagick/libmagickcore-dev@8:6.9.11.60+dfsg-1.3 > imagemagick/libmagickcore-6.q16-dev@8:6.9.11.60+dfsg-1.3 > tiff/[email protected] > tiff/[email protected] > tiff/[email protected]
10  and 3 more...
11
12✗ High severity vulnerability found in imagemagick/imagemagick-6-common
13  Description: Information Exposure
14  Info: https://snyk.io/vuln/SNYK-DEBIAN11-IMAGEMAGICK-1246513
15  Introduced through: imagemagick/libmagickcore-dev@8:6.9.11.60+dfsg-1.3, imagemagick/libmagickwand-dev@8:6.9.11.60+dfsg-1.3, imagemagick@8:6.9.11.60+dfsg-1.3
16  From: imagemagick/libmagickcore-dev@8:6.9.11.60+dfsg-1.3 > imagemagick/imagemagick-6-common@8:6.9.11.60+dfsg-1.3
17  From: imagemagick/libmagickwand-dev@8:6.9.11.60+dfsg-1.3 > imagemagick/imagemagick-6-common@8:6.9.11.60+dfsg-1.3
18  From: imagemagick@8:6.9.11.60+dfsg-1.3 > imagemagick/imagemagick-6.q16@8:6.9.11.60+dfsg-1.3 > imagemagick/libmagickcore-6.q16-6@8:6.9.11.60+dfsg-1.3 > imagemagick/imagemagick-6-common@8:6.9.11.60+dfsg-1.3
19  and 24 more...
20
21✗ Critical severity vulnerability found in python3.9/libpython3.9-stdlib
22  Description: Improper Input Validation
23  Info: https://snyk.io/vuln/SNYK-DEBIAN11-PYTHON39-1290158
24  Introduced through: [email protected]
25  From: [email protected] > python3-defaults/[email protected] > python3-defaults/[email protected] > python3.9/[email protected]
26  From: [email protected] > python3-defaults/[email protected] > [email protected] > python3.9/[email protected]
27  From: [email protected] > python3-defaults/[email protected] > python3-defaults/[email protected] > python3.9/[email protected]
28  and 4 more...
29
30✗ Critical severity vulnerability found in glibc/libc-bin
31  Description: Use After Free
32  Info: https://snyk.io/vuln/SNYK-DEBIAN11-GLIBC-1296898
33  Introduced through: glibc/[email protected]+deb11u2, meta-common-packages@meta
34  From: glibc/[email protected]+deb11u2
35  From: meta-common-packages@meta > glibc/[email protected]+deb11u2
36  From: meta-common-packages@meta > glibc/[email protected]+deb11u2
37  and 1 more...
38
39Organization:      snyk-demo-567
40Package manager:   deb
41Project name:      docker-image|python-flask-app
42Docker image:      python-flask-app
43Platform:          linux/amd64
44Base image:        python:3.8.12-bullseye
45Licenses:          enabled
46
47Tested 427 dependencies for known issues, found 171 issues.
48
49Base Image              Vulnerabilities  Severity
50python:3.8.12-bullseye  171              6 critical, 6 high, 27 medium, 132 low
51
52Recommendations for base image upgrade:
53
54Alternative image types
55Base Image                   Vulnerabilities  Severity
56python:3.9-slim              37               1 critical, 0 high, 1 medium, 35 low
57python:3.11-rc-slim          37               1 critical, 0 high, 1 medium, 35 low
58python:3.8.12-slim-bullseye  37               1 critical, 0 high, 1 medium, 35 low
59python:3.10-slim-buster      70               2 critical, 9 high, 9 medium, 50 low           

因此,作为 Python 3.8 操作系统内容的一部分,我们以开源库的形式引入了 427 个依赖项。由于我们选择了python:3.8.

说到这里,你可能会不解地问:“我该如何解决呢?” 对我们来说幸运的是,Snyk 提供了一些建议,我们可以升级或完全切换到哪些其他基础映像,以降低攻击面。

这是此基本图像推荐建议的清晰可视化屏幕截图:

使用 Docker 容器化 Python 应用程序的最佳实践(译文-来自:snyk)

现在我们可以做出明智的、数据驱动的决定来保护我们的 Python 应用程序。通过选择 Snyk 推荐的任何替代 Docker 镜像,我们可以显着降低应用程序中捆绑软件的攻击面。

为了更好地控制应用程序的安全性,请将您的存储库连接到 Snyk UI以导入您的源代码和 Dockerfile,这样您不仅可以找到这些漏洞,还可以持续监控它们是否存在新的安全问题。这是来自 Snyk UI 的相同 Docker 基础镜像报告:

使用 Docker 容器化 Python 应用程序的最佳实践(译文-来自:snyk)

有什么比监视和查找安全漏洞更好的呢?修复它们!:-)

如果您将 Git 存储库连接到 Snyk,我们还可以在您的存储库中创建拉取请求(自动!)以提供这些 Docker 基础映像升级,就像您在此处看到的那样:

使用 Docker 容器化 Python 应用程序的最佳实践(译文-来自:snyk)

如果您喜欢这篇文章,请查看这篇关于使用 Dockerfile 拉取请求自动化容器安全性的后续文章。

Python 应用程序如何容器化?

Docker 是一种软件虚拟化技术,它允许我们以基于 Python Docker 映像的容器化 Python 应用程序的形式构建可重用、跨平台和快速部署的软件。这些应用程序通过基础架构定义为带有名为 Dockerfile 的文件的代码。

要构建和使用容器化 Python 应用程序,请运行以下命令:

docker build -t flask-application .

docker run -p 8080:5000 flask-application

针对开发人员的 Python 安全建议

这些最佳实践应该可以帮助您更好地创建、管理和保护您的容器化 Python 应用程序。如果您喜欢阅读这些最佳实践,并且重视应用程序安全和对整体安全的拥护,那么我们建议您将以下资源作为后续阅读材料:

  • Daniel Berman 的Snyk 安全 Python 开发入门
  • Brian Vermeer 的Snyk CLI 秘密和一个很棒的备忘单
  • 由 Liran Tal 和 Yoni Golberg 编写的针对 Docker 基础应用程序的全面 Node.js 最佳实践
  • 最后,对于您的 Java 朋友,这个Docker for Java developers: 5 things you need to know to not fail your security by Brian Vermeer。
  • Frank Fischer 的Python 安全最佳实践备忘单
  • 关于Python 项目中常见安全问题的功能齐全且见解丰富的报告

作者:Liran Tal

出处:https://snyk.io/blog/best-practices-containerizing-python-docker/