07月06日
使用 Nginx Uwsgi 实现 Python Flask 应用在 CentOS 服务器上的部署

Flask 是一个轻量化的 web 框架,与 Django 相比,Flask 更具灵活性,但在项目的部署上,Flask亦可以使用Django的部署方案,下面就介绍一下如何在CentOS上部署Flask项目

项目及服务器环境

Python 版本:3.6.5

Flask 版本:1.0.2

服务器版本: CentOS 7.4

Nginx 与 Uwsgi 版本皆为目前最新


部署开始

Web项目

  • 虚拟环境
        为了防止 Python 包的版本之间起冲突,我们需要使用虚拟环境将各个项目隔离开来,一般对于这种情况网上的教程都会选择用原生的 virtualenv,但是这里推荐一个超好用的第三方工具,据说也是 Python 官方推荐的虚拟环境管理工具,名字叫 pipenv,使用方法自行百度,安装方法如下:
    # pip install pipenv
        安装完成后,进入 Flask 项目根目录,使用 pipenv 创建虚拟环境,值得一提的是,pipenv 不一定要用正规的 install 命令来创建环境,使用进入虚拟环境 shell 的命令也一样可以,pipenv 会自动在虚拟环境列表中匹配当前项目的虚拟环境,如果找不到则会自动帮你新创建一个,具体如下:
    # pipenv shell
    Creating a virtualenv for this project…
    Using /usr/local/bin/python3.6 (3.6.5) to create virtualenv…
    Spawning environment shell (/bin/bash). Use 'exit' to leave.
    . /home/shenghun/.local/share/virtualenvs/Fisher-kuuv-2eR/bin/activate
    #  . /home/shenghun/.local/share/virtualenvs/Fisher-kuuv-2eR/bin/activate
        成功进入后会在最前面有一个前缀,表示当前虚拟环境
    (Fisher-kuuv-2eR) [root@shenghun]
        之后,由于虚拟环境间相互独立,因此需要重新安装一下包,否则 Python 找不到包会造成程序崩溃。另外,由于 pipenv 生成的包统一会放在另一个地方,和原生的 virtualenv 直接放在项目根目录不一样,所以注意记录下虚拟环境的地址,之后在配置 Uwsgi 和 Nginx 的时候用的上,地址为 ".home/shenghun/.local/share/virtualenvs/Fisher-kuuv-2eR" ,问我在哪看?你猜啊(~ ̄▽ ̄)~

Uwsgi

        Uwsgi 是一个 Web 服务器,具体信息就不再赘述了,自己找度娘,要使用 Uwsgi,首先需要安装 Python 中的 Uwsgi 包,最好在全局环境和虚拟环境中都装上(起码我是这样的,如果不都装上会发生什么我也不知道),但需要注意的一点是,Uwsgi 需要一些其他环境的支持,因此在安装 Uwsgi 前要把先行条件准备好:
    yum -y install gcc  gcc-c++ zlib zlib-devel openssl openssl-devel pcre pcre-devel GeoIP gd libXpm libxslt sqlite-devel
        完成之后,再使用 `pip install uwsgi`(虚拟环境中使用 `pipenv install` )安装 Uwsgi。
        装完 Uwsgi,我们还要在项目根目录下创建一个 Uwsgi 配置文件,实际上,Uwsgi 可以用命令使其带参数启动,但有了配置文件就不用每次写一大串命令了;为了方便,我直接把文件命名为 uwsgi.ini, 大致配置如下:
    [uwsgi]
    # Http access port. 
    # If this option comes into effect, we can visit our web site on http://[our IP]:[Port]
    # http=:5001

    # Uwsgi's ip and port when it is loaded by Nginx
     socket=127.0.0.1:5001
    
    # Point to the main directory of the Web Site
     chdir=/home/Servers_Nginx/Fisher/
    
    # Setting up a virtual environment
     virtualenv=/root/.local/share/virtualenvs/Fisher-e_ddg0zZ

    # Python startup file
     wsgi-file=fisher.py

    # The application variable of Python Flask Core Oject 
     callable=app

    # The maximum numbers of Processes
     processes=1

    # The maximum numbers of Threads
     threads=2 
        其他的配置没什么好说的,英语稍微有点水平的都能看懂我写的什么,重点在 http 和 socket 上,这两个配置是不能同时存在的(起码在我印象中)。http 是用来使外网能直接访问 Uwsgi 的,而 socket 是用来和 Nginx 通信的,Nginx 获取到外网的请求后,就通过这个地址转发给 Uwsgi 处理,socket 的用法在下面 Nginx 的配置文件中会看到。
        配置好了之后,可以用 `uwsgi uwsgi.ini` 命令查看是否成功配置 ,如果没问题,则会有以下启动信息(只截取一部分,事实上有老大一串):
    [uWSGI] getting INI configuration from uwsgi.ini
    *** Starting uWSGI 2.0.17 (64bit) on [Tue Jul  3 22:42:15 2018] ***
    compiled with version: 4.8.5 20150623 (Red Hat 4.8.5-16) on 02 July 2018 14:12:13
    os: Linux-3.10.0-693.21.1.el7.x86_64 #1 SMP Wed Mar 7 19:03:37 UTC 2018
    nodename: shenghun
    machine: x86_64
    clock source: unix
    pcre jit disabled
    detected number of CPU cores: 1
    current working directory: /home/Servers_Nginx/Fisher
    detected binary path: /usr/bin/uwsgi
    uWSGI running as root, you can use --uid/--gid/--chroot options
        Ctrl+C 结束运行,开始 Nginx 的安装与配置

