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

本教程介绍了如何使用Docker将Django Polls应用容器化,涉及创建PostgreSQL数据库,配置环境变量,处理静态资产,配置日志,编写Dockerfile,以及在Docker中运行和测试应用。通过这些步骤,实现了云原生环境下的可扩展和可移植Django应用。
摘要由CSDN通过智能技术生成

介绍 (Introduction)

Django is a powerful web framework that can help you get your Python application off the ground quickly. It includes several convenient features like an object-relational mapper, user authentication, and a customizable administrative interface for your application. It also includes a caching framework and encourages clean app design through its URL Dispatcher and Template system.

Django是一个功能强大的网络框架,可以帮助您快速启动Python应用程序。 它包括几个方便的功能,例如对象关系映射器 ,用户身份验证和适用于您的应用程序的可自定义管理界面。 它还包括一个缓存框架,并通过其URL DispatcherTemplate系统鼓励进行干净的应用程序设计。

In this tutorial, you’ll learn how to build a scalable and portable Django Polls app with Docker containers. Out of the box, a Django app requires several modifications to run effectively inside of containers, like logging to standard output streams and configuring itself through environment variables passed into the container. In addition, offloading static assets like JavaScript and CSS stylesheets to object storage allows you to streamline and centralize the management of these files in a multi-container environment.

在本教程中,您将学习如何使用Docker容器构建可扩展且可移植的Django Polls应用程序。 Django应用程序开箱即用,需要进行一些修改才能在容器内有效运行,例如登录到标准输出流并通过传递到容器中的环境变量对其进行配置。 此外,将JavaScript和CSS样式表之类的静态资产卸载到对象存储中,可以使您在多容器环境中简化和集中管理这些文件。

You’ll implement these modifications—inspired by the Twelve-Factor methodology for building scalable, cloud-native web apps—on a sample Django Polls app. Then, you’ll build the application image and run the containerized app with Docker.

您将在示例Django Polls应用程序上实施这些修改(受十二要素方法启发,用于构建可扩展的云原生Web应用程序)。 然后,您将构建应用程序映像并使用Docker运行容器化的应用程序。

By the end of this tutorial, you’ll have containerized the setup in How to Set Up a Scalable Django App. In subsequent tutorials in this series, you’ll learn how to use Docker Compose to pair the Django container with an Nginx reverse proxy, and deploy this architecture to a Kubernetes cluster.

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

It’s highly recommended to work through the tutorial to understand the changes you’re making to the app, but if you’d like to skip ahead, you can obtain the modified code from the polls-docker branch of the Polls app GitHub repository.

强烈建议您通读本教程,以了解对应用程序所做的更改,但是如果您想跳过,可以从Polls应用程序GitHub存储库的polls-docker分支中获取修改后的代码。

先决条件 (Prerequisites)

To follow this tutorial, you’ll need:

要遵循本教程,您需要:

第1步-创建PostgreSQL数据库和用户 (Step 1 — Creating the PostgreSQL Database and User)

To begin, we’ll connect to the PostgreSQL server from the Ubuntu instance. Then, we’ll create a PostgreSQL database and user for the Django app, and configure the database to work effectively with Django.

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

Before we connect to the database from our Ubuntu machine (not the app container), we need to install the postgresql-client package from the Ubuntu repositories. First update the local apt package index and then download and install the package:

在从Ubuntu机器(不是应用程序容器)连接到数据库之前,我们需要从Ubuntu存储库中安装postgresql-client软件包。 首先更新本地apt软件包索引,然后下载并安装该软件包:

sudo apt update
sudo apt install postgresql-client

Hit Y and then ENTER when prompted to begin downloading and installing the packages.

当提示您开始下载和安装软件包时,请按Y ,然后按ENTER

Now that you’ve installed the client, we’ll use it to create a database and database user for our Django application.

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

To begin, grab the Connection Parameters for your cluster by navigating to Databases from the Cloud Control Panel, and clicking into your database. You should see a Connection Details box containing some Connection parameters for your cluster. Note these down.

首先,通过从Cloud Control Panel导航到Databases ,然后单击进入数据库,获取集群的Connection Parameters 。 您应该看到一个Connection Details框,其中包含群集的一些Connection参数 。 注意这些。

Back on the command line, log in to your cluster using these credentials and the psql PostgreSQL client we just installed:

返回命令行,使用以下凭据和我们刚安装的psql PostgreSQL客户端登录到集群:

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

When prompted, enter the password displayed alongside the Postgres username, and hit ENTER.

出现提示时,输入显示在Postgres用户名旁边的密码,然后按ENTER

You will be given a PostgreSQL prompt from which you can manage the database.

系统将显示一个PostgreSQL提示,您可以从中管理数据库。

First, create a database for your project called polls:

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

CREATE DATABASE polls;

Note: Every Postgres statement must end with a semicolon, so make sure that your command ends with one if you are experiencing issues.

注意:每个Postgres语句必须以分号结尾,因此,如果遇到问题,请确保命令以1结尾。

We can now switch to the polls database:

现在,我们可以切换到polls数据库:

\c polls;

Next, create a database user for the project. Make sure to select a secure password:

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

CREATE USER sammy WITH PASSWORD 'password';

We’ll now modify a few of the connection parameters for the user we just created. This will speed up database operations so that the correct values do not have to be queried and set each time a connection is established.

现在,我们将为刚创建的用户修改一些连接参数。 这将加速数据库操作,从而不必在每次建立连接时都查询和设置正确的值。

We are setting the default encoding to UTF-8, which Django expects. We are also setting the default transaction isolation scheme to “read committed”, which blocks reads from uncommitted transactions. Lastly, we are setting the timezone. By default, our Django projects will be set to use UTC. These are all recommendations from the Django project itself.

我们将Django期望的默认编码设置为UTF-8 。 我们还将默认事务隔离方案设置为“读已提交”,该方案将阻止未提交事务的读取。 最后,我们设置时区。 默认情况下,我们的Django项目将设置为使用UTC 。 这些都是Django项目本身的建议。

Enter the following commands at the PostgreSQL prompt:

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

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

Now we can give our new user access to administer our new database:

现在,我们可以授予新用户访问权限来管理新数据库:

GRANT ALL PRIVILEGES ON DATABASE polls TO sammy;

When you are finished, exit out of the PostgreSQL prompt by typing:

完成后,通过键入以下命令退出PostgreSQL提示符:

\q

A Django app, properly configured, can now connect to and manage this database. In the next step, we’ll clone the Polls app code from GitHub and explicitly define its Python package dependencies.

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

第2步-克隆应用程序存储库并声明依赖关系 (Step 2 — Cloning App Repository and Declaring Dependencies)

To begin the process of containerizing our Django Polls app, we’ll first clone the django-polls repository, which contains the complete code for the Django project’s tutorial Polls app.

要开始对Django Polls应用程序进行容器化的过程,我们将首先克隆django-polls存储库,其中包含Django项目的教程 Polls应用程序的完整代码。

Log in to your server, create a directory called polls-project and use git to clone the django-polls repo from GitHub:

登录到您的服务器,创建一个名为polls-project的目录,并使用git从GitHub复制django-polls库:

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

Access the django-polls directory and list the repository contents:

访问django-polls目录并列出存储库内容:

cd django-polls
ls

   
   
Output
LICENSE README.md manage.py mysite polls templates

You should see the following objects:

您应该看到以下对象:

  • manage.py: The main command-line utility used to manipulate the app.

    manage.py :用于操作应用程序的主要命令行实用程序。

  • polls: Contains the polls app code.

    polls :包含polls应用程序代码。

  • mysite: Contains Django project-scope code and settings.

    mysite :包含Django项目范围的代码和设置。

  • templates: Contains custom template files for the administrative interface.

    templates :包含管理界面的定制模板文件。

