阿里云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 就可以打开登录页面了

参考文章:
Docker部署 – Django+MySQL+uWSGI+Nginx

打赏 赞(1)

为您推荐

发表评论

电子邮件地址不会被公开。 必填项已用*标注

1条评论