如何为Python选择Docker基础映像

JFrog支持
2023-01-22 11:06

谈到容器化应用程序时,选择正确的容器基础映像(即在其上构建新的定制容器映像的基础映像)是成功的一半。您选择的基本映像在应用程序的安全性、性能、可维护性等方面起着核心作用。

为了说明这一点,本文介绍了为用Python(当今最流行的编程语言之一)开发的应用程序选择基本映像的几种方法。正如我们将看到的,尽管Python应用程序与几乎任何类型的容器基映像兼容,但根据您的需要和优先级,一个基映像可能比另一个基映像更有意义。

为什么选择正确的基本图像是至关重要的

在深入Python的基本图像选项之前,让我们讨论一下为什么选择正确的基本图像如此重要的众多原因:

  • 性能:基本映像的总体大小影响映像的下载速度。此外,容器启动时映像运行的代码量会影响应用启动和运行所需的时间。这两个因素都是整体容器性能的关键考虑因素。
  • 依赖关系:许多应用程序都有依赖关系,这意味着它们需要运行库或其他代码。如果这些依赖被烘焙到基本映像中,你就不需要做那么多的工作来配置容器或自定义它的内容来适合你的应用程序。
  • 更新:你需要确保你的基本映像是最新的。如果您的基本映像基于由第三方开发人员积极维护和定期修补的软件平台或发行版,那么这就更容易做到。
  • 安全性:基本映像中的代码越多,容器的攻击面就越大。因此,只包含运行应用程序所必需的代码的基本映像更安全。

如您所见,这些优先级之间存在一些冲突。例如,包含大量库的基本映像更有可能满足应用程序的依赖关系。另一方面,从安全角度来看,它们也可能具有更大的风险,因为它们可能包含您实际上并不需要的库,因此会增加您面临潜在安全漏洞的风险。

考虑到在选择基本图像时要权衡的竞争因素,很少有明显理想的选择。相反,在使用特定的基本映像之前,您需要从战略上考虑应用程序和托管环境的特定需求。

Python的三个基本映像选项

作为如何在基本映像选择中考虑不同优先级的示例,假设您有一个希望在容器中运行的Python应用程序。出于举例目的,我们将使用一个非常简单的Hello World应用程序,其源代码仅由以下一行Python源代码组成:

打印(“Hello world !”)

我们将应用程序命名为hello.py。如果你想,你可以在命令行上运行它:

python hello.py

当然,输出应该是:

你好世界!

但是我们不希望直接从命令行运行应用程序。我们想把它作为容器运行。为此,我们需要创建一个码头工人形象运行应用程序。

因为Python是一种几乎可以在任何地方运行的跨平台语言,所以几乎可以使用任何基本映像创建容器化的Python应用程序。不过,你选择的基本映像会影响应用程序的运行方式、维护和安全的难易程度等等。

因此,让我们研究一下使用Python基本映像的三种不同方法:轻量级Linux发行版、标准Linux发行版和从头构建的最小基本映像。

用于Python的轻量级Linux基本映像

对于依赖关系很少或没有依赖关系的简单Python应用程序,可以使用使用轻量级Linux发行版创建的基本映像。这种类型的基本映像为容器的运行提供了极简的Linux环境。这使得它比包含更多潜在漏洞的“重量级”环境更安全。

使用极简基本图像的缺点是,如果你的应用程序确实有依赖关系,而这些依赖关系并没有内置到基本图像中(如果是极简图像,它们很可能不会内置到基本图像中),那么在创建图像时,你将需要编写更多的代码行。

但是现在,让我们假设你的应用程序没有依赖关系。它可以使用轻量级的Linux基本映像运行,比如高山你可以免费从里面拉码头工人中心.在本例中,我们需要在基本映像之上添加的唯一东西是Python解释器,因为默认情况下Alpine没有安装Python。

因此,让我们创建一个Dockerfile,使用Alpine作为基本映像,安装Python,然后运行我们的应用程序。它看起来像这样:

