如何使用Docker,Nginx和加密来扩展和保护Django应用程序

news/2024/7/4 5:27:05

介绍 (Introduction)

In cloud-based environments, there are multiple ways to scale and secure a Django application. By scaling horizontally, and running several copies of your app, you can build a more fault-tolerant and highly-available system, while also increasing its throughput so that requests can be processed simultaneously. One way to horizontally scale a Django app is to provision additional app servers that run your Django application and its WSGI HTTP server (like Gunicorn or uWSGI). To route and distribute incoming requests across this set of app servers, you can use a load balancer and reverse proxy like Nginx. Nginx can also cache static content and terminate Transport Layer Security (TLS) connections, used to provide HTTPS and secure connections to your app.

在基于云的环境中,有多种扩展和保护Django应用程序的方法。 通过水平扩展并运行应用程序的多个副本,可以构建更具容错性和高可用性的系统,同时还可以提高其吞吐量,以便可以同时处理请求。 水平缩放一个Django应用程序的一种方法是运行你的Django应用程序和WSGI HTTP服务器(如提供额外的应用服务器 Gunicorn或uWSGI )。 要在这组应用程序服务器之间路由和分发传入请求,您可以使用负载平衡器和反向代理(例如Nginx) 。 Nginx还可以缓存静态内容并终止传输层安全性 (TLS)连接,用于提供与应用程序的HTTPS和安全连接。

Running your Django application and Nginx proxy inside of Docker containers ensures that these components behave the same way regardless of the environment they are deployed into. In addition, containers provide many features that facilitate packaging and configuring your application.

在Docker 容器中运行Django应用程序和Nginx代理可确保这些组件的行为方式相同,而与部署它们的环境无关。 此外,容器提供了许多功能,可简化打包和配置应用程序的过程。

In this tutorial, you’ll horizontally scale a containerized Django and Gunicorn Polls application by provisioning two application servers that will each run a copy of a Django and Gunicorn app container.

在本教程中,您将通过预配置两个将分别运行Django和Gunicorn应用程序容器副本的应用程序服务器来水平扩展容器化的Django和Gunicorn Polls应用程序。

You’ll also enable HTTPS by provisioning and configuring a third proxy server that will run an Nginx reverse proxy container and a Certbot client container. Certbot will provision TLS certificates for Nginx from the Let’s Encrypt certificate authority. This will ensure that your site receives a high security rating from SSL Labs. This proxy server will receive all of your app’s external requests and sit in front of the two upstream Django application servers. Finally, you’ll harden this distributed system by restricting external access to only the proxy server.

您还将通过配置和配置将运行Nginx反向代理容器和Certbot客户端容器的第三台代理服务器来启用HTTPS。 Certbot将通过“ 让我们加密”证书颁发机构为Nginx设置TLS证书。 这将确保您的站点获得SSL Labs的高度安全评级。 该代理服务器将接收您应用程序的所有外部请求,并位于两个上游 Django应用程序服务器的前面。 最后,通过限制对代理服务器的外部访问来增强此分布式系统。

先决条件 (Prerequisites)

To follow this tutorial, you will need:

要遵循本教程,您将需要:

  • Three Ubuntu 18.04 servers:

    三台Ubuntu 18.04服务器:

    • Two servers will be application servers, used to run your Django and Gunicorn app.

      两个服务器将是应用程序服务器,用于运行Django和Gunicorn应用程序。

    • One server will be a proxy server, used to run Nginx and Certbot.

      一台服务器将是代理服务器,用于运行Nginx和Certbot。

    • All should have a non-root user with sudo privileges, and an active firewall. For guidance on how to set these up, please see this Initial Server Setup guide.

      所有用户都应具有非sudo特权的root用户和活动的防火墙。 有关如何进行设置的指导,请参阅此初始服务器设置指南 。

  • Docker installed on all three servers. For guidance on installing Docker, follow Steps 1 and 2 of How To Install and Use Docker on Ubuntu 18.04.

    Docker安装在所有三台服务器上。 有关安装Docker的指导,请遵循如何在Ubuntu 18.04上安装和使用Docker的步骤1和2。

  • A registered domain name. This tutorial will use your_domain.com throughout. You can get one for free at Freenom, or use the domain registrar of your choice.

    注册域名。 本教程将整个使用your_domain.com 。 您可以从Freenom免费获得一个,或使用您选择的域名注册商。

  • An A DNS record with your_domain.com pointing to your proxy server’s public IP address. You can follow this introduction to DigitalOcean DNS for details on how to add it to a DigitalOcean account, if that’s what you’re using.

    一个带有your_domain.com A DNS记录,指向您的代理服务器的公共IP地址。 您可以按照DigitalOcean DNS简介进行操作,以获取有关如何将其添加到DigitalOcean帐户的详细信息(如果您正在使用的话)。

  • An S3 object storage bucket such as a DigitalOcean Space to store your Django project’s static files and a set of Access Keys for this Space. To learn how to create a Space, consult the How to Create Spaces product documentation. To learn how to create Access Keys for Spaces, consult Sharing Access to Spaces with Access Keys. With minor changes, you can use any object storage service that the django-storages plugin supports.

    一个S3对象存储桶,例如DigitalOcean Space,用于存储Django项目的静态文件和该Space的一组访问密钥。 要了解如何创建空间,请查阅“ 如何创建空间”产品文档。 要了解如何为空间创建访问密钥,请参阅与访问密钥共享对空间的访问 。 进行较小的更改,就可以使用django-storages插件支持的任何对象存储服务。

  • A PostgreSQL server instance, database, and user for your Django app. With minor changes, you can use any database that Django supports.

    Django应用程序的PostgreSQL服务器实例,数据库和用户。 进行较小的更改,就可以使用Django支持的任何数据库。

    • The PostgreSQL database should be called polls (or another memorable name to input in your config files below) and in this tutorial the database user will be named sammy. For guidance on creating these, follow Step 1 of How to Build a Django and Gunicorn Application with Docker. You can perform these steps from any of the three servers.

      PostgreSQL数据库应称为polls (或在下面的配置文件中输入的另一个易记名称),在本教程中,数据库用户将被命名为sammy 。 有关创建它们的指导,请遵循如何使用Docker构建Django和Gunicorn应用程序的步骤1。 您可以从三台服务器中的任何一台执行这些步骤。

    • A DigitalOcean Managed PostgreSQL cluster is used in this tutorial. To learn how to create a cluster, consult the DigitalOcean Managed Databases product documentation.

      本教程中使用了DigitalOcean 托管PostgreSQL集群 。 要了解如何创建集群,请查阅DigitalOcean 托管数据库产品文档 。

    • You can also install and run your own PostgreSQL instance. For guidance on installing and administering PostgreSQL on an Ubuntu server, please see How To Install and Use PostgreSQL on Ubuntu 18.04.

      您也可以安装和运行自己的PostgreSQL实例。 有关在Ubuntu服务器上安装和管理PostgreSQL指导,请参阅如何在Ubuntu 18.04上安装和使用PostgreSQL 。

第1步-配置第一个Django应用服务器 (Step 1 — Configuring the First Django Application Server)

To begin, we’ll clone the Django application repository onto the first app server. Then, we’ll configure and build the application Docker image, and test the application by running the Django container.

