0%

Django+uWsgi+nginx+https 完全配置

编译Nginx

  • 这里要使用http v2所以 nginx 版要大于1.9.4,nginx 主要用这里的 WEB 前端反向代理.
1
2
# apt-get install libpcre3-dev libssl-dev  zlib1g-dev libzip-dev
# ./configure --with-http_v2_module --with-http_ssl_module --with-threads --with-poll_module --with-file_aio --user=nginx --group=nginx --with-http_gzip_module --with-stream --with-stream_ssl_module --with-pcre

nginx配置

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
# cat /usr/local/nginx/conf/nginx.conf | grep -v '^$' | grep -v '#'
worker_processes 8;
worker_rlimit_nofile 1048576;
events {
use epoll;
multi_accept on;
worker_connections 65535;
}
http {
include mime.types;
default_type application/octet-stream;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
reset_timedout_connection on;
client_body_timeout 10;
send_timeout 2;
keepalive_timeout 30;
keepalive_requests 500000;
client_body_buffer_size 128k;
client_max_body_size 10m;
client_header_buffer_size 8k;
large_client_header_buffers 16 64k;
output_buffers 1 32k;
postpone_output 1460;
gzip on;
gzip_http_version 1.0;
gzip_proxied any;
gzip_min_length 500;
gzip_disable "MSIE [1-6]\.";
gzip_types text/plain text/xml text/css
text/comma-separated-values
text/javascript
application/x-javascript
application/atom+xml;
server {
listen 443 ssl http2 ;

server_name ssl.example.com;

## 优化HTTPS设置
ssl_session_cache shared:SSL:200m;
ssl_session_timeout 1800m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
add_header Strict-Transport-Security "max-age=31536000" always;
ssl_ciphers AES128-SHA:DES-CBC3-SHA:AES128-SHA256:!ADH:!AECDH:!MD5;

# 这里使用Let's Encrypt 的证书,免费值得信赖. 后面会讲如何申请与更新它.
ssl_certificate /etc/letsencrypt/live/exmaple.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/fullchain.pem;

access_log off;
location /static/ {
alias /home/www/mqtt_auth/static/;
}

location / {
include uwsgi_params;
# 这里把请求转发给uwsgi 处理.
uwsgi_pass unix:///tmp/uwsgi.sock;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

申请Let' Encrypt证书

  • Let’s Encrypt 是由互联网安全研究小组(ISRG,一个公益组织)提供的服务.主要赞助商包括电子前哨基金会,Mozilla 基金会,Akamai 以及思科.

  • 这里参照Certbot安装相关的客户端程序.根据自已的域创建一个配置文件,用于自动请求证书.

1
2
3
4
5
6
7
8
9
10
# cat /etc/letsencrypt/configs/www.example.com.conf
domains = www.example.com
rsa-key-size = 2048
email = yjdwbj@gmail.com
text = True

authenticator = standalone

webroot-path = /opt/www


测试NGINX HTTPS设置

  • 查看网站使用的证书
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
42
43
44
45
46
openssl s_client -connect  ftp.example.com:18090
[...]
Certificate chain
0 s:/CN=ftp.example.com
i:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
1 s:/C=US/O=Let's Encrypt/CN=Let's Encrypt Authority X3
i:/O=Digital Signature Trust Co./CN=DST Root CA X3
---
Server certificate
....
No client certificate CA names sent
---
SSL handshake has read 2812 bytes and written 631 bytes
---
New, TLSv1/SSLv3, Cipher is AES128-SHA
Server public key is 2048 bit
Secure Renegotiation IS supported
Compression: NONE
Expansion: NONE
SSL-Session:
Protocol : TLSv1.2
Cipher : AES128-SHA
Session-ID: 47AD316DA0733301D8E2C982BE52168749FF6272CA584466584DA39C28EC2FD9
Session-ID-ctx:
Master-Key: D2EA8F60B4E83D7CA91DD42CA176D6AB21608824C2A7631130291214E7092831B0D795B8E82085E7BCF2D5D32C7A06CA
Key-Arg : None
PSK identity: None
PSK identity hint: None
SRP username: None
TLS session ticket lifetime hint: 600000 (seconds)
TLS session ticket:
0000 - 3a a1 7f 93 72 a6 d1 04-d9 79 f7 ab 24 0d 17 94 :...r....y..$...
0010 - fa 68 1e e1 1c 6c b0 8f-04 cb ae ad 3a 16 68 b3 .h...l......:.h.
0020 - e4 1a 69 46 30 08 21 50-6f 1d c5 7e d5 77 a8 0c ..iF0.!Po..~.w..
0030 - 66 74 82 f8 3d bb 2f 2a-43 71 65 f3 a9 d0 36 8e ft..=./*Cqe...6.
0040 - 8b 57 d8 a3 4f 9d c8 42-89 7e 9e 27 bc 7d 7a 21 .W..O..B.~.'.}z!
0050 - 5e fe 99 63 b7 20 9d c4-1a ad 80 16 4c cc 67 13 ^..c. ......L.g.
0060 - f1 7e cf 48 f6 ee c4 d6-dc 49 0b f1 c5 58 59 b2 .~.H.....I...XY.
0070 - 2b 4e 74 b6 05 74 b1 3c-68 72 b2 3a 92 3e 31 b9 +Nt..t.<hr.:.>1.
0080 - c6 99 20 7a 80 63 93 e8-e5 7c a5 be 7a 49 a9 43 .. z.c...|..zI.C
0090 - 08 df 34 18 d5 91 0c b0-1d 53 ca b3 24 ea 24 c5 ..4......S..$.$.
00a0 - c0 8f d0 74 76 3d 7d a0-e7 59 fe e7 87 57 68 8f ...tv=}..Y...Wh.

Start Time: 1481641417
Timeout : 300 (sec)
Verify return code: 0 (ok)

SSLScan测试

  • 根据不同的发行版安装 sslcan , apt-get install sslcan.
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
$ sslscan -tlsall ftp.example.com:18090
_
___ ___| |___ ___ __ _ _ __
/ __/ __| / __|/ __/ _` | '_ \
\__ \__ \ \__ \ (_| (_| | | | |
|___/___/_|___/\___\__,_|_| |_|

Version 1.8.2
http://www.titania.co.uk
Copyright Ian Ventura-Whiting 2009

Testing SSL server ftp.example.com on port 18090

Supported Server Cipher(s):
Failed SSLv3 256 bits ECDHE-RSA-AES256-GCM-SHA384
Failed SSLv3 256 bits ECDHE-ECDSA-AES256-GCM-SHA384
Failed TLSv1 112 bits SRP-DSS-3DES-EDE-CBC-SHA
Failed TLSv1 112 bits SRP-RSA-3DES-EDE-CBC-SHA
Accepted TLSv1 112 bits DES-CBC3-SHA
Failed TLSv1 112 bits PSK-3DES-EDE-CBC-SHA
Rejected TLSv1 0 bits ECDH-ECDSA-NULL-SHA
Failed TLSv1 0 bits NULL-SHA256
[...]

Prefered Server Cipher(s):
TLSv1 128 bits AES128-SHA

SSL Certificate:
Version: 2
SerialCPS: http://cps.letsencrypt.org
[....]
User Notice:
Explicit Text: This Certificate may only be relied upon by Relying Parties and only in accordance with the Certificate Policy found at https://letsencrypt.org/repository/

Verify Certificate:
unable to get local issuer certificate

NMAP测试

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
$ nmap --script ssl-enum-ciphers -p 443 ssl.example.com

Starting Nmap 6.47 ( http://nmap.org ) at 2016-12-13 23:13 CST
Nmap scan report for ssl.example.com (18x.2x4.21.5x)
Host is up (0.0063s latency).
PORT STATE SERVICE
443/tcp open https
| ssl-enum-ciphers:
| TLSv1.0:
| ciphers:
| TLS_RSA_WITH_3DES_EDE_CBC_SHA - strong
| TLS_RSA_WITH_AES_128_CBC_SHA - strong
| compressors:
| NULL
| TLSv1.1:
| ciphers:
| TLS_RSA_WITH_3DES_EDE_CBC_SHA - strong
| TLS_RSA_WITH_AES_128_CBC_SHA - strong
| compressors:
| NULL
| TLSv1.2:
| ciphers:
| TLS_RSA_WITH_3DES_EDE_CBC_SHA - strong
| TLS_RSA_WITH_AES_128_CBC_SHA - strong
| TLS_RSA_WITH_AES_128_CBC_SHA256 - strong
| compressors:
| NULL
|_ least strength: strong

Nmap done: 1 IP address (1 host up) scanned in 0.27 seconds

在线检测


Django 环境

  • 安装 virtualenv
1
2
3
4
5
6
7
8
9
10
11

# pip install virtualenv

# cd /opt/www

# virtualenv pyenv

# source pyenv/bin/active
 # 这里通过pip安装项目要用的库.
(pyenv) root@localhost:/opt/www# pip install django uwsgi

Django关键配置

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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# 使用Redis 保存django的会话.
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION" : "unix://:f4e4821080ca489d3361a520fc2123495755559b45fb24323c5b02e79163e425@/var/run/redis/redis.sock?db=1",
"OPTIONS": {
"DB":0,
"PASSWORD":"f4e4821080ca489d3361a520fc2123495755559b45fb24323c5b02e79163e425",
"CONNECTION_POOL_KWARGS": {"max_connections": 65535},
}
}
}

SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
PASSWORD_HASER=[
"django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher",
"django.contrib.auth.hashers.SHA1PasswordHasher",
]
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'user_manager',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'my_app.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [os.path.join(BASE_DIR),'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'my_app.wsgi.application'

# 配置连接接两个数据库
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME' : 'testapp',
'USER' :'testapp',
'PASSWORD':'testapp123',
'HOST' : '127.0.0.1',
## 端口这里可以指定数据库的端口,也可以是连接池的端口
'PORT' : '5432',
},
'seconddb':{
'ENGINE':'django.db.backends.postgresql_psycopg2',
'NAME' : 'otherdb',
'USER' : 'testapp',
'PASSWORD':'testapp123',
'HOST':'127.0.0.1',
'PORT':'5432',
}
}
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_L10N = True
USE_TZ = True
AUTO_LOGOUT_DELAY = 1
APPEND_SLASH=True
STATICFILES_DIRS = (
os.path.join(BASE_DIR,'static'),
)
STATIC_ROOT = os.path.join(BASE_DIR)
STATIC_URL = '/static/'

uWSGI 配置

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

project = my_app
base = /home/www
chdir = %(base)/%(project)
module = %(project).wsgi:application
uid = nginx
gid = nginx
listen = 65536
master = true
virtualenv = /home/www/pyenv
home = /home/www/pyenv
master = true
cpu = affinity
async = true
py-autoreload=true
processes = 88
die-on-term = true
#threads = 32
max-requests = 65536
#socket = /tmp/uwsgi.sock
#http = 127.0.0.1:9090
#socket = 192.168.25.109:4599
disable-logging = true
buffer-size =32768
harakiri = 20
chmod-socket = 664
daemonize=/home/www/log/uwsgi.log
vcauum = true

Postgresql配置连接池

  • 如果不为 Postgresl 配置连接池,程序会在高并发的情况下把数据库的连接全部用完
  • 参考链接pgbouncer
  • 下面设置很简单.更多高级功能后面挖掘.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# grep -v "^;" /etc/pgbouncer/pgbouncer.ini  | grep -v "^$"
[databases]
## 下面这个连接信息必需与postgresql里设一样.就是说,postgresql 要有test_db数库名,test用户,它的密码是test123,....
test_db = host=127.0.0.1 port=5432 user=test password=test123 client_encoding=UNICODE datestyle=ISO connect_query='SELECT 1'

[pgbouncer]
pidfile = /var/run/postgresql/pgbouncer.pid
## 绑定所有地址
listen_addr = *
listen_port = 6432
## 通过unix socket与postgresql 通信
unix_socket_dir = /var/run/postgresql
auth_type = md5

## 认证用户的信息,每行格式: "test" "test123"
auth_file = /etc/pgbouncer/userlist.txt
admin_users = admin
pool_mode = transaction
server_reset_query = DISCARD ALL

## 我测试到的一个比较理想的数值,过大过小对程序的并发都有影响.
max_client_conn = 100
default_pool_size = 20
listen_backlog = 1024

总结

  • 在网上看到好多人声称uWSGINginx会达到很高的性能,我用这种配置组合,压测的数据很难看.TPS 1K/sec.安装了连接池之后,虽然没有做读写分享,但是它的提升还是很大的.

谢谢支持