To learn more about the project structure and files, consult Creating a Project from the official Django documentation.

要了解有关项目结构和文件的更多信息,请参考官方Django文档中的创建项目

In this directory we’ll also create a file called requirements.txt that will contain the Django app’s Python dependencies.

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

Open a file called requirements.txt in your editor of choice and paste in the following Python dependencies:

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

polls-project/django-polls/requirements.txt
polls-project / django-polls / requirements.txt
boto3==1.9.252
botocore==1.12.252
Django==2.2.6
django-storages==1.7.2
docutils==0.15.2
gunicorn==19.9.0
jmespath==0.9.4
psycopg2==2.8.3
python-dateutil==2.8.0
pytz==2019.3
s3transfer==0.2.1
six==1.12.0
sqlparse==0.3.0
urllib3==1.25.6

Here we install Django, the django-storages plugin for offloading static assets to object storage, the gunicorn WSGI server, the psycopg2 PostgreSQL adapter, as well as some additional dependency packages. Note that we explicitly list and version every Python package required by our app.

在这里,我们安装Django,用于将静态资产卸载到对象存储的django-storages gunicorn插件, gunicorn WSGI服务器, psycopg2 PostgreSQL适配器以及一些其他依赖包。 请注意,我们明确列出了应用所需的每个Python软件包并对其进行了版本控制。

Save and close the file.

保存并关闭文件。

Now that we’ve cloned the app and defined its dependencies, we can move on to modifying it for portability.

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

第3步-通过环境变量使Django可配置 (Step 3 — Making Django Configurable by Environment Variables)

One of the most important recommendations from the twelve-factor app methodology is extracting hard-coded config from your application’s codebase. This allows you to easily change the behavior of your application at runtime by modifying environment variables. Docker and Kubernetes both suggest this method of configuring containers, so we will adapt our application’s settings file to use this pattern.

十二要素应用程序方法中最重要的建议之一是从应用程序的代码库中提取硬编码的配置。 这使您可以通过修改环境变量轻松地在运行时更改应用程序的行为。 Docker和Kubernetes都建议使用这种配置容器的方法,因此我们将调整应用程序的设置文件以使用此模式。

The main settings file for our Django project (django-polls/mysite/settings.py) is a Python module that uses native data structures to configure the application. By default, most of the values in the file are hard-coded, meaning that you have to edit the configuration file to change the application behavior. We can use Python’s getenv function in the os module to configure Django to read configuration parameters from local environment variables instead.

我们的Django项目的主要设置文件( django-polls/mysite/settings.py )是一个Python模块,该模块使用本机数据结构来配置应用程序。 默认情况下,文件中的大多数值都是硬编码的,这意味着您必须编辑配置文件才能更改应用程序的行为。 我们可以在os模块中使用Python的getenv函数将Django配置为从本地环境变量读取配置参数。

To do this, we’ll go through settings.py and replace the hard-coded values of each of the variables we want to set at runtime with a call to os.getenv. The os.getenv function reads the value from a provided environment variable name. You can optionally provide a second parameter with a default value that will be used if the environment variable is not set.

为此,我们将遍历settings.py并使用对os.getenv的调用os.getenv要在运行时设置的每个变量的硬编码值。 os.getenv函数从提供的环境变量名称中读取值。 您可以选择提供第二个参数,其默认值将在未设置环境变量的情况下使用。

This allows us to set variables like this:

这使我们可以像这样设置变量:

polls-project/django-polls/mysite/settings.py
polls-project / django-polls / mysite / settings.py
. . .
SECRET_KEY = os.getenv('DJANGO_SECRET_KEY')
. . .
DEBUG = os.getenv('DJANGO_DEBUG', False)
. . .

For SECRET_KEY, Django will look for an environment variable called DJANGO_SECRET_KEY. Since this shouldn’t be hard-coded and needs to be the same across our application servers, we’ll want to set this externally with no fallback value. We want the application to fail if we do not provide this, since it could lead to problems if various copies of our application use different keys.

对于SECRET_KEY ,Django将寻找一个名为DJANGO_SECRET_KEY的环境变量。 由于不应对此进行硬编码,并且在我们的应用程序服务器之间必须相同,因此我们希望在外部设置此值,而不会使用回退值。 如果我们不提供此功能,我们希望应用程序失败,因为如果我们的应用程序的各种副本使用不同的密钥,则可能导致问题。

For DEBUG, Django will look for an environment variable called DJANGO_DEBUG. However, this time, we’ve provided a default value that will be used as fallback if the variable is not set. In this case, we’ve opted to set DEBUG to False if no value is provided so that we do not accidentally leak sensitive information unless the variable is intentionally defined and set to True..

对于DEBUG ,Django将寻找一个名为DJANGO_DEBUG的环境变量。 但是,这次,我们提供了一个默认值,如果未设置该变量,它将用作备用。 在这种情况下,如果没有提供任何值,我们选择将DEBUG设置为False ,以便除非意外定义该变量并将其设置为True否则我们不会意外泄漏敏感信息。

To apply this technique, open the polls-project/django-polls/mysite/settings.py file in your editor of choice, and move through it, externalizing the following variables with the provided default values:

要应用此技术,请在您选择的编辑器中打开polls-project/django-polls/mysite/settings.py文件,并进行遍历,使用提供的默认值外部化以下变量:

  • SECRET_KEY = os.getenv('DJANGO_SECRET_KEY')

    SECRET_KEY = os.getenv('DJANGO_SECRET_KEY')

  • DEBUG = os.getenv('DEBUG', False)

    DEBUG = os.getenv('DEBUG', False)

  • ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '127.0.0.1').split(',')

    ALLOWED_HOSTS = os.getenv('DJANGO_ALLOWED_HOSTS', '127.0.0.1').split(',')

For ALLOWED_HOSTS, we fetch the DJANGO_ALLOWED_HOSTS environment variable, and split it into a Python list using , as a separator. If the variable isn’t set, ALLOWED_HOSTS is set to 127.0.0.1.

对于ALLOWED_HOSTS ,我们取DJANGO_ALLOWED_HOSTS环境变量,并将其拆分为使用Python列表,作为分隔符。 如果未设置变量,则ALLOWED_HOSTS设置为127.0.0.1

Once you’ve modified the above variables, navigate to the DATABASES variable and configure it as follows:

修改完以上变量后,导航至DATABASES变量并按如下所示进行配置:

polls-project/django-polls/mysite/settings.py
polls-project / django-polls / mysite / settings.py
. . . 
# Database
# https://docs.djangoproject.com/en/2.1/ref/settings/#databases

DATABASES = {
     'default': {
         'ENGINE': 'django.db.backends.{}'.format(
             os.getenv('DATABASE_ENGINE', 'sqlite3')
         ),
         'NAME': os.getenv('DATABASE_NAME', 'polls'),
         'USER': os.getenv('DATABASE_USERNAME', 'myprojectuser'),
         'PASSWORD': os.getenv('DATABASE_PASSWORD', 'password'),
         'HOST': os.getenv('DATABASE_HOST', '127.0.0.1'),
         'PORT': os.getenv('DATABASE_PORT', 5432),
         'OPTIONS': json.loads(
             os.getenv('DATABASE_OPTIONS', '{}')
         ),
     }
 }
 . . .

This will set the default database parameters using environment variables.

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

For DATABASES['default']['OPTIONS'], we used json.loads to deserialize a JSON object passed in through the DATABASE_OPTIONS environment variable. Most of the time, interpreting environment variables as simple strings makes the translation to Django settings easier to read. However, in this instance, being able to pass in an arbitrary data structure is valuable. Each database engine has a unique set of valid options, so being able to encode a JSON object with the appropriate parameters gives us much greater flexibility at the expense of some legibility.

