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实现全角半角字符互转的方法
Nov 28 Python
Python通过future处理并发问题
Oct 17 Python
python交互式图形编程实例(二)
Nov 17 Python
Python matplotlib绘图可视化知识点整理(小结)
Mar 16 Python
python实现自动网页截图并裁剪图片
Jul 30 Python
Python mutiprocessing多线程池pool操作示例
Jan 30 Python
关于sys.stdout和print的区别详解
Dec 05 Python
Python文件时间操作步骤代码详解
Apr 13 Python
python openCV自制绘画板
Oct 27 Python
利用Python pandas对Excel进行合并的方法示例
Nov 04 Python
PyQt5中QSpinBox计数器的实现
Jan 18 Python
解决python存数据库速度太慢的问题
Apr 23 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
PHP模拟QQ登录的方法
2015/07/29 PHP
PHP实现搜索地理位置及计算两点地理位置间距离的实例
2016/01/08 PHP
CI框架表单验证实例详解
2016/11/21 PHP
PHP微信发送推送消息乱码的解决方法
2019/02/28 PHP
浅谈PHP封装CURL
2019/03/06 PHP
PHP与SQL语句写一句话木马总结
2019/10/11 PHP
Gird事件机制初级读本
2007/03/10 Javascript
一个cssQuery对象 javascript脚本实现代码
2009/07/21 Javascript
Jquery Select操作方法集合脚本之家特别版
2010/05/17 Javascript
jQuery 1.9.1源码分析系列(十五)动画处理之缓动动画核心Tween
2015/12/03 Javascript
APP中javascript+css3实现下拉刷新效果
2016/01/27 Javascript
批量下载对路网图片并生成html的实现方法
2016/06/07 Javascript
JSONP跨域请求实例详解
2016/07/04 Javascript
Vue实现web分页组件详解
2017/11/28 Javascript
layui从数据库中获取复选框的值并默认选中方法
2018/08/15 Javascript
浅谈React Event实现原理
2018/09/20 Javascript
VUE2.0+ElementUI2.0表格el-table循环动态列渲染的写法详解
2018/11/30 Javascript
详解vue-cli3多页应用改造
2019/06/04 Javascript
vue服务端渲染操作简单入门实例分析
2019/08/28 Javascript
python读取Android permission文件
2013/11/01 Python
详解python列表(list)的使用技巧及高级操作
2019/08/15 Python
Python进程,多进程,获取进程id,给子进程传递参数操作示例
2019/10/11 Python
pandas按行按列遍历Dataframe的几种方式
2019/10/23 Python
python常用数据重复项处理方法
2019/11/22 Python
Pycharm生成可执行文件.exe的实现方法
2020/06/02 Python
Python实现定时监测网站运行状态的示例代码
2020/09/30 Python
html5 postMessage前端跨域并前端监听的方法示例
2018/11/01 HTML / CSS
班组长安全生产职责
2013/12/16 职场文书
十岁生日同学答谢词
2014/01/19 职场文书
ktv筹备计划书
2014/05/03 职场文书
小学生志愿者活动方案
2014/08/23 职场文书
乡镇团委工作总结2015
2015/05/26 职场文书
JS ES6异步解决方案
2021/04/29 Javascript
CSS 伪元素::marker详解
2021/06/26 HTML / CSS
bootstrapv4轮播图去除两侧阴影及线框的方法
2022/02/15 HTML / CSS
Windows Server 2022 超融合部署(图文教程)
2022/06/25 Servers