*前言:
本例中我们将使用docker-compose编排并启动4个容器,这个更接近于实际生成环境下的部署。
本例中我们将使用docker-compose编排并启动4个容器,这个更接近于实际生成环境下的部署。
Django + Uwsgi容器 :核心应用程序,处理动态请求
MySQL 容器 :数据库服务
Redis 容器 :缓存服务
Nginx容器 :反向代理服务并处理静态资源请求
1 2 3 4 5 6 这四个容器的依赖关系是: Django+Uwsgi 容器依赖 Redis 容器和 MySQL 容器,Nginx 容器依赖Django+Uwsgi容器。 为了方便容器间的相互访问和通信,我们使用docker-compose时可以给每个容器取个别名,这样访问容器时就可以直接使用别名访问,而不使用Docker临时给容器分配的IP了。 在构建mysql和Redis镜像容器是使用的是官网镜像,非dockerfile构建!
这四个容器的别名及通信端口如下图所示:
一、Docker-compose部署Django项目布局树形图 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 myproject_docker ├── compose │ ├── mysql │ │ ├── conf │ │ │ └── my.cnf │ │ └── init │ │ └── init.sql │ ├── nginx │ │ ├── Dockerfile │ │ ├── log │ │ ├── nginx.conf │ │ └── ssl │ ├── redis │ │ └── redis.conf │ └── uwsgi ├── docker-compose.yml └── myproject ├── Dockerfile ├── apps ├── manage.py ├── myproject │ ├── asgi.py │ ├── __init__.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── pip.conf ├── requirements.txt ├── .env ├── start.sh ├── media ├── static └── uwsgi.ini
二、容器编排文件docker-compose.yaml
docker-compose.yml的核心内容如下。定义了4个数据卷,用于挂载各个容器内动态生成的数据,比如MySQL的存储数据,redis生成的快照、django+uwsgi容器中收集的静态文件以及用户上传的媒体资源。这样即使删除容器,容器内产生的数据也不会丢失。
我们还定义了3个网络,分别为nginx_network
(用于nginx和web容器间的通信),db_network
(用于db和web容器间的通信)和redis_network
(用于redis和web容器间的通信)。
整个编排里包含4项容器服务,别名分别为redis
, db
, nginx
和web
,接下来我们将依次看看各个容器的Dockerfile和配置文件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 version: "3" volumes: db_vol: redis_vol: media_vol: static_vol: networks: nginx_network: driver: bridge db_network: driver: bridge redis_network: driver: bridge services: redis: image: redis:latest command: redis-server /etc/redis/redis.conf networks: - redis_network volumes: - redis_vol:/data - ./compose/redis/redis.conf:/etc/redis/redis.conf ports: - "6379:6379" restart: always db: image: mysql:8.0.21 env_file: - ./myproject/.env networks: - db_network volumes: - db_vol:/var/lib/mysql:rw - ./compose/mysql/conf/my.cnf:/etc/mysql/my.cnf - ./compose/mysql/init:/docker-entrypoint-initdb.d/ ports: - "3306:3306" restart: always web: build: ./myproject expose: - "8000" volumes: - ./myproject:/var/www/html/myproject - static_vol:/var/www/html/myproject/static - media_vol:/var/www/html/myproject/media - ./compose/uwsgi:/tmp networks: - nginx_network - db_network - redis_network depends_on: - db - redis restart: always tty: true stdin_open: true nginx: build: ./compose/nginx ports: - "80:80" - "443:443" expose: - "80" volumes: - ./compose/nginx/nginx.conf:/etc/nginx/conf.d/nginx.conf - ./compose/nginx/ssl:/usr/share/nginx/ssl - ./compose/nginx/log:/var/log/nginx - static_vol:/usr/share/nginx/html/static - media_vol:/usr/share/nginx/html/media networks: - nginx_network depends_on: - web restart: always
三、相关依赖文件 1、构建WEB镜像(DJango+Uwsgi) dockerfile 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 FROM python:3.9 RUN apt-get update && apt install -y netcat MAINTAINER DJGENV PYTHONDONTWRITEBYTECODE 1 ENV PYTHONUNBUFFERED 1 COPY pip.conf /root/.pip/pip.conf ENV APP_HOME=/var/www/html/myprojectRUN mkdir -p $APP_HOME WORKDIR $APP_HOME ADD . $APP_HOME RUN /usr/local/bin/python -m pip install --upgrade pip RUN pip install -r requirements.txt RUN sed -i 's/\r//' ./start.sh RUN chmod +x ./start.sh ENTRYPOINT /bin/bash ./start.sh
requirements.txt
可通过命令生成:pip freeze > requirements.txt
start.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #!/bin/bash while ! nc -z db 3306 ; do echo "Waiting for the MySQL Server" sleep 3 done python manage.py collectstatic --noinput&& python manage.py makemigrations&& python manage.py migrate&& uwsgi --ini /var/www/html/myproject/uwsgi.ini&& tail -f /dev/nullexec "$@ "
uwsgi.ini 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 [uwsgi] project=myproject uid=www-data gid=www-data base=/var/www/html chdir=%(base)/%(project) module=%(project).wsgi:application master=True processes=2 socket=0.0.0.0:8000 chown-socket=%(uid):www-data chmod-socket=664 vacuum=True max-requests=5000 pidfile=/tmp/%(project)-master.pid daemonize=/tmp/%(project)-uwsgi.log # 设置一个请求的超时时间(秒),如果一个请求超过了这个时间,则请求被丢弃 harakiri = 60 post buffering = 8192 buffer-size= 65535 # 当一个请求被harakiri杀掉会,会输出一条日志 harakiri-verbose = true # 开启内存使用情况报告 memory-report = true # 设置平滑的重启(直到处理完接收到的请求)的长等待时间(秒) reload-mercy = 10 # 设置工作进程使用虚拟内存超过N MB就回收重启 reload-on-as= 1024
pip.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 [global] index-url = https://mirrors.aliyun.com/pypi/simple/ [install] trusted-host = mirrors.aliyun.com EOF https://pypi.douban.com/simple/ 豆瓣 https://mirrors.aliyun.com/pypi/simple/ 阿里 https://pypi.hustunique.com/simple/ 华中理工大学 https://pypi.sdutlinux.org/simple/ 山东理工大学 https://pypi.mirrors.ustc.edu.cn/simple/ 中国科学技术大学 https://pypi.tuna.tsinghua.edu.cn/simple/ 清华 RUN pip config set global.index-url http://mirrors.aliyun.com/pypi/simple/
2、构建nginx镜像 dockerfile 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 FROM nginx:latestRUN rm /etc/nginx/conf.d/default.conf \ && mkdir -p /usr/share/nginx/html/static \ && mkdir -p /usr/share/nginx/html/media \ && mkdir -p /usr/share/nginx/ssl RUN chown -R www-data:www-data /usr/share/nginx/html/media \ && chmod -R 775 /usr/share/nginx/html/media ADD ./nginx.conf /etc/nginx/conf.d/ CMD ["nginx" , "-g" , "daemon off;" ]
nginx.conf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 upstream django { ip_hash; server web:8000 ; } server { listen 80 ; server_name 127.0.0.1 ; charset utf-8 ; client_max_body_size 10M ; access_log /var/log/nginx/access.log main; error_log /var/log/nginx/error .log warn ; location /static { alias /usr/share/nginx/html/static; } location /media { alias /usr/share/nginx/html/media; } location / { include /etc/nginx/uwsgi_params; uwsgi_pass django; uwsgi_read_timeout 600 ; uwsgi_connect_timeout 600 ; uwsgi_send_timeout 600 ; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for ; proxy_redirect off ; proxy_set_header X-Real-IP $remote_addr ; } }
3、构建mysql镜像 my.cnf 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 [mysqld] user=mysql default-storage-engine=INNODB character-set-server=utf8 secure-file-priv=NULL default-authentication-plugin=mysql_native_password port = 3306 basedir = /usr datadir = /var/lib/mysql tmpdir = /tmp pid-file = /var/run/mysqld/mysqld.pid socket = /var/run/mysqld/mysqld.sock skip-name-resolve [client] port = 3306 default-character-set=utf8 [mysql] no -auto-rehashdefault-character-set=utf8
init.sql
设置MySQL服务启动时需要执行的脚本命令, 注意这里的用户名和password必需和myproject目录下.env
文件中的环境变量保持一致。
1 2 3 4 Alter user 'dbuser' @'%' IDENTIFIED WITH mysql_native_password BY 'password' ; GRANT ALL PRIVILEGES ON myproject.* TO 'dbuser' @'%' ; FLUSH PRIVILEGES;
.env 1 2 3 4 MYSQL_ROOT_PASSWORD=123456 MYSQL_USER=dbuser MYSQL_DATABASE=myproject MYSQL_PASSWORD=password
4、构建Redis镜像 redis.conf 1 2 3 4 5 6 7 8 9 bind 127.0.0.1requirepass yourpassword
四、修改Django配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 Debug = False ALLOWED_HOSTS = ['your_server_IP' , 'your_domain_name' ] STATIC_ROOT = os.path.join(BASE_DIR, 'static' ) STATIC_URL = "/static/" MEDIA_ROOT = os.path.join(BASE_DIR, 'media' ) MEDIA_URL = "/media/" DATABASES = { 'default' : { 'ENGINE' : 'django.db.backends.mysql' , 'NAME' : 'myproject' , 'USER' :'dbuser' , 'PASSWORD' :'password' , 'HOST' : 'db' , 'PORT' :'3306' , } } CACHES = { "default" : { "BACKEND" : "django_redis.cache.RedisCache" , "LOCATION" : "redis://redis:6379/1" , "OPTIONS" : { "CLIENT_CLASS" : "django_redis.client.DefaultClient" , "PASSWORD" : "yourpassword" , }, } }
五、启动容器组 1 2 3 4 5 6 sudo docker-compose buildsudo docker imagessudo docker-compose up
六、排错 1 2 free -mh # 查看资源使用情况 netstat -tunlp # 查看端口使用情况
[参考大江狗的博客](Docker部署Django | 大江狗的博客 (pythondjango.cn) )