对于DATABASES['default']['OPTIONS'] ,我们使用json.loads反序列化通过DATABASE_OPTIONS环境变量传入的JSON对象。 在大多数情况下,将环境变量解释为简单的字符串使翻译为Django设置更容易阅读。 但是,在这种情况下,能够传递任意数据结构是很有价值的。 每个数据库引擎都有一组唯一的有效选项,因此能够使用适当的参数对JSON对象进行编码,以牺牲一些易读性为我们提供了更大的灵活性。

To make use of the json library, import it at the top of settings.py:

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

polls-project/django-polls/mysite/settings.py
polls-project / django-polls / mysite / settings.py
"""
Django settings for mysite project.

Generated by 'django-admin startproject' using Django 2.1.

For more information on this file, see
https://docs.djangoproject.com/en/2.1/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.1/ref/settings/
"""

import os
import json
. . .

The other area that requires special attention is DATABASES['default']['NAME']. For most database engines, this is the database name within the relational database management system. On the other hand, if you’re using SQLite, NAME is used to specify the database file so be sure to set this parameter accordingly.

另一个需要特别注意的地方是DATABASES['default']['NAME'] 。 对于大多数数据库引擎,这是关系数据库管理系统中的数据库名称。 另一方面,如果您使用的是SQLite,则使用NAME来指定数据库文件,因此请确保相应地设置此参数。

Since the settings.py file is Python code, there are many different ways you can handle reading values from the environment. The method we’ve used here is just one possible technique for externalizing configuration from your codebase.

由于settings.py文件是Python代码,因此您可以使用多种不同的方法来处理从环境中读取的值。 我们在这里使用的方法只是从代码库外部化配置的一种可能技术。

In this step we’ve configured the main Django settings variables in a generic and portable fashion, including the database parameters. In the following step, we’ll continue configuring settings for static files like Javascript and CSS stylesheets, which we’ll centralize and offload to an S3-compatible object storage service.

在这一步中,我们以通用和可移植的方式配置了主要的Django设置变量,包括数据库参数。 在接下来的步骤中,我们将继续为静态文件(例如Javascript和CSS样式表)配置设置,将其集中并卸载到与S3兼容的对象存储服务中。

第4步-卸载静态资产 (Step 4 — Offloading Static Assets)

When running multiple Django containers in a production environment, it can be cumbersome to maintain specific versions of static assets and files across the entire fleet of running containers. To streamline this architecture, we can offload all shared elements and state to external storage. Instead of trying to keep these items in sync across replicas or implementing backup and loading routines to ensure data is locally available, we can implement access to these assets as network-accessible services.

在生产环境中运行多个Django容器时,在整个运行中的容器中维护静态资产和文件的特定版本可能很麻烦。 为了简化此架构,我们可以将所有共享元素和状态卸载到外部存储。 我们可以尝试将这些资产作为网络可访问的服务来实现,而不必尝试使这些项目在副本中保持同步或执行备份和加载例程以确保数据在本地可用。

In the last step, we configured Django so that we could pass in database connection parameters through environment variables. In this step, we’ll do the same for our object storage service, which we’ll use to store static assets that will be shared by Django containers.

在最后一步中,我们配置了Django,以便我们可以通过环境变量传入数据库连接参数。 在这一步中,我们将对对象存储服务执行相同的操作,该服务将用于存储将由Django容器共享的静态资产。

The django-storages package provides remote storage backends (including S3-compatible object storage) that Django can use to offload files. We’ll configure the Polls app to use django-storages to upload static files to a DigitalOcean Space, as outlined in Step 7 of How to Set Up a Scalable Django App with DigitalOcean Managed Databases and Spaces. In this guide, we’ll use DigitalOcean Spaces, but you can use any S3-compatible object storage provider.

django-storages软件包提供了Django可用于卸载文件的远程存储后端(包括与S3兼容的对象存储)。 我们将配置Polls应用程序以使用django-storages将静态文件上载到DigitalOcean Space,如如何使用DigitalOcean托管数据库和空间来设置可扩展Django应用程序的步骤7所述。 在本指南中,我们将使用DigitalOcean Spaces,但是您可以使用任何与S3兼容的对象存储提供程序。

To begin, we’ll make some modifications to the same django-polls/mysite/settings.py file we’ve altered in previous steps.

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

Begin by opening up the mysite/settings.py file for editing and appending the storages app to Django’s list of INSTALLED_APPS:

首先打开mysite/settings.py文件进行编辑,然后将storages应用程序附加到Django的INSTALLED_APPS列表中:

polls-project/django-polls/mysite/settings.py
polls-project / django-polls / mysite / settings.py
. . .
INSTALLED_APPS = [
    . . .
    'django.contrib.staticfiles',
    'storages',
]
. . .

The storages app is installed via django-storages in the requirements.txt file we defined in Step 1.

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

Now, locate the STATIC_URL variable at the bottom of the file, and replace it with the following block:

现在,在文件底部找到STATIC_URL变量,并将其替换为以下块:

polls-project/django-polls/mysite/settings.py
polls-project / django-polls / mysite / settings.py
. . .

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.1/howto/static-files/

