在 2014 年 6 月 Google 开源了 Kubernetes 后,经过这几年的发展,已逐渐成为容器编排领域的事实标准, 可以称之为云原生时代的操作系统,它使得基础设施维护变得异常简单。在云原生时代,微服务依赖于 Kubernetes 的优势在哪,微服务的生命周期基于 Kubernetes 该如何实践呢?本文整理自石墨文档架构负责人彭友顺在 Gopher China Meetup 西安站的主题演讲《石墨文档基于 Kubernetes 的 Go 微服务实践(上篇)》。下篇会在近期整理出来,敬请期待。
互联网的 WEB 架构演进可以分为三个阶段:单体应用时期、垂直应用时期、微服务时期。
单体应用时期一般处于一个公司的创业初期,他的好处就是运维简单、开发快速、能够快速适应业务需求变化。但是当业务发展到一定程度后,会发现许多业务会存在一些莫名奇妙的耦合,例如你修改了一个支付模块的函数,结果登录功能挂了。为了避免这种耦合,会将一些功能模块做一个垂直拆分,进行业务隔离,彼此之间功能相互不影响。但是在业务发展过程中,会发现垂直应用架构有许多相同的功能,需要重复开发或者复制粘贴代码。所以要解决以上复用功能的问题,我们可以将同一个业务领域内功能抽出来作为一个单独的服务,服务之间使用 RPC 进行远程调用,这就是我们常所说的微服务架构。
总的来说,我们可以将这三个阶段总结为以下几点。单体应用架构快速、简单,但耦合性强;垂直应用架构隔离性、稳定性好,但复制粘贴代码会比较多;微服务架构可以说是兼顾了垂直应用架构的隔离性、稳定性,并且有很强的复用性能力。可以说微服务架构是公司发展壮大后,演进到某种阶段的必然趋势。
但微服务真的那么美好吗?我们可以看到一个单体架构和微服务架构的对比图。在左图我们可以看到一个业务可以通过 Nginx+ 服务器 + 数据库就能实现业务需求。但是在右图微服务架构中,我们完成一个业务需要引入大量的组件,比如在中间这一块我们会引入 DNS、HPA、ConfigMap 等、下面部分引入了存储组件 Redis、MySQL、Mongo 等。以前单体应用时期我们可能直接上机器看日志或上机器上查看资源负载监控,但是到了微服务阶段,应用太多了,肯定不能这么去操作,这个时候我们就需要引入 ELK、Prometheus、Grafana、Jaeger 等各种基础设施,来更方便地对我们的服务进行观测。
微服务的组件增多、架构复杂,使得我们运维变得更加复杂。对于大厂而言,人多维护起来肯定没什么太大问题,可以自建完整的基础设施,但对于小厂而言,研发资源有限,想自建会相当困难。
不过微服务的基础设施维护困难的问题在 Kubernetes 出现后逐渐出现了转机。在 2014 年 6 月 Google 开源了 Kubernetes 后,经过这几年的发展,已逐渐成为容器编排领域的事实标准。同时 Kubernetes 已俨然成为云原生时代的超级操作系统,它使得基础设施维护变得异常简单。
在传统模式下,我们不仅需要关注应用开发阶段存在的问题,同时还需要关心应用的测试、编译、部署、观测等问题,例如程序是使用 systemd、supervisor 启动、还是写 bash 脚本启动?日志是如何记录、如何采集、如何滚动?我们如何对服务进行观测?Metrics 指标如何采集?采集后的指标如何展示?服务如何实现健康检查、存活检查?服务如何滚动更新?如何对流量进行治理,比如实现金丝雀发布、流量镜像?诸如此类的问题。我们业务代码没写几行,全在考虑和权衡基础设施问题。然而使用 Kubernetes 后,可以发现大部分问题都已经被 Kubernetes 或周边的生态工具解决了,我们仅仅只需要关心上层的应用开发和维护 Kubernetes 集群即可。
Kubernetes 在微服务中的作用就如同建高楼的地基,做了很多基础工作,统一了大量的基础设施标准,以前我们要实现服务的启动、配置、日志采集、探活等功能需要写很多中间件,现在我们只需要写写 yaml 文件,就可以享受这些基础设施的能力。运维更加简单这个也显而易见,例如在以前出现流量高峰时研发提工单后增加副本数,运维处理工单,人肉扩缩容,现在我们可以根据实际应用的负载能力,合理的配置好副本 CPU、Mem 等资源及 HPA 规则,在流量高峰时由 Kubernetes 自动扩容、流量低谷时自动缩容,省去了大量人工操作。
同时在框架层面,传统模式下基础设施组件很多都是自研的,基本上没有太多标准可言,框架需要做各种 switch case 对这种基础设施组件的适配,并且框架经常会为因为基础设施的改变,做一些不兼容的升级。现在只需要适配 Kubernetes 即可,大大简化微服务的框架难度和开发成本。
刚才我们讲到 Kubernetes 的优势非常明显,在这里会描述下我们自己研发的微服务框架 Ego 怎么和 Kubernetes 结合起来的一些有趣实践。
我们将微服务的生命周期分为以下 6 个阶段:开发、测试、部署、启动、调用、治理。
在开发阶段我们最关注三个问题:如何配置、如何对接,如何调试。
大家在使用开源组件的时候,其实会发现每个开源组件的配置、调用方式、debug 方式、记录日志方式都不一样,导致我们需要不停去查看组件的示例、文档、源码,才能使用好这个组件。我们只想开发一个功能,却需要关心这么多底层实现细节,这对我们而言是一个很大的心智负担。
所以我们将配置、调用方式做了统一。可以看到上图我们所有组件的地址都叫 addr,然后在下图中我们调用 redis、gRPC、MySQL 的时候,只需要基于组件的配置 Key path 去 Load 对应的组件配置,通过 build 方法就可以构造一个组件实例。可以看到调用方式完全相同,就算你不懂这个组件,你只要初始化好了,就可以根据编辑器代码提示,调用这个组件里的 API,大大简化我们的开发流程。
配置补齐这个功能,是源于我们在最开始使用一些组件库的时候,很容易遗漏配置,例如使用gRPC
的客户端,未设置连接错误、导致我们在阻塞模式下连接不上的时候,没有报正确的错误提示;或者在使用 Redis、MySQL 没有超时配置,导致线上的调用出现问题,产生雪崩效应。这些都是因为我们对组件的不熟悉,才会遗漏配置。框架要做的是在用户不配置的情况下,默认补齐这些配置,并给出一个最佳实践配置,让业务方的服务更加稳定、高效。
我们编写完配置后,需要将配置发布到测试环境,我们将配置中心 IDE 化,能够非常方便的编写配置,通过鼠标右键,就可以插入资源引用,鼠标悬停可以看到对应的配置信息。通过配置中心,使我们在对比配置版本,发布,回滚,可以更加方便。
我们内部系统全部统一采用gRPC
协议和protobuf
编解码。统一的好处在于不需要在做任何协议、编解码转换,这样就可以使我们所有业务采用同一个protobuf
仓库,基于 CI/CD 工具实现许多自动化功能。
我们要求所有服务提供者提前在独立的路径下定义好接口和错误码的 protobuf 文件,然后提交到 GitLab,我们通过 GitLab CI 的 check 阶段对变更的 protobuf 文件做 format、lint、breaking 检查。然后在 build 阶段,会基于 protobuf 文件中的注释自动产生文档,并推送至内部的微服务管理系统接口平台中,还会根据 protobuf 文件自动构建 Go/PHP/Node/Java 等多种语言的桩代码和错误码,并推送到指定对应的中心化仓库。
推送到仓库后,我们就可以通过各语言的包管理工具拉取客户端、服务端的 gRPC 和错误码的依赖,不需要口头约定对接数据的定义,也不需要通过 IM 工具传递对接数据的定义文件,极大的简化了对接成本。
有了以上比较好的 protobuf 生成流程后,我们可以进一步简化业务错误状态码的对接工作。而我们采用了以下方式:
对接中调试的第一步是阅读文档,我们之前通过 protobuf 的 ci 工具里的 lint,可以强制让我们写好注释,这可以帮助我们生成非常详细的文档。
基于 gRPC Reflection 方法,服务端获得了暴露自身已注册的元数据能力,第三方可以通过 Reflection 接口获取服务端的 Service、Message 定义等数据。结合 Kubernetes API,用户选择集群、应用、Pod 后,可直接在线进行 gRPC 接口测试。同时我们可以对测试用例进行存档,方便其他人来调试该接口。
我们大部分的时候都是对接各种组件 API,如果我们能够展示各种组件例如 gRPC、HTTP、MySQL、Redis、Kafka 的调试信息,我们就能够快速的 debug。在这里我们定义了一种规范,我们将配置名、请求 URL、请求参数、响应数据、耗时时间、执行行号称为 Debug 的六元组信息。
将这个 Debug 的六元组信息打印出来,如下图所示。我们就可以看到我们的响应情况,数据结构是否正确,是否有错误。
Debug 里面有个最重要的一点能够快速定位错误问题,所以我们在实践的过程中,会遵循 Fail Fast 理念。将框架中影响功能的核心错误全部设置为 panic,让程序尽快的报错,并且将错误做好高亮,在错误信息里显示 Panic 的错误码,组件、配置名、错误信息,尽快定位错误根因。这个图里面就是我们的错误示例,他会高亮的显示出来,你的配置可能不存在,这个时候业务方在配置文件中需要找到server.grpc
这个配置,设置一下即可。
开发完成后,我们会进入到测试阶段。我们测试可以分为四种方式:单元测试、接口测试、性能测试、集成测试。
我们会通过 docker-compose 跑本地的一些单元测试,使用 GitLab CI 跑提交代码的单元测试。我们接口测试则使用上文所述接口平台里的测试用例集。性能测试主要是分两种,一类是 benchmark 使用 GitLab ci。另一类是全链路压测就使用平台工具。集成测试目前还做的不够好,之前是用 GitLab ci 去拉取镜像,通过 dind(Docker in Docker)跑整个流程,但之前我们没有拓扑图,所以需要人肉配置 yaml,非常繁琐,目前我们正在结合配置中心的依赖拓扑图,准备用 jekins 完成集成测试。
在这里我主要介绍下单元测试。
单元测试优势大家都应该很清楚,能够通过单测代码保证代码质量。但单测缺点其实也非常明显,如果每个地方都写单测,会消耗大家大量的精力。
所以我们首先定义了一个规范,业务代码里面不要出现基础组件代码,所有组件代码下层到框架里做单元测试。业务代码里只允许有 CRUD 的业务逻辑,可以大大简化我们的测试用例数量。同时我们的业务代码做好 gRPC,HTTP 服务接口级别的单元测试,可以更加简单、高效。
然后我们可以通过开发 protobuf 工具的插件,拿到 gRPC 服务的描述信息,通过他结合我们的框架,使用指令自动生成测试代码用例。在这里我们框架使用了 gRPC 中的测试 bufconn 构造一个 listener,这样就可以在测试中不关心 gRPC 服务的 ip port。
以下是我们通过工具生成的单元测试代码,我们业务人员只需要在红框内填写好对应的断言内容,就可以完成一个接口的单测。
目前单元测试大部分的玩法,都是在做解除依赖,例如以下的一些方式:
不可否认,以上的方法确实可以使代码变得更加优雅,更加方便测试。但是实现了以上的代码,会让我们的代码变得更加复杂、增加更多的开发工作量,下班更晚。如果我们不方便解除依赖,我们是否可以让基础设施将所有依赖构建起来。基础设施能做的事情,就不要让研发用代码去实现。
以下举我们一个实际场景的 MySQL 单元测试例子。我们可以通过 docker-compose.yml,构建一个 mysql。然后通过 Ego 的应用执行 job。
可以看到我们可以每次都在干净的环境里,构建起服务的依赖项目,跑完全部的测试用例。详细 example 请看 https://github.com/gotomicro/go-engineering。
编译是微服务的重要环节。我们可以在编译阶段通过-ldflags
指令注入必要的信息,例如应用名称、应用版本号、框架版本号、编译机器 Host Name、编译时间。该编译脚本可以参考 https://github.com/gotomicro/ego/blob/master/scripts/build/gobuild.sh:
go build -o bin/hello -ldflags -X "github.com/gotomicro/ego/core/eapp.appName=hello -X github.com/gotomicro/ego/core/eapp.buildVersion=cbf03b73304d7349d3d681d3abd42a90b8ba72b0-dirty -X github.com/gotomicro/ego/core/eapp.buildAppVersion=cbf03b73304d7349d3d681d3abd42a90b8ba72b0-dirty -X github.com/gotomicro/ego/core/eapp.buildStatus=Modified -X github.com/gotomicro/ego/core/eapp.buildTag=v0.6.3-2-gcbf03b7 -X github.com/gotomicro/ego/core/eapp.buildUser=`whoami` -X github.com/gotomicro/ego/core/eapp.buildHost=`hostname -f` -X github.com/gotomicro/ego/core/eapp.buildTime=`date +%Y-%m-%d--%T`"
通过该方式注入后,编译完成后,我们可以使用./hello --version ,查看该服务的基本情况,如下图所示。
微服务还有一个比较重要的就是能够知道你的应用当前线上跑的是哪个框架版本。我们在程序运行时,使用 go 里面的 debug 包,读取到依赖版本信息,匹配到我们的框架,得到这个版本。
然后我们就可以在 prometheus 中或者二进制中看到我们框架的版本,如果框架某个版本真有什么大 bug,可以查询线上运行版本,然后找到对应的应用,让他们升级。
发布配置版本,我们在没有 Kubernetes 的时候,不得不做个 agent,从远端 ETCD 读取配置,然后将文件放入到物理机里,非常的繁琐。而使用 Kubernetes 发布配置,就会非常简单。我们会在数据库记录配置版本信息,然后调用 Kubernetes API,将配置写入到 config map 里,然后再将配置挂载到应用里。
发布微服务应用版本,因为有了 Kubernetes 就更加简单,我们只需要发布系统调用一下 deployment.yml 就能实现,应用的拉取镜像、启动服务、探活、滚动更新等功能。
EGO
内置很多环境变量,这样可以很方便的通过基础设施将公司内部规范的一些数据预设在Kubernetes
环境变量内,业务方就可以简化很多启动参数,在dockerfile
里启动项变为非常简单的命令行:CMD ["sh", "-c", "./${APP}"]
我们通过 Kubernetes configmap 挂载到应用 pod,通过框架 watch 该配置。在这里要提醒一点,Kubernetes 的配置是软链模式,框架要想要监听该配置,必须使用 filepath.EvalSymlinks(fp.path) 计算出真正的路径。然后我们就可以通过配置中心更改配置,通过 configmap 传递到我们的框架内部,实现配置的实时更新。
首先我们探活的概念。
转换成我们常见的研发人话就是,liveness 通常是你服务 panic 了,进程没了,检测 ip port 不存在了,这个时候 Kubernetes 会杀掉你的容器。而 readinessProbe 则是你服务可能因为负载问题不响应了,但是 ip port 还是可以连上的,这个时候 Kubernetes 会将你从 service endpoints 中剔除。
所以我们 liveness Probe 设置一个 tcp 检测 ip port 即可,readness 我们需要根据 HTTP,gRPC 设置不同的探活策略。
当我们确保服务接口是 readness,这个时候流量就会导入进来。然后在结合我们的滚动更新,我们服务可以很优雅的启动起来。(liveness、readness 必须同时设置,而且策略必须有差异,否则会带来一些问题)
我们在使用 Kubernetes 的时候,初期也使用最简单的 dns 服务发现,他的好处就是简单方便,gRPC 中直接内置。但是在实际的使用过程中,发现 gRPC DNS Resolver 还是存在一些问题。
gRPC DNS Resolver 使用了 rn 的 channel 传递事件。当客户端发现连接有异常,都会执行 ResolveNow,触发客户端更新服务端副本的列表。但是当 K8S 增加服务端副本时,客户端连接是无法及时感知的。
因为 gRPC DNS Resolver 存在的问题,我们自己实现了 Kubernetes API Resolver。我们根据 Kubernetes 的 API,watch 服务的 endpoints 方式,实现服务发现。
我们再来梳理下微服务在 Kubernetes 的注册与发现的流程,首先我们服务启动会,探针会通过 ip port 检测我们的端口查看我们是否是活的,如果是活的就说明我们的 pod 已经跑起来了,然后会通过探针访问我们 gRPC 服务的 health 接口,如果是可用的,这个时候 Kubernetes 会将我们这个服务的 pod ip 注册到 service endpoints,流量就会随之导入进来。然后我们的客户端会通过 Kubernetes API Watch 到 service endpoints 的节点变化,然后将该节点添加到它自己的服务列表里,然后它就可以通过 Balancer 调用服务节点,完成 RPC 调用。
由于篇幅较多,以上介绍了微服务生命周期的一部分,下期我们在介绍微服务治理中的监控、日志、链路、限流熔断、报警、微服务管理等内容。以下是 ego 架构图和研发生命周期的全景图。
资料链接
本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/8DJ0sUlSYxk9HjDVJg2PeA
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。