首先,我们将Django应用程序存储库克隆到第一台应用程序服务器上。 然后,我们将配置和构建应用程序Docker映像,并通过运行Django容器来测试应用程序。

Note: If you’re continuing from How to Build a Django and Gunicorn Application with Docker, you will have already completed Step 1 and can skip ahead to Step 2 to configure the second app server.

注意:如果您要继续学习如何使用Docker构建Django和Gunicorn应用程序 ,那么您已经完成了步骤1,可以跳到步骤2以配置第二个应用程序服务器。

Start by logging in to the first of the two Django application servers and using git to clone the polls-docker branch of the Django Tutorial Polls App GitHub repository. This repo contains code for the Django documentation’s sample Polls application. The polls-docker branch contains a Dockerized version of the Polls app. To learn how the Polls app was modified to work effectively in a containerized environment, please see How to Build a Django and Gunicorn Application with Docker.

首先登录到两个Django应用程序服务器中的第一个服务器,然后使用git克隆Django Tutorial Polls App GitHub存储库的polls-docker分支。 此仓库包含Django文档的示例Polls应用程序的代码 。 polls-docker docker分支包含Polls应用程序的Dockerized版本。 要了解如何修改Polls应用程序以使其在容器化环境中有效运行,请参阅如何使用Docker构建Django和Gunicorn应用程序 。

  • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

    git clone --single-branch --branch民意调查-docker https://github.com/do-community/django-polls.git

Navigate into the django-polls directory:

导航到django-polls目录:

cd django-polls

This directory contains the Django application Python code, a Dockerfile that Docker will use to build the container image, as well as an env file that contains a list of environment variables to be passed into the container’s running environment. Inspect the Dockerfile using cat:

该目录包含Django应用程序Python代码,Docker将用于构建容器映像的Dockerfile以及包含将传递到容器的运行环境中的环境变量列表的env文件。 使用cat检查Dockerfile

cat Dockerfile

   
Output
FROM python:3.7.4-alpine3.10 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 CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi"]

This Dockerfile uses the official Python 3.7.4 Docker image as a base, and installs Django and Gunicorn’s Python package requirements, as defined in the django-polls/requirements.txt file. It then removes some unnecessary build files, copies the application code into the image, and sets the execution PATH. Finally, it declares that port 8000 will be used to accept incoming container connections, and runs gunicorn with 3 workers, listening on port 8000.

该Dockerfile使用官方的Python 3.7.4 Docker映像作为基础,并安装django-polls/requirements.txt文件中定义的Django和Gunicorn的Python软件包要求。 然后,它将删除一些不必要的构建文件,将应用程序代码复制到映像中,并设置执行PATH 。 最后,它声明端口8000将用于接受传入的容器连接,并与3个工人一起运行gunicorn ,监听端口8000

To learn more about each of the steps in this Dockerfile, please see Step 6 of How to Build a Django and Gunicorn Application with Docker.

要了解有关此Dockerfile中每个步骤的更多信息,请参见如何使用Docker构建Django和Gunicorn应用程序的步骤6。

Now, build the image using docker build:

现在,使用docker builddocker build映像:

  • docker build -t polls .

    docker build -t 民意调查 。

We name the image polls using the -t flag and pass in the current directory as a build context, the set of files to reference when constructing the image.

我们使用-t标志命名图像polls ,并将当前目录作为构建上下文 (构建图像时要参考的文件集)传递。

After Docker builds and tags the image, list available images using docker images:

Docker构建并标记映像后,使用docker images列出可用docker images

docker images

You should see the polls image listed:

您应该看到列出的polls图像:


   
Output
REPOSITORY TAG IMAGE ID CREATED SIZE polls latest 80ec4f33aae1 2 weeks ago 197MB python 3.7.4-alpine3.10 f309434dea3a 8 months ago 98.7MB

Before we run the Django container, we need to configure its running environment using the env file present in the current directory. This file will be passed into the docker run command used to run the container, and Docker will inject the configured environment variables into the container’s running environment.

在运行Django容器之前,我们需要使用当前目录中存在的env文件配置其运行环境。 该文件将传递到用于运行容器的docker run命令中,并且Docker会将配置好的环境变量注入到容器的运行环境中。

Open the env file with nano or your favorite editor:

打开env与文件nano或你喜欢的编辑器:

nano env

We’ll be configuring the file like so, and you’ll need to add some additional values as outlined below.

我们将像这样配置文件,并且您需要添加一些其他值,如下所示。

django-polls/env
django-polls / env
DJANGO_SECRET_KEY=
DEBUG=True
DJANGO_ALLOWED_HOSTS=
DATABASE_ENGINE=postgresql_psycopg2
DATABASE_NAME=polls
DATABASE_USERNAME=
DATABASE_PASSWORD=
DATABASE_HOST=
DATABASE_PORT=
STATIC_ACCESS_KEY_ID=
STATIC_SECRET_KEY=
STATIC_BUCKET_NAME=
STATIC_ENDPOINT_URL=
DJANGO_LOGLEVEL=info

Fill in the missing values for the following keys:

填写以下键的缺失值:

  • 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: This variable secures the app and prevents HTTP Host header attacks. For testing purposes, set this to *, a wildcard that will match all hosts. In production you should set this to your_domain.com. To learn more about this Django setting, consult Core Settings from the Django docs.

    DJANGO_ALLOWED_HOSTS :此变量可保护应用程序安全并防止HTTP主机标头攻击。 为了进行测试,请将其设置为* ,这将匹配所有主机。 在生产中,应将其设置为your_domain.com 。 要了解有关此Django设置的更多信息,请查阅Django文档中的Core Settings 。

  • DATABASE_USERNAME: Set this to the PostgreSQL database user created in the prerequisite steps.

    DATABASE_USERNAME :将此设置为在先决条件步骤中创建的PostgreSQL数据库用户。

  • DATABASE_NAME: Set this to polls or the name of the PostgreSQL database created in the prerequisite steps.

    DATABASE_NAME :将此设置为polls或先决条件步骤中创建的PostgreSQL数据库的名称。

  • DATABASE_PASSWORD: Set this to the PostgreSQL user password created in the prerequisite steps.

    DATABASE_PASSWORD :将此设置为在先决步骤中创建的PostgreSQL用户密码。

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

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

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

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

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

    STATIC_ACCESS_KEY_ID :将其设置为S3存储桶或Space的访问密钥。

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

    STATIC_SECRET_KEY :将其设置为您的S3存储桶或Space的访问密钥Secret。

  • STATIC_BUCKET_NAME: Set this to your S3 bucket or Space name.

    STATIC_BUCKET_NAME :将其设置为您的S3存储桶或空间名称。

  • STATIC_ENDPOINT_URL: Set this to the appropriate S3 bucket or Space endpoint URL, for example https://space-name.nyc3.digitaloceanspaces.com if your Space is located in the nyc3 region.

    STATIC_ENDPOINT_URL :如果您的Space位于nyc3区域, STATIC_ENDPOINT_URL设置为适当的S3存储桶或Space终结点URL,例如https:// space-name .nyc3.digitaloceanspaces.com

Once you’ve finished editing, 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 polls sh -c "python manage.py makemigrations && python manage.py migrate"

We run the polls:latest container image, pass in the environment variable file we just modified, 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. If you’re running this for the first time you should see:

我们运行polls:latest容器映像,传入我们刚刚修改的环境变量文件,并使用sh -c "python manage.py makemigrations && python manage.py migrate"覆盖Dockerfile命令,这将创建由定义的数据库架构应用程式码。 如果是第一次运行此程序,则应该看到:


   
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.

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

If you’re running migrate a subsequent time, Django will perform a no-op unless the database schema has changed.

如果您随后进行migrate ,则除非数据库模式已更改,否则Django将不执行操作。

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 polls 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

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. Note that this may take a bit of time to complete.

最后,我们将为应用程序生成静态文件,并使用collectstatic将它们上传到DigitalOcean Space。 请注意,这可能需要一些时间才能完成。

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

After these files are generated and uploaded, you’ll receive the following output.

生成并上传这些文件后,您将收到以下输出。


   
Output
121 static files copied.

We can now run the app:

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

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

   
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 polls container.

在这里,我们运行在Dockerfile中定义的默认命令gunicorn --bind :8000 --workers 3 mysite.wsgi:application ,并公开容器端口8000以便Ubuntu服务器上的端口80映射到polls容器的端口8000 。 。

You should now be able to navigate to the polls app using your web browser by typing http://APP_SERVER_1_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.

现在,您应该可以使用Web浏览器通过在URL栏中键入http:// APP_SERVER_1_IP来导航到polls应用程序。 由于没有为/路径定义路由,因此很可能会收到404 Page Not Found错误。

Warning: When using the UFW firewall with Docker, Docker bypasses any configured UFW firewall rules, as documented in this GitHub issue. This explains why you have access to port 80 of your server, even though you haven’t explicitly created a UFW access rule in any prerequisite step. In Step 5 we will address this security hole by patching the UFW configuration. If you are not using UFW and are using DigitalOcean’s Cloud Firewalls, you can safely ignore this warning.

警告:将UFW防火墙与Docker结合使用时,Docker会绕过所有已配置的UFW防火墙规则,如本GitHub问题中所述 。 这说明了为什么即使没有在任何先决步骤中明确创建UFW访问规则,也可以访问服务器的端口80 。 在步骤5中,我们将通过修补UFW配置来解决此安全漏洞。 如果您不使用UFW,而是使用DigitalOcean的Cloud Firewalls ,则可以安全地忽略此警告。

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

导航到http:// APP_SERVER_1_IP /polls以查看“轮询”应用程序界面:

To view the administrative interface, visit http://APP_SERVER_1_IP/admin. You should see the Polls app admin authentication window:

要查看管理界面,请访问http:// APP_SERVER_1_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.

探索完成后,在运行Docker容器的终端窗口中按CTRL+C可以杀死该容器。

Now that you’ve confirmed that the app container runs as expected, you can run it in detached mode, which will run it in the background and allow you to log out of your SSH session:

现在,您已经确认应用程序容器可以按预期运行,可以在分离模式下运行它,它将在后台运行,并允许您退出SSH会话:

docker run -d --rm --name polls --env-file env -p 80:8000 polls

The -d flag instructs Docker to run the container in detached mode, the -rm flag cleans up the container’s filesystem after the container exits, and we name the container polls.

-d标志指示Docker以分离模式运行容器, -rm标志在容器退出后清理容器的文件系统,我们将容器命名为polls

Log out of the first Django app server, and navigate to http://APP_SERVER_1_IP/polls to confirm that the container is running as expected.

注销第一台Django应用服务器,并导航至http:// APP_SERVER_1_IP /polls以确认该容器正在按预期运行。

Now that your first Django app server is up and running, you can set up your second Django app server.

现在您的第一个Django应用程序服务器已启动并正在运行,您可以设置第二个Django应用程序服务器。

第2步-配置第二个Django应用服务器 (Step 2 — Configuring the Second Django Application Server)

Since many of the commands to set up this server will be the same as those in the previous step, they will be presented here in abbreviated form. Please review Step 1 for more information on any particular command in this step.

由于设置此服务器的许多命令将与上一步中的命令相同,因此此处将以缩写形式显示它们。 请查看步骤1 ,以获取有关此步骤中任何特定命令的更多信息。

Begin by logging in to the second Django application server.

首先登录第二个 Django应用程序服务器。

Clone the polls-docker branch of the django-polls GitHub repository:

克隆django-polls GitHub存储库的polls-docker分支:

  • git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git

    git clone --single-branch --branch民意调查-docker https://github.com/do-community/django-polls.git

Navigate into the django-polls directory:

导航到django-polls目录:

cd django-polls

Build the image using docker build:

使用docker build来构建镜像:

  • docker build -t polls .

    docker build -t 民意调查 。

Open the env file with nano or your favorite editor:

打开env与文件nano或你喜欢的编辑器:

nano env
django-polls/env
django-polls / env
DJANGO_SECRET_KEY=
DEBUG=True
DJANGO_ALLOWED_HOSTS=
DATABASE_ENGINE=postgresql_psycopg2
DATABASE_NAME=polls
DATABASE_USERNAME=
DATABASE_PASSWORD=
DATABASE_HOST=
DATABASE_PORT=
STATIC_ACCESS_KEY_ID=
STATIC_SECRET_KEY=
STATIC_BUCKET_NAME=
STATIC_ENDPOINT_URL=
DJANGO_LOGLEVEL=info

Fill in the missing values as in Step 1. When you’ve finished editing, save and close the file.

按照步骤1填写缺少的值。 完成编辑后,保存并关闭文件。

Finally, run the app container in detached mode:

最后,以分离模式运行应用程序容器:

docker run -d --rm --name polls --env-file env -p 80:8000 polls

Navigate to http://APP_SERVER_2_IP/polls to confirm that the container is running as expected. You can safely log out of the second app server without terminating your running container.

导航到http:// APP_SERVER_2_IP /polls以确认该容器正在按预期运行。 您可以安全退出第二台应用服务器,而无需终止正在运行的容器。

With both Django app containers up and running, you can move on to configuring the Nginx reverse proxy container.

随着Django应用容器的启动和运行,您可以继续配置Nginx反向代理容器。

步骤3 —配置Nginx Docker容器 (Step 3 — Configuring the Nginx Docker Container)

Nginx is a versatile web server that offers a number of features including reverse proxying, load balancing, and caching. In this tutorial we’ve offloaded Django’s static assets to object storage, so we won’t use Nginx’s caching capabilities. However, we will use Nginx as a reverse proxy to our two backend Django app servers, and distribute incoming requests between them. In addition, Nginx will perform TLS termination and redirection using a TLS certificate provisioned by Certbot. This means that it will force clients to use HTTPS, redirecting incoming HTTP requests to port 443. It will then decrypt HTTPS requests and proxy them to the upstream Django servers.

Nginx是一种多功能Web服务器,它提供许多功能,包括反向代理 , 负载平衡和缓存 。 在本教程中,我们将Django的静态资产卸载到了对象存储中,因此我们将不使用Nginx的缓存功能。 但是,我们将使用Nginx作为两个后端Django应用服务器的反向代理,并在它们之间分配传入的请求。 此外,Nginx将使用Certbot提供的TLS证书执行TLS终止和重定向。 这意味着它将强制客户端使用HTTPS,将传入的HTTP请求重定向到端口443。然后,它将解密HTTPS请求并将其代理到上游Django服务器。

