使用 Kubernetes 使 Django 应用变得可扩展并具有弹性

发布于 2024-10-15 22:59:05 字数 15853 浏览 7 评论 0

如果事情如你预想般工作,那么在你的 webapp 的生命周期中,它将会有服务于大量用户的时候。当事情已经走到了这一步,那么如果你已经将你的 webapp 架构成其规模可以优雅的满足这种负荷,同时对于底层计算资源的任意故障具有弹性,这将是很理想的。

这篇文章是关于你可以如何使用 Docker 容器 以及 Kubernetes 来帮助你的 Django webapp 来达到这些架构目标。虽然它在理论和理念上涉猎不多,但是它确实逐步完善一个 具体的例子 ,以帮助巩固概念。

注意事项

在我们深入之前,我想指出,在这篇文章中表达的想法与 Django 没有什么特别的关系。我选择 Django 作为例子,只是单纯因为它是一个我熟悉的流行框架。对其他软件栈而言,重新利用这些原则是直截了当的。

我还想指出,这篇文章涉及了许多发展中的作品,它们中的一些相当不成熟。如果在你的 webapp 的生命周期的当前阶段中,你可以避免这种复杂度,那么你应该避免。相反,集中精力更好的理解用户的问题,以及测试你的应用是否解决了这些问题。除非足够多的人经常使用你的应用,否则没有人会知道或者抱怨你把你的应用运行在一个单一脆弱的服务器上。

没有人。

有了这样的方式,那么就让我们开始吧!

传统的基于虚拟机(VM)的部署存在有些问题

让我们来想象下,你正在做一个 Django web 应用,它以一种相当标准的形式进行布局:你所有的应用数据都保存在 PostgreSQL 服务器上。应用本身是用 Django 风格的 Python 编写 的,并使用 Gunicorn 应用服务器。而在这一切之前,你使用 NGINX web 服务器,它即作为反向代理,又作为静态内容服务器。

Layout of a non-trivial Django application.

Layout of a non-trivial Django application.

当你第一次开始了你的应用,并且只有少量用户时,它可以完美的将所有东东都运行在一台服务器上。所以你把应用跑在你 最喜欢的云服务提供商 在,启动一个 VPS 来运行 Debian 或其他什么操作系统,并在同一台机器上安装所有这些软件。

All pieces making up the app on a single machine.

All pieces making up the app on a single machine.

然后,随着你的应用开始变得受欢迎,你开始进行扩展工作。搜寻,你遵循简单的方法,简单的提供越来越大的单一机器来运行你应用。这就是所谓的垂直扩展(vertical scaling),它行之有效,知道应用拥有了上千个用户。

接着,你的应用变得更受欢迎。

现在,你意识到,如果你分开组成你应用的组件,然后将它们放在不同的机器上,那么你就可以独立地扩展组件。这意味着,例如,你可以运行 Django 应用的多个实例(称为水平扩展(horizontal scaling))来处理不断增长的用户群,同时继续把你的 PostgreSQL 服务器运行在唯一一个(但可能日益强大的)机器上。

Running many instances of the app, talking to a single database.

Running many instances of the app, talking to a single database.

其实,这是一个相当不错的部署方案(并且它的基本理念是我们今天在我的 日常工作 中实践的基础,使用 Ansible 来设置服务器),但它还有一些不便之处:

  1. 为每个组件建立并保持最新的服务器是烦人的。这不是你想考虑的关于服务器的问题。
  2. 通常,你拥有较差的资源利用率,因为每一个组件都不能有效地使用它所运行在的服务必须提供的所有资源。这主要是因为你通常为高峰负载进行安装,而不是平均负载。
  3. 如果你试图通过在同个机器上运行多个组件(例如,应用和数据库)来解决(2),那么就没有办法阻止组件之间的资源抢占。例如,在一个给定服务器上的匮乏资源隔离。

所以,如果我们能够将我们的关注点从管理服务器转移到简单在计算资源的集合上运行我们的应用的组件呢?此外,如果这些组件之间很好的相互隔离,并且有效地利用它们所拥有的资源呢?

然后,我们的部署图可能看起来更像下面这样,其中,我们关心的主要组件(应用组件)以橙色显示。组件运行的实际节点(物理机或虚拟机)在视觉上弱化 了,因为我们不关心细节。并且,我们相信我们的基本计算基础架构为我们提供了一些基本功能,例如持久性存储和负载平衡器(以绿色显示)这些任何较重的 web 应用的常用功能。

The application running on an abstract collection of resources.

The application running on an abstract collection of resources.

这种理念的转变 — 从 管理服务器 到简单理想地 运行我们应用的组件 — 恰恰是容器技术,例如 Docker ,和集群业务流程框架,例如 Kubernetes ,所要提供的。而在实际的 下面的例子 中,我们将看到这些工具如何让我们能够轻松地重新创建上面显示的理想部署方案。

