容器

容器

容器环境下Node.js的内存管理

老马 发表了文章 • 0 个评论 • 157 次浏览 • 2019-06-03 15:26 • 来自相关话题

【编者的话】在基于容器的Node.js应用程序中管理内存的最佳实践。 在docker容器中运行Node.js应用程序时,传统的内存参数调整并不总是按预期工作。本文我们将阐述在基于容器的Node.js应用程序内存参数调优中并不总是有效的 ...查看全部
【编者的话】在基于容器的Node.js应用程序中管理内存的最佳实践。

在docker容器中运行Node.js应用程序时,传统的内存参数调整并不总是按预期工作。本文我们将阐述在基于容器的Node.js应用程序内存参数调优中并不总是有效的原因,并提供了在容器环境中使用Node.js应用程序时可以遵循的建议和最佳实践。
#综述
当Node.js应用程序运行在设置了内存限制的容器中时(使用docker --memory选项或者系统中的其他任意标志),请使用--max-old-space-size选项以确保Node.js知道其内存限制并且设置其值小于容器限制。

当Node.js应用程序在容器内运行时,将Node.js应用程序的峰值内存值设置为容器的内存容量(假如容器内存可以调整的话)。

接下来让我们更详细地探讨一下。
#Docker内存限制
默认情况下,容器是没有资源限制的,可以使用系统(OS)允许的尽可能多的可用内存资源。但是docker 运行命令可以指定选项,用于设置容器可以使用的内存或CPU。

该docker-run命令如下所示:docker run --memory --interactive --tty bash。

参数介绍:

* x是以y为单位的内存
* y可以是b(字节),k(千字节),m(兆字节),g(千兆字节)

例如:docker run --memory 1000000b --interactive --tty bash将内存或CPU限制设置为1,000,000字节。

要检查容器内的内存限制(以字节为单位),请使用以下命令:
cat /sys/fs/cgroup/memory/memory.limit_in_bytes

接下来我们一起来看下设置了--max_old_space_size之后容器的各种表现。

“旧生代”是V8内存托管堆的公共堆部分(即JavaScript对象所在的位置),并且该--max-old-space-size标志控制其最大大小。有关更多信息,请参阅关于-max-old-space-size

通常,当应用程序使用的内存多于容器内存时,应用程序将终止。如果你想和更多容器技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

以下示例应用程序以10毫秒的间隔插入记录到列表。这个快速的间隔使得堆无限制地增长,模拟内存泄漏。
'use strict';
const list = [];
setInterval(()=> {
const record = new MyRecord();
list.push(record);
},10);
function MyRecord() {
var x='hii';
this.name = x.repeat(10000000);
this.id = x.repeat(10000000);
this.account = x.repeat(10000000);
}
setInterval(()=> {
console.log(process.memoryUsage())
},100);

本文所有的示例程序都可以在我推入Docker Hub的Docker镜像中获得。你也可以拉取Docker镜像并运行程序。使用docker pull ravali1906/dockermemory来获取图像。

或者,你可以自己构建镜像,并使用内存限制运行镜像,如下所示:
docker run --memory 512m --interactive --tty ravali1906/dockermemory bash

ravali1906/dockermemory是镜像的名称。

接下来,运行内存大于容器限制的应用程序:
$ node --max_old_space_size=1024 test-fatal-error.js
{ rss: 550498304,
heapTotal: 1090719744,
heapUsed: 1030627104,
external: 8272 }
Killed

PS:

* --max_old_space_size 取M为单位的值
* process.memoryUsage() 以字节为单位输出内存使用情况

当内存使用率超过某个阈值时,应用程序终止。但这些阈值是多少?有什么限制?我们来看一下约束。
#在容器中设置了--max-old-space-size约束的预期结果
默认情况下,Node.js(适用于11.x版本及以下)在32位和64位平台上使用最大堆大小分别为700MB和1400MB。对于当前默认值,请参阅博客末尾参考文章。

因此,理论上,当设置--max-old-space-size内存限制大于容器内存时,期望应用程序应直接被OOM(Out Of Memory)终止。

实际上,这可能不会发生。
#在容器中设置了--max-old-space-size约束的实际结果
并非所有通过--max-old-space-size指定的内存的容量都可以提前分配给应用程序。

相反,为了响应不断增长的需求,JavaScript内存堆是逐渐增长的。

应用程序使用的实际内存(以JavaScript堆中的对象的形式)可以在process.memoryUsage()API中的heapUsed字段看到。

因此,现在修改后的期望是,如果实际堆大小(驻留对象大小)超过OOM-KILLER阈值(--memory容器中的标志),则容器终止应用程序。
实际上,这也可能不会发生。

当我在容器受限的环境下分析内存密集型Node.js应用程序时,我看到两种情况:

* OOM-KILLER在heapTotal和heapUsed的值都高于容器限制之后,隔一段很长的时间才执行。
* OOM-KILLER根本没有执行。

#容器环境中的Node.js相关行为解释
监控容器中运行应用程序的重要指标是驻留集大小(RSS-resident set size)。

它属于应用程序虚拟内存的一部分。

或者说,它代表应用程序被分配的内存的一部分。

更进一步说,它表示应用程序分配的内存中当前处于活动状态的部分。

并非应用程序中的所有已分配内存都属于活动状态,这是因为“分配的内存”只有在进程实际开始使用它时才会真实分配。另外,为了响应其他进程的内存需求,系统可能swap out当前进程中处于非活动或休眠状态的内存给其他进程,后续如果当前进程需要的时候通过swapped in重新分配回来。

RSS反映了应用程序的可用和活动的内存量。
#证明
##示例1.创建一个大小超过容器内存限制的空Buffer对象
以下buffer_example.js为往内存分配空Buffer对象的实例代码:
const buf = Buffer.alloc(+process.argv[2] [i] 1024 [/i] 1024)
console.log(Math.round(buf.length / (1024 * 1024)))
console.log(Math.round(process.memoryUsage().rss / (1024 * 1024)))

运行Docker镜像并限制其内存用量:
docker run --memory 1024m --interactive --tty ravali1906/dockermemory bash

运行该应用程序。你会看到以下内容:
$ node buffer_example 2000
2000
16

即使内存大于容器限制,应用程序也不会终止。这是因为分配的内存还未被完全访问。rss值非常低,并且没有超过容器内存限制。
##示例2.创建一个大小超过容器内存限制的并填满的Buffer对象
以下为往内存分配Buffer对象并填满值的实例代码:
const buf = Buffer.alloc(+process.argv[2] [i] 1024 [/i] 1024,'x')
console.log(Math.round(buf.length / (1024 * 1024)))
console.log(Math.round(process.memoryUsage().rss / (1024 * 1024)))

运行Docker镜像并限制其内存用量:
docker run --memory 1024m --interactive --tty ravali1906/dockermemory bash

运行该应用程序
$ node buffer_example_fill.js 2000
2000
984

即使在这里应用也没有被终止!为什么?当活动内存达到容器设置限制时,并且swap space还有空间时,一些旧内存片段将被推送到swap space并可供同一进程使用。默认情况下,docker分配的交换空间量等于通过--memory标志设置的内存限制。有了这种机制,这个进程几乎可以使用2GB内存 - 1GB活动内存和1GB交换空间。简而言之,由于内存的交换机制,rss仍然在容器强制限制范围内,并且应用程序能够持续运行。
##示例3.创建一个大小超过容器内存限制的空Buffer对象并且限制容器使用swap空间
const buf = Buffer.alloc(+process.argv[2] [i] 1024 [/i] 1024,'x')
console.log(Math.round(buf.length / (1024 * 1024)))
console.log(Math.round(process.memoryUsage().rss / (1024 * 1024)))

运行镜像时限制docker内存,交换空间和关闭匿名页面交换,如下所示:
docker run --memory 1024m --memory-swap=1024m --memory-swappiness=0 --interactive --tty ravali1906/dockermemory bash

$ node buffer_example_fill.js 2000
Killed

当--memory-swap的值等于--memory的值时,它表示容器不使用任何额外的交换空间。此外,默认情况下,容器的内核可以交换出一定比例的匿名页,因此将--memory-swappiness设置为0以禁用它。因此,由于容器内没有发生交换,rss超出了容器限制,在正确的时间终止了进程。
#总结和建议
当您运行Node.js应用程序并将其--max-old-space-size设置为大于容器限制时,看起来Node.js可能不会“尊重”容器强制限制。但正如您在上面的示例中看到的,原因是应用程序可能无法使用标志访问JavaScript堆集的全长。

请记住,当您使用的内存多于容器中可用的内存时,无法保证应用按期望行为方式运行。为什么?因为进程的活动内存(rss)受到许多因素的影响,这些因素超出了应用程序的控制范围,并且可能依赖于高负载和环境 - 例如工作负载本身,系统中的并发级别,操作系统调度程序,垃圾收集率等。此外,这些因素可以在运行之间发生变化。
#关于Node.js堆大小的建议(当你可以控制它,但不能控制容器大小时)

* 运行一个空的Node.js应用程序,并测量空转情况下rss的使用情况(我在Node.js v10.x版本得到它的值约为20 MB)。
* 由于Node.js在堆中具有其他内存区域(例如new_space,code_space等),因此假设其默认配置会占用额外的20 MB。如果更改其默认值,请相应地调整此值。
* 从容器中的可用内存中减去此值(40 MB),得到的值设置为JavaScript的旧生代大小,应该是一个相当安全的值。

#关于容器内存大小的建议(当你可以控制它,但不能控制Node.js内存时)

* 运行涵盖高峰工作负载的应用程序。
* 观察rss空间的增长。使用top命令和process.memoryUsage()API得到最高值。
* 如果容器中不存在其他活动进程,将此值用作容器的内存限制。该值上浮10%以上会更加安全。

#备注
如果在容器环境下运行,Node.js 12.x的堆内存限制根据当前可用内存进行配置,而不是使用默认值。对于设置了max_old_space_size的场景,上面的建议仍然适用。此外,了解相关限制可以让您更好地调整应用并发挥应用的性能,因为默认值是相对保守的。

有关更多信息,请参阅配置默认堆转储

作者:Make_a_decision
链接:https://juejin.im/post/5cef9efc6fb9a07ec56e5cc5

容器监控之kube-state-metrics

徐亚松 发表了文章 • 0 个评论 • 167 次浏览 • 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 个评论 • 157 次浏览 • 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在未来会成为构建容器云的三驾马车。

容器、微服务与服务网格

cleverlzc 发表了文章 • 0 个评论 • 256 次浏览 • 2019-05-26 11:03 • 来自相关话题

【编者的话】本文结合dotCloud的发展为例,阐述了一个基本的服务网格应该具备什么样的能力,对诸如Istio、Linkerd、Consul Connect等现代服务网格系统的基本模式进行了阐述,最后对于“自建还是购买(或者使用开源)”这个老生常谈的话题作者给 ...查看全部
【编者的话】本文结合dotCloud的发展为例,阐述了一个基本的服务网格应该具备什么样的能力,对诸如Istio、Linkerd、Consul Connect等现代服务网格系统的基本模式进行了阐述,最后对于“自建还是购买(或者使用开源)”这个老生常谈的话题作者给出了自己的见解。

如你所知,已经有很多关于服务网格的资料(1234),但这是另外一篇。是的!但是为什么会有这篇文章呢?因为我想给你们一些不同的视角,他们希望服务网格在10年前就已经存在,远早于Docker和Kubernetes这样的容器平台的兴起。我并不是说这个视角比其他视角更好或更差,但是由于服务网格是相当复杂的“野兽”,所以我相信多种视角有助于更好地理解它们。

我将讨论dotCloud平台,这是一个建立在100多个微服务之上的平台,支持数千个运行在容器中的生产应用程序;我将解释在构建和运行它时所面临的挑战;以及服务网格会(或不会)提供帮助。
# dotCloud的历史
我已经写过关于dotCloud平台的历史和它的一些设计选择,但是我没有过多地讨论它的网络层。如果你不想深入了解我之前关于dotCloud的博客,你需要知道的是它是一个PaaS,允许客户运行各种应用程序(Java、PHP、Python等),支持广泛的数据服务(MongoDB、MySQL、Redis等)以及类似于Heroku的工作流程:你可以将代码推送到平台,平台将构建容器映像,并部署这些容器映像。

我将告诉你流量是如何在dotCloud平台上路由的;不是因为它是特别棒或其他什么(我认为现在是比较合适的时间),但主要是因为,如果一个普通的团队需要一种在一个微服务群或一个应用程序群之间路由流量的方法,那么这种设计可以在短时间内用现在已有的工具轻松实现。因此,它将为我们提供一个很好的比较点,“如果我们破解它,我们会得到什么”和“如果我们使用现有的服务网格,我们会得到什么”,也就是老生常谈的“构建与购买”的困境。
# 托管应用的流量路由
部署在dotCloud上的应用程序会暴露HTTP和TCP端点。

HTTP端点被动态地添加到Hipache负载平衡器集群的配置中。这与我们今天使用Kubernetes Ingress资源和Traefik这样的负载平衡器可以实现的功能类似。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

只要域名指向dotCloud的负载平衡器,客户端就可以使用它们的关联域名连接到HTTP端点。这里没有什么特别的。

TCP端点与端口号相关联,然后端口号通过环境变量与该堆栈上的所有容器通信。

客户端可以使用指定的主机名(类似于gateway-X.dotcloud.com)和端口号连接到TCP端点。

该主机名将解析为一个“nats”服务器集群(与NATS没有任何关系),该集群将把传入的TCP连接路由到正确的容器(或者,在负载平衡服务的情况下,路由到正确的容器)。

如果你熟悉Kubernetes,这可能会让你想起NodePort服务。

dotCloud平台没有集群IP服务的等价物:为了简单起见,从内部和外部访问服务的方式是相同的。

这非常简单,最初的HTTP和TCP路由网格的实现可能都是几百行Python代码,使用相当简单(我敢说,很天真)的算法,但是随着时间的推移,它们不断发展,以处理平台的增长和额外的需求。

它不需要对现有应用程序代码进行大量重构。十二因素应用程序尤其可以直接使用通过环境变量提供的地址信息。
# 它与现代服务网络有何不同?
可观察性有限。对于TCP路由网格根本没有度量标准。至于HTTP路由网格,后来的版本提供了详细的HTTP度量,显示错误状态码和响应时间;但是现代服务网格的功能远远不止于此,它还提供了与度量收集系统(例如Prometheus)的集成。

可观察性非常重要,不仅从操作角度(帮助我们解决问题),还可以提供安全的蓝/绿部署金丝雀部署等功能。

路由效率也受到限制。在dotCloud路由网格中,所有流量都必须经过一组专用路由节点。这意味着可能跨越几个AZ(可用性区域)边界,并显著增加延迟。我记得对一些代码进行故障排除,这些代码发出100多个SQL请求来显示给定的页面,并为每个请求打开了到SQL服务器的新连接。在本地运行时,页面会立即加载,但在dotCloud上运行时,需要几秒钟,因为每个TCP连接(以及随后的SQL请求)都需要几十毫秒才能完成。在这种特定的情况下,使用持久连接起了作用。

现代服务网络做得更好。首先,通过确保连接在源位置路由。逻辑流仍然是客户端-->网格-->服务,但是现在网格在本地运行,而不是在远程节点上运行,因此客户端-->网格连接是本地连接,因此速度非常快(微秒而不是毫秒)。

现代服务网格还实现了更智能的负载平衡算法。通过监控后端的运行及健康状况,它们可以在更快的后端上发送更多的流量,从而提高整体性能。

随着现代服务网络的出现,安全性也越来越强。dotCloud路由网格完全在EC2 Classic上运行,并且没有加密流量(假设如果有人设法嗅探EC2上的网络流量,那么无论如何都会遇到更大的问题)。现代服务网格可以透明地保护我们所有的通信,例如通过相互的TLS身份验证和随后的加密。
# 平台服务的流量路由
OK,我们已经讨论了应用程序是如何通信的,但是dotCloud平台本身呢?

平台本身由大约100个微服务组成,负责各种功能。其中一些服务接受来自其他服务的请求,而其中一些服务是后台工作应用,它们将连接到其他服务,但不能自己接收连接。无论哪种方式,每个服务都需要知道它需要连接到的地址的端点。

许多高级服务都可以使用上面描述的路由网格。事实上,dotCloud平台的100多个微服务中有很大一部分是作为常规应用程序部署在dotCloud平台上的。但是少数低级服务(特别是那些实现路由网格的服务)需要一些更简单的东西,需要更少的依赖关系(因为它们不能依靠自己来运行;这是一个老生常谈的“先有鸡还是先有蛋”的问题)。

通过直接在几个关键节点上启动容器,而不是依赖于平台的构建器、调度程序和运行器服务,部署了这些底层的基本平台服务。如果你想要与现代容器平台进行比较,这就像直接在节点上运行Docker来启动我们的控制平面,而不是让Kubernetes为我们做这件事。这与kubeadmbootkube在引导自托管集群时使用的静态Pod的概念非常相似。

这些服务以一种非常简单和粗糙的方式被公开:有一个YAML文件列出了这些服务,将它们的名称映射到它们的地址;作为其部署的一部分,这些服务的每个使用者都需要一份该YAML文件的副本。

一方面,这是非常强大的,因为它不涉及像ZooKeeper那样维护外部键值存储(记住,etcd或Consul在那个时候不存在)。另一方面,这使得服务难以移动。每次移动服务时,它的所有消费者都需要接收更新的YAML文件(并且可能会重新启动)。不太方便!

我们开始实现的解决方案是让每个消费者都连接到一个本地代理。使用者不需要知道服务的完整地址+端口,只需要知道它的端口号,并通过localhost进行连接。本地代理将处理该连接,并将其路由到实际后端。现在,当一个后端需要移动到另一台机器上,或按比例放大或缩小,而不是更新它的所有消费者,我们只需要更新所有这些本地代理;我们不再需要重新启动消费者。

(还计划将流量封装在TLS连接中,并在接收端使用另一个代理来打开TLS并验证证书,而不涉及接收服务,该服务将被设置为仅在本地主机上接受连接。稍后会详细介绍。)

这与AirBNB的SmartStack非常相似;与SmartStack实现并部署到生产环境的显著区别是,当dotCloud转向Docker时,它的新的内部路由网格被搁置了。

我个人认为SmartStack是诸如Istio、Linkerd、Consul Connect等系统的先驱之一,因为所有这些系统都遵循这种模式:

  • 在每个节点上运行代理
  • 消费者连接到代理
  • 后端改变时,控制平面更新代理的配置
# 今天实现一个服务网格如果我们今天必须实现类似的网格,我们可以使用类似的原则。例如,我们可以设置一个内部域名系统区域,将服务名映射到127.0.0.0/8空间中的地址。然后在集群的每个节点上运行HAProxy,接受每个服务地址(在127.0.0.0/8子网中)上的连接,并将它们转发/负载平衡到适当的后端。HAProxy配置可以由confd管理,允许在etcd或Consul中存储后端信息,并在需要时自动将更新的配置推送到HAProxy。这就是Istio的工作原理!但是有一些不同之处:
  • 它使用Envoy Proxy而不是HAProxy
  • 它使用Kubernetes API而不是etcd或Consul来存储后端配置
  • 服务在内部子网中分配地址(Kubernetes集群IP地址),而不是127.0.0.0/8
  • 它有一个额外的组件(Citadel),用于在客户机和服务器之间添加相互的TLS身份验证
  • 它增加了对诸如断路、分布式跟踪、金丝雀部署等新特性的支持

让我们快速回顾一下这些差异。
## Envoy Proxy
Envoy Proxy由Lyft撰写。它与其他代理(如HAProxy、NGINX、Traefik)有许多相似之处,但Lyft编写它是因为它们需要当时这些代理中不存在的功能,而且构建一个新的代理比扩展现有代理更有意义。

Envoy可以单独使用。如果有一组给定的服务需要连接到其他服务,可以把它连接到Envoy,然后动态地配置和重新配置其他服务的Envoy的位置,而得到很多漂亮的额外的功能,比如域的可观测性。这里,没有使用定制的客户端库,也没有在代码中添加跟踪调用,而是将流量定向到Envoy,让它为我收集指标。

但Envoy也可以用作服务网格的数据平面。这意味着现在将由该服务网格的控制平面配置Envoy。
## 控制平面
说到控制平面,Istio依赖于Kubernetes API。这与使用confd没有太大的不同。confd依赖etcd或Consul来监视数据存储中的一组密钥。Istio依赖Kubernetes API来监视一组Kubernetes资源。

Aparte:我个人认为阅读Kubernetes API描述非常有帮助。

Kubernetes API服务器是一个“哑服务器”,它提供API资源上的存储、版本控制、验证、更新和监视语义。



Istio是为与Kubernetes合作而设计的;如果你想在Kubernetes之外使用它,则需要运行Kubernetes API服务器的实例(以及支持的etcd服务)。
## 服务地址
Istio依赖Kubernetes分配的集群IP地址,因此Istio得到一个内部地址(不在127.0.0.1/8范围)。

在没有Istio的Kubernetes集群上,前往给定服务的ClusterIP地址的流量被kube-proxy拦截,并发送到该代理的后端。更具体地说,如果你想确定技术细节:kube-proxy设置iptables规则(或IPVS负载平衡器,取决于它是如何设置的)来重写连接到集群IP地址的目标IP地址。

一旦Istio安装在Kubernetes集群上,就不会发生任何变化,直到通过将sidecar容器注入到使用者Pod中,显式地为给定的使用者甚至整个名称空间启用Istio。sidecar将运行一个Envoy实例,并设置一些iptables规则来拦截到其他服务的流量,并将这些流量重定向到Envoy。

结合Kubernetes DNS集成,这意味着我们的代码可以连接到一个服务名,一切都可以正常工作。换句话说,比如我们的代码向`http://api/v1/users/4242`发起一个请求,`api`将解析到10.97.105.48,一条iptables规则将解释连接到10.97.105.48并重定向到本地Envoy代理,本地代理将这个请求路由到实际的API后端。
## 额外的铃声和哨声
Istio还可以通过名为Citadel的组件通过mTLS(双向TLS)提供端到端加密和身份验证。

它还包括混合器,Envoy组件可以查询每一个请求,对请求进行一个临时的决定取决于各种因素,例如请求头、后端负载(别担心,有丰富的规定以确保混合高度可用,即使它休息,Envoy可以继续代理流量)。

当然,我提到了可观察性。Envoy在提供分布式跟踪的同时收集大量的度量指标。微服务架构,如果单个API请求必须经过微服务A、B、C和D,分布式跟踪将添加一个惟一的标识符请求进入系统,并保留标识符在子请求中,所有这些微服务允许收集所有相关的调用、延迟等。
# 自建还是购买
Istio以复杂著称。相比之下,使用我们今天拥有的工具,构建像我在本文开头描述的那样的路由网格相对比较简单。那么,构建我们自己的服务网格是否有意义呢?

如果我们有适度的需求(如果我们不需要可观察性,断路器,和其他细节),我们可能想建立自己的。但是如果我们正在使用Kubernetes,我们甚至可能不需要这样做,因为Kubernetes已经提供了基本的服务发现和负载平衡。

现在,如果我们有高级的需求,购买服务网格可能是一个更好的选择。(由于Istio是开源的,所以它并不总是真正的购买,但是我们仍然需要投入工程时间来理解它是如何工作、部署和运行的。)
#如何选择Istio、Linkerd和Consul Connect
到目前为止,我们只讨论了Istio,但它并不是唯一的服务网格。Linkerd是另一个流行的选择,还有Consul Connect

我们应该选哪一个呢?

实际上在这一点上我也不好说,我不认为我有足够的了解能够帮助任何人做决策。不过,已经有一些有趣的文章比较它们(12),甚至基准测试

一种值得一提并且很有潜力的方法是使用像SuperGloo这样的工具。SuperGloo提供了一个抽象层来简化和统一服务网格公开的API。我们可以使用SuperGloo提供的更简单的构造,并无缝地从一个服务网格切换到另一个服务网格,而不是学习各种服务网格的特定API(在我看来,相对复杂)。有点像我们有一个描述HTTP前端和后端的中间配置格式,能够为NGINX、HAProxy、Traefik、Apache生成实际配置

我已经使用SuperGloo稍微涉足Istio,在未来的博客文章中,我想说明如何使用SuperGloo将Isio或Linkerd添加到现有的集群中,以及后者是否能实现它的承诺,即允许我在不重写配置的情况下从一个路由网格切换到另一个。

如果你喜欢这篇文章,并且想让我尝试一些具体的场景,我很乐意听到你的消息!

原文链接:Containers, microservices, and service meshes

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

【腾讯云】#容器团队#高级容器研发#招聘

Shirleyee 发表了文章 • 1 个评论 • 209 次浏览 • 2019-05-24 10:33 • 来自相关话题

高级容器研发工程师 工作职责 负责公有云/私有云中 Kubernetes/Devops 等产品技术方案设计与研发工作;负责 Kubernetes 相关前沿技术规划和调研工作,从技术上保证产品的竞争力;负责与产品及客户沟通,判定需求的合理 ...查看全部
高级容器研发工程师
工作职责
  1. 负责公有云/私有云中 Kubernetes/Devops 等产品技术方案设计与研发工作;
  2. 负责 Kubernetes 相关前沿技术规划和调研工作,从技术上保证产品的竞争力;
  3. 负责与产品及客户沟通,判定需求的合理性,找出最合适的方式解决客户问题。
工作要求
  1. 3 年以上后端开发经验,Coding、Debug 能力强, 有丰富的架构设计经验;
  2. 熟悉 C/C++/Go/Java/Python/Ruby 等至少二种编程语言;
  3. 熟悉 Docker/Kubernetes/Swarm/Mesos 等技术;
  4. 熟悉 Jenkins/ELK/Prometheus 等技术优先;
  5. 熟悉 AWS/Google Cloud 等云计算厂商产品优先。


有意请戳:
Wechat:13723737494
Email:Shirleyeee@foxmail.com

Rainbond 5.1.4发布,复杂微服务架构整体升级和回滚

goodrain 发表了文章 • 0 个评论 • 188 次浏览 • 2019-05-21 09:34 • 来自相关话题

Rainbond 5.1.4发布, 复杂微服务架构整体升级和回滚 今天为大家带来Rainbond 5.1系列第四个更新版本,本次版本更新的主要内容是复杂微服务架构应用整体升级和回滚,能实现复杂微服务架构的持续交付,和复杂架构企业级应用 ...查看全部
Rainbond 5.1.4发布, 复杂微服务架构整体升级和回滚

今天为大家带来Rainbond 5.1系列第四个更新版本,本次版本更新的主要内容是复杂微服务架构应用整体升级和回滚,能实现复杂微服务架构的持续交付,和复杂架构企业级应用快速交付和升级,另外还有一些小的优化和BUG的修复。如果你想和更多微服务技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

Rainbond是开源的企业应用云操作系统,支撑企业应用的开发、架构、交付和运维的全流程,通过无侵入架构,无缝衔接各类企业应用,底层资源可以对接和管理IaaS、虚拟机和物理服务器。

复杂微服务架构应用整体升级和回滚

面对复杂的微服务架构,微服务组件可能几十个,服务之间存在业务依赖;微服务的版本管理复杂;开发测试流程低效,针对以上问题,单个微服务管理的模式已经不适用,需要考虑微服务架构整体管理。这次的更新能实现复杂微服务架构的整体版本,微服务独立开发,测试环境和生产环境整体升级和回滚,升级的过程只更新变化的服务和配置,过程滚动更新,实现业务不间断升级。



升级和回滚的过程通过Rainbond应用市场实现,Rainbond应用市场定义了一种对应用的存储、共享、交付、管理途径.



