第一部分:执行摘要:2025年 Kotlin Multiplatform 最终评估

 

2025年,Kotlin Multiplatform (KMP) 已经毫无疑问地成为一项成熟且可用于生产环境的技术,尤其在Android与iOS应用之间共享业务逻辑方面。其来自JetBrains和Google的双重战略支持 ,已经巩固了它作为该领域一流解决方案的地位。对于在2025年考虑采用KMP的团队而言,最关键且最微妙的决策已不再是“是否能用”,而是“如何架构用户界面(UI)”。

近期,Compose Multiplatform for iOS的稳定版发布 ,为开发者开辟了两条截然不同但均可行的路径:

  1. 共享逻辑,原生UI (The Pragmatic Approach):一种灵活的、原生优先的策略,仅共享非UI部分的业务逻辑,而UI层则由各个平台的原生技术(Android的Jetpack Compose/XML,iOS的SwiftUI/UIKit)独立构建。
  2. 共享逻辑与UI (The Unified Approach):一种追求高效率的统一策略,使用Compose Multiplatform将业务逻辑和UI代码一并共享。

这一选择对团队结构、开发体验、最终产品的用户体验(UX)乃至项目风险管理都具有深远的影响。本报告将深入剖析KMP生态系统的现状,对这两种UI策略进行详尽的比较分析,并提供一个可操作的决策框架,旨在为技术决策者在进行这项重大的技术投资时提供清晰、有据可依的指引。

 

第二部分:平台现状:核心技术与战略支持

 

要评估一项技术的成熟度,首先必须审视其底层基础的稳定性以及来自行业领导者的战略承诺。在2025年,KMP在这两方面都表现出前所未有的强势。

 

2.1 K2编译器与Kotlin 2.2:构建于速度与现代性之上的基础

 

KMP的性能和开发效率在2025年得到了根本性的提升,这主要归功于K2编译器的全面落地和Kotlin语言本身的持续进化。

  • K2编译器成为默认选项:从IntelliJ IDEA 2025.1版本开始,K2编译器已成为默认选项 。这不仅仅是一次版本更新,而是一次对编译器架构的彻底重写。JetBrains内部一个包含超过1200万行Kotlin代码的巨型项目,在切换到K2模式后,构建时间缩短了40% 。这一成就直接解决了跨平台项目长期以来的痛点之一:缓慢的编译和持续集成(CI)流程,极大地提升了开发生产力。
  • Kotlin 2.2语言新特性:随着Kotlin 2.2.0成为最新的稳定版本 ,一系列新的语言特性,如

    when表达式的守卫条件(guard conditions)和上下文参数(context parameters),进一步增强了代码的安全性和表达力 。这意味着开发者可以编写出更健壮、更易于维护的共享业务逻辑。同时,2.2.20的Beta版本已经发布 ,显示出JetBrains正以极快的节奏进行迭代和优化。

 

2.2 2025年路线图:双轨并行的市场策略

 

JetBrains为KMP制定的2025年路线图清晰地展示了一种双轨并行的市场渗透策略,旨在满足不同类型团队的需求,从而最大化其市场接受度。

  • 第一轨道:“全栈Kotlin”路径与Compose Multiplatform:JetBrains的首要任务是将Compose Multiplatform for iOS打造成一流的稳定解决方案。2025年的工作重点包括:实现与Jetpack Compose的功能对等、优化iOS端的渲染性能,以及完善导航、资源管理、无障碍(Accessibility)和国际化等核心功能 。在KotlinConf 2025上正式宣布其稳定,是整个生态系统的一个里程碑事件 。
  • 第二轨道:“务实集成”路径与Swift导出:与此同时,JetBrains正在积极开发一种直接将Kotlin代码导出为Swift模块的机制,目标是在2025年发布首个公开版本 。该工具旨在提供与现有Objective-C桥接相当的用户体验,同时克服后者的种种限制,例如能更好地处理可空类型、泛型和枚举,使Swift调用Kotlin代码时感觉更自然、更符合语言习惯 。

这种双轨策略并非摇摆不定,而是对市场需求的深刻洞察。它允许KMP同时吸引两个截然不同的用户群体:一是追求最大化代码复用和统一开发流程的初创公司或Android背景浓厚的团队(Compose Multiplatform路径);二是拥有庞大且对原生工具有着深厚执念的iOS团队的大型企业(Swift导出路径)。这种灵活性是KMP相较于Flutter等“全有或全无”框架的显著竞争优势。通过提供一个低摩擦的集成选项,KMP有效地降低了在复杂组织中推广的阻力。

 

2.3 Google的官方背书:从实验到生产级支持

 

如果说JetBrains的努力是KMP发展的内生动力,那么Google的官方支持则为其注入了强大的外部可信度。

  • 官方支持与生产应用:在Google I/O大会上,Google正式宣布为KMP提供支持,特别是用于在Android和iOS之间共享业务逻辑 。这并非空头支票,Google已将自家的旗舰应用,如Google Docs,在iOS端投入使用KMP进行生产环境的运行,并报告其性能与之前相比持平甚至更优 。这种规模的应用实践为KMP的稳定性、性能和可扩展性提供了最有力的证明。
  • Jetpack库的多平台化:Google正在投入大量资源,将核心的Jetpack库进行多平台化改造。目前,包括Room(数据库)、DataStore(数据存储)、Paging(分页加载)、ViewModel(视图模型)在内的多个关键库已经发布了支持iOS的稳定版本 。这对开发者而言是一个颠覆性的改变,因为它意味着团队可以在共享代码中直接使用那些他们在Android开发中早已熟悉、功能强大且经过大规模实战检验的架构组件。

下表总结了KMP技术栈中关键组件在2025年的成熟度状态,为技术决策者提供了一个清晰的概览。

表2.1:KMP技术栈成熟度 (2025年)

组件 状态 2025年关键里程碑 相关资料来源
Kotlin语言 (K2) Stable K2编译器成为IDE默认选项,显著提升构建速度
KMP核心框架 Stable 已在众多大型应用中得到生产验证
Compose Multiplatform for iOS Stable API稳定,正式发布,可用于生产环境
Compose Multiplatform for Desktop Stable 已稳定,广泛用于桌面应用开发
Compose for Web (Wasm) Beta 性能和API对等性持续改进,向Beta阶段迈进
Kotlin-to-Swift 导出 Experimental 计划2025年发布首个公开版本
Amper 构建工具 Experimental 2025年重点支持KMP移动应用开发

 

第三部分:核心决策:用户界面(UI)的架构选择

 

在确认KMP核心技术已然成熟之后,团队面临的最关键决策便是如何处理UI。2025年,两条路径都已清晰且可行,但它们各自通往截然不同的开发模式和产品形态。

 

3.1 策略一:共享逻辑,原生UI(务实主义路径)

 

这种模式是KMP最经典、风险最低的应用方式,它将代码共享的边界严格限制在非UI层面。

  • 架构解析:该模型的核心是创建一个共享的Kotlin模块,其中包含所有的业务逻辑、数据层(网络请求、数据库访问)、状态管理等。然后,在此之上构建两个完全独立的、100%原生的UI层:Android端使用Jetpack Compose或传统的XML,iOS端则使用SwiftUI或UIKit 。当共享代码需要调用平台特有的API(如传感器、文件系统、加密算法)时,通过KMP提供的

    expect/actual机制来实现,这允许在共享模块中定义一个期望的(expect)接口,然后在各个平台模块中提供实际的(actual)实现 。

  • 优势分析
    • 极致的原生保真度:UI/UX是完全原生的,可以完美地遵循各平台的设计规范、交互习惯和动画效果,确保用户获得最流畅、最地道的体验,不存在任何“非原生感” 。
    • 发挥团队专长:允许Android和iOS的专家团队继续使用他们最擅长的工具链(Android Studio/Compose, Xcode/SwiftUI),最大程度地减少了学习成本和文化摩擦 。
    • 低风险的增量迁移:对于已有的原生应用,这是理想的迁移路径。团队可以从共享一小部分逻辑开始(例如一个复杂的算法或网络层),然后逐步扩大共享范围,风险完全可控 。
  • 挑战与开发体验
    • “两个团队,一个大脑”的协作难题:此模式的成败高度依赖于团队间的无缝协作。如果Android和iOS团队处于孤岛状态,很容易出现“逻辑分叉”的问题:iOS开发者可能会因为共享模块过于“Android中心化”或难以使用,而选择在Swift中重写逻辑,这便完全违背了KMP的初衷 。
    • 状态管理的互操作性:将Kotlin现代的状态管理模式(如StateFlow)转换为SwiftUI能够方便消费的形式,并非易事。如果没有清晰的架构约定和良好的封装,这里很容易成为产生摩擦和bug的重灾区 。
    • iOS端的调试困境:从Xcode中调试共享的Kotlin代码至今仍是一个挑战,断点功能可能不稳定。这常常迫使iOS开发者将共享模块视为一个“黑盒”,严重影响了生产力,也增加了他们重写逻辑的动机 。
  • 案例深度剖析
    • Netflix:在其内部使用的Prodicle工作室应用中,Netflix利用KMP共享了平台无关的业务逻辑、网络层(使用Ktor)和数据持久化(使用SQLDelight),尤其是在需要强大离线支持的场景下,同时保持UI完全原生 。
    • Forbes:共享了超过80%的业务逻辑,这使得他们能够在两个平台同步推出新功能,同时保留了根据平台特性定制原生UI的灵活性 。
    • McDonald’s:共享了应用内支付等复杂功能的逻辑。在KotlinConf 2025上,他们展示了一个关键的创新:将导航逻辑(即决定在何种条件下从哪个屏幕跳转到哪个屏幕)也放入了KMP共享模块,而UI界面和导航控制器本身仍然是原生的 。

 

3.2 策略二:共享逻辑与UI,采用Compose Multiplatform(统一化路径)

 

随着Compose Multiplatform for iOS的稳定,这条追求极致代码复用的路径变得切实可行。

  • 架构解析:在这种模式下,一个单一的Kotlin代码库不仅包含业务逻辑,还包含了使用Compose Multiplatform编写的声明式UI代码 。这套共享的UI代码在Android上通过Jetpack Compose渲染,在iOS上则通过一个宿主

    UIViewController嵌入,并利用原生的图形库进行绘制 。

  • 优势分析
    • 最大化代码复用:可以实现高达95%甚至更高的代码共享,极大地减少了开发和维护的工作量,避免了“一份需求,两份实现”的资源浪费 。
    • 提升开发速度:由于无需在两个平台上分别编写和同步UI代码,新功能的交付速度得以大幅提升。这对于开发MVP(最小可行产品)、内部工具,或者那些品牌视觉一致性高于平台原生性的应用来说,优势尤为明显 。
    • 统一的团队结构:可以组建一个单一的Kotlin开发者团队来负责两个平台的开发,简化了团队管理和知识传递。例如,Physics Wallah公司通过此举将Android和iOS工程师整合成了一个统一的移动团队 。
  • 挑战与“稳定版”的现实: Compose for iOS的“稳定”主要指API的稳定性,为生产使用提供了保障。然而,开发者在实际项目中仍会遇到一些挑战 :
    • UI/UX的一致性问题:目前Compose Multiplatform缺少一套原生的“Cupertino”风格组件库,导致所有组件默认呈现Material Design风格,这在iOS上可能会显得格格不入。尽管滚动物理效果、文本处理等方面已经对齐了iOS的原生行为 ,但要让所有组件都达到完美的iOS原生质感,仍需大量的自定义工作 。
    • 性能与调试:虽然应用的启动时间与原生应用相当 ,但在复杂的UI界面上,开发者报告了布局卡顿(jank)、CPU占用率高和内存泄漏等问题。此外,由于Xcode的视图调试工具无法检查Compose的视图层级,可视化调试变得非常困难 。
    • 无障碍功能(Accessibility):将Compose视图正确地暴露给iOS的无障碍服务,目前仍存在一些挑战,可能会影响特殊用户群体的使用体验 。
  • 案例深度剖析
    • Bitkey by Block:这家公司最初采用了原生UI的策略,但后来主动迁移到了Compose Multiplatform。其核心原因并非技术瓶颈,而是组织效率:在一个没有严格区分iOS/Android专家的精干团队中,频繁在Jetpack Compose和SwiftUI之间切换上下文,既繁琐又容易出错。统一的UI代码库提升了他们对功能一致性的信心,并简化了开发流程 。
    • Feres & WallHub:这两个应用报告称使用Compose Multiplatform共享了90-100%的UI代码,充分展示了该策略在代码复用方面的巨大潜力 。

技术决策的核心,往往是在不同类型的风险之间进行权衡。在KMP的UI策略选择上,这一点体现得淋漓尽致。选择“原生UI”路径,意味着将赌注押在团队的协作和架构能力上,它最小化了产品/UX风险(保证了最佳用户体验),但最大化了执行/团队风险。一旦团队协作出现问题 ,其所有优势都将荡然无存。反之,选择“共享UI”路径,则最小化了

执行/团队风险(简化了开发流程和团队结构),但引入了产品/UX风险。团队可能会交付一个在iOS上感觉不够“地道”的应用,从而影响用户观感和接受度。

因此,这个决策并非纯技术问题,而是要求技术领导者对团队文化、技能储备和项目目标进行综合评估。

表3.1:UI策略决策矩阵 (KMP原生UI vs. Compose Multiplatform)

决策因素 策略1:原生UI (共享逻辑) 策略2:共享UI (Compose Multiplatform) 推荐指南
用户体验保真度 极高 (100%原生) 中到高 (依赖优化,可能存在非原生感) 如果应用对平台原生体验有极致要求,选策略1。
开发速度 (上市时间) 较慢 (需开发两套UI) 极快 (UI只需开发一次) 对于MVP或追求快速迭代的项目,策略2优势明显。
代码复用率 (%) 30-80% (仅限逻辑) 90-95+% (逻辑+UI) 如果最大化代码复用是首要目标,选策略2。
长期维护成本 较高 (需维护两套UI代码) 较低 (单一代码库) 对于需要长期维护的大型项目,策略2成本更低。
团队结构与技能 需要专业的Android和iOS团队,并强调协作 可由统一的Kotlin团队负责,降低招聘和管理复杂性 策略1适合现有的大型专业团队,策略2适合精干或新组建的团队。
原生API/SDK访问 无缝直接访问 通过expect/actual或平台视图嵌入,可能增加复杂性 如果应用深度依赖复杂的原生SDK(如ARKit),策略1更直接。
生态与库支持 UI层可使用所有原生库 共享UI库正在发展,但不如原生生态成熟 策略1在UI库选择上更丰富。
项目风险画像 执行风险高,产品风险低 产品风险高,执行风险低 需根据公司对不同风险的容忍度进行选择。

 

第四部分:开发者生态:工具、库与社区

 

一项技术能否在实际项目中成功落地,其周边的生态系统——包括开发工具、可用的库和社区支持——起着决定性的作用。

 

4.1 开发环境:IDE与构建工具

 

  • IDE的统一:目前,KMP的开发体验已经全面整合到开发者熟悉的IntelliJ IDEA和Android Studio中 。JetBrains已经调整了策略,不再将实验性的Fleet IDE作为KMP的主要开发环境,而是加倍投入于完善基于IDEA的工具链 。官方的KMP插件提供了项目创建向导和集成化工具,尽管一些开发者仍然认为,与纯原生开发相比,其流程中还存在一些摩擦点 。
  • 构建系统的挑战:Gradle:Gradle是当前官方指定的构建工具。然而,它的复杂性是一个被广泛承认的痛点,特别是对于不熟悉JVM生态的开发者(例如大多数iOS开发者)而言。多平台的Gradle配置可能相当复杂,并且是导致构建失败的常见原因 。
  • 构建系统的未来:Amper的承诺:JetBrains正在开发一个名为Amper的实验性项目。这是一个专为Kotlin和JVM设计的、更为简洁的构建工具。2025年的重点目标就是使其能够完全支持包含共享UI的KMP移动应用开发 。Amper的存在本身就是对Gradle复杂性的承认,它预示着未来KMP项目的配置体验将得到极大的改善。

 

4.2 库生态景观:两大生态的融合

 

KMP的库生态系统在2025年呈现出前所未有的强大,这得益于两个成熟生态的交汇。

  • 成熟的Kotlin原生库生态:由JetBrains和社区主导开发的核心库已经非常成熟且可用于生产。这包括用于并发的kotlinx.coroutines、用于JSON序列化的kotlinx.serialization、用于网络编程的Ktor,以及用于本地数据库的SQLDelight 。这些库构成了绝大多数KMP项目的基石。
  • 颠覆性的Jetpack多平台库生态:Google承诺将Jetpack库多平台化,是近年来KMP生态最重要的发展。像RoomDataStorePagingViewModel这样的库,现在已经为iOS提供了稳定(Tier 1级别)的支持 。这意味着团队可以在共享代码中使用与Android开发完全相同的、功能强大、文档齐全且经过实战检验的架构组件。
  • 分级支持模型:必须清楚地认识到,Jetpack库的支持是分级的。Android、iOS和JVM属于Tier 1级别,意味着得到了全面的测试和支持。macOS和Linux等桌面平台属于Tier 2级别(部分测试)。而Web/Wasm则属于Tier 3级别(实验性) 。这意味着,虽然KMP在移动端的故事非常强大,但如果要扩展到其他平台,就需要仔细核对所需库的支持级别。

KMP生态系统的力量源于两个强大库生态(Kotlin自身和Android Jetpack)的融合,但其当前的短板在于将它们粘合在一起的工具(Gradle)。JetBrains显然已经认识到Gradle的陡峭学习曲线是阻碍KMP被更广泛采用(尤其是在非JVM背景的iOS开发者中)的主要瓶颈。因此,Amper项目是一个旨在降低入门门槛、平滑开发者体验的战略性举措。对于2025年的决策者来说,当前的工具链可以被视为一个“充满挑战但正在改善”的现状,并且已经有了一条官方明确支持的、通往更美好未来的道路。

表4.1:关键Jetpack多平台库对iOS的支持情况 (2025年)

Jetpack库 Maven Group ID 最新稳定版 对iOS的支持级别 在共享代码中的常见用途
Room androidx.room 2.7.2 Tier 1 在共享模块中实现结构化的本地数据库
DataStore androidx.datastore 1.1.7 Tier 1 用于存储键值对或类型化的对象,替代SharedPreferences
Paging androidx.paging 3.3.6 Tier 1 实现高效的、支持网络和数据库的分页加载逻辑
ViewModel androidx.lifecycle 2.9.2 Tier 1 在共享模块中管理UI相关的状态和业务逻辑
Lifecycle androidx.lifecycle 2.9.2 Tier 1 在共享ViewModel中管理协程作用域
Collection androidx.collection 1.5.0 Tier 1 提供优化的集合类,如LruCache
SQLite androidx.sqlite 2.5.2 Tier 1 为Room提供底层的SQLite驱动
Annotation androidx.annotation 1.9.1 Tier 1 在共享代码中使用注解以增强静态检查

注:版本号基于截至2025年8月1日的数据 。

 

第五部分:性能、架构与团队动态

 

一个成功的技术选型不仅要看其功能,还要评估其非功能性表现以及对团队组织结构的影响。本部分将探讨KMP在实际运行中的性能、构建可维护应用的架构模式,以及成功实施所必需的“人”的因素。

 

5.1 性能画像:原生设计

 

KMP的性能优势根植于其核心设计理念:将代码编译为目标平台的原生二进制文件(Android为JVM字节码,iOS为基于LLVM的原生代码) 。这意味着共享的业务逻辑是以原生速度运行的,这与那些依赖于JavaScript桥接的框架形成了鲜明对比,后者在复杂计算或高频交互时可能成为性能瓶颈 。

  • 基准测试数据:与React Native相比,KMP应用在基准测试中展现出更优的性能。其启动时间快约30%,内存消耗低15-20%,电池消耗也减少了10-15% 。
  • UI性能:当采用“原生UI”策略时,UI性能与完全原生的应用完全相同 。当采用“共享UI”(Compose Multiplatform)策略时,对于大多数场景,性能表现良好且与原生应用相当。然而,如前所述,在处理极其复杂的UI或长列表时,可能会遇到需要特别优化的性能问题,如卡顿或CPU占用过高 。

 

5.2 架构最佳实践:构建长青应用

 

社区在长期的实践中,已经沉淀出了一套行之有效的KMP架构模式。

  • 整洁架构 (Clean Architecture):这是KMP社区中最主流的架构模式。它倡导将应用清晰地划分为不同的层次(如领域层、数据层、表现层),这与KMP将共享逻辑与平台特定UI分离的理念完美契合 。
  • 多模块结构:对于大型项目,强烈建议采用多模块的组织方式,而不是将所有代码都放在一个庞大的shared模块中。可以按功能划分模块(如core-network, feature-login, feature-dashboard),这有助于改善编译速度、提高代码的可维护性,并支持团队并行开发 。
  • expect/actual模式的审慎使用:这是KMP实现平台互操作性的关键机制。最佳实践是将其严格限制在架构的边界处(例如,在数据层提供平台特定的数据库驱动或网络客户端实现),以此来封装平台差异,避免平台相关的细节泄漏到核心业务逻辑中 。

 

5.3 人的因素:团队结构与协作

 

KMP项目最常见的失败原因并非技术本身,而是组织和协作上的障碍。

  • 最大的风险:团队孤岛:一种注定会失败的模式是:Android团队负责开发共享模块,然后像“扔过墙”一样将其交给iOS团队使用 。
  • “黑盒”反模式:这种孤岛式的协作方式,会导致iOS团队将KMP模块视为一个不透明、不可信的第三方依赖。当遇到问题时,他们无法或不愿去调试Kotlin代码,最终选择在Swift中重写逻辑,从而使KMP的优势荡然无存 。
  • 成功的模式:统一的移动团队:成功的KMP实践需要文化的转变。像Physics Wallah这样的公司,已经将其Android和iOS工程师整合成一个统一的“移动团队” ,共同对整个代码库负责。
  • 协作最佳实践
    • 共同所有权:Android和iOS开发者都必须对共享模块负责。这可能需要对iOS开发者进行Kotlin/Gradle的培训,或者在处理共享代码相关的任务时采用结对编程 。
    • 清晰的架构契约:共享模块与原生UI之间的接口必须被良好地定义和文档化,并避免做出“Android中心化”的假设 。
    • 同理心与沟通:向团队推广KMP需要充分理解各平台专家的顾虑。JetBrains官方甚至提供了详细的指南,指导如何管理这一过渡过程,包括解释采用KMP的“原因”,以及创建概念验证项目来展示其价值 。

KMP不仅仅是一项技术,它更像是一个组织变革的催化剂。它迫使团队重新审视移动开发的协作模式。一个项目的成功,在很大程度上不取决于代码写得如何,而取决于协作模式是否有效。一个技术领导者在考虑KMP时,不仅要为开发时间做预算,更要为组织变革管理(如培训、工作坊、团队重组)预留资源。对KMP投资的回报率,与跨团队协作的水平成正比。忽视人的因素,是导致失败的最大预兆。

 

第六部分:竞争格局与最终建议

 

为了做出最终决策,我们需要将KMP置于更广阔的跨平台市场中进行比较,并综合所有分析,给出一套明确的建议。

 

6.1 KMP vs. Flutter vs. React Native:2025年战略快照

 

这三个框架并非简单的优劣之分,而是代表了三种不同的开发哲学和应用场景。

  • Kotlin Multiplatform:“务实的”原生派。它最适合那些希望在保留100%原生UI/UX控制权的同时共享业务逻辑的团队,或者是那些寻求使用Compose技术栈实现最大化代码复用的团队。其核心优势在于性能灵活性
  • Flutter:“统一的”UI派。它最适合用单一代码库构建视觉效果丰富、高度定制化的UI。其核心优势在于UI一致性和UI密集型应用的开发速度
  • React Native:“Web到移动”的桥梁。它最适合拥有现有JavaScript/React技术栈的团队。其核心优势在于庞大的生态系统和对于Web开发者极低的入门门槛

表6.1:KMP vs. Flutter vs. React Native:2025年战略快照

属性 Kotlin Multiplatform Flutter React Native
核心哲学 共享代码,而非平台抽象 一套代码库,绘制所有平台的UI 用Web技术构建原生应用
主要用例 共享业务逻辑,原生UI优先;或全栈Kotlin UI驱动,品牌一致性强的应用 Web团队快速开发移动端MVP
性能模型 编译为原生代码,性能极高 编译为原生代码,自带渲染引擎,性能高 通过JS桥接与原生通信,性能良好但有瓶颈
UI策略 灵活可选:原生UI或共享UI (Compose) 统一UI:所有平台使用相同的Widget 使用原生组件,通过JS控制
代码复用 灵活 (30% – 95+%) 极高 (UI+逻辑) 高 (UI+逻辑)
学习曲线 (对Android开发者) 极低 中等 (需学习Dart) 中等 (需熟悉React生态)
学习曲线 (对iOS开发者) 中等 (需学习Kotlin/Gradle) 中等 (需学习Dart) 中等 (需熟悉React生态)
生态成熟度 快速成熟,受益于Kotlin和Android生态 成熟,社区活跃 非常成熟,库最丰富
企业支持 JetBrains + Google Google Meta

 

6.2 最终建议与项目准备清单

 

KMP在2025年的最终价值主张在于其战略灵活性。它是唯一一个不强迫开发者采用“一刀切”模式的主流框架。它允许企业根据自身需求,选择代码共享的程度——从共享10%(一个核心算法)到共享95%(全部逻辑和UI),并允许这种策略随着产品和团队的演进而动态调整。这种适应性降低了被单一技术路线锁定的风险。

在2025年,您的团队应该采纳Kotlin Multiplatform,如果:

  • 您的应用拥有复杂且关键的业务逻辑,必须在各平台间保持高度一致(例如金融、健康、物流领域) 。
  • 您正在计划迁移一个现有的大型原生应用,并需要一条低风险、可增量实施的代码共享路径 。
  • 您的团队已经拥有深厚的Kotlin/Android开发经验,这将极大地降低学习成本 。
  • 性能和真实的原生外观感受是不可妥协的需求(这将引导您选择“原生UI”策略) 。
  • 您是一个初创公司或一个优先考虑开发速度的团队,并且愿意拥抱完整的Compose Multiplatform技术栈(这将引导您选择“共享UI”策略) 。

您应该持谨慎态度或选择其他方案,如果:

  • 您的应用极其简单,主要以UI展示为主,几乎没有复杂的共享逻辑 。
  • 您的首要目标是以最快速度推出一个简单的MVP,且团队拥有强大的Web开发背景(在这种情况下,React Native的初始开发速度可能更快) 。
  • 您的组织内部,移动开发团队之间壁垒森严,存在强烈的协作抵触文化(这是采用KMP的最大危险信号) 。

总而言之,Kotlin Multiplatform在2025年已经发展成为一个强大、灵活且得到行业巨头支持的成熟技术。它为那些希望在代码复用带来的效率和原生应用提供的卓越体验之间找到最佳平衡点的团队,提供了一个无与伦比的解决方案。它并非适用于所有场景的“银弹”,但对于合适的项目和团队而言,它无疑是通往未来跨平台开发的一条坚实路径。

转载,原文 敖小剑  https://skyao.io/talk/202004-mecha-mesh-through-to-the-end/

摘要

Servicemesh落地实践三年,效果一直并不理想,到了该反思的时候了。Mecha作为面向服务的分布式能力抽象层,是Servicemesh模式的自然进化版本,预计也将是云原生化和Mesh化的必然趋势,让我们将Mesh进行到底。

Mecha介绍

什么是Macha?

Mecha一词,相信爱好动漫的同学应该都不陌生。是的,就是大家熟悉的那个Mecha(机甲):

Mecha这个词之所以出现在这里,主要是因为 Bilgin Ibryam 的这个博客文章 “Multi-Runtime Microservices Architecture”,提出了微服务架构的一个新的设想:Multiple Runtime。

