Post

在 Kubernetes 集群上部署 OpenWhisk 无服务计算框架

前置知识:Kubernetes 一小时轻松入门

1 使用 Multipass 创建虚拟机集群

本文的 Kubernetes 集群中包含四台虚拟机。其中包括一台 master 节点和两台 worker 节点,以及一个用于持久化存储的 NFS 服务器。它们的配置如下:

虚拟机名称CPU 核心数内存容量硬盘空间
master26GB50GB
worker126GB20GB
worker226GB20GB
storage24GB50GB

OpenWhisk 对节点配置的最低要求

1.1 安装 Multipass

1
sudo snap install multipass

1.2 创建虚拟机

1
2
3
4
multipass launch --name master --cpus 2 --memory 6G --disk 50G
multipass launch --name worker1 --cpus 2 --memory 6G --disk 20G
multipass launch --name worker2 --cpus 2 --memory 6G --disk 20G
multipass launch --name storage --cpus 2 --memory 4G --disk 50G

查看创建结果:

1
multipass list
1
2
3
4
5
Name                    State             IPv4             Image
master                  Running           10.251.200.141   Ubuntu 24.04 LTS
storage                 Running           10.251.200.186   Ubuntu 24.04 LTS
worker1                 Running           10.251.200.142   Ubuntu 24.04 LTS
worker2                 Running           10.251.200.168   Ubuntu 24.04 LTS

其他可能用到的命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 查看虚拟机资源使用情况
multipass info <instance-name>

# 将虚拟机移动至回收站
multipass delete <instance-name>

# 将虚拟机移出回收站
multipass recover <instance-name>

# 销毁虚拟机
multipass delete --purge <instance-name>

# 销毁所有虚拟机
multipass delete --purge --all

1.3 在 master 节点中安装 K3s

从宿主机连接到 master 节点:

1
multipass shell master

执行 K3s 安装脚本

1
2
3
4
curl -sfL https://get.k3s.io | sh -

# 使用国内镜像
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -

1.4 将 worker 节点加入集群

在宿主机中获取 master 节点的 IP 地址和 K3s 的 token:

1
2
MASTER_IP=$(multipass info master | grep IPv4 | awk '{print $2}')
TOKEN=$(multipass exec master sudo cat /var/lib/rancher/k3s/server/node-token)

在所有 worker 节点上执行 K3s 安装脚本

1
2
3
4
5
6
7
8
for index in 1 2; do
    multipass exec worker${index} -- bash -c "curl -sfL https://get.k3s.io | K3S_URL=https://${MASTER_IP}:6443 K3S_TOKEN=${TOKEN} sh -"
done

# 使用国内镜像
for index in 1 2; do
    multipass exec worker${index} -- bash -c "curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn K3S_URL=https://${MASTER_IP}:6443 K3S_TOKEN=${TOKEN} sh -"
done

查看集群节点状态:

1
multipass exec master sudo kubectl get nodes
1
2
3
4
NAME      STATUS   ROLES                  AGE     VERSION
master    Ready    control-plane,master   10m     v1.29.4+k3s1
worker1   Ready    <none>                 2m12s   v1.29.4+k3s1
worker2   Ready    <none>                 2m4s    v1.29.4+k3s1

1.5 搭建 NFS 服务器

参考文档

从宿主机连接到 storage 节点:

1
multipass shell storage

安装 NFS 服务:

1
sudo apt install nfs-kernel-server

创建 Kubernetes 集群数据存储目录:

1
sudo mkdir /var/nfs/kubedata -p

将目录的拥有者设置为 nobody

1
sudo chown nobody: /var/nfs/kubedata

启用 NFS 服务:

1
2
sudo systemctl enable nfs-server.service
sudo systemctl start nfs-server.service

编辑 /etc/exports 文件:

1
sudo vim /etc/exports

在文件末尾添加集群数据存储目录的配置:

1
/var/nfs/kubedata *(rw,sync,no_subtree_check,no_root_squash,no_all_squash)

应用配置文件:

1
sudo exportfs -rav
1
exporting *:/var/nfs/kubedata

2 安装 NFS 客户端

为 Kubernetes 集群中的所有节点安装 NFS 客户端:

1
2
3
multipass exec master -- bash -c "sudo apt install nfs-common -y"
multipass exec worker1 -- bash -c "sudo apt install nfs-common -y"
multipass exec worker2 -- bash -c "sudo apt install nfs-common -y"

否则在创建 NFS Dynamic Storage 时,节点无法挂载 NFS 服务器的共享目录。

3 安装 Kubernetes 集群的包管理工具 Helm

从宿主机连接到 master 节点:

1
multipass shell master

使用 snap 安装 Helm:

1
sudo snap install helm --classic

4 安装 OpenWhisk Command-line Interface (wsk CLI)

从宿主机连接到 master 节点:

1
multipass shell master

下载并安装 wsk CLI 二进制文件:

1
2
3
4
mkdir ~/downloads && cd ~/downloads
wget https://github.com/apache/openwhisk-cli/releases/download/latest/OpenWhisk_CLI-latest-linux-amd64.tgz
tar -xvf OpenWhisk_CLI-latest-linux-amd64.tgz
sudo mv wsk /usr/local/bin/wsk

5 为 Kubernetes 集群部署 NFS Dynamic Storage

参考文档deployment.yaml 文件所使用的 quay.io/external_storage/nfs-client-provisioner 镜像已停止维护。Pod 运行时会报错 unexpected error getting claim reference: selfLink was empty, can’t make referenceselfLink 字段在 Kubernetes 1.20 版本中被废弃。

根据官方文档,使用 NFS subdir 为 Kubernetes 集群提供 NFS Dynamic Storage 。

使用 Helm Chart 安装 NFS Subdirectory External Provisioner

参考文档

从宿主机连接到 master 节点:

1
multipass shell master

添加 Helm Chart 仓库:

1
2
sudo helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
sudo helm repo update

以名称 openwhisk-nfs 部署 NFS Subdirectory External Provisioner,并指定 NFS 服务器的 IP 地址共享目录

1
2
3
sudo helm --kubeconfig /etc/rancher/k3s/k3s.yaml install openwhisk-nfs nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
    --set nfs.server=10.251.200.186 \
    --set nfs.path=/var/nfs/kubedata

在 K3s 中,需要使用 --kubeconfig 参数指定 kubeconfig 文件的路径,使得 Helm 能够访问集群

安装成功后显示信息:

1
2
3
4
5
6
NAME: openwhisk-nfs
LAST DEPLOYED: Wed May  8 18:15:14 2024
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None

检查资源创建结果

1
sudo kubectl get clusterrole,clusterrolebinding,role,rolebinding | grep nfs
1
2
3
4
clusterrole.rbac.authorization.k8s.io/openwhisk-nfs-nfs-subdir-external-provisioner-runner                   2024-05-08T10:15:14Z
clusterrolebinding.rbac.authorization.k8s.io/run-openwhisk-nfs-nfs-subdir-external-provisioner        ClusterRole/openwhisk-nfs-nfs-subdir-external-provisioner-runner     112s
role.rbac.authorization.k8s.io/leader-locking-openwhisk-nfs-nfs-subdir-external-provisioner   2024-05-08T10:15:14Z
rolebinding.rbac.authorization.k8s.io/leader-locking-openwhisk-nfs-nfs-subdir-external-provisioner   Role/leader-locking-openwhisk-nfs-nf-subdir-external-provisioner   112s
1
sudo kubectl get storageclass
1
2
3
NAME                   PROVISIONER                                                   RECLAIMPOLICY   VOLUMEBINDINGMODE      ALLOWVOLUMEEXPANSION   AGE
local-path (default)   rancher.io/local-path                                         Delete          WaitForFirstConsumer   false                  7h16m
nfs-client             cluster.local/openwhisk-nfs-nfs-subdir-external-provisioner   Delete          Immediate              true                   3m26s
1
sudo kubectl get pods
1
2
NAME                                                             READY   STATUS    RESTARTS   AGE
openwhisk-nfs-nfs-subdir-external-provisioner-54846ffcfb-26j8r   1/1     Running   0          5m35s

其他可能用到的命令

1
2
# 卸载 NFS Subdirectory External Provisioner
sudo helm --kubeconfig /etc/rancher/k3s/k3s.yaml uninstall openwhisk-nfs

6 部署 OpenWhisk

从宿主机连接到 master 节点:

1
multipass shell master

6.1 为集群中的节点添加标签

在部署 OpenWhisk 之前,需要指明哪些节点运行 OpenWhisk 的控制面板(如:controller、kafka、zookeeper 和 CouchDB),哪些节点执行函数调用。

通过给节点添加标签来指明节点的角色:

1
2
3
sudo kubectl label node master openwhisk-role=core
sudo kubectl label node worker1 openwhisk-role=invoker
sudo kubectl label node worker2 openwhisk-role=invoker

使用 sudo kubectl describe nodes <node-name> 命令查看节点的标签。

6.2 编写安装配置文件 mycluster.yaml

参考文档

mycluster.yaml 配置了 Kubernetes 集群的 Ingress 服务的地址和端口号(默认 31001 ),用于 wsk CLI 连接 OpenWhisk 服务。

1
2
3
4
5
6
7
8
9
10
11
12
whisk:
  ingress:
    type: NodePort
    apiHostName: 10.251.200.141
    apiHostPort: 31001

nginx:
  httpsNodePort: 31001

invoker:
  containerFactory:
    impl: "kubernetes"

将上述内容写入 ~/kubeyaml/mycluster.yaml 文件中。

6.3 配置 wsk CLI

配置 wsk CLI 的 apihost 为 Ingress 服务的地址和端口号,auth 为 OpenWhisk 的 guest 用户的默认 token :

1
2
sudo wsk property set --apihost 10.251.200.141:31001
sudo wsk property set --auth 23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP

6.4 使用 Helm 部署 OpenWhisk

Helm Repository 中的 OpenWhisk Chart 使用的 npm 镜像版本过低,会导致安装失败。因此使用 GitHub 上的 OpenWhisk Chart 安装。

1
2
3
4
cd ~
git clone https://github.com/apache/openwhisk-deploy-kube.git
cd openwhisk-deploy-kube
sudo helm --kubeconfig /etc/rancher/k3s/k3s.yaml install owdev ./helm/openwhisk -n openwhisk --create-namespace -f ~/kubeyaml/mycluster.yaml

在 K3s 中,需要使用 --kubeconfig 参数指定 kubeconfig 文件的路径,使得 Helm 能够访问集群

安装成功后显示信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
NAME: owdev
LAST DEPLOYED: Wed May  8 18:26:12 2024
NAMESPACE: openwhisk
STATUS: deployed
REVISION: 1
NOTES:
Apache OpenWhisk
Copyright 2016-2021 The Apache Software Foundation

This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).

To configure your wsk cli to connect to it, set the apihost property
using the command below:

  $ wsk property set --apihost 10.251.200.141:31001

Your release is named owdev.

To learn more about the release, try:

  $ helm status owdev
  $ helm get owdev

Once the 'owdev-install-packages' Pod is in the Completed state, your OpenWhisk deployment is ready to be used.

Once the deployment is ready, you can verify it using:

  $ helm test owdev --cleanup

此时可以使用命令 sudo kubectl get pvc,pv -A 查看 NFS Dynamic Storage 的创建结果。STATUS 为 Bound 时表示创建成功。

使用命令 sudo kubectl get pods -n openwhisk --watch 查看集群容器部署的状态。当 install-packages Pod 的状态为 Completed 时,OpenWhisk 部署完成。

使用命令 sudo wsk -i package list /whisk.system 查看安装好的 OpenWhisk 包。-i 选项用于跳过 SSL 证书验证。

1
2
3
4
5
6
7
8
packages
/whisk.system/messaging                                                shared
/whisk.system/alarms                                                   shared
/whisk.system/websocket                                                shared
/whisk.system/utils                                                    shared
/whisk.system/samples                                                  shared
/whisk.system/slack                                                    shared
/whisk.system/github                                                   shared

其他可能用到的命令

1
2
3
4
5
6
# 查看 OpenWhisk 全部资源状态
sudo kubectl get all -n openwhisk
sudo kubectl get all -o wide -n openwhisk

# 卸载 OpenWhisk
sudo helm --kubeconfig /etc/rancher/k3s/k3s.yaml uninstall owdev -n openwhisk

7 函数部署示例

OpenWhisk 文档

从宿主机连接到 master 节点:

1
multipass shell master

创建用于存储函数的目录:

