第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 | “” | 用于保存用户上传文件的绝对路径 |
# Static files (CSS, JavaScript, Images)# https://docs.djangoproject.com/en/2.0/howto/static-files/STATIC_URL = '/static/'MEDIA_URL = "static/media/"STATIC_ROOT = os.path.join(BASE_DIR, 'static/') # collectstatic的绝对路径MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_URL) # 上传文件的绝对路径
部署
架构

协议
WSGI:PEP3333- Python Web Server Gateway Interface,一种规范,描述Web Server如何与 Web Application 通信的规范。
uwsgi:线程协议,不是通信协议,用于uWSGI server与web server的数据通信
服务器
web servernginx:支持uwsgi/WSGI/http/https/协议的Web Server
WSGI serverGunicorn:支持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服务器,这样可以达到很好的客户端响应。
环境依赖
架构
+---------+ +-----------+ +--------------+ +-----------+| client +-------> | nginx +----> | gunicorn +----> | app || | | | | | | |+---------+ +-----------+ +-------+------+ +-----------+^| 服务管理|+--------------+| supervisord || or systemd |+--------------+
环境依赖
# ubuntu/redhat 都建议安装好 开发工具套件 开发环境$ yum groupinstall "Development Tools" -y$ sudo yum -y install epel-release# ubuntu$ sudo apt-get install build-essential
安装 pyenv
# (可选)centos 6.5 安装依赖包$ yum -y install gcc gcc-c++ make git patch openssl-devel zlib-devel readline-devel sqlite-devel bzip2-devel bzip2-libs$ curl -L https://github.com/pyenv/pyenv-installer/raw/master/bin/pyenv-installer | bash$ pyenv install 3.6.6# (可选) 加速上面安装v=3.6.6; wget https://www.python.org/ftp/python/$v/Python-$v.tar.xz -P ~/.pyenv/cache/;pyenv install $v# 创建虚拟环境# pyenv virtualenv x.x.x name$ pyenv virtualenv 3.5.6 blog# 使用$ pyenv activate name
项目依赖
依需求依次安装
# 安装依赖pip3 install -r requirements.txt# mysqlclient 安装报错时,ubuntu 下安装此系统依赖sudo apt-get install libmysqlclient-dev# mysqlclient 安装报错时# pip install mysqlclient# centos 6.5$ yum install gcc python-devel mysql-devel -y# centos 7$ yum install mariadb-devel
(可选)安装douban源
# linux: ~/.pip/pip.conf[global]timeout = 60index-url = http://pypi.douban.com/simpletrusted-host = pypi.douban.com
项目 settings.py配置
#设置ALLOW_HOSTS = ['example.com','www.domainname.com', 'ipaddr']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。
- 在部署到生产环境的时候,只需要配置Apache把/static/映射到STATIC_ROOT。
- 然后运行manage.py collectstatic,自动地STATICFILES_DIRS列出的目录以及各个App下的static子目录的所有文件复制到STATIC_ROOT。
- 因为复制过程可能会覆盖掉原来的文件,所以,一定不能把我们辛苦做出来静态文件放这边!
在开发阶段,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设置
# Static files (CSS, JavaScript, Images)# https://docs.djangoproject.com/en/2.0/howto/static-files/STATIC_URL = '/static/'STATIC_ROOT = os.path.join(BASE_DIR, 'static')MEDIA_URL = "static/media/"MEDIA_ROOT = os.path.join(BASE_DIR, MEDIA_URL)ALLOWED_EXTENSIONS = set(['png', 'jpg', 'jpeg', 'gif'])# shell$ python manage.py collectstatic# nginx静态文件配置# 作用是为下面采集静态文件使用# STATIC_ROOT = os.path.join(BASE_DIR, "static/")
静态文件部署
$ python manage.py collectstatic
nginx/conf.d/project.conf 设置
# nginx 中只能设置成这样location /static {alias /path/to/your/mysite/static_dist; # your Django project's static files - amend as required}
配置mysql
安装与配置mysql5.7
# 下载yum源文件$ wget http://repo.mysql.com/mysql57-community-release-el7-9.noarch.rpm$ sudo rpm -ivh mysql57-community-release-el7-9.noarch.rpm# 更新yum软件包$ yum check-update# 安装mysql$ yum install mysql mysql-server# my.cnf 配置# [mysqld]# plugin-load=validate_password.so# validate-password=OFF # 关闭密码检查# character-set-server=utf8 # utf8 支持# skip-grant-tables # 跳过权限表检查# 建立数据库create database weme character set utf8;# 授权grant all privileges on *.* to 'root'@'localhost' identified by 'redhat';# 刷新权限表flush privileges;
修改 mysql root 密码
# mysql5.7 修改root密码# https://mysqldbblog.wordpress.com/2016/05/05/set-new-password-in-mysql-5-7-for-root/$ cat /var/log/mysqld.log | egrep 'password'2016-12-10T06:30:12.041470Z 1 [Note] A temporary password is generated for root@localhost: nWoo>o0rXtr4# 需要先修改密码mysql> show databases;ERROR 1820 (HY000): You must reset your password using ALTER USER statement before executing this statement.# 修改用户密码方法mysql> update mysql.user set authentication_string=password('admin') where user='root' and Host = 'localhost';mysql> alter user 'root'@'localhost' identified by 'admin';mysql> set password for 'root'@'localhost'=password('admin');# 不符合安全策略mysql> ALTER USER 'root'@'localhost' IDENTIFIED BY '123';ERROR 1819 (HY000): Your password does not satisfy the current policy requirements# mysql5.7引入了一个 validate_password 插件来检验密码强度mysql> show variables like 'vali%';+--------------------------------------+--------+| Variable_name | Value |+--------------------------------------+--------+| validate_password_dictionary_file | || validate_password_length | 8 || validate_password_mixed_case_count | 1 || validate_password_number_count | 1 || validate_password_policy | MEDIUM || validate_password_special_char_count | 1 |+--------------------------------------+--------+6 rows in set (0.01 sec)# 意义如下:validate_password_length # 密码的最小长度,默认为8。validate_password_mixed_case_count # 至少要包含小写或大写字母的个数,默认为1。validate_password_number_count # 至少要包含的数字的个数,默认为1。validate_password_policy # 强度等级,# 可设置为0、1、2。#【0/LOW】:只检查长度。#【1/MEDIUM】:在0的基础上多检查数字、大小写、特殊字符。#【2/STRONG】:在1的基础上多检查特殊字符字典文件,此处为1。validate_password_special_char_count # 至少要包含的特殊字符的个数,默认为1。# 所以,初始设置密码必须大于8位、包含数字、大小写字母、特殊字符。
mysql root 无法本地登录
参考地址:
https://superuser.com/questions/603026/mysql-how-to-fix-access-denied-for-user-rootlocalhost
出错提示
ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
错误原理:root的plugin被修改成了auth_socket,用密码登录的plugin应该是mysql_native_password
mysql root@(none):mysql> select user, plugin from mysql.user;+------------------+-----------------------+| user | plugin |+------------------+-----------------------+| root | auth_socket || mysql.session | mysql_native_password || mysql.sys | mysql_native_password || debian-sys-maint | mysql_native_password || root | mysql_native_password || root | mysql_native_password || ebeedb | mysql_native_password |+------------------+-----------------------+
关于auth_socket官方说明: https://dev.mysql.com/doc/mysql-security-excerpt/5.5/en/socket-authentication-plugin.html
修复方案:
# 1. 使用安全设置启动 mysql 实例$ mysqld --skip-grant-tables# 2. 执行以下语句$ mysql -uroot mysql$ mysql> update user set authentication_string=PASSWORD('newPwd'), plugin='mysql_native_password' where user='root';Query OK, 1 row affected, 1 warning (0.00 sec)Rows matched: 1 Changed: 1 Warnings: 1$ mysql> flush privileges;Query OK, 0 rows affected (0.00 sec)# 3. 重启实例$ systemctl restart mysql
配置redis
$ yum install redis$ systemctl enable redis && systemctl start redis
配置 gunicorn
测试 django 项目可以成功启动
python manage.py runserver 0.0.0.0:8000
注册 gunicorn
INSTALLED_APPS = (..."gunicorn",)
尝试使用 gunicorn 启动 django 项目(注意缓存,可以修改一下端口)
# djangopro.wsgi 为项目目录里 wsgi.py 文件# gunicorn 默认使用同步阻塞的网络模型(-k sync),对于大并发的访问可能表现不够好, 它还支持其它更好的模式,比如:gevent 或 meinheld$ 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.
(PythonEnv)/tmp/testproj $ cat /tmp/gunicorn.pid19363(PythonEnv)/tmp/testproj $ kill -9 19363
所有设置项目http://docs.gunicorn.org/en/stable/settings.html
gunicorn worker type
# sync 请求/processgunicorn -w 1 -b 0.0.0.0:8000 futurestar.wsgi# gthread 请求/thread$ gunicorn -w 1 -k gthread --thread=4 -b 0.0.0.0:8000 futurestar.wsgi# async io 异步IO$ gunicorn -w 1 -k gevent -b 0.0.0.0:8000 futurestar.wsgi
https://medium.com/@genchilu/淺談-gunicorn-各個-worker-type-適合的情境-490b20707f28
配置 Supervisor
使用 supervisor 管理 gunicorn 方式:
- gunicorn 命令行方式
- gunicorn shell脚本方式
安装supervisor
$ yum install supervisor
Supervisor 相当强大,提供了很丰富的功能,不过我们可能只需要用到其中一小部分。安装完成之后,可以编写配置文件,来满足自己的需求。为了方便,我们把配置分成两部分:supervisord(supervisor 是一个 C/S 模型的程序,这是 server 端,对应的有 client 端:supervisorctl)和应用程序(即我们要管理的程序)。
首先来看 supervisord 的配置文件。安装完 supervisor 之后,可以运行echo_supervisord_conf 命令输出默认的配置项,也可以重定向到一个配置文件里:
$ echo_supervisord_conf > /etc/supervisord.conf
去除里面大部分注释和“不相关”的部分,我们可以先看这些配置:
; Sample supervisor config file.;; For more information on the config file, please see:; http://supervisord.org/configuration.html;; Notes:; - Shell expansion ("~" or "$HOME") is not supported. Environment; variables can be expanded using this syntax: "%(ENV_HOME)s".; - Comments must have a leading space: "a=b ;comment" not "a=b;comment".; =====================; 配置分三部分; 1. supervisord server; 2. supervisorctl client; 3. programe; =====================[supervisord]logfile=/var/log/supervisor/supervisord.log ; (main log file;default $CWD/supervisord.log)pidfile=/var/run/supervisord.pid ; (supervisord pidfile;default supervisord.pid)childlogdir=/var/log/supervisor ; ('AUTO' child log dir, default $TEMP)logfile_maxbytes=50MB ; (max main logfile bytes b4 rotation;default 50MB)logfile_backups=10 ; (num of main logfile rotation backups;default 10)loglevel=info ; (log level;default info; others: debug,warn,trace);nodaemon=true ; (start in foreground if true;default false)nodaemon=false ; (start in foreground if true;default false)minfds=1024 ; (min. avail startup file descriptors;default 1024)minprocs=200 ; (min. avail process descriptors;default 200);umask=022 ; (process file creation umask;default 022);user=chrism ; (default is current user, required if root);identifier=supervisor ; (supervisord identifier, default is 'supervisor');directory=/tmp ; (default is not to cd during start);nocleanup=true ; (don't clean up tempfiles at start;default false);childlogdir=/tmp ; ('AUTO' child log dir, default $TEMP);environment=KEY="value" ; (key value pairs to add to environment);strip_ansi=false ; (strip ansi escape codes in logs; def. false); the below section must remain in the config file for RPC; (supervisorctl/web interface) to work, additional interfaces may be; added by defining them in separate rpcinterface: sections; 包含其他的配置文件; 1. 使用 socket 管理 supervisor (二选一);[unix_http_server];file=/var/run/supervisor.sock ; (the path to the socket file);chmod=0700 ; sockef file mode (default 0700);[supervisorctl];serverurl=unix:///var/run/supervisor.sock ; use a unix:// URL for a unix socket;[rpcinterface:supervisor];files = /etc/supervisor/conf.d/*.conf;supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface; 2. 使用 tcp 管理 supervisor (二选一)[inet_http_server]port=127.0.0.1:9001 ;[rpcinterface:supervisor]supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface[supervisorctl]superurl=http://127.0.0.1:9001; 我们把上面这部分配置保存到 /etc/supervisord.conf(或其他任意有权限访问的文件),然后启动; supervisord(通过 -c 选项指定配置文件路径,如果不指定会按照这个顺序查找配置文件:; $CWD/supervisord.conf,; $CWD/etc/supervisord.conf,; /etc/supervisord.conf; 上面我们已经把 supervisrod 运行起来了,现在可以添加我们要管理的进程的配置文件。这些配置可以都写到 supervisord.conf 文件里,如果应用程序很多,最好通过 include 的方式把不同的程序(组)写到不同的配置文件里。;; 为了举例,我们新建一个目录 /etc/supervisor/conf.d 用于存放这些配置文件,相应的把 /etc/supervisord.conf 里 include 部分的的配置修改一下:;[include];files = /etc/supervisor/conf.d/*.conf; ===================== 项目 futrestar ====================[program:futurestar]; 指定运行目录directory=/srv/future_star_be/futurestar/apps/; 运行目录下执行命令; futurestar.wsgi 为项目中 wsgi.py 文件,directory 配置项需要能够定位到 futurestart.wsgicommand=/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.logenvironment=PYTHONPATH="/home/python/.pyenv/versions/futurestar36/lib/python3.6/site-packages:$PYTHONPATH"user=python; ================ 项目 futrestar_tcpserver ===============[program:futurestar_tcpserver]; 指定运行目录directory=/srv/futurestar_tcpserver/; 运行目录下执行命令command=/home/python/.pyenv/versions/futurestar_tcp36/bin/python /srv/futurestar_tcpserver/srv_maps.pyenvironment=PYTHONPATH="/home/python/.pyenv/versions/futurestar36/lib/python3.6/site-packages:$PYTHONPATH"user=python
我们把上面这部分配置保存到 /etc/supervisord.conf(或其他任意有权限访问的文件),然后启动 supervisord(通过 -c 选项指定配置文件路径,如果不指定会按照这个顺序查找配置文件:
$CWD/supervisord.conf,$CWD/etc/supervisord.conf,/etc/supervisord.conf
# 启动 supervisor$ supervisord -c /etc/supervisord.conf# 查看 supervisord 是否在运行$ 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 部分的的配置修改一下:
[include]files = /etc/supervisor/*.conf
测试 gunicorn 是否可以启用项目
(注意缓存,可以修改一下端口)
gunicorn 默认使用同步阻塞的网络模型(
-k sync),对于大并发的访问可能表现不够好, 它还支持其它更好的模式,比如:gevent或meinheld假设有个用 Flask 开发的用户系统 usercenter, 生产环境使用 gunicorn 运行。项目代码位于
/home/leon/projects/usercenter,WSGI 对象位于 wsgi.py。在命令行启动的方式是这样的:
cd /home/leon/projects/usercentergunicorn -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
[program:test]directory = /home/wuyong/workspace ; 程序的启动目录command = python test.py ;启动命令,可以看出与手动在命令行启动的命令是一样的autostart = true ; 在 supervisord 启动的时候也自动启动startsecs = 5 ; 启动 5 秒后没有异常退出,就当作已经正常启动了autorestart = true ; 程序异常退出后自动重启startretries = 3 ; 启动失败自动重试次数,默认是 3user = root ; 用哪个用户启动redirect_stderr = true ; 把 stderr 重定向到 stdout,默认 falsestdout_logfile_maxbytes = 20MB ; stdout 日志文件大小,默认 50MBstdout_logfile_backups = 20 ; stdout 日志文件备份数; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录>(supervisord 会自动创建日志文件)stdout_logfile = /home/wuyong/workspace/test_out.logstderr_logfile = /home/wuyong/workspace/test_err.log; 可以通过 environment 来添加需要的环境变量,一种常见的用法是修改 PYTHONPATH; environment=PYTHONPATH=$PYTHONPATH:/path/to/somewhere
注意:子进程将继承用于启动“supervisord”的shell的环境变量,除了在此处重写的那些。
针对python环境
如果项目中使用了python的pyenv模块来设置环境,则 supervisord 配置文件中需要指定 python 环境的路径。其中有两种方式指定程序使用的python的环境:
- command=绝对路径
- environment=NAME=”value1” # 配置 PYTHONPATH
查看 pyenv 管理的虚拟环境
$ pyenv which python # 查看虚拟环境的 python$ python # 进入虚拟环境的 python shell>>> import site>>> site.getsitepackages() # 查看 site-packages 包位置['/home/python/.pyenv/versions/futurestar36_tcpserver/lib/python3.6/site-packages']
参考: 为什么需要虚拟环境
软链接上面项目的配置文件到 /etc/supervisor/conf.d/
sudo ln -s /your/path/djangopro_supervisor.conf /etc/supervisor/conf.d/
使用supervisor
$ systemctl enable supervisor.service$ systemctl start supervisor.service$ supervisorctl> gunicorn RUNNING pid 2642, uptime 0:00:03
然后,在您的身份验证后端settings.py设置AUTHENTICATION_BACKENDS中:
AUTHENTICATION_BACKENDS = ('path.to.UsernameOrEmailBackend,)\
请注意,此解决方案并不完美。例如,密码重置只适用于您USERNAME_FIELD设置中指定的字段。
解决方案:
可以通过实现自己的电子邮件身份验证后端来实现
- 在settings.py中替换自定义的用户模型
- 编写自定义身份验证后端的逻辑
- 在设置中指定自定义身份验证后端
# settings.pyAUTH_USER_MODEL = 'myapp.MyUser# 编写自定义身份验证后端的逻辑 <https://docs.djangoproject.com/zh-hans/2.0/topics/auth/customizing/># 编写后端需要至少实现两个方法:# 1. get_user(user_id)# 2. authenticate(request, **credentials)# 以及其它一系列可选的权限相关的方法:ref:`authorization methods<authorization_methods> `.# - get_user 方法只接受一个参数``user_id``,user_id 有可能是 用户名、数据库 ID 或者其它任何值(该值必须是用户对象的主键),该方法返回一个用户对象。# - authenticate``方法接受 ``request 参数和 credentials 关键字参数,大多数情况下,该方法类似于下面的代码:#### 使用 systemd 管理建立 gunicorn unit 配置文件```ini[Unit]Description=gunicorn daemonAfter=network.target[Service]# Update all paths below# Setting provided are for reference only !User=rootGroup=www-dataWorkingDirectory=/var/webapps/sbprod/django_project_dirExecStart=/var/webapps/virtual_sample_env/bin/gunicorn --workers 2 --bind unix:/var/webapps/run/SOMENAME.sock Django_project_name.wsgi:application[Install]WantedBy=multi-user.target# WorkingDirectory 设置进程的工作目录。支持~或者以RootDirectory为基准的绝对路径# 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脚本,来运行程序
#!/bin/bashset -xNAME='futurestar_app' # 应用的名称,默认为 gunicornDJANGODIR=/srv/future_star_be/futurestar # django项目的目录#SOCKFILE=/tmp/gunicorn.sock # 使用这个sock来通信BIND='127.0.0.1:9000'USER=python # 运行此应用的用户GROUP=python # 运行此应用的组#NUM_WORKERS=3 # gunicorn使用的工作进程数NUM_WORKERS=1 # gunicorn使用的工作进程数DJANGO_SETTINGS_MODULE=futurestar.settings # django的配置文件DJANGO_WSGI_MODULE=futurestar.wsgi # wsgi模块GUNICORN_PATH="/home/python/.pyenv/versions/3.6.6/envs/futurestar_36/bin/python /home/python/.pyenv/versions/futurestar_36/bin/gunicorn" # gunicorn路径GUNICORN_LOG_PATH=/var/log/gunicornecho "starting $NAME as `whoami`"#source ../bin/activateexport DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULEexport PYTHONPATH=$DJANGODIR:$PYTHONPATH#如果gunicorn.sock所在目录不存在则创建#RUNDIR=$(dirname $SOCKFILE)#test -d $RUNDIR || mkdir -p $RUNDIRtest -d $GUNICORN_LOG_PATH || mkdir -p $GUNICORN_LOG_PATH#启动Djangocd $DJANGODIRexec ${GUNICORN_PATH} ${DJANGO_WSGI_MODULE}:application \--name $NAME \--workers $NUM_WORKERS \--user=$USER --group=$GROUP \--log-level=debug \--access-logfile=${GUNICORN_LOG_PATH}/${NAME}.access.log \--error-logfile=${GUNICORN_LOG_PATH}/${NAME}.error.log \#--bind=unix:$SOCKFILE \--bind=$BINDexport DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULEexport PYTHONPATH=$DIR:$PYTHONPATH # 配置环境变量cd $DIR##下面的/usr/local/bin/gunicorn, 使用 whereis gunicornexec /usr/local/bin/gunicorn ${DJANGO_WSGI_MODULE}:application \--name $NAME \--workers $WORKERS \--user=$USER \--group=$GROUP \--bind=$BIND \--log-level=$LOG_LEVEL \--log-file=/usr/local/bin/gunicorn.log # 需要事先创建log文件
为该文件增加执行权限并复制到/bin下面
chmod u+x gunicorn_start.shsudo cp gunicorn_start.sh /bin/gunicorn_start.sh$ sudo usermod -a -G nginx `python` # 添加 python到 nginx组$ chown -R python:nginx /srv/future_star_be/$ chown -R g+w !$$ sudo su - python$ bin/gunicorn_start.sh
配置 circus
circus 修复了一些 supervisor的缺点
安装
$ pip install circus
配置实例
# /etc/circus.ini[circus]check_delay = 5endpoint = tcp://127.0.0.1:5555pubsub_endpoint = tcp://127.0.0.1:5556statsd = true[watcher:myproject]working_dir = /var/www/django/myprojectcmd = gunicornargs = -w 1 -t 60 --pythonpath=. -b 127.0.0.1:8081 myproject.wsgiuid = djangousernumprocesses = 1send_hup = trueautostart = truestdout_stream.class = FileStreamstdout_stream.filename = /var/log/myproject.stdout.logstdout_stream.max_bytes = 10485760stdout_stream.backup_count = 4stderr_stream.class = FileStreamstderr_stream.filename = /var/log/myproject.stderr.logstderr_stream.max_bytes = 10485760stderr_stream.backup_count = 4[env:myproject]PATH = /home/djangouser/.virtualenvs/django/bin:$PATHTERM=rxvt-256colorSHELL=/bin/bashUSER=djangouserLANG=en_US.UTF-8HOME=/home/ecerpPYTHONPATH=/home/djangouser/.virtualenvs/django/lib/python3.4/site-packages
说明一下:
[circus] 节不用管,有一个就够;
[watcher:myproject] 和 [env:myproject] 是关于这个 django 项目的;前者是跑 django 的,后面是设置运行的虚拟环境的。
如果需要添加其他的 gunicorn 实例,只需要按样子添加这两个节即可。
项目实例 - 使用pyenv
[python@VM_22_46_centos code]$ tail futurestar.iniworking_dir = /srv/future_star_be/futurestarcmd = /home/python/.pyenv/versions/3.6.6/envs/futurestar_36/bin/python /home/python/.pyenv/versions/futurestar_36/bin/gunicornargs = -w 1 -t 60 -b 0.0.0.0:9000 futurestar.wsgiuid = pythonnumprocesses = 1stdout_stream.class = FileStreamstdout_stream.filename = /var/log/gunicorn/circus.stdout.logstdout_stream.refresh_time = 0.3stdout_stream.max-bytes = 10485760stdout_stream.backup_count = 4stderr_stream.class = FileStreamstderr_stream.filename = /var/log/gunicorn/circus.stderr.logstderr_stream.refresh_time = 0.3stdout_stream.max-bytes = 10485760stdout_stream.backup_count = 4[env:futurestar]PATH =/home/python/.pyenv/versions/3.6.6/bin:$PATHPYTHONPATH=/home/python/.pyenv/versions/futurestar_36/lib/python3.6/site-packages:$PYTHONPATH
sysv部署 circus
#!/bin/bash### BEGIN INIT INFO# Provides: circusd# Required-Start: $remote_fs $network# Required-Stop: $remote_fs $network# Default-Start: 2 3 4 5# Default-Stop: 0 1 6# Short-Description: circus master control daemon# Description: This is a daemon that controls the circus minions### END INIT INFO# source function library. /etc/init.d/functionsRETVAL=0PATH=/sbin:/usr/sbin:/bin:/usr/binDESC="circus daemon"NAME=circusdDAEMON=/usr/bin/circusd # circusd 命令地址PIDFILE=/var/run/$NAME.pidCONFIG=/etc/circus/circusd.ini # 项目配置文件地址LOGFILE=/var/log/circus/circus.logBOOTLOG=/var/log/circus/circus.bootDAEMON_ARGS="--log-output $LOGFILE --daemon --pidfile $PIDFILE $CONFIG"# Exit if the package is not installed[ -x "$DAEMON" ] || exit 5# Read configuration variable file if it is present[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAMEstart() {echo -n "Starting circusd daemon: "daemon --pidfile="$PIDFILE" nohup $DAEMON $DAEMON_ARGS 2> $BOOTLOGRETVAL=$?echoreturn $RETVAL}stop() {echo -n "Stopping $DESC ..."killproc -p "$PIDFILE" $DAEMONRETVAL=$?echoreturn $RETVAL}rhstatus() {status -p "$PIDFILE" -l $NAME $DAEMON}restart() {stopstart}case "$1" instart)start;;stop)stop;;restart|force-reload)restart;;status)rhstatus;;condrestart|try-restart)rhstatus >/dev/null 2>&1 || exit 0restart;;*)echo "Usage: $0 {start|stop|restart|condrestart|try-restart|force-reload|status}"exit 3esacexit $?
systemd 部署 circus(可选)
官网:https://circus.readthedocs.io/en/latest/for-ops/deployment/
服务与日志
$ ln -s '/usr/lib/systemd/system/circus.service' '/etc/systemd/system/circus.service'$ 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
注册启动脚本
$ update-rc.d$ update-rc.d circusd defaults$ update-rc.d remove xxxx-named
Systemd 部署 Gunicorn(可选)
# /etc/systemd/system/gunicorn.service[Unit]Description=gunicorn daemonRequires=gunicorn.socketAfter=network.target[Service]PIDFile=/run/gunicorn/pidUser=rootGroup=nginxRuntimeDirectory=gunicornWorkingDirectory=/var/webapps/django_project_dir# ExecStart=/var/webapps/virtual_sample_env/bin/gunicorn \--workers 2 \--bind unix:/var/webapps/run/SOMENAME.sock \ Django_project_name.wsgi:applicationExecStart=/usr/bin/gunicorn \--pid /run/gunicorn/pid \--bind unix:/run/gunicorn/socket applicationname.wsgi# ExecStart=/path/to/gunicorn main:application -c /path/to/gunicorn.conf.pyExecReload=/bin/kill -s HUP $MAINPIDExecStop=/bin/kill -s TERM $MAINPIDPrivateTmp=true[Install]WantedBy=multi-user.target
socket文件:/etc/systemd/system/gunicorn.socket
[Unit]Description=gunicorn socket[Socket]ListenStream=/run/gunicorn/socket[Install]WantedBy=sockets.target
临时文件:/etc/tmpfiles.d/gunicorn.conf
d /run/gunicorn 0755 root nginx -
设置开机启动
systemctl enable gunicorn.scoketsystemctl start gunicorn.socket# 测试 socketcurl --unix-socket /run/gunicorn/socket http
nginx配置
http {server {listen 8000;server_name 127.0.0.1;location / {proxy_pass http://unix:/run/gunicorn/socket;}}}
启动nginx
systemctl enable nginx.servicesystemctl start nginx
tcp 部署
systemd service 服务部署
$ root@server 04:43:02 ~ → systemctl cat futurestar.service# /etc/systemd/system/futurestar.service[Unit]Description = Futurestar TCP ServerAfter=network.target[Service]Type=simpleExecStartPre=/usr/bin/rm -f /var/run/futurestar.pidUser=pythonGroup=nginxWorkingDirectory=/srv/futurestar_tcpserverExecStart = /home/python/.pyenv/versions/futurestar_tcp36/bin/python srv_maps.pyTimeoutStopSec=5[Install]WantedBy=multi-user.target
自动化部署
fabric
最新版本:fabric 2.1
文档地址:http://docs.fabfile.org/en/2.1/
依赖:
- invoke 实现CLI解析,任务组织和shell命令执行
- paramiko 实现ssh功能,包括ssh和sftp会话、密钥管理等
入门
认证
配置
fabric的配置依赖于invoke功能
