Jason Pan

理解FaaS & OpenFaaS部署

潘忠显 / 2021-03-26


最近在了解和学习使用FaaS,其中的一些理解和实践过程,在这里做简单的笔记。理解不对的,请不吝指正。

一、FaaS是什么?

1. 与Serverless的关系

Serverless 和 FaaS 通常相互混淆,实际上FaaS是Serverless的子集。CNCF的白皮书中提到:

Serverless计算平台能够提供FaaS或/和BaaS。

Serverless专注于任何服务类别,包括计算、存储、数据库、消息传递、API网关等,对用户屏蔽了服务器的配置、管理和计费。

而FaaS是Serverless架构中的核心技术,但它专注于事件驱动的计算范例,仅当需要响应事件或请求时,应用程序代码或容器才会运行。

2. 与微服务的对比

接口形式类似微服务类似。微服务将服务拆解成更小的服务(函数集合),然后打到镜像,运行在容器上。这些容器的维护和扩缩容,仍然需要服务提供者自己去关心。

FaaS是微服务更进一步,会将服务拆成单个的函数,每个函数部署在FaaS平台,平台会规范函数、扩缩容、打补丁、维护环境。

Monolithic vs Microservices vs Functions

3. 与PaaS的区别

FaaS变更时间更短,在毫秒几倍。

对于FaaS来讲,不需要进行任何的容量规划。而PaaS提供了扩容能力但是相对缓慢,并且容量评估复杂。

用户部署的FaaS无法维护长连接,因为函数用完就会被销毁,但可以将连接维护在外部服务中以保持长连接。同样的,对于服务的状态,也只能存储在外部资源中来提供有状态的服务。

FaaS可以不需要空闲流量,只在请求时才使用资源(一些云厂商支持预留一部分资源在空闲时使用)。而PaaS一般需要一定的资源来保持空闲容量。

FaaS计费粒度更细,通常以计算时间、调用次数、使用的流量等计费,代码未运行时不产生费用,而PaaS通常以资源和时间粒度计费。以腾讯云的SCF为例:

什么是 PaaS?

4. FaaS平台架构

FaaS的简单架构包括以下四种元素:

这四种元素的关系如下:

更具体的可以参考后文中OpenFaaS的架构图。

另外一种角度看,函数开发完成后,会经历构建、部署、扩缩容等不同生命阶段:

二、FaaS供应商

目前主流的云厂商,都有提供FaaS服务,并且会集成各种同步和异步的事件源,负责函数的扩缩容和部署,按照调用计费。

厂商 FaaS称呼 文档入口 使用框架
Google Cloud Functions 链接
AWS Lambda 链接
腾讯云 SCF / Serverless Cloud Function / 云函数 链接
阿里云 Function Compute / 函数计算 链接

1. 腾讯云 SCF

本节动图演示使用腾讯云的云函数(文档地址),通过其提供的Web IDE编辑Python函数,快速的创建一个云函数。

如果以下.gif文件不能正常显示,请使用电脑浏览器打开

serverless

几点说明:

2. 腾讯云 Serverless

本节动图演示使用腾讯云的Serverless服务(文档地址),在几分钟内,将一个静态网站部署上去并能正常访问。

目前腾讯云的Serverless服务是免费的,对于想搭建自己网站的用户而言,无需购买云服务器,只需要花几块钱购置域名,配置其指向Serverless服务即可拥有自己的网站了。

如果以下.gif文件不能正常显示,请使用电脑浏览器打开

serverless

几点说明:

3. 常见事件源

4. 应用场景汇总

各大厂商均在云函数的介绍页面,挂了一些应用场景的描述。下边以Google Cloud Functions和AWS Lambda的几个示图做简单的介绍。

Integration with third party services workflow diagram

 Serverless mobile back ends workflow diagram

 Real-time file processing workflow diagram

Real time stream processing diagram

AWS Lambda IoT 后端

AWS Lambda Web 后端

AWS Lambda 移动后端

这些应用场景具有一些特点,也可以说是哪些场景适合使用FaaS:

三、FaaS平台的架构

1. 当前FaaS框架

这里列举了当前较为流行的FaaS框架,以及他们的主页链接、Github地址和Star数。Openfaas是目前Star最多的,而且也更新也是几个项目中较频繁的,像Fn项目很久没有更新,这里就没有列出来。

框架 链接 Star
OpenFaaS 主页 Github 19.5k
OpenWhisk 主页 Github 5.2k
Fission 主页 Github 6k
Kubeless 主页 Github 6.4k
Knative 主页 Github 3.6k

2. 部署工具 – Serverless Framework

不是Serverless框架,而是一个部署Serverless应用的工具。

说明文档:https://github.com/serverless/serverless/blob/HEAD/README_CN.md

支持腾讯云SCF、AWS Lambda等部署。

3. OpenFaaS

OpenFaaS 主要是在K8s之上做了一些抽象的封装,每个函数或者服务会构建成镜像,以供编排。使用PLONK栈,分别是:

Stack

img

OpenFaaS默认会创建两个命名空间,openfaasopenfaas-fn。前者是用于维护OpenFaaS自身组件,后者用于维护创建的函数部署。“faas-netes"子项目,用于部署OpenFaaS到K8s集群。

接下来一节会介绍在MacOS上部署OpenFaaS以及创建、部署函数的过程。

四、OpenFaaS部署实践 [MacOS]

环境:Macbook (macOS 10.15.7)

部署时间:2021-03-21

部署完成后各个组件的版本情况:

1. 安装Docker

因为机器上之前就有安装docker,这次没有重新安装。如果有更新需求,可以参考这里的官方文档

2. 安装K8s相关组件

安装 minikube

brew install minikube

启动 minikube

➜  ~ minikube start
😄  Darwin 10.15.7 上的 minikube v1.18.1
✨  根据现有的配置文件使用 docker 驱动程序
👍  Starting control plane node minikube in cluster minikube
🏃  Updating the running docker "minikube" container ...
🐳  正在 Docker 20.10.3 中准备 Kubernetes v1.20.2…
🔎  Verifying Kubernetes components...
    ▪ Using image kubernetesui/dashboard:v2.1.0
    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v4
    ▪ Using image kubernetesui/metrics-scraper:v1.0.4
🌟  Enabled addons: storage-provisioner, default-storageclass, dashboard
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

安装kubectl

curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/darwin/amd64/kubectl"
chmod +x ./kubectl
sudo mv ./kubectl /usr/local/bin/kubectl

查看kubectl版本信息

kubectl version

3. 通过 faas-nets 部署 OpenFaaS

git clone https://github.com/openfaas/faas-netes
cd faas-netes
kubectl apply -f namespaces.yml
kubectl apply -f ./yaml/

设置base-auth用户名(admin) 和密码,这个密码后边有用。如果为了简单测试,也可以将其设置为简单易记的短字符串。

export PASSWORD=$(head -c 12 /dev/urandom | shasum| cut -d' ' -f1)
echo $PASSWORD
kubectl -n openfaas create secret generic basic-auth --from-literal=basic-auth-user=admin --from-literal=basic-auth-password=$PASSWORD

4. 拉起并展示minikube的控制面板

启动minikube控制面板、获得访问地址。注意,启动之后不能 Ctrl + C 结束掉,不然HTTP服务就关掉了。

➜  ~ minikube dashboard --url
🤔  正在验证 dashboard 运行情况 ...
🚀  Launching proxy ...
🤔  正在验证 proxy 运行状况 ...
http://127.0.0.1:51502/api/v1/namespaces/kubernetes-dashboard/services/http:kubernetes-dashboard:/proxy/

点击最后的地址即可访问,minikube面板如下(选择了命名空间 openfaas):

5. 拉起并展示OpenFaaS的控制面板

查询OpenFaaS控制面板地址,这里涉及到minikube获得服务访问地址的方法:

➜  ~ minikube service gateway-external --url -n openfaas
🏃  Starting tunnel for service gateway-external.
|-----------|------------------|-------------|------------------------|
| NAMESPACE |       NAME       | TARGET PORT |          URL           |
|-----------|------------------|-------------|------------------------|
| openfaas  | gateway-external |             | http://127.0.0.1:53156 |
|-----------|------------------|-------------|------------------------|
http://127.0.0.1:53156
❗  Because you are using a Docker driver on darwin, the terminal needs to be open to run it.

点开最后的地址即可访问,用户名和密码就是在 3. 通过 faas-nets 部署 OpenFaaS 小节中设置的admin和$PASSWORD。OpenFaaS面板如下:

页面提示中可以看到,当前没有任何的函数部署。如果要部署新的函数,有两种方式:一种是通过页面进行操作,就是点击页面上的"Deploy New Function"按钮,一种是通过faas-cli进行部署。

6. 通过页面部署函数

点击 按钮,进入选择常用函数页面,也可以选择自定义的函数部署,不过要事先创建好镜像。这里选择常用函数shasum来部署(点选shasum- 点击右下角DEPLOY即可):

调用测试

部署完成之后,在页面左侧会出现一个"shasum"的函数,点击进入函数信息页面。其中包括对应的镜像地址、函数状态、函数URL、函数的进程

7. 通过faas-cli部署函数

7.1 安装faas-cli

brew install faas-cli

Linux上可以通过下边的方式安装:

curl -sSL https://cli.openfaas.com | sh

7.2 查询OpenFaaS提供的模板

faas-cli template store list

7.3 创建函数

根据文档指引,创建一个Python3的简单函数:

mkdir fn && cd fn
faas-cli new pycon --lang python3

得到的主要文件

pycon.yml
pycon/handler.py
pycon/requirements.txt

7.4 构建函数镜像

有很多构建选项,可以参考文档。这里简单的进行构建:

faas-cli build -f pycon.yml

7.5 登入

在之前的3. 通过 faas-nets 部署 OpenFaaS小节中有提到过的密码,同时需要指定网关地址,即#拉起并展示OpenFaaS的控制面板小节中提到的链接。

faas-cli login --password 9013a977f4ded57a72f395f03dff3218df254583 --gateway http://127.0.0.1:53156

其实如果不想记住这个忘关地址,可以每次去动态获取也可以:

faas-cli login --password 9013a977f4ded57a72f395f03dff3218df254583 --gateway $(minikube service gateway-external --url)

7.6 推送镜像(使用Minikube才有的问题)

在OpenFaaS ISSUE #135 中有提到minikube情况有点特殊,不能直接使用docker中存在的镜像。一定需要取从DockerHub或本地的Registry才行。

minikube is a special case - you will probably have to push to a registry for that - either the Docker Hub or a local registry - see also the Minkube registry add-ons.

不然到话,部署到时候会有如下的错误,提示拉取不到镜像:

Failed to pull image “pycon:latest”: rpc error: code = Unknown desc = Error response from daemon: pull access denied for pycon, repository does not exist or may require ‘docker login’: denied: requested access to the resource is denied

不使用Docker Hub的本地推送方法看上去有些麻烦,所以我直接登录并推送镜像到Docker Hub了。

直接docker push不行:

docker image push pycon:latest

The push refers to repository [docker.io/library/pycon]

denied: requested access to the resource is denied

原因是第一行显示的那样,他会认为要将那个镜像直接推送到docker.io/library/pycon而不是你的个人目录。可以通过打一个tag(简单的认为是对那个镜像加了个软链一样)来指向自己的仓库,然后再进行推送:

docker login
docker tag pycon panzhongxian/pycon
docker image push panzhongxian/pycon:latest

最后将pycon.yml文件做一下修改:

image: pycon:latest
# 修改为⬇️
image: panzhongxian/pycon:latest

7.7 部署函数

同样需要指定网关:

faas-cli deploy -f pycon.yml --gateway http://127.0.0.1:53156

成功后会列出函数的访问地址:

Deploying: pycon.

Deployed. 202 Accepted.
URL: http://127.0.0.1:53156/function/pycon

查看是否成功部署了pod

kubectl get pods -n openfaas-fn

返回我们通过页面部署的一个pod和另外一个通过faas-cli部署的pod

NAME READY STATUS RESTARTS AGE pycon-76f6d9876b-t8zs9 1/1 Running 1 3m shasum-67dddcdb6-6jm9t 1/1 Running 1 175m

7.8 测试函数

curl http://127.0.0.1:53156/function/pycon -X POST --data "test"

7.9 修改函数

修改"pycon/handler.py"文件,最初的文件内容为:

def handle(req):
    """handle a request to the function
    Args:
        req (str): request body
    """

    return req

req是HTTP POST的内容。可以对其进行计算之后,然后再返回结果。再根据上述过程的部署即可。

8. 清理OpenFaaS部署

当我们测试完之后,需要清理对应的进程、资源。具体步骤如下:

清理账号密码

kubectl delete secret basic-auth -n openfaas

进入目录faas-netes,清理资源文件

kubectl delete -f ./yaml

清理函数命名空间 openfaas-fn

kubectl delete namespace openfaas-fn

清理框架命名空间 openfaas

kubectl delete namespace openfaas

五、附录

K8s的一些常用操作

之前没有利用K8s进行容器编排的经验,这里记录一下在部署过程中的一些技巧。

JSONPath 支持

Kubectl 使用 JSONPath 表达式来过滤 JSON 对象中的特定字段并格式化输出。本节列出一些查询的常用操作,其他详细参数可以参考下边链接:

https://kubernetes.io/zh/docs/reference/kubectl/jsonpath/

显示所有pod中的容器名、镜像名

kubectl get pods --all-namespaces -o=jsonpath='{range .items[*]}{"\n"}{.metadata.name}{":\t"}{range .spec.containers[*]}{.name}{", "}{.image}{", "}{end}{end}'

登录运行中的容器和镜像调试

参考使用容器 exec 进行调试,主要的用法是:

kubectl exec ${POD_NAME} -c ${CONTAINER_NAME} -- ${CMD} ${ARG1} ${ARG2} ... ${ARGN}

也就是上述命令让运行的容器运行指定的指令:

kubectl exec -n openfaas-fn shasum-67dddcdb6-6jm9t -c shasum -- netstat -alnp

如果要通过终端进入到容器的shell,需要加上-t -i或者长指令--stdin --tty

# 以下任意一种方式都可以。
kubectl exec --stdin --tty -n openfaas-fn shasum-67dddcdb6-6jm9t -c shasum -- /bin/sh
kubectl exec -it -n openfaas-fn shasum-67dddcdb6-6jm9t -c shasum -- /bin/sh

TODO