所以 Docker 和 Kubernetes 是如何行之有效的呢?

你可以通俗地把 Docker 容器当做你的应用的 fat 静态二进制文件 。 它们捆绑你的应用代码,底层库,以及你的应用程序需要运行的所有东东到一个便捷的包里,这个包可以直接的在 Linux 内核上的一个薄层之上运行。这在实践 中意味着,你可以获得一个你已经构建了一次的容器,然后把它运行在不同版本的 Linux 发行版本上,或者完全不同的 Linux 发行版本上。所有这一切都应 该无缝工作。

因此,通过形成一个可构建、测试已经随处运行的基本单元,容器将你的关注层次提高到操作系统的细节上,运行你关注自己的应用。容器还提供资源隔离,意味着如果其中两个并排运行,那么每一个都只能看到它应该看到的东东,以及做它应该做的事。

这意味着我们的部署之旅现在可以大略分成两个步骤。第一个步骤是得到我们的应用的不同组件,然后将它们打包到容器中。第二步是在我们的计算资源之上运行它们 —— 利用底层计算原语,例如负载均衡器,并确保容器正确联网。

这里,第二步就是 Kubernetes 的用武之地。

Kubernetes 是一个用于管理集群和部署“容器化”应用的开源系统。Kubernetes 抽象(你的云供应商或者本地集群的)底层硬件,并提 供一个简单的 API 以允许你容易地控制它。你向这个 API 发送一些声明式状态,例如:“我想要我的 Django 应用容器的三个副本运行在负载均衡器之后, 拜托了”,然后,它可以确保在你的集群的节点上安排适当的容器。另外,它监控状态,并确保维持此状态,允许其对系统中的任意修改保持健壮。例如,这意味 着,如果一个容器因为节点内存耗尽而被过早关闭,Kubernetes 会注意到这点,并确保重新启动其他位置的另一个副本。

Kubernetes 通过位于集群的每个节点上的代理来工作。这些代理允许一些行为,例如运行 Docker 容器 (docker 守护进程),确保维持需要的状态 (kubelet),以及容器可以彼此交流 (kube-proxy)。这些代理监听及与一个集中的 API 服务器同步,以确保系统处于期望的状态。

A simplified look at Kubernetes

A simplified look at Kubernetes' architecture.

Kubernetes API 暴露了集群配置资源集合,我们可以修改以表达我们希望我们的集群所处的状态。该 API 提供给了一个标准的 REST 接口,允许我们以以多种方式与它进行交互。在即将到来的例子中,我们将使用一个瘦命令行客户端,名为 kubectl ,来与 API 服务器进行通信。

虽然该 API 提供大量原语 以供使用,但是这里还是有一些对于我们今天的例子重要的东西:

  • Pod 是在相同节点上被安排在一起的紧密耦合的容器的集合,允许它们共享卷和本地网络。它们是可以部署在一个 Kubernetes 集合的最小单元。
  • 标签(Label) 是任意的与 Kubernetes 资源相关的键/值对 (例如, name: app 或者 stage: production )。它们允许以一种简单的方式来选择和组织资源组。
  • 复制控制器(Replication Controller) 确保指定数量的(特定类型的)pod 在任何给定时间运行。它们通过标签来对 pod 进行分组。
  • 服务(Service) 提供了一组执行相同功能的 pod 的逻辑分组。通过为这组 pod 提供一个永久名称,IP 地址或者端口,它们提供服务发现和负载均衡的功能。

如果此时,这些都看起来有点太抽象,请不要烦恼。现在,我们要跳到一个例子,这个例子证明了这些在实践中如何工作,以助我们部署我们的 Django 应用。

谷歌容器引擎(Google Container Engine)上的应用实例

我们将要关注的例子应用是一个简单的博客应用。

Sample blog app following the Django Girls Tutorial.

虽然这是一个非常简单的例子,但是它包含了我们在实践中讨论的想法所有需要看到的必须组件。在这个例子的过程中,我们会访问由 Kubernetes 控制的集群,分开我们的博客应用到不同的 Docker 容器中,并使用 Kubernetes 进行部署。最终结果与前面介绍的理想化图相匹配。

The application running on an abstract collection of resources.

一旦我们让事情运转起来,我们会使用 Kubernetes API 来做不同的事情,如缩放应用,观察它如何从失败中恢复,并学习如何在无需停机的情况下将你的 Django 应用从一个版本升级到另一个版本。

