├── .gitignore ├── Dockerfile ├── README.md ├── config └── nginx │ └── django_app.conf ├── db.sqlite3 ├── django_app ├── __init__.py ├── __pycache__ │ ├── __init__.cpython-36.pyc │ ├── __init__.cpython-37.pyc │ ├── settings.cpython-36.pyc │ ├── settings.cpython-37.pyc │ ├── urls.cpython-36.pyc │ ├── urls.cpython-37.pyc │ └── wsgi.cpython-37.pyc ├── settings.py ├── urls.py └── wsgi.py ├── docker-compose.yml ├── manage.py ├── md ├── 01.Docker简介.md ├── 02.Docker-Django本地部署.md ├── 03.Docker-Django-MySQL本地部署.md └── 04.Docker-Django-MySQL-Nginx-Gunicorn云端部署.md └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__/ 2 | *.py[cod] 3 | *$py.class 4 | mysql/ 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.7 2 | 3 | ENV PYTHONUNBUFFERED 1 4 | 5 | RUN echo \ 6 | deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free\ 7 | deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free\ 8 | deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free\ 9 | deb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free\ 10 | > /etc/apt/sources.list 11 | 12 | RUN apt-get update 13 | RUN apt-get install python3-dev default-libmysqlclient-dev -y 14 | 15 | RUN mkdir /code 16 | WORKDIR /code 17 | RUN pip install pip -U -i https://pypi.tuna.tsinghua.edu.cn/simple 18 | ADD requirements.txt /code/ 19 | RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 20 | ADD . /code/ 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![](https://img.shields.io/badge/docker-19.03.1-blue)](https://getbootstrap.com/docs/4.1/getting-started/introduction/) 2 | [![](https://img.shields.io/badge/python-3.7-orange.svg)](https://www.python.org/downloads/release/python-370/) 3 | [![](https://img.shields.io/badge/django-2.2-green.svg)](https://docs.djangoproject.com/en/2.1/releases/2.1/) 4 | [![](https://img.shields.io/badge/license-MIT-000000.svg)](https://opensource.org/licenses/MIT) 5 | 6 | **这是教你手把手部署 Docker + Django + Mysql + Ngnix + Gunicorn 的傻瓜式教程**,目的是让 Docker 菜鸟也能快速部署容器化的 Django 应用。 7 | 8 | 教程传送门: 9 | 10 | - [GitHub版](https://github.com/stacklens/django-docker-tutorial/tree/master/md) 11 | - [个人博客版](https://www.dusaiphoto.com/article/detail/73) 12 | 13 | > 注:两个版本是完全相同的。需要留言请前往个人博客。 14 | 15 | **教程导航:** 16 | 17 | **01** - [Docker 简介](https://github.com/stacklens/django-docker-tutorial/blob/master/md/01.Docker简介.md) 18 | 19 | **02** - [Docker-Django本地部署](https://github.com/stacklens/django-docker-tutorial/blob/master/md/02.Docker-Django本地部署.md) 20 | 21 | **03** - [Docker-Django-MySQL 本地部署](https://github.com/stacklens/django-docker-tutorial/blob/master/md/03.Docker-Django-MySQL本地部署.md) 22 | 23 | **04** - [Docker-Django-MySQL-Nginx-Gunicorn 云端部署](https://github.com/stacklens/django-docker-tutorial/blob/master/md/04.Docker-Django-MySQL-Nginx-Gunicorn云端部署.md) 24 | 25 | ![](https://www.dusaiphoto.com/media/image/image_source/20190928/reduce_docker_small.jpg) 26 | 27 | ## 教程特色 28 | 29 | - 零基础、免费、中文、完整项目代码 30 | - 基于最新的 Docker 19.03.1、Python 3.7、Django 2.1 31 | - 博主热情的技术支持 32 | 33 | ## 教程适宜人群 34 | 35 | - 完全没接触过 Docker,但却想迅速搭建容器化项目的 36 | - 接触过 Docker,但是却不清楚如何用 Docker 搭建 Django 项目的 37 | 38 | ## 代码使用方法 39 | 40 | 在 Linux/Mac 系统中预先安装好 Docker、Docker-compose。 41 | 42 | 下载示例代码: 43 | 44 | ```shel 45 | $ git clone https://github.com/stacklens/django-docker-tutorial.git 46 | ``` 47 | 48 | 进入代码根目录: 49 | 50 | ```shell 51 | $ cd django-docker-tutorial 52 | ``` 53 | 54 | 然后运行容器: 55 | 56 | ```shell 57 | $ docker-compose up 58 | ``` 59 | 60 | 基于 Django + Docker + MySQL + Nginx + Gunicorn 的容器化项目就运行起来了。 61 | 62 | 你可以用浏览器访问地址 `127.0.0.1:8000` ,即可看到 Django 首页(小火箭起飞页面)。 63 | 64 | **Enjoy!** 65 | 66 | > 第一次启动容器时有可能会失败,原因是 db 容器未完全初始化造成的。遇到这种情况请尝试重启容器,问题就消失了。 67 | 68 | ## 资源列表 69 | 70 | 如果你对如何将 Django 项目部署到云端完全不了解的,强烈建议先看看**传统部署流程**,建立大致印象: 71 | 72 | - [将 Django 项目部署到服务器](https://www.dusaiphoto.com/article/detail/71/) 73 | 74 | 如果你想重新学习 Django 开发的,可以先看博主的 Django 教程: 75 | 76 | - [Django 搭建个人博客教程](https://www.dusaiphoto.com/article/detail/2/) 77 | 78 | 此外,本文撰写时主要参考了以下资料: 79 | 80 | - [django-mysql-with-docker](http://www.nisanthsojan.com/django-mysql-with-docker -a-step-by-step-guide-for-local-development-part-1/) 81 | - [Docker Documention](https://docs.docker.com/) 82 | 83 | --- 84 | 85 | ![](http://blog.dusaiphoto.com/QR-h.jpg) -------------------------------------------------------------------------------- /config/nginx/django_app.conf: -------------------------------------------------------------------------------- 1 | upstream app { 2 | ip_hash; 3 | server app:8000; 4 | } 5 | 6 | server { 7 | listen 8000; 8 | server_name localhost; 9 | 10 | location /static/ { 11 | autoindex on; 12 | alias /code/collected_static/; 13 | } 14 | 15 | location / { 16 | proxy_pass http://app/; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacklens/django-docker-tutorial/47d818487476c1c8a750e87f3b1ef27a4e0ba323/db.sqlite3 -------------------------------------------------------------------------------- /django_app/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacklens/django-docker-tutorial/47d818487476c1c8a750e87f3b1ef27a4e0ba323/django_app/__init__.py -------------------------------------------------------------------------------- /django_app/__pycache__/__init__.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacklens/django-docker-tutorial/47d818487476c1c8a750e87f3b1ef27a4e0ba323/django_app/__pycache__/__init__.cpython-36.pyc -------------------------------------------------------------------------------- /django_app/__pycache__/__init__.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacklens/django-docker-tutorial/47d818487476c1c8a750e87f3b1ef27a4e0ba323/django_app/__pycache__/__init__.cpython-37.pyc -------------------------------------------------------------------------------- /django_app/__pycache__/settings.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacklens/django-docker-tutorial/47d818487476c1c8a750e87f3b1ef27a4e0ba323/django_app/__pycache__/settings.cpython-36.pyc -------------------------------------------------------------------------------- /django_app/__pycache__/settings.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacklens/django-docker-tutorial/47d818487476c1c8a750e87f3b1ef27a4e0ba323/django_app/__pycache__/settings.cpython-37.pyc -------------------------------------------------------------------------------- /django_app/__pycache__/urls.cpython-36.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacklens/django-docker-tutorial/47d818487476c1c8a750e87f3b1ef27a4e0ba323/django_app/__pycache__/urls.cpython-36.pyc -------------------------------------------------------------------------------- /django_app/__pycache__/urls.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacklens/django-docker-tutorial/47d818487476c1c8a750e87f3b1ef27a4e0ba323/django_app/__pycache__/urls.cpython-37.pyc -------------------------------------------------------------------------------- /django_app/__pycache__/wsgi.cpython-37.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/stacklens/django-docker-tutorial/47d818487476c1c8a750e87f3b1ef27a4e0ba323/django_app/__pycache__/wsgi.cpython-37.pyc -------------------------------------------------------------------------------- /django_app/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for django_app project. 3 | 4 | Generated by 'django-admin startproject' using Django 2.2. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/2.2/ref/settings/ 11 | """ 12 | 13 | import os 14 | 15 | # Build paths inside the project like this: os.path.join(BASE_DIR, ...) 16 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 17 | 18 | 19 | # Quick-start development settings - unsuitable for production 20 | # See https://docs.djangoproject.com/en/2.2/howto/deployment/checklist/ 21 | 22 | # SECURITY WARNING: keep the secret key used in production secret! 23 | SECRET_KEY = 'n+r_yfk4u61pxpr3lqy#bsu94s90nbey+c)mek_@fecq@3moe9' 24 | 25 | # SECURITY WARNING: don't run with debug turned on in production! 26 | DEBUG = True 27 | 28 | ALLOWED_HOSTS = ['*'] 29 | 30 | 31 | # Application definition 32 | 33 | INSTALLED_APPS = [ 34 | 'django.contrib.admin', 35 | 'django.contrib.auth', 36 | 'django.contrib.contenttypes', 37 | 'django.contrib.sessions', 38 | 'django.contrib.messages', 39 | 'django.contrib.staticfiles', 40 | ] 41 | 42 | MIDDLEWARE = [ 43 | 'django.middleware.security.SecurityMiddleware', 44 | 'django.contrib.sessions.middleware.SessionMiddleware', 45 | 'django.middleware.common.CommonMiddleware', 46 | 'django.middleware.csrf.CsrfViewMiddleware', 47 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 48 | 'django.contrib.messages.middleware.MessageMiddleware', 49 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 50 | ] 51 | 52 | ROOT_URLCONF = 'django_app.urls' 53 | 54 | TEMPLATES = [ 55 | { 56 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 57 | 'DIRS': [], 58 | 'APP_DIRS': True, 59 | 'OPTIONS': { 60 | 'context_processors': [ 61 | 'django.template.context_processors.debug', 62 | 'django.template.context_processors.request', 63 | 'django.contrib.auth.context_processors.auth', 64 | 'django.contrib.messages.context_processors.messages', 65 | ], 66 | }, 67 | }, 68 | ] 69 | 70 | WSGI_APPLICATION = 'django_app.wsgi.application' 71 | 72 | 73 | # Database 74 | # https://docs.djangoproject.com/en/2.2/ref/settings/#databases 75 | 76 | DATABASES = { 77 | 'default': { 78 | 'ENGINE': 'django.db.backends.mysql', 79 | 'NAME': 'django_app', 80 | 'USER': 'root', 81 | 'PASSWORD': 'mypassword', 82 | 'HOST': 'db', 83 | 'PORT': '3306', 84 | 'OPTIONS': {'charset': 'utf8mb4'}, 85 | } 86 | } 87 | 88 | 89 | # Password validation 90 | # https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators 91 | 92 | AUTH_PASSWORD_VALIDATORS = [ 93 | { 94 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 95 | }, 96 | { 97 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 98 | }, 99 | { 100 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 101 | }, 102 | { 103 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 104 | }, 105 | ] 106 | 107 | 108 | # Internationalization 109 | # https://docs.djangoproject.com/en/2.2/topics/i18n/ 110 | 111 | LANGUAGE_CODE = 'en-us' 112 | 113 | TIME_ZONE = 'UTC' 114 | 115 | USE_I18N = True 116 | 117 | USE_L10N = True 118 | 119 | USE_TZ = True 120 | 121 | 122 | # Static files (CSS, JavaScript, Images) 123 | # https://docs.djangoproject.com/en/2.2/howto/static-files/ 124 | 125 | STATIC_URL = '/static/' 126 | STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static') 127 | -------------------------------------------------------------------------------- /django_app/urls.py: -------------------------------------------------------------------------------- 1 | """django_app URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.contrib import admin 17 | from django.urls import path 18 | 19 | urlpatterns = [ 20 | path('admin/', admin.site.urls), 21 | ] 22 | -------------------------------------------------------------------------------- /django_app/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for django_app project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_app.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3" 2 | services: 3 | app: 4 | restart: always 5 | build: . 6 | command: bash -c "python3 manage.py collectstatic --no-input && python3 manage.py migrate && gunicorn --timeout=30 --workers=4 --bind :8000 django_app.wsgi:application" 7 | volumes: 8 | - .:/code 9 | - static-volume:/code/collected_static 10 | expose: 11 | - "8000" 12 | depends_on: 13 | - db 14 | networks: 15 | - web_network 16 | - db_network 17 | db: 18 | image: mysql:5.7 19 | volumes: 20 | - "./mysql:/var/lib/mysql" 21 | ports: 22 | - "3306:3306" 23 | restart: always 24 | environment: 25 | - MYSQL_ROOT_PASSWORD=mypassword 26 | - MYSQL_DATABASE=django_app 27 | networks: 28 | - db_network 29 | nginx: 30 | restart: always 31 | image: nginx:latest 32 | ports: 33 | - "8000:8000" 34 | volumes: 35 | - static-volume:/code/collected_static 36 | - ./config/nginx:/etc/nginx/conf.d 37 | depends_on: 38 | - app 39 | networks: 40 | - web_network 41 | 42 | networks: 43 | web_network: 44 | driver: bridge 45 | db_network: 46 | driver: bridge 47 | 48 | volumes: 49 | static-volume: 50 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'django_app.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /md/01.Docker简介.md: -------------------------------------------------------------------------------- 1 | **这是教你手把手部署 Docker + Django + MySQL + Nginx + Gunicorn 的傻瓜式教程**,目的是让 Docker 菜鸟也能快速部署容器化的 Django 应用。 2 | 3 | > 需要全面深入了解 Docker 的读者,请系统性地学习[官方文档](https://docs.docker.com/get-started/)。 4 | 5 | **教程共四章:** 6 | 7 | - 前言 8 | - Docker + Django 本地部署 9 | - Docker + Django + MySQL 本地部署 10 | - Docker + Django + MySQL + Nginx + Gunicorn 本地 + 云端部署 11 | 12 | 章节之间是继承关系,建议小白读者按顺序阅读,不要着急。 13 | 14 | ![](https://blog.dusaiphoto.com/dusainet-7000K/d01-1.jpg) 15 | 16 | ## 教程特色 17 | 18 | - 零基础、免费、中文、完整项目代码 19 | - 基于最新的 Docker 19.03.1、Python 3.7、Django 2.2 20 | - 博主热情的技术支持 21 | 22 | ## 教程适宜人群 23 | 24 | - 完全没接触过 Docker,但却想迅速搭建容器化项目的 25 | - 接触过 Docker,但是却不清楚如何用 Docker 搭建 Django 项目的 26 | 27 | ## 什么是Docker 28 | 29 | Docker 是一种基于 Linux 的**容器技术**,它可以将你的代码以及代码需要的环境打包到一起,从而组装为一个**标准、轻量级、安全**的隔离环境。 30 | 31 | 在容器技术之前,业界类似的明星产品是**虚拟机**:也就是在你的操作系统里面装一个软件,通过这个软件模拟出多个子系统出来。子系统之间是隔离的,互不影响。但虚拟机要模拟出整个系统,因此占用空间巨大,启动更是缓慢。 32 | 33 | 而 Docker 没有虚拟机的缺点。它只需要虚拟一个小规模的环境(小到甚至只有几MB),类似于“沙箱”。 34 | 35 | ## 为什么要学Docker 36 | 37 | 看本教程的读者,相信你已经看过博主的[Django 搭建个人博客教程](https://www.dusaiphoto.com/article/detail/71/)了,也在部署项目时挣扎痛苦过了。部署为什么难,是因为各云服务器的环境都有所不同,而这些微小的不同累积多了,就导致同样的部署流程,这台机器行,到另外一台机器就怎么都不行了。 38 | 39 | 但如果你有了 Docker 就不一样了,因为 Docker 可以把系统环境和代码一同打包进去,真正做到了**"一次开发,处处运行"**,不管你的机器有多复杂的环境,容器就像一个集装箱,把无关的东西统统隔离在外面。 40 | 41 | 有了 Docker,**更换服务器**也变得非常的简单,只需要把项目代码、数据、Docker 构建文件下载到新服务器上,几条指令就搞定了。啥配置 Nginx 、配置 Gunicorn 、配置 Mysql,统统都可以不管了,因为 Docker 构建文件里早就写好了。 42 | 43 | 有了 Docker,你只需要购买一台服务器,就可以在上面部署多个站点,并且不用担心它们互相影响,因为每个容器都是**隔离**起来的,非常的安全。 44 | 45 | 难怪容器技术近几年如此的火热了。不学你说的过去吗? 46 | 47 | ## 提问须知 48 | 49 | 博主会在力所能及的情况下帮助每一个人,但是每天下班后需要面对读者千奇百怪的开发问题,精力实在有限。在此说明向博主提问的规范,提高双方的沟通效率: 50 | 51 | - **能够在博客评论区说清楚的问题,请尽量通过评论提问。**这样做的好处是其他读者也都能够看到,方便大家一起讨论、解决类似的问题。评论时请精炼语言并稍加排版,帮助博主节省一点点精力。 52 | - 喜欢寻找学伴互相交流学习的,可以加博主的 **Django交流QQ群**:107143175,或者**博主公众号**:FullStack程序猿。一个人学习难免走弯路,有热心人帮忙就不再寂寞了。 53 | 54 | ![](http://blog.dusaiphoto.com/QR-h.jpg) 55 | 56 | - 如果你的问题必须结合图片说明、或者特别难描述清楚的,可以通过Email提问。请详细描述你的意图、遇到的问题,然后将**所有相关的代码、报错、配置等信息**展示给我。博主不是技术大牛,没办法仅凭一句话或者一张图来帮你排查问题。 57 | - 如果以上方法均无效,请先与博主沟通,将项目打包发送给博主,运行你的代码来查找问题。**请注意这是最后才考虑的提问方式**,虽然博主很乐于和读者交流技术,但很多时候没有精力逐个排查网友的代码。 58 | 59 | ## 资源列表 60 | 61 | 如果你对如何将 Django 项目部署到云端完全不了解的,强烈建议先看看**传统部署流程**,建立大致印象: 62 | 63 | - [将 Django 项目部署到服务器](https://www.dusaiphoto.com/article/detail/71/) 64 | 65 | 如果你想重新学习 Django 开发的,可以先看博主的 Django 教程: 66 | 67 | - [Django 搭建个人博客教程](https://www.dusaiphoto.com/article/detail/2/) 68 | 69 | 此外,本文撰写时主要参考了以下资料: 70 | 71 | - [django-mysql-with-docker](http://www.nisanthsojan.com/django-mysql-with-docker -a-step-by-step-guide-for-local-development-part-1/) 72 | - [Docker Documention](https://docs.docker.com/) 73 | 74 | ## 挑战开始 75 | 76 | 多说无益,吸一口气,让挑战开始吧! 77 | -------------------------------------------------------------------------------- /md/02.Docker-Django本地部署.md: -------------------------------------------------------------------------------- 1 | 本章将在本地搭建一个容器化的 Django 项目,感受 Docker 的运作方式。 2 | 3 | ## 前期准备 4 | 5 | ### 开发环境 6 | 7 | 虽然有基于 Windows 的 Docker 版本,但各方面兼容做得都不太好(安装也麻烦些),因此建议读者在学习前,自行安装好 Linux 或 Mac 系统。当然你愿意折腾的话,在 Windows 上搞也行。 8 | 9 | > 别担心,以后开发 Django 项目仍然可以在 Windows 下进行,仅仅是开发时不使用 Docker 而已。 10 | 11 | ### 软件安装 12 | 13 | - **Docker**:学习 Docker 当然要安装 Docker 软件了(免费的社区版),安装方法见[官方文档](https://docs.docker.com/install/linux/docker-ce/ubuntu/)。 14 | - **Docker-compose**:这是 Docker 官方推出的用于编排、运行多个容器的工具,安装方法见[官方文档](https://docs.docker.com/compose/install/)。本教程大部分内容都与它有关。 15 | - **Python3**:教程部署的是 Django 项目,那 Python3 是当然要有的了(包括 python 的包管理工具 **pip**)。 16 | 17 | 准备就绪后就继续下一步吧。 18 | 19 | ### 创建 Django 项目 20 | 21 | 打开 Linux/Mac 的终端,安装 Django 库: 22 | 23 | ```shell 24 | $ pip install django==2.2 25 | ``` 26 | 27 | 在一个你喜欢的位置(比如/home/)创建新的 Django 项目: 28 | 29 | ```shell 30 | $ django-admin startproject django_app 31 | ``` 32 | 33 | 进入项目根目录: 34 | 35 | ```shell 36 | $ cd django_app 37 | ``` 38 | 39 | 教程后面的内容全部都在此目录中操作了。**为方便阅读**,命令提示符 `$` 代表目前在项目根目录 `django_app/`,`mysql $` 则代表目前在目录 `django_app/mysql/` 中,请读者操作时稍加留意当前的工作目录。 40 | 41 | 然后迁移数据: 42 | 43 | ```shell 44 | $ python manage.py migrate 45 | 46 | Operations to perform: 47 | Apply all migrations: admin, auth, contenttypes, sessions 48 | Running migrations: 49 | Applying contenttypes.0001_initial... OK 50 | ... 51 | Applying sessions.0001_initial... OK 52 | ``` 53 | 54 | 准备工作就搞定了。 55 | 56 | ## 用Docker构建项目 57 | 58 | ### 初识Docker 59 | 60 | Docker 的整个生命周期由三部分组成:镜像(image)+ 容器(container)+ 仓库(repository)。 61 | 62 | **容器是由镜像实例化而来**,这有点像面向对象的概念:镜像就是类,容器是类实例化之后的对象。 63 | 64 | **镜像**是一个只读的模板,它包括了运行容器所需的数据。镜像可以包含一个完整的 Linux 操作环境,里面仅安装了 Python 或者其他用户需要的程序。 65 | 66 | **容器**是由镜像创建出来的实例,类似虚拟机,里面可以运行特定的应用,并且容器与容器是相互隔离的。 67 | 68 | **仓库**概念与 Git 和 Github 类似,如果你用过它们就非常容易理解。Docker 使用的默认仓库是由官方维护的 Docker hub 公共仓库,从中上传、拉取的操作类似 Git。 69 | 70 | 目前需要了解的就这么多,下面通过实践来理解。 71 | 72 | ### Hello-world 73 | 74 | 为了确认 Docker 已经正确安装了,运行以下指令: 75 | 76 | ```shell 77 | $ docker run hello-world 78 | 79 | Unable to find image 'hello-world:latest' locally 80 | ... 81 | latest: Pulling from library/hello-world 82 | 1b930d010525: Pull complete 83 | ... 84 | 85 | Hello from Docker! 86 | This message shows that your installation appears to be working correctly. 87 | ... 88 | ``` 89 | 90 | 一切正常的话,终端将打印出如上图所示的欢迎语句。`docker run hello-world` 指令的含义是:用名称为 `hello-world` 的镜像构建一个容器并运行。如果本地上没有这个 `hello-world` 的镜像, Docker 会自动从**仓库**搜索并下载同名的镜像。 91 | 92 | 我们可以用 `docker images` 查看本地已有的镜像: 93 | 94 | ```shell 95 | $ docker images 96 | 97 | REPOSITORY TAG IMAGE ID CREATED SIZE 98 | hello-world latest fce289e99eb9 9 months ago 1.84kB 99 | ``` 100 | 101 | 表列分别为镜像名、版本、ID 号、创建时间、大小。 102 | 103 | 还可以查看本地已有的容器: 104 | 105 | ```shell 106 | $ docker ps -a 107 | 108 | CONTAINER ID IMAGE .. CREATED .. 109 | 38cb03a96dca hello-world .. 2 minutes ago .. 110 | ``` 111 | 112 | 除此之外还有一些非常有用的基础指令: 113 | 114 | ```shell 115 | docker rmi [images ID] # 删除此 ID 的镜像 116 | docker container stop [container ID] # 停止此 ID 的容器 117 | docker container start [container ID] # 启动此 ID 的容器 118 | docker container rm [container ID] # 删除此 ID 的容器 119 | ``` 120 | 121 | 由于测试时会频繁生成镜像,因此你肯定会用上面的指令查看、删除无用的镜像和容器。 122 | 123 | 牛刀小试完毕,接下来正式构建 Django 容器。 124 | 125 | ### Dockerfile 126 | 127 | Docker 允许通过文本格式的配置文件来构建镜像,默认名称为 **Dockerfile**。因此在项目根目录新建文件 **Dockerfile**,写入: 128 | 129 | ```shell 130 | # 从仓库拉取 带有 python 3.7 的 Linux 环境 131 | FROM python:3.7 132 | 133 | # 设置 python 环境变量 134 | ENV PYTHONUNBUFFERED 1 135 | 136 | # 创建 code 文件夹并将其设置为工作目录 137 | RUN mkdir /code 138 | WORKDIR /code 139 | # 更新 pip 140 | RUN pip install pip -U 141 | # 将 requirements.txt 复制到容器的 code 目录 142 | ADD requirements.txt /code/ 143 | # 安装库 144 | RUN pip install -r requirements.txt 145 | # 将当前目录复制到容器的 code 目录 146 | ADD . /code/ 147 | ``` 148 | 149 | 理解这些 Docker 指令的关键在于,一定要牢记**容器**里的环境和外界**(宿主机)**是隔离的,它两是完全不一样的。换句话说,**要搞清楚哪些操作是针对宿主机、哪些操作是针对容器**。 150 | 151 | `FROM python:3.7` 指令从仓库拉取一个包含 python 3.7 的 Linux 操作系统环境(Linux 版本为 Debian)。 152 | 153 | `RUN` 和 `WORKDIR` 指令都是针对容器的,功能是在**容器里**创建目录、并将其设置为工作目录。注意**宿主机**是没有这个目录的。 154 | 155 | `ADD` 指令出现了两次。`ADD requirements.txt /code/` 意思是将宿主机当前目录(即 Dockerfile 所在目录)的 `requirements.txt` 文件复制到容器的 `/code` 目录中。`ADD . /code/` 意思是把当前目录所有内容复制到容器 `/code/` 目录,注意中间那个**点**。 156 | 157 | 目前项目依赖的唯一库就是 Django,所以在项目根目录创建 `requirements.txt` 并写入: 158 | 159 | ```shell 160 | django==2.2 161 | ``` 162 | 163 | > 前面不是已经安装过 Django 了吗,为什么这里还要安装?原因是前面是在宿主机安装的,容器里是没有 Django 的! 164 | 165 | 所以目前的文件结构如下: 166 | 167 | ```shell 168 | django_app 169 | - Dockerfile 170 | - requirements.txt 171 | - manage.py 172 | - django_app 173 | - db.sqlite3 174 | ``` 175 | 176 | 配置文件就写好了,接下来看看 `Docker-compose` 怎么用。 177 | 178 | ### Docker-compose 179 | 180 | 在线上环境中,通常不会将项目的所有组件放到同一个容器中;更好的做法是**把每个独立的功能装进单独的容器**,这样方便复用。比如将 Django 代码放到容器A,将 Mysql 数据库放到容器B,以此类推。 181 | 182 | 因此同一个服务器上有可能会运行着多个容器,如果每次都靠一条条指令去启动,未免也太繁琐了。 `Docker-compose` 就是解决这个问题的,它用来编排多个容器,将启动容器的命令统一写到 docker-compose.yml 文件中,以后每次启动这一组容器时,只需要 `docker-compose up` 就可以了。因此教程也会用 docker-compose 来管理容器。 183 | 184 | 首先确认 docker-compose 是否安装成功: 185 | 186 | ```shell 187 | $ docker-compose -v 188 | docker-compose version 1.24.1, build 4667896b 189 | ``` 190 | 191 | 确认无误后,在项目根目录创建 **docker-compose.yml** 并写入: 192 | 193 | ```shell 194 | version: "3" 195 | services: 196 | app: 197 | restart: always 198 | build: . # '点'代表当前目录 199 | command: "python3 manage.py runserver 0.0.0.0:8000" 200 | volumes: 201 | - .:/code 202 | ports: 203 | - "8000:8000" 204 | ``` 205 | 206 | 让我们来分解一下其中的各项含义。 207 | 208 | `version` 代表 docker-compose.yml 的版本,目前最新版为 3,不需要改动它。 209 | 210 | 接着定义了一个名叫 `app` 的容器。后面的内容都是 `app` 容器的相关配置: 211 | 212 | - `restart` :除正常工作外,容器会在任何时候重启,比如遭遇 bug、进程崩溃、docker 重启等情况。 213 | - `build` :指定一个包含 **Dockerfile** 的路径,并通过此 Dockerfile 来构建容器镜像。注意那个 **"."** ,代表当前目录。 214 | - `command` :容器运行时需要执行的命令。这里就是我们很熟悉的运行开发服务器了。 215 | - `volumes` :**卷,这是个很重要的概念。**前面说过容器是和宿主机完全隔离的,但是有些时候又需要将其连通;比如我们开发的 Django 项目代码常常会更新,并且更新时还依赖如 Git 之类的程序,在容器里操作就显得不太方便。所以就有**卷**,它定义了宿主机和容器之间的映射:**"."** 表示宿主机的当前目录,**":"** 为分隔符,"/code" 表示容器中的目录。即宿主机当前目录和容器的 /code 目录是连通的,宿主机当前目录的 Django 代码更新时,容器中的 /code 目录中的代码也相应的更新了。这有点儿像是在容器上打了一个洞,某种程度上也是**实用性**和**隔离性**的一种妥协。 216 | 217 | > 严格意义上讲,这里用到的 `.:/code` 并不是**卷**,而是叫**挂载**,它两是有区别的,只不过 docker-compose 允许将挂载写到卷的配置中。后面章节会讲到。 218 | 219 | - `ports` :定义了宿主机和容器的端口映射。容器的隔离不止环境,甚至连端口都隔离起来了。但 web 应用不通过端口跟外界通信当然不行,因此这里定义将宿主机的 8000 端口映射到容器的 8000 端口,即访问宿主机的 8000 端口就是访问到了容器的 8000 端口,但要确保端口没有被其他程序占用。 220 | 221 | 配置就写好了。现在项目的目录结构如下: 222 | 223 | ```shell 224 | django_app 225 | - docker-compose.yml 226 | - Dockerfile 227 | - requirements.txt 228 | - manage.py 229 | - django_app 230 | - db.sqlite3 231 | ``` 232 | 233 | ## 测试 234 | 235 | 输入指令 `docker-compose up` 启动容器服务: 236 | 237 | ```shell 238 | $ docker-compose up 239 | 240 | Creating network "django_app_default" with the default driver 241 | Building app 242 | Step 1/8 : FROM python:3.7 243 | 3.7: Pulling from library/python 244 | 4a56a430b2ba: Pull complete 245 | ... 246 | 6933d3d46042: Pull complete 247 | Digest: sha256:0f0e991a97426db345ca7ec59fa911c8ed27ced27c88ae9966b452bcc6438c2f 248 | Status: Downloaded newer image for python:3.7 249 | ---> 02d2bb146b3b 250 | Step 1/8 : FROM python:3.7 251 | ---> 02d2bb146b3b 252 | ... 253 | Step 7/8 : RUN pip install -r requirements.txt 254 | ---> Running in 62a60a3003fe 255 | Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple 256 | Collecting django==2.2 (from -r requirements.txt (line 1)) 257 | Downloading https://files.pythonhosted.org/packages/54/85/0bef63668fb170888c1a2970ec897d4528d6072f32dee27653381a332642/Django-2.2-py3-none-any.whl (7.4MB) 258 | ... 259 | Installing collected packages: sqlparse, pytz, django 260 | Successfully installed django-2.2 pytz-2019.2 sqlparse-0.3.0 261 | ... 262 | Step 8/8 : ADD . /code/ 263 | ---> cb23f483ffb6 264 | Successfully built cb23f483ffb6 265 | Successfully tagged django_app_app:latest 266 | WARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. 267 | Creating django_app_app_1 ... done 268 | Attaching to django_app_app_1 269 | app_1 | Watching for file changes with StatReloader 270 | app_1 | Performing system checks... 271 | app_1 | 272 | app_1 | System check identified no issues (0 silenced). 273 | app_1 | October 05, 2019 - 15:03:15 274 | app_1 | Django version 2.2, using settings 'django_app.settings' 275 | app_1 | Starting development server at http://0.0.0.0:8000/ 276 | app_1 | Quit the server with CONTROL-C. 277 | ``` 278 | 279 | 可以看到 Docker 按照配置文件的要求,成功构建了镜像及容器,并启动了容器。 280 | 281 | 打开浏览器,输入本地 IP 端口 `127.0.0.1:8000` : 282 | 283 | ![](https://blog.dusaiphoto.com/dusainet-7000K/d02-1.jpg) 284 | 285 | 看到 Django 的小火箭,项目成功运行起来啦。按 `Ctrl + C` 即可停止开发服务器运行。 286 | 287 | 停止服务器后实际上容器还存在,只是停止运行了而已。输入: 288 | 289 | ```shell 290 | $ docker-compose down 291 | ``` 292 | 293 | 就可以删除容器。 294 | 295 | 如果想在后台运行容器,则输入: 296 | 297 | ```shell 298 | $ docker-compose up -d 299 | ``` 300 | 301 | 另外,如果你需要重新构建镜像,则输入命令: 302 | 303 | ```shell 304 | $ docker-compose build 305 | ``` 306 | 307 | 启动和停止已有的容器: 308 | 309 | ```shell 310 | $ docker-compose start 311 | $ docker-compose stop 312 | ``` 313 | 314 | 很简单吧。 315 | 316 | ## 下载太慢? 317 | 318 | 由于众所周知的原因,国内的网络环境非常复杂。在构建镜像时经常需要从国外的远程仓库拉取资源,岿然不动的下载速度真的头疼。 319 | 320 | 解决方法就是将资源拉取链接修改为国内的镜像源,比如清华的镜像源。 321 | 322 | 修改 Dockerfile 如下: 323 | 324 | ```shell 325 | FROM python:3.7 326 | ENV PYTHONUNBUFFERED 1 327 | 328 | # 添加 Debian 清华镜像源 329 | RUN echo \ 330 | deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free\ 331 | deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free\ 332 | deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free\ 333 | deb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free\ 334 | > /etc/apt/sources.list 335 | 336 | RUN mkdir /code 337 | WORKDIR /code 338 | # 添加 pip 清华镜像源 339 | RUN pip install pip -U -i https://pypi.tuna.tsinghua.edu.cn/simple 340 | ADD requirements.txt /code/ 341 | # 添加 pip 清华镜像源 342 | RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple 343 | ADD . /code/ 344 | ``` 345 | 346 | 重新构建镜像,下载速度就飞一样快了。 347 | 348 | 教程后面的内容都使用了清华源,但为了方便阅读,**把这一部分更换源的代码省去了**,读者心里明白就好。 349 | 350 | ## 总结 351 | 352 | 本章初步感受了 Docker 的工作流程,并且很轻松的构建了一个容器化的 Django 项目。 353 | 354 | 下一章将 MySQL 数据库也加入到容器编排中。 355 | 356 | ------ 357 | 358 | - 有疑问请在[杜赛的个人网站](https://www.dusaiphoto.com)留言,我会尽快回复。 359 | - 教程示例代码:[django-docker-tutorial](https://github.com/stacklens/django-docker-tutorial) 360 | - 或Email私信我:dusaiphoto@foxmail.com -------------------------------------------------------------------------------- /md/03.Docker-Django-MySQL本地部署.md: -------------------------------------------------------------------------------- 1 | 上一章我们成功搭建了容器化的 Django 项目,用到的数据库为默认的 Sqlite。Sqlite 虽然简单易用,但是线上部署时通常会选择更高效、更可靠的数据库,比如 MySQL。 2 | 3 | 本章将在上一章的基础上,修改并构建 Docker + Django + MySQL 的容器项目。 4 | 5 | ## Docker-compose 6 | 7 | 我们在学习面向对象的编程语言时,会想方设法把功能独立的模块给独立出来,方便复用和维护。 8 | 9 | 容器也是一样的。虽然理论上可以把所有组件塞到同一个容器中去,但更好的做法是各模块在单独容器中,只要保持必要的通信就可以了。 10 | 11 | 也就是说,本教程中现在需要两个容器了: 12 | 13 | - 名称叫 app 的 Django 容器 14 | - 名称叫 db 的 MySQL 容器 15 | 16 | 所以如何构建 MySQL 镜像?别担心,这么常用的镜像官方已经帮你构建好了,只需要把它从仓库拉取到本地就可以了。 17 | 18 | 修改上一章写的 `docker-compose.yml` ,增加 MySQL 容器: 19 | 20 | ```shell 21 | version: "3" 22 | services: 23 | app: 24 | restart: always 25 | build: . 26 | command: bash -c "python3 manage.py migrate && python3 manage.py runserver 0.0.0.0:8000" 27 | volumes: 28 | - .:/code 29 | ports: 30 | - "8000:8000" 31 | depends_on: 32 | - db 33 | db: 34 | image: mysql:5.7 35 | volumes: 36 | - "./mysql:/var/lib/mysql" 37 | ports: 38 | - "3306:3306" 39 | restart: always 40 | environment: 41 | - MYSQL_ROOT_PASSWORD=mypassword 42 | - MYSQL_DATABASE=django_app 43 | ``` 44 | 45 | `app` 容器的 `command` 指令做了修改,让其在运行前先执行数据迁移;新增了配置 `depends_on` ,意思是此容器需要等待 `db` 容器启动完毕才能够启动。 46 | 47 | 分析一下新添加的 `db` 容器: 48 | 49 | - `image` :从仓库拉取 MySQL 5.7 。最新版本为 MySQL 8,不过很坑的是新版本修改了用户登录的验证方法,导致很容易出现无法通过身份验证的问题。教程为了简单起见选用 5.7 版本。后期会在[教程示例代码](https://github.com/stacklens/django-docker-tutorial)中添加[mysql-8]()分支并给出操作方法,有兴趣的读者可以查看。 50 | - `volumes` :定义卷(这里实际是挂载),上一章已经讲过了,它实现了宿主机和容器目录的映射。功能是将容器中的 MySQL 数据映射到宿主机。 51 | - `ports` :MySQL 默认通信端口为 3306 。 52 | - `environment` :定义容器的环境变量,设置了 MySQL 的 root 用户的密码、数据库的名称。 53 | 54 | 这里为什么要用**卷**?就让数据在容器中、保持隔离不好吗?把数据保存在容器中,理论上确实是可以的,但有一个致命的问题,即数据和容器的生命周期挂钩了:万一哪天手贱把容器给删了,连同里面的数据随风而逝,你就是全公司那个删库跑路的传奇人物了。要知道容器的生命周期可能会非常短暂,删除指令也相当顺滑(`docker-compose down`)。将数据映射到宿主机,容器即使被删除掉,但数据还是安全的躺在你的服务器中的。换句话说,容器内部非常**适合运行无状态的应用**;涉及到如数据之类有状态的东西,一定要谨慎思考。 55 | 56 | ## Dockerfile 57 | 58 | 接下来修改 `Dockerfile` : 59 | 60 | ```shell 61 | FROM python:3.7 62 | ENV PYTHONUNBUFFERED 1 63 | 64 | # 添加这两行 65 | RUN apt-get update 66 | RUN apt-get install python3-dev default-libmysqlclient-dev -y 67 | 68 | RUN mkdir /code 69 | WORKDIR /code 70 | RUN pip install pip -U 71 | ADD requirements.txt /code/ 72 | RUN pip install -r requirements.txt 73 | ADD . /code/ 74 | ``` 75 | 76 | 增加的两行代码在系统中安装了 MySQL 的连接器,具体解释见[官方文档](https://pypi.org/project/mysqlclient/)。 77 | 78 | ## 其他配置 79 | 80 | 修改 `requirements.txt` ,增加 MySQL 驱动: 81 | 82 | ```python 83 | django==2.2 84 | mysqlclient==1.3.14 85 | ``` 86 | 87 | 然后还需要修改 `django_app/settings.py` ,将数据库设置为 MySQL: 88 | 89 | ```python 90 | DATABASES = { 91 | 'default': { 92 | 'ENGINE': 'django.db.backends.mysql', 93 | 'NAME': 'django_app', 94 | 'USER': 'root', 95 | 'PASSWORD': 'mypassword', 96 | 'HOST': 'db', 97 | 'PORT': '3306', 98 | 'OPTIONS': {'charset': 'utf8mb4'}, 99 | } 100 | } 101 | ``` 102 | 103 | 注意 HOST 填写的是容器的名称,即 db 。 104 | 105 | 这就可以啦。接下来测试。 106 | 107 | ## 测试 108 | 109 | 测试之前,请先确认没有其他程序占用了 3306 端口,比如宿主机安装的 MySQL。 110 | 111 | 重新生成镜像: 112 | 113 | ```shell 114 | $ docker-compose build 115 | ``` 116 | 117 | 生成并启动容器: 118 | 119 | ```shell 120 | $ docker-compose up 121 | 122 | Creating network "django_app_default" with the default driver 123 | Creating django_app_db_1 ... done 124 | Creating django_app_app_1 ... done 125 | Attaching to django_app_db_1, django_app_app_1 126 | db_1 | 2019-10-06T12:24:57.183860Z 0 [Note] mysqld (mysqld 5.7.27) starting as process 1 ... 127 | 128 | ... 129 | 130 | db_1 | 2019-10-06T12:24:58.120480Z 0 [Note] mysqld: ready for connections. 131 | db_1 | Version: '5.7.27' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL) 132 | 133 | app_1 | Operations to perform: 134 | app_1 | Apply all migrations: admin, auth, contenttypes, sessions 135 | app_1 | Running migrations: 136 | app_1 | Applying contenttypes.0001_initial... OK 137 | ... 138 | app_1 | Applying sessions.0001_initial... OK 139 | 140 | app_1 | Watching for file changes with StatReloader 141 | app_1 | Performing system checks... 142 | app_1 | 143 | app_1 | System check identified no issues (0 silenced). 144 | app_1 | October 06, 2019 - 12:24:58 145 | app_1 | Django version 2.2, using settings 'django_app.settings' 146 | app_1 | Starting development server at http://0.0.0.0:8000/ 147 | app_1 | Quit the server with CONTROL-C. 148 | ``` 149 | 150 | 打开浏览器访问 `127.0.0.1:8000` ,又能看到 Django 小火箭啦。 151 | 152 | > **注意:**第一次启动容器时可能会出现无法连接 MySQL 的错误,这是由于虽然 db 容器已经启动,但初始化并未完成;重新启动容器之后就可以正常工作了。若多次启动都无法正常工作,那就是别的原因了,好好检查吧。 153 | 154 | ## 总结 155 | 156 | 本章加入了 MySQL 容器,并实现了多容器协同工作。 157 | 158 | 下一章将实现正式部署的 Docker + Django + MySQL + Nginx + Gunicorn 项目。 159 | 160 | ------ 161 | 162 | - 有疑问请在[杜赛的个人网站](https://www.dusaiphoto.com)留言,我会尽快回复。 163 | - 教程示例代码:[django-docker-tutorial](https://github.com/stacklens/django-docker-tutorial) 164 | - 或Email私信我:[dusaiphoto@foxmail.com](mailto:dusaiphoto@foxmail.com) -------------------------------------------------------------------------------- /md/04.Docker-Django-MySQL-Nginx-Gunicorn云端部署.md: -------------------------------------------------------------------------------- 1 | 上一章我们实现了在 Docker 中添加了 MySQL 数据库,但采用的开发服务器虽然使用便捷,但性能差、可靠性低,无法应用在生产环境中。 2 | 3 | 因此本章将实现 Docker + Django + MySQL + Nginx + Gunicorn 容器项目,完成最终的服务器部署。 4 | 5 | > 直接进入本章的 Docker 入门读者,建议回到教程第一章开始阅读,否则某些内容不好理解。对 Django 项目部署都没有概念的读者,还可以先阅读我的博文:[将 Django 项目部署到服务器](https://www.dusaiphoto.com/article/detail/71/)。 6 | 7 | ## Docker-compose 8 | 9 | 在部署到服务器之前,先来尝试本地部署。 10 | 11 | 在上一章的基础上,继续修改 `docker-compose.yml` 配置: 12 | 13 | ```shell 14 | version: "3" 15 | 16 | services: 17 | app: 18 | restart: always 19 | build: . 20 | command: bash -c "python3 manage.py collectstatic --no-input && python3 manage.py migrate && gunicorn --timeout=30 --workers=4 --bind :8000 django_app.wsgi:application" 21 | volumes: 22 | - .:/code 23 | - static-volume:/code/collected_static 24 | expose: 25 | - "8000" 26 | depends_on: 27 | - db 28 | networks: 29 | - web_network 30 | - db_network 31 | db: 32 | image: mysql:5.7 33 | volumes: 34 | - "./mysql:/var/lib/mysql" 35 | ports: 36 | - "3306:3306" 37 | restart: always 38 | environment: 39 | - MYSQL_ROOT_PASSWORD=mypassword 40 | - MYSQL_DATABASE=django_app 41 | networks: 42 | - db_network 43 | nginx: 44 | restart: always 45 | image: nginx:latest 46 | ports: 47 | - "8000:8000" 48 | volumes: 49 | - static-volume:/code/collected_static 50 | - ./config/nginx:/etc/nginx/conf.d 51 | depends_on: 52 | - app 53 | networks: 54 | - web_network 55 | 56 | networks: 57 | web_network: 58 | driver: bridge 59 | db_network: 60 | driver: bridge 61 | 62 | volumes: 63 | static-volume: 64 | ``` 65 | 66 | 有点复杂。来看看大体思路: 67 | 68 | - 定义了 3 个**容器**,分别是 `app` 、 `db` 和 `nginx` 。容器之间通过定义的端口进行通讯。 69 | - 定义了 2 个**网络**,分别是 `web_network` 和 `db_network` 。只有处在相同网络的容器才能互相通讯。不同网络之间是隔离的,即便采用同样的端口,也无法通讯。 70 | - 定义了 1 个**数据卷**, `static-volume` 。数据卷非常适合多个容器共享使用同一数据,你可以看到 `app` 和 `nginx` 都用到了它。 71 | - `expose` 和 `ports` 都可以暴露容器的端口,区别是 expose 仅暴露给其他容器,而 ports 会暴露给其他容器和宿主机。 72 | 73 | 这么讲可能还是很难理解,让我们继续分解。 74 | 75 | ### 网络 network 76 | 77 | Docker 允许用户给每个容器定义其工作的网络,只有在相同的网络之中才能进行通讯。你可以看到 `nginx` 容器处于 `web_network` 网络,而 `db` 容器处于 `db_network` 网络,因此它两是无法通讯的,实际上确实也不需要通讯。而 `app` 容器同时处于 `web_network` 和 `db_network` 网络,相当于是桥梁,连通了3个容器。 78 | 79 | 定义网络可以隔离容器的网络环境,也方便运维人员一眼看出网络的逻辑关系。 80 | 81 | ### 数据卷 82 | 83 | 之前我们见识过的用于映射宿主机和容器目录的卷了,实际上称为**挂载**;现在新出现的 `static-volume` 才叫**卷**。它的使用方式像这样:`static-volume:/code/collected_static` ,冒号后面还是容器内的目录,但冒号前的却不是宿主机目录、仅仅是卷的名称而已。从本质上讲,数据卷也是实现了宿主机和容器的目录映射,但是数据卷是由 Docker 进行管理的,你甚至都不需要知道数据卷保存在宿主机的具体位置。 84 | 85 | 相比挂载,数据卷的优点是由于是 Docker 统一管理的,不存在由于权限不够引发的挂载问题,也不需要在不同服务器指定不同的路径;缺点是它不太适合单配置文件的映射。 86 | 87 | 和挂载一样,数据卷的生命周期脱离了容器,删除容器之后卷还是存在的。下次构建镜像时,指定卷的名称就可以继续使用了。 88 | 89 | > 既然 Docker 能够管理卷,所以要想删除卷也是非常容易的。指令嘛,我不告诉你,生产环境千万不要手贱。定期备份数据是个好习惯。 90 | 91 | **数据卷有个很重要的特性**: 92 | 93 | - 容器启动时,如果挂载一个**空的数据卷**到容器中的一个**非空目录**中,那么这个目录下的文件会被复制到数据卷中; 94 | - 如果挂载一个**非空数据卷**到容器中的一个目录中,那么容器中的目录中会显示数据卷中的数据;如果原来容器中的目录中有数据,那么这些原始数据会被隐藏掉。 95 | 96 | 换句话说就是,只要卷初始化完成后,容器原始的 `collected_static` 目录就被隐藏起来不再使用了,新增的文件也只存在于卷中,容器中是没有的。 97 | 98 | 另外, static 静态文件(以及 media 媒体文件)的持久存储,通过**挂载**或者**数据卷**都可以实现;具体用哪种,这个就见仁见智了,你自己选择。 99 | 100 | > 篇幅有限,教程没有讲到 media 媒体文件,但它的设置和 static 是完全相同的。 101 | 102 | ## 其他配置 103 | 104 | 首先修改 Nginx 的配置文件,即映射到 `nginx` 容器的 `config/nginx/django_app.conf` : 105 | 106 | ```nginx 107 | upstream app { 108 | ip_hash; 109 | server app:8000; 110 | } 111 | 112 | server { 113 | listen 8000; 114 | server_name localhost; 115 | 116 | location /static/ { 117 | autoindex on; 118 | alias /code/collected_static/; 119 | } 120 | 121 | location / { 122 | proxy_pass http://app/; 123 | } 124 | } 125 | ``` 126 | 127 | 此配置下 Nginx 会监听容器的 8000 端口,并将收到的请求发送到 `app` 容器(静态文件请求除外)。 128 | 129 | 在 `requirements.txt` 文件中增加 `gunicorn` 库: 130 | 131 | ```python 132 | django==2.2 133 | mysqlclient==1.3.14 134 | gunicorn==19.9.0 135 | ``` 136 | 137 | 最后修改 `django_app/settings.py` 的**域**和静态文件**存放目录**的配置: 138 | 139 | ```python 140 | ... 141 | 142 | ALLOWED_HOSTS = ['*'] 143 | 144 | ... 145 | 146 | STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static') 147 | STATIC_URL = '/static/' 148 | ``` 149 | 150 | 所有配置就完成了。 151 | 152 | > 教程使用空的 Django 项目,为演示效果,就没有修改 DEBUG=False 了。若你用的自己的项目测试,记得把它为 False。 153 | 154 | ## 测试 155 | 156 | 测试指令就一条: 157 | 158 | ```shell 159 | $ docker-compose up 160 | ``` 161 | 162 | 浏览器访问 `127.0.0.1:8000` 又看到熟悉的 Django 小火箭了。 163 | 164 | > 和上一章类似,第一次启动容器时可能会出现无法连接 MySQL 的错误,这是由于虽然 db 容器已经启动,但初始化并未完成;重新启动容器之后就可以正常工作了。若多次启动都无法正常工作,那就是别的原因了,好好检查吧。 165 | 166 | 本地部署成功,下一步服务器部署。 167 | 168 | ## 服务器部署 169 | 170 | 有了本地部署的经验,服务器部署就非常非常简单了。 171 | 172 | 还是类似的,部署前将 Docker 、 Docker-compose 、 Python3 等工具在服务器上安装好;将项目用 Git 克隆到服务器本地。 173 | 174 | 接下来把 `settings.py`、`config/nginx/django_app.conf`、`requirements.txt` 相关位置都按教程流程改好;将 `docker-compose.yml` 和 `Dockerfile` 复制到服务器。 175 | 176 | 由于 http 请求默认为 80 端口,所以为了接收公网请求,还需要做一点点修改 `docker-compose.yml` 的工作: 177 | 178 | ```shell 179 | version: "3" 180 | 181 | services: 182 | app: 183 | ... 184 | command: bash -c "... your_project_name.wsgi:application" # 改为你的项目名称 185 | ... 186 | db: 187 | ... 188 | nginx: 189 | ... 190 | ports: 191 | - "80:8000" # 监听 80 端口 192 | ... 193 | 194 | networks: 195 | ... 196 | 197 | volumes: 198 | ... 199 | ``` 200 | 201 | 修改 Gunicorn 绑定的项目名称,以及让宿主机监听公网 http 默认的 80 端口。 202 | 203 | 此外还要修改 `config/nginx/django_app.conf` : 204 | 205 | ```nginx 206 | upstream your_domain_name { 207 | ip_hash; 208 | server app:8000; 209 | } 210 | 211 | server { 212 | ... 213 | 214 | location / { 215 | proxy_pass http://your_domain_name/; 216 | } 217 | } 218 | ``` 219 | 220 | 这个改动主要是为了照顾各种第三方登录的回调地址(不改这里, GitHub、Weibo 三方登录都会失败)。如果你没有类似的需求,不改也是可以的。比如[博主的个人网站](https://www.dusaiphoto.com/)是 `www.dusaiphoto.com`,所以这里的 `your_domain_name` 就修改为 `www.dusaiphoto.com` 。 221 | 222 | 最后,记得将 `settings.py` 中的 DEBUG 配置修改好: 223 | 224 | ```python 225 | # DEBUG=True 注释掉 226 | DEBUG=False 227 | ``` 228 | 229 | 这样就可以了!构建镜像并启动容器: 230 | 231 | ```shell 232 | $ docker-compose up 233 | ``` 234 | 235 | 在浏览器中就可以正常访问你的网站了。 236 | 237 | ## 总结 238 | 239 | 现在你已经可以部署一个线上的容器化 Django 项目了,恭喜! 240 | 241 | 若本教程对你有帮助,请到[GitHub](https://github.com/stacklens/django-docker-tutorial)给个 Star 哟,也欢迎阅读我的[Django 搭建博客教程](https://www.dusaiphoto.com/article/detail/2/)。 242 | 243 | 老朋友们,下个教程见! 244 | 245 | ------ 246 | 247 | - 有疑问请在[杜赛的个人网站](https://www.dusaiphoto.com)留言,我会尽快回复。 248 | - 教程示例代码:[django-docker-tutorial](https://github.com/stacklens/django-docker-tutorial) 249 | - 或Email私信我:dusaiphoto@foxmail.com -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | django==2.2 2 | mysqlclient==1.3.14 3 | gunicorn==19.9.0 4 | --------------------------------------------------------------------------------