Docker Compose 搭建简单的Python网络应用程序

4,909 阅读4分钟

在这个页面上,你可以建立一个简单的Python网络应用程序,运行在Docker Compose上。该应用使用Flask框架,并在Redis中维护一个点击计数器。虽然样本使用了Python,但即使你不熟悉它,这里展示的概念也应该是可以理解的。(官网教程翻译)

前提条件

请确保你已经安装了Docker Engine和Docker Compose。你不需要安装Python或Redis,因为两者都是由Docker镜像提供的。

第1步:设置

定义应用程序的依赖。

  1. 为该项目创建一个目录。
$ mkdir composetest
$ cd composetest
  1. 在你的项目目录下创建一个名为 app.py 的文件,并把以下内容粘贴进去:
import time

import redis
from flask import Flask

app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)

def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)

@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.\n'.format(count)

在这个例子中,redis 是应用程序网络中的redis容器的主机名。我们使用Redis的默认端口,6379。

处理瞬时错误 注意 get_hit_count 函数的编写方式。这个基本的重试循环让我们在redis服务不可用时多次尝试我们的请求。这在应用程序上线的时候很有用,而且如果Redis服务需要在应用程序的生命周期内随时重启,这也使我们的应用程序更具弹性。在一个集群中,这也有助于处理节点之间的瞬间连接中断。

  1. 在你的项目目录下创建另一个名为 requirements.txt 的文件,并将以下内容粘贴进去:
flask
redis

第2步:创建一个Dockerfile

在这一步,你要写一个Dockerfile来构建一个Docker镜像。这个镜像包含了Python应用程序所需的所有依赖项,包括Python本身。

在你的项目目录中,创建一个名为 Dockerfile 的文件并粘贴以下内容:

# syntax=docker/dockerfile:1
FROM python:3.7-alpine
WORKDIR /code
ENV FLASK_APP=app.py
ENV FLASK_RUN_HOST=0.0.0.0
RUN apk add --no-cache gcc musl-dev linux-headers
COPY requirements.txt requirements.txt
RUN pip install -r requirements.txt
EXPOSE 5000
COPY . .
CMD ["flask", "run"]

这告诉Docker:

  • 从Python 3.7镜像开始构建一个镜像。

  • 设置工作目录为 /code

  • 设置 flask 命令使用的环境变量。

  • 安装gcc和其他依赖项

  • 复制 requirements.txt 并安装Python依赖项。

  • 给镜像添加元数据,描述容器在5000端口上监听

  • 将项目中的当前目录.复制到镜像中的工作目录.。

  • 将容器的默认命令设置为 flask run

第3步:在Compose文件中定义服务

在你的项目目录下创建一个名为 docker-compose.yml 的文件,并粘贴以下内容:

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
  redis:
    image: "redis:alpine"

这个 Compose 文件定义了两个服务:webredis

Web服务

web 服务使用一个从当前目录下的 Dockerfile 文件构建的镜像。然后它将容器和主机绑定到暴露的端口8000上。本例服务使用Flask网络服务器的默认端口,即5000。

Redis服务

redis 服务使用了一个从Docker Hub仓库中拉取的公共Redis镜像。

第4步:用Compose构建和运行你的应用

  1. 在你的项目目录中,通过运行 docker-compose up 来启动你的应用程序。
$ docker-compose up

Creating network "composetest_default" with the default driver
Creating composetest_web_1 ...
Creating composetest_redis_1 ...
Creating composetest_web_1
Creating composetest_redis_1 ... done
Attaching to composetest_web_1, composetest_redis_1
web_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
redis_1  | 1:C 17 Aug 22:11:10.480 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis_1  | 1:C 17 Aug 22:11:10.480 # Redis version=4.0.1, bits=64, commit=00000000, modified=0, pid=1, just started
redis_1  | 1:C 17 Aug 22:11:10.480 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
web_1    |  * Restarting with stat
redis_1  | 1:M 17 Aug 22:11:10.483 * Running mode=standalone, port=6379.
redis_1  | 1:M 17 Aug 22:11:10.483 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128.
web_1    |  * Debugger is active!
redis_1  | 1:M 17 Aug 22:11:10.483 # Server initialized
redis_1  | 1:M 17 Aug 22:11:10.483 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled.
web_1    |  * Debugger PIN: 330-787-903
redis_1  | 1:M 17 Aug 22:11:10.483 * Ready to accept connections

Compose会拉取一个Redis镜像,为你的代码构建一个镜像,并启动你定义的服务。在这种情况下,代码在构建时被静态地复制到镜像中。

  1. 在浏览器中输入http://localhost:8000/,可以看到应用程序正在运行。