Rainbond应用市场与传统意义上的镜像仓库不同之处在于,它基于镜像仓库、包仓库和对象存储等存储系统支持,定义了支持大型、分布式数字化业务系统的标准云原生应用模型,并针对应用模型提供创建、发布、存储、交付、安装、升级等一系列业务支持,对内可作为以便捷灵活的方式共享企业创造的业务系统、中间件的业务性管理平台,对外可作为根据行业特性构建行业话交付标准、交付流程和交付路径的基础,应用市场的最大优点在于它涵盖的不仅是服务组件和应用(业务系统),甚至于解决方案都可以支持一键分享、一键安装使用,极大的便利用户,只需安装使用,使用者不需要懂技术。

在5.1.4之前, rainbond仅仅支持对云市应用中单个服务的升级, 如果想要升级整个云市应用, 则需要单独地对每个服务进行升级, 且无法升级新添加的服务. 这给各位用户的使用带来了极大的不便. 为了让用户有的操作更加的简单, 提高使用体验, 我们在5.1.4版本中, 对应用市场进行了改造升级.

功能特性

  • 灵活的升级方式: 可以自由地选择需要升级的服务, 可以全部升级也可以部份升级.
  • 创建新添加服务: 除了可以升级已有的服务外, 还可以创建旧版本没有, 但是新版本有的服务.
  • 详细的变更信息: 在升级界面中, 可以查看当前版本与新版本服务之间属性的变更.
  • 详细的升级记录: 对每次升级操作, rainbond都进行了详细的记录, 包括: 升级操作的时间, 版本号的变更和各服务属性的变更信息等.
  • 自动回滚: 在应用升级的过程中, 如果程序发生了异常, 会回滚到升级前的状态, 避免只升级部分属性或服务.
  • 手动回滚: 升级成功后, 如果新版本有缺陷导致各个服务无法正常工作, 或者你更倾向升级前的版本, 那么可以选择手动回滚, 回到之前的版本.
简单的演示更详细的说明, 请参考: 服务升级文档其他改进
  • 第三方服务新添加实例地址时, 允许地址中带有端口
  • 镜像服务支持修改镜像仓库帐号, 密码等信息
  • grctl命令行工具增加身份属性gateway
  • 将自定义网关策略的域名以环境变量的方式注入到服务中(相关文档)
  • 将环境变量,配置文件等配置信息综合为环境配置
  • 分享应用时支持定义不分享的服务
  • 支持服务链接信息和环境变量的相互转移
  • 关闭或重启服务时, 增加二次确认, 防止误操作
安装方面:
  • 优化安装时初始化数据中心流程
  • 优化调整安装任务结构,调整离线镜像文件路径
  • 支持调整网络类型
  • 优化部分组件配置参数
  • 优化安装过程中宿主机IP段与容器ip段冲突问题
BUG修复
  • 【重要】修复了关闭服务时, pod无法被删除或删除需要花费比较多时间的问题
  • 【重要】修复了多管理节点中, 某个节点rbd-hub服务异常了,但gateway没有将其下线导致goodrain.me服务异常的问题
  • 修复了第三方服务的网关访问策略控制错误
  • 修复了删除端口报系统异常的错误
  • 修复了编辑HTTPs网关策略, 无法勾选 HTTP rewriet HTTPs 的问题
  • 修复了更改构建源后无法重新检测语言的错误
  • 修复了无法修改健康检测参数的错误
  • 修复了云市应用版本号显示不全的问题
  • 修复了添加镜像服务时, 没有高级选项按钮的问题
  • 修复了构建源中镜像Tag显示不全的问题
  • 修复了创建应用时勾选的是有状态应用,创建成功后却是无状态应用的问题
  • 修复了无法将无状态应用修改为有状态应用的问题
  • 修复了禁止调度计算节点后, 导致可用资源统计错误的问题
  • 修复了第三方服务TCP访问策略状态错误且无法操作的问题
  • 修复了网关策略参数配置中Websocket不生效的问题
  • 修复了云市应用导出的docker-compose.yaml中的镜像有误的问题
  • 修复了环境变量名格式验证有误的问题, 支持带"."的环境变量名



安装和升级

新集群安装参考Rainbond安装文档:https://www.rainbond.com/docs/quick-start/rainbond_install/

升级已有集群到5.1.4版本: https://www.rainbond.com/docs/user-operations/upgrade/5.1.3-5.1.4/


devicemapper存储,容器mount点的问题

徐新坤 回复了问题 • 2 人关注 • 3 个回复 • 3802 次浏览 • 2019-04-02 18:01 • 来自相关话题

Rainbond v5.1.2发布,微服务架构应用便捷管理和交付

goodrain 发表了文章 • 0 个评论 • 497 次浏览 • 2019-04-02 08:48 • 来自相关话题

Rainbond v5.1.2发布,微服务架构应用便捷管理和交付 Rainbond是开源的企业应用云操作系统,支撑企业应用的开发、架构、交付和运维的全流程,通过无侵入架构,无缝衔接各类企业应用,底层资源可以对接和管理IaaS、虚拟机和 ...查看全部
Rainbond v5.1.2发布,微服务架构应用便捷管理和交付

Rainbond是开源的企业应用云操作系统,支撑企业应用的开发、架构、交付和运维的全流程,通过无侵入架构,无缝衔接各类企业应用,底层资源可以对接和管理IaaS、虚拟机和物理服务器。

2019年3月,Rainbond发布v5.1版本,经过1个月在上百家企业的实际使用,团队持续跟进版本缺陷,迄今为止发布了2个BUG修复版本。

Rainbond开源产品的目标是成为企业IT系统的云操作系统,作为基础平台支持各行各业的企业用户,优化IT软件开发企业的开发流程和交付流程,做到一站式开发和交付。作为广大行业IT厂商的合作伙伴,为其提供稳定的、好用的、高效的基础平台,服务于行业软件的架构、开发和交付,Rainbond在这条路上砥砺前行。在V5.1版本中我们引入了以下功能体系来服务用户。

支持第三方微服务集成和管理

Rainbond在众多的企业中落地使用的过程中出现了两类共同的问题:

  • 循序渐进的迁移策略,已经上Rainbond的服务如何与遗留服务通信和统一管理。
  • Rainbond应用网关很好用,但是遗留的服务没办法与Rainbond上的服务共享外网端口或域名。
