文章目录
原文地址:
阿里云ECS Centos7使用Docker部署Django2.0 + mysql + nginx + uwsgi + supervisor,需要python2和python3共存
之前在腾讯云上手动部署过,总的来说还是挺麻烦的,并且排错比较难,也很难进行环境迁移,于是想到了使用docker容器化部署
腾讯云手动部署的文章:
腾讯云Centos7+Uwsgi+Nginx+pyenv+virtualenv +supervisor 的生产环境进行Django2.0部署
以下安装过程是在,阿里云ECS 1核1G Centos7.4中进行的
一、首先创建docker容器的目录
在/root目录下
tree -L 3 -h /root
目录说明
1、wwwroot存放的是connorflow项目,app也是connorflow
具体的Django项目这里就不做过多介绍了
2、docker目录存放了两个docker容器的配置
1)、djang-uwsgi-nginx目录存放的是
Uwsgi+Nginx+supervisor的配置
2)、mysql目录存放的是mysql容器相关的配置
查看docker镜像:
docker images
二、创建MySQL的docker容器
目录:
[root@iZbp16yqwdlflk6ysilycbZ docker]# tree mysql/
mysql/
├── conf.d
│ └── my.cnf
├── data
└── start.sh
1、start.sh
#!/bin/bash
echo "create a mysql container.."
docker run -d --name mysql \
-v $(pwd)/conf.d:/etc/mysql/conf.d \
-v $(pwd)/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD="xxxxxxxx" \
-e MYSQL_DATABASE="connforflow" \
-p 3307:3306 \
mysql:5.7.19 \
--character-set-server=utf8 --collation-server=utf8_general_ci
其中MYSQL_ROOT_PASSWORD=“my-secret-password”,指的是用户root的密码
简单说明:docker run 为运行容器的命令,若本地仓库不存在mysql:5.7.19的镜像则自动从DockerHub pull下来。
参数: -d, --detach Run container in background and print container ID 分离在后台运行容器并打印容器ID
-v, --volume list Bind mount a volume 卷列表绑定卷
-e, --env list Set environment variables env list设置环境变量
-p, --publish list Publish a container’s port(s) to the host发布列表将容器的端口发布到主机
2、my.cnf
[client]
default-character-set=utf8
[mysqld]
character-set-server=utf8
performance_schema = OFF
[mysql]
no-auto-rehash
default-character-set=utf8
然后运行start.sh
sh /root/docker/mysql/start.sh
可以看到容器已成功运行
docker ps -a
现在看看mysql容器是否正确运行
docker exec -it mysql bash
使用mysql -uroot -p
进行登录
docker run创建时,写入的环境变量MYSQL_DATABASE会由mysql镜像处理,创建database
查看创建的mysql镜像
三、创建Django+uWSGI+Nginx+Supervisor镜像并启动容器,由于supervisor在python3中无法使用,所以需要python2和python3共存
由于该容器需要与MySQL容器互联,Docker通过两种方式为容器公开连接信息:
- 更新环境变量
- 更新/etc/hosts文件
对于第一种方式:互联之后会在该容器生成mysql地址、端口、密码等信息作为环境变量供其使用,这些信息的格式是固定的。
因此需要在Django项目中读取这些环境变量。
创建镜像之前先修改一下Django项目中settings.py
:
/root/wwwroot/connorflow/connorflow/settings.py
这里之所以这么做,是因为服务器存在被攻破的可能性,如果所有的配置都在配置文件中,那么数据库将直接暴露,所以通常情况下,会讲配置文件,直接设置到环境变量中
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': os.environ.get('MYSQL_DATABASE_NAME'),
'USER': 'root',
'PASSWORD': os.environ.get('MYSQL_ENV_MYSQL_ROOT_PASSWORD'),
'HOST': os.environ.get('MYSQL_PORT_3306_TCP_ADDR'),
'OPTIONS': {
"init_command": "SET foreign_key_checks=0;",
}
}
}
说明:MYSQL_ENV_MYSQL_ROOT_PASSWORD(-e MYSQL_ROOT_PASSWORD=“xxxxxxxx”)、MYSQL_PORT_3306_TCP_ADDR 这两个环境变量就是Docker提供的连接信息,例如:
docker run -d --link mysql_container:mysql webapp
–link name:alias,其中name是要连接的容器名称,alias是这个连接的别名。
变量名中的’MYSQL’就是alias,这一点需要注意。
目录:
[root@iZbp16yqwdlflk6ysilycbZ docker]# tree django-uwsgi-nginx/
django-uwsgi-nginx/
├── Dockerfile
├── nginx-app.conf
├── requirements.txt
├── run
├── supervisor-app.ini
├── uwsgi.ini
└── uwsgi_params
其中run是一个目录,只是用来存放日志类的东西,方便查看,另外网上很多操作使用的是supervisor-app.conf,原本我也是使用的这种配置,但是supervisor的一个默认include使用的是supervisord.d/*.ini,所以这里我改成了ini的配置文件
1、下面开始编写Dockerfile:
网上找了很久,不知道什么原因,基本上都是Ubuntu的吸用,centos7的配置文件跟Ubuntu还是有很大区别的
##############################################
# 基于centos7构建python3运行环境
# 进入容器:docker exec -it connorflow /bin/bash
##############################################
FROM centos:centos7
MAINTAINER connorflow
RUN yum -y update; yum clean all
# 没有这一步将安装nginx失败
RUN yum -y install epel-release tar ; yum clean all
RUN yum -y install nginx ; yum clean all
RUN set -ex \
# 预安装所需组件
&& yum install -y wget tar libmysqlclient-dev supervisor libffi-devel zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gcc make initscripts \
&& wget https://www.python.org/ftp/python/3.6.4/Python-3.6.4.tgz \
&& tar -zxvf Python-3.6.4.tgz \
&& cd Python-3.6.4 \
&& ./configure prefix=/usr/local/python3 \
&& make \
&& make install \
&& make clean \
&& rm -rf /Python-3.6.4* \
&& yum install -y epel-release \
&& yum install -y python-pip
# 设置默认为python3
RUN set -ex \
# 备份旧版本python
#&& mv /usr/bin/python /usr/bin/python27 \
#&& mv /usr/bin/pip /usr/bin/pip-python2.7 \
# 配置默认为python3
&& ln -s /usr/local/python3/bin/python3.6 /usr/bin/python3 \
&& ln -s /usr/local/python3/bin/pip3 /usr/bin/pip3
# 修复因修改python版本导致yum失效问题
#RUN set -ex \
# && sed -i "s#/usr/bin/python#/usr/bin/python2.7#" /usr/bin/yum \
# && sed -i "s#/usr/bin/python#/usr/bin/python2.7#" /usr/libexec/urlgrabber-ext-down \
# && yum install -y deltarpm
# 基础环境配置
RUN set -ex \
# 修改系统时区为东八区
&& rm -rf /etc/localtime \
&& ln -s /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& yum install -y vim \
# 安装定时任务组件
&& yum -y install cronie
# 支持中文
RUN yum install kde-l10n-Chinese -y
RUN localedef -c -f UTF-8 -i zh_CN zh_CN.utf8
# 更新pip版本
RUN pip3 install --upgrade pip
ENV LC_ALL zh_CN.UTF-8
# uwsgi
RUN pip3 install -i https://pypi.doubanio.com/simple/ uwsgi
RUN set -ex \
&& ln -s /usr/local/python3/bin/uwsgi /usr/bin/uwsgi
# 环境变量
ENV MYSQL_DATABASE_NAME connorflow
ENV EMAIL_HOST_USER a37free@163.com
ENV EMAIL_HOST_PASSWORD xxxxxxxx
# nginx、supervisor配置
COPY supervisor-app.ini /etc/supervisord.d/
# 安装项目所需python第三方库
COPY requirements.txt /home/docker/code/connorflow/
RUN pip3 install -i https://pypi.doubanio.com/simple/ \
-r /home/docker/code/connorflow/requirements.txt
# uwsgi.ini 及 uwsgi_params
COPY . /home/docker/code/
EXPOSE 80
CMD ["supervisord", "-n"]
pip安装使用了国内的镜像源,提高下载速度 https://pypi.doubanio.com/simple/
环境变量是提供给Django项目,例如邮箱配置,数据库名
因为项目使用的的是python3版本,而supervisor只能在python2下运行,所以使用的centos7的默认环境是python2.7,所以我们需要在环境中设置python2和python3同时存在
2、 配置supervisor-app.ini
这里之所以先说supervisor,是为了说明后续的nginx和uwsig的配置
[program:app-uwsgi]
command = /usr/bin/uwsgi --ini /home/docker/code/uwsgi.ini
[program:nginx-app]
command = /usr/sbin/nginx -c /home/docker/code/nginx-app.conf
这里可以看出,nginx和uwsgi全部都是使用的自定义配置
3、Nginx配置: nginx-app.conf
# For more information on configuration, see:
# * Official English Documentation: http://nginx.org/en/docs/
# * Official Russian Documentation: http://nginx.org/ru/docs/
user root;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Load modular configuration files from the /etc/nginx/conf.d directory.
# See http://nginx.org/en/docs/ngx_core_module.html#include
# for more information.
include /etc/nginx/conf.d/*.conf;
upstream django {
server unix:/home/docker/code/app.sock; # for a file socket
# server 127.0.0.1:8000; # for a web port socket (we'll use this first)
}
server {
listen 80;
listen [::]:80 default_server;
server_name _;
#root /usr/share/nginx/html;
# Load configuration files for the default server block.
include /etc/nginx/default.d/*.conf;
location / {
root /root/www/vue/dist;
index index.html;
try_files $uri $uri/ /index.html;
}
# 后端接口转发
location /api {
uwsgi_pass django;
uwsgi_connect_timeout 3000;
uwsgi_send_timeout 3000;
uwsgi_read_timeout 3000;
uwsgi_param Host $host;
uwsgi_param X-Real-IP $remote_addr;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto;
include /home/docker/code/uwsgi_params;
}
location ^~ /admin/ {
uwsgi_pass django;
include /home/docker/code/uwsgi_params;
}
location /static {
alias /home/docker/code/connorflow/static;
}
# location /media {
# uwsgi_pass django;
# alias /home/docker/code/connorflow/media;
# }
# 由于我们在后端设置了
# path('ueditor/', include(DjangoUeditor_urls)),
# re_path('^media/(?P<path>.*)$', serve, {'document_root': MEDIA_ROOT}),
# 所以,media和ueditor不能直接给定固定地址
location /ueditor {
uwsgi_pass django;
uwsgi_connect_timeout 3000;
uwsgi_send_timeout 3000;
uwsgi_read_timeout 3000;
uwsgi_param Host $host;
uwsgi_param X-Real-IP $remote_addr;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto;
include /home/docker/code/uwsgi_params;
}
location /media {
uwsgi_pass django;
uwsgi_connect_timeout 3000;
uwsgi_send_timeout 3000;
uwsgi_read_timeout 3000;
uwsgi_param Host $host;
uwsgi_param X-Real-IP $remote_addr;
uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for;
uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto;
include /home/docker/code/uwsgi_params;
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
# Settings for a TLS enabled server.
#
# server {
# listen 443 ssl http2 default_server;
# listen [::]:443 ssl http2 default_server;
# server_name _;
# root /usr/share/nginx/html;
#
# ssl_certificate "/etc/pki/nginx/server.crt";
# ssl_certificate_key "/etc/pki/nginx/private/server.key";
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 10m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
#
# # Load configuration files for the default server block.
# include /etc/nginx/default.d/*.conf;
#
# location / {
# }
#
# error_page 404 /404.html;
# location = /40x.html {
# }
#
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# }
# }
}
daemon off;
第一件事:注意、注意、注意,daemon off;必须存在,因为在docker中supervisor和nginx都不能后台运行,这是我在操作过程中遇到的一个非常大的坑,没有daemon off;时,使用nginx启动的时候没有任何问题,但是使用supervisor启动的时候,nginx就一直失败,启动不了
4、uwsgi.ini和uwsgi_params
1) uwsgi.ini
[uwsgi]
ini = :base
socket = %dapp.sock
master = true
processes = 4
logto = /home/docker/code/run/uwsgi8000.log
enable-threads = true
[dev]
ini = :base
socket = :8001
[local]
ini = :base
http = :8000
[base]
chdir = %dconnorflow/
module=connorflow.wsgi:application
chmod-socket=666
2) uwsgi_params,该文件是在nginx的安装目录下,所以需要先安装nginx,通常情况下内容是固定的
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
5、通过docker build命令来创建镜像
docker build -t connorflow /root/docker/django-uwsgi-nginx
由于安装了python3.6,体积比较大,安装速度比较慢,尤其在安装nginx时,需要耐心等待;
创建完成:
docker images来查看一下本地的镜像:
6、根据创建好的镜像启动一个容器,挂载app目录,链接mysql容器
docker run -itd \
--link mysql:mysql \
-v /root/wwwroot/connorflow/:/home/docker/code/connorflow \
--name webapp-connorflow \
-p 80:80 \
connorflow \
bash
-v, 是挂载一个宿主机目录connorflow,作为数据卷(也可以在Dockerfile中,声明VOLUME来配置)
通过–link参数即可连接mysql容器,如之前所述
启动后我们进入容器检查一下
docker exec -it webapp-connorflow bash
下发命令env查看环境变量
可以看到,存在连接mysql容器后提供的部分环境变量,也有通过Dockerfile写入镜像的环境变量
7、接下来需要做的是Django db migrate,以及静态文件collectstatic
migrate
python3 /home/docker/code/connorflow/manage.py migrate
collectstatic,Django的静态文件
python3 /home/docker/code/connorflow/manage.py collectstatic
django创建超级用户
python3 /home/docker/code/connorflow/manage.py createsuperuser
8、紧接着就可以通过supervisor启动项目了
supervisord -n
supervisord -n & # 注意,这里不要使用nohup,在docker中supervisor不支持
访问http://ip/admin 就可以打开登录页面了