如何使用 Docker 构建 Django 和 Gunicorn 应用程序

介绍

[Django] (https://www.djangoproject.com/)是一个强大的网络框架,可以帮助您快速地将您的Python应用程序从地上取出. 它包括几个方便的功能,如对象-关系映射器,用户认证,以及您应用程序的自定义的行政界面. 它还包括一个搜索框架,并通过其URL调度机Template system鼓励清洁应用设计.

在此教程中, 您将学习如何用 Docker 容器构建一个可伸缩且可移植的 Django [Polls] (https://docs.djangoproject.com/en/2.2/intro/tutorial01/) 应用程序 。 在盒子外,一个Django app需要几次修改,才能有效地在容器内部运行,比如记录到标准输出流,并通过传递到容器的环境变量来配置自己. 此外,将JavaScript和CSS样式表等静态资产卸下,用于对象存储,可以使您在多容器环境中精简并集中管理这些文件.

您将将这些更改应用于一个样本 Django Polls应用程序中,以 Twelve-Factor方法为灵感,然后,您将构建应用程序图像并使用 Docker 运行容器化应用程序。

到本教程结束时,您将已在 如何设置可扩展的Django应用程序中集装设置。在本系列的后续教程中,您将学习如何使用 Docker Compose将Django容器与Nginx反向代理程序并部署到Kubernetes集群中。

强烈建议您通过教程来了解您正在对应用程序进行的更改,但如果您想跳过,您可以从GitHub存储库中获取修改的代码(https://github.com/do-community/django-polls/tree/polls-docker)。

前提条件

要遵循这个教程,你需要:

第1步:创建PostgreSQL数据库和用户

首先,我们将从 Ubuntu 实例连接到 PostgreSQL 服务器,然后为 Django 应用程序创建一个 PostgreSQL 数据库和用户,并配置数据库以便与 Django 有效地工作。

在我们从 Ubuntu 机器(而不是应用容器)连接到数据库之前,我们需要从 Ubuntu 存储库中安装postgresql-client包。

1sudo apt update
2sudo apt install postgresql-client

当被提示开始下载和安装软件包时,点击Y,然后点击ENTER

现在你已经安装了客户端,我们将使用它为我们的Django应用程序创建一个数据库和数据库用户。

首先,请从 Cloud 控制面板,获取您的集群的 连接参数 ,然后点击您的数据库。您应该看到一个** 连接细节** 框,其中包含您的集群的一些** 连接参数** 。 记住这些。

回到命令行上,使用这些凭据和我们刚刚安装的psql PostgreSQL 客户端登录您的集群:

1psql -U username -h host -p port -d database --set=sslmode=require

当被提示时,输入显示在Postgres用户名旁边的密码,然后点击ENTER

您将收到一个PostgreSQL提示,您可以管理数据库。

首先,为您的项目创建一个名为调查的数据库:

1CREATE DATABASE polls;

注意:每一个 Postgres 语句都必须以半字符号结束,因此,如果您遇到问题,请确保您的命令以一个结束。

我们现在可以切换到调查数据库:

1\c polls;

接下来,为项目创建一个数据库用户. 请确保选择安全密码:

1CREATE USER sammy WITH PASSWORD 'password';

现在我们将对我们刚刚创建的用户修改一些连接参数,这将加快数据库操作,以便每次建立连接时不需要查询和设置正确的值。

我们正在将默认编码设置为)。

在 PostgreSQL 提示中输入以下命令:

1ALTER ROLE sammy SET client_encoding TO 'utf8';
2ALTER ROLE sammy SET default_transaction_isolation TO 'read committed';
3ALTER ROLE sammy SET timezone TO 'UTC';

现在我们可以为我们的新用户提供访问权限来管理我们的新数据库:

1GRANT ALL PRIVILEGES ON DATABASE polls TO sammy;

完成后,通过键入退出 PostgreSQL 提示:

1\q

正确配置的Django应用程序现在可以连接和管理该数据库,在下一步,我们将从GitHub克隆Polls应用程序代码,并明确定义其Python包依赖性。

步骤 2 - 克隆应用程序存储库和声明依赖

要开始包装我们的 Django 调查应用程序的过程,我们首先将克隆 django-polls存储库,其中包含了 Django项目的 教程调查应用程序的完整代码。

登录您的服务器,创建一个名为polls-project的目录,并使用git来克隆来自GitHub的django-polls复制:

1mkdir polls-project
2cd polls-project
3git clone https://github.com/do-community/django-polls.git

进入django-polls目录并列出存储库内容:

1cd django-polls
2ls
1[secondary_label Output]
2LICENSE README.md manage.py mysite polls templates

你应该看到以下对象:

  • manage.py: 用于操纵应用程序的主要命令行实用程序. * polls: 包含 polls 应用程序代码. * mysite: 包含 Django 项目范围代码和设置。

有关项目结构和文件的更多信息,请参阅创建项目从官方 Django 文档。

在此目录中,我们还将创建一个名为requirements.txt的文件,该文件将包含 Django 应用程序的 Python 依赖。

在您选择的编辑器中打开名为requirements.txt的文件,并粘贴到以下Python依赖:

 1[label polls-project/django-polls/requirements.txt]
 2boto3==1.9.252
 3botocore==1.12.252
 4Django==2.2.6
 5django-storages==1.7.2
 6docutils==0.15.2
 7gunicorn==19.9.0
 8jmespath==0.9.4
 9psycopg2==2.8.3
10python-dateutil==2.8.0
11pytz==2019.3
12s3transfer==0.2.1
13six==1.12.0
14sqlparse==0.3.0
15urllib3==1.25.6

在这里,我们安装了 Django,用于卸载静态资产到对象存储的django-storages插件,gunicorn WSGI 服务器,psycopg2 PostgreSQL 适配器以及一些额外的依赖包。

保存并关闭文件。

现在我们已经克隆了该应用程序并定义了它的依赖性,我们可以继续修改它以便可移植性。

第3步:通过环境变量配置Django

十二因素應用程式方法的最重要的建議之一是從您的應用程式的代碼庫中提取硬碼配置,這可以讓您通過修改環境變量來輕鬆改變您的應用程式在執行時間的行為。Docker 和 Kubernetes 都建議這個方法來設定容器,所以我們將調整我們的應用程式的設定檔案以使用這個模式。

我们的 Django 项目的主要设置文件(‘django-polls/mysite/settings.py’)是一个 Python 模块,它使用原生数据结构来配置应用程序. 默认情况下,该文件中的大多数值都是硬编码的,这意味着您必须编辑配置文件以改变应用程序的行为。

要做到这一点,我们将通过settings.py来代替我们想要在运行时设置的每个变量的硬代码值,通过调用os.getenv来。

这使我们能够设置这样的变量:

1[label polls-project/django-polls/mysite/settings.py]
2. . .
3SECRET_KEY = os.getenv('DJANGO_SECRET_KEY')
4. . .
5DEBUG = os.getenv('DJANGO_DEBUG', False)
6. . .

对于SECRET_KEY,Django 将寻找一个名为DJANGO_SECRET_KEY的环境变量. 因为这不应该是硬编码的,而且需要在我们的应用程序服务器上相同,我们将想要在外部设置,没有反馈值。

对于DEBUG,Django 将寻找一个名为DJANGO_DEBUG的环境变量,然而,这次我们提供了默认值,如果变量未设置,则将其用作倒退值。

要应用此技术,请在您所选择的编辑器中打开polls-project/django-polls/mysite/settings.py文件,并通过它来外包以下变量以提供的默认值:

  • SECRET_KEY = os.getenv('DJANGO_SECRET_KEY') * DEBUG = os.getenv('DEBUG', False) * ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '127.0.0.1').split(',')

对于ALLOWED_HOSTS,我们收集了DJANGO_ALLOWED_HOSTS环境变量,并将其分成一个Python列表,使用,作为分离器。

一旦您已更改上述变量,请导航到DATABASES变量并配置如下:

 1[label polls-project/django-polls/mysite/settings.py]
 2. . . 
 3# Database
 4# https://docs.djangoproject.com/en/2.1/ref/settings/#databases
 5
 6DATABASES = {
 7     'default': {
 8         'ENGINE': 'django.db.backends.{}'.format(
 9             os.getenv('DATABASE_ENGINE', 'sqlite3')
10         ),
11         'NAME': os.getenv('DATABASE_NAME', 'polls'),
12         'USER': os.getenv('DATABASE_USERNAME', 'myprojectuser'),
13         'PASSWORD': os.getenv('DATABASE_PASSWORD', 'password'),
14         'HOST': os.getenv('DATABASE_HOST', '127.0.0.1'),
15         'PORT': os.getenv('DATABASE_PORT', 5432),
16         'OPTIONS': json.loads(
17             os.getenv('DATABASE_OPTIONS', '{}')
18         ),
19     }
20 }
21 . . .

这将使用环境变量设置默认数据库参数。

对于DATABAS['default']['OPTIONS']',我们使用json.loads'将通过`DATABASE_OPTIONS'环境变量的JSON物体去区域化。 大多数时候,将环境变量解释为简单的字符串,使得翻译到Django设置更容易被读取. 然而,在这种情况下,能够通过任意的数据结构是有价值的。 每个数据库引擎都有一套独特的有效选项,因此能够用适当的参数来编码一个JSON对象,在牺牲一些可识别性的情况下,给我们带来更大的灵活性.

要使用json库,请在settings.py的顶部导入:

 1[label polls-project/django-polls/mysite/settings.py]
 2"""
 3Django settings for mysite project.
 4
 5Generated by 'django-admin startproject' using Django 2.1.
 6
 7For more information on this file, see
 8https://docs.djangoproject.com/en/2.1/topics/settings/
 9
10For the full list of settings and their values, see
11https://docs.djangoproject.com/en/2.1/ref/settings/
12"""
13
14import os
15import json
16. . .

另一个需要特别注意的区域是)()。对于大多数数据库引擎,这是关系数据库管理系统中的数据库名称。

由于 settings.py 文件是 Python 代码,所以您可以通过多种不同的方式来处理来自环境的读取值。

在此步骤中,我们将以通用和便携的方式配置 Django 设置的主要变量,包括数据库参数。在下一步,我们将继续配置静态文件的设置,如 JavaScript 和 CSS 样式表,我们将集中并卸载到 S3 兼容的对象存储服务。

步骤4 - 卸载静态资产

在一个生产环境中运行多个Django集装箱时,在整个运行的集装箱车队中维护特定版本的静态资产和文件可能很麻烦. 为了精简这个架构,我们可以卸下所有共享元素,并声明到外部存储. 我们不是试图在复制品之间保持这些物品的同步,或实施备份和加载例行程序以确保数据在当地可获得,而是可以将这些资产作为网络无障碍服务加以使用.

在最后一步中,我们配置了Django,以便我们可以通过环境变量传输数据库连接参数。

[django-storages] (https://github.com/jschneier/django-storages)包提供远程存储后端(包括S3相容对象存储),Django可以用来卸载文件. 我们将配置Polls应用程序,以使用`django-storages'将静态文件上传到数字海洋空间,这在)步骤7中已有概述。 在本指南中,我们将使用"数字海洋"空间,但您可以使用任何S3相容的天体存储提供商.

首先,我们将对我们在之前的步骤中修改的相同的django-polls/mysite/settings.py文件进行一些修改。

开始打开mysite/settings.py文件来编辑并将存储应用程序附加到Django的INSTALLED_APPS列表中:

1[label polls-project/django-polls/mysite/settings.py]
2. . .
3INSTALLED_APPS = [
4    . . .
5    'django.contrib.staticfiles',
6    'storages',
7]
8. . .

存储应用程序通过django-storages在我们在步骤 1 中定义的requirements.txt文件中安装。

现在,在文件底部找到STATIC_URL变量,并用以下块取代它:

 1[label polls-project/django-polls/mysite/settings.py]
 2. . .
 3
 4# Static files (CSS, JavaScript, Images)
 5# https://docs.djangoproject.com/en/2.1/howto/static-files/
 6
 7# Moving static assets to DigitalOcean Spaces as per:
 8# https://andsky.com/tech/tutorials/how-to-set-up-object-storage-with-django
 9AWS_ACCESS_KEY_ID = os.getenv('STATIC_ACCESS_KEY_ID')
10AWS_SECRET_ACCESS_KEY = os.getenv('STATIC_SECRET_KEY')
11
12AWS_STORAGE_BUCKET_NAME = os.getenv('STATIC_BUCKET_NAME')
13AWS_S3_ENDPOINT_URL = os.getenv('STATIC_ENDPOINT_URL')
14AWS_S3_OBJECT_PARAMETERS = {
15    'CacheControl': 'max-age=86400',
16}
17AWS_LOCATION = 'static'
18AWS_DEFAULT_ACL = 'public-read'
19
20STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
21
22STATIC_URL = '{}/{}/'.format(AWS_S3_ENDPOINT_URL, AWS_LOCATION)
23STATIC_ROOT = 'static/'

我们硬编码以下配置变量:

  • 统计': 设置存储后端 Django 将用来卸载静态文件 。 这个S3Boto3Storage`后端应当与任何S3兼容后端,包括数字海洋空间相通。
  • `AWS-S3-OBJECT-PARAMETERs' 设置静态文件中的缓存控制头。
  • AWS-LOCATION':定义一个名为静态'的目录,在物体存储桶内放置所有静态文件。
  • "AWS_DEFAUT_ACL": 定义静态文件的访问控制列表(ACL)。 将其设置为 " 公众阅读 " ,确保最终用户能够公开查阅这些文件。
  • `STATIC_URL':指定Django生成静态文件的URL时应当使用的基础URL. 在这里,我们结合了端点 URL 和静态文件子目录来构建静态文件的基础 URL.
  • `STATIC_ROOT':在复制静态文件到对象存储之前指定在何地收集静态文件。 .

为了保持灵活性和可移植性,我们设置了许多参数以在运行时使用环境变量进行配置,就像我们之前一样。

  • AWS_ACESS_KEY-ID': 由STIC_ACCESS_KEY_ID`环境变量设定。 数字海洋空间访问密钥标识符.
  • `AWS_SECRET-ACESS-KEY': 由"STIC_SECRET_KEY"设定. 数字海洋空间的秘密密钥.
  • `AWS_STORAGE_BUKET_NAME: 由 STIC_BKKET_NAME 设定 。 Django 将把资产上传到的对象存储桶 。
  • AWS-S3-ENDPOINT-URL': 由"STATIC_ENDPOINT_URL"设定. 访问对象存储服务的端点 URL. 就数字海洋空间而言,这将类似于https://nyc3.digital Oceanspaces.com',这取决于空间桶所在的区域。 .

当你完成对settings.py的更改时,保存并关闭文件。

从现在开始,当您运行manage.py collectstatic来组装项目的静态文件时,Django 会将这些文件上传到远程对象存储中。

此时,如果您正在使用数字海洋空间,您可以选择为您的空间启用一个CDN,这将通过将您的 Django 项目的静态文件夹入一个地理分布的边缘服务器网络来加速其发送. 您也可以选择为您的空间配置自定义子域 。 要进一步了解CDN,请咨询[使用CDN来加速静态内容的提供] (https://andsky.com/tech/tutorials/using-a-cdn-to-speed-up-static-content-delivery). 配置一个CDN超出了此教程的范围,但步骤与[如何用数字大洋管理数据库和空间设置可扩展的Django App(https://andsky.com/tech/tutorials/how-to-set-up-a-scalable-django-app-with-digitalocean-managed-databases-and-spaces# enabling-cdn-optional)的[Enabling CDN](https://andsky.com/tech/tutorials/how-to-set-up-a-scalable-django-app-with-digitalocean-managed-databases-and-spaces enabling-cdn-optional)部分中的步骤非常相近.

在下一步,我们将对settings.py进行最后一组更改,这将允许Django登录到STDOUT和STDERR,以便这些流可以由Docker Engine收集并使用Docker日志进行检查。

步骤5 - 配置日志

默认情况下,Django在运行开发的 HTTP 服务器时或当 DEBUG 选项设定为 True 时,将信息登录到标准输出和标准出错。 然而,当DEBUG ' 被设定为假'或使用不同的HTTP服务器时,Django使用不同的伐木机制,这两种服务器在生产环境中都可能是真的。 它没有将优先事项INFO ' 及以上的所有信息记录到标准流,而是向行政电子邮件账户发送优先事项ERROR'或`TRITICAL ' 的信息.

这在很多情况下都是有道理的,但是在Kubernetes和被容器化的环境中,强烈推荐将记录到标准输出和标准出错. 日志信息在节点文件系统中的集中目录中收集,并使用kubectldocker命令进行交互访问。 这个节点级汇总通过允许操作团队在每个节点上运行一个进程来监视并转发日志来方便日志采集. 为了利用这一架构,应用程序必须将其日志写入这些标准汇.

幸运的是,Django 中的登录使用了 Python 标准库中高度可配置的)来定义我们想要的输出和格式化。要了解更多关于此技术和其他用于配置 Django 登录的信息,请参阅 Django 登录,正确的方式

再次,在您的编辑器中打开django-polls/mysite/settings.py

我们将首先在文件顶部添加一个额外的导入声明,以便我们可以操纵日志配置:

1[label polls-project/django-polls/mysite/settings.py]
2import json
3import os
4import logging.config
5. . .

logging.config导入允许我们将Django的默认日志行为转换为dictConfig函数在新日志配置的字典中。

现在,导航到文件的底部,并粘贴下面的日志配置代码块:

 1[label polls-project/django-polls/mysite/settings.py]
 2. . .
 3# Logging Configuration
 4
 5# Clear prev config
 6LOGGING_CONFIG = None
 7
 8# Get loglevel from env
 9LOGLEVEL = os.getenv('DJANGO_LOGLEVEL', 'info').upper()
10
11logging.config.dictConfig({
12    'version': 1,
13    'disable_existing_loggers': False,
14    'formatters': {
15        'console': {
16            'format': '%(asctime)s %(levelname)s [%(name)s:%(lineno)s] %(module)s %(process)d %(thread)d %(message)s',
17        },
18    },
19    'handlers': {
20        'console': {
21            'class': 'logging.StreamHandler',
22            'formatter': 'console',
23        },
24    },
25    'loggers': {
26        '': {
27            'level': LOGLEVEL,
28            'handlers': ['console',],
29        },
30    },
31})

在这里,我们将LOGGING_CONFIG设置为None,以禁用 Django 提供的默认日志配置。

最后,我们使用dictConfig函数设置一个新的配置字典,使用logging.config模块。在字典中,我们使用formatters来定义文本格式,通过设置handlers来定义输出,并使用loggers来配置哪些消息应该传递给每个处理器。

这是一个相当小的配置,允许您使用一个名为)从官方 Django 文件中。

通过此配置,当我们集装应用程序时,Docker 会通过docker logs命令曝光这些日志,同样,Kubernetes 也会通过kubectl logs命令捕捉输出并曝光它。

这结束了我们对 Django Polls 应用程序的代码修改,在下一步,我们将通过写入应用程序的 Dockerfile 来开始容器化过程。

步骤 6 – 编写应用程序Dockerfile

在此步骤中,我们将定义将运行我们的Django应用程序的容器图像和将其服务的Gunicorn WSGI服务器。它涉及通过定义运行环境,安装应用程序及其依赖,并完成一些基本配置来构建容器图像的容器图像。

选择合适的父母形象

在建造容器图像时,你必须作出的第一个重大决定是从中建立的基础。 集装箱图像可以来自SCRATCH,表示一个空文件系统,也可以取自现有的集装箱图像。 有许多不同的碱性容器图像可供使用,每个都定义一个文件系统并提供一套独特的预设包. 基于Vanilla Linux发行的图像如Ubuntu 18.04提供了通用的操作环境,而更专业的图像往往包括通用的库和特定编程语言的工具.

只要可能,通常可以使用来自 Docker 的官方存储库的图像作为基础,这些图像已被 Docker 验证以遵循最佳实践,并定期更新安全修复和改进。

由于我们的应用程序是用Django构建的,一个具有标准Python环境的图像将提供一个坚实的基础,并包含我们开始所需的许多工具。Python的官方Docker存储库提供(广泛选择基于Python的图像)(https://hub.docker.com/_/python/),每个安装Python版本和一些常见的工具在操作系统的顶部。

虽然适当的功能水平取决于您的使用情况,但基于 Alpine Linux的图像往往是一个坚实的跳跃点。Alpine Linux为运行应用程序提供了强大但最小的操作环境。它的默认文件系统非常小,但包含一个完整的包管理系统,具有相当广泛的存储库,使添加功能简单。

<美元 > [注] 注: 您可能在 [Python图像标签列表] (https://hub.docker.com/_/python/)中注意到每个图像都有多个标签. 多克标记是可变的,维护者可以在未来将同一标记重新分配给不同的图像. 因此,许多维护者提供了具有不同程度的特异性的一组标记来允许不同的使用. 例如,"3-阿尔卑斯山"的标记用于指代最新的阿尔卑斯山版本上最新的可得到的Python 3版本,因此当Python或Alpine的新版本发布后,它会被重新分配到不同的图像. 要让图像构建更具决定力,最好为您想要使用的图像使用最具体的标记. < $ > (美元)

在本指南中,我们将使用标记为3.7.4-alpine3.10的Python图像作为我们的Django应用程序的母图像。

首先,导航django-polls目录。

1cd ..

然后,在您所选择的编辑器中打开名为Dockerfile的文件。

1[label polls-project/Dockerfile]
2FROM python:3.7.4-alpine3.10

这定义了我们正在构建的自定义 Docker 图像以运行我们的应用程序的起点。

添加指示来设置应用程序

一旦您选择了母图像,您可以开始添加指示来安装依赖性,复制我们的应用程序文件,并设置运行环境。

行后,粘贴下面的Dockerfile代码块:

 1[label polls-project/Dockerfile]
 2. . .
 3
 4ADD django-polls/requirements.txt /app/requirements.txt
 5
 6RUN set -ex \
 7    && apk add --no-cache --virtual .build-deps postgresql-dev build-base \
 8    && python -m venv /env \
 9    && /env/bin/pip install --upgrade pip \
10    && /env/bin/pip install --no-cache-dir -r /app/requirements.txt \
11    && runDeps="$(scanelf --needed --nobanner --recursive /env \
12        | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
13        | sort -u \
14        | xargs -r apk info --installed \
15        | sort -u)" \
16    && apk add --virtual rundeps $runDeps \
17    && apk del .build-deps
18
19ADD django-polls /app
20WORKDIR /app
21
22ENV VIRTUAL_ENV /env
23ENV PATH /env/bin:$PATH
24
25EXPOSE 8000

要了解更多关于构建生产准备的Django应用程序的Dockerfiles,请参阅(https://www.caktusgroup.com/blog/2017/03/14/production-ready-dockerfile-your-python-django-app/)。

第一个Docker将把requiredments.txt文件复制到/app/requiredments.txt,以便在图像的文件系统中提供我们的应用程序的依赖性。 我们将用它来安装所有 Python 软件包, 我们的应用程序需要它才能运行 。 我们复制依赖文件,作为我们代码库其它部分的单独步骤,以便多克可以缓存包含依赖文件的图像层. 每当 requiredments.txt 文件在建构之间没有变化时,Docker就可以重新使用缓存的地层而不是重建地层,加快进程.

接下来,我们有一个单一的RUN命令,执行一个长的命令列表,每个命令都链接在一起,使用Linux的&&操作员。

  • 使用 Alpine 的「apk」包管理器安装 PostgreSQL 开发文件和基本构建依赖性 * 创建虚拟环境 * 使用「pip」安装在「requirements.txt」中列出的 Python 依赖性 * 通过分析安装的 Python 包的要求来编写运行时所需的包列表 * 卸载任何不必要的构建依赖性

我们将命令链接在一起,而不是在单独的RUN步骤中执行,因为Docker构建图像层的方式。对于每个ADD,COPYRUN命令,Docker在现有文件系统上方创建一个新的图像层,执行命令,然后保存结果的层。

一旦一个项目被写入图像层,它不能在下一个层中删除以减少图像大小. 如果我们安装 build 依赖,但想要在应用程序设置后删除它们,我们需要在相同的说明中这样做以减少图像大小。

RUN指令之后,我们使用ADD来复制应用程序代码和WORKDIR来设置图像的工作目录到我们的代码目录。

然后,我们使用ENV命令来设置两个环境变量,这些变量将可用于从我们的图像中产生的容器中。第一个命令将VIRTUAL_ENV设置为/env,第二个命令会修改PATH变量以包括/env/bin目录。

最后,我们使用EXPOSE来通知Docker,容器将在运行时听到端口8000

在这一点上,Dockerfile几乎完成了,我们只需要定义默认命令,当我们使用图像启动容器时将运行。

定义默认命令

Docker 图像的默认命令决定当一个容器启动时会发生什么而没有明确提供执行命令。

当定义了ENTRYPOINT'和CMD'时,ENTRYPOINT'定义了由容器运行的可执行性,而CMD'代表了该命令的默认参数列表。 用户可以通过在命令行上附加替代参数来覆盖默认参数列表: ` docker run 。 在这种格式下,用户将无法轻易地将 " ENTRYPOINT " 命令推翻,因此, " NENTRYPOINT " 命令往往被设定为将设置环境并根据其收到的参数列表执行不同动作的脚本.

当单独使用时,ENTRYPOINT将配置容器的可执行,但不会定义默认参数列表. 如果只设置了CMD,则将被解释为默认命令和参数列表,可以在运行时被翻译。

在我们的图像中,我们希望容器通过)。

CMD命令可以用下列任何格式定义:

  • CMD [) * CMD [参数 1,参数 1,参数 2,...,参数 n]:执行格式 * CMD指令参数 1参数 2..参数 n :壳格式

第一种格式只列出参数,并与ENTRYPOINT'一并使用。 另外两种格式指定了命令及其参数,有一些关键差异. 推荐的执行格式直接执行命令,在引号列表中通过,不处理 shell 。 另一方面,贝壳格式将整个清单传递给sh-c'。 例如,如果在命令中需要替换一个环境变量的值,但一般认为其可预测性较低,则有必要这样做.

对于我们而言,我们的Dockerfile中的最终指示看起来是这样的:

1[label polls-project/Dockerfile]
2. . .
3CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi:application"]

默认情况下,使用此图像的容器将执行连接到 localhost 端口8000gunicorn,并在mysite目录中发现的wsgi.py文件中运行应用程序函数。

在此时,您可以使用docker build来构建您的应用图像和docker run在您的机器上运行容器。

构建 Docker 图像

默认情况下,docker build命令在当前目录中寻找一个Dockerfile,以便找到其构建指令。它还将构建的背景,即在构建过程中应该可用的本地文件系统等级,发送到Docker大象。

在访问包含你的Dockerfile的目录后,运行docker build,输入一个图像和标签名称与t旗帜,并使用当前目录作为构建背景。

1docker build -t django-polls:v0 .

该命令将将Dockerfile和当前目录作为构建背景传递给Docker对象,该对象将通过创建一系列图像层来构建您的图像,因为它处理了Dockerfile指令。

docker build完成时,您应该看到以下输出:

1[secondary_label Output]
2Successfully built 8260b58f5713
3Successfully tagged django-polls:v0

成功构建图像后,您可以使用docker run运行应用程序容器,但 Run命令很可能在这里失败,因为我们还没有配置容器的运行环境,例如SECRET_KEY和从settings.py的数据库设置等外置变量将是空的或设置为默认值。

在最后一步中,我们将使用环境变量文件配置容器的运行环境,然后,我们将创建数据库方案,生成和上传应用程序的静态文件到对象存储,并最终测试应用程序。

第7步:配置运行环境并测试应用程序

Docker 提供 若干方法 设置容器内的环境变量. 由于我们必须设置我们在步骤 1 中外包的所有变量,我们将使用 `--env-file' 方法,这使我们能够通过包含环境变量列表及其值的文件。

polls-project目录中创建一个名为env的文件,并粘贴到以下变量列表中:

 1[label polls-project/env]
 2DJANGO_SECRET_KEY=your_secret_key
 3DEBUG=True
 4DJANGO_ALLOWED_HOSTS=your_server_IP_address
 5DATABASE_ENGINE=postgresql_psycopg2
 6DATABASE_NAME=polls
 7DATABASE_USERNAME=sammy
 8DATABASE_PASSWORD=your_database_password
 9DATABASE_HOST=your_database_host
10DATABASE_PORT=your_database_port
11STATIC_ACCESS_KEY_ID=your_space_access_key_id
12STATIC_SECRET_KEY=your_space_secret_key
13STATIC_BUCKET_NAME=your_space_name
14STATIC_ENDPOINT_URL=https://nyc3.digitaloceanspaces.com
15DJANGO_LOGLEVEL=info

在此文件中取代以下值:

  • `DJANGO-SECRET-KEY':将此设定为一个独特的、无法预测的价值,详见Django docs。 生成此密钥的一个方法是在 [可扩展 Django App (https://andsky.com/tech/tutorials/how-to-set-up-a-scalable-django-app-with-digitalocean-managed-databases-and-spaces step-5-%E2%80%94-adjusting-the-app-settings) 教程中的 [调整 App 设置 (https://andsky.com/tech/tutorials/how-to-set-up-a-scalable-django-app-with-digitalocean-managed-databases-and-spaces# step-5-%E2%80%94-adjusting-the-app-settings ) 中提供 。
  • `DJANGO_ALLOUED-HOSTS': 设定为您的 Ubuntu 服务器的 IP 地址 。 为测试目的,您也可以将其设置为QQ,一个匹配所有主机的通配符. 在生产环境中运行 Django 时, 请确定此值 。
  • `巴哈马-美国 ' : 设定为上一步创建的数据库用户 。
  • `DATABASE-PASSWORD': 设定为上一步创建的用户密码 。
  • `DATABASE-HOST':将此设定为数据库的主机名。
  • `DATABASE-PORT':将其设置在数据库的端口。
  • `统计-ACCESS-KEY-ID': 设定为您的空间访问密钥 。
  • `STATIC_SECRET-KEY':将此设定为您的空间访问密钥。
  • `STIC_BUKET_NAME: 将此设定为您的空间名 。
  • `统计-ENDPOINT-URL': 设定为相应的空间端点 URL 。 .

当 Django 在生产中运行时,请确保将DEBUG设置为False,并根据您想要的语音水平调整日志级别。

保存并关闭文件。

现在我们将使用docker run来取代Dockerfile中设置的CMD,并使用manage.py makemigrationsmanage.py migrate命令创建数据库方案:

1docker run --env-file env django-polls:v0 sh -c "python manage.py makemigrations && python manage.py migrate"

在这里,我们运行django-polls:v0容器图像,传入我们刚刚创建的环境变量文件,并用sh -c``python manage.py makemigrations && python manage.py迁移来代替Dockerfile命令,这将创建应用程序代码定义的数据库方案。

 1[secondary_label Output]
 2No changes detected
 3Operations to perform:
 4  Apply all migrations: admin, auth, contenttypes, polls, sessions
 5Running migrations:
 6  Applying contenttypes.0001_initial... OK
 7  Applying auth.0001_initial... OK
 8  Applying admin.0001_initial... OK
 9  Applying admin.0002_logentry_remove_auto_add... OK
10  Applying admin.0003_logentry_add_action_flag_choices... OK
11  Applying contenttypes.0002_remove_content_type_name... OK
12  Applying auth.0002_alter_permission_name_max_length... OK
13  Applying auth.0003_alter_user_email_max_length... OK
14  Applying auth.0004_alter_user_username_opts... OK
15  Applying auth.0005_alter_user_last_login_null... OK
16  Applying auth.0006_require_contenttypes_0002... OK
17  Applying auth.0007_alter_validators_add_error_messages... OK
18  Applying auth.0008_alter_user_username_max_length... OK
19  Applying auth.0009_alter_user_last_name_max_length... OK
20  Applying auth.0010_alter_group_name_max_length... OK
21  Applying auth.0011_update_proxy_permissions... OK
22  Applying polls.0001_initial... OK
23  Applying sessions.0001_initial... OK

这表明数据库方案已成功创建。

接下来,我们将运行应用程序容器的另一个实例,并使用其内部的交互式壳创建 Django 项目的管理用户。

1docker run -i -t --env-file env django-polls:v0 sh

这将为您提供运行容器内部的壳提示,您可以使用它来创建Django用户:

1python manage.py createsuperuser

输入您的用户的用户名、电子邮件地址和密码,然后创建用户后,点击CTRL+D,离开容器并杀死它。

最后,我们将生成应用程序的静态文件,并使用collectstatic将其上传到DigitalOcean Space:

1docker run --env-file env django-polls:v0 sh -c "python manage.py collectstatic --noinput"
1[secondary_label Output]
2
3121 static files copied.

现在我们可以运行应用程序:

1docker run --env-file env -p 80:8000 django-polls:v0
1[secondary_label Output]
2[2019-10-17 21:23:36 +0000] [1] [INFO] Starting gunicorn 19.9.0
3[2019-10-17 21:23:36 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1)
4[2019-10-17 21:23:36 +0000] [1] [INFO] Using worker: sync
5[2019-10-17 21:23:36 +0000] [7] [INFO] Booting worker with pid: 7
6[2019-10-17 21:23:36 +0000] [8] [INFO] Booting worker with pid: 8
7[2019-10-17 21:23:36 +0000] [9] [INFO] Booting worker with pid: 9

在这里,我们运行Dockerfile中定义的默认命令gunicorn --bind :8000 -workers 3 mysite.wsgi:application,并曝光容器端口8000,使Ubuntu服务器上的端口80被映射到容器django-polls:v0的端口8000

您现在应该能够通过在URL栏中键入http://your_server_ip来导航到调查应用程序,因为没有定义的路径,您可能会收到一个404页面未找到的错误,这是预期的。

点击http://your_server_ip/polls查看Polls应用界面:

Polls App Interface

要查看管理员界面,请访问http://your_server_ip/admin。您应该看到调查应用程序管理员身份验证窗口:

Polls Admin Auth Page

输入您用createsuperuser命令创建的管理用户名和密码。

验证后,您可以访问 Polls 应用程序的管理界面:

Polls Admin Main Interface

请注意,对)。

当你完成探索时,在运行 Docker 容器的终端窗口中按CTRL-C来杀死容器。

结论

在这个教程中,你改编了一个Django网络应用程序, 在一个基于容器的云母环境中有效工作。 然后您为容器图像写了一个最小的多克文件, 在当地建造, 并使用多克引擎运行 。 您可以看到在 Polls app GitHub 的 [polls- docker 分支] https://github.com/do-community/django-polls/tree/polls-docker 中执行的修改的 [diff] (LINK0) 。 此分支包含此教程中描述的所有修改 .

从这里,您可以将 Django/Gunicorn 容器与 Nginx 反向代理容器并配对以处理和路由接收的 HTTP 请求,以及一个 Certbot 容器以获取 TLS 证书。您可以使用 Docker Compose来管理这个多容器架构;这将在下面的教程中描述。

请注意,此设置已准备好生产,因为您应该始终在HTTP代理程序背后运行Gunicorn以缓冲慢客户端。否则,您的DjangoWeb应用程序将容易受到拒绝服务攻击。我们还选择了3作为本教程中的Gunicorn员工的任意数量。在生产中,您应该使用性能基准设置工人和线索的数量。

在此架构中,我们做了一个设计选择,将静态资产卸载到对象存储中,以便容器不必使用 Nginx 组合这些资产的版本并服务它们,这在像 Kubernetes 这样的多容器集群环境中可以变得复杂。

最后,现在你已经完全集装了Django Polls应用程序,你可以将图像推到一个容器注册表,如 Dockerhub并使其可用于任何Docker可用的系统:Ubuntu服务器,虚拟机和容器集群,如Kubernetes。

Published At
Categories with 技术
comments powered by Disqus