如何将Flask应用程序部署到Elastic Beanstalk上的完整教程

185 阅读9分钟

在本教程中,我们将介绍将Flask应用程序部署到AWS Elastic Beanstalk的过程。

目标

在本教程结束时,你将能够:

  1. 解释什么是Elastic Beanstalk
  2. 初始化和配置Elastic Beanstalk
  3. 对运行在Elastic Beanstalk上的应用程序进行故障排除
  4. 将Elastic Beanstalk与RDS结合起来
  5. 通过AWS证书管理器获得一个SSL证书
  6. 使用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设置。

Elastic Beanstalk Architecture

AWS Elastic Beanstalk没有额外收费。你只需为你的应用程序所消耗的资源付费。

要了解更多关于Elastic Beanstalk的信息,请查看AWS ElasticBeanstalk官方文档什么是AWS Elastic Beanstalk?

弹性Beanstalk的概念

在进入教程本身之前,让我们看看与Elastic Beanstalk有关的几个关键概念

  1. 一个**应用程序**是Elastic Beanstalk组件的逻辑集合,包括环境、版本和环境配置。一个应用程序可以有多个版本
  2. 一个**环境**是运行一个应用程序版本的AWS资源的集合。
  3. 一个**平台**是操作系统、编程语言运行时间、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

打开你最喜欢的网络浏览器并导航到:

  1. http://localhost:5000- 应该显示 "flask-movies "文本
  2. http://localhost:5000/api/movies- 应该显示一个电影的列表

在继续之前,请务必注册一个AWS账户。通过创建一个账户,你也可能有资格获得AWS免费层

Elastic Beanstalk命令行界面(EB CLI)允许你执行各种操作来部署和管理你的Elastic Beanstalk应用程序和环境。

有两种安装EB CLI的方式。

  1. 通过EB CLI安装程序
  2. 通过pip (awsebcli)

建议使用安装程序(第一个选项)全局安装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请求允许你根据你的标准,按需启动实例。我们不会在本教程中使用它们,所以说 "不"。

--

就这样,环境将被启动。

  1. 你的代码将被压缩并上传到一个新的S3 Bucket。
  2. 之后,各种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控制台。

Elastic Beanstalk Console

同样,你可以看到环境的健康状况是 "Severe",我们将在下一步解决这个问题。

配置一个环境

在上一步中,我们尝试访问我们的应用程序,它返回502 Bad Gateway 。这背后有两个原因。

  1. Python需要PYTHONPATH ,以便在我们的应用程序中找到模块。
  2. 默认情况下,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"

注意:

  1. 我们将PYTHONPATH 设置为我们 EC2 实例上的 Python 路径(docs)。
  2. 我们把WSGIPath 改为我们的WSGI应用程序(文档)。

EB*.config*文件是如何工作的?

  1. 你可以有你想要的数量。
  2. 它们是按照以下顺序加载的。01_x, 02_x, 03_x, 等等。
  3. 你不必记住这些设置;你可以通过运行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配置支持两种不同的命令部分,commandscontainer_commands。它们之间的主要区别是在部署过程中何时运行。

  1. commands 在应用程序和Web服务器设置完毕、应用程序版本文件被提取之前运行。
  2. 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的价格

RDS settings

环境更新完成后,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为你的应用程序提供服务,我们需要。

  1. 申请并验证SSL/TLS证书
  2. 将你的域名指向你的EB CNAME
  3. 修改负载平衡器以提供HTTPS服务
  4. 修改你的应用程序设置

请求和验证SSL/TLS证书

导航到AWS证书管理器控制台。点击 "请求一个证书"。将证书类型设置为 "公共 "并点击 "下一步"。在表格输入中输入你的完全合格域名,将 "验证方法 "设置为 "DNS验证",然后点击 "请求"。

AWS Request Public Certificate

然后你会被转到一个页面,在那里你可以看到你所有的证书。你刚刚创建的证书应该有一个 "待验证 "的状态。

要让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控制台,点击 "配置"。然后,在 "负载平衡器 "类别中,点击 "编辑"。点击 "添加监听器",创建一个监听器,详情如下。

  1. 端口 - 443
  2. 协议 - HTTPS
  3. 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:// 。确保证书也被正确加载。

secure app

环境变量

在生产中,最好将特定环境的配置存储在环境变量中。通过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控制台。导航到 "配置" > "软件" > "编辑"。然后,向下滚动到 "环境属性"。

AWS Elastic Beanstalk Environment Variables

完成后,点击 "应用",你的环境将会更新。

然后你可以通过os.environ ,在你的Python环境中访问这些变量。

比如说。

VARIABLE_NAME = os.environ['VARIABLE_NAME']

调试Elastic Beanstalk

在使用Elastic Beanstalk时,如果你不知道如何访问日志文件,要找出出错的原因可能会非常令人沮丧。在本节中,我们将讨论这个问题。

有两种方法可以访问日志。

  1. Elastic Beanstalk CLI或控制台
  2. 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的工作原理有了一定的了解。通过回顾本教程开头的目标,进行一次快速的自我检查。

接下来的步骤。

  1. 你应该考虑创建两个独立的EB环境(devproduction )。
  2. 回顾Elastic Beanstalk环境的自动扩展组,了解如何配置触发器以自动扩展你的应用程序。

要删除我们在整个教程中创建的所有AWS资源,首先终止Elastic Beanstalk环境。

你需要手动删除SSL证书。

最后,你可以在GitHub上的flask-elastic-beanstalkrepo中找到最终版本的代码。