0%

Linux下Python Web项目的部署总结

一、案例描述

现在使用Linux系统作为服务器已经非常普遍,无论是生态上还是稳定性上Linux系统都有它独特的优势。那么,如何去配置一套可用并且好用的环境来实现Python Web项目的部署呢?以Ubuntu系统为例,下面内容是个人的一些经验总结。

二、案例分析及解决过程

  1. 修改apt源地址为公司源,进行软件的安装与更新。修改/etc/apt/sources.list为以下内容:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    deb http://mirrors.xxxxxxxx.com.cn/ubuntu/ xenial main restricted universe multiverse
    deb http://mirrors.xxxxxxxx.com.cn/ubuntu/ xenial-security main restricted universe multiverse
    deb http://mirrors.xxxxxxxx.com.cn/ubuntu/ xenial-updates main restricted universe multiverse
    deb http://mirrors.xxxxxxxx.com.cn/ubuntu/ xenial-proposed main restricted universe multiverse
    deb http://mirrors.xxxxxxxx.com.cn/ubuntu/ xenial-backports main restricted universe multiverse
    deb-src http://mirrors.xxxxxxxx.com.cn/ubuntu/ xenial main restricted universe multiverse
    deb-src http://mirrors.xxxxxxxx.com.cn/ubuntu/ xenial-security main restricted universe multiverse
    deb-src http://mirrors.xxxxxxxx.com.cn/ubuntu/ xenial-updates main restricted universe multiverse
    deb-src http://mirrors.xxxxxxxx.com.cn/ubuntu/ xenial-proposed main restricted universe multiverse
    deb-src http://mirrors.xxxxxxxx.com.cn/ubuntu/ xenial-backports main restricted universe multiverse

    其中xentialUbuntu 16.04的代号,可替换成其他版本相应的代号。
    使用sudo apt update && sudo apt upgrade -y命令将软件包都更新到最新。


  2. shell的强化

    这里使用了zsh来替换默认的bashzsh对比bash有以下优势:

    • 兼容bash。原来在bash上面的习惯可以无缝转移过来。

    • 更完善的历史记录功能。比如输入su按上下箭头,就能翻阅所有执行过的su开头的命令,如sudo -s, supervisorctl reload

    • 智能拼写纠错,输入history | gtep reload,它会贴心提醒你

      1
      zsh: correct 'gtep' to 'grep' [nyae]?

      这时输入y就会自动展示history | grep reload的结果。

    • 路径补全、命令补全等功能。只需要按一下或两下Tab,就能看到推荐的补全选项,再按Tab或者ctrl+n/p/f/b还能在补全选项中上下左右切换。比如要进入/etc/nginx/conf.d目录,只需输入cd /e/n/c再按Tab,系统会自动补全出/etc/nginx/conf.d

    • 目录跳转强化。输入d,就会列出你在这个会话中访问的目录列表,再输入前面的序号,即可直接跳转到该目录。还有在任何目录输入~都会跳转到主目录,输入.或者...都会跳转到上级或上上级目录等等。

    • 各种插件的支持。比如git插件可以自动识别git目录,并且识别目录的状态,是否有修改等等。

      首先查看是否已安装zsh。使用命令

      1
      cat /etc/shells

      显示如下:

      1
      2
      3
      4
      5
      6
      7
      8
      /bin/sh
      /bin/dash
      /bin/bash
      /bin/rbash
      /usr/bin/tmux
      /usr/bin/screen
      /bin/zsh
      /usr/bin/zsh

      如果结果中没有zsh,则需要使用命令sudo apt install zsh先进行安装。其实zsh只是个基础,我们用oh-my-zsh来获得最佳的体验。可以使用Github上面的自动安装脚本,由于网络管控原因我们采用手动安装的方式。先从https://github.com/robbyrussell/oh-my-zsh下载zip包并解压,cptools/install.sh到主目录,修改install.sh里面的部分内容:

      1
      2
      3
      4
      git clone --depth=1 --branch "$BRANCH" "$REMOTE" "$ZSH" || {
      error "git clone of oh-my-zsh repo failed"
      exit 1
      }

      1
      mv oh-my-zsh-master ~/.oh-my-zsh

      运行sh install.sh,会提示你要不要把默认shell替换成zsh,选择y即可,退出shell重新登录就能使用zsh了。


  3. 使用pyenv给不同的项目创建虚拟环境

    有时候我们需要在一台服务器上面部署多个项目,这时不同项目所依赖的Python环境的隔离就尤为必要。下面介绍pyenv的安装与使用。

    https://github.com/pyenv/pyenv下载zip包,解压到~/.pyenv
    https://github.com/pyenv/pyenv-virtualenv下载zip包,解压至 $~/.pyenv/plugins/pyenv-virtualenv。添加以下三行代码到shell的环境文件中(如使用zsh则环境文件是~/.zshrc

    1
    2
    3
       export PATH="$HOME/.pyenv/bin:$PATH"
    eval "$(pyenv init -)"
    eval "$(pyenv virtualenv-init -)"

    使用命令

    1
    source ~/.zshrc

    来使环境立即生效。

    安装Python基础版本。比如安装3.6.5版本作为基础版本,再从这个版本上面创建出虚拟环境。我们可手动下载安装包进行安装,先去Python官网下载Python-3.6.5.tar.xz,放至~/.pyenv/cache/目录下。修改.pyenv/plugins/python-build/share/python-build/3.6.5文件的以下内容

    1
    install_package "Python-3.6.5" "https://www.python.org/ftp/python/3.6.5/Python-3.6.5.tar.xz#f434053ba1b5c8a5cc597e966ead3c5143012af827fd3f0697d21450bb8d87a6" ldflags_dirs standard verify_py36 copy_python_gdb ensurepip

    1
    install_package "Python-3.6.5" "~/.pyenv/cache/Python-3.6.5.tar.xz" ldflags_dirs standard verify_py36 copy_python_gdb ensurepip

    然后运行pyenv install 3.6.5就能进行基础版本的安装了。

    pyenv基本命令介绍:

    • pyenv versions 列出当前所有安装的虚拟环境,前面带*的是正在使用的环境

    • pyenv virtualenv 3.6.5 my_env 基于3.6.5基础版本建立一个名为my_env的虚拟环境

    • pyenv activate my_env 激活my_env环境,当shell前面有(my_env)这样的显示表示当前正在使用虚拟环境

    • pyenv deactivate 退出当前虚拟环境

      注意需要修改pip源地址为公司源,才能使用pip命令安装包。在主目录~下面创建.pip/pip.conf,写入如下内容

      1
      2
      3
      4
      5
      [global]
      timeout = 6000
      index-url = http://mirrors.xxxxxxxx.com.cn/python-pypi/simple
      [install]
      trusted-host = mirrors.xxxxxxxx.com.cn

  4. 使用supervisor做统一的项目启动管理

    使用sudo apt intstall supervisor安装supervisor,使用echo_supervisord_conf > /etc/supervisor/supervisord.conf生成默认配置文件,修改下面几项:

    1
    2
    3
    4
    5
    6
    7
    [inet_http_server]  ; inet (TCP) server disabled by default
    port=0.0.0.0:9001 ; (ip_address:port specifier, *:port for all iface)
    username=*** ; (default is no username (open server))
    password=******* ; (default is no password (open server))

    [include]
    files = /etc/supervisor/conf.d/*.conf

    这样就开启了界面管理,各配置项都可以放到/etc/supervisor/conf.d/中,以*.conf命名。其中usernamepassword项是登录界面管理的用户名和密码。

    /etc/supervisor/conf.d/添加配置文件myproject.conf

    1
    2
    3
    4
    5
    6
    7
    8
    [program:myproject]
    directory=/home/guo/project/myproject ; 项目目录
    command=/home/guo/.pyenv/versions/myproject/bin/gunicorn -w 10 --timeout 60 -b 0.0.0.0:4000 app:app ; 项目使用gunicorn启动在4000端口
    autostart=true ; 启动supervisor自动启动
    autorestart=true ; 异常退出后自动重启
    startretries=10
    redirect_stderr=true ; stderr的日志也重定向到stdout地址
    stdout_logfile=/var/log/supervisor/myproject.log ; stdout地址

    使用service supervisor status查看supervisor是否启动。

    supervisor基本命令介绍:

    • supervisorctl status 查看所有管理的进程的状态
    • supervisorctl stop program_name 关闭program_name进程
    • supervisorctl start program_name 启动program_name进程
    • supervisorctl restart program_name 重启program_name进程
    • supervisorctl stop all 关闭所有进程
    • supervisorctl start all 启动所有进程
    • supervisorctl update 更新新的配置到supervisord
    • supervisorctl reload 重新启动配置中的所有程序

  5. Nginx的配置

    一台服务器上要让多个项目能同时提供服务,各项目需要启动在不同的端口,但这样访问时就需要ip域名加端口号,使用上来说有不便之处。这里可以通过Nginx做请求转发,它启动在80端口,通过项目标识来区分不同项目,转发请求到项目相应的启动端口上。

    使用sudo apt install nginx来进行Nginx的安装,这样它会以服务的形式启动。默认的配置文件在/etc/nginx/nginx.conf,配置events项中使用epoll模型,

    1
    2
    3
    4
    5
    events {
    use epoll; # 异步非阻塞IO模型
    worker_connections 65535;
    # multi_accept on;
    }

    配置http项中自定义配置文件的位置

    1
    2
    3
    4
    http {
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
    }

    然后在/etc/nginx/sites-enabled/目录下就可以写自己的配置文件了,如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    # configuration of the server
    server {
    # the port your site will be served on
    listen 80;
    # the domain name it will serve for
    server_name _; # substitute your machine's IP address or FQDN
    charset utf-8;

    # max upload size
    client_max_body_size 75M; # adjust to taste

    location /my_project1/ {
    add_header Cache-Control no-cache;
    proxy_set_header Host 这里替换成服务器IP ;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass http://localhost:8000/my_project1/;
    }

    location /my_project2/ {
    add_header Cache-Control no-cache;
    proxy_set_header Host 这里替换成服务器IP ;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_pass http://localhost:8001/my_project2/;
    }

    }

    假设两个项目的标识分别为my_project1my_project2,分别启动在端口80008001上,那么访问服务器IP/my_project1/路径就能访问到相应的项目服务了。

    Nginx常用命令介绍:

    • service nginx start 启动Nginx
    • service nginx status 查看Nginx状态
    • service nginx reload 重新加载Nginx配置
    • service nginx restart 重启Nginx进程

三、经验总结及注意事项

  1. 校时。

    在拿到服务器后,需要对服务器时间做校正,公司有10.1.7.88提供校时服务。使用sudo apt install ntp安装ntp服务,修改配置文件/etc/ntp.conf

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    driftfile /var/lib/ntp/ntp.drift
    # Enable this if you want statistics to be logged.
    #statsdir /var/log/ntpstats/
    statistics loopstats peerstats clockstats
    filegen loopstats file loopstats type day enable
    filegen peerstats file peerstats type day enable
    filegen clockstats file clockstats type day enable
    restrict -4 default kod notrap nomodify nopeer noquery limited
    restrict -6 default kod notrap nomodify nopeer noquery limited
    # Local users may interrogate the ntp server more closely.
    restrict 127.0.0.1
    restrict ::1
    server 10.1.7.88 iburst

    service ntp restart重启ntp服务,ntpq -p查看同步状态,

    1
    2
    3
    remote           refid      st t when poll reach   delay   offset  jitter
    ==========================================================================
    *hik-dc2.hikvisi 10.1.7.17 5 u 298 1024 377 1.503 -33.461 78.224

    校时还有可能需要调整时区。先用date -R命令查看当前系统时间信息,如果最后面显示的不是+0800则系统时区需要调整。运行tzselect,在出现的选项中依次选择AsiaChinaBeijing,然后把东八区的时区文件复制到系统时区目录下,

    1
    stt@ubuntu:/$ sudo cp /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime

    再次查看时间信息,可以看到已经修改为北京时间。

  2. 软件包的安装与更新

    定期使用sudo apt update从软件源更新最新软件包信息,使用sudo apt upgrade -y安装最新软件包,使用sudo apt autoremove自动卸载不需要的软件包。如遇到/boot分区空间满了无法更新软件包的情况,先使用uname -r命令查看当前系统内核版本,dpkg --get-selections | grep linux查看所有安装的系统内核,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    console-setup-linux				        install
    libselinux1:amd64 install
    linux-base install
    linux-firmware install
    linux-generic install
    linux-headers-4.4.0-157 install
    linux-headers-4.4.0-157-generic install
    linux-headers-4.4.0-159 install
    linux-headers-4.4.0-159-generic install
    linux-headers-generic install
    linux-image-4.4.0-154-generic deinstall
    linux-image-4.4.0-157-generic install
    linux-image-4.4.0-159-generic install
    linux-image-generic install
    linux-libc-dev:amd64 install
    linux-modules-4.4.0-154-generic deinstall
    linux-modules-4.4.0-157-generic install
    linux-modules-4.4.0-159-generic install
    linux-modules-extra-4.4.0-154-generic deinstall
    linux-modules-extra-4.4.0-157-generic install
    linux-modules-extra-4.4.0-159-generic install
    util-linux install

    把当前使用的内核及前一个版本的之前的内核全都卸载掉,sudo dpkg --purge linux-headers-4.4.0-128-generic linux-headers-4.4.0-128 linux-image-extra-4.4.0-128-generic linux-image-4.4.0-128-generic,以上排名是有先后顺序的。其实卸载了一个版本的,看/boot有空间了以后就能使用sudo apt autoremove自动卸载其他版本的内核了。

  3. crontab任务加锁

    比如需要每分钟定时运行任务A,如果A的运行持续时间超过了一分钟,同一时刻可能会有两个A在运行,这并不是设计的初衷。这里可以使用Linux的文件锁flock,下面是一个加了锁的定时任务例子:

    1
    */1 * * * * flock -xn /tmp/rsync_A.lock -c 'rsync -vzrtopg --progress --exclude=.svn --delete stt@****:/home/stt/A /home/stt/ >> /var/log/rsync_cron/A_$(date +\%Y\%m\%d).log 2>&1'

    每分钟运行rsync命令去远程同步内容到本地,如果已经有锁了说明上一次同步命令还在进行,将不会启动这次的任务。

  4. 不建议直接使用root账号

    有以下几个原因,不建议直接使用root账号做日常管理与维护,应该使用专门的管理账号和部署账号。

    • root账号权限太大,容易误操作到系统的一些文件造成系统问题
    • root运行的应用程序如果被攻击,其权限也是root,会有潜在的安全性问题
    • 多个用户都对服务器进行操作维护的情况下,都用root账号不便于管理
  5. 小键盘映射

    Windows使用XShell进行远程连接时,如果出现了小键盘不能的情况,尝试把XShell属性里面的终端->终端类型改为linux。如果还是存在问题,把一下小键盘的映射方案加入到shell环境文件中(如.zshrc

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    # key bindings
    bindkey "\e[1~" beginning-of-line
    bindkey "\e[4~" end-of-line
    bindkey "\e[5~" beginning-of-history
    bindkey "\e[6~" end-of-history
    # #
    # # # for rxvt
    bindkey "\e[8~" end-of-line
    bindkey "\e[7~" beginning-of-line
    # # # for non RH/Debian xterm, can't hurt for RH/DEbian xterm
    bindkey "\eOH" beginning-of-line
    bindkey "\eOF" end-of-line
    # # # for freebsd console
    bindkey "\e[H" beginning-of-line
    bindkey "\e[F" end-of-line
    # # # completion in the middle of a line
    bindkey '^i' expand-or-complete-prefix
    #
    # # Fix numeric keypad
    # # # # 0 . Enter
    bindkey -s "^[Op" "0"
    bindkey -s "^[On" "."
    bindkey -s "^[OM" "^M"
    # # # 1 2 3
    bindkey -s "^[Oq" "1"
    bindkey -s "^[Or" "2"
    bindkey -s "^[Os" "3"
    # # # # 4 5 6
    bindkey -s "^[Ot" "4"
    bindkey -s "^[Ou" "5"
    bindkey -s "^[Ov" "6"
    # # # # 7 8 9
    bindkey -s "^[Ow" "7"
    bindkey -s "^[Ox" "8"
    bindkey -s "^[Oy" "9"
    # # # # + - * /
    bindkey -s "^[Ol" "+"
    bindkey -s "^[Om" "-"
    bindkey -s "^[Oj" "*"
    bindkey -s "^[Oo" "/"
    # #
  6. 使用自带的service来管理系统服务

    serviceRedHat Linux发行版下面控制系统服务的实用工具,用于对系统服务进行管理,比如启动(start)、停止(stop)、重启(restart)、查看状态(status)等。

    相关的命令还包括chkconfigntsysv等,chkconfig用于查看、设置服务的运行级别,ntsysv用于直观方便的设置各个服务是否自动启动。
    service命令本身是一个shell脚本,它在/etc/init.d/目录查找指定的服务脚本,然后调用该服务脚本来完成任务。因此使用者可以编写自己的service服务,要设置开机启动的话还需要结合chkconfig命令。

  7. 本文提供的部署资料不仅限于Python Web项目,其他方式启动的Web项目都可以使用supervisorNginx这一套方式去部署。