如果你在本地Linux上使用Docker、Docker Desktop for Mac或Docker Desktop for Windows,那么网络应用现在应该在你的Docker守护主机上监听8000端口。将你的网络浏览器指向http://localhost:8000,找到`Hello World` 消息。如果这还没有解决,你也可以试试http://127.0.0.1:8000。

你应该在你的浏览器中看到一条消息说:

Hello World! I have been seen 1 times.

quick-hello-world-1.png 3. 刷新页面。

这个数字应该是递增的。

Hello World! I have been seen 2 times.

quick-hello-world-2.png

  1. 切换到另一个终端窗口,并输入 docker image ls 来列出本地图像。

在这一点上,列出图像应该返回 redisweb

$ docker image ls

REPOSITORY        TAG           IMAGE ID      CREATED        SIZE
composetest_web   latest        e2c21aa48cc1  4 minutes ago  93.8MB
python            3.4-alpine    84e6077c7ab6  7 days ago     82.5MB
redis             alpine        9d8fa9aa0e5b  3 weeks ago    27.5MB

你可以用 docker inspect <tag or id> 检查镜像。

  1. 停止应用程序,可以在第二个终端中的项目目录中运行 docker-compose down,或者在启动应用程序的原始终端中点击CTRL+C。

第5步:编辑Compose文件以添加绑定挂载

在你的项目目录中编辑 docker-compose.yml,为 web 服务添加一个绑定挂载。

version: "3.9"
services:
  web:
    build: .
    ports:
      - "8000:5000"
    volumes:
      - .:/code
    environment:
      FLASK_ENV: development
  redis:
    image: "redis:alpine"

新的 volumes 键将主机上的项目目录(当前目录)挂载到容器内的 /code,允许你即时修改代码,而不需要重建镜像。environment 键设置 FLASK_ENV 环境变量,它告诉 flask run 在开发模式下运行,并在代码变化时重新加载。这种模式只应在开发中使用。

第6步:用Compose重新构建并运行应用程序

在你的项目目录下,输入 docker-compose up,用更新的Compose文件构建应用程序,并运行它。

$ docker-compose up
Creating network "composetest_default" with the default driver
Creating composetest_web_1 ...
Creating composetest_redis_1 ...
Creating composetest_web_1
Creating composetest_redis_1 ... done
Attaching to composetest_web_1, composetest_redis_1
web_1    |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
...

再次在网络浏览器中检查 Hello World 消息,并刷新以看到计数的增加。

共享文件夹、卷和绑定挂载

  • 如果你的项目在 Users 目录之外(cd ~),那么你需要共享你正在使用的Dockerfile文件和volume的驱动器或位置。如果你得到运行时的错误,表明找不到应用文件,卷挂载被拒绝,或服务无法启动,请尝试启用文件或驱动器共享。卷挂载需要在 C:\Users(Windows)或 /Users(Mac)之外的项目共享驱动器,而且对于Docker Desktop for Windows上任何使用Linux容器的项目都是必需的。

  • 如果你在旧的Windows操作系统上使用Oracle VirtualBox,你可能会遇到这个VB故障单中描述的共享文件夹的问题。较新的Windows系统符合Docker Desktop for Windows的要求并且不需要VirtualBox。

第7步:更新应用程序

因为应用程序代码现在是用volume装入容器的,所以你可以对其代码进行修改,并立即看到变化,而不需要重建镜像。

改变 app.py 中的问候语,并保存它。例如,将 Hello World!的信息改为 Hello from Docker!

return 'Hello from Docker! I have been seen {} times.\n'.format(count)

在你的浏览器中刷新该应用程序。问候语应该被更新,而计数器应该仍然在递增。

quick-hello-world-3.png

第8步:试验一些其他命令

如果你想在后台运行你的服务,你可以向 docker-compose up 传递 -d 标志(用于 "分离 "模式),并使用docker-compose ps 来查看当前正在运行的内容:

$ docker-compose up -d
Starting composetest_redis_1...
Starting composetest_web_1...

$ docker-compose ps

       Name                      Command               State           Ports         
-------------------------------------------------------------------------------------
composetest_redis_1   docker-entrypoint.sh redis ...   Up      6379/tcp              
composetest_web_1     flask run                        Up      0.0.0.0:8000->5000/tcp

docker-compose 的运行命令允许你为你的服务运行一次性的命令。例如,看看 web 服务有哪些环境变量可用:

$ docker-compose run web env

参见 docker-compose --help 以查看其他可用的命令。

如果你用 docker-compose up -d 启动了Compose,一旦你完成了你的服务,就停止它们:

$ docker-compose stop

你可以用 down 命令来关闭一切,完全删除容器。通过 --volumes,也可以删除Redis容器所使用的数据卷:

$ docker-compose down --volumes

在这一点上,你已经明白了 Compose 是如何工作的。