在本教程中,我们将看看如何用Postgres和Docker来设置Flask。对于生产环境,我们将添加Gunicorn、Traefik和Let's Encrypt。
项目设置
首先创建一个项目目录。
请随意将virtualenv和Pip换成Poetry或Pipenv。更多信息,请查看现代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文件_。
因此,我们从一个基于slim 的Docker镜像开始,用于Python 3.9.5。然后我们设置了一个工作目录和两个环境变量。
PYTHONDONTWRITEBYTECODE:防止Python将pyc文件写入磁盘(相当于python -B选项)。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-SQLAlchemy和Psycopg2。
再次更新___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。
- 反向代理和负载平衡器
- 通过Let's Encrypt,自动签发和更新SSL证书,开箱即用。
- 将Traefik用于简单的、基于Docker的微服务。
Nginx。
- 网络服务器、反向代理和负载平衡器
- 比Traefik稍快一些
- 将Nginx用于复杂的服务
在 "services "目录下添加一个名为 "traefik "的新文件夹,以及以下文件。
你的项目结构现在应该是这样的。
在_traefik.dev.toml_中添加以下内容。
在这里,由于我们不想暴露db 服务,我们将exposedByDefault设置为false 。要手动暴露一个服务,我们可以在Docker Compose文件中添加"traefik.enable=true" 的标签。
接下来,更新_docker-compose.yml_文件,使我们的web 服务被Traefik发现,并添加一个新的traefik 服务。
首先,web 服务只暴露给端口为5000 的其他容器。我们还在web 服务上添加了以下标签。
traefik.enable=true使得Traefik能够发现该服务traefik.http.routers.flask.rule=Host(`flask.localhost`)当请求有Host=flask.localhost,请求会被重定向到这个服务上
注意到traefik 服务内的卷。
./services/traefik/traefik.dev.toml:/etc/traefik/traefik.toml将本地配置文件映射到容器中的配置文件,这样设置就会保持同步/var/run/docker.sock:/var/run/docker.sock:ro,使Traefik能够发现其他容器。
为了测试,首先要关闭所有现有的容器。
建立新的开发镜像并启动容器。
创建表格并应用种子。
导航到flask.localhost。你应该看到。
你也可以通过cURL进行测试。
接下来,在flask.localhost:8081,检查仪表板 。

完成后将容器和卷关闭。
让我们加密
我们已经成功创建了一个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)用你的实际电子邮件地址。
这里发生了什么。
entryPoints.web将我们不安全的 HTTP 应用程序的入口点设置为 80 端口entryPoints.websecure将我们的安全 HTTPS 应用程序的入口点设置为端口 443entryPoints.web.http.redirections.entryPoint将所有不安全的请求重定向到安全端口exposedByDefault = false取消所有的服务dashboard = true启用监控仪表板
最后,请注意。
这里是Let's Encrypt配置的位置。我们定义了证书的存储位置以及验证类型,也就是HTTP挑战。
接下来,假设你更新了域名的DNS记录,创建两个新的A记录,都指向你的计算实例的公共IP。
flask-traefik.your-domain.com- 用于网络服务dashboard-flask-traefik.your-domain.com- 用于 Traefik 仪表板
确保用你的实际域名替换
your-domain.com。
接下来,像这样更新_docker-compose.prod.yml_。
同样,确保用你的实际域名替换
your-domain.com。
这里有什么新内容?
在web 服务中,我们添加了以下标签。
traefik.http.routers.flask.rule=Host(`flask-traefik.your-domain.com`)将主机改为实际域名traefik.http.routers.flask.tls=true启用HTTPStraefik.http.routers.flask.tls.certresolver=letsencrypt设置证书颁发者为Let's Encrypt
接下来,对于traefik 服务,我们添加了适当的端口和一个用于证书目录的卷。这个卷可以确保即使容器被关闭,证书也能持续存在。
至于标签。
traefik.http.routers.dashboard.rule=Host(`dashboard-flask-traefik.your-domain.com`)定义了仪表盘主机,所以它可以被访问到$Host/dashboard/traefik.http.routers.dashboard.tls=true启用HTTPStraefik.http.routers.dashboard.tls.certresolver=letsencrypt设置证书解析器为Let's Encrypttraefik.http.routers.dashboard.middlewares=auth启用HTTP BasicAuth中间件traefik.http.middlewares.auth.basicauth.users定义登录用的用户名和散列密码
你可以使用htpasswd工具创建一个新的密码哈希值。
随意使用env_file ,将用户名和密码存储为环境变量
更新_Dockerfile.traefik_。
接下来,旋转新的容器。
创建表并应用种子。
确保这两个URL是有效的。
另外,确保当你访问上述URL的HTTP版本时,你会被重定向到HTTPS版本。
最后,Let's Encrypt证书的有效期为90天。Treafik会在幕后自动为你处理证书的更新,这样你就可以少操心一件事了
总结
在本教程中,我们介绍了如何将一个带有Postgres的Flask应用进行容器化开发。我们还创建了一个可用于生产的Docker Compose文件,设置了Traefik和Let's Encrypt以通过HTTPS为应用程序提供服务,并启用了一个安全仪表板来监控我们的服务。
在实际部署到生产环境方面,你可能想使用一个。
你可以在flask-docker-traefikrepo中找到这些代码。