[](https://res.cloudinary.com/practicaldev/image/fetch/s--SB34bIRM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/770/1%2ApIK7TbSe8lHn9PiWMOY6Lg.png)

Docker 与 Nginx、Flask 和 Postgres

术语

  • Docker Image:是用于在 Docker 容器中执行代码的文件,由一组指令构建而成。

  • Docker Container:是正在执行或运行的Docker镜像。

Docker 🐳 是一种相对较新且令人兴奋的技术,Docker 是一种容器化工具。使用 Docker 的主要好处是您可以使用相同的环境进行开发、测试和生产。由于 Docker 环境是一致的,这意味着如果应用程序在测试环境中工作,它也将在生产环境中工作。

Docker 的另一大优势是我不需要直接在自己的机器上下载所有依赖项。要构建一个完整的应用程序并运行它,我只需要一个 dockerfile,所以如果我经常使用很多不同的机器,我可以通过使用 Docker 轻松设置自己的开发环境。所有 Docker 都会执行一组指令,因此由于指令相同,Docker 将创建的环境(Docker 容器)也将相同。

在本教程中,我将向您展示如何使用多个 Docker 容器设置 Python 🐍 应用程序。理论上,您可以拥有一个包含 Nginx、Flask 和 Postgres 的大型 Docker 容器,但我更喜欢将应用程序拆分。比如它的核心组件,web服务器(Nginx)、应用程序(Flask)和数据库(Postgres)。这样做的主要优点是,它可以更轻松地替换需要应用程序的组件,并且如果发生错误,更容易查看是哪个组件导致了错误。

注意:本教程中的所有内容都已在 Ubuntu Linux 上进行了测试。

先决条件

  • 安装 Docker

  • (可选)安装docker-compose

Nginx

第一个名为 Nginx 的 Docker 容器将成为我们应用程序的主要网关,它将用作代理服务器。它将接收 HTTP 请求并将它们转发到我们的 Python 应用程序。

Dockerfile

[](https://res.cloudinary.com/practicaldev/image/fetch/s--_QJahJMl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/1024/1%2A-MtBsrUXuDXaxR4AZYK-PQ.png)

Dockerfile

这是一个非常简单的 dockerfile,它使用了最新的 Nginx docker 镜像。然后它会删除默认配置并添加我们的配置。

示例.conf

[](https://res.cloudinary.com/practicaldev/image/fetch/s--ybgjDnWI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/870/1%2A-Sus706dxr5YdAQY7jh9Yw.png)

例子.conf

这是一个简单的 Nginx 配置文件,用于侦听端口 80 (HTTP) 上的流量。然后它将数据传递给 uWSGI(因此是位置 /)。然后,我们将 HTTP 请求传递给另一个名为 flask 的 Docker 容器,端口为 8080。此配置不能用于 https。 警告 仅使用 https 发送安全数据。我们将容器名称命名为 flask 而不是 localhost 的原因是因为这就是 Docker 网络默认的工作方式(桥接网络)以允许容器到容器的通信。在将 Flask 容器连接到 Postgres 容器时,我们做了一些允许的事情。

烧瓶

注意:链接到 source_code/src/example/ 中的Python 应用程序源代码这是在 dist 文件夹中转换为 tar 的代码。

第二个 Docker 容器将包含我们在 uWSGI 服务器上运行的 Python 应用程序。 uWSGI 服务器是一个基于 WSGI 规范的 Web 应用服务器,它将允许 Python 与 Web 服务器进行通信。在这种情况下,它本质上充当 Nginx 和 Flask 之间的中间件,在它们之间转换请求。所以本质上 uWSGI 接收来自 Nginx 的 HTTP 请求并翻译成 Flask 可以理解的东西。这个容器存储了这个简单 API 所需的所有核心 Python 代码。

Dockerfile

[](https://res.cloudinary.com/practicaldev/image/fetch/s--E3U7IGVI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/800/1%2A8fouUS_-fHGdV3gcCJNeQw.png)

Dockerfile

这个 dockerfile 使用了一个相对较新的 Docker 特性,称为多阶段构建。这里我们使用一个基础镜像(Python3.6)来生成一些 Python Wheel 文件。这些 Wheel 文件需要特定的 Linux 依赖项,而我们在主 Docker 容器中实际上并不需要这些依赖项。然后我们定义我们的实际图像并复制我们应用程序所需的轮文件。这样做是为了帮助减少 Docker 镜像文件的大小,我们希望尽可能地制作镜像(尽可能多地有意义)。在 dockerfile 结束时,BASE 映像被销毁。

所以我们实际的 Docker 镜像更有趣一些。它执行以下操作;

  • 它暴露了 8080 端口,这实际上并没有做任何事情,只是为了文档目的

  • 然后它会创建一个默认目录 /app/

  • 我们定义了这个容器需要的一些环境变量(都是用来连接Postgres的)。 请注意 在生产环境中,您不想将密码和用户名作为环境变量暴露在 docker 容器上,而是应该使用秘密存储,例如HashiCorp Vault。这些变量将在我们运行容器时定义

  • 它将包含我们的 Python 包的 dist 文件夹复制为 tar 文件。如果您有 setup.py 文件并在与 setup.py 相同的文件夹中运行 python setup.py sdist,则可以生成此文件

  • 它复制用于配置uWSGI服务器的uwsgi.ini

  • 它从 BASE 映像中复制所有的 wheel 文件夹,因此 --fromu003dBASE

  • 然后我们将轮子和我们的所有东西安装在 dist 文件夹中,我们的 Python 代码为 tar

  • 最后,当 Docker 镜像运行时,它将执行 uwsgi --ini /app/uwsgi.ini。使用我们复制到镜像中的 uwsgi.ini 文件

理论上,您可以简单地复制并安装 requirements.txt 并将所有源代码复制到 /app 文件夹。但是,我更喜欢生成和安装实际的 Python 包,我认为它更干净,您只需复制一个 tar 文件。但是,这确实需要您在尝试构建 Docker 映像之前运行命令以生成 dist 文件夹。

uwsgi.ini

[](https://res.cloudinary.com/practicaldev/image/fetch/s--CIUDWPOk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/646/1%2AY0yzWPxoNB5uaDpD3_vP0w.png)

uwsgi.ini

这是 uWSGI 服务器使用的配置文件。这是我们定义uWSGI监听流量的地方,在这种情况下它是8080。注意由于我们已经定义了套接字,所以你不能直接访问uWSGI服务器,如果你愿意,你需要在它前面有一个Web服务器要仅使用 uWSGI,您可以将此选项更改为 http。另一个导入选项是模块,我们将它指向我们安装的模块示例,然后是 wsgi 模块和 app 变量。因此 moduleu003dexample.wsgi:app。在这个例子中,wsgi.py 模块调用 create_app() 函数来创建 Flask 应用程序。

Postgres

Postgres 镜像比来自 Docker hub 的最新 Postgres 镜像更简单,然后我们将一些环境变量传递给它来配置它。

数据库.conf

[](https://res.cloudinary.com/practicaldev/image/fetch/s--ro9ROU3Y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1。 medium.com/max/666/1%2AHuLIgSd25nFTbcSIkotj_Q.png)

数据库.conf

传递的环境变量看起来像这样。 注意 您不需要将端口或主机传递给 Postgres Docker 容器。这些由 Flask 容器使用。

码头工人-compose.yml

所以我们已经定义了我们的 dockerfile 和这些 dockerfile 使用的配置文件,但是我们如何实际使用 Docker。我们可以使用 docker 的一种方法是使用 docker-compose 来定义它。在这里,我们定义了一组服务,Docker 将自动运行和构建这些服务并为我们处理网络。我个人使用 docker-compose 进行开发,因为它为每个 Docker 映像/容器运行 docker build 和 docker run 命令节省了大量时间。

[](https://res.cloudinary.com/practicaldev/image/fetch/s--DTDz2tq6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://cdn-images-1. medium.com/max/974/1%2AQqpZqciQLUuc3M9MIP-UFg.png)

码头工人-compose.yml

所以在文件顶部我们定义了docker-compose的版本号,建议用户现在使用版本3。然后我们定义我们的服务,一个服务等于一个 Docker 容器。每个服务都有一个名称,例如 web_server、app 和 database。

网络_服务器

该服务运行我们的 Nginx 服务器。出于显而易见的原因,我们将容器称为 nginx。我们将要构建的 dockerfile 的位置传递给它。构建上下文:.简单地说是相对于当前工作目录,所以当我们复制文件时,我们将相对于 docker-compose.yml 文件复制它们。我们发布端口的最后一件事是,主机上端口 80 上的所有流量都映射到端口 80 上的 Docker 容器。我们可以使用客户机上的任何端口,比如使用 8001,然后我们将访问 Web 服务器通过转到本地主机:8001。最后一部分指定此容器依赖于应用程序容器,因此应用程序将在此容器之前运行。

应用程序

这是一个相对简单的服务,我们再次将其指向我们的烧瓶 dockerfile 并将构建上下文设置为当前文件夹。然后我们将一些环境变量作为文件传递,变量取自该文件(与上面定义的相同的 database.conf)。这些变量用于允许 Python 连接到数据库。最后,我们公开了 8080 端口,这也是出于文档目的,以便其他用户知道此容器需要端口 8080 上的流量。由于我们在设置应用程序时需要连接到数据库,因此我们依赖于首先运行的数据库容器。

数据库

该服务没有自己的 dockerfile,而是使用官方 Postgres 镜像。然后它将一些环境变量作为文件传递,与传递给 Flask 容器的文件相同。我们实际上不需要主机或端口变量,但在这种情况下维护单个文件更容易。然后我们将主机端口 5432 映射到来宾 Docker 容器端口 5432。这是 Postgres 侦听的端口。与 Nginx 一样,您可以将主机端口设置为您想要的任何端口,但请确保在 database.conf 中更改它并更新 POSTGRES_PORT 变量。最后,我们挂载了一个卷,以便数据是持久的,如果没有这个,当数据库 Docker 容器被杀死时,您将丢失所有数据。通过挂载 db_volume,即使您杀死容器以更新 Postgres 映像,您的数据也将保留。

Docker Compose 构建/运行

要实际运行 docker-compose 文件(与 docker-compose.yml 位于同一文件夹中),您可以执行以下操作。其中 -d 表示它在后台运行。这将构建所有三个服务,一旦它构建了我们的 Docker 镜像,它将把这些 Docker 镜像作为 Docker 容器运行。

docker-compose up --build -d

Docker 构建/运行

需要注意的一件重要事情是 docker-compose 不应该真正用于生产,原因有几个,例如更新 Docker 容器时的停机时间。如果您只部署到 1 个主机 docker-compose 应该没问题,但实际上,大多数应用程序需要高可用性和零停机时间更新,在这种情况下,您应该使用容器编排工具,例如 Kuberenetes。所以另一种方法是自己构建和运行每个 Docker 容器,这个 docker-compose.yml 文件的等效命令是。

# Build our images first
docker build -f docker/nginx/Dockerfile -t nginx .
docker build -f docker/flask/Dockerfile -t flask .

# Run our containers
docker run -d --name nginx -p 80:80 nginx
docker run -d --name flask -p 8080 --env-file docker/database.conf flask
docker volume create --name db\_volume
docker run -d --name postgres -p 5432:5432 \
 --env-file docker/database.conf \
 -v db\_volume:/var/lib/postgresql postgres:latest

注意:构建自己的镜像后,您可以将它们推送到公共或私有 Docker 注册表,以便您或其他人可以访问它们。这是在 CI 管道期间访问图像的常用方法。事实上,像 postgres:latest 这样的基础镜像取自官方 Docker 注册表。

附录

  • 示例源代码

  • 用碳制成的代码图像

Logo

云原生社区为您提供最前沿的新闻资讯和知识内容

更多推荐