前言
从 Kubernetes 诞生以来,以 DevOps、容器化、可观测、微服务、Serverless 等技术为代表的云原生,催生了利用架构新一轮的降级。有意思的是,与以往的技术迭代更新不同,本来是一个技术圈惯例的一次技术实际,在千行百业数字化转型大背景,叠加继续疫情冲击的双重影响之下,加上局部传统行业科技自主政策的催化;这一次的技术迭代简直变成了 IT 从业人员全民参加的一次盛宴。然而平缓的学习曲线、简单的技术体系、瞬态的根底资源状态,让企业的信息体系建设在研发、构建、交付、运维等多方面都带来了不少的挑战。这些挑战也加深了日益更新的技术栈与习惯于聚焦在一线业务开发的开发者之间矛盾,这个矛盾间接催生了最近的平台工程理念的诞生。
这个理念抓住这个冲突点,提出了“外部研发自助平台”的构想:“企业应该以平台化建设的形式,提供一系列的自助型工具,帮助开发者在各个环节中解决遇到的各种技术问题”。这个说法一下戳中泛滥开发者的痒点,这也是这一概念忽然之间大火的起因之一。理念背地又引申进去了一个更为间接的问题:这个工具外面应该有点啥?
揭开问题的面纱
早在 2018 年,EDAS 产研团队访问一家具备百人研发团队的客户,过后客户正在进行微服务拆分和迁徙上云,他们遇到了一些新问题:
- 本地因为依赖问题,没法起动残缺的环境,导致开发艰难。
- 云上环境调用关系简单,无奈做调试。
客户冀望将特定的实例启动到本地,云端能调本地,本地调云端,实现微服务端云联调。对于这些问题咱们没有任何筹备,于是回来后连忙开始调研剖析,于是缓缓揭开了藏在水面下的冰山。
客户的诉求很简略,就是想把微服务利用起在本地,利用能跟云端微服务相互调用,如下所示:
迁徙上云后,客户的网关、利用、音讯队列、缓存、数据库等组件模块都部署在云端网络内,本地须要通过堡垒机能力进行拜访。在这种状况下,本地节点是不可能失常启动的(因为连不通数据库等要害组件),也是不可能跟云端服务相互调用的。采取了云原生架构之后,客户却再也回不去原来简略高效的本地开发方式了。
这个问题只有这个客户遇到吗?不是的,这几年咱们跟很多客户聊下来他们都有这个问题。但其实也不是一点解决办法都没有,最间接的方法是:通过架设公有网络的形式,连通本地办公网跟云端网络,实现网络互通。但实际上这个方法有三大缺点,导致并不是很多客户采纳。
- 老本昂扬:搭建专线网络的投入相当大,相比于收益来说并不划算。
- 安全性低:买通本地到云上网络,对本地办公网和云上生产网都带来了不稳固因素,实质上扩充了平安域,也扩充了攻击面。
- 运维简单:网络运维是相当简单的,在高度可伸缩的云原生架构下打平本地和云端网络,这个是很多网络工程师的噩梦。本地和云端两张网络必须做好布局,两边网段不可抵触,同时双向网络路由和安全策略都须要人工治理,简单费劲且容易呈现问题。
对于这些问题,有的企业采取折中的方法,在云端找一台机器作为 VPN 服务器,搭建本地到云端的 VPN 链路。这个计划同样须要保护网络路由以实现网络互通,另外 OpenVPN 虽便宜但不稳固,专用 VPN 性能高但费用低廉,鱼与熊掌不可兼得。
意识到这些问题之后,咱们便开始了“路漫漫其修远兮”的摸索之路。
端云互联,问题的初解答
在一开始咱们就确定两个指标:一是双向买通本地和云端链路,二是不须要对网络架构进行伤筋动骨的革新。
在历经三个月的闭关研发之后,咱们在 2018 年底研发进去了这个工具。它反对双向联通,而且是插件化开箱即用,反对 Windows 和 MacOS 零碎。咱们把它命名为端云互联,其整体组成如下所示:
端云互联插件会在启动微服务的时候拉起一个 sidecar 过程 – 通道服务,通道服务负责接管本地微服务的流量,并通过堡垒机转发至云端指标微服务。上面进一步阐明其中三个外围要点。
本地微服务调用转发到 sidecar
咱们应用了 Java 原生的流量代理技术,通过注入启动参数,能够使得本地微服务流量以 socks 协定转发至通道服务 sidecar 上。对于具体参数细节,可浏览 Java Networking and Proxies 来理解细节。
sidecar 将调用转发到云端微服务
其实 SSH 自身就能够用来进行数据转发,充当正向代理和反向代理。SSH 协定从下到上分为传输层、认证层和连贯层三层协定:
- 传输层协定(Transport Layer Protocol):这层协定负责建设起平安的连贯通道,是整个 SSH 的安全性基石。
- 用户认证协定(User Authentication Protocol):这层协定负责实现近程身份认证。
- 连贯协定(Connection Protocol):这层协定实现 SSH 通道的多路复用和信息交互,能够通过它来实现近程命令执行和数据转发等多种性能。
咱们基于 SSH 的连贯协定来使得通道服务 sidecar 将调用转发到云端微服务,尽管 SSH 底层原理有点简单,但下层应用是挺简略的,支流的编程语言也根本都有现成的库来应用。
云端微服务调用转发到堡垒机
这里咱们利用了微服务的机制,将堡垒机的 IP 和特定端口作为本地微服务的地址信息注册到注册核心。这样云端微服务调用时就会通过注册核心发现堡垒机,并发动服务申请,再联合 SSH 的数据转发就能回到本地微服务。
众里寻他千百度
端云互联工具上线后,受到了很多客户的欢送,但客户应用过程中遇到了新的问题:
- NIO 流量代理问题:Java Networking and Proxies 里的流量代理参数只对 BIO 的流量失效,NIO 框架并不反对。这个问题影响面十分大,因为微服务利用根本都会间接或间接地应用 Java NIO 框架。具体简略的例子,Netty 自身就是基于 Java Nio 的,而很多风行的中间件框架都应用 Netty 作为传输框架。
- 域名解析问题:对于域名解析,本地微服务利用在拜访之前会发动域名解析,而这些域名解析同样是不反对 Java 流量代理的。也就是说,如果这个域名只能在云端实现解析,那么整个调用就会失败。例如,K8s 里的 service 域名只能在集群节点上实现 DNS 解析,本地是无奈解析的,从而导致本地无奈调用 K8s service。
对于这些问题,业界通常的做法是采取容器来解决(例如 Telepresence),通过将利用跑在容器内,再联合 iptables 来拦挡并转发整个容器内的流量。这种形式是可行的,咱们也反对了这种形式,整体架构如下所示:
这个链路跟之前是差不多的,除了本地引入了容器技术之外。在上图中,咱们通过 docker network connect,能够使得利用容器和 sidecard 容器共享网络栈,这样通道服务便能够通过 iptables 去拦挡本地微服务过程的流量(包含 NIO 和 DNS 流量)并进行转发。
计划很美妙,但事实很骨感,很多客户用不起来。究其原因,那就是:本地开发须要引入容器这个重量级的依赖。这里有两个问题,一个是“重”,另一个是“依赖”。“重”,是因为本地开发机器的算力是十分小的。Chrome、IDE 和通信软件等利用往往曾经占据了大部分的本地机器资源,在这背景下本地再启动容器往往会导致死机。另外,Windows 和 MacOS 等支流操作系统也并不自带 Docker 等容器软件,开发者须要本人在本地自行装置,因为网络带宽等起因整个装置过程也会遇到许多问题。
那除了应用容器,还有别的方法来解决利用的流量代理问题吗?还是有的,不过局限性也十分大。例如 torsocks 这个工具就能够实现过程级别的 TCP 和 DNS 流量拦挡并转发,但问题是它并不反对 Windows 零碎。MacOS 也存在问题。因为 torsocks 是基于 LD_PRELOAD/DYLD_INSERT_LIBRARIES 机制来改写零碎调用来进行流量拦挡的,而 MacOS 零碎自身有零碎完整性爱护,会阻止特定零碎调用被改写,因而并不是所有的流量都能被拦挡到。
难道就没有更好的计划了吗?
那人却在,灯火阑珊处
回顾一下咱们所面临的问题:Java 原生流量代理不反对 NIO 和 DNS 流量转发。这里有一个十分重要的信息 –Java。业界或者开源社区的流量拦挡计划广泛谋求通用性,从而引入了容器依赖,顾此失彼。
既然谋求通用性有诸多问题,那么聚焦到 Java 语言是否有更优的解法?答案是必定的。Java 能够通过 Agent 字节码技术,动静批改利用运行时的行为,而下层代码无需任何变动。比方像 Pinpoint、SkyWalking 等链路跟踪工具,它们都是通过注入一个字节码 Agent 来实现无侵入的链路埋点的。再比方诊断畛域风行的 Arthas 工具,它也是基于字节码技术来实现 Java 过程的调用跟踪和 Profiling。
于是,咱们开始摸索基于字节码技术的解决方案。整个摸索过程是艰巨且乏味的。在微服务框架层面,咱们须要适配 SpringCloud、Dubbo、HSF 甚至是 gRPC 等支流框架;在组件层面,咱们须要反对微服务、数据库、音讯队列、任务调度、缓存等等组件;在 JDK 版本上,咱们须要兼容从 JDK 1.7 到 JDK18 之间的版本 … 在这过程中,咱们一直进行迭代改良,也一直收到客户的侧面反馈,让工具日趋完满。
在通过 1 年工夫的打磨之后,咱们终于自研出基于字节码的 Java 流量代理技术,架构如下所示:
这个计划只须要引入一个代理字节码 Agent,并没有引入内部依赖。这个代理 Agent 相比于容器来说轻量得多,而且会在启动阶段被端云互联插件主动拉取并注入,下层应用是无感知的。至此,咱们终于很好地解决了本地微服务利用的流量代理问题。
独上高楼,望尽咫尺路
在这几年里,咱们始终在抬头赶路,一直地发现问题并解决问题。与此同时,云原生社区也在逐渐演进。同样在 2018 年,kubernetes 社区发表了一篇名为 Developing on Kubernetes 的文章,上面对不同的开发模式有一个十分好的总结:
remote 示意云端,local 示意本地。cluster 为 K8s 集群,dev 为开发环境。针对 dev 和 cluster 不同的地位,整体可分为四种开发模式:
- pure off-line:这种模式示意你的 K8s 集群和开发环境都在本地。K3s、Minikube 和 EDAS Core(这里先卖个关子,下文再进行介绍)都属于这种模式,你能够在本地间接启动一个轻量级的开发集群。
- proxied:这种模式示意 K8s 集群运行在云端,开发环境在本地,云端集群和本地开发环境通过代理进行互联。这个模式的典型代表为社区的 Telepresence 和 EDAS 端云互联。在多语言通用性上 Telepresence 稍逊一筹,而在 Java 上 EDAS 端云互联更加易用。
- live:这种模式示意 K8s 集群运行在云端,开发环境在本地,本地代码通过 CICD 等形式来更新云端的利用。这个模式是最常见的模式,也是广泛效率最低的模式。如果通过 CICD 部署,意味着你每次代码批改都须要通过漫长的构建和部署能力失效到集群外面。如果在开发过程中须要一直批改代码来调试,这个迭代部署过程是十分耗时的。
- remote:这种模式示意 K8s 集群和开发环境都在云端。Cloud IDE 是典型的例子,代码和运行环境都在云端,本地通过浏览器或者轻量级的端利用来编辑代码。实际上来看,这种形式仍未失去宽广开发者认可,本地 IDE 的体验优于 Cloud IDE 体验,本地开发依然是支流。
在 proxied 模式上,咱们曾经把端云互联打磨的相当不错了,但它不能解掉所有问题。这样的场景并不常见:本地开发调试都好好的,但一部署下来就是有问题。这种问题的本源是,本地运行的环境和云端集群里运行环境是不统一的,这个不统一会产生种种问题。例如,本地能失常运行一个须要 2c4g 的 Java 过程,不代表云上集群也能失常调配一个 2c4g 的 Pod,因为以后集群内可能是没有多余资源的。
这样的问题有很多,不可一一枚举。这也促使咱们进一步思考应该如何解决这些问题。在通过半年的调研、摸索和研发,咱们研发出云原生工具箱(Cloud Native Development Kit,简称 CNKit),由它来解决这些问题,并提供云原生架构下的开发、调试和诊断能力。
云原生工具箱(Cloud Native Development Kit)
咱们解决问题的思路是,要解决环境不统一的问题,只能回到环境中去。一个利用在云原生环境下启动尽管看上去很简略,但实际上是要经验很多个步骤的。利用须要通过从 K8s 调度,到 Pod 初始化,再到服务拉起,最终能力实现利用运行。在这个过程中,你可能会遇到以下问题:
对于这些问题,咱们进行演绎总结,积淀出一套解决方案:通过 CNKit 来疾速复制 Pod,而后进行迭代开发、部署、调试和诊断。整体性能如下所示:
通过与 EDAS 全流量流控集成,CNKit 可使得只有合乎特定规定的调试流量进入复制的 Pod,而不影响其余失常流量。对于复制的 Pod,你可应用 CNKit 开箱即用的部署、调试和诊断能力,并且能够应用基于审计的命令终端。上面来具体阐明复制、部署、调试和诊断能力。
复制
这个复制的 Pod 相当于咱们本人的一个长期工作空间,咱们能够一直通过浏览器或者 IDE 来部署本人的利用包,并进行调试和诊断。复制 Pod 以后反对如下配置:
具体作用为:
- 启动命令:即 Pod 的启动命令。默认下应用原镜像的启动命令,而如果须要进行迭代部署或者开启调试的话,须要自定义启动命令。这是因为在生产环境中,原镜像启动命令往往会应用利用过程来作为 1 号过程,一旦利用退出或重启,这个 Pod 就会随之开释。因而,须要设置一个非凡的启动命令来避免 Pod 随利用退出而被开释。
- 复制模式:反对基于 Pod 复制或者从 Deployment 的 spec 进行创立。
- 指标节点:即 Pod 运行在那个集群节点上。默认通过 K8s 调度来运行 Pod,但你也能够间接指定特定集群节点来运行此 Pod。
- Pod 日志:配置将 Pod 日志打到规范输入或者重定向到文件。
- 流量管制:通过全链路流控,可使得只有合乎特定规定的申请进入该 Pod 节点。
- 诊断选项:反对利用启动时立刻运行 tcpdump 来进行监测、一键关上 JVM 异样记录和去除 Liveness 探针。
这些选项都是基于 EDAS 长年累月的客户反对所总结进去的教训,可能看上去并不酷炫,但却是十分实用的。拿“去除 Liveness 探针”这个配置项来阐明。如果利用启动阶段就出现异常,这种状况下 Liveness 探针是会失败的,K8s 会间接 Kill 掉这个 Pod 容器并从新拉起。咱们会陷入 Liveness 失败,Pod 容器被杀死,而后容器重启导致现场失落,Liveness 又失败的有限循环当中。去除 Liveness 探针之后,你就能够进去 Pod 容器中进行问题排查了。
这里还有一个十分有意思的配置项 – 全链路流控。全链路流控是 EDAS 上微服务治理的杀手锏,能够使得整体微服务链路的流量指哪打哪。对于复制的 Pod,咱们可能会心愿只有本人的申请才进入这个 Pod,而不影响其他人的调用申请。这种状况只须要在界面上勾选退出特定流控分组即可,应用上非常简单。
部署
还记得最后的问题吗?在云原生架构下,咱们每次部署都须要通过 CICD、EDAS 部署和 K8s 调度来拉起利用,这个过程是漫长而苦楚的。而且,咱们在排查问题时往往须要来长期装置特定工具,每次拉起新的利用 Pod 意味着须要重新安装所需工具。
对于这个问题,咱们举荐采取“一次复制,屡次应用”策略。在下面提到,咱们能够通过复制 Pod 来创立出属于本人的“长期工作区”(本质也是一个 Pod),而后能够通过浏览器或者 IDE 来间接把利用包部署到长期工作区,并且进行调试诊断。基于 CICD 和 CNKit 的开发流程是截然不同的,如下所示:
CICD 部署门路适宜生产环境,通过规范流程保障了线上业务的稳定性。但同时它又是流程简短的,在开发阶段劣势不显著,反而会升高开发效率。这种状况下 CNKit 部署流程是很好的互补,你只须要复制 Pod,而后便能够通过浏览器或者 IDE 来不断更新利用包调试代码。
调试
调试(这里特指 remote debug)是利用开发过程中相当重要的一环,如果无奈调试,那么利用开发效率将会大大降低。在云原生架构下,调试利用并不是那么简略,但总是能够实现的。在这一方面,CNKit 除了简化调试流程外,还提供了流量控制能力:
- 简化流程:你只须要在页面上点击“开启调试”,CNKit 便会重启 Pod 里的利用来开启调试端口。而后本地通过 IDE 来一键连贯到 CNKit,接着就能够开始断点调试了。
- 流量管制:通过集成 EDAS 全链路流控,CNKit 可使得只有特定申请能进入复制 Pod,触发断点调试逻辑。
通过这两点,你能够十分不便地实现代码调试。下图是一个简略的阐明样例,如果你在开发一个商品核心,上游为交易中心,上游为库存核心,应用 CNKit 进行调试的整体链路如下所示:
标记为开发版本的商品核心即为复制进去的 Pod 节点,云端环境中通过全链路流控来将特定流量转发到该 Pod 中,开发者本地则通过 CNKit proxy 来连贯到该 Pod 的调试端口。
实际上,在一个多人并行开发的服务中,每个人都能够领有属于本人的开发版本节点,只须要设定不同的流量管制规定即可,这样可并行开发可互不烦扰。
诊断
咱们将问题诊断为 K8s 调度、利用启动、利用运行和利用下线四个阶段。在不同阶段,采取的诊断伎俩并不相同:
在 K8s 调度过程中,咱们次要关注其产生的相干事件,产生调度异样时 K8s 会给出相干起因。下图为失常调度时的 K8s 事件:
当呈现调度异样(例如资源有余导致调度失败)的问题时,K8s 会产生相应事件:
而在利用启动阶段,除了 K8s 事件,咱们还能够察看 Pod 日志,这部分日志是利用产生的,外面蕴含更详尽的信息。下图为 Pod 的规范输入日志样例:
另外,利用启动阶段会产生较多网络拜访,利用启动失败很多状况下都是因为网络申请异样引起的,因而 CNKit 反对在启动前主动运行 Tcpdump 来记录网络申请。下图为利用启动时主动抓取的 Tcpdump 包,CNKit 反对文本和 pcap 两种格局,下图为文本格式的 Tcpdump 数据:
最初,在利用运行和下线阶段,你依然能够应用 K8s 事件、Pod 日志和 Tcpdump,另外还能够一键应用 CNKit 集成的 Arthas 工具。通过在页面上一键运行 Arthas,CNKit 会主动实现 Arthas 装置并运行,整体交互如下所示:
至此,CNKit 的复制、部署、调试和诊断都一一分享结束。但除了这些能力,CNKit 还有一些暗藏彩蛋,例如审计 Webshell 等等,这些中央留给读者来缓缓摸索,此处不再赘述。
EDAS Core
除了端云互联和 CNKit,咱们还凋谢了 EDAS Core。如果依照下面 Developing on Kubernetes 划分的规范来看,端云互联属于 proxied 模式,CNKit 则为 live 模式,而 EDAS Core 则为 pure off-line 模式。
EDAS 自身是免费的商业化产品,它是一个利用托管和微服务治理的云原生 PaaS 平台,提供利用开发、部署、监控、运维等全栈式解决方案,同时反对 Spring Cloud 和 Apache Dubbo 等微服务运行环境。而 EDAS Core 则为收费的轻量级 EDAS 内核版本,同样反对上述能力,但剥离了商业化个性,不提供服务 SLA 和实时的运维反对,适宜在开发阶段应用。
EDAS Core 最低只须要 4 核 8g 的机器资源,咱们齐全能够在本地笔记本上来运行一个离线的 EDAS 平台,并进行微服务开发。EDAS Core 整体架构如下所示:
这里进行简略阐明:
- EDAS Core:蕴含了 EDAS 利用托管能力,反对 Nacos 服务注册发现和 Minio 长久化存储,可运行于 Kind、K3s、Docker-Desktop 和 K8s 集群之上,只需 4c8g 的资源占用。
- 开发者工具:反对应用 Jenkins 插件进行继续部署、Terraform 进行基础设施保护、ACT 进行本地开发,并兼容 EDAS 凋谢 API 和 SDK。
- 装置介质:反对通过 Helm、OSS 和 ADP 进行 EDAS Core 装置。
- K8s Cluster:此 K8s 集群即为 EDAS Core 托管的集群,下面运行微服务利用(并主动注入服务治 OneAgent)。
- 服务集成:在横向服务集成上,EDAS Core 反对和 EDAS 商业化利用进行一键转换,并集成了 ARMS 和 SkyWalking 等链路跟踪产品,同时反对应用 ACR 进行镜像托管。
上面为 EDAS Core 的运行界面(EDAS 老用户应该对这个界面比拟相熟了):
以后 EDAS Core 处于外部邀测状态,如果心愿应用此能力,欢送在阿里云上向 EDAS 产品发动工单征询:)。
结语
云原生架构和微服务开发这两个都是十分风行的技术畛域,但“云原生架构下的微服务开发”这个命题却甚少见国内厂商提及。EDAS 作为微服务托管畛域的先行者很早就开始了云原生架构的反对,并始终在关注新架构下的微服务开发问题。
从最早的端云互联模式开始,到最近推出的云原生工具箱(CNKit)和 EDAS Core,EDAS 始终站在开发者角度来思考云原生技术演进所面临的新问题,并一直提供解决这些问题的工具和产品。最初,对于这几个工具产品进行简略的总结来完结本文:
参考资料
- EDAS:https://www.aliyun.com/product/edas
- 云原生工具箱:https://help.aliyun.com/document_detail/450983.html
- CLM:https://github.com/cloudnativeapp/CLM
- torsocks:https://github.com/dgoulet/torsocks
- docker network connect:https://docs.docker.com/engine/reference/commandline/network_connect
- The Secure Shell (SSH) Connection Protocol:https://www.rfc-editor.org/rfc/rfc4254
- Java Networking and Proxies:https://docs.oracle.com/javase/8/docs/technotes/guides/net/proxies.html
- Developing on Kubernetes:https://kubernetes.io/blog/2018/05/01/developing-on-kubernetes/
- Arthas:https://github.com/alibaba/arthas
原文链接
本文为阿里云原创内容,未经容许不得转载。