第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 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服务器,这样可以达到很好的客户端响应。
环境依赖
架构
+---------+ +-----------+ +--------------+ +-----------+
| 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 = 60
index-url = http://pypi.douban.com/simple
trusted-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.pid
19363
(PythonEnv)/tmp/testproj $ kill -9 19363
所有设置项目http://docs.gunicorn.org/en/stable/settings.html
gunicorn worker type
# sync 请求/process
gunicorn -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.wsgi
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
environment=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.py
environment=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/usercenter
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
[program:test]
directory = /home/wuyong/workspace ; 程序的启动目录
command = python test.py ;启动命令,可以看出与手动在命令行启动的命令是一样的
autostart = true ; 在 supervisord 启动的时候也自动启动
startsecs = 5 ; 启动 5 秒后没有异常退出,就当作已经正常启动了
autorestart = true ; 程序异常退出后自动重启
startretries = 3 ; 启动失败自动重试次数,默认是 3
user = root ; 用哪个用户启动
redirect_stderr = true ; 把 stderr 重定向到 stdout,默认 false
stdout_logfile_maxbytes = 20MB ; stdout 日志文件大小,默认 50MB
stdout_logfile_backups = 20 ; stdout 日志文件备份数
; stdout 日志文件,需要注意当指定目录不存在时无法正常启动,所以需要手动创建目录>(supervisord 会自动创建日志文件)
stdout_logfile = /home/wuyong/workspace/test_out.log
stderr_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.py
AUTH_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 daemon
After=network.target
[Service]
# Update all paths below
# Setting provided are for reference only !
User=root
Group=www-data
WorkingDirectory=/var/webapps/sbprod/django_project_dir
ExecStart=/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/bash
set -x
NAME='futurestar_app' # 应用的名称,默认为 gunicorn
DJANGODIR=/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/gunicorn
echo "starting $NAME as `whoami`"
#source ../bin/activate
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DJANGODIR:$PYTHONPATH
#如果gunicorn.sock所在目录不存在则创建
#RUNDIR=$(dirname $SOCKFILE)
#test -d $RUNDIR || mkdir -p $RUNDIR
test -d $GUNICORN_LOG_PATH || mkdir -p $GUNICORN_LOG_PATH
#启动Django
cd $DJANGODIR
exec ${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=$BIND
export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE
export PYTHONPATH=$DIR:$PYTHONPATH # 配置环境变量
cd $DIR
##下面的/usr/local/bin/gunicorn, 使用 whereis gunicorn
exec /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.sh
sudo 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 = 5
endpoint = tcp://127.0.0.1:5555
pubsub_endpoint = tcp://127.0.0.1:5556
statsd = true
[watcher:myproject]
working_dir = /var/www/django/myproject
cmd = gunicorn
args = -w 1 -t 60 --pythonpath=. -b 127.0.0.1:8081 myproject.wsgi
uid = djangouser
numprocesses = 1
send_hup = true
autostart = true
stdout_stream.class = FileStream
stdout_stream.filename = /var/log/myproject.stdout.log
stdout_stream.max_bytes = 10485760
stdout_stream.backup_count = 4
stderr_stream.class = FileStream
stderr_stream.filename = /var/log/myproject.stderr.log
stderr_stream.max_bytes = 10485760
stderr_stream.backup_count = 4
[env:myproject]
PATH = /home/djangouser/.virtualenvs/django/bin:$PATH
TERM=rxvt-256color
SHELL=/bin/bash
USER=djangouser
LANG=en_US.UTF-8
HOME=/home/ecerp
PYTHONPATH=/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.ini
working_dir = /srv/future_star_be/futurestar
cmd = /home/python/.pyenv/versions/3.6.6/envs/futurestar_36/bin/python /home/python/.pyenv/versions/futurestar_36/bin/gunicorn
args = -w 1 -t 60 -b 0.0.0.0:9000 futurestar.wsgi
uid = python
numprocesses = 1
stdout_stream.class = FileStream
stdout_stream.filename = /var/log/gunicorn/circus.stdout.log
stdout_stream.refresh_time = 0.3
stdout_stream.max-bytes = 10485760
stdout_stream.backup_count = 4
stderr_stream.class = FileStream
stderr_stream.filename = /var/log/gunicorn/circus.stderr.log
stderr_stream.refresh_time = 0.3
stdout_stream.max-bytes = 10485760
stdout_stream.backup_count = 4
[env:futurestar]
PATH =/home/python/.pyenv/versions/3.6.6/bin:$PATH
PYTHONPATH=/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/functions
RETVAL=0
PATH=/sbin:/usr/sbin:/bin:/usr/bin
DESC="circus daemon"
NAME=circusd
DAEMON=/usr/bin/circusd # circusd 命令地址
PIDFILE=/var/run/$NAME.pid
CONFIG=/etc/circus/circusd.ini # 项目配置文件地址
LOGFILE=/var/log/circus/circus.log
BOOTLOG=/var/log/circus/circus.boot
DAEMON_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/$NAME
start() {
echo -n "Starting circusd daemon: "
daemon --pidfile="$PIDFILE" nohup $DAEMON $DAEMON_ARGS 2> $BOOTLOG
RETVAL=$?
echo
return $RETVAL
}
stop() {
echo -n "Stopping $DESC ..."
killproc -p "$PIDFILE" $DAEMON
RETVAL=$?
echo
return $RETVAL
}
rhstatus() {
status -p "$PIDFILE" -l $NAME $DAEMON
}
restart() {
stop
start
}
case "$1" in
start)
start
;;
stop)
stop
;;
restart|force-reload)
restart
;;
status)
rhstatus
;;
condrestart|try-restart)
rhstatus >/dev/null 2>&1 || exit 0
restart
;;
*)
echo "Usage: $0 {start|stop|restart|condrestart|try-restart|force-reload|status}"
exit 3
esac
exit $?
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 daemon
Requires=gunicorn.socket
After=network.target
[Service]
PIDFile=/run/gunicorn/pid
User=root
Group=nginx
RuntimeDirectory=gunicorn
WorkingDirectory=/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:application
ExecStart=/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.py
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID
PrivateTmp=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.scoket
systemctl start gunicorn.socket
# 测试 socket
curl --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.service
systemctl start nginx
tcp 部署
systemd service 服务部署
$ root@server 04:43:02 ~ → systemctl cat futurestar.service
# /etc/systemd/system/futurestar.service
[Unit]
Description = Futurestar TCP Server
After=network.target
[Service]
Type=simple
ExecStartPre=/usr/bin/rm -f /var/run/futurestar.pid
User=python
Group=nginx
WorkingDirectory=/srv/futurestar_tcpserver
ExecStart = /home/python/.pyenv/versions/futurestar_tcp36/bin/python srv_maps.py
TimeoutStopSec=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功能