作者都是各自领域经过审查的专家,并撰写他们有经验的主题. 我们所有的内容都经过同行评审,并由同一领域的Toptal专家验证.
作为一名机器学习工程师 计算机视觉专家我发现自己经常用Flask创建api甚至web应用程序. In this post, 我想分享一些技巧和有用的食谱来构建一个完整的生产就绪的Flask应用程序.
我们将讨论以下主题:
让我们从创建应用程序代码和资产开始. 请注意,我不会在这篇文章中讨论正确的Flask应用程序结构. 为了简洁和清晰,演示应用程序由最少数量的模块和包组成.
首先,创建一个目录结构并初始化一个空的Git存储库.
mkdir flask-deploy
cd flask-deploy
# init GIT repo
git init
创建文件夹结构
Mkdir静态任务模型配置
#使用pipenv安装所需的包,这将创建一个Pipfile
Pipenv安装flask-restful flask-sqlalchemy flask-migrate celery
#创建测试静态资产
echo "Hello World!" > static/hello-world.txt
接下来,我们将添加代码.
在配置模块中,我们将定义我们的小型配置管理框架. 这个想法是使应用程序的行为根据配置预先选择的 APP_ENV
environment variable, plus, 添加一个选项,以便在需要时使用特定环境变量覆盖任何配置设置.
import os
import sys
import config.settings
创建与指定env对应的设置对象
APP_ENV = os.environ.get(“APP_ENV”、“开发”)
_current = getattr(sys ..modules['config.设置”,“{0}配置”.format(APP_ENV))()
#将属性复制到模块以方便使用
For atr in [f For f in dir(_current) if not '__' in f]:
# environment可以覆盖任何内容
val = os.environ.获取(atr, getattr(_current, atr))
setattr(sys.module [__name__], atr, val)
def as_dict():
res = {}
For atr in [f For f in dir(config) if not '__' in f]:
Val = getattr(config, atr)
res[atr] = val
return res
这是一组配置类,其中一个由 APP_ENV
variable. 当应用程序运行时,代码插入 __init__.py
将实例化这些类中的一个,用特定的环境变量覆盖字段值, if they are present. 我们将在稍后初始化Flask和芹菜配置时使用最终配置对象.
class BaseConfig():
API_PREFIX = '/api'
TESTING = False
DEBUG = False
类DevConfig (BaseConfig):
FLASK_ENV = 'development'
DEBUG = True
SQLALCHEMY_DATABASE_URI = 'postgresql://db_user:db_password@db-postgres:5432/flask-deploy'
CELERY_BROKER = 'pyamqp://rabbit_user:rabbit_password@broker-rabbitmq//'
CELERY_RESULT_BACKEND = 'rpc://rabbit_user:rabbit_password@broker-rabbitmq//'
类ProductionConfig (BaseConfig):
FLASK_ENV = 'production'
SQLALCHEMY_DATABASE_URI = 'postgresql://db_user:db_password@db-postgres:5432/flask-deploy'
CELERY_BROKER = 'pyamqp://rabbit_user:rabbit_password@broker-rabbitmq//'
CELERY_RESULT_BACKEND = 'rpc://rabbit_user:rabbit_password@broker-rabbitmq//'
类TestConfig (BaseConfig):
FLASK_ENV = 'development'
TESTING = True
DEBUG = True
#让芹菜在同一进程中同步执行任务
CELERY_ALWAYS_EAGER = True
tasks包包含芹菜初始化代码. Config package, 在初始化时,已经在模块级别复制了所有设置, 用于更新芹菜配置对象,以防我们将来会有一些芹菜特定的设置,例如, 计划任务和工作超时.
从芹菜进口芹菜
import config
def make_celery():
芹菜=芹菜(__name__, broker=config).CELERY_BROKER)
celery.conf.update(config.as_dict())
return celery
芹菜= make_芹菜()
启动和初始化一个Celery worker需要这个模块, 哪一个将在一个单独的Docker容器中运行. 它初始化Flask应用程序上下文,使其能够访问与应用程序相同的环境. 如果不需要,这些线可以安全地移除.
从应用程序导入create_app
app = create_app()
app.app_context().push()
从tasks中导入芹菜
接下来是API包,它使用Flask-Restful包定义REST API. 我们的应用程序只是一个演示,将只有两个端点:
/process_data
-在一个Celery worker上启动一个虚拟的long操作,并返回一个新任务的ID./tasks/
—根据任务ID返回任务状态.import time
从flask导入jsonify
从flask_rest中导入Api
从tasks中导入芹菜
import config
api = api (prefix=config . api.API_PREFIX)
类TaskStatusAPI(资源):
Def (self, task_id):
task = celery.AsyncResult(task_id)
return jsonify(task.result)
类DataProcessingAPI(资源):
def post(self):
task = process_data.delay()
返回{'task_id':任务.id}, 200
@celery.task()
def process_data():
time.sleep(60)
#数据处理端点
api.add_resource (DataProcessingAPI / process_data)
#任务状态端点
api.add_resource(TaskStatusAPI, '/tasks/')
的SQLAlchemy模型 User
对象,以及数据库引擎初始化代码. The User
对象不会被我们的演示应用以任何有意义的方式使用, 但我们需要它来确保数据库迁移工作和SQLAlchemy-Flask集成设置正确.
import uuid
从SQLAlchemy导入SQLAlchemy
db = SQLAlchemy()
class User(db.Model):
id = db.Column(db.String(), primary_key=True, default=lambda: str(uuid . key.uuid4()))
username = db.Column(db.String())
email = db.Column(db.字符串()、独特的= True)
注意UUID是如何通过默认表达式自动生成为对象ID的.
最后,让我们创建一个主Flask应用程序文件.
从烧瓶导入烧瓶
logging.basicConfig(=日志级别.DEBUG,
格式= ' [% (asctime) s]: {} % (levelname) %(消息)年代'.format(os.getpid()),
datefmt = ' % Y - % m - H % d %: % m: % S的,
handlers=[logging.StreamHandler()])
logger = logging.getLogger()
def create_app():
logger.在{配置文件}中启动应用程序.APP_ENV}环境”)
app = Flask(__name__)
app.config.from_object(配置)
api.init_app(app)
#初始化SQLAlchemy
db.init_app(app)
# define hello world page
@app.route('/')
def hello_world():
return 'Hello, World!'
return app
如果__name__ == "__main__":
app = create_app()
app.run(host='0.0.0.0', debug=True)
Here we are:
- 以适当的格式配置基本日志,包括时间、级别和进程ID
- Defining the Flask app creation 函数的API初始化和“Hello, world .!” page
- 定义在开发期间运行应用程序的入口点
wsgi.py
此外,我们还需要一个单独的模块来运行带有Gunicorn的Flask应用. 它将只有两行:
从应用程序导入create_app
app = create_app()
应用程序代码准备好了. 下一步是创建一个Docker配置.
建造码头集装箱
我们的应用需要多个Docker容器来运行:
- 应用程序容器,用于提供模板页面和公开API端点. 在生产环境中拆分这两个函数是个好主意, 但我们的演示应用中没有任何模板化页面. 容器将运行Gunicorn web服务器,该服务器将通过WSGI协议与Flask通信.
- 执行长任务的Celery worker容器. 这是同一个应用程序容器, 而是使用自定义的运行命令来启动芹菜, instead of Gunicorn.
- 芹菜搅拌器——和上面一样, 但对于定期调用的任务, 比如删除那些从未确认过电子邮件的用户的账户.
-
RabbitMQ container. 芹菜需要一个消息代理来在worker和应用程序之间进行通信,并存储任务结果. RabbitMQ是一个常见的选择,但你也可以使用Redis或Kafka.
- 数据库容器与PostgreSQL.
轻松管理多个容器的一种自然方式是使用Docker Compose. 但首先,我们需要创建一个Dockerfile来为我们的应用程序构建一个容器映像. 让我们把它放到项目目录中.
FROM python:3.7.2
运行pip install pipenv
ADD . /flask-deploy
WORKDIR / flask-deploy
执行命令pipenv install——system——skip-lock
运行pip install gunicorn[gevent]
EXPOSE 5000
CMD gunicorn——worker-class gevent——workers 8——bind 0.0.0.0:5000 wsgi:app——max-requests 10000——timeout 5——keep-alive 5——日志级信息
这个文件指示Docker:
- 使用Pipenv安装所有依赖项
- 向容器中添加一个应用程序文件夹
- 将TCP端口5000暴露给主机
- 将容器的默认启动命令设置为一个Gunicorn调用
让我们进一步讨论最后一行发生了什么. 它运行Gunicorn,指定工人类为 gevent. Gevent是用于协作多任务处理的轻量级并发库. 它在I/O绑定负载上提供了相当大的性能提升, 与操作系统抢占式多任务线程相比,提供了更好的CPU利用率. The --workers
参数为工作进程数. 将其设置为服务器上的核数是个好主意.
一旦我们有了应用容器的Dockerfile,我们就可以创建一个 docker-compose.yml
文件,该文件将定义应用程序运行所需的所有容器.
version: '3'
services:
broker-rabbitmq:
image: "rabbitmq:3.7.14-management"
environment:
- RABBITMQ_DEFAULT_USER = rabbit_user
- RABBITMQ_DEFAULT_PASS = rabbit_password
db-postgres:
image: "postgres:11.2"
environment:
- POSTGRES_USER = db_user
- POSTGRES_PASSWORD = db_password
migration:
build: .
environment:
- APP_ENV=${APP_ENV}
命令:flask db upgrade
depends_on:
- db-postgres
api:
build: .
ports:
- "5000:5000"
environment:
- APP_ENV=${APP_ENV}
depends_on:
- broker-rabbitmq
- db-postgres
- migration
api-worker:
build: .
命令:celery worker——workdir=. -A tasks.芹菜——loglevel = info
environment:
- APP_ENV=${APP_ENV}
depends_on:
- broker-rabbitmq
- db-postgres
- migration
api-beat:
build: .
命令:celery beat -A tasks.芹菜——loglevel = info
environment:
- APP_ENV=${APP_ENV}
depends_on:
- broker-rabbitmq
- db-postgres
- migration
我们定义了以下服务:
-
broker-rabbitmq
—RabbitMQ消息代理容器. 连接凭据由环境变量定义
-
db-postgres
-一个PostgreSQL容器及其凭证
-
migration
-一个应用程序容器,它将使用Flask-Migrate执行数据库迁移并退出. API容器依赖于它,并将在之后运行.
-
api
—主应用容器
-
api-worker
and api-beat
-为从API接收的任务和计划任务运行Celery worker的容器
每个应用容器也将接收 APP_ENV
variable from the docker-compose up
command.
一旦我们准备好了所有的应用程序资产, 我们把它们放到GitHub上, 哪个将帮助我们在服务器上部署代码.
git add *
git commit -a -m 'Initial commit'
Git远程添加origin git@github.com:你的名字/ flask-deploy.git
Git push -u origin master
配置服务器
我们的代码现在在GitHub上, 剩下的就是执行初始服务器配置和部署应用程序. 在我的例子中,服务器是一个运行AMI Linux的AWS实例. 对于其他版本的Linux,指令可能略有不同. 我还假设服务器已经有一个外部IP地址, DNS中配置了指向该IP的记录, 和为该域颁发SSL证书.
Security tip: 不要忘记为HTTP(S)通信允许端口80和443, 主机控制台中用于SSH的端口22(或使用 iptables
),并关闭所有其他端口的外部访问! 一定要做同样的 IPv6 protocol!
安装依赖关系
首先,我们需要Nginx和Docker在服务器上运行,再加上Git来提取代码. 让我们通过SSH登录并使用包管理器来安装它们.
安装-y docker-compose nginx git
Configuring Nginx
下一步是配置Nginx. The main nginx.conf
配置文件通常是原样的. 不过,一定要检查一下它是否适合你的需要. 对于我们的应用程序,我们将创建一个新的配置文件 conf.d
folder. 顶级配置有一个包含所有的指令 .conf
files from it.
cd /etc/nginx/conf.d
执行命令vim flask-deploy.conf
这是一个Nginx的Flask站点配置文件,包括电池. 它具有以下特点:
- SSL is configured. 您应该拥有域的有效证书,例如.g., a free Let’s Encrypt certificate.
-
www.your-site.com
请求被重定向到 your-site.com
- HTTP请求被重定向到安全的HTTPS端口.
- 反向代理配置为将请求传递到本地端口5000.
- 静态文件由Nginx从本地文件夹提供.
server {
listen 80;
listen 443;
server_name www.your-site.com;
检查您的证书路径!
ssl_certificate /etc/nginx/ssl/your-site.com/fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/your-site.com/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
#重定向到非www域名
返回301 http://your-site.com$request_uri;
}
# HTTP到HTTPS重定向
server {
listen 80;
server_name站点.com;
返回301 http://your-site.com$request_uri;
}
server {
listen 443 ssl;
检查您的证书路径!
ssl_certificate /etc/nginx/ssl/your-site.com/fullchain.crt;
ssl_certificate_key /etc/nginx/ssl/your-site.com/server.key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
#影响用户可以通过HTTP POST上传的文件大小
client_max_body_size 10米;
server_name站点.com;
location / {
包括/etc/nginx/mime.types;
根/home/ec2-user/flask-deploy /静态;
#如果没有找到静态文件-将请求传递给Flask
Try_files $uri @flask
}
location @flask {
add_header 'Access-Control-Allow-Origin' '*' always;
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,Authorization';
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
proxy_read_timeout 10;
proxy_send_timeout 10;
send_timeout 60;
resolver_timeout 120;
client_body_timeout 120;
#设置headers向Flask传递请求信息
proxy_set_header $http_host;
proxy_set_header x - forward - proto $scheme;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_redirect;
proxy_pass http://127.0.0.1:5000$uri;
}
}
编辑完文件后,运行命令 sudo nginx -s reload
看看是否有错误.
设置GitHub凭据
有一个单独的“部署”VCS帐户来部署项目和CI/CD系统是一个很好的做法. 这样,您就不会有暴露自己帐户凭据的风险. 以进一步保护项目存储库, 您还可以将此类帐户的权限限制为只读访问. 对于GitHub存储库,您需要一个组织帐户来完成此操作. 部署我们的演示应用程序, 我们只需在服务器上创建一个公钥,并在GitHub上注册它,就可以访问我们的项目,而不必每次都输入凭据.
要创建一个新的SSH密钥,请运行:
cd ~/.ssh
Ssh-keygen -b 2048 -t rsa -f id_rsa.pub -q -N "" -C "deploy"
然后登录GitHub和 add your public key from ~/.ssh/id_rsa.pub
in account settings.
Deploying an App
最后的步骤非常简单——我们需要从GitHub获取应用程序代码,并使用Docker Compose启动所有容器.
cd ~
Git克隆http://github.com/your-name/flask-deploy.git
git checkout master
APP_ENV=生产docker-compose up -d
省略它可能是个好主意 -d
(它以分离模式启动容器),以便在终端中查看每个容器的输出并检查可能的问题. 另一种选择是使用 docker logs
afterward. 让我们看看是否所有的容器都在运行 docker ps.
![image_alt_text](data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==)
Great. 所有五个容器都已启动并运行. Docker Compose根据在Docker - Compose中指定的服务自动分配容器名称.yml. 现在是测试整个配置如何工作的时候了! 最好从外部机器运行测试,以确保服务器具有正确的网络设置.
#测试HTTP协议,你应该得到一个301响应
curl your-site.com
HTTPS请求应该返回Hello World消息
curl http://your-site.com
#和nginx应该正确发送测试静态文件:
curl http://your-site.com/hello-world.txt
That’s it. 我们有一个极简主义, 而是在AWS实例上运行的应用程序的完全生产就绪配置. 希望它能帮助您快速开始构建实际应用程序并避免一些常见错误! 完整的代码是可用的 在GitHub存储库上.
Conclusion
In this article, 我们讨论了一些结构化的最佳实践, configuring, 将Flask应用打包并部署到生产环境中. 这是一个非常大的话题,不可能在一篇博客文章中完全涵盖. 以下是我们没有提到的一些重要问题:
本文不涉及以下内容:
- 持续集成和持续部署
- Automatic testing
- Log shipping
- API monitoring
- 将应用程序扩展到多个服务器
- 源代码中的凭证保护
但是,您可以使用本博客上的其他一些很棒的资源来学习如何做到这一点. 例如,要探索日志记录,请参见 Python日志:深度教程,或有关CI/CD和自动化测试的一般概述,请参阅 如何构建有效的初始部署管道. 我把这些方法的实现留给读者作为练习.
Thanks for reading!
了解基本知识
什么是Flask应用程序?
Flask应用程序是使用Flask库为web构建的Python应用程序.
Flask和Django哪个更好?
这两个框架都适用于各种与web相关的任务. Flask通常用于构建web服务,而这些web服务并不是完全成熟的网站, 并以其灵活性而闻名.
What is Gunicorn?
Gunicorn是一个Python WSGI HTTP服务器. 它的常见用例是在生产环境中通过WSGI接口为Flask或Django Python应用程序提供服务.
什么是芹菜,为什么使用它与Flask?
像Flask这样的Web服务器不适合长时间运行的任务,比如视频处理. 芹菜是一个任务队列,用于以方便和异步的方式处理这些任务. 任务数据存储在支持的后端存储引擎中,如RabbitMQ或Redis.
为什么要使用Docker Compose?
Docker Compose是一个方便的Docker工具, 哪一个允许通过定义服务在更高的抽象级别上使用容器. 它还处理启动和停止容器的常见场景.
为什么我们需要一个Nginx web服务器为Flask?
而Gunicorn则非常适合作为应用服务器, 由于安全考虑和web请求处理限制,让它面对Internet并不是一个好主意. 一个常见的模式是在Gunicorn前面使用Nginx作为反向代理来路由服务之间的请求.
什么时候PostgreSQL是一个好的选择?
PostgreSQL是一个成熟的高性能关系数据库引擎,有许多插件和库,适用于所有主要的编程语言. 如果您的项目(无论大小)只需要一个数据库,那么它是一个完美的选择. 另外,它是开源和免费的.
聘请Toptal这方面的专家.Hire Now世界级的文章,每周发一次.
世界级的文章,每周发一次.
Toptal Developers
- Algorithm Developers
- Angular Developers
- AWS Developers
- Azure Developers
- Big Data Architects
- 区块链开发人员
- 商业智能开发人员
- C Developers
- 计算机视觉开发人员
- Django Developers
- Docker Developers
- Elixir Developers
- Go Engineers
- GraphQL Developers
- Jenkins Developers
- Kotlin Developers
- Kubernetes Experts
- 机器学习工程师
- Magento Developers
- .NET Developers
- R Developers
- React原生开发者
- Ruby on Rails开发者
- Salesforce开发者
- SQL Developers
- Sys Admins
- Tableau Developers
- 虚幻引擎开发人员
- Xamarin Developers
- 查看更多自由开发者
Join the Toptal® community.
MG-Games-feedback@shikstar.com
新葡京博彩
9188彩票网
养猪巴巴网
365体育投注
新葡京
赶集好车
皇冠体育博彩
太阳鸟
365-Sports-help@u88xw.com
遵义百姓网
江苏新闻周刊网
新视听
好大夫在线浙江站
bet365体育
名表论坛网
365-Sports-sales@peakuniverse.com
Venice-Macao-contact@zhuaren.net
太阳城官网
太阳城
柒牌男装官网
teamviewer官网
陕西广电网络
天狼保镖公司
MAC魅可官方网站
中国化学工程集团公司
图片仓库
掌上大学
河南财政税务高等专科学校
山水人家装饰
大猫网
美赞臣官网
大众口腔
激动网
首航直升