# Moving static assets to DigitalOcean Spaces as per:
# https://www.digitalocean.com/community/tutorials/how-to-set-up-object-storage-with-django
AWS_ACCESS_KEY_ID = os.getenv('STATIC_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.getenv('STATIC_SECRET_KEY')

AWS_STORAGE_BUCKET_NAME = os.getenv('STATIC_BUCKET_NAME')
AWS_S3_ENDPOINT_URL = os.getenv('STATIC_ENDPOINT_URL')
AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
AWS_DEFAULT_ACL = 'public-read'

STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'

STATIC_URL = '{}/{}/'.format(AWS_S3_ENDPOINT_URL, AWS_LOCATION)
STATIC_ROOT = 'static/'

We hard-code the following configuration variables:

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

  • STATICFILES_STORAGE: Sets the storage backend Django will use to offload static files. This S3Boto3Storage backend should work with any S3-compatible backend, including DigitalOcean Spaces.

    STATICFILES_STORAGE :设置Django将用于卸载静态文件的存储后端。 此S3Boto3Storage后端应与任何与S3兼容的后端一起使用,包括DigitalOcean Spaces。

  • AWS_S3_OBJECT_PARAMETERS Sets the cache control headers on static files.

    AWS_S3_OBJECT_PARAMETERS设置静态文件上的缓存控制标头。

  • AWS_LOCATION: Defines a directory called static within the object storage bucket where all static files will be placed.

    AWS_LOCATION :在对象存储桶中定义一个名为static的目录,所有静态文件都将放置在该目录中。

  • `AWS_DEFAULT_ACL: Defines the access control list (ACL) for the static files. Setting it to public-read ensures that the files are publicly accessible to end users.

    ` AWS_DEFAULT_ACL :定义静态文件的访问控制列表(ACL)。 将其设置为public-read可确保最终用户可以公开访问这些文件。

  • STATIC_URL: Specifies the base URL that Django should use when generating URLs for static files. Here, we combine the endpoint URL and the static files subdirectory to construct a base URL for static files.

    STATIC_URL :指定Django在为静态文件生成URL时应使用的基本URL。 在这里,我们结合端点URL和静态文件子目录来构造静态文件的基本URL。

  • STATIC_ROOT: Specifies where to collect static files locally before copying them to object storage.

    STATIC_ROOT :指定在将静态文件复制到对象存储之前在本地收集静态文件的位置。

To maintain flexibility and portability, we set up many of the parameters to be configurable at runtime using environment variables, just as we did previously. These include:

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

  • AWS_ACCESS_KEY_ID: Set by the STATIC_ACCESS_KEY_ID environment variable. The DigitalOcean Spaces access key identifier.

    AWS_ACCESS_KEY_ID :由STATIC_ACCESS_KEY_ID环境变量设置。 DigitalOcean Spaces访问密钥标识符。

  • AWS_SECRET_ACCESS_KEY: Set by STATIC_SECRET_KEY. The DigitalOcean Spaces secret key.

    AWS_SECRET_ACCESS_KEY :由STATIC_SECRET_KEY设置。 DigitalOcean Spaces密钥。

  • AWS_STORAGE_BUCKET_NAME: Set by STATIC_BUCKET_NAME. The object storage bucket to which Django will upload assets.

    AWS_STORAGE_BUCKET_NAME :由STATIC_BUCKET_NAME设置。 Django将资产上传到的对象存储桶。

  • AWS_S3_ENDPOINT_URL: Set by STATIC_ENDPOINT_URL. The endpoint URL used to access the object storage service. For DigitalOcean Spaces, this will be something like https://nyc3.digitaloceanspaces.com, depending on the region where your Spaces bucket is located.

    AWS_S3_ENDPOINT_URL :由STATIC_ENDPOINT_URL设置。 用于访问对象存储服务的端点URL。 对于DigitalOcean Spaces,这将类似于https://nyc3.digitaloceanspaces.com ,具体取决于您的Spaces存储桶所在的区域。

When you’re done making changes to settings.py, save and close the file.

完成对settings.py更改后,保存并关闭文件。

From now on, when you run manage.py collectstatic to assemble your project’s static files, Django will upload these to remote object storage. Django is also now configured to serve static assets from this object storage service.

从现在开始,当您运行manage.py collectstatic来汇编项目的静态文件时,Django会将这些文件上传到远程对象存储中。 Django现在也已配置为通过此对象存储服务提供静态资产。

At this point, if you’re using a DigitalOcean Space, you can optionally enable a CDN for your Space, which will speed up delivery of your Django project’s static files by caching them across a geographically-distributed network of edge servers. You can also optionally configure a custom subdomain for your Space. To learn more about CDNs, consult Using a CDN to Speed Up Static Content Delivery. Configuring a CDN goes beyond the scope of this tutorial, but the steps very closely match those in the Enabling CDN section of How to Set Up a Scalable Django App with DigitalOcean Managed Databases and Spaces.

此时,如果您使用的是DigitalOcean Space,则可以选择为您的Space启用CDN,这可以通过在地理分布的边缘服务器网络中缓存Django项目的静态文件来加快它们的交付速度。 您还可以选择为您的Space配置自定义子域。 要了解有关CDN的更多信息,请参阅《 使用CDN加快静态内容交付》 。 配置CDN超出了本教程的范围,但是步骤与“ 如何使用DigitalOcean托管数据库和空间来设置可扩展Django应用程序”的“ 启用CDN”部分非常匹配。

In the next step, we’ll make a final set of changes to settings.py which will enable Django logging to STDOUT and STDERR so that these streams can be picked up by the Docker Engine and inspected using docker logs.

在下一步中,我们将对settings.py进行最后一组更改,这将使Django登录到STDOUT和STDERR,以便Docker Engine可以拾取这些流并使用docker logs检查。

步骤5 —配置日志记录 (Step 5 — Configuring Logging)

By default, Django logs information to standard output and standard error when running the development HTTP server or when the DEBUG option is set to True. However, when DEBUG is set to False or when using a different HTTP server, both of which are likely true in production environments, Django uses a different logging mechanism. Instead of logging everything of priority INFO and above to standard streams, it sends messages of priority ERROR or CRITICAL to an administrative email account.

默认情况下,当运行开发HTTP服务器或DEBUG选项设置为True时,Django将信息记录到标准输出和标准错误中。 但是,当DEBUG设置为False或使用其他HTTP服务器(在生产环境中这两种可能都是正确的)时,Django使用不同的日志记录机制。 它没有将优先级为INFO或更高级别的所有内容都记录到标准流中,而是将优先级为ERRORCRITICAL消息发送到管理电子邮件帐户。

This makes sense for many situations, but in Kubernetes and containerized environments, logging to standard output and standard error is highly recommended. Logging messages are collected in a centralized directory on the Node’s filesystem and are accessible interactively using kubectl and docker commands. This Node-level aggregation facilitates log collection by allowing operations teams to run a process on each node to watch and forward logs. To leverage this architecture, the application must write its logs to these standard sinks.

这在许多情况下都有意义,但是在Kubernetes和容器化环境中,强烈建议记录到标准输出和标准错误。 记录信息被收集在该节点的文件系统集中式目录和交互地访问使用kubectldocker命令。 节点级聚合通过允许操作团队在每个节点上运行一个进程来监视和转发日志来促进日志收集。 要利用此体系结构,应用程序必须将其日志写入这些标准接收器。

Fortunately, logging in Django uses the highly configurable logging module from the Python standard library, so we can define a dictionary to pass to logging.config.dictConfig to define our desired outputs and formatting. To learn more about this technique and others for configuring Django logging, consult Django Logging, The Right Way.

幸运的是,Django中的日志记录使用来自Python标准库的高度可配置的logging模块,因此我们可以定义一个字典以传递给logging.config.dictConfig来定义所需的输出和格式。 要了解有关此技术以及其他配置Django日志记录的更多信息,请参阅Django日志记录,正确的方法

Once again, open up django-polls/mysite/settings.py in your editor.

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

We’ll first add an additional import statement to the top of the file so that we can manipulate the logging configuration:

我们将首先在文件顶部添加一个附加的import语句,以便我们可以操作日志记录配置:

polls-project/django-polls/mysite/settings.py
polls-project / django-polls / mysite / settings.py
import json
import os
import logging.config
. . .

The logging.config import allows us to override Django’s default logging behavior by passing in a dictionary of new logging configuration to the dictConfig function.

logging.config导入使我们可以通过将新的日志记录配置字典传递给dictConfig函数来覆盖Django的默认日志记录行为。

Now, navigate to the bottom of the file, and paste in the following block of logging configuration code:

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

polls-project/django-polls/mysite/settings.py
polls-project / django-polls / mysite / settings.py
. . .
# Logging Configuration

# Clear prev config
LOGGING_CONFIG = None

# Get loglevel from env
LOGLEVEL = os.getenv('DJANGO_LOGLEVEL', 'info').upper()

logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'console': {
            'format': '%(asctime)s %(levelname)s [%(name)s:%(lineno)s] %(module)s %(process)d %(thread)d %(message)s',
        },
    },
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
            'formatter': 'console',
        },
    },
    'loggers': {
        '': {
            'level': LOGLEVEL,
            'handlers': ['console',],
        },
    },
})

Here, we set LOGGING_CONFIG to None to disable the default logging configuration provided by Django. We set LOGLEVEL to INFO by default, but check the DJANGO_LOGLEVEL environment variable so that we can override as necessary.

在这里,我们将LOGGING_CONFIG设置为None以禁用Django提供的默认日志记录配置。 默认情况下,我们将LOGLEVEL设置为INFO ,但是检查DJANGO_LOGLEVEL环境变量,以便可以根据需要覆盖。

Finally, we use the dictConfig function to set a new configuration dictionary using the logging.config module. In the dictionary, we define the text format using formatters, define the output by setting up handlers, and configure which messages should go to each handler using loggers.

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

