前言
在天秤座的纠结性格反复权衡后,最终我还是拥有了自己的第一个个人云主机。我这里选择的是“阿里云学生专享服务器”,在做学生的最后一年有幸享受到9.5/月的优惠价格,还是要感谢阿里爸爸的关爱!选择阿里云的原因没有什么可供参考的,主要是因为我比较信赖阿里系的产品。
系统我选择了比较稳定的CentOS 7.3 64位。以下是一些关键参数信息:
操作系统 | CPU | 内存 | 带宽 |
---|---|---|---|
CentOS 7.3 64位 | 1核 | 2GB | 1Mbps |
以下我将详细记录我这次云主机的配置过程,一来是熟悉一下CentOS系统,二来是试用云主机。相信这次经历会让我学到很多东西。
阅读指南
预备资源
- 拥有一台CentOS系统的服务器或计算机
- 良好的网络
- 至少有一种已会使用的编辑器(本文中使用
vim
)
预备技能
- 基本的Linux系统操作能力
- 基本的Python3编程技能
- 对“管理员权限”、“虚拟环境”、“包管理”、“网络代理”等概念有基本认识
配置过程
0. 创建新用户
$ adduser <user_name> # 创建新用户<user_name>
$ passwd <user_name> # 设置新用户的密码
$ usermod -aG wheel <user_name> # 使<user_name>获得sudo权限
$ su <user_name> # 切换至用户<user_name>
注意:
(1) “<user_name>
”替换成你实际的项目名称;
(2) 你会发现,非root
用户的主目录是/home
的子目录。
(3) 设置密码的步骤是必须的。
1. 更新包管理器yum
及安装相关工具包
$ sudo yum update # 更新yum
$ sudo yum install yum-utils # 安装yum-utils
$ sudo yum groupinstall development # 一并安装一系列常用包
注意:
(1) 由于是个人使用,在root
账号下,以下的sudo
命令其实都是不必需的;
(2) yum
是类似于Ubuntu
下apt-get
般的存在;
(3) 尽管CentOS下shell的命令提示符为#
,但为了此处不与Markdown的记号冲突,我会始终使用$
。
说明:此步骤无报错。每个命令运行成功输出内容均以“Complete!”结尾,中间遇到的确认一律yes(输入y
),当然你也可以选择对于每一个yum
命令添加-y
参数,就会一路yes自动安装下去了,下同。
2. 安装Python3
2.1 基础安装
$ sudo yum install https://centos7.iuscommunity.org/ius-release.rpm # 安装IUM repo
$ sudo yum install python36u # 安装Python3.6
$ sudo yum install python36u-pip # 安装pip包管理
$ sudo yum install python36u-devel # 安装其他相关开发包
注意:
(1) 执行python3.6 -V
在2019.2.19的结果是Python 3.6.7;
(2) 此时如果使用which python3
或which pip3
均会报错,因为它们的实际名字分别是python3.6
和pip3.6
;
(3) 如果你是用其他方式安装的Python3,那么这里你可能会遇到文件冲突(尤其是当Python3的版本不一致的时候);
(4) 不推荐源码编译安装(除非你是娴熟的老司机)。
说明:此步骤无报错。
2.2 (软)链接python3
和pip3
好了,对于在MacOS已经习惯使用python3
和pip3
这样的命令的我们来说,每次都要写3.6
未免也太臃肿了(简单估算一下是要多28.6%的敲击频率),而且一些代码也需要额外做调整,费时费力。我们可以使用ln
来创建(软)链接:
$ ln -s /usr/bin/python3.6 /usr/bin/python3 # (软)链接python3
$ ln -s /usr/bin/pip3.6 /usr/bin/pip3 # (软)链接pip3
这样我们就可以自由地使用python3
和pip3
了。
注意:
(1) 关于ln
命令:在不同的目录,用到相同的文件时,不需要在每一个目录下都放一个必须相同的文件,只须在某个固定的目录,放上该文件,然后在其它的目录下用ln
命令链接它就可以,不必重复地占用磁盘空间。这里只是把它用成类似alias
的功能。
(2) 随手可以把pip
更新到最新版本(不过这里其实不需要完成更新,因为在虚拟环境中如希望使用最新版本pip
,需要在虚拟环境中更新)
$ pip3 install --upgrade pip # 将pip更新至最新
3. 创建虚拟环境
创建虚拟环境的目的是保证业务独立,各依赖库版本控制不受其他项目影响。
Python3中内置了venv
模块来创建虚拟环境。
$ mkdir <project_name> && cd <project_name> # 创建并进入<project_name>目录
$ python3 -m venv <venv_name> # 创建名为<venv_name>的虚拟环境
注意:
(1) “<project_name>
”替换成你实际的项目名称,“<venv_name>
”替换成你实际的虚拟环境名称;
(2) 激活虚拟环境见4.2小节,(在激活状态下)退出虚拟环境使用命令deactivate
;
(3) 有两种方案,第一种就是像这里给出的将虚拟环境相关文件的目录设在所需项目中(项目导向);第二种你可以在主目录下创建目录venvs
,然后将所有的虚拟目录创建在其下(文件管理导向)。
(4) 以下内容如无特别说明,执行的系统命令都在~/<project_name>/
目录中。
4. 搭建服务器
4.1 安装服务器配置所需的工具
$ sudo yum install gcc uwsgi nginx
4.2 在虚拟环境中安装Python程序依赖的第三方库
$ source ~/<project_name>/<venv_name>/bin/activate # 激活虚拟环境<venv_name>
$ pip3 install requests beautifulsoup4 lxml flask gunicorn werobot cryptography
注意:
(1) 以上Pypi安装的库中:
requests
,beautifulsoup4
和lxml
:爬虫业务相关(非必需),
flask
:所采用的Web框架(非必需),
gunicorn
:是一个高性能的Python WSGI UNIX HTTP服务器(必需),
werobot
:此处开发微信公众号的核心库(必需),
cryptography
:当你在微信后台基本配置选择的消息传输方式是“兼容模式”或“安全模式”时,WeRoBot所需要的密码库(推荐,非必需)。
(2) 似乎CentOS7系统自带了nginx
,因为后来我which nginx
得到的是/usr/sbin/nginx
,而其他通过yum
安装的命令工具都在/usr/bin/
下。
说明:此步骤无报错。cryptography
库是我后来调试时根据异常信息后才知道要安装的。另外,假如你没有将pip
更新到最新,会看到提示信息:
You are using pip version 9.0.1, however version 19.0.2 is available.
You should consider upgrading via the 'pip install --upgrade pip' command.
强迫症都会更新的。
4.3 配置Nginx反向代理
4.3.1 创建项目的Nginx配置文件
$ sudo vim /etc/nginx/conf.d/<project_name>.conf # 创建项目所需的nginx配置文件
在所打开的空文件中完成监听及反向代理的配置(没有注释的部分我也还不太懂,按照Flask官网教程写的),:wq
保存关闭。
server {
listen 80;
server_name <我的ECS公网ip>; # 有域名的朋友这里填域名
access_log /var/log/nginx/access.log; # 设置正常通信日志
error_log /var/log/nginx/error.log; # 设置报错日志
location /<project_name>/ {
proxy_pass http://127.0.0.1:8000; # 反向代理Gunicorn本地的服务地址
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
4.3.2 启动Nginx服务
$ sudo systemctl start nginx
$ sudo systemctl enable nginx
每当你修改任意的Nginx配置文件后,都需要重启nginx
来使配置生效:
$ sudo systemctl restart nginx
注意:
(1) “<project_name>
”替换成你实际的项目名称,“<我的ECS公网ip>”替换成你的公网ip或外部域名;
(2) 查看Nginx状态使用命令sudo systemctl status nginx
;要停止Nginx服务,使用sudo service nginx stop
。
说明:这个地方卡了我很长的时间,由于是第一次部署服务器和第一次开发微信公众号,甚至定位问题出现在这里都费了很多工夫。特别要注意配置文件中server_name
和location
这里,参看Nginx: Server names,特别是当查看Nginx状态时遇到suspicious symbol
的异常时。
(3) [2019-05-20补充] 即使在/etc/nginx/conf.d
中,也不要重复设置server
,尤其是同一个ip,否则会造成地址冲突,导致服务启动失败。(这里的2条补充是由于我遇到了修改nginx后无法恢复公众号服务的问题)
(4) [2019-05-20补充] sudo nginx -t
用来检查你的配置文件是否正确。每次更新nginx配置文件后,要在restart
和enable
前使配置重新加载sudo nginx -s reload
。如果你做了以上这些仍然无法恢复你的项目,尝试去腾讯微信公众平台去“停用”并重新“启用”服务器配置。
4.4 部署一个"Hello World!"的Flask-WeRoBot项目
4.4.1 创建一个robot.py
,用来管理你的机器人(自动回复程序)
from werobot import WeRoBot
robot = WeRoBot(
token='<我的令牌>',
encoding_aes_key='<我的消息加解密密钥>',
app_id='<我的开发者ID>'
)
@robot.handler
def hello(message):
return 'Hello World!'
注意:token
和encoding_aes_key
务必与微信公众平台设置的“令牌(Token)”和“消息加解密密钥(EncodingAESKey)”保持一致(前者是自定义的,后者是提交服务器设置时微信那边随机生成的)
说明:在WeRobot的官方文档和很多网上博客资料的“Hello World”中没有强调encoding_aes_key
参数,因为他们默认在微信公众平台设置选择的是“明文模式”。
4.4.2 创建一个app.py
,作为主应用程序
from flask import Flask
from robot import robot
from werobot.contrib.flask import make_view
app = Flask(__name__)
app.add_url_rule(rule='/<project_name>/', # WeRoBot挂载地址
endpoint='werobot', # Flask的endpoint
view_func=make_view(robot),
methods=['GET', 'POST'])
if __name__ == '__main__':
app.run()
注意:rule
参数的值结尾处的/
必须有,因为要同Nginx配置信息以及微信设置的URL保持一致。
4.5 启动Gunicorn服务器
4.5.1 尝试启动Gunicorn服务器
# 最简单的命令方式(默认绑定的是127.0.0.1:8000)
$ gunicorn app:app
# 或者设置一些参数的方式
$ gunicorn -w 3 -b 127.0.0.1:8000 app:app
这时你的终端就会显示服务器的监听状态,类似于下面这样:
[2019-02-28 12:13:54 +0800] [16859] [INFO] Starting gunicorn 19.9.0
[2019-02-28 12:13:54 +0800] [16859] [INFO] Listening at: http://127.0.0.1:8000 (16859)
[2019-02-28 12:13:54 +0800] [16859] [INFO] Using worker: sync
[2019-02-28 12:13:54 +0800] [16862] [INFO] Booting worker with pid: 16862
[2019-02-28 12:13:54 +0800] [16863] [INFO] Booting worker with pid: 16863
[2019-02-28 12:13:54 +0800] [16864] [INFO] Booting worker with pid: 16864
然后打开网页浏览器,输入url:http://<我的ECS公网ip>/<project_name>/
,如果你看到一个中心标题为“这是一个WeRoBot应用”的页面而非“404”、“502”这种错误页面,说明你的服务器启动成功了(Nginx反代理也设置正确了)。退出Control + c
。
4.5.2 创建WSGI入口文件
$ sudo vim wsgi.py
文件内容如下:
from app import app
if __name__ == '__main__':
app.run()
这个文件存在的意义是业务逻辑分离。
现在启动Gunicorn服务器就变为
$ gunicorn wsgi:app
# 或
$ gunicorn -w 3 -b 127.0.0.1:8000 wsgi:app
5. 完成微信公众平台设置
这里默认你已经完成了微信公众号的申请。
登录微信公众平台后,在左侧下方找到 开发 >> 基本配置 >> 修改配置,然后填写服务器信息,根据本文的上下文约定,填写的内容应该符合这个格式:
项目 | 内容 |
---|---|
URL | http://<我的ECS公网ip>/<project_name>/ |
Token | <我的令牌> |
EncodingAESKey | 操作:选择随机生成 |
消息加解密方式 | 操作:选择安全(或明文/兼容)模式 |
随后点击提交完成设置。
注意:在点击提交前请确定服务器已启动。EncodingAESKey处的字符串对应上文中的“<我的消息加解密密钥>”
6. 创建Systemd Unit File
这个是为了在CentOS系统重启后自动启动我们的Gunicorn服务器以恢复Flask项目,同时在后台守护Gunicorn运行的进程(也就是说即使你登出后依然保持运行状态)。我们只需要创建相应的服务文件(.service
):
$ sudo vim /etc/systemd/system/<project_name>.service
添加文件如下内容保存退出。
[Unit]
Description=Gunicorn instance to serve <project_name>
After=network.target
[Service]
User=<user_name>
Group=nginx
WorkingDirectory=/home/<user_name>/<project_name>
Environment="PATH=/home/<user_name>/<project_name>/<venv_name>/bin"
ExecStart=/home/<user_name>/<project_name>/<venv_name>/bin/gunicorn --workers 3 --bind 127.0.0.1:8000 wsgi:app
[Install]
WantedBy=multi-user.target
接下来启动我们的<project_name>
服务
$ sudo systemctl daemon-reload # 使服务文件生效
$ sudo systemctl start <project_name> # 启动<project_name>服务
$ sudo systemctl enable <project_name> # 使<project_name>服务可用
注意:查看状态使用命令sudo systemctl status <project_name>
;停止服务使用sudo service <project_name> stop
。
说明:我们这里不采用其他文档或资料中提到的守护程序Supervisor,因为CentOS7自带的systemd足够强大易用,省去了安装新工具的麻烦(也节省了空间)。
7. 测试
进入公众号回复任意消息,如果一切顺利的话,你将收到文本消息“Hello World!”
大功告成!
后续就是移植旧功能,开发新功能,并且逐步迭代优化的过程了。
总结
常言道,“生命在于折腾”,历经1周的探索尝试,包括到Github提issue寻求帮助,咨询阿里云的工程师,联系腾讯客服,搜索浏览上百个链接,终于成功将服务器跑起来,很开心!也完成了这篇记录,给自己留下一个见证,如果这篇文章能够帮助到你那就更好了!也请点个“喜欢”,让我知道探索技术的道路上并不孤独,也让我知道能帮你少走了弯路!完结撒花~
参考资料
[1] How-to install Python 3.6.1 on CentOS 7
[2] Flask中文文档
[3] WeRoBot官方文档
[4] 阿里云ECS搭建微信公众平台
[5] How To Serve Flask Applications with Gunicorn and Nginx on CentOS 7
[6] Gunicorn as a SystemD service