Kubernetes

Kubernetes

Shell-operator:用于简化Kubernetes operator的创建

dummy 发表了文章 • 0 个评论 • 139 次浏览 • 2019-06-18 17:24 • 来自相关话题

我们很高兴在此介绍我们新的开源方案,它能将Kubernetes中Operator的开发提升到一个全新的更简单的水平。它可以让你在15分钟内将你的小脚本变成完全成熟的Operator而吸引你。欢迎 ...查看全部

我们很高兴在此介绍我们新的开源方案,它能将Kubernetes中Operator的开发提升到一个全新的更简单的水平。它可以让你在15分钟内将你的小脚本变成完全成熟的Operator而吸引你。欢迎shell-operator




目标


shell-operator的思路很简单,它订阅来自Kubernetes对象的事件,并在事件发生后执行外部程序,为其提供有关事件的信息:




在我们运行Kubernetes集群期间,很多小任务开始显现。在Flant,我们迫切想要以正确的方式来自动化它们,因此我们觉得需要更智能的解决方案。通常你可以使用基本的bash脚本来解决所有这些任务,但是如你所知,更推荐的方式是使用Golang来编写Operators。很显然,为每个小任务分别开发成熟的Operator将会很低效。


15分钟内创建一个Operator


我们将举一个在Kubernetes集群中可以被自动化的例子,以及shell-operator可以如何帮助我们。我们将尝试复制用于访问Docker仓库的凭证。


使用私有仓库镜像的Pod应在其清单中包含指定的用于访问仓库的secret。这个secret必须在创建Pod之前先创建在每个命名空间中。你可以手动执行此操作,但是,如果我们将配置动态的多个环境,我们将为单个应用程序创建许多命名空间。在多个应用程序(甚至两个或三个)的情况下,secret的数量会变得巨大。关于secret还有一个需求:我们希望能够偶尔更改(注册表)仓库的访问密钥。因此,手动解决方案变得非常低效,你必须自动创建和更新secret。


简单的自动化


我们来写一个脚本,每N秒运行一次,并检查命名空间中secret是否存在。如果secret不存在,那么它将会被创建。这个解决方案的优势是它看起来就像是cron中的一个shell脚本,一种经典且易于理解的方法。缺点是在此脚本的两次启动之间的间隔期间可能会出现一些新的命名空间,因此在一段时间内它将不会持有这个secret。这种情况会导致启动Pod的过程中出错。


使用shell-operator进行自动化


为了使我们的脚本准确运行,经典的cron执行应该被当有新增命名空间事件发生时的执行所取代。在这种情况下,你可以在使用之前创建一个secret。让我们看看如何使用shell-operator来实现这个功能。


首先,我们先分析一下脚本,就shell-operator而言,脚本都被称之为“钩子“。每个钩子在使用--config标志执行时都会通知shell-operator将其绑定(即需要执行哪些事件)。在我们的例子中,我们将使用onKubernetesEvent


#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <

在这里,我们定义我们关注的namespace类型的添加(add)对象事件。


现在我们需要添加当事件发生时需要执行的代码:


#!/bin/bash
if [[ $1 == "--config" ]] ; then
# configuration
cat <

真棒!我们现在已有一个简洁且漂亮的脚本,想让它能真正发挥作用,我们需要准备一个镜像并将其跑在集群中。


使用钩子制作我们的镜像


你可以很轻易观察到我们在脚本里面使用了kubectljq命令。这意味着镜像中需要包含钩子,shell-operator二进制文件(它将监视事件并执行这个钩子),以及钩子需要用到的命令(kubectljq)。hub.docker.com上已提供了包含shell-operator,kubectl和jq的即用型镜像。现在是时候使用Dockerfile来添加一个钩子:


$ cat Dockerfile
FROM flant/shell-operator:v1.0.0-beta.1-alpine3.9
ADD namespace-hook.sh /hooks
$ docker build -t registry.example.com/my-operator:v1 .
$ docker push registry.example.com/my-operator:v1

在集群中运行


我们再来看看这个钩子,这次我们将关注具体的操作以及它在集群中执行的对象:



  1. 它订阅了namespace的创建事件;

  2. 它在不与它所运行的命名空间相同的空间创建一个secret。


这里我们会发现运行这个镜像的Pod需要有执行这些操作的权限。你可以授权给一个ServiceAccount。由于我们是关注整个集群中的对象,那么权限需要使用ClusterRoleClusterRoleBinding形式来配置。


YAML最终配置描述如下:


---
apiVersion: v1
kind: ServiceAccount
metadata:
name: monitor-namespaces-acc
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: monitor-namespaces
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "watch", "list"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "create", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: monitor-namespaces
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: monitor-namespaces
subjects:
- kind: ServiceAccount
name: monitor-namespaces-acc
namespace: example-monitor-namespaces

你可以将创建的镜像部署为一个简单的Deployment:


apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-operator
spec:
template:
spec:
containers:
- name: my-operator
image: registry.example.com/my-operator:v1
serviceAccountName: monitor-namespaces-acc

为方便起见,我们将创建一个单独的命名空间,用于运行shell-operator并应用创建的部署清单:


$ kubectl create ns example-monitor-namespaces
$ kubectl -n example-monitor-namespaces apply -f rbac.yaml
$ kubectl -n example-monitor-namespaces apply -f deployment.yaml

好了,shell-operator启动,它将订阅命名空间创建事件并在需要时执行钩子。



这样一个简单的shell脚本就变成了Kubernetes中一个真正的Operator,并成为集群的一部分。这样做的好处是我们避免了使用Golang来开发Operator的复杂过程:




过滤


关于对象的观察很棒,但我们通常需要响应对象中某些属性的更改,例如,增加/减少部署中的副本数量或对象对标签中的任何更新。


当一个事件发生时,shell-operator接收该对象的JSON清单。在此JSON中,你可以选择要监视的属性,并仅在更改时启动钩子。jqFilter字段可以帮助你完成这点:你应该输入将应用于JSON清单的jq表达式。


举个例子,要响应Deployment对象标签中的修改,你必须从metadata字段中提取labels字段。这个例子中你将需要如下的配置:


cat <

jqFilter表达式将Deployment的长长的JSON清单转换成带有标签的简短的JSON:




shell-operator将只会在这个简短的JSON发生变化时执行钩子。其它属性的变更将会被忽略。


钩子的执行上下文


钩子的配置允许你指定几种事件。例如你可以定义两个Kubernetes事件和两个计划调度:


{
"onKubernetesEvent": [
{
"name": "OnCreatePod",
"kind": "pod",
"event": [
"add"
]
},
{
"name": "OnModifiedNamespace",
"kind": "namespace",
"event": [
"update"
],
"jqFilter": ".metadata.labels"
}
],
"schedule": [
{
"name": "every 10 min",
"crontab": "0 */10 * * * *"
},
{
"name": "on Mondays at 12:10",
"crontab": "0 10 12 * * 1"
}
]
}

注意:shell-operator支持以crontab样式运行脚本!你可以在文档中找到额外的信息。


为了区分钩子执行的原因,shell-operator会创建一个临时文件并将其路径保存到BINDING_CONTEXT_TYPE变量中。此文件包含了执行钩子的原因的JSON描述。例如,每隔10分钟将会使用以下内容启动钩子:


[{ "binding": "every 10 min" }]

在周一的话它将以以下内容启动:


[{ "binding": "every 10 min" }, { "binding": "on Mondays at 12:10" }]

同时将有onKubernetesEvent调用的更详细的JSON,因为它包含了对象的描述:


[
{
"binding": "onCreatePod",
"resourceEvent": "add",
"resourceKind": "pod",
"resourceName": "foo",
"resourceNamespace": "bar"
}
]

你能通过名称来全面了解字段的内容(更多详细信息可在文档中找到)。使用jq从resourceName获取资源名称的示例已经在复制secret的钩子中展示:


jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH

你可以通过类似的方式去获取到其它字段。


下一步是什么呢?


在该项目仓库中的/examples directory目录里包含了一些可以直接在集群中使用的示例。你可以将它们用作你开发自己的钩子的基础。


Shell-operator同样支持使用Prometheus来收集指标。METRICS章节已描述了这些可用的指标。


你能轻易想到,shell-operator是使用Go编写的,并根据开源许可证(Apache 2.0)的条款进行分发。我们非常感谢任何关于开发在Github上的这个项目的帮助。你可以通过给我们点Star,反馈问题或者是PR来支持我们!


原文链接:Announcing shell-operator to simplify creating of Kubernetes operators(翻译:冯旭松)


Go版微服务开发框架Micro及标准2019年大整合

cleverlzc 发表了文章 • 0 个评论 • 300 次浏览 • 2019-06-16 08:38 • 来自相关话题