In this tutorial we’ve made the design decision to decouple the Nginx containers from the backend servers. Depending on your use case, you may choose to run the Nginx container on one of the Django app servers, proxying requests locally, as well as to the other Django server. Another possible architecture would be running two Nginx containers, one on each backend server, with a cloud load balancer in front. Each architecture presents different security and performance advantages, and you should load test your system to discover bottlenecks. The flexible architecture described in this tutorial allows you to scale both the backend Django app layer, as well as the Nginx proxying layer. Once the single Nginx container becomes a bottleneck, you can scale out to multiple Nginx proxies, and add a cloud load balancer or fast L4 load balancer like HAProxy.

在本教程中,我们已做出设计决策,以将Nginx容器与后端服务器分离。 根据您的用例,您可以选择在其中一个Django应用程序服务器上运行Nginx容器,在本地代理请求以及对其他Django服务器的请求。 另一种可能的架构是运行两个Nginx容器,每个后端服务器上一个,前面有一个云负载均衡器 。 每种体系结构都具有不同的安全性和性能优势,您应该对系统进行负载测试以发现瓶颈。 本教程中描述的灵活架构允许您扩展后端Django应用程序层以及Nginx代理层。 一旦单个Nginx容器成为瓶颈,您就可以扩展到多个Nginx代理,并添加云负载均衡器或快速的L4负载均衡器(如HAProxy) 。

With both Django app servers up and running, we can begin setting up the Nginx proxy server. Log in to your proxy server and create a directory called conf:

在Django应用服务器都已启动并运行的情况下,我们可以开始设置Nginx代理服务器。 登录到代理服务器并创建一个名为conf的目录:

mkdir conf

Create a configuration file called nginx.conf using nano or your favorite editor:

使用nano或您喜欢的编辑器创建一个名为nginx.conf的配置文件:

nano conf/nginx.conf

Paste in the following Nginx configuration:

粘贴以下Nginx配置:

conf/nginx.conf
conf / nginx.conf
upstream django {
    server APP_SERVER_1_IP;
    server APP_SERVER_2_IP;
}

server {
    listen 80 default_server;
    return 444;
}

server {
    listen 80;
    listen [::]:80;
    server_name your_domain.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name your_domain.com;

    # SSL
    ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;

    ssl_session_cache shared:le_nginx_SSL:10m;
    ssl_session_timeout 1440m;
    ssl_session_tickets off;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";

    client_max_body_size 4G;
    keepalive_timeout 5;

        location / {
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_set_header X-Forwarded-Proto $scheme;
          proxy_set_header Host $http_host;
          proxy_redirect off;
          proxy_pass http://django;
        }

    location ^~ /.well-known/acme-challenge/ {
        root /var/www/html;
    }

}

These upstream, server, and location blocks configure Nginx to redirect HTTP requests to HTTPS, and load balance them across the two Django app servers configured in Steps 1 and 2. To learn more about Nginx configuration file structure, please refer to this article on Understanding the Nginx Configuration File Structure and Configuration Contexts. Additionally, this article on Understanding Nginx Server and Location Block Selection Algorithms may be helpful.

这些upstreamserverlocation块将Nginx配置为将HTTP请求重定向到HTTPS,并在步骤1和2中配置的两个Django应用服务器之间进行负载平衡。要了解有关Nginx配置文件结构的更多信息,请参阅本文了解Nginx配置文件的结构和配置上下文 。 此外,这篇有关了解Nginx服务器和位置块选择算法的文章可能会有所帮助。

This configuration was assembled from sample configuration files provided by Gunicorn, Cerbot, and Nginx and is meant as a minimal Nginx configuration to get this architecture up and running. Tuning this Nginx configuration goes beyond the scope of this article, but you can use a tool like NGINXConfig to generate performant and secure Nginx configuration files for your architecture.

这个配置在从提供示例配置文件组装Gunicorn , Cerbot和Nginx的 ,并意味着作为最小的Nginx配置得到这个架构和运行。 调整Nginx配置超出了本文的范围,但是您可以使用NGINXConfig之类的工具来为您的体系结构生成高性能和安全的Nginx配置文件。

The upstream block defines the group of servers used to proxy requests to using the proxy_pass directive:

upstream块定义用于使用proxy_pass指令代理请求的服务器组:

conf/nginx.conf
conf / nginx.conf
upstream django {
    server APP_SERVER_1_IP;
    server APP_SERVER_2_IP;
}
. . .

In this block we name the upstream django and include the IP addresses of both Django app servers. If the app servers are running on DigitalOcean and have VPC Networking enabled, you should use their private IP addresses here. To learn how to enable VPC Networking on DigitalOcean, please see How to Enable VPC Networking on Existing Droplets.

在此块中,我们命名上游django并包括两个Django应用程序服务器的IP地址。 如果应用程序服务器在DigitalOcean上运行并启用了VPC网络,则应在此处使用其专用IP地址。 要了解如何在DigitalOcean上启用VPC网络,请参阅如何在现有Droplet上启用VPC网络 。

The first server block captures requests that do not match your domain and terminates the connection. For example, a direct HTTP request to your server’s IP address would be handled by this block:

第一个server块捕获与您的域不匹配的请求,并终止连接。 例如,此块将处理对服务器IP地址的直接HTTP请求:

conf/nginx.conf
conf / nginx.conf
. . .
server {
    listen 80 default_server;
    return 444;
}
. . .

The next server block redirects HTTP requests to your domain to HTTPS using an HTTP 301 redirect. These requests are then handled by the final server block:

下一个server块使用HTTP 301重定向将对您域的HTTP请求重定向到HTTPS。 然后,这些请求由最终server块处理:

conf/nginx.conf
conf / nginx.conf
. . .
server {
    listen 80;
    listen [::]:80;
    server_name your_domain.com;
    return 301 https://$server_name$request_uri;
}
. . .

These two directives define the paths to the TLS certificate and secret key. These will be provisioned using Certbot and mounted into the Nginx container in the next step.

这两个指令定义了TLS证书和密钥的路径。 这些将使用Certbot进行设置,并在下一步中安装到Nginx容器中。

conf/nginx.conf
conf / nginx.conf
. . .
ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
. . .

These parameters are SSL security defaults recommended by Certbot. To learn more about them, please see Module ngx_http_ssl_module from the Nginx docs. Mozilla’s Security/Server Side TLS is another helpful guide that you can use to tune your SSL configuration.

这些参数是Certbot建议的SSL安全默认值。 要了解有关它们的更多信息,请参见Nginx文档中的ngx_http_ssl_module模块 。 Mozilla的安全性/服务器端TLS是另一个有用的指南,可用于调整SSL配置。

conf/nginx.conf
conf / nginx.conf
. . .
    ssl_session_cache shared:le_nginx_SSL:10m;
    ssl_session_timeout 1440m;
    ssl_session_tickets off;

    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;

    ssl_ciphers "ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384";
. . .

These two directives from Gunicorn’s sample Nginx configuration set the maximum allowed size of the client request body and assign the timeout for keep-alive connections with the client. Nginx will close connections with the client after keepalive_timeout seconds.

Gunicorn的示例Nginx配置中的这两个指令设置了客户端请求主体的最大允许大小,并为与客户端保持活动连接指定了超时。 Nginx将在keepalive_timeout秒后关闭与客户端的连接。

conf/nginx.conf
conf / nginx.conf
. . .
client_max_body_size 4G;
keepalive_timeout 5;
. . .

