用Postgres、Gunicorn和Traefik实现Flask的Docker化

在本教程中,我们将看看如何用Postgres和Docker来设置Flask。对于生产环境,我们将添加Gunicorn、Traefik和Let’s Encrypt。

项目设置

首先创建一个项目目录。

复制代码

请随意将virtualenv和Pip换成PoetryPipenv。更多信息,请查看现代Python环境

然后,创建以下文件和文件夹。

复制代码

Flask添加到_requirements.txt_中。

从 “services/web “中安装该软件包。

复制代码

接下来,让我们在___init.py___中创建一个简单的Flask应用程序。

复制代码

然后,为了配置Flask CLI工具,以便从命令行运行和管理应用程序,在_services/web/manage.py_中添加以下内容。

复制代码

在这里,我们创建了一个新的FlaskGroup 实例,用与Flask应用相关的命令来扩展正常的CLI。

从 “web “目录中运行服务器。

复制代码

导航到127.0.0.1:5000,你应该看到。

完成后关闭服务器。从虚拟环境中退出,并把它也删除。

Docker

安装Docker,如果你还没有的话,然后在 “web “目录中添加一个_Docker文件_。

复制代码

因此,我们从一个基于slimDocker镜像开始,用于Python 3.9.5。然后我们设置了一个工作目录和两个环境变量。

  1. PYTHONDONTWRITEBYTECODE :防止Python将pyc文件写入磁盘(相当于python -B 选项)。
  2. PYTHONUNBUFFERED: 防止Python对stdout和stder进行缓冲(相当于python -u 选项)

最后,我们复制了_requirements.txt_文件,安装了依赖项,并复制了Flask应用本身。

回顾一下Docker for Python Developers,了解更多关于结构化Docker文件的信息,以及为基于Python的开发配置Docker的一些最佳实践。

接下来,在项目根目录下添加一个_docker-compose.yml_文件。

复制代码

查看Compose文件的参考信息,了解该文件的工作原理。

构建镜像。

一旦镜像构建完成,运行容器。

导航到http://127.0.0.1:5000/,再次查看hello world的正确性检查。

如果没有成功,请通过docker-compose logs -f ,检查日志中是否有错误。

为了配置Postgres,我们需要在_docker-compose.yml_文件中添加一个新服务,设置Flask-SQLAlchemy,并安装Psycopg2

首先,在_docker-compose.yml_中添加一个名为db 的新服务。

复制代码

为了使数据在容器的生命周期后仍能持续,我们配置了一个卷。这个配置将把postgres_data 绑定到容器中的”/var/lib/postgresql/data/”目录。

我们还添加了一个环境键来定义默认数据库的名称,并设置了一个用户名和密码。

查看Postgres Docker Hub页面的 “环境变量 “部分以了解更多信息。

请注意web 服务中的新命令。

复制代码

while !</dev/tcp/db/5432; do sleep 1 将持续到Postgres启动。一旦启动,python manage.py run -h 0.0.0.0 运行。

然后,在 “项目 “目录下添加一个名为_config.py_的新文件,我们将在这里定义特定环境的配置变量。

复制代码

在这里,数据库是根据我们刚刚定义的DATABASE_URL 环境变量来配置的。请注意默认值。

更新___init__.py_以在启动时拉入配置。

复制代码

在_requirements.txt_中加入Flask-SQLAlchemyPsycopg2

复制代码

再次更新___init__.py_以创建一个新的SQLAlchemy 实例并定义一个数据库模型。

复制代码

在数据库模型上使用dataclass装饰器有助于我们序列化数据库对象。

最后,更新_manage.py_。

复制代码

这将在CLI中注册一个新的命令,create_db ,这样我们就可以在命令行中运行它,我们很快就会用它来将模型应用到数据库中。

建立新的镜像并启动两个容器。

复制代码

创建表。

复制代码

得到以下错误?

复制代码

运行docker-compose down -v ,把卷和容器一起删除。然后,重新构建镜像,运行容器,并应用迁移。

确保users 表被创建。

复制代码

你可以通过运行来检查卷是否也被创建了。

复制代码

你应该看到类似的内容。

复制代码

导航到http://127.0.0.1:5000。理智检查显示一个空的列表。这是因为我们还没有填充users 表。让我们在_manage.py_中添加一个CLI种子命令,将样本users 添加到用户表中。

复制代码

试试吧。

复制代码

再次导航到http://127.0.0.1:5000。你现在应该看到。

Gunicorn

