- 原文地址:KUBERNETES DISTRIBUTED APPLICATION DEPLOYMENT WITH SAMPLE FACE RECOGNITION APP
- 原文作者:skarlso
- 译文出自:掘金翻译计划
在这个例子中,接收器通过共享数据库集群来存储图像路径。当数据库存储图像路径成功后,接收器实例就能从数据库服务中接收图像 ID。此应用程序是基于在持久层提供实体对象唯一标识的模型的。一旦 ID 产生,接收器会向 NSQ 发送一个消息。到这里,接收器的工作就完成了。
下面是激动人心的开始。当图像处理器第一次运行时,它会创建两个 Go 协程(routine)。 他们是:
这是一个 NSQ 消费者。它有三个必要的工作。首先,它能够监听队列中的消息。其次,当其接收到消息后,会将收到的 ID 添加到第二个例程处理的线程安全的 ID 切片中去。最后,它通过 sync.Condition 告知第二个协程有工作要做。
该例程处理 ID 切片,直到切片完全耗尽。一旦切片消耗完,例程将暂停而不是等待 channel。以下是处理单个 ID 的步骤:
这个服务可以被复制,换句话说,可以同时运行多个服务。
虽然这是一个不需要太大精力就能够复制资源的系统,但仍可能存在状况,例如网络故障、服务间的通信问题。因此我在 gRRC 调用上实现了一个小小的断路器作为乐趣。
它是这样工作的:
NSQ
NSQ 是一个极好的基于 Go 语言的队列。它可伸缩并且在系统上具有最小的占用空间。它还具有消费者用来接收消息的查找服务,以及发送者在发送消息时使用的守护程序。
NSQ 的理念是守护进程应该与发送者应用程序一起运行。这样,发件人只会发送到本地主机。但守护进程连接到查找服务,他们就是这样实现全局队列。
这就意味着,有多少个发送者,有需要部署多少个 NSQ 守护进程。由于守护进程的资源要求很小,不会影响主应用程序的需求。
为了尽可能灵活,以及使用 Kubernetes 的 ConfigSet,我在开发中使用 .env 文件来存储配置,如数据库服务的位置或 NSQ 的查找地址。 在生产中,这意味着在 Kubernetes 环境中,我将使用环境变量。
这就是我们即将部署的应用程序的架构。它的所有组件都是可变的,只能通过数据库,队列和 gRPC 进行耦合。由于更新机制的工作原因,这在部署分布式应用程序时非常重要。我将在“部署”部分中介绍该部分。
什么是 Kubernetes?
我将在这里介绍一些基础知识,但不会过多介绍细节。如果你想了解更多,可阅读的整本书:Kubernetes Up And Running。另外,如果你足够大胆,你可以看看这个文档:Kubernetes Documentation。
Kubernetes 是一个容器化的服务和应用程序管理平台。它容易扩展,可管理一大堆容器,最重要的是,它可以通过基于 yaml 的模板文件高度配置。人们经常将 Kubernetes 与Docker 集群进行比较,但 Kubernetes 确实不止于此!例如:它可以管理不同的容器。你可以使用 Kubernetes 来对LXC 进行管理和编排,同时也可以使用相同的方式管理 Docker。它提供了一个高于管理已部署服务和应用程序集群的层。怎么样?让我们快速浏览一下 Kubernetes 的构建模块吧。
在 Kubernetes 中,您将描述应用程序的期望状态,Kubernetes 会做一些事情,使之达到这个状态。状态可能是部署、暂停、重复两次等等。
Kubernetes 的基础知识之一是它为所有组件使用标签和注解。Services,Deployments,ReplicaSets,DaemonSets,一切都能够被标记。考虑以下情况。为了确定哪个 pod 属于哪个应用程序,我们将会使用了一个名为 app:myapp
的标签。假设您已部署了此应用程序的两个容器; 如果您从其中一个容器中移除标签 app
,则 Kubernetes 只会检测到一个标签,因此会启动一个新的 myapp
实例。
对于 Kuberenetes 的工作,需要有 Kubernetes 集群的存在。配置集群可能是非常痛苦的,但幸运的是,帮助就在眼前。Minikube 在本地为我们配置一个带有一个节点的集群。AWS 有一个以 Kubernetes 集群形式运行的测试服务,其中您唯一需要做的就是请求节点并定义你的部署。Kubernetes 集群组件的文档在此处:Kubernetes Cluster Components。
一个节点就是一台工作主机。它可以是任何事物,例如物理机、虚拟机以及各种云服务提供的虚拟资源。
Pods 是一个逻辑上分组的容器,也就意味着一个 Pod 可以容纳多个容器。一个 Pod 在创建后会获得自己的 DNS 和虚拟 IP 地址,这样Kubernetes 就可以为其平衡流量。你很少需要直接处理容器,即使在调试时(比如查看日志),通常也会调用 kubectl logs deployment / your-app -f
而不是查看特定的容器。尽管有可能会调用 -c container_name
。 -f
参数会持续显示日志文件的末尾部分。
在 Kubernetes 中创建任何类型的资源时,它将在后台使用 Deployment。一个 Deployment 对象描述当前应用程序的期望状态。这东西可以用来变换 Pod 或 Service 的状态,更新或推出新版的应用。您不直接控制 ReplicaSet(如稍后所述),但可以控制 Deployment 对象来创建和管理 ReplicaSet。
默认情况下,Pod 会得到一个 IP 地址。然而,因为 Pods 在 Kubernetes 中是一个不稳定的东西,所以你需要更持久的东西。队列、mysql、内部API、前端,这些需要长时间运行并且需要在一个静态的,不变的IP或最好是 DNS 记录之后。
为此,Kubernetes 提供可定义可访问模式的 Services。负载均衡,简单 IP 或内部 DNS。
Kubernetes 如何知道服务是否正确运行?你可以配置运行状况检查和可用性检查。运行状况检查将检查容器是否正在运行,但这并不意味着你的服务正在运行。为此,你需要在您的应用程序中对可用的端点进行可用性检查。
由于 Services 非常重要,我建议你稍后在这里阅读它们:Services。预先提醒,这部分文档内容很多,有 24 个 A4 大小的页面,内容包含网络、服务和发现。但是这对于你是否决定要在生产环境中使用 Kubernetes 是至关重要的。
如果您在集群中创建服务,该服务将获取由特殊的Kubernetes Deployments 对象(被称作为 kube-proxy 和 kube-dns)提供的在 Kubernetes 中的 DNS 记录。这两个对象在集群中提供了服务发现。如果您运行了mysql服务并设置了 clusterIP:none
,那么集群中的每个人都可以通过 ping 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 也提供了配置模板的基础设施。这意味着你很少需要手工做任何事情。
例如,请看下面使用 yaml 文件来配置 nginx 部署的模板:
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
在这个简单的部署中,我们做了以下工作:
kind
属性定义模板的类型replicas
ReplicaSet 是低级复制管理器。 它确保为应用程序运行正确数量的复制。 但是,当部署处于较高级别,应始终管理 ReplicaSets。你很少需要直接使用 ReplicaSets,除非您有一个需要控制复制细节的特殊案例。
还记得我说的Kubernetes是如何持续使用标签的吗?DaemonSet 是一个控制器,用于确保守护程序应用程序始终在具有特定标签的节点上运行。
例如:您希望所有标有 logger
或 mission_critical
的节点运行记录器/审计服务守护程序。然后你创建一个 DaemonSet,并给它一个名为 logger
或 mission_critical
的节点选择器。Kubernetes 将寻找具有该标签的节点。始终确保它将有一个守护进程的实例在其上运行。因此,在该节点上运行的每个实例都可以在本地访问该守护进程。
在我的应用程序中,NSQ 守护进程可能是一个 DaemonSet。为了确保它在具有接收器组件的节点上运行,我采用 receiver
标记一个节点,并用 receiver
应用程序选择器指定一个 DaemonSet。
DaemonSet 具有 ReplicaSet 的所有优点。它是可扩展的并由Kubernetes管理它。这意味着,所有的生命周期事件都由 Kube 处理,确保它永不消亡,并且一旦发生,它将立即被替换。
在 Kubernetes 中做扩展很简单。ReplicaSets 负责管理 Pod 的实例数量,如 nginx 部署中所看到的,使用“replicas:3”设置。我们应该以允许 Kubernetes 运行它的多个副本的方式编写我们的应用程序。
当然这些设置是巨大的。你可以指定哪些复制必须在什么节点上运行,或者在各种等待时间等待实例出现的时间。你可以在这里阅读关于此主题的更多信息:Horizontal Scaling 和此处:Interactive Scaling with Kubernetes,当然还有一个 ReplicaSet 控件的详细信息 所有的 scaling 都可以在 Kubernetes 中实现。
这是一个处理容器编排的便利工具。 它的基本单位是具有分层的架构的 Pods。顶层是 Deployments,通过它处理所有其他资源。它高度可配置,提供了一个用于所有调用的 API,因此比起运行 kubectl
,你可以编写自己的逻辑将信息发送到 Kubernetes API。
Kubernetes 现在支持所有主要的云提供商,它完全是开源的,随意贡献!如果你想深入了解它的工作方式,请查看代码:Kubernetes on Github。
我将使用 Minikube。Minikube 是一个本地 Kubernetes 集群模拟器。尽管模拟多个节点并不是很好,但如果只是着手去学习并在本地折腾一下的话,这种方式不需要任何的开销,是极好的。Minikube是基于虚拟机的,如果需要的话,可以使用 VirtualBox 等进行微调。
所有我将要使用的 kube 模板文件可以在这里找到:Kube files。
注意:如果稍后想要使用 scaling 但注意到复制总是处于“Pending”状态,请记住 minikube 仅使用单个节点。它可能不允许同一节点上有多个副本,或者只是明显耗尽了资源。您可以使用以下命令检查可用资源:
kubectl get nodes -o yaml
Kubernetes 支持大部分容器。我将要使用 Docker。对于我构建的所有服务,存储库中都包含一个 Dockerfile。我鼓励你去研究它们。他们大多数都很简单。对于 Go 服务,我正在使用最近引入的多阶段构建。Go 服务是基于 Alpine Linux 的。人脸识别服务是 Python实现的。NSQ 和 MySQL 正在使用他们自己的容器。
Kubernetes 使用命名空间。如果你没有指定任何命名空间,它将使用 default
命名空间。我将永久设置一个上下文以避免污染默认命名空间。 你可以这样做:
? kubectl config set-context kube-face-cluster --namespace=face
Context "kube-face-cluster" created.
一旦它创建完毕,你也必须开始使用上下文,如下所示:
? kubectl config use-context kube-face-cluster
Switched to context "kube-face-cluster".
在此之后,所有 kubectl
命令将使用命名空间 face
。
Pods 和 Services 概述:
它会在 / unknown_people
下寻找名为 unknown220.jpg 的图像,在 unknown_folder 中找到与未知图像中的人相对应的图像,并返回匹配图像的名称。
查看日志,你会看到如下内容:
# Receiver
? curl -d '{"path":"/unknown_people/unknown219.jpg"}' http://192.168.99.100:30251/image/post
got path: {Path:/unknown_people/unknown219.jpg}
image saved with id: 4
image sent to nsq
# Image Processor
2018/03/26 18:11:21 INF 1 [images/ch] querying nsqlookupd http://nsqlookup.default.svc.cluster.local:4161/lookup?topic=images
2018/03/26 18:11:59 Got a message: 4
2018/03/26 18:11:59 Processing image id: 4
2018/03/26 18:12:00 got person: Hannibal
2018/03/26 18:12:00 updating record with person id
2018/03/26 18:12:00 done
这样,所有服务就部署完成了。
最后,还有一个小型的 web 应用程序,它能够方便地展示数据库中的信息。这也是一个面向公众的接收服务,其参数与接收器相同。
它看起来像这样:
在这里,客户端是 curl
。通常情况下,如果客户端是个服务,我会改一下,在新的路径抛出 404 时可以再试试老的路径。
为简洁起见,我不修改 NSQ 和其他用来批量图像处理的操作,他们仍然会一个一个接收。这就当作业留给你们来做了。
要执行滚动更新,我必须首先从接收器服务创建一个新镜像。
docker build -t skarlso/kube-receiver-alpine:v1.1 .
一旦完成,我们可以开始推出更改。
在 Kubernetes 中,您可以通过多种方式配置滚动更新:
如果我在我的配置文件中使用了一个名为 v1.0
的容器版本,那么更新只是简单地调用:
kubectl rolling-update receiver --image:skarlso/kube-receiver-alpine:v1.1
如果在部署期间出现问题,我们总是可以回滚。
kubectl rolling-update receiver --rollback
它将恢复以前的版本。 不需要大惊小怪,没有任何麻烦。
手动更新的问题在于它们不在源代码控制中。
考虑一下:由于手动进行“快速修复”,一些服务器得到了更新,但没有人目睹它,并且没有记录。另一个人出现并对模板进行更改并将模板应用到群集。所有服务器都会更新,然后突然出现服务中断。
长话短说,更新后的服务器已经被覆盖,因为该模板没有反映手动完成的工作。
推荐的方法是更改??模板以使用新版本,并使用 apply
命令应用模板。
Kubernetes 建议使用 ReplicaSets 进行部署应处理分发这意味着滚动更新必须至少有两个副本。如果少于两个副本存在,则更新将不起作用(除非 maxUnavailable
设置为 1)。我增加了 yaml 的副本数量。我还为接收器容器设置了新的镜像版本。
replicas: 2
...
spec:
containers:
- name: receiver
image: skarlso/kube-receiver-alpine:v1.1
...
看看处理情况,这是你应该看到的:
? kubectl rollout status deployment/receiver-deployment
Waiting for rollout to finish: 1 out of 2 new replicas have been updated...
您可以通过指定模板的 strategy
部分添加其他部署配置设置,如下所示:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
有关滚动更新的更多信息,请参见以下文档:Deployment Rolling Update, Updating a Deployment, Manage Deployments, Rolling Update using ReplicaController。
MINIKUBE 的用户注意:由于我们在具有一个节点和一个应用程序副本的本地机器上执行此操作,我们必须将 maxUnavailable
设置为 1
; 否则 Kubernetes 将不允许更新发生,并且新版本将保持 Pending
状态。这是因为我们不允许存在没有运行容器的服务,这基本上意味着服务中断。
用 Kubernetes 来 scaling 比较容易。由于它正在管理整个集群,因此基本上只需将一个数字放入所需副本的模板中即可使用。
迄今为止这是一篇很棒的文章,但时间太长了。我正在计划编写一个后续行动,我将通过多个节点和副本真正扩展 AWS 的功能; 再加上 Kops 部署 Kubernetes 集群。敬请期待!
kubectl delete deployments --all
kubectl delete services -all
女士们,先生们。我们用 Kubernetes 编写,部署,更新和扩展了(当然还不是真的)分布式应用程序。
如果您有任何问题,请随时在下面的评论中讨论。我非常乐意解答。
我希望你享受阅读它,虽然这很长, 我正在考虑将它分成多篇博客,但是一个整体的单页指南是有用的,并且可以很容易地找到,保存和打印。
感谢您的阅读。
- 本文https://github.com/xitu/gold-miner/blob/master/TODO1/kubernetes-distributed-application.md
- 译者:maoqyhz
- 校对者:cf020031308、HCMY
- 如果发现译文存在错误或其他需要改进的地方,欢迎到 掘金翻译计划 对译文进行修改并 PR,也可获得相应奖励积分。文章开头的 本文永久链接 即为本文在 GitHub 上的 MarkDown 链接。
[译]Kubernetes 分布式应用部署和人脸识别 app 实例