The first location block instructs Nginx to proxy requests to the upstream django servers over HTTP. It additionally preserves client HTTP headers that capture the originating IP address, protocol used to connect, and target host:

第一个location块指示Nginx通过HTTP将请求代理到upstream django服务器。 此外,它还会保留捕获原始IP地址,用于连接的协议以及目标主机的客户端HTTP标头:

conf/nginx.conf
conf / nginx.conf
. . .
location / {
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_set_header Host $http_host;
    proxy_redirect off;
    proxy_pass http://django;
}
. . .

To learn more about these directives, please see Deploying Gunicorn and Module ngx_http_proxy_module from the Nginx docs.

要了解有关这些指令的更多信息,请参见Nginx文档中的Deploying Gunicorn and Module ngx_http_proxy_module 。

The final location block captures requests to the /well-known/acme-challenge/ path, used by Certbot for HTTP-01 challenges to verify your domain with Let’s Encrypt and provision or renew TLS certificates. For more information on the HTTP-01 challenge used by Certbot, please see Challenge Types from the Let’s Encrypt docs.

最终的location块捕获对/well-known/acme-challenge/路径的请求,Certbot将其用于HTTP-01挑战,以使用Let's Encrypt加密和提供或更新TLS证书来验证您的域。 有关Certbot使用的HTTP-01质询的更多信息,请参阅“让我们加密”文档中的质询类型 。

conf/nginx.conf
conf / nginx.conf
. . .
location ^~ /.well-known/acme-challenge/ {
        root /var/www/html;
}

Once you’ve finished editing, save and close the file.

完成编辑后,保存并关闭文件。

You can now use this configuration file to run an Nginx Docker container. In this tutorial we’ll use the nginx:1.19.0 image, version 1.19.0 of the official Docker image maintained by Nginx.

现在,您可以使用此配置文件运行Nginx Docker容器。 在本教程中,我们将使用Nginx维护的官方Docker映像 1.19.0版本的nginx:1.19.0映像。

When we run the container for the first time, Nginx will throw an error and fail as we haven’t yet provisioned the certificates defined in the configuration file. However, we’ll still run the command to download the Nginx image locally and test that everything else is functioning correctly:

当我们第一次运行该容器时,Nginx将抛出一个错误并失败,因为我们尚未配置配置文件中定义的证书。 但是,我们仍将运行命令以在本地下载Nginx图像并测试其他所有功能是否正常运行:

docker run --rm --name nginx -p 80:80 -p 443:443 \
    -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \
    -v /var/www/html:/var/www/html \
    nginx:1.19.0

Here we name the container nginx and map the host ports 80 and 443 to the respective container ports. The -v flag mounts the config file into the Nginx container at /etc/nginx/conf.d/nginx.conf, which the Nginx image is preconfigured to load. It is mounted in ro or “read only” mode, so the container cannot modify the file. The web root directory /var/www/html is also mounted into the container. Finally nginx:1.19.0 instructs Docker to pull and run the nginx:1.19.0 image from Dockerhub.

在这里,我们将容器命名为nginx并将主机端口80443映射到相应的容器端口。 -v标志将配置文件安装到/etc/nginx/conf.d/nginx.conf的Nginx容器中,该容器已预先配置为要加载Nginx映像。 它以ro或“只读”模式安装,因此容器无法修改文件。 Web根目录/var/www/html也已安装到容器中。 最后, nginx:1.19.0指示Docker从Dockerhub中拉出并运行nginx:1.19.0映像。

Docker will pull and run the image, then Nginx will throw an error when it doesn’t find the configured TLS certificate and secret key. In the next step we’ll provision these using a Dockerized Certbot client and the Let’s Encrypt certificate authority.

Docker将拉并运行该映像,然后Nginx在找不到已配置的TLS证书和密钥时会抛出错误。 在下一步中,我们将使用Dockerized Certbot客户端和Let's Encrypt证书颁发机构来提供这些证书。

第4步-配置Certbot,让我们加密证书续订 (Step 4 — Configuring Certbot and Let’s Encrypt Certificate Renewal)

Certbot is a Let’s Encrypt client developed by the Electronic Frontier Foundation. It provisions free TLS certificates from the Let’s Encrypt certificate authority which allow browsers to verify the identity of your web servers. Given that we have Docker installed on our Nginx proxy server, we’ll use the Certbot Docker image to provision and renew the TLS certificates.

Certbot是由电子前沿基金会开发的“让我们加密”客户端。 它从Let's Encrypt证书颁发机构提供免费的TLS证书,该证书颁发机构允许浏览器验证Web服务器的身份。 鉴于我们已在Nginx代理服务器上安装了Docker,我们将使用Certbot Docker映像来配置和更新TLS证书。

Begin by ensuring that you have a DNS A record mapped to the proxy server’s public IP address. Then, on your proxy server, provision a staging version of the certificates using the certbot Docker image:

首先,确保您已将DNS A记录映射到代理服务器的公用IP地址。 然后,在代理服务器上,使用certbot Docker映像配置证书的暂存版本:

docker run -it --rm -p 80:80 --name certbot \
         -v "/etc/letsencrypt:/etc/letsencrypt" \
         -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
         certbot/certbot certonly --standalone --staging -d your_domain.com

This command runs the certbot Docker image in interactive mode, and forwards port 80 on the host to container port 80. It creates and mounts two host directories into the container: /etc/letsencrypt/ and /var/lib/letsencrypt/. certbot is run in standalone mode, without Nginx, and will use the Let’s Encrypt staging servers to perform domain validation.

此命令以交互方式运行certbot Docker映像,并将主机上的端口80转发到容器端口80 。 它创建两个主机目录并将其挂载到容器中: /etc/letsencrypt//var/lib/letsencrypt/certbot在没有Nginx的standalone模式下运行,并将使用Let's Encrypt staging服务器执行域验证。

When prompted, enter your email address and agree to the Terms of Service. If domain validation was successful, you should see the following output:

出现提示时,输入您的电子邮件地址并同意服务条款。 如果域验证成功,则应该看到以下输出:


   
Output
Obtaining a new certificate Performing the following challenges: http-01 challenge for stubb.dev Waiting for verification... Cleaning up challenges IMPORTANT NOTES: - Congratulations! Your certificate and chain have been saved at: /etc/letsencrypt/live/your_domain.com/fullchain.pem Your key file has been saved at: /etc/letsencrypt/live/your_domain.com/privkey.pem Your cert will expire on 2020-09-15. To obtain a new or tweaked version of this certificate in the future, simply run certbot again. To non-interactively renew *all* of your certificates, run "certbot renew" - Your account credentials have been saved in your Certbot configuration directory at /etc/letsencrypt. You should make a secure backup of this folder now. This configuration directory will also contain certificates and private keys obtained by Certbot so making regular backups of this folder is ideal.

You can inspect the certificate using cat:

您可以使用cat检查证书:

sudo cat /etc/letsencrypt/live/your_domain.com/fullchain.pem

With the TLS certificate provisioned, we can test the Nginx configuration assembled in the previous step:

配备TLS证书后,我们可以测试在上一步中组装的Nginx配置:

docker run --rm --name nginx -p 80:80 -p 443:443 \
    -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \
    -v /etc/letsencrypt:/etc/letsencrypt \
    -v /var/lib/letsencrypt:/var/lib/letsencrypt \
    -v /var/www/html:/var/www/html \
    nginx:1.19.0

