如何使用MongoDB和Docker设置Flask
作为 Write for DOnations 计划的一部分,作者选择了 Internet Archive 来接收捐赠。
介绍
在构建和维护多种不同技术时,开发 Web 应用程序可能会变得复杂且耗时。 考虑为降低应用程序的复杂性和生产时间而设计的更轻量级选项可以产生更灵活和可扩展的解决方案。 作为基于 Python 构建的微型 Web 框架,Flask 为开发人员提供了一种可扩展的方式,通过可以集成到项目中的扩展来扩展他们的应用程序。 为了继续开发人员技术堆栈的可扩展性,MongoDB 是一个 NoSQL 数据库,旨在扩展和处理频繁的更改。 开发人员可以使用 Docker 来简化打包和部署应用程序的过程。
Docker Compose 允许您在单个文件中定义基础设施,包括应用程序服务、网络卷和绑定挂载,从而进一步简化了开发环境。 使用 Docker Compose 比运行多个 docker container run
命令提供了易用性。 它允许您在单个 Compose 文件中定义所有服务,并使用单个命令从您的配置中创建和启动所有服务。 这可确保在整个容器基础架构中都有版本控制。 Docker Compose 使用项目名称将环境彼此隔离,这允许您在单个主机上运行多个环境。
在本教程中,您将在 Docker 容器 中使用 Flask、Nginx 和 MongoDB 构建、打包和运行待办事项 Web 应用程序。 您将在 docker-compose.yml
文件中定义整个堆栈配置,以及 Python、MongoDB 和 Nginx 的配置文件。 Flask 需要一个 Web 服务器来服务 HTTP 请求,因此您还将使用 Gunicorn,它是一个 Python WSGI HTTP 服务器来服务应用程序。 Nginx 充当反向代理服务器,将请求转发到 Gunicorn 进行处理。
先决条件
要学习本教程,您将需要以下内容:
- 按照初始服务器设置教程中的步骤配置的具有
sudo
权限的非root用户。 - 使用 如何安装和使用 Docker 的步骤 1 和步骤 2 中的说明安装 Docker。
- 使用 如何安装 Docker Compose 的步骤 1 中的说明安装 Docker Compose。
第 1 步 — 在 Docker Compose 中编写堆栈配置
在 Docker 上构建应用程序允许您根据在 Docker Compose 中所做的配置更改轻松地对基础架构进行版本控制。 基础设施可以在单个文件中定义并使用单个命令构建。 在这一步中,您将设置 docker-compose.yml
文件来运行您的 Flask 应用程序。
docker-compose.yml
文件允许您将应用程序基础架构定义为单独的服务。 这些服务可以相互连接,并且每个服务都可以附加一个 volume 以进行持久存储。 卷存储在由 Docker 管理的主机文件系统的一部分中(Linux 上的 /var/lib/docker/volumes/
)。
卷是在 Docker 中持久化数据的最佳方式,因为卷中的数据可以导出或与其他应用程序共享。 有关在 Docker 中共享数据的更多信息,您可以参考如何在 Docker 容器和主机之间共享数据。
首先,在服务器的主目录中为应用程序创建一个目录:
mkdir flaskapp
进入新创建的目录:
cd flaskapp
接下来,创建 docker-compose.yml
文件:
nano docker-compose.yml
docker-compose.yml
文件以标识 Docker Compose 文件版本 的版本号开头。 Docker Compose 文件版本 3
以 Docker 引擎版本 1.13.0+
为目标,这是此设置的先决条件。 您还将添加您将在下一步中定义的 services
标签:
码头工人-compose.yml
version: '3' services:
您现在将 flask
定义为 docker-compose.yml
文件中的第一个服务。 添加以下代码来定义 Flask 服务:
码头工人-compose.yml
. . . flask: build: context: app dockerfile: Dockerfile container_name: flask image: digitalocean.com/flask-python:3.6 restart: unless-stopped environment: APP_ENV: "prod" APP_DEBUG: "False" APP_PORT: 5000 MONGODB_DATABASE: flaskdb MONGODB_USERNAME: flaskuser MONGODB_PASSWORD: your_mongodb_password MONGODB_HOSTNAME: mongodb volumes: - appdata:/var/www depends_on: - mongodb networks: - frontend - backend
build
属性定义构建的 context
。 在这种情况下,app
文件夹将包含 Dockerfile
。
您使用 container_name
属性为每个容器定义一个名称。 image
属性指定镜像名称以及 Docker 镜像将被标记为什么。 restart
属性定义了容器应该如何重新启动——在你的例子中是 unless-stopped
。 这意味着您的容器只会在 Docker 引擎停止/重新启动或您明确停止容器时停止。 使用 unless-stopped
属性的好处是,一旦 Docker 引擎重新启动或发生任何错误,容器将自动启动。
environment
属性包含传递给容器的环境变量。 您需要为环境变量 MONGODB_PASSWORD
提供安全密码。 volumes
属性定义了服务正在使用的卷。 在您的情况下,卷 appdata
安装在 /var/www
目录的容器内。 depends_on
属性定义了 Flask 正常运行所依赖的服务。 在这种情况下,flask
服务将依赖于 mongodb
,因为 mongodb
服务充当应用程序的数据库。 depends_on
确保 flask
服务仅在 mongodb
服务正在运行时运行。
networks
属性将 frontend
和 backend
指定为 flask
服务可以访问的网络。
定义了 flask
服务后,您就可以将 MongoDB 配置添加到文件中了。 在此示例中,您将使用官方的 4.0.8
版本 mongo
图像。 将以下代码添加到 flask service
之后的 docker-compose.yml
文件中:
码头工人-compose.yml
. . . mongodb: image: mongo:4.0.8 container_name: mongodb restart: unless-stopped command: mongod --auth environment: MONGO_INITDB_ROOT_USERNAME: mongodbuser MONGO_INITDB_ROOT_PASSWORD: your_mongodb_root_password MONGO_INITDB_DATABASE: flaskdb MONGODB_DATA_DIR: /data/db MONDODB_LOG_DIR: /dev/null volumes: - mongodbdata:/data/db networks: - backend
此服务的 container_name
为 mongodb
,重启策略为 unless-stopped
。 您使用 command
属性来定义将在容器启动时执行的命令。 命令 mongod --auth
将禁止在没有凭据的情况下登录 MongoDB shell,这将通过要求身份验证来保护 MongoDB。
环境变量 MONGO_INITDB_ROOT_USERNAME
和 MONGO_INITDB_ROOT_PASSWORD
使用给定的凭据创建 root 用户,因此请务必使用强密码替换占位符。
MongoDB 默认将其数据存储在 /data/db
中,因此 /data/db
文件夹中的数据将写入命名卷 mongodbdata
以进行持久化。 因此,在重新启动时您不会丢失数据库。 mongoDB
服务不暴露任何端口,因此只能通过 backend
网络访问该服务。
接下来,您将为您的应用程序定义 Web 服务器。 将以下代码添加到您的 docker-compose.yml
文件中以配置 Nginx:
码头工人-compose.yml
. . . webserver: build: context: nginx dockerfile: Dockerfile image: digitalocean.com/webserver:latest container_name: webserver restart: unless-stopped environment: APP_ENV: "prod" APP_NAME: "webserver" APP_DEBUG: "false" SERVICE_NAME: "webserver" ports: - "80:80" - "443:443" volumes: - nginxdata:/var/log/nginx depends_on: - flask networks: - frontend
在这里,您定义了 build
的 context
,即包含 Dockerfile
的 nginx
文件夹。 使用 image
属性,您可以指定用于标记和运行容器的图像。 ports
属性将配置 Nginx 服务通过 :80
和 :443
和 volumes
将 nginxdata
卷挂载到容器内公开访问在 /var/log/nginx
目录下。
您已将 Web 服务器服务 depends_on
所在的服务定义为 flask
。 最后,networks
属性定义了网络 Web 服务器服务将有权访问 frontend
。
接下来,您将创建 桥接网络 以允许容器相互通信。 将以下行附加到文件的末尾:
码头工人-compose.yml
. . . networks: frontend: driver: bridge backend: driver: bridge
您为要连接的服务定义了两个网络 - frontend
和 backend
。 前端服务,例如 Nginx,将连接到 frontend
网络,因为它需要可公开访问。 后端服务,比如MongoDB,会连接到backend
网络,防止未经授权的访问服务。
接下来,您将使用卷来持久化数据库、应用程序和配置文件。 由于您的应用程序将使用数据库和文件,因此必须保留对它们所做的更改。 这些卷由 Docker 管理并存储在文件系统上。 将此代码添加到 docker-compose.yml
文件以配置卷:
码头工人-compose.yml
. . . volumes: mongodbdata: driver: local appdata: driver: local nginxdata: driver: local
volumes
部分声明应用程序将用于持久化数据的卷。 在这里,您已经定义了卷 mongodbdata
、appdata
和 nginxdata
分别用于持久化 MongoDB 数据库、Flask 应用程序数据和 Nginx Web 服务器日志。 所有这些卷都使用 local
驱动程序在本地存储数据。 这些卷用于保存这些数据,这样一旦你重新启动容器,MongoDB 数据库和 Nginx 网络服务器日志等数据可能会丢失。
您完整的 docker-compose.yml
文件将如下所示:
码头工人-compose.yml
version: '3' services: flask: build: context: app dockerfile: Dockerfile container_name: flask image: digitalocean.com/flask-python:3.6 restart: unless-stopped environment: APP_ENV: "prod" APP_DEBUG: "False" APP_PORT: 5000 MONGODB_DATABASE: flaskdb MONGODB_USERNAME: flaskuser MONGODB_PASSWORD: your_mongodb_password MONGODB_HOSTNAME: mongodb volumes: - appdata:/var/www depends_on: - mongodb networks: - frontend - backend mongodb: image: mongo:4.0.8 container_name: mongodb restart: unless-stopped command: mongod --auth environment: MONGO_INITDB_ROOT_USERNAME: mongodbuser MONGO_INITDB_ROOT_PASSWORD: your_mongodb_root_password MONGO_INITDB_DATABASE: flaskdb MONGODB_DATA_DIR: /data/db MONDODB_LOG_DIR: /dev/null volumes: - mongodbdata:/data/db networks: - backend webserver: build: context: nginx dockerfile: Dockerfile image: digitalocean.com/webserver:latest container_name: webserver restart: unless-stopped environment: APP_ENV: "prod" APP_NAME: "webserver" APP_DEBUG: "true" SERVICE_NAME: "webserver" ports: - "80:80" - "443:443" volumes: - nginxdata:/var/log/nginx depends_on: - flask networks: - frontend networks: frontend: driver: bridge backend: driver: bridge volumes: mongodbdata: driver: local appdata: driver: local nginxdata: driver: local
验证您的配置后,保存文件并退出编辑器。
您已经在 docker-compose.yml
文件中为整个应用程序堆栈定义了 Docker 配置。 您现在将继续为 Flask 和 Web 服务器编写 Dockerfile。
第 2 步 — 编写 Flask 和 Web 服务器 Dockerfile
使用 Docker,您可以构建容器以从名为 Dockerfile 的文件运行应用程序。 Dockerfile 是一种工具,可让您创建自定义映像,您可以使用这些映像安装应用程序所需的软件并根据您的要求配置容器。 您可以将您创建的自定义镜像推送到 Docker Hub 或任何私有注册表。
在这一步中,您将为 Flask 和 Web 服务器服务编写 Dockerfile。 首先,为您的 Flask 应用程序创建 app
目录:
mkdir app
接下来,在 app
目录中为您的 Flask 应用程序创建 Dockerfile
:
nano app/Dockerfile
将以下代码添加到文件中以自定义您的 Flask 容器:
应用程序/Dockerfile
FROM python:3.6.8-alpine3.9 LABEL MAINTAINER="FirstName LastName <example@domain.com>" ENV GROUP_ID=1000 \ USER_ID=1000 WORKDIR /var/www/
在此 Dockerfile
中,您将在 3.6.8-alpine3.9 映像 之上创建一个映像,该映像基于预装 Python 3.6.8 的 Alpine 3.9。
ENV
指令用于为我们的组和用户 ID 定义环境变量。 Linux Standard Base (LSB) 指定 UID 和 GID 0-99 由系统静态分配。 UID 100-999 应该为系统用户和组动态分配。 UID 1000-59999 应该为用户帐户动态分配。 记住这一点,您可以安全地分配 1000
的 UID 和 GID,此外,您可以通过更新 GROUP_ID
和 USER_ID
来更改 UID/GID 以满足您的要求。
WORKDIR
指令定义了容器的工作目录。 请务必将 LABEL MAINTAINER
字段替换为您的姓名和电子邮件地址。
添加以下代码块以将 Flask 应用程序复制到容器中并安装必要的依赖项:
应用程序/Dockerfile
. . . ADD ./requirements.txt /var/www/requirements.txt RUN pip install -r requirements.txt ADD . /var/www/ RUN pip install gunicorn
以下代码将使用 ADD
指令将文件从本地 app
目录复制到容器上的 /var/www
目录。 接下来,Dockerfile 将使用 RUN
指令安装 Gunicorn 和在 requirements.txt
文件中指定的包,您将在本教程后面创建。
以下代码块添加新用户和组并初始化应用程序:
应用程序/Dockerfile
. . . RUN addgroup -g $GROUP_ID www RUN adduser -D -u $USER_ID -G www www -s /bin/sh USER www EXPOSE 5000 CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]
默认情况下,Docker 容器以 root 用户身份运行。 root 用户可以访问系统中的所有内容,因此安全漏洞的影响可能是灾难性的。 为了减轻这种安全风险,这将创建一个只能访问 /var/www
目录的新用户和组。
此代码将首先使用 addgroup
命令创建一个名为 www
的新组。 -g
标志将组 ID 设置为之前在 Dockerfile
中定义的 ENV GROUP_ID=1000
变量。
adduser -D -u $USER_ID -G www www -s /bin/sh
行创建一个 www
用户,用户 ID 为 1000
,由 ENV
变量定义。 -s
标志创建用户的主目录(如果不存在)并将默认登录 shell 设置为 /bin/sh
。 -G
标志用于将用户的初始登录组设置为 www
,它是由前面的命令创建的。
USER
命令定义容器中运行的程序将使用 www
用户。 Gunicorn 将监听 :5000
,因此您将使用 EXPOSE
命令打开此端口。
最后,CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]
行运行命令以启动 Gunicorn 服务器,其中有四个工作人员在端口 5000
上侦听。 服务器中每个核心的数量通常应该在 2-4 个工人之间,Gunicorn 文档建议 (2 x $num_cores) + 1 作为开始的工人数量。
您完成的 Dockerfile
将如下所示:
应用程序/Dockerfile
FROM python:3.6.8-alpine3.9 LABEL MAINTAINER="FirstName LastName <example@domain.com>" ENV GROUP_ID=1000 \ USER_ID=1000 WORKDIR /var/www/ ADD . /var/www/ RUN pip install -r requirements.txt RUN pip install gunicorn RUN addgroup -g $GROUP_ID www RUN adduser -D -u $USER_ID -G www www -s /bin/sh USER www EXPOSE 5000 CMD [ "gunicorn", "-w", "4", "--bind", "0.0.0.0:5000", "wsgi"]
保存文件并退出文本编辑器。
接下来,创建一个新目录来保存您的 Nginx 配置:
mkdir nginx
然后在 nginx
目录中为您的 Nginx Web 服务器创建 Dockerfile
:
nano nginx/Dockerfile
将以下代码添加到文件中以创建将为 Nginx 容器构建映像的 Dockerfile:
nginx/Dockerfile
FROM alpine:latest LABEL MAINTAINER="FirstName LastName <example@domain.com>" RUN apk --update add nginx && \ ln -sf /dev/stdout /var/log/nginx/access.log && \ ln -sf /dev/stderr /var/log/nginx/error.log && \ mkdir /etc/nginx/sites-enabled/ && \ mkdir -p /run/nginx && \ rm -rf /etc/nginx/conf.d/default.conf && \ rm -rf /var/cache/apk/* COPY conf.d/app.conf /etc/nginx/conf.d/app.conf EXPOSE 80 443 CMD ["nginx", "-g", "daemon off;"]
这个 Nginx Dockerfile
使用 alpine
基础镜像,这是一个小型 Linux 发行版,具有最小的攻击面,旨在确保安全。
在 RUN
指令中,您正在安装 nginx
以及创建符号链接以发布错误和访问日志到标准错误 (/dev/stderr
) 和输出 ([ X178X])。 将错误发布到标准错误和输出是最佳实践,因为容器是短暂的,这样做会将日志发送到 docker logs,然后您可以从那里将日志转发到 Elastic 堆栈等日志服务以实现持久性。 完成此操作后,运行命令以删除 default.conf
和 /var/cache/apk/*
以减小生成图像的大小。 在单个 RUN
中执行所有这些命令会减少图像中的层数,这也会减小生成图像的大小。
COPY
指令复制容器内的 app.conf
Web 服务器配置。 EXPOSE
指令确保容器侦听端口 :80
和 :443
,因为您的应用程序将在 :80
上运行,并且 :443
作为安全港口。
最后,CMD
指令定义了启动 Nginx 服务器的命令。
保存文件并退出文本编辑器。
现在 Dockerfile
已准备就绪,您可以配置 Nginx 反向代理以将流量路由到 Flask 应用程序。
第 3 步 — 配置 Nginx 反向代理
在这一步中,您将把 Nginx 配置为反向代理,将请求转发到 :5000
上的 Gunicorn。 反向代理服务器用于将客户端请求定向到适当的后端服务器。 它提供了额外的抽象层和控制层,以确保客户端和服务器之间的网络流量顺畅流动。
通过创建 nginx/conf.d
目录开始:
mkdir nginx/conf.d
配置Nginx,需要在nginx/conf.d/
文件夹下创建一个app.conf
文件,配置如下。 app.conf
文件包含反向代理将请求转发给 Gunicorn 所需的配置。
nano nginx/conf.d/app.conf
将以下内容放入app.conf
文件中:
nginx/conf.d/app.conf
upstream app_server { server flask:5000; } server { listen 80; server_name _; error_log /var/log/nginx/error.log; access_log /var/log/nginx/access.log; client_max_body_size 64M; location / { try_files $uri @proxy_to_app; } location @proxy_to_app { gzip_static on; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header Host $http_host; proxy_buffering off; proxy_redirect off; proxy_pass http://app_server; } }
这将首先定义上游服务器,它通常用于指定用于路由或负载平衡的Web或应用服务器。
您的上游服务器 app_server
使用 server
指令定义服务器地址,该指令由容器名称 flask:5000
标识。
Nginx Web 服务器的配置在 server
块中定义。 listen
指令定义您的服务器将侦听传入请求的端口号。 error_log
和 access_log
指令定义用于写入日志的文件。 proxy_pass
指令用于设置上游服务器将请求转发到 http://app_server
。
保存并关闭文件。
配置好 Nginx Web 服务器后,您可以继续创建 Flask 待办事项 API。
第 4 步——创建 Flask To-do API
现在您已经构建了您的环境,您已经准备好构建您的应用程序了。 在这一步中,您将编写一个待办事项 API 应用程序,该应用程序将保存和显示从 POST 请求发送的待办事项。
通过在 app
目录中创建 requirements.txt
文件开始:
nano app/requirements.txt
此文件用于安装应用程序的依赖项。 本教程的实现将使用 Flask、Flask-PyMongo 和 requests。 将以下内容添加到 requirements.txt
文件中:
应用程序/requirements.txt
Flask==1.0.2 Flask-PyMongo==2.2.0 requests==2.20.1
输入需求后保存文件并退出编辑器。
接下来,创建 app.py
文件以包含 app
目录中的 Flask 应用程序代码:
nano app/app.py
在新的 app.py
文件中,输入代码以导入依赖项:
应用程序/应用程序.py
import os from flask import Flask, request, jsonify from flask_pymongo import PyMongo
os
包用于导入环境变量。 从 flask
库中,您导入了 Flask
、request
和 jsonify
对象以分别实例化应用程序、处理请求和发送 JSON 响应。 从 flask_pymongo
您导入了 PyMongo
对象以与 MongoDB 交互。
接下来,添加连接 MongoDB 所需的代码:
应用程序/应用程序.py
. . . application = Flask(__name__) application.config["MONGO_URI"] = 'mongodb://' + os.environ['MONGODB_USERNAME'] + ':' + os.environ['MONGODB_PASSWORD'] + '@' + os.environ['MONGODB_HOSTNAME'] + ':27017/' + os.environ['MONGODB_DATABASE'] mongo = PyMongo(application) db = mongo.db
Flask(__name__)
将应用程序对象加载到 application
变量中。 接下来,代码使用 os.environ
从环境变量构建 MongoDB 连接字符串。 将 application
对象传递给 PyMongo()
方法将为您提供 mongo
对象,从而为您提供来自 [X146X 的 db
对象]。
现在您将添加代码以创建索引消息:
应用程序/应用程序.py
. . . @application.route('/') def index(): return jsonify( status=True, message='Welcome to the Dockerized Flask MongoDB app!' )
@application.route('/')
定义了 API 的 /
GET 路由。 在这里,您的 index()
函数使用 jsonify
方法返回一个 JSON 字符串。
接下来,添加 /todo
路由以列出所有待办事项:
应用程序/应用程序.py
. . . @application.route('/todo') def todo(): _todos = db.todo.find() item = {} data = [] for todo in _todos: item = { 'id': str(todo['_id']), 'todo': todo['todo'] } data.append(item) return jsonify( status=True, data=data )
@application.route('/todo')
定义了 API 的 /todo
GET 路由,它返回数据库中的待办事项。 db.todo.find()
方法返回数据库中的所有待办事项。 接下来,您遍历 _todos
以构建一个 item
,其中仅包含来自将它们附加到 data
的对象的 id
和 todo
] 数组,最后将它们作为 JSON 返回。
接下来,添加用于创建待办事项的代码:
应用程序/应用程序.py
. . . @application.route('/todo', methods=['POST']) def createTodo(): data = request.get_json(force=True) item = { 'todo': data['todo'] } db.todo.insert_one(item) return jsonify( status=True, message='To-do saved successfully!' ), 201
@application.route('/todo')
定义了 API 的 /todo
POST 路由,它在数据库中创建一个待办事项。 request.get_json(force=True)
获取您发布到路由的 JSON,而 item
用于构建将保存在待办事项中的 JSON。 db.todo.insert_one(item)
用于将一项插入数据库。 待办事项保存在数据库中后,您将返回一个带有 201 CREATED
状态代码的 JSON 响应。
现在添加代码来运行应用程序:
应用程序/应用程序.py
. . . if __name__ == "__main__": ENVIRONMENT_DEBUG = os.environ.get("APP_DEBUG", True) ENVIRONMENT_PORT = os.environ.get("APP_PORT", 5000) application.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)
条件 __name__ == "__main__"
用于检查模块中的全局变量 __name__
是否是程序的入口点,是否为 "__main__"
,然后运行应用程序。 如果 __name__
等于 "__main__"
,则 if
块内的代码将使用此命令 application.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)
执行应用程序。
接下来,我们使用 os.environ.get()
从环境变量中获取 ENVIRONMENT_DEBUG
和 ENVIRONMENT_PORT
的值,使用键作为第一个参数,默认值作为第二个参数。 application.run()
设置应用程序的 host
、port
和 debug
值。
完成的 app.py
文件将如下所示:
应用程序/应用程序.py
import os from flask import Flask, request, jsonify from flask_pymongo import PyMongo application = Flask(__name__) application.config["MONGO_URI"] = 'mongodb://' + os.environ['MONGODB_USERNAME'] + ':' + os.environ['MONGODB_PASSWORD'] + '@' + os.environ['MONGODB_HOSTNAME'] + ':27017/' + os.environ['MONGODB_DATABASE'] mongo = PyMongo(application) db = mongo.db @application.route('/') def index(): return jsonify( status=True, message='Welcome to the Dockerized Flask MongoDB app!' ) @application.route('/todo') def todo(): _todos = db.todo.find() item = {} data = [] for todo in _todos: item = { 'id': str(todo['_id']), 'todo': todo['todo'] } data.append(item) return jsonify( status=True, data=data ) @application.route('/todo', methods=['POST']) def createTodo(): data = request.get_json(force=True) item = { 'todo': data['todo'] } db.todo.insert_one(item) return jsonify( status=True, message='To-do saved successfully!' ), 201 if __name__ == "__main__": ENVIRONMENT_DEBUG = os.environ.get("APP_DEBUG", True) ENVIRONMENT_PORT = os.environ.get("APP_PORT", 5000) application.run(host='0.0.0.0', port=ENVIRONMENT_PORT, debug=ENVIRONMENT_DEBUG)
保存文件并退出编辑器。
接下来,在app
目录下创建wsgi.py
文件。
nano app/wsgi.py
wsgi.py
文件创建一个应用程序对象(或可调用对象),以便服务器可以使用它。 每次请求到来时,服务器都会使用此应用程序对象在解析 URL 时运行应用程序的请求处理程序。
将以下内容放入wsgi.py
文件中,保存文件,退出文本编辑器:
应用程序/wsgi.py
from app import application if __name__ == "__main__": application.run()
这个 wsgi.py
文件从 app.py
文件中导入应用程序对象,并为 Gunicorn 服务器创建一个应用程序对象。
待办事项应用程序现已到位,因此您已准备好开始在容器中运行应用程序。
第 5 步 - 构建和运行容器
现在您已经在 docker-compose.yml
文件中定义了所有服务及其配置,您可以启动容器了。
由于服务是在单个文件中定义的,因此您需要发出单个命令来启动容器、创建卷和设置网络。 此命令还为您的 Flask 应用程序和 Nginx Web 服务器构建映像。 运行以下命令来构建容器:
docker-compose up -d
第一次运行该命令时,它将下载所有必要的 Docker 映像,这可能需要一些时间。 一旦图像被下载并存储在您的本地机器中,docker-compose
将创建您的容器。 -d
标志守护进程,允许它作为后台进程运行。
构建过程完成后,使用以下命令列出正在运行的容器:
docker ps
您将看到类似于以下内容的输出:
OutputCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES f20e9a7fd2b9 digitalocean.com/webserver:latest "nginx -g 'daemon of…" 2 weeks ago Up 2 weeks 0.0.0.0:80->80/tcp, 0.0.0.0:443->443/tcp webserver 3d53ea054517 digitalocean.com/flask-python:3.6 "gunicorn -w 4 --bin…" 2 weeks ago Up 2 weeks 5000/tcp flask 96f5a91fc0db mongo:4.0.8 "docker-entrypoint.s…" 2 weeks ago Up 2 weeks 27017/tcp mongodb
CONTAINER ID
是用于访问容器的唯一标识符。 IMAGE
定义给定容器的图像名称。 NAMES
字段是创建容器的服务名称,类似于 CONTAINER ID
可用于访问容器。 最后,STATUS
提供有关容器状态的信息,无论是运行、重新启动还是停止。
您已使用 docker-compose
命令从配置文件构建容器。 在下一步中,您将为您的应用程序创建一个 MongoDB 用户。
第 6 步 — 为您的 MongoDB 数据库创建用户
默认情况下,MongoDB 允许用户在没有凭据的情况下登录并授予无限权限。 在这一步中,您将通过创建一个专用用户来访问它来保护您的 MongoDB 数据库。
为此,您需要在 docker-compose.yml
文件环境变量 MONGO_INITDB_ROOT_USERNAME
和 MONGO_INITDB_ROOT_PASSWORD
中为 mongodb
服务设置的 root 用户名和密码。 通常,在与数据库交互时最好避免使用 root 管理帐户。 相反,您将为 Flask 应用程序创建一个专用的数据库用户,以及一个允许 Flask 应用程序访问的新数据库。
要创建一个新用户,首先在 mongodb
容器上启动一个交互式 shell:
docker exec -it mongodb bash
您可以使用 docker exec
命令在正在运行的容器内运行命令,并使用 -it
标志在容器内运行交互式 shell。
进入容器后,登录 MongoDB root 管理帐户:
mongo -u mongodbuser -p
系统将提示您输入作为 docker-compose.yml
文件中 MONGO_INITDB_ROOT_PASSWORD
变量值的密码。 可以通过在 mongodb
服务中为 MONGO_INITDB_ROOT_PASSWORD
设置新值来更改密码,在这种情况下,您必须重新运行 docker-compose up -d
命令。
运行 show dbs;
命令列出所有数据库:
show dbs;
您将看到以下输出:
Outputadmin 0.000GB config 0.000GB local 0.000GB 5 rows in set (0.00 sec)
admin
数据库是一个特殊的数据库,它授予用户管理权限。 如果用户对 admin
数据库具有读取权限,则他们将对所有其他数据库具有读取和写入权限。 由于输出列出了 admin
数据库,因此用户可以访问该数据库,因此可以读取和写入所有其他数据库。
保存第一个待办事项会自动创建MongoDB数据库。 MongoDB 允许您使用 use database
命令切换到不存在的数据库。 当文档保存到集合时,它会创建一个数据库。 因此这里没有创建数据库; 当您通过 API 在数据库中保存第一个待办事项时,就会发生这种情况。 执行use
命令切换到flaskdb
数据库:
use flaskdb
接下来,创建一个允许访问此数据库的新用户:
db.createUser({user: 'flaskuser', pwd: 'your password', roles: [{role: 'readWrite', db: 'flaskdb'}]}) exit
此命令创建一个名为 flaskuser 的用户,该用户具有 readWrite
访问 flaskdb
数据库的权限。 请务必在 pwd
字段中使用安全密码。 这里的 user
和 pwd
是您在 docker-compose.yml
文件中为 flask
服务的环境变量部分下定义的值。
使用以下命令登录到经过身份验证的数据库:
mongo -u flaskuser -p your password --authenticationDatabase flaskdb
现在您已经添加了用户,请退出数据库。
exit
最后,退出容器:
exit
您现在已经为您的 Flask 应用程序配置了一个专用的数据库和用户帐户。 数据库组件已准备就绪,现在您可以继续运行 Flask 待办事项应用程序。
第 7 步 — 运行 Flask 待办事项应用程序
现在您的服务已配置并正在运行,您可以通过在浏览器中导航到 http://your_server_ip
来测试您的应用程序。 此外,您可以运行 curl
来查看来自 Flask 的 JSON 响应:
curl -i http://your_server_ip
您将收到以下回复:
Output{"message":"Welcome to the Dockerized Flask MongoDB app!","status":true}
Flask 应用程序的配置从 docker-compose.yml
文件传递给应用程序。 使用 flask
服务的 environment
部分中定义的 MONGODB_*
变量设置有关数据库连接的配置。
要测试所有内容,请使用 Flask API 创建待办事项。 您可以通过对 /todo
路由的 POST
curl 请求来执行此操作:
curl -i -H "Content-Type: application/json" -X POST -d '{"todo": "Dockerize Flask application with MongoDB backend"}' http://your_server_ip/todo
当待办事项保存到 MongoDB 时,此请求会导致状态码为 201 CREATED
的响应:
Output{"message":"To-do saved successfully!","status":true}
您可以通过对 /todo
路由的 GET 请求列出来自 MongoDB 的所有待办事项:
curl -i http://your_server_ip/todo
Output{"data":[{"id":"5c9fa25591cb7b000a180b60","todo":"Dockerize Flask application with MongoDB backend"}],"status":true}
有了这个,你已经将一个运行 MongoDB 后端的 Flask API Docker 化,并将 Nginx 作为部署到服务器的反向代理。 对于生产环境,您可以使用 sudo systemctl enable docker
来确保您的 Docker 服务在运行时自动启动。
结论
在本教程中,您使用 Docker、MongoDB、Nginx 和 Gunicorn 部署了一个 Flask 应用程序。 您现在拥有一个功能强大且可扩展的现代无状态 API 应用程序。 虽然您可以通过使用 docker container run
之类的命令来实现此结果,但 docker-compose.yml
可以简化您的工作,因为可以将此堆栈放入版本控制中并根据需要进行更新。
从这里您还可以查看我们进一步的 Python 框架教程 。