有任何问题可以在评论区提问,我会的,尽量回答。
背景
公司的官网要更新,想做个产品返修进度查询。但是数据都在OA系统里面,所以需要写接口,把OA中的每个节点数据传给前端。
之前用nodejs实现过这个接口,但是由于这次官网做了SSL,所以它请求不了http的接口。因此,我不得不把接口做成https协议的。那与其做一遍SSL,不如把整个nodejs接口重构到python下(脑回路清奇,明明重构工作量大多了)。为啥选python,因为人生苦短。好吧,因为我现在是以开发运维的身份存在的,学点python对以后的自动化运维总有好处。
概述
项目本体由python3.6+flask+pymssql完成,使用uwsgi做容器发布,用nginx做https协议请求转发。
详细过程与配置
python代码(在python环境可以单独跑起来)
这一部分麻雀虽小五脏俱全,囊括了很多restful的知识(一天从python 0基础到写完接口,我骄傲)。
from collections import OrderedDict
import pymssql
import json
import datetime
from flask import Flask, request, jsonify
from sqlalchemy import null
app = Flask(__name__)
app.config['JSON_SORT_KEYS'] = False
app.config['JSON_AS_ASCII'] = False
#用作解析数据库中的日期
class DateEncoder(json.JSONEncoder):
def default(self, obj):
if isinstance(obj, datetime.datetime):
return obj.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(obj, datetime.date):
return obj.strftime("%Y-%m-%d")
else:
return json.JSONEncoder.default(self, obj)
def queryRepair(sn):
conn = pymssql.connect(host="10.0.0.11",
port=1433,
user="a",
password="a",
database="a",
charset='cp936')
if conn:
cursor = conn.cursor()
sql = "select * from V_Kingorld_query_repair where sn = %s;"
cursor.execute(sql, str(sn))
col_names = [desc[0] for desc in cursor.description]
result = []
for row in cursor.fetchall():
dict_obj = OrderedDict()
for i, v in enumerate(row): # index, value
dict_obj[col_names[i]] = v
result.append(dict_obj)
# result = json.dumps(result, cls=DateEncoder, ensure_ascii=False)
cursor.close()
conn.close()
if len(result):
res = {
"status": 0,
"msg": "success",
"data": result
}
else:
res = {
"status": 1,
"msg": "sn无效",
"data": result
}
else:
res = {
"status": 1,
"msg": "fail",
"data": null
}
return res
@app.route('/getNode', methods=['GET'])
def getNode():
sn = request.args.get('sn')
print(sn)
txt = queryRepair(sn)
return jsonify(txt)
if __name__ == '__main__':
app.run(debug=False, host='0.0.0.0', port='3331')
python代码在此不赘述了。简单的restful接口而已。看不懂的地方可以提问。注意我中间注释的那一句result = json.dumps(result, cls=DateEncoder, ensure_ascii=False) 这句话,当时是用来解决数据库返回对象乱码问题的。后来发现,这么写是没必要转字符编码,否则会出现斜杠双引号的情况。原理就是,如果我先把数据库返回结果转成了json,那么在后面res={}这个json中,由于result是字符串,所以res会自动把result中的双引号用转义字符转一下。
我是用Pycharm写的代码,运行直接run就行。
传输到服务器
由于我的开发环境是 ubuntu18所以,直接一行命令搞定
scp /home/Kingorld/PythonProjects/repairQuery apiuser@10.0.0.10:/Api/Python/repairQuery
服务器环境搭建
服务器是CentOS8
# 首先安装python3.6 centos8自带3.6
yum install -y python
python --version #检查一下版本
pip --version #检查版本
# 创建python虚拟环境
cd /Api/Python/repairQuery
python -m venv venv
# 激活虚拟环境 用虚拟环境的好处就是,随便你怎么造,不影响其他环境
source ./venv/bin/activate
# 此时命令行前面会出现小括号 (venv)表示你现在是在虚拟环境中执行命令
# 安装我需要的包(根据项目不同,需要下载的包也不同)
(venv) pip install pymssql
(venv) pip install flask
# 安装uwsgi(不多介绍了)
(venv) pip install uwsgi
uwsgi配置
# 新建ini配置文件(可自定义命名)
(venv) touch uwsgi.ini
# 编辑
(venv) vim ./uwsgi.ini
以下是配置
[uwsgi]
# main文件所在目录
chdir=/APIs/python/repairQuery
# 文件里如果是 app.run 那就用app
callable=app
# flask文件名
wsgi-file=repairQuery.py
# 进程数
processes=1
# 使用3333端口
http=0.0.0.0:3333
# 日志输出目录
daemonize =/APIs/python/repairQuery/flask.log
# 关闭uwsgi时需要用到这个文件
pidfile = repairQuery.pid
然后保存退出即可
启动uwsgi
uwsgi --ini uwsgi.ini # 启动后直接进控制台
nohup uwsgi --ini uwsgi.ini & # 后台启动uwsgi服务器
uwsgi --reload uwsgi.pid # 重启
uwsgi --stop uwsgi.pid # 停止
kill -9 pid # 关闭服务,直接kill uwsgi的端口
nginx部分
首先下载安装nginx
# 如果还在虚拟环境需要先执行
# (venv) deactivate
# 安装nginx
yum install -y nginx
# 通过yum安装的nginx主目录一般在 /etc/nginx下
# 因为这是个接口服务器,所以我的nginx可能会代理多个端口。因此,我把每个系统的接口都新建了不同的配置文件
# 因为nginx自动会识别到conf.d目录下的所有配置,所以直接新建就好了
# 具体的代码在nginx.conf中是 include /etc/nginx/conf.d/*.conf
# 把默认的文件复制到/etc/nginx/conf.d下并且重命名为repairQuery.conf(需要sudo权限)
sudo cp /etc/nginx/nginx.conf /etc/nginx/conf.d/OA/repairQuery.conf
# 接着把pem文件和key文件(证书)上传到服务器
# 在nginx文件夹下新建cert目录(需要sudo权限)
sudo mkdir /etc/nginx/cert
# 把上传的文件移动到cert目录下
sudo mv /home/kingorld/aaa.pem /etc/nginx/cert
sudo mv /home/kingorld/aaa.key /etc/nginx/cert
修改配置文件为以下内容
#二级域名
server {
listen 443 ssl ;
server_name aaa.com ; # 这是申请证书时候的域名
# 把你的pem文件和key文件的目录下载下面
ssl_certificate cert/aaa.pem;
ssl_certificate_key cert/aaa.key;
ssl_session_timeout 5m;
ssl_protocols SSLv2 SSLv3 TLSv1 TLSv1.1 TLSv1.2 ;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
location / {
#下面的3333端口就是uwgsi的http端口
proxy_pass http://127.0.0.1:3333;
add_header Access-Control-Allow-Origin *;
# 当然由于这里只是转发请求,所以没有跳转index
# 如果需要跳转index页面的话需要加上root和index路径
}
}
下面启动nginx
# 先检查nginx状态
systemctl status nginx
# 如果开启了,重新加载一下配置就行(一般需要sudo权限)
sudo nginx -s reload
# 如果没有开启,需要启动nginx(一般需要sudo权限)
sudo systemctl start nginx
sudo systemctl restart nginx # 这条是重启命令
sudo systemctl stop nginx # 停止nginx
此时应该可以请求到接口数据了。
如果不行可以去看下uwgsi日志和nginx日志。一定要记得看日志,别瞎TM整。
容易忽略的地方
1. 服务是否开启
如果请求出现了502,可能是服务没有开启。由于现在已经用nginx代理了,所以外网(包括postman)的请求都是502.
第一步,可以先在服务器上面用curl命令测试一下是否能够获取到数据。
curl localhost:3333/getNode?sn=1
如果获取到数据,说明服务开启,并且能正常访问。如果返回是什么empty server之类的或者压根没有返回(长时间无返回),就是异常启动。
此时就要去查看uwsgi日志,看服务是否正常启动。
2. 防火墙端口是否开启
检查443端口和3333端口是否开启(一般3333服务器内部访问,只开启443端口就行)
# 先看防火墙状态是否正常 我用的是firewalld 你可能用其他的
systemctl status firewalld
# 如果防火墙没开启,这个问题跳过,跟防火墙没关系。如果开启,继续看端口是否开启。
firewall-cmd --zone=public --query-port=443/tcp
# 如果显示yes,那也不是端口问题。如果显示no,继续开启443端口
firewall-cmd --zone=public --add-port=443/tcp
此时如果还是请求不到。首先去看日志。
3. selinux问题
关于selinux不深究,究了你也不明白。当然我目前也不是很清楚。如果你明白的话,可以跟我探讨交流。
所以对于selinux,关掉吧。
# 查看selinux状态,如果是1,置为0
getenforce
# 关闭selinux(临时关闭,重启服务器又会开启。永久关闭selinux方法自行百度)
setenforce 0
如果还是不行。还是看日志,百度 (为啥推荐百度,因为83.64%的程序员上不了google,能上的也有92.37%的人不想看英文)。
4.permission denied问题
权限不足问题。可能由nginx权限不足和selinux导致。selinux问题参照3。
如果是nginx问题。
设置nginx的权限为root(最高权限)
# 编辑/etc/nginx/nginx.conf
vim /etc/nginx/nginx.conf
# 修改nginx权限为root
# 找到user nginx(或者其他权限) 修改为如下
user root
结语
CentOS+flask+uwsgi+nginx+ssl的部署就到这里。如果你是python开发工程师,那么比较难的部分应该是nginx和ssl。如果你是运维工程师,那么应该是python代码问题比较多。
好了,以上就是我在开发与部署过程中碰到的一些问题,如果各位看官有不同意见或者看法的,随时在评论区回复,我看到会响应的。
最后,挚谢阅读。