This is a fairly minimal configuration that allows you to specify a logging severity level using an environment variable called DJANGO_LOGLEVEL, and then log all messages at or above that level to standard streams. For an in-depth discussion of Django logging mechanisms, consult Logging from the official Django docs.

这是一个相当小的配置,它允许您使用名为DJANGO_LOGLEVEL的环境变量指定日志记录严重性级别,然后将该级别或更高级别的所有消息记录到标准流中。 有关Django日志记录机制的深入讨论,请参阅Django官方文档中的Logging

With this configuration, when we containerize the application, Docker will expose these logs through the docker logs command. Likewise, Kubernetes will capture the output and expose it through the kubectl logs command.

使用此配置,当我们对应用程序进行容器化时,Docker将通过docker logs命令公开这些日志。 同样,Kubernetes将捕获输出并通过kubectl logs命令公开它。

This concludes our code modifications to the Django Polls app. In the next step, we’ll begin the containerization process by writing the app’s Dockerfile.

至此,我们完成了对Django Polls应用程序的代码修改。 在下一步中,我们将通过编写应用程序的Dockerfile开始容器化过程。

步骤6 —编写应用程序Dockerfile (Step 6 — Writing the Application Dockerfile)

In this step we’ll define the container image that will run our Django app and the Gunicorn WSGI server that will serve it. It involves building a container image by defining the runtime environment, installing the application and its dependencies, and completing some basic configuration. While there are many possible ways to encapsulate an application in a container image, the practices followed in this step produce a slim, streamlined app image.

在这一步中,我们将定义将运行我们的Django应用程序的容器映像以及将为其提供服务的Gunicorn WSGI服务器。 它涉及通过定义运行时环境,安装应用程序及其依赖项以及完成一些基本配置来构建容器映像。 尽管有许多可能的方法可以将应用程序封装在容器映像中,但此步骤中遵循的实践可生成精简,精简的应用程序映像。

选择适当的父图像 (Choosing an Appropriate Parent Image)

The first major decision that you will have to make when building a container image is the foundation to build from. Container images can either be built from SCRATCH, indicating an empty filesystem, or from an existing container image. Many different base container images are available, each defining a filesystem and providing a unique set of preinstalled packages. Images based on vanilla Linux distributions like Ubuntu 18.04 provide a generic operating environment, while more specialized images often include common libraries and tooling for specific programming languages.

构建容器映像时必须做出的第一个主要决定是构建的基础。 容器映像可以从SCRATCH (指示空文件系统)构建,也可以从现有容器映像构建。 有许多不同的基本容器映像可用,每个映像都定义一个文件系统并提供一组独特的预安装软件包。 基于香草Linux发行版(如Ubuntu 18.04)的映像提供了通用的操作环境,而更专业的映像通常包括用于特定编程语言的通用库和工具。

Whenever possible, it’s often a good idea to use an image from one of Docker’s official repositories as a base. These images have been verified by Docker to follow best practices and are updated regularly for security fixes and improvements.

只要有可能,最好使用Docker官方存储库之一中的映像作为基础。 这些映像已由Docker验证以遵循最佳实践,并定期更新以进行安全修复和改进。

Since our application is built with Django, an image with a standard Python environment will provide a solid foundation and include many of the tools we need to get started. The official Docker repository for Python offers a wide selection of Python-based images, each installing a version of Python and some common tooling on top of an operating system.

由于我们的应用程序是使用Django构建的,因此具有标准Python环境的图像将提供坚实的基础,并包含许多我们需要的入门工具。 用于Python的官方Docker存储库提供了多种基于Python的映像 ,每个映像都在操作系统之上安装了一个版本的Python和一些常用工具。

While the appropriate level of functionality depends on your use case, images based on Alpine Linux are often a solid jumping off point. Alpine Linux offers a robust, but minimal, operating environment for running applications. Its default filesystem is very small, but includes a complete package management system with fairly extensive repositories to make adding functionality straightforward.

虽然适当的功能级别取决于您的用例,但是基于Alpine Linux的映像通常是可靠的起点。 Alpine Linux为运行应用程序提供了一个健壮但最小的操作环境。 它的默认文件系统很小,但是包括一个完整的程序包管理系统,该系统具有相当广泛的存储库,可以使添加功能变得简单明了。

Note: You may have noticed in the list of tags for Python images that multiple tags are available for each image. Docker tags are mutable and maintainers can reassign the same tag to a different image in the future. As a result, many maintainers provide sets of tags with varying degrees of specificity to allow for different use cases. For example, the tag 3-alpine is used to point to the latest available Python 3 version on the latest Alpine version, so it will be reassigned to a different image when a new version of Python or Alpine is released. To make image builds more deterministic, it’s best to use the most specific tags you can find for the image you want to use.

注意:您可能已经在Python图像的标签列表中注意到,每个图像都可以使用多个标签。 Docker标签是可变的,维护者将来可以将同一标签重新分配给不同的映像。 结果,许多维护者提供了具有不同程度的特异性的标签集,以允许不同的用例。 例如,标签3-alpine用于指向最新的Alpine版本上的最新可用Python 3版本,因此当发布新版本的Python或Alpine时,它将重新分配给其他图像。 为了使图像构建更具确定性,最好为要使用的图像使用最具体的标签。

In this guide, we’ll use the Python image tagged as 3.7.4-alpine3.10 as the parent image for our Django application. We specify the repository and tag of the parent image in our Dockerfile using the FROM instruction.

在本指南中,我们将使用标记为3.7.4-alpine3.10的Python图像作为Django应用程序的父图像。 我们使用FROM指令在Dockerfile指定父映像的存储库和标签。

First, navigate out of the django-polls directory.

首先,导航到django-polls目录。

  • cd ..

    光盘..

Then, open a file called Dockerfile in your editor of choice. Paste in the following parent image definition:

然后,在您选择的编辑器中打开一个名为Dockerfile的文件。 粘贴以下父图像定义:

polls-project/Dockerfile
民意调查项目/ Dockerfile
FROM python:3.7.4-alpine3.10

This defines the starting point for the custom Docker image we are building to run our application.

这定义了我们为运行应用程序而构建的自定义Docker映像的起点。

添加说明以设置应用程序 (Adding Instructions to Set Up the Application)

Once you’ve chosen a parent image, you can begin adding instructions to install dependencies, copy over our application files, and set up the running environment. This process generally mirrors the steps you would take to set up a server for your application, with some key differences to account for the container abstractions.

选择父映像后,就可以开始添加说明以安装依赖项,复制我们的应用程序文件并设置运行环境。 此过程通常反映出为应用程序设置服务器所要执行的步骤,但有一些关键差异来说明容器抽象。

After the FROM line, paste in the following block of Dockerfile code:

FROM行之后,粘贴以下Dockerfile代码块:

polls-project/Dockerfile
民意调查项目/ Dockerfile
. . .

ADD django-polls/requirements.txt /app/requirements.txt

RUN set -ex \
    && apk add --no-cache --virtual .build-deps postgresql-dev build-base \
    && python -m venv /env \
    && /env/bin/pip install --upgrade pip \
    && /env/bin/pip install --no-cache-dir -r /app/requirements.txt \
    && runDeps="$(scanelf --needed --nobanner --recursive /env \
        | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
        | sort -u \
        | xargs -r apk info --installed \
        | sort -u)" \
    && apk add --virtual rundeps $runDeps \
    && apk del .build-deps

ADD django-polls /app
WORKDIR /app

ENV VIRTUAL_ENV /env
ENV PATH /env/bin:$PATH

EXPOSE 8000

Let’s go over these instructions to explain some of the less obvious choices. To learn even more about building production-ready Dockerfiles for Django apps, consult A Production-Ready Dockerfile for your Django App.

