夏日清风 - 基于Docker Swarm的极简Serverless实践

    xiaoxiao2022-07-16  146

    在今年4月份的DockerCon压轴的 Moby's Cool Hack Session上,Alex Ellis给大家展现了一个名为Function as a Service (FaaS)项目。FaaS基于Docker Swarm集群上实现了一个极简的Serverless框架,支持将任意Unix进程作为函数实现来对外提供服务。

    FaaS 架构

    在 FaaS 原型系统中

    任何进程都可以转化成为一个函数,并利用Docker镜像进行打包和交付利用 Docker Swarm 集群的资源调度和routing mesh的负载均衡能力简洁地实现了函数的调度能力。其中每个函数对应一个Docker集群中的服务基于 Prometheus 实现函数调用监控和自动伸缩

    其设计架构非常简单,其中

    API Gateway 负责接受服务调用,路由请求到后端函数实现,并采集服务调用的指标发送给 Prometheus。 Prometheus 则会根据一段时间内服务调用的次数,回调API Gateway 来动态伸缩服务容器实例数量。Function Watchdog 将HTTP请求转发为进程调用,并将请求数据通过 STDIN 传递给进程,而将进程的 STDOUT 作为 HTTP 响应的结果返回给调用者。将函数进程和Function Watchdog打包成一个容器镜像进行部署。其调用流程如下:

    FaaS 在本地部署非常简单

    首先你需要准备一个本地的Docker Swarm集群,如果没有,可以安装最新Docker Engine并执行下面命令:

    docker swarm init

    执行如下命令来部署FaaS

    git clone https://github.com/alexellis/faas cd faas ./deploy_stack.sh

    在部署完成之后,我们可以通过如下命令检查FaaS的状态

    $ docker stack services func ID NAME MODE REPLICAS IMAGE 1a8b2tb19ulk func_gateway replicated 1/1 functions/gateway:0.5.6 4jdexem6kppg func_webhookstash replicated 1/1 functions/webhookstash:latest 9ju4er5jur9l func_wordcount replicated 1/1 functions/alpine:health e190suippx7i func_markdown replicated 1/1 alexellis2/faas-markdownrender:latest l70j4c7kf99t func_alertmanager replicated 1/1 functions/alertmanager:latest mgujgoa2u8f3 func_decodebase64 replicated 1/1 functions/alpine:health o44asbnhqbda func_hubstats replicated 1/1 alexellis2/faas-dockerhubstats:latest q8rx49ow3may func_echoit replicated 1/1 functions/alpine:health t1ao5psnsj0s func_base64 replicated 1/1 functions/alpine:health vj5z7rpdlo48 func_prometheus replicated 1/1 functions/prometheus:latest xmwzd4z7l4dv func_nodeinfo replicated 1/1 functions/nodeinfo:latest

    随后通过浏览器来访问 http://127.0.0.1:8080/ui FaaS

    整个流程非常简单,就像夏日的清风,让人感到自然愉悦。

    在阿里云上测试FaaS

    由于FaaS是基于Docker Swarm mode集群进行部署的,你首先需要在阿里云容器服务创建一个Swarm mode集群

    然后利用如下模板来部署应用

    version: "3" services: # Core API services are pinned, HA is provided for functions. gateway: volumes: - "/var/run/docker.sock:/var/run/docker.sock" ports: - 8080:8080 labels: aliyun.routing.port_8080: faas image: functions/gateway:0.5.6 networks: - functions environment: dnsrr: "true" # Temporarily use dnsrr in place of VIP while issue persists on PWD deploy: placement: constraints: [node.role == manager] prometheus: image: functions/prometheus:latest # autobuild from Dockerfile in repo. command: "-config.file=/etc/prometheus/prometheus.yml -storage.local.path=/prometheus -storage.local.memory-chunks=10000 --alertmanager.url=http://alertmanager:9093" ports: - 9090:9090 depends_on: - gateway - alertmanager labels: aliyun.routing.port_9090: prometheus environment: no_proxy: "gateway" networks: - functions deploy: placement: constraints: [node.role == manager] alertmanager: image: functions/alertmanager:latest # autobuild from Dockerfile in repo. environment: no_proxy: "gateway" command: - '-config.file=/alertmanager.yml' networks: - functions ports: - 9093:9093 deploy: placement: constraints: [node.role == manager] # Sample functions go here. # Service label of "function" allows functions to show up in UI on http://gateway:8080/ webhookstash: image: functions/webhookstash:latest labels: function: "true" depends_on: - gateway networks: - functions environment: no_proxy: "gateway" https_proxy: $https_proxy # Pass a username as an argument to find how many images user has pushed to Docker Hub. hubstats: image: alexellis2/faas-dockerhubstats:latest labels: function: "true" depends_on: - gateway networks: - functions environment: no_proxy: "gateway" https_proxy: $https_proxy # Node.js gives OS info about the node (Host) nodeinfo: image: functions/nodeinfo:latest labels: function: "true" depends_on: - gateway networks: - functions environment: no_proxy: "gateway" https_proxy: $https_proxy # Uses `cat` to echo back response, fastest function to execute. echoit: image: functions/alpine:health labels: function: "true" depends_on: - gateway networks: - functions environment: fprocess: "cat" no_proxy: "gateway" https_proxy: $https_proxy # Counts words in request with `wc` utility wordcount: image: functions/alpine:health labels: function: "true" com.faas.max_replicas: "10" depends_on: - gateway networks: - functions environment: fprocess: "wc" no_proxy: "gateway" https_proxy: $https_proxy # Calculates base64 representation of request body. base64: image: functions/alpine:health labels: function: "true" depends_on: - gateway networks: - functions environment: fprocess: "base64" no_proxy: "gateway" https_proxy: $https_proxy # Decodes base64 representation of request body. decodebase64: image: functions/alpine:health labels: function: "true" depends_on: - gateway networks: - functions environment: fprocess: "base64 -d" no_proxy: "gateway" https_proxy: $https_proxy # Converts body in (markdown format) -> (html) markdown: image: alexellis2/faas-markdownrender:latest labels: function: "true" depends_on: - gateway networks: - functions environment: no_proxy: "gateway" https_proxy: $https_proxy networks: functions: driver: overlay

    和本地部署相比只是增加了两个 label,定义了API Gatway和Prometheus的路由

    "aliyun.routing.port_8080: faas" : API Gatway的虚拟域名"aliyun.routing.port_9090: prometheus" : prometheus服务的虚拟域名

    然后基于上面模板创建应用,

    注:这里的应用名为 “faas_default”,部署完成之后所有函数服务和访问的名空间都基于这个名称。

    部署完成之后,我们可以看见相应的服务列表

    选择路由列表标签,我们可以看到之前定义的路由地址已经出现在列表中

    可以点击连接上面连接访问FaaS的API Gateway和Prometheus服务界面

    下面我们来进行一个测试来验证服务的伸缩性,首先,我们参照文档将Prometheus的URL修改为

    http://<prometheus-endpoint>/graph?g0.range_input=15m&g0.expr=gateway_service_count&g0.tab=0&g1.range_input=15m&g1.expr=rate(gateway_function_invocation_total[20s])&g1.tab=0&g2.range_input=15m&g2.expr=gateway_functions_seconds_sum+/+gateway_functions_seconds_count&g2.tab=0

    注:其中URL的prometheus-endpoint需要替换为上文中端点地址

    然后在本地运行如下命令

    while [ true ] ; do curl -X POST http://<faas-endpoint>/function/faas-default_echoit -d 'Hello, Function as a Service'; done

    注:命令中路径需要替换faas-endpoint,如果服务名称与faas-default_echoit不同,也请自行调整。

    在Prometheus界面可以看到服务调用量的变化

    在容器服务的应用服务列表界面,可以看到,faas-default_echoit的容器实例从1个扩容到20个。

    当结束测试之后,服务实例也会缩容到一个

    如果对创建自己的函数感兴趣,可以参考 Alex 的博客,本文不再赘述

    https://blog.alexellis.io/build-and-deploy-with-faas/

    总结

    FaaS基于Docker Swarm集群技术,实现了一个极简的Serverless框架,支持将任意Unix进程作为函数来对外提供服务。FaaS目前还是一个原型系统,如果大家需要完备的Function as a Service能力,体验无服务器运维,还是建议采用阿里云 FunctionCompute服务。

    FaaS框架展示了一些有趣的可能性

    将现有程序封装成为函数,可以作为服务方便地集成到应用业务逻辑中。将函数计算和现有微服务架构应用有机结合在一起。基于Docker和Swarm集群技术,部署运维非常简单。可以在任何地方部署,甚至是ARM设备上。下图来自RICHARD GEE,演示了在树莓派集群上运行FaaS。

    最新回复(0)