什么是部署? 部署指的是把程序托管到拥有公网ip的远程服务器(计算机)上,这样程序可才可以被互联网上的所有人通过IP或映射到IP的域名访问到
如果你将程序代码托管在在线代码托管平台比如Github\BitBucket\Gitlab等,那么使用Git部署程序的过程非常简单,和在你自己的电脑上运行程序的基本相同,大致流程如下:
* 在本地执行测试 * 将文件添加到Git仓库并提交(git add & git commit) * 在本地将代码推送到代码托管平台(git push) * 在远程主机上从代码通过平台复制程序仓库 git clone * 创建虚拟环境并安装依赖 * 创建实例文件夹,添加部署特定的配置文件或是创建.env文件存储环境变量并导入。 * 初始化程序和数据库,创建迁移环境 * 使用Web服务器运行程序
## 1.1 更新程序步骤
* 在本地执行测试 * 将文件添加到Git仓库并提交 git add & git commit * 在本地将代码推送到代码托管平台 git push * 在远程文件上从代码托管平台拉去程序仓库 git pull * 如果有依赖变动,或是数据库表结构变动,那么执行依赖安装和数据库迁移操作 * 重启Web服务器
## 2.1 更新程序配置
python# 随机密钥的生成方式有很多。比如,os模块的urandom()方法可以用来生成随机密码
import os
os.urandom((12))
# 如果使用python3.6,可以使用secrets模块提供的token_bytes()、token_hex()、token_urlsafe()方法
import secrets
token_urlsafe(16)
# 或者使用uuid
在PythonAnywhere部署程序时,因为PythonAnywhere使用的数据库服务会在5分钟断开连接,需要将flask_sqlalchemy提供SQLALCHEMY_POOL_RECYCLE配置变量的值设为300一下,用来设置数据库连接池的回收时间,比如:
shellSQLALCHEMY_POOL_RECYCLE=280
## 3.1 设置迁移工具 详情查看5数据库
## 3.2 程序日志
pythonimport logging
from logging.handlers import RotatingFileHandler
def create_app(config_name=None):
register_logger(app)
def register_logger():
app.logger.setLevel(loggging.INFO)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler = RotatingFileHandler('logs/bluelog.log',maxBytes=10*1024*1024,backupCount=10)
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.INFO)
if not app.debug:
app.logger.addHandler()
代码解释:
- 为了让日志记录器记录INFO等级的日志时间,我们首先将app.logger的等级设为INFO。
- 然后创建一个logging.Formatter对象设置日志的输出格式。
- 为避免日积月累产生一个巨大的日志文件,我们使用RotatingFileHandler类(从logging.handlers模块导入)创建一个轮转文件类型的日志处理器,实例化这个类传入日志文件目标路径、最大文件尺寸和备份数量。
- 当日志文件的大小超过实例化传入的maxBytes参数设定的值时(单位为字节byte,我们这里设为10MB),它会循环覆盖之前的记录
- 将backupCount参数设为10次,会依次创建10个日志文件,10个文件全部存满10MB后会开始覆盖之前的文件;
- TimeRotatingFileHandler类,它会根据设定的时间间隔(通过实例化传入参数设定)来定期覆盖日志数据
云部署平台的日志 云部署平台会从stdout(标准输出)和stderr(标准错误输出)收集日志,如果要吧程序部署到云平台上,需要创建一个StreamHanler类型的日志处理器来替代上面的RotatingFileHandler处理器
通过邮件发送关键日志 在生产环境重,低等级的信息可以记录到日志文件重,而关键的信息(比如Error等级以上)则需要通过邮件发送给管理员,以便及时休闲服问题
为了在日志信息重插入出发这个日志事件的请求信息,我们创建一个自定义的RequestFormatter类,它继承自logging.Formatter类,添加了几个自定义字段来插入请求信息。
pythonimport requests
import logging
def register_logger():
class RequestFormatter(logging.Formatter):
def format(self, record):
record.url = requests.url
record.remote_addr = request.remote_addr
return super(RequestFormatter,self).format(record)
request_formatter = RequestFormatter(
'[%(asctime)s] %(remote_addr)s requested %(url)s \n %(levelname)s in %(module)s: %(message)s'
)
使用SMTPHandler类可以创建一个SMTP处理器,传入的参数太多从相应的Flask-Mail配置变量获取。我们将这个邮件日志处理器的等级设为logging.ERROR,当发生Error等级及以上的日志事件时会将日志通过邮件发送给管理员
pythonimport os
import logging
from logging.handlers import SMTPHandler
def register_logger():
mail_handler = SMTPHandler(
mailhost=os.getenv('MAIL_SERVER'),
fromaddr=os.getenv('MAIL_USERNAME'),
toaddrs=os.getenv('BLUELOG_ADMIN_EMAIL'),
subject='Application Error',
credentials=(os.getenv('MAIL_USERNAME'),os.getenv('MAIL_PASSWORD'))
)
mail_handler.setLevel(logging.ERROR)
mail_handler.setFormatter(request_formatter)
if not app.debug:
app.logger.addHandler(mail_handler)
除了传统的日志记录,我们还可以使用第三方错误追踪工具来处理程序中的错误。流行的选择是Sentry,当在程序中集成Sentry后,它可以在程序出现异常时通过我们设置的各种方式发送提醒(除了邮件,还可以集成Slack、Whatsapp、IRC等第三方工具)
更重要的是,我们可以在的控制面板中查看关于这个异常的相关代码、上下文变量的值、函数调用堆栈、以及异常触发的次数、涉及的客户端信息等一系列数据,这能够帮助我们及时找出问题的根源并解决问题。
准备:
我们直接通过命令来运行gunicorn,这并不十分可靠。我们需要一个工具来自动在后台运行它,同时监控它的运行状况,并在系统出错或是重启时自动重启程序。 在/etc/supervisor.d/创建wiki.ini 视个人情况,创建配置文件
shell[program:eric_web] command=pipenv run gunicorn -w 2 -b 127.0.0.1:5001 wsgi:app --preload # 需要执行的命令 directory=/root/program/EricWeb #命令执行的目录 environment =PYTHONPATH="$PYTHONPATH:/root/.local/share/virtualenvs/EricWeb-qMyTWs2e/bin" #环境变量 user=root #用户 stopsignal=INT autostart=true #是否自启动 autorestart=true #是否自动重启 startsecs=60 #自动重启时间间隔(s) stderr_logfile=/var/log/eric_web/webs.err.log #错误日志文件 stdout_logfile=/var/log/eric_web/webs.out.log #输出日志文件
重启
shellsupervisorctl reload
除了命令行工具supervisorctl,Supervisor还提供了Web客户端,你可以通过在/etc/supervisor/supervisord.conf中写入下面的配置来开启:
shell[inet_http_server] ; inet (TCP) server disabled by default port=*:9001 ; (ip_address:port specifier, *:port for all iface) username=yourname ; (default is no username (open server)) password=yourpassword
; 后边的语法代表注释
不过,尽量避免这种方式,先在测试环境测试ok了之后,再提交到生产环境中
都是国外的,而且基于Amazon来实现的,有一定的延迟,不适合国内生产。
本文作者:Eric
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!