Kubernetes 分布式应用部署实战:以人脸识别应用为例 | Linux 中国

Kubernetes 分布式应用部署实战:以人脸识别应用为例 | Linux 中国伙计们,请搬好小板凳坐好,下面将是一段漫长的旅程,期望你能够乐在其中。-- Hannibal
有用的原文链接请访问文末的“原文链接”获得可点击的文内链接、全尺寸原图和相关文章。致谢编译自 | 
http://skarlso.github.io/2018/03/15/kubernetes-distributed-application/
 
 作者 | Hannibal
 译者 | Andy Song (pinewall) 共计翻译:27 篇 贡献时间:121 天

简介

[好文分享:www.11jj.com]

伙计们,请搬好小板凳坐好,下面将是一段漫长的旅程,期望你能够乐在其中。

[转载出处:www.11jj.com]

我将基于 Kubernetes[1] 部署一个分布式应用。我曾试图编写一个尽可能真实的应用,但由于时间和精力有限,最终砍掉了很多细节。

我将聚焦 Kubernetes 及其部署。

让我们开始吧。

应用

TL;DR

Kubernetes 分布式应用部署实战:以人脸识别应用为例 | Linux 中国

该应用本身由 6 个组件构成。代码可以从如下链接中找到:Kubenetes 集群示例[2]

这是一个人脸识别服务,通过比较已知个人的图片,识别给定图片对应的个人。前端页面用表格形式简要的展示图片及对应的个人。具体而言,向 接收器[2] 发送请求,请求包含指向一个图片的链接。图片可以位于任何位置。接受器将图片地址存储到数据库 (MySQL) 中,然后向队列发送处理请求,请求中包含已保存图片的 ID。这里我们使用 NSQ[3] 建立队列。

图片处理[4] 服务一直监听处理请求队列,从中获取任务。处理过程包括如下几步:获取图片 ID,读取图片,通过 gRPC[5] 将图片路径发送至 Python 编写的 人脸识别[6] 后端。如果识别成功,后端给出图片对应个人的名字。图片处理器进而根据个人 ID 更新图片记录,将其标记为处理成功。如果识别不成功,图片被标记为待解决。如果图片识别过程中出现错误,图片被标记为失败。

标记为失败的图片可以通过计划任务等方式进行重试。

那么具体是如何工作的呢?我们深入探索一下。

接收器

