zl程序教程

您现在的位置是:首页 >  后端

当前栏目

移植 Python 项目到容器 (Container) 中

Python项目容器 移植 container
2023-09-14 09:12:59 时间

使用容器 (Container) 的主要原因,有以下几点:

  • 比较复杂的项目,Python 可能还需要调用第三方非 Python 的库,甚至是跟一些服务器交互,因此就会遇到操作系统版本不同、环境变量设定没设定或是配置文件没设定正确等,而造成整个项目无法正常的运行起来,会花费很多的时间在调参而造成工作的效率变差。
  • 如果在程序的撰写过程中需要有多个测试环境,例如测试不同版本的服务器是否能在我们撰写的代码上正常的执行。如果把这个服务器直接安装在自已的操作系统环境上,需要换版本测试时会反复的安装和解安装,容易因为没有解安装完整,旧版的档案还存在,而造成版本混乱服务器也就无法正常的启动起来。
  • 在开发一个开源的项目,如果希望要给很多人来使用,那就需要有一个简单的安装方法。如把开发完的程序包成容器映像 (container image) 然后放到公开下载点,如 docker hub 上,使用者如果想要使用,只要下几行命令就可以把开发的程序运行起来了。

而 Docker 是一种软件平台,可让您快速地建立、测试和部署应用程序,它将软件封装到容器 (Container) 的标准化单位,其中包含链接库、系统工具、程序代码和运行时间等执行软件所需的所有项目,用户可以将应用程序快速地部署到各种环境并加以扩展,而且知道程序代码可以执行。从下图可以看出,容器比虚拟机 (VM) 来的更轻量,更节省资源。
Container 与 VM 的差别图 1. Container 与 VM 的差别

虚拟机与容器的差异比较如下:
虚拟机

  • 需要安装操作系统
  • 虚拟机里面的操作系统开机需要花时间开机
  • 完全的把系统的硬件资源隔离
  • 占用硬盘的容量较大

容器

  • 直接从 Docker Hub 下拉不同操作系统的映像
  • 不用开机,启动速度比 VM 快
  • 底层还是使用原操作系统的 Kernel
  • 占用硬盘的容量较小