让我们仔细阅读这些说明,以解释一些不太明显的选择。 要了解有关为Django应用程序构建可用于生产环境的Dockerfile的更多信息,请参阅Django App的可用于生产环境的Dockerfile

First Docker will copy the requirements.txt file to /app/requirements.txt so that our application’s dependencies are available on the image’s filesystem. We will use this to install all of the Python packages that our application needs in order to run. We copy the dependencies file as a separate step from the rest of our codebase so that Docker can cache the image layer containing the dependencies file. Any time the requirements.txt file doesn’t change between builds, Docker can then reuse the cached layer instead of rebuilding it, speeding up the process.

首先,Docker将把requirements.txt文件复制到/app/requirements.txt以便在图像的文件系统上可以使用我们应用程序的依赖项。 我们将使用它来安装应用程序运行所需的所有Python软件包。 我们将依赖文件复制为与代码库其余部分分开的单独步骤,以便Docker可以缓存包含依赖文件的图像层。 任何时候两次构建之间的requirements.txt文件都没有改变时,Docker便可以重用缓存的层而不是重新构建它,从而加快了过程。

Next, we have a single RUN instruction that executes a long list of commands, each chained together using the Linux && operator. To summarize, these commands:

接下来,我们有一条RUN指令,它执行一长串命令,每个命令都使用Linux &&运算符链接在一起。 总结一下,这些命令:

  • Install the PostgreSQL development files and basic build dependencies using Alpine’s apk package manager

    使用Alpine的apk软件包管理器安装PostgreSQL开发文件和基本构建依赖项

  • Create a virtual environment

    创建一个虚拟环境
  • Install the Python dependencies listed in requirements.txt with pip

    使用pip安装requirements.txt列出的Python依赖项

  • Compile a list of packages needed at runtime by analyzing the requirements of the installed Python packages

    通过分析已安装的Python软件包的需求来编译运行时所需的软件包列表
  • Uninstall any unneeded build dependencies

    卸载任何不需要的构建依赖项

We chain the commands together instead of executing each in a separate RUN step because of the way that Docker constructs image layers. For each ADD, COPY, and RUN instruction, Docker creates a new image layer on top of the existing filesystem, executes the instruction, and then saves the resulting layer. This means compressing commands in RUN instructions will result in fewer image layers.

由于Docker构造映像层的方式,我们将命令链接在一起,而不是在单独的RUN步骤中执行每个命令。 对于每条ADDCOPYRUN指令,Docker都会在现有文件系统之上创建一个新的映像层,执行该指令,然后保存结果层。 这意味着在RUN指令中压缩命令将导致更少的图像层。

Once an item has been written to an image layer, it cannot be removed in a subsequent layer to reduce the image size. If we install build dependencies but want to remove them once the application is set up, we need to do so within the same instruction to reduce the image size. In this RUN command, we install build dependencies, use them to build the app’s packages, and subsequently remove them using apk del.

将项目写入图像层后,就无法在后续层中将其删除以减小图像尺寸。 如果我们安装了构建依赖项,但想要在应用程序安装后将其删除,则需要在同一指令中执行以减小映像大小。 在此RUN命令中,我们安装构建依赖项,使用它们来构建应用程序的软件包,然后使用apk del删除它们。

After the RUN instruction, we use ADD to copy in the application code and WORKDIR to set the working directory for the image to our code directory.

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

Then, we use the ENV instruction to set two environment variables that will be available within containers spawned from our image. The first one sets VIRTUAL_ENV to /env and the second instruction modifies the PATH variable to include the /env/bin directory. These two lines emulate the results of sourcing the /env/bin/activate script, which is the traditional method of activating a virtual environment.

然后,我们使用ENV指令设置两个环境变量,这些变量将在从图像生成的容器中可用。 第一个将VIRTUAL_ENV设置为/env ,第二个指令将PATH变量修改为包括/env/bin目录。 这两行模拟了/env/bin/activate脚本的来源结果,这是激活虚拟环境的传统方法。

Finally, we use EXPOSE to inform Docker that the container will listen on port 8000 at runtime.

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

At this point, the Dockerfile is nearly complete. We just need to define the default command that will run when we start containers using the image.

至此, Dockerfile即将完成。 我们只需要定义使用映像启动容器时将运行的默认命令即可。

定义默认命令 (Defining the Default Command)

A Docker image’s default command determines what happens when a container is started without explicitly providing a command to execute. ENTRYPOINT and CMD instructions can be used independently or in tandem to define a default command within the Dockerfile.

Docker映像的默认命令确定了在不显式提供要执行的命令的情况下启动容器时发生的情况。 ENTRYPOINTCMD指令可以单独使用,也可以串联使用,以在Dockerfile定义默认命令。

When both ENTRYPOINT and CMD are defined, the ENTRYPOINT defines the executable that will be run by the container, and the CMD represents the default argument list for that command. Users can override the default argument list by appending alternative arguments on the command line: docker run <image> <arguments>. In this format, users will be unable to easily override the ENTRYPOINT command, so the ENTRYPOINT command is often set to a script that will set up the environment and perform different actions based on the argument list it receives.

同时定义ENTRYPOINTCMDENTRYPOINT定义将由容器运行的可执行文件,而CMD表示该命令的默认参数列表。 用户可以通过在命令行上附加替代参数来覆盖默认参数列表: docker run <image> <arguments> 。 以这种格式,用户将无法轻松地覆盖ENTRYPOINT命令,因此ENTRYPOINT命令通常设置为脚本,该脚本将设置环境并根据收到的参数列表执行不同的操作。

When used alone, ENTRYPOINT configures the container’s executable, but does not define a default argument list. If only CMD is set, it will be interpreted as the default command and argument list, which can be overridden at runtime.

单独使用时, ENTRYPOINT配置容器的可执行文件,但未定义默认参数列表。 如果仅设置了CMD ,它将被解释为默认命令和参数列表,可以在运行时覆盖它们。

In our image, we want the container to run our application by default using the gunicorn application server. The argument list that we pass to gunicorn doesn’t need to be configurable at runtime, but we want the ability to easily run other commands if necessary to debug or perform management tasks (like collecting static assets or initializing the database). With these requirements in mind, it makes sense for us to use CMD to define a default command with no ENTRYPOINT.

在我们的镜像中,我们希望容器默认使用gunicorn应用程序服务器运行我们的应用程序。 我们传递给gunicorn的参数列表不需要在运行时进行配置,但是我们希望能够在必要时轻松运行其他命令以调试或执行管理任务(例如收集静态资产或初始化数据库)。 考虑到这些要求,使用CMD定义不带ENTRYPOINT的默认命令对我们来说是有意义的。

The CMD instruction can be defined using any of the following formats:

可以使用以下任何一种格式定义CMD指令:

  • CMD ["argument 1", "argument 2", . . . ,"argument n"]: The argument list format (used to define the default argument list for an ENTRYPOINT)

    CMD [" argument 1 ", " argument 2 ", . . . ," argument n "] CMD [" argument 1 ", " argument 2 ", . . . ," argument n "] :参数列表格式(用于定义ENTRYPOINT的默认参数列表)

  • CMD ["command", "argument 1", "argument 2", . . . ,"argument n"]: The exec format

    CMD [" command ", " argument 1 ", " argument 2 ", . . . ," argument n "] CMD [" command ", " argument 1 ", " argument 2 ", . . . ," argument n "]exec格式

  • CMD command "argument 1" "argument 2" . . . "argument n": The shell format

    CMD command " argument 1 " " argument 2 " . . . " argument n " CMD command " argument 1 " " argument 2 " . . . " argument n " :shell格式