This is the same command run in Step 3, with the addition of both recently created Let’s Encrypt directories.

这与步骤3中运行的命令相同,并添加了两个最近创建的Let's Encrypt目录。

Once Nginx is up and running, navigate to http://your_domain.com. You may receive a warning in your browser that the certificate authority is invalid. This is expected as we’ve provisioned staging certificates and not production Let’s Encrypt certificates. Check the URL bar of your browser to confirm that your HTTP request was redirected to HTTPS.

Nginx启动并运行后,导航至http:// your_domain.com 。 您可能会在浏览器中收到一条警告,指出证书颁发机构无效。 这是预期的,因为我们已设置了过渡证书而不是生产的“让我们加密”证书。 检查浏览器的URL栏,以确认您的HTTP请求已重定向到HTTPS。

Hit CTRL+C in your terminal to quit Nginx, and run the certbot client again, this time omitting the --staging flag:

在终端中按CTRL+C退出Nginx,然后再次运行certbot客户端,这次省略--staging标志:

docker run -it --rm -p 80:80 --name certbot \
         -v "/etc/letsencrypt:/etc/letsencrypt" \
         -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
         certbot/certbot certonly --standalone -d your_domain.com

When prompted to either keep the existing certificate or renew and replace it, hit 2 to renew it and then ENTER to confirm your choice.

当提示您保留现有证书或续订并替换它时,请按2进行续订,然后按ENTER确认选择。

With the production TLS certificate provisioned, run the Nginx server once again:

设置生产TLS证书后,再次运行Nginx服务器:

docker run --rm --name nginx -p 80:80 -p 443:443 \
    -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \
    -v /etc/letsencrypt:/etc/letsencrypt \
    -v /var/lib/letsencrypt:/var/lib/letsencrypt \
    -v /var/www/html:/var/www/html \
    nginx:1.19.0

In your browser, navigate to http://your_domain.com. In the URL bar, confirm that the HTTP request has been redirected to HTTPS. Given that the Polls app has no default route configured, you should see a Django Page not found error. Navigate to https://your_domain.com/polls and you’ll see the standard Polls app interface:

在浏览器中,导航到http:// your_domain.com 。 在URL栏中,确认HTTP请求已重定向到HTTPS。 鉴于“轮询”应用未配置默认路由,您应该看到Django Page not found错误。 导航到https:// your_domain.com /polls ,您将看到标准的Polls应用程序界面:

At this point you’ve provisioned a production TLS certificate using the Certbot Docker client, and are reverse proxying and load balancing external requests to the two Django app servers.

至此,您已经使用Certbot Docker客户端配置了生产TLS证书,并且正在反向代理和平衡对两个Django应用服务器的外部请求。

Let’s Encrypt certificates expire every 90 days. To ensure that your certificate remains valid, you should renew it regularly before its scheduled expiry. With Nginx running, you should use the Certbot client in webroot mode instead of standalone mode. This means that Certbot will perform validation by creating a file in the /var/www/html/.well-known/acme-challenge/ directory, and the Let’s Encrypt validation requests to this path will be captured by the location rule defined in the Nginx config in Step 3. Certbot will then rotate certificates, and you can reload Nginx so that it uses this newly provisioned certificate.

让我们加密证书每90天过期一次。 为了确保您的证书仍然有效,您应该在计划的到期日之前定期对其进行续订。 在运行Nginx的情况下,应该在webroot模式下而不是standalone模式下使用Certbot客户端。 这意味着,Certbot将创建在一个文件执行验证/var/www/html/.well-known/acme-challenge/目录中,让我们加密验证请求,这条道路将被捕获location在定义的规则第3步中的 Nginx配置。 然后,Certbot将轮换证书,您可以重新加载Nginx,以便它使用此新提供的证书。

There are multiple ways to automate this procedure and the automatic renewal of TLS certificates goes beyond the scope of this tutorial. For a similar process using the cron scheduling utility, please see Step 6 of How To Secure a Containerized Node.js Application with Nginx, Let’s Encrypt, and Docker Compose.

有多种方法可以使此过程自动化,并且TLS证书的自动续订超出了本教程的范围。 有关使用cron调度实用程序的类似过程,请参阅如何使用Nginx,Let's Encrypt和Docker Compose保护容器化Node.js应用程序的步骤6。

In your terminal, hit CTRL+C to kill the Nginx container. Run it again in detached mode by appending the -d flag:

在您的终端中,按CTRL+C杀死Nginx容器。 通过附加-d标志,以分离模式再次运行它:

docker run --rm --name nginx -d -p 80:80 -p 443:443 \
    -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \
    -v /etc/letsencrypt:/etc/letsencrypt \
    -v /var/lib/letsencrypt:/var/lib/letsencrypt \
  -v /var/www/html:/var/www/html \
    nginx:1.19.0

With Nginx running in the background, use the following command to perform a dry run of the certificate renewal procedure:

在Nginx在后台运行的情况下,使用以下命令对证书续订过程执行空运行:

docker run -it --rm --name certbot \
    -v "/etc/letsencrypt:/etc/letsencrypt" \
  -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
  -v "/var/www/html:/var/www/html" \
  certbot/certbot renew --webroot -w /var/www/html --dry-run

We use the --webroot plugin, specify the web root path, and use the --dry-run flag to verify that everything is working correctly without actually performing the certificate renewal.

我们使用--webroot插件,指定Web根目录路径,并使用--dry-run标志来验证一切是否正常运行,而无需实际执行证书更新。

If the renewal simulation succeeds, you should see the following output:

如果续订模拟成功,您应该看到以下输出:


   
Output
Cert not due for renewal, but simulating renewal for dry run Plugins selected: Authenticator webroot, Installer None Renewing an existing certificate Performing the following challenges: http-01 challenge for your_domain.com Using the webroot path /var/www/html for all unmatched domains. Waiting for verification... Cleaning up challenges - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - new certificate deployed without reload, fullchain is /etc/letsencrypt/live/your_domain.com/fullchain.pem - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates below have not been saved.) Congratulations, all renewals succeeded. The following certs have been renewed: /etc/letsencrypt/live/your_domain.com/fullchain.pem (success) ** DRY RUN: simulating 'certbot renew' close to cert expiry ** (The test certificates above have not been saved.) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

In a production setting, after renewing certificates, you should reload Nginx so that the changes take effect. To reload Nginx, run the following command:

在生产设置中,续订证书后,应重新加载Nginx,以使更改生效。 要重新加载Nginx,请运行以下命令:

docker kill -s HUP nginx

This command will send a HUP Unix signal to the Nginx process running inside of the nginx Docker container. Upon receiving this signal, Nginx will reload its configuration and renewed certificates.

此命令将一个发送HUP Unix的信号到的Nginx的运行过程内nginx泊坞容器。 收到此信号后,Nginx将重新加载其配置并更新证书。

With HTTPS enabled and all the components of this architecture up and running, the final step is to lock down the setup by preventing external access to the two backend app servers; all HTTP requests should flow through the Nginx proxy.

启用HTTPS并运行此架构的所有组件后,最后一步是通过阻止外部访问两个后端应用程序服务器来锁定设置。 所有HTTP请求都应流经Nginx代理。

第5步-阻止对Django App服务器的外部访问 (Step 5 — Preventing External Access to Django App Servers)

