第12章 部署

部署配置

网站通常需要提供其他文件, 如图像、js、css。django中我们将这些文件称为

静态文件。django提供了django.contrib.staticfiles来管理它们

官方地址:https://docs.djangoproject.com/zh-hans/2.0/ref/settings/

配置项目 默认项 说明
STATIC_URL None 指定引用静态文件时使用的 URL,必须以斜杠结尾
STATICFILES_DIRS [] 定义staticfiles应用程序将遍历的其他路径
STATIC_ROOT None 静态文件的绝对路径,collectstatic
MEDIA_URL “” 指定媒体文件的URL,必须以斜杠结尾;不可以=STATIC_URL
MEDIA_ROOT “” 用于保存用户上传文件的绝对路径
  1. # Static files (CSS, JavaScript, Images)
  2. # https://docs.djangoproject.com/en/2.0/howto/static-files/
  3. STATIC_URL = '/static/'
  4. MEDIA_URL = "static/media/"
  5. STATIC_ROOT = os.path.join(BASE_DIR, 'static/') # collectstatic的绝对路径
  6. MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_URL) # 上传文件的绝对路径

部署

架构

第12章 部署 - 图1

协议

WSGI:PEP3333- Python Web Server Gateway Interface,一种规范,描述Web Server如何与 Web Application 通信的规范。

uwsgi:线程协议,不是通信协议,用于uWSGI server与web server的数据通信

服务器

  • web server
    • nginx:支持uwsgi/WSGI/http/https/协议的Web Server
  • WSGI server
    • Gunicorn:支持WSGI的Web Server,全名叫(Green unicorn),来源于Ruby的Unicorn项目,一款Pyhton的WSGI(web server gateway interface) HTTP服务器,采用pre-fork工作模式
    • uWSGI: 实现了WSGI/uwsgi/http 协议的Web Server应用程序
  • 系统监控工具
    • supervisor:是一个python实现的守护进程的应用,可以帮你管理你的进程,当进程莫名挂掉可以帮你restart,保证服务持久在线

为什么有了uWSGI为什么还需要nginx?

因为nginx具备优秀的静态内容处理能力,然后将动态内容转发给uWSGI服务器,这样可以达到很好的客户端响应。

环境依赖

架构

  1. +---------+ +-----------+ +--------------+ +-----------+
  2. | client +-------> | nginx +----> | gunicorn +----> | app |
  3. | | | | | | | |
  4. +---------+ +-----------+ +-------+------+ +-----------+
  5. ^
  6. | 服务管理
  7. |
  8. +--------------+
  9. | supervisord |
  10. | or systemd |
  11. +--------------+

环境依赖

  1. # ubuntu/redhat 都建议安装好 开发工具套件 开发环境
  2. $ yum groupinstall "Development Tools" -y
  3. $ sudo yum -y install epel-release
  4. # ubuntu
  5. $ sudo apt-get install build-essential

安装 pyenv

  1. # (可选)centos 6.5 安装依赖包
  2. $ yum -y install gcc gcc-c++ make git patch openssl-devel zlib-devel readline-devel sqlite-devel bzip2-devel bzip2-libs
  3. $ curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash
  4. $ pyenv install 3.6.6
  5. # (可选) 加速上面安装
  6. v=3.6.6; wget https://www.python.org/ftp/python/$v/Python-$v.tar.xz -P ~/.pyenv/cache/;pyenv install $v
  7. # 创建虚拟环境
  8. # pyenv virtualenv x.x.x name
  9. $ pyenv virtualenv 3.5.6 blog
  10. # 使用
  11. $ pyenv activate name

项目依赖

依需求依次安装

  1. # 安装依赖
  2. pip3 install -r requirements.txt
  3. # mysqlclient 安装报错时,ubuntu 下安装此系统依赖
  4. sudo apt-get install libmysqlclient-dev
  5. # mysqlclient 安装报错时
  6. # pip install mysqlclient
  7. # centos 6.5
  8. $ yum install gcc python-devel mysql-devel -y
  9. # centos 7
  10. $ yum install mariadb-devel

(可选)安装douban源

  1. # linux: ~/.pip/pip.conf
  2. [global]
  3. timeout = 60
  4. index-url = http://pypi.douban.com/simple
  5. trusted-host = pypi.douban.com

项目 settings.py配置

  1. #设置
  2. ALLOW_HOSTS = ['example.com','www.domainname.com', 'ipaddr']
  3. DEBUG=False

部署静态文件

django静态文件介绍

配置项 说明
MEDIA_ROOT 主要是为了存放上传的文件,比如ImageField中这个值加上upload_to的值就是真实存放上传图片的文件位置;Django里边文件内容实际上是不会存放到DB里边的,大多数DB存放数据效率低,需要保存在文件系统
MEDIA_URL URL的映射,前后要加上“/”表示从根目录开始,比如”/site_media/“, 加上这个属性之后,静态文件的链接前面会加上这个值。
STATIC_ROOT 在这个文件里边的目录会当成静态文件处理。但是不能把自己辛苦写的javascrit或者图片等静态文件放进去。
STATIC_URL URL映射,指定静态目录的URL,默认的是”/static/“
STATICFILES_DIRS 指定一个工程里边哪个目录放在了与这个工程相关的静态文件,是一个列表,如果列表中有一个是”/home/shishang/test/static/“,其中有一个文件内容是productlist.html,我们只要访问http://localhost:8000/static/productlist.html
就可以直接访问界面了。