1
mkdir ~/functions && cd ~/functions

7.1 通过 wsk CLI 调用函数

创建 Node.js 函数 hello_cli.js

1
2
3
4
function main(params) {
    var name = params.name || 'World';
    return {payload: 'Hello, ' + name + '!'};
}

使用 wsk CLI 部署函数:

1
sudo wsk -i action create hellocli hello_cli.js

调用函数:

1
sudo wsk -i action invoke hellocli --result
1
2
3
{
    "payload": "Hello, World!"
}

传递参数:

1
sudo wsk -i action invoke hellocli --result --param name JavaScript
1
2
3
{
    "payload": "Hello, JavaScript!"
}

7.2 通过 web URL 调用函数

参考文档

创建 Python 函数 hello_web.py

1
2
3
4
5
6
def main(args):
    return {
        "statusCode": 200,
        "headers": { "Content-Type": "application/json" },
        "body": args
    }

使用 wsk CLI 部署函数:

1
sudo wsk -i action create helloweb hello_web.py --web true

获取函数的 web URL:

1
sudo wsk -i action get helloweb --url
1
https://10.251.200.141:31001/api/v1/web/guest/default/helloweb

在宿主机中使用 curl 命令调用函数,-k 选项用于跳过 SSL 证书验证:

1
curl -k https://10.251.200.141:31001/api/v1/web/guest/default/helloweb
1
2
3
4
5
6
7
8
9
{
  "__ow_headers": {
    "accept": "*/*",
    "host": "controllers",
    "user-agent": "curl/7.81.0"
  },
  "__ow_method": "get",
  "__ow_path": ""
}

传递参数:

1
curl -k https://10.251.200.141:31001/api/v1/web/guest/default/helloweb?name=Python
1
2
3
4
5
6
7
8
9
10
{
  "__ow_headers": {
    "accept": "*/*",
    "host": "controllers",
    "user-agent": "curl/7.81.0"
  },
  "__ow_method": "get",
  "__ow_path": "",
  "name": "Python"
}

8 运维相关

8.1 关闭虚拟机

关闭顺序:worker1 -> worker2 -> master -> storage

1
2
3
4
multipass stop worker1
multipass stop worker2
multipass stop master
multipass stop storage

8.2 启动虚拟机

启动顺序:storage -> master -> worker1 -> worker2

1
2
3
4
multipass start storage
multipass start master
multipass start worker1
multipass start worker2

9 wsk CLI 常用命令

9.1 函数相关

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 列出所有函数
sudo wsk -i action list

# 创建函数
sudo wsk -i action create <action_name> <action_file>
sudo wsk -i action create <action_name> <action_zip> --kind <available_runtime>
# --kind 可选的参数在 https://github.com/apache/openwhisk-deploy-kube/blob/master/helm/openwhisk/runtimes.json 中配置

# 删除函数
sudo wsk -i action delete <action_name>

# 更新函数
sudo wsk -i action update <action_name> <action_file> --docker openwhisk/action-python-v3.11
# --docker 选项可以指定函数的运行环境(必须在 Docker Hub 上存在)

# 调用函数
sudo wsk -i action invoke --result <action_name> --param <key1> <value1> ...

9.2 调用相关

1
2
3
4
5
6
7
8
9
10
11
# 列出所有函数调用
sudo wsk -i activation list

# 获取函数调用的详细信息
sudo wsk -i activation get <request_id>

# 获取函数调用的结果
sudo wsk -i activation result <request_id>

# 获取函数调用的日志
sudo wsk -i activation logs <request_id>

9.3 利用 virtualenv 打包 Python 函数和依赖

参考文档:

__main__.pyrequirements.txt 所在的目录挂载到 openwhisk/action-python-v3.12 容器的 /tmp 目录下,然后利用容器的环境创建 virtualenv 文件夹并安装 requirements.txt 中的依赖:

1
sudo docker run --rm -v "$(pwd):/tmp" --entrypoint "/bin/bash" openwhisk/action-python-v3.12 -c "cd /tmp && virtualenv virtualenv && source virtualenv/bin/activate && pip install -r requirements.txt"

然后将文件夹内的内容打包成 zip 文件:

1
zip -r ../<action_name>.zip .
This post is licensed under CC BY 4.0 by the author.