DigitalOcean Kubernetes + OpenFaaS 轻量级 Serverless 实战指南
1. 项目概述:为什么在 DigitalOcean Kubernetes 上跑 OpenFaaS 是个务实选择
你有没有遇到过这种场景:一个轻量级 API 接口,每天只被调用几十次,但为了它单独维护一台云服务器,光是基础运维(系统更新、安全补丁、日志轮转、资源监控)就占掉你两小时;或者团队刚上线一个内部工具,用户增长不可预测,你既不敢贸然扩容虚拟机,又怕突发流量把服务打挂——这时候,Serverless 不是玄学概念,而是能立刻帮你省下 60% 云账单、减少 80% 运维负担的实操方案。而 OpenFaaS,就是那个不依赖 AWS Lambda 或 Azure Functions 生态、完全开源、可自主掌控、且对 Kubernetes 友好到近乎“原生”的 FaaS 框架。它不像 Knative 那样动辄需要十几个 CRD 和复杂网络策略,也不像 Kubeless 那样社区活跃度逐年下滑;OpenFaaS 的核心设计哲学就一条:让开发者用最熟悉的方式写函数(HTTP handler),用最简单的命令部署(faas-cli deploy),用最直观的 UI 管理(UI Dashboard),所有底层调度、自动扩缩、冷启动优化都由 Operator 和 Gateway 统一兜底。
DigitalOcean Kubernetes(简称 DOKS)则是这个组合里最被低估的“黄金搭档”。它不是功能最全的托管 K8s(比如 GKE 或 EKS),但它是目前公有云中对中小团队最友好的:控制台极简、文档直白、集群创建 90 秒内完成、节点价格透明($0.03/小时起)、内置负载均衡器和块存储开箱即用,最关键的是——它默认启用 RBAC、NetworkPolicy 和 PodSecurityPolicy(v1.25+ 后为 PodSecurityAdmission),这意味着你不用花三天时间手动配置 ClusterRoleBinding 和 SecurityContextConstraints 就能跑起 OpenFaaS。我去年帮一家做跨境电商 SaaS 的客户迁移时,他们原有 3 台 Ubuntu 22.04 的 Droplet 分别跑着订单通知、库存同步、邮件模板渲染三个微服务,每月固定支出 $126;换成 DOKS + OpenFaaS 后,用 1 台 $20/月的 Standard Droplet 作为 control plane + 2 台 $10/月的 worker node,函数按需执行、空闲时自动缩容至零副本,月均成本压到 $48,且再也不用半夜被 Prometheus 告警叫醒处理磁盘满或 OOMKilled。这不是理论推演,是真实跑在生产环境里的数字。所以如果你正在看 “kubernetes菜鸟教程” 或 “ubuntu 22.04 安装kubernetes”,请先停一下——DOKS 就是你跳过 kubeadm、kubekey、etcd 手动部署这些“硬核入门课”的捷径;而 OpenFaaS,则是你把 Kubernetes 从“运维基础设施”真正变成“开发生产力平台”的第一块跳板。
2. 整体架构设计与选型逻辑:为什么不是 Knative、不是 AWS Lambda、也不是裸写 Deployment
2.1 OpenFaaS vs 其他 FaaS 方案:轻量、可控、无厂商锁定
很多人看到 Serverless 第一反应是“上云厂商服务”,但实际落地时会发现几个硬伤:AWS Lambda 的最大执行时间 15 分钟,对需要跑 20 分钟数据清洗的任务直接判死刑;冷启动延迟在毫秒级看似快,但首次调用平均 800ms+,对实时性要求高的前端接口体验打折;更关键的是,调试极其痛苦——你没法 ssh 进去查 /proc/pid/environ,没法用 strace 跟踪系统调用,日志只能靠 CloudWatch Logs 拼接,出问题时就像在黑盒里摸大象。OpenFaaS 完全规避了这些:函数以标准 Docker 容器运行在你自己的 Kubernetes 集群里,你可以 exec 进容器、用 top 查 CPU、用 df -h 看磁盘、用 curl 测试内部服务连通性,所有操作和调试方式和你日常运维普通 Pod 完全一致。它的 Operator 模式也决定了扩展性极强——我们曾在一个 12 节点的 DOKS 集群上同时运行 217 个函数,其中 3 个是 Python + Pandas 的 CPU 密集型任务,通过设置 requests.cpu=2000m 和 limits.cpu=3000m,配合 HorizontalPodAutoscaler 基于 CPU 使用率自动伸缩,峰值 QPS 达到 1800 时仍保持 P95 延迟 < 320ms。这背后没有魔法,只有 Kubernetes 原生能力的扎实复用。
再对比 Knative:它确实功能强大,支持事件驱动(Eventing)、服务网格集成、灰度发布等高级特性,但代价是复杂度指数级上升。一个最小可用的 Knative Serving 部署,需要至少 8 个独立命名空间(knative-serving、knative-eventing、istio-system 等),CRD 数量超 40 个,Operator 自身就有 3 个不同版本(Knative Operator、Kourier、Contour)。我们曾为某金融客户评估过,仅完成 Knative 的 TLS 证书自动续期(Let’s Encrypt + cert-manager 集成)就花了 3 人日,期间踩了 Istio mTLS 与 Knative Revision 网络策略冲突、cert-manager webhook 无法访问 Knative internal service 等 7 个坑。而 OpenFaaS 的安装,一行命令 faas-netes 插件 + helm install 即可完成,Dashboard 默认启用 Basic Auth,所有函数状态、日志、调用统计都在一个页面呈现,新来的实习生半小时就能上手部署第一个函数。这不是功能多寡的问题,而是“交付价值速度”的本质差异。
2.2 DigitalOcean Kubernetes 的不可替代性:省掉 70% 的集群治理成本
为什么不是自己用 kubeadm 在三台 Ubuntu 22.04 机器上搭集群?坦白说,可以,但不值得。我亲手搭过 5 次:第一次花 14 小时搞定 etcd 备份恢复;第二次卡在 Calico CNI 的 IP-in-IP 模式与本地防火墙冲突;第三次因为 kubelet 版本和 containerd 不兼容导致节点 NotReady;第四次升级 Kubernetes 1.24 时发现 dockershim 已移除,连夜重配 containerd;第五次……算了,第五次我直接买了 DOKS。DOKS 的核心优势在于它把 Kubernetes 最反人类的部分全部封装掉了:
- 节点生命周期管理 :你不需要关心 kubelet 是否健康、containerd 是否崩溃、节点磁盘是否满了——DOKS 的 Node Health Check 每 30 秒探测一次,异常节点自动隔离并触发新节点替换,整个过程对工作负载无感知;
- 控制平面高可用 :免费提供 3 副本 etcd + API Server,无需你手动配置 keepalived + haproxy 做 VIP;
- 网络策略即开即用 :创建集群时勾选 “Enable NetworkPolicies”,所有命名空间默认拒绝所有入站流量,你只需为 openfaas 命名空间添加一条允许 8080 端口的 Ingress 规则即可,比手动写 calicoctl 命令友好十倍;
- 存储动态供给 :DOKS 内置 DO Block Storage CSI Driver,OpenFaaS 的 Prometheus 监控数据、函数构建缓存、甚至临时文件系统(tmpfs)都能直接绑定 PVC,不用再折腾 NFS 或 Longhorn。
特别要提的是 DOKS 对 Ubuntu 22.04 的原生支持。很多教程还在教你怎么在 Ubuntu 20.04 上降级 containerd 版本以兼容旧版 Kubernetes,而 DOKS 1.28+ 集群默认使用 Ubuntu 22.04 LTS 镜像,内核 5.15,cgroup v2 全启用,systemd 作为 init 系统,这意味着你写的任何基于 systemd 的健康检查脚本(比如检测 /var/run/openfaas.lock 文件是否存在)、任何需要 cgroup v2 特性的 Go 程序(如使用 memory.max 控制内存上限),都能原生运行,不用加 --cgroup-driver=systemd 这类 hack 参数。
2.3 faas-cli 的设计哲学:让 CLI 成为开发者的第二大脑
faas-cli 不是简单包装 kubectl 的外壳,它是一套完整的函数生命周期管理协议。它的核心命令只有 5 个:
-
faas-cli build:根据 stack.yml 构建函数镜像,自动识别语言模板(Python、Node.js、Go、Rust 等),注入 OpenFaaS 标准入口(handler); -
faas-cli push:推送镜像到你指定的 registry(Docker Hub、DigitalOcean Container Registry、自建 Harbor); -
faas-cli deploy:生成 Kubernetes Deployment + Service + Ingress(如果启用)YAML,并 apply 到集群; -
faas-cli invoke:直接调用函数,支持 stdin 输入、header 设置、超时控制; -
faas-cli list:列出所有函数,显示当前副本数、最近调用次数、平均延迟。
这个设计的精妙之处在于“约定优于配置”。比如你新建一个 Python 函数,目录结构必须是:
my-function/
├── handler.py # 必须包含 def handle(req)
├── requirements.txt # pip 依赖自动解析
└── stack.yml # 定义函数名、镜像、环境变量
faas-cli build 会自动:
- 创建临时 Dockerfile,FROM functions/python:3.11;
- COPY handler.py 和 requirements.txt;
- RUN pip install -r requirements.txt;
- 设置 ENTRYPOINT ["/home/app/function"];
- 构建镜像并打 tag 为 my-registry/my-function:latest。
你完全不用写一行 Dockerfile,也不用记 kubectl rollout restart deployment,所有操作都在一个 CLI 里闭环。我见过太多团队因为 kubectl apply -f xxx.yaml 里漏了一个 env 变量,导致函数启动失败却查不出原因;而 faas-cli deploy 会校验 stack.yml 的 schema,缺失必填字段(如 image、name)直接报错,错误信息明确指出哪一行哪一列,这是真正的开发者体验。
3. 核心细节解析与实操要点:从零搭建可生产的 OpenFaaS 环境
3.1 DOKS 集群创建:避开 3 个新手必踩的配置陷阱
创建 DOKS 集群看似简单,但三个关键选项选错,后续会付出数倍代价:
陷阱一:Region 选择影响函数延迟
DigitalOcean 全球有 10+ 数据中心,但并非所有都支持最新 Kubernetes 版本。截至 2024 年 7 月,纽约(nyc3)、阿姆斯特丹(ams3)、新加坡(sgp1)已支持 Kubernetes 1.28,而班加罗尔(blr1)仍停留在 1.26。更重要的是,你的主要用户在哪里?如果 80% 用户在东南亚,却把集群建在 nyc3,函数调用链路要跨太平洋,P95 延迟必然突破 500ms。实测数据:同样一个 echo 函数,在 sgp1 集群响应平均 42ms,在 nyc3 集群平均 218ms。建议原则:用户地理集中区优先,其次考虑版本新旧,最后才是价格(各区域价格基本一致)。
陷阱二:Node Pool 配置决定函数弹性能力
DOKS 允许你创建多个 Node Pool,但新手常犯两个错误:
- 只建一个 Pool 且类型固定 :比如全用 s-2vcpu-4gb,当 CPU 密集型函数并发激增时,节点 CPU 使用率瞬间 100%,新 Pod 无法调度,出现 Pending 状态;
-
忽略 Auto Scaling 开关
:DOKS 的 Auto Scaling 默认关闭!必须手动开启并设置 Min/Max 节点数。我们的生产配置是:
- Min Nodes: 2(保障基础服务不中断)
- Max Nodes: 8(防止单次误操作拉爆预算)
-
Scale Down Delay: 10 分钟(避免短时流量尖峰后立即缩容,造成反复扩缩)
这样当函数调用量突增,HPA 触发扩容时,DOKS 会在 2 分钟内自动创建新节点并加入集群,整个过程无需人工干预。
陷阱三:Networking 设置决定函数可访问性
创建集群时,“Networking” 页面有两个关键开关:
- ✅ Enable Private Networking:必须开启。OpenFaaS 的 gateway、queue-worker、nats-streaming 等组件间通信走内网,开启后所有节点自动分配私有 IP(10.100.0.0/16),避免公网带宽费用和安全风险;
- ❌ Enable IPv6:建议关闭。DOKS 的 IPv6 支持尚不完善,OpenFaaS 的某些组件(如 prometheus-operator)在 IPv6 环境下会出现 DNS 解析失败,导致 metrics 抓取中断。我们曾因此丢失连续 36 小时的函数调用成功率数据,排查了两天才发现是 IPv6 的 /etc/resolv.conf 配置问题。
提示:创建完成后,务必执行
kubectl get nodes -o wide确认所有节点 STATUS 为 Ready,INTERNAL-IP 为 10.100.x.x 段,且 AGE 小于 5 分钟。如果出现 NotReady,大概率是节点磁盘空间不足(DOKS 默认系统盘 25GB),此时需登录 Droplet 执行df -h /,清理 /var/log/journal/ 下的旧日志(journalctl --vacuum-size=200M)。
3.2 OpenFaaS 安装:helm chart 的 5 个关键参数详解
OpenFaaS 官方推荐使用 Helm 安装,但直接
helm install openfaas openfaas/openfaas
会得到一个“玩具版”——无认证、无监控、无持久化。生产环境必须覆盖以下 5 个核心参数:
1.
--set functionNamespace=openfaas-fn
这是 OpenFaaS 的函数命名空间,默认是 openfaas-fn。千万别改成 default!否则你的函数会和集群其他应用混在一起,RBAC 权限难以隔离。我们曾因误设为 default,导致一个测试函数的 ServiceAccount 意外获得了 cluster-admin 权限,险些酿成事故。
2.
--set basic_auth=true
强制开启 Basic Auth。OpenFaaS 的 gateway 默认监听 8080 端口,不加认证等于把函数管理后台暴露在公网。该参数会自动创建 secret
basic-auth
,包含 admin 用户密码(base64 编码),并配置 gateway 的 auth middleware。密码生成命令:
htpasswd -nbB admin $(openssl rand -base64 12)
,输出结果直接填入
--set basic_auth_user=admin,basic_auth_password=xxx
。
3.
--set serviceType=LoadBalancer
DOKS 的 LoadBalancer 类型 Service 会自动创建 DigitalOcean Load Balancer,并分配公网 IP。这是最简单的函数对外暴露方式。注意:不要选 NodePort,因为 DOKS 的 NodePort 范围是 30000-32767,而 OpenFaaS gateway 默认用 8080,需额外修改 service port,徒增复杂度。
4.
--set operator.create=true
启用 OpenFaaS Operator。这是 OpenFaaS 的“大脑”,负责监听 Function CRD 变化,自动创建 Deployment、Service、Ingress。没有它,faas-cli deploy 只是生成 YAML,不会真正部署。Operator 还提供高级功能:比如设置
limits.memory=512Mi
时,Operator 会自动在 Deployment 的 containers.resources.limits 添加对应字段,并确保节点有足够内存。
5.
--set async=true
启用异步执行模式。OpenFaaS 默认是同步 HTTP 调用,适合低延迟场景;但当你有耗时任务(如视频转码、大文件处理),同步模式会导致客户端超时。async=true 会部署 queue-worker 和 nats-streaming,函数调用变为“发消息→进队列→后台处理→回调”,客户端立即返回 202 Accepted,真正实现解耦。
完整安装命令(请替换 YOUR_DOMAIN 为你的域名):
helm repo add openfaas https://openfaas.github.io/faas-netes/
helm repo update
kubectl create namespace openfaas
kubectl create namespace openfaas-fn
helm upgrade openfaas --install openfaas/openfaas \
--namespace openfaas \
--set functionNamespace=openfaas-fn \
--set basic_auth=true \
--set serviceType=LoadBalancer \
--set operator.create=true \
--set async=true \
--set ingress.enabled=true \
--set ingress.hosts[0]=YOUR_DOMAIN \
--set ingress.tls[0].hosts[0]=YOUR_DOMAIN \
--set ingress.tls[0].secretName=tls-secret
注意:tls-secret 需要提前用 cert-manager 或手动创建。DOKS 的 Load Balancer 支持 TLS 终止,但更推荐在 gateway 层做 TLS,这样函数调用链路全程加密。我们用 cert-manager 自动签发 Let’s Encrypt 证书,命令为
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.2/cert-manager.yaml,然后创建 Issuer 和 Certificate 资源。
3.3 faas-cli 配置与函数开发:从 “Hello World” 到生产就绪
3.3.1 CLI 初始化:绕过 proxy 和 ca-bundle 的 2 个坑
在 Ubuntu 22.04 上安装 faas-cli 后,首次运行
faas-cli version
可能报错:
-
x509: certificate signed by unknown authority:因为 DOKS 的 gateway 使用自签名证书(或 Let’s Encrypt 证书链不全),faas-cli 默认校验证书; -
connection refused:公司网络有代理,faas-cli 无法直连 gateway。
解决方案:
-
获取 gateway 的 CA 证书:
kubectl -n openfaas get secret gateway-tls -o jsonpath='{.data.ca\.crt}' | base64 -d > /tmp/gateway-ca.crt; -
配置 faas-cli 使用该证书:
export OPENFAAS_URL=https://YOUR_DOMAIN && export OPENFAAS_CA_CERT=/tmp/gateway-ca.crt; -
如果走代理,设置
export HTTPS_PROXY=http://your-proxy:3128,但必须排除集群内网:export NO_PROXY=10.100.0.0/16,localhost,127.0.0.1。
3.3.2 函数开发:stack.yml 的 7 个生产级字段
一个生产可用的 stack.yml 不只是定义 name 和 image,它承载着函数的 SLA 承诺。以下是我们的标准模板(以 Python 函数为例):
version: 1.0
provider:
name: openfaas
gateway: https://YOUR_DOMAIN
functions:
my-python-func:
lang: python311
handler: ./my-python-func
image: your-registry/my-python-func:latest
environment:
write_timeout: "30s" # HTTP 响应超时,避免长连接占用
read_timeout: "30s" # 请求体读取超时
upstream_url: "https://api.example.com" # 函数内调用的上游地址
secrets:
- db-password # 引用 Kubernetes Secret,避免明文写密码
labels:
com.openfaas.scale.zero: "true" # 启用零副本缩容
annotations:
prometheus.io/scrape: "true" # 开启 Prometheus 抓取
limits:
memory: 512Mi
cpu: 1000m
requests:
memory: 256Mi
cpu: 500m
关键字段说明:
-
com.openfaas.scale.zero: "true":这是 OpenFaaS 的杀手级功能。当函数 5 分钟内无调用,Operator 自动将副本数缩为 0,释放所有资源;下次调用时,冷启动时间约 1.2 秒(DOKS 节点 SSD 磁盘 + containerd 镜像缓存优化),远低于 AWS Lambda 的平均 800ms; -
secrets:函数启动时,OpenFaaS Operator 会自动将名为 db-password 的 Secret 挂载为 /var/openfaas/secrets/db-password 文件,你的 Python 代码只需with open("/var/openfaas/secrets/db-password") as f: password = f.read().strip()即可安全读取,无需硬编码; -
limits/requests:这是 Kubernetes 资源管理的基石。我们严格遵循“requests = 50% of limits”原则,比如 memory limits=512Mi,则 requests=256Mi,这样既能保证函数有足够资源运行,又能让 kube-scheduler 合理调度,避免节点资源碎片化。
3.3.3 构建与部署:如何让镜像体积缩小 65%
Python 函数的镜像动辄 800MB,主要来自 base image(python:3.11-slim)和 pip install 的依赖。我们通过 3 步压缩:
-
换用 distroless base image
:OpenFaaS 官方提供
ghcr.io/openfaas/python311:distroless,基于 gcr.io/distroless/python3-debian12,镜像大小仅 120MB,且无 shell(/bin/sh 不存在),安全性大幅提升; -
多阶段构建
:在 stack.yml 中添加
build_options: ["--no-cache"],并在 handler.py 同级目录创建 .dockerignore,排除 pycache 、.git、tests/ 等无关文件; -
依赖精简
:requirements.txt 中禁用
--no-binary :all:,强制编译 wheel,但对 pandas、numpy 等 C 扩展包,改用pip install --only-binary=all --find-links https://download.pytorch.org/whl/torch_stable.html指定预编译 wheel 源。
实测效果:一个含 pandas、numpy、requests 的函数,镜像从 782MB → 276MB,DOKS 节点拉取时间从 42 秒 → 9 秒,部署效率提升 4.6 倍。
4. 实操过程与核心环节实现:一次完整的函数上线全流程
4.1 场景设定:为电商后台开发一个“订单履约状态查询”函数
需求很具体:前端 App 每次打开订单详情页,需调用此函数,输入 order_id,返回 JSON 格式的状态(pending、shipped、delivered)、预计送达时间、物流单号。数据源是 PostgreSQL 数据库,表结构为 orders(id, status, estimated_delivery, tracking_number)。
4.1.1 步骤一:准备数据库连接与 Secret
首先,创建 Kubernetes Secret 存储数据库凭证:
kubectl create secret generic db-secret \
--from-literal=host=your-db-do-user.db.ondigitalocean.com \
--from-literal=port=25060 \
--from-literal=name=ecommerce_db \
--from-literal=user=do_admin \
--from-literal=password=your-strong-password \
-n openfaas-fn
注意:DOKS 的 Managed PostgreSQL 默认开启 TLS,所以连接字符串需为
postgresql://do_admin:password@host:port/name?sslmode=require
。
4.1.2 步骤二:编写 handler.py 与 requirements.txt
目录结构:
order-status/
├── handler.py
├── requirements.txt
└── stack.yml
handler.py 内容(关键点:连接池复用、错误分类、结构化日志):
import os
import json
import logging
from datetime import datetime
import psycopg2
from psycopg2 import sql
from psycopg2.pool import ThreadedConnectionPool
# 初始化连接池,避免每次请求新建连接
pool = ThreadedConnectionPool(
minconn=1,
maxconn=5,
host=os.getenv("DB_HOST"),
port=os.getenv("DB_PORT"),
dbname=os.getenv("DB_NAME"),
user=os.getenv("DB_USER"),
password=os.getenv("DB_PASSWORD"),
sslmode="require"
)
def handle(req):
"""OpenFaaS handler for order status query"""
try:
# 解析请求体
event = json.loads(req)
order_id = event.get("order_id")
if not order_id:
return json.dumps({"error": "order_id is required"}, ensure_ascii=False)
# 从连接池获取连接
conn = pool.getconn()
cursor = conn.cursor()
# 查询订单
cursor.execute(
"SELECT status, estimated_delivery, tracking_number FROM orders WHERE id = %s",
(order_id,)
)
row = cursor.fetchone()
if not row:
return json.dumps({"error": "order not found"}, ensure_ascii=False)
# 构建响应
result = {
"order_id": order_id,
"status": row[0],
"estimated_delivery": row[1].isoformat() if row[1] else None,
"tracking_number": row[2],
"timestamp": datetime.utcnow().isoformat()
}
return json.dumps(result, ensure_ascii=False)
except psycopg2.OperationalError as e:
logging.error(f"Database connection error: {e}")
return json.dumps({"error": "database unavailable"}, ensure_ascii=False)
except Exception as e:
logging.error(f"Unexpected error: {e}")
return json.dumps({"error": "internal server error"}, ensure_ascii=False)
finally:
# 归还连接到池
if 'conn' in locals():
pool.putconn(conn)
requirements.txt:
psycopg2-binary==2.9.7
注意:psycopg2-binary 是预编译 wheel,避免在构建时编译 C 扩展,大幅缩短构建时间。生产环境建议用源码版(psycopg2)以获得更好性能,但需在 Dockerfile 中安装 build-essential。
4.1.3 步骤三:编写 stack.yml 并部署
stack.yml(关键参数已加注释):
version: 1.0
provider:
name: openfaas
gateway: https://api.yourdomain.com
functions:
order-status:
lang: python311
handler: ./order-status
image: registry.digitalocean.com/your-registry/order-status:latest
environment:
write_timeout: "10s" # 订单查询必须快,10秒足够
read_timeout: "5s"
DB_HOST: "your-db-do-user.db.ondigitalocean.com"
DB_PORT: "25060"
DB_NAME: "ecommerce_db"
DB_USER: "do_admin"
secrets:
- db-secret # 引用前面创建的 Secret
labels:
com.openfaas.scale.zero: "true"
com.openfaas.function.cache: "true" # 启用函数级缓存
annotations:
prometheus.io/scrape: "true"
limits:
memory: 256Mi
cpu: 500m
requests:
memory: 128Mi
cpu: 250m
部署命令:
# 登录 DO Container Registry
docker login registry.digitalocean.com -u your_do_token
# 构建、推送、部署一步到位
faas-cli build -f ./order-status/stack.yml
faas-cli push -f ./order-status/stack.yml
faas-cli deploy -f ./order-status/stack.yml
4.1.4 步骤四:验证与压测:用真实数据检验 SLA
部署成功后,用
faas-cli list
确认函数状态:
Function Invocations Replicas Uptime
order-status 0 1/1 2m15s
调用测试:
echo '{"order_id": "ORD-2024-0001"}' | faas-cli invoke order-status
# 返回 {"order_id":"ORD-2024-0001","status":"shipped",...}
压测(用 hey 工具模拟 100 并发,持续 60 秒):
hey -z 60s -c 100 -m POST -H "Content-Type: application/json" \
-d '{"order_id":"ORD-2024-0001"}' https://api.yourdomain.com/function/order-status
实测结果(DOKS 3 节点集群):
| 指标 | 数值 | 说明 |
|---|---|---|
| Requests/sec | 128.7 | 单函数实例处理能力 |
| P50 Latency | 42ms | 一半请求在 42ms 内返回 |
| P95 Latency | 186ms | 95% 请求在 186ms 内返回,满足电商 SLA |
| Error Rate | 0% | 无超时或连接错误 |
| CPU Usage | 320m | 未触及 limits.cpu=500m,有余量 |
实操心得:压测时发现 P95 延迟突然跳到 800ms,排查发现是 PostgreSQL 连接池耗尽。解决方案是在 handler.py 中增加连接池大小(maxconn=10),并将 stack.yml 的 replicas 设为 3,利用 HPA 自动扩缩。OpenFaaS 的 autoscale.yml 文件可定义更细粒度规则,比如
cpuPercentage: 70表示 CPU 使用率超 70% 时扩容。
4.2 进阶实战:用异步模式处理“批量订单导出”任务
订单履约函数是同步的,但后台运营需要每天凌晨导出昨日所有订单 Excel,这个任务可能耗时 15 分钟,绝不能阻塞 API。这时就要用 OpenFaaS 的异步模式。
4.2.1 创建异步函数:batch-export
stack.yml 关键差异:
functions:
batch-export:
lang: python311
handler: ./batch-export
image: registry.digitalocean.com/your-registry/batch-export:latest
environment:
write_timeout: "900s" # 允许最长 15 分钟执行
read_timeout: "30s"
OUTPUT_BUCKET: "s3://your-do-spaces-bucket" # 输出到 DO Spaces
secrets:
- spaces-key # DO Spaces 的 access key/secret
labels:
com.openfaas.scale.zero: "false" # 异步任务不缩容,常驻 1 副本
annotations:
com.openfaas.function.async: "true" # 显式声明异步
handler.py 核心逻辑:
import boto3
import pandas as pd
from io import BytesIO
def handle(req):
# 1. 从消息队列获取参数(req 是 JSON,含 start_date, end_date)
params = json.loads(req)
# 2. 查询数据库(此处省略连接池代码)
df = pd.read_sql_query(
"SELECT * FROM orders WHERE created_at BETWEEN %s AND %s",
conn, params=[params['start_date'], params['end_date']]
)
# 3. 生成 Excel
output = BytesIO()
with pd.ExcelWriter(output, engine='openpyxl') as writer:
df.to_excel(writer, index=False)
output.seek(0)
# 4. 上传到 DO Spaces
s3 = boto3.client('s3',
endpoint_url='https://nyc3.digitaloceanspaces.com',
aws_access_key_id=os.getenv('SPACES_KEY'),
aws_secret_access_key=os.getenv('SPACES_SECRET')
)
s3.upload_fileobj(
output,
'your-bucket-name',
f'exports/orders_{params["start_date"]}_{params["end_date"]}.xlsx'
)
return json.dumps({"status": "success", "file": "s3://..."})
4.2.2 触发异步任务:用 curl 发送消息
curl -X POST https://api.yourdomain.com/async-function/batch-export \
-H "Content-Type: application/json" \
-d '{"start_date":"2024-07-01","end_date":"2024-07-01"}' \
-u admin:your-password
返回
202 Accepted
,表示消息已入队。任务完成后,OpenFaaS 会调用你配置的 callback URL(或写入指定 S3 bucket 的 status 文件),实现真正的事件驱动。
5. 常见问题与排查技巧实录:那些文档里不会写的坑
5.1 函数调用返回 503 Service Unavailable:90% 是 gateway 问题
现象:
faas-cli invoke my-func
返回
Error: error received from function: 503 Service Unavailable
,但
kubectl get pods -n openfaas
显示所有 Pod 都 Running。
排查路径:
-
检查 gateway 日志
:
kubectl logs -n openfaas deploy/gateway,常见错误:-
upstream connect error or disconnect/reset before headers:gateway 无法连接到函数 Pod,通常是 Service 名称不匹配(函数名含下划线,但 Kubernetes Service 名称只允许小写字母、数字、连字符); -
no healthy upstream:函数 Pod 的 readiness probe 失败,检查 handler.py 是否监听了正确端口(默认 8080),且/healthz路径返回 200;
-
-
检查函数 Service
:
kubectl get svc -n openfaas-fn
更多推荐
所有评论(0)