Django提供了一个方法自动地将所有的静态文件放在一起。

只要在写App的时候创建一个static子目录专门保存静态文件就行了。

在开发阶段,不必费心去做映射,不需要配置urls.py。

  1. 在部署到生产环境的时候,只需要配置Apache把/static/映射到STATIC_ROOT。
  2. 然后运行manage.py collectstatic,自动地STATICFILES_DIRS列出的目录以及各个App下的static子目录的所有文件复制到STATIC_ROOT。
  3. 因为复制过程可能会覆盖掉原来的文件,所以,一定不能把我们辛苦做出来静态文件放这边!

在开发阶段,Django把/static映射到django.contrib.staticfiles这个App。staticfiles自动地从STATICFILES_DIRS、STATIC_ROOT以及各个App的static子目录里面搜索静态文件。一旦部署到开发环境上,settings.py不需要重新编写,只要在Apache的配置文件里面写好映射,/static将会被Apache处理。django.contrib.staticfiles虽然仍然存在,但因为不会接收到以/static/开始的路径,所以将不会产生作用。不必担心Django会使用处理速度变慢。

另外,当settings.DEBUG=False的时候,staticfiles将自动关闭,不对静态文件处理(需要前端代理做这样的事)。

settings.py设置

  1. # Static files (CSS, JavaScript, Images)
  2. # https://docs.djangoproject.com/en/2.0/howto/static-files/
  3. STATIC_URL = '/static/'
  4. STATIC_ROOT = os.path.join(BASE_DIR, 'static')
  5. MEDIA_URL = "static/media/"
  6. MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_URL)
  7. ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])
  8. # shell
  9. $ python manage.py collectstatic
  10. # nginx静态文件配置
  11. # 作用是为下面采集静态文件使用
  12. # STATIC_ROOT = os.path.join(BASE_DIR, "static/")

静态文件部署

  1. $ python manage.py collectstatic

nginx/conf.d/project.conf 设置

  1. # nginx 中只能设置成这样
  2. location /static {
  3. alias /path/to/your/mysite/static_dist; # your Django project's static files - amend as required
  4. }

配置mysql

安装与配置mysql5.7

  1. # 下载yum源文件
  2. $ wget http://repo.mysql.com/mysql57-community-release-el7-9.noarch.rpm
  3. $ sudo rpm -ivh mysql57-community-release-el7-9.noarch.rpm
  4. # 更新yum软件包
  5. $ yum check-update
  6. # 安装mysql
  7. $ yum install mysql mysql-server
  8. # my.cnf 配置
  9. # [mysqld]
  10. # plugin-load=validate_password.so
  11. # validate-password=OFF # 关闭密码检查
  12. # character-set-server=utf8 # utf8 支持
  13. # skip-grant-tables # 跳过权限表检查
  14. # 建立数据库
  15. create database weme character set utf8;
  16. # 授权
  17. grant all privileges on *.* to 'root'@'localhost' identified by 'redhat';
  18. # 刷新权限表
  19. flush privileges;

修改 mysql root 密码

  1. # mysql5.7 修改root密码
  2. # https://mysqldbblog.wordpress.com/2016/05/05/set-new-password-in-mysql-5-7-for-root/
  3. $ cat /var/log/mysqld.log | egrep 'password'
  4. 2016-12-10T06:30:12.041470Z 1 [Note] A temporary password is generated for root@localhost: nWoo>o0rXtr4
  5. # 需要先修改密码
  6. mysql> show databases;
  7. ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.
  8. # 修改用户密码方法
  9. mysql> update mysql.user set authentication_string=password('admin') where user='root' and Host = 'localhost';
  10. mysql> alter user 'root'@'localhost' identified by 'admin';
  11. mysql> set password for 'root'@'localhost'=password('admin');
  12. # 不符合安全策略
  13. mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '123';
  14. ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
  15. # mysql5.7引入了一个 validate_password 插件来检验密码强度
  16. mysql> show variables like 'vali%';
  17. +--------------------------------------+--------+
  18. | Variable_name | Value |
  19. +--------------------------------------+--------+
  20. | validate_password_dictionary_file | |
  21. | validate_password_length | 8 |
  22. | validate_password_mixed_case_count | 1 |
  23. | validate_password_number_count | 1 |
  24. | validate_password_policy | MEDIUM |
  25. | validate_password_special_char_count | 1 |
  26. +--------------------------------------+--------+
  27. 6 rows in set (0.01 sec)
  28. # 意义如下:
  29. validate_password_length # 密码的最小长度,默认为8。
  30. validate_password_mixed_case_count # 至少要包含小写或大写字母的个数,默认为1。
  31. validate_password_number_count # 至少要包含的数字的个数,默认为1。
  32. validate_password_policy # 强度等级,
  33. # 可设置为0、1、2。
  34. #【0/LOW】:只检查长度。
  35. #【1/MEDIUM】:在0的基础上多检查数字、大小写、特殊字符。
  36. #【2/STRONG】:在1的基础上多检查特殊字符字典文件,此处为1。
  37. validate_password_special_char_count # 至少要包含的特殊字符的个数,默认为1。
  38. # 所以,初始设置密码必须大于8位、包含数字、大小写字母、特殊字符。