继续,对于生产环境,让我们在需求文件中添加Gunicorn,一个生产级的WSGI服务器。

复制代码

因为我们仍然想在开发中使用Flask的内置服务器,所以在项目根目录下创建一个新的compose文件,名为_docker-compose.prod.yml_,用于生产。

复制代码

如果你有多个环境,你可能想看看使用docker-compose.override.yml配置文件。使用这种方法,你会把你的基本配置添加到_docker-compose.yml_文件中,然后使用_docker-compose.override.yml_文件来根据环境覆盖这些配置设置。

请注意默认的command 。我们正在运行Gunicorn而不是Flask开发服务器。我们还从web 服务中移除卷,因为我们在生产中不需要它。

将开发容器(以及使用-v标志的相关卷)关闭

然后,建立生产镜像并启动容器。

复制代码

创建表并应用种子。

复制代码

验证hello_flask_prod 数据库和users 表一起被创建。测试一下http://127.0.0.1:5000/

同样,如果容器不能启动,通过docker-compose -f docker-compose.prod.yml logs -f ,检查日志中的错误。

生产Docker文件

在 “web “目录下创建一个新的Dockerfile,叫做_Dockerfile.prod_,用于生产构建。

复制代码

在这里,我们使用了Docker多阶段构建来减少最终镜像的大小。基本上,builder 是一个临时镜像,用于构建Python轮子。然后,这些轮子被复制到最终的生产镜像上,而builder 镜像被丢弃。

你可以进一步采用多阶段构建方法,使用一个Docker文件,而不是创建两个Docker文件。想想使用这种方法比使用两个不同的文件有什么利弊。

你注意到我们创建了一个非root用户吗?默认情况下,Docker在容器内以root身份运行容器进程。这是一个不好的做法,因为攻击者如果设法突破容器,就可以获得对Docker主机的root权限。如果你在容器中是root,你在主机上也是root。

更新docker-compose.prod.yml文件中的web 服务,用_Dockerfile.prod_构建。

复制代码

试试吧。

复制代码

Traefik

接下来,让我们把Traefik,一个反向代理,加入到这个组合中。

初次接触Traefik?请看正式的入门指南。

Traefik vs Nginx:Traefik是一个现代的HTTP反向代理和负载平衡器。它经常被比作Nginx,一个网络服务器和反向代理。由于Nginx主要是一个网络服务器,它可以用来提供一个网页,也可以作为一个反向代理和负载平衡器。一般来说,Traefik更容易启动和运行,而Nginx的功能更全面。

Traefik

  1. 反向代理和负载平衡器
  2. 通过Let’s Encrypt,自动签发和更新SSL证书,开箱即用。
  3. 将Traefik用于简单的、基于Docker的微服务。

Nginx

  1. 网络服务器、反向代理和负载平衡器
  2. 比Traefik稍快一些
  3. 将Nginx用于复杂的服务

在 “services “目录下添加一个名为 “traefik “的新文件夹,以及以下文件。

复制代码

你的项目结构现在应该是这样的。

复制代码

在_traefik.dev.toml_中添加以下内容。

复制代码

在这里,由于我们不想暴露db 服务,我们将exposedByDefault设置为false 。要手动暴露一个服务,我们可以在Docker Compose文件中添加"traefik.enable=true" 的标签。

接下来,更新_docker-compose.yml_文件,使我们的web 服务被Traefik发现,并添加一个新的traefik 服务。

复制代码

首先,web 服务只暴露给端口为5000 的其他容器。我们还在web 服务上添加了以下标签。

  1. traefik.enable=true 使得Traefik能够发现该服务
  2. traefik.http.routers.flask.rule=Host(`flask.localhost`) 当请求有Host=flask.localhost ,请求会被重定向到这个服务上

注意到traefik 服务内的卷。

  1. ./services/traefik/traefik.dev.toml:/etc/traefik/traefik.toml 将本地配置文件映射到容器中的配置文件,这样设置就会保持同步
  2. /var/run/docker.sock:/var/run/docker.sock:ro ,使Traefik能够发现其他容器。

为了测试,首先要关闭所有现有的容器。

复制代码

建立新的开发镜像并启动容器。

复制代码

创建表格并应用种子。

复制代码

导航到flask.localhost。你应该看到。

你也可以通过cURL进行测试。

复制代码

接下来,在flask.localhost:8081,检查仪表板

traefik dashboard

完成后将容器和卷关闭。

让我们加密