Nginx

Nginx 是什么

        虽然只有 Uwsgi 也可以直接部署项目,但是为了安全,需要 Nginx 来进行请求代理,众所周知类似于学校之类的内部计算机通过信息中心服务器访问外网的行为称为正向代理,所有的访问请求都由中央服务器代理,此时正常情况下外网是不知道你的计算机的地址的;而 Nginx 的作用就是实现反向代理,正向代理代理的是内部计算机,而反向代理代理的是内部服务器,正是有 Nginx 存在,Uwsgi 的真正接口才不会暴露在网络上,服务器的安全才有了一定的保障(当然,恶意攻击另说)。此外,Nginx 还自带防 DDoS 攻击机制,可以说日常使用是非常省心了。

Nginx 安装

        关于 Nginx 的安装,有很多种方法,这里选择了 yum 源安装 (其他 linux 发行版也有类似的安装方法) 。
  • yum 源配置
        首先在 /etc/yum.repo.d/ 文件夹下添加 yum 源,名字就叫做 Nginx.repo 吧 (为了方便懒得起名)
    # vim Nginx.repo
        输入以上命令进入文件编辑,完成相关配置,对于yum源的配置官方文档是这么说的 :
    To set up the yum repository for RHEL/CentOS, create the file named /etc/yum.repos.d/nginx.repo with the following contents:

        [nginx]
        name=nginx repo
        baseurl=http://nginx.org/packages/OS/OSRELEASE/$basearch/
        gpgcheck=0
        enabled=1
    
    Replace “OS” with “rhel” or “centos”, depending on the distribution used, and “OSRELEASE” with “6” or “7”, for 6.x or 7.x versions, respectively.
        其中 baseurl 中的 OS 需要改成自己的系统名,OSRELEASE 需要改成系统的发型版本号。完成之后配置文件如下:
    [Nginx]
    name=nginx repo
    baseurl=http://nginx.org/packages/centos/7/$basearch/
    gpgcheck=0
    enabled=1
        配置完成后还没完,需要使用一下命令重新加载 yum 源:
    # yum clear all
    # yum makecache
        出现 `Metadata Cache Created` 即为成功。
        其他系统添加源方法可以到 http://nginx.org/en/linux_packages.html 中查看,我就不一一赘述了.
  • yum 源安装
        yum 安装就很简单了,直接 `yum install nginx` 就行,如果不想提示是否继续可以再加上 -y 参数像这样:
    # yum -y install nginx
        回车以后会自动安装 nginx,稍等一会,等屏幕提示 complate 就可以了,如果之前就已经安装成功了,就会提示 `Nothing to do` 。
        另外这里解释一下,我这里选择 yum 除了安装方便外,还有一个很重要的因素,那就是 yum 安装可以自动将 Nginx 加入系统服务,不用再傻傻地到安装目录下适用 ./nginx 或者一大串路径启动了,直接用 systemctl (CentOS 6 使用 service) 的 start、stop、restart 就可以完成启动、停止、重载,甚至可以用 `systemctl status nginx` 就可以直接看到 nginx 启动状态,非常方便。

