Django Docker容器化部署之Django-Docker本地部署


Posted in Python onOctober 09, 2019

本章将在本地搭建一个容器化的 Django 项目,感受 Docker 的运作方式。

前期准备

开发环境

虽然有基于 Windows 的 Docker 版本,但各方面兼容做得都不太好(安装也麻烦些),因此建议读者在学习前,自行安装好 Linux 或 Mac 系统。当然你愿意折腾的话,在 Windows 上搞也行。

别担心,以后开发 Django 项目仍然可以在 Windows 下进行,仅仅是开发时不使用 Docker 而已。

软件安装

  • Docker:学习 Docker 当然要安装 Docker 软件了(免费的社区版),安装方法见官方文档。
  • Docker-compose:这是 Docker 官方推出的用于编排、运行多个容器的工具,安装方法见官方文档。本教程大部分内容都与它有关。
  • Python3:教程部署的是 Django 项目,那 Python3 是当然要有的了(包括 python 的包管理工具 pip)。

准备就绪后就继续下一步吧。

创建 Django 项目

打开 Linux/Mac 的终端,安装 Django 库:

$ pip install django==2.2

在一个你喜欢得位置(比如/home/)创建新的 Django 项目:

$ django-admin startproject django_app

进入项目根目录:

$ cd django_app

教程后面的内容全部都在此目录中操作了。为方便阅读,命令提示符 $ 代表目前在项目根目录 django_app/,mysql $ 则代表目前在目录 django_app/mysql/ 中,请读者操作时稍加留意当前的工作目录。

然后迁移数据:

$ python manage.py migrate

Operations to perform:
 Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
 Applying contenttypes.0001_initial... OK
 ...
 Applying sessions.0001_initial... OK

准备工作就搞定了。

用Docker构建项目

初识Docker

Docker 的整个生命周期由三部分组成:镜像(image)+ 容器(container)+ 仓库(repository)。

容器是由镜像实例化而来,这有点像面向对象的概念:镜像就是类,容器是类实例化之后的对象。

镜像是一个只读的模板,它包括了运行容器所需的数据。镜像可以包含一个完整的 Linux 操作环境,里面仅安装了 Python 或者其他用户需要的程序。

容器是由镜像创建出来的实例,类似虚拟机,里面可以运行特定的应用,并且容器与容器是相互隔离的。

仓库概念与 Git 和 Github 类似,如果你用过它们就非常容易理解。Docker 使用的默认仓库是由官方维护的 Docker hub 公共仓库,从中上传、拉取的操作类似 Git。

目前需要了解的就这么多,下面通过实践来理解。

Hello-world

为了确认 Docker 已经正确安装了,运行以下指令:

$ docker run hello-world

Unable to find image 'hello-world:latest' locally
...
latest: Pulling from library/hello-world
1b930d010525: Pull complete 
...

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

一切正常的话,终端将打印出如上图所示的欢迎语句。docker run hello-world 指令的含义是:用名称为 hello-world 的镜像构建一个容器并运行。如果本地上没有这个 hello-world 的镜像, Docker 会自动从仓库搜索并下载同名的镜像。

我们可以用 docker images 查看本地已有的镜像:

$ docker images

REPOSITORY   TAG    IMAGE ID      CREATED      SIZE
hello-world   latest   fce289e99eb9    9 months ago   1.84kB

表列分别为镜像名、版本、ID 号、创建时间、大小。

还可以查看本地已有的容器:

$ docker ps -a

CONTAINER ID  IMAGE    ..  CREATED     ..
38cb03a96dca  hello-world ..  2 minutes ago  ..

除此之外还有一些非常有用的基础指令:

docker rmi [images ID] # 删除此 ID 的镜像
docker container stop [container ID] # 停止此 ID 的容器
docker container start [container ID] # 启动此 ID 的容器
docker container rm [container ID] # 删除此 ID 的容器

由于测试时会频繁生成镜像,因此你肯定会用上面的指令查看、删除无用的镜像和容器。

牛刀小试完毕,接下来正式构建 Django 容器。

Dockerfile

Docker 允许通过文本格式的配置文件来构建镜像,默认名称为 Dockerfile。因此在项目根目录新建文件 Dockerfile,写入:

# 从仓库拉取 带有 python 3.7 的 Linux 环境
FROM python:3.7

# 设置 python 环境变量
ENV PYTHONUNBUFFERED 1

# 创建 code 文件夹并将其设置为工作目录
RUN mkdir /code
WORKDIR /code
# 更新 pip
RUN pip install pip -U
# 将 requirements.txt 复制到容器的 code 目录
ADD requirements.txt /code/
# 安装库
RUN pip install -r requirements.txt
# 将当前目录复制到容器的 code 目录
ADD . /code/

理解这些 Docker 指令的关键在于,一定要牢记容器里的环境和外界(宿主机)是隔离的,它两是完全不一样的。换句话说,要搞清楚哪些操作是针对宿主机、哪些操作是针对容器。

FROM python:3.7 指令从仓库拉取一个包含 python 3.7 的 Linux 操作系统环境(Linux 版本为 Debian)。

RUN 和 WORKDIR 指令都是针对容器的,功能是在容器里创建目录、并将其设置为工作目录。注意宿主机是没有这个目录的。

ADD 指令出现了两次。ADD requirements.txt /code/ 意思是将宿主机当前目录(即 Dockerfile 所在目录)的 requirements.txt 文件复制到容器的 /code 目录中。ADD . /code/ 意思是把当前目录所有内容复制到容器 /code/ 目录,注意中间那个点。

目前项目依赖的唯一库就是 Django,所以在项目根目录创建 requirements.txt 并写入:

django==2.2

前面不是已经安装过 Django 了吗,为什么这里还要安装?原因是前面是在宿主机安装的,容器里是没有 Django 的!

所以目前的文件结构如下:

django_app
 - Dockerfile
 - requirements.txt
 - manage.py
 - django_app
 - db.sqlite3

配置文件就写好了,接下来看看 Docker-compose 怎么用。

Docker-compose

在线上环境中,通常不会将项目的所有组件放到同一个容器中;更好的做法是把每个独立的功能装进单独的容器,这样方便复用。比如将 Django 代码放到容器A,将 Mysql 数据库放到容器B,以此类推。

因此同一个服务器上有可能会运行着多个容器,如果每次都靠一条条指令去启动,未免也太繁琐了。 Docker-compose 就是解决这个问题的,它用来编排多个容器,将启动容器的命令统一写到 docker-compose.yml 文件中,以后每次启动这一组容器时,只需要 docker-compose up 就可以了。因此教程也会用 docker-compose 来管理容器。

首先确认 docker-compose 是否安装成功:

$ docker-compose -v
docker-compose version 1.24.1, build 4667896b

确认无误后,在项目根目录创建 docker-compose.yml 并写入:

version: "3"
services:
 app:
  restart: always
  build: . # '点'代表当前目录
  command: "python3 manage.py runserver 0.0.0.0:8000"
  volumes:
   - .:/code
  ports:
   - "8000:8000"

让我们来分解一下其中的各项含义。

version 代表 docker-compose.yml 的版本,目前最新版为 3,不需要改动它。

接着定义了一个名叫 app 的容器。后面的内容都是 app 容器的相关配置:

  • restart :除正常工作外,容器会在任何时候重启,比如遭遇 bug、进程崩溃、docker 重启等情况。
  • build :指定一个包含 Dockerfile 的路径,并通过此 Dockerfile 来构建容器镜像。注意那个 "." ,代表当前目录。
  • command :容器运行时需要执行的命令。这里就是我们很熟悉的运行开发服务器了。
  • volumes :卷,这是个很重要的概念。前面说过容器是和宿主机完全隔离的,但是有些时候又需要将其连通;比如我们开发的 Django 项目代码常常会更新,并且更新时还依赖如 Git 之类的程序,在容器里操作就显得不太方便。所以就有卷,它定义了宿主机和容器之间的映射:"." 表示宿主机的当前目录,":" 为分隔符,"/code" 表示容器中的目录。即宿主机当前目录和容器的 /code 目录是连通的,宿主机当前目录的 Django 代码更新时,容器中的 /code 目录中的代码也相应的更新了。这有点儿像是在容器上打了一个洞,某种程度上也是实用性和隔离性的一种妥协。

