最近在Flask Web Development作者博客看到第二版Flask Mega-Tutorial已在2017年底更新,现翻译给大家参考,希望帮助大家学习flask。
这是Flask Mega-Tutorial系列的第十七章,我将把Microblog部署到Linux服务器。
供您参考,以下是本系列文章的列表。
注意1:如果您正在寻找本教程的旧版本,请在此处。
注意2:如果您想在此博客上支持我的工作,或者只是没有耐心等待每周的文章,我将提供完整的本教程版本,打包成电子书或视频集。欲了解更多信息,请访问courses.miguelgrinberg.com。
在本章中,我将达到Microblog应用生命中的一个里程碑,因为我将讨论在生产服务器上部署该应用的方式,以便真实用户可以访问该应用。
部署的主题很广泛,因此不可能在这里涵盖所有范畴。 本章致力于探讨传统托管方式,包括Ubuntu发行版的Linux服务器和流行的树莓派微机。 我将在后面的章节中介绍其他选项,例如云和容器部署。
本章的GitHub链接是:Browse,Zip,Diff。
当我提到“传统托管”时,我的意思是应用是手动或通过原始服务器机器上的脚本安装部署的。该过程涉及安装应用程序,其依赖项和生产规模的Web服务器,并配置系统以使其安全。
当您将要部署自己的项目时,您需要问的第一个问题是在哪里可以找到服务器。目前有很多经济托管服务。例如,Digital Ocean,Linode或Amazon Lightsail会以每月5美元的价格租用一台虚拟Linux服务器,在其中运行您的部署实验(Linode和Digital Ocean为其入门级服务器提供1GB RAM,而Amazon仅提供512MB )。如果您一分钱都不愿意花,那么Vagrant和VirtualBox这两个工具结合在一起,使可以您在自己的计算机上创建类似于付费服务器的虚拟服务器。
从技术的角度来看,就操作系统的选择而言,此应用程序可以部署在任何主要操作系统上,该列表包括各种各样的开源Linux和BSD发行版以及商业OS X和Microsoft Windows(尽管OS X是基于开源BSD衍生产品Darwin的,它是开源/商业的混合选项)。
由于OS X和Windows是未优化为用作服务器的桌面操作系统,因此我将其作为候选者丢弃。在Linux或BSD操作系统之间进行选择主要取决于喜好,因此,我将选择两种操作系统中最流行的一种,即Linux。至于Linux发行版,我将再次选择流行度,并选择Ubuntu。
如果您有兴趣与我一起进行此部署,则显然需要一台服务器来进行工作。我将为您推荐两种选择来购买服务器,一种是付费的,另一种是免费的。如果您愿意花一点钱,可以在Digital Ocean,Linode或Amazon Lightsail上获得一个帐户,然后创建一个Ubuntu 16.04虚拟服务器。您应该使用最小的服务器选项,在我撰写本文时,这三个提供商的费用均为每月5美元。开销是按照服务器启动的小时数进行比例计算的,因此,如果您创建服务器,将其使用几个小时然后删除,则只需支付几分钱。
免费替代方案基于您可以在自己的计算机上运行的虚拟机。要使用此选项,请在计算机上安装Vagrant和VirtualBox,然后创建一个名为Vagrantfile的文件并用以下内容来描述虚拟机的规格:
Vagrantfile:Vagrant配置。
Vagrant.configure("2") do |config|
config.vm.box = "ubuntu/xenial64"
config.vm.network "private_network", ip: "192.168.33.10"
config.vm.provider "virtualbox" do |vb|
vb.memory = "1024"
end
end
该文件配置了一个带有1GB RAM的Ubuntu 16.04服务器,你可以用其IP地址192.168.33.10来访问该服务器。 要创建服务器,请运行以下命令:
$ vagrant up
请查阅Vagrant命令行文档,以了解管理虚拟服务器的其他选项。
您的服务器处于后端,因此不会像在您自己的计算机上那样拥有桌面。您将通过SSH客户端连接到服务器,并通过命令行对其进行操作。如果您使用的是Linux或Mac OS X,则可能已经安装了OpenSSH。如果您使用的是Microsoft Windows,则Cygwin,Git和 Windows Subsystem for Linux 都提供OpenSSH,因此您可以安装这些选项中的任何一个。
如果您使用的是来自第三方提供商的虚拟服务器,则在创建服务器时会为其提供IP地址。您可以使用以下命令打开与该服务器的终端会话:
$ ssh root@
系统将提示您输入密码。密码已在创建服务器后自动生成并显示给你,或者你自己指定了密码。
如果使用的是Vagrant VM,则可以使用以下命令打开终端会话:
$ vagrant ssh
如果使用Windows并具有Vagrant VM,请注意,你需要从可以调用ssh
命令的shell运行上述命令。
如果您使用的是Vagrant VM,则可以跳过本节,因为Vagrant已将VM正确配置为使用名为ubuntu
的非root用户帐户,而无需密码。
如果您使用的是虚拟服务器,建议您创建一个常规用户帐户来进行部署工作,并配置该帐户以不使用密码即可登录,这乍一看似乎是个坏主意,但是您我会发现它不仅更方便,而且更安全。
我将创建一个名为ubuntu
(如果您愿意,可以使用其他名称)的用户帐户。要创建此用户帐户,请使用ssh
上一节中的说明登录到服务器的root帐户,然后键入以下命令来创建用户,赋予用户sudo
权限并最终切换到该用户:
$ adduser --gecos "" ubuntu
$ usermod -aG sudo ubuntu
$ su ubuntu
现在,我将配置此新ubuntu
帐户以使用公共密钥身份验证,以便您无需输入密码即可登录。
先不管服务器上打开的终端会话,然后在本地计算机上启动第二个终端。 如果你使用的是Windows,这需要是可以访问ssh
命令的终端,所以它可能是一个bash
或者类似的提示符的终端,而不是本地的Windows终端。 在该终端会话中,检查 ~/.ssh 目录的内容:
$ ls ~/.ssh
id_rsa id_rsa.pub
如果目录列表显示的文件名为id_rsa和id_rsa.pub,如上所示,则您已经有了一个密钥。如果没有这两个文件,或者根本没有〜/ .ssh目录,则需要通过运行以下命令(也是OpenSSH工具集的一部分)来创建SSH密钥对:
$ ssh-keygen
该应用将提示您输入一些内容,我建议您通过在所有提示上按Enter接受默认值。如果您知道自己在做什么,而又想做其他设置,那么您当然可以。
运行此命令后,您应该有上面列出的两个文件。文件id_rsa.pub是您的公共密钥,它是您将提供给第三方以识别您身份的文件。该id_rsa文件是你的私钥,不应与任何人共享。
现在,您需要将公钥配置为服务器中的授权主机。在您自己的计算机上打开的终端上,将公共密钥打印到屏幕上:
$ cat ~/.ssh/id_rsa.pub
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCjw....F8Xv4f/0+7WT miguel@miguelspc
这将是一个非常长的字符序列,可能跨越多行。您需要将此数据复制到剪贴板,然后切换回远程服务器上的终端,在该终端上您将发出以下命令来存储公钥:
$ echo >> ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys
无密码登录现在应该可以正常工作了。背后的逻辑是,你机器上的ssh
会用私钥执行加密操作来向服务器标识自己。然后,服务器使用您的公共密钥验证该操作是否有效。
现在,您可以注销ubuntu
会话,然后从会话中注销root
,然后尝试使用以下方法直接登录到ubuntu
帐户:
$ ssh ubuntu@
这次您不必输入密码!
为了最大程度地降低服务器受到攻击的风险,您可以采取一些步骤,直接关闭许多潜在的后门,攻击者可以通过这些后门进行访问。
我要进行的第一个更改是禁用root用户通过SSH登录。现在,您可以无密码访问ubuntu
帐户,并且可以通过sudo
来从该帐户运行管理员命令,因此实际上不需要暴露root帐户。要禁用root登录,您需要在服务器上编辑/etc/ssh/sshd_config文件。您可能已在服务器中安装了vi
和nano
文本编辑器,可用于编辑文件(如果您对这两个文件都不熟悉,请先尝试nano
)。由于SSH配置对普通用户是不可访问的,所以你需要在编辑器命令前添加sudo
(即sudo vi /etc/ssh/sshd_config
)。 您需要在此文件中更改一行:
/etc/ssh/sshd_config:禁用root登录。
PermitRootLogin no
请注意,要进行此更改,您需要找到以PermitRootLogin
开头的行(找不到就新建一行),并将该值更改为no
。
下一个更改在同一文件中。现在,我将禁用所有帐户的密码登录。您已设置了无密码登录名,因此根本不需要允许密码。如果您对完全禁用密码感到不安,可以跳过此更改,但是对于生产服务器,这是一个不错的主意,因为攻击者一直在尝试在所有服务器上尝试使用随机的帐户名和密码,并希望能中奖。要禁用密码登录,请在 /etc/ssh/sshd_config 中更改以下行:
/etc/ssh/sshd_config :禁用密码登录。
PasswordAuthentication no
完成SSH配置的编辑后,需要重新启动SSH服务以使更改生效:
$ sudo service ssh restart
我要进行的第三项更改是安装防火墙。这是一款阻止在未明确启用的任何端口上访问服务器的软件:
$ sudo apt-get install -y ufw
$ sudo ufw allow ssh
$ sudo ufw allow http
$ sudo ufw allow 443/tcp
$ sudo ufw --force enable
$ sudo ufw status
这些命令将安装ufw(简单防火墙),并将其配置为仅允许端口22(ssh),80(http)和443(https)上的外部通信。不允许使用任何其他端口。
如果您遵循我的建议并为服务器配置了Ubuntu 16.04版本,那么您的系统将完全支持Python 3.5,因此这是我将用于部署的Python版本。
基本的Python解释器可能已预先安装在您的服务器上,但是可能没有一些额外的软件包,而且Python之外还有一些其他软件包可用于创建健壮的生产环境部署。对于数据库服务器,我将从SQLite切换到MySQL。postfix包是一个邮件传输代理,我将使用它来发送电子邮件。Supervisor工具将监视Flask服务器进程,并在其崩溃时自动重启,并当Supervisor服务重启后自动启动其监视的服务。 Nginx服务器将接受来自外部世界的所有请求,并将它们转发给应用程序。 最后,我将使用git来从git仓库下载应用程序。
$ sudo apt-get -y update
$ sudo apt-get -y install python3 python3-venv python3-dev
$ sudo apt-get -y install mysql-server postfix supervisor nginx git
这些安装大多在无人值守的情况下运行,但是在运行第三条安装语句时,有时会提示您选择MySQL服务的root密码,并且还会询问一些有关postfix软件包安装的问题。您可以接受其默认答案。
请注意,对于此部署,我选择不安装Elasticsearch。此服务需要大量的RAM,因此只有在拥有2GB以上RAM的大型服务器时,此服务才可行。为避免服务器内存不足的问题,我将取消搜索功能。如果您有足够大的服务器,则可以从Elasticsearch站点下载官方.deb软件包,然后按照其安装说明将其添加到服务器中。请注意,Ubuntu 16.04软件包存储库中可用的Elasticsearch软件包太旧,无法使用,您需要版本6.x或更高版本。
我还注意到,默认安装的postfix可能不足以在生产环境中发送电子邮件。 为了避免垃圾邮件和恶意邮件,很多服务器都要求发件人服务器通过安全扩展标识自己,这意味着至少你必须拥有与你的服务器相关联的域名。 如果你想了解如何完全配置电子邮件服务器以使其通过标准安全测试,请参阅以下Digital Ocean的指南:
现在,我将用于git
从我的GitHub存储库下载Microblog源代码。如果您不熟悉git源代码控制,我建议你阅读git for beginners。
要将应用下载到服务器,请确保您位于ubuntu
用户的主目录中,然后运行:
$ git clone https://github.com/miguelgrinberg/microblog
$ cd microblog
$ git checkout v0.17
这会将代码安装在您的服务器上,并将其同步到本章。如果将本教程代码的版本保留在自己的git存储库中,则可以将存储库URL更改为自己的git,在这种情况下,可以跳过git checkout
命令。
现在,我需要创建一个虚拟环境,并使用所所有的包依赖项来填充它,我将它们方便地保存到第15章中的requirements.txt文件中:
$ python3 -m venv venv
$ source venv/bin/activate
(venv) $ pip install -r requirements.txt
除了requirements.txt中的包之外,我还将使用此生产部署指定的两个包,因此它们不包含在requirements.txt文件中。 gunicorn
软件包是Python应用程序的生产Web服务器。 pymysql
软件包包含MySQL驱动程序,它使SQLAlchemy能够与MySQL数据库一起工作:
(venv) $ pip install gunicorn pymysql
我需要创建一个.env文件,其中包含所有需要的环境变量:
/home/ubuntu/microblog/.env:环境配置。
SECRET_KEY=52cb883e323b48d78a0a36e8e951ba4a
MAIL_SERVER=localhost
MAIL_PORT=25
DATABASE_URL=mysql+pymysql://microblog:@localhost:3306/microblog
MS_TRANSLATOR_KEY=
该.env文件与我在第15章中显示的示例最相似,但是我使用了一个随机字符串作为SECRET_KEY
。为了生成此随机字符串,我使用了以下命令:
python3 -c "import uuid; print(uuid.uuid4().hex)"
对于DATABASE_URL
变量,我定义了一个MySQL URL。在下一节中,我将向您展示如何配置数据库。
我需要将FLASK_APP
环境变量设置为应用的入口点,以使flask
命令起作用,但是在解析.env文件之前需要此变量,因此需要手动设置。为了避免每次都要设置它,我将其添加到ubuntu
帐户的〜/.profile文件的底部,以便每次登录时自动设置:
$ echo "export FLASK_APP=microblog.py" >> ~/.profile
如果您注销并重新登录,现在FLASK_APP
将为您设置就已经设置好了。您可以通过运行flask --help
确认它是否已经设置好了。如果帮助消息显示了应用添加的translate
命令,则说明已找到该应用。
既然flask
命令已经可以使用了,我就可以编译语言翻译了:
(venv) $ flask translate compile
我在开发过程中使用的sqlite数据库非常适合简单的应用程序,但是当部署功能强大的Web服务器(可能一次需要处理多个请求)时,最好使用功能更强大的数据库。因此,我将建立一个名为microblog
的MySQL数据库。
为了管理数据库服务器,我将使用mysql
命令,该命令应该已经安装在您的服务器上:
$ mysql -u root -p
Enter password:
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 6
Server version: 5.7.19-0ubuntu0.16.04.1 (Ubuntu)
Copyright (c) 2000, 2017, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql>
请注意,您将需要输入在安装MySQL时选择的MySQL超级用户密码,才能访问MySQL命令提示符。
这些命令将创建一个名为microblog
的新数据库,以及一个具有完全访问权限的同名用户:
mysql> create database microblog character set utf8 collate utf8_bin;
mysql> create user 'microblog'@'localhost' identified by '';
mysql> grant all privileges on microblog.* to 'microblog'@'localhost';
mysql> flush privileges;
mysql> quit;
你将需要用你选择的密码来替换
。这将是microblog
数据库用户的密码,因此最好不要使用您为root用户选择的密码。microblog
用户的密码需要与你包含在 .env 文件中的DATABASE_URL
变量中的密码相匹配。
如果您的数据库配置正确,你现在应该能够运行数据库迁移以创建所有的表:
(venv) $ flask db upgrade
在继续操作之前,请确保上述命令已完成且不会产生任何错误。
当使用flask run
运行服务器时,您使用的是Flask随附的Web服务器。该服务器在开发过程中非常有用,但不是用于生产服务器的好选择,因为它不考虑性能和稳健性。 取而代之,我决定使用gunicorn,它是一个纯粹的Python Web服务器,但与Flask不同,它是一个支持高并发的强大生产服务器,同时它也非常容易使用。
要在gunicorn下启动Microblog,可以使用以下命令:
(venv) $ gunicorn -b localhost:8000 -w 4 microblog:app
-b
选项告诉gunicorn在哪里侦听请求,我将其设置为8000端口的内部网络接口。 在没有外部访问的情况下运行Python Web应用程序通常是一个好主意,然后还需要一个非常快速的Web服务器,它可以优化来自客户端的所有静态文件的请求。 这个快速的Web服务器将直接提供静态文件,并将用于应用的任何请求转发到内部服务器。在下一节中,我将向您展示如何将nginx设置为面向公众的服务器。
-w
选项配置gunicorn将运行多少worker。 拥有四个进程可以让应用程序同时处理多达四个客户端,这对于Web应用程序通常足以处理大量客户端请求,因为并非所有客户端都在不断请求内容。 根据服务器的RAM大小,你可能需要调整worker数量,以免内存不足。
microblog:app
参数告诉gunicorn如何加载应用实例。冒号之前的名称是包含应用的模块,冒号之后的名称是此应用的名称。
虽然gunicorn的设置非常简单,但是从命令行运行服务器实际上对于生产服务器不是一个好的解决方案。我要做的是让服务器在后台运行,并在不断的监视下进行,因为如果服务器由于某种原因崩溃并退出,我想确保自动启动新服务器来代替它。而且我还想确保如果重新启动计算机,则服务器在启动时会自动运行,而无需人工登录和启动。我将使用上面安装的supervisor包来执行此操作。
Supervisor使用配置文件定义它要监视什么程序以及如何在必要时重新启动它们。 配置文件必须存储在 /etc/supervisor/conf.d 中。 这是Microblog的配置文件,我将其称为microblog.conf:
/etc/supervisor/conf.d/microblog.conf:Supervisor配置。
[program:microblog]
command=/home/ubuntu/microblog/venv/bin/gunicorn -b localhost:8000 -w 4 microblog:app
directory=/home/ubuntu/microblog
user=ubuntu
autostart=true
autorestart=true
stopasgroup=true
killasgroup=true
command
,directory和
user
设置告诉supervisor如何运行应用程序。autostart
和autorestart
设置会使microblog自动重新启动。在stopasgroup
和killasgroup
选项确保当supervisor需要停止应用程序来重新启动它时,它仍然会调度成顶级gunicorn进程的子进程。
写入此配置文件后,必须重新加载supervisor服务才能将其导入:
$ sudo supervisorctl reload
就像这样,gunicorn Web服务器应该已启动和运行,并处于监控之中!
由gunicorn启动的microblog应用服务器现在正在本地端口8000上运行。我现在需要将该应用暴露给外界,为了使面向公众的web服务器能够被访问,我在防火墙上打开了两个端口(80和443)来处理应用程序的Web通信。
我希望这是一个安全的部署,所以我要配置端口80将所有流量转发到将要加密的端口443。 我将首先创建一个SSL证书。创建一个自签名SSL证书,这对于测试是可以的,但对于真正的部署不太好,因为Web浏览器会警告用户,证书不是由可信证书颁发机构颁发的。 创建microblog的SSL证书的命令是:
$ mkdir certs
$ openssl req -new -newkey rsa:4096 -days 365 -nodes -x509 \
-keyout certs/key.pem -out certs/cert.pem
该命令将要求你提供关于应用程序和你自己的一些信息。 这些信息将包含在SSL证书中,如果用户请求查看它,Web浏览器则会向用户显示它们。上述命令的结果将是名为key.pem和cert.pem的两个文件,我将其放置在Microblog根目录的certs子目录中。
要让nginx服务一个网站,您需要为其编写一个配置文件。在大多数nginx安装中,此文件必须位于/etc/nginx/sites-enabled目录中。Nginx在这个位置安装了一个我不需要的测试站点,所以我将首先删除它:
$ sudo rm /etc/nginx/sites-enabled/default
在下面,您可以看到Microblog的nginx配置文件,该文件位于 /etc/nginx/sites-enabled/microblog 中:
/etc/nginx/sites-enabled/microblog :Nginx配置。
server {
# listen on port 80 (http)
listen 80;
server_name _;
location / {
# redirect any requests to the same URL but on https
return 301 https://$host$request_uri;
}
}
server {
# listen on port 443 (https)
listen 443 ssl;
server_name _;
# location of the self-signed SSL certificate
ssl_certificate /home/ubuntu/microblog/certs/cert.pem;
ssl_certificate_key /home/ubuntu/microblog/certs/key.pem;
# write access and error logs to /var/log
access_log /var/log/microblog_access.log;
error_log /var/log/microblog_error.log;
location / {
# forward application requests to the gunicorn server
proxy_pass http://localhost:8000;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /static {
# handle static files directly, without forwarding to the application
alias /home/ubuntu/microblog/app/static;
expires 30d;
}
}
Nginx配置绝非易事,但我添加了一些注释,以便您至少知道每个部分的功能。如果您想获得有关特定指令的信息,请查阅nginx官方文档。
添加此文件后,需要告诉nginx重新加载配置以将其激活:
$ sudo service nginx reload
现在应用程序应该部署成功了。在Web浏览器中,您可以键入服务器的IP地址(如果使用的是Vagrant VM,则为192.168.33.10),然后该服务器将连接到应用程序。由于您使用的是自签名证书,因此您将从网络浏览器中收到一条警告,必须将其解除。
针对您自己的项目按照上述说明完成部署后,强烈建议您使用真实的证书替换自签名证书,以便浏览器不会警告用户有关您的站点的信息。为此,您首先需要购买一个域名并将其配置为指向服务器的IP地址。一旦你有一个域名,你可以申请一个免费的Let's Encrypt SSL证书。 我在博客上写了一篇关于如何通过HTTPS运行你的Flask应用程序的详细文章。
关于基于Linux的部署,我要讨论的最后一个主题是如何处理应用升级。应用源代码通过git
安装在服务器中,因此,每当您要将应用程序升级到最新版本时,都可以运行git pull
以下载自上次部署以来所做的新提交。
当然,下载新版本的代码不会引起升级。当前正在运行的服务器进程将继续使用已读取并存储在内存中的旧代码运行。要触发升级,您必须停止当前服务器并启动新服务器,以强制再次读取所有代码。
通常,进行升级比仅重启服务器要复杂得多。您可能需要应用数据库迁移或编译新的语言翻译,因此实际上,执行升级的过程涉及以下命令序列:
(venv) $ git pull # download the new version
(venv) $ sudo supervisorctl stop microblog # stop the current server
(venv) $ flask db upgrade # upgrade the database
(venv) $ flask translate compile # upgrade the translations
(venv) $ sudo supervisorctl start microblog # start a new server
树莓派是具有非常低的功耗低成本革命性的小台Linux计算机,因此它是托管家庭Web服务器的理想设备,可以每天24小时在线而不会占用您的桌面电脑或笔记本电脑。有几个Linux发行版可以在树莓派上运行。 我的选择是Raspbian,这是树莓派基金会的官方发行版。
为了准备树莓派的环境,我将安装一个新的Raspbian版本。我将使用Raspbian Stretch Lite的2017年9月版,但是到您阅读此书时,可能会有较新的版本发布,因此请查看官方下载页面以获取最新版本。
Raspbian镜像需要安装在SD卡上,然后插入树莓派,以便它启动时可以识别到。 在树莓派站点上可以查看到从Windows,Mac OS X和Linux将Raspbian镜像复制到SD卡的方法。
首次启动树莓派时,请在连接键盘和显示器的情况下进行操作,以便进行设置。至少应启用SSH,以便可以从计算机登录以更轻松地执行部署任务。
与Ubuntu一样,Raspbian是Debian的衍生产品,因此上面有关Ubuntu Linux的说明在大多数情况下也适用于树莓派。但是,如果您打算在家庭网络上运行没有外部访问权限的小型应用程序,则可以决定跳过某些步骤。例如,您可能不需要防火墙或无密码登录。您可能想在如此小的计算机上使用SQLite而不是MySQL。 你可以选择不使用nginx,并且让gunicorn服务器直接监听来自客户端的请求。 你可能只想要一个gunicorn worker进程。 Supervisor服务对于确保应用程序始终处于运行状态非常有用,因此我建议你仍然在树莓派上使用它。
原文链接:https://blog.miguelgrinberg.com/post/the-flask-mega-tutorial-part-xvii-deployment-on-linux