【编者的话】Micro是构建微服务的最简单方式,并逐渐成为云计算中基于Go的微服务开发的实际标准。通过将我们的努力整合到一个开发框架和运行时中,我们使这个过程更加简单。 Micro作为[go-micro](https://gith ...查看全部

【编者的话】Micro是构建微服务的最简单方式,并逐渐成为云计算中基于Go的微服务开发的实际标准。通过将我们的努力整合到一个开发框架和运行时中,我们使这个过程更加简单。 

Micro作为[go-micro](https://github.com/micro/go-micro)——一个微服务框架开始了它的旅程,专注于提供微服务开发的核心需求。它通过抽象出分布式系统的复杂性,为构建微服务创造了更简单的体验。

随着时间的推移,我们已经从go-micro扩展到其他工具、库和插件。这导致了我们解决问题的方式和开发人员使用微服务工具的方式的分散化。我们现在正在整合所有这些工具,以简化开发人员的体验。

从本质上来说,Micro已经成为一个独立的开发框架和微服务开发的运行时。

在讨论整合之前,让我们回顾一下迄今为止的历程。

### 主要关注点

Go-micro最初主要专注于微服务的通信方面,我们一直努力做到这一点。到目前为止,这种固执己见的方法和关注点是驱动框架成功的真正驱动力。多年来,我们已经收到了无数的请求,要求解决第二天在go-micro中构建生产就绪软件的问题。其中大部分都与可伸缩性、安全性、同步和配置有关。


虽然增加所要求的额外特性是有好处的,但我们确实希望一开始就非常专注于很好地解决一个问题。所以我们采取了一种不同的方式来促进社区这样做。

### 生态系统和插件

投入生产所涉及的不仅仅是普通的服务发现、消息编码和请求-响应。我们真正明白这一点,希望使用户能够通过可插拔和可扩展的接口选择更广泛的平台需求。通过[资源管理器](https://micro.mu/explore/)促进生态系统,资源管理器聚合了GitHub上的基于微服务的开源项目,并通过[go-plugins](https://github.com/micro/go-plugins)扩展插件。


一般来说,Go插件已经取得了巨大的成功,因为它允许开发人员将大量的复杂性转移到为这些需求构建的系统上。例如用于度量的Prometheus、用于分布式跟踪的Zipkin和用于持久消息传递的Kafka。

### 交互点

Go Micro确实是微服务开发的核心,但是随着服务的编写,接下来的问题就转移到了:我如何查询它们,如何与它们交互,如何通过传统方式为它们服务。

鉴于go-micro使用了一个基于RPC/Protobuf的协议,该协议既可插拔又不依赖于运行时,我们需要某种方法来解决这个问题。这导致了微服务工具包[Micro](https://github.com/micro/micro)的产生。Micro提供了API网关、网络仪表板、cli命令行工具、slack bot机器人程序、服务代理等等。


Micro工具包通过http api、浏览器、slack命令和命令行接口充当交互点。这些是我们查询和构建应用程序的常见方式,对于我们来说,提供一个真正支持这一点的运行时非常重要。然而,但它仍然把重点放在通信上。

###其他工具

虽然插件和工具包极大地帮助了使用了Micro的用户,但在关键领域仍然缺乏。很明显,我们的社区希望我们能够围绕产品开发的平台工具来解决更多的问题,而不是必须在他们各自的公司中单独完成。我们需要为动态配置、分布式同步和为Kubernetes这样的系统提供更广泛的解决方案等方面提供相同类型的抽象。

于是我们创建了以下项目:


- [micro/go-config](https://github.com/micro/go-config): 一个动态配置库

- [micro/go-sync](https://github.com/asim/go-sync):一个分布式同步库

- [micro/kubernetes](https://github.com/micro/kubernetes):在Kubernetes平台上的初始化

- [examples](https://github.com/micro/examples):使用举例

- [microhq](https://github.com/microhq):微服务预构建


这些是一部分repos、库和工具,用于尝试解决我们社区更广泛的需求。在过去的四年里,repos的数量不断增长,新用户的入门体验也变得更加困难。进入壁垒急剧增加,我们意识到需要做出一些改变。

在过去的几周里,我们意识到[go-micro](https://github.com/micro/go-micro)确实是大多数用户开发微服务的焦点。很明显,他们想要额外的功能,作为这个库的一部分以及一个自我描述的框架,我们真的需要通过解决那些第二天的问题来实现这一点,而不要求开发人员寻求其他方法。

本质上,go-micro将成为微服务开发的全面和独立框架。

我们通过将所有库迁移到go-micro开始了整合过程,在接下来的几周里,我们将继续进行重构,以提供更简单的默认入门体验,同时还为日志记录、跟踪、度量、身份验证等添加更多功能。


不过,我们也没有忘记Micro。在我们看来,当构建了微服务之后,仍然需要一种查询、运行和管理它们的方法。所有人都认为Micro将是微服务开发的运行时。我们正在致力于提供一种更简单的方法来管理微服务开发的端到端流程,并且应该很快会有更多消息发布。

### 总结

Micro是构建微服务的最简单方式,并逐渐成为云计算中基于Go的微服务开发的实际标准。通过将我们的努力整合到一个开发框架和运行时中,我们使这个过程更加简单。

原文链接:Micro - The great consolidation of 2019


**译者**:Mr.lzc,软件工程师、DevOpsDays深圳核心组织者,目前供职于华为,从事云存储工作,以Cloud Native方式构建云文件系统服务,专注于Kubernetes、微服务领域。


睿云智合 | 开源项目Breeze成为Kubernetes认证安装工具

wise2c 发表了文章 • 0 个评论 • 388 次浏览 • 2019-06-14 11:46 • 来自相关话题

近日,睿云智合开源的Kubernetes图形化部署工具Breeze项目通过Kubernetes一致性认证成为认证Kubernetes安装工具。目前Breeze已经列入 Cloud Native Landscape 生态图谱的认证安装工具板块。 ...查看全部

近日,睿云智合开源的Kubernetes图形化部署工具Breeze项目通过Kubernetes一致性认证成为认证Kubernetes安装工具。目前Breeze已经列入 Cloud Native Landscape 生态图谱的认证安装工具板块。


Beeze项目是睿云智合(Wise2C)2018年5月所开源的Kubernetes图形化部署工具,能大大简化Kubernetes部署的步骤,支持全离线环境的部署,而且可以不需要翻墙获取Google的相应资源包,尤其适合中国金融、运营商、国有企业等不便访问互联网的私有数据中心部署环境场景。目前支持最新版Kubernetes、Etcd、Docker、Harbor以及Prometheus等云原生工具部署,同时支持Istio部署 (内置Kiali, Jaeger, Prometheus, Grafana)。推出至今获得开源技术社区用户的一致好评目前在Github上已获600余星。



功能亮点:

易于运行:

Breeze将您需要的所有资源(比如Kubernetes组件镜像以及用于部署Kubernetes集群的 ansible playbooks)组合为一个Docker镜像(wise2c/playbook)。它还可以作为本地yum存储库服务器工作。您只需要安装了Docker引擎的Linux服务器来运行Breeze。



简化了kubernetes集群部署的过程:


通过几个简单的命令,您可以轻而易举地运行,然后通过图形界面完成所有其他部署过程。



支持离线部署:

在部署服务器上加载了4个镜像(playbook、yum repo、pagoda、deploy ui)后,就可以在不接入互联网的情况下设置kubernetes集群。Breeze作为一个yum存储库服务器工作,部署一个本地的Harbor镜像仓库,并使用kubeadm来设置kubernetes集群。所有docker镜像将从本地镜像仓库提取。



支持多集群:

Breeze支持多个Kubernetes集群部署。

支持高可用架构:

通过Breeze,您可以设置Kubernetes集群,其中3台主服务器和3台ETCD服务器与Haproxy和Keepalived相结合。所有工作节点将使用虚拟浮动IP地址与主服务器通信。



Breeze项目是100%开源的云原生部署工具


GitHub项目地址:

https://github.com/wise2c-devops/breeze

安装过程演示视频在此:

 https://pan.baidu.com/s/1X0ZYt48wfYNrSrH7vvEKiw

常见排错说明在此:

https://github.com/wise2c-devops/breeze/blob/master/TroubleShooting-CN.md

PDF手册请在此处下载:

https://github.com/wise2c-devops/breeze/raw/master/manual/BreezeManual-CN.pdf

DockOne微信分享(二一一):基于Actor模型的CQRS/ES解决方案分享

JetLee 发表了文章 • 0 个评论 • 442 次浏览 • 2019-06-05 16:17 • 来自相关话题

【编者的话】2017年加密货币比较流行,我曾有幸在加密货币交易所参与开发工作,为了应对交易系统高性能、高并发、高可用的要求,我们使用基于Actor模型的Orleans技术,采用CQRS/ES架构结合Service Fabric开展了富有挑战性的工作。本文 ...查看全部

【编者的话】2017年加密货币比较流行,我曾有幸在加密货币交易所参与开发工作,为了应对交易系统高性能、高并发、高可用的要求,我们使用基于Actor模型的Orleans技术,采用CQRS/ES架构结合Service Fabric开展了富有挑战性的工作。本文将从不同视角为大家介绍Actor模型、CQRS/ES架构以及Service Fabric在高并发场景中的考量和应用。



话题由三部分组成:




  • Actor模型&Orleans:在编程的层面,从细粒度-由下向上的角度介绍Actor模型;

  • CQRS/ES:在框架的层面,从粗粒度-由上向下的角度介绍Actor模型,说明Orleans技术在架构方面的价值;

  • Service Fabric:从架构部署的角度将上述方案落地上线。



群里的小伙伴技术栈可能多是Java和Go体系,分享的话题主要是C#技术栈,没有语言纷争,彼此相互学习。比如:Scala中,Actor模型框架有akka,CQRS/ES模式与编程语言无关,Service Fabric与Kubernetes是同类平台,可以相互替代,我自己也在学习Kubernetes。





Actor模型&Orleans(细粒度)





共享内存模型



多核处理器出现后,大家常用的并发编程模型是共享内存模型。


这种编程模型的使用带来了许多痛点,比如:




  • 编程:多线程、锁、并发集合、异步、设计模式(队列、约定顺序、权重)、编译

  • 无力:单系统的无力性:①地理分布型、②容错型

  • 性能:锁,性能会降低

  • 测试:



    • 从坑里爬出来不难,难的是我们不知道自己是不是在坑里(开发调试的时候没有热点可能是正常的)

    • 遇到bug难以重现。有些问题特别是系统规模大了,可能运行几个月才能重现问题

  • 维护:



    • 我们要保证所有对象的同步都是正确的、顺序的获取多个锁。

    • 12个月后换了另外10个程序员仍然按照这个规则维护代码。



简单总结:




  • 并发问题确实存在

  • 共享内存模型正确使用掌握的知识量多

  • 加锁效率就低

  • 存在许多不确定性





Actor模型



Actor模型是一个概念模型,用于处理并发计算。Actor由3部分组成:状态(State)+行为(Behavior)+邮箱(Mailbox),State是指Actor对象的变量信息,存在于Actor之中,Actor之间不共享内存数据,Actor只会在接收到消息后,调用自己的方法改变自己的state,从而避免并发条件下的死锁等问题;Behavior是指Actor的计算行为逻辑;邮箱建立Actor之间的联系,一个Actor发送消息后,接收消息的Actor将消息放入邮箱中等待处理,邮箱内部通过队列实现,消息传递通过异步方式进行。


Actor是分布式存在的内存状态及单线程计算单元,一个Id对应的Actor只会在集群种存在一个(有状态的 Actor在集群中一个Id只会存在一个实例,无状态的可配置为根据流量存在多个),使用者只需要通过Id就能随时访问不需要关注该Actor在集群的什么位置。单线程计算单元保证了消息的顺序到达,不存在Actor内部状态竞用问题。



Actor是分布式存在的内存状态及单线程计算单元,一个ID对应的Actor只会在集群种存在一个(有状态的 Actor在集群中一个ID只会存在一个实例,无状态的可配置为根据流量存在多个),使用者只需要通过ID就能随时访问不需要关注该Actor在集群的什么位置。单线程计算单元保证了消息的顺序到达,不存在Actor内部状态竞用问题。



举个例子:



多个玩家合作在打Boss,每个玩家都是一个单独的线程,但是Boss的血量需要在多个玩家之间同步。同时这个Boss在多个服务器中都存在,因此每个服务器都有多个玩家会同时打这个服务器里面的Boss。



如果多线程并发请求,默认情况下它只会并发处理。这种情况下可能造成数据冲突。但是Actor是单线程模型,意味着即使多线程来通过Actor ID调用同一个Actor,任何函数调用都是只允许一个线程进行操作。并且同时只能有一个线程在使用一个Actor实例。





Actor模型:Orleans



Actor模型这么好,怎么实现?



可以通过特定的Actor工具或直接使用编程语言实现Actor模型,Erlang语言含有Actor元素,Scala可以通过Akka框架实现Actor编程。C#语言中有两类比较流行,Akka.NET框架和Orleans框架。这次分享内容使用了Orleans框架。



特点:



Erlang和Akka的Actor平台仍然使开发人员负担许多分布式系统的复杂性:关键的挑战是开发管理Actor生命周期的代码,处理分布式竞争、处理故障和恢复Actor以及分布式资源管理等等都很复杂。Orleans简化了许多复杂性。



优点:




  • 降低开发、测试、维护的难度

  • 特殊场景下锁依旧会用到,但频率大大降低,业务代码里甚至不会用到锁

  • 关注并发时,只需要关注多个actor之间的消息流

  • 方便测试

  • 容错

  • 分布式内存



缺点:




  • 也会出现死锁(调用顺序原因)

  • 多个Actor不共享状态,通过消息传递,每次调用都是一次网络请求,不太适合实施细粒度的并行

  • 编程思维需要转变






第一小节总结:上面内容由下往上,从代码层面细粒度层面表达了采用Actor模型的好处或原因。





CQRS/ES(架构层面)





从1000万用户并发修改用户资料的假设场景开始







  1. 每次修改操作耗时200ms,每秒5个操作

  2. MySQL连接数在5K,分10个库

  3. 5 *5k *10=25万TPS

  4. 1000万/25万=40s

 

在秒杀场景中,由于对乐观锁/悲观锁的使用,推测系统响应时间更复杂。





使用Actor解决高并发的性能问题





1000万用户,一个用户一个Actor,1000万个内存对象。


200万件SKU,一件SKU一个Actor,200万个内存对象。




  • 平均一个SKU承担1000万/200万=5个请求

  • 1000万对数据库的读写压力变成了200万

  • 1000万的读写是同步的,200万的数据库压力是异步的

  • 异步落盘时可以采用批量操作



总结:



由于1000万+用户的请求根据购物意愿分散到200万个商品SKU上:每个内存领域对象都强制串行执行用户请求,避免了竞争争抢;内存领域对象上扣库存操作处理时间极快,基本没可能出现请求阻塞情况。



从架构层面彻底解决高并发争抢的性能问题。理论模型,TPS>100万+……





EventSourcing:内存对象高可用保障



Actor是分布式存在的内存状态及单线程计算单元,采用EventSourcing只记录状态变化引发的事件,事件落盘时只有Add操作,上述设计中很依赖Actor中State,事件溯源提高性能的同时,可以用来保证内存数据的高可用。






CQRS



上面1000万并发场景的内容来自网友分享的PPT,与我们实际项目思路一致,就拿来与大家分享这个过程,下图是我们交易所项目中的架构图:


开源版本架构图:


开源项目github:https://github.com/RayTale/Ray



第二小节总结:由上往下,架构层面粗粒度层面表达了采用Actor模型的好处或原因。





Service Fabric



系统开发完成后Actor要组成集群,系统在集群中部署,实现高性能、高可用、可伸缩的要求。部署阶段可以选择Service Fabric或者Kubernetes,目的是降低分布式系统部署、管理的难度,同时满足弹性伸缩。



交易所项目可以采用Service Fabric部署,也可以采用Kubernetes,当时Kubernetes还没这么流行,我们采用了Service Fabric,Service Fabric 是一款微软开源的分布式系统平台,可方便用户轻松打包、部署和管理可缩放的可靠微服务和容器。开发人员和管理员不需解决复杂的基础结构问题,只需专注于实现苛刻的任务关键型工作负荷,即那些可缩放、可靠且易于管理的工作负荷。支持Windows与Linux部署,Windows上的部署文档齐全,但在Linux上官方资料没有。现在推荐Kubernetes。



第三小节总结:




  1. 借助Service Fabric或Kubernetes实现低成本运维、构建集群的目的。

  2. 建立分布式系统的两种最佳实践:



    • 进程级别:容器+运维工具(Kubernetes/Service Fabric)

    • 线程级别:Actor+运维工具(Kubernetes/Service Fabric)

参考:




  1. ES/CQRS部分内容参考:《领域模型 + 内存计算 + 微服务的协奏曲:乾坤(演讲稿)》2017年互联网应用架构实战峰会

  2. 其他细节来自互联网,不一一列出





Q&A



Q:单点故障后,正在处理的cache数据如何处理的,例如,http、tcp请求……毕竟涉及到钱?

A:Actor有激活和失活的生命周期,激活的时候使用快照和Events来恢复最新内存状态,失活的时候保存快照。Actor框架保证系统中同一个key只会存在同一个Actor,当单点故障后,Actor会在其它节点重建并恢复最新状态。



Q:数据落地得策略是什么?还是说就是直接落地?

A:event数据直接落地;用于支持查询的数据,是Handler消费event后异步落库。



Q:Grain Persistence使用Relational Storage容量和速度会不会是瓶颈?

A:Grain Persistence存的是Grain的快照和event,event是只增的,速度没有出现瓶颈,而且开源版本测试中PostgreSQL性能优于MongoDB,在存储中针对这两个方面做了优化:比如分表、归档处理、快照处理、批量处理。



Q:Orleans中,持久化事件时,是否有支持并发冲突的检测,是如何实现的?

A:Orleans不支持;工作中,在事件持久化时做了这方面的工作,方式是根据版本号。



以上内容根据2019年6月4日晚微信群分享内容整理。分享人郑承良,上海某科技公司架构师,对高并发场景下的分布式金融系统拥有丰富的实战经验,曾为澳大利亚、迪拜多家交易所提供技术支持。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiese,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

Kubernetes IN Docker - local clusters for testing Kubernetes

老马 发表了文章 • 0 个评论 • 289 次浏览 • 2019-06-04 21:52 • 来自相关话题

Brief Kind(Kubernetes IN Docker是一个用来快速创建 ...查看全部

Brief



Kind(Kubernetes IN Docker是一个用来快速创建和测试kubernetes的工具,Kind把环境的依赖降低到了最小,仅需要机器安装了Docker即可。



Kind 可以做什么?




  • 快速创建一个或多个Kubernetes集群(几分钟)

  • 支持HA Master部署高可用的Kubernetes集群

  • 支持从源码构建并部署一个Kubernetes集群

  • 可以快速低成本体验一个最新的Kubernetes集群,并支持Kubernetes的绝大部分功能

  • 支持本地离线运行一个多节点集群



Kind 有哪些优势?




  • 最小的安装依赖,仅需要安装Docker即可

  • 使用快速简单,使用kind cli工具即可快速创建集群

  • 使用container来 mockkubernetes node

  • 内部使用kubeadm的官方主流部署工具

  • 使用了containerd

  • 通过了CNCF官方的k8s conformance测试





Usage





GO111MODULE="on" go get sigs.k8s.io/kind@v0.3.0 && kind create cluster




How it work



Kind 使用一个 container 来模拟一个 Node,在 Container 里面跑了 systemd ,并用 systemd 托管了 kubelet 以及 containerd,然后容器内部的 kubelet 把其他 Kubernetes 组件,比如 kube-apiserver、etcd、CNI 等组件跑起来。



可以通过配置文件的方式,来通过创建多个 container 的方式,来模拟创建多个 Node,并以这些 Node 来构建一个多节点的 Kubernetes 集群。



Kind 内部使用了 kubeadm 这个工具来做集群的部署,包括 ha master 的高可用集群,也是借助 kubeadm 提供的aplha特性提供的。同时,在 HA Master 下,额外部署了一个 Nginx 用来提供负载均衡 vip。





Build Images



Kind 的镜像分为两个,一个 Node 镜像,一个 Base 镜像。



Node 镜像



Node 镜像的构建比较复杂,目前是通过运行 Base 镜像,并在 Base 镜像内执行操作,再保存此容器内容为镜像的方式来构建的,包含的操作有:




  • 构建 Kubernetes 相关资源(比如二进制文件和镜像)

  • 运行一个用于构建的容器

  • 把构建的 Kubernetes 相关资源复制到容器里

  • 调整部分组件配置参数,以支持在容器内运行

  • 预先拉去运行环境需要的镜像

  • 通过 docker commit 方式保存当前的构建容器为 node 镜像



具体的逻辑,可以参考node.go



Base 镜像



Base 镜像目前使用了 Ubuntu 19.04 作为基础镜像,做了下面的调整:




  • 安装 systemd 相关的包,并调整一些配置以适应在容器内运行

  • 安装 Kubernetes 运行时的依赖包,比如 conntrack、socat、CNI

  • 安装容器



运行环境,比如 containerd、crictl




  • 配置自己的 ENTRYPOINT 脚本,以适应和调整容器内运行的问题



具体的逻辑,可以参考构建的Dockerfile





Create Cluster



Kind 创建集群的基本过程为:




  1. 根据传入的参数,来创建 container,分为 control node 和 worker node 两种(如果是 ha master,还有一个 loadbalancer node)

  2. 如果需要,配置 loadbalancer 的配置,主要是 Nginx 配置文件

  3. 生成 kubeadm 配置

  4. 对于第一个控制节点,使用 kubeadm init 初始化单节点集群

  5. 配置安装 CNI 插件

  6. 配置存储(实际是安装了一个使用 hostpath 的 storageclass)

  7. 其他的控制节点,通过kubeadm join --experimental-control-plane的方式来扩容控制节点

  8. 通过 kubeadm join 扩容其他的工作节点

  9. 等待集群创建完成

  10. 生成访问配置,打印使用帮助具体的创建流程,可以参考代码create.go



这里关于每个容器,是如何作为 Node 跑起来的,可以简单讲解些原理:



根据不同的角色,调用不同的函数创建节点nodes.go





// TODO(bentheelder): remove network in favor of []cri.PortMapping when that is in
func (d *nodeSpec) Create(clusterLabel string) (node *nodes.Node, err error) {
// create the node into a container (docker run, but it is paused, see createNode)
// TODO(bentheelder): decouple from config objects further
switch d.Role {
case constants.ExternalLoadBalancerNodeRoleValue:
node, err = nodes.CreateExternalLoadBalancerNode(d.Name, d.Image, clusterLabel, d.APIServerAddress, d.APIServerPort)
case constants.ControlPlaneNodeRoleValue:
node, err = nodes.CreateControlPlaneNode(d.Name, d.Image, clusterLabel, d.APIServerAddress, d.APIServerPort, d.ExtraMounts)
case constants.WorkerNodeRoleValue:
node, err = nodes.CreateWorkerNode(d.Name, d.Image, clusterLabel, d.ExtraMounts)
default:
return nil, errors.Errorf("unknown node role: %s", d.Role)
}
return node, err
}


节点(容器)创建时,通过配置 –privileged,挂载 tmpfs,修改主机名等,来运行节点create



func createNode(name, image, clusterLabel, role string, mounts []cri.Mount, extraArgs ...string) (handle *Node, err error) {
runArgs := []string{
"-d", // run the container detached
"-t", // allocate a tty for entrypoint logs
// running containers in a container requires privileged
// NOTE: we could try to replicate this with --cap-add, and use less
// privileges, but this flag also changes some mounts that are necessary
// including some ones docker would otherwise do by default.
// for now this is what we want. in the future we may revisit this.
"--privileged",
"--security-opt", "seccomp=unconfined", // also ignore seccomp
"--tmpfs", "/tmp", // various things depend on working /tmp
"--tmpfs", "/run", // systemd wants a writable /run
// some k8s things want /lib/modules
"-v", "/lib/modules:/lib/modules:ro",
"--hostname", name, // make hostname match container name
"--name", name, // ... and set the container name
// label the node with the cluster ID
"--label", clusterLabel,
// label the node with the role ID
"--label", fmt.Sprintf("%s=%s", constants.NodeRoleKey, role),
}

// pass proxy environment variables to be used by node's docker deamon
proxyDetails := getProxyDetails()
for key, val := range proxyDetails.Envs {
runArgs = append(runArgs, "-e", fmt.Sprintf("%s=%s", key, val))
}

// adds node specific args
runArgs = append(runArgs, extraArgs...)

if docker.UsernsRemap() {
// We need this argument in order to make this command work
// in systems that have userns-remap enabled on the docker daemon
runArgs = append(runArgs, "--userns=host")
}

err = docker.Run(
image,
docker.WithRunArgs(runArgs...),
docker.WithMounts(mounts),
)

// we should return a handle so the caller can clean it up
handle = FromName(name)
if err != nil {
return handle, errors.Wrap(err, "docker run error")
}

return handle, nil
}


More



Kind是一个比较简单有趣的项目,Kind的scope定的比较明确和具体,也定的比较小,其实借助 Kind 或者 Kind 的思想,可以做更多的事情,比如:




  • 在单节点部署自己的上层平台

  • 借助容器 mock 节点的方式,优化现有的测试方案

  • 自动化的部署测试

  • 自动化的 e2e 测试



原文链接:https://mp.weixin.qq.com/s/Vhr0ml1wI1BIoxKqKGTXRg

58集团云平台架构实践与演进

玻璃樽 发表了文章 • 0 个评论 • 458 次浏览 • 2019-06-04 19:23 • 来自相关话题

【编者的话】在17年底,我们分享了《高可用Docker容器云在58集团的实践》这篇文章,对整个容器云基础环境搭建与应用选型进行了详细介绍,本文是在该文章基础之上的进阶篇,是针对具体业务场景的落地解决方案。如果对基础环境选型比较感兴趣,可以查看上篇文章,在本文的 ...查看全部
【编者的话】在17年底,我们分享了《高可用Docker容器云在58集团的实践》这篇文章,对整个容器云基础环境搭建与应用选型进行了详细介绍,本文是在该文章基础之上的进阶篇,是针对具体业务场景的落地解决方案。如果对基础环境选型比较感兴趣,可以查看上篇文章,在本文的最后会附上相关文章的链接。对于上篇文章讨论过的内容,本文将不再进行详细讨论。后续每个月,云团队都会选择平台中某一具体领域的相关工作进行详细讨论与分享,欢迎大家关注。大家想了解哪方面的实现方案与细节,可进行相应留言。
#背景

通过容器化技术,58云计算平台主要解决以下几个问题:

  1. 资源利用率低:通过云化技术可以将资源利用率提升至原有的3-4倍,甚至更高。
  2. 服务扩容效率低:将传统扩容的时间从小时级别降低为分钟级别。
  3. 上线流程不规范:基于同一的镜像模板,约束整个上线过程。

为了解决上述问题,云团队通过技术选型与反复论证最终决定基于Docker与Kubernetes体系构建整个容器云环境。

云计算平台的发展历程如下:
1.png

#整体架构

58云计算平台的整体架构如下:
2.png

所有容器云的架构都是相似的,这里不做赘述,具体可查看上篇文章。

云计算平台承载了集团90%以上的业务流量,作为核心的服务管理与上线系统,它并不是独立运作的,为了保证整个上线流程的一致性与流畅度,满足业务日常管理与维护的通用需求,云平台与集团内部多个核心系统与组件进行了相应的对接与联动。
3.jpg

在项目管理方面,云平台与代码管理系统、项目管理等系统进行了内部对接,实现了代码从编译到生成镜像,再到环境部署的完全自动化。

在运维方面,云平台与CMDB、服务树、监控系统等多个运维系统进行了打通与整合,保证整个运维体系的完整性与用户体验的一致性。

在基础组件方面,集团内部的服务治理体系与常用的中间件系统都针对云平台进行了相应的改造,以适配云平台的工作模式,同时云平台也集成了现有的数据收集组件,保证数据流与用户习惯在云化前后是一致的。

云平台的使用方面,平台定义了四套环境,四套环境基于唯一的镜像仓库,这使得同一个代码版本可以在不同的环境之间进行流转,在保证代码唯一性的同时也使得相关环境的变更不会传递到下一个环境。
4.png

#架构演进

构建一个适配多种业务场景的容器云有很多细节需要考虑,云团队做了很多工作,由于篇幅关系,这里主要和大家分享58云计算平台在“网络架构”和“服务发现”两个核心组件上的架构实践与演进。
##网络架构

在网络架构方面,通过对比常用的六种容器组网模型,云计算平台选择“bridge+vlan”的方式作为基础网络模型。
5.png

原生bridge网络模型存在两个明显的缺点:IP利用率低、缺少网络限速。

为了解决IP地址利用率低的问题,云平台基于docker的CNM接口开发了网络插件,支持多个宿主间共享同一个容器网段,也支持IP地址在不同的宿主间复用。
6.png

这种网络模式下,分配给业务实例的IP地址是随机的,实例每次重启IP地址都可能会发生变更。在推进业务云化的早期,这种模式被业务所接受,但是随着云化进程的不断深入,业务方面提出了更高的要求:固定IP。

业务需求来源于真实的案例:有些服务要求IP不能发生变化,特别是某些依赖于第三方外部接口的服务。同时集团内部很多系统都是以IP固定为前提,如果IP随机分配,这些现有系统将不可用或很难用,极大的影响用户体验。如果IP固定的需求不能被满足,业务云化将很难推进下去。所以在18年4月份,云平台对网络架构进行了升级,支持了固定IP模式。网络架构的升级很好的保证了业务云化的进程。
7.png

固定IP的网络架构基于Kubernetes的CNI接口实现,增加了IP控制器模块,业务每次扩缩容时,都会与IP控制器交互,进行IP的变更。在业务正常升级流程中,归属于业务的IP将不会发生变化。依托于腾讯机房的网络支撑,平台将容器网段的路由规则下发到交换机,实现了容器的全网漂移,而不仅仅局限于固定的交换机。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

针对网络限速的需求,云平台结合容器网络虚拟化的特性,基于自研监控与tc工具实现了容器网络限速的能力。
8.jpg

标准的tc工具只支持单向限速,即出口流量限速。单方向限速无法满足业务的实际需求。在深入研究容器网络虚拟化的原理后,我们发现,容器在做虚拟化时,会创建一个网卡对,他们是对等的网络设备,在宿主机上体现为veth,在容器内部体现为eth0。基于这个特性,我们实现了双向限速:即对两块网卡同时做出口流量限速。由于是对等网卡,所以对veth做出口流量限速就相当于对eth0做入口流量限速。

在网络限速的基础上,云平台又进行了多维度的完善,分别支持动态限速、秒级限速与弹性限速等网络应用场景,极大的实现带宽复用。
##服务发现

服务发现是容器云平台中非常核心的服务组件,是流量的入口,也是服务对外暴露的接口。云平台中IP动态分配,节点弹性伸缩,需要有一套自动变更负载均衡器的方式。对于后端服务,集团有成熟的服务治理框架与体系,在云化过程中,中间件团队对服务治理体系进行了改造使其可以适配不断变化的云环境。对于前端服务,集团是基于Nginx做负载均衡,并且没有一套支持IP自动变更的架构。为了实现前端服务的云化,云平台团队与运维团队共同设计了全新的服务发现架构。

在调研阶段,云团队也调研了Kubernetes自带的服务发现机制,这一机制无法提供复杂的负载均衡策略,并且无法满足业务在特殊场景下需要对部分节点摘除流量的需求,所以最终我们否定了这一方案。
9.png

这是业界典型的服务发现架构。服务端和负载均衡器通过Consul进行解耦。服务注册在Consul中,负载均衡器通过Watch Consul中目录的变化来实时感知节点变更。在云化的早期,为了满足快速上线的需求,云平台对集团内部的Java Web框架进行了修改,使得其以心跳的方式自动注册到Consul中。这很好的解决了负载均衡器自动感知业务变更的问题以及云化过程中的流量灰度问题。

这一架构也引入了新的问题:调试问题与多语言扩展问题。由于是Java框架代理业务注册到Consul中,这使得活跃节点无法被下掉流量从而进行调试,但实际场景中很多故障调试需要下掉流量后才能进行。对Java语言的支持是通过修改框架来完成的,而集团中还有很多其他类型语言,比如PHP、Node.js和Go等。对每种接入语言或框架都需要以修改代码的方式才能接入到云平台中来,开发成本高并且对业务不友好。Consul被直接暴露给服务方,也增加了Consul的安全风险。

基于此,在18年4月,云平台对整个服务发现架构进行了升级。
10.png

新的服务发现架构中,业务与Consul中间增加了Proxy代理层,代理层通过Watch Kubernetes事件实时感知业务节点信息的变化,配合健康检查功能可以保证业务节点在变更时流量无损。基于代理层,任意语言的程序不需要做任何修改,仅通过简单配置即可接入到云平台。服务的注册与发现托管到云平台,Consul组件对业务透明,开发人员使用更友好。
#复盘与反思

回顾这两年来的容器云架构演进过程与业务云化历程,复盘遇到的棘手问题及其解决方案,与大家共同探讨。

在整个云平台的设计之初,我们有如下的设计考量:
11.png

容器云都会面临一个问题:对于业务来说,是容器还是虚拟机?虚拟机是业务习惯的使用模式,业务更容易接受。容器是以服务为核心,服务存在即存在,服务关闭即销毁,不再是虚拟机以机器为核心的模式。思虑再三,最终决定以容器的模式来定位,全新的平台提供全新的服务模式与体验。

虽然是以容器为核心,但容器中可运行单进程也可运行多进程。我们规范容器中只运行一个业务进程,防止由于运行多进程而导致业务之间相互干扰情况的发生,这类问题很难进行排查与定位。

去Agent化:在传统物理机模式下,一台物理机上可能会有多个Agent:运维管控Agent、监控Agent、业务自定义Agent等。云化后面临一个现实的问题:我们需要在每个容器中都安装这些Agent么?如果每个容器中都集成这些Agent,对云平台来说是简单的,这没有任何工作量。但是一台物理机上可能会运行上百个容器,这些Agent数量也会大量膨胀,带来系统负载的增加的问题。基于此,云平台投入大量的精力来实现容器的无Agent化,即在所有功能正常运行的前提下,Agent只运行在宿主上,不会运行在容器中。

业务云化过程中云团队遇到了很多问题案例,并形成自己独特的解决方案。这里选择了几个有代表性的案例,与大家进行分享。
12.png

服务启动耗CPU过高是云化早期时遇到的棘手问题。这个问题产生的原因很多:Java的语言特性导致JVM是在运行中逐步进行优化的;内部的很多开发框架都是在流量过来时才初始化链接;某些业务资源也是在流量过来时才进行初始化。当流量分发过来时,多种资源同时初始化导致服务需要大量的CPU资源,超过了平台为其分配的CPU配额,服务出现大量超时与抛弃。这一问题的简单解法是多分配CPU资源,但从长远角度来看这会导致资源利用无法有效把控,也会影响弹性调度的效果。最终,我们从两个维度解决这一问题:在调用方增加预热策略,流量逐步分发过来,CPU资源使用更平滑;在服务方增加预热方法,默认初始化链接等资源,同时引导用户进行自定义的初始化。

在容器监控维度方面,由于默认的监控数据是基于随机采样的分钟级数据,导致当服务出现短期秒级的CPU波动时,监控系统无法捕获,从而影响问题的排查与定位。针对这一问题,云平台对监控维度进行了深化,增加了容器级别秒级监控,实时采集每分钟的最大值上报,便于问题的排查与跟踪。
13.png

由于Cgroups只对CPU和内存资源进行了隔离与限速,并没有对系统负载进行隔离,如果容器中运行进程或线程数过多,会导致宿主机整体的负载波动,进而对上面的所有服务都会造成影响。

云平台增加了两个维度的控制策略:容器级别的最大线程数控制,防止由于单个容器线程数过多会对宿主造成影响。宿主级别的过载保护,当宿主上的负载过高时,过载保护策略会自动触发宿主上的容器进行漂移,直至负载降至合理的范围。

云平台必须关闭swap交换分区,这是一个深刻的经验教训。在云化的早期,云平台的交换分区没有关闭,部分服务经常出现由于使用交换分区而导致的耗时随机抖动,这类问题很难排查,耗费了大量的精力。

58云计算平台核心软件版本变迁过程如下:
14.png

#后记

容器云在58集团的实践与探索过程中,云团队做了很多技术选型与优化工作,也进行了很多技术方案的架构演进工作,究其根本原因是58集团是发展了十多年的互联网公司,它有自己独特的业务场景和成熟的开发体系与基础组件,容器云在集团内部起步较晚,内部系统很难为云平台做适配,只能云平台通过不断演进来适配不停发展的业务场景。这也是在较有规模的互联网公司做基础架构与创业公司的区别和挑战所在。所幸在团队的共同努力下,我们用1年多的时间完成了集团流量服务的云化工作。目前,集团内部还有很多服务需要云化,未来的挑战更大。

原文链接:https://mp.weixin.qq.com/s/gJM5-DByvMH54QhVsJ5HEA

介绍一个小工具:Kubedog

尼古拉斯 发表了文章 • 0 个评论 • 178 次浏览 • 2019-06-04 10:46 • 来自相关话题

Kubedog 是一个开源的 Golang 项目,使用 watch 方式对 Kubernetes 资源进行跟踪,能够方便的用于日常运维和 CI/CD 过程之中,项目中除了一个 CLI 小工具之外,还提供了一组 SDK,用户可以将其中的 Watch 功能集成到自 ...查看全部
Kubedog 是一个开源的 Golang 项目,使用 watch 方式对 Kubernetes 资源进行跟踪,能够方便的用于日常运维和 CI/CD 过程之中,项目中除了一个 CLI 小工具之外,还提供了一组 SDK,用户可以将其中的 Watch 功能集成到自己的系统之中。安装过程非常简单,在项目网页直接下载即可。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

Kubedog CLI 有两个功能:rollout track 和 follow。
#rollout track
在 Kubernetes 上运行应用时,通常的做法是使用 kubectl apply 提交 YAML 之后,使用 kubectl get -w 或者 watch kubectl get 之类的命令等待 Pod 启动。如果启动成功,则进行测试等后续动作;如果启动失败,就需要用 kubectl logs、kubectl describe 等命令来查看失败原因。kubedog 能在一定程度上简化这一过程。

例如使用 kubectl run 命令创建一个新的 Deployment 资源,并使用 kubedog 跟进创建进程:
$ kubectl run nginx --image=nginx22
...
deployment.apps/nginx created

$ kubedog rollout track deployment nginx
# deploy/nginx added
# deploy/nginx rs/nginx-6cc78cbf64 added
# deploy/nginx po/nginx-6cc78cbf64-8pnjz added
# deploy/nginx po/nginx-6cc78cbf64-8pnjz nginx error: ImagePullBackOff: Back-off pulling image "nginx22"
deploy/nginx po/nginx-6cc78cbf64-8pnjz nginx failed: ImagePullBackOff: Back-off pulling image "nginx22"

$ echo $?
130

很方便的看出,运行失败的状态及其原因,并且可以使用返回码来进行判断,方便在 Pipeline 中的运行。接下来可以使用 kubectl edit 命令编辑 Deployment,修改正确的镜像名称。然后再次进行验证:
$ kubectl edit deployment nginx
deployment.extensions/nginx edited
$ kubedog rollout track deployment nginx
# deploy/nginx added
# deploy/nginx rs/nginx-dbddb74b8 added
# deploy/nginx po/nginx-dbddb74b8-x4nkm added
# deploy/nginx event: po/nginx-dbddb74b8-x4nkm Pulled: Successfully pulled image "nginx"
# deploy/nginx event: po/nginx-dbddb74b8-x4nkm Created: Created container
# deploy/nginx event: po/nginx-dbddb74b8-x4nkm Started: Started container
# deploy/nginx event: ScalingReplicaSet: Scaled down replica set nginx-6cc78cbf64 to 0
# deploy/nginx become READY
$ echo $?
0

修改完成,重新运行 kubedog,会看到成功运行的情况,并且返回值也变成了 0。
#follow
follow 命令的功能和 kubetail 的功能有少量重叠,可以用 Deployment/Job/Daemonset 等为单位,查看其中所有 Pod 的日志,例如前面用的 Nginx,如果有访问的话,就会看到如下结果:
$ kubedog follow deployment nginx
# deploy/nginx appears to be ready
# deploy/nginx rs/nginx-6cc78cbf64 added
# deploy/nginx new rs/nginx-dbddb74b8 added
# deploy/nginx rs/nginx-dbddb74b8(new) po/nginx-dbddb74b8-x4nkm added
# deploy/nginx rs/nginx-6cc54845d9 added
# deploy/nginx event: ScalingReplicaSet: Scaled up replica set nginx-6cc54845d9 to 1
# deploy/nginx rs/nginx-6cc54845d9(new) po/nginx-6cc54845d9-nhlvs added
# deploy/nginx event: po/nginx-6cc54845d9-nhlvs Pulling: pulling image "nginx:alpine"
# deploy/nginx event: po/nginx-6cc54845d9-nhlvs Pulled: Successfully pulled image "nginx:alpine"
# deploy/nginx event: po/nginx-6cc54845d9-nhlvs Created: Created container
# deploy/nginx event: po/nginx-6cc54845d9-nhlvs Started: Started container
# deploy/nginx event: ScalingReplicaSet: Scaled down replica set nginx-dbddb74b8 to 0
# deploy/nginx become READY
# deploy/nginx event: po/nginx-dbddb74b8-x4nkm Killing: Killing container with id docker://nginx:Need to kill Pod
[quote]> deploy/nginx rs/nginx-dbddb74b8 po/nginx-dbddb74b8-x4nkm nginx[/quote]

[quote]> deploy/nginx rs/nginx-6cc54845d9(new) po/nginx-6cc54845d9-nhlvs nginx
127.0.0.1 - - [02/Jun/2019:11:35:08 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
127.0.0.1 - - [02/Jun/2019:11:35:11 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"

项目地址
https://github.com/flant/kubedog[/quote]

原文链接:https://blog.fleeto.us/post/intro-kubedog/

容器监控之kube-state-metrics

徐亚松 发表了文章 • 0 个评论 • 194 次浏览 • 2019-06-03 13:21 • 来自相关话题

概述 已经有了cadvisor、heapster、metric-server,几乎容器运行的所有指标都能拿到,但是下面这种情况却无能为力: * 我调度了多少个replicas?现在可用的有几个? ...查看全部
概述

已经有了cadvisor、heapster、metric-server,几乎容器运行的所有指标都能拿到,但是下面这种情况却无能为力:

* 我调度了多少个replicas?现在可用的有几个?
* 多少个Pod是running/stopped/terminated状态?
* Pod重启了多少次?
* 我有多少job在运行中

而这些则是kube-state-metrics提供的内容,它基于client-go开发,轮询Kubernetes API,并将Kubernetes的结构化信息转换为metrics。

功能

kube-state-metrics提供的指标,按照阶段分为三种类别:

  • 1.实验性质的:k8s api中alpha阶段的或者spec的字段。
  • 2.稳定版本的:k8s中不向后兼容的主要版本的更新
  • 3.被废弃的:已经不在维护的。
指标类别包括:* CronJob Metrics* DaemonSet Metrics* Deployment Metrics* Job Metrics* LimitRange Metrics* Node Metrics* PersistentVolume Metrics* PersistentVolumeClaim Metrics* Pod Metrics* Pod Disruption Budget Metrics* ReplicaSet Metrics* ReplicationController Metrics* ResourceQuota Metrics* Service Metrics* StatefulSet Metrics* Namespace Metrics* Horizontal Pod Autoscaler Metrics* Endpoint Metrics* Secret Metrics* ConfigMap Metrics以pod为例:* kube_pod_info* kube_pod_owner* kube_pod_status_phase* kube_pod_status_ready* kube_pod_status_scheduled* kube_pod_container_status_waiting* kube_pod_container_status_terminated_reason* ... 使用部署清单
 kube-state-metrics/    ├── kube-state-metrics-cluster-role-binding.yaml    ├── kube-state-metrics-cluster-role.yaml    ├── kube-state-metrics-deployment.yaml    ├── kube-state-metrics-role-binding.yaml    ├── kube-state-metrics-role.yaml    ├── kube-state-metrics-service-account.yaml    ├── kube-state-metrics-service.yaml
主要镜像有:image: quay.io/coreos/kube-state-metrics:v1.5.0image: k8s.gcr.io/addon-resizer:1.8.3(参考metric-server文章,用于扩缩容)对于pod的资源限制,一般情况下:`200MiB memory0.1 cores`超过100节点的集群:`2MiB memory per node0.001 cores per node`kube-state-metrics做过一次性能优化,具体内容参考下文部署成功后,prometheus的target会出现如下标志
1.png
因为kube-state-metrics-service.yaml中有`prometheus.io/scrape: 'true'`标识,因此会将metric暴露给prometheus,而Prometheus会在kubernetes-service-endpoints这个job下自动发现kube-state-metrics,并开始拉取metrics,无需其他配置。如果你想和更多监控技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态。使用kube-state-metrics后的常用场景有:存在执行失败的Job: kube_job_status_failed{job="kubernetes-service-endpoints",k8s_app="kube-state-metrics"}==1
  • 集群节点状态错误: kube_node_status_condition{condition="Ready",status!="true"}==1
  • 集群中存在启动失败的Pod:kube_pod_status_phase{phase=~"Failed|Unknown"}==1
  • 最近30分钟内有Pod容器重启: changes(kube_pod_container_status_restarts[30m])>0
配合报警可以更好地监控集群的运行 与metric-server的对比* metric-server(或heapster)是从api-server中获取cpu、内存使用率这种监控指标,并把他们发送给存储后端,如influxdb或云厂商,他当前的核心作用是:为HPA等组件提供决策指标支持。
  • kube-state-metrics关注于获取k8s各种资源的最新状态,如deployment或者daemonset,之所以没有把kube-state-metrics纳入到metric-server的能力中,是因为他们的关注点本质上是不一样的。metric-server仅仅是获取、格式化现有数据,写入特定的存储,实质上是一个监控系统。而kube-state-metrics是将k8s的运行状况在内存中做了个快照,并且获取新的指标,但他没有能力导出这些指标
  • 换个角度讲,kube-state-metrics本身是metric-server的一种数据来源,虽然现在没有这么做。
  • 另外,像Prometheus这种监控系统,并不会去用metric-server中的数据,他都是自己做指标收集、集成的(Prometheus包含了metric-server的能力),但Prometheus可以监控metric-server本身组件的监控状态并适时报警,这里的监控就可以通过kube-state-metrics来实现,如metric-serverpod的运行状态。
深入解析kube-state-metrics本质上是不断轮询api-server,代码结构也很简单主要代码目录
.├── collectors│   ├── builder.go│   ├── collectors.go│   ├── configmap.go│   ......│   ├── testutils.go│   ├── testutils_test.go│   └── utils.go├── constant│   └── resource_unit.go├── metrics│   ├── metrics.go│   └── metrics_test.go├── metrics_store│   ├── metrics_store.go│   └── metrics_store_test.go├── options│   ├── collector.go│   ├── options.go│   ├── options_test.go│   ├── types.go│   └── types_test.go├── version│   └── version.go└── whiteblacklist    ├── whiteblacklist.go    └── whiteblacklist_test.go
所有类型:
var (	DefaultNamespaces = NamespaceList{metav1.NamespaceAll}	DefaultCollectors = CollectorSet{		"daemonsets":               struct{}{},		"deployments":              struct{}{},		"limitranges":              struct{}{},		"nodes":                    struct{}{},		"pods":                     struct{}{},		"poddisruptionbudgets":     struct{}{},		"replicasets":              struct{}{},		"replicationcontrollers":   struct{}{},		"resourcequotas":           struct{}{},		"services":                 struct{}{},		"jobs":                     struct{}{},		"cronjobs":                 struct{}{},		"statefulsets":             struct{}{},		"persistentvolumes":        struct{}{},		"persistentvolumeclaims":   struct{}{},		"namespaces":               struct{}{},		"horizontalpodautoscalers": struct{}{},		"endpoints":                struct{}{},		"secrets":                  struct{}{},		"configmaps":               struct{}{},	})
构建对应的收集器Family即一个类型的资源集合,如job下的kube_job_info、kube_job_created,都是一个FamilyGenerator实例
metrics.FamilyGenerator{			Name: "kube_job_info",			Type: metrics.MetricTypeGauge,			Help: "Information about job.",			GenerateFunc: wrapJobFunc(func(j *v1batch.Job) metrics.Family {				return metrics.Family{&metrics.Metric{					Name:  "kube_job_info",					Value: 1,				}}			}),		},
func (b [i]Builder) buildCronJobCollector() [/i]Collector {   // 过滤传入的白名单	filteredMetricFamilies := filterMetricFamilies(b.whiteBlackList, cronJobMetricFamilies)	composedMetricGenFuncs := composeMetricGenFuncs(filteredMetricFamilies)  // 将参数写到header中	familyHeaders := extractMetricFamilyHeaders(filteredMetricFamilies)  // NewMetricsStore实现了client-go的cache.Store接口,实现本地缓存。	store := metricsstore.NewMetricsStore(		familyHeaders,		composedMetricGenFuncs,	)  // 按namespace构建Reflector,监听变化	reflectorPerNamespace(b.ctx, b.kubeClient, &batchv1beta1.CronJob{}, store, b.namespaces, createCronJobListWatch)	return NewCollector(store)}
性能优化:kube-state-metrics在之前的版本中暴露出两个问题:
  • 1. /metrics接口响应慢(10-20s)
  • 2. 内存消耗太大,导致超出limit被杀掉
问题一的方案就是基于client-go的cache tool实现本地缓存,具体结构为:`var cache = map[uuid][]byte{}`问题二的的方案是:对于时间序列的字符串,是存在很多重复字符的(如namespace等前缀筛选),可以用指针或者结构化这些重复字符。 优化点和问题
  • 1.因为kube-state-metrics是监听资源的add、delete、update事件,那么在kube-state-metrics部署之前已经运行的资源,岂不是拿不到数据?kube-state-metric利用client-go可以初始化所有已经存在的资源对象,确保没有任何遗漏
  • 2.kube-state-metrics当前不会输出metadata信息(如help和description)
  • 3.缓存实现是基于golang的map,解决并发读问题当期是用了一个简单的互斥锁,应该可以解决问题,后续会考虑golang的sync.Map安全map。
  • 4.kube-state-metrics通过比较resource version来保证event的顺序
  • 5.kube-state-metrics并不保证包含所有资源

监控数据展示

基于kube-state-metrics的监控数据,可以组装一些常用的监控面板,如下面的grafana面板:
2.png

3.png

本文为容器监控实践系列文章,完整内容见:container-monitor-book

容器云未来:Kubernetes、Istio 和 Knative

博云BoCloud 发表了文章 • 0 个评论 • 186 次浏览 • 2019-06-03 10:38 • 来自相关话题

导读 目前以Kubernetes为基础构建的容器生态逐渐完善,这其中Kubernetes、Istio、Knative三个独立项目被越来越多的人提及,并且已经开始尝试大规模落地实践,它们恰好构成了容器云的未来拼图。今天与大家一起分享下,这三个项目究 ...查看全部
导读
目前以Kubernetes为基础构建的容器生态逐渐完善,这其中Kubernetes、Istio、Knative三个独立项目被越来越多的人提及,并且已经开始尝试大规模落地实践,它们恰好构成了容器云的未来拼图。今天与大家一起分享下,这三个项目究竟解决了什么问题,为什么它们能够一鸣惊人。


随着微服务理念不断深入人心,越来越多的企业把自己的应用逐步由单体转变成微服务架构,Container容器技术的出现恰恰加速了这个转移过程,因为它有效地解决了N多服务的快速部署问题。但是随着服务数目的增多,越来越多的企业希望能够把相关服务有效地“聚合”在一起,方便统一部署与管理。Kubenretes的出现恰恰解决了大规模微服务编排部署所带来的挑战,让整个行业意识到PaaS的落地可以成为现实。

当随着微服务体系下的服务数目越来越多,服务运维成为必然要解决的问题,于是Istio出现了,基于网络代理与控制相分离的实现策略,允许对服务控制策略进行有效合理的管控。如果你想和更多容器技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

到这里似乎到了很美好的阶段:

微服务:解决应用内聚、臃肿的问题。
Container:解决服务运行环境统一,和部署问题。
Kubernetes:解决大量微服务有效“聚合”部署问题。
Istio:解决服务上线面临的一系列治理问题。

这个阶段乍一看来,构建容器云似乎有了一个完整的链路和解决方式,一切都将变得那么“完美”。

现在让我们回过头来深入分析一下,微服务体系下的服务交互,目前是否存在问题。

首先,无论是http,还是rpc,本质上都是服务与服务的远程调用。开发应用程序中,无法做到服务与服务间的彼此透明。这样会导致一个问题:无论微服务业务拆分多么“精细”,本质上业务单元之间还是不能够独立运行和发展。同时在面向不同开发领域的衍生,无法选择最合适的实现方式。因此我们希望能够基于不同的“模板”+“配置”的方式能够把开发环境标准化处理,同时提供“事件”机制,将服务与服务交互的耦合度降到最低。

其次,服务线上运行的动态伸缩问题。当下kubernetes环境下的弹性伸缩,需要由客户搜集监测数据,并自主手动来实现,但是我们更希望服务线上能够更加自动化和智能化。

最后,服务标准化问题。我们希望服务内部的模型是标准的、能够快速复制和快速构建的;服务通信是标准的:协议标准,格式标准;运行环境是标准的:快速部署,快速迁移。

Knative的出现恰好解决远程直接调用,服务线上自动管理以及一些列标准化问题。

下面我们来看一下三者的关联:


微信图片_20190603102740.png



Kubernetes和Istio相信大家比较熟悉了,这里不做过多介绍,有需要的同学可以关注下我们之前发布的相关文章,这里我们重点来看一下Knative。

Knative是谷歌开源的serverless架构方案,旨在提供一套简单易用的serverless方案,把serverless标准化。目前参与的公司主要是Google、Pivotal、IBM、Red Hat,于2018年7月份对外发布,目前处于快速发展阶段。


Knative组成


Build
构建系统:把用户定义的应用构建成容器镜像,面向kubernetes的标准化构建,区别于Dockerfile镜像构建,重点解决kubernetes环境的构建标准化问题。

Serving
服务系统:利用Istio的部分功能,来配置应用路由,升级以及弹性伸缩。Serving中包括容器生命周期管理,容器外围对象(service,ingres)生成(恰到好处的把服务实例与访问统一在一起),监控应用请求,自动弹性负载,并且利用Virtual service和destination配置服务访问规则。只有这样才能保证服务呈现一致性以及服务运行自动化管理。

Eventing
事件系统:用于自动完成事件的绑定与触发。事件系统与直接调用最大的区别在于响应式设计,它允许运行服务本身不需要屏蔽了调用方与被调用方的关系。从而在业务层面能够实现业务的快速聚合,或许为后续业务编排创新提供事件。

现在我们换一个角度,聚焦应用服务生命周期:
**· Knative 解决应用模板+面向统一环境的标准化构建场景;
· Kubernetes作为基础设施,解决应用编排和运行环境场景;
· 加粗文字Isito作为通信基础设施层,保证应用服务运行可检测、可配置、可追踪问题。**

这三者贯穿应用服务生命周期全过程,容器云恰恰也是管理应用服务的控制平台,这就能够很好地解释,为什么Kubernetes,Istio,Knative在未来会成为构建容器云的三驾马车。

在Kubernetes上运行Kafka合适吗?

whole 发表了文章 • 0 个评论 • 369 次浏览 • 2019-06-02 11:42 • 来自相关话题

【编者的话】本文介绍了在Kubernetes运行Kafka的互补性和一些坑,以及相关方案选择。 # 介绍 Kubernetes设计的初衷是运行无状态工作负载。这些通常采用微服务架构的工作负载,是轻量级,可水平扩展,遵循十二要素应用程序, ...查看全部
【编者的话】本文介绍了在Kubernetes运行Kafka的互补性和一些坑,以及相关方案选择。
# 介绍
Kubernetes设计的初衷是运行无状态工作负载。这些通常采用微服务架构的工作负载,是轻量级,可水平扩展,遵循十二要素应用程序,可以处理环形断路和随机Monkey测试。

另一方面,Kafka本质上是一个分布式数据库。这意味着你必须处理状态,它比微服务更重量级。Kubernetes支持有状态的工作负载,但你必须谨慎对待它,正如Kelsey Hightower在最近的两条推文中指出的那样:
1.png

现在你应该在Kubernetes上运行Kafka吗?我的反问是:没有它,Kafka会跑得更好吗?这就是为什么我要指出Kafka和Kubernetes之间的相互补充性以及你可能遇到的陷阱。
# 运行时
让我们先看一下基本的东西——运行时本身。
## 进程
Kafka brokers对CPU很友好。TLS可能会引入一些开销。如果Kafka客户端使用加密,则需要更多CPU,但这不会影响brokers。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态
## 内存
Kafka brokers是内存消耗大户。JVM堆通常可以限制为4-5 GB,但由于Kafka大量使用页面缓存,因此还需要足够的系统内存。在Kubernetes中,可以相应地设置容器资源限制和请求。
## 存储
容器中的存储是短暂的——重启后数据将丢失。可以对Kafka数据使用emptyDir卷,这将产生相同的效果:brokers的数据将在停机后丢失。您的消息在其他broker上作为副本还是可以使用的。因此,重新启动后,失败的broker必须得复制所有的数据,这可能是一个耗时过程。

这就是你应该使用持久存储的原因。使用XFS或ext4的非本地持久性块存储更合适。我警告你:不要使用NFS。NFS v3和v4都不会起作用。简而言之,Kafka broker会因为NFS“愚蠢重命名”问题而无法删除数据目录,自行终止。如果你仍然不相信我,那么请仔细阅读这篇博文。存储必须是非本地的,以便Kubernetes在重新启动或重新定位时可以更灵活地选择另一个节点。
## 网络
与大多数分布式系统一样,Kafka性能在很大程度上取决于低网络延迟和高带宽。不要试图将所有代理放在同一节点上,因为这会降低可用性。如果Kubernetes节点出现故障,那么整个Kafka集群都会出现故障。不要跨数据中心扩展Kafka集群。这同样适用于Kubernetes集群。不同的可用区域是一个很好的权衡。
# 配置
## 清单
Kubernetes网站包含一个非常好的教程,介绍如何使用清单设置ZooKeeper。由于ZooKeeper是Kafka的一部分,因此可以通过这个了解哪些Kubernetes概念被应用在这里。一旦理解,您也可以对Kafka集群使用相同的概念。

  • Pod:Pod是Kubernetes中最小的可部署单元。它包含您的工作负载,它代表群集中的一个进程。一个Pod包含一个或多个容器。整体中的每个ZooKeeper服务器和Kafka集群中的每个Kafka broker都将在一个单独的Pod中运行。
  • StatefulSet:StatefulSet是一个Kubernetes对象,用于处理需要协调的多个有状态工作负载。StatefulSets保证Pod的有序性和唯一性的。
  • Headless Services:服务通过逻辑名称将Pod与客户端分离。Kubernetes负责负载平衡。但是,对于ZooKeeper和Kafka等有状态工作负载,客户端必须与特定实例进行通信。这就是 Headless Services发挥作用的地方:作为客户端,仍然可以获得逻辑名称,但不必直接访问Pod。
  • 持久卷:如上所述,需要配置非本地持久块存储。

Yolean提供了一套全面的清单,可以帮助您开始使用Kubernetes上的Kafka。

## Helm Charts
Helm是Kubernetes的包管理器,类似yum,apt,Homebrew或Chocolatey等OS包管理器。它允许您安装Helm Charts中描述的预定义软件包。精心设计的Helm Charts能简化所有参数正确配置的复杂任务,以便在Kubernetes上运行Kafka。有几张图表适用于Kafka的的可供选择:一个是处于演进状态的官方图表,一个来自Confluent,另一个来自Bitnami,仅举几例。
## Operators
由于Helm的一些限制,另一种工具变得非常流行:Kubernetes Operators。Operators不仅可以为Kubernetes打包软件,还可以为Kubernetes部署和管理一个软件。

评价很高的Operators名单中提到Kafka有两个,其中一个是Strimzi,Strimzi使得在几分钟内启动Kafka集群变得非常容易,几乎不需要任何配置,它增加了一些漂亮的功能,如群集间点对点TLS加密。Confluent还宣布即将推出新的Operator。
## 性能
运行性能测试以对Kafka安装进行基准测试非常重要。在您遇到麻烦之前,它会为您提供有关可能的瓶颈的地方。幸运的是,Kafka已经提供了两个性能测试工具:kafka-producer-perf-test.sh和kafka-consumer-perf-test.sh。记得经常使用它们。作为参考,可以使用Jay Kreps博客结果,或者 Stéphane Maarek在 Amazon MSK的评论
# 运维
## 监控
可见性非常重要,否则您将不知道发生了什么。如今,有一种不错的工具可以用云原生方式监控指标。Prometheus和Grafana是两种流行的工具。Prometheus可以直接从JMX导出器收集所有Java进程(Kafka,ZooKeeper,Kafka Connect)的指标。添加cAdvisor指标可为提供有关Kubernetes资源使用情况的其他信息。

Strimzi为Kafka提供了一个优雅的Grafana仪表板示例。它以非常直观的方式可视化关键指标,如未复制的和离线分区。它通过资源使用和性能以及稳定性指标来补充这些指标。因此,可以免费获得基本的Kafka集群监控!
2.png

资料来源:https://strimzi.io/docs/master/#kafka_dashboard

可以通过客户端监控(消费者和生产者指标),使用Burrow滞后监控,使用Kafka Monitor进行端到端监控,来完成这个任务
## 日志记录
日志记录是另一个关键部分。确保Kafka安装中的所有容器都记录到标准输出(stdout)和标准错误输出(stderr),并确保Kubernetes集群将所有日志聚合到中央日志记录设施中如Elasticsearch中。
## 健康检查
Kubernetes使用活跃度和就绪探测器来确定Pod是否健康。如果活跃度探测失败,Kubernetes将终止容器并在相应设置重启策略时自动重启。如果准备就绪探测失败,那么Kubernetes将通过服务从服务请求中删除该Pod。这意味着在这种情况下不再需要人工干预,这是一大优点。
## 滚动更新
StatefulSets支持自动更新:滚动更新策略将一次更新一个Kafka Pod。通过这种方式,可以实现零停机时间,这是Kubernetes带来的另一大优势。
## 扩展
扩展Kafka集群并非易事。但是,Kubernetes可以很容易地将Pod缩放到一定数量的副本,这意味着可以声明式地定义所需数量的Kafka brokers。困难的部分是在放大或缩小之前重新分配部分。同样,Kubernetes可以帮助您完成这项任务。
## 管理
通过在Pod中打开shell,可以使用现有的shell脚本完成Kafka群集的管理任务,例如创建主题和重新分配分区。这不是一个很好的解决方案。Strimzi支持与另一个Operator管理主题。这还有改进的余地。
## 备份和还原
现在Kafka的可用性还取决于Kubernetes的可用性。如果Kubernetes群集出现故障,那么在最坏的情况下Kafka群集也会故障。墨菲定律告诉我们,这也会发生在你身上,你会丢失数据。要降低此风险,请确保您具有备份想法。MirrorMaker是一种可选方案,另一种可能是利用S3进行连接备份,如Zalando的博客文章所述。
# 结论
对于中小型Kafka集群,我肯定会选择Kubernetes,因为它提供了更大的灵活性并简化了操作。如果您在延迟和/或吞吐量方面具有非常高的非功能性要求,则不同的部署选项可能更有益。

原文链接:Kafka on Kubernetes — a good fit?(译者:姜俊厚)
条新动态, 点击查看
杨章显

杨章显 回答了问题 • 2015-03-30 11:37 • 7 个回复 不感兴趣

Kubernetes的service该如何访问?

赞同来自:

master配置的portalnet包含的IP都是本地有效的,对外没有路由,肯定ping不通的。如果你需要对外访问服务,可以在Service里设置publicIP,然后通过publicIP:servicePort访问服务。
master配置的portalnet包含的IP都是本地有效的,对外没有路由,肯定ping不通的。如果你需要对外访问服务,可以在Service里设置publicIP,然后通过publicIP:servicePort访问服务。
杨章显

杨章显 回答了问题 • 2015-06-05 15:24 • 6 个回复 不感兴趣

关于Kubernetes的一些困惑

赞同来自:

hi , &amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;比如minionA/B上分别有serviceA的podA/B。那么访问minionA的kube-proxy是不是也会被导流到minionB上的po... 显示全部 »
hi , &amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;比如minionA/B上分别有serviceA的podA/B。那么访问minionA的kube-proxy是不是也会被导流到minionB上的podB上?&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt;&amp;lt;strong&amp;gt;由于通过Service IP + Service Port访问Service,Service不关心Pod运行在哪个节点上,所以Proxy可能分发到minionB。&amp;lt;/strong&amp;gt; &amp;lt;ol&amp;gt;&amp;lt;li&amp;gt;service的重要部件proxy就是起到了个负载均衡和反向代理的作用。这个角色是不是直接可以用haproxy/nginx这样的软件代替呢?&amp;lt;/li&amp;gt;&amp;lt;/ol&amp;gt; &amp;lt;strong&amp;gt;直接替代应该不行,proxy除了代理,还做了一些其他工作,如初始化iptables,service注册到proxy,更新service的podlist更新等,所以如果要替换,这部分工作得自己实现。&amp;lt;/strong&amp;gt;

Shell-operator:用于简化Kubernetes operator的创建

dummy 发表了文章 • 0 个评论 • 139 次浏览 • 2019-06-18 17:24 • 来自相关话题

我们很高兴在此介绍我们新的开源方案,它能将Kubernetes中Operator的开发提升到一个全新的更简单的水平。它可以让你在15分钟内将你的小脚本变成完全成熟的Operator而吸引你。欢迎 ...查看全部

我们很高兴在此介绍我们新的开源方案,它能将Kubernetes中Operator的开发提升到一个全新的更简单的水平。它可以让你在15分钟内将你的小脚本变成完全成熟的Operator而吸引你。欢迎shell-operator




目标


shell-operator的思路很简单,它订阅来自Kubernetes对象的事件,并在事件发生后执行外部程序,为其提供有关事件的信息:




在我们运行Kubernetes集群期间,很多小任务开始显现。在Flant,我们迫切想要以正确的方式来自动化它们,因此我们觉得需要更智能的解决方案。通常你可以使用基本的bash脚本来解决所有这些任务,但是如你所知,更推荐的方式是使用Golang来编写Operators。很显然,为每个小任务分别开发成熟的Operator将会很低效。


15分钟内创建一个Operator


我们将举一个在Kubernetes集群中可以被自动化的例子,以及shell-operator可以如何帮助我们。我们将尝试复制用于访问Docker仓库的凭证。


使用私有仓库镜像的Pod应在其清单中包含指定的用于访问仓库的secret。这个secret必须在创建Pod之前先创建在每个命名空间中。你可以手动执行此操作,但是,如果我们将配置动态的多个环境,我们将为单个应用程序创建许多命名空间。在多个应用程序(甚至两个或三个)的情况下,secret的数量会变得巨大。关于secret还有一个需求:我们希望能够偶尔更改(注册表)仓库的访问密钥。因此,手动解决方案变得非常低效,你必须自动创建和更新secret。


简单的自动化


我们来写一个脚本,每N秒运行一次,并检查命名空间中secret是否存在。如果secret不存在,那么它将会被创建。这个解决方案的优势是它看起来就像是cron中的一个shell脚本,一种经典且易于理解的方法。缺点是在此脚本的两次启动之间的间隔期间可能会出现一些新的命名空间,因此在一段时间内它将不会持有这个secret。这种情况会导致启动Pod的过程中出错。


使用shell-operator进行自动化


为了使我们的脚本准确运行,经典的cron执行应该被当有新增命名空间事件发生时的执行所取代。在这种情况下,你可以在使用之前创建一个secret。让我们看看如何使用shell-operator来实现这个功能。


首先,我们先分析一下脚本,就shell-operator而言,脚本都被称之为“钩子“。每个钩子在使用--config标志执行时都会通知shell-operator将其绑定(即需要执行哪些事件)。在我们的例子中,我们将使用onKubernetesEvent


#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <

在这里,我们定义我们关注的namespace类型的添加(add)对象事件。


现在我们需要添加当事件发生时需要执行的代码:


#!/bin/bash
if [[ $1 == "--config" ]] ; then
# configuration
cat <

真棒!我们现在已有一个简洁且漂亮的脚本,想让它能真正发挥作用,我们需要准备一个镜像并将其跑在集群中。


使用钩子制作我们的镜像


你可以很轻易观察到我们在脚本里面使用了kubectljq命令。这意味着镜像中需要包含钩子,shell-operator二进制文件(它将监视事件并执行这个钩子),以及钩子需要用到的命令(kubectljq)。hub.docker.com上已提供了包含shell-operator,kubectl和jq的即用型镜像。现在是时候使用Dockerfile来添加一个钩子:


$ cat Dockerfile
FROM flant/shell-operator:v1.0.0-beta.1-alpine3.9
ADD namespace-hook.sh /hooks
$ docker build -t registry.example.com/my-operator:v1 .
$ docker push registry.example.com/my-operator:v1

在集群中运行


我们再来看看这个钩子,这次我们将关注具体的操作以及它在集群中执行的对象:



  1. 它订阅了namespace的创建事件;

  2. 它在不与它所运行的命名空间相同的空间创建一个secret。


这里我们会发现运行这个镜像的Pod需要有执行这些操作的权限。你可以授权给一个ServiceAccount。由于我们是关注整个集群中的对象,那么权限需要使用ClusterRoleClusterRoleBinding形式来配置。


YAML最终配置描述如下:


---
apiVersion: v1
kind: ServiceAccount
metadata:
name: monitor-namespaces-acc
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: monitor-namespaces
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "watch", "list"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "create", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: monitor-namespaces
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: monitor-namespaces
subjects:
- kind: ServiceAccount
name: monitor-namespaces-acc
namespace: example-monitor-namespaces

你可以将创建的镜像部署为一个简单的Deployment:


apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-operator
spec:
template:
spec:
containers:
- name: my-operator
image: registry.example.com/my-operator:v1
serviceAccountName: monitor-namespaces-acc

为方便起见,我们将创建一个单独的命名空间,用于运行shell-operator并应用创建的部署清单:


$ kubectl create ns example-monitor-namespaces
$ kubectl -n example-monitor-namespaces apply -f rbac.yaml
$ kubectl -n example-monitor-namespaces apply -f deployment.yaml

好了,shell-operator启动,它将订阅命名空间创建事件并在需要时执行钩子。



这样一个简单的shell脚本就变成了Kubernetes中一个真正的Operator,并成为集群的一部分。这样做的好处是我们避免了使用Golang来开发Operator的复杂过程:




过滤


关于对象的观察很棒,但我们通常需要响应对象中某些属性的更改,例如,增加/减少部署中的副本数量或对象对标签中的任何更新。


当一个事件发生时,shell-operator接收该对象的JSON清单。在此JSON中,你可以选择要监视的属性,并仅在更改时启动钩子。jqFilter字段可以帮助你完成这点:你应该输入将应用于JSON清单的jq表达式。


举个例子,要响应Deployment对象标签中的修改,你必须从metadata字段中提取labels字段。这个例子中你将需要如下的配置:


cat <

jqFilter表达式将Deployment的长长的JSON清单转换成带有标签的简短的JSON:




shell-operator将只会在这个简短的JSON发生变化时执行钩子。其它属性的变更将会被忽略。


钩子的执行上下文


钩子的配置允许你指定几种事件。例如你可以定义两个Kubernetes事件和两个计划调度:


{
"onKubernetesEvent": [
{
"name": "OnCreatePod",
"kind": "pod",
"event": [
"add"
]
},
{
"name": "OnModifiedNamespace",
"kind": "namespace",
"event": [
"update"
],
"jqFilter": ".metadata.labels"
}
],
"schedule": [
{
"name": "every 10 min",
"crontab": "0 */10 * * * *"
},
{
"name": "on Mondays at 12:10",
"crontab": "0 10 12 * * 1"
}
]
}

注意:shell-operator支持以crontab样式运行脚本!你可以在文档中找到额外的信息。


为了区分钩子执行的原因,shell-operator会创建一个临时文件并将其路径保存到BINDING_CONTEXT_TYPE变量中。此文件包含了执行钩子的原因的JSON描述。例如,每隔10分钟将会使用以下内容启动钩子:


[{ "binding": "every 10 min" }]

在周一的话它将以以下内容启动:


[{ "binding": "every 10 min" }, { "binding": "on Mondays at 12:10" }]

同时将有onKubernetesEvent调用的更详细的JSON,因为它包含了对象的描述:


[
{
"binding": "onCreatePod",
"resourceEvent": "add",
"resourceKind": "pod",
"resourceName": "foo",
"resourceNamespace": "bar"
}
]

你能通过名称来全面了解字段的内容(更多详细信息可在文档中找到)。使用jq从resourceName获取资源名称的示例已经在复制secret的钩子中展示:


jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH

你可以通过类似的方式去获取到其它字段。


下一步是什么呢?


在该项目仓库中的/examples directory目录里包含了一些可以直接在集群中使用的示例。你可以将它们用作你开发自己的钩子的基础。


Shell-operator同样支持使用Prometheus来收集指标。METRICS章节已描述了这些可用的指标。


你能轻易想到,shell-operator是使用Go编写的,并根据开源许可证(Apache 2.0)的条款进行分发。我们非常感谢任何关于开发在Github上的这个项目的帮助。你可以通过给我们点Star,反馈问题或者是PR来支持我们!


原文链接:Announcing shell-operator to simplify creating of Kubernetes operators(翻译:冯旭松)


DockOne微信分享(二一一):基于Actor模型的CQRS/ES解决方案分享

JetLee 发表了文章 • 0 个评论 • 442 次浏览 • 2019-06-05 16:17 • 来自相关话题

【编者的话】2017年加密货币比较流行,我曾有幸在加密货币交易所参与开发工作,为了应对交易系统高性能、高并发、高可用的要求,我们使用基于Actor模型的Orleans技术,采用CQRS/ES架构结合Service Fabric开展了富有挑战性的工作。本文 ...查看全部

【编者的话】2017年加密货币比较流行,我曾有幸在加密货币交易所参与开发工作,为了应对交易系统高性能、高并发、高可用的要求,我们使用基于Actor模型的Orleans技术,采用CQRS/ES架构结合Service Fabric开展了富有挑战性的工作。本文将从不同视角为大家介绍Actor模型、CQRS/ES架构以及Service Fabric在高并发场景中的考量和应用。



话题由三部分组成:




  • Actor模型&Orleans:在编程的层面,从细粒度-由下向上的角度介绍Actor模型;

  • CQRS/ES:在框架的层面,从粗粒度-由上向下的角度介绍Actor模型,说明Orleans技术在架构方面的价值;

  • Service Fabric:从架构部署的角度将上述方案落地上线。



群里的小伙伴技术栈可能多是Java和Go体系,分享的话题主要是C#技术栈,没有语言纷争,彼此相互学习。比如:Scala中,Actor模型框架有akka,CQRS/ES模式与编程语言无关,Service Fabric与Kubernetes是同类平台,可以相互替代,我自己也在学习Kubernetes。





Actor模型&Orleans(细粒度)





共享内存模型



多核处理器出现后,大家常用的并发编程模型是共享内存模型。


这种编程模型的使用带来了许多痛点,比如:




  • 编程:多线程、锁、并发集合、异步、设计模式(队列、约定顺序、权重)、编译

  • 无力:单系统的无力性:①地理分布型、②容错型

  • 性能:锁,性能会降低

  • 测试:



    • 从坑里爬出来不难,难的是我们不知道自己是不是在坑里(开发调试的时候没有热点可能是正常的)

    • 遇到bug难以重现。有些问题特别是系统规模大了,可能运行几个月才能重现问题

  • 维护:



    • 我们要保证所有对象的同步都是正确的、顺序的获取多个锁。

    • 12个月后换了另外10个程序员仍然按照这个规则维护代码。



简单总结:




  • 并发问题确实存在

  • 共享内存模型正确使用掌握的知识量多

  • 加锁效率就低

  • 存在许多不确定性





Actor模型



Actor模型是一个概念模型,用于处理并发计算。Actor由3部分组成:状态(State)+行为(Behavior)+邮箱(Mailbox),State是指Actor对象的变量信息,存在于Actor之中,Actor之间不共享内存数据,Actor只会在接收到消息后,调用自己的方法改变自己的state,从而避免并发条件下的死锁等问题;Behavior是指Actor的计算行为逻辑;邮箱建立Actor之间的联系,一个Actor发送消息后,接收消息的Actor将消息放入邮箱中等待处理,邮箱内部通过队列实现,消息传递通过异步方式进行。


Actor是分布式存在的内存状态及单线程计算单元,一个Id对应的Actor只会在集群种存在一个(有状态的 Actor在集群中一个Id只会存在一个实例,无状态的可配置为根据流量存在多个),使用者只需要通过Id就能随时访问不需要关注该Actor在集群的什么位置。单线程计算单元保证了消息的顺序到达,不存在Actor内部状态竞用问题。



Actor是分布式存在的内存状态及单线程计算单元,一个ID对应的Actor只会在集群种存在一个(有状态的 Actor在集群中一个ID只会存在一个实例,无状态的可配置为根据流量存在多个),使用者只需要通过ID就能随时访问不需要关注该Actor在集群的什么位置。单线程计算单元保证了消息的顺序到达,不存在Actor内部状态竞用问题。



举个例子:



多个玩家合作在打Boss,每个玩家都是一个单独的线程,但是Boss的血量需要在多个玩家之间同步。同时这个Boss在多个服务器中都存在,因此每个服务器都有多个玩家会同时打这个服务器里面的Boss。



如果多线程并发请求,默认情况下它只会并发处理。这种情况下可能造成数据冲突。但是Actor是单线程模型,意味着即使多线程来通过Actor ID调用同一个Actor,任何函数调用都是只允许一个线程进行操作。并且同时只能有一个线程在使用一个Actor实例。





Actor模型:Orleans



Actor模型这么好,怎么实现?



可以通过特定的Actor工具或直接使用编程语言实现Actor模型,Erlang语言含有Actor元素,Scala可以通过Akka框架实现Actor编程。C#语言中有两类比较流行,Akka.NET框架和Orleans框架。这次分享内容使用了Orleans框架。



特点:



Erlang和Akka的Actor平台仍然使开发人员负担许多分布式系统的复杂性:关键的挑战是开发管理Actor生命周期的代码,处理分布式竞争、处理故障和恢复Actor以及分布式资源管理等等都很复杂。Orleans简化了许多复杂性。



优点:




  • 降低开发、测试、维护的难度

  • 特殊场景下锁依旧会用到,但频率大大降低,业务代码里甚至不会用到锁

  • 关注并发时,只需要关注多个actor之间的消息流

  • 方便测试

  • 容错

  • 分布式内存



缺点:




  • 也会出现死锁(调用顺序原因)

  • 多个Actor不共享状态,通过消息传递,每次调用都是一次网络请求,不太适合实施细粒度的并行

  • 编程思维需要转变






第一小节总结:上面内容由下往上,从代码层面细粒度层面表达了采用Actor模型的好处或原因。





CQRS/ES(架构层面)





从1000万用户并发修改用户资料的假设场景开始







  1. 每次修改操作耗时200ms,每秒5个操作

  2. MySQL连接数在5K,分10个库

  3. 5 *5k *10=25万TPS

  4. 1000万/25万=40s

 

在秒杀场景中,由于对乐观锁/悲观锁的使用,推测系统响应时间更复杂。





使用Actor解决高并发的性能问题





1000万用户,一个用户一个Actor,1000万个内存对象。


200万件SKU,一件SKU一个Actor,200万个内存对象。




  • 平均一个SKU承担1000万/200万=5个请求

  • 1000万对数据库的读写压力变成了200万

  • 1000万的读写是同步的,200万的数据库压力是异步的

  • 异步落盘时可以采用批量操作



总结:



由于1000万+用户的请求根据购物意愿分散到200万个商品SKU上:每个内存领域对象都强制串行执行用户请求,避免了竞争争抢;内存领域对象上扣库存操作处理时间极快,基本没可能出现请求阻塞情况。



从架构层面彻底解决高并发争抢的性能问题。理论模型,TPS>100万+……





EventSourcing:内存对象高可用保障



Actor是分布式存在的内存状态及单线程计算单元,采用EventSourcing只记录状态变化引发的事件,事件落盘时只有Add操作,上述设计中很依赖Actor中State,事件溯源提高性能的同时,可以用来保证内存数据的高可用。






CQRS



上面1000万并发场景的内容来自网友分享的PPT,与我们实际项目思路一致,就拿来与大家分享这个过程,下图是我们交易所项目中的架构图:


开源版本架构图:


开源项目github:https://github.com/RayTale/Ray



第二小节总结:由上往下,架构层面粗粒度层面表达了采用Actor模型的好处或原因。





Service Fabric



系统开发完成后Actor要组成集群,系统在集群中部署,实现高性能、高可用、可伸缩的要求。部署阶段可以选择Service Fabric或者Kubernetes,目的是降低分布式系统部署、管理的难度,同时满足弹性伸缩。



交易所项目可以采用Service Fabric部署,也可以采用Kubernetes,当时Kubernetes还没这么流行,我们采用了Service Fabric,Service Fabric 是一款微软开源的分布式系统平台,可方便用户轻松打包、部署和管理可缩放的可靠微服务和容器。开发人员和管理员不需解决复杂的基础结构问题,只需专注于实现苛刻的任务关键型工作负荷,即那些可缩放、可靠且易于管理的工作负荷。支持Windows与Linux部署,Windows上的部署文档齐全,但在Linux上官方资料没有。现在推荐Kubernetes。



第三小节总结:




  1. 借助Service Fabric或Kubernetes实现低成本运维、构建集群的目的。

  2. 建立分布式系统的两种最佳实践:



    • 进程级别:容器+运维工具(Kubernetes/Service Fabric)

    • 线程级别:Actor+运维工具(Kubernetes/Service Fabric)

参考:




  1. ES/CQRS部分内容参考:《领域模型 + 内存计算 + 微服务的协奏曲:乾坤(演讲稿)》2017年互联网应用架构实战峰会

  2. 其他细节来自互联网,不一一列出





Q&A



Q:单点故障后,正在处理的cache数据如何处理的,例如,http、tcp请求……毕竟涉及到钱?

A:Actor有激活和失活的生命周期,激活的时候使用快照和Events来恢复最新内存状态,失活的时候保存快照。Actor框架保证系统中同一个key只会存在同一个Actor,当单点故障后,Actor会在其它节点重建并恢复最新状态。



Q:数据落地得策略是什么?还是说就是直接落地?

A:event数据直接落地;用于支持查询的数据,是Handler消费event后异步落库。



Q:Grain Persistence使用Relational Storage容量和速度会不会是瓶颈?

A:Grain Persistence存的是Grain的快照和event,event是只增的,速度没有出现瓶颈,而且开源版本测试中PostgreSQL性能优于MongoDB,在存储中针对这两个方面做了优化:比如分表、归档处理、快照处理、批量处理。



Q:Orleans中,持久化事件时,是否有支持并发冲突的检测,是如何实现的?

A:Orleans不支持;工作中,在事件持久化时做了这方面的工作,方式是根据版本号。



以上内容根据2019年6月4日晚微信群分享内容整理。分享人郑承良,上海某科技公司架构师,对高并发场景下的分布式金融系统拥有丰富的实战经验,曾为澳大利亚、迪拜多家交易所提供技术支持。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiese,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

Kubernetes IN Docker - local clusters for testing Kubernetes

老马 发表了文章 • 0 个评论 • 289 次浏览 • 2019-06-04 21:52 • 来自相关话题

Brief Kind(Kubernetes IN Docker是一个用来快速创建 ...查看全部

Brief



Kind(Kubernetes IN Docker是一个用来快速创建和测试kubernetes的工具,Kind把环境的依赖降低到了最小,仅需要机器安装了Docker即可。



Kind 可以做什么?




  • 快速创建一个或多个Kubernetes集群(几分钟)

  • 支持HA Master部署高可用的Kubernetes集群

  • 支持从源码构建并部署一个Kubernetes集群

  • 可以快速低成本体验一个最新的Kubernetes集群,并支持Kubernetes的绝大部分功能

  • 支持本地离线运行一个多节点集群



Kind 有哪些优势?




  • 最小的安装依赖,仅需要安装Docker即可

  • 使用快速简单,使用kind cli工具即可快速创建集群

  • 使用container来 mockkubernetes node

  • 内部使用kubeadm的官方主流部署工具

  • 使用了containerd

  • 通过了CNCF官方的k8s conformance测试





Usage





GO111MODULE="on" go get sigs.k8s.io/kind@v0.3.0 && kind create cluster




How it work



Kind 使用一个 container 来模拟一个 Node,在 Container 里面跑了 systemd ,并用 systemd 托管了 kubelet 以及 containerd,然后容器内部的 kubelet 把其他 Kubernetes 组件,比如 kube-apiserver、etcd、CNI 等组件跑起来。



可以通过配置文件的方式,来通过创建多个 container 的方式,来模拟创建多个 Node,并以这些 Node 来构建一个多节点的 Kubernetes 集群。



Kind 内部使用了 kubeadm 这个工具来做集群的部署,包括 ha master 的高可用集群,也是借助 kubeadm 提供的aplha特性提供的。同时,在 HA Master 下,额外部署了一个 Nginx 用来提供负载均衡 vip。





Build Images



Kind 的镜像分为两个,一个 Node 镜像,一个 Base 镜像。



Node 镜像



Node 镜像的构建比较复杂,目前是通过运行 Base 镜像,并在 Base 镜像内执行操作,再保存此容器内容为镜像的方式来构建的,包含的操作有:




  • 构建 Kubernetes 相关资源(比如二进制文件和镜像)

  • 运行一个用于构建的容器

  • 把构建的 Kubernetes 相关资源复制到容器里

  • 调整部分组件配置参数,以支持在容器内运行

  • 预先拉去运行环境需要的镜像

  • 通过 docker commit 方式保存当前的构建容器为 node 镜像



具体的逻辑,可以参考node.go



Base 镜像



Base 镜像目前使用了 Ubuntu 19.04 作为基础镜像,做了下面的调整:




  • 安装 systemd 相关的包,并调整一些配置以适应在容器内运行

  • 安装 Kubernetes 运行时的依赖包,比如 conntrack、socat、CNI

  • 安装容器



运行环境,比如 containerd、crictl




  • 配置自己的 ENTRYPOINT 脚本,以适应和调整容器内运行的问题



具体的逻辑,可以参考构建的Dockerfile





Create Cluster



Kind 创建集群的基本过程为:




  1. 根据传入的参数,来创建 container,分为 control node 和 worker node 两种(如果是 ha master,还有一个 loadbalancer node)

  2. 如果需要,配置 loadbalancer 的配置,主要是 Nginx 配置文件

  3. 生成 kubeadm 配置

  4. 对于第一个控制节点,使用 kubeadm init 初始化单节点集群

  5. 配置安装 CNI 插件

  6. 配置存储(实际是安装了一个使用 hostpath 的 storageclass)

  7. 其他的控制节点,通过kubeadm join --experimental-control-plane的方式来扩容控制节点

  8. 通过 kubeadm join 扩容其他的工作节点

  9. 等待集群创建完成

  10. 生成访问配置,打印使用帮助具体的创建流程,可以参考代码create.go



这里关于每个容器,是如何作为 Node 跑起来的,可以简单讲解些原理:



根据不同的角色,调用不同的函数创建节点nodes.go





// TODO(bentheelder): remove network in favor of []cri.PortMapping when that is in
func (d *nodeSpec) Create(clusterLabel string) (node *nodes.Node, err error) {
// create the node into a container (docker run, but it is paused, see createNode)
// TODO(bentheelder): decouple from config objects further
switch d.Role {
case constants.ExternalLoadBalancerNodeRoleValue:
node, err = nodes.CreateExternalLoadBalancerNode(d.Name, d.Image, clusterLabel, d.APIServerAddress, d.APIServerPort)
case constants.ControlPlaneNodeRoleValue:
node, err = nodes.CreateControlPlaneNode(d.Name, d.Image, clusterLabel, d.APIServerAddress, d.APIServerPort, d.ExtraMounts)
case constants.WorkerNodeRoleValue:
node, err = nodes.CreateWorkerNode(d.Name, d.Image, clusterLabel, d.ExtraMounts)
default:
return nil, errors.Errorf("unknown node role: %s", d.Role)
}
return node, err
}


节点(容器)创建时,通过配置 –privileged,挂载 tmpfs,修改主机名等,来运行节点create



func createNode(name, image, clusterLabel, role string, mounts []cri.Mount, extraArgs ...string) (handle *Node, err error) {
runArgs := []string{
"-d", // run the container detached
"-t", // allocate a tty for entrypoint logs
// running containers in a container requires privileged
// NOTE: we could try to replicate this with --cap-add, and use less
// privileges, but this flag also changes some mounts that are necessary
// including some ones docker would otherwise do by default.
// for now this is what we want. in the future we may revisit this.
"--privileged",
"--security-opt", "seccomp=unconfined", // also ignore seccomp
"--tmpfs", "/tmp", // various things depend on working /tmp
"--tmpfs", "/run", // systemd wants a writable /run
// some k8s things want /lib/modules
"-v", "/lib/modules:/lib/modules:ro",
"--hostname", name, // make hostname match container name
"--name", name, // ... and set the container name
// label the node with the cluster ID
"--label", clusterLabel,
// label the node with the role ID
"--label", fmt.Sprintf("%s=%s", constants.NodeRoleKey, role),
}

// pass proxy environment variables to be used by node's docker deamon
proxyDetails := getProxyDetails()
for key, val := range proxyDetails.Envs {
runArgs = append(runArgs, "-e", fmt.Sprintf("%s=%s", key, val))
}

// adds node specific args
runArgs = append(runArgs, extraArgs...)

if docker.UsernsRemap() {
// We need this argument in order to make this command work
// in systems that have userns-remap enabled on the docker daemon
runArgs = append(runArgs, "--userns=host")
}

err = docker.Run(
image,
docker.WithRunArgs(runArgs...),
docker.WithMounts(mounts),
)

// we should return a handle so the caller can clean it up
handle = FromName(name)
if err != nil {
return handle, errors.Wrap(err, "docker run error")
}

return handle, nil
}


More



Kind是一个比较简单有趣的项目,Kind的scope定的比较明确和具体,也定的比较小,其实借助 Kind 或者 Kind 的思想,可以做更多的事情,比如:




  • 在单节点部署自己的上层平台

  • 借助容器 mock 节点的方式,优化现有的测试方案

  • 自动化的部署测试

  • 自动化的 e2e 测试



原文链接:https://mp.weixin.qq.com/s/Vhr0ml1wI1BIoxKqKGTXRg

58集团云平台架构实践与演进

玻璃樽 发表了文章 • 0 个评论 • 458 次浏览 • 2019-06-04 19:23 • 来自相关话题

【编者的话】在17年底,我们分享了《高可用Docker容器云在58集团的实践》这篇文章,对整个容器云基础环境搭建与应用选型进行了详细介绍,本文是在该文章基础之上的进阶篇,是针对具体业务场景的落地解决方案。如果对基础环境选型比较感兴趣,可以查看上篇文章,在本文的 ...查看全部
【编者的话】在17年底,我们分享了《高可用Docker容器云在58集团的实践》这篇文章,对整个容器云基础环境搭建与应用选型进行了详细介绍,本文是在该文章基础之上的进阶篇,是针对具体业务场景的落地解决方案。如果对基础环境选型比较感兴趣,可以查看上篇文章,在本文的最后会附上相关文章的链接。对于上篇文章讨论过的内容,本文将不再进行详细讨论。后续每个月,云团队都会选择平台中某一具体领域的相关工作进行详细讨论与分享,欢迎大家关注。大家想了解哪方面的实现方案与细节,可进行相应留言。
#背景

通过容器化技术,58云计算平台主要解决以下几个问题:

  1. 资源利用率低:通过云化技术可以将资源利用率提升至原有的3-4倍,甚至更高。
  2. 服务扩容效率低:将传统扩容的时间从小时级别降低为分钟级别。
  3. 上线流程不规范:基于同一的镜像模板,约束整个上线过程。

为了解决上述问题,云团队通过技术选型与反复论证最终决定基于Docker与Kubernetes体系构建整个容器云环境。

云计算平台的发展历程如下:
1.png

#整体架构

58云计算平台的整体架构如下:
2.png

所有容器云的架构都是相似的,这里不做赘述,具体可查看上篇文章。

云计算平台承载了集团90%以上的业务流量,作为核心的服务管理与上线系统,它并不是独立运作的,为了保证整个上线流程的一致性与流畅度,满足业务日常管理与维护的通用需求,云平台与集团内部多个核心系统与组件进行了相应的对接与联动。
3.jpg

在项目管理方面,云平台与代码管理系统、项目管理等系统进行了内部对接,实现了代码从编译到生成镜像,再到环境部署的完全自动化。

在运维方面,云平台与CMDB、服务树、监控系统等多个运维系统进行了打通与整合,保证整个运维体系的完整性与用户体验的一致性。

在基础组件方面,集团内部的服务治理体系与常用的中间件系统都针对云平台进行了相应的改造,以适配云平台的工作模式,同时云平台也集成了现有的数据收集组件,保证数据流与用户习惯在云化前后是一致的。

云平台的使用方面,平台定义了四套环境,四套环境基于唯一的镜像仓库,这使得同一个代码版本可以在不同的环境之间进行流转,在保证代码唯一性的同时也使得相关环境的变更不会传递到下一个环境。
4.png

#架构演进

构建一个适配多种业务场景的容器云有很多细节需要考虑,云团队做了很多工作,由于篇幅关系,这里主要和大家分享58云计算平台在“网络架构”和“服务发现”两个核心组件上的架构实践与演进。
##网络架构

在网络架构方面,通过对比常用的六种容器组网模型,云计算平台选择“bridge+vlan”的方式作为基础网络模型。
5.png

原生bridge网络模型存在两个明显的缺点:IP利用率低、缺少网络限速。

为了解决IP地址利用率低的问题,云平台基于docker的CNM接口开发了网络插件,支持多个宿主间共享同一个容器网段,也支持IP地址在不同的宿主间复用。
6.png

这种网络模式下,分配给业务实例的IP地址是随机的,实例每次重启IP地址都可能会发生变更。在推进业务云化的早期,这种模式被业务所接受,但是随着云化进程的不断深入,业务方面提出了更高的要求:固定IP。

业务需求来源于真实的案例:有些服务要求IP不能发生变化,特别是某些依赖于第三方外部接口的服务。同时集团内部很多系统都是以IP固定为前提,如果IP随机分配,这些现有系统将不可用或很难用,极大的影响用户体验。如果IP固定的需求不能被满足,业务云化将很难推进下去。所以在18年4月份,云平台对网络架构进行了升级,支持了固定IP模式。网络架构的升级很好的保证了业务云化的进程。
7.png

固定IP的网络架构基于Kubernetes的CNI接口实现,增加了IP控制器模块,业务每次扩缩容时,都会与IP控制器交互,进行IP的变更。在业务正常升级流程中,归属于业务的IP将不会发生变化。依托于腾讯机房的网络支撑,平台将容器网段的路由规则下发到交换机,实现了容器的全网漂移,而不仅仅局限于固定的交换机。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

针对网络限速的需求,云平台结合容器网络虚拟化的特性,基于自研监控与tc工具实现了容器网络限速的能力。
8.jpg

标准的tc工具只支持单向限速,即出口流量限速。单方向限速无法满足业务的实际需求。在深入研究容器网络虚拟化的原理后,我们发现,容器在做虚拟化时,会创建一个网卡对,他们是对等的网络设备,在宿主机上体现为veth,在容器内部体现为eth0。基于这个特性,我们实现了双向限速:即对两块网卡同时做出口流量限速。由于是对等网卡,所以对veth做出口流量限速就相当于对eth0做入口流量限速。

在网络限速的基础上,云平台又进行了多维度的完善,分别支持动态限速、秒级限速与弹性限速等网络应用场景,极大的实现带宽复用。
##服务发现

服务发现是容器云平台中非常核心的服务组件,是流量的入口,也是服务对外暴露的接口。云平台中IP动态分配,节点弹性伸缩,需要有一套自动变更负载均衡器的方式。对于后端服务,集团有成熟的服务治理框架与体系,在云化过程中,中间件团队对服务治理体系进行了改造使其可以适配不断变化的云环境。对于前端服务,集团是基于Nginx做负载均衡,并且没有一套支持IP自动变更的架构。为了实现前端服务的云化,云平台团队与运维团队共同设计了全新的服务发现架构。

在调研阶段,云团队也调研了Kubernetes自带的服务发现机制,这一机制无法提供复杂的负载均衡策略,并且无法满足业务在特殊场景下需要对部分节点摘除流量的需求,所以最终我们否定了这一方案。
9.png

这是业界典型的服务发现架构。服务端和负载均衡器通过Consul进行解耦。服务注册在Consul中,负载均衡器通过Watch Consul中目录的变化来实时感知节点变更。在云化的早期,为了满足快速上线的需求,云平台对集团内部的Java Web框架进行了修改,使得其以心跳的方式自动注册到Consul中。这很好的解决了负载均衡器自动感知业务变更的问题以及云化过程中的流量灰度问题。

这一架构也引入了新的问题:调试问题与多语言扩展问题。由于是Java框架代理业务注册到Consul中,这使得活跃节点无法被下掉流量从而进行调试,但实际场景中很多故障调试需要下掉流量后才能进行。对Java语言的支持是通过修改框架来完成的,而集团中还有很多其他类型语言,比如PHP、Node.js和Go等。对每种接入语言或框架都需要以修改代码的方式才能接入到云平台中来,开发成本高并且对业务不友好。Consul被直接暴露给服务方,也增加了Consul的安全风险。

基于此,在18年4月,云平台对整个服务发现架构进行了升级。
10.png

新的服务发现架构中,业务与Consul中间增加了Proxy代理层,代理层通过Watch Kubernetes事件实时感知业务节点信息的变化,配合健康检查功能可以保证业务节点在变更时流量无损。基于代理层,任意语言的程序不需要做任何修改,仅通过简单配置即可接入到云平台。服务的注册与发现托管到云平台,Consul组件对业务透明,开发人员使用更友好。
#复盘与反思

回顾这两年来的容器云架构演进过程与业务云化历程,复盘遇到的棘手问题及其解决方案,与大家共同探讨。

在整个云平台的设计之初,我们有如下的设计考量:
11.png

容器云都会面临一个问题:对于业务来说,是容器还是虚拟机?虚拟机是业务习惯的使用模式,业务更容易接受。容器是以服务为核心,服务存在即存在,服务关闭即销毁,不再是虚拟机以机器为核心的模式。思虑再三,最终决定以容器的模式来定位,全新的平台提供全新的服务模式与体验。

虽然是以容器为核心,但容器中可运行单进程也可运行多进程。我们规范容器中只运行一个业务进程,防止由于运行多进程而导致业务之间相互干扰情况的发生,这类问题很难进行排查与定位。

去Agent化:在传统物理机模式下,一台物理机上可能会有多个Agent:运维管控Agent、监控Agent、业务自定义Agent等。云化后面临一个现实的问题:我们需要在每个容器中都安装这些Agent么?如果每个容器中都集成这些Agent,对云平台来说是简单的,这没有任何工作量。但是一台物理机上可能会运行上百个容器,这些Agent数量也会大量膨胀,带来系统负载的增加的问题。基于此,云平台投入大量的精力来实现容器的无Agent化,即在所有功能正常运行的前提下,Agent只运行在宿主上,不会运行在容器中。

业务云化过程中云团队遇到了很多问题案例,并形成自己独特的解决方案。这里选择了几个有代表性的案例,与大家进行分享。
12.png

服务启动耗CPU过高是云化早期时遇到的棘手问题。这个问题产生的原因很多:Java的语言特性导致JVM是在运行中逐步进行优化的;内部的很多开发框架都是在流量过来时才初始化链接;某些业务资源也是在流量过来时才进行初始化。当流量分发过来时,多种资源同时初始化导致服务需要大量的CPU资源,超过了平台为其分配的CPU配额,服务出现大量超时与抛弃。这一问题的简单解法是多分配CPU资源,但从长远角度来看这会导致资源利用无法有效把控,也会影响弹性调度的效果。最终,我们从两个维度解决这一问题:在调用方增加预热策略,流量逐步分发过来,CPU资源使用更平滑;在服务方增加预热方法,默认初始化链接等资源,同时引导用户进行自定义的初始化。

在容器监控维度方面,由于默认的监控数据是基于随机采样的分钟级数据,导致当服务出现短期秒级的CPU波动时,监控系统无法捕获,从而影响问题的排查与定位。针对这一问题,云平台对监控维度进行了深化,增加了容器级别秒级监控,实时采集每分钟的最大值上报,便于问题的排查与跟踪。
13.png

由于Cgroups只对CPU和内存资源进行了隔离与限速,并没有对系统负载进行隔离,如果容器中运行进程或线程数过多,会导致宿主机整体的负载波动,进而对上面的所有服务都会造成影响。

云平台增加了两个维度的控制策略:容器级别的最大线程数控制,防止由于单个容器线程数过多会对宿主造成影响。宿主级别的过载保护,当宿主上的负载过高时,过载保护策略会自动触发宿主上的容器进行漂移,直至负载降至合理的范围。

云平台必须关闭swap交换分区,这是一个深刻的经验教训。在云化的早期,云平台的交换分区没有关闭,部分服务经常出现由于使用交换分区而导致的耗时随机抖动,这类问题很难排查,耗费了大量的精力。

58云计算平台核心软件版本变迁过程如下:
14.png

#后记

容器云在58集团的实践与探索过程中,云团队做了很多技术选型与优化工作,也进行了很多技术方案的架构演进工作,究其根本原因是58集团是发展了十多年的互联网公司,它有自己独特的业务场景和成熟的开发体系与基础组件,容器云在集团内部起步较晚,内部系统很难为云平台做适配,只能云平台通过不断演进来适配不停发展的业务场景。这也是在较有规模的互联网公司做基础架构与创业公司的区别和挑战所在。所幸在团队的共同努力下,我们用1年多的时间完成了集团流量服务的云化工作。目前,集团内部还有很多服务需要云化,未来的挑战更大。

原文链接:https://mp.weixin.qq.com/s/gJM5-DByvMH54QhVsJ5HEA

在Kubernetes上运行Kafka合适吗?

whole 发表了文章 • 0 个评论 • 369 次浏览 • 2019-06-02 11:42 • 来自相关话题

【编者的话】本文介绍了在Kubernetes运行Kafka的互补性和一些坑,以及相关方案选择。 # 介绍 Kubernetes设计的初衷是运行无状态工作负载。这些通常采用微服务架构的工作负载,是轻量级,可水平扩展,遵循十二要素应用程序, ...查看全部
【编者的话】本文介绍了在Kubernetes运行Kafka的互补性和一些坑,以及相关方案选择。
# 介绍
Kubernetes设计的初衷是运行无状态工作负载。这些通常采用微服务架构的工作负载,是轻量级,可水平扩展,遵循十二要素应用程序,可以处理环形断路和随机Monkey测试。

另一方面,Kafka本质上是一个分布式数据库。这意味着你必须处理状态,它比微服务更重量级。Kubernetes支持有状态的工作负载,但你必须谨慎对待它,正如Kelsey Hightower在最近的两条推文中指出的那样:
1.png

现在你应该在Kubernetes上运行Kafka吗?我的反问是:没有它,Kafka会跑得更好吗?这就是为什么我要指出Kafka和Kubernetes之间的相互补充性以及你可能遇到的陷阱。
# 运行时
让我们先看一下基本的东西——运行时本身。
## 进程
Kafka brokers对CPU很友好。TLS可能会引入一些开销。如果Kafka客户端使用加密,则需要更多CPU,但这不会影响brokers。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态
## 内存
Kafka brokers是内存消耗大户。JVM堆通常可以限制为4-5 GB,但由于Kafka大量使用页面缓存,因此还需要足够的系统内存。在Kubernetes中,可以相应地设置容器资源限制和请求。
## 存储
容器中的存储是短暂的——重启后数据将丢失。可以对Kafka数据使用emptyDir卷,这将产生相同的效果:brokers的数据将在停机后丢失。您的消息在其他broker上作为副本还是可以使用的。因此,重新启动后,失败的broker必须得复制所有的数据,这可能是一个耗时过程。

这就是你应该使用持久存储的原因。使用XFS或ext4的非本地持久性块存储更合适。我警告你:不要使用NFS。NFS v3和v4都不会起作用。简而言之,Kafka broker会因为NFS“愚蠢重命名”问题而无法删除数据目录,自行终止。如果你仍然不相信我,那么请仔细阅读这篇博文。存储必须是非本地的,以便Kubernetes在重新启动或重新定位时可以更灵活地选择另一个节点。
## 网络
与大多数分布式系统一样,Kafka性能在很大程度上取决于低网络延迟和高带宽。不要试图将所有代理放在同一节点上,因为这会降低可用性。如果Kubernetes节点出现故障,那么整个Kafka集群都会出现故障。不要跨数据中心扩展Kafka集群。这同样适用于Kubernetes集群。不同的可用区域是一个很好的权衡。
# 配置
## 清单
Kubernetes网站包含一个非常好的教程,介绍如何使用清单设置ZooKeeper。由于ZooKeeper是Kafka的一部分,因此可以通过这个了解哪些Kubernetes概念被应用在这里。一旦理解,您也可以对Kafka集群使用相同的概念。

  • Pod:Pod是Kubernetes中最小的可部署单元。它包含您的工作负载,它代表群集中的一个进程。一个Pod包含一个或多个容器。整体中的每个ZooKeeper服务器和Kafka集群中的每个Kafka broker都将在一个单独的Pod中运行。
  • StatefulSet:StatefulSet是一个Kubernetes对象,用于处理需要协调的多个有状态工作负载。StatefulSets保证Pod的有序性和唯一性的。
  • Headless Services:服务通过逻辑名称将Pod与客户端分离。Kubernetes负责负载平衡。但是,对于ZooKeeper和Kafka等有状态工作负载,客户端必须与特定实例进行通信。这就是 Headless Services发挥作用的地方:作为客户端,仍然可以获得逻辑名称,但不必直接访问Pod。
  • 持久卷:如上所述,需要配置非本地持久块存储。

Yolean提供了一套全面的清单,可以帮助您开始使用Kubernetes上的Kafka。

## Helm Charts
Helm是Kubernetes的包管理器,类似yum,apt,Homebrew或Chocolatey等OS包管理器。它允许您安装Helm Charts中描述的预定义软件包。精心设计的Helm Charts能简化所有参数正确配置的复杂任务,以便在Kubernetes上运行Kafka。有几张图表适用于Kafka的的可供选择:一个是处于演进状态的官方图表,一个来自Confluent,另一个来自Bitnami,仅举几例。
## Operators
由于Helm的一些限制,另一种工具变得非常流行:Kubernetes Operators。Operators不仅可以为Kubernetes打包软件,还可以为Kubernetes部署和管理一个软件。

评价很高的Operators名单中提到Kafka有两个,其中一个是Strimzi,Strimzi使得在几分钟内启动Kafka集群变得非常容易,几乎不需要任何配置,它增加了一些漂亮的功能,如群集间点对点TLS加密。Confluent还宣布即将推出新的Operator。
## 性能
运行性能测试以对Kafka安装进行基准测试非常重要。在您遇到麻烦之前,它会为您提供有关可能的瓶颈的地方。幸运的是,Kafka已经提供了两个性能测试工具:kafka-producer-perf-test.sh和kafka-consumer-perf-test.sh。记得经常使用它们。作为参考,可以使用Jay Kreps博客结果,或者 Stéphane Maarek在 Amazon MSK的评论
# 运维
## 监控
可见性非常重要,否则您将不知道发生了什么。如今,有一种不错的工具可以用云原生方式监控指标。Prometheus和Grafana是两种流行的工具。Prometheus可以直接从JMX导出器收集所有Java进程(Kafka,ZooKeeper,Kafka Connect)的指标。添加cAdvisor指标可为提供有关Kubernetes资源使用情况的其他信息。

Strimzi为Kafka提供了一个优雅的Grafana仪表板示例。它以非常直观的方式可视化关键指标,如未复制的和离线分区。它通过资源使用和性能以及稳定性指标来补充这些指标。因此,可以免费获得基本的Kafka集群监控!
2.png

资料来源:https://strimzi.io/docs/master/#kafka_dashboard

可以通过客户端监控(消费者和生产者指标),使用Burrow滞后监控,使用Kafka Monitor进行端到端监控,来完成这个任务
## 日志记录
日志记录是另一个关键部分。确保Kafka安装中的所有容器都记录到标准输出(stdout)和标准错误输出(stderr),并确保Kubernetes集群将所有日志聚合到中央日志记录设施中如Elasticsearch中。
## 健康检查
Kubernetes使用活跃度和就绪探测器来确定Pod是否健康。如果活跃度探测失败,Kubernetes将终止容器并在相应设置重启策略时自动重启。如果准备就绪探测失败,那么Kubernetes将通过服务从服务请求中删除该Pod。这意味着在这种情况下不再需要人工干预,这是一大优点。
## 滚动更新
StatefulSets支持自动更新:滚动更新策略将一次更新一个Kafka Pod。通过这种方式,可以实现零停机时间,这是Kubernetes带来的另一大优势。
## 扩展
扩展Kafka集群并非易事。但是,Kubernetes可以很容易地将Pod缩放到一定数量的副本,这意味着可以声明式地定义所需数量的Kafka brokers。困难的部分是在放大或缩小之前重新分配部分。同样,Kubernetes可以帮助您完成这项任务。
## 管理
通过在Pod中打开shell,可以使用现有的shell脚本完成Kafka群集的管理任务,例如创建主题和重新分配分区。这不是一个很好的解决方案。Strimzi支持与另一个Operator管理主题。这还有改进的余地。
## 备份和还原
现在Kafka的可用性还取决于Kubernetes的可用性。如果Kubernetes群集出现故障,那么在最坏的情况下Kafka群集也会故障。墨菲定律告诉我们,这也会发生在你身上,你会丢失数据。要降低此风险,请确保您具有备份想法。MirrorMaker是一种可选方案,另一种可能是利用S3进行连接备份,如Zalando的博客文章所述。
# 结论
对于中小型Kafka集群,我肯定会选择Kubernetes,因为它提供了更大的灵活性并简化了操作。如果您在延迟和/或吞吐量方面具有非常高的非功能性要求,则不同的部署选项可能更有益。

原文链接:Kafka on Kubernetes — a good fit?(译者:姜俊厚)

如何限制Kubernetes本地临时存储的容量

aoxiang 发表了文章 • 0 个评论 • 266 次浏览 • 2019-05-30 19:02 • 来自相关话题

#介绍 作为Kubernetes平台的提供方,必须要对某些“流氓”应用做出一些限制,防止它们滥用平台的CPU、内存、磁盘、网络等资源。 例如,Kubernetes提供了对CPU,内存的限制,可以防止应用无限制的使用系统的资 ...查看全部
#介绍
作为Kubernetes平台的提供方,必须要对某些“流氓”应用做出一些限制,防止它们滥用平台的CPU、内存、磁盘、网络等资源。

例如,Kubernetes提供了对CPU,内存的限制,可以防止应用无限制的使用系统的资源;Kubernetes提供的PVC,如CephFS、RBD,也支持容量的限制。

但是,早期Kubernetes版本并没有限制container的rootfs的容量,由于默认容器使用的log存储空间是在 /var/lib/kubelet/ 下,rootfs在/var/lib/docker下,而这两个目录默认就在宿主机Node的根分区,如果应用恶意攻击,可以通过在容器内大量dd从而迅速造成宿主机Node根分区文件系统满。我们知道,当Linux根分区使用达到100%的时候,通常会很危险。

Kubernetes在1.8版本引入了一种新的resource:local ephemeral storage(临时存储),用来管理本地临时存储,对应特性 LocalStorageCapacityIsolation。从1.10开始该特性转为beta状态,默认开启。如果你想和更多 Kubernetes 技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

临时存储,如emptyDir volumes, container logs,image layers and container writable layers,默认它们使用的是 /var/lib/kubelet,通过限制临时存储容量,也就可以保护Node的root分区了。

本地临时存储管理只对root分区有效,如果你定制了相关的参数,例如 --root-dir,则不会生效。
#配置
我的集群版本是1.14,默认开启了 local ephemeral storage 的特性,只需要配置Pod即可。

Pod的每个container都可以配置:

* spec.containers[].resources.limits.ephemeral-storage
* spec.containers[].resources.requests.ephemeral-storage

单位是byte,可以直接配置,也可以按E/P/T/G/M/K或者Ei, Pi, Ti, Gi, Mi, Ki.为单位来配置,例如 128974848, 129e6, 129M, 123Mi 表示的是同一个容量。

下面创建一个Deployment,设置其使用的临时存储最大为2Gi。
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: nginx
namespace: default
spec:
selector:
matchLabels:
run: nginx
template:
metadata:
labels:
run: nginx
spec:
containers:
- image: nginx
name: nginx
resources:
limits:
ephemeral-storage: 2Gi
requests:
ephemeral-storage: 2Gi

Pod启动后,进入容器,执行dd if=/dev/zero of=/test bs=4096 count=1024000 ,尝试创建一个4Gi的文件,可以发现在执行一段时间后,Pod被 Evict,controller重新创建了新的Pod。
nginx-75bf8666b8-89xqm                    1/1     Running             0          1h
nginx-75bf8666b8-pm687 0/1 Evicted 0 2h

#实现
Evict Pod动作是由kubelet完成的。每个节点上的kubelet会启动一个evict manager,每10秒种(evictionMonitoringPeriod)进行一次检查,ephemeral storage的检查也是在这个阶段完成的。

evict manager可以对pod和container来检查超额应用。
func (m [i]managerImpl) localStorageEviction(summary [/i]statsapi.Summary, pods [][i]v1.Pod) [][/i]v1.Pod {
statsFunc := cachedStatsFunc(summary.Pods)
evicted := []*v1.Pod{}
for _, pod := range pods {
podStats, ok := statsFunc(pod)
if !ok {
continue
}

if m.emptyDirLimitEviction(podStats, pod) {
evicted = append(evicted, pod)
continue
}

if m.podEphemeralStorageLimitEviction(podStats, pod) {
evicted = append(evicted, pod)
continue
}

if m.containerEphemeralStorageLimitEviction(podStats, pod) {
evicted = append(evicted, pod)
}
}

return evicted
}

其中Pod为GetActivePods获取的本节点所有非Terminated状态的Pod。

kubelet会依此检查Pod的emptyDir、Pod级临时存储、container级临时存储,若Pod需要被evict,则加到evicted数组,之后会将evicted的Pod挤出。

contaier级检查比较简单,因为ephemeral storage设置的就是在container上,依次检查container的使用情况和设置的limits,如果超过了limits,则要加入到evicted pods列表中。

相关代码在 containerEphemeralStorageLimitEviction 中。

而Pod级别的检查会复杂一点。

首先是限制值的计算。

kubelet会统计Pod所有container(但不包括init container)的ephemeral storage limits之和。init container指定的是Pod的配额最低需求(有点像最低工资标准,用于生活保障),当所有container指定的配额,超过init container指定的配额时,将忽略init container指定的配额。数学描述如下。
max(sum(containers), initContainer1, initContainer2, ...)

而实际临时存储用量的计算,除了会计算指定过ephemeral storage的container的使用量,还会统计未指定过ephemeral storage的container,以及emptyDir的使用量。

当实际临时存储用量,超过了限制值时,kubelet会将该Pod Evict,然后等待controller重新创建新的Pod并重新调度。

相关代码在 podEphemeralStorageLimitEviction 中。
#requests
注意,设置的local ephemeralstorage requests在evict manager处理过程中没有用到。但是它不是没用的。

创建Pod后,scheduler会将该Pod调度到集群中某个node上。由于每个node所能承载的local ephemeral storage是有上限的,所以scheduler会保证该node上所有Pod的 local ephemeralstorage requests 总和不会超过node的根分区容量。
#inode 保护
有的时候,我们会发现磁盘写入时会报磁盘满,但是df查看容量并没有100%使用,此时可能只是因为inode耗尽造成的。因此,对平台来说,inode的保护也是需要的。

其中,podLocalEphemeralStorageUsage 也统计了container或者pods使用的inode的数量。

但是当前Kubernetes并不支持对Pod的临时存储设置inode的limits/requests。

当然了,如果node进入了inode紧缺的状态,kubelet会将node设置为 under pressure,不再接收新的Pod请求。
#emptyDir
emptyDir也是一种临时存储,因此也需要限制使用。

在Pod级别检查临时存储使用量时,也会将emptyDir的使用量计算在内,因此如果对emptyDir使用过量后,也会导致该Pod被kubelet Evict。

另外,emptyDir本身也可以设置容量上限。如下所摘录编排文件片段,我指定了emptyDir使用内存作为存储介质,这样用户可以获得极好的读写性能,但是由于内存比较珍贵,我只提供了64Mi的空间,当用户在 /cache 目录下使用超过64Mi后,该Pod会被kubelet evict。
        volumeMounts:
- mountPath: /cache
name: cache-volume
volumes:
- emptyDir:
medium: Memory
sizeLimit: 64Mi
name: cache-volume

相关代码在 emptyDirLimitEviction 中。

原文链接:https://ieevee.com/tech/2019/05/23/ephemeral-storage.html

火热的云原生到底是什么?一文了解云原生四要素!

阿娇 发表了文章 • 0 个评论 • 240 次浏览 • 2019-05-30 18:33 • 来自相关话题

所谓云原生,它不是一个产品,而是一套技术体系和一套方法论,而数字化转型是思想先行,从内到外的整体变革。更确切地说,它是一种文化,更是一种潮流,是云计算的一个必然导向。 随着虚拟化技术的成熟和分布式架构的普及,用来部署、管理和运行应 ...查看全部
所谓云原生,它不是一个产品,而是一套技术体系和一套方法论,而数字化转型是思想先行,从内到外的整体变革。更确切地说,它是一种文化,更是一种潮流,是云计算的一个必然导向。

随着虚拟化技术的成熟和分布式架构的普及,用来部署、管理和运行应用的云平台被越来越多的提及。IaaS、PaaS和SaaS是云计算的3种基本服务类型,它们是关注硬件基础设施的基础设施即服务、关注软件和中间件平台的平台即服务以及关注业务应用的软件即服务。

在容器技术、可持续交付、编排系统等开源社区的推动下,以及微服务等开发理念的带动下,应用上云已经是不可逆转的趋势。随着云化技术的不断进展,云原生的概念也应运而生。
#云原生概念的诞生

云原生(Cloud Native)的概念,由来自Pivotal的MattStine于2013年首次提出,被一直延续使用至今。这个概念是Matt Stine根据其多年的架构和咨询经验总结出来的一个思想集合,并得到了社区的不断完善,内容非常多,包括DevOps、持续交付(Continuous Delivery)、微服务(MicroServices)、敏捷基础设施(Agile Infrastructure)和12要素(The Twelve-Factor App)等几大主题,不但包括根据业务能力对公司进行文化、组织架构的重组与建设,也包括方法论与原则,还有具体的操作工具。采用基于云原生的技术和管理方法,可以更好地把业务生于“云”或迁移到云平台,从而享受“云”的高效和持续的服务能力。
1.jpg

The Twelve-Factor App

顾名思义,云原生是面向“云”而设计的应用,因此技术部分依赖于传统云计算的3层概念,基础设施即服务(IaaS)、平台即服务(PaaS)和软件即服务(SaaS),例如,敏捷的不可变基础设施交付类似于IaaS,用来提供计算网络存储等基础资源,这些资源是可编程且不可变的,直接通过API可以对外提供服务;有些应用通过PaaS服务本来就能组合成不同的业务能力,不一定需要从头开始建设;还有一些软件只需要“云”的资源就能直接运行起来为云用户提供服务,即SaaS能力,用户直接面对的就是原生的应用。
##云原生并不是一个产品

最近讨论云原生应用越来越多。关于云原生应用,简单地说,就是大多数传统的应用,不做任何改动,都是可以在云平台运行起来,只要云平台支持这个传统应用所运行的计算机架构和操作系统。只不过这种运行模式,仅仅是把虚拟机当物理机一样使用,不能够真正利用起来云平台的能力。

云并非把原先在物理服务器上跑的东西放到虚拟机里跑,真正的云化不仅是基础设施和平台的事情,应用也要做出改变,改变传统的做法,实现云化的应用——应用的架构、应用的开发方式、应用部署和维护技术都要做出改变,真正的发挥云的弹性、动态调度、自动伸缩……一些传统IT所不具备的能力。这里说的“云化的应用”也就是“云原生应用”。云原生架构和云原生应用所涉及的技术很多,如容器技术、微服务、可持续交付、DevOps等。
2.jpg

而云原生应用最大的特点就是可以迅速部署新业务。在企业里,提供新的应用程序环境及部署软件新版本通常所需时间以日、周甚至以月计算。这种速度严重限制了软件发布所能承受的风险,因为犯错及改错也需要花费同样的时间成本,竞争优势就会由此产生。

所以云原生不是一个产品,而是一套技术体系和一套方法论,而数字化转型是思想先行,从内到外的整体变革。更确切地说,它是一种文化,更是一种潮流,是云计算的一个必然导向。意义在于让云成为云化战略成功的基石,而不是障碍。它可以根据商业能力对公司进行重组的能力,既包含技术、也包含管理,可以说是一系列云技术和企业管理方法的集合,通过实践及与其他工具相结合更好地帮助用户实现数字化转型。
##云原生计算基金会(CNCF)

CNCF,即云原生计算基金会,2015年由谷歌牵头成立,基金会成员目前已有一百多企业与机构,包括亚马逊、微软、思科等巨头。

目前CNCF所托管的应用已达14个,下图为其公布的Cloud Native Landscape,给出了云原生生态的参考体系。
3.jpg

Cloud Native Landscape新版

CNCF(云原生计算基金会)认为云原生系统需包含的属性:

* 容器化封装:以容器为基础,提高整体开发水平,形成代码和组件重用,简化云原生应用程序的维护。在容器中运行应用程序和进程,并作为应用程序部署的独立单元,实现高水平资源隔离。
* 自动化管理:统一调度和管理中心,从根本上提高系统和资源利用率,同时降低运维成本。
* 面向微服务:通过松耦合方式,提升应用程序的整体敏捷性和可维护性。

正因为如此,你可以专注于创新,解决业务问题,而不是把时间花在“静态、不灵活的传统架构”存在的许多技术问题。
#云原生的四要素:持续交付、DevOps、微服务、容器

从云原生的概念中,我们总是能看到持续交付、DevOps、微服务、容器等技术的出现,那么它们到底是什么,这里引用Pivotal台湾云计算资深架构师的部分观点,为大家逐一揭开他们的神秘面纱!
4.jpg

##持续交付——缩小开发者认知,灵活开发方向

首先是持续交付,什么样的时候客户要求持续交付?敏捷开发要求持续交付,因为敏捷开发要求随时有一个版本可以上到大群环境,所以要持续交付。

而换句话说,持续交付就是不误时开发。举一个例子,有些公司非常喜欢谈需求,谈很久,可是开发只剩1/3时间就开发完成,然后交付,再上线运营。这就会碰到一个问题,就是你开始谈需求到最后交付产品的时间,短则三月,长则半年,这中间市场已经变化了,需求也随之变化了。因此市场上出现了新的想法,即是不是能够小步快跑,把交付的周期缩短一点,我可以实现快速交付,每次交付都可以重新确认方向,这样尽量避免与未来期待的落差。
5.jpg

用小步快跑的方式,打破瀑布式开发流程

那么问题来了,持续交付对于开发的人谈的需求、开发的方式有改变,那它对于开发有影响吗?如果说公司的开发团队一天可以交付五次,那研发团队要帮忙部署一次吗?现在公司大部分部署都是研发团队帮忙部署应用的,研发团队部署五次,要改版五次就需要部署一次,这是无法实现的。而且每次部署的时候都要面对停机,而实际公司的应用经不起一天停机五次部署,在互联网的思维之下,零宕机时间已经是现在企业的基本要求。于是“蓝绿部署”的概念营运而生。即在一个环境里面,第一版还在线上服务,第二版先做封测,封测完成后,让外面的流量进来一些,看log是不是开发人员要的,确认后再把全部的流量导到新的版本上。
6.jpg

蓝绿(Blue-Green)部署

但“蓝绿部署”在系统过多过复杂的情况下,在传统架构上实现非常困难,所以企业要做到zero down time的持续交付就需要有良好的平台與工具协助。因此,持续交付的优势在于,它可以缩小开发者认知,重新确认开发方向。
##微服务——内聚更强,更加敏捷

第二部分是微服务。微服务是什么?有客户表示,提供商出产品,客户把应用全部放上去,结果就是一个微服务。这种认知是错误的,因为微服务是一个架构的改变。那么微服务是怎么做的呢?它所面临的最大挑战是什么?

是切割。那么如何切割呢?其实这件事情早在1968年康威就提出了——康威定律,系统的服务划分应该是根据组织架构的功能来划分。1968年康威就提出了这个想法,我认为拿来做微服务的切割非常适用。
7.jpg

Going Agile - Breaking the monolith Conway's Law and Microservices

这样按照组织架构划分的优势在于:

  1. 内聚更强,所有遵循同一种业务准则的人内聚在一起,就容易解决问题。
  2. 服务解耦,变更容易,更加敏捷。当做到解耦合的时候,要变更就容易。所以微服务应该是切分成这个样子,由上而下来切,根据Function来切。

另外一个划分微服务的技巧,可以运用领域驱动设计(Domain Driven Design)的理论,而领域驱动设计亦可算是面向物件的一种设计思维;聚合可以让微服务划分更有依据,也让未來的系統变更具有弹性。值得一提的是领域驱动设计,也提供微服务中的事物问题。因为过去巨石应用进行两个报数的阶段,相当容易也常见,但在微服务架构中,如何在分散的服务中进行事物就显得相当困难。利用领域驱动设计的Event Souring进行设计,是目前最好的解決办法。

那么在什么情况下需要微服务?我认为有三个标准:

  1. 有HA(High Available)的需求需要微服务。
  2. 有性能调校的需求(例如:图片的呈现或者搜寻)需要微服务。
  3. 经常变更的需要微服务。

实际上,微服务需要关注的源代码范围比较小,使得各个服务解耦、变更容易,内聚更强,因为都会集中在服务里。另外,它更容易单独改版,因为微服务之间是用RESTful间接起来的,用RESTful只要API的界面不改,原则上则不会错,也更敏捷。

但微服务也会留下一些问题,例如App团队如何分工?环境怎么配合?如何实现自动化部署?
##容器技术——使资源调度、微服务更容易

再来看看容器。在机器上运行的容器只是主机操作系统上的一个进程,与任何其他进程无异。那么,为什么容器如此受欢迎呢?原因在于这个进程被隔离和限制的方式。这种方式很特殊,可简化开发和运维。

其实1979年就有容器技术,很多人会以为说Docker是不是等于容器,其实Docker不等于容器。容器的历史可追溯到Linux操作系统。容器利用了Linux的内核功能。Linux中容器的核心概念(cgroup、namespaces和filesystems)在独立的区域运行。容器的神奇之处在于将这些技术融为一体,以实现最大的便利性。

VMware之前的技术专家在2011年发展出一个技术,把这个技术贡献出来成立了一个Cloud Foundry基金会。Docker在2013年才开始有,而且它第一版是用SLC的技术去做的。后来陆续一路成长,使得为服务的实现更容易了。
8.jpg

从 Infra 角度来看技术演进

从上面这个表中可以看出,从左边开始,IaaS,虚拟化技术有了之后,刚刚提到的所谓第三代平台,这四个区块开发人员交付的内容不一样。所有的IaaS、CaaS、PaaS、FaaS一路的变化演进,对于客户的负担越到后面越小,而对于开发人员的想象力则愈发抽象。

大家一定会遇到下列这些计算,一个是所谓的单体应用,或者翻译成巨石应用。此外,你们一定会有一些批次的管理,另外就是所谓的数据库的部分,开始可能会有容器技术,像Kubernetes、Docker。

Docker是软件行业最受欢迎的软件容器项目之一。思科、谷歌和IBM等公司在其基础设施和产品中使用Docker容器。

Kubernetes是软件容器领域的另一个值得关注的项目。Kubernetes是一个允许自动化部署、管理和伸缩容器的工具。为了便于管理其容器,谷歌建立了Kubernetes。它提供了一些强大的功能,例如容器之间的负载均衡,重启失败的容器以及编排容器使用的存储。如果你想和更多 Kubernetes 技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态
9.jpg

容器生态图

容器为云原生应用程序增加了更多优势。使用容器,你可以将微服务及其所需的所有配置、依赖关系和环境变量移动到全新的服务器节点上,而无需重新配置环境,这样就实现了强大的可移植性。
##DevOps——以终为始,运维合一

10.png

最后让我们走向DevOps,它不是一种工具,DevOps其实要谈的是运维合一。

DevOps如果从字面上来理解只是Dev(开发人员)+Ops(运维人员),实际上,它是一组过程、方法与系统的统称,其概念从2009年首次提出发展到现在,内容也非常丰富,有理论也有实践,包括组织文化、自动化、精益、反馈和分享等不同方面。

首先,组织架构、企业文化与理念等,需要自上而下设计,用于促进开发部门、运维部门和质量保障部门之间的沟通、协作与整合,简单而言组织形式类似于系统分层设计。

其次,自动化是指所有的操作都不需要人工参与,全部依赖系统自动完成,比如上述的持续交付过程必须自动化才有可能完成快速迭代。再次,DevOps的出现是由于软件行业日益清晰地认识到,为了按时交付软件产品和服务,开发部门和运维部门必须紧密合作。

总之,DevOps强调的是高效组织团队之间如何通过自动化的工具协作和沟通来完成软件的生命周期管理,从而更快、更频繁地交付更稳定的软件。在内部沟通上,你可以想象DevOps是一个敏捷思維,是一个沟通的文化。当运营和研发有良好的沟通效率,才可以有更大的生产力。如果你的自动化程度够高,可以自主可控,工作负担降低,DevOps能够带来更好的工作文化、更高的工作效率。
#总结

综上所述,云原生的DevOps、平台、持续交付、微服务都是云原生不可或缺的一部分,需要以全局地眼光看待问题,脱离任何一个元素,对于企业来说都是“管中窥豹”、“一叶障目”,只有加以整合才能见到云原生的全局风貌。

面对业态各异的业务上云以及碎片化的物联网解决方案部署,利用云原生思维和模式,构建基于云原生的物联网平台以及解决方案,势必将加速企业,甚至整个社会的数字化转型。

原文链接:https://mp.weixin.qq.com/s/RaAyjfGacHc7xpRahpfv8Q

聊一聊 Kafka 应用实践与生态集成的相关内容

齐达内 发表了文章 • 0 个评论 • 212 次浏览 • 2019-05-30 11:38 • 来自相关话题

#前言 Apache Kafka 发展至今,已经是一个很成熟的消息队列组件了,也是大数据生态圈中不可或缺的一员。Apache Kafka 社区非常的活跃,通过社区成员不断的贡献代码和迭代项目,使得 Apache Kafka 功能越发丰富、性能越发稳 ...查看全部
#前言
Apache Kafka 发展至今,已经是一个很成熟的消息队列组件了,也是大数据生态圈中不可或缺的一员。Apache Kafka 社区非常的活跃,通过社区成员不断的贡献代码和迭代项目,使得 Apache Kafka 功能越发丰富、性能越发稳定,截止本篇文章 Apache Kafka 发布了 V2.2.0 版本。

那么,今天就来聊一聊 Kafka 应用实践与生态集成的相关内容。
#如何知道 Kafka 是否适合你?
项目立项时,会做技术调研,那么如何知道你选择的 Kafka 技术是否能够满足你?据 Confluent 公司调研报告可知,Apache Kafka 在过去几年中在功能和覆盖范围方面取得了很大成就。它被财富500强中的三分之一用于生产,包括全球十大银行中的七家,十大保险公司中的八家,以及美国十大电信公司中的九家。接下来,为大家介绍 Kafka 示例来帮助大家了解常见的使用模式。并且希望大家能找到与自己的工作流程有交集的地方,这样大家就可以开始利用 Kafka 的强大功能了。
1.jpg

下面让先来看看 Kafka 提供的两个核心功能:
##消息系统
消息系统常见的两种模式:

* 队列:队列消费者充当了工作组的角色,每条消息记录只传递给一个工作进程,从而有效的划分工作流程;
* 发布与订阅:订阅者通常是彼此独立的,每个订阅者都可以获得每条消息的副本。

这两种模式都是有效和实用的,通过队列将工作内容分开,用于容错和扩展;发布与订阅能够允许多租户,来使系统解耦。而 Apache Kafka 的有点之一在于它将队列、发布与订阅结合到了一个强大的消息系统中。
##流处理
Apache Kafka 拥有强大,可扩展的消息系统,只需要一种简单的方法来处理消息流。而在 Kafka 中,Stream API 提供这一功能,它是一个 Java 客户端类库,提供比 Producer 和 Consumer 更高级别的抽象 API 。

这使得它使用起来非常的方便:

* 无状态操作,例如过滤和转换流消息;
* 有状态操作,例如时间窗口上的连接和聚合。

Stream API 处理消息的序列化与反序列化,同时维护有状态操作所需要的状态。
##典型的 Kafka 案例
旅游行业:例如,在一个旅游网站,酒店和航班的价格是一直在变化的,系统的一些组件 ( 价格告警、分析等 ) 需要了解这些变化。你在 Kafka 的 Topic 上发布更改,并且需要通知的每个组件都充当一个消费者。每个消费者应用所组成的节点形成一个消费者组。给消费者组所消费的 Topic 的发送消息动态记录,这样每个消费者均可获取消息记录,同时每个消费者内能够有效的划分工作内容。

用户分析:页面查看、搜索、用户行为分析等,这些实际上是 Kafka 在 LinkedIn 设计的原始初衷。用户点击网站活动内容,每个活动类型均有一个 Topic,可以实时的反馈,以便深入了解用户参与度、下载量、页面流量等。

GPS:例如,能够实时获取智能手机设备的位置数据,并且希望能够实时处理这些数据来显示车辆路径、行驶距离等。传入数据到 Kafka 的 Topic 中,并使用 Stream API 来进行处理。当需要在特定时间段内提取和处理给定用户的所有位置数据时,使用窗口进行状态处理会有不错的效果。
#Kafka 的内部存储工作原理是什么?
如何你确定了 Kafka 技术适合你当前的项目,满足你的业务需求。你可能会很好奇,Kafka 的内部存储工作原理是什么呢?接下来,将给大家分析 Kafka 是如何存储其数据的。
##Kafka存储单元是分区
Topic 中的分区是有序写入的,且不可变的消息序列。分区不能跨多个 Broker 或者多个磁盘来进行分割。
2.jpg

##保留策略来管理 Topic 中消息
在你创建的 Topic 中,你可以指定保留多少数据或者保留多长时间的数据,然后 Kafka 会按照顺序来清除这些消息 ( 不管消息是否有被使用 ) 。
##分区片段
Kafka 需要定期查找磁盘上待清除的数据,对于分区消息单个非常长的文件,该操作会很慢并且容易出错。为了解决这个问题,Kafka 实行了分区分片策略。当 Kafka 将消息写入分区时,它会写入到一个片段,如果该片段到达阀值,则会新开一个新的片段来写入。片段以偏移量来命名,片段的偏移量是大于前一个片段的偏移量且小于或者等于当前片段中的偏移量。
3.jpg

##片段日志是存储消息的位置
每条消息都包含值、偏移量、时间戳、主键 ( KEY ) 、消息大小、压缩编解码器、校验、以及消息格式的版本。磁盘上的数据格式与 Broker 通过网络从 Producer 端接收的格式完全相同,然后由 Consumer 去获取数据,这使得 Kafka 能够通过零拷贝技术有效的传输数据。
##片段索引将消息偏移量映射到它们在日志中的位置
4.jpg

索引文件是内存映射的,偏移量查找时使用二进制搜索来查找小于或等于最近的目标偏移量。索引文件由8个字节组成,4个字节用于存储基本偏移量,另外4个字节来存储位置。
##Kafka 将压缩的消息包装在一起
发送压缩消息的 Producer 端会将压缩批处理,并将其作为包装消息的有效负载发送。和之前一样,磁盘上的数据与 Broker 通过网络从 Producer 端接收并发送给其 Consumer 的数据完全相同。
5.jpg

##Kafka 内部存储工作原理小结

* Kafka 的存储单元是分区;
* 分区通过片段来进行分割;
* 片段包含两个文件:索引和日志文件;
* 索引将每个偏移量映射到它们所在日志中的消息位置,用于查找消息;
* 压缩消息批处理作为包装消息的有效负载;
* 存储在磁盘上的数据与 Broker 通过网络从 Producer 端接收并发给 Consumer 的数据相同。

#Kafka API 之间的选择与竞争
Kafka 的核心尽管在一段时间内保持相对的稳定,但是 Kafka 生态圈仍然在快速的发展。最初的 Kafka,包含 Producer 和 Consumer,很容易理解。现在 Kafka 处理 Producer 和 Consumer,还有 Kafka Connect 、Kafka Streams 、以及 KSQL。
6.jpg

##如何正确的选择 Kafka API
Kafka Producer API:应用直接生成数据,例如移动设备、PC 、其他硬件等。

Kafka Connect Source API:应用程序桥接在我们无法控制的数据存储介质,例如 MongoDB 、ElasticSearch、RESTAPI 等。

Kafka Streams API/KSQL:如果希望像 SQL 一样操作实时流数据,可以通过 KSQL 来完成;如果需要编写复杂的业务逻辑,可以使用 Kafka Streams API 来完成。

Kafka Consumer API:直接读取流数据,并对其执行实时操作,例如推送商品促销活动、发送邮件、获取游戏行为等。

Kafka Connect Sink API:读取实时流并将其存储到目标介质中,例如 Kafka 到 S3、Kafka 到 HDFS、Kafka 到 HBase 等。

选择不同的 API 来实现不同的业务需求,例如,如果希望为实现的需求编写大量的自定义代码,Kafka Consumer API 和 Kafka Connect Sink API 完全是可以互换的。总而言之,上述 API 可以帮助你在实际的业务中以最少的代码量来实现最有效的工作流程。
##各个API的优势和局限
Kafka Producer API

优势: Kafka Producer API 使用起来非常的简单,异步发送数据,获取回调结果。非常适合直接发送数据流的应用程序,例如日志、点击流、物联网等。

局限:可以扩展和构建 Kafka Producer API 以便执行更多的操作,但是这需要开发人员编写更多的附加逻辑。例如,试图使用 Kafka Producer API 在数据库和 Kafka 之间执行 ETL 操作时,如何跟踪偏移量 ( 即当 Producer 端停止后,如何正确恢复你的 Producer 应用程序 ) ?如何在若干个 Producer 之间分配 ETL 的负载?这种情况,我们使用 Kafka Connect Source API 会更好一些。

Kafka Connect Source API

优势:Kafka Connect Source API 是一个构建在 Kafka Producer API 之上的完整框架。它的构建是为了让开发人员能够获得更好的 API,以便为并行处理生成并分配任务。另外,可以使用各种各样的连接器,利用这些连接器来处理大多数数据介质,且无需编写任何代码。

局限:适配的数据源连接器均是专属的,如果你当前的数据源在已有的连接器中不包含,需要自行编写连接器来进行适配。

Kafka Consumer API

优势:Kafka Consumer API 非常简单,可以使用 Consumer Groups,因此可以并行使用 Topic 。新版本的 Kafka ( V2.2.0 ) 对于偏移量的管理和提交、Balance 、幂等性等无需开发者关心。

局限:在 ETL 场景中,Kafka Connect Sink 更加合适,因为它们会避免针对外部数据源编写复杂的逻辑。

Kafka Connect Sink API

优势:与 Kafka Connect Source API 类似,Kafka Connect Sink API 允许利用现有的 Kafka 连接器的生态系统来执行流式 ETL,且无需编写任何代码。Kafka Connect Sink API 建立在 Kafka Consumer API 的基础之上,但是与它有所不同。

局限:如果写入的数据源没有可用的适配器,那么需要自行编写 Kafka Connect 连接器,并且调试过程会有些复杂。

Kafka Streams API

优势:对于流处理场景,Kafka 中附带 Kafka Streams API,并且能够编写高级 DSL ( 类似于函数式编程或者Spark类型的程序 ) 或偏底层的 API ( 类似于Storm ) 。Kafka Streams API 完全隐藏了 Producer 和 Consumer 的复杂性,让开发者更加专注于流处理的逻辑实现上。同时,它还具有连接、聚合、一次性处理等功能。

局限:使用 Kafka Streams API 会让编码门槛提高,同时也可能让你业务逻辑变得复杂。

KSQL

优势:KSQL 不是 Kafka API 的直接组成部分,而是 Kafka Streams 之上的包装器。这里还是值得一说的,虽然 Kafka Streams 允许编写一些复杂的 Topology,但它还是需要一些实质性的编程知识,尤其是新手来说。KSQL 希望通过提供与现有的 SQL 语义类似来抽象出这种复杂性。对于开发者来说,KSQL 是非常具有诱惑力的,它使得流处理器变得轻而易举。

局限:对于复杂的业务场景,对数据进行复杂的转换操作,或一些特定的需求,可能还是需要使用 Kafka Streams 来完成。
#Kafka 与 Kubernetes 结合是否效率更高?
7.jpg

##介绍
Kubernetes 是 Google 开源的一个容器编排引擎,它支持自动化部署、大规模可伸缩、应用容器化管理。Kubernetes 旨在运行无状态工作负载,这些工作负载通常采用微服务架构形式,轻量级、水平扩展。而 Kafka 的本质上是一个分布式的存储介质,这意味着你在使用时必需处理状态,它比微服务更重要。尽管 Kubernetes 支持有状态的工作负载,但还是需要谨慎使用。如果你想和更多 Kubernetes 技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

那么,应该在 Kubernetes 上运行 Kafka 吗?如何让 Kafka 和 Kubernetes 互相补充,以及如何避免可能遇到的“坑”?
##基础指标
进程:Kafka Broker 对 CPU 很友好,TLS 的引入可能会产生一些开销。Kafka Client 如果使用加密会需要更多的 CPU,但是这并不会影响 Broker。

内存:Kafka Broker 的 JVM 通常可以设置为 4GB-8GB 之间,但是由于 Kafka 大量使用了页面缓存,因此还是需要有足够的系统内存。在 Kubernetes 中,相应的设置容器资源限制和请求。

存储:容器中的存储是暂时的,重启后数据将会丢失,但是可以对 Kafka 数据使用空目录卷。因此,需要使用持久化存储,存储必须是非本地的,以便 Kubernetes 在重启后或重新定位后更加灵活的选择另一个节点。

网络:与大多数分布式系统一样,Kafka 性能在很大程度上取决于低网络延迟和高带宽。建议不要把所有的 Broker 放在同一个节点,因为这样会降低可用性。如果 Kubernetes 节点出现故障,那么整个 Kafka 集群都会出现故障。
##性能
安装 Kafka 之前,做 POC 测试是非常重要的。这样做的好处是,在遇到有关性能瓶颈问题时,可以提供帮助。而 Kafka 附带了两个 POC 测试工具,它们分别是:kafka-producer-perf-test.sh 和 kafka-consumer-perf-test.sh。

监控:监控 Kafka 指标是非常有必要的,能够让我们及时的掌握 Kafka、Zookeeper 集群的健康状态,例如使用 Kafka Eagle 来监控和管理 Kafka Topic(http://www.kafka-eagle.org/ )。

日志:日志是一个比较关键的部分,确保 Kafka 安装中所有的容器都记录到 stdout 和 stderr 中,并确保 Kubernetes 集群日志能集中管理,例如输送到 ElasticSearch。

动态更新:StatefulSets 支持自动更新,RollingUpdate 策略将一次更新一个 Kafka Pod,来实现零停机维护,这也是 Kubernetes 的优势之一。

扩容:Kubernetes 可以很容易的将 Pod 缩放到一定数量的副本,这意味着可以声明性的定义所需数量的 Kafka Broker 。

备份&还原:Kafka 部署在 Kubernetes 中,这样 Kafka 的可用性就取决于 Kubernetes 的可用性,如果 Kubernetes 集群出现故障,那么 Kafka 的可用性就会下降,同时,也会出现数据丢失的风险,因此需要做好数据备份策略,例如 MirrorMaker,或是 S3 进行连接备份。
##对于 Kubernetes 的选择
对于中小型的 Kafka 集群,将 Kafka 部署到 Kubernetes 是一个不错的选择,因为它提供了更大的灵活性、且简化了操作。如果在延迟或吞吐量方面有较高的功能性要求,独立部署的方式可能会更好。
#总结
本篇文章,介绍了 Kafka 应用实践与生态集成,通过参考本篇文章的内容,大家可以做出合理、有效的选择。

原文链接:https://mp.weixin.qq.com/s/KWkMGd7bvEYhWXRn_udiNg

云原生之下的Java

尼古拉斯 发表了文章 • 0 个评论 • 197 次浏览 • 2019-05-30 10:22 • 来自相关话题

自从公司的运行平台全线迁入了 Kubenetes 之后总是觉得 DevOps 变成了一个比以前更困难的事情,反思了一下,这一切的困境居然是从现在所使用的 Java 编程语言而来,那我们先聊聊云原生。 Cloud Native 在我的理 ...查看全部
自从公司的运行平台全线迁入了 Kubenetes 之后总是觉得 DevOps 变成了一个比以前更困难的事情,反思了一下,这一切的困境居然是从现在所使用的 Java 编程语言而来,那我们先聊聊云原生。

Cloud Native 在我的理解是,虚拟化之后企业上云,现在的企业几乎底层设施都已经云化之后,对应用的一种倒逼,Cloud Native 是一个筐,什么都可以往里面扔,但是有些基础是被大家共识的,首先云原生当然和编程语言无关,说的是一个应用如何被创建/部署,后续的就引申出了比如 DevOps 之类的新的理念,但是回到问题的本身,Cloud Native 提出的一个很重要的要求,应用如何部署 这个问题从以前由应用决定,现在变成了,基础设施 决定 应用应该如何部署。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

让我们回到一切的开始,首先云原生亦或者是 DevOps 都有一个基础的要求,当前版本的代码能够在任何一个环境运行,看起来是不是一个很简单的需求,但是这个需求有一个隐喻所有的环境的基础设施是一样的,显然不能你的开发环境是 Windows 测试环境 Debian 生产环境又是 CentOS 那怎么解决呢,从这一环,我们需要一个工具箱然后往这个工具箱里面扔我们需要的工具了。首先我们需要的就是 Cloud Native 工具箱中最为明显的产品 Docker/Continar,经常有 Java 开发者问我,Docker 有什么用,我的回答是,Docker 对 Java 不是必须的,但是对于其他的语言往往是如果伊甸园中的苹果一样的诱人,打个比方,一个随系统打包的二进制发行版本,可以在任何地方运行,是不是让人很激动,对于大部分的 Java 开发者可能无感,对于 C 语言项目的编写者,那些只要不是基于虚拟机的语言,他们都需要系统提供运行环境,而系统千变万化,当然开发者不愿意为了不同的系统进行适配,在以前我们需要交叉编译,现在我们把这个复杂的事情交给了 Docker,让 Docker 如同 Java 一样,一次编写处处运行,这样的事情简直就像是端了 Java 的饭碗,以前我们交付一个复杂的系统,往往连着操作系统一起交付,而客户可能买了一些商业系统,为了适配有可能还要改代码,现在你有了Docker,开发者喜大普奔,而这里的代价呢?C&C++&GO 他们失去的是枷锁,获得全世界,而 Java 如同被革命一般,失去了 Once Code,Everywhere Run,获得的是更大的 Docker Image Size,获得被人诟病的 Big Size Runtime。

当我们从代码构建完成了镜像,Cloud Navtive 的故事才刚刚开始,当你的 Team Leader 要求你的系统架构是 MicroServices 的,你把原来的项目进行拆分了,或者是开发的就拆分的足够小的时候,你发现因为代码拆分开了,出现了一点点的代码的重复,有适合也避免不了的,你的依赖库也变的 xN,隔壁 Go 程序员想了想,不行我们就搞个 .so 共享一部分代码吧,然后看了构建出来的二进制文件才 15MB,运维大手一挥,这点大小有啥要共享的,Java 程序员望了望了自己的 Jar 包,60MB 还行吧,维护镜像仓库的运维同事这个时候跑出来,你的镜像怎么有 150MB 了, 你看看你们把磁盘都塞满了,只能苦笑,运维小哥坑次坑次的给打包机加了一块硬盘,顺便问你马上部署了,你需要多大的配额,你说道 2C4G,运维一脸嫌弃的问你,为什么隔壁 Go 项目组的同事才需要 0.5C512MB。你当然也不用告诉他,SpringBoot 依赖的了 XXX,YYY,ZZZ 的库,虽然一半的功能你都没用到。

部署到线上,刚刚准备喘口气,突然发现新的需求又来了,虽然是一个很小的功能,但是和现在的系统内的任何一个服务都没有什么直接关联性,你提出再新写一个服务,运维主管抱怨道,现在的服务器资源还是很紧张,你尝试着用现在最流行的 Vertx 开发一个简单的 Web 服务,你对构建出来的 jar 只有 10MB 很满意,可是镜像加起来还是有 60 MB,也算一种进步,你找到 QA 主管,准备 Show 一下你用了 Java 社区最酷的框架,最强的性能,QA 主管找了一个台 1C2G 的服务让你压测一下,你发现你怎么也拼不过别人 Go 系统,你研究之后发现,原来协程模型在这样的少核心的情况下性能要更好,你找运维希望能升级下配置,你走到运维门口的时候,你停了下来,醒醒吧,不是你错了,而是时代变了。

云原生压根不是为了 Java 存在的,云原生的时代已经不是 90 年代,那时候的软件是一个技术活,每一个系统都需要精心设计,一个系统数个月才会更新一个版本,每一个功能都需要进行完整的测试,软件也跑在了企业内部的服务器上,软件是IT部分的宝贝,给他最好的环境,而在 9012 年,软件是什么?软件早就爆炸了,IT 从业者已经到达一个峰值,还有源源不断的人输入进来,市场的竞争也变的激烈,软件公司的竞争力也早就不是质量高,而是如何更快的应对市场的变化,Java 就如同一个身披无数荣光的二战将军,你让他去打21世纪的信息战,哪里还跟着上时代。

云原生需要的是,More Fast & More Fast 的交付系统,一个系统开发很快的系统,那天生就和精心设计是违背的,一个精心设计又能很快开发完的系统实在少见,所以我们从 Spring Boot 上直接堆砌业务代码,最多按照 MVC 进行一个简单的分层,那些优秀的 OOP 理念都活在哪里,那些底层框架,而你突然有一天对 Go 来了兴趣,你按照学 juc 的包的姿势,想要学习下 Go 的优雅源码,你发现,天呐,那些底层库原来可以设计的如此简单,Cache 只需要使用简单的 Map 加上一个 Lock 就可以获得很好的性能了,你开始怀疑了,随着你了解的越深入,你发现 Go 这个语言真是充满了各种各样的缺点,但是足够简单这个优势简直让你羡慕到不行,你回想起来,Executors 的用法你学了好几天,看了好多文章,才把自己的姿势学完,你发现 go func(){} 就解决你的需求了,你顺手删掉了 JDK,走上了真香之路。虽然你还会怀念 SpringBoot 的方便,你发现 Go 也足够满足你 80% 的需求了,剩下俩的一点点就捏着鼻子就好了。你老婆也不怪你没时间陪孩子了,你的工资也涨了点,偶尔翻开自己充满设计模式的 Old Style 代码,再也没有什么兴趣了。

原文链接:http://blog.yannxia.top/2019/05/29/fxxk-java-in-cloud-native/

容器云平台基础架构方案设计思考

aoxiang 发表了文章 • 0 个评论 • 286 次浏览 • 2019-05-29 22:02 • 来自相关话题

【编者的话】本文是作者作为一名云计算运维工程师在参与容器云平台建设中的一些有感记录,此次主要针对基础架构技术方案设计方面谈谈自己的一些认知,如有描述不妥之处,还请多包涵并给予指正。 如今,在移动互联的大时代下,互联网公司金融业务的爆炸 ...查看全部
【编者的话】本文是作者作为一名云计算运维工程师在参与容器云平台建设中的一些有感记录,此次主要针对基础架构技术方案设计方面谈谈自己的一些认知,如有描述不妥之处,还请多包涵并给予指正。

如今,在移动互联的大时代下,互联网公司金融业务的爆炸式发展给传统金融行业带来了巨大的冲击和挑战,金融行业也纷纷顺应发展趋势,大力发展移动端业务和互金类型业务,因此对业务需求的响应速度有了更高的要求,越来越多传统应用架构,为了适应不断变化的业务需求,难以预估的访问量,而开始进行分布式改造、微服务改造,实现持续集成、持续部署、支持弹性伸缩、灰度发布、蓝绿部署等能力,容器云平台恰恰可以很好的支撑上述需求。容器技术是近些年来最火热的技术弄潮儿,我们关注他,肯定不止是因为它足够火热,需要任何一项技术,一定都是以更好服务于应用,让用户使用感受越来越美好为目的。

笔者在容器平台的建设过程中,参与了大量的技术讨论和思维碰撞,一个非常大的感触就是,容器云的知识栈非常长,想对平台有一个全局的透彻理解,要学习的东西非常之多,而且很多是跨领域的。作为一名云计算运维工程师,在这里也简单聊聊在平台基础架构设计中自己的一些认知。
#平台选型
在做IaaS时,即使经过了深度的定制化自动化改造,IaaS上的流程走完时也普遍是在交付时把带有应用软件及软件配置的一台虚拟机交到申请者手中,申请者要自己通过IP登录去部署应用,更不用说各应用组件之间的配合设置。而在容器平台里,从代码开发集成,到一个容器镜像里打包了应用和应用的运行时环境,加上容器的配置文件,一套完整流程走下来时,应用已经可以上线了,负载均衡、安全策略都可以具备了,可以说容器云平台是DevOps理论的最佳实现。所以,对于容器平台的建设,从初期就需要各技术团队紧密结合,了解互相的需求,平台相关技术的原理机制,才能共同设计好一个容器平台。如果你想和更多容器技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

而对于传统应用容器化改造,应用接入容器云平台一定是一个循序渐进的过程,更是一个不断演讲的过程。为了让应用尽可能平滑的接入,平台设计也应适当考虑业务原场景和使用习惯,尽可能的避免大幅度修改逻辑,不希望为了容器化而容器化,还是要因地制宜,互相找到一个平衡。虽做了很多技术实践经验的调研,但真正在自己的环境中落地时,大家都是摸着石头过河,要通过试点应用,积累应用改造的经验,运维的经验。

对于容器平台,Docker已经成了容器引擎公认的事实标准,而Kubernetes也是公认的容器编排最佳调度平台,早在一两年前声音还没有如此的一致,记得最初做容器平台技术调研时,还要关注Mesos,Swarm等,但现在无论是商业产品还是纯自研,基本都转向了Kubernetes的阵营,说到这里我来简单谈谈Docker和Kubernetes最吸引人,或者说最值得应用的地方。
##Docker
Docker我认为最有价值的地方是优秀的可移植性,我们日常中最常见的问题就是应用部署的环境差异,操作系统版本差异,生产测试环境不一致等,往往这种差异会导致应用部署调试消耗大量的人力,还容易出现安全隐患,即使很多部署工作已经自动化,但也对基础环境有一定要求,这点比虚拟机有了更友好的移植能力,Docker能够将应用程序和它依赖的操作系统、类库以及运行时环境整体打包,统一交付,真正抹去了应用程序多个运行实例间的环境和依赖版本差异,同时也把对运维人员的重度依赖解耦开来。Docker有一句响亮的口号不是白叫的“Build once,Run anywhere”。

还有一点就是快,秒级启动,虚拟机虽说是分钟级的启动,其实也已经非常快了,但从单体来看,当出现故障重启(Docker是重新生成),这种差别就是非常明显的,容器甚至可以做到外界无感知,尤其对于当需要应用扩容弹新的节点时,这种秒与分钟效率的差别,对应用的使用对象就是天壤之别的使用体验了,这里还不说虚拟机应急扩节点时应用配置的复杂度和配置时长,单是创建和启动时间,可能就是一个外部用户无感知,一个外部用户感觉到慢甚至短时间内服务不可用了。
1.jpg

##Kubernetes
Kubernetes,这个单词来自于希腊语,含义是舵手或领航员,简称K8S。2014年以Google内部用了很久的Brog为原型孵化出来贡献给开源社区的一个自动化部署、伸缩和编排容器的开源平台,其功能强大,Kubernetes实现了IP地址管理、负载均衡、服务发现和DNS名称注册,还原生提供运维人员更为关注的高可用特性及资源智能调度,在我看来Kubernetes对管理基础设施的抽象解耦比虚拟化技术更为彻底、极致,其对CPU兼容性、系统兼容性更为优秀。Kubernetes的自动扩缩容、负载均衡和重新启动应用程序的功能,这些也是使用IaaS或裸机时要二次定制开发的。

这里也要说下,Kubernetes不只支持Docker,它支持符合CRI(containerruntime interface)接口的容器引擎,CoreOS的rkt就是一大代表。

当然构成一套容器云平台,不只是Docker和Kubernetes就够了,他们只是应用运行和调度的最核心的载体,还要有一系列CI\CD、镜像管理、安全、监控、网络、存储等组件,才能构成一个完整的解决方案。
2.jpg

#计算方案
##应该部署在物理机还是虚拟机上?
这是架构设计时一定会讨论的一个问题,从容器云的架构设计来看,我觉得没有绝对的谁更好的答案,物理机或虚拟化平台均可以,前面也说到了,其核心组件K8S将基础设施抽象的更为极致,所以我认为要综合企业自身基础设施建设现状和内部制度流程等综合因素权衡。

如果之前已经有了IaaS平台建设,并且已经有成熟的运维规范和配套工具,那么就部署于IaaS之上,享受IaaS建设的红利。融入了自助服务、内部流程审批、应用软件安装等自动化流程及规范,且IaaS平台有一些对于运维人员爱不释手的功能——热迁移、快照、HA、快速创建虚拟机等,IaaS平台在易管理性和资源弹性上相比物理机还是优势巨大的。

如果没有现成的IaaS建设,那么我认为首选物理机,没有必要再去投入人力去设计IAAS基础设施,K8S原生解耦了基础设施的依赖,提供了智能调度和高可用能力,针对物理机去定制一些满足自身的管理功能和运维的自动化手段也是理想之选,毕竟建设一套适合自身企业需求的IAAS本身也是个巨大的工程,而且少了一层虚拟化,从架构来看更为清晰简洁,troubleshooting时理论故障点也会少些,虚拟化层的性能损耗也不用考虑了。
#网络方案
在容器平台的基础架构层面,网络方案的设计一般都是涉及讨论最多,意见碰撞最多的地方,毕竟网络是底层的基础设施,牵一发动全身,所以定会格外谨慎。underlay、overlay、routing的网络模型都比较了遍,当然这些方案都是要Kubernetes CNI(Container Network Interface)插件标准的,主要关注的有:ipvlan(underlay)、Macvlan(underlay)、Flannel(overlay)、Calico(routing)、NSX-T(routing)。

对于underlay的方案,对于传统的网络架构可以无缝适配,网络的管理模式(IP资源管理,流量管理等)也可以保持一致,但从Kubernetes的管理功能和生态圈来看,underlay的网络方案都不是方向,更多是一种适配传统网络架构的过渡方案,对于ip的管理还是要在外部完成,而且Kubernetes也失去了对于容器的隔离控制能力,此外,例如Macvlan要求启用混杂模式,ipvlan要求linux内核版本要在4.1之上等刚性要求,也不适合绝大多数企业的网络管理规范及操作系统使用现状。对于Overlay的方案,对于传统网络和容器通讯及跨Kubernetes集群的容器通讯需求,该类型方案均存在很大弊端,而且基于vxlan的数据封装传输,对于容器ip的流量监控也不易实现(除非支持解析 vxlan 数据包),对于vxlan的解封包,在性能上也会一定损失,性能表现亦不占优。所以,综合应用的通信需求、运维的管理需求、功能的完善度、技术趋势、性能、安全等多方面的因素,我认为routing的网络模型方案更优,routing模式对传统应用适配,接受传统组织结构都更友好。

接下来我也将routing方案的主要代表介绍下:
##Calico
Calico是综合对比后,各方面功能需求最为满足的开源网络方案,而且其在社区的活跃度非常高,通过调研了解到一些容器平台厂商也开始或已经在产品中集成此方案。

Calico是一个纯三层的数据中心网络方案(不需要Overlay),基于BGP协议通过路由转发的方式实现容器的跨主机通信。Calico 将每个节点(Node)虚拟为一个“路由器”,并为之分配独立的虚拟网段,该路由器为当前节点上的容器提供路由服务,完全利用路由规则实现动态组网,通过BGP协议通告路由,小规模部署可以直接互联,大规模下可通过指定的BGProutereflector来完成。

Calico在每一个计算节点实现了Linux内核级的vRouter来负责数据转发,所以性能优异。

其相较其他开源方案,还有一大优势是安全能力,基于iptables提供了丰富而灵活的network policy,支持很细致的ACL控制。

Calico主要由Felix、etcd、BGP client以及BGPRoute Reflector组成:

  1. Felix,Calico Agent,跑在每台需要运行Workload的节点上,主要负责为容器配置IP,路由及ACLs(iptable规则)等信息来确保Endpoint的连通状态;
  2. etcd,分布式键值存储,主要负责网络元数据一致性,确保Calico网络状态的准确性;
  3. BGP Client(BIRD),主要负责把Felix写入Kernel的路由信息分发(广播)到当前Calico网络( 通过BGP协议),确保Workload间的通信的有效性;
  4. BGP Route Reflector(BIRD),大规模集群的分级路由分发,摒弃所有节点互联的Mesh模式,通过一个或者多个BGP Route Reflector来完成集中式的路由分发。

3.jpg

#NSX-T
NSX-T是VMware的企业级网络虚拟化商业解决方案,是2016年5月在NSX-V/NSX-MH之后推出的新产品,到现在是2.3版本,T版本主要是加入容器网络支持和兼容裸金属物理机这两个重要功能,未来也会逐步将NSX-V/NSX-MH的代码整合完成替代。同Calico一样,也是内核级的数据转发,性能优异,而且产品自身提供了良好的负载均衡及安全能力。对于商业软件,企业级的服务支持和可视化的管理界面是商业方案优势较为明显的地方。

NSX-T我认为最显著的特点是,与传统网络有个南北向清晰地边界——Edge节点,而东西向全部在自己的自制范围内。

这里还有两个我认为比较重要的点应该了解一下,NSX-T与K8S的适配方式,是通过NSX-T Container Plug-in(NCP)插件与其集成,包括其他的容器平台解决方案也是通过此插件进行集成,例如Pivotal Cloud Foundry等。

还有一点是从NSX-T开始,VMware已经从NSX-V使用的基于vxlan的封装转移,并采用了更新的“Geneve”封装。Geneve是由VMware,Microsoft,Red Hat和Intel共同撰写的新版封装。Geneve将当前最佳的封装协议(如VXLAN,STT和NVGRE)整合到一个协议中。封装头的MTU为1600,所以对NSX-T自治域内的MTU值要求大于1600。
4.jpg

#存储方案
存储也是基础架构的重要一环,尤其是对于有状态的应用,数据持久化需求的支持。

我觉得在Kubernetes里,对于基础资源(CPU、内存、网络、存储)的管理,存储使用的设计较为别致,使用方式有别于常见的思路,还需要认真去理解。

这里简单介绍下在K8S中存储设计的四个重要概念:Volume、PV(PersistentVolume)、PVC(PersistentVolumeClaim)、Storage Class。
##Volume
Volume是最基础的存储抽象,其支持多种类型,包括本地存储、NFS、FC以及众多的云存储,也可以通过flex volume driver或CSI(contaioner storage Interface)编写自己的存储插件来支持特定的存储系统。

Volume可以被Pod直接使用,也可以被PV使用。普通的Volume和Pod之间是一种静态的绑定关系,在定义Pod的同时,通过volume属性来定义存储的类型,通过volumeMount来定义容器内的挂载点。

Volume有一个重要属性是,它与所属的Pod具有相同的生命周期。

Pod是Kubernetes的最小工作单元,每个 Pod 包含一个或多个容器
##PV
PV与普通的Volume不同,它是Kubernetes中的一个资源对象,创建一个PV相当于创建了一个存储资源对象,向用户屏蔽了具体的存储实现形式,而且这个资源的使用要通过PVC来请求。PV定义的内容包含存储的类型,存储的大小和访问模式等。

PV的生命周期独立于Pod,即当使用它的Pod销毁时对PV没有影响。
##PVC
PVC是用户对存储资源PV的请求,请求信息包含存储大小,访问模式等。根据PVC中指定的条件Kubernetes寻找系统中的PV资源并进行绑定。PVC消耗PV资源,PV和PVC是一一对应的。
##StorageClass
StorageClass的引入是为了解决个性化的存储需求动态供给的问题,集群管理员可以先将存储资源定义为不同类型的资源,比如高性能存储、常规存储等,之后通过StorageClass的定义动态的去创建PV,然后通过PVC来请求PV绑定。

对于多数银行业的企业,都有丰富的SAN和NAS的存储管理及运维经验,结合应用的存储需求、平台镜像方案的设计,以及银行业的应用系统普遍有多中心部署的监管需求,我认为采用NFS类型的存储支持容器数据持久化以及镜像服务的相关存储需求对容器平台在银行业的落地不失为一个不错的选择,此外还应同时开展对象存储的研究与测试,以给应用对数据存储的使用方式更多选择。

容器云平台建设会是一个不断完善、迭代积累的过程,同时容器相关技术的发展变化非常之快,在平台的建设中要持续保持新技术的敏锐嗅觉,来提升应用服务、运维管理、安全监管等各方面的水平。

原文链接:https://mp.weixin.qq.com/s/O6Za5JGywmnZ-Rswlu5WzA

k8s搭建zookeeper 失败,

回复

lincoln_alex 回复了问题 • 1 人关注 • 1 个回复 • 668 次浏览 • 2019-04-22 11:02 • 来自相关话题

k8s 中在自定义的 namespace 中创建 ingress 后不能访问

回复

tangjiaxing669 发起了问题 • 1 人关注 • 0 个回复 • 538 次浏览 • 2019-03-31 03:18 • 来自相关话题

为什么Kubernetes Service的负载均衡会有模拟丢包率的IPtables条目?

回复

徐新坤 回复了问题 • 3 人关注 • 3 个回复 • 3446 次浏览 • 2019-03-26 09:29 • 来自相关话题

怎样在Kubernetes中实现CI/CD的发布流程,大家都贡献出来一下吧!

回复

Twilight 回复了问题 • 2 人关注 • 1 个回复 • 893 次浏览 • 2019-03-25 11:50 • 来自相关话题

kubernetes集群加入两个新节点后部署pod报错

回复

qianboe92 回复了问题 • 1 人关注 • 1 个回复 • 1300 次浏览 • 2019-01-18 13:59 • 来自相关话题

kubernetes部署InitializerConfiguration之后java无法调用集群api

回复

qianboe92 发起了问题 • 1 人关注 • 0 个回复 • 966 次浏览 • 2018-12-27 15:17 • 来自相关话题

kubernetes 的dashboard无法进入容器

回复

qianboe92 发起了问题 • 1 人关注 • 0 个回复 • 1593 次浏览 • 2018-12-20 14:22 • 来自相关话题

kubernetes执行kubectl exec命令部分容器无法进入

回复

Felixw 回复了问题 • 2 人关注 • 1 个回复 • 2324 次浏览 • 2018-12-20 08:55 • 来自相关话题

k8s部署一个叫做test-nginx的Deployment,但是报错说叫做test-nginx的Deployment已存在

回复

qianboe92 回复了问题 • 2 人关注 • 2 个回复 • 1810 次浏览 • 2018-12-19 17:49 • 来自相关话题

kubernetes离线一键安装教程

回复

icyboy 发起了问题 • 1 人关注 • 0 个回复 • 2165 次浏览 • 2018-12-18 13:24 • 来自相关话题

Shell-operator:用于简化Kubernetes operator的创建

dummy 发表了文章 • 0 个评论 • 139 次浏览 • 2019-06-18 17:24 • 来自相关话题

我们很高兴在此介绍我们新的开源方案,它能将Kubernetes中Operator的开发提升到一个全新的更简单的水平。它可以让你在15分钟内将你的小脚本变成完全成熟的Operator而吸引你。欢迎 ...查看全部

我们很高兴在此介绍我们新的开源方案,它能将Kubernetes中Operator的开发提升到一个全新的更简单的水平。它可以让你在15分钟内将你的小脚本变成完全成熟的Operator而吸引你。欢迎shell-operator




目标


shell-operator的思路很简单,它订阅来自Kubernetes对象的事件,并在事件发生后执行外部程序,为其提供有关事件的信息:




在我们运行Kubernetes集群期间,很多小任务开始显现。在Flant,我们迫切想要以正确的方式来自动化它们,因此我们觉得需要更智能的解决方案。通常你可以使用基本的bash脚本来解决所有这些任务,但是如你所知,更推荐的方式是使用Golang来编写Operators。很显然,为每个小任务分别开发成熟的Operator将会很低效。


15分钟内创建一个Operator


我们将举一个在Kubernetes集群中可以被自动化的例子,以及shell-operator可以如何帮助我们。我们将尝试复制用于访问Docker仓库的凭证。


使用私有仓库镜像的Pod应在其清单中包含指定的用于访问仓库的secret。这个secret必须在创建Pod之前先创建在每个命名空间中。你可以手动执行此操作,但是,如果我们将配置动态的多个环境,我们将为单个应用程序创建许多命名空间。在多个应用程序(甚至两个或三个)的情况下,secret的数量会变得巨大。关于secret还有一个需求:我们希望能够偶尔更改(注册表)仓库的访问密钥。因此,手动解决方案变得非常低效,你必须自动创建和更新secret。


简单的自动化


我们来写一个脚本,每N秒运行一次,并检查命名空间中secret是否存在。如果secret不存在,那么它将会被创建。这个解决方案的优势是它看起来就像是cron中的一个shell脚本,一种经典且易于理解的方法。缺点是在此脚本的两次启动之间的间隔期间可能会出现一些新的命名空间,因此在一段时间内它将不会持有这个secret。这种情况会导致启动Pod的过程中出错。


使用shell-operator进行自动化


为了使我们的脚本准确运行,经典的cron执行应该被当有新增命名空间事件发生时的执行所取代。在这种情况下,你可以在使用之前创建一个secret。让我们看看如何使用shell-operator来实现这个功能。


首先,我们先分析一下脚本,就shell-operator而言,脚本都被称之为“钩子“。每个钩子在使用--config标志执行时都会通知shell-operator将其绑定(即需要执行哪些事件)。在我们的例子中,我们将使用onKubernetesEvent


#!/bin/bash
if [[ $1 == "--config" ]] ; then
cat <

在这里,我们定义我们关注的namespace类型的添加(add)对象事件。


现在我们需要添加当事件发生时需要执行的代码:


#!/bin/bash
if [[ $1 == "--config" ]] ; then
# configuration
cat <

真棒!我们现在已有一个简洁且漂亮的脚本,想让它能真正发挥作用,我们需要准备一个镜像并将其跑在集群中。


使用钩子制作我们的镜像


你可以很轻易观察到我们在脚本里面使用了kubectljq命令。这意味着镜像中需要包含钩子,shell-operator二进制文件(它将监视事件并执行这个钩子),以及钩子需要用到的命令(kubectljq)。hub.docker.com上已提供了包含shell-operator,kubectl和jq的即用型镜像。现在是时候使用Dockerfile来添加一个钩子:


$ cat Dockerfile
FROM flant/shell-operator:v1.0.0-beta.1-alpine3.9
ADD namespace-hook.sh /hooks
$ docker build -t registry.example.com/my-operator:v1 .
$ docker push registry.example.com/my-operator:v1

在集群中运行


我们再来看看这个钩子,这次我们将关注具体的操作以及它在集群中执行的对象:



  1. 它订阅了namespace的创建事件;

  2. 它在不与它所运行的命名空间相同的空间创建一个secret。


这里我们会发现运行这个镜像的Pod需要有执行这些操作的权限。你可以授权给一个ServiceAccount。由于我们是关注整个集群中的对象,那么权限需要使用ClusterRoleClusterRoleBinding形式来配置。


YAML最终配置描述如下:


---
apiVersion: v1
kind: ServiceAccount
metadata:
name: monitor-namespaces-acc
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
name: monitor-namespaces
rules:
- apiGroups: [""]
resources: ["namespaces"]
verbs: ["get", "watch", "list"]
- apiGroups: [""]
resources: ["secrets"]
verbs: ["get", "list", "create", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
name: monitor-namespaces
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: monitor-namespaces
subjects:
- kind: ServiceAccount
name: monitor-namespaces-acc
namespace: example-monitor-namespaces

你可以将创建的镜像部署为一个简单的Deployment:


apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: my-operator
spec:
template:
spec:
containers:
- name: my-operator
image: registry.example.com/my-operator:v1
serviceAccountName: monitor-namespaces-acc

为方便起见,我们将创建一个单独的命名空间,用于运行shell-operator并应用创建的部署清单:


$ kubectl create ns example-monitor-namespaces
$ kubectl -n example-monitor-namespaces apply -f rbac.yaml
$ kubectl -n example-monitor-namespaces apply -f deployment.yaml

好了,shell-operator启动,它将订阅命名空间创建事件并在需要时执行钩子。



这样一个简单的shell脚本就变成了Kubernetes中一个真正的Operator,并成为集群的一部分。这样做的好处是我们避免了使用Golang来开发Operator的复杂过程:




过滤


关于对象的观察很棒,但我们通常需要响应对象中某些属性的更改,例如,增加/减少部署中的副本数量或对象对标签中的任何更新。


当一个事件发生时,shell-operator接收该对象的JSON清单。在此JSON中,你可以选择要监视的属性,并仅在更改时启动钩子。jqFilter字段可以帮助你完成这点:你应该输入将应用于JSON清单的jq表达式。


举个例子,要响应Deployment对象标签中的修改,你必须从metadata字段中提取labels字段。这个例子中你将需要如下的配置:


cat <

jqFilter表达式将Deployment的长长的JSON清单转换成带有标签的简短的JSON:




shell-operator将只会在这个简短的JSON发生变化时执行钩子。其它属性的变更将会被忽略。


钩子的执行上下文


钩子的配置允许你指定几种事件。例如你可以定义两个Kubernetes事件和两个计划调度:


{
"onKubernetesEvent": [
{
"name": "OnCreatePod",
"kind": "pod",
"event": [
"add"
]
},
{
"name": "OnModifiedNamespace",
"kind": "namespace",
"event": [
"update"
],
"jqFilter": ".metadata.labels"
}
],
"schedule": [
{
"name": "every 10 min",
"crontab": "0 */10 * * * *"
},
{
"name": "on Mondays at 12:10",
"crontab": "0 10 12 * * 1"
}
]
}

注意:shell-operator支持以crontab样式运行脚本!你可以在文档中找到额外的信息。


为了区分钩子执行的原因,shell-operator会创建一个临时文件并将其路径保存到BINDING_CONTEXT_TYPE变量中。此文件包含了执行钩子的原因的JSON描述。例如,每隔10分钟将会使用以下内容启动钩子:


[{ "binding": "every 10 min" }]

在周一的话它将以以下内容启动:


[{ "binding": "every 10 min" }, { "binding": "on Mondays at 12:10" }]

同时将有onKubernetesEvent调用的更详细的JSON,因为它包含了对象的描述:


[
{
"binding": "onCreatePod",
"resourceEvent": "add",
"resourceKind": "pod",
"resourceName": "foo",
"resourceNamespace": "bar"
}
]

你能通过名称来全面了解字段的内容(更多详细信息可在文档中找到)。使用jq从resourceName获取资源名称的示例已经在复制secret的钩子中展示:


jq -r '.[0].resourceName' $BINDING_CONTEXT_PATH

你可以通过类似的方式去获取到其它字段。


下一步是什么呢?


在该项目仓库中的/examples directory目录里包含了一些可以直接在集群中使用的示例。你可以将它们用作你开发自己的钩子的基础。


Shell-operator同样支持使用Prometheus来收集指标。METRICS章节已描述了这些可用的指标。


你能轻易想到,shell-operator是使用Go编写的,并根据开源许可证(Apache 2.0)的条款进行分发。我们非常感谢任何关于开发在Github上的这个项目的帮助。你可以通过给我们点Star,反馈问题或者是PR来支持我们!


原文链接:Announcing shell-operator to simplify creating of Kubernetes operators(翻译:冯旭松)


Go版微服务开发框架Micro及标准2019年大整合

cleverlzc 发表了文章 • 0 个评论 • 300 次浏览 • 2019-06-16 08:38 • 来自相关话题

【编者的话】Micro是构建微服务的最简单方式,并逐渐成为云计算中基于Go的微服务开发的实际标准。通过将我们的努力整合到一个开发框架和运行时中,我们使这个过程更加简单。 Micro作为[go-micro](https://gith ...查看全部

【编者的话】Micro是构建微服务的最简单方式,并逐渐成为云计算中基于Go的微服务开发的实际标准。通过将我们的努力整合到一个开发框架和运行时中,我们使这个过程更加简单。 

Micro作为[go-micro](https://github.com/micro/go-micro)——一个微服务框架开始了它的旅程,专注于提供微服务开发的核心需求。它通过抽象出分布式系统的复杂性,为构建微服务创造了更简单的体验。

随着时间的推移,我们已经从go-micro扩展到其他工具、库和插件。这导致了我们解决问题的方式和开发人员使用微服务工具的方式的分散化。我们现在正在整合所有这些工具,以简化开发人员的体验。

从本质上来说,Micro已经成为一个独立的开发框架和微服务开发的运行时。

在讨论整合之前,让我们回顾一下迄今为止的历程。

### 主要关注点

Go-micro最初主要专注于微服务的通信方面,我们一直努力做到这一点。到目前为止,这种固执己见的方法和关注点是驱动框架成功的真正驱动力。多年来,我们已经收到了无数的请求,要求解决第二天在go-micro中构建生产就绪软件的问题。其中大部分都与可伸缩性、安全性、同步和配置有关。


虽然增加所要求的额外特性是有好处的,但我们确实希望一开始就非常专注于很好地解决一个问题。所以我们采取了一种不同的方式来促进社区这样做。

### 生态系统和插件

投入生产所涉及的不仅仅是普通的服务发现、消息编码和请求-响应。我们真正明白这一点,希望使用户能够通过可插拔和可扩展的接口选择更广泛的平台需求。通过[资源管理器](https://micro.mu/explore/)促进生态系统,资源管理器聚合了GitHub上的基于微服务的开源项目,并通过[go-plugins](https://github.com/micro/go-plugins)扩展插件。


一般来说,Go插件已经取得了巨大的成功,因为它允许开发人员将大量的复杂性转移到为这些需求构建的系统上。例如用于度量的Prometheus、用于分布式跟踪的Zipkin和用于持久消息传递的Kafka。

### 交互点

Go Micro确实是微服务开发的核心,但是随着服务的编写,接下来的问题就转移到了:我如何查询它们,如何与它们交互,如何通过传统方式为它们服务。

鉴于go-micro使用了一个基于RPC/Protobuf的协议,该协议既可插拔又不依赖于运行时,我们需要某种方法来解决这个问题。这导致了微服务工具包[Micro](https://github.com/micro/micro)的产生。Micro提供了API网关、网络仪表板、cli命令行工具、slack bot机器人程序、服务代理等等。


Micro工具包通过http api、浏览器、slack命令和命令行接口充当交互点。这些是我们查询和构建应用程序的常见方式,对于我们来说,提供一个真正支持这一点的运行时非常重要。然而,但它仍然把重点放在通信上。

###其他工具

虽然插件和工具包极大地帮助了使用了Micro的用户,但在关键领域仍然缺乏。很明显,我们的社区希望我们能够围绕产品开发的平台工具来解决更多的问题,而不是必须在他们各自的公司中单独完成。我们需要为动态配置、分布式同步和为Kubernetes这样的系统提供更广泛的解决方案等方面提供相同类型的抽象。

于是我们创建了以下项目:


- [micro/go-config](https://github.com/micro/go-config): 一个动态配置库

- [micro/go-sync](https://github.com/asim/go-sync):一个分布式同步库

- [micro/kubernetes](https://github.com/micro/kubernetes):在Kubernetes平台上的初始化

- [examples](https://github.com/micro/examples):使用举例

- [microhq](https://github.com/microhq):微服务预构建


这些是一部分repos、库和工具,用于尝试解决我们社区更广泛的需求。在过去的四年里,repos的数量不断增长,新用户的入门体验也变得更加困难。进入壁垒急剧增加,我们意识到需要做出一些改变。

在过去的几周里,我们意识到[go-micro](https://github.com/micro/go-micro)确实是大多数用户开发微服务的焦点。很明显,他们想要额外的功能,作为这个库的一部分以及一个自我描述的框架,我们真的需要通过解决那些第二天的问题来实现这一点,而不要求开发人员寻求其他方法。

本质上,go-micro将成为微服务开发的全面和独立框架。

我们通过将所有库迁移到go-micro开始了整合过程,在接下来的几周里,我们将继续进行重构,以提供更简单的默认入门体验,同时还为日志记录、跟踪、度量、身份验证等添加更多功能。


不过,我们也没有忘记Micro。在我们看来,当构建了微服务之后,仍然需要一种查询、运行和管理它们的方法。所有人都认为Micro将是微服务开发的运行时。我们正在致力于提供一种更简单的方法来管理微服务开发的端到端流程,并且应该很快会有更多消息发布。

### 总结

Micro是构建微服务的最简单方式,并逐渐成为云计算中基于Go的微服务开发的实际标准。通过将我们的努力整合到一个开发框架和运行时中,我们使这个过程更加简单。

原文链接:Micro - The great consolidation of 2019


**译者**:Mr.lzc,软件工程师、DevOpsDays深圳核心组织者,目前供职于华为,从事云存储工作,以Cloud Native方式构建云文件系统服务,专注于Kubernetes、微服务领域。


睿云智合 | 开源项目Breeze成为Kubernetes认证安装工具

wise2c 发表了文章 • 0 个评论 • 388 次浏览 • 2019-06-14 11:46 • 来自相关话题

近日,睿云智合开源的Kubernetes图形化部署工具Breeze项目通过Kubernetes一致性认证成为认证Kubernetes安装工具。目前Breeze已经列入 Cloud Native Landscape 生态图谱的认证安装工具板块。 ...查看全部

近日,睿云智合开源的Kubernetes图形化部署工具Breeze项目通过Kubernetes一致性认证成为认证Kubernetes安装工具。目前Breeze已经列入 Cloud Native Landscape 生态图谱的认证安装工具板块。


Beeze项目是睿云智合(Wise2C)2018年5月所开源的Kubernetes图形化部署工具,能大大简化Kubernetes部署的步骤,支持全离线环境的部署,而且可以不需要翻墙获取Google的相应资源包,尤其适合中国金融、运营商、国有企业等不便访问互联网的私有数据中心部署环境场景。目前支持最新版Kubernetes、Etcd、Docker、Harbor以及Prometheus等云原生工具部署,同时支持Istio部署 (内置Kiali, Jaeger, Prometheus, Grafana)。推出至今获得开源技术社区用户的一致好评目前在Github上已获600余星。



功能亮点:

易于运行:

Breeze将您需要的所有资源(比如Kubernetes组件镜像以及用于部署Kubernetes集群的 ansible playbooks)组合为一个Docker镜像(wise2c/playbook)。它还可以作为本地yum存储库服务器工作。您只需要安装了Docker引擎的Linux服务器来运行Breeze。



简化了kubernetes集群部署的过程:


通过几个简单的命令,您可以轻而易举地运行,然后通过图形界面完成所有其他部署过程。



支持离线部署:

在部署服务器上加载了4个镜像(playbook、yum repo、pagoda、deploy ui)后,就可以在不接入互联网的情况下设置kubernetes集群。Breeze作为一个yum存储库服务器工作,部署一个本地的Harbor镜像仓库,并使用kubeadm来设置kubernetes集群。所有docker镜像将从本地镜像仓库提取。



支持多集群:

Breeze支持多个Kubernetes集群部署。

支持高可用架构:

通过Breeze,您可以设置Kubernetes集群,其中3台主服务器和3台ETCD服务器与Haproxy和Keepalived相结合。所有工作节点将使用虚拟浮动IP地址与主服务器通信。



Breeze项目是100%开源的云原生部署工具


GitHub项目地址:

https://github.com/wise2c-devops/breeze

安装过程演示视频在此:

 https://pan.baidu.com/s/1X0ZYt48wfYNrSrH7vvEKiw

常见排错说明在此:

https://github.com/wise2c-devops/breeze/blob/master/TroubleShooting-CN.md

PDF手册请在此处下载:

https://github.com/wise2c-devops/breeze/raw/master/manual/BreezeManual-CN.pdf

DockOne微信分享(二一一):基于Actor模型的CQRS/ES解决方案分享

JetLee 发表了文章 • 0 个评论 • 442 次浏览 • 2019-06-05 16:17 • 来自相关话题

【编者的话】2017年加密货币比较流行,我曾有幸在加密货币交易所参与开发工作,为了应对交易系统高性能、高并发、高可用的要求,我们使用基于Actor模型的Orleans技术,采用CQRS/ES架构结合Service Fabric开展了富有挑战性的工作。本文 ...查看全部

【编者的话】2017年加密货币比较流行,我曾有幸在加密货币交易所参与开发工作,为了应对交易系统高性能、高并发、高可用的要求,我们使用基于Actor模型的Orleans技术,采用CQRS/ES架构结合Service Fabric开展了富有挑战性的工作。本文将从不同视角为大家介绍Actor模型、CQRS/ES架构以及Service Fabric在高并发场景中的考量和应用。



话题由三部分组成:




  • Actor模型&Orleans:在编程的层面,从细粒度-由下向上的角度介绍Actor模型;

  • CQRS/ES:在框架的层面,从粗粒度-由上向下的角度介绍Actor模型,说明Orleans技术在架构方面的价值;

  • Service Fabric:从架构部署的角度将上述方案落地上线。



群里的小伙伴技术栈可能多是Java和Go体系,分享的话题主要是C#技术栈,没有语言纷争,彼此相互学习。比如:Scala中,Actor模型框架有akka,CQRS/ES模式与编程语言无关,Service Fabric与Kubernetes是同类平台,可以相互替代,我自己也在学习Kubernetes。





Actor模型&Orleans(细粒度)





共享内存模型



多核处理器出现后,大家常用的并发编程模型是共享内存模型。


这种编程模型的使用带来了许多痛点,比如:




  • 编程:多线程、锁、并发集合、异步、设计模式(队列、约定顺序、权重)、编译

  • 无力:单系统的无力性:①地理分布型、②容错型

  • 性能:锁,性能会降低

  • 测试:



    • 从坑里爬出来不难,难的是我们不知道自己是不是在坑里(开发调试的时候没有热点可能是正常的)

    • 遇到bug难以重现。有些问题特别是系统规模大了,可能运行几个月才能重现问题

  • 维护:



    • 我们要保证所有对象的同步都是正确的、顺序的获取多个锁。

    • 12个月后换了另外10个程序员仍然按照这个规则维护代码。



简单总结:




  • 并发问题确实存在

  • 共享内存模型正确使用掌握的知识量多

  • 加锁效率就低

  • 存在许多不确定性





Actor模型



Actor模型是一个概念模型,用于处理并发计算。Actor由3部分组成:状态(State)+行为(Behavior)+邮箱(Mailbox),State是指Actor对象的变量信息,存在于Actor之中,Actor之间不共享内存数据,Actor只会在接收到消息后,调用自己的方法改变自己的state,从而避免并发条件下的死锁等问题;Behavior是指Actor的计算行为逻辑;邮箱建立Actor之间的联系,一个Actor发送消息后,接收消息的Actor将消息放入邮箱中等待处理,邮箱内部通过队列实现,消息传递通过异步方式进行。


Actor是分布式存在的内存状态及单线程计算单元,一个Id对应的Actor只会在集群种存在一个(有状态的 Actor在集群中一个Id只会存在一个实例,无状态的可配置为根据流量存在多个),使用者只需要通过Id就能随时访问不需要关注该Actor在集群的什么位置。单线程计算单元保证了消息的顺序到达,不存在Actor内部状态竞用问题。



Actor是分布式存在的内存状态及单线程计算单元,一个ID对应的Actor只会在集群种存在一个(有状态的 Actor在集群中一个ID只会存在一个实例,无状态的可配置为根据流量存在多个),使用者只需要通过ID就能随时访问不需要关注该Actor在集群的什么位置。单线程计算单元保证了消息的顺序到达,不存在Actor内部状态竞用问题。



举个例子:



多个玩家合作在打Boss,每个玩家都是一个单独的线程,但是Boss的血量需要在多个玩家之间同步。同时这个Boss在多个服务器中都存在,因此每个服务器都有多个玩家会同时打这个服务器里面的Boss。



如果多线程并发请求,默认情况下它只会并发处理。这种情况下可能造成数据冲突。但是Actor是单线程模型,意味着即使多线程来通过Actor ID调用同一个Actor,任何函数调用都是只允许一个线程进行操作。并且同时只能有一个线程在使用一个Actor实例。





Actor模型:Orleans



Actor模型这么好,怎么实现?



可以通过特定的Actor工具或直接使用编程语言实现Actor模型,Erlang语言含有Actor元素,Scala可以通过Akka框架实现Actor编程。C#语言中有两类比较流行,Akka.NET框架和Orleans框架。这次分享内容使用了Orleans框架。



特点:



Erlang和Akka的Actor平台仍然使开发人员负担许多分布式系统的复杂性:关键的挑战是开发管理Actor生命周期的代码,处理分布式竞争、处理故障和恢复Actor以及分布式资源管理等等都很复杂。Orleans简化了许多复杂性。



优点:




  • 降低开发、测试、维护的难度

  • 特殊场景下锁依旧会用到,但频率大大降低,业务代码里甚至不会用到锁

  • 关注并发时,只需要关注多个actor之间的消息流

  • 方便测试

  • 容错

  • 分布式内存



缺点:




  • 也会出现死锁(调用顺序原因)

  • 多个Actor不共享状态,通过消息传递,每次调用都是一次网络请求,不太适合实施细粒度的并行

  • 编程思维需要转变






第一小节总结:上面内容由下往上,从代码层面细粒度层面表达了采用Actor模型的好处或原因。





CQRS/ES(架构层面)





从1000万用户并发修改用户资料的假设场景开始







  1. 每次修改操作耗时200ms,每秒5个操作

  2. MySQL连接数在5K,分10个库

  3. 5 *5k *10=25万TPS

  4. 1000万/25万=40s

 

在秒杀场景中,由于对乐观锁/悲观锁的使用,推测系统响应时间更复杂。





使用Actor解决高并发的性能问题





1000万用户,一个用户一个Actor,1000万个内存对象。


200万件SKU,一件SKU一个Actor,200万个内存对象。




  • 平均一个SKU承担1000万/200万=5个请求

  • 1000万对数据库的读写压力变成了200万

  • 1000万的读写是同步的,200万的数据库压力是异步的

  • 异步落盘时可以采用批量操作



总结:



由于1000万+用户的请求根据购物意愿分散到200万个商品SKU上:每个内存领域对象都强制串行执行用户请求,避免了竞争争抢;内存领域对象上扣库存操作处理时间极快,基本没可能出现请求阻塞情况。



从架构层面彻底解决高并发争抢的性能问题。理论模型,TPS>100万+……





EventSourcing:内存对象高可用保障



Actor是分布式存在的内存状态及单线程计算单元,采用EventSourcing只记录状态变化引发的事件,事件落盘时只有Add操作,上述设计中很依赖Actor中State,事件溯源提高性能的同时,可以用来保证内存数据的高可用。






CQRS



上面1000万并发场景的内容来自网友分享的PPT,与我们实际项目思路一致,就拿来与大家分享这个过程,下图是我们交易所项目中的架构图:


开源版本架构图:


开源项目github:https://github.com/RayTale/Ray



第二小节总结:由上往下,架构层面粗粒度层面表达了采用Actor模型的好处或原因。





Service Fabric



系统开发完成后Actor要组成集群,系统在集群中部署,实现高性能、高可用、可伸缩的要求。部署阶段可以选择Service Fabric或者Kubernetes,目的是降低分布式系统部署、管理的难度,同时满足弹性伸缩。



交易所项目可以采用Service Fabric部署,也可以采用Kubernetes,当时Kubernetes还没这么流行,我们采用了Service Fabric,Service Fabric 是一款微软开源的分布式系统平台,可方便用户轻松打包、部署和管理可缩放的可靠微服务和容器。开发人员和管理员不需解决复杂的基础结构问题,只需专注于实现苛刻的任务关键型工作负荷,即那些可缩放、可靠且易于管理的工作负荷。支持Windows与Linux部署,Windows上的部署文档齐全,但在Linux上官方资料没有。现在推荐Kubernetes。



第三小节总结:




  1. 借助Service Fabric或Kubernetes实现低成本运维、构建集群的目的。

  2. 建立分布式系统的两种最佳实践:



    • 进程级别:容器+运维工具(Kubernetes/Service Fabric)

    • 线程级别:Actor+运维工具(Kubernetes/Service Fabric)

参考:




  1. ES/CQRS部分内容参考:《领域模型 + 内存计算 + 微服务的协奏曲:乾坤(演讲稿)》2017年互联网应用架构实战峰会

  2. 其他细节来自互联网,不一一列出





Q&A



Q:单点故障后,正在处理的cache数据如何处理的,例如,http、tcp请求……毕竟涉及到钱?

A:Actor有激活和失活的生命周期,激活的时候使用快照和Events来恢复最新内存状态,失活的时候保存快照。Actor框架保证系统中同一个key只会存在同一个Actor,当单点故障后,Actor会在其它节点重建并恢复最新状态。



Q:数据落地得策略是什么?还是说就是直接落地?

A:event数据直接落地;用于支持查询的数据,是Handler消费event后异步落库。



Q:Grain Persistence使用Relational Storage容量和速度会不会是瓶颈?

A:Grain Persistence存的是Grain的快照和event,event是只增的,速度没有出现瓶颈,而且开源版本测试中PostgreSQL性能优于MongoDB,在存储中针对这两个方面做了优化:比如分表、归档处理、快照处理、批量处理。



Q:Orleans中,持久化事件时,是否有支持并发冲突的检测,是如何实现的?

A:Orleans不支持;工作中,在事件持久化时做了这方面的工作,方式是根据版本号。



以上内容根据2019年6月4日晚微信群分享内容整理。分享人郑承良,上海某科技公司架构师,对高并发场景下的分布式金融系统拥有丰富的实战经验,曾为澳大利亚、迪拜多家交易所提供技术支持。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiese,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

Kubernetes IN Docker - local clusters for testing Kubernetes

老马 发表了文章 • 0 个评论 • 289 次浏览 • 2019-06-04 21:52 • 来自相关话题

Brief Kind(Kubernetes IN Docker是一个用来快速创建 ...查看全部

Brief



Kind(Kubernetes IN Docker是一个用来快速创建和测试kubernetes的工具,Kind把环境的依赖降低到了最小,仅需要机器安装了Docker即可。



Kind 可以做什么?




  • 快速创建一个或多个Kubernetes集群(几分钟)

  • 支持HA Master部署高可用的Kubernetes集群

  • 支持从源码构建并部署一个Kubernetes集群

  • 可以快速低成本体验一个最新的Kubernetes集群,并支持Kubernetes的绝大部分功能

  • 支持本地离线运行一个多节点集群



Kind 有哪些优势?




  • 最小的安装依赖,仅需要安装Docker即可

  • 使用快速简单,使用kind cli工具即可快速创建集群

  • 使用container来 mockkubernetes node

  • 内部使用kubeadm的官方主流部署工具

  • 使用了containerd

  • 通过了CNCF官方的k8s conformance测试





Usage





GO111MODULE="on" go get sigs.k8s.io/kind@v0.3.0 && kind create cluster




How it work



Kind 使用一个 container 来模拟一个 Node,在 Container 里面跑了 systemd ,并用 systemd 托管了 kubelet 以及 containerd,然后容器内部的 kubelet 把其他 Kubernetes 组件,比如 kube-apiserver、etcd、CNI 等组件跑起来。



可以通过配置文件的方式,来通过创建多个 container 的方式,来模拟创建多个 Node,并以这些 Node 来构建一个多节点的 Kubernetes 集群。



Kind 内部使用了 kubeadm 这个工具来做集群的部署,包括 ha master 的高可用集群,也是借助 kubeadm 提供的aplha特性提供的。同时,在 HA Master 下,额外部署了一个 Nginx 用来提供负载均衡 vip。





Build Images



Kind 的镜像分为两个,一个 Node 镜像,一个 Base 镜像。



Node 镜像



Node 镜像的构建比较复杂,目前是通过运行 Base 镜像,并在 Base 镜像内执行操作,再保存此容器内容为镜像的方式来构建的,包含的操作有:




  • 构建 Kubernetes 相关资源(比如二进制文件和镜像)

  • 运行一个用于构建的容器

  • 把构建的 Kubernetes 相关资源复制到容器里

  • 调整部分组件配置参数,以支持在容器内运行

  • 预先拉去运行环境需要的镜像

  • 通过 docker commit 方式保存当前的构建容器为 node 镜像



具体的逻辑,可以参考node.go



Base 镜像



Base 镜像目前使用了 Ubuntu 19.04 作为基础镜像,做了下面的调整:




  • 安装 systemd 相关的包,并调整一些配置以适应在容器内运行

  • 安装 Kubernetes 运行时的依赖包,比如 conntrack、socat、CNI

  • 安装容器



运行环境,比如 containerd、crictl




  • 配置自己的 ENTRYPOINT 脚本,以适应和调整容器内运行的问题



具体的逻辑,可以参考构建的Dockerfile





Create Cluster



Kind 创建集群的基本过程为:




  1. 根据传入的参数,来创建 container,分为 control node 和 worker node 两种(如果是 ha master,还有一个 loadbalancer node)

  2. 如果需要,配置 loadbalancer 的配置,主要是 Nginx 配置文件

  3. 生成 kubeadm 配置

  4. 对于第一个控制节点,使用 kubeadm init 初始化单节点集群

  5. 配置安装 CNI 插件

  6. 配置存储(实际是安装了一个使用 hostpath 的 storageclass)

  7. 其他的控制节点,通过kubeadm join --experimental-control-plane的方式来扩容控制节点

  8. 通过 kubeadm join 扩容其他的工作节点

  9. 等待集群创建完成

  10. 生成访问配置,打印使用帮助具体的创建流程,可以参考代码create.go



这里关于每个容器,是如何作为 Node 跑起来的,可以简单讲解些原理:



根据不同的角色,调用不同的函数创建节点nodes.go





// TODO(bentheelder): remove network in favor of []cri.PortMapping when that is in
func (d *nodeSpec) Create(clusterLabel string) (node *nodes.Node, err error) {
// create the node into a container (docker run, but it is paused, see createNode)
// TODO(bentheelder): decouple from config objects further
switch d.Role {
case constants.ExternalLoadBalancerNodeRoleValue:
node, err = nodes.CreateExternalLoadBalancerNode(d.Name, d.Image, clusterLabel, d.APIServerAddress, d.APIServerPort)
case constants.ControlPlaneNodeRoleValue:
node, err = nodes.CreateControlPlaneNode(d.Name, d.Image, clusterLabel, d.APIServerAddress, d.APIServerPort, d.ExtraMounts)
case constants.WorkerNodeRoleValue:
node, err = nodes.CreateWorkerNode(d.Name, d.Image, clusterLabel, d.ExtraMounts)
default:
return nil, errors.Errorf("unknown node role: %s", d.Role)
}
return node, err
}


节点(容器)创建时,通过配置 –privileged,挂载 tmpfs,修改主机名等,来运行节点create



func createNode(name, image, clusterLabel, role string, mounts []cri.Mount, extraArgs ...string) (handle *Node, err error) {
runArgs := []string{
"-d", // run the container detached
"-t", // allocate a tty for entrypoint logs
// running containers in a container requires privileged
// NOTE: we could try to replicate this with --cap-add, and use less
// privileges, but this flag also changes some mounts that are necessary
// including some ones docker would otherwise do by default.
// for now this is what we want. in the future we may revisit this.
"--privileged",
"--security-opt", "seccomp=unconfined", // also ignore seccomp
"--tmpfs", "/tmp", // various things depend on working /tmp
"--tmpfs", "/run", // systemd wants a writable /run
// some k8s things want /lib/modules
"-v", "/lib/modules:/lib/modules:ro",
"--hostname", name, // make hostname match container name
"--name", name, // ... and set the container name
// label the node with the cluster ID
"--label", clusterLabel,
// label the node with the role ID
"--label", fmt.Sprintf("%s=%s", constants.NodeRoleKey, role),
}

// pass proxy environment variables to be used by node's docker deamon
proxyDetails := getProxyDetails()
for key, val := range proxyDetails.Envs {
runArgs = append(runArgs, "-e", fmt.Sprintf("%s=%s", key, val))
}

// adds node specific args
runArgs = append(runArgs, extraArgs...)

if docker.UsernsRemap() {
// We need this argument in order to make this command work
// in systems that have userns-remap enabled on the docker daemon
runArgs = append(runArgs, "--userns=host")
}

err = docker.Run(
image,
docker.WithRunArgs(runArgs...),
docker.WithMounts(mounts),
)

// we should return a handle so the caller can clean it up
handle = FromName(name)
if err != nil {
return handle, errors.Wrap(err, "docker run error")
}

return handle, nil
}


More



Kind是一个比较简单有趣的项目,Kind的scope定的比较明确和具体,也定的比较小,其实借助 Kind 或者 Kind 的思想,可以做更多的事情,比如:




  • 在单节点部署自己的上层平台

  • 借助容器 mock 节点的方式,优化现有的测试方案

  • 自动化的部署测试

  • 自动化的 e2e 测试



原文链接:https://mp.weixin.qq.com/s/Vhr0ml1wI1BIoxKqKGTXRg

58集团云平台架构实践与演进

玻璃樽 发表了文章 • 0 个评论 • 458 次浏览 • 2019-06-04 19:23 • 来自相关话题

【编者的话】在17年底,我们分享了《高可用Docker容器云在58集团的实践》这篇文章,对整个容器云基础环境搭建与应用选型进行了详细介绍,本文是在该文章基础之上的进阶篇,是针对具体业务场景的落地解决方案。如果对基础环境选型比较感兴趣,可以查看上篇文章,在本文的 ...查看全部
【编者的话】在17年底,我们分享了《高可用Docker容器云在58集团的实践》这篇文章,对整个容器云基础环境搭建与应用选型进行了详细介绍,本文是在该文章基础之上的进阶篇,是针对具体业务场景的落地解决方案。如果对基础环境选型比较感兴趣,可以查看上篇文章,在本文的最后会附上相关文章的链接。对于上篇文章讨论过的内容,本文将不再进行详细讨论。后续每个月,云团队都会选择平台中某一具体领域的相关工作进行详细讨论与分享,欢迎大家关注。大家想了解哪方面的实现方案与细节,可进行相应留言。
#背景

通过容器化技术,58云计算平台主要解决以下几个问题:

  1. 资源利用率低:通过云化技术可以将资源利用率提升至原有的3-4倍,甚至更高。
  2. 服务扩容效率低:将传统扩容的时间从小时级别降低为分钟级别。
  3. 上线流程不规范:基于同一的镜像模板,约束整个上线过程。

为了解决上述问题,云团队通过技术选型与反复论证最终决定基于Docker与Kubernetes体系构建整个容器云环境。

云计算平台的发展历程如下:
1.png

#整体架构

58云计算平台的整体架构如下:
2.png

所有容器云的架构都是相似的,这里不做赘述,具体可查看上篇文章。

云计算平台承载了集团90%以上的业务流量,作为核心的服务管理与上线系统,它并不是独立运作的,为了保证整个上线流程的一致性与流畅度,满足业务日常管理与维护的通用需求,云平台与集团内部多个核心系统与组件进行了相应的对接与联动。
3.jpg

在项目管理方面,云平台与代码管理系统、项目管理等系统进行了内部对接,实现了代码从编译到生成镜像,再到环境部署的完全自动化。

在运维方面,云平台与CMDB、服务树、监控系统等多个运维系统进行了打通与整合,保证整个运维体系的完整性与用户体验的一致性。

在基础组件方面,集团内部的服务治理体系与常用的中间件系统都针对云平台进行了相应的改造,以适配云平台的工作模式,同时云平台也集成了现有的数据收集组件,保证数据流与用户习惯在云化前后是一致的。

云平台的使用方面,平台定义了四套环境,四套环境基于唯一的镜像仓库,这使得同一个代码版本可以在不同的环境之间进行流转,在保证代码唯一性的同时也使得相关环境的变更不会传递到下一个环境。
4.png

#架构演进

构建一个适配多种业务场景的容器云有很多细节需要考虑,云团队做了很多工作,由于篇幅关系,这里主要和大家分享58云计算平台在“网络架构”和“服务发现”两个核心组件上的架构实践与演进。
##网络架构

在网络架构方面,通过对比常用的六种容器组网模型,云计算平台选择“bridge+vlan”的方式作为基础网络模型。
5.png

原生bridge网络模型存在两个明显的缺点:IP利用率低、缺少网络限速。

为了解决IP地址利用率低的问题,云平台基于docker的CNM接口开发了网络插件,支持多个宿主间共享同一个容器网段,也支持IP地址在不同的宿主间复用。
6.png

这种网络模式下,分配给业务实例的IP地址是随机的,实例每次重启IP地址都可能会发生变更。在推进业务云化的早期,这种模式被业务所接受,但是随着云化进程的不断深入,业务方面提出了更高的要求:固定IP。

业务需求来源于真实的案例:有些服务要求IP不能发生变化,特别是某些依赖于第三方外部接口的服务。同时集团内部很多系统都是以IP固定为前提,如果IP随机分配,这些现有系统将不可用或很难用,极大的影响用户体验。如果IP固定的需求不能被满足,业务云化将很难推进下去。所以在18年4月份,云平台对网络架构进行了升级,支持了固定IP模式。网络架构的升级很好的保证了业务云化的进程。
7.png

固定IP的网络架构基于Kubernetes的CNI接口实现,增加了IP控制器模块,业务每次扩缩容时,都会与IP控制器交互,进行IP的变更。在业务正常升级流程中,归属于业务的IP将不会发生变化。依托于腾讯机房的网络支撑,平台将容器网段的路由规则下发到交换机,实现了容器的全网漂移,而不仅仅局限于固定的交换机。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

针对网络限速的需求,云平台结合容器网络虚拟化的特性,基于自研监控与tc工具实现了容器网络限速的能力。
8.jpg

标准的tc工具只支持单向限速,即出口流量限速。单方向限速无法满足业务的实际需求。在深入研究容器网络虚拟化的原理后,我们发现,容器在做虚拟化时,会创建一个网卡对,他们是对等的网络设备,在宿主机上体现为veth,在容器内部体现为eth0。基于这个特性,我们实现了双向限速:即对两块网卡同时做出口流量限速。由于是对等网卡,所以对veth做出口流量限速就相当于对eth0做入口流量限速。

在网络限速的基础上,云平台又进行了多维度的完善,分别支持动态限速、秒级限速与弹性限速等网络应用场景,极大的实现带宽复用。
##服务发现

服务发现是容器云平台中非常核心的服务组件,是流量的入口,也是服务对外暴露的接口。云平台中IP动态分配,节点弹性伸缩,需要有一套自动变更负载均衡器的方式。对于后端服务,集团有成熟的服务治理框架与体系,在云化过程中,中间件团队对服务治理体系进行了改造使其可以适配不断变化的云环境。对于前端服务,集团是基于Nginx做负载均衡,并且没有一套支持IP自动变更的架构。为了实现前端服务的云化,云平台团队与运维团队共同设计了全新的服务发现架构。

在调研阶段,云团队也调研了Kubernetes自带的服务发现机制,这一机制无法提供复杂的负载均衡策略,并且无法满足业务在特殊场景下需要对部分节点摘除流量的需求,所以最终我们否定了这一方案。
9.png

这是业界典型的服务发现架构。服务端和负载均衡器通过Consul进行解耦。服务注册在Consul中,负载均衡器通过Watch Consul中目录的变化来实时感知节点变更。在云化的早期,为了满足快速上线的需求,云平台对集团内部的Java Web框架进行了修改,使得其以心跳的方式自动注册到Consul中。这很好的解决了负载均衡器自动感知业务变更的问题以及云化过程中的流量灰度问题。

这一架构也引入了新的问题:调试问题与多语言扩展问题。由于是Java框架代理业务注册到Consul中,这使得活跃节点无法被下掉流量从而进行调试,但实际场景中很多故障调试需要下掉流量后才能进行。对Java语言的支持是通过修改框架来完成的,而集团中还有很多其他类型语言,比如PHP、Node.js和Go等。对每种接入语言或框架都需要以修改代码的方式才能接入到云平台中来,开发成本高并且对业务不友好。Consul被直接暴露给服务方,也增加了Consul的安全风险。

基于此,在18年4月,云平台对整个服务发现架构进行了升级。
10.png

新的服务发现架构中,业务与Consul中间增加了Proxy代理层,代理层通过Watch Kubernetes事件实时感知业务节点信息的变化,配合健康检查功能可以保证业务节点在变更时流量无损。基于代理层,任意语言的程序不需要做任何修改,仅通过简单配置即可接入到云平台。服务的注册与发现托管到云平台,Consul组件对业务透明,开发人员使用更友好。
#复盘与反思

回顾这两年来的容器云架构演进过程与业务云化历程,复盘遇到的棘手问题及其解决方案,与大家共同探讨。

在整个云平台的设计之初,我们有如下的设计考量:
11.png

容器云都会面临一个问题:对于业务来说,是容器还是虚拟机?虚拟机是业务习惯的使用模式,业务更容易接受。容器是以服务为核心,服务存在即存在,服务关闭即销毁,不再是虚拟机以机器为核心的模式。思虑再三,最终决定以容器的模式来定位,全新的平台提供全新的服务模式与体验。

虽然是以容器为核心,但容器中可运行单进程也可运行多进程。我们规范容器中只运行一个业务进程,防止由于运行多进程而导致业务之间相互干扰情况的发生,这类问题很难进行排查与定位。

去Agent化:在传统物理机模式下,一台物理机上可能会有多个Agent:运维管控Agent、监控Agent、业务自定义Agent等。云化后面临一个现实的问题:我们需要在每个容器中都安装这些Agent么?如果每个容器中都集成这些Agent,对云平台来说是简单的,这没有任何工作量。但是一台物理机上可能会运行上百个容器,这些Agent数量也会大量膨胀,带来系统负载的增加的问题。基于此,云平台投入大量的精力来实现容器的无Agent化,即在所有功能正常运行的前提下,Agent只运行在宿主上,不会运行在容器中。

业务云化过程中云团队遇到了很多问题案例,并形成自己独特的解决方案。这里选择了几个有代表性的案例,与大家进行分享。
12.png

服务启动耗CPU过高是云化早期时遇到的棘手问题。这个问题产生的原因很多:Java的语言特性导致JVM是在运行中逐步进行优化的;内部的很多开发框架都是在流量过来时才初始化链接;某些业务资源也是在流量过来时才进行初始化。当流量分发过来时,多种资源同时初始化导致服务需要大量的CPU资源,超过了平台为其分配的CPU配额,服务出现大量超时与抛弃。这一问题的简单解法是多分配CPU资源,但从长远角度来看这会导致资源利用无法有效把控,也会影响弹性调度的效果。最终,我们从两个维度解决这一问题:在调用方增加预热策略,流量逐步分发过来,CPU资源使用更平滑;在服务方增加预热方法,默认初始化链接等资源,同时引导用户进行自定义的初始化。

在容器监控维度方面,由于默认的监控数据是基于随机采样的分钟级数据,导致当服务出现短期秒级的CPU波动时,监控系统无法捕获,从而影响问题的排查与定位。针对这一问题,云平台对监控维度进行了深化,增加了容器级别秒级监控,实时采集每分钟的最大值上报,便于问题的排查与跟踪。
13.png

由于Cgroups只对CPU和内存资源进行了隔离与限速,并没有对系统负载进行隔离,如果容器中运行进程或线程数过多,会导致宿主机整体的负载波动,进而对上面的所有服务都会造成影响。

云平台增加了两个维度的控制策略:容器级别的最大线程数控制,防止由于单个容器线程数过多会对宿主造成影响。宿主级别的过载保护,当宿主上的负载过高时,过载保护策略会自动触发宿主上的容器进行漂移,直至负载降至合理的范围。

云平台必须关闭swap交换分区,这是一个深刻的经验教训。在云化的早期,云平台的交换分区没有关闭,部分服务经常出现由于使用交换分区而导致的耗时随机抖动,这类问题很难排查,耗费了大量的精力。

58云计算平台核心软件版本变迁过程如下:
14.png

#后记

容器云在58集团的实践与探索过程中,云团队做了很多技术选型与优化工作,也进行了很多技术方案的架构演进工作,究其根本原因是58集团是发展了十多年的互联网公司,它有自己独特的业务场景和成熟的开发体系与基础组件,容器云在集团内部起步较晚,内部系统很难为云平台做适配,只能云平台通过不断演进来适配不停发展的业务场景。这也是在较有规模的互联网公司做基础架构与创业公司的区别和挑战所在。所幸在团队的共同努力下,我们用1年多的时间完成了集团流量服务的云化工作。目前,集团内部还有很多服务需要云化,未来的挑战更大。

原文链接:https://mp.weixin.qq.com/s/gJM5-DByvMH54QhVsJ5HEA

介绍一个小工具:Kubedog

尼古拉斯 发表了文章 • 0 个评论 • 178 次浏览 • 2019-06-04 10:46 • 来自相关话题

Kubedog 是一个开源的 Golang 项目,使用 watch 方式对 Kubernetes 资源进行跟踪,能够方便的用于日常运维和 CI/CD 过程之中,项目中除了一个 CLI 小工具之外,还提供了一组 SDK,用户可以将其中的 Watch 功能集成到自 ...查看全部
Kubedog 是一个开源的 Golang 项目,使用 watch 方式对 Kubernetes 资源进行跟踪,能够方便的用于日常运维和 CI/CD 过程之中,项目中除了一个 CLI 小工具之外,还提供了一组 SDK,用户可以将其中的 Watch 功能集成到自己的系统之中。安装过程非常简单,在项目网页直接下载即可。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

Kubedog CLI 有两个功能:rollout track 和 follow。
#rollout track
在 Kubernetes 上运行应用时,通常的做法是使用 kubectl apply 提交 YAML 之后,使用 kubectl get -w 或者 watch kubectl get 之类的命令等待 Pod 启动。如果启动成功,则进行测试等后续动作;如果启动失败,就需要用 kubectl logs、kubectl describe 等命令来查看失败原因。kubedog 能在一定程度上简化这一过程。

例如使用 kubectl run 命令创建一个新的 Deployment 资源,并使用 kubedog 跟进创建进程:
$ kubectl run nginx --image=nginx22
...
deployment.apps/nginx created

$ kubedog rollout track deployment nginx
# deploy/nginx added
# deploy/nginx rs/nginx-6cc78cbf64 added
# deploy/nginx po/nginx-6cc78cbf64-8pnjz added
# deploy/nginx po/nginx-6cc78cbf64-8pnjz nginx error: ImagePullBackOff: Back-off pulling image "nginx22"
deploy/nginx po/nginx-6cc78cbf64-8pnjz nginx failed: ImagePullBackOff: Back-off pulling image "nginx22"

$ echo $?
130

很方便的看出,运行失败的状态及其原因,并且可以使用返回码来进行判断,方便在 Pipeline 中的运行。接下来可以使用 kubectl edit 命令编辑 Deployment,修改正确的镜像名称。然后再次进行验证:
$ kubectl edit deployment nginx
deployment.extensions/nginx edited
$ kubedog rollout track deployment nginx
# deploy/nginx added
# deploy/nginx rs/nginx-dbddb74b8 added
# deploy/nginx po/nginx-dbddb74b8-x4nkm added
# deploy/nginx event: po/nginx-dbddb74b8-x4nkm Pulled: Successfully pulled image "nginx"
# deploy/nginx event: po/nginx-dbddb74b8-x4nkm Created: Created container
# deploy/nginx event: po/nginx-dbddb74b8-x4nkm Started: Started container
# deploy/nginx event: ScalingReplicaSet: Scaled down replica set nginx-6cc78cbf64 to 0
# deploy/nginx become READY
$ echo $?
0

修改完成,重新运行 kubedog,会看到成功运行的情况,并且返回值也变成了 0。
#follow
follow 命令的功能和 kubetail 的功能有少量重叠,可以用 Deployment/Job/Daemonset 等为单位,查看其中所有 Pod 的日志,例如前面用的 Nginx,如果有访问的话,就会看到如下结果:
$ kubedog follow deployment nginx
# deploy/nginx appears to be ready
# deploy/nginx rs/nginx-6cc78cbf64 added
# deploy/nginx new rs/nginx-dbddb74b8 added
# deploy/nginx rs/nginx-dbddb74b8(new) po/nginx-dbddb74b8-x4nkm added
# deploy/nginx rs/nginx-6cc54845d9 added
# deploy/nginx event: ScalingReplicaSet: Scaled up replica set nginx-6cc54845d9 to 1
# deploy/nginx rs/nginx-6cc54845d9(new) po/nginx-6cc54845d9-nhlvs added
# deploy/nginx event: po/nginx-6cc54845d9-nhlvs Pulling: pulling image "nginx:alpine"
# deploy/nginx event: po/nginx-6cc54845d9-nhlvs Pulled: Successfully pulled image "nginx:alpine"
# deploy/nginx event: po/nginx-6cc54845d9-nhlvs Created: Created container
# deploy/nginx event: po/nginx-6cc54845d9-nhlvs Started: Started container
# deploy/nginx event: ScalingReplicaSet: Scaled down replica set nginx-dbddb74b8 to 0
# deploy/nginx become READY
# deploy/nginx event: po/nginx-dbddb74b8-x4nkm Killing: Killing container with id docker://nginx:Need to kill Pod
[quote]> deploy/nginx rs/nginx-dbddb74b8 po/nginx-dbddb74b8-x4nkm nginx[/quote]

[quote]> deploy/nginx rs/nginx-6cc54845d9(new) po/nginx-6cc54845d9-nhlvs nginx
127.0.0.1 - - [02/Jun/2019:11:35:08 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"
127.0.0.1 - - [02/Jun/2019:11:35:11 +0000] "GET / HTTP/1.1" 200 612 "-" "Wget" "-"

项目地址
https://github.com/flant/kubedog[/quote]

原文链接:https://blog.fleeto.us/post/intro-kubedog/

容器监控之kube-state-metrics

徐亚松 发表了文章 • 0 个评论 • 194 次浏览 • 2019-06-03 13:21 • 来自相关话题

概述 已经有了cadvisor、heapster、metric-server,几乎容器运行的所有指标都能拿到,但是下面这种情况却无能为力: * 我调度了多少个replicas?现在可用的有几个? ...查看全部
概述

已经有了cadvisor、heapster、metric-server,几乎容器运行的所有指标都能拿到,但是下面这种情况却无能为力:

* 我调度了多少个replicas?现在可用的有几个?
* 多少个Pod是running/stopped/terminated状态?
* Pod重启了多少次?
* 我有多少job在运行中

而这些则是kube-state-metrics提供的内容,它基于client-go开发,轮询Kubernetes API,并将Kubernetes的结构化信息转换为metrics。

功能

kube-state-metrics提供的指标,按照阶段分为三种类别:

  • 1.实验性质的:k8s api中alpha阶段的或者spec的字段。
  • 2.稳定版本的:k8s中不向后兼容的主要版本的更新
  • 3.被废弃的:已经不在维护的。
指标类别包括:* CronJob Metrics* DaemonSet Metrics* Deployment Metrics* Job Metrics* LimitRange Metrics* Node Metrics* PersistentVolume Metrics* PersistentVolumeClaim Metrics* Pod Metrics* Pod Disruption Budget Metrics* ReplicaSet Metrics* ReplicationController Metrics* ResourceQuota Metrics* Service Metrics* StatefulSet Metrics* Namespace Metrics* Horizontal Pod Autoscaler Metrics* Endpoint Metrics* Secret Metrics* ConfigMap Metrics以pod为例:* kube_pod_info* kube_pod_owner* kube_pod_status_phase* kube_pod_status_ready* kube_pod_status_scheduled* kube_pod_container_status_waiting* kube_pod_container_status_terminated_reason* ... 使用部署清单
 kube-state-metrics/    ├── kube-state-metrics-cluster-role-binding.yaml    ├── kube-state-metrics-cluster-role.yaml    ├── kube-state-metrics-deployment.yaml    ├── kube-state-metrics-role-binding.yaml    ├── kube-state-metrics-role.yaml    ├── kube-state-metrics-service-account.yaml    ├── kube-state-metrics-service.yaml
主要镜像有:image: quay.io/coreos/kube-state-metrics:v1.5.0image: k8s.gcr.io/addon-resizer:1.8.3(参考metric-server文章,用于扩缩容)对于pod的资源限制,一般情况下:`200MiB memory0.1 cores`超过100节点的集群:`2MiB memory per node0.001 cores per node`kube-state-metrics做过一次性能优化,具体内容参考下文部署成功后,prometheus的target会出现如下标志
1.png
因为kube-state-metrics-service.yaml中有`prometheus.io/scrape: 'true'`标识,因此会将metric暴露给prometheus,而Prometheus会在kubernetes-service-endpoints这个job下自动发现kube-state-metrics,并开始拉取metrics,无需其他配置。如果你想和更多监控技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态。使用kube-state-metrics后的常用场景有:存在执行失败的Job: kube_job_status_failed{job="kubernetes-service-endpoints",k8s_app="kube-state-metrics"}==1
  • 集群节点状态错误: kube_node_status_condition{condition="Ready",status!="true"}==1
  • 集群中存在启动失败的Pod:kube_pod_status_phase{phase=~"Failed|Unknown"}==1
  • 最近30分钟内有Pod容器重启: changes(kube_pod_container_status_restarts[30m])>0
配合报警可以更好地监控集群的运行 与metric-server的对比* metric-server(或heapster)是从api-server中获取cpu、内存使用率这种监控指标,并把他们发送给存储后端,如influxdb或云厂商,他当前的核心作用是:为HPA等组件提供决策指标支持。
  • kube-state-metrics关注于获取k8s各种资源的最新状态,如deployment或者daemonset,之所以没有把kube-state-metrics纳入到metric-server的能力中,是因为他们的关注点本质上是不一样的。metric-server仅仅是获取、格式化现有数据,写入特定的存储,实质上是一个监控系统。而kube-state-metrics是将k8s的运行状况在内存中做了个快照,并且获取新的指标,但他没有能力导出这些指标
  • 换个角度讲,kube-state-metrics本身是metric-server的一种数据来源,虽然现在没有这么做。
  • 另外,像Prometheus这种监控系统,并不会去用metric-server中的数据,他都是自己做指标收集、集成的(Prometheus包含了metric-server的能力),但Prometheus可以监控metric-server本身组件的监控状态并适时报警,这里的监控就可以通过kube-state-metrics来实现,如metric-serverpod的运行状态。
深入解析kube-state-metrics本质上是不断轮询api-server,代码结构也很简单主要代码目录
.├── collectors│   ├── builder.go│   ├── collectors.go│   ├── configmap.go│   ......│   ├── testutils.go│   ├── testutils_test.go│   └── utils.go├── constant│   └── resource_unit.go├── metrics│   ├── metrics.go│   └── metrics_test.go├── metrics_store│   ├── metrics_store.go│   └── metrics_store_test.go├── options│   ├── collector.go│   ├── options.go│   ├── options_test.go│   ├── types.go│   └── types_test.go├── version│   └── version.go└── whiteblacklist    ├── whiteblacklist.go    └── whiteblacklist_test.go
所有类型:
var (	DefaultNamespaces = NamespaceList{metav1.NamespaceAll}	DefaultCollectors = CollectorSet{		"daemonsets":               struct{}{},		"deployments":              struct{}{},		"limitranges":              struct{}{},		"nodes":                    struct{}{},		"pods":                     struct{}{},		"poddisruptionbudgets":     struct{}{},		"replicasets":              struct{}{},		"replicationcontrollers":   struct{}{},		"resourcequotas":           struct{}{},		"services":                 struct{}{},		"jobs":                     struct{}{},		"cronjobs":                 struct{}{},		"statefulsets":             struct{}{},		"persistentvolumes":        struct{}{},		"persistentvolumeclaims":   struct{}{},		"namespaces":               struct{}{},		"horizontalpodautoscalers": struct{}{},		"endpoints":                struct{}{},		"secrets":                  struct{}{},		"configmaps":               struct{}{},	})
构建对应的收集器Family即一个类型的资源集合,如job下的kube_job_info、kube_job_created,都是一个FamilyGenerator实例
metrics.FamilyGenerator{			Name: "kube_job_info",			Type: metrics.MetricTypeGauge,			Help: "Information about job.",			GenerateFunc: wrapJobFunc(func(j *v1batch.Job) metrics.Family {				return metrics.Family{&metrics.Metric{					Name:  "kube_job_info",					Value: 1,				}}			}),		},
func (b [i]Builder) buildCronJobCollector() [/i]Collector {   // 过滤传入的白名单	filteredMetricFamilies := filterMetricFamilies(b.whiteBlackList, cronJobMetricFamilies)	composedMetricGenFuncs := composeMetricGenFuncs(filteredMetricFamilies)  // 将参数写到header中	familyHeaders := extractMetricFamilyHeaders(filteredMetricFamilies)  // NewMetricsStore实现了client-go的cache.Store接口,实现本地缓存。	store := metricsstore.NewMetricsStore(		familyHeaders,		composedMetricGenFuncs,	)  // 按namespace构建Reflector,监听变化	reflectorPerNamespace(b.ctx, b.kubeClient, &batchv1beta1.CronJob{}, store, b.namespaces, createCronJobListWatch)	return NewCollector(store)}
性能优化:kube-state-metrics在之前的版本中暴露出两个问题:
  • 1. /metrics接口响应慢(10-20s)
  • 2. 内存消耗太大,导致超出limit被杀掉
问题一的方案就是基于client-go的cache tool实现本地缓存,具体结构为:`var cache = map[uuid][]byte{}`问题二的的方案是:对于时间序列的字符串,是存在很多重复字符的(如namespace等前缀筛选),可以用指针或者结构化这些重复字符。 优化点和问题
  • 1.因为kube-state-metrics是监听资源的add、delete、update事件,那么在kube-state-metrics部署之前已经运行的资源,岂不是拿不到数据?kube-state-metric利用client-go可以初始化所有已经存在的资源对象,确保没有任何遗漏
  • 2.kube-state-metrics当前不会输出metadata信息(如help和description)
  • 3.缓存实现是基于golang的map,解决并发读问题当期是用了一个简单的互斥锁,应该可以解决问题,后续会考虑golang的sync.Map安全map。
  • 4.kube-state-metrics通过比较resource version来保证event的顺序
  • 5.kube-state-metrics并不保证包含所有资源

监控数据展示

基于kube-state-metrics的监控数据,可以组装一些常用的监控面板,如下面的grafana面板:
2.png

3.png

本文为容器监控实践系列文章,完整内容见:container-monitor-book

容器云未来:Kubernetes、Istio 和 Knative

博云BoCloud 发表了文章 • 0 个评论 • 186 次浏览 • 2019-06-03 10:38 • 来自相关话题

导读 目前以Kubernetes为基础构建的容器生态逐渐完善,这其中Kubernetes、Istio、Knative三个独立项目被越来越多的人提及,并且已经开始尝试大规模落地实践,它们恰好构成了容器云的未来拼图。今天与大家一起分享下,这三个项目究 ...查看全部
导读
目前以Kubernetes为基础构建的容器生态逐渐完善,这其中Kubernetes、Istio、Knative三个独立项目被越来越多的人提及,并且已经开始尝试大规模落地实践,它们恰好构成了容器云的未来拼图。今天与大家一起分享下,这三个项目究竟解决了什么问题,为什么它们能够一鸣惊人。


随着微服务理念不断深入人心,越来越多的企业把自己的应用逐步由单体转变成微服务架构,Container容器技术的出现恰恰加速了这个转移过程,因为它有效地解决了N多服务的快速部署问题。但是随着服务数目的增多,越来越多的企业希望能够把相关服务有效地“聚合”在一起,方便统一部署与管理。Kubenretes的出现恰恰解决了大规模微服务编排部署所带来的挑战,让整个行业意识到PaaS的落地可以成为现实。

当随着微服务体系下的服务数目越来越多,服务运维成为必然要解决的问题,于是Istio出现了,基于网络代理与控制相分离的实现策略,允许对服务控制策略进行有效合理的管控。如果你想和更多容器技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

到这里似乎到了很美好的阶段:

微服务:解决应用内聚、臃肿的问题。
Container:解决服务运行环境统一,和部署问题。
Kubernetes:解决大量微服务有效“聚合”部署问题。
Istio:解决服务上线面临的一系列治理问题。

这个阶段乍一看来,构建容器云似乎有了一个完整的链路和解决方式,一切都将变得那么“完美”。

现在让我们回过头来深入分析一下,微服务体系下的服务交互,目前是否存在问题。

首先,无论是http,还是rpc,本质上都是服务与服务的远程调用。开发应用程序中,无法做到服务与服务间的彼此透明。这样会导致一个问题:无论微服务业务拆分多么“精细”,本质上业务单元之间还是不能够独立运行和发展。同时在面向不同开发领域的衍生,无法选择最合适的实现方式。因此我们希望能够基于不同的“模板”+“配置”的方式能够把开发环境标准化处理,同时提供“事件”机制,将服务与服务交互的耦合度降到最低。

其次,服务线上运行的动态伸缩问题。当下kubernetes环境下的弹性伸缩,需要由客户搜集监测数据,并自主手动来实现,但是我们更希望服务线上能够更加自动化和智能化。

最后,服务标准化问题。我们希望服务内部的模型是标准的、能够快速复制和快速构建的;服务通信是标准的:协议标准,格式标准;运行环境是标准的:快速部署,快速迁移。

Knative的出现恰好解决远程直接调用,服务线上自动管理以及一些列标准化问题。

下面我们来看一下三者的关联:


微信图片_20190603102740.png



Kubernetes和Istio相信大家比较熟悉了,这里不做过多介绍,有需要的同学可以关注下我们之前发布的相关文章,这里我们重点来看一下Knative。

Knative是谷歌开源的serverless架构方案,旨在提供一套简单易用的serverless方案,把serverless标准化。目前参与的公司主要是Google、Pivotal、IBM、Red Hat,于2018年7月份对外发布,目前处于快速发展阶段。


Knative组成


Build
构建系统:把用户定义的应用构建成容器镜像,面向kubernetes的标准化构建,区别于Dockerfile镜像构建,重点解决kubernetes环境的构建标准化问题。

Serving
服务系统:利用Istio的部分功能,来配置应用路由,升级以及弹性伸缩。Serving中包括容器生命周期管理,容器外围对象(service,ingres)生成(恰到好处的把服务实例与访问统一在一起),监控应用请求,自动弹性负载,并且利用Virtual service和destination配置服务访问规则。只有这样才能保证服务呈现一致性以及服务运行自动化管理。

Eventing
事件系统:用于自动完成事件的绑定与触发。事件系统与直接调用最大的区别在于响应式设计,它允许运行服务本身不需要屏蔽了调用方与被调用方的关系。从而在业务层面能够实现业务的快速聚合,或许为后续业务编排创新提供事件。

现在我们换一个角度,聚焦应用服务生命周期:
**· Knative 解决应用模板+面向统一环境的标准化构建场景;
· Kubernetes作为基础设施,解决应用编排和运行环境场景;
· 加粗文字Isito作为通信基础设施层,保证应用服务运行可检测、可配置、可追踪问题。**

这三者贯穿应用服务生命周期全过程,容器云恰恰也是管理应用服务的控制平台,这就能够很好地解释,为什么Kubernetes,Istio,Knative在未来会成为构建容器云的三驾马车。

在Kubernetes上运行Kafka合适吗?

whole 发表了文章 • 0 个评论 • 369 次浏览 • 2019-06-02 11:42 • 来自相关话题

【编者的话】本文介绍了在Kubernetes运行Kafka的互补性和一些坑,以及相关方案选择。 # 介绍 Kubernetes设计的初衷是运行无状态工作负载。这些通常采用微服务架构的工作负载,是轻量级,可水平扩展,遵循十二要素应用程序, ...查看全部
【编者的话】本文介绍了在Kubernetes运行Kafka的互补性和一些坑,以及相关方案选择。
# 介绍
Kubernetes设计的初衷是运行无状态工作负载。这些通常采用微服务架构的工作负载,是轻量级,可水平扩展,遵循十二要素应用程序,可以处理环形断路和随机Monkey测试。

另一方面,Kafka本质上是一个分布式数据库。这意味着你必须处理状态,它比微服务更重量级。Kubernetes支持有状态的工作负载,但你必须谨慎对待它,正如Kelsey Hightower在最近的两条推文中指出的那样:
1.png

现在你应该在Kubernetes上运行Kafka吗?我的反问是:没有它,Kafka会跑得更好吗?这就是为什么我要指出Kafka和Kubernetes之间的相互补充性以及你可能遇到的陷阱。
# 运行时
让我们先看一下基本的东西——运行时本身。
## 进程
Kafka brokers对CPU很友好。TLS可能会引入一些开销。如果Kafka客户端使用加密,则需要更多CPU,但这不会影响brokers。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态
## 内存
Kafka brokers是内存消耗大户。JVM堆通常可以限制为4-5 GB,但由于Kafka大量使用页面缓存,因此还需要足够的系统内存。在Kubernetes中,可以相应地设置容器资源限制和请求。
## 存储
容器中的存储是短暂的——重启后数据将丢失。可以对Kafka数据使用emptyDir卷,这将产生相同的效果:brokers的数据将在停机后丢失。您的消息在其他broker上作为副本还是可以使用的。因此,重新启动后,失败的broker必须得复制所有的数据,这可能是一个耗时过程。

这就是你应该使用持久存储的原因。使用XFS或ext4的非本地持久性块存储更合适。我警告你:不要使用NFS。NFS v3和v4都不会起作用。简而言之,Kafka broker会因为NFS“愚蠢重命名”问题而无法删除数据目录,自行终止。如果你仍然不相信我,那么请仔细阅读这篇博文。存储必须是非本地的,以便Kubernetes在重新启动或重新定位时可以更灵活地选择另一个节点。
## 网络
与大多数分布式系统一样,Kafka性能在很大程度上取决于低网络延迟和高带宽。不要试图将所有代理放在同一节点上,因为这会降低可用性。如果Kubernetes节点出现故障,那么整个Kafka集群都会出现故障。不要跨数据中心扩展Kafka集群。这同样适用于Kubernetes集群。不同的可用区域是一个很好的权衡。
# 配置
## 清单
Kubernetes网站包含一个非常好的教程,介绍如何使用清单设置ZooKeeper。由于ZooKeeper是Kafka的一部分,因此可以通过这个了解哪些Kubernetes概念被应用在这里。一旦理解,您也可以对Kafka集群使用相同的概念。

  • Pod:Pod是Kubernetes中最小的可部署单元。它包含您的工作负载,它代表群集中的一个进程。一个Pod包含一个或多个容器。整体中的每个ZooKeeper服务器和Kafka集群中的每个Kafka broker都将在一个单独的Pod中运行。
  • StatefulSet:StatefulSet是一个Kubernetes对象,用于处理需要协调的多个有状态工作负载。StatefulSets保证Pod的有序性和唯一性的。
  • Headless Services:服务通过逻辑名称将Pod与客户端分离。Kubernetes负责负载平衡。但是,对于ZooKeeper和Kafka等有状态工作负载,客户端必须与特定实例进行通信。这就是 Headless Services发挥作用的地方:作为客户端,仍然可以获得逻辑名称,但不必直接访问Pod。
  • 持久卷:如上所述,需要配置非本地持久块存储。

Yolean提供了一套全面的清单,可以帮助您开始使用Kubernetes上的Kafka。

## Helm Charts
Helm是Kubernetes的包管理器,类似yum,apt,Homebrew或Chocolatey等OS包管理器。它允许您安装Helm Charts中描述的预定义软件包。精心设计的Helm Charts能简化所有参数正确配置的复杂任务,以便在Kubernetes上运行Kafka。有几张图表适用于Kafka的的可供选择:一个是处于演进状态的官方图表,一个来自Confluent,另一个来自Bitnami,仅举几例。
## Operators
由于Helm的一些限制,另一种工具变得非常流行:Kubernetes Operators。Operators不仅可以为Kubernetes打包软件,还可以为Kubernetes部署和管理一个软件。

评价很高的Operators名单中提到Kafka有两个,其中一个是Strimzi,Strimzi使得在几分钟内启动Kafka集群变得非常容易,几乎不需要任何配置,它增加了一些漂亮的功能,如群集间点对点TLS加密。Confluent还宣布即将推出新的Operator。
## 性能
运行性能测试以对Kafka安装进行基准测试非常重要。在您遇到麻烦之前,它会为您提供有关可能的瓶颈的地方。幸运的是,Kafka已经提供了两个性能测试工具:kafka-producer-perf-test.sh和kafka-consumer-perf-test.sh。记得经常使用它们。作为参考,可以使用Jay Kreps博客结果,或者 Stéphane Maarek在 Amazon MSK的评论
# 运维
## 监控
可见性非常重要,否则您将不知道发生了什么。如今,有一种不错的工具可以用云原生方式监控指标。Prometheus和Grafana是两种流行的工具。Prometheus可以直接从JMX导出器收集所有Java进程(Kafka,ZooKeeper,Kafka Connect)的指标。添加cAdvisor指标可为提供有关Kubernetes资源使用情况的其他信息。

Strimzi为Kafka提供了一个优雅的Grafana仪表板示例。它以非常直观的方式可视化关键指标,如未复制的和离线分区。它通过资源使用和性能以及稳定性指标来补充这些指标。因此,可以免费获得基本的Kafka集群监控!
2.png

资料来源:https://strimzi.io/docs/master/#kafka_dashboard

可以通过客户端监控(消费者和生产者指标),使用Burrow滞后监控,使用Kafka Monitor进行端到端监控,来完成这个任务
## 日志记录
日志记录是另一个关键部分。确保Kafka安装中的所有容器都记录到标准输出(stdout)和标准错误输出(stderr),并确保Kubernetes集群将所有日志聚合到中央日志记录设施中如Elasticsearch中。
## 健康检查
Kubernetes使用活跃度和就绪探测器来确定Pod是否健康。如果活跃度探测失败,Kubernetes将终止容器并在相应设置重启策略时自动重启。如果准备就绪探测失败,那么Kubernetes将通过服务从服务请求中删除该Pod。这意味着在这种情况下不再需要人工干预,这是一大优点。
## 滚动更新
StatefulSets支持自动更新:滚动更新策略将一次更新一个Kafka Pod。通过这种方式,可以实现零停机时间,这是Kubernetes带来的另一大优势。
## 扩展
扩展Kafka集群并非易事。但是,Kubernetes可以很容易地将Pod缩放到一定数量的副本,这意味着可以声明式地定义所需数量的Kafka brokers。困难的部分是在放大或缩小之前重新分配部分。同样,Kubernetes可以帮助您完成这项任务。
## 管理
通过在Pod中打开shell,可以使用现有的shell脚本完成Kafka群集的管理任务,例如创建主题和重新分配分区。这不是一个很好的解决方案。Strimzi支持与另一个Operator管理主题。这还有改进的余地。
## 备份和还原
现在Kafka的可用性还取决于Kubernetes的可用性。如果Kubernetes群集出现故障,那么在最坏的情况下Kafka群集也会故障。墨菲定律告诉我们,这也会发生在你身上,你会丢失数据。要降低此风险,请确保您具有备份想法。MirrorMaker是一种可选方案,另一种可能是利用S3进行连接备份,如Zalando的博客文章所述。
# 结论
对于中小型Kafka集群,我肯定会选择Kubernetes,因为它提供了更大的灵活性并简化了操作。如果您在延迟和/或吞吐量方面具有非常高的非功能性要求,则不同的部署选项可能更有益。

原文链接:Kafka on Kubernetes — a good fit?(译者:姜俊厚)
Kubernetes是Google开源的容器集群管理系统,其提供应用部署、维护、 扩展机制等功能,利用Kubernetes能方便地管理跨机器运行容器化的应用。