译者注:
本文篇幅较长,有助于理解 FaaS 和 OpenFaaS。作者别离从开发人员和运维人员的视角来理解 OpenFaaS,对理解新的技术是个很好的形式。

本文翻译自 Ivan Velichko 的 OpenFaaS - Run Containerized Functions On Your Own Terms。


长期以来,无服务器(serverless) 对我来说无非就是 AWS Lambda 的代名词。Lambda 提供了一种不便的路径,能够将任意代码附加到平台事件(云实例的状态变更、DynamoDB 记录的更新或新的 SNS 音讯)中。然而,我时不时会想到某个逻辑,但其又没大到足以有本人的服务,同时有不适宜任何现有服务的范畴。因而,我常常将其放入函数中,以便日后应用 CLI 命令或者 HTTP 调用来调用它。

几年前,我来开了 AWS,自那当前,我始终思念部署无服务器性能的便利性。因而,当我得悉 OpenFaaS
我的项目时惊喜万分。它将在 Kubernetes 集群上部署函数变得简略,甚至仅须要 Containerd 就能够部署到虚拟机上。

有趣味?那么持续!

无服务器与 FaaS

无服务器 已成为一个风行词,目前其理论含意扔不够清晰。

许多古代平台被视为 无服务器 平台。在 AWS Fargate 或 GCP Cloud Run 上部署容器化服务?无服务器!在 Heroku 上运行应用程序?也可能是无服务器的。

同时,我更喜爱将 FaaS 视为一种具体的设计模式。依照 FaaS 范式,能够部署代码片段(响应某些内部工夫执行的函数)。这些函数与事件驱动程序中的回调相似,然而是运行在其他人的的服务器上。因为操作的是函数而不是服务器,顾名思义 FaaS 是无服务器的。


source

OpenFaaS 我的项目旨在将 Kubernetes 集群或者独立的虚拟机等低级基础设施转化为治理无服务器函数的高级平台。

站在开发人员的角度,这样一个平台看起来是真的无服务器的 -- 你只须要晓得特定的 CLI/UI/API 来解决 函数 形象。但站在运维的角度,须要理解 OpenFaaS 如何应用 服务器 来运行这些函数。

就我而言,我常常既是开发又是运维,上面我将尝试从二者开展阐明。然而,我认为在评估 UX 时,咱们应该明确辨别它们。

开发人员眼中的 OpenFaaS

OpenFaaS 创立于 2016 年,当初网上也有大量的教程。这里不会反复介绍,但能够通过以下链接理解:

  • How to deploy OpenFaaS
  • Create Functions
  • Build Functions
  • Writing a Node.js function - step-by-step guide

相同,我将形容我所了解的 OpenFaaS。我心愿有助于一些须要评估该技术是否解决其问题的人,以及那些心愿更无效地应用该技术的人。

函数运行时

在进入正式编码之前,有必要理解下其将来的执行环境(又名运行时)。或者,简略说:

  • 如何启动函数
  • 如何组织 I/O 操作
  • 如何重置 / 终止函数
  • 如何隔离函数和调用

OpenFaaS 自带多个运行时模式,这些模式针对不同的场景定制。因而,不同的场景下上述问题的答案会略有不同。

OpenFaaS 函数在容器中运行,并且每个容器必须恪守简略的约定 :它作为监听在预设端口(默认为 8080)上的 HTTP 服务器,长期存储并且是无状态的。

然而,OpenFaaS 通过 函数 watchdog(译者注:watchdog 不做翻译)模式防止了用户编写此类服务器。函数 watchdog 是一种轻量级 HTTP 服务器,能够感知如何执行理论函数业务逻辑。因而,装置在容器中的所有内容加上作为入口点的 watchdog,就形成了函数的运行时环境。

经典 watchdog

最简略的开始,或者又是被称为经典 watchdog:

这种模式下,watchdog 启动了监听在 8080 端口的轻量级 HTTP 服务器,每个进来的申请都会:

  • 读取申请头和申请体
  • fork 或者 exec 蕴含理论函数的可执行文件
  • 将申请头和申请体写入到函数过程的 stdin
  • 期待函数过程的退出(或者超市)
  • 读取函数过程的 stdoutstderr
  • 在 HTTP 响应中将去读的字节发送回调用方

