本文发表于一年多前。旧文章可能包含过时内容。请检查页面中的信息自发布以来是否已变得不正确。

Kyma - 轻松扩展和构建 Kubernetes

根据最近完成的CNCF 调查,云原生技术在生产环境中的采用率正在迅速增长。Kubernetes 是这场技术革命的核心。自然,云原生技术的增长也伴随着其生态系统的增长。当然,云原生技术的复杂性也随之增加。只需在谷歌搜索“Kubernetes is hard”这个短语,你就会得到大量文章来解释这个复杂性问题。CNCF 社区最棒的一点是,这样的问题可以通过聪明的开发者构建新工具来赋能 Kubernetes 用户而得到解决:例如,像 Knative 及其Build 资源扩展等项目,旨在降低各种场景的复杂性。尽管增加复杂性似乎是需要解决的最重要问题,但它并不是您在向云原生过渡时面临的唯一挑战。

待解决的问题

选择正确的技术很困难

现在您已经了解了 Kubernetes,您的团队已经接受了培训,并且您已经开始在其上构建应用程序,是时候面对新一层的挑战了。云原生不仅仅意味着部署一个供开发者在其上构建的平台。开发者还需要存储、备份、监控、日志记录和服务网格来对传输中的数据强制执行策略。这些独立的系统中的每一个都必须正确配置和部署,并且需要独立地进行日志记录、监控和备份。CNCF 旨在提供帮助。我们提供了所有云原生技术的概览图,但列表非常庞大,可能会让人不知所措。

这就是 Kyma 让您生活更轻松的地方。它的使命宣言是提供一种灵活简单的方式来扩展应用程序。

Kyma in center

该项目旨在为您提供构建端到端、生产级云原生应用程序所需的工具。KymaSAP 捐赠给开源社区;SAP 在编写生产级云原生应用程序方面拥有丰富的经验。这就是我们如此兴奋地宣布 Kyma 的第一个主要版本,Kyma 1.0 发布的原因!

决定从单体应用转向云原生的路径很困难

尝试在谷歌搜索 `monolith to cloud native` 或 `monolith to microservices`,你会得到大量关于如何应对这一挑战的演讲和论文。将单体应用迁移到云有许多不同的途径,我们的经验告诉我们在这个领域应该相当有主见。首先,让我们回答为什么要从单体应用转向云原生的问题。推动这一转变的目标通常是:

  • 提高可伸缩性。
  • 更快地实现新功能。
  • 更灵活的可扩展性方法。

您无需重写您的单体应用程序即可实现这些目标。为什么要花费所有时间重写已有的功能呢?只需专注于让您的单体应用程序支持事件驱动架构

Kyma 如何解决您的挑战?

什么是 Kyma?

Kyma 运行在 Kubernetes 上,由多个不同的组件组成,其中三个是:

  • 应用程序连接器,您可以使用它将任何应用程序与 Kubernetes 集群连接,并通过 Kubernetes 服务目录暴露其 API 和事件。
  • Serverless 让您能够轻松为应用程序编写扩展。您的函数代码可以由 API 调用触发,也可以由来自外部系统的事件触发。您还可以从函数中安全地回调集成系统。
  • 服务目录旨在暴露集成系统。这种集成还使您能够使用 Azure、AWS 或 Google Cloud 等超大规模云服务提供商的服务。Kyma 允许轻松集成由 Microsoft 和 Google 维护的官方服务代理。

core components

您可以观看此视频,通过真实的演示场景简要了解 Kyma 的主要功能。

我们为您选择了正确的技术

只有在妥善监控和配置的情况下,您才能在 Kyma 这样的项目中提供可靠的可扩展性。我们决定不重复造轮子。CNCF 生态系统中有许多优秀的项目,大部分都有庞大的社区支持。我们决定挑选最好的项目,并将它们整合到 Kyma 中。您可以看到与上方相同的架构图,但重点是我们将哪些项目组合在一起创建了 Kyma:

Kyma architecture

您无需集成这些工具:我们确保它们能很好地协同工作,并且始终保持最新(Kyma 已经在使用 Istio 1.1)。通过我们定制的安装程序Helm图表,我们实现了 Kyma 的轻松安装和升级到新版本。

不要重写您的单体应用

重写既困难又耗资巨大,并且在大多数情况下是没有必要的。最终,您需要的是能够更快地编写新功能并将其投入生产。您可以通过使用应用程序连接器将您的单体应用连接到 Kyma 来实现这一目标。简而言之,这个组件确保了:

  • 您可以安全地回调已注册的单体应用,而无需处理授权问题,因为应用程序连接器会处理这些问题。
  • 从您的单体应用发送的事件安全地到达 Kyma 事件总线。

目前,您的单体应用可以消费三种不同类型的服务:用于同步通信的 REST(带有OpenAPI规范)和 OData(带有实体数据模型规范),以及用于异步通信的基于AsyncAPI规范的事件目录。您的事件随后使用带有Knative eventingNATS Streaming通道在内部交付。

一旦您的单体服务连接成功,您就可以通过前面提到的服务目录集成,在选定的命名空间中配置这些服务。作为开发者,您可以访问目录,查看所有可消费的服务列表。这包括来自您的单体应用程序的服务,以及通过已注册的服务代理(如Azure 的 OSBA)提供的其他第三方服务。这是一个包含您所需一切的单一场所。如果您想搭建一个新应用程序,所有您需要的东西都已在 Kyma 中可用。

最后是一些代码

查看我为将一个单体应用与 Azure 服务集成而编写的一些代码。我想要了解客户在产品评论部分表达的情绪。对于每一个包含评论的事件,我想使用机器学习调用一个情感分析服务,如果评论是负面的,我希望将其存储在数据库中以供后续审查。这是通过我们的Serverless组件创建的函数的代码。请注意我的代码注释。

您可以观看短视频,了解情感分析功能的完整演示。

/* It is a function powered by NodeJS runtime so I have to import some necessary dependencies. I choosed Azure's CosmoDB that is a Mongo-like database, so I could use a MongoClient */
const axios = require("axios");
const MongoClient = require('mongodb').MongoClient;

module.exports = { main: async function (event, context) {
    /* My function was triggered because it was subscribed to customer review event. I have access to the payload of the event. */
    let negative = await isNegative(event.data.comment)
    
    if (negative) {
      console.log("Customer sentiment is negative:", event.data)
      await mongoInsert(event.data)
    } else {
      console.log("This positive comment was not saved:", event.data) 
    }
}}

/* Like in case of isNegative function, I focus of usage of the MongoClient API. The necessary information about the database location and an authorization needed to call it is injected into my function and I just need to pick a proper environment variable. */
async function mongoInsert(data) {

    try {
          client = await MongoClient.connect(process.env.connectionString, { useNewUrlParser: true });
          db = client.db('mycommerce');
          const collection = db.collection('comments');
          return await collection.insertOne(data);
    } finally {
      client.close();
    }
}
/* This function calls Azure's Text Analytics service to get information about the sentiment. Notice process.env.textAnalyticsEndpoint and process.env.textAnalyticsKey part. When I wrote this function I didn't have to go to Azure's console to get these details. I had these variables automatically injected into my function thanks to our integration with Service Catalog and our Service Binding Usage controller that pairs the binding with a function. */
async function isNegative(comment) {
    let response = await axios.post(`${process.env.textAnalyticsEndpoint}/sentiment`,
      { documents: [{ id: '1', text: comment }] }, {headers:{ 'Ocp-Apim-Subscription-Key': process.env.textAnalyticsKey }})
    return response.data.documents[0].score < 0.5
}

多亏了Kyma,我无需担心函数周围的基础设施。正如我所提到的,我在Kyma中拥有所有必要的工具,并且它们都已集成在一起。我可以快速通过Loki访问我的日志,并且可以快速访问预配置的Grafana仪表盘,通过PrometheusIstio查看我的Lambda指标。

Grafana with preconfigured lambda dashboard

这种方法在添加新功能时为您提供了很大的灵活性。它还让您有时间重新思考重写旧功能的必要性。

贡献和提供反馈

Kyma 是一个开源项目,我们非常乐意帮助它成长。它的发展离不开您的帮助。阅读本文后,您已经知道我们不想重复造轮子。我们在工作模式中坚持这一原则,这使社区贡献者能够参与进来。我们在特殊兴趣小组中工作,并有公开录制的会议,您可以随时加入,因此我们的设置类似于您从 Kubernetes 本身了解到的那种。欢迎通过TwitterSlack与我们分享您的反馈。