In the architecture described in this tutorial, SSL termination occurs at the Nginx proxy. This means that Nginx decrypts the SSL connection, and packets are proxied to the Django app servers unencrypted. For many use cases, this level of security is sufficient. For applications involving financial or health data, you may want to implement end-to-end encryption. You can do this by forwarding encrypted packets through the load balancer and decrypting on the app servers, or re encrypting at the proxy and once again decrypting on the Django app servers. These techniques go beyond the scope of this article, but to learn more please consult End-to-end encryption.

在本教程描述的架构中,SSL终止发生在Nginx代理上。 这意味着Nginx解密SSL连接,并且将数据包代理到未加密的Django应用服务器。 对于许多用例,这种级别的安全性就足够了。 对于涉及财务或健康数据的应用程序,您可能需要实施端到端加密。 您可以通过通过负载平衡器转发加密的数据包,然后在应用服务器上解密,或者在代理服务器上重新加密,然后在Django应用服务器上再次解密,来实现此目的。 这些技术超出了本文的范围,但是要了解更多信息,请查阅端到端加密 。

The Nginx proxy acts as a gateway between external traffic and the internal network. Theoretically no external clients should have direct access to the internal app servers, and all requests should flow through the Nginx server. The note in Step 1 briefly describes an open issue with Docker where Docker bypasses ufw firewall settings by default and opens ports externally, which may be insecure. To address this security concern, it’s recommended to use cloud firewalls when working with Docker-enabled servers. To get more information on creating Cloud Firewalls with DigitalOcean, consult How to Create Firewalls. You can also manipulate iptables directly instead of using ufw. To learn more about using iptables with Docker, please see Docker and iptables.

Nginx代理充当外部流量和内部网络之间的网关。 从理论上讲,任何外部客户端都不能直接访问内部应用服务器,所有请求都应流经Nginx服务器。 第1步中的注释简要描述了Docker的一个开放性问题 ,其中Docker默认情况下绕过ufw防火墙设置并从外部打开端口,这可能是不安全的。 为了解决此安全问题,建议在启用Docker的服务器上使用云防火墙 。 要获取有关使用DigitalOcean创建云防火墙的更多信息,请参阅如何创建防火墙 。 您也可以直接操作iptables而不是使用ufw 。 要了解有关在Docker上使用iptables更多信息,请参阅Docker和iptables 。

In this step we’ll modify UFW’s configuration to block external access to host ports opened by Docker. When running Django on the app servers, we passed the -p 80:8000 flag to docker, which forwards port 80 on the host to container port 8000. This also opened up port 80 to external clients, which you can verify by visiting http://your_app_server_1_IP. To prevent direct access, we’ll modify UFW’s configuration using the method described in the ufw-docker GitHub repository.

在这一步中,我们将修改UFW的配置,以阻止外部访问Docker打开的主机端口。 在应用服务器上运行Django时,我们将-p 80:8000标志传递给了docker ,后者将主机上的端口80转发至容器端口8000 。 这也为外部客户端打开了端口80 ,您可以通过访问http:// your_app_server_1_IP进行验证。 为了防止直接访问,我们将使用ufw-docker GitHub存储库中描述的方法修改UFW的配置。

Begin by logging in to the first Django app server. Then, open the /etc/ufw/after.rules file with superuser privileges, using nano or your favorite editor:

首先登录第一个Django应用服务器。 然后,使用nano或您喜欢的编辑器以超级用户权限打开/etc/ufw/after.rules文件:

sudo nano /etc/ufw/after.rules

Enter your password when prompted, and hit ENTER to confirm.

出现提示时输入密码,然后按ENTER确认。

You should see the following ufw rules:

您应该看到以下ufw规则:

/etc/ufw/after.rules
/etc/ufw/after.rules
#
# rules.input-after
#
# Rules that should be run after the ufw command line added rules. Custom
# rules should be added to one of these chains:
#   ufw-after-input
#   ufw-after-output
#   ufw-after-forward
#

# Don't delete these required lines, otherwise there will be errors
*filter
:ufw-after-input - [0:0]
:ufw-after-output - [0:0]
:ufw-after-forward - [0:0]
# End required lines

# don't log noisy services by default
-A ufw-after-input -p udp --dport 137 -j ufw-skip-to-policy-input
-A ufw-after-input -p udp --dport 138 -j ufw-skip-to-policy-input
-A ufw-after-input -p tcp --dport 139 -j ufw-skip-to-policy-input
-A ufw-after-input -p tcp --dport 445 -j ufw-skip-to-policy-input
-A ufw-after-input -p udp --dport 67 -j ufw-skip-to-policy-input
-A ufw-after-input -p udp --dport 68 -j ufw-skip-to-policy-input

# don't log noisy broadcast
-A ufw-after-input -m addrtype --dst-type BROADCAST -j ufw-skip-to-policy-input

# don't delete the 'COMMIT' line or these rules won't be processed
COMMIT

Scroll to the bottom, and paste in the following block of UFW configuration rules:

滚动到底部,然后粘贴以下UFW配置规则块:

/etc/ufw/after.rules
/etc/ufw/after.rules
. . .

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN
COMMIT
# END UFW AND DOCKER

These rules restrict public access to ports opened by Docker, and enable access from the 10.0.0.0/8, 172.16.0.0/12, and 192.168.0.0/16 private IP ranges. If you are using VPC with DigitalOcean, then Droplets in your VPC network will have access to the open port over the private network interface, but external clients will not. For more information about VPC, please see the VPC official documentation. To learn more about the rules implemented in this snippet, please see How it works? from the ufw-docker README.

这些规则限制由泊坞窗打开的端口公共接入,并且能够从接入10.0.0.0/8172.16.0.0/12192.168.0.0/16专用IP范围。 如果将VPC与DigitalOcean一起使用,则VPC网络中的Droplet可以通过专用网络接口访问开放端口,而外部客户端则不能。 有关VPC的更多信息,请参阅VPC官方文档 。 要了解有关此代码段中实施的规则的更多信息,请参阅其工作原理? 来自ufw-docker README 。

If you are not using VPC with DigitalOcean, and have entered the public IP addresses of the app servers in the upstream block of your Nginx config, you will have to explicitly modify the UFW firewall to allow traffic from the Nginx server through port 80 on the Django app servers. For guidance on creating allow rules with the UFW firewall, please see UFW Essentials: Common Firewall Rules and Commands.

如果您未将VPC与DigitalOcean一起使用,并且已在Nginx配置的upstream块中输入了应用服务器的公共IP地址,则必须显式修改UFW防火墙,以允许来自Nginx服务器的通信通过端口上的端口80进行。 Django应用服务器。 有关使用UFW防火墙创建allow规则的指导,请参阅UFW Essentials:通用防火墙规则和命令 。

When you’ve finished editing, save and close the file.

完成编辑后,保存并关闭文件。

Restart ufw so that it picks up the new configuration:

重新启动ufw ,使其采用新配置:

sudo systemctl restart ufw

Navigate to http://APP_SERVER_1_IP in your web browser to confirm that you can no longer access the app server over port 80.

在Web浏览器中导航至http:// APP_SERVER_1_IP ,以确认您不再可以通过端口80访问应用程序服务器。

Repeat this process on the second Django app server.

在第二个Django应用服务器上重复此过程。

