区块链技术

在Deckhouse管理的Kubernetes集群中运行WebAssembly应用详解

本文主要是介绍在Deckhouse管理的Kubernetes集群中运行WebAssembly应用详解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

WebAssembly (Wasm)技术的高性能和安全性正让我们越来越重视它的实际应用。我决定了解它是什么以及它是如何运作的。我想尝试在Kubernetes中使用Wasm——这样我就能享受到Kubernetes的所有好处,例如资源共享、容错性、可扩展性等。

但是,在普通的 Kubernetes 集群上运行 Wasm 应用并不像听起来那么简单,因为在工作节点上设置运行时环境相当复杂。内置的 K8s 工具并没有设计得对普通用户来说容易定制节点。这种方法的缺点在于,如果你需要尝试不同的运行时或运行大量应用程序,你希望集群扩展尽可能简单轻松。在这种情况下,以声明性的方式管理节点也非常合适。因此,我想尝试使用 Deckhouse Kubernetes 平台 (DKP) 来运行 Wasm 应用程序。该平台极大地简化了 Kubernetes 集群的部署和管理过程。

我叫叶戈尔·拉扎列夫,在 Flant 的 DevOps 工程师。在这篇文章里,我会教你如何在 DKP 管理的 Kubernetes 集群里运行 Wasm 应用。我们将搭建环境,安装所需的组件,并运行一个简单的 WebAssembly 程序。

设置 NodeGroup(节点组)

我觉得把常规工作负载和Wasm工作负载分开是有道理的,这样可以为实验工作负载分配专门的节点。为此,我们创建一个NodeGroup,平台将使用它来管理单独的节点。在配置NodeGroup时,你应该为这些节点添加标签。这样,你可以使用NodeSelector将工作负载分配给合适的节点。

kubectl create -f -<<EOF
apiVersion: deckhouse.io/v1
kind: 节点组
metadata:
  name: wasm
spec:
  云实例:
    类型引用:
      类型: YandexInstanceClass
      名称: worker
    maxPerZone: 1
    minPerZone: 1
    区域:
    - ru-central1-a
  中断策略:
    批准模式: 自动
  kubelet:
    容器日志最大文件数: 4
    容器日志最大文件大小: 50Mi
    资源预留:
      模式: 自动
  节点模板:
    标签:
      node.deckhouse.io/group: wasm
  节点类型: 云暂态
EOF

一旦NodeGroup被创建,DKP将在eu-west-1a区域中为AWSInstanceClass=worker实例类型创建一台虚拟机,并在该虚拟机上添加node.deckhouse.io/group=wasm标签。

安装WasmEdge运行时环境.

Kubernetes 需要一个专门的运行时,即 WebAssembly System Interface (WASI),来运行 Wasm 应用程序。在本文中,我们将使用 WasmEdge。除此之外,我们还需要更新 containerd 配置,以便支持新的运行时环境。NodeGroupConfiguration 资源使你能够在节点上运行 bash 脚本,因此我们将使用它来安装 WasmEdge 并进行一些额外的设置。

检查是否已存在WASI bin文件,若不存在则下载它。接下来,使用bashbooster将主containerd配置与/etc/containerd/conf.d/*.toml中的配置合并。修改/etc/containerd/config.toml这会促使containerd重启:

kubectl create -f -<<EOF
apiVersion: deckhouse.io/v1alpha1
kind: NodeGroupConfiguration
metadata:
  name: wasm-additional-shim.sh
spec:
  bundles:
    - '*'
  content: |
    [ -f "/bin/containerd-shim-wasmedge-v1" ] || curl -L https://github.com/containerd/runwasi/releases/download/containerd-shim-wasmedge%2Fv0.3.0/containerd-shim-wasmedge-$(uname -m | sed s/arm64/aarch64/g | sed s/amd64/x86_64/g).tar.gz | tar -xzf - -C /bin

    mkdir -p /etc/containerd/conf.d
    bb-sync-file /etc/containerd/conf.d/additional_shim.toml - containerd-config-changed << "EOF"
    [plugins]
      [plugins."io.containerd.grpc.v1.cri"]
        [plugins."io.containerd.grpc.v1.cri".containerd]
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
            [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.wasmedge]
              runtime_type = "io.containerd.wasmedge.v1"
              [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.wasmedge.options]
                BinaryName = "/bin/containerd-shim-wasmedge-v1"
    EOF
  nodeGroups:
    - "节点组"
  weight: "权重"
EOF
定义新的运行时类别

安装了 WasmEdge 后,你需要定义一个新的 Runtime 类。这将让你可以指定如何运行特定的工作负载:使用默认的运行时,或者通过在 spec.runtimeClassName 中明确指定另一个运行时。

kubectl apply -f -<<EOF
---
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: wasmedge
handler: wasmedge
EOF
运行测试 Wasm 应用程序

首先,确保平台已经完成了节点配置,并且更新了containerd的配置。

root@wasm:~# 用 `grep` 命令搜索 wasm 关键词在容器配置文件中
    [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.wasmedge]  
      runtime_type = "io.containerd.wasmedge.v1"  
      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.wasmedge.options]  
        BinaryName = "/bin/containerd-shim-wasmedge-v1"  # 二进制文件名,这里指的就是容器运行时的启动程序

现在你可以运行这个测试的Wasm应用程序了。为此,你需要创建一个Job,并使用一个简单的WebAssembly模块。然后,你需要在Job中指定NodeSelector,并使用新创建的wasmedge RuntimeClass。

kubectl apply -f -<<EOF
apiVersion: batch/v1
kind: 任务
metadata:
  name: wasm-test
规范:
  模板:
    规范:
      容器:
      - image: wasmedge/example-wasi:latest
        name: wasm-test
        资源: {}
      重启策略: Never
      运行时类名: wasmedge
      节点选择器:
        node.deckhouse.io/group: wasm
  回退限制: 1
EOF

检查 pod 的状态和日志内容,确保一切运行顺畅,看看是否有任何问题。

    root@test-master-0:~# kubectl get pods  
    NAME              准备就绪   状态      重启次数   存活时间  
    wasm-test-2g5jl   0/1     已完成       0          18秒  

    root@test-master-0:~# kubectl logs wasm-test-2g5jl  
    随机数: -700610054  
    随机字节: [163, 184, 229, 154, 4, 145, 145, 96, 181, 77, 64, 159, 123, 45, 5, 134, 93, 193, 207, 74, 129, 113, 204, 174, 188, 152, 172, 151, 125, 78, 199, 177, 127, 112, 116, 255, 188, 180, 47, 110, 22, 241, 63, 87, 78, 168, 36, 202, 168, 90, 248, 79, 38, 59, 204, 128, 141, 92, 209, 205, 129, 51, 71, 214, 91, 237, 115, 145, 77, 136, 166, 115, 221, 66, 123, 186, 19, 39, 122, 204, 103, 221, 89, 97, 148, 57, 250, 255, 165, 53, 14, 241, 97, 138, 147, 201, 204, 29, 76, 219, 128, 48, 143, 165, 138, 231, 62, 235, 190, 94, 142, 63, 197, 37, 57, 241, 33, 99, 240, 215, 216, 33, 68, 141, 82, 21, 152, 93]  
    来自 wasi 的打印: 这是主函数中的内容  
    这是主函数中的内容  
    环境变量如下:  
    KUBERNETES_SERVICE_PORT_HTTPS: 443  
    KUBERNETES_PORT_443_TCP: tcp://10.222.0.1:443  
    KUBERNETES_PORT_443_TCP_ADDR: 10.222.0.1  
    KUBERNETES_PORT_443_TCP_PROTO: tcp  
    KUBERNETES_SERVICE_PORT: 443  
    HOSTNAME: wasm-test-2g5jl  
    PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin  
    KUBERNETES_SERVICE_HOST: 10.222.0.1  
    KUBERNETES_PORT: tcp://10.222.0.1:443  
    KUBERNETES_PORT_443_TCP_PORT: 443  
    参数如下:  
    /wasi_example_main.wasm  
    文件内容为这是在文件中的内容

如果是这样,pod 的状态将是 Completed,这意味着任务已经执行完成,且一切正常。

在日志中,你应该能看到一个随机数和许多由应用程序生成的随机字节,正如预期的那样。这也说明应用程序确实可以访问环境和文件系统。

运行一个使用初始化容器的测试 Wasm 应用程序

现在让我们把事情稍微难一点。经常需要在 Pod 中运行 init 或 sidecar 容器,它们基于常规的容器镜像。因此,你需要为每个容器指定不同的运行时。runtimeClassName 是在 Pod 级别定义的,而不是在单个容器级别。

Containerd 支持容器运行时的切换,因此你需要一个工具来确定为特定容器应使用哪个运行时。默认情况下,集群中常规使用的 runc 不支持这一点。幸运的是,crun 的测试版则支持这种特性。

首先,你需要自己构建crun,因为它从官方仓库使用包管理器安装时不支持WasmEdge。NodeGroupConfiguration 可以帮助你完成这个步骤。

kubectl apply -f -<<EOF  
apiVersion: deckhouse.io/v1alpha1  
kind: NodeGroupConfiguration  
metadata:  
  name: crun-install.sh  
spec:  
  bundles:  
  - '*'  
  content: |  
    if ! [ -x /usr/local/bin/crun ]; then  
      apt-get update && apt-get install -y make git gcc build-essential pkgconf libtool libsystemd-dev libprotobuf-c-dev libcap-dev libseccomp-dev libyajl-dev go-md2man autoconf python3 automake  
      cd /root  
      [ -f "/root/.wasmedge/bin/wasmedge" ] || curl -sSf https://raw.githubusercontent.com/WasmEdge/WasmEdge/master/utils/install.sh | bash  
      git clone https://github.com/containers/crun && cd crun  
      ./autogen.sh  
      source /root/.wasmedge/env && ./configure --with-wasmedge  
      make  
      make install  
      cd .. && rm -rf crun  
    fi  
    echo "crun has been installed"  
    mkdir -p /etc/containerd/conf.d  
    bb-sync-file /etc/containerd/conf.d/add_crun.toml - containerd-config-changed << "EOF"  
    [plugins]  
      [plugins."io.containerd.grpc.v1.cri"]  
        [plugins."io.containerd.grpc.v1.cri".containerd]  
          [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]  
            [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.crun]  
              runtime_type = "io.containerd.runc.v2"  
              pod_annotations = ["*.wasm.*", "wasm.*", "module.wasm.image/*", "*.module.wasm.image", "module.wasm.image/variant.*"]  
              [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.crun.options]  
                BinaryName = "/usr/local/bin/crun"  
    EOF  
  nodeGroups:  
  - wasm  
  weight: 30  
EOF

上面的代码片段将安装 WasmEdge(这不同于我们在本文早些时候安装的 WasmEdge 运行时),同时安装所需的依赖项并构建 crun。此外,您还需要在 /etc/containerd/config.toml 配置文件中添加一个新的运行时容器,就像我们之前为 Wasm 添加运行时那样。

请注意 pod_annotations:这是一个要传递给运行时环境和容器OCI注解的列表。稍后我会解释为什么这一步是必需的。

接下来,创建一个新的运行时类:

kubectl apply -f -<<EOF

---
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: crun
handler: crun
EOF

应用RuntimeObject类(RuntimeClass)配置到crun运行时。

现在来试试运行你的工作负载。

kubectl apply -f -<<EOF  
apiVersion: batch/v1  
kind: Job  
metadata:  
  name: wasm-test  
  # 元数据定义了作业的名称和注释
spec:  
  template:  
    metadata:  
      annotations:  
        module.wasm.image/variant: compat-smart  
    spec:  
      initContainers:  
      - name: hello  
        image: busybox:latest  
        command: ['sh', '-c', 'echo "你好,中二病!"']  
        # 命令: ['sh', '-c', 'echo "Hello, Medium!"']
      containers:  
      - image: wasmedge/example-wasi:latest  
        name: wasm-test  
        resources: {}  
      restartPolicy: Never  
      runtimeClassName: crun  
      nodeSelector:  
        node.deckhouse.io/group: wasm  
        # 节点选择器用于选择符合特定条件的节点
      backoffLimit: 1  
      # 后退限制定义了任务在失败后可以重新尝试的最大次数
EOF

The runtimeClassName: crun 参数表示现在用 crun 而不是默认的 runc 来启动容器。另外,module.wasm.image/variant: compat-smart 标签让 crun 使用哪种模式。

为了使这生效起来,你需要在构建WASM图像,的逗号应移除,改为在构建WASM图像后添加如下所示的OCI注解:

...
"annotations": {  
 "run.oci.handler": "wasm"  
},  
...

Crun 利用 containerd 配置中的 pod_annotations 注解以及 K8s 对象上的 compat-smart 注解来判断哪个工作负载由其自身执行,哪个交给 Wasm 运行时执行。

检查 pod 的状态和日志。你应该会看到和以前一样的情况,如下。

    root@test-master-0:~# kubectl get pods  
    NAME              READY   STATUS      RESTARTS   AGE  
    wasm-test-pn4gv   0/1     Completed   0          32s  

    root@test-master-0:~# kubectl logs wasm-test-pn4gv  
    默认的容器是 "wasm-test",它是 wasm-test 和 hello (init) 的其中之一  
    随机数: -158793507  
    随机字节: [210, 246, 181, 132, 184, 214, 110, 71, 198, 68, 154, 182, 253, 103, 116, 207, 5, 205, 185, 81, 19, 28, 61, 61, 85, 26, 222, 111, 239, 110, 21, 68, 119, 245, 153, 190, 105, 175, 191, 163, 48, 198, 41, 207, 155, 30, 122, 166, 23, 56, 59, 168, 91, 57, 103, 213, 145, 10, 130, 224, 28, 5, 73, 176, 206, 111, 37, 241, 38, 57, 98, 158, 150, 115, 249, 233, 194, 156, 13, 109, 85, 130, 232, 91, 253, 16, 8, 233, 92, 162, 237, 197, 151, 112, 52, 140, 83, 179, 31, 48, 233, 56, 54, 75, 43, 239, 233, 169, 169, 81, 36, 52, 59, 66, 102, 40, 52, 202, 34, 56, 167, 229, 197, 25, 72, 136, 147, 254]  
    从 wasi 打印: 这是从 main 函数输出的  
    环境变量如下:  
    PATH: /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin  
    HOSTNAME: wasm-test-pn4gv  
    KUBERNETES_PORT: tcp://10.222.0.1:443  
    KUBERNETES_PORT_443_TCP: tcp://10.222.0.1:443  
    KUBERNETES_PORT_443_TCP_PROTO: tcp  
    KUBERNETES_PORT_443_TCP_PORT: 443  
    KUBERNETES_PORT_443_TCP_ADDR: 10.222.0.1  
    KUBERNETES_SERVICE_HOST: 10.222.0.1  
    KUBERNETES_SERVICE_PORT: 443  
    KUBERNETES_SERVICE_PORT_HTTPS: 443  
    HOME: /  
    命令行参数如下:  
    /wasi_example_main.wasm  
    文件内容是 This is in a file

查看初始化容器的日志信息。

root@test-master-0:~# kubectl logs wasm-test-pn4gv -c hello  
你好,中型!
结论

或以更口语化的方式表达为:话说回来,结论是。

在 Kubernetes 上运行 WebAssembly 应用程序可能听起来挺难的,但使用 Deckhouse Kubernetes 平台,这个过程变得相当容易。本文将详细介绍如何设置环境、安装所需组件以及运行一个测试的 Wasm 应用程序。希望这些信息能对你有所帮助。

DKP提供了许多用于管理Kubernetes集群的特性。我们将在接下来的文章中分享新的实践和技巧。请拭目以待!

随时可以在下方评论中提出你的问题并提出建议。你也可以通过Deckhouse 电报群提问,或在Deckhouse 项目在 GitHub 上的仓库中提交一个 Issue。我们很乐意帮助你。如果你喜欢这个项目,别忘了给它点个赞 这里。

这篇关于在Deckhouse管理的Kubernetes集群中运行WebAssembly应用详解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!