严格意义上讲,这里用到的 .:/code 并不是卷,而是叫挂载,它两是有区别的,只不过 docker-compose 允许将挂载写到卷的配置中。后面章节会讲到。

ports :定义了宿主机和容器的端口映射。容器的隔离不止环境,甚至连端口都隔离起来了。但 web 应用不通过端口跟外界通信当然不行,因此这里定义将宿主机的 8000 端口映射到容器的 8000 端口,即访问宿主机的 8000 端口就是访问到了容器的 8000 端口,但要确保端口没有被其他程序占用。

配置就写好了。现在项目的目录结构如下:

django_app
 - docker-compose.yml
 - Dockerfile
 - requirements.txt
 - manage.py
 - django_app
 - db.sqlite3

测试

输入指令 docker-compose up 启动容器服务:

$ docker-compose up

Creating network "django_app_default" with the default driver
Building app
Step 1/8 : FROM python:3.7
3.7: Pulling from library/python
4a56a430b2ba: Pull complete
...
6933d3d46042: Pull complete
Digest: sha256:0f0e991a97426db345ca7ec59fa911c8ed27ced27c88ae9966b452bcc6438c2f
Status: Downloaded newer image for python:3.7
 ---> 02d2bb146b3b
Step 1/8 : FROM python:3.7
 ---> 02d2bb146b3b
...
Step 7/8 : RUN pip install -r requirements.txt
 ---> Running in 62a60a3003fe
Looking in indexes: https://pypi.tuna.tsinghua.edu.cn/simple
Collecting django==2.2 (from -r requirements.txt (line 1))
 Downloading https://files.pythonhosted.org/packages/54/85/0bef63668fb170888c1a2970ec897d4528d6072f32dee27653381a332642/Django-2.2-py3-none-any.whl (7.4MB)
...
Installing collected packages: sqlparse, pytz, django
Successfully installed django-2.2 pytz-2019.2 sqlparse-0.3.0
...
Step 8/8 : ADD . /code/
 ---> cb23f483ffb6
