如何使用Kubernetes部署可扩展且安全的Django应用程序
介绍
在本教程中,您将在 Kubernetes 集群中部署一个容器化的 Django polls 应用程序。
Django 是一个强大的 Web 框架,可以帮助您快速启动 Python 应用程序。 它包括几个方便的功能,例如 对象关系映射器 、用户身份验证和可自定义的应用程序管理界面。 它还包括一个 缓存框架 并通过其 URL Dispatcher 和 模板系统 鼓励干净的应用程序设计。
在 How to Build a Django and Gunicorn Application with Docker 中,Django Tutorial Polls 应用程序 根据 Twelve-Factor 方法进行了修改,用于构建可扩展的、云的本机网络应用程序。 在 How To Scale and Secure a Django Application with Docker, Nginx, and Let's Encrypt 中使用 Nginx 反向代理和 Let's Encrypt 签名的 TLS 证书对这种容器化设置进行了扩展和保护。 在 From Containers to Kubernetes with Django 系列的最后一个教程中,现代化的 Django polls 应用程序将部署到 Kubernetes 集群中。
Kubernetes 是一个强大的开源容器编排器,可以自动部署、扩展和管理容器化应用程序。 诸如 ConfigMaps 和 Secrets 之类的 Kubernetes 对象允许您将配置与容器集中化和解耦,而诸如 Deployments 之类的控制器会自动重新启动失败的容器并启用容器副本的快速扩展。 使用 Ingress 对象和 ingress-nginx 开源 Ingress Controller 启用 TLS 加密。 cert-manager Kubernetes 插件使用免费的 Let's Encrypt 证书颁发机构更新和颁发证书。
先决条件
要遵循本教程,您将需要:
- 启用了 基于角色的访问控制 (RBAC) 的 Kubernetes 1.15+ 集群。 此设置将使用 DigitalOcean Kubernetes 集群,但您可以使用 另一种方法 自由创建集群。
kubectl
命令行工具安装在您的本地机器上并配置为连接到您的集群。 您可以在官方文档中阅读更多关于安装kubectl
的信息。 如果您使用的是 DigitalOcean Kubernetes 集群,请参考 如何连接到 DigitalOcean Kubernetes 集群 了解如何使用kubectl
连接到您的集群。- 一个注册的域名。 本教程将自始至终使用
your_domain.com
。 您可以在 Freenom 免费获得一个,或使用您选择的域名注册商。 - ingress-nginx Ingress Controller 和 cert-manager TLS 证书管理器安装到您的集群中并配置为颁发 TLS 证书。 要了解如何使用 cert-manager 安装和配置 Ingress,请参阅 如何在 DigitalOcean Kubernetes 上使用 Cert-Manager 设置 Nginx Ingress 。
A
DNS 记录,其中your_domain.com
指向 Ingress 负载均衡器的公共 IP 地址。 如果您使用 DigitalOcean 管理您域的 DNS 记录,请参阅 如何管理 DNS 记录 了解如何创建A
记录- 一个 S3 对象存储桶,例如 DigitalOcean Space,用于存储您的 Django 项目的静态文件和该空间的一组访问密钥。 要了解如何创建空间,请参阅 如何创建空间 产品文档。 要了解如何为空间创建访问密钥,请参阅 使用访问密钥共享对空间的访问。 稍作改动,您就可以使用 django-storages 插件支持的任何对象存储服务。
- 您的 Django 应用程序的 PostgreSQL 服务器实例、数据库和用户。 只需稍作更改,您就可以使用 Django 支持的任何数据库 。 PostgreSQL 数据库应该被称为 polls(或者在下面的配置文件中输入的另一个容易记住的名称),在本教程中,Django 数据库用户将被命名为 sammy。 有关创建这些的指导,请遵循如何使用 Docker 构建 Django 和 Gunicorn 应用程序的第 1 步。 您应该从本地计算机执行这些步骤。 本教程中使用了 DigitalOcean Managed PostgreSQL 集群。 要了解如何创建集群,请查阅 DigitalOcean Managed Databases 产品文档。 您还可以安装和运行您自己的 PostgreSQL 实例。 有关在 Ubuntu 服务器上安装和管理 PostgreSQL 的指导,请参阅如何在 Ubuntu 18.04 上安装和使用 PostgreSQL。
- 一个 Docker Hub 帐户和公共存储库。 有关创建这些的更多信息,请参阅 Docker 文档中的 Repositories。
- 安装在本地机器上的 Docker 引擎。 请参阅 如何在 Ubuntu 18.04 上安装和使用 Docker 以了解更多信息。
设置好这些组件后,您就可以开始阅读本指南了。
第 1 步 — 克隆和配置应用程序
在这一步中,我们将从 GitHub 克隆应用程序代码并配置数据库凭据和对象存储密钥等设置。
应用程序代码和 Dockerfile 可以在 Django Tutorial Polls App GitHub 存储库 的 polls-docker
分支中找到。 这个 repo 包含 Django 文档的 示例轮询应用程序 的代码,它教您如何从头开始构建轮询应用程序。
polls-docker
分支包含这个 Polls 应用程序的 Dockerized 版本。 要了解如何修改 Polls 应用程序以在容器化环境中有效工作,请参阅 如何使用 Docker 构建 Django 和 Gunicorn 应用程序。
首先使用 git
将 Django Tutorial Polls App GitHub 存储库 的 polls-docker
分支克隆到本地计算机:
git clone --single-branch --branch polls-docker https://github.com/do-community/django-polls.git
导航到 django-polls
目录:
cd django-polls
此目录包含 Django 应用程序 Python 代码、Docker 将用于构建容器映像的 Dockerfile
以及包含要传递到容器的环境变量列表的 env
文件运行环境。 检查 Dockerfile
:
cat Dockerfile
OutputFROM 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"]
此 Dockerfile 使用官方 Python 3.7.4 Docker 镜像 作为基础,并按照 django-polls/requirements.txt
文件中的定义安装 Django 和 Gunicorn 的 Python 包要求。 然后它会删除一些不必要的构建文件,将应用程序代码复制到映像中,并设置执行 PATH
。 最后,它声明端口 8000
将用于接受传入的容器连接,并与 3 个工作人员一起运行 gunicorn
,监听端口 8000
。
要了解有关此 Dockerfile 中每个步骤的更多信息,请参阅 如何使用 Docker 构建 Django 和 Gunicorn 应用程序 的第 6 步。
现在,使用 docker build
构建映像:
docker build -t polls .
我们使用 -t
标志将映像命名为 polls
,并将当前目录作为 构建上下文 传递,这是构建映像时要引用的文件集。
在 Docker 构建并标记镜像后,使用 docker images
列出可用的镜像:
docker images
您应该会看到列出的 polls
图像:
OutputREPOSITORY TAG IMAGE ID CREATED SIZE polls latest 80ec4f33aae1 2 weeks ago 197MB python 3.7.4-alpine3.10 f309434dea3a 8 months ago 98.7MB
在我们运行 Django 容器之前,我们需要使用当前目录中的 env
文件来配置它的运行环境。 这个文件会传入到运行容器的docker run
命令中,Docker会将配置好的环境变量注入到容器的运行环境中。
使用 nano
或您喜欢的编辑器打开 env
文件:
nano 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
填写以下键的缺失值:
DJANGO_SECRET_KEY
:将此设置为唯一的、不可预测的值,如 Django 文档 中所述。 Scalable Django App 教程的 Adjusting the App Settings 提供了一种生成此密钥的方法。DJANGO_ALLOWED_HOSTS
:此变量保护应用程序并防止 HTTP 主机标头攻击。 出于测试目的,将其设置为*
,这是一个匹配所有主机的通配符。 在生产中,您应该将其设置为your_domain.com
。 要了解有关此 Django 设置的更多信息,请参阅 Django 文档中的 Core Settings。DATABASE_USERNAME
:将此设置为在先决条件步骤中创建的 PostgreSQL 数据库用户。DATABASE_NAME
:将此设置为polls
或在先决条件步骤中创建的 PostgreSQL 数据库的名称。DATABASE_PASSWORD
:将此设置为在先决条件步骤中创建的 PostgreSQL 用户密码。DATABASE_HOST
:将此设置为数据库的主机名。DATABASE_PORT
:将此设置为数据库的端口。STATIC_ACCESS_KEY_ID
:将此设置为您的空间或对象存储的访问密钥。STATIC_SECRET_KEY
:将此设置为您的空间或对象存储的访问密钥 Secret。STATIC_BUCKET_NAME
:将此设置为您的空间名称或对象存储桶。STATIC_ENDPOINT_URL
:将此设置为适当的空间或对象存储端点 URL,例如https://your_space_name.nyc3.digitaloceanspaces.com
如果您的空间位于nyc3
区域。
完成编辑后,保存并关闭文件。
在下一步中,我们将在本地运行配置的容器并创建数据库模式。 我们还将将样式表和图像等静态资产上传到对象存储。
第 2 步 - 创建数据库模式并将资产上传到对象存储
构建和配置容器后,使用 docker run
覆盖 Dockerfile 中设置的 CMD
并使用 manage.py makemigrations
和 manage.py migrate
命令创建数据库模式:
docker 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 migrate"
覆盖 Dockerfile 命令,这将创建应用程序代码定义的数据库模式。
如果您是第一次运行它,您应该会看到:
OutputNo 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
这表明数据库模式已成功创建。
如果您随后运行 migrate
,除非数据库模式已更改,否则 Django 将执行无操作。
接下来,我们将运行应用程序容器的另一个实例,并在其中使用交互式 shell 为 Django 项目创建管理用户。
docker run -i -t --env-file env polls sh
这将在正在运行的容器内为您提供一个 shell 提示,您可以使用它来创建 Django 用户:
python manage.py createsuperuser
输入用户的用户名、电子邮件地址和密码,创建用户后,点击 CTRL+D
退出容器并杀死它。
最后,我们将为应用程序生成静态文件并使用 collectstatic
将它们上传到 DigitalOcean Space。 请注意,这可能需要一些时间才能完成。
docker run --env-file env polls sh -c "python manage.py collectstatic --noinput"
生成并上传这些文件后,您将收到以下输出。
Output121 static files copied.
我们现在可以运行应用程序:
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
在这里,我们运行 Dockerfile 中定义的默认命令 gunicorn --bind :8000 --workers 3 mysite.wsgi:application
,并公开容器端口 8000
,以便本地计算机上的端口 80
映射到端口 [ polls
容器的 X176X]。
您现在应该可以通过在 URL 栏中键入 http://localhost
使用 Web 浏览器导航到 polls
应用程序。 由于没有为 /
路径定义路由,因此您可能会收到 404 Page Not Found
错误,这是预期的。
导航到 http://localhost/polls
以查看投票应用程序界面:
要查看管理界面,请访问 http://localhost/admin
。 您应该会看到 Polls 应用管理员身份验证窗口:
输入您使用 createsuperuser
命令创建的管理用户名和密码。
验证后,您可以访问投票应用的管理界面:
请注意,admin
和 polls
应用程序的静态资产直接从对象存储中交付。 要确认这一点,请参阅 Testing Spaces 静态文件交付 。
完成探索后,在运行 Docker 容器的终端窗口中点击 CTRL+C
以杀死容器。
通过测试 Django 应用程序 Docker 映像,将静态资产上传到对象存储,并配置数据库模式并准备好与您的应用程序一起使用,您就可以将 Django 应用程序映像上传到 Docker Hub 之类的映像注册表。
第 3 步 — 将 Django 应用程序映像推送到 Docker Hub
要在 Kubernetes 上推出您的应用程序,您的应用程序映像必须上传到像 Docker Hub 这样的注册表。 Kubernetes 将从其存储库中提取应用程序映像,然后将其部署到您的集群。
您可以使用私有 Docker 注册表,例如 DigitalOcean Container Registry,目前在 Early Access 中免费,也可以使用公共 Docker 注册表,例如 Docker Hub。 Docker Hub 还允许您创建私有 Docker 存储库。 公共存储库允许任何人查看和提取容器映像,而私有存储库允许您限制对您和您的团队成员的访问。
在本教程中,我们会将 Django 映像推送到先决条件中创建的公共 Docker Hub 存储库。 您也可以将镜像推送到私有仓库,但从私有仓库拉取镜像超出了本文的范围。 要了解有关使用 Docker Hub 对 Kubernetes 进行身份验证和提取私有映像的更多信息,请参阅 Kubernetes 文档中的 从私有注册表中提取映像。
首先在本地机器上登录 Docker Hub:
docker login
OutputLogin with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one. Username:
输入您的 Docker Hub 用户名和密码进行登录。
Django 图像当前具有 polls:latest
标签。 要将其推送到您的 Docker Hub 存储库,请使用您的 Docker Hub 用户名和存储库名称重新标记该映像:
docker tag polls:latest your_dockerhub_username/your_dockerhub_repo_name:latest
将图像推送到仓库:
docker push sammy/sammy-django:latest
在本教程中,Docker Hub 用户名是 sammy,repo 名称是 sammy-django。 您应该将这些值替换为您自己的 Docker Hub 用户名和存储库名称。
您会看到一些输出随着镜像层被推送到 Docker Hub 而更新。
现在,您的映像可用于 Docker Hub 上的 Kubernetes,您可以开始在集群中推出它。
第 4 步 — 设置 ConfigMap
当我们在本地运行 Django 容器时,我们将 env
文件传入 docker run
以将配置变量注入运行时环境。 在 Kubernetes 上,可以使用 ConfigMaps 和 Secrets 注入配置变量。
ConfigMaps 应该用于存储非机密配置信息,例如应用程序设置,而 Secrets 应该用于存储 API 密钥和数据库凭据等敏感信息。 它们都以类似的方式注入到容器中,但 Secrets 具有额外的访问控制和安全功能,例如 静态加密 。 Secrets 也以 base64 存储数据,而 ConfigMaps 以纯文本存储数据。
首先,创建一个名为 yaml
的目录,我们将在其中存储 Kubernetes 清单。 导航到目录。
mkdir yaml cd
在 nano
或您喜欢的文本编辑器中打开一个名为 polls-configmap.yaml
的文件:
nano polls-configmap.yaml
粘贴以下 ConfigMap 清单:
民意调查-configmap.yaml
apiVersion: v1 kind: ConfigMap metadata: name: polls-config data: DJANGO_ALLOWED_HOSTS: "*" STATIC_ENDPOINT_URL: "https://your_space_name.space_region.digitaloceanspaces.com" STATIC_BUCKET_NAME: "your_space_name" DJANGO_LOGLEVEL: "info" DEBUG: "True" DATABASE_ENGINE: "postgresql_psycopg2"
我们从 Step 1 中修改的 env
文件中提取了非敏感配置,并将其粘贴到 ConfigMap 清单中。 ConfigMap 对象称为 polls-config
。 复制在上一步中输入到 env
文件中的相同值。
出于测试目的,将 DJANGO_ALLOWED_HOSTS
保留为 *
以禁用基于主机头的过滤。 在生产环境中,您应该将其设置为您的应用程序的域。
完成文件编辑后,保存并关闭它。
使用 kubectl apply
在集群中创建 ConfigMap:
kubectl apply -f polls-configmap.yaml
Outputconfigmap/polls-config created
创建 ConfigMap 后,我们将在下一步创建应用程序使用的 Secret。
第 5 步 — 设置秘密
Secret 值必须是 base64-encoded,这意味着在集群中创建 Secret 对象比创建 ConfigMap 稍微复杂一些。 您可以重复上一步的过程,手动对 Secret 值进行 base64 编码并将它们粘贴到清单文件中。 您还可以使用环境变量文件 kubectl create
和 --from-env-file
标志来创建它们,我们将在此步骤中执行这些操作。
我们将再次使用 Step 1 中的 env
文件,删除插入到 ConfigMap 中的变量。 在 yaml
目录中复制名为 polls-secrets
的 env
文件:
cp ../env ./polls-secrets
在您喜欢的编辑器中编辑文件:
nano polls-secrets
民意调查秘密
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
删除所有插入 ConfigMap 清单的变量。 完成后,它应该如下所示:
民意调查秘密
DJANGO_SECRET_KEY=your_secret_key DATABASE_NAME=polls DATABASE_USERNAME=your_django_db_user DATABASE_PASSWORD=your_django_db_user_password DATABASE_HOST=your_db_host DATABASE_PORT=your_db_port STATIC_ACCESS_KEY_ID=your_space_access_key STATIC_SECRET_KEY=your_space_access_key_secret
请务必使用与 Step 1 中相同的值。 完成后,保存并关闭文件。
使用 kubectl create secret
在集群中创建 Secret:
kubectl create secret generic polls-secret --from-env-file=poll-secrets
Outputsecret/polls-secret created
这里我们创建一个名为 polls-secret
的 Secret 对象,并传入我们刚刚创建的 secrets 文件。
您可以使用 kubectl describe
检查 Secret:
kubectl describe secret polls-secret
OutputName: polls-secret Namespace: default Labels: <none> Annotations: <none> Type: Opaque Data ==== DATABASE_PASSWORD: 8 bytes DATABASE_PORT: 5 bytes DATABASE_USERNAME: 5 bytes DJANGO_SECRET_KEY: 14 bytes STATIC_ACCESS_KEY_ID: 20 bytes STATIC_SECRET_KEY: 43 bytes DATABASE_HOST: 47 bytes DATABASE_NAME: 5 bytes
此时,您已使用 Secret 和 ConfigMap 对象类型将应用程序的配置存储在 Kubernetes 集群中。 我们现在已准备好将应用程序部署到集群中。
第 6 步 — 使用部署部署 Django 应用程序
在此步骤中,您将为您的 Django 应用程序创建一个部署。 Kubernetes 部署是一个 控制器,可用于管理集群中的无状态应用程序。 控制器是一个控制回路,通过扩大或缩小工作负载来调节工作负载。 控制器还会重新启动并清除失败的容器。
部署控制一个或多个 Pod,这是 Kubernetes 集群中最小的可部署单元。 Pod 包含一个或多个容器。 要详细了解您可以启动的不同类型的工作负载,请查看 An Introduction to Kubernetes 。
首先在您喜欢的编辑器中打开一个名为 polls-deployment.yaml
的文件:
nano polls-deployment.yaml
粘贴以下部署清单:
民意调查-deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: polls-app labels: app: polls spec: replicas: 2 selector: matchLabels: app: polls template: metadata: labels: app: polls spec: containers: - image: your_dockerhub_username/app_repo_name:latest name: polls envFrom: - secretRef: name: polls-secret - configMapRef: name: polls-config ports: - containerPort: 8000 name: gunicorn
填写适当的容器镜像名称,引用您在 Step 2 中推送到 Docker Hub 的 Django Polls 镜像。
在这里,我们定义了一个名为 polls-app
的 Kubernetes 部署,并使用键值对 app: polls
对其进行标记。 我们指定要运行定义在 template
字段下方的 Pod 的两个副本。
将 envFrom
与 secretRef
和 configMapRef
一起使用,我们指定来自 polls-secret
Secret 和 polls-config
ConfigMap 的所有数据都应该注入到容器作为环境变量。 ConfigMap 和 Secret 键成为环境变量名称。
最后,我们公开 containerPort
8000
并将其命名为 gunicorn
。
要了解有关配置 Kubernetes 部署的更多信息,请参阅 Kubernetes 文档中的 Deployments。
完成文件编辑后,保存并关闭它。
使用 kubectl apply -f
在集群中创建部署:
kubectl apply -f polls-deployment.yaml
deployment.apps/polls-app created
使用 kubectl get
检查部署是否正确展开:
kubectl get deploy polls-app
OutputNAME READY UP-TO-DATE AVAILABLE AGE polls-app 2/2 2 2 6m38s
如果您遇到错误或某些事情不太正常,您可以使用 kubectl describe
检查失败的部署:
kubectl describe deploy
您可以使用 kubectl get pod
检查两个 Pod:
kubectl get pod
OutputNAME READY STATUS RESTARTS AGE polls-app-847f8ccbf4-2stf7 1/1 Running 0 6m42s polls-app-847f8ccbf4-tqpwm 1/1 Running 0 6m57s
您的 Django 应用程序的两个副本现在已在集群中启动并运行。 要访问该应用程序,您需要创建一个 Kubernetes 服务,我们接下来将执行此操作。
第 7 步 — 允许使用服务进行外部访问
在这一步中,您将为您的 Django 应用程序创建一个服务。 Kubernetes 服务是一种抽象,它允许您将一组正在运行的 Pod 公开为网络服务。 使用服务,您可以为您的应用程序创建一个稳定的端点,该端点不会随着 Pod 的死亡和重新创建而改变。
有多种服务类型,包括 ClusterIP 服务,它在集群内部 IP 上公开服务,NodePort 服务,它在每个节点上的一个名为 NodePort 的静态端口上公开服务,以及 LoadBalancer 服务,它提供一个云负载均衡器来将外部流量引导到集群中的 Pod(通过它自动创建的 NodePorts)。 要了解有关这些的更多信息,请参阅 Kubernetes 文档中的 Service。
在我们的最终设置中,我们将使用通过 Ingress 公开的 ClusterIP 服务,以及在本指南的先决条件中设置的 Ingress Controller。 现在,为了测试一切是否正常,我们将创建一个临时的 NodePort 服务来访问 Django 应用程序。
首先使用您喜欢的编辑器创建一个名为 polls-svc.yaml
的文件:
nano polls-svc.yaml
粘贴以下服务清单:
民意调查-svc.yaml
apiVersion: v1 kind: Service metadata: name: polls labels: app: polls spec: type: NodePort selector: app: polls ports: - port: 8000 targetPort: 8000
在这里,我们创建一个名为 polls
的 NodePort 服务,并为其指定 app: polls
标签。 然后,我们选择带有 app: polls
标签的后端 Pod,并以它们的 8000
端口为目标。
完成文件编辑后,保存并关闭它。
使用 kubectl apply
推出服务:
kubectl apply -f polls-svc.yaml
Outputservice/polls created
确认您的服务是使用 kubectl get svc
创建的:
kubectl get svc polls
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE polls NodePort 10.245.197.189 <none> 8000:32654/TCP 59s
此输出显示服务的集群内部 IP 和 NodePort (32654
)。 要连接到服务,我们需要集群节点的外部 IP 地址:
kubectl get node -o wide
OutputNAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME pool-7no0qd9e0-364fd Ready <none> 27h v1.18.8 10.118.0.5 203.0.113.1 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9 pool-7no0qd9e0-364fi Ready <none> 27h v1.18.8 10.118.0.4 203.0.113.2 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9 pool-7no0qd9e0-364fv Ready <none> 27h v1.18.8 10.118.0.3 203.0.113.3 Debian GNU/Linux 10 (buster) 4.19.0-10-cloud-amd64 docker://18.9.9
在您的 Web 浏览器中,使用任何 Node 的外部 IP 地址和 NodePort 访问您的 Polls 应用程序。 鉴于上面的输出,应用程序的 URL 将是:http://203.0.113.1:32654/polls
。
您应该会看到您在第 1 步中本地访问的相同民意调查应用程序界面:
您可以使用 /admin
路线重复相同的测试:http://203.0.113.1:32654/admin
。 您应该看到与以前相同的管理界面:
在这个阶段,您已经使用 Deployment 推出了 Django Polls 应用程序容器的两个副本。 您还为这两个副本创建了一个稳定的网络端点,并使用 NodePort 服务使其可以从外部访问。
本教程的最后一步是使用 HTTPS 保护应用程序的外部流量。 为此,我们将使用先决条件中安装的 ingress-nginx
Ingress Controller,并创建一个 Ingress 对象将外部流量路由到 polls
Kubernetes 服务。
第 8 步 — 使用 Nginx Ingress 和 cert-manager 配置 HTTPS
Kubernetes Ingresses 允许您灵活地将流量从 Kubernetes 集群外部路由到集群内部的服务。 这是使用 Ingress 对象完成的,它定义了将 HTTP 和 HTTPS 流量路由到 Kubernetes 服务的规则,以及 Ingress Controllers,它通过负载平衡流量并将其路由到适当的后端服务来实现规则。
在先决条件中,您安装了 ingress-nginx Ingress Controller 和 cert-manager TLS 证书自动化插件。 您还使用 Let's Encrypt 证书颁发机构为您的域设置了暂存和生产 ClusterIssuers,并创建了一个 Ingress 来测试证书颁发和对两个虚拟后端服务的 TLS 加密。 在继续此步骤之前,您应该删除 必备教程 中创建的 echo-ingress
Ingress:
kubectl delete ingress echo-ingress
如果您愿意,也可以使用 kubectl delete svc
和 kubectl delete deploy
删除虚拟服务和部署,但这对于完成本教程不是必需的。
您还应该创建了一个 DNS A
记录,其中 your_domain.com
指向 Ingress 负载均衡器的公共 IP 地址。 如果您使用的是 DigitalOcean 负载均衡器,您可以在控制面板的 Load Balancers 部分找到此 IP 地址。 如果您还使用 DigitalOcean 来管理您域的 DNS 记录,请参阅 如何管理 DNS 记录 以了解如何创建 A
记录。
如果您使用的是 DigitalOcean Kubernetes,还请确保您已实施 如何在 DigitalOcean Kubernetes 上使用 Cert-Manager 设置 Nginx 入口的第 5 步中描述的解决方法。
一旦有 A
记录指向 Ingress Controller 负载均衡器,您就可以为 your_domain.com
和 polls
服务创建一个 Ingress。
使用您喜欢的编辑器打开一个名为 polls-ingress.yaml
的文件:
nano polls-ingress.yaml
粘贴以下 Ingress 清单:
[polls-ingress.yaml] apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: polls-ingress annotations: kubernetes.io/ingress.class: "nginx" cert-manager.io/cluster-issuer: "letsencrypt-staging" spec: tls: - hosts: - your_domain.com secretName: polls-tls rules: - host: your_domain.com http: paths: - backend: serviceName: polls servicePort: 8000
我们创建一个名为 polls-ingress
的 Ingress 对象并对其进行注释以指示控制平面使用 ingress-nginx 入口控制器和登台 ClusterIssuer。 我们还为 your_domain.com
启用 TLS,并将证书和私钥存储在名为 polls-tls
的机密中。 最后,我们定义一个规则,将 your_domain.com
主机的流量路由到端口 8000
上的 polls
服务。
完成文件编辑后,保存并关闭它。
使用 kubectl apply
在集群中创建 Ingress:
kubectl apply -f polls-ingress.yaml
Outputingress.networking.k8s.io/polls-ingress created
您可以使用 kubectl describe
来跟踪您刚刚创建的 Ingress 的状态:
kubectl describe ingress polls-ingress
OutputName: polls-ingress Namespace: default Address: workaround.your_domain.com Default backend: default-http-backend:80 (<error: endpoints "default-http-backend" not found>) TLS: polls-tls terminates your_domain.com Rules: Host Path Backends ---- ---- -------- your_domain.com polls:8000 (10.244.0.207:8000,10.244.0.53:8000) Annotations: cert-manager.io/cluster-issuer: letsencrypt-staging kubernetes.io/ingress.class: nginx Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 51s nginx-ingress-controller Ingress default/polls-ingress Normal CreateCertificate 51s cert-manager Successfully created Certificate "polls-tls" Normal UPDATE 25s nginx-ingress-controller Ingress default/polls-ingress
您还可以在 polls-tls
证书上运行 describe
以进一步确认其成功创建:
kubectl describe certificate polls-tls
Output. . . Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal Issuing 3m33s cert-manager Issuing certificate as Secret does not exist Normal Generated 3m32s cert-manager Stored new private key in temporary Secret resource "polls-tls-v9lv9" Normal Requested 3m32s cert-manager Created new CertificateRequest resource "polls-tls-drx9c" Normal Issuing 2m58s cert-manager The certificate has been successfully issued
这确认 TLS 证书已成功颁发,并且现在为 your_domain.com
启用了 HTTPS 加密。
鉴于我们使用了 staging ClusterIssuer,大多数 Web 浏览器不会信任它颁发的假 Let's Encrypt 证书,因此导航到 your_domain.com
将带您进入错误页面。
要发送测试请求,我们将在命令行中使用 wget
:
wget -O - http://your_domain.com/polls
Output. . . ERROR: cannot verify your_domain.com's certificate, issued by ‘CN=Fake LE Intermediate X1’: Unable to locally verify the issuer's authority. To connect to your_domain.com insecurely, use `--no-check-certificate'.
我们将使用建议的 --no-check-certificate
标志绕过证书验证:
wget --no-check-certificate -q -O - http://your_domain.com/polls
Output <link rel="stylesheet" type="text/css" href="https://your_space.nyc3.digitaloceanspaces.com/django-polls/static/polls/style.css"> <p>No polls are available.</p>
此输出显示 /polls
界面页面的 HTML,还确认样式表是从对象存储中提供的。
现在您已经使用暂存 ClusterIssuer 成功测试了证书颁发,您可以修改 Ingress 以使用生产 ClusterIssuer。
再次打开polls-ingress.yaml
进行编辑:
nano polls-ingress.yaml
修改cluster-issuer
注解:
[polls-ingress.yaml] apiVersion: networking.k8s.io/v1beta1 kind: Ingress metadata: name: polls-ingress annotations: kubernetes.io/ingress.class: "nginx" cert-manager.io/cluster-issuer: "letsencrypt-prod" spec: tls: - hosts: - your_domain.com secretName: polls-tls rules: - host: your_domain.com http: paths: - backend: serviceName: polls servicePort: 8000
完成后,保存并关闭文件。 使用 kubectl apply
更新 Ingress:
kubectl apply -f polls-ingress.yaml
Outputingress.networking.k8s.io/polls-ingress configured
您可以使用 kubectl describe certificate polls-tls
和 kubectl describe ingress polls-ingress
来跟踪证书颁发状态:
kubectl describe ingress polls-ingress
Output. . . Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal CREATE 23m nginx-ingress-controller Ingress default/polls-ingress Normal CreateCertificate 23m cert-manager Successfully created Certificate "polls-tls" Normal UPDATE 76s (x2 over 22m) nginx-ingress-controller Ingress default/polls-ingress Normal UpdateCertificate 76s cert-manager Successfully updated Certificate "polls-tls"
以上输出确认新的生产证书已成功颁发并存储在 polls-tls
Secret 中。
在 Web 浏览器中导航到 your_domain.com/polls
以确认 HTTPS 加密已启用并且一切正常。 您应该会看到 Polls 应用程序界面:
确认 HTTPS 加密在您的 Web 浏览器中处于活动状态。 如果您使用的是谷歌浏览器,那么在没有任何错误的情况下到达上述页面即可确认一切正常。 此外,您应该会在 URL 栏中看到一个挂锁。 单击挂锁将允许您检查 Let's Encrypt 证书详细信息。
作为最后的清理任务,您可以选择将 polls
服务类型从 NodePort 切换到仅限内部的 ClusterIP 类型。
使用编辑器修改 polls-svc.yaml
:
nano polls-svc.yaml
将 type
从 NodePort
更改为 ClusterIP
:
民意调查-svc.yaml
apiVersion: v1 kind: Service metadata: name: polls labels: app: polls spec: type: ClusterIP selector: app: polls ports: - port: 8000 targetPort: 8000
完成文件编辑后,保存并关闭它。
使用 kubectl apply
展开更改:
kubectl apply -f polls-svc.yaml --force
Outputservice/polls configured
使用 kubectl get svc
确认您的服务已修改:
kubectl get svc polls
OutputNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE polls ClusterIP 10.245.203.186 <none> 8000/TCP 22s
此输出显示服务类型现在是 ClusterIP。 访问它的唯一方法是通过您的域和在此步骤中创建的 Ingress。
结论
在本教程中,您将一个可扩展的、受 HTTPS 保护的 Django 应用程序部署到 Kubernetes 集群中。 静态内容直接从对象存储中提供,并且可以使用 polls-app
部署清单中的 replicas
字段快速扩展或缩减正在运行的 Pod 的数量。
如果您使用的是 DigitalOcean Space,您还可以通过内容交付网络启用静态资产交付,并为您的 Space 创建自定义子域。 请参阅 How to Set Up a Scalable Django App with DigitalOcean Managed Databases and Spaces 中的 启用 CDN 以了解更多信息。
要查看该系列的其余部分,请访问我们的 使用 Django 从容器到 Kubernetes 系列页面。