Nginx 配置

        Nginx 安装完成后,接下来就需要配置虚拟主机用以部署项目了,进入 nginx 配置文件目录,如果不知道在哪的可以使用一下方法找到:
    # whereis nginx
        出现以下五个目录:
    nginx: /usr/sbin/nginx /user/lib64/nginx /etc/nginx /usr/share/nginx /usr/share/man/man8/nginx.8.gz
        第一个路径是启动快捷键,第二个 Nginx 依赖的一下库,第四个是 nginx 项目目录,第五个看名字就知道应该是文档之类的,所以配置文件目录是 /etc/nginx 没跑了。
  • 编辑主配置文件
        主配置文件就是 nginx.conf,就在、etc/nginx 目录下,使用 vim 进入编辑:
    user  nginx;
    worker_processes  1;

    error_log  /var/log/nginx/error.log warn;
    pid        /var/run/nginx.pid;


    events {
        worker_connections  1024;
    }


    http {
        include       /etc/nginx/mime.types;
        default_type  application/octet-stream;

        log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                          '$status $body_bytes_sent "$http_referer" '
                          '"$http_user_agent" "$http_x_forwarded_for"';

        access_log  /var/log/nginx/access.log  main;

        sendfile        on;
        #tcp_nopush     on;

        keepalive_timeout  65;

        #gzip  on;

        include /etc/nginx/conf.d/*.conf;
    }
        文件中的 ‘worker_processes’ 的值是1,这里最好改成 auto,这样 Nginx 可以根据需要自动匹配工作进程数,其他的就不用动了;关于虚拟主机的配置在最后一行有,意思就是每次启动 Nginx 的时候自动载入 /etc/nginx/conf.d/ 下的全部 .conf 文件,至于这些 .conf 文件是什么 ,先卖个关子,等下就知道了。
  • 新建虚拟主机
        进入 conf.d 文件夹后查看文件夹目录发现,这里只有一个 default.conf,使用 vim 查看一下:
    server {
        listen       80;
        server_name  localhost;

        #charset koi8-r;
        #access_log  /var/log/nginx/host.access.log  main;

        location / {
            root   /usr/share/nginx/html;
            index  index.html index.htm;
        }

        #error_page  404              /404.html;

        # redirect server error pages to the static page /50x.html
        #
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   /usr/share/nginx/html;
        }

        # proxy the PHP scripts to Apache listening on 127.0.0.1:80
        #
        #location ~ \.php$ {
        #    proxy_pass   http://127.0.0.1;
        #}

        # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
        #
        #location ~ \.php$ {
        #    root           html;
        #    fastcgi_pass   127.0.0.1:9000;
        #    fastcgi_index  index.php;
        #    fastcgi_param  SCRIPT_FILENAME  /scripts$fastcgi_script_name;
        #    include        fastcgi_params;
        #}

        # deny access to .htaccess files, if Apache's document root
        # concurs with nginx's one
        #
        #location ~ /\.ht {
        #    deny  all;
        #}
    }
        原来这就是虚拟主机的配置文件,默认监听的是 80 端口,如果 80 端口已经有进程占用了会启动失败,遇到启动失败的情况可以试着更改一下监听端口,此时启动 Nginx 就已经可以访问到欢迎页面了,这里我们改成6000端口试试:
    # systemctl restart nginx
        在浏览器中使用 http://localhost:6000 查看一下,此时应该就能访问到Nginx的欢迎界面了。
        接下来就是正式的虚拟机配置了,在 conf.d 文件夹下新建一个 .conf 文件,名字就用项目名吧 (方便些),使用 vim 进入,配置如下:
    upstream flask{
        server 127.0.0.1:5001;
    }


    # Virtual host, with this option, we can visit our web site on server_name.
    # If the server_name is an ip address and we has configured a DNS, 
    # we can also use the corresponding domain name.

    server {

        listen              5000;

        # domain name or ip address
        server_name         www.shenghuntianlang.xin;

        charset             utf-8;


        # the web site resources path    
        location / {
            include         uwsgi_params;

            # The inner address whilch point to Uwsgi. all the requests will resend to Uwsgo to resolve.
            # If we has configured the upstream options, we can fill in the upstream name instead.
            uwsgi_pass      flask;

            # If the following options are already exists in 'Uwsgi.ini'.these options can be ignored.
            # Configure Uwsgi's running environment path.
            # uwsgi_param   UWSGI_PYHOME    /root/.local/share/virtualenvs/Fisher-e_ddg0zZ;

            # Configure the main directory of web site
            # uwsgi_param   UWSGI_CHDIR     /home/Servers_Nginx/Fisher;

            # Configure the Flask Core Object
            # uwsgi_param   UWSGI_SCRIPT    fisher:app;

            }
    }    
        每个选项的作用都写在注释中了,不过还是稍微解释一下吧,首先 upstream 是用来做负载均衡的 Nginx 在接收到请求后,会将请求转发到其中的服务器上,这里的每一个 server 就是一个服务器,另外,127.0.0.1:5001 是不是有点熟悉?是滴,就是上面配置的 Uwsgi 的 socket,Nginx 通过 socket 连接 Uwsgi 把请求发送到 Uwsgi 服务器,Uwsgi 处理后把响应反馈给 Nginx,再由 Nginx 转发给客户端,使用 upstream 时只需要将设置的 upstream 名 ( 这里是 flask ) 填入 server 中的 location 中的 uwsgi_pass 就可以了,另外也可以不配置 upstream,直接把 Uwsgi 的 socket 填入 uwsgi_pass 也行得通。
        其次是 server 的 location,其中的 include 是加载 uwsgi_params 文件 (本身在 Nginx 配置目录 /etc/nginx 下就有一个,可以看上面的截图),这个文件是用来确定 Uwsgi 的运行信息的,由于 Nginx 配置目录下有现成的,所以就不用管了,直接指向那个文件即可 (如果有需要需要置顶到特定文件则需要填入绝对地址),还有就是下面全部注释掉的配置信息,这些是用来额外配置 Uwsgi 的启动配置的,如果 Uwsgi 的 .ini 文件里面有相关配置(虚拟环境啊核心 APP 对象啊什么的)就不需要了,因此这里只给了两个配置,uwsg_params 文件和 Uwsgi 的 socket。
        到此虚拟主机就配置成功了,另外配置完 Nginx 之后可以使用 nginx -t 测试配置文件,如果成功就万事大吉,有错误的话会有相关信息弹出,之后使用 `systemctl restart nginx` 重启 Nginx,就可以用配置的 server_name 加端口访问 Nginx 了。

杂记

  • 查看端口号监听状态与关闭监听进程
        查看端口号
    # netstat -ntlp | grep 5000
    tcp        0      0 0.0.0.0:5000            0.0.0.0:*               LISTEN      10316/nginx: master 
        通过 pid 关闭监听进程
    [root@shenghun /] kill -9 pid
        使用场景:端口被占用,服务起不来
        注:其中 pid 是监听进程的名字的 '/' 前面的那个数字
  • 查看服务名相关运行信息并关闭
        查看服务名相关运行信息
    # ps -ef | grep nginx
    root     10316     1  0 10:37 ?        00:00:00 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
    nginx    10317 10316  0 10:37 ?        00:00:00 nginx: worker process
    root     10589  8661  0 11:22 pts/6    00:00:00 grep --color=auto nginx
        关闭此服务
    # pkill -9 nginx
        使用场景:同名服务已存在,需要重新启动新服务
        注:此方式是关闭此服务名下的所有进程,如果需要精准关闭,可以使用通过 pid 方式。此外,就算关闭所有进程,仍然会有一个名为 `grep --color=auto nginx` 的进程存在(Uwsgi以及其他一些服务也是),属于正常情况,不比特别在意
  • Nginx 和 Uwsgi 无法同时启动
        解决方法:Nginx 的监听端口不要和 Uwsgi 的端口一样,不然会起冲突导致其中一个起不来。
  • 自定义执行命令
        编辑用户目录下的 .bashrc 可以自定义执行命令:
    # vim .bashrc
        在开头使用 `alias` 命令添加新命令:
    alias lh='ll -h'  #添加名为 lh 的新命令,使其能执行与 'll -h' 相同的操作
        编辑完成后保存退出,并使 .bashrc 生效:
    # source .bashrc
        此后就可以使用新命令了,除此以外还可以直接用 alias 临时定义执行命令,只不过是暂时的,退出终端之后就没了。
  • ※ 切换程序前后台并在退出终端( xshell 之类的工具同理)后继续执行 ※

         切换前后台

        执行一个命令:
    # uwsgi uwsgi.ini
        此时 Uwsgi 会在前台执行,在当前终端中无法进行其他操作。
        切换到后台,先 Ctrl+Z 挂起程序
        然后使用:
    # bg
        此时程序就会在后台执行,终端解锁,可以进行其他操作,使用 fg 可以将后台的程序调到前台
        其他用法可以自行找度娘

         保持程序在退出终端后正常运行

        终端下,即使程序在后台运行,一旦退出终端,程序进程仍旧会被系统 kill 掉,可以使用 tmux 工具:         创建新会话:
    # tmux new -s 会话名(自定)
        之后会进入新会话,底部会有绿色提示
        在这里面运行程序程序执行后,使用 Ctrl+b | d(先输入 Ctrl+b,再输入单个 d)退出当前会话,此时会回到前面的终端环境
        使用 Ctrl+b | s 可以查看所有会话
        进入先前会话
    # tmux attach -t uwsgi-fisher

2条评论

@chengxiao 我周围也有用Django,实测也可以用在部署Django上

圣魂天狼7 天前回复

同适用于 Django 的吧
另外端口进程更喜欢用

lsof -i:端口号

chengxiao18 天前回复