预备步骤

  1. 获取此示例的代码。
    git clone https://github.com/hnarayanan/kubernetes-django.git
    
  2. 安装 Docker
  3. 看看并感受一下在这个仓库中使用的 例子 Django 应用 。这是一个简单的博客,它的构建遵循优秀的 Django Girls 教程
  4. 安装一个由 Kubernetes 管理的集群 。要做到这一点可能需要付出巨大的努力,所以一个简单的入门方法是早谷歌云平台(免费)注册,并使用一个名为 谷歌容器引擎(Google Container Engine) (GKE) 的 Kubernetes 托管版本。
    1. 在谷歌云平台上建立一个账户,然后更新你的账单资料。
    2. 安装 命令行接口 .
    3. 创建一个使用该 web 接口的项目(今后我们将用 $GCP_PROJECT 指代)。
    4. 现在,我们我们已经准备好了设置一些基本配置。
      gcloud config set project $GCP_PROJECT
      gcloud config set compute/zone europe-west1-d
      
    5. 然后创建集群自身。
      gcloud container clusters create demo
      gcloud container clusters list
      
    6. 最后,配置 kubectl 来与该集群通信。
      gcloud container clusters get-credentials demo
      kubectl get nodes
      

创建及发布 Docker 容器

在这个例子中,我们将使用 Docker Hub 来集群我们的容器。而且,由于我们并没有什么敏感信息,因此我们将公开这些容器。

PostgreSQL

构建容器,记得用你自己在 Docker Hub 上的用户名来取代 hnarayanan :

cd containers/database
docker build -t hnarayanan/postgresql:9.5 .

如果你想要的话,可以将它迁出到本地:

docker run --name database -e POSTGRES_DB=app_db -e POSTGRES_PASSWORD=app_db_pw -e POSTGRES_USER=app_db_user -d hnarayanan/postgresql:9.5
# Echoes $PROCESS_ID to the screen
docker exec -i -t $PROCESS_ID bash

将它推送到一个仓库中:

docker login
docker push hnarayanan/postgresql:9.5

在 Gunicorn 中运行 Django app

构建容器:

cd containers/app
docker build -t hnarayanan/djangogirls-app:1.2-orange .

将它推送到一个仓库中:

docker push hnarayanan/djangogirls-app:1.2-orange

稍后,在这个例子中,我们将看到如何执行滚动更新。要做到这点,让我们创建我们的应用的另一种版本,这个版本只有不同的标题颜色,然后构建一个新的容器应用,接着也把它推送到该容器仓库。

cd containers/app
emacs blog/templates/blog/base.html

# Add the following just before the closing </head> tag
    <style>
      .page-header {
        background-color: #ac4142;
      }
    </style>

