Make it Possible

使用docker部署以uwsgi方式运行的django

Django是个被广泛使用的web框架,如何给这个框架进行docker化是个比较复杂的问题
我们先来确定一下我们的项目框架,进行过django-admin startproject ${PROJECT}之后的django项目文件结构应该是这样的:

.
├── ${APP_1}
│   ├── admin.py
│   ├── apps.py
│   ├── decorators.py
│   ├── __init__.py
│   ├── migrations
│   ├── models.py
│   ├── permissions.py
│   ├── serializer.py
│   ├── serializers.py
│   ├── static     
│   ├── templates
│   ├── tests.py
│   ├── urls.py
│   └── views.py
├── ${PROJECT}
│   ├── __init__.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
├── deploy
│   ├── entrypoint.sh
│   ├── nginx-app.conf
│   ├── supervisor-app.conf
│   ├── uwsgi.ini
│   └── uwsgi_params
├── Dockerfile
├── manage.py
├── README.md
└── requirements.txt

其中包含一个与项目名称同名的文件夹(包),以及若干个app。
我们再新建deploy文件夹,其中包含各种需要的配置文件。

Dockerfile

FROM python:3
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY . /usr/src/app

# Use sed because of potential file owner issue
RUN sed -i 's/deb.debian.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apt/sources.list && \
    sed -i 's|security.debian.org/debian-security|mirrors.tuna.tsinghua.edu.cn/debian-security|g' /etc/apt/sources.list && \
    sed -i 's|security.debian.org|mirrors.tuna.tsinghua.edu.cn/debian-security|g' /etc/apt/sources.list && \
    apt-get update && \
    apt-get install -y nginx supervisor && \
    rm -rf /var/lib/apt/lists/* && \
    pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple && \
    pip install -r requirements.txt --no-cache-dir && \
    rm -rf /var/lib/apt/lists/* && \
    echo "daemon off;" >> /etc/nginx/nginx.conf && \
    python manage.py collectstatic --noinput
ENV DJANGO_PRODUCTION=1
COPY deploy/nginx-app.conf /etc/nginx/sites-available/default
COPY deploy/supervisor-app.conf /etc/supervisor/conf.d/
EXPOSE 80
ENTRYPOINT [ "/bin/bash", "deploy/entrypoint.sh" ]
CMD ["supervisord", "-n"]

先换源,安装nginx和supervisor,对pip换源,安装requirements.txt中的所有以来,复制配置文件。

Django设置

Django的SECRET_KEY和数据库设置是个比较麻烦的点,但是django的配置文件是动态的,所以我们可以做如下设置

SECRET_KEY = os.environ.get('SECRET_KEY') or 'xxxxxxxxxxxxx'
if os.environ.get('DJANGO_PRODUCTION'):
    DEBUG = False
else:
    DEBUG = True

通过环境变量来确定当前是生产环境还是开发环境,利用同样的原理,我们可以这样配置数据库:

if DEBUG:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
else:
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.mysql',
            'NAME': os.environ.get('MYSQL_DATABASE_NAME') or os.environ.get('DJANGO_DATABASE_NAME') or 'clinic',
            'USER': 'root',
            'PASSWORD': os.environ.get('MYSQL_ENV_MYSQL_ROOT_PASSWORD') or os.environ.get('DJANGO_DATABASE_PASSWORD'),
            'HOST': os.environ.get('MYSQL_PORT_3306_TCP_ADDR') or os.environ.get('DJANGO_DATABASE_HOST'),
        }
    }

值得注意的是,如果容器与数据库容器进行过link操作,那么在容器中就会产生${连接时指定的hostname}_DATABASE_NAME这样的环境变量,我们只要连接数据库时指定--link some-db:mysql就可以使用这些变量自动设置数据库。
并且要配置STATIC_ROOT:

STATIC_ROOT = '/usr/share/nginx/html/static/'

为了Dockerfile中的collectstatic,将所有的静态文件都集中到一个文件夹中,在这里我们集中到了nginx的默认文件目录。

配置nginx

首先要在nginx主配置文件中写入deamon off,并且把相关配置写入/etc/nginx/sites-available/default中,相关配置如下

upstream django {
    server unix:/usr/src/app/deploy/app.sock; # for a file socket
    # server 127.0.0.1:8001; # for a web port socket (we'll use this first)
}

# configuration of the server
server {
    # the port your site will be served on, default_server indicates that this server block
    # is the block to use if no blocks match the server_name
    listen      80 default_server;

    # the domain name it will serve for
    server_name .example.com; # substitute your machine's IP address or FQDN
    charset     utf-8;

    # max upload size
    client_max_body_size 75M;   # adjust to taste

    location /static {
        alias /usr/share/nginx/html/static; # your Django project's static files - amend as required
    }

    # Finally, send all non-media requests to the Django server.
    location / {
        uwsgi_pass  django;
        include     /usr/src/app/deploy/uwsgi_params; # the uwsgi_params file you installed
    }
}

server_name可以自己改一改。
并且还要引入一个配置文件uwsgi_params,内容如下:

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  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;

uWSIG配置

还需要配置一个uwsgi.ini内容如下

[uwsgi]
# this config will be loaded if nothing specific is specified
# load base config from below
ini = :base

# %d is the dir this configuration file is in
socket = /usr/src/app/deploy/app.sock
master = true
processes = 4

[dev]
ini = :base
# socket (uwsgi) is not the same as http, nor http-socket
socket = :8001

[local]
ini = :base
http = :8000
# set the virtual env to use

[base]
# chdir to the folder of this config file, plus app/website
chdir = /usr/src/app
# load the module from wsgi.py, it is a python path from 
# the directory above.
module=${PROJECT}:application
# allow anyone to connect to the socket. This is very permissive
chmod-socket=666

注意将${PROJECT}改成项目名称(就是包含wsgi.py的包)

supervisord配置

supervisor保证nginx和uwsgi一起运行,supervisor-app.conf文件配置如下:

[supervisord]
nodaemon=true

[program:app-uwsgi]
command = /usr/local/bin/uwsgi --ini /usr/src/app/deploy/uwsgi.ini
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
redirect_stderr=true

[program:nginx-app]
command = /usr/sbin/nginx
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
redirect_stderr=true

将两个程序的日志全部重定向到stdout

完成migrate

migrate操作是在容器开始运行,连接数据库后需要进行的操作,该操作无法在docker build的时候完成,所以我们需要一个简单的entrypoint.sh

#!/bin/bash
set -e
python3 manage.py migrate
exec "$@"
点赞

发表评论

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

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据