从高山
WORKDIR工作
ENV PYTHONUNBUFFERED = 1
执行apk add -update -no-cache python3 && ln -sf python3 /usr/bin/python
复制hello.py /usr/bin/hello
CMD python /usr/bin/hello

以Dockerfile的名称保存此文件,并确保您的Python应用程序存储在与Dockerfile相同的目录中。然后,使用以下命令构建容器:

Docker build -t hello。

这告诉Docker用“hello”标记来命名容器。

要运行容器,输入:

Docker run -你好

您应该看到“Hello world!”打印在命令行上。

如果你想知道你的新容器映像有多大,运行下面的命令:

Docker镜像ls

如果您使用Alpine作为基本映像,您将看到容器映像的大小相对较小-大约52兆字节:

存储库标记映像id创建大小
51.7MB

Python的标准Linux基映像

像Alpine这样的轻量级Linux发行版非常适合简单的“Hello World”应用程序,该应用程序除了Python之外没有其他依赖项。但是,如果您有一个更复杂的Python应用程序,有很多依赖项,那么Alpine可能不是一个很好的选择,因为您必须在基本映像之上安装每个模块。

相反,您可能更喜欢使用标准的Linux发行版,比如Ubuntu。在这种情况下,你可以使用Dockerfile为你的Python应用程序创建一个容器,看起来像这样(在这个例子中,我们使用Ubuntu 20.04基本映像):

罗ubuntu: 20.04
RUN apt update
运行apt install -y python3
复制hello.py /usr/bin/hello.py
ENTRYPOINT ["python3"]
CMD(“/ usr / bin / hello.py”)

你可以继续使用与Alpine base image相同的命令创建一个基于Dockerfile的容器:

Docker build -t hello_ubuntu。

在本例中,我们将映像命名为“hello_ubuntu”,以区别于基于alpine的映像。

你可以使用以下命令运行新容器:

Docker运行- hello_ubuntu

因为我们使用的是Ubuntu基本映像,所以在这个例子中,映像的总大小是147兆字节——比我们的Alpine映像大很多:

存储库标记映像id创建大小
147MB

极简主义的基础图像从零开始

容器化Python应用程序的第三种方法是使用从头创建的基映像。在这种方法下,您根本不使用第三方基本映像。相反,您可以生成自己唯一的基本映像,然后使用基本映像为应用程序创建容器映像。(从技术上讲,这种方法仍然涉及到一个基本映像——Docker的“scratch”映像——但这是一个专门的、极简的映像,其唯一目的是为创建全新的基本映像提供一张白纸。)

使用从头创建的基本映像的优点是,它将生成尽可能小且安全的容器。主要的缺点是创建和更新容器需要更多的工作,因为您必须将Python代码编译为静态二进制文件。这是必要的,因为您需要能够在容器中没有可用的Python解释器的情况下执行应用程序。

要将Python代码编译为静态代码,首先安装cython(它将Python代码转换为C语言):

sudo -H pip3安装cython

然后,根据Python代码生成C源代码(这里,我们使用的是" hello.py "示例程序):

Cython hello.py -embed

最后,使用gcc编译代码:

gcc -Os -I /usr/include/python2.7 -o hello hello.c -lpython2.7 -lpthread -lm -lutil -ldl

(请注意,您可能需要根据您在系统上安装的Python版本修改上面的命令。)

在这一点上,你应该有一个可执行的二进制文件,名为hello,你可以在没有Python解释器的情况下运行:

。/你好

现在,我们可以创建一个Dockerfile,它定义了一个容器来运行这个二进制文件:

从头开始
ADD hello /
CMD (" / hello”)

使用如下命令构建并运行容器:

Docker build -t hello_scratch
Docker运行- hello_scratch

如果你检查这个容器的大小,你会发现它非常非常小——只有24kb左右,这比我们使用Alpine基本映像创建的容器小了大约20,000倍:

存储库标记映像id创建大小
hello_scratch latest 8459e05660ad About a minute ago 24.1kB

因此,尽管从头开始为Python应用程序创建基本映像需要更多的努力,但您将获得一个极其高效且(由于其极简主义性质)安全的容器。