迁移了一个Django项目的Docker容器,发现运行存在问题,所以重建了virtualenv
环境。多个Django项目中,其中有一个Django运行出现异常:
[20/Mar/2018 12:38:41] code 400, message Bad request syntax ('\x00\xf9\x02\x00\x0c\x00QUERY_STRING\x00\x00\x0e\x00REQUEST_METHOD\x03\x00GET
...
Traceback (most recent call last):
File "/usr/local/lib/python2.7/logging/__init__.py", line 861, in emit
msg = self.format(record)
File "/usr/local/lib/python2.7/logging/__init__.py", line 734, in format
return fmt.format(record)
File "/data/myapp_admin/venv2/lib/python2.7/site-packages/django/utils/log.py", line 192, in format
return super(ServerFormatter, self).format(record)
File "/usr/local/lib/python2.7/logging/__init__.py", line 476, in format
raise e
UnicodeDecodeError: 'ascii' codec can't decode byte 0xf9 in position 9: ordinal not in range(128)
这个问题看上去像是客户端请求中包含了Unicode字符,但是服务器端恢复virtualenv
环境没有支持?
google了一些文档,发现很多都指向了http请求中出现https,原因和nginx反向uwsgi_pass
给Django有关,似乎是客户端试图以https
访问http
导致的问题。 - 参考 strange UnicodeDecodeError on django
检查了nginx配置:
upstream django_perfree {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name myapp.example.com;
charset utf-8;
client_max_body_size 75M;
location /media {
alias /home/admin/perfree/media;
}
location /static {
alias /home/admin/perfree/static;
}
然而,在检查访问链路的时候,发现了一些和线上不同的区别:
- 虽然nginx配置和线上相似,但是线上环境是直接访问 nginx 反向代理到后端
uwsgi
运行的8080
端口 - 线下开发环境是自己搭建的Docker环境,但是为了模拟复杂的网络环境,并且能够运行不同的测试环境,我采用了Docker来运行测试机,而且将Docker所在的主机放置在一台运行了HAProxy的主机(
gw-1
)之后再连接到局域网:- 模拟前端有防火墙的Internet服务,在
gw-1
上启用了Ubuntu UFW(使用ufw配置NAT masquerade) gw-1
上使用HAProxy反向代理到svr-1
上运行的Docker容器映射端口
- 模拟前端有防火墙的Internet服务,在
listen mydjango
bind 192.168.44.11:80
mode tcp
balance roundrobin
server mydjango-1 192.168.0.2:80 check
Haproxy Charset 似乎说明HAProxy实际上默认使用UTF-8作为文本存储和渲染。
暂时停止gw-1
上HAProxy,然后暂时使用ufw端口映射来替代这个反向代理。编辑/etc/ufw/before.rules
添加到
# nat Table rules
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
...
-A PREROUTING -i wlp3s0 -p tcp --dport 80 -j DNAT --to-destination 192.168.0.2
但是发现使用ufw直接转发HTTP访问(没有使用HAProxy)依然出现同样的报错信息。看来,不是HAProxy代理的原因。
由于不经过nginx反向代理,直接访问Django 8080端口是可以正常工作,所以修改docker,重建端口映射,直接将Docker 8001端口映射给Django 8001端口来运行应用。
docker commit myapp local:myapp
docker stop myapp
docker rm myapp
docker run -it -p 221:22 -p 801:80 -p 4431:443 -p 8081:8080 -p 33061:3306 --hostname myapp \
--name myapp -v share-data:/data local:myapp /bin/bash
然后在防火墙gw-1
上修改映射
-A PREROUTING -i wlp3s0 -p tcp --dport 8081 -j DNAT --to-destination 192.168.0.2
暂时绕过nginx反向代理到Django端口运行到问题,采用直接访问Django 8080端口来测试。