介绍
在基于云的环境中,有多种方式来扩展和保护 Django 应用程序。 通过 scaling 水平 和运行多个副本的应用程序,您可以构建一个更易于故障和高度可用的系统,同时增加其 throughput 以便请求可以同时处理。 一个方法来横向扩展 Django 应用程序是提供额外的 _app 服务器来运行您的 Django 应用程序和其 WSGI HTTP 服务器(如 Gunicorn 或 uWSGI)。 为了在这个应用程序服务器组中路由和分发收入请求,您可以使用 负载平衡器和 [反向代理](
运行您的 Django 应用程序和 Nginx 代理程序在 Docker 容器内,确保这些组件以相同的方式行为,无论部署到哪个环境。
在本教程中,您将通过提供两个应用程序服务器,每个应用程序将运行Django和Gunicorn应用程序容器的副本,以横向扩展集装箱化的Django和Gunicorn Polls应用程序。
您还将通过提供和配置第三方代理服务器来启用 HTTPS,该服务器将运行 Nginx 反向代理容器和 Certbot客户端容器。Certbot 将从 Let's Encrypt 证书授予 Nginx 的 TLS 证书。这将确保您的网站从 SSL Labs获得高安全级别。这个代理服务器将收到您的应用程序的所有外部请求,并坐在两个 upstream Django 应用服务器前面。
前提条件
要遵循本教程,您将需要:
- 三台Ubuntu 18.04服务器:
- 有两台服务器将是应用程序服务器,用于运行你的Django和Gunicornapp. (_) ) - 一个服务器将是一个** proxy** 服务器,用于运行Nginx和 Certbot.
- 所有服务器都应有一个拥有"sudo"权限的非根用户,并有一个活动防火墙. 关于这些设置的指导,请参见此初始服务器设置指导.
- 在所有三个服务器上安装了 Docker. 关于安装多克的指南,请遵循[如何在Ubuntu 18.04上安装和使用多克]的第1步和第2步(https://andsky.com/tech/tutorials/how-to-install-and-use-docker-on-ubuntu-18-04). (_ (
)* 已注册域名. 此教程将使用
your_ domain.com
整个 您可以在 [Freenom] (http://www.freenom.com/en/index.html] 免费获得一个,或者使用您选择的域注册员. (_) ) * 有"你的域名.com"的"A" DNS记录. 指向您的** proxy** 服务器的公开IP地址 。 您可以沿用DigitalOcean DNS的介绍,了解如何将其添加到DigitalOcean的账户中的细节,如果这是你正在使用的。 - 用于存储您 Django 项目的静态文件以及此空间的一组访问密钥的 S3 对象存储桶, 如 [ DigitalOcean space] (https://www.digitalocean.com/products/spaces/ ) 。 为了学习如何创建空间,请参考如何创建空间产品文档. 要学习如何为空间创建访问密钥,请咨询与访问密钥共享访问空间. 稍作修改后,您可以使用 [django- storages] (https://django-storages.readthedocs.io/en/latest/] 插件支持的任何对象存储服务.
- 您的 Django 应用程序的 PostgreSQL 服务器实例、数据库和用户 。 稍作修改后,您可以使用任何Django支持.
- PostgreSQL数据库应该被叫作polls(或者在下面的配置文件中输入另一个值得纪念的名称),在这个教程中,数据库用户将被命名为sammy. 关于创建这些应用程序的指导,请遵循[如何用多克来构建Django和Gunicorn应用程序]的第1步(https://andsky.com/tech/tutorials/how-to-build-a-django-and-gunicorn-application-with-docker#step-1-%E2%80%94-creating-the-postgresql-database-and-user)。 您可以从三个服务器中任何一个服务器中执行这些步骤( _)
- 此教程中使用了 Digital Ocean [managed PostgreSQL Cluster] (https://www.digitalocean.com/products/managed-databases/). 要学习如何创建集群,请查阅"数字海洋"管理数据库产品文档. ) - 您也可以安装并运行自己的PostgreSQL实例. 关于在 Ubuntu 服务器上安装和管理 PostgreSQL 的指南,请参见[如何安装和使用 Ubuntu 18.04] (https://andsky.com/tech/tutorials/how-to-install-and-use-postgresql-on-ubuntu-18-04). (_). (英语)
步骤 1 – 配置第一个 Django 应用服务器
首先,我们将Django应用程序存储库克隆到第一个应用程序服务器,然后,我们将配置和构建应用程序Docker图像,并通过运行Django容器来测试应用程序。
<$>[注] 注: 如果您正在继续从 如何使用 Docker 构建 Django 和 Gunicorn 应用程序,您已经完成了步骤 1 并可以跳到 步骤 2)来配置 第二 应用程序服务器。
首先,您要登录两个 Django 应用程序服务器中的第一个,然后使用git
来克隆 Django Tutorial Polls App(GitHub 存储库)的polls-docker
分支(https://github.com/do-community/django-polls)。这个复制程序包含 Django 文档的 [样本 Polls 应用程序]的代码(https://docs.djangoproject.com/en/3.0/intro/)。
1[environment second]
2git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git
导航到‘django-polls’目录:
1[environment second]
2cd django-polls
该目录包含了Django应用程序Python代码,一个Dockerfile
,Docker将用来构建容器图像,以及一个env
文件,其中包含一份将环境变量列入容器运行环境的列表。
1[environment second]
2cat Dockerfile
1[secondary_label Output]
2[environment second]
3FROM python:3.7.4-alpine3.10
4
5ADD django-polls/requirements.txt /app/requirements.txt
6
7RUN set -ex \
8 && apk add --no-cache --virtual .build-deps postgresql-dev build-base \
9 && python -m venv /env \
10 && /env/bin/pip install --upgrade pip \
11 && /env/bin/pip install --no-cache-dir -r /app/requirements.txt \
12 && runDeps="$(scanelf --needed --nobanner --recursive /env \
13 | awk '{ gsub(/,/, "\nso:", $2); print "so:" $2 }' \
14 | sort -u \
15 | xargs -r apk info --installed \
16 | sort -u)" \
17 && apk add --virtual rundeps $runDeps \
18 && apk del .build-deps
19
20ADD django-polls /app
21WORKDIR /app
22
23ENV VIRTUAL_ENV /env
24ENV PATH /env/bin:$PATH
25
26EXPOSE 8000
27
28CMD ["gunicorn", "--bind", ":8000", "--workers", "3", "mysite.wsgi"]
此 Dockerfile 使用官方 Python 3.7.4 Docker 图像作为基础,并安装 Django 和 Gunicorn 的 Python 包要求,如django-polls/requirements.txt
文件中所定义。它然后删除一些不必要的构建文件,将应用程序代码复制到图像中,并设置执行PATH
。
有关本 Dockerfile 中的每一个步骤的更多信息,请参阅 如何使用 Docker 构建 Django 和 Gunicorn 应用程序的第 6 步。
现在,使用docker build
来构建图像:
1[environment second]
2docker build -t polls .
我们使用t
旗号命名图像调查
,并在当前目录中作为 build context 传输,这是构建图像时要参考的文件集。
在 Docker 构建和标记图像后,使用docker 图像
列出可用的图像:
1[environment second]
2docker images
你应该看到列出的民意调查
图像:
1[environment second]
2[secondary_label Output]
3REPOSITORY TAG IMAGE ID CREATED SIZE
4polls latest 80ec4f33aae1 2 weeks ago 197MB
5python 3.7.4-alpine3.10 f309434dea3a 8 months ago 98.7MB
在运行Django容器之前,我们需要使用当前目录中存在的env
文件来配置其运行环境,该文件将传入用于运行容器的docker run
命令,Docker将将配置的环境变量注入容器的运行环境。
使用nano
或您最喜欢的编辑器打开env
文件:
1[environment second]
2nano env
我们将这样配置文件,您将需要添加一些额外的值,如下所述。
1[environment second]
2[label django-polls/env]
3DJANGO_SECRET_KEY=
4DEBUG=True
5DJANGO_ALLOWED_HOSTS=
6DATABASE_ENGINE=postgresql_psycopg2
7DATABASE_NAME=polls
8DATABASE_USERNAME=
9DATABASE_PASSWORD=
10DATABASE_HOST=
11DATABASE_PORT=
12STATIC_ACCESS_KEY_ID=
13STATIC_SECRET_KEY=
14STATIC_BUCKET_NAME=
15STATIC_ENDPOINT_URL=
16DJANGO_LOGLEVEL=info
填写以下键的缺失值:
- `DJANGO-SECRET-KEY':将此设定为一个独特的、无法预测的价值,详见Django docs。 生成此密钥的一个方法是在 [可缩放 Django App (https://andsky.com/tech/tutorials/how-to-set-up-a-scalable-django-app-with-digitalocean-managed-databases-and-spaces#step-5-%E2%80%94-adjusting-the-app-settings) 的 [调整 App 设置] (https://andsky.com/tech/tutorials/how-to-set-up-a-scalable-django-app-with-digitalocean-managed-databases-and-spaces#step-5-%E2%80%94-adjusting-the-app-settings] 教程中提供。
DJANGO-ALLULED-HOSTS': 这个变量可以保护应用程序,防止HTTP主机头攻击. 为测试目的, 将此设定为QQ, 一个匹配所有主机的通配符 。 在制作中,您应该将此设定为
your_domain.com`。 欲了解更多关于Django设置的情况,请参考Django文档中的核心设置。美利坚合众国: 设定为在先决条件步骤中创建的 PostgreSQL 数据库用户 。 (_) ( )*
DATABASE-NAME':将其设定为polls'或先决条件步骤中创建的PostgreSQL数据库的名称。 (_ ( )*
DATABASE_PASWORD': 设定为在先决条件步骤中创建的 PostgreSQL 用户密码 。 () ( )*DATABASE_HOST': 将此设定为数据库的主机名。 (_) ( )*
DATABASE PORT` : 将此设定到您的数据库端口。- 统计: 设定为您的 S3 桶或 Space 访问密钥 。 ( _) ( )* `STATIC_SECRET_KEY': 将此设定在您的 S3 桶或 Space 访问密钥上 。
STIC_BUKET_NAME': 设定为您的 S3 桶或空格名 。 (_) ( )*
STATIC-ENDPOINT-URL': 设定为合适的 S3 桶或空间端点 URL, 例如https://space-name.nyc3. digital Oceanspaces.com' 如果您的空间位于
nyc3` 区域。 (_) (英语)
完成编辑后,保存并关闭文件。
现在我们将使用docker run
来取代Dockerfile中设置的CMD
,并使用manage.py makemigrations
和manage.py migrate
命令创建数据库方案:
1[environment second]
2docker run --env-file env polls sh -c "python manage.py makemigrations && python manage.py migrate"
我们运行polls:latest
容器图像,传入我们刚刚修改的环境变量文件,并用sh -c``python manage.py makemigrations && python manage.py迁移
来代替Dockerfile命令,这将创建应用程序代码定义的数据库方案。
1[environment second]
2[secondary_label Output]
3No changes detected
4Operations to perform:
5 Apply all migrations: admin, auth, contenttypes, polls, sessions
6Running migrations:
7 Applying contenttypes.0001_initial... OK
8 Applying auth.0001_initial... OK
9 Applying admin.0001_initial... OK
10 Applying admin.0002_logentry_remove_auto_add... OK
11 Applying admin.0003_logentry_add_action_flag_choices... OK
12 Applying contenttypes.0002_remove_content_type_name... OK
13 Applying auth.0002_alter_permission_name_max_length... OK
14 Applying auth.0003_alter_user_email_max_length... OK
15 Applying auth.0004_alter_user_username_opts... OK
16 Applying auth.0005_alter_user_last_login_null... OK
17 Applying auth.0006_require_contenttypes_0002... OK
18 Applying auth.0007_alter_validators_add_error_messages... OK
19 Applying auth.0008_alter_user_username_max_length... OK
20 Applying auth.0009_alter_user_last_name_max_length... OK
21 Applying auth.0010_alter_group_name_max_length... OK
22 Applying auth.0011_update_proxy_permissions... OK
23 Applying polls.0001_initial... OK
24 Applying sessions.0001_initial... OK
这表明数据库方案已成功创建。
如果您在随后的时间运行迁移
,Django 将执行禁令,除非数据库架构发生了变化。
接下来,我们将运行应用程序容器的另一个实例,并使用其内部的交互式壳创建 Django 项目的管理用户。
1[environment second]
2docker run -i -t --env-file env polls sh
这将为您提供运行容器内部的壳提示,您可以使用它来创建Django用户:
1[environment second]
2python manage.py createsuperuser
输入您的用户的用户名、电子邮件地址和密码,然后创建用户后,点击CTRL+D
,离开容器并杀死它。
最后,我们将生成应用程序的静态文件,并使用collectstatic
将其上传到DigitalOcean Space。
1[environment second]
2docker run --env-file env polls sh -c "python manage.py collectstatic --noinput"
这些文件被生成和上传后,您将收到以下输出。
1[environment second]
2[secondary_label Output]
3121 static files copied.
现在我们可以运行应用程序:
1[environment second]
2docker run --env-file env -p 80:8000 polls
1[environment second]
2[secondary_label Output]
3[2019-10-17 21:23:36 +0000] [1] [INFO] Starting gunicorn 19.9.0
4[2019-10-17 21:23:36 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1)
5[2019-10-17 21:23:36 +0000] [1] [INFO] Using worker: sync
6[2019-10-17 21:23:36 +0000] [7] [INFO] Booting worker with pid: 7
7[2019-10-17 21:23:36 +0000] [8] [INFO] Booting worker with pid: 8
8[2019-10-17 21:23:36 +0000] [9] [INFO] Booting worker with pid: 9
在这里,我们运行在Dockerfile中定义的默认命令gunicorn --bind :8000 -workers 3 mysite.wsgi:application
,并曝光容器端口8000
,以便Ubuntu服务器上的端口80
被映射到8000
的容器。
您现在应该能够通过键入URL栏中的http://APP_SERVER_1_IP
来导航到调查
应用程序,因为没有定义的路径为/
路径,您可能会收到一个404页未找到
错误,这是预期的。
<$>[警告]
警告: 使用 UFW 防火墙与 Docker 时,Docker 会绕过任何已配置的 UFW 防火墙规则,如本 GitHub 问题中文档所示。这解释了为什么您可以访问服务器的端口 80
,即使您在任何先决步骤中都没有明确创建 UFW 访问规则。
点击http://APP_SERVER_1_IP/polls
查看调查应用程序界面:
要查看管理界面,请访问http://APP_SERVER_1_IP/admin。您应该看到调查应用程序管理员身份验证窗口:
输入您用createsuperuser
命令创建的管理用户名和密码。
验证后,您可以访问 Polls 应用程序的管理界面:
请注意,对于admin
和polls
应用程序的静态资产正在直接从对象存储中交付,请参阅 Testing Spaces Static File Delivery来确认此情况。
当你完成探索时,在运行 Docker 容器的终端窗口中按CTRL + C
来杀死容器。
现在你已经确认应用容器按照预期运行,你可以运行它在 detached 模式,这将在背景中运行,并允许你退出你的 SSH 会话:
1[environment second]
2docker run -d --rm --name polls --env-file env -p 80:8000 polls
-d
旗指示Docker在分离模式下运行容器,-rm
旗在容器离开后清理容器的文件系统,我们将容器命名为调查
。
登出第一个 Django 应用服务器,然后导航到http://APP_SERVER_1_IP/polls
,以确认容器按照预期运行。
现在你的第一个Django应用服务器已启动并运行,你可以设置你的第二个Django应用服务器。
步骤 2 — 配置第二个 Django 应用服务器
由于许多设置此服务器的命令将与前一步中的命令相同,因此它们将在这里以简短的形式呈现。
首先,您可以登录到第二个Django应用程序服务器。
克隆django-polls
GitHub 存储库的polls-docker
分支:
1[environment third]
2git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git
导航到‘django-polls’目录:
1[environment third]
2cd django-polls
使用docker build
构建图像:
1[environment third]
2docker build -t polls .
使用nano
或您最喜欢的编辑器打开env
文件:
1[environment third]
2nano env
1[environment third]
2[label django-polls/env]
3DJANGO_SECRET_KEY=
4DEBUG=True
5DJANGO_ALLOWED_HOSTS=
6DATABASE_ENGINE=postgresql_psycopg2
7DATABASE_NAME=polls
8DATABASE_USERNAME=
9DATABASE_PASSWORD=
10DATABASE_HOST=
11DATABASE_PORT=
12STATIC_ACCESS_KEY_ID=
13STATIC_SECRET_KEY=
14STATIC_BUCKET_NAME=
15STATIC_ENDPOINT_URL=
16DJANGO_LOGLEVEL=info
填写缺少的值,如在 步骤 1。当你完成编辑后,保存并关闭文件。
最后,在分离模式下运行应用程序容器:
1[environment third]
2docker run -d --rm --name polls --env-file env -p 80:8000 polls
点击http://APP_SERVER_2_IP/polls
确认容器按预期运行,您可以安全地退出第二个应用服务器,而无需终止运行容器。
随着 Django 应用程序容器的启动和运行,您可以继续配置 Nginx 反向代理容器。
步骤 3 – 配置 Nginx Docker 容器
Nginx是一个多功能的网页服务器,提供了一些功能,包括 反向代理, 负载平衡,和 缓存。在本教程中,我们已将Django的静态资产下载到对象存储中,所以我们不会使用Nginx的缓存功能。然而,我们将使用Nginx作为向我们两个后端Django应用程序服务器的反向代理,并在它们之间分发接收请求。此外,Nginx将执行TLS终止(https://en.wikipedia.org/wiki/TLS_termination_proxy)和重定向使用Certbot提供的TLS证书。这意味着它将迫使客户使用HTTPS,将接收的HTTP请求重定向到端口443。
在本教程中,我们决定将 Nginx 容器从后端服务器中分开。根据您的使用情况,您可以选择在 Django 应用服务器中的一个上运行 Nginx 容器,在本地和其他 Django 服务器上进行代理请求。另一个可能的架构是运行两个 Nginx 容器,在每个后端服务器上运行一个,前面有一个云 负载平衡器 每个架构都具有不同的安全性和性能优势,您应该 负载测试您的系统发现瓶颈。本教程中描述的灵活架构允许您扩展后端 Django 应用层以及接口 Nginx 层。一旦单个 Nginx 容器变成瓶颈,您可以扩展到多个 Nginx,并
随着 Django 应用服务器的启动和运行,我们可以开始设置 Nginx 代理服务器. 登录您的代理服务器并创建一个名为 conf
的目录:
1[environment fourth]
2mkdir conf
使用nano
或您最喜欢的编辑器创建名为nginx.conf
的配置文件:
1[environment fourth]
2nano conf/nginx.conf
使用以下 Nginx 配置:
1[environment fourth]
2[label conf/nginx.conf]
3
4upstream django {
5 server APP_SERVER_1_IP;
6 server APP_SERVER_2_IP;
7}
8
9server {
10 listen 80 default_server;
11 return 444;
12}
13
14server {
15 listen 80;
16 listen [::]:80;
17 server_name your_domain.com;
18 return 301 https://$server_name$request_uri;
19}
20
21server {
22 listen 443 ssl http2;
23 listen [::]:443 ssl http2;
24 server_name your_domain.com;
25
26 # SSL
27 ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
28 ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
29
30 ssl_session_cache shared:le_nginx_SSL:10m;
31 ssl_session_timeout 1440m;
32 ssl_session_tickets off;
33
34 ssl_protocols TLSv1.2 TLSv1.3;
35 ssl_prefer_server_ciphers off;
36
37 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";
38
39 client_max_body_size 4G;
40 keepalive_timeout 5;
41
42 location / {
43 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
44 proxy_set_header X-Forwarded-Proto $scheme;
45 proxy_set_header Host $http_host;
46 proxy_redirect off;
47 proxy_pass http://django;
48 }
49
50 location ^~ /.well-known/acme-challenge/ {
51 root /var/www/html;
52 }
53
54}
这些上游
、服务器
和位置
块将 Nginx 配置为将 HTTP 请求重定向到 HTTPS,并在第 1 步和 2 步中配置的两台 Django 应用服务器上平衡其负荷。
此配置是由 Gunicorn, Cerbot和 Nginx提供的样本配置文件组合的,旨在作为一个最小的 Nginx 配置来实现该架构并运行。调整此 Nginx 配置超出了本文的范围,但您可以使用像 NGINXConfig这样的工具来为您的架构生成性能和安全的 Nginx 配置文件。
上游
块定义了用于使用proxy_pass
指令的代理请求的服务器组:
1[environment fourth]
2[label conf/nginx.conf]
3upstream django {
4 server APP_SERVER_1_IP;
5 server APP_SERVER_2_IP;
6}
7. . .
在此块中,我们将上游名称称为django,并包括 Django 应用程序服务器的 IP 地址。如果应用程序服务器在 DigitalOcean 上运行,并且已启用 VPC 网络,请在此处使用他们的私人 IP 地址。
第一个服务器
块捕捉了不匹配您的域的请求,并终止了连接,例如,直接HTTP请求到您的服务器的IP地址将由此块处理:
1[environment fourth]
2[label conf/nginx.conf]
3. . .
4server {
5 listen 80 default_server;
6 return 444;
7}
8. . .
下一个服务器
块将HTTP请求重定向到您的域名到HTTPS使用HTTP 301重定向(https://en.wikipedia.org/wiki/HTTP_301)。
1[environment fourth]
2[label conf/nginx.conf]
3. . .
4server {
5 listen 80;
6 listen [::]:80;
7 server_name your_domain.com;
8 return 301 https://$server_name$request_uri;
9}
10. . .
这两个指令定义了通往 TLS 证书和秘密密钥的路径,这些路径将通过 Certbot 提供并在下一步安装到 Nginx 容器中。
1[environment fourth]
2[label conf/nginx.conf]
3. . .
4ssl_certificate /etc/letsencrypt/live/your_domain.com/fullchain.pem;
5ssl_certificate_key /etc/letsencrypt/live/your_domain.com/privkey.pem;
6. . .
这些参数是 Certbot 推荐的 SSL 安全默认值。 有关这些参数的更多信息,请参阅 Nginx 文档中的 模块 ngx_http_ssl_module。
1[environment fourth]
2[label conf/nginx.conf]
3. . .
4 ssl_session_cache shared:le_nginx_SSL:10m;
5 ssl_session_timeout 1440m;
6 ssl_session_tickets off;
7
8 ssl_protocols TLSv1.2 TLSv1.3;
9 ssl_prefer_server_ciphers off;
10
11 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";
12. . .
Gunicorn 的 样本 Nginx 配置的这两个指令设置了客户端请求体的最大允许大小,并分配了与客户端保持连接的时间。
1[environment fourth]
2[label conf/nginx.conf]
3. . .
4client_max_body_size 4G;
5keepalive_timeout 5;
6. . .
第一个位置
块指示 Nginx 通过 HTTP 向上游 django 服务器发送代理请求,还保留了客户端 HTTP 标题,这些标题捕捉了源 IP 地址、用于连接的协议和目标主机:
1[environment fourth]
2[label conf/nginx.conf]
3. . .
4location / {
5 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
6 proxy_set_header X-Forwarded-Proto $scheme;
7 proxy_set_header Host $http_host;
8 proxy_redirect off;
9 proxy_pass http://django;
10}
11. . .
有关这些指令的更多信息,请参阅 部署 Gunicorn 和 模块 ngx_http_proxy_module 从 Nginx 文档。
最终的位置
块捕获了对/已知/acme-challenge/
路径的请求,Certbot 用于 HTTP-01 挑战,以便使用 Let’s Encrypt 验证您的域名,并提供或更新 TLS 证书。
1[environment fourth]
2[label conf/nginx.conf]
3. . .
4location ^~ /.well-known/acme-challenge/ {
5 root /var/www/html;
6}
完成编辑后,保存并关闭文件。
您现在可以使用此配置文件运行 Nginx Docker 容器. 在本教程中,我们将使用由 Nginx 维护的 官方 Docker 图像的 nginx:1.19.0
图像,版本 1.19.0
。
当我们第一次运行容器时, Nginx 会发出错误并失败,因为我们还没有提供配置文件中定义的证书。
1[environment fourth]
2docker run --rm --name nginx -p 80:80 -p 443:443 \
3 -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \
4 -v /var/www/html:/var/www/html \
5 nginx:1.19.0
在这里,我们将容器命名为nginx
,并将主机端口80
和443
绘制到相应的容器端口中。v
旗将配置文件安装在/etc/nginx/conf.d/nginx.conf
的 Nginx 容器中,而 Nginx 图像已预配置为加载。它安装在ro
或只读
模式中,因此容器无法修改文件。根网页目录/var/www/html
也安装在容器中。最后,nginx:1.19
.0指示 Docker 将
nginx:1.19.0`从 Dockerhub 图像中拉动并运行。
Docker 会拖动并运行图像,然后 Nginx 会发出错误,当它找不到配置的 TLS 证书和秘密密密钥时。
步骤 4 – 配置 Certbot 和 Let’s Encrypt 证书更新
Certbot是由 Electronic Frontier Foundation开发的 Let's Encrypt 客户端,它提供免费的来自 Let's Encrypt的证书授权的 TLS 证书,允许浏览器验证您的 Web 服务器的身份。鉴于我们在我们的 Nginx 代理服务器上安装了 Docker,我们将使用 Certbot Docker 图像提供和更新 TLS 证书。
首先,请确保您有一个 DNS 记录A
,并将其映射到代理服务器的公共 IP 地址,然后在代理服务器上使用certbot
Docker 图像提供证书的阶段版本:
1[environment fourth]
2docker run -it --rm -p 80:80 --name certbot \
3 -v "/etc/letsencrypt:/etc/letsencrypt" \
4 -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
5 certbot/certbot certonly --standalone --staging -d your_domain.com
此命令在交互模式下运行certbot
Docker 图像,并将主机上的端口80
提前到容器端口80
。它创建并将两个主机目录安装到容器中:/etc/letsencrypt/
和/var/lib/letsencrypt/
。
当被提示时,输入您的电子邮件地址并同意服务条款. 如果域验证成功,您应该看到以下输出:
1[environment fourth]
2[secondary_label Output]
3Obtaining a new certificate
4Performing the following challenges:
5http-01 challenge for stubb.dev
6Waiting for verification...
7Cleaning up challenges
8
9IMPORTANT NOTES:
10 - Congratulations! Your certificate and chain have been saved at:
11 /etc/letsencrypt/live/your_domain.com/fullchain.pem
12 Your key file has been saved at:
13 /etc/letsencrypt/live/your_domain.com/privkey.pem
14 Your cert will expire on 2020-09-15. To obtain a new or tweaked
15 version of this certificate in the future, simply run certbot
16 again. To non-interactively renew *all* of your certificates, run
17 "certbot renew"
18 - Your account credentials have been saved in your Certbot
19 configuration directory at /etc/letsencrypt. You should make a
20 secure backup of this folder now. This configuration directory will
21 also contain certificates and private keys obtained by Certbot so
22 making regular backups of this folder is ideal.
您可以使用猫
检查证书:
1[environment fourth]
2sudo cat /etc/letsencrypt/live/your_domain.com/fullchain.pem
有了提供的 TLS 证书,我们可以测试上一步组装的 Nginx 配置:
1[environment fourth]
2docker run --rm --name nginx -p 80:80 -p 443:443 \
3 -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \
4 -v /etc/letsencrypt:/etc/letsencrypt \
5 -v /var/lib/letsencrypt:/var/lib/letsencrypt \
6 -v /var/www/html:/var/www/html \
7 nginx:1.19.0
这是在 步骤 3中运行的相同命令,加上最近创建的 Let's Encrypt 目录。
一旦 Nginx 启动并运行,请导航到 http://your_domain.com
. 您可能会在浏览器中收到一个警告,即证书授权权是无效的。 这是我们预期的,因为我们已经提供阶段性证书,而不是生产 Let's Encrypt 证书。 检查您的浏览器的 URL 栏以确认您的 HTTP 请求被重定向到 HTTPS。
点击您的终端中的CTRL+C
,退出 Nginx,然后再次运行certbot
客户端,这一次错过了--staging
旗帜:
1[environment fourth]
2docker run -it --rm -p 80:80 --name certbot \
3 -v "/etc/letsencrypt:/etc/letsencrypt" \
4 -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
5 certbot/certbot certonly --standalone -d your_domain.com
当被要求保留现有证书或更新并更换它时,点击2
来更新它,然后点击ENTER
来确认您的选择。
有了生产 TLS 证书,重新运行 Nginx 服务器:
1[environment fourth]
2docker run --rm --name nginx -p 80:80 -p 443:443 \
3 -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \
4 -v /etc/letsencrypt:/etc/letsencrypt \
5 -v /var/lib/letsencrypt:/var/lib/letsencrypt \
6 -v /var/www/html:/var/www/html \
7 nginx:1.19.0
在您的浏览器中,导航到 http://your_domain.com
. 在 URL 栏中,确认 HTTP 请求已被重定向到 HTTPS. 鉴于 Polls 应用程序没有默认路线配置,您应该看到 Django Page not found 错误。 导航到 https://your_domain.com/polls
,您将看到标准 Polls 应用程序界面:
此时,您已使用 Certbot Docker 客户端提供生产 TLS 证书,并对两个 Django 应用服务器进行反向代理和负载平衡外部请求。
让我们加密证书每90天都到期。为了确保您的证书仍然有效,您应该在计划到期前定期更新它。随着 Nginx 运行,您应该使用 Certbot 客户端在webroot
模式而不是standalone
模式。这意味着 Certbot 将通过在/var/www/html/.well-known/acme-challenge/
目录中创建一个文件来进行验证,然后将使用 Nginx 配置中的位置
规则捕捉到该路径的 Let’s Encrypt 验证请求。
有多种方法可以自动化此过程,并且自动更新 TLS 证书超出本教程的范围。 对于使用cron
编程工具的类似过程,请参阅 如何使用 Nginx、Let's Encrypt 和 Docker Compose 保护 Containerized Node.js 应用程序的步骤 6。
在您的终端中,点击CTRL+C
来杀死 Nginx 容器,并在-d
标志上重新运行它:
1[environment fourth]
2docker run --rm --name nginx -d -p 80:80 -p 443:443 \
3 -v ~/conf/nginx.conf:/etc/nginx/conf.d/nginx.conf:ro \
4 -v /etc/letsencrypt:/etc/letsencrypt \
5 -v /var/lib/letsencrypt:/var/lib/letsencrypt \
6 -v /var/www/html:/var/www/html \
7 nginx:1.19.0
如果 Nginx 在背景中运行,请使用以下命令执行证书更新程序的干运行:
1[environment fourth]
2docker run -it --rm --name certbot \
3 -v "/etc/letsencrypt:/etc/letsencrypt" \
4 -v "/var/lib/letsencrypt:/var/lib/letsencrypt" \
5 -v "/var/www/html:/var/www/html" \
6 certbot/certbot renew --webroot -w /var/www/html --dry-run
我们使用--webroot
插件,指定 Web 根路径,并使用--dry-run
旗帜来验证一切正常工作,而无需实际执行证书更新。
如果更新模拟成功,您应该看到以下输出:
1[environment fourth]
2[secondary_label Output]
3Cert not due for renewal, but simulating renewal for dry run
4Plugins selected: Authenticator webroot, Installer None
5Renewing an existing certificate
6Performing the following challenges:
7http-01 challenge for your_domain.com
8Using the webroot path /var/www/html for all unmatched domains.
9Waiting for verification...
10Cleaning up challenges
11
12- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
13new certificate deployed without reload, fullchain is
14/etc/letsencrypt/live/your_domain.com/fullchain.pem
15- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
16
17- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
18** DRY RUN: simulating 'certbot renew' close to cert expiry
19** (The test certificates below have not been saved.)
20
21Congratulations, all renewals succeeded. The following certs have been renewed:
22 /etc/letsencrypt/live/your_domain.com/fullchain.pem (success)
23** DRY RUN: simulating 'certbot renew' close to cert expiry
24** (The test certificates above have not been saved.)
25- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
在生产设置中,在更新证书后,您应该重新加载 Nginx,以便更改生效。
1[environment fourth]
2docker kill -s HUP nginx
此命令将向nginx
Docker 容器内运行的 Nginx 进程发送一个 HUP Unix 信号.接收此信号后, Nginx 将重新加载其配置和更新证书。
有了 HTTPS 启用并启用了该架构的所有组件,最后一步是通过阻止对两个后端应用服务器的外部访问来锁定设置;所有 HTTP 请求都应该通过 Nginx 代理程序流动。
步骤 5 – 防止外部访问 Django App 服务器
在本教程中描述的架构中,SSL终止发生在 Nginx 代理程序中,这意味着 Nginx 会解密 SSL 连接,并且数据包被非加密地向 Django 应用程序服务器传输。对于许多使用情况,这种安全级别是足够的。对于涉及财务或健康数据的应用程序,您可能想要实施端到端加密。您可以通过负载平衡器传输加密包,并在应用程序服务器上解密,或者在代理程序上重新加密,再一次在 Django 应用程序服务器上解密。这些技术超出本文的范围,但要了解更多信息,请参阅 端到端加密。
Nginx 代理服务器作为外部流量和内部网络之间的通道。理论上,任何外部客户端都不能直接访问内部应用程序服务器,所有请求都应该通过 Nginx 服务器流动。在 步骤 1的注释中简要描述了 Docker 的 开放问题 其中 Docker 默认地绕过ufw
防火墙设置,并在外部打开端口,这可能不安全。 为了解决这个安全问题,建议在与 Docker 支持的服务器一起工作时使用 cloud firewalls 了解更多有关创建 Cloud Firewalls 与 DigitalOcean 的信息,请参阅 How to Create Firewalls 您也可以直接操纵`iptables
在此步骤中,我们将更改 UFW 的配置,以阻止对 Docker 打开的主机端口的外部访问。在应用服务器上运行 Django 时,我们将 -p 80:8000
标志转移到 docker,该标志将主机上的
80端口转移到集装箱端口
8000。这也为外部客户端打开了
80端口,您可以通过访问
http://your_app_server_1_IP` 来验证。 为了防止直接访问,我们将使用 ufw-docker GitHub 存储库中描述的方法修改 UFW 的配置。
开始登录到第一个 Django 应用程序服务器,然后打开具有超级用户权限的 /etc/ufw/after.rules' 文件,使用
nano' 或您最喜欢的编辑器:
1[environment second]
2sudo nano /etc/ufw/after.rules
当被提示时,输入您的密码,然后点击ENTER
来确认。
你应该看到以下UFW
规则:
1[environment second]
2[label /etc/ufw/after.rules]
3#
4# rules.input-after
5#
6# Rules that should be run after the ufw command line added rules. Custom
7# rules should be added to one of these chains:
8# ufw-after-input
9# ufw-after-output
10# ufw-after-forward
11#
12
13# Don't delete these required lines, otherwise there will be errors
14*filter
15:ufw-after-input - [0:0]
16:ufw-after-output - [0:0]
17:ufw-after-forward - [0:0]
18# End required lines
19
20# don't log noisy services by default
21-A ufw-after-input -p udp --dport 137 -j ufw-skip-to-policy-input
22-A ufw-after-input -p udp --dport 138 -j ufw-skip-to-policy-input
23-A ufw-after-input -p tcp --dport 139 -j ufw-skip-to-policy-input
24-A ufw-after-input -p tcp --dport 445 -j ufw-skip-to-policy-input
25-A ufw-after-input -p udp --dport 67 -j ufw-skip-to-policy-input
26-A ufw-after-input -p udp --dport 68 -j ufw-skip-to-policy-input
27
28# don't log noisy broadcast
29-A ufw-after-input -m addrtype --dst-type BROADCAST -j ufw-skip-to-policy-input
30
31# don't delete the 'COMMIT' line or these rules won't be processed
32COMMIT
滚动到底部,并粘贴到以下 UFW 配置规则块:
1[environment second]
2[label /etc/ufw/after.rules]
3. . .
4
5# BEGIN UFW AND DOCKER
6*filter
7:ufw-user-forward - [0:0]
8:DOCKER-USER - [0:0]
9-A DOCKER-USER -j RETURN -s 10.0.0.0/8
10-A DOCKER-USER -j RETURN -s 172.16.0.0/12
11-A DOCKER-USER -j RETURN -s 192.168.0.0/16
12
13-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
14
15-A DOCKER-USER -j ufw-user-forward
16
17-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
18-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
19-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
20-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
21-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
22-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
23
24-A DOCKER-USER -j RETURN
25COMMIT
26# END UFW AND DOCKER
这些规则限制了公共访问由Docker打开的端口,并允许从10.0.0.0/8
,172.16.0.0/12
和192.168.0.0/16
私有IP范围访问。如果您正在使用VPC与DigitalOcean,那么VPC网络中的Droplets将通过私有网络接口访问开放端口,但外部客户端不会。 有关VPC的更多信息,请参阅 VPC官方文档。
如果您不使用 VPC 与 DigitalOcean,并且在 Nginx 配置的上游
块中输入了应用程序服务器的公共 IP 地址,则需要明确修改 UFW 防火墙以允许来自 Nginx 服务器的流量通过 Django 应用程序服务器的端口 80。
完成编辑后,保存并关闭文件。
重新启动ufw
,以获取新的配置:
1[environment second]
2sudo systemctl restart ufw
在您的 Web 浏览器中导航到http://APP_SERVER_1_IP
,以确认您不再可以通过端口80
访问应用程序服务器。
在第二个 Django 应用程序服务器上重复此过程。
登出第一個應用程式伺服器或開啟另一個終端窗口,然後登入第二個 Django 應用程式伺服器,然後使用「nano」或您最喜愛的編輯器開啟「/etc/ufw/after.rules」檔案:
1[environment third]
2sudo nano /etc/ufw/after.rules
当被提示时,输入您的密码,然后点击ENTER
来确认。
滚动到底部,并粘贴到以下 UFW 配置规则块:
1[environment third]
2[label /etc/ufw/after.rules]
3. . .
4
5# BEGIN UFW AND DOCKER
6*filter
7:ufw-user-forward - [0:0]
8:DOCKER-USER - [0:0]
9-A DOCKER-USER -j RETURN -s 10.0.0.0/8
10-A DOCKER-USER -j RETURN -s 172.16.0.0/12
11-A DOCKER-USER -j RETURN -s 192.168.0.0/16
12
13-A DOCKER-USER -p udp -m udp --sport 53 --dport 1024:65535 -j RETURN
14
15-A DOCKER-USER -j ufw-user-forward
16
17-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 192.168.0.0/16
18-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 10.0.0.0/8
19-A DOCKER-USER -j DROP -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -d 172.16.0.0/12
20-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 192.168.0.0/16
21-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 10.0.0.0/8
22-A DOCKER-USER -j DROP -p udp -m udp --dport 0:32767 -d 172.16.0.0/12
23
24-A DOCKER-USER -j RETURN
25COMMIT
26# END UFW AND DOCKER
完成编辑后,保存并关闭文件。
重新启动ufw
,以获取新的配置:
1[environment third]
2sudo systemctl restart ufw
在您的 Web 浏览器中导航到http://APP_SERVER_2_IP
,以确认您不再可以通过端口80
访问应用程序服务器。
最后,导航到https://your_domain_here/polls
,以确认 Nginx 代理仍然可以访问 Django 上流服务器。
结论
在本教程中,您使用 Docker 容器设置了可扩展的 Django Polls 应用程序. 随着流量增长和系统负载增加,您可以单独扩展每个层: Nginx 代理层, Django 后端应用层和 PostgreSQL 数据库层。
在构建分布式系统时,通常会遇到多个设计决策,几个架构可能会满足您的使用情况,本教程中描述的架构旨在作为设计Django和Docker可扩展应用程序的灵活蓝图。
您可能希望在遇到错误时控制容器的行为,或者在系统启动时自动运行容器。 要做到这一点,您可以使用一个流程管理器,例如 Systemd或实施重新启动策略。
当使用运行同一 Docker 图像的多个主机进行规模工作时,使用配置管理工具(如 Ansible或 Chef等操作步骤可以更有效地自动化。
除了在每个主机上构建相同的图像之外,您还可以通过像 Docker Hub这样的图像注册表来简化部署,该注册表可以集中构建、存储和向多个服务器分发 Docker 图像。