• 注册
  • Docker 关注:1 内容:3

    阿里云ECS Centos7系统使用Docker部署Django2.0 + mysql + nginx + uwsgi + supervisor

  • 查看作者
  • 打赏作者
  • 拉黑名单
    • 之前在腾讯云上手动部署过,总的来说还是挺麻烦的,并且排错比较难,也很难进行环境迁移,于是想到了使用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

      你需要登录,才能进行发帖操作
    • 帖子间隔 侧栏位置: