技术

下一个平台Agent 激发LLM涌现——提示工程 LLM微调理论及实践 大佬沉思 LLM外挂知识库 LLMOps 多模态LLM Python一些比较有意思的库 LLM部分技术源码学习 LangChain源码学习 通用分布式计算引擎Ray Python并发 go依赖注入 go collection gc的基本原理 golang性能分析及优化 数据湖 高性能计算与存储 Linux2.1.13网络源代码学习 《大数据经典论文解读》 三驾马车学习 Spark 内存管理及调优 Yarn学习 从Spark部署模式开始讲源码分析 容器狂占内存资源怎么办? 多角度理解一致性 golang io使用及优化模式 Flink学习 c++学习 学习ebpf go设计哲学 ceph学习 学习mesh kvm虚拟化 学习MQ go编译器以及defer实现 学习go 为什么要有堆栈 汇编语言 计算机组成原理 运行时和库 Prometheus client mysql 事务 mysql 事务的隔离级别 mysql 索引 坏味道 学习分布式 学习网络 学习Linux go堆内存分配 golang 系统调用与阻塞处理 Goroutine 调度过程 重新认识cpu mosn有的没的 负载均衡泛谈 单元测试的新解读 《Redis核心技术与实现》笔记 《Prometheus监控实战》笔记 Prometheus 告警学习 calico源码分析 对容器云平台的理解 Prometheus 源码分析 并发的成本 基础设施优化 hashicorp raft源码学习 docker 架构 mosn细节 与微服务框架整合 Java动态代理 编程范式 并发通信模型 《网络是怎样连接的》笔记 go channel codereview gc分析 jvm 线程实现 go打包机制 go interface及反射 如何学习Kubernetes 《编译原理之美》笔记——后端部分 《编译原理之美》笔记——前端部分 Pilot MCP协议分析 go gc 内存管理玩法汇总 软件机制 istio流量管理 Pilot源码分析 golang io 学习Spring mosn源码浅析 MOSN简介 《datacenter as a computer》笔记 学习JVM Tomcat源码分析 Linux可观测性 学习存储 学计算 Gotty源码分析 kubernetes operator kaggle泰坦尼克问题实践 kubernetes扩缩容 神经网络模型优化 直觉上理解深度学习 如何学习机器学习 TIDB源码分析 什么是云原生 Alibaba Java诊断工具Arthas TIDB存储——TIKV 《Apache Kafka源码分析》——简介 netty中的线程池 guava cache 源码分析 Springboot 启动过程分析 Spring 创建Bean的年代变迁 Linux内存管理 自定义CNI IPAM 共识算法 spring redis 源码分析 kafka实践 spring kafka 源码分析 Linux进程调度 让kafka支持优先级队列 Codis源码分析 Redis源码分析 C语言学习 《趣谈Linux操作系统》笔记 docker和k8s安全访问机制 jvm crash分析 Prometheus 学习 Kubernetes监控 容器日志采集 Kubernetes 控制器模型 容器狂占资源怎么办? Kubernetes资源调度——scheduler 时序性数据库介绍及对比 influxdb入门 maven的基本概念 《Apache Kafka源码分析》——server Kubernetes类型系统 源码分析体会 《数据结构与算法之美》——算法新解 Kubernetes源码分析——controller mananger Kubernetes源码分析——apiserver Kubernetes源码分析——kubelet Kubernetes介绍 ansible学习 Kubernetes源码分析——从kubectl开始 jib源码分析之Step实现 jib源码分析之细节 线程排队 跨主机容器通信 jib源码分析及应用 为容器选择一个合适的entrypoint kubernetes yaml配置 《持续交付36讲》笔记 mybatis学习 程序猿应该知道的 无锁数据结构和算法 CNI——容器网络是如何打通的 为什么很多业务程序猿觉得数据结构和算法没用? 串一串一致性协议 当我在说PaaS时,我在说什么 《数据结构与算法之美》——数据结构笔记 PouchContainer技术分享体会 harbor学习 用groovy 来动态化你的代码 精简代码的利器——lombok 学习 《深入剖析kubernetes》笔记 编程语言那些事儿 rxjava3——背压 rxjava2——线程切换 spring cloud 初识 《深入拆解java 虚拟机》笔记 《how tomcat works》笔记 hystrix 学习 rxjava1——概念 Redis 学习 TIDB 学习 如何分发计算 Storm 学习 AQS1——论文学习 Unsafe Spark Stream 学习 linux vfs轮廓 《自己动手写docker》笔记 java8 实践 中本聪比特币白皮书 细读 区块链泛谈 比特币 大杂烩 总纲——如何学习分布式系统 hbase 泛谈 forkjoin 泛谈 看不见摸不着的cdn是啥 《jdk8 in action》笔记 程序猿视角看网络 bgp初识 calico学习 AQS——粗略的代码分析 我们能用反射做什么 web 跨域问题 《clean code》笔记 《Elasticsearch权威指南》笔记 mockito简介及源码分析 2017软件开发小结—— 从做功能到做系统 《Apache Kafka源码分析》——clients dns隐藏的一个坑 《mysql技术内幕》笔记 log4j学习 为什么netty比较难懂? 递归、回溯、动态规划 apollo client源码分析及看待面向对象设计 学习并发 docker运行java项目的常见问题 OpenTSDB 入门 spring事务小结 分布式事务 javascript应用在哪里 《netty in action》读书笔记 netty对http2协议的解析 ssl证书是什么东西 http那些事 苹果APNs推送框架pushy apple 推送那些事儿 编写java框架的几大利器 java内存模型和jvm内存布局 java exception Linux IO学习 netty内存管理 测试环境docker化实践 netty在框架中的使用套路 Nginx简单使用 《Linux内核设计的艺术》小结 Go并发机制及语言层工具 Linux网络源代码学习——数据包的发送与接收 《docker源码分析》小结 docker namespace和cgroup zookeeper三重奏 数据库的一些知识 Spark 泛谈 链式处理的那些套路 netty回顾 Thrift基本原理与实践(二) Thrift基本原理与实践(一) 回调 异步执行抽象——Executor与Future Docker0.1.0源码分析 java gc Jedis源码分析 深度学习泛谈 Linux网络命令操作 JTA与TCC 换个角度看待设计模式 Scala初识 向Hadoop学习NIO的使用 以新的角度看数据结构 并发控制相关的硬件与内核支持 systemd 简介 quartz 源码分析 基于docker搭建测试环境(二) spring aop 实现原理简述 自己动手写spring(八) 支持AOP 自己动手写spring(七) 类结构设计调整 分析log日志 自己动手写spring(六) 支持FactoryBean 自己动手写spring(九) 总结 自己动手写spring(五) bean的生命周期管理 自己动手写spring(四) 整合xml与注解方式 自己动手写spring(三) 支持注解方式 自己动手写spring(二) 创建一个bean工厂 自己动手写spring(一) 使用digester varnish 简单使用 关于docker image的那点事儿 基于docker搭建测试环境 分布式配置系统 JVM执行 git maven/ant/gradle/make使用 再看tcp kv系统 java nio的多线程扩展 《Concurrency Models》笔记 回头看Spring IOC IntelliJ IDEA使用 Java泛型 vagrant 使用 Go常用的一些库 Python初学 Goroutine 调度模型 虚拟网络 《程序员的自我修养》小结 Kubernetes存储 访问Kubernetes上的Service Kubernetes副本管理 Kubernetes pod 组件 Go基础 JVM类加载 硬币和扑克牌问题 LRU实现 virtualbox 使用 ThreadLocal小结 docker快速入门

架构

大模型推理服务框架 模型服务化(未完成) 大模型RHLF 大模型训练 大模型推理 从Attention到Transformer k8s设备管理 LLM工具栈 ddd从理念到代码 如何应用LLM 小鼠如何驾驭大象(LLM)? 多类型负载协调员Koordinator controller-runtime细节分析 finops学习 kubevela多集群 kubevela中cue的应用 基于k8s的工作流 容器和CPU那些事儿 kubevela源码分析 数据集管理fluid 应用管理平台kubevela karmada支持crd 多集群管理 AutoML和AutoDL 特征平台 实时训练 分布式链路追踪 helm tensorflow原理——python层分析 如何学习tensorflow 数据并行——allreduce 数据并行——ps 机器学习中的python调用c 机器学习训练框架概述 embedding的原理及实践 tensornet源码分析 大模型训练和推理 X的生成——特征工程 tvm tensorflow原理——core层分析 模型演变 《深度学习推荐系统实战》笔记 keras 和 Estimator tensorflow分布式训练 分布式训练的一些问题 基于Volcano的弹性训练 图神经网络 pytorch弹性分布式训练 从混部到统一调度 从RNN到Attention pytorch分布式训练 CNN 《动手学深度学习》笔记 pytorch与线性回归 多活 volcano特性源码分析 推理服务 kubebuilder 学习 mpi 学习pytorch client-go学习 tensorflow学习 提高gpu 利用率 GPU与容器的结合 GPU入门 AI云平台梳理 tf-operator源码分析 k8s批处理调度/Job调度 喜马拉雅容器化实践 Kubernetes 实践 学习rpc BFF openkruise学习 可观察性和监控系统 基于Kubernetes选主及应用 《许式伟的架构课》笔记 Admission Controller 与 Admission Webhook 发布平台系统设计 k8s水平扩缩容 Scheduler如何给Node打分 Scheduler扩展 深入controller openkruise cloneset学习 controller-runtime源码分析 pv与pvc实现 csi学习 client-go informer源码分析 kubelet 组件分析 调度实践 Pod是如何被创建出来的? 《软件设计之美》笔记 mecha 架构学习 Kubernetes events学习及应用 CRI 资源调度泛谈 业务系统设计原则 grpc学习 元编程 以应用为中心 istio学习 下一代微服务Service Mesh 《实现领域驱动设计》笔记 概率论 serverless 泛谈 《架构整洁之道》笔记 处理复杂性 那些年追过的并发 服务器端编程 网络通信协议 架构大杂烩 如何学习架构 《反应式设计模式》笔记 项目的演化特点 反应式架构摸索 函数式编程的设计模式 服务化 ddd反模式——CRUD的败笔 研发效能平台 重新看面向对象设计 业务系统设计的一些体会 函数式编程 《左耳听风》笔记 业务程序猿眼中的微服务管理 DDD实践——CQRS 项目隔离——案例研究 《编程的本质》笔记 系统故障排查汇总及教训 平台支持类系统的几个点 代码腾挪的艺术 abtest 系统设计汇总 《从0开始学架构》笔记 初级权限系统设计 领域驱动理念 现有上传协议分析 移动网络下的文件上传要注意的几个问题 推送系统的几个基本问题 做配置中心要想好的几个基本问题 不同层面的异步 分层那些事儿 性能问题分析 用户认证问题 资源的分配与回收——池 消息/任务队列

标签

k8s设备管理 多类型负载协调员Koordinator controller-runtime细节分析 finops学习 kubevela多集群 kubevela中cue的应用 基于k8s的工作流 容器和CPU那些事儿 kubevela源码分析 数据集管理fluid 应用管理平台kubevela karmada支持crd 多集群管理 helm 从混部到统一调度 volcano特性源码分析 kubebuilder 学习 client-go学习 tf-operator源码分析 k8s批处理调度/Job调度 喜马拉雅容器化实践 Kubernetes 实践 openkruise学习 基于Kubernetes选主及应用 Admission Controller 与 Admission Webhook k8s水平扩缩容 Scheduler如何给Node打分 Scheduler扩展 深入controller openkruise cloneset学习 controller-runtime源码分析 pv与pvc实现 csi学习 client-go informer源码分析 kubelet 组件分析 调度实践 Pod是如何被创建出来的? Kubernetes events学习及应用 CRI 资源调度泛谈 如何学习Kubernetes 以应用为中心 kubernetes operator kubernetes扩缩容 serverless 泛谈 什么是云原生 自定义CNI IPAM docker和k8s安全访问机制 Kubernetes监控 Kubernetes 控制器模型 Kubernetes资源调度——scheduler Kubernetes类型系统 Kubernetes源码分析——controller mananger Kubernetes源码分析——apiserver Kubernetes源码分析——kubelet Kubernetes介绍 Kubernetes源码分析——从kubectl开始 kubernetes yaml配置 CNI——容器网络是如何打通的 当我在说PaaS时,我在说什么 《深入剖析kubernetes》笔记 Kubernetes存储 访问Kubernetes上的Service Kubernetes副本管理 Kubernetes pod 组件

mecha 架构学习

2020年05月11日

简介

两篇文章很多深度,需要多次重复阅读。

Mecha:将Mesh进行到底

Multi-Runtime Microservices Architecture[译] 多运行时微服务架构

云原生时代,Java危矣?不可变基础设施的内涵已不再局限于方便运维、程序升级和部署的手段,而是升华一种为向应用代码隐藏环境复杂性的手段,是分布式服务得以成为一种可普遍推广的普适架构风格的必要前提。

Kubernetes+Mesh是不够的

对于Kubernetes,要管理的最小原语是容器,它专注于在容器级别和流程模型上交付分布式原语。这意味着它在管理应用的生命周期,健康检查,恢复,部署和扩展方面做得很出色,但是在容器内的分布式应用的其他方面却没有做得很好,例如灵活的网络,状态管理和绑定。

Dapr 在阿里云原生的实践

  1. Service Mesh 的实现,本质是原协议转发,原协议转发可以给应用带来零侵入的优势。但是原协议转发也带来了一些问题,应用侧中间件SDK还需要去实现序列化和编解码工作,所以在多语言实现方面还有一定成本;随着开源技术的不断发展,使用的技术也在不断迭代,如果想从 Spring Cloud 迁移到 Dubbo ,要么应用开发者需要切换依赖的 SDK,如果想借助Service Mesh来达到这个效果,Service Mesh 需要进行协议转换,成本较高。
  2. Service Mesh 更加聚焦于服务间的通讯,而对其他形态的 Mesh 的支持上非常少。比如 Envoy, 除了在 RPC 领域比较成功外,在 Redis、消息等领域的尝试都未见成效。如此多形态的 Mesh ,是共用一个进程吗?如果是共用一个进程,那么是共用一个端口吗?许多问题都没有答案。而控制面方面,从功能角度来看的话,大都围绕流量来展开。看过 xDS 协议里的内容,核心是围绕发现服务和路由来展开。其他类型的分布式能力,在 Service Mesh的控制面中基本没有涉及,更谈不上抽象各种类似 xDS 的协议去支持这些分布式能力。
  3. 用户在云上部署业务的形态主要有普通应用类型和FaaS类型。Faas 场景下,比较吸引用户的是成本和研发效率。FaaS 对多语言和编程 API 的友好性上有了更多诉求,那么 Service Mesh 在这两块还是不能给客户带来额外的的价值。

rpc 有mesh,db、mq、redis 都搞mesh,mesh 的未来一定不是更多的sidecar, 运维根本受不了。蚂蚁云原生应用运行时的探索和实践

  1. 跨语言 SDK 的维护成本高:拿 RPC 举例,大部分逻辑已经下沉到了 MOSN 里,但是还有一部分通信编解码协议的逻辑是在 Java 的一个轻量级 SDK 里的,这个 SDK 还是有一定的维护成本的,有多少个语言就有多少个轻量级 SDK,一个团队不可能有精通所有语言的研发,所以这个轻量级 SDK 的代码质量就是一个问题。
  2. 从 Service Mesh 到 Multi-Mesh:蚂蚁最早的场景是 Service Mesh,MOSN 通过网络连接代理的方式进行了流量拦截,其它的中间件都是通过原始的 SDK 与服务端进行交互。而现在的 MOSN 已经不仅仅是 Service Mesh 了,而是 Multi-Mesh,因为除了 RPC,我们还支持了更多中间件的 Mesh 化落地,包括消息、配置、缓存的等等。可以看到每个下沉的中间件,在应用侧几乎都有一个对应的轻量级 SDK 存在,这个在结合刚才的第一问题,就发现有非常多的轻量级 SDK 需要维护。为了保持功能不互相影响,每个功能它们开启不同的端口,通过不同的协议去和 MOSN 进行调用。例如 RPC 用的 RPC 协议,消息用的 MQ 协议,缓存用的 Redis 协议。然后现在的 MOSN 其实也不仅仅是面向流量了,例如配置就是暴露了一下 API 给业务代码去使用。 必然需要出现新的形态来解决 Sidecar 过多的问题,合并为一个或者多个 Sidecar 就会成为必然。

从云原生中间件的视角

无责任畅想:云原生中间件的下一站在云原生时代,不变镜像作为核心技术的 docker 定义了不可变的单服务部署形态,统一了容器编排形态的 k8s 则定义了不变的 service 接口,二者结合定义了服务可依赖的不可变的基础设施。有了这种完备的不变的基础设置,就可以定义不可变的中间件新形态 – 云原生中间件。云原生时代的中间件,包含了不可变的缓存、通信、消息、事件(event) 等基础通信设施,应用只需通过本地代理即可调用所需的服务,无需关心服务能力来源。

除了序列化协议和通信协议,微服务时代的中间件体系大概有如下技术栈:

  • RPC,其代表是 Dubbo/Spring Cloud/gRPC 等。
  • 限流熔断等流控,如 hystrix/sentinel 等。
  • Cache,其代表是 Redis。
  • MQ,其代表有 kafka/rocketmq 等。
  • 服务跟踪,如兼容 Opentracing 标准的各种框架。
  • 日志收集,如 Flume/Logtail 等。
  • 指标收集,如 prometheus。
  • 事务框架,如阿里的 seata。
  • 配置下发,如 apollo/nacos。
  • 服务注册,如 zookeeper/etcd 等。
  • 流量控制,如 hystrix/sentinel 等。
  • 搜索,如 ElasticSearch。
  • 流式计算,如 spark/flink。

把各种技术栈统一到一种事实上的技术标准,才能反推定义出不可变的中间件设施的终态。把上面这些事实上的中间件梳理一番后,整体工作即是:

  • 统一定义各服务的标准模型
  • 定义这些标准模型的可适配多种语言的 API
  • 一个具备通信和中间件标准模型 API 的 Proxy
  • 适配这些 API 的业务

Service Proxy 可能是一个集状态管理、event 传递、消息收发、分布式追踪、搜索、配置管理、缓存数据、旁路日志传输等诸多功能于一体的 Proxy, 也可能是分别提供部分服务的多个 Proxy 的集合,但对上提供的各个服务的 API 是不变的。Application Mesh 具有如下更多的收益:

  • 更好的扩展性与向后兼容性
  • 与语言无关
  • 与云平台无关
  • 应用与中间件更彻底地解耦
  • 应用开发更简单。基于新形态的中间件方案,Low Code 或者 No Code 技术才能更好落地。单体时代的 IDE 才能更进一步 – 分布式时代的 IDE,基于各种形态中间件的标准 API 之对这些中间件的能力进行组合,以 WYSIWYG 方式开发出分布式应用。
  • 更快的启动速度。蚂蚁云原生应用运行时的探索和实践FaaS 冷启预热池也是我们近期在探索的一个场景,大家知道 FaaS 里的 Function 在冷启的时候,是需要从创建 Pod 到下载 Function 再到启动的,这个过程会比较长。有了运行时之后,我们可以提前把 Pod 创建出来并启动好运行时,等到应用启动的时候其实已经非常简单的应用逻辑了,经过测试发现可以将从 5s 缩短 80% 到 1s。这个方向我们还会持续探索当中。
  • 以统一技术形态的 Service Mesh 为基础的云原生中间件技术体系真正发起起来,在其之上的 Serverless 才有更多的落地场景,广大中小企业才能分享云原生时代的技术红利,业务开发人员的编码工作就会越来越少,编程技术也会越来越智能–从手工作坊走向大规模机器自动生产时代。

Mecha 架构

云原生运行时的下一个五年 重sdk ==> mesh ==> 基础设施泛mesh化 ==>

在这种架构下,虽然应用跟基础设施之间加了一层网络代理,但对于基础设施协议部分的处理依然保留在 SDK 中,这就导致应用本质上还是要面向某个基础设施做开发,比如想使用 Redis 作为缓存实现,那么应用需要引入 Redis 的 SDK,未来如果想切换到 Memcache 等其他缓存实现,则必须对应用进行改造。由于 SDK 里仍然保留了通信、序列化等协议的处理逻辑,因此随着接入的语言越来越多样化,这里依然存在不能忽视的开发成本。换句话说,泛 Mesh 化改造带来的“轻” SDK 跟传统微服务架构相比虽然降低了异构语言接入基础设施的门槛,但是随着接入语言越来越多样,依赖的中间件能力越来越丰富,我们还需要尝试进一步降低这种门槛。如果对上述两个问题做一层抽象,本质上都可以归结为应用跟基础设施之间的边界不够清晰,或者说应用中始终嵌入了某种基础设施实现中特有的处理逻辑,导致两者一直耦合在一起。

dapr和mesh虽然有一些交集,但本质不同,Service Mesh 强调的是透明的网络代理,它并不关心数据本身,而 Dapr 强调的是提供能力,是真正站在应用的角度来思考如何降低应用的开发成本。

现代分布式应用的对外需求分为四种类型(生命周期,网络,状态,绑定)。

单机时代,我们习惯性认为 应用 ==> systemcall ==> 内核。 但实际上,换个视角(以应用为中心),应用 对外的需求由systemcall 抽象,最终由内核提供服务。那么在分布式时代,就缺一个类似systemcall 的分布式原语,把分布式的能力 统一标准化之后 给到应用。

当前的项目开发,开发人员就像老妈子一样,把db、redis、mq 等资源聚在一起,还得考虑他们的容量、负载、连接池等。后续,它们 会向水电一样,支持项目随取随用。

API 和配置的制订以及标准化,预计将会是 Mecha 成败的关键。PS:历史一次次的告诉我们:产品不重要,协议才重要,协议才是最直接反应理念的东西

  1. 数据库产品不重要,牛逼的是sql
  2. istio 还好, 牛逼的是xds

  1. 所有分布式能力使用的过程(包括访问内部生态体系和访问外部系统)都被 Runtime 接管和屏蔽实现
  2. 通过 CRD/ 控制平面实现声明式配置和管理(类似 Servicemesh)
  3. 部署方式上 Runtime 可以部署为 Sidecar 模式,或者 Node 模式,取决于具体需求,不强制

在传统的中间件模式下,应用和分布式能力是在一个进程中,以 SDK 方式进行集成。随着各种基础设施下沉,各种分布式能力从应用中移到了应用外。如 K8s 负责了生命周期相关的需求,Istio、Knative 等都负责一些分布式能力。如果将这些能力都移动到独立的 Runtime 中,那么这种情况无论从运维层面还是资源层面来看,都是没办法接受的。所以这时候肯定需要将部分 Runtime 进行整合,最理想的方式肯定是整合成一个。这种方式被定义成 Mecha ,中文意思是机甲的意思。那么对于将各种分布式能力进行整合的 Mecha Runtime 这一目标本身问题不大,那么怎么整合呢?对 Mecha 有什么要求呢?

  1. Mecha 的组件能力是抽象的,任何一个开源产品可以快速进行扩展和集成。
  2. Mecha 需要有一定的可配置能力,可以通过 yaml/json 进行配置和激活。这些文件格式最好能和主流的云原生方式对齐。
  3. Mecha 提供标准的 API ,和主应用之间的交互的网络通信基于此 API 来完成,不再是原协议转发,这样对于组件扩展和 SDK 的维护都能带来极大的便利性。 分布式能力中的生命周期,可以将部分能力交接过底层的基础设施,比如 K8s。当然有些复杂的场景,可能需要 K8s、APP、Mecha Runtime 一起来完成。

应用运行时落地——以dapr 为例

在云原生的时代,我们到底需要什么样的应用运行时?

以微软开源的 dapr 为例,应用所有与 外界的交互(消息队列、redis、db、rpc) 都通过dapr http api

  1. 消息队列:
    • 发布消息 http://localhost:daprport/v1.0/publish/<topic>
    • 订阅消息 dapr 询问app 要订阅哪些topic,dapr 订阅topic, 当收到topic 消息时,发给app
  2. rpc : 请求远程服务 http://localhost:daprport/v1.0/invoke/<appId>/method/<method-name>

为了进一步简化调用的过程(毕竟发一个最简单的 HTTP GET 请求也要应用实现 HTTP 协议的调用 / 连接池管理等),dapr 提供了各个语言的 SDK,如 java / go / python / dotnet / js / cpp / rust 。另外同时提供 HTTP 客户端和 gRPC 客户端。我们以 Java 为例,java 的 client API 接口定义如下:

public interface DaprClient {  
   Mono<Void> publishEvent(String topic, Object event);
   Mono<Void> invokeService(Verb verb, String appId, String method, Object request);
    ......
}

蚂蚁开源多运行时项目 Layotto 简介

Layotto 作为 Dapr 之外的一个应用运行时实现方案,目的就是希望把应用运行时跟 Service Mesh 两者的优势结合起来。因此 Layotto 是建立在 MOSN 之上,分工上希望让 MOSN 来处理网络部分,而自己负责向应用提供各种中间件能力。此外基于蚂蚁集团内部的生产运维经验,Layotto 还抽象了一套面向 PaaS 的 API,主要目的是希望把应用跟 Layotto 本身的运行状态透出给 PaaS 平台,让 SRE 可以快速了解应用的运行状态,降低日常运维的成本。

Dapr 项目给我们最大的启示在于,它定义了应用跟基础设施之间的边界,但应用需要的不仅仅是这些。Dapr 为我们提供了很好的思路,是一个好的开端,但还不能够完全覆盖我们想要的东西,我们希望可以完全定义应用跟依赖资源之间的边界,可以覆盖系统资源,基础设施,资源限制等多个环节。成为应用的“真”运行时,应用除了业务逻辑之外无需关注任何其他资源。以当前 Sidecar 思路的落地情况来看,无论是 Dapr,MOSN 还是 Envoy,解决的都是应用到基础设施的问题。而对于系统调用,资源限制等方面仍旧由应用自己完成,这部分操作不需要经过任何中间环节,而没有被接管就意味着很难统一治理,类似网络流量如果没有统一的出入口,治理起来自然会困难重重。同时如果不能对应用可访问的系资源进行精细化控制,那始终会存在安全隐患。现在一名业务开发人员想要上手写代码,不仅要熟悉本身的业务逻辑,还需要熟悉缓存、消息、配置等各种各样基础设施的实现细节,成本非常高,而一旦把边界定义清楚以后,会降低业务开发人员的上手门槛,进而降低整体的开发成本。PS:就好像,java至于jvm,python 至于python 代码,开发些代码 直接python script 即可,剩下的所有事情 python runtime 解决。

函数是不是下一站?FaaS 和 Dapr 结合的点:Dapr 能够给函数计算的价值就是提供多语言的统一的面向能力的编程界面,而开发者无需关注具体的产品。像 Java 语言如果要使用阿里云上的 OSS 服务,需要引入 maven 依赖,同时需要写一些 OSS 代码,而通过 Dapr 你只需要调用 Dapr SDK 的 Binding 方法即可以做到,方便编程的同时,整个可运行包也无需引入多余的依赖包,而是可控的。函数计算英文名是 Function Compute,简称为 FC。FC 的架构包含的系统比较多,和开发者相关的主要包括 Function Compute Gateway和函数运行的环境。FC Gateway主要负责承接流量,同时会根据承接的流量的大小,当前的 CPU、内存使用情况,对当前函数实例进行扩缩容。函数计算运行时环境部署在一个 Pod 中,函数实例在主容器中,dapr 则是在 sidecar 容器中。当有外部流量访问函数计算的服务时,流量会先走到 Gateway ,Gateway 会根据访问的内容将流量转发到提供当前服务的函数实例中,函数实例接收到请求之后如果需要访问外部资源,就可以通过Dapr 的多语言 SDK 来发起调用。这时候 SDK 会向 Dapr实例发起gRPC请求,而在dapr 实例中回根据请求的类型和 body 体,选择对应的能力和组件实现,进而向外部资源发起调用。

云原生运行时的下一个五年 如果未来以函数作为跟当前微服务架构具有同等地位的另一种基础研发模型,我们就需要考虑整个函数模式的生态建设问题

  1. 基础框架,得益于 WebAssembly 这项技术的支持,函数本身是可以使用多种主流语言进行开发, 但为了更好的管理每个函数,仍旧需要让业务同学在开发过程中遵循一定的模板,如函数加载时会执行一个 start() 方法,可以做一些初始化工作,卸载时会执行一个 destroy() 方法,这可以做一些清理工作。
  2. 开发调试
  3. 打包部署,wasm 跟镜像之间的关系是什么?K8s 是以镜像为基础来创建 Pod,而函数编译的产物是 wasm 文件
  4. 生命周期管理 + 资源调度,如何让 K8s 管理部署 wasm ?

对于编译好的 *.wasm 文件,我们把它打在一个镜像里,然后 push 到镜像仓库用于后续调度使用。我们自己实现了一个叫做 containerd-shim-layotto-v2 的插件,在 K8s 收到调度 Pod 的请求以后它会把真正的处理逻辑交给 Kubelet,然后再经过 Containerd 转交给我们的自定义插件,该插件会从目标镜像中提取出 *.wasm 文件让 Layotto 加载运行。目前 Layotto 集成了 wasmer 作为 wasm 的运行时。对于一个开发好的函数来说,首先把它编译成 *.wasm 文件,然后再构建成镜像,部署过程中只需要在 yaml 文件中指定 runtimeClassName 为 Layotto 即可。后续如创建容器、查看容器状态、删除容器等操作都保留了 K8s 的语义

我们来畅想一下未来可能的研发模型

  1. 在研发阶段,开发人员可以自由选择适合业务场景的语言编写代码。对于开发工具来说,除了本地 IDE 以外可能越来越多的人会选择 Cloud IDE 来开发,这将很大的提高开发人员的协作效率。
  2. 部署阶段,对于一些轻量的业务场景,可能会按照函数模型进行部署,而对于传统的业务,可能会保留 BaaS 模型,同时如果有更高的安全性诉求,一种可行方案是把业务部署在 Kata 之类的安全容器中。随着 Unikernel 技术的成熟,可能会有越来越多的人在这个方向上进行尝试,比如把 Layotto 打在 kernel 中跟应用一起编译部署。
  3. 最后在向用户提供服务的阶段,随着函数服务启动的速度越来越快,可以做到收到请求以后再加载运行函数,并且对它们可使用的资源进行严格精确的控制,真正做到按需计费。

Dapr 入门教程之发布订阅 未读 Dapr 入门教程之消息队列 未读 Dapr 可观测性之分布式追踪 未读 Dapr 可观测性之指标与日志 未读

OCI容器与Wasm初体验 未读

基础设施下沉的大趋势

软件架构的发展历史及其精彩。回顾阿里巴巴系统架构演进的历史,能让人了解国内甚至全球的软件架构的发展历史。淘宝最开始成立的时候,是单体应用;随着业务规模的发展,系统首先对硬件进行升级这种Scale Up的方式;但是很快发现这种方式遇到了各种各样的问题,所以在2008年开始引入了微服务的解决方案;SOA的解决方案是分布式的,对于稳定性,可观测性等方面,需要引入熔断、隔离、全链路监控等高可用方案;接下来面临的问题是怎么在机房、IDC层面来让业务达到99.99%以上可用的SLA,这时候就有了同城双机房、异地多活等解决方案。而随着云技术的不断发展,阿里巴巴拥抱和引导云原生技术的发展,积极拥抱云原生技术,以K8s为基础,积极开展云原生技术的升级。从这个历史中,我们可以发现,软件架构新的诉求越来越多,原先底层基础设施无法完成只能交给应用侧富SDK去完成,在K8s和容器逐渐成为标准之后,重新将微服务和一些分布式能力还给基础设施。未来的趋势是以Service Mesh和Dapr为代表的分布式能力下沉,释放云和云原生技术发展的红利。

从软件历史看架构的未来:编程不再是精英们的游戏

如何看待 Dapr、Layotto 这种多运行时架构?

  1. 如何看待“可移植性”。标准化 API 能满足所有需求吗?死生之地不可不察:论API标准化对Dapr的重要性 计算机科学中有一种思想:如果一个问题太难了解决不了,那就放宽假设,弱化需求。既然“可移植性”这个问题太难了,那就让我们弱化一下需求,先解决一些更简单的问题:“弱移植性”。
    1. level 0:业务系统换云平台部署时,需要改业务代码(比如换一套基础设施 sdk,然后重构业务代码)。
    2. level 1:换云平台部署时,业务代码不用改,但是需要换一套 sdk,重新编译。
    3. level 2:换云平台部署时,业务系统不需要改代码,不需要重新编译,但是 Sidecar 要改代码。
    4. level 3:换云平台部署时,业务系统和 Sidecar 都不需要改代码,不需要重新编译,只需要改配置。
    5. level 4:换依赖的开源产品时(比如原先使用 Redis,现在要换成别的分布式缓存),业务系统和 Sidecar 都不需要改代码。
  2. 让“下沉”合理化
    1. 多语言复用中间件,比如,以前的中间件都是为 Java 开发的,C++ 用不了,现在可以让 Node.js/Python/C++ 语言的应用通过 gRPC 调 Sidecar,复用中间件。
    2. 微服务启动加速、FaaS 冷启加速,原先微服务应用的框架比较重,比如有和配置中心建连、初始化、缓存预热之类的逻辑,现在这些启动逻辑都挪到 Runtime 里。当应用或者函数需要扩容时,可以复用原有 Runtime,不需要再做一遍类似的建连预热动作,从而达到启动加速的效果。
    3. 不用推动用户升级 sdk 了
    4. 让业务逻辑也能下沉
  3. 让“下沉”规范化:约束“私有协议”
  4. 如何划分 Serivce Mesh,Event Mesh 和 Multi-Runtime 的边界?Layotto 这个 Sidecar 支持了各种协议,好像已经“非驴非马”了。可以把 Dapr 的“标准化 API”看做“Sidecar 增强”。比如“InvokeService API”可以看成“Service Mesh 增强”,“Pubsub API”可以看成是“Event Mesh 增强”,“State API”可以看成“数据中间件增强”,这里说的数据中间件包括缓存流量转发和 DB Mesh。从这种角度看,Layotto 更像是 Sidecar 里的“API 网关”。

如果你把 Runtime 类比成操作系统的内核,那么 API 这层就是系统调用,负责抽象基础设施,简化编程,而不同的组件类似于驱动,负责把系统调用翻译成不同基础设施的协议。Runtime 把所有组件都放在一个进程里,类似于“宏内核”的操作系统把所有子模块都塞在一起,变成了巨石应用。巨石应用有什么问题?模块间互相耦合,隔离性不好,稳定性降低。如果 Dapr 或者 Layotto 的一个组件出现 bug,会影响整个 Sidecar。怎么解决巨石应用的问题呢?拆!一个思路是把 Runtime 按模块拆分,每个模块是一个 Container,整个 Runtime 以 DaemonSet 的形式部署:这种方案就像操作系统的“微内核”,不同子模块之间有一定的隔离性,但相互通信的性能损耗会高一些。那么应该选择单容器 Runtime 还是多容器 Runtime 呢?这就像操作系统选择“宏内核”还是“微内核”架构,全看取舍。巨石应用的好处是子模块之间互相通信性能好,缺点是紧耦合,隔离性不好;如果把 Runtime 拆成多个 Sidecar 则刚好相反。

其它

未来:应用交付的革命不会停止Kubernetes 项目一直在做的,其实是在进一步清晰和明确“应用交付”这个亘古不变的话题。只不过,相比于交付一个容器和容器镜像, Kubernetes 项目正在尝试明确的定义云时代“应用”的概念。在这里,应用是一组容器的有机组合,同时也包括了应用运行所需的网络、存储的需求的描述。而像这样一个“描述”应用的 YAML 文件,放在 etcd 里存起来,然后通过控制器模型驱动整个基础设施的状态不断地向用户声明的状态逼近,就是 Kubernetes 的核心工作原理了。PS: 以后你给公有云一个yaml 文件就可以发布自己的应用了。

解读容器 2019:把“以应用为中心”进行到底云原生的本质是一系列最佳实践的结合;更详细的说,云原生为实践者指定了一条低心智负担的、能够以可扩展、可复制的方式最大化地利用云的能力、发挥云的价值的最佳路径。这种思想,以一言以蔽之,就是“以应用为中心”。正是因为以应用为中心,云原生技术体系才会无限强调让基础设施能更好的配合应用、以更高效方式为应用“输送”基础设施能力,而不是反其道而行之。而相应的, Kubernetes 、Docker、Operator 等在云原生生态中起到了关键作用的开源项目,就是让这种思想落地的技术手段。

分布式系统在 Kubernetes 上的进化 是讲mecha的,但对k8s有一个新的视角

  1. 从一开始,进行健康状况探测的能力就是 Kubernetes 受欢迎的原因
  2. 围绕应用程序的托管生命周期–你不再控制何时启动、何时关闭服务。你相信平台可以做到这一点。Kubernetes 可以启动你的应用;它可以将其关闭,然后在不同的节点上移动它。为此,你必须正确执行平台在应用启动和关闭期间告诉你的事件。
  3. 围绕着声明式部署。这意味着你不再需要启动服务;检查日志是否已经启动。你不必手动升级实例
  4. 声明你的资源需求。 Kubernetes将为我们做出最佳的决策。

互联网后端架构演进及未来猜想

  1. 单体 ==> 按业务功能拆分 ==> SOA ==> 微服务( 富sdk ==> service mesh) ==> 统一编程平面/应用运行时(比如dapr) ==> Serverless。回顾整个历史的发展,应用被不断拆分的越来越细,且从前期的横向拆分到后面的 sidecar 等方式的纵向单节点内的拆分
  2. 如果将来统一编程平面成了后端开发者的编程标准,那么将彻底屏蔽所有中间件以及基础设施和服务调用的差异,开发者的所有业务逻辑都将由标准 api 来组合完成,就像内核接口和系统调用一样,底层基础设施就变成了这个”庞大操作系统的内核”
  3. 云原生编程语言:既然 Serverless 目的是让业务不再关注服务器等基础设施的细节,那么能不能直接从编程语言下手,细到每一个对象的 new ,每一行语句的执行都能被整个集群内分布式的调度(比函数级别更细),而开发者方编程的时候只需要把这个集群都当成一个巨大的单机机器即可

知乎是怎么落地Istio的?我们将为业务:

  1. 提供缓存能力⽽不是 Redis、Memcached ……
  2. 提供异步通信⽽不是 Kafka、Pulsar ……
  3. 提供存储能⼒⽽不是 MySQL、TiDB ……
  4. 提供同步通信⽽不是 Dubbo、Spring Cloud、go-micro