我们已经成功创建了一个Flask、Docker和Traefik在开发模式下的工作实例。对于生产来说,你需要配置Traefik来通过Let’s Encrypt管理TLS证书。简而言之,Traefik会自动联系证书颁发机构来颁发和更新证书。

由于Let’s Encrypt不会为localhost ,你需要在云计算实例(如DigitalOceandroplet或AWS EC2实例)上旋转你的生产容器。你还需要一个有效的域名。如果你没有,你可以在Freenom创建一个免费域名。

我们使用DigitalOcean液滴和Docker机器来快速配置Docker的计算实例,并部署生产容器来测试Traefik的配置。查看Docker文档中的DigitalOcean例子,了解更多关于使用Docker机器配置液滴的信息。

假设你配置了一个计算实例并设置了一个免费域名,你现在就可以在生产模式下设置Traefik了。

首先,将生产版本的Traefik配置添加到_traefik.prod.toml_。

复制代码

请确保将 [[email protected]](https://testdriven.io/cdn-cgi/l/email-protection)用你的实际电子邮件地址。

这里发生了什么。

  1. entryPoints.web 将我们不安全的 HTTP 应用程序的入口点设置为 80 端口
  2. entryPoints.websecure 将我们的安全 HTTPS 应用程序的入口点设置为端口 443
  3. entryPoints.web.http.redirections.entryPoint 将所有不安全的请求重定向到安全端口
  4. exposedByDefault = false 取消所有的服务
  5. dashboard = true 启用监控仪表板

最后,请注意。

复制代码

这里是Let’s Encrypt配置的位置。我们定义了证书的存储位置以及验证类型,也就是HTTP挑战

接下来,假设你更新了域名的DNS记录,创建两个新的A记录,都指向你的计算实例的公共IP。

  1. flask-traefik.your-domain.com – 用于网络服务
  2. dashboard-flask-traefik.your-domain.com – 用于 Traefik 仪表板

确保用你的实际域名替换your-domain.com

接下来,像这样更新_docker-compose.prod.yml_。

复制代码

同样,确保用你的实际域名替换your-domain.com

这里有什么新内容?

web 服务中,我们添加了以下标签。

  1. traefik.http.routers.flask.rule=Host(`flask-traefik.your-domain.com`) 将主机改为实际域名
  2. traefik.http.routers.flask.tls=true 启用HTTPS
  3. traefik.http.routers.flask.tls.certresolver=letsencrypt 设置证书颁发者为Let’s Encrypt

接下来,对于traefik 服务,我们添加了适当的端口和一个用于证书目录的卷。这个卷可以确保即使容器被关闭,证书也能持续存在。

至于标签。

  1. traefik.http.routers.dashboard.rule=Host(`dashboard-flask-traefik.your-domain.com`) 定义了仪表盘主机,所以它可以被访问到$Host/dashboard/
  2. traefik.http.routers.dashboard.tls=true 启用HTTPS
  3. traefik.http.routers.dashboard.tls.certresolver=letsencrypt 设置证书解析器为Let’s Encrypt
  4. traefik.http.routers.dashboard.middlewares=auth 启用HTTP BasicAuth 中间件
  5. traefik.http.middlewares.auth.basicauth.users 定义登录用的用户名和散列密码

你可以使用htpasswd工具创建一个新的密码哈希值。

复制代码

随意使用env_file ,将用户名和密码存储为环境变量

复制代码

更新_Dockerfile.traefik_。

复制代码

接下来,旋转新的容器。

复制代码

创建表并应用种子。

复制代码

确保这两个URL是有效的。

  1. flask-traefik.your-domain.com
  2. dashboard-flask-traefik.your-domain.com/dashboard/

另外,确保当你访问上述URL的HTTP版本时,你会被重定向到HTTPS版本。

最后,Let’s Encrypt证书的有效期为90天。Treafik会在幕后自动为你处理证书的更新,这样你就可以少操心一件事了

总结

在本教程中,我们介绍了如何将一个带有Postgres的Flask应用进行容器化开发。我们还创建了一个可用于生产的Docker Compose文件,设置了Traefik和Let’s Encrypt以通过HTTPS为应用程序提供服务,并启用了一个安全仪表板来监控我们的服务。

在实际部署到生产环境方面,你可能想使用一个。

  1. 完全管理的数据库服务–如RDSCloud SQL–而不是在容器中管理你自己的Postgres实例。
  2. 服务的非root用户

你可以在flask-docker-traefikrepo中找到这些代码。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享