Rainbond V5.1版本中在提出了第三方服务的概念,即将运行于Rainbond集群外且与Rainbond可以正常网络通信的服务称为第三方服务。对于此类服务,我们支持以静态注册、动态注册(Etcd、Zookeeper、Consule)的方式来获取第三方服务的通信地址,赋予第三方服务以下能力:[list=1]
  • 集成Rainbond内置的ServiceMesh架构,与集群内服务无缝互联,并提供服务通信治理功能。
  • 集成Rainbond 应用网关,统一管理服务外网访问。
  • 运行于不同环境和系统的业务系统统一管理和可视化,形成完整业务架构。
  • 更多第三方服务的说明和支持情况,见文档: Rainbond支持第三方服务集成此功能发布之后,在阿里云运行Rainbond的企业用户可以更便捷的对接阿里云的RDS资源。更加充分的利用云资源以降低企业维护IT系统的成本。支持微服务启动顺序在一个复杂微服务架构下,一些服务必须依赖于另一些服务才能正常工作,如何根据依赖关系处理服务的启动顺序是简化复杂微服务架构管理的关键。Rainbond实现了根据依赖关系自动处理服务的启动顺序,当被依赖的服务正常工作后,才会启动后续服务,依次迭代启动所有服务。这方面的功能实现主要在体现Rainbond的主要抽象层次,我们比较清楚的是docker的抽象层次是容器级别,kubernetes的抽象层次主要可以认为是服务级别(Pod级别),Rainbond的关键抽象层是更高的应用级,特别是微服务架构盛行的今天,服务组件多,对于大多数业务程序都需要手动的控制启动顺序来确保整个业务的正常工作。Rainbond能够做到能够做到在应用级整体控制生命周期和其他自动化运维。此功能发布后在某工业互联网软件企业用户中创造了较大价值,一个完整的工业互联网APP开发平台由20多个服务组件构成,过去他们每交付一个工厂的交付成本需要一个熟练的交付工程师出差调试大概2天才能基本完成。其中主要的就是需要熟练掌握服务之间的依赖关系,启动顺序,服务配置,这还是建立在他们产品的成熟度已经比较高。后期这一套业务系统交付用户的运维成本也非常大。当使用Rainbond作为基础交付平台以后,他们通过1天的时间将所有服务完整部署的Rainbond并发布于应用市场。由于Rainbond完整的应用系统生命周期控制和启动顺序控制,实现了完整的工业互联网APP开发平台的一键部署,10分钟完成业务可工作。对于最终用户来说也可以更加直观的运维管理业务系统。源码构建系统升级基于源代码持续构建服务是Rainbond用户使用最多的功能之一,既5.0版本作较大升级以后,5.1版本继续带来升级,在Java、PHP、NodeJS等常用语言方面支持更加完善:
    • 增加对NodeJS前端项目源码类型的支持,可以部署Vue和React。
    • Java-Maven增加maven编译参数的UI配置。
    • 所有Java类型支持OpenJDK版本和OracleJDK版本的UI配置。
    • PHP、静态语言支持UI选择中间件类型和版本。
    • 将公共代码模块和资源从云端本地化、更好的支持离线环境下源码构建
    • 支持服务源码类型重新检测和变更

    另外Rainbond对各类型源码的支持规范文档进行了更加细致的描述,请参考 Rainbond源码支持规范

    从源码构建主要服务于开发场景,目前还是有较多的企业开发者出于学习成本无法定义优质的Dockerfile,直接使用Rainbond提供的基于源代码构建的机制是开发者使用Rainbond发布服务最易用的方式。

    我们从用户使用中总结发现目前开发语言最多的依然是Java,因此Rainbond对Java语言支持的持续优化依然是V5.1版本的重点,其中有大量用户使用的是SpringCloud,因此Rainbond将在V5.1后续小版本中增加直接基于Maven源码创建多个服务模块的便捷服务创建方式,进一步提供用户创建服务的效率。

    除了上述提到的Rainbond V5.1版本大的功能变化以外,Rainbond还进行了大量的功能改进和优化。详细参考:

    https://github.com/goodrain/rainbond/releases/tag/v5.1.0-release

    https://github.com/goodrain/rainbond/releases/tag/v5.1.1-release

    https://github.com/goodrain/rainbond/releases/tag/v5.1.2-release

    开始你的Rainbond之旅

    你的企业是否也遇到过上文提到的种种影响你的产品开发和交付的效率的问题,不妨使用Rainbond来优化一下你的现有模式和体验。

    Rainbond 安装参考手册 https://www.rainbond.com/docs/quick-start/rainbond_install/

    Rainbond 使用参考手册 https://www.rainbond.com/docs/user-manual/

    Rainbond 进阶场景手册 https://www.rainbond.com/docs/advanced-scenarios/

    Docker--容器技术

    CCE_SWR 发表了文章 • 0 个评论 • 591 次浏览 • 2019-03-13 15:23 • 来自相关话题

    什么是“容器”和“虚拟机” 容器和虚拟机它们的目的很相似:即将应用程序和它的依赖放到一个可以在任何环境运行的自足单元中。 此外,容器和虚拟机消除了对物理硬件的需求,从而在能源消耗和成本效益方面能让我们更有效地使用计算资源 ...查看全部
    什么是“容器”和“虚拟机”
    容器和虚拟机它们的目的很相似:即将应用程序和它的依赖放到一个可以在任何环境运行的自足单元中。

    此外,容器和虚拟机消除了对物理硬件的需求,从而在能源消耗和成本效益方面能让我们更有效地使用计算资源,

    容器和虚拟机的主要区别在于它们的架构方式。让我们继续深入了解。

    虚拟机
    虚拟机在本质上是对现实中计算机的仿真,它会像真实的计算机一样执行程序。使用 “hypervisor” 可以将虚拟机运行于物理机上。hypervisor 可以在主机运行,也可以在“裸机”上运行。

    让我们来揭开这些术语的面纱:

    hypervisor(之后都以虚拟机管理程序称呼)是能让虚拟机在其上运行的软件,固件或者硬件。虚拟机管理程序本身会在物理计算机上运行,称为“主机”。主机为虚拟机提供资源,包括 RAM 和 CPU。这些资源在虚拟机之间被划分并且可以根据需要进行分配。所以如果一个虚拟机上运行了资源占用更大的应用程序,相较于其它运行在同一个主机的虚拟机你可以给其分配更多的资源。

    运行在主机上的虚拟机(再次说明,通过使用虚拟机管理程序)通常也被叫做“访客机”。访客机包含了应用以及运行这个应用所需要的全部依赖(比如:系统二进制文件和库)。它还带有一个自己的完整虚拟化硬件栈,包括虚拟化的网络适配器,储存和 CPU-这意味着它还拥有自己成熟的整个访客操作系统。从虚拟机内部来看,访客机的操作都认为其使用的都是自己的专用资源。从外部来看,我们知道它是一个虚拟机-和其它虚拟机一起共享主机提供的资源。

    就像前面所提到的,访客机既可以运行在托管的虚拟机管理程序上,也可以运行在裸机虚拟机管理程序上。它们之间存在一些重要的差别。

    首先,托管的虚拟化管理程序是在主机的操作系统上运行。比如说,可以在一台运行 OSX 操作系统的计算机的系统上安装虚拟机(例如:VirtualBox 或者 VMware Workstation 8)。虚拟机无法直接访问硬件,因此必须通过主机上运行的操作系统访问(在我们的例子中,也就是 Mac 的 OSX 操作系统)。

    托管虚拟机管理程序的好处是底层硬件并不那么重要。主机的操作系统会负责硬件的驱动而不需要管理程序参与。因此这种方式被认为具备更好的“硬件兼容性”。在另一方面,在硬件和管理程序之间这个额外的附加层会产生更多的资源开销,这会降低虚拟机的性能。

    裸机虚拟机管理程序通过直接在主机硬件上安装和运行来解决这个性能问题。因为它直接面对底层的硬件,所以并不需要运行在主机的操作系统之上。在这种情况下,安装在主机上第一个作为操作系统运行的就是这个裸机虚拟机管理程序。与托管虚拟机管理程序不同,它有自己的设备驱动直接与每个组件交互,以执行任何 I/O,处理或特定于操作系统的任务。这样可以获得更好的性能,可伸缩性和稳定性。这里的权衡在于其对硬件的兼容性有限,因为裸机虚拟机管理程序内置的设备驱动只有那么多。

    在讨论了虚拟机管理程序之后,你可能想知道为什么我们需要在虚拟机和主机之间这个额外的“虚拟机管理程序”层。

    好吧,虚拟机管理程序在其中确实发挥了重要的作用,由于虚拟机拥有自己的虚拟操作系统,管理程序为虚拟机管理和执行访客操作系统提供了一个平台。它允许主机与作为客户端运行的虚拟机之间共享其资源。


    1.png



    虚拟机图示

    正如你可以在图示中所看到的,VMS 会为每个新的虚拟机打包虚拟硬件,一个内核(即操作系统)和用户空间。

    容器
    与提供硬件虚拟化的虚拟机不同,容器通过抽象“用户空间”来提供操作系统级别的虚拟化。当我们详解容器这个术语的时候你就会明白我的意思。

    从所有的意图和目的来看,容器看起来就像一个虚拟机。比如说,它们有执行进程的私有空间,可以使用 root 权限执行命令,具有专有的网络接口和 IP 地址,允许自定义路由和 iptable 规则,可以挂载文件系统等。

    容器和虚拟机之间的一个重要区别在于容器和其它容器共享主机系统的内核。


    2.png



    容器图示

    这图表明容器只会打包用户空间,而不是像虚拟机那样打包内核或虚拟硬件。每个容器都有自己独立的用户空间从而可以让多个容器在单个主机上运行。我们可以看到所有操作系统级别的体系架构是所有容器共享的。要从头开始创建的部分只有 bins 和 libs 目录。这就是容器如此轻巧的原因。

    Docker 是从哪来的?
    Docker 是基于 Linux 容器技术的开源项目。它使用 Luinux 的内核功能(如命名空间和控制组)在操作系统上创建容器。

    容器已经远远不是一个新技术:Google 已经使用他们自己的容器技术好多年了。其它的容器技术包括 Solaris Zones、BSD jails 和 LXC 也已经存在好多年。

    那么为啥 Docker 会突然取得成功呢?

    使用简单:Docker 使得任何人(开发人员,运维,架构师和其他人)都可以更轻松的利用容器的优势来快速构建和测试可移植的应用程序。它可以让任何人在他们的笔记本电脑上打包应用程序,不需要任何修改就可以让应用运行在公有云,私有云甚至裸机上。Docker 的口头禅是:“一次构建,处处运行”。

    速度:Docker 容器非常轻量级和快速。因为容器只是运行在内核上的沙盒环境,因此它们占用的资源更少。与可能需要更多时间来创建的虚拟机相比,你可以在几秒钟内创建一个 Docker 容器,因为虚拟机每次都必须启动一个完整的操作系统。

    Docker Hub:Docker 用户也可以从日益丰富的 Docker Hub 生态中受益,你可以把 Docker Hub 看作是 “Docker 镜像的应用商店”。Docker Hub 拥有数万个由社区构建的公共镜像,这些镜像都是随时可用的。在其中搜索符合你需求的镜像非常容易,你只需要准备拉取镜像而且几乎不需要任何修改。

    模块化和可扩展性:Docker 可以让你轻松地把应用程序按功能拆分为单个独立的容器。比如说,你的 Postgre 数据库可以运行在一个容器中,Redis 服务运行在另一个容器中,而 Node.js 应用运行在另一个容器中。使用 Docker,将这个容器链接在一起以创建你的应用程序将会变得更简单,同时在将来可以很轻松地扩展和更新单独的组件。最后但并不重要的是,有谁不喜欢 Docker 的鲸鱼(Docker 的标志)呢?:)


    3.png

    Kubernetes taint & toleration

    jonathan 发表了文章 • 0 个评论 • 538 次浏览 • 2019-03-09 22:33 • 来自相关话题

    一、概述 前一篇文章讲解了 Kubernetes 亲和性调度, 所涉及的内容都是描述 pod 的属性,来声明此 pod 希望调度到哪类 nodes。而本文介绍的 `Taint(污点)` 刚好相反,它是node 的一个属性,允许 node 主动排斥 ...查看全部
    一、概述
    前一篇文章讲解了 Kubernetes 亲和性调度, 所涉及的内容都是描述 pod 的属性,来声明此 pod 希望调度到哪类 nodes。而本文介绍的 `Taint(污点)` 刚好相反,它是node 的一个属性,允许 node 主动排斥 pod 的调度。
    对应的 k8s 又给 pod 新增了配套属性 `toleration(容忍)` ,用于表示这些 pod 可以(但不强制要求)被调度到具有相应 taints 的 nodes 上。
    这两者经常一起搭配,来确保不将 pod 调度到不合适的 nodes。

    看下 taint & toleration 结构体,下面足点介绍时会涉及相关字段。

    type Taint struct {
    Key string
    Value string
    Effect TaintEffect
    // add taint 的时间点
    // 只有 Effect = NoExecute, 该值才会被 nodeController 写入
    TimeAdded *metav1.Time
    }

    type Toleration struct {
    Key string
    Operator TolerationOperator
    Value string
    Effect TaintEffect
    // 容忍时间
    TolerationSeconds *int64
    }

    type TaintEffect string

    const (
    TaintEffectNoSchedule TaintEffect = "NoSchedule"
    TaintEffectPreferNoSchedule TaintEffect = "PreferNoSchedule"
    TaintEffectNoExecute TaintEffect = "NoExecute"
    )

    type TolerationOperator string

    const (
    TolerationOpExists TolerationOperator = "Exists"
    TolerationOpEqual TolerationOperator = "Equal"
    )


    二、Taint
    我们可以对 node 设置多个 taints,当然也可以在 pod 配置相同个数的 tolerations。影响调度和运行的具体行为,我们可以分为以下几类:

    • 如果至少有一个 `effect == NoSchedule` 的 taint 没有被 pod toleration,那么 pod 不会被调度到该节点上。
    • 如果所有 `effect == NoSchedule` 的 taints 都被 pod toleration,但是至少有一个 `effect == PreferNoSchedule` 没有被 pod toleration,那么 k8s 将努力尝试不把 pod 调度到该节点上。
    • 如果至少有一个 `effect == NoExecute` 的 taint 没有被 pod toleration,那么不仅这个 pod 不会被调度到该节点,甚至这个节点上已经运行但是也没有设置容忍该污点的 pods,都将被驱逐。
    三、Toleration再看下 PodSpec 配置 Tolerations,其中的 key、value、effect 与 Node Taint 设置需要保持一致,operator 支持两类:
    • Exists: 这个配置下,不需要指定 value。
    • Equal: 需要配置 value 值。(operator 的默认值)
    有几个特殊情况:
    • key 为空并且 operator 等于 Exists,表示匹配了所有的 keys,values 和 effects。换句话说就是容忍了所有的 taints。
    ```tolerations:
    • operator: "Exists"
    ```
    • effect 为空,则表示匹配所有的 effects(NoSchedule、PreferNoSchedule、NoExecute)
    ```tolerations:
    • key: "key"
    operator: "Exists"```还有一个 `TolerationSeconds`,该值与 effect 为 `NoExecute` 配套使用。用来指定在 node 添加了 effect = NoExecute 的 taint 后,能容忍该 taint 的 pods 可停留在 node 上的时间。例如:```tolerations:
    • key: "key1"
    operator: "Equal" value: "value1" effect: "NoExecute" tolerationSeconds: 3600```表示如果这个 pod 已经运行在 node 上并且该 node 添加了一个对应的 taint,那么这个 pod 将会在 node 上停留 3600 秒后才会被驱逐。但是如果 taint 在这个时间前被移除,那么这个 pod 也就不会被驱逐了。来个比较形象的图,描述下:

    该图参考: Taints and tolerations, pod and node affinities demystified · Banzai Cloud

    四、内置行为kubernetes 1.6 版本,node controller 会跟进系统情况自动设置 node taint 属性。内置 taint key 如下:
    • node.kubernetes.io/not-ready: 节点尚未就绪
    • node.kubernetes.io/unreachable: 节点无法被访问
    • node.kubernetes.io/unschedulable: 节点不可调度
    • node.kubernetes.io/out-of-disk: 节点磁盘不足
    • node.kubernetes.io/memory-pressure: 节点有内存压力
    • node.kubernetes.io/disk-pressure: 节点有磁盘压力
    • node.kubernetes.io/network-unavailable: 节点网络不可用
    • node.kubernetes.io/pid-pressure: 节点有 pid 压力
    • node.cloudprovider.kubernetes.io/uninitialized: 云节点未初始化
    • node.cloudprovider.kubernetes.io/shutdown: 云节点已下线
    kubernetes 会通过 `DefaultTolerationSeconds admission controller` 为创建的 pod 添加两个默认的 toleration: `node.kubernetes.io/not-ready` 和 `node.kubernetes.io/unreachable`,并且设置 `tolerationSeconds = 300`。当然这个默认配置,用户可以自行覆盖。这些自动添加的默认配置,确保 node 出现问题后,pod 可以继续在 node 上停留 5 分钟。

    DefaultTolerationSeconds admission controller 设置的 tolerationSeconds 值,也可以由用户指定。> 具体参考: https://github.com/kubernetes/kubernetes/blob/master/plugin/pkg/admission/defaulttolerationseconds/admission.go

    还有一个默认行为,就是 DaemonSet。所有 DaemonSet 创建的 pod 都会添加两个 toleration: `node.alpha.kubernetes.io/unreachable` 和 `node.alpha.kubernetes.io/notReady`。设置 `effect = NoExecute`,并且不指定 `tolerationSeconds`。目的是确保在 node 出现 unreachable 或 notReady 的问题时,DaemonSet Pods 永远不会被驱逐。# 示例常见的示例如下:
    • `专用节点`
    - 如果你希望将一组节点专用于特定的用户,那可以将这些节点设置 taints: `kubectl taint nodes nodename dedicated=groupName:NoSchedule`, 然后为 pods 设置对应的 tolerations。 - 如果你希望节点被专用并且确保服务仅使用这批节点,那么你还应该向这批节点设置 labels (`dedicated=groupName`),并且为对应的 pods 设置 NodeAffinity,以控制 pods 只能跑到这批节点上。
    • `具有特殊硬件的节点`
    - 有部分带有特殊硬件的节点,比如 GPU、FPGA 等,要确保不将不需要专用硬件的 pods 调度到这些节点。也可以和 `专有节点` 一样的方式设置 taints:`kubectl taint nodes nodename special=true:NoSchedule` 或 `kubectl taint nodes nodename special=true:PreferNoSchedule`。建议还可以通过 Extended ResourcesExtendedResourceToleration admission controller 来更方便的调度依赖特殊硬件的 pods,而不需要手动添加容器的 toleration
    • `基于 taint 的驱逐`
    - 前面也提到了,可以通过 `NoExecute taint` 来驱逐节点上已经运行的 pods。 五、参考资料
    条新动态, 点击查看
    <ol><li>如果容器里没有运行sshd,可以登录宿主机后执行`docker exec -it CONTAINER_NAME_OR_ID /bin/sh`&amp... 显示全部 »
    <ol><li>如果容器里没有运行sshd,可以登录宿主机后执行`docker exec -it CONTAINER_NAME_OR_ID /bin/sh`</li><li>可以在容器里运行个sshd,通过SSH客户端登录。 但由于是用的host网络,所以容器里的sshd进程无法使用默认端口,需要修改其配置文件把端口改成非22端口</li></ol>
    当然是“容器”了! <ol><li>对于三个字的“集装箱”来说,两个字更显得轻量级。</li&amp... 显示全部 »
    当然是“容器”了! <ol><li>对于三个字的“集装箱”来说,两个字更显得轻量级。</li><li>符合“大部分“人的听觉习惯,而且说起来也朗朗上口。</li><li>咬文嚼字的话,Docker容器不仅仅是大型容器,很多都是小型甚至微型服务容器。而(http://xh.5156edu.com/html5/267011.html)的定义:具有一定规格、便于机械装卸、可以重复使用的装运货物的<strong>大型容器</strong>,形状像箱子,多用金属材料制成。有的地区叫货柜。 </li></ol> 因此,用“容器”更贴切。

    容器、微服务与服务网格

    cleverlzc 发表了文章 • 0 个评论 • 256 次浏览 • 2019-05-26 11:03 • 来自相关话题

    【编者的话】本文结合dotCloud的发展为例,阐述了一个基本的服务网格应该具备什么样的能力,对诸如Istio、Linkerd、Consul Connect等现代服务网格系统的基本模式进行了阐述,最后对于“自建还是购买(或者使用开源)”这个老生常谈的话题作者给 ...查看全部
    【编者的话】本文结合dotCloud的发展为例,阐述了一个基本的服务网格应该具备什么样的能力,对诸如Istio、Linkerd、Consul Connect等现代服务网格系统的基本模式进行了阐述,最后对于“自建还是购买(或者使用开源)”这个老生常谈的话题作者给出了自己的见解。

    如你所知,已经有很多关于服务网格的资料(1234),但这是另外一篇。是的!但是为什么会有这篇文章呢?因为我想给你们一些不同的视角,他们希望服务网格在10年前就已经存在,远早于Docker和Kubernetes这样的容器平台的兴起。我并不是说这个视角比其他视角更好或更差,但是由于服务网格是相当复杂的“野兽”,所以我相信多种视角有助于更好地理解它们。

    我将讨论dotCloud平台,这是一个建立在100多个微服务之上的平台,支持数千个运行在容器中的生产应用程序;我将解释在构建和运行它时所面临的挑战;以及服务网格会(或不会)提供帮助。
    # dotCloud的历史
    我已经写过关于dotCloud平台的历史和它的一些设计选择,但是我没有过多地讨论它的网络层。如果你不想深入了解我之前关于dotCloud的博客,你需要知道的是它是一个PaaS,允许客户运行各种应用程序(Java、PHP、Python等),支持广泛的数据服务(MongoDB、MySQL、Redis等)以及类似于Heroku的工作流程:你可以将代码推送到平台,平台将构建容器映像,并部署这些容器映像。

    我将告诉你流量是如何在dotCloud平台上路由的;不是因为它是特别棒或其他什么(我认为现在是比较合适的时间),但主要是因为,如果一个普通的团队需要一种在一个微服务群或一个应用程序群之间路由流量的方法,那么这种设计可以在短时间内用现在已有的工具轻松实现。因此,它将为我们提供一个很好的比较点,“如果我们破解它,我们会得到什么”和“如果我们使用现有的服务网格,我们会得到什么”,也就是老生常谈的“构建与购买”的困境。
    # 托管应用的流量路由
    部署在dotCloud上的应用程序会暴露HTTP和TCP端点。

    HTTP端点被动态地添加到Hipache负载平衡器集群的配置中。这与我们今天使用Kubernetes Ingress资源和Traefik这样的负载平衡器可以实现的功能类似。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

    只要域名指向dotCloud的负载平衡器,客户端就可以使用它们的关联域名连接到HTTP端点。这里没有什么特别的。

    TCP端点与端口号相关联,然后端口号通过环境变量与该堆栈上的所有容器通信。

    客户端可以使用指定的主机名(类似于gateway-X.dotcloud.com)和端口号连接到TCP端点。

    该主机名将解析为一个“nats”服务器集群(与NATS没有任何关系),该集群将把传入的TCP连接路由到正确的容器(或者,在负载平衡服务的情况下,路由到正确的容器)。

    如果你熟悉Kubernetes,这可能会让你想起NodePort服务。

    dotCloud平台没有集群IP服务的等价物:为了简单起见,从内部和外部访问服务的方式是相同的。

    这非常简单,最初的HTTP和TCP路由网格的实现可能都是几百行Python代码,使用相当简单(我敢说,很天真)的算法,但是随着时间的推移,它们不断发展,以处理平台的增长和额外的需求。

    它不需要对现有应用程序代码进行大量重构。十二因素应用程序尤其可以直接使用通过环境变量提供的地址信息。
    # 它与现代服务网络有何不同?
    可观察性有限。对于TCP路由网格根本没有度量标准。至于HTTP路由网格,后来的版本提供了详细的HTTP度量,显示错误状态码和响应时间;但是现代服务网格的功能远远不止于此,它还提供了与度量收集系统(例如Prometheus)的集成。

    可观察性非常重要,不仅从操作角度(帮助我们解决问题),还可以提供安全的蓝/绿部署金丝雀部署等功能。

    路由效率也受到限制。在dotCloud路由网格中,所有流量都必须经过一组专用路由节点。这意味着可能跨越几个AZ(可用性区域)边界,并显著增加延迟。我记得对一些代码进行故障排除,这些代码发出100多个SQL请求来显示给定的页面,并为每个请求打开了到SQL服务器的新连接。在本地运行时,页面会立即加载,但在dotCloud上运行时,需要几秒钟,因为每个TCP连接(以及随后的SQL请求)都需要几十毫秒才能完成。在这种特定的情况下,使用持久连接起了作用。

    现代服务网络做得更好。首先,通过确保连接在源位置路由。逻辑流仍然是客户端-->网格-->服务,但是现在网格在本地运行,而不是在远程节点上运行,因此客户端-->网格连接是本地连接,因此速度非常快(微秒而不是毫秒)。

    现代服务网格还实现了更智能的负载平衡算法。通过监控后端的运行及健康状况,它们可以在更快的后端上发送更多的流量,从而提高整体性能。

    随着现代服务网络的出现,安全性也越来越强。dotCloud路由网格完全在EC2 Classic上运行,并且没有加密流量(假设如果有人设法嗅探EC2上的网络流量,那么无论如何都会遇到更大的问题)。现代服务网格可以透明地保护我们所有的通信,例如通过相互的TLS身份验证和随后的加密。
    # 平台服务的流量路由
    OK,我们已经讨论了应用程序是如何通信的,但是dotCloud平台本身呢?

    平台本身由大约100个微服务组成,负责各种功能。其中一些服务接受来自其他服务的请求,而其中一些服务是后台工作应用,它们将连接到其他服务,但不能自己接收连接。无论哪种方式,每个服务都需要知道它需要连接到的地址的端点。

    许多高级服务都可以使用上面描述的路由网格。事实上,dotCloud平台的100多个微服务中有很大一部分是作为常规应用程序部署在dotCloud平台上的。但是少数低级服务(特别是那些实现路由网格的服务)需要一些更简单的东西,需要更少的依赖关系(因为它们不能依靠自己来运行;这是一个老生常谈的“先有鸡还是先有蛋”的问题)。

    通过直接在几个关键节点上启动容器,而不是依赖于平台的构建器、调度程序和运行器服务,部署了这些底层的基本平台服务。如果你想要与现代容器平台进行比较,这就像直接在节点上运行Docker来启动我们的控制平面,而不是让Kubernetes为我们做这件事。这与kubeadmbootkube在引导自托管集群时使用的静态Pod的概念非常相似。

    这些服务以一种非常简单和粗糙的方式被公开:有一个YAML文件列出了这些服务,将它们的名称映射到它们的地址;作为其部署的一部分,这些服务的每个使用者都需要一份该YAML文件的副本。

    一方面,这是非常强大的,因为它不涉及像ZooKeeper那样维护外部键值存储(记住,etcd或Consul在那个时候不存在)。另一方面,这使得服务难以移动。每次移动服务时,它的所有消费者都需要接收更新的YAML文件(并且可能会重新启动)。不太方便!

    我们开始实现的解决方案是让每个消费者都连接到一个本地代理。使用者不需要知道服务的完整地址+端口,只需要知道它的端口号,并通过localhost进行连接。本地代理将处理该连接,并将其路由到实际后端。现在,当一个后端需要移动到另一台机器上,或按比例放大或缩小,而不是更新它的所有消费者,我们只需要更新所有这些本地代理;我们不再需要重新启动消费者。

    (还计划将流量封装在TLS连接中,并在接收端使用另一个代理来打开TLS并验证证书,而不涉及接收服务,该服务将被设置为仅在本地主机上接受连接。稍后会详细介绍。)

    这与AirBNB的SmartStack非常相似;与SmartStack实现并部署到生产环境的显著区别是,当dotCloud转向Docker时,它的新的内部路由网格被搁置了。

    我个人认为SmartStack是诸如Istio、Linkerd、Consul Connect等系统的先驱之一,因为所有这些系统都遵循这种模式:

    • 在每个节点上运行代理
    • 消费者连接到代理
    • 后端改变时,控制平面更新代理的配置
    # 今天实现一个服务网格如果我们今天必须实现类似的网格,我们可以使用类似的原则。例如,我们可以设置一个内部域名系统区域,将服务名映射到127.0.0.0/8空间中的地址。然后在集群的每个节点上运行HAProxy,接受每个服务地址(在127.0.0.0/8子网中)上的连接,并将它们转发/负载平衡到适当的后端。HAProxy配置可以由confd管理,允许在etcd或Consul中存储后端信息,并在需要时自动将更新的配置推送到HAProxy。这就是Istio的工作原理!但是有一些不同之处:
    • 它使用Envoy Proxy而不是HAProxy
    • 它使用Kubernetes API而不是etcd或Consul来存储后端配置
    • 服务在内部子网中分配地址(Kubernetes集群IP地址),而不是127.0.0.0/8
    • 它有一个额外的组件(Citadel),用于在客户机和服务器之间添加相互的TLS身份验证
    • 它增加了对诸如断路、分布式跟踪、金丝雀部署等新特性的支持

    让我们快速回顾一下这些差异。
    ## Envoy Proxy
    Envoy Proxy由Lyft撰写。它与其他代理(如HAProxy、NGINX、Traefik)有许多相似之处,但Lyft编写它是因为它们需要当时这些代理中不存在的功能,而且构建一个新的代理比扩展现有代理更有意义。

    Envoy可以单独使用。如果有一组给定的服务需要连接到其他服务,可以把它连接到Envoy,然后动态地配置和重新配置其他服务的Envoy的位置,而得到很多漂亮的额外的功能,比如域的可观测性。这里,没有使用定制的客户端库,也没有在代码中添加跟踪调用,而是将流量定向到Envoy,让它为我收集指标。

    但Envoy也可以用作服务网格的数据平面。这意味着现在将由该服务网格的控制平面配置Envoy。
    ## 控制平面
    说到控制平面,Istio依赖于Kubernetes API。这与使用confd没有太大的不同。confd依赖etcd或Consul来监视数据存储中的一组密钥。Istio依赖Kubernetes API来监视一组Kubernetes资源。

    Aparte:我个人认为阅读Kubernetes API描述非常有帮助。

    Kubernetes API服务器是一个“哑服务器”,它提供API资源上的存储、版本控制、验证、更新和监视语义。



    Istio是为与Kubernetes合作而设计的;如果你想在Kubernetes之外使用它,则需要运行Kubernetes API服务器的实例(以及支持的etcd服务)。
    ## 服务地址
    Istio依赖Kubernetes分配的集群IP地址,因此Istio得到一个内部地址(不在127.0.0.1/8范围)。

    在没有Istio的Kubernetes集群上,前往给定服务的ClusterIP地址的流量被kube-proxy拦截,并发送到该代理的后端。更具体地说,如果你想确定技术细节:kube-proxy设置iptables规则(或IPVS负载平衡器,取决于它是如何设置的)来重写连接到集群IP地址的目标IP地址。

    一旦Istio安装在Kubernetes集群上,就不会发生任何变化,直到通过将sidecar容器注入到使用者Pod中,显式地为给定的使用者甚至整个名称空间启用Istio。sidecar将运行一个Envoy实例,并设置一些iptables规则来拦截到其他服务的流量,并将这些流量重定向到Envoy。

    结合Kubernetes DNS集成,这意味着我们的代码可以连接到一个服务名,一切都可以正常工作。换句话说,比如我们的代码向`http://api/v1/users/4242`发起一个请求,`api`将解析到10.97.105.48,一条iptables规则将解释连接到10.97.105.48并重定向到本地Envoy代理,本地代理将这个请求路由到实际的API后端。
    ## 额外的铃声和哨声
    Istio还可以通过名为Citadel的组件通过mTLS(双向TLS)提供端到端加密和身份验证。

    它还包括混合器,Envoy组件可以查询每一个请求,对请求进行一个临时的决定取决于各种因素,例如请求头、后端负载(别担心,有丰富的规定以确保混合高度可用,即使它休息,Envoy可以继续代理流量)。

    当然,我提到了可观察性。Envoy在提供分布式跟踪的同时收集大量的度量指标。微服务架构,如果单个API请求必须经过微服务A、B、C和D,分布式跟踪将添加一个惟一的标识符请求进入系统,并保留标识符在子请求中,所有这些微服务允许收集所有相关的调用、延迟等。
    # 自建还是购买
    Istio以复杂著称。相比之下,使用我们今天拥有的工具,构建像我在本文开头描述的那样的路由网格相对比较简单。那么,构建我们自己的服务网格是否有意义呢?

    如果我们有适度的需求(如果我们不需要可观察性,断路器,和其他细节),我们可能想建立自己的。但是如果我们正在使用Kubernetes,我们甚至可能不需要这样做,因为Kubernetes已经提供了基本的服务发现和负载平衡。

    现在,如果我们有高级的需求,购买服务网格可能是一个更好的选择。(由于Istio是开源的,所以它并不总是真正的购买,但是我们仍然需要投入工程时间来理解它是如何工作、部署和运行的。)
    #如何选择Istio、Linkerd和Consul Connect
    到目前为止,我们只讨论了Istio,但它并不是唯一的服务网格。Linkerd是另一个流行的选择,还有Consul Connect

    我们应该选哪一个呢?

    实际上在这一点上我也不好说,我不认为我有足够的了解能够帮助任何人做决策。不过,已经有一些有趣的文章比较它们(12),甚至基准测试

    一种值得一提并且很有潜力的方法是使用像SuperGloo这样的工具。SuperGloo提供了一个抽象层来简化和统一服务网格公开的API。我们可以使用SuperGloo提供的更简单的构造,并无缝地从一个服务网格切换到另一个服务网格,而不是学习各种服务网格的特定API(在我看来,相对复杂)。有点像我们有一个描述HTTP前端和后端的中间配置格式,能够为NGINX、HAProxy、Traefik、Apache生成实际配置

    我已经使用SuperGloo稍微涉足Istio,在未来的博客文章中,我想说明如何使用SuperGloo将Isio或Linkerd添加到现有的集群中,以及后者是否能实现它的承诺,即允许我在不重写配置的情况下从一个路由网格切换到另一个。

    如果你喜欢这篇文章,并且想让我尝试一些具体的场景,我很乐意听到你的消息!

    原文链接:Containers, microservices, and service meshes

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

    微服务和容器——企业持续交付指南

    cleverlzc 发表了文章 • 0 个评论 • 899 次浏览 • 2019-03-03 11:28 • 来自相关话题

    【编者的话】微服务、容器及其编排平台是当今的热门话题,并且很多企业已经开始在生产中使用它们,本文就微服务的崛起、企业应用如何从微服务中受益、微服务基于容器技术如何更好更快地持续交付进行了详细的阐述。 很大程度上,当今的企业需要依靠软件 ...查看全部
    【编者的话】微服务、容器及其编排平台是当今的热门话题,并且很多企业已经开始在生产中使用它们,本文就微服务的崛起、企业应用如何从微服务中受益、微服务基于容器技术如何更好更快地持续交付进行了详细的阐述。

    很大程度上,当今的企业需要依靠软件应用来促进大量的商业需求。在大多数企业中,一个软件应用往往提供了数百项功能——所有这些功能都集中在一个大型单体应用中。例如,ERP和CRM平台具有统一的架构,有效地服务于数百种功能。但是,由于多个依赖项重叠并创建集群,故障诊断、扩展和升级它们的任务变成了噩梦。有时,企业会为了方便而调整这种单体应用,因为单体应用一旦陷入瓶颈,便不再为任何真正的业务目的提供服务。这时,企业开始寻找实现应用程序现代化和采用灵活性的架构方法。
    # 微服务的崛起
    企业对微服务架构的需求越来越大,以实现向现代交付的转变。在此架构下,功能被设计为独立的微服务,这些微服务之间松耦合,以创建一个能够执行多任务的应用程序。这种方法有助于大规模地构建应用,在组件级别进行更改变得容易,而不会干扰应用的其他部分。
    mmexport1551583252515.jpg

    Netflix是最大的、也是最有趣的成功案例之一,它从单体架构过渡到基于微服务架构的应用。这家媒体服务提供商永远不会忘记2008年的一天,一个分号的丢失导致了重大的数据库损坏,导致整个平台瘫痪了几个小时。Netflix意识到他们必须改变架构方法,从而考虑从单体架构转向微服务架构。

    尽管Netflix从2009年开始转向微服务架构,并在2011年成功地运行了基于云的微服务架构,但微服务这个术语在2012年之前并没有出现。直到2014年Martin Flower和该行业的其他领军人物开始讨论这个问题时,它才开始流行起来。

    Netflix的首席云工程师Adrian Cockcroft是一位有远见的人,他在架构格局的变化中扮演了重要角色,他将微服务解释为“具有有限上下文的松散耦合的面向服务的架构”。

    由于他大胆的决定转向微服务,Netfix在可扩展性上取得了巨大飞跃。2016年初,Netfix宣布将服务扩展到130多个新的国家。
    # 微服务如何使企业应用受益
    从单体架构到微服务的过渡可以为企业打开一个充满各种可能性的世界,例如:

    • 能够创建支持服务且独立运行的组件,这样,每个组件自身都是独立的,但是它们都通过API耦合在一起,作为应用程序以统一的方式工作。
    • 独立测试和运行的组件,你可以很轻松的对一个组件运行测试与更改,而不必更改任何其他组件。
    • 互连组件的同步工作,组件使用简单的通信通道和协议作为个体单元共存并协同工作。
    • 一个非集中化的应用程序,每个组件都是独立的,可以单独开发和部署。因此,消除了由于一个小缺陷而导致整个应用程序崩溃的风险。
    • 分散的数据管理 ,每个组件都有自己独立的数据库,因此,可有效地防止数据泄露导致接管整个应用程序,并将其限制在一个组件中,这增强了应用程序的安全性。
    • 灵活和可伸缩的应用程序,应用程序的部分升级或扩展,无需对已经存在的组件进行任何更改。

    尽管微服务架构有很多优点,但它也有自己的局限性。微服务面临的最大挑战之一仍然是如何大规模的提供这些服务。这种分段应用的持续集成和交付变得非常复杂,因为需要大量的协调才能同步地集成和部署一组微服务。只有非常高效的DevOps团队才能实现这一壮举。关键是在微服务和它们赖以运行的基础设施之间拥有无缝的通信通道。为了充分挖掘微服务的价值,必须将它们作为可自我维持和可移植的交付单元,这些单元由容器支持。
    # 对于微服务,为什么选择容器
    “容器简化了微服务的持续部署”——这句话经常被技术专家重复。但是究竟什么是软件容器?它们是如何简化微服务的交付的?

    容器所做的与物理容器完全相同,但是更加数字化。简而言之,容器允许你将微服务放入专用的盒子中,其思想是将类服务及其所需的基础设施打包到其中。容器在虚拟化操作系统中提供了一个独立的工作负载环境。通过在单独的容器中运行微服务,可以独立地部署它们。由于容器在隔离的环境中操作,因此可以使用它们部署微服务,而不考虑用于创建每个微服务的代码语言。因此,容器消除了语言、库或者框架之间的任何摩擦或冲突的风险,从而使它们兼容。

    由于容器非常轻量且可移植,因此可以使用它们快速部署微服务。通常,应用程序由小型自包含的微服务组成,每个微服务充当一个单一功能的应用程序,通过不依赖于特定语言的API一起工作。因此,容器在这种情况下提供了所需的隔离性,从而支持组件协同。

    作为微服务使用容器好处的支持,Docker报告表明,使用Docker容器的软件发布频率增加了46%。



    这些容器可以通过容器编排平台进行编排,如Kubernetes、Docker Swarm、Helios等。这些平台可以根据需要创建多个容器,并使它们易于应用的顺利部署。编排还控制如何连接容器,以便从多个微服务构建复杂的应用程序。
    # 展望,前方的道路
    虽然容器和编排平台是当今热门话题的一部分,但更大的问题是企业如何以及何时可以开始在生产中使用它们。这两种技术都为应用交付的速度、规模和频率设定了新的基准,如果没有自动化和流程标准化,这将很难实现。这可以通过选择一个高效的应用交付平台来实现,该平台可以为现有应用和未来的云原生应用提供容器化,并无缝地将它们导入Kubernetes,从而实现应用交付过程的自动化。这样可以简单的规范APP的交付流程,加快容器原生交付的关键环节,实现微服务的持续交付。

    原文链接:Of Microservices & Containers-An enterprise’s guide to continuous delivery

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

    知乎容器化构建系统设计和实践

    大卫 发表了文章 • 0 个评论 • 1213 次浏览 • 2019-01-03 23:09 • 来自相关话题

    #关于 知乎应用平台团队基于 Jenkins Pipeline 和 Docker 打造了一套持续集成系统。Jenkins Master 和 Slave 基于 Docker 部署,每次构建也是在容器中进行。目前有三千个 Jenkins Job,支撑着 ...查看全部
    #关于
    知乎应用平台团队基于 Jenkins Pipeline 和 Docker 打造了一套持续集成系统。Jenkins Master 和 Slave 基于 Docker 部署,每次构建也是在容器中进行。目前有三千个 Jenkins Job,支撑着整个团队每日近万次的构建和部署量。

    整个系统的设计目标是具备以下的能力:

    * 较低的应用接入成本,较高的定制能力:写一个构建系统配置文件成本要尽可能简单方便,或者可以通过模板一键创建,但又要能满足应用的各种定制化的需求。
    * 具备语言开放性和部署多样性:平台需要能支撑业务技术选型上的多语言,同时,要能满足应用不同的部署类型,如单纯的打包发布,或者进一步部署到物理机、容器、离线任务平台等。
    * 构建快和稳定,复现问题成本低:每次构建都在干净的容器中,减少非应用本身问题带来的构建异常。同时,如果构建出现问题,在权限控制的前提下,要能方便开发者自己调试和排查。
    * 推动业界标准以及最佳实践,同时在代码合并之前就能更好把控住质量。
    * 整个集群高可用,可扩展,以及具备较低的运维成本。

    #背景
    知乎选用 Jenkins 作为构建方案,因其强大和灵活,且有非常丰富的插件可供使用和扩展。

    早期,应用数量较少时,每个开发者都手动创建并维护着几个 Job,各自编写 Jenkins Job 的配置,以及手动触发构建。随着服务化以及业务类型,开发者以及 Jenkins Job 数量的增加,我们面临了以下的问题:

    * 每个开发者都需要去理解 Jenkins 的基本配置和触发逻辑,使得配置创建和维护成本高。
    * 构建在物理机上进行,每个应用可能有着不同的版本依赖,构建时会遇到版本冲突,甚至上线之后发现行为不一致导致故障等。
    * 构建一旦失败,需要开发者能登录 Jenkins Slave 所在的物理机进行调试,权限控制成为了一个问题。

    于是,一个能方便应用接入构建部署的系统,成为了必须。
    #完整的生命周期
    知乎的构建工作流主要是以下两种场景:

    * 只有 Master 分支的代码可以用于线上部署,但支持指定任意的分支进行构建。
    * 所有对 Master 分支的修改必须通过 Merge Request 来进行。为了避免潜在代码冲突导致测试结果不准的情况,对 Merge Request 上的代码进行构建前,会模拟跟 Master 分支的代码做一次合并。

    一个 Commit 从提交到最后部署,会经历以下的环节:
    1.png


    1. 开发者提交代码到 GitLab。
    2. GitLab 通过 Webhook 通知到 ZAE(Zhihu App Engine, 知乎的私有云平台)。
    3. ZAE 将构建的上下文信息,如 GitLab 仓库 ID,ZAE 应用信息给到构建系统 Lavie。目前只处理用户提交 MR 以及合并到 Master 分支的事件。
    4. 构建系统 Lavie 读取应用仓库中的配置文件后生成配置,触发一个构建。在构建过程中获取动态生成的 Jenkinsfile,生成 Dockerfile 构建出应用的镜像,并跑起容器,在容器中执行构建,测试等应用指定的步骤。
    5. 测试成功之后,分别往物理机部署平台,容器部署平台,离线任务平台上传 Artifact,注册待发布版本的信息,并 Slack 通知用户结果。
    6. 构建结束,用户在 ZAE 上可以进行后续操作,如选择一个候选版本进行部署。

    每个应用的拉取代码,准备数据库,处理测试覆盖率,发送消息,候选版本的注册等通用的部分,都会由构建系统统一处理,而接入构建系统的应用,只需要在代码仓库中包含一个约定格式的配置文件。

    #达到的目标以及中间遇到的问题
    ##较低的接入成本,较高的定制能力
    构建系统去理解应用要做的事情靠的是约定格式的 yaml 配置文件,而我们希望这个配置文件能足够简单,声明上必要的部分,如环境、构建、测试步骤就能开始构建。

    同时,也要有能力提供更多的定制功能让应用可以使用,如选择系统依赖和版本,缓存的路径,是否需要构建系统提供 MySQL 以及需要的 MySQL 版本等。以及可以根据应用的类别自动生成配置文件。

    一个最简单的应用场景:
    base_image: python2/jessie

    build:
    - buildout

    test:
    unittest:
    - bin/test --cover-package=pin --with-xunit --with-coverage --cover-xml

    一个更多定制化的场景:
    base_image: py_node/jessie
    deps:
    - libffi-dev
    build:
    - buildout
    - cd admin && npm install && gulp
    test:
    deps:
    - mysql:5.7
    unittest:
    - bin/test --cover-package=lived,liveweb --with-xunit --with-coverage
    coverage_test:
    report_fpath: coverage.xml
    post_build:
    scripts:
    - /bin/bash scripts/release_sentry.sh
    artifacts:
    targets:
    - docker
    - tarball
    cache:
    directories:
    - admin/static/components
    - admin/node_modules

    为了尽可能满足多样化的业务场景,我们主要将配置文件分为三部分:声明环境和依赖、构建相关核心环节、声明 Artifact 类型。

    声明环境和依赖:

    * image,基础镜像,需要指明已提前准备好的语言镜像
    * deps,dependencies 的简写,声明使用的系统依赖以及对应的版本

    构建相关核心环节:

    * build,构建的步骤,如 buildout,npm install,或者执行一个脚本
    * test,测试环节,应用需要声明构建的步骤,也可以在这里定制使用的 MySQL 以及对应的版本。构建系统会每次为其创建新的数据库,将关键信息 export 为环境变量。
    * post build,最后一个环节,如发包,发 Slack 、邮件通知,或发布一个 Sentry release 等

    声明 Artifact 类型:

    artifact,用于选择部署的类型,目前支持的有:

    * Tarball:构建系统会将整个应用 Workspace 打包上传到 HDFS 用于后续的物理机部署
    * Docker:镜像会被 push 到私有的 Docker Registry 用于容器部署
    * static:应用指定的路径打包后会被上传到 HDFS,用于后续的静态资源部署
    * offline:应用指定的文件会被上传到离线平台,用于离线任务的执行

    ##语言开放性
    早期所有的构建都在物理机上进行,构建之前需要提前在物理机上安装好对应的系统依赖,而如果遇到所需要的版本不同时,调度和维护的成本就高了很多。随着团队业务数量和种类的增加,技术选型的演进,这样的挑战越来越大。于是构建系统整体的优化方向由物理机向 Docker 容器化前进,如今,所有构建都在干净的容器中进行,基础的语言镜像由应用自己选择。

    目前镜像管理的方式是:

    * 我们会事先准备好系统的基础镜像。
    * 在系统镜像的基础上,会构建出不同的语言镜像供应用使用,如 Python,Golang,Java,Node,Rust 的各种版本以及混合语言的镜像。
    * 在应用指定的 `image` 语言镜像之上,会安装上 `deps` 指定的系统依赖,再构建出应用的镜像,应用会在这个环境里面进行构建测试等。

    2.png

    语言这一层的 Dockerfile 会被严格 review,通过的镜像才能被使用,以更好了解和支持业务技术选型和使用场景。

    ##减少不稳定构建,降低问题复现成本
    缓存的设计

    最开始构建的缓存是落在对应的 Jenkins Slave 上的,随着 Slave 数量的增多,应用构建被分配到不同 Slave 带来的代价也越来越大。

    为了让 Slave 的管理更加灵活以及构建速度和 Slave 无关,我们最后将缓存按照应用使用的镜像和系统依赖作为缓存的标识,上传到 HDFS。在每次构建前拉取,构建之后再上传更新。

    针对镜像涉及到的语言,我们会对常见的依赖进行缓存,如 eggs,node_modules,.ivy2/cache,.ivy2/repository。应用如果有其他的文件想要缓存,也支持在配置文件中指定。

    依赖获取稳定性

    在对整个构建时间的开销和不稳定因素的观察中,我们发现拉取外部依赖是个非常耗时且失败率较高的环节。

    为了让这个过程更加稳定,我们做了以下的事情:

    * 完善内部不同语言的源
    * 在不同语言的基础镜像中放入优先使用内部源的配置
    * 搭建 HTTP Proxy,提供给以上覆盖不到的场景

    更低的排查错误的成本

    本地开发和构建环境存在明显的差异,可能会出现本地构建成功但是在构建系统失败的情况。

    为了让用户能够快速重现,我们在项目 docker-ssh 的基础上做了二次开发,支持直接 ssh 到容器进行调试。由于容器环境与其他人的构建相隔离,我们不必担心 SSH 权限导致的各种安全问题。构建失败的容器会多保留一天,之后便被回收。
    ##规范和标准的落地抓手
    我们希望能给接入到构建系统的提高效率的同时,也希望能推动一些标准或者好的实践,比如完善测试。

    围绕着测试和测试覆盖率,我们做了以下的事情:

    * 配置文件中强制要有测试环节。
    * 应用测试结束之后,取到代码覆盖率的报告并打点。在提交的 Merge Request 评论中会给出现在的值和主分支的值的比较,以及最近主分支代码覆盖率的变化趋势。
    * 在知乎有应用重要性的分级,对于重要的应用,构建系统会对其要求有测试覆盖率报告,以及更高的测试覆盖率。

    3.png

    4.png

    对于团队内或者业界的基础库,如果发现有更稳定版本或者发现有严重问题,构建系统会按照应用的重要性,从低到高提示应用去升级或者去掉对应依赖。
    5.png

    ##高可用和可扩展的集群
    Job 调度策略

    Jenkins Master 只进行任务的调度,而实际执行是在不同的 Jenkins Node 上。

    每个 Node 会被赋予一些 label 用于任务调度,比如:mysql:5.6,mysql:5.7,common 等。构建系统会根据应用的类型分配到不同的 label,由 Jenkins Master 去进一步调度任务到对应的 Node 上。

    高可用设计

    集群的设计如下,一个 Node 对应的是一台物理机,上面跑了 Jenkins Slave (分别连 Master 和 Master Standby),Docker Deamon 和 MySQL(为应用提供测试的 MySQL)。
    6.png

    Slave 连接 Master 等待被调度,而当 Jenkins Slave 出现故障时,只需摘掉这台 Slave 的 label,后续将不会有任务调度调度上来。

    而当 Jenkins Master 故障时,如果不能短时间启动起来时,集群可能就处于不可用状态了,从而影响整个构建部署。为了减少这种情况带来的不可用,我们采用了双 Master 模型,一台作为 Standby,如果其中一台出现异常就切换到另一台健康的 Master。

    监控和报警

    为了更好监控集群的运行状态,及时发现集群故障,我们加了一系列的监控报警,如:

    * 两个 Jenkins Master 是否可用,当前的排队数量情况。
    * 集群里面所有 Jenkins Node 的在线状态,Node 被命中的情况。
    * Jenkins Job 的执行时间,是否有不合理的过长构建或者卡住。
    * 以及集群机器的 CPU,内存,磁盘使用情况。

    #后续的计划
    在未来我们还希望完善以下的方面:

    * Jenkins Slave 能更根据集群的负载情况进行动态扩容。
    * 一个节点故障时能自动下掉并重新分配已经在上面执行的任务。一个 Master down 掉能被主动探测到并发生切换。
    * 在 Merge Request 的构建环节推动更多的质量保证标准实施,如更多的接口自动化测试,减少有问题的代码被合并到主分支。

    ##参考资料

    * Jenkinsfile 相关文档:https://jenkins.io/doc/book/pipeline/jenkinsfile/
    * Jenkins Logo: https://jenkins.io/
    * Docker Logo:Brand Guidelines

    原文链接:https://zhuanlan.zhihu.com/p/45694823

    揭开容器的神秘面纱:帮助初学者深入了解容器技术

    新牛哥 发表了文章 • 0 个评论 • 1731 次浏览 • 2018-12-09 18:33 • 来自相关话题

    【编者的话】本文为初学者深入分析了容器技术,包括操作系统和内核的概念,cgroup和LXC的概念,Docker技术的原理,以及容器技术的优势,并指出编排才能让容器发挥其真正作用。 # 简介 无论你是学生,还是公司的开发人员,或是软件爱好 ...查看全部
    【编者的话】本文为初学者深入分析了容器技术,包括操作系统和内核的概念,cgroup和LXC的概念,Docker技术的原理,以及容器技术的优势,并指出编排才能让容器发挥其真正作用。
    # 简介
    无论你是学生,还是公司的开发人员,或是软件爱好者,相信你都听说过容器。 你可能还听说容器是轻量级虚拟机,但这究竟意味着什么,容器究竟是如何工作的,以及它们为什么如此重要呢?

    本文将带你深入了解容器,它们的关键技术思想,以及它们的应用。 除了计算机科学的基本概念之外,你不需要该领域的其它任何先决知识。
    # 内核和操作系统
    你的笔记本电脑以及其它所有计算机都是基于CPU,持久存储(磁盘驱动器,SSD),内存,网卡等硬件构建的。

    要与此硬件进行交互,操作系统中被称为内核的软件将充当硬件与系统其余部分之间的桥梁。内核负责调度要运行的进程(程序),管理设备(在磁盘和内存上读写地址)等等。

    操作系统的其余部分用于引导和管理用户进程的运行用户空间,并将不断与内核进行交互。
    1.png

    内核是操作系统的一部分,并与硬件接口。 整个操作系统都位于“内核空间”中,而用户程序则位于“用户空间”中。 内核空间负责管理用户空间。
    # 虚拟机
    假定你有一台运行MacOS的计算机和一个可以在Ubuntu上运行的应用程序。一个常见的解决方案是在运行Ubuntu的MacOS计算机上启动虚拟机,然后在那里运行你的程序。

    虚拟机由某些特定的硬件和内核虚拟化组成,运行客户操作系统。称为管理程序的软件创建虚拟化硬件,其可以包括虚拟磁盘,虚拟网络接口,虚拟CPU等。虚拟机还包括可以与此虚拟硬件通信的宾客内核。

    管理程序可以托管,这意味着它是一些在主机操作系统(MacOS)上运行的软件,如示例中所示。它也可以是裸机,直接在机器硬件上运行(替换你的操作系统)。无论哪种方式,管理程序方法都被认为是重量级的,因为它需要虚拟化多个部分(如果不是全部硬件和内核)。

    当同一台机器上需要有多个隔离组时,为每个组运行一个VM太繁重且浪费资源,不是一个好方法。
    2.png

    开销不按比例

    VM需要硬件虚拟化才能实现机器级隔离,而容器则只需要在同一操作系统内进行隔离操作。 随着隔离空间数量的增加,开销差异变得非常明显。 普通的笔记本电脑可以运行数十个容器,但很难运行一台VM。
    # cgroups
    2006年,Google的工程师发明了Linux“控制组”,缩写为cgroups。这是Linux内核的一项功能,可隔离和控制用户进程的资源使用情况。

    这些进程可以放入命名空间,实质上是共享相同资源限制的进程集合。计算机可以有多个命名空间,每个命名空间都具有内核强制执行的资源属性。

    我们可以管理每个命名空间的资源分配,以便限制一组进程可以使用的总CPU,RAM等的数量。例如,后台日志聚合应用程序可能需要限制其资源,以免意外地压倒它正在记录的实际服务器。

    虽然不是原始功能,但Linux中的cgroup最终被重新设计为包含命名空间隔离的功能。命名空间隔离本身并不新鲜,Linux已经有多种命名空间隔离。一个常见的例子是进程隔离,它将每个单独的进程分开并防止诸如共享内存之类的事情。

    cgroup隔离是一种更高级别的隔离,可确保cgroup命名空间中的进程独立于其他命名空间中的进程。下面概述了一些重要的命名空间隔离功能,为我们对容器的隔离奠定了基础。

    • PID(进程标识符)命名空间:这可确保一个命名空间内的进程不知道其他命名空间中的进程。
    • 网络命名空间:隔离网络接口控制器,iptables,路由表和其他低级网络工具。
    • 挂载命名空间:已挂载文件系统,因此命名空间的文件系统范围仅限于已挂载的目录。
    • 用户名空间:将命名空间内的用户限制为仅限该命名空间,并避免跨命名空间的用户ID冲突。
    简单地说,每个命名空间看起来都是它自己的机器。# Linux 容器Linux cgroups为一种名为Linux容器(LXC)的技术铺平了道路。 LXC实际上是我们今天所知的第一个实现容器的主要实现,利用cgroup和命名空间隔离来创建具有独立进程和网络空间的虚拟环境。从某种意义上说,这允许独立和隔离的用户空间。 容器的概念直接来自LXC。 事实上,早期版本的Docker直接构建在LXC之上。## DockerDocker是最广泛使用的容器技术,也是大多数人在引用容器时的意思。 虽然还有其他开源容器技术(如CoreOS的rkt)和大型公司构建自己的容器引擎(如谷歌的lmctfy),但Docker已成为容器化的行业标准。 它仍然建立在Linux内核和最近的Windows提供的cgroups和命名空间之上。
    3.png
    图片来源:DockerDocker容器由多层镜像组成,二进制文件一起打包到一个包中。 基本镜像包含容器的操作系统,该操作系统可以与主机的操作系统不同。容器的操作系统是镜像形式。 这不是主机上的完整操作系统,不同之处在于镜像只是操作系统的文件系统和二进制文件,而完整的操作系统包括文件系统,二进制文件和内核。在基础镜像的顶部是多个镜像,每个镜像构建容器的一部分。 例如,在基本镜像的顶部可以是包含`apt-get`依赖性的镜像。 最重要的可能是包含应用程序二进制文件的镜像,依此类推。很酷的部分是如果有两个带有镜像层`a,b,c`和`a,b,d`的容器,那么你只需要在本地和存储库中存储每个镜像层`a,b,c,d`的一个副本。 这是Docker的联合文件系统
    4.png
    由散列标识的每个镜像只是构成容器的许多可能镜像层之一。但是,容器仅由其顶级镜像标识,该镜像具有对父镜像的引用。此处显示的两个顶级镜像(镜像1和镜像2)共享前三个图层。镜像2具有两个附加的配置相关层,但与镜像1共享相同的父镜像。引导容器时,将从repo下载镜像及其父镜像,创建cgroup和命名空间,并使用该镜像创建虚拟环境。在容器内,镜像中指定的文件和二进制文件似乎是整个计算机中的唯一文件。然后启动容器的主进程,并将容器视为活动状态。Docker还有其他一些非常酷的功能,例如写入时复制,卷(容器之间的共享文件系统),docker守护程序(管理机器上的容器),版本控制的存储库(如容器的Github)等等。要了解有关它们的更多信息并查看有关如何使用Docker的一些实际示例,这篇Medium上的文章非常有用。
    5.png
    命令行客户端(1)告诉计算机上的进程名为docker daemon(2)该做什么。 守护程序从注册表/存储库中提取图像(3)。 这些镜像在本地计算机上缓存(4),并且可以由守护程序启动以运行容器(5)。 图片来源:Docker。# 为什么使用容器除了工艺隔离外,容器还具有许多其他有益的特性。容器可作为一个独立的单元,可以在任何支持它的地方运行。在每个实例中,容器本身都是完全相同的。如果主机操作系统是CentOS,Ubuntu,MacOS,甚至是像Windows这样的非UNIX系统都无关紧要——从容器内部看操作系统将是容器指定的任何操作系统。因此,你可以确定你在笔记本电脑上构建的容器也将在公司的服务器上运行。容器还充当标准化的工作或计算单元。一个常见的范例是每个容器运行单个Web服务器,数据库的单个分片或单个Spark工作程序等。然后,为了扩展应用程序,你只需要扩展容器的数量。在这个范例中,每个容器都有一个固定的资源配置(CPU,RAM,线程数等),并且扩展应用程序需要只扩展容器的数量而不是单个资源原语。当应用程序需要按比例放大或缩小时,这为工程师提供了更容易的抽象。容器也是实现微服务架构的一个很好的工具,每个微服务只是一组协作容器。例如,可以使用单个主容器和多个从容器来实现Redis微服务。这种(微)服务导向架构具有一些非常重要的属性,使工程团队可以轻松创建和部署应用程序(有关更多详细信息,请参阅我之前的文章)。# 编排自从Linux容器出现以来,用户一直试图在许多虚拟机上部署大型应用程序,其中每个进程都在自己的容器中运行。这样做需要能够在数百个虚拟机中有效地部署数十到数千个容器,并管理他们的网络,文件系统,资源等。今天Docker使它更容易,因为它公开了抽象来定义容器网络,文件卷系统,资源配置等。但是仍然需要一个工具来:
    • 真实地采取规范并将容器分配给机器(调度)
    • 真实地通过Docker在机器上启动指定的容器
    • 处理升级/回滚/系统不断变化的性质
    • 应对容器崩溃等故障
    • 创建集群资源,如服务发现,VM间网络,集群入口/出口等。

    这组问题涉及在一组(可能是瞬态的或不断变化的)容器之上构建的分布式系统的编排,人们已经建立了一些非常奇妙的系统来解决这个问题。

    在下一篇文章中,我将深入讨论Kubernetes(主流的开源编排器)的实现,以及两个同样重要但鲜为人知的Mesos和Borg。

    原文链接:Demystifying containers 101: a deep dive into container technology for beginners (翻译:池剑锋)

    容器将成为下一个“Linux”

    cleverlzc 发表了文章 • 0 个评论 • 1376 次浏览 • 2018-11-23 23:30 • 来自相关话题

    【编者的话】随着容器在使用、流行和复杂性方面的不断增长,它们将与Linux操作系统一样,很快成为不可或缺的开发工具。 Linux操作系统在过去的20年里给数据中心带来了革命性的变化,今天它是应用程序托管平台无可争议的领导者。很难想象, ...查看全部
    【编者的话】随着容器在使用、流行和复杂性方面的不断增长,它们将与Linux操作系统一样,很快成为不可或缺的开发工具。

    Linux操作系统在过去的20年里给数据中心带来了革命性的变化,今天它是应用程序托管平台无可争议的领导者。很难想象,将任何关键任务的生产工作负载部署到Linux以外的任何平台。

    当Docker使Linux容器流行起来,这与几年前在打包、部署和托管应用程序方面开始的革命何其相似。此后,整个行业的容器使用呈指数级增长,并且与日俱增。

    正如我们无法想象将任何关键应用程序部署到非Linux操作系统上一样,容器也是如此:任何应用程序都必须是容器,以满足未来Web规模的需求(即安全性、可伸缩性、平台无关性和易移植性)。这些容器现在是,未来也将是运行任何关键应用程序和工作负载的新一代“Linux”。
    # 容器采用趋势
    根据Datadog于2018年6月更新的调查显示,有一种趋势是,25%的公司已经采用Docker,其余公司正在迅速追赶。这里有详细的调查数据。
    1.png

    # 容器和编排
    Kubernetes、OpenShift、Apache Mesos和Docker Swarm对容器提供了极好的编排支持,使得管理一个数千个容器的平台或基础设施要容易得多。在编排工具提供的自动化功能的帮助下,一名工程师就可以管理容器化应用程序的异构部署。

    Kubernetes正在统治Docker容器的编排世界,谷歌是它的最初创建者,CNCF是所有者。Kubernetes在整个行业中也拥有最好的社区支持和极好的适应性。

    为任何容器化应用程序提供编排支持的一些特性:

    • 基于资源利用率的水平自动伸缩。
    • 与云基础设施紧密集成时,应用程序实例可以无限伸缩。
    • 应用程序实例的自愈。
    • 在线升级。
    • 金丝雀升级。
    # 容器与微服务作为编排工具,Kubernetes再次主导微服务部署领域,用于承载和运行容器支持的微服务,它是最著名、最受欢迎的平台之一。所有标识的微服务首先被转换为Docker容器,然后转到Kubernetes平台进行部署,这使得它们在整个生命周期内快速部署并易于管理。基于容器的微服务在伸缩和自愈场景下更容易,启动和服务工作负载更快。# 非常规应用中的容器采用在采用容器的过程中发生了许多不可思议的事情;曾经被认为不可能在容器中运行的应用程序现在正在迁移到容器中,并且现在可以在容器化平台中使用。## 数据库几年前,数据库被认为是保持在容器技术之外的东西,但今天我们有了NuoDB,它是一个容器和云原生数据库,专门设计和开发用于在容器上运行,以支持数据库级别的WebScale功能。Oracle 12c现在可以在Docker仓库的官方Docker镜像中预先配置。数据库层容器化令人由衷地兴奋,这将使应用程序更容易WebScale,因为按需扩展数据库(部署在物理服务器上)的限制将消失,数据库在容器内运行以及部署在业务流程引擎上, 将能够在需要时轻松地与应用程序实例一起扩展。## 电信行业中容器化的电信网络组件电信行业向使用的可编程网络转变的势头正在加快,这将帮助所有CSP以虚拟化方式运行所有网络功能,而不是在传统昂贵的硬件上运行这些功能,同时大幅降低运营成本。这也将使所有CSP能够在需求增加时以完全自动化的方式快速扩展其网络。然而,容器相对于VM的优点已经在应用程序中得到了验证,因此整个行业都非常希望telco VNFs获得相同的优点。电信行业向使用SDN-NFV的可编程网络转变的势头正在加快,这将帮助所有CSP显著降低其运营成本,同时以虚拟化的方式运行所有网络功能,而不是在传统昂贵的硬件运行。这也将使所有CSP能够随着需求的增加,以完全自动化的方式迅速扩展其网络。然而,容器比虚拟机的好处已经在应用程序世界中得到了证明,因此整个行业对电信VNF也获得同样的益处有着非常高的兴趣。RedHat以及ONAP也在以下文档中对此进行了详细介绍:伴随着这些趋势,在应用程序和虚拟化网络级别上,电信世界将被容器化平台统治的日子不远了。## 基于容器的大数据平台在大数据世界中,HortonWorks的HDP 3.0数据平台的新版本现在使用Docker容器来运行其内部组件(YARN容器、Web应用程序和HDP服务)。Hadoop集群是一个自我管理的集群,完全支持现代和标准的集群应用程序,因此,大数据平台能够在容器上运行并由编排工具(集群中的集群!!)管理,在之前这是一个有争议的话题。但是由于我们已经有了容器化服务的新版本,所以在不久的将来,我们将在Kubernetes平台中运行Hadoop集群也就不足为奇了。# 云和容器随着容器的普及,所有云服务提供商(AWS、Azure、GCE、Oracle、OpenStack等)也意识到他们必须提供并支持本地容器和Kubernetes编排服务。今天,我们看到几乎所有的云提供商都拥有自己的本地容器和编排服务/平台。以下是云计算公司提供的一些本地容器平台示例:
    • AWS — KOPs
    • 谷歌云- GKE
    • Azure - Azure Kubernetes服务
    • RedHat OpenStack - RedHat OpenShift
    • Oracle云- Oracle Kubernetes引擎

    同样,这种方式为我们提供了一个清晰的趋势,即使组织正在迁移到云基础设施,以取代其遗留的本地数据中心,容器仍然是部署和运行应用程序的首选。
    # 基于容器的私有云部署
    当我们谈论云和容器时,已经有云在容器上运行了。

    OpenStack无疑是私有云领域的赢家,整个私有云托管世界由OpenStack统治,为全球各地的组织提供云计算的强大功能。

    然而,现在OpenStack的新版本可以以一种容器化的方式部署。这使得整个私有云部署并运行在容器中,令人难以置信。

    随着容器采用率的指数级增长,以及软件行业和各个产品部门的根本性变化,容器现在是、未来也将是承载和运行各种工作负载的新Linux,这一说法变得更加现实。

    原文链接:Containers Are and Will Be the New Linux

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

    携程一次Redis迁移容器后Slowlog“异常”分析

    大卫 发表了文章 • 1 个评论 • 2512 次浏览 • 2018-11-16 21:21 • 来自相关话题

    容器化对于Redis自动化运维效率、资源利用率方面都有巨大提升,携程在对Redis在容器上性能和稳定性进行充分验证后,启动了生产Redis迁移容器化的项目。其中第一批次两台宿主机,第二批次五台宿主机。 本次“异常”是第二批次迁移过 ...查看全部
    容器化对于Redis自动化运维效率、资源利用率方面都有巨大提升,携程在对Redis在容器上性能和稳定性进行充分验证后,启动了生产Redis迁移容器化的项目。其中第一批次两台宿主机,第二批次五台宿主机。

    本次“异常”是第二批次迁移过程中发现的,排查过程一波三折,最终得出让人吃惊的结论。

    希望本次结论能给遇到同样问题的小伙伴以启发,另外本次分析问题的思路对于分析其他疑难杂症也有一定借鉴作用。
    #一、问题描述

    在某次Redis迁移容器后,DBA发来告警邮件,slowlog>500ms,同时在DBA的慢日志查询里可以看到有1800ms左右的日志,如下图1所示:
    1.jpg

    图 1
    #二、分析过程

    ##2.1 什么是slowlog

    在分析问题之前,先简单解释下Redis的slowlog。阅读Redis源码(图2)不难发现,当某次Redis的操作大于配置中slowlog-log-slower-than设置的值时,Redis就会将该值记录到内存中,通过slowlog get可以获取该次slowlog发生的时间和耗时,图1的监控数据也是从此获得。
    2.jpg

    图 2

    也就是说,slowlog只是单纯的计算Redis执行的耗时时间,与其他因素如网络之类的都没关系。
    ##2.2 矛盾的日志

    每次slowlog都是1800+ms并且都随机出现,在第一批次Redis容器化的宿主机上完全没有这种现象,而QPS远小于第一批次迁移的某些集群,按常理很难解释,这时候翻看CAT记录,更加加重了我们的疑惑,见图3:
    3.jpg

    图 3

    CAT是携程根据开源软件(https://github.com/dianping/cat)的定制版本,用于客户端记录打点的耗时,从图中可以很清晰的看到,Redis打点的最大值367ms也远小于1800ms,它等于是说下面这张自相矛盾图,见图4:
    4.jpg

    图 4
    ##2.3 求助社区

    所以说,slowlog问题要么是CAT误报,要么是Redis误报,但Redis使用如此广泛,并且经过询问CAT的维护者说CAT有一定的消息丢弃率,而Redis在官方github issue中并没有发现类似的slowlog情形,因此我们第一感觉是CAT误报,并在官方Redis issue中提问,试图获取社区的帮助。

    很快社区有人回复,可能是NUMA架构导致的问题,但也同时表示NUMA导致slowlog高达1800ms很不可思议。关于NUMA的资料网上有很多,这里不再赘述,我们在查阅相关NUMA资料后也发现,NUMA架构导致如此大的slowlog不太可能,因此放弃了这条路径的尝试。
    ##2.4 豁然开朗

    看上去每个方面好像都没有问题,而且找不到突破口,排障至此陷入了僵局。

    重新阅读Redis源代码,直觉发现gettimeofday()可能有问题,模仿Redis获取slowlog的代码,写了一个简答的死循环,每次Sleep一秒,看看打印出来的差值是否正好1秒多点,如图5所示:
    5.jpg

    图 5

    图5的程序大概运行了20分钟后,奇迹出现了,gettimeofday果然有问题,下面是上面程序测试时间打印出来的LOG,如图6:
    6.jpg

    图 6

    图6中标红的时间减去1秒等于1813ms,与slowlog时间如此相近!在容器所在的物理机上也测试一遍,发现有同样的现象,排除因容器导致slowlog,希望的曙光似乎就在眼前了,那么问题又来了:

    1. 到底为什么会相差1800ms+呢?
    2. 为什么第一批机器没有这种现象呢?
    3. 为什么之前跑在物理机上的Redis没有这种现象呢?

    带着这三个问题,重新审视系统调用gettimeofday获取当前时间背后的原理,发现一番新天地。
    #三、系统时钟

    系统时钟的实现非常复杂,并且参考资料非常多。

    简单来说 我们可以通过命令:
    cat /sys/devices/system/clocksource/clocksource0/current_clocksource

    来获取当前系统的时钟源,携程的宿主机上都是统一Time Stamp Counter(TSC):80x86微处理器包括一个时钟输入插口,用来接收来自外部振荡器的时钟信号,从奔腾80x86微处理器开始,增加了一个计数器。

    随着每增加一个时钟信号而加一,通过rdtsc汇编指令也可以去读TSC寄存器,这样如果CPU的频率是1GHz,TSC寄存器就能提供纳秒级别的计时精度,并且现代CPU通过FLAG constant_tsc来保证即使CPU休眠也不影响TSC的频率。

    当选定TSC为时钟源后,gettimeofday获取墙上时钟(wall-clock)正是从TSC寄存器读出来的值转换而来,所谓墙上时钟主要是参照现实世界人们通过墙上时钟获取当前时间,但是用来计时并不准确,可能会被NTP或者管理员修改。

    那么问题又来了,宿主机的时间没有被管理员修改,难道是被NTP修改?即使是NTP来同步,每次相差也不该有1800ms这么久,它的意思是说难道宿主机的时钟每次都在变慢然后被NTP拉回到正常时间?我们手工执行了下NTP同步,发现的确是有很大偏差,如图7所示:
    7.jpg

    图 7

    按常识时钟正常的物理机与NTP服务器时钟差异都在1ms以内,相差1s+绝对有问题,而且还是那个老问题,为什么第一批次的机器上没有问题?
    #四、内核BUG

    两个批次宿主机一样的内核版本,第一批没问题而第二批有问题,差异只可能在硬件上,非常有可能在计时上,翻看内核的commit log终于让我们发现了这样的commit,如图8所示:
    8.jpg

    图 8

    该commit非常清楚指出,在4.9以后添加了一个宏定义INTEL_FAM6_SKYLAKE_X,但因为搞错了该类型CPU的crystal frequency会导致该类型的CPU每10分钟慢1秒钟。

    这时再看看我们的出问题的第二批宿主机xeon bronze 3104正好是skylake-x的服务器,影响4.9-4.13的内核版本,宿主机内核4.10正好中招。

    并且NTP每次同步间隔1024秒约慢1700ms,与slowlog异常完全吻合,而第一批次的机器CPU都不是SKYLAKE-X平台的,避开了这个BUG,迁移之前Redis所在的物理机内核是3.10版本,自然也不存在这个问题。至此,终于解开上面三个疑惑。
    #五、总结

    ##5.1 问题根因

    通过上面的分析可以看出,问题根因在于内核4.9-4.13之间skylake-x平台TSC晶振频率的代码BUG,也就是说同时触发这两个因素都会导致系统时钟变慢,叠加上Redis计时使用的gettimeofday会容易被NTP修改导致了本文开头诡异的slowlog“异常”。有问题的宿主机内核升级到4.14版本后,时钟变慢的BUG得到了修复。
    ##5.2 怎么获取时钟

    对于应用需要打点记录当前时间的场景,也就是说获取Wall-Clock,可以使用clock_gettime传入CLOCK_REALTIME参数,虽然gettimeofday也可以实现同样的功能,但不建议继续使用,因为在新的POSIX标准中该函数已经被废弃。

    对于应用需要记录某个方法耗时的场景,必须使用clock_gettime传入CLOCK_MONOTONIC参数,该参数获得的是自系统开机起单调递增的纳秒级别精度时钟,相比gettimeofday精度提高不少,并且不受NTP等外部服务影响,能准确更准确来统计耗时(Java中对应的是System.nanoTime),也就是说所有使用gettimeofday来统计耗时(Java中是System.currenttimemillis)的做法本质上都是错误的。

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

    容器正在吞噬世界

    Sywooyz 发表了文章 • 0 个评论 • 873 次浏览 • 2018-11-01 10:18 • 来自相关话题

    为了充分利用容器带来的敏捷性,软件开发团队必须调整其软件交付流程。 容器正迅速成为企业应用打包和部署的基本单位。然而IT部门的很多人仍然认为容器只是接下来的发展方向,相对于VM而言,容器只是使计算密度又增加一个数量级。 ...查看全部
    为了充分利用容器带来的敏捷性,软件开发团队必须调整其软件交付流程。

    容器正迅速成为企业应用打包和部署的基本单位。然而IT部门的很多人仍然认为容器只是接下来的发展方向,相对于VM而言,容器只是使计算密度又增加一个数量级。

    虽然容器的出现看似增加了IT部门的管理负担,但容器生态系统同时亦带来一大重要影响——即软件交付方式的根本性转变。

    在传统的软件交付流程中,运维团队和开发团队分别负责技术栈的不同层:运维团队负责操作系统镜像,开发团队负责应用的开发。在这种流程中,开发团队使用操作系统打包工具(RPM,MSI等等工具)向运维团队交付应用和应用相关的依赖。然后运维团队把应用添加到符合公司规范的VM镜像中,并且这些VM镜像都附带了监控和日志软件,最后组合VM镜像并在生产环境中运行。开发团队通过把新包交给运维团队来迭代应用程序,运维团队使用脚本或者配置管理软件来部署新版本的应用和其他的一些更新(例如修复操作系统漏洞的补丁)。
    #基于容器的软件交付流程是不同的
    容器交付流程和之前基于虚拟机的交付流程是完全不同的。在基于容器的交付流程中,开发团队和运维团队合作创建一个由不同层组成的单一容器镜像。这些层中最底层是操作系统层,然后是依赖层,最后是应用程序层。最重要的是,容器镜像在软件交付过程中被视为不可变的:容器中涉及的任何软件变动都需要重新构建整个容器镜像。容器技术和Docker镜像通过使用联合文件系统来构建一个包含应用及其依赖的基础容器镜像,这比VM镜像等早期方法更具可操作性;对容器镜像每层的更改只需要重新构建该层。这使得每个容器镜像重新构建比重新创建一个完整的VM镜像代价小得多。此外,拥有良好架构的容器只会运行一个前台进程,这与将应用程序分解为多个应用的实践相吻合(这通常称为微服务)。另外,重新构建一个容器镜像比重新构建一个VM镜像更小也更加容易,部署和启动所需要的时间也就更短。

    容器镜像的不可变性和应用的微服务架构使得运维团队用于处理配置管理,监控和日志记录的软件通常不会包含在容器中。也就是,如果软件进行变更,应用需要重新构建整个镜像,但容器编排系统不会把监控和日志编排进容器镜像中。换句话来说,处理配置管理,监控和日志记录的软件并不是在运行时处理软件的变更——他们是在构建时处理的。通过使用自动构建、自动测试、自动部署(通常称为持续集成、持续交付),自动化过程从运行时活动转变成构建时活动。
    #在容器范例背景下进行交付
    当然,我们在IT中所关心的核心问题并没有消失:我们需要确保我们的应用是没有漏洞的,运行的是已经经过IT部门确认的最新应用,可以随着负载伸缩,同时也能提供数据让日志和监控系统来协助我们确认应用是否有问题,甚至能在问题发生前预测问题。

    为了充分利用容器来带的敏捷性,同时为确保应用的安全,治理,合规性以及审计追踪,我们必须重新设计我们的软件交付流程。 现在,我们必须维护和操作的最重要两项技术是容器编排系统和容器交付管道。

    对于容器编排系统,在过去的两年中,Kubenetes已经成为开源标准。Kubenetes提供的特性曾经在每个IT部门都被得到应用:负载调度,日志聚合,扩缩容,健康检查和应用无缝升级。IT部门并不需要保留旧的流程和工具,而应该把Kubenetes这些特性当做新运维系统的一部分,基于Kubenetes提供的功能来创建他们的流程。

    第二个关键组件是容器交付管道:这是一个对所有代码进行检查时,自动进行构建和测试的系统,并且把检查通过的代码部署到容器编排系统上。运维流程中最关键的转变是把软件交付过程中核心点(例如漏洞修复)从生产系统的运行时监控转移到构建管道。例如,运维团队不需要对正在运行的容器打补丁,而是应该是用容器检查工具标记有漏洞的包的版本,触发一个容器镜像的重新构建,把检索容器镜像是否有漏洞作为CI/CD的一个步骤,并且只部署通过检查的镜像。
    #通过新的基于容器的交付流程来统一开发团队和运维团队
    这个转变对IT部门来说可能会感到非常可怕,但是实际上它与DevOps需要进行的转变完全保持一致:应用程序的构建阶段由开发团队和运维团队共同负责,这样能尽早的发现问题,通过为开发团队和运维团队提供更严格的工作流程来消除DevOps过程中造成的大量浪费。

    IT部门现在有额外的两个系统来标准化和运维业务系统:容器编排系统和容器交付管道。但是对例如配置管理系统,日志聚合系统和监控系统等IT公共组件会发生什么呢?他们并不会消失在容器世界里。相反,他们会在容器中扮演不同的角色。配置管理系统被用于部署和管理容器编排系统、容器交付管道和其他的例如数据管理系统等没有运行在容器中的核心分布式系统的生命周期。日志聚合系统继续从容器编排系统和容器交付管道系统的日志中为审计、预测性的分析提供关键性的功能。同时监控系统也会聚合容器编排系统和其他外部数据源的数据。
    #通过DevOps和容器建立结构化竞争优势
    通过引入企业标准的容器编排系统和容器交付管道来拥抱DevOps的企业将会具有敏捷性优势,能更快的试验和理解客户的需求,最终能正确并且比竞争对手更快的交付软件。这些有远见的企业将会具有结构性竞争优势并将成为DevOps和容器的主要受益者。

    原文链接:Containers are eating the world(翻译:吴通)

    简析容器、无服务器和虚拟机的安全性差异

    kelvinji2009 发表了文章 • 0 个评论 • 906 次浏览 • 2018-10-20 11:45 • 来自相关话题

    在三十多年前,虚拟化仅适用于拥有大型机和众多小型计算机的用户,而安全问题仅仅是物理上的。 二十年前,VMware发布了其第一款产品,网络边界安全性仍处于起步阶段,依赖于防火墙。 十二年前,AWS推出,网络安全成为一个问题。 五年前,由于Docker,容器成为主 ...查看全部
    在三十多年前,虚拟化仅适用于拥有大型机和众多小型计算机的用户,而安全问题仅仅是物理上的。 二十年前,VMware发布了其第一款产品,网络边界安全性仍处于起步阶段,依赖于防火墙。 十二年前,AWS推出,网络安全成为一个问题。 五年前,由于Docker,容器成为主流,主机安全成为焦点。 今天,随着无服务器安全性的增长,应用程序级安全性终于受到已经存在多年的计算和网络层全面审查。

    随着应用程序,计算和网络安全都被审计,通过SOC type 2等报告,管理层和客户端都可以更加了解安全问题。随着客户端透明度的提高,安全专业人员是确保部署资产和使生产具有坚实安全性的关键。 根据正在使用的部署类型,相应的配置规模大小可能会急剧增加。

    以上就是理解不同类型的新兴部署技术(即容器,无服务器计算和虚拟机)之间的安全细微差别的原因。 下面,我们对比一下他们的安全方面。
    # 无服务器安全
    首先,让我们解决无服务器安全问题,因为无服务器应用程序通常是纯粹执行单个函数的代码——因此叫做`function-as-a-service`。 你实际部署平台可能对无服务器应用程序中发生的最常见安全问题无动于衷。

    除了遵循安全编码最佳实践,例如仅返回处理请求绝对需要的数据并让应用程序使用仅具有允许其执行其工作所需的访问权限的服务帐户,发现的任何漏洞都将导致数据被泄露, 这远远超出了无服务器应用程序的范围——这可能导致公关噩梦。

    另一个主要关注领域是应用程序中包含的任何第三方库,这些第三方库主要是为了提供增强功能,并节省开发团队的开发时间。 比如用于验证电话号码或邮政编码的库,以及连接到外部PostgreSQL数据库所需的JDBC驱动程序等客户端库。 如果不使用自动更新并定期扫描构建的工件的扫描工具,那么在组织内针对所有第三方库的监控所有各种漏洞公告列表的过程,将是一项巨大的手动工作。
    # 容器安全
    实际上,由于无服务器应用程序通常在后台运行在容器中,因此容器将承载与无服务器相同的所有问题,以及容器为开发人员提供的附加功能的新问题。

    针对容器的特定安全问题可以简化为两个不同的区域:您基于部署的容器源的可信度,以及容器对主机操作系统的访问级别。

    在任何主机(无论是Windows还是Linux)上运行容器时,不应使用root或管理员权限运行容器。 使用命名空间和卷等功能而不是原始磁盘访问,允许这些容器守护程序在一个或多个容器之间共享存储以获取持久数据,而不需要容器本身具有升级的权限。 甚至还有一些项目,例如谷歌的gVisor,它们更进一步,隐藏了除容器需要运行的确切系统调用之外的所有项目。

    对容器的更大关注是构建容器的层的可信度。 有多种方法可以解决这个问题。 它们包括指向您已经测试并确定的特定版本,而不是依赖于最新的tag。 您还可以扩展针对无服务器应用程序中的第三方库所进行的任何扫描范围,以便扫描整个容器以查找已知漏洞。 此扫描可以在容器源中提前执行,也可以在构建过程中执行,因为您将它们用作构建的基础。
    # 虚拟机安全
    虚拟机是另一个需要解决的大问题。 有不少关于如何保护操作系统的书籍和最佳实践指南。 甚至美国政府也有一个团体在NIST网站上发布一些清单。 最近推出的操作系统提供了开箱即用的合理安全配置文件,但它们总需要进行改进。

    一种改进upis的方法是将运行服务限制为绝对需要的服务。 例如,默认的HTTP服务器很适合查看日志,但是当您的应用程序在Java中运行时,有不少可用的产品,哪些产品可以通过SSH连接并集中整合日志?

    另一种选择是在发布后尽快应用补丁。 一些补丁每月发布。 还有微软的“Patch Tuesday”,而其他更关键的补丁在有可用修复的那天发布(这些被称为out-of-band patch)。 与容器和无服务器不同,在完整虚拟机上需要应用任何指定补丁的几率要高得多,因为需要和安装的软件包要多得多。
    # 结论
    只有了解您和您的开发团队正在部署应用程序的计算环境类型,您才可能应用所有安全性最佳实践。 理想情况下,您的每个应用程序都可以被评估,并且我们鼓励您使用最合适和简化的部署选项。 通过将更多应用程序迁移到容器,并在适当的时候使用无服务器,它将使得类似生产的安全实践能够在开发周期的早期实施,并最终提高您的整体安全性。

    原文链接:Security Differences: Containers vs. Serverless vs. Virtual Machines(翻译:kelvinji2009)

    阿里集团八年容器化演进之路

    尼古拉斯 发表了文章 • 0 个评论 • 1680 次浏览 • 2018-09-11 08:02 • 来自相关话题

    【编者的话】近日,阿里集团内部已经实现 100% 容器化镜像化;距离 PouchContainer 开源不到一年时间,PouchContainer 开源版 1.0 GA 版本发布,已经完全达到生产级别。另外,作为百万级开源容器技术,PouchContainer ...查看全部
    【编者的话】近日,阿里集团内部已经实现 100% 容器化镜像化;距离 PouchContainer 开源不到一年时间,PouchContainer 开源版 1.0 GA 版本发布,已经完全达到生产级别。另外,作为百万级开源容器技术,PouchContainer 被收录进为高校教材《云计算导论》。

    PouchContainer 现在服务于阿里巴巴集团和蚂蚁金服集团的绝大部分 BU, 包括交易&中间件,B2B/CBU/ICBU,搜索广告数据库,还有收购或入股的一些公司,比如优酷高德、UC等。其中体量最大的是交易和电商平台,在 2017 年双 11 的时候我们支撑了破纪录的峰值,背后的应用都是跑在 PouchContainer 里面,整体容器实例已经到了百万级规模。使用了 PouchContainer 的应用涵盖了各种各样的场景。这些场景从运行模式来看,有标准的在线 App,还有像购物车、广告、测试环境等比较特殊的场景。不同的场景对 PouchContainer 有不同的使用方式和需求。从编程语言看,实际运行着 Java、C/C++,Nodejs,GoLang 等语言编写的应用。从技术栈的角度看,包含了电商、DB、流计算、大数据、专有云等场景,每个场景对于容器各方面要求,所用到的特性都不太一样,PouchContainer 针对每个场景的需求都在产品上都做了支持。

    PouchContainer 容器技术在阿里的演进过程伴随着阿里技术架构本身的演进。阿里内部技术架构经历了一个从集中式单体应用到分布式微服务化的演进。

    淘宝最开始是一个巨石型的应用,一个应用里包含了商品、用户、下单等等所有交易链路的功能。随着功能越来越完善,维护起来也越来越困难。为了提高研发效率,从 2008 年开始我们逐渐把这个应用拆分成了多个分布式应用,商品的,交易的,用户的,前台的,后端的;通过 HSF 远程调用框架,TDDL 分布式数据层和 Notify 分布式消息中间件串联起来。其中每个服务都有多个实例,都可以独立研发演进,并可以进一步继续拆分。于是就逐渐形成了一个庞大的分布式服务集群。从巨石型应用到多个单一功能的轻量级服务型应用,总的应用实例数变多了,每个实例需要的系统资源变少了。于是从最初的每个实例直接使用物理机自然过渡到使用 Xen,KVM 等虚拟化技术。VM 使用了一段时间之后,发现整体物理机的利用率还是很低。当时一个 24 核的物理机只能虚出 4 台 4 核的 VM,除了当时虚拟化本身的开销不小外,每个应用实例在 VM 里仍然用不完分到的资源。于是就想能不能不用虚拟机,用更轻量的基于进程级别的资源切分使用方式。

    这个时候阿里内部的运维体系已经比较庞大了,从应用的构建部署到分发,到一些运行期的监控告警等管控系统,都依赖于一个应用实例跑在一个独立机器里的假定。这个假定已经不经意间贯穿到了研发运维的各个环节里面,包括系统的设计,运维习惯等都严重依赖这个假定。我们不可能重新搭建集群,把存量的业务停掉再到新的集群里面用新的运维模式去跑起来,这个业务和运维上都是没法接受的,不可能电商交易的研发停几个月,系统停几天来搞这个事情。所以我们首先要做到兼容,新的资源使用方式必须兼容原先的假定。我们经过仔细分析了这个假定的内涵,发现每个应用实例归纳下来无非有如下 4 点要求:

    * 有独立IP
    * 能够SSH登陆
    * 有独立的,隔离的文件系统
    * 资源隔离,并且使用量和可见性隔离

    首先是有独立 IP,能够 SSH 登录。其次有独立的文件系统,应用程序跑起来,希望程序看到的整个文件系统都是给他专用的,因为现有的代码和配置中必然有很多路径的硬编码,需要满足这个潜在要求。还有不管通过工具还是代码,他只能看到分配给他自己的资源。比如 4 个 CPU,8G 的内存,他能够根据这些资源的用量做一些监控,做一些对自己资源使用量的采集和告警。这四个特点总结下来就是新的资源使用方式要做到和物理机或者 VM 的使用体验一致。能够做到这样的话原先跑在 VM 里的应用就可以很平滑的迁移过来,现有的应用系统和运维系统不需要做很大的改动。

    我们为了能达到这四点,最开始是多隆大神手工 Hack 系统调用,glibc 基础库等,实现了一些资源上的隔离。像有独立的 IP 可登录 ,就用虚拟网卡,在每个容器里面起一个 sshd 进程;资源的隔离和可见性上,就用 Cgroup 和 Namespace 等内核特性;后来发现开源的 LXC 项目也在做同样的事情,并且比手工 Hack 更通用化,更优雅一些。于是我们集成 LXC,并且在内核上加了定制的资源可见性隔离的 patch,让用户的实例只能看到分配给他的 CPU和内存,另外还增加了基于目录的磁盘空间隔离的 patch,这样就形成了我们第一代的容器产品。这个产品当时代号是 T4,寓意是第四代淘宝技术,淘宝 4.0;在 2011 年的时候 T4 容器技术灰度上线。T4 相比 VM,完全没有虚拟化 Hypervisor 层的开销,资源切分和分配上更加灵活,可以支持不同程度的资源超卖。这样就很好的支持了业务爆发增长的需求,控制了物理机按业务增长比例膨胀的势头。另外因为 T4 完全兼容了之前研发和运维对物理机和 VM 的使用习惯,绝大多数应用都能够做到透明的切换,应用无感知。因为有这些特性,在接下来的短短几年时间里,T4 逐步接管了交易和电商主体的在线应用。

    到 2015 年的时候 Docker 技术火起来了。我们写程序的都知道有个著名的公式,程序=数据结构+算法。从程序交付使用变成一个软件产品的角度来看,我们可以套用这个公式:

    * 软件= 文件(集)+ 进程(组);

    从静态来看,软件从构建分发到部署,最终形式是一个有依赖层次的文件集。从动态来看,这些文件集,包括二进制和配置,由操作系统加载到内存后执行,就是一个有交互关系的进程组。我们之前的 T4 容器在进程(组),或者说运行时上做的事情和 Docker 基本类似,比如说都使用了 Cgroup、Namespace、linux bridge 等技术。还有些是 T4 特有的,比如基于目录的磁盘空间的隔离,资源可见性隔离,对老版本内核的兼容等。我们从最早物理机演化到 VM,再到现在的容器,内核的升级周期比较漫长,迭代很慢,15年的时候存量的机器上全部都是 2.6.32 内核,T4是兼容 2.6.32 内核的。 但是另一方面在文件(集)的处理上 Docker 做得更好,更加系统化。 T4 只做了很薄的一层镜像,给相同的业务域做了一个基础的运行和配置环境,这个镜像没有深入到每一个特定的应用。 而 Docker 是将每个应用的整个依赖栈打包到了镜像中。因此在 2015 年我们引入了 Docker 的镜像机制来完善自己的容器。
    1.jpg

    在将 Docker 镜像整合进来之后,原来基于 T4 的研发运维体系受到了很大的冲击。 首先交付方式变了,之前是 build 一个应用的代码包,把代码包交给我们的部署发布系统,后者创建一个空的容器,根据这个业务所在的很薄的模板把一个空的容器跑起来,再到容器里面安装依赖的一些 IPM 包,设置一些配置,按每个应用定好的一个列表一个一个的安装好,然后把应用包解压启动起来。这个应用依赖的软件和配置列表我们内部叫做应用的基线。引入镜像之后,在将 Docker 镜像整合进来之后,原有的交付方式发生了变化。之前是 build 一个应用的代码包,把代码包交给我们的部署发布系统,后者创建一个空的容器,根据这个业务对应的很薄的一个模板,把一个空的容器跑起来,再到容器里面安装依赖的一些 RPM 包,设置一些配置,按每个应用定好的一个清单一个一个的安装好,然后把应用包解压到主目录启动起来。这个应用依赖的软件和配置清单我们内部叫做应用的基线。引入镜像之后,我们应用的代码包和依赖的所有的这些三方软件、二方软件都会打成一个镜像。之前通过基线维护应用依赖环境,现在都放到每个应用自己的 Dockerfile 中了,整个研发构建和分发运维的过程大大简化了。

    做了这个事情之后,研发和运维之间的职责和边界就发生了变化。之前研发只需要关注功能,性能,稳定性,可扩展性,可测试性等等。引入了镜像之后,因为要自己去写 Dockerfile,要了解这个技术依赖和运行的环境倒底是什么,应用才能跑起来,原来这些都是相应运维人员负责的。研发人员自己梳理维护起来后,就会知道这些依赖是否合理,是否可以优化等等。研发还需要额外关注应用的可运维性和运维成本,关注自己的应用是有状态的还是无状态的,有状态的运维成本就比较高。这个职责的转换,可以更好的让研发具备全栈的能力,思考问题涵盖运维领域后,对如何设计更好的系统会带来更深刻的理解。所以引入 Docker 之后对研发也提出了新的要求。我们总结新的时期,新的运维模式下对研发能力要求的几个要素,总结起来就是几个原则:
    2.jpg

    为了更好的把自己的系统建设好,我们要倡导研发从第一天建立系统的时候,就要考量最终的可运维性,比如参数是否可配置,是否可以随时重启。机器每天都有硬件故障产生,这些硬故障不可能每天都人工处理,必须要尽可能自动化处理,自动化处理时,虽然有些故障只影响了一部分实例,另一部分是好的,但是也可能需要一起处理,比如需要物理机上的业务全部迁移走来维修物理机的时候。所以不管当时容器里的业务是好的还是不好的,都要满足随时可重启,可迁移的要求。原来是部分交付,现在要考虑你到底运行环境是什么样的,什么样的运行环境才能跑起来,尽量做标准化的操作。比如说启动,Dockerfile 里面写好启动的路径,不要再搞一些特殊的处理,如果有任何特殊的处理都没法做统一的调度和运维。统一的业务迁移,机器腾挪也没法做。我们的目标其实就是从一开始的比较粗放的运维,到不断的开发自动化的工具和系统,形成一个体系,通过前期人工运维的过程把一些固定的故障处理的流程模式化,最后提取出来一些可以自动处理故障,自动恢复的机制。我们的最终目标是无人职守。所有这些加起来其实就是我们引入镜像化之后,并且要朝着无人值守的方向演进时,对研发和运维的新的要求。
    3.jpg

    上面是 PouchContainer 容器的 Roadmap, 2011 年的时候 T4上线 ,到 2015 年 3 月的T4 覆盖了交易的大部分应用。这个时候开始引入了 Docker 镜像机制,这里面做了很多兼容性的工作。比如说原来 T4 轻量化的模板转化成对应的基础镜像,里面兼容了很多之前运维的习惯和运维的工具,如账号推送,安全策略,系统检测。我们在 2016 年初上线了第一个镜像化应用,到 5 月份的时候集团决定主站全部应用容器化。在做镜像之前阿里是有一两百人的团队做每个应用的部署,运维,稳定性控制,后来这个团队都没有了,全部转成了 DevOps,转向开发工具和运维平台,通过代码的方式,工具的方式解决运维的问题。之前专职做运维的同学最大的负担就是线上环境的变更,研发提交变更申请给运维同学,运维同学做线上操作,研发不知道代码运行环境具体依赖了哪些基础软件。做了镜像化的事情后,研发自己负责编写 Dockerfile,运维就把环境变更的事情通过 Dockerfile 的机制移交给了研发。运维和研发之间的边界就非常清楚了,这个边界就是由 Dockerfile 来定义的。研发负责把他代码依赖的环境在 Dockerfile 定义好,运维保证其构建分发时没有问题。我们在 2016 年双11的时候完成了交易核心应用的镜像化 PouchContainer 化改造。在 2017 年双11的时候交易全部应用完成了镜像化改造。然后我们在 2017 年 11 月 19 日的时候宣布了 PouchContainer 的正式开源。

    我们的内部 PouchContainer 经过大规模的运行,支持了各种各样的业务场景,各种不同的技术栈,不同的运行形态,积累了非常多的经验。这些经验之前跟阿里内部的环境耦合性比较大。比如说我们的网络模型,我们其实是嵌入到了阿里内部的网络管控平台,包括IP分配在内部都有独立的系统去完成。比如什么时候启用 IP,什么时候下发路由等等,这些是有一个统一的 SDN 网络管理系统来管理的。还有类似的内部存储系统,还有运维的一些指令推送系统。内部系统耦合性比较大,没法直接开源。所以我们最后选择的策略是先在外部孵化一个从零开始全新的项目,把内部的特性一点点搬上去。这个过程中我们内部的版本也会做重构,把内部的依赖做一些插件化解耦合的方式,这样最后全新的项目在外部可以跑得很好;在内部用一些耦合内部环境的插件也可以跑起来,最终的目标是内外用一套开源版本。

    那么我们的 PouchContainer 容器相对于其他容器有什么差异呢?主要体现在隔离性、镜像分发优化、富容器模式、规模化应用和内核兼容性几个方面。传统的容器隔离维度就是 namespace、cgroup;在资源可见性方面,我们前几年是通过在内核上打 patch,在容器内看内存和 CPU 利用率等数据时,把统计数值和当前容器的 Cgroup 和 Namespace 关联起来,使容器能使用的资源和已使用的资源都是容器自己的。18年的时候我们引入了社区的lxcfs,这样就不需要对特定内核 patch 的依赖了。磁盘空间的限制也是在低版本内核上加了补丁,支持了基于文件目录的磁盘空间隔离,能够把每个容器的 rootfs 限制住。在 4.9 以上的内核上,我们是用 overlay2 文件系统来完成同样功能的。我们也在做基于 hypervisor 的容器方案,提升容器的隔离性和安全性,我们在 PouchContainer 里面集成了 RunV,用于一些多租户的场景。
    4.jpg

    阿里内部的离在线混部之所以能推进,在同一个机器上既能跑在线的业务又能跑离线的一些任务,互相之间不会出现太大的干扰,其核心的技术就是 PouchContaienr 容器可以根据优先级,把不同业务的资源使用隔离开来,保证在线业务优先使用资源。这个资源包括很多的维度,比如 CPU、内存,CPU cache、磁盘、网络等等。
    5.jpg

    这是 PouchContainer 的镜像分发设计。我们内部有很多比较核心的应用,体量比较大,实例会分布在上万台物理机上。发布新版本的时候上万台机器同时拉镜像,任何中心的镜像仓库都扛不住。因此我们设计了一套镜像分发的二级架构,在每个地域建一个 mirror,在同一个地域内拉镜像的时候用 P2P 分发技术---我们内部的产品名叫蜻蜓,已经开源;需要拉镜像的服务器之间可以分散互相拉文件片段,这样就直接化解了中心镜像仓库的服务压力和网络压力。后面其实还有更好的解决镜像分发的思路,我们正在尝试镜像的远程化,通过存储计算分离技术,用远程盘的方式挂载镜像,直接跳过或者说异步化了镜像分发这一步,目前正在内部环境灰度运行中。
    6.jpg

    这是 PouchContainer 内部版本的体系结构。在最底层的宿主机层面,我们会做一些管理和运维,目的是为了确保容器运行依赖的基础环境是健康的,包括宿主机的一些镜像清理,包括安全控制、权限管理等。OS 的低版本内核我们是适配到最低 2.6.32 内核,包括容器里面的进程管理也做了很多的适配。资源隔离前面讲过了,网络模型我们内部其实主体用的是 Bridge,但是其他各种各样的场景也都支持。我们开发了很多插件,PouchContainer 开源后,我们才将这些插件逐步做了标准化,兼容适配了社区的 CNI 标准。最上层是一个富容器模式的支持,每个容器里面会启动一些跟内部的运维工具,运维系统息息相关的一些组件,包括一些发布模式的优化。可以看到我们内部体系结构是比较复杂的,尤其依赖内部的其他系统比较多,在外部直接跑是跑不起来的,因此也没法直接开源。
    7.jpg

    所以我们开源版本是重新开始搭建的,这样会比较清爽一些。我们引入了contained,支持不同的 runtime 实现,包括我们自己包装 lxc 开发的 RunLXC 运行时,可以用来支持老版本 2.6.32 内核。开源版 PouchContainer 兼容所有 Docker 的接口,也支持 CRI 协议,这样也就同时支持了比较主流的两种集群管理系统。网络方面我们内部基于 libnetwork 做了增强,包括不同场景暴露出来的一些问题,一些稳定性,规模化的时候各种细节的一些优化。存储方面我们支持了多盘,内存盘,远程盘等各种不同形式的存储。PouchContainer 可以无缝集成到上层编排工具中,包括 Kubelet 和 Swarm 等。我们内部的 Sigma 调度系统,不同的版本Docker 协议和CRI协议都会使用。

    这是 PouchContainer 的开源地址:https://github.com/alibaba/pouch

    如何贡献:https://github.com/alibaba/pouch/blob/master/CONTRIBUTING.md

    最近 PouchContainer 开源版本 GA 已经发布,PouchContainer 能够在如此短的时间内 GA,离不开容器社区的支持,在超过 2300 个 commit 的背后,有 80 多位社区开发者的踊跃贡献,其中不乏国内一线互联网公司、容器明星创业公司贡献者的参与。

    PouchContainer 开源版本发布 GA 之前,此开源容器引擎技术已在阿里巴巴数据中心得到大规模的验证;GA 之后,相信其一系列的突出特性同样可以服务于行业,作为一种开箱即用的系统软件技术,帮助行业服务在推进云原生架构转型上占得先机。

    Q:你们是怎么样做到把阿里巴巴集团包括高德还有菜鸟那些,都能把这个技术推过去,因为大公司在不同的部门跨部门甚至是跨子公司之间要想推行你们的某一个部门的研究成果是一件比较困难的事情。
    A:这是一个好问题,我们其实也面临过这个问题。我们的方法就是首先要和大家宣导这个理念,让大家在认知上都接受镜像化运维能带来的优势,长远发展的好处。虽然很难有直接立竿见影的收益,长远来看一定能提高运维效率,降低资源使用的成本。实际上从这两年来看,我们确实降低了不少运维成本。

    Q:你好,我想问一下容器里面的那些持久化是怎么处理的?
    A:容器我们持久化现在大体分两类数据,一个是日志,一种是应用自己会写一些数据,像搜索业务。要么就是放在本地盘,放在本地的话做迁移的时候要自己处理数据的迁移,每个不同的业务处理的都不太一样。还有一种方式是用远程,数据远程化。我们有分布式存储系统“盘古”,通过容器创建的时候在远程存储集群建一块远程盘,我们现在用的是块设备,然后挂载到容器里面,容器用完或者是迁移的时候,数据是在远端的,可以随意迁移到另一个地方,再把这个数据盘挂载回来。

    搜索也可以放远端,对于阿里各种搜索场景,我理解如果replica数多的话,用远端存储是比较经济划算的,如果replica数就1行,或2行,而且远端性能又满足不了部分场景的需求,短时间内就不如本地多块盘来进行混部。总体趋势来说,如果没有性能要求的话,都放远端是趋势。

    Q:哪种方式会更多一些?
    A:宿主机直接到容器里面,相对来说最大的场景是在数据库,数据库现在大部分是在本地,但是有一部分是放在远端的,正在演进的过程中,还没有百分之百完成存储计算的分离。后面有一天可能就完全没有本地数据了。

    Q:这是云框架,一听到云这个架构就感觉很大,是一个什么海量运维,海量数据,对于中小型公司规模可能没那么大,对于要想用这套框架,实施的成本是多少,用户量在多少以下适合或者不适合?
    A:很难有明确的临界点,说什么时候该用云化架构了。从中小型公司来说可以从第一天就往这个方向,或者是朝这个模式去实现。比如说搭一个很小的资源池,通过弹性混合云的方式,在云上去扩容,这也是一种很好的方式。如果第一天完全不考虑这些事情,怎么方便怎么搭建,也不考虑这些单点,容灾这些弹性的事情,后面改造起来可能就会比较痛苦。

    Q:这个部署的成本,两个人或者三个人的研发团队,用你这个东西周期有多长时间呢?它的难易度,因为要理解整个框架,你要部署这个东西要理解这个东西,我觉得学习的曲线还有部署的难度到底是什么样的?
    A:后面这套系统在做 Sigma 敏捷版就是解决中小企业的问题,两三个开发者不可能开发出一套像现在这个规模的完整云化架构,最好的是用云上支持这些场景的产品。云产品本身经过很多用户的考验,有这么多云上运作的一些经验,一些技术上的沉淀,比自己开发要靠谱得多。

    Q:我想问一下 PouchContainer 这个容器跟底层还会去封装 Docker 之类的东西,我第一次接触这个,另外镜像库的话是能够跟 Docker 兼容吗?
    A:首先镜像库跟 Docker 是完全兼容的,Docker 分了很多层,底层的 runV 和 containerd 都贡献到了社区,是开源的,我们在 runV 和 containerd 的基础上做了增强。总体来说是兼容两个社区的两种主流的技术路线,两种集群管理系统,Kubernetes 和 Docker 公司 Swarm,这种两种路径都支持。

    DockOne微信分享(一八四):基于Spring Cloud的微服务容器化实践

    DarkForces. 发表了文章 • 0 个评论 • 2952 次浏览 • 2018-08-13 00:01 • 来自相关话题

    【编者的话】近几年,互联网飞速发展的同时,也推动了云计算、大数据、人工智能的快速落地,数据本身价值也得到提升。互联网发展对应用开发提出了更高要求。首先数据采集的量级和效率提高,传统的单体架构将出现瓶颈,其次是数据联通性的需求,对数据对接必须保证高性能、高安全、 ...查看全部
    【编者的话】近几年,互联网飞速发展的同时,也推动了云计算、大数据、人工智能的快速落地,数据本身价值也得到提升。互联网发展对应用开发提出了更高要求。首先数据采集的量级和效率提高,传统的单体架构将出现瓶颈,其次是数据联通性的需求,对数据对接必须保证高性能、高安全、高标准。使用微服务架构恰好解决了大部分痛点。

    本次主要介绍基于Spring Cloud构建微服务,以及配套的DevOps思路,并着重介绍在Docker容器里如何部署基于Spring Cloud的微服务。
    #1、基于Spring Cloud构建微服务
    历史总是惊人的相似,合久必分,分久必合。我们经历了“合”:单体架构(软)、计算能力超强的小型机(硬)到“分”:分布式架构的转变,后期可能会将分发挥到了极致(去中心化的分布式,如区块链),最后很可能再经历“合”:计算和存储能力超强的“智人”(集超级计算和存储一身的人工智能)。

    单体架构也有自身优势,这里不做详细介绍,大家在做架构选型时可以根据公司组织架构和业务需求综合考虑。

    Spring Cloud作为Java语言的微服务框架,它依赖于Spring Boot,是由Pivotal团队提供的全新Web框架。有快速开发、持续交付和容易部署等特点。Spring Cloud提供了开发分布式服务系统的一些常用组件,例如服务注册和发现、配置中心、熔断器、智能路由、微代理、控制总线、全局锁、分布式会话等。

    先看看我们使用的一个架构:
    图片1.png


    • 服务发现中心(Service Discovery):服务注册与发现组件(选用Erueka)
    • 监控面板(Monitor Dashboard):整合了熔断监控(选用Hystrix)、服务调用链路监控(选用Spring Cloud Sleuth)
    • 日志分析面板(Logging Analyses):基于efk构建的日志采集系统
    • 服务网关(Edge Server):服务网关组件(Zuul & Spring Cloud Security)
    • OAuth认证服务器(OAuth Authorization Server):授权认证模块(Spring Cloud security OAuth2)
    • 配置服务器(Configuration Server):配置中心(Spring Cloud Config & Spring Cloud Bus)
    #2、基于微服务架构体系的DevOps思路DevOps带来的不仅是技术挑战,还受公司组织架构和文化影响,这里是从技术角度去实现的思路。先看两个微服务应用案例:##案例1:基于微服务架构的接口开发平台主要提供Restful API服务,服务方式多样,其中一个最简单的案例流程如下:
    • 首先企业通过申请账号、密码和需要调用的API
    • 平台针对申请企业创建可调用API的账号和密码,并将该企业调用端IP加入服务白名单,用户可在平台下载文档、SDK,并进行测试
    • 企业根据用户名、密码去获取token,并在请求header中加入申请的token调用接口
    图片2.png
    ##案例2:基于微服务架构的应用开发Web应用开发采用前后端分离方式,前端采用AngularJS,后端仍是基于Spring Cloud的微服务,整套系统部署到容器云实现CI/CD,架构如下图所示:
    图片3.png
    微服务引入增加了团队配合、测试、运维等后续一系列操作的复杂度,必须考虑自动化,因此需要有一套CI/ CD方案应对:
    图片4.png
    大致流程:[list=1]
  • 研发完成本地开发和测试提交代码
  • 代码连同Dockerfile等构建文件一起push到GitLab(可以自己搭建)
  • 代码提交触发Jenkins自动构建
  • Jenkins调用单元测试、代码检查等测试任务,如果测试通过自动构建Docker镜像
  • Jenkins将构建好的镜像推送到私有镜像仓库(自己搭建Harbor镜像库)
  • Jenkins调用容器管理平台(我们使用的Rancher)的接口进行升级
  • 容器管理平台拉取镜像完成部署(测试环境or生产环境)
  • 说明:这里我们不仅使用了Docker,还选用容器编排工具构建了容器云平台,以方便我们快速实现CI/CD。大家可以根据自己情况选择,如Kubernetes、Rancher(Rancher 2.0以后底层使用的编排工具也是Kubernetes)等。#3、Spring Cloud基于Docker的实践我们使用Docker,主要因为以下4点:[list=1]
  • Docker提供一个简单、轻量的建模方式
  • Docker使团队职责的逻辑分离
  • 可以实现快速高效的开发生命周期
  • 团队使用面向服务的架构
  • 以下介绍如何构建Docker镜像和配置细节。##1. 项目目录Dockerfile及相关文件一般放到项目子目录,由开发维护。这种方式对研发提出了更高的技能要求,这也是近期全栈工程师比较火的一个原因。当然,具体协作方式还是根据公司情况定。以下是我们的项目目录:
    图片5.png
    ##2、编写Dockerfile(供参考)注:配合自动化工具使用效果更好。
    FROM harbor.jry.com/library/alpine-jdk-time:slimADD *.jar service.jarCOPY formFile /ENTRYPOINT [ "sh", "-c", "java -jar service.jar" ] 
    ##3、模块配置我们将Spring Cloud相关的配置放到了bootstrap.yml文件,需要注意的配置如下:
    图片6.png
    注意标红部分,服务要以IP地址方式注册到注册中心,否则注册中心无法做心跳检测,因为容器之间默认无法通过hostname通信。我们也可以搭建内部DNS方式解决此问题。服务发现中心建议配置成高可用(比如两个注册中心互相注册),也需要上图配置。##4、打包构建这里截取了Jenkins里的打包及构建命令:[list=1]
  • 打包
  • 图片7.png
    [list=1]
  • 构建
  • 图片8.png
    ##5、容器启动配置(docker-compose.yml)
    version: '2'services:  zipkin:    image: harbor.jry.com/zipkin:v1.0.1    environment:      eureka.client.service-url.defaultZone: http://discovery:8761/eureka/    stdin_open: true    tty: true     links:    - discovery2:discovery     volumes:    - /data/log:/target/log    ports:    - 9411:9411/tcp
      service-config:    image: harbor.jry.com/service-config:v1.0.1    hostname: config    environment:      eureka.client.service-url.defaultZone: http://discovery:8761/eureka/    stdin_open: true    tty: true    links:    - discovery2:discovery    volumes:    - /data/log:/target/log    ports:    - 8889:8889/tcp
      service-monitor:    image: harbor.jry.com/service-monitor:v1.0.1    environment:      eureka.client.service-url.defaultZone: http://discovery:8761/eureka/    stdin_open: true    tty: true    links:    - discovery1:discovery    volumes:    - /data/log:/target/log
      discovery2:    image: harbor.jry.com/service-discovery    environment:      eureka.client.service-url.defaultZone: http://discovery:8761/eureka/    stdin_open: true    tty: true    links:    - discovery1:discovery    volumes:    - /data/log:/target/log    ports:    - 8762:8761/tcp
      discovery1:    image: harbor.jry.com/service-discovery:v1.0.1    environment:      eureka.client.service-url.defaultZone: http://discovery:8761/eureka/    stdin_open: true    tty: true    links:    - discovery2:discovery    volumes:    - /data/log:/target/log
      daas-monitor:    image: harbor.jry.com/daas-monitor:v1.0.1    environment:      eureka.client.service-url.defaultZone: http://discovery:8761/eureka/    stdin_open: true    tty: true    links:    - discovery1:discovery    volumes:    - /data/log:/target/log    ports:    - 9080:9080/tcp
      proxy-server:    image: harbor.jry.com/service-proxy:v1.0.1    environment:      eureka.client.serviceUrl.defaultZone: http://discovery:8761/eureka/    stdin_open: true    links:    - discovery1:discovery    tty: true    links:    - oauth2-server:oauth    volumes:    - /data/log:/target/log    ports:    - 8111:8443/tcp
      oauth2-server:    image: harbor.jry.com/oauth2-cg:v1.0.1    environment:      eureka.client.serviceUrl.defaultZone: http://discovery:8761/eureka/    stdin_open: true    links:    - discovery2:discovery    volumes:    - /data/log:/target/log    tty: true    ports:    - 9999:9999/tcp
    说明:
    • Zipkin:服务调用链路监控
    • service-config:配置中心服务
    • service-monitor:断路监控服务
    • discovery1, discovery2:高可用的服务发现中心
    • daas-monitor:自研的监控面板,整合其他监控服务
    • proxy-server:服务网关
    • oauth2-server:OAuth认证服务器
    注:[list=1]
  • 虽然使用的配置文件,但维护起来还是很麻烦,所以建议使用编排工具,一般会提供部分DevOps工具集
  • 上述各服务模块可以根据实际情况启动多个相同容器,以保证高可用
  • 以上各服务模块做了磁盘映射,主要为采集日志,这里我们使用的EFK,时间关系暂不展开。

  • #Q&A
    Q:WSO2的API Manager会将所有流量都统一到他这里进出,如何解决统一API网关的大流量问题?

    A:API Manager是可以拆开的,分开部署,比如调用你接口走网关模块,可以做高可用。当然不拆开也可以做高可用,前面加负载均衡。



    Q:Eureka在生产上的Kubernetes中是如何做到高可用和动态扩容的?

    A :好问题,Eureka我们目前是指定数量,可以结合脚本(或代码)做动态扩容和高可用。目前我们Hadoop就是结合代码做到的。



    Q:服务之间二阶段事务控制一般怎么做?或者说有没有现成工具或代码?服务间用http靠谱还是其他协议靠谱?

    A:这个网上有一些思路比如补偿的方式,还有就是通过流数据库(Kafka),事件驱动的方式。我们目前正在尝试这种方式。

    未标题-1.jpg



    以上内容根据2018年7月31日晚微信群分享内容整理。分享人刘凯,现就职于中金云金融(北京)大数据科技股份有限公司,负责应用架构设计及大数据平台建设。2015年开始实践微服务架构,参与基于Dubbo的微服务搭建,并实现部分源码优化、实现安卓端SDK,2016年参与公司区块链应用预研,同年开始进行基于Spring Cloud的微服务架构实践,2017年作为团队核心人员完成容器云搭建、大数据平台搭建及实践、DevOps工具集优化。DockOne每周都会组织定向的技术分享,欢迎感兴趣的同学加微信:liyingjiesd,进群参与,您有想听的话题或者想分享的话题都可以给我们留言。

    devicemapper存储,容器mount点的问题

    回复

    徐新坤 回复了问题 • 2 人关注 • 3 个回复 • 3802 次浏览 • 2019-04-02 18:01 • 来自相关话题

    DB2数据库容器化后写入速度变慢

    回复

    BruceKun 发起了问题 • 1 人关注 • 0 个回复 • 1100 次浏览 • 2018-06-15 17:07 • 来自相关话题

    为什么要Docker,为什么要虚拟化,为什么要容器?

    回复

    云龙云 回复了问题 • 3 人关注 • 3 个回复 • 6809 次浏览 • 2018-06-01 00:17 • 来自相关话题

    【招聘帖】【上海】【携程】为了打造混合云和容器云,上海携程云平台招聘各类优秀工程师

    回复

    ljpsfree 回复了问题 • 1 人关注 • 2 个回复 • 1916 次浏览 • 2017-12-28 09:52 • 来自相关话题

    Docker容器使用--net=host的方式启动的,怎么用ssh去连接容器

    回复

    cannyco 回复了问题 • 15 人关注 • 9 个回复 • 26611 次浏览 • 2017-11-06 10:57 • 来自相关话题

    Vmware北京研发招聘容器开发(软件定义网络)

    回复

    Jackyguo11 发起了问题 • 1 人关注 • 0 个回复 • 2095 次浏览 • 2017-09-27 15:52 • 来自相关话题

    有没有如何评估一个旧的应用容器化可行性评估的材料?

    回复

    xds2000 回复了问题 • 2 人关注 • 1 个回复 • 1861 次浏览 • 2017-08-28 01:05 • 来自相关话题

    容器运行一段时间后会变的很慢,重启之后恢复正常,什么原因?

    回复

    xiphis 回复了问题 • 3 人关注 • 2 个回复 • 2455 次浏览 • 2017-04-14 11:38 • 来自相关话题

    容器之间多主机通信问题

    回复

    wisen 回复了问题 • 3 人关注 • 2 个回复 • 2166 次浏览 • 2017-04-11 09:02 • 来自相关话题

    如何将容器内的日志挂载到宿主机中?

    回复

    wisen 回复了问题 • 2 人关注 • 1 个回复 • 3735 次浏览 • 2017-04-10 12:45 • 来自相关话题

    容器环境下Node.js的内存管理

    老马 发表了文章 • 0 个评论 • 157 次浏览 • 2019-06-03 15:26 • 来自相关话题

    【编者的话】在基于容器的Node.js应用程序中管理内存的最佳实践。 在docker容器中运行Node.js应用程序时,传统的内存参数调整并不总是按预期工作。本文我们将阐述在基于容器的Node.js应用程序内存参数调优中并不总是有效的 ...查看全部
    【编者的话】在基于容器的Node.js应用程序中管理内存的最佳实践。

    在docker容器中运行Node.js应用程序时,传统的内存参数调整并不总是按预期工作。本文我们将阐述在基于容器的Node.js应用程序内存参数调优中并不总是有效的原因,并提供了在容器环境中使用Node.js应用程序时可以遵循的建议和最佳实践。
    #综述
    当Node.js应用程序运行在设置了内存限制的容器中时(使用docker --memory选项或者系统中的其他任意标志),请使用--max-old-space-size选项以确保Node.js知道其内存限制并且设置其值小于容器限制。

    当Node.js应用程序在容器内运行时,将Node.js应用程序的峰值内存值设置为容器的内存容量(假如容器内存可以调整的话)。

    接下来让我们更详细地探讨一下。
    #Docker内存限制
    默认情况下,容器是没有资源限制的,可以使用系统(OS)允许的尽可能多的可用内存资源。但是docker 运行命令可以指定选项,用于设置容器可以使用的内存或CPU。

    该docker-run命令如下所示:docker run --memory --interactive --tty bash。

    参数介绍:

    * x是以y为单位的内存
    * y可以是b(字节),k(千字节),m(兆字节),g(千兆字节)

    例如:docker run --memory 1000000b --interactive --tty bash将内存或CPU限制设置为1,000,000字节。

    要检查容器内的内存限制(以字节为单位),请使用以下命令:
    cat /sys/fs/cgroup/memory/memory.limit_in_bytes

    接下来我们一起来看下设置了--max_old_space_size之后容器的各种表现。

    “旧生代”是V8内存托管堆的公共堆部分(即JavaScript对象所在的位置),并且该--max-old-space-size标志控制其最大大小。有关更多信息,请参阅关于-max-old-space-size

    通常,当应用程序使用的内存多于容器内存时,应用程序将终止。如果你想和更多容器技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

    以下示例应用程序以10毫秒的间隔插入记录到列表。这个快速的间隔使得堆无限制地增长,模拟内存泄漏。
    'use strict';
    const list = [];
    setInterval(()=> {
    const record = new MyRecord();
    list.push(record);
    },10);
    function MyRecord() {
    var x='hii';
    this.name = x.repeat(10000000);
    this.id = x.repeat(10000000);
    this.account = x.repeat(10000000);
    }
    setInterval(()=> {
    console.log(process.memoryUsage())
    },100);

    本文所有的示例程序都可以在我推入Docker Hub的Docker镜像中获得。你也可以拉取Docker镜像并运行程序。使用docker pull ravali1906/dockermemory来获取图像。

    或者,你可以自己构建镜像,并使用内存限制运行镜像,如下所示:
    docker run --memory 512m --interactive --tty ravali1906/dockermemory bash

    ravali1906/dockermemory是镜像的名称。

    接下来,运行内存大于容器限制的应用程序:
    $ node --max_old_space_size=1024 test-fatal-error.js
    { rss: 550498304,
    heapTotal: 1090719744,
    heapUsed: 1030627104,
    external: 8272 }
    Killed

    PS:

    * --max_old_space_size 取M为单位的值
    * process.memoryUsage() 以字节为单位输出内存使用情况

    当内存使用率超过某个阈值时,应用程序终止。但这些阈值是多少?有什么限制?我们来看一下约束。
    #在容器中设置了--max-old-space-size约束的预期结果
    默认情况下,Node.js(适用于11.x版本及以下)在32位和64位平台上使用最大堆大小分别为700MB和1400MB。对于当前默认值,请参阅博客末尾参考文章。

    因此,理论上,当设置--max-old-space-size内存限制大于容器内存时,期望应用程序应直接被OOM(Out Of Memory)终止。

    实际上,这可能不会发生。
    #在容器中设置了--max-old-space-size约束的实际结果
    并非所有通过--max-old-space-size指定的内存的容量都可以提前分配给应用程序。

    相反,为了响应不断增长的需求,JavaScript内存堆是逐渐增长的。

    应用程序使用的实际内存(以JavaScript堆中的对象的形式)可以在process.memoryUsage()API中的heapUsed字段看到。

    因此,现在修改后的期望是,如果实际堆大小(驻留对象大小)超过OOM-KILLER阈值(--memory容器中的标志),则容器终止应用程序。
    实际上,这也可能不会发生。

    当我在容器受限的环境下分析内存密集型Node.js应用程序时,我看到两种情况:

    * OOM-KILLER在heapTotal和heapUsed的值都高于容器限制之后,隔一段很长的时间才执行。
    * OOM-KILLER根本没有执行。

    #容器环境中的Node.js相关行为解释
    监控容器中运行应用程序的重要指标是驻留集大小(RSS-resident set size)。

    它属于应用程序虚拟内存的一部分。

    或者说,它代表应用程序被分配的内存的一部分。

    更进一步说,它表示应用程序分配的内存中当前处于活动状态的部分。

    并非应用程序中的所有已分配内存都属于活动状态,这是因为“分配的内存”只有在进程实际开始使用它时才会真实分配。另外,为了响应其他进程的内存需求,系统可能swap out当前进程中处于非活动或休眠状态的内存给其他进程,后续如果当前进程需要的时候通过swapped in重新分配回来。

    RSS反映了应用程序的可用和活动的内存量。
    #证明
    ##示例1.创建一个大小超过容器内存限制的空Buffer对象
    以下buffer_example.js为往内存分配空Buffer对象的实例代码:
    const buf = Buffer.alloc(+process.argv[2] [i] 1024 [/i] 1024)
    console.log(Math.round(buf.length / (1024 * 1024)))
    console.log(Math.round(process.memoryUsage().rss / (1024 * 1024)))

    运行Docker镜像并限制其内存用量:
    docker run --memory 1024m --interactive --tty ravali1906/dockermemory bash

    运行该应用程序。你会看到以下内容:
    $ node buffer_example 2000
    2000
    16

    即使内存大于容器限制,应用程序也不会终止。这是因为分配的内存还未被完全访问。rss值非常低,并且没有超过容器内存限制。
    ##示例2.创建一个大小超过容器内存限制的并填满的Buffer对象
    以下为往内存分配Buffer对象并填满值的实例代码:
    const buf = Buffer.alloc(+process.argv[2] [i] 1024 [/i] 1024,'x')
    console.log(Math.round(buf.length / (1024 * 1024)))
    console.log(Math.round(process.memoryUsage().rss / (1024 * 1024)))

    运行Docker镜像并限制其内存用量:
    docker run --memory 1024m --interactive --tty ravali1906/dockermemory bash

    运行该应用程序
    $ node buffer_example_fill.js 2000
    2000
    984

    即使在这里应用也没有被终止!为什么?当活动内存达到容器设置限制时,并且swap space还有空间时,一些旧内存片段将被推送到swap space并可供同一进程使用。默认情况下,docker分配的交换空间量等于通过--memory标志设置的内存限制。有了这种机制,这个进程几乎可以使用2GB内存 - 1GB活动内存和1GB交换空间。简而言之,由于内存的交换机制,rss仍然在容器强制限制范围内,并且应用程序能够持续运行。
    ##示例3.创建一个大小超过容器内存限制的空Buffer对象并且限制容器使用swap空间
    const buf = Buffer.alloc(+process.argv[2] [i] 1024 [/i] 1024,'x')
    console.log(Math.round(buf.length / (1024 * 1024)))
    console.log(Math.round(process.memoryUsage().rss / (1024 * 1024)))

    运行镜像时限制docker内存,交换空间和关闭匿名页面交换,如下所示:
    docker run --memory 1024m --memory-swap=1024m --memory-swappiness=0 --interactive --tty ravali1906/dockermemory bash

    $ node buffer_example_fill.js 2000
    Killed

    当--memory-swap的值等于--memory的值时,它表示容器不使用任何额外的交换空间。此外,默认情况下,容器的内核可以交换出一定比例的匿名页,因此将--memory-swappiness设置为0以禁用它。因此,由于容器内没有发生交换,rss超出了容器限制,在正确的时间终止了进程。
    #总结和建议
    当您运行Node.js应用程序并将其--max-old-space-size设置为大于容器限制时,看起来Node.js可能不会“尊重”容器强制限制。但正如您在上面的示例中看到的,原因是应用程序可能无法使用标志访问JavaScript堆集的全长。

    请记住,当您使用的内存多于容器中可用的内存时,无法保证应用按期望行为方式运行。为什么?因为进程的活动内存(rss)受到许多因素的影响,这些因素超出了应用程序的控制范围,并且可能依赖于高负载和环境 - 例如工作负载本身,系统中的并发级别,操作系统调度程序,垃圾收集率等。此外,这些因素可以在运行之间发生变化。
    #关于Node.js堆大小的建议(当你可以控制它,但不能控制容器大小时)

    * 运行一个空的Node.js应用程序,并测量空转情况下rss的使用情况(我在Node.js v10.x版本得到它的值约为20 MB)。
    * 由于Node.js在堆中具有其他内存区域(例如new_space,code_space等),因此假设其默认配置会占用额外的20 MB。如果更改其默认值,请相应地调整此值。
    * 从容器中的可用内存中减去此值(40 MB),得到的值设置为JavaScript的旧生代大小,应该是一个相当安全的值。

    #关于容器内存大小的建议(当你可以控制它,但不能控制Node.js内存时)

    * 运行涵盖高峰工作负载的应用程序。
    * 观察rss空间的增长。使用top命令和process.memoryUsage()API得到最高值。
    * 如果容器中不存在其他活动进程,将此值用作容器的内存限制。该值上浮10%以上会更加安全。

    #备注
    如果在容器环境下运行,Node.js 12.x的堆内存限制根据当前可用内存进行配置,而不是使用默认值。对于设置了max_old_space_size的场景,上面的建议仍然适用。此外,了解相关限制可以让您更好地调整应用并发挥应用的性能,因为默认值是相对保守的。

    有关更多信息,请参阅配置默认堆转储

    作者:Make_a_decision
    链接:https://juejin.im/post/5cef9efc6fb9a07ec56e5cc5

    容器监控之kube-state-metrics

    徐亚松 发表了文章 • 0 个评论 • 167 次浏览 • 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 个评论 • 157 次浏览 • 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在未来会成为构建容器云的三驾马车。

    容器、微服务与服务网格

    cleverlzc 发表了文章 • 0 个评论 • 256 次浏览 • 2019-05-26 11:03 • 来自相关话题

    【编者的话】本文结合dotCloud的发展为例,阐述了一个基本的服务网格应该具备什么样的能力,对诸如Istio、Linkerd、Consul Connect等现代服务网格系统的基本模式进行了阐述,最后对于“自建还是购买(或者使用开源)”这个老生常谈的话题作者给 ...查看全部
    【编者的话】本文结合dotCloud的发展为例,阐述了一个基本的服务网格应该具备什么样的能力,对诸如Istio、Linkerd、Consul Connect等现代服务网格系统的基本模式进行了阐述,最后对于“自建还是购买(或者使用开源)”这个老生常谈的话题作者给出了自己的见解。

    如你所知,已经有很多关于服务网格的资料(1234),但这是另外一篇。是的!但是为什么会有这篇文章呢?因为我想给你们一些不同的视角,他们希望服务网格在10年前就已经存在,远早于Docker和Kubernetes这样的容器平台的兴起。我并不是说这个视角比其他视角更好或更差,但是由于服务网格是相当复杂的“野兽”,所以我相信多种视角有助于更好地理解它们。

    我将讨论dotCloud平台,这是一个建立在100多个微服务之上的平台,支持数千个运行在容器中的生产应用程序;我将解释在构建和运行它时所面临的挑战;以及服务网格会(或不会)提供帮助。
    # dotCloud的历史
    我已经写过关于dotCloud平台的历史和它的一些设计选择,但是我没有过多地讨论它的网络层。如果你不想深入了解我之前关于dotCloud的博客,你需要知道的是它是一个PaaS,允许客户运行各种应用程序(Java、PHP、Python等),支持广泛的数据服务(MongoDB、MySQL、Redis等)以及类似于Heroku的工作流程:你可以将代码推送到平台,平台将构建容器映像,并部署这些容器映像。

    我将告诉你流量是如何在dotCloud平台上路由的;不是因为它是特别棒或其他什么(我认为现在是比较合适的时间),但主要是因为,如果一个普通的团队需要一种在一个微服务群或一个应用程序群之间路由流量的方法,那么这种设计可以在短时间内用现在已有的工具轻松实现。因此,它将为我们提供一个很好的比较点,“如果我们破解它,我们会得到什么”和“如果我们使用现有的服务网格,我们会得到什么”,也就是老生常谈的“构建与购买”的困境。
    # 托管应用的流量路由
    部署在dotCloud上的应用程序会暴露HTTP和TCP端点。

    HTTP端点被动态地添加到Hipache负载平衡器集群的配置中。这与我们今天使用Kubernetes Ingress资源和Traefik这样的负载平衡器可以实现的功能类似。如果你想和更多Kubernetes技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

    只要域名指向dotCloud的负载平衡器,客户端就可以使用它们的关联域名连接到HTTP端点。这里没有什么特别的。

    TCP端点与端口号相关联,然后端口号通过环境变量与该堆栈上的所有容器通信。

    客户端可以使用指定的主机名(类似于gateway-X.dotcloud.com)和端口号连接到TCP端点。

    该主机名将解析为一个“nats”服务器集群(与NATS没有任何关系),该集群将把传入的TCP连接路由到正确的容器(或者,在负载平衡服务的情况下,路由到正确的容器)。

    如果你熟悉Kubernetes,这可能会让你想起NodePort服务。

    dotCloud平台没有集群IP服务的等价物:为了简单起见,从内部和外部访问服务的方式是相同的。

    这非常简单,最初的HTTP和TCP路由网格的实现可能都是几百行Python代码,使用相当简单(我敢说,很天真)的算法,但是随着时间的推移,它们不断发展,以处理平台的增长和额外的需求。

    它不需要对现有应用程序代码进行大量重构。十二因素应用程序尤其可以直接使用通过环境变量提供的地址信息。
    # 它与现代服务网络有何不同?
    可观察性有限。对于TCP路由网格根本没有度量标准。至于HTTP路由网格,后来的版本提供了详细的HTTP度量,显示错误状态码和响应时间;但是现代服务网格的功能远远不止于此,它还提供了与度量收集系统(例如Prometheus)的集成。

    可观察性非常重要,不仅从操作角度(帮助我们解决问题),还可以提供安全的蓝/绿部署金丝雀部署等功能。

    路由效率也受到限制。在dotCloud路由网格中,所有流量都必须经过一组专用路由节点。这意味着可能跨越几个AZ(可用性区域)边界,并显著增加延迟。我记得对一些代码进行故障排除,这些代码发出100多个SQL请求来显示给定的页面,并为每个请求打开了到SQL服务器的新连接。在本地运行时,页面会立即加载,但在dotCloud上运行时,需要几秒钟,因为每个TCP连接(以及随后的SQL请求)都需要几十毫秒才能完成。在这种特定的情况下,使用持久连接起了作用。

    现代服务网络做得更好。首先,通过确保连接在源位置路由。逻辑流仍然是客户端-->网格-->服务,但是现在网格在本地运行,而不是在远程节点上运行,因此客户端-->网格连接是本地连接,因此速度非常快(微秒而不是毫秒)。

    现代服务网格还实现了更智能的负载平衡算法。通过监控后端的运行及健康状况,它们可以在更快的后端上发送更多的流量,从而提高整体性能。

    随着现代服务网络的出现,安全性也越来越强。dotCloud路由网格完全在EC2 Classic上运行,并且没有加密流量(假设如果有人设法嗅探EC2上的网络流量,那么无论如何都会遇到更大的问题)。现代服务网格可以透明地保护我们所有的通信,例如通过相互的TLS身份验证和随后的加密。
    # 平台服务的流量路由
    OK,我们已经讨论了应用程序是如何通信的,但是dotCloud平台本身呢?

    平台本身由大约100个微服务组成,负责各种功能。其中一些服务接受来自其他服务的请求,而其中一些服务是后台工作应用,它们将连接到其他服务,但不能自己接收连接。无论哪种方式,每个服务都需要知道它需要连接到的地址的端点。

    许多高级服务都可以使用上面描述的路由网格。事实上,dotCloud平台的100多个微服务中有很大一部分是作为常规应用程序部署在dotCloud平台上的。但是少数低级服务(特别是那些实现路由网格的服务)需要一些更简单的东西,需要更少的依赖关系(因为它们不能依靠自己来运行;这是一个老生常谈的“先有鸡还是先有蛋”的问题)。

    通过直接在几个关键节点上启动容器,而不是依赖于平台的构建器、调度程序和运行器服务,部署了这些底层的基本平台服务。如果你想要与现代容器平台进行比较,这就像直接在节点上运行Docker来启动我们的控制平面,而不是让Kubernetes为我们做这件事。这与kubeadmbootkube在引导自托管集群时使用的静态Pod的概念非常相似。

    这些服务以一种非常简单和粗糙的方式被公开:有一个YAML文件列出了这些服务,将它们的名称映射到它们的地址;作为其部署的一部分,这些服务的每个使用者都需要一份该YAML文件的副本。

    一方面,这是非常强大的,因为它不涉及像ZooKeeper那样维护外部键值存储(记住,etcd或Consul在那个时候不存在)。另一方面,这使得服务难以移动。每次移动服务时,它的所有消费者都需要接收更新的YAML文件(并且可能会重新启动)。不太方便!

    我们开始实现的解决方案是让每个消费者都连接到一个本地代理。使用者不需要知道服务的完整地址+端口,只需要知道它的端口号,并通过localhost进行连接。本地代理将处理该连接,并将其路由到实际后端。现在,当一个后端需要移动到另一台机器上,或按比例放大或缩小,而不是更新它的所有消费者,我们只需要更新所有这些本地代理;我们不再需要重新启动消费者。

    (还计划将流量封装在TLS连接中,并在接收端使用另一个代理来打开TLS并验证证书,而不涉及接收服务,该服务将被设置为仅在本地主机上接受连接。稍后会详细介绍。)

    这与AirBNB的SmartStack非常相似;与SmartStack实现并部署到生产环境的显著区别是,当dotCloud转向Docker时,它的新的内部路由网格被搁置了。

    我个人认为SmartStack是诸如Istio、Linkerd、Consul Connect等系统的先驱之一,因为所有这些系统都遵循这种模式:

    • 在每个节点上运行代理
    • 消费者连接到代理
    • 后端改变时,控制平面更新代理的配置
    # 今天实现一个服务网格如果我们今天必须实现类似的网格,我们可以使用类似的原则。例如,我们可以设置一个内部域名系统区域,将服务名映射到127.0.0.0/8空间中的地址。然后在集群的每个节点上运行HAProxy,接受每个服务地址(在127.0.0.0/8子网中)上的连接,并将它们转发/负载平衡到适当的后端。HAProxy配置可以由confd管理,允许在etcd或Consul中存储后端信息,并在需要时自动将更新的配置推送到HAProxy。这就是Istio的工作原理!但是有一些不同之处:
    • 它使用Envoy Proxy而不是HAProxy
    • 它使用Kubernetes API而不是etcd或Consul来存储后端配置
    • 服务在内部子网中分配地址(Kubernetes集群IP地址),而不是127.0.0.0/8
    • 它有一个额外的组件(Citadel),用于在客户机和服务器之间添加相互的TLS身份验证
    • 它增加了对诸如断路、分布式跟踪、金丝雀部署等新特性的支持

    让我们快速回顾一下这些差异。
    ## Envoy Proxy
    Envoy Proxy由Lyft撰写。它与其他代理(如HAProxy、NGINX、Traefik)有许多相似之处,但Lyft编写它是因为它们需要当时这些代理中不存在的功能,而且构建一个新的代理比扩展现有代理更有意义。

    Envoy可以单独使用。如果有一组给定的服务需要连接到其他服务,可以把它连接到Envoy,然后动态地配置和重新配置其他服务的Envoy的位置,而得到很多漂亮的额外的功能,比如域的可观测性。这里,没有使用定制的客户端库,也没有在代码中添加跟踪调用,而是将流量定向到Envoy,让它为我收集指标。

    但Envoy也可以用作服务网格的数据平面。这意味着现在将由该服务网格的控制平面配置Envoy。
    ## 控制平面
    说到控制平面,Istio依赖于Kubernetes API。这与使用confd没有太大的不同。confd依赖etcd或Consul来监视数据存储中的一组密钥。Istio依赖Kubernetes API来监视一组Kubernetes资源。

    Aparte:我个人认为阅读Kubernetes API描述非常有帮助。

    Kubernetes API服务器是一个“哑服务器”,它提供API资源上的存储、版本控制、验证、更新和监视语义。



    Istio是为与Kubernetes合作而设计的;如果你想在Kubernetes之外使用它,则需要运行Kubernetes API服务器的实例(以及支持的etcd服务)。
    ## 服务地址
    Istio依赖Kubernetes分配的集群IP地址,因此Istio得到一个内部地址(不在127.0.0.1/8范围)。

    在没有Istio的Kubernetes集群上,前往给定服务的ClusterIP地址的流量被kube-proxy拦截,并发送到该代理的后端。更具体地说,如果你想确定技术细节:kube-proxy设置iptables规则(或IPVS负载平衡器,取决于它是如何设置的)来重写连接到集群IP地址的目标IP地址。

    一旦Istio安装在Kubernetes集群上,就不会发生任何变化,直到通过将sidecar容器注入到使用者Pod中,显式地为给定的使用者甚至整个名称空间启用Istio。sidecar将运行一个Envoy实例,并设置一些iptables规则来拦截到其他服务的流量,并将这些流量重定向到Envoy。

    结合Kubernetes DNS集成,这意味着我们的代码可以连接到一个服务名,一切都可以正常工作。换句话说,比如我们的代码向`http://api/v1/users/4242`发起一个请求,`api`将解析到10.97.105.48,一条iptables规则将解释连接到10.97.105.48并重定向到本地Envoy代理,本地代理将这个请求路由到实际的API后端。
    ## 额外的铃声和哨声
    Istio还可以通过名为Citadel的组件通过mTLS(双向TLS)提供端到端加密和身份验证。

    它还包括混合器,Envoy组件可以查询每一个请求,对请求进行一个临时的决定取决于各种因素,例如请求头、后端负载(别担心,有丰富的规定以确保混合高度可用,即使它休息,Envoy可以继续代理流量)。

    当然,我提到了可观察性。Envoy在提供分布式跟踪的同时收集大量的度量指标。微服务架构,如果单个API请求必须经过微服务A、B、C和D,分布式跟踪将添加一个惟一的标识符请求进入系统,并保留标识符在子请求中,所有这些微服务允许收集所有相关的调用、延迟等。
    # 自建还是购买
    Istio以复杂著称。相比之下,使用我们今天拥有的工具,构建像我在本文开头描述的那样的路由网格相对比较简单。那么,构建我们自己的服务网格是否有意义呢?

    如果我们有适度的需求(如果我们不需要可观察性,断路器,和其他细节),我们可能想建立自己的。但是如果我们正在使用Kubernetes,我们甚至可能不需要这样做,因为Kubernetes已经提供了基本的服务发现和负载平衡。

    现在,如果我们有高级的需求,购买服务网格可能是一个更好的选择。(由于Istio是开源的,所以它并不总是真正的购买,但是我们仍然需要投入工程时间来理解它是如何工作、部署和运行的。)
    #如何选择Istio、Linkerd和Consul Connect
    到目前为止,我们只讨论了Istio,但它并不是唯一的服务网格。Linkerd是另一个流行的选择,还有Consul Connect

    我们应该选哪一个呢?

    实际上在这一点上我也不好说,我不认为我有足够的了解能够帮助任何人做决策。不过,已经有一些有趣的文章比较它们(12),甚至基准测试

    一种值得一提并且很有潜力的方法是使用像SuperGloo这样的工具。SuperGloo提供了一个抽象层来简化和统一服务网格公开的API。我们可以使用SuperGloo提供的更简单的构造,并无缝地从一个服务网格切换到另一个服务网格,而不是学习各种服务网格的特定API(在我看来,相对复杂)。有点像我们有一个描述HTTP前端和后端的中间配置格式,能够为NGINX、HAProxy、Traefik、Apache生成实际配置

    我已经使用SuperGloo稍微涉足Istio,在未来的博客文章中,我想说明如何使用SuperGloo将Isio或Linkerd添加到现有的集群中,以及后者是否能实现它的承诺,即允许我在不重写配置的情况下从一个路由网格切换到另一个。

    如果你喜欢这篇文章,并且想让我尝试一些具体的场景,我很乐意听到你的消息!

    原文链接:Containers, microservices, and service meshes

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

    【腾讯云】#容器团队#高级容器研发#招聘

    Shirleyee 发表了文章 • 1 个评论 • 209 次浏览 • 2019-05-24 10:33 • 来自相关话题

    高级容器研发工程师 工作职责 负责公有云/私有云中 Kubernetes/Devops 等产品技术方案设计与研发工作;负责 Kubernetes 相关前沿技术规划和调研工作,从技术上保证产品的竞争力;负责与产品及客户沟通,判定需求的合理 ...查看全部
    高级容器研发工程师
    工作职责
    1. 负责公有云/私有云中 Kubernetes/Devops 等产品技术方案设计与研发工作;
    2. 负责 Kubernetes 相关前沿技术规划和调研工作,从技术上保证产品的竞争力;
    3. 负责与产品及客户沟通,判定需求的合理性,找出最合适的方式解决客户问题。
    工作要求
    1. 3 年以上后端开发经验,Coding、Debug 能力强, 有丰富的架构设计经验;
    2. 熟悉 C/C++/Go/Java/Python/Ruby 等至少二种编程语言;
    3. 熟悉 Docker/Kubernetes/Swarm/Mesos 等技术;
    4. 熟悉 Jenkins/ELK/Prometheus 等技术优先;
    5. 熟悉 AWS/Google Cloud 等云计算厂商产品优先。


    有意请戳:
    Wechat:13723737494
    Email:Shirleyeee@foxmail.com

    Rainbond 5.1.4发布,复杂微服务架构整体升级和回滚

    goodrain 发表了文章 • 0 个评论 • 188 次浏览 • 2019-05-21 09:34 • 来自相关话题

    Rainbond 5.1.4发布, 复杂微服务架构整体升级和回滚 今天为大家带来Rainbond 5.1系列第四个更新版本,本次版本更新的主要内容是复杂微服务架构应用整体升级和回滚,能实现复杂微服务架构的持续交付,和复杂架构企业级应用 ...查看全部
    Rainbond 5.1.4发布, 复杂微服务架构整体升级和回滚

    今天为大家带来Rainbond 5.1系列第四个更新版本,本次版本更新的主要内容是复杂微服务架构应用整体升级和回滚,能实现复杂微服务架构的持续交付,和复杂架构企业级应用快速交付和升级,另外还有一些小的优化和BUG的修复。如果你想和更多微服务技术专家交流,可以加我微信liyingjiese,备注『加群』。群里每周都有全球各大公司的最佳实践以及行业最新动态

    Rainbond是开源的企业应用云操作系统,支撑企业应用的开发、架构、交付和运维的全流程,通过无侵入架构,无缝衔接各类企业应用,底层资源可以对接和管理IaaS、虚拟机和物理服务器。

    复杂微服务架构应用整体升级和回滚

    面对复杂的微服务架构,微服务组件可能几十个,服务之间存在业务依赖;微服务的版本管理复杂;开发测试流程低效,针对以上问题,单个微服务管理的模式已经不适用,需要考虑微服务架构整体管理。这次的更新能实现复杂微服务架构的整体版本,微服务独立开发,测试环境和生产环境整体升级和回滚,升级的过程只更新变化的服务和配置,过程滚动更新,实现业务不间断升级。



    升级和回滚的过程通过Rainbond应用市场实现,Rainbond应用市场定义了一种对应用的存储、共享、交付、管理途径.



    Rainbond应用市场与传统意义上的镜像仓库不同之处在于,它基于镜像仓库、包仓库和对象存储等存储系统支持,定义了支持大型、分布式数字化业务系统的标准云原生应用模型,并针对应用模型提供创建、发布、存储、交付、安装、升级等一系列业务支持,对内可作为以便捷灵活的方式共享企业创造的业务系统、中间件的业务性管理平台,对外可作为根据行业特性构建行业话交付标准、交付流程和交付路径的基础,应用市场的最大优点在于它涵盖的不仅是服务组件和应用(业务系统),甚至于解决方案都可以支持一键分享、一键安装使用,极大的便利用户,只需安装使用,使用者不需要懂技术。

    在5.1.4之前, rainbond仅仅支持对云市应用中单个服务的升级, 如果想要升级整个云市应用, 则需要单独地对每个服务进行升级, 且无法升级新添加的服务. 这给各位用户的使用带来了极大的不便. 为了让用户有的操作更加的简单, 提高使用体验, 我们在5.1.4版本中, 对应用市场进行了改造升级.

    功能特性

    • 灵活的升级方式: 可以自由地选择需要升级的服务, 可以全部升级也可以部份升级.
    • 创建新添加服务: 除了可以升级已有的服务外, 还可以创建旧版本没有, 但是新版本有的服务.
    • 详细的变更信息: 在升级界面中, 可以查看当前版本与新版本服务之间属性的变更.
    • 详细的升级记录: 对每次升级操作, rainbond都进行了详细的记录, 包括: 升级操作的时间, 版本号的变更和各服务属性的变更信息等.
    • 自动回滚: 在应用升级的过程中, 如果程序发生了异常, 会回滚到升级前的状态, 避免只升级部分属性或服务.
    • 手动回滚: 升级成功后, 如果新版本有缺陷导致各个服务无法正常工作, 或者你更倾向升级前的版本, 那么可以选择手动回滚, 回到之前的版本.
    简单的演示更详细的说明, 请参考: 服务升级文档其他改进
    • 第三方服务新添加实例地址时, 允许地址中带有端口
    • 镜像服务支持修改镜像仓库帐号, 密码等信息
    • grctl命令行工具增加身份属性gateway
    • 将自定义网关策略的域名以环境变量的方式注入到服务中(相关文档)
    • 将环境变量,配置文件等配置信息综合为环境配置
    • 分享应用时支持定义不分享的服务
    • 支持服务链接信息和环境变量的相互转移
    • 关闭或重启服务时, 增加二次确认, 防止误操作
    安装方面:
    • 优化安装时初始化数据中心流程
    • 优化调整安装任务结构,调整离线镜像文件路径
    • 支持调整网络类型
    • 优化部分组件配置参数
    • 优化安装过程中宿主机IP段与容器ip段冲突问题
    BUG修复
    • 【重要】修复了关闭服务时, pod无法被删除或删除需要花费比较多时间的问题
    • 【重要】修复了多管理节点中, 某个节点rbd-hub服务异常了,但gateway没有将其下线导致goodrain.me服务异常的问题
    • 修复了第三方服务的网关访问策略控制错误
    • 修复了删除端口报系统异常的错误
    • 修复了编辑HTTPs网关策略, 无法勾选 HTTP rewriet HTTPs 的问题
    • 修复了更改构建源后无法重新检测语言的错误
    • 修复了无法修改健康检测参数的错误
    • 修复了云市应用版本号显示不全的问题
    • 修复了添加镜像服务时, 没有高级选项按钮的问题
    • 修复了构建源中镜像Tag显示不全的问题
    • 修复了创建应用时勾选的是有状态应用,创建成功后却是无状态应用的问题
    • 修复了无法将无状态应用修改为有状态应用的问题
    • 修复了禁止调度计算节点后, 导致可用资源统计错误的问题
    • 修复了第三方服务TCP访问策略状态错误且无法操作的问题
    • 修复了网关策略参数配置中Websocket不生效的问题
    • 修复了云市应用导出的docker-compose.yaml中的镜像有误的问题
    • 修复了环境变量名格式验证有误的问题, 支持带"."的环境变量名



    安装和升级

    新集群安装参考Rainbond安装文档:https://www.rainbond.com/docs/quick-start/rainbond_install/

    升级已有集群到5.1.4版本: https://www.rainbond.com/docs/user-operations/upgrade/5.1.3-5.1.4/


    Rainbond v5.1.2发布,微服务架构应用便捷管理和交付

    goodrain 发表了文章 • 0 个评论 • 497 次浏览 • 2019-04-02 08:48 • 来自相关话题

    Rainbond v5.1.2发布,微服务架构应用便捷管理和交付 Rainbond是开源的企业应用云操作系统,支撑企业应用的开发、架构、交付和运维的全流程,通过无侵入架构,无缝衔接各类企业应用,底层资源可以对接和管理IaaS、虚拟机和 ...查看全部
    Rainbond v5.1.2发布,微服务架构应用便捷管理和交付

    Rainbond是开源的企业应用云操作系统,支撑企业应用的开发、架构、交付和运维的全流程,通过无侵入架构,无缝衔接各类企业应用,底层资源可以对接和管理IaaS、虚拟机和物理服务器。

    2019年3月,Rainbond发布v5.1版本,经过1个月在上百家企业的实际使用,团队持续跟进版本缺陷,迄今为止发布了2个BUG修复版本。

    Rainbond开源产品的目标是成为企业IT系统的云操作系统,作为基础平台支持各行各业的企业用户,优化IT软件开发企业的开发流程和交付流程,做到一站式开发和交付。作为广大行业IT厂商的合作伙伴,为其提供稳定的、好用的、高效的基础平台,服务于行业软件的架构、开发和交付,Rainbond在这条路上砥砺前行。在V5.1版本中我们引入了以下功能体系来服务用户。

    支持第三方微服务集成和管理

    Rainbond在众多的企业中落地使用的过程中出现了两类共同的问题:

    • 循序渐进的迁移策略,已经上Rainbond的服务如何与遗留服务通信和统一管理。
    • Rainbond应用网关很好用,但是遗留的服务没办法与Rainbond上的服务共享外网端口或域名。
    Rainbond V5.1版本中在提出了第三方服务的概念,即将运行于Rainbond集群外且与Rainbond可以正常网络通信的服务称为第三方服务。对于此类服务,我们支持以静态注册、动态注册(Etcd、Zookeeper、Consule)的方式来获取第三方服务的通信地址,赋予第三方服务以下能力:[list=1]
  • 集成Rainbond内置的ServiceMesh架构,与集群内服务无缝互联,并提供服务通信治理功能。
  • 集成Rainbond 应用网关,统一管理服务外网访问。
  • 运行于不同环境和系统的业务系统统一管理和可视化,形成完整业务架构。
  • 更多第三方服务的说明和支持情况,见文档: Rainbond支持第三方服务集成此功能发布之后,在阿里云运行Rainbond的企业用户可以更便捷的对接阿里云的RDS资源。更加充分的利用云资源以降低企业维护IT系统的成本。支持微服务启动顺序在一个复杂微服务架构下,一些服务必须依赖于另一些服务才能正常工作,如何根据依赖关系处理服务的启动顺序是简化复杂微服务架构管理的关键。Rainbond实现了根据依赖关系自动处理服务的启动顺序,当被依赖的服务正常工作后,才会启动后续服务,依次迭代启动所有服务。这方面的功能实现主要在体现Rainbond的主要抽象层次,我们比较清楚的是docker的抽象层次是容器级别,kubernetes的抽象层次主要可以认为是服务级别(Pod级别),Rainbond的关键抽象层是更高的应用级,特别是微服务架构盛行的今天,服务组件多,对于大多数业务程序都需要手动的控制启动顺序来确保整个业务的正常工作。Rainbond能够做到能够做到在应用级整体控制生命周期和其他自动化运维。此功能发布后在某工业互联网软件企业用户中创造了较大价值,一个完整的工业互联网APP开发平台由20多个服务组件构成,过去他们每交付一个工厂的交付成本需要一个熟练的交付工程师出差调试大概2天才能基本完成。其中主要的就是需要熟练掌握服务之间的依赖关系,启动顺序,服务配置,这还是建立在他们产品的成熟度已经比较高。后期这一套业务系统交付用户的运维成本也非常大。当使用Rainbond作为基础交付平台以后,他们通过1天的时间将所有服务完整部署的Rainbond并发布于应用市场。由于Rainbond完整的应用系统生命周期控制和启动顺序控制,实现了完整的工业互联网APP开发平台的一键部署,10分钟完成业务可工作。对于最终用户来说也可以更加直观的运维管理业务系统。源码构建系统升级基于源代码持续构建服务是Rainbond用户使用最多的功能之一,既5.0版本作较大升级以后,5.1版本继续带来升级,在Java、PHP、NodeJS等常用语言方面支持更加完善:
    • 增加对NodeJS前端项目源码类型的支持,可以部署Vue和React。
    • Java-Maven增加maven编译参数的UI配置。
    • 所有Java类型支持OpenJDK版本和OracleJDK版本的UI配置。
    • PHP、静态语言支持UI选择中间件类型和版本。
    • 将公共代码模块和资源从云端本地化、更好的支持离线环境下源码构建
    • 支持服务源码类型重新检测和变更

    另外Rainbond对各类型源码的支持规范文档进行了更加细致的描述,请参考 Rainbond源码支持规范

    从源码构建主要服务于开发场景,目前还是有较多的企业开发者出于学习成本无法定义优质的Dockerfile,直接使用Rainbond提供的基于源代码构建的机制是开发者使用Rainbond发布服务最易用的方式。

    我们从用户使用中总结发现目前开发语言最多的依然是Java,因此Rainbond对Java语言支持的持续优化依然是V5.1版本的重点,其中有大量用户使用的是SpringCloud,因此Rainbond将在V5.1后续小版本中增加直接基于Maven源码创建多个服务模块的便捷服务创建方式,进一步提供用户创建服务的效率。

    除了上述提到的Rainbond V5.1版本大的功能变化以外,Rainbond还进行了大量的功能改进和优化。详细参考:

    https://github.com/goodrain/rainbond/releases/tag/v5.1.0-release

    https://github.com/goodrain/rainbond/releases/tag/v5.1.1-release

    https://github.com/goodrain/rainbond/releases/tag/v5.1.2-release

    开始你的Rainbond之旅

    你的企业是否也遇到过上文提到的种种影响你的产品开发和交付的效率的问题,不妨使用Rainbond来优化一下你的现有模式和体验。

    Rainbond 安装参考手册 https://www.rainbond.com/docs/quick-start/rainbond_install/

    Rainbond 使用参考手册 https://www.rainbond.com/docs/user-manual/

    Rainbond 进阶场景手册 https://www.rainbond.com/docs/advanced-scenarios/

    Docker--容器技术

    CCE_SWR 发表了文章 • 0 个评论 • 591 次浏览 • 2019-03-13 15:23 • 来自相关话题

    什么是“容器”和“虚拟机” 容器和虚拟机它们的目的很相似:即将应用程序和它的依赖放到一个可以在任何环境运行的自足单元中。 此外,容器和虚拟机消除了对物理硬件的需求,从而在能源消耗和成本效益方面能让我们更有效地使用计算资源 ...查看全部
    什么是“容器”和“虚拟机”
    容器和虚拟机它们的目的很相似:即将应用程序和它的依赖放到一个可以在任何环境运行的自足单元中。

    此外,容器和虚拟机消除了对物理硬件的需求,从而在能源消耗和成本效益方面能让我们更有效地使用计算资源,

    容器和虚拟机的主要区别在于它们的架构方式。让我们继续深入了解。

    虚拟机
    虚拟机在本质上是对现实中计算机的仿真,它会像真实的计算机一样执行程序。使用 “hypervisor” 可以将虚拟机运行于物理机上。hypervisor 可以在主机运行,也可以在“裸机”上运行。

    让我们来揭开这些术语的面纱:

    hypervisor(之后都以虚拟机管理程序称呼)是能让虚拟机在其上运行的软件,固件或者硬件。虚拟机管理程序本身会在物理计算机上运行,称为“主机”。主机为虚拟机提供资源,包括 RAM 和 CPU。这些资源在虚拟机之间被划分并且可以根据需要进行分配。所以如果一个虚拟机上运行了资源占用更大的应用程序,相较于其它运行在同一个主机的虚拟机你可以给其分配更多的资源。

    运行在主机上的虚拟机(再次说明,通过使用虚拟机管理程序)通常也被叫做“访客机”。访客机包含了应用以及运行这个应用所需要的全部依赖(比如:系统二进制文件和库)。它还带有一个自己的完整虚拟化硬件栈,包括虚拟化的网络适配器,储存和 CPU-这意味着它还拥有自己成熟的整个访客操作系统。从虚拟机内部来看,访客机的操作都认为其使用的都是自己的专用资源。从外部来看,我们知道它是一个虚拟机-和其它虚拟机一起共享主机提供的资源。

    就像前面所提到的,访客机既可以运行在托管的虚拟机管理程序上,也可以运行在裸机虚拟机管理程序上。它们之间存在一些重要的差别。

    首先,托管的虚拟化管理程序是在主机的操作系统上运行。比如说,可以在一台运行 OSX 操作系统的计算机的系统上安装虚拟机(例如:VirtualBox 或者 VMware Workstation 8)。虚拟机无法直接访问硬件,因此必须通过主机上运行的操作系统访问(在我们的例子中,也就是 Mac 的 OSX 操作系统)。

    托管虚拟机管理程序的好处是底层硬件并不那么重要。主机的操作系统会负责硬件的驱动而不需要管理程序参与。因此这种方式被认为具备更好的“硬件兼容性”。在另一方面,在硬件和管理程序之间这个额外的附加层会产生更多的资源开销,这会降低虚拟机的性能。

    裸机虚拟机管理程序通过直接在主机硬件上安装和运行来解决这个性能问题。因为它直接面对底层的硬件,所以并不需要运行在主机的操作系统之上。在这种情况下,安装在主机上第一个作为操作系统运行的就是这个裸机虚拟机管理程序。与托管虚拟机管理程序不同,它有自己的设备驱动直接与每个组件交互,以执行任何 I/O,处理或特定于操作系统的任务。这样可以获得更好的性能,可伸缩性和稳定性。这里的权衡在于其对硬件的兼容性有限,因为裸机虚拟机管理程序内置的设备驱动只有那么多。

    在讨论了虚拟机管理程序之后,你可能想知道为什么我们需要在虚拟机和主机之间这个额外的“虚拟机管理程序”层。

    好吧,虚拟机管理程序在其中确实发挥了重要的作用,由于虚拟机拥有自己的虚拟操作系统,管理程序为虚拟机管理和执行访客操作系统提供了一个平台。它允许主机与作为客户端运行的虚拟机之间共享其资源。


    1.png



    虚拟机图示

    正如你可以在图示中所看到的,VMS 会为每个新的虚拟机打包虚拟硬件,一个内核(即操作系统)和用户空间。

    容器
    与提供硬件虚拟化的虚拟机不同,容器通过抽象“用户空间”来提供操作系统级别的虚拟化。当我们详解容器这个术语的时候你就会明白我的意思。

    从所有的意图和目的来看,容器看起来就像一个虚拟机。比如说,它们有执行进程的私有空间,可以使用 root 权限执行命令,具有专有的网络接口和 IP 地址,允许自定义路由和 iptable 规则,可以挂载文件系统等。

    容器和虚拟机之间的一个重要区别在于容器和其它容器共享主机系统的内核。


    2.png



    容器图示

    这图表明容器只会打包用户空间,而不是像虚拟机那样打包内核或虚拟硬件。每个容器都有自己独立的用户空间从而可以让多个容器在单个主机上运行。我们可以看到所有操作系统级别的体系架构是所有容器共享的。要从头开始创建的部分只有 bins 和 libs 目录。这就是容器如此轻巧的原因。

    Docker 是从哪来的?
    Docker 是基于 Linux 容器技术的开源项目。它使用 Luinux 的内核功能(如命名空间和控制组)在操作系统上创建容器。

    容器已经远远不是一个新技术:Google 已经使用他们自己的容器技术好多年了。其它的容器技术包括 Solaris Zones、BSD jails 和 LXC 也已经存在好多年。

    那么为啥 Docker 会突然取得成功呢?

    使用简单:Docker 使得任何人(开发人员,运维,架构师和其他人)都可以更轻松的利用容器的优势来快速构建和测试可移植的应用程序。它可以让任何人在他们的笔记本电脑上打包应用程序,不需要任何修改就可以让应用运行在公有云,私有云甚至裸机上。Docker 的口头禅是:“一次构建,处处运行”。

    速度:Docker 容器非常轻量级和快速。因为容器只是运行在内核上的沙盒环境,因此它们占用的资源更少。与可能需要更多时间来创建的虚拟机相比,你可以在几秒钟内创建一个 Docker 容器,因为虚拟机每次都必须启动一个完整的操作系统。

    Docker Hub:Docker 用户也可以从日益丰富的 Docker Hub 生态中受益,你可以把 Docker Hub 看作是 “Docker 镜像的应用商店”。Docker Hub 拥有数万个由社区构建的公共镜像,这些镜像都是随时可用的。在其中搜索符合你需求的镜像非常容易,你只需要准备拉取镜像而且几乎不需要任何修改。

    模块化和可扩展性:Docker 可以让你轻松地把应用程序按功能拆分为单个独立的容器。比如说,你的 Postgre 数据库可以运行在一个容器中,Redis 服务运行在另一个容器中,而 Node.js 应用运行在另一个容器中。使用 Docker,将这个容器链接在一起以创建你的应用程序将会变得更简单,同时在将来可以很轻松地扩展和更新单独的组件。最后但并不重要的是,有谁不喜欢 Docker 的鲸鱼(Docker 的标志)呢?:)


    3.png

    Kubernetes taint & toleration

    jonathan 发表了文章 • 0 个评论 • 538 次浏览 • 2019-03-09 22:33 • 来自相关话题

    一、概述 前一篇文章讲解了 Kubernetes 亲和性调度, 所涉及的内容都是描述 pod 的属性,来声明此 pod 希望调度到哪类 nodes。而本文介绍的 `Taint(污点)` 刚好相反,它是node 的一个属性,允许 node 主动排斥 ...查看全部
    一、概述
    前一篇文章讲解了 Kubernetes 亲和性调度, 所涉及的内容都是描述 pod 的属性,来声明此 pod 希望调度到哪类 nodes。而本文介绍的 `Taint(污点)` 刚好相反,它是node 的一个属性,允许 node 主动排斥 pod 的调度。
    对应的 k8s 又给 pod 新增了配套属性 `toleration(容忍)` ,用于表示这些 pod 可以(但不强制要求)被调度到具有相应 taints 的 nodes 上。
    这两者经常一起搭配,来确保不将 pod 调度到不合适的 nodes。

    看下 taint & toleration 结构体,下面足点介绍时会涉及相关字段。

    type Taint struct {
    Key string
    Value string
    Effect TaintEffect
    // add taint 的时间点
    // 只有 Effect = NoExecute, 该值才会被 nodeController 写入
    TimeAdded *metav1.Time
    }

    type Toleration struct {
    Key string
    Operator TolerationOperator
    Value string
    Effect TaintEffect
    // 容忍时间
    TolerationSeconds *int64
    }

    type TaintEffect string

    const (
    TaintEffectNoSchedule TaintEffect = "NoSchedule"
    TaintEffectPreferNoSchedule TaintEffect = "PreferNoSchedule"
    TaintEffectNoExecute TaintEffect = "NoExecute"
    )

    type TolerationOperator string

    const (
    TolerationOpExists TolerationOperator = "Exists"
    TolerationOpEqual TolerationOperator = "Equal"
    )


    二、Taint
    我们可以对 node 设置多个 taints,当然也可以在 pod 配置相同个数的 tolerations。影响调度和运行的具体行为,我们可以分为以下几类:

    • 如果至少有一个 `effect == NoSchedule` 的 taint 没有被 pod toleration,那么 pod 不会被调度到该节点上。
    • 如果所有 `effect == NoSchedule` 的 taints 都被 pod toleration,但是至少有一个 `effect == PreferNoSchedule` 没有被 pod toleration,那么 k8s 将努力尝试不把 pod 调度到该节点上。
    • 如果至少有一个 `effect == NoExecute` 的 taint 没有被 pod toleration,那么不仅这个 pod 不会被调度到该节点,甚至这个节点上已经运行但是也没有设置容忍该污点的 pods,都将被驱逐。
    三、Toleration再看下 PodSpec 配置 Tolerations,其中的 key、value、effect 与 Node Taint 设置需要保持一致,operator 支持两类:
    • Exists: 这个配置下,不需要指定 value。
    • Equal: 需要配置 value 值。(operator 的默认值)
    有几个特殊情况:
    • key 为空并且 operator 等于 Exists,表示匹配了所有的 keys,values 和 effects。换句话说就是容忍了所有的 taints。
    ```tolerations:
    • operator: "Exists"
    ```
    • effect 为空,则表示匹配所有的 effects(NoSchedule、PreferNoSchedule、NoExecute)
    ```tolerations:
    • key: "key"
    operator: "Exists"```还有一个 `TolerationSeconds`,该值与 effect 为 `NoExecute` 配套使用。用来指定在 node 添加了 effect = NoExecute 的 taint 后,能容忍该 taint 的 pods 可停留在 node 上的时间。例如:```tolerations:
    • key: "key1"
    operator: "Equal" value: "value1" effect: "NoExecute" tolerationSeconds: 3600```表示如果这个 pod 已经运行在 node 上并且该 node 添加了一个对应的 taint,那么这个 pod 将会在 node 上停留 3600 秒后才会被驱逐。但是如果 taint 在这个时间前被移除,那么这个 pod 也就不会被驱逐了。来个比较形象的图,描述下:

    该图参考: Taints and tolerations, pod and node affinities demystified · Banzai Cloud

    四、内置行为kubernetes 1.6 版本,node controller 会跟进系统情况自动设置 node taint 属性。内置 taint key 如下:
    • node.kubernetes.io/not-ready: 节点尚未就绪
    • node.kubernetes.io/unreachable: 节点无法被访问
    • node.kubernetes.io/unschedulable: 节点不可调度
    • node.kubernetes.io/out-of-disk: 节点磁盘不足
    • node.kubernetes.io/memory-pressure: 节点有内存压力
    • node.kubernetes.io/disk-pressure: 节点有磁盘压力
    • node.kubernetes.io/network-unavailable: 节点网络不可用
    • node.kubernetes.io/pid-pressure: 节点有 pid 压力
    • node.cloudprovider.kubernetes.io/uninitialized: 云节点未初始化
    • node.cloudprovider.kubernetes.io/shutdown: 云节点已下线
    kubernetes 会通过 `DefaultTolerationSeconds admission controller` 为创建的 pod 添加两个默认的 toleration: `node.kubernetes.io/not-ready` 和 `node.kubernetes.io/unreachable`,并且设置 `tolerationSeconds = 300`。当然这个默认配置,用户可以自行覆盖。这些自动添加的默认配置,确保 node 出现问题后,pod 可以继续在 node 上停留 5 分钟。

    DefaultTolerationSeconds admission controller 设置的 tolerationSeconds 值,也可以由用户指定。> 具体参考: https://github.com/kubernetes/kubernetes/blob/master/plugin/pkg/admission/defaulttolerationseconds/admission.go

    还有一个默认行为,就是 DaemonSet。所有 DaemonSet 创建的 pod 都会添加两个 toleration: `node.alpha.kubernetes.io/unreachable` 和 `node.alpha.kubernetes.io/notReady`。设置 `effect = NoExecute`,并且不指定 `tolerationSeconds`。目的是确保在 node 出现 unreachable 或 notReady 的问题时,DaemonSet Pods 永远不会被驱逐。# 示例常见的示例如下:
    • `专用节点`
    - 如果你希望将一组节点专用于特定的用户,那可以将这些节点设置 taints: `kubectl taint nodes nodename dedicated=groupName:NoSchedule`, 然后为 pods 设置对应的 tolerations。 - 如果你希望节点被专用并且确保服务仅使用这批节点,那么你还应该向这批节点设置 labels (`dedicated=groupName`),并且为对应的 pods 设置 NodeAffinity,以控制 pods 只能跑到这批节点上。
    • `具有特殊硬件的节点`
    - 有部分带有特殊硬件的节点,比如 GPU、FPGA 等,要确保不将不需要专用硬件的 pods 调度到这些节点。也可以和 `专有节点` 一样的方式设置 taints:`kubectl taint nodes nodename special=true:NoSchedule` 或 `kubectl taint nodes nodename special=true:PreferNoSchedule`。建议还可以通过 Extended ResourcesExtendedResourceToleration admission controller 来更方便的调度依赖特殊硬件的 pods,而不需要手动添加容器的 toleration
    • `基于 taint 的驱逐`
    - 前面也提到了,可以通过 `NoExecute taint` 来驱逐节点上已经运行的 pods。 五、参考资料

    Kubernetes 亲和性调度

    jonathan 发表了文章 • 0 个评论 • 761 次浏览 • 2019-03-09 22:29 • 来自相关话题

    一、概述 前一篇文章 Kubernetes 调度器浅析,大致讲述了调度器的工作原理及相关调度策略。这一章会继续深入调度器,介绍下“亲和性调度”。 Kubernetes 支持限制 Pod 在指定的 Node 上运行,或者指定 ...查看全部
    一、概述
    前一篇文章 Kubernetes 调度器浅析,大致讲述了调度器的工作原理及相关调度策略。这一章会继续深入调度器,介绍下“亲和性调度”。

    Kubernetes 支持限制 Pod 在指定的 Node 上运行,或者指定更倾向于在某些特定 Node 上运行。
    有几种方式可以实现这个功能:

    • `NodeName`: 最简单的节点选择方式,直接指定节点,跳过调度器。
    • `NodeSelector`: 早期的简单控制方式,直接通过键—值对将 Pod 调度到具有特定 label 的 Node 上。
    • `NodeAffinity`: NodeSelector 的升级版,支持更丰富的配置规则,使用更灵活。(NodeSelector 将被淘汰.)
    • `PodAffinity`: 根据已在节点上运行的 Pod 标签来约束 Pod 可以调度到哪些节点,而不是根据 node label。
    二、NodeNamenodeName 是 PodSpec 的一个字段,用于直接指定调度节点,并运行该 pod。调度器在工作时,实际选择的是 nodeName 为空的 pod 并进行调度然后再回填该 nodeName,所以直接指定 nodeName 实际是直接跳过了调度器。换句话说,指定 nodeName 的方式是优于其他节点选择方法。方法很简单,直接来个官方示例:```apiVersion: v1kind: Podmetadata: name: nginxspec: containers: - name: nginx image: nginx nodeName: kube-01```当然如果选择的节点不存在,或者资源不足,那该 pod 必然就会运行失败。 三、NodeSelectornodeSelector 也是 PodSpec 中的一个字段,指定键—值对的映射。如果想要将 pod 运行到对应的 node 上,需要先给这些 node 打上 label,然后在 podSpec.NodeSelector 指定对应 node labels 即可。步骤如下:
    • 设置标签到 node 上:
    `kubectl label nodes kubernetes-node type=gpu`
    • pod 配置添加 nodeSelector 字段:
    ```apiVersion: v1kind: Podmetadata: name: nginxspec: containers: - name: nginx image: nginx nodeSelector: type: gpu```# 内置 Node 标签[list=1]
  • Kubernetes 内置了一些节点标签:
    • kubernetes.io/hostname
    • beta.kubernetes.io/instance-type
    • beta.kubernetes.io/os
    • beta.kubernetes.io/arch
    • failure-domain.beta.kubernetes.io/zone
    • failure-domain.beta.kubernetes.io/region

    有些标签是对云提供商使用。

    [list=1]
  • 还有些表示 node role 的 labels(可以指定 master、lb 等):
    • kubernetes.io/role
    • node-role.kubernetes.io
    四、NodeAffinitynodeSelector 通过 k-v 的方式非常简单的支持了 pod 调度限制到具有特定标签的节点上。而 nodeAffinity 根据亲和力 & 反亲和力极大地扩展了能够表达的约束信息。

    nodeAffinity 特性的设计初衷就是为了替代 nodeSelector。

    nodeAffinity 当前支持的匹配符号包括:In、NotIn、Exists、DoesNotExists、Gt、Lt 。nodeAffinity 当前支持两种调度模式:
    • `requiredDuringSchedulingIgnoredDuringExecution`: 一定要满足的条件,如果没有找到满足条件的节点,则 Pod 创建失败。所有也称为`hard 模式`。
    • `preferredDuringSchedulingIgnoredDuringExecution`: 优先选择满足条件的节点,如果没有找到满足条件的节点,则在其他节点中择优创建 Pod。所有也称为 `soft 模式`。
    两种模式的名字特长,这是 k8s 的命名风格。其中`IgnoredDuringExecution`的意义就跟 `nodeSelector` 的实现一样,即使 node label 发生变更,也不会影响之前已经部署且又不满足 affinity rules 的 pods,这些 pods 还会继续在该 node 上运行。换句话说,亲和性选择节点仅在调度 Pod 时起作用。

    k8s 社区正在计划提供 `requiredDuringSchedulingRequiredDuringExecution ` 模式,便于驱逐 node 上不满足 affinity rules 的 pods。

    来个官方示例,看下怎么玩:```apiVersion: v1kind: Podmetadata: name: with-node-affinityspec: affinity: nodeAffinity: # 必须选择 node label key 为 kubernetes.io/e2e-az-name, # value 为 e2e-az1 或 e2e-az2. requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/e2e-az-name operator: In values: - e2e-az1 - e2e-az2 # 过滤掉上面的必选项后,再优先选择 node label key 为 another-node-label-key # value 为 another-node-label-value. preferredDuringSchedulingIgnoredDuringExecution: # 如果满足节点亲和,积分加权重(优选算法,会对 nodes 打分) # weight: 0 - 100 - weight: 1 preference: matchExpressions: - key: another-node-label-key operator: In values: - another-node-label-value containers: - name: with-node-affinity image: k8s.gcr.io/pause:2.0```简单看下 NodeAffinity 的结构体,下面介绍注意事项时会涉及:
    type NodeAffinity struct {	RequiredDuringSchedulingIgnoredDuringExecution *NodeSelector	PreferredDuringSchedulingIgnoredDuringExecution []PreferredSchedulingTerm}type NodeSelector struct {	NodeSelectorTerms []NodeSelectorTerm}type NodeSelectorTerm struct {	MatchExpressions []NodeSelectorRequirement	MatchFields []NodeSelectorRequirement}
    配置相关的注意点:
    • 如果 `nodeSelector` 和 `nodeAffinity` 两者都指定,那 node 需要两个条件都满足,pod 才能调度。
    • 如果指定了多个 `NodeSelectorTerms`,那 node 只要满足其中一个条件,pod 就可以进行调度。
    • 如果指定了多个 `MatchExpressions`,那必须要满足所有条件,才能将 pod 调度到该 node。
    五、PodAffinitynodeSelector & nodeAffinity 都是基于 node label 进行调度。而有时候我们希望调度的时候能考虑 pod 之间的关系,而不只是 pod 和 node 的关系。举个例子,会有需求希望服务 A 和 B 部署在同一个机房、机架或机器上,因为这些服务可能会对网路延迟比较敏感,需要低延时;再比如,希望服务 C 和 D 又希望尽量分开部署,即使一台主机甚至一个机房出了问题,也不会导致两个服务一起挂而影响服务可用性,提升故障容灾的能力。podAffinity 会基于节点上已经运行的 pod label 来约束新 pod 的调度。其规则就是“如果 X 已经运行了一个或者多个符合规则 Y 的 Pod,那么这个 Pod 应该(如果是反亲和性,则是不应该)调度到 X 上”。这里的 Y 是关联 namespace 的 labelSelector,当然 namespace 也可以是 all。和 node 不同,pod 是隶属于 namespace 下的资源,所以基于 pod labelSelector 必须指定具体的 namespace;而 X 则可以理解为一个拓扑域,类似于 node、rack、zone、cloud region 等等,就是前面提到的 `内置 Node 标签` ,当然也可以自定义。看下 pod affinity 涉及的结构体,便于进行功能介绍:
    type Affinity struct {	// NodeAffinity 前面介绍了	NodeAffinity *NodeAffinity	// pod 亲和性	PodAffinity *PodAffinity	// pod 反亲和性	PodAntiAffinity *PodAntiAffinity}type PodAffinity struct {	// hard 模式, 必选项	RequiredDuringSchedulingIgnoredDuringExecution []PodAffinityTerm	// soft 模式, 进行 node 优先	PreferredDuringSchedulingIgnoredDuringExecution []WeightedPodAffinityTerm}type PodAffinityTerm struct {	LabelSelector *metav1.LabelSelector	Namespaces []string	TopologyKey string}type WeightedPodAffinityTerm struct {	Weight int32	PodAffinityTerm PodAffinityTerm}
    podAffinity 和 nodeAffinity 有相似的地方,使用了 labelSelector 进行匹配,支持的匹配符号包括:In、NotIn、Exists、DoesNotExists;也支持两种调度模式 `requiredDuringSchedulingIgnoredDuringExecution` 和 `preferredDuringSchedulingIgnoredDuringExecution`, 功能和 nodeAffinity 一样,这里就不在累述。podAffinity 和 nodeAffinity 也有较大的差异,前面讲了 pod 是 namespace 资源,所以必然会需要配置 namespaces,支持配置多个 namespace。如果省略的话,默认为待调度 pod 所属的 namespace;如果定义了但是值为空,则表示使用 “all” namespaces。还有一个较大的差别 `TopologyKey`, 便于理解进行单独介绍。# TopologyKeyTopologyKey 用于定义 `in the same place`,前面也介绍了是`拓扑域`的概念。看下面的图,这两个 pod 到底该如何算在一个拓扑域?如果我们使用`k8s.io/hostname`,`in the same place` 则意味着在同一个 node,那下图的 pods 就不在一个 place:如果我们使用`failure-domain.k8s.io/zone` 来表示一个 place,那下图的 pods 就表示在一个 zone:当然我们也可以自定义 node labels 作为 TopologyKey。比如我们可以给一组 node 打上 `rack` 标签,那下图的 pods 表示在同一个 place:原则上,topologyKey 可以是任何合法的 label key。但是出于性能和安全考虑,topologyKey 存在一些限制:
    • 对于亲和性和反亲和性的 `requiredDuringSchedulingIgnoredDuringExecution` 模式,topologyKey 不能为空
    • pod 反亲和性 `requiredDuringSchedulingIgnoredDuringExecution` 模式下,`LimitPodHardAntiAffinityTopology` 权限控制器会限制 topologyKey 只能设置为 `kubernetes.io/hostname`。当然如果你想要使用自定义 topology,那可以简单禁用即可。
    • pod 反亲和性 `preferredDuringSchedulingIgnoredDuringExecution` 模式下,topologyKey 为空则表示所有的拓扑域。截止 v1.12 版本,所有的拓扑域还只能是 `kubernetes.io/hostname`, `failure-domain.beta.kubernetes.io/zone` 和 `failure-domain.beta.kubernetes.io/region` 的组合。
    • 除此之外,topologyKey 可以是任何合法的 label key。
    # 示例来个官方示例,有三节点集群,需要分别部署 3 份 web 和 redis 服务。希望 web 与 redis 服务共存,但需要保证各个服务的副本分散部署。先创建 redis 集群:
    apiVersion: apps/v1kind: Deploymentmetadata:  name: redis-cachespec:  selector:    matchLabels:      app: store  replicas: 3  template:    metadata:      labels:        app: store    spec:      affinity:        // pod 反亲和性, 打散 redis 各个副本        podAntiAffinity:          requiredDuringSchedulingIgnoredDuringExecution:          - labelSelector:              matchExpressions:              - key: app                operator: In                values:                - store            topologyKey: "kubernetes.io/hostname"      containers:      - name: redis-server        image: redis:3.2-alpine
    再部署 web 服务,需要打散并且与 redis 服务共存,配置如下:
    apiVersion: apps/v1kind: Deploymentmetadata:  name: web-serverspec:  selector:    matchLabels:      app: web-store  replicas: 3  template:    metadata:      labels:        app: web-store    spec:      affinity:        podAntiAffinity:          requiredDuringSchedulingIgnoredDuringExecution:          - labelSelector:              matchExpressions:              - key: app                operator: In                values:                - web-store            topologyKey: "kubernetes.io/hostname"        podAffinity:          requiredDuringSchedulingIgnoredDuringExecution:          - labelSelector:              matchExpressions:              - key: app                operator: In                values:                - store            topologyKey: "kubernetes.io/hostname"      containers:      - name: web-app        image: nginx:1.12-alpine

    注意1: pod affinity 需要进行大量处理,所以会明显减慢大型集群的调度时间,不建议在大于几百个节点的集群中使用该功能。> 注意2: pod antiAffinity 要求对节点进行一致标志,即集群中的所有节点都必须具有适当的标签用于配置给 topologyKey,如果节点缺少指定的 topologyKey 指定的标签,则可能会导致意外行为。

    六、参考资料# 官方# blog