Successfully built cb23f483ffb6
Successfully tagged django_app_app:latest
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`.
Creating django_app_app_1 ... done
Attaching to django_app_app_1
app_1 | Watching for file changes with StatReloader
app_1 | Performing system checks...
app_1 | 
app_1 | System check identified no issues (0 silenced).
app_1 | October 05, 2019 - 15:03:15
app_1 | Django version 2.2, using settings 'django_app.settings'
app_1 | Starting development server at http://0.0.0.0:8000/
app_1 | Quit the server with CONTROL-C.

可以看到 Docker 按照配置文件的要求,成功构建了镜像及容器,并启动了容器。

打开浏览器,输入本地 IP 端口 127.0.0.1:8000 :

Django Docker容器化部署之Django-Docker本地部署 

看到 Django 的小火箭,项目成功运行起来啦。按 Ctrl + C 即可停止开发服务器运行。

停止服务器后实际上容器还存在,只是停止运行了而已。输入:

$ docker-compose down

就可以删除容器。

如果想在后台运行容器,则输入:

$ docker-compose up -d

另外,如果你需要重新构建镜像,则输入命令:

$ docker-compose build

启动和停止已有的容器:

$ docker-compose start
$ docker-compose stop

很简单吧。

下载太慢?

由于众所周知的原因,国内的网络环境非常复杂。在构建镜像时经常需要从国外的远程仓库拉取资源,岿然不动的下载速度真的头疼。

解决方法就是将资源拉取链接修改为国内的镜像源,比如清华的镜像源。

修改 Dockerfile 如下:

FROM python:3.7
ENV PYTHONUNBUFFERED 1

# 添加 Debian 清华镜像源
RUN echo \
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-free\
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-free\
deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-free\
deb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free\
  > /etc/apt/sources.list

RUN mkdir /code
WORKDIR /code
# 添加 pip 清华镜像源
RUN pip install pip -U -i https://pypi.tuna.tsinghua.edu.cn/simple
ADD requirements.txt /code/
# 添加 pip 清华镜像源
RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
ADD . /code/

重新构建镜像,下载速度就飞一样快了。

教程后面的内容都使用了清华源,但为了方便阅读,把这一部分更换源的代码省去了,读者心里明白就好。

总结

本章初步感受了 Docker 的工作流程,并且很轻松的构建了一个容器化的 Django 项目。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
Python交换变量
Sep 06 Python
Python的Socket编程过程中实现UDP端口复用的实例分享
Mar 19 Python
Python将多个excel文件合并为一个文件
Jan 03 Python
wxpython实现图书管理系统
Mar 12 Python
python将每个单词按空格分开并保存到文件中
Mar 19 Python
python 与服务器的共享文件夹交互方法
Dec 27 Python
python 根据时间来生成唯一的字符串方法
Jan 14 Python
详解利用python+opencv识别图片中的圆形(霍夫变换)
Jul 01 Python
Django项目创建到启动详解(最全最详细)
Sep 07 Python
Python代码块及缓存机制原理详解
Dec 13 Python
Python 3.8 新功能大揭秘【新手必学】
Feb 05 Python
使用python将HTML转换为PDF pdfkit包(wkhtmltopdf) 的使用方法
Apr 21 Python
Python3实现zip分卷压缩过程解析
Oct 09 #Python
基于Python新建用户并产生随机密码过程解析
Oct 08 #Python
Python小程序 控制鼠标循环点击代码实例
Oct 08 #Python
Python3 无重复字符的最长子串的实现
Oct 08 #Python
解决python 读取excel时 日期变成数字并加.0的问题
Oct 08 #Python
python3.7 openpyxl 删除指定一列或者一行的代码
Oct 08 #Python
python实现的按要求生成手机号功能示例
Oct 08 #Python
You might like
PHP5与MySQL数据库操作常用代码 收集
2010/03/21 PHP
php数据库备份还原类分享
2014/03/20 PHP
ThinkPHP添加更新标签的方法
2014/12/05 PHP
屏蔽PHP默认设置中的Notice警告的方法
2016/05/20 PHP
ThinkPHP中create()方法自动验证表单信息
2017/04/28 PHP
Riot.js 快速的JavaScript单元测试框架
2009/11/09 Javascript
简单的jquery拖拽排序效果实现代码
2011/09/20 Javascript
JSON辅助格式化处理方法
2013/03/26 Javascript
你必须知道的Javascript知识点之"字面量和对应类型"说明介绍
2013/04/23 Javascript
ScrollDown的基本操作示例
2013/06/09 Javascript
jquery map方法使用示例
2014/04/23 Javascript
javascript实现简单计算器效果【推荐】
2016/04/19 Javascript
全国省市二级联动下拉菜单 js版
2016/05/10 Javascript
如何提高数据访问速度
2016/12/26 Javascript
性能优化之代码优化页面加载速度
2017/03/01 Javascript
VUE + UEditor 单图片跨域上传功能的实现方法
2018/02/08 Javascript
vue组件开发props验证的实现
2019/02/12 Javascript
Vue中点击active并第一个默认选中功能的实现
2020/02/24 Javascript
详解Vue中的MVVM原理和实现方法
2020/07/15 Javascript
在Python的Flask框架下使用sqlalchemy库的简单教程
2015/04/09 Python
关于numpy中np.nonzero()函数用法的详解
2017/02/07 Python
在python中将list分段并保存为array类型的方法
2019/07/15 Python
python通过txt文件批量安装依赖包的实现步骤
2019/08/13 Python
用Python调用win命令行提高工作效率的实例
2019/08/14 Python
Python 网络编程之TCP客户端/服务端功能示例【基于socket套接字】
2019/10/12 Python
python循环输出三角形图案的例子
2019/11/22 Python
keras用auc做metrics以及早停实例
2020/07/02 Python
python爬虫中抓取指数的实例讲解
2020/12/01 Python
POS解决方案:MUNBYN(热敏打印机、条形码扫描仪)
2020/06/09 全球购物
为什么如下的代码int a=100,b=100;long int c=a * b;不能工作
2013/11/29 面试题
opencv实现图像平移效果
2021/03/24 Python
暑期研修感言
2014/02/17 职场文书
老师对学生的评语
2014/04/18 职场文书
物流专业自荐信
2014/05/23 职场文书
班子成员四风问题自我剖析材料
2014/09/29 职场文书
windows安装 redis 6.2.6最新步骤详解
2022/04/26 Redis