.. _docker_compose_django: ======================== 在Docker环境运行Django ======================== .. note:: 本文实践采用 :ref:`docker_compose` 完成设置,运行一个简单的Django/MySQK应用。 请参考 :ref:`django_env_linux` 完成设置,并确保能够 :ref:`run_django` 。 在Docker环境中运行Django,需要创建Dockerfile,Python依赖文件,以及 ``docker-compose.yml`` 文件。 定义项目组件 ============== 在项目目录下,需要创建一个 ``Dockerfie`` ,定义了应用程序的镜像名以及更多的build命令来定制镜像。一旦构建之后,就可以在容器中运行镜像。 - ``Dockerfile`` 内容如下: .. literalinclude:: ./Dockerfile :language: bash :linenos: 这里的 ``Dockerfile`` 开始部分使用了 `Python 3 parent image `_ ,这个父镜像通过添加 ``code`` 目录,然后安装通过 ``requirements.txt`` 文件定义的Python运行依赖。 - 在项目目录下创建 ``requirements.txt`` ,例如,我的app项目内容如下: .. literalinclude:: ./requirements.txt :language: bash :linenos: 这个Python运行依赖定义文件是通过 ``RUN pip install -r requirements.txt`` 执行的。 - 创建 ``docker-compose.yml`` 配置 ``docker-compose.yml`` 文件描述应用服务,这里是web服务和数据库服务。compose文件描述了服务使用的Docker镜像,连接方式,以及在容器中挂载的卷。最后 ``docker-compose.yml`` 文件还描述了输出服务的端口。 ``docker-compose.yml`` : .. literalinclude:: ./docker-compose.yml :language: yaml :linenos: .. note:: 后文我详细解释配置项用途。 创建Django项目 ======================= 通过上述 ``docker-compose.yml`` 定义的image以及服务依赖关系,我们现在就可以创建Django项目( 通过 ``docker-compose run`` 命令可以在容器中执行命令) :: docker-compose run web django-admin startproject myapp . 上述指令在容器中运行了 ``django-admin startproject myapp`` ,并且使用了web服务镜像和配置。初次运行时,web镜像还不存在。则Compose就会在当前目录下构建,因为这里在 ``docker-compose.yml`` 中指定了 ``build: .`` 。 .. note:: 由于我已经 :ref:`django_env_linux` 完成设置,并确保能够 :ref:`run_django` ,所以我在这里执行的命令是:: docker-compose run web 这样就跳过了 ``django-admin startporject myapp .`` 初始化Django项目,直接使用之前已经创建好的Django环境。 运行提示:: WARNING: You are using pip version 19.3.1; however, version 20.0.1 is available. You should consider upgrading via the 'pip install --upgrade pip' command. Successfully built adb7f7c47877 Successfully tagged onesre_web:latest WARNING: Image for service web was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). January 22, 2020 - 08:34:52 Django version 3.0.2, using settings 'onesre.settings' Starting development server at http://0.0.0.0:8000/ Quit the server with CONTROL-C. 注意,此时还不能打开 http://0.0.0.0:8000/ 。但是,通过 ``docker exec -it /bin/sh`` 登陆到容器内部,检查 ``ps aux | grep pyton`` 可以看到服务进程:: root 1 0.0 1.7 43568 35648 pts/0 Ss+ 13:18 0:00 python manage.py runserver 0.0.0.0:8000 root 9 4.4 1.8 118980 38520 pts/0 Sl+ 13:18 0:50 /usr/local/bin/python manage.py runserver 0.0.0.0:8000 这说明容器内部服务已经正常启动,但是docker没有实现port map。仔细看了 ``docker ps`` ,可以看到仅仅是启动了 ``myapp_web`` 容器。所以,此时没有启动db情况下,还没有做docker的端口映射。 MySQL容器化运行 ================== .. note:: 参考 `dockerhub MySQL 说明 `_ 可以看到,官方mysql镜像支持通过环境变量传递初始化密码和数据库名。 - ``docker-compose.yml`` 的以下高亮黄色的两行是读取环境 ``db.env`` 配置: .. literalinclude:: ./docker-compose.yml :language: yaml :emphasize-lines: 8-9 :linenos: .. note:: 虽然 docker compose 支持直接在 ``docker-compose.yml`` 中直接配置环境变量,但是通常会把 ``docker-compose.yml`` 文件提交到软件仓库,这样存在安全隐患。 所以,我们采用 ``env_file`` 方式引用一个不会添加到git仓库的文件,例如 ``db.env`` ,内容包含如下: .. literalinclude:: ./db.env :language: bash :linenos: 然后,在 ``.gitignore`` 中添加一行内容 ``db.env`` 避免该文件被提交到git仓库,以保证安全。 Compose 环境变量请参考 `Environment variables in Compose `_ - 启动数据库:: docker-compose run db 此时会看到mysql数据库按照环境配置启动并初始化。完成后请执行:: docker exec -it /bin/bash 进入数据库服务器,然后执行:: mysql mydb -umyapp_user -pmyapp_passwd 验证数据库连接和运行,并且可以执行简单的查询。 数据库连接 =========== - 默认Django数据库连接是本地sqlite,通过配置项目目录下 ``myapp/settings.py`` 文件的 ``DATABASES = ...`` 部分来连接数据库 Django密码安全secrets.json ------------------------------ 由于Django项目的 ``settings.py`` 也同样提交的git仓库,则为了避免风险,需要将数据库配置部分分离到独立配置文件,并且这个数据库账号配置文件不可提交到git仓库。 参考 `Django, Security and Settings `_ ,有两种方法实现Django敏感数据: 环境变量设置数据库密码 ~~~~~~~~~~~~~~~~~~~~~~~~ - 如果你没有使用docker来运行django,则通常可以在 ``~/.bash_profile`` 中设置环境变量,这样启动Django也是能获得数据库密码账号:: export MYSQL_PASSWORD=myapp_passwd - 修订 ``settings.py`` 导入配置:: ... DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': os.getenv('MYSQL_DATABASE'), 'USER': os.getenv('MYSQL_USER'), 'PASSWORD': os.getenv('MYSQL_PASSWORD'), 'HOST': 'db', 'PORT': '3306', } } 这样启动后Django就可以从环境变量中获取账号密码。 .. note:: `How do I pass environment variables to Docker containers? `_ 介绍了多种方法传递环境变量到Docker容器内部。在docker docs文档中提供案例: `Set environment variables (-e, --env, --env-file) `_ docker run -e MYVAR1 --env MYVAR2=foo --env-file ./env.list ubuntu bash 这里的案例,你可以设置一个 ``DB_PASSWORD=myapp_passwd`` 环境变量,然后执行docker 同样,参考前面针对 ``db`` 服务的环境变量设置, ``docker-compose`` 也支持读取环境变量,我们可以创建一个 ``web.env`` 保存Django的数据库环境变量(实际上就是 ``db.env`` 配置的部分内容)。为了能够简化配置,我们可以复用 ``db`` 服务的环境配置(所以这里就用了相同的环境变量名)。 启动服务 =========== 集中上述配置,现在我们可以启动 ``docker-compose.yml`` 配置的 db 和 web 服务:: docker-compose up 此时通过浏览器访问 http://127.0.0.1:8000/ 可以看到Django欢迎页面: .. figure:: ../../_static/docker/applications/docker_compose_django.png :scale: 75 注意,这里控制台提示信息:: web_1 | You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions. web_1 | Run 'python manage.py migrate' to apply them. 这说明我们遗漏了一步数据库迁移,所以我们先停止掉 db 和 web 容器,然后执行一次django数据库迁移:: docker-compose run web python manage.py migrate 这个步骤会自动启动数据库,然后执行django迁移,完成后再次启动 ``docker-compose up`` 就可以正常使用。 - 停止应用的方法可以通过在控制台按下 ``Ctrl-C`` 来停止容器,另外也有一种比较优雅的方法,就是使用 ``docker-compose`` 命令,即启动另外一个shell窗口,还是在这个目录下执行:: docker-compose down .. _docker_django_quickstart: 快速实现Docker运行Djgnao =========================== 上述的部署过程比较曲折,以下为总结,可以快速完成 django + mysql 部署。 - ``Dockerfile`` : .. literalinclude:: ./Dockerfile :language: bash :linenos: - ``requirements.txt`` (指示pip安装) : .. literalinclude:: ./requirements.txt :language: bash :linenos: - ``docker-compose.yml`` (构建容器关系,和数据库相关账号数据通过 ``db.env`` 引入): .. literalinclude:: ./docker-compose.yml :language: yaml :linenos: - ``db.env`` 配置db和web使用的账号和数据库配置: .. literalinclude:: ./db.env :language: bash :linenos: - 启动并初始化数据库:: docker-compose run db - 数据库初始化完成后,开启另外一个shell,在当前目录下停止容器:: docker-compose down - 初始化Django项目(在当前目录执行创建myapp应用):: docker-compose run web django-admin startproject myapp . .. note:: 这个步骤将启动django进行项目初始化,并提示:: Successfully built 6ab89519f3bd Successfully tagged myapp_web:latest # 这里标签是根据当前目录 myapp 加上 _web - 修改 Django 配置 ``myapp/settings.py`` :: ... DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': os.getenv('MYSQL_DATABASE'), 'USER': os.getenv('MYSQL_USER'), 'PASSWORD': os.getenv('MYSQL_PASSWORD'), 'HOST': 'db', 'PORT': '3306', } } - 执行django数据库迁移(该命令会依次启动db,然后再启动web服务进行django数据库初始化:: docker-compose run web python manage.py migrate - 可以停止数据库再次启动验证:: docker-compose down docker-compose up 一切正常情况下,使用浏览器就可以访问Django最初的欢迎页面,后续进行开发 - 当代码迭代开发,则执行:: docker-compose build - 然后再次启动:: docker-compose up 也可以将上述两个命令结合成 ``docker-comose up --build`` .. note:: 目前发现这个部署还是有一点问题,mysql数据库初始化( ``docker-compose build`` )较慢,导致web启动后连接数据库失败。不过第二次启动,MySQL无需初始化则启动迅速,则web正常工作。 参考 ======= - `Quickstart: Compose and Django `_