上述逻辑相似于传统的 通用网关接口(CGI)。一方面,每次函数调用都启动独自的过程看起来不够高效,而另一方面,它的确超级不便,因为 任何应用 stdio 流进行 I/O 解决的程序(包含最喜爱的 CLI 工具)都能够部署为 OpenFaaS 函数

提起隔离,咱们有必要辨别下函数调用

  • OpenFaaS 中的不同函数始终散布在不同的容器中
  • 一个函数能够有一个或多个容器 —— 取决于缩放选项
  • 同一函数的独立调用可能会最终进入同一个容器
  • 同一函数的独立调用将始终应用不同的过程进行

反向代理 watchdog

留神:应用 OpenFaaS 官网术语,本节探讨在 HTTP 模式下运行的 of-watchdog。但我集体认为称之为反向代理 watchdog 更加形象。

如果 经典 运行时相似于 CGI,那么这个运行时模式相似于起初的 FastCGI。运行时心愿在 watchdog 前面有一个长期运行的 HTTP 服务器,而不是每次函数调用时创立新的过程。这实质上是 将 watchdog 组件变成反向代理:

当容器启动时,反向代理 watchdog 也会创立一个监听在 8080 端口的轻量级 HTTP 服务器。然而,与 经典 watchdog 不同的是反向代理watchdog 只创立一次函数的过程,并将其当成(长期运行的)上游服务器。而后,函数调用转变成到该上游的 HTTP 申请。

然而,反向代理模式并不为了取代经典模式。经典模式的强项在于其函数的编写非常简单。这也是没有 HTTP 服务器的代码的惟一抉择。比方应用 Cobol、bash 或者 PowerShell 脚本等等编写的函数。

何时该应用反向代理运行时模式:

  • 函数须要在两次调用之间放弃状态:

    • 缓存
    • 长久连贯(例如,放弃从函数到数据库的连贯关上)
    • 有状态函数
  • 每个函数启动一个过程可能开销很大,为每个调用带来了提早
  • 你想运行一个(微)服务作为函数
依据 OpenFaaS 的创建者 Alex Ellis 的解释,_FaaS_,特地是 OpenFaaS,能够被视为在不依赖服务器形象的状况下 部署微服务的简化形式。即 FaaS 是无服务器架构的标准示例。

因而,应用反向代理的形式,函数能够被看作是部署微服务的执著的形式。不便、疾速、简略。但应用有状态函数时,要注意因为多个调用可能在同一个过程中完结而导致的正告:

  • 在一个过程中完结的并发调用可能会触发代码中的竞争条件(例如,一个带有全局变量的 Go 函数,而全局变量没有锁的爱护)。
  • 在一个过程中完结的后续调用可能会导致穿插调用数据泄露(当然,就像传统微服务一样)。
  • 因为该过程在两次调用之间被复用,因而代码中的任何内存透露都不会被缓解。

其余运行时模式

经典运行时模式在将函数后果发送回调用方之前缓冲了函数的整个响应。但如果响应的大小超出了容器的内存怎么办?OpenFaaS 提供了另一种运行时模式,该模式依然为每个调用创立过程,但增加了响应流

另一个又去的场景是从函数中提供动态文件服务。OpenFaaS 也有解决方案。

这可能是所有的内置运行时模式。但如果仍未满足需要,OpenFaaS 是一个开源我的项目!看下现有的watchdog(1 & 2),简洁明了。因而,能够随时提交 PR 或者 issue,让整个社区因你的奉献收益。

编写函数

此时,咱们曾经理解函数如何在装备了函数 watchdog 的容器中运行。那么最小的函数是什么样子的?

上面的示例将简略的 shell 脚本封装到 OpenFaaS 函数中:

######################################################### WARNING: Not for Production - No Security Hardening! ########################################################## This FROM is just to get the watchdog executable.FROM ghcr.io/openfaas/classic-watchdog:0.2.0 as watchdog# FROM this line the actual runtime definion starts.FROM alpine:latest# Mandatory step - put the watchdog.COPY --from=watchdog /fwatchdog /usr/bin/fwatchdog# Optionally - install extra packages, libs, tools, etc.# Function's payload - script echoing its STDIN, a bit transformed.RUN echo '#!/bin/sh' > /echo.shRUN echo 'cat | rev | tr "[:lower:]" "[:upper:]"' >> /echo.shRUN chmod +x /echo.sh# Point the watchdog to the actual thingy to run.ENV fprocess="/echo.sh"# Start the watchdog server.CMD ["fwatchdog"]

