本文使用 Zhihu On VSCode 创作并发布
1. 总体目标
终于开始准备毕业设计了。在和导师们的讨论之后,我们确定之后的研究方向将会专注于 Serverless Computing. 先长舒一口气!
最近在读论文的过程中,发现很多论文的实验都借助于开源的无服务计算平台 OpenWhisk. 之前接触过一丢丢的 Kubernetes, 觉得两者相结合可以对计算资源的管理产生较大的帮助。然而网上对于在 Kubernetes 环境下部署 OpenWhisk 服务的介绍却寥寥无几,尤其是在国内不可抗力因素的干扰下,搭建环境也难上加难。鼓捣了三四天之后,终于成功利用虚拟机搭建了 Kubernetes 集群,然后部署了 OpenWhisk 计算框架。
这篇文章的总体目标包括以下几点:
- 了解 Kubernetes 和 OpenWhisk 的基本操作;
- 在 Kubernetes 环境下部署 OpenWhisk 服务;
- 在搭建成功的平台上运行简单的 Stateless Functions.
2. 准备工作
2.1. Kubernetes
Kubernetes 的官网对它的介绍如下:
Kubernetes is a portable, extensible, open-source platform for managing containerized workloads and services, that facilitates both declarative configuration and automation. It has a large, rapidly growing ecosystem. Kubernetes services, support, and tools are widely available.
翻译过来便是:
Kubernetes是一个可移植的、可扩展的、开源的平台,用于管理容器化的工作负载和服务,它促进了声明式配置和自动化。它有一个巨大的,快速增长的生态系统。Kubernetes的服务、支持和工具是广泛可用的。
因此 Kubernetes 的主要功能是对容器化的工作负载和服务进行管理。
2.2. OpenWhisk
OpenWhisk 的官网对它的介绍如下:
Apache OpenWhisk is an open source, distributed Serverless platform that executes functions (fx) in response to events at any scale. OpenWhisk manages the infrastructure, servers and scaling using Docker containers so you can focus on building amazing and efficient applications.
翻译过来便是:
OpenWhisk 是一个开源的分布式无服务器平台,它执行函数以响应任何规模的事件。OpenWhisk 使用 Docker 容器管理基础设施、服务器和扩展,因此您可以专注于构建令人惊叹的高效应用程序。
哦,我的老伙计,这奇怪的翻译腔,真是太不可思议了!总的来说,在以 OpenWhisk 为代表的无服务云计算平台中,用户只关注应用程序的构建,服务提供商负责计算资源的管理。
2.3. 硬件准备
初始阶段搭建的环境只用于熟悉无服务计算的环境,掌握常用的工具,对性能的要求不是特别高。因此,目前的集群是在我自己的笔记本电脑上,通过虚拟机搭建成功的。目前安装了两台虚拟机,内存均为 3GB, 硬盘分配了 20GB, 系统为 Ubuntu 20.04. 虚拟机的其他信息如下:
3. 搭建流程
3.1. 搭建 Kubernetes 集群
注意: 3.1.1-3.1.4 需要在 Master 与 Worker 上面分别执行。
3.1.1. 修改 Host 配置
分别在 Master 与 Worker 上执行下面两条命令,编辑对应的文件。注意,涉及到 IP 地址填写的地方要与自己的机器 IP 地址一致,下不赘述。
> vim /etc/hosts
> vim /etc/hostname
文件修改后,对应的结果如下。
# master
> cat /etc/hosts
192.168.124.129 k8s-master
192.168.124.131 k8s-worker-1
> cat /etc/hostname
k8s-master
# worker
> cat /etc/hosts
192.168.124.129 k8s-master
192.168.124.131 k8s-worker-1
> cat /etc/hostname
k8s-worker-1
3.1.2. 禁止交换分区
编辑/etc/fstab
文件,将最后一行注释之后,重启机器。
> vim /etc/fstab
# /swapfile none swap sw 0 0
由于是虚拟机,因此[2]中涉及的防火墙、SELinux 等操作都不需要操作。如果不禁止交换分区,在初始化 Kubernetes 集群的时候会产生错误。
3.1.3. 安装 Docker
> curl -fsSL https://get.docker.com | sudo sh -s -- --mirror Aliyun
> sudo usermod -aG docker $USER
> sudo mkdir -p /etc/docker
> sudo tee /etc/docker/daemon.json <<-'EOF'
{
"exec-opts": ["native.cgroupdriver=systemd"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m"
},
"storage-driver": "overlay2",
"registry-mirrors": ["https://docker.mirrors.ustc.edu.cn/"]
}
EOF
> sudo systemctl daemon-reload
> sudo systemctl restart docker
这里在安装 Docker 时将镜像替换成了中科大的镜像源,[1]里面使用的阿里云的镜像源,但是在后面下载 OpenWhisk 需要的镜像时,效果不如中科大的镜像源。大家如果遇到下载卡顿的时候,可以换镜像源试一试。
3.1.4. 安装 Kubernetes 组件
# 添加并信任APT证书
> curl https://mirrors.aliyun.com/kubernetes/apt/doc/apt-key.gpg | sudo apt-key add -
# 添加源地址
> sudo add-apt-repository "deb https://mirrors.aliyun.com/kubernetes/apt/ kubernetes-xenial main"
# 更新源并安装
> sudo apt update && sudo apt install -y kubelet kubeadm kubectl
# 添加 completion
> source <(kubectl completion bash)
> source <(kubeadm completion bash)
3.1.5. Master 节点启动
在 Master 节点上面执行以下命令:
> sudo kubeadm init --apiserver-advertise-address 192.168.124.129
--pod-network-cidr=10.244.0.0/16
--image-repository registry.cn-hangzhou.aliyuncs.com/google_containers
其中 192.168.124.129
为 Master 的 IP 地址,需要与自己的机器相适应。10.244.0.0/16
与我们后续使用的网络插件 Flannel 相关联。
执行完上面的命令之后会有大段的输出,我们按照它的指示执行以下命令。
> sudo mkdir -p $HOME/.kube
> sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
> sudo chown $(id -u):$(id -g) $HOME/.kube/config
同时,在上面的输出的最后,需要记录最后一段类似于以下的内容,这是后续子节点加入集群的命令。
kubeadm join 192.168.124.129:6443 --token t5765k.6son6uf1k6p8edt8
--discovery-token-ca-cert-hash sha256:2e45a15683e0e85aea6c1d61dd2674855dc75796b1a803e5cfeb504a85cee628
在初始化 Master 节点之后,需要安装网络插件。这里我们选择的是 Flannel 插件,因此执行命令:
> kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
Master 节点操作完毕!
3.1.6. Worker 节点加入集群
在 Worker 节点上执行我们之前保存的输出,加入集群中。
> kubeadm join 192.168.124.129:6443 --token t5765k.6son6uf1k6p8edt8
--discovery-token-ca-cert-hash sha256:2e45a15683e0e85aea6c1d61dd2674855dc75796b1a803e5cfeb504a85cee628
在 Master 节点上检查操作的正确性,先检查 Node 的正确性。
> kubectl get nodes
NAME STATUS ROLES AGE VERSION
k8s-master Ready master 4h15m v1.19.3
k8s-worker-1 Ready <none> 4h14m v1.19.3
在检查 Pod 的正确性。
> kubectl get pods -A
NAMESPACE NAME READY STATUS RESTARTS AGE
kube-system coredns-6c76c8bb89-pvnlv 1/1 Running 0 4h14m
kube-system coredns-6c76c8bb89-z75vl 1/1 Running 0 4h14m
kube-system etcd-k8s-master 1/1 Running 0 4h14m
kube-system kube-apiserver-k8s-master 1/1 Running 0 4h14m
kube-system kube-controller-manager-k8s-master 1/1 Running 0 4h14m
kube-system kube-flannel-ds-lwdmw 1/1 Running 0 4h13m
kube-system kube-flannel-ds-tzt9k 1/1 Running 0 4h14m
kube-system kube-proxy-ttt2k 1/1 Running 0 4h13m
kube-system kube-proxy-z4r7m 1/1 Running 0 4h14m
kube-system kube-scheduler-k8s-master 1/1 Running 0 4h14m
集群搭建成功!
3.2. 搭建 OpenWhisk 服务
注意:这里主要参考[3][4], 操作繁琐,需要耐心查阅官网进行环境配置。
3.2.1. 安装 Helm
Helm is a tool to simplify the deployment and management of applications on Kubernetes clusters. The OpenWhisk Helm chart requires the Helm 3.
安装命令如下,同时我们给 Helm 配置国内的镜像源,加快下载文件速度。
sudo snap install helm --classic
helm repo add stable http://mirror.azure.cn/kubernetes/charts/
3.2.2 配置 Dynamic Storage Provisioning
这里完全参考[4], 需要按照教程一步步地执行。由于我暂时没有明白内在原理,因此仅列出各个步骤作为参考。以下操作均在 Master 节点执行。
Set up the nfs server
> sudo mkdir /var/nfs/kubedata -p
> sudo chown nobody: /var/nfs/kubedata
> sudo systemctl enable nfs-server.service
> sudo systemctl start nfs-server.service
Set up nfs client provisioner
按照官网的说明,新建四个文件,分别命名为rbac.yaml
, sc.yaml
, deployment.yaml
, persistentvolumeclaim.yaml
.
文件的内容如下。
rbac.yaml
kind: ServiceAccount
apiVersion: v1
metadata:
name: nfs-client-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: nfs-client-provisioner-runner
rules:
- apiGroups: [""]
resources: ["persistentvolumes"]
verbs: ["get", "list", "watch", "create", "delete"]
- apiGroups: [""]
resources: ["persistentvolumeclaims"]
verbs: ["get", "list", "watch", "update"]
- apiGroups: ["storage.k8s.io"]
resources: ["storageclasses"]
verbs: ["get", "list", "watch"]
- apiGroups: [""]
resources: ["events"]
verbs: ["create", "update", "patch"]
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: run-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get", "list", "watch", "create", "update", "patch"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: default
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
sc.yaml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: openwhisk-nfs
annotations:
storageclass.kubernetes.io/is-default-class: "true"
provisioner: example/nfs
allowVolumeExpansion: true
parameters:
archiveOnDelete: "false"
deployment.yaml
:注意,将<NFS-SERVER-HOST-IP>
替换成对应的 Master IP。
kind: Deployment
apiVersion: apps/v1
metadata:
name: nfs-client-provisioner
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: quay.io/external_storage/nfs-client-provisioner:latest
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: example/nfs
- name: NFS_SERVER
value: <NFS-SERVER-HOST-IP>
- name: NFS_PATH
value: /var/nfs/kubedata
volumes:
- name: nfs-client-root
nfs:
server: <NFS-SERVER-HOST-IP>
path: /var/nfs/kubedata
persistentvolumeclaim.yaml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-nfs
spec:
storageClassName: openwhisk-nfs
accessModes:
- ReadWriteMany
resources:
requests:
storage: 500Mi
执行以下命令:
> kubectl apply -f rbac.yaml
> kubectl apply -f sc.yaml
> kubectl apply -f deployment.yaml
> kubectl apply -f persistentvolumeclaim.yaml
最后,检查结果执行的正确性。
> kubectl get pvc,pv -A
3.2.3 部署 OpenWhisk
以下操作均在 Master 节点执行。
首先下载 OpenWhisk 部署文件并进入文件夹。
> git clone https://github.com/apache/openwhisk-deploy-kube.git
> cd openwhisk-deploy-kube
新建 mycluster.yaml
文件后续使用,这里我们采用了最基本的安装需求。注意将192.168.124.129
替换成自己的 Master IP.
whisk:
ingress:
type: NodePort
apiHostName: 192.168.124.129
apiHostPort: 31001
nginx:
httpsNodePort: 31001
invoker:
containerFactory:
impl: "kubernetes"
使用 Helm 安装 OpenWhisk 并配置 WSK CLI。注意将192.168.124.129
替换成自己的 Master IP.
> helm install owdev ./helm/openwhisk -n openwhisk --create-namespace -f mycluster.yaml
> wsk property set --apihost 192.168.124.129:31001
> wsk property set --auth 23bc46b1-71f6-4ed5-8c54-816aa4f8c502:123zO3xZCLrMN6v2BKK1dXYFpXlPkccOFqm12CdAsMgRU4VrNZ9lyGVCGuMDGIwP
等待10分钟左右,OpenWhisk 便会部署成功。期间可以使用kubectl get pods -n openwhisk
查看 Pod 的状态。部署成功后,Pod 的状态应该全部变为Running
或者Complete
.
3.3. 测试
在 Master 节点上,我们测试函数的创建与调用。
新建文件hello.js
, 内容如下:
function main(params) {
var name;
if(params.name == undefined)
name = 'World';
else
name = params.name;
return {
payload:'Hello, ' + name + '!'
};
}
执行以下命令,创建函数。我们创建了一个名为hellojs
的函数。
> wsk action create -i hellojs hello.js
执行以下命令,调用函数。
> wsk action invoke -i hellojs --result --param name Zinuo
执行结果如下所示。
![12083a5e8a0cc47e358f47dd7f30499c.png](https://i-blog.csdnimg.cn/blog_migrate/a25d00c785020ea128fcfbba97a92f0a.png)
4. 总结
总的来说,无论是搭建 Kubernetes, 还是部署 OpenWhisk, 都是比较繁琐的过程。而且因为网络的原因,一次部署成功的可能性很小。因此需要多次重复的尝试。为了搭建这个环境,我搭建或者销毁 Kubernetes 集群不下数十次。三个注意点包括:
- 除了步骤 3.1.1-3.1.4 需要在 Master 与 Worker 上面分别执行,其余操作均在 Master 节点执行。
- 文中所有出现 IP 地址的地方,均要替换成自己实际的地址。
- 文中三次涉及更换镜像源来加快下载速度,分别是 Docker, Kubernetes, Helm.
Enjou yourself!
参考文献
[1] k8s 国内源安装备忘清单. https://github.com/islishude/blog/issues/212
[2] Kubernetes(一) 跟着官方文档从零搭建K8S. https://juejin.im/post/6844903943051411469
[3] OpenWhisk Deployment on Kubernetes. https://github.com/apache/openwhisk-deploy-kube
[4] Using Dynamic Storage Provisioning for OpenWhisk. https://github.com/apache/openwhisk-deploy-kube/blob/master/docs/k8s-nfs-dynamic-storage.md