接下来说明一下操作容器常见的一些名词

  1. Docker image:它是 Docker 的映像档主要是一个只读的档案,是启动 Docker container 要使用到的档案。另外 Docker 的 image 可以像是堆积木一样,一层一层的把 Docker image 堆起来。可以透过撰写 Dockerfile 体会。
    Docker Image 要从哪里来?
  • 从网络上的 Docker Hub 下拉 (pull) 下来 (https://hub.docker.com)。
  • 从另外一台计算机上的 Docker image 导出,然后在导入到自已的计算机
  • 自行撰写 Dockerfile
  1. Docker Container:Docker Container 是透过 Docker image 执行起来的进程 (Process),同一个 Docker image 可以启动多个 Docker Container。Docker container 和 Docker container 之间的环境是隔离开离来的。

  2. Docker Hub:Docker Hub 可以把它想象成 GitHub ,GitHub 是用来存放程序代码的仓库,Docker Hub 是来存放 Docker image 的仓库。可以使用网络上公开的 Docker Hub,或是在自已的内部环境下架设一个私有的 Docker Hub 又被称为 Docker Registry。

安装 Windows Docker
因为 Docker 的技术是采用 Linux 的虚拟化技术,所以要在 Windows 上安装 Docker 应用程序会比 Linux 上复杂,首先要打开主机基本输入输出系统 (basic input/output system,BIOS) 上的虚拟化支援,接着要安装适用于 Linux 的 Windows 子系统 (Windows subsystem for Linux, WSL),最后再安装 Docker Desktop for Windows。

  1. 致能主机虚拟化支援:关闭自己的计算机或笔记本,根据主机的出厂预设值来进入 BIOS 设定画面,以 惠普 (HP) 笔记本而言,是按 ESC 键,进入后在菜单上选 System Configuration 中的 Virtualization Technology 打开致能 (Enabled),存储后重新开机即可,如下图所示。
    设定 BIOS 支援虚拟化
    图 2 设定 BIOS 支援虚拟化

进入 Windows 后安装 WSL 元件,安装时要先注意版本限制,目前有 WSL 1 与 WSL 2 两个版本,如要安装 WSL 2 ,对于 Windows x64 系统而言,需要版本 1903 或更高版本,采用 内部版本 18362.1049+ 或更高版本。要检查自己主机的版本,可以打开命令提示符后,输入 ver 命令就可以显示出主机的操作系统版本,如下图所示。

显示当前操作系统版本
图 3 显示当前操作系统版本

  1. 安装 WSL
  • 启用适用于 WSL 功能
    以管理员身份打开 PowerShell 并运行:
dism.exe /online /enable-feature /featurename:Microsoft-Windows-Subsystem-Linux /all /norestart
  • 启用虚拟机功能
    以管理员身份打开 PowerShell 并运行
dism.exe /online /enable-feature /featurename:VirtualMachinePlatform /all /norestart

以管理员身份打开 PowerShell 并运行

wsl --set-default-version 2
  • 安装所选的 Linux 分发
    打开 Microsoft Store,并选择你偏好的 Linux 分发版。本次选择较常用的 Ubuntu 18.04 LTS 作为 WSL 内的 Linux 操作系统,安装完毕后点选 启动 打开一个控制台窗口,并进行账号密码设定

安装 Ubuntu 18.04 LTS 分发版
图 4 安装 Ubuntu 18.04 LTS 分发版

 启动 WSL 控制台窗口并设定
图 5 启动 WSL 控制台窗口并设定

  1. 安装 Docker Desktop for Windows

下载后,直接安装即可,因为需要用到操作系统核心技术,所以需要重新启动,并会自动启动,安装成功后可看到如下图所示。
Docker Desktop for Windows 运行画面
图 6 Docker Desktop for Windows 运行画面

将项目移植到容器内

  1. 找到适当的 Docker Image
    首先到 Docker hub 中找到 Python 相关镜像文档,因为本书用的是 Python 3.7.9,所以可以在 Docker Hub 上先找到 Python 这个大类,然后在 filter tag 中输入 3.7.9 版本号,可将范围缩少很多,如下图所示。因为这是练习,所以找一个小一点的映像档 python:3.7.9-slim,只有 41 MB。
    Docker for Python 官方网页根据版号 3.7.9 过滤
    图 7 Docker for Python 官方网页根据版号 3.7.9 过滤

  2. 下载后设定 Docker Image

按下 Windows 徽标键 + R,然后键入“wsl”,选择“确定”,启动 WSL 控制台窗口,在控制台窗口中输入下列命令,来下载 python:3.7.9-slim 映像文档,接着创建一个新的容器并以交互模式运行容器, -it 选项是指以交互模式运行容器并为容器分配一个伪输入终端,容器内放置 python:3.7.9-slim 映像,交互的界面使用 bash。

$docker pull python:3.7.9-slim
$docker run -it python:3.7.9-slim bash

接着就可以在容器内进行操作,可以发现前面的提示号已经改变成 root@1879b9b7a980 ,@前面是管理者帐号,后面是 container id 可以安装需要的库类包,因为在容器内选择不用交互的后台进行绘图,所以就不安装 PyQt,安装 numpy, matplotlib,指定安装的网站是国内网站,并增加逾时限制,避免因为逾时而无法安装成功

#pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
#pip install --default-timeout=1000 matplotlib==3.3.3 numpy==1.19.3

进入 Docker 容器内进行安装 Python 包
图 8 进入 Docker 容器内进行安装 Python 包

  1. 建立新的 Docker Image

按下 Windows 徽标键 + R,然后键入“wsl”,选择“确定”,启动另一个新的 WSL 控制台窗口,在控制台窗口中输入下列命令,将目前的容器内的内容写入影像文档内。输入 docker ps 显示目前正在运行的容器,根据 NAMES 下达 docker commit 命令,将 container 存成影像文挡,调用 docker images 列出目前本地仓库内的影像列表,会发现有一个影像没有仓库 (REPOSITORY) 与标签 (TAG) 名称,用 docker tag 命令来为影像设定标签,要透过 IMAGE ID 来指定,最后再用 docker images 来观察结果是否成功。

$docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
1879b9b7a980        python:3.7.9-slim   "bash"              2 hours ago         Up 2 hours                              festive_khayyam

$docker commit festive_khayyam
sha256:24cf3d6357a48ce47c6366ce87ccb26af8a52cf312fcb8944860de33477db19d

$docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
<none>              <none>              24cf3d6357a4        5 seconds ago       254MB
pycontainer         3.7.9               0c90562c628f        26 hours ago        784MB
python              3.7.9-slim          0cbc88b2116d        5 days ago          112MB

$docker tag 24cf3d6357a4 pythoncontainer:3.7.9

$docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
pythoncontainer     3.7.9               24cf3d6357a4        14 minutes ago      254MB
pycontainer         3.7.9               0c90562c628f        27 hours ago        784MB
python              3.7.9-slim          0cbc88b2116d        5 days ago          112MB
  1. 运行项目

接下来要运行项目,首先在容器内先建立一个工作目录

#mkdir /usr/app
#ls -la /usr/app

plot2Container.py

import matplotlib.pyplot as plt
import matplotlib
import numpy as np
matplotlib.use('Agg')

# 创建一个 figure 对象,相当于一个空白的画布
figure = plt.figure()
 
# 在画布上添加一个坐标系,画布内有 2 x 1 个子图,回传第一个子图
axes1 = figure.add_subplot(2, 1, 1)
 
# 准备画图的数据
x = [1, 2, 3, 4, 5, 6, 7]
y = [21, 27, 29, 32, 29, 28, 35]

# 设置画布的基本元素
axes1.set_xlabel('x label')
axes1.set_ylabel('y label')
axes1.set_title("title")

# 画图
axes1.plot(x, y)
plt.savefig("plot2Container.jpg")

接着将要运行的项目复制一份至容器中

$docker cp plot2Container.py festive_khayyam:/usr/app

在容器中运行后,会产生一份图档

#cd /usr/app
#python plot2Container.py
#ls -la
total 40
drwxr-xr-x 2 root root  4096 Dec  9 05:19 .
drwxr-xr-x 1 root root  4096 Dec  9 05:18 ..
-rw-r--r-- 1 root root 20657 Dec  9 05:19 plot2Container.jpg
-rwxrwxrwx 1 1000 1000   597 Dec  8 02:45 plot2Container.py

将图档复制到本地机器检视图档

docker cp festive_khayyam:/usr/app/plot2Container.jpg .

下图说明本地机器与容器内操作的交互状况,右边是本地机器的 WSL 终端机操作画面,左边是容器内的操作画面。
本地机器与容器内操作的交互状况
图 9 本地机器与容器内操作的交互状况

还有一个比较简易的方法就是直接在本机机器调用容器内的环境运行,指定本地机器的一个文件夹作为容器内的工作目录(必须要事先存在),接着套用运行容器内的环境来运行项目。 -v 选项是指定本地机器内的文件夹与容器内文件夹的对应关系,将本地文件夹对应到容器内的工作文件夹,-w 选项是指定容器内的工作文件夹, pythoncontainer:3.7.9 是指定容器内要运行的影像档内容, “python plot2Container.py” 是指定给容器内要运行的命令。下图显示运行结果,因为是把文件夹进行对应,所以是不用进行复制的。

docker run  -v /mnt/d/czcit:/home -w /home pythoncontainer:3.7.9 python plot2Container.py

本地机器直接指定运行容器的交互状况
图 10 本地机器直接指定运行容器的交互状况

  1. 全部自动化完成

可以透过撰写 Dockerfile 的方式将上述动作全部一次完成,这种方法比较适合项目测试之用
Dockerfile

# Use an official Python runtime as a parent image
FROM python:3.7.9-slim

# Set the working directory to /app
WORKDIR /home

# Copy the current directory contents into the container at /app including plot2Container.py and requirements.txt
COPY . /home

# Install any needed packages specified in requirements.txt
RUN pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
RUN pip install --default-timeout=1000 -r requirements.txt

# Define environment variable
ENV NAME World

# Run app.py when the container launches
CMD ["python", "plot2Container.py"]

使用 Dockerfile 自动运行指定项目
图 11 使用 Dockerfile 自动运行指定项目

以上的 Dockerfile 主要有用到的指令说明如下

  • FROM: 使用到的 Docker Image 名称,目前使用 python:3.7.9-slim
  • WORKDIR: 指定容器内的工作文件夹
  • COPY: 把本地机器的档案复制到容器里
  • RUN: 运行容器内的操作
  • ENV: 用来设定环境变量
  • CMD: 在运行 docker run 的指令时会直接呼叫