mysql root 无法本地登录

参考地址:

https://superuser.com/questions/603026/mysql-how-to-fix-access-denied-for-user-rootlocalhost

出错提示

  1. ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)

错误原理:root的plugin被修改成了auth_socket,用密码登录的plugin应该是mysql_native_password

  1. mysql root@(none):mysql> select user, plugin from mysql.user;
  2. +------------------+-----------------------+
  3. | user | plugin |
  4. +------------------+-----------------------+
  5. | root | auth_socket |
  6. | mysql.session | mysql_native_password |
  7. | mysql.sys | mysql_native_password |
  8. | debian-sys-maint | mysql_native_password |
  9. | root | mysql_native_password |
  10. | root | mysql_native_password |
  11. | ebeedb | mysql_native_password |
  12. +------------------+-----------------------+

关于auth_socket官方说明: https://dev.mysql.com/doc/mysql-security-excerpt/5.5/en/socket-authentication-plugin.html

修复方案:

  1. # 1. 使用安全设置启动 mysql 实例
  2. $ mysqld --skip-grant-tables
  3. # 2. 执行以下语句
  4. $ mysql -uroot mysql
  5. $ mysql> update user set authentication_string=PASSWORD('newPwd'), plugin='mysql_native_password' where user='root';
  6. Query OK, 1 row affected, 1 warning (0.00 sec)
  7. Rows matched: 1 Changed: 1 Warnings: 1
  8. $ mysql> flush privileges;
  9. Query OK, 0 rows affected (0.00 sec)
  10. # 3. 重启实例
  11. $ systemctl restart mysql

配置redis

  1. $ yum install redis
  2. $ systemctl enable redis && systemctl start redis

配置 gunicorn

测试 django 项目可以成功启动

  1. python manage.py runserver 0.0.0.0:8000

注册 gunicorn

  1. INSTALLED_APPS = (
  2. ...
  3. "gunicorn",
  4. )

尝试使用 gunicorn 启动 django 项目(注意缓存,可以修改一下端口)

  1. # djangopro.wsgi 为项目目录里 wsgi.py 文件
  2. # gunicorn 默认使用同步阻塞的网络模型(-k sync),对于大并发的访问可能表现不够好, 它还支持其它更好的模式,比如:gevent 或 meinheld
  3. $ gunicorn [-k sync] testproj.wsgi:application --bind 127.0.0.1:8000 --pid /tmp/gunicorn.pid --daemon

Kill this gunicorn process, we will let supervisor handle gunicorn hereafter.

  1. (PythonEnv)/tmp/testproj $ cat /tmp/gunicorn.pid
  2. 19363
  3. (PythonEnv)/tmp/testproj $ kill -9 19363

所有设置项目http://docs.gunicorn.org/en/stable/settings.html

gunicorn worker type

  1. # sync 请求/process
  2. gunicorn -w 1 -b 0.0.0.0:8000 futurestar.wsgi
  3. # gthread 请求/thread
  4. $ gunicorn -w 1 -k gthread --thread=4 -b 0.0.0.0:8000 futurestar.wsgi
  5. # async io 异步IO
  6. $ gunicorn -w 1 -k gevent -b 0.0.0.0:8000 futurestar.wsgi

https://medium.com/@genchilu/淺談-gunicorn-各個-worker-type-適合的情境-490b20707f28

配置 Supervisor

使用 supervisor 管理 gunicorn 方式:

  1. gunicorn 命令行方式
  2. gunicorn shell脚本方式

安装supervisor

  1. $ yum install supervisor

Supervisor 相当强大,提供了很丰富的功能,不过我们可能只需要用到其中一小部分。安装完成之后,可以编写配置文件,来满足自己的需求。为了方便,我们把配置分成两部分:supervisord(supervisor 是一个 C/S 模型的程序,这是 server 端,对应的有 client 端:supervisorctl)和应用程序(即我们要管理的程序)。

首先来看 supervisord 的配置文件。安装完 supervisor 之后,可以运行echo_supervisord_conf 命令输出默认的配置项,也可以重定向到一个配置文件里:

  1. $ echo_supervisord_conf > /etc/supervisord.conf

