2019年12月,AWS REInvent上发布了诸多Serverless相关的更新,反响热烈。
Serverless无疑已经成为学术界和工业界关注的焦点。
本文主要聊聊个人对Serverless的一些理解。
回顾过去互联网快速发展的数年,催生了诸多技术领域的创新,如大数据、微服务、DevOps等。这些技术并没有个统一的定义,而是依赖于社区的蓬勃发展,不断优化其定义。
类似的,Serverless也没有一个官方的定义,但已经有不少总结帮助我们理解到底什么是Serverless,如下便是个人认为的重要总结:
Serverless technology is an infrastructure abstraction which enables developers to focus on writing software and managing application performance, rather than deploying, maintaining and scaling the underlying server infrastructure。
—《Overview of severless technology, Enterprise Serverless User Cases》
Serverless computing refers to the concept of building and running applications that do not require server management. It describes a fine-grained deployment meodel where applications, bundled as one or more functions, are uploaded to a platform and then executed, scaled and billed in response to the exact demand needed at the moment.
— 《What is Serveress Computing, CNCF Serverles Whitepaper v1.0》
Serverless encompasses two different areas: FaaS and BaaS.
FaaS(Function As A Service) - 由开发人员编写函数,事件触发、短生命周期,通常在容器或轻量级VM中运行。 BaaS(Backend As A Service) - 由云厂商提供,支撑函数实现业务逻辑。典型的BaaS包括:
基于如上的定义,我们对Serverless已经有了大致的理解,核心关键字无需关注服务器,使能业务快速开发,由FaaS+BaaS生态构成。
总结如下:Serverless就是使能开发者聚焦业务实现、无需关注运维与基础设施、并能按需使用和付费的技术与模式
在伯克利的论文中,提出了一大假设:
Serverless的出现,极大简化了云计算的变成模型,将是软件生产力的一次大变革。举例来说,(Serverful)向Serverless的演进,就如同若干年前,汇编语言向高级语言的演进。
回想汇编语言的时代,开发者不仅需要考虑指令集,还需要熟悉寄存器的读取、信号的中断等。而高级语言的诞生,解放了开发者,不再需要考虑这些底层的细节,更关注业务逻辑。 就好比没有人会用汇编来实现Web应用,而是使用诸如PHP/JSP/NodeJS等技术。
纵观历史,软件开发领域存在的五次大变革,都极大提高了开发者的工作效率:
这些变革有一个共同点:那就是为软件工程师赋予了更多权利,帮助更快的交付有价值的应用。
围绕Severless带来的变革,我理解主要有两大核心特征:
零运维:开发人员/运维人员不再需要承担构建分布式系统的(大部分)运维成本。通常,为了构建高可用的分布式系统,我们需要考虑:
单点故障:
Serverless 不仅帮助开发人员降低资源的维护和管理成本,也能够完成如上所述的自动扩缩容、高可用等机制。使得开发人员可以把精力聚焦在真正的业务上,快速创新
以计算资源为例,对于传统公有云基础设施资源而言,计费方式是基于资源的租用时长(如EC2启动后,即便不运行任何业务,也会以分钟为单位进行计费。)
基于Serverless,基础设施是按照运行(代码)时消耗的资源进行收费,如Lambda是按照请求次数及(占用资源的)GB-S为单位进行计费。无请求处理, 则无需支付任何费用。
Serverless的应用场景,通常是“事件驱动、短生命周期的无状态”应用。主要包括如下几类:
包括Web、移动App、IOT应用的后端服务等,如:
包括对数据的渲染、叠加或者处理等应用,如:
参考伯克利的论文,目前Serverless主要应用场景如下:
可以看出,移动应用/互联网应用作为目前应用形态的主流,在Serverless应用占比中是最高的。个人估计,随着IOT、AI等应用的普适与快速发展,这类应用在采用Serverless的统计中占比会增加。
Serverless的出现,使得开发者更注重逻辑的快速实现,而无需关注运行、维护的成本,也就是从以资源为中心向以应用为中心观念的转变。
Serverless的出现,使得架构师、开发人员和运维人员的职责都发生了变化:
架构师:从单体应用的只关注架构设计,到微服务时代的You build it, you own it(从设计到运行到运维)。Serverless的出现,使得架构师能够更加聚焦业务带来的技术挑战。
开发人员:微服务时代,开发者需学习不同框架、语言;Serveless时代, 关注输入/输出,和函数代码的处理逻辑
运维人员:关注基础设施、运行时、监控告警、可靠性等问题,大部分可以交给Serverless平台处理了。
Serverless为开发人员带来了诸多便捷,但它依然处理发展的初期,相比与完善的Serverful的生态,还存在很多不足。主要包括:
Serverless中的Function是不能直接寻址的,所以无法通过现有的数据一致性算法/机制实现数据的共享或者传递。Serverless必然会推动分布式数据协同机制的发展。
本书的作者是香奈儿前CEO莫琳·希凯,书名《深度思考》,副标题 - 不断逼近问题的本质
。
原以为这是一本思考类或者实践类的书籍,读完后,感觉有一点意外,其实是作者的自传。 书中描述了作者在职场三十年的缩影,讲述了真实的经历,从欧莱雅、GAP到香奈儿,从中规中矩的袜子、皮带销售到敢于突破传统,创新的老海军女裤,从十几人的小团队到200多人的高管,再到香奈儿全球的CEO,这背后的成长有运气,但更多体现的是作者的睿智以及对信念的追求、思考以及坚持。
莫林·希凯,2007年加入香奈儿担任全球CEO,2016年离职。加入法国香奈儿品牌之前在美国大众品牌GAP有15年的工作经验,从小职员成长到CEO,从法国欧莱雅开始第一份工作。
从孩童时期法国寄宿的不适开始,逐渐放下先见之明,拥抱陌生地域和文化体验的影响,并帮助她意识到在最能派上用场的地方发挥感染力。一个文科生眼中的新浪潮电影,将电影课完美的融入到文学专业中,提高观察力,感受电影对情绪和感觉的影响,以及如何通过细节触动他人。
工作后,作为产品经理加入欧莱雅,同商学院毕业的营销同事相比,并非仅依赖于市场调查或传统的评估模式,重要的是观察和感受顾客是如何理解营销背后的符号的。顾客不会研究广告图像,更多的会用心感受,感觉其与自身的情感、生活、理想的关系。
几年后加入GAP,因推动的新品与传统GAP的定位不一样,想尽办法并通过自身的试穿赢得了机会,并因为出色的能力升到了高管。
Leader心中都有预想好的“愿景、使命和战略”,但不管初衷设计的如何精美,一旦员工阅读并阐述时,这些“意愿”都会被杀死 —《作者之死》。 任何Leader都必须有一个“我”的定位(我们是谁,什么是重要的) 如果强加自己的观点,团队不是沉默地反抗,高声的否定,就是盲目的遵循,每种反应都是灾难性的。 人文科学的每个领域,其实是教我们反思如何理解世界的方式
在巴黎居住的时间里,舍友菲勒让她感受了到了超越约束、回归真我、追求实用的重要性,这也成为成她日后生活工作的根基
你需要了解那些仅占市场份额少的竞争对手吗?我们可以超出社会的审美标准想象美吗?我们从未认真观察过的东西,它们存在的独特之处你想过吗?
到法国北部地区的推销,并作为培训计划的第一部分,让莫琳感受到压力。第一次参加执行总裁例会,不论人们的立场,经验,教育和年龄如何不同,从新位置开始是不容易的: 尴尬的时刻、新手的无知、奇异的规则和仪式、新奇的行话、有形无形的考验,微妙的社交细节…..这些元素都是意料之中的。
无论你的职业发展到哪个阶段,“学习”和“反学习”(忘记你所学到的,重新学习)的循环是不可避免的。
你的标签是什么?你如何能扭转规则,创造重要的事物?什么独特才能让你成功?什么“音乐”能撩动你内心的心扉,启发你的工作? 什么特长能让你在职业中“出类拔萃”?透过谁的视觉能让你“高瞻远瞩”?
无论角色多么细小,我也可以有着自己的影响力。我学到的一切东西,甚至最微不足道的细节,都能建立我的能力和信心,争取自己的权利,必要时作出超出常规的抉择。
坚持自己的立场,提出自己的主张并不是为了挑战权威或证明自己的价值。坚持自己的立场的意义在于,你能作出贡献:贡献新的视角,新的愿景,新的充满启发的观点。
简而言之,无论多么微小的机会都能发挥你的才能,无论多么苛刻的老板你都能从她们身上学到东西。即使她们一无是处,你也可以学到什么是错误的领导方式,张开双眼,重塑所见,你会看到无线资源等你挖掘。
如果想在事业和生活上取得成功,必须练习倾听的技能。米奇教会了她如何倾听,不要总是一味的去展示自己的意见,要听听别人的声音,会对自己的想法有新的补充,完全坚持自己就会变成独裁者。 严于自律是一种优秀品质,但讨好每个人会让我疲惫不堪,也会使我质疑自己的每个决定,最终限制了领导力。
为什么香奈儿最终雇佣了我?除了在GAP获得的成就,其他一些特征非常重要:
创意往往源于悖论。将两个看似相反的想法、品质、设计或物体结合起来,发明出令人惊讶、超乎所料的美。打破任何强加在你身上的标签,拥抱自己的悖论,重新审视所学的知识,发展出强大的观点,在世界中留下足迹。打破标签,返璞归真。
]]>趁这几天宅在家里,前细后粗的读了一遍。(后面的太啰嗦,选择性放弃)
全书主要分为四大部分:表达的逻辑、思考的逻辑、解决问题的逻辑 和 演示的逻辑
笔记如下:
任何事情都可以归纳出一个中心论点,而此中心论点可以由3-7个论据支持,这些一级论据本身也可以是论点,被二级的3-7个论据支持,如此延伸,状如金字塔
。
序言
作为开头,包括背景、挑战、疑问、回答纵向结构
,上层是下层的抽象,下层是支持上层的论点,呈总-分/金字塔
关系。横向结构
,相互不重叠,总体不遗漏。一个模型:S-C-Q-A
公认事实
- 读者肯定会同意的内容。引出背景
的句子都有一个重要的特征,将你锁定在特定的时间和空间。二个推理:演绎推理、归纳推理 金字塔横向结构中,同一组的思想间存在逻辑顺序,包括演绎推理和归纳推理。
演绎推理:演绎推理更容易,三段论形式:大小提、小前提、推导出结论
归纳推理:是将具有共同点的事实、思想或观点归类分组,并概括其共同性。
归纳与演绎的区别:
三个逻辑:时间顺序、结构顺序、程度顺序
四个特征: 结论先行、以上统下、归类分组、逻辑推进
]]>思考的逻辑 * 总结句说明行动产生的结果/目标,找出明显的因果关系: 将每组的活动、步骤控制在5个以内。 * 区分行动步骤的层次 - 看到不同的行动、步骤、流程时,要有意的区分层次控制在5个以内
解决问题的逻辑 提高写咨询报告效率的秘诀是: * 界定问题 * 有条理的搜集和分析数据、转换为金字塔的形式
假定在K8S中存在这样的应用: * Service * 3个Pod * 使用ReplicaSet * Clients
初始情况,运行V1版本的应用。接下来,我们希望生成V2版本的镜像,并使用V2版本的Pod/容器进行升级。
存在两种方式:
* 先删除V1版本的应用,然后部署V2版本。
* 先新增V2版本的应用,然后删除V1版本。(对于V2版本的新增,可以选择 一次新增全部数量
,多次新增,每次部分数量
)
对于第一种方式:简单,但是存在部署的停机时间
对于第二种方式:系统需要同时处理两个版本的应用,尤其是数据Schema需要兼容新旧两个版本
本篇文章看看如何在Kubernetes中配置应用程序的信息。
命令行 最简单的应用配置方式,是使用命令行。
配置文件
随着配置信息增多,考虑到维护成本,可以将配置信息存储到配置文件中。但对于容器而言,使用配置文件的方式需要将配置项打包到镜像中,而且每次配置信息的变更都会导致重新生成镜像,重新部署,维护和变更成本较高。
在容器应用中,使用环境变量来实现配置,也是较普遍的一种做法,通过将参数传递给容器中的应用,变更容器运行期的配置信息,如MySQL官方的镜像就使用环境变量MYSQL_ROOT_PASSWORD
来修改超级用户root的密码。
另外,基于volume的方式获取配置信息也是一种可行的方式,如使用Git Repo存储配置信息,能有效的做到版本化管理会随时回退。
在K8S中,存储配置信息的资源被称ConfigMap,本部分将介绍ConfigMap、Secret的使用。
在Docker容器中,通常使用如下方式传递参数:
ENTRYPOINT
定义可执行命令CMD
传递参数在ENTRYPOINT中,可以使用如下两种方式启动应用:
ENTRYPOINT node app.js
ENTRYPOINT ["node", "app.js"]
注意: 这两种方式的区别在于前者是先启动Shell,由Shell调用node,而后者直接启动node应用。
在K8S中,可以通过配置文件中的command
和args
来设置容器中的ENTRYPOINT
和CMD
譬如
1 2 3 4 5 6 |
|
它们之间的区别如下图所示:
Docker | Kubernetes | 描述 |
---|---|---|
ENTRYPOINT | command | 在容器中执行命令 |
CMD | args | 给命令传递参数 |
在K8S中,使用env
设置镜像中定义的环境变量。
譬如,容器中存在如下脚本,其中的INTERVAL
使用环境变量进行设置:
1 2 3 4 5 6 |
|
在K8S中,其配置文件类似如下:
1 2 3 4 5 6 7 8 |
|
Kubernetes允许将配置项分离到一个称为ConfigMap
的单独对象中,它包含若干键/值对,并且值的范围可以从文本到整个配置文件。
应用程序不需要直接读取ConfigMap,甚至不需要知道它的存在。Map的内容可以作为环境变量或者卷传递给容器。
使用kubectl
创建ConfigMap的过程中,可以指定多样化的配置机制,类似如下所示:
1 2 3 4 5 |
|
接下来, 在如上环境变量的例子中,我们使用ConfigMap配置环境变量$INTERVAL
的值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
在如上的例子中,读取ConfigMapfortune-config
中Keysleep-interval
对应的值,作为$INTERVAL
的值。
譬如有个ConfigMap,它有两个键,分别是foo、bar。您可以使用envFrom属性将它们全部公开为环境变量,而不是像在前面的示例中那样依次使用env。
如下所示:
1 2 3 4 5 6 7 8 |
|
CONFIG_作为前缀,将导出如下环境变量CONFIG_foo和CONFIG_bar。当然,前缀是可选的,如不设置,则容器中的环境变量为foo和bar。
接下来,让我们看看如何将ConfigMap中的值作为参数传递给容器中运行的进程。我们不能在pod.spec.containers.args字段中直接引用ConfigMap,但是可以从ConfigMap中初始化一个环境变量,然后引用参数中的值,相关代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
其关系如图所示:
ConfigMap除了可以作为环境变量以及命令行参数外,还可以包括整个目录中的配置文件。
譬如,在configmap-files
目录下存在如下2个文件:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
1
|
|
接下来,使用命令创建ConfigMap
1
|
|
然后,我们可以使用volume将ConfigMap中的内容暴露给容器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
|
最简单的卷类型是emptyDir。顾名思义,emptyDir是从空目录开始。在pod中运行的应用程序可以向它写入任何文件。因为emptyDir卷的生命周期与pod的生命周期相关联,所以当pod被删除时,卷的内容会丢失。
emptyDir卷对于在同一个pod中运行的容器间共享文件特别有用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
- 第1个容器名为
html-generator
,其镜像为luksa/fortune
- volume的名称是
html
,其在容器中的路径为/var/htdocs
- 第2个容器为
web-server
,镜像为nginx:alpine
- 第2个容器使用的卷为
html
,加载到/usr/share/nginx/html
- volume名称为
name
,emptyDir初始为为空
默认情况下,emptyDir使用Node的磁盘,但是你也可以使用内存,类似如下:
1 2 3 4 |
|
Git Repo卷基本上是基于emptyDir,通过克隆Git仓库并在pod启动时(但在创建其容器之前)签出特定版本。如图6.3所示:
示例代码如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
|
默认情况下,使用gitrepo卷存在一个缺点,它不会与所引用的git repo保持同步。只有当pod重启或者创建一个新pod时,才会从Git仓库获取最新的内容。
hostPath是一种持久性存储,它指向Node文件系统中的目录,如下图所示。
运行在同一节点上的Pod,如果使用相同的hostPath卷,则可以彼此访问相同的文件。
使用hostPath的方式如下所示:
1 2 3 4 5 6 7 |
|
当一个pod被删除时,gitrepo和emptydir卷的内容都会被删除,但hostPath卷的内容不会。对于在某Node上创建的新pod,如果设置hostPath卷与之前pod的路径一致,那么它能也能访问到之前Pod写入的数据。
但是,如果您考虑使用hostPath作为数据库数据目录的话,请谨慎考虑。因为卷的内容存储在特定Node的文件系统中,当数据库pod被重新调度到另一个节点时,数据将无法被看到。
对于网络存储而言,可以使用GCE Persistent Disk、AWS Elastic BlockStore、Azure File、Azure Disk。
譬如,使用GCE Persisteng Disk的方式如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|
- 使用GCE Persistent Disk.
- 使用GCE Persistent Disk,挂载到/data/db上
除此之外,也可以使用自建的文件系统,如 NFS、iscsi(ISCSI disk)、glusterfs(GlusterFS), rbd(RADOS Block Device), flexVolume, cinder, cephfs, flocker, fc (Fibre Channel)等
到目前为止,所有持久卷类型都要求POD的开发人员了解集群实际的网络存储基础结构。例如,要创建一个支持NFS的卷,开发人员必须知道NFS所在的实际服务器。这与Kubernetes的基本理念背道而驰,Kubernetes的目标是对应用程序及其开发人员透明化其背后的基础设施,使他们不必关心基础设施的具体情况,也不必让应用程序在各种云提供商和内部数据中心之间实现数据的移植。
理想情况下,在Kubernetes上部署应用程序的开发人员不必知道底层使用的是哪种存储技术,就像他们不必知道运行其pod所使用的物理服务器类型一样。当开发人员需要为他们的应用程序提供持久化的存储时,他们应该直接能够配置,就像在创建pod时配置CPU、内存和其他资源一样。
为了使应用程序能够在Kubernetes集群中请求存储,而不必处理基础设施的具体细节,K8S引入了两种新的资源:
使用方式如下所示:
如图所示,开发人员不需要在pod中使用特定的存储机制,集群管理员设置底层存储,然后通过kubernetes创建PV,并且指定其大小和支持的访问模式。
当开发人员需要在pod中使用持久存储时,他们首先创建一个PVC清单,指定所需的大小和访问模式。然后用户将PVC清单提交给kubernetes ,kubernetes找到适当的PV并将其绑定到PV上。
当使用了PV和PVC后,使用GCE Persisent Disk的机制如下所示:
总而言之,在pod中使用持久性存储的最佳方法是创建pvc(必要时使用显式指定的storageclassname),并由动态PersistentVolume Provider负责创建。
在K8S中使用存储机制总结如下:
在非k8s世界中,管理员可以通过在配置文件中指定IP地址或主机名,容许客户端访问,但在k8s中这种方式是行不通的。因为Pod 是有生命周期的,它们可以被创建或销毁。虽然通过 ReplicationController 能够动态地创建Pod,但当Pod被分配到某个节点时,K8s都会为其分配一个IP地址,而该IP地址不总是稳定可依赖的。因此,在 Kubernetes 集群中,如果一组 Pod(称为 backend)为其它 Pod (称为 frontend)提供服务,那么那些 frontend 该如何发现,并连接到这组backend的Pod呢?
Kubernetes中的Service是一种资源的定义,它将Pod逻辑分组,并提供客户端访问。
通过 Label Selector,一组 Pod 能够被暴露为Service,供客户端访问。
举个例子,考虑一个图片处理 backend,它运行了3个副本。这些副本是可互换的,通过Service能够解耦frontend与backend的关联。
frontend 不需要关心它们调用了哪个 backend 副本。 然而组成这一组 backend 程序的 Pod 实际上可能会发生变化,frontend 客户端不应该也没必要知道,而且也不需要跟踪这一组 backend 的状态。
一个 Service 在 Kubernetes 中是一个 REST 对象,和 Pod 类似。 像所有的 REST 对象一样, Service 定义可以基于 POST 方式,请求 apiserver 创建新的实例。 例如,假定有一组 Pod,它们对外暴露了 9376 端口,同时还被打上 “app=MyApp” 标签。
1 2 3 4 5 6 7 8 9 10 11 |
|
接下来,我们可以使用kubectl
访问该Service
可以通过如下三种方式,容许外部网络的Client访问Service:
NodePort
对于NotePort的Service,每个节点(Node)都会打开节点本身的端口,并将该端口上接收到的流量重定向到Service。
1 2 3 4 5 6 7 8 9 10 11 12 |
|
使用NodePort的Service如下图所示:
LoadBalancer
通过设置LoadBalancer
,Service可以通过一个专用的负载均衡器来访问(这个均衡器是运行kubernetes的基础设施提供的)。负载均衡器将流量重定向到所有节点上的节点端口。客户机通过负载均衡器的IP连接到服务。
如果kubernetes在不支持LoadBalancer服务的环境中运行,则不会提供负载均衡器,但该服务的行为仍将类似于NodePort服务。
1 2 3 4 5 6 7 8 9 10 11 |
|
使用如上配置文件创建Service之后,会调用云基础设施,创建负载均衡器并将其IP地址写入Service对象。一旦结束,IP地址将作为Service的外部IP地址列出:
如下图所示:
Ingress
资源这是一种完全不同的机制,通过一个IP地址公开多个服务,它在HTTP(网络层7)上运行,因此可以提供比第4层服务更多的功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
通过Ingress访问Service的流程如下:
另外,可以通过Ingress访问多个服务:
1 2 3 4 5 6 7 8 9 10 11 12 |
|
Service是Kubernetes的关键概念,也是令许多开发人员沮丧的根源。我见过许多开发人员耗费大量时间,来弄清楚为什么无法通过Servic IP或fqdn访问到自己的pods。 出于这个原因,简单介绍一下如何对服务进行故障排除。当无法通过Service访问您的pod时,可以从以下列表开始:
首先,确保从集群内而不是从外部连接到Service的集群IP。
不要费心Ping Service的IP来确定服务是否可以访问(记住,服务的集群IP是一个虚拟IP,Ping它永远不会工作)。
如果您已经定义了一个readiness probe,请确保它是成功的;否则Pod将不属于服务的一部分。
要确认Pod是服务的一部分,请使用kubectl get endpoints检查相应的endpoint对象。
如果您试图通过其fqdname或其一部分(例如,myservice.mynamespace.svc.cluster.local或myservice.mynamespace)访问服务,但该服务不起作用,请查看是否可以使用其群集IP而不是fqdname访问该服务。
检查您是否连接到服务公开的端口,而不是目标端口。
尝试直接连接到pod ip以确认pod是否接受正确端口上的连接。
如果你甚至不能通过pod的IP访问你的应用,确保你的应用是否绑定到locahost。
Service是K8s中重要的概念,你应该至少明白Service的这些内容:
通过Lable selector,将一组Pod设置为Service,并为Service配置静态的IP和端口
Service可以从Cluster内部访问,也可以通过设置为NodePort或者LoadBalancer的方式从外部访问
Pod可以通过环境变量获取Service的IP和Port,进行访问
可以将对Pod的关联关系,设置到Endpoint资源中,而简化label selector的方式
通过设置ServiceType为ExternalName
,可以访问外部的Service
通过Ingress可以设置多个Service被外部访问
使用pod的readiness probe可以决定pod是否被作为service的一部分
通过headless Service,可以使用DNS获取Pod的IP
《Kubernetes in action》 《Kubernetes handbook》
]]>这里已经讲的很详细了,请参考
Deployment为Pod和Replica Set(下一代Replication Controller)提供声明式更新。
您只需要在 Deployment 中描述期望的目标状态,Deployment controller 会帮您将 Pod 和ReplicaSet 的实际状态改变到您的目标状态。
典型的用例如下:
1 2 |
|
1 2 |
|
1 2 3 4 5 6 |
|
1 2 |
|
1 2 |
|
Deployment 在生命周期中有多种状态。在创建一个新的 ReplicaSet 的时候它可以是 progressing
状态, complete
状态,或者 fail to progress
状态。
在所有的 Kubernetes 配置中,Deployment 也需要apiVersion
,kind
和metadata
这些配置项。配置文件的通用使用说明查看 部署应用,配置容器,和 使用 kubectl 管理资源 文档。
.spec.template
是 .spec
中唯一要求的字段。
.spec.template
是 pod template. 它跟 Pod有一模一样的schema,除了它是嵌套的并且不需要apiVersion
和 kind
字段。
另外为了划分Pod的范围,Deployment中的pod template必须指定适当的label(不要跟其他controller重复了,参考selector)和适当的重启策略。
.spec.template.spec.restartPolicy
可以设置为 Always
, 如果不指定的话这就是默认配置。
.spec.replicas
是可以选字段,指定期望的pod数量,默认是1。
.spec.selector
是可选字段,用来指定 label selector ,圈定Deployment管理的pod范围。
如果被指定, .spec.selector
必须匹配 .spec.template.metadata.labels
,否则它将被API拒绝。如果 .spec.selector
没有被指定, .spec.selector.matchLabels
默认是 .spec.template.metadata.labels
。
在Pod的template跟.spec.template
不同或者数量超过了.spec.replicas
规定的数量的情况下,Deployment会杀掉label跟selector不同的Pod。
注意: 您不应该再创建其他label跟这个selector匹配的pod,或者通过其他Deployment,或者通过其他Controller,例如ReplicaSet和ReplicationController。否则该Deployment会被把它们当成都是自己创建的。Kubernetes不会阻止您这么做。
如果您有多个controller使用了重复的selector,controller们就会互相打架并导致不正确的行为。
.spec.strategy
指定新的Pod替换旧的Pod的策略。 .spec.strategy.type
可以是"Recreate"或者是 “RollingUpdate"。"RollingUpdate"是默认值。
.spec.strategy.type==Recreate
时,在创建出新的Pod之前会先杀掉所有已存在的Pod。
.spec.strategy.type==RollingUpdate
时,Deployment使用rolling update 的方式更新Pod 。您可以指定maxUnavailable
和 maxSurge
来控制 rolling update 进程。
.spec.strategy.rollingUpdate.maxUnavailable
是可选配置项,用来指定在升级过程中不可用Pod的最大数量。该值可以是一个绝对值(例如5),也可以是期望Pod数量的百分比(例如10%)。通过计算百分比的绝对值向下取整。如果.spec.strategy.rollingUpdate.maxSurge
为0时,这个值不可以为0。默认值是1。
例如,该值设置成30%,启动rolling update后旧的ReplicatSet将会立即缩容到期望的Pod数量的70%。新的Pod ready后,随着新的ReplicaSet的扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻可以用的Pod数量至少是期望Pod数量的70%。
.spec.strategy.rollingUpdate.maxSurge
是可选配置项,用来指定可以超过期望的Pod数量的最大个数。该值可以是一个绝对值(例如5)或者是期望的Pod数量的百分比(例如10%)。当MaxUnavailable
为0时该值不可以为0。通过百分比计算的绝对值向上取整。默认值是1。
例如,该值设置成30%,启动rolling update后新的ReplicatSet将会立即扩容,新老Pod的总数不能超过期望的Pod数量的130%。旧的Pod被杀掉后,新的ReplicaSet将继续扩容,旧的ReplicaSet会进一步缩容,确保在升级的所有时刻所有的Pod数量和不会超过期望Pod数量的130%。
.spec.progressDeadlineSeconds
是可选配置项,用来指定在系统报告Deployment的failed progressing ——表现为resource的状态中type=Progressing
、Status=False
、 Reason=ProgressDeadlineExceeded
前可以等待的Deployment进行的秒数。Deployment controller会继续重试该Deployment。未来,在实现了自动回滚后, deployment controller在观察到这种状态时就会自动回滚。
如果设置该参数,该值必须大于 .spec.minReadySeconds
。
.spec.minReadySeconds
是一个可选配置项,用来指定没有任何容器crash的Pod并被认为是可用状态的最小秒数。默认是0(Pod在ready后就会被认为是可用状态)。进一步了解什么什么后Pod会被认为是ready状态,参阅 Container Probes。
.spec.rollbackTo
是一个可以选配置项,用来配置Deployment回退的配置。设置该参数将触发回退操作,每次回退完成后,该值就会被清除。
.spec.rollbackTo.revision
是一个可选配置项,用来指定回退到的revision。默认是0,意味着回退到上一个revision。
Deployment revision history存储在它控制的ReplicaSets中。
.spec.revisionHistoryLimit
是一个可选配置项,用来指定可以保留的旧的ReplicaSet数量。该理想值取决于心Deployment的频率和稳定性。如果该值没有设置的话,默认所有旧的Replicaset或会被保留,将资源存储在etcd中,是用kubectl get rs
查看输出。每个Deployment的该配置都保存在ReplicaSet中,然而,一旦您删除的旧的RepelicaSet,您的Deployment就无法再回退到那个revison了。
如果您将该值设置为0,所有具有0个replica的ReplicaSet都会被删除。在这种情况下,新的Deployment rollout无法撤销,因为revision history都被清理掉了。
.spec.paused
是可以可选配置项,boolean值。用来指定暂停和恢复Deployment。Paused和没有paused的Deployment之间的唯一区别就是,所有对paused deployment中的PodTemplateSpec的修改都不会触发新的rollout。Deployment被创建之后默认是非paused。
Pod是一个服务的多个进程的聚合单位,pod通过提供一个更高级别的抽象的方式,极大简化了应用部署管理。 Pod作为一个独立的部署单位,支持横向扩展和复制、协同调度、命运共同体(例如被同时终结),协同复制,资源共享,依赖管理等,Pod会自动的为容器处理这些问题。
Pod是kubernetes中你可以创建和部署的最小也是最简的单位。Pod代表着集群中运行的进程。
Pod中封装着应用的容器(1或多个容器)、存储、独立的网络IP,并管理着容器运行的策略选项。
在Kubrenetes集群中Pod有如下两种使用方式:
一个Pod中运行一个容器。“每个Pod中一个容器”的模式是最常见的用法;在这种使用方式中,你可以把Pod想象成是单容器的封装,kuberentes管理的是Pod而不是容器。
在一个Pod中同时运行多个容器。一个Pod中也可以同时封装几个紧密耦合、互相协作的容器,它们之间共享资源。这些在同一个Pod中的容器互相协作,成为一个service单位——如一个容器共享文件,另一个容器更新文件。Pod将这些容器的存储资源作为一个实体来管理。
Pod 的 status 字段是一个 PodStatus 对象,PodStatus中有一个 phase 字段。
下面是 phase 可能的值:
下图是Pod的生命周期示意图,从图中可以看到Pod状态的变化。
Pod中可以同时运行多个容器(独立进程运行)并协同工作。同一个Pod中的容器会自动的分配到同一个node 上运行,同时,同一个Pod中的容器共享存储、网络和依赖,它们总是被同时调度。
网络
每个Pod都会被分配唯一的一个IP地址。Pod中的所有容器共享网络空间,包括IP地址和端口。Pod内部的容器可以使用localhost互相通信。Pod中的容器与外界通信时,必须分配共享网络资源(例如使用宿主机的端口映射)。
存储
可以为一个Pod指定多个共享的Volume。Pod中的所有容器都可以访问共享的volume。Volume也可以用来持久化Pod中的存储资源,以防容器重启后文件丢失。
Pod中应用容器共享网络空间(IP地址和端口),因此可以通过localhost互相发现。 Pod中应用容器的hostname被设置成Pod的名字。 Pod中应用容器可以共享volume。volume能够保证pod重启时使用的数据不丢失。
Pod也可以用于垂直应用(例如LAMP),这样使用的动机是为了支持共同调度和协调应用程序,例如: * 内容管理系统、文件和数据加载器、本地换群管理器等。 * 日志和检查点备份、压缩、旋转、快照等。 * 数据变更观察者、日志和监控适配器、活动发布者等。 * 代理、桥接和适配器等。 * 控制器、管理器、配置器、更新器等。
Pod不是作为持久化设计的。在调度失败、节点故障、缺少资源或者节点维护的状态下都会失败。
通常,用户不需要手动直接创建Pod,而是应该使用controller(例如Deployments),即使是在创建单个Pod的情况下。Controller可以提供集群级别的自愈功能、复制和升级管理。
Kubernetes提供了一个准入控制器(PodPreset),当其启用时,Pod Preset 会将应用创建请求传入到该控制器上。因此,Pod Preset
是用来在 Pod 被创建的时候向其中注入额外的运行时需求的 API 资源。
您可以使用label selector
来指定为哪些 Pod 应用 Pod Preset。
Controller可以创建和管理多个Pod,提供副本管理、滚动升级和集群级别的自愈能力。例如,如果一个Node故障,Controller就能自动将该节点上的Pod调度到其他健康的Node上。
包含一个或者多个Pod的Controller示例:
通常,Controller会用你提供的Pod Template来创建相应的Pod。
Init是一种专用的容器,在应用程序容器启动之前运行,包含应用镜像中工具或环境的安装脚本。
Pod 能够有一个或多个先于应用容器启动的 Init 容器。
Init 容器与普通的容器非常像,除了如下两点: * Init 容器总是运行到成功完成为止。 * 每个 Init 容器都必须在下一个 Init 容器启动之前成功完成。
如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init容器成功为止。然而,如果 Pod 对应的 restartPolicy 为 Never,它不会重新启动。
因为 Init 容器具有与应用程序容器分离的单独镜像,所以它们的启动相关代码具有如下优势:
它们可以包含并运行实用工具,它们可以包含使用工具和定制化代码来安装。例如,创建镜像没必要FROM 另一个镜像,只需要在安装过程中使用类似 sed、 awk、 python 或 dig 这样的工具。
应用程序镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像。
Init 容器使用 Linux Namespace,所以相对应用程序容器来说具有不同的文件系统视图。因此,它们能够具有访问 Secret 的权限,而应用程序容器则不能。
它们必须在应用程序容器启动之前运行完成,所以 Init 容器能够提供一种简单的阻塞或延迟应用容器的启动的方法,直到满足了一组先决条件。
Pod 安全策略 是集群级别的资源,它能够控制 Pod 运行的行为,以及它具有访问什么资源的能力。
]]>Kubernetes集群内部存在三类IP,分别是:
Node是kubernetes集群的工作节点,可以是物理机也可以是虚拟机。
Address
Condition
Capacity
Info:节点的一些版本信息,如OS、kubernetes、docker等
在一个Kubernetes集群中可以使用namespace创建多个“虚拟集群”,这些集群之间可以完全隔离。如当项目和人员众多的时候可以考虑根据项目属性,例如生产、测试、开发划分不同的namespace。
另外,也可以让一个namespace中的service访问到其他的namespace中的服务。
Label是附在对象上(例如Pod)的键值对。可以在创建对象的时候指定,也可以在对象创建后随时指定。Labels的值对系统本身并没有什么含义,只是对用户有意义。通过label selector,客户端/用户可以指定一个object集合,通过label selector对object的集合进行操作。
Annotation,可以将Kubernetes资源对象关联到任意的非标识性元数据。使用客户端(如工具和库)可以检索到这些元数据。
Label和Annotation都可以将元数据关联到Kubernetes资源对象。Label主要用于选择对象,可以挑选出满足特定条件的对象。相比之下,annotation 不能用于标识及选择对象。annotation中的元数据可多可少,可以是结构化的或非结构化的,也可以包含label中不允许出现的字符。
]]>安全性(Safety)
保证系统的稳定,保证系统不会崩溃,不会出现业务错误,不会做坏事,是严格约束的。
活性(Liveness)
使得系统可以提供功能,提高性能,增加易用性,让系统可以在用户“看到的时间内”做些好事,是尽力而为的。
从Kubernetes的系统架构和设计来看,存在两个最核心的设计理念,符合Lamport的理论:
容错性
容错性实际是保证Kubernetes系统稳定性和安全性的基础
易扩展性
易扩展性是保证对变更友好,可以快速迭代增加新功能的基础
在K8S中,支撑核心设计理念的背后,存在着诸多领域对象,本文主要梳理了K8S系统中存在的主要概念。
类别 | 名称 |
---|---|
资源对象 | Pod、ReplicaSet、ReplicationController、Deployment、StatefulSet、DaemonSet、Job、CronJob、HorizontalPodAutoscaling、Node、Namespace、Service、Ingress、Label、CustomResourceDefinition |
存储对象 | Volume、PersistentVolume、Secret、ConfigMap |
策略对象 | SecurityContext、ResourceQuota、LimitRange |
身份对象 | ServiceAccount、Role、ClusterRole |
Pod是在Kubernetes集群中运行部署应用或服务的最小单元,它是可以支持多容器的。Pod的设计理念是支持多个容器在一个Pod中共享网络地址和文件系统,可以通过进程间通信和文件共享这种简单高效的方式组合完成服务。 目前Kubernetes中的业务主要可以分为长期伺服型(long-running)、批处理型(batch)、后台支撑型(node-daemon)和有状态应用型(stateful application);分别对应的控制器为Deployment、Job、DaemonSet和StatefulSet,
部署表示用户对Kubernetes集群的一次更新操作。部署是一个比RS应用模式更广的API对象,可以是创建一个新的服务,更新一个新的服务,也可以是滚动升级一个服务。滚动升级一个服务,实际是创建一个新的RS,然后逐渐将新RS中副本数增加到理想状态。以Kubernetes的发展方向,未来对所有长期伺服型的的业务的管理,都会通过Deployment来管理。
RC、RS和Deployment只是保证了支撑服务的微服务Pod的数量,但是没有解决如何访问这些服务的问题。一个Pod只是一个运行服务的实例,随时可能在不同节点上停止或者启动,因此不能确定其IP和端口号提供服务。那谁来稳定地提供
服务发现
和负载均衡
的能力呢?在K8集群中,客户端要访问的服务就是Service对象。 每个Service会对应一个集群内部有效的虚拟IP,集群内部通过虚拟IP访问服务。 在K8集群中微服务的负载均衡是由Kube-proxy实现的。Kube-proxy是Kubernetes集群内部的负载均衡器。它是一个分布式代理服务器,在Kubernetes的每个节点上都有一个。这一设计体现了它的伸缩性优势,需要访问服务的节点越多,提供负载均衡能力的Kube-proxy就越多,高可用节点也随之增多。
Kubernetes集群中的计算能力由Node提供,最初Node称为服务节点Minion,后来改名为Node。Kubernetes集群中的Node也就等同于Mesos集群中的Slave节点,是所有Pod运行所在的工作主机,可以是物理机也可以是虚拟机。不论是物理机还是虚拟机,工作主机的统一特征是上面要运行kubelet管理节点上运行的容器。
命名空间为Kubernetes集群提供虚拟的隔离作用,Kubernetes集群初始有两个命名空间,分别是默认命名空间default和系统命名空间kube-system,除此以外,管理员可以可以创建新的命名空间满足需要。
Job是Kubernetes用来控制批处理型任务的API对象。批处理业务与长期伺服业务的主要区别是批处理业务的运行有头有尾,而长期伺服业务在用户不停止的情况下永远运行。Job管理的Pod把任务成功完成就自动退出了。成功完成的标志根据不同的spec.completions策略而不同:单Pod型任务有一个Pod成功就标志完成;多任务的成功保证有N个任务全部执行成功;工作队列型任务根据应用确认的全局成功而标志成功。
API对象是Kubernetes集群中的管理操作单元。Kubernetes集群系统每支持一项新功能,引入一项新概念,一定会新引入对应的API对象,支持对该功能的管理操作。例如副本集Replica Set对应的API对象是RS。 每个API对象都有3大类属性:元数据metadata、规范spec和状态status
RC是Kubernetes集群中最早的保证Pod高可用的API对象。通过监控运行中的Pod来保证集群中运行指定数目的Pod副本。
RS是新一代RC,提供同样的高可用能力,区别主要在于RS后来居上,能支持更多种类的匹配模式。副本集对象一般不单独使用,而是作为Deployment的理想状态参数使用。
长期伺服型和批处理型服务的核心在业务应用,可能有些节点运行多个同类业务的Pod,有些节点上又没有这类Pod运行;而后台支撑型服务的核心关注点在Kubernetes集群中的节点(物理机或虚拟机),要保证每个节点上都有一个此类Pod运行。节点可能是所有集群节点也可能是通过nodeSelector选定的一些特定节点。典型的后台支撑型服务包括,存储,日志和监控等在每个节点上支持Kubernetes集群运行的服务。
在云原生应用的体系里,有下面两组近义词;第一组是无状态(stateless)、牲畜(cattle)、无名(nameless)、可丢弃(disposable);第二组是有状态(stateful)、宠物(pet)、有名(having name)、不可丢弃(non-disposable) RC和RS主要是控制提供无状态服务的,其所控制的Pod的名字是随机设置的,一个Pod出故障了就被丢弃掉,在另一个地方重启一个新的Pod,名字变了和启动在哪儿都不重要,重要的只是Pod总数;而StatefulSet是用来控制有状态服务,StatefulSet中的每个Pod的名字都是事先确定的,不能更改。 对于RC和RS中的Pod,一般不挂载存储或者挂载共享存储,保存的是所有Pod共享的状态,Pod像牲畜一样没有分别(这似乎也确实意味着失去了人性特征);对于StatefulSet中的Pod,每个Pod挂载自己独立的存储,如果一个Pod出现故障,从其他节点启动一个同样名字的Pod,要挂载上原来Pod的存储继续以它的状态提供服务。 适合于StatefulSet的业务包括数据库服务MySQL和PostgreSQL,集群化管理服务ZooKeeper、etcd等有状态服务。使用StatefulSet,Pod仍然可以通过漂移到不同节点提供高可用,而存储也可以通过外挂的存储来提供高可靠性,StatefulSet做的只是将确定的Pod与确定的存储关联起来保证状态的连续性。
在云计算环境中,服务的作用距离范围从近到远一般可以有:同主机(Host,Node)、跨主机同可用区(Available Zone)、跨可用区同地区(Region)、跨地区同服务商(Cloud Service Provider)、跨云平台。Kubernetes的设计定位是单一集群在同一个地域内,因为同一个地区的网络性能才能满足Kubernetes的调度和计算存储连接要求。而联合集群服务就是为提供跨Region跨服务商Kubernetes集群服务而设计的。 每个Kubernetes Federation有自己的分布式存储、API Server和Controller Manager。用户可以通过Federation的API Server注册该Federation的成员Kubernetes Cluster。当用户通过Federation的API Server创建、更改API对象时,Federation API Server会在自己所有注册的子Kubernetes Cluster都创建一份对应的API对象。在提供业务请求服务时,Kubernetes Federation会先在自己的各个子Cluster之间做负载均衡,而对于发送到某个具体Kubernetes Cluster的业务请求,会依照这个Kubernetes Cluster独立提供服务时一样的调度模式去做Kubernetes Cluster内部的负载均衡。而Cluster之间的负载均衡是通过域名服务的负载均衡来实现的。
用户帐户为人提供账户标识,对应的是人的身份,人的身份与服务的namespace无关,所以用户账户是跨namespace的
服务账户为计算机进程和Kubernetes集群中运行的Pod提供账户标识。服务帐户对应的是一个运行中程序的身份,与特定namespace是相关的。
Secret是用来保存和传递密码、密钥、认证凭证这些敏感信息的对象。为了避免将类似的敏感信息明文写在配置文件中,可以将其存入一个Secret对象,并在配置文件中通过Secret对象引用这些敏感信息
RBAC主要是引入了角色(Role)以及与角色绑定(RoleBinding)的相关抽象概念。
除了核心组件,还有一些推荐的插件,其中有的已经成为CNCF中的托管项目:
生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
Kubernetes外部:日志、监控、配置管理、CI/CD、Workflow、FaaS、OTS应用、ChatOps、GitOps、SecOps等
Kubernetes内部:CRI、CNI、CSI、镜像仓库、Cloud Provider、集群自身的配置和管理等
关于分层架构,可以关注下Kubernetes社区正在推进的Kubernetes architectual roadmap和slide。
Borg, Omega, and Kubernetes - Lessons learned from three container-management systems over a decade
]]>过去的几个月,我作为独立咨询师,为多个传统企业提供了微服务架构的培训、咨询以及交付工作。实际上,传统企业在过去多年的业务积累中,由于组织架构、业务发展和市场竞争等综合因素,技术体系相对封闭,缺乏快速交付的理念。因此,微服务的出现,加之社区的热捧,导致很多传统的团队过于追热而并没有完全理解微服务。
经过2015年的快速普及,微服务的优势被越来越多的传统组织和企业所认可,但由于架构相关的知识本身比较抽象,虽然各大会议上有很多互联网公司的案例分享,但开发者似乎依然很难全面了解微服务架构。
所以,希望通过本系列的文章以及视频,以一个真实的案例为背景,以持续交付和DevOps为主线,帮助初学者理解微服务架构,并能通过动手实验,了解相关的实践以及方法论。
精彩课程已经出炉,请移步这里
REST API通常作为服务间协作的轻量级通信协议(语言无关、平台无关),被微服务架构广泛采用。
在微服务架构的中,如何有效的设计REST API,如何处理API响应中资源的依赖关系,服务规模化后如何提高服务团队间的协作效率…..这些都成为微服务实践中API设计面临的挑战。
本篇将介绍REST、REST成熟度模型、为什么使用HAL以及HAL的核心。
REST(Representational State Transfer,表述性状态传递)是近几年使用广泛的架构风格之一。在微服务架构的实践中,REST经常作为服务间协作的轻量级通信协议(语言无关、平台无关)被采用。
REST从语义层面将响应结果定义为资源,并使用HTTP协议的标准动词映射为对资源的操作,形成了一种以资源为核心、以HTTP协议为操作方式的,与语言无关、平台无关的服务间的通信机制。
通过资源表述、状态转移以及统一接口,REST将客户端的请求、服务器端的响应基于资源联系起来,形成一种以资源为核心、以HTTP协议为操作方式的,与语言无关、平台无关的通信机制。
同时,由于HTTP协议本身的无状态性,使用REST,能够有效保持服务应用的无状态型,并利于水平伸缩。
REST成熟度模型描述了REST在实施过程中不同的级别。
随着组织业务的不断增长,服务规模化的实施,以及响应内容复杂度的增加,REST的使用面临如下两个挑战:
使用REST,需要将业务场景的响应抽象为资源,并基于JSON或者XML的格式,返回给客户端。随着业务复杂度的增加,响应的内容会愈发复杂。
REST架构风格,并没有定义响应结构应该遵循什么标准。这也就意味着,在企业内部,不同的部门,不同的开发团队,对同一类资源,所定义的结构可能不尽相同;
譬如,如下是服务器端对客户端获取产品请求的的响应结果,两种结构都是合理的,但存在着差异
GET http://bookstore.com/books/12
Accept: application/json
响应结构一
{
"name":"Spring Boot In Action",
"category": "Book",
"price":69.00,
"ref": "http://bookstore.com/books/12",
"created_at": "2015-05-01 10:00:00",
"updated_at": "2015-06-01 11:00:00"
}
响应结构二
{
"basic_info":
{
"name":"Spring Boot In Action",
"category":"Book"
"price":69.00,
},
"ref":{
"self": "http://bookstore.com/books/12”,
“list": "http://bookstore.com/books"
},
"timestamp":{
"created_at": "2015-05-01 10:00:00",
"updated_at": "2015-06-01 11:00:00"
}
}
因此,如何定义一套标准的资源响应结构,成为服务规模化后使用REST面临的一个挑战。
在《Richardson Maturity Model》模型中,定义了REST API不同成熟度应该具备的特征。
对于REST API Level 3,明确提出了"资源跳转的重要性",即HATEOAS
。
对于实际情况而言,大部分REST的实现,都是基于JSON作为传输格式,不过JSON最大的遗憾,正如W3C所描述的:
JSON has no built-in support for hyperlinks, which are a fundamental building block on the Web.
没有对超链接处理做内建的支持,是JSON最大的遗憾。而这部分却恰恰是互联网的基石。
这带来的潜在问题是,对于调用接口的Consumer而言,需要通过查看相关文档,才能了解如何获取相关的资源信息。譬如,某些社交系统可能会提供类似如下的接口文档,来帮助Consumer了解如何使用其提供的接口。
https://api.example.com/users/1234567890 GET 获取用户明细
https://api.example.com/users/[ID]/friends GET 获取用户相关的好友
https://api.example.com/users/[ID]/posts GET 获取用户相关的文章
HAL(Hypertext Application Language)是一种轻量级超文本应用描述协议。HAL的实现基于REST,并对REST中资源结构无法标准化和不支持资源间跳转链接做了有效的互补。
目前,越来越多的企业和组织开始使用HAL提供标准化的服务接口,譬如
更多案例请可以参考HAL官方网站。
在HAL中,任何响应都被定义成一种资源(Resource),这是遵循REST原则对资源的定义标准。 同REST不同的是,在每个资源中,HAL又将其分成了如下三个标准的部分:
对于单一资源而言,如果没有使用HAL,通常我们会定义成这样:
GET - /api/users/wldandan
Content-Type: application/json
{
"id": "wldandan",
"name": "Wang Lei",
"email": "useremail@email.com",
"wechat": "abcdefg"
}
如果要访问用户相关的联系人资源,则可能需要查看文档获取相应的API接口,或者将相关信息放在之前返回的结果里:
{
"id": "wldandan",
"name": "Wang Lei",
"email": "useremail@email.com",
"wechat": "abcdefg",
"contacts": [
{
"id": "chenyue",
"name": "Chen Yue"
"link": "/api/users/chenyue"
},
{
"id": "kouxi",
"name": "Kou Xi"
"link": "/api/users/chenyue"
}
]
}
如果基于HAL,则可以使用_links
描述相关链接,同时使用_embedded
描述嵌套资源,类似如下:
{
"_links": {
"self": {
"href": "http://example.org/api/users/wldandan"
}
},
"id": "wldandan",
email: 'useremail@email.com',
"name": "Wang Lei"
wechat: 'abcdefg',
"_embedded": {
"contacts": [
{
"_links": {
"self": {
"href": "http://example.org/api/users/chenyue"
}
},
"id": "chenyue",
"name": "Chen Yue"
},
{
"_links": {
"self": {
"href": "http://example.org/api/users/kouxi"
}
},
"id": "kouxi",
"name": "Kou Xi"
}
]
}
}
对于集合资源而言,如果没有使用HAL,通常我们会定义成这样:
GET - /api/users
Content-Type: application/json
{
total: 10
page: 5
page_size: 2
users: [
{
id: 'wldandan',
name: 'Wang Lei'
},
{
id: 'chenyue',
name: 'Chen Yue'
},
]
}
基于HAL,则使用_links
描述相关链接,同时使用_embedded
描述嵌套资源,类似则如下所示:
{
“links”: {
“self”: {
“href”: “http://example.org/api/users?page=3”
},
“first”: {
“href”: “http://example.org/api/users”
},
“prev”: {
“href”: “http://example.org/api/users?page=2”
},
“next”: {
“href”: “http://example.org/api/users?page=4”
},
“last”: {
“href”: “http://example.org/api/users?page=5”
}
},
“page_size”: 2,
“total”: 10,
“page”: 5,
“embedded”: {
“users”: [
{
“links”: {
“self”: {
“href”: “http://example.org/api/users/wldandan”
}
},
“id”: “wldandan”,
“name”: “Wang Lei”
},
{
“links”: {
“self”: {
“href”: “http://example.org/api/user/chenyue”
}
},
“id”: “chenyue”,
“name”: “Chen Yue”
}
]
}
}
所以,HAL的最大价值,帮助我们标准化定义了Resource的结构,并同时实现了HATEOAS。
关于课程更多内容,请看这里
Hypertext Application Language
Creating Service Contract with AutoRest, Swagger and HAL
Implementing HAL hypermedia REST API using Spring HATEOAS
]]>过去的几个月,我作为独立咨询师,为多个传统企业提供了微服务架构的培训、咨询以及交付工作。实际上,传统企业在过去多年的业务积累中,由于组织架构、业务发展和市场竞争等综合因素,技术体系相对封闭,缺乏快速交付的理念。因此,微服务的出现,加之社区的热捧,导致很多传统的团队过于追热而并没有完全理解微服务。
经过2015年的快速普及,微服务的优势被越来越多的传统组织和企业所认可,但由于架构相关的知识本身比较抽象,虽然各大会议上有很多互联网公司的案例分享,但开发者似乎依然很难全面了解微服务架构。
所以,希望通过本系列的文章以及视频,以一个真实的案例为背景,以持续交付和DevOps为主线,帮助初学者理解微服务架构,并能通过动手实验,了解相关的实践以及方法论。
精彩课程已经出炉,请移步这里
在上一节中,我们学习了Spring Boot的核心,明白了Starter
与AutoConfiguration
的重要性,并知道了如何快速创建Spring Boot应用。
基于现有的Spring Framework
,以及Spring Boot的Starter
,加上官方CLI
、 Initializr
以及各种IDE
提供的快速创建SpringBoot 应用的方式,我们能轻松的完成一个基于RESTful API的服务实现。
因此,从Spring Boot的优势来看,构建单个的服务单元非常容易了~
从IT社区的发展来看,工具正在变得越来越强大,开发人员的大部分重复性工作都将会被简化。
伴随着人工智能的快速发展,将来编码的工作都可以交给机器了,喝着咖啡告诉它,你想要什么语言,你的验收条件,啦啦啦….
但是,对于微服务的实施而言,服务单元不会孤立存在,必然相互协作,共同实现业务价值。
而随着服务规模化的推进,服务间协作和管理的成本会越来越高,包括但不限于:
所以,如何应对如上这些问题,成为微服务实施中重要的环节。
Spring Cloud
是Pivotal官方提供的旨在帮助开发者降低构建复杂分布式系统的工具集。
2015年3月4日,Spring Cloud发布了第一个GA版本。
Spring Cloud的核心宗旨是:
A toolset designed for building complexed distributed systems.
在Spring Cloud中,整合了很多功能组件,包括Config、Messaging、Netflix OSS以及对Heroku、Amazon Web Service、Cloud Foundry等云平台的接口支持。
基于这些组件,能够帮助我们解决之前提到的服务实施后面临的挑战。
Spring Cloud中的组件很多,而且在快速的演进中,在本系列服务构建篇
里,主要涉及的有
Spring Cloud Netflix
集成了Netflix OSS的组件(Eureka/Ribbon/Hystrix/Zuul等)
Spring Cloud Config
提供集中化的服务配置信息,动态更新实例的配置
Spring Cloud Bus
使用分布式消息机制,提供不同服务实例之间的协作
Spring Cloud Security
提供服务安全相关的实现机制(OAuth2)
]]>Spring Cloud本着
全家桶
的一站式解决方案,为微服务的实施提供了支持。虽然Spring Cloud GA的时间并不长,但其快速的演进以及大量社区用户的支持,已成为Java领域微服务架构实施的利器,帮助我们有效的应对服务实践时的构建支撑组件的挑战。
后续我们将使用Spring Cloud构建本案例服务的支撑组件。
关于课程,请看这里
过去的几个月,我作为独立咨询师,为多个传统企业提供了微服务架构的培训、咨询以及交付工作。实际上,传统企业在过去多年的业务积累中,由于组织架构、业务发展和市场竞争等综合因素,技术体系相对封闭,缺乏快速交付的理念。因此,微服务的出现,加之社区的热捧,导致很多传统的团队过于追热而并没有完全理解微服务。
经过2015年的快速普及,微服务的优势被越来越多的传统组织和企业所认可,但由于架构相关的知识本身比较抽象,虽然各大会议上有很多互联网公司的案例分享,但开发者似乎依然很难全面了解微服务架构。
所以,希望通过本系列的文章以及视频,以一个真实的案例为背景,以持续交付和DevOps为主线,帮助初学者理解微服务架构,并能通过动手实验,了解相关的实践以及方法论。
精彩课程已经出炉,请移步这里
在Java开发领域,估计没有多少兄弟不知道Spring,当年的SSH组合,风靡社区,几乎成为J2EE 程序开发的标配。
另外,Spring对诸多企业特性的强大支持,为构建Java的企业应用提供了全家桶
的解决方案。
对于现代Java开发,尤其是以微服务为主的应用,虽然有DropWizard、KumuluzEE等微服务框架的诞生,但Spring Boot借助极致的Convention Over Configuration
,加上对Spring Framework
的无缝支持,被社区普遍看好。
在今年10月中旬结束的2016 JAX Java Innovation评选中,Spring Boot一举拔得头筹,而去年Java领域的这个奖项是颁给了著名的Netflix OSS,足以证明Spring Boot在社区的影响力。
快速构建
可运行的应用
无XML配置
内嵌
WebServer(Tomcat/Jetty/Undertow)注解
的方式,一行代码启动应用自动配置
和装载机制
Metrics/health
显示健康监控状态Trace/dump
显示调用/调试信息1.Starter
Starter负责将申明的依赖Jar包导入到当前ClassPath。
每个Starter都提供一个spring.providers文件
,申明当前Starter依赖的Jar包。
譬如,Spring-boot-starter-web
中的spring.providers文件
描述的依赖如下所示:
1
|
|
包括spring-webmvc
,spring-web
,jackson-databind
,分别提供mvc,web和JSON解析绑定的功能。
欲了解更多的Starter,请移步官方列表
2.AutoConfiguratioin
AutoConfiguratioin根据ConditionalOnXxx
条件,使用@Bean
注解,完成对Bean的创建和组装。
根据不同的ConditionalOnXxx
,AutoConfiguration可以根据合适的场景创建并组装Bean。
譬如
1
|
|
表示当前指定的A实例不存在,才创建@Bean.
1
|
|
表示当前是在Web Application才创建@Bean
Java Web中我们经常使用HttpEncodingAutoConfiguration
,完成UTF8的转码。如下是SpringBoot中,HttpEncodingAutoConfiguration的部分实现:
仅当ApplicationContext中没有CharacterEncodingFilter的时候,才会创建
CharacterEncodingFilter
1 2 3 4 5 6 7 8 9 |
|
所以, 我对SpringBoot的理解为,如下两个核心点(配合起来,天下无敌….)
Starter打包提供相关的包依赖,加载到ClassPath
EnableAutoConfiguration借助ConditionalOnXxx条件,创建并配置Bean的依赖关系
关于更多Conditional
的细节,请查看包
org.springframework.boot.autoconfigure.condition中的具体实现,其列表如下图所示:
这个标签是SpringBoot应用的核心标签,主要包括三部分子:
Spring3.0引入@Configuration(Java配置),使用Java配置简化XML配置。
譬如以前我们通常使用类似如下XML配置Spring Bean
1 2 3 4 5 6 7 |
|
通过使用Java Configuration,声明当前类是一个配置类, 相当于声明一个Spring配置的XML文件
1 2 3 4 5 6 7 8 |
|
它帮助我们加载当前Spring Boot中META-INF/spring.factories,并使用其中的*AutoConfiguration
1 2 3 4 |
|
每个AutoConfiguration都会根据其中的Condition条件,在合适的场景完成对相关Bean的创建
1 2 3 4 |
|
它定义Spring了自动加载Bean的根路径,是Spring Framwork中较早的一个标签,这里就不赘述了。
Spring Boot提供了方便的项目创建方式,使得我们可以快速创建基于SpringBoot的项目:
]]>本部分介绍了SpringBoot的核心,并提到了快速创建SpringBoot应用的方式,通过这部分内容,能够帮助大家完成
SpringBoot从0到1的过程
:)关于课程,请看这里
过去的几个月,我作为独立咨询师,为多个传统企业提供了微服务架构的培训、咨询以及交付工作。在这些企业中,大部分的开发者对微服务的理解,以“银弹观念”为主。实际上,传统企业在过去多年的业务积累中,由于组织架构、业务发展和市场竞争等综合因素,技术体系相对封闭,缺乏快速交付的理念。因此,微服务的出现,加之社区的热捧,导致这种现象出现也是比较能理解的。
经过2015年的快速普及,微服务的优势被越来越多的传统组织和企业所认可,但由于架构相关的知识本身比较抽象,虽然各大会议上有很多互联网公司的案例分享,但开发者似乎依然很难全面了解微服务架构。
所以,希望通过本系列的文章,以一个模拟的案例为背景,以持续交付和DevOps为主线,帮助初学者理解微服务架构,并能通过动手实验,了解相关的实践以及方法论。
精彩课程已经出炉,请移步这里
构建一个用户查看活动、报名活动、接收通知的系统
- 匿名用户可查看活动列表
- 匿名用户可以查看活动详情
- 匿名用户可以查看相关活动推荐和评论
- 用户登陆成功后完成报名
- 报名成功,用户获取通知
关于服务的划分,是一个非常有深度的话题,与业务场景、技术实现、团队能力有着密不可分的关系。 从方法论上有:
- 根据DDD,包括业务上下文、事件驱动、读写分离等
- 根据名词类对象,譬如商品、订单等
- 根据动词类行为,譬如支付,预订等
- 其他切入点
在这个模拟场景中,为了保持简洁,我假定使用名词和动词进行划分,包括如下:
活动服务Event-service
(提供活动的列表和活动详情的相关数据)推荐服务Recommendation-service
(提供与某个活动相关的推荐信息)评论服务Review-service
(提供与某个活动相关的评论信息)活动聚合服务Event-composite-service
(聚合服务 - 提供某个活动及其相关的推荐、评论信息报名服务Enroll-service
(为登录用户提供报名)通知服务Notification-service
(用户报名成功后获取通知)该活动报名系统的应用架构图如下:
该活动报名系统的微服务生态系统图如下:
]]>通过理论+模拟+实战的方式,梳理微服务的生态系统,并以持续交付和DevOps的实施为主线,体 系化的形成微服务从0到1的学习过程。
关于课程,请看这里
[如需转载,请联系本人]
过去的几个月,我作为独立咨询师,为多个传统企业提供了微服务架构的培训、咨询以及交付工作。实际上,传统企业在过去多年的业务积累中,由于组织架构、业务发展和市场竞争等综合因素,技术体系相对封闭,缺乏快速交付的理念。因此,微服务的出现,加之社区的热捧,导致很多传统的团队过于追热而并没有完全理解微服务。
经过2015年的快速普及,微服务的优势被越来越多的传统组织和企业所认可,但由于架构相关的知识本身比较抽象,虽然各大会议上有很多互联网公司的案例分享,但开发者似乎依然很难全面了解微服务架构。
所以,希望通过本系列的文章以及视频,以一个真实的案例为背景,以持续交付和DevOps为主线,帮助初学者理解微服务架构,并能通过动手实验,了解相关的实践以及方法论。
精彩视频课程已经出炉,请移步这里
微服务生态系统
和持续交付
为指导原则模拟案例实战
为主,并使用SpringBoot
和Spring Cloud
实现服务服务构建
与服务实施
两个专题,包括应用架构
,部署模型
和交付流水线
持续交付体系
微服务架构生态系统
REST & HAL & HAL Browser
的使用方式Spring Boot
的核心与使用Spring Cloud
的服务支撑组件微服务生态系统
PACT契约测试
验证服务接口OAuth
与JWT
实现服务的安全RESTful API
设计相关基于消费者驱动的契约测试
Docker私有仓库
,并将服务发布成Docker镜像Docker搭建Jenkins
持续交付流水线Pipeline as Code
的方式管理流水线ELK实现日志聚合
的实践Prometheus
实现监控告警的实践Rancher
完成服务Docker镜像的部署部署模型图
持续交付流水线
]]>通过理论+模拟+实战的方式,梳理微服务的生态系统,并以持续交付和DevOps的实施为主线,体 系化的形成微服务从0到1的学习过程。
关于课程,请看这里
过去的几个月,我作为独立咨询师,为多个传统企业提供了微服务架构的培训、咨询以及交付工作。实际上,传统企业在过去多年的业务积累中,由于组织架构、业务发展和市场竞争等综合因素,技术体系相对封闭,缺乏快速交付的理念。因此,微服务的出现,加之社区的热捧,导致很多传统的团队过于追热而并没有完全理解微服务。
经过2015年的快速普及,微服务的优势被越来越多的传统组织和企业所认可,但由于架构相关的知识本身比较抽象,虽然各大会议上有很多互联网公司的案例分享,但开发者似乎依然很难全面了解微服务架构。
所以,希望通过本系列的文章以及视频,以一个模拟的案例为背景,以持续交付和DevOps为主线,帮助初学者理解微服务架构,并能通过动手实验,了解相关的实践以及方法论。
精彩课程已经出炉,请移步这里
微服务架构提倡将单一应用程序
划分成一组小的服务
,每个服务运行在独立的进程中
,服务间采用轻量级的通信机制
互相协作(通常是基于 HTTP 协议的 RESTful API ),每个服务都围绕着具体业务进行构建,并且能被独立的开发、测试、构建、部署和交付
。
客观来说,微服务架构所涉及的内容,已经不仅仅是架构本身
,还包括了持续集成
、持续交付
、自动化测试
、部署/运维、监控
以及DevOps
等多个方面,这些方面互相配合、相辅相成,才能在微服务实施的过程中展现威力。
一个完整的微服务系统,在实施的过程中,可能还要考虑服务的公共支撑部分
,包括但不限于:
日志聚合
(将不同服务实例上的日志聚合起来,便于分析、统计和定位问题)监控
和告警
(监控每个服务的状态,必要时产生告警)注册和发现
机制认证
和鉴权
服务构建
和打包机制
接口测试
持续集成
/持续交付流水线
服务依赖
关系管理对于这么多的内容,如果能基于模拟场景的理解与练习,再借鉴各大会议上诸多公司的微服务实施案例,能够快速理解并开始尝试。
2015年4月,我开始撰写《微服务架构与实践》一书,当时国内微服务架构的概念还谈的比较少,所以《微服务架构与实践》侧重讲了微服务的理论基础和诞生背景,并分享了我在ThoughtWorks就职期间,实现微服务的工作方式(我们以持续交付和DevOps为核心,构建服务开发模板,并基于AWS实现快速发布,快速监控,快速反馈)。同时,也探讨了我们如何将遗留系统改造成微服务架构的策略和步骤,但由于精力有限,很多细节无法一一展开讨论。
另外,书中的大部分例子都是以Ruby和AWS
为主,很多读者也提出了建议,希望给出采用Java实现的例子,更容易理解和使用。
当今时代,技术发展的速度越来越快,诞生的框架、工具越来越多,虽然有心更新书的内容,但时间和精力都无法快速更新并发布新版,所以,这次希望能通过这个系列,先将过去的一些实践细化,包括DevOps、持续集成、持续交付以及使用Pact等实践,并采用Java(基于Spring Boot/Spring Cloud的快速发展)实现一个模拟的案例。
过去两年微服务的快速发展,已经有很多传统企业开始尝试使用微服务解耦业务系统。传统企业和互联网企业的业务形态不一样,互联网公司业务变化快,响应速度快,组织愿意积极尝试开源的工具和方法论,降低交付成本和缩短交付周期。互联网领域中,大部分的产品,本身就是基于分布式系统构建,所以微服务的概念只是新瓶装旧酒。
而传统企业在过去多年的业务运作积累中,综合多种因素(组织结构、业务发展等),技术体系相对封闭,缺乏持续交付的理念。如果光有微服务的理论基础,和业界的成功案例,不动手尝试持续交付、DevOps等,很难有深刻的理解。
Github上微服务相关的实现Repo蛮多,但作为完整系列的不多。
所以,基于这些原因,结合过去为企业实施微服务架构培训的内容,也希望能输出这个系列。
本系列的受众用户,主要是年轻(1~3年)的Java开发者、传统应用的IT架构师,以及对微服务感兴趣的童鞋们。
希望通过本系列,能将微服务、持续交付、Docker以及DevOps等技术,作为整体输出,帮助大家缩短系统了解微服务的成本。
关于IT社区对微服务的关注度和市场的需求数据,请看下图,一个字🔥。
图的上部分是Google Trends上关于
Microservice
的增长趋势。图的左下部分是
Microservice
相关关键字的增长速度,Breakout
表明在搜索的区间内,其增长的幅度超过5000%。图的右下部分是Indeed.com中对于
Microservice
的市场招聘需求的增长曲线。(趋势来了,挡都挡不住,提升个人竞争力,赶紧哈~ 😝)
]]>好的,进入正题,请移步课程
目录大概如下