备注:这篇博客文章强烈推荐阅读,我甚至建议在阅读本文之前先阅读这篇文章,因为我今天的内容,可以视为对这个文章的深度解读和思考。为了方便,这里提供一份中文翻译版本 多运行时微服务架构

在这篇博客中,Bilgin Ibryam 首先分析并总结了分布式应用的四大需求:

  • 生命周期(Lifecycle)
  • 网络(Networking)
  • 状态(State)
  • 捆绑(Binding)

由于每种需求存在的问题和局限性,导致传统解决方案如企业服务总线(ESB)及其变体(例如面向消息的中间件,更轻量级的集成框架等)不再适用。随着微服务架构的发展,以及容器和Kubernetes的普及和广泛使用,云原生思想开始影响这些需求的实现方式。未来的架构趋势是通过将所有传统的中间件功能移至其他运行时来全面发展,最后的目标是在服务中只需编写业务逻辑。

备注:详情请见原文,为了节约篇幅,这里只做简单概述,不完全引用原文内容。

下图是传统中间件平台和云原生平台的对比,传统中间件以各种SDK的方式提供能力,而云原生平台则通过各种外围Runtime(典型如大家熟悉的Servicemesh/Istio):

因此作者引入了Multiple Runtime的概念:

作者提出:很可能在将来,我们最终将使用多个运行时来实现分布式系统。多个运行时,不是因为有多个微服务,而是因为每个微服务都将由多个运行时组成,最有可能是两个运行时-自定义业务逻辑运行时和分布式原语运行时。

对多运行时微服务架构和Mecha的说明:

您还记得电影《阿凡达》和科学家们制作的用于去野外探索潘多拉的 Amplified Mobility Platform (AMP)“机车服”吗?这个多运行时架构类似于这些 Mecha-套装,为类人驾驶员赋予超能力。在电影中,您要穿上套装才能获得力量并获得破坏性武器。在这个软件架构中,您将拥有构成应用核心的业务逻辑(称为微逻辑/micrologic)和提供强大的开箱即用的分布式原语的sidecar mecha组件。Micrologic与mecha功能相结合,形成多运行时微服务,该服务将进程外功能用于其分布式系统需求。最棒的是,Avatar 2即将面世,以帮助推广这种架构。我们最终可以在所有软件会议上用令人赞叹的机甲图片代替老式的边车摩托车;-)。接下来,让我们看看该软件架构的详细信息。

这是一个类似于客户端-服务器体系结构的双组件模型,其中每个组件都是独立的运行时。它与纯客户端-服务器架构的不同之处在于,这两个组件都位于同一主机上,彼此之间有可靠的网络连接。这两个组件的重要性相当,它们可以在任一方向上发起操作并充当客户端或服务器。其中的一个组件称为Micrologic,拥有非常少的业务逻辑,把几乎所有分布式系统问题都剥离出去了。另一个伴随的组件是Mecha,提供了我们在本文中一直讨论的所有分布式系统功能(生命周期除外,它是平台功能)。

作者在这里正式提出了Mecha的理念:

思路大体是:Smart Runtime, Dumb Pipes

我对Mecha的理解是:业务逻辑在编码开始阶段应该是“裸奔”的,专注于业务逻辑的实现,而尽量不涉及到底层实现逻辑;而在运行时,则应该装备“机甲”,全副武装,大杀四方。熟悉的味道是吧?标准而地道的云原生思想。

Mecha的本质

作者在原文中探讨了Mecha运行时的特性:

  1. Mecha是通用的,高度可配置的,可重用的组件,提供分布式原语作为现成的能力。
  2. Mecha 可以与单个Micrologic组件一起部署(Sidecar模式),也可以部署为多个共享(注:我称之为Node模式)。
  3. Mecha不对Micrologic运行时做任何假设。它与使用开放协议和格式(例如HTTP/gRPC,JSON,Protobuf,CloudEvents)的多语言微服务甚至单体一起使用。
  4. Mecha以简单的文本格式(例如YAML,JSON)声明式地配置,指示要启用的功能以及如何将其绑定到Micrologic端点。
  5. 与其依靠多个代理来实现不同的目的(例如网络代理,缓存代理,绑定代理),不如使用一个Mecha提供所有这些能力。

下面是我对上述特性的个人理解:

  1. Mecha提供的是能力,以分布式原语体现的各种能力,而不局限于单纯的网络代理。
  2. Mecha的部署模型,不局限于Sidecar模式,Node模式在某些场景下(如Edge/IoT,Serverless FaaS)可能会是更好的方式。至少,Mecha下有机会按需选择,而不是绑死在Sidecar模式上
  3. Mecha和Micrologic之间的交互是开放而有API标准的,Mecha和Micrologic之间的“协议”体现在API上,而不是TCP通讯协议。这提供了一个契机:一个统一Micrologic和Mecha之间通讯方式的契机。
  4. Mecha可以以声明式的方式进行配置和控制,这非常符合云原生的理念,同样也使得API更关注于能力本身,而不是能力如何配置。
  5. 应用需要的能力如此之多(参见上面的图:分布式应用的四大需求),如果每个能力都对应一个代理(不管是Node还是Sidecar),数量会非常夸张,带来的运维压力会很可怕。因此,如Mecha这个名字暗示的,运行时应该是整套的形式提供能力,而不是分散。

如果用一句话来总结,那么我认为Mecha的本质应该是:

“面向应用的分布式能力抽象层”

如Servicemesh的本质是服务间通讯的抽象层一样,Mecha的本质是应用需要的各种分布式能力和原语,包括但不限于服务间通讯。

从这个角度上说,Mecha覆盖的范围是Servicemesh的超集:毕竟Servicemesh只覆盖到应用的部分需求(服务间通讯,还只限于同步/一对一/request-response模式),还有更多的分布式能力和原语有待覆盖。

换一句话说,Mecha的目标应该是:“将Mesh进行到底!”

Mecha的优势和未来

作者指出:Mecha的好处是业务逻辑和越来越多的分布式系统问题之间的松耦合。

下图是业务逻辑和分布式系统问题在不同架构中的耦合:

其实思路和Servicemesh是一脉相承的,只是覆盖的分布式能力更广泛一些。

有一个问题:Mecha会不会成为微服务架构的演进的下一个形态?我个人的答案:是,随着云原生的推进,分布式能力(以传统中间件为典型代表)下沉是大势所趋,Mesh化的范围必然会继续扩大,也就离Mecha的形态越来越近了。这也就是本文标题的立意所在,Mecha会是微服务乃至云原生的下一站。

微软Dapr

在介绍完 Mecha/Multiple Runtime 的理念之后,我们来看看目前微软新推出来的Dapr项目 —— 这应该是业界第一个Multiple Runtime的开源实践项目。

项目地址:https://github.com/dapr/dapr。

Dapr介绍

Dapr 是 Distributed Application Runtime (分布式应用运行时)的缩写,官方介绍说Dapr是“一种可移植的,事件驱动的运行时,用于构建跨云和边缘的分布式应用”。

Dapr的详细介绍是:

Dapr是一种可移植的,serverless的,事件驱动的运行时,它使开发人员可以轻松构建弹性,无状态和有状态微服务,这些服务运行在云和边缘上,并包含多种语言和开发框架。

Dapr 整理了构建微服务应用为开放,独立的构建块的最佳实践,使您能够使用自己选择的语言和框架来构建可移植的应用程序。 每个构建块都是独立的,您可以在应用中使用其中的一个或多个。

Dapr的功能和定位,下面这一张图就可以概括了:

  • 最底下基础设施是各种云平台(主流公有云都支持)或者边缘环境
  • 其上是dapr提供的分布式能力,dapr称之为“building block”。
  • 这些building block的能力,以统一的API(支持HTTP和gRPC)对外提供服务
  • 应用可以用各种语言编写,然后通过dapr提供的API使用这些能力,dapr也提供客户端类库来简化对API的调用,实现了多语言的支持。

Dapr提供的具体分布式能力(building block)如下图所示:

每个building block提供的具体能力请参加 Dapr 的官方文档:https://github.com/dapr/docs/tree/master/concepts。

Dapr的API例子

我们来看一下应用调用Darp API的例子,体验一下使用Dapr的方式。

以 Service Invocation / 服务调用为例:

部署和调用方式与 Servicemesh/Istio 极为相似,但是,差别在于:Dapr是以提供API的方式提供API背后的能力,而不是提供提供协议代理的方式。

上图中1,是ServiceA发起请求来调用一个远程服务。其HTTP request 如下所示:

POST/GET/PUT/DELETE http://localhost:<daprPort>/v1.0/invoke/<appId>/method/<method-name>

其中:

  • 参数 daprPort 是Dapr Runtime启动的监听端口,用来接受应用的 outbound 请求
  • 参数 appId 是远程应用在darp中的关联id,每个注册到dapr的应用都有一个唯一的appId
  • 参数 method-name 是要调用的远程应用的方法名或者URL

负载可以存放在HTTP body中随请求发送,如 json。

注意,虽然都是提供相同的功能,这里体现了Dapr(或者说背后的Mecha)和Servicemesh在方式上的差异:暴露API还是代理通讯协议。

我们看一个更明显的例子,dapr提供的 “publish/subscriptions” 能力,让应用可以方便的发布消息,或者订阅主题并接收消息。下图是应用发布消息,请求直接发给dapr即可:

例子中,参数 topic 指定了消息要发往的主题(例子中是 deathStarStatus)。后续dapr会完成将消息入queue,然后推送到订阅了该topic的应用。接收消息的方式也类似,不过这次是darp主动发起:

  1. dapr首先会请求应用,咨询应用需要订阅那些主题(topic),如例子中应用返回的的TopicA / TopicB
  2. dapr实现主题订阅,在接收到消息之后,再把消息发送给应用,通过URL参数的不同来区分不同的主题

注意在这个调用期间,无论是收发消息,应用完全不用理会底层pub/sub的实现机制(比如是kafka,还是rocketmq,还是其他公有云提供的消息机制),也完全不用引入该实现机制的客户端SDK,只是简单的使用darp定义的API即可,从而实现了和底层的解耦,以及“厂商不绑定”。

为了进一步简化调用的过程(毕竟发一个最简单的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);
    ......
}

具体可见:

https://github.com/dapr/java-sdk/blob/master/sdk/src/main/java/io/dapr/client/DaprClient.java

分析和总结

前面介绍了Multiple Runtime / Mecha 的架构思想,以及参考实现之一的微软Dapr项目。

由于 Multiple Runtime / Mecha 这个思想非常的新,刚刚提出不久,而微软 Dapr 项目也是一个新出来的项目,不管是理论思想还是实践都处于非常早期的状态,也还没有形成完善的方法论。

特别申明:以下内容更多是我个人当下的理解和感悟,仅代表个人意见,肯定有很多不成熟甚至谬误的地方,欢迎指正和探讨。

Mecha和Dapr的启示

  1. Mesh 模式应该推向更大的领域

    随着云原生的深入,应用需要的分布式能力应该全面下沉,而不仅仅局限于Servicemesh提供的服务间通讯能力;应用形态会朝纯业务逻辑这个目标更进一步,应用更加的云原生化。

    这是大势所趋,也是Mecha架构出现和发展的原动力。

  2. Mecha强调是“提供能力”,而不是通讯代理

    Mecha的使用方式和Servicemesh有非常大的差异:Mecha强调的是提供分布式能力给应用使用,这些能力最终以封装完善的API的方式呈现。API体现的是应用对能力的“需求”和“意愿”,不涉及到如何实现,实现是Mecha的职责,采用什么样的实现也是由Mecha来控制。

    在Servicemesh下,不存在这个需求:Servicemesh提供的是服务间通讯能力,这个能力是由sidecar来提供,没有其他的更底层的实现,不存在隔离和替换的可能。受服务通讯协议和报文schema的限制,Servicemesh只能做请求的“转发”,能力聚焦在“如何转发”上,没有其他需要隔离和替代的能力。

    当Mecha把能力扩展到Servicemesh之外时,很多能力是由外部系统提供:比如 pub-sub 能力可以由不同的Message Queue实现;状态管理能力可以连接不同的Key-Value实现。此时能力的隔离性和可替代性就成为关键需求:解耦应用和能力实现,容许Mecha替换底层实现(进而实现供应商不锁定等)。

  3. 不强求“零侵入”

    在Servicemesh中,“零侵入”是一个非常强调的特性,为此不惜引入 iptables 等流量劫持方案。“零侵入”在某些特殊场景下会发挥巨大的优势,如旧有应用不做改造的前提下接入servicemesh。好处自然不言而喻,但零侵入也有自身的限制:客户端必须能发出符合服务器端要求的网络通讯请求,这个过程外部无法插手。

    对于服务间通讯,这个不是大问题。但是对于其他能力,由于有和实现解耦的需求,再通过客户端自行发起原生协议的请求就不合适了。因此,Mecha中更倾向于采用低侵入的轻量级SDK方案,同样也可以实现跨语言和跨平台,只是需要付出实现各种语言SDK的代价。由于这个SDK足够轻量,因此代价还不算很高。

    而这些少量的工作量,少量的侵入,可以换取轻量级SDK能提供的各种便利和配合(简单理解:开后门),可以实现能力的抽象和API的封装。权衡利弊,Mecha下更倾向于轻量级SDK方案。

  4. 不限定 Sidecar 部署

    Sidecar部署模式,存在资源占用、维护成本增加等缺点,在某些情况下可能并不合适:

    • 边缘网络,IoT场景:资源非常有限,不适合启动太多Sidecar
    • FaaS场景:应用自身足够轻量,甚至比Sidecar还要轻量
    • Serverless场景:Scale to Zero时,对冷启动速度有严格要求,Sidecar的启动和初始化可能拖累应用启动速度

    Mecha下,部署模式不限定于 Sidecar ,在合适时容许选择 Node 模式,甚至 Node 模式和 Sidecar 模式混合使用。

  5. API和配置是关键

    API是分布式能力的抽象,需要要对(开发上层业务应用的)客户友好,简单好用,稳定不变。这些API 也需要标准化,被社区广泛接受和采纳,才能实现厂商不锁定和自由迁移,提升客户价值。

    另外,API还需要配合配置使用,在把能力抽象为API时,是不提供能力的细节控制的。这些控制将在运行时由Mecha根据配置实现,可以理解为:“API + 配置 = 完整的能力”。

    API和配置的制订以及标准化,预计将会是Mecha成败的关键。

Mecha的精髓

Program to an interface, not an implementation.

Design Patterns: Elements of Reusable Object-Oriented Software (GOF, 1994)

Mecha的精髓,要从上面这句名言开始:

  1. 在Mecha下,为了实现解耦可替换, Runtime 隔离了底层实现,因此演变为:”Program to an Runtime, not an implementation.“”
  2. 考虑到 Runtime 不管是部署为Sidecar模式,还是部署为 Node 模式,都是Localhost,因此有: “Program to an Localhost, not an implementation.”
  3. 为了简化开发,Mecha还是会提供轻量级SDK,提供API作为能力的抽象:“Program to an API, not an implementation.”
  4. 考虑到 API 通常是以 interface 的形式提供,因此绕了一圈,Mecha最后还是回到原点:“Program to an interface, not an implementation.”

个人理解,Mecha的精髓就在于这几个关键点:隔离/抽象/解耦/可替换。如下图所示:

  • 在Mecha下,MicroLogic(也就是业务逻辑的代码实现)不容许直接使用底层实现提供的分布式能力
  • Mecha Runtime将为Micro Logic提供分布式能力,同时隔离应用和底层实现
  • 为了方便使用,提供轻量级SDK,其中的API层实现了分布式能力的抽象,应用只需面向API编程
  • 轻量级SDK和Mecah Runtime配合,完成对底层实现的解耦和可替换。

Mecha的实现原则

在Mecha的实现上,我理解的原则是这样:

  1. Runtime 是主力,要做厚
  2. 轻量级SDK 主要是给 Runtime 打配合,要做薄

具体的职责划分:

  1. 轻量级SDK:实现多语言接入,低侵入(但不追求零侵入)
  2. API 接口:由轻量级SDK中提供统一,目标社区化+标准化,给开发者提供一致的编程体验,同时提供可移植性
  3. 应用:轻量级SDK/Runtime配合,提供各种分布式能力,应用无感,只需简单使用API,不耦合底层实现

在Mecha架构中,Runtime 自然是整个架构的核心,扮演类似Servicemesh中数据平面的角色

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

备注:Mecha有非常多的能力,实现上也有非常多的细节,这里先做一个High Level的概述。细节后面会有一系列文章一一覆盖,欢迎多交流讨论。

Mecha总结

大概是在3月初,当时我第一次阅读 “Multi-Runtime Microservices Architecture” 这篇文章,有一种豁然开朗的感觉,尤其是有很多之前在反复考虑和权衡但是下不了结论的问题,在这个文章中得到了清晰的解答。可谓受益匪浅。

在Servicemesh探索和实践的这三年中,遇到很多问题,有很多之前没有想到过的问题浮现。比如,以前一直觉得Servicemesh中引入Sidecar带来的最大麻烦会是性能,但实际上,从目前的实践看,Sidecar引入后带来的维护代价才是更令人头疼的事情,相比之下Sidecar引入带来的性能损失显得无伤大雅。

总结一下我个人对 Mecha 架构的核心理解,主要是两点:

  1. Mecha是云原生化和Mesh化的必然趋势:云原生在继续发展,应用需要的分布式能力需要继续下沉,越来越多的能力会以sidecar的形式出现,这是大势所趋。但不可能出现一个应用部署十几个sidecar的局面,这会是运维地狱。因此,必然需要出现新的形态来解决Sidecar过多的问题,合并为一个或者多个Sidecar就会成为必然。
  2. Mecha是Servicemesh模式的自然进化版本:Servicemesh落地实践三年了,效果一直并不理想,到了该反思反省的时候了;而且Mecha的范围也远不止服务间通讯,新的需求下应该有新的思考和突破。Servicemesh现有的固定模式,在Mecha下可以尝试打破以探索新的方式:不必拘泥于Sidecar,试试Node模式;不必拘泥于通讯协议转发,试试Runtime提供能力解耦底层实现;不必拘泥于零侵入,试试在应用中保留一个足够轻的轻量级SDK。

正如曾有说法,说“微服务是SOA实践中正确的部分(the Good Part)”,我希望在 Mecha 的探索和实践中,能够从Servicemesh的实践中吸取成功的经验和失败的教训,希望 Mecha 也能成为Servicemesh的Good Part。希望在云原生的演进路线中,Mecha 可以继微服务和Servicemesh之后,成为云原生落地实践的下一站。

回到现实,目前 Mecha 和 Multi-Runtime 还是一个非常新的想法,Dapr 项目也才刚刚起步,Mecha 要探索的道路还很漫长,一切都还需要摸索着前进。

附录:参考资料

在文章的最后,特别鸣谢 “Multi-Runtime Microservices Architecture”一文的作者 “Bilgin Ibryam”,我非常认可这篇文章中的思想和理念,分析归纳的非常到位,提炼和升华的能力令人佩服。

作者介绍:

Red Hat的首席架构师,Apache Software Foundation 的 committer 和成员。开源的布道师,博客作者,偶尔演讲,著有书籍 Kubernetes Patterns 和 Camel Design Patterns 。

本文参考了 Bilgin Ibryam 出品的如下内容:

应用错误跟踪系统:对软件系统运行过程中产生的错误日志进行收集从而实现监控告警。

虽然软件错误❌是不可避免的,但是可以降低错误数。

提高对错误的治理能力能让错误带来的损失降到最低 ​👍🏻 。

错误日志监控在最顶层的业务层监控,有他将会帮助你打造更好的软件!

我们需要业务场景下自己发现Bug的速度快于用户报告Bug的速度,毕竟让用户报告已经晚了。

典型的这套系统的架构:错误日志(前端、后端) => 传输(HTTP)=>错误跟踪平台(收集、展示、分析、告警)。

本文不讨论哪款软件来解决这个问题,只讲Sentry这个轮子,SASS版本和私有部署版体验几乎一致

img

本文主要讲了

应用错误需要监控的场景案例

Sentry有哪些功能

Sentry如何在k8s中私有化部署

本文由 www.iamle.com 流水理鱼 原创,wx公众号搜索 流水理鱼 或 liushuiliyu

SEO 关键字

Sentry私有化部署

Sentry helm部署

Sentry kubernets部署 k8s部署

1. 需要应用错误监控的场景案例

  • 多年的老web项目需要https适配,因为老项目大量写死了http://所以文件修改数巨大,即便测试很仔细也不能保证完全没问题
  • 运营发现落地页转化异常了,有访问没表单提交,找到技术,技术发现是某个JS报错,导致表单无法提交,造成推广费用的损失
  • 上线了一个功能,由于环境差异,只在生产环境才触发,用户报告了才去查问题
  • 应用错误日志虽然通过ELK采集了,但是缺乏及时分析和告警
  • 特定的环境才产生错误,要解决问题的先去构建环境并复现错误,因为不清楚发生错误时的具体参数

2. Sentry有哪些功能

Sentry is cross-platform application monitoring, with a focus on error reporting.

Sentry跨平台应用监控,专注错误报告。

Sentry英文直译中文叫“哨兵”。

Sentry提供了一个应用程序监视平台,可以帮助您实时识别问题。

  • 提供WEB UI
  • 提供SASS版和私有部署2种方式
  • 开源,授权协议为BSL,只要你不拿他来做SASS服务卖钱,自用可以免费商用
  • 提供几乎所有主流开发语言和框架的SDK
  • 提供完整的错误详情
  • 支持自动上报错误和手动上报错误
  • 支持WEB前端、后端、APP
  • 支持多项目管理
  • 支持账号权限管理
  • 提供统一错误的聚合分析

  • 今日头条等很多大公司都在用

img

3. 在Kubernets中部署Sentry

目前网上大部分都是讲DockerCompose的部署方式

其实Helm Hub上可以找到Sentry的helm charts包(⑤),那么直接用Helm部署Sentry是最快速方便的

3.1 Sentry部署资源要求

下面给出一个部署资源情况参考,这基本也就是最低资源消耗情况了

  • Kubernets存储已经支持了动态PVC

  • Kubernets已经支持Ingress

  • 准备一个子域名 (例如, sentry.iamle.com)

  • Helm部署Sentry会部署sentry-corn、sentry-web()、sentry-worker、Redis、PostgreSQL

  • 空负载资源详细sentry-corn(220MB/0.01Core)、sentry-web(850MB/0.012Core)、sentry-worker(2048MB/0.04Core)、Redis(132+179MB/0.04+0.054Core)、PostgreSQL(506MB/0.03Core)

  • 空负载整体资源占用情况,内存:3935MB、CPU核数:0.2 Cores、存储PVC:34G

image-20200419182509260

3.2 Helm 部署 Sentry

helm为helm3

使用国内加速的charts(微软azure)

helm repo add stable http://mirror.azure.cn/kubernetes/charts
helm repo add incubator http://mirror.azure.cn/kubernetes/charts-incubator
helm repo update
helm search repo sentry
#NAME                              CHART VERSION   APP VERSION DESCRIPTION
#stable/sentry                     4.2.0           9.1.2       Sentry is a cross-platform crash reporting and ...
#已经可以在stable repo 找到 sentry
## helm安装sentry
kubectl create namespace sentry
helm install sentry stable/sentry \
-n sentry \
--set persistence.enabled=true,user.email=i@iamle.com,user.password=i@iamle.com \
--set ingress.enabled=true,ingress.hostname=sentry.iamle.com,service.type=ClusterIP \
--set email.host=smtp.yourhost.com,email.port=25 \
--set email.user=user,email.password=password,email.use_tls=false \
--wait
#参数一看就懂,更多安装参数看文末的参考⑤
#第一次安装需要耗时10分钟以上,等等等,db-init-job初始化数据库的时候花费了太多时间
#在安装完成之前,访问sentry.iamle.com会出现服务器内部错误

#出现下面的内容就表示部署好了
#NAME: sentry
#LAST DEPLOYED: Sun Apr 19 21:01:26 2020
#NAMESPACE: sentry
#STATUS: deployed
#REVISION: 1
#TEST SUITE: None
#NOTES:
#1. Get the application URL by running these commands:
#  export POD_NAME=$(kubectl get pods --namespace sentry -l "app=sentry,role=web" -o jsonpath="{.items[0].metadata.name}")
#  echo "Visit http://127.0.0.1:8080 to use your application"
#  kubectl port-forward --namespace sentry $POD_NAME 8080:9000
#
#2. Log in with
#
#  USER: i@iamle.com
#  Get login password with
#    kubectl get secret --namespace sentry sentry -o jsonpath="{.data.user-password}" | base64 --decode

#查看登陆密码,也就是user.password设置的值
#kubectl get secret --namespace default sentry -o jsonpath="{.data.user-password}" | base64 --decode

#删除sentry
#helm uninstall sentry -n sentry

ingress根据自己实际环境做一些微调,笔者的环境下ssl证书使用 cert-manager 自动管理,备注annotations就自动配置ssl

# sentry ingress示例
kubectl apply -f - <<EOF
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: sentry
  namespace: sentry
  annotations:
    kubernetes.io/ingress.class: nginx
    kubernetes.io/tls-acme: "true"
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  rules:
  - host: sentry.iamle.com
    http:
      paths:
      - path: /
        backend:
          serviceName: sentry
          servicePort: 9000
  tls:
  - hosts:
    - sentry.iamle.com
    secretName: sentry-cert
EOF

浏览器访问

https://sentry.iamle.com 使用安装时设置的账号密码登陆即可

3.3 Sentry新增一个项目

理论上是需要给每个项目都在Sentry中创建一个对应的项目

但是实际上这么干会比较麻烦,笔者建议对访问量不大,比如后台类的相同类型的新建一个项目即可

发现混在一起已经不好区分了,再去拆开也不迟

对于本身访问量巨大的“前端”项目,建议在Sentry管理后台的一对一配置

Projects 》 Add new Project 选择一个JavaScript类型

image-20200419211855759

image-20200419212037861

⚠️ 官方提供的SDK是国外的CDN,实际使用的时候需要把https://browser.sentry-cdn.com/5.5.0/bundle.min.js下载后放在自己的OSS+CDN上

SDK和初始化代码一般放入全站头部当中,当页面有js错误的时候已经会自动上报了

我们用chrome的console手动上报一个消息测试下sentry是否工作正常

Sentry.captureMessage("流水理鱼 www.iamle.com")

image-20200419212444197

image-20200419212641447

3.4 Helm安装sentry常见问题及解决

  • 解决数据库不能初始化的问题

    如果安装过程数据库不能初始化,可以手动初始化

kubectl exec -it -n sentry $(kubectl get pods  -n sentry  |grep sentry-web |awk '{print $1}') bash
sentry upgrade

  • 手动创建一个新的管理员账号
kubectl exec -it -n sentry $(kubectl get pods  -n sentry  |grep sentry-web |awk '{print $1}') bash
sentry createuser

4. 总结

本文看起来洋洋洒洒一大篇,实际上15分钟就能部署好sentry🎉

Sentry不仅仅支持WEB前端,也支持桌面、APP、后端,全平台💯

注意如果要升级sentry,先备份数据先,之前的数据会被清空 ⚠️

如果遇到Sentry的部署、使用等问题可以在博客 www.iamle.com 中找到我的wx,加群讨论📣

5. 参考

1. 导言

绝大多数业务场景都是需要知道客户端IP的
在k8s中运行的业务项目,如何获取到客户端真实IP?
本文总结了通行的2种方式
要答案的直接看方式一、方式二和总结
SEO 关键字
nginx ingress客户端真实ip
kubernets获取客户端真实ip
rke获取客户端真实ip
rancher获取客户端真实ip
本文由 www.iamle.com 流水理鱼 原创,wx公众号同名

1.1 流量链路介绍

7层转发链路 Client(客户端) > Nginx > K8s Ingress(Nginx ingress)
4层转发链路 Client(客户端) > 公有云LB > K8s Ingress(Nginx ingress)
ps: 实际业务会串联更多层级的转发。WAF、CDN、Api Gateway一般是http 7层转发,LB一般是4层tcp转发

1.2 准备whoami探针

whomai是一个go编写的调试探针工具,回显http头信息
在k8s中部署一个containous/whoami用来作为探针,配置好ingress公网和访问,这样客户端web访问可以看到基本的http头信息,方便调试

kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  name: whoami
  namespace: default
  labels:
    app: whoami
spec:
  replicas: 1
  selector:
    matchLabels:
      app: whoami
  template:
    metadata:
      labels:
        app: whoami
    spec:
      containers:
      - image: containous/whoami
        imagePullPolicy: Always
        name: whoami
        ports:
        - containerPort: 80
          name: 80tcp02
          protocol: TCP
      dnsPolicy: ClusterFirst
      restartPolicy: Always
EOF 

ps:ingress自行增加

客户端web访问,回显http头示例

Hostname: whoami-65b8cc4b-6vwns
IP: 127.0.0.1
IP: 10.42.2.12
RemoteAddr: 10.42.1.0:47850
GET / HTTP/1.1
Host: whoami.iamle.com
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/80.0.3987.162 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7,zh-TW;q=0.6,la;q=0.5
Cookie: _ga=GA1.2.30707523.1570429261;
Dnt: 1
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: cross-site
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
X-Forwarded-For: 8.8.8.8, 10.0.0.1
X-Forwarded-Host: whoami.iamle.com
X-Forwarded-Port: 443
X-Forwarded-Proto: https
X-Original-Forwarded-For: 8.8.8.8
X-Original-Uri: /
X-Real-Ip: 8.8.8.8
X-Request-Id: 3852c9780589ffba4c1f9f2785691d5f
X-Scheme: https

2. 两种方式 7层http头X-Forwarded-For透传 和 4层Proxy Protocol透传

获得客户端真实IP有针对7层和针对4层两种方式

2.1 7层http头X-Forwarded-For透传介绍

http工作在网络第7层,http中有个X-Forwarded-For字段

大部分CDN、WAF、LB用X-Forwarded-For字段来存客户端IP,也有用X-Real-Ip字段,cloudflare、百度云加速还扩展了CF-Connecting-IP字段
标准数据为

X-Forwareded-For:Client,proxy1,proxy2,proxy3……

第一个ip是客户端ip,后面的proxy为路过一层就加一层的ip
这里的proxy可以是WAF、CDN、LB、Api Gateway等

2.2 4层Proxy Protocol透传

tcp工作在网络第4层,Proxy Protocol就是在tcp中增加一个小的报头,用来存储额外的信息

代理协议即 Proxy Protocol,是haproxy的作者Willy Tarreau于2010年开发和设计的一个Internet协议,通过为tcp添加一个很小的头信息,来方便的传递客户端信息(协议栈、源IP、目的IP、源端口、目的端口等),在网络情况复杂又需要获取客户IP时非常有用。
其本质是在三次握手结束后由代理在连接中插入了一个携带了原始连接四元组信息的数据包。

目前 proxy protocol有两个版本,v1仅支持human-readable报头格式(ASCIII码),v2需同时支持human-readable和二进制格式,即需要兼容v1格式
proxy protocol的接收端必须在接收到完整有效的 proxy protocol 头部后才能开始处理连接数据。因此对于服务器的同一个监听端口,不存在兼容带proxy protocol包的连接和不带proxy protocol包的连接。如果服务器接收到的第一个数据包不符合proxy protocol的格式,那么服务器会直接终止连接。

Proxy protocol是比较新的协议,但目前已经有很多软件支持,如haproxy、nginx、apache、squid、mysql等等,要使用proxy protocol需要两个角色sender和receiver,sender在与receiver之间建立连接后,会先发送一个带有客户信息的tcp header,因为更改了tcp协议头,需receiver也支持proxy protocol,否则不能识别tcp包头,导致无法成功建立连接。
nginx是从1.5.12起开始支持的

3. 方式一 X-Forwarded-For配置

适用于7层http转发

3.1 NGINX Ingress Controller X-Forwarded-For配置

查看NGINX Ingress Controller的ConfigMaps配置文档,可以找到以下配置项
use-forwarded-headers
如果为true,NGINX会将传入的 X-Forwarded-* 头传递给upstreams。当NGINX位于另一个正在设置这些标头的 L7 proxy / load balancer 之后时,请使用此选项。
如果为false,NGINX会忽略传入的 X-Forwarded-* 头,用它看到的请求信息填充它们。如果NGINX直接暴露在互联网上,或者它在基于 L3/packet-based load balancer 后面,并且不改变数据包中的源IP,请使用此选项。
ps: NGINX Ingress Controller直接暴露互联网也就是Edge模式不能开启为true,否则会有伪造ip的安全问题。也就是k8s有公网ip,直接让客户端访问,本配置不要设为true!

forwarded-for-header
设置标头字段以标识客户端的原始IP地址。 默认: X-Forwarded-For
ps:如果 NGINX Ingress Controller 在CDN,WAF,LB等后面,设置从头的哪个字段获取IP,默认是X-Forwarded-For
这个配置应该和use-forwarded-headers配合使用

compute-full-forwarded-for
将远程地址附加到 X-Forwarded-For 标头,而不是替换它。 启用此选项后,upstreams应用程序将根据其自己的受信任代理列表提取客户端IP

修改configmap nginx-configuration配置

kubectl -n ingress-nginx edit cm nginx-configuration

在apiVersion: v1下,kind: ConfigMap上加入

data:
  compute-full-forwarded-for: "true"
  forwarded-for-header: "X-Forwarded-For"
  use-forwarded-headers: "true"

或者直接apply附加配置

kubectl apply -f - <<EOF
apiVersion: v1
data:
  compute-full-forwarded-for: "true"
  forwarded-for-header: X-Forwarded-For
  use-forwarded-headers: "true"
kind: ConfigMap
metadata:
  labels:
    app: ingress-nginx
  name: nginx-configuration
  namespace: ingress-nginx
EOF

ps:如果nginx-configuration不在namespace ingress-nginx中就在namespace kube-system中找

3.2 Nginx作为边缘节点(Edge)配置

作为Edge需要重写remote_addr,保证了客户端IP不会被伪造
必须:X-Forwarded-For 重写为 $remote_addr
非必须扩展:X-Real-IP 重写为 $remote_addr

upstream wwek-k8s {
    server 8.8.8.8:443;
    server 8.8.8.7:443;
    server 8.8.8.6:443;
}

map $http_upgrade $connection_upgrade {
    default Upgrade;
    ''      close;
}
server {
    if ($http_x_forwarded_proto = '') {
        set $http_x_forwarded_proto  $scheme;
   }
location / {

        proxy_set_header Host              $http_host;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Port  $server_port;
        #proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-For   $remote_addr;
        proxy_set_header X-Real-IP         $remote_addr;

        proxy_pass          https://wwek-k8s;
        proxy_http_version  1.1;
        proxy_set_header    Upgrade $http_upgrade;
        proxy_set_header    Connection $connection_upgrade;
        proxy_read_timeout 900s;
        proxy_buffering off;
    }
}

3.3 X-Forwarded-For是否可以伪造

客户端是否能伪造IP,取决于边缘节点(Edge)是如何处理X-Forwarded-For字段的。
客户端直接连接的首个proxy节点都叫做边缘节点(Edge),不管是网关、CDN、LB等只要这一层是直接接入客户端访问的,那么他就是一个边缘节点。

不重写-不安全的边缘节点(Edge)
边缘节点如果是透传http头中的X-Forwarded-For字段,那么这个就是不安全的,客户端可以在http中实现包含X-Forwarded-For字段值,这个值又被透传了。

#不安全
X-Forwareded-For:Client(Edge不重写,只透传),proxy1,proxy2,proxy3……

重写-安全的边缘节点(Edge)
边缘节点(Edge)如果重写remote_addr到X-Forwarded-For,那么这就是安全的。边缘节点(Edge)获取的remote_addr就是客户端的真实IP

#安全
X-Forwareded-For:Client(Edge获取的remote_addr),proxy1,proxy2,proxy3……

4. 方式二 Proxy Protocol 配置实例

适用于4层tcp转发
公有云的负载均衡LB一般都支持Proxy Protocol

查看NGINX Ingress Controller的ConfigMaps配置文档,可以找到如何配置Proxy Protocol
use-proxy-protocol
启用或禁用roxy Protocol,以接收通过代理服务器和负载均衡器(例如HAProxy和Amazon Elastic Load Balancer(ELB))传递的客户端连接(真实IP地址)信息。

NGINX Ingress Controller 作为receiver角色 Proxy Protocol配置

kubectl -n ingress-nginx edit cm nginx-configuration

在apiVersion: v1下,kind: ConfigMap上加入

data:
  use-proxy-protocol: "true"

或者直接apply附加配置

kubectl apply -f - <<EOF
apiVersion: v1
data:
  use-proxy-protocol: "true"
kind: ConfigMap
metadata:
  labels:
    app: ingress-nginx
  name: nginx-configuration
  namespace: ingress-nginx
EOF

ps: 注意需要上一层LB支持Proxy Protocol,才能这么配置,否则会导致无法链接

5. 总结

7层http头X-Forwarded-For透传
链路proxy有透传X-Forwarded-For
访问链路上多层proxy,任意一个节点不支持Proxy Protocol

4层协议Proxy Protocol透传
上下游可控都支持Proxy Protocol协议
链路proxy中丢失了http头
https反向代理http(某些情况下由于Keep-alive导致不是每次请求都传递x-forword-for

应该用那种方式?
7层用X-Forwarded-For,4层用Proxy Protocol
如果链路的边缘节点(Edge)X-Forwarded-For字段是安全的,建议用X-Forwarded-For
如果链路proxy全路径都支持Proxy Protocol,那么建议用Proxy Protocol
如果有4层tcp业务应用,那么获取客户端IP就的用Proxy Protocol
总之搞清楚了这2种方式的原理按照场景选择

5. 参考

Istio

Istio官网
Istio中文官网
Istio开源
无需太多介绍Service Mesh明日之星,扛把子,截止2019.11还有太多问题没解决
复杂性,性能让人望而却步,能上生产的是真要技术厉害,还得内心强大,项目允许

Linkerd

Linkerd官网
Linkerd中文官网

A service mesh for Kubernetes and beyond. Main repo for Linkerd 2.x.
Linkerd is a service mesh, designed to give platform-wide observability, reliability, and security without requiring configuration or code changes.

Linkerd is a Cloud Native Computing Foundation (CNCF) project.

Maesh

Maesh官网
Maesh开源

Containous(Traefik 团队)推出了全新设计的轻量级 service mesh(服务网格)工具:Maesh,Maesh 允许对 Kubernetes 集群内流动的流量进行管理,这与入流量和出流量同样很重要。
Maesh 构建在 Traefk 之上,是一个简单但功能齐全的服务网格,支持最新的服务网格接口规范 SMI,有助于与现有的一些解决方案进行集成。此外,Maesh 默认是需要开启使用的,这意味着在你决定将它们添加到网格之前,你的现有服务不会受到影响。

比较喜欢用go语言编写的东西,毕竟CNCF中绝大多数项目都是go语言编写的,看后续走向

Consul

Consul官网
Consul开源

Secure Service Networking
Consul is a service networking solution to connect and secure services across any runtime platform and public or private cloud

consul原本是个分布式kv,可以用来做配置中心,注册中心,现在官方默认的宣传语都改成了连接和保护服务

定位服务发现(Service discovery)和 服务网格(Service Mesh)

Envoy

Envoy官网
Envoy开源
ENVOY IS AN OPEN SOURCE EDGE AND SERVICE PROXY, DESIGNED FOR CLOUD-NATIVE APPLICATIONS
ENVOY是开放源代码边缘和服务代理,专为云应用程序而设计

envoy作为服务网格中的通用数据平面, xDS API 已经成为数据平面API接口的实施标准
就好像AWS的OSS对象存储中的S3一样,成了事实标准

SOFAMson

SOFAMson官网
SOFAMson开源

SOFAMson 是一款使用 Go 语言开发的 Service Mesh 数据平面代理,旨在为服务提供分布式、模块化、可观察和智能化的代理能力。SOFAMosn 是 SOFAStack 中的一个项目,其中 MOSN 是 Modular Observable Smart Network 的简称。SOFAMosn 可以与任何支持 xDS API 的 Service Mesh 集成,亦可以作为独立的四、七层负载均衡使用。未来 SOFAMosn 将支持更多云原生场景,并支持 Nginx 的核心转发功能。

目前蚂蚁金服有大规模落地,2019年的双十一已经做了生产实践
API接口方面也是兼容Envoy的 xDS API 标准

1. 单机运行的Docker

容器化部署是现在进行时,开源应用大多数支持容器化部署
在少量机器的场景下往往采用docker cli 和 docker-compose管理,进行“单机式管理”
机器稍多点会采用Docker Swarm群集的方式,毕竟k8s稍重
如果有更多的机器情况下一般会采用k8s的方式
在个人、创业公司、小团队的场景下我们往往在多个云、家里、vps上拥有少量虚拟机服务器,这些服务器上都运行了docker实例
虽然我们也可以用公网vpn、zerotier等方式打通各个地域的机器形成一个内网,从而构建“群集”,但实际情况是没那么多带宽,从而无法“负载均衡”,所以实际的实际还是独立使用
受限制于公网带宽很小,只能独立使用
我们也拥有一些4G移动流量接入这种EDGE边缘场景的情况,这些IoT上也运行着Docker实例
那么这些NAT内网环境下的Docker实例,公网IP环境下的Docker实例,是否能集中管理?
当然可以,用Portainer就挺方便

2. 用Portainer作为Docker实例管理平台实战步骤

2.1 主节点-Portainer server主控安装

选一个7×24小时且有公网IP的节点运行Portainer server主控

docker run -d -p 8000:8000 -p 9000:9000 --name portainer --restart always -v /var/run/docker.sock:/var/run/docker.sock -v /data/appdata/portainer:/data portainer/portainer

9000端口为web管理界面端口
8000端口为Agent接入端口
这2个端口公网IP防火墙都必须放行

浏览器打开web管理界面
http://Portainer server主控公网IP:9000

初次访问设置一个密码,设置密码后需要选择连接docker的方式
选第一个Local 然后点击Connect
默认进入Home菜单,点击这个Local本地docker实例进入其他管理

Portainer官方安装手册参考

2.2 配置https nginx代理(可选)

如果不配置https可跳过本节
http://你的公网IP:9000 默认没有https不安全
规划一个域名用于https访问例如https://portainer.iamle.com
用nginx作为接入反向代理到 http://portainerip:9000
下面为示例nginx portainer配置(其中包含需要的websocket proxy)

# portainer.iamle.com.conf
map $http_upgrade $connection_upgrade {
    default Upgrade;
    ''      close;
}
upstream portainer {
    server 127.0.0.1:9000;
}
server {
        listen       80;
        server_name portainer.iamle.com;
        return      301 https://$server_name$request_uri;
}
server {

                listen 443 ssl http2;
        server_name portainer.iamle.com;

                ssl_certificate         ssl/iamle.com.cer;
                ssl_certificate_key     ssl/iamle.com.key;
                #ssl_session_cache           shared:SSL:10m;
                #ssl_session_timeout         10m;
                #ssl_session_tickets         off;

                ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
                ssl_ciphers         HIGH:!aNULL:!MD5:!EXPORT56:!EXP;
                ssl_prefer_server_ciphers on;

        location / {
                    proxy_set_header Host              $host;
                    proxy_set_header X-Forwarded-Proto $scheme;
                    proxy_set_header X-Forwarded-Port  $server_port;
                    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
                    proxy_set_header X-Real-IP         $remote_addr;
                    proxy_set_header X-Request-Id $request_id;
                    proxy_set_header Upgrade           $http_upgrade;
                    proxy_set_header Connection        $connection_upgrade;
                    proxy_read_timeout 60m;
                    proxy_send_timeout 60m;
                    proxy_http_version 1.1;
                    proxy_pass http://portainer;
                    break;
        }


        error_log   /data/logs/portainer.iamle.com-error.log;
        access_log  /data/logs/portainer.iamle.com.log access;

}

2.3 主节点-配置一个在NAT内网Agent客户端

前面我们已经配置好一个具有公网IP的Portainer管理控制节点
那么现在把内网(NAT、IoT)、公有云、vps等运行的Docker实例来接入控制节点管理

一图胜千言,官方介绍图

图中这个Portainer管理了2个内网的Agent
其中一个是个Swarm群集,另外一个是个单机

Portainer server上增加一个Edge Agent
Endpoints菜单 》 Add endpoint 》 Edge Agent

Name:为自定义
Portainer server URL:默认为当前Portainer server ip (如果用nginx配置了https可以使用https不加端口号)
》 Add endpoint

增加端点后出现

部署客户端agent 有2种选择 Standalone 和 Swarm
如果已经组过Swarm那么选Swarm,默认就是Standalone
先点击 “Copy command” 复制命令,在Agent客户端去执行
Public IP: 如果有可以设置,这样在以后部署了docker容器暴露的端口可以自动生成url

2.4 被管理节点-需要被管理的Docker实例客户端机

在被管理的客户端终端上执行(内网(NAT、IOT)、公网环境都可以,只要能连接上我们的Portainer server)
本例内网1台ip为 192.168.0.8的机器

docker run -d -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/docker/volumes:/var/lib/docker/volumes -v /:/host --restart always -e EDGE=1 -e EDGE_ID=6ad0f1ff-6fea-4710-97e2-513ef1066fd8 -e CAP_HOST_MANAGEMENT=1 -p 8000:80 -v portainer_agent_data:/data --name portainer_edge_agent portainer/agent:1.5.1

访问 http://192.168.0.8:8000(如果有公网ip用公网ip)
打开后会有个输入框
输入上一步获得的Join token
点击Submit 出现 Agent setup OK. You can close this page. 代表完成agent接入

回到 Portainer server管理界面等待上线

点击 iamle-lan-01 等待几秒钟

这样我们就可以管理多个docker实例了

3. 贴士

  • 如果agent运行不起来,无限重启
    需要根据docker logs portainer_edge_agent 获取到的错误信息排查, 官方github issue是个好去处
    另外发现2019年10月19日16:55:01 pull 下来的portainer/agent:latest 也运行不起来 改为 portainer/agent:1.5.1正常

  • Stacks粘贴docker-compose.yml进来后一直报version版本不对
    目前只支持version 2 改为2即可

4. 参考

Portainer Edge Agent 官方发布
Portainer内网边缘节点配置说明书PDF