去除里面大部分注释和“不相关”的部分,我们可以先看这些配置:

  1. ; Sample supervisor config file.
  2. ;
  3. ; For more information on the config file, please see:
  4. ; http://supervisord.org/configuration.html
  5. ;
  6. ; Notes:
  7. ; - Shell expansion ("~" or "$HOME") is not supported. Environment
  8. ; variables can be expanded using this syntax: "%(ENV_HOME)s".
  9. ; - Comments must have a leading space: "a=b ;comment" not "a=b;comment".
  10. ; =====================
  11. ; 配置分三部分
  12. ; 1. supervisord server
  13. ; 2. supervisorctl client
  14. ; 3. programe
  15. ; =====================
  16. [supervisord]
  17. logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)
  18. pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)
  19. childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP)
  20. logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)
  21. logfile_backups=10 ; (num of main logfile rotation backups;default 10)
  22. loglevel=info ; (log level;default info; others: debug,warn,trace)
  23. ;nodaemon=true ; (start in foreground if true;default false)
  24. nodaemon=false ; (start in foreground if true;default false)
  25. minfds=1024 ; (min. avail startup file descriptors;default 1024)
  26. minprocs=200 ; (min. avail process descriptors;default 200)
  27. ;umask=022 ; (process file creation umask;default 022)
  28. ;user=chrism ; (default is current user, required if root)
  29. ;identifier=supervisor ; (supervisord identifier, default is 'supervisor')
  30. ;directory=/tmp ; (default is not to cd during start)
  31. ;nocleanup=true ; (don't clean up tempfiles at start;default false)
  32. ;childlogdir=/tmp ; ('AUTO' child log dir, default $TEMP)
  33. ;environment=KEY="value" ; (key value pairs to add to environment)
  34. ;strip_ansi=false ; (strip ansi escape codes in logs; def. false)
  35. ; the below section must remain in the config file for RPC
  36. ; (supervisorctl/web interface) to work, additional interfaces may be
  37. ; added by defining them in separate rpcinterface: sections
  38. ; 包含其他的配置文件
  39. ; 1. 使用 socket 管理 supervisor (二选一)
  40. ;[unix_http_server]
  41. ;file=/var/run/supervisor.sock ; (the path to the socket file)
  42. ;chmod=0700 ; sockef file mode (default 0700)
  43. ;[supervisorctl]
  44. ;serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket
  45. ;[rpcinterface:supervisor]
  46. ;files = /etc/supervisor/conf.d/*.conf
  47. ;supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface
  48. ; 2. 使用 tcp 管理 supervisor (二选一)
  49. [inet_http_server]
  50. port=127.0.0.1:9001 ;
  51. [rpcinterface:supervisor]
  52. supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
  53. [supervisorctl]
  54. superurl=http://127.0.0.1:9001
  55. ; 我们把上面这部分配置保存到 /etc/supervisord.conf(或其他任意有权限访问的文件),然后启动
  56. ; supervisord(通过 -c 选项指定配置文件路径,如果不指定会按照这个顺序查找配置文件:
  57. ; $CWD/supervisord.conf,
  58. ; $CWD/etc/supervisord.conf,
  59. ; /etc/supervisord.conf
  60. ; 上面我们已经把 supervisrod 运行起来了,现在可以添加我们要管理的进程的配置文件。这些配置可以都写到 supervisord.conf 文件里,如果应用程序很多,最好通过 include 的方式把不同的程序(组)写到不同的配置文件里。
  61. ;
  62. ; 为了举例,我们新建一个目录 /etc/supervisor/conf.d 用于存放这些配置文件,相应的把 /etc/supervisord.conf 里 include 部分的的配置修改一下:
  63. ;[include]
  64. ;files = /etc/supervisor/conf.d/*.conf
  65. ; ===================== 项目 futrestar ====================
  66. [program:futurestar]
  67. ; 指定运行目录
  68. directory=/srv/future_star_be/futurestar/apps/
  69. ; 运行目录下执行命令
  70. ; futurestar.wsgi 为项目中 wsgi.py 文件,directory 配置项需要能够定位到 futurestart.wsgi
  71. command=/home/python/.pyenv/versions/3.6.7/envs/futurestar36/bin/python /home/python/.pyenv/versions/futurestar36/bin/gunicorn futurestar.wsgi -b 0.0.0.0:9000 --access-logfile /tmp/futurestar.access.log --error-logfile /tmp/futurestar.error.log
  72. environment=PYTHONPATH="/home/python/.pyenv/versions/futurestar36/lib/python3.6/site-packages:$PYTHONPATH"
  73. user=python
  74. ; ================ 项目 futrestar_tcpserver ===============
  75. [program:futurestar_tcpserver]
  76. ; 指定运行目录
  77. directory=/srv/futurestar_tcpserver/
  78. ; 运行目录下执行命令
  79. command=/home/python/.pyenv/versions/futurestar_tcp36/bin/python /srv/futurestar_tcpserver/srv_maps.py
  80. environment=PYTHONPATH="/home/python/.pyenv/versions/futurestar36/lib/python3.6/site-packages:$PYTHONPATH"
  81. user=python

我们把上面这部分配置保存到 /etc/supervisord.conf(或其他任意有权限访问的文件),然后启动 supervisord(通过 -c 选项指定配置文件路径,如果不指定会按照这个顺序查找配置文件:

  • $CWD/supervisord.conf,
  • $CWD/etc/supervisord.conf,
  • /etc/supervisord.conf
  1. # 启动 supervisor
  2. $ supervisord -c /etc/supervisord.conf
  3. # 查看 supervisord 是否在运行
  4. $ ps aux | egrep supervisord

上面我们已经把 supervisrod 运行起来了,现在可以添加我们要管理的进程的配置文件。这些配置可以都写到 supervisord.conf 文件里,如果应用程序很多,最好通过 include 的方式把不同的程序(组)写到不同的配置文件里。

为了举例,我们新建一个目录 /etc/supervisor/ 用于存放这些配置文件,相应的,把 /etc/supervisord.conf 里 include 部分的的配置修改一下:

配置参考:https://www.agiliq.com/blog/2014/05/supervisor-with-django-and-gunicorn/

配置 program

上面我们已经把 supervisrod 运行起来了,现在可以添加我们要管理的进程的配置文件。这些配置可以都写到 supervisord.conf 文件里,如果应用程序很多,最好通过 include 的方式把不同的程序(组)写到不同的配置文件里。

为了举例,我们新建一个目录 /etc/supervisor/ 用于存放这些配置文件,相应的,把 /etc/supervisord.conf 里 include 部分的的配置修改一下:

  1. [include]
  2. files = /etc/supervisor/*.conf

测试 gunicorn 是否可以启用项目

(注意缓存,可以修改一下端口)

gunicorn 默认使用同步阻塞的网络模型(-k sync),对于大并发的访问可能表现不够好, 它还支持其它更好的模式,比如:geventmeinheld

假设有个用 Flask 开发的用户系统 usercenter, 生产环境使用 gunicorn 运行。项目代码位于 /home/leon/projects/usercenter,WSGI 对象位于 wsgi.py。在命令行启动的方式是这样的:

  1. cd /home/leon/projects/usercenter
  2. gunicorn -w 8 -b 0.0.0.0:17510 wsgi:app

所有设置项目http://docs.gunicorn.org/en/stable/settings.html

Gunicorn安装好后,接下来再写一个bash脚本做一些配置使之用起来更方便。 文件保存为bin/gunicorn_start.sh

对应的配置文件可能是:

http://supervisord.org/configuration.html?highlight=environment

  1. [program:test]
  2. directory = /home/wuyong/workspace ; 程序的启动目录
  3. command = python test.py ;启动命令,可以看出与手动在命令行启动的命令是一样的
  4. autostart = true ; supervisord 启动的时候也自动启动
  5. startsecs = 5 ; 启动 5 秒后没有异常退出,就当作已经正常启动了
  6. autorestart = true ; 程序异常退出后自动重启
  7. startretries = 3 ; 启动失败自动重试次数,默认是 3
  8. user = root ; 用哪个用户启动
  9. redirect_stderr = true ; stderr 重定向到 stdout,默认 false
  10. stdout_logfile_maxbytes = 20MB ; stdout 日志文件大小,默认 50MB
  11. stdout_logfile_backups = 20 ; stdout 日志文件备份数
  12. ; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录>(supervisord 会自动创建日志文件)
  13. stdout_logfile = /home/wuyong/workspace/test_out.log
  14. stderr_logfile = /home/wuyong/workspace/test_err.log
  15. ; 可以通过 environment 来添加需要的环境变量,一种常见的用法是修改 PYTHONPATH
  16. ; environment=PYTHONPATH=$PYTHONPATH:/path/to/somewhere

注意:子进程将继承用于启动“supervisord”的shell的环境变量,除了在此处重写的那些。

针对python环境

如果项目中使用了python的pyenv模块来设置环境,则 supervisord 配置文件中需要指定 python 环境的路径。其中有两种方式指定程序使用的python的环境:

  1. command=绝对路径
  2. environment=NAME=”value1” # 配置 PYTHONPATH

查看 pyenv 管理的虚拟环境

  1. $ pyenv which python # 查看虚拟环境的 python
  2. $ python # 进入虚拟环境的 python shell
  3. >>> import site
  4. >>> site.getsitepackages() # 查看 site-packages 包位置
  5. ['/home/python/.pyenv/versions/futurestar36_tcpserver/lib/python3.6/site-packages']

参考: 为什么需要虚拟环境

软链接上面项目的配置文件到 /etc/supervisor/conf.d/

  1. sudo ln -s /your/path/djangopro_supervisor.conf /etc/supervisor/conf.d/

使用supervisor

  1. $ systemctl enable supervisor.service
  2. $ systemctl start supervisor.service
  3. $ supervisorctl
  4. > gunicorn RUNNING pid 2642, uptime 0:00:03

然后,在您的身份验证后端settings.py设置AUTHENTICATION_BACKENDS中:

AUTHENTICATION_BACKENDS = ('path.to.UsernameOrEmailBackend,)\

请注意,此解决方案并不完美。例如,密码重置只适用于您USERNAME_FIELD设置中指定的字段。

解决方案:

可以通过实现自己的电子邮件身份验证后端来实现

  1. 在settings.py中替换自定义的用户模型
  2. 编写自定义身份验证后端的逻辑
  3. 在设置中指定自定义身份验证后端
  1. # settings.py
  2. AUTH_USER_MODEL = 'myapp.MyUser
  3. # 编写自定义身份验证后端的逻辑 <https://docs.djangoproject.com/zh-hans/2.0/topics/auth/customizing/>
  4. # 编写后端需要至少实现两个方法:
  5. # 1. get_user(user_id)
  6. # 2. authenticate(request, **credentials)
  7. # 以及其它一系列可选的权限相关的方法:ref:`authorization methods<authorization_methods> `.
  8. # - get_user 方法只接受一个参数``user_id``,user_id 有可能是 用户名、数据库 ID 或者其它任何值(该值必须是用户对象的主键),该方法返回一个用户对象。
  9. # - authenticate``方法接受 ``request 参数和 credentials 关键字参数,大多数情况下,该方法类似于下面的代码:
  10. #### 使用 systemd 管理
  11. 建立 gunicorn unit 配置文件
  12. ```ini
  13. [Unit]
  14. Description=gunicorn daemon
  15. After=network.target
  16. [Service]
  17. # Update all paths below
  18. # Setting provided are for reference only !
  19. User=root
  20. Group=www-data
  21. WorkingDirectory=/var/webapps/sbprod/django_project_dir
  22. ExecStart=/var/webapps/virtual_sample_env/bin/gunicorn --workers 2 --bind unix:/var/webapps/run/SOMENAME.sock Django_project_name.wsgi:application
  23. [Install]
  24. WantedBy=multi-user.target
  25. # WorkingDirectory 设置进程的工作目录。支持~或者以RootDirectory为基准的绝对路径
  26. # RootDirectory 设置以chroot方式执行进程的根目录。

修复UnicodeEncodeError文件上传

如果您UnicodeEncodeError在上传包含非ASCII字符的文件名的文件时,请确保将uWSGI配置为接受非ASCII文件名,方法是将其添加到您的uwsgi.ini

env = LANG=en_US.UTF-8

参考:https://docs.djangoproject.com/zh-hans/2.0/ref/unicode/#unicode-files

可以编写一个shell脚本,来运行程序

  1. #!/bin/bash
  2. set -x
  3. NAME='futurestar_app' # 应用的名称,默认为 gunicorn
  4. DJANGODIR=/srv/future_star_be/futurestar # django项目的目录
  5. #SOCKFILE=/tmp/gunicorn.sock # 使用这个sock来通信
  6. BIND='127.0.0.1:9000'
  7. USER=python # 运行此应用的用户
  8. GROUP=python # 运行此应用的组
  9. #NUM_WORKERS=3 # gunicorn使用的工作进程数
  10. NUM_WORKERS=1 # gunicorn使用的工作进程数
  11. DJANGO_SETTINGS_MODULE=futurestar.settings # django的配置文件
  12. DJANGO_WSGI_MODULE=futurestar.wsgi # wsgi模块
  13. GUNICORN_PATH="/home/python/.pyenv/versions/3.6.6/envs/futurestar_36/bin/python /home/python/.pyenv/versions/futurestar_36/bin/gunicorn" # gunicorn路径
  14. GUNICORN_LOG_PATH=/var/log/gunicorn
  15. echo "starting $NAME as `whoami`"
  16. #source ../bin/activate
  17. export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
  18. export PYTHONPATH=$DJANGODIR:$PYTHONPATH
  19. #如果gunicorn.sock所在目录不存在则创建
  20. #RUNDIR=$(dirname $SOCKFILE)
  21. #test -d $RUNDIR || mkdir -p $RUNDIR
  22. test -d $GUNICORN_LOG_PATH || mkdir -p $GUNICORN_LOG_PATH
  23. #启动Django
  24. cd $DJANGODIR
  25. exec ${GUNICORN_PATH} ${DJANGO_WSGI_MODULE}:application \
  26. --name $NAME \
  27. --workers $NUM_WORKERS \
  28. --user=$USER --group=$GROUP \
  29. --log-level=debug \
  30. --access-logfile=${GUNICORN_LOG_PATH}/${NAME}.access.log \
  31. --error-logfile=${GUNICORN_LOG_PATH}/${NAME}.error.log \
  32. #--bind=unix:$SOCKFILE \
  33. --bind=$BIND
  34. export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
  35. export PYTHONPATH=$DIR:$PYTHONPATH # 配置环境变量
  36. cd $DIR
  37. ##下面的/usr/local/bin/gunicorn, 使用 whereis gunicorn
  38. exec /usr/local/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \
  39. --name $NAME \
  40. --workers $WORKERS \
  41. --user=$USER \
  42. --group=$GROUP \
  43. --bind=$BIND \
  44. --log-level=$LOG_LEVEL \
  45. --log-file=/usr/local/bin/gunicorn.log # 需要事先创建log文件

为该文件增加执行权限并复制到/bin下面

  1. chmod u+x gunicorn_start.sh
  2. sudo cp gunicorn_start.sh /bin/gunicorn_start.sh
  3. $ sudo usermod -a -G nginx `python` # 添加 python到 nginx组
  4. $ chown -R python:nginx /srv/future_star_be/
  5. $ chown -R g+w !$
  6. $ sudo su - python
  7. $ bin/gunicorn_start.sh

配置 circus

circus 修复了一些 supervisor的缺点

安装

  1. $ pip install circus

配置实例

  1. # /etc/circus.ini
  2. [circus]
  3. check_delay = 5
  4. endpoint = tcp://127.0.0.1:5555
  5. pubsub_endpoint = tcp://127.0.0.1:5556
  6. statsd = true
  7. [watcher:myproject]
  8. working_dir = /var/www/django/myproject
  9. cmd = gunicorn
  10. args = -w 1 -t 60 --pythonpath=. -b 127.0.0.1:8081 myproject.wsgi
  11. uid = djangouser
  12. numprocesses = 1
  13. send_hup = true
  14. autostart = true
  15. stdout_stream.class = FileStream
  16. stdout_stream.filename = /var/log/myproject.stdout.log
  17. stdout_stream.max_bytes = 10485760
  18. stdout_stream.backup_count = 4
  19. stderr_stream.class = FileStream
  20. stderr_stream.filename = /var/log/myproject.stderr.log
  21. stderr_stream.max_bytes = 10485760
  22. stderr_stream.backup_count = 4
  23. [env:myproject]
  24. PATH = /home/djangouser/.virtualenvs/django/bin:$PATH
  25. TERM=rxvt-256color
  26. SHELL=/bin/bash
  27. USER=djangouser
  28. LANG=en_US.UTF-8
  29. HOME=/home/ecerp
  30. PYTHONPATH=/home/djangouser/.virtualenvs/django/lib/python3.4/site-packages

说明一下:

[circus] 节不用管,有一个就够;

[watcher:myproject][env:myproject] 是关于这个 django 项目的;前者是跑 django 的,后面是设置运行的虚拟环境的。

如果需要添加其他的 gunicorn 实例,只需要按样子添加这两个节即可。

项目实例 - 使用pyenv

  1. [python@VM_22_46_centos code]$ tail futurestar.ini
  2. working_dir = /srv/future_star_be/futurestar
  3. cmd = /home/python/.pyenv/versions/3.6.6/envs/futurestar_36/bin/python /home/python/.pyenv/versions/futurestar_36/bin/gunicorn
  4. args = -w 1 -t 60 -b 0.0.0.0:9000 futurestar.wsgi
  5. uid = python
  6. numprocesses = 1
  7. stdout_stream.class = FileStream
  8. stdout_stream.filename = /var/log/gunicorn/circus.stdout.log
  9. stdout_stream.refresh_time = 0.3
  10. stdout_stream.max-bytes = 10485760
  11. stdout_stream.backup_count = 4
  12. stderr_stream.class = FileStream
  13. stderr_stream.filename = /var/log/gunicorn/circus.stderr.log
  14. stderr_stream.refresh_time = 0.3
  15. stdout_stream.max-bytes = 10485760
  16. stdout_stream.backup_count = 4
  17. [env:futurestar]
  18. PATH =/home/python/.pyenv/versions/3.6.6/bin:$PATH
  19. PYTHONPATH=/home/python/.pyenv/versions/futurestar_36/lib/python3.6/site-packages:$PYTHONPATH

sysv部署 circus

  1. #!/bin/bash
  2. ### BEGIN INIT INFO
  3. # Provides: circusd
  4. # Required-Start: $remote_fs $network
  5. # Required-Stop: $remote_fs $network
  6. # Default-Start: 2 3 4 5
  7. # Default-Stop: 0 1 6
  8. # Short-Description: circus master control daemon
  9. # Description: This is a daemon that controls the circus minions
  10. ### END INIT INFO
  11. # source function library
  12. . /etc/init.d/functions
  13. RETVAL=0
  14. PATH=/sbin:/usr/sbin:/bin:/usr/bin
  15. DESC="circus daemon"
  16. NAME=circusd
  17. DAEMON=/usr/bin/circusd # circusd 命令地址
  18. PIDFILE=/var/run/$NAME.pid
  19. CONFIG=/etc/circus/circusd.ini # 项目配置文件地址
  20. LOGFILE=/var/log/circus/circus.log
  21. BOOTLOG=/var/log/circus/circus.boot
  22. DAEMON_ARGS="--log-output $LOGFILE --daemon --pidfile $PIDFILE $CONFIG"
  23. # Exit if the package is not installed
  24. [ -x "$DAEMON" ] || exit 5
  25. # Read configuration variable file if it is present
  26. [ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
  27. start() {
  28. echo -n "Starting circusd daemon: "
  29. daemon --pidfile="$PIDFILE" nohup $DAEMON $DAEMON_ARGS 2> $BOOTLOG
  30. RETVAL=$?
  31. echo
  32. return $RETVAL
  33. }
  34. stop() {
  35. echo -n "Stopping $DESC ..."
  36. killproc -p "$PIDFILE" $DAEMON
  37. RETVAL=$?
  38. echo
  39. return $RETVAL
  40. }
  41. rhstatus() {
  42. status -p "$PIDFILE" -l $NAME $DAEMON
  43. }
  44. restart() {
  45. stop
  46. start
  47. }
  48. case "$1" in
  49. start)
  50. start
  51. ;;
  52. stop)
  53. stop
  54. ;;
  55. restart|force-reload)
  56. restart
  57. ;;
  58. status)
  59. rhstatus
  60. ;;
  61. condrestart|try-restart)
  62. rhstatus >/dev/null 2>&1 || exit 0
  63. restart
  64. ;;
  65. *)
  66. echo "Usage: $0 {start|stop|restart|condrestart|try-restart|force-reload|status}"
  67. exit 3
  68. esac
  69. exit $?

systemd 部署 circus(可选)

官网:https://circus.readthedocs.io/en/latest/for-ops/deployment/

服务与日志

  1. $ ln -s '/usr/lib/systemd/system/circus.service' '/etc/systemd/system/circus.service'
  2. $ mkdir -p /home/taiga/logs touch /home/taiga/logs/gunicorn.stdout.log touch /home/taiga/logs/gunicorn.stderr.log cd /home/taiga/circus/ python3.5 setup.py install ln -s /opt/python3.5/bin/circusd /usr/local/bin/circusd systemctl start circus.service

注册启动脚本

  1. $ update-rc.d
  2. $ update-rc.d circusd defaults
  3. $ update-rc.d remove xxxx-named

Systemd 部署 Gunicorn(可选)

  1. # /etc/systemd/system/gunicorn.service
  2. [Unit]
  3. Description=gunicorn daemon
  4. Requires=gunicorn.socket
  5. After=network.target
  6. [Service]
  7. PIDFile=/run/gunicorn/pid
  8. User=root
  9. Group=nginx
  10. RuntimeDirectory=gunicorn
  11. WorkingDirectory=/var/webapps/django_project_dir
  12. # ExecStart=/var/webapps/virtual_sample_env/bin/gunicorn \
  13. --workers 2 \
  14. --bind unix:/var/webapps/run/SOMENAME.sock \ Django_project_name.wsgi:application
  15. ExecStart=/usr/bin/gunicorn \
  16. --pid /run/gunicorn/pid \
  17. --bind unix:/run/gunicorn/socket applicationname.wsgi
  18. # ExecStart=/path/to/gunicorn main:application -c /path/to/gunicorn.conf.py
  19. ExecReload=/bin/kill -s HUP $MAINPID
  20. ExecStop=/bin/kill -s TERM $MAINPID
  21. PrivateTmp=true
  22. [Install]
  23. WantedBy=multi-user.target

socket文件:/etc/systemd/system/gunicorn.socket

  1. [Unit]
  2. Description=gunicorn socket
  3. [Socket]
  4. ListenStream=/run/gunicorn/socket
  5. [Install]
  6. WantedBy=sockets.target

临时文件:/etc/tmpfiles.d/gunicorn.conf

  1. d /run/gunicorn 0755 root nginx -

设置开机启动

  1. systemctl enable gunicorn.scoket
  2. systemctl start gunicorn.socket
  3. # 测试 socket
  4. curl --unix-socket /run/gunicorn/socket http

nginx配置

  1. http {
  2. server {
  3. listen 8000;
  4. server_name 127.0.0.1;
  5. location / {
  6. proxy_pass http://unix:/run/gunicorn/socket;
  7. }
  8. }
  9. }

启动nginx

  1. systemctl enable nginx.service
  2. systemctl start nginx

tcp 部署

systemd service 服务部署

  1. $ root@server 04:43:02 ~ systemctl cat futurestar.service
  2. # /etc/systemd/system/futurestar.service
  3. [Unit]
  4. Description = Futurestar TCP Server
  5. After=network.target
  6. [Service]
  7. Type=simple
  8. ExecStartPre=/usr/bin/rm -f /var/run/futurestar.pid
  9. User=python
  10. Group=nginx
  11. WorkingDirectory=/srv/futurestar_tcpserver
  12. ExecStart = /home/python/.pyenv/versions/futurestar_tcp36/bin/python srv_maps.py
  13. TimeoutStopSec=5
  14. [Install]
  15. WantedBy=multi-user.target

自动化部署

fabric

最新版本:fabric 2.1

文档地址:http://docs.fabfile.org/en/2.1/

依赖:

  • invoke 实现CLI解析,任务组织和shell命令执行
  • paramiko 实现ssh功能,包括ssh和sftp会话、密钥管理等

入门

认证

配置

fabric的配置依赖于invoke功能

联网

命令行接口