介绍和目标

在这篇博文中,我想描述我使用 Netherite 作为 Azure Durable Functions 的存储提供程序以及将这种组合部署到 Kubernetes 集群的过程。我的主要目标是让事情运行起来,看看哪里有一些粗糙的地方。因此,这篇博文不能作为高效设置的指南,它更像是朝着这种设置迈出的第一步。

备注:如果您还没有听说过 Netherite 作为 Azure Durable Functions 的存储提供商,我建议您从这里开始🧐:https://microsoft.github.io/durabletask-netherite/#/

示例代码

这篇博文使用的所有代码都可以在 GitHub 上找到:

https://github.com/lechnerc77/netherite-kyma-sample

设置

旅程的设置非常基本。我们使用 VS Code](https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-azurefunctions)的[Azure Functions Extension 附带的 Durable Functions 示例。我们将使用 TypeScript 作为语言。你应该可以使用 C#/.NET Core 3.1 做同样的事情,但我使用的语言与此不同,因为离开 .NET 区域时经常会有一些惊喜。

代码方面,我们有一个触发 Orchestrator 功能的 _HTTP Starter 功能。然后,此函数使用不同的参数值调用 Activity 函数 3 次:

import * as df from "durable-functions"

const orchestrator = df.orchestrator(function* (context) {
    const outputs = []

    outputs.push(yield context.df.callActivity("HelloCity", "Tokyo"))
    outputs.push(yield context.df.callActivity("HelloCity", "Seattle"))
    outputs.push(yield context.df.callActivity("HelloCity", "London"))

    return outputs
})

export default orchestrator

进入全屏模式 退出全屏模式

活动函数返回一个字符串,其中包含传递给函数的参数:

import { AzureFunction, Context } from "@azure/functions"

const activityFunction: AzureFunction = async function (context: Context): Promise<string> {
    return `Hello ${context.bindings.name}!`
}

export default activityFunction

进入全屏模式 退出全屏模式

此设置在“通常”的 Azure 存储上运行。现在让我们把它带到下界。

转移到 Netherite

由于_扩展包机制_尚不支持 Netherite,我们从host.json中删除扩展包部分,并通过以下方式添加 Netherite 扩展:

func extensions install  --package Microsoft.Azure.DurableTask.Netherite --version 0.5.0-alpha`

进入全屏模式 退出全屏模式

这为我们提供了一个包含相关依赖项的extensions.csproj文件。

此外,我们在host.json文件中添加一个配置,让主机知道新的存储提供程序:

"extensions": {
    "durableTask": {
      "hubName": "HelloNetherite",
      "useGracefulShutdown": true,
      "storageProvider": {
        "type": "Netherite",
        "StorageConnectionName": "AzureWebJobsStorage",
        "EventHubsConnectionName": "EventHubsConnection",
        "CacheOrchestrationCursors": "false"
      }
    }
  }

进入全屏模式 退出全屏模式

⚠ 注意参数"CacheOrchestrationCursors": "false":此设置对于非 .NET 世界保持编排运行是必需的。否则,您的处理将在第一个yield之后中止(请参阅https://github.com/microsoft/durabletask-netherite/issues/69)

这是一个非常精简的配置,可以让事情正常进行,但是如 Netherite 的文档中所述,还有更多的微调可能。

本地试用

作为第一个测试,我们在本地运行该函数,因此我们必须调整local.settings.json以指向本地存储模拟器。此外(与“经典”持久函数相反)我们还需要指定与 EventHub 的连接,该连接用于本地执行指向内存:

{
  "IsEncrypted": false,
  "Values": {
    "AzureWebJobsStorage": "UseDevelopmentStorage=true",
    "EventHubsConnection": "MemoryF",
    "FUNCTIONS_WORKER_RUNTIME": "node"
  }
}

进入全屏模式 退出全屏模式

作为存储模拟器,我们使用 Azurite i。 e.Azurite VSCode 扩展。

执行函数应该会产生预期的输出:

Netherite本地函数执行

构建 Docker 镜像并运行它

随着本地设置的工作,是时候构建包含 Azure 函数的 Docker 映像了。 func CLI 通过以下方式帮助我们:

func init --docker-only

进入全屏模式 退出全屏模式

此命令创建Dockerfile以及.dockerignore文件

使用 Azurite VSCode 扩展时,它创建了几个文件。为了避免将它们复制到 Docker 映像,我们将以下行放入您的.dockerignore文件中:

__azurite_db*__.json
__blobstorage__
__queuestorage__

进入全屏模式 退出全屏模式

有了它,我们可以建立您的形象。我们使用Makefile进行构建和推送,如下所示:

RELEASE=0.0.1
APP=containered_netherite
DOCKER_ACCOUNT=<YOUR DOCKER ACCOUNT NAME>
CONTAINER_IMAGE=${DOCKER_ACCOUNT}/${APP}:${RELEASE}

.PHONY: build-image push-image

build-image:
    docker build -t $(CONTAINER_IMAGE) --no-cache --rm .

push-image: build-image
    docker push $(CONTAINER_IMAGE)

进入全屏模式 退出全屏模式

为了验证容器是否按预期运行,我们使用 Netherite 存储库示例中提供的脚本在 Azure 上创建必要的资源 (https://github.com/microsoft/durabletask-netherite/tree/main/samples/ scripts) 即用于创建 Azure 存储和事件中心的init.ps1脚本。在运行它们之前,请确保您根据需要调整了settings.ps1文件,尤其是。为资源输入一个合适的名称(或多个名称)。

成功创建资源后,我们获取两个资源的可用连接字符串(例如,在 Azure 门户中)并将它们放入env.list文件中。这使得注入容器更容易。

我们将那个突击队员也放在Makefile中来启动容器,以便将 Docker 周围的所有东西都放在一个地方:

docker run --env-file env.list -it -p 8080:80 $(CONTAINER_IMAGE) 

进入全屏模式 退出全屏模式

当我们启动容器时,我们会看到......一个错误:

Microsoft.Azure.WebJobs.Host.FunctionInvocationException: Exception while executing function: 
 ---> System.InvalidOperationException: Webhooks are not   
   at Microsoft.Azure.WebJobs.Extensions.DurableTask.HttpApiHandler.ThrowIfWebhooksNotConfigured()

进入全屏模式 退出全屏模式

嗯......做一些严肃的高级开发人员研究来弄清楚🤪 aka google-fu 并搜索堆栈溢出,我遇到了这个:https://stackoverflow.com/questions/64400695/azure-durable-function-httpstart-failure -webhooks-are-not-configured/64404153#64404153

所以我们还需要一个参数,即WEBSITE_HOSTNAME来注入容器以使事情顺利进行。该参数必须指向 Azure 函数使用的 HTTP 主机名。

对于通过 Docker 本地执行,这意味着将以下行添加到env.list:

WEBSITE_HOSTNAME=localhost:8080

进入全屏模式 退出全屏模式

通过此调整,Docker 容器按预期工作。我们现在通过Makefile中的相应命令将其推送到 Docker 注册表。

之后,让我们进入合乎逻辑的下一步,将事物带到 Kubernetes。

将其部署到 Kubernetes ...... 来一波

我在 Kubernetes 上使用Kyma作为自以为是的堆栈(准确地说是在Gardener集群上)进行此练习。普通堆栈的设置应该类似,但 API 网关必须根据您使用的内容进行相应调整。

备注:如果您想试用 Kyma,可以通过SAP Business Technology Platform 试用版免费试用。

为了部署到 Kyma,我们需要以下文件:

  • deployment.yaml:包含您的应用程序aka容器以及对配置映射以及机密和服务的引用

  • secrets.yaml:包含到 Azure 存储和事件中心的连接字符串

  • apirule.yaml:包含 Kyma 提供的 API 网关的配置,用于暴露 HTTP 端点。注意 - API 规则中没有身份验证!

通过kubectl apply -f将这些文件应用到 Kyma 集群后,我们需要在 Kyma Dashboard 中查找托管 API 的端点,并将其添加到configmap.yaml以提供WEBSITE_HOSTNAME参数的端点。应用最后一个文件后,设置应该启动并运行。所以试一试,当 Starter Function 执行时,我们会看到如下输出:

Azure Function Starter 结果

导航到状态 URI,我们将看到预期的结果:

持久功能执行结果

任务完成🥳:以 Netherite 作为存储提供者的耐用功能在 Kyma (Kubernetes) 上启动并运行!

清理

由于 Azure 上的资源需要花钱,因此您可以使用 GitHub 存储库中的脚本delete.ps1进行清理,这将删除 Azure 上的完整资源组。

请注意,当您重新启动服务以交换secrets.yaml文件中的连接字符串并将其重新应用到您的 Kubernetes 部署时。

总结

尽管 Netherite 仍处于 alpha 阶段,但你已经可以用它弄脏你的手了。在这篇博文中,我们经历了一个将存储提供程序用例部署到 Kubernetes 的场景。除了两个小障碍外,我们还必须克服两个小障碍才能让设置按预期运行。

然而,这只是使用 Netherite 的第一步,还有更多的步骤可以实现生产级设置。

一个肯定有意义的未来主题是以一种更“自然”的方式将 Azure 服务集成到 Kubernetes 集群中,而不仅仅是通过调用外部世界。所以,更多博客文章的内容 - 到时候见🤠

Logo

K8S/Kubernetes社区为您提供最前沿的新闻资讯和知识内容

更多推荐