数据专栏

智能大数据搬运工,你想要的我们都有

科技资讯:

科技学院:

科技百科:

科技书籍:

网站大全:

软件大全:

【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
本文作者:HelloDeveloper
交通灯感知模块旨在使用相机提供准确和全面的交通灯状态信息。通常,交通灯有三种状态,即红、黄、绿。但是,如果交通灯出现故障,它可能会显示黑色或红灯/黄灯闪烁。如果摄像头的视野中无法找到交通灯,模块将无法识别其状态。

那么,交通灯感知模块是如何在Apollo 2.0中工作的呢?

以下,ENJOY

简介

为了解决交通灯出现故障的不确定情况,交通灯感知模块支持五种状态的输出:
红色 黄色 绿色 黑色 未知

模块 反复查询HD-Map(高精地图) 以确认车辆前方是否有交通灯。交通灯由其边界上的四个点表示,根据车辆的位置,通过查询HD-Map可以获得交通灯信息。如果车辆前方有交通灯,模块会将它从 世界坐标系 投射到 图像坐标系 。

Apollo 认为只使用具有固定视野的单摄像机 无法 保证看到所有地方的交通灯。 局限 是由于以下因素引起的:

① 感知范围可能超过100米
② 交通灯的高度或交叉路口的宽度变化很大

因此,Apollo 2.0使用 两个相机 来扩大感知范围:

安装了一个焦距为25毫米的远距摄像机,用来观察 前方远处的交通灯 。在长焦相机中捕获的交通灯 非常大且易于检测 。然而,长焦相机的 视野非常有限 。如果车道不够直,或者车辆与交通灯比较接近,则交通灯通常将处于图像之外(处于相机视野之外)。

长焦相机检测交通信号灯所获图像

一个 广角相机 ,其焦距为6mm,用于 提供足够宽的视野 。该模块根据光线投影自适应地选择使用哪个相机。虽然Apollo汽车上只有两个摄像头,但该算法可以处理多个摄像头。

广角相机检测交通信号灯所获图像

流水线

以下将介绍流水线的 两个主要组成部分 :
Pre-process(预处理阶段) 交通灯投影 相机选择 图像和缓存交通灯的同步
Process(处理阶段) 矫正 - 提供准确的交通灯边界框 识别 - 提供每个边界框的颜色 修订 - 根据时间顺序校正颜色

Pre-process

无需对每一帧图像都进行交通灯检测。 因为交通灯变化的频率比较低,并且计算资源也比较有限。通常情况下,来自不同摄像机的图像几乎同时到达,但是只有一张图片会被送到整个流水线的Process部分。因此, 图像的选择和匹配 就显得十分必要。

输入and输出

本节介绍 Pre-process模块的输入和输出 。输入可以通过订阅系统中的 相关主题名称 或直接从 本地存储的文件中读取 获得,输出则直接给后续的 Process模块 。

① 输入

• 通过订阅主题名称获得的来自不同相机的图像,例如:

/apollo/sensor/camera/traffic/image_long
/apollo/sensor/camera/traffic/image_short

• 通过查询主题获得的定位信息:
/tf
• 高精地图
• 标定结果
② 输出

• 所选相机拍摄的图片
• 从世界坐标系投射到图像坐标系的交通灯边界框

摄像头选择

交通灯由 唯一ID 和 其边界上的四个点 表示,每个点在世界坐标系中被描述为3D点。
以下示例是 交通灯signal info 的一种典型表示。根据汽车的位置,通过查询HD地图可以获得 四个边界点 。


然后将 三维世界坐标中的边界点 投影到每个相机的 二维图像坐标系 中。对于一个交通灯而言,由长焦相机图片中四个投影点描述的边界框区域比较大。检测效果优于广角相机图像。因此,可以看到 所有交通灯的最长焦距相机的图像 将被选为输出图像。投影在此图像上的 交通灯边界框 将作为 边框输出 。

带有 时间戳 的被选中摄像机ID将被缓存到队列中,如下所示:


到目前为止,我们需要的所有信息包括 定位,标定结果和高精地图 。选择过程可以在任何时间点进行,因为投影是独立于图像内容的。在图像到达时执行选择任务仅仅是为了简单起见。此外,不需要在每个图像到达时执行图像选择,可以为设选择设置一个 时间间隔 。

图像同步

图像到达时带有 时间戳 和 摄像机ID 等信息。时间戳和摄像机ID的配对用于查找适当的 缓存信息 。如果可以找到一个具有相同摄像机ID且时间戳与当前图像时间戳 相差很小的缓存记录 ,则可以将图像发布到 “Process”模块 。所有不合适的图像都被废弃。

Process

Process模块分为如下三个步骤,每一步聚焦一个任务上
矫正 – 在ROI中检测交通灯边框 识别 – 对边界框的颜色进行分类 修订 - 根据时序信息更正颜色

输入and输出

本节介绍Process阶段的输入和输出数据。 输入从Pre-process模块获得,输出将作为交通灯主题发布。

① 输入

• 所选摄像机拍摄的图像
• 一组边框数据

② 输出

• 一组带颜色标签的边框

Rectifier(矫正器)

受标定,定位和高精地图标签的影响, 投影位置并不完全可靠 。基于交通灯投影位置计算得到的 较大感兴趣区域(ROI) 将被用于找到更加精确的交通灯边界框。

在下面的照片中, 蓝色矩形 表示投影交通灯边框,它与实际交通灯的位置还有很大偏差。范围更大的 黄色矩形框 是ROI。


交通灯检测 被当成一个 常规卷积神经网络(CNN)检测任务 来实现。它接收一个带ROI的图形作为输入,输出一系列边框。ROI中的交通灯可能比输入中的交通灯多。

Apollo需要根据 检测分数、输入的交通灯位置和形状 来选择合适的交通灯。如果CNN网络无法在ROI中找到任何交通灯,则输入交通灯的状态将标记为 未知 ,并跳过剩余的两个步骤(识别器和修订器)。

交通灯识别被当成一个典型的 CNN分类任务 实现。该神经网络以一个 带ROI的图形 和一个 边框列表 作为输入。具有ROI和边界框列表作为输入的图像。网络的输出一个 4*n的向量 ,表示每个 候选框 的分别为 黑色,红色,黄色和绿色的概率 。

有且只有概率足够大时,具有 最大概率的类 才被视为交通灯的状态。否则, 交通灯的状态将设置为 黑色 , 这意味着 状态 不确定 。

Recognizer(修订器)

由于红绿灯可能闪烁或被遮挡, 并且识别器不是完美的, 因此当前状态可能无法表示真实状态。一个可以 纠正状态的修订器 就显得十分必要。

如果修订器收到一个确定的状态 (如红色或绿色), "修订器" 将直接保存并输出该状态。如果接收到的状态为黑色或未知, 则 Reviser将查找保存的地图 。如果交通灯的状态在一段时间内都是确定, Reviser将输出保存的状态 。否则, 黑色或未知的状态将作为输出。

由于时间顺序的影响, 黄灯只存在于绿灯之后和红灯之前。为了安全起见, 红灯之后的任何黄灯都将重置为红灯, 直到绿灯亮起。
原文链接地址: https://developer.baidu.com/topic/show/290458
云计算
2019-12-10 22:49:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
随着云原生微服务的日益火热,很多人都开始对微服务的相关知识内容感兴趣。本篇内容,旨在扫盲(意思是小白可入),希望能对大家有帮助。如有问题,欢迎大家一起讨论,共同学习进步。
01 微服务从哪里来?--- 服务架构的演进史
互联网初期, 2G还是个时髦词儿,人们的需求也很朴实,一个静态网站告诉大家我是谁、一个留言板让大家能够与我联系,就能满足信息传播和互相交流的需要。于是码农们给我们提供了这样一套解决方案: 界面+业务处理+数据处理 ,通过一个zip包就可完成所有的事情,这也就是服务架构的 单体架构时代 。
图片为作者原创
随着3G的普及,越来越多的人们可以通过PC上网了,此时BBS、门户咨询网站的出现开始吸引着大量观众。当漂亮的交互更能抓人眼球、有趣的信息瞬间引爆千万用户在线围观时,“并发“问题产生了,于是码农们加班奋战,将系统分为前端和后端,通过拆分出可复用的中间件,来提升业务处理能力、解决并发问题,这便是 分层架构时代的到来 。
图片为作者原创
后来,互联网进入微博时代,几乎网民都有Blog,打开手机就刷weibo。而此时的分层架构面对更复杂服务要求时,在应用扩展、服务调用、扩容等方面都越发桎梏,于是服务架构走进了面向服务的架构(SOA)时代。SOA网上说的很多,这里列举几个关键词: 中心化的服务治理, ESB(企业服务总线)中心化、服务之间通过精确定义的接口进行通讯、耦合度更低、扩展性更高、维护成本较高。
图片为作者原创
又过了几年,电商掀起了各个时节的线上大促活动,与之伴随而来的还有持续交付、灰度发布、服务限流、容错保护、链路跟踪、日志监控、弹性伸缩等等一大串需求,也还有程序员日益见秃的头顶和度数越来越深的眼镜。当运维压力已经赶不上业务的快速发展时, 微服务时代来临 。可以这样理解,微服务架构也是SOA架构分布式化的一种实现方式。它的优势在于小而治之、去中心化,但与之对应的问题是,你要管理越来越多的微服务。而如何进行微服务拆分和服务治理,是十分考验能力的试金石。
纵观前后,服务架构历次的迭代更新,都是围绕着用户如何节约成本和提升效率,来解决不断出现的新问题, 微服务就是服务架构演进史的产物之一 。
02 微服务是什么?
图片为作者原创
微服务最流行的定义是由 Martin Fowler 与 James Lewis 于 2014 年共同提出。引用老爷子们的说法: 微服务是架构层的一个概念,通过分解(业务单元),将项目拆解出 n 个单元,互相没有强依赖关系(解耦),自我准备需要的依赖条件,进而达到可以独立运行、独立部署,不再受环境与地点上的限制。 微服务架构风格是一种使用一套小服务来开发单个应用的一种方式,每个服务运行在自己的进程中,并使用轻量级机制通信,通常采用HTTP资源API这样轻量的机制来相互通信,这些服务围绕业务功能进行构建,并能够通过自动化部署机制来独立部署,这些服务使用不同编程语言实现,以及不同数据存储技术,并保持最低限度的集中式管理。
根据InfoQ发布的2019架构和设计领域趋势报告显示,微服务架构已经走过了盲目追捧阶段,开始逐渐走向成熟,走向切实可落地阶段。
图片来源:InfoQ发布的2019架构和设计领域趋势报告
03 如何选?适合的才是最好的
在我们选择之前,先来看看有什么能够选的?
1、微服务框架的分类
目前市场上已经出现的微服务框架非常多,他们各有所长。常见的微服务框架都有哪些呢?
如果从常见微服务框架形式分类来看,目前主要分为4类。 组件类——用户可以按需加载使用。常见的有Kubernetes、Eureka/Consul/etcd、ZipKin/Jagger等。 集成类——集成类的优势在于简化了分布式系统基础设施的开发,提供服务发现注册、配置中心、消息总线、负载均衡、数据监控等内容。常见的有Spring Cloud、Dubbo等。 网格类——常见的有Istio、Linkerd、Kong Mesh等。 无服务类——目前主要是大厂在使用,常见的有Knative、OpenFaaS、Kubeless、Fission等。
如果按照目前的主流生态体系来看,目前有三大生态体系: Spring Cloud家族( https://www. springcloud.cc/ ) Dubbo家族( http:// dubbo.apache.org/en-us/ ) 云原生家族( https://www. cncf.io/ )
这里特提供了官方地址供大家学习,本文不再详细展开讨论,每一个家族都需要熬夜掉一把头发,潜心实践和学习才能掌握。
总而言之,微服务的核心是服务治理,而服务治理就需要好的微服务框架,要不然微服务化可能是灾难!但做为用户,选择适合自己实际情况的才是最好的。
2、选择适合自己的微服务框架
那么我们该如何选择微服务框架呢?依赖于业务特点和技术能力。先选定方向,再研究技术细节。下面关于方向选择的思路供各位参考:
如果你的业务模块与服务治理是整合在一起的,依托特定开发语言和开发框架,通过配置来调整规则和策略,依赖业务上线对服务治理进行功能升级,那么你可能比较适合集成方式进行服务治理。你可以在Spring Cloud、Dubbo生态中去选择合适自己的方式。
如果你的业务模块与服务治理是分开的,与开发语言无关、与开发框架无关,通过动态调整来配置运行时各种规则和策略,服务治理功能升级独立于业务模块,那么你可能比较适合服务网格的方式进行服务治理。你可以在云原生家族中去选择合适自己的方式,比如尝试下Istio。
3、什么时候引入微服务?
微服务不是万能的,换句话说,微服务未见得适合于你。
1)天时
在业务运行初期,如果你是单业务系统架构,如果业务量不大且复杂性不高,如果你追求快速响应、开拓服务、节省成本、提高效率,那么你可能真的用不着微服务。比如你如果只是要用CMS做一套公司网站,那真的可能不用微服务这把牛刀。当随着你的业务进入扩展期,你的系统架构开始走向面向服务架构,业务不断扩大,业务系统复杂性不断提高,但效率在不断下降,那么这个时候你可以开始考虑业务拆分、使用微服务了。
2)地利
如果要进行微服务改造,还需要具备一定的资源条件,如物理机资源、网络资源。举个例子:假设一个电商平台,现状如图。如果业务框架不那么复杂则可考虑不用微服务架构。而如果需要进行微服务改造,那么至少需要准备规划好如下资源: 硬件资源:主机/容器、数据库 软件资源:注册中心、拆分的服务、负载均衡、网关、缓存、监控软件 人力资源:至少需要架构师构建微服务、前端、后端、测试,其中运维的角色可以由研发+微服务平台 代替。
3)人和
如果要享受微服务带来的优势,就需要接受微服务带来的挑战。比如: 虽然微服务的服务边界限定,每个团队可以独立维护演进自己的服务,但是当服务扩展到几十个甚至上百个后,就需要考虑分布式带来的复杂性。 如果说不同服务可独立部署、独立扩展,那么维护不同版本和版本兼容就是需要面对的挑战。 如果说不同的服务可以采用不同的技术栈,只需按照约定好的通信协议即可完成交互,那么服务之间的认证/鉴权/证书管理,共享数据与分离数据后如何保持一致性等一系列复杂的问题等,就是需要面对的挑战。 ……
总之,使用微服务也是需要天时地利人和的,使用的过程也不要一蹴而就,服务治理是巨大的挑战。
你的业务适合微服务吗?我收集整理了几个最简单的问题以供快速自测:
04 微服务化的实施方法
简单来说4步走:
其实每一步都有可能会有“坑”,这里面都是学问,都可独立成章。因此,本文不详细展开,以后再写相关内容文章详谈。
但是,Demo可以快速帮我们一探究竟,这里我选择了京东智联云的微服务平台来做体验。
为啥是云上公有云产品?
因为微服务的体系太复杂庞大了,如果你自建,1个人搞不定1个团队的工作内容。
还因为在“云”上是托管服务,少运维,所见即所得组件多,开源、多可用区、上手够简单够快、学习和使用成本低呀。
简单总结下我的学习路径:
1、体验“云”上高可用注册中心
如果您已有成熟的微服务项目,目前正在上云过程中,希望享受 “云” 带来的注册中心多可用区部署、最大程度的保证集群高可用的能力。那么可以直接使用微服务平台的命名空间注册中心功能。目前JDSF支持的微服务框架包含:SpringCloud、Dubbo、JSF。使用大致步骤如下:
入门示例参见: https:// docs.jdcloud.com/cn/jd- distributed-service-framework/basic-example 。
2、体验调用链分析服务便捷定位性能瓶颈
如果您已有成熟的微服务项目,目前正在上云过程中,希望通过 “云” 计算能力,更加便捷全面掌握服务间调用关系、精准发现系统的瓶颈和隐患的服务、减少运维投入,那么可以直接使用微服务平台的调用链分析服务。目前支持协议:ZipKin、Thrift、HTTP。
入门示例参见: https:// docs.jdcloud.com/cn/jd- distributed-service-framework/basic-example 。
3、体验应用部署
如果您已经使用云上JDSF服务,还可以使用JDSF平台提供的成熟、灵活的部署方案发布微服务应用。目前应用类型支持:云主机应用、K8S应用。
入门示例参见: https:// docs.jdcloud.com/cn/jd- distributed-service-framework/demo-deploy-k8s
4、体验开放服务给客户使用
如果需要开放服务给你的用户使用,假如你已经使用了微服务平台的注册中心服务,微服务网关可以在调用时自动完成服务发现、负载均衡,无需再使用其他负载均衡或网关服务。假如你的服务已经通过其他方式在内网发布到了负载均衡服务上,也可以通过微服务网关实现与API网关的无缝对接,避免公网暴露,不再需要申请公网IP和产生公网流量费用。
入门示例参见: https:// docs.jdcloud.com/cn/jd- distributed-service-framework/gw_vpc
怎么样,微服务看上去是不是又没有那么抽象、那么难、那么抓狂了呢?!更多内容下次再分解。

欢迎点击“ 京东智联云 ”了解更多精彩内容!
云计算
2020-05-27 23:16:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
导读:以Kubernetes的Kong为例,聊聊当前流行的开源且与云无关的Ingress控制器。
您可以通过使用诸如Kong for Kubernetes的Ingress控制器(使用自定义资源定义并提供许多插件)来极大地扩展Ingress资源的功能。
Kubernetes正在整个技术行业中得到采用,并且正逐渐成为现代云服务交付的实际编排平台。Kubernetes不仅提供了用于在云中部署微服务的原语,而且更进一步,帮助开发人员定义交互并管理其API的生命周期。
Kubernetes中的Ingress API允许您将微服务公开给外部世界,并为南北流量(即进入虚拟数据中心的流量)定义路由策略。使用Ingress 使用持续集成和持续交付(CI / CD)管道来管理API生命周期的好处很多,但是在我们介绍这些之前,让我们从一些基础知识开始。
Ingress资源的设计和目的
Kubernetes集群的最简单描述是在容器中运行应用程序的一组受管节点。在大多数情况下,Kubernetes集群中的节点不会直接暴露于公共互联网。这是有道理的,因为将所有服务公开在一个节点上会带来不可思议的风险。为了向公众提供对选定服务的访问,Kubernetes提供了Ingress资源。
Ingress资源公开了从群集外部到其中的选定服务的HTTP和HTTPS路由。Ingress资源还提供控制流量的规则。这使得Ingress资源成为处理大量独立服务提供的各种API的理想解决方案。为此,它为所有客户端提供一个入口点,然后处理对后端服务的请求。这通常称为扇出配置。


还可以为基于名称的虚拟主机设置Ingress资源,该资源将基于主机头路由请求:


为了使Ingress资源正常工作,需要在Kubernetes集群上安装Ingress控制器。控制器在Kubernetes集群和现有的各种面向公众的接口之间建立了桥梁。例如,大多数托管Kubernetes的云提供商都提供了一个独特的Ingress控制器,以与他们规定的面向公众的方法对接。各个控制器的操作彼此不同,并且可以提供不同数量的附加功能。
使用Ingress通过CI / CD管道管理API生命周期的好处
Ingress资源是通过声明性配置文件定义的,该文件通常在YAML中进行描述。这与所有Kubernetes资源一致,并允许直接集成到现代部署模式中,例如CI / CD的组合实践。这就是快速,频繁且安全地部署Ingress更改的能力。这样,可以将Ingress资源与应用程序本身合并到相同类型的软件开发生命周期模式中。
开发人员如何使用Kong for Kubernetes完成Ingress
面向Kubernetes的Kong是一个流行的开源且与云无关的Ingress控制器。Kong for Kubernetes入口控制器是作为Kubernetes中的自定义资源定义(CRD)构建的。这为那些已经习惯在该平台内定义资源的人提供了Kubernetes原生体验。
像您的应用程序和服务一样,可以通过Manifest,Helm或Kustomize安装Kong for Kubernetes。
Kong for Kubernetes Ingress Controller通过提供广泛的插件集来扩展Ingress资源的功能,这些插件涵盖了包括身份验证,分析,监视以及请求和响应转换在内的各种功能,仅举几例。通过在Ingress控制器上提供这些常见(有时不是很常见)的要求,Kong for Kubernetes可使开发人员将更多精力放在服务的核心要求上。当组织从少数的单一应用程序迁移到数百个(甚至数千个)微服务时,其价值变得尤为明显。
有关常见插件的列表,请访问 https:// docs.konghq.com/hub/ 。
Kong插件被定义为Kubernetes资源,其中的config部分提供了各个插件的设置。以下是一个限速插件示例,该插件将流量限制为每分钟五个请求:


通过在资源的元数据部分中的简单注释即可将Kong插件添加到Kubernetes资源中。这允许将插件应用于不同的层。例如,您可以将一个插件应用于整个Ingress资源,或者以一种更细粒度的方式将其应用于单个服务资源。
这是将上述插件应用于Ingress资源的示例:


Kong for Kubernetes还可以集成到Kong Enterprise产品的完整套件中,包括Kong Studio,Kong Dev Portal,Kong Manager, Kong Brain和Kong Immunity。这样就可以使用更高级的Kong插件以及完整的API生命周期解决方案。这套产品涵盖API规范的创作和发布,以及Kong资源的管理,甚至流量分析。
您可以采用“规范优先”的方法来使用Kong Studio开发API。在该库中,您将找到用于在标准OpenAPI规范中编写文档的工具以及用于立即反馈的测试工具。Kong Studio还提供了使用GraphQL的工具。Kong Studio直接同步到Git,这可以将您的规格文件集成到CI / CD工作流程中,从而可以自动更新Kong Dev Portal。
Kong Dev Portal托管您的API文档(可以是私人的也可以是公共的)。它具有极高的可定制性,可让您使其适应组织的风格和品牌。拥有完善文档的API对于提高生产力很重要,并且在Kong Studio和Dev Portal之间进行良好管理的流程可以帮助确保文档尽可能保持最新。
Kong Manager提供了图形界面来观察和管理整个Kong产品套件。从这里,您可以观察路由,服务和插件之间的关系。您可以实时了解流量并跟踪消费者。
Kong Brain会分析通过Ingress的流量,并创建服务间依赖关系的可视服务图。它还具有根据生成的地图自动生成OpenAPI规范文档的功能。这是一项有价值的功能,因为即使出于最佳意图,也可能无法正确记录所部署的服务。
Kong Immunity会分析通过Ingress的所有流量,并学习识别异常的模式。这些通常是微妙的请求,它们并不引人注目,但可能会引起人们的兴趣,例如不断尝试通过的未知参数。这也是一个非常有价值的功能,因为将这些针刺入成千上万个日志条目的大海捞针并不容易。


充分利用Ingress
Kubernetes入口资源提供了从外部Kubernetes到内部后端服务的单个入口点。通过利用声明性定义文件,可以像对待所有其他形式的代码一样对待Ingress资源,并将其集成到常见的软件开发生命周期中。
为了桥接Kubernetes之外的通信,需要一个Ingress控制器。Kong for Kubernetes是一个Ingress控制器,它使用自定义资源定义通过提供大量插件来极大地扩展Ingress资源的功能,从而使开发人员可以专注于核心业务价值。Kong拥有一套企业工具,可以在整个API生命周期内极大地提高生产力和安全性。
云计算
2020-05-18 16:38:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
以“云”为核心的软件研发思想,正逐步成为所有开发者的默认选项。像 Kubernetes 等云原生技术正在成为技术人员的必修课,大量的工作岗位正在涌现出来。2020 年,云原生技术大规模普及。这种背景下,“会 Kubernetes”已经远远不够了,“懂 Kubernetes 如何落地”、“会云原生架构”的重要性正日益凸显出来。
2019年 4 月,阿里云联合 CNCF 发布了《CNCF X Alibaba 云原生技术公开课》,课程发布至今已可统计学习者 12万,在收到学习者“点赞”的同时,我们也对学习者的学习需求以及痛点进行了调研和采访,几乎课程学习者反馈的关键词都落在了**“实践”**这两个字上,具体有以下 3 个问题: 企业/传统企业如何实现 K8s 落地 生产环境下,K8s 问题排查:比如网络问题 希望有以具体场景为切入点的实操课程
(学习者对课程部分反馈截图)
作为云原生技术的忠实践行者,阿里巴巴是全球少数大规模落地云原生架构的企业。在落地云原生的过程中,阿里巴巴积累了许多宝贵的实战经验。基于学习者的反馈和建议,我们决定重磅推出《云原生技术实践公开课》。
本课程集结 30+ 位实战经验丰富的云原生技术专家。以具体场景为切入点,深度剖析真实案例;设置可操作实验环境,让你快速上手并在生产环境下实践 K8s。我们希望能以此帮助每一位云时代的开发者和技术人员,把握到云原生时代的精髓与本质,高效解决生产环境中遇到的难题。
课程亮点 以具体场景为切入点讲解 K8s 应用和落地难题 剖析最佳实践从零实践 K8s 提供真实的企业级落地案例 提供可动手操作的实验环境
开始听课
课程地址: https://developer.aliyun.com/learning/roadmap/cloudnative2020
适合人群 计算机科学、软件工程等领域学生 Kubernetes 运维工程师 云原生应用程序开发人员 企业架构师、CTO
课程讲师
张磊,阿里巴巴高级技术专家,CNCF 官方大使,CNCF 应用交付领域 co-chair。Kubernetes 项目资深维护者。主要关注云原生应用管理体系与架构,容器运行时接口(CRI)、调度、资源管理等特性,共同负责 Kubernetes 上游和阿里集团大型集群管理系统的工程工作。
Andy Shi,阿里巴巴高级技术专家,阿里巴巴集团开发者布道师,常年在硅谷从事开源技术推广,在云平台使用和网络基础架构方面拥有丰富经验。
声东,阿里巴巴售后技术专家,阿里云云原生售后技术领头人,系统技术专家;在软硬件技术,操作系统,云原生领域有多年技术经验。
王夕宁,阿里巴巴高级技术专家,阿里云服务网格 ASM 技术负责人,关注 Kubernetes、云原生、服务网格等领域,《Istio 服务网格技术解析与实践》作者。
莫源,阿里巴巴高级技术专家,主要负责阿里云容器服务产品的底层服务发现系统、集群管理系统、弹性伸缩与监控的研发,从事容器的持续交付、持续集成的方案的设计与实现。在云计算、分布式系统、图像识别与虚拟现实方向有多年的开发实践经验。
王炳燊,阿里巴巴技术专家,主要负责容器服务 ACK 容器网络设计和研发;阿里云开源 CNI 插件Terway(github.com/AliyunContainerService/terway)项目维护者。
酒祝,阿里巴巴技术专家,OpenKruise maintainer,阿里集团云原生应用 workload 开发负责人,多年支撑阿里巴巴双十一超大规模容器集群经验。
孙健波,阿里巴巴技术专家,是 OAM 规范的主要制定者之一,致力于推动云原生应用标准化。同时也在阿里巴巴参与大规模云原生应用交付与应用管理相关工作。
课程大纲
互动福利
8 月 14 日 11:00 前欢迎在阿里巴巴云原生公众号留言区 说出关于课程你还有哪些想听的内容: 留言点赞第 1 名,后续课程将安排相应的内容 留言点赞前 3 名,将获得阿里云定制全自动雨伞 “ 阿里巴巴云原生 关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”
云计算
2020-08-13 11:26:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
北京时间2019年10月17日,Kubernetes发布了新的补丁版本,修复了新近发现的两个安全漏洞:CVE-2019-11253和CVE-2019-16276。Rancher第一时间响应,就在当天紧随其后发布了Rancher v2.3.1和Rancher v2.2.9。更新的版本中不仅支持Kubernetes新的补丁版本,还修复了一些原有的bug并对部分功能进行了优化。
目前,Rancher的Latest和Stable版本信息如下:
Kubernetes CVE漏洞详情
CVE-2019-11253: CVE-2019-11253是kube-apiserver中的一个拒绝服务漏洞,它允许授权用户发送恶意的YAML或JSON有效负载,然后导致kube-apiserver消耗过多的CPU或内存,从而可能崩溃并变得不可用。
CVE-2019-16276: 在Go的net/ http库中CVE-2019-16276会导致无效的请求头被HTTP服务器规范化并解释为有效。如果Go HTTP服务器前的反向代理允许并转发某无效的请求头,但又不对其进行规范化,则Go服务器对这些请求头的解释可能与反向代理不同。
Kubernetes发布了下述这些新版本,解决上述安全漏洞: v1.13.12 v1.14.8 v1.15.5 v1.16.2
为了您的集群安全,强烈建议您将Kubernetes集群都升级至最新发布的修复版本。
Rancher第一时间有效应对
Rancher第一时间对漏洞做出响应,发布了新的版本Rancher 2.3.1和Rancher 2.2.9以支持上述Kubernetes补丁版本。Rancher 2.2.9支持Kubernetes v1.13.12、v1.14.8和v1.15.5(默认),Rancher v2.3.1在此基础上还添加了对Kubernetes v1.16.2的实验性支持。
请注意:
Rancher 1.6.x用户不受Kubernetes的这两个安全漏洞影响,因为Rancher 1.6.x自身不支持这两个漏洞所影响的Kubernetes版本。
关于Rancher 2.0.x的用户:
正如Rancher服务条款页面所示,Rancher 2.0.x目前处于其产品生命周期的EOM到EOL支持阶段。因此,Rancher官方没有计划发布v2.0.x补丁版本来修复这两个漏洞。对于Rancher的企业级订阅客户,如果您有特殊情况,需要在v2.0.x版本中修复这两个漏洞,请联系Rancher的技术支持团队。或者,请在v2.0.x 的EOL日期(2019年11月1日)之前,将您的Rancher升级到最新版本。
关于Rancher 2.1.x的用户: Rancher v2.1.x仅支持Kubernetes v1.13.x 正如Rancher服务条款页面所示,Rancher v2.1.x目前处于其产品生命周期的EOM到EOL支持阶段。因此,Rancher官方没有计划发布v2.1.x补丁版本来修复这两个漏洞。对于Rancher的企业级订阅客户,如果您有特殊情况,需要在v2.1.x版本中修复这两个漏洞,请联系Rancher的技术支持团队。
Rancher 2.3.1修复的bug 修复了由于引用v1 API [#23365] 而无法使用LetsEncrypt证书安装单节点的问题; 修复了守护脚本无法完成超时原因并允许超时可配置的问题[ #22379,#23160 ]; 修复了在模板创建和模板显示期间未正确处理Kubernetes版本的RKE模板的问题。[#23360,#23359,#23361] 修复了RKE模板无法为某些Kubernetes版本启用Windows支持的问题[#23395] 修复了尝试将群集更新到新的RKE模板修订版不起作用的问题[#23383] 修复了在升级后无法将捆绑的系统图表与单个节点容器或绑定安装一起使用的问题[#23427] Rancher和RKE使用的yaml库升级到包含CVE-2019-11253修复程序的 v2.2.4版本
如果您想了解与上述issue相关的详细信息,请至Rancher Github issue界面输入issue编号进行查询:
https://github.com/rancher/rancher/issues
下载及升级
您可以至Rancher GitHub主页阅读完整的Rancher v2.3.1和Rancher v2.2.9 Release Note、下载使用最新版本、或了解更多与升级回滚有关的注意事项。
Github链接:
https://github.com/rancher/rancher/releases
云计算
2019-10-18 10:15:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
来源:Redislabs
作者:Roshan Kumar
翻译:Kevin (公众号:中间件小哥)
最近,我有幸在 Spark +AI 峰会上发表了题目为“ Redis + Structured Streaming: 扩展您的持续应用的完美组合 ”的演讲。
我对这个主题的兴趣是由 Apache Spark 和 Redis 在过去几个月中引入的新功能引起的。根据我之前使用 Apache Spark 的经验,我很欣赏它在运行批处理时的优雅,并且它在 2.0 版本中引入 Structured Streaming 是在这个方向上的进一步发展。
与此同时,Redis 最近宣布了用于管理流数据的新数据结构,称为“ Streams ”。Redis Streams 提供了生产者和消费者之间的异步通信功能以及持久性、回顾性查询功能和类似于 Apache Kafka 的横向扩展选项。从本质上讲,Redis 通过Streams 提供了一个轻便、快速、易于管理的流数据库,使数据工程师们受益良多。
此外,开发 Spark-Redis 库 是为了使 Redis 可以作为弹性分布式数据集(RDD)使用。因为现在有了 Structured Streaming 和 Redis Streams,我们决定扩展 Spark-Redis 库将 Redis Streams 集成为 Apache Spark Structured Streaming 的数据源。


在上个月的演讲中,我演示了如何在 Redis Streams 中收集用户活动数据并将其下载到 Apache Spark 进行实时数据分析。我开发了一个小型的适合移动设备的 Node.js 应用程序,在这个程序中人们可以点击投票给他们最喜欢的狗来进行有趣的比赛。
这是一场艰苦的战斗,有几个观众甚至是黑客很有创意地攻击了我的应用程序。他们使用“页面检查”选项更改了 HTML 按钮名称试图弄乱应用的显示。但最终他们失败了,因为 Redis Streams,Apache Spark,Spark-Redis 库和我的代码都足够的强大,可以有效地应对这些攻击。


在我演讲期间和之后观众还询问了一些有趣的问题,例如:
1. 如果数据处理速度低于 Redis Streams 接收数据的速率,该如何扩展?
我的回答 :配置一个 Redis Streams 的消费者组,将每个 Spark 作业作为属于该组的一个消费者,这样每个作业都会获得一组独有的数据,将输出模式设置为“更新”非常重要,这样每个作业都不会覆盖其他作业的数据提交。

2. 如果我重新启动 Spark 作业,Redis Streams 中的数据会发生什么变化?
我的回答: RedisStreams 持久化数据。因此您的 Spark 作业不会遗漏任何数据,如果重新启动 Spark 作业,它将从之前停止的位置提取数据。

3. 我可以用 Python 开发我的 Spark 应用程序吗? (我的演示是用 Scala 编写的)
我的回答 :是的,你可以,请参阅 GitHub 上的 Spark-Redis 文档。

4. 我可以在云上部署 Redis Streams 吗?
我的回答: 是的,Streams 只是 Redis 中的另一个数据结构,从 5.0 版开始内置于 Redis 中,最快捷的方式是在 https://redislabs.com/get-started 上注册。

我在峰会上的主要收获是了解到人们对连续处理和数据流的兴趣日益浓厚。根据大家的需求,我们在 InfoQ 上发布了一篇关于此主题的更详细的文章,在其中提供了有关如何设置 Redis Streams 和 Apache Spark 以及使用 Spark-Redis 库进行连接的详细信息,大家也可以随时查看我演讲的完整视频。

更多优质中间件技术资讯/原创/翻译文章/资料/干货,请关注“中间件小哥”公众号!
云计算
2019-10-17 17:44:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
云计算有三种服务模式,IaaS,PaaS和SaaS。单从英文全称去理解,他们分别是“基础设施即服务”“平台即服务”和“软件即服务”。
这样翻译过来可不好理解,但是我们可以举个例子。现在我们就以做饭吃饭这件“人生大事”来类比这三种服务模式。
一、
1.“田园”式吃饭
到了饭点,要吃饭了。
身处深山的你去田里摘一把菜,拿上一条风干的肉,架起土锅土灶,点上昨日砍好的柴禾,一通忙活,1个小时过去,荤素搭配的一顿饭做好,就能够开心地吃饭了。
这是“自力更生,丰衣足食。”
自力更生,丰衣足食
2.城市的吃饭日常
到了饭点,要吃饭了。
住在小区楼房里的你有一个大大的厨房。将超市买来的食材清洗,一边开煤气倒油菜入锅,一边打开手机搜菜谱,一通忙活,40分钟过去了,美美吃上一顿饭,吃完饭一边刷锅刷碗一边还想着“迟早买个洗碗机”。
这是“工业社会完善的生活设施”。
工业社会完善的生活设施
3.做饭的共享经济
到了饭点,要吃饭了。
公寓里的你来到公寓统一提供的公共厨房。锅碗瓢盆皆备,煤气按量付费,烤箱炸锅任君使用,甚至还可以参考参考人家的今日菜谱。你拿着食材,20分钟就做好了一顿饭。垃圾有专人处理,炊具有专人清洗。
这是“共享经济时代的资源复利用。”
共享经济时代的资源复利用
4.出去吃
到了饭点,要吃饭了。
你施施然走到楼下快餐面馆。你看了看菜单,“老板,一碗牛肉面,不要面!”哦不是,“老板,一碗牛肉面,不要香菜!”几分钟后,一碗热气腾腾的面就端到面前。你就着手机网友的下饭评论,风卷残云吃完,留下钱,抹抹嘴就走人了。
这是“当代年轻人的生活现状(误)”
高度标准化服务
二、
总结一下,你可以通过四种途径吃饭:

完全DIY/日常做饭/共享厨房/下馆子
而这些步骤实际就对应着云计算的四种类型:

On-premises,Iaas,Paas,Saas
第一个对应着本地化部署(On-premises)。自己种菜,自己准备燃料,做饭过程中一切操作基本不依赖外界,自给自足。对应到云计算中就是公司什么都自己准备好。
第二个是基础设施服务(Iaas)。厨房是存储与服务器,燃料则是网络。你的公司运营中不需要操心服务器、存储、网络这些,因为他们就跟现代城市生活中的水电一样,是云计算时代的基础设施。
第三个是平台(Paas)。这其中炊具是操作系统,餐具则是中间件。你的公司购买了平台服务,所以除了基础设施,服务商还会通过中间件提供给你各种开发使用的解决方案,节省时间和资源。
第四个当然是软件(Saas)。普通消费者接触的基本就是这一层,最常见的就是各式web微应用。你甚至不需要自己准备数据(食材),只需想好自己需要什么,然后想用就可以了。
三、

JEPaaS云平台,是一款专为SaaS而生的PaaS开发平台。作为凯特科技自主研发的基于互联网架构的新一代智能型高性能技术平台,JEPaaS凝聚了10多年来服务于国企、央企等大型集团性企业管理咨询及信息化落地的实践探索和丰富经验,JEPaaS在商用化程度及性能水平上与国际品牌同类型产品拥有同等竞争力,并具有独特的优势:
(1)专为SaaS而生
快速便捷搭建企业级SaaS应用,支持个性化定制开发,SaaS应用全方位技术支持;
(2)快速开发成本低
可视化开发,丰富的前后端工具提升开发质量,并从人员成本、时间成本、沟通成本及机会成本四个纬度降低开发成本;
(3)完善的安全与服务体系
专业技术与服务团队,提供整体解决方案服务以及金融级的全面的防护手段和监控体系,保证系统安全稳定的运行;
(4)丰富的工具 任务管理:页面定制、执行、监控任务,分布式计算高效利用系统资源; 流程引擎:图形化配置审批流程,自动流转,实现审批流程和开发的分离; 规则引擎:图形化配置业务规则,自动流转,实现业务和功能开发的分离; 架构先进:采用分布式架构设计,组件化开发,根据客户需求提供完善的解决方案。
(5)强大的接口
JEPaaS的接口引擎,基于面向服务的SOA体系设计,内置多种标准接口协议,大量接口服务可供调用;
(6)良好的兼容性
适配主流的操作系统、中间件、数据库,浏览器等;
(7)完整产业链条
JEPaaS拥有不同行业的海量客户,基于JEPaaS的云端产业链,可实现各产品及功能模块的全民开发、全民共享、全民受益。
JEPaaS云平台,新一代的企业级PaaS平台,为企业数字化业务提供了按需使用、持续运行的基础能力。快速满足企业多变的需求,允许个性化定制,提供支撑企业业务的完美解决方案,为企业业务的快速创新提供了重要支撑,加速企业数字化转型。
云计算
2019-10-17 16:05:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 阿里巴巴高级技术专家  叶磊
一、Kubernetes 基本网络模型
本文来介绍一下 Kubernetes 对网络模型的一些想法。大家知道 Kubernetes 对于网络具体实现方案,没有什么限制,也没有给出特别好的参考案例。Kubernetes 对一个容器网络是否合格做出了限制,也就是 Kubernetes 的容器网络模型。可以把它归结为约法三章和四大目标。 约法三章的意思是:在评价一个容器网络或者设计容器网络的时候,它的准入条件。它需要满足哪三条? 才能认为它是一个合格的网络方案。 四大目标意思是在设计这个网络的拓扑,设计网络的具体功能的实现的时候,要去想清楚,能不能达成连通性等这几大指标。
约法三章
先来看下约法三章: 第一条:任意两个 pod 之间其实是可以直接通信的,无需经过显式地使用 NAT 来接收数据和地址的转换; 第二条:node 与 pod 之间是可以直接通信的,无需使用明显的地址转换; 第三条:pod 看到自己的 IP 跟别人看见它所用的IP是一样的,中间不能经过转换。
后文中会讲一下我个人的理解,为什么 Kubernetes 对容器网络会有一些看起来武断的模型和要求。
四大目标
四大目标其实是在设计一个 K8s 的系统为外部世界提供服务的时候,从网络的角度要想清楚,外部世界如何一步一步连接到容器内部的应用? **外部世界和 service 之间是怎么通信的?**就是有一个互联网或者是公司外部的一个用户,怎么用到 service?service 特指 K8s 里面的服务概念。 service 如何与它后端的 pod 通讯? pod 和 pod 之间调用是怎么做到通信的? 最后就是 pod 内部容器与容器之间的通信?
最终要达到目标,就是外部世界可以连接到最里面,对容器提供服务。
对基本约束的解释
对基本约束,可以做出这样一些解读:因为容器的网络发展复杂性就在于它其实是寄生在 Host 网络之上的。从这个角度讲,可以把容器网络方案大体分为 **Underlay/Overlay **两大派别: Underlay 的标准是它与 Host 网络是同层的,从外在可见的一个特征就是它是不是使用了 Host 网络同样的网段、输入输出基础设备、容器的 IP 地址是不是需要与 Host 网络取得协同(来自同一个中心分配或统一划分)。这就是 Underlay; Overlay 不一样的地方就在于它并不需要从 Host 网络的 IPM 的管理的组件去申请 IP,一般来说,它只需要跟 Host 网络不冲突,这个 IP 可以自由分配的。
为什么社区会提出 perPodperIP 这种简单武断的模型呢?我个人是觉得这样为后面的 service 管理一些服务的跟踪性能监控,带来了非常多的好处。因为一个 IP 一贯到底,对 case 或者各种不大的事情都会有很大的好处。
二、Netns 探秘
Netns 究竟实现了什么
下面简单讲一下,Network Namespace 里面能网络实现的内核基础。狭义上来说 runC 容器技术是不依赖于任何硬件的,它的执行基础就是它的内核里面,进程的内核代表就是 task,它如果不需要隔离,那么用的是主机的空间( namespace),并不需要特别设置的空间隔离数据结构( nsproxy-namespace proxy)。
相反,如果一个独立的网络 proxy,或者 mount proxy,里面就要填上真正的私有数据。它可以看到的数据结构如上图所示。
从感官上来看一个隔离的网络空间,它会拥有自己的网卡或者说是网络设备。网卡可能是虚拟的,也可能是物理网卡,它会拥有自己的 IP 地址、IP 表和路由表、拥有自己的协议栈状态。这里面特指就是 TCP/Ip协议栈,它会有自己的status,会有自己的 iptables、ipvs。
从整个感官上来讲,这就相当于拥有了一个完全独立的网络,它与主机网络是隔离的。当然协议栈的代码还是公用的,只是数据结构不相同。
Pod 与 Netns 的关系
这张图可以清晰表明 pod 里 Netns 的关系,每个 pod 都有着独立的网络空间,pod net container 会共享这个网络空间。一般 K8s 会推荐选用 Loopback 接口,在 pod net container 之间进行通信,而所有的 container 通过 pod 的 IP 对外提供服务。另外对于宿主机上的 Root Netns,可以把它看做一个特殊的网络空间,只不过它的 Pid 是1。
三、主流网络方案简介
典型的容器网络实现方案
接下来简单介绍一下典型的容器网络实现方案。容器网络方案可能是 K8s 里最为百花齐放的一个领域,它有着各种各样的实现。容器网络的复杂性,其实在于它需要跟底层 Iass 层的网络做协调、需要在性能跟 IP 分配的灵活性上做一些选择,这个方案是多种多样的。
下面简单介绍几个比较主要的方案:分别是 Flannel、Calico、Canal ,最后是 WeaveNet,中间的大多数方案都是采用了跟 Calico 类似的策略路由的方法。 **Flannel **是一个比较大一统的方案,它提供了多种的网络 backend。不同的 backend 实现了不同的拓扑,它可以覆盖多种场景; **Calico **主要是采用了策略路由,节点之间采用 BGP 的协议,去进行路由的同步。它的特点是功能比较丰富,尤其是对 Network Point 支持比较好,大家都知道 Calico 对底层网络的要求,一般是需要 mac 地址能够直通,不能跨二层域; 当然也有一些社区的同学会把 Flannel 的优点和 Calico 的优点做一些集成。我们称之为嫁接型的创新项目  Cilium ; 最后讲一下  WeaveNet ,如果大家在使用中需要对数据做一些加密,可以选择用 WeaveNet,它的动态方案可以实现比较好的加密。
Flannel 方案
Flannel 方案是目前使用最为普遍的。如上图所示,可以看到一个典型的容器网方案。它首先要解决的是 container 的包如何到达 Host,这里采用的是加一个 Bridge 的方式。它的 backend 其实是独立的,也就是说这个包如何离开 Host,是采用哪种封装方式,还是不需要封装,都是可选择的。
现在来介绍三种主要的 backend: 一种是用户态的 udp,这种是最早期的实现; 然后是内核的 Vxlan,这两种都算是 overlay 的方案。Vxlan 的性能会比较好一点,但是它对内核的版本是有要求的,需要内核支持 Vxlan 的特性功能; 如果你的集群规模不够大,又处于同一个二层域,也可以选择采用 host-gw 的方式。这种方式的 backend 基本上是由一段广播路由规则来启动的,性能比较高。
四、Network Policy 的用处
Network Policy 基本概念
下面介绍一下 Network Policy 的概念。
刚才提到了 Kubernetes 网络的基本模型是需要 pod 之间全互联,这个将带来一些问题:可能在一个 K8s 集群里,有一些调用链之间是不会直接调用的。比如说两个部门之间,那么我希望 A 部门不要去探视到 B 部门的服务,这个时候就可以用到策略的概念。
基本上它的想法是这样的:它采用各种选择器(标签或 namespace),找到一组 pod,或者找到相当于通讯的两端,然后通过流的特征描述来决定它们之间是不是可以联通,可以理解为一个白名单的机制。
在使用 Network Policy 之前,如上图所示要注意 apiserver 需要打开一下这几个开关。另一个更重要的是我们选用的网络插件需要支持 Network Policy 的落地。大家要知道,Network Policy 只是 K8s 提供的一种对象,并没有内置组件做落地实施,需要取决于你选择的容器网络方案对这个标准的支持与否及完备程度,如果你选择 Flannel 之类,它并没有真正去落地这个 Policy,那么你试了这个也没有什么用。
配置实例
接下来讲一个配置的实例,或者说在设计一个 Network Policy 的时候要做哪些事情?我个人觉得需要决定三件事: 第一件事是控制对象,就像这个实例里面 spec 的部分。spec 里面通过 podSelector 或者 namespace 的 selector,可以选择做特定的一组 pod 来接受我们的控制; 第二个就是对流向考虑清楚,需要控制入方向还是出方向?还是两个方向都要控制? 最重要的就是第三部分,如果要对选择出来的方向加上控制对象来对它流进行描述,具体哪一些 stream 可以放进来,或者放出去?类比这个流特征的五元组,可以通过一些选择器来决定哪一些可以作为我的远端,这是对象的选择;也可以通过 IPBlock 这种机制来得到对哪些 IP 是可以放行的;最后就是哪些协议或哪些端口。其实流特征综合起来就是一个五元组,会把特定的能够接受的流选择出来 。
本文总结
本文内容到这里就结束了,我们简单总结一下: 在 pod 的容器网络中核心概念就是 IP,IP 就是每个 pod 对外通讯的地址基础,必须内外一致,符合 K8s 的模型特征; 那么在介绍网络方案的时候,影响容器网络性能最关键的就是拓扑。要能够理解你的包端到端是怎么联通的,中间怎么从 container 到达 Host,Host 出了 container 是要封装还是解封装?还是通过策略路由?最终到达对端是怎么解出来的? 容器网络选择和设计选择。如果你并不清楚你的外部网络,或者你需要一个普适性最强的方案,假设说你对 mac 是否直连不太清楚、对外部路由器的路由表能否控制也不太清楚,那么你可以选择 Flannel 利用 Vxlan 作为 backend 的这种方案。如果你确信你的网络是 2 层可直连的,你可以进行选用 Calico 或者 Flannel-Hostgw 作为一个 backend; 最后就是对 Network Policy,在运维和使用的时候,它是一个很强大的工具,可以实现对进出流的精确控制。实现的方法我们也介绍了,要想清楚你要控制谁,然后你的流要怎么去定义。
五、思考时间
最后留一些思考,大家可以想一想: 为什么接口标准化 CNI 化了,但是容器网络却没有一个很标准的实现,内置在 K8s 里面? Network Policy 为什么没有一个标准的 controller 或者一个标准的实现,而是交给这个容器网络的 owner 来提供? 有没有可能完全不用网络设备来实现容器网络呢?考虑到现在有 RDMA 等有别于 TCP/IP 的这种方案。 在运维过程中网络问题比较多、也比较难排查,那么值不值得做一个开源工具,让它可以友好的展示从 container 到 Host 之间、Host 到 Host 之间,或者说封装及解封装之间,各个阶段的网络情况,有没有出现问题,能够快速的定位。据我所知应该现在是没有这样的工具的。
以上就是我对 K8s 容器网络的基本概念、以及 Network Policy 的一些介绍。 “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”
云计算
2019-10-17 15:29:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
2019年9月17日,TPC官宣Alibaba Cloud MaxCompute认证结果。同月26日,杭州云栖大会阿里巴巴宣布了这一成绩,飞天大数据平台计算引擎MaxCompute成为全球首个TPCx-BB认证的公共云产品,是除Hive、Spark以外TPCx-BB第三个标准支持的大数据引擎。不仅首次将数据规模拓展到100TB,性能达到25641.21QPM,更在TPCx-BB已有最大30TB规模上,将性能提升近一倍,达到6427.86QPM,单位价格下降一半,达到169.76$/QPM。

TPCx-BB是由国际标准化测试权威组织(TPC)发布的基于零售业场景构建的端到端大数据测试基准,支持主流分布式大数据处理引擎,模拟了整个线上与线下业务流程,有30个查询语句,涉及到描述性过程型查询、数据挖掘以及机器学习的算法。涵盖了结构化、半结构化和非结构化数据,能够从客户实际场景角度更全面的评估大数据系统软硬件性能、性价比、服务和功耗等各个方面。
MaxCompute正是希望能够从更加接近实际生产场景和客户场景的角度,来呈现飞天大数据平台的计算性能和性价比优势。而MaxCompute在TPCx-BB性能、性价比等方面的领先无疑是由MaxCompute技术先进性决定的。
作为支撑MaxCompute计算力的核心之一的SQL引擎,包括了编译器、运行时和优化器3个模块。SQL编译器支持标准SQL,100%支持TPC-DS、TPCx-BB语法。运行时支持列式处理和丰富的关系算符,基于LLVM进行微架构级别的优化。优化器支持基于历史信息的HBO和基于Calcite的CBO,通过多种优化手段提升MaxCompute SQL的性能。
存储方面,则使用先进的存储格式Aliorc,支持列式存储、灵活的编码格式、异步预读及高效的压缩算法,与开源存储格式相比,在存储效率和读写效率上都有显著的提升。MaxCompute以外表的形式支持多种数据源,比如HDFS、OSS外表,可以将TPCx-BB生成在HDFS中的数据高效导入MaxCompute。
调度方面采用基于飞天平台的Fuxi2.0调度系统。其DAG2.0将资源调度overhead控制在了10us级别,远远领先业界同类框架。Shuffle2.0通过数据重排,在磁盘和网络之间找到平衡点,将集群吞吐效率提升30%。
此外,MaxCompute原生支持阿里巴巴机器学习平台PAI,用户可以一站式完成大数据处理与机器学习模型训练及预测。机器学习PAI是飞天AI平台中的核心产品,构建在阿里云MaxCompute等计算平台之上,在机器学习大规模分布式训练场景拥有非常强的性能表现,在本次TPCx-BB的比赛中,PAI基于MaxCompute,在逻辑回归、Kmeans、朴素贝叶斯三个算法的表现性能上取得很好的成绩。
在MaxCompute/PAI多年的系统优化过程中,英特尔作为阿里巴巴重要的合作伙伴,提供了许多助力。英特尔作为测试基准中BigBench的重要贡献者,与阿里云开发团队深入合作,共同扩展TPCx-BB测试集,增加对MaxCompute计算引擎的支持,并一起在TPCx-BB委员会共同推广MaxCompute/PAI,促成TPCx-BB官方测试集升级,继而正式纳入阿里云MaxCompute/PAI计算引擎的支持。
对于未来双方在飞天大数据和AI平台(MaxCompute/PAI)持续优化上的合作,英特尔高级首席工程师、大数据分析和人工智能创新院院长戴金权先生表示,双方在新的硬件架构技术平台有非常紧密的合作,共同探索如何更好地利用新的技术,为大数据处理分析、机器学习平台赋能。另外,双方也合作致力于将AI平台和大数据平台紧密联合,使不同的组件更好地联合在一起,打通整个计算流水线。

阅读原文
本文为云栖社区原创内容,未经允许不得转载。
云计算
2019-10-17 11:58:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者: OAM 项目负责人
导读:2019 年 10 月 17 日,阿里巴巴合伙人、阿里云智能基础产品事业部总经理蒋江伟(花名:小邪)在 Qcon 上海重磅宣布,阿里云与微软联合推出开放应用模型 Open Application Model (OAM)开源项目。OAM的愿景是以标准化的方式沟通和连接应用开发者、运维人员、应用基础设施,让云原生应用管理与交付变得更加简洁,高效,并且可控。
OAM 为什么值得关注? **关注点分离:**开发者关注应用本身,运维人员关注模块化运维能力,让应用管理变得更轻松、应用交付变得更可控。 **平台无关与高可扩展:**应用定义与平台层实现解耦,应用描述支持任意扩展和跨环境实现 **模块化应用运维特征:**可以自由组合和支持模块化实现的运维特征描述
Kubernetes 项目作为容器编排领域的事实标准, 成功推动了诸如阿里云 Kubernetes (ACK)等云原生服务的迅速增长。但同时我们也关注到,Kubernetes 的核心 API 资源比如 Service、Deployment 等,实际上只是应用中的不同组成部分,并不能代表一个应用的全部。也许我们可以通过像 Helm charts 这样的方式来尝试表达一个可部署的应用,可一旦部署起来,实际运行的应用中却依旧缺乏以应用为中心的约束模型。这些问题都反映出,Kubernetes 以及云原生技术栈需要一种以应用为中心的 API 资源来提供一个专注于应用管理的、标准的、高度一致的模型,这个 API 资源可以代表完整运行的应用本身,而不仅仅是应用模板或者一个应用的几个组成部分,这就是今天阿里云与微软联合宣布推出 开放应用模型 Open Application Model (OAM) 的原因。 项目地址: https://oam.dev/
OAM 项目目前由规范和实现两部分组成
什么是 Open Application Model?
OAM 是一个专注于描述应用的标准规范。有了这个规范,应用描述就可以彻底与基础设施部署和管理应用的细节分开。这种 关注点分离(Seperation of Conerns)的设计好处是非常明显的 。 举个例子,在实际生产环境中,无论是 Ingress ,CNI,还是 Service Mesh,这些表面看起来一致的运维概念,在不同的 Kubernetes 集群中可谓千差万别。 通过将应用定义与集群的运维能力分离,我们就可以让应用开发者更专注于应用本身的价值点,而不是”应用部署在哪“这样的运维细节。 此外,关注点的分离让平台架构师可以轻松地把平台的运维能力封装成可被复用的组件,从而让应用开发者能够专注于将这些运维组件与代码进行集成,从而快速、轻松地构建可信赖的应用。 Open Application Model 的目标是让简单的应用管理变得更加轻松,让复杂的应用交付变得更加可控。
一、应用组件(Components)
在 OAM 中,“应用”是由多个概念共同组合而成的。 第一个概念是:应用组件(Components),它是整个应用的重要组成部分。 所以说,应用组件既可以包括应用运行所依赖的服务:比如 MySQL 数据库,也包括应用服务本身:比如拥有多个副本的 PHP 服务器。 开发者可以把他们写的代码“打包”成一个应用组件,然后编写配置文件来描述该组件与其他服务之间的关系。 应用组件的概念,让平台架构师能够将应用分解成一个个可被复用的模块,这种模块化封装应用组成部分的思想,代表了一种构建安全、高可扩展性应用的最佳实践:它通过一个完全分布式的架构模型,实现了应用组件描述和实现的解耦。
二、应用部署配置文件(Application Configuration)
而为了将这些应用组件描述变成一个真正运行起来的应用,应用运维人员会通过一个专门的、包含了所有应用组件信息的部署配置文件来实例化这个待运行的应用。 这个配置文件本身也是 OAM 规范中的一个声明式 API,用来让应用运维人员能够根据开发者或者平台提交的应用描述,实例化出对应的、真正运行起来的应用。
三、应用运维特征(Traits)
最后一个概念是一组应用运维特征(Traits) ,它们描述了应用在具体部署环境中的运维特征,比如应用的水平扩展的策略和 Ingress 规则,这些特征对于应用的运维来说非常重要,但它们在不同的部署环境里却往往有着截然不同的实现方式。 举一个简单例子,同样是 Ingress,它在公有云上和本地数据中心的实现可能是完全不同的:前者一般是 SLB 这样的云服务,而后者则可能是一个专门的硬件。这也就意味着针对这两个环境的 Ingress 运维工作,将会有天壤之别。 但与此同时,无论是在哪个环境里,这个 Ingress 规则对于应用开发人员来说,可能是完全相同的。 应用特征的设计,让这种关注点分离成为可能:只要这两个环境在 OAM 模型下提供了对 Ingress 这个应用运维特征的实现,那么你的应用就可以使用统一的 Ingress 规则描述无差别的在这两个地方运行起来。而与此同时,这两个环境的基础设施供应商可以继续通过配置这些应用特征的实现,来满足它们各自的运维要求(例如:不同环境里 Ingress 实现在满足合规性和安全性上的差异)
OAM:平台无关、高可扩展的应用描述能力
与 PaaS 应用模型相比,OAM 有很多独有的特点,其中最重要一点是:平台无关性。虽然我们目前发布的 OAM 实现(rudr)是基于 Kubernetes 的,但 Open Application Model 与 Kubernetes 并没有强耦合。实际上 ,OAM 可以实现到任意平台或运行环境之上,这当然也包括边缘计算与物联网的场景。我们也认同Kubernetes 在很多运行环境中可能并不是最好的选择,或者是像 Serverless 这类用户并不需要关心基础设施复杂性的运行环境。在这些场景下,OAM 都可以提供完全一致的应用管理体验。
第二个重要的特点是,OAM 的 specification (OAM 规范) 在设计上天然是可扩展的。OAM 不像 PaaS 那样自成封闭体系,也不会通过某种独有的应用管理环境来屏蔽掉底层平台的特点(比如:在 Kubernetes 之上”盖一个大帽子“)。 相反,OAM 使平台层可以通过应用特征系统 (Trait system)来体现平台的特性和差异性。也就是说,只要不同的平台都能够提供应用所需要的某些应用特征 (Trait),开发人员就能轻松地研发跨平台的应用。类似地,哪怕最底层的硬件提供商,也可以通过应用特征系统来体现其平台特性。 OAM 的整体设计,就是为了避免在平台可移植性中经常发生的“最小公分母”锁定问题。相反,OAM 不但提供了可移植性的能力,它还确保了每个平台有能力去透出独有的特性和用途。 OAM 让开发人员可以自由地针对不同平台以标准方式在可移植性和差异化功能之间取得平衡。
开放的社区与未来
如今,开放应用模型以及相应的 Kubernetes 实现有了初步的成果,我们感到非常兴奋。 OAM 规范是基于 Open Web Foundation 协议进行开发的。我们的目标,从一开始就是让开放应用模型 Open Application Model 成为中立基金会的项目,以便实现开放治理与广泛合作。如果您想了解更多信息,请前往开放应用模型项目的GitHub 仓库: OAM specification ,以及 基于 Kubernetes 的 OAM 标准实现  Rudr 。
今天 OAM 项目的发布只是迈出的一小步。我们非常期待得到您的反馈,并与大家密切协作,针对 Kubernetes 和任意云环境打造一个简单、可移植、可复用的应用模型。
点击阅读原文直达OAM主页: https://oam.dev/ “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”
云计算
2019-10-17 11:06:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> 作者: 马若飞,lead software engineer in FreeWheel,《Istio实战指南》作者,ServiceMesher社区管委会成员。
前言
近两年随着微服务架构的流行,服务网格(Service Mesh)技术受到了越来越多的人关注,并拥有了大批的拥趸。目前市面上比较成熟的开源服务网格主要有下面几个:Linkerd,这是第一个出现在公众视野的服务网格产品,由Twitter的finagle库衍生而来,目前由Buoyant公司负责开发和维护;Envoy,Lyft开发并且是第一个从CNCF孵化的服务网格产品,定位于通用的数据平面或者单独作为Sidecar代理使用;Istio,由Google、IBM、Lyft联合开发的所谓第二代服务网格产品,控制平面的加入使得服务网格产品的形态更加完整。
服务网格技术作为构建云原生应用的重要一环,逐渐的被越来越多的人和厂商认可,并看好它的发展前景。在Istio大红大紫的今天,作为和Google在云服务市场竞争的Amazon来说,自然不愿错失这块巨大的蛋糕。他们在今年4月份发布了自己的服务网格产品:AWS App Mesh。本文会聚焦于Istio和App Mesh这两个产品,通过横向的对比分析让大家对它们有一个更深入的认识。
概念
产品定位
从官方的介绍来看,Istio和App Mesh都比较明确的表示自己是一种服务网格产品。Istio强调了自己在连接、安全、控制和可视化4个方面的能力;而App Mesh主要强调了一致的可见性和流量控制这两方面能力,当然也少不了强调作为云平台下的产品的好处:托管服务,无需自己维护。
从某种程度上讲,Istio是一个相对重一点的解决方案,提供了不限于流量管理的各个方面的能力;而App Mesh是更加纯粹的服务于运行在AWS之上的应用并提供流控功能。笔者认为这和它目前的产品形态还不完善有关(后面会具体提到)。从与AWS内部开发人员的沟通中可以感觉到,App Mesh应该是一盘很大的棋,目前只是初期阶段而已。
核心术语
和AWS里很多产品一样,App Mesh也不是独创,而是基于Envoy开发的。AWS这样的闭环生态必然要对其进行改进和整合。同时,也为了把它封装成一个对外的服务,提供适当的API接口,在App Mesh这个产品中提出了下面几个重要的技术术语,我们来一一介绍一下。 服务网格(Service mesh):服务间网络流量的逻辑边界。这个概念比较好理解,就是为使用App mesh的服务圈一个虚拟的边界。 虚拟服务(Virtual services):是真实服务的抽象。真实服务可以是部署于抽象节点的服务,也可以是间接的通过路由指向的服务。 虚拟节点(Virtual nodes):虚拟节点是指向特殊工作组(task group)的逻辑指针。例如AWS的ECS服务,或者Kubernetes的Deployment。可以简单的把它理解为是物理节点或逻辑节点的抽象。 Envoy:AWS改造后的Envoy(未来会合并到Envoy的官方版本),作为App Mesh里的数据平面,Sidecar代理。 虚拟路由器(Virtual routers):用来处理来自虚拟服务的流量。可以理解为它是一组路由规则的封装。 路由(Routes):就是路由规则,用来根据这个规则分发请求。
上面的图展示了这几个概念的关系:当用户请求一个虚拟服务时,服务配置的路由器根据路由策略将请求指向对应的虚拟节点,这些节点本质上是AWS里的EKS或者ECS的节点。
那么这些App Mesh自创的术语是否能在Istio中找到相似甚至相同的对象呢?我归纳了下面的表格来做一个对比:
App Mesh Istio 服务网格(Service mesh) Istio并未显示的定义这一概念,我们可以认为在一个集群中,由Istio管理的服务集合,它们组成的网络拓扑即是服务网格。
虚拟服务(Virtual services) Istio中也存在虚拟服务的概念。它的主要功能是定义路由规则,使请求可以根据这些规则被分发到对应的服务。从这一点来说,它和App Mesh的虚拟服务的概念基本上是一致的。
虚拟节点(Virtual nodes) Istio没有虚拟节点的概念,可以认为类似Kubernetes里的Deployment。
虚拟路由器(Virtual routers)
路由(Routes)
Istio也没有虚拟路由器的概念。
Istio中的目标规则(DestinationRule)和路由的概念类似,为路由设置一些策略。从配置层面讲,其中的子集(subset)和App Mesh路由里选择的目标即虚拟节点对应。但Istio的目标规则更加灵活,也支持更多的路由策略。
从上面的对比看出,App Mesh目前基本上实现了最主要的流量控制(路由)的功能,但像超时重试、熔断、流量复制等高级一些的功能还没有提供,有待进一步完善。
架构
AWS App Mesh是一个商业产品,目前还没有找到架构上的技术细节,不过我们依然可以从现有的、公开的文档或介绍中发现一些有用的信息。
从这张官网的结构图中可以看出,每个服务的橙色部分就是Sidecar代理:Envoy。而中间的AWS App Mesh其实就是控制平面,用来控制服务间的交互。那么这个控制平面具体的功能是什么呢?我们可以从今年的AWS Summit的一篇PPT中看到这样的字样: 控制平面用来把逻辑意图转换成代理配置,并进行分发。
熟悉Istio架构的朋友有没有觉得似曾相识?没错,这个控制平面的职责和Pilot基本一致。由此可见,不管什么产品的控制平面,也必须具备这些核心的功能。
那么在平台的支持方面呢?下面这张图展示了App Mesh可以被运行在如下的基础设施中,包括EKS、ECS、EC2等等。当然,这些都必须存在于AWS这个闭环生态中。
而Istio这方面就相对弱一些。尽管Istio宣称是支持多平台的,但目前来看和Kubernetes还是强依赖。不过它并不受限于单一的云平台,这一点有较大的优势。
从可观测性来看,App Mesh依然发挥了自家生态的优势,可以方便的接入CloudWatch、X-Ray对服务进行观测。另外,App Mesh也提供了更大的灵活性,可以在虚拟节点里配置服务后端(可以是虚拟服务或者ARN),流量可以出站到这些配置的服务。这一点来说,和Istio的Mixer又有了异曲同工之妙。Mixer通过插件方式为Istio提供了极大的可扩展性,App Mesh在这一点上也不算落下风。
Istio的架构大家都非常熟悉了,这里就不再赘述了,感兴趣的同学可以直接去 官网 查看。
功能与实现方式
部署
Istio部署后类似一个网一样附着在你的Kubernetes集群上, 控制平面会使用你设置的资源;而App Mesh是一种托管方式,只会使用Envoy代理。完整安装后的Istio需要添加50个左右的CRD,而App Mesh只添加了3个CRD: meshes.appmesh.k8s.aws , virtualnodes.appmesh.k8s.aws 和 virtualservices.appmesh.k8s.aws 。这一点也反映出了功能上的区别。
流量控制
尽管两者的数据平面都是基于Envoy,但它们提供的流量控制能力目前还是有比较大的差距的。在路由的设置方面,App Mesh提供了相对比较丰富的匹配策略,基本能满足大部分使用场景。下面是App Mesh控制台里的路由配置截图,可以看出,除了基本的URI前缀、HTTP Method和Scheme外,也支持请求头的匹配。
Istio的匹配策略更加完善,除了上面提到的,还包括HTTP Authority,端口匹配,请求参数匹配等,具体信息可以从官方文档的虚拟服务 设置 查看。下面两段yaml分别展示了两个产品在虚拟服务配置上的差异。
App Mesh配置: apiVersion: appmesh.k8s.aws/v1beta1 kind: VirtualService metadata: name: my-svc-a namespace: my-namespace spec: meshName: my-mesh routes: - name: route-to-svc-a http: match: prefix: / action: weightedTargets: - virtualNodeName: my-app-a weight: 1
Istio配置: apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: ratings-route spec: hosts: - ratings.prod.svc.cluster.local http: - match: - headers: end-user: exact: jason uri: prefix: "/ratings/v2/" ignoreUriCase: true route: - destination: host: ratings.prod.svc.cluster.local
另外一个比较大的不同是,App Mesh需要你对不同版本的服务分开定义(即定义成不同的虚拟服务),而Istio是通过目标规则 DestinationRule 里的子集 subsets 和路由配置做的关联。本质上它们没有太大区别。
除了路由功能外,App Mesh就显得捉襟见肘了。就在笔者撰写本文时,AWS刚刚添加了重试功能。而Istio借助于强大的Envoy,提供了全面的流量控制能力,如超时重试、故障注入、熔断、流量镜像等。
安全
在安全方面,两者的实现方式具有较大区别。默认情况下,一个用户不能直接访问App Mesh的资源,需要通过AWS的 IAM策略 给用户授权。比如下面的配置是容许用户用任意行为去操作网格内的任意资源: { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "appmesh:*" ], "Resource": "*" } ] }
而虚拟节点间的授权方面,App Mesh目前只有TLS访问的支持,且仅仅是预览版(Preview)并未正式发布。下面的配置展示了一个虚拟节点只容许 tls 方式的访问: { "meshName" : "app1", "spec" : { "listeners" : [ { "portMapping" : { "port" : 80, "protocol" : "http" }, "tls" : { "mode" : "STRICT", "certificate" : { "acm" : { "certificateArn" : "arn:aws:acm:us-west-2:123456789012:certificate/12345678-1234-1234-1234-123456789012" } } } } ], "serviceDiscovery" : { "dns" : { "hostname" : "serviceBv1.mesh.local" } } }, "virtualNodeName" : "serviceBv1" }
而Istio中端到端的认证是支持mTLS的,同时还支持JWT的用户身份认证。下面的配置分别展示了这两种认证方式: apiVersion: "authentication.istio.io/v1alpha1" kind: "Policy" metadata: name: "reviews" spec: targets: - name: reviews peers: - mtls: {} origins: - jwt: issuer: "https://accounts.google.com" jwksUri: "https://www.googleapis.com/oauth2/v3/certs" trigger_rules: - excluded_paths: - exact: /health
Istio的授权是通过RBAC实现的,可以提供基于命名空间、服务和HTTP方法级别的访问控制。这里就不具体展示了,大家可以通过官网 文档 来查看。
可观察性
一般来说,可以通过三种方式来观察你的应用:指标数据、分布式追踪、日志。Istio在这三个方面都有比较完整的支持。指标方面,可以通过Envoy获取请求相关的数据,同时还提供了服务级别的指标,以及控制平面的指标来检测各个组件的运行情况。通过内置的Prometheus来收集指标,并使用Grafana展示出来。分布式追踪也支持各种主流的OpenTracing工具,如Jaeger、Zipkin等。访问日志一般都通过ELK去完成收集、分析和展示。另外,Istio还拥有Kiali这样的可视化工具,给你提供整个网格以及微服务应用的拓扑视图。总体来说,Istio在可观察方面的能力是非常强大的,这主要是因为Mixer组件的插件特性带来了巨大的灵活性。
App Mesh在这方面做的也不错。在如下图虚拟节点的配置中可以看到,你可以配置服务的后端基础设施,这样流量就可以出站到这些服务。同时,在日志收集方面,也可以配置到本地日志,或者是其他的日志系统。
另一方面,AWS又一次发挥了自己闭环生态的优势,提供了App Mesh与自家的CloudWatch、X-Ray这两个监控工具的整合。总的来说,App Mesh在可观察性上也不落下风。
总结
AWS App Mesh作为一个今年4月份才发布的产品,在功能的完整性上和Istio有差距也是情有可原的。从App Mesh的 Roadmap 可以看出,很多重要的功能,比如熔断已经在开发计划中。以笔者与AWS的开发人员了解的信息来看,他们还是相当重视这个产品,优先级很高,进度也比较快,之前还在预览阶段的重试功能在上个月也正式发布了。另外,App Mesh是可以免费使用的,用户只需要对其中的实例资源付费即可,没有额外费用。App Mesh一部分的开发重点是和现有产品的整合,比如Roadmap列出的使用AWS Gateway作为App Mesh的Ingress。借助着自己的生态优势,这种整合即方便快捷的完善了App Mesh,同时又让生态内的产品结合的更紧密,使得闭环更加的牢固,不得不说是一步好棋。
和App Mesh目前只强调流控能力不同,Istio更多的是把自己打造成一个更加完善的、全面的服务网格系统。架构优雅,功能强大,但性能上受到质疑。在产品的更迭上貌似也做的不尽如人意(不过近期接连发布了1.3到1.3.3版本,让我们对它的未来发展又有了期待)。Istio的优势在于3大顶级技术公司加持的强大资源,加上开源社区的反哺,控制好的话容易形成可持续发展的局面,并成为下一个明星级产品。但目前各大厂商都意识到了网格的重要性并推出自己的产品(AWS App Mesh,Kong的Kuma等),竞争也会逐渐激烈。未来是三分天下还是一统山河,让我们拭目以待。
参考 what is app mesh aws app mesh roadmap Redefining application communications with AWS App Mesh istio offical
关于 ServiceMesher 社区
ServiceMesher 社区是由一群拥有相同价值观和理念的志愿者们共同发起,于 2018 年 4 月正式成立。
社区关注领域有:容器、微服务、Service Mesh、Serverless,拥抱开源和云原生,致力于推动 Service Mesh 在中国的蓬勃发展。
社区官网: https://www.servicemesher.com
云计算
2019-10-17 10:20:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> 下周六,深圳,阔别已久的线下技术沙龙要和你见面啦!
现场有Rancher Labs研发经理demo刚刚发布的Rancher 2.3中的Istio、Windows容器、集群模板等功能及使用,还有k3s首次线下workshop,由Rancher Labs资深架构师带你一起玩转k3s!还有长城证券的运维负责人分享数字化浪潮下传统金融IT的转型。
访问链接 即可了解详情及报名啦!
诸如容器、Kubernetes等云原生架构和技术的成熟推动了服务网格架构的极速增长以及广泛采用。尽管云原生环境可以为企业带来一系列好处,但是其复杂性也对负责开发维护这类系统的人员,如软件开发人员、网络运维人员、基础架构工程师以及CIO、CTO等带来了重大挑战。
服务网格框架能够为跨不同云原生环境的应用程序整合一致的服务和网络管理能力,它还极大地加快了DevOps实践的进程,正缘于此,服务网格近年来可谓是发展迅猛。云原生普及的加快,要求拥有云原生应用程序的工程团队必须熟悉服务网格功能,以判断该技术将来是否能为企业提供价值。
什么是服务网格?
服务网格可以连接、保护、控制以及监控在编排平台上的服务。“服务网格”这一术语本身用于分布式应用程序中服务之间的一组搭接网络连接,也适用于管理该组连接服务的一系列工具。如果你有两个通过网络连接进行交互的微服务,那就意味着你有了一个服务网格。下图是一个十分简单的示例,一个网格和两个服务:
更有可能的是,由于在环境中微服务的数量会继续增长,你的服务网格会如下图所示:
随着云环境扩展到混合云和多云部署,开发人员将会使用微服务来加速开发并且确保在多个容器和分布式云资源中的的可移植性。随着微服务生态系统的复杂性增长,我们需要高效且智能地管理它,并且深入了解微服务如何交互以及保护微服务之间的通信。
什么是Istio?
如果你已经听说了服务网格,那么你一定顺带听说了Istio。Istio是一个开源的服务网格,它可以部署在已有的云原生应用程序上。它还具有类似于平台的功能——可以将集成到日志平台、遥测或策略系统中。策略集成使得Istio在创建一个统一的方法来保护、连接以及监控既定环境中的微服务中扮演一个安全工具的角色。当泛指“Istio服务网格”时,通常是指Istio中的一系列工具,而特指“某个Istio 服务网格”时则表明由Istio安装管理的指定应用程序集群。Istio的许多CRD允许对应用程序网络层的行为进行编程配置(通过使用Kubernetes API),其中应用程序是相互依赖的微服务集。Istio在某种程度上可以称为当今云原生堆栈中服务网格的同义词,因为它的功能最丰富、最标准化。
我是否需要一个服务网格?
尽管服务网格的采用率可能会持续快速增长,特别是当功能设置和类似Istio的管理工具进一步完善之后,但并不是每个云原生环境都需要服务网格。所以你如何知道一个服务是否适合你的企业或者环境呢?如果你需要解决下面所描述的一个或多个需求或问题的方案,那么你应该考虑部署一个服务网格: 你在基于分布式微服务的应用程序中遇到性能问题 你需要为所有微服务收集并交付一致的请求和连接指标 你想直接默认在线加密设置,而无需直接管理TLS证书 你需要比Kubernetes网络策略提供的更细粒度的解决方案进行服务到服务的控制 你想使用金丝雀发布和应用程序API多版本支持进行自动release 你想无需修改应用程序就可以添加用户的身份验证和授权认证信息
另一方面,如果在你的堆栈中不需要服务网格,那么你需要做一些权衡。考虑到这些环境的复杂性,部署一个服务网格(包括Istio)需要大量的迁移工作和运维成本。如果你的微服务部署数量不会增长,或者如果有其他解决方案可以满足你内部的HTTP请求路由的需求,或者如果你已经有了一个可管理且高效的解决方案可以解决上述的关键需求,那么此刻服务网格对你来说真的不是一个最佳选择。
但是如果服务网格继续极速被广泛采用,为支持它而开发的功能生态系统将会继续扩展。这种增长将提升可管理性和功能性,以便将来DevOps团队可以更加轻松地访问更强大的服务网格工具,而不必担心将新的基础架构层部署到云原生堆栈中而出现棘手的问题或花费很高的成本。
Istio工作原理
Istio组件被分为两部分——控制平面和数据平面。控制平面是指管理配置和监控数据平面的服务。数据平面由作为sidecar由在应用程序pod中的智能代理(proxy)组成,这是Kubernetes对象模型中最小的可部署对象。这些Istio proxy有助于控制和监控微服务间的网络连接。从控制平面接收路由和策略规则,然后数据平面报告回连接处理遥测。
通过创建Kubernetes资源来配置Istio服务网格。此外,有许多Kubernetes CRD可以映射到Istio各种功能上。接下来,我们会讨论更多关于控制和数据平面的作用,但在此之前我们先了解关于Istio的潜在能力,以及它的不足。
潜力与不足
Istio通过其可动态配置代理的网格提供了一系列用于处理和控制网络连接的特性。但这些功能配置繁重并且拥有陡峭的学习曲线。并且有时把已有的应用程序迁移到Istio架构时依旧会出现一些常见的问题,尽管这些架构已经是Kubernetes原生的微服务。
此外,Istio缺乏对如何将用户提供的配置转换为Envoy路由的了解。Envoy是作为服务网格中服务的入站和出站流量的中介开发的一种高性能的代理,是由来自共享出行服务公司Lyft的开发人员创建的,可以用于从单体架构转变为服务网格架构。其他在使用中的问题还包括部署和服务资源配置要求所需的学习曲线、在打开mTLS时中断Kubernetes readiness和liveness探针以及使用没有ClusterIP的Kubernetes服务或绕开Kubernetes服务发现流程的服务。
Istio的优势在于可以让你在不修改微服务源代码的情况之下,很轻松地给微服务加上诸如负载均衡、身份验证、监控等等的功能。而且目前它正在快速发展迭代,频繁发布新版本,并且积极征求用户反馈。尽管目前Envoy还有很多局限,但是随着Istio持续发展,它也会积极开发和完善自己的功能。
配置控制平面
在Kubernetes集群中,一个典型的Istio部署应该包含以下服务:
Pilot,在Istio网络自定义资源中集合流量管理规范配置,并将该配置交付到istio-proxy sidecar。
Mixer,用于处理由proxy sidecar生成的请求指标的遥测,并将其发送到已配置完成的后端,并执行授权策略。如果开启了策略检查(Istio 1.1中默认关闭),proxy sidecar将会连接到Mixer以确认连接是被允许的。但是,这个方法会稍微增加网络延迟。
Citadel,这个是Istio的公钥基础设施(PKI)服务,它可以生成、轮换和吊销用于身份验证的客户端TLS证书。
Galley,它是大多数Istio CRD的Kubernetes controller,使用户可以更改自定义资源并将内容分配到其他Istio服务中。
数据平面
数据平面由Envoy服务代理提供支持,该代理使用Istio扩展构建。Proxy会拦截到pod服务端口的传入流量,并默认拦截来自pod其他容器的所有创出TCP流量。在大部分情况下,无需更改应用程序代码,仅对应用程序的Kubernetes部署和服务资源规范进行较小的更改,proxy sidecar 就可以在pod中运行。Proxy sidecar的配置由在Istio 控制面板中的服务进行动态管理。
最终,也许会在某个时间点你需要部署服务网格以确保你的云原生环境完全正常运行并得到充分保护。因此,熟悉有关服务网格的基础只是将可以帮助你做出准确的判断——什么时候应该部署服务网格以及应该如何部署。如果你正在计划在Kubernetes和其他容器平台上进行扩展计划,那么你通过了解Istio的设计和功能以及它如何降低容器化微服务和云原生环境的固有复杂性,你可以知道Istio是一个功能强大且快速改进的解决方案并且正在积极增强弹性伸缩能力、安全性以及管理的简易性。
如果企业继续采用云原生和分布式架构,那么Istio的服务网格功能以及底层基础架构的网络控制和Kubernetes的安全实践将会极大程度解放DevOps团队在弹性伸缩和管理应用程序基础架构上的压力。
在10月9日 GA的Rancher 2.3版本 中,正式集成了Istio,极大简化了Istio的安装和配置。你只需要在UI中使用工具菜单,即可启动Istio。Rancher中现已内置支持: 用于流量和遥测可视化的Kiali仪表板 用于追踪的Jaeger 用于监控和可观察性的Prometheus和Grafana
如果你还想了解更多关于Rancher 2.3的新功能,欢迎参加我们在下周六(10月26日)举办的技术沙龙,坐标深圳。届时将有Rancher Labs大中华区的研发经理现场介绍并demo Rancher 2.3的新功能, 点击此处 ,赶紧报名啦! 欢迎添加小助手(wx:rancher2),进官方技术群,了解更多Kubernetes使用攻略
云计算
2019-10-17 10:10:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
在vShpere中,访问ESXi主机的途径很多,如下: ESXi DCUI ESXi Shell ESXi SSH ESXi Host Client vCenter --> vSphere web client / vSphere Client
VMware vSphere ESXi主机的访问控制,除了强制要求使用https加密连接之外,还可以关闭DCUI通道,设置为Normal Lockdown(普通锁定)或Strict Lockdown。
大别阿郎,原名张瑞旗,河南光山县人,广东省作家协会会员。1994年焦作矿业学院英语专业毕业,获西悉尼大学工商管理硕士学位。做过翻译、秘书、销售、程序员、服务器管理员;创办公司13载;创作了长篇小说《神级宅男网管》《枪手》。出版过非虚构文学作品《从大别山到修水河》《午后三点的乡愁》《请与我同框》。2018年根据自己的长篇小说《行辘》改编的同名电影剧本获中国首届工业文学作品大赛推荐作品奖。现在广州腾科任红帽培训讲师。
云计算
2019-10-16 17:58:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
来源:Redislabs
作者:Martin Forstner
翻译:Kevin (公众号:中间件小哥)
以我的经验,将某些应用拆分成更小的、松耦合的、可协同工作的独立逻辑业务服务会更易于构建和维护。这些服务(也被称为微服务)各自管理自己的技术栈,因此很容易独立于其他服务进行开发和部署。前人已经总结了很多关于使用这种架构设计的好处,在此我就不再赘述了。关于这种设计,有一个方面我一直在重点关注,因为如果没有它,将会导致一些有趣的挑战。虽然构建松耦合的微服务是一个非常轻量级和快速的开发过程,但是这些服务之间共享状态、事件以及数据的通信模型却不那么简单。我使用过的最简单的通信模型就是服务间直接通信,但是这种模型被 Fernando Dogio 明确地证明一旦服务规模扩大就会失效,会导致服务崩溃、重载逻辑以及负载增加等问题,从而可能引起的巨大的麻烦,因此应该尽量避免使用这种模型。还有一些其他通信模型,比如通用的发布/订阅模型、复杂的 kafka 事件流模型等,但是最近我在使用 Redis 构建微服务间的通信模型。

拯救者 Redis!
微服务通过网络边界发布状态,为了跟踪这种状态,事件通常需要被保存在事件存储中。由于事件通常是一种异步写入操作的不可变流的记录(又被称为事务日志),因此适用于以下场景:
1. 顺序很重要(时间序列数据)
2. 丢失一个事件会导致错误状态
3. 回放状态在任何给定时间点都是已知的
4. 写操作简单且快捷
5. 读操作需要更多的时间,以至于需要缓存
6. 需要高可扩展性,服务之间都是解耦的,没有关联
使用 Redis,我始终可以轻松实现发布-订阅模式。但现在,Redis 5.0 提供了新的Streams 数据类型,我们可以以一种更加抽象的方式对日志数据结构进行建模-使之成为时间序列数据的理想用例(例如最多一次或最少一次传递语义的事务日志)。基于双主功能,轻松简单的部署以及内存中的超快速处理能力,Redis 流成为一种管理大规模微服务通信的必备工具。基本的模型被称为命令查询职责分离(CQRS),它将命令和查询分开执行,命令使用 HTTP 协议,而查询采用 RESP(Redis 序列化协议)。让我们使用一个例子来说明如何使用 Redis 作为事件存储。

OrderShop简单应用概述
我创建了一个简单但是通用的电子商务应用作为例子。当创建/删除客户、库存物品或订单时,使用 RESP 将事件异步传递到 CRM 服务,以管理 OrderShop 与当前和潜在客户的互动。像许多常见应用程序的需求一样,CRM 服务可以在运行时启动和停止,而不会影响其他微服务。这需要捕获在其停机期间发送给它的所有消息以进行后续处理。
下图展示了 9 个解耦的微服务的互连性,这些微服务使用由 Redis 流构建的事件存储进行服务间通信。他们通过侦听事件存储(即 Redis 实例)中特定事件流上的任何新创建的事件来执行此操作。
图1. OrderShop 架构

我们的 OrderShop 应用程序的域模型由以下 5 个实体组成: 顾客 产品 库存 订单 账单
通过侦听域事件并保持实体缓存为最新状态,事件存储的聚合功能仅需调用一次或在响应时调用。
图2. OrderShop 域模型

安装并运行OrderShop
按照如下步骤安装并运行 OrderShop 应用:
1. 从这里下载代码仓库:
https://github.com/Redislabs-Solution-Architects/ordershop
2. 确保已经安装了 Docker Engine和Docker Compose
3. 安装 Python3:
https://python-docs.readthedocs.io/en/latest/starting/install3/osx.html
4. 使用 docker-compose up启动应用程序
5. 使用 pip3 install -r client / requirements.txt 安装需求
6. 然后使用 python3 -m unittest client / client.py 执行客户端
7. 使用 docker-compose stop crm-service 停止 CRM 服务
8. 重新执行客户端,您会看到该应用程序正常运行,没有任何错误

深入了解
以下是来自 client.py 的一些简单测试用例,以及相应的 Redis 数据类型和键。

我选择流数据类型来保存这些事件,因为它们背后的抽象数据类型是事务日志,非常适合我们连续事件流的用例。我选择了不同的键来分配分区,并决定为每个流生成自己的条目 ID,ID 包含秒“-”微秒的时间戳(为了保持 ID 的唯一,并保留了键/分区之间事件的顺序)。我选择集合来存储 ID(UUID),并选择列表和哈希来对数据建模,因为它反映了它们的结构,并且实体缓存只是域模型的简单投影。

结论
Redis 提供的各种数据结构-包括集合,有序集合,哈希,列表,字符串,位数组,HyperLogLogs,地理空间索引以及现在的流-可以轻松适应任何数据模型。流包含的元素不仅是单个字符串,而且是由字段和值组成的对象。范围查询速度很快,并且流中的每个条目都有一个 ID,这是一个逻辑偏移量。流提供了针对时间序列等应用的解决方案,并可为其他应用提供流消息,例如,替换需要更高可靠性的通用发布/ 订阅应用程序,以及其他全新的应用。
您可以通过分片(聚集多个实例)来扩展 Redis 实例并提供容灾恢复的持久性选项,所以 Redis 可以作为企业级应用的选择。
更多优质中间件技术资讯/原创/翻译文章/资料/干货,请关注“中间件小哥”公众号!
云计算
2019-10-16 17:40:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
自从我接触 云桌面 以来,听到和看到最多的就是云桌面是如何如何的好,特别是这几年来在传统PC销量下滑的情况下,更是被很多人说云桌面才是未来办公的首要选择,这究竟是一些厂家在夸大云桌面的功能来达到宣传的效果,还是说它确实有一些独到之处的。看完以下这些就知道它到底有没有被夸大的了。
首先桌面性能,说到云桌面我们不得不先来介绍下它的桌面性能的,虽然说很多情况下连接显示器的云终端配置都不高,但是云终端在很多情况下是不进行计算的,而是通过调用服务器的资源使用,通过充分调用服务器的资源使得虚拟桌面达到i3、i5甚至i7配置电脑的速度,完美适应办公、教学、播放高清视频等应用场景。同时云终端采用全闪存SSD设计不安装操作系统,开机速度比PC更快的。
其次维护难度,传统的PC配置是很强大,但是随着使用年限和数量的增加,管理和维护起来是比较麻烦的,而云桌面的维护难度相对来说就比较简单了,不管是软件还是硬件的维护都会比PC简单方便很多的,硬件方面云终端故障直接换新即可,而系统软件方面,数据和计算都在服务器上进行,所以我们只需要管理和维护好服务器即可,终端不需要进行系统维护的。
第三使用成本,相比于传统PC来说,云桌面的硬件组成上虽然多了服务器,但是云终端成本不到PC的一半,虽然多了服务器但是评价下来单台的成本是不会比购买PC高的,同时云终端低功耗只有5W功率和免维护的特点,在使用过程中可以节省90%的用电成本和95%的用电成本。而这些都是使用传统PC无法相比的。
最后应用场景,虽然说当前云桌面还不能像传统PC的应用场景这么的广泛,但是它所涉及的应用场景正在慢慢的增加并且是越来越广的,学校计算机教室、企业办公、工厂车间办公、医院等各个行业几乎只要是要用到电脑的地方,云桌面都是可以应用的,并且随着技术的发展它的应用场景还在继续增加的。
而正是因为云桌面可以达到传统PC一样的运行速度,又比传统PC管理维护更简单和更节省成本以及一样广泛的应用场景,所以说云桌面确实有它的独到之处的而不是被夸大的。
来源禹龙云
云计算
2019-10-16 16:28:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
随着社会的进步与技术的发展,人们对资源的高效利用有了更为迫切的需求。近年来,互联网、移动互联网的高速发展与成熟,大应用的微服务化也引起了企业的热情关注,而基于Kubernetes+Docker的容器云方案也随之进入了大众的视野。开普勒云是一个基于Kubernetes+Docker+Istio的微服务治理解决方案。
一、Microservices
1.1 解决大应用微服务化后的问题
现在各大企业都在谈论微服务,在微服务的大趋势之下技术圈里逢人必谈微服务,及微服务化后的各种解决方案。
1.2 当我们在讨论微服务的时候我们在讨论什么?
使用微服务架构有很多充分的理由,但天下没有免费的午餐,微服务虽有诸多优势,同时也增加了复杂性。团队应该积极应对这种复杂性,前提是应用能够受益于微服务。
1.2.1 如何微服务化的问题 微服务要如何拆分 业务API规则 数据一致性保证 后期可扩展性考虑
当然这不是本文主要讨论的问题,我不讲微服务具体要如何拆分,每个企业每个应用的情况都不太一样,适合自己的方案就是最好的拆分方案。我们主要来解决微服务化后所带来的一些问题。
1.2.2 微服务化后带来的问题 环境一致性 如何对资源快速分配 如何快速度部署 怎么做基本监控 服务注册与发现 负载均衡如何做
以上都是大应用微服务化所需要解决的基础问题,如果还按照传统的方式使用虚拟机来实现,资源开支将会非常大。那么这些问题要怎么解决呢?比如: 流量管理 服务降级 认证、授权
当然面对上述这些问题我们广大的猿友们肯定是有解决方案的。
1.3 Service governance
1.3.1 Java 体系
假设我们是Java体系的应用,那解决起来就很方便了,比如我们可以考虑使用SpringCloud全家桶系列。也可以拆分使用: Eureka Hystrix Zuul Spring-cloud Spring-boot ZipKin
Java体系下能很方便的做以我们微服务化后的基础部分,但依然不能非常舒服地解决环境一致性,并且如果有其他语系的服务将很难融入进去。
我们来看基础编程语言一般有什么组合方式来解决基础问题。
1.3.2 其他体系 Consul Kong Go-kit Jaeger/Zipkin
假设我们是使用Golang语言,这里再捧一下Golang语言。go语言简直就是天生为微服务而生的语言,实在不要太方便了。高效的开发速度及相当不错的性能,简单精悍。
跑题了~我们使用上面这些工具也可以组成一套还不错的微服务架构。 Consul: 当作服务发现及配置中心来使 Kong: 作为服务网关 Jaeger: 作为链路追踪来使 Go-kit: 开发组件
但是这种方案也有问题,对服务的侵入性太强了,每个服务都需要嵌入大量代码,这还是很头疼的。
二、Docker & Kubernetes
基于Docker+k8s搭建平台的实践方案。
2.1 Docker
Docker 是一个非常强大的容器。 资源利用率的提升 环境一致性、可移植性 快速度扩容伸缩 版本控制
使用了Docker之后,我们发现可玩的东西变多了,更加灵活了。不仅仅是资源利用率提升、环境一致性得到了保证,版本控制也变得更加方便了。
以前我们使用Jenkins进行构建,需要回滚时,又需要重新走一次jenkins Build过程,非常麻烦。如果是Java应用,它的构建时间将会变得非常长。
使用了Docker之后,这一切都变得简单了,只需要把某个版本的镜像拉下来启动就完事了(如果本地有缓存直接启动某个版本就行了),这个提升是非常高效的。
(图片来源网络)
既然使用了Docker容器作为服务的基础,那我们肯定需要对容器进行编排,如果没有编排那将是非常可怕的。而对于Docker容器的编排,我们有多种选择:Docker Swarm、Apache Mesos、Kubernetes,在这些编排工具之中,我们选择了服务编排王者Kubernetes。
2.1.1 Docker VS VM
VM: 创建虚拟机需要1分钟,部署环境3分钟,部署代码2分钟。 Docker: 启动容器30秒内。
2.2 Why choose Kubernetes
我们来对比这三个容器编排工具。
2.2.1 Apache Mesos
Mesos的目的是建立一个高效可扩展的系统,并且这个系统能够支持各种各样的框架,不管是现在的还是未来的框架,它都能支持。这也是现今一个比较大的问题:类似Hadoop和MPI这些框架都是独立开的,这导致想要在框架之间做一些细粒度的分享是不可能的。
但它的基础语言不是Golang,不在我们的技术栈里,我们对它的维护成本将会增高,所以我们首先排除了它。
2.2.2 Docker Swarm
Docker Swarm是一个由Docker开发的调度框架。由Docker自身开发的好处之一就是标准Docker API的使用。Swarm的架构由两部分组成:
(图片来源网络)
它的使用,这里不再具体进行介绍。
2.2.3 Kubernetes
Kubernetes是一个Docker容器的编排系统,它使用label和pod的概念来将容器换分为逻辑单元。Pods是同地协作(co-located)容器的集合,这些容器被共同部署和调度,形成了一个服务,这是Kubernetes和其他两个框架的主要区别。相比于基于相似度的容器调度方式(就像Swarm和Mesos),这个方法简化了对集群的管理.
不仅如此,它还提供了非常丰富的API,方便我们对它进行操作,及玩出更多花样。其实还有一大重点就是符合我们的Golang技术栈,并且有大厂支持。
Kubernetes 的具体使用这里也不再过多介绍,网站上有大把资料可以参考。
2.3 Kubernetes in kubernetes
kubernetes(k8s)是自动化容器操作的开源平台,这些操作包括部署、调度和节点集群间扩展。 自动化容器的部署和复制 随时扩展或收缩容器规模 将容器组织成组,并且提供容器间的负载均衡 很容易地升级应用程序容器的新版本 提供容器弹性,如果容器失效就替换它,等等...
2.4 Kubernetes is not enough either
到这里我们解决了以下问题: Docker: 环境一致性、快速度部署。 Kubernetes: 服务注册与发现、负载均衡、对资源快速分配。
当然还有监控,这个我们后面再说。我们先来看要解决一些更高层次的问题该怎么办呢?
在不对服务进行侵入性的代码修改的情况下,服务认证、链路追踪、日志管理、断路器、流量管理、错误注入等等问题要怎么解决呢?
这两年非常流行一种解决方案:Service Mesh。
三、Service Mesh
处理服务间通信的基础设施层,用于在云原生应用复杂的服务拓扑中实现可靠的请求传递。 用来处理服务间通讯的专用基础设施层,通过复杂的拓扑结构让请求传递的过程变得更可靠。 作为一组轻量级高性能网络代理,和程序部署在一起,应用程序不需要知道它的存在。
在云原生应用中可靠地传递请求可能非常复杂,通过一系列强大技术来管理这种复杂性: 链路熔断、延迟感知、负载均衡,服务发现、服务续约及下线与剔除。
市面上的ServiceMesh框架有很多,我们选择了站在风口的Istio。
3.1 Istio
连接、管理和保护微服务的开放平台。 平台支持: Kubernetes, Mesos, Cloud Foundry。 可观察性:Metrics, logs, traces, dependency 。visualisation。 Service Identity & Security: 为服务、服务到服务的身份验证提供可验证的标识。 Traffic 管理: 动态控制服务之间的通信、入口/出口路由、故障注入。 Policy 执行: 前提检查,服务之间的配额管理。
3.2 我们为什么选择Istio?
因为有大厂支持~其实主要还是它的理念是相当好的。
虽然它才到1.0版本,我们是从 0.6 版本开始尝试体验,测试环境跑,然后0.7.1版本出了,我们升级到0.7.1版本跑,后来0.8.0LTS出了,我们开始正式使用0.8.0版本,并且做了一套升级方案。
目前最新版已经到了1.0.4, 但我们并不准备升级,我想等到它升级到1.2之后,再开始正式大规模应用。0.8.0LTS在现在来看小规模还是可以的。
3.3 Istio 架构
我们先来看一下Istio的架构。
其中Istio控制面板主要分为三大块,Pilot、Mixer、Istio-Auth。 Pilot: 主要作为服务发现和路由规则,并且管理着所有Envoy,它对资源的消耗是非常大的。 Mixer: 主要负责策略请求和配额管理,还有Tracing,所有的请求都会上报到Mixer。 Istio-Auth: 升级流量、身份验证等等功能,目前我们暂时没有启用此功能,需求并不是特别大,因为集群本身就是对外部隔离的。
每个Pod都会被注入一个Sidecar,容器里的流量通过iptables全部转到Envoy进行处理。
四、Kubernetes & Istio
Istio可以独立部署,但显然它与Kuberntes结合是更好的选择。基于Kubernetes的小规模架构。有人担心它的性能,其实经过生产测试,上万的QPS是完全没有问题的。
4.1 Kubernetes Cluster
在资源紧缺的情况下,我们的k8s集群是怎么样的?
4.1.1 Master集群 Master Cluster: ETCD、Kube-apiserver、kubelet、Docker、kube-proxy、kube-scheduler、kube-controller-manager、Calico、 keepalived、 IPVS。
4.1.2 Node节点 Node: Kubelet、 kube-proxy 、Docker、Calico、IPVS。
(图片来源网络)
我们所调用的Master的API都是通过 keepalived 进行管理,某一master发生故障,能保证顺滑的飘到其他master的API,不影响整个集群的运行。
当然我们还配置了两个边缘节点。
4.1.3 Edge Node 边缘节点 流量入口
边缘节点的主要功能是让集群提供对外暴露服务能力的节点,所以它也不需要稳定,我们的IngressGateway 就是部署在这两个边缘节点上面,并且通过Keeplived进行管理。
4.2 外部服务请求流程
最外层是DNS,通过泛解析到Nginx,Nginx将流量转到集群的VIP,VIP再到集群的HAproxy,将外部流量发到我们的边缘节点Gateway。
每个VirtualService都会绑定到Gateway上,通过VirtualService可以进行服务的负载、限流、故障处理、路由规则及金丝雀部署。再通过Service最终到服务所在的Pods上。
这是在没有进行Mixer跟策略检测的情况下的过程,只使用了Istio-IngressGateway。如果使用全部Istio组件将有所变化,但主流程还是这样的。
4.3 Logging
日志收集我们采用的是低耦合、扩展性强、方便维护和升级的方案。 节点Filebeat收集宿主机日志。 每个Pods注入Filebeat容器收集业务日志。
Filebeat会跟应用容器部署在一起,应用也不需要知道它的存在,只需要指定日志输入的目录就可以了。Filebeat所使用的配置是从ConfigMap读取,只需要维护好收集日志的规则。
上图是我们可以从Kibana上看到所采集到的日志。
4.4 Prometheus + Kubernetes 基于时间序列的监控系统。 与kubernetes无缝集成基础设施和应用等级。 具有强大功能的键值数据模型。 大厂支持。
4.4.1 Grafana
4.4.2 Alarm
目前我们支持的报警有Wechat、kplcloud、Email、IM。所有报警都可在平台上配置发送到各个地方。
4.4.3 整体架构
整个架构由外围服务及集群内的基础服务组成,外围服务有: Consul作为配置中心来使用。 Prometheus+Grafana用来监控K8s集群。 Zipkin提供自己定义的链路追踪。 ELK日志收集、分析,我们集群内的所有日志会推送到这里。 Gitlab代码仓库。 Jenkins用来构建代码及打包成Docker镜像并且上传到仓库。 Repository 镜像仓库。
集群有: HAProxy+keeprlived 负责流量转发。 网络是Calico, Calico对kube-proxy的ipvs代理模式有beta级支持。如果Calico检测到kube-proxy正在该模式下运行,则会自动激活Calico ipvs支持,所以我们启用了IPVS。 集群内部的DNS是 CoreDNS。 我们部署了两个网关,主要使用的是Istio的 IngressGateway,TraefikIngress备用。一旦IngressGateway挂了我们可以快速切换到TraefikIngress。 上面是Istio的相关组件。 最后是我们的APP服务。 集群通过Filebeat收集日志发到外部的ES。 集群内部的监控有: State-Metrics 主要用来自动伸缩的监控组件 Mail&Wechat 自研的报警服务 Prometheus+Grafana+AlertManager 集群内部的监控,主要监控服务及相关基础组件 InfluxDB+Heapster 流数据库存储着所有服务的监控信息
4.5 有了Kubernetes那怎么部署应用呢?
4.5.1 研发打包成镜像、传仓库、管理版本 学习Docker。 学习配置仓库、手动打包上传麻烦。 学习k8s相关知识。
4.5.2 用Jenkins来负责打包、传镜像、更新版本 运维工作增加了不少,应用需要进行配置、服务需要做变更都得找运维。 需要管理一堆的YAML文件。
有没有一种傻瓜式的,不需要学习太多的技术,可以方便使用的解决方案?
五、Kplcloud platform
5.1 开普勒云平台
开普勒云平台是一个轻量级的PaaS平台。 为微服务化的项目提供一个可控的管理平台。 实现每个服务独立部署、维护、扩展。 简化流程,不再需要繁琐的申请流程,最大限度的自动化处理。 实现微服务的快速发布、独立监控、配置。 实现对微服务项目的零侵入式的服务发现、服务网关、链路追踪等功能。 提供配置中心,统一管理配置。 研发、产品、测试、运维甚至是老板都可以自己发布应用。
5.2 在开普勒平台部署服务
为了降低学习成本及部署难度,在开普勒平台上部署应用很简单,只需要增加一个Dockerfile 就好了。
Dockerfile 参考:
以上是普通模式,Jenkins代码Build及Docker build。
这是一种相对自由的部署方式,可以根据自己的需求进行定制,当然有学习成本。
5.2.1 为什么不自动生成Dockerfile呢?
其实完全可以做到自动生成Dockerfile,但每个服务的要求可能不一样,有些需要增加文件、有些在Build时需要增加参数等等。我们不能要求所有的项目都是一样的,这会阻碍技术的发展。所以退而求其次,我们给出模版,研发根据自己的需求调整。
5.3 工具整合 开普勒云平台整合了 gitlab,Jenkins,repo,k8s,istio,promtheus,email,WeChat 等API。 实现对服务的整个生命周期的管理。 提供服务管理、创建、发布、版本、监控、报警、日志已及一些周边附加功能,消息中心、配置中心、还能登陆到容器,服务下线等等。 可对服务进行一健调整服务模式、服务类型、一键扩容伸缩,回滚服务API管理以及存储的管理等操作。
5.4 发布流程
用户把自己的Dockerfile跟代码提交到Gitlab,然后在开普勒云平台填写一些参数创建自己的应用。
应用创建完后会在Jenkins创建一个Job,把代码拉取下来并执行Docker build(如果没有选择多阶构建会先执行go build或mvn),再把打包好的Docker image推送到镜像仓库,最后回调平台API或调用k8s通知拉取最新的版本。
用户只需要在开普勒云平台上管理好自己的应用就可以,其他的全部自动化处理。
5.5 从创建一个服务开始
我们从创建一个服务开始介绍平台。
平台主界面:
点击“创建服务”后进入创建页面。
填写基本信息:
填写详细信息:
基本信息以Golang为例,当选择其他语言时所需填写的参数会略有不同。
如果选择了对外提供服务的话,会进入第三步,第三步是填写路由规则,如没有特殊需求直接默认提交就行了。
5.5.1 服务详情

Build 升级应用版本:
调用服务模式,可以在普通跟服务网格之间调整。
服务是否提供对外服务的能力:
扩容调整CPU、内存:
调整启动的Pod数量:
网页版本的终端:
5.5.2 定时任务

5.5.3 持久化存储

管理员创建StorageClass跟PersistentVolumeClaim,用户只需要在自己服务选择相关的PVC进行绑写就行了。
存储使用的是NFS。
5.5.4 Tracing

5.5.5 Consul
Consul当作配置中心来使用,并且我们提供Golang的客户端。 $ go get github.com/lattecake/consul-kv-client
它会自动同步consul的目录配置存在内存,获取配置只需要直接从内存拿就行了。
5.5.6 Repository
Github: https://github.com/kplcloud/kplcloud Document: https://docs.nsini.com Demo: https://kplcloud.nsini.com 作者:王聪
首发:宜技之长
云计算
2019-10-16 16:03:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> DNS服务器,即域名服务器,它作为域名和IP地址之间的桥梁,在互联网访问中,起到至关重要的作用。每一个互联网上的域名,背后都至少有一个对应的DNS。对于一个企业来说,如果你的DNS服务器因为攻击而无法使用,整个企业的网站、邮箱、办公系统将全部瘫痪,这意味着对你造成的是成千上万个用户的不可访问,将产生不可估量的影响。你的DNS服务器是否受到过安全威胁,它现在真的安全么?
DNS面临着各种各样的网络安全威胁,它一直是网络基础架构中较弱的一环。 我们先来看看DNS服务器的威胁之一:DDoS攻击。DDoS攻击,即分布式拒绝服务攻击,攻击者通过控制大量的傀儡机,对目标主机发起洪水攻击,造成服务器瘫痪。 DDoS攻击通常分为流量型攻击和应用型攻击。以bps为单位的流量型的攻击,这类攻击会瞬间堵塞网络带宽;以qps为单位的应用层攻击,主要是将服务器的资源耗尽,攻击将造成DNS服务器反应缓慢直至再也无法响应正常的请求。DNS服务易受攻击的原因,除了专门针对DNS服务器发起的;还有就是利用DNS服务的特点,用“DNS放大攻击”对其他受害主机发起攻击。如下就是DNS放大攻击的示意图,DNS方法攻击的危害极大。
16年美国针对 DNS 供应商Dyn,爆发来史上最大规模的DDoS攻击,这一攻击造成了半个美国网络瘫痪的严重影响。这次严重的攻击事件,就是DNS放大攻击造成的。
接下来,我们再来看看DNS劫持对DNS服务器会造成哪些威胁。DNS劫持是指攻击者在劫持的网络范围内拦截域名解析的请求,篡改了某个域名的解析结果。比如用户本来想访问www.aliyun.com,却被指引到了另一个假冒的地址上,从而达到非法窃取用户信息或其他不正常的网络服务的目的。对用户而言,DNS劫持是很难感知的,因此造成的影响极大。如2013年就爆发过一次大型的DNS钓鱼攻击事件,导致约800万用户感染。攻击者通常会通过两种方式实现DNS劫持,一种是直接攻击域名注册商或者域名站点获取控制域名的账户口令,这样可以修改域名对应的IP地址,另外一种方式是攻击权威名称服务器,直接修改区域文件内的资源记录。如下图,中国电信发现域名劫持时,给访问用户的提醒。
DNS劫持主要是将NS纪录指向到黑客可以控制的DNS服务器,而DNS投毒却是指一些有意或无意制造出来的域名服务器数据包,利用控制DNS缓存服务器,将域名指引到了不正确的IP地址。一般DNS服务器为了加快解析速度,通常都会把访问过的域名服务器数据暂存起来。待下次其他用户需要解析域名时,如果发现缓存中有该数据,就立刻调出来提供服务。如果DNS的缓存受到了污染,就会把访问的域名指引到错误的服务器上。简单点说,DNS污染是指把自己伪装成DNS服务器,在检查到用户访问某些网站后,使域名解析到错误的IP地址。
DNS服务如此重要,当对DNS的解析配置操作时,也必须谨慎小心。这里最后介绍的一种DNS常见威胁,就是人为误操作造成的。大家都知道域名做好解析后并不能立即访问到您的网站,因为解析生效需要时间。由于各地的dns服务器刷新时间不同,各地的大概时间范围为0-24小时。最长的生效时间,达到24小时,如果不小心改错了域名的解析配置,也将造成极大的影响。这就要求我们对域名的管理需要精细化,分配好使用者的权限,存留好操作记录等日志信息。在域名修改配置或进行迁移时,操作一定要谨慎。
以上简单介绍了DNS服务器可能遇到的常见威胁。作为互联网最基础、最核心的服务,DNS服务安全至关重要。打垮DNS服务能够间接打垮一家公司的全部业务,或者打垮一个地区的网络服务。面对重重威胁,怎么才能保障好我们的DNS服务器呢?接下来的专题,我们会针对DNS服务器常受到的攻击一一突破,敬请期待。
原文链接
本文为云栖社区原创内容,未经允许不得转载。
云计算
2019-10-16 15:23:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
2019年10月15日,由上海市软件行业协会、中国云体系产业创新战略联盟、拓普会展和国际云安全联盟联合主办的ECSC 2019第二届企业云服务大会在上海盛大举办。作为 中国领先的实干型数字化中台SaaS快速开发领导者的JEPaaS云平台 应邀出席本届大会。
在企业数字化转型升级不断推进的当下,企业云化已成为下一阶段商业竞赛的重要角力点。企业如何部署多元化云服务架构,驱动数字化转型和重构,推进企业创新和管理变革成为迫切思考的问题?
针对这些问题,本届大会以“企业智变—云化未来”为主题,聚焦数字化变革、多云管理、边缘计算、云安全、SaaS应用、数据智能、云网融合、5G新机遇等热点话题,邀请了来自金融、教育、医疗、零售、制造等各领域各行业企业的领导、专家和学者等业内资深人士,共同探讨和分享了企业数字化转型中上云时遇到的难题及对云服务需求的变化。
在15日下午大会举行的“技术驱动企业新动能”分论坛中,JEPaaS创始人/凯特伟业CEO云凤程先生受邀出席,与业内专家就“用云之道---如何在安全/效率/成本三者之间寻求平衡点”的话题进行了深入探讨。云总在致辞中分享了他对于当下企业数字化转型趋势的看法,并就企业在上云过程中所遇到的难题分享了JEPaaS的成功案例。

JEPaaS创始人/凯特伟业CEO云凤程先生发表致辞
JEPaaS作为北京凯特科技着力打造的一款企业级信息化应用,凝聚了十多年来服务于国企、央企、集团公司等大型集团企业的深厚技术积累与丰富服务经验。多年的技术积淀和对客户业务流程以及应用管理难点的深刻理解,以及数万家企业的成功实践,让JEPaaS构建了一套专业的数字化开发体系。其中,JEPaaS拥有的四大核心承载力:数字化中台工具、SaaS应用开发管理体系、低代码快速开发以及可实现万物互联的物联网接口引擎,这四大核心承载力为企业提高核心业务生产率发挥着显著作用。
与此同时,在大会现场的展会区域,JEPaaS展台也吸引众多参会嘉宾和企业代表们前来进行交流互动,JEPaaS的专家顾问团队和现场的嘉宾代表们进行了详细、深入的洽谈。
随着数字经济的快速创新发展,企业上云已成为各企业转型发展的共识。JEPaaS此次参加大会,将JEPaaS对于企业数字化创新的思考、理解及服务展示给业界,JEPaaS的创新思路、观点和技术也必将给业界带来更多新启发。
未来,JEPaaS将持续不断为各行业提供优质、可靠的企业数字化解决方案和服务,为推动中国企业的信息化和数字化,引领和服务更多企业迈向成功而不懈努力!

云计算
2019-10-16 14:40:04
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>

前言
首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute) : 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传。函数计算准备计算资源,并以弹性伸缩的方式运行用户代码,而用户只需根据实际代码运行所消耗的资源进行付费。函数计算更多信息 参考 。 Fun : Fun 是一个用于支持 Serverless 应用部署的工具,能帮助您便捷地管理函数计算、API 网关、日志服务等资源。它通过一个资源配置文件(template.yml),协助您进行开发、构建、部署操作。Fun 的更多文档 参考 。
备注: 本文介绍的技巧需要 Fun 版本大于等于 3.6.7。

函数计算运行环境中内置一些常用字体,但仍不满足部分用户的需求。如果应用中需要使用其它字体,需要走很多弯路。本文将介绍如何通过 Fun 工具将自定义字体部署到函数计算,并正确的在应用中被引用。


你需要做什么? 在代码(CodeUri)目录新建一个 fonts 目录 将字体复制到 fonts 目录 使用 fun deploy 进行部署

工具安装
建议直接从这里下载二进制可执行程序,解压后即可直接使用。 下载地址 。
执行 fun --version 检查 Fun 是否安装成功。 $ fun --version 3.7.0

示例
demo 涉及的代码,托管在  github  上。项目目录结构如下: $ tree -L -a 1 ├── index.js ├── package.json └── template.yml
index.js 中代码: 'use strict'; var fontList = require('font-list') module.exports.handler = async function (request, response, context) { response.setStatusCode(200); response.setHeader('content-type', 'application/json'); response.send(JSON.stringify(await fontList.getFonts(), null, 4)); };
index.js 中借助 node 包 font-list 列出系统上可用的字体。
template.yml: ROSTemplateFormatVersion: '2015-09-01' Transform: 'Aliyun::Serverless-2018-04-03' Resources: fonts-service: # 服务名 Type: 'Aliyun::Serverless::Service' Properties: Description: fonts example fonts-function: # 函数名 Type: 'Aliyun::Serverless::Function' Properties: Handler: index.handler Runtime: nodejs8 CodeUri: ./ InstanceConcurrency: 10 Events: http-test: Type: HTTP Properties: AuthType: ANONYMOUS Methods: - GET - POST - PUT tmp_domain: # 临时域名 Type: 'Aliyun::Serverless::CustomDomain' Properties: DomainName: Auto Protocol: HTTP RouteConfig: Routes: /: ServiceName: fonts-service FunctionName: fonts-function
template.yml 中定义了名为 fonts-service 的服务,此服务下定义一个名为 fonts-function 的 http trigger 函数。tmp_domain 中配置自定义域名中路径(/)与函数(fonts-service/fonts-function)的映射关系。


1. 下载字体
你可以通过 这里 下载自定义字体 Hack ,然后将 复制 字体到 fonts 目录。 此时 demo 目录结构如下: $ tree -L 2 -a ├── fonts(+) │ ├── Hack-Bold.ttf │ ├── Hack-BoldItalic.ttf │ ├── Hack-Italic.ttf │ └── Hack-Regular.ttf ├── index.js ├── package.json └── template.yml

2. 安装依赖 $ npm install

3. 部署到函数计算
可以通过 fun deploy 直接发布到远端。



4. 预览线上效果
fun deploy 部署过程中,会为此函数生成有时效性的临时域名:

打开浏览器,输入临时域名并回车:


可以看到字体 Hack 已生效!!!


原理介绍: fun deploy 时,如果检测到 CodeUri 下面有 fonts 目录,则为用户在 CodeUri 目录生成一个 .fonts.conf 配置文件。在该配置中,相比于原来的 /etc/fonts/fonts.conf 配置,添加了 /code/fonts 作为字体目录。 自动在 template.yml 中添加环境变量,FONTCONFIG_FILE = /code/.fonts.conf,这样在函数运行时就可以正确的读取到自定义字体目录。
如果依赖过大,超过函数计算的限制(50M)则: 将 fonts 目录添加到 .nas.yml 将 fonts 对 nas 的映射目录追加到 .fonts.conf 配置
fun deploy 对大依赖的支持可参考 《开发函数计算的正确姿势——轻松解决大依赖部署》


总结
你只需要在代码(CodeUri)目录新建一个 fonts 目录,然后复制所有字体到该目录即可。Fun 会自动帮你处理配置文件(.fonts.conf),环境变量以及大依赖场景的情况。如果大家在使用 Fun 的过程中遇到了一些问题,可以在 github 上提 issue ,或者加入我们的钉钉群 11721331 进行反馈。 “ 阿里巴巴云原生 关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”
云计算
2020-04-02 15:59:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
上一篇《 玩转阿里云Terraform(一):Terraform 是什么 》介绍了 Terraform 的基本定义和特点之后,本文将着重介绍几个Terraform中的关键概念。

Terraform 关键概念
在使用Terraform的过程中,通常接触到很多名词,如configuration,provider,resource,datasource,state,backend,provisioner等,本文将一一跟大家介绍这些概念。
Configuration:基础设施的定义和描述
“基础设施即代码(Infrastructure as Code)”,这里的Code就是对基础设施资源的代码定义和描述,也就是通过代码表达我们想要管理的资源。 # VPC 资源 resource "alicloud_vpc" "vpc" { name = "tf_vpc" cidr_block = "172.16.0.0/16" } # VSwitch 资源 resource "alicloud_vswitch" "vswitch" { vpc_id = alicloud_vpc.vpc.id cidr_block = "172.16.1.0/24" availability_zone = "cn-beijing-a" }
对所有资源的代码描述都需要定义在一个以 tf 结尾的文件用于Terraform加载和解析,这个文件我们称之为“Terraform模板”或者“Configuration”。
Provider:基础设施管理组件
Terraform 通常用于对云上基础设施,如虚拟机,网络资源,容器资源,存储资源等的创建,更新,查看,删除等管理动作,也可以实现对物理机的管理,如安装软件,部署应用等。
【Provider】 是一个与Open API直接交互的后端驱动,Terraform 就是通过Provider来完成对基础设施资源的管理的。不同的基础设施提供商都需要提供一个Provider来实现对自家基础设施的统一管理。目前Terraform目前支持超过160多种的providers,大多数云平台的Provider插件均已经实现了,阿里云对应的Provider为 alicloud 。
在操作环境中,Terraform和Provider是两个独立存在的package,当运行Terraform时,Terraform会根据用户模板中指定的provider或者resource/datasource的标志自动的下载模板所用到的所有provider,并将其放在执行目录下的一个隐藏目录 .terraform 下。 provider "alicloud" { version = ">=1.56.0" region = "cn-hangzhou" configuration_source = "terraform-alicloud-modules/classic-load-balance" }
模板中显示指定了一个阿里云的Provider,并显示设置了provider的版本为 1.56.0+ (默认下载最新的版本),指定了需要管理资源的region,指定了当前这个模板的标识。
通常Provider都包含两个主要元素 resource 和 data source。
Resource:基础设施资源和服务的管理
在Terraform中,一个具体的资源或者服务称之为一个resource,比如一台ECS 实例,一个VPC网络,一个SLB实例。每个特定的resource包含了若干可用于描述对应资源或者服务的属性字段,通过这些字段来定义一个完整的资源或者服务,比如实例的名称(name),实例的规格(instance_type),VPC或者VSwitch的网段(cidr_block)等。
定义一个Resource的语法非常简单,通过 resource 关键字声明,如下: # 定义一个ECS实例 resource "alicloud_instance" "default" { image_id = "ubuntu_16_04_64_20G_alibase_20190620.vhd" instance_type = "ecs.sn1ne.large" instance_name = "my-first-vm" system_disk_category = "cloud_ssd" ... } 其中 alicloud_instance 为 资源类型(Resource Type) ,定义这个资源的类型,告诉Terraform这个Resource是阿里云的ECS实例还是阿里云的VPC。 default 为 资源名称(Resource Name) ,资源名称在同一个模块中必须唯一,主要用于供其他资源引用该资源。 大括号里面的block块为 配置参数(Configuration Arguments) ,定义资源的属性,比如ECS 实例的规格、镜像、名称等。
显然这个Terraform模板的功能为在阿里云上创建一个ECS实例,镜像ID为 ubuntu_16_04_64_20G_alibase_20190620.vhd ,规格为 ecs.sn1ne.large ,自定义了实例名称和系统盘的类型。
除此之外,在Terraform中,一个资源与另一个资源的关系也定义为一个资源,如一块云盘与一台ECS实例的挂载,一个弹性IP(EIP)与一台ECS或者SLB实例的绑定关系。这样定义的好处是,一方面资源架构非常清晰,另一方面,当模板中有若干个EIP需要与若干台ECS实例绑定时,只需要通过Terraform的 count 功能就可以在无需编写大量重复代码的前提下实现绑定功能。 resource "alicloud_instance" "default" { count = 5 ... } resource "alicloud_eip" "default" { count = 5 ... } resource "alicloud_eip_association" "default" { count = 5 instance_id = alicloud_instance.default[count.index].id allocation_id = alicloud_eip.default[count.index].id }
显然这个Terraform模板的功能为在阿里云上创建5个ECS实例和5个弹性IP,并将它们一一绑定。
Data Source:基础设施资源和服务的查询
对资源的查询是运维人员或者系统最常使用的操作,比如,查看某个region下有哪些可用区,某个可用区下有哪些实例规格,每个region下有哪些镜像,当前账号下有多少机器等,通过对资源及其资源属性的查询可以帮助和引导开发者进行下一步的操作。
除此之外,在编写Terraform模板时,Resource使用的参数有些是固定的静态变量,但有些情况下可能参数变量不确定或者参数可能随时变化。比如我们创建ECS 实例时,通常需要指定我们自己的镜像ID和实例规格,但我们的模板可能随时更新,如果在代码中指定ImageID和Instance,则一旦我们更新镜像模板就需要重新修改代码。
在Terraform 中,Data Source 提供的就是一个查询资源的功能,每个data source实现对一个资源的动态查询,Data Souce的结果可以认为是动态变量,只有在运行时才能知道变量的值。
Data Sources通过 data 关键字声明,如下: // Images data source for image_id data "alicloud_images" "default" { most_recent = true owners = "system" name_regex = "^ubuntu_18.*_64" } data "alicloud_zones" "default" { available_resource_creation = "VSwitch" enable_details = true } // Instance_types data source for instance_type data "alicloud_instance_types" "default" { availability_zone = data.alicloud_zones.default.zones.0.id cpu_core_count = 2 memory_size = 4 } resource "alicloud_instance" "web" { image_id = data.alicloud_images.default.images[0].id instance_type = data.alicloud_instance_types.default.instance_types[0].id instance_name = "my-first-vm" system_disk_category = "cloud_ssd" ... }
如上例子中的ECS Instance 没有指定镜像ImageID和实例规格,而是通过 data 引用,Terraform运行时将首先根据镜像名称前缀选择系统镜像,如果同时有多个镜像满足条件,则选择最新的镜像。实例规格也是类似,在某个可用区下选择2核4G的实例规格进行返回。

State:保存资源关系及其属性文件的数据库
Terraform创建和管理的所有资源都会保存到自己的数据库上,这个数据库不是通常意义上的数据库(MySQL,Redis等),而是一个文件名为 terraform.tfstate 的文件,在Terraform 中称之为 state ,默认存放在执行Terraform命令的本地目录下。这个 state 文件非常重要,如果该文件损坏,Terraform 将认为已创建的资源被破坏或者需要重建(实际的云资源通常不会受到影响),因为在执行Terraform命令是,Terraform将会利用该文件与当前目录下的模板做Diff比较,如果出现不一致,Terraform将按照模板中的定义重新创建或者修改已有资源,直到没有Diff,因此可以认为Terraform是一个有状态服务。
当涉及多人协作时不仅需要拷贝模板,还需要拷贝 state 文件,这无形中增加了维护成本。幸运的是,目前Terraform支持把 state 文件放到远端的存储服务 OSS 上或者 consul 上,来实现 state 文件和模板代码的分离。具体细节可参考 官方文档Remote State 或者关注后续文章的详细介绍。

Backend:存放 State 文件的载体
正如上节提到,Terraform 在创建完资源后,会将资源的属性存放在一个 state 文件中,这个文件可以存放在本地也可以存放在远端。存放 state 文件的载体就是 Backend 。
Backend 分为本地(local)和远端(remote)两类,默认为本地。远端的类型也非常多,目前官方网站提供的有13种,并且阿里云的 OSS 就位列其中。
使用远端的Backend,既可以降低多人协作时对state的维护成本,而且可以将一些敏感的数据存放在远端,保证了数据的安全性。

Provisioner:在机器上执行操作的组件
Provisioner 通常用来在本地机器或者登陆远程主机执行相关的操作,如 local-exec provisioner 用来执行本地的命令, chef provisioner 用来在远程机器安装,配置和执行chef client, remote-exec provisioner 用来登录远程主机并在其上执行命令。
Provisioner 通常跟 Provider一起配合使用,provider用来创建和管理资源,provisioner在创建好的机器上执行各种操作。

小结
Terraform中涉及到的概念非常多,用法也多种多样,本文只是介绍了其中的几个关键的概念,如果想要了解更多的概念,可以访问Terraform的官方网站。学习Terraform最好的方式是要多动手,使用terraform去实现和操作一些特定的场景,在不断操作的过程中,持续解决遇到的问题可以帮助大家更快和更好的使用Terraform。下一篇将向大家介绍Terraform的运行原理和基本操作。
最后,欢迎大家关注 Terraform,关注阿里云 Provider,如有任何使用问题,可直接在 terraform-provider-alicloud 提交您的问题,我们将尽快解决。

阅读原文
本文为云栖社区原创内容,未经允许不得转载。
云计算
2019-10-18 14:45:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 元毅  阿里巴巴高级开发工程师
阿里巴巴云原生公众号后台回复  Knative ,免费下载《Knative 云原生应用开发指南》电子书!
想必大家都比较了解 RocketMQ 消息服务,那么 RocketMQ 与 Serverless 结合会碰撞出怎样的火花呢?我们今天介绍一下如何基于 RocketMQ + Knative 驱动云原生 Serverless 应用 。本文主要从以下几个方面展开介绍: 云原生与 Serverless Knative 简介 RocketMQSource 餐饮配送场景示例
云原生
先看一下 CNCF 对云原生的定义:
云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。
这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。
其实云原生旨在以标准化云服务的提供方式衔接云厂商和客户。这种方式对于客户而言降低了上云和跨云迁移的成本,让客户始终保有和云厂商议价的能力;对云厂商而言,因为客户跨云迁移的成本低,所以只要能提供性价比更高的云服务,就能很容易的聚集大量用户。
Serverless
Serverless(无服务器架构)是指服务端逻辑由开发者实现,运行在无状态的计算容器中,由事件触发,完全被第三方管理,其业务层面的状态则存储在数据库或其他介质中。
Serverless 可以理解为云原生技术发展的高级阶段,使开发者更聚焦在业务逻辑,而减少对基础设施的关注。
这里提到的是 Functions Serverless, 其实除了 Functions Serverless, 还有另外一种 Serverless 形态:容器化的Serverless。相较于 Function Serverless,容器化的 Serverless, 可移植性更强, 开发者对复杂应用程序能进行更好的掌控。除此之外,对于那些经历过容器时代洗礼的用户,容器化的 serverless或许是一种更好的选择。
对于Serverless, 有如下几点需要关注一下: 事件(event)驱动:Serverless 是由事件(event)驱动(例如 HTTP、pub/sub)的全托管计算服务; 自动弹性:按需使用,削峰填谷; 按使用量计费:相对于传统服务按照使用的资源(ECS 实例、VM 的规格等)计费,Serverless 场景下更多的是按照服务的使用量(调用次数、时长等)计费; 绿色的计算: 所谓绿色的计算其实就是最大化的提升资源使用效率,减少资源浪费,做的“节能减排”。
Knative
上面提到了容器化的 Serverless,那么有没有这样的 Serveless 平台框架呢?答案就是:Knative。
Knative 是在 2018 的 Google Cloud Next 大会上发布的一款基于 Kubernetes 的 Serverless 编排引擎。Knative 一个很重要的目标就是制定云原生、跨平台的 Serverless 编排标准。Knative 是通过整合容器构建(或者函数)、工作负载管理(弹性)以及事件模型这三者来实现的这一 Serverless 标准。Knative 社区的当前主要贡献者有 Google、Pivotal、IBM、RedHat。另外像 CloudFoundry、OpenShift 这些 PaaS 提供商都在积极的参与 Knative 的建设。
1. Knative 核心模块
Knative 核心模块主要包括事件驱动框架 Eventing 和部署工作负载的 Serving。
2. Serverless 服务引擎 - Serving
Knative Serving 核心能力就是其简洁、高效的应用托管服务,这也是其支撑 Serverless 能力的基础。Knative 提供的应用托管服务可以大大降低直接操作 Kubernetes 资源的复杂度和风险,提升应用的迭代和服务交付效率。当然作为 Severlesss Framework 就离不开按需分配资源的能力,阿里云容器服务 Knative 可以根据您应用的请求量在高峰时期自动扩容实例数,当请求量减少以后自动缩容实例数,可以非常自动化的帮助您节省成本。
Serving 通过与 Istio 结合还提供了强大的流量管理能力和灵活的灰度发布能力。流量管理能力可以根据百分比切分流量,灰度发布能力可以根据流量百分比进行灰度,同时灰度发布能力还能通过自定义 tag 的方式进行上线前的测试,非常便于和自己的 CICD 系统集成。
Serving 应用模型
Service: 对应用 Serverless 编排的抽象,通过 Service 管理应用的生命周期; Configuration: 当前期望状态的配置。每次更新 Service 就会更新 Configuration; Revision: configuration 的每次更新都会创建一个快照,用来做版本管理; Route: 将请求路由到 Revision,并可以向不同的 Revision 转发不同比例的流量。
3. 事件驱动框架 - Eventing
1.采用 CloudEvent 作为事件传输协议: CloudEvent 以通用的格式描述事件数据,提供跨平台的服务交互能力。KnativeEventing 使用 CloudEvent 作为事件传输标准,极大的提升了应用的跨平台可移植性; 2.外部事件源接入和注册: 提供 Github、RocketMQ 以及 Kafka 等事件源的支持,当然用户可以自定义事件源。 3.事件的订阅和触发:引入 Broker 和 Trigger 模型意义,不仅将事件复杂的处理实现给用户屏蔽起来,更提供丰富的事件订阅、过滤机制; 4.兼容现有消息系统:KnativeEventing 充分解耦了消息系统的实现,目前除了系统自身支持的基于内存的消息通道 InMemoryChannel 之外,还支持 Kafka、NATSStreaming 等消息服务,此外可以方便的对接现有的消息系统。
Eventing 中 Broker/Trigger模型
这里介绍一下 Eventing 中 Broker/Trigger 模型, 其实并不复杂。外部事件源将事件发送给 Broker, Broker 接收事件之后发送给对应的 Channel(也就是消息缓存,转发的地方,如 Kafka,InMemoryChannel 等),通过创建 Trigger 订阅 Broker 实现事件的订阅,另外在 Trigger 中定义对应的服务,实现最终的事件驱动服务。
消息队列 RocketMQ
消息队列 RocketMQ 版是阿里云基于 Apache RocketMQ 构建的低延迟、高并发、高可用、高可靠的分布式消息中间件。消息队列 RocketMQ 版既可为分布式应用系统提供异步解耦和削峰填谷的能力,同时也具备互联网应用所需的海量消息堆积、高吞吐、可靠重试等特性。
RocketMQSource
RocketMQSource 是 Knative 平台的 RocketMQ 事件源。其可以将 RocketMQ 集群的消息以 Cloud Event 的格式实时转发到 Knative 平台,是 Apahe RocketMQ 和 Knative 之间的连接器。
Knative + RocketMQ 场景示例-餐饮配送场景
我们接下来以餐饮配送为例进行演示,餐饮配送场景具有以下特征: 餐饮配送一天之内存在明显的高峰、低谷; 高峰时间下单量很大。
针对这样的情况,我们采用消息驱动 Serverless, 在高峰的时候自动扩容资源,在低谷的时候缩减资源,按需使用能极大的提升资源使用率,从而降低成本。
1. 典型架构
如上图所示,当用餐时间来临,客户点餐生成下单消息发送到 RocketMQ, 通过 RocketMQSource 获取下单消息转换成事件发送到 Broker,通过 Trigger 订阅下单事件最终驱动订单服务生成订餐单。采用该方案具有以下优势: 通过 Knative 技术以 RocketMQ 为核心将餐饮配送系统 Serverless 化可以极大程度降低服务器运维与成本; Knative 的弹性可以帮你轻松应对早、中、晚三餐资源高峰需求; 系统以 RocketMQ 做异步解耦,避免长链路调用等问题,提高系统可用性。
2. 操作
部署 Knative
参见 阿里云容器服务部署 Knative 。
部署 RocketMQSource
在 Knative 组件管理中,选择 RocketMQSource 点击部署。
部署订单服务
参考示例 代码仓库 。
一键部署服务命令如下: kubectl apply -f 200-serviceaccount.yaml -f 202-clusterrolebinding.yaml -f 203-secret.yaml -f alirocketmqsource.yaml -f broker.yaml -f ksvc-order-service.yaml -f trigger.yaml
模拟高峰订餐下单
通过模拟下单,往 RocketMQ 中并发发送消息即可。消息格式参考: {"orderId":"123214342","orderStatus":"completed","userPhoneNo":"152122131323","prodId":"2141412","prodName":"test","chargeMoney":"30.0","chargeTime":"1584932320","finishTime":"1584932320"}
3. 演示效果
如下图所示:
其它应用场景
1. Knative + RocketMQ 典型场景 - 构建 Serverless 电商系统
Knative 弹性可以帮你轻松应对团购、双11 等电商的大促活动; 系统以 RocketMQ 为中心做异步解耦,避免长链路调用等问题,提高系统可用性。
2. Knative + RocketMQ 典型场景- 构建监控告警平台
Metric、Log 等数据通过 RocketMQ 集群推送到 Knative 服务; Knative 服务通过数据分析将告警内容推送钉钉或 slack 等通讯工具; Knative 服务可以将 Metric 或 logs 数据进行处理,推送第三方系统。
3. Knative + RocketMQ 典型场景- 多数据格式转换
处理数据日志以生成多个结果派生词,这些结果派生词可用于运营,营销,销售等; 将内容从一种格式转换为另一种格式,例如,将 Microsoft Word 转换为 PDF; 需要转换为多种格式的主媒体文件。
总结
通过以上 RocketMQ 事件驱动 Knative Serverless 应用的介绍,是否也给你碰撞出了火花?可以结合自身的应用场景不妨一试,相信会给你带来不一样的体验。
欢迎钉钉扫码加入交流群
<关注阿里巴巴云原生公众号,回复 Knative 即可下载电子书> “ 阿里巴巴云原生 关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”
云计算
2020-04-27 10:55:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 冬岛  阿里云容器平台工程师 **导读:**虽然说 Knative 默认就支持 WebSocket 和 gRPC,但在使用中会发现,有时想要把自己的 WebSocket 或 gRPC 部署到 Knative 中,还是存在各种不顺利。虽然最后排查发现,大多是自己的程序问题或是配置错误导致的。本文分别给出了一个 WebSocket 和 gRPC 的例子,当需要在生产或者测试环境部署相关服务时,可以使用本文给出的示例进行 Knative 服务的测试。
WebSocket
如果自己手动的配置 Istio Gateway 支持 WebSocket 就需要开启 websocketUpgrade 功能。但使用 Knative Serving 部署其实就自带了这个能力。本示例的完整代码放在 https://github.com/knative-sample/websocket-chat ,这是一个基于 WebSocket 实现的群聊的例子。
使用浏览器连接到部署的服务中就可以看到一个接收信息的窗口和发送信息的窗口。当你发出一条信息以后所有连接进来的用户都能收到你的消息。所以你可以使用两个浏览器窗口分别连接到服务中,一个窗口发送消息一个窗口接收消息,以此来验证 WebSocket 服务是否正常。
本示例是在 gorilla/websocket  基础之上进行了一些优化: 代码中添加了 vendor 依赖,你下载下来就可以直接使用 添加了 Dockerfile 和 Makefile 可以直接编译二进制和制作镜像 添加了 Knative Sevice 的 yaml 文件( service.yaml ),你可以直接提交到 Knative 集群中使用 也可以直接使用编译好的镜像 registry.cn-hangzhou.aliyuncs.com/knative-sample/websocket-chat:2019-10-15
Knative Service 配置: apiVersion: serving.knative.dev/v1 kind: Service metadata: name: websocket-chat spec: template: spec: containers: - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/websocket-chat:2019-10-15 ports: - name: http1 containerPort: 8080
代码 clone 下来以后执行 kubectl apply -f service.yaml 把服务部署到 Knative 中,然后直接访问服务地址即可使用。
查看 ksvc 列表,并获取访问域名。 └─# kubectl get ksvc NAME URL LATESTCREATED LATESTREADY READY REASON websocket-chat http://websocket-chat.default.serverless.kuberun.com websocket-chat-7ghc9 websocket-chat-7ghc9 True
现在使用浏览器打开 http://websocket-chat.default.serverless.kuberun.com 即可看到群聊窗口。
打开两个窗口,在其中一个窗口发送一条消息,另外一个窗口通过 WebSocket 也收到了这条消息。
gRPC
gRPC 不能通过浏览器直接访问,需要通过 client 端和 server 端进行交互。本示例的完整代码放在 https://github.com/knative-sample/grpc-ping-go ,本示例会给一个可以直接使用的镜像,测试 gRPC 服务。
Knative Service 配置: apiVersion: serving.knative.dev/v1 kind: Service metadata: name: grpc-ping spec: template: spec: containers: - image: registry.cn-hangzhou.aliyuncs.com/knative-sample/grpc-ping-go:2019-10-15 ports: - name: h2c containerPort: 8080
代码 clone 下来以后执行 kubectl apply -f service.yaml 把服务部署到 Knative 中。
获取 ksvc 列表和访问域名: └─# kubectl get ksvc NAME URL LATESTCREATED LATESTREADY READY REASON grpc-ping http://grpc-ping.default.serverless.kuberun.com grpc-ping-p2tft Unknown RevisionMissing websocket-chat http://websocket-chat.default.serverless.kuberun.com websocket-chat-6hgld websocket-chat-6hgld True
现在我们已经知道 gRPC  server 的地址是 grpc-ping.default.serverless.kuberun.com,端口是 80,那么我们可以发起测试请求: └─# docker run --rm registry.cn-hangzhou.aliyuncs.com/knative-sample/grpc-ping-go:2019-10-15 /client -server_addr="grpc-ping.default.serverless.kuberun.com:80" -insecure 2019/10/16 11:35:07 Ping got hello - pong 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854794231 +0800 CST m=+73.061909052 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854827273 +0800 CST m=+73.061942072 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854835802 +0800 CST m=+73.061950606 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854842843 +0800 CST m=+73.061957643 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854849211 +0800 CST m=+73.061964012 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854855249 +0800 CST m=+73.061970049 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854861659 +0800 CST m=+73.061976460 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854875071 +0800 CST m=+73.061989873 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854905416 +0800 CST m=+73.062020221 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.85491183 +0800 CST m=+73.062026630 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.85492533 +0800 CST m=+73.062040133 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854932285 +0800 CST m=+73.062047083 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854946977 +0800 CST m=+73.062061782 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854953311 +0800 CST m=+73.062068112 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854966639 +0800 CST m=+73.062081440 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854973939 +0800 CST m=+73.062088739 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854985463 +0800 CST m=+73.062100268 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854993275 +0800 CST m=+73.062108073 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.854999812 +0800 CST m=+73.062114613 2019/10/16 11:35:07 Got pong 2019-10-16 11:35:07.855012676 +0800 CST m=+73.062127479
小结
本文通过两个例子分别展示了 WebSocket 和 gRPC 的部署方法: WebSocket 示例通过一个 chat 的方式展示发送和接受消息 gRPC 通过启动一个 client 的方式展示 gRPC 远程调用的过程
作者简介: 冬岛,阿里云容器平台工程师,负责阿里云容器平台 Knative 相关工作。 了解更多 ACK 详情: https://www.aliyun.com/product/kubernetes
欢迎加入 Knative 交流群
“ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”
云计算
2019-10-18 14:31:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
从去年4月份开始我们公司就开始使用 云桌面 来进行上网办公的,在这一年多的使用过程中并没有出现像网上和有些用户说的那样,说云桌面各种坑老是出现这样和那样的问题,而我们之所以用的还不错,主要是因为我们在部署云桌面的时应用了一些小技巧的,这里就拿来和大家分享下我们部署云桌面时的几个小技巧的。

首先明确定位,首先一点我认为很重要的就是我们公司在部署云桌面的时候就明确了我们部署云桌面的目的是什么,想要达到什么样的效果。我们是一家互联网公司应用最多的就是浏览网页和QQ以及PS这些,基本不涉及3D类应用,而公司目前最看重的就是数据安全,确保数据不被拷贝和泄露出去的,我们后续的工作都是围绕这些进行的。

其次多找厂家,这里说的多找厂家并不是满世界的厂家都找过来的,而是说在部署云桌面的时候不能只找一家厂家的,而是多找几家符合自己需求和定位的厂家进行多方面的对比,从这些厂家中找一个性价比各个方面都符合公司需求的厂家的,当然也不能找太多厂家对比这样自己会更茫然的,我的建议是找3到4家左右就可以的,这样比较容易对比。

第三进行测试,这一点很重要,在选好厂家之后我们并没有说价格合适就马上部署的,而是进行1个星期左右的测试使用的,测试厂家说的这些云桌面功能是否都可以实现的,以及在使用过程是否有需要改进和调整的地方,以及系统桌面的稳定性这些都需要测试一下的。确保部署后的正常使用的。

第四先小后大,测试没问题部署的时候要采取先小后大的原则,即先在某个部门部署云桌面使用的,等这个部门使用没有问题之后,然后在慢慢普及到全部员工统一使用的,因为云桌面毕竟是一个新的应用,如果一开始就大规模的部署容易造成某些的员工不适应,也能防止在部署过程中出现问题而影响全部员工的正常使用的。

最后必要培训,虽然说云桌面的使用习惯和传统的PC差不多,但是对于使用前的一些培训还是很有必要的,我们在部署的时候就专门叫云桌面厂家的工程师专门给我们IT部门运维人员做了管理和维护上培训的,同时给员工做了一些使用上的培训。确保云桌面的管理维护和使用都是没有问题的。

其实当前云桌面的功能和技术是已经很成熟的了,在很多的应用场景中都是可以使用的,而之所以有人说它坑,主要还是因为在部署的时候没有做好准备工作的。
来源禹龙云
云计算
2019-10-18 14:02:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 临石、元毅、冬岛、衷源、天元
业界要闻
全球首个开放应用模型 OAM 开源
2019 年 10 月 17 日,阿里巴巴合伙人、阿里云智能基础产品事业部总经理蒋江伟(花名:小邪)在 Qcon 上海重磅宣布,阿里云与微软联合推出开放应用模型 Open Application Model (OAM)开源项目。OAM 的愿景是以标准化的方式沟通和连接应用开发者、运维人员、应用基础设施,让云原生应用管理与交付变得更加简洁,高效,并且可控。
KubeVirt 进入 CNCF Sandbox
KubeVirt  尽管容器技术提供了各种便利,但是在特定情况下使用 VM 仍然是不可避免的(编者按:VM 只是选择之一,不同的安全容器解决方案都可以在不同方面代替单纯的 VM)。来自 Redhat 的 KubeVirt 项目可以提供在 K8s 集群内部准备、部署、运行和管理 VM 的能力,让用户可以使用 Pod 一样来使用一个 VM。现在,这个项目已经正式进入了 CNCF 的 Sandbox 了。
Megalix 发布 KubeAdvisor 1.0 版本
KubeAdvisor 日前发布了 1.0 版本。KubeAdvisor 一款辅助 K8s 运维的工具,能够扫描 K8s 集群的资源、状态、配置等,通过提供恰当的“可观测性”(编者按:眼花缭乱的监控数据和曲线图不等于可观测性),为集群使用者提供最有价值的信息,来辅助保障基础设施和上层应用的可靠性和稳定性。
上游重要进展
1. 允许动态调整 kube-scheduler 的 log level 。kube-scheduler 作为 K8s 重要的默认调度组件,在一些情况下它们的日志可以反映出不少信息,这个 PR 允许 kube-scheduler 的日志打印级别可以动态被调整。
2.调度器方面最近的一个举动是将一些 Prioritizing 的逻辑插件化(Score Plugin)。最近几个相关的PR如下,感兴趣的同学可以关注。 BalancedResourceAllocation MostRequestedPriority LeastResourcePriority
3. 为 scheduler 添加一个跟踪 Binding 和 Prioritizing 的 goroutine 数目的 metric 。
在大规模应用部署等一些特殊场景中,调度器可能成为整体性能的瓶颈。
4.这里有两个优化 kubeadm 使用体验的 PR: kubeadm 的 structured output :结构化输出 kubeadm 的一些输出信息(yaml、json 等),方便被继续处理; kubeadm 添加 --with-ca flag 来显示 ca 的过期信息 :ca 过期是常见的造成组件不可用的愿意之一,有了这个 flag 可以更方便的指导 ca 的过期信息。
5.下面是几个比较重要 / 有意思的 KEP,感兴趣的朋友可以关注一下: 为 Kube API Server 的 network proxy 添加了 beta 版毕业条件 。KAS 允许配置 Kube API Server 的网络流量到(或者不到)指定的 proxy; insecure kubelet log :通过一个开关,使得在 kubelet 的 serving cert 过期(kube apiserver 不认识 kubelet)但kube-apiserver 的 client cert 没有过期(kubelet 认识 kube apiserver)的条件下,允许 kubelet 通过跳过 tls 验证返回一些 log。这个功能在测试和debug的场景是有用的。 规范化 conformanece test 的内容的实施 (文档、API schema、代码检验、专家知识等) 扩展 NodeRestriction Controller 来限制更多 Node 可以 Pod 进行的操作(主要是来自安全上的考虑); HPA 的状态达到 implementable 两个计划中的 GA,对于想要参与到 K8s 社区的新手贡献者来说,这两个 KEP 是不错的切入点(目标、方法很明确的需求) 1. 根据 Node condition 给 Node 打上 taint 标记 ,自动化帮助调度器识别出不适合的调度节点; 1. 之前 DaemonSet 的调度逻辑是在 DaemonSet controller 中的, 这个 KEP 希望把调度逻辑移动到调度器中实现。
开源项目推荐
VMware-Tanzu
VMware  已经开始全面支持 K8s,最近该公司在开源方面的一个举措是将几个自己拥有的云原生开源项目迁移到了新的 Organization:VMware-Tanzu。这个项目中目前包括下面几个项目: velero:应用迁移工具 octant:一个集群状态展示的 dashboard sonobuoy:一个 K8s 分析工具 ······
k8s-transmogrifier
K8s 1.16 中废弃了大量的 API,影响到很多的已经用于生产的集群配置和 Helm chart 等。这里有一个自动转换 K8s 1.16 中的 depreciated 的 API 的 工具 ,有需要的人可以了解一下。
本周阅读推荐
1.《 阿里巴巴的研发模式是如何演进的? 》
随着云计算的不断发展,很多开发者都对这一技术将为开发方式带来的变化充满兴趣,云计算解决的是从 CAPEX 到 OPEX 的转变问题。云可以带来哪些切实的好处?在云环境下应该怎么做应用架构?基于从传统架构到云架构的亲身经历,阿里巴巴合伙人、阿里云智能基础产品事业部总经理蒋江伟(小邪)阐述了企业由新架构和新研发模式带来的价值点。本文整理自正在召开的  QCon 上海 2019  蒋江伟(小邪)的演讲内容。
2.《 How Zalando manages 140+ Kubernetes Cluster 》
本文介绍了 Zalando 的团队在(公有云上)管理数量棒庞大的 K8s 集群中得到的一些实践经验,例如每个 domain 或者 production community 总是部署双集群(prod & non-prod)、使用 Github 托管配置文件、通过 CLM(Cluster Lifecycle Manager)管理升级等等。
3.《 Liveness Probes are Dangerous 》
Liveness Probe 和 Readiness Probe 是 K8s 中判断应用是否可用的重要工具,然而不正确的使用 Liveness Probe 或者 Readiness Probe 会带来的风险(例如不小心使用了外部依赖、Liveness Probe 有时候反而会阻止 Pod 正确的进入失败状态从而无法彻底恢复健康等),这篇文章总结了使用这两者的最佳实践。
4.《 A Practical Guide to Setting Kubernetes Requests and Limits 》
Kubernetes 资源定义中的 request 和 limit 是老生常谈的问题了,本文除了更好的解释这些概念意外,还从 SLA 的角度提供了如何配置它们的建议。
5.《 Protecting Kubernetes API Against CVE-2019-11253 (Billion Laughs Attack) and Other Vulnerabilities 》
本文通过介绍 K8s 中的一个可能导致 billions laughs attacks 漏洞的例子,讲解了在生产环境中使用 K8s 的常规安全操作,包括正确配置 RBAC、定期检查 Role 和 RoleBinding、永远不要暴露 Master host 的地址等等。
6.《 基于 Knative Serverless 技术实现天气服务-下篇 》
上一期我们介绍了如何 基于 Knative Serverless 技术实现天气服务-上篇 ,本篇文章我们介绍如何通过表格存储提供的通道服务,实现 Knative 对接表格存储事件源,订阅并通过钉钉发送天气提醒通知。关于 Knative 更多精彩文章请看 《 Knative 系列文章 》。 “ 阿里巴巴云原生微信公众号(ID:Alicloudnative)关注微服务、Serverless、容器、Service Mesh等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术公众号。”
云计算
2019-10-18 14:01:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
从本文起,我将陆续推出一系列有关 Terraform 的文章,从概念,特点,工作机制,用法以及最佳实践等多个方面由浅入深的向大家介绍如何在阿里云上玩转 Terraform。同时也希望借此机会,与感兴趣的开发者相互探讨和学习。
作为本系列的引子,本文将从 Terraform 是什么开始介绍。

Terraform 是什么
如果两年前问 Terraform 是什么,国内熟悉它的人可能并不是很多,毕竟它是2014年的新生儿,提到资源编排,大家能优先想到的也是 AWS 的 CloudFormation,Azure 的 ARM 以及 阿里云的 ROS 。但是如果今天再问“Terraform 是什么”,相信很大一部分人对它不再陌生,并且一些公司或者感兴趣的开发者已经开始使用 Terraform 来管理他们的基础设施。不论是 Hashicorp 的官方宣传,还是 阿里云云栖社区 的布道;不论是 Devops的持续发展,还是云原生的火热传播;不论是国内最初只有阿里云支持,还是后来国内其他云厂商的陆续支持,Terraform 近两年在国内被越来越多的提及。
在此之前,不止一篇云栖文章介绍过Terraform是什么,本文将结合Terraform的官方文档,对这些文章中的介绍做一个整理和总结。
2014年, Hashicorp 公司推出了一款产品 Terraform
Terraform 官方的定义 如下: Terraform is a tool for building, changing, and versioning infrastructure safely and efficiently. Terraform can manage existing and popular service providers as well as custom in-house solutions.
Configuration files describe to Terraform the components needed to run a single application or your entire datacenter. Terraform generates an execution plan describing what it will do to reach the desired state, and then executes it to build the described infrastructure. As the configuration changes, Terraform is able to determine what changed and create incremental execution plans which can be applied.
The infrastructure Terraform can manage includes low-level components such as compute instances, storage, and networking, as well as high-level components such as DNS entries, SaaS features, etc.
总结起来,三句话: Terraform 是一个可以安全、高效地建立,变更以及版本化管理基础设施的工具,并可在主流的服务提供商上提供自定义的解决方案。 Terraform 以配置文件为驱动,在文件中定义所要管理的组件(基础设施资源),以此生成一个可执行的计划(如果不可执行,会提示报错),通过执行这个计划来完成所定义组件的创建,增量式的变更和持续的管理。 Terraform不仅可以管理IaaS层的资源,如计算实例,网络实例,存储实例等,也可以管理更上层的服务,如DNS 域名和解析记录,SaaS 应用的功能等
更通俗的讲,Terraform 就是运行在客户端的一个开源的,用于资源编排的自动化运维工具。以代码的形式将所要管理的资源定义在模板中,通过解析并执行模板来自动化完成所定义资源的创建,变更和管理,进而达到自动化运维的目标。
Terraform 主要特点
Terraform 具备以下几个主要特点: 基础设施即代码(IaC, Infrastructure as Code)
Terraform 基于一种特定的配置语言(HCL, Hashicorp Configuration Language)来描述基础设施资源。由此,可以像对待任何其他代码一样,实现对所描述的解决方案或者基础架构的版本控制和管理。同时,通用的解决方案和基础架构可以以模板的形式进行便捷的共享和重用。 resource "alicloud_instance" "instance" { count = 5 instance_name = "my-first-tf-vm" image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" instance_type = "ecs.sn1ne.small" security_groups = ["sg-abc12345"] vswitch_id = "vsw-abc12345" tags = { from = "terraform" } } 执行计划(Execution Plans)
Terraform 在执行模板前,运行 terraform plan 命令会先通过解析模板生成一个可执行的计划,这个计划展示了当前模板所要创建或变更的资源及其属性。操作人员可以预览这个计划,在确认无误后执行 terraform apply 命令,即可完成对所定义资源的快速创建和变更,以免发生一些超预期的问题。 An execution plan has been generated and is shown below. Resource actions are indicated with the following symbols: + create Terraform will perform the following actions: # alicloud_instance.instance[0] will be created + resource "alicloud_instance" "instance" { + availability_zone = (known after apply) + id = (known after apply) + image_id = "ubuntu_140405_64_40G_cloudinit_20161115.vhd" + instance_name = "my-first-tf-vm" + instance_type = "ecs.sn1ne.small" + security_groups = [ + "sg-abc12345", ] + tags = { + "from" = "terraform" } + vswitch_id = "vsw-abc12345" ... } # alicloud_instance.instance[1] will be created + resource "alicloud_instance" "instance" { ... } # alicloud_instance.instance[2] will be created + resource "alicloud_instance" "instance" { ... } ... Plan: 5 to add, 0 to change, 0 to destroy. 资源拓扑图(Resource Graph)
Terraform 会根据模板中的定义,构建所有资源的图形,并且以并行的方式创建和修改那些没有任何依赖资源的资源,以保证执行的高效性。对于有依赖资源的资源,被依赖的资源优先执行。 自动化变更(Change Automation)
不论多复杂的资源,当模板中定义的资源内容发生变更时,Terraform 都会基于新的资源拓扑图将变更的内容plan 出来,在确认无误后,只需一个命令即可完成数个变更操作,避免了人为操作带来的错误。

Terraform 活跃的社区
Terraform 作为 DevOps 的一个利器,大大降低了基础设施的管理成本,并且随着业务架构的不断复杂,基础设施资源及其资源关系的不断复杂,Terraform 所带来的便利性和优势将越来越明显。
正因为如此,云计算火热发展的今天,Terraform 受到越来越多的云厂商和其他软件服务商的青睐。目前,Terraform 累计提供了超过100个 Providers 和 Provisioners ,支持15个 Backend Types 。阿里云作为国内第一家与 Terraform 集成的云厂商,目前已经提供了超过 138 个 resource 和 92 个 datasource ,覆盖计算,存储,网络,负载均衡,CDN,中间件,访问控制,数据库等多个领域。并且从 Terraform 0.12.2 版本开始,阿里云 OSS 作为 标准的 Backend 开始提供远端存储 State 的能力,持续提升开发者的使用体验,降低使用成本。
欢迎大家关注 Terraform,关注阿里云 Provider,如有任何使用问题,可直接在 terraform-provider-alicloud 提交您的问题,我们将尽快解决。

阅读原文
本文为云栖社区原创内容,未经允许不得转载。
云计算
2019-10-18 13:43:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
Flannel是cereos开源的CNI网络插件,下图flannel官网提供的一个数据包经过封包、传输以及拆包的示意图,从这个图片中可以看出两台机器的docker0分别处于不同的段:10.1.20.1/24 和 10.1.15.1/24 ,如果从Web App Frontend1 pod(10.1.15.2)去连接另一台主机上的Backend Service2 pod(10.1.20.3),网络包从宿主机192.168.0.100发往192.168.0.200,内层容器的数据包被封装到宿主机的UDP里面,并且在外层包装了宿主机的IP和mac地址。这就是一个经典的overlay网络,因为容器的IP是一个内部IP,无法从跨宿主机通信,所以容器的网络互通,需要承载到宿主机的网络之上。
flannel支持多种网络模式,常用的是vxlan、UDP、hostgw、ipip以及gce和阿里云等,vxlan和UDP的区别是:vxlan是内核封包,而UDP是flanneld用户态程序封包,所以UDP的方式性能会稍差;hostgw模式是一种主机网关模式,容器到另外一个主机上容器的网关设置成所在主机的网卡地址,这个和calico非常相似,只不过calico是通过BGP声明,而hostgw是通过中心的etcd分发,所以hostgw是直连模式,不需要通过overlay封包和拆包,性能比较高,但hostgw模式最大的缺点是必须是在一个二层网络中,毕竟下一跳的路由需要在邻居表中,否则无法通行。
在实际的生产环境中,最常用的还是vxlan模式,我们先看工作原理,然后通过源码解析实现过程。
安装的过程非常简单,主要分为两步:
第一步安装flannel
yum install flannel 或者通过kubernetes的daemonset方式启动,配置flannel用的etcd地址
第二步配置集群网络 curl -L http://etcdurl:2379/v2/keys/flannel/network/config -XPUT -d value="{\"Network\":\"172.16.0.0/16\",\"SubnetLen\":24,\"Backend\":{\"Type\":\"vxlan\",\"VNI\":1}}"
然后启动每个节点的flanned程序。
一、工作原理
1、容器的地址如何分配
Docker容器启动时通过docker0分配IP地址,flannel为每个机器分配一个IP段,配置在docker0上,容器启动后就在本段内选择一个未占用的IP,那么flannel如何修改docker0网段呢?
先看一下 flannel的启动文件 /usr/lib/systemd/system/flanneld.service [Service] Type=notify EnvironmentFile=/etc/sysconfig/flanneld ExecStart=/usr/bin/flanneld-start $FLANNEL_OPTIONS ExecStartPost=/opt/flannel/mk-docker-opts.sh -k DOCKER_NETWORK_OPTIONS -d /run/flannel/docker
文件里面指定了flannel环境变量和启动脚本和启动后执行脚本 ExecStartPost 设置的mk-docker-opts.sh,这个脚本的作用是生成/run/flannel/docker,文件内容如下: DOCKER_OPT_BIP="--bip=10.251.81.1/24" DOCKER_OPT_IPMASQ="--ip-masq=false" DOCKER_OPT_MTU="--mtu=1450" DOCKER_NETWORK_OPTIONS=" --bip=10.251.81.1/24 --ip-masq=false --mtu=1450"
而这个文件又被docker启动文件/usr/lib/systemd/system/docker.service所关联, [Service] Type=notify NotifyAccess=all EnvironmentFile=-/run/flannel/docker EnvironmentFile=-/etc/sysconfig/docker
这样便可以设置docker0的网桥了。
在开发环境中,有三台机器,分别分配了如下网段:
host-139.245 10.254.44.1/24
host-139.246 10.254.60.1/24
host-139.247 10.254.50.1/24
2、容器如何通信
上面介绍了为每个容器分配IP,那么不同主机上的容器如何通信呢,我们用最常见的vxlan举例,这里有三个关键点,一个路由,一个arp,一个FDB。我们按照容器发包的过程,逐一分析上面三个元素的作用,首先容器出来的数据包会经过docker0,那么下面是直接从主机网络出去,还是通过vxlan封包转发呢?这是每个机器上面路由设定的。 #ip route show dev flannel.1 10.254.50.0/24 via 10.254.50.0 onlink 10.254.60.0/24 via 10.254.60.0 onlink
可以看到每个主机上面都有到另外两台机器的路由,这个路由是onlink路由,onlink参数表明强制此网关是“在链路上”的(虽然并没有链路层路由),否则linux上面是没法添加不同网段的路由。这样数据包就能知道,如果是容器直接的访问则交给flannel.1设备处理。
flannel.1这个虚拟网络设备将会对数据封包,但下面一个问题又来了,这个网关的mac地址是多少呢?因为这个网关是通过onlink设置的,flannel会下发这个mac地址,查看一下arp表 # ip neig show dev flannel.1 10.254.50.0 lladdr ba:10:0e:7b:74:89 PERMANENT 10.254.60.0 lladdr 92:f3:c8:b2:6e:f0 PERMANENT
可以看到这个网关对应的mac地址,这样内层的数据包就封装好了
还是最后一个问题,外出的数据包的目的IP是多少呢?换句话说,这个封装后的数据包应该发往那一台机器呢?难不成每个数据包都广播。vxlan默认实现第一次确实是通过广播的方式,但flannel再次采用一种hack方式直接下发了这个转发表FDB # bridge fdb show dev flannel.1 92:f3:c8:b2:6e:f0 dst 10.100.139.246 self permanent ba:10:0e:7b:74:89 dst 10.100.139.247 self permanent
这样对应mac地址转发目标IP便可以获取到了。
这里还有个地方需要注意, 无论是arp表还是FDB表都是permanent ,它表明写记录是手动维护的,传统的arp获取邻居的方式是通过广播获取,如果收到对端的arp相应则会标记对端为reachable,在超过reachable设定时间后,如果发现对端失效会标记为stale,之后会转入的delay以及probe进入探测的状态,如果探测失败会标记为Failed状态。之所以介绍arp的基础内容,是因为老版本的flannel并非使用本文上面的方式,而是采用一种临时的arp方案,此时下发的arp表示reachable状态,这就意味着,如果在flannel宕机超过reachable超时时间的话,那么这台机器上面的容器的网络将会中断,我们简单回顾试一下之前(0.7.x)版本的做法,容器为了为了能够获取到对端arp地址,内核会首先发送arp征询,如果尝试 /proc/sys/net/ipv4/neigh/$NIC/ucast_solicit
此时后会向用户空间发送arp征询 /proc/sys/net/ipv4/neigh/$NIC/app_solicit
之前版本的flannel正是利用这个特性,设定 # cat /proc/sys/net/ipv4/neigh/flannel.1/app_solicit 3
从而flanneld便可以获取到内核发送到用户空间的L3MISS,并且配合etcd返回这个IP地址对应的mac地址,设置为reachable。从分析可以看出,如果flanneld程序如果退出后,容器之间的通信将会中断,这里需要注意。Flannel的启动流程如下图所示:
Flannel启动执行newSubnetManager,通过他创建后台数据存储,当前有支持两种后端,默认是etcd存储,如果flannel启动指定“kube-subnet-mgr”参数则使用kubernetes的接口存储数据。
具体代码如下: func newSubnetManager() (subnet.Manager, error) { if opts.kubeSubnetMgr { return kube.NewSubnetManager(opts.kubeApiUrl, opts.kubeConfigFile) } cfg := &etcdv2.EtcdConfig{ Endpoints: strings.Split(opts.etcdEndpoints, ","), Keyfile: opts.etcdKeyfile, Certfile: opts.etcdCertfile, CAFile: opts.etcdCAFile, Prefix: opts.etcdPrefix, Username: opts.etcdUsername, Password: opts.etcdPassword, } // Attempt to renew the lease for the subnet specified in the subnetFile prevSubnet := ReadCIDRFromSubnetFile(opts.subnetFile, "FLANNEL_SUBNET") return etcdv2.NewLocalManager(cfg, prevSubnet) }
通过SubnetManager,结合上面介绍部署的时候配置的etcd的数据,可以获得网络配置信息,主要指backend和网段信息,如果是vxlan,通过NewManager创建对应的网络管理器,这里用到简单工程模式,首先每种网络模式管理器都会通过init初始化注册,
如vxlan func init() { backend.Register("vxlan", New)
如果是udp func init() { backend.Register("udp", New) }
其它也是类似,将构建方法都注册到一个map里面,从而根据etcd配置的网络模式,设定启用对应的网络管理器。
3、注册网络
RegisterNetwork,首先会创建flannel.vxlanID的网卡,默认vxlanID是1.然后就是向etcd注册租约并且获取相应的网段信息,这样有个细节,老版的flannel每次启动都是去获取新的网段,新版的flannel会遍历etcd里面已经注册的etcd信息,从而获取之前分配的网段,继续使用。
最后通过WriteSubnetFile写本地子网文件, # cat /run/flannel/subnet.env FLANNEL_NETWORK=10.254.0.0/16 FLANNEL_SUBNET=10.254.44.1/24 FLANNEL_MTU=1450 FLANNEL_IPMASQ=true
通过这个文件设定docker的网络。细心的读者可能发现这里的MTU并不是以太网规定的1500,这是因为外层的vxlan封包还要占据50 Byte。
当然flannel启动后还需要持续的watch etcd里面的数据,这是当有新的flannel节点加入,或者变更的时候,其他flannel节点能够动态更新的那三张表。主要的处理方法都在handleSubnetEvents里面 func (nw *network) handleSubnetEvents(batch []subnet.Event) { . . . switch event.Type {//如果是有新的网段加入(新的主机加入) case subnet.EventAdded: . . .//更新路由表 if err := netlink.RouteReplace(&directRoute); err != nil { log.Errorf("Error adding route to %v via %v: %v", sn, attrs.PublicIP, err) continue } //添加arp表 log.V(2).Infof("adding subnet: %s PublicIP: %s VtepMAC: %s", sn, attrs.PublicIP, net.HardwareAddr(vxlanAttrs.VtepMAC)) if err := nw.dev.AddARP(neighbor{IP: sn.IP, MAC: net.HardwareAddr(vxlanAttrs.VtepMAC)}); err != nil { log.Error("AddARP failed: ", err) continue } //添加FDB表 if err := nw.dev.AddFDB(neighbor{IP: attrs.PublicIP, MAC: net.HardwareAddr(vxlanAttrs.VtepMAC)}); err != nil { log.Error("AddFDB failed: ", err) if err := nw.dev.DelARP(neighbor{IP: event.Lease.Subnet.IP, MAC: net.HardwareAddr(vxlanAttrs.VtepMAC)}); err != nil { log.Error("DelARP failed: ", err) } continue }//如果是删除实践 case subnet.EventRemoved: //删除路由 if err := netlink.RouteDel(&directRoute); err != nil { log.Errorf("Error deleting route to %v via %v: %v", sn, attrs.PublicIP, err) } else { log.V(2).Infof("removing subnet: %s PublicIP: %s VtepMAC: %s", sn, attrs.PublicIP, net.HardwareAddr(vxlanAttrs.VtepMAC)) //删除arp if err := nw.dev.DelARP(neighbor{IP: sn.IP, MAC: net.HardwareAddr(vxlanAttrs.VtepMAC)}); err != nil { log.Error("DelARP failed: ", err) } //删除FDB if err := nw.dev.DelFDB(neighbor{IP: attrs.PublicIP, MAC: net.HardwareAddr(vxlanAttrs.VtepMAC)}); err != nil { log.Error("DelFDB failed: ", err) } if err := netlink.RouteDel(&vxlanRoute); err != nil { log.Errorf("failed to delete vxlanRoute (%s -> %s): %v", vxlanRoute.Dst, vxlanRoute.Gw, err) } } default: log.Error("internal error: unknown event type: ", int(event.Type)) } } }
这样flannel里面任何主机的添加和删除都可以被其它节点所感知到,从而更新本地内核转发表。
作者:陈晓宇
来源:宜信技术学院
云计算
2019-10-18 11:20:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
本期课程:
制造行业客户痛点
常见场景与解决方案
典型客户案例等
关注“ ZStack云计算 ”历史图文参与直播学习
云计算
2020-04-01 20:57:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
预置
harbor url: https://harbor.test.com:6443
image url: harbor.test.com:6443/test/test-secret:latest
namespace: harbor-test 将harbor的ca.crt复制到node的/etc/docker/certs.d/harbor.test.com:6443目录下 cp ca.crt /etc/docker/certs.d/harbor.test.com:6443/ 创建Pod拉取镜像需使用的secret kubectl create secret docker-registry my-harbor-secret --docker-server=harbor.test.com:6443 --docker-username= --docker-password= --docker-email= --namespace=harbor-test
secret默认仅在default namespace下有效,若不加--namespace,非default namespace无法使用该secret,会显示拉取镜像失败 Failed to pull image "harbor.test.com:6443/test/test-secret:latest": rpc error: code = Unknown desc = Error response from daemon: pull access denied for harbor.test.com:6443/test/test-secret:latest, repository does not exist or may require 'docker login': denied: requested access to the resource is denied 创建测试用Pod private-reg-pod.yaml apiVersion: v1 kind: Pod metadata: name: private-reg namespace: harbor-test spec: containers: - name: private-reg-container image: harbor.test.com:6443/test/test-secret:latest imagePullSecrets: - name: my-harbor-secret kubectl create -f private-reg-pod.yaml
云计算
2020-04-01 15:06:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 孙健波(天元)阿里云技术专家
导读 :OAM Spec 经历了近 3 个月的迭代, v1alpha2  版本终于发布啦!新版本在坚持 OAM Spec 平台无关的基础上,整体变得更 Kubernetes 友好化,很大程度上平衡了标准与可扩展性,更好的支持 CRD。如果你已经编写了现成的 CRD Operator,可以平滑的接入到 OAM 体系中,并且享受到 OAM 模型的红利。
目前 OAM 已经成为了包括阿里、微软、Upbond、谐云等多家公司构建云产品的核心架构。他们通过 OAM 构建了“以应用为中心”、用户友好化的 Kubernetes PaaS;充分发挥 OAM 的标准化与可扩展性,实现 OAM 核心 Controller 的同时,快速接入了已有的 Operator 能力;通过 OAM 横向打通多个模块,破除了原有 Operator 彼此孤立、无法复用的窘境。
了解 OAM 的背景及由来,可以参考 《深度解读!阿里统一应用管理架构升级的教训与实践》 ; OAM 能为终端用户带来哪些价值?可以参考 《OAM 深入解读:OAM 为云原生应用带来哪些价值?》
下面言归正传,让我们来看一下 v1alpha2 到底做了哪些改动?
主要改动说明
为了方便大家阅读,这里只罗列了最主要的改动点,一些细节还是以上游 OAM Spec Github 仓库为准。
术语说明 CRD(Custom Resource Definition):在 OAM 中说的 CRD 是一种泛指的自定义资源描述定义。在 K8s 的 OAM 实现中可以完全对应 K8s 的 CRD,在非 K8s 的实现中,OAM 的 CRD 需要包含 APIVersion/Kind 并且能够描述字段进行校验; CR (Custom Resource),OAM 中的 CR 是 CRD 的一个实例,是符合 CRD 中字段格式定义的一个资源描述。在 K8s 的 OAM 实现中可以完全对应 K8s 的 CR,在 非 K8s 的实现中,可以需要对齐 APIVersion/Kind  和字段格式定义。
主要改动 1 使用 Reference 模型定义 Workload、Trait 和 Scope
 v1alpha1 原先的方式是这样的: // 老版本,仅对比使用 apiVersion: core.oam.dev/v1alpha1 kind: WorkloadType metadata: name: OpenFaaS annotations: version: v1.0.0 description: "OpenFaaS a Workload which can serve workload running as functions" spec: group: openfaas.com version: v1alpha2 names: kind: Function singular: function plural: functions workloadSettings: | { "$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "required": [ "name", "image" ], "properties": { "name": { "type": "string", "description": "the name to the function" }, "image": { "type": "string", "description": "the docker image of the function" } } }
在原先的模式中,group/version/kind 分别是字段,spec 的校验通过 jsonschema 表示,整体的格式实际上类似 CRD,但不完全一致。
新版 v1alpha2 中彻底改为了引用模型,通过 WorkloadDefinition   TraitDefinition   ScopeDefinition 的形式,描述了一个引用关系。可以直接引用一个 CRD,name 就是 CRD 的名称。对于非 K8s 的 OAM 实现来说,这里的名字则是一个索引,可以找到类似 CRD 的校验文件,校验文件中包含 apiVersion 和 kind,以及相应的 schema 校验。 Workload apiVersion: core.oam.dev/v1alpha2 kind: WorkloadDefinition metadata: name: containerisedworkload.core.oam.dev spec: definitionRef: # Name of CRD. name: containerisedworkload.core.oam.dev Trait apiVersion: core.oam.dev/v1alpha2 kind: TraitDefinition metadata: name: manualscalertrait.core.oam.dev spec: appliesToWorkloads: - containerizedworkload.core.oam.dev definitionRef: name: manualscalertrait.core.oam.dev Scope apiVersion: core.oam.dev/v1alpha2 kind: ScopeDefinition metadata: name: networkscope.core.oam.dev spec: allowComponentOverlap: true definitionRef: name: networkscope.core.oam.dev
注意:  这里对于 K8s 的 OAM 实现来说,name 就是 K8s 里面 CRD 的 name,由 . 组成。社区的最佳实践是一个 CRD 只有一个 version 在集群中运行,一般新 version 都会向前兼容,升级时都一次性替换到最新 version。如果确实有 2 个 version 同时存在的场景,用户也可以通过 kubectl get crd 的方式进一步选择; Definition 这一层不面向 end user,主要给平台实现使用,对于非 K8s 实现来说,如果存在多个 version 的场景,OAM 的实现平台可以给终端用户展示不同 version 的选择。
主要改动 2 直接嵌入 K8s CR 作为 Component 和 Trait 实例
原先的方式在 Workload 和 Trait 层面我们都只把 CR 的 spec 部分拿出来,分别放在 workloadSettings 和 properties 字段里。
这样的方式虽然已经可以“推导”出 K8s CR,但是不利于 K8s 生态内的 CRD 接入,需要换种格式重新定义一遍 spec。 // 老版本,仅对比使用 apiVersion: core.oam.dev/v1alpha1 kind: ComponentSchematic metadata: name: rediscluster spec: workloadType: cache.crossplane.io/v1alpha1.RedisCluster workloadSettings: engineVersion: 1.0 region: cn // 老版本,仅对比使用 apiVersion: core.oam.dev/v1alpha1 kind: ApplicationConfiguration metadata: name: custom-single-app annotations: version: v1.0.0 description: "Customized version of single-app" spec: variables: components: - componentName: frontend instanceName: web-front-end parameterValues: traits: - name: manual-scaler properties: replicaCount: 5
现在的方式则直接嵌入 CR,可以看到,在 workload 和 trait 字段下面是完整的 CR 描述。 apiVersion: core.oam.dev/v1alpha2 kind: Component metadata: name: example-server spec: prameters: - name: xxx fieldPaths: - "spec.osType" workload: apiVersion: core.oam.dev/v1alpha2 kind: Server spec: osType: linux containers: - name: my-cool-server image: name: example/very-cool-server:1.0.0 ports: - name: http value: 8080 env: - name: CACHE_SECRET apiVersion: core.oam.dev/v1alpha2 kind: ApplicationConfiguration metadata: name: cool-example spec: components: - componentName: example-server traits: - trait: apiVersion: core.oam.dev/v1alpha2 kind: ManualScalerTrait spec: replicaCount: 3
这样的好处很明显: 可以很容易的对接现有 K8s 体系里的 CRD,甚至包括 K8s 原生的 Deployment (作为自定义 workload 接入)等资源; K8s CR 层面的字段定义是成熟的,解析和校验也完全交由 CRD 体系。
这里大家注意到 traits 下面是 []trait{CR} 而不是 []CR   的结构,多了一层看似无用的 trait 字段,主要由如下 2 个原因: 为后续在 trait 这个维度做扩展留下空间,比如可能的编排( ordering ) 等。 非 K8s 体系在这一层可以不严格按照 CR 的写法来,完全自定义,不绑定 K8s 描述格式。
主要改动 3 参数传递使用 jsonPath 替换原先的 fromParam
研发能够留出字段给运维覆盖 ,一直是 OAM 很重要的功能。
体现在 OAM Spec 的流程上就是:研发在 Component 里面定义 parameter,运维在 AppConfig 里面通过 parameterValue 去覆盖对应的参数。
最初的参数传递,是在每个字段后面有个 fromParam   字段,对于支持了自定义 schema 后,这样的方式显然是无法覆盖所有场景的: // 老版本,仅对比使用 apiVersion: core.oam.dev/v1alpha1 kind: ComponentSchematic metadata: name: rediscluster spec: workloadType: cache.crossplane.io/v1alpha1.RedisCluster parameters: - name: engineVersion type: string workloadSettings: - name: engineVersion type: string fromParam: engineVersion
后来我们曾经提议过这样的方案: // 老版本,仅对比使用 apiVersion: core.oam.dev/v1alpha1 kind: ComponentSchematic metadata: name: rediscluster spec: workloadType: cache.crossplane.io/v1alpha1.RedisCluster parameters: - name: engineVersion type: string workloadSettings: engineVersion: "[fromParam(engineVersion)]"
这个方案最大的问题是 静态的 IaD (Infrastructure as Data) 里面加入了动态的函数,给理解和使用带来了复杂性。
经过多方面的讨论,在新方案里我们通过 JsonPath 的形式描述要注入的参数位置,在用户理解上保证了 AppConfig 是静态的。 apiVersion: core.oam.dev/v1alpha2 kind: Component metadata: name: example-server spec: workload: apiVersion: core.oam.dev/v1alpha2 kind: Server spec: containers: - name: my-cool-server image: name: example/very-cool-server:1.0.0 ports: - name: http value: 8080 env: - name: CACHE_SECRET value: cache parameters: - name: instanceName required: true fieldPaths: - ".metadata.name" - name: cacheSecret required: true fieldPaths: - ".workload.spec.containers[0].env[0].value"
fieldPaths 是个数组,每个元素定义了参数和对应 Workload 里的字段。 apiVersion: core.oam.dev/v1alpha2 kind: ApplicationConfiguration metadata: name: my-app-deployment spec: components: - componentName: example-server parameterValues: - name: cacheSecret value: new-cache
在 AppConfig 中还是走 parameterValues 去覆盖 Component 中的 parameter。
主要改动 4 ComponentSchematic 名称改为 Component
原先组件这个概念叫 ComponentSchematic,这样命名的主要原因是里面混了一些语法描述和选择,比如针对 Core Workload( container )和针对扩展 Workload ( workloadSettings ),写法上不一样的, container  里是定义具体的参数, workloadSettings  更像是 schema(参数怎么填的说明)。v1alpha1 版本的 WorkloadSetting 还融入了 type/description之类的,更显得模棱两可。 // 老版本,仅对比使用 apiVersion: core.oam.dev/v1alpha1 kind: ComponentSchematic metadata: name: rediscluster spec: containers: ... workloadSettings: - name: engineVersion type: string description: engine version fromParam: engineVersion ...
在 v1alpha2 版本中,组件这个概念改为 Component,明确为 Workload 的实例,所有语法定义都是在WorkloadDefinition 中引用的实际 CRD 定义的。
在 K8s 实现中,WorkloadDefinition 就是引用 CRD ,Component.spec.workload 里就是写 CRD 对应的实例 CR。 apiVersion: core.oam.dev/v1alpha2 kind: Component metadata: name: example-server spec: workload: apiVersion: core.oam.dev/v1alpha2 kind: Server spec: ...
主要改动 5 Scope 由 CR 单独创建,不再由 AppConfig 创建
v1alpha1 中的 Scope 是由 AppConfig 创建的,从例子中可以看出,本质上它也是个 CR,可以“推导”创建出 CR来。但是由于 Scope 的定位是可以容纳不同 AppConfig 中的 Component,且 Scope 本身并非一个 App,所以使用 AppConfig 创建 Scope 一直是不太合适的。 // 老版本,仅对比使用 apiVersion: core.oam.dev/v1alpha1 kind: ApplicationConfiguration metadata: name: my-vpc-network spec: variables: - name: networkName value: "my-vpc" scopes: - name: network type: core.oam.dev/v1alpha1.Network properties: network-id: "[fromVariable(networkName)]" subnet-ids: "my-subnet1, my-subnet2"
v1alpha2 新版本全面使用 CR 来对应实例,为了让 Scope 的概念更清晰,更方便对应不同类型的 Scope,将 Scope 拿出来直接由 ScopeDefinition 定义的 CRD 对应的 CR 创建。例子如下所示: apiVersion: core.oam.dev/v1alpha2 kind: ScopeDefinition metadata: name: networkscope.core.oam.dev spec: allowComponentOverlap: true definitionRef: name: networkscope.core.oam.dev apiVersion: core.oam.dev/v1alpha2 kind: NetworkScope metadata: name: example-vpc-network labels: region: us-west environment: production spec: networkId: cool-vpc-network subnetIds: - cool-subnetwork - cooler-subnetwork - coolest-subnetwork internetGatewayType: nat
在 AppConfig 中使用 scope 引用如下所示: apiVersion: core.oam.dev/v1alpha2 kind: ApplicationConfiguration metadata: name: custom-single-app annotations: version: v1.0.0 description: "Customized version of single-app" spec: components: - componentName: frontend scopes: - scopeRef: apiVersion: core.oam.dev/v1alpha2 kind: NetworkScope name: my-vpc-network - componentName: backend scopes: - scopeRef: apiVersion: core.oam.dev/v1alpha2 kind: NetworkScope name: my-vpc-network
主要改动 6 移除 Variable 列表和 [fromVariable()] 动态函数
v1alpha1 版本中有 Variable 是为了 AppConfig 里开源引用一些公共变量减少冗余,所以加入了 Variable 列表。但实践来看,减少的冗余并没有明显减少 OAM spec 的复杂性,相反,增加动态的函数显著增加了复杂性。
另一方面,fromVariable 这样的能力完全可以通过 helm template/ kustomiz 等工具来做,由这些工具渲染出完整的 OAM spec,再进行使用。
所以这里 variables 列表和相关的 fromVariable 均去掉,并不影响任何功能。 // 老版本,仅对比使用 apiVersion: core.oam.dev/v1alpha1 kind: ApplicationConfiguration metadata: name: my-app-deployment spec: variables: - name: VAR_NAME value: SUPPLIED_VALUE components: - componentName: my-web-app-component instanceName: my-app-frontent parameterValues: - name: ANOTHER_PARAMETER value: "[fromVariable(VAR_NAME)]" traits: - name: ingress properties: DATA: "[fromVariable(VAR_NAME)]"
主要改动 7 用 ContainerizedWorkload 替代原来的六种核心 Workload
因为现在已经统一用 WorkloadDefinition 定义 Workload,Component 变成了实例,所以原先的六种核心Workload 实际上都变成了同一个 WorkloadDefinition,字段描述完全一样,唯一的不同是对 trait 约束和诉求不一样。因此原先的六种核心 Workload 的 spec,统一修改为一种名为 ContainerizedWorkload 的 Workload 类型。
同时,这里计划要通过增加 policy 这样的概念,来让研发表达对运维策略的诉求,即 Component 中可以表达希望增加哪些 trait。 apiVersion: core.oam.dev/v1alpha2 kind: WorkloadDefinition metadata: name: containerizedworkloads.core.oam.dev spec: definitionRef: name: containerizedworkloads.core.oam.dev
一个使用 ContainerizedWorkload 示例: apiVersion: core.oam.dev/v1alpha2 kind: Component metadata: name: frontend annotations: version: v1.0.0 description: "A simple webserver" spec: workload: apiVersion: core.oam.dev/v1alpha2 kind: ContainerizedWorkload metadata: name: sample-workload spec: osType: linux containers: - name: web image: example/charybdis-single:latest@@sha256:verytrustworthyhash resources: cpu: required: 1.0 memory: required: 100MB env: - name: MESSAGE value: default parameters: - name: message description: The message to display in the web app. required: true type: string fieldPaths: - ".spec.containers[0].env[0].value"
下一步计划 应用级别的组件间参数传递和依赖关系( workflow ); Policy 方案 ,便于研发在 Component 对 trait 提出诉求; Component 增加版本的概念,同时给出 OAM 解决应用版本发布相关方式 。
常见 FAQ 我们原有平台改造为 OAM 模型实现需要做什么?
对于原先是 K8s 上的应用管理平台,接入改造为 OAM 实现可以分为两个阶段: 实现 OAM ApplicationConfiguration Controller(简称 AppConfig Controller),这个 Controller 同时包含 OAM 的 Component、WorkloadDefinition、TraitDefinition、ScopeDefinition 等 CRD。AppConfig Controller 根据 OAM AppConfig 中的描述,拉起原有平台的 CRD Operator; 逐渐将原先的 CRD Operator 根据关注点分离的思想,分为 Workload 和 Trait。同时接入和复用 OAM 社区中更多的 Workload、Trait,丰富更多场景下的功能。
  现有的 CRD Operator 为接入 OAM 需要做什么改变?
现有的 CRD Operator** 功能上可以平滑接入 OAM 体系,比如作为一个独立扩展 Workload 接入。但是为了更好的让终端用户体会到 OAM 关注点分离的好处,我们强烈建议 CRD Operator 根据研发和运维不同的关注点分离为不同的 CRD,研发关注的 CRD 作为 Workload 接入 OAM,运维关注的 CRD 作为 Trait 接入 OAM。
目前,OAM 规范和模型实际已解决许多现有问题,但它的路程才刚刚开始。OAM 是一个中立的开源项目,我们欢迎更多的人参与其中,共同定义云原生应用交付的未来。
参与方式: 钉钉扫码进入 OAM 项目中文讨论群
通过 Gitter 直接参与讨论 OAM 开源实现地址 点击 star 一下
作者简介
孙健波(花名:天元)阿里云技术专家,是 OAM 规范的主要制定者之一,致力于推动云原生应用标准化。同时也在阿里巴巴参与大规模云原生应用交付与应用管理相关工作。团队诚邀应用交付、Serverless、PaaS 领域的专家加入,欢迎联系  jianbo.sjb AT alibaba-inc.com
招人啦!
云原生应用平台诚邀 Kubernetes / Serverless / PaaS / 应用交付领域专家( P6-P8 )加盟: 工作年限:建议 P6-7 三年起,P8 五年起,具体看实际能力; 工作地点:国内(北京 / 杭州 / 深圳);海外(旧金山湾区 / 西雅图); 岗位包含:架构师、技术专家、全栈工程师等。
简历立刻回复,2~3 周出结果,简历投递:jianbo.sjb AT alibaba-inc.com。
“ 阿里巴巴云原生 关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的技术圈。”
云计算
2020-04-01 11:47:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
Pod安全策略对于强化K8S集群安全至关重要。本文将延续之前的文章继续深入介绍Pod安全策略。
首先,简单介绍了如何将Pod与Pod安全策略相关联,并使用RBAC来展示具体步骤。然后介绍如何在Rancher中启用默认的PSP和创建自定义PSP。最后将使用一种工具来简化生产中Pod安全策略的使用,极大提升生产力,赶紧戳文咯~
本文来自 RancherLabs
在 之前的文章 中,我们演示了如何使用受限的PSP策略作为默认值在Rancher中启用PSP。我们还展示了如何防止特权Pod被接纳到集群中。
我们有意省略了有关基于角色的访问控制(RBAC)以及如何将Pod与特定PSP连接的具体细节。那么,这篇文章让我们继续深入研究PSP。
将Pod与Pod 安全策略匹配
你可能已经注意到,PSP模式没有与任何Kubernetes命名空间、Service Account或Pod相关联。实际上,PSP是集群范围的资源。那么,我们如何指定哪些Pod应该由哪些PSP来管理呢?下图显示了所有参与组件、资源以及准入流程的工作方式。
也许一开始听起来很复杂。现在,我们来详细介绍一下。
部署Pod时,准入控制将根据请求deployment的对象来应用策略。
Pod本身没有任何关联的策略——执行该Deployment的是service account。在上图中,Jorge使用webapp-sa service account部署了pod。
RoleBinding将service account与Roles(或ClusterRoles)相关联,Role是指定可以使用PSP的资源。在该图中,webapp-sa与webapp-role关联,后者为特定的PSP资源提供使用许可。部署Pod时,将根据webapp-sa PSP对Pod进行检查。实际上,一个service account可以使用多个PSP,并且其中一个可以验证Pod就足够了。你可以在官方文档中查看详细信息:
https://kubernetes.io/docs/concepts/policy/pod-security-policy/#policy-order
然后,准入控制将决定Pod是否符合其中任何一个PSP。如果Pod符合要求,准入控制将调度Pod;如果Pod不符合规定,则会阻止部署。
以上内容可以总结为以下几点: Pod身份由其service account确定 如果规范中未声明任何service account,则将使用默认账户 你需要允许使用声明Role或ClusterRole
最后,需要有一个RoleBinding,它将Role(从而允许访问使用PSP)与Pod规范中声明的Servcie Account相关联。
让我们用一些例子来说明。
RBAC的真实示例
假设你已经有一个启用了PSP的集群,这是采用PSP创建限制性PSP的常用方法,该PSP可以被任意Pod使用。然后你将添加更为特定的PSP,该PSP有绑定到特定service account的其他特权。拥有默认、安全且严格的策略有助于集群的管理,因为大多数Pod不需要特殊的特权或功能,并且在默认情况下即可运行。然后,如果你的某些工作负载需要其他特权,我们可以创建一个自定义PSP并将该工作负载的特定service account绑定到限制较少的PSP。
但是,如何将Pod绑定到特定的PSP而不是默认的受限PSP?以及如何使用不自动添加RoleBindings的普通Kubernetes集群来做到这一点?
让我们看一个完整的示例,在该示例中,我们定义一些安全的默认值(集群中任何service account都可以使用的受限PSP),然后为需要该服务的特定deployment向单个service account提供其他特权。
首先,我们手动创建一个新的命名空间。它不会由Rancher管理,所以不会自动创建RoleBindings。然后我们在该命名空间中尝试部署一个受限的Pod: $ kubectl create ns psp-test $ cat deploy-not-privileged.yaml apiVersion: apps/v1 kind: Deployment metadata: labels: app: not-privileged-deploy name: not-privileged-deploy spec: replicas: 1 selector: matchLabels: app: not-privileged-deploy template: metadata: labels: app: not-privileged-deploy spec: containers: - image: alpine name: alpine stdin: true tty: true securityContext: runAsUser: 1000 runAsGroup: 1000 $ kubectl -n psp-test apply -f deploy-not-privileged.yaml $ kubectl -n psp-test describe rs ... Warning FailedCreate 4s (x12 over 15s) replicaset-controller Error creating: pods "not-privileged-deploy-684696d5b5-" is forbidden: unable to validate against any pod security policy: []
由于命名空间psp-test中没有RoleBinding,且该命名空间绑定到允许使用任何PSP的角色,因此无法创建pod。我们将通过创建集群范围的ClusterRole和ClusterRoleBinding来解决此问题,以允许任何Service Account默认使用受限的PSP。之前的文章中启用PSP时,Rancher创建了受限PSP。 resourceNames: - restricted-psp --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: restricted-role-bind roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: use-restricted-psp subjects: - apiGroup: rbac.authorization.k8s.io kind: Group name: system:serviceaccounts $ kubectl apply -f clusterrole-use-restricted.yaml
我们应用这些更改之后,非特权deployment应正常工作。
但是,如果我们需要部署特权Pod,则不会被现有策略允许: $ cat deploy-privileged.yaml apiVersion: v1 kind: ServiceAccount metadata: name: privileged-sa namespace: psp-test --- apiVersion: apps/v1 kind: Deployment metadata: labels: app: privileged-deploy name: privileged-deploy namespace: psp-test spec: replicas: 1 selector: matchLabels: app: privileged-deploy template: metadata: labels: app: privileged-deploy spec: containers: - image: alpine name: alpine stdin: true tty: true securityContext: privileged: true hostPID: true hostNetwork: true serviceAccountName: privileged-sa $ kubectl -n psp-test apply -f deploy-privileged.yaml $ kubectl -n psp-test describe rs privileged-deploy-7569b9969d Name: privileged-deploy-7569b9969d Namespace: default Selector: app=privileged-deploy,pod-template-hash=7569b9969d Labels: app=privileged-deploy ... Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedCreate 4s (x14 over 45s) replicaset-controller Error creating: pods "privileged-deploy-7569b9969d-" is forbidden: unable to validate against any pod security policy: [spec.securityContext.hostNetwork: Invalid value: true: Host network is not allowed to be used spec.securityContext.hostPID: Invalid value: true: Host PID is not allowed to be used spec.containers[0].securityContext.privileged: Invalid value: true: Privileged containers are not allowed]
在这种情况下,由于我们创建了ClusterRoleBinding,所以Pod可以使用PSP,但是restricted-psp不会验证Pod,因为它需要privileged 、hostNetwork等参数。
我们已经在前文中看到了restricted-psp策略。让我们检查一下default-psp的细节,这是由Rancher在启用PSP允许这些特权时创建的: $ kubectl get psp default-psp -o yaml apiVersion: policy/v1beta1 kind: PodSecurityPolicy metadata: annotations: seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*' creationTimestamp: "2020-03-10T08:45:08Z" name: default-psp resourceVersion: "144774" selfLink: /apis/policy/v1beta1/podsecuritypolicies/default-psp uid: 1f83b803-bbee-483c-8f66-bfa65feaef56 spec: allowPrivilegeEscalation: true allowedCapabilities: - '*' fsGroup: rule: RunAsAny hostIPC: true hostNetwork: true hostPID: true hostPorts: - max: 65535 min: 0 privileged: true runAsUser: rule: RunAsAny seLinux: rule: RunAsAny supplementalGroups: rule: RunAsAny volumes: - '*'
你可以看到这是一个非常宽松的策略,特别是我们允许privileged、hostNetwork、hostPID、hostIPC、hostPorts以及以root身份运行以及其他功能。
我们只需要明确允许该Pod使用PSP。我们通过创建类似于现有restricted-clusterrole的ClusterRole,但允许使用default-psp资源,然后为我们的psp-test 命名空间创建RoleBinding来将privileged-sa ServiceAccount绑定到该ClusterRole: $ cat clusterrole-use-privileged.yaml --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: use-privileged-psp rules: - apiGroups: ['policy'] resources: ['podsecuritypolicies'] verbs: ['use'] resourceNames: - default-psp --- apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: name: privileged-role-bind namespace: psp-test roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: use-privileged-psp subjects: - kind: ServiceAccount name: privileged-sa $ kubectl -n psp-test apply -f clusterrole-use-privileged.yaml
一会儿之后,特权Pod将会被创建。然后你会注意到restricted-psp和default-psp现在已经可以直接使用。接下来,我们来详细介绍一下它们。
在Rancher上默认的PSP
在Rancher中通过编辑集群设置来启用PSP准入控制,并选择其中一个已经定义好的PSP作为默认选项:
Rancher将在集群中创建一对PSP资源: restricted-psp:如果你选择“受限(restricted)”作为默认的PSP default-psp:默认的PSP,允许创建特权Pod。
除了restricted-psp和default-psp,Rancher还会创建名为 restricted-clusterrole的ClusterRole: annotations: serviceaccount.cluster.cattle.io/pod-security: restricted creationTimestamp: "2020-03-10T08:44:39Z" labels: cattle.io/creator: norman name: restricted-clusterrole rules: - apiGroups: - extensions resourceNames: - restricted-psp resources: - podsecuritypolicies verbs: - use
此ClusterRole允许使用restricted-psp策略。那么Binding在何处才可以允许授权使用pod ServiceAccount 呢?
对于属于Rancher中项目的命名空间,它还在其中为你设置RoleBinding配置: $ kubectl -n default get rolebinding default-default-default-restricted-clusterrole-binding -o yaml apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: annotations: podsecuritypolicy.rbac.user.cattle.io/psptpb-role-binding: "true" serviceaccount.cluster.cattle.io/pod-security: restricted labels: cattle.io/creator: norman name: default-default-default-restricted-clusterrole-binding namespace: default ... roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: restricted-clusterrole subjects: - kind: ServiceAccount name: default namespace: default
资源名称(default-default-default-restricted-clusterrole-binding)可能会令人感到困惑,实际上它的构成为:
default-serviceaccountname-namespace-restricted-clusterrole-binding
并且如果你创建一个类似myserviceaccount的新的service account,那么将会自动创建一个新的角色绑定: $ kubectl create sa myserviceaccount serviceaccount/myserviceaccount created $ kubectl get rolebinding NAME AGE --- default-default-default-restricted-clusterrole-binding 13m default-myserviceaccount-default-restricted-clusterrole-binding 4s
借助这种神奇的功能,你无需为不需要任何提升特权的安全pod配置RBAC。
在Rancher中创建你的PSP
PSP是一个标准的Kubernetes资源,全程是Pod安全策略,所以你可以通过Kubernetes API或kubectl CLI来使用它。
你可以通过在YAML文件中定义它们来创建你的自定义PSP,然后使用kubectl在集群中创建资源。查看官方文档即可了解所有可用控件( https://kubernetes.io/docs/concepts/policy/pod-security-policy/ )。在YAML中定义了控件集之后,你可以运行: $ kubectl create psp my-custom-psp
以创建PSP资源。
使用Rancher,你可以从UI中直接查看或添加新的策略:
PSP十分强大,但也十分复杂
配置Pod安全策略是一个十分乏味的过程。你在Kubernetes集群中启用PSP之后,你想要部署的任意Pod都必须经由其中一个PSP允许。实施强大的安全策略可能十分耗时,而且为每个应用程序的每个deployment生成一个策略也是一种负担。如果你的策略十分宽松,那么不强制执行最小特权访问方法;然而,如果它存在很多限制,你可能会破坏你的应用程序,因为Pod无法在Kubernetes中成功运行。
能够以最少的访问要求集自动生成Pod安全策略,将帮助你更轻松地安装PSP。并且你不能只在生产环境中部署它们,而不验证你的应用程序是否可以正常工作,而且进行手动测试既繁琐又效率低下。如果你可以针对Kubernetes工作负载的运行时行为验证PSP呢?
如何简化生产环境中PSP的使用?
Kubernetes Pod安全策略 Advisor(又名kube-psp-advisor)是一个Sysdig的开源工具。kube-psp-advisor会从Kubernetes资源(如deployment、daemonset、replicaset等)中扫描现有的安全上下文,将其作为我们想要执行的reference模型,然后在整个集群中为所有资源自动生成Pod安全策略。kube-psp-advisor通过查看不同的属性以创建推荐的Pod安全策略: allowPrivilegeEscalation allowedCapabilities allowedHostPaths hostIPC hostNetwork hostPID Privileged readOnlyRootFilesystem runAsUser Volume
查看kube-psp-advisor教程,以获取有关其工作原理的更多详细信息:
https://sysdig.com/blog/enable-kubernetes-pod-security-policy/
使用Sysdig Secure自动生成PSP
Sysdig Secure Kubernetes Policy Advisor可以帮助用户创建和验证Pod安全策略。
第一步是设置新的PSP模拟环境。你可以针对不同范围内的不同策略(例如Kubernetes命名空间)进行多种模拟。
Sysdig在你的Deployment定义中分析Pod规范的要求,并为你的应用程序创建权限最小的PSP。这可以控制是否允许特权Pod,用户将其作为容器、volume等运行。Ni 可以微调PSP并针对你将要运行的模拟环境定义命名空间:
左侧的策略会破坏应用程序,因为nginx Deployment是特权Pod,并且具有主机网络访问权限。你必须决定是扩大PSP以允许这种行为,还是选择减少Deployment的特权以适应该策略。无论如何,你在应用PSP之前都会检测到此情况,这会阻止Pod运行并在应用程序部署上造成破坏。
结 论
如这些示例所示,通过授予或拒绝对特定资源的访问,PSP使你可以对在Kubernetes中运行的Pod和容器进行精细控制。这些策略相对来说容易创建和部署,并且应该是任何Kubernetes安全策略的有用组件。
云计算
2020-04-01 10:50:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> Serverless 架构是云发展的产物,是一种去服务器化更加明显的架构。然而,细心的朋友可能会发现,有一个开发者工具也叫 Serverless,那么 Serverless 到底是一个架构,还是一个开发者工具呢?这个开发者工具和 Serverless 架构又有什么关系呢?
初探 Serverless 开发者工具
Serverless 架构开始发展没多久,就有一群人注册了 serverless.com 的域名,成立了一家叫 Serverless 的公司,同时还开发了一款同名工具。
Serverless 架构和 Serverless 开发者工具是两个不同的东西,如果类比一下的话,就相当于中国电信,一方面指的是中国电信行业,另一方面也指的是中国电信运营商。
从 Serverless 的公司名称,我们也可以推断出其推出的产品与 Serverless 架构紧密相关。在各个云厂商都有自己函数计算业务的时候,Serverless 团队做了一个类似多云管理平台的工具,可以认为是多 Serverless 管理的工具。利用这个工具,可以快速直接使用 AWS 的 Lambda、Azure 的 Funtions 以及腾讯云 SCF 等众多云厂商的函数计算相关服务,大体支持的功能如下:
通过这个上表,大家也可以感觉到这其实是个开发者工具,帮助用户快速使用多个云厂商的函数服务,打包、部署、回滚…当然,各个厂商也都推出类似的工具,例如 AWS 的 SAM、腾讯云的 SCFCLI 等。
除了一个以函数计算为核心的多云开发者工具之外,Serverless 公司还推出了组件化工具:Components。换句话说,Serverless 开发者工具不仅仅关注 Serverless 中的 FaaS,也要关注 BaaS,将 API 网关、对象存储、CDN、数据库等众多的后端服务和函数计算有机集合,让用户可以一站式开发,一站式部署,一站式更新,一站式维。
Serverless Framework 开发者工具可以被一分为二:Plugin 和 Components。
如果说最初的 Serverless Cli 更多是一种以插件(Plugin)形式提供各个云厂商的函数计算功能,那么这个叫 Components 的功能更多就是以各个云厂商整体服务为基础,来帮助用户快速将项目部署到 Serverless 架构上。
所谓的 Components 可以认为是很多 Component 的组合,例如如果部署一个网站,可能会需要有以下部分:静态资源部分、函数计算部分、API 网关部分、CDN 部分、域名解析部分等,而 Components 就可以帮我们一站式部署这些资源,将静态资源部署到对象存储中,将函数计算部分部署到函数中,将 API 网关、CDN 等业务部署到对应的产品或者服务中,如果有域名解析需求,会自动解析域名,同时将整个项目的所有资源进行关联。
除了一键部署、自动关联之外,Components 还提供了若干的传统 Web 框架部署到 Serverless 架构的解决方案,用户可将自己已有的或者使用这些框架新开发的项目,直接一键部署到云端,对开发者来说这是一个巨大的便利。
用户如何使用 Plugin 和 Components 呢?其实这两个功能都是 Serverless Cli 作为承载,也就是说,只要我们安装了 Serverless Framework 这个开发者工具,就可以同时使用这两个功能。
安装 Serverless Framework 开发者工具的过程也很简单: 安装 Nodejs,官方说的 nodejs 只需要 6 以上就好,但是在实际使用过程中,发现 6 不行,至少 8 以上才可以。 安装 Serverless 开发者工具: npm install -g serverless ,安装完成之后可以通过 serverless -v 查看版本号,来确定是否成功的安装该工具。
至于如何使用 Serverless Framework 开发者工具,可以参考接下来的 Plugin 和 Components 部分。
什么是 Serverless Plugin
首先,什么是 Plugin,Serverless Framework Plugin 实际上是一个函数的管理工具,使用这个工具,可以很轻松的部署函数、删除函数、触发函数、查看函数信息、查看函数日志、回滚函数、查看函数数据等。
Plugin 的使用比较简单,可以直接使用 Serverlss Framework 进行创建,例如: serverless create -t tencent-python -p mytest
然后就会生成下图:
这其中,-t 指的是模板,-p 指的是路径,在 Serverless Plugin 操作下,可以在任何指令中使用 -h 查看帮助信息,例如查看 Serverless Plugin 的全部指令,可以直接:
如果想查看 Create 的帮助:
创建完 Serverless Plugin 的项目之后,我们可以看一下它的 Yaml 长什么样子:
通过 Yaml,我们可以看到其从上到下包括了几个主要的 Key:Service、Provider、Plugins 以及 Functions。
Service 可以认为是一个服务或分组,即在一个 Service 下面的函数是可以被统一管理的,例如部署、删除、查看统计信息等。
Provider 可以认为是供应商以及全局变量的定义场景,这里使用的是腾讯云的云函数,供应商是腾讯云,所以就要写 tencent,同时在这里还可以定义全局变量,这样在部署的时候,会将这些全局变量分别配置到不同的函数中。
Plugin 就是插件,Serverless 团队提供了超级多的 Plugin,例如上文提到的 serverless-tencent-scf。
最后就是 Functions,是定义函数的地方。
创建项目,完成代码编写和 Yaml 的配置之后,接下来就是安装 Plugin: npm install
使用相关功能,例如部署服务: serverless deploy
在使用这个工具部署的时候,我们并没事先指定账号信息,所以它会自动唤起扫码登录,登陆之后会继续进行操作:
操作完成会看到 Service 信息,这里要注意,如果是使用 CICD,就没办法扫码了,必须手动配置账户信息,格式是: [default] tencent_appid = appid tencent_secret_id = secretid tencent_secret_key = secretkey
配置完成之后,在 Yaml 中指定这个文件路径:
完成部署之后,触发函数:
服务信息:
除此之外,还有很多其它操作,大家有兴趣可以都试一下: 创建服务 打包服务 部署服务 部署函数 云端调用 查看日志 回滚服务 删除服务 获取部署列表 获取服务详情 获取统计数据

需要注意的是,Plugin 是函数开发者工具,只针对对函数资源的管理(触发器除外),不包括 API 网关、COS、数据库、CDN 等。另外,在腾讯云函数中只有命名空间和函数的概念,但是在 Serverless Framework Plugin 中却有 Service、Stage 以及函数的三层概念,同时云函数在 Plugin 不支持命名空间,所以我们可以理解为,云函数只有函数的概念,而工具却有服务、阶段和函数的三层概念,这就会产生问题:Service 和 Stage 是什么?在函数中怎么体现?
以我们刚才部署的 hello_world 为例:
从上图可以看到,Service 和 Stage 体现在函数名和标签两个地方。函数名在简单使用时可能没有影响,但如果涉及到函数间调用或者是云 API 使用函数时,就要注意,这里的函数名并不是在 Yaml 中的函数名!
当然,这里也会出现另一个问题,即如果用户已经有一个函数,且这个函数不是按照三段式命名的,那么可能没有办法使用 Plugin 进行部署,除非把函数进行迁移,将原函数删掉,使用 Serverless 重新进行部署。
什么是 Serverless Component
Plugin 主要是对函数的管理,那么 Component 呢?Component 可以认为是云产品的工具,因为通过 Componnt 可以对所有的组件进行组合使用,甚至还可以很简单开发出自己的 Component 来满足需求。
Component 的 Yaml 是一段一段的,而 Plugin 的 Yaml 是一个整体,Component 中前后两个组件可能是完全没有任何关系的,例如: test1: component: "@gosls/tencent-website" inputs: code: src: ./public index: index.html error: index.html region: ap-shanghai bucketName: test1 test2: component: "@gosls/tencent-website" inputs: code: src: ./public index: index.html error: index.html region: ap-shanghai bucketName: test2
通过 Yaml 我们可以看到整个的代码可以分为两部分,是把一个网站的代码放到了不同的 Bucket。
目前腾讯云的 Component 的基础组件包括: @serverless/tencnet-scf @serverless/tencnet-cos @serverless/tencnet-cdn @serverless/tencnet-apigateway @serverless/tencnet-cam-role @serverless/tencnet-cam-policy
封装的上层 Component 包括: @serverless/tencnet-express @serverless/tencnet-bottle @serverless/tencnet-django @serverless/tencnet-egg @serverless/tencnet-fastify @serverless/tencnet-flask @serverless/tencnet-koa @serverless/tencnet-laravel @serverless/tencnet-php-slim @serverless/tencnet-pyramid @serverless/tencnet-tornado @serverless/tencnet-website
基础 Component 指的是可通过相关的 Component 部署相关的资源,例如 tencent-scf 就可以部署云函数,tencent-cos 就可以部署一个存储桶;上层的 Component 实际上就是对底层 Component 的组合,同时增加一些额外的逻辑,实现一些高阶功能,例如 tencent-django 就可以通过对请求的 WSGI 转换,将 Django 框架部署到云函数上,其底层依赖了 tencent-scf/tencent-apigateway 等组件。
相对于 Plugin 而言,Component 并没有那么多的操作,只有两个:部署和移除。
例如部署操作: serverless --debug
移除操作: serverless remove --debug
相对于 Plugin 而言,Component 的产品纬度是增加了,但是实际功能数量是缩减了。不过,这也不是大的问题,毕竟 Plugin 可以和 Component 混用,真正需要解决的问题是,这两者的 Yaml 不一样,如何混用?
总结 Plugin 部署到线上的函数,会自动变更名字,例如函数是 myFunction,服务和阶段是 myService-Dev,那么函数部署到线上就是 myService-Dev-myFunction,这样的函数名,很可能会让函数间调用产生很多不可控因素:如果环境是 Dev,函数间调用就要写函数名是 myService-Dev-myFunction,如果环境是 Test,此时就要写 myService-Test-myFunction。在我看来,环境更改只需要更改配置,无需更改更深入的代码逻辑,因此这一点会让我觉得不友好; Plugin 也是有优势的,例如如果有 Invoke、Remove 以及部署单个函数的功能,同时 Plugin 也有全局变量,它像是一个开发者工具,可以进行开发、部署、调用、查看信息、指标以及删除回滚等操作; Components 可以看作是一个组件集,这里面包括了很多的 Components,有基础的 Components,例如 cos、scf、apigateway 等,也有一些拓展的 Components,例如在 cos 上拓展出来的 website,可以直接部署静态网站等,还有一些框架级的,例如 Koa,Express; Components 除了支持的产品多,可以部署框架之外,对我来说,最大吸引力在于其部署到线上的函数名字就是指定的名字,不会出现额外的东西; Components 相对 Plugin 在功能上略显单薄,除了部署和删除,再没有其他功能。当你需要部署多个东西,并写在了某个 Components 的 yaml 上,那么即使你只修改了一个函数,它都需要全部重新部署一遍; Components 更多的定义是组件,所以在 Components 中是没有全局变量的。
Serverless Framework 30 天试用计划
我们诚邀您来体验最便捷的 Serverless 开发和部署方式。在试用期内,相关联的产品及服务均提供免费资源和专业的技术支持,帮助您的业务快速、便捷地实现 Serverless! 详情可查阅: Serverless Framework 试用计划
One More Thing
3 秒你能做什么?喝一口水,看一封邮件,还是 —— 部署一个完整的 Serverless 应用? 复制链接至 PC 浏览器访问: https://serverless.cloud.tencent.com/deploy/express
3 秒极速部署,立即体验史上最快的 Serverless HTTP 实战开发! 传送门: GitHub: github.com/serverless 官网: serverless.com
欢迎访问: Serverless 中文网 ,您可以在 最佳实践 里体验更多关于 Serverless 应用的开发! 推荐阅读: 《Serverless 架构:从原理、设计到项目实战》
云计算
2020-04-01 10:44:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
学员点评@肖强:
相对于基于OpenStack架构的云厂商来说,ZStack更容易部署,基于全异步架构,并发创建虚拟机的速度更快更稳定,后期升级也十分方便。总的来说,结合我接触过的其他云产品,ZStack更简单易用,功能也比较完善,这契合了ZStack简单Simple、健壮Strong、弹性Scalable、智能Smart的“4S”特性理念。
接触
作为一名即将上岗实习的云计算专业大三学生,受疫情影响,我无法进入公司实习,学校安排我们在家里自主学习。正巧,我看到几名网友的朋友圈晒出了ZCCT和ZCCC的证书。通过和他们联系,我了解到ZStack疫情期间推出了ZCCT在线认证免费开放的活动,出于了解业内云计算厂商产品与技术、提升自身技能水平等目的,我仔细研究了ZCCT和ZCCC认证课程大纲,然后马不停蹄的开始了学习。
学习
从周三上午到周四凌晨,我一鼓作气拿下了ZCCT与ZCCC的理论考试与上机实验考试,看到我的学习成果和邮箱里发来的证书,当天晚上兴奋的睡不着觉哈哈哈。虽然还没有参加工作,但是作为相关专业的学生,我对云计算还是很感兴趣的,在校期间也有学习其他云厂商的产品,所以在学习ZStack初级认证课程的时候还比较轻松。而且ZStack培训课程讲的非常好,知识很系统全面,同时还结合实战演练,提供免费的线上实验环境。ZStack老师讲课很温柔,我当时半夜直接笑场了,还回放了好几次。整个学习过程很轻松,很快乐。
实战
ZCCT与ZCCC考试都有理论笔试题,考前我特意复习了上课做的笔记,但还是太粗心和大意了,用了很短的时间把题目做完了,最后都只考了80多分。最让我纠结的是ZCCT的LAB,看完实验步骤后,我规划了两台虚拟机,一台部署ZStack云平台,而另一台作为iSCSI服务器向云平台提供sharedblock存储,根据实验指导一步一步来。
在“将嵌套云平台添加Sharedblock主存储”步骤,一直显示添加集群失败。根据日志分析,我仔细检查了iSCSI服务器的配置,确认无误后发现并不是文档错了,而是需要在iSCSI服务器上打开lvm某配置,经过多次找资料并尝试无果后,我最终还是向ZStack申请了线上资源。
本想着睡个觉明日再战!没想到ZStack很快就给我准备好了实验资源,我迫不及待登陆了上去,想着今天一定要拿下它。果然根据ZStack提供的标准环境,我半小时就顺利做完了实验,提交日志,审核通过!
总结
本次学习过程,最大的收获就是了解了ZStack的产品与技术,并且在ZStack官网看了部分技术白皮书与技术类的文章,更进一步加深了对ZStack架构和产品特性的掌握。相对于很多基于OpenStack架构的云厂商来说,ZStack更容易部署,基于全异步架构,并发创建虚拟机的速度更快更稳定,后期升级也十分方便。总的来说,结合我接触过的其他云产品,ZStack更简单易用,功能也比较完善,这契合了ZStack简单Simple、健壮Strong、弹性Scalable、智能Smart的“4S”特性理念。我相信ZStack未来发展潜力很大,我也会密切关注ZStack,期待未来ZStack能提供更多的培训课程。
云计算
2020-03-31 20:40:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 王旭  蚂蚁金服资深技术专家
本文整理自《CNCF x Alibaba 云原生技术公开课》第 28 讲, 点击直达课程页面 。
关注“阿里巴巴云原生”公众号,回复关键词**“入门”**,即可下载从零入门 K8s 系列文章 PPT。
一、缘起:安全容器的命名
Phil Karlton 有一句名言:“计算机科学界只有两个真正的难题——缓存失效和命名。”
对我们容器圈而言,我相信「命名」绝对配得上这句话。这毫无疑问是一件让老开发者沉默、让新人落泪的事情。仅就系统软件而言,我们当今比较通行地称为**「Linux 容器技术」**这个概念,它曾经用过的名字还有 Jail, Zone, Virtual Server, Sandbox 等。同样,在早期虚拟化的技术栈里也把一类虚拟机叫做容器,毕竟这个词本身就指代那些用来包容、封装和隔离的器物。它实在太过常见了,以至于以严谨著称的 Wikipedia,它的词条叫做「OS-Level Virtualization」(系统级虚拟化) ,从而回避了「什么是容器」这个问题。
在 2013 年,Docker 问世之后,容器这个概念伴随着不可变基础设施、云原生这一系列概念在随后的几年间以摧枯拉朽之势颠覆了基于“软件包+配置”这种细粒度组合的应用部署,用简单的声明式策略和不可变的容器就清爽地定义了软件栈。应用怎么部署,在这儿似乎有点离题了,我在这里想要强调的是:
“云原生语境下的容器,实质是「应用容器」——是以标准格式封装的,运行于标准操作系统环境(常常是 Linux ABI)上的应用打包——或运行这一应用打包的程序/技术。”
这个定义是我下的,但它并不是我的个人意志,是基于 OCI 规范这一共识写出来的。这个规范规定了容器之中应用被放到什么样的环境下、如何运行,比如说容器的根文件系统上哪个可执行文件会被执行,是用什么用户执行,需要什么样的 CPU,有什么样的内存资源、外置存储,还有什么样的共享需求等等。
所以说,标准格式的封装、标准的操作系统环境在一起以应用为中心就构成了应用容器的打包。
以这个共识为基础,就可以来说说安全容器了。当年,我和我的联合创始人赵鹏使用「虚拟化容器」这个名字来命名我们的技术的,不过为了博人眼球,我们用了「Secure as VM, Fast as Container」这样的 Slogan,于是,被容器安全性问题戳中心坎的人们立刻用「Secure Container」或者说「安全容器」来称呼这种东西了,一发而不可收。虽然在我们的内心里,这个技术是一层额外的隔离,它只是安全中的一环,但是呢,用户还是愿意用安全容器这个名字来称呼它。我们给安全容器下的定义就是:
安全容器是一种运行时技术,为容器应用提供一个完整的操作系统执行环境(常常是 Linux ABI),但将应用的执行与宿主机操作系统隔离开,避免应用直接访问主机资源,从而可以在容器主机之间或容器之间提供额外的保护。
这就是我们的安全容器。
二、间接层:安全容器的精髓
说安全容器的时候,就要提到「间接层」这个词。它出自于 Linus Torvalds 在 2015 年的 LinuxCon 上提出的:
“安全问题的唯一正解在于允许那些(导致安全问题的)Bug 发生,但通过额外的隔离层来阻挡住它们。”
为了安全,为什么要引入隔离层呢?其实 Linux 本身这样的规模是非常大的,无法从理论上来验证程序是没有 Bug 的,于是,一旦合适的 Bug 被利用,安全性风险就变成安全性问题了。安全性的框架和修补并不能确保安全,所以我们需要进行一些额外的隔离来减少漏洞以及因为这些漏洞造成的被彻底攻破的风险。
这就是安全容器的由来。
三、Kata Containers:云原生化的虚拟化
2017 年 12 月,我们在 KubeCon 上对外发布了 Kata Containers 的安全容器项目,这个项目有两个前身:由我们之前开始的 runV 以及 Intel 的 Clear Container 项目。这两个项目都是 2015 年 5 月开始开展的,实际上是早于 Linus 在 KubeCon 2015 说的那番话的。
它们的思路都很简单: 操作系统本身的容器机制没法解决安全性问题,需要一个隔离层; 虚拟机本身,VM,它是一个现成的隔离层,比如说像阿里云、AWS,它们都使用了虚拟化技术,所以对于全世界来说,大家已经普遍地相信,对于用户来说,只要能做到「secure of VM」,那这个安全性就可以满足公有云的需求了; 虚拟机中如果有个内核,就可以支持我们刚才所提到的 OCI 的定义,也就是说提供了 Linux ABI 的运行环境,在这个运行环境中跑一个 Linux 应用不太难实现。
现在的问题是虚机不太够快,阻碍了它在容器环境中的应用,如果能拥有「speed of container」的话,那我们就可能可以有一个用虚拟机来做隔离的安全容器技术了。这个也就是 Kata Containers 本身的一个思路,就是用虚拟机来做 Kubernetes 的 PodSandbox。在 Kata 里面被拿来做 VM 的先后有 qemu, firecracker, ACRN, cloud-hypervisor 等。
下图就是 Kata Containers 怎么去和 Kubernetes 集成的,这里的例子用的是 containerd,当然 CRI-O 也是一样的。
目前,Kata Containers 通常是在 Kubernetes 中使用。首先 Kubelet 通过 CRI 接口找到 containerd 或者 CRI-O,这个时候比如镜像这样的操作一般也是由 containerd 或者 CRI-O 来执行的。根据请求,它会把 runtime 部分的需求变成一个 OCI spec,并交给 OCI runtime 执行。比如说上图上半部分中的 kata-runtime,或者说下半部分精简过后的 containerd-shim-kata-v2。具体的过程是这样的: 当 containerd 拿到一个请求的时候,它会首先创建一个 shim-v2。这个 shim-v2 就是一个 PodSandbox 的代表,也就是那个VMM 的代表; 每一个 Pod 都会有一个 shim-v2 来为 containerd/CRI-O 来执行各种各样的操作。shim-v2 会为这个 Pod 启动一个虚拟机,在里面运行着一个 linux kernel,也就是图里面的 Guest kernel。如果这个里面用的是 qemu 的话,我们会通过一些配置和一些补丁,让它变得小一些。同时这个里面也没有额外的 Guest 操作系统,不会跑一个完整的像 CentOS, Ubuntu 这样的操作系统; 后我们会把这个容器的 spec 以及这个容器本身打包的存储,包括 rootfs 和文件系统,交给这个 PodSandbox。这个 PodSandbox 会在虚机中由 kata-agent 把容器启动起来; 依照 CRI 语义和 OCI 规范,在一个 Pod 里面是可以启动多个相关联的容器的。它们会被放到同一个虚拟机里面,并且可以根据需求共享某些 namespace; 除了这些之外,其它的一些外置的存储和卷也可以通过热插拔的方式来插到这个 PodSandbox 里面来; 对于网络来说,目前使用 tcfilter 就可以无缝地接入几乎所有的 Kubernetes 的 CNI 插件。而且我们还提供了一个 enlightened 的模式,这样的话会有一个特制的 CNI 插件来提高容器的网络能力。
可以看到,在我们的 PodSandbox 里面,实际上只有一个 Guest Kernel 跑着一些容器本身的打包和容器应用,并不包含一个完整的操作系统。就是说,这个过程,它用起来并不像是传统的虚拟机,对于容器来说,它只有容器的引擎,并且通过少用不必要的内存、共享能共享的内存来进一步地降低内存的开销。
与传统的虚拟机比起来,开销更小、启动更轻快,对于大部分的场景来说,它可以做到「secure as VM」、「fast as container」。同时,在安全性技术以外,相比传统的虚机,它有更多的弹性,更少了机器的那种物理操作的手感,比如说这里面说过的包括动态资源的插拔以及使用 virtio-fs 这样的技术等。它是一个专门为我们这种场景、为像 kata 这样的场景来做的一个把 host 的基本文件系统的内容(比如说容器的 rootfs )共享给虚拟机的这样一个技术。
通过其中一些之前为非易失存储、非易失内存来做的 DAX 的技术,能够在不同的 PodSandbox 之间,也就是不同的 Pod 之间、不同的容器之间,共享一些可以共享的只读的内存部分。这样可以在不同的 PodSandbox 之间去节省很多的内存。同时所有的 Pod 的管理都是通过 Kubernetes 从外部进行的容器管理,并且从外部来获取 metrics 和 debug 信息,并没有登陆虚拟机这样一种手感。所以它看起来是一种非常容器化的操作,虽然从底层来看,它还是一个虚拟机,但是实际上它是一个面向云原生的虚拟化。
四、gVisor:进程级虚拟化
gVisor,我们又把它叫做进程级的虚拟化,它是和 kata 不一样的另外一种方式。
在 2018 年的 5 月份,哥本哈根的 KubeCon 上,Google 开源了他们内部开发了 5 年的 gVisor 安全容器作为对 kata containers 的回应,表明了他们有一种不同的安全容器的解决方案。
如果说 Kata Containers 是通过对现有的隔离技术进行组合和改造来构建容器间的隔离层的话,那么 gVisor 的设计显然是更加简洁的。
如上图右侧所示,它是一个用 Go 语言重写的运行在用户态的操作系统内核,这个内核的名字叫做 sentry,它并不依赖于虚拟化和虚拟机技术,相反,它是借助一个它们内部叫做一个 Platform(平台)的能力,让宿主机的操作系统做一个操作,把应用所有的期望对操作系统的操作都转交给 sentry 来进行,sentry 做处理之后会把其中的一部分交给操作系统来帮它完成,大部分则由自己来完成。
gVisor 是一个纯粹的面向应用的隔离层,从一开始就不是一个完全等同于虚拟机的东西,它就是用来在 Linux 上面跑一个 Linux 程序的。作为一个隔离层,它的安全性依据在于: gVisor 的开发者们首先要把攻击面变小,宿主机的操作系统将只为沙箱里的应用提供大约 20% 的系统调用;
Linux 大概有 300 多个 Syscall,实际上 sentry 最后向操作系统发起的调用只会集中在 60 多个 Syscall 上。这个是源于 gVisor 的开发者们对操作系统的安全做了一些研究,他们发现,大多数对操作系统的成功的攻击都是来自于不常用的系统调用的。
这个很容易理解,因为不常用的系统调用,它的实现路径一般都是比较老的路径,也就是说这些部分的开发一般不是太积极,只有很少的开发者来维护,那些热门路径上的代码要更安全一些,因为那些代码被 review 的次数比较多。所以 gVisor 的设计就是让应用对那些并不常用的 Syscall 的访问根本就到不了操作系统层面,而只在 sentry 里就把它处理掉。
从 sentry 访问宿主机的,只使用那些被验证过的、比较成熟、比较热的路径上的系统调用,这样的话,安全性就会比原来看起来好很多。我们现在 Syscall 是原来的 1/5,但是被攻击的可能性是并不到 1/5 的。 其次,他们发现,一些经常被攻击的系统调用需要把它隔离出来,比如 open(),就是打开文件的那个操作;
在 Unix 系统里面,大部分东西都是一些文件,所以 open 可以做太多的事情了,大部分的攻击都是通过 open 来进行的。gVisor 的开发者就单独地把 open 放到了一个独立的进程里面去实现,这个进程叫做 Gofer。一个独立的进程实际上是更容器被 seccomp、被一些系统的限制、一些 "capbility drop" 来保护。Gofer 可以做更少的事情,可以用非 root 去执行,如此一来整个系统的安全性就被进一步地被提高了。 最后,sentry 和 Gofer 都是用 Go 语言来实现的,不是用传统的 C 语言实现的。
Go 语言本身是一个内存更安全的一个实现,因此整个 gVisor 就更不容易被攻击,更不容易发生一些内存上的问题。当然,Go 语言在有些地方还是不够太系统级的,gVisor 的开发者也坦言,他们为了做这件事情,也对 Go Runtime 做了很多调整,并把这些东西也反馈回给了 Go 语言的社区。
可以说 gVisor 的架构很漂亮,有很多开发者跟我坦诚,他们其实很喜欢 gVisor 的架构,觉得这个更简单、更纯粹、更干净。当然了,虽然它的架构很漂亮,但重新实现一个内核这件事情也只有 Google 这样的巨头能做得出来,类似的可能还有微软的 WSL 1。而且这个设计是比较超前的,它其实存在一些问题: 首先,sentry 并不是 Linux,所以在兼容性方面与 kata 这样的方案比起来还是有一定的差距的。这个没有办法,但是对于特定应用来说,这个可能并不是问题; 其次,对于当前的系统调用的实现方式,还有 CPU 的指令系统来说,我们从应用去拦截 Syscall,再把这个 Syscall 送给 sentry 去执行,这个过程本身是有相当大的开销的。在一定场景之下,gVisor 是可以有更好的性能的。但是,在大部分的场景之下,gVisor 的性能仍然是比不上 kata 这样的解决方案的。
所以短时间之内,gVisor 这样的解决方案并不能成为一个终极的解决方案,不过它可以适应一些特定的场景,并且它也带来一些启示性。我觉得这个启示性对未来的操作系统、CPU 指令集的发展都可能会有一些作用。而且我相信,在未来,不管是 kata 还是 gVisor,都会有一个演进,我们期待着最后会有一个公共的解决方案来统一地解决应用的执行问题。
五、安全容器:不止于安全
安全容器的名字虽叫安全,但是它提供的是一个隔离性。它的作用是不止于安全的。
安全容器通过隔离层让应用的问题——不管是来自于外部的恶意攻击还是说意外的错误,都不至于影响主机,也不会在不同的 Pod 之间相互影响,所以实际上,这个额外的隔离层,它所带来的影响不只是安全,还有其它的方面。它对于系统的调度、服务质量,还有应用信息的保护都是有好处的。
我们说传统的操作系统容器技术是内核进程管理的一个延伸,容器进程本身是一组相关联的进程,对于宿主机的调度系统来说,它是完全可见的,一个 Pod 里的所有容器或进程,同时也都被宿主机调度和管理。这就意味着,如果你有一个大量容器的环境,宿主机本身内核的负担就会很重,在很多实际环境中已经可以观察到这个负担带来的开销了。
尤其是现在计算机技术的不断发展,一个操作系统会有大量的内存,大量的 CPU,几百 G 的内存都是可以见到的。在这个情况下,如果分配的容器数量很多,调度系统就会有非常沉重的开销。在采纳安全容器之后,在宿主机上就看不到这些完整的信息了,这个隔离层同时承担了一些对隔离层上面应用的调度,于是在主机上面就只需要调度这些沙箱本身,降低了宿主机的调度开销,这也就是它为什么会提高调度效率的原因。
提高调度效率的同时,它会把所有的应用彼此隔离起来,这样就避免了容器之间、容器和主机之间的干扰,提高了服务质量。从另外一个方向来看,我们做安全容器的初衷是为了保护宿主机不受到容器内恶意或者有问题的应用的影响,反过来,作为一个云来说,我们有可能会面对有恶意的攻击,所以也是保护我们自己。
同时用户也不愿意让我们过多地去访问用户的资源,用户需要使用资源,但它并不需要我们看到它的数据。安全容器可以把用户运行的东西完全封装在容器里,这样的话可以让主机的运维管理操作并不能访问到应用的数据,从而把应用的数据保护在沙箱里,不需要去碰到用户数据。如果我们要访问用户数据,作为一个云的话,那就必须得让用户给你授权,这个时候,用户不确定你是不是有什么恶意的操作,如果我们的沙箱封装得很好的话,那也就不需要额外的对用户授权的要求,这对于保护用户的私密性是更好的。
当我们把目光看向未来的时候,可以看到,安全容器不仅仅是在做安全隔离,安全容器隔离层的内核相对于宿主机的内核是独立的,专门对应用服务,从这个角度来说,主机和应用的功能之间实际上是一个合理的功能分配与优化。它可以展现出很多的潜力,未来的安全容器,可能不仅仅是隔离性能开销的降低,同时也是在提高应用的性能。隔离技术会让云原生基础设施更加完美。
六、本文总结
本文的主要内容就到此为止了,这里为大家简单总结一下: 现在,所谓“安全容器”是指一种容器运行时技术,为容器应用提供一个完整的操作系统执行环境(常常是 Linux ABI),但将应用的执行与宿主机操作系统隔离开,避免应用直接访问主机资源,从而可以在容器主机之间或容器之间提供额外的保护; Kata Containers 是一个使用虚拟化来提供隔离层的开源安全容器项目,完全兼容 Kubernetes 等云原生生态系统,项目托管在OpenStack Foundation,由蚂蚁金服和Intel共同领导; gVisor 是一种利用进程级虚拟化技术实现的安全容器技术,由 Google 开发并开源,用 Go 语言实现了一个用户态的兼容内核; 最后,安全容器提供的隔离性不止是安全中的一环,也可以提供性能、调度、管理方面的隔离。
“ 阿里巴巴云原生 关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”
云计算
2020-03-31 18:55:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
这是 Cgroup 系列的第四篇,往期回顾: Linux Cgroup 入门教程:基本概念 Linux Cgroup 入门教程:CPU Linux Cgroup 入门教程:内存
通过 上篇文章 的学习,我们学会了如何查看当前 cgroup 的信息,如何通过操作 /sys/fs/cgroup 目录来动态设置 cgroup,也学会了如何设置 CPU shares 和 CPU quota 来控制 slice 内部以及不同 slice 之间的 CPU 使用时间。本文将继续探讨对 CPU 使用时间的限制。
对于某些 CPU 密集型的程序来说,不仅需要获取更多的 CPU 使用时间,还要减少工作负载在节流时引起的上下文切换。现在的多核系统中每个核心都有自己的缓存,如果频繁的调度进程在不同的核心上执行势必会带来缓存失效等开销。那么有没有方法针对 CPU 核心进行隔离呢?准确地说是把运行的进程绑定到指定的核心上运行。虽然对于操作系统来说,所有程序生而平等, 但有些程序比其他程序更平等。
对于那些更平等的程序来说,我们需要为它分配更多的 CPU 资源,毕竟人都是很偏心的。废话少说,我们来看看如何使用 cgroup 限制进程使用指定的 CPU 核心。
1. 查看 CPU 配置
CPU 核心的编号一般是从 0 开始的,4 个核心的编号范围是 0-3 。我们可以通过查看 /proc/cpuinfo 的内容来确定 CPU 的某些信息: $ cat /proc/cpuinfo ... processor : 3 vendor_id : GenuineIntel cpu family : 6 model : 26 model name : Intel(R) Xeon(R) CPU X5650 @ 2.67GHz stepping : 4 microcode : 0x1f cpu MHz : 2666.761 cache size : 12288 KB physical id : 6 siblings : 1 core id : 0 cpu cores : 1 apicid : 6 initial apicid : 6 fpu : yes fpu_exception : yes cpuid level : 11 wp : yes flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx rdtscp lm constant_tsc arch_perfmon nopl xtopology tsc_reliable nonstop_tsc eagerfpu pni ssse3 cx16 sse4_1 sse4_2 x2apic popcnt tsc_deadline_timer hypervisor lahf_lm ssbd ibrs ibpb stibp tsc_adjust arat spec_ctrl intel_stibp flush_l1d arch_capabilities bogomips : 5333.52 clflush size : 64 cache_alignment : 64 address sizes : 43 bits physical, 48 bits virtual processor : 表示核心的编号,但这不是物理 CPU 的核心,更确切地可以称之为**逻辑核编号。 physical id : 表示当前逻辑核所在的物理 CPU 的核心,也是从 0 开始编号,这里表示这个逻辑核在第 7 个 物理 CPU 上。 core id : 如果这个值大于 0,你就要注意了,你的服务器可能开启了超线程。如果启用了超线程,每个物理 CPU 核心会模拟出 2 个线程,也叫逻辑核(和上面的逻辑核是两回事,只是名字相同而已)。如果你想确认服务器有没有开启超线程,可以通过下面的命令查看: $ cat /proc/cpuinfo | grep -e "core id" -e "physical id" physical id : 0 core id : 0 physical id : 2 core id : 0 physical id : 4 core id : 0 physical id : 6 core id : 0
如果 physical id 和 core id 皆相同的 processor 出现了两次,就可以断定开启了超线程。显然我的服务器没有开启。
2. NUMA 架构
这里需要涉及到一个概念叫 NUMA(Non-uniform memory access) ,即 非统一内存访问架构 。如果主机板上插有多块 CPU,那么就是 NUMA 架构。每块 CPU 独占一块面积,一般都有独立风扇。
一个 NUMA 节点包含了直连在该区域的 CPU、内存等硬件设备,通信总线一般是 PCI-E 。由此也引入了 CPU 亲和性的概念,即 CPU 访问同一个 NUMA 节点上的内存的速度大于访问另一个节点的。
可以通过下面的命令查看本机的 NUMA 架构: $ numactl --hardware available: 1 nodes (0) node 0 cpus: 0 1 2 3 node 0 size: 2047 MB node 0 free: 1335 MB node distances: node 0 0: 10
可以看出该服务器并没有使用 NUMA 架构,总共只有一个 NUMA 节点,即只有一块 CPU,4 个逻辑核心均在此 CPU 上。
3. isolcpus
Linux 最重要的职责之一就是调度进程,而进程只是程序运行过程的一种抽象,它会执行一系列指令,计算机会按照这些指令来完成实际工作。从硬件的角度来看,真正执行这些指令的是中央处理单元,即 CPU。默认情况下,进程调度器可能会将进程调度到任何一个 CPU 核心上,因为它要根据负载来均衡计算资源的分配。
为了增加实验的明显效果,可以隔离某些逻辑核心,让系统默认情况下永远不会使用这些核心,除非我指定某些进程使用这些核心。要想做到这一点,就要使用到内核参数 isolcpus 了,例如:如果想让系统默认情况下不使用逻辑核心 2,3 和 4,可以将以下内容添加到内核参数列表中: isolcpus=1,2,3 # 或者 isolcpus=1-3
对于 CnetOS 7 来说,可以直接修改 /etc/default/grub : $ cat /etc/default/grub GRUB_TIMEOUT=5 GRUB_DISTRIBUTOR="$(sed 's, release .*$,,g' /etc/system-release)" GRUB_DEFAULT=saved GRUB_DISABLE_SUBMENU=true GRUB_TERMINAL_OUTPUT="console" GRUB_CMDLINE_LINUX="crashkernel=auto rd.lvm.lv=rhel/root rd.lvm.lv=rhel/swap rhgb quiet isolcpus=1,2,3" GRUB_DISABLE_RECOVERY="true"
然后重新构建 grub.conf : $ grub2-mkconfig -o /boot/grub2/grub.cfg
重启系统之后,系统将不再使用逻辑核心 2,3 和 4,只会使用核心 1。找个程序把 CPU 跑满( 上篇文章 用的程序),使用命令 top 查看 CPU 的使用状况:
执行 top 命令后,在列表页按数字 1 键,就可以看到所有 CPU 了。
可以看到系统只使用了核心 1,下面我们来看看如何将程序绑到特定的 CPU 核心上。
4. 创建 cgroup
将程序绑到指定的核心其实很简单,只需设置好 cpuset 控制器就行了。 systemctl 可以管理受其控制资源的 cgroup 控制器,但只能管理有限的控制器(CPU、内存和 BlockIO),不能管理 cpuset 控制器。虽然 systemd 不支持 cpuset,但是相信以后会支持的,另外,现在有一个略显笨拙,但是可以实现同样的目标的方法,后面会介绍。
cgroup 相关的所有操作都是基于内核中的 cgroup virtual filesystem,使用 cgroup 很简单,挂载这个文件系统就可以了。文件系统默认情况下都是挂载到 /sys/fs/cgroup 目录下,查看一下这个目录: $ ll /sys/fs/cgroup 总用量 0 drwxr-xr-x 2 root root 0 3月 28 2020 blkio lrwxrwxrwx 1 root root 11 3月 28 2020 cpu -> cpu,cpuacct lrwxrwxrwx 1 root root 11 3月 28 2020 cpuacct -> cpu,cpuacct drwxr-xr-x 2 root root 0 3月 28 2020 cpu,cpuacct drwxr-xr-x 2 root root 0 3月 28 2020 cpuset drwxr-xr-x 4 root root 0 3月 28 2020 devices drwxr-xr-x 2 root root 0 3月 28 2020 freezer drwxr-xr-x 2 root root 0 3月 28 2020 hugetlb drwxr-xr-x 2 root root 0 3月 28 2020 memory lrwxrwxrwx 1 root root 16 3月 28 2020 net_cls -> net_cls,net_prio drwxr-xr-x 2 root root 0 3月 28 2020 net_cls,net_prio lrwxrwxrwx 1 root root 16 3月 28 2020 net_prio -> net_cls,net_prio drwxr-xr-x 2 root root 0 3月 28 2020 perf_event drwxr-xr-x 2 root root 0 3月 28 2020 pids drwxr-xr-x 4 root root 0 3月 28 2020 systemd
可以看到 cpuset 控制器已经默认被创建并挂载好了。看一下 cpuset 目录下有什么: $ ll /sys/fs/cgroup/cpuset 总用量 0 -rw-r--r-- 1 root root 0 3月 28 2020 cgroup.clone_children --w--w--w- 1 root root 0 3月 28 2020 cgroup.event_control -rw-r--r-- 1 root root 0 3月 28 2020 cgroup.procs -r--r--r-- 1 root root 0 3月 28 2020 cgroup.sane_behavior -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.cpu_exclusive -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.cpus -r--r--r-- 1 root root 0 3月 28 2020 cpuset.effective_cpus -r--r--r-- 1 root root 0 3月 28 2020 cpuset.effective_mems -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.mem_exclusive -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.mem_hardwall -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.memory_migrate -r--r--r-- 1 root root 0 3月 28 2020 cpuset.memory_pressure -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.memory_pressure_enabled -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.memory_spread_page -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.memory_spread_slab -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.mems -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.sched_load_balance -rw-r--r-- 1 root root 0 3月 28 2020 cpuset.sched_relax_domain_level -rw-r--r-- 1 root root 0 3月 28 2020 notify_on_release -rw-r--r-- 1 root root 0 3月 28 2020 release_agent -rw-r--r-- 1 root root 0 3月 28 2020 tasks
该目录下只有默认的配置,没有任何 cgroup 子系统。接下来我们来创建 cpuset 子系统并设置相应的绑核参数: $ mkdir -p /sys/fs/cgroup/cpuset/test $ echo "3" > /sys/fs/cgroup/cpuset/test/cpuset.cpus $ echo "0" > /sys/fs/cgroup/cpuset/test/cpuset.mems
首先创建了一个 cpuset 子系统叫 test ,然后将核心 4 绑到该子系统,即 cpu3 。对于 cpuset.mems 参数而言,每个内存节点和 NUMA 节点一一对应。如果进程的内存需求量较大,可以把所有的 NUMA 节点都配置进去。这里就用到了 NUMA 的概念。出于性能的考虑,配置的逻辑核和内存节点一般属于同一个 NUMA 节点,可用 numactl --hardware 命令获知它们的映射关系。很显然,我的主机没有采用 NUMA 架构,只需将其设为节点 0 就好了。
查看 test 目录: $ cd /sys/fs/cgroup/cpuset/test $ ll 总用量 0 -rw-rw-r-- 1 root root 0 3月 28 17:07 cgroup.clone_children --w--w---- 1 root root 0 3月 28 17:07 cgroup.event_control -rw-rw-r-- 1 root root 0 3月 28 17:07 cgroup.procs -rw-rw-r-- 1 root root 0 3月 28 17:07 cpuset.cpu_exclusive -rw-rw-r-- 1 root root 0 3月 28 17:07 cpuset.cpus -r--r--r-- 1 root root 0 3月 28 17:07 cpuset.effective_cpus -r--r--r-- 1 root root 0 3月 28 17:07 cpuset.effective_mems -rw-rw-r-- 1 root root 0 3月 28 17:07 cpuset.mem_exclusive -rw-rw-r-- 1 root root 0 3月 28 17:07 cpuset.mem_hardwall -rw-rw-r-- 1 root root 0 3月 28 17:07 cpuset.memory_migrate -r--r--r-- 1 root root 0 3月 28 17:07 cpuset.memory_pressure -rw-rw-r-- 1 root root 0 3月 28 17:07 cpuset.memory_spread_page -rw-rw-r-- 1 root root 0 3月 28 17:07 cpuset.memory_spread_slab -rw-rw-r-- 1 root root 0 3月 28 17:07 cpuset.mems -rw-rw-r-- 1 root root 0 3月 28 17:07 cpuset.sched_load_balance -rw-rw-r-- 1 root root 0 3月 28 17:07 cpuset.sched_relax_domain_level -rw-rw-r-- 1 root root 0 3月 28 17:07 notify_on_release -rw-rw-r-- 1 root root 0 3月 28 17:07 tasks $ cat cpuset.cpus 3 $ cat cpuset.mems 0
目前 tasks 文件是空的,也就是说,还没有进程运行在该 cpuset 子系统上。需要想办法让指定的进程运行在该子系统上,有两种方法: 将已经运行的进程的 PID 写入 tasks 文件中; 使用 systemd 创建一个守护进程,将 cgroup 的设置写入 service 文件中(本质上和方法 1 是一样的)。
先来看看方法 1,首先运行一个程序: $ nohup sha1sum /dev/zero & [1] 3767
然后将 PID 写入 test 目录的 tasks 中: $ echo "3767" > /sys/fs/cgroup/cpuset/test/tasks
查看 CPU 使用情况:
可以看到绑核生效了, PID 为 3767 的进程被调度到了 cpu3 上。
下面再来看看方法 2,虽然目前 systemd 不支持使用 cpuset 去指定一个 Service 的 CPU,但我们还是有一个变相的方法,Service 文件内容如下: $ cat /etc/systemd/system/foo.service [Unit] Description=foo After=syslog.target network.target auditd.service [Service] ExecStartPre=/usr/bin/mkdir -p /sys/fs/cgroup/cpuset/testset ExecStartPre=/bin/bash -c '/usr/bin/echo "2" > /sys/fs/cgroup/cpuset/testset/cpuset.cpus' ExecStartPre=/bin/bash -c '/usr/bin/echo "0" > /sys/fs/cgroup/cpuset/testset/cpuset.mems' ExecStart=/bin/bash -c "/usr/bin/sha1sum /dev/zero" ExecStartPost=/bin/bash -c '/usr/bin/echo $MAINPID > /sys/fs/cgroup/cpuset/testset/tasks' ExecStopPost=/usr/bin/rmdir /sys/fs/cgroup/cpuset/testset Restart=on-failure [Install] WantedBy=multi-user.target
启动该服务,然后查看 CPU 使用情况:
该服务中的进程确实被调度到了 cpu2 上。
5. 回到 Docker
最后我们回到 Docker , Docker 实际上就是将系统底层实现的 cgroup 、 namespace 等技术集成在一个使用镜像方式发布的工具中,于是形成了 Docker ,这个想必大家都知道了,我就不展开了。对于 Docker 来说,有没有办法让容器始终在一个或某几个 CPU 上运行呢?其实还是很简单的,只需要利用 --cpuset-cpus 参数就可以做到!
下面就来演示一下,指定运行容器的 CPU 核心编号为 1: 🐳 → docker run -d --name stress --cpuset-cpus="1" progrium/stress -c 4
查看主机 CPU 的负载:
只有 Cpu1 达到了 100% ,其它的 CPU 并未被容器使用。
如果你看过该系列的 第一篇文章 ,应该知道,在新的使用 systemd 实现 init 的系统中(比如 ConetOS 7 ),系统默认创建了 3 个顶级 slice : System , User 和 Machine ,其中 machine.slice 是所有虚拟机和 Linux 容器的默认位置,而 Docker 其实是 machine.slice 的一个变种,你可以把它当成 machine.slice 。
如果系统中运行的是 Kubernetes, machine.slice 就变成了 kubepods :
为了便于管理 cgroup, systemd 会为每一个 slice 创建一个子系统,比如 docker 子系统:
然后再根据容器的设置,将其放入相应的控制器下面,这里我们关心的是 cpuset 控制器,看看它的目录下有啥:
查看 docker 目录:
可以看到 Docker 为每个容器创建了一个子目录, 7766.. 对应的就是之前我们创建的容器: 🐳 → docker ps|grep stress 7766580dd0d7 progrium/stress "/usr/bin/stress --v…" 36 minutes ago Up 36 minutes stress
我们来检验一下该目录下的配置: $ cd /sys/fs/cgroup/cpuset/docker/7766580dd0d7d9728f3b603ed470b04d0cac1dd923f7a142fec614b12a4ba3be $ cat cpuset.cpus 1 $ cat cpuset.mems 0 $ cat tasks 6536 6562 6563 6564 6565 $ ps -ef|grep stress root 6536 6520 0 10:08 ? 00:00:00 /usr/bin/stress --verbose -c 4 root 6562 6536 24 10:08 ? 00:09:50 /usr/bin/stress --verbose -c 4 root 6563 6536 24 10:08 ? 00:09:50 /usr/bin/stress --verbose -c 4 root 6564 6536 24 10:08 ? 00:09:50 /usr/bin/stress --verbose -c 4 root 6565 6536 24 10:08 ? 00:09:50 /usr/bin/stress --verbose -c 4
当然,你也可以将容器绑到多个 CPU 核心上运行,这里我就不赘述了。下篇文章将会介绍如何通过 cgroup 来限制 BlockIO 。
微信公众号
扫一扫下面的二维码关注微信公众号,在公众号中回复◉加群◉即可加入我们的云原生交流群,和孙宏亮、张馆长、阳明等大佬一起探讨云原生技术
云计算
2020-03-31 12:43:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
近年来,IT 技术的更新迭代速度非常快,每个时间点都有典型的代表名词以及概念,就目前而言,人工智能领域中的机器学习、深度学习、强化学习等名词和概念就非常热,同时区块链、物联网等技术发展也是异常火热。
在云计算领域,有这样一个技术被众多云厂商认为是“风口项目”,甚至可以颠覆现有云计算中的某些格局,为此包括 AWS、谷歌以及腾讯云、阿里云等在内的云厂商,都为此投入了重大人力以及精力进行相关产品建设,它就是 Serverless 技术。
自 2006 年 8 月 9 日,Google 首席执行官埃里克·施密特(Eric Schmidt)在搜索引擎大会(SESSanJose2006)首次提出“云计算”(Cloud Computing)的概念之后,云计算的发展可以用日新月异这个词来形容。
在短短十几年的发展过程中,云计算也从 IaaS 到 PaaS,再到 SaaS,逐渐将去服务器化趋势表现得愈发明显。就目前的情况来看,全球各大 IT 企业,都在紧罗密布的部署自己的“云事业”,尤其是 Serverless 相关概念的推广和产品的推出以及项目的落地,包括 AWS、Google Cloud、Azure、阿里云、腾讯云、华为云等在内的云厂商,无一例外的向 Serverless 进军。或许云计算下一个阶段,可能就是 BaaS+FaaS+Others,即 Serverless,当然也可能这个阶段就是!
什么是 Serverless
Serverless 可以说是一种架构,一种云计算发展的产物,至于具体说什么是 Serverless,可能没有谁无法给一个明确的概念,如果非要给出一个概念,那或许可以参考 Martin Fowler 在《Serverless Architectures》中对 Serverless 这样定义: Serverless was first used to describe applications that significantly or fully incorporate third-party, cloud-hosted applications and services, to manage server-side logic and state. These are typically “rich client” applications—think single-page web apps, or mobile apps—that use the vast ecosystem of cloud-accessible databases (e.g., Parse, Firebase), authentication services(e.g., Auth0, AWS Cognito), and so on. These types of services have been previously described as “(Mobile) Backend as a service", and I use “BaaS” as shorthand in the rest of this article. Serverless can also mean applications where server-side logic is still written by the application developer, but, unlike traditional architectures, it’s run in stateless compute containers that are event-triggered, ephemeral (may only last for one invocation), and fully managed by a third party. One way to think of this is “Functions as a Service” or “FaaS”.(Note: The original source for this name—a tweet by @marak—isno longer publicly available.) AWS Lambda is one of the most popular implementations of a Functions-as-a-Service platform at present, but there are many others, too.
当然这个描述貌似很长,读起来也有点干涩难懂。不过,大家可以简单粗暴的把 Serverless 认为是 BaaS + FaaS,如果用一张图来表示上面的描述,可以是:
说到这里,不同的人可能已经对 Serverless 有了不同的勾勒,但是可能普遍还有一个疑问,我怎么用 Serverless?向云服务器上传我项目?还是像一种框架,用来写代码?用了它我可以得到什么?性能的提升?效率的提高?成本的降低?
首先,我们以一个常见的 Web 服务为例:
在这个图中,服务器中可能涉及路由规则、鉴权逻辑以及其他各类复杂的业务代码,同时,开发团队要付出很大的精力在这个服务器的运维上面,包括客户量突然增多时是否需要扩容服务器;服务器上的脚本,业务代码等是否还在健康运行;是否有黑客在不断地对服务器发起攻击;也就是说,当我们把这个思路切换到 Serverless 的逻辑之后,上图就变成了这样:
可以认为,当客户端和数据库未发生变化的前提下,服务器变化巨大,之前需要开发团队维护的路由模块以及鉴权模块都将接入服务商提供的 API 网关系统以及鉴权系统,开发团队无须再维护这两部分的业务代码,只需要持续维护相关规则即可。同时业务代码也被拆分成了函数粒度,不同函数表示不同的功能。同时,在这个结构下,我们已经看不到服务器的存在,是因为 Serverless 的目的是让使用者只关注自己的业务逻辑即可,所以一部分安全问题、资源调度问题(例如用户量暴增、如何实现自动扩容等)全都交给云厂商负责,并且相对于传统项目而言,传统项目无论是否有用户访问,服务都在运行中,都是有成本支出,而 Serverless 而言,只有在用户发起请求时,函数才会被激活并且执行,按量收费,相对来说,可以在有流量的时候才有支持,没有流量的时候就没有支出,成本会进一步降低。
通过分析和描述,不难看出,Serverless 架构相对于传统的开发模式有什么区别。但是问题来了,很多工作都不需要我们做了,都交给云厂商做了,那么我们做什么?
使用 Serverless 架构,用户不需要自己维护服务器,也不需要自己操心服务器的各种性能指标和资源利用率,而是可以让用户付出更多的时间和精力去关心应用程序本身的状态和逻辑。同时 Serverless 应用本身的部署十分容易,我们只要上传基本的代码,例如 Python 程序只需要上传其逻辑与依赖包,C/C++、Go 等语言只需上传其二进制文件,Java 只需要上传其 Jar 包等即可,同时不需使用 Puppet、Chef、Ansible 或 Docker 来进行配置管理,大大降低了运维成本。对于运维来说,Serverless 架构也不再需要监控底层的数据,例如不再需要监控磁盘使用量、CPU 使用率等,可以更加专注的将监控目光放到监控应用程序本身的度量。同时在 Serverless 架构上,运维人员的工作角色会有所转变,部署将变得更加自动化,监控将更加面向应用程序本身。
总而言之,Serverless 是在传统容器技术和服务网格上发展起来,它更多指的是后端服务与函数服务的结合,对于开发者而言,会更多关注在函数服务商,让使用者只关注自己的业务逻辑即可。Serverless 是云计算发展到一定阶段的必然产物,云计算作为普惠科技,发展到最后一定是绿色科技(最大程度利用资源,减少空闲资源浪费),大众科技(成本低,包括学习成本及使用成本)的产品,而 Serverless 将很好的诠释这些!Serverless 架构被称为是“真正实现了当初云计算的目标”,这种说法虽然有些夸张,但是也从另一方面表现出了大家对 Serverless 架构的期盼和信心,自 2012 年被提出至今,Serverless 架构也是经历了 7 年多的时间,正在逐渐的走向成熟。
入门 Serverless
说起 Serverless,就不得不说 BaaS 和 FaaS,BaaS 服务更多是云厂商给我们提供 / 维护,所以开发者精力可以更多放在 FaaS 层面,或者说是在函数计算层面。
接下来,我们来体验一下 Serverless。以腾讯云为例,我们通过腾讯云控制台,选择 Serverless 分类下的云函数:
接下来就可以看到 Serverless 中的一部分:函数计算部分。此时,我们可以新建一个函数,进行基本的测试,体验一下 Serverless 下的 Hello World 和我们传统的 Hello World 有什么不同。 新建函数: 选择运行时(就是我们要用的编程语言):
进行代码的编写:
点击完成,即可保存代码 进行代码测试:
可以看到测试结果:
至此,我们完成了一个函数的基本编写,但是仔细想一下:貌似和一些在线编程工具差不多,可以在线编写代码、运行。BaaS 体现在了哪里?体现在提供了运行环境?除了写了一个 hello world,我还能干什么?
接下来,我们进行触发器的体验。所谓的触发器,是指我们的函数一般情况下都是 " 休息 " 的,只有在一个 " 东西触碰它 ",“激活它”,才会起来干活。刚刚我们是怎么让函数 " 起来工作的 "?是通过屏幕上的 " 测试按钮 ",所以说这也算是一个触发器。那么除了这个触发器,还有那些?
可以看到,目前腾讯云提供给我们的触发器包括: 定时触发器 (顾名思义,就是定好时间进行函数的触发,例如说每天几点触发一次,或者说每隔多久触发一次,这类操作适合我们做定时任务,例如进行数据采集 / 数据聚合,消息推送等。) COS 触发器 我们可能会将文件存储到文件系统,在传统的云主机中,我们可以存到机器本身,但是 Serverless 架构下,由于函数是无状态的,所以我们不能做持久化,那么就需要一个外部的媒体," 对象存储 " 就是我们常用的持久化文件产品,可以将一些文件存储在上面,例如图片、文档、可执行程序…,同时也可以通过存入到上面一个文件,来触发我们的函数。例如当有图片上传到对象存储中,函数计算会下载这个图片,进行图片压缩和水印等处理。 CMQ 主题订阅触发器 CMQ 主题订阅是指,当我们 CMQ 中有队列存在,就可以将内容发给云函数,云函数来进行消费处理。 Ckafka 触发 与上面说的 CMQ 主题订阅触发器基本一样,只不过这个是 Ckafka。当 Ckafka 中消息出现(可以是每条触发也可以是最多多少条触发),会让函数 " 起来工作 ",进行数据处理、完成消费。 API 网关触发器 是和函数关系非常紧密的一个服务。通过 API 网关触发,可以让函数具备被访问能力。什么叫做被访问呢?就是说可以通过浏览器 / 接口直接使用,所以 API 网关触发器和云函数结合通常可以作网站、后台服务等。
此时,我们可以建立一个 API 网关触发器,看看函数和 API 网关结合所带来的有趣碰撞:
初探 API 网关与函数
我们新建一个 API 网关服务:
创建完成,系统会给我们分配一个地址:
通过浏览器打开这个地址:
这时,我们就成功的搭建了一个 Web 服务,后台会展示 Hello World ,如果是传统开发条件下,做一个这样的页面,需要做哪些工作? 使用框架开发一个 Hello World 购买服务器,并配置服务器的环境 将本地开发好的项目上传到服务器中 购买域名 / 使用服务器 IP,绑定我们的项目
这个过程可能涉及到的有常用的 Web 框架(例如 Django,Spring,Express…),服务器的软件(Nginx,Apache,Tomcat…)等等,甚至我们还要考虑网站的流量有多大,买多大内存的机器,启动多少进程,多少线程,还要想办法对服务器进行各种优化。
但我们刚刚做的操作只有: 建立函数 增加 API 网关触发器
其余的一切操作都不用我们关心,我们可以将更多的精力放在了 "Coding"。
用函数和 API 网关做点有趣的
在生产生活中,我们经常需要获取 IP 地址进行某些工作,例如我之前做了一个网站,这个网站的用户签名体系包括了用户的 IP,而客户端想获得用户 IP 是一个比较复杂的过程。一般情况下是需要通过访问服务端的获取 IP 接口来获得客户端对应的 IP 地址。那么通过函数计算和 API 网关,我们应该怎么做呢?
刚才说到了触发器,每种触发器都会和函数有一个规约,我给你一种什么样的格式数据,通过函数下面的测试模板可以看到:
通过这里,可以看到 API 网关和函数约定的一个结构: { "requestContext": { "serviceId": "service-f94sy04v", "path": "/test/{path}", "httpMethod": "POST", "requestId": "c6af9ac6-7b61-11e6-9a41-93e8deadbeef", "identity": { "secretId": "abdcdxxxxxxxsdfs" }, "sourceIp": "10.0.2.14", "stage": "release" }, "headers": { "Accept-Language": "en-US,en,cn", "Accept": "text/html,application/xml,application/json", "Host": "service-3ei3tii4-251000691.ap-guangzhou.apigateway.myqloud.com", "User-Agent": "User Agent String" }, "body": "{\"test\":\"body\"}", "pathParameters": { "path": "value" }, "queryStringParameters": { "foo": "bar" }, "headerParameters":{ "Refer": "10.0.2.14" }, "stageVariables": { "stage": "release" }, "path": "/test/value", "queryString": { "foo" : "bar", "bob" : "alice" }, "httpMethod": "POST" }
同时,函数会将这个结构作为入参之一传递给开发人员,例如腾讯云将这个参数命名为 event ,也就是说,开发者可以通过函数入口的 event 参数进行 API 网关相关内容的解析。
那么什么是函数的入口呢?
入口函数实际上就是用户代码中的文件名 + 方法名,这里面默认设定就是 index 文件中的 main_handler 方法,可以看到 main_handler 方法,确实有一个参数是 event,这个参数就是触发器传递过来的数据结构。另外一个 context 参数是上下文,用户对上下文内容的处理,例如上游资源产生的 RequestId、环境信息、密钥信息等。
通过上面的数据接口,可以看到在 requestContext 中 sourceIp,是用户的 IP 地址,那么我们是否就可以把这个 IP 直接返回给用户,实现 IP 查询功能呢? # -*- coding: utf8 -*- import json def main_handler(event, context): return({"ip": event['requestContext']['sourceIp']})
通过 4 行代码编写之后,我们绑定 API 网关,并且通过浏览器访问可以看到:
是的,这样一个功能,只需要 4 行代码就可以搞定。
再说说 Serverless
刚刚我们已经入门了云函数,对云函数也有了一个初步的了解了,那么接下来,我们说说 Serverless 架构有哪些优点和缺点。
优点 弹性伸缩
传统意义上,一台服务器能接受多大的流量,峰值是多少,是需要我们进行评估的,同时后期也要不断维护和更新数据的。但是在 Serverless 架构下,用户不需要考虑这个问题,云厂商将会为用户实现弹性伸缩的能力。当平台接收到第一个触发函数的事件时,将启动容器来运行你的代码。如果此时收到了新的事件,而第一个容器仍在处理上一个事件,平台将启动第二个代码实例来处理第二个事件。SCF 的这种自动零管理水平缩放,将持续到有足够的代码实例来处理所有的工作负载。当并发出现的时候,云厂商会启动多个容器来应对 " 流量洪峰 ",相对于传统服务器来说,在这一层面上,Serverless 架构或者说云函数真的是很方便了。 按量付费
按量付费是 Serverless 架构的一个优点,传统服务器,无论是否有流量,我们都要进行成本支出,并且服务器配置还要按照某个时间段最大流量来进行配置,所以支出情况实际上是不合理的。但是函数计算实际上是按量收费,而且相对来说价格很低,尤其对不同时间段资源消耗峰值低谷有较大差距的项目而言,是真的很棒。
缺点 冷启动
说到 Serverless 架构的缺点就不得不说冷启动问题,冷启动无论是 AWS 还是 Google 还是腾讯云、阿里云,都是普遍存在的。一般情况下来说,冷启动就是函数在 " 睡觉 ",突然有一个触发的过程,后台拉起容器、下载代码、启动进程、触发入口方法的一个过程,所以一般情况下,容器在首次启动的时候都会有冷启动,通过上图可以看到,函数冷启动可能达到几百毫秒甚至数秒,这对一些业务可能是致命打击,当然各个云厂商也在努力通过各种策略、方案降低冷启动率。 调试困难
云函数的另一个缺点是调试困难,由于它提供给我们的是一个函数运行的容器,而且很多基本业务又是和厂商绑定的,这就导致我们调试困难。例如,我们要调试一个函数,本来可以通过模拟一些触发器情况进行调试,但是,如果函数中涉及到了一些内网资源,例如与 redis 相关,只能通过 vpc 访问的资源,那么这个时候进行本地调试困难度就会成倍增加,在线调试又可能因为日志输出过慢,导致调试整体体验极差。
总结
云计算的发展,Serverless 是一个必然的产物。Serverless 作为一个新技术或者说是一个新架构,很难通过一篇文章进行描述清楚,其优点和缺点都不只是上文中描述的那两个,我们只是挑了比较典型的列出了而已。Serverless 在使用的时候也会有很多坑,有的时候真的是从入门到放弃,有的时候也会觉得真的很方便,又从放弃到入坑,但是无论怎么说,作为一个相对来说比较新鲜的事物,Serverless 有更多的领域和价值在等待我们去开发和探索,包括 Serverless 的应用领域、使用经验等。
Serverless 架构被称为是“真正实现了当初云计算的目标”,这种说法虽然有些夸张,但是也从另一方面表现出了大家对 Serverless 架构的期盼和信心,自 2012 年被提出至今,Serverless 架构也是经历了 7 年多时间,正在逐渐的走向成熟。随着容器技术、IoT、5G、区块链等技术的快速发展, 技术上对去中心化、轻量虚拟化、细粒度计算等技术需求愈发强烈,相信未来 Serverless 将在云计算的舞台上大放异彩!
Serverless Framework 30 天试用计划
我们诚邀您来体验最便捷的 Serverless 开发和部署方式。在试用期内,相关联的产品及服务均提供免费资源和专业的技术支持,帮助您的业务快速、便捷地实现 Serverless! 详情可查阅: Serverless Framework 试用计划
One More Thing
3 秒你能做什么?喝一口水,看一封邮件,还是 —— 部署一个完整的 Serverless 应用? 复制链接至 PC 浏览器访问: https://serverless.cloud.tencent.com/deploy/express
3 秒极速部署,立即体验史上最快的 Serverless HTTP 实战开发! 传送门: GitHub: github.com/serverless 官网: serverless.com
欢迎访问: Serverless 中文网 ,您可以在 最佳实践 里体验更多关于 Serverless 应用的开发! 推荐阅读: 《Serverless 架构:从原理、设计到项目实战》
云计算
2020-03-31 11:15:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
后疫情时代,企业如何以高效、敏捷的速度在数字化浪潮中成长壮大,从而能有条不紊地应对诸如疫情等突发事件,是CIO必须关心的问题。随着移动互联网的发展和应用云化的普及,微服务已经成为企业应用服务化架构最流行的设计理念,然而企业对推行微服务架构的认知各有不同。
Nebulogy纳比云特邀K2中国研发副总裁焦锟,为您带来 《微服务架构下,流程平台解耦的最佳实践》 的主题分享。赶快报名吧!
直播时间:2020年4月10日(周五)15:00-16:00
直播方式:45分钟左右的直播+互动交流
01 主题简介
本期直播将从微服务架构和传统平台架构的区别讲起,通过企业流程平台建设中面临的问题以及推行微服务架构后带来的改变,向听众介绍微服务架构的优势。进而从流程平台解耦的应用场景为企业推行微服务带来借鉴思路。
02 扫码报名


Nebulogy 品牌介绍
Nebulogy致力于通过云原生理念,帮助企业构建PaaS平台,提高开发资源利用率,满足应用快速上线和迭代需求,助力企业实现真正应用云化、业务互联网化。
网站: http://www. nebulogy.com
邮箱:service@nebulogy.com
电话:400-105-0300
云计算
2020-03-31 10:57:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
本期嘉宾:
ZStack资深技术工程师 李朗
本期主题:
ZStack VPC网络相关介绍:
1、VPC网络定义
2、VPC防火墙和安全组
3、VPC网络特点和应用场景
4、VPC网络服务及演示
5、VPC 与云路由的区别
6、典型VPC 网络场景案例
7、VPC 网络创建流程

教学现场部分Q&A
Q1:安全组作用在物理机上,如果用户私自修改了云主机内网IP地址,安全组规则会发生改变吗?有没有规避用户修改云主机地址的方法?
A1:安全组是基于iptables针对云主机的虚拟网卡进行安全策略的配置,不会随着云主机系统内部IP地址的变化而变化。
用户在云主机操作系统内部修改云主机的IP地址,安全组规则不会发生改变;
ZStack支持网络防欺诈,可以在云主机详情页或者全局设置开启,开启并重启云主机后,修改云主机IP或者MAC地址后,将无法对外通信。
Q2:请问不同VPC下的云主机,在同一个宿主机上的网络报文是如何隔离的?
A2:不同VPC下的云主机在相同的宿主机上可以通过二层隔离,可以通过划分不同的VLAN或者VXLAN进行网络报文隔离。
Q3:组播网络下,如果RP配置在内网云主机上,是否可行?是否会与VPC路由器的分布式冲突?
A3:组播RP不建议配置在内网的云主机,一般ZStack VPC路由器去对接物理环境的组播RP路由器并加入到组播组中来提供组播服务。
以上是本期直播的主要内容,欢迎点击链接获取课程视频回放:
https://www.zstack.io/plus/view.php?aid=2061
云计算
2020-03-30 14:14:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
作者 | 良名  阿里巴巴技术专家
背景
相信很多人都使用过 start.spring.io 来初始化自己的 Spring Boot 工程,这个工具为开发者提供了丰富的可选组件,并且可以选择多种打包方式,大大方便了开发人员的使用。最近,阿里的 Nacos、Sentinel 也进入 start.spring.io 的选项中,进一步的方便开发者使用阿里云的产品。
但是,生成的工程骨架中,只有组件坐标信息,缺少对应的使用方法和 Demo 代码;于是,开发者还是需要去寻找相关使用教程,或者样例代码;如果找的不对,或者是版本不匹匹配,还需要花费不少时间去排查和解决问题;这些问题都在无形中增加用户的工作量。
我们将对软件工程的抽象层次自上而下进行切分,会得到如下的几个层级:行业、解决方案、应用、功能、组件;明显的, start.spring.io 目前只能提供组件级别的支持。再将组件这层展开,会发现这样一个生命周期:组件引入、组件配置、功能开发、线上运维。start.spring.io 也只实现了“组件引入”这一功能。
我们的目标是**“让阿里云成为广大 Java 开发者最好用的云”**。要实现这个目标,是否可以再向前走几步,在解决“组件引入”问题的基础上,将组件的典型使用方法、样例代码、使用说明也加入到工程中呢?
基于这种思考,我们上线了自己的 bootstrap 站点 start.aliyun.com :
https://start.aliyun.com/
当然,本着不重复造轮子的原则,我们不再构建一套工程生成底层框架,而是使用 Spring Initializr 来实现这部分功能。在此之上专注于增加新特性,实现服务广大开发者的目标。
Spring Initializr: https://github.com/spring-io/initializr
在 start.aliyun.com 中,我们为广大开发者带来了如下便利特性: 为每个组件提供了单独的 DemoCode 和对应的配置样例(本次已发布); 工程内置说明,减少用户查找文档的困难(部分实现); 开发者只需要做减法,而非加法的使用方式(部分实现); 提供多组件集成的解决方案(开发中); 定期跟进 start.spring.io 的更新,方便大家使用到 spring 的最新功能。
start.aliyun.com: https://start.aliyun.com/
未来,我们还需要再助力开发者这条路上继续发力,不仅仅是做好组件集成的工作,还要需要继续向上支持,提供更多功能、服务、应用层级的快速构建能力。
本文,围绕 spring initializr 框架,以 start.spring.io 为例,全面的给大家介绍如何使用和扩展这个框架,以及背后的运行原理。
使用篇
由于 spring-initializr 提供了灵活的扩展能力,以及丰富的默认实现;其使用方式也是非常的灵活多变;为了便于说明,我们直接通过 start.spring.io ,看看 Spring 自己是怎么使用这套框架的。
1. 基本用法
基本用法的原则,是尽量少写代码,甚至是不写代码。只通过配置就可以实现 initializr 工程的创建。
依赖引入
要使用 spring-initializr ,首先要引入这套框架。很简单,直接依赖 bom 即可: io.spring.initializr initializr-bom 0.9.0.BUILD-SNAPSHOT pom import
有了这个 bom 依赖,我们就不用再关心内部组件的版本等信息了。
一般来说,我们还需要引入具体组件: io.spring.initializr initializr-generator-spring io.spring.initializr initializr-version-resolver io.spring.initializr initializr-web
具体每个子模块的用途,这里列出来,供读者参考: initializr-actuator: 监控诊断的附加信息,这个暂时忽略; initializr-bom: 便于外部使用的bom依赖; initializr-docs: 使用文档; initializr-generator: 核心工程生成库; initializr-generator-spring: 用于生成典型的spring boot工程; initializr-generator-test: 测试框架; initializr-metadata: 项目各个方面的元数据基础结构; initializr-service-sample: 基本使用案例; initializr-version-resolver:版本号解析能力; initializr-web: 提供给三方客户端使用的web入口。
基本配置 完成了框架引入,就需要做一些基础配置了 支持哪些语言:Java、groovy、Kotlin 支持哪些版本:1.8、11、13 支持哪些打包方式:jar、war
将这些信息全部配置到 application.yml 文件中,如下: initializr: packagings: - name: Jar id: jar default: true - name: War id: war default: false javaVersions: - id: 13 default: false - id: 11 default: false - id: 1.8 name: 8 default: true languages: - name: Java id: java default: true - name: Kotlin id: kotlin default: false - name: Groovy id: groovy default: false
其中 name 是可选的, id 是必填的。
每个配置项下,可以有一个默认值(将 default 这是为 true 即可),除了这些基本配置,我们还需要定义可以支持的项目类型: initializr: types: - name: Maven Project id: maven-project description: Generate a Maven based project archive. tags: build: maven format: project default: true action: /starter.zip - name: Maven POM id: maven-build description: Generate a Maven pom.xml. tags: build: maven format: build default: false action: /pom.xml - name: Gradle Project id: gradle-project description: Generate a Gradle based project archive. tags: build: gradle format: project default: false action: /starter.zip - name: Gradle Config id: gradle-build description: Generate a Gradle build file. tags: build: gradle format: build default: false action: /build.gradle
默认情况下, initializr 已经支持 4 种项目类型: /pom.xml 生成一个 Maven 的 pom.xml 配置文件 /build.gradle 生成 Gradle 的配置文件 /starter.zip 生成 zip 方式压缩的工程文件 /starter.tgz 生成以 tgz 方式压缩的工程文件
通过 tags 标签,我们可以定义不同配型的编译方式 (build) 和打包格式(format)。
配置基本依赖
完成了基本配置以后,就可以配置可选的依赖组件了。
依赖配置以 dependency 为 key ,同样配置在 application.yml 的 initializr 下面,这里给出一个简单的样例: initializr: dependencies: - name: Web content: - name: Web id: web description: Full-stack web development with Tomcat and Spring MVC - name: Developer Tools content: - name: Spring Boot DevTools id: devtools groupId: org.springframework.boot artifactId: spring-boot-devtools description: Provides fast application restarts, LiveReload, and configurations for enhanced development experience. - name: Lombok id: lombok groupId: org.projectlombok artifactId: lombok description: Java annotation library which helps to reduce boilerplate code.
dependencies 下定义分组。分组的作用是便于展示和快速查找,所以不需要 id ,只需要 name 信息;每个分组的 content 是分组的具体内容,也就是这个分组下的组件定义;支持以列表形式定义多个;另外,每个分组都可以设置当前分组内组件公用的配置信息。
每一依赖,包含如下的基本信息: id:组件的唯一标识符 groupId & artifactId:组件的坐标 name:显示名称 description:描述信息,主要用于展示用途 version:组件版本
关于 groupId & artifactId:如果设置了坐标,生成的项目里会使用这里的坐标定位组件;但是如果没有设置坐标,框架会认为这是一个标准的 spring-boot 组件,自动添加 spring-boot-starter-{id} 作为生成的依赖坐标。
关于 version:如果直接在组件上设置版本信息,框架会直接使用这个值作为组件依赖的版本;但是很多时候,组件的版本会受到 spring-boot 版本的影响,此时就需要对版本做特殊的定义 & 管理。
配置依赖版本管理
这里需要先了解一下版本命名规则:一个典型的版本,一般包含如下 4 个信息:大版本、小版本、修正版本、版本限定符。
版本范围有一个上界和下界,可以方括号 [] 或者圆括号 () 表示。方括号代表上下界的闭区间,圆括号代表上下界的开区间。
例如: “[1.1.6.RELEASE,1.3.0.M1)”代表所有从 1.1.6.RELEASE 到 1.3.0.M1 之间所有的版本(包含 1.1.6.RELEASE ,但不包含 1.3.0.M1 )。
同时,可以使用单一版本号作为版本范围,例如 “1.2.0.RELEASE”。单一版本号的版本范围代表“从这个版本以及之后的所有版本”。
如果需要使用“最新的 Release 版本”的概念,可以使用一个字母 x 代表具体的版本号。
例如, 1.4.x.BUILD-SNAPSHOT 代表 1.4.x 的最新快照版本。
再比如:如果需要表达,从 1.1.0.RELEASE 到 1.3.x 之间的所有版本,可以用[1.1.0.RELEASE,1.3.x.RELEASE]来表达。
另外,版本限定符也是有顺序的(升序): M:里程碑版本 RC:发布候选版本 RELEASE:发布版本 BUILD-SNAPSHOT:为开发构建的快照版本
所以快照版本是所有限定符里优先级最高的。假设某个组件需要 Spring Boot 的最新版本,可以使用 1.5.x.BUILD-SNAPSHOT  (假设 1.5 版是 Spring Boot 的最新版本)。
最后,版本范围中讨论的版本,指的都是 Spring Boot的版本,而不是组件自己的版本。
前面介绍了,可以使用 version 属性定义组件的具体版本号;但是,如果组件版本与Spring Boot 的版本存在关联关系,就需要使用 compatibilityRange 来配置依赖的版本范围。
 compatibilityRange 可以定义在两个地方: 直接定义在组件(或 Bom )上
这种定义方式,代表组件只支持  Spring Boot 的某一个版本范围,例如下面的配置: initializr: dependencies: - name: Stuff content: - name: Foo id: foo ... compatibilityRange: 1.2.0.M1 - name: Bar id: bar ... compatibilityRange: "[1.5.0.RC1,2.0.0.M1)"
Foo 可以支持 Spring boot 1.2.0  之后的所有版本;而Bar只能支持 Spring Boot 1.5.0  到  2.0.0 之间的版本,且不包含 2.0.0 ; 定义在组件的 mappgin 属性下
可以支持在 Spring Boot 不同版本之下对组件做不同的设置(可以重置组件部分或者是所有的属性),下面的例子中对 artifactId 做了特殊定义: initializr: dependencies: - name: Stuff content: - name: Foo id: foo groupId: org.acme.foo artifactId: foo-spring-boot-starter compatibilityRange: 1.3.0.RELEASE mappings: - compatibilityRange: "[1.3.0.RELEASE,1.3.x.RELEASE]" artifactId: foo-starter - compatibilityRange: "1.4.0.RELEASE"
这个例子中, foo 在 Spring Boot 的 1.3 使用 foo-starter 作为坐标的 artifactId ;在 1.4.0.RELEASE 以及之后的版本中,还是使用 foo-spring-boot-starter 作为 artifactId 的值;
**使用 Bom 管理版本:**有时候,需要使用 Bom 的方式管理组件版本;此时不需要对组件单独设置版本号。
要使用 Bom ,首先要配置 Bom 定义: initializr: env: boms: my-api-bom: groupId: org.acme artifactId: my-api-dependencies version: 1.0.0.RELEASE repositories: my-api-repo-1
注意:Bom 信息,定义在 initializr.env.boms下面。
其属性和依赖组件基本一致,都是坐标、版本;同时, Bom 也支持版本范围管理。
完成了 Bom 的定义,就需要在组件中引用 Bom : initializr: dependencies: - name: Other content: - name: My API id : my-api groupId: org.acme artifactId: my-api bom: my-api-bom
一旦用户选择了 my-api 组件,框架会自动为生成的项目添加了 my-api-dependencies 的 Bom 依赖;
2. 高级定制
启用缓存
如果你启动过 start.spring.io 项目,你会在日志里发现这样的输出 “Fetching boot metadata from spring.io/project_metadata/spring-boot” 为了避免过于频繁的检查 Spring Boot 版本,官方是建议配合缓存一起使用。
首先需要引入缓存框架: javax.cache cache-api org.ehcache ehcache
然后,在 SpringBootApplication 类上增加 @EnableCaching 注解:
如果需要自己定义缓存,可以调整如下缓存配置:
**增加 Demo代码:**由于不同的组件有不同的功能,如果需要为项目增加 Demo 代码。
**为不同的组件增加独立配置:**还记得原理篇中提到的 spring.factories 吗?对,我们要增加自己的配置项,就需要在这里增加针对不同组件样例代码的扩展入口。 io.spring.initializr.generator.project.ProjectGenerationConfiguration=\ com.alibaba.alicloud.initializr.extension.dependency.springboot.SpringCloudProjectGenerationConfiguration
在 SpringCloudProjectGenerationConfiguration 中,我们通过 ConditionalOnRequestedDependency 注解来识别不同组件: @ProjectGenerationConfiguration public class SpringCloudAlibabaProjectGenerationConfiguration { private final InitializrMetadata metadata; private final ProjectDescription description; private final IndentingWriterFactory indentingWriterFactory; private final TemplateRenderer templateRenderer; public SpringCloudAlibabaProjectGenerationConfiguration(InitializrMetadata metadata, ProjectDescription description, IndentingWriterFactory indentingWriterFactory, TemplateRenderer templateRenderer) { this.metadata = metadata; this.description = description; this.indentingWriterFactory = indentingWriterFactory; this.templateRenderer = templateRenderer; } @Bean @ConditionalOnRequestedDependency("sca-oss") public OSSDemoCodeContributor ossContributor() { return new OSSDemoCodeContributor(description, templateRenderer); } ...... }
上面的代码,会在选择了 sca-oss 组件时,创建一个 OSSDemoCodeContributor 用于对应 Demo 代码的生成。
**生成具体的 Demo 代码:**继续以 OSSDemoCodeContributor 为例,它是一个 ProjectContributor ,会在项目文件空间创建完成了调用。我们需要为这个 Contributor 在实例化时增加生成过程中需要的元数据信息,例如 ProjectDescription 。
代码生成过程,比较简单,可以直接复用框架中就提供的 mstache 模板引擎。
我们直接将 Demo 代码,以模板的形式,放置在 resources 文件夹之下:
然后,我们再通过模板引擎,解析这些模板文件,再拷贝到项目目录下即可: private void writeCodeFile(TemplateRenderer templateRenderer, Language langeuage, Map params, Path path, String temp) throws IOException { ...... Path pkgPath = 生成包路径 Path filePath = 成成代码文件路径 // 渲染模板 String code = templateRenderer.render(temp, params); // demo 文件写入 Files.createDirectories(pkgPath); Files.write(filePath, code.getBytes("UTF-8")); }
除了模板代码以外,我们通常还需要在 applicatioin.properties 文件写入模块的配置信息。
这里,我们依然可以使用代码生成的方式:创建模板、解析模板,追加文件的方式来实现。具体代码这里就不贴了,读者可以自己发挥。
原理篇
原理篇,主要介绍 spring.initializr 是如何实现项目工程构建的,以及作为一个框架,如何提供丰富的扩展能力的。
在原理篇,我们将 initializr 的执行分为两个阶段:启动阶段和生成阶段。 启动阶段:启动应用,加载配置,扩展信息初始化; 生成阶段:一个项目生成,从收到请求,到返回内容的完整流程。
1. 启动阶段
再开始启动流程之前,先要看一下 initializr 的扩展体系。
整个架构大量使用了 spring 的 spi 机制,我们来看一下一共有哪些 spring.factories : initializr-generator/src/main/resources/META-INF/spring.factories initializr-generator-spring/src/main/resources/META-INF/spring.factories initializr-web/src/main/resources/META-INF/spring.factories initializr-actuator/src/main/resources/META-INF/spring.factories start-site/src/main/resources/META-INF/spring.factories
其中只有一个在 start.spring.io 中,其他 4 个都在 initializr 工程中(各 spring.factories 的具体内容见参考资料)。
不过要注意,这些 spring.factories 定义,仅仅代表了各个 SPI 有哪些扩展。不同spi的实现创建和使用完全是在不同的阶段进行的。
在应用启动阶段,其实只有一个 spi 会被加载(暂不考虑 actuator):io.spring.initializr.web.autoconfigure.InitializrAutoConfiguration 。 @Configuration @EnableConfigurationProperties(InitializrProperties.class) public class InitializrAutoConfiguration { @Bean @ConditionalOnMissingBean public ProjectDirectoryFactory projectDirectoryFactory() @Bean @ConditionalOnMissingBean public IndentingWriterFactory indentingWriterFactory() @Bean @ConditionalOnMissingBean(TemplateRenderer.class) public MustacheTemplateRenderer templateRenderer(Environment environment, ObjectProvider cacheManager) @Bean @ConditionalOnMissingBean public InitializrMetadataUpdateStrategy initializrMetadataUpdateStrategy(RestTemplateBuilder restTemplateBuilder, ObjectMapper objectMapper) @Bean @ConditionalOnMissingBean(InitializrMetadataProvider.class) public InitializrMetadataProvider initializrMetadataProvider(InitializrProperties properties, InitializrMetadataUpdateStrategy initializrMetadataUpdateStrategy) @Bean @ConditionalOnMissingBean public DependencyMetadataProvider dependencyMetadataProvider() @Configuration @ConditionalOnWebApplication static class InitializrWebConfiguration { @Bean InitializrWebConfig initializrWebConfig() @Bean @ConditionalOnMissingBean ProjectGenerationController projectGenerationController( InitializrMetadataProvider metadataProvider, ApplicationContext applicationContext) @Bean @ConditionalOnMissingBean ProjectMetadataController projectMetadataController(InitializrMetadataProvider metadataProvider, DependencyMetadataProvider dependencyMetadataProvider) @Bean @ConditionalOnMissingBean CommandLineMetadataController commandLineMetadataController(InitializrMetadataProvider metadataProvider, TemplateRenderer templateRenderer) @Bean @ConditionalOnMissingBean SpringCliDistributionController cliDistributionController(InitializrMetadataProvider metadataProvider) } }
这里会做如下几件事情: 初始化元数据 Provider 创建模板引擎 创建目录、缩进工厂 初始化 web 配置 创建 spring mvc 的 web 入口 各种 ProjectGenerationController
其中最关键的元数据加载部分,使用了 EnableConfigurationProperties 注解,将 spring 环境中的配置项写到 InitializrProperties 上:
在 application.yml 文件中,可以找到如下的配置信息,这里就是实际的项目依赖关系元数据的配置存储点:
整体来看,启动阶段的动作还是比较简单的,这也是为什么 start.spring.io 启动只需要数秒的原因。
更多的逻辑,都被留在了工程生成阶段。
2. 生成阶段
生成阶段,spring-initializr 使用了一个很有意思的实现方式:initializr 框架会为每一次项目生成,创建一个独立的 context 用于存放生成流程中需要使用到的各种 bean 。
先来一张时序图:
蓝色的类,是在应用启动阶段就完成了创建和数据填充;其生命周期和整个应用一致; 黄色的类,会在具体的项目构建过程中生成;其生命周期在一次项目生成流程之内结束。
从上面的时序图中可以看出:一个典型的创建行为,通常从 ProjectGenerationController收到web端的创建请求开始,通过 ProjectGenerationInvoker 这个中间层转换,最终进入 ProjectGenerator 的核心构建流程。
主干流程
下图,是 ProjectGenerator 的核心构建流程:
106 行,通过 contextFactory 构建了一个新的 ProjectGenerationContext 。
看一下这个context的继承关系,原来于spring提供的AnnotationConfigApplicationContext 。
再结合 110 行的 refresh() 方法,是不是发现了什么?就是 spring 的 ApplicationContext 的刷新流程。
107 行的 resolve 方法,向 context 中注册了一个 ProjectDescription的Provider,代码如下:
由于注册的是 Provider ,所以这段逻辑会在 Context 执行 refresh 时运行。
这里的 ProjectDescriptionCustomizer 就是针对 ProjectDescription 的扩展,用于对用户传入的 ProjectDescription 做调整。这里主要是一些强制依赖关系的调整,例如语言版本等。
这时候再看 108 行,这里向 Context 注册一个 Configuration 。
那么这个 Configuration 包含了什么内容呢?一起来看下面这段代码:
ProjectGenerationConfiguration!!!前面提到的 spring.factories 中有很多这个 SPI 的实现(参见参考资料)。
原来,initializr 的整个扩展体系,在这里才开始创建实例;
ProjectGenerator 的 109 行,对一个 consumer 做了 accept 操作;其实就是调用了下面的代码:
这里通过 setParent 将应用的主上下文设置为这次 ProjectGenerationContext 的父节点。
并且向这次 ProjectGenerationContext 中注册了元数据对象。
最后,在 ProjectGenerator 的 112 行,调用了 projectAssetGenerator 的 generate 方法,实现如下:
通过上面的代码可以发现,这里对实际的工程构建工作,其实就是很多的 ProjectContributor 共同叠加;
至此,主干流程已经结束了。
我们可以发现,在主干流程中,没有做任何写文件的操作(只创建了根文件夹);它仅仅是定义了一套数据加载、扩展加载的机制与流程,将所有的具体实现都作为扩展的一部分。
扩展流程
spring-initializr 提供了 2 种主要扩展途径:ProjectContributor 和 xxxxxCustomizer。
从方法签名就可以看出,入参只有一个项目的根路径,其职责就是向这个路径下些人项目文件。这个扩展点非常的灵活,几乎可以支持任何的代码、配置文件写入工作。
实现过程中,可以通过 ProjectGenerationContext 获取相关依赖,然后通过自定义逻辑完成文件生成工作。
下面是 initializr 和 start.spring.io 提供的 ProjectContributor 实现:
拿几个主要的实现看看: MavenBuildProjectContributor:写入 maven 项目 pom.xml 文件; WebFoldersContributor:创建 web 项目的资源文件夹; ApplicationPropertiesContributor:写入 application.properties 文件; MainSourceCodeProjectContributor:写入应用入口类 xxxApplication.java 文件; HelpDocumentProjectContributor:写入帮助文档 HELP.md 文件。
相对于 ProjectContributor,xxxxxCustomizer  不是一个统一的接口,我把他理解为一种感念和与之对应的命名习惯;每个 Customizer 都有自己明确的名字,同时也对应了明确的触发逻辑和职责边界。
下面列出框架提供的 Customizer 的说明: MainApplicationTypeCustomizer:自定义 MainApplication 类; MainCompilationUnitCustomizer:自定义 MainApplication 编译单元; MainSourceCodeCustomizer:自定义 MainApplication 源码; BuildCustomizer:自定义项目构建工具的配置内容; GitIgnoreCustomizer:自定义项目的 .gitignore 文件; HelpDocumentCustomizer:自定义项目的帮助文档; InitializrMetadataCustomizer:自定义项目初始化配置元数据;这个 Customizer 比较特殊,框架会在首次加载元数据配置时调用; ProjectDescriptionCustomizer:自定义 ProjectDescription ;即在生成项目文件之前,允许调整项目描述信息; ServletInitializerCustomizer:自定义 web 应用在类上的配置内容; TestApplicationTypeCustomizer:自定义测试 Application 类; TestSourceCodeCustomizer:自定义测试 Application 类的源码。
参考资料
1. 相关链接 initializr 说明文档
https://docs.spring.io/initializr/docs/current-SNAPSHOT/reference/html/ spring-initializr 项目地址
https://github.com/spring-io/initializr start.spring.io 项目地址
https://github.com/spring-io/start.spring.io
2. spring.factories 明细 initializr-generator/src/main/resources/META-INF/spring.factoriesio.spring.initializr.generator.buildsystem.BuildSystemFactory=\ io.spring.initializr.generator.buildsystem.gradle.GradleBuildSystemFactory,\ io.spring.initializr.generator.buildsystem.maven.MavenBuildSystemFactory io.spring.initializr.generator.language.LanguageFactory=\ io.spring.initializr.generator.language.groovy.GroovyLanguageFactory,\ io.spring.initializr.generator.language.java.JavaLanguageFactory,\ io.spring.initializr.generator.language.kotlin.KotlinLanguageFactory io.spring.initializr.generator.packaging.PackagingFactory=\ io.spring.initializr.generator.packaging.jar.JarPackagingFactory,\ io.spring.initializr.generator.packaging.war.WarPackagingFactory
initializr-generator-spring/src/main/resources/META-INF/spring.factories: io.spring.initializr.generator.project.ProjectGenerationConfiguration=\ io.spring.initializr.generator.spring.build.BuildProjectGenerationConfiguration,\ io.spring.initializr.generator.spring.build.gradle.GradleProjectGenerationConfiguration,\ io.spring.initializr.generator.spring.build.maven.MavenProjectGenerationConfiguration,\ io.spring.initializr.generator.spring.code.SourceCodeProjectGenerationConfiguration,\ io.spring.initializr.generator.spring.code.groovy.GroovyProjectGenerationConfiguration,\ io.spring.initializr.generator.spring.code.java.JavaProjectGenerationConfiguration,\ io.spring.initializr.generator.spring.code.kotlin.KotlinProjectGenerationConfiguration,\ io.spring.initializr.generator.spring.configuration.ApplicationConfigurationProjectGenerationConfiguration,\ io.spring.initializr.generator.spring.documentation.HelpDocumentProjectGenerationConfiguration,\ io.spring.initializr.generator.spring.scm.git.GitProjectGenerationConfiguration
initializr-web/src/main/resources/META-INF/spring.factories: org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ io.spring.initializr.web.autoconfigure.InitializrAutoConfiguration org.springframework.boot.env.EnvironmentPostProcessor=\ io.spring.initializr.web.autoconfigure.CloudfoundryEnvironmentPostProcessor
initializr-actuator/src/main/resources/META-INF/spring.factories: org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ io.spring.initializr.actuate.autoconfigure.InitializrActuatorEndpointsAutoConfiguration,\ io.spring.initializr.actuate.autoconfigure.InitializrStatsAutoConfiguration
start-site/src/main/resources/META-INF/spring.factories: io.spring.initializr.generator.project.ProjectGenerationConfiguration=\ io.spring.start.site.extension.build.gradle.GradleProjectGenerationConfiguration,\ io.spring.start.site.extension.build.maven.MavenProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.DependencyProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.springamqp.SpringAmqpProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.springboot.SpringBootProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.springcloud.SpringCloudProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.springdata.SpringDataProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.springintegration.SpringIntegrationProjectGenerationConfiguration,\ io.spring.start.site.extension.dependency.springrestdocs.SpringRestDocsProjectGenerationConfiguration,\ io.spring.start.site.extension.description.DescriptionProjectGenerationConfiguration,\ io.spring.start.site.extension.code.kotin.KotlinProjectGenerationConfiguration
作者信息 : 陈曦(花名:良名)阿里巴巴技术专家。目前在应用容器&服务框架团队,Spring Cloud Alibaba 项目成员,致力于将阿里云打造为Java开发者最好用的云。2014 年加入 B2B,多次参与 双11、618 作战。
“ 阿里巴巴云原生 关注微服务、Serverless、容器、Service Mesh 等技术领域、聚焦云原生流行技术趋势、云原生大规模的落地实践,做最懂云原生开发者的公众号。”
/
云计算
2020-03-30 11:45:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>>
Linux Namespace 是 Linux 提供的一种内核级别环境隔离的方法。用官方的话来说,Linux Namespace 将全局系统资源封装在一个抽象中,从而使 namespace 内的进程认为自己具有独立的资源实例。这项技术本来没有掀起多大的波澜,是容器技术的崛起让他重新引起了大家的注意。
Linux Namespace 有如下 6 个种类:
分类 系统调用参数 相关内核版本 Mount namespaces CLONE_NEWNS Linux 2.4.19
UTS namespaces CLONE_NEWUTS Linux 2.6.19
IPC namespaces CLONE_NEWIPC Linux 2.6.19
PID namespaces CLONE_NEWPID Linux 2.6.24
Network namespaces
User namespaces
CLONE_NEWNET
CLONE_NEWUSER
始于Linux 2.6.24 完成于 Linux 2.6.29
始于 Linux 2.6.23 完成于 Linux 3.8
namespace 的 API 由三个系统调用和一系列 /proc 文件组成,本文将会详细介绍这些系统调用和 /proc 文件。为了指定要操作的 namespace 类型,需要在系统调用的 flag 中通过常量 CLONE_NEW* 指定(包括 CLONE_NEWIPC , CLONE_NEWNS , CLONE_NEWNET , CLONE_NEWPID , CLONE_NEWUSER 和 `CLONE_NEWUTS),可以指定多个常量,通过 | (位或)操作来实现。
简单描述一下三个系统调用的功能: clone() : 实现线程的系统调用,用来创建一个新的进程,并可以通过设计上述系统调用参数达到隔离的目的。 unshare() : 使某进程脱离某个 namespace。 setns() : 把某进程加入到某个 namespace。
具体的实现原理请往下看。
1. clone()
clone() 的原型如下: int clone(int (*child_func)(void *), void *child_stack, int flags, void *arg); child_func : 传入子进程运行的程序主函数。 child_stack : 传入子进程使用的栈空间。 flags : 表示使用哪些 CLONE_* 标志位。 args : 用于传入用户参数。
clone() 与 fork() 类似,都相当于把当前进程复制了一份,但 clone() 可以更细粒度地控制与子进程共享的资源(其实就是通过 flags 来控制),包括虚拟内存、打开的文件描述符和信号量等等。一旦指定了标志位 CLONE_NEW* ,相对应类型的 namespace 就会被创建,新创建的进程也会成为该 namespace 中的一员。
clone() 的原型并不是最底层的系统调用,而是封装过的,真正的系统调用内核实现函数为 do_fork() ,形式如下: long do_fork(unsigned long clone_flags, unsigned long stack_start, unsigned long stack_size, int __user *parent_tidptr, int __user *child_tidptr)
其中 clone_flags 可以赋值为上面提到的标志。
下面来看一个例子: /* demo_uts_namespaces.c Copyright 2013, Michael Kerrisk Licensed under GNU General Public License v2 or later Demonstrate the operation of UTS namespaces. */ #define _GNU_SOURCE #include #include #include #include #include #include #include /* A simple error-handling function: print an error message based on the value in 'errno' and terminate the calling process */ #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) static int /* Start function for cloned child */ childFunc(void *arg) { struct utsname uts; /* 在新的 UTS namespace 中修改主机名 */ if (sethostname(arg, strlen(arg)) == -1) errExit("sethostname"); /* 获取并显示主机名 */ if (uname(&uts) == -1) errExit("uname"); printf("uts.nodename in child: %s\n", uts.nodename); /* Keep the namespace open for a while, by sleeping. This allows some experimentation--for example, another process might join the namespace. */ sleep(100); return 0; /* Terminates child */ } /* 定义一个给 clone 用的栈,栈大小1M */ #define STACK_SIZE (1024 * 1024) static char child_stack[STACK_SIZE]; int main(int argc, char *argv[]) { pid_t child_pid; struct utsname uts; if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); exit(EXIT_FAILURE); } /* 调用 clone 函数创建一个新的 UTS namespace,其中传出一个函数,还有一个栈空间(为什么传尾指针,因为栈是反着的); 新的进程将在用户定义的函数 childFunc() 中执行 */ child_pid = clone(childFunc, child_stack + STACK_SIZE, /* 因为栈是反着的, 所以传尾指针 */ CLONE_NEWUTS | SIGCHLD, argv[1]); if (child_pid == -1) errExit("clone"); printf("PID of child created by clone() is %ld\n", (long) child_pid); /* Parent falls through to here */ sleep(1); /* 给子进程预留一定的时间来改变主机名 */ /* 显示当前 UTS namespace 中的主机名,和 子进程所在的 UTS namespace 中的主机名不同 */ if (uname(&uts) == -1) errExit("uname"); printf("uts.nodename in parent: %s\n", uts.nodename); if (waitpid(child_pid, NULL, 0) == -1) /* 等待子进程结束 */ errExit("waitpid"); printf("child has terminated\n"); exit(EXIT_SUCCESS); }
该程序通过标志位 CLONE_NEWUTS 调用 clone() 函数创建一个 UTS namespace。UTS namespace 隔离了两个系统标识符 — 主机名 和 NIS 域名 —它们分别通过 sethostname() 和 setdomainname() 这两个系统调用来设置,并通过系统调用 uname() 来获取。
下面将对程序中的一些关键部分进行解读(为了简单起见,我们将省略其中的错误检查)。
程序运行时后面需要跟上一个命令行参数,它将会创建一个在新的 UTS namespace 中执行的子进程,该子进程会在新的 UTS namespace 中将主机名改为命令行参数中提供的值。
主程序的第一个关键部分是通过系统调用 clone() 来创建子进程: child_pid = clone(childFunc, child_stack + STACK_SIZE, /* Points to start of downwardly growing stack */ CLONE_NEWUTS | SIGCHLD, argv[1]); printf("PID of child created by clone() is %ld\n", (long) child_pid);
子进程将会在用户定义的函数 childFunc() 中开始执行,该函数将会接收 clone() 最后的参数(argv[1])作为自己的参数,并且标志位包含了 CLONE_NEWUTS ,所以子进程会在新创建的 UTS namespace 中执行。
接下来主进程睡眠一段时间,让子进程能够有时间更改其 UTS namespace 中的主机名。然后调用 uname() 来检索当前 UTS namespace 中的主机名,并显示该主机名: sleep(1); /* Give child time to change its hostname */ uname(&uts); printf("uts.nodename in parent: %s\n", uts.nodename);
与此同时,由 clone() 创建的子进程执行的函数 childFunc() 首先将主机名改为命令行参数中提供的值,然后检索并显示修改后的主机名: sethostname(arg, strlen(arg); uname(&uts); printf("uts.nodename in child: %s\n", uts.nodename);
子进程退出之前也睡眠了一段时间,这样可以防止新的 UTS namespace 不会被关闭,让我们能够有机会进行后续的实验。
执行程序,观察父进程和子进程是否处于不同的 UTS namespace 中: $ su # 需要特权才能创建 UTS namespace Password: # uname -n antero # ./demo_uts_namespaces bizarro PID of child created by clone() is 27514 uts.nodename in child: bizarro uts.nodename in parent: antero
除了 User namespace 之外,创建其他的 namespace 都需要特权,更确切地说,是需要相应的 Linux Capabilities ,即 CAP_SYS_ADMIN 。这样就可以避免设置了 SUID(Set User ID on execution)的程序因为主机名不同而做出一些愚蠢的行为。如果对 Linux Capabilities 不是很熟悉,可以参考我之前的文章: Linux Capabilities 入门教程:概念篇 。
2. proc 文件
每个进程都有一个 /proc/PID/ns 目录,其下面的文件依次表示每个 namespace, 例如 user 就表示 user namespace。从 3.8 版本的内核开始,该目录下的每个文件都是一个特殊的符号链接,链接指向 $namespace:[$namespace-inode-number] ,前半部份为 namespace 的名称,后半部份的数字表示这个 namespace 的句柄号。句柄号用来对进程所关联的 namespace 执行某些操作。 $ ls -l /proc/$$/ns # $$ 表示当前所在的 shell 的 PID total 0 lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 ipc -> ipc:[4026531839] lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 mnt -> mnt:[4026531840] lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 net -> net:[4026531956] lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 pid -> pid:[4026531836] lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 user -> user:[4026531837] lrwxrwxrwx. 1 mtk mtk 0 Jan 8 04:12 uts -> uts:[4026531838]
这些符号链接的用途之一是用来 确认两个不同的进程是否处于同一 namespace 中 。如果两个进程指向的 namespace inode number 相同,就说明他们在同一个 namespace 下,否则就在不同的 namespace 下。这些符号链接指向的文件比较特殊,不能直接访问,事实上指向的文件存放在被称为 nsfs 的文件系统中,该文件系统用户不可见,可以使用系统调用 stat() 在返回的结构体的 st_ino 字段中获取 inode number。在 shell 终端中可以用命令(实际上就是调用了 stat())看到指向文件的 inode 信息: $ stat -L /proc/$$/ns/net File: /proc/3232/ns/net Size: 0 Blocks: 0 IO Block: 4096 regular empty file Device: 4h/4d Inode: 4026531956 Links: 1 Access: (0444/-r--r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2020-01-17 15:45:23.783304900 +0800 Modify: 2020-01-17 15:45:23.783304900 +0800 Change: 2020-01-17 15:45:23.783304900 +0800 Birth: -
除了上述用途之外,这些符号链接还有其他的用途, 如果我们打开了其中一个文件,那么只要与该文件相关联的文件描述符处于打开状态,即使该 namespace 中的所有进程都终止了,该 namespace 依然不会被删除 。通过 bind mount 将符号链接挂载到系统的其他位置,也可以获得相同的效果: $ touch ~/uts $ mount --bind /proc/27514/ns/uts ~/uts
3. setns()
加入一个已经存在的 namespace 可以通过系统调用 setns() 来完成。它的原型如下: int setns(int fd, int nstype);
更确切的说法是: setns() 将调用的进程与特定类型 namespace 的一个实例分离,并将该进程与该类型 namespace 的另一个实例重新关联。 fd 表示要加入的 namespace 的文件描述符,可以通过打开其中一个符号链接来获取,也可以通过打开 bind mount 到其中一个链接的文件来获取。 nstype 让调用者可以去检查 fd 指向的 namespace 类型,值可以设置为前文提到的常量 CLONE_NEW* ,填 0 表示不检查。如果调用者已经明确知道自己要加入了 namespace 类型,或者不关心 namespace 类型,就可以使用该参数来自动校验。
结合 setns() 和 execve() 可以实现一个简单但非常有用的功能:将某个进程加入某个特定的 namespace,然后在该 namespace 中执行命令。直接来看例子: /* ns_exec.c Copyright 2013, Michael Kerrisk Licensed under GNU General Public License v2 or later Join a namespace and execute a command in the namespace */ #define _GNU_SOURCE #include #include #include #include #include /* A simple error-handling function: print an error message based on the value in 'errno' and terminate the calling process */ #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) int main(int argc, char *argv[]) { int fd; if (argc < 3) { fprintf(stderr, "%s /proc/PID/ns/FILE cmd [arg...]\n", argv[0]); exit(EXIT_FAILURE); } fd = open(argv[1], O_RDONLY); /* 获取想要加入的 namespace 的文件描述符 */ if (fd == -1) errExit("open"); if (setns(fd, 0) == -1) /* 加入该 namespace */ errExit("setns"); execvp(argv[2], &argv[2]); /* 在加入的 namespace 中执行相应的命令 */ errExit("execvp"); }
该程序运行需要两个或两个以上的命令行参数,第一个参数表示特定的 namespace 符号链接的路径(或者 bind mount 到这些符号链接的文件路径);第二个参数表示要在该符号链接相对应的 namespace 中执行的程序名称,以及执行这个程序所需的命令行参数。关键步骤如下: fd = open(argv[1], O_RDONLY); /* 获取想要加入的 namespace 的文件描述符 */ setns(fd, 0); /* 加入该 namespace */ execvp(argv[2], &argv[2]); /* 在加入的 namespace 中执行相应的命令 */
还记得我们之前已经通过 bind mount 将 demo_uts_namespaces 创建的 UTS namespace 挂载到 ~/uts 中了吗?可以将本例中的程序与之结合,让新进程可以在该 UTS namespace 中执行 shell: $ ./ns_exec ~/uts /bin/bash # ~/uts 被 bind mount 到了 /proc/27514/ns/uts My PID is: 28788
验证新的 shell 是否与 demo_uts_namespaces 创建的子进程处于同一个 UTS namespace: $ hostname bizarro $ readlink /proc/27514/ns/uts uts:[4026532338] $ readlink /proc/$$/ns/uts # $$ 表示当前 shell 的 PID uts:[4026532338]
在早期的内核版本中,不能使用 setns() 来加入 mount namespace、PID namespace 和 user namespace,从 3.8 版本的内核开始, setns() 支持加入所有的 namespace。
util-linux 包里提供了 nsenter 命令,其提供了一种方式将新创建的进程运行在指定的 namespace 里面,它的实现很简单,就是通过命令行(-t 参数)指定要进入的 namespace 的符号链接,然后利用 setns() 将当前的进程放到指定的 namespace 里面,再调用 clone() 运行指定的执行文件。我们可以用 strace 来看看它的运行情况: # strace nsenter -t 27242 -i -m -n -p -u /bin/bash execve("/usr/bin/nsenter", ["nsenter", "-t", "27242", "-i", "-m", "-n", "-p", "-u", "/bin/bash"], [/* 21 vars */]) = 0 ………… ………… pen("/proc/27242/ns/ipc", O_RDONLY) = 3 open("/proc/27242/ns/uts", O_RDONLY) = 4 open("/proc/27242/ns/net", O_RDONLY) = 5 open("/proc/27242/ns/pid", O_RDONLY) = 6 open("/proc/27242/ns/mnt", O_RDONLY) = 7 setns(3, CLONE_NEWIPC) = 0 close(3) = 0 setns(4, CLONE_NEWUTS) = 0 close(4) = 0 setns(5, CLONE_NEWNET) = 0 close(5) = 0 setns(6, CLONE_NEWPID) = 0 close(6) = 0 setns(7, CLONE_NEWNS) = 0 close(7) = 0 clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f4deb1faad0) = 4968
4. unshare()
最后一个要介绍的系统调用是 unshare() ,它的原型如下: int unshare(int flags);
unshare() 与 clone() 类似,但它运行在原先的进程上,不需要创建一个新进程,即:先通过指定的 flags 参数 CLONE_NEW* 创建一个新的 namespace,然后将调用者加入该 namespace。最后实现的效果其实就是将调用者从当前的 namespace 分离,然后加入一个新的 namespace。
Linux 中自带的 unshare 命令,就是通过 unshare() 系统调用实现的,使用方法如下: $ unshare [options] program [arguments]
options 指定要创建的 namespace 类型。
unshare 命令的主要实现如下: /* 通过提供的命令行参数初始化 'flags' */ unshare(flags); /* Now execute 'program' with 'arguments'; 'optind' is the index of the next command-line argument after options */ execvp(argv[optind], &argv[optind]);
unshare 命令的完整实现如下: /* unshare.c Copyright 2013, Michael Kerrisk Licensed under GNU General Public License v2 or later A simple implementation of the unshare(1) command: unshare namespaces and execute a command. */ #define _GNU_SOURCE #include #include #include #include /* A simple error-handling function: print an error message based on the value in 'errno' and terminate the calling process */ #define errExit(msg) do { perror(msg); exit(EXIT_FAILURE); \ } while (0) static void usage(char *pname) { fprintf(stderr, "Usage: %s [options] program [arg...]\n", pname); fprintf(stderr, "Options can be:\n"); fprintf(stderr, " -i unshare IPC namespace\n"); fprintf(stderr, " -m unshare mount namespace\n"); fprintf(stderr, " -n unshare network namespace\n"); fprintf(stderr, " -p unshare PID namespace\n"); fprintf(stderr, " -u unshare UTS namespace\n"); fprintf(stderr, " -U unshare user namespace\n"); exit(EXIT_FAILURE); } int main(int argc, char *argv[]) { int flags, opt; flags = 0; while ((opt = getopt(argc, argv, "imnpuU")) != -1) { switch (opt) { case 'i': flags |= CLONE_NEWIPC; break; case 'm': flags |= CLONE_NEWNS; break; case 'n': flags |= CLONE_NEWNET; break; case 'p': flags |= CLONE_NEWPID; break; case 'u': flags |= CLONE_NEWUTS; break; case 'U': flags |= CLONE_NEWUSER; break; default: usage(argv[0]); } } if (optind >= argc) usage(argv[0]); if (unshare(flags) == -1) errExit("unshare"); execvp(argv[optind], &argv[optind]); errExit("execvp"); }
下面我们执行 unshare.c 程序在一个新的 mount namespace 中执行 shell: $ echo $$ # 显示当前 shell 的 PID 8490 $ cat /proc/8490/mounts | grep mq # 显示当前 namespace 中的某个挂载点 mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0 $ readlink /proc/8490/ns/mnt # 显示当前 namespace 的 ID mnt:[4026531840] $ ./unshare -m /bin/bash # 在新创建的 mount namespace 中执行新的 shell $ readlink /proc/$$/ns/mnt # 显示新 namespace 的 ID mnt:[4026532325]
对比两个 readlink 命令的输出,可以知道两个shell 处于不同的 mount namespace 中。改变新的 namespace 中的某个挂载点,然后观察两个 namespace 的挂载点是否有变化: $ umount /dev/mqueue # 移除新 namespace 中的挂载点 $ cat /proc/$$/mounts | grep mq # 检查是否生效 $ cat /proc/8490/mounts | grep mq # 查看原来的 namespace 中的挂载点是否依然存在? mqueue /dev/mqueue mqueue rw,seclabel,relatime 0 0
可以看出,新的 namespace 中的挂载点 /dev/mqueue 已经消失了,但在原来的 namespace 中依然存在。
5. 总结
本文仔细研究了 namespace API 的每个组成部分,并将它们结合起来一起使用。后续的文章将会继续深入研究每个单独的 namespace,尤其是 PID namespace 和 user namespace。
参考链接 Namespaces in operation, part 2: the namespaces API Docker 基础技术:Linux Namespace(上)
微信公众号
扫一扫下面的二维码关注微信公众号,在公众号中回复◉加群◉即可加入我们的云原生交流群,和孙宏亮、张馆长、阳明等大佬一起探讨云原生技术
云计算
2020-03-29 11:33:00
【围观】麒麟芯片遭打压成绝版,华为亿元投入又砸向了哪里?>>> 本文为 Serverless 社区成员撰稿。作者云洋,从事信息管理工作,多年电子政务信息系统建设管理经验,对 Serverless 技术和架构有浓厚兴趣。
这个假期挺长的,不过有幸在腾讯云 Serverless 在线直播里看到了 Serverless 的相关课程,从第一期学完,还是凭添了很多学习乐趣。
前面三节课学了一些 Serverless 的基本知识和架构特点,也跟着开发部署,其实都蛮有趣的,唯一就是都没有管理后台。第四期课程很好的弥补了这一不足。刘宇老师给大家带来的项目 Python+HTML 的动态博客,后台是基于 Flask 的,虽然我不太熟悉这个框架,但是老师给提供了课程的源码,所以可以先用后学。 这节动态博客的直播回放地址是:
https://cloud.tencent.com/edu/learning/live-1926 刘宇老师的项目的 Github 地址是:
https://github.com/anycodes/ServerlessBlog
直播那天我提前准备好手机和电脑,就进教室了,不过当天课程收到网络的干扰很大,一直很卡,后来刘老师重新录制了课程,据说录到凌晨两点,这一点必须点赞,敬业精神太感人啦。在老师的热情带领下,我们的学习劲头也是十足的啊。
我的学习路径可谓十分曲折,整整折腾了三天,不过最后终于实现了项目的成功部署,很开心!
整体来说,其实项目部署就是四个步骤: 下载源文件 执行init.py 修改 serverless.yaml 文件 sls --debug 部署到云端。
我在每一个步骤都踩过坑,我基本上按照顺序把坑和解决方案列出来,感兴趣的同学可以基于上面的链接尝试开发部署,如果遇到相关的坑,可以对照参考。
坑一:Git 下载报错
Git下载报错,错误信息是这样如下: remote: Counting objects: 100% (1438/1438), done.remote: Compressing objects: 100% (1100/1100), done.error: RPC failed; curl 18 transfer closed with outstanding read data remainingfatal: The remote end hung up unexpectedlyfatal: early EOFfatal: index-pack failed
百度之后,发现针对RPC错误需要修改Git的buffer,调到700M后依然报了上面的fatal错误,和网络有关,最后放弃用git,选择老师发在文档里的压缩包,地址上面列出来了,大家可以去自行取用。
坑二:pip 的源设置和 pymysql
部署过程中因为 init.py 里面用到了 pymysql,可是我没有安装,于是打开Anaconda Prompt 开始用 pip 安装,然而 pip 好像死了一样,完全没有反应,最后报错说找不到包。后来经同学和老师提醒,修改 pip 的源到国内,我用了清华的源,速度一下很快啊,安装成功。修改源的代码是: pip install pymysql -i https://pypi.tuna.tsinghua.edu.cn/simple
如果大家需要安装其他包,就把 pymysql 换成要安装的包。
但是 init.py 依然报错,于是就吧 pymysql 的包放在项目目录中,解决了问题。
坑三:yaml 没有 FullLoader 属性
init.py 的执行一致不顺利,pymysql 的坑填完后继续报错。
于是以为自己没有安装对 yaml,后来老师建议删掉该语句,大家要注意,这个语句里 Fullloader 出现在一个逗号后面,所以我们只删掉逗号后面的 loader=yaml.FullLoader 就好了。
坑四:数据库连接
其实如果用老师提供的测试库的话,这个坑就不是问题,但是我觉得那么多同学做作业,老师的库压力会很大,正好我自己买了一个腾讯的云数据库是 MySql的,可以直接拿来用。
于是我就天真的把数据库地址改成了自己的,以为它可以连上,但是连接超时,于是我有把用户和密码改成自己的,结果还是拒绝访问,一直到我去云数据库控制台去测试连接,才发现原来我的数据库端口没有写对。
云数据库的外网链接设置还是很方便的,这次作业让我还进入了 PMA,进行了在线数据库操作,挺好用的。到这里,init.py 的坑就填平了,我看到新建数据库的成功。
坑五:网络问题
这个坑有点深,我在里面爬了两天才爬出来,而且之所以爬出来,完全是因为换了网络哦。
不得不说移动的家庭宽带真的不给力啊,Git 连不上,部署函数总是断线。我之所以在这个坑里没有很快出来,还有一个原因,就是每次的报错信息不一样,这深深的吸引了我。
我的报错信息五花八门,给大家分享一下:
这个错误出现的原因其实也很难想通,因为给的信息太少,并不知道到底是哪里有问题。后来还报过一个类似的错误,印象里是说读不到 'admin_add_article',我后来发现 git 上面又更新了一个文件夹 picture,里面有这个名字对应的图片,所以我就重新 git clone 了项目文件,把picture文件夹拷贝了过来。

这类 443 的错误,我是一直没有解决啦,直到我更换了网络。不过大家可以看到上面有一个存储桶的部署信息,那个桶不是我的。
关于 InvalidParameterValue 不太理解,什么样的 ID 是合法的呢?我考虑应该是网络中断丢失了数据,导致有些字符没了。
ECONNRESET 这个错误报了很多次,具体是什么意思不太明白,socket hang up应该还是网络不通畅吧。
还有一个是超时了,估计是网络情况不好。
这个 undefined RequestId 也是出现了多次的错误,很奇特。估计是网络数据丢包造成的吧。
以上所有的问题都是网络问题,解决方法:同学们提醒我不要部署到 hongkong 区域,可能海外服务器会有网络不稳定的情况,于是我换到了beijing,依然不行,我还换到多 guangdong,网络错误依然不断。
这个网络问题引发了我的不少猜疑啊:比如,我是不是频繁部署系统被封IP了?是不是防止勒索病毒,443端口封闭啦?我家的宽带是不是该换运营商了?
显然最后一个猜疑是并确认了的。我利用手机数据流量包部署系统就很快,240s 左右解决问题。
这里面其实报错信息不是很友好,首先,我不知道具体到哪一个文件的时候网络断开的,所以我不能肯定是不是某一个文件有问题,如果这时候能够定位一个断开的时候处理在哪一个文件,会对用户更有帮助。
而关于那个存储桶,应该是老师的,因为后来在部署成功的一次我在信息里看到了从老师的存储桶里上传了代码到一个存储桶里,而我在自己的存储桶列表里看到了那几个桶。
之所以有几个,是因为我换过几个区来部署。有一些程序老师可能部署在他的桶里,这样是不是省我们的流量呢?也可能是为了部署 Flask Admin 的过程更加平顺,老师把一些依赖部署到了线上的桶里。
记得上节课的老师也用了 Flask,他说 Flask 需要对应 Python 的精确版本,我们每个人的版本可能都会有些细小差别,造成部署过程的颠簸,老师为了避免这种状况,就额外处理了。老师,您用心啦!
坑六:UploadPicture
因为每次网络问题都发生在部署uploadPicture这个部分,时间很长,所以我都会忍不住cancel重来。
于是这个部分后来被我注释掉了。注释之后,部署很顺利,因为换用了手机的网络。这突如其来的顺利让我觉得 uploadPicture 可能是无罪的,我应该把它放回来。于是我就取消了注释,这时候灾难发生了,10000s 之后它还在部署。
我很好奇,为什么这个部分这么特殊,于是我打开一些 uploadPicture 的源文件来看,发现在 demo.py 里面有很多设置和我们的 global 设置不一样,于是我开始七七八八的修改起来,然而,老师说那个文件不在全局发挥最用,只是qqcloud_cos 这个依赖的一个 demo,于是我又改了回去。
后来,有同学说清空存储桶可以解决这种超长时间部署的问题,于是我试了一下。恩,真的管用哦!感谢同学的提醒。 PS:我后来回想很可能就是缺少 picture 那个文件夹吧,我猜测部署的过程可能会跑有些 test 程序,文件的代码会被执行,而如果里面的参数读取不到就会一直停在那里。
坑七:后台无法使用
部署成功之后,我访问后台,发现只有登陆页可用,其他页面基本上一点就报错,Internal error。在老师的提示下,我到了函数的控制台,查看了Blog_Admin的报错信息,发现是数据库连接不到。

可是我之前明明在数据库里新增数据的,于是我打开了 serverless.yml 文件和数据库里的库名称对照, 发现数据库的名称多了一个字母 l,哎,粗心啊粗心。修改了 yaml 文件后再次部署,成功了!
应该说控制台的日志挺详细的,我觉得如果能把成功信息和报错信息在颜色上区分一下就更好啦,目前看来是用时间戳来区分的,不过有时候请求多起来,很多正确和报错信息在一起,找起来很麻烦啊。要是有丰富一些的查询选项也许更好用。
最后,我终于跳过了这些坑,上了岸!
部署成功后的页面如下: http://blogdemo-1253166145.cos-website.ap-beijing.myqcloud.com/
于是,我带着刘宇老师布置的作业来投稿,信息化资产复用最大化,感觉是一次非常开心的学习体验! 传送门: GitHub: github.com/serverless 官网: serverless.com
欢迎访问: Serverless 中文网 ,您可以在 最佳实践 里体验更多关于 Serverless 应用的开发! 推荐阅读: 《Serverless 架构:从原理、设计到项目实战》
云计算
2020-03-28 20:37:00