电商业务容器化遇瓶颈公有云Docker镜像P2P加速很安全

当前,电商平台会采用基于Docker的容器技术来承载618大促期间的一些关键业务版块,包括最简单的商品图片展示、订单详情页面等等。 通过容器化改造,电商平台的每个业务版块解耦,可以独立开发、部署和上线,从而让后台业务系统具备更高的稳定性、可扩展性和安全性,即便某个环节出现问题,也能保障平台高峰值期间的平稳运行。 镜像是Docker容器的基石,只有通过它才可以创建容器,而Registry是存放Docker镜像的仓库。但在实际应用中,由于需要频繁地从Registry下载镜像运行容器应用(比如发布新版本,打补钉等情形),其间的文件传输成为镜像分发的瓶颈, P2P加速镜像下载是有效的解决方案,但如何确保用户数据在公有云环境下的P2P传输安全性尤为关键,本文主要从链路层和业务层的安全加固,阐述了公有云Docker镜像P2P加速的安全性。 问题:在使用Docker运行容器化应用时,宿主机通常先要从Registry服务(如Docker Hub)下载相应的镜像(image)。这种镜像机制在开发环境中使用还是很有效的,团队成员之间可以很方便地共享同样的镜像。然而在实际的生产环境中,当大量主机需要同时从Registry下载镜像运行容器应用时(比如发布新版本,打补钉等情形),Registry 服务往往会成为镜像分发的瓶颈,应用镜像需要较长时间才能传送到所有主机上,使得应用发布的周期大大延长。 不少企业提出了P2P加速镜像下载的解决方案,但都是私有云及内部环境的使用场景,在公有云未得到使用。其中很大一部分原因是公有云使用P2P的安全性问题,如何确保用户数据在P2P传输中是安全的成为了其中的难点。我们就该问题设计实现了确保用户数据安全的P2P镜像分发系统。本文就其安全性展开阐述。 架构: 华为P2P容器镜像分发系统示例图 华为P2P容器镜像分发系统包含3个组件:客户端代理(Proxy)、BT客户端和BT Tracker。 客户端代理(Proxy) 客户端代理部署在集群的每个节点中,配置为Docker的Http Proxy,截获Docker Daemon的镜像下载请求,通知Client下载,并最终将镜像导入到Docker daemon中。 BT客户端 部署在集群节点的BT客户端和Tracker共同组成了一个完整的P2P文件传输系统。在整个镜像的分发过程中,它们利用BT协议完成镜像下载。 BT Tracker Tracker是BT系统的一部分,它存储了BT客户端下载过程中所需要的元数据信息和种子信息,并协助各个BT客户端完成整个通信过程。 安全:首先,我们限制了跨集群的P2P下载,最大限度防止租户间的数据泄露。 之后,在链路层面的安全性和业务层面的安全性做了增强。 链路安全一想到链路安全,我们首先会想到的是加密。 对称加密服务端和客户端采用相同的秘钥加密和解密,只要这个秘钥不公开,并且秘钥足够安全,那么链路就是安全的。但是在网络中都使用相同的对称加密秘钥,无异于公开传输,如果秘钥被劫持,那么就可以篡改链路的所有数据。 然后我们肯定会想到HTTPS,它是怎么实现安全的?我们先来了解下HTTPS的实现方式。 在具体的数据传输过程中,HTTPS采用的是对称加解密的方式,但是它在连接建立时增加了握手协商的过程。 什么是公钥:公钥是非对称加密中的概念。非对称加密算法方式基于一个秘钥对,数据通过一个秘钥加密,只有通过另外一个秘钥才能解密。服务端保存私钥,公钥发给客户端。 我们假设一个场景,我们生成秘钥对,客户端通过公钥加密数据,服务端通过私钥解密。那么即使用户劫持到公钥,他无法劫持篡改用户的数据。然而从服务端到客户端的链路还是不安全的。 HTTPS借助了非对称加密的这个特性,确保对称机密秘钥的传输是安全的,最后采用对称加密传输数据。 证书的意义:然而,这又产生了一个新的问题,公钥被劫持了怎么办? HTTPS当然不会这么简单就被劫持,为了解决上诉问题,它引入了数字证书和第三方机构。证书是由第三方认证机构通过公钥签发的,其中不仅包含公钥,还包含签名( 由签发节点的私钥加密产生)、有限期、签发机构、网址、失效日期等。 HTTPS返回的不在是私钥,而是证书。当客户端接收到证书,会对证书做一个校验。在各个机器中都会维护一个权威的第三方机构列表(包括它们的公钥),当客户端需要公钥时,可根据颁发机构信息本地查找到公钥。客户端通过颁发机构的公钥验证签名的有效性和证书的完整性,保证公钥未被篡改。 HTTPS通过私钥、证书、和CA(签发机构)确保了链路的安全性。在P2P场景下,BT Client之间是对等的,他们相互传输数据,更应该是服务端校验客户端,而不是HTTPS的客户端校验服务端。并且由于BT Client是部署在用户的节点,还需要考虑证书和私钥都被劫持的风险。 我们是怎么做的Client之间BT Client间传输数据肯定是需要加密的,防止链路的数据被劫持。但是只增加HTTPS,虽然链路被加密,但是客户端可能会被假冒,只要假冒者不校验服务端的证书,直接和服务端握手,就能从其他BT Client获取到他想要的数据。 我们借鉴HTTPS的实现,采用了双向验证的模式。 需要有证书,首先需要一个统一的CA(签发机构),因此我们在Tracker中保存证书和私钥做为签发机构,Proxy获取种子的同时返回CA,用户校验客户端的证书。 然后,只使用一个证书对并且放在Bt Client是危险的,很有可能性被入侵截获到证书,因此我们获取证书的方式改为从Tracker获取,获取种子的同时获取Tracker生成的临时证书私钥对,把它加入BT Client的下载队列。在BT Client开始相互连接时,首先相互确认对方的证书的有效性(签名、签发机构等信息),校验通过后才能请求并相互下载数据。 这种方式下,Client之间的链路是安全的。 (1)链路经过证书加密,直接截获链路是不可行的 (2)即使仿照BT Client的方式,由于Client每个连接都需要进行双向的证书校验,想通过这个方式截获数据就必须请求Tracker去获取,而访问Tracker首先是HTTPS的,然后我们还做了业务层的安全校验(下文业务层安全会提及),也是不可行的。 Docker Daemon 到 Proxy我们在Proxy中需要劫持Docker的请求,因为Docker在不配置时访问Registry采用的是HTTPS,因此Proxy劫持Docker请求就必须和Docker保持HTTPS连接。 我们让客户端代理只监听localhost端口,杜绝外部使用该代理的可能性。同时,客户端代理绑定一套临时生成的签发给registry域名的自签名证书和CA证书,用于劫持Docker Daemon的请求,并将CA证书添加到机器的信任证书当中。 代理绑定的证书只保存在内存中,即使通过特定方式获取到当前节点的CA证书和服务端证书,也无法截取其他节点数据。 从用户节点到Registry、Tracker首先,为了确保链路的安全,Regstry、Tracker都绑定从权威第三方机构购买的HTTPS证书私钥对。Proxy和BT Client在访问它们的时候都会去校验证书的有效性,只要在证书有效的情况下才发送请求,这从根源上杜绝了Regstry、Tracker被假冒的可能。 业务加密在确保链路安全后,我们还做了一层业务安全加固。首先我们先了解下JWT Token。 ...

June 18, 2020 · 1 min · jiezi

OpenYurt-开箱测评-一键让原生-K8s-集群具备边缘计算能力

作者| 郑超 阿里云高级开发工程师 随着物联网技术以及 5G 技术的高速发展,将云计算的能力延伸至边缘设备端,并通过中心进行统一交付、管控,已成为云计算的重要发展趋势。为服务更多开发者把握这一趋势,5 月 29 日,阿里巴巴正式对外开源了基于 ACK@Edge(边缘集群托管服务)的云原生边缘计算框架 —— OpenYurt。 自 OpenYurt 开源以来受到了开发者的关注,今天这篇文章将带大家快速上手 OpenYurt ,介绍如何使用 OpenYurt 提供的命令行管理工具 Yurtctl, 高效快速地部署 OpenYurt 集群。 OpenYurt 介绍OpenYurt 主打“云边一体化”概念,依托 Kubernetes 强大的容器应用编排能力,满足了云-边一体化的应用分发、交付、和管控的诉求。相较于其他基于 Kubernetes 的边缘计算框架,OpenYurt 秉持着“最小修改”原则,通过在边缘节点安装 Yurthub 组件,和在云端部署 Yurt-controller-manager,保证了在对 Kubernetes 零侵入的情况下,提供管理边缘计算应用所需的相关能力。OpenYurt 能帮用户解决在海量边、端资源上完成大规模应用交付、运维、管控的问题,并提供中心服务下沉通道,实现和边缘计算应用的无缝对接。在设计 OpenYurt 之初,我们就非常强调保持用户体验的一致性,不增加用户运维负担,让用户真正方便地 “Extending your native kubernetes to edge”。 Yurtctl:一键让原生 K8s 集群具备边缘计算能力为了让原生 K8s 集群具备边缘计算能力,OpenYurt 以 addon 为载体,非侵入式给原生 K8s 增强了如下能力: 边缘自治能力(YurtHub: 已开源),保证在弱网或者重启节点的情况下,部署在边缘节点上的应用也能正常运行;云边协同能力(待开源),通过云边运维通道解决边缘的运维需求,同时提供云边协同能力;单元化管理能力(待开源),为分散的边缘节点,边缘应用,应用间流量提供单元化闭环管理能力;其他一些能力。对于大家比较关心的问题:如何将增强的边缘计算能力和原生 K8s 无缝融合。基于过往 ACK@Edge 的线上运维经验,我们开源了 Yurtctl 命令行工具,帮助实现了原生 Kubernetes 和 OpenYurt 之间的无缝转换以及对 OpenYurt 相关组件的高效运维。 ...

June 9, 2020 · 3 min · jiezi

容器与虚拟化的结合浅谈安全容器技术发展趋势

【摘要】无论公有云还是私有云厂商,都认识到了将虚拟化的隔离性和容器的高效运维特性相结合,是云原生平台发展的必然趋势。容器是如何解决隔离问题的众所周知,容器技术的出现有两个关键原因: 1. 软件运行过程中的资源和环境的隔离。 2. 软件因为运行环境多样带来的打包和配置的复杂性。 而对于软件运行环境的隔离需求,从计算机出现之初就已经开始了,多任务分时操作系统和虚拟地址的引入,都是为了解决多个任务在同一主机上运行,并且让任务本身认为自己独占机器。当然这样的隔离是远远不够的,当今软件,根据不同的层级,可以将隔离技术分为以下三类: 1. 进程级别的隔离 2. 操作系统级别的隔离 3. 虚拟化级别的隔离 操作系统以进程作为程序运行过程的抽象,进程拥有独立的地址空间,进程的执行依靠操作系统的调度。但是进程共享了文件系统,函数库等资源,程序之间出现互相干扰的可能性很大。这个层级的隔离适合在相同主机上运行单个用户的不同程序,由用户自己保证程序之间的资源分配。 上世纪70年代出现了chroot,进行文件系统的隔离,21世纪初开始,随着计算硬件的性能提升,软件隔离的需求更加强烈,这时候开始出现如jail,cgroup,namespace等各种不同资源的隔离技术。我们将这些技术统一划分到操作系统的隔离技术,这类隔离技术可以实现软件在诸如硬件资源、文件系统、网络、进程号等方面的隔离,但是不同的应用依然是运行在相同的操作系统内核上,对于恶意的利用漏洞攻击的场景,这种隔离技术依然会捉襟见肘。但是这种级别的隔离带来的额外资源消耗较小,适合相同的组织内不同用户的应用在相同主机上运行。 虚拟化技术的出现,让相同的物理机上也能运行多个不同的操作系统。操作系统对硬件的接口,由虚拟机管理程序(VMM)负责模拟。运行在不同系统中的程序,内核也是隔离的。硬件资源则通过各种硬件辅助手段进行划分,虚拟机上的程序很难突破虚拟化层加上的资源限制。并且因为内核的隔离,资源的可见性也做到了很强的隔离性。即使是恶意的用户,也很难突破这层虚拟化的限制,已达到攻击物理机,或者相同主机上其他虚拟机的目的。 以上三种隔离是按照层级一步一步加强的,同时带来的理论损耗也是逐步递进的。虚拟化由于需要模拟各种设备,带来的资源损耗比其他两种隔离方式要大很多。 什么是“安全容器”容器概念出来的时候,最初大家做隔离的思路,几乎都是操作系统级的隔离,也就是基于内核提供的namespace、cgroup、seccomp等机制,实现容器内的资源、文件、系统调用等限制和隔离。这种隔离方式更加高效,损耗更小。但是随着容器的大规模使用,尤其是各种容器编排系统,如k8s的深入使用,人们逐渐发现这样的隔离级别往往不能满足要求。在公有云场景下,相同主机如果需要运行不同租户的应用,因为这种隔离级别依然采用了共内核的机制,存在这广泛的攻击面,容器的隔离级别完全不能满足要求。所以最初的公有云上的容器服务,都是配合虚拟机的使用来完成的,首先是用户需要创建一批虚拟机作为运行容器的节点,形成一个私有的集群,然后才能在其上创建容器应用。虚拟化级别的隔离已经被各种公有云的实践证明,是一种安全的隔离技术。 自从云计算的概念提出开始,虚拟机一直是云平台的基础,无论是平台本身服务还是用户的使用,都是从IaaS平台创建通用虚拟机开始的。一般都是创建相应的规格的虚拟机,使用一个完成操作系统镜像启动一个完整的操作系统,随后在其安装,配置,运行软件和服务。包括我们上节提到的公有云容器服务的提供形式也是如此。 虚拟化本身带来的隔离能力是受到普遍认可的,不过IaaS层希望提供的是一个和应用完全无关的基础设施层,它几乎完全不感知应用的任何信息。于是从基础设施到应用运维,中间巨大的鸿沟则是由PaaS和用户自己来填平,这中间依然需要耗费无数的人力和成本。通用的虚拟机服务诚然有它的好处,比如完整的操作系统很适合程序调试,或者作为工作办公环境等等。但是对于多数运行于云平台的软件,它需要的是它独有的运行环境,它的运行环境由它的镜像已经完全定义好了。如果在此之外,又启动一个完整的操作系统,既浪费资源,也增加了运维的成本。为什么不能直接将虚拟化级别的隔离引入到容器技术中呢?结合虚拟化的安全性和容器在软件生命周期管理方面的优势,是不是可以给软件开发运维带来巨大的便利呢? 也就是在这个时间,docker和coreos等一起成立了OCI组织,其目的是将容器的运行时和镜像管理等流程标准化。OCI标准定义了一套容器运行时的命令行接口和文件规范,docker将其RunC捐给OCI作为运行时标准的一个参考实现。2015年Hyper.sh开源了RunV,则是一种基于虚拟化的容器运行时接口的实现,它很好地结合了虚拟化的安全性和容器的便利性。后来RunV和ClearContainer合并成立了kata项目,kata提供了更加完整的基于虚拟化的容器实现。我们把这种基于虚拟化的容器实现称作安全容器。 除kata之外,还相继出现了多个安全容器的实现方式,比如google的gVisor、AWS的firecracker-containerd、IBM的Nabla、VMware的CRX等等,其原理不尽相同,但共同反应了一个趋势,就是将容器的便利和虚拟化的安全隔离结合起来,让虚拟化直接和应用运维结合,成为云原生技术的大势所趋。下面对这些“安全容器”的实现技术进行简要的介绍和对比。 Google gVisor相比于其他几种实现,gVisor的显著不同之处在于,它通过拦截容器中应用的系统调用,模拟了一个操作系统内核,因此gVisor实际上没有虚拟化,而是在用户态实现了一个操作系统。这种方式可以降低因为虚拟化带来的模拟设备内存损耗。gVisor提供的runsc符合OCI标准,可以直接对接docker、containerd等容器平台,同时它也完全兼容docker的镜像格式。但是由于不是一个标准linux内核,因为应用的兼容性有较大问题。另外拦截系统调用带来的巨大的性能损耗也是阻止其广泛使用的一个阻碍。 图片来自https://gvisor.dev/docs/ IBM nablanabla是继承于unikernel的隔离方式,应用采用rumprun打包成一个unikernel镜像,直接运行在一个专为运行unikernel定制虚拟机(ukvm)中。应用直接打包首先可以降低很多内核态和用户态转换的开销,另外通过ukvm暴露非常有限的主机上的syscall(只剩7个),可以大大缩小主机的攻击面。它是这些安全容器实现中,最安全的。不过由于它要求应用打包成unikernel镜像,因此和当前docker的镜像标准是不兼容的。另外,unikernel应用在诸如支持创建子进程等一些常规操作上都有很难解决的问题。 图片来自https://unit42.paloaltonetworks.com/making-containers-more-isolated-an-overview-of-sandboxed-container-technologies/ AWS Firecracker最初firecracker是为AWS的Lambda打造的高密度轻量级虚拟化组件。由于它是从头开始构建虚拟化,带着轻量化的目的,因此他抛掉了qemu这种通用虚拟化组件的大部分功能,只留下运行容器和Function必要的一些模拟设备。因此它的内存开销非常小(小于5M),启动速度非常快(小于125ms),一秒钟可以在一个节点上运行150个轻量级虚拟机。 为了让firecracker-microvm更好的运行容器,AWS启动了firecracker-containerd项目,firecracker-containerd是containerd-shim-v2的一个实现,不符合OCI标准,但是可以直接对接containerd启动容器,也就很容易通过containerd对接k8s的CRI接口。containerd-shim-v2是containerd定义的新的runtime接口,它是对最初shim-v1的一个简化,可以大大精简runtime的组件和内存使用。containerd是一个全插件化的代码框架,扩展性非常好。firecracker-containerd在该框架下,实现了一个snapshotter作为镜像插件,负责块设备镜像的生成;实现了一个shim-v2的runtime,负责容器的生命周期管理。另外还有个fc-control-plugin作为虚拟机的管理插件,提供grpc接口供runtime调用。在轻量级虚拟机内部,有一个agent负责监听runtime传进来的vsock,接收runtime的指令进行虚机内部真正的容器生命周期管理操作,它是直接调用runc来管理容器的。 图片来自https://github.com/firecracker-microvm/firecracker-containerd/blob/master/docs/architecture.md VMware CRXVMware发布的vSphere 7与kubernetes进行了融合,它利用k8s的CRD将其集群的虚拟机、容器、函数等运行实体管理的所有功能都集成到了k8s中。VMware是一个做虚拟化起家的公司,因此虚拟化作为它的老本行,这块的积累是很深厚的。它将虚拟化融合到它的容器runtime CRX中,因此和firecracker-containerd和kata类似,也是一个基于轻量级虚拟化的容器runtime的实现。通过对其虚拟化组件和guest内核的精简,CRX 容器同样做到了很低的内存损耗(20MB)、快速(100ms)的启动以及高密度部署(单个节点大于1000个pod)。 图片来自https://cormachogan.com/2019/11/22/project-pacific-vmworld-2019-deep-dive-updates/ vSphere对node上的kubelet组件改造比较大,节点上的Spherelet部分功能和kubelet重合,负责pod的生命周期管理(他们称之为Native Pod),运行在虚拟机中的SphereletAgent则集成了符合OCI接口规范的libcontainer,实现容器的生命周期管理,以及容器的监控日志采集、容器的shell登录(kubectl exec)。Spherelet和虚机中的SphereletAgent交互实现类似于kubelet使用CRI接口管理pod的效果。 kata containers相比于以上几种,kata containers 最大的特点是它专注于实现一个开放的符合OCI标准的安全容器runtime实现,对于对接什么样的虚拟化方案,它抽象了一套hypervisor接口,如今已经对接了多种虚拟化实现,比如qemu、nemu、firecracker、cloud-hypervisor等等。 图片来自https://katacontainers.io/ 通过containerd-shim-v2和vsock技术,kata精简了大量的组件,配合轻量级hypervisor和精简内核,kata可以做到将额外内存消耗降低到10MB以下。容器启动时间降低到100ms以下。后续还会通过rust语言重写等方式,提供更低内存额外消耗。 图片来自https://github.com/kata-containers/documentation/blob/master/design/architecture.md 现有安全容器技术对比 安全容器技术发展趋势随着Serverless等技术的兴起,应用部署和运维工作已经下沉到云平台上,人们需要一个更加高效的云原生技术平台。从这几年涌现的安全容器实现技术可以观察到,无论公有云还是私有云厂商,都认识到了将虚拟化的隔离性和容器的高效运维特性相结合,是云原生平台发展的必然趋势。结合当前安全容器实现中遇到的一些问题,我们可以预见到,未来这项技术发展的几个走向: 需要一个为安全容器而生的轻量级hypervisor,当前qemu+kvm是主流的虚拟化技术,但因为qemu是为通用的虚拟机而设计的,其体量过于庞大,而对于安全容器而言,一个模块化可定制的虚拟化实现尤为重要。如果结合gVisor和nabla的实现来看,内核的可定制性也是安全容器场景的必然要求。rust-vmm即是这样一种模块化的虚拟化组件库。linux内核本身的模块化特性可以部分满足容器场景下的内核定制需求,不过也许像gVisor那样实现一个专为容器而生的内核也许是未来的趋势。容器的启动时间是衡量一个云原生平台效率的重要指标,尤其是在Serverless场景下,程序运行时间本身可能很短,这时候启动时间可能占用了其绝大部分,那么这种低效就显得尤为明显。安全容器通过极致的轻量化,可以将容器启动时间降低到100ms以下,但是容器镜像拉取时间过长仍是当前容器部署过程中的一个短板。由于当前容器镜像格式和镜像挂载方式等方面的限制,需要在启动容器之前将容器镜像拉取到本地以后,才能启动容器。而容器启动本身需要的数据只占镜像的6%左右。因此我们亟需一个高效的镜像挂载方式,当前已经有一些免下载和懒加载(Lazy-Loading)的技术原型,后续需要尽快推出一个可商用的镜像下载加速方案。3. 当前公有云的网络普遍采用原IaaS的网络管理模式,在地址分配,网络配置效率等方面完全赶不上容器快速启停的需求,docker容器采用的veth方式在性能等方面也很难满足高效转发的要求。因此需要一个专为云原生设计的网络管理方案。 4. 我们看到云平台对应用的管理逐步深入,从只管理基础设施的IaaS层,发展到管理应用整体部署和运维的PaaS,再到如今服务网格(Service Mesh)技术将平台管理能力深入到应用内部的微服务级别。同时我们也看到,以K8s容器、Istio服务网格为代表的云原生技术已经和下层的计算/网络虚拟化等技术逐步整合到了一起,比如安全容器即是容器与计算虚拟化的结合,而Istio服务网格也会与虚拟化网络进行深度整合以提供更高的性能与更精细的QoS控制。 5. 当前硬件加速技术在AI和大数据等领域大行其道,安全容器需要与各种计算加速硬件技术进行结合,使得AI、大数据、科学计算等批量计算领域的用户可以利用云平台直接投递其海量的计算任务,可大大降低他们在底层技术上的心智负担。通过硬件加速也是提升云平台本身效率、降低云平台运行成本的有效手段。比如华为云擎天架构在硬件加速方面拥有的技术优势在“安全容器”领域已得到充分的证明,CCE Turbo裸金属容器已经实现了安全容器的部分硬件卸载能力。 总结未来必然是云原生技术大行其道的时代,我们已经看到很多传统行业也逐渐认识到云原生技术可以给传统企业的IT软件,工业自动化,线上运维和数据管理等带来明显的效率提升。我们将看到通过对底层硬件和上层服务的全栈整合,打造出一个高效智能的云计算技术平台,而这中间,容器将是串联整个技术栈的关键所在。 ...

June 8, 2020 · 1 min · jiezi

阿里巴巴的云原生与开发者

摘要:利用云原生技术构建应用简便快捷,部署应用轻松自如,运行应用按需伸缩。如今,云原生已经成为下一代技术发展的趋势。在 2019 杭州云栖大会开发者峰会上,阿里巴巴资深技术专家李响就为大家分享了阿里巴巴的云原生技术与开发者的那些故事。为什么选择云原生?云原生的本质目标就是充分释放云计算带来的红利,阿里巴巴希望开发者能够使用云上极致弹性的资源交付能力,也能够使用云上极为便捷的产品和服务。阿里巴巴实践云原生也已经走过了几年时间,并在去年启动了全站上云。阿里巴巴希望业务可以更好地拥抱云,更加全面地推进云原生。 云原生既带来了技术红利,同时也带来了业务红利。 在资源效率方面,云原生为资源效率带来了极大提升。使用容器、调度等技术不仅提高了单机维度的部署密度和资源效率,还提高了集群维度资源配置的合理性和使用率。除此之外,将业务搬到云上,还可以使用云的极致弹性的资源交付能力,既能够应对流量洪峰,也降低了资源的使用成本。在开发效率方面,使用 Kubernetes 这样的平台增强了运维自动化的能力,也增强了对应用和资源进行编排的能力,并降低了运维负担,提高了开发效率。同时,阿里云也在推动 CI/CD 系统的演进,希望加速应用从完成到上线的过程,并提高这一过程的稳定性和确定性,提高开发人员迭代的信心。此外,阿里云也希望将通用框架下沉到基础设施中,使得开发者能够在多语言的环境下更容易地接入和使用平台的能力,其中具有代表性的就是对服务网格的探索。在标准与开放方面,通过云原生可以帮助阿里巴巴实现基础设施和生态标准的完全融合。这不仅可以将开源生态中的新技术、新理念引入到阿里巴巴的基础设施中,利用阿里内部的大规模场景和复杂业务进行打磨、锤炼,并将经过打磨的结果回馈给社区,让它带给广大开发者更多的技术红利。还可以将打造的技术引入阿里云,打造出一套完整的云原生产品技术家族,帮助云上的开发者。从 Kubernetes 开始谈到实践云原生,其实可以从 Kubernetes 这个云原生的基石开始。从 Kubernetes 开始实践云原生的原因主要有两点: 一是 Kubernetes 向下可以对接各种不同的资源层,既可以对接像阿里云这样的公有云,也可以对接专有云,因此可以利用 Kubernetes 构建混合云平台。二是 Kubernetes 向上提供了强大的自动化运维能力、编排能力以及强大的拓展性,因此可以在 Kubernetes 上层构建一些垂直性的平台能力,比如可观测性、服务治理以及无服务化。基于这些垂直能力,可以进一步构建更贴近业务的平台,这也正是 Kubernetes 本身“Platform for Platform”的定位。 阿里巴巴的规模阿里巴巴的规模非常庞大,内部有超过 10 个集群,超过 10 万个节点,以及超过百万级别的容器。那么,开源技术和开源项目是否能够承受阿里巴巴的体量?能够抗住双11峰值规模的考验呢? 阿里巴巴坚信一定要和上游共建生态,阿里不希望只是去 ForkKubernetes,而希望去锤炼和打造 Kubernetes,因此有三个策略: 第一个策略是对自身业务进行负载的追踪和模拟,并将追踪和模拟的结果应用到 Kubernetes 云原生体系中进行测试。第二个策略是对于测试过程中发现的规模性问题,会持续优化,并反馈到上游,使得整个社区受益,进而推动云原生领域的发展。第三个策略是持续推动社区去接受这些可扩展的能力,阿里巴巴也定制了自己的调度器和控制器,来满足自身的业务场景。Kubernetes 1.16 和 etcd 3.4在最近发布的 Kubernetes 1.16 和 etcd 3.4 版本中,阿里巴巴和蚂蚁金服也一同作出了大量贡献,单机的数据存储规模提升了 50 倍,单集群节点规模也提升了 3 倍,完全可以满足阿里巴巴这样体量的公司在双 11 这种场景下的需求。今年的 618,蚂蚁金服已经对 Kubernetes 集群进行了验证,在即将到来的双 11,阿里会对 Kubernetes 集群进行再次验证。如果阿里巴巴可以使用 Kubernetes 作为云原生的基石,相信 99.9% 的企业都可以信任 Kubernetes 以及云原生技术。 应用运维与交付除了云原生的基石 Kubernetes 以外,阿里巴巴还希望推动整体基础设施的全面升级,希望把云原生技术的红利传导到更上层。 基础设施中一个重要的应用就是交付和运维,以前面向虚拟机的交付和运维非常复杂,并且不同企业有自己不同的运维模式和方法,可能是手动也可能是半自动,甚至在同一家企业内,运维手段也不尽相同。云原生倡导的是统一的平台和统一的理念,希望能够实现自动化运维,因此构建统一的应用模型和统一的应用架构分层就非常重要。 因此,阿里巴巴也同社区一起在推进云原生领域中应用交付与运维分层模型。并且将这一理念在阿里内部进行了规模化落地,目前已经推动了阿里内部的 4 个应用管理产品线面向云原生全面升级,构建了 100 多个应用。阿里认为这套应用结构非常有意义和价值,因此也与 CNCF 成立了应用交付领域小组,并由阿里的张磊担任小组联系主席 ,希望与社区一起推进这个项目。 服务网格在云原生领域中,位于更上层的系统就是服务网格。服务网格的好处在于具有强大的多语言支持能力,能够降低业务对接服务治理的难度,更易于实现统一的基础设施升级。在真正的实践中,Service Mesh 也会面临一些挑战,比如如何支持更多的协议,能否在大规模场景下得以较好运用,数据链路、服务面是否足够稳定等。 为了解决 Service Mesh 面临的上述挑战,阿里巴巴和蚂蚁金服率先决定选用一套与社区兼容的体系,并在阿里巴巴和蚂蚁金服内部的云原生系统当中进行打磨。今年 618 蚂蚁金服已经完成核心系统上到 SOFAMosn 的验证工作,在马上来临的今年的双 11,阿里巴巴和蚂蚁金服将会在核心系统大规模上线 Service Mesh,同时会把自身技术演进的结果及时反馈到上游去,和社区一起把 Service Mesh 从一个非常先进的技术变成一个非常稳定和可靠的技术,并且会在阿里云推出 Service Mesh 服务,让阿里云上的开发者能够便捷地使用服务网格技术。 FaaSFaaS 是开发人员非常喜欢的技术和概念,它可以极大地提升开发效率,使得开发者无需关注脚手架,可以直接以响应式模式来填充代码。同时,FaaS 可以帮助大家实现自动的扩容和缩容,当遇到问题时也可以帮助大家自动恢复,免去了运维的困扰。 但 FaaS 本身也存在几个问题: 第一个问题是如何找到一个既能够适应不同业务,也能够适应复杂业务逻辑的编程模型。第二个问题是如何在提供运维和弹性的自动化能力的同时,不增加额外的开销。阿里巴巴希望在今年的双 11 考验 FaaS 服务的极致弹性和极致的启动时间,阿里也会把 FaaS 的研究结果及时反馈到上游和阿里云中去,为广大开发者提供更大的便利和红利。 开源社区贡献阿里巴巴与蚂蚁金服对于核心开源社区的云原生领域都做出了巨大的贡献,并且在 etcd、Kata Containers、Containerd 以及 Dragonfly 等项目中都有核心 Maintainer。除这些项目外,我们也会持续向 Kubernetes 等云原生其他领域提供贡献,希望通过我们的努力,可以让云原生成为一个稳定、可靠的技术,成为企业未来的 IT 基石。 引领开发者走向云原生阿里巴巴在中国的开源社区以及开发者社区中有深厚的积累,之前就一直在运作很多开源项目。阿里巴巴希望用自身构建的土壤引领中国开发者走向云原生模式。 DubboDubbo 是国内最受开发者欢迎的微服务引擎之一,最近阿里把 Dubbo 捐献给了 Apache 基金会,希望它可以成为更加开放、更加标准的微服务引擎,希望能够与社区和开发者共建这个引擎。 现在,阿里巴巴对 Apache Dubbo 有了新的期望,希望 Apache Dubbo 能够成为更云原生的微服务框架。因此,在 Apache Dubbo 未来的演进道路中,有三个具体目标: 第一个目标是希望 Apache Dubbo 作为 RPC 框架可以更好地结合 Service Mesh,并对 Apache Dubbo 进行一定程度的瘦身,将一些能力下沉到 Service Mesh 上去。第二个目标是希望 Apache Dubbo 可以变得更加标准,能够和其他生态体系互融互通,能够支持 HTTP/2,能够融入 gRPC、Spring Cloud、Kubernetes 体系,为开发者提供更多选择。第三个目标是希望提升 Dubbo 本身的可观测性,支持 OpenTracing 等,使得基于 Apache Dubbo 构建的分布式系统能够具备更高的可运维能力。Nacos Nacos 是阿里巴巴去年开源的一个项目,开源之后很快就收到了广泛的好评。目前在 GitHub 上有超过 8000 个 Star,也有数十家企业已经将 Nacos 应用到了生产环境中。Nacos 本身在阿里巴巴内部也支撑了巨大体量微服务的注册。Nacos 的研发目标是简单且易用,其提供了一组非常简单的 API,方便开发者使用 HTTP 或 DNS 进行服务发现。阿里巴巴希望 Nacos 能够成为联动传统微服务框架和云原生微服务框架的桥梁,因此也希望 Nacos 既可以注册传统服务,又能注册云原生服务,让两边的服务实现互融互通。这样才能帮助阿里巴巴以及广大开发者平滑、稳定地从传统服务治理模式演进到云原生的服务治理模式。 ...

October 15, 2019 · 1 min · jiezi

K8s-从懵圈到熟练-–-集群网络详解

导读:阿里云 K8S 集群网络目前有两种方案:一种是 flannel 方案;另外一种是基于 calico 和弹性网卡 eni 的 terway 方案。Terway 和 flannel 类似,不同的地方在于 terway 支持 Pod 弹性网卡,以及 NetworkPolicy 功能。本文中,作者基于当前的 1.12.6 版本,以 flannel 为例,深入分析阿里云 K8S 集群网络的实现方法。鸟瞰总体上来说,阿里云 K8S 集群网络配置完成之后,如下图所示:包括集群 CIDR、VPC 路由表、节点网络、节点的 podCIDR、节点上的虚拟网桥 cni0、连接 Pod 和网桥的 veth 等部分。 类似的图大家可能在很多文章中都看过,但因为其中相关配置过于复杂,比较难理解。这里我们可以看下这些配置背后的逻辑。 基本上我们可以把这些配置分三种情况来理解:集群配置,节点配置以及 Pod 配置。与这三种情况对应的,其实是对集群网络 IP 段的三次划分:首先是集群 CIDR,接着是为每个节点分配 podCIDR(即集群 CIDR 的子网段),最后在 podCIDR 里为每个 Pod 分配自己的 IP。 集群网络搭建初始阶段集群的创建,基于云资源 VPC 和 ECS,在创建完 VPC 和 ECS 之后,我们基本上可以得到如下图的资源配置。我们得到一个 VPC,这个 VPC 的网段是 192.168.0.0/16,我们得到若干 ECS,他们从 VPC 网段里分配到 IP 地址。 ...

October 9, 2019 · 2 min · jiezi

我们为什么需要关心容器

我们为什么要关心Docker? 好。第一个问题:什么是Docker? 如果你是一名开发人员,你可能遇到过以下问题:软件可以在机器上运行,但是在测试环境中却无法工作。那么,Docker在最基本的层面上解决了这个问题。 Docker什么时候开始工作? Docker在软件开发生命周期(SDLC)的部署阶段发挥作用。它简化了应用程序的部署过程,解决了大量相关问题。 Docker是做什么的?软件不仅仅是一段代码。它由前端组件、后端服务器、数据库、信封、库和依赖项组成,我们必须确保所有组件都能正常工作,并且软件也能在所有平台上运行,这包括:手机、平板电脑、电脑,以及所有不同的操作系统。如果我们画一张所有组件和平台的图,我们会看到每个组件都连接到各个平台——看起来就像一个神经网络一样! 可以用一个简单的比喻来理解这一点:想象你要搬家了,你必须带着所有的东西去一个不同的国家。你移动的东西有不同的大小和形状,有不同的要求(易碎品,“轻拿轻放”,等等)。你必须考虑运输、包装、专业劳动力、运输。但是如果你有一个容器,那么所有这些问题都会消失,你所要做的就是把你的东西完整地、正确地打包在容器里。 Docker是一个通过使用容器来进行轻松部署和运行应用程序的工具。Docker允许开发人员将应用程序与所需的所有部分(如库和依赖项)打包在一起,并将它们作为一个包发送出去。Docker通过将其发送到目的地并在所有可能的平台上运行它来处理其余的工作。 开发人员需要考虑的是如何开发软件,他们只需要将其打包,而不需要考虑运行平台的需求。 怎么创建Docker容器 Docker文件用于构建一个Docker映像,该映像还将包含所有项目代码。同一个Docker映像可以用来旋转多个容器,每个容器都对底层映像进行了修改。最后的映像可以被上传到Docker的中心,并与其他协作者共享以进行测试和部署。因此,构建Docker映像将是你最主要的挑战。Docker容器只是Docker映像的一个实例。 定义Docker文件:一个文本文档,包含了用户可以在命令行上调用的用来组装图像的所有命令。 Docker镜像:一种只读模板,用于创建Docker用户构建的容器;它将存储在Docker中心或本地注册表中。 Docker容器:独立的应用程序平台,包含了运行由一个或多个映像构建的应用程序所需的一切。 什么是Docker Compose?通常,我们使用一个容器来承载一个服务器。考虑一个使用许多web服务器的大型产品,这些服务器必须单独或在单独的虚拟机(vm)中运行。 在这种情况下,运行这么多不同的vm会使计算机过热并降低所有进程的速度。但是,我们有一个单独的容器,叫做Docker Compose,它将包含一个Docker Composer。如果我们在编写器中有两个Docker映像,它将在两个实例中运行,允许它们彼此交互并监视它们。 假设你正在使用MongoDB、Express、Angular和Node.js开发一个全堆栈应用程序。您必须使用三个包含三个容器的Docker文件。一个容器用于MongoDB作为数据库;一个容器用于服务器,它是Express和Node.js的组合;另一个容器用于Angular(前端客户端)。 如果你不知道完整堆栈应用程序是如何工作的,那么你首先需要了解用户所在的客户端。用户向应用程序发送请求,然后应用程序将请求发送到服务器。服务器(即后端)执行所有的功能。然后,服务器从数据库(本例中为MongoDB)获取数据,并将其发送到用户的前端。容器中的所有Docker文件都由用YAML编写的Docker撰写文件控制。在YAML文件中,你必须指定容器的位置,以及Composer与其他容器交互时所必须使用的端口号。 简单来说,Docker组合用于运行多容器应用程序。每个容器将运行一个独立的应用程序,它可以与同一主机中的每个容器通信。 Docker使用客户机-服务器架构。请参见下面的图片进行说明。 原文链接:https://medium.com/better-pro... 以上信息来源于网络,由“京东云开发者社区”公众号编辑整理,不代表京东云立场。点击“京东云”了解京东云原生容器产品

October 9, 2019 · 1 min · jiezi

从-SOA-到微服务企业分布式应用架构在云原生时代如何重塑

阿里妹导读:从十余年前的各种分布式系统研发到现在的容器云,从支撑原有业务到孵化各个新业务,企业的发展离不开统一的、与时俱进的技术架构。本篇文章从企业分布式应用架构层面介绍了云原生计算架构带来的变化,希望能够帮助更多企业的 IT 转型,利用云计算技术推动其成为市场竞争中的敏捷力量。进入 21 世纪以来,我们见证了企业分布式应用架构从 SOA (Service-oriented Architecture),到微服务架构,再到云原生应用架构的演化。 为了说明企业架构演化背后的思考,我们先谈一些玄学。 第一,企业 IT 系统的复杂性(熵)符合热力学第二定律。随着时间的推演,业务的变化,企业 IT 系统的复杂度会越来越高;第二,在计算机交互设计中有一个著名的复杂性守恒定律[1]。应用交互的复杂性不会消失,只会换一种方式存在。这个原理也同样适用于软件架构。引入新的软件架构,不会降低IT系统的整体复杂性。听到这里,是否让生命不息、折腾不止的我们感到一丝凉凉? 现代软件架构的核心任务之一就是定义基础设施与应用的边界,合理切分复杂性,减少应用开发者需要面对的复杂性。换句话说,就是让开发者专注在核心价值创新上,而把一些问题交给更合适的人和系统来解决。 我们就从下面这张图开始,探究企业分布式应用架构演进背后的逻辑。 本图来自 Bilgin Ibryam 的 twitter[2] 蜕变之痛:SOA2004 年,IBM 建立 SOA 全球设计中心,我作为研发 TL 和架构师参与了一系列全球客户的 pilot 项目,帮助 Pepboys, Office Depot 等国际企业利用 SOA 优化企业内部和企业间的业务流程,提升业务敏捷性。 当时的大背景是:随着经济全球化逐渐深入,企业面对的竞争加剧,商业变革也开始提速。在大型企业内部的 IT 系统已经经过了数十年的演化,整个的技术体系变得异常复杂,并存着诸如主机系统上的 CISC/COBOL 交易应用,小型机 AS400 中的 RPG 业务系统,和 X86/Power 等分布式系统的 C/JEE/.Net 应用。 大量应用系统由三方供应商提供,一些系统甚至已经无人维护。而且随着业务迭代,一些新的业务系统被持续构建出来,由于缺乏合理的方法论指导,系统之间缺乏有机的链接,形成了若干的孤岛,持续加剧了 IT 架构的复杂性,无法支撑业务的发展诉求。这就仿佛各派高手为了帮助受伤的令狐冲,把异种真气输入体中,虽然短时间可以缓解伤势。可是多道真气无法融合,互相激荡,长时间下来会伤上加伤。 因此,企业 IT 所面临的首要挑战就是整合企业中大量竖桶型(silo-ed)的 IT 系统,支撑日益复杂的业务流程,进行高效的业务决策和支撑业务快速变化。 在这种背景下,IBM 等公司提出了 SOA(面向服务的架构)理念,将应用系统抽象成一个个粗粒度的服务,构建松耦合服务架构,可以通过业务流程对服务进行灵活组合,提升企业 IT 资产复用,提高了系统的适应性、灵活性和扩展性,解决“信息孤岛”问题。 SOA 提出了一系列构建分布式系统的原则,这些思考直到今天也依然适用: 服务具备明确定义的标准化的接口。通过服务定义描述,将服务消费者(Service Consumer)和服务提供者 (Service Provider) 的实现进行解耦,并且服务应该采用 contract-first 而非 code-first 方式进行开发。服务间通信采用面向文档的消息而非特定语言 RPC 协议,一方面可以解决服务与实现语言的解耦,另一方面可以灵活选择同步或者异步的通信实现,提升系统可用性和可伸缩性;服务应该是松耦合的,服务之间不应存在时间、空间、技术、团队上的依赖;服务应该是无状态的,使得服务调用与会话上下文状态实现解耦;服务应该是自治和自包含的,服务的实现是可以独立进行部署、版本控制、自我管理和恢复;服务是可发现、可组合的。比如可以通过 Service Registry 进行服务发现,实现了服务消费者和服务提供者的动态绑定。业务流程中可以对来自不同系统的的业务服务进行编排组装。在初始构建 SOA 系统的时候,大多采用点对点的通信连接,服务调用和集成逻辑被内嵌在应用实现中。这种方式在服务数量比较少的时候,确实是一种简单和高效的开发方式。但其最大的问题是,随着服务规模的增长,服务之间通信愈发复杂,连接路径和复杂性会剧增,给服务治理带来巨大的挑战。 ...

October 8, 2019 · 2 min · jiezi

干货-博云基于OVS自研容器网络插件在金融企业的落地实践

本文根据博云在dockerone社区微信群分享内容整理 过去几年博云在企业中落地容器云平台遇到了很多痛点,其中一个比较典型的痛点来自网络方面,今天很高兴跟大家聊聊这个话题并介绍下我们基于OVS自研的CNI插件——内部称之为fabric项目。 01容器平台落地时网络方面的需求 从2013年左右Docker技术在开发者中流行起来,到如今kubernetes已经成为事实上的容器编排引擎,容器、微服务、DevOps互相支持互相促进,容器云平台的实际落地案例开始越来越多。特别是2018年以来,越来越多的企业开始思考如何利用容器云平台支持其生产场景最终提高生产效率。 不同于开发测试场景,生产场景上线一套平台或系统要求会严格很多。安全、监控、流程、现有系统集成、业务暴露等等的建设要求都要匹配上,否则不可能上线。在这个过程中,特别是在传统的金融类企业对监管要求严格的情况下,容器云平台落地时会碰到很多问题,这其中,最典型的一个需求就是容器云平台的网络建设,必须同时满足业务方,运维人员,安全人员,网络人员的诉求。 现在容器云平台大部分都是基于Kubernetes构建,市面上的CNI插件也非常多,各个企业现网的需求也有很大的不同,所以几乎不可能出现一种网络模型适配所有客户场景的情况。现在主流的比较成熟稳定的CNI比如calico在扩展性、稳定性等方面表现优秀,但在传统金融类企业落地时非常困难,经常需要对不同的需求做出妥协。 我们和多家客户进行了深入沟通,虽然需求有所差异,但总结下来主要的诉求包括:在主流的二层组网的数据中心中,受限于硬件能力或管理复杂度,大部分客户不希望引入BGP等三层路由概念。企业业务系统往往会在容器云平台内外同时同时部署,希望平台内外网络能够直接打通。IP地址是业务的身份识别,希望能够具备固定IP的能力,而且是可管理、可审计的IP地址。管理网络和数据网络分离。具备网络隔离能力,硬件隔离的强安全性和软件隔离的灵活性都需要。网络模型应该尽量简单,易于掌控,易于调试。高性能,低抖动的网络吞吐能力。其他的高级特性,如双向限速、DPDK、overlay等。 我们对市面上主流的CNI插件进行了广泛的调研后,发现主流的CNI对以上“国产化”的需求的支持并不理想,主要的不满足点包括: 网络模型差异(三层VS二层,当然L2的方案也很多,OVS有流表等等高级功能,非常适合当今云化的环境),要适配现网环境、安全策略等。 云原生理念。主流的CNI较好的满足了云原生的概念,但客户的实际需求中其实是有些“anti-cloudnative”的,如何在cloudnative和anti-cloudnative之间做到平衡其实普遍缺少实践经验。 简单稳定可靠。这其实是非常重要的要考虑的点,厂商、企业都要有相应的人员能够掌控网络模型,毕竟网络作为云平台的底层,出现问题后影响太大。 我们针对网络建设的核心需求及社区现状综合分析之后,决定启动beyondFabric项目,目前该项目已经作为博云容器云平台重点支持的两个网络模型(calico/beyondFabric)之一,支撑了多家企业的生产系统的稳定运行。 02BeyondFabric BeyondFabric是我们自研的kubernetes CNI插件,利用etcd作为其数据存储单元,内置完善的IPAM能力,能够很好的满足前面提到的客户的核心诉求。因为BeyondFabric是基于二层网络设计的,同时针对特定需求做了很多优化,所以其在一部分场景下(特别是国内高度重视安全的金融类企业数据中心中)表现良好;但同时也决定了BeyondFabric不能适合于所有的场景,具体选择哪种CNI还是要根据自身情况作出评估。(实际上没有任何一种CNI能满足所有的场景需求。) fabric经典模式示意图 从fabric的概念图中可以一目了然的看清楚云平台的网络拓扑,每个计算节点上安装一个OVS,并且作为一个单纯的虚拟交换机使用,容器通过veth pair连接到OVS的端口上从而自然的获得物理环境下的网络身份;在网络的层面上,容器、虚拟机、物理机是完全对等的。不论是网络管理人员还是业务人员都可以简单清晰的了解到网络的拓扑情况。而且在这种简化的部署模型中(同时也是使用度最广的模型)不包括控制器等复杂逻辑,提供了简单、高效、稳定的网络环境。 fabric的组件图 fabric是基于OVS的CNI插件,其具体职能为为POD组建网络并设置IP地址。fabric-ctl负责网络及IP地址管理,通过RESTFUL API提供网络/IP的管理能力,如创建网络, 编辑网络,查找IP等。fabric-ctl本身是无状态的,所有状态信息存储到etcd中。fabric-admin的主要使用人员为平台管理员或BOC运维人员,方便使用人员查看和管理网络及IPAM等。fabric-admin的命令行格式参考Kubectl。 在经典组网模式下,将ovs作为一个基本的虚拟交换机使用即可,非常简单。如果使用networkpolicy等隔离策略,需要在每个节点上引入一个分布式控制器。 网络管理能力fabric项目除了CNI协议规定的组建容器网络的功能之外,还以restful API、annotation等方式额外提供了对网络的管理能力。通过界面集成后可以方便管理人员使用,如下图中的增加网络,查看网络,查看IP地址使用,固定IP等。 增加网络 查看网络 查看IP地址使用 固定IP地址 成熟度接下来看一下fabric项目的成熟度,一个项目的成熟度是由很多方面决定的,除了fabric设计之初的简单网络模型,成熟的组件(无额外复杂组件,即使在做策略控制/overlay等场景下,也只是在每个节点上引入一个分布式控制器)之外,我们还做了以下几个方面的工作。 fabric-admin考虑到软硬件层面的异常情况,例如kubelet或fabric的bug,环境(硬件损坏)等均可能对系统的正常运行造成不同程度的影响,所以提供了一个fabric-admin的工具,位于/opt/cni/bin目录下,其作用类似于文件系统的FSCK能力,为运行时管理提供保障。同时其命令行格式完全匹配kubectl,对熟悉kubernetes的用户非常友好。 例如可以查看pod的IP占用情况(示例输出已被截断) : 同时,fabric-admin还提供了多种运行时管理能力支持,运行--help后可以提示: 如同FSCK是文件系统成熟的重要标志,fabric-admin是beyondFabric项目成熟的有力保障!(fabric-admin虽然功能强大,但客户现网环境中还从来没有被使用过,也从侧面说明了fabric项目的成熟度) kubernetes社区CNI测试套件因为fabric项目完全满足CNI协议规范,因此可以使用任意的CNI测试工具进行测试。我们的测试团队结合社区提供的CNI测试工具及k8s job对象等对beyondFabric进行了长时间的严格测试,测试结果证明fabric项目具备生产可用能力。 多种平台支持私有云建设中,容器云平台一般运行在物理环境或vmware/openstack等虚拟化环境中。fabric对于这几种部署环境均能完善支持。对于网络环境复杂不易变更的场景下,fabric基于overlay可以显著减少环境依赖。 多个落地案例博云容器云平台基于fabric已经有多个的落地案例,在可管理性、稳定性、性能等多个方面运行良好。 BeyondFabric性能接下来看一下fabric的性能表现。由于fabric采用了稳定可靠的OVS作为其基本单元,所以从原理上讲其性能损耗应该是非常小的,我们在物理环境中基于万兆网络的性能测试也验证了这一点。图中可以看出pod-pod/pod-node的损耗较node-node约在5%左右。 博云容器云平台网络模型选型建议然后我们来看一下选型建议。在客户落地容器云平台的过程中,我们会和客户进行大量沟通,其中一个很重要的沟通就是涉及业务方、安全人员、网络人员、运维人员的网络模型沟通。具体的选型建议如下表所示,最终的选型结果由所有涉及人员共同决定。 fabric项目的小结OK,简单总结一下fabric项目。fabric项目解决了企业落地容器云平台的一些主要的痛点,通过经典网络模式可以很好的满足各个职能部门的诉求。但毕竟没有任何一种网络方案能满足所有的网络诉求,fabric也有其天生的缺点,例如经典网络模式下需要客户真实的IP网络,这些网络资源在容器化的环境下消耗速度很快,需要根据业务需要提前创建好网络资源,然而有些客户的IPV4资源会比较紧张。当然这一点随着VXLAN的支持和IPV6的普及,将会得到很大的改善。fabric核心是二层的解决方案,二层就必然面临扩展性的问题,我们目前的解决方案是通过分区的概念去映射真实的网络分区,然后通过扩展分区的方式扩展Kubernetes集群。 Q:IPAM的固定IP是怎么实现的?IP与Pod UID关联吗?A:管理员录入网络信息后,Fabric会将所有IP地址存储到etcd中统一管理。目前固定IP是通过给deployment等workload对象增加Annotation实现的。IP不与Pod UID关联。 Q:这里面提到的三层网络和二层网络是指七层协议的三层二层吗?A:是的,比如交换机工作在2层,路由器工作在三层。 Q:服务负载均衡怎么实现的呢?A:外部流量导入集群的负载均衡是通过另外一个组件,ingress controller实现的,没有实现在CNI里面。Kubernetes svc的负载均衡是通过iptables实现的,Fabric项目也会往iptables里面加入一些规则,主要是跨节点SNAT。 Q:支持流量限流么?A:支持Ingress/Egress限速,通过给容器加Annotation即可以实现容器的限速。 Q:有和Contiv做过对比吗?A:选型阶段做过,比较早了,那时候貌似Contiv还不太成熟,所以没深入研究。 Q:这些网络方案有什么好的学习方式吗?A:网络虽然很复杂,但万变不离其宗。容器网络这个词最近几年比较流行,是因为网络再容器环境下遇到了一些挑战,但网络本质的概念还是过去非常成熟的那一套。所以首先得学习基本的网络知识,然后去看下容器环境快速弹性等带来的痛点。 Q:TC怎么实现的?A:这个实现的比较久了,早在过去重点支持Calico的时候就已经做了。有些细节模糊了,但基本是通过Linux tc实现的,因为本质是veth pair,所以限速可以在主机侧veth端实现。基本的限速命令可以查找tc机制就可以了,我们碰到限速不太准确,最后也通过调整参数解决了,误差控制在百分之几吧。 Q:与Kube-OVN做过对比吗?A:Kube-OVN是友商开源的产品,我了解过。首先Kube-OVN和Fabric项目都是基于OVS进行研发的,都支持Overylay/underlay模式,都可以实现CNI协议。但其实差别还是比较大。OVN项目源于OpenStack,OpenStack里的网络模型是非常重的,概念、组件都比较多,OVN也在试图统一Kubernetes/OpenStack的网络模型,所以Kube-OVN里有一些能力其实已经不在CNI spec的范围内了,比如负载均衡,DNS等,其实在社区都有对应的实现。而Fabric会简单很多,是一个标准的CNI实现,网络模型也非常清晰,能够把容器直接融入现网环境,企业的网管一般都能掌控,对安全监管等已有体系兼容性比较好。 网络是非常复杂的,很难有一个统一的模型去兼顾所有的场景,个人认为这也是Kubernetes社区聪明的地方,把这些复杂的,不宜标准的东西抽象出来,交给第三方去做。也正是由于CNI协议的简单性和网络的复杂性,现在市场上CNI可以说百家争鸣,各有所长。个人认为这是一个非常好的现象。具体使用哪种CNI,还是要根据企业自身的情况作出决定。

August 8, 2019 · 1 min · jiezi

Kubernetes事件离线工具kubeeventer正式开源

前言监控是保障系统稳定性的重要组成部分,在Kubernetes开源生态中,资源类的监控工具与组件百花齐放。除了社区自己孵化的metrics-server,还有从CNCF毕业的Prometheus等等,开发者可选的方案有很多。但是,只有资源类的监控是远远不够的,因为资源监控存在如下两个主要的缺欠: 监控的实时性与准确性不足大部分资源监控都是基于推或者拉的模式进行数据离线,因此通常数据是每隔一段时间采集一次,如果在时间间隔内出现一些毛刺或者异常,而在下一个采集点到达时恢复,大部分的采集系统会吞掉这个异常。而针对毛刺的场景,阶段的采集会自动削峰,从而造成准确性的降低。 监控的场景覆盖范围不足部分监控场景是无法通过资源表述的,比如Pod的启动停止,是无法简单的用资源的利用率来计量的,因为当资源为0的时候,我们是不能区分这个状态产生的真实原因。 基于上述两个问题,Kubernetes是怎么解决的呢? 事件监控-监控的新维度Kubernetes作为云原生的平台实现,从架构设计上将接口与实现做到了完整的解耦和插拔,以状态机为整体的设计原则,通过设定期望状态、执行状态转换、检查并补偿状态的方式将资源的生命周期进行接管。 状态之间的转换会产生相应的转换事件,在Kubernetes中,事件分为两种,一种是Warning事件,表示产生这个事件的状态转换是在非预期的状态之间产生的;另外一种是Normal事件,表示期望到达的状态,和目前达到的状态是一致的。我们用一个Pod的生命周期进行举例,当创建一个Pod的时候,首先Pod会进入Pending的状态,等待镜像的拉取,当镜像录取完毕并通过健康检查的时候,Pod的状态就变为Running。此时会生成Normal的事件。而如果在运行中,由于OOM或者其他原因造成Pod宕掉,进入Failed的状态,而这种状态是非预期的,那么此时会在Kubernetes中产生Warning的事件。那么针对这种场景而言,如果我们能够通过监控事件的产生就可以非常及时的查看到一些容易被资源监控忽略的问题。 一个标准的Kubernetes事件有如下几个重要的属性,通过这些属性可以更好地诊断和告警问题。 Namespace:产生事件的对象所在的命名空间。Kind:绑定事件的对象的类型,例如:Node、Pod、Namespace、Componenet等等。Timestamp:事件产生的时间等等。Reason:产生这个事件的原因。Message: 事件的具体描述。其他信息通过事件的机制,我们可以丰富Kuernetes在监控方面的维度和准确性,弥补其他监控方案的缺欠。 kube-eventer v1.0.0的发布与开源针对Kubernetes的事件监控场景,Kuernetes社区在Heapter中提供了简单的事件离线能力,后来随着Heapster的废弃,相关的能力也一起被归档了。为了弥补事件监控场景的缺失,阿里云容器服务发布并开源了kubernetes事件离线工具kube-eventer。支持离线kubernetes事件到钉钉机器人、SLS日志服务、Kafka开源消息队列、InfluxDB时序数据库等等。 在本次正式发布的v1.0.0的版本中,作了如下功能的增强。 钉钉插件支持Namespace、Kind的过滤支持与NPD插件的集成与部署优化SLS插件的数据离线性能修复InfluxDB插件启动参数失效的问题修复Dockerfile的安全漏洞以及其他共11项功能修复典型场景分析: 使用钉钉进行事件告警使用SLS进行事件告警结合NPD进行异常检测与事件告警,此外使用容器服务的开发者可以直接用应用目录中的Helm Chart进行部署。项目开源地址:https://github.com/AliyunContainerService/kube-eventer 本文作者:莫源阅读原文 本文为云栖社区原创内容,未经允许不得转载。

July 15, 2019 · 1 min · jiezi

宜信开源UAV心跳机制与容器进程数据采集

服务心跳机制主要用于确认服务的存活状态,UAVStack的心跳数据还负责上报节点的容器及进程监控数据,支持在前端实时查看应用容器和进程的运行状态,并根据这些数据对容器和进程做出预警。 一、背景在微服务架构中,服务心跳是一个简单但非常重要的机制,用于确认微服务的存活状态。UAVStack中的心跳是一个Http请求,MonitorAgent(以下简称MA)通过定时向HealthManager(以下简称HM)发送一个带有特定报文格式的Http请求完成一次心跳的发送过程。心跳报文含有发送时的时间戳,用于更新HM端的数据状态。 与普通的心跳不同,UAVStack中的心跳还负责上送MA端的应用容器和进程监控数据。每次发送心跳的时候,在MA端会有定时任务去收集MA所在的应用容器心跳的基本信息,及应用容器上的进程数据,随着心跳数据包一起上送。 本文将首先介绍UAVStack的基础心跳机制,之后对应用容器、进程的数据采集做详细说明。 二、基本架构心跳的实现有很多种方式,心跳的发起可以由客户端发起也可以由服务端发起,只需完成确认存活这一基本功能即可。但是在一般的实现中,我们更倾向于客户端主动向服务端进行报告,因为当客户端逐渐增加,单纯通过服务端的轮询会导致服务端的压力,影响性能。 在UAVStack的实现中,我们也采用了这样的方式,通过客户端(MA)主动向服务端(HM)发送心跳信息,告知HM自身的存活情况。 一次心跳由UAV的MA和HM共同完成: MA定时生成心跳数据,携带MA节点的应用容器信息、进程信息以及服务信息,通过Http请求上报给HM;HM负责将接收到的心跳数据存入Redis缓存,并定时扫描心跳数据,确认节点的存活状态。对于随同的应用容器等监控信息,会在Redis进行暂存,后续随着HM的定时任务最终存入OpenTSDB进行落盘。整体的架构如下所示: 三、基础心跳机制 心跳服务主要流程如上图所示,其逻辑有以下几步: 1)MA的定时心跳任务生成一个空心跳数据,将心跳数据交给MA端的容器、进程数据采集任务。 2)MA端的容器、进程数据采集任务负责产生心跳数据的时间戳、采集节点的应用容器、进程监控数据、节点的基本信息、节点的可用服务信息等。经过以上过程之后,心跳数据将包含以下内容: 心跳时间戳:节点发送心跳时的时间戳,方便HM后续比对判定节点的存活状态。应用容器的基本信息:包括节点ID、名称、主机名、IP等。应用容器的简单监控数据:包括CPU负载、内存使用、硬盘使用等。应用容器上的进程信息:包括每个进程的pid、资源占用等。节点的能力信息:包括该节点上启用的Feature功能。节点的服务信息:包括该节点上可提供的服务及其访问接口,用以实现服务发现。可选)子节点的节点信息:如果MA和HM部署在不同的网段,则MA无法通过直接Http的方式推送数据给HM,此时需要MA将数据上送给自己网段内的HM,再由该HM的心跳客户端发送给总的HM。这种情况下,上报的数据中会携带子节点的节点信息。每个节点信息均会包含上述几种数据。最后将心跳数据发送给HM。 3)HM端在接收到心跳数据之后,将其存入自身的Redis缓存。使用上报数据中的服务信息更新Redis中的服务状态,用于服务发现请求。 4)HM端在启动心跳接收服务时,会同时启动心跳检查任务。这个任务会定时扫描Redis中的心跳数据,根据当前系统时间与心跳时间戳的差,判断心跳节点的存活状态,更新节点的状态,并对于过期的节点做删除处理。 四、应用容器、进程数据采集UAV的心跳数据除了完成心跳功能之外,还要上报节点的应用容器及进程的监控数据。 将应用容器与进程数据通过Http方式上报是为了保证应用容器监控数据与应用监控数据的隔离,通过不同方式的上送可以保证在MQ服务不能使用时不影响容器与进程数据的采集。 本节将集中说明这些数据的采集细节。 4.1 应用容器数据采集应用容器的数据分为两部分: 其一是容器的基本信息,即节点的ID,主机名,系统信息和JVM信息等; 另一部分是一些简单的实时监控采集数据,包括CPU的负载、内存占用情况和磁盘占用情况等。这些数据在每次上报心跳数据的时候会分别从以下数据源实时采集: 应用启动后的System.getProperty:这部分数据主要包括操作系统的基本信息,JVM信息等。Java提供的工具类:这部分主要包括网卡信息。通过JMX获取的信息:包括CPU占用、内存占用等。系统本身记录的信息:这部分包括可提供的服务信息、启动的Feature信息、节点ID等。通过执行系统命令得到的信息:包括磁盘占用情况。通过直接读取/proc目录下的文件获取的信息:包括CPU占用、内存占用等。4.2 进程数据采集不同于应用容器数据采集,进程的数据并不是在心跳进程中进行采集的,而是由专门的Feature负责。在Feature中将进程数据采集进一步分解成进程端口流量数据采集以及其他数据采集。这两者均由定时任务完成,互相协作,最终由进程探测的定时任务更新心跳客户端的进程数据。 这种使用多个采集任务分别采集的方式可以针对不同的数据进行不同频度的采集。如对于网络端口流量的采集,就可以以更长的周期进行,以减低数据采集带来的性能损耗。同时,不同的任务也可以使用不同的线程执行,提升执行的效率。 进程数据采集流程大致如下图所示: 进程端口流量探测定时任务每隔一定时间读取本地变量端口列表,获取要采集的端口号。 之后对于Windows环境,采用JPcap获取网卡对象,并在网卡上设置tcp过滤器来统计一段时间内的端口流量。对于Linux环境则是直接通过调用Python脚本打开socket,分析流过的数据包获得。 获得全部端口上的流量数据后,任务会将采集数据交给进程数据采集任务,更新其本地变量,同时设置本次采集的时间戳。 进程探测定时任务由一系列子任务构成,在任务开始的时候,会先准备好一个Map结构的数据容器,用于存放采集到的进程信息,每个进程由pid区分,作为Map的key。 任务会先扫描所有的进程,获取pid和进程的端口。扫描到的进程会经过一个过滤器排除不需要采集数据的进程,之后正式采集每个进程上的数据。 对于每一个进程,会通过运行系统命令采集连接数、CPU、内存占用,磁盘读写数据以及网络端口流量数据。其中网络端口流量数据是由端口流量探测任务采集并更新的本地变量,而进程探测任务也会将扫描到的最新的端口列表更新到端口流量探测任务的本地变量。 如果应用是部署在容器上的,则还会有对应的容器信息采集。最后进程探测任务会将采集到的进程数据更新到心跳客户端的本地变量,随着每次心跳数据的生成被一起采集并上报。 进程数据的采集分别来自以下数据源: 系统命令:包括CPU、内存、连接数等(top等命令)/proc目录下各进程子目录:包括CPU、内存等信息、磁盘读写等信息执行脚本:包括Linux环境下的端口流量数据采集第三方工具包:包括win环境下的端口流量数据采集(JPcap)五、HM处理心跳数据和容器数据在通过Http上送到HM端之后,会由HM端对应的服务进行处理。 HM在启动时会启动自己的心跳客户端,负责发送本机的心跳数据和采集HM所在容器的监控数据。同时还会启动一个心跳服务,负责接收处理所有上送的心跳和容器数据信息。 心跳服务在收到心跳数据请求后,会根据HM的配置,判定当前的HM是不是Master节点。如果HM是Master节点,心跳服务会从Http携带的报文中拿出上报的数据,取得上报节点中的可用服务用于更新服务发现信息,之后将数据存入后端的Redis缓存中;如果不是Master节点,则会将数据移交至本机的心跳客户端,由其下次发送心跳时一起上送。 这样的设计是考虑到大规模监控时会有跨机房的情况存在,此时各监控节点往往不在同一个网段内,通过将同一个网段内的机器上交到边界的“网关”统一上交可解决这一问题。此时的HM即充当着“网关”这一角色。 HM在启动的时候同时还会启动一个定时任务,这个任务负责处理各节点的存活状况。任务定时从Redis中读取全部心跳数据,依次检查上送心跳数据中的客户端时间戳与当前系统时间戳的差值。 当时间超过一定的上送时间间隔之后,更改对应的节点存活状态。当超过一倍上送时间间隔,意味节点可能死亡,处于dying状态。当超过两倍时间间隔时,意味着节点已经死亡。当超过三倍时间间隔时,心跳服务会删除该节点的缓存记录。 随心跳一起上报的容器和进程数据会随着心跳数据一同被存入Redis中,后续由HM的其他定时任务读取并发送给预警中心进行处理,最终监控指标被格式化成特定的结构存入OpenTSDB。 同时采集的容器数据和进程数据会提供前端AppHub查看界面,如图所示: 点击页面上的每一个节点,可以查看详细的节点信息,包括节点的操作系统信息、JVM信息、提供的服务和安装的Feture等等。这些也就是前文所说的随心跳数据上报的那部分信息。如图所示: 六、总结心跳是微服务架构基础但重要的机制,通过定时发送心跳数据,MA节点报告了自身的存活状态,使得HM能够知晓当前系统的运行状态。 同时,UAVStack的心跳数据还同时负责上报节点的容器及进程监控数据,随着这些数据的上报,HM可以对监控的容器和进程做出预警,也能够在前端实时看到应用容器和进程的运行状态。 官方网站:https://uavorg.github.io/main/ 开源地址:https://github.com/uavorg 作者:张明明 来源:宜信技术学院

July 12, 2019 · 1 min · jiezi

Serverless-市场观察和落地挑战

KubeCon China 2019 大会上, Serverless 应用服务正式亮相,在 SOFAStack 工作坊吸引了百余名参与者同场体验。市场观察当我们回顾云计算的发展历程,会看到基础架构经历了从物理机到虚拟机,从虚拟机再到容器的演进过程。在这大势之下,应用架构也在同步演进,从单体过渡到多层,再到当下的微服务。在变化的背后,有一股持续的动力,它来自于三个不变的追求:提高资源利用率,优化开发运维体验,以及更好地支持业务发展。 目前, Serverless 已成为云原生社区关注的重点之一,它的发展也不例外。相比容器技术,Serverless 可以将资源管理的粒度更加细化,使开发者更快上手云原生,并且倡导事件驱动模型支持业务发展。从而帮助用户解决了资源管理复杂、低频业务资源占用等问题;实现面向资源使用,以取代面向资源分配的模式。根据 CNCF 在2018年底基于 2400 人的一份统计报告,已有 38% 的组织正在使用 Serverless 技术,相比 2017 同期增长了 22%。(数据来源:CNCF Survey) 图片来源:Gartner Report: China Summary Translation Evolution of Server Computing - VMs to Containers to Serverless - Which to Use When目前市场上,云厂商提供了多种 Serverless 产品和解决方案,大致可划分为: 函数计算服务:如 AWS Lambda,特点是以代码片段为单位运行,并对代码风格有一定要求。面向应用的 Serverless 服务:如 Knative,特点是基于容器服务,并提供了从代码包到镜像的构建能力。容器托管服务:如 AWS Fargate,特点是以容器镜像为单元运行,但用户仍需感知容器。从社区来看,CNCF 云原生基金会正通过 Serverless 工作组协调社区讨论并促进规范和标准的形成,工作组产出了 Serverless 白皮书和全景图等重要内容。其中,全景图将目前的生态划分为了平台层,框架层,工具链层和安全层这四个模块。 图片来源:https://landscape.cncf.io/落地挑战在交流过程中,我们发现 Serverless 很好地解决了客户的一些诉求:包括通过 0-1-0 的伸缩能力来提高资源时用率,降低成本;支持代码包出发,从而让客户无感化实现云原生,历史应用无需经过容器化改造;支持灵活的触发器配置,引导用户对应用进行事件驱动的改造,从而适应业务的高速发展等。这些优势,使得 Serverless 在小程序开发的场景下大放异彩。 同时,对于在企业级应用的生产环境落地 Serverless,各方也有了很多探索和突破。在本周刚结束的 KubeCon China 2019 大会上,Serverless 工作组会议也以此为话题展开了讨论。目前的核心挑战可归纳为: ...

July 11, 2019 · 1 min · jiezi

容器日志采集利器filebeat深度剖析与实践

在云原生时代和容器化浪潮中,容器的日志采集是一个看起来不起眼却又无法忽视的重要议题。对于容器日志采集我们常用的工具有filebeat和fluentd,两者对比各有优劣,相比基于ruby的fluentd,考虑到可定制性,我们一般默认选择golang技术栈的filbeat作为主力的日志采集agent。 相比较传统的日志采集方式,容器化下单节点会运行更多的服务,负载也会有更短的生命周期,而这些更容易对日志采集agent造成压力,虽然filebeat足够轻量级和高性能,但如果不了解filebeat的机制,不合理的配置filebeat,实际的生产环境使用中可能也会给我们带来意想不到的麻烦和难题。 整体架构日志采集的功能看起来不复杂,主要功能无非就是找到配置的日志文件,然后读取并处理,发送至相应的后端如elasticsearch,kafka等。 filebeat官网有张示意图,如下所示:针对每个日志文件,filebeat都会启动一个harvester协程,即一个goroutine,在该goroutine中不停的读取日志文件,直到文件的EOF末尾。一个最简单的表示采集目录的input配置大概如下所示: filebeat.inputs:- type: log # Paths that should be crawled and fetched. Glob based paths. paths: - /var/log/*.log不同的harvester goroutine采集到的日志数据都会发送至一个全局的队列queue中,queue的实现有两种:基于内存和基于磁盘的队列,目前基于磁盘的队列还是处于alpha阶段,filebeat默认启用的是基于内存的缓存队列。 每当队列中的数据缓存到一定的大小或者超过了定时的时间(默认1s),会被注册的client从队列中消费,发送至配置的后端。目前可以设置的client有kafka、elasticsearch、redis等。 虽然这一切看着挺简单,但在实际使用中,我们还是需要考虑更多的问题,例如: 日志文件是如何被filbebeat发现又是如何被采集的?filebeat是如何确保日志采集发送到远程的存储中,不丢失一条数据的?如果filebeat挂掉,下次采集如何确保从上次的状态开始而不会重新采集所有日志?filebeat的内存或者cpu占用过多,该如何分析解决?filebeat如何支持docker和kubernetes,如何配置容器化下的日志采集?想让filebeat采集的日志发送至的后端存储,如果原生不支持,怎样定制化开发?这些均需要对filebeat有更深入的理解,下面让我们跟随filebeat的源码一起探究其中的实现机制。 一条日志是如何被采集的filebeat源码归属于beats项目,而beats项目的设计初衷是为了采集各类的数据,所以beats抽象出了一个libbeat库,基于libbeat我们可以快速的开发实现一个采集的工具,除了filebeat,还有像metricbeat、packetbeat等官方的项目也是在beats工程中。 如果我们大致看一下代码就会发现,libbeat已经实现了内存缓存队列memqueue、几种output日志发送客户端,数据的过滤处理processor等通用功能,而filebeat只需要实现日志文件的读取等和日志相关的逻辑即可。 从代码的实现角度来看,filebeat大概可以分以下几个模块: input: 找到配置的日志文件,启动harvesterharvester: 读取文件,发送至spoolerspooler: 缓存日志数据,直到可以发送至publisherpublisher: 发送日志至后端,同时通知registrarregistrar: 记录日志文件被采集的状态1. 找到日志文件对于日志文件的采集和生命周期管理,filebeat抽象出一个Crawler的结构体,在filebeat启动后,crawler会根据配置创建,然后遍历并运行每个input: for _, inputConfig := range c.inputConfigs { err := c.startInput(pipeline, inputConfig, r.GetStates()) }在每个input运行的逻辑里,首先会根据配置获取匹配的日志文件,需要注意的是,这里的匹配方式并非正则,而是采用linux glob的规则,和正则还是有一些区别。 matches, err := filepath.Glob(path)获取到了所有匹配的日志文件之后,会经过一些复杂的过滤,例如如果配置了exclude_files则会忽略这类文件,同时还会查询文件的状态,如果文件的最近一次修改时间大于ignore_older的配置,也会不去采集该文件。 2. 读取日志文件匹配到最终需要采集的日志文件之后,filebeat会对每个文件启动harvester goroutine,在该goroutine中不停的读取日志,并发送给内存缓存队列memqueue。 在(h *Harvester) Run()方法中,我们可以看到这么一个无限循环,省略了一些逻辑的代码如下所示: for { message, err := h.reader.Next() if err != nil { switch err { case ErrFileTruncate: logp.Info("File was truncated. Begin reading file from offset 0: %s", h.state.Source) h.state.Offset = 0 filesTruncated.Add(1) case ErrRemoved: logp.Info("File was removed: %s. Closing because close_removed is enabled.", h.state.Source) case ErrRenamed: logp.Info("File was renamed: %s. Closing because close_renamed is enabled.", h.state.Source) case ErrClosed: logp.Info("Reader was closed: %s. Closing.", h.state.Source) case io.EOF: logp.Info("End of file reached: %s. Closing because close_eof is enabled.", h.state.Source) case ErrInactive: logp.Info("File is inactive: %s. Closing because close_inactive of %v reached.", h.state.Source, h.config.CloseInactive) default: logp.Err("Read line error: %v; File: %v", err, h.state.Source) } return nil } ... if !h.sendEvent(data, forwarder) { return nil }}可以看到,reader.Next()方法会不停的读取日志,如果没有返回异常,则发送日志数据到缓存队列中。 返回的异常有几种类型,除了读取到EOF外,还会有例如文件一段时间不活跃等情况发生会使harvester goroutine退出,不再采集该文件,并关闭文件句柄。 filebeat为了防止占据过多的采集日志文件的文件句柄,默认的close_inactive参数为5min,如果日志文件5min内没有被修改,上面代码会进入ErrInactive的case,之后该harvester goroutine会被关闭。 这种场景下还需要注意的是,如果某个文件日志采集中被移除了,但是由于此时被filebeat保持着文件句柄,文件占据的磁盘空间会被保留直到harvester goroutine结束。 ...

July 10, 2019 · 3 min · jiezi

蚂蚁金服王旭开源的意义是把社区往前推进一步

互联网技术发展速度之快是所有从业者甚至非从业者都能感受到的。尤记得在世纪之交时,那时候互联网刚刚在中国开始向民用普及,不说支撑大规模的网站访问量的相关技术,就连 Linux、负载均衡甚至都没有被普遍使用。而在二十年之后,云计算已经大行其道,当今的技术人员已经言必称虚拟化、容器和 Serverless,就连刚刚准备入行互联网运维行业的新人们都已经从最初觉得考个 Linux 认证就够了到开始问询 Kubernetes 培训哪家强了。 从一届届 LC3、DockerCon 到 KubeCon,蓬勃发展的云计算与容器化似乎已经称霸了互联网领域。这次我带队参加了 6 月 24 ~ 26 日的 KubeCon 大会,对此感受尤为深刻。事实上,这次在上海举办的 KubeCon 2019 距离上次在同一地点举办的同一会议才仅仅过去半年,但是我们依旧在这次大会上看到了层出不穷的大量新技术、新动态。 云与容器的结合,引爆了这一切。作为容器领域的资深专家,让我们来看看王旭是如何看待容器和云原生领域当前的发展态势的,以及作为这个领域的一家初创技术企业的创始人,他是如何投身到这个领域的,开源又在其间起到了什么作用。 创立于 2015 年的 runV 项目已于 2017 年和另外一个来自英特尔的 Clear 容器项目合并为 Kata 容器项目,并由 OpenStack 基金会(OSF)进行管理,它与来自谷歌的 gVisor 项目并称为目前两大安全容器技术。 开源与初创要么就去加强容器,要不就是引入别的安全技术来让它像容器一样。临近 2015 年,Docker 逐渐被业界主流所接受,互联网技术已经有一个比较明显的发展趋势:第一是云,第二是容器。而云加上容器一定会产生隔离性的需求,这应该说是王旭和他的联合创始人赵鹏做安全容器最早的一个思路:要么就去加强容器,要不就是引入别的安全技术来让它像容器一样。这就是 runV 这个项目想法的起源。 runV 发布的同一个星期,英特尔Clear 容器也发布了。2016 年 8 月份,在西雅图的 LinuxCon 上,王旭和 Clear 团队见面交流,双方在一些细节上面展开合作。在 2017 年 9 月份一个会议上,英特尔软件副总裁、开源技术中心总经理 Imad Sousou 提出项目合并,然后放到基金会里管理。当时大家都觉得这是很好的一个思路。 对于 runV 和 Clear 来说,避免了重复开发以及花费精力在如何说明两者的不同上,同时合并之后可以共同推动发展一个社区,一起去寻找更多的用户。同时,两者合并还有更多的意义。 ...

July 8, 2019 · 1 min · jiezi

JVMSANDBOX从阿里精准测试走出的开源贡献奖

阿里妹导读:稳定性是历年双11的技术质量保障核心。从 2016 年开始淘宝技术质量部潜心修行,创新地研发了一套实时无侵入的字节码增强框架,于是「JVM-SANDBOX」诞生了,并且顺手在 MTSC 大会上拿了开源贡献奖,今天,我们来瞅瞅这个拿奖的项目。 在近日举行的中国移动互联网测试开发大会(简称MTSC大会),来自淘系技术质量开源项目「JVM-SANDBOX」以及淘系同学参与维护的「 ATX」 包揽了 MTSC 2019 年度开源贡献奖,表彰过去一年在测试领域开源项目中的突出贡献。其中,「JVM-SANDBOX」致力于为服务端稳定性领域提供实时无侵入的字节码增强框架。 一、JVM-Sandbox的诞生功能回归、业务/系统监控、问题排查定位、强弱依赖、故障演练等是阿里 10 年双十一沉淀积累下来的稳定性专项,也是历年双十一质量保障的核心要素。要有效、轻量级地实现这些稳定性专项,都会触及到一块底层技术—— java 字节码增强。如果每个专项都能自己实现一套字节码增强逻辑,实现的门槛高、投入和维护成本高,且不同专项间相互影响造成不可预知的风险。如何屏蔽字节码增强技术的高门槛,降低成本,同时又能支持上层多个专项功能的快速实现和动态管理,成为淘宝技术质量部的目标。从 2016 年开始我们潜心修行,创新地研发了一套实时无侵入的字节码增强框架,于是 「JVM-SANDBOX」 诞生了。 对上面提到的专项进行抽象分析: 故障演练:在运行前,抛出异常或增加运行时间,即:干预方法的执行顺序和改变返回值;强弱依赖梳理:系统运行时,实时记录系统的对外调用情况,即:感知方法的入参和返回值;录制回放:运行时,记录方法的入参和返回值,回放时,不真实对外调用,而是直接返回录制时的返回值。即:感知方法入参和返回值,干预方法执行顺序,改变返回值;精准回归:获取每个请求的行调用链路,根据行调用链路进行场景去重,根据代码改动的情况和行调用链路确定需要回归范围,即:运行时行链路感知。不难发现,要解决这些问题本质就是如何完成 java 方法的环绕管控和运行时行链路的获取,即 AOP 框架的解决方案。目前常用 AOP 框架的解决方案有两种:proxy 和埋点。proxy 的优点在于已实现了统一的 API,减少了重复投入,但是不能实时生效,需要系统编译重启。埋点的优点在于动态生效灵活度高,但是没有统一 API。 要快速解决上边的三个问题,我们需要的 AOP 解决方案必须具备两个特性: 动态可插拔,即实现埋点方式的统一的 API;无侵入性,即解决 JVM 类隔离的问题。基于以上需求,我们研发了 JVM-Sandbox。 二、实现方式JVM-Sandbox 由纯 Java 编码完成,基于 JVMTI 技术规范,为观察和改变代码运行结果提供了即插即用模块接口的容器,提供两个核心功能:实时无侵入 AOP 框架和动态可插拔的模块管理容器。 2.1 JVM-Sandbox的核心功能 使用埋点技术提供统一的 API,来实现无需重启的 AOP 解决方案;使用容器完成 JVM 类隔离,来解决侵入性问题;提供容器管理机制,来完成各种容器的管理。2.2 JVM—Sandbox的核心事件模型 BEFORE、RETURN 和 THROWS 三个环节事件的正常流转和干预流转。 2.3 整体架构 ...

July 5, 2019 · 1 min · jiezi

云原生应用-Kubernetes-监控与弹性实践

前言   云原生应用的设计理念已经被越来越多的开发者接受与认可,而Kubernetes做为云原生的标准接口实现,已经成为了整个stack的中心,云服务的能力可以通过Cloud Provider、CRD Controller、Operator等等的方式从Kubernetes的标准接口向业务层透出。开发者可以基于Kubernetes来构建自己的云原生应用与平台,Kubernetes成为了构建平台的平台。今天我们会向大家介绍一个云原生应用该如何在Kubernetes中无缝集成监控和弹性能力。 本文整理自由阿里云容器平台技术专家 刘中巍(莫源)在 KubeCon 分享的《Cloud Native Application monitoring and autoscaling in kubernetes》演讲。获取 KubeCon 全部阿里演讲PPT,关注阿里巴巴云原生公众号,微信菜单栏点击 PPT下的“获取PPT” 阿里云容器服务Kubernetes的监控总览 云服务集成  阿里云容器服务Kubernetes目前已经和四款监控云服务进行了打通,分别是SLS(日志服务)、ARMS(应用性能监控)、AHAS(架构感知监控服务)、Cloud Monitor(云监控)。 SLS主要负责日志的采集、分析。在阿里云容器服务Kubernetes中,SLS可以采集三种不同类型的日志 APIServer等核心组件的日志Service Mesh/Ingress等接入层的日志应用的标准日志  除了采集日志的标准链路外,SLS还提供了上层的日志分析能力,默认提供了基于APIServer的审计分析能力、接入层的可观测性展现、应用层的日志分析。在阿里云容器服务Kubernetes中,日志组件已经默认安装,开发者只需要通过在集群创建时勾选即可。 ARMS主要负责采集、分析、展现应用的性能指标。目前主要支持Java与PHP两种语言的集成,可以采集虚拟机(JVM)层的指标,例如GC的次数、应用的慢SQL、调用栈等等。对于后期性能调优可以起到非常重要的作用。 AHAS是架构感知监控,通常在Kubernetes集群中负载的类型大部分为微服务,微服务的调用拓扑也会比较复杂,因此当集群的网络链路出现问题时,如何快速定位问题、发现问题、诊断问题则成为了最大的难题。AHAS通过网络的流量和走向,将集群的拓扑进行展现,提供更高层次的问题诊断方式。 开源方案集成开源方案的兼容和集成也是阿里云容器服务Kubernetes监控能力的一部分。主要包含如下两个部分: Kubernetes内置监控组件的增强与集成  在kubernetes社区中,heapster/metrics-server是内置的监控方案,而且例如Dashboard、HPA等核心组件会依赖于这些内置监控能力提供的metrics。由于Kubernetes生态中组件的发布周期和Kubernetes的release不一定保证完整的同步,这就造成了部分监控能力的消费者在Kubernetes中存在监控问题。因此阿里云就这个问题做了metrics-server的增强,实现版本的兼容。此外针对节点的诊断能力,阿里云容器服务增强了NPD的覆盖场景,支持了FD文件句柄的监测、NTP时间同步的校验、出入网能力的校验等等,并开源了eventer,支持离线Kubernetes的事件数据到SLS、kafka以及钉钉,实现ChatOps。 Prometheus生态的增强与集成   Promethes作为Kubernetes生态中三方监控的标准,阿里云容器服务也提供了集成的Chart供开发者一键集成。此外,我们还在如下三个层次作了增强: 存储、性能增强:支持了产品级的存储能力支持(TSDB、InfluxDB),提供更持久、更高效的监控存储与查询。采集指标的增强:修复了部分由于Prometheus自身设计缺欠造成的监控不准的问题,提供了GPU单卡、多卡、共享分片的exporter。提供上层可观测性的增强:支持场景化的CRD监控指标集成,例如argo、spark、tensorflow等云原生的监控能力,支持多租可观测性。阿里云容器服务Kubernetes的弹性总览   阿里云容器服务Kubernetes主要包含如下两大类弹性组件:调度层弹性组件与资源层弹性组件。 调度层弹性组件  调度层弹性组件是指所有的弹性动作都是和Pod相关的,并不关心具体的资源情况。 HPAHPA是Pod水平伸缩的组件,除了社区支持的Resource Metrics和Custom Metrics,阿里云容器服务Kubernetes还提供了external-metrics-adapter,支持云服务的指标作为弹性伸缩的判断条件。目前已经支持例如:Ingress的QPS、RT,ARMS中应用的GC次数、慢SQL次数等等多个产品不同维度的监控指标。 VPA VPA是Pod的纵向伸缩的组件,主要面向有状态服务的扩容和升级场景。    cronHPA cronHPA是定时伸缩组件,主要面向的是周期性负载,通过资源画像可以预测有规律的负载周期,并通过周期性伸缩,实现资源成本的节约。 ResizerResizer是集群核心组件的伸缩控制器,可以根据集群的CPU核数、节点的个数,实现线性和梯度两种不同的伸缩,目前主要面对的场景是核心组件的伸缩,例如:CoreDNS。 资源层弹性组件资源层弹性组件是指弹性的操作都是针对于Pod和具体资源关系的。 Cluster-Autoscaler Cluster-Autoscaler是目前比较成熟的节点伸缩组件,主要面向的场景是当Pod资源不足时,进行节点的伸缩,并将无法调度的Pod调度到新弹出的节点上。 virtual-kubelet-autoscaler virtual-kubelet-autoscaler是阿里云容器服务Kubernetes开源的组件,和Cluster-Autoscaler的原理类似,当Pod由于资源问题无法调度时,此时弹出的不是节点,而是将Pod绑定到虚拟节点上,并通过ECI的方式将Pod进行启动。 Demo Show Case  最后给大家进行一个简单的Demo演示:应用主体是apiservice,apiservice会通sub-apiservice调用database,接入层通过ingress进行管理。我们通过PTS模拟上层产生的流量,并通过SLS采集接入层的日志,ARMS采集应用的性能指标,并通过alibaba-cloud-metrics-adapster暴露external metrics触发HPA重新计算工作负载的副本,当伸缩的Pod占满集群资源时,触发virtual-kubelet-autoscaler生成ECI承载超过集群容量规划的负载。 总结  在阿里云容器服务Kubernetes上使用监控和弹性的能力是非常简单的,开发者只需一键安装相应的组件Chart即可完成接入,通过多维度的监控、弹性能力,可以让云原生应用在最低的成本下获得更高的稳定性和鲁棒性。 本文作者:jessie筱姜阅读原文 本文为云栖社区原创内容,未经允许不得转载。

July 4, 2019 · 1 min · jiezi

深入浅出聊一聊Docker

网易云信IM私有化部分用到Docker技术,今天我们就深入浅出来聊聊Docker。 Docker是什么?Docker是一个工具,能把应用打包部署于container里,这里可以把container看做是一个简易版的 Linux 环境和运行在其中的应用程序,每个container运行一个application。它诞生于 2013 年初,最初是 dotCloud公司内部的一个业余项目,创始人是Solomon Hykes。Docker自开源后受到广泛的关注和讨论,Redhat已经在其 RHEL6.5 中明确支持Docker;Google也在其PaaS产品中广泛应用。Docker项目的目标是实现轻量级的操作系统虚拟化解决方案。 现在Docker已经从一个工具转化成平台,小生态圈。 Docker的优势有哪些? 以前企业部署软件会购买真正的服务器,这种模式的资源利用率很低。后来出现了云端的虚拟服务器,比如AWS,提高了一定的资源利用率,但是不同阶段的应用环境可能不同。 Docker对这些有很大的优化,比如: 1. Docker 容器可以实现秒级启动 2. 容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,系统的开销很小。传统虚拟机方式运行10个不同的应用就要起10个虚拟机,而Docker只需要启动10个隔离的应用即可。 下图是传统虚拟化方式和Docker的不同。Docker本质上是一种虚拟化的技术,不是虚拟机。Docker是在操作系统层面上实现虚拟化,直接复用本地主机的操作系统,而传统方式则是在硬件层面实现。在传统模式下,Guest OS会占用大量空间,而且不同的应用会需要不同的虚拟机。在Docker中只有一个OS,各种application运行在一个OS上。 *Hypervisor是一种虚拟化的技术具体来说,Docker的优势包括: Faster developer onboardingNo vendor lockinEliminate environment inconsistenciesShip applications fasterScale quicklyEasily remediate issuesPlay with Docker container下载安装完Docker后,可以尝试使用以下命令来运行一个聊天软件。docker run -d -p 3000:3000 unclebarney/chit-chat这个命令的含义是启动Docker容器。-d表示在后台启动。-p表示做端口的映射,把容器里的3000端口映射到宿主机上的3000。使用的镜像为unclebarney/chit-chat。 这个命令有两部分操作:1.从Dockerhub(所有镜像存储的地方)下载此镜像,大概5到30秒(取决于带宽)2.根据镜像启动container,并运行node server Docker imageDocker image(镜像)是container的基础。所有container都是从image构建的。 Docker 运行容器前需要本地存在对应的镜像,如果镜像不存在本地,Docker 会从镜像仓库下载(默认是 Docker Hub 公共注册服务器中的仓库)。Docker每个运行的实例由最上层的container和下面的多层镜像构成。Docker使用Union FS(union filesystem)将这些不同镜像整合在一起。通常Union FS有两个用途, 一方面可以实现不借助LVM、RAID将多个disk挂到同一个目录下;另一个更常用的就是将一个只读的分支和一个可写的分支联合在一起,Live CD正是基于此方法可以允许在镜像不变的基础上允许用户在其上进行一些写操作。 镜像的每一层都有以下这些信息:1.这一层的meta data,以JSON的形式存储2.image filessystem changeset3.image ID,比如:74fe38d11401镜像有两种构建方式:1.启动一个最基础的容器,在里面运行一些命令,像git一样把这些命令commit,形成自己的镜像。2.引用一个base image,再加上一些需要的指令。这些指令存在一个文件中,叫Dockerfile。以下是Dockerfile的例子,是刚才提到的聊天软件的镜像的生成方式。# 引用mhart/alpine-node这个镜像 # Dockerfile中第一个命令必须是FROM命令 FROM mhart/alpine-node:base # 将Dockerfile所在文件夹中的内容添加到Docker镜像中 # 第一个点指的是Dockerfile所在的目录 # 第二个点指的是Docker镜像中的当前目录 ADD . . # 为这个镜像暴露3000端口 EXPOSE 3000 # 运行node命令。值得注意的是在构建镜像的时候这个命令不会执行 # 而是在真正基于这个镜像启动了容器时才会执行这个命令 CMD [“node”, “index.js”] ...

July 2, 2019 · 1 min · jiezi

OpenKruise-云原生应用自动化引擎正式开源

在本次 KubeCon 上,阿里云将为全球用户分享阿里巴巴超大规模云原生落地实践、云原生前沿技术与应用包括OpenKruise 开源项目、开放云原生应用中心(Cloud Native App Hub),同时将重磅发布边缘容器、云原生应用管理与交付体系等产品和服务。 OpenKruise Github 地址: https://github.com/openkruise/kruise “云原生应用自动化引擎”加持下的阿里“云原生”随着云原生概念的兴起,越来越多的应用开始尝试在云原生的土壤上耕耘。那么什么是云原生,简而言之,云原生就是一套能够充分利用“云”的能力,高效构建与交付应用的方法论集合,使得应用容器化的用户可以充分的利用云的弹性、“不可变基础设施”等优势专注于自身核心业务价值。 当前,阿里巴巴基础设施的云原生演进与升级也正在如火如荼的进行。而在阿里巴巴上云的过程中,阿里内部在超大规模的互联网场景中,已经开始进行大量的云原生的理念落地实践,比如轻量级容器化,阿里巴巴经济体正在大规模推进应用的轻量级容器化,从而达成利用容器的敏捷、一致等特性快速构建符合云原生理念的电商站点交付的能力,适应类似“双十一”大促的严苛技术需求;再比如说云原生应用管理, 阿里巴巴经济体正在将 Kubernetes 等项目的应用编排与自动化能力,穿透到上层运维框架当中,驱动电商应用按照云原生的技术理念进行编排、交付和运行。 在阿里巴巴经济体的整体云原生化过程当中,阿里的技术团队逐渐沉淀出了一套紧贴上游社区标准、适应互联网规模化场景的技术理念与最佳实践。这其中,最重要的无疑是如何对应用进行自动化的发布、运行和管理。 OpenKruise:来自阿里经济体云原生化历程的宝贵经验与最佳实践在 KubeCon 上海,阿里云容器平台团队正式宣布了重量级项目 - OpenKruise(以下简称Kruise)的开源。 Kruise 是 cruise的谐音,'k' for Kubernetes. 字面意义巡航,豪华游艇。寓意Kubernetes上应用的自动巡航,满载阿里巴巴多年应用部署管理经验。 Kruise 的目标是automate everything on Kubernetes ! Kruise 项目源自于阿里巴巴经济体应用过去多年的大规模应用部署、发布与管理的最佳实践,源于容器平台团队对集团应用规模化运维,规模化建站的能力,源于阿里云Kubernetes服务数千客户的需求沉淀。Kruise 借力于云原生社区,集成阿里巴巴云原生实践之精华,反哺社区,指引业界云原生化最佳实践,少走弯路。 Kruise 核心在于自动化,我们将从不同维度解决 Kubernetes之上应用的自动化,包括,部署,升级,弹性扩缩容,Qos调节,健康检查,迁移修复等等。此次Kruise开源的内容主要在应用部署,升级方面,即一套增强版controller组件用于应用的部署和级和运维。后续,Kruise会依次开源智能化的弹性扩缩容组件,以及应用Qos自调节能力的组件等。 Kruise Controllers:将 Kubernetes 的“控制器模式”进行到底以下内容主要介绍 Kruise Controllers - 一套用于 Kubernetes 之上应用自动化部署管理的 controller 组件。众所周知,Kubernetes 项目的核心原理,就是“控制器模式”。目前,Kubernetes 项目默认已经提供了一套 Controller 组件,例如 Deployment, Statefulset, DaemonSet 等,这些 Controller 提供了比较丰富的应用部署和管理功能。但是,随着 Kubernetes 的使用范围越来越广,真实的企业与规模性场景中的业务诉求与上游 Controller 功能不匹配的情况也越来越常见。以阿里巴巴为例:阿里巴巴内部的 Kubernetes 集群需要服务涵盖50几个 BU,上万种应用。这个体量非常庞大,对规模性和高可用性带来了巨大的挑战。与此同时,阿里云上的 Kubernetes 服务也接入了上千家企业客户,收集并支撑了各种各样的客户需求。这些诉求与最后阿里经济体的实践经验,最终促成了 Kruise 开源项目的诞生。 ...

July 1, 2019 · 1 min · jiezi

干货看云原生时代阿里云的四个最

云原生已经成为 IT 领域最热的词之一。到底有多火,大家感受一下:2015 年在旧金山召开的首届 KubeCon 只有 200 余参会者,而今年第二次在中国举办的KubeCon迎来了3000+现场观众,遍布全球的线上关注开者则更是不计其数。Gartner最近发布报告表示云原生时代已来,在未来三年中将有75%的全球化企业将在生产中使用容器化的应用。 作为云原生技术与应用的领先企业,阿里云在今年的 KubeCon + CloudNativeCon 大会上为全球企业和开发者分享了26场行业趋势和技术演讲。 阿里云智能容器平台负责人丁宇指出: 云原生正在重塑整个软件生命周期,容器、Kuberentes、云原生成为云时代的三个重要标准。阿里云将继续加大云原生技术栈产品体系的研发,并持续回馈开源社区,与生态合作伙伴一起,共同推动云原生标准制定以及应用的落地。 9年技术沉淀,阿里云云原生的四个“最”阿里巴巴是国内最早布局云原生技术的公司之一,丁宇在 26 日的主题演讲中表示:“早在 2011年,阿里巴巴就率先开始了容器化进程,开启了中国公司将云原生技术体系在电商、金融、制造等领域中大规模应用的先河。” 历经9年技术沉淀,阿里云成为国内唯一进入 Gartner《公有云容器服务竞争格局》报告的企业。今年 3 月,阿里云智能总裁张建锋表示,未来 1-2 年内,阿里巴巴要实现 100% 的业务跑在公共云之上,并且继续大力投入云原生技术的研发。 最丰富的云原生产品家族经过 9 年的内部技术实践,阿里云已拥有国内最丰富的云原生产品家族,覆盖八大类别 20 余款产品,涵盖底层基础设施、数据智能、分布式应用等,可以满足不同行业场景的需求。 目前阿里巴巴集团内部电商、城市大脑等核心业务已经大规模使用云原生技术,去年双 11,阿里云完成了 10 分钟 1000 台以上服务器的快速部署,容器部署规模达到百万级,两年内实现全部上云,是全球最大规模的云原生应用实践。 最全面的云原生开源贡献 阿里云一直致力于回馈社区、积极拥抱开源,是国内在云原生领域的开源贡献最全面的科技公司,涵盖编排调度、作业管理、无服务器框架等: ●主导维护 etcd、containerd、dragonfly 等多个 CNCF 明星项目的发展,已有超过 10 个项目进入 CNCFlandscape; ●项目建设层面:积极建设 Kubernetes 项目,贡献量位居全球前 10; ●开源生态支持:加入 CNCF、OCI、CDF 等基金会,成为多个基金会的顶级会员,共建开源生态。 今年 1 月,阿里云资深技术专家李响成为首个入选全球顶级开源社区 CNCF 技术监督委员会的中国工程师,致力于推动云原生技术的落地。 最大的容器集群和客户群体除了支持集团内部应用规模化运维,阿里云云原生技术还向全社会输出。阿里云 ACK(容器服务)遍布全球 18 个 region,拥有国内最大公共云容器集群以及客户群体。 ...

June 28, 2019 · 1 min · jiezi

坚持探索与落地并重阿里巴巴云原生之路全景揭秘

阿里妹导读:阿里云已经成功地规模化落地云原生,26日的 KubeCon 大会上,CNCF TOC 和阿里云资深技术专家李响发表主题演讲,分享了阿里巴巴在规模扩展、可靠性、开发效率、迁移策略等方面的经验,并探讨云原生的落地及应对若干技术挑战。为什么要做云原生?云原生究竟能带来什么价值?从最初的独自摸索到如今拥抱开源回馈社区,阿里巴巴走过了怎样的云原生旅程?又有哪些技术心得?今天,将全部分享出来。 多年沉淀,坚持探索与落地并重阿里巴巴从2011年开始通过容器实践云原生技术体系,在整个业界都还没有任何范例可供参考的大背境下,逐渐摸索出了一套比肩全球一线技术公司并且服务于整个阿里集团的容器化基础设施架构。这个探索历程虽然孤独,但却被始终如一的坚持至今。这正是在这个孤注一掷的技术探索与奋进的过程中,阿里巴巴的技术团队完整的经历了云原生技术浪潮里的所有关键节点,不仅成为了这次技术革命的重要见证者,也逐渐成为中国云原生技术体系当之无愧的推动者与引领者之一。 阿里的体量大、业务复杂,推动云原生要找到合适的切入点。在双十一成本压力的推动下,资源成本与效率优化成了阿里云原生的起点。 阿里从容器入手,研究低成本虚拟化与调度技术:提供灵活、标准的部署单元;将静态资源分配更换为动态按需调度,进一步提升部署效率,解决资源碎片化问题,提高部署密度;通过存储网络虚拟化和存储计算分离等技术,增强任务的可迁移性,进一步提高了资源的可靠性,降低了资源成本。 在资源成本的推动下,阿里完成了全面容器化,资源分配也被高效调度平台接管。阿里的云原生并未止步于此。提高研发效率与加快迭代周期是推动阿里业务增强的秘密武器。阿里希望通过云原生让开发者效率更高。 为了降低应用部署难度,提高部署自动化程度,阿里开始采用 Kubernetes 作为容器编排平台,并且持续推动 Kubernetes 的性能与可扩展性。具体 Kubernetes,阿里持续对研发、部署流程进行改进。为了构建更云原生化的 CI/CD,进一步做到标准化和自动化,从研发到上线流程,阿里引入了诸如 Helm 的应用标准化管理,也尝试了 GitOps 这样的部署流程,还推动了 PaaS 层的面向终态自动化改造。于此同时,阿里也开始探索服务网格,致力于进一步提高服务治理的普适性与标准性,降低开发者采用门槛,进一步推动微服务在多语言和多环境下的普及。 今年,阿里也展开了全站上云。经过云原生的探索与改造,阿里基础架构体系是现代化和标准化的。利用容器技术,应用与宿主机运行时完成了解耦;利用 Kubernetes 对 Pod 与 Volume 等的抽象,完成了对多种资源实现的统一化;通过智能调度与 PaaS 平台,让自动迁移应用,修复不稳定因素成为了可能,阿里通过云原生技术大大降低了上云的难度。 在这个提高资源和人员效率的过程中,阿里巴巴的整个基础设施也变得更加开放,连通开源生态,在交流互动中不断吸收和贡献好的理念、技术、思想。如今,阿里云不仅支撑着中国最大的云原生应用双11,而且拥有国内最大的公共云集群和镜像仓库。作为唯一入选 Gartner 的公有云容器服务竞争格局的厂商,阿里云也积累了最为丰富和宝贵的客户实践。 追求极致,优化扩展性和规模性弹性和规模性,这是支撑阿里巴巴各种类型的复杂场景以及流量高峰的关键因素。 经过不断打磨,阿里巴巴在 Kubernetes 规模与性能上取得了显著的成果:将存储object 的数量提升25倍,支持的节点数从5000提升到上万,在端到端调度延迟从5s变为100ms等等。其中有不少工作在阿里巴巴和社区中共同开展,而这些研发成果都已经贡献给社区,我们期望其他企业及开发者也可以享受阿里巴巴规模所带来的技术红利。 阿里巴巴持续优化性能,可以分为以下四个维度:工作负载追踪、性能分析、定制化调度、大规模镜像分发。首先对工作负载调度有完整的追踪、重放机制,其次将所有性能问题的进行细致分析,逐一攻克技术瓶颈。Kubernetes 本身的可定制性很强,阿里巴巴针对自身业务场景沉淀了定制化的调度能力和镜像分发系统。开源Dragonfly 项目脱胎于双十一,具备极强的镜像分发能力。数十个超级集群,每个超级集群具有数万节点,数百万的容器。 阿里巴巴落地 Kubernetes 可以分为三个阶段:首先通过 Kubernetes 提供资源供给,但是不过多干扰运维流程,这系统容器是富容器,将镜像标准化与轻量级虚拟化能力带给了上面的 PaaS 平台。第二步,通过 Kubernetes controller 的形式改造PaaS 平台的运维流程,给 PaaS 带来更强的面向终态的自动化能力。最后把运行环境等传统重模式改成原生容器与 pod 的轻量模式,同时将 PaaS 能力完全移交给Kubernetes controller,从而形成一个完全云原生的架构体系。 如何解决云原生的关键难点阿里巴巴云原生的探索,起步于自研容器和调度系统,到如今拥抱开源的标准化技术。对于当下开发者的建议是:如果想构建云原生架构,建议直接从 Kubernetes 入手即可。一方面,Kubernetes 为平台建设者而生,已经成为云原生生态的中流砥柱,它不仅向下屏蔽了底层细节,而且向上支撑各种周边业务生态;另一方面,更重要的是社区中有着越来越多围绕 Kubernetes 构建的开源项目,比如Service Mesh、Kubeflow。 ...

June 28, 2019 · 1 min · jiezi

GMTC2019闲鱼基于Flutter的架构演进与创新

2012年应届毕业加入阿里巴巴,主导了闲鱼基于Flutter的新混合架构,同时推进了Flutter在闲鱼各业务线的落地。未来将持续关注终端技术的演变及趋势 Flutter的优势与挑战 Flutter是Google开源的跨端便携UI工具包,除了具有非常优秀的跨端渲染一致性,还具备非常高效的研发体验,丰富的开箱即用的UI组件,以及跟Native媲美的性能体验。由于它的众多优势,也使得Flutter成为了近些年来热门的新技术。 通过以上的特点可以看出,Flutter可以极大的加速客户端的研发效率,与此同时得到优秀的性能体验,基于我的思考,Flutter会为以下团队带来较大的收益: 中小型的客户端团队非常适合Flutter开发,不仅一端编写双端产出,还有效的解决了小团队需要双端人员(iOS:Android)占比接近1:1的限制,在项目快速推进过程中,能让整个团队的产能最大化。App在Android市场占比远高于iOS的团队,比如出海东南亚的一些App,Android市场整体占比在90%以上,通过Flutter可以将更多的人力Focus在Android市场上,同时通过在iOS端较小的投入,在结果上达到买一送一的效果。以量产App为主要策略的团队,不论是量产ToB的企业App,还是有针对性的产出不同领域的ToC的App的公司,都可以通过一端开发多端产出的Flutter得到巨大的产能提升。 闲鱼在以上的场景中属于第一种场景,服务3亿用户的闲鱼App的背后,开发资源投入很少,与竞对相比,我们是一只再小不过的团队,在这种场景下,Flutter为闲鱼业务的稳定发展以及提供更多的创新产品给予了很大的帮助。 但与此同时,Flutter在设计上带来的优势同时又会带来新的问题。所有的新技术都是脱胎于老技术的,Flutter也不例外,其身上带有很多Chrome的影子。我们再做一层简化,如果我们认为Flutter是一个使用Dart语言的浏览器容器,请大家思考一下两个问题如何解决。 如果在一个已经存在的App中加入Flutter,如何让Native与Flutter进行无缝的衔接,同时保证相互开发之间的隔离性如果在Flutter的容器中,使用已有的Native UI组件,在Flutter与Native渲染机制不同的情况下,怎么保证两者的无缝衔接以及高性能。闲鱼的架构演进与创新带着上面两个问题,我们来到闲鱼场景下的具体Case以及解决方案的演进过程。 已有App+Flutter容器 在这种情况下,闲鱼需要考虑的是首先要考虑引入Flutter容器后的内存压力,保证不要产生更多的内存溢出。与此同时我们希望能让Flutter和Native之间的页面切换是顺畅的,对不同技术栈之间的同学透明。因此我们有针对性的进行了多次迭代。 在没有任何改造的情况下以iOS为例,你可以通过创建新的FlutterViewController来创建一个新的Flutter容器,这个方案下,当创建多个FlutterViewController时会同时在内存中创建多个Flutter Engine的Runtime(虽然底层Dart VM依然只有一个),这对内存消耗是相当大的,同时多个Flutter Engine的Runtime会造成每个Runtime内的数据无法直接共享,造成数据同步困难。 这种情况下,闲鱼选择了全局共享同一个FlutterViewController的方式保证了内存占用的最小化,同时通过基础框架Flutter Boost提供了Native栈与Flutter栈的通信与管理,保证了当Native打开或关闭一个新的Flutter页面时,Dart侧的Navigator也做到自动的打开或关闭一个新的Widget。目前Google官方的提供的方案上就是参考闲鱼早先的这个版本进行的实现的。 然而在这种情况下,如果出现如闲鱼图中所示多个Tab的场景下,整个堆栈逻辑就会产生混乱,因此闲鱼在这个基础上对Flutter Boost的方案进行了升级并开源,通过在Dart侧提供一个BoostContainerManager的方式,提供了对多个Navigator的管理能力,如果打比方来看这件事,就相当于,针对Flutter的容器提供了一个类似WebView的OpenWindow的能力,每做一次OpenWindow的调用,就会产生一个新的Navigator,这样开发者就可以自由的选择是在Navigator里进行Push和Pop,还是直接通过Flutter Boost新开一个Navigator进行独立管理。 Flutter Boost目前已在github开源,由于闲鱼目前线上版本只支持Flutter 1.2的版本,因此需要支持1.5的同学等稍等,我们会在近期更新支持1.5的Flutter Boost版本。 Flutter页面+Native UI 由于闲鱼是一个闲置交易社区,因此图片和视频相对较多,对图片视频的线上性能以及内存占用有较严格的要求。目前Flutter已提供的几种方案中(Platform View以及Flutter Plugin),不论是对内存的占用还是整个的线上流畅度上还存在一定的问题,这就造成了当大部分同学跟闲鱼一样实现一个复杂的图文Feed推荐场景的时候,非常容易产生内存溢出。而实际上,闲鱼在以上的场景下有针对性的做出了较大的优化。 在整个的Native UI到Flutter渲染引擎桥接的过程中,我们选用了Flutter Plugin中提供的FlutterTextureRegistry的能力,在去年上半年我们优先针对视频的场景进行了优化,优化的思路主要是针对Flutter Engine底层的外接纹理接口进行修改,将原有接口中必须传入一个PixelBuffer的内存对象这一限制做了扩展,增加一个新的接口保证其可以传入一个GPU对象的TextureID。 如图中所示,优化后的整个链路Flutter Engine可以直接通过Native端已经生成好的TextureID进行Flutter侧的渲染,这样就将链路从Native侧生成的TextureID->copy的内存对象PixelBuffer->生成新的TextureID->渲染,转变为Native侧生成的TextureID->渲染。整个链路极大的缩短,保证了整个的渲染效率以及更小的内存消耗。闲鱼在将这套方案上线后,又尝试将该方案应用于图片渲染的场景下,使得图片的缓存,CDN优化,图片裁切等方案与Native归一,在享受已有集团中间件的性能优化的同时,也得到了更小的内存消耗,方案落地后,内存溢出大幅减少。 目前该方案由于需要配合Flutter Engine的修改,因此暂时无法提供完整的方案至开源社区,我们正在跟google积极沟通整个修改方案,相信在这一两个月内会将试验性的Engine Patch开源至社区,供有兴趣的同学参考。 复杂业务场景的架构创新实践 将以上两个问题解决以后,闲鱼开始了Flutter在业务侧的全面落地,然而很快又遇到新的问题,在多人协作过程中: 如何提供一些标准供大家进行参考保证代码的一致性如何将复杂业务进行有效的拆解变成子问题如何保证更多的同学可以快速上手并写出性能和稳定性都不错的代码 在方案的前期,我们使用了社区的Flutter Redux方案,由于最先落地的详情,发布等页面较为复杂,因此我们有针对性的对View进行了组件化的拆分,但由于业务的复杂性,很快这套方案就出现了问题,对于单个页面来说,State的属性以及Reducer的数量都非常多,当产生新需求堆叠的时候,修改困难,容易产生线上问题。 针对以上的情况,我们进行了整个方案的第二个迭代,在原有Page的基础上提供了Component的概念,使得每个Component具备完整的Redux元素,保证了UI,逻辑,数据的完整隔离,每个Component单元下代码相对较少,易于维护和开发,但随之而来的问题是,当页面需要产生数据同步时,整个的复杂性飙升,在Page的维度上失去了统一状态管理的优势。 在这种情况下闲鱼换个角度看端侧的架构设计,我们参考React Redux框架中的Connect的思想,移除掉在Component的Store,随之而来的是新的Connector作为Page和Component的数据联通的桥梁,我们基于此实现了Page State到Component State的转换,以及Component State变化后对Page State的自动同步,从而保证了将复杂业务有效的拆解成子问题,同时享受到统一状态管理的优势。与此同时基于新的框架,在统一了大家的开发标准的情况下,新框架也在底层有针对性的提供了对长列表,多列表拼接等case下的一些性能优化,保证了每一位同学在按照标准开发后,可以得到相对目前市面上其他的Flutter业务框架相比更好的性能。 目前这套方案Fish Redux已经在github开源,目前支持1.5版本,感兴趣的同学可以去github进行了解。 研发智能化在闲鱼的应用闲鱼在去年经历了业务的快速成长,在这个阶段上,我们同时进行了大量的Flutter的技术改造和升级,在尝试新技术的同时,如何能保证线上的稳定,线下的有更多的时间进行新技术的尝试和落地,我们需要一些新的思路和工作方式上的改变。 以我们日常工作为例,Flutter的研发同学,在每次开发完成后,需要在本地进行Flutter产物的编译并上传到远端Repo,以便对Native同学透明,保证日常的研发不受Flutter改造的干扰。在这个过程中,Flutter侧的业务开发同学面临着很多打包上传更新同步等繁琐的工作,一不小心就会出错,后续的排查等让Flutter前期的开发变成了开发5分钟,打包测试2小时。同时Flutter到底有没有解决研发效率快的问题,以及同学们在落地过程中有没有Follow业务架构的标准,这一切都是未知的。 在痛定思痛以后,我们认为数据化+自动化是解决这些问题的一个较好的思路。因此我们首先从源头对代码进行管控,通过commit,将代码与后台的需求以及bug一一关联,对于不符合要求的commit信息,不允许进行代码合并,从而保证了后续数据报表分析的数据源头是健康的。 在完成代码和任务关联后,通过webhook就可以比较轻松的完成后续的工作,将每次的commit有效的关联到我们的持续集成平台的任务上来,通过闲鱼CI工作平台将日常打包自动化测试等流程变为自动化的行为,从而极大的减少了日常的工作。粗略统计下来,在去年自动化体系落地的过程中单就自动打Flutter包上传以及触发最终的App打包这一流程就让每位同学每天节省一个小时以上的工作量,效果非常明显。另外,基于代码关联需求的这套体系,可以相对容易的构建后续的数据报表对整个过程和结果进行细化的分析,用数据驱动过程改进,保证新技术的落地过程的收益有理有据。 总结与展望回顾一下上下文 Flutter的特性非常适合中小型客户端团队/Android市场占比较高的团队/量产App的团队。同时由于Flutter的特性导致其在混合开发的场景下面存在一定劣势。闲鱼团队针对混合开发上的几个典型问题提供了对应的解决方案,使整个方案达到上线要求,该修改会在后续开放给google及社区。为全面推动Flutter在业务场景下的落地,闲鱼团队通过多次迭代演进出Fish Redux框架,保证了每位同学可以快速写出相对优秀的Flutter代码。新技术的落地过程中,在过程中通过数据化和自动化的方案极大的提升了过程中的效率,为Flutter在闲鱼的落地打下了坚实的基础。除了本文提及的各种方案外,闲鱼目前还在多个方向上发力,并对针对Flutter生态的未来进行持续的关注,分享几个现在在做的事情 Flutter整个上层基础设施的标准化演进,混合工程体系是否可以在上层完成类似Spring-boot的完整体系构架,帮助更多的Flutter团队解决上手难,无行业标准的问题。动态性能力的扩展,在符合各应用商店标准的情况下,助力业务链路的运营效率提升,保证业务效果。目前闲鱼已有的动态化方案会后续作为Fish-Redux的扩展能力提供动态化组件能力+工具链体系。Fish-Redux + UI2Code,打通代码生成链路和业务框架,保证在团队标准统一的情况下,将UI工作交由机器生成。Flutter + FaaS,让客户端同学可以成为全栈工程师,通过前后端一体的架构设计,极大的减少协同,提升效率。让工程师去从事更多创造性的工作,是我们一直努力的目标。闲鱼团队也会在新的一年更多的完善Flutter体系的建设,将更多已有的沉淀回馈给社区,帮助Flutter社区一起健康成长。 ...

June 26, 2019 · 1 min · jiezi

阿里开源云原生应用自动化引擎-OpenKruise-直击-KubeCon

阿里妹导读:在近期开展的 KubeCon China 2019 上,阿里云将陆续为全球用户分享阿里巴巴超大规模云原生落地实践、云原生前沿技术与应用包括 OpenKruise 开源项目、开放云原生应用中心(Cloud Native App Hub),同时将重磅发布边缘容器、云原生应用管理与交付体系等产品和服务。接下来的三天,阿里妹将连线会场,为你带来实时报道。 2019年6月24日至26日,由 CNCF 主办的云原生技术大会 KubeCon 在中国上海盛装启幕,阿里云容器平台团队正式宣布开源重量级项目 OpenKruise,将基于阿里巴巴经济体多年大规模应用部署、发布与管理最佳实践沉淀的能力开放给业界。 OpenKruise 是阿里巴巴开源的 Kubernetes 之上云原生应用自动化的引擎。Kruise 项目源自于阿里巴巴经济体应用过去多年的大规模应用部署、发布与管理的最佳实践,源于阿里云Kubernetes服务数千客户的需求沉淀。 “云原生应用自动化引擎”加持下的阿里经济体“全面上云”随着云原生概念的兴起,越来越多的应用开始尝试在云原生的土壤上耕耘。那么什么是云原生?简而言之,云原生就是一套能够充分利用“云”的能力,高效构建与交付应用的方法论集合,使得应用容器化的用户可以充分的利用云的弹性和“不可变基础设施”等优势专注于自身核心业务价值。 当前,阿里巴巴基础设施的云原生演进与升级也正在如火如荼的进行。而在这个阿里巴巴经济体整体云化的过程中,阿里内部在超大规模的互联网场景中,已经开始进行大量的云原生的理念落地实践,比如轻量级容器化。 阿里巴巴经济体正在大规模推进应用的轻量级容器化,从而达成利用容器的敏捷和一致等特性快速构建符合云原生理念的电商站点交付的能力,适应类似“双十一”大促的严苛技术需求。再比如说云原生应用管理,阿里巴巴经济体正在将 Kubernetes 等项目的应用编排与自动化能力,穿透到上层运维框架当中,驱动电商应用按照云原生的技术理念进行编排、交付、运行。 在阿里巴巴经济体的整体云原生化过程当中,阿里的技术团队逐渐沉淀出了一套紧贴上游社区标准,适应互联网规模化场景的技术理念与最佳实践。这其中,最重要的无疑是如何对应用进行自动化的发布、运行和管理。 OpenKruise:来自阿里经济体云原生化历程的宝贵经验与最佳实践在 KubeCon 上海,阿里云容器平台团队正式宣布了重量级项目 OpenKruise(以下简称 Kruise)的开源。 Kruise 是 cruise 的谐音,"k" for Kubernetes。字面意义是巡航或豪华游艇,寓意 Kubernetes 上应用的自动巡航,满载阿里巴巴多年应用部署管理经验。 Kruise 的目标是 automate everything on Kubernetes ! Kruise 项目源自于阿里巴巴经济体应用过去多年的大规模应用部署、发布与管理的最佳实践,源于容器平台团队对集团应用规模化运维,规模化建站的能力,源于阿里云 Kubernetes 服务数千客户的需求沉淀。Kruise 借力于云原生社区,集成阿里巴巴云原生实践之精华,反哺社区,指引业界云原生化最佳实践,少走弯路。 OpenKruise 是阿里巴巴开源的 Kubernetes 之上云原生应用自动化的引擎。Kruise 核心在于自动化,我们将从不同维度解决 Kubernetes 之上应用的自动化,包括,部署、升级、弹性扩缩容、Qos 调节、健康检查、迁移修复等等。此次 Kruise 开源的内容主要在应用部署,升级方面,即一套增强版 controller 组件用于应用的部署、升级、运维。后续,Kruise 会依次开源智能化的弹性扩缩容组件,以及应用 Qos 自调节能力的组件等。 ...

June 26, 2019 · 2 min · jiezi

容器服务Windows-Kubernetes使用阿里云日志服务来收集容器日志

目前,容器服务Windows Kubernetes支持将业务容器产生的stdout输出、日志文件同步到阿里云日志服务(SLS)进行统一管理。 支撑组件安装在Windows Kubernetes集群安装界面勾选使用日志服务,集群会安装支持日志收集的必要组件logtail。 集群安装完毕后,可以在日志服务控制台 查看到按k8s-sls-{Kubernetes 集群 ID}形式命名的工程。收集到的业务容器日志都会放在该工程下。 使用YAML模版部署业务容器YAML 模板的语法同 Kubernetes 语法,但是为了给容器指定采集配置,需要使用 env 来为 container 增加采集配置和自定义 Tag,并根据采集配置,创建对应的 volumeMounts 和 volumns。以下是一个简单的 Deployment 示例: apiVersion: extensions/v1beta1kind: Deploymentmetadata: labels: app: logtail-test name: logtail-testspec: replicas: 1 template: metadata: labels: app: logtail-test name: logtail-test spec: containers: - name: logtail image: registry-vpc.cn-hangzhou.aliyuncs.com/acs/windows-logtail:1809-1.0.0.4 command: ["powershell.exe"] args: [cmd /k "ping -t 127.0.0.1 -w 10000 > C:\log\data.log"] env: ######### 配置 环境变量 ########### - name: aliyun_logs_log-stdout value: stdout - name: aliyun_logs_log-varlog value: C:\log\*.log - name: aliyun_logs_log_tags value: tag1=v1 ################################# ######### 配置vulume mount ####### volumeMounts: - name: volumn-sls-win mountPath: c:\log volumes: - name: volumn-sls-win emptyDir: {} ############################### nodeSelector: beta.kubernetes.io/os: windows其中有三部分需要根据您的需求进行配置,一般按照顺序进行配置。 ...

June 20, 2019 · 1 min · jiezi

蚂蚁金服轻量级监控分析系统-SOFALookout-服务端开源

SOFAStack Scalable Open Financial  Architecture Stack 是蚂蚁金服自主研发的金融级分布式架构,包含了构建金融级云原生架构所需的各个组件,是在金融场景里锤炼出来的最佳实践。SOFALookout 是蚂蚁金服在 SOFAStack 体系内研发开源的一款解决系统的度量和监控问题的轻量级中间件服务。本文给大家介绍下 SOFALookout 服务器端主要提供的特性以及使用方式。SOFALookout:https://github.com/sofastack/sofa-lookout 前言容器,K8S,微服务,Mesh 以及 Serverless 这些新技术方向正在根本的变革我们运行软件的方式。我们构建的系统更加分布式化,另外由于容器,系统的生命周期更加短,变得易逝。针对这些变化,SOFALookout 希望提供一套轻量级解决方案。之前 SOFALookout 已经开源客户端的能力。今天,SOFALookout 服务器端 Metrics 部分的代码终于正式开源啦!本文给大家介绍下 SOFALookout 服务器端的主要特性以及使用方法。 什么是 SOFALookoutSOFALookout 是蚂蚁金服开源的一款解决系统的度量和监控问题的轻量级中间件服务。它提供的服务包括:Metrics 的埋点、收集、加工、存储与查询等。该开源项目包括了两个独立部分,分别是客户端与服务器端服务。 SOFALookout 目标是打造一套轻量级 Observability 实时工具平台,帮助用户解决基础设施、应用和服务等的监控和分析的问题。SOFALookout(目前已开源部分) 是一个利用多维度的 metrics 对目标系统进行度量和监控的项目。SOFALookout 的多维度 metrics 参考 Metrics2.0 标准。 SOFALookout :https://github.com/sofastack/sofa-lookoutSOFALookout 安装文档:https://www.sofastack.tech/sofa-lookout/docs/quickstart-metrics-server  SOFALookout 服务器端的主要特性: 适配社区主要 Metrics 数据源协议写入(比如: Prometheus,Metricbeat 等);数据的存储支持扩展,暂时开源版默认支持 Elasticsearch, 并且透明和自动化了相关运维操作;遵循 Prometheus 查询 API 的标准以及支持 PromQL,并进行了适当改进;自带数据查询的控制台,并支持 Grafana 进行数据可视化;使用简单,支持单一进程运行整个服务器端模块。随着 SOFALookout (metrics)服务器端代码开源,metrics 数据的处理已经形成闭环。后续我们将会进一步开源 Trace 和 Event 相关的服务能力,敬请期待。 SOFALookout 项目结构服务器端代码分别包括两部分:Gateway 模块和 Server 模块。如下图所示(展示了 SOFALookout 源码项目的模块概要结构) ├── boot├── client├── gateway└── server项目中的 boot 模块作用是方便集成和运行服务端的模块,既可以单独运行 Gateway 和 Server 的服务,也可以借助 SOFAArk 完成(Gateway 和 Server)的 All in One 的合并为单一进程运行。 ...

June 20, 2019 · 1 min · jiezi

Istio-在阿里云容器服务的部署及流量治理实践

目标在阿里云容器服务 Kubernetes 集群上部署 Istio 服务网格实践灰度发布、故障注入、熔断等 Istio 流量管理特性准备工作安装和设置 kubectl 客户端,请参考不同的操作系统,如果已经安装请忽略:macOScurl -LO https://kubectl.oss-cn-hangzhou.aliyuncs.com/macos/kubectlchmod +x ./kubectlsudo mv ./kubectl /usr/local/bin/kubectlkubectl --helpLinuxcurl -LO https://kubectl.oss-cn-hangzhou.aliyuncs.com/linux/kubectlchmod +x ./kubectlsudo mv ./kubectl /usr/local/bin/kubectlkubectl --helpWindows把 https://kubectl.oss-cn-hangzhou.aliyuncs.com/windows/kubectl.exe 放到系统PATH路径下 kubectl --help配置 kubectl 连接 Kubernetes 集群的配置,可参考文档 通过kubectl连接Kubernetes集群 实验步骤部署Istio登录容器服务控制台,在集群列表中选择一个集群,打开更多 - 部署 Istio 保持默认配置,点击"部署 Istio" 等待完成后,Istio 已经成功部署在 Kubernetes 集群中使用 kiali 查看服务网格可视化kubectl port-forward -n istio-system "$(kubectl get -n istio-system pod --selector=app=kiali -o jsonpath='{.items..metadata.name}')" 20001在本地浏览器中访问 http://localhost:20001 ,使用默认账户 admin/admin 登录 灰度发布创建命名空间并开启 Sidecar 自动注入:单击左侧导航栏中的集群 > 命名空间,进入命令空间页面,创建一个名称为 demo 的命名空间,并为其添加一个 istio-injection:enabled 的标签 单击左侧导航栏中服务网格 > 虚拟服务,进入虚拟服务(Virtual Service)页面,点击创建,创建一个简单的示例应用,指定应用名称为istio-app,指定应用版本为v1,选择刚刚创建的命名空间 demo 配置应用容器,使用如下镜像:registry.cn-beijing.aliyuncs.com/test-node/node-server:v1 ...

June 19, 2019 · 2 min · jiezi

云原生生态周报-Vol-8-Gartner-发布云原生趋势

业界要闻 Gartner 发布云原生基础设施未来的八大趋势:权威分析机构 Gartner 在对 2020 年技术趋势的展望当中指出:“预计2020年所有领先的容器管理软件均内置服务融合技术,到2022年有75%的全球化企业将在生产中使用容器化的应用、还有50%的应用软件将容器化适应超融合环境”。Gartner 在报告中表示,未来基础设施技术演进的八大趋势包括: 多云与混合云; Service Mesh;基于 Kubernetes 的 fPaaS(即:函数计算 PaaS);裸金属容器和微虚拟机;第三方应用和 ISV 的大规模容器化;对有状态应用的完善支持;整个技术栈都会基于 CNCF 中的知名项目来构建。Gartner 2019年发布了《公有云容器服务竞争格局》,阿里云是唯一进入该报告的国内云厂商, 拥有国内最大公共云容器集群。 Kubernetes 五周年官方回顾:在本月,Kubernetes 项目迎来了自己的五周岁生日,整个 Kubernetes 社区开展了一系列纪念活动来,CNCF 官方博客也刊登了 Kubernetes 社区对自己的五年历程的总结与回顾: Kubernetes 项目的成功,首先归功于数以千计的开源开发者的智慧与劳动;Kubernetes 已经成长为这个星球上最庞大的单一开源项目之一。而在维护这个项目的过程中,数千位开发者的远程协同与严谨自动化的开源项目管理方式,带来了项目的稳定性与高质量,这个过程堪称软件工程史上的典型范例;Kubernetes 项目取得今天的成绩,还归功于其富有远见的设计思想与技术理念。Kubernetes 的核心原理与实现,终于使得“云原生”从虚无缥缈的概念,变成了运行在每一个数据中心里的代码与架构;Kubernetes 项目的发展永远不会停止,这是因为 Kubernetes 的 API,已经成为了新一代开发者编写与构建软件的基础假设与核心依赖:这个生长于 Kubernetes 之上的“云原生”生态系统,已经成为了推动整个社区不断向前发展的动力源泉。上游重要进展 Kubernetes 项目 kubelet cAdvisor JSON API 要正式下线了: SIG-Node 已经为 kubelet 加入了可以禁用这些接口的参数,同时,这些接口也被标记为“废弃” (deprecrated)。 实际上,社区上游 CAdvisor 的独立性一直在被削弱,因为 kubelet的metrics指标要聚焦,以便提高kubelet性能,允许更频繁的查看 Node metrics,详见:#68522Kubelet到API Server的连接被强制关闭会出错,目前社区已经合并了修复,但是短时内可能会出现node NotReady 的错误。Admission Hook 添加 ObjectSelector:Admission WebHook 一直以来都是开发者对 Kubernetes 进行扩展的重要手段。但是 WebHook 里很长一段时间以来只支持按照 Namespace 过滤 API 对象。而在本周,Admission Hook 的语义中终于添加了 ObjectSelector。这样,Hook 的开发者终于可以按照更细致的 Label 来过滤出该 Hook 关系的 API 对象了。开源项目推荐 ...

June 18, 2019 · 1 min · jiezi

基于Knative开发应用

目录安装 Istio安装 Knative玩转 helloworld-goWordPress 实战创建 Kubernetes 集群确保 Kubernetes 集群创建的时候已经选择了启用日志服务确保 Kubernetes 集群和 OSS 在一个 regionKubernetes 集群创建的时候需要开启 kube-apiserver 公网访问提前帮用户配置好 kubeconfig 命令行安装 Istio安装 Istio 时注意以下几点: 默认要安装 gateway日志服务和 Xtrace 要提前开通,Istio 需要使用 ZipKin v1 向 Xtrace 汇报监控数据在容器服务集群管理页面可以直接在目标集群上部署 Istio 安装 Knative选择好目标集群使用一键部署功能直接安装即可, 安装文档 玩转 helloworld-go配置日志采集策略部署 Helloworld监控告警调用链压测数据展示日志管理日志服务控制台: https://sls.console.aliyun.com本示例以容器标准输出采集为例进行展示,详细设置步骤可以参考日志服务文档根据 Kubernetes 集群 ID 找到对应的日志服务 Project创建一个新的 Logstore设置数据导入方式 选择 Docker标准输出 配置容器标准输出日志采集策略{ "inputs": [ { "detail": { "IncludeEnv": { "K_SERVICE": "helloworld-go" }, "IncludeLabel": {}, "ExcludeLabel": {} }, "type": "service_docker_stdout" } ], "processors": [ { "detail": { "KeepSource": false, "NoMatchError": true, "Keys": [ "time", "level", "msg" ], "NoKeyError": true, "Regex": "(\\d+-\\d+-\\d+ \\d+:\\d+:\\d+)\\s+(\\w+)\\s+(*)", "SourceKey": "content" }, "type": "processor_regex" } ]}分别为相应的键值 time、level 和 msg 设置数据类型 ...

June 17, 2019 · 2 min · jiezi

揭秘一个高准确率的Flutter埋点框架如何设计

背景用户行为埋点是用来记录用户在操作时的一系列行为,也是业务做判断的核心数据依据,如果缺失或者不准确将会给业务带来不可恢复的损失。闲鱼将业务代码从Native迁移到Flutter上过程中,发现原先Native体系上的埋点方案无法应用在Flutter体系之上。而如果我们只把业务功能迁移过来就上线,对业务是极其不负责任的。因此,经过不断探索,我们沉淀了一套Flutter上的高准确率的用户行为埋点方案。 用户行为埋点定义先来讲讲在我们这里是如何定义用户行为埋点的。在如下用户时间轴上,用户进入A页面后,看到了按钮X,然后点击了这个按钮,随即打开了新的页面B。 这个时间轴上有如下5个埋点事件发生: 进入A页面。A页面首帧渲染完毕,并获得了焦点。曝光坑位X。按钮X处于手机屏幕内,且停留一段时间,让用户可见可触摸。点击坑位X。用户对按钮X的内容很感兴趣,于是点击了它。按钮X响应点击,然后需要打开一个新页面。离开A页面。A页面失去焦点。进入B页面。B页面首帧渲染完毕,并获得焦点。在这里,打埋点最重要的是时机,即在什么时机下的事件中触发什么埋点,下面来看看闲鱼在Flutter上的实现方案。 实现方案进入/离开页面 在Native原生开发中,Android端是监听Activity的onResume和onPause事件来做为页面的进入和离开事件,同理iOS端是监听UIViewController的viewWillAppear和viewDidDisappear事件来做为页面的进入和离开事件。同时整个页面栈是由Android和iOS操作系统来维护。 在Flutter中,Android和iOS端分别是用FlutterActivity和FlutterViewController来做为容器承载Flutter的页面,通过这个容器可以在一个Native的页面内(FlutterActivity/FlutterViewController)来进行Flutter原生页面的切换。即在Flutter自己维护了一个Flutter页面的页面栈。这样,原来我们最熟悉的那套在Native原生上方案在Flutter上无法直接运作起来。 针对这个问题,可能很多人会想到去注册监听Flutter的NavigatorObserver,这样就知道Flutter页面的进栈(push)和出栈(pop)事件。但是这会有两个问题: 假设A、B两个页面先后进栈(A enter -> A leave -> B enter)。然后B页面返回退出(B leave),此时A页面重新可见,但是此时是收不到A页面push(A enter)的事件。假设在A页面弹出一个Dialog或者BottomSheet,而这两类也会走push操作,但实际上A页面并未离开。好在Flutter的页面栈不像Android Native的页面栈那么复杂,所以针对第一个问题,我们可以来维护一个和页面栈匹配的索引列表。当收到A页面的push事件时,往队列里塞一个A的索引。当收到B页面的push事件时,检测列表内是否有页面,如有,则对列表最后一个页面执行离开页面事件记录,然后再对B页面执行进入页面事件记录,接着往队列里塞一个B的索引。当收到B页面的pop事件时,先对B页面执行离开页面事件记录,然后对队列里存在的最后一个索引对应的页面(假设为A)进行判断是否在栈顶(ModalRoute.of(context).isCurrent),如果是,则对A页面执行进入页面事件记录。 针对第二个问题,Route类内有个成员变量overlayEntries,可以获取当前Route对应的所有图层OverlayEntry,在OverlayEntry对象中有个成员变量opaque可以判断当前这个图层是否全屏覆盖,从而可以排除Dialog和BottomSheet这种类型。再结合问题1,还需要在上述方案中加上对push进来的新页面来做判断是否为一个有效页面。如果是有效页面,才对索引列表中前一个页面做离开页面事件,且将有效页面加到索引列表中。如果不是有效页面,则不操作索引列表。 以上并不是闲鱼的方案,只是笔者给出的一个建议。因为闲鱼APP在一开始落地Flutter框架时,就没有使用Flutter原生的页面栈管理方案,而是采用了Native+Flutter混合开发的方案。具体可参考前面的一篇文章《已开源|码上用它开始Flutter混合开发——FlutterBoost》。因此接下来也是基于此来阐述闲鱼的方案。 闲鱼的方案如下(以Android为例,iOS同理): 注:首次打开指的是基于混合栈新打开一个页面,非首次打开指的是通过回退页面的方式,在后台的页面再次到前台可见。 看似我们将何时去触发进入/离开页面事件的判断交给Flutter侧,实际上依然跟Native侧的页面栈管理保持了一致,将原先在Native侧做打埋点的时机告知Flutter侧,然后Flutter侧再立刻通过channel来调用Native侧的打埋点方法。那么可能会有人问,为什么这么绕,不全部交给Native侧去直接管理呢?交给Native侧去直接管理这样做针对非首次打开这个场景是合适的,但是对首次打开这个场景却是不合适的。因为在首次打开这个场景下,onResume时Flutter页面尚未初始化,此时还不知道页面信息,因此也就不知道进入了什么页面,所以需要在Flutter页面初始化(init)时再回过来调Native侧的进入页面埋点接口。为了避免开发人员去关注是否为首次打开Flutter页面,因此我们统一在Flutter侧来直接触发进入/离开页面事件。 曝光坑位先讲下曝光坑位在我们这里的定义,我们认为图片和文本是有曝光意义的,其他用户看不见的是没有曝光意义的,在此之上,当一个坑位同时满足以下两点时才会被认为是一次有效曝光: 坑位在屏幕可见区域中的面积大于等于坑位整体面积的一半。坑位在屏幕可见区域中停留超过500ms。基于此定义,我们可以很快得出如下图所示的场景,在一个可以滚动的页面上有A、B、C、D共4个坑位。其中: 坑位A已经滑出了屏幕可见区域,即invisible;坑位B即将向上从屏幕中可见区域滑出,即visible->invisible;坑位C还在屏幕中央可视区域内,即visible;坑位D即将滑入屏幕中可见区域,invisible->visible; 那么我们的问题就是如何算出坑位在屏幕内曝光面积的比例。要算出这个值,需要知道以下几个数值: 容器相对屏幕的偏移量坑位相对容器的偏移量坑位的位置和宽高容器的位置和宽高其中坑位和容器的宽和高很容易获取和计算,这里就不再累述。 获取容器相对屏幕的偏移量 //监听容器滚动,得到容器的偏移量double _scrollContainerOffset = scrollNotification.metrics.pixels;获取坑位相对容器的偏移量 //曝光坑位Widget的contextfinal RenderObject childRenderObject = context.findRenderObject();final RenderAbstractViewport viewport = RenderAbstractViewport.of(childRenderObject);if (viewport == null) { return;}if (!childRenderObject.attached) { return;}//曝光坑位在容器内的偏移量final RevealedOffset offsetToRevealTop = viewport.getOffsetToReveal(childRenderObject, 0.0);逻辑判断 if (当前坑位是invisible && 曝光比例 >= 0.5) { 记录当前坑位是visible状态 记录出现时间} else if (当前坑位是visible && 曝光比例 < 0.5) { 记录当前坑位是invisible状态 if (当前时间-出现时间 > 500ms) { 调用曝光埋点接口 }}点击坑位 ...

June 13, 2019 · 1 min · jiezi

技术选型之Docker容器引擎

题外话 最近对Docker和Kubernetes进行了一番学习,前两天做了一次技术分享,回去听了一遍自己演讲的录音,发现单单PPT做好还是远远不够的,没有提前准备好逻辑严谨的讲稿,在讲的时候出现了卡壳、漏掉技术点、逻辑矛盾的问题。为了解决这个问题,我打算以后在做技术分享前,都按着PPT的内容先写成博客,理顺表达逻辑。另外,我觉得每次技术分享使用的PPT都应该尽可能的做好,因为你不知道未来会不会还要拿来再讲几遍。本文以PPT+讲稿的方式编写,权当对自己这次技术分享做个记录,欢迎大家拍砖。 1. Docker出现的背景 在平常的研发和项目场景中,以下情况普遍存在: 个人开发环境 为了做大数据相关项目,需要安装一套CDH集群,常见的做法是在自己电脑里搭建3台与CDH版本对应的虚拟机,把CDH集群装起来后,考虑到以后很有可能还要使用一个干净的CDH集群,为了避免以后重复安装环境,通常会对整套CDH集群做一个备份,这样电脑里就有6个虚拟机镜像了。另外,后面在学习其他技术时,比如学习Ambari大数据集群,那么为了不破坏已有的虚拟机环境,又要重新搭建3台虚拟机,本机磁盘很快被一大堆的虚拟机镜像占满。公司内部开发环境 公司里往往会以小团队的方式来做项目,一般由运维部门从他们管理的服务器资源中分配出虚拟机供团队内部开发测试使用。比如做一个与机器学习相关的项目: 小明在运维部门分配的虚拟机上搭建了一套Ambari集群,拿来跑大数据相关业务小刚用python3写了一个机器学习算法,放到虚拟机上运行发现虚拟机里是python2,算法不兼容,于是把虚拟机里的python版本升级了,算法跑通了,但Ambari用到python的部分功能可能就报错了小李开发了应用,放到虚拟机上启动tomcat,发现虚拟机里的是OpenJDK,导致tomcat起不来,于是又安装了一个JDK,这时候可能Ambari里的Java代码可能就报错了小赵想利用服务器资源做性能测试,发现虚拟机严重削减了性能,最终还是要直接找物理机来跑测试,破坏了物理机原来的环境做完项目后,这些虚拟机上安装的东西往往变得没用了,下个项目组来还是得新申请虚拟机重新部署软件开发/测试/现场环境研发人员在开发环境里写好了代码做好测试后,提交给测试部门,测试人员在测试环境跑起来发现有BUG,研发人员说在开发环境没这个BUG,和测试人员多次扯皮解决BUG后发布版本,发到现场在生产环境部署后,又发现有BUG,这下轮到工程人员和测试人员扯皮。有时候为了兼容特殊的现场环境,还需要对代码进行定制化修改,拉出分支,这样导致了每次到现场升级都是一场噩梦升级或迁移项目 在每次发版本要升级到现场时,如果现场起了多个tomcat应用,那么需要对每个tomcat都先停掉,替换war包,然后再起起来,轮流着做,不仅繁琐而且很容易出错,如果遇到升级后出现严重BUG,还要手工做回退。另外,如果项目想上云,那么在云上部署后要重新进行一轮测试,如果后面考虑还云厂商,可能相同的测试还要再进行一次(比如更换了数据存储组件),费时费力。 总结以上列举的所有场景,他们存在的一个共同的问题是:没有一种既能够屏蔽操作系统差异,又能够以不降低性能的方式来运行应用的技术,来解决环境依赖的问题。Docker应运而生。 2. Docker是什么 Docker是一种应用容器引擎。首先说一下何为容器,Linux系统提供了Namespace和CGroup技术实现环境隔离和资源控制,其中Namespace是Linux提供的一种内核级别环境隔离的方法,能使一个进程和该进程创建的子进程的运行空间都与Linux的超级父进程相隔离,注意Namespace只能实现运行空间的隔离,物理资源还是所有进程共用的,为了实现资源隔离,Linux系统提供了CGroup技术来控制一个进程组群可使用的资源(如CPU、内存、磁盘IO等),把这两种技术结合起来,就能构造一个用户空间独立且限定了资源的对象,这样的对象称为容器。Linux Container是Linux系统提供的容器化技术,简称LXC,它结合Namespace和CGroup技术为用户提供了更易用的接口来实现容器化。LXC仅为一种轻量级的容器化技术,它仅能对部分资源进行限制,无法做到诸如网络限制、磁盘空间占用限制等。dotCloud公司结合LXC和以下列出的技术实现了Docker容器引擎,相比于LXC,Docker具备更加全面的资源控制能力,是一种应用级别的容器引擎。 Chroot:该技术能在container里构造完整的Linux文件系统;Veth:该技术能够在主机上虚拟出一张网卡与container里的eth0网卡进行桥接,实现容器与主机、容器之间的网络通信;UnionFS:联合文件系统,Docker利用该技术“Copy on Write”的特点实现容器的快速启动和极少的资源占用,后面会专门介绍该文件系统;Iptables/netfilter:通过这两个技术实现控制container网络访问策略;TC:该技术主要用来做流量隔离,限制带宽;Quota:该技术用来限制磁盘读写空间的大小;Setrlimit:该技术用来限制container中打开的进程数,限制打开的文件个数等也正是因为Docker依赖Linux内核的这些技术,至少使用3.8或更高版本的内核才能运行Docker容器,官方建议使用3.10以上的内核版本。3. 与传统虚拟化技术的区别 传统的虚拟化技术在虚拟机(VM)和硬件之间加了一个软件层Hypervisor,或者叫做虚拟机管理程序。Hypervisor的运行方式分为两类: 直接运行在物理硬件之上。如基于内核的KVM虚拟机,这种虚拟化需要CPU支持虚拟化技术;运行在另一个操作系统。如VMWare和VitrualBox等虚拟机。 因为运行在虚拟机上的操作系统是通过Hypervisor来最终分享硬件,所以虚拟机Guest OS发出的指令都需要被Hypervisor捕获,然后翻译为物理硬件或宿主机操作系统能够识别的指令。VMWare和VirtualBox等虚拟机在性能方面远不如裸机,但基于硬件虚拟机的KVM约能发挥裸机80%的性能。这种虚拟化的优点是不同虚拟机之间实现了完全隔离,安全性很高,并且能够在一台物理机上运行多种内核的操作系统(如Linux和Window),但每个虚拟机都很笨重,占用资源多而且启动很慢。 Docker引擎运行在操作系统上,是基于内核的LXC、Chroot等技术实现容器的环境隔离和资源控制,在容器启动后,容器里的进程直接与内核交互,无需经过Docker引擎中转,因此几乎没有性能损耗,能发挥出裸机的全部性能。但由于Docker是基于Linux内核技术实现容器化的,因此使得容器内运行的应用只能运行在Linux内核的操作系统上。目前在Window上安装的docker引擎其实是利用了Window自带的Hyper-V虚拟化工具自动创建了一个Linux系统,容器内的操作实际上是间接使用这个虚拟系统实现的。 4. Docker基本概念 Docker主要有如下几个概念: 引擎:创建和管理容器的工具,通过读取镜像来生成容器,并负责从仓库拉取镜像或提交镜像到仓库中;镜像:类似于虚拟机镜像,一般由一个基本操作系统环境和多个应用程序打包而成,是创建容器的模板;容器:可看作一个简易版的Linxu系统环境(包括root用户权限、进程空间、用户空间和网络空间等)以及运行在其中的应用程序打包而成的盒子;仓库:集中存放镜像文件的场所,分为公共仓库和私有仓库,目前最大的公共仓库是官方提供的Docker Hub,此外国内的阿里云、腾讯云等也提供了公共仓库;宿主机:运行引擎的操作系统所在服务器。5. Docker与虚拟机、Git、JVM的类比 为了让大家对Docker有更直观的认识,下面分别进行三组类比: 上图中Docker的镜像仓库类似于传统虚拟机的镜像仓库或存放镜像的本地文件系统,Docker引擎启动容器来运行Spark集群(容器内包含基础的Linux操作系统环境),类比于虚拟机软件启动多个虚拟机,在虚拟机内分别运行Spark进程,两者区别在于Docker容器内的应用在使用物理资源时,直接与内核打交道,无需经过Docker引擎。 Docker的仓库思想与Git是相同的。 Docker的口号是“Build,Ship,and Run Any App,Anywhere”,也就是可以基于Docker构建、装载和运行应用程序,一次构建到处运行。Java的口号是“Write Once,Run Anywhere”,即一次编写到处运行。Java是基于JVM适配操作系统的特点来屏蔽系统的差异,Docker则是利用内核版本兼容性的特点来实现一次构建导出运行,只要Linux系统的内核是3.8或更高的版本,就都能把容器跑起来。 当然,正如Java中如果应用代码使用了JDK10的新特性,基于JDK8就无法运行一样,如果容器内的应用使用了4.18版本的内核特性,那么在CentOS7(内核版本为3.10)启动容器时,虽然容器能够启动,但里面应用的功能是无法正常运行的,除非把宿主机的操作系统内核升级到4.18版本。6. Docker镜像文件系统 Docker镜像采用分层存储格式,每个镜像可依赖其他镜像进行构建,每一层的镜像可被多个镜像引用,上图的镜像依赖关系,K8S镜像其实是CentOS+GCC+GO+K8S这四个软件结合的镜像。这种分层结构能充分共享镜像层,能大大减少镜像仓库占用的空间,而对用户而言,他们所看到的容器,其实是Docker利用UnionFS(联合文件系统)把相关镜像层的目录“联合”到同一个挂载点呈现出来的一个整体,这里需要简单介绍一个UnionFS是什么: UnionFS可以把多个物理位置独立的目录(也叫分支)内容联合挂载到同一个目录下,UnionFS允许控制这些目录的读写权限,此外对于只读的文件和目录,它具有“Copy on Write(写实复制)”的特点,即如果对一个只读的文件进行修改,在修改前会先把文件复制一份到可写层(可能是磁盘里的一个目录),所有的修改操作其实都是对这个文件副本进行修改,原来的只读文件并不会变化。其中一个使用UnionFS的例子是:Knoppix,一个用于Linux演示、光盘教学和商业产品演示的Linux发行版,它就是把一个CD/DVD和一个存在在可读写设备(例如U盘)联合挂载,这样在演示过程中任何对CD/DVD上文件的改动都会在被应用在U盘上,不改变原来的CD/DVD上的内容。 UnionFS有很多种,其中Docker中常用的是AUFS,这是UnionFS的升级版,除此之外还有DeviceMapper、Overlay2、ZFS和 VFS等。Docker镜像的每一层默认存放在/var/lib/docker/aufs/diff目录中,当用户启动一个容器时,Docker引擎首先在/var/lib/docker/aufs/diff中新建一个可读写层目录,然后使用UnionFS把该可读写层目录和指定镜像的各层目录联合挂载到/var/lib/docker/aufs/mnt里的一个目录中(其中指定镜像的各层目录都以只读方式挂载),通过LXC等技术进行环境隔离和资源控制,使容器里的应用仅依赖mnt目录中对应的挂载目录和文件运行起来。 利用UnionFS写实复制的特点,在启动一个容器时, Docker引擎实际上只是增加了一个可写层和构造了一个Linux容器,这两者都几乎不消耗系统资源,因此Docker容器能够做到秒级启动,一台服务器上能够启动上千个Docker容器,而传统虚拟机在一台服务器上启动几十个就已经非常吃力了,而且虚拟机启动很慢,这是Docker相比于传统虚拟机的两个巨大的优势。 当应用只是直接调用了内核功能来运作的情况下,应用本身就能直接作为最底层的层来构建镜像,但因为容器本身会隔绝环境,因此容器内部是无法访问宿主机里文件的(除非指定了某些目录或文件映射到容器内),这种情况下应用代码就只能使用内核的功能。但是Linux内核仅提供了进程管理、内存管理、文件系统管理等一些基础且底层的管理功能,在实际的场景中,几乎所有软件都是基于操作系统来开发的,因此往往都需要依赖操作系统的软件和运行库等,如果这些应用的下一层直接是内核,那么应用将无法运行。所以实际上应用镜像往往底层都是基于一个操作系统镜像来补足运行依赖的。 Docker中的操作系统镜像,与平常安装系统时用的ISO镜像不同。ISO镜像里包含了操作系统内核及该发行版系统包含的所有目录和软件,而Docker中的操作系统镜像,不包含系统内核,仅包含系统必备的一些目录(如/etc /proc等)和常用的软件和运行库等,可把操作系统镜像看作内核之上的一个应用,一个封装了内核功能,并为用户编写的应用提供运行环境的工具。应用基于这样的镜像构建,就能够利用上相应操作系统的各种软件的功能和运行库,此外,由于应用是基于操作系统镜像来构建的,就算换到另外的服务器,只要操作系统镜像中被应用使用到的功能能适配宿主机的内核,应用就能正常运行,这就是一次构建到处运行的原因。 ...

June 13, 2019 · 1 min · jiezi

规模化落地云原生阿里云即将重磅亮相-KubeCon-China

2019 年 6 月 24 日至 26 日, 由 Cloud Native Computing Foundation (CNCF) 主办的云原生技术大会 KubeCon + CloudNativeCon + Open Source Summit(上海 )即将在中国上海盛装启幕。 继 2018 年 KubeCon 首次成功登陆中国,本届 KubeCon 将吸引来自全世界数千名技术人员将会参加此次盛会,参与CNCF 全部项目和话题的深度探讨和案例分析,聆听 CNCF 项目的运维者和最终用户的分享。本届 KubeCon + CloudNativeCon + Open Source Summit 大会项目委员会由 75 名专家组成,审阅 KubeCon + CloudNativeCon 的 618 项提案,在本次 KubeCon China 2019 上,阿里巴巴共有 26 个技术演讲入选。  在本次 KubeCon 上,阿里云智能容器平台负责人丁宇(叔同)、 CNCF TOC、etcd 项目作者、阿里云容器平台资深技术专家李响,CNCF 大使、Kubernetes 项目维护者、阿里云高级技术专家张磊等众多云原生技术大咖都会悉数到场并做技术分享,同时会为您带来包括开源 Virtual Cluster 强多租户设计、 OpenKruise 开源项目、开放云原生应用中心(Cloud Native App Hub)等众多云原生先进技术的最新动态与进展。我们非常期待您能够在 KubeCon China 上与阿里容器平台团队见面、进行交流或者开展技术合作。 ...

June 12, 2019 · 2 min · jiezi

Knative-初体验Serving-Hello-World

通过前面两章的学习你已经掌握了很多 Knative 的理论知识,基于这些知识你应该对 Knative 是谁、它来自哪里以及它要做什么有了一定的认识。可是即便如此你可能还是会有一种犹抱琵琶半遮面,看不清真容的感觉,这就好比红娘拿姑娘的 100 张生活照给你看也不如你亲自去见一面。按常理出牌,一般到这个阶段就该 Hello World 出场了。本篇文章就通过一个 Hello World 和 Knative 来一个“约会”,让你一睹 Knative 这位白富美的真容。 安装 KnativeKnative 社区提供的安装步骤见这里,整个流程大概包含如下三个部分: 准备 kubernetes 环境(你可以在阿里云容器服务中快速创建一个 kubernetes 集群 )安装istio安装 Knative组件虽然看起来只有三步,但是每一步其实都需要手动做大量的工作,执行一堆命令。另外社区文档提供的 yaml 文件默认使用了大量的 gcr.io 镜像,目前国内无法拉取 gcr.io 镜像。所以这些 yaml 文件在国内不能直接使用,至少需要手动同步 30 多个镜像才行。 不过别着急,阿里云容器服务的应用目录已经有 Knative 的安装包,现在只需要在阿里云容器服务上面点击几下鼠标就能轻轻松松搭建一个 Knative 集群 O ^ ~ ^ O O ^ ~ ^ O O ^ ~ ^ O 创建 Kubernetes 集群阿里云容器服务可以通过管理控制台非常方便地创建 Kubernetes 集群。具体过程可以参考创建Kubernetes集群。容器服务提供了专有集群和托管集群两种类型,如果不知道该怎么选择建议你直接选择托管版的 Kubernetes 集群。托管版无需你自己承担 Kubernetes Master 组件的管理和运维,你只需要提供 Node 节点即可。 ...

June 11, 2019 · 6 min · jiezi

高可用-kubernetes-集群部署实践

前言Kubernetes(k8s) 凭借着其优良的架构,灵活的扩展能力,丰富的应用编排模型,成为了容器编排领域的事实标准。越来越多的企业拥抱这一趋势,选择 k8s 作为容器化应用的基础设施,逐渐将自己的核心服务迁移到 k8s 之上。 可用性对基础设施而言至关重要。各大云计算厂商纷纷推出了高可用、可扩展的 k8s 托管服务,其中比较有代表性的有 Amazon EKS、Azure Kubernetes Service (AKS)、Google Kubernetes Engine、阿里云容器服务 Kubernetes 版等。 虽然公有云托管的 k8s 服务百花齐放,但很多企业仍有自建集群的需求。正是这样的原因,促进了一大批出色的 k8s 集群部署方案的诞生,他们的特点如下表所示。 部署方案特点Kubeadm1. 官方出品的部署工具,提供了 k8s 集群生命周期管理的领域知识。2. 旨在成为更高级别工具的可组合构建块。 Kubespray1. 支持在裸机和 AWS、GCE、Azure 等众多云平台上部署 k8s。2. 基于 Ansible Playbook 定义 k8s 集群部署任务。3. 支持大部分流行的 Linux 发行版。 || Kops | 1. 仅支持在 AWS、GCE 等少数云平台上部署 k8s。2. 建立在状态同步模型上,用于 dry-run 和自动幂等性。3. 能够自动生成 Terraform 配置。 || Rancher Kubernetes Engine(RKE) | 1. 著名的开源企业级容器管理平台 Rancher 提供的轻量级 k8s 安装工具。2. 支持在裸机、虚拟机、公有云上部署和管理 k8s 集群。 | ...

June 10, 2019 · 3 min · jiezi

容器监控实践Grafana

概述Grafana 是一个开源的,可以用于大规模指标数据的可视化项目,甚至还能对指标进行报警。基于友好的 Apache License 2.0 开源协议,目前是prometheus监控展示的首选。优点如下: 1.使用:配置方便:支持Dashboard、Panel、Row等组合,且支持折线图、柱状图等多种图例图表漂亮:可以选择暗黑系或纯白系,你也可以自己定义颜色模板很多:grafana模板很活跃,有很多用户贡献的面板,直接导入就能用支持多种数据源:grafana作为展示面板,支持很多数据源,如Graphite、ES、Prometheus等权限管理简单:有admin、viewer等多种角色管控2.二次开发:如果默认的grafana不能满足你的需求,要二次开发,官方也提供了很多支持: 协议为Apache License 2.0:商业友好,随便改吧,改完拿去卖也行。完善的API调用:权限、面板、用户、报警都支持api调用。多种鉴权方式:OAuth、LADP、Proxy多种方式,你可以接入自己公司的鉴权系统插件开发:如果你不想直接改代码,可以做自己的插件go+Angular+react:常用的技术栈,方便二次开发prometheus + grafana 做为监控组合很方便,很强大,改造了鉴权之后更加香。一开始我们还尝试使用grafana自带的报警功能,可惜比较鸡肋,无法用于生产,报警的issue一大堆官方也不想修改,作罢 部署步骤一:安装grafana Grafana提供了很多种部署方式,如果你的展示报警是在K8S集群外,可以二进制直接部署,如果grafana本身在集群内,或者管理端也是k8s集群,可以用yaml部署: Deployment配置: apiVersion: apps/v1kind: Deploymentmetadata: namespace: kube-system name: grafanaspec: replicas: 1 selector: matchLabels: app: grafana template: metadata: namespace: kube-system annotations: grafana-version: '1.0' name: grafana labels: app: grafana spec: containers: - name: grafana image: grafana/grafana:5.1.0 imagePullPolicy: Always securityContext: runAsUser: 0 env: - name: GF_SECURITY_ADMIN_PASSWORD value: "admin" ports: - name: grafana containerPort: 3000 resources: requests: memory: "100Mi" cpu: "100m" limits: memory: "2048Mi" cpu: "1024m"Service配置: ...

June 6, 2019 · 2 min · jiezi

UI2CODE再进化结合Redux的框架升级

摘要: 自从有了ui2code,妈妈再也不用担心我加班背景UI2CODE的目标是通过分析视觉稿得到对应的代码,让AI提高开发效率。然而过去静态化页面的产出,不能得到业务场景的需求。针对于此,我们以UI2CODE自动化开发为基底,结合Redux的消息机制,将自动化生成的维度提升到页面的处理。 透过框架,可自动化生成页面代码,并且具有数据驱动展示、消息派送等动态性能力。期望在复杂的业务场景下,简化开发的工作。并且在使用一致化的架构下,避免未来业务代码耦合严重,使代码分工明确,容易修改维护。 进化后的UI2CODE? 开发者可以透过Intellij Plugin分析视觉稿后会生成对应的视图代码,以及和此页面框架结合的能力。 在整体开发的定位上我们的野心是,提供一套可扩充的页面消息框架,并且自动生成大部分的UI代码。目标带来以下的好处: 快速建构新应用,框架将完成大部分的事,业务开发同学只要专注于业务代码让开发人员的进入门槛降低,在我们落地的经验中,后端同学只要有基本的概念,无需花费太多经历,可直接上手帮忙写代码让页面的架构统一化,让页面的开发有统一的规则,也方便后续的维护提供通用的组件库,可重复利用核心设计思路我们在设计上主要参考于MVP、CLEAN、VIPER以及FISH_REDUX等框架。目的在实践高聚合低耦合的前提下,分拆为视图组装层、视图展示层、数据层、事件交互层。层层分离职责,不互相干扰,又可互相通讯。 分层拆开的好处为容易维护,并且可以单元测试"业务展示"以及"业务逻辑",框架上清晰,容易有一个统一的遵循规则,达到简单编写重复可利用。 UI2CODE页面框架的设计概念为,主要分为Page、Card、Reaction三大元素。在上层的Page负责组装页面,制定页面的风格。Card则为可重复利用的视图展示元素。Reaction则为事件反应的监听。 在整个页面框架上,可以透过UI2CODE Plugin分析自动化生成业务UI,产生出Page、Card、Card(DataModel)。仅需修改Card上额外的业务展示,以及撰写Reaction中的业务逻辑。 具体实现架构在介绍框架组件前,先理解UI2CODE的基本组成页面目录如下: 以Page为单位,页面本体demo_page为其他页面路由调用的起点,透过设置Config.dart决定内部含的卡片列表以及事件处理列表,组合出Card以及Reaction的关联。 其详细的架构关系如下: PagePage为框架基础的单位,为单一页面,负责决定视图的组装以及页面的样式(Template)。 在Page之内可包含若干的Card以及Reaction,分别为视图的展示以及视图的事件处理。可以很清晰地将业务场景做分割成小模块,不互相影响。 Abstract class PageStatelessWidget extends StatelessWidgetimplements Lifecycle 可由UI2CODE Plugin自动化产生框架统一分发管理页面生命周期Lifecycle透过设定Template指定页面要呈现的样版,或者修改如背景等属性透过设定Config指定这个页面含有的Cards和Reactions透过设定PageState可添加额外的数据 Page TemplateTemplate样板为页面的抽象化,在整体页面上分为多个样板可选择。并且支持设置AppBar(非必选)、Header(非必选)、Body、Stack(非必选)等子样板。 样板可比喻为页面的容器,目前支持以下样板,并且可扩充: PageTemplate,通用页面容器,并支持NestedScrollView的Silver Header滚动(若需要)PageBottomNavigatorTemplate,含有底部导航的容器,如首页PageSwitchTabTemplate,含有分页Tab功能的容器各个子样板也有相对应的Template可选择,如在Body内的样板功能为决定内部Cards排列的方式。举例来说,BodyListViewTemplate则是列表展示。 使用Template最大的好处为减少开发工作,可直接使用封装后的接口。并且页面内的所有样板将共用消息机制,可以互相传递消息,如Body内部的卡片很容易发送消息给AppBar等。这是框架上的有力之处。 Page ConfigConfig决定页面的组装,包含了元件有哪些,以及事件处理反射的类绑定。 Extends PageConfig 可由UI2CODE Plugin自动化产生透过设定cards注册这个页面所载入的卡片透过设定actions注册这个页面所响应的类,支持卡片事件以及页面事件支持额外设置AppBar、Header、Stack等组件(非必须)如何绑定,举例来说: void actionConfig(List< List < Function>> actions) {//卡片Card8575, 响应事件的类为Card8575Reactionactions.add(< Function>[() => Card8575, () => new Card8575Reaction()]);} CardCard代表基本的视图展示,业务UI,其中包含了View widget以及DataModel数据。框架内会将两者Data binding起来,由数据来驱动最终展示的呈现。达到如MVP中View和ViewModel的隔离效果。 Abstract class BaseCard<T extends DataModel> extends StatefulWidget 可由UI2CODE Plugin分析视觉稿产生透过BaseCard<T extends DataModel>的标准化,指定数据DataModel绑定Card可以发出事件,不直接操控数据,而让接收者响应 ...

June 6, 2019 · 1 min · jiezi

基于ExternalDNS的多集群Ingress-DNS实践

概要External-DNS提供了编程方式管理Kubernetes Ingress资源的DNS的功能,方便用户从Ingress管理DNS解析记录。而在kubernetes federation v2环境中,使用External-DNS可以快速的管理多个联邦集群的Ingress DNS解析,降低用户的操作成本。下面将简单介绍在阿里云容器服务环境中,如何使用External-DNS管理联邦集群的Ingress DNS解析。 联邦集群准备参考阿里云Kubernetes容器服务上体验Federation v2 搭建两个集群组成的联邦集群(配置好kubeconfig,并完成两个集群的join)。 配置RAM信息选择Kubernetes集群节点列表内任意一个Worker节点,打开对应的节点列表信息页面。 找到对应的 RAM 角色,打开RAM控制台,找到对应的角色名称,添加【AliyunDNSFullAccess】权限。 注意:每个集群都需要配置RAM信息。 部署External-DNS配置RBAC 执行下面yaml: apiVersion: v1kind: ServiceAccountmetadata: name: external-dns---apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRolemetadata: name: external-dnsrules:- apiGroups: [""] resources: ["services"] verbs: ["get","watch","list"]- apiGroups: [""] resources: ["pods"] verbs: ["get","watch","list"]- apiGroups: ["extensions"] resources: ["ingresses"] verbs: ["get","watch","list"]- apiGroups: [""] resources: ["nodes"] verbs: ["list"]- apiGroups: ["multiclusterdns.federation.k8s.io"] resources: ["dnsendpoints"] verbs: ["get", "watch", "list"]---apiVersion: rbac.authorization.k8s.io/v1beta1kind: ClusterRoleBindingmetadata: name: external-dns-viewerroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: external-dnssubjects:- kind: ServiceAccount name: external-dns namespace: default部署External-DNS服务 ...

June 6, 2019 · 2 min · jiezi

从开源小白到-Apache-Member我的成长之路

我们走过的每一步路,都会留下印记,越坚实,越清晰。 近日,Apache 软件基金会(ASF)官方 Blog 宣布全球新增 40 位 Apache Member,张乎兴有幸成为其中一位。 目前,全球共有771位 ASF Member,中国仅13位。本文将分享作者从0基础的开源小白,一路走来的感触,希望把期间的经历分享出来,让更多的人看到,世界开源舞台的中国力量。只要有持续的付出,总会有所收获。 初次参与开源2014年,我加入阿里巴巴中间件团队,开始接手集团应用容器的维护工作。当时集团的应用容器绝大部分都是基于 JBoss,老旧且无人维护,另外有一小部分跑在 Jetty 和 Tomcat 之上,当时中间件团队维护了 Tomcat 的一个私有分支,最大的目标就是要统一所有集团的应用容器为 Tomcat。而在那之前,我从未接触过 Tomcat 的开发和运维,对我来说,挑战很大。然而,更大的挑战来自于团队大 leader 提出的,在当时看来几乎是无法实现的目标:成为 Apache Tomcat 的 committer。 要知道,作为 Apache 的核心项目之一,Tomcat 自1999年发布第一版以来,一直是开源届和 Apache 基金会的明星项目,至今仍然在应用容器领域市场占有率保持第一,历经20年发展,依旧热度不减。Tomcat 历经这么多年的发展,却从未出现过一位来自中国的 committer。而我们小团队只有4个人,根本没有任何开源的经验,也完全不知道从何做起。团队 leader 问我们,谁愿意挑战一下的时候,我也没有多想,就承担了下来。 就这样,我开始了自己的 Apache 开源之旅。 说实话,一开始,自己并没有太多的思路,于是开始反复浏览 Tomcat 官网,希望能够查询到一些有用的信息。所有的 Apache 项目几乎都会有新用户指南,介绍如何参与到此开源项目当中,Tomcat也不例外。很快,我从修复 bug 开始,第一个 patch 是修复一个Websocket 测试用例失败的问题,修复该问题涉及到了对代码的一些重构。 还记得当时提交之前我非常谨慎,和 leader 一起反复讨论了很多次,终于形成了一个比较满意的方案。提交给 Tomcat 社区之后,很快,社区便有了响应,并合并了我提交的补丁。第一次提交便获得了认可,心里很开心,紧张的情绪也缓解了。看到 Tomcat 的 release note 里面出现了自己的名字,真的非常开心。 Apache Roadshow China2015年10月,我有幸在北京参加了 Apache Roadshow China 的活动,算是第一次和Apache 基金会有了亲密接触。在大会上,亲眼目睹了时任 Apache 基金会主席的 Brett Porter、Apache 基金会副总裁 Niclas Hedhman 等大牛们的演讲,以及在 Panel Disussion 里面,和他们,以及几位来自中国的 Apache 成员,一起探讨社区领导的开发流程等。 ...

June 3, 2019 · 2 min · jiezi

2019企业IT现状和趋势调研报告707的企业有云原生相关计划

2019年第一季度,灵雀云发起了“企业IT应用现状和云原生技术落地情况”的调研,通过定向邀请,3个月内共收集了400余份有效调研问卷,这些调研问卷80%以上都来自于国内政府、金融、能源、制造、汽车等传统行业的IT从业者。 发起本次调研的初衷,是我们希望了解当前企业,尤其是传统企业目前IT应用开发现状、以及以DevOps、Kubernetes、微服务等为代表的云原生技术在企业的应用情况,从而勾勒出传统行业IT发展趋势,并对于判断国内用户对云原生相关技术的认知度提供一个有价值的参考。 核心要点解读: 1、 约70%的参与调研者所在企业2019年IT预算有上浮; 2、 24.4%的参与调研者表示公司IT系统基本全靠自研,企业开始自建软件研发团队,主导IT应用的研发; 3、 70.7%的参与调研者所在企业表示在2019年有容器、DevOps和微服务方面的规划; 4、 11.4%的参与调研者所在企业已经试点了具有标杆意义的云原生实践,如精英团队的DevOps实践,小范围非核心应用的微服务拆分改造实践等。 本次调研的400多位调研对象中,80%以上来自金融、能源、制造、汽车等传统行业,其中17.3%来自基础架构部门, 22.5%来自运维部门,34.1%来自研发部门,还有约10%的被调研对象为企业的CIO/CTO等高级IT管理者。 被调研企业中,服务器规模在100-500台的比例为26.8%,500-1000台的企业占比22%,1000台服务器以上规模的14.6%。 IT系统自研还是外包 在数字化转型的背景下,传统外包的做法在被逐渐改变。在此次调查中,70.7%的参与调研者表示目前IT系统是自研外包兼而有之,其中核心业务系统以自己开发为主,24.4%的参与调研者表示公司IT系统基本全靠自研,只有4.9%的参与调研者选择了纯外包选项。这表明,企业开始不再将大部分业务系统,尤其是核心业务需求开发外包,开始自建软件研发团队,主导IT应用的研发。只有企业自己主导IT研发,才能够打造IT核心竞争力。 软件能力成为企业的核心竞争力,这恰好是数字化转型的本质之一。何谓成功的数字化转型?灵雀云认为,有三大衡量标志:IT部门由成本中心转为收入中心;企业自己主导IT产品的研发;改进工具、流程、文化来提高交付速度和质量。最终,实现客户满意度的提升、打造差异化竞争优势、加速产品上市。 IT系统更新频率 在IT系统更新频率方面,每月都要更新、升级的比例达到了51.2%的高占比。同时,每3-6个月更新一次的比例达22%。每个传统领域,都受到了来自Fintech金融科技、车联网、物联网、新零售等新技术驱动的创新业务的挑战,传统企业只有借助IT手段才能实现持续发展,在速度和规模上保持竞争力。 IT系统和研发团队TOP 3挑战 本次参与调研的企业以中大型企业为主,其中研发团队规模达到100人以上的比例高达44.3%,20-100人规模的占32.4%。 今天,许多企业都经过了大量IT建设,从分散到集中,造成IT系统越来越复杂,信息孤岛林立,架构臃肿等问题突出。调研中企业IT系统支撑所面临的压力位列前三的挑战分别是:系统复杂性越来越高(65.9%);应用交付压力大,交付速度无法满足业务需求(61.4%);运维管理复杂度提升,IT部门很难构建一支全功能团队(53.7%)。 同时,研发团队所面临的挑战前三甲分别是:部署和运维复杂,运维成本高(74.6%);研发、测试、运维等角色之间相互孤立(62.3%);升级和变更流程复杂,IT服务和应用交付周期长(45.7%)。此外,比较突出的挑战还有,工具链无法完整集成,工具使用困难(32.3%),单体应用过于庞大,迭代效率低下(20.4%)。 上述结果充分表明,面对高度创新、快速变化和充满不确定性的新型业务需求,传统开发模式和IT架构已经成为掣肘。70.7%的参与调研企业表示2019年有容器、DevOps和微服务方面的规划和实施计划。 只有朝着持续交付、敏捷部署、快速迭代,通过敏捷IT赋予业务足够的敏捷,才能够满足不断变化的业务需求,重塑自身的生产力,形成竞争优势,带来更好的用户体验,这最终落到以Kubernetes/容器、DevOps和微服务为核心的云原生技术的落地上。云原生架构和理念与数字化转型一脉相承,帮助企业更加顺畅地实施数字化转型。 业务上云需求最强烈,开源、数字化转型受追捧 在企业最关注的新兴技术趋势方面,云计算占比82.9%,企业将业务上云,提升IT资源效率作为首要关注对象。大数据和人工智能紧随其后,占比分别为73.2%和46.3%。其中开源解决方案在调研对象中的关注程度达到24.4%。 当前开源技术正在进入快速发展阶段,向着企业应用的方方面面深入。开源及开源社区不断将新的工具、方法和最佳实践用于云原生的实际业务用例,解决云原生用户的关键问题。借助许多开源解决方案,云原生部署的复杂性和难度也在得以降低。 此外,数字化转型的关注度为33.6%。如今每位IT从业者言必称数字化转型,IT能力也直接指向助力数字化转型。CIO和其他IT管理者已将企业的数字化计划置于新的高度,希望通过数字化来改变企业的商业和业务模式,数字化业务将从初步试验走向大规模应用。伴随企业数字化业务的不断成熟,预计未来几年,数字化转型将进入爆发阶段。 传统企业2019年IT预算稳中有升 本次调研中,被调研企业今年IT工作的重点包括业务上云(56.1%),云原生、大数据、人工智能等新技术采用(53.7%),打造数字化团队,引领企业的数字化创新(43.9%),选择传统业务应用的比例不足20%。越来越多的企业将工作负载放在云端,将正在开发的应用或服务托管在云平台上,云市场不断增长。 在IT预算方面,比客观经济形势略显乐观,和2018年IT预算相比,接近70%参与调研企业2019年的IT预算略有上浮,其中增长5%以内的企业占比37.5%,增长5-10%的企业占比21.2%,增长10%以上的企业达到12.7%。 此外,调研结果显示,数字化转型是一项需要通盘考虑的工作,需要项目管理部门、技术管理部门、开发部门、运维部门共同参与,制定统一的数字化转型方案和决策并推进。有些参与调研的企业特别强调2018年已经在全公司范围内试点了具有标杆意义的云原生实践,如精英团队的DevOps实践,小范围非核心应用的微服务拆分改造实践等,并且这些都将在2019年进行大范围推广。

June 3, 2019 · 1 min · jiezi

Knative-核心概念介绍BuildServing-和-Eventing-三大核心组件

Knative 主要由 Build、Serving 和 Eventing 三大核心组件构成。Knative 正是依靠这三个核心组件,驱动着 Knative 这艘 Serverless 巨轮前行。下面让我们来分别介绍一下这三个核心组件。 BuildKnative Build 是基于现有的 Kubernetes 能力之上,提供的一套标准化、可移植、可复用的容器镜像构建方式。通过在 Kubernetes 上运行复杂的构建任务,Knative Build 使你不必再单独开发和重复这些镜像构建过程, 从而通过系统化、工程化的方式,减少了镜像构建时间及成本。 Build 通过 Kubernetes 自定义资源定义(CRD)实现。 通过 Build 你可以自定义一个从运行到结束的构建流程。例如,可以使用 Knative Build 来获取、构建和打包代码。Build 具备以下功能: 支持 Source 源挂载,目前支持的 Source 源包括: git 代码仓库任意容器镜像支持通过 BuildTemplate 创建可重复执行构建的模板支持 K8s ServiceAccount 身份验证典型的 Build 示意图: 虽然目前 Knative Build 并不提供完整的独立 CI/CD 解决方案,但它却提供了一个底层的构建模块,用户可单独使用该构建模块在大型系统中实现集成和利用。 ServingKnative 作为 Severless 框架最终是用来提供服务的, 那么 Knative Serving 应运而生。 Knative Serving 构建于 Kubernetes 和 Istio 之上,为  Serverless 应用提供部署和服务支持。其特性如下: ...

May 31, 2019 · 2 min · jiezi

阿里云高级技术专家张毅萍我眼中的边缘计算

导读:边缘计算是目前公认的大方向,越来越多的边缘计算应用将随着5G建设的步伐而兴起。阿里云边缘计算团队的目标是在行业爆发来临之前,完成基础计算资源平台的构建,为产业提供基于体验的计算调度能力,进而助推整个产业快速应用发展。那么在目标背后为之奋斗的工程师是怎样的?他们们是如何一步步助推阿里云边缘计算实现从零到一的启动?这样的团队又需要怎样的人才呢?阿里云高级技术专家张毅萍在本文中给出答案。阿里云高级技术专家张毅萍,在学生时代就属于比较喜欢“折腾”的人,本硕都是计算机专业,也就让他更早踏入IT这个行当。在学校期间,相比日常的课程,他将更多的时间花在课外折腾上,比同龄人更早有了一些技术上的积累。在大三时参加科技创新方面的竞赛获奖并保研,之后因为时间上比较宽裕,大四就踏上了创业这条更“折腾”的路。 在创业期间,他更是把“折腾”特质发挥到极致,利用自身在分布式网络与音视频传输方面的技术经验,做过VoIP、P2P视频,赶上过移动互联网手机APP的第一波浪潮,也经历过从浪尖拍死到沙滩的全过程。一晃神,竟已经在创业这条赛道跑了13年。 在一年多以前,他选择加入阿里云,聚焦在边缘网络的整体架构设计、基于边缘网络的各种业务应用的设计和研发。他经常说,自己非常幸运能搭上边缘计算这班起步并飞速发展的快车,也非常期待为这个新方向的爆发付出自己的努力。每次朋友问起他的创业梦怎么办,他则笑着回答:自己并没有换赛道,边缘计算仍然属于创业,需要每一个看好的人为之付出200%的努力。 张毅萍来到阿里云边缘计算团队的一年多时间内,这个团队从0到1完成了边缘节点服务(ENS)产品的商业化,为客户实现边缘计算业务规模化提供了坚实的基础,引领了产业良性发展。同时,团队也积极推进行业标准化,去年12月份联合中国电子技术标准化研究院发布《边缘云计算技术及标准化白皮书》,在业界首次对边缘云计算下了定义。 边缘云计算提供的基础能力,主要包括网络和资源覆盖,以及匹配场景需求的计算、存储、安全、网络、稳定性、性能、成本优化等能力。其中很多方案是基于中心云的虚拟化方案进行改造的,比如计算虚拟化、块存储、VPC网络等,当然方案改造也有非常大的挑战,因为飞天系统的很多方案是基于规模化设计。有些则是新场景下的新方案,比如边缘回云的链路优化,资源调度等。 张毅萍所在团队的脚步不止于此,在边缘云计算的基础能力之上,他们要考虑的是服务形态问题,即用什么样的方式提供边缘计算的能力,新的挑战随之而来。 ENS产品1.0版本是一个IaaS的服务形态,依托独立的小规模边缘节点服务时,面对更广泛的客户需求,服务在资源弹性和稳定性等方面需要所有突破。同时,用户拿到一批边缘虚机实例后,需要考虑如何部署/升级应用,如何运维主机,如何监控等问题。另外,随着当前互联网架构的快速迭代,针对边缘计算场景,是否可以有一个先进的业务架构可以参考,是否有一组标准的API和基础中间件让用户快速构建自己的边缘业务架构呢? 张毅萍团队认为不能让这些问题变成用户享受边缘计算红利的门槛,所以,在年初ENS推出2.0版本,基于全局边缘资源的调度支持更好的资源弹性,应用镜像快速分发/升级能力以及配套的DevOps能力,并与阿里云容器团队合作,在边缘节点实现容器K8S+Docker生态的DevOps能力,通过云-边多区域统一的容器托管服务,赋能用户快速将业务下沉至边缘,缩短业务响应时间。 在未来,团队也会做持续的技术投入,勇作行业探路者。同时,张毅萍所在的阿里云边缘计算团队也期待更多有志之士加入,一起迎接更多未知和挑战。 谈到技术型公司的人才要求,张毅萍说:在互联网加速发展的阶段,我认为对于任何一个技术和非技术同学来说,最重要的是不要怕做错事情,很多事情技术上没有前人的经验做参考、商业上没有可复制的模式、现实情况下也没有太多的确定因素作为判断依据,那么就需要大胆假设,并为这个假设付出努力去证明它,哪怕证明假设的结果是错误的。过于担心自己付出的努力得不到好的回报,除了给自己制造心理失衡和焦虑,没有其它任何作用。 赶拼的小伙伴们,快加入我们吧,这里永远不会缺少挑战! 本文作者:山哥在这里阅读原文 本文为云栖社区原创内容,未经允许不得转载。

May 30, 2019 · 1 min · jiezi

容器监控实践Cortex

一.概述cortex:一个支持多租户、水平扩展的prometheus服务。 当时调研cortex其实是因为看到了Weave Cloud这个商业产品中的监控模块介绍,weave也叫weave works,官方地址是:https://cloud.weave.works,是一个专注于容器微服务的paas平台。 WeaveCloud在监控模块最大化利用了Prometheus,并在其基础上添加了很多组件,实现了多租户管理、高可用的监控集群。其使用的核心监控组件就是cortex。 本文主要分享的是cortex的运行机制,关于Weave Cloud的产品定位和功能可以看下后续的文章:[商业方案-weave work]() Cortex是一个CNCF的沙盒项目,目前被几个线上产品使用:Weave Cloud、GrafanaCloud和FreshTracks.io 为什么不直接运行Prometheus,而用Cortex? ps:来自cortex kubecon大会演讲 作为服务,cortex提供了鉴权和访问控制数据永久保留,状态能够被管理提供持久化、高可用、伸缩性提供更好的查询效率,尤其是长查询二.主要功能针对以上需求,Cortex提供的主要功能或特色如下: 支持多租户:Prometheus本身没有的租户概念。这意味着,它无法对特定于租户的数据访问和资源使用配额,提供任何形式的细粒度控制。Cortex可以从多个独立的prometheus实例中获取数据,并按照租户管理。长期存储:基于远程写入机制,支持四种开箱即用的长期存储系统:AWS DynamoDB、AWS S3、Apache Cassandra和Google Cloud Bigtable。全局视图:提供所有prometheus server 整合后的时间序列数据的单一,一致的“全局”视图。高可用:提供服务实例的水平扩展、联邦集群等最大化利用了Prometheus相似的竞品: Prometheus + InfluxDB:使用InfluxDataPrometheus + Thanos:长期存储、全局视图Timbala:多副本、全局视图,作者是Matt BostockM3DB:自动扩缩容,来自uber产品形态ps:来自weave work上试用监控模块时的截图 1.安装监控的agent: 2.概览视图 3.资源监控面板 4.监控详情页面 5.添加监控 6.配置报警 在k8s集群中部署所需要的yaml列表为: [https://github.com/weaveworks...](https://github.com/weaveworks...) 部署的agent时的脚本内容是: #!/bin/shset -e# Create a temporary file for the bootstrap binaryTMPFILE="$(mktemp -qt weave_bootstrap.XXXXXXXXXX)" || exit 1finish(){ # Send only when this script errors out # Filter out the bootstrap errors if [ $? -ne 111 ] && [ $? -ne 0 ]; then curl -s >/dev/null 2>/dev/null -H "Accept: application/json" -H "Authorization: Bearer $token" -X POST -d \ '{"type": "onboarding_failed", "messages": {"browser": { "type": "onboarding_failed", "text": "Installation of Weave Cloud agents did not finish."}}}' \ https://cloud.weave.works/api/notification/external/events || true fi # Arrange for the bootstrap binary to be deleted rm -f "$TMPFILE"}# Call finish function on exittrap finish EXIT# Parse command-line argumentsfor arg in "$@"; do case $arg in --token=*) token=$(echo $arg | cut -d '=' -f 2) ;; esacdoneif [ -z "$token" ]; then echo "error: please specify the instance token with --token=<TOKEN>" exit 1fi# Notify installation has startedcurl -s >/dev/null 2>/dev/null -H "Accept: application/json" -H "Authorization: Bearer $token" -X POST -d \ '{"type": "onboarding_started", "messages": {"browser": { "type": "onboarding_started", "text": "Installation of Weave Cloud agents has started"}}}' \ https://cloud.weave.works/api/notification/external/events || true# Get distributionunamestr=$(uname)if [ "$unamestr" = 'Darwin' ]; then dist='darwin'elif [ "$unamestr" = 'Linux' ]; then dist='linux'else echo "This OS is not supported" exit 1fi# Download the bootstrap binaryecho "Downloading the Weave Cloud installer... "curl -Ls "https://get.weave.works/bootstrap?dist=$dist" >> "$TMPFILE"# Make the bootstrap binary executablechmod +x "$TMPFILE"# Execute the bootstrap binary"$TMPFILE" "--scheme=https" "--wc.launcher=get.weave.works" "--wc.hostname=cloud.weave.works" "--report-errors" "$@"三.实现原理Cortex与Prometheus的交互图: ...

May 27, 2019 · 2 min · jiezi

容器监控实践Dockbix

一.概述Dockbix意为docker+zabbix,即使用zabbix来监控docker容器的插件或者模块,既然有专业的cadvisor、prometheus等容器监控方案,为什么还要用传统的zabbix呢? 在docker刚出现时,还没有专业的容器监控方案公司已有zabbix的成熟实践,想直接集成到zabbix中(虽然不太优雅)使用zabbix来监控docker有几种方案,比如: 自己写agent,利用docker的api获取stats信息,暴露api接口给zabbix采集使用zabbix的Module,将docker的采集展示集成到现有的zabbix系统中如何使用写APIpython sdk:https://docker-py.readthedocs.io/en/stable/containers.html#docker.models.containers.Container.stats stats(**kwargs)Stream statistics for this container. Similar to the docker stats command.Parameters: decode (bool) – If set to true, stream will be decoded into dicts on the fly. Only applicable if stream is True. False by default.stream (bool) – If set to false, only the current stats will be returned instead of a stream. True by default.Raises: docker.errors.APIError – If the server returns an error.如计算cpu: ...

May 27, 2019 · 1 min · jiezi

Kubernetes网络分析之Flannel

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=notifyEnvironmentFile=/etc/sysconfig/flanneldExecStart=/usr/bin/flanneld-start $FLANNEL_OPTIONSExecStartPost=/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=notifyNotifyAccess=allEnvironmentFile=-/run/flannel/dockerEnvironmentFile=-/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.110.254.50.0/24 via 10.254.50.0 onlink10.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.110.254.50.0 lladdr ba:10:0e:7b:74:89 PERMANENT10.254.60.0 lladdr 92:f3:c8:b2:6e:f0 PERMANENT可以看到这个网关对应的mac地址,这样内层的数据包就封装好了 ...

May 27, 2019 · 2 min · jiezi

浅谈Cgroups

奇技指南在开发一款软件时,为了延长软件的生命周期,需要一款配套软件来对发布的软件进行监控。随着容器技术的成熟,系统的定制和软件的打包变得越来越容易,同时,对容器进行监控成为了容器使用者所必备的技能。下来,作者将带领大家认识一下容器的资源管理工具Cgroups。说起容器监控,首先会想到通过Cadvisor, Docker stats等多种方式获取容器的监控数据,并同时会想到容器通过Cgroups实现对容器中的资源进行限制。但是这些数据来自哪里,并且如何计算的?答案是Cgroups。最近在写docker容器监控组件,在深入Cadvisor和Docker stats源码发现数据都来源于Cgroups。了解之余,并对Cgroups做下笔记。 01、Cgroups介绍Cgroups 是 control groups 的缩写,是Linux内核提供的一种可以限制,记录,隔离进程组(process groups)所使用物理资源的机制。最初有google工程师提出,后来被整合进Linux的内核。因此,Cgroups为容器实现虚拟化提供了基本保证,是构建Docker,LXC等一系列虚拟化管理工具的基石。 02、Cgroups作用资源限制(Resource limiting):Cgroups可以对进程组使用的资源总额进行限制。如对特定的进程进行内存使用上限限制,当超出上限时,会触发OOM。优先级分配(Prioritization): 通过分配的CPU时间片数量及硬盘IO带宽大小,实际上就相当于控制了进程运行的优先级。资源统计(Accounting): Cgroups可以统计系统的资源使用量,如CPU使用时长、内存用量等等,这个功能非常适用于计费。进程控制(ControlCgroups): 可以对进程组执行挂起、恢复等操作。03、Cgroups 组成Cgroups主要由task,cgroup,subsystem及hierarchy构成。下面分别介绍下各自的概念。 task: 在Cgroups中,task就是系统的一个进程。cgroup: Cgroups中的资源控制都以cgroup为单位实现的。cgroup表示按照某种资源控制标准划分而成的任务组,包含一个或多个子系统。一个任务可以加入某个cgroup,也可以从某个cgroup迁移到另外一个cgroup。subsystem: Cgroups中的subsystem就是一个资源调度控制器(Resource Controller)。比如CPU子系统可以控制CPU时间分配,内存子系统可以限制cgroup内存使用量。hierarchy: hierarchy由一系列cgroup以一个树状结构排列而成,每个hierarchy通过绑定对应的subsystem进行资源调度。hierarchy中的cgroup节点可以包含零或多个子节点,子节点继承父节点的属性。整个系统可以有多个hierarchy。组件之间的关系Subsystems, Hierarchies,Control Group和Tasks之间有许多的规则,下面介绍下:1、同一个hierarchy能够附加一个或多个subsystem。如下图,将cpu和memory subsystems(或者任意多个subsystems)附加到同一个hierarchy。 2、一个subsystem只能附加到一个hierarchy上。如下图,cpu subsystem已经附加到了hierarchy A,并且memory subsystem已经附加到了hierarchy B。因此cpusubsystem不能在附加到hierarchy B。 3、系统每次新建一个hierarchy时,该系统上的所有task默认构成了这个新建的hierarchy的初始化cgroup,这个cgroup也称为root cgroup。对于你创建的每个hierarchy,task只能存在于其中一个cgroup中,即一个task不能存在于同一个hierarchy的不同cgroup中,但是一个task可以存在在不同hierarchy中的多个cgroup中。如果操作时把一个task添加到同一个hierarchy中的另一个cgroup中,则会从第一个cgroup中移除。如下图,cpu和memory被附加到cpu_mem_cg的hierarchy。而net_cls被附加到net_cls hierarchy。并且httpd进程被同时加到了cpu_mem_cg hierarchy的cg1 cgroup中和net hierarchy的cg3 cgroup中。并通过两个hierarchy的subsystem分别对httpd进程进行cpu,memory及网络带宽的限制。 4、系统中的任何一个task(Linux中的进程)fork自己创建一个子task(子进程)时,子task会自动的继承父task cgroup的关系,在同一个cgroup中,但是子task可以根据需要移到其它不同的cgroup中。父子task之间是相互独立不依赖的。如下图,httpd进程在cpu_and_mem hierarchy的/cg1 cgroup中并把PID 4537写到该cgroup的tasks中。之后httpd(PID=4537)进程fork一个子进程httpd(PID=4840)与其父进程在同一个hierarchy的统一个cgroup中,但是由于父task和子task之间的关系独立不依赖的,所以子task可以移到其它的cgroup中。 04、Cgroups使用我们直接使用shell 命令直接操作hierarchy并设置cgroup参数。在centos6上也可以直接使用libcgroup提供的工具可简化对cgroup的使用。 Create a Hierarchy使用shell命令创建hierarchy并附加subsystems到该hierarchy上。 作为root为hierarchy创建一个mount point。并且在mount point中包含cgrou的名字。 例如: 接下来使用mount命令去挂载hierarchy并附加一个或多个subsystem到该hierarchy上。 例如: 如果想在已有的hierarchy上attch或detach subsystem,可以使用remount操作,例如我们想detach掉memory subsystem。 ...

May 24, 2019 · 1 min · jiezi

从HelloWorld看Knative-Serving代码实现

摘要: Knative Serving以Kubernetes和Istio为基础,支持无服务器应用程序和函数的部署并提供服务。我们从部署一个HelloWorld示例入手来分析Knative Serving的代码细节。概念先知官方给出的这几个资源的关系图还是比较清晰的: 1.Service: 自动管理工作负载整个生命周期。负责创建route,configuration以及每个service更新的revision。通过Service可以指定路由流量使用最新的revision,还是固定的revision。2.Route:负责映射网络端点到一个或多个revision。可以通过多种方式管理流量。包括灰度流量和重命名路由。3.Configuration:负责保持deployment的期望状态,提供了代码和配置之间清晰的分离,并遵循应用开发的12要素。修改一次Configuration产生一个revision。4.Revision:Revision资源是对工作负载进行的每个修改的代码和配置的时间点快照。Revision是不可变对象,可以长期保留。 看一个简单的示例我们开始运行官方hello-world示例,看看会发生什么事情: apiVersion: serving.knative.dev/v1alpha1kind: Servicemetadata: name: helloworld-go namespace: defaultspec: runLatest: // RunLatest defines a simple Service. It will automatically configure a route that keeps the latest ready revision from the supplied configuration running. configuration: revisionTemplate: spec: container: image: registry.cn-shanghai.aliyuncs.com/larus/helloworld-go env: - name: TARGET value: "Go Sample v1"查看 knative-ingressgateway: kubectl get svc knative-ingressgateway -n istio-system 查看服务访问:DOMAIN kubectl get ksvc helloworld-go --output=custom-columns=NAME:.metadata.name,DOMAIN:.status.domain 这里直接使用cluster ip即可访问 ...

May 22, 2019 · 4 min · jiezi

利用Packer自定义镜像创建容器集群

阿里云容器服务Kubernetes集群支持CentOS操作系统,在绝大多数情况下可以满足客户的要求。但是有些客户由于业务系统对操作系统依赖比较高,希望定制化一些操作系统参数,则可以用自定义镜像来创建Kubernetes集群。 创建自定义操作系统镜像有两种方式,一是在控制台上通过为一台ECS创建快照的方式创建镜像,注意一定要基于阿里云CentOS作为基础镜像,把对操作系统的定制化更新完打成镜像即可。但这种方式的不便之处在于,如果每次对操作系统镜像有更新,则都要手动操作一遍,很难自动化。而且如果是从已有的Kubernetes节点制作镜像,还需要把Docker,Kubelet等清理干净才能制作镜像,步骤繁琐且容易遗漏。 另外一种方式就是本文介绍的用Packer构建镜像。相关的参考文档:使用Packer创建自定义镜像。采用Packer构建镜像的好处是可以把构建方式自动化,构建所需的参数文件中包含了对干净的基础镜像所做的修改,一目了然,并且可以把配置进行版本化管理。后期需要构建新的镜像,只需改变配置重新执行一下Packer构建即可,非常方便,是在生产环境中使用自定义镜像的推荐方式。 那么有没有一个针对容器服务集群的Packer配置模版呢?容器服务团队开源的ack-image-builder就是一个这样的示例项目。下面我们就来一起动手实践一下。 安装Packer可以根据官方文档安装Packer https://www.packer.io/intro/getting-started/install.html 。 创建自定义镜像克隆ack-image-builder项目到本地,可以看到config和scripts目录下是一些示例定制化脚本,读者可以根据自己的需求更新改。 $ git clone https://github.com/AliyunContainerService/ack-image-builder.git$ cd ack-image-builderack-image-builder $ tree.├── LICENSE.txt├── README.md├── ack-centos.json├── config│   └── default.sh└── scripts ├── cleanUpKerneles.sh ├── reboot.sh ├── updateKernel.sh └── verify.sh2 directories, 8 files在ack-centos.json 可以配置在把生成好的自定义镜像存在哪个区(示例中为cn-hangzhou)。 { "variables": { "region": "cn-hangzhou", "image_name": "ack_test_image{{timestamp}}", "source_image": "centos_7_06_64_20G_alibase_20190218.vhd", ... },配置好阿里云账号的AK,然后执行构建命令。 export ALICLOUD_ACCESS_KEY=XXXexport ALICLOUD_SECRET_KEY=XXXpacker build ack-centos.json大约7-8分钟一个新的自定义镜像就构建成功了。可以进入ECS控制台查看新生成的镜像。 利用自定义镜像创建容器集群开通自定义镜像白名单 读者如果需要尝试自定义镜像能力,需要先开工单,申请在容器服务控制台上开通自定义镜像的白名单。 创建容器集群 白名单开通后进入容器服务控制台 https://cs.console.aliyun.com/#/k8s/cluster/list,创建Kubernetes集群。选择自定义镜像所在的区,在示例中是cn-hangzhou。 在创建集群的页面中点击"显示高级选项",会出现"自定义镜像"的选择界面: 如果在选择中找不到刚创建的镜像,请检查一下集群和自定义镜像是否在同一个Region。 选择了自定义镜像后点击创建集群即可完成一个自定义镜像集群的创建。 集群扩容与自动伸缩 使用自定义镜像创建集群后,集群的扩容与自动伸缩中所用的都是自定义镜像。 Terraform 中自定义镜像支持利用Terraform创建容器集群也可以使用自定义镜像,具体参数是: image_id - The ID of node image.相关链接如下: ...

May 16, 2019 · 1 min · jiezi

Knative-Eventing-中-Channel-如何注入默认-Provisioner

摘要: 在 Knative Eventing 中创建 Broker 时,如果不指定 provisioner, 系统会自动创建默认的 provisioner, 那么这个机制是如何实现的呢? 本文基于 Knative Eventing 0.5 版本,介绍了这个实现机制。场景通常的在创建Broker时,我们需要通过 spec.ChannelTemplate 指定使用某个具体的 Channel Provisioner。例如这样的Broker: apiVersion: eventing.knative.dev/v1alpha1kind: Brokermetadata: name: pubsub-channelspec: channelTemplate: provisioner: apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterChannelProvisioner name: gcp-pubsub这里通过spec.ChannelTemplate 指定了名称为gcp-pubsub的provisioner。那么我们也遇到过这样的Broker: apiVersion: eventing.knative.dev/v1alpha1kind: Brokermetadata: name: default并没有指定使用某个具体的 channel, 但创建完Broker之后会发现已经创建出来了Channel: apiVersion: eventing.knative.dev/v1alpha1kind: Channelmetadata: ... name: default-broker-8ml79 namespace: default ownerReferences: - apiVersion: eventing.knative.dev/v1alpha1 blockOwnerDeletion: true controller: true kind: Broker name: default uid: 2e4c3332-6755-11e9-a81f-00163f005e02spec: provisioner: apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterChannelProvisioner name: in-memory...分析我们知道 Broker创建之后,会通过 reconcile controller 会创建相应的Channel, 也就是下面这段代码: ...

May 13, 2019 · 2 min · jiezi

阿里云Kubernetes服务上从零搭建GitLabJenkinsGitOps应用发布模型的实践全纪录

关于GitOps的介绍,可以参考 GitOps:Kubernetes多集群环境下的高效CICD实践 1. 在 容器服务控制台 创建kubernetes集群1.1 新建Kubernetes集群: 1.2 新建命名空间gitops 我们将会把gitlab和jenkins全部部署到此命名空间下 2. 创建GitLab应用 (可选项,可以对接已有GitLab环境)容器服务控制台上依次点击 市场 -> 应用目录 -> gitlab-ce : 在 参数 中设置externalUrl和gitlabRootPassword后选择gitops命名空间并创建应用,本次实践中 externalUrl 设置为 http://ls-gitlab.example.com/, 如果没有dns解析的话,可以在创建成功后直接使用ip 容器服务控制台上依次点击 路由与负载均衡 -> 服务 查看gitlab应用的访问地址,大约2分钟后可访问gitlab并登陆: 3. 设置GitLab并上传示例源码项目3.1 新建private group application 创建private group application: 3.2 新建并上传private project application-demo 创建private project application-demo, 示例源码地址: https://code.aliyun.com/haoshuwei/application-demo.git 从master新建一个分支latest: 设置master和latest分支只有管理员才能merge和push代码的操作: 3.3 新建private group builds 3.4 新建并上传private project preview-pipeline staging-pipeline production-pipeline preview-pipeline示例源码地址为: https://code.aliyun.com/haoshuwei/preview-pipeline.gitstaging-pipeline示例源码地址为: https://code.aliyun.com/haoshuwei/staging-pipeline.gitproduction-pipeline示例源码地址为: https://code.aliyun.com/haoshuwei/production-pipeline.git上传3个构建项目之前需要替换以下字段:IMAGE_REPO:  应用容器镜像要上传到哪个镜像仓库,镜像仓库地址dingTalkToken: 钉钉通知所使用的钉钉机器人accessTokenFetch Git Repo -> credentialsId : 用于Jenkins拉取git项目的证书名称,需要在Jenkins中创建名为gitlab的证书Fetch Git Repo -> url : Jenkins拉取git repo的url ...

May 9, 2019 · 2 min · jiezi

3类6种主流容器操作系统全比较

5月8日 晚20:30,Kubernetes Master Class在线培训第五期《Kubernetes中的日志、监控与告警》即将开播,点击链接:http://live.vhall.com/317569561 即可免费预约注册! 介 绍 容器已迅速成为现代数据中心的必要组成部分。容器可以构建在各类操作系统中,那么企业该如何选择最合适的操作系统来运行自己的容器? 在容器部署时,研发的负责人需要知道操作系统的哪些特性和功能对于正在发布的应用程序至关重要,以及是否存在需要额外考虑的其他因素(如可管理性和配置灵活性),不同企业的情况与需求不同,选择自然也不尽相同。 不同的操作系统,如何在特性和基本功能方面进行比较?这些差异如何影响它们支持应用程序的方式?这些都是我们必须考量的重要问题。本文中我们将比较三类具有代表性的操作系统: 传统的全功能操作系统通用的精简操作系统专用于容器的操作系统在每个类别中,我们都会选择两个代表性的产品,这些产品能代表这一大类中的其他全产品或发行版。 通过本文,大家将能更清楚地了解不同操作系统类型之间的差异。IT负责人们也将更好地了解到,为什么开发人员可以为容器化应用程序选择一个操作系统而不是另一个,以及为什么他们可能支持或质疑这些选择。 全功能操作系统 “全功能操作系统”是什么意思?为什么在容器部署的情况下,完整功能会很重要?本节将介绍为什么在传统服务器部署中使用的操作系统,也可能是为容器平台选择操作系统时的最佳答案。 首先要知道的是,这类操作系统的功能无疑是最齐全的。如果某个应用程序需要某个特定的特性或功能,全功能操作系统或许都能满足它。不过这种“齐全”也是有一定代价的:在存储、内存和CPU资源方面,这类操作系统对系统的要求最高。同时,这些功能还会增加操作系统的攻击面,为潜在的攻击者提供更多的角落和缝隙进行攻击。不论是价格成本还是安全风险,如果操作系统的这些功能都是应用程序需要的,那么这些成本也就很容易承担了;但如果只需要少量功能,相较之下性价比就略低。 这些功能齐全的操作系统,最适合的用例,是企业需要在单个OS实例之上的容器中部署多个不同应用程序。在这些情况下,操作系统的功能的多而全,可能是支持应用程序队列的最经济的方式。 Ubuntu Ubuntu已经成为许多企业在服务器、云甚至桌面上的默认操作系统。Canonical公司为Ubuntu提供了非常好的支持,Ubuntu提供各种可下载格式,包括支持物联网、容器、服务器或云的部署所需的实用程序包、shell、功能和功能集。 如今,Ubuntu已经开始瓜分曾经由Red Hat Linux一家独大的领域:Ubuntu一贯良好的声誉和企业级的支持,使得对于企业部署而言,它成为了一个稳妥的、“合理的”选择。不过,大家仍需记住的、很重要的一点是,不论在什么情况下,“合理的”都不意味着一定是“最好的”——它的功能齐全也意味着它的庞大,企业需要思考这种重与大是否适合自己。 CentOS 在全功能操作系统这一大类中,Ubuntu已经成为企业的一大主流选择,除此之外,CentOS则是另一个流行的、由社区驱动的开源操作系统,它是由Red Hat Enterprise Linux依照开放源代码规定释出的源代码所编译而成。 CentOS强调社区对特性和功能的贡献及支持,同时仍然建立在其Red Hat基础的稳定性上。开源不意味着CentOS不被大型组织使用——美国国家实验室和几大主流云提供商的服务器上都用着CentOS。但是Ubuntu自称拥有比CentOS更快速的更新,其中包括那些更老、但经过良好测试的软件包。 精简的操作系统 容器,尽可能地将少而精的功能汇集在一起,创建完整的应用程序。那么,这类精简的操作系统缺少“完整”Linux发行版中的哪些功能——这对您的应用程序是否重要?从另一视角来看,将应用程序部署在那些将功能剥离到极限的精简操作系统上,优势是什么? 问题的答案在于,你的应用程序究竟对操作系统有什么要求,而精简的操作系统是否能满足这些基础要求。如果没有周全的准备,在使用过程中还需要人为添加应用程序所需的各种功能和小程序,那选择这类精简操作系统可以说是失败的,因为它在大小和简单性方面的优势所剩无几。 本章节将介绍两个发行版,BusyBox和Alpine Linux,以及它们在适当的环境中可以带来的优势。这两个操作系统是相关的——Alpine是基于BusyBox的,但是二者也存在一些关键的差异,使用户会在这二者之间做出不同选择。这些差异不仅涉及具体功能及特性,还涉及支持社区和生态系统。 BusyBox BusyBox很适合容器部署,恰巧因为它在设计时没有刻意考虑容器。BusyBox被其开发人员称为“嵌入式Linux的瑞士军刀”,它作为一个单一的小型可执行文件,包含大多数嵌入式应用程序所需的所有功能。这也“迫使”它在容器技术出现之前,就可以开始采用类似容器的方法进行部署了。 BusyBox可以使用Linux或其他POSIX操作系统作为其基础进行部署,并将它们与许多常见的Linux实用程序捆绑在一起。如此一来,它成为了一个紧凑的单文件可执行文件,其中包含“完整”Linux发行版的许多功能——尽管这些完整版本中的不少其他功能选项,都以节省空间的名义,被从BusyBox中删除了。 Alpine Linux 如前所述,Alpine Linux基于BusyBox,但不论是目标还是细节,它都建立在更早期的Linux发行版上。BusyBox是因为是单一可执行文件而体积很小, 而Alpine Linux则是使用强化的内核,为其前身BusyBox的紧凑、简单的目标增加安全性。 相较于BusyBox,Alpine Linux能让开发人员更容易添加功能。它的发行版基于BusyBox和musl库之上,因此在添加功能的方便性或结构紧凑度的这些维度上,Alpine Linux一枝独秀。 Alpine Linux这是一个极限精简的操作系统,能够生成非常小的容器镜像以进行部署,而且加固的内核使其更适用于生产以及开发、部署。 容器操作系统 容器操作系统开箱即用,拥有内置的自动化和容器编排工具。它们被设计和构建为“主机”操作系统——托管Alpine和BusyBox等容器操作系统的操作系统。既然如此,为什么它们不是每个容器部署的自动选择呢? 容器操作系统的特征在于,它不仅仅是一个支持容器的软件,而是使用容器技术部署的软件。“容器一直向下”的体系结构意味部署的自定义程序更高、更灵活,从而比传统的OS部署复杂得多。另一方面,对于早期转向容器的组织,或者对于那些不一定适合容器架构的应用程序部署中,“全容器”架构并非那么容易。 对于那些寻找容器操作系统的企业而言,Rancher OS和Container Linux是两个主流的选择。本章节将介绍它们各自的优势,帮助开发人员根据自身情况做进一步选择。 RancherOS RancherOS中的每个进程都在Docker管理的单独容器中运行。对Docker的优化和依赖让RancherOS可以做到体积极小、启动极快。 除了基本的性能优势之外,RancherOS系统服务由Docker Compose定义和配置。这种依赖意味着只加载和部署应用程序所需的服务,从而进一步加速和简化部署。通过与cloud-init集成,再次简化了部署,从而实现了广泛和高速的自动配置和部署。 Container Linux CoreOS的Container Linux专为基于云的容器部署而设计。Container Linux已被Red Hat收购,针对公有云或私有云基础架构的集群部署进行了优化。 Container Linux和内核以及必要的实用程序一起,部署在单个可执行文件中,其他的实用程序和功能都部署在容器中。 Container Linux长期以来一直被广泛使用,可以在大多数公有云上部署。被Red Hat收购并没有减缓它的采用速度。Container Linux与开源许可一起分发,并拥有一个活跃的开发人员社区。 ...

April 29, 2019 · 1 min · jiezi

程序员笔记如何编写优雅的Dockerfile

导读Kubernetes要从容器化开始,而容器又需要从Dockerfile开始,本文将介绍如何写出一个优雅的Dockerfile文件。 文章主要内容包括: Docker容器Dockerfile使用多阶构建感谢公司提供大量机器资源及时间让我们可以实践,感谢在此专题上不断实践的部分项目及人员的支持。 一、Docker容器1.1 容器的特点我们都知道容器就是一个标准的软件单元,它有以下特点: 随处运行:容器可以将代码与配置文件和相关依赖库进行打包,从而确保在任何环境下的运行都是一致的。高资源利用率:容器提供进程级的隔离,因此可以更加精细地设置CPU和内存的使用率,进而更好地利用服务器的计算资源。快速扩展:每个容器都可作为单独的进程予以运行,并且可以共享底层操作系统的系统资源,这样一来可以加快容器的启动和停止效率。1.2 Docker容器目前市面上的主流容器引擎有Docker、Rocket/rkt、OpenVZ/Odin等等,而独霸一方的容器引擎就是使用最多的Docker容器引擎。 Docker容器是与系统其他部分隔离开的一系列进程,运行这些进程所需的所有文件都由另一个镜像提供,从开发到测试再到生产的整个过程中,Linux 容器都具有可移植性和一致性。相对于依赖重复传统测试环境的开发渠道,容器的运行速度要快得多,并且支持在多种主流云平台(PaaS)和本地系统上部署。Docker容器很好地解决了“开发环境能正常跑,一上线就各种崩”的尴尬。 Docker容器的特点: 轻量:容器是进程级的资源隔离,而虚拟机是操作系统级的资源隔离,所以Docker容器相对于虚拟机来说可以节省更多的资源开销,因为Docker容器不再需要GuestOS这一层操作系统了。快速:容器的启动和创建无需启动GuestOS,可以实现秒级甚至毫秒级的启动。可移植性:Docker容器技术是将应用及所依赖的库和运行时的环境技术改造包成容器镜像,可以在不同的平台运行。自动化:容器生态中的容器编排工作(如:Kubernetes)可帮助我们实现容器的自动化管理。二、DockerfileDockerfile是用来描述文件的构成的文本文档,其中包含了用户可以在使用行调用以组合Image的所有命令,用户还可以使用Docker build实现连续执行多个命令指今行的自动构建。 通过编写Dockerfile生磁镜像,可以为开发、测试团队提供基本一致的环境,从而提升开发、测试团队的效率,不用再为环境不统一而发愁,同时运维也能更加方便地管理我们的镜像。 Dockerfile的语法非常简单,常用的只有11个: 2.1 编写优雅地Dockerfile编写优雅的Dockerfile主要需要注意以下几点: Dockerfile文件不宜过长,层级越多最终制作出来的镜像也就越大。构建出来的镜像不要包含不需要的内容,如日志、安装临时文件等。尽量使用运行时的基础镜像,不需要将构建时的过程也放到运行时的Dockerfile里。只要记住以上三点就能写出不错的Dockerfile。 为了方便大家了解,我们用两个Dockerfile实例进行简单的对比: FROM ubuntu:16.04RUN apt-get updateRUN apt-get install -y apt-utils libjpeg-dev \ python-pipRUN pip install --upgrade pipRUN easy_install -U setuptoolsRUN apt-get cleanFROM ubuntu:16.04RUN apt-get update && apt-get install -y apt-utils \ libjpeg-dev python-pip \ && pip install --upgrade pip \ && easy_install -U setuptools \ && apt-get clean我们看第一个Dockerfile,乍一看条理清晰,结构合理,似乎还不错。再看第二个Dockerfile,紧凑,不易阅读,为什么要这么写? ...

April 28, 2019 · 2 min · jiezi

云原生的新思考为什么容器已经无处不在了

4月24日,中国信息通信研究院主办的首届云原生产业大会在北京举行,在《云原生数字引领未来》的主题演讲中,阿里云容器服务总监易立表示:“云原生不但可以很好的支持互联网应用,也在深刻影响着新的计算架构、新的智能数据应用。以容器、服务网格、微服务、Serverless为代表的云原生技术,带来一种全新的方式来构建应用。”本文根据易立演讲内容整理而成。 拥抱云原生技术,解耦系统复杂度如今,大多数企业开始全面拥抱云计算,在All-in-Cloud全面到来的时代,三个重要转变:基础设施的云化、核心技术的互联网化、业务的数据化和智能化。在各行各业中,都有很多业务应用从诞生之初就生长在云端,各个企业也因此越来越像互联网公司,而技术能力被视为不可或缺的核心竞争力。在2019阿里云峰会·北京站上,阿里云智能总裁张建锋在谈及‘核心技术的互联网化’时,也提到了大力投资云原生。 为什么要拥抱云原生?一方面,云计算已经重塑了软件的整个生命周期,从架构设计到开发,再到构建、交付和运维等所有环节;另一方面,企业IT架构也随之发生巨大变化,而业务又深度依赖IT能力。这带来了一定程度的复杂性和挑战性。 正如人类社会发展伴随着技术革命与社会大分工一样,云原生技术的出现解耦了很多复杂性,这是IT技术的进步。 首先,Docker实现了应用与运行环境的解耦,众多业务应用负载都可以被容器化,而且应用容器化满足了敏捷、可迁移、标准化的诉求;其次,Kubernetes的出现让资源编排调度与底层基础设施解耦,应用和资源的管控也开始得心应手,容器编排实现资源编排、高效调度;随后,Istio为代表的服务网格技术解耦了服务实现与服务治理能力。此外,阿里云还提供了Open API、SDK等丰富的开发工具,实现第三方被集成,为云的生态伙伴提供广阔的可能性。这样的技术分层推动了社会分工,极大促进了技术和业务创新。 在阿里云看来,云原生首先可以支持互联网规模应用,可以更加快速地创新、和低成本试错;其次,屏蔽了底层基础架构的差异和复杂性;同时,服务网格、无服务计算等新的计算范型的不断涌现,给整体IT架构能力带来了极致弹性,从而更好地服务于业务。用户可以基于阿里云容器服务构建面向领域的云原生框架,如面向机器学习的Kubeflow,和面向无服务器的Knative等等。 方兴未艾,容器应用的新思考容器已经无处不在了, 作为容器服务的提供者,我们认为容器技术会继续发展,被应用于“新的计算形态”,“新的应用负载”和“新的物理边界”,在此将相关观察和新思考分享给大家。 1 新的计算形态:云原生的Serverless Runtime已来 云原生技术理念,是使企业用户及开发者只关注应用开发,无需关注基础设施及基础服务。与之相似的Serverless计算,将应用服务资源化并以API接口的方式提供出来,使用者只需从客户端发起调用请求即可,更重要的是,pay as you go 能够真正为用户节省成本。 Serverless Runtime 分为面向基础架构容器的实现,面向应用服务封装的实现,和事件驱动面向函数计算的实现。 云原生Serverless Runtime形态包含多种方式。业界各个厂商也相应地设计出了不同服务解决方案: 面向函数的Function as a Service(FaaS) - 比如AWS Lambda,阿里云的函数计算,提供了事件驱动的编程方式,用户只需提供函数实现响应触发实践,开发效率很高。阿里云函数计算按照调用量计费,可以根据业务流量平滑调整计算资源,在典型场景下,会有10%~90%的成本下降。客户码隆科技做模型预测,利用函数计算降低了40%的计算成本。面向应用 - 比如Google App Engine、新发布的Cloud Run和阿里云EDAS Serverless,用户只需提供应用实现而平台负责应用弹性、自动化运维,这主要面向互联网类型应用。相比于FaaS,面向应用的Serverless形态无需改造现有应用,阿里云EDAS Serverless为流行的开源微服务框架提供了无服务器应用托管平台,支持Spring Cloud,Apache Dubbo,或者阿里云HSF框架。面向容器 – 比如AWS fargate,或者是阿里云的Serverless Kubernetes 应用的载体是容器镜像,灵活性很好,配合调度系统可以支持各种类型应用,而无需管理底层基础架构。针对容器化应用,阿里云在去年5月推出了Serverless Kubernetes容器服务,无需节点管理和容量规划,按应用所需资源付费,弹性扩容。针对阿里云基础能力优化,安全,高效。极大降低了管理Kubernetes集群的。Serverless Kubernetes的底层是构建在阿里云针对容器优化的轻量虚拟化弹性容器实例之上,提供了轻量、高效、安全的容器应用执行环境。Serverless Kubernetes无需修改即可部署容器类型应用。 2 新的应用负载:容器正被用于越来越多类型应用 最早容器被认为不适合传统的已有应用,但是现在状况已大为改观。容器已经开启了对Windows生态的支持,新发布的1.14版本中Kubernetes 的Pod,Service,应用编排,CNI 网络等绝大多数核心能力都已经在 Windows 节点上得到了支持。当今Windows系统依然占有60%的份额,比如企业的ERP软件、基于ASP的应用、大量的Windows的数据库等,这些传统的基于虚拟化的应用,都可以在代码不用重写的情况下实现容器化。 基于容器技术构建的新架构,会催生新的应用业务价值。云原生AI是非常重要的应用场景,快速搭建AI环境,高效利用底层资源,无缝配合深度学习的全生命周期。对于AI工程,云原生系统可以在四个维度上为提效: 优化异构资源调度弹性、高效、细粒度(支持GPU共享)简化异构资源管理复杂性,提升可观测性和使用效率可移植、可组装、可重现的AI流程以深度学习分布式训练为例,通过阿里云容器服务可以获得三重加强。资源优化:统一调度CPU/GPU等异构资源,使用VPC/RDMA网络加速;性能提升:GPU 64卡P100,加速比提升90%,相比原生Tensorflow有45%提升;算法优化:MPI代替gRPC通信、ring-allreduce环形通信、计算和通信重叠、梯度融合等。 还有其他高性能计算的场景,以基因数据处理为例,阿里云某用户在5小时内完成WGS 100GB数据处理,支持5000+步骤的复杂流程, 90秒实现500节点扩容充分发挥容器极致弹性。 3 新的物理边界:云-边-端,容器不止运行在IDC服务器中 容器最被熟知的基础环境是数据中心,在业务流量高峰与低谷之时,凭借容器极致弹性可以实现应用与资源伸缩,有效地保证高利用率与高性价比。 随着5G和物联网时代的到来,传统云计算中心集中存储、计算的模式已经无法满足终端设备对于时效、容量、算力的需求。将云计算的能力下沉到边缘侧、设备侧,并通过中心进行统一交付、运维、管控,将是云计算的重要发展趋势。以Kubernetes为基础的云原生技术,在任何基础设施上提供与云一致的功能和体验,实现云-边-端一体化的应用分发, 支持不同系统架构和网络状况下,应用的分发和生命周期管理,并且针对边缘及设备进行如访问协议、同步机制、安全机制的种种优化。 如前所述,应用容器化实现了标准化的可移植性,促成了敏捷弹性的云原生应用架构。不仅大大简化了多云/混合云的部署,而且优化成本,同时提供更多的选择,比如满足安全合规的要求、提升业务敏捷性、提升地域覆盖性等等。 容器可以适用于多种基础环境,比如数据中心、边缘云、和多云/混合云,使得开发者关注回归到应用本身。 写在最后云原生时代,是开发者最好的时代。 云原生不但可以很好的支持互联网应用,也在深刻影响着新的计算架构、新的智能数据应用。以容器、服务网格、微服务、Serverless为代表的云原生技术,带来一种全新的方式来构建应用。此外,云原生也在拓展云计算的边界,一方面是多云、混合云推动无边界云计算,一方面云边端的协同。 ...

April 26, 2019 · 1 min · jiezi

解读 kubernetes client-go 官方 examples - Part Ⅰ

转发请注明出处:https://www.cnblogs.com/guang... 1. 介绍最近,因为需要对 Kubernetes 进行二次开发,接触了 client-go 库。client-go 作为官方维护的 go 语言实现的 client 库,提供了大量的高质量代码帮助开发者编写自己的客户端程序,来访问、操作 Kubernetes 集群。 在学习过程中我发现,除了官方的几个 examples 和 README 外,介绍 client-go 的文章较少。因此,这里有必要总结一下我的学习体会,分享出来。 访问 Kubernetes 集群的方式有多种(见 Access Clusters Using the Kubernetes API ),但本质上都要通过调用 Kubernetes REST API 实现对集群的访问和操作。比如,使用最多 kubernetes 命令行工具 kubectl,就是通过调用 Kubernetes REST API 完成的。当执行 kubectl get pods -n test 命令时, kubectl 向 Kubernetes API Server 完成认证、并发送 GET 请求: GET /api/v1/namespaces/test/pods---200 OKContent-Type: application/json{ "kind": "PodList", "apiVersion": "v1", "metadata": {"resourceVersion":"10245"}, "items": [...]}那么如何编写自己的 http 客户端程序呢? 这就需要 Kubernetes 提供的 Golang client 库。 ...

April 23, 2019 · 5 min · jiezi

码上用它开始Flutter混合开发——FlutterBoost

开源地址: https://github.com/alibaba/fl…为什么需要混合方案具有一定规模的App通常有一套成熟通用的基础库,尤其是阿里系App,一般需要依赖很多体系内的基础库。那么使用Flutter重新从头开发App的成本和风险都较高。所以在Native App进行渐进式迁移是Flutter技术在现有Native App进行应用的稳健型方式。闲鱼在实践中沉淀出一套自己的混合技术方案。在此过程中,我们跟Google Flutter团队进行着密切的沟通,听取了官方的一些建议,同时也针对我们业务具体情况进行方案的选型以及具体的实现。官方提出的混合方案基本原理Flutter技术链主要由C++实现的Flutter Engine和Dart实现的Framework组成(其配套的编译和构建工具我们这里不参与讨论)。Flutter Engine负责线程管理,Dart VM状态管理和Dart代码加载等工作。而Dart代码所实现的Framework则是业务接触到的主要API,诸如Widget等概念就是在Dart层面Framework内容。一个进程里面最多只会初始化一个Dart VM。然而一个进程可以有多个Flutter Engine,多个Engine实例共享同一个Dart VM。我们来看具体实现,在iOS上面每初始化一个FlutterViewController就会有一个引擎随之初始化,也就意味着会有新的线程(理论上线程可以复用)去跑Dart代码。Android类似的Activity也会有类似的效果。如果你启动多个引擎实例,注意此时Dart VM依然是共享的,只是不同Engine实例加载的代码跑在各自独立的Isolate。官方建议引擎深度共享在混合方案方面,我们跟Google讨论了可能的一些方案。Flutter官方给出的建议是从长期来看,我们应该支持在同一个引擎支持多窗口绘制的能力,至少在逻辑上做到FlutterViewController是共享同一个引擎的资源的。换句话说,我们希望所有绘制窗口共享同一个主Isolate。但官方给出的长期建议目前来说没有很好的支持。多引擎模式我们在混合方案中解决的主要问题是如何去处理交替出现的Flutter和Native页面。Google工程师给出了一个Keep It Simple的方案:对于连续的Flutter页面(Widget)只需要在当前FlutterViewController打开即可,对于间隔的Flutter页面我们初始化新的引擎。例如,我们进行下面一组导航操作:Flutter Page1 -> Flutter Page2 -> Native Page1 -> Flutter Page3 我们只需要在Flutter Page1和Flutter Page3创建不同的Flutter实例即可。这个方案的好处就是简单易懂,逻辑清晰,但是也有潜在的问题。如果一个Native页面一个Flutter页面一直交替进行的话,Flutter Engine的数量会线性增加,而Flutter Engine本身是一个比较重的对象。多引擎模式的问题冗余的资源问题.多引擎模式下每个引擎之间的Isolate是相互独立的。在逻辑上这并没有什么坏处,但是引擎底层其实是维护了图片缓存等比较消耗内存的对象。想象一下,每个引擎都维护自己一份图片缓存,内存压力将会非常大。插件注册的问题。插件依赖Messenger去传递消息,而目前Messenger是由FlutterViewController(Activity)去实现的。如果你有多个FlutterViewController,插件的注册和通信将会变得混乱难以维护,消息的传递的源头和目标也变得不可控。Flutter Widget和Native的页面差异化问题。Flutter的页面是Widget,Native的页面是VC。逻辑上来说我们希望消除Flutter页面与Naitve页面的差异,否则在进行页面埋点和其它一些统一操作的时候都会遇到额外的复杂度。增加页面之间通信的复杂度。如果所有Dart代码都运行在同一个引擎实例,它们共享一个Isolate,可以用统一的编程框架进行Widget之间的通信,多引擎实例也让这件事情更加复杂。因此,综合多方面考虑,我们没有采用多引擎混合方案。现状与思考前面我们提到多引擎存在一些实际问题,所以闲鱼目前采用的混合方案是共享同一个引擎的方案。这个方案基于这样一个事实:任何时候我们最多只能看到一个页面,当然有些特定的场景你可以看到多个ViewController,但是这些特殊场景我们这里不讨论。我们可以这样简单去理解这个方案:我们把共享的Flutter View当成一个画布,然后用一个Native的容器作为逻辑的页面。每次在打开一个容器的时候我们通过通信机制通知Flutter View绘制成当前的逻辑页面,然后将Flutter View放到当前容器里面。老方案在Dart侧维护了一个Navigator栈的结构。栈数据结构特点就是每次只能从栈顶去操作页面,每一次在查找逻辑页面的时候如果发现页面不在栈顶那么需要往回Pop。这样中途Pop掉的页面状态就丢失了。这个方案无法支持同时存在多个平级逻辑页面的情况,因为你在页面切换的时候必须从栈顶去操作,无法再保持状态的同时进行平级切换。举个例子:有两个页面A,B,当前B在栈顶。切换到A需要把B从栈顶Pop出去,此时B的状态丢失,如果想切回B,我们只能重新打开B之前页面的状态无法维持住。这也是老方案最大的一个局限。如在pop的过程当中,可能会把Flutter 官方的Dialog进行误杀。这也是一个问题。而且基于栈的操作我们依赖对Flutter框架的一个属性修改,这让这个方案具有了侵入性的特点。这也是我们需要解决的一个问题。具体细节,大家可以参考老方案开源项目地址:https://github.com/alibaba-flutter/hybrid_stack_manager新一代混合技术方案 FlutterBoost重构计划在闲鱼推进Flutter化过程当中,更加复杂的页面场景逐渐暴露了老方案的局限性和一些问题。所以我们启动了代号FlutterBoost(向C++ Boost致敬)的新混合技术方案。这次新的混合方案我们的主要目标有:可复用通用型混合方案支持更加复杂的混合模式。比如支持主页Tab这种情况无侵入性方案:不再依赖修改Flutter的方案支持通用页面生命周期统一明确的设计概念跟老方案类似,新的方案还是采用共享引擎的模式实现。主要思路是由Native容器Container通过消息驱动Flutter页面容器Container,从而达到Native Container与Flutter Container的同步目的。我们希望做到Flutter渲染的内容是由Naitve容器去驱动的。简单的理解,我们想做到把Flutter容器做成浏览器的感觉。填写一个页面地址,然后由容器去管理页面的绘制。在Native侧我们只需要关心如果初始化容器,然后设置容器对应的页面标志即可。主要概念Native层概念Container:Native容器,平台Controller,Activity,ViewControllerContainer Manager:容器的管理者Adaptor:Flutter是适配层Messaging:基于Channel的消息通信Dart层概念Container:Flutter用来容纳Widget的容器,具体实现为Navigator的派生类-Container Manager:Flutter容器的管理,提供show,remove等ApiCoordinator: 协调器,接受Messaging消息,负责调用Container Manager的状态管理。Messaging:基于Channel的消息通信关于页面的理解在Native和Flutter表示页面的对象和概念是不一致的。在Native,我们对于页面的概念一般是ViewController,Activity。而对于Flutter我们对于页面的概念是Widget。我们希望可统一页面的概念,或者说弱化抽象掉Flutter本身的Widget对应的页面概念。换句话说,当一个Native的页面容器存在的时候,FlutteBoost保证一定会有一个Widget作为容器的内容。所以我们在理解和进行路由操作的时候都应该以Native的容器为准,Flutter Widget依赖于Native页面容器的状态。那么在FlutterBoost的概念里说到页面的时候,我们指的是Native容器和它所附属的Widget。所有页面路由操作,打开或者关闭页面,实际上都是对Native页面容器的直接操作。无论路由请求来自何方,最终都会转发给Native去实现路由操作。这也是接入FlutterBoost的时候需要实现Platform协议的原因。另一方面,我们无法控制业务代码通过Flutter本身的Navigator去push新的Widget。对于业务不通过FlutterBoost而直接使用Navigator操作Widget的情况,包括Dialog这种非全屏Widget,我们建议是业务自己负责管理其状态。这种类型Widget不属于FlutterBoost所定义的页面概念。理解这里的页面概念,对于理解和使用FlutterBoost至关重要。与老方案主要差别前面我们提到老方案在Dart层维护单个Navigator栈结构用于Widget的切换。而新的方案则是在Dart侧引入了Container的概念,不再用栈的结构去维护现有的页面,而是通过扁平化key-value映射的形式去维护当前所有的页面,每个页面拥有一个唯一的id。这种结构很自然的支持了页面的查找和切换,不再受制于栈顶操作的问题,之前的一些由于pop导致的问题迎刃而解。同时也不再需要依赖修改Flutter源码的形式去进行实现,除去了实现的侵入性。那这是如何做到的呢?多Navigator的实现Flutter在底层提供了让你自定义Navigator的接口,我们自己实现了一个管理多个Navigator的对象。当前最多只会有一个可见的Flutter Navigator,这个Navigator所包含的页面也就是我们当前可见容器所对应的页面。Native容器与Flutter容器(Navigator)是一一对应的,生命周期也是同步的。当一个Native容器被创建的时候,Flutter的一个容器也被创建,它们通过相同的id关联起来。当Native的容器被销毁的时候,Flutter的容器也被销毁。Flutter容器的状态是跟随Native容器,这也就是我们说的Native驱动。由Manager统一管理切换当前在屏幕上展示的容器。我们用一个简单的例子描述一个新页面创建的过程:创建Native容器(iOS ViewController,Android Activity or Fragment)。Native容器通过消息机制通知Flutter Coordinator新的容器被创建。Flutter Container Manager进而得到通知,负责创建出对应的Flutter容器,并且在其中装载对应的Widget页面。当Native容器展示到屏幕上时,容器发消息给Flutter Coordinator通知要展示页面的id.Flutter Container Manager找到对应id的Flutter Container并将其设置为前台可见容器。这就是一个新页面创建的主要逻辑,销毁和进入后台等操作也类似有Native容器事件去进行驱动。总结目前FlutterBoost已经在生产环境支撑着在闲鱼客户端中所有的基于Flutter开发业务,为更加负复杂的混合场景提供了支持。同时也解决了一些历史遗留问题。我们在项目启动之初就希望FlutterBoost能够解决Native App混合模式接入Flutter这个通用问题。所以我们把它做成了一个可复用的Flutter插件,希望吸引更多感兴趣的朋友参与到Flutter社区的建设。我们的方案可能不是最好的,这个方案距离完美还有很大的距离,我们希望通过多分享交流以推动Flutter技术社区的发展与建设。我们更希望看到社区能够涌现出更加优秀的组件和方案。在有限篇幅中,我们分享了闲鱼在Flutter混合技术方案中积累的经验和代码。欢迎兴趣的同学能够积极与我们一起交流学习。扩展补充性能相关在两个Flutter页面进行切换的时候,因为我们只有一个Flutter View所以需要对上一个页面进行截图保存,如果Flutter页面多截图会占用大量内存。这里我们采用文件内存二级缓存策略,在内存中最多只保存2-3个截图,其余的写入文件按需加载。这样我们可以在保证用户体验的同时在内存方面也保持一个较为稳定的水平。页面渲染性能方面,Flutter的AOT优势展露无遗。在页面快速切换的时候,Flutter能够很灵敏的相应页面的切换,在逻辑上创造出一种Flutter多个页面的感觉。Release 1.0支持项目开始的时候我们基于闲鱼目前使用的Flutter版本进行开发,而后进行了Release 1.0兼容升级测试目前没有发现问题。接入只要是集成了Flutter的项目都可以用官方依赖的方式非常方便的以插件形式引入FlutterBoost,只需要对工程进行少量代码接入即可完成接入。详细接入文档,请参阅GitHub主页官方项目文档。现已开源目前,新一代混合栈已经在闲鱼全面应用。我们非常乐意将沉淀的技术回馈给社区。欢迎大家一起贡献,一起交流,携手共建Flutter社区。项目开源地址:https://github.com/alibaba/flutter_boost本文作者:闲鱼技术-福居阅读原文本文为云栖社区原创内容,未经允许不得转载。

April 17, 2019 · 1 min · jiezi

谷歌助力,快速实现 Java 应用容器化

原文地址:梁桂钊的博客博客地址:http://blog.720ui.com欢迎关注公众号:「服务端思维」。一群同频者,一起成长,一起精进,打破认知的局限性。Google 在 2018 年下旬开源了一款新的 Java 工具 Jib,可以轻松地将 Java 应用程序容器化。通过 Jib,我们不需要编写 Dockerfile 或安装 Docker,通过集成到 Maven 或 Gradle 插件,就可以立即将 Java 应用程序容器化。开源地址:https://github.com/GoogleContainerTools/jib一、什么是 JibJib 是一个快速而简单的容器镜像构建工具,它作为 Maven 或 Gradle 的一部分运行,不需要编写 Dockerfile 或运行 Docker 守护进程。它从 Maven 或 Gradle 中构建我们的 Docker 镜像, 并只将发生变更的层(而不是整个应用程序)推送到注册表来节省宝贵的构建时间。现在,我们对 Docker 构建流程和 Jib 构建流程进行对比。Docker 构建流程,如下所示。Jib 构建流程,则是这样的。二、实战出真知1. 构建一个简单的 Java 工程我们编写一个简单的 Java 类。public class HelloWorld { public static void main(String[] args) { System.out.println(“Hello World!”); System.out.println(“http://blog.720ui.com”); }}紧接着,我们再创建一个 pom.xml 文件。<project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.lianggzone.sample.lib</groupId> <artifactId>helloworld-samples</artifactId> <version>0.1</version> <packaging>jar</packaging> <name>helloworld-samples</name> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jib-maven-plugin.version>1.0.2</jib-maven-plugin.version> <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version> </properties> <dependencies> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>${maven-compiler-plugin.version}</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <!– Jib –> <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>${jib-maven-plugin.version}</version> <configuration> <from> <image>registry.cn-hangzhou.aliyuncs.com/lianggzone/oracle_java8</image> </from> <to> <image>registry.cn-hangzhou.aliyuncs.com/lianggzone/jib-helloworld:v1</image> </to> <container> <jvmFlags> <jvmFlag>-Xms512m</jvmFlag> <jvmFlag>-Xdebug</jvmFlag> </jvmFlags> <mainClass>com.lianggzone.HelloWorld</mainClass> </container> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>build</goal> </goals> </execution> </executions> </plugin> </plugins> </build></project>由于默认访问谷歌的 gcr.io 仓库,而国内访问 gcr.io 不稳定会经常导致网络超时,所以笔者使用了国内的阿里云镜像服务,那么就不需要访问谷歌的仓库了。现在,我们执行 mvn compile jib:build 命令进行自动化构建,它会从 <from> 拉取镜像,并把生成的镜像上传到 <to> 设置的地址。这里,笔者还通过 <jvmFlags>` 设置了一些 JVM 参数。mvn compile jib:build此外,如果"登录失败,未授权”,需要通过 docker login 登录鉴权一下。此外,更好的做法是,你可以考虑在Maven 中放置凭据。<settings> … <servers> … <server> <id>registry.cn-hangzhou.aliyuncs.com</id> <username>你的阿里云账号</username> <password>你的阿里云密码</password> </server> </servers></settings>最后,执行完成后,我们可以在阿里云镜像仓库获取镜像。大功告成,现在,我们来验证一把。我们通过 docker pull 拉取镜像,并运行。docker pull registry.cn-hangzhou.aliyuncs.com/lianggzone/jib-helloworld:v1docker run –name jib-helloworld -it registry.cn-hangzhou.aliyuncs.com/lianggzone/jib-helloworld:v1 /bin/bash执行结果,如下所示。2. 构建一个 SpringBoot 的可运行 Jar我们来一个复杂一些的项目,构建一个 SpringBoot 的项目。关于 SpringBoot 的使用,可以阅读笔者之前的文章:http://blog.720ui.com/columns/springboot_all/。现在,我们首先需要搭建一个工程,并创建一个启动类。@SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }}同时,需要一个 Web 的接口。@RestControllerpublic class WebController { @RequestMapping("/blog”) public String index() { return “http://blog.720ui.com”; }}紧接着,我们再创建一个 pom.xml 文件。<?xml version=“1.0” encoding=“UTF-8”?><project xmlns=“http://maven.apache.org/POM/4.0.0" xmlns:xsi=“http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=“http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.2.RELEASE</version> </parent> <groupId>com.lianggzone.sample.lib</groupId> <artifactId>springboot-samples</artifactId> <version>0.1</version> <packaging>jar</packaging> <name>springboot-samples</name> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <jib-maven-plugin.version>1.0.2</jib-maven-plugin.version> <maven-compiler-plugin.version>3.8.0</maven-compiler-plugin.version> </properties> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>${maven-compiler-plugin.version}</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <!– Jib –> <plugin> <groupId>com.google.cloud.tools</groupId> <artifactId>jib-maven-plugin</artifactId> <version>${jib-maven-plugin.version}</version> <configuration> <from> <image>registry.cn-hangzhou.aliyuncs.com/lianggzone/oracle_java8</image> </from> <to> <image>registry.cn-hangzhou.aliyuncs.com/lianggzone/jib-springboot:v1</image> </to> <container> <jvmFlags> <jvmFlag>-Xms512m</jvmFlag> <jvmFlag>-Xdebug</jvmFlag> </jvmFlags> </container> </configuration> <executions> <execution> <phase>package</phase> <goals> <goal>build</goal> </goals> </execution> </executions> </plugin> </plugins> </build></project>现在,我们执行 mvn compile jib:build 命令进行自动化构建。执行完成后,我们可以在阿里云镜像仓库获取镜像。现在,我们再来验证一把。我们通过 docker pull 拉取镜像,并运行。docker pull registry.cn-hangzhou.aliyuncs.com/lianggzone/jib-springboot:v1docker run -p 8080:8080 –name jib-springboot -it registry.cn-hangzhou.aliyuncs.com/lianggzone/jib-springboot:v1 /bin/bash执行结果,如下所示。现在,我们访问 http://localhost:8080/blog ,我们可以正常调用 API 接口了。3. 构建一个 WAR 工程Jib 还支持 WAR 项目。如果 Maven 项目使用 war-packaging 类型,Jib 将默认使用 distroless Jetty 作为基础镜像来部署项目。要使用不同的基础镜像,我们可以自定义 <container><appRoot> , <container> <entrypoint> 和 <container> <args> 。以下是使用 Tomcat 镜像的案例。<configuration> <from> <image>tomcat:8.5-jre8-alpine</image> </from> <container> <appRoot>/usr/local/tomcat/webapps/ROOT</appRoot> </container></configuration>三、源码地址源码地址:https://github.com/lianggzone/jib-samples附:参考资料https://github.com/GoogleContainerTools/jibhttps://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin(完,转载请注明作者及出处。)写在末尾【服务端思维】:我们一起聊聊服务端核心技术,探讨一线互联网的项目架构与实战经验。同时,拥有众多技术大牛的「后端圈」大家庭,期待你的加入,一群同频者,一起成长,一起精进,打破认知的局限性。更多精彩文章,尽在「服务端思维」! ...

April 16, 2019 · 2 min · jiezi

最简单的kubernetes HA安装方式-sealos详解

kubernetes集群三步安装概述本文教你如何用一条命令构建k8s高可用集群且不依赖haproxy和keepalived,也无需ansible。通过内核ipvs对apiserver进行负载均衡,并且带apiserver健康检测。快速入门sealos项目地址准备条件装好docker并启动docker把离线安装包 下载好拷贝到所有节点的/root目录下, 不需要解压,如果有文件服务器更好,sealos支持从一个服务器上wget到所有节点上安装sealos已经放在离线包中,解压后在kube/bin目录下(可以解压一个,获取sealos bin文件)sealos init \ –master 192.168.0.2 \ –master 192.168.0.3 \ –master 192.168.0.4 \ # master地址列表 –node 192.168.0.5 \ # node地址列表 –user root \ # 服务用户名 –passwd your-server-password \ # 服务器密码,用于远程执行命令 –pkg kube1.14.1.tar.gz \ # 离线安装包名称 –version v1.14.1 # kubernetes 离线安装包版本,这渲染kubeadm配置时需要使用然后,就没有然后了其它参数: –kubeadm-config string kubeadm-config.yaml local # 自定义kubeadm配置文件,如有这个sealos就不去渲染kubeadm配置 –pkg-url string http://store.lameleg.com/kube1.14.1.tar.gz download offline pakage url # 支持从远程拉取离线包,省的每个机器拷贝,前提你得有个http服务器放离线包 –vip string virtual ip (default “10.103.97.2”) # 代理master的虚拟IP,只要与你地址不冲突请不要改清理sealos clean \ –master 192.168.0.2 \ –master 192.168.0.3 \ –master 192.168.0.4 \ # master地址列表 –node 192.168.0.5 \ # node地址列表 –user root \ # 服务用户名 –passwd your-server-password增加节点新增节点可直接使用kubeadm, 到新节点上解压cd kube/shell && init.shecho “10.103.97.2 apiserver.cluster.local” >> /etc/hosts # using vipkubeadm join 10.103.97.2:6443 –token 9vr73a.a8uxyaju799qwdjv \ –master 10.103.97.100:6443 \ –master 10.103.97.101:6443 \ –master 10.103.97.102:6443 \ –discovery-token-ca-cert-hash sha256:7c2e69131a36ae2a042a339b33381c6d0d43887e2de83720eff5359e26aec866安装dashboard prometheus等离线包里包含了yaml配置和镜像,用户按需安装。cd /root/kube/confkubectl taint nodes –all node-role.kubernetes.io/master- # 去污点,根据需求看情况,去了后master允许调度kubectl apply -f heapster/ # 安装heapster, 不安装dashboard上没监控数据kubectl apply -f heapster/rbac kubectl apply -f dashboard # 装dashboardkubectl apply -f prometheus # 装监控是不是很神奇,到底是如何做到这点的?那就需要去看下面两个东西关于超级kubeadm我们定制了kubeadm,做了两个事情:在每个node节点上增加了一条ipvs规则,其后端代理了三个master在node上起了一个lvscare的static pod去守护这个 ipvs, 一旦apiserver不可访问了,会自动清理掉所有node上对应的ipvs规则, master恢复正常时添加回来。通过这样的方式实现每个node上通过本地内核负载均衡访问masters: +———-+ +—————+ virturl server: 127.0.0.1:6443 | mater0 |<———————-| ipvs nodes | real servers: +———-+ |+—————+ 10.103.97.200:6443 | 10.103.97.201:6443 +———-+ | 10.103.97.202:6443 | mater1 |<———————+ +———-+ | | +———-+ | | mater2 |<———————+ +———-+这是一个非常优雅的方案其实sealos就是帮你执行了如下命令:super-kubeadm在你的node上增加了三个东西:cat /etc/kubernetes/manifests # 这下面增加了lvscare的static podipvsadm -Ln # 可以看到创建的ipvs规则cat /etc/hosts # 增加了虚拟IP的地址解析关于lvscare这是一个超级简单轻量级的lvs创建与守护进程,支持健康检查,底层与kube-proxy使用的是相同的库,支持HTTP的健康检测。清理机器上的IPVS规则ipvsadm -C启动几个nginx作为ipvs代理后端的realserverdocker run -p 8081:80 –name nginx1 -d nginxdocker run -p 8082:80 –name nginx2 -d nginxdocker run -p 8083:80 –name nginx3 -d nginx启动lvscare守护它们lvscare care –vs 10.103.97.12:6443 –rs 127.0.0.1:8081 –rs 127.0.0.1:8082 –rs 127.0.0.1:8083 --health-path / –health-schem http可以看到规则已经被创建ipvsadm -Ln[root@iZj6c9fiza9orwscdhate4Z ~]# ipvsadm -LnIP Virtual Server version 1.2.1 (size=4096)Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConnTCP 10.103.97.12:6443 rr -> 127.0.0.1:8081 Masq 1 0 0 -> 127.0.0.1:8082 Masq 1 0 0 -> 127.0.0.1:8083 Masq 1 0 0 curl vip:[root@iZj6c9fiza9orwscdhate4Z ~]# curl 10.103.97.12:6443 <!DOCTYPE html><html><head><title>Welcome to nginx!</title><style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; }</style></head>删除一个nginx,规则就少了一条[root@iZj6c9fiza9orwscdhate4Z ~]# docker stop nginx1nginx1[root@iZj6c9fiza9orwscdhate4Z ~]# ipvsadm -LnIP Virtual Server version 1.2.1 (size=4096)Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConnTCP 10.103.97.12:6443 rr -> 127.0.0.1:8082 Masq 1 0 0 -> 127.0.0.1:8083 Masq 1 0 1 再删除一个:[root@iZj6c9fiza9orwscdhate4Z ~]# docker stop nginx2nginx2[root@iZj6c9fiza9orwscdhate4Z ~]# ipvsadm -LnIP Virtual Server version 1.2.1 (size=4096)Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConnTCP 10.103.97.12:6443 rr -> 127.0.0.1:8083 Masq 1 0 0 此时VIP任然可以访问:[root@iZj6c9fiza9orwscdhate4Z ~]# curl 10.103.97.12:6443 <!DOCTYPE html><html><head><title>Welcome to nginx!</title><style> body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; }</style></head>全部删除, 规则就自动被清除光了, curl也curl不通了,因为没realserver可用了[root@iZj6c9fiza9orwscdhate4Z ~]# docker stop nginx3nginx3[root@iZj6c9fiza9orwscdhate4Z ~]# ipvsadm -LnIP Virtual Server version 1.2.1 (size=4096)Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConnTCP 10.103.97.12:6443 rr[root@iZj6c9fiza9orwscdhate4Z ~]# curl 10.103.97.12:6443 curl: (7) Failed connect to 10.103.97.12:6443; 拒绝连接再把nginx都启动起来,规则就自动被加回来[root@iZj6c9fiza9orwscdhate4Z ~]# docker start nginx1 nginx2 nginx3nginx1nginx2nginx3[root@iZj6c9fiza9orwscdhate4Z ~]# ipvsadm -LnIP Virtual Server version 1.2.1 (size=4096)Prot LocalAddress:Port Scheduler Flags -> RemoteAddress:Port Forward Weight ActiveConn InActConnTCP 10.103.97.12:6443 rr -> 127.0.0.1:8081 Masq 1 0 0 -> 127.0.0.1:8082 Masq 1 0 0 -> 127.0.0.1:8083 Masq 1 0 0 所以sealos中,上面apiserver就是上面三个nginx,lvscare会对其进行健康检测。当然你也可以把lvscare用于一些其它场景,比如代理自己的TCP服务等 ...

April 15, 2019 · 3 min · jiezi

如何扩展Laravel

注册服务向容器中注册服务// 绑定服务$container->bind(’log’, function(){ return new Log();});// 绑定单例服务$container->singleton(’log’, function(){ return new Log();});扩展绑定扩展已有服务$container->extend(’log’, function(Log $log){ return new RedisLog($log);});ManagerManager实际上是一个工厂,它为服务提供了驱动管理功能。Laravel中的很多组件都使用了Manager,如:Auth、Cache、Log、Notification、Queue、Redis等等,每个组件都有一个xxxManager的管理器。我们可以通过这个管理器扩展服务。比如,如果我们想让Cache服务支持RedisCache驱动,那么我们可以给Cache服务扩展一个redis驱动:Cache::extend(‘redis’, function(){ return new RedisCache();});这时候,Cache服务就支持redis这个驱动了。现在,找到config/cache.php,把default选项的值改成redis。这时候我们再用Cache服务时,就会使用RedisCache驱动来使用缓存。Macro和Mixin有些情况下,我们需要给一个类动态增加几个方法,Macro或者Mixin很好的解决了这个问题。在Laravel底层,有一个名为Macroable的Trait,凡是引入了Macroable的类,都支持Macro和Mixin的方式扩展,比如Request、Response、SessionGuard、View、Translator等等。Macroable提供了两个方法,macro和mixin,macro方法可以给类增加一个方法,mixin是把一个类中的方法混合到Macroable类中。举个例子,比如我们要给Request类增加两个方法。使用macro方法时:Request::macro(‘getContentType’, function(){ // 函数内的$this会指向Request对象 return $this->headers->get(‘content-type’);});Request::macro(‘hasField’, function(){ return !is_null($this->get($name));});$contentType = Request::getContentstType();$hasPassword = Request::hasField(‘password’);使用mixin方法时:class MixinRequest{ public function getContentType(){ // 方法内必须返回一个函数 return function(){ return $this->headers->get(‘content-type’); }; } public function hasField(){ return function($name){ return !is_null($this->get($name)); }; }}Request::mixin(new MixinRequest());$contentType = Request::getContentType();$hasPassword = Request::hasField(‘password’);

April 14, 2019 · 1 min · jiezi

如何实现Laravel的服务容器

如何实现服务容器(Ioc Container)1. 容器的本质服务容器本身就是一个数组,键名就是服务名,值就是服务。服务可以是一个原始值,也可以是一个对象,可以说是任意数据。服务名可以是自定义名,也可以是对象的类名,也可以是接口名。// 服务容器$container = [ // 原始值 ’text’ => ‘这是一个字符串’, // 自定义服务名 ‘customName’ => new StdClass(), // 使用类名作为服务名 ‘StdClass’ => new StdClass(), // 使用接口名作为服务名 ‘Namespace\StdClassInterface’ => new StdClass(),];// ———– ↓↓↓↓示例代码↓↓↓↓ ———– //// 绑定服务到容器$container[‘standard’] = new StdClass();// 获取服务$standard = $container[‘standard’];var_dump($standard);2. 封装成类为了方便维护,我们把上面的数组封装到类里面。$instances还是上面的容器数组。我们增加两个方法,instance用来绑定服务,get用来从容器中获取服务。class BaseContainer{ // 已绑定的服务 protected $instances = []; // 绑定服务 public function instance($name, $instance) { $this->instances[$name] = $instance; } // 获取服务 public function get($name) { return isset($this->instances[$name]) ? $this->instances[$name] : null; }}// ———– ↓↓↓↓示例代码↓↓↓↓ ———– //$container = new BaseContainer();// 绑定服务$container->instance(‘StdClass’, new StdClass());// 获取服务$stdClass = $container->get(‘StdClass’);var_dump($stdClass);3. 按需实例化现在我们在绑定一个对象服务的时候,就必须要先把类实例化,如果绑定的服务没有被用到,那么类就会白白实例化,造成性能浪费。为了解决这个问题,我们增加一个bind函数,它支持绑定一个回调函数,在回调函数中实例化类。这样一来,我们只有在使用服务时,才回调这个函数,这样就实现了按需实例化。这时候,我们获取服务时,就不只是从数组中拿到服务并返回了,还需要判断如果是回调函数,就要执行回调函数。所以我们把get方法的名字改成make。意思就是生产一个服务,这个服务可以是已绑定的服务,也可以是已绑定的回调函数,也可以是一个类名,如果是类名,我们就直接实例化该类并返回。然后,我们增加一个新数组$bindings,用来存储绑定的回调函数。然后我们把bind方法改一下,判断下$instance如果是一个回调函数,就放到$bindings数组,否则就用make方法实例化类。class DeferContainer extend BaseContainer{ // 已绑定的回调函数 protected $bindings = []; // 绑定服务 public function bind($name, $instance) { if ($instance instanceof Closure) { // 如果$instance是一个回调函数,就绑定到bindings。 $this->bindings[$name] = $instance; } else { // 调用make方法,创建实例 $this->instances[$name] = $this->make($name); } } // 获取服务 public function make($name) { if (isset($this->instances[$name])) { return $this->instances[$name]; } if (isset($this->bindings[$name])) { // 执行回调函数并返回 $instance = call_user_func($this->bindings[$name]); } else { // 还没有绑定到容器中,直接new. $instance = new $name(); } return $instance; }}// ———– ↓↓↓↓示例代码↓↓↓↓ ———– //$container = new DeferContainer();// 绑定服务$container->bind(‘StdClass’, function () { echo “我被执行了\n”; return new StdClass();});// 获取服务$stdClass = $container->make(‘StdClass’);var_dump($stdClass);StdClass这个服务绑定的是一个回调函数,在回调函数中才会真正的实例化类。如果没有用到这个服务,那回调函数就不会被执行,类也不会被实例化。4. 单例从上面的代码中可以看出,每次调用make方法时,都会执行一次回调函数,并返回一个新的类实例。但是在某些情况下,我们希望这个实例是一个单例,无论make多少次,只实例化一次。这时候,我们给bind方法增加第三个参数$shared,用来标记是否是单例,默认不是单例。然后把回调函数和这个标记都存到$bindings数组里。为了方便绑定单例服务,再增加一个新的方法singleton,它直接调用bind,并且$shared参数强制为true。对于make方法,我们也要做修改。在执行$bindings里的回调函数以后,做一个判断,如果之前绑定时标记的shared是true,就把回调函数返回的结果存储到$instances里。由于我们是先从$instances里找服务,所以这样下次再make的时候就会直接返回,而不会再次执行回调函数。这样就实现了单例的绑定。class SingletonContainer extends DeferContainer{ // 绑定服务 public function bind($name, $instance, $shared = false) { if ($instance instanceof Closure) { // 如果$instance是一个回调函数,就绑定到bindings。 $this->bindings[$name] = [ ‘callback’ => $instance, // 标记是否单例 ‘shared’ => $shared ]; } else { // 调用make方法,创建实例 $this->instances[$name] = $this->make($name); } } // 绑定一个单例 public function singleton($name, $instance) { $this->bind($name, $instance, true); } // 获取服务 public function make($name) { if (isset($this->instances[$name])) { return $this->instances[$name]; } if (isset($this->bindings[$name])) { // 执行回调函数并返回 $instance = call_user_func($this->bindings[$name][‘callback’]); if ($this->bindings[$name][‘shared’]) { // 标记为单例时,存储到服务中 $this->instances[$name] = $instance; } } else { // 还没有绑定到容器中,直接new. $instance = new $name(); } return $instance; }}// ———– ↓↓↓↓示例代码↓↓↓↓ ———– //$container = new SingletonContainer();// 绑定服务$container->singleton(‘anonymous’, function () { return new class { public function __construct() { echo “我被实例化了\n”; } };});// 无论make多少次,只会实例化一次$container->make(‘anonymous’);$container->make(‘anonymous’);// 获取服务$anonymous = $container->make(‘anonymous’);var_dump($anonymous)上面的代码用singleton绑定了一个名为anonymous的服务,回调函数里返回了一个匿名类的实例。这个匿名类在被实例化时会输出一段文字。无论我们make多少次anonymous,这个回调函数只会被执行一次,匿名类也只会被实例化一次。5. 自动注入自动注入是Ioc容器的核心,没有自动注入就无法做到控制反转。自动注入就是指,在实例化一个类时,用反射类来获取__construct所需要的参数,然后根据参数的类型,从容器中找到已绑定的服务。我们只要有了__construct方法所需的所有参数,就能自动实例化该类,实现自动注入。现在,我们增加一个build方法,它只接收一个参数,就是类名。build方法会用反射类来获取__construct方法所需要的参数,然后返回实例化结果。另外一点就是,我们之前在调用make方法时,如果传的是一个未绑定的类,我们直接new了这个类。现在我们把未绑定的类交给build方法来构建,因为它支持自动注入。class InjectionContainer extends SingletonContainer{ // 获取服务 public function make($name) { if (isset($this->instances[$name])) { return $this->instances[$name]; } if (isset($this->bindings[$name])) { // 执行回调函数并返回 $instance = call_user_func($this->bindings[$name][‘callback’]); if ($this->bindings[$name][‘shared’]) { // 标记为单例时,存储到服务中 $this->instances[$name] = $instance; } } else { // 使用build方法构建此类 $instance = $this->build($name); } return $instance; } // 构建一个类,并自动注入服务 public function build($class) { $reflector = new ReflectionClass($class); $constructor = $reflector->getConstructor(); if (is_null($constructor)) { // 没有构造函数,直接new return new $class(); } $dependencies = []; // 获取构造函数所需的参数 foreach ($constructor->getParameters() as $dependency) { if (is_null($dependency->getClass())) { // 参数类型不是类时,无法从容器中获取依赖 if ($dependency->isDefaultValueAvailable()) { // 查找参数的默认值,如果有就使用默认值 $dependencies[] = $dependency->getDefaultValue(); } else { // 无法提供类所依赖的参数 throw new Exception(‘找不到依赖参数:’ . $dependency->getName()); } } else { // 参数类型是类时,就用make方法构建该类 $dependencies[] = $this->make($dependency->getClass()->name); } } return $reflector->newInstanceArgs($dependencies); }}// ———– ↓↓↓↓示例代码↓↓↓↓ ———– //class Redis{}class Cache{ protected $redis; // 构造函数中依赖Redis服务 public function __construct(Redis $redis) { $this->redis = $redis; }}$container = new InjectionContainer();// 绑定Redis服务$container->singleton(Redis::class, function () { return new Redis();});// 构建Cache类$cache = $container->make(Cache::class);var_dump($cache);6. 自定义依赖参数现在有个问题,如果类依赖的参数不是类或接口,只是一个普通变量,这时候就无法从容器中获取依赖参数了,也就无法实例化类了。那么接下来我们就支持一个新功能,在调用make方法时,支持传第二个参数$parameters,这是一个数组,无法从容器中获取的依赖,就从这个数组中找。当然,make方法是用不到这个参数的,因为它不负责实例化类,它直接传给build方法。在build方法寻找依赖的参数时,就先从$parameters中找。这样就实现了自定义依赖参数。需要注意的一点是,build方法是按照参数的名字来找依赖的,所以parameters中的键名也必须跟__construct中参数名一致。class ParametersContainer extends InjectionContainer{ // 获取服务 public function make($name, array $parameters = []) { if (isset($this->instances[$name])) { return $this->instances[$name]; } if (isset($this->bindings[$name])) { // 执行回调函数并返回 $instance = call_user_func($this->bindings[$name][‘callback’]); if ($this->bindings[$name][‘shared’]) { // 标记为单例时,存储到服务中 $this->instances[$name] = $instance; } } else { // 使用build方法构建此类 $instance = $this->build($name, $parameters); } return $instance; } // 构建一个类,并自动注入服务 public function build($class, array $parameters = []) { $reflector = new ReflectionClass($class); $constructor = $reflector->getConstructor(); if (is_null($constructor)) { // 没有构造函数,直接new return new $class(); } $dependencies = []; // 获取构造函数所需的参数 foreach ($constructor->getParameters() as $dependency) { if (isset($parameters[$dependency->getName()])) { // 先从自定义参数中查找 $dependencies[] = $parameters[$dependency->getName()]; continue; } if (is_null($dependency->getClass())) { // 参数类型不是类或接口时,无法从容器中获取依赖 if ($dependency->isDefaultValueAvailable()) { // 查找默认值,如果有就使用默认值 $dependencies[] = $dependency->getDefaultValue(); } else { // 无法提供类所依赖的参数 throw new Exception(‘找不到依赖参数:’ . $dependency->getName()); } } else { // 参数类型是类时,就用make方法构建该类 $dependencies[] = $this->make($dependency->getClass()->name); } } return $reflector->newInstanceArgs($dependencies); }}// ———– ↓↓↓↓示例代码↓↓↓↓ ———– //class Redis{}class Cache{ protected $redis; protected $name; protected $default; // 构造函数中依赖Redis服务和name参数,name的类型不是类,无法从容器中查找 public function __construct(Redis $redis, $name, $default = ‘默认值’) { $this->redis = $redis; $this->name = $name; $this->default = $default; }}$container = new ParametersContainer();// 绑定Redis服务$container->singleton(Redis::class, function () { return new Redis();});// 构建Cache类$cache = $container->make(Cache::class, [’name’ => ’test’]);var_dump($cache);提示:实际上,Laravel容器的build方法并没有第二个参数$parameters,它是用类属性来维护自定义参数。原理都是一样的,只是实现方式不一样。这里为了方便理解,不引入过多概念。7. 服务别名别名可以理解成小名、外号。服务别名就是给已绑定的服务设置一些外号,使我们通过外号也能找到该服务。这个就比较简单了,我们增加一个新的数组$aliases,用来存储别名。再增加一个方法alias,用来让外部注册别名。唯一需要我们修改的地方,就是在make时,要先从$aliases中找到真实的服务名。class AliasContainer extends ParametersContainer{ // 服务别名 protected $aliases = []; // 给服务绑定一个别名 public function alias($alias, $name) { $this->aliases[$alias] = $name; } // 获取服务 public function make($name, array $parameters = []) { // 先用别名查找真实服务名 $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name; return parent::make($name, $parameters); }}// ———– ↓↓↓↓示例代码↓↓↓↓ ———– //$container = new AliasContainer();// 绑定服务$container->instance(’text’, ‘这是一个字符串’);// 给服务注册别名$container->alias(‘string’, ’text’);$container->alias(‘content’, ’text’);var_dump($container->make(‘string’));var_dump($container->make(‘content’));8. 扩展绑定有时候我们需要给已绑定的服务做一个包装,这时候就用到扩展绑定了。我们先看一个实际的用法,理解它的作用后,才看它是如何实现的。// 绑定日志服务$container->singleton(’log’, new Log());// 对已绑定的服务再次包装$container->extend(’log’, function(Log $log){ // 返回了一个新服务 return new RedisLog($log);});现在我们看它是如何实现的。增加一个$extenders数组,用来存放扩展器。再增加一个extend方法,用来注册扩展器。然后在make方法返回$instance之前,按顺序依次调用之前注册的扩展器。class ExtendContainer extends AliasContainer{ // 存放扩展器的数组 protected $extenders = []; // 给服务绑定扩展器 public function extend($name, $extender) { if (isset($this->instances[$name])) { // 已经实例化的服务,直接调用扩展器 $this->instances[$name] = $extender($this->instances[$name]); } else { $this->extenders[$name][] = $extender; } } // 获取服务 public function make($name, array $parameters = []) { $instance = parent::make($name, $parameters); if (isset($this->extenders[$name])) { // 调用扩展器 foreach ($this->extenders[$name] as $extender) { $instance = $extender($instance); } } return $instance; }}// ———– ↓↓↓↓示例代码↓↓↓↓ ———– //class Redis{ public $name; public function __construct($name = ‘default’) { $this->name = $name; } public function setName($name) { $this->name = $name; }}$container = new ExtendContainer();// 绑定Redis服务$container->singleton(Redis::class, function () { return new Redis();});// 给Redis服务绑定一个扩展器$container->extend(Redis::class, function (Redis $redis) { $redis->setName(‘扩展器’); return $redis;});$redis = $container->make(Redis::class);var_dump($redis->name);9. 上下文绑定有时侯我们可能有两个类使用同一个接口,但希望在每个类中注入不同的实现,例如两个控制器,分别为它们注入不同的Log服务。class ApiController{ public function __construct(Log $log) { }}class WebController{ public function __construct(Log $log) { }}最终我们要用以下方式实现:// 当ApiController依赖Log时,给它一个RedisLog$container->addContextualBinding(‘ApiController’,‘Log’,new RedisLog());// 当WebController依赖Log时,给它一个FileLog$container->addContextualBinding(‘WebController’,‘Log’,new FileLog());为了更直观更方便更语义化的使用,我们把这个过程改成链式操作:$container->when(‘ApiController’) ->needs(‘Log’) ->give(new RedisLog());我们增加一个$context数组,用来存储上下文。同时增加一个addContextualBinding方法,用来注册上下文绑定。以ApiController为例,$context的真实模样是:$context[‘ApiController’][‘Log’] = new RedisLog();然后build方法实例化类时,先从上下文中查找依赖参数,就实现了上下文绑定。接下来,看看链式操作是如何实现的。首先定义一个类Context,这个类有两个方法,needs和give。然后在容器中,增加一个when方法,它返回一个Context对象。在Context对象的give方法中,我们已经具备了注册上下文所需要的所有参数,所以就可以在give方法中调用addContextualBinding来注册上下文了。class ContextContainer extends ExtendContainer{ // 依赖上下文 protected $context = []; // 构建一个类,并自动注入服务 public function build($class, array $parameters = []) { $reflector = new ReflectionClass($class); $constructor = $reflector->getConstructor(); if (is_null($constructor)) { // 没有构造函数,直接new return new $class(); } $dependencies = []; // 获取构造函数所需的参数 foreach ($constructor->getParameters() as $dependency) { if (isset($this->context[$class]) && isset($this->context[$class][$dependency->getName()])) { // 先从上下文中查找 $dependencies[] = $this->context[$class][$dependency->getName()]; continue; } if (isset($parameters[$dependency->getName()])) { // 从自定义参数中查找 $dependencies[] = $parameters[$dependency->getName()]; continue; } if (is_null($dependency->getClass())) { // 参数类型不是类或接口时,无法从容器中获取依赖 if ($dependency->isDefaultValueAvailable()) { // 查找默认值,如果有就使用默认值 $dependencies[] = $dependency->getDefaultValue(); } else { // 无法提供类所依赖的参数 throw new Exception(‘找不到依赖参数:’ . $dependency->getName()); } } else { // 参数类型是一个类时,就用make方法构建该类 $dependencies[] = $this->make($dependency->getClass()->name); } } return $reflector->newInstanceArgs($dependencies); } // 绑定上下文 public function addContextualBinding($when, $needs, $give) { $this->context[$when][$needs] = $give; } // 支持链式方式绑定上下文 public function when($when) { return new Context($when, $this); }}class Context{ protected $when; protected $needs; protected $container; public function __construct($when, ContextContainer $container) { $this->when = $when; $this->container = $container; } public function needs($needs) { $this->needs = $needs; return $this; } public function give($give) { // 调用容器绑定依赖上下文 $this->container->addContextualBinding($this->when, $this->needs, $give); }}// ———– ↓↓↓↓示例代码↓↓↓↓ ———– //class Dog{ public $name; public function __construct($name) { $this->name = $name; }}class Cat{ public $name; public function __construct($name) { $this->name = $name; }}$container = new ContextContainer();// 给Dog类设置上下文绑定$container->when(Dog::class) ->needs(’name’) ->give(‘小狗’);// 给Cat类设置上下文绑定$container->when(Cat::class) ->needs(’name’) ->give(‘小猫’);$dog = $container->make(Dog::class);$cat = $container->make(Cat::class);var_dump(‘Dog:’ . $dog->name);var_dump(‘Cat:’ . $cat->name);10. 完整代码class Container{ // 已绑定的服务 protected $instances = []; // 已绑定的回调函数 protected $bindings = []; // 服务别名 protected $aliases = []; // 存放扩展器的数组 protected $extenders = []; // 依赖上下文 protected $context = []; // 绑定服务实例 public function instance($name, $instance) { $this->instances[$name] = $instance; } // 绑定服务 public function bind($name, $instance, $shared = false) { if ($instance instanceof Closure) { // 如果$instance是一个回调函数,就绑定到bindings。 $this->bindings[$name] = [ ‘callback’ => $instance, // 标记是否单例 ‘shared’ => $shared ]; } else { // 调用make方法,创建实例 $this->instances[$name] = $this->make($name); } } // 绑定一个单例 public function singleton($name, $instance) { $this->bind($name, $instance, true); } // 给服务绑定一个别名 public function alias($alias, $name) { $this->aliases[$alias] = $name; } // 给服务绑定扩展器 public function extend($name, $extender) { if (isset($this->instances[$name])) { // 已经实例化的服务,直接调用扩展器 $this->instances[$name] = $extender($this->instances[$name]); } else { $this->extenders[$name][] = $extender; } } // 获取服务 public function make($name, array $parameters = []) { // 先用别名查找真实服务名 $name = isset($this->aliases[$name]) ? $this->aliases[$name] : $name; if (isset($this->instances[$name])) { return $this->instances[$name]; } if (isset($this->bindings[$name])) { // 执行回调函数并返回 $instance = call_user_func($this->bindings[$name][‘callback’]); if ($this->bindings[$name][‘shared’]) { // 标记为单例时,存储到服务中 $this->instances[$name] = $instance; } } else { // 使用build方法构建此类 $instance = $this->build($name, $parameters); } if (isset($this->extenders[$name])) { // 调用扩展器 foreach ($this->extenders[$name] as $extender) { $instance = $extender($instance); } } return $instance; } // 构建一个类,并自动注入服务 public function build($class, array $parameters = []) { $reflector = new ReflectionClass($class); $constructor = $reflector->getConstructor(); if (is_null($constructor)) { // 没有构造函数,直接new return new $class(); } $dependencies = []; // 获取构造函数所需的参数 foreach ($constructor->getParameters() as $dependency) { if (isset($this->context[$class]) && isset($this->context[$class][$dependency->getName()])) { // 先从上下文中查找 $dependencies[] = $this->context[$class][$dependency->getName()]; continue; } if (isset($parameters[$dependency->getName()])) { // 从自定义参数中查找 $dependencies[] = $parameters[$dependency->getName()]; continue; } if (is_null($dependency->getClass())) { // 参数类型不是类或接口时,无法从容器中获取依赖 if ($dependency->isDefaultValueAvailable()) { // 查找默认值,如果有就使用默认值 $dependencies[] = $dependency->getDefaultValue(); } else { // 无法提供类所依赖的参数 throw new Exception(‘找不到依赖参数:’ . $dependency->getName()); } } else { // 参数类型是一个类时,就用make方法构建该类 $dependencies[] = $this->make($dependency->getClass()->name); } } return $reflector->newInstanceArgs($dependencies); } // 绑定上下文 public function addContextualBinding($when, $needs, $give) { $this->context[$when][$needs] = $give; } // 支持链式方式绑定上下文 public function when($when) { return new Context($when, $this); }}class Context{ protected $when; protected $needs; protected $container; public function __construct($when, Container $container) { $this->when = $when; $this->container = $container; } public function needs($needs) { $this->needs = $needs; return $this; } public function give($give) { // 调用容器绑定依赖上下文 $this->container->addContextualBinding($this->when, $this->needs, $give); }} ...

April 14, 2019 · 7 min · jiezi

如何画出一张合格的技术架构图?

阿里妹导读:技术传播的价值,不仅仅体现在通过商业化产品和开源项目来缩短我们构建应用的路径,加速业务的上线速率,也体现在优秀工程师在工作效率提升、产品性能优化和用户体验改善等经验方面的分享,以提高我们的专业能力。接下来,阿里巴巴技术专家三画,将分享自己和团队在画好架构图方面的理念和经验,希望对你有所帮助。当我们想用一张或几张图来描述我们的系统时,是不是经常遇到以下情况:对着画布无从下手、删了又来?如何用一张图描述我的系统,并且让产品、运营、开发都能看明白?画了一半的图还不清楚受众是谁?画出来的图到底是产品图功能图还是技术图又或是大杂烩?图上的框框有点少是不是要找点儿框框加进来?布局怎么画都不满意……如果有同样的困惑,本文将介绍一种画图的方法论,来让架构图更清晰。先厘清一些基础概念1、什么是架构?架构就是对系统中的实体以及实体之间的关系所进行的抽象描述,是一系列的决策。架构是结构和愿景。系统架构是概念的体现,是对物/信息的功能与形式元素之间的对应情况所做的分配,是对元素之间的关系以及元素同周边环境之间的关系所做的定义。做好架构是个复杂的任务,也是个很大的话题,本篇就不做深入了。有了架构之后,就需要让干系人理解、遵循相关决策。2、什么是架构图?系统架构图是为了抽象地表示软件系统的整体轮廓和各个组件之间的相互关系和约束边界,以及软件系统的物理部署和软件系统的演进方向的整体视图。3、架构图的作用一图胜千言。要让干系人理解、遵循架构决策,就需要把架构信息传递出去。架构图就是一个很好的载体。那么,画架构图是为了:解决沟通障碍达成共识减少歧义4、架构图分类搜集了很多资料,分类有很多,有一种比较流行的是4+1视图,分别为场景视图、逻辑视图、物理视图、处理流程视图和开发视图。场景视图场景视图用于描述系统的参与者与功能用例间的关系,反映系统的最终需求和交互设计,通常由用例图表示。逻辑视图逻辑视图用于描述系统软件功能拆解后的组件关系,组件约束和边界,反映系统整体组成与系统如何构建的过程,通常由UML的组件图和类图来表示。物理视图物理视图用于描述系统软件到物理硬件的映射关系,反映出系统的组件是如何部署到一组可计算机器节点上,用于指导软件系统的部署实施过程。处理流程视图处理流程视图用于描述系统软件组件之间的通信时序,数据的输入输出,反映系统的功能流程与数据流程,通常由时序图和流程图表示。开发视图开发视图用于描述系统的模块划分和组成,以及细化到内部包的组成设计,服务于开发人员,反映系统开发实施过程。以上 5 种架构视图从不同角度表示一个软件系统的不同特征,组合到一起作为架构蓝图描述系统架构。怎样的架构图是好的架构图上面的分类是前人的经验总结,图也是从网上摘来的,那么这些图画的好不好呢?是不是我们要依葫芦画瓢去画这样一些图?先不去管这些图好不好,我们通过对这些图的分类以及作用,思考了一下,总结下来,我们认为,在画出一个好的架构图之前, 首先应该要明确其受众,再想清楚要给他们传递什么信息 ,所以,不要为了画一个物理视图去画物理视图,为了画一个逻辑视图去画逻辑视图,而应该根据受众的不同,传递的信息的不同,用图准确地表达出来,最后的图可能就是在这样一些分类里。那么,画出的图好不好的一个直接标准就是:受众有没有准确接收到想传递的信息。明确这两点之后,从受众角度来说,一个好的架构图是不需要解释的,它应该是自描述的,并且要具备一致性和足够的准确性,能够与代码相呼应。画架构图遇到的常见问题1、方框代表什么?为什么适用方框而不是圆形,它有什么特殊的含义吗?随意使用方框或者其它形状可能会引起混淆。2、虚线、实线什么意思?箭头什么意思?颜色什么意思?随意使用线条或者箭头可能会引起误会。3、运行时与编译时冲突?层级冲突?架构是一项复杂的工作,只使用单个图表来表示架构很容易造成莫名其妙的语义混乱。本文推荐的画图方法C4 模型使用容器(应用程序、数据存储、微服务等)、组件和代码来描述一个软件系统的静态结构。这几种图比较容易画,也给出了画图要点,但最关键的是,我们认为,它明确指出了每种图可能的受众以及意义。下面的案例来自C4官网,然后加上了一些我们的理解,来看看如何更好的表达软件架构1、语境图(System Context Diagram)这是一个想象的待建设的互联网银行系统,它使用外部的大型机银行系统存取客户账户、交易信息,通过外部电邮系统给客户发邮件。可以看到,非常简单、清晰,相信不需要解释,都看的明白,里面包含了需要建设的系统本身,系统的客户,和这个系统有交互的周边系统。用途这样一个简单的图,可以告诉我们,要构建的系统是什么;它的用户是谁,谁会用它,它要如何融入已有的IT环境。这个图的受众可以是开发团队的内部人员、外部的技术或非技术人员。即:构建的系统是什么谁会用它如何融入已有的IT环境怎么画中间是自己的系统,周围是用户和其它与之相互作用的系统。这个图的关键就是梳理清楚待建设系统的用户和高层次的依赖,梳理清楚了画下来只需要几分钟时间。2、容器图(Container Diagram)容器图是把语境图里待建设的系统做了一个展开。上图中,除了用户和外围系统,要建设的系统包括一个基于javaspring mvc的web应用提供系统的功能入口,基于xamarin架构的手机app提供手机端的功能入口,一个基于java的api应用提供服务,一个mysql数据库用于存储,各个应用之间的交互都在箭头线上写明了。看这张图的时候,不会去关注到图中是直角方框还是圆角方框,不会关注是实线箭头还是虚线箭头,甚至箭头的指向也没有引起太多注意。我们有许多的画图方式,都对框、线的含义做了定义,这就需要画图的人和看图的人都清晰的理解这些定义,才能读全图里的信息,而现实是,这往往是非常高的一个要求,所以,很多图只能看个大概的含义。用途这个图的受众可以是团队内部或外部的开发人员,也可以是运维人员。用途可以罗列为:展现了软件系统的整体形态体现了高层次的技术决策系统中的职责是如何分布的,容器间的是如何交互的告诉开发者在哪里写代码怎么画用一个框图来表示,内部可能包括名称、技术选择、职责,以及这些框图之间的交互,如果涉及外部系统,最好明确边界。3、组件图(Component Diagram)组件图是把某个容器进行展开,描述其内部的模块。用途这个图主要是给内部开发人员看的,怎么去做代码的组织和构建。其用途有:描述了系统由哪些组件/服务组成厘清了组件之间的关系和依赖为软件开发如何分解交付提供了框架4、类图(Code/Class Diagram)这个图很显然是给技术人员看的,比较常见,就不详细介绍了。案例分享下面是内部的一个实时数据工具的架构图。作为一个应该自描述的架构图,这里不多做解释了。如果有看不明白的,那肯定是还画的不够好。画好架构图可能有许多方法论,本篇主要介绍了C4这种方法,C4的理论也是不断进化的。但不论是哪种画图方法论,我们回到画图初衷,更好的交流,我们在画的过程中不必被条条框框所限制。简而言之,画之前想好:画图给谁看,看什么,怎么样不解释就看懂。作者简介:三画,阿里巴巴技术专家,梓敬、鹏升和余乐对此文亦有贡献。三画曾多年从事工作流引擎研发工作,现专注于高并发移动互联网应用的架构和开发,和本文贡献者均来自阿里巴巴零售通部门。目前零售通大量招Java开发,欢迎有志之士投简历到 lst-wireless@alibaba-inc.com,和我们一起共建智能分销网络,让百万小店拥抱DT时代。本文作者:三画阅读原文本文来自云栖社区合作伙伴“ 阿里技术”,如需转载请联系原作者。

April 12, 2019 · 1 min · jiezi

Dart编译技术在服务端的探索和应用

前言最近闲鱼技术团队在Flutter+Dart的多端一体化的基础上,实现了FaaS研发模式。Dart吸取了其它高级语言设计的精华,例如Smalltalk的Image技术、JVM的HotSpot和Dart编译技术又师出同门。由Dart实现的语言容器,它可以在启动速度、运行性能有不错的表现。Dart提供了AoT、JIT的编译方式,JIT拥有Kernel和AppJIT的运行模式,此外服务端应用有各自不同的运行特点,那么如何选择合理的编译方法来提升应用的性能?接下来我们用一些有典型特点的案例来引入我们在Dart编译方案的实践和思考。案例详情相应的,我们准备了短周期应用(EmptyMain & Fibonnacci & faas_tool),长周期应用(HttpServer)分别来说明不同的编译方法在各种场景下的性能表现测试环境参考#实验机1Mac OS X 10.14.3 Intel(R) Core(TM) i7-4770HQ CPU @ 2.20GHz * 4 / 16GB RAM#实验机2Linux x86_64Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz * 4 / 8GB RAM#Dart版本Dart Ver. 2.2.1-edge.eeb8fc8ccdcef46e835993a22b3b48c0a2ccc6f1 #Java HotSpot版本Java build 1.8.0_121-b13 Java HotSpot(TM) 64-Bit Server VM (build 25.121-b13, mixed mode)#GCC版本Apple LLVM version 10.0.1 (clang-1001.0.46.3)Target: x86_64-apple-darwin18.2.0Thread model: posix短周期应用Case1. EmptyMain例子是一个空函数实现,以此来评估语言平台本身的启动性能,我们使用默认参数编译一个snapshot#1.默认条件下的app-jit snapshot生成dart snapshot-kind=app-jit snapshot=empty_main.snapshot empty_main.dart测试结果作为现代高级语言Dart和Java在启动速度上在同一水平线C语言的启动速度是其它语言的20x,基本原因是C没有Java、Dart语言平台的RuntimeKernel和AppJIT方式运行有稳定的微小差异,总体AppJIT优于KernelCase2. Fibonacci数列我们分别用C、Java、Dart用递归实现Fibonacci(50)数列,来考察编译工作对性能的影响。long fibo(long n){ if(n < 2){ return n; } return fibo(n - 1) + fibo(n - 2);}AppJIT使用优化阈值实现激进优化,这样编译器在Training Run中立即获得生成Optimized代码#2.执行激进优化 dart –no-background-compilation \ –optimization-counter-threshold=1 \ –snapshot-kind=app-jit \ –snapshot=fibonacci.snapshot fibonacci.dart将Fibonacci编译成Kernel#3.生成Kernel snapshotdart –snapshot=fibonacci.snapshot fibonacci.dartAoT的Runtime不在Dart SDK里,需要自行编译AoT Runtime#4.AoT编译pkg/vm/tools/precompiler2 fibonacci.dart fibonacci.aot#5.AoT的方式执行out/ReleaseX64/dart_precompiled_runtime fibonacci.aot测试结果Dart JIT对比下,AppJIT在激进优化后性能稍好于Kernel,差距微小,编译的成本占比可以忽略不计Dart AoT模式下的性能约为JIT的1/6不到JIT运行模式下,HotSpot的执行性能最优,优于Dart AppJIT 25%以上包括C语言在内的AoT运行模式性能均低于JIT,Dart AppJIT性能优于25%问题AoT由于自身的特性(和语言无关),无法在运行时基于Profile实现代码优化,峰值性能在此场景下要差很多,但是为何Dart VM比HotSpot有25%的差距?接下来我们针对Fibonacci做进一步优化#6.编译器调优,调整递归内联深度dart –inlining_recursion_depth_threshold=5 fibonacci.snapshot 50#7.编译器调优,HotSpot调整递归内联深度java -XX:MaxRecursiveInlineLevel=5 Fabbonacci 50测试结果HotSpot VM性能全面领先于Dart VM;两者在最优情况下HotSpot VM的性能优于Dart 9%左右Dart VM 借助JIT调优,性能有大幅提升,相比默认情况有40%左右的提升Dart AppJIT 性能微弱领先Kernel也许也不难想象JVM HotSpot目前在服务器开发领域上的相对Dart成熟,相比HotSpot,DartVM的“出厂设置”比较保守,当然我们也可以大胆猜测,在服务端应用下应该还有除JIT的其它优化空间;和Case1相同,Kernel模式的性能依然低于AppJIT,主要原因是Kernel在运行前期需要把AST转换为堆数据结构、经历Compile、Compile Optimize等过程,而在适当Training run后的AppJIT snapshot在VM启动时以优化后的IL(中间代码)执行,但很快Kernel会追上App-jit,最后性能保持持平。有兴趣的读者可以参阅Vyacheslav Egorov Dart VM的文章。Case3. FaaS容器编译工具在前面我们提到过Dart版本的FaaS语言容器,为追求极致的研发体验,我们需要缩短用户Function打包到部署运行的时间。就语言容器层面而言,Dart提供的Snapshot技术可以大大提升启动速度,但是从用户Function到Snapshot(如下图)生成所产生的编译时间在不做优化的情况下超过10秒,还远远达不到极致体验的要求。我们这里通过一些测试,来寻找提升性能的途径faas_tool是一个完全用Dart编写的代码编译、生成工具。依托于faas_tool, Function的编写者不用关心如何打包、接入中间件,faas_tool提供一系列的模版及代码生成工具可以将用户的使用成本降低,此外faas_tool还提供了HotReload机制可以快速响应变更。这次我们提供了基于AoT、Kernel、AppJIT的用例来执行Function构建流程,分别记录时间消耗、中间产物大小、产物生成时间。为了验证在JIT场景下DartVM是否可通过调整Complier的行为带来性能提升,我们增加了JIT的测试分组测试结果AoT>AppJIT>kernel,其中AoT比优化后的AppJIT有3倍左右性能提升,性能是Source的1000倍JIT(Kernel, AppJIT)分组下,通过在运行时减少CompilerOptimize或暂停PGO可以提升性能很显然faas_tool最终选择了AoT编译,但是性能结果和Case2大相径庭,为了搞清楚原因我们进一步做一下CPU ProfileCPU ProfileAppJITDart App-jit模式 43%以上的时间参与编译,当然取消代码优化,可以让编译时间大幅下降,在优化情况下可以将这个比率下降到13%KernelKernel模式有61%以上的CPU时间参与编译工作, 如果关闭JIT优化代码生成,性能有15%左右提升,反之进行激进优化将有1倍左右的性能损耗AoT下的编译成本AoT模式下在运行时几乎编译和优化成本(CompileOptimized、CompileUnoptimized、CompileUnoptimized 占比为0),直接以目标平台的代码执行,因此性能要好很多。P.S. DartVM 的Profile模块在后期的版本升级更改了Tag命名, 有需要进一步了解的读者参考VM Tags附:DartVM调优和命令代码#8.模拟单核并执行激进优化 dart –no-background-compilation \ –optimization-counter-threshold=1 \ tmp/faas_tool.snapshot.kernel #9.JIT下关闭优化代码生成dart –optimization-counter-threshold=-1 \ tmp/faas_tool.snapshot.kernel #10. Appjit verbose snapshotdart –print_snapshot_sizes \ –print_snapshot_sizes_verbose \ –deterministic \ –snapshot-kind=app-jit \ –snapshot=/tmp/faas_tool.snapshot faas_tool.dart #11.Profile CPU 和 timeline dart –profiler=true \ –startup_timeline=true \ –timeline_dir=/tmp \ –enable-vm-service \ –pause-isolates-on-exit faas_tool.snapshot长周期应用HttpServer我们用一个简单的Dart版的HttpServer作为典型长周期应用的测试用例,该用例中有JsonToObject、ObjectToJson的转换,然后response输出。我们分别用Source、Kernel以及AppJIT的方式在一定的并发量下运行一段时间void processReq(HttpRequest request){ try{ final List<Map<String,dynamic>> buf = <Map<String,dynamic>>[]; final Boss boss = new Boss(numOfEmployee: 10); //Json反序列化对象 getHeadCount(max: 20).forEach((hc){ boss.hire(hc.idType, hc.docId); buf.add(hc.toJson()); }); request.response.headers.add(‘cal’,’${boss.calc()}’); //Json对象转JsonString request.response.write(jsonEncode(buf)); request.response.close() .then((v) => counter_success ++) .timeout(new Duration(seconds:3)) .catchError((e) => counter_fail ++)); } catch(e){ request.response.statusCode = 500; counter_fail ++; request.response.close(); }}测试结果 上面三种无论是何种方式启动,最终的运行时性能趋向一致,编译成本在后期可以忽略不计,这也是JIT的运行特点在AppJIT模式下在应用启动起初就有接近峰值的性能,即使在Kernel模式下也需要时间预热达到峰值性能,Source模式下VM启动需要2秒以上,因此需要相对更长时间达到峰值性能。从另一方面看应用很快完成了预热,不久达到了峰值性能P.S. 长周期的应用Optimize Compiler会经过Optimize->Deoptimize->Reoptimize的过程, 由于此案例比较简单,没体现Deoptimize到Reoptimize的表现附:VM调优脚本#12.调整当前isolate的新生代大小,默认2M最大32M的新生代大小造成频繁的YGCdart –new_gen_semi_max_size=512 \ –new_gen_semi_initial_size=512 \ http_server.dart \ –interval=2 总结和展望Dart编译方式的选择编译成本为主导的应用,应优先考虑AoT来提高应用性能长周期的应用在启动后期编译成本可忽略,应该选择JIT方式并开启Optimize Compiler,让优化器介入长周期的应用可以选择Kernel的方式来提升启动速度,通过AppJIT的方式进一步缩短warmup时间AppJIT减少了编译预热的成本,这个特性非常适合对一些高并发应用在线扩容。Kernel作为Dart编译技术的前端,其平台无关性将继续作为整个Dart编译工具链的基础。在FaaS构建方案的选择通过CPU Profile得出faas_tool是一个编译成本主导的应用,最终选择了AoT编译方案,结果大大提升了语言容器的构建的构建速度,很好满足了faas对开发效率的诉求仍需改进的地方从JIT性能表现来看,DartVM JIT的运行时性和HotSpot相比有提升余地,由于Dart语言作为服务端开发的历史不长,也许随着Dart在服务端的技术应用全面推广,相信DarVM在编译器后端技术上对服务器级的处理器架构做更多优化。本文作者:闲鱼技术-无浩阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 11, 2019 · 2 min · jiezi

30 分钟快速入门 Docker 教程

原文地址:梁桂钊的博客博客地址:http://blog.720ui.com欢迎关注公众号:「服务端思维」。一群同频者,一起成长,一起精进,打破认知的局限性。30 分钟快速入门 Docker 教程一、欢迎来到 Docker 世界1. Docker 与虚拟化在没有 Docker 的时代,我们会使用硬件虚拟化(虚拟机)以提供隔离。这里,虚拟机通过在操作系统上建立了一个中间虚拟软件层 Hypervisor ,并利用物理机器的资源虚拟出多个虚拟硬件环境来共享宿主机的资源,其中的应用运行在虚拟机内核上。但是,虚拟机对硬件的利用率存在瓶颈,因为虚拟机很难根据当前业务量动态调整其占用的硬件资源,因此容器化技术得以流行。其中,Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上。Docker 容器不使用硬件虚拟化,它的守护进程是宿主机上的一个进程,换句话说,应用直接运行在宿主机内核上。因为容器中运行的程序和计算机的操作系统之间没有额外的中间层,没有资源被冗余软件的运行或虚拟硬件的模拟而浪费掉。Docker 的优势不仅如此,我们来比较一番。特性Docker虚拟机启动速度秒级分钟级交付/部署开发、测试、生产环境一致无成熟体系性能近似物理机性能损耗大体量极小(MB)较大(GB)迁移/扩展跨平台,可复制较为复杂2. 镜像、容器和仓库Docker 由镜像(Image)、容器(Container)、仓库(Repository) 三部分组成。Docker 的镜像可以简单的类比为电脑装系统用的系统盘,包括操作系统,以及必要的软件。例如,一个镜像可以包含一个完整的 centos 操作系统环境,并安装了 Nginx 和 Tomcat 服务器。注意的是,镜像是只读的。这一点也很好理解,就像我们刻录的系统盘其实也是可读的。我们可以使用 docker images 来查看本地镜像列表。Docker 的容器可以简单理解为提供了系统硬件环境,它是真正跑项目程序、消耗机器资源、提供服务的东西。例如,我们可以暂时把容器看作一个 Linux 的电脑,它可以直接运行。那么,容器是基于镜像启动的,并且每个容器都是相互隔离的。注意的是,容器在启动的时候基于镜像创建一层可写层作为最上层。我们可以使用 docker ps -a 查看本地运行过的容器。Docker 的仓库用于存放镜像。这一点,和 Git 非常类似。我们可以从中心仓库下载镜像,也可以从自建仓库下载。同时,我们可以把制作好的镜像 commit 到本地,然后 push 到远程仓库。仓库分为公开仓库和私有仓库,最大的公开仓库是官方仓库 Dock Hub,国内的公开仓库也有很多选择,例如阿里云等。3. Docker 促使开发流程变更笔者认为,Docker 对开发流程的影响在于使环境标准化。例如,原来我们存在三个环境:开发(日常)环境、测试环境、生产环境。这里,我们对于每个环境都需要部署相同的软件、脚本和运行程序,如图所示。事实上,对于启动脚本内容都是一致的,但是没有统一维护,经常会出问题。此外,对于运行程序而言,如果所依赖的底层运行环境不一致,也会造成困扰和异常。现在,我们通过引入 Docker 之后,我们只需要维护一个 Docker 镜像。换句话说,多套环境,一个镜像,实现系统级别的一次构建到处运行。此时,我们把运行脚本标准化了,把底层软件镜像化了,然后对于相同的将要部署的程序实行标准化部署。因此,Docker 为我们提供了一个标准化的运维模式,并固化运维步骤和流程。通过这个流程的改进,我们更容易实现 DevOps 的目标,因为我们的镜像生成后可以跑在任何系统,并快速部署。此外,使用 Docker 的很大动力是基于 Docker 实现弹性调度,以更充分地利用机器资源,节省成本。哈哈,笔者在使用 Docker 过程中,还发现了一些很棒的收益点,例如我们发布回滚的时候只需要切换 TAG 并重启即可。还比如,我们对环境升级,也只需要升级基础镜像,那么新构建的应用镜像,自动会引用新的版本。(欢迎补充~~~)二、从搭建 Web 服务器开始说起1. 环境先行,安装 Docker现在,我们需要安装以下步骤安装 Docker。注册帐号:在 https://hub.docker.com/ 注册账号。下载安装官方下载地址:(Mac):https://download.docker.com/mac/stable/Docker.dmg阿里云下载地址(Mac):http://mirrors.aliyun.com/docker-toolbox/mac/docker-for-mac/阿里云下载地址(Windows): http://mirrors.aliyun.com/docker-toolbox/windows/docker-for-windows/安装指南这里,双击刚刚下载的 Doker.dmg 安装包进行安装。安装完成后启动, Mac 顶部导航栏出现了一个图标,通过菜单可以进行 docker 配置和退出等操作。官方指南:https://docs.docker.com/install/阿里云指南(Linux):https://yq.aliyun.com/articles/110806?spm=5176.8351553.0.0.468b1991jdT95t设置加速服务市面上有很多加速服务的提供商,如:DaoCloud,阿里云等。这里,笔者使用的是阿里云。(注意的是,笔者操作系统是 Mac,其他操作系列参见阿里云操作文档) 右键点击桌面顶栏的 docker 图标,选择 Preferences ,在 Daemon 标签(Docker 17.03 之前版本为 Advanced 标签)下的 Registry mirrors 列表中将https://xxx.mirror.aliyuncs.com 加到"registry-mirrors"的数组里,点击 Apply & Restart 按钮,等待 Docker 重启并应用配置的镜像加速器。阿里云操作文档:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors查看版本至此,我们已经安装完成了。这里,我们来查看版本。docker version查看结果,如下所示。2. 实干派,从搭建 Web 服务器开始我们作为实干派,那么先来搭建一个 Web 服务器吧。然后,笔者带你慢慢理解这个过程中,做了什么事情。首先,我们需要拉取 centos 镜像。docker run -p 80 –name web -i -t centos /bin/bash紧接着,我们安装 nginx 服务器,执行以下命令:rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm安装完 Nginx 源后,就可以正式安装 Nginx 了。yum install -y nginx至此,我们再输入 whereis nginx 命令就可以看到安装的路径了。最后,我们还需要将 Nginx 跑起来。nginx现在,我们执行 ctrl + P + Q 切换到后台。然后,通过 docker ps -a 来查看随机分配的端口。这里,笔者分配的端口是 32769 ,那么通过浏览器访问 http://127.0.0.1:32769 即可。大功告成,哈哈哈~3. 复盘理解全过程现在,我们来理解下这个流程。首先,我们输入 docker run -p 80 –name web -i -t centos /bin/bash 命令会运行交互式容器,其中 -i 选项告诉 Docker 容器保持标准输入流对容器开放,即使容器没有终端连接,另一个 -t 选项告诉 Docker 为容器分配一个虚拟终端,以便于我们接下来安装 Nginx 服务器。(笔者备注:Docker 还支持输入 -d 选项告诉 Docker 在后台运行容器的守护进程)Docker 会为我们创建的每一个容器自动生成一个随机的名称。事实上,这种方式虽然便捷,但是可读性很差,并且对我们后期维护的理解成本会比较大。因此,我们通过 –name web 选项告诉 Docker 创建一个名称是 web 的容器。此外,我们通过 -p 80 告诉 Docker 开放 80 端口,那么, Nginx 才可以对外通过访问和服务。但是,我们的宿主机器会自动做端口映射,比如上面分配的端口是 32769 ,注意的是,如果关闭或者重启,这个端口就变了,那么怎么解决固定端口的问题,笔者会在后面详细剖析和带你实战。这里,还有一个非常重要的知识点 docker run 。Docker 通过 run 命令来启动一个新容器。Docker 首先在本机中寻找该镜像,如果没有安装,Docker 在 Docker Hub 上查找该镜像并下载安装到本机,最后 Docker 创建一个新的容器并启动该程序。但是,当第二次执行 docker run 时,因为 Docker 在本机中已经安装该镜像,所以 Docker 会直接创建一个新的容器并启动该程序。注意的是,docker run 每次使用都会创建一个新的容器,因此,我们以后再次启动这个容器时,只需要使用命令 docker start 即可。这里, docker start 的作用在用重新启动已存在的镜像,而docker run 包含将镜像放入容器中 docker create ,然后将容器启动 docker start ,如图所示。现在,我们可以在上面的案例的基础上,通过 exit 命令关闭 Docker 容器。当然,如果我们运行的是后台的守护进程,我们也可以通过 docker stop web 来停止。注意的是,docker stop 和 docker kill 略有不同,docker stop 发送 SIGTERM 信号,而 docker kill 发送SIGKILL 信号。然后,我们使用 docker start 重启它。docker start webDocker 容器重启后会沿用 docker run 命令指定的参数来运行,但是,此时它还是后台运行的。我们必须通过 docker attach 命令切换到运行交互式容器。docker attach web4. 不止如此,还有更多命令Docker 提供了非常丰富的命令。所谓一图胜千言,我们可以从下面的图片了解到很多信息和它们之前的用途。(可以直接跳过阅读,建议收藏,便于扩展阅读)如果希望获取更多信息,可以阅读官方使用文档。CommandDescriptiondocker attachAttach local standard input, output, and error streams to a running containerdocker buildBuild an image from a Dockerfiledocker builderManage buildsdocker checkpointManage checkpointsdocker commitCreate a new image from a container’s changesdocker configManage Docker configsdocker containerManage containersdocker cpCopy files/folders between a container and the local filesystemdocker createCreate a new containerdocker deployDeploy a new stack or update an existing stackdocker diffInspect changes to files or directories on a container’s filesystemdocker engineManage the docker enginedocker eventsGet real time events from the serverdocker execRun a command in a running containerdocker exportExport a container’s filesystem as a tar archivedocker historyShow the history of an imagedocker imageManage imagesdocker imagesList imagesdocker importImport the contents from a tarball to create a filesystem imagedocker infoDisplay system-wide informationdocker inspectReturn low-level information on Docker objectsdocker killKill one or more running containersdocker loadLoad an image from a tar archive or STDINdocker loginLog in to a Docker registrydocker logoutLog out from a Docker registrydocker logsFetch the logs of a containerdocker manifestManage Docker image manifests and manifest listsdocker networkManage networksdocker nodeManage Swarm nodesdocker pausePause all processes within one or more containersdocker pluginManage pluginsdocker portList port mappings or a specific mapping for the containerdocker psList containersdocker pullPull an image or a repository from a registrydocker pushPush an image or a repository to a registrydocker renameRename a containerdocker restartRestart one or more containersdocker rmRemove one or more containersdocker rmiRemove one or more imagesdocker runRun a command in a new containerdocker saveSave one or more images to a tar archive (streamed to STDOUT by default)docker searchSearch the Docker Hub for imagesdocker secretManage Docker secretsdocker serviceManage servicesdocker stackManage Docker stacksdocker startStart one or more stopped containersdocker statsDisplay a live stream of container(s) resource usage statisticsdocker stopStop one or more running containersdocker swarmManage Swarmdocker systemManage Dockerdocker tagCreate a tag TARGET_IMAGE that refers to SOURCE_IMAGEdocker topDisplay the running processes of a containerdocker trustManage trust on Docker imagesdocker unpauseUnpause all processes within one or more containersdocker updateUpdate configuration of one or more containersdocker versionShow the Docker version informationdocker volumeManage volumesdocker waitBlock until one or more containers stop, then print their exit codes官方阅读链接:https://docs.docker.com/engine/reference/commandline/docker/5. 进阶:仓库与软件安装的简化还记得笔者在文章开头介绍的「镜像、容器和仓库」吗?Docker 的仓库用于存放镜像。我们可以从中心仓库下载镜像,也可以从自建仓库下载。同时,我们可以把制作好的镜像从本地推送到远程仓库。首先,笔者先引入一个知识点:Docker 的镜像就是它的文件系统,一个镜像可以放在另外一个镜像的上层,那么位于下层的就是它的父镜像。所以,Docker 会存在很多镜像层,每个镜像层都是只读的,并且不会改变。当我们创建一个新的容器时,Docker 会构建出一个镜像栈,并在栈的最顶层添加一个读写层,如图所示。现在,我们可以通过 docker images 命令查看本地的镜像。docker images查询结果,如图所示。这里,对几个名词解释一下含义。REPOSITORY:仓库名称。TAG: 镜像标签,其中 lastest 表示最新版本。注意的是,一个镜像可以有多个标签,那么我们就可以通过标签来管理有用的版本和功能标签。IMAGE ID :镜像唯一ID。CREATED :创建时间。SIZE :镜像大小。那么,如果第一次我们通过 docker pull centos:latest 拉取镜像,那么当我们执行 docker run -p 80 –name web -i -t centos /bin/bash 时,它就不会再去远程获取了,因为本机中已经安装该镜像,所以 Docker 会直接创建一个新的容器并启动该程序。事实上,官方已经提供了安装好 Nginx 的镜像,我们可以直接使用。现在,我们通过拉取镜像的方式重新构建一个 Web 服务器。首先,我们通过 docker search 来查找镜像。我们获取到 Nginx 的镜像清单。docker search nginx补充一下,我们也可以通过访问 Docker Hub (https://hub.docker.com/)搜索仓库,那么 star 数越多,说明它越靠谱,可以放心使用。现在,我们通过 docker pull nginx 拉取最新的 Nginx 的镜像。当然,我们也可以通过 docker pull nginx:latest 来操作。docker pull nginx然后,我们创建并运行一个容器。与前面不同的是,我们通过 -d 选项告诉 Docker 在后台运行容器的守护进程。并且,通过 8080:80 告诉 Docker 8080 端口是对外开放的端口,80 端口对外开放的端口映射到容器里的端口号。docker run -p 8080:80 -d –name nginx nginx我们再通过 docker ps -a 来查看,发现容器已经后台运行了,并且后台执行了 nginx 命令,并对外开放 8080 端口。因此,通过浏览器访问 http://127.0.0.1:8080 即可。6. 其他选择,使用替代注册服务器Docker Hub 不是软件的唯一来源,我们也可以切换到国内的其他替代注册服务器,例如阿里云。我们可以登录 https://cr.console.aliyun.com 搜索,并拉取公开的镜像。现在,我们输入 docker pull 命令进行拉取。docker pull registry.cn-hangzhou.aliyuncs.com/qp_oraclejava/orackejava:8u172_DCEVM_HOTSWAPAGEN_JCE这里,笔者继续补充一个知识点:注册服务器的地址。事实上,注册服务器的地址是有一套规范的。完整格式是:仓库主机/容器短名[:标签]。这里,仓库主机是 registry.cn-hangzhou.aliyuncs.com,用户名是 qp_oraclejava,容器短名是 orackejava,标签名是 8u172_DCEVM_HOTSWAPAGEN_JCE。事实上,我们上面通过 docker pull centos:latest 拉取镜像,相当于 docker pull registry.hub.docker.com/centos:latest 。三、构建我的镜像通过上面的学习,笔者相信你已经对 Docker 使用有了一个大致的了解,就好比我们通过 VMware 安装了一个系统,并让它跑了起来,那么我们就可以在这个 Linux 系统(CentOS 或者 Ubuntu ) 上面工作我们想要的任何事情。事实上,我们还会经常把我们安装好的 VMware 系统进行快照备份并实现克隆来满足我们下次快速的复制。这里,Docker 也可以构建定制内容的 Docker 镜像,例如上面我们使用官方提供的安装好 Nginx 的 Docker 镜像。注意的是,我们通过基于已有的基础镜像,在上面添加镜像层的方式构建新镜像而已。总结一下,Docker 提供自定义镜像的能力,它可以让我们保存对基础镜像的修改,并再次使用。那么,我们就可以把操作系统、运行环境、脚本和程序打包在一起,并在宿主机上对外提供服务。Docker 构建镜像有两种方式,一种方式是使用 docker commit 命令,另外一种方式使用 docker build 命令和 Dockerfile 文件。其中,不推荐使用 docker commit 命令进行构建,因为它没有使得整个流程标准化,因此,在企业的中更加推荐使用 docker build 命令和 Dockerfile 文件来构建我们的镜像。我们使用Dockerfile 文件可以让构建镜像更具备可重复性,同时保证启动脚本和运行程序的标准化。1. 构建第一个 Dockerfile 文件现在,我们继续实战。这里,我们把一开始搭建的 Web 服务器构建一个镜像。首先,我们需要创建一个空的 Dokcerfile 文件。mkdir dockerfile_testcd dockerfile_test/touch Dockerfilenano Dockerfile紧接着,我们需要编写一个 Dockerfile 文件,代码清单如下FROM centos:7MAINTAINER LiangGzone “lianggzone@163.com"RUN rpm -ivh http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpmRUN yum install -y nginxEXPOSE 80最后,我们通过 docker build 命令进行构建。docker build -t=“lianggzone/nginx_demo:v1” .现在, 我们来通过 docker images 看下我们的新镜像吧。2. 理解 Dockerfile 全过程哇,我们通过编写一个 Dockerfile 文件顺利构建了一个新的镜像。这个过程简单得让人无法相信。现在,让我们来理解一下这个全过程吧。首先, FROM centos:7 是 Dockerfile 必须要的第一步,它会从一个已经存在的镜像运行一个容器,换句话说,Docker 需要依赖于一个基础镜像进行构建。这里,我们指定 centos 作为基础镜像,它的版本是 7 (CentOS 7)。然后,我们通过 MAINTAINER LiangGzone “lianggzone@163.com” 指定该镜像的作者是 LiangGzone,邮箱是 lianggzone@163.com。这有助于告诉使用者它的作者和联系方式。接着,我们执行两个 RUN 指令进行 Nginx 的下载安装,最后通过 EXPOSE 80 暴露 Dokcer 容器的 80 端口。注意的是,Docker 的执行顺序是从上而下执行的,所以我们要明确整个流程的执行顺序。除此之外,Docker 在执行每个指令之后都会创建一个新的镜像层并且进行提交。我们使用 docker build 命令进行构建,指定 - t 告诉 Docker 镜像的名称和版本。注意的是,如果没有指定任何标签,Docker 将会自动为镜像设置一个 lastest 标签。还有一点,我们最后还有一个 . 是为了让 Docker 到当前本地目录去寻找 Dockerfile 文件。注意的是,Docker 会在每一步构建都会将结果提交为镜像,然后将之前的镜像层看作缓存,因此我们重新构建类似的镜像层时会直接复用之前的镜像。如果我们需要跳过,可以使用 –no-cache 选项告诉 Docker 不进行缓存。3. Dockerfile 指令详解Dockerfile 提供了非常多的指令。笔者这里特别整理了一份清单,建议收藏查看。官方地址:https://docs.docker.com/engine/reference/builder/#usage指令辨别一:RUN、CMD、ENTRYPOINTRUN 、 CMD 、 ENTRYPOINT 三个指令的用途非常相识,不同在于,RUN 指令是在容器被构建时运行的命令,而CMD 、 ENTRYPOINT 是启动容器时执行 shell 命令,而 RUN 会被 docker run 命令覆盖,但是 ENTRYPOINT 不会被覆盖。事实上,docker run 命令指定的任何参数都会被当作参数再次传递给 ENTRYPOINT 指令。CMD 、 ENTRYPOINT 两个指令之间也可以一起使用。例如,我们 可以使用 ENTRYPOINT 的 exec 形式设置固定的默认命令和参数,然后使用任一形式的 CMD 来设置可能更改的其他默认值。FROM ubuntuENTRYPOINT [“top”, “-b”]CMD ["-c”]指令辨别二:ADD、COPYADD 、 COPY 指令用法一样,唯一不同的是 ADD 支持将归档文件(tar, gzip, bzip2, etc)做提取和解压操作。注意的是,COPY 指令需要复制的目录一定要放在 Dockerfile 文件的同级目录下。4. 将镜像推送到远程仓库远程仓库:Docker Hub 镜像构建完毕之后,我们可以将它上传到 Docker Hub 上面。首先,我们需要通过 docker login 保证我们已经登录了。紧接着,我们使用 docker push 命令进行推送。docker push lianggzone/nginx_demo:v1这里,我们了解下它的使用,格式是 docker push [OPTIONS] NAME[:TAG] ,其中,笔者设置 NAME 是 lianggzone/nginx_demo,TAG 是 v1。 (笔者注:推送 Docker Hub 速度很慢,耐心等待) 最后,上传完成后访问:https://hub.docker.com/u/lianggzone/,如图所示。远程仓库:阿里云同时,我们也可以使用国内的仓库,比如阿里云。首先,在终端中输入访问凭证,登录 Registry 实例。如果你不知道是哪个,可以访问 https://cr.console.aliyun.com/cn-hangzhou/instances/credentials。docker login –username=帐号 registry.cn-hangzhou.aliyuncs.com现在,将镜像推送到阿里云镜像仓库。其中, docker tag [IMAGE_ID] registry.cn-hangzhou.aliyuncs.com/[命名空间]/[镜像名称]:[版本] 和 docker push registry.cn-hangzhou.aliyuncs.com/[命名空间]/[镜像名称]:[版本] 命令的使用如下所示。docker tag 794c07361565 registry.cn-hangzhou.aliyuncs.com/lianggzone/nginx_demo:v1docker push registry.cn-hangzhou.aliyuncs.com/lianggzone/nginx_demo:v1最后,上传完成后访问:https://cr.console.aliyun.com/cn-hangzhou/instances/repositories,如图所示。5. Dockerfile 的 Github 源码地址这里,附上我整理的 Dockerfile 的仓库。后面,笔者会陆续更新用到的一些常用文件,欢迎 star 关注。https://github.com/lianggzone/dockerfile-images附:参考资料《Docker实战》《第一本Docker书》Docker 命令参考文档Dockerfile 镜像构建参考文档(完,转载请注明作者及出处。)写在末尾【服务端思维】:我们一起聊聊服务端核心技术,探讨一线互联网的项目架构与实战经验。同时,拥有众多技术大牛的「后端圈」大家庭,期待你的加入,一群同频者,一起成长,一起精进,打破认知的局限性。更多精彩文章,尽在「服务端思维」! ...

April 10, 2019 · 5 min · jiezi

SpringBoot | 自动配置原理

微信公众号:一个优秀的废人。如有问题,请后台留言,反正我也不会听。前言这个月过去两天了,这篇文章才跟大家见面,最近比较累,大家见谅下。下班后闲着无聊看了下 SpringBoot 中的自动配置,把我的理解跟大家说下。配置文件能写什么?相信接触过 SpringBoot 的朋友都知道 SpringBoot 有各种 starter 依赖,想要什么直接勾选加进来就可以了。想要自定义的时候就直接在配置文件写自己的配置就好。但你们有没有困惑,为什么 SpringBoot 如此智能,到底配置文件里面能写什么呢?带着这个疑问,我翻了下 SpringBoot 官网看到这么一些配置样例:发现 SpringBoot 可配置的东西非常多,上图只是节选。有兴趣的查看这个网址:https://docs.spring.io/spring-boot/docs/2.1.3.RELEASE/reference/htmlsingle/#boot-features-external-config-yaml自动配置原理这里我拿之前创建过的 SpringBoot 来举例讲解 SpringBoot 的自动配置原理,首先看这么一段代码:@SpringBootApplicationpublic class JpaApplication { public static void main(String[] args) { SpringApplication.run(JpaApplication.class, args); }}毫无疑问这里只有 @SpringBootApplication 值得研究,进入 @SpringBootApplication 的源码:SpringBoot 启动的时候加载主配置类,开启了自动配置功能 @EnableAutoConfiguration,再进入 @EnableAutoConfiguration 源码:发现最重要的就是 @Import(AutoConfigurationImportSelector.class) 这个注解,其中的 AutoConfigurationImportSelector 类的作用就是往 Spring 容器中导入组件,我们再进入这个类的源码,发现有这几个方法:/*** 方法用于给容器中导入组件**/@Overridepublic String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry( autoConfigurationMetadata, annotationMetadata); // 获取自动配置项 return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}// 获取自动配置项protected AutoConfigurationEntry getAutoConfigurationEntry( AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); List < String > configurations = getCandidateConfigurations(annotationMetadata, attributes); // 获取一个自动配置 List ,这个 List 就包含了所有自动配置的类名 configurations = removeDuplicates(configurations); Set < String > exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions);}// 获取一个自动配置 List ,这个 List 就包含了所有的自动配置的类名protected List < String > getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { // 通过 getSpringFactoriesLoaderFactoryClass 获取默认的 EnableAutoConfiguration.class 类名,传入 loadFactoryNames 方法 List < String > configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()); Assert.notEmpty(configurations, “No auto configuration classes found in META-INF/spring.factories. If you " + “are using a custom packaging, make sure that file is correct.”); return configurations;}// 默认的 EnableAutoConfiguration.class 类名protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class;}代码注释很清楚:首先注意到 selectImports 方法,其实从方法名就能看出,这个方法用于给容器中导入组件,然后跳到 getAutoConfigurationEntry 方法就是用于获取自动配置项的。再来进入 getCandidateConfigurations 方法就是 获取一个自动配置 List ,这个 List 就包含了所有的自动配置的类名 。再进入 SpringFactoriesLoader 类的 loadFactoryNames 方法,跳转到 loadSpringFactories 方法发现 ClassLoader 类加载器指定了一个 FACTORIES_RESOURCE_LOCATION 常量。然后利用PropertiesLoaderUtils 把 ClassLoader 扫描到的这些文件的内容包装成 properties 对象,从 properties 中获取到 EnableAutoConfiguration.class 类(类名)对应的值,然后把他们添加在容器中。public static List < String > loadFactoryNames(Class < ? > factoryClass, @Nullable ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());}private static Map < String, List < String >> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap < String, String > result = cache.get(classLoader); if (result != null) { return result; } try { // 扫描所有 jar 包类路径下 META-INF/spring.factories Enumeration < URL > urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap < > (); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); // 把扫描到的这些文件的内容包装成 properties 对象 Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry < ? , ? > entry : properties.entrySet()) { String factoryClassName = ((String) entry.getKey()).trim(); for (String factoryName: StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { // 从 properties 中获取到 EnableAutoConfiguration.class 类(类名)对应的值,然后把他们添加在容器中 result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException(“Unable to load factories from location [” + FACTORIES_RESOURCE_LOCATION + “]”, ex); }}点击 FACTORIES_RESOURCE_LOCATION 常量,我发现它指定的是 jar 包类路径下 META-INF/spring.factories 文件:public static final String FACTORIES_RESOURCE_LOCATION = “META-INF/spring.factories”;将类路径下 META-INF/spring.factories 里面配置的所有 EnableAutoConfiguration 的值加入到了容器中,所有的 EnableAutoConfiguration 如下所示:注意到 EnableAutoConfiguration 有一个 = 号,= 号后面那一串就是这个项目需要用到的自动配置类。# Auto Configureorg.springframework.boot.autoconfigure.EnableAutoConfiguration=\org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\org.springframework.boot.autoconfigure.cloud.CloudServiceConnectorsAutoConfiguration,\org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveDataAutoConfiguration,\org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.cassandra.CassandraRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.jdbc.JdbcRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.ldap.LdapRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.neo4j.Neo4jDataAutoConfiguration,\org.springframework.boot.autoconfigure.data.neo4j.Neo4jRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.solr.SolrRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\org.springframework.boot.autoconfigure.elasticsearch.rest.RestClientAutoConfiguration,\org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\org.springframework.boot.autoconfigure.gson.GsonAutoConfiguration,\org.springframework.boot.autoconfigure.h2.H2ConsoleAutoConfiguration,\org.springframework.boot.autoconfigure.hateoas.HypermediaAutoConfiguration,\org.springframework.boot.autoconfigure.hazelcast.HazelcastAutoConfiguration,\org.springframework.boot.autoconfigure.hazelcast.HazelcastJpaDependencyAutoConfiguration,\org.springframework.boot.autoconfigure.http.HttpMessageConvertersAutoConfiguration,\org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration,\org.springframework.boot.autoconfigure.influx.InfluxDbAutoConfiguration,\org.springframework.boot.autoconfigure.info.ProjectInfoAutoConfiguration,\org.springframework.boot.autoconfigure.integration.IntegrationAutoConfiguration,\org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\org.springframework.boot.autoconfigure.groovy.template.GroovyTemplateAutoConfiguration,\org.springframework.boot.autoconfigure.jersey.JerseyAutoConfiguration,\org.springframework.boot.autoconfigure.jooq.JooqAutoConfiguration,\org.springframework.boot.autoconfigure.jsonb.JsonbAutoConfiguration,\org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\org.springframework.boot.autoconfigure.ldap.embedded.EmbeddedLdapAutoConfiguration,\org.springframework.boot.autoconfigure.ldap.LdapAutoConfiguration,\org.springframework.boot.autoconfigure.liquibase.LiquibaseAutoConfiguration,\org.springframework.boot.autoconfigure.mail.MailSenderAutoConfiguration,\org.springframework.boot.autoconfigure.mail.MailSenderValidatorAutoConfiguration,\org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\org.springframework.boot.autoconfigure.mustache.MustacheAutoConfiguration,\org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\org.springframework.boot.autoconfigure.quartz.QuartzAutoConfiguration,\org.springframework.boot.autoconfigure.reactor.core.ReactorCoreAutoConfiguration,\org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration,\org.springframework.boot.autoconfigure.security.servlet.SecurityRequestMatcherProviderAutoConfiguration,\org.springframework.boot.autoconfigure.security.servlet.UserDetailsServiceAutoConfiguration,\org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration,\org.springframework.boot.autoconfigure.security.reactive.ReactiveSecurityAutoConfiguration,\org.springframework.boot.autoconfigure.security.reactive.ReactiveUserDetailsServiceAutoConfiguration,\org.springframework.boot.autoconfigure.sendgrid.SendGridAutoConfiguration,\org.springframework.boot.autoconfigure.session.SessionAutoConfiguration,\org.springframework.boot.autoconfigure.security.oauth2.client.servlet.OAuth2ClientAutoConfiguration,\org.springframework.boot.autoconfigure.security.oauth2.client.reactive.ReactiveOAuth2ClientAutoConfiguration,\org.springframework.boot.autoconfigure.security.oauth2.resource.servlet.OAuth2ResourceServerAutoConfiguration,\org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerAutoConfiguration,\org.springframework.boot.autoconfigure.solr.SolrAutoConfiguration,\org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration,\org.springframework.boot.autoconfigure.task.TaskSchedulingAutoConfiguration,\org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\org.springframework.boot.autoconfigure.validation.ValidationAutoConfiguration,\org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration,\org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration,\org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration,\org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration,\org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration,\org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration,\org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration,\org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration,\org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration,\org.springframework.boot.autoconfigure.webservices.client.WebServiceTemplateAutoConfiguration每一个这样的 xxxAutoConfiguration 类都是容器中的一个组件,都加入到容器中,用他们来做自动配置。上述的每一个自动配置类都有自动配置功能,也可在配置文件中自定义配置。举例说明 Http 编码自动配置原理@Configuration // 表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件@EnableConfigurationProperties(HttpEncodingProperties.class) // 启动指定类的 ConfigurationProperties 功能;将配置文件中对应的值和 HttpEncodingProperties 绑定起来;并把 HttpEncodingProperties 加入到 ioc 容器中@ConditionalOnWebApplication // Spring 底层 @Conditional 注解,根据不同的条件,如果满足指定的条件,整个配置类里面的配置就会生效;判断当前应用是否是 web 应用,如果是,当前配置类生效@ConditionalOnClass(CharacterEncodingFilter.class) // 判断当前项目有没有这个类 CharacterEncodingFilter;SpringMVC 中进行乱码解决的过滤器;@ConditionalOnProperty(prefix = “spring.http.encoding”, value = “enabled”, matchIfMissing = true) // 判断配置文件中是否存在某个配置 spring.http.encoding.enabled;如果不存在,判断也是成立的// 即使我们配置文件中不配置 pring.http.encoding.enabled=true,也是默认生效的;public class HttpEncodingAutoConfiguration { // 已经和 SpringBoot 的配置文件建立映射关系了 private final HttpEncodingProperties properties; //只有一个有参构造器的情况下,参数的值就会从容器中拿 public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) { this.properties = properties; } @Bean // 给容器中添加一个组件,这个组件的某些值需要从 properties 中获取 @ConditionalOnMissingBean(CharacterEncodingFilter.class) public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; }自动配置类做了什么不在这里赘述,请见上面代码。所有在配置文件中能配置的属性都是在 xxxxProperties 类中封装的;配置文件能配置什么就可以参照某个功能对应的这个属性类,例如上述提到的 @EnableConfigurationProperties(HttpProperties.class) ,我们打开 HttpProperties 文件源码节选:@ConfigurationProperties(prefix = “spring.http”)public class HttpProperties { public static class Encoding { public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8; /** * Charset of HTTP requests and responses. Added to the “Content-Type” header if * not set explicitly. / private Charset charset = DEFAULT_CHARSET; /* * Whether to force the encoding to the configured charset on HTTP requests and * responses. */ private Boolean force;}在上面可以发现里面的属性 charset 、force 等,都是我们可以在配置文件中指定的,它的前缀就是 spring.http.encoding 如:另外,如果配置文件中有配该属性就取配置文件的,若无就使用 XxxxProperties.class 文件的默认值,比如上述代码的 Charset 属性,如果不配那就使用 UTF-8 默认值。总结1. SpringBoot 启动会加载大量的自动配置类2. 我们看我们需要的功能有没有 SpringBoot 默认写好的自动配置类;3. 我们再来看这个自动配置类中到底配置了哪些组件 ( 只要我们要用的组件有,我们就不需要再来配置,若没有,我们可能就要考虑自己写一个配置类让 SpringBoot 扫描了)4. 给容器中自动配置类添加组件的时候,会从 properties 类中获取某些属性。我们就可以在配置文件中指定这些属性的值;xxxxAutoConfigurartion 自动配置类的作用就是给容器中添加组件xxxxProperties 的作用就是封装配置文件中相关属性至此,总算弄明白了 SpringBoot 的自动配置原理。我水平优先,如有不当之处,敬请指出,相互交流学习,希望对你们有帮助。后语如果本文对你哪怕有一丁点帮助,请帮忙点好看。你的好看是我坚持写作的动力。另外,关注之后在发送 1024 可领取免费学习资料。资料详情请看这篇旧文:Python、C++、Java、Linux、Go、前端、算法资料分享 ...

April 4, 2019 · 3 min · jiezi

寻找 K8s 1.14 Release 里的“蚌中之珠”

摘要: K8s 1.14 发布了,Release Note那么长,我们该从何读起?本文由张磊、心贵、临石、徙远、衷源、浔鸣等同学联合撰写。Kubernetes 1.14.0 Release 已经于3月25日正式发布。相信你也已经注意到,相比于1.13 和 1.12 版本,这次发布包含的重要变更非常多,其对应的 Release Note 的篇幅长度也创下了“新高”。面对这样一份“海量信息”的 Release Note,我们该如何从这份文档里进行高效的信息过滤和挖掘,帮助团队更精准、快速的梳理出这次发布最主要的技术脉络呢?在本篇文章中,我们将 1.14 的Release Note 按照主题进行了重新归纳和梳理,按照类别对重要变更进行了技术剖析和讨论。希望这种“分类解读”的方式,能够帮助大家更好的理解 1.14 这个发布的核心内容。Windows Node 正式生产可用随着1.14的发布,Kubernetes 对windows节点的生产级支持无疑是一个重要的里程碑。具体来说,1.14 版本针对 Windows 做了大量增强;Pod:Pod内支持readiness和liveness探针;支持进程隔离和volume共享的多容器Pod;Pod支持原生configmap和sercret;Pod支持emptyDir;支持对Pod进行资源配额等;但是像优雅删除、Termination message、Privileged Containers、HugePages、Pod驱逐策略等部分特性还未在1.14版本提供;Service:支持服务环境变量提供DNS解析;支持NodePort、ClusterIP、LoadBalancer、Headless service;暂不支持Pod的hostnetwork模式;常规 Workload controller:RS、deployment、statefulset、daemonset、job、cronjob均支持windows容器;除此之外,支持Pod和container维度的metrics、HPA、“kubectl exec”、调度抢占、resource quotas、CNI 网络支持等多种特性让windows workload更加云原生;由于windows的特殊兼容性,目前 host OS的版本必须和容器镜像OS版本一致,1.14版本支持win server 2019;未来版本中会考虑使用Hyper-V隔离机制来解决版本兼容性问题。而伴随着 Windows 容器的生态正在慢慢壮大,能够在生产级别支持 Windows 节点的容器服务开始见诸各大云厂商。阿里云容器服务(ACK)近期已经推出了 Windows Container 的支持,提供了linux/windows应用混合部署的统一管理能力。参见:Support for Windows Nodes is Graduating to Stable (#116 )本地持久化数据卷(Local PV) 正式可用长期以来,能够让 Kubernetes 直接用宿主机的本地存储设备(比如:本地 SSD 硬盘)来提供持久化数据卷(即:Local PV 功能),一直是社区里非常强烈的一个诉求。这个原因很容易理解:相对于远程存储(网络存储),Local PV 在时延性、易用性、稳定性和费用上具有独特的优势,尤其是对于相关特性比较敏感的应用,如数据库应用和搜索引擎应用来说,有着重要的意义。而在 1.14 中,Local PV 终于正式宣布 GA,为云上的持久化存储选择增加了一种重要的的可能。不过,必须要明确的是, 选择使用 Local PV,也意味着用户必须自己承担一些潜在的风险,这包括:目前社区的开源方案无法动态创建卷调度器需要由额外的调度逻辑工作,以确保调度的节点可以分配出足够的磁盘容量容错性差,如果pod正在运行的宿主机宕机或者磁盘发生异常,那么它的持久化卷里的信息可能丢失第一个问题,可以通过比如阿里云的 local-volume-provisioner 实现本地 SSD Nvme实例自动创建数据卷来解决,但对于容错性和健壮性的问题,就是比较棘手的了。参见:Durable Local Storage Management is Now GA (#121)Pod 优先级与抢占机制稳定可用Kubernetes 里的任务优先级(priority)和抢占机制(preemption)的目的十分明确:保证高优先级的任务可以在需要的时候通过抢占低优先级任务的方式得到运行。这其中,优先级定义了一个Pod在集群中的重要程度,这个重要程度体现且仅体现在两个地方:(1)高优先级的Pod在调度阶段更容易被优先调度(K8s采用队列调度模型),注意这里并不保证高优先级Pod永远被优先调度,实际影响调度顺序的因素有很多;(2)在集群整体负载较高时,如果出现高优先级Pod无法被调度的情况(集群中没有满足条件的Node供Pod运行),K8s会启动抢占机制,通过抢占已经运行的低优先级的Pod的方式,让高优先级的Pod可以运行起来。抢占机制便是在这里引入的。抢占机制指当调度器发现某个Pod(如Pod-A)无法在集群中找到合适的节点部署时(所有节点Predicates全部失败),会试图通过删除一些优先级低于Pod-A的Pod来“腾出空间”部署Pod-A,这样Pod-A就可以被调度了。这样一个“看似简单”的需求在分布式环境中实施起来有很多细节,例如:如何决定删除哪个节点的哪些Pod、如何保证为Pod-A腾出的空间不被其它Pod占用、如何保证Pod-A不被饿死(Starvation)、如何处理有亲和性需求的Pod调度约束、是否需要支持跨节点Preemption以支持某些特定的约束(例如某Failure Domain的反亲和约束)等等。这些内容,可以参见:Pod Priority and Preemption in Kubernetes (#564) 你一定要知道什么是 Pod Ready++在 1.14 版本之前,Kubernetes 判断一个Pod是否Ready,就是检查这个Pod的容器是否全部正常运行。但是这里有个问题,那就是容器或者说里面的主进程Ready,并不一定意味着这个应用副本就一定是就绪的。为了确认Pod确实可以正常可用,我们希望给它增加一些外部指标(比如,该 Pod 需要的 Service,DNS,存储等服务全部就绪),来反应这个Pod是否“真正”Ready。这个特性,就是1.14 里一个叫做“Pod Readiness Gates”、也叫做 Pod Ready ++ 的特性。它为pod的“Ready 状态” 提供了一个非常强大的扩展点。需要注意的是,用户需要编写一个外部控制器(Controller)来为这个Pod Readiness Gates 字段对应的指标设置值。参见:Pod Ready++ (#580) Kubernetes 原生应用管理能力1.14之后,Kubernetes 项目本身开始具备了原生的应用管理能力,这其中最重要的一个功能,就是 Kustomize。Kustomize 允许用户从一个基础 YAML 文件,通过 overlay 的方式生成最终部署应用所需的 YAML 文件,而不是像 Helm 那样通过字符串替换的方式来直接修改基础 YAML 文件(模板)。这样,在一个用户通过 overlay 生成新的 YAML 文件的同时,其他用户可以完全不受影响的使用任何一个基础 YAML 或者某一层生成出来的 YAML 。这使得每一个用户,都可以通过 fork/modify/rebase 这样 Git 风格的流程来管理海量的 YAML 文件。这种 PATCH 的思想跟 Docker 镜像是非常类似的,它既规避了“字符串替换”对 YAML 文件的入侵,也不需要用户学习蹩脚的 DSL 语法(比如 Lua)。在1.14之后,Kustomize 已经成为了 kubectl 的一个内置命令。不难看到,Kubernetes 社区正在探索一种 Helm 之外的、更加 Kubernetes 原生的应用管理方法。具体效果如何,我们不妨拭目以待。参见:Added Kustomize as a subcommand in kubectl (#73033, @Liujingfang1)用户友好度进一步提升随着大家对Kubernetes越来越熟悉,对kubectl依赖也越来越强烈,需求也越来越多样化。而在 1.14 中,kubectl 着重在以下几个方面,提升用户体验,加强对日常运维能力的支持。之前 kubectl cp 操作每次只能 copy 一个文件,没办法使用通配符拷贝一批文件,非常不方便。在1.14中,蚂蚁金服的工程师提交了一个拷贝操作的通配符功能,方便对容器中的文件进行操作。参见:#72641以往,用户通常无法方便的知道自己被管理员通过 RBAC 配置的权限到底有哪些。而从v1.14开始,用户可以通过 kubectl auth can-i –list –namespace=ns1 来查看自己在 ns1 这个namespace下可以访问哪些资源 (比如Pod,Service等),并有哪些操作的权限(比如Get,List,Patch,Delete等)了。参见:#64820Kubernetes 用户需要删除的API 资源,往往分散在多个namespace中,删除非常不方便。在v1.14新版本中,用户终于可以借助于 kubectl delete xxx –all-namespaces 来进行统一的删除操作了(这里 XXX 可以是Pod,Services,Deployment,自定义的CRD等等),并且还可以配合 -l 和 –field-selector 可以更精确地删除满足特定条件的资源。参见:#73716稳定性进一步提升和之前每个版本一样,Kubernetes 的新版本发布对稳定性和可靠性增强的关注一直是重中之重,下面我们列举出一些值得注意的修复和升级。在做Pod驱逐时,会优先尝试使用优雅删除模式,而不是暴力删除etcd内的Pod数据。这个修复能够使被驱逐的 Pod更加优雅的退出。参见:#72730Kubelet要重建Pod的容器时,如果旧容器是unknown状态,现在Kubelet会首先尝试Stop容器。这避免了一个 Pod的同一个容器申明会有多个实例同时运行的风险。参见:#73802在大规模集群中,节点因为个别Pod使用了大量磁盘 IO,可能会导致节点频繁的在Ready/NotReady状态之间变化。这种状态会引起大规模的、不可预期的 Pod Eviction,导致线上故障。蚂蚁金服的工程师针对 Docker 环境下的这个问题提交了修复,建议大家也排查一下其它运行时的集群里是否有同样的问题。参见:#74389当 Kubelet在压力较大情况下,可能会发生 Kubelet 的Pod 生命周期事件消费频次弱于事件产生频次,导致负责这个事件的 Channel 被占满,这种情况持续一段时间后会直接导致Kubelet 死锁。阿里巴巴的工程师针对修这个问题提交了修复。参见:#72709大规模场景下的性能提升与优化在 Kubernetes 的主干功能日趋稳定之后,社区已经开始更多的关注大规模场景下 Kubernetes 项目会暴露出来的各种各样的问题。在v1.14中,Kubernetes 社区从面向最终用户的角度做出了很多优化,比如:kubectl 在实现中会顺序遍历 APIServer暴露出的全部资源的Group/Version/Kind,直到查找到需要处理的资源。这种遍历方式导致了用户在大规模集群下使用 kubectl 的性能体验受到很大影响。在v1.14版本中,kubectl的顺序遍历行为终于改为了并行,极大地提升了kubectl的使用体验(经过测试,性能指标提升了10倍以上)。参见: #73345在 1.14 中,APIServer 里的一个重要变更,是对单次 PATCH 请求内容里的操作个数做出了限制,不能超过10000个,否则就不处理此请求。这样做的目的,是防止 APIServer 因为处理海量的甚至是恶意PATCH 请求导致整个集群瘫痪。这也其实也是社区的 CVE-2019-1002100 主要的修复方法。参见:#74000Kubernetes 的 Aggregated API允许 k8s 的开发人员编写一个自定义服务,并把这个服务注册到k8s的 API 里面像原生 API 一样使用。在这个情况下,APIServer 需要将用户自定义 API Spec 与原生的 API Spec 归并起来,这是一个非常消耗CPU 的性能痛点。而在v1.14中,社区大大优化了这个操作的速率,极大地提升了APIServer 归并 Spec 的性能(提升了不止十倍)。参见:#71223文中相关链接一览Release Note :https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG-1.14.md#kubernetes-v114-release-notesSupport for Windows Nodes is Graduating to Stable (#116 ):https://github.com/kubernetes/enhancements/issues/116Durable Local Storage Management is Now GA (#121):https://github.com/kubernetes/enhancements/issues/121#issuecomment-457396290Pod Priority and Preemption in Kubernetes (#564) :https://github.com/kubernetes/enhancements/issues/564Pod Ready++ (#580) :https://github.com/kubernetes/enhancements/issues/580Added Kustomize as a subcommand in kubectl (#73033, @Liujingfang1):https://github.com/kubernetes/kubernetes/pull/73033https://github.com/Liujingfang1用户友好度:72641:https://github.com/kubernetes/kubernetes/pull/7264164820:https://github.com/kubernetes/kubernetes/pull/6482073716:https://github.com/kubernetes/kubernetes/pull/73716稳定性:72730:https://github.com/kubernetes/kubernetes/pull/7273073802:https://github.com/kubernetes/kubernetes/pull/7380274389:https://github.com/kubernetes/kubernetes/pull/7438972709:https://github.com/kubernetes/kubernetes/pull/72709大规模场景下的性能提升与优化:73345:https://github.com/kubernetes/kubernetes/pull/7334574000:https://github.com/kubernetes/kubernetes/pull/7400071223:https://github.com/kubernetes/kubernetes/pull/71223阿里云和CNCF联合开发推出的免费公开课,讲解以Kubernetes主体的云原生技术知识。一线技术专家精心打造,期待各位的学习反馈。 更多课程信息可以一步:官宣|《CNCF x Alibaba 云原生技术公开课》即将重磅上线本文作者:木环阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

April 1, 2019 · 2 min · jiezi

K8S 生态周报| 2019.03.25~2019.03.31

「K8S 生态周报」内容主要包含我所接触到的 K8S 生态相关的每周值得推荐的一些信息。欢迎订阅知乎专栏「k8s生态」。Kubernetes 1.14 正式发布1.14 的主要更新:对 Windows Node 和 container 的支持达到生产级别,支持 Windows Server 2019;本地持久化数据卷正式可用,这可以方便使用本地 SSD 之类的存储,但注意这个特性容错性较差;Pod 优先级和抢占机制正式可用,(建议慎重使用);Pod Ready++ (Pod Readiness Gates) 达到稳定,可以更好的判断 Pod 及其需要的资源是否均已就绪;当然还有很多的改进和很多被废弃的功能特性等,建议阅读 ReleaseNote。Minikube 1.0.0 正式发布Minikube 是一个用于本地搭建 Kubernetes 环境的工具,使用方法可参考 使用 Minikube 搭建本地 Kubernetes 环境。1.0.0 的主要更新:默认 Kubernetes 版本更新至 1.14.0;新增 –image-repository 参数,方便国内用户使用镜像解决网络问题;其他特性请阅读 ReleaseNoterunc 1.0-rc7 发布注意,低版本内核(尤其是 3.x)的系统,请不要升级至此版本这个版本主要为解决之前的漏洞及修正一些规范等,版本说明请参考 runc 1.0-rc7 发布之际。其他特性请阅读 ReleaseNoteBrigade 1.0 正式发布Brigade 是一个使用 JavaScript 来为 Kubernetes 构建 pipeline 的工具,现在是 CNCF 的 sandbox 项目。详细内容请阅读 ReleaseNoteKind 0.2.1 发布Kind 是 Kubernetes In Docker 的缩写,是一款便于在本地和 CI 环境中进行 Kubernetes 环境搭建的工具,具体用法请参考 使用 Kind 搭建你的本地 Kubernetes 集群0.2.1 是 kind 的首个 patch 版本,主要为了修复一个偶发的 panic , 建议尽快升级。可以通过下面二维码订阅我的文章公众号【MoeLove】 ...

April 1, 2019 · 1 min · jiezi

runc 1.0-rc7 发布之际

在 18 年 11 月底时,我写了一篇文章 《runc 1.0-rc6 发布之际》 。如果你还不了解 runc 是什么,以及如何使用它,请参考我那篇文章。本文中,不再对其概念和用法等进行说明。在 runc 1.0-rc6 发布之时,给版本的别名为 “For Real This Time”,当时我们原定计划是发布 1.0 的,但是作为基础依赖软件,我们认为当时的版本还有几个问题:不够规范;发布周期不明确;为了给相关的 runtime 足够的时间进行修正/升级,以及规范版本生命周期等,最终决定了发布 runc 1.0-rc6。为何有 runc 1.0-rc7 存在前面已经基本介绍了相关背景,并且也基本明确了 rc6 就是在 1.0 正式发布之前的最后一个版本,那 rc7 为什么会出现呢?CVE-2019-5736我们首先要介绍今年 runc 的一个提权漏洞 CVE-2019-5736 。2019 年 2 月 11 日在 oss-security 邮件组正式批露该漏洞,攻击者可以利用恶意容器覆盖主机上的 runc 文件,从而达到攻击的目的;(具体的攻击方式此处略过),注意不要轻易使用来源不可信的镜像创建容器便可有效避免被攻击的可能。简单补充下可能被攻击的方式:运行恶意的 Docker 镜像在主机上执行 docker exec 进入容器内关于容器安全或者容器的运行机制,其实涉及的点很多,我在去年的一次线上分享 《基于 GitLab 的 CI 实践》 有提到过 Linux Security Modules(LSM)等相关的内容,对容器安全感兴趣的朋友可以对 LSM 多了解下。不过本文主要看的是 runc 如何修复该漏洞的,以及后续产生的影响。修复方式// 对 memfd_create 系统调用做了个封装 省略部分代码#if !defined(SYS_memfd_create) && defined(__NR_memfd_create)# define SYS_memfd_create __NR_memfd_create#endif#ifdef SYS_memfd_create# define HAVE_MEMFD_CREATE# ifndef MFD_CLOEXEC# define MFD_CLOEXEC 0x0001U# define MFD_ALLOW_SEALING 0x0002U# endifint memfd_create(const char *name, unsigned int flags){ return syscall(SYS_memfd_create, name, flags);}// 一个简单的只读缓存区static char *read_file(char *path, size_t *length){ int fd; char buf[4096], *copy = NULL; if (!length) return NULL; fd = open(path, O_RDONLY | O_CLOEXEC); if (fd < 0) return NULL; *length = 0; for (;;) { int n; n = read(fd, buf, sizeof(buf)); if (n < 0) goto error; if (!n) break; copy = must_realloc(copy, (*length + n) * sizeof(*copy)); memcpy(copy + *length, buf, n); *length += n; } close(fd); return copy;error: close(fd); free(copy); return NULL;}// 将复制后的 fd 重赋值/执行static int clone_binary(void){ int binfd, memfd; ssize_t sent = 0;#ifdef HAVE_MEMFD_CREATE memfd = memfd_create(RUNC_MEMFD_COMMENT, MFD_CLOEXEC | MFD_ALLOW_SEALING);#else memfd = open("/tmp", O_TMPFILE | O_EXCL | O_RDWR | O_CLOEXEC, 0711);#endif if (memfd < 0) return -ENOTRECOVERABLE; binfd = open("/proc/self/exe", O_RDONLY | O_CLOEXEC); if (binfd < 0) goto error; sent = sendfile(memfd, binfd, NULL, RUNC_SENDFILE_MAX); close(binfd); if (sent < 0) goto error;#ifdef HAVE_MEMFD_CREATE int err = fcntl(memfd, F_ADD_SEALS, RUNC_MEMFD_SEALS); if (err < 0) goto error;#else int newfd; char *fdpath = NULL; if (asprintf(&fdpath, “/proc/self/fd/%d”, memfd) < 0) goto error; newfd = open(fdpath, O_RDONLY | O_CLOEXEC); free(fdpath); if (newfd < 0) goto error; close(memfd); memfd = newfd;#endif return memfd;error: close(memfd); return -EIO;}int ensure_cloned_binary(void){ int execfd; char **argv = NULL, **envp = NULL; int cloned = is_self_cloned(); if (cloned > 0 || cloned == -ENOTRECOVERABLE) return cloned; if (fetchve(&argv, &envp) < 0) return -EINVAL; execfd = clone_binary(); if (execfd < 0) return -EIO; fexecve(execfd, argv, envp); return -ENOEXEC;}省略掉了部分代码,完整代码可直接参考 runc 代码仓库 。整个的修复逻辑我在上面的代码中加了备注,总结来讲其实就是:创建了一个只存在于内存中的 memfd ;将原本的 runc 拷贝至这个 memfd ;在进入 namespace 前,通过这个 memfd 重新执行 runc ; (这是为了确保之后即使被攻击/替换也操作的还是内存中的这个只读的 runc)经过以上的操作,就基本修复了 CVE-2019-5736 。影响内核相关在上面讲完修复方式后,我们来看下会产生哪些影响。涉及到了系统调用 memfd_create(2) 和 fcntl(2)增加了系统调用,那自然就要看内核是否支持了。实际上,这些函数是在 2015 年 2 月(距这次修复整整 4 年,也挺有趣)被加入到 Linux 3.17 内核中的。换句话说就是 凡是在此内核版本之前的系统,均无法正常使用该功能,对我们的影响就是,如果你在此版本内核之前的机器上使用了包含上述修复代码的 runc 或构建在其之上的 containerd、 Docker 等都无法正常工作 。 以 Docker 举例:安装 docker-ce-18.09.2 或 docker-ce-18.06.3 可避免受 CVE-2019-5736 影响,但如果内核版本较低,在运行容器时可能会有如下情况出现: (不同版本/内核可能出现其他情况)[tao@moelove ~]# docker run –rm my-registry/os/debian echo Hello docker: Error response from daemon: OCI runtime create failed: container_linux.go:344: starting containerprocess caused “process_linux.go:293: copying bootstrap data to pipe caused "write init-p: broken pipe"”: unknown.解决办法升级内核;这是最直接的办法,而且使用一个新版本的内核也能省去很多不必要的麻烦:)rancher 提供了一个 runc-cve 的 patch,可兼容部分 3.x 内核的系统(我没有测试过)如果你不升级 runc/containerd/Docker 等版本的话,那建议你 1. 将 runc 可执行程序放到只读文件系统上,可避免被覆盖;2. 启动容器时,启用 SELinux; 3. 在容器内使用低权限用户或者采用映射的方式,但保证用户对主机上的 runc 程序无写权限。注意: memfd_create 等相关系统调用,也被加入到了 Debian 3.16 和 Ubuntu 14.04 updates 中,当然也被反向移植到了 CentOS 7.3 内核 3.10.0-514 版本之后。 (Red Hat 给 CentOS 7.x 的 3.10 内核上反向移植了很多特性)内存相关从上面的说明中,也很容易可以看到, 内存的使用上会有所增加,不过之后已做了修复。这里不再进行展开。其他偶尔可能触发一些内核 bug 之类的(总之建议升级 :)等待 rc8 发布上面已经介绍了 1.0-rc7 出现的主要原因 CVE-2019-5736;当然这个版本中也有一些新特性和一些 bugfix 不过不是本文的主要内容,不再赘述。值得一提的是这次的版本命名:runc 1.0-rc7 – “The Eleventh Hour” 后面这个别名其实来自于一部英剧,感兴趣也可以去看看。至于下个版本是不是会是 1.0 正式版呢?目前来看应该不是,有极大可能会发布 runc 1.0-rc8 做一些 bugfix,让我们拭目以待。可以通过下面二维码订阅我的文章公众号【MoeLove】 ...

March 31, 2019 · 3 min · jiezi

使用 Kind 搭建你的本地 Kubernetes 集群

Kind 是我很喜欢也一直在参与的项目,我计划将 Kind 相关的文章写成一个系列。(flag++) 这是第一篇。Kind 介绍Kind 是 Kubernetes In Docker 的缩写,顾名思义是使用 Docker 容器作为 Node 并将 Kubernetes 部署至其中的一个工具。官方文档中也把 Kind 作为一种本地集群搭建的工具进行推荐。安装二进制安装Kind 使用 Golang 进行开发,在仓库的 Release 页面,已经上传了构建好的二进制,支持多种操作系统,可直接按需下载进行使用。e.g.# 下载最新的 0.2.0 版本wget -O /usr/local/bin/kind https://github.com/kubernetes-sigs/kind/releases/download/0.2.0/kind-linux-amd64 && chmod +x /usr/local/bin/kind通过源码安装如果你本地已经配置好了 Golang 的开发环境,那你可以直接通过源码进行安装。e.g.go get -u sigs.k8s.io/kind运行完上述命令后,会将 kind 的可执行文件放到 $(go env GOPATH)/bin 文件夹内,你可能需要将此目录加入到 $PATH 中。或者也可以先 clone 源代码再通过 go build 进行构建。依赖Kind 的主要功能目前需要有 Docker 环境的支持,可参考 Docker 官方文档进行安装。如果需要操作集群,则需要安装 kubectl 命令行。安装方法可参考官方文档搭建单节点集群以下的演示均使用最新的代码(即通过源码安装)。基础用法搭建单节点集群是 Kind 最基础的功能。e.g.master $ kind create cluster –name moeloveCreating cluster “moelove” … ✓ Ensuring node image (kindest/node:v1.13.4) ???? ✓ Preparing nodes ???? ✓ Creating kubeadm config ???? ✓ Starting control-plane ????️Cluster creation complete. You can now use the cluster with:export KUBECONFIG="$(kind get kubeconfig-path –name=“moelove”)“kubectl cluster-info以上命令中, –name 是可选参数,如不指定,默认创建出来的集群名字为 kind。我们根据命令执行完的输出进行操作:master $ export KUBECONFIG="$(kind get kubeconfig-path –name=“moelove”)“master $ kubectl cluster-infoKubernetes master is running at https://localhost:34458KubeDNS is running at https://localhost:34458/api/v1/namespaces/kube-system/services/kube-dns:dns/proxyTo further debug and diagnose cluster problems, use ‘kubectl cluster-info dump’.master $ kubectl get nodesNAME STATUS ROLES AGE VERSIONmoelove-control-plane Ready master 2m v1.13.4以上命令中,kind get kubeconfig-path –name=“moelove” 会返回该指定集群配置文件所在的路径。可以看到单节点的 Kubernetes 已经搭建成功。注意默认情况下,Kind 会先下载 kindest/node:v1.13.4 镜像,该镜像目前托管于 Docker Hub 上,下载时间取决于网络状况。Kind 实际使用 kubeadm 进行集群的创建,对 kubeadm 有所了解的人都知道它默认使用的镜像在国内下载不到,所以需要自己解决网络问题。或者参考下面的方式:Kind 在创建集群的时候,支持通过 –config 的参数传递配置文件给 Kind,在国内,我们可以通过使用国内镜像源的方式来加速集群的创建。(这个方法也适用于直接通过 kubeadm 搭建 Kubernetes 集群)我们先通过以下命令删除刚才搭建的集群:master $ kind delete cluster –name moeloveDeleting cluster “moelove” …$KUBECONFIG is still set to use /root/.kube/kind-config-moelove even though that file has been deleted, remember to unset it接下来,将下面的配置内容保存至一个 YAML 文件中,比如名为 kind-config.yamlkind: ClusterapiVersion: kind.sigs.k8s.io/v1alpha3kubeadmConfigPatches:- | apiVersion: kubeadm.k8s.io/v1beta1 kind: ClusterConfiguration metadata: name: config networking: serviceSubnet: 10.0.0.0/16 imageRepository: registry.aliyuncs.com/google_containers nodeRegistration: kubeletExtraArgs: pod-infra-container-image: registry.aliyuncs.com/google_containers/pause:3.1- | apiVersion: kubeadm.k8s.io/v1beta1 kind: InitConfiguration metadata: name: config networking: serviceSubnet: 10.0.0.0/16 imageRepository: registry.aliyuncs.com/google_containersnodes:- role: control-plane我们使用该配置文件搭建集群。master $ kind create cluster –name moelove –config kind.yamlCreating cluster “moelove” … ✓ Ensuring node image (kindest/node:v1.13.4) ???? ✓ Preparing nodes ???? ✓ Creating kubeadm config ???? ✓ Starting control-plane ????️Cluster creation complete. You can now use the cluster with:export KUBECONFIG="$(kind get kubeconfig-path –name=“moelove”)“kubectl cluster-info上面通过 –config 将我们的配置文件传递给 Kind 用于搭建集群,推荐国内用户使用这种方式。搭建高可用集群Kind 也支持搭建高可用的 K8S 集群,不过只能通过配置文件来实现。可以直接将下面的内容保存至文件中,再将配置文件传递给 Kind 即可。e.g.kind: ClusterapiVersion: kind.sigs.k8s.io/v1alpha3kubeadmConfigPatches:- | apiVersion: kubeadm.k8s.io/v1beta1 kind: ClusterConfiguration metadata: name: config networking: serviceSubnet: 10.0.0.0/16 imageRepository: registry.aliyuncs.com/google_containers nodeRegistration: kubeletExtraArgs: pod-infra-container-image: registry.aliyuncs.com/google_containers/pause:3.1- | apiVersion: kubeadm.k8s.io/v1beta1 kind: InitConfiguration metadata: name: config networking: serviceSubnet: 10.0.0.0/16 imageRepository: registry.aliyuncs.com/google_containersnodes:- role: control-plane- role: control-plane- role: control-plane- role: worker- role: worker- role: worker我们使用以下的命令来搭建高可用的 Kubernetes 集群:master $ kind create cluster –name moelove-ha –config kind-ha-config.yamlCreating cluster “moelove-ha” … ✓ Ensuring node image (kindest/node:v1.13.4) ???? ✓ Preparing nodes ???????????????????????????? ✓ Starting the external load balancer ⚖️ ✓ Creating kubeadm config ???? ✓ Starting control-plane ????️ ✓ Joining more control-plane nodes ???? ✓ Joining worker nodes ????Cluster creation complete. You can now use the cluster with:export KUBECONFIG="$(kind get kubeconfig-path –name=“moelove-ha”)“kubectl cluster-infomaster $ export KUBECONFIG="$(kind get kubeconfig-path –name=“moelove-ha”)“master $ kubectl cluster-infoKubernetes master is running at https://localhost:44019KubeDNS is running at https://localhost:44019/api/v1/namespaces/kube-system/services/kube-dns:dns/proxyTo further debug and diagnose cluster problems, use ‘kubectl cluster-info dump’.可以做下简单的验证:master $ kubectl get nodesNAME STATUS ROLES AGE VERSIONmoelove-ha-control-plane Ready master 3m42s v1.13.4moelove-ha-control-plane2 Ready master 3m24s v1.13.4moelove-ha-control-plane3 Ready master 2m13s v1.13.4moelove-ha-worker Ready <none> 96s v1.13.4moelove-ha-worker2 Ready <none> 98s v1.13.4moelove-ha-worker3 Ready <none> 95s v1.13.4可以看到已经成功创建了多 master 的 Kubernetes 集群。总结这是使用 Kind 搭建本地 Kubernetes 集群的第一篇,同时本篇的内容也是《Kubernetes 从上手到实践》第 4 节内容的补充,搭配食用效果更佳 :)可以通过下面二维码订阅我的文章公众号【MoeLove】 ...

March 31, 2019 · 3 min · jiezi

六年打磨!阿里开源混沌工程工具 ChaosBlade

阿里妹导读:减少故障的最好方法就是让故障经常性的发生。通过不断重复失败过程,持续提升系统的容错和弹性能力。今天,阿里巴巴把六年来在故障演练领域的创意和实践汇浓缩而成的工具进行开源,它就是 “ChaosBlade”。如果你想要提升开发效率,不妨来了解一下。高可用架构是保障服务稳定性的核心。阿里巴巴在海量互联网服务以及历年双11场景的实践过程中,沉淀出了包括全链路压测、线上流量管控、故障演练等高可用核心技术,并通过开源和云上服务的形式对外输出,以帮助企业用户和开发者享受阿里巴巴的技术红利,提高开发效率,缩短业务的构建流程。例如,借助阿里云性能测试 PTS,高效率构建全链路压测体系,通过开源组件 Sentinel 实现限流和降级功能。这一次,经历了 6 年时间的改进和实践,累计在线上执行演练场景达数万次,我们将阿里巴巴在故障演练领域的创意和实践,浓缩成一个混沌工程工具,并将其开源,命名为 ChaosBlade。ChaosBlade 是什么?ChaosBlade 是一款遵循混沌工程实验原理,提供丰富故障场景实现,帮助分布式系统提升容错性和可恢复性的混沌工程工具,可实现底层故障的注入,特点是操作简洁、无侵入、扩展性强。ChaosBlade 基于 Apache License v2.0 开源协议,目前有 chaosblade 和 chaosblade-exe-jvm 两个仓库。chaosblade 包含 CLI 和使用 Golang 实现的基础资源、容器相关的混沌实验实施执行模块。chaosblade-exe-jvm 是对运行在 JVM 上的应用实施混沌实验的执行器。ChaosBlade 社区后续还会添加 C++、Node.js 等其他语言的混沌实验执行器。为什么要开源?很多公司已经开始关注并探索混沌工程,渐渐成为测试系统高可用,构建对系统信息不可缺少的工具。但混沌工程领域目前还处于一个快速演进的阶段,最佳实践和工具框架没有统一标准。实施混沌工程可能会带来一些潜在的业务风险,经验和工具的缺失也将进一步阻止 DevOps 人员实施混沌工程。混沌工程领域目前也有很多优秀的开源工具,分别覆盖某个领域,但这些工具的使用方式千差万别,其中有些工具上手难度大,学习成本高,混沌实验能力单一,使很多人对混沌工程领域望而却步。阿里巴巴集团在混沌工程领域已经实践多年,将混沌实验工具 ChaosBlade 开源目的,我们希望:让更多人了解并加入到混沌工程领域;缩短构建混沌工程的路径;同时依靠社区的力量,完善更多的混沌实验场景,共同推进混沌工程领域的发展。ChaosBlade 能解决哪些问题?衡量微服务的容错能力通过模拟调用延迟、服务不可用、机器资源满载等,查看发生故障的节点或实例是否被自动隔离、下线,流量调度是否正确,预案是否有效,同时观察系统整体的 QPS 或 RT 是否受影响。在此基础上可以缓慢增加故障节点范围,验证上游服务限流降级、熔断等是否有效。最终故障节点增加到请求服务超时,估算系统容错红线,衡量系统容错能力。验证容器编排配置是否合理通过模拟杀服务 Pod、杀节点、增大 Pod 资源负载,观察系统服务可用性,验证副本配置、资源限制配置以及 Pod 下部署的容器是否合理。测试 PaaS 层是否健壮通过模拟上层资源负载,验证调度系统的有效性;模拟依赖的分布式存储不可用,验证系统的容错能力;模拟调度节点不可用,测试调度任务是否自动迁移到可用节点;模拟主备节点故障,测试主备切换是否正常。验证监控告警的时效性通过对系统注入故障,验证监控指标是否准确,监控维度是否完善,告警阈值是否合理,告警是否快速,告警接收人是否正确,通知渠道是否可用等,提升监控告警的准确和时效性。定位与解决问题的应急能力通过故障突袭,随机对系统注入故障,考察相关人员对问题的应急能力,以及问题上报、处理流程是否合理,达到以战养战,锻炼人定位与解决问题的能力。功能和特点场景丰富度高ChaosBlade 支持的混沌实验场景不仅覆盖基础资源,如 CPU 满载、磁盘 IO 高、网络延迟等,还包括运行在 JVM 上的应用实验场景,如 Dubbo 调用超时和调用异常、指定方法延迟或抛异常以及返回特定值等,同时涉及容器相关的实验,如杀容器、杀 Pod。后续会持续的增加实验场景。使用简洁,易于理解ChaosBlade 通过 CLI 方式执行,具有友好的命令提示功能,可以简单快速的上手使用。命令的书写遵循阿里巴巴集团内多年故障测试和演练实践抽象出的故障注入模型,层次清晰,易于阅读和理解,降低了混沌工程实施的门槛。场景扩展方便所有的 ChaosBlade 实验执行器同样遵循上述提到的故障注入模型,使实验场景模型统一,便于开发和维护。模型本身通俗易懂,学习成本低,可以依据模型方便快捷的扩展更多的混沌实验场景。ChaosBlade 的演进史EOS(2012-2015):故障演练平台的早期版本,故障注入能力通过字节码增强方式实现,模拟常见的 RPC 故障,解决微服务的强弱依赖治理问题。MonkeyKing(2016-2018):故障演练平台的升级版本,丰富了故障场景(如:资源、容器层场景),开始在生产环境进行一些规模化的演练。AHAS(2018.9-至今):阿里云应用高可用服务,内置演练平台的全部功能,支持可编排演练、演练插件扩展等能力,并整合了架构感知和限流降级的功能。ChaosBlade(2019.3):是 MonkeyKing 平台底层故障注入的实现工具,通过对演练平台底层的故障注入能力进行抽象,定义了一套故障模型。配合用户友好的 CLI 工具进行开源,帮助云原生用户进行混沌工程测试。近期规划功能迭代:增强 JVM 演练场景,支持更多的 Java 主流框架,如 Redis,GRPC增强 Kubernetes 演练场景增加对 C++、Node.js 等应用的支持社区共建:欢迎访问 ChaosBlade@GitHub,参与社区共建,包括但不限于:架构设计模块设计代码实现Bug FixDemo样例文档、网站和翻译本文作者:中亭阅读原文本文来自云栖社区合作伙伴“ 阿里技术”,如需转载请联系原作者。 ...

March 29, 2019 · 1 min · jiezi

Kubernetes 如何打赢容器之战?

阿里妹导读:Kubernetes 近几年很热门,在各大技术论坛上被炒的很火。它提供了强大的容器编排能力,与此同时 DevOps 的概念也来到大家身边,广大的开发同学也能简单地运维复杂的商业化分布式系统,打破了传统开发和运维之间的界限。本文会以初学者的视角,希望能让读者更好地理解 Kubernetes 出现的背景、超前的设计理念和优秀的技术架构。背景PaaSPaaS 技术,一句话概括就是:它提供了“应用托管”的能力。早期的主流做法基本上是租 AWS 或者 OpenStack 的虚拟机,然后把这些虚拟机当作物理机一样,用脚本或者手工的方式在上面部署应用。这个过程中如何保证本地环境和云端环境的一致性是一个很大的课题,而提供云计算服务的公司的核心竞争力就是比拼谁做的更好。从某种意义上来说 PaaS 的出现,算是一个比较好的解决方案。以 Cloud Foundry 为例,在虚拟机上部署上 Cloud Foundry 项目后,用户可以很方便地把自己的应用上云。以上帝视角来看这个过程:Cloud Foundry 最核心的是提供了一套应用的打包和分发机制,它为不同的编程语言定义了不同的打包格式,它能把可执行文件、启动参数等等一起打包成压缩包然后上传至 Cloud Foundry 存储中心,最后由调度器选择虚拟机,由虚拟机上的 Agent 下载并启动应用。分布式系统随着软件的规模越来越大,业务模式越来越复杂,用户量的上升、地区的分布、系统性能的苛刻要求都促成服务架构从最初的单体变成 SOA 再到如今的微服务,未来还可能演变为 Service Mesh ,Serverless 等等。如今,一个完整的后端系统不再是单体应用架构了,多年前的 DDD 概念重新回到大家的视线中。现在的系统被不同的职责和功能拆成多个服务,服务之间复杂的关系以及单机的单点性能瓶颈让部署和运维变得很复杂,所以部署和运维大型分布式系统的需求急迫待解决。容器技术前面提到诸如 Cloud Foundry 的 PaaS,用户必须为不同语言、不同框架区分不同的打包方式,这个打包过程是非常具有灾难性的。而现实往往更糟糕,当在本地跑的好好的应用,由于和远端环境的不一致,在打包后却需要在云端各种调试,最终才能让应用“平稳”运行。而 Docker 的出现改变了一切,它凭借镜像解决了这个问题。Docker 一不做二不休,干脆把完整的操作系统目录也打包进去,如此高的集成度,保证了云端和本地环境的高度一致,并且随时随地轻易地移植。谁也不知道就因为“镜像”这个简单的功能,Docker 完成了对 PaaS 的降维打击,占有了市场。此时,一些聪明的技术公司纷纷跟进 Docker,推出了自家的容器集群管理项目,并且称之为 CaaS。容器技术利用 Namespace 实现隔离,利用 Cgroups 实现限制;在 Docker 实现上,通过镜像,为容器提供完整的系统执行环境,并且通过 UnionFS 实现 Layer 的设计。Docker 容器是完全使用沙箱机制,相互之间不会有任何接口。通过 Docker,实现进程、网络、挂载点和文件隔离,更好地利用宿主机资源。Docker 强大到不需要关心宿主机的依赖,所有的一切都可以在镜像构建时完成,这也是 Docker 目前成为容器技术标准的原因。所以我们能看到在 Kubernetes 中默认使用 Docker 作为容器(也支持 rkt)。Kubernetes铺垫了这么多,终于说到本文的主角了。说 Kubernetes 之前,不得不提 Compose、Swarm、Machine 三剑客,其实在 Kubernetes 还未一统江湖之前,它们已经能实现大部分容器编排的能力了。但是在真正的大型系统上,它们却远远不如 Mesosphere 公司出品的大型集群管理系统,更别说之后的 Kubernetes 了。在容器化和微服务时代,服务越来越多,容器个数也越来越多。Docker 如它 Logo 所示一样,一只只鲸鱼在大海里自由地游荡,而 Kubernetes 就像一个掌舵的船长,带着它们,有序的管理它们,这个过程其实就是容器编排。Kubernetes 起源于 Google,很多设计都是源自于 Borg,是一个开源的,用于管理云平台中多个主机上的容器化的应用,Kubernetes 的目标是让部署容器化的应用简单并且高效,并且提供了应用部署,规划,更新,维护的一种机制。小结至此,读者了解了 Kubernetes 的前世今生,由 PaaS 的火热,引爆了容器技术的战争,而赢得这场战争中最关键的即是拥有强大的容器编排的能力,而 Kubernetes 无疑是这场战争的胜利者。设计理念这一部分,我们会围绕 Kubernetes 的四个设计理念看看这些做法能给我们带来什么。声明式 VS 命令式声明式和命令式是截然不同的两种编程方式,在命令式 API 中,我们可以直接发出服务器要执行的命令,例如: “运行容器”、“停止容器”等;在声明式 API 中,我们声明系统要执行的操作,系统将不断向该状态驱动。我们常用的 SQL 就是一种声明式语言,告诉数据库想要的结果集,数据库会帮我们设计获取这个结果集的执行路径,并返回结果集。众所周知,使用 SQL 语言获取数据,要比自行编写处理过程去获取数据容易的多。apiVersion: extensions/v1beta1kind: Deploymentmetadata: name: etcd-operatorspec: replicas: 1 template: metadata: labels: name: etcd-operator spec: containers: - name: etcd-operator image: quay.io/coreos/etcd-operator:v0.2.1 env: - name: MY_POD_NAMESPACE valueFrom: fieldRef: fieldPath: metadata.namespace - name: MY_POD_NAME valueFrom: fieldRef: fieldPath: metadata.name我们来看看相同设计的 YAML,利用它,我们可以告诉 Kubernetes 最终想要的是什么,然后 Kubernetes 会完成目标。声明式 API 使系统更加健壮,在分布式系统中,任何组件都可能随时出现故障。当组件恢复时,需要弄清楚要做什么,使用命令式 API 时,处理起来就很棘手。但是使用声明式 API ,组件只需查看 API 服务器的当前状态,即可确定它需要执行的操作。显式的 APIKubernetes 是透明的,它没有隐藏的内部 API。换句话说 Kubernetes 系统内部用来交互的 API 和我们用来与 Kubernetes 交互的 API 相同。这样做的好处是,当 Kubernetes 默认的组件无法满足我们的需求时,我们可以利用已有的 API 实现我们自定义的特性。无侵入性感谢 Docker 容器技术的流行,使得 Kubernetes 为大家提供了无缝的使用方式。在容器化的时代,我们的应用达到镜像后,不需要改动就可以遨游在 Kubernetes 集群中。Kubernetes 还提供存储 Secret、Configuration 等包含但不局限于密码、证书、容器镜像信息、应用启动参数能力。如此,Kubernetes 以一种友好的方式将这些东西注入 Pod,减少了大家的工作量,而无需重写或者很大幅度改变原有的应用代码。有状态的移植在有状态的存储场景下,Kubernetes 如何做到对于服务和存储的分离呢?假设一个大型分布式系统使用了多家云厂商的存储方案,如何做到开发者无感于底层的存储技术体系,并且做到方便的移植?为了实现这一目标,Kubernetes 引入了 PersistentVolumeClaim(PVC)和 PersistentVolume(PV)API 对象。这些对象将存储实现与存储使用分离。PersistentVolumeClaim 对象用作用户以与实现无关的方式请求存储的方法,通过它来抹除对底层 PersistentVolume 的差异性。这样就使 Kubernetes 拥有了跨集群的移植能力。架构首先要提及的是 Kubernetes 使用很具代表性的 C/S 架构方式,Client 可以使用 kubectl 命令行或者 RESTful 接口与 Kubernetes 集群进行交互。下面这张图是从宏观上看 Kubernetes 的整体架构,每一个 Kubernetes 集群都由 Master 节点 和 很多的 Node 节点组成。MasterMaster 是 Kubernetes 集群的管理节点,负责管理集群,提供集群的资源数据访问入口。拥有 Etcd 存储服务,运行 API Server 进程,Controller Manager 服务进程及 Scheduler 服务进程,关联工作节点 Node。Kubernetes API Server 提供 HTTP Rest 接口的关键服务进程,是 Kubernetes 里所有资源的增、删、改、查等操作的唯一入口。也是集群控制的入口进程; Kubernetes Controller Manager 是 Kubernetes 所有资源对象的自动化控制中心,它驱使集群向着我们所需要的最终目的状态; Kubernetes Schedule 是负责 Pod 调度的进程。NodeNode 是 Kubernetes 集群架构中运行 Pod 的服务节点。Node 是 Kubernetes 集群操作的单元,用来承载被分配 Pod 的运行,是 Pod 运行的宿主机。关联 Master 管理节点,拥有名称和 IP、系统资源信息。运行 Docker Runtime、kubelet 和 kube-proxy。kubelet 负责对 Pod 对于的容器的创建、启停等任务,发送宿主机当前状态; kube-proxy 实现 Kubernetes Service 的通信与负载均衡机制的重要组件; Docker Runtime 负责本机容器的创建和管理工作。实现原理为了尽可能地让读者能明白 Kubernetes 是如何运作的,这里不会涉及到具体的细节实现,如有读者感兴趣可以自行参阅官网文档。这里以一个简单的应用部署示例来阐述一些概念和原理。创建 Kubernetes 集群介绍架构的时候我们知道,Kubernetes 集群由 Master 和 Node 组成。Master 管理集群的所有行为例如:应用调度、改变应用的状态,扩缩容,更新/降级应用等。Node 可以是是一个虚拟机或者物理机,它是应用的“逻辑主机”,每一个 Node 拥有一个 Kubelet,Kubelet 负责管理 Node 节点与 Master 节点的交互,同时 Node 还需要有容器操作的能力,比如 Docker 或者 rkt。理论上来说,一个 Kubernetes 为了应对生产环境的流量,最少部署3个 Node 节点。当我们需要在 Kubernetes 上部署应用时,我们告诉 Master 节点,Master 会调度容器跑在合适的 Node 节点上。我们可以使用 Minikube 在本地搭一个单 Node 的 Kubernetes 集群。部署应用当创建好一个 Kubernetes 集群后,就可以把容器化的应用跑在上面了。我们需要创建一个 Deployment,它会告诉 Kubernetes Master 如何去创建应用,也可以来更新应用。当应用实例创建后,Deployment 会不断地观察这些实例,如果 Node 上的 Pod 挂了,Deployment 会自动创建新的实例并且替换它。相比传统脚本运维的方式,这种方式更加优雅。我们能通过 kubectl 命令或者 YAML 文件来创建 Deployment,在创建的时候需要指定应用镜像和要跑的实例个数,之后 Kubernetes 会自动帮我们处理。查看 Pods 和 Nodes下面来介绍下 Pod 和 Node:当我们创建好 Deployment 的时候,Kubernetes 会自动创建 Pod 来承载应用实例。Pod 是一个抽象的概念,像一个“逻辑主机”,它代表一组应用容器的集合,这些应用容器共享资源,包括存储,网络和相同的内部集群 IP。任何一个 Pod 都需要跑在一个 Node 节点上。Node 是一个“虚拟机器”,它可以是虚拟机也可以是物理机,一个 Node 可以有多个 Pods,Kubernetes 会自动调度 Pod 到合适的 Node 上。Service 与 LabelSelectorPods 终有一死,也就是说 Pods 也有自己的生命周期,当一个 Pod 挂了的时候,ReplicaSet 会创建新的,并且调度到合适的 Node 节点上。考虑下访问的问题,Pod 替换伴随着 IP 的变化,对于访问者来说,变化的 IP 是合理的;并且当有多个 Pod 节点时,如何 SLB 访问也是个问题,Service 就是为了解决这些问题的。Service 是一个抽象的概念,它定义了一组逻辑 Pods,并且提供访问它们的策略。和其他对象一样,Service 也能通过 kubectl 或者 YAML 创建。Service 定义的 Pod 可以写在 LabelSelector 选项中(下文会介绍),也存在不指定 Pods 的情况,这种比较复杂,感兴趣的读者可以自行查阅资料。Service 有以下几种类型:ClusterIP(默认):在集群中内部IP上暴露服务,此类型使Service只能从群集中访问;NodePort:通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 :,可以从集群的外部访问一个 NodePort 服务;LoadBalancer:使用云提供商的负载均衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务;ExternalName:通过返回 CNAME 和它的值,(适用于外部 DNS 的场景)Labels 和 Selectors 能够让 Kubernetes 拥有逻辑运算的能力,有点像 SQL。举个例子:可以查找 app=hello_word 的所有对象,也可以查找 app in (a,b,c) abc的所有对象。Labels是一个绑定在对象上的 K/V 结构,它可以在创建或者之后的时候的定义,在任何时候都可以改变。扩容应用前文提到我们可以使用 Deployment 增加实例个数,下图是原始的集群状态:我们可以随意的更改 replicas (实例个数)来扩容,当我们更改了 Deployment 中的 replicas 值时,Kubernetes 会自动帮我们达到想要的目标实例个数,如下图:更新应用更新应用和扩容类似,我们可以更改 Deployment 中的容器镜像,然后 Kubernetes 会帮住我们应用更新(蓝绿、金丝雀等方式),通过此功能,我们还可以实现切换应用环境、回滚、不停机 CI/CD。下面是部署的过程,需要注意的是我们可以指定新创建的 Pod 最大个数和不可用 Pod 最大个数:总结到了最后,大家对 Kubernetes 有个大概的了解了,但 Kubernetes 远远不止本文所介绍的这些内容。在云原生概念逐渐清晰的今天,Kubernetes 作为 CNCF 中一个接地气的落地项目,其重要性不言而喻。本文作者: 淘敏阅读原文本文来自云栖社区合作伙伴“ 阿里技术”,如需转载请联系原作者。 ...

March 28, 2019 · 3 min · jiezi

如何利用 Webshell 诊断 EDAS Serverless 应用

本文主要介绍 Serverless 应用的网络环境以及 Serverless 应用容器内的环境,了解背景知识以及基本的运维知识后可以利用 Webshell 完成基本的运维需求。Webshell 简介用户可以通过阿里云控制台直接获取 ECS 的 Shell,从而完成自己的运维需求。如果 ECS 内开启了 SSH 服务,且 ECS 存在弹性公网 IP,那么用户也可以在本地通过 SSH 服务获取 ECS 的 Shell 完成运维需求。由于 EDAS Serverless 特殊的架构以及网络环境,用户暂时无法直接从本地通过 SSH 服务获取应用容器的 Shell。在 Serverless 场景中,容器是一个暂态的、供应用运行的环境,一般来说不需要进入运维。为了方便用户进行线上问题定位排查,EDAS 在控制台提供了一个简单的Webshell,供用户查看调试自己的容器。EDAS 默认给出的 Jar War 类型应用的容器基础镜像主要是面向应用运行时,不带有冗余的排查工具,因此对运维人员可能不够友好。对于用户自身的镜像,不需要镜像中启动 SSH 服务,只需要带有可执行的/bin/bash即可。用户自己的镜像可以带上必须的运维工具方便排查。目前 Webshell 不支持 Windows 镜像。EDAS 应用节点的网络环境EDAS 应用节点处于用户自己购买的阿里云 VPC 内。在 EDAS 中,还额外提供了一层中间件服务调用隔离的手段:EDAS 命名空间。EDAS 命名空间与 VPC 内的 VSWITCH 是绑定关系,一个 EDAS 命名空间对应一个 VSWITCH,一个 VSWITCH 可以对应多个EDAS命名空间。VPC 的原理以及基本的产品情况可以在阿里云VPC官方文档了解。简单来讲,VPC 内的 IP 地址为局域网地址,不同 VPC 内的2层以上数据包无法路由到目的地。EDAS 命名空间主要做中间件逻辑隔离,不同命名空间内的应用在中间件层面是隔离的,如服务发现以及配置下发等。由于 VPC 的产品特性以及当前的 EDAS Serverless 的产品特性,容器无法直接触达 VPC 外的服务(阿里云产品除外,如 OSS、镜像服务等)。在没有额外配置的情况下,你的容器运行在网络“孤岛”环境。了解了基本的网络情况,现在可以明白为什么用户无法直接触达自己的容器了。容器内需要访问公网服务,可以通过购买 NAT,并配置 VPC 内 VSWITCH 的SNAT规则即可,详见阿里云Serverless文档。SNAT规则可以让VPC内地址访问公网地址,从而使用公网暴露的服务,获取到公网的资源。EDAS 构建的镜像的方案基于阿里云容器镜像服务,EDAS 集成了为用户构建以及管理镜像的功能。用于构建的基础镜像为centos:7,在此基础上为用户配置好了时区、语言与编码方式、Open JDK 运行环境。容器存在的目的是为了让应用运行起来,EDAS 不可能以占用所有用户运行时资源为代价,集成过多的工具,对于容器内工具有需求的用户,建议自行构建镜像,或者按需从 OSS 拉取。常见的分析手段线上容器的运维一般是不必要的。如果你确定需要进入容器进行运维,请务必了解你的操作对线上业务的风险:对于单点应用,你的行为可能导致容器 OOM,从而导致分钟级别的业务中断,而对于多点部署的业务,上述现象可能造成业务秒级中断。诊断 EDAS 应用一般从这几个方面入手:常规检查,上传搜集的日志。常规检查常规检查的方法比较多,以 Java 应用为例,一般是检查进程、线程以及 JVM 的健康状态。首先执行命令ps -ef | grep java检查你的 Java 进程是否还存在。这里必须特别说明的是,容器内一般需要使用主进程启动你的应用,这样一旦你的应用被kill掉,容器也会退出,EDAS 会将退出的容器重新启动,防止业务中断。如果进程不见了,可以执行命令dmesg | grep -i kill查看OOM相关日志。如果存在日志,那么说明你的应用进程被 kill 掉了,接着检查工作目录下hs_err_pid{PID}.log日志文件,定位具体的原因。Java 类型应用的在线分析可以使用阿里巴巴开源软件 Arthas 解决,建议在测试镜像中集成Arthas工具进行常规诊断。Arthas可以很方便地实时查看类加载情况,观察方法出入参,环境变量等。# 接入arthas,需求打通公网wget https://alibaba.github.io/arthas/arthas-boot.jarjava -jar arthas-boot.jar对于网络层的诊断,在了解上述EDAS应用节点网络情况的前提下,一般可以通过curl -v {host/ip} {port}检查域名解析以及连通性,通过tcpdump抓包观察分析网络调用情况。日志上传解决方案受限于容器内工具的匮乏,比较推荐的方案是将容器内搜集到的日志上传到云端,然后下载到本地进行分析。目前,EDAS 暂时没有提供容器内日志的下载功能,这里给出一种基于阿里云 OSS 服务的解决方案。OSS 打通了阿里云生态几乎所有的网络环境,你几乎可以在任何网络环境下上传以及下载 OSS 上的文件。首先在容器内部安装OSS命令行工具。以64位centos系统,root下没有打通公网的情况下可以选择在本地下载,然后将这个文件上传到oss,然后取oss的vpc内地址进行下载wget http://gosspublic.alicdn.com/...chmod 755 ossutil64* 然后配置你的 OSS 命令行工具,附上当前 region VPC 内的endpoint(VPC内的上传不要求打通公网,也不消耗公网带宽流量,更加经济),填写用于接收上传文件的账号的AK/SK,然后查看已经创建的Bucket,来检查你的OSS服务是否可用。请先确保账号(不必是当前账号,任意开通阿里云oss服务的账号均可)已开通 OSS 服务按照提示配置你的 AK SK endpoint信息,ststoken 不需要填写./ossutil64 config检查账号是否可用,如果报错则配置错误,如果没有bucket,则建议前往oss控制台创建,命令行工具也支持创建./ossutil64 ls这里创建一个模拟的日志文件,用于上传echo “Hello” > edas-app.log./ossutil64 cp edas-app.log {bucket-address,例如:oss://test-bucket,可以从上述命令"./ossutil64 ls"中查看}* 从 OSS 控制台或其他工具中找到你的日志文件,下载到本地,并使用你熟悉的工具进行分析。本文作者:落语(阿里云智能中间件技术开发工程师,负责分布式应用服务 EDAS 的开发和维护。)<hr>本文作者:中间件小哥阅读原文 ...

March 27, 2019 · 1 min · jiezi

阿里工程师开发了一款免费工具,提升Kubernetes应用开发效率

对于使用了Kubernetes作为应用运行环境的开发者而言,在同一个集群中我们可以使用命名空间(Namespace)快速创建多套隔离环境,在相同命名空间下,服务间使用Service的内部DNS域名进行相互访问。 基于Kubernetes强大的隔离以及服务编排能力,可以实现一套定义编排(YAML)多处部署的能力。不过,一般来说Kubernetes使用的容器网络与开发者的所在的办公网络直接并不能直接连通。 因此,如何高效的利用Kubernetes进行服务间的联调测试,成为在日常开发工作中一道绕不开的坎。本文我们就来聊一聊,如何加速基于Kubernetes的研发效率。使用自动流水线为了能够让开发者能够更快的将修改的代码部署到集群测试环境中,一般来说我们会引入持续交付流水线,将代码的编译,镜像的打包上传以及部署通过自动化的方式来解决。如下所示:从一定程度上来说,这种方式可以避免开发人员进行大量重复性的工作。但是,虽然整个过程自动化了,但是开发人员也不得不每次进行代码变更之后都需要等待流水线的运行。对于开发人员来说,每次代码变更后等待流水线运行或许已经成为整个开发任务过程中体验最糟糕的部分。打破网络限制,本地联调理想状态下是开发者可以直接在本地启动服务,并且这个服务就可以无缝的和远程的kubernetes集群中的各个其它服务实现互相调用。需要解决两个问题:我依赖了其它的服务:运行在本地的代码可以直接通过podIP,clusterIP甚至是Kubernetes集群内的DNS地址访问到部署在集群中的其它应用,如下图左;其它的服务依赖了我:运行在Kubernetes集群中的其它应用可以在不做任何改变的情况下访问我到运行的本地的代码,如下图右。要实现刚才说的两种本地联调方式,主要需要解决以下3个问题:本地网络与Kubernetes集群网络直接的连通问题在本地实现Kubernetes中内部服务的DNS解析;如果将对集群中其它Pod访问的流量转移到本地;云效开发者工具KT为了简化在Kubernetes下进行联调测试的复杂度,云效在SSH隧道网络的基础上并结合Kubernetes特性构建了一款面向开发者的免费辅助工具KT(点击前往下载),如下所示:当本地运行的服务C’希望能够直接访问集群中default命名空间下的Service A和Service B时,运行如下命令:$ ktctl -namespace=defaultKT会自动在集群中部署SSH/DNS代理容器,并构建本地到Kubernetes集群的VPN网络并通过DNS代理实现集群服务DNS域名解析,在运行KT之后,开发者的本地程序可以直接像运行在集群中的服务一样通过service名字调用集群中部署的其它应用:而如果希望集群中的其它Pod(比如图中的PodD和PodE)能够通过ServiceC访问到本地运行的程序C‘,通过如下命令,指定需要替换的目标Deployment以及指定本地服务端口:#-swap-deployment指定需要替换的目标Deployment # -expose 指定本地服务运行的端口 ktctl -swap-deployment c-deployment -expose=8080KT在构建VPN网络的同时,还会自动通过代理容器接管集群原有的PodC实例,并直接转发的本地的8080端口。实现集群应用联调本地。经过上述两个命令,开发者就可以真正的使用云原生的方式来开发调试Kubernetes中的应用了。工作原理下面解析KT的工作原理,如果你已经迫不及待的想尝试KT的功能,可以直接前往下载KT工具。KT主要由两部分组成:在本地运行的命令行工具ktctl运行在集群中的SSH/DNS代理容器。在工作原理上KT实际上是结合Kubernetes自身能力实现的一个基于SSH的VPN网络。这这部分,笔者将详细介绍云效Kubernetes开发者工具KT的工作原理:打通SSH协议通道在Kubernetes命令行工具kubectl中内置的port-forward命令可以帮助用户建立本地端口到Kubernetes集群中特定Pod实例端口间的网络转发。当我们在集群中部署一个包含sshd服务的容器后,通过port-forward可以将容器的SSH服务端口映射到本地:# 将对本地2222端口转发到kt-porxy实例的22端口 $ kubectl port-forward deployments/kt-proxy 2222:22 Forwarding from 127.0.0.1:8080 -> 8080 Forwarding from [::1]:8080 -> 8080在运行端口转发后,就可以直接通过本地的2222端口通过SSH协议进入到Kubernetes集群的kt-proxy实例中。从而打通本地与集群之间的SSH网络链路。本地动态端口转发与VPN在打通SSH网络之后,我们就可以利用SSH通道实现本地到集群的网络请求,其中最基本的方式就是使用SSH动态端口转发的能力。使用如下命令,通过本地2000运行的代理,可以将网络请求通过集群中运行的kt-proxy容器进行转发,从而实现本地到集群网络请求的转发:# ssh -D [本地网卡地址:]本地端口 name@ip -p映射到kt-proxy的22端口的本地端口 ssh -D 2000 root@127.0.0.1 -p2222在启用SSH动态端口转发后,通过设置http_proxy环境变量后,即可直接在命令行中访问集群网络:# export http_proxy=socks5://127.0.0.1:ssh动态端口转发的代理端口 export http_proxy=socks5://127.0.0.1:2000不过原生SSH动态端口转发也有一定的限制那就是无法直接使用UDP协议,这里我们选择了一个替代方案sshuttle. 如下命令所示:# export http_proxy=socks5://127.0.0.1:ssh动态端口转发的代理端口 export http_proxy=socks5://127.0.0.1:2000 sshuttle –dns –to-ns 172.16.1.36 -e ‘ssh -oStrictHostKeyChecking=no -oUserKnownHostsFile=/dev/null’ -r root@127.0.0.1:2222 172.16.1.0/16 172.19.1.0/16 -vvsshuttle工具在SSH协议之上构建了一个简易的VPN网络,同时支持DNS协议转发。因此,接下来的问题就是实现一个自定义的DNS服务即可,而该服务在KT中是直接内置在KT代理镜像中。远程端口转发在本地到集群的链路打通之后。 接下来需要解决的就是从集群到本地的访问链路。这部分,我们会使用到SSH的远程端口转发能力,如下所示,指定所有对kt-proxy的8080端口的网络请求都会通过SSH隧道直接转发到本地的8080端口:# ssh -R 8080:localhost:8080 root@127.0.0.1 -p2222 ssh -R 8080:localhost:8080 root@127.0.0.1 -p2222因此,在KT的实现过程之中,结合Kubernetes基于标签的松耦合能力,我们只需要克隆原有应用实例的YAML描述,并将容器替换为kt-proxy即可。从而将对集群中原有应用的请求通过SSH远程端口转发到本地。综上,通过利用Kubernetes原生能力以及适度的扩展,开发者可以快速在本地利用KT打破本地网络与Kubernetes网络之间的界限,大大提升使用Kubernetes进行联调测试的效率。小结工具承载了对特定问题的解决方案,而工程技术实践则是对其价值的放大。阿里巴巴云效平台,致力于为开发者提供一站式的企业研发与协作服务,并将阿里多年的软件工程实践以一种更加开发的形态反馈技术社区,欢迎更多的技术开发者入驻。目前,Mac用户可以前往下载并体验KT工具作者:郑云龙,阿里巴巴研发效能部高级研发工程师本文作者:云效鼓励师阅读原文本文为云栖社区原创内容,未经允许不得转载。

March 26, 2019 · 1 min · jiezi

容器 PaaS 助力中国一汽 IT 基础技术平台建设

随着容器技术的发展,传统的数据中心模式正被云数据中心所取代,传统企业与互联网企业的差距也越来越大。对于传统企业来说,上云已是大势所趋,智能化、数字化转型迫在眉睫。国有特大型汽车生产企业中国第一汽车集团有限公司(简称中国一汽)成立于上个世纪50年代,是中央直属国有特大型汽车生产企业,旗下拥有红旗品牌乘用车、奔腾乘用车、解放商用车等汽车品牌。业务领域包括汽车的研发、生产、销售、物流、服务、汽车零部件、金融服务、汽车保险、移动出行等。经过60余年的发展,中国一汽年产销已达300万辆级,产销总量位列行业第一阵营。为了在动态竞争中持续保持领先地位,顺应技术发展趋势,一汽体系管理及数字化部提出了从传统汽车制造企业向全链条移动出行服务提供企业的战略转型。希望从骨子里将一汽由一个传统制造业企业转变为具备互联网思维、敏捷高效的数字化、互联网企业。但转型过程中,一汽仍面临着许多问题,比如基础设施资源浪费严重、无法在线测试和发布业务、不具备弹性伸缩能力等。联手打造IT基础技术平台为解决以上问题、满足业务和技术发展需求,中国一汽联手时速云共同打造了“IT基础技术平台”。时速云基于 Docker + Kubernetes,提供以应用为中心的云计算解决方案,涵盖轻量级容器 PaaS 平台、微服务治理、服务编排、DevOps、持续集成持续部署、API 网关等多种服务,最大化地帮助企业实现业务应用的快速交付与持续创新。平台建设目标:建设一汽具备自主控制能力的数字化产品研发、运维一体化平台,为一汽范围内 IT 及数字化产品研发项目提供混合云(虚拟化、容器)、开发运行环境,加速一汽数字化产品研发过程。建设并维护一汽开发者社区,从提升开发者服务满意度入手,持续不懈的发掘现行体系及流程中不合理及耗时多的环节,为开发团队减负,使产品能专注于产品设计,使开发能专注于代码实现。平台基于统一企业 IT 技术架构需要,制定相应的 IT 开发技术标准与规范,涵盖敏捷项目管理规范、分布式与微服务架构开发指导、混合云使用指导、虚拟化与容器化部署规范、应用发布规范、应用性能及日志监控规范等内容。3个阶段实现平台的全面建设根据“ IT 基础技术平台”总体建设方案,时速云通过容器云PaaS、敏捷与 DevOps 推广应用、平台功能及服务完善三个阶段来帮助中国一汽实现该平台的全面建设目标。容器云PaaS:基于容器技术打造云原生应用产品体系,立足企业开发、测试及IT管理需求,提供一站式容器云平台,为企业提供轻量、快速、高效、友好的服务运行及开发环境,助力企业IT架构升级。敏捷与 DevOps 推广应用 :培育种子项目,推广应用敏捷、DevOps,强调快速迭代、用户体验,力争产出千万月活的拳头数字化产品,以种子项目带动所有数字化产品的快速转型。推进存量信息系统平台化改造,实现关键应用服务化。平台功能及服务完善 :建设基础技术平台分布式及微服务应用管控中心,实现统一的应用、服务运行及性能监控、日志分析等功能,使管理层能实时掌握所有数字化产品的运行服务状态。建设开放 API 服务中心,纳管第三方开放 API 及内部核心应用开放 API,对一汽体系内分子公司及合作企业开放访问,逐步构建一汽自主的开放平台。同时,时速云携手中国一汽打造的IT基础技术平台还具有 Kubernetes 系统主节点、用户服务、系统数据库、系统管理平台的高可用性,还将从资源隔离、镜像安全、租户网络隔离以及内置防火墙等方面保障信息安全。目前,时速云容器 PaaS 提供的 DevOps 开发体系与工具已经在支撑一汽自主智能网联、新营销等多个项目群的敏捷开发与迭代,2019年以来的新立项项目无一例外都需要基于该平台进行实施。此外,今年1月中国一汽已启动存量信息系统的平台化改造,已有10数个系统的源代码实现统一管理。

March 19, 2019 · 1 min · jiezi

论文解读:Design patterns for container-based distributed systems

这是由Kubernetes创始人发表的论文,总结了基于容器的分布式系统的设计模式,让我们来一览究竟吧。论文认为,继OOP(面向对象编程)所引领的软件开发革命之后,如今似乎在分布式系统开发中也发生着一场相似的革命:基于容器化组件构建的微服务架构。容器的一大独特优势在于:良好的边界——恰好适合应用开发的隔离性。作者总结了一些设计模式,并且分为三大类型:Single-container management patterns容器的传统接口有run(), pause(), stop()可以有更丰富的接口“向上看”的视角:metrics, config, logs等,通常用HTTP+JSON来暴露“向下看”的视角:lifecycle(提供生命周期回调钩子), priority(为了空出资源给高优先级任务,甚至能提前关掉低优先级任务),“replicate yourself”(迅速创建一组相同的服务容器来应对突发流量)接下来两类都依赖Pod抽象(Kubernetes有提供),因为都涉及容器编排了,而且已进入Sevice Mesh这门新概念的范围。Single-node, multi-container application patterns多个容器组成一个原子单位Sidecar模式例如:web server + log collector前提是容器间能共享“存储卷”之类的资源顺带一提,为什么要用多容器,而非容器内多进程?资源审计和分配:这点很重要,虽然多进程也勉强能做,但多容器做得更成熟职责划分、解耦重用:如果一个容器包含多种进程,就笨重而难以重用了,小巧的单用途容器更适合重用故障隔离:重启一个容器,比修复容器内的故障进程要容易多了交付隔离:每个容器能独立地升级/降级Ambassador模式类似于OOP的proxy模式例如:memcache + twemproxy,这类模式是单结点的,因此twemproxy要和其中1个memcache部署到同一结点上前提是容器间能共享localhost网络接口Adapter模式例如:统一的metrics接口(JMX,statsd等统一收集到HTTP端点)可以免于修改已有的容器(因为已经以容器作为软件开发的单位了)Multi-node application patternsLeader election模式领导者选举这件事已经有很多库了,但往往对编程语言有所限定容器则对编程语言中立容器只需构建一次就能重用,高度贯彻了抽象和封装的原则Work queue模式传统的工作队列框架对编程语言有所限定实现了run(), mount()接口的容器,可作为“工作”的抽象基于此可打造一种通用的工作队列框架(可能是Kubernetes的未来方向)虽然没给例子,但有些类似Mesos的可插拔调度器架构Scatter/gather模式这样传递请求:client->root->serverroot结点把请求分发给很多servers,再把它们的响应汇总到一起,交给client例如:搜索引擎,分布式查询引擎多个leaf容器+1个merge容器,就能实现通用的scatter/gather框架(可能也是Kubernetes的未来方向)结语总之,论文将容器视为软件开发的一等公民,像OOP的对象一样重要,提倡单用途可组合可重用的容器。这似乎是对UNIX编程艺术的重申。

March 18, 2019 · 1 min · jiezi

码上用它开始Flutter混合开发——FlutterBoost

开源地址:https://github.com/alibaba/flutter_boost为什么要混合方案具有一定规模的App通常有一套成熟通用的基础库,尤其是阿里系App,一般需要依赖很多体系内的基础库。那么使用Flutter重新从头开发App的成本和风险都较高。所以在Native App进行渐进式迁移是Flutter技术在现有Native App进行应用的稳健型方式。闲鱼在实践中沉淀出一套自己的混合技术方案。在此过程中,我们跟Google Flutter团队进行着密切的沟通,听取了官方的一些建议,同时也针对我们业务具体情况进行方案的选型以及具体的实现。官方提出的混合方案1基本原理Flutter技术链主要由C++实现的Flutter Engine和Dart实现的Framework组成(其配套的编译和构建工具我们这里不参与讨论)。Flutter Engine负责线程管理,Dart VM状态管理和Dart代码加载等工作。而Dart代码所实现的Framework则是业务接触到的主要API,诸如Widget等概念就是在Dart层面Framework内容。一个进程里面最多只会初始化一个Dart VM。然而一个进程可以有多个Flutter Engine,多个Engine实例共享同一个Dart VM。我们来看具体实现,在iOS上面每初始化一个FlutterViewController就会有一个引擎随之初始化,也就意味着会有新的线程(理论上线程可以复用)去跑Dart代码。Android类似的Activity也会有类似的效果。如果你启动多个引擎实例,注意此时Dart VM依然是共享的,只是不同Engine实例加载的代码跑在各自独立的Isolate。2官方建议引擎深度共享在混合方案方面,我们跟Google讨论了可能的一些方案。Flutter官方给出的建议是从长期来看,我们应该支持在同一个引擎支持多窗口绘制的能力,至少在逻辑上做到FlutterViewController是共享同一个引擎的资源的。换句话说,我们希望所有绘制窗口共享同一个主Isolate。但官方给出的长期建议目前来说没有很好的支持。多引擎模式我们在混合方案中解决的主要问题是如何去处理交替出现的Flutter和Native页面。Google工程师给出了一个Keep It Simple的方案:对于连续的Flutter页面(Widget)只需要在当前FlutterViewController打开即可,对于间隔的Flutter页面我们初始化新的引擎。例如,我们进行下面一组导航操作:我们只需要在Flutter Page1和Flutter Page3创建不同的Flutter实例即可。这个方案的好处就是简单易懂,逻辑清晰,但是也有潜在的问题。如果一个Native页面一个Flutter页面一直交替进行的话,Flutter Engine的数量会线性增加,而Flutter Engine本身是一个比较重的对象。多引擎模式的问题冗余的资源问题.多引擎模式下每个引擎之间的Isolate是相互独立的。在逻辑上这并没有什么坏处,但是引擎底层其实是维护了图片缓存等比较消耗内存的对象。想象一下,每个引擎都维护自己一份图片缓存,内存压力将会非常大。插件注册的问题。插件依赖Messenger去传递消息,而目前Messenger是由FlutterViewController(Activity)去实现的。如果你有多个FlutterViewController,插件的注册和通信将会变得混乱难以维护,消息的传递的源头和目标也变得不可控。Flutter Widget和Native的页面差异化问题。Flutter的页面是Widget,Native的页面是VC。逻辑上来说我们希望消除Flutter页面与Naitve页面的差异,否则在进行页面埋点和其它一些统一操作的时候都会遇到额外的复杂度。增加页面之间通信的复杂度。如果所有Dart代码都运行在同一个引擎实例,它们共享一个Isolate,可以用统一的编程框架进行Widget之间的通信,多引擎实例也让这件事情更加复杂。因此,综合多方面考虑,我们没有采用多引擎混合方案。现状及思考前面我们提到多引擎存在一些实际问题,所以闲鱼目前采用的混合方案是共享同一个引擎的方案。这个方案基于这样一个事实:任何时候我们最多只能看到一个页面,当然有些特定的场景你可以看到多个ViewController,但是这些特殊场景我们这里不讨论。我们可以这样简单去理解这个方案:我们把共享的Flutter View当成一个画布,然后用一个Native的容器作为逻辑的页面。每次在打开一个容器的时候我们通过通信机制通知Flutter View绘制成当前的逻辑页面,然后将Flutter View放到当前容器里面。这个方案无法支持同时存在多个平级逻辑页面的情况,因为你在页面切换的时候必须从栈顶去操作,无法再保持状态的同时进行平级切换。举个例子:有两个页面A,B,当前B在栈顶。切换到A需要把B从栈顶Pop出去,此时B的状态丢失,如果想切回B,我们只能重新打开B之前页面的状态无法维持住。如在pop的过程当中,可能会把Flutter 官方的Dialog进行误杀。而且基于栈的操作我们依赖对Flutter框架的一个属性修改,这让这个方案具有了侵入性的特点。具体细节,大家可以参考老方案开源项目地址:https://github.com/alibaba-flutter/hybridstackmanager新一代混合技术方案 FlutterBoost1重构计划在闲鱼推进Flutter化过程当中,更加复杂的页面场景逐渐暴露了老方案的局限性和一些问题。所以我们启动了代号FlutterBoost(向C++ Boost库致敬)的新混合技术方案。这次新的混合方案我们的主要目标有:可复用通用型混合方案支持更加复杂的混合模式,比如支持主页Tab这种情况无侵入性方案:不再依赖修改Flutter的方案支持通用页面生命周期统一明确的设计概念跟老方案类似,新的方案还是采用共享引擎的模式实现。主要思路是由Native容器Container通过消息驱动Flutter页面容器Container,从而达到Native Container与Flutter Container的同步目的。我们希望做到Flutter渲染的内容是由Naitve容器去驱动的。简单的理解,我们想做到把Flutter容器做成浏览器的感觉。填写一个页面地址,然后由容器去管理页面的绘制。在Native侧我们只需要关心如果初始化容器,然后设置容器对应的页面标志即可。2主要概念3Native层概念Container:Native容器,平台Controller,Activity,ViewControllerContainer Manager:容器的管理者Adaptor:Flutter是适配层Messaging:基于Channel的消息通信4Dart层概念Container:Flutter用来容纳Widget的容器,具体实现为Navigator的派生类-Container Manager:Flutter容器的管理,提供show,remove等ApiCoordinator: 协调器,接受Messaging消息,负责调用Container Manager的状态管理。Messaging:基于Channel的消息通信5关于页面的理解在Native和Flutter表示页面的对象和概念是不一致的。在Native,我们对于页面的概念一般是ViewController,Activity。而对于Flutter我们对于页面的概念是Widget。我们希望可统一页面的概念,或者说弱化抽象掉Flutter本身的Widget对应的页面概念。换句话说,当一个Native的页面容器存在的时候,FlutteBoost保证一定会有一个Widget作为容器的内容。所以我们在理解和进行路由操作的时候都应该以Native的容器为准,Flutter Widget依赖于Native页面容器的状态。那么在FlutterBoost的概念里说到页面的时候,我们指的是Native容器和它所附属的Widget。所有页面路由操作,打开或者关闭页面,实际上都是对Native页面容器的直接操作。无论路由请求来自何方,最终都会转发给Native去实现路由操作。这也是接入FlutterBoost的时候需要实现Platform协议的原因。另一方面,我们无法控制业务代码通过Flutter本身的Navigator去push新的Widget。对于业务不通过FlutterBoost而直接使用Navigator操作Widget的情况,包括Dialog这种非全屏Widget,我们建议是业务自己负责管理其状态。这种类型Widget不属于FlutterBoost所定义的页面概念。理解这里的页面概念,对于理解和使用FlutterBoost至关重要。6与老方案主要差别前面我们提到老方案在Dart层维护单个Navigator栈结构用于Widget的切换。而新的方案则是在Dart侧引入了Container的概念,不再用栈的结构去维护现有的页面,而是通过扁平化key-value映射的形式去维护当前所有的页面,每个页面拥有一个唯一的id。这种结构很自然的支持了页面的查找和切换,不再受制于栈顶操作的问题,之前的一些由于pop导致的问题迎刃而解。也不需要依赖修改Flutter源码的形式去进行页面栈操作,去掉了实现的侵入性。实际上我们引入的Container就是Navigator的,也就是说一个Native的容器对应了一个Navigator。那这是如何做到的呢?7多Navigator的实现Flutter在底层提供了让你自定义Navigator的接口,我们自己实现了一个管理多个Navigator的对象。当前最多只会有一个可见的Flutter Navigator,这个Navigator所包含的页面也就是我们当前可见容器所对应的页面。Native容器与Flutter容器(Navigator)是一一对应的,生命周期也是同步的。当一个Native容器被创建的时候,Flutter的一个容器也被创建,它们通过相同的id关联起来。当Native的容器被销毁的时候,Flutter的容器也被销毁。Flutter容器的状态是跟随Native容器,这也就是我们说的Native驱动。由Manager统一管理切换当前在屏幕上展示的容器。我们用一个简单的例子描述一个新页面创建的过程:创建Native容器(iOS ViewController,Android Activity or Fragment)。Native容器通过消息机制通知Flutter Coordinator新的容器被创建。Flutter Container Manager进而得到通知,负责创建出对应的Flutter容器,并且在其中装载对应的Widget页面。当Native容器展示到屏幕上时,容器发消息给Flutter Coordinator通知要展示页面的id.Flutter Container Manager找到对应id的Flutter Container并将其设置为前台可见容器。这就是一个新页面创建的主要逻辑,销毁和进入后台等操作也类似有Native容器事件去进行驱动。总结目前FlutterBoost已经在生产环境支撑着在闲鱼客户端中所有的基于Flutter开发业务,为更加负复杂的混合场景提供了支持,稳定为亿级用户提供服务。我们在项目启动之初就希望FlutterBoost能够解决Native App混合模式接入Flutter这个通用问题。所以我们把它做成了一个可复用的Flutter插件,希望吸引更多感兴趣的朋友参与到Flutter社区的建设。在有限篇幅中,我们分享了闲鱼在Flutter混合技术方案中积累的经验和代码。欢迎兴趣的同学能够积极与我们一起交流学习。扩展补充1性能相关在两个Flutter页面进行切换的时候,因为我们只有一个Flutter View所以需要对上一个页面进行截图保存,如果Flutter页面多截图会占用大量内存。这里我们采用文件内存二级缓存策略,在内存中最多只保存2-3个截图,其余的写入文件按需加载。这样我们可以在保证用户体验的同时在内存方面也保持一个较为稳定的水平。页面渲染性能方面,Flutter的AOT优势展露无遗。在页面快速切换的时候,Flutter能够很灵敏的相应页面的切换,在逻辑上创造出一种Flutter多个页面的感觉。2Release1.0的支持项目开始的时候我们基于闲鱼目前使用的Flutter版本进行开发,而后进行了Release 1.0兼容升级测试目前没有发现问题。3接入只要是集成了Flutter的项目都可以用官方依赖的方式非常方便的以插件形式引入FlutterBoost,只需要对工程进行少量代码接入即可完成接入。详细接入文档,请参阅GitHub主页官方项目文档。4现已开源目前,新一代混合栈已经在闲鱼全面应用。我们非常乐意将沉淀的技术回馈给社区。欢迎大家一起贡献,一起交流,携手共建Flutter社区。项目开源地址:https://github.com/alibaba/flutter_boost本文作者:福居阅读原文本文来自云栖社区合作伙伴“闲鱼技术”,如需转载请联系原作者。

March 18, 2019 · 1 min · jiezi

使用Ansible和Vagrant设置Kubernetes

作者:Naresh L J(Infosys)目的此博客文章介绍了为开发而设的多节点Kubernetes集群所需的步骤。此设置提供了类似生产环境的群集,可以在本地计算机上进行。为什么需要多节点群集设置?多节点Kubernetes集群提供类似生产的环境,具有各种优势。尽管Minikube提供了很好的入门平台,但它并没有提供使用多节点集群的机会,帮助解决与应用程序设计和体系结构相关的问题或错误。例如,Ops可以在多节点集群环境中重现问题,测试者可以部署多个版本的应用程序来执行测试用例和验证更改。这些优势使团队能够更快地解决问题,从而提高敏捷性。为什么使用Vagrant和Ansible?Vagrant可以让我们轻松创建虚拟环境,消除导致“在我的机器能工作”现象的陷阱。它可以与多个供应程序一起使用,例如Oracle VirtualBox、VMware、Docker等。它允许我们通过使用配置文件来创建一次性环境。Ansible是自动化软件配置管理的基础架构自动化引擎。它是无代理的,允许我们使用SSH密钥连接到远程计算机。Ansible playbooks以yaml编写,以简单的文本文件提供库存管理。先决条件在你的机器上安装Vagrant。可以在此处找到安装二进制文件。Oracle VirtualBox可以作为Vagrant供应程序,也可以使用Vagrant官方文档描述的类似供应程序。Ansible安装在你的机器上。有关特定平台的安装,请参阅Ansible安装指南。设置概述我们将建立一个Kubernetes集群,包含一个主节点和两个工作节点。所有节点将运行Ubuntu Xenial 64位操作系统,配置使用Ansible playbooks。第1步:创建Vagrantfile使用你喜欢的文本编辑器,创建名为Vagrantfile的文件,插入下面的代码。N的值表示集群中存在的节点数,可以相应地进行修改。在下面的示例中,我们将N的值设置为2。IMAGE_NAME = “bento/ubuntu-16.04"N = 2Vagrant.configure(“2”) do |config| config.ssh.insert_key = false config.vm.provider “virtualbox” do |v| v.memory = 1024 v.cpus = 2 end config.vm.define “k8s-master” do |master| master.vm.box = IMAGE_NAME master.vm.network “private_network”, ip: “192.168.50.10” master.vm.hostname = “k8s-master” master.vm.provision “ansible” do |ansible| ansible.playbook = “kubernetes-setup/master-playbook.yml” end end (1..N).each do |i| config.vm.define “node-#{i}” do |node| node.vm.box = IMAGE_NAME node.vm.network “private_network”, ip: “192.168.50.#{i + 10}” node.vm.hostname = “node-#{i}” node.vm.provision “ansible” do |ansible| ansible.playbook = “kubernetes-setup/node-playbook.yml” end end end第2步:为Kubernetes master创建Ansible playbook。在Vagrantfile相同的目录中创建名为kubernetes-setup的目录。在kubernetes-setup目录中创建两个名为master-playbook.yml和node-playbook.yml的文件。在master-playbook.yml文件中,添加以下代码。步骤2.1:安装Docker及其相关组件。我们将安装以下软件包,然后将名为“vagrant”的用户添加到“docker”组。—- hosts: all become: true tasks: - name: Install packages that allow apt to be used over HTTPS apt: name: “{{ packages }}” state: present update_cache: yes vars: packages: - apt-transport-https - ca-certificates - curl - gnupg-agent - software-properties-common - name: Add an apt signing key for Docker apt_key: url: https://download.docker.com/linux/ubuntu/gpg state: present - name: Add apt repository for stable version apt_repository: repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable state: present - name: Install docker and its dependecies apt: name: “{{ packages }}” state: present update_cache: yes vars: packages: - docker-ce - docker-ce-cli - containerd.io notify: - docker status - name: Add vagrant user to docker group user: name: vagrant group: docker步骤2.2:如果系统启用了swap,Kubelet将无法启动,因此我们使用以下代码禁用swap。 - name: Remove swapfile from /etc/fstab mount: name: “{{ item }}” fstype: swap state: absent with_items: - swap - none - name: Disable swap command: swapoff -a when: ansible_swaptotal_mb > 0步骤2.3:使用以下代码安装kubelet、kubeadm和kubectl。 - name: Add an apt signing key for Kubernetes apt_key: url: https://packages.cloud.google.com/apt/doc/apt-key.gpg state: present - name: Adding apt repository for Kubernetes apt_repository: repo: deb https://apt.kubernetes.io/ kubernetes-xenial main state: present filename: kubernetes.list - name: Install Kubernetes binaries apt: name: “{{ packages }}” state: present update_cache: yes vars: packages: - kubelet - kubeadm - kubectl步骤2.3:使用以下代码使用kubeadm初始化Kubernetes集群(仅适用于主节点)。 - name: Initialize the Kubernetes cluster using kubeadm command: kubeadm init –apiserver-advertise-address=“192.168.50.10” –apiserver-cert-extra-sans=“192.168.50.10” –node-name k8s-master –pod-network-cidr=192.168.0.0/16步骤2.4:使用以下代码为vagrant用户设置kube配置文件以访问Kubernetes集群。 - name: Setup kubeconfig for vagrant user command: “{{ item }}” with_items: - mkdir -p /home/vagrant/.kube - cp -i /etc/kubernetes/admin.conf /home/vagrant/.kube/config - chown vagrant:vagrant /home/vagrant/.kube/config步骤2.5:使用以下代码设置容器网络供应商和网络政策引擎。 - name: Install calico pod network become: false command: kubectl create -f https://docs.projectcalico.org/v3.4/getting-started/kubernetes/installation/hosted/calico.yaml步骤2.6:生成kube join命令将节点加入Kubernetes集群,并将该命令存储在名为join-command的文件中。 - name: Generate join command command: kubeadm token create –print-join-command register: join_command - name: Copy join command to local file local_action: copy content=”{{ join_command.stdout_lines[0] }}" dest="./join-command"步骤2.7:使用以下代码设置检查Docker守护程序的处理程序。 handlers: - name: docker status service: name=docker state=started步骤3:为Kubernetes节点创建Ansible playbook。在kubernetes-setup目录中创建名为node-playbook.yml的文件。将以下代码添加到node-playbook.yml中步骤3.1:开始添加步骤2.1到2.3的代码。步骤3.2:使用以下代码将节点加入Kubernetes集群。 - name: Copy the join command to server location copy: src=join-command dest=/tmp/join-command.sh mode=0777 - name: Join the node to cluster command: sh /tmp/join-command.sh步骤3.3:添加步骤2.7中的代码以完成此playbook。第4步:完成Vagrantfile和playbooks后,请按照以下步骤操作。$ cd /path/to/Vagrantfile$ vagrant up完成上述所有步骤后,Kubernetes集群应该已启动并运行。我们可以使用Vagrant登录主节点或工作节点,如下所示:$ ## Accessing master$ vagrant ssh k8s-mastervagrant@k8s-master:~$ kubectl get nodesNAME STATUS ROLES AGE VERSIONk8s-master Ready master 18m v1.13.3node-1 Ready <none> 12m v1.13.3node-2 Ready <none> 6m22s v1.13.3$ ## Accessing nodes$ vagrant ssh node-1$ vagrant ssh node-2KubeCon + CloudNativeCon + Open Source Summit大会日期:会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日KubeCon + CloudNativeCon + Open Source Summit赞助方案KubeCon + CloudNativeCon + Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国KubeCon + CloudNativeCon + Open Source Summit购票窗口,立即购票!CNCF邀请你加入最终用户社区 ...

March 18, 2019 · 3 min · jiezi

利用Serverless Kubernetes和Kaniko快速自动化构建容器镜像

摘要: 本文介绍了一种新的面向开发者的简单镜像构建实践,基于阿里云Serverless Kubernetes容器服务,可以自动化而且低成本的构建容器镜像,以便让开发者了解如何使用Serverless运行CI/CD和自动化任务。前言:在云原生时代中,容器镜像是一切应用分发的基础载体,除了dockerhub作为流行的镜像仓库外,各大公有云厂商也都提供了功能丰富镜像仓库服务,如ACR(Aliyun Container Registry), GCR(Goolge Container Registry),构建容器镜像已是所有开发者必须掌握的基础实践能力。无论开发者选择在本地使用docker完成基本的镜像构建,还是使用CI/CD服务(如Jenkins),本质上都是遵循“pull -> build -> push”的过程,完成镜像的构建、分发和同步等操作。本文介绍了一种新的面向开发者的简单镜像构建实践,基于阿里云Serverless Kubernetes容器服务,可以自动化而且低成本的构建容器镜像,以便让开发者了解如何使用Serverless运行CI/CD和自动化任务。why serverless kubernetes?容器镜像的构建是需要计算资源的,开发者在本地使用docker pull/build/push时,其计算资源是本地开发机器,如果开发者在传统kubernetes集群中部署Jenkins或Gitlab-runner服务,其计算资源也是需要持续运行。但是,容器镜像的构建基本属于高度动态的行为,往往是定时或者条件触发引起的操作,所以为了动态的构建操作而维护一个固定的计算资源池对成本是不利的。Serverless Kubernetes不同与传统基于节点的k8s集群,serverless集群中只有pod运行时才会收费,意味着只有在构建镜像时用户才需要付费,当构建结束时,也就停止计费。所以在成本上与传统k8s集群或ecs部署的方式相比显著减少。Kaniko在Serverless Kubernetes集群中,pod没有privileged权限,无法访问主机上的docker daemon,也就无法使用docker in docker方案进行镜像的操作,那么如何在kubernetes集群中不依赖宿主机的Docker情况下构建镜像呢?显然这是一个通用需求,社区也有了推荐的方案:kaniko。kaniko的工作原理与docker build类似,但是不依赖docker daemon,在pod中完成Dockfile各层的解析和build,这使得pod不需要privileged权限也可以完成镜像的pull/build/push。实践示例:定时同步容器镜像下面让我们使用Serverless Kubernetes和Kaniko实现一个简单的镜像build实验:定时同步镜像到国内ACR。步骤1: 创建Serverless Kubernetes集群登陆容器服务控制台, 5s即可完成Serverless集群创建。(如果是国外的源镜像仓库,可以选择美西区域)步骤2: 创建secret,配置ACR的用户名密码,用于推送镜像到ACR可登陆cloudshell操作如下命令。#docker login registry.cn-hangzhou.aliyuncs.com…#kubectl create secret generic regsecret –from-file=/root/.docker/config.json…步骤3: 创建定时任务CronJob在控制台选择模版创建如下定时任务,每小时同步busybox镜像到ACR。apiVersion: v1kind: ConfigMapmetadata: name: dockerfile-cmdata: Dockerfile: | FROM busybox:latest—apiVersion: batch/v1beta1kind: CronJobmetadata: name: kaniko-builderspec: schedule: “*/60 * * * *” jobTemplate: spec: template: spec: containers: - name: builder image: gcr.io/kaniko-project/executor:latest imagePullPolicy: Always args: - “–dockerfile=/configs/Dockerfile” - “–destination=registry.cn-hangzhou.aliyuncs.com/jovizhangwei/busybox:latest” volumeMounts: - name: dockerfile readOnly: true mountPath: “/configs/” - name: secrets readOnly: true mountPath: “/root/.docker/” volumes: - name: dockerfile configMap: name: dockerfile-cm - name: secrets secret: secretName: regsecret restartPolicy: OnFailure待job执行后,可查看ACR镜像仓库,确认镜像已同步。用户可以按照需求定制此模版文件,比如修改需要同步的镜像,添加build步骤等,也可以设置pod的资源限制(vcpu 0.25/0.5/1等), 以最小的计算成本完成同步任务。结束通过上面的示例,我们看到基于Serverless Kubernetes的按需付费特性,可以使用很低的成本运行一些定时和CI/CD任务,而不用维护一个固定的计算资源池,其同样适用于压力测试、数据计算、工作流处理等其他场景。Happy Hacking!更多参考信息Serverless Kubernetes快速部署jenkins环境及执行流水线构建: https://yq.aliyun.com/articles/685219kaniko intro:https://cloud.google.com/blog/products/gcp/introducing-kaniko-build-container-images-in-kubernetes-and-google-container-builder-even-without-root-access本文作者:贤维阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 15, 2019 · 1 min · jiezi

云原生土壤孕育的开源森林

当今的社会是高速发展的新型科技社会,互联网、物联网、人工智能等一系列新鲜事物如雨后春笋般喷薄而出,并以前所未有的速度迅速成长。在这个风起“云”涌的时代,云原生技术作为孕育这些高科技的土壤,亦是备受世人关注。早在2013年的时候,云原生 (Cloud Native) 这个概念一经提出,就已经广受专家们的关注与推崇。再经历了诸多大企业的部署、实践、应用之后,云原生技术的优势更进一步显现,它不仅带动了新的 IT 技术的大发展,也加速了很多类型企业向云技术化转型的进程。那么,火热的云原生究竟怎么去理解呢?其实,由于它具有可以为企业迅速部署新业务的特性,且在我们的实际应用中,可以清晰的感受到它的高弹性、动态调度、自动伸缩等一些传统IT技术所不具备的能力,在部署过程中除了我们要考量技术上的如 :DevOps、持续交付、微服务、敏捷基础设施等特性,也要考量如何体现其与企业管理、企业文化之间的深度融合,所以,综上考虑,我们可以认为云原生的实质,就是一个关联着管理与技术跨层面交叠的融合式集合。为了便于更好的运用云原生应用为我们服务,2015年,由谷歌牵头成立了 CNCF (Cloud Native Computing Foundation,云原生计算基金会) 。该组织来自社区的技术监督委员会整理了“云原生”的概念并通过了“CNCF 云原生定义 v1.0”:云原生技术有利于各组织在公有云,私有云和混合云等新型动态环境中,构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段,云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。云原生计算基金会 (CNCF) 致力于培育和维护一个厂商中立的开源生态系统,来推广云原生技术。我们通过将最前沿的模式民主化,让这些创新为大众所用。之后, CNCF 绘制了 Cloud Native Landscape,并不断更新给出了比较详尽的云原生生态的参考体系。CNCF 也绘制了无服务器 Landscape,从而能够更好的跟踪这一热门领域的发展。以下是CNCF 无服务器 Landscape。为了便于大家理解和运用不同阶段的云原生应用,CNCF 在这片广袤的开源生态中,为不同程度的使用者绘制了不同应用阶段的 Trail map,来提供参考。自2017年的 LC3 大会登陆中国以来,云原生的议题便在中国的 IT 界生根发芽,蓬勃发展,面对这一高速发展的态势,我们甚至可以毫不夸张的说,2017年是云原生在中国发展的开元之年,如:Kubernetes 与华为 IT 系统的 PaaS 演变 – Kevin Wang & Jianlin Wu,华为;阿里巴巴在 Linux 内核中资源管理工作 – 马涛,阿里云;Kubernetes API & 下一代自动化工具 – Ian Lewis,Google Inc.;随着 Kubernetes 不断的融入我们的生产环境,在2018的 LC3 大会中,我们更是聆听了许多热门大咖关于这一热门领域的演讲,如:采用 Kubernetes 进行基因组测序的最佳实践 – Shengjun Tang,华为;基于 JStorm 的网络分析平台 – Biao Lyu,阿里云;DPDK 中的 FPGA 和虚拟化技术加速和扩展云网络 – Tianfei Zhang 和 Rosen Xu,Intel;Bcache 稳定性 – 云基础设施的改进 – Coly (Yong) Li,SUSE;在 KVM / QEMU 上实现更好的实时迁移 – Guangong Xiao,Tencent Cloud;使用开源工具加速 Kubernetes 上的云原生应用程序的 DevOps – Rome Li,Microsoft;在同年举办的首届 KubeCon + CloudNativeCon 中国论坛中,大咖们继续为我们带来了更精彩的演讲,如:CNCF 互动景观 – Dan Kohn,云原生计算基金会 (CNCF) ;中国的云原生生态系统 – Tao Ma,阿里云首席工程师;使用 “KubeEdge” 管理边缘节点 – Yulin Sun, 华为 和 Li Xing, 华为;谁在运行我的 Pod?深入研究 K8s Container Runtime Interface – Phil Estes,IBM;用于腾讯机器学习的深度定制 Kubernetes – Shengbo Song,腾讯。那么,在2019年,有关云原生对开源应用的影响等方面的议题又会有哪些大咖加以完善,并与您分享最新的实战观点呢?请您务必不要错过,2019年,CNCF和LF为您即将呈现的开源技术盛宴 - KubeCon + CloudNativeCon + Open Source Summit (原LC3)。KubeCon + CloudNativeCon + Open Source Summit大会日期:会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日KubeCon + CloudNativeCon + Open Source Summit赞助方案KubeCon + CloudNativeCon + Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国KubeCon + CloudNativeCon + Open Source Summit购票窗口,立即购票!CNCF邀请你加入最终用户社区 ...

March 14, 2019 · 1 min · jiezi

CNCF发布KubeCon + CloudNativeCon欧洲2019节目表

基金会迄今为止最大的欧洲活动将包括蚂蚁金服、AT&T、Dyson、Etsy.com、GO-JEK、NTT Corporation、NVIDIA、Offerup、Spotify、Zalando等的演讲加利福尼亚州旧金山 - 2019年3月13日 - 支持和集成Kubernetes®和Prometheus™等开源技术的CNCF(云原生计算基金会)发布了于2019年5月20日至23日在巴塞罗那举行的KubeCon + CloudNativeCon 欧洲的会议阵容。这次活动的亮点包括专家分享使用Kubernetes和其他云原生技术的真实案例研究,他们来自ADMIRALTY、Booking.com、英国电信、Carnegie Mellon、eBay、Engel & Völkers Technology、ING、领英、LSST & French National Institute of Nuclear and Particle Physics (IN2P3)、Mediakind、McKesson、VTT等机构。继去年12月在西雅图举办,有史以来规模最大的KubeCon会议之后,KubeCon + CloudNativeCon 欧洲将汇集来自全球蓬勃发展的开源社区的10,000多名技术专家,以进一步围绕云原生计算进行协作。 CNCF托管项目的维护者和最终用户 - 包括Kubernetes、Prometheus、Envoy、CoreDNS、containerd、OpenTracing、Fluentd、gRPC、rkt、CNI、Jaeger、Notary、TUF、Vitess、NATS、Rook、Harbour、etcd、Linkerd和Helm - 和其他云原生技术将聚集四天,分享见解并鼓励参与这个快速发展的生态系统。3月20日前注册KubeCon + CloudNativeCon欧洲,最高可节省300美元。“令人惊讶的是,我们有望从去年12月在西雅图的8,000人,有史以来最大的开源开发者大会,到今年5月在巴塞罗那召开规模更多的会议。”CNCF执行总Dan Kohn说。“云原生计算已经跨越了鸿沟,因此来自数百个最终用户组织的开发者渴望与社区其他成员合作。”由会议联合主席VMware的Bryan Liles和Google Cloud的Janet Kuo领导的110名专家组成的计划委员会,审核了1,535份提交,以帮助创建该节目的多样化内容。该议程包括令人印象深刻的主题组合,包括技术会议、深度研究和案例研究,涵盖应用和开发(包括Helm和Telepresence)、CI/CD、定制和扩展Kubernetes、促进社区、机器学习和数据、网络(包括NATS、CoreDNS、CNI和gRPC)、可观察性(包括Jaeger、OpenTracing、Fluentd和Prometheus)、操作、性能+可扩展性、运行时(包括containerd和rkt)、安全性、身份和策略(包括OPA、SPIFFE/SPIRE、Notary和TUF)、无服务器(包括CloudEvents)、存储(包括Vitess和Rook)等。“在审阅巴塞罗那的提交文件时,我们对整个社区广泛采用云原生技术,以及从研究机构到银行的所有垂直行业感到惊讶。我们对这个快速发展的生态系统中构建的新工具和技术印象深刻。”KubeCon + CloudNativeCon联合主席兼Google软件工程师Janet Kuo表示。“我们希望今年的与会者能够从专家和项目维护者那里获得第一手新知识,以及使用这些技术的实践经验。”“能够代表这样一个快速发展的多元化社区,从根本上改变我们构建软件和数字体验的方式,我感到非常荣幸。”VMware高级工程师兼会议联合主席Bryan Liles说。“今年的节目表包含了我们见过的一些最具创新性和信息量的会议,我们期待成员们聚在一起分享他们的知识。”社区策划的节目表将包括来自领先的开源技术专家的会议,包括:“How Spotify accidentally deleted all its Kube clusters, with no user impact” – David Xia, Spotify“Getting Started in the Kubernetes Community” – Lucas Käldström & Nikhita Raghunath, Independent“Reperforming a Nobel Prize Discovery on Kubernetes” – Ricardo Rocha & Lukas Heinrich, CERN“From COBOL to Kubernetes: A 250 Year Old Bank’s Cloud-Native Journey” – Mike Ryan, Epitech BV & Laura Rehorst, ABN AMRO Bank NV“Panel Discussion: GitOps & Best Practices for Cloud Native CI/CD” – Moderated by Laura Tacho, CloudBees“Kubernetes the new Research Platform” – Lindsey Tulloch, Brock University & Bob Killen, University of Michigan“From Snowflake Servers to Snowflake Clusters – The GitOps Journey” – Allison Richardet, Asteris, LLC & Fabio Giannetti, MasterCard“Panel Discussion: Democratizing HPC & AI: Startups Scale up with Cloud Native” – Moderated by Emily Tanaka-Delgado, Oracle“Kubectl Apply 2019: Defense against the Dark Arts” – Phillip Wittrock & Jennifer Buckley, Google“Helm 3: Navigating To Distant Shores” – Bridget Kromhout, Microsoft & Jessica Deen, Microsoft“Sharing is Caring: Your Kubernetes Cluster, Namespaces, and You” – Amy Chen, VMware & Eryn Muetzel, VMware“Panel Discussion: Going Multi-cloud for realz. Stories from media, retail, & infrastructure” – Moderated by Lisa-Marie Namphy, Portworx作为KubeCon + CloudNativeCon的一部分,CNCF还将于5月20日星期一举办以下同地活动 - 如果有兴趣,与会者应在预订航班和酒店时预早计划:KubeSec Enterprise Summit hosted by Aqua SecurityA Linkerd in Production Workshop hosted by BuoyantContinuous Delivery Summit hosted by CDFCephaloconContributor SummitServerless Practitioners Summit hosted by CNCFCNCF End User Partner SummitCloud Native Storage Day hosted by CNS EcosystemKubernetes Operator Framework Workshop hosted by Red HatOpenShift Commons Gathering hosted by Red HatIntro to Containers & Kubernetes hosted by VMware完整的KubeCon + CloudNativeCon欧洲计划,请到日程安排。谢谢赞助商在赞助商的支持下,KubeCon + CloudNativeCon成为可能,钻石赞助商:Cisco、IBM Cloud、Microsoft Azure、Oracle、Red Hat和VMware;白金赞助商:AWS、Ballerina、CloudBees、Google Cloud、Mirantis、Pivotal、Rancher、Sysdig和Ubuntu,以及更多金牌、银牌、初创公司和媒体赞助商。有关赞助的更多信息可在网站获得。注册和住宿在太平洋标准时间3月20日晚上11:59之前注册,以节省300美元的会议通行证。此外,网站的Venue/Travel部分提供酒店房价折扣。请提前预订,因为折扣价格视供应情况而定。KubeCon + CloudNativeCon + Open Source Summit大会日期:会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日KubeCon + CloudNativeCon + Open Source Summit赞助方案KubeCon + CloudNativeCon + Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国KubeCon + CloudNativeCon + Open Source Summit购票窗口,立即购票!CNCF邀请你加入最终用户社区 ...

March 14, 2019 · 2 min · jiezi

容器监控实践—Prometheus存储机制

概述Prometheus提供了本地存储,即tsdb时序数据库,本地存储给Prometheus带来了简单高效的使用体验,prometheus2.0以后压缩数据能力也得到了很大的提升。可以在单节点的情况下满足大部分用户的监控需求。但本地存储也限制了Prometheus的可扩展性,带来了数据持久化等一系列的问题。为了解决单节点存储的限制,prometheus没有自己实现集群存储,而是提供了远程读写的接口,让用户自己选择合适的时序数据库来实现prometheus的扩展性。Prometheus 1.x版本的TSDB(V2存储引擎)基于LevelDB,并且使用了和Facebook Gorilla一样的压缩算法,能够将16个字节的数据点压缩到平均1.37个字节。Prometheus 2.x版本引入了全新的V3存储引擎,提供了更高的写入和查询性能以下所有内容均基于prometheus2.7版本本地存储存储原理Prometheus按2小时一个block进行存储,每个block由一个目录组成,该目录里包含:一个或者多个chunk文件(保存timeseries数据)、一个metadata文件、一个index文件(通过metric name和labels查找timeseries数据在chunk文件的位置)。最新写入的数据保存在内存block中,达到2小时后写入磁盘。为了防止程序崩溃导致数据丢失,实现了WAL(write-ahead-log)机制,启动时会以写入日志(WAL)的方式来实现重播,从而恢复数据。删除数据时,删除条目会记录在独立的tombstone文件中,而不是立即从chunk文件删除。通过时间窗口的形式保存所有的样本数据,可以明显提高Prometheus的查询效率,当查询一段时间范围内的所有样本数据时,只需要简单的从落在该范围内的块中查询数据即可。这些2小时的block会在后台压缩成更大的block,数据压缩合并成更高level的block文件后删除低level的block文件。这个和leveldb、rocksdb等LSM树的思路一致。这些设计和Gorilla的设计高度相似,所以Prometheus几乎就是等于一个缓存TSDB。它本地存储的特点决定了它不能用于long-term数据存储,只能用于短期窗口的timeseries数据保存和查询,并且不具有高可用性(宕机会导致历史数据无法读取)。内存中的block数据未写入磁盘时,block目录下面主要保存wal文件:./data/01BKGV7JBM69T2G1BGBGM6KB12./data/01BKGV7JBM69T2G1BGBGM6KB12/meta.json./data/01BKGV7JBM69T2G1BGBGM6KB12/wal/000002./data/01BKGV7JBM69T2G1BGBGM6KB12/wal/000001持久化的block目录下wal文件被删除,timeseries数据保存在chunk文件里。index用于索引timeseries在wal文件里的位置。./data/01BKGV7JC0RY8A6MACW02A2PJD./data/01BKGV7JC0RY8A6MACW02A2PJD/meta.json./data/01BKGV7JC0RY8A6MACW02A2PJD/index./data/01BKGV7JC0RY8A6MACW02A2PJD/chunks./data/01BKGV7JC0RY8A6MACW02A2PJD/chunks/000001./data/01BKGV7JC0RY8A6MACW02A2PJD/tombstones存储配置对于本地存储,prometheus提供了一些配置项,主要包括:–storage.tsdb.path: 存储数据的目录,默认为data/,如果要挂外部存储,可以指定该目录–storage.tsdb.retention.time: 数据过期清理时间,默认保存15天–storage.tsdb.retention.size: 实验性质,声明数据块的最大值,不包括wal文件,如512MB–storage.tsdb.retention: 已被废弃,改为使用storage.tsdb.retention.timePrometheus将所有当前使用的块保留在内存中。此外,它将最新使用的块保留在内存中,最大内存可以通过storage.local.memory-chunks标志配置。监测当前使用的内存量:prometheus_local_storage_memory_chunksprocess_resident_memory_bytes监测当前使用的存储指标:prometheus_local_storage_memory_series: 时间序列持有的内存当前块数量prometheus_local_storage_memory_chunks: 在内存中持久块的当前数量prometheus_local_storage_chunks_to_persist: 当前仍然需要持久化到磁盘的的内存块数量prometheus_local_storage_persistence_urgency_score: 紧急程度分数prometheus 2.0的存储升级prometheus 2.0于2017-11-08发布,主要是存储引擎进行了优化。性能的整体提高:与 Prometheus 1.8 相比,CPU使用率降低了 20% - 40%与 Prometheus 1.8 相比,磁盘空间使用率降低了 33% - 50%没有太多查询,平均负载的磁盘 I/O<1%在Kubernetes集群这样的动态环境中,prometheus的数据平面通常看起来是这种样式垂直维度表示所有存储的序列水平维度表示样本传播的时间如:requests_total{path="/status", method=“GET”, instance=“10.0.0.1:80”}requests_total{path="/status", method=“POST”, instance=“10.0.0.3:80”}requests_total{path="/", method=“GET”, instance=“10.0.0.2:80”}Prometheus定期为所有系列收集新数据点,这意味着它必须在时间轴的右端执行垂直写入。但是,在查询时,我们可能希望访问平面上任意区域的矩形(各种label条件)因此为了能够在大量数据中有效地查找查询序列,我们需要一个索引。在Prometheus 1.x存储层可以很好地处理垂直写入模式,但是随着规模增大,索引或出现一些问题,因此在2.0版本中重新设计了存储引擎和索引,主要改造是:样本压缩现有存储层的样本压缩功能在Prometheus的早期版本中发挥了重要作用。单个原始数据点占用16个字节的存储空间。但当普罗米修斯每秒收集数十万个数据点时,可以快速填满硬盘。但,同一系列中的样本往往非常相似,我们可以利用这一类样品(同样label)进行有效的压缩。批量压缩一系列的许多样本的块,在内存中,将每个数据点压缩到平均1.37字节的存储。这种压缩方案运行良好,也保留在新版本2存储层的设计中。具体压缩算法可以参考:Facebook的“Gorilla”论文中时间分片我们将新的存储层划分为块(block),每个块在一段时间内保存所有序列。每个块充当独立数据库。这样每次查询,仅检查所请求的时间范围内的块子集,查询执行时间自然会减少。这种布局也使删除旧数据变得非常容易(这在1.x的存储设计中是一个很耗时的操作)。但在2.x中,一旦块的时间范围完全落后于配置的保留边界,它就可以完全丢弃。索引一般prometheus的查询是把metric+label做关键字的,而且是很宽泛,完全用户自定义的字符,因此没办法使用常规的sql数据库,prometheus的存储层使用了全文检索中的倒排索引概念,将每个时间序列视为一个小文档。而metric和label对应的是文档中的单词。例如,requests_total{path="/status", method=“GET”, instance=“10.0.0.1:80”}是包含以下单词的文档:name=“requests_total"path="/status"method=“GET"instance=“10.0.0.1:80"基准测试cpu、内存、查询效率都比1.x版本得到了大幅度的提升具体测试结果参考:https://dzone.com/articles/pr…故障恢复如果您怀疑数据库中的损坏引起的问题,则可以通过使用storage.local.dirtyflag配置,来启动服务器来强制执行崩溃恢复。如果没有帮助,或者如果您只想删除现有的数据库,可以通过删除存储目录的内容轻松地启动:1.停止服务:stop prometheus.2.删除数据目录:rm -r <storage path>/*3.启动服务:start prometheus远程存储Prometheus默认是自己带有存储的,保存的时间为15天。但本地存储也意味着Prometheus无法持久化数据,无法存储大量历史数据,同时也无法灵活扩展。为了保证Prometheus的简单性,Prometheus并没有从自身集群的维度来解决这些问题,而是定义了两种接口,remote_write/remote_read,将数据抛出去,你自己处理。Prometheus的remote_storage 其实是一个adapter,至于在adapter的另一端是什么类型的时序数据库它根本不关心,如果你愿意,你也可以编写自己的adpater。如:存储的方式为:Prometheus —-发送数据—- > remote_storage_adapter —- 存储数据 —-> influxdb。prometheus通过下面两种方式来实现与其他的远端存储系统对接:Prometheus 按照标准的格式将metrics写到远端存储Prometheus 按照标准格式从远端的url来读取metrics远程读在远程读的流程当中,当用户发起查询请求后,Promthues将向remote_read中配置的URL发起查询请求(matchers,ranges),Adaptor根据请求条件从第三方存储服务中获取响应的数据。同时将数据转换为Promthues的原始样本数据返回给Prometheus Server。当获取到样本数据后,Promthues在本地使用PromQL对样本数据进行二次处理。远程写用户可以在Promtheus配置文件中指定Remote Write(远程写)的URL地址,一旦设置了该配置项,Prometheus将样本数据通过HTTP的形式发送给适配器(Adaptor)。而用户则可以在适配器中对接外部任意的服务。外部服务可以是真正的存储系统,公有云的存储服务,也可以是消息队列等任意形式。配置配置非常简单,只需要将对应的地址配置下就行remote_write: - url: “http://localhost:9201/write"remote_read: - url: “http://localhost:9201/read"社区支持现在社区已经实现了以下的远程存储方案AppOptics: writeChronix: writeCortex: read and writeCrateDB: read and writeElasticsearch: writeGnocchi: writeGraphite: writeInfluxDB: read and writeOpenTSDB: writePostgreSQL/TimescaleDB: read and writeSignalFx: write可以使用读写完整的InfluxDB,我们使用了多prometheus server同时远程读+写,验证了速度还是可以的。并且InfluxDB生态完整,自带了很多管理工具。容量规划在一般情况下,Prometheus中存储的每一个样本大概占用1-2字节大小。如果需要对Prometheus Server的本地磁盘空间做容量规划时,可以通过以下公式计算:磁盘大小 = 保留时间 * 每秒获取样本数 * 样本大小保留时间(retention_time_seconds)和样本大小(bytes_per_sample)不变的情况下,如果想减少本地磁盘的容量需求,只能通过减少每秒获取样本数(ingested_samples_per_second)的方式。因此有两种手段,一是减少时间序列的数量,二是增加采集样本的时间间隔。考虑到Prometheus会对时间序列进行压缩,因此减少时间序列的数量效果更明显。其他远程读写解决了Promtheus的数据持久化问题。使其可以进行弹性扩展。另外还支持联邦集群模式,用于解决横向扩展、网络分区的问题(如地域A+B+C的监控数据,统一汇总到D),联邦集群的配置将在后面的Promthues高可用文章中详细说明。附:kubecon2018上讲Prometheus 2.0的帅哥还有一本专门讲Prometheus的书:Prometheus: Up & Running(600多页…)国内没找到卖的,找到了一本英文pdf的,还在翻译理解中,有新的内容会继续同步在这个系列博客。。。又找到一本:https://www.prometheusbook.com/参考资料:https://prometheus.io/docs/pr…https://coreos.com/blog/prome...https://dzone.com/articles/pr...https://www.linuxidc.com/Linu...http://ylzheng.com/2018/03/06...https://www.cnblogs.com/vovli...https://files-cdn.cnblogs.com...https://www.bookstack.cn/read…本文为容器监控实践系列文章,完整内容见:container-monitor-book ...

March 12, 2019 · 1 min · jiezi

上下文切换,你确定了解吗?

本文由云+社区发表作者:cocoding前言听到上下文切换,大家第一反应肯定是:一定要减少这货出现的次数。确实上下文切换对性能的影响显而易见,但有时又无法完全避免,这就要求我们对上下文性能损耗了然于胸,才能更准确地评估系统性能。另外,现在云厂商提供的机器种类如此之多,虚拟机在这方面是否有区别。以上都需要有科学的方法来衡量上下文的耗时,进而帮助系统评估以及机型选择。本文将从这以下两个方面来展开上下文切换有哪些类型以及可能出现的场景衡量各场景上下文切换耗时1, 上下文切换类型及场景上下文大体上可以分为两类进程上下文中断上下文进程上下文具体包括:(1)用户级上下文: 正文、数据、用户堆栈以及共享存储区;(2)寄存器上下文: 通用寄存器、程序寄存器(IP)、处理器状态寄存器(EFLAGS)、栈指针(ESP);(3)系统级上下文: 进程控制块task_struct、内存管理信息(mm_struct、vm_area_struct、pgd、pte)、内核栈。中断上下文具体包括:(1)硬件传递过来的参数因此上下文切换可以分为以下几类:(1)进程之间的上下文切换:A进程切换到B进程(2)进程和中断之间的上下文切换:进程A被中断打断(3)中断之间的上下文切换:低级别中断被高级别中断打断其中第一种上下文切换最为常见,第二种次之,第三种最少见,因此本文接下来主要讨论前面两种上下文切换的耗时。模式切换这是要说一种特殊的上下文切换:模式切换,即进程A从用户态因为系统调用进入内核态,这种切换之所以特殊,是因为它并没有经过完整的上下文切换,只是寄存器上下文进行了切换,所以模式切换的耗时相对完整进程上下文更低。虽然模式切换较完整上下文切换耗少,但仍不能小觑,在物理机上,一次系统调用(以SYS_gettid为例)在5060ns。(本文所有数据均是Intel(R) Xeon(R) V4和V5 CPU上得到)而在虚拟机上,一次系统调用更是可能达到240ns ,从perf来看,system_call_after_swapgs函数消耗CPU较物理机多很多,网上有人说可能是为了解决Spectre漏洞,在每次系统调 用返回用户空间时,会清理一次BTB(branch target buffer),需要进一步确认。1.png因此,我们在代码里面也要尽量减少系统调用,常见的优化方法有:每次读写磁盘时,使用buffer减少read/write调用次数等。2,上下文切换性能评估测试上下文切换性能的工具有unixbenchtsuna/contextswitch两个工具的原理类似,都是创建两个进程,然后互相唤醒:(1) unixbench是创建两个进程,两个进程之间创建两个管道(pipe),通过管道来互相读写数据,结果是10s内完成的切换次数。(2) contextswitch同样是创建两个进程,通过futex(快速用户区互斥)来互相唤醒,结果是循环500000次的耗时进程之间的上下文切换使用这两个工具在测试进程上下文时,需要注意一点:把两个进程绑定到同一个核上运行,否则可能测试的就不仅仅是进程上下文切换了,下面会介绍不绑核的情况。unixbenchtaskset -c 1 ./Run -c 1 context1对于contextswitchtaskset -c 1 ./timectxsw或者直接运行make或者./cpubench.sh2.png从上图可以看到,一次ctx的耗时在10121263ns ,但其实perf看,绑核情况下,运行timectxsw实际的ctx是在300w次(这里一次循环需要6次ctx),所以实际的ctx应该是674842ns3.png进程和中断上下文切换上文提到如果要测试进程上下文切换耗时就一定要绑核,否则测试的很可能会包含进程和中断上下文切换的耗时,因为默认内核会把测试程序产生的进程调度到不同的核上,进程之间的唤醒,需要先发送IPI中断,对方CPU在收到IPI中断之后,会完成一次中断上下文切换,执行中断函数,进而再唤醒相应进程。所以在不绑核的情况下,测试的就包含了进程-中断上下文以及中断处理函数的耗时。4.pngunixbench 如果使用unixbench在腾讯云上,默认调度到1个核上,这样就测试的进程上下文切换,所以需要手动修改代码绑核,或者用git上的unixbench-fix,强制将两个进程放到不同的核上5.pngcontextswitch contextswitch这里还增加了在同一个NUMA上的测试,从测试数据看,两个进程如果调度到同一个NUMA上时,耗时会更短。6.png从测试数据看:如果两个进程跨NUMA,一次上下文切换的耗时在2500ns如果两个进程在同NUMA,一次上下文切换的耗时在1500ns在虚拟机里面,跨核的上下文切换会更大,因为vcpu无法处理IPI中断,需要退出的宿主机上处理,从而增加了上下文切换的耗时,总体上虚拟机跨核ctx的耗时是宿主机的23倍。下一篇文章我们会对IPI中断的测试方法以及虚拟化之后可能的优化方法进行介绍,欢迎订阅,及时查看。此文已由腾讯云+社区在各渠道发布获取更多新鲜技术干货,可以关注我们腾讯云技术社区-云加社区官方号及知乎机构号

March 12, 2019 · 1 min · jiezi

Nvidia GPU如何在Kubernetes 里工作

Nvidia GPU如何在Kubernetes 里工作本文介绍Nvidia GPU设备如何在Kubernetes中管理调度。 整个工作流程分为以下两个方面:如何在容器中使用GPUKubernetes 如何调度GPU如何在容器中使用GPU想要在容器中的应用可以操作GPU, 需要实两个目标容器中可以查看GPU设备容器中运行的应用,可以通过Nvidia驱动操作GPU显卡详细介绍可见: https://devblogs.nvidia.com/gpu-containers-runtime/Nvidia-dockerGitHub: https://github.com/NVIDIA/nvidia-dockerNvidia提供Nvidia-docker项目,它是通过修改Docker的Runtime为nvidia runtime工作,当我们执行 nvidia-docker create 或者 nvidia-docker run 时,它会默认加上 –runtime=nvidia 参数。将runtime指定为nvidia。当然,为了方便使用,可以直接修改Docker daemon 的启动参数,修改默认的 Runtime为 nvidia-container-runtime cat /etc/docker/daemon.json{ “default-runtime”: “nvidia”, “runtimes”: { “nvidia”: { “path”: “/usr/bin/nvidia-container-runtime”, “runtimeArgs”: [] } }}gpu-containers-runtimeGitHub: https://github.com/NVIDIA/nvidia-container-runtimegpu-containers-runtime 是一个NVIDIA维护的容器 Runtime,它在runc的基础上,维护了一份 Patch, 我们可以看到这个patch的内容非常简单, 唯一做的一件事情就是在容器启动前,注入一个 prestart 的hook 到容器的Spec中(hook的定义可以查看 OCI规范 )。这个hook 的执行时机是在容器启动后(Namespace已创建完成),容器自定义命令(Entrypoint)启动前。nvidia-containers-runtime 定义的 prestart 的命令很简单,只有一句 nvidia-container-runtime-hook prestart gpu-containers-runtime-hookGitHub: https://github.com/NVIDIA/nvidia-container-runtime/tree/master/hook/nvidia-container-runtime-hook gpu-containers-runtime-hook 是一个简单的二进制包,定义在Nvidia container runtime的hook中执行。 目的是将当前容器中的信息收集并处理,转换为参数调用 nvidia-container-cli 。主要处理以下参数:根据环境变量 NVIDIA_VISIBLE_DEVICES 判断是否会分配GPU设备,以及挂载的设备ID。如果是未指定或者是 void ,则认为是非GPU容器,不做任何处理。 否则调用 nvidia-container-cli , GPU设备作为 –devices 参数传入环境环境变量 NVIDIA_DRIVER_CAPABILITIES 判断容器需要被映射的 Nvidia 驱动库。环境变量 NVIDIA_REQUIRE_ 判断GPU的约束条件。 例如 cuda>=9.0 等。 作为 –require= 参数传入传入容器进程的Pidgpu-containers-runtime-hook 做的事情,就是将必要的信息整理为参数,传给 nvidia-container-cli configure 并执行。nvidia-container-clinvidia-container-cli 是一个命令行工具,用于配置Linux容器对GPU 硬件的使用。支持list: 打印 nvidia 驱动库及路径info: 打印所有Nvidia GPU设备configure: 进入给定进程的命名空间,执行必要操作保证容器内可以使用被指定的GPU以及对应能力(指定 Nvidia 驱动库)。 configure是我们使用到的主要命令,它将Nvidia 驱动库的so文件 和 GPU设备信息, 通过文件挂载的方式映射到容器中。代码如下: https://github.com/NVIDIA/libnvidia-container/blob/master/src/cli/configure.c#L272 / Mount the driver and visible devices. */ if (perm_set_capabilities(&err, CAP_EFFECTIVE, ecaps[NVC_MOUNT], ecaps_size(NVC_MOUNT)) < 0) { warnx(“permission error: %s”, err.msg); goto fail; } if (nvc_driver_mount(nvc, cnt, drv) < 0) { warnx(“mount error: %s”, nvc_error(nvc)); goto fail; } for (size_t i = 0; i < dev->ngpus; ++i) { if (gpus[i] != NULL && nvc_device_mount(nvc, cnt, gpus[i]) < 0) { warnx(“mount error: %s”, nvc_error(nvc)); goto fail; } }如果对其他模块感兴趣,可以在 https://github.com/NVIDIA/libnvidia-container 阅读代码。以上就是一个nvidia-docker的容器启动的所有步骤。当我们安装了nvidia-docker, 我们可以通过以下方式启动容器docker run –rm -it -e NVIDIA_VISIBLE_DEVICES=all ubuntu:18.04在容器中执行 mount 命令,可以看到名为 libnvidia-xxx.so 和 /proc/driver/nvidia/gpus/xxx 映射到容器中。 以及 nvidia-smi 和 nvidia-debugdump 等nvidia工具。# mount ## …./dev/vda1 on /usr/bin/nvidia-smi type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/bin/nvidia-debugdump type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/bin/nvidia-persistenced type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/bin/nvidia-cuda-mps-control type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/bin/nvidia-cuda-mps-server type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/lib/x86_64-linux-gnu/libnvidia-ml.so.396.37 type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/lib/x86_64-linux-gnu/libnvidia-cfg.so.396.37 type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/lib/x86_64-linux-gnu/libcuda.so.396.37 type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/lib/x86_64-linux-gnu/libnvidia-opencl.so.396.37 type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/lib/x86_64-linux-gnu/libnvidia-ptxjitcompiler.so.396.37 type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/lib/x86_64-linux-gnu/libnvidia-fatbinaryloader.so.396.37 type ext4 (ro,nosuid,nodev,relatime,data=ordered)/dev/vda1 on /usr/lib/x86_64-linux-gnu/libnvidia-compiler.so.396.37 type ext4 (ro,nosuid,nodev,relatime,data=ordered)devtmpfs on /dev/nvidiactl type devtmpfs (ro,nosuid,noexec,relatime,size=247574324k,nr_inodes=61893581,mode=755)devtmpfs on /dev/nvidia-uvm type devtmpfs (ro,nosuid,noexec,relatime,size=247574324k,nr_inodes=61893581,mode=755)devtmpfs on /dev/nvidia-uvm-tools type devtmpfs (ro,nosuid,noexec,relatime,size=247574324k,nr_inodes=61893581,mode=755)devtmpfs on /dev/nvidia4 type devtmpfs (ro,nosuid,noexec,relatime,size=247574324k,nr_inodes=61893581,mode=755)proc on /proc/driver/nvidia/gpus/0000:00:0e.0 type proc (ro,nosuid,nodev,noexec,relatime)我们可以执行nvidia-smi查看容器中被映射的GPU卡Kubernetes 如何调度GPU之前我们介绍了如何在容器中使用Nvidia GPU卡。 那么当一个集群中有成百上千个节点以及GPU卡,我们的问题变成了如何管理和调度这些GPU。Device pluginKubernetes 提供了Device Plugin 的机制,用于异构设备的管理场景。原理是会为每个特殊节点上启动一个针对某个设备的DevicePlugin pod, 这个pod需要启动grpc服务, 给kubelet提供一系列接口。type DevicePluginClient interface { // GetDevicePluginOptions returns options to be communicated with Device // Manager GetDevicePluginOptions(ctx context.Context, in *Empty, opts …grpc.CallOption) (*DevicePluginOptions, error) // ListAndWatch returns a stream of List of Devices // Whenever a Device state change or a Device disapears, ListAndWatch // returns the new list ListAndWatch(ctx context.Context, in *Empty, opts …grpc.CallOption) (DevicePlugin_ListAndWatchClient, error) // Allocate is called during container creation so that the Device // Plugin can run device specific operations and instruct Kubelet // of the steps to make the Device available in the container Allocate(ctx context.Context, in *AllocateRequest, opts …grpc.CallOption) (*AllocateResponse, error) // PreStartContainer is called, if indicated by Device Plugin during registeration phase, // before each container start. Device plugin can run device specific operations // such as reseting the device before making devices available to the container PreStartContainer(ctx context.Context, in *PreStartContainerRequest, opts …grpc.CallOption) (*PreStartContainerResponse, error)}DevicePlugin 注册一个 socket 文件到 /var/lib/kubelet/device-plugins/ 目录下,kubelet 通过这个目录下的socket文件向对应的 Device plugin 发送grpc请求。本文不过多介绍Device Plugin 的设计, 感兴趣可以阅读这篇文章: https://yq.aliyun.com/articles/498185Nvidia pluginGithub: https://github.com/NVIDIA/k8s-device-plugin为了能够在Kubernetes中管理和调度GPU, Nvidia提供了Nvidia GPU的Device Plugin。 主要功能如下支持ListAndWatch 接口,上报节点上的GPU数量支持Allocate接口, 支持分配GPU的行为。 Allocate 接口只做了一件事情,就是给容器加上 NVIDIA_VISIBLE_DEVICES 环境变量。 https://github.com/NVIDIA/k8s-device-plugin/blob/v1.11/server.go#L153// Allocate which return list of devices.func (m *NvidiaDevicePlugin) Allocate(ctx context.Context, reqs *pluginapi.AllocateRequest) (*pluginapi.AllocateResponse, error) { devs := m.devs responses := pluginapi.AllocateResponse{} for _, req := range reqs.ContainerRequests { response := pluginapi.ContainerAllocateResponse{ Envs: map[string]string{ “NVIDIA_VISIBLE_DEVICES”: strings.Join(req.DevicesIDs, “,”), }, } for _, id := range req.DevicesIDs { if !deviceExists(devs, id) { return nil, fmt.Errorf(“invalid allocation request: unknown device: %s”, id) } } responses.ContainerResponses = append(responses.ContainerResponses, &response) } return &responses, nil}前面我们提到, Nvidia的 gpu-container-runtime 根据容器的 NVIDIA_VISIBLE_DEVICES 环境变量,会决定这个容器是否为GPU容器,并且可以使用哪些GPU设备。 而Nvidia GPU device plugin做的事情,就是根据kubelet 请求中的GPU DeviceId, 转换为 NVIDIA_VISIBLE_DEVICES 环境变量返回给kubelet, kubelet收到返回内容后,会自动将返回的环境变量注入到容器中。当容器中包含环境变量,启动时 gpu-container-runtime 会根据 NVIDIA_VISIBLE_DEVICES 里声明的设备信息,将设备映射到容器中,并将对应的Nvidia Driver Lib 也映射到容器中。总体流程整个Kubernetes调度GPU的过程如下:GPU Device plugin 部署到GPU节点上,通过 ListAndWatch 接口,上报注册节点的GPU信息和对应的DeviceID。 当有声明 nvidia.com/gpu 的GPU Pod创建出现,调度器会综合考虑GPU设备的空闲情况,将Pod调度到有充足GPU设备的节点上。节点上的kubelet 启动Pod时,根据request中的声明调用各个Device plugin 的 allocate接口, 由于容器声明了GPU。 kubelet 根据之前 ListAndWatch 接口收到的Device信息,选取合适的设备,DeviceID 作为参数,调用GPU DevicePlugin的 Allocate 接口GPU DevicePlugin ,接收到调用,将DeviceID 转换为 NVIDIA_VISIBLE_DEVICES 环境变量,返回kubeletkubelet将环境变量注入到Pod, 启动容器容器启动时, gpu-container-runtime 调用 gpu-containers-runtime-hook gpu-containers-runtime-hook 根据容器的 NVIDIA_VISIBLE_DEVICES 环境变量,转换为 –devices 参数,调用 nvidia-container-cli prestart nvidia-container-cli 根据 –devices ,将GPU设备映射到容器中。 并且将宿主机的Nvidia Driver Lib 的so文件也映射到容器中。 此时容器可以通过这些so文件,调用宿主机的Nvidia Driver。本文作者:萧元阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

March 11, 2019 · 3 min · jiezi

Kubernetes taint & toleration

一、概述前一篇文章讲解了 Kubernetes 亲和性调度, 所涉及的内容都是描述 pod 的属性,来声明此 pod 希望调度到哪类 nodes。而本文介绍的 Taint(污点) 刚好相反,它是node 的一个属性,允许 node 主动排斥 pod 的调度。对应的 k8s 又给 pod 新增了配套属性 toleration(容忍) ,用于表示这些 pod 可以(但不强制要求)被调度到具有相应 taints 的 nodes 上。这两者经常一起搭配,来确保不将 pod 调度到不合适的 nodes。看下 taint & toleration 结构体,下面足点介绍时会涉及相关字段。type Taint struct { Key string Value string Effect TaintEffect // add taint 的时间点 // 只有 Effect = NoExecute, 该值才会被 nodeController 写入 TimeAdded *metav1.Time}type Toleration struct { Key string Operator TolerationOperator Value string Effect TaintEffect // 容忍时间 TolerationSeconds *int64}type TaintEffect stringconst ( TaintEffectNoSchedule TaintEffect = “NoSchedule” TaintEffectPreferNoSchedule TaintEffect = “PreferNoSchedule” TaintEffectNoExecute TaintEffect = “NoExecute”)type TolerationOperator stringconst ( TolerationOpExists TolerationOperator = “Exists” TolerationOpEqual TolerationOperator = “Equal”)二、Taint我们可以对 node 设置多个 taints,当然也可以在 pod 配置相同个数的 tolerations。影响调度和运行的具体行为,我们可以分为以下几类:如果至少有一个 effect == NoSchedule 的 taint 没有被 pod toleration,那么 pod 不会被调度到该节点上。如果所有 effect == NoSchedule 的 taints 都被 pod toleration,但是至少有一个 effect == PreferNoSchedule 没有被 pod toleration,那么 k8s 将努力尝试不把 pod 调度到该节点上。如果至少有一个 effect == NoExecute 的 taint 没有被 pod toleration,那么不仅这个 pod 不会被调度到该节点,甚至这个节点上已经运行但是也没有设置容忍该污点的 pods,都将被驱逐。三、Toleration再看下 PodSpec 配置 Tolerations,其中的 key、value、effect 与 Node Taint 设置需要保持一致,operator 支持两类:Exists: 这个配置下,不需要指定 value。Equal: 需要配置 value 值。(operator 的默认值)有几个特殊情况:key 为空并且 operator 等于 Exists,表示匹配了所有的 keys,values 和 effects。换句话说就是容忍了所有的 taints。tolerations:- operator: “Exists"effect 为空,则表示匹配所有的 effects(NoSchedule、PreferNoSchedule、NoExecute)tolerations:- key: “key” operator: “Exists"还有一个 TolerationSeconds,该值与 effect 为 NoExecute 配套使用。用来指定在 node 添加了 effect = NoExecute 的 taint 后,能容忍该 taint 的 pods 可停留在 node 上的时间。例如:tolerations: - key: “key1” operator: “Equal” value: “value1” effect: “NoExecute” tolerationSeconds: 3600表示如果这个 pod 已经运行在 node 上并且该 node 添加了一个对应的 taint,那么这个 pod 将会在 node 上停留 3600 秒后才会被驱逐。但是如果 taint 在这个时间前被移除,那么这个 pod 也就不会被驱逐了。来个比较形象的图,描述下:该图参考: Taints and tolerations, pod and node affinities demystified · Banzai Cloud四、内置行为kubernetes 1.6 版本,node controller 会跟进系统情况自动设置 node taint 属性。内置 taint key 如下:node.kubernetes.io/not-ready: 节点尚未就绪node.kubernetes.io/unreachable: 节点无法被访问node.kubernetes.io/unschedulable: 节点不可调度node.kubernetes.io/out-of-disk: 节点磁盘不足node.kubernetes.io/memory-pressure: 节点有内存压力node.kubernetes.io/disk-pressure: 节点有磁盘压力node.kubernetes.io/network-unavailable: 节点网络不可用node.kubernetes.io/pid-pressure: 节点有 pid 压力node.cloudprovider.kubernetes.io/uninitialized: 云节点未初始化node.cloudprovider.kubernetes.io/shutdown: 云节点已下线kubernetes 会通过 DefaultTolerationSeconds admission controller 为创建的 pod 添加两个默认的 toleration: node.kubernetes.io/not-ready 和 node.kubernetes.io/unreachable,并且设置 tolerationSeconds = 300。当然这个默认配置,用户可以自行覆盖。这些自动添加的默认配置,确保 node 出现问题后,pod 可以继续在 node 上停留 5 分钟。DefaultTolerationSeconds admission controller 设置的 tolerationSeconds 值,也可以由用户指定。具体参考: https://github.com/kubernetes…还有一个默认行为,就是 DaemonSet。所有 DaemonSet 创建的 pod 都会添加两个 toleration: node.alpha.kubernetes.io/unreachable 和 node.alpha.kubernetes.io/notReady。设置 effect = NoExecute,并且不指定 tolerationSeconds。目的是确保在 node 出现 unreachable 或 notReady 的问题时,DaemonSet Pods 永远不会被驱逐。示例专用节点如果你希望将一组节点专用于特定的用户,那可以将这些节点设置 taints: kubectl taint nodes nodename dedicated=groupName:NoSchedule, 然后为 pods 设置对应的 tolerations。如果你希望节点被专用并且确保服务仅使用这批节点,那么你还应该向这批节点设置 labels (dedicated=groupName),并且为对应的 pods 设置 NodeAffinity,以控制 pods 只能跑到这批节点上。具有特殊硬件的节点有部分带有特殊硬件的节点,比如 GPU、FPGA 等,要确保不将不需要专用硬件的 pods 调度到这些节点。也可以和 专有节点 一样的方式设置 taints:kubectl taint nodes nodename special=true:NoSchedule 或 kubectl taint nodes nodename special=true:PreferNoSchedule。建议还可以通过 Extended Resources 和 ExtendedResourceToleration admission controller 来更方便的调度依赖特殊硬件的 pods,而不需要手动添加容器的 toleration基于 taint 的驱逐前面也提到了,可以通过 NoExecute taint 来驱逐节点上已经运行的 pods。五、参考资料community/taint-toleration-dedicated.md at master · kubernetes/community · GitHubcommunity/taint-node-by-condition.md at master · kubernetes/community · GitHubTaints and TolerationsTaints and tolerations, pod and node affinities demystified · Banzai Cloud ...

March 9, 2019 · 2 min · jiezi

Kubernetes 调度器浅析

一、概述Kubernetes 是 Google 开源的容器集群管理系统(谷歌内部:Borg),而今天要介绍的 kube-scheduler 是 k8s 系统的核心组件之一,其主要职责就是通过自身的调度算法,为新创建的 Pod 寻找一个最合适的 Node。主要包含如下几个步骤:通过一组叫做谓词 predicates 的过滤算法,先挑出满足条件的 Node;通过一组叫做优先级 priorities 的打分算法,来给上一步符合条件的每个 Node 进行打分排名;最终选择得分最高的节点,当然如果得分一样就随机一个节点,填回 Pod 的 spec.nodeName 字段。官方流程图如下:For given pod: +———————————————+ | Schedulable nodes: | | | | +——–+ +——–+ +——–+ | | | node 1 | | node 2 | | node 3 | | | +——–+ +——–+ +——–+ | | | +——————-+————————-+ | | v +——————-+————————-+ Pred. filters: node 3 doesn’t have enough resource +——————-+————————-+ | | v +——————-+————————-+ | remaining nodes: | | +——–+ +——–+ | | | node 1 | | node 2 | | | +——–+ +——–+ | | | +——————-+————————-+ | | v +——————-+————————-+ Priority function: node 1: p=2 node 2: p=5 +——————-+————————-+ | | v select max{node priority} = node 2scheduler 的工作看似很简单,但其实不然。考虑的问题非常多,比如要保证每个节点被公平调度,提高资源利用率,提高 pod 调度效率,提升调度器扩展能力等等。可涉及的内容非常多,接下来会围绕两个核心步骤对 k8s 的 默认调度策略 深入了解。参考 Kubernetes 版本: v1.12二、PredicatesPredicates 在调度过程中的作用就是先进行过滤,过滤掉所有不符合条件的节点后,剩下的所有节点就都是可以运行带调度 Pod。Predicates 的可以分为如下四类:GeneralPredicates:负责最基础的调度策略,比如 PodFitsResources 计算宿主机资源是否够用。与 Volume 相关的过滤规则:负责与容器持久化 Volume 相关的调度策略。与宿主机相关的过滤规则:负责考察待调度 Pod 是否满足 Node 本身的一些条件。与已运行 Pod 相关的过滤规则:负责检查待调度 Pod 与 Node 上已有 Pod 之间的亲和性关系。具体的 Predicates 默认策略,可以参考: 默认调度策略当开始调度一个 Pod 的时候,调度器会同时开启多个协程并发的进行 Node Predicates 过滤,最后返回一个可以运行 Pod 的节点列表。每个协程都是按照固定的顺序进行计算过滤的。接下来,我们看下四大类具体运行的调度策略内容。1. GeneralPredicates看字面意思就知道 GeneralPredicates 负责的是最基础的调度策略,其包含的具体策略如下:PodFitsResources: 计算宿主机的 CPU、内存、扩展资源(如 GPU)等是否够用。PodFitsHost: 检查宿主机的名字是否跟 Pod 的 spec.nodeName 匹配。PodFitsHostPorts: 检查 Pod 申请的宿主机端口有没有冲突。PodMatchNodeSelector: 检查节点是否能匹配 Pod 的 nodeSelector 和 nodeAffinity。因为 GeneralPredicates 是最基础的调度策略,所以该接口也会被别的组件直接调用,比如 kubelet、daemonSet controller。kubelet 在启动 pod 之前,还会再执行一遍 GeneralPredicates,用于二次确认。2. 与 Volume 相关的过滤规则不废话就直接列举具体的策略了:NoDiskConflict:检查该节点上所有的 Pods 是否与待调度的 Pod 的 Volume 有冲突,比如 AWS、GCE 的 Volume 是不允许被两个 Pod 同时使用的。VolumeZonePredicate:检查 Pod Volume 的 zone 标签是否与节点的 zone 标签匹配。如果 Node 没有 zone 标签则认定为匹配。MaxPDVolumeCountPredicate:检查节点上某种类型的 Volume 是否已经超过指定数目。CSIMaxVolumeLimitPredicate:检查 csi volume 相关的限制VolumeBindingPredicate:检查 Pod 对应的 Local PV 的 nodeAffinity 字段,是否跟某个节点的标签相匹配。如果该 Pod PVC 还没有绑定 PV 的话,则调度器还要负责检查所有待绑定的 PV,且该 PV 的 nodeAffinity 是否与节点标签匹配。3. 与宿主机相关的过滤规则这些规则主要考察待调度的 Pod 是否满足 Node 本身的一些条件。具体的策略如下:NodeConditionPredicate:检查 Node 是否还未准备好或者处于NodeOutOfDisk、NodeNetworkUnavailable 状态,又或者 Node spec.Unschedulable 设置为 true,那该节点都将无法被调度。PodToleratesNodeTaints:检查 Node 的 taint(污点)机制。只有当 Pod 的 Toleration 与 Node 的 Taint 匹配时,Pod 才能调度到该节点上。NodeMemoryPressurePredicate:检查当前节点的内存是否已经不够使用。NodeDiskPressurePredicate:检查当前节点的磁盘是否已经不够使用。NodePIDPressurePredicate:检查当前节点的 PID 是否已经不够使用。4. 与已运行 Pod 相关的过滤规则该规则主要就是 PodAffinityPredicate,用于检查待调度 Pod 与 Node 上已有的 Pod 之间的亲和性和反亲和性关系。具体的亲和性相关的调度,后面会单独拿一篇文章进行介绍。三、Priorities完成了前一个阶段的节点 “过滤” 之后,便需要通过 Priorities 为这些节点打分,选择得分最高的节点,作为调度对象。打分函数很多,总得分可以参考:总分 = (权重1 * 打分函数1) + (权重2 * 打分函数2) + … + (权重n * 打分函数n)。每一次打分的范围是 0 — 10 分。10 表示非常合适,0 表示非常不合适。并且每个打分函数都可以配置对应的权重值,下面介绍 调度器策略配置 时,也会涉及权重值的配置。默认权重值是 1,如果觉得某个打分函数特别重要,便可以加大该权重值。具体的 Priorities 默认策略可以参考: defaultPriorities 。Priorities 最常用到的一个打分规则是 LeastRequestedPriority, 该算法用于选出空闲资源(cpu & memory)最多的宿主机。还有一个常见的是 BalancedResourceAllocation,该规则主要目的是资源平衡。在所有节点里选择各种资源分配最均衡的节点,避免出现某些节点 CPU 被大量分配,但是 Memory 大量剩余的情况。此外,还有 InterPodAffinityPriority、NodeAffinityPriority、TaintTolerationPriority,与亲和性与污点调度有关,后面会有单独的文章进行介绍。这里表示节点满足的规则越多,那得分就越高。在 K8S v1.12 版本还引入了一个调度策略,即 ImageLocalityPriority。该策略主要目的是优先选择那些已经存有 Pod 所需 image 的节点,可以避免实际运行 Pod 时,再去下载 image。注意: pod 运行时是否会下载 image,还跟 Pod ImagePullPolicy 配置有关。可以看到 k8s scheduler 完成一次调度所需的信息非常之多。所以在实际的调度过程中,大量的信息都事先已经缓存,提高了 Pod 的调度效率。四、调度策略配置Kubernetes 调度器有默认的调度策略,具体可以参考 default 。当然用户也可以修改调度策略,可以通过命令行参数 policy-config-file 指定一个 JSON 文件来描述哪些 predicates 和 priorities 在启动 k8s 时被使用, 通过这个参数调度就能使用管理者定义的策略了。示例如下:{“kind” : “Policy”,“apiVersion” : “v1”,“predicates” : [ {“name” : “PodFitsHostPorts”}, {“name” : “PodFitsResources”}, {“name” : “NoDiskConflict”}, {“name” : “NoVolumeZoneConflict”}, {“name” : “MatchNodeSelector”}, {“name” : “HostName”} ],“priorities” : [ {“name” : “LeastRequestedPriority”, “weight” : 1}, {“name” : “BalancedResourceAllocation”, “weight” : 1}, {“name” : “ServiceSpreadingPriority”, “weight” : 1}, {“name” : “EqualPriority”, “weight” : 1} ],“hardPodAffinitySymmetricWeight” : 10,“alwaysCheckAllPredicates” : false}五、自定义调度器前面提到了调度器的扩展能力,除了使用 k8s 自带的调度器,你也可以编写自己的调度器。通过修改 Pod 的 spec.schedulername 参数来指定调度器的名字。参考资料The Kubernetes SchedulerScheduler Algorithm in Kubernetes ...

March 9, 2019 · 2 min · jiezi

Kubernetes 亲和性调度

一、概述前一篇文章 Kubernetes 调度器浅析,大致讲述了调度器的工作原理及相关调度策略。这一章会继续深入调度器,介绍下“亲和性调度”。Kubernetes 支持限制 Pod 在指定的 Node 上运行,或者指定更倾向于在某些特定 Node 上运行。有几种方式可以实现这个功能:NodeName: 最简单的节点选择方式,直接指定节点,跳过调度器。NodeSelector: 早期的简单控制方式,直接通过键—值对将 Pod 调度到具有特定 label 的 Node 上。NodeAffinity: NodeSelector 的升级版,支持更丰富的配置规则,使用更灵活。(NodeSelector 将被淘汰.)PodAffinity: 根据已在节点上运行的 Pod 标签来约束 Pod 可以调度到哪些节点,而不是根据 node label。二、NodeNamenodeName 是 PodSpec 的一个字段,用于直接指定调度节点,并运行该 pod。调度器在工作时,实际选择的是 nodeName 为空的 pod 并进行调度然后再回填该 nodeName,所以直接指定 nodeName 实际是直接跳过了调度器。换句话说,指定 nodeName 的方式是优于其他节点选择方法。方法很简单,直接来个官方示例:apiVersion: v1kind: Podmetadata: name: nginxspec: containers: - name: nginx image: nginx nodeName: kube-01当然如果选择的节点不存在,或者资源不足,那该 pod 必然就会运行失败。三、NodeSelectornodeSelector 也是 PodSpec 中的一个字段,指定键—值对的映射。如果想要将 pod 运行到对应的 node 上,需要先给这些 node 打上 label,然后在 podSpec.NodeSelector 指定对应 node labels 即可。步骤如下:设置标签到 node 上:kubectl label nodes kubernetes-node type=gpupod 配置添加 nodeSelector 字段:apiVersion: v1kind: Podmetadata: name: nginxspec: containers: - name: nginx image: nginx nodeSelector: type: gpu内置 Node 标签Kubernetes 内置了一些节点标签:kubernetes.io/hostnamebeta.kubernetes.io/instance-typebeta.kubernetes.io/osbeta.kubernetes.io/archfailure-domain.beta.kubernetes.io/zonefailure-domain.beta.kubernetes.io/region有些标签是对云提供商使用。还有些表示 node role 的 labels(可以指定 master、lb 等):kubernetes.io/rolenode-role.kubernetes.io四、NodeAffinitynodeSelector 通过 k-v 的方式非常简单的支持了 pod 调度限制到具有特定标签的节点上。而 nodeAffinity 根据亲和力 & 反亲和力极大地扩展了能够表达的约束信息。nodeAffinity 特性的设计初衷就是为了替代 nodeSelector。nodeAffinity 当前支持的匹配符号包括:In、NotIn、Exists、DoesNotExists、Gt、Lt 。nodeAffinity 当前支持两种调度模式:requiredDuringSchedulingIgnoredDuringExecution: 一定要满足的条件,如果没有找到满足条件的节点,则 Pod 创建失败。所有也称为hard 模式。preferredDuringSchedulingIgnoredDuringExecution: 优先选择满足条件的节点,如果没有找到满足条件的节点,则在其他节点中择优创建 Pod。所有也称为 soft 模式。两种模式的名字特长,这是 k8s 的命名风格。其中IgnoredDuringExecution的意义就跟 nodeSelector 的实现一样,即使 node label 发生变更,也不会影响之前已经部署且又不满足 affinity rules 的 pods,这些 pods 还会继续在该 node 上运行。换句话说,亲和性选择节点仅在调度 Pod 时起作用。k8s 社区正在计划提供 requiredDuringSchedulingRequiredDuringExecution 模式,便于驱逐 node 上不满足 affinity rules 的 pods。来个官方示例,看下怎么玩:apiVersion: v1kind: Podmetadata: name: with-node-affinityspec: affinity: nodeAffinity: # 必须选择 node label key 为 kubernetes.io/e2e-az-name, # value 为 e2e-az1 或 e2e-az2. requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/e2e-az-name operator: In values: - e2e-az1 - e2e-az2 # 过滤掉上面的必选项后,再优先选择 node label key 为 another-node-label-key # value 为 another-node-label-value. preferredDuringSchedulingIgnoredDuringExecution: # 如果满足节点亲和,积分加权重(优选算法,会对 nodes 打分) # weight: 0 - 100 - weight: 1 preference: matchExpressions: - key: another-node-label-key operator: In values: - another-node-label-value containers: - name: with-node-affinity image: k8s.gcr.io/pause:2.0简单看下 NodeAffinity 的结构体,下面介绍注意事项时会涉及:type NodeAffinity struct { RequiredDuringSchedulingIgnoredDuringExecution *NodeSelector PreferredDuringSchedulingIgnoredDuringExecution []PreferredSchedulingTerm}type NodeSelector struct { NodeSelectorTerms []NodeSelectorTerm}type NodeSelectorTerm struct { MatchExpressions []NodeSelectorRequirement MatchFields []NodeSelectorRequirement}配置相关的注意点:如果 nodeSelector 和 nodeAffinity 两者都指定,那 node 需要两个条件都满足,pod 才能调度。如果指定了多个 NodeSelectorTerms,那 node 只要满足其中一个条件,pod 就可以进行调度。如果指定了多个 MatchExpressions,那必须要满足所有条件,才能将 pod 调度到该 node。五、PodAffinitynodeSelector & nodeAffinity 都是基于 node label 进行调度。而有时候我们希望调度的时候能考虑 pod 之间的关系,而不只是 pod 和 node 的关系。举个例子,会有需求希望服务 A 和 B 部署在同一个机房、机架或机器上,因为这些服务可能会对网路延迟比较敏感,需要低延时;再比如,希望服务 C 和 D 又希望尽量分开部署,即使一台主机甚至一个机房出了问题,也不会导致两个服务一起挂而影响服务可用性,提升故障容灾的能力。podAffinity 会基于节点上已经运行的 pod label 来约束新 pod 的调度。其规则就是“如果 X 已经运行了一个或者多个符合规则 Y 的 Pod,那么这个 Pod 应该(如果是反亲和性,则是不应该)调度到 X 上”。这里的 Y 是关联 namespace 的 labelSelector,当然 namespace 也可以是 all。和 node 不同,pod 是隶属于 namespace 下的资源,所以基于 pod labelSelector 必须指定具体的 namespace;而 X 则可以理解为一个拓扑域,类似于 node、rack、zone、cloud region 等等,就是前面提到的 内置 Node 标签 ,当然也可以自定义。看下 pod affinity 涉及的结构体,便于进行功能介绍:type Affinity struct { // NodeAffinity 前面介绍了 NodeAffinity *NodeAffinity // pod 亲和性 PodAffinity *PodAffinity // pod 反亲和性 PodAntiAffinity *PodAntiAffinity}type PodAffinity struct { // hard 模式, 必选项 RequiredDuringSchedulingIgnoredDuringExecution []PodAffinityTerm // soft 模式, 进行 node 优先 PreferredDuringSchedulingIgnoredDuringExecution []WeightedPodAffinityTerm}type PodAffinityTerm struct { LabelSelector *metav1.LabelSelector Namespaces []string TopologyKey string}type WeightedPodAffinityTerm struct { Weight int32 PodAffinityTerm PodAffinityTerm}podAffinity 和 nodeAffinity 有相似的地方,使用了 labelSelector 进行匹配,支持的匹配符号包括:In、NotIn、Exists、DoesNotExists;也支持两种调度模式 requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution, 功能和 nodeAffinity 一样,这里就不在累述。podAffinity 和 nodeAffinity 也有较大的差异,前面讲了 pod 是 namespace 资源,所以必然会需要配置 namespaces,支持配置多个 namespace。如果省略的话,默认为待调度 pod 所属的 namespace;如果定义了但是值为空,则表示使用 “all” namespaces。还有一个较大的差别 TopologyKey, 便于理解进行单独介绍。TopologyKeyTopologyKey 用于定义 in the same place,前面也介绍了是拓扑域的概念。看下面的图,这两个 pod 到底该如何算在一个拓扑域?如果我们使用k8s.io/hostname,in the same place 则意味着在同一个 node,那下图的 pods 就不在一个 place:如果我们使用failure-domain.k8s.io/zone 来表示一个 place,那下图的 pods 就表示在一个 zone:当然我们也可以自定义 node labels 作为 TopologyKey。比如我们可以给一组 node 打上 rack 标签,那下图的 pods 表示在同一个 place:原则上,topologyKey 可以是任何合法的 label key。但是出于性能和安全考虑,topologyKey 存在一些限制:对于亲和性和反亲和性的 requiredDuringSchedulingIgnoredDuringExecution 模式,topologyKey 不能为空pod 反亲和性 requiredDuringSchedulingIgnoredDuringExecution 模式下,LimitPodHardAntiAffinityTopology 权限控制器会限制 topologyKey 只能设置为 kubernetes.io/hostname。当然如果你想要使用自定义 topology,那可以简单禁用即可。pod 反亲和性 preferredDuringSchedulingIgnoredDuringExecution 模式下,topologyKey 为空则表示所有的拓扑域。截止 v1.12 版本,所有的拓扑域还只能是 kubernetes.io/hostname, failure-domain.beta.kubernetes.io/zone 和 failure-domain.beta.kubernetes.io/region 的组合。除此之外,topologyKey 可以是任何合法的 label key。示例来个官方示例,有三节点集群,需要分别部署 3 份 web 和 redis 服务。希望 web 与 redis 服务共存,但需要保证各个服务的副本分散部署。先创建 redis 集群:apiVersion: apps/v1kind: Deploymentmetadata: name: redis-cachespec: selector: matchLabels: app: store replicas: 3 template: metadata: labels: app: store spec: affinity: // pod 反亲和性, 打散 redis 各个副本 podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - store topologyKey: “kubernetes.io/hostname” containers: - name: redis-server image: redis:3.2-alpine再部署 web 服务,需要打散并且与 redis 服务共存,配置如下:apiVersion: apps/v1kind: Deploymentmetadata: name: web-serverspec: selector: matchLabels: app: web-store replicas: 3 template: metadata: labels: app: web-store spec: affinity: podAntiAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - web-store topologyKey: “kubernetes.io/hostname” podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: app operator: In values: - store topologyKey: “kubernetes.io/hostname” containers: - name: web-app image: nginx:1.12-alpine注意1: pod affinity 需要进行大量处理,所以会明显减慢大型集群的调度时间,不建议在大于几百个节点的集群中使用该功能。注意2: pod antiAffinity 要求对节点进行一致标志,即集群中的所有节点都必须具有适当的标签用于配置给 topologyKey,如果节点缺少指定的 topologyKey 指定的标签,则可能会导致意外行为。六、参考资料官方Assigning Pods to Nodes - Kubernetesnode affinitypod affinity性能优化: Improve performance of affinity/anti-affinity predicate by 20x in large clusters by bsalamatblogK8S 的调度 (一) 抽象优雅的 AffinityScheduling in Kubernetes, Part 2: Pod Affinity – Koki – Medium ...

March 9, 2019 · 3 min · jiezi

Kubernetes v1.13对原始块卷支持转移到beta

作者:Ben Swartzlander(NetApp),Saad Ali(谷歌)Kubernetes v1.13将原始块卷(raw block volume)支持转移到beta。此功能允许持久卷(persistent volume)作为块设备(block device),而不是作为已安装的文件系统在容器内部公开。什么是块设备?块设备允许随机访问固定大小的块中的数据。硬盘驱动器、SSD和CD-ROM驱动器都是块设备的示例。持久存储通常以分层方式实现,在块设备(如旋转磁盘或SSD)之上使用文件系统(如ext4)。然后,应用程序读取和写入文件,而不是在块上操作。操作系统负责使用指定的文件系统,将文件作为块读取和写入底层设备。值得注意的是,整个磁盘都是块设备,磁盘分区也是,存储区域网络(SAN)设备的LUN也是。为什么要将原始块卷添加到kubernetes?有些专门的应用程序需要直接访问块设备,例如,文件系统层会引入不必要的开销。最常见的情况是数据库,它们更喜欢直接在底层存储上组织数据。原始块设备也常用于任何本身实现某种存储服务的软件(软件定义的存储系统)。从程序员的角度来看,块设备是一个非常大的字节数组,通常具有一些最小的读写粒度,通常为512字节,但更常见为4K或更大。随着在Kubernetes内部运行数据库软件和存储基础架构软件变得越来越普遍,Kubernetes中对原始块设备支持的需求变得更加重要。哪个卷插件支持原始块?在发布此博客时,以下树内(in-tree)卷类型支持原始块:AWS EBSAzure DiskCinderFibre ChannelGCE PDiSCSILocal volumesRBD (Ceph)Vsphere树外(Out-of-tree)CSI卷驱动程序也可以支持原始块卷。 Kubernetes CSI对原始块卷的支持目前是alpha。请参阅此处的文档。Kubernetes原始块卷API原始块与普通卷有很多共同点。两者都是通过创建绑定到PersistentVolume对象的PersistentVolumeClaim对象来请求的,并通过将它们包含在PodSpec的volumes数组中而附加到Kubernetes中的Pod。但是有两个重要的区别。首先,要请求原始块PersistentVolumeClaim,必须在PersistentVolumeClaimSpec中设置volumeMode =“Block”。将volumeMode留空与指定volumeMode =“Filesystem”相同,这会导致传统行为。PersistentVolumes在其PersistentVolumeSpec中也有一个volumeMode字段,而“Block”类型的PVC只能绑定到“Block”类型的PV,而“Filesystem”PVC只能绑定到“Filesystem”PV。其次,在Pods中使用原始块卷时,必须在PodSpec的Container部分而不是VolumeMount中指定VolumeDevice。VolumeDevices具有devicePaths而不是mountPaths,并且在容器内部,应用程序将在该路径中看到设备而不是已安装的文件系统。应用程序打开、读取和写入容器内的设备节点,就像它们将与非容器化或虚拟化环境中的系统上的任何块设备进行交互一样。创建新的原始块PVC首先,确保你选择的存储类关联的配置程序是支持原始块的配置程序。然后创建PVC。apiVersion: v1kind: PersistentVolumeClaimmetadata: name: my-pvcspec: accessModes: - ReadWriteMany volumeMode: Block storageClassName: my-sc resources: requests: storage: 1Gi使用原始块PVC在pod定义中使用PVC时,可以选择块设备的设备路径,而不是文件系统的安装路径。apiVersion: v1kind: Podmetadata: name: my-podspec: containers: - name: my-container image: busybox command: - sleep - “3600” volumeDevices: - devicePath: /dev/block name: my-volume imagePullPolicy: IfNotPresent volumes: - name: my-volume persistentVolumeClaim: claimName: my-pvc作为存储供应商,如何在我的CSI插件中添加对原始块设备的支持?CSI插件对原始块支持仍然是alpha,但今天可以添加支持。CSI规范详细说明了如何处理具有BlockVolume功能而不是MountVolume功能的卷请求。CSI插件可以支持这两种卷。有关更多详细信息,请参阅此处。问题/陷阱因为块设备实际上是设备,所以可以从容器内部对它们执行低级操作,这是文件系统卷无法实现的。例如,实际上是SCSI磁盘的块设备支持使用Linux ioctls向设备发送SCSI命令。默认情况下,Linux不允许容器将SCSI命令从容器内部发送到磁盘。为此,你必须将SYS_RAWIO功能授予容器安全上下文(context)以允许此操作。请参阅此处的文档。此外,虽然Kubernetes保证向容器提供块设备,但不能保证它实际上是SCSI磁盘或任何其他类型的磁盘。用户必须确保所需的磁盘类型与其pod一起使用,或者仅部署可处理各种块设备类型的应用程序。怎样能了解更多?在此处查看有关快照功能的其他文档。我如何参与?加入Kubernetes存储SIG和CSI社区,帮助我们添加更多优秀功能,并改进现有功能如原始块存储!鸣谢特别感谢帮助Kubernetes增加块卷支持的所有贡献者,包括:Ben Swartzlander(https://github.com/bswartz)Brad Childs(https://github.com/childsb)Erin Boyd(https://github.com/erinboyd)Masaki Kimura(https://github.com/mkimuram)Matthew Wong(https://github.com/wongma7)Michelle Au(https://github.com/msau42)Mitsuhiro Tanino(https://github.com/mtanino)Saad Ali(https://github.com/saad-ali)KubeCon + CloudNativeCon和Open Source Summit大会日期:会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日KubeCon + CloudNativeCon和Open Source Summit赞助方案KubeCon + CloudNativeCon和Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国KubeCon + CloudNativeCon和Open Source Summit购票窗口,立即购票! ...

March 8, 2019 · 1 min · jiezi

2019 年,容器技术生态会发生些什么?

Kubernetes 项目被采纳度将持续增长作为“云原生”(Cloud Native)理念落地的核心,Kubernetes 项目已经成为了构建容器化平台体系的默认选择。但是,不同于一个只能生产资源的集群管理工具,Kubernetes 项目最大的价值,乃在于它从一开始就提倡的声明式 API 和以此为基础“控制器”模式。在这个体系的指导下, Kubernetes 项目保证了在自身突飞猛进的的发展过程中 API 层的相对稳定的和一定的向后兼容能力,这是作为一个平台级项目被用户广泛接受和认可的重要前提。更重要的是,Kubernetes 项目为使用者提供了宝贵的 API 可扩展能力和良好的 API 编程范式,催生出了一个完全基于Kubernetes API 构建出来的上层应用服务生态。可以说,正是这个生态的逐步完善与日趋成熟,才确立了 Kubernetes 项目如今在云平台领域牢不可破的领导地位,也间接宣告了其它竞品方案的边缘化。与此同时,上述事实标准的确立,也使得“正确和合理的使用了 Kubernetes 的能力”,在某种意义上成为了评判上层应用服务框架(比如 PaaS 和 Serverless )的一个重要依据:这不仅包括了对框架本身复杂性和易用性的考量,也包括了对框架可扩展性和演进趋势的预期与判断。不过,相比于国外公有云上以 Kubernetes 为基础的容器化作业的高占比,国内公有云市场对容器的采纳程度目前仍然处于比较初步的水平,直接贩卖虚拟机及其关联 IaaS 层能力依然是国内绝大多数公有云提供商的主要业务形态。所以,不同于国外市场容器技术增长逐步趋于稳定、Kubernetes 公有云服务已经开始支撑头部互联网客户的情况,Kubernetes 以及容器技术在国内云计算市场里的依然具有巨大的增长空间和强劲的发展势头。不难预测,Kubernetes 项目在国内公有云上的逐渐铺开,会逐渐成为接下来几年国内公有云市场上的一个重要趋势。而无论是国内外,大量 Kubernetes 项目相关岗位的涌现,正是验证这个趋势与变化的一个最直接的征兆。2. “Serverless 化”与“多样性”将成为上层应用服务生态的两大关键词当云上的平台层被 Kubernetes 项目逐步统一之后,过去长期纠结在应用编排、调度与资源管理上裹足不前的 PaaS 项目得到了生产力的全面释放,进而在云平台层之上催生出了一个日趋多样化的应用服务生态。事实上,这个生态的本质与2014年之前的 PaaS 生态没有太大不同。只不过,当原本 PaaS 项目的平台层功能(编排、调度、资源管理等)被剥离了出来之后,PaaS 终于可以专注于应用服务和发布流程管理这两个最核心的功能上,开始向更轻、更薄、更以应用为中心的方向进行演进。而在这个过程中, Serverless 自然开始成为了主流话题。这里需要指出的是,Serverless 从2014年 AWS 发布 Lambda时专门用来指代函数计算(或者说 FaaS)发展到今天,已经被扩展成了包括大多数 PaaS 功能在内的一个泛指术语,即:Serverless = FaaS + BaaS。而究其本质,“高可扩展性”、“工作流驱动”和“按使用计费”,可以认为是 Serverless 最主要的三个特征。这也是为什么我们会说今天大家所谈论的 Serverless,其实是经典 PaaS 演进到今天的一种“极端”形态。伴随着 Serverless 概念本身的“横向发展”,我们不难预料到,2019年之后云端的应用服务生态,一定会趋于多样化,进而覆盖到更多场景下的应用服务管理需求。并且,无论是Function,传统应用,容器,存储服务,网络服务,都会开始尝试以不同的方式和形态嵌入到“高可扩展性”、“工作流驱动”和“按使用计费”这三个特征当中。当然,这种变化趋势的原因也不言而喻:Serverless 三个特征背后所体现的,**乃是云端应用开发过程向“用户友好”和“低心智负担”方向演进的最直接途径。而这种“简单、经济、可信赖”的朴实诉求,正是云计算诞生的最初期许和永恒的发展方向。**而在这种上层应用服务能力向 Serverless 迁移的演进过程中,不断被优化的 Auto-scaling 能力和细粒度的资源隔离技术,将会成为确保 Serverless 能为用户带来价值的最有力保障。3. 看得见、摸得着、能落地的“云原生”自从 CNCF 社区迅速崛起以来,“云原生”三个字就成了各大云厂商竞相角逐的一个关键词。不过,相比于 Kubernetes 项目和容器技术实实在在的发展和落地过程,云原生(Cloud Native)的概念却长期以来“曲高和寡”,让人很难说出个所以然来。其实,“云原生”的本质,不是简单对 Kubernetes 生态体系的一个指代。“云原生” 刻画出的,是一个使用户能低心智负担的、敏捷的,以可扩展、可复制的方式,最大化利用”云“的能力、发挥”云“的价值的一条最佳路径。而这其中,”不可变基础设施“是“云原生”的实践基础(这也是容器技术的核心价值);而 Kubernetes、Prometheus、Envoy 等 CNCF 核心项目,则可以认为是这个路径落地的最佳实践。这套理论体系的发展过程,与 CNCF 基金会创立的初衷和云原生生态的发展历程是完全一致的。也正是伴随着这样的发展过程,云原生对于它的使用者的意义,在2019年之后已经变得非常清晰:是否采用云原生技术体系,实际上已经成为了一个关系到是不是要最大化”云“的价值、是不是要在”云“上赢取最广泛用户群体的一个关键取舍。这涉及到的,关系到整个组织的发展、招聘、产品形态等一系列核心问题,而绝非一个单纯的技术决定。明白了这一层道理,在2019年,我们已经不难看到,国内最顶尖的技术公司们,都已经开始在云原生技术框架下发起了实实在在的技术体系升级与落地的“战役”。显然,大家都已经注意到,相比于纠结于“云原生到底是什么”这样意识形态话题,抓紧时间和机遇将 Kubernetes 及其周边核心技术生态在组织中生长起来,并借此机会完成自身基础技术体系的转型与升级,才是这些体量庞大的技术巨人赶上这次云计算浪潮的不二法宝。在这个背景下,所谓“云原生”体系在这些公司的落地,只是这个激动人心的技术革命背后的一个附加值而已。而在”云原生”这个关键词的含义不断清晰的过程中,我们一定要再次强调:”云原生不等于 CNCF,更不等于 Kubernetes“。云原生固然源自于 Kubernetes 技术生态和理念,但也必然是一个超越 CNCF 和 Kubernetes 存在的一个全集。它被创立的目的和始终在坚持探索的方向,是使用户能够最大化利用”云“的能力、发挥”云“的价值,而不是在此过程中构建一个又一个不可复制、不可扩展的“巨型烟囱”。所以说,云原生这个词语的准确定义,是围绕着 Kubernetes 技术生态为核心的,但也一定是一个伴随着 CNCF 社区和 Kubernetes 项目不断演进而日趋完善的一个动态过程。而更为重要的是,在这次以”云“为关键词的技术革命当中,阿里巴巴很可能成为”云原生“的一个重要的定义者。本文作者:jessie筱姜阅读原文本文为云栖社区原创内容,未经允许不得转载。

March 7, 2019 · 1 min · jiezi

容错性好、易于管理和便于观察:浅谈如何利用K8s全面拥抱微服务架构

KubeCon + CloudNativeCon 论坛,作为 CNCF 的旗舰会议,自2016年以来已经在北美和欧洲两地的旧金山、伦敦、硅丘(奥斯汀)、哥本哈根等知名城市举办。2018年11月15日,KubeCon + CloudNativeCon 论坛首次来到中国,在上海跨国采购会展中心召开并获得了圆满成功。在去年的论坛上,CNCF不仅邀请到了Liz Rice、Janet Kuo等开源技术大牛到场为KubeCon + CloudNativeCon 在中国举行的首秀呐喊助威,更吸引到了包括美国、日本、印度在内的五大洲48个国家的开源精英前来参与。期间,有954家公司派代表到场展示自己的理念或者聆听精彩的技术分享。据统计,2018年盛会的参会者超过2500人,CNCF 为参会者提供了13场次的主题演讲、97场次的分组会议、55场次的维护者会议和15场次的快闪演讲。华为云、阿里云、腾讯云、微软、GitLab、Lyft等众多知名企业纷纷登台为大家分享相关的产品和技术架构,并为在场观众解答了相关问题。在上届论坛上,大家询问最多的,就是在 K8s(Kubernetes)架构上的使用精髓,而其中,比较有代表性的问题,则是企业如何利用 K8s 作为微服务架构,并且如何通过这样的部署来提升企业的自身价值。那么,什么是微服务架构呢?其实,微服务架构是与当前常用的单体架构相比较而言的。单体架构虽然方便管理,易于规划,但是灵活性和稳定性还是有所欠缺的,甚至一旦局部架构受损则可能面临全盘崩溃的危险;而且单体架构的迭代操作,比较复杂,部署速度慢不说,还会出现阻塞持续集成的现象,这对于要求时效性且相对复杂的生产环境来说,是不言而喻的灾难。基于此,便出现了小的模块化的自治服务架构,即我们常说的微服务架构。因为它所有的节点都被连接到了API网关,所以API网关的用户都会自动连接到这个完整的系统来调用或者聚合所需资源,来完成整个工作。所以,微服务架构在目前来看,基本解决了单体架构的弊端,这如同使用并联的灯泡组,即使一个灯泡损坏,亦不会影响全局的稳定和整体的生产进度,更不会出现整个局部系统的损坏而导致整体系统全面崩塌的灾难性后果。众所周知的是,任何一种架构的运用都会面临一些问题,我们采用的微服务架构也不例外。虽然微服务架构本身具有稳定、轻量、高速的特性,但是在现实的企业生产环境部署过程中,也会出现诸如调度、负载均衡、集群管理、有状态数据的管理等问题。而作为一切以服务为中心的K8s,则为我们提供了解决上述问题的最佳方式。K8s拥抱微服务就成为大势所趋。作为上届大会的焦点 - K8s如何更好的拥抱微服务?主题演讲有这些分享:Zhenqin (Alan) Liao(华为云 PaaS 服务产品部部长)的“垂直扩展,Kubernetes 如何加速各行业的云原生移动”Vicki Cheung(Lyft 工程经理)的“Kubernetes 是架构的基础层”Brendan Burns(杰出工程师及Microsoft K8s 联合创始人)的“Kubernetes 无服务器架构的现在和未来”专题演讲也分享了不少:Tony Erwin和Jonathan Schweikhart(IBM)的“将企业微服务从Cloud Foundry迁移到Kubernetes”Xiang Li(阿里巴巴)的“日新月异的 Sigma:在阿里巴巴使用 Kubernetes”Dan Romlein和Spencer Sugarman(Google)的“用户介面:利用 Kubernetes Dashboard 并决定其未来”Peter Zhao和Yuan Ji(ZTE)的“Kubernetes:使用、贡献并享受它!“Land Lu和Zhang Lei Mao(Canonical)的“利用 MicroK8s 和 Kubeflow 达成的 Kubernetes CICD 小技巧”Hui Chi(PetroChina)和Kai Chen(Alauda)的“石油巨头与Kubernetes, Microservice & DevOps共舞”Shikha Srivastava和Erica Brown(IBM)的“通过 Kubernetes 实现从容器化应用到安全和缩放”YIN SUN(小米)的“从 Mesos 到 Kubernetes”那么,在2019年,这些议题又会有哪些大咖加以完善,并与您分享更新的实战观点呢?敬请关注,今年2019年,CNCF和LF为您即将呈现的开源技术盛宴 - KubeCon + CloudNativeCon 和 Open SourceSummit(原LC3)。KubeCon + CloudNativeCon和Open Source Summit大会日期:会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日KubeCon + CloudNativeCon和Open Source Summit赞助方案KubeCon + CloudNativeCon和Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国KubeCon + CloudNativeCon和Open Source Summit购票窗口,立即购票! ...

March 7, 2019 · 1 min · jiezi

Kubernetes概念与术语

综述学习Kubernetes时,发现它的概念和术语还是比较多的,光靠啃官方文档比较晦涩。所以边学习边整理,对主要的概念和术语做一下分类及简要说明。感觉把重要概念都理解了,对Kubernetes的设计思想,整体框架也就有了初步的认识。从功能上来说,我把Kubernetes的概念或术语大概分为以下三类:组件:指实际在集群中运行的Kubernetes程序集,分为Master组件、Node组件及附加组件。对象:对象是一个抽象的概念实体,Kubernetes把应用、运行场景、功能、集群状态等都抽象成一个个对象,并通过API对这些对象进行管理,从而管理整个集群的状态。标识:对组件和对象的各种表示方式,例如命名、标签、注释等。标识是KubernetesAPI与操作对象间的纽带。组件Master组件Master组件提供了k8s管理集群的核心功能,k8s通过Master组件实现整个集群的调度管理,它就像集群的大脑,会根据集群当前的状态,不断进行调整,使集群一直保持在期望的状态。Master组件包括以下几个进程:kube-apiserverapiserver进程对外暴露REST API接口给外部客户端及内部组件调用,它封装了对各个对象的增、删、改、查操作,可以说是整个集群管理的总入口。kube-schedulerkube-scheduler做的事情说来简单–监控新pod的创建,并把pod分配到合适的node来运行。但实际上,选择“合适的Node”是一个很复杂的过程,需要考虑的因素包括单node及整个集群的资源需求、软硬件资源的限制策略、数据存储、工作负载间的干扰等等。kube-controllers-managercontrollers-manager进程从字面上理解,是用来做控制器管理的。controllers-manager其实是一组守护进程,通过监控集群各组件的状态,并不断调整集群实际状态,使的集群向期望状态调整,它由一组子进程构成:replication controllerendpoints controllernamespace controllerserviceaccounts controllerEtcdEtcd是Kubernetes集群的基础组件,用于保存集群所有的元数据信息、配置信息、对象状态等。Node组件运行Node组件的节点称为Node节点,是k8s集群实际的工作负载节点。Node节点可以是物理机、虚拟机或任何可以运行Node组件的设备。所有的业务应用都运行在Node节点中。Node组件包括以下几个部分:Kubeletkubelet负责实际的容器管理,Kubernetes通过apiserver给kubelet发送Pod管理请求,同时把Pod及Node的运行状态汇报给apiserver。kube-proxykube-proxy负责集群内部的请求转发及负载均衡工作。它通过Service对象的配置信息,为Pod创建代理服务,实现请求从Service到Pod的路由转发。容器运行时实际的容器运行引擎。Kubernetes其实支持其他的容器技术比如rkt,但是Docker相比与它们处于绝对的优势地位。所以,Kubernetes生态中的容器也基本特指指Docker容器。对象Kubernetes中,所谓对象指的就是API对象。Kubernetes集群为每个对象维护三类信息:对象元数据(ObjectMeta)、期望状态(Desired State)与实际状态(Autual State),元数据指对象的基本信息,比如命名、标签、注释等等,用于识别对象;期望状态一般由用户配置spec来描述的;实际状态是由集群各个组件上报的集群实际的运行情况。Kubernetes努力使每个对象的实际状态与期望状态相匹配,从而使整个集群的状态与用户配置的期望状态一致。Kubernetes对象一般用yaml文件来描述,有几个个字段是必须的:apiVersion: 创建该对象所使用的 Kubernetes API 的版本kind: 想要创建的对象的类型metadata: 帮助识别对象唯一性的数据,包括一个 name 字符串、UID 和可选的 namespacespec: 对象期望状态的描述用户通过命令行工具kubectl来操作这些对象,Kubectl吧yaml转换成JSON格式来执行API请求。下面是一个POD对象的描述文件示例及注释:apiVersion: v1kind: Pod # 对象名称metadata: # 对象的基本信息 name: pod-example # 对象唯一标示spec: # 期望状态 containers: # 指定pod中运行的容器镜像及运行参数(类似Dockerfile) - name: ubuntu image: ubuntu:trusty command: [“echo”] args: [“Hello World”]其中,kind字段描述对象类型"Pod",spec之前是对象的ObjectMeta,spec开始,就是对象期望状态的描述。根据官方API对象参考文档的分类,下面我们把对象分为几大类来分别简要说明。计划以后的文章,会结合实际操作,对各个重要对象做更深入的说明。Workloads 资源对象- workloads类的对象用于管理运行容器实例Pod: Pod是Kubernetes里最重要的对象,也是Kubernetes集群中运行部署应用或服务的最小单位。Pod对象描述一个Pod由哪些容器镜像构成,以及这些容器应该怎么运行。类比传统的业务架构,Pod更接近于虚拟机的角色而不是应用进程的角色。ReplicaSet: ReplicaSet对象是对Pod的更高一层抽象,它用来管理一组相同POD副本,并确保这组相同Pod的数量始终与用户期望一致,并实现Pod的高可用。即如果有容器异常退出,ReplicaSet会自动创建新的Pod来替代,保证Pod数量永远与用户配置一致。Deployment: Deployment对象是Kubernetes中最常用的对象之一,用于部署无状态服务。它在ReplicaSet对象的基础上,又做了一层抽象。通过管理多组ReplicaSet对象,来实现POD的滚动升级、回滚、扩容缩容等。StatefulSet: 不同于Deployment,StatefulSet对象顾名思义是对有状态服务的一种抽象。所以,StatefulSet对象在定义时,相比与Deployment多了一些与状态相关的内容,比如持久化存储、服务对外的不变的唯一标示、部署、扩容、滚动升级时确保有序等等。DaemonSet: DaemonSet对象是Kubernetes对守护进程的抽象。当我们需要集群中的每个Node都跑一些如日志收集、监控等守护进程时,就可以把这类型进程包装成Pod,并用调用DaemonSet来部署了。DaemonSet做的事情其实和Deployment差不多,只不过Deployment对象部署的Pod,会根据集群情况,在不同Node间自动调度。而DaemonSet会指定的NODE上(默认是集群所有Node),都部署定义Pod,确保这些NODE都有跑着守护进程Pod。Job、CronJob: 除了上面几个对象所抽象描述的应用工作场景外,实际业务场景中,还有一类应用或服务并不需要一直运行,比如一些一次性任务,或需要定时执行的任务。这种不需要长时间运行的任务,完成了就可以退出的服务,就可以用Job对象来定义。CronJob和Job对象的关系,和Deployment与ReplicaSet的关系很像,可以类比来理解。首先,Job也是直接用于管理Pod的,同时可以定义这个一次性任务Pod的执行时间、超时时间、错误处理(重启还是生成新Pod)等任务属性。而CronJob管理是用来管理Job,类似crobtab,它可以通过yaml文件中的schedule字段配置具体时间,来控制指定Job定时执行,从而实现定时执行特定的一次性任务。Discovery & LB 资源对象- 该类对象用于服务发现和负载均衡的对象Service:上面提到的对象都是用于管理POD及容器实例的。但是,业务系统光有POD实例还不够,还必须对外提供服务。这里就会衍生出两个问题:Pod的IP地址并不是固定,而是随着编排不断变化的,外面的请求怎么找到对应的POD,这个也就是大多数分布式业务都会遇到的服务发现问题多个相同Pod一起提供服务的时候,它们的负载均衡怎么实现而Service对象就是用来解决这两个问题的。它用固定的VIP或DNS名称和指定标识的一组Pod对应起来,不管Pod IP怎么变,Service对外的VIP都不会变化,并且,自动的将请求在这组Pod中负载均衡。Config & Storage 资源对象- 该类对象用于初始化容器配置及持久化容器存储的对象Volume:前面说到,Pod里的不同容器可以共享存储,这个共享存储就靠Volume来配置的。要注意的是,这里Volume与Docker中的定义不用,Kubernetes中Volume跟Pod生命周期一致,Pod终止了,该Pod挂载的Volume就失效了(根据挂载volume的类型不同,数据可能丢失也可能不丢失)。PV,PVC:Volume可以支持多种类型的存储,除了直接在Volume字段中去声明所有存储细节,K8S还抽象出了PV和PVC对象。简单来说PV是对具体存储资源的描述,比如存储类型、地址、访问账户等;而PVC,是对怎么使用资源的方法的描述,比如只读只写,需要的空间等;而Pod通过Volume字段挂载具体要使用的PVC。PV和PVC是独立于Pod单独定义的,这样,就把共享存储的配置、分割、挂载等操作都解耦了。比如,一个NFS迁移后ip地址变了,存储管理员只需要修改PV中的配置,而不用关心具体哪个业务POD在使用这个NFS。ConfigMap,SecuretK8S中除了常见的存储,还有一类特殊的Volume,不是为了Pod存放数据,而是为了给Pod提供数据的。ConfigMap和Secret对象就是用来定义这类型的Volume。简单来说,可以将它们理解为一份Key:Value格式的数据,Pod可以通过Volume挂载它们,将这份数据保存成文件随时调用。唯一的区别是,ConfigMap对象保存的数据是明文,一般作为应用配置文件;而Secret对象保存的对象要求是经过Base64转码的,用于提供数据库密码等对安全要求比较高的配置。不过这些配置,直接在做容器镜像时就配置不就好了,为啥要多此一举呢?原因和上面PV和PVC一样,都是为了尽可能解耦业务核心与经常可能变化的依赖配置。比如数据库更换了账号,只需要修改Secret对象,不用重新去构建容器镜像。Cluster resources objects- 该类对象定义整个K8S集群运行方式的对象NamespaceKubernetes作为一个集群管理平台,为不同用户划分不同权限管理,例如多租户等,是必备的功能。而不同用户作用域的隔离,就是靠Namespace对象实现的。Namespace是Kubernetes项目中的一个逻辑管理单位,不同Namespace的API对象,在调用API进行操作是,是相互隔离开的。通过不同用户角色与Namespace关联,从而实现权限管理。Metadata resources- 除了上述分类外的其他对象都归为这一类,一般用来管理集群的特殊功能比如弹性伸缩等Horizontal Pod Autoscaling容器作为一个轻量级的承载应用的技术,快速启动和快速部署是它的优点之一。自然的,根据业务负载自动扩缩容等需求,在容器集群的可行性和效率就可以变得很高。而Kubernetes中 Horizontal Pod Autoscaling对象,就是用于进行POD自动水平缩放的,这也是Kubernetes最能体现运维优势的特性之一。Horizontal Pod Autoscaling具体的操作对象是Deployment和ReplicaSet,通过不同循环获取每个Pod的资源情况,比如CPU利用率,并根据设定的目标利率来计算目前需要的Pod副本缩放的比例,然后通过访问相应的Deployment或ReplicaSet对象,来对Pod数量进行自动缩放,从而提高了集群资源整体利用率。标识标识是Kubernetes中最重要的概念,因为它是所有API的操作与操作对象间的纽带。Kubernetes中的标识主要有以下几种:LabelLabel可以说是Kubernetes中最最重要的核心概念,一个Lable是一个KEY=Value的键值对。任何对象资源都可以有一个或多个Label,而同个Label,也可以配置在多个对象上。然后重点来了,大多数API对象的yaml字段中,都有Label Selector字段,可以实现对Label中的key或value的多维度选择,例如in、not in、and、or等等。这样,Kubernetes对API作用对象就能进行多维度,细粒度的选择,从而实现比较精细化的容器编排调度工作,比如,对所有"ENV:release"执行扩容操作,或者把部分请求灰度到有"ver:v1.14.0"的pod上等等。Label把资源对象和应用、基础设施都解耦了,你不用关心这个Pod具体在那个Node上运行,也不用关心具体是什么应用用了哪个容器镜像,只要Label符合Label Selector,就可以通过API调用统一进行操作。Names除了Label,所有资源对象肯定还必须有一个全局唯一的标识,即Names字段。AnnotationAnnotation很好理解,与通常意义的注释一样,用于描述作者、版本、配置说明等等任何需要的信息,需要说明的是,Annotation也必须是Key:Value格式的。总结通过上面对Kubernetes重要概念的说明,我们可以大致梳理出Kubernetes的一些设计理念:Kubernetes对容器应用进行了抽象,例如把一个容器或强关联的一组容器抽象为Pod,把各类存储都抽象成Volume,把一组Pod抽象成Service等等基础对象。在基础对象的层面上,Kubernetes又对应用使用场景,做了一层抽象。极客时间的Kubernetes课程有一张图画的很好:可以看到,Kubernetes中各个对象实际就是对生产业务场景的各类需求的抽象。抽象出各类型对象以后,用户可以通过yaml文件(或直接命令行调用API)来描述这些对象的期望状态,确认了各对象些期望状态的集合,也就确认了整个集群的期望状态。这些所有的操作,都是声明式的,而不是命令集群要怎么做Kubernetes通过控制器循环不断将各组件收集来的集群实际状态与各对象的期望状态对比,并自动化的将集群实际状态向期望状态转移,这个过程,也就是Kubernetes最核心的概念:编排。本文对Kubernetes中的重要概念做了分类和简要,后面的文章会结合集群的实际操作,对每个概念做更详细的说明。 ...

March 6, 2019 · 1 min · jiezi

使用OperatorHub.io自动化群集上的操作

作者:Diane Mueller,红帽云平台社区发展总监开发者和Kubernetes管理员面临的重要挑战之一,是缺乏快速查找在Kubernetes提供运营就绪的公共服务的能力。通常情况下,存在特定服务的Operator - 这种模式在2016年推出并获得了动力 - 对于Kubernetes服务的运营就绪是一个很好的信号。但是,迄今为止还没有Operator注册表来简化发现此类服务。为了帮助应对这一挑战,今天Red Hat与AWS、Google Cloud和Microsoft合作推出OperatorHub.io。OperatorHub.io使开发者和Kubernetes管理员能够查找和安装策划好的、Operator支持的服务,其中包括基础文档、社区或供应商的主动维护、基本测试以及Kubernetes优化生命周期管理的打包。目前在OperatorHub.io中的Operator只是开始。我们邀请Kubernetes社区加入我们,通过在OperatorHub.io上开发、打包和发布Operator,为Operator建立一个充满活力的社区。OperatorHub.io提供什么?OperatorHub.io旨在满足Kubernetes开发者和用户的需求。对于前者,它提供了通用的注册表,他们可以在其中发布他们的Operator以及描述、相关的详细信息,如版本、镜像、代码仓库,并打包准备方便安装。他们也可以对已发布的Operator发布更新版本。用户可以在一个中心位置发现和下载Operator,该Operator的内容已根据前面提到的标准进行筛选并扫描已知漏洞。此外,开发者可以使用他们引入的CustomResources的说明性示例,指导其Operator的用户,与应用程序进行交互。Operator是什么?Operator最初由CoreOS于2016年推出,并已被Red Hat和Kubernetes社区用作打包、部署和管理Kubernetes原生应用程序的方法。Kubernetes原生应用程序是一个部署在Kubernetes上的应用程序,使用Kubernetes API和众所周知的工具进行管理,如kubectl。Operator实现为自定义控制器,用于监视某些Kubernetes资源的显示、修改或删除。这些通常是Operator“拥有”的CustomResourceDefinition。在这些对象的spec属性中,用户声明应用程序或操作的所需状态。Operator的协调循环将选择这些,并执行所需的操作以实现所需的状态。例如,可以通过创建EtcdCluster类型的新资源,来表达创建高可用性etcd集群的意图:apiVersion: “etcd.database.coreos.com/v1beta2"kind: “EtcdCluster"metadata: name: “my-etcd-cluster"spec: size: 3 version: “3.3.12"这样,EtcdOperator将负责创建运行版本v3.3.12的3节点etcd集群。类似地,可以定义类型为EtcdBackup的对象,以表示创建etcd数据库一致备份到S3存储桶的意图。如何创建和运行Operator?一种入门方法是使用Operator框架,这是一个开源工具包,提供SDK、生命周期管理、计量和监视功能。它使开发者能够构建、测试和打包Operator。Operator可以用几种编程和自动化语言实现,包括Go、Helm和Ansible,这三种语言都直接由SDK支持。如果你有兴趣创建自己的Operator,我们建议你查看Operator框架以开始使用。Operator的功能范围各不相同,从基本功能到应用程序的特定操作逻辑,以及备份、恢复或调整等高级方案的自动化。除了基本安装之外,高级Operator可以更加无缝地处理升级并自动应对故障。目前,OperatorHub.io上的Operator来自不同成熟度范围,但我们预计它们会随着时间而持续成熟。虽然不需要使用SDK实现OperatorHub.io上的Operator,但它们是打包给通过Operator Lifecycle Manager(OLM)进行部署。格式主要由称为ClusterServiceVersion的YAML清单组成。它提供有关Operator拥有或要求的CustomResourceDefinitions的信息、所需的RBAC定义、存储图像的位置等。此文件通常附带定义Operator自己的CRD的其他YAML文件。OLM在用户请求安装Operator以提供依赖性解析和自动化时处理此信息。OperatorHub.io上的Operator列表是什么意思?要列出,Operator必须成功显示群集生命周期功能,打包为CSV并通过OLM维护,以及为其预期用户提供可接受的文档。目前在OperatorHub.io上列出的Operator的一些示例包括:Amazon Web Services Operator、Couchbase Autonomous Operator、CrunchyData’s PostgreSQL、etcd Operator、Jaeger Operator for Kubernetes、Kubernetes Federation Operator、MongoDB Enterprise Operator、Percona MySQL Operator、PlanetScale’s Vitess Operator、Prometheus Operator和Redis Operator。想要将你的Operator添加到OperatorHub.io?跟着这些步骤如果你有现有的Operator,请遵循贡献指南使用社区Operator仓库的分支。每个贡献包含CSV、所有CustomResourceDefinitions、访问控制规则以及安装和运行Operator所需的容器映像的资料,其功能描述和支持的Kubernetes版本等其他信息。EtcdOperator可以作为完整的示例,包括Operator的多个版本。在你自己的集群上测试Operator之后,将PR提交到社区存储库,其中包含此目录结构的所有YAML文件。可以以相同的方式发布Operator的后续版本。刚开始这将是手动审查,但往后会自动化。由维护者合并之后,它将显示在OperatorHub.io上,以及其文档和方便的安装方法。想了解更多?参加即将举行的Kubernetes Operator框架实践研讨会:3月7日在Pasadena的ScaleX举行,以及3月11日在Santa Clara的OpenShift Commons Gathering on Operating举行听听Daniel Messer和Diane Mueller关于“Operator现况”的OpenShift Commons简报加入社区Kubernetes-Operator Slack Channel和Operator框架Google Group的在线对话最后,阅读如何将你的Operator添加到OperatorHub.io:https://operatorhub.io/contri…KubeCon + CloudNativeCon和Open Source Summit大会日期:会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日KubeCon + CloudNativeCon和Open Source Summit赞助方案KubeCon + CloudNativeCon和Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国KubeCon + CloudNativeCon和Open Source Summit购票窗口,立即购票! ...

March 4, 2019 · 1 min · jiezi

containerd正式从CNCF毕业!

containerd是被阿里云、AWS、Rancher Labs、Docker、Google、IBM以及容器生态系统内的其他更多成员最广泛采用的容器运行时。美国时间2019年2月28日,CNCF正式宣布containerd毕业了!containerd是继Kubernetes、Prometheus、Envoy和CoreDNS之后,第五个从CNCF毕业的项目。从孵化成熟阶段开始,要想最终毕业,项目必须表现出高度的多样性,被大量用户广泛使用,拥有正式的治理过程,并且面向整个开源社区坚持其可持续性及包容性。“大概两年前,containerd正式成为CNCF的项目,这之后我们能明显看到containerd显著的发展势头,而这也充分展示了市场对基础容器技术的巨大需求。”CNCF首席技术官Chris Aniszczyk表示,“社区共同协作并投入了大量精力,对稳定的核心容器运行时进行开发和测试。而且社区也付出了很多努力为其扩大维护者和采用基础、通过外部安全审计,现在,我们无比激动看到containerd正式毕业!”2014年,containerd诞生于Docker,最初它是Docker引擎的底层运行时管理器。继2017年3月被CNCF接受之后,containerd已经成为一个行业标准的容器运行时,它简单、稳定、有良好的可移植性,最广泛的使用方式是作为Docker引擎和OCI runc执行器之间的层。containerd亦可用作Linux和Windows的守护进程。它还可以管理其主机系统的完整容器生命周期,从镜像传输和存储,到容器执行和监督,再到底层存储,再到网络附件等等。“当Docker向社区提供containerd时,我们的目标是共享一个强大且可扩展的运行时,数百万用户和成千上万的组织已经将其标准化为Docker Engine的一部分,”containerd维护者、Docker工程师Michael Crosby说。“我们一直在不断努力希望能进一步满足现代容器平台(如Docker平台和Kubernetes生态系统)的需求。过去一年中containerd被更多组织采用,并且拥有了进一步的创新,我们的工作也就有所回报了。containerd的用户数量不断增长,使用范围更为广泛了,我们期待在整个生态系统中继续合作,继续推动我们的行业发展。“containerd自成立以来就拥有各种维护者和贡献者,目前有来自阿里巴巴、Cruise Automation、Docker、Facebook、Google、华为、IBM、微软、NTT、特斯拉等公司的14位committer,拥有4406份commit以及166位贡献者。containerd是被阿里云、AWS、Rancher Labs、Docker、Google、IBM、Cloud Foundry以及更多生态系统支持者最广泛采用的容器运行时。containerd和Rancher Labs的很多产品都颇有渊源,比如容器操作系统RancherOS和新近发布的轻量级Kubernetes发行版K3s。对于RancherOS,它是专为容器而生的container Linux,与其他Linux发行版不同的是,它使用system-docker来代替systemd来作为Pid 1,而containerd就是system-docker的重要组成部分。Rancher Labs两天前新近发布的K3s——史上最轻量的、开源Kubernetes发行版,正是使用containerd代替Docker作为运行时的容器引擎。K3s只有40M大小,通过用containderd替换Docker,K3s显著减少了运行时占用空间,删除libnetwork、swarm、Docker存储驱动程序和其他插件等功能。为了正式从孵化状态毕业,containerd还采用了CNCF 行为准则,执行了独立的安全审计,并确定了自己的治理结构以发展社区。此外,containerd还必须获得(并维护)核心基础设施倡议(CII)的最佳实践徽章。2018年9月1 日,containerd获得的CII徽章,正是证实了其代码质量和安全最佳实践的一贯承诺。containerd,毕业快乐!

March 4, 2019 · 1 min · jiezi

容器监控实践—Prometheus的配置与服务发现

本文将分析Prometheus的常见配置与服务发现,分为概述、配置详解、服务发现、常见场景四个部分进行讲解。一. 概述Prometheus的配置可以用命令行参数、或者配置文件,如果是在k8s集群内,一般配置在configmap中(以下均为prometheus2.7版本)查看可用的命令行参数,可以执行 ./prometheus -h也可以指定对应的配置文件,参数:–config.file 一般为prometheus.yml如果配置有修改,如增添采集job,Prometheus可以重新加载它的配置。只需要向其进程发送SIGHUP或向/-/reload端点发送HTTP POST请求。如:curl -X POST http://localhost:9090/-/reload二. 配置详解2.1 命令行参数执行./prometheus -h 可以看到各个参数的含义,例如:–web.listen-address=“0.0.0.0:9090” 监听端口默认为9090,可以修改只允许本机访问,或者为了安全起见,可以改变其端口号(默认的web服务没有鉴权)–web.max-connections=512 默认最大连接数:512–storage.tsdb.path=“data/” 默认的存储路径:data目录下–storage.tsdb.retention.time=15d 默认的数据保留时间:15天。原有的storage.tsdb.retention配置已经被废弃–alertmanager.timeout=10s 把报警发送给alertmanager的超时限制 10s–query.timeout=2m 查询超时时间限制默认为2min,超过自动被kill掉。可以结合grafana的限时配置如60s–query.max-concurrency=20 并发查询数 prometheus的默认采集指标中有一项prometheus_engine_queries_concurrent_max可以拿到最大查询并发数及查询情况–log.level=info 日志打印等级一共四种:[debug, info, warn, error],如果调试属性可以先改为debug等级…..在prometheus的页面上,status的Command-Line Flags中,可以看到当前配置,如promethues-operator的配置是:2.2 prometheus.yml从官方的download页下载的promethues二进制文件,会自带一份默认配置prometheus.yml-rw-r–r–@ LICENSE-rw-r–r–@ NOTICEdrwxr-xr-x@ console_librariesdrwxr-xr-x@ consoles-rwxr-xr-x@ prometheus-rw-r–r–@ prometheus.yml-rwxr-xr-x@ promtoolprometheus.yml配置了很多属性,包括远程存储、报警配置等很多内容,下面将对主要属性进行解释:# 默认的全局配置global: scrape_interval: 15s # 采集间隔15s,默认为1min一次 evaluation_interval: 15s # 计算规则的间隔15s默认为1min一次 scrape_timeout: 10s # 采集超时时间,默认为10s external_labels: # 当和其他外部系统交互时的标签,如远程存储、联邦集群时 prometheus: monitoring/k8s # 如:prometheus-operator的配置 prometheus_replica: prometheus-k8s-1# Alertmanager的配置alerting: alertmanagers: - static_configs: - targets: - 127.0.0.1:9093 # alertmanager的服务地址,如127.0.0.1:9093 alert_relabel_configs: # 在抓取之前对任何目标及其标签进行修改。 - separator: ; regex: prometheus_replica replacement: $1 action: labeldrop # 一旦加载了报警规则文件,将按照evaluation_interval即15s一次进行计算,rule文件可以有多个rule_files: # - “first_rules.yml” # - “second_rules.yml”# scrape_configs为采集配置,包含至少一个jobscrape_configs: # Prometheus的自身监控 将在采集到的时间序列数据上打上标签job=xx - job_name: ‘prometheus’ # 采集指标的默认路径为:/metrics,如 localhost:9090/metric # 协议默认为http static_configs: - targets: [’localhost:9090’]# 远程读,可选配置,如将监控数据远程读写到influxdb的地址,默认为本地读写remote_write: 127.0.0.1:8090# 远程写remote_read: 127.0.0.1:8090 2.3 scrape_configs配置prometheus的配置中,最常用的就是scrape_configs配置,比如添加新的监控项,修改原有监控项的地址频率等。最简单配置为:scrape_configs:- job_name: prometheus metrics_path: /metrics scheme: http static_configs: - targets: - localhost:9090完整配置为(附prometheus-operator的推荐配置):# job 将以标签形式出现在指标数据中,如node-exporter采集的数据,job=node-exporterjob_name: node-exporter# 采集频率:30sscrape_interval: 30s# 采集超时:10sscrape_timeout: 10s# 采集对象的path路径metrics_path: /metrics# 采集协议:http或者httpsscheme: https# 可选的采集url的参数params: name: demo# 当自定义label和采集到的自带label冲突时的处理方式,默认冲突时会重名为exported_xxhonor_labels: false# 当采集对象需要鉴权才能获取时,配置账号密码等信息basic_auth: username: admin password: admin password_file: /etc/pwd# bearer_token或者文件位置(OAuth 2.0鉴权)bearer_token: kferkhjktdgjwkgkrwgbearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token# https的配置,如跳过认证,或配置证书文件tls_config: # insecure_skip_verify: true ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt server_name: kubernetes insecure_skip_verify: false# 代理地址proxy_url: 127.9.9.0:9999# Azure的服务发现配置azure_sd_configs:# Consul的服务发现配置consul_sd_configs: # DNS的服务发现配置dns_sd_configs:# EC2的服务发现配置ec2_sd_configs:# OpenStack的服务发现配置openstack_sd_configs:# file的服务发现配置file_sd_configs:# GCE的服务发现配置gce_sd_configs:# Marathon的服务发现配置marathon_sd_configs:# AirBnB的服务发现配置nerve_sd_configs:# Zookeeper的服务发现配置serverset_sd_configs:# Triton的服务发现配置triton_sd_configs:# Kubernetes的服务发现配置kubernetes_sd_configs: - role: endpoints namespaces: names: - monitoring# 对采集对象进行一些静态配置,如打特定的标签static_configs: - targets: [’localhost:9090’, ’localhost:9191’] labels: my: label your: label # 在Prometheus采集数据之前,通过Target实例的Metadata信息,动态重新写入Label的值。如将原始的__meta_kubernetes_namespace直接写成namespace,简洁明了relabel_configs: - source_labels: [__meta_kubernetes_namespace] separator: ; regex: (.) target_label: namespace replacement: $1 action: replace - source_labels: [__meta_kubernetes_service_name] separator: ; regex: (.) target_label: service replacement: $1 action: replace - source_labels: [_meta_kubernetes_pod_name] separator: ; regex: (.) target_label: pod replacement: $1 action: replace - source_labels: [__meta_kubernetes_service_name] separator: ; regex: (.) target_label: job replacement: ${1} action: replace - separator: ; regex: (.*) target_label: endpoint replacement: web action: replace# 指标relabel的配置,如丢掉某些无用的指标metric_relabel_configs: - source_labels: [name] separator: ; regex: etcd(debugging|disk|request|server).* replacement: $1 action: drop # 限制最大采集样本数,超过了采集将会失败,默认为0不限制sample_limit: 0三. 服务发现上边的配置文件中,有很多*sd_configs的配置,如kubernetes_sd_configs,就是用于服务发现的采集配置。支持的服务发现类型:// prometheus/discovery/config/config.gotype ServiceDiscoveryConfig struct { StaticConfigs []*targetgroup.Group yaml:"static_configs,omitempty" DNSSDConfigs []*dns.SDConfig yaml:"dns_sd_configs,omitempty" FileSDConfigs []*file.SDConfig yaml:"file_sd_configs,omitempty" ConsulSDConfigs []*consul.SDConfig yaml:"consul_sd_configs,omitempty" ServersetSDConfigs []*zookeeper.ServersetSDConfig yaml:"serverset_sd_configs,omitempty" NerveSDConfigs []*zookeeper.NerveSDConfig yaml:"nerve_sd_configs,omitempty" MarathonSDConfigs []*marathon.SDConfig yaml:"marathon_sd_configs,omitempty" KubernetesSDConfigs []kubernetes.SDConfig yaml:"kubernetes_sd_configs,omitempty" GCESDConfigs []gce.SDConfig yaml:"gce_sd_configs,omitempty" EC2SDConfigs []ec2.SDConfig yaml:"ec2_sd_configs,omitempty" OpenstackSDConfigs []openstack.SDConfig yaml:"openstack_sd_configs,omitempty" AzureSDConfigs []azure.SDConfig yaml:"azure_sd_configs,omitempty" TritonSDConfigs []triton.SDConfig yaml:"triton_sd_configs,omitempty"}因为prometheus采用的是pull方式来拉取监控数据,这种方式需要由server侧决定采集的目标有哪些,即配置在scrape_configs中的各种job,pull方式的主要缺点就是无法动态感知新服务的加入,因此大多数监控都默认支持服务发现机制,自动发现集群中的新端点,并加入到配置中。Prometheus支持多种服务发现机制:文件,DNS,Consul,Kubernetes,OpenStack,EC2等等。基于服务发现的过程并不复杂,通过第三方提供的接口,Prometheus查询到需要监控的Target列表,然后轮询这些Target获取监控数据。对于kubernetes而言,Promethues通过与Kubernetes API交互,然后轮询资源端点。目前主要支持5种服务发现模式,分别是:Node、Service、Pod、Endpoints、Ingress。对应配置文件中的role: node/role:service如:动态获取所有节点node的信息,可以添加如下配置:- job_name: kubernetes-nodes scrape_interval: 1m scrape_timeout: 10s metrics_path: /metrics scheme: https kubernetes_sd_configs: - api_server: null role: node namespaces: names: [] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true relabel_configs: - separator: ; regex: _meta_kubernetes_node_label(.+) replacement: $1 action: labelmap - separator: ; regex: (.) target_label: address replacement: kubernetes.default.svc:443 action: replace - source_labels: [__meta_kubernetes_node_name] separator: ; regex: (.+) target_label: metrics_path replacement: /api/v1/nodes/${1}/proxy/metrics action: replace就可以在target中看到具体内容对应的service、pod也是同样的方式。需要注意的是,为了能够让Prometheus能够访问收到Kubernetes API,我们要对Prometheus进行访问授权,即serviceaccount。否则就算配置了,也没有权限获取。prometheus的权限配置是一组ClusterRole+ClusterRoleBinding+ServiceAccount,然后在deployment或statefulset中指定serviceaccount。ClusterRole.yamlapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRolemetadata: namespace: kube-system name: prometheusrules:- apiGroups: [""] resources: - configmaps - secrets - nodes - pods - nodes/proxy - services - resourcequotas - replicationcontrollers - limitranges - persistentvolumeclaims - persistentvolumes - namespaces - endpoints verbs: [“get”, “list”, “watch”]- apiGroups: [“extensions”] resources: - daemonsets - deployments - replicasets - ingresses verbs: [“get”, “list”, “watch”]- apiGroups: [“apps”] resources: - daemonsets - deployments - replicasets - statefulsets verbs: [“get”, “list”, “watch”]- apiGroups: [“batch”] resources: - cronjobs - jobs verbs: [“get”, “list”, “watch”]- apiGroups: [“autoscaling”] resources: - horizontalpodautoscalers verbs: [“get”, “list”, “watch”]- apiGroups: [“policy”] resources: - poddisruptionbudgets verbs: [“get”, list", “watch”]- nonResourceURLs: ["/metrics"] verbs: [“get”]ClusterRoleBinding.yamlapiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: namespace: kube-system name: prometheusroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheussubjects:- kind: ServiceAccount name: prometheus namespace: kube-systemServiceAccount.yamlapiVersion: v1kind: ServiceAccountmetadata: namespace: kube-system name: prometheusprometheus.yaml….spec: serviceAccountName: prometheus….完整的kubernete的配置如下:- job_name: kubernetes-apiservers scrape_interval: 1m scrape_timeout: 10s metrics_path: /metrics scheme: https kubernetes_sd_configs: - api_server: null role: endpoints namespaces: names: [] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true relabel_configs: - source_labels: [__meta_kubernetes_namespace, __meta_kubernetes_service_name, __meta_kubernetes_endpoint_port_name] separator: ; regex: default;kubernetes;https replacement: $1 action: keep- job_name: kubernetes-nodes scrape_interval: 1m scrape_timeout: 10s metrics_path: /metrics scheme: https kubernetes_sd_configs: - api_server: null role: node namespaces: names: [] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true relabel_configs: - separator: ; regex: _meta_kubernetes_node_label(.+) replacement: $1 action: labelmap - separator: ; regex: (.) target_label: address replacement: kubernetes.default.svc:443 action: replace - source_labels: [__meta_kubernetes_node_name] separator: ; regex: (.+) target_label: metrics_path replacement: /api/v1/nodes/${1}/proxy/metrics action: replace- job_name: kubernetes-cadvisor scrape_interval: 1m scrape_timeout: 10s metrics_path: /metrics scheme: https kubernetes_sd_configs: - api_server: null role: node namespaces: names: [] bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: false relabel_configs: - separator: ; regex: _meta_kubernetes_node_label(.+) replacement: $1 action: labelmap - separator: ; regex: (.) target_label: address replacement: kubernetes.default.svc:443 action: replace - source_labels: [__meta_kubernetes_node_name] separator: ; regex: (.+) target_label: metrics_path replacement: /api/v1/nodes/${1}/proxy/metrics/cadvisor action: replace- job_name: kubernetes-service-endpoints scrape_interval: 1m scrape_timeout: 10s metrics_path: /metrics scheme: http kubernetes_sd_configs: - api_server: null role: endpoints namespaces: names: [] relabel_configs: - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape] separator: ; regex: “true” replacement: $1 action: keep - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scheme] separator: ; regex: (https?) target_label: scheme replacement: $1 action: replace - source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path] separator: ; regex: (.+) target_label: metrics_path replacement: $1 action: replace - source_labels: [address, __meta_kubernetes_service_annotation_prometheus_io_port] separator: ; regex: ([^:]+)(?::\d+)?;(\d+) target_label: address replacement: $1:$2 action: replace - separator: ; regex: _meta_kubernetes_service_label(.+) replacement: $1 action: labelmap - source_labels: [__meta_kubernetes_namespace] separator: ; regex: (.) target_label: kubernetes_namespace replacement: $1 action: replace - source_labels: [__meta_kubernetes_service_name] separator: ; regex: (.) target_label: kubernetes_name replacement: $1 action: replace配置成功后,对应的target是:四. 常见场景1.获取集群中各节点信息,并按可用区或地域分类如使用k8s的role:node采集集群中node的数据,可以通过"meta_domain_beta_kubernetes_io_zone"标签来获取到该节点的地域,该label为集群创建时为node打上的标记,kubectl decribe node可以看到。然后可以通过relabel_configs定义新的值relabel_configs:- source_labels: [“meta_domain_beta_kubernetes_io_zone”] regex: “(.)” replacement: $1 action: replace target_label: “zone"后面可以直接通过node{zone=“XX”}来进行地域筛选2.过滤信息,或者按照职能(RD、运维)进行监控管理对于不同职能(开发、测试、运维)的人员可能只关心其中一部分的监控数据,他们可能各自部署的自己的Prometheus Server用于监控自己关心的指标数据,不必要的数据需要过滤掉,以免浪费资源,可以最类似配置;metric_relabel_configs: - source_labels: [name] separator: ; regex: etcd(debugging|disk|request|server).* replacement: $1 action: dropaction: drop代表丢弃掉符合条件的指标,不进行采集。3.搭建prometheus联邦集群,管理各IDC(地域)监控实例如果存在多个地域,每个地域又有很多节点或者集群,可以采用默认的联邦集群部署,每个地域部署自己的prometheus server实例,采集自己地域的数据。然后由统一的server采集所有地域数据,进行统一展示,并按照地域归类配置:scrape_configs: - job_name: ‘federate’ scrape_interval: 15s honor_labels: true metrics_path: ‘/federate’ params: ‘match[]’: - ‘{job=“prometheus”}’ - ‘{name=“job:.*”}’ - ‘{name=“node.*”}’ static_configs: - targets: - ‘192.168.77.11:9090’ - ‘192.168.77.12:9090’本文为容器监控实践系列文章,完整内容见:container-monitor-book ...

March 4, 2019 · 4 min · jiezi

容器监控实践—PromQL查询解析

一. 概述Prometheus除了存储数据外,还提供了一种强大的功能表达式语言 PromQL,允许用户实时选择和汇聚时间序列数据。表达式的结果可以在浏览器中显示为图形,也可以显示为表格数据,或者由外部系统通过 HTTP API 调用。通过PromQL用户可以非常方便地查询监控数据,或者利用表达式进行告警配置如:k8s中的node在线率:sum(kube_node_status_condition{condition=“Ready”, status=“true”}) / sum(kube_node_info) 100 Metric类型关于时间序列存储,可以参考:https://www.infoq.cn/article/…Prometheus会将所有采集到的样本数据以时间序列(time-series)的方式保存在内存数据库TSDB中,并且定时保存到硬盘上。time-series是按照时间戳和值的序列顺序存放的,我们称之为向量(vector)。每条time-series通过指标名称(metrics name)和一组标签集(labelset)命名。在time-series中的每一个点称为一个样本(sample),样本由以下三部分组成:指标(metric):metric name和描述当前样本特征的labelsets;时间戳(timestamp):一个精确到毫秒的时间戳;样本值(value): 一个folat64的浮点型数据表示当前样本的值。如某一时刻的node_cpu指标为459.71node_cpu{app=“node-exporter”,cpu=“cpu0”,instance=“192.168.0.4:9100”,job=“kubernetes-service-endpoints”,kubernetes_name=“node-exporter”,kubernetes_namespace=“kube-system”,mode=“guest”} 459.71Prometheus定义了4中不同的指标类型(metric type):Counter 计数器计数器,只增不减,如http_requests_total请求总数例如,通过rate()函数获取HTTP请求量的增长率:rate(http_requests_total[5m])Gauge 仪表盘当前状态,可增可减。如kube_pod_status_ready当前pod可用数可以获取样本在一段时间返回内的变化情况,如:delta(kube_pod_status_ready[2h])Histogram 直方图Histogram 由 <basename>_bucket{le="<upper inclusive bound>"},<basename>_bucket{le="+Inf"}, <basename>_sum,<basename>_count 组成,主要用于表示一段时间范围内对数据进行采样(通常是请求持续时间或响应大小),并能够对其指定区间以及总数进行统计,通常它采集的数据展示为直方图。例如 Prometheus server 中 prometheus_local_storage_series_chunks_persisted, 表示 Prometheus 中每个时序需要存储的 chunks 数量,我们可以用它计算待持久化的数据的分位数。Summary 摘要Summary 和 Histogram 类似,由 <basename>{quantile="<>"},<basename>_sum,<basename>_count 组成,主要用于表示一段时间内数据采样结果(通常是请求持续时间或响应大小),它直接存储了 quantile 数据,而不是根据统计区间计算出来的。例如 Prometheus server 中 prometheus_target_interval_length_seconds。Histogram 需要通过 <basename>_bucket 计算 quantile, 而 Summary 直接存储了 quantile 的值。基础查询PromQL是Prometheus内置的数据查询语言,其提供对时间序列数据丰富的查询,聚合以及逻辑运算能力的支持。如http_requests_total指标你可以通过附加一组标签,并用{}括起来,来进一步筛选这些时间序列。下面这个例子只选择有http_requests_total名称的、有prometheus工作标签的、有canary组标签的时间序列:http_requests_total{job=“prometheus”,group=“canary”}如果条件为空,可以写为:http_requests_total{}另外,也可以也可以将标签值反向匹配,或者对正则表达式匹配标签值。如操作符: =:选择正好相等的字符串标签 !=:选择不相等的字符串标签 =~:选择匹配正则表达式的标签(或子标签) !=:选择不匹配正则表达式的标签(或子标签) 范围查询类似http_requests_total{job=“prometheus”,group=“canary”}的方式,得到的是瞬时值,如果想得到一定范围内的值,可以使用范围查询时间范围通过时间范围选择器[]进行定义。例如,通过以下表达式可以选择最近5分钟内的所有样本数据,如:http_request_total{}[5m]除了分钟,支持的单位有:s - 秒m - 分钟h - 小时d - 天w - 周y - 年偏移查询如:查询http_requests_total在当前时刻的一周的速率:rate(http_requests_total{} offset 1w)偏移修饰符允许更改查询中单个即时向量和范围向量的时间偏移量,例如,以下表达式返回相对于当前查询时间5分钟前的http_requests_total值:http_requests_total offset 5m等价于http_requests_total{job=“prometheus”}[5m]请注意,偏移量修饰符始终需要跟随选择器,即以下是正确的:sum(http_requests_total{method=“GET”} offset 5m) // GOOD.下面是错误的:sum(http_requests_total{method=“GET”}) offset 5m // INVALID.操作符Prometheus 的查询语言支持基本的逻辑运算和算术运算二元算术运算:加法减法乘法/ 除法% 模^ 幂等运算中用到的基础数据类型:瞬时向量(Instant vector) - 一组时间序列,每个时间序列包含单个样本,它们共享相同的时间戳。也就是说,表达式的返回值中只会包含该时间序列中的最新的一个样本值。而相应的这样的表达式称之为瞬时向量表达式。区间向量(Range vector) - 一组时间序列,每个时间序列包含一段时间范围内的样本数据。标量(Scalar) - 一个浮点型的数据值。字符串(String) - 一个简单的字符串值。二元运算操作符支持 scalar/scalar(标量/标量)、vector/scalar(向量/标量)、和 vector/vector(向量/向量) 之间的操作。在两个标量之间进行数学运算,得到的结果也是标量。例如,如果我们想根据 node_disk_bytes_written 和 node_disk_bytes_read 获取主机磁盘IO的总量,可以使用如下表达式:node_disk_bytes_written + node_disk_bytes_read或者node的内存数GBnode_memory_free_bytes_total / (1024 * 1024)布尔运算== (相等)!= (不相等)(大于)< (小于)= (大于等于)<= (小于等于)如:获取http_requests_total请求总数是否超过10000,返回0和1,1则报警http_requests_total > 10000 # 结果为 true 或 falsehttp_requests_total > bool 10000 # 结果为 1 或 0集合运算and (并且)or (或者)unless (排除)优先级四则运算有优先级,promql的复杂运算也有优先级例如,查询主机的CPU使用率,可以使用表达式:100 * (1 - avg (irate(node_cpu{mode=‘idle’}[5m])) by(job) )其中irate是PromQL中的内置函数,用于计算区间向量中时间序列每秒的即时增长率在PromQL操作符中优先级由高到低依次为:^, /, %+, -==, !=, <=, <, >=, >and, unlessor匹配模式(联合查询)与数据库中的join类似,promsql有两种典型的匹配查询:一对一(one-to-one)多对一(many-to-one)或一对多(one-to-many)例如当存在样本:method_code:http_errors:rate5m{method=“get”, code=“500”} 24method_code:http_errors:rate5m{method=“get”, code=“404”} 30method_code:http_errors:rate5m{method=“put”, code=“501”} 3method_code:http_errors:rate5m{method=“post”, code=“500”} 6method_code:http_errors:rate5m{method=“post”, code=“404”} 21method:http_requests:rate5m{method=“get”} 600method:http_requests:rate5m{method=“del”} 34method:http_requests:rate5m{method=“post”} 120使用 PromQL 表达式:method_code:http_errors:rate5m{code=“500”} / ignoring(code) method:http_requests:rate5m该表达式会返回在过去 5 分钟内,HTTP 请求状态码为 500 的在所有请求中的比例。如果没有使用 ignoring(code),操作符两边表达式返回的瞬时向量中将找不到任何一个标签完全相同的匹配项。因此结果如下:{method=“get”} 0.04 // 24 / 600{method=“post”} 0.05 // 6 / 120同时由于 method 为 put 和 del 的样本找不到匹配项,因此不会出现在结果当中。多对一模式例如,使用表达式:method_code:http_errors:rate5m / ignoring(code) group_left method:http_requests:rate5m该表达式中,左向量 method_code:http_errors:rate5m 包含两个标签 method 和 code。而右向量 method:http_requests:rate5m 中只包含一个标签 method,因此匹配时需要使用 ignoring 限定匹配的标签为 code。 在限定匹配标签后,右向量中的元素可能匹配到多个左向量中的元素 因此该表达式的匹配模式为多对一,需要使用 group 修饰符 group_left 指定左向量具有更好的基数。最终的运算结果如下:{method=“get”, code=“500”} 0.04 // 24 / 600{method=“get”, code=“404”} 0.05 // 30 / 600{method=“post”, code=“500”} 0.05 // 6 / 120{method=“post”, code=“404”} 0.175 // 21 / 120提醒:group 修饰符只能在比较和数学运算符中使用。在逻辑运算 and,unless 和 or 操作中默认与右向量中的所有元素进行匹配。聚合操作Prometheus 还提供了下列内置的聚合操作符,这些操作符作用域瞬时向量。可以将瞬时表达式返回的样本数据进行聚合,形成一个具有较少样本值的新的时间序列。sum (求和)min (最小值)max (最大值)avg (平均值)stddev (标准差)stdvar (标准差异)count (计数)count_values (对 value 进行计数)bottomk (样本值最小的 k 个元素)topk (样本值最大的k个元素)quantile (分布统计)这些操作符被用于聚合所有标签维度,或者通过 without 或者 by 子语句来保留不同的维度。without 用于从计算结果中移除列举的标签,而保留其它标签。by 则正好相反,结果向量中只保留列出的标签,其余标签则移除。通过 without 和 by 可以按照样本的问题对数据进行聚合。例如:如果指标 http_requests_total 的时间序列的标签集为 application, instance, 和 group,我们可以通过以下方式计算所有 instance 中每个 application 和 group 的请求总量:sum(http_requests_total) without (instance)等价于 sum(http_requests_total) by (application, group)如果只需要计算整个应用的 HTTP 请求总量,可以直接使用表达式:sum(http_requests_total)count_values 用于时间序列中每一个样本值出现的次数。count_values 会为每一个唯一的样本值输出一个时间序列,并且每一个时间序列包含一个额外的标签。这个标签的名字由聚合参数指定,同时这个标签值是唯一的样本值。例如要计算运行每个构建版本的二进制文件的数量:count_values(“version”, build_version)返回结果如下:{count=“641”} 1{count=“3226”} 2{count=“644”} 4topk 和 bottomk 则用于对样本值进行排序,返回当前样本值前 n 位,或者后 n 位的时间序列。获取 HTTP 请求数前 5 位的时序样本数据,可以使用表达式:topk(5, http_requests_total)quantile 用于计算当前样本数据值的分布情况 quantile(, express) ,其中 0 ≤ ≤ 1例如,当 为 0.5 时,即表示找到当前样本数据中的中位数:quantile(0.5, http_requests_total)返回结果如下:{} 656内置函数Prometheus 提供了其它大量的内置函数,可以对时序数据进行丰富的处理。如上文提到的irate100 * (1 - avg (irate(node_cpu{mode=‘idle’}[5m])) by(job) )常用的有:两分钟内的平均CPU使用率: rate(node_cpu[2m])和irate(node_cpu[2m])需要注意的是使用rate或者increase函数去计算样本的平均增长速率,容易陷入“长尾问题”当中,其无法反应在时间窗口内样本数据的突发变化。 例如,对于主机而言在2分钟的时间窗口内,可能在某一个由于访问量或者其它问题导致CPU占用100%的情况,但是通过计算在时间窗口内的平均增长率却无法反应出该问题。为了解决该问题,PromQL提供了另外一个灵敏度更高的函数irate(v range-vector)。irate同样用于计算区间向量的计算率,但是其反应出的是瞬时增长率。irate函数是通过区间向量中最后两个两本数据来计算区间向量的增长速率。这种方式可以避免在时间窗口范围内的“长尾问题”,并且体现出更好的灵敏度,通过irate函数绘制的图标能够更好的反应样本数据的瞬时变化状态。irate函数相比于rate函数提供了更高的灵敏度,不过当需要分析长期趋势或者在告警规则中,irate的这种灵敏度反而容易造成干扰。因此在长期趋势分析或者告警中更推荐使用rate函数。完整的函数列表为:abs()absent()ceil()changes()clamp_max()clamp_min()day_of_month()day_of_week()days_in_month()delta()deriv()exp()floor()histogram_quantile()holt_winters()hour()idelta()increase()irate()label_join()label_replace()ln()log2()log10()minute()month()predict_linear()rate()resets()round()scalar()sort()sort_desc()sqrt()time()timestamp()vector()year()<aggregation>_over_time()API访问Prometheus当前稳定的HTTP API可以通过/api/v1访问错误状态码:404 Bad Request:当参数错误或者缺失时。422 Unprocessable Entity 当表达式无法执行时。503 Service Unavailiable 当请求超时或者被中断时。所有的API请求均使用以下的JSON格式:{ “status”: “success” | “error”, “data”: <data>, // 为error时,有如下报错信息 “errorType”: “<string>”, “error”: “<string>"}通过HTTP API我们可以分别通过/api/v1/query和/api/v1/query_range查询PromQL表达式当前或者一定时间范围内的计算结果。瞬时数据查询URL请求参数:query=:PromQL表达式。time=:用于指定用于计算PromQL的时间戳。可选参数,默认情况下使用当前系统时间。timeout=:超时设置。可选参数,默认情况下使用-query,timeout的全局设置。$ curl ‘http://localhost:9090/api/v1/query?query=up&time=2015-07-01T20:10:51.781Z’返回:{ “status” : “success”, “data” : { “resultType” : “vector”, “result” : [ { “metric” : { “name” : “up”, “job” : “prometheus”, “instance” : “localhost:9090” }, “value”: [ 1435781451.781, “1” ] }, { “metric” : { “name” : “up”, “job” : “node”, “instance” : “localhost:9100” }, “value” : [ 1435781451.781, “0” ] } ] }}区间查询URL请求参数:query=: PromQL表达式。start=: 起始时间。end=: 结束时间。step=: 查询步长。timeout=: 超时设置。可选参数,默认情况下使用-query,timeout的全局设置。$ curl ‘http://localhost:9090/api/v1/query_range?query=up&start=2015-07-01T20:10:30.781Z&end=2015-07-01T20:11:00.781Z&step=15s’返回:{ “status” : “success”, “data” : { “resultType” : “matrix”, “result” : [ { “metric” : { “name” : “up”, “job” : “prometheus”, “instance” : “localhost:9090” }, “values” : [ [ 1435781430.781, “1” ], [ 1435781445.781, “1” ], [ 1435781460.781, “1” ] ] }, { “metric” : { “name” : “up”, “job” : “node”, “instance” : “localhost:9091” }, “values” : [ [ 1435781430.781, “0” ], [ 1435781445.781, “0” ], [ 1435781460.781, “1” ] ] } ] }}本文为容器监控实践系列文章,完整内容见:container-monitor-book ...

March 4, 2019 · 3 min · jiezi

容器监控实践—Prometheus基本架构

系统架构图1.x版本的Prometheus的架构图为:目前Prometheus版本为2.7,架构图为:Prometheus从exporter拉取数据,或者间接地通过网关gateway拉取数据(如果在k8s内部署,可以使用服务发现的方式),它默认本地存储抓取的所有数据,并通过一定规则进行清理和整理数据,并把得到的结果存储到新的时间序列中,采集到的数据有两个去向,一个是报警,另一个是可视化。PromQL和其他API可视化地展示收集的数据,并通过Alertmanager提供报警能力。组件内容Prometheus Server负责从 Exporter 拉取和存储监控数据,并提供一套灵活的查询语言(PromQL)Retrieval: 采样模块TSDB: 存储模块默认本地存储为tsdbHTTP Server: 提供http接口查询和面板,默认端口为9090Exporters/Jobs负责收集目标对象(host, container…)的性能数据,并通过 HTTP 接口供 Prometheus Server 获取。支持数据库、硬件、消息中间件、存储系统、http服务器、jmx等。只要符合接口格式,就可以被采集。Short-lived jobs瞬时任务的场景,无法通过pull方式拉取,需要使用push方式,与PushGateway搭配使用PushGateway可选组件,主要用于短期的 jobs。由于这类 jobs 存在时间较短,可能在 Prometheus 来 pull 之前就消失了。为此,这次 jobs 可以直接向 Prometheus server 端推送它们的 metrics。这种方式主要用于服务层面的 metrics,对于机器层面的 metrices,需要使用 node exporter。客户端sdk官方提供的客户端类库有go、java、scala、python、ruby,其他还有很多第三方开发的类库,支持nodejs、php、erlang等PromDash使用rails开发的dashboard,用于可视化指标数据,已废弃Alertmanager从 Prometheus server 端接收到 alerts 后,会进行去除重复数据,分组,并路由到对收的接受方式,发出报警。常见的接收方式有:电子邮件,pagerduty,OpsGenie, webhook 等。Service Discovery服务发现,Prometheus支持多种服务发现机制:文件,DNS,Consul,Kubernetes,OpenStack,EC2等等。基于服务发现的过程并不复杂,通过第三方提供的接口,Prometheus查询到需要监控的Target列表,然后轮训这些Target获取监控数据。其大概的工作流程是:Prometheus server 定期从配置好的 jobs 或者 exporters 中拉 metrics,或者接收来自 Pushgateway 发过来的 metrics,或者从其他的 Prometheus server 中拉 metrics。Prometheus server 在本地存储收集到的 metrics,并运行已定义好的 alert.rules,记录新的时间序列或者向 Alertmanager 推送警报。Alertmanager 根据配置文件,对接收到的警报进行处理,发出告警。在图形界面中,可视化采集数据。关于Push与PullPrometheus采集数据是用的pull也就是拉模型,通过HTTP协议去采集指标,只要应用系统能够提供HTTP接口就可以接入监控系统,相比于私有协议或二进制协议来说开发、简单。优点主要是:开发任何新功能,你甚至可以在电脑上查看你的监控如果目标实例挂掉,你可以很快知道你可以手动指定目标实例,并且在浏览器中查看他的健康状态总体来说,Pull模式比Push模式更好一些,在监控系统中这也不是一个很重要的点。如果要使用push的方式,可以使用Pushgateway的方式,如定时任务的采集。对于定时任务这种短周期的指标采集,如果采用pull模式,可能造成任务结束了,Prometheus还没有来得及采集,这个时候可以使用加一个中转层,客户端推数据到Push Gateway缓存一下,由Prometheus从push gateway pull指标过来。(需要额外搭建Push Gateway,同时需要新增job去从gateway采数据)推的代表有 ElasticSearch,InfluxDB,OpenTSDB 等,需要你从程序中将指标使用 TCP,UDP 等方式推送至相关监控应用,只是使用 TCP 的话,一旦监控应用挂掉或存在瓶颈,容易对应用本身产生影响,而使用 UDP 的话,虽然不用担心监控应用,但是容易丢数据。拉的代表,主要代表就是 Prometheus,让我们不用担心监控应用本身的状态。而且,可以利用 DNS-SRV 或者 Consul 等服务发现功能就可以自动添加监控。当然,InfluxDB 加上 collector,或者 ES 加上 metricbeat 也可以变为 『拉』,而 Prometheus 加上 Push Gateway 也可以变为 『推』。更多区别可以参考下图:存储机制Prometheus有着非常高效的时间序列数据存储方法,每个采样数据仅仅占用3.5byte左右空间,上百万条时间序列,30秒间隔,保留60天,大概花了200多G(引用官方PPT)。Prometheus内部主要分为三大块,Retrieval是负责定时去暴露的目标页面上去抓取采样指标数据,Storage是负责将采样数据写磁盘,PromQL是Prometheus提供的查询语言模块。Prometheus内置了一个基于本地存储的时间序列数据库。在Prometheus设计上,使用本地存储可以降低Prometheus部署和管理的复杂度同时减少高可用(HA)带来的复杂性。 在默认情况下,用户只需要部署多套Prometheus,采集相同的Targets即可实现基本的HA。同时由于Promethus高效的数据处理能力,单个Prometheus Server基本上能够应对大部分用户监控规模的需求。同时为了适应数据持久化的问题,Prometheus提供了remote_write和remote_read的特性,支持将数据存储到远端和从远端读取数据。通过将监控与数据分离,Prometheus能够更好地进行弹性扩展。关于存储用量规划:https://www.jianshu.com/p/934…更多:Prometheus存储机制详解https://yunlzheng.gitbook.io/…https://www.cnblogs.com/vovli...https://www.linuxidc.com/Linu...https://segmentfault.com/a/11...https://www.infoq.cn/article/…关于日志处理不建议将日志监控放在Prometheus中,这不是他的专长,还是使用ELK或EFK的方式处理日志信息竞品对比参考: https://toutiao.io/posts/fsjq…未来规划服务端度量指标元数据支持在度量指标类型和其他元数据仅仅在客户库和展示格式中使用,并不会在Prometheus服务中持久保留或者利用。将来我们计划充分利用这些元数据。第一步是在Prometheus服务的内存中聚合这些数据,并开放一些实验性的API来提供服务支持OpenMetricsOpenMetrics组开放了一个新的监控指标暴露标准,我们将支持这种标准:https://openmetrics.io/回溯时间序列允许将过去一段时间的数据发送到其他的监控系统HTTP服务支持TLS安全认证当前的Prometheus, Alertmanager和一些官方exporter,暴露服务时,都不支持tls认证,有很大的安全风险,现在的实现都是基于反向代理,之后将内置到组件中支持子查询当前的Promq不支持子查询,如max_over_time() of a rate()),后续将会支持支持生态建设Prometheus有很多的client库和exporter,我们将会对其进行规范和生态建设。在K8S中使用在之前的版本中,k8s默认以及推荐的监控体系是它自己的一套东西:Heapster + cAdvisor + Influxdb + Grafana,1.8后Heaspter由Metric-server替代。如果你部署了Dashboard,就能看到监控数据(来自heapster)k8s 自身的 HPA (Horizontal Pod Autoscaler),默认从 Heapster 中获取数据进行自动伸缩1.8版本以后,K8S希望将核心监控指标收拢到metric api的形式,而自定义监控指标,由prometheus来实现,prometheus正式成为k8s推荐的监控实现方案。参考文档:https://www.ibm.com/developer…https://prometheus.io/docs/in...https://www.kancloud.cn/cdh08...https://yunlzheng.gitbook.io/…本文为容器监控实践系列文章,完整内容见:container-monitor-book ...

March 4, 2019 · 1 min · jiezi

容器监控实践—Prometheus部署方案

一.单独部署二进制安装各版本下载地址:https://prometheus.io/download/Docker运行运行命令:docker run –name prometheus -d -p 127.0.0.1:9090:9090 prom/prometheus暴露服务: http://localhost:9090/二.在K8S中部署如果在Kubernetes中部署Prometheus,可以使用prometheus in kubernetes,含exporter、grafana等组件。安装方式:kubectl apply \ –filename https://raw.githubusercontent.com/giantswarm/kubernetes-prometheus/master/manifests-all.yaml卸载方式:kubectl delete namespace monitoring该方式为大多数用户和云厂商使用的方式,可以基于Prometheus的服务发现:在annotation中设置prometheus.io/scrape为true,就可以把K8S的所有服务都加入到监控中,但在使用的过程中会有一些问题:1.如果增加了新的exporter,如nginx-exporter,需要修改prometheus配置并重启2.服务本身和监控配置没有分离3.监控集群多实例的状态不好管理4.报警配置也包含在prometheus的配置中,监控与报警没有分离,添加规则麻烦以上问题一般的处理方式为:在prometheus上加一个控制台,来动态配置target、报警规则,并向后端server发起修改、重启操作。同时有权限控制、日志审计、整体配置过期时间等功能。但如果使用了Prometheus Operator,就可以将以上大多数操作抽象为k8s中的资源提交、修改,减少上层封装的工作量。三.Prometheus Operator部署Prometheus-Operator是一套为了方便整合prometheus和kubernetes的开源方案,使用Prometheus-Operator可以非常简单的在kubernetes集群中部署Prometheus服务,用户能够使用简单的声明性配置来配置和管理Prometheus实例,这些配置将响应、创建、配置和管理Prometheus监控实例。官方地址:https://github.com/coreos/pro…目前状态:beta状态,还不够完整,但向后兼容。将成为趋势前置条件:要求k8s的版本>=1.8.0(应该是因为metric api和CRD支持的限制)Operator的核心思想是将Prometheus的部署与它监控的对象的配置分离,做到部署与监控对象的配置分离之后,就可以轻松实现动态配置。使用Operator部署了Prometheus之后就可以不用再管Prometheus Server了,以后如果要添加监控对象或者添加告警规则,只需要编写对应的ServiceMonitor和Prometheus资源就可以,不用再重启Prometheus服务,Operator会动态的观察配置的改动,并将其生成为对应的prometheus配置文件其中Operator可以部署、管理Prometheus Service四种CRD作用如下:Prometheus: 由 Operator 依据一个自定义资源kind: Prometheus类型中,所描述的内容而部署的 Prometheus Server 集群,可以将这个自定义资源看作是一种特别用来管理Prometheus Server的StatefulSets资源。ServiceMonitor: 一个Kubernetes自定义资源(和kind: Prometheus一样是CRD),该资源描述了Prometheus Server的Target列表,Operator 会监听这个资源的变化来动态的更新Prometheus Server的Scrape targets并让prometheus server去reload配置(prometheus有对应reload的http接口/-/reload)。而该资源主要通过Selector来依据 Labels 选取对应的Service的endpoints,并让 Prometheus Server 通过 Service 进行拉取(拉)指标资料(也就是metrics信息),metrics信息要在http的url输出符合metrics格式的信息,ServiceMonitor也可以定义目标的metrics的url。Alertmanager:Prometheus Operator 不只是提供 Prometheus Server 管理与部署,也包含了 AlertManager,并且一样通过一个 kind: Alertmanager 自定义资源来描述信息,再由 Operator 依据描述内容部署 Alertmanager 集群。PrometheusRule:对于Prometheus而言,在原生的管理方式上,我们需要手动创建Prometheus的告警文件,并且通过在Prometheus配置中声明式的加载。而在Prometheus Operator模式中,告警规则也编程一个通过Kubernetes API 声明式创建的一个资源.告警规则创建成功后,通过在Prometheus中使用想servicemonitor那样用ruleSelector通过label匹配选择需要关联的PrometheusRule即可。安装方式:创建命名空间:monitoring执行yaml文件:https://github.com/coreos/pro…prometheus的target列表: grafana的自带监控图列表:常见问题:因为要operator中要支持聚合api,在某些版本的集群上可能需要一些配置,如下:安装cfssl证书生成工具:http://www.cnblogs.com/xuling…生成证书cfssl gencert -ca=/etc/kubernetes/pki/ca.pem -ca-key=/etc/kubernetes/pki/ca-key.pem -config=/etc/kubernetes/pki/ca-config.json -profile=jpaas metrics-server-csr.json | cfssljson -bare metrics-server{ “CN”: “aggregator”, “host”: [], “key”: { “algo”: “rsa”, “size”: 2048 }, “names”: [ { “C”: “CN”, “ST”: “BeiJing”, “L”: “BeiJing”, “O”: “k8s”, “OU”: “cloudnative” } ]}配置master组件参数,以支持metric-servervim /etc/systemd/system/kube-apiserver.service–requestheader-client-ca-file=/etc/kubernetes/pki/ca.pem --requestheader-allowed-names=“aggregator” --requestheader-extra-headers-prefix=“X-Remote-Extra-” --requestheader-group-headers=X-Remote-Group --requestheader-username-headers=X-Remote-User --proxy-client-cert-file=/etc/kubernetes/pki/metrics-server.pem --proxy-client-key-file=/etc/kubernetes/pki/metrics-server-key.pem --runtime-config=api/all=true --enable-aggregator-routing=true \systemctl daemon-reloadsystemctl restart kube-apiserver.servicesystemctl status kube-apiserver.servicevim /etc/systemd/system/kube-controller.service–horizontal-pod-autoscaler-use-rest-clients=truesystemctl daemon-reloadsystemctl restart kube-controller.servicesystemctl status kube-controller.service启动成功后,prometheus的target中,kubelet没有值,401报错vim /etc/systemd/system/kubelet.service–authentication-token-webhook=true–authorization-mode=Webhooksystemctl daemon-reloadsystemctl restart kubelet.service参考文档:https://github.com/coreos/pro…https://www.yinjk.cn/2018/09/...http://www.servicemesher.com/…本文为容器监控实践系列文章,完整内容见:container-monitor-book ...

March 4, 2019 · 1 min · jiezi

恭喜 containerd 毕业

今年的第一篇文章更新,带来一个重大的消息。CNCF(云原生计算基金会)在美国时间 2019 年 2 月 28 日宣布 containerd 今天正式毕业了。这是 CNCF 中毕业的第 5 个项目,之前已经毕业的项目为 Kubernetes、Prometheus、Envoy 和 CoreDNS 。containerd 2014 年从 Docker 孵化出来,最初是作为 Docker 引擎的底层管理器;在 2017 年 3 月被 CNCF 接受后,containerd 几乎成为了行业容器运行引擎的标准,它专注于简单,健壮和可移植性,任何人都可以使用它来构建自己的容器引擎/平台。“When Docker contributed containerd to the community, our goal was to share a robust and extensible runtime that millions of users and tens of thousands of organizations have already standardized on as part of Docker Engine,” said Michael Crosby, containerd maintainer and Docker engineer.截至目前,containerd 的 GitHub 项目有 3533 个 Star ;221 个 Watch 和 726 个 Fork,贡献者超过了 166 位。相信在之后也会发展的更好。下面附上 containerd 的架构图,以后会更新关于 containerd 相关原理的文章。再次恭喜 containerd 毕业。可以通过下面二维码订阅我的文章公众号【MoeLove】 ...

March 1, 2019 · 1 min · jiezi

CNCF宣布containerd毕业

阿里云、AWS、Cloud Foundry、Docker、Google、IBM、Rancher Labs以及更多支持促进生态系统最广泛采用的容器运行引擎加利福尼亚州旧金山,2018年2月28日 - 支持Kubernetes®和Prometheus™等开源技术的CNCF®(云原生计算基金会Cloud Native Computing Foundation®)今天宣布,在Kubernetes、Prometheus、Envoy和CoreDNS之后,containerd是第五个毕业项目。要从孵化的成熟水平到毕业,项目必须表现出蓬勃的采用、多样性、正式的治理过程,以及对社区永续性和包容性的坚定承诺。“在差不多两年前被CNCF接纳后,containerd继续看到显着的发展势头,展示了对基础容器技术的需求。”CNCF首席技术官Chris Aniszczyk说。“项目社区投放了大量的工作和协作,以稳定核心容器运行引擎的开发和测试,社区也努力扩大其维护者和采用基础,同时通过外部安全审计,所以我很激动看到项目毕业。”在2014年出生于Docker,containerd最初是Docker引擎的低层运行管理器。继2017年3月被CNCF接受之后,containerd已经成为行业标准的容器运行引擎,专注于简单、健壮和可移植,其最广泛的用途和采用是作为Docker引擎和OCI runc执行器的中间层。 “当Docker向社区贡献containerd的时候,我们的目标是分享一个强大且可扩展的运行引擎,这个引擎作为Docker Engine的一部分已经是数百万用户和成千上万的组织的标准。”containerd维护者和Docker工程师Michael Crosby说。“随着我们扩大范围以满足Docker平台和Kubernetes生态系统等现代化容器平台的需求,看到containerd在过去一年的采用和进一步的创新得到了回报。随着containerd的采用不断增长,我们期待整个生态系统继续合作推动我们的行业发展。”“IBM Cloud Kubernetes Service(IKS)致力于为我们的客户提供卓越的托管Kubernetes体验。为实现这一目标,我们一直在努力简化IKS的架构和运营状况。”IBM Cloud Kubernetes Service杰出工程师Dan Berg说。“迁移到containerd有助于简化我们替客户配置和管理的Kubernetes架构。通过采用containerd作为我们的容器引擎,我们减少了架构中的附加层,从而为我们的客户改善了运营,并提高了服务性能。”containerd自成立以来就拥有各种维护者和审阅者,目前有来自阿里巴巴、Cruise Automation、Docker、Facebook、Google、华为、IBM、微软、NTT、特斯拉等公司的14位提交者,4,406份提交和166位贡献者。可以在DevStats上找到containerd项目统计信息、贡献者统计信息等。“自成立以来,阿里巴巴一直使用containerd,我们很高兴看到该项目达到了这一里程碑。containerd作为容器运营引擎的开放、可靠和通用基础发挥着关键作用。在阿里云,我们的阿里云Kubernetes服务和无服务器Kubernetes利用了containerd的简单、稳健和可扩展性。”阿里云高级工程师Li Yi说。“阿里巴巴团队将继续致力于社区以推动创新。”为了正式从孵化状态毕业,该项目采用了CNCF行为准则,执行了独立的安全审计,并确定了自己的治理结构以发展社区。此外,containerd还必须获得(并维护)核心基础设施倡议(Core Infrastructure Initiative,CII)最佳实践徽章。CII徽章于2018年9月1日完成,显示了对代码质量和安全最佳实践的持续承诺。containerd背景containerd是行业标准的容器运行引擎,强调简单、健壮和可移植性。containerd可用作Linux和Windows的守护程序。containerd管理其主机系统的完整容器生命周期,从镜像传输和存储,到容器执行和监督,到低级存储,到网络附件等。有关下载、文档以及如何参与,请到https://github.com/containerd…。KubeCon + CloudNativeCon和Open Source Summit大会日期:会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日KubeCon + CloudNativeCon和Open Source Summit赞助方案KubeCon + CloudNativeCon和Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国KubeCon + CloudNativeCon和Open Source Summit购票窗口,立即购票!

March 1, 2019 · 1 min · jiezi

使用Logtail采集Kubernetes上挂载的NAS日志

采集k8s挂载Nas后的日志该文档主要介绍使用logtail以两种不同的方式进行k8s挂载Nas后的日志采集。两种采集方式的实现原理是一样的,都是通过将Logtail和业务容器挂载到相同的NAS上,使Logtail和业务容器的日志数据共享,以此实现日志采集。下面是两种采集方式的各自特点:1. SideCar模式。比较灵活、适合水平扩容,适用于数据量较大的场景;2. 单独部署Logtail的Deployment。资源消耗比较低、但灵活性以及伸缩性不强,适用于整体集群数据量较少的场景(建议整体日志量不超过每秒10M)。1. Sidecar NAS采集方式通过 链接 使用PV&PVC的方式配置挂载Nas的nas-pvc步骤一 创建pv步骤二 创建pvc步骤三 根据下面的yaml模板创建含有logtail的Pod,进行单个Pod的内部采集sideCar模式实验yaml内容:apiVersion: batch/v1kind: Jobmetadata: name: nginx-log-sidecar1-demospec: template: metadata: name: nginx-log-sidecar-demo spec: # volumes配置 volumes: - name: nginx-log persistentVolumeClaim: claimName: nas-pvc containers: # 主容器配置 - name: nginx-log-demo image: registry.cn-hangzhou.aliyuncs.com/log-service/docker-log-test:latest command: ["/bin/mock_log"] args: ["–log-type=nginx", “–stdout=false”, “–stderr=true”, “–path=/var/log/nginx/access.log”, “–total-count=1000000000”, “–logs-per-sec=100”] volumeMounts: - name: nginx-log mountPath: /var/log/nginx # Logtail的Sidecar容器配置 - name: logtail image: registry.cn-hangzhou.aliyuncs.com/log-service/logtail:latest env: # user id - name: “ALIYUN_LOGTAIL_USER_ID” value: “${your_aliyun_user_id}” # user defined id - name: “ALIYUN_LOGTAIL_USER_DEFINED_ID” value: “${your_machine_group_user_defined_id}” # config file path in logtail’s container - name: “ALIYUN_LOGTAIL_CONFIG” value: “/etc/ilogtail/conf/${your_region_config}/ilogtail_config.json” # env tags config - name: “ALIYUN_LOG_ENV_TAGS” value: “pod_name|pod_ip|namespace|node_name|node_ip” - name: “pod_name” valueFrom: fieldRef: fieldPath: metadata.name - name: “pod_ip” valueFrom: fieldRef: fieldPath: status.podIP - name: “namespace” valueFrom: fieldRef: fieldPath: metadata.namespace - name: “node_name” valueFrom: fieldRef: fieldPath: spec.nodeName - name: “node_ip” valueFrom: fieldRef: fieldPath: status.hostIP # 和主容器共享volume volumeMounts: - name: nginx-log mountPath: /var/log/nginx # 健康检查 livenessProbe: exec: command: - /etc/init.d/ilogtaild - status initialDelaySeconds: 30 periodSeconds: 30 restartPolicy: “Never"SLS控制台采集配置设置如下图:日志路径与被采集容器的日志所在路径一致注意:由于NAS路径已经挂载到了Logtail容器上,所以不需要打开docker文件的按钮采集上来的系统默认字段含义:source: pod容器内部IP__tag__:hostname: pod名称__tag__:path: 日志路径__tag__:receive_time: 采集时间__tag__:user_defined_id: 用户自定义标识__tag__:namespace: pod所属namaspace__tag__:node_ip: pod所在Node的IP地址__tag__:node_name: pod所属Node的name__tag__:pod_ip: pod容器内部IP__tag__:pod_name: pod名称用户参数:2. 一个Logtail采集所有POD的NAS数据注意项:副本数spec.replicas只能为1,不能更多,多了会重复采集。首先,创建一个logtail的deployment,以下是本次使用的模板:apiVersion: apps/v1kind: Deploymentmetadata: name: logtail-deployment namespace: kube-system labels: k8s-app: nas-logtail-collecterspec: replicas: 1 selector: matchLabels: k8s-app : nas-logtail-collecter template: metadata: name: logtail-deployment labels: k8s-app : nas-logtail-collecter spec: containers: # Logtail的配置 - name: logtail image: registry.cn-hangzhou.aliyuncs.com/log-service/logtail:latest env: # aliuid - name: “ALIYUN_LOGTAIL_USER_ID” value: “${your_aliyun_user_id}” # user defined id - name: “ALIYUN_LOGTAIL_USER_DEFINED_ID” value: “${your_machine_group_user_defined_id}” # config file path in logtail’s container - name: “ALIYUN_LOGTAIL_CONFIG” value: “/etc/ilogtail/conf/${your_region_config}/ilogtail_config.json” volumeMounts: - name: nginx-log mountPath: /var/log/nginx # volumes配置 volumes: - name: nginx-log persistentVolumeClaim: claimName: pvc-test-nginx__注意:__这里的 claimName: pvc-test-nginx 以及mountPath: /var/log/nginx 是将logtail的/var/log/nginx挂载了Nas下的/nginx文件夹相关参数设置请参考方案1中的表格说明logtail运行成功之后,可以在SLS控制台根据模板中的ALIYUN_LOGTAIL_USER_DEFINED_ID创建对应的机器组,请参考方案1中的表格说明。这里新建2个Pod来测试采集是否成功,其中一个POD的模板为:apiVersion: v1kind: Podmetadata: name: “test-nginx-2"spec: containers: - name: “nginx-log-demo” image: “registry.cn-hangzhou.aliyuncs.com/log-service/docker-log-test:latest” command: ["/bin/mock_log”] args: [”–log-type=nginx", “–stdout=false”, “–stderr=true”, “–path=/var/log/nginx/access.log”, “–total-count=1000000000”, “–logs-per-sec=100”] volumeMounts: - name: “nas2” mountPath: “/var/log/nginx” volumes: - name: “nas2” flexVolume: driver: “alicloud/nas” options: server: “Nas挂载地址” path: “/nginx/test2” vers: “4.0"另一个Pod将 /var/log/nginx 挂载在了 /nginx/test1 目录下;结合logtail的挂载情况,现在两个Pod分别挂载在 /nginx/test1 和 /nginx/test2,而logtail挂载在了 /nginx 下。最后配置logtail的采集配置因为logtail也挂载了相同的Nas,所以logtail只需要采集自身文件夹下的日志就可以了,这里的是否为docker文件选项关闭。注意:由于NAS路径已经挂载到了Logtail容器上,所以不需要打开docker文件的按钮本文作者:元乙阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 27, 2019 · 2 min · jiezi

基于 Kubernetes 实践弹性的 CI/CD 系统

大家好,我是来自阿里云容器服务团队的华相。首先简单解释一下何为 Kubernetes 来帮助大家理解。Kuberentes 是一个生产可用的容器编排系统。Kuberentes 一方面在集群中把所有 Node 资源做一个资源池,然后它调度的单元是 Pod,当然 Pod 里面可以有多个容器。 就像一个人左手抓着 ECS 资源或计算资源,右手抓容器,然后把它们两个匹配起来,这样它就可以作为一个容器的编排系统。而 Cloudnative 这个概念现在会经常被大家提起,很多人迷惑 Cloudnative 又与 Kuberentes 有什么关联?我们又该如何判断一个应用是 Cloudnative 呢?我认为有以下三个判断标准:第一,它能够给资源做池化;第二,应用可以快速接入池的网络。在 Kuberentes 里面有一层自己的独立网络,然后只需要知道我要去访问哪个服务名就可以,就是各种服务发现的一些功能,它可以通过 service mesh 去做一个快速地访问;第三是有故障转移功能,如果一个池子里面有一台主机,或者某一个节点 down 掉了,然后整个应用就不可用了,这肯定不算是 Cloudnative 的应用。比较这三点就可以发现 Kuberentes 做的非常好。首先我们看一个资源池的概念,Kuberentes 一个大的集群就是一个资源池,我们再也不用去关心说我这个应用要跑在哪台主机上了,我只要把我们部署的 yaml 文件往 Kuberentes 上发布就可以了,它会自动做这些调度,并且它可以快速地接入我们整个应用的网络,然后故障转移也是自动。接下来我就来分享如何基于 Kuberentes 实现一个弹性的 CI/CD 系统。CI/CD 的现状首先了解一下 CI/CD 的现状。CI/CD 这个概念实际上已经提出很多年了,但是随着技术地演进和新工具地不断推出,它在整个流程和实现方式上逐渐丰富。而我们通常最早接触 CI/CD 就是代码提交,随之触发一个事件,然后在某个 CI/CD 系统上做自动构建。下图可以反映目前 CI/CD 的现状:另外还有 Gitlab CI,它主要特点是与 Gitlab 代码管理工具结合地比较好。Jenkins 2.0 开始引入 pipeline as code 特性,pipeline as code 可以帮我们自动生成一个 jenkins file。在 Jenkins 1.0 时,如果我们想要配置一条流水线,需要先登录 Jenkins 建一个项目,然后在里面写一些 shell。这样虽然能达到同样的效果,但是它有一个最大的弊端,就是可复制性和迁移性不好。而且它与 Devops 有天然地割裂,比如一般是由运维人员来管理 Jenkins 系统,开发人员编写代码,但是这个代码怎么去构建,发布到哪里,开发人员完全不知道。这就造成了开发和运维地割裂。但 pipeline as code 方式出现了之后,jenkins file 与代码源码可以放在同样的仓库里面。首先它有一个非常大的好处是发布的流程也可以纳入版本管理,这样对一个错误就可追溯。这是一个非常大地改动,但是实际上我们在与客户沟通中发现,虽然很多人在 Jenkins 上都已经升到 2.0 系列了,但是它们的用法还是完全在 1.0 系列,很多用户都没有把 jenkins file 这种方式用起来。另外一个就是对容器地支持,大概 2016 年左右,那时对容器的支持是非常弱的,在容器里面运行 Jenkins,同时构建的产物也是 Docker 会非常麻烦。 但是, Drone 对容器的支持力度就非常好。首先它完全用 Docker 方式来运行,就是说你的构建环境也在一个容器里,你需要构建一个 Docker build 镜像,然后在推送出去的时候,它也在容器里面运行,然后它需要一个 privilege 权限。它这种方式有几个特别好的地方,首先它不会对宿主机产生任何残留,比如说你这个容器一销毁,构建中产生的一些中间文件完全都跟着销毁了,但是你如果用 Jenkins 的话,随着用的时间越来越久会沉淀出很多临时文件,它占的空间会越来越大。你需要定期做一些清理,而且清理过程中你又不能直接一键清空,所以这是很麻烦的过程。然后插件的管理 Jenkins 还有一个特别让人头疼的地方,就是它的插件升级。首先你在 Jenkins 登录进去,然后就做插件升级。如果说我想临时在一个新的环境里面,起一个 Jenkins 先测试一下或做一些调试可能每新建一个环境,都需要把这些插件升级一次。而且刚才我们说的在 Jenkins 里面做的所有配置,也都需要重新配置一遍,这是一个非常繁琐的一个过程。但是 Drone 这个工具它有一个特别好的地方,就是所有的插件都是 Docker 容器,比如说你在 pipeline 里用这个插件,你只要声明用这个插件就可以了,你不用去自己管理把插件下载到哪里,然后怎么安装,它这个一切都是全自动,只要说你网络能把插件容器镜像访问到就可以了,这非常便利。然后关于生态的构建,Jenkins 的最大的优势就是它的插件非常多,就是你想用的各种东西都有,而且它基础的底座非常好,你的插件可以实现的能力非常的强。比如说 pipeline 就是这种方式,它从 1.0 到 2.0 虽然有了,但是它完全是通过插件来实现的。但是现在 Jenkins 的发展又开始有点第二春的感觉。他开始对 Kuberentes 的支持力度明显的加大了很多,首先从 JenkinsX 开始,它融合了一些 Kuberentes 生态相关的一些工具,比如 Harbor、Helm 它可以非常便利地在 Kuberentes 集群上来做一些构建,并且把一些做服务的固化的一些编排文件放到 Helm 里面。另外,现在它有一个新的子项目叫 config as code,也就是说他把所有 Jenkin 里面做了一些配置,都可以输出成一个 code 的形式,就是对整个 Jenkins 的迁移,或者说复制都是一个很便利的改进。讲了那么多,实际上最后我们选择的东西还是 Jenkins,因为最重要的是生态的构建,他们已经很好了。今天我们要讲的在做一个弹性在 CI/CD 这个 Jenkins 上已经有这个插件了,但是在 Drone 的社区里面,有人提这个事,但是现在还没有看到。CI/CD 工具的选择接下来,我们看一下 CI/CD 这些工具的选择和他们的发展。首先最老牌的肯定是 Jenkins。实际上在容器技术兴起之前,CI/CD 简直约等于 Jenkins。但是在出现容器技术之后,很多新生 CI/CD 的工具也应运而生,比如说图中 Drone 工具,它是一个完全基于容器来实现的 CI/CD 工具。它与容器地结合地非常好,并且它的构建过程是完全在容器中实现的。第三个是 Gitlab CI,它主要特点是与 Gitlab 代码管理工具结合地比较好。Jenkins 2.0 时开始引入 pipeline as code 特性,什么叫 pipeline as code?pipeline as code 可以帮我们自动生成一个 jenkins file。在 Jenkins 1.0 时,如果我们想要配置一条流水线,需要先登录 Jenkins,然后建一个项目,然后在里面写一些 shell。这样虽然能达到同样的效果,但是它有一个最大的弊端,就是可复制性和迁移性不好。而且它与 Devops 有天然地割裂,比如一般是运维人员来管理 Jenkins 这个系统。开发人员编写代码,但是这个代码怎么去构建,发布到哪里,他是完全不知道的,是运维人员在Jenkins 里面配的。这个就造成了开发和运维地割裂。但 pipeline as code 这种方式出现了之后,我们可以把 jenkins file 跟代码源码放在同样的仓库里面。首先它有一个非常大的好处就是我们发布的流程也可以纳入版本管理,这样对一个错误就可追溯。这是一个非常大地改动,但是实际上我们在与客户沟通中发现,虽然很多人在 Jenkins 上都已经升到 2.0 系列了,但是它们的用法还是完全在 1.0 系列,很多用户都没有把 jenkins file 这种方式用起来。另外一个就是对容器地支持,大概 2016 年左右,那时对容器的支持是非常弱的,在容器里面运行 Jenkins,同时构建的产物也是 Doker 会非常麻烦。 但是, Drone 对容器的支持力度就非常好。首先它完全用 Doker 方式来运行,就是说你的构建环境也在一个容器里,你需要构建一个 Doker build 镜像,然后在推送出去的时候,它也在容器里面运行,然后它需要一个 privilege 权限。它这种方式有几个特别好的地方,首先它不会对宿主机产生任何残留,比如说你这个容器一销毁,构建中产生的一些中间文件完全都跟着销毁了,但是你如果用 Jenkins 的话,随着用的时间越来越久会沉淀出很多临时文件,它占的空间会越来越大。你需要定期做一些清理,而且清理过程中你又不能直接一键清空,所以这是很麻烦的过程。然后插件的管理 Jenkins 还有一个特别让人头疼的地方,就是它的插件升级。首先你在 Jenkins 登录进去,然后就做插件升级。如果说我想临时在一个新的环境里面,起一个 Jenkins 先测试一下或做一些调试可能每新建一个环境,都需要把这些插件升级一次。而且刚才我们说的在 Jenkins 里面做的所有配置,也都需要重新配置一遍,这是一个非常繁琐的一个过程。但是 Drone 这个工具它有一个特别好的地方,就是所有的插件都是 Doker 容器,比如说你在 pipeline 里用这个插件,你只要声明用这个插件就可以了,你不用去自己管理把插件下载到哪里,然后怎么安装,它这个一切都是全自动,只要说你网络能把插件容器镜像访问到就可以了,这非常便利。然后关于生态的构建,Jenkins 的最大的优势就是它的插件非常多,就是你想用的各种东西都有,而且它基础的底座非常好,你的插件可以实现的能力非常的强。比如说 pipeline 就是这种方式,它从 1.0 到 2.0 虽然有了,但是它完全是通过插件来实现的。但是现在 Jenkins 的发展又开始有点第二春的感觉。他开始对 Kuberentes 的支持力度明显的加大了很多,首先从 JenkinsX 开始,它融合了一些 Kuberentes 生态相关的一些工具,比如 Harbor、Helm 它可以非常便利地在 Kuberentes 集群上来做一些构建,并且把一些做服务的固化的一些编排文件放到 Helm 里面。另外,现在它有一个新的子项目叫 config as code,也就是说他把所有 Jenkin 里面做了一些配置,都可以输出成一个 code 的形式,就是对整个 Jenkins 的迁移,或者说复制都是一个很便利的改进。讲了那么多,实际上最后我们选择的东西还是 Jenkins,因为最重要的是生态的构建,他们已经很好了。今天我们要讲的在做一个弹性在 CI/CD 这个 Jenkins 上已经有这个插件了,但是在 Drone 的社区里面,有人提这个事,但是现在还没有看到。CI/CD的系统业务场景然后我们看一下 CI/CD 它的系统的业务场景,它有一个比较典型的场景与特点,首先它面向开发人员,这是比较少见的,因为开发人员一般都比较挑剔一点。所以你要是这个系统做的不够稳健了,或者说响应时间比较长一点的话,会被经常吐槽。然后就是有时效性要求,因为我们代码写完之后,往上提交,我们都不希望在这个代码的构建中一直排队,我们希望马上就开始进行构建,并且资源足够丰富。另外一个就是它的资源占用的波峰波谷是非常明显的。就因为开发人员不可能时时刻刻都在提交代码,有的人可能一天提交几次,有的人会提交很多次。因为我之前看过有一个分享,有一个人画了一条反映自家公司构建任务的曲线。他们公司大概是每天下午三、四点的时候代码提交量最高,其他时间都比较平缓。这说明他们公司三、四点的时候,程序员提交代码开始划水了。然后随着 CI/CD 资源地需求越来越高,构建集群是一个必须要做的一件事情。就是提高负载能力,缩短任务的排队时间。当然真正的集群有一个让人觉得很不太好的地方,就是它的 Master 实际上只有一个,当然这个也可以通过插件来做改进。容器可以给我们 CI/CD 系统来注入新的能力,就是环境隔离的能力。我们可以通过 Kubernetes 来为 CI/CD 系统注入更多的能力,然后矛盾点就出现了。开发人员总是希望 CI/CD 系统能够快速地响应代码提交的一个事件,但是每个公司资源都不可能是无限的。因为就像上面提到的,如果每天下午三、四点的时候是一个代码提交的高峰,这个时候可能需要 30 或 40 台机器才能满足构建的任务。但是我不可能每天就开着 30 或 40 台机器在这里,就为了每天下午三、四点,可能会构建一、两个小时。Kubernetes 可以为 jenkins 注入新的能力,让 CI/CD 系统实现弹性的能力。我们期望的目标是什么呢?有构建任务的时候,可以自动为我们资源增加新的机器也好,或增加新的运计算能力也好,反正就是当我需要的时候,可以帮我自动地做一个资源扩张,但是同时也在我不需要的时候,可以自动把这些资源释放掉。我们期望的目标就是这样,Kuberentes 就可以为 Jenkins 来做这样的能力。Kuberentes 作为一个容器编排的系统,它所能提供的能力,它可以快速地弹出一些新的实例,并且把它们自动调度到空闲的机器上,做一个资源池,在资源池里面做一个调度,并且他执行完任务之后,它可以做一个回收。而且如果把这 Jenkins Master 也部署在 Kuberentes 之上,它可以对 Master 做一个故障转移,就是说如果我们系统可以容忍的话,Master 就算挂了,我可以快速把它调到另外一台机器上,这个响应时间不会很长。Kuberentes-plugin这里面也是用一个插件来实现,这个插件名字比较直接叫 Kuberentes-plugin,它这个插件所能提供的能力就是说,他直接管理一个 Kuberentes 集群,它在 Jenkins 里面安装之后,它可以监听 Jenkins 的构建任务。有构建任务,在等待资源的时候,它就可以向 Kuberenetes 去申请一个新的资源,申请一个新的 Pod 去做自动地构建完之后,它就会自动的清理。先简单介绍一下它的能力,因为这个插件安装完之后,它对 pipeline 的语法也有一个改造,一会我们来看一下实例。但是就算到了这一步,还是不行的。首先,Kuberentes 的集群规划还是一个问题。比说我有个集群有 30 个节点,真正的 master 部署在这上面,然后装了那些插件,做了一个管理之后,我们可以发现来了一个新的任务,它就起一个新的 Pod,把这个构建任务给执行制定完。执行完之后,这 Pod 自动销毁不占集群的资源。 平时我们可以在这集群上做一些别的任务,但这个终究还是有一点不好,就是我们这个集群到底规划多大,并且这个集群我们平时不做构建任务的时候,可以在上面做一些别的任务。但是如果正在做任务,突然来了一些构建任务,它可能会出现资源的冲突问题。Kubernetes Autoscaler总的来说,还是有一些不完美的地方,那么我们可以利用 Kuberentes 一些比较没那么常见的特性,来解决我们刚才说的这个问题。这两个东西一个是叫 Autoscaler,一个叫 Virtual node。我们先看一下 Autoscaler,Autoscaler 是一个 Kubernetes 官方的一个组件。在 Kuberentes 的 group 下面支持三种能力:Cluster Autoscaler,可以对集群节点做自动伸缩;Vertical Pod Autoscaler,对集群的 Pod 竖直方向的资源伸缩。因为 Kuberentes 本身里面就带着 HPA 可以做水平方向的 Pod 伸缩、节点数量的伸缩;这个特性还不是生产可用的特性;Addone Resizer,是 Kuberentes 上那些 addone 比如说 Ingress Controler、 DNS 可以根据 Node 的数量来对资源的分配做调整。Cluster autoscaler我要讲的是 Cluster autoscaler,是对集群 node 节点数量做一个扩缩容。 首先我们看一下,这个是在阿里云我们容器服务上所实现的 Autoscaler 的一个方式。我们看一下这个图,这个是 HPA 和 Autoscler 做结合使用的一个场景。HPA 监听监控的事件时发现资源使用率上升到一定程度了之后,HPA 会自动通知 workload,来弹出一个新的 Pod,弹出一个新的 Pod,可能这时候集群资源就已经不够了,所以这个 Pod 可能就会 pending 在这里。它就会触发 Autoscaler 的事件,Autoscaler 就会根据我们之前配置好的 ESS 这个模板来去弹出来一台新的 Node,然后自动地把 Node 加入到我们集群里面。 它是利用了 ESS 定制的模板功能,并且它可以支持多种的 Node 实例的类型,可以支持普通实例,支持 gpu,还有抢占式实例。Virtual node然后第二种是 Virtual node,Virtual node 实现方式是基于微软开源的 Virtual Kubelet 这个项目。它做了一个虚拟的 Kubelet,然后向 Kubernetes 集群里面去注册上面。但如果不太好理解的话,可以想象一下 MySQL proxy ,然后他把自己伪装成一个MySQL server,然后后端可能管理着非常多的 MySQL server,并且它可以帮你自动的做一些 SQL 查询的路由或者拼接。Virtual kubelet 做的也是类似的工作,就是说它本身向着 Kubernetes 注册说我是一个节点,但实际上它后端管理的可能是整个公有云上的非常多的资源,他可能对接公有云的一些 ECI 或者说对接的这些 VPC,这是一个它的大体的示意图。在阿里云上他们对接的是阿里云的 ECI 做一个弹性的容器实例,它响应时间就非常快,因为它不需要把 Node 去加入到集群里面,它是大概能够到一分钟一百个 Pod 左右这种性能。而且我们可以在 Pod 上声明这种资源的使用情况,这是一个非常快的响应速度时间。然后刚才说我们利用这两种方式,就可以对我们 CI/CD 弹性的系统做出新的改造,我们不用非常早规划好我们集群的规模,我们可以让集群规模在需要的时候自动的做一些伸缩的动作。但是你做了这些动作之后,我们做了这些把真正的放在容器里面的这些动作之后,引入了一些新的门槛:docker-outside-of-docker 和 docker in docker。我们在 Docker 中运行 Jenkins 时,通常有两种方式,一个是把宿主机的 docker.sock 挂载到容器里面,让 Jenkins 通过这个文件来和本机的 docker daemon 做一个通信,然后它在这里面做 docker build 构建镜像,或者把这些镜像 push 到远程的仓库里面,所以它中间产生的所有镜像都会堆积在本机上,这是一个问题。在一些 serverless 的那些场景上使用,它就会有一些限制,因为 serverless 本身就不允许把 socket 文件给挂在进去。另外一个就是 docker in docker 这种方式,它的优点就在于在容器里面它启动一个新的 Docker daemon,它所有的中间产物、构建产物随着容器都一起销毁,但是它有问题,它就是需要 privilege 的权限。很多时候我们是尽量不要用它。另外一个就是说你做 docker build 的时候能在宿主机上做的时候,它如果有已经有镜像了,它会直接就使用这个镜像,但是你用 docker in docker 这种方式来使用的,它每次都会重新拉进项,拉镜像也是需要一定时间,这个取决于我们各个使用场景来判断。新的构建工具——Kaniko这时又引入了一个谷歌开源的新工具——Kaniko。它做的东西是 docker in docker 的方式。它有一个非常大的好处就是不依赖 Docker,而且所以它不需要 privilege 权限就可以在容器里面用用户态的模式,来完全构建 docker image。用户态执行 Dockerfile 的命令,它把这个镜像完全构建出来。这算是一个比较期望的弹性的 CI/CD 系统。然后这个时候就是说从真正的节点到底层的计算资源全部是弹性扩缩的,而且满足交付的需求,可以非常精细化地管理我们的资源。Demo 演示然后我们可以看一下 Demo 演示:https://github.com/hymian/webdemo 这里是我准备好的一个例子,重点在这个 Jenkinsfile 文件,里面定义了agent 的 pod template,包含两个容器,一个用来做 golang 的 build,一个用来做 image 的 build。然后我们现在构建它。开始构建了,刚开始的,因为是现在我们在这环境里面只有一个,只有一个 master,所以他就是没有不会有构建节点。大家可以看到,它现在新启动了一个 Pod,这个 Pod 是作为节点加进来的,但是因为我在这个 Pod 模板里面定义了一个 label,所以它没有这个节点,所以它 Pod 状态是 pending 的。所以我们在构建日志里面显示的这个是 agent 节点是离线的。 但是我们在这个集群里面定义了一个弹性伸缩的一个东西,当没有节点的时候,它会自动做一个新节点分配加入,可以看到有一个节点正在加入,这个我就可以稍等一下。就是说这段时间可能会有个一分钟两分钟的时间。这个是异常,是因为这个节点正在向集群加入,所以它显示是异常,这是我们从命令行看一下,好,已经是四个节点了,加了一个节点,这时候我们看 Pod,这时候在 agent 正在创建,这时候大家可能有一个小的细节,大家可以看一下,就是 0/3 是显示 Pod,它有三个容器,但是我刚才在这个里面定义的,它实际上是 Pod 里面只有两个容器,这就是我们刚才 PPT 上写的一个地方。 JNLP 那个容器,是 plugin 自动注入的一个容器,它通过这个容器实时的向 master 汇报构建的一个中间的状态,我把它的日志给发送出去。这个是 agent 的节点在初始化的一个过程一个事情这时候 slave节点已经在运行了。我这边已经输出完了,构建完成。我的分享内容就这些,谢谢大家。本文作者:jessie筱姜阅读原文本文为云栖社区原创内容,未经允许不得转载。 ...

February 20, 2019 · 3 min · jiezi

Golang项目部署

文章来源:https://goframe.org/deploymen…一、独立部署使用GF开发的应用程序可以独立地部署到服务器上,设置为后台守护进程运行即可。这种模式常用在简单的API服务项目中。服务器我们推荐使用*nix服务器系列(包括:Linux, MacOS, BSD),以下使用Ubuntu系统为例,介绍如何部署使用GF框架开发的项目。1. nohup我们可以使用简单的nohup命令来运行应用程序,使其作为后台守护进程运行,即使远程连接的SSH断开也不会影响程序的执行。在流行的Linux发行版中往往都默认安装好了nohup命令工具。命令如下:nohup ./gf-app &2. tmuxtmux是一款Linux下的终端复用工具,可以开启不同的终端窗口来将应用程序作为后台守护进程执行,即使远程连接的SSH断开也不会影响程序的执行。在ubuntu系统下直接使用sudo apt-get install tmux安装即可。使用以下步骤将应用程序后台运行。tmux new -s gf-app;在新终端窗口中执行./gf-app即可;使用crt + B & D快捷键可以退出当前终端窗口;使用tmux attach -t gf-app可进入到之前的终端窗口;3. supervisorsupervisor是用Python开发的一套通用的进程管理程序,能将一个普通的命令行进程变为后台daemon,并监控进程状态,异常退出时能自动重启。官方网站:http://supervisord.org/常见配置如下:[program:gf-app]user=rootcommand=/var/www/mainstdout_logfile=/var/log/gf-app-stdout.logstderr_logfile=/var/log/gf-app-stderr.logautostart=trueautorestart=true使用步骤如下:使用sudo service supervisor start启动supervisor服务;创建应用配置文件/etc/supervisor/conf.d/gf-app.conf, 内容如上;使用sudo supervisorctl进入supervisor管理终端;使用reload重新读取配置文件并重启当前supoervisor管理的所有进程;也可以使用update重新加载配置(默认不重启),随后使用start gf-app启动指定的应用程序;随后可以使用status指令查看当前supervisor管理的进程状态;二、代理部署代理部署即前置一层第三方的WebServer服务器处理所有的请求,将部分请求(往往是动态处理请求)有选择性地转交给后端的Golang应用程序执行,后端部署的Golang应用程序可以配置有多个。这种模式常用在复杂的WebServer配置中,常见的场景例如:需要静态文件分离、需要配置多个域名及证书、需要自建负载均衡层,等等。虽然Golang实现的WebServer也能够处理静态文件,但是相比较于专业性的WebServer如nginx/apache来说比较简单,性能也较弱。因此不推荐使用Golang WebServer作为前端服务直接处理静态文件请求。Nginx我们推荐使用Nginx作为反向代理的前端接入层,有两种配置方式实现动静态请求的拆分。静态文件后缀这种方式通过文件名后缀区分,将指定的静态文件转交给nginx处理,其他的请求转交给golang应用。配置示例如下:server { listen 80; server_name goframe.org; access_log /var/log/gf-app-access.log; error_log /var/log/gf-app-error.log; location ~ ..(gif|jpg|jpeg|png|js|css|eot|ttf|woff|svg|otf)$ { access_log off; expires 1d; root /var/www/gf-app/public; try_files $uri @backend; } location / { try_files $uri @backend; } location @backend { proxy_pass http://127.0.0.1:8199; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }}其中,8199为本地的golang应用WebServer监听端口。例如,在该配置下,我们可以通过http://goframe.org/my.png访问到指定的静态文件。静态文件目录这种方式通过文件目录区分,将指定目录的访问请求转交给nginx处理,其他的请求转交给golang应用。配置示例如下:server { listen 80; server_name goframe.org; access_log /var/log/gf-app-access.log; error_log /var/log/gf-app-error.log; location ^~ /public { access_log off; expires 1d; root /var/www/gf-app; try_files $uri @backend; } location / { try_files $uri @backend; } location @backend { proxy_pass http://127.0.0.1:8199; proxy_redirect off; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; }}其中,8199为本地的golang应用WebServer监听端口。例如,在该配置下,我们可以通过http://goframe.org/piblic/my.png访问静态文件。三、容器部署容器部署即使用docker化部署golang应用程序,这是在云服务时代最流行的部署方式,也是最推荐的部署方式。1. 编译程序跨平台交叉编译是golang的特点之一,可以非常方便地编译出我们需要的目标服务器平台的版本,而且是静态编译,非常方便地解决了运行依赖问题。使用以下方式静态编译Linux平台amd64架构的可执行文件:CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o gf-app main.go这样便编译出来一个gf-app的可执行文件。2. 编译镜像我们需要将该可执行文件gf-app编译生成docker镜像,以便于分发及部署。Golang的运行环境推荐使用alpine基础系统镜像,编译出的容器镜像约为20MB左右。一个参考的Dockerfile文件如下( 可以参考gf-home项目的Dcokerfile: https://github.com/gogf/gf-home ):FROM loads/alpine:3.8LABEL maintainer=“john@johng.cn”################################################################################ INSTALLATION###############################################################################ADD ./gf-app /bin/mainRUN chmod +x /bin/main################################################################################ START###############################################################################CMD main其中,我们的基础镜像使用了loads/alpine:3.8这个镜像,基础镜像的Dockerfile地址:https://github.com/johngcn/do… ,仓库地址:https://hub.docker.com/u/loads随后使用 docker build gf-app . 命令编译生成名为gf-app的docker镜像。3. 运行镜像使用以下命令运行镜像:docker run gf-app4. 镜像分发容器的分发可以使用docker官方的平台:https://hub.docker.com/ ,国内也可以考虑使用阿里云:https://www.aliyun.com/produc… 。5. 容器编排在企业级生产环境中,docker容器往往需要结合kubernetes或者docker swarm容器编排工具一起使用。容器编排涉及到的内容比较多,感兴趣的同学可以参考以下资料:https://kubernetes.io/https://docs.docker.com/swarm/ ...

February 19, 2019 · 1 min · jiezi

containerd发布了CRI修复程序和CVE-2019-5736更新的runc

欢迎来到containerd的v1.1.6版本!这是containerd 1.1版本的第六个补丁版本。这个发布专门跟新了runc以修复CVE-2019-5736容器逃逸漏洞。几个CRI修复也包括在内此版本并列在下面。运行引擎Update runc to 6635b4f0c6af3810594d2770f662f34ddc15b40d to fix CVE-2019-5736CRIcontainerd/cri#984 filter events for non k8s.io namespaces (resolves firecracker-microvm/firecracker-containerd#35)containerd/cri#991 Remove container lifecycle image dependency (fixes containerd/cri#990)containerd/cri#1016 Specify platform for image pull (fixes containerd/cri#1015)containerd/cri#1027 Fix the log ending newline handling (fixes containerd/cri#1026)containerd/cri#1042 Set /etc/hostname (fixes containerd/cri#1041)containerd/cri#1045 Fix env performance issue (fixes containerd/cri#1044)Update cri to f0b5665a959119b6a6234001e6d55206d9200e95Please try out the release binaries and report any issues at https://github.com/containerd…贡献者Lantao LiuPhil EstesMichael CrosbySebastiaan van StijnAkihiro SudaDerek McGowanLifubangMike BrownWei FuAce-TangMike Browncontainerd项目获得核心基础结构计划(CII)的最佳实践徽章:KubeCon + CloudNativeCon中国论坛提案征集(CFP)2月22日截止KubeCon + CloudNativeCon 论坛让用户、开发人员、从业人员汇聚一堂,面对面进行交流合作。与会人员有 Kubernetes、Prometheus 及其他云原生计算基金会 (CNCF) 主办项目的领导,和我们一同探讨云原生生态系统发展方向。Open Source Summit中国提案征集(CFP)2月22日截止在Open Source Summit中国,与会者将共同合作及共享信息,了解最新和最有趣的开源技术,包括Linux、IoT、区块链、AI、网络等;并获得如何在开源社区中导向和引领的信息。大会日期:提案征集截止日期:太平洋标准时间 2 月 22 日,星期五,晚上 11:59提案征集通知日期:2019 年 4 月 8 日会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日提醒:这是一场社区会议。因此,让我们尽量避开公然推销产品和/或供应商销售宣传。KubeCon + CloudNativeCon和Open Source Summit赞助方案出炉KubeCon + CloudNativeCon和Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon和Open Source Summit即将首次合体落地中国 ...

February 15, 2019 · 1 min · jiezi

Runc和CVE-2019-5736

2月11日早上有宣布关于runc中的容器逃逸漏洞。我们希望为Kubernetes用户提供一些指导,以确保每个人都安全。Runc是什么?简单来说,runc是一个低层工具,它负责大量生成Linux容器。Docker、Containerd和CRI-O等其他工具位于runc之上,用于处理数据格式化和序列化等问题,但runc是所有这些系统的核心。Kubernetes位于这些工具之上,因此虽然Kubernetes本身的任何部分都不容易受到攻击,大多数Kubernetes安装都使用了runc。这个漏洞是关于?虽然完整的细节仍然被禁止以提供人们时间补丁,但粗略的版本是:当在容器内以root(UID 0)运行进程时,该进程可以利用runc中的错误来获取运行容器的主机的root权限。然后,这允许他们无限制地访问服务器以及该服务器上的任何其他容器。如果容器内的进程是可信任的(是你知道的东西而不是敌对的)或者没有以UID 0运行,则不受该漏洞影响。如果已应用适当的政策,SELinux也可以阻止它。RedHat Enterprise Linux和CentOS都在其软件包中包含适当的SELinux权限,因此如果启用SELinux,则应该不受影响。最常见的风险来源是攻击者-控制器(attacker-controller)容器镜像,例如来自公共存储库的未经审查的镜像。我该怎么办?与所有安全问题一样,两个主要选项是缓解漏洞,或将runc版本升级到包含此修补程序的版本。由于漏洞需要容器内的UID 0,因此直接缓解是确保所有容器都以非0用户身份运行。这可以在容器镜像中设置,也可以通过pod规范设置:apiVersion: v1kind: Podmetadata: name: run-as-uid-1000spec: securityContext: runAsUser: 1000 # …这也可以使用PodSecurityPolicy全局实施:apiVersion: policy/v1beta1kind: PodSecurityPolicymetadata: name: non-rootspec: privileged: false allowPrivilegeEscalation: false runAsUser: # Require the container to run without root privileges. rule: ‘MustRunAsNonRoot’考虑到在容器内作为UID 0运行的整体风险,强烈建议设置这样的政策。另一个潜缓解措施是确保所有容器镜像都经过审查和可信任。这可以通过自己构建所有镜像,或者通过审查镜像的内容然后固定到镜像版本哈希来实现(image:external/someimage@sha256:7832659873hacdef)。升级runc通常可以通过升级你的发行版的软件包runc,或通过升级你的操作系统镜像(如果使用不可变镜像)来完成。这是各种发行版和平台的已知安全版本列表:Ubuntu - runc 1.0.0~rc4+dfsg1-6ubuntu0.18.10.1Debian - runc 0.1.1+dfsg1-2RedHat Enterprise Linux - docker 1.13.1-91.git07f3374.el7 (if SELinux is disabled)Amazon Linux - docker 18.06.1ce-7.25.amzn1.x86_64CoreOS - 2051.0.0Kops Debian - in progressDocker - 18.09.2一些平台还发布了更具体的说明:Google Container Engine (GKE)Google发布了一份包含更多详细信息的安全公告,但简而言之,如果你使用默认的GKE节点镜像,那么你就是安全的。如果你使用的是Ubuntu节点镜像,则需要缓解或使用已知安全版本的runc来升级镜像。Amazon Elastic Container Service for Kubernetes (EKS)亚马逊还发布了一份包含更详细信息的安全公告。所有EKS用户都应该缓解问题或升级到新节点镜像。Docker我们没有具体确认Docker for Mac和Docker for Windows是易受攻击的,但似乎很可能。Docker发布了18.09.2版本的修复程序,建议你升级到它。这也适用于使用Docker的其他部署系统。如果您无法升级Docker,Rancher团队已在github.com/rancher/runc-cve上为许多旧版本提供了修复的后端。获取更多信息如果你对此漏洞如何影响Kubernetes有任何疑问,请通过discuss.kubernetes.io加入我们的讨论。如果你想与runc团队取得联系,你可以通过Google网上论坛或Freenode IRC上的#opencontainers与他们联系。KubeCon + CloudNativeCon中国论坛提案征集(CFP)2月22日截止KubeCon + CloudNativeCon 论坛让用户、开发人员、从业人员汇聚一堂,面对面进行交流合作。与会人员有 Kubernetes、Prometheus 及其他云原生计算基金会 (CNCF) 主办项目的领导,和我们一同探讨云原生生态系统发展方向。中国开源峰会提案征集(CFP)2月22日截止在中国开源峰会上,与会者将共同合作及共享信息,了解最新和最有趣的开源技术,包括Linux、IoT、区块链、AI、网络等;并获得如何在开源社区中导向和引领的信息。大会日期:提案征集截止日期:太平洋标准时间 2 月 22 日,星期五,晚上 11:59提案征集通知日期:2019 年 4 月 8 日会议日程通告日期:2019 年 4 月 10 日会议活动举办日期:2019 年 6 月 24 至 26 日提醒:这是一场社区会议。因此,让我们尽量避开公然推销产品和/或供应商销售宣传。KubeCon + CloudNativeCon + Open Source Summit赞助方案出炉KubeCon + CloudNativeCon + Open Source Summit多元化奖学金现正接受申请KubeCon + CloudNativeCon + Open Source Summit即将首次合体落地中国 ...

February 13, 2019 · 1 min · jiezi

PHP实现一个轻量级容器

什么是容器在开发过程中,经常会用到的一个概念就是依赖注入。我们借助依懒注入来解耦代码,选择性的按需加载服务,而这些通常都是借助容器来实现。容器实现对对象的统一管理,并且确保对象实例的唯一性容器可以很轻易的找到有很多实现示例,如 PHP-DI 、 YII-DI 等各种实现,通常他们要么大而全,要么高度适配特定业务,与实际需要存在冲突。出于需要,我们自己造一个轻量级的轮子,为了保持规范,我们基于 PSR-11 来实现。 PSR-11 PSR 是 php-fig 提供的标准化建议,虽然不是官方组织,但是得到广泛认可。PSR-11 提供了容器接口。它包含 ContainerInterface 和 两个异常接口,并提供使用建议。/** * Describes the interface of a container that exposes methods to read its entries. /interface ContainerInterface{ /* * Finds an entry of the container by its identifier and returns it. * * @param string $id Identifier of the entry to look for. * * @throws NotFoundExceptionInterface No entry was found for this identifier. * @throws ContainerExceptionInterface Error while retrieving the entry. * * @return mixed Entry. / public function get($id); /* * Returns true if the container can return an entry for the given identifier. * Returns false otherwise. * * has($id) returning true does not mean that get($id) will not throw an exception. * It does however mean that get($id) will not throw a NotFoundExceptionInterface. * * @param string $id Identifier of the entry to look for. * * @return bool / public function has($id);}实现示例我们先来实现接口中要求的两个方法abstract class AbstractContainer implements ContainerInterface{ protected $resolvedEntries = []; /* * @var array / protected $definitions = []; public function __construct($definitions = []) { foreach ($definitions as $id => $definition) { $this->injection($id, $definition); } } public function get($id) { if (!$this->has($id)) { throw new NotFoundException(“No entry or class found for {$id}”); } $instance = $this->make($id); return $instance; } public function has($id) { return isset($this->definitions[$id]); }实际我们容器中注入的对象是多种多样的,所以我们单独抽出实例化方法。 protected function make($name) { if (isset($this->resolvedEntries[$name])) { return $this->resolvedEntries[$name]; } $definition = $this->definitions[$name]; $params = []; if (is_array($definition) && isset($definition[‘class’])) { $params = $definition; $definition = $definition[‘class’]; unset($params[‘class’]); } $object = $this->reflector($definition, $params); return $this->resolvedEntries[$name] = $object; } public function reflector($concrete, array $params = []) { if ($concrete instanceof \Closure) { return $concrete($params); } elseif (is_string($concrete)) { $reflection = new \ReflectionClass($concrete); $dependencies = $this->getDependencies($reflection); foreach ($params as $index => $value) { $dependencies[$index] = $value; } return $reflection->newInstanceArgs($dependencies); } elseif (is_object($concrete)) { return $concrete; } } /* * @param \ReflectionClass $reflection * @return array / private function getDependencies($reflection) { $dependencies = []; $constructor = $reflection->getConstructor(); if ($constructor !== null) { $parameters = $constructor->getParameters(); $dependencies = $this->getParametersByDependencies($parameters); } return $dependencies; } /* * * 获取构造类相关参数的依赖 * @param array $dependencies * @return array $parameters * / private function getParametersByDependencies(array $dependencies) { $parameters = []; foreach ($dependencies as $param) { if ($param->getClass()) { $paramName = $param->getClass()->name; $paramObject = $this->reflector($paramName); $parameters[] = $paramObject; } elseif ($param->isArray()) { if ($param->isDefaultValueAvailable()) { $parameters[] = $param->getDefaultValue(); } else { $parameters[] = []; } } elseif ($param->isCallable()) { if ($param->isDefaultValueAvailable()) { $parameters[] = $param->getDefaultValue(); } else { $parameters[] = function ($arg) { }; } } else { if ($param->isDefaultValueAvailable()) { $parameters[] = $param->getDefaultValue(); } else { if ($param->allowsNull()) { $parameters[] = null; } else { $parameters[] = false; } } } } return $parameters; }如你所见,到目前为止我们只实现了从容器中取出实例,从哪里去提供实例定义呢,所以我们还需要提供一个方法. /* * @param string $id * @param string | array | callable $concrete * @throws ContainerException */ public function injection($id, $concrete) { if (!is_string($id)) { throw new \InvalidArgumentException(sprintf( ‘The id parameter must be of type string, %s given’, is_object($id) ? get_class($id) : gettype($id) )); } if (is_array($concrete) && !isset($concrete[‘class’])) { throw new ContainerException(‘数组必须包含类定义’); } $this->definitions[$id] = $concrete; }只有这样吗?对的,有了这些操作我们已经有一个完整的容器了,插箱即用。不过为了使用方便,我们可以再提供一些便捷的方法,比如数组式访问。class Container extends AbstractContainer implements \ArrayAccess{ public function offsetExists($offset) { return $this->has($offset); } public function offsetGet($offset) { return $this->get($offset); } public function offsetSet($offset, $value) { return $this->injection($offset, $value); } public function offsetUnset($offset) { unset($this->resolvedEntries[$offset]); unset($this->definitions[$offset]); }}这样我们就拥有了一个功能丰富,使用方便的轻量级容器了,赶快整合到你的项目中去吧。点击这里查看完整代码 ...

January 27, 2019 · 3 min · jiezi

在阿里,我们如何管理测试环境

前言阿里的许多实践看似简单,背后却蕴涵着许多思考,譬如测试环境的管理。互联网产品的服务通常是由Web应用、中间件、数据库和许多后台业务程序组成的,一套运行环境就是一个自成一体的小生态。最基本的运行环境是线上环境,部署产品的正式发布版本,为用户提供持续可靠的服务。除此以外,还有许多不对外部用户开放的运行环境,用于产品团队日常的开发和验证,统称为测试环境。正式环境的稳定性,除去软件自身的质量因素,主要与运行的主机、网络等基础设施相关,而测试环境的稳定性则更多受到人为因素影响。由于频繁的版本变更,以及部署未经充分验证的代码,测试环境出故障的情况屡见不鲜。良好的代码提交习惯、适当的变更前检查有助于减少故障的发生,但无法彻底杜绝后患。增加多套测试环境副本能够有效控制故障的影响范围,然而企业的资源终归有限,降低测试环境成本和提高测试环境稳定性成为了矛盾的两面。在这个领域里,独具匠心的阿里研发效能团队设计了一种服务级复用的虚拟化技术,称为“特性环境”,其巧妙的思路令人赞叹。本文将围绕测试环境管理的话题,聊聊这种具有阿里特色的工作方式。测试环境管理的困局测试环境的用途很广泛,常见的测试环境譬如系统集成测试环境、用户验收测试环境、预发布测试环境、灰度测试环境等,它们体现了产品的交付生命周期,也间接反映出整个团队的组织结构。小作坊型产品团队的测试环境管理起来十分简单,每个工程师本地就能启动全套软件组件进行调试,倘若不放心,再加上一个公共的集成测试环境也就足够。随着产品规模扩大,本地启动所有服务组件逐渐变得既费时又费事,工程师们只能在本地运行一部分待调试的组件,然后利用公共测试环境上的其余组件组成完整系统。与此同时,团队规模的扩张,使得每个团队成员的职责进一步细分,新的子团队被划分出来,这意味着项目的沟通成本增加,公共测试环境的稳定性开始变得难以控制。在这个过程中,测试环境管理复杂性带来的影响,不仅体现在服务联调变得繁琐,更直接反映在交付流程和资源成本的变化上。在交付流程方面,一个显著的变化是测试环境种类增多。出于不同的用途和目的,工程师们设计出了各式各样的专用测试环境。这些测试环境的组合形成了各个企业独具特色的交付流程。下图展示了一种用于大型项目的复杂交付流程。从单独服务的角度来看,环境与环境之间是由流水线相连的,再加上自动化测试或手工审批操作组成关卡,实现环境之间的传递。通常越高级别环境的部署频率越低,因此相对稳定性也越高。与之相反,在级别较低的环境上,就随时可能存在新的部署,会打扰正在使用该环境的其他人。有时为了复现某些特殊的问题场景,一些开发者不得不直接登录到服务器上面去“搞事情”,进一步影响环境的稳定性和可用性。面对随时可能崩溃的测试环境,小企业会试着去“堵”:约束服务变更时间、设立严格的变更规范,大企业则善于用“疏”:增加测试环境副本,隔离故障影响范围。显然,不堪重负的测试环境一定越“堵”越“漏”,千年以前大禹治水的故事早就揭示了的道理,刻意的管控拯救不了脆弱的测试环境。近年来,DevOps文化的兴起,端到端解放了开发者的双手,这对于测试环境的管理而言却是一把双刃剑。一方面,DevOps鼓励开发人员参与运维,了解产品的完整生命周期,有助于减少不必要的低级运维事故;另一方面,DevOps让更多的手伸向测试环境,更多的变更、更多的Hotfix出现了。这些实践从全局来看利大于弊,然而并不能缓解测试环境的动荡。单纯的流程疏通同样拯救不了脆弱的测试环境。那么该投入的还得投入。将不同团队所用的低级别测试环境各自独立,此时每个团队看到的都是线性流水线,从整体上观察,则会程现出河流汇聚的形状。由此推广,理想情况下,每位开发者都应该得到独占且稳定的测试环境,各自不受干扰的完成工作。然而由于成本因素,现实中在团队内往往只能共享有限的测试资源,不同成员在测试环境相互干扰成为影响软件开发质量的隐患。增加测试环境副本数本质上是一种提高成本换取效率的方法,然而许多试图在成本和效率之间寻找最优平衡的探索者们,似乎都在同一条不归路上越行越远。由于客观的规模和体量,上述这些测试环境管理的麻烦事儿,阿里的产品团队都无法幸免。首先是测试环境种类的管理。在阿里内部,同样有十分丰富的测试环境区分。各种测试环境的命名与其作用息息相关,虽然业界有些常用的名称,但都未形成权威的标准。实际上,环境的名称只是一种形式,关键还在于各种测试环境应当分别适配于特定应用场景,且场景之间应当或多或少存在一些差异。这种差异有些在于运行的服务种类,譬如性能测试环境很可能只需要运行与压力测试相关的那部分访问量最大的关键业务服务,其他服务运行了也是浪费资源。有些差异在于接入数据的来源,譬如开发自测的环境的数据源与正式环境肯定不一样,这样测试使用的假数据就不会污染线上用户的请求;预发布环境(或用户验收测试环境)会用与正式环境一致的数据源(或正式数据源的拷贝),以便反映新功能在真实数据上运行的情况;自动化测试相关的环境会有单独的一套测试数据库,以避测试运行过程中受到其他人为操作的干扰。还有些差异在于使用者的不同,譬如灰度和预发布环境都使用正式的数据源,但灰度环境的使用者是一小撮真实的外部用户,而预发布环境的使用者都是内部人员。总之,没必要为一个不存在业务特殊性的测试场景专门发明一种测试环境。在集团层面,阿里对流水线形式的约束相对宽松。客观的讲,只有在一线的开发团队知道最适合团队的交付流程应该是什么样子。阿里的开发平台只是规范了一些推荐的流水线模板,开发者可在此基础上进行发挥。列举几个典型的模板例子:这里出现了几种外界不太常见的环境类型名称,稍后会详细介绍。其次是测试环境成本的管理。成本管理的问题十分棘手且十分值得深究。与测试环境相关的成本主要包括管理环境所需的“人工成本”和购买基础设施所需的“资产成本”。通过自动化以及自服务化的工具可以有效降低人工相关的成本,自动化又是个很大的话题,宜另起一篇文章讨论,此处暂且收住。资产购买成本的降低依赖技术的改良和进步(排除规模化采购带来的价格变化因素),而基础设施技术的发展史包括两大领域:硬件和软件。硬件发展带来的成本大幅下降,通常来自于新的材料、新的生产工艺、以及新的硬件设计思路;软件发展带来的基础设施成本大幅下降,目前看来,大多来自于虚拟化(即资源隔离复用)技术的突破。最早的虚拟化技术是虚拟机,早在20世纪50年代,IBM就开始利用这种硬件级的虚拟化方法获得成倍的资源利用率提升。虚拟机上的不同隔离环境之间各自运行完整操作系统,具有很好的隔离性,通用性强,但对于运行业务服务的场景,显得略为笨重。2000年后,KVM、XEN等开源项目使得硬件级虚拟化广泛普及。与此同时,另一种更轻量的虚拟化技术出现了,以OpenVZ、LXC为代表的早期容器技术,实现了建立于操作系统内核之上的运行环境虚拟化,减少了独立操作系统的资源消耗,以牺牲一定隔离性为代价,获得更高的资源利用率。之后诞生的Docker以其镜像封装和单进程容器的理念,将这种内核级虚拟化技术推上百万人追捧的高度。阿里紧随技术前进的步伐,早早的就用上了虚拟机和容器,在2017年双十一时,在线业务服务的容器化比例已经达到100%。然而,接下来的挑战是,基础设施资源利用率还能做得更高吗?甩掉了虚拟机的硬件指令转换和操作系统开销,运行在容器中的程序与普通程序之间只有一层薄薄的内核Namespace隔离,完全没有运行时性能损耗,虚拟化在这个方向上似乎已经发展到了极限。唯一的可能是,抛开通用场景,专注到测试环境管理的特定场景上,继续寻找突破。终于,阿里在这个领域里发现了新的宝藏:服务级虚拟化。所谓服务级虚拟化,本质上是基于消息路由的控制,实现集群中部分服务的复用。在服务级虚拟化方式下,许多外表庞大的独立测试环境实际只需要消耗极小的额外基础设施资源,即使给每个开发者配备一套专用的测试环境集群都不再是吹牛。具体来说,在阿里的交付流程上,包含两种特殊类型的测试环境:“公共基础环境”和“特性环境”,它们形成了具有阿里特色的测试环境使用方法。公共基础环境是一个全套的服务运行环境,它通常运行一个相对稳定的服务版本,也有些团队将始终部署各服务的最新版本的低级别环境(称为“日常环境”)作为公共基础环境。特性环境是这套方法中最有意思的地方,它是虚拟的环境。从表面上看,每个特性环境都是一套独立完整的测试环境,由一系列服务组成集群,而实际上,除了个别当前使用者想要测试的服务,其余服务都是通过路由系统和消息中间件虚拟出来的,指向公共基础环境的相应服务。由于在阿里通常的开发流程中,开发任务需要经过特性分支、发布分支和诸多相关环节最后发布上线,大多数环境都从发布分支部署,唯独这种开发者自用的虚拟环境部署来自代码特性分支的版本,故可称为“特性环境”(阿里内部叫“项目环境”)。举个具体例子,某交易系统的完整部署需要由鉴权服务、交易服务、订单服务、结算服务等十几种小系统以及相应的数据库、缓存池、消息中间件等组成,那么它的公共基础环境就是这样一套具备所有服务和周边组件的完整环境。假设此时有两套特性环境在运行,一套只启动了交易服务,另一套启动了交易服务、订单服务和结算服务。对于第一套特性环境的使用者而言,虽然除交易服务外的所有服务实际上都由公共基础环境代理,但在使用时就像是自己独占一整套完整环境:可以随意部署和更新环境中交易服务的版本,并对它进行调试,不用担心会影响其他用户。对于第二套特性环境的使用者,则可以对部署在该环境中的三个服务进行联调和验证,倘若在场景中使用到了鉴权服务,则由公共基础环境的鉴权服务来响应。咋看起来,这不就是动态修改域名对应的路由地址、或者消息主题对应的投递地址么?实事并没那么简单,因为不能为了某个特性环境而修改公共基础环境的路由,所以单靠正统路由机制只能实现单向目标控制,即特性环境里的服务主动发起调用能够正确路由,若请求的发起方在公共基础环境上,就无法知道该将请求发给哪个特性环境了。对于HTTP类型的请求甚至很难处理回调的情况,当处于公共基础环境的服务进行回调时,域名解析会将目标指向公共基础环境上的同名服务。如何才能实现数据双向的正确路由和投递呢?不妨先回到这个问题的本质上来:请求应该进入哪个特性环境,是与请求的发起人相关的。因此实现双向绑定的关键在于,识别请求发起人所处的特性环境和进行端到端的路由控制。这个过程与“灰度发布”很有几分相似,可采用类似的思路解决。得益于阿里在中间件领域的技术积累,和鹰眼等路由追踪工具的广泛使用,识别请求发起人和追溯回调链路都不算难事。如此一来,路由控制也就水到渠成了。当使用特性环境时,用户需要“加入”到该环境,这个操作会将用户标识(如IP地址或用户ID)与指定的特性环境关联起来,每个用户只能同时属于一个特性环境。当数据请求经过路由中间件(消息队列、消息网关、HTTP网关等),一旦识别到请求的发起人当前处在特性环境中,就会尝试把请求路由给该环境中的服务,若该环境没有与目标一致的服务,才路由或投递到公共基础环境上。特性环境并不是孤立存在的,它可以建立在容器技术之上,从而获得更大的灵活性。正如将容器建立在虚拟机之上得到基础设施获取的便利性一样,在特性环境中,通过容器快速而动态的部署服务,意味着用户可以随时向特性环境中增加一个需要修改或调试的服务,也可以将环境中的某个服务随时销毁,让公共基础环境的自动接替它。还有一个问题是服务集群调试。配合AoneFlow的特性分支工作方式,倘若将几个服务的不同特性分支部署到同一个特性环境,就可以进行多特性的即时联调,从而将特性环境用于集成测试。不过,即使特性环境的创建成本很低,毕竟服务是部署在测试集群上的。这意味着每次修改代码都需要等待流水线的构建和部署,节约了空间开销,却没有缩短时间开销。为了进一步的降低成本、提高效率,阿里团队又捣鼓出了一种开脑洞的玩法:将本地开发机加入特性环境。在集团内部,由于开发机和测试环境都使用内网IP地址,稍加变通其实不难将特定的测试环境请求直接路由到开发机。这意味着,在特性环境的用户即使访问一个实际来自公共基础环境的服务,在后续处理链路上的一部分服务也可以来自特性环境,甚至来自本地环境。现在,调试集群中的服务变得非常简单,再也不用等待漫长的流水线构建,就像整个测试环境都运行在本地一样。DIY体验特性环境觉得服务级虚拟化太小众,离普通开发者很远?实事并非如此,我们现在就可以动手DIY个体验版的特性环境来玩。阿里的特性环境实现了包括HTTP调用、RPC调用、消息队列、消息通知等各类常用服务通信方式的双向路由服务级虚拟化。要完成这样的功能齐全的测试环境有点费劲,从通用性角度考虑,咱不妨从最符合大众口味的HTTP协议开始,做个支持单向路由的简易款。为了便于管理环境,最好得有一个能跑容器的集群,在开源社区里,功能齐全的Kubernetes是个不错的选择。在Kubernetes中有些与路由控制有关的概念,它们都以资源对象的形式展现给用户。简单介绍一下,Namespace对象能隔离服务的路由域(与容器隔离使用的内核Namespace不是一个东西,勿混淆),Service对象用来指定服务的路由目标和名称,Deployment对象对应真实部署的服务。类型是ClusterIP(以及NodePort和LoadBalancer类型,暂且忽略它们)的Service对象可路由相同Namespace内的一个真实服务,类型是ExternalName的Service对象则可作为外部服务在当前Namespace的路由代理。这些资源对象的管理都可以使用YAML格式的文件来描述,大致了解完这些,就可以开始动工了。基础设施和Kubernetes集群搭建的过程略过,下面直接进正题。先得准备路由兜底的公共基础环境,这是一个全量测试环境,包括被测系统里的所有服务和其他基础设施。暂不考虑对外访问,公共基础环境中的所有服务相应的Service对象都可以使用ClusterIP类型,假设它们对应的Namespace名称为pub-base-env。这样一来,Kubernetes会为此环境中的每个服务自动赋予Namespace内可用的域名“服务名.svc.cluster”和集群全局域名“服务名.pub-base-env.svc.cluster”。有了兜底的保障后,就可以开始创建特性环境了,最简单的特性环境可以只包含一个真实服务(例如trade-service),其余服务全部用ExternalName类型的Service对象代理到公共基础环境上。假设它使用名称为feature-env-1的Namespace,其描述的YAML如下(省略了非关键字段的信息):kind: Namespacemetadata:name: feature-env-1*kind: Servicemetadata:name: trade-servicenamespace: feature-env-1spec:type: ClusterIP…*kind: Deploymentmetadata:name: trade-servicenamespace: feature-env-1spec:…*kind: Servicemetadata:name: order-servicenamespace: feature-env-1spec:type: ExternalNameexternalName: order-service.pub-base-env.svc.cluster…*kind: Service…注意其中的order-service服务,它在当前特性环境Namespace中可以使用局部域名order-service.svc.cluster访问,请求会路由到它配置的全局域名order-service.pub-base-env.svc.cluster,即公共基础环境的同名服务上处理。处于该Namespace中的其它服务感知不到这个差异,而是会觉得这个Namespace中部署了所有相关的服务。若在特性的开发过程中,开发者对order-service服务也进行了修改,此时应该将修改过的服务版本添加到环境里来。只需修改order-service的Service对象属性(使用Kubernetes的patch操作),将其改为ClusterIP类型,同时在当前Namespace中创建一个Deployment对象与之关联即可。由于修改Service对象只对相应Namespace(即相应的特性环境)内的服务有效,无法影响从公共基础环境回调的请求,因此路由是单向的。在这种情况下,特性环境中必须包含待测调用链路的入口服务和包含回调操作的服务。例如待测的特性是由界面操作发起的,提供用户界面的服务就是入口服务。即使该服务没有修改,也应该在特性环境中部署它的主线版本。通过这种机制也不难实现把集群服务局部替换成本地服务进行调试开发的功能,倘若集群和本地主机都在内网,将ExternalName类型的Service对象指向本地的IP地址和服务端口就可以了。否则需要为本地服务增加公网路由,通过动态域名解析来实现。与此同时,云效也正在逐步完善基于Kubernetes的特性环境解决方案,届时将会提供更加全面的路由隔离支持。值得一提的是,由于公有云的特殊性,在联调时将本地主机加入云上集群是个必须克服的难题。为此云效实现了通过隧道网络+kube-proxy自身路由能力,将本地局域网主机(无需公网IP地址)加入到不在同一内网Kubernetes集群进行联调的方式。其中的技术细节也将在近期的云效公众号向大家揭晓,敬请留意。小结当许多人还在等待,在虚拟机和容器之后,下一轮虚拟化技术的风口何时到来的时候,阿里已经给出了一种答案。创业者的心态让阿里人懂得,能省必须省。其实,限制创新的往往不是技术而是想象力,服务级虚拟化的理念突破了人们对环境副本的传统认知,以独特的角度化解了测试环境成本与稳定性的矛盾。作为一种颇具特色的技术载体,特性环境的价值不仅仅在于轻量的测试环境管理体验,更在于为每位开发人员带来流畅的工作方式,实则是“简约而不简单”。实践出真知,阿里巴巴云效平台致力于解决大型项目协作、敏捷高速迭代、海量代码托管、高效测试工具、分布式秒级构建、大规模集群部署发布等世界级业务和技术难题,为阿里巴巴集团内部、生态伙伴以及云上开发者服务。诚挚欢迎业界同行与我们探讨交流。相关阅读:在阿里,我们如何管理代码分支当kubernetes应用遇到阿里分批发布模式本文作者:云效鼓励师阅读原文本文为云栖社区原创内容,未经允许不得转载。

January 25, 2019 · 1 min · jiezi

简介:Virtual Kubelet

Virtual Kubelet是Kubernetes kubelet的一个实现,它伪装成一个kubelet,用于将Kubernetes集群连接到其他API。这允许Kubernetes节点由其他服务支持,例如无服务器容器平台。架构kubelets通常如何工作一般来讲,Kubernetes kubelet为每个Kubernetes节点(Node)实现Pod和容器操作。它们作为每个节点上的代理运行,无论该节点是物理服务器还是虚拟机,并在该节点上处理Pod/容器操作。kubelets将名为PodSpec的配置作为输入,并确保PodSpec中指定的容器正在运行且运行正常。Virtual Kubelet的工作原理从Kubernetes API服务器的角度来看,Virtual Kubelet看起来像普通的kubelet,但其关键区别在于它们在其他地方调度容器,例如在云无服务器API中,而不是在节点上。下面显示了一个Kubernetes集群,其中包含一系列标准kubelet和一个Virtual Kubelet:供应商Virtual Kubelet支持各种供应商(Provider):Alibaba Cloud Elastic Container Instance (ECI)AWS FargateAzure BatchAzure Container Instances (ACI)Kubernetes Container Runtime Interface (CRI)Huawei Cloud Container Instance (CCI)Hyper.shHashiCorp NomadService Fabric MeshvSphere Integrated Containers (VIC)你还可以添加自己的供应商。这里有早前在西雅图举办的KubeCon + CloudNativeCon 2018北美大会的Virtual Kubelet视频:简介:Virtual Kubelet深入了解:Virtual KubeletVirtual Kubelet项目最近跟新了网页,快来了解一下!2019年KubeCon + CloudNativeCon中国论坛提案征集(CFP)现已开放KubeCon + CloudNativeCon 论坛让用户、开发人员、从业人员汇聚一堂,面对面进行交流合作。与会人员有 Kubernetes、Prometheus 及其他云原生计算基金会 (CNCF) 主办项目的领导,和我们一同探讨云原生生态系统发展方向。2019年中国开源峰会提案征集(CFP)现已开放在中国开源峰会上,与会者将共同合作及共享信息,了解最新和最有趣的开源技术,包括 Linux、容器、云技术、网络、微服务等;并获得如何在开源社区中导向和引领的信息。大会日期:提案征集截止日期:太平洋标准时间 2 月 15 日,星期五,晚上 11:59提案征集通知日期:2019 年 4 月 1 日会议日程通告日期:2019 年 4 月 3 日幻灯片提交截止日期:6 月 17 日,星期一会议活动举办日期:2019 年 6 月 24 至 26 日2019年KubeCon + CloudNativeCon + Open Source Summit China赞助方案出炉啦 ...

January 24, 2019 · 1 min · jiezi