The first format only lists arguments and is used in conjunction with an ENTRYPOINT. The other two formats specify commands and their arguments, with a few key differences. The exec format, which is recommended, executes the command directly, passing in the argument list with no shell processing. The shell format, on the other hand, passes the entire list to sh -c. This is necessary if, for example, you need to substitute the value of an environment variable in a command, but is generally regarded as less predictable.

第一种格式仅列出参数,并且与ENTRYPOINT结合使用。 其他两种格式指定命令及其参数,但有一些关键区别。 建议使用exec格式,直接执行命令,并传入参数列表,而不进行任何shell处理。 另一方面,shell格式将整个列表传递给sh -c 。 例如,如果您需要在命令中替换环境变量的值,但是通常认为它的可预测性较差,则这是必需的。

For our purposes, the final instruction in our Dockerfile looks like this:

为了我们的目的, Dockerfile的最终指令如下所示:

polls-project/Dockerfile
民意调查项目/ Dockerfile
. . .
CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi:application"]

By default, containers using this image will execute gunicorn bound to localhost port 8000 with 3 workers, and run the application function in the wsgi.py file found in the mysite directory. You can optionally provide a command at runtime to execute a different process instead of gunicorn.

默认情况下,使用此映像的容器将使用3个工作gunicorn执行绑定到localhost端口8000 gunicorn ,并在mysite目录中的wsgi.py文件中运行application功能。 您可以选择在运行时提供命令来执行其他进程,而不是gunicorn

At this point you can use docker build to build your app image and docker run to run the container on your machine.

在这一点上,你可以使用docker build来构建应用程序图像,并docker run到你的机器上运行的容器。

构建Docker映像 (Building the Docker Image)

By default, the docker build command looks for a Dockerfile in the current directory to find its build instructions. It also sends the build “context”, the local filesystem hierarchy that should be available during the build process, to the Docker daemon. Often, the current directory is set as the build context.

默认情况下, Dockerfile docker build命令在当前目录中查找Dockerfile来查找其构建说明。 它还会将构建“上下文”(即在构建过程中应该可用的本地文件系统层次结构)发送到Docker守护程序。 通常,当前目录被设置为构建上下文。

After accessing the directory containing your Dockerfile, run docker build, passing in an image and tag name with the -t flag, and use the current directory as build context. Here, we name the image django-polls and tag it with version v0:

访问包含Dockerfile的目录后,运行Dockerfile docker build ,传入带有-t标志的图像和标签名称,并将当前目录用作构建上下文。 在这里,我们将图像命名为django-polls并将其标记为v0版本:

  • docker build -t django-polls:v0 .

    docker build -t django-polls : v0 。

The command will pass the Dockerfile and current directory as the build context to the Docker daemon. The daemon will build your image by creating a series of image layers as it processes the Dockerfile instructions.

该命令会将Dockerfile和当前目录作为构建上下文传递给Docker守护程序。 守护程序将在处理Dockerfile指令时通过创建一系列映像层来构建映像。

When docker build completes, you should see the following output:

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


   
   
Output
Successfully built 8260b58f5713 Successfully tagged django-polls:v0

After successfully building the image, you’re able to run the app container using docker run. However, the run command will most likely fail here as we still haven’t configured the container’s running environment. Externalized variables like SECRET_KEY and database settings from settings.py will be either blank or set to default values.

成功构建映像后,您可以使用docker run应用程序容器。 但是,由于我们尚未配置容器的运行环境,因此run命令很可能会在此处失败。 来自settings.py外部化变量(例如SECRET_KEY和数据库设置)将为空白或设置为默认值。

In the final step, we’ll configure the container’s running environment using an environment variable file. Then, we’ll create the database schema, generate and upload the app’s static files to object storage, and finally test the app.

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

第7步-配置运行环境并测试应用 (Step 7 — Configuring the Running Environment and Testing the App)

Docker provides several methods for setting environment variables inside of the container. Since we have to set all of the variables we externalized in Step 1, we’ll use the --env-file method, which allows us to pass in a file containing a list of environment variables and their values.

Docker提供了几种在容器内部设置环境变量的方法 。 由于必须设置在步骤1中外部化的所有变量,因此我们将使用--env-file方法,该方法允许我们传入一个包含环境变量及其值列表的文件。

Create a file called env in the polls-project directory, and paste in the following list of variables:

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

polls-project/env
民意调查项目
DJANGO_SECRET_KEY=your_secret_key
DEBUG=True
DJANGO_ALLOWED_HOSTS=your_server_IP_address
DATABASE_ENGINE=postgresql_psycopg2
DATABASE_NAME=polls
DATABASE_USERNAME=sammy
DATABASE_PASSWORD=your_database_password
DATABASE_HOST=your_database_host
DATABASE_PORT=your_database_port
STATIC_ACCESS_KEY_ID=your_space_access_key_id
STATIC_SECRET_KEY=your_space_secret_key
STATIC_BUCKET_NAME=your_space_name
STATIC_ENDPOINT_URL=https://nyc3.digitaloceanspaces.com
DJANGO_LOGLEVEL=info

Replace the following values in this file:

替换此文件中的以下值:

  • DJANGO_SECRET_KEY: Set this to a unique, unpredictable value, as detailed in the Django docs. One method of generating this key is provided in Adjusting the App Settings of the Scalable Django App tutorial.

    DJANGO_SECRET_KEY :将其设置为唯一的,不可预测的值,如Django文档中所述调整 可扩展Django应用程序教程的应用程序设置中提供了一种生成此密钥的方法。

  • DJANGO_ALLOWED_HOSTS: Set this to the IP address of your Ubuntu server. For testing purposes, you can also set it to *, a wildcard that will match all hosts. Be sure to set this value appropriately when running Django in a production environment.

    DJANGO_ALLOWED_HOSTS :将此设置为您的Ubuntu服务器的IP地址。 为了进行测试,您还可以将其设置为* ,该通配符将匹配所有主机。 在生产环境中运行Django时,请确保正确设置此值。

  • DATABASE_USERNAME: Set this to the database user created in the previous step.

    DATABASE_USERNAME :将此设置为上一步中创建的数据库用户。

  • DATABASE_PASSWORD: Set this to the user password created in the previous step.

    DATABASE_PASSWORD :将此设置为上一步中创建的用户密码。

  • DATABASE_HOST: Set this to your database’s hostname.

    DATABASE_HOST :将此设置为数据库的主机名。

  • DATABASE_PORT: Set this to your databases’s port.

    DATABASE_PORT :将此设置为数据库的端口。

  • STATIC_ACCESS_KEY_ID: Set this to your Space’s access key.

    STATIC_ACCESS_KEY_ID :将此设置为您空间的访问密钥。

  • STATIC_SECRET_KEY: Set this to your Space’s access key Secret.

    STATIC_SECRET_KEY :将此设置为您空间的访问密钥Secret。

  • STATIC_BUCKET_NAME: Set this to your Space name.

    STATIC_BUCKET_NAME :将其设置为您的空间名称。

  • STATIC_ENDPOINT_URL: Set this to the appropriate Space endpoint URL.

    STATIC_ENDPOINT_URL :将其设置为适当的Space端点URL。

When running Django in production, be sure to set DEBUG to False and adjust the log level according to your desired verbosity.

在生产环境中运行Django时,请确保将DEBUG设置为False并根据所需的详细程度调整日志级别。

Save and close the file.

保存并关闭文件。

We’ll now use docker run to override the CMD set in the Dockerfile and create the database schema using the manage.py makemigrations and manage.py migrate commands:

现在,我们将使用docker docker run覆盖Dockerfile中的CMD设置,并使用manage.py makemigrationsmanage.py migrate命令创建数据库模式:

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

    docker run --env-file env django-polls:v0 sh -c“ python manage.py makemigrations && python manage.py migration”

