将 Python 应用容器化
wemake-django-template
概述
Python 是最流行的编程语言之一,应用范围非常广泛,从构建后端到创建神经网络都能胜任。然而,正如上文所述,在不同项目之间保持 Python 环境的一致性可能非常麻烦。
这时 Docker 就派上了用场。Docker 支持应用程序容器化,这意味着您可以运行服务和应用程序的独立实例。此外,Docker 还允许您使用 Docker Compose 将应用程序和服务连接起来,这使得构建 Python 应用程序变得非常便捷。
请注意,本文中引用的所有代码均来自此GitHub 代码库。这是一个正在运行的应用程序,虽然该版本中的代码尚未达到生产就绪状态,但仍然可以通过 Docker 运行所有功能。
什么是 Docker?
Docker到底是什么?我前面已经简单介绍过了,正如前面提到的,它是一款可以将应用程序和服务容器化的软件。它的优势在于,你无需安装和使用多个Python版本,也无需管理多个虚拟环境,Docker可以为每个应用程序分别构建独立的隔离环境。
这意味着我们不需要python -m venv venv每次构建新项目时都运行,只要我们设置好DockerFile一个docker-compose.yml文件即可。
图片、容器和排版
好了,既然这件事已经解决了,我们需要快速复习一些词汇。
首先是 Docker 镜像。Docker 镜像本质上是应用程序的一个不可变的“快照”。它包含在新的容器中启动应用程序的指令,并且可以与其他镜像一起构建。这种不可变性非常重要,因为它允许我们拥有同一应用程序的多个版本,而不会出现镜像版本冲突的情况。
其次是 Docker 容器。Docker 容器是基于我们镜像运行的应用程序实例。它类似于前面提到的“快照”的复制品,无法共享。但是,我们可以根据需要启动任意数量的特定镜像的 Docker 容器,这对于数据库和类似服务来说非常方便!
安装 Docker
如果您还没有安装Docker Desktop ,则需要先进行安装。如果您已经安装了 Docker Desktop 并且知道如何操作,则可以跳到下一节。
我还会简要介绍一下您可以使用 Docker CLI 运行的一些命令。
docker image ls
docker image ls列出您计算机上当前所有可用的图像。
docker container ls
docker container ls列出所有当前已启动/正在运行的容器。
docker container ls -a
docker container ls -a列出所有容器,无论它们是否已启动或处于活动状态。该-a标志是以下情况的缩写:--all
docker container rm <first 3 symbols of the container ID or container name> [...other containers you want to remove]
docker container rm移除列出的容器。请注意,您需要提供容器 ID 的前三位字符或容器名称,并且移除容器之前该容器不能正在运行。
docker [COMMAND] --help
帮助标志非常有用,可以与任何命令一起使用。它会列出所有选项,并提供命令的使用说明。
设置 Docker 环境
也就是说,要设置 Docker 环境,我们需要一些东西。
- 我们需要为我们的应用程序创建一个 Docker 镜像。
- 我们需要以某种方式协调我们的应用程序使用的所有数据库。
关于第一点,这就是 `.a` 文件的作用Dockerfile。`.a`Dockerfile文件定义了设置和运行应用程序所需的命令行参数。简单来说,它是一个指定设置和运行应用程序所需的最基本信息的文件。
以下是一个示例Dockerfile:
FROM python:3.8
WORKDIR /src
COPY ./requirements.txt .
RUN pip install -r requirements.txt
COPY . .
EXPOSE 5000
CMD ["flask", "run", "--host", "0.0.0.0"]
Dockerfile 中包含什么内容
简单概括一下所有内容:
第一行指定我们的应用程序镜像基于 Python 3.8 镜像。
- 既然我们可以利用其他图像来构建自己的图像,我们就充分利用了这一点。
- 更多镜像可以在DockerHub中找到。
- 我们用冒号后跟标签来指定所需的标签/版本,所以这里
python:3.8指定的是Python 3.8标签。
第二行代码设置了容器的工作目录。这一点很重要,因为我们的应用程序文件将存放在这个目录中。
第三行代码将requirements.txt文件复制到我们的工作目录中。我们可以一次性复制所有内容,但实际上我们不这样做是有重要原因的,接下来会解释。
第四行会运行我们列出的命令。所以这里它会根据requirements.txt文件安装我们的 Python 包。
- 我们单独执行此操作,因为 Docker 会缓存我们可能需要的任何外部软件包,具体取决于
requirements.txt(或package.jsonNPM)的更改。 - 如果我们不这样做,Docker 每次都会卸载缓存的软件包,浪费大量原本可以节省的时间。
第五行代码将应用程序目录中的剩余文件复制到工作目录中。如果需要,我们也可以将其添加到不同的子目录中,如下所示:
COPY . ./my-subdirectory
第六行定义了一个端口,供我们访问容器。这里是端口 5000。
最后一行指定了容器启动时要执行的默认命令。请注意,命令的每个部分都位于一个数组中。
- 请注意,这种字符串数组是首选方法,并且每个命令片段都必须用双引号 (") 括起来,因为它会被解析为 JSON 数组。
Docker Compose
太好了,那数据库呢?当然,我们可以在本地运行数据库,但由于 Docker 也提供了数据库镜像,我们实际上可以运行任意数量的容器来运行我们的数据库。
但这里有个大问题,我们不想手动创建和启动应用程序依赖的每一个服务。如果只有一个数据库,那当然没问题,但如果我们有两个不同的数据库,而且还有一个应用程序/服务需要自己的数据库呢?
这就docker-compose派上用场了(随着规模扩大,Kubernetes 也派上用场了,但这又是另一个话题了)。与其手动启动和创建应用程序所依赖的服务的容器,不如使用一个docker-compose.yml配置文件来自动化大部分操作。
请点击以下链接查看示例docker-compose.yml文件。
遗憾的是,本文无法提供完整的 Docker Compose 文件创建指南,但我会概述各个部分。
所以文件中包含以下内容(可能有一些注释):
version: '3.5' # Docker compose version
services:
app:
build:
context: .
# Binding the current working directory to the new container
volumes:
- .:/
# The working directory in the app Dockerfile
working_dir: ''
command: ''
links:
- app-redis
# ENV configuration
env_file: .env
# Additional app secrets
environment:
APP_TOKEN: /run/secrets/app_token
secrets:
- app_token
ports:
- '5000:5000'
app-redis:
image: redis:5
ports:
- '6379:6379'
# App secret registration
secrets:
app_token:
file: app_token.txt
总体而言,compose 文件包含三个主要部分:
- 我们想要的 Docker Compose 版本
- 指定 Docker Compose 版本非常重要,因为某些属性在其他版本中不可用,更多信息请参阅Docker Compose v3 文档。
- 注册服务
- 注册容器密钥
在服务注册部分,我们有以下内容:
- 配置我们的 Python 应用容器
- 将我们的容器连接到另一个服务(Redis)
- 绑定我们的工作目录,以便我们可以实时更新容器。
- 添加 .env 文件
- 添加我们的应用程序秘密
- 暴露端口并启动命令
- 配置 Redis 容器
- 添加要从中构建容器的镜像
- 暴露端口
请注意,该links属性的作用是明确表明该app-redis服务是我们 Python 应用的依赖项。这意味着我们在 Flask 应用中访问数据库的方式与通常情况不同,但我们稍后会详细介绍。
我们的 Python 应用程序需要更新
现在我们已经启动了docker-compose.yml文件,但我们不能像之前那样访问 Redis 实例了。因此,localhost我们不能再像以前那样让 Python 应用程序访问 Redis 实例,而是需要更新它,使其使用服务名称,如下所示:
# Fetch.py
# Std lib imports
import os
import json
from pathlib import Path
## Lib Imports
import redis
import requests
from dotenv import load_dotenv
# Global Module Var
isDocker = os.getenv('IS_DOCKER')
cacheHost = 'app-redis'
# ...Extra code
red_cache = redis.Redis(host = cacheHost, port = 6379)
# ...Rest of the module's implementation
非常快捷方便,这样我们就可以docker-compose up在我们的应用程序目录中运行并启动 Flask 应用程序。
结论
所以我们总结了如何快速将 Python 应用 Docker 化。这包括以下步骤:
- 创建 Dockerfile 时:
- 我们开发这款应用所依据的图片
- 我们正在复制的内容
- 我们想要运行的任何终端命令
- 启动命令
- 一个要暴露的端口
- 创建
docker-compose.yml包含以下内容的文件:- Docker Compose 的版本
- 将要创建和运行的服务列表
- 上述服务需要明确说明它们之间是否相互关联。
- 此外,我们的 Python 应用程序所连接的服务还进行了额外的 .env 和密钥设置。
- 我们想在应用程序中隐藏的任何秘密
最终生成的应用程序的依赖项由 Docker而非本地机器提供。这使得开发环境每次都能保持一致的性能和配置。
文章来源:https://dev.to/wilsonj806/dockerizing-a-python-app-2ee