docker build -t hnarayanan/djangogirls-app:1.2-maroon .
docker push hnarayanan/djangogirls-app:1.2-maroon`

部署这些容器到 Kubernetes 集群

PostgreSQL

虽然我们的应用只需要运行一个 PostgreSQL 实例,但是我们仍然将其运行在一个(pod) 复制控制器下。通过这种方式,我们拥有了一个服务,这个服务监控我们的数据库 pod,确保即使一些奇怪的情况发生,例如底层的节点出现故障,我们的实例也是运行的。

cd  kubernetes/database
kubectl create -f replication-controller.yaml

kubectl get rc
kubectl get pods

kubectl describe pod <pod-id>
kubectl logs <pod-id>

现在,我们启动一个服务来指向该 pod。

cd  kubernetes/database
kubectl create -f service.yaml

kubectl get svc
kubectl describe svc database

在 Gunicorn 中运行 Django 应用

首先,我们有了与单一数据库通信的三个应用 pod(橙色应用容器的副本)。

cd kubernetes/app
kubectl create -f replication-controller-orange.yaml
kubectl get pods

kubectl describe pod <pod-id>
kubectl logs <pod-id>

然后,我们启动了一个服务指向该 pod。这是一个带有外部 IP 的负载均衡器,所以我们可以访问该站点。

cd kubernetes/app
kubectl create -f service.yaml
kubectl get svc

在我们使用 kubectl get svc 所显示的外部 IP 来访问该网站之前,我们需要做几件事:

  1. 执行初始迁移: kubectl exec <some-app-orange-pod-id> -- python /app/manage.py migrate
  2. 为该博客创建一个初始用户: kubectl exec -it <some-app-orange-pod-id> -- python /app/manage.py createsuperuser
  3. 拥有一个 CDN 主机静态文件,因为我们不想要使用 Gunicorn。该演示使用谷歌云存储,但是你可以自由使用任何你想要的存储。只要确保在 containers/app/mysite/settings.py 中的 STATIC_URL 参数反映了文件所在位置即可。
gsutil mb gs://demo-assets
gsutil defacl set public-read gs://demo-assets
cd django-k8s/containers/app
virtualenv --distribute --no-site-packages venv
source venv/bin/activate
pip install Django==1.9.5
export DATABASE_ENGINE='django.db.backends.sqlite3'
./manage.py collectstatic --noinput
gsutil -m cp -r static/* gs://demo-assets

此时,你应该可以通过访问用于该应用服务的外部 IP(运行 kubectl get svc 获得)在你的浏览器中加载该网站。

使用你前面(在创建一个超级用户时)安装的凭证登录 http://app-service-external-ip/admin/ ,并返回到该网站,创建一些博文。注意,当你刷新该网站时,为该站点服务的应用 pod 的名称发生了改变,而内容保持不变。

感受一下 Kubernetes API 吧

现在,假设你的网站并没有获得多少流量,你可以优雅地降低运行的应用 pod 的数量到 1.(类似地,如果你的流量开始增长,那么你可以增加 pod 的数量!)

kubectl scale rc app-orange --replicas=1
kubectl get pods

你可以通过删除一个或多个应用 pod 来检查其灵活性,并看到它重生(respawn)。

kubectl delete pod <pod-id>
kubectl get pods

注意,Kubernetes 会调整 pod 的数量以匹配该复制控制器最后的已知状态。

最后,要显示我们可以怎样迁移该站点的一个版本到下一个版本,我们将从应用现有的橙色版本移动到另一个栗色版本。

首先,将我们的橙色版本缩小到只有一个副本:

kubectl scale rc app-orange --replicas=1
kubectl get pods

接着,我们启动该新的栗色版本的一些副本:

cd kubernetes/app
kubectl create -f replication-controller-maroon.yaml
kubectl get pods

注意,由于该应用简单地指向标签 name: app ,因此这个橙色版本的应用和三个栗色版本的应用都响应 http 请求道外部 IP。

当你愉快地看到该栗色版本工作了,那么你就可以关闭剩下的橙色版本,并删除它的复制控制器了。

kubectl scale rc app-orange --replicas=0
kubectl delete rc app-orange

清理

在你结束了这个例子后,记得干净地弃用我们为它启动的计算资源。

gcloud container clusters delete demo
gsutil -m rm -r gs://demo-assets

总结

本文介绍了许多基础知识。我们首先激发了在一般情况下使用容器和集群编配框架的需要的积极性。然后,我们看到了 Docker 和 Kubernetes 如何帮助我们部署一个 Django 应用,这个应用可以优雅地扩展以满足负载,同时对于任意的底层计算资源故障都具有弹性。

虽然这是对于概念的一个很好的介绍,但我掩盖了一些在你决定 Kubernetes 是否适合你之前会想要仔细考虑的细节。

首先,Kubernetes 集群的安装(当不使用托管版本,例如在我们的例子中的谷歌容器引擎)一点都不简单。虽然 Kubernetes 试图抽象体 层硬件,但是你使用它的真实体验非常依赖于你正在运行的实际基础设施。所以,务必在你的环境上玩一玩它,以衡量你是否能够接受它的复杂度。

其次是,在它变得实际有用之前,我们的示例部署需要使用额外的 Kubernetes 原语来进行更多的工作:

  • 持久卷(Persistent Volume) (和 Persistent Volume Claim) 确保 PostgreSQL 数据超越 Pod 生命周期持久存在。
  • Secret 处理数据库密码和其他敏感信息。
  • 水平 Pod 自动缩放(Horizontal Pod Autoscaling)基于所观察到的 CPU 使用率自动调整运行的 Pod 的数量。
  • 守护进程(Daemon Set)帮助汇总跨节点日志。

留心 示例项目的问题列表 ,以了解更多有关这些方面的进展。而且你也可以自由地为此出一份力。你也可以添加额外的组件到里面(例如 Redis 或者 Elasticsearch)。如果你实现了这些,非常欢迎拉取请求!

我将留给你一个真的令我非常兴奋的想法。神奇的理念转变正在发生,我们正将我们的注意力从管理服务器简单转变成运行我们的应用的组件。而这个级别的抽象感觉恰到好处。

选定的参考和进一步阅读

  1. Linux 容器: Parallels, LXC, OpenVZ, Docker 等等
  2. Borg, Omega, 和 Kubernetes
  3. 在谷歌云平台上构建可扩展和弹性的 Web 应用
  4. 从头开始了解 Kubernetes —  Kubelet , API Server , Scheduler
  5. 把 Django 打包到容器中
  6. 使用谷歌容器引擎在 Kubernetes 中运行 Postgres
  7. 使用 Kubernetes 部署 Django — Talk , 示例代码
  8. 使用 Kubernetes 部署一个容器化的 Rails 应用到谷歌容器引擎 —  第一部分 , 第二部分 , 第三部分

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

太阳哥哥

暂无简介

0 文章
0 评论
23 人气
更多

推荐作者

謌踐踏愛綪

文章 0 评论 0

开始看清了

文章 0 评论 0

高速公鹿

文章 0 评论 0

alipaysp_PLnULTzf66

文章 0 评论 0

热情消退

文章 0 评论 0

白色月光

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文