如何使用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 makemigrationsmanage.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 命令创建的管理用户名和密码。

验证后,您可以访问投票应用的管理界面:

请注意,adminpolls 应用程序的静态资产直接从对象存储中交付。 要确认这一点,请参阅 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 上,可以使用 ConfigMapsSecrets 注入配置变量。

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-secretsenv 文件:

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 的两个副本。

envFromsecretRefconfigMapRef 一起使用,我们指定来自 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 svckubectl 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.compolls 服务创建一个 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-tlskubectl 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

typeNodePort 更改为 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 系列页面