在本教程中,我们将介绍将Flask应用程序部署到AWS Elastic Beanstalk的过程。
- 目标
- 什么是Elastic Beanstalk?
- 项目设置
- 弹性Beanstalk CLI
- 初始化Elastic Beanstalk
- 配置环境
- 配置RDS
- 使用证书管理器的HTTPS
- 环境变量
- 调试Elastic Beanstalk
- 总结
目标
在本教程结束时,你将能够:
- 解释什么是Elastic Beanstalk
- 初始化和配置Elastic Beanstalk
- 对运行在Elastic Beanstalk上的应用程序进行故障排除
- 将Elastic Beanstalk与RDS结合起来
- 通过AWS证书管理器获得一个SSL证书
- 使用SSL证书,在HTTPS上为你的应用程序提供服务
什么是Elastic Beanstalk?
AWS Elastic Beanstalk(EB)是一种易于使用的服务,用于部署和扩展Web应用程序。它连接了多个AWS服务,如计算实例(EC2)、数据库(RDS)、负载平衡器(应用负载平衡器)和文件存储系统(S3),仅举几例。EB允许你快速开发和部署你的网络应用,而不用考虑底层的基础设施。它支持用Go、Java、.NET、Node.js、PHP、Python和Ruby开发的应用程序。如果你需要配置自己的软件栈,或者部署用EB目前不支持的语言(或版本)开发的应用程序,EB也支持Docker。
典型的Elastic Beanstalk设置。
AWS Elastic Beanstalk没有额外收费。你只需为你的应用程序所消耗的资源付费。
要了解更多关于Elastic Beanstalk的信息,请查看AWS ElasticBeanstalk的官方文档《什么是AWS Elastic Beanstalk?
弹性Beanstalk的概念
在进入教程本身之前,让我们看看与Elastic Beanstalk有关的几个关键概念。
- 一个**应用程序**是Elastic Beanstalk组件的逻辑集合,包括环境、版本和环境配置。一个应用程序可以有多个版本。
- 一个**环境**是运行一个应用程序版本的AWS资源的集合。
- 一个**平台**是操作系统、编程语言运行时间、Web服务器、应用服务器和Elastic Beanstalk组件的组合。
这些术语将在整个教程中使用。
项目设置
在本教程中,我们将部署一个简单的Flask应用程序,名为flask-movies。
在跟随本教程的过程中,通过部署自己的应用程序来检查你的理解。
首先,从GitHub上的存储库中抓取代码:
$ git clone git@github.com:duplxey/flask-movies.git
$ cd flask-movies
创建一个新的虚拟环境并激活它:
$ python3 -m venv venv && source venv/bin/activate
安装需求并初始化数据库:
(venv)$ pip install -r requirements.txt (venv)$ python init_db.py
运行服务器:
(venv)$ flask run
打开你最喜欢的网络浏览器并导航到:
- http://localhost:5000- 应该显示 "flask-movies "文本
- http://localhost:5000/api/movies- 应该显示一个电影的列表
Elastic Beanstalk命令行界面(EB CLI)允许你执行各种操作来部署和管理你的Elastic Beanstalk应用程序和环境。
有两种安装EB CLI的方式。
建议使用安装程序(第一个选项)全局安装EB CLI(在任何特定的虚拟环境之外),以避免可能的依赖性冲突。更多细节请参考这个解释。
安装完EB CLI后,你可以通过运行来检查版本:
$ eb --version
EB CLI 3.20.3 (Python 3.10.)
如果该命令不起作用,你可能需要将EB CLI添加到$PATH
。
EB CLI命令的列表和它们的描述可以在EB CLI命令参考中找到。
初始化Elastic Beanstalk
一旦我们运行了EB CLI,我们就可以开始与Elastic Beanstalk进行交互。让我们来初始化一个新的项目和一个EB环境。
初始化
在项目根目录下("flask-movies"),运行:
$ eb init
你会被提示一些问题。
默认区域
你的Elastic Beanstalk环境(和资源)的AWS区域。如果你不熟悉不同的AWS区域,请查看AWS区域和可用区。一般来说,你应该选择离你的客户最近的地区。请记住,资源价格因地区而异。
应用程序名称
这是你的Elastic Beanstalk应用程序的名称。我建议直接按回车键,使用默认的:"flask-movies"。
平台和平台分支
EB CLI会检测你是否使用Python环境。之后,它会给你不同的Python版本和亚马逊Linux版本,你可以使用。选择 "Python 3.8 运行于 64bit Amazon Linux 2"。
CodeCommit
CodeCommit是一个安全的、高度可扩展的、可管理的源码控制服务,它可以托管私有的 Git 仓库。我们不会使用它,因为我们已经在使用GitHub进行源代码控制。所以说 "不"。
SSH
为了以后连接到EC2实例,我们需要设置SSH。在提示时说 "是"。
密钥对
为了连接到EC2实例,我们需要一个RSA密钥对。继续并生成一个,它将被添加到你的"~/.ssh "文件夹。
在你回答完所有问题后,你会注意到在你的项目根中有一个名为".elasticbeanstalk "的隐藏目录。该目录应该包含一个config.yml文件,其中有你刚才提供的所有数据。
.elasticbeanstalk
└── config.yml
该文件应该包含类似的内容。
branch-defaults:
master:
environment: null
group_suffix: null
global:
application_name: flask-movies
branch: null
default_ec2_keyname: aws-eb
default_platform: Python 3.8 running on 64bit Amazon Linux 2
default_region: us-west-2
include_git_submodules: true
instance_profile: null
platform_name: null
platform_version: null
profile: eb-cli
repository: null
sc: git
workspace_type: Application
创建
接下来,让我们创建Elastic Beanstalk环境并部署应用程序。
$ eb create
同样,你会被提示几个问题。
环境名称
这代表了EB环境的名称。我建议坚持使用默认的:"flask-movies-env"。
在环境中添加
└-env
或└-dev
后缀被认为是很好的做法,这样你可以很容易地将 EB 应用程序与环境区分开来。
DNS CNAME前缀
你的网络应用将可以在%cname%.%region%.elasticbeanstalk.com
。同样,使用默认值。
负载平衡器
负载平衡器在你的环境的实例中分配流量。选择 "应用程序"。
如果你想了解不同的负载平衡器类型,请查看Elastic Beanstalk环境的负载平衡器。
Spot Fleet请求
Spot Fleet请求允许你根据你的标准,按需启动实例。我们不会在本教程中使用它们,所以说 "不"。
--
就这样,环境将被启动。
- 你的代码将被压缩并上传到一个新的S3 Bucket。
- 之后,各种AWS资源将被创建,如负载平衡器、安全和自动扩展组,以及EC2实例。
一个新的应用程序也将被部署。
这将需要大约三分钟,所以请随意拿杯咖啡。
部署完成后,EB CLI将修改*.elasticbeanstalk/config.yml*。
你的项目结构现在应该是这样的。
|-- .elasticbeanstalk
| └-- config.yml
|-- .gitignore
|-- README.md
|-- app.py
|-- default.db
|-- init_db.py
└-- requirements.txt
状态
一旦你部署了你的应用程序,你可以通过运行来检查其状态。
$ eb status
Environment details for: flask-movies-env
Application name: flask-movies
Region: us-west-2
Deployed Version: app-82fb-220311_171256090207
Environment ID: e-nsizyek74z
Platform: arn:aws:elasticbeanstalk:us-west-2::platform/Python 3.8 running on 64bit Amazon Linux 2/3.3.11
Tier: WebServer-Standard-1.0
CNAME: flask-movies-env.us-west-2.elasticbeanstalk.com
Updated: 2022-03-11 23:16:03.822000+00:00
Status: Launching
Health: Red
你可以看到,我们环境的当前健康状况是Red
,这意味着出了问题。先不要担心这个,我们会在接下来的步骤中解决这个问题。
你还可以看到AWS给我们分配了一个CNAME,这是我们EB环境的域名。我们可以通过打开浏览器并导航到CNAME来访问Web应用程序。
打开
这个命令将打开你的默认浏览器并导航到CNAME域名。你会看到502 Bad Gateway
,我们很快会在这里解决这个问题。
$ eb open
控制台
$ eb console
这个命令将在你的默认浏览器中打开Elastic Beanstalk控制台。
同样,你可以看到环境的健康状况是 "Severe",我们将在下一步解决这个问题。
配置一个环境
在上一步中,我们尝试访问我们的应用程序,它返回502 Bad Gateway
。这背后有两个原因。
- Python需要
PYTHONPATH
,以便在我们的应用程序中找到模块。 - 默认情况下,Elastic Beanstalk试图从application.py中启动WSGI应用程序,而这个应用程序并不存在。
默认情况下,Elastic Beanstalk用Gunicorn为Python应用程序服务。EB会在部署过程中自动安装Gunicorn,因此我们不需要将其添加到requirements.txt中。如果你想把Gunicorn换成其他东西,可以看看用Procfile配置WSGI服务器。
让我们来修复这些错误。
在项目根目录下创建一个新的文件夹,名为".ebextensions"。在新创建的文件夹中创建一个名为01_flask.config的文件。
# .ebextensions/01_flask.config
option_settings:
aws:elasticbeanstalk:application:environment:
PYTHONPATH: "/var/app/current:$PYTHONPATH"
aws:elasticbeanstalk:container:python:
WSGIPath: "app:app"
注意:
EB*.config*文件是如何工作的?
- 你可以有你想要的数量。
- 它们是按照以下顺序加载的。01_x, 02_x, 03_x, 等等。
- 你不必记住这些设置;你可以通过运行
eb config
,列出你所有的环境设置。如果你想了解更多关于高级环境定制的信息,请查看用配置文件进行高级环境定制。
接下来,我们要告诉Elastic Beanstalk,当新的应用程序版本被部署时,要初始化数据库。在*.ebextensions/01_flask.config*的末尾添加以下内容。
# .ebextensions/01_flask.config
container_commands:
01_initdb:
command: "source /var/app/venv/*/bin/activate && python3 init_db.py"
leader_only: true
现在,EB环境将在我们每次部署新的应用程序版本时执行上述命令。我们使用leader_only
,所以只有第一个EC2实例执行它们(在我们的EB环境运行多个EC2实例的情况下)。
Elastic Beanstalk配置支持两种不同的命令部分,commands和container_commands。它们之间的主要区别是在部署过程中何时运行。
commands
在应用程序和Web服务器设置完毕、应用程序版本文件被提取之前运行。container_commands
在应用程序和Web服务器设置完毕、应用程序版本档案提取完毕之后,但在应用程序版本部署之前(在文件从暂存文件夹移到最终位置之前)运行。
在这一点上,你的项目结构应该是这样的。
|-- .ebextensions
| └-- 01_flask.config
|-- .elasticbeanstalk
| └-- config.yml
|-- .gitignore
|-- README.md
|-- app.py
|-- default.db
|-- init_db.py
└-- requirements.txt
将修改提交到git并部署。
$ git add .
$ git commit -m "updates for eb"
$ eb deploy
你会注意到,如果你不提交,Elastic Beanstalk就不会检测到这些变化。这是因为EB与git集成,只检测已提交(更改)的文件。
部署完成后,运行eb open
,看看是否一切顺利。之后,在URL上添加/api/movies
,看看电影是否还能显示。
耶!我们的应用程序的第一个版本现在已经部署完毕。
配置RDS
如果你正在部署flask-movies,你会发现它默认使用SQLite数据库。虽然这对开发来说是完美的,但你通常希望在生产中使用更强大的数据库,如Postgres或MySQL。让我们看看如何把SQLite换成Postgres。
本地Postgres
首先,让我们在本地运行Postgres。你可以从PostgreSQL下载,或者启动一个Docker容器。
$ docker run --name flask-movies-postgres -p 5432:5432 \
-e POSTGRES_USER=flask-movies -e POSTGRES_PASSWORD=complexpassword123 \
-e POSTGRES_DB=flask-movies -d postgres
检查容器是否正在运行。
$ docker ps -f name=flask-movies-postgres
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c05621dac852 postgres "docker-entrypoint.s…" About a minute ago Up About a minute 0.0.0.0:5432->5432/tcp flask-movies-postgres
现在,让我们试着用我们的Flask应用程序连接到它。
像这样在app.py中改变SQLALCHEMY_DATABASE_URI
。
app.config['SQLALCHEMY_DATABASE_URI'] = \
'postgresql://{username}:{password}@{host}:{port}/{database}'.format(
username='flask-movies',
password='complexpassword123',
host='localhost',
port='5432',
database='flask-movies',
)
接下来,安装psycopg2-binary,它是Postgres所需要的。
(venv)$ pip install psycopg2-binary==2.9.3
把它添加到requirements.txt中。
Flask==2.0.3 Flask-SQLAlchemy==2.5.1 psycopg2-binary==2.9.3
删除现有的数据库*default.*db,然后初始化新的数据库。
(venv)$ python init_db.py
运行服务器。
通过检查http://localhost:5000/api/movies,确保电影仍然能够正确地被提供。
AWS RDS Postgres
要为生产设置Postgres,首先运行以下命令,打开AWS控制台。
$ eb console
点击左侧栏的 "配置",向下滚动到 "数据库",然后点击 "编辑"。
用以下设置创建一个数据库,并点击 "应用"。
- 引擎:postgres
- 引擎版本:12.9(较早的Postgres版本,因为db.t2.micro在13.1+中不可用)。
- 实例类:db.t2.micro
- 存储空间。5GB(应该是绰绰有余)
- 用户名:选择一个用户名
- 密码:选择一个强密码
如果你想保持在AWS免费层内,确保你选择db.t2.micro。RDS的价格会根据你选择的实例等级而成倍增加。如果你不想用
micro
,请确保查看AWS PostgreSQL的价格。
环境更新完成后,EB会自动将以下DB凭证传递给我们的flask应用。
RDS_DB_NAME
RDS_USERNAME
RDS_PASSWORD
RDS_HOSTNAME
RDS_PORT
现在我们可以在app.py中使用这些变量来连接到我们的数据库。
if 'RDS_DB_NAME' in os.environ:
app.config['SQLALCHEMY_DATABASE_URI'] = \
'postgresql://{username}:{password}@{host}:{port}/{database}'.format(
username=os.environ['RDS_USERNAME'],
password=os.environ['RDS_PASSWORD'],
host=os.environ['RDS_HOSTNAME'],
port=os.environ['RDS_PORT'],
database=os.environ['RDS_DB_NAME'],
)
else:
app.config['SQLALCHEMY_DATABASE_URI'] = \
'postgresql://{username}:{password}@{host}:{port}/{database}'.format(
username='flask-movies',
password='complexpassword123',
host='localhost',
port='5432',
database='flask-movies',
)
不要忘记在app.py的顶部导入os
包。
import os
将修改提交给git并部署。
$ git add .
$ git commit -m "updates for eb"
$ eb deploy
等待部署完成。一旦完成,运行eb open
,在一个新的浏览器标签中打开你的应用程序。通过列举/api/movies
的电影,确保一切仍然正常工作。
使用证书管理器的HTTPS
本教程的这一部分要求你有一个域名。
需要一个便宜的域名来练习吗?一些域名注册商对'.xyz'域名有优惠。另外,你也可以在Freenom创建一个免费的域名。如果你没有域名,但仍想使用HTTPS,你可以创建并签署一个X509证书。
为了通过HTTPS为你的应用程序提供服务,我们需要。
- 申请并验证SSL/TLS证书
- 将你的域名指向你的EB CNAME
- 修改负载平衡器以提供HTTPS服务
- 修改你的应用程序设置
请求和验证SSL/TLS证书
导航到AWS证书管理器控制台。点击 "请求一个证书"。将证书类型设置为 "公共 "并点击 "下一步"。在表格输入中输入你的完全合格域名,将 "验证方法 "设置为 "DNS验证",然后点击 "请求"。
然后你会被转到一个页面,在那里你可以看到你所有的证书。你刚刚创建的证书应该有一个 "待验证 "的状态。
要让AWS签发证书,你首先要证明你是该域名的所有者。在表中,点击证书,查看 "证书详情"。注意 "CNAME名称 "和 "CNAME值"。为了验证域名的所有权,你需要在域名的DNS设置中创建一个CNAME记录"。为此要使用 "CNAME名称 "和 "CNAME值"。一旦完成,亚马逊将需要几分钟的时间来接收域名的变化并颁发证书。状态应该从 "待验证 "变为 "已签发"。
将域名指向EB CNAME
接下来,你需要将你的域名(或子域名)指向你的EB环境CNAME。在你的域名的DNS设置中,添加另一条CNAME记录,其值为你的EB CNAME -- 例如,flask-movies-dev.us-west-2.elasticbeanstalk.com
。
等待几分钟,让你的DNS刷新,然后在你的浏览器中从你的域名的http://
味道来测试。
修改负载均衡器以提供HTTPS服务
回到Elastic Beanstalk控制台,点击 "配置"。然后,在 "负载平衡器 "类别中,点击 "编辑"。点击 "添加监听器",创建一个监听器,详情如下。
- 端口 - 443
- 协议 - HTTPS
- SSL证书 - 选择你刚刚创建的证书
点击 "添加"。然后,滚动到页面底部,点击 "应用"。这将需要几分钟的时间来更新环境。
修改你的应用程序设置
接下来,我们需要对我们的Flask应用程序做一些修改。
我们需要将所有流量从HTTP重定向到HTTPS。有多种方法可以做到这一点,但最简单的方法是将Apache设置为代理主机。我们可以通过在*.ebextensions/01_flask.config*中的option_settings
的末尾添加以下内容来实现这一程序。
# .ebextensions/01_flask.config
option_settings:
# ...
aws:elasticbeanstalk:environment:proxy: # new
ProxyServer: apache # new
你最终的01_flask.config文件现在应该是这样的。
# .ebextensions/01_flask.config
option_settings:
aws:elasticbeanstalk:application:environment:
PYTHONPATH: "/var/app/current:$PYTHONPATH"
aws:elasticbeanstalk:container:python:
WSGIPath: "app:app"
aws:elasticbeanstalk:environment:proxy:
ProxyServer: apache
container_commands:
01_initdb:
command: "source /var/app/venv/*/bin/activate && python3 init_db.py"
leader_only: true
接下来,在项目根部创建一个".platform "文件夹,并添加以下文件和文件夹。
└-- .platform
└-- httpd
└-- conf.d
└-- ssl_rewrite.conf
ssl_rewrite.conf。
# .platform/httpd/conf.d/ssl_rewrite.conf
RewriteEngine On
<If "-n '%{HTTP:X-Forwarded-Proto}' && %{HTTP:X-Forwarded-Proto} != 'https'">
RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R,L]
</If>
你的项目结构现在应该是这样的。
|-- .ebextensions
| └-- 01_flask.config
|-- .elasticbeanstalk
| └-- config.yml
|-- .gitignore
|-- .platform
| └-- httpd
| └-- conf.d
| └-- ssl_rewrite.conf
|-- README.md
|-- app.py
|-- default.db
|-- init_db.py
└-- requirements.txt
将修改提交到git并部署。
$ git add .
$ git commit -m "updates for eb"
$ eb deploy
现在,在你的浏览器中,你的应用程序的https://
味道应该工作。试着进入http://
的版本。你应该被重定向到https://
。确保证书也被正确加载。
环境变量
在生产中,最好将特定环境的配置存储在环境变量中。通过Elastic Beanstalk,你可以通过两种不同的方式设置自定义环境变量。
通过EB CLI设置环境变量
让我们把FlaskSECRET_KEY
变成一个环境变量。
从运行开始。
$ eb setenv FLASK_SECRET_KEY='<replace me with your own secret key>'
你可以用一条命令设置多个环境变量,用空格隔开它们。这是推荐的方法,因为它只导致对EB环境的一次更新。
在app.py中相应地改变SECRET_KEY
。
# app.py
app.config['SECRET_KEY'] = os.environ.get(
'FLASK_SECRET_KEY',
'<replace me with your own fallback secret key>'
)
将修改提交到git并部署。
$ git add .
$ git commit -m "updates for eb"
$ eb deploy
通过EB控制台的环境变量
通过eb open
,进入Elastic Beanstalk控制台。导航到 "配置" > "软件" > "编辑"。然后,向下滚动到 "环境属性"。
完成后,点击 "应用",你的环境将会更新。
然后你可以通过os.environ
,在你的Python环境中访问这些变量。
比如说。
VARIABLE_NAME = os.environ['VARIABLE_NAME']
调试Elastic Beanstalk
在使用Elastic Beanstalk时,如果你不知道如何访问日志文件,要找出出错的原因可能会非常令人沮丧。在本节中,我们将讨论这个问题。
有两种方法可以访问日志。
- Elastic Beanstalk CLI或控制台
- SSH进入EC2实例
根据个人经验,我已经能够用第一种方法解决所有问题。
Elastic Beanstalk CLI或控制台
CLI:
$ eb logs
这个命令将从以下文件中获取最后100行。
/var/log/web.stdout.log
/var/log/eb-hooks.log
/var/log/nginx/access.log
/var/log/nginx/error.log
/var/log/eb-engine.log
运行
eb logs
,相当于登录到EB控制台,导航到 "日志"。
我建议用管道将日志送到CloudWatch。运行下面的命令来启用它。
$ eb logs --cloudwatch-logs enable
你通常会在*/var/log/web.stdout.log或/var/log/eb-engine.log*中发现Flask错误。
要了解更多关于Elastic Beanstalk日志的信息,请查看查看亚马逊EC2实例的日志。
SSH进入EC2实例
要连接到运行Flask应用程序的EC2实例,请运行:
$ eb ssh
你会被提示第一次将该主机添加到你的已知主机中。说 "是"。这样,你现在就可以完全访问你的EC2实例了。请随意检查上一节中提到的一些日志文件。
请记住,Elastic Beanstalk会自动扩展和部署新的EC2实例。你在这个特定的EC2实例上所做的改变不会反映在新启动的EC2实例上。一旦这个特定的EC2实例被替换,你的改动将被抹去。
总结
在本教程中,我们走过了将Flask应用程序部署到AWS Elastic Beanstalk的过程。现在你应该对Elastic Beanstalk的工作原理有了一定的了解。通过回顾本教程开头的目标,进行一次快速的自我检查。
接下来的步骤。
- 你应该考虑创建两个独立的EB环境(
dev
和production
)。 - 回顾Elastic Beanstalk环境的自动扩展组,了解如何配置触发器以自动扩展你的应用程序。
要删除我们在整个教程中创建的所有AWS资源,首先终止Elastic Beanstalk环境。
你需要手动删除SSL证书。
最后,你可以在GitHub上的flask-elastic-beanstalkrepo中找到最终版本的代码。