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 拷贝对象(深拷贝deepcopy与浅拷贝copy)
Sep 06 Python
python字符串排序方法
Aug 29 Python
跟老齐学Python之list和str比较
Sep 20 Python
Python中str is not callable问题详解及解决办法
Feb 10 Python
使用Python爬了4400条淘宝商品数据,竟发现了这些“潜规则”
Mar 23 Python
python实现人民币大写转换
Jun 20 Python
给你一面国旗 教你用python画中国国旗
Sep 24 Python
Pytorch学习之torch用法----比较操作(Comparison Ops)
Jun 28 Python
Python 捕获代码中所有异常的方法
Aug 03 Python
python opencv实现直线检测并测出倾斜角度(附源码+注释)
Dec 31 Python
Python就将所有的英文单词首字母变成大写
Feb 12 Python
关于Numpy之repeat、tile的用法总结
Jun 02 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下SSL加密解密、验证、签名方法(很简单)
2020/06/28 PHP
PHP实现可自定义样式的分页类
2016/03/29 PHP
Ubuntu VPS中wordpress网站打开时提示”建立数据库连接错误”的解决办法
2016/11/03 PHP
thinkPHP通用控制器实现方法示例
2017/11/23 PHP
php实现微信支付之现金红包
2018/05/30 PHP
JavaScript 高级语法介绍
2009/06/15 Javascript
JQuery分别取得每行最后一列和最后一行的示例代码
2013/08/18 Javascript
jquery购物车实时结算特效实现思路
2013/09/23 Javascript
javascript制作的cookie封装及使用指南
2015/01/02 Javascript
js鼠标滑过图片震动特效的方法
2015/02/17 Javascript
创建自己的jquery表格插件
2015/11/25 Javascript
JS实现数字格式千分位相互转换方法
2016/08/01 Javascript
如何使用vuejs实现更好的Form validation?
2017/04/07 Javascript
微信小程序联网请求的轮播图
2017/07/07 Javascript
微信小程序五子棋游戏AI实现方法【附demo源码下载】
2019/02/20 Javascript
记录vue项目中遇到的一点小问题
2019/05/14 Javascript
使用axios请求接口,几种content-type的区别详解
2019/10/29 Javascript
在Python的Django框架中使用通用视图的方法
2015/07/21 Python
浅谈python中的__init__、__new__和__call__方法
2017/07/18 Python
Anaconda2下实现Python2.7和Python3.5的共存方法
2018/06/11 Python
基于python traceback实现异常的获取与处理
2019/12/13 Python
python GUI库图形界面开发之PyQt5复选框控件QCheckBox详细使用方法与实例
2020/02/28 Python
新加坡最受追捧的体验平台:Hapz
2018/01/01 全球购物
The North Face北面德国官网:美国著名户外品牌
2018/12/12 全球购物
法院实习人员自我鉴定
2013/09/26 职场文书
五型班组建设方案
2014/02/10 职场文书
恐龙的灭绝教学反思
2014/02/12 职场文书
招股说明书范本
2014/05/06 职场文书
国庆65周年演讲稿:回首往昔,展望未来
2014/09/21 职场文书
工作业绩不及格检讨书
2014/10/28 职场文书
竞聘报告优秀范文
2014/11/06 职场文书
春季运动会开幕词
2015/01/28 职场文书
捐款通知怎么写
2015/04/24 职场文书
Java中使用Filter过滤器的方法
2021/06/28 Java/Android
【2·13】一图读懂中国无线电发展
2022/02/18 无线电
Android 中的类文件和类加载器详情
2022/06/05 Java/Android