Here, we run the django-polls:v0 container image, pass in the environment variable file we just created, and override the Dockerfile command with sh -c "python manage.py makemigrations && python manage.py migrate", which will create the database schema defined by the app code. After running the command you should see:

在这里,我们运行django-polls:v0容器映像,传入我们刚刚创建的环境变量文件,并使用sh -c "python manage.py makemigrations && python manage.py migrate"覆盖Dockerfile命令,这将创建应用程式程式码定义的资料库架构。 After running the command you should see:


   
   
Output
No changes detected Operations to perform: Apply all migrations: admin, auth, contenttypes, polls, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying admin.0002_logentry_remove_auto_add... OK Applying admin.0003_logentry_add_action_flag_choices... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying auth.0007_alter_validators_add_error_messages... OK Applying auth.0008_alter_user_username_max_length... OK Applying auth.0009_alter_user_last_name_max_length... OK Applying auth.0010_alter_group_name_max_length... OK Applying auth.0011_update_proxy_permissions... OK Applying polls.0001_initial... OK Applying sessions.0001_initial... OK

This indicates that the database schema has successfully been created.

这表明数据库架构已成功创建。

Next, we’ll run another instance of the app container and use an interactive shell inside of it to create an administrative user for the Django project.

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

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

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

This will provide you with a shell prompt inside of the running container which you can use to create the Django user:

这将在正在运行的容器内为您提供一个shell提示,您可以使用它来创建Django用户:

  • python manage.py createsuperuser

    python manage.py createsuperuser

Enter a username, email address, and password for your user, and after creating the user, hit CTRL+D to quit the container and kill it.

输入用户的用户名,电子邮件地址和密码,然后在创建用户后,按CTRL+D退出容器并杀死它。

Finally, we’ll generate the static files for the app and upload them to the DigitalOcean Space using collectstatic:

Finally, we'll generate the static files for the app and upload them to the DigitalOcean Space using collectstatic :

  • docker run --env-file env django-polls:v0 sh -c "python manage.py collectstatic --noinput"

    docker run --env-file env django-polls:v0 sh -c "python manage.py collectstatic --noinput"

   
   
Output
121 static files copied.

We can now run the app:

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

  • docker run --env-file env -p 80:8000 django-polls:v0

    docker run --env-file env -p 80:8000 django-polls:v0

   
   
Output
[2019-10-17 21:23:36 +0000] [1] [INFO] Starting gunicorn 19.9.0 [2019-10-17 21:23:36 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) [2019-10-17 21:23:36 +0000] [1] [INFO] Using worker: sync [2019-10-17 21:23:36 +0000] [7] [INFO] Booting worker with pid: 7 [2019-10-17 21:23:36 +0000] [8] [INFO] Booting worker with pid: 8 [2019-10-17 21:23:36 +0000] [9] [INFO] Booting worker with pid: 9

Here, we run the default command defined in the Dockerfile, gunicorn --bind :8000 --workers 3 mysite.wsgi:application, and expose container port 8000 so that port 80 on the Ubuntu server gets mapped to port 8000 of the django-polls:v0 container.

Here, we run the default command defined in the Dockerfile, gunicorn --bind :8000 --workers 3 mysite.wsgi:application , and expose container port 8000 so that port 80 on the Ubuntu server gets mapped to port 8000 of the django-polls:v0 container.

You should now be able to navigate to the polls app using your web browser by typing http://your_server_ip in the URL bar. Since there is no route defined for the / path, you’ll likely receive a 404 Page Not Found error, which is expected.

You should now be able to navigate to the polls app using your web browser by typing http:// your_server_ip in the URL bar. Since there is no route defined for the / path, you'll likely receive a 404 Page Not Found error, which is expected.

Navigate to http://your_server_ip/polls to see the Polls app interface:

Navigate to http:// your_server_ip /polls to see the Polls app interface:

To check out the admin interface, visit http://your_server_ip/admin. You should see the Polls app admin authentication window:

To check out the admin interface, visit http:// your_server_ip /admin . 您应该看到“轮询应用程序管理员身份验证”窗口:

Enter the administrative username and password you created with the createsuperuser command.

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

After authenticating, you can access the Polls app’s administrative interface:

验证之后,您可以访问“轮询”应用程序的管理界面:

Note that static assets for the admin and polls apps are being delivered directly from object storage. To confirm this, consult Testing Spaces Static File Delivery.

请注意,直接从对象存储中交付adminpolls应用程序的静态资产。 要确认这一点,请查阅Testing Spaces静态文件传递

When you are finished exploring, hit CTRL-C in the terminal window running the Docker container to kill the container.

When you are finished exploring, hit CTRL-C in the terminal window running the Docker container to kill the container.

结论 (Conclusion)

In this tutorial you adapted a Django web app to work effectively in a container-based, cloud-native environment. You then wrote a minimal Dockerfile for the container image, built it locally, and ran it using Docker Engine. You can see a diff of the changes you implemented in the polls-docker branch of the Polls app GitHub repository. This branch contains all the modifications described in this tutorial.

In this tutorial you adapted a Django web app to work effectively in a container-based, cloud-native environment. You then wrote a minimal Dockerfile for the container image, built it locally, and ran it using Docker Engine. You can see a diff of the changes you implemented in the polls-docker branch of the Polls app GitHub repository. This branch contains all the modifications described in this tutorial.

From here, you can pair the Django/Gunicorn container with an Nginx reverse proxy container to handle and route incoming HTTP requests, and a Certbot container to obtain TLS certificates. You can manage this multi-container architecture using Docker Compose; this will be described in a subsequent tutorial.

From here, you can pair the Django/Gunicorn container with an Nginx reverse proxy container to handle and route incoming HTTP requests, and a Certbot container to obtain TLS certificates. You can manage this multi-container architecture using Docker Compose ; this will be described in a subsequent tutorial.

Note that as-is, this setup is not production ready as you should always run Gunicorn behind an HTTP proxy to buffer slow clients. If not, your Django web app will be vulnerable to denial-of-service attacks. We also chose 3 as an arbitrary number of Gunicorn workers in this tutorial. In production, you should set the number of workers and threads using performance benchmarks.

Note that as-is, this setup is not production ready as you should always run Gunicorn behind an HTTP proxy to buffer slow clients. If not, your Django web app will be vulnerable to denial-of-service attacks. We also chose 3 as an arbitrary number of Gunicorn workers in this tutorial. In production, you should set the number of workers and threads using performance benchmarks.

In this architecture, we made a design choice to offload static assets to object storage so that containers wouldn’t have to bundle a version of these assets and serve them using Nginx, which can become cumbersome to manage in multi-container cluster environments like Kubernetes. Depending on your use case, this may not be an effective design, so you should adapt the steps in this tutorial accordingly.

In this architecture, we made a design choice to offload static assets to object storage so that containers wouldn't have to bundle a version of these assets and serve them using Nginx, which can become cumbersome to manage in multi-container cluster environments like Kubernetes. Depending on your use case, this may not be an effective design, so you should adapt the steps in this tutorial accordingly.

Finally, now that you’ve fully containerized the Django Polls app, you can push the image to a container registry like Dockerhub and make it available to any system where Docker is available: Ubuntu servers, virtual machines, and container clusters like Kubernetes.

Finally, now that you've fully containerized the Django Polls app, you can push the image to a container registry like Dockerhub and make it available to any system where Docker is available: Ubuntu servers, virtual machines, and container clusters like Kubernetes.

翻译自: https://www.digitalocean.com/community/tutorials/how-to-build-a-django-and-gunicorn-application-with-docker

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值