接收器服务是整个流程的起点,通过如下形式的 API 接收请求:

  • curl -d "{"path":"/unknown_images/unknown0001.jpg"}" http://127.0.0.1:8000/image/post

  • 此时,接收器将路径path存储到共享数据库集群中,该实体存储后将从数据库服务收到对应的 ID。本应用采用“实体对象Entity Object的唯一标识由持久层提供”的模型。获得实体 ID 后,接收器向 NSQ 发送消息,至此接收器的工作完成。

    图片处理器

    从这里开始变得有趣起来。图片处理器首次运行时会创建两个 Go 协程routine,具体为:

    Consume

    这是一个 NSQ 消费者,需要完成三项必需的任务。首先,监听队列中的消息。其次,当有新消息到达时,将对应的 ID 追加到一个线程安全的 ID 片段中,以供第二个协程处理。最后,告知第二个协程处理新任务,方法为 sync.Condition[7]

    ProcessImages

    该协程会处理指定 ID 片段,直到对应片段全部处理完成。当处理完一个片段后,该协程并不是在一个通道上睡眠等待,而是进入悬挂状态。对每个 ID,按如下步骤顺序处理:

    ◈ 与人脸识别服务建立 gRPC 连接,其中人脸识别服务会在人脸识别部分进行介绍◈ 从数据库获取图片对应的实体◈ 为 断路器[8] 准备两个函数◈ 函数 1: 用于 RPC 方法调用的主函数◈ 函数 2: 基于 ping 的断路器健康检查◈ 调用函数 1 将图片路径发送至人脸识别服务,其中路径应该是人脸识别服务可以访问的,最好是共享的,例如 NFS◈ 如果调用失败,将图片实体状态更新为 FAILEDPROCESSING◈ 如果调用成功,返回值是一个图片的名字,对应数据库中的一个个人。通过联合 SQL 查询,获取对应个人的 ID◈ 将数据库中的图片实体状态更新为 PROCESSED,更新图片被识别成的个人的 ID

    这个服务可以复制多份同时运行。

    断路器

    即使对于一个复制资源几乎没有开销的系统,也会有意外的情况发生,例如网络故障或任何两个服务之间的通信存在问题等。我在 gRPC 调用中实现了一个简单的断路器,这十分有趣。

    下面给出工作原理:

    Kubernetes 分布式应用部署实战:以人脸识别应用为例 | Linux 中国

    当出现 5 次不成功的服务调用时,断路器启动并阻断后续的调用请求。经过指定的时间后,它对服务进行健康检查并判断是否恢复。如果问题依然存在,等待时间会进一步增大。如果已经恢复,断路器停止对服务调用的阻断,允许请求流量通过。

    前端

    前端只包含一个极其简单的表格视图,通过 Go 自身的 html/模板显示一系列图片。

    人脸识别

    人脸识别是整个识别的关键点。仅因为追求灵活性,我将这个服务设计为基于 gRPC 的服务。最初我使用 Go 编写,但后续发现基于 Python 的实现更加适合。事实上,不算 gRPC 部分的代码,人脸识别部分仅有 7 行代码。我使用的人脸识别[9]库极为出色,它包含 OpenCV 的全部 C 绑定。维护 API 标准意味着只要标准本身不变,实现可以任意改变。

    注意:我曾经试图使用 GoCV[10],这是一个极好的 Go 库,但欠缺所需的 C 绑定。推荐马上了解一下这个库,它会让你大吃一惊,例如编写若干行代码即可实现实时摄像处理。

    这个 Python 库的工作方式本质上很简单。准备一些你认识的人的图片,把信息记录下来。对于我而言,我有一个图片文件夹,包含若干图片,名称分别为 hannibal_1.jpg、 hannibal_2.jpg、 gergely_1.jpg、 john_doe.jpg。在数据库中,我使用两个表记录信息,分别为 person、 person_images,具体如下:

  • +----+----------+

  • | id | name     |

  • +----+----------+

  • |  1 | Gergely  |

  • |  2 | John Doe |

  • |  3 | Hannibal |

  • +----+----------+

  • +----+----------------+-----------+

  • | id | image_name     | person_id |

  • +----+----------------+-----------+

  • |  1 | hannibal_1.jpg |         3 |

  • |  2 | hannibal_2.jpg |         3 |

  • +----+----------------+-----------+

  • 人脸识别库识别出未知图片后,返回图片的名字。我们接着使用类似下面的联合查询找到对应的个人。

  • select person.name, person.id from person inner join person_images as pi on person.id = pi.person_id where image_name = "hannibal_2.jpg";

  • gRPC 调用返回的个人 ID 用于更新图片的 person 列。

    NSQ

    NSQ 是 Go 编写的小规模队列,可扩展且占用系统内存较少。NSQ 包含一个查询服务,用于消费者接收消息;包含一个守护进程,用于发送消息。

    在 NSQ 的设计理念中,消息发送程序应该与守护进程在同一台主机上,故发送程序仅需发送至 localhost。但守护进程与查询服务相连接,这使其构成了全局队列。

    这意味着有多少 NSQ 守护进程就有多少对应的发送程序。但由于其资源消耗极小,不会影响主程序的资源使用。

    配置

    为了尽可能增加灵活性以及使用 Kubernetes 的 ConfigSet 特性,我在开发过程中使用 .env文件记录配置信息,例如数据库服务的地址以及 NSQ 的查询地址。在生产环境或 Kubernetes 环境中,我将使用环境变量属性配置。

    应用小结

    这就是待部署应用的全部架构信息。应用的各个组件都是可变更的,他们之间仅通过数据库、消息队列和 gRPC 进行耦合。考虑到更新机制的原理,这是部署分布式应用所必须的;在部署部分我会继续分析。

    使用 Kubernetes 部署应用

    基础知识

    Kubernetes 是什么?

    这里我会提到一些基础知识,但不会深入细节,细节可以用一本书的篇幅描述,例如 Kubernetes 构建与运行[11]。另外,如果你愿意挑战自己,可以查看官方文档:Kubernetes 文档[12]

    Kubernetes 是容器化服务及应用的管理器。它易于扩展,可以管理大量容器;更重要的是,可以通过基于 yaml 的模板文件高度灵活地进行配置。人们经常把 Kubernetes 比作 Docker Swarm,但 Kubernetes 的功能不仅仅如此。例如,Kubernetes 不关心底层容器实现,你可以使用 LXC 与 Kubernetes 的组合,效果与使用 Docker 一样好。Kubernetes 在管理容器的基础上,可以管理已部署的服务或应用集群。如何操作呢?让我们概览一下用于构成 Kubernetes 的模块。

    在 Kubernetes 中,你给出期望的应用状态,Kubernetes 会尽其所能达到对应的状态。状态可以是已部署、已暂停,有 2 个副本等,以此类推。

    Kubernetes 使用标签和注释标记组件,包括服务、部署、副本组、守护进程组等在内的全部组件都被标记。考虑如下场景,为了识别 pod 与应用的对应关系,使用 app: myapp 标签。假设应用已部署 2 个容器,如果你移除其中一个容器的 app 标签,Kubernetes 只能识别到一个容器(隶属于应用),进而启动一个新的具有 myapp 标签的实例。

    Kubernetes 集群

    要使用 Kubernetes,需要先搭建一个 Kubernetes 集群。搭建 Kubernetes 集群可能是一个痛苦的经历,但所幸有工具可以帮助我们。Minikube 为我们在本地搭建一个单节点集群。AWS 的一个 beta 服务工作方式类似于 Kubernetes 集群,你只需请求节点并定义你的部署即可。Kubernetes 集群组件的文档如下:Kubernetes 集群组件[13]

    节点

    节点node是工作单位,形式可以是虚拟机、物理机,也可以是各种类型的云主机。

    Pod

    Pod 是本地容器逻辑上组成的集合,即一个 Pod 中可能包含若干个容器。Pod 创建后具有自己的 DNS 和虚拟 IP,这样 Kubernetes 可以对到达流量进行负载均衡。你几乎不需要直接和容器打交道;即使是调试的时候,例如查看日志,你通常调用 kubectl logs deployment/your-app -f 查看部署日志,而不是使用 -c container_name 查看具体某个容器的日志。-f 参数表示从日志尾部进行流式输出。

    部署

    在 Kubernetes 中创建任何类型的资源时,后台使用一个部署deployment组件,它指定了资源的期望状态。使用部署对象,你可以将 Pod 或服务变更为另外的状态,也可以更新应用或上线新版本应用。你一般不会直接操作副本组 (后续会描述),而是通过部署对象创建并管理。

    服务

    默认情况下,Pod 会获取一个 IP 地址。但考虑到 Pod 是 Kubernetes 中的易失性组件,我们需要更加持久的组件。不论是队列,MySQL、内部 API 或前端,都需要长期运行并使用保持不变的 IP 或更好的 DNS 记录。

    为解决这个问题,Kubernetes 提供了服务service组件,可以定义访问模式,支持的模式包括负载均衡、简单 IP 或内部 DNS。

    Kubernetes 如何获知服务运行正常呢?你可以配置健康性检查和可用性检查。健康性检查是指检查容器是否处于运行状态,但容器处于运行状态并不意味着服务运行正常。对此,你应该使用可用性检查,即请求应用的一个特别接口endpoint。

    由于服务非常重要,推荐你找时间阅读以下文档:服务[14]。严肃的说,需要阅读的东西很多,有 24 页 A4 纸的篇幅,涉及网络、服务及自动发现。这也有助于你决定是否真的打算在生产环境中使用 Kubernetes。

    DNS / 服务发现

    在 Kubernetes 集群中创建服务后,该服务会从名为 kube-proxy 和 kube-dns 的特殊 Kubernetes 部署中获取一个 DNS 记录。它们两个用于提供集群内的服务发现。如果你有一个正在运行的 MySQL 服务并配置 clusterIP: no,那么集群内部任何人都可以通过 mysql.default.svc.cluster.local 访问该服务,其中:

    ◈ mysql – 服务的名称◈ default – 命名空间的名称◈ svc – 对应服务分类◈ cluster.local – 本地集群的域名

    可以使用自定义设置更改本地集群的域名。如果想让服务可以从集群外访问,需要使用 DNS 服务,并使用例如 Nginx 将 IP 地址绑定至记录。服务对应的对外 IP 地址可以使用如下命令查询:

    ◈ 节点端口方式 – kubectl get -o jsonpath="{.spec.ports[0].nodePort}" services mysql◈ 负载均衡方式 – kubectl get -o jsonpath="{.spec.ports[0].LoadBalancer}" services mysql

    模板文件

    类似 Docker Compose、TerraForm 或其它的服务管理工具,Kubernetes 也提供了基础设施描述模板。这意味着,你几乎不用手动操作。

    以 Nginx 部署为例,查看下面的 yaml 模板:

  • apiVersion: apps/v1

  • kind: Deployment #(1)

  • metadata: #(2)

  •  name: nginx-deployment

  •  labels: #(3)

  •    app: nginx

  • spec: #(4)

  •  replicas: 3 #(5)

  •  selector:

  •    matchLabels:

  •      app: nginx

  •  template:

  •    metadata:

  •      labels:

  •        app: nginx

  •    spec:

  •      containers: #(6)

  •      - name: nginx

  •        image: nginx:1.7.9

  •        ports:

  •        - containerPort: 80

  • 自媒体 微信号:11jj 扫描二维码关注公众号
    爱八卦,爱爆料。

    小编推荐

    1. 1

      唯亭街道电瓶车不让进小区吗(唯亭街道电瓶车不让进小区吗)

      大家好,小美今天来为大家解答唯亭街道电瓶车不让进小区吗以下问题,唯亭街道电瓶车不让进小区吗很多人还不知道,现在让我们一起来看看吧!

    2. 2

      幼小衔接零距离 双向奔赴育美好

      幼小跟尾零距离双向奔赴育美妙——福华小学幼小跟尾之福华小学从属幼儿园参观校园运动为了贯彻落实《教育部关于鼎力推进幼儿园与小学科学跟

    3. 3

      白天:减脂必须开始

      晚上:骑手正赶往商家

    4. 4

      速看!成绩可查!

      点击上方蓝色文字存眷我们吧!!!凭据工作放置,现将内蒙古自治区2024年通俗高档教育专升本统一测验考生成就查询及复核有关事宜通知如下:

    5. 5

      梦幻129五开怎么赚钱最效率(梦幻129五开收益咋样)

      大家好,小娟今天来为大家解答梦幻129五开怎么赚钱最效率以下问题,梦幻129五开收益咋样很多人还不知道,现在让我们一起来看看吧!1、五开赚钱

    6. 6

      紧急辟谣!事关薛之谦天外来物演唱会

      声 明列位薛之谦的粉丝同伙、港城的市民同伙们:近日,一则关于薛之谦将于6月29日、30日在连云港市体育中心举办小我演唱会的不实传闻在收集上

    7. 7

      【来了】 新闻来了!后附电子书!

      起原:安溪融媒书以载道,文以化人。为贯彻落实党的二十大精神,传承弘扬中华精良文化,推进文化自信自强,也为雄厚师生的校园文化生活,提

    8. 8

      苹果手机电池哪个牌子好(苹果手机换什么品牌的电池最好)

      大家好,小美今天来为大家解答苹果手机电池哪个牌子好以下问题,苹果手机换什么品牌的电池最好很多人还不知道,现在让我们一起来看看吧!1、

    Copyright 2024.依依自媒体,让大家了解更多图文资讯!