Log out of the first app server or open another terminal window, and log in to the second Django app server. Then, open the /etc/ufw/after.rules file with superuser privileges, using nano or your favorite editor:

注销第一个应用程序服务器或打开另一个终端窗口,然后登录第二个Django应用程序服务器。 然后,使用nano或您喜欢的编辑器以超级用户权限打开/etc/ufw/after.rules文件:

sudo nano /etc/ufw/after.rules

Enter your password when prompted, and hit ENTER to confirm.

出现提示时输入密码,然后按ENTER确认。

Scroll to the bottom, and paste in the following block of UFW configuration rules:

滚动到底部,然后粘贴以下UFW配置规则块:

/etc/ufw/after.rules
/etc/ufw/after.rules
. . .

# BEGIN UFW AND DOCKER
*filter
:ufw-user-forward - [0:0]
:DOCKER-USER - [0:0]
-A DOCKER-USER -j RETURN -s 10.0.0.0/8
-A DOCKER-USER -j RETURN -s 172.16.0.0/12
-A DOCKER-USER -j RETURN -s 192.168.0.0/16

-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN

-A DOCKER-USER -j ufw-user-forward

-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12

-A DOCKER-USER -j RETURN
COMMIT
# END UFW AND DOCKER

When you’ve finished editing, save and close the file.

完成编辑后,保存并关闭文件。

Restart ufw so that it picks up the new configuration:

重新启动ufw ,使其采用新配置:

sudo systemctl restart ufw

Navigate to http://APP_SERVER_2_IP in your web browser to confirm that you can no longer access the app server over port 80.

在Web浏览器中导航至http:// APP_SERVER_2_IP ,以确认您不再可以通过端口80访问应用程序服务器。

Finally, navigate to https://your_domain_here/polls to confirm that the Nginx proxy still has access to the upstream Django servers. You should see the default Polls app interface.

最后,导航至https:// your_domain_here /polls以确认Nginx代理仍然有权访问上游Django服务器。 您应该看到默认的“轮询”应用程序界面。

结论 (Conclusion)

In this tutorial, you’ve set up a scalable Django Polls application using Docker containers. As your traffic grows and load on the system increases, you can scale each layer separately: the Nginx proxying layer, the Django backend app layer, and the PostgreSQL database layer.

在本教程中,您已经使用Docker容器设置了可扩展的Django Polls应用程序。 随着流量的增长和系统负载的增加,您可以分别扩展每个层:Nginx代理层,Django后端应用程序层和PostgreSQL数据库层。

When building a distributed system, there are often multiple design decisions you must face, and several architectures may satisfy your use case. The architecture described in this tutorial is meant as a flexible blueprint for designing scalable apps with Django and Docker.

在构建分布式系统时,通常必须面对多个设计决策,并且几种架构可能会满足您的用例。 本教程中描述的架构是用于使用Django和Docker设计可扩展应用程序的灵活蓝图。

You may wish to control the behavior of your containers when they encounter errors, or run containers automatically when your system boots. To do this, you can use a process manager like Systemd or implement restart policies. For more information about these, please see Start containers automatically from the Docker documentation.

您可能希望控制遇到错误的容器的行为,或者在系统引导时自动运行容器。 为此,您可以使用Systemd之类的进程管理器或实施重启策略。 有关这些的更多信息,请参阅Docker文档中的自动启动容器 。

When working at scale with multiple hosts running the same Docker image, it can be more efficient to automate steps using a configuration management tool like Ansible or Chef. To learn more about configuration management, please consult An Introduction to Configuration Management and Automating Server Setup with Ansible: A DigitalOcean Workshop Kit.

当与运行同一Docker映像的多个主机进行大规模协作时,使用诸如Ansible或Chef的配置管理工具来自动执行步骤会更有效。 要了解有关配置管理的更多信息,请查阅Ansible的 配置管理和服务器设置自动化简介:DigitalOcean Workshop Kit 。

Instead of building the same image on every host, you can also streamline deployment using an image registry like Docker Hub, which centrally builds, stores, and distributes Docker images to multiple servers. Along with an image registry, a continuous integration and deployment pipeline can help you build, test, and deploy images to your app servers. For more information on CI/CD, please consult An Introduction to CI/CD Best Practices.

您可以使用Docker Hub之类的映像注册表简化部署,而不是在每个主机上都构建相同的映像,它可以将Docker映像集中构建,存储和分发到多台服务器。 与映像注册表一起,持续的集成和部署管道可以帮助您构建,测试映像并将其部署到应用服务器。 有关CI / CD的更多信息,请查阅CI / CD最佳实践简介 。

翻译自: https://www.digitalocean.com/community/tutorials/how-to-scale-and-secure-a-django-application-with-docker-nginx-and-let-s-encrypt


http://www.niftyadmin.cn/n/3648094.html

相关文章

EasyJF开源团队欢迎您的加入

您想玩转JavaEE领域里最前沿的技术吗?您想与一群来自大江南北的技术狂热爱好者成为朋友吗?您愿意让您的代码、知识传播到世界每一个需要程序及程序员的角落吗?您想认识一群长期蜷伏在开源社区中各大中型软件公司的技术掌门人吗?您…

如何在Ubuntu 20.04上安装Elasticsearch,Logstash和Kibana(弹性堆栈)

介绍 (Introduction) The Elastic Stack — formerly known as the ELK Stack — is a collection of open-source software produced by Elastic which allows you to search, analyze, and visualize logs generated from any source in any format, a practice known as cen…

使用Mylyn插件管理bug

今天使用Jira的时候遇到一些问题,搜索的时候无意中发现有一个插件Mylyn可以管理Jira中的bug,于是装了一个。弄了一下午,终于可以使用了。现在把安装的过程写下来,或许对大家有用。第一步,当然是下载插件了。下载地址&a…

如何在Ubuntu 20.04上使用Docker和Caddy远程访问GUI应用程序

介绍 (Introduction) Even with the growing popularity of cloud services, the need for running native applications still exists. 即使云服务越来越流行,仍然需要运行本机应用程序。 By using noVNC and TigerVNC, you can run native applications inside a…

EasyJF与Cownew携手打造BlueFin

作为国内两个比较活跃的开源团队,EasyJF及CowNew都在各自所专注的领域里为开源社区作了不少的贡献。EasyJF开源的EasyJWeb已经推出了1.0m1正式版本,并通过在国内多个大中型项目中的成功应用,充分证明了EasyJWeb是一个优秀并适合快速开发JavaW…

零配置及惯例代替配置

这是[挑战MVC极限]EasyJWeb-1.0特性的第四篇文章,今天主要介绍零配置及惯例代替配置。配置是好还是坏首先,我承认配置是好东西,它能够通过修改程序以外的数据来改变系统的运行性质或功能,大大提高了系统的灵活性,可维护…

如何在Ubuntu 18.04上将Postfix安装和配置为仅发送SMTP服务器

介绍 (Introduction) Postfix is a mail transfer agent (MTA), an application used to send and receive email. It can be configured so that it can be used to send emails by local application only. This is useful in situations when you need to regularly send em…

超级IOC容器SuperContainer

在JavaEE乃至其它的java应用程序中,容器显得非常重要。web容器、applet容器、EJB容器等,可谓容器无处不在。  从程序员的角度来说,IOC容器是一个非常好的东西,他能使得我们非常灵活的管理组件及依赖关系。可以毫不夸张地说&…