1. Day 15 - 部署Web App
    2. 阅读: 69793
    3. 作为一个合格的开发者,在本地环境下完成开发还远远不够,我们需要把Web App部署到远程服务器上,这样,广大用户才能访问到网站。
    4. 很多做开发的同学把部署这件事情看成是运维同学的工作,这种看法是完全错误的。首先,最近流行DevOps理念,就是说,开发和运维要变成一个整体。其次,运维的难度,其实跟开发质量有很大的关系。代码写得垃圾,运维再好也架不住天天挂掉。最后,DevOps理念需要把运维、监控等功能融入到开发中。你想服务器升级时不中断用户服务?那就得在开发时考虑到这一点。
    5. 下面,我们就来把awesome-python3-webapp部署到Linux服务器。
    6. 搭建Linux服务器
    7. 要部署到Linux,首先得有一台Linux服务器。要在公网上体验的同学,可以在AmazonAWS申请一台EC2虚拟机(免费使用1年),或者使用国内的一些云服务器,一般都提供Ubuntu Server的镜像。想在本地部署的同学,请安装虚拟机,推荐使用VirtualBox
    8. 我们选择的Linux服务器版本是Ubuntu Server 14.04 LTS,原因是apt太简单了。如果你准备使用其他Linux版本,也没有问题。
    9. Linux安装完成后,请确保ssh服务正在运行,否则,需要通过apt安装:
    10. $ sudo apt-get install openssh-server
    11. 有了ssh服务,就可以从本地连接到服务器上。建议把公钥复制到服务器端用户的.ssh/authorized_keys中,这样,就可以通过证书实现无密码连接。
    12. 部署方式
    13. 利用Python自带的asyncio,我们已经编写了一个异步高性能服务器。但是,我们还需要一个高性能的Web服务器,这里选择Nginx,它可以处理静态资源,同时作为反向代理把动态请求交给Python代码处理。这个模型如下:
    14. Nginx负责分发请求:
    15. 在服务器端,我们需要定义好部署的目录结构:
    16. /
    17. +- srv/
    18. +- awesome/ <-- Web App根目录
    19. +- www/ <-- 存放Python源码
    20. | +- static/ <-- 存放静态资源文件
    21. +- log/ <-- 存放log
    22. 在服务器上部署,要考虑到新版本如果运行不正常,需要回退到旧版本时怎么办。每次用新的代码覆盖掉旧的文件是不行的,需要一个类似版本控制的机制。由于Linux系统提供了软链接功能,所以,我们把www作为一个软链接,它指向哪个目录,哪个目录就是当前运行的版本:
    23. Nginxpython代码的配置文件只需要指向www目录即可。
    24. Nginx可以作为服务进程直接启动,但app.py还不行,所以,Supervisor登场!Supervisor是一个管理进程的工具,可以随系统启动而启动服务,它还时刻监控服务进程,如果服务进程意外退出,Supervisor可以自动重启服务。
    25. 总结一下我们需要用到的服务有:
    26. Nginx:高性能Web服务器+负责反向代理;
    27. Supervisor:监控服务进程的工具;
    28. MySQL:数据库服务。
    29. Linux服务器上用apt可以直接安装上述服务:
    30. $ sudo apt-get install nginx supervisor python3 mysql-server
    31. 然后,再把我们自己的Web App用到的Python库安装了:
    32. $ sudo pip3 install jinja2 aiomysql aiohttp
    33. 在服务器上创建目录/srv/awesome/以及相应的子目录。
    34. 在服务器上初始化MySQL数据库,把数据库初始化脚本schema.sql复制到服务器上执行:
    35. $ mysql -u root -p < schema.sql
    36. 服务器端准备就绪。
    37. 部署
    38. FTP还是SCP还是rsync复制文件?如果你需要手动复制,用一次两次还行,一天如果部署50次不但慢、效率低,而且容易出错。
    39. 正确的部署方式是使用工具配合脚本完成自动化部署。Fabric就是一个自动化部署工具。由于Fabric是用Python 2.x开发的,所以,部署脚本要用Python 2.7来编写,本机还必须安装Python 2.7版本。
    40. 要用Fabric部署,需要在本机(是开发机器,不是Linux服务器)安装Fabric
    41. $ easy_install fabric
    42. Linux服务器上不需要安装FabricFabric使用SSH直接登录服务器并执行部署命令。
    43. 下一步是编写部署脚本。Fabric的部署脚本叫fabfile.py,我们把它放到awesome-python-webapp的目录下,与www目录平级:
    44. awesome-python-webapp/
    45. +- fabfile.py
    46. +- www/
    47. +- ...
    48. Fabric的脚本编写很简单,首先导入FabricAPI,设置部署时的变量:
    49. # fabfile.py
    50. import os, re
    51. from datetime import datetime
    52. # 导入Fabric API:
    53. from fabric.api import *
    54. # 服务器登录用户名:
    55. env.user = 'michael'
    56. # sudo用户为root:
    57. env.sudo_user = 'root'
    58. # 服务器地址,可以有多个,依次部署:
    59. env.hosts = ['192.168.0.3']
    60. # 服务器MySQL用户名和口令:
    61. db_user = 'www-data'
    62. db_password = 'www-data'
    63. 然后,每个Python函数都是一个任务。我们先编写一个打包的任务:
    64. _TAR_FILE = 'dist-awesome.tar.gz'
    65. def build():
    66. includes = ['static', 'templates', 'transwarp', 'favicon.ico', '*.py']
    67. excludes = ['test', '.*', '*.pyc', '*.pyo']
    68. local('rm -f dist/%s' % _TAR_FILE)
    69. with lcd(os.path.join(os.path.abspath('.'), 'www')):
    70. cmd = ['tar', '--dereference', '-czvf', '../dist/%s' % _TAR_FILE]
    71. cmd.extend(['--exclude=\'%s\'' % ex for ex in excludes])
    72. cmd.extend(includes)
    73. local(' '.join(cmd))
    74. Fabric提供local('...')来运行本地命令,with lcd(path)可以把当前命令的目录设定为lcd()指定的目录,注意Fabric只能运行命令行命令,Windows下可能需要Cgywin环境。
    75. awesome-python-webapp目录下运行:
    76. $ fab build
    77. 看看是否在dist目录下创建了dist-awesome.tar.gz的文件。
    78. 打包后,我们就可以继续编写deploy任务,把打包文件上传至服务器,解压,重置www软链接,重启相关服务:
    79. _REMOTE_TMP_TAR = '/tmp/%s' % _TAR_FILE
    80. _REMOTE_BASE_DIR = '/srv/awesome'
    81. def deploy():
    82. newdir = 'www-%s' % datetime.now().strftime('%y-%m-%d_%H.%M.%S')
    83. # 删除已有的tar文件:
    84. run('rm -f %s' % _REMOTE_TMP_TAR)
    85. # 上传新的tar文件:
    86. put('dist/%s' % _TAR_FILE, _REMOTE_TMP_TAR)
    87. # 创建新目录:
    88. with cd(_REMOTE_BASE_DIR):
    89. sudo('mkdir %s' % newdir)
    90. # 解压到新目录:
    91. with cd('%s/%s' % (_REMOTE_BASE_DIR, newdir)):
    92. sudo('tar -xzvf %s' % _REMOTE_TMP_TAR)
    93. # 重置软链接:
    94. with cd(_REMOTE_BASE_DIR):
    95. sudo('rm -f www')
    96. sudo('ln -s %s www' % newdir)
    97. sudo('chown www-data:www-data www')
    98. sudo('chown -R www-data:www-data %s' % newdir)
    99. # 重启Python服务和nginx服务器:
    100. with settings(warn_only=True):
    101. sudo('supervisorctl stop awesome')
    102. sudo('supervisorctl start awesome')
    103. sudo('/etc/init.d/nginx reload')
    104. 注意run()函数执行的命令是在服务器上运行,with cd(path)和with lcd(path)类似,把当前目录在服务器端设置为cd()指定的目录。如果一个命令需要sudo权限,就不能用run(),而是用sudo()来执行。
    105. 配置Supervisor
    106. 上面让Supervisor重启awesome的命令会失败,因为我们还没有配置Supervisor呢。
    107. 编写一个Supervisor的配置文件awesome.conf,存放到/etc/supervisor/conf.d/目录下:
    108. [program:awesome]
    109. command = /srv/awesome/www/app.py
    110. directory = /srv/awesome/www
    111. user = www-data
    112. startsecs = 3
    113. redirect_stderr = true
    114. stdout_logfile_maxbytes = 50MB
    115. stdout_logfile_backups = 10
    116. stdout_logfile = /srv/awesome/log/app.log
    117. 配置文件通过[program:awesome]指定服务名为awesomecommand指定启动app.py
    118. 然后重启Supervisor后,就可以随时启动和停止Supervisor管理的服务了:
    119. $ sudo supervisorctl reload
    120. $ sudo supervisorctl start awesome
    121. $ sudo supervisorctl status
    122. awesome RUNNING pid 1401, uptime 5:01:34
    123. 配置Nginx
    124. Supervisor只负责运行app.py,我们还需要配置Nginx。把配置文件awesome放到/etc/nginx/sites-available/目录下:
    125. server {
    126. listen 80; # 监听80端口
    127. root /srv/awesome/www;
    128. access_log /srv/awesome/log/access_log;
    129. error_log /srv/awesome/log/error_log;
    130. # server_name awesome.liaoxuefeng.com; # 配置域名
    131. # 处理静态文件/favicon.ico:
    132. location /favicon.ico {
    133. root /srv/awesome/www;
    134. }
    135. # 处理静态资源:
    136. location ~ ^\/static\/.*$ {
    137. root /srv/awesome/www;
    138. }
    139. # 动态请求转发到9000端口:
    140. location / {
    141. proxy_pass http://127.0.0.1:9000;
    142. proxy_set_header X-Real-IP $remote_addr;
    143. proxy_set_header Host $host;
    144. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    145. }
    146. }
    147. 然后在/etc/nginx/sites-enabled/目录下创建软链接:
    148. $ pwd
    149. /etc/nginx/sites-enabled
    150. $ sudo ln -s /etc/nginx/sites-available/awesome .
    151. Nginx重新加载配置文件,不出意外,我们的awesome-python3-webapp应该正常运行:
    152. $ sudo /etc/init.d/nginx reload
    153. 如果有任何错误,都可以在/srv/awesome/log下查找NginxApp本身的log。如果Supervisor启动时报错,可以在/var/log/supervisor下查看Supervisorlog
    154. 如果一切顺利,你可以在浏览器中访问Linux服务器上的awesome-python3-webapp了:
    155. 如果在开发环境更新了代码,只需要在命令行执行:
    156. $ fab build
    157. $ fab deploy
    158. 自动部署完成!刷新浏览器就可以看到服务器代码更新后的效果。
    159. 友情链接
    160. 嫌国外网速慢的童鞋请移步网易和搜狐的镜像站点:
    161. http://mirrors.163.com/
    162. http://mirrors.sohu.com/
    163. 参考源码
    164. day-15