当构建、部署和调用时,下面的函数作为 回显服务器,倒转并大写其输出。

略微高级点的例子:一个 Node.js Hello World 脚本作为函数:

######################################################### WARNING: Not for Production - No Security Hardening! #########################################################FROM ghcr.io/openfaas/classic-watchdog:0.2.0 as watchdogFROM node:17-alpineCOPY --from=watchdog /fwatchdog /usr/bin/fwatchdogRUN echo 'console.log("Hello World!")' > index.jsENV fprocess="node index.js"CMD ["fwatchdog"]

因而,要编写一个简略的函数,只须要在 Dockerfile 中退出:

  • 理论脚本(或可执行文件)
  • 它的所有依赖项——软件包、操作系统库等
  • 首选的 watchdog

而后将 watchdog 指向该脚本(或可执行文件),并将 watchdog 作为入口。有点酷,因为:

  • 能够齐全管制函数将来的运行时
  • 能够部署任何能够在容器中作为函数运行的货色

但上述办法有个显著的毛病 -- 一个生产就绪的 Dockerfile 可能有上百行。如果我只想运行一个简略的 Node.js/Python 脚本或者一个小的 Go 程序作为函数,要怎么解决 Dockerfile?就不能有一个占位符来粘贴代码片段?

性能模板

OpenFaaS 的美好之处在于,咱们能够两者兼而有之 —— 应用 Dockerfile 进行低级修补或目标语言编写高级脚本!得益于丰盛的性能模板库!

$ faas-cli template store listNAME                     SOURCE             DESCRIPTIONcsharp                   openfaas           Classic C# templatedockerfile               openfaas           Classic Dockerfile templatego                       openfaas           Classic Golang templatejava8                    openfaas           Java 8 template...node14                   openfaas           HTTP-based Node 14 templatenode12                   openfaas           HTTP-based Node 12 templatenode                     openfaas           Classic NodeJS 8 templatephp7                     openfaas           Classic PHP 7 templatepython                   openfaas           Classic Python 2.7 templatepython3                  openfaas           Classic Python 3.6 template...python3-flask            openfaas           Python 3.7 Flask templatepython3-http             openfaas           Python 3.7 with Flask and HTTP...golang-http              openfaas           Golang HTTP template...

上述性能模板由 OpenFaaS 作者和社区精心只做。典型的模板附带一个简单的 Dockerfile,指向虚构处理程序函数。当疏导新函数时,通过 faas-cli new 命令来应用这些模板。例如:

$ faas-cli new --lang python my-fnFolder: my-fn created.Function created in folder: my-fnStack file written: my-fn.yml$ cat my-fn/handler.pydef handle(req):    """ PUT YOUR BUSINESS LOGIC HERE """    return req

因而,对于模板,编写函数的工作能够归结为简略地将业务逻辑放入响应的处理程序文件中。

应用模板时,理解应用那种 watchdog模式 很重要:

  • 应用经典的相似 CGI 的 watchdog,处理程序通常被编写为承受和返回纯字符串的函数(例如:python3、php7)
  • HTTP 模式下,应用 of-watchdog时,处理程序看起来更像 HTTP 处理程序承受申请并返回响应构造(例如:python3-http,node17)。

函数商店

你的最佳函数是什么?对,你不须要写。OpenFaaS 承受这种想法,并带来了函数商店(通过社区测试并依据过往教训抉择的 OpenFaaS 函数精选索引)。

该商店蕴含一些乏味的函数,能够一键部署到现有的 OpenFaaS 中:

$ faas-cli store listFUNCTION                        DESCRIPTIONNodeInfo                        Get info about the machine that you'r...alpine                          An Alpine Linux shell, set the "fproc...env                             Print the environment variables prese...sleep                           Simulate a 2s duration or pass an X-S...shasum                          Generate a shasum for the given inputFiglet                          Generate ASCII logos with the figlet CLIcurl                            Use curl for network diagnostics, pas...SentimentAnalysis               Python function provides a rating on ...hey                             HTTP load generator, ApacheBench (ab)...nslookup                        Query the nameserver for the IP addre...SSL/TLS cert info               Returns SSL/TLS certificate informati...Colorization                    Turn black and white photos to color ...Inception                       This is a forked version of the work ...Have I Been Pwned               The Have I Been Pwned function lets y...Face Detection with Pigo        Detect faces in images using the Pigo...Tesseract OCR                   This function brings OCR - Optical Ch...Dockerhub Stats                 Golang function gives the count of re...QR Code Generator - Go          QR Code generator using GoNmap Security Scanner           Tool for network discovery and securi...ASCII Cows                      Generate a random ASCII cowYouTube Video Downloader        Download YouTube videos as a functionOpenFaaS Text-to-Speech         Generate an MP3 of text using Google'...Docker Image Manifest Query     Query an image on the Docker Hub for ...face-detect with OpenCV         Detect faces in images. Send a URL as...Face blur by Endre Simo         Blur out faces detected in JPEGs. Inv...Left-Pad                        left-pad on OpenFaaSnormalisecolor                  Automatically fix white-balance in ph...mememachine                     Turn any image into a meme.Business Strategy Generator     Generates a Business Strategy (using ...Image EXIF Reader               Reads EXIF information from an URL or...Open NSFW Model                 Score images for NSFW (nudity) content.Identicon Generator             Create an identicon from a provided s...

这些函数实际上是存储在 Docker Hub 或者 Quay 等公共库的容器镜像,能够自在复用。

场景示例:

  • 应用 env 函数调试函数接管的HTTP标头
  • 应用 curl 函数从 OpenFaaS 部署内部测试连贯
  • 从运行多个正本的函数中应用 hey 来减少负载

函数的构建和部署

因为函数是在容器中运行的,因而须要有人为这些容器构建镜像。无论你喜不喜欢,这都是开发人员的事件。OpenFaaS 提供了不便的 faas-cli build 命令,但没有服务器端构建。因而,要么须要(在装置 Docker 的机器上)手动运行 faas-cli build,要么应用 CI/CD 实现。

接下来,构建好的镜像须要通过 faas-cli push 到仓库。显然,这种仓库也应该能够从 OpenFaaS 服务器端拜访。否则,使 用faas-cli deploy 部署函数时会失败。

开发人员的工作流程如下:

调用函数

函数部署后,能够通过向 $API_HOST:$API_PORT/function/<fn-name> 端点发送 GET、POST、PUT 或者 DELET HTTP 申请来调用它。常见的调用形式有:

  • 各种钩子(webhook)
  • faas-cli invoke
  • event connectors

前两个选项相当简略。应用函数作为作为 webhook 处理器(GitHub、IFTTT 等)很不便,每个函数开发人员都曾经装置了 faas-cli,因而能够成为日常脚本编写的组成部分。

那什么是事件连接器?

在本文结尾是我对 AWS Lambda 与 AWS 平台事件严密集成的和煦回顾。请记住,能够在响应新 SQS/SNS 音讯、新的 Kinesis 记录、EC2 实例生命周期等事件时调用 Lambda。OpenFaaS 函数是否存在相似的货色呢?

显然,OpenFaaS 无奈开箱即用地与任何生态系统集成。然而,它提供了一种名为事件连接器模式的通用解决方案。

官网反对的连接器:

  • Cron connector
  • MQTT connector
  • NATS connector
  • Kafka connector (须要专业版订阅)

OpenFaaS 还提供了很小的连接器-sdk库来简化连接器的开发。

运维眼中的 OpenFaaS

开发眼中的 OpenFaaS 是个黑盒,提供简略的 API 来部署和调用函数。然而,作为运维可能会从理解 一点 OpenFaaS 外部原理中受害。

OpenFaaS 通用架构

OpenFaaS 有一个简略但弱小的架构,容许应用不同的基础设施作为后端。如果曾经有了 Kubernetes 集群,能够通过在下面部署 OpenFaaS 轻松将其变成 FaaS 解决方案。然而如果旧的虚构(或物理)机,依然能够在下面装置 OpenFaaS,并取得差不多性能的更小的 FaaS 解决方案。

下面的架构中惟一面向用户的组件是 API 网关。OpenFaaS 的 API 网关:

  • 裸露 API 来治理和调用函数
  • 提供内置的 UI 来治理函数
  • 处理函数的主动缩放
  • 预计前面会有兼容的 OpenFaaS 提供商

因而,当开发人员运行 faas-cli deployfaas-cli list 或应用 curl $API_URL/function/foobar 调用函数等内容时,申请将发送到上述的 API 网关。

上图中的另一个重要组成部分是  faas-provider。它不是一个具体的组件,而更像是接口。任何实现(十分简洁)的提供商 API 的软件都能够成为提供商。OpenFaaS 提供商:

  • 治理性能(部署、列表、缩放、删除)
  • 调用函数
  • 裸露一些零碎信息

两个最注明的提供商是 faas-netes(Kubernetes 上的 OpenFaaS)和 faasd(Containerd 上的 OpenFaaS)。上面,将介绍他们的实现。

Kubernetes 上的 OpenFaaS(faas-nets)

当部署在 Kubernetes 上时,OpenFaaS 利用了该平台开箱即用的弱小原语。

要害要点:

  • API 网关成为规范(部署+服务)对。因而,能够得心应手地扩大它。也能够得心应手地把它裸露进去
  • 每个函数也成为(部署+服务)对。可能不会间接处理函数,但对于 faas-netes,缩放变得就像调整相应的正本数一样简略
  • 高可用性和开箱即用的程度缩放 - 同一性能的 pod 能够(而且应该)跨多个集群节点运行。
  • Kubernetes 作为一个数据库工作;例如,当运行 faas-cli list 等命令来获取以后部署的函数列表时,faas-netes 只会将其转换为相应的 Kubernetes API 查问

Containerd 上的 OpenFaaS(faasd)

对于没有应用 Kubernetes 集群的人来说,OpenFaaS 提供了名为 faasd 的代替轻量级提供商。它能够装置在(虚构或物理)服务器上,,并利用 containerd 来治理容器。正如我之前写的那样,容器是一个在 Docker 和 Kubernetes 下应用的较低级别的容器管理器。联合 CNI 插件,它成为编写容器调度器的构建组件,OpenFaaS 的 faasd 是个很好的考究案例:

要害要点:

  • 被设计为在 IoT 设施上或 VM 中运行
  • 应用 containerd 的原生 pause (通过cgroup freezers)和超疾速函数冷启动疾速扩大到零
  • 它能够在每台服务器上运行比 faas-netes 多十倍的函数,并且能够无效地应用更便宜的硬件,包含树莓派
  • containerd 和 faasd 作为 systemd 服务进行治理,因而会主动解决日志、重启等
  • 没有 Kubernetes DNS,但 faasd 确保 DNS 在函数之间共享以简化互操作
  • containerd 扮演着数据库的角色(比方 faas-cli list 变成了相似 ctr container list的操作 ),所以如果服务器挂了,所有状态就会失落,每个函数都须要重新部署
  • 没有开箱即用的高可用性或程度扩大(参见 issue/225)

总结

对你所应用的软件有个好的心智模型是是无益的,它能够进步开发效率,避免单例场景的产生,并简化了故障排查。

以 OpenFaaS 为例,辨别开发人员和运维人员对系统的认识可能是个很好的思路。从开发人员角度来看,这是一个简略而弱小的无服务器解决方案,次要关注 FaaS 场景。该解决方案由一个用于治理和调用函数的简洁 API、一个涵盖开发人员工作流的命令行工具以及一个函数模板库组成。无服务器函数以不同的运行时模式(类 CGI、反向代理)在容器中运行,并提供不同的隔离和状态保障。

运维人员的角度来看,OpenFaaS 是一个具备灵便架构的模块化零碎,能够部署在不同类型的基础设施之上:从树莓派到裸机或虚拟机、以及成熟的 Kubernetes、OpenShift 或 Docker Swarm 集群。当然,每种抉择都有其优缺点,须要具体评估取舍。但即便现有选项都不适合,简略的 faas-provider 形象容许开发本人的后端来运行无服务器性能。

上述内容次要集中在 OpenFaaS 基础知识上。然而 OpenFaaS 也有一些高级性能。通过以下链接进一步理解:

  • 应用 NATS 消息传递零碎的异步函数调用
  • 应用 Prometheus 和 AlertManager 主动缩放性能
  • 函数调用限度
  • 应用 docker-compose 通过 faasd 运行有状态的工作负载

资源

  • OpenFaaS 官网文档
  • 一堆清晰的 OpenFaaS 用例
  • 应用 OpenFaaS 将任何 CLI 转换为函数
  • faasd 介绍、动机、次要用例
  • 面向其他人的无服务器- 只管有通用名称,但它是 faasd 的一个十分具体的指南

作者介绍:
Ivan Velichko
Software Engineer at heart, SRE at day job, Tech Storyteller at night.
文章对立公布在公众号云原生指北