OpenFaaS 在 DigitalOcean Kubernetes 上的生产级落地实践
1. 这不是“云函数”的简单搬运,而是把 OpenFaaS 当作 Kubernetes 的原生扩展来用
OpenFaaS 在 DigitalOcean Kubernetes 上跑起来,很多人第一反应是“又一个 Serverless 平台部署教程”。但实际动手做过三轮以上集群迭代后,我越来越确信:这种理解偏差,恰恰是多数人部署失败、后续维护卡壳、甚至误判技术价值的根源。OpenFaaS 不是挂在 Kubernetes 外面的“插件”,它是一套深度嵌入 K8s 控制平面的声明式函数编排系统——它的 Operator 直接监听 Function 自定义资源(CRD),它的网关(Gateway)本质是带自动 TLS 终止和请求路由的 Ingress Controller 增强版,它的队列驱动器(NATS Streaming)与 K8s 的 Pod 生命周期事件联动紧密。你如果把它当成“先装个 kubectl,再 helm install 一下就完事”的黑盒,那很快就会在函数冷启动超时、日志无法聚合、健康检查反复失败这些地方撞墙。
核心关键词 OpenFaaS 、 Kubernetes 、 DigitalOcean 、 serverless 、 faas-cli ,它们不是并列关系,而是一个分层依赖链:DigitalOcean 提供的是经过轻量加固、默认启用 CSI 存储插件和 Metrics Server 的托管 K8s 底座;Kubernetes 是调度、网络、安全策略的统一执行引擎;OpenFaaS 是构建在其上的、面向开发者交付函数工作流的抽象层;faas-cli 则是这个抽象层唯一被官方长期维护的、与底层解耦最干净的客户端工具。它不碰 YAML 渲染逻辑,不封装 Helm 命令,所有 faas-cli deploy 操作最终都转化为对 /system/functions API 的 POST 请求——这意味着你完全可以在 CI/CD 流水线里用 curl 替代 faas-cli,只要构造出正确的 JSON payload。
适合谁来看?不是只看“5分钟部署”的新手,而是已经能独立完成 kubeadm init 或 doctl kubernetes cluster create 的中级运维/DevOps 工程师,或是正在为微服务拆分后遗留大量胶水代码(如定时清洗、格式转换、Webhook 转发)寻找轻量级替代方案的后端开发者。如果你还在纠结“要不要上 Istio”或者“Knative 和 OpenFaaS 到底选哪个”,这篇内容可能暂时不适合你——我们聚焦在“已选定 OpenFaaS + DO K8s”这一明确前提下的真实落地细节,包括那些官网文档里不会写、但你在生产环境凌晨三点排查故障时 desperately 需要的答案。
2. 内容整体设计与思路拆解:为什么必须绕开 Helm Chart 的“一键安装”幻觉
2.1 官方 Helm Chart 的三大隐性代价
OpenFaaS 官方 GitHub 仓库里那个 star 数最高的 openfaas/faas-netes Helm Chart,表面看是“一键部署神器”,实测下来却是多数团队踩坑的起点。我带过两个项目组,第一次直接 helm install openfaas openfaas/openfaas ,结果上线第三天就因网关 Pod 反复 OOM 被驱逐;第二次改用 --set functionNamespace=openfaas-fn 显式指定命名空间,又发现自定义域名的 TLS 证书始终无法注入到 Gateway 的 Envoy 实例中。问题根源不在 Chart 本身,而在它试图“抹平”K8s 原生能力差异的设计哲学。
-
代价一:Operator 模式被弱化为静态 DaemonSet
Chart 默认将faas-netesd(即 OpenFaaS Operator)部署为 DaemonSet,这在 DigitalOcean 的标准 DOKS 集群(节点数 ≥3)下会导致多个 Operator 实例同时监听同一组 CRD 事件。K8s 的 etcd watch 机制不保证事件顺序,当两个 Operator 几乎同时收到Function创建请求时,其中一个会因409 Conflict报错退出 reconcile 循环,而错误日志被淹没在kubectl logs -n openfaas deploy/gateway的海量输出里。正确做法是强制将其改为 Deployment,并设置replicas: 1+podAntiAffinity确保单实例高可用。 -
代价二:Gateway 的 Service 类型硬编码为 LoadBalancer
DigitalOcean 的 LoadBalancer 服务背后是其专有 L4 负载均衡器,它不支持 HTTP/2 ALPN 协商,也不透传X-Forwarded-For头部。当你用faas-cli invoke --gateway https://myapp.example.com hello-world调用函数时,Gateway 收到的RemoteAddr是 DO LB 的内网 IP,而非真实客户端 IP。更致命的是,DO LB 默认关闭了 WebSocket 支持,导致faas-cli logs -f功能彻底失效。解决方案不是换云厂商,而是将 Gateway Service 改为ClusterIP,再通过 DigitalOcean 官方推荐的 Nginx Ingress Controller(带nginx.ingress.kubernetes.io/ssl-passthrough: "true"注解)做七层代理。 -
代价三:TLS 证书管理与 Cert-Manager 强耦合
Chart 中certManager.enabled=true的开关看似方便,但它要求你提前在openfaas命名空间里部署 Cert-Manager 的 CRD 和控制器。而 DigitalOcean 的 DOKS 集群默认不启用ValidatingWebhookConfiguration,Cert-Manager 的 webhook 服务会因证书校验失败而 CrashLoopBackOff。我们最终采用的是“手动注入”模式:用openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/CN=*.openfaas.example.com"生成通配符证书,再通过kubectl create secret tls openfaas-tls --cert=tls.crt --key=tls.key -n openfaas注入,Gateway Deployment 中通过 volumeMount 挂载该 Secret。
2.2 我们选择的最小可行架构(MVA)
基于上述教训,我们为 DigitalOcean Kubernetes 设计了一套“去 Helm 化”的最小可行架构(Minimal Viable Architecture),它只包含 4 个核心组件,全部用原生 K8s YAML 管理:
- openfaas 命名空间 :带
istio-injection=disabled标签(避免与 Istio 冲突),资源配额限制 CPU 200m / 内存 512Mi; - faas-netesd Operator Deployment :单副本,使用
nodeSelector锁定到专用控制节点(避免与业务 Pod 争抢资源),livenessProbe 检查/healthz端点; - gateway Service + Deployment :Service 类型为 ClusterIP,Deployment 启用
--set gateway.replicas=2并配置 HPA(CPU 使用率 >70% 时扩容); - NATS Streaming StatefulSet :3 副本,使用 DigitalOcean Block Storage 的 PVC(
storageClassName: do-block-storage),确保消息持久化不丢。
这套架构放弃了一切“自动化便利”,换来的是对每个组件生命周期的绝对掌控。比如当需要升级 OpenFaaS 版本时,我们不再运行 helm upgrade ,而是直接 kubectl set image deploy/gateway gateway=openfaas/gateway:0.28.0 -n openfaas ,整个过程耗时 <12 秒,且滚动更新期间函数调用零中断——因为 Gateway 的 readinessProbe 会精确等待新 Pod 的 /system/health 返回 200 后才将其加入 Endpoints。
提示:DigitalOcean 的 DOKS 集群默认启用了
PodSecurityPolicy(PSP),而 OpenFaaS 的默认 YAML 中faas-netesd的 SecurityContext 缺少privileged: false声明。这会导致 Pod 无法启动,报错admission webhook "psp.mutating.webhook.admission.k8s.io" denied the request。必须在所有 Deployment 的spec.template.spec.containers[].securityContext中显式添加该字段。
3. 核心细节解析与实操要点:从 DO 控制台到第一个函数的 7 个关键断点
3.1 断点一:DOKS 集群创建时的三个必选参数
很多教程跳过这一步,直接假设“你已经有了一个 K8s 集群”。但在 DigitalOcean,集群创建阶段的三个参数,直接决定了后续 OpenFaaS 的稳定性上限:
-
Kubernetes 版本选择 :必须选
v1.26.x或v1.27.x, 严禁使用 v1.28+ 。原因在于 OpenFaaS 0.28.x(当前最新稳定版)的 CRD 定义仍基于apiextensions.k8s.io/v1beta1,而 v1.28+ 已彻底移除该 API 组。你若强行部署,kubectl apply -f https://raw.githubusercontent.com/openfaas/faas-netes/master/namespaces.yml会返回error: unable to recognize "STDIN": no matches for kind "CustomResourceDefinition" in version "apiextensions.k8s.io/v1beta1"。DigitalOcean 控制台在创建集群时,默认推荐的是最新版,务必手动下拉选择。 -
节点池配置中的“标签” :在“Node Pools”配置页,点击“Add Node Pool”后,在“Labels”输入框里填入
openfaas-role=control。这不是可选项,而是为后续 Operator 部署做准备。我们将 Operator 限定在此类节点上运行,避免其与业务函数 Pod 共享 CPU CFS 配额,导致函数冷启动延迟飙升。命令行创建方式为:doctl kubernetes cluster create my-openfaas-cluster --version 1.27.10-do.0 --node-pool "name=control;count=2;size=s-2vcpu-4gb;tags=openfaas-role=control"。 -
VPC 与防火墙策略 :创建集群时,必须勾选“Use existing VPC”并选择你已配置好的私有网络。更重要的是,在“Firewall Rules”部分, 必须放行 TCP 端口 8080 和 8081 。这是 OpenFaaS Gateway 的默认监听端口(HTTP 和 HTTPS),DigitalOcean 的默认防火墙规则会阻止所有入站流量,即使你后续配置了 Ingress,LB 也无法将流量转发到 Gateway Pod。
3.2 断点二:faas-cli 的安装与身份认证不是“下一步”,而是安全基线
faas-cli 看似只是个命令行工具,但它承载着 OpenFaaS 的全部访问控制逻辑。DigitalOcean 的 DOKS 集群默认启用 RBAC,而 OpenFaaS 的 gateway ServiceAccount 默认只有 openfaas 命名空间内的读写权限。如果你跳过认证步骤直接 faas-cli list ,会得到 Unauthorized 错误。
正确流程是四步闭环:
- 获取集群凭证 :
doctl kubernetes cluster kubeconfig save my-openfaas-cluster - 创建专用 ServiceAccount :
kubectl create serviceaccount openfaas-admin -n openfaas kubectl create clusterrolebinding openfaas-admin --clusterrole=cluster-admin --serviceaccount=openfaas:openfaas-admin - 提取 Token 并配置 faas-cli :
TOKEN=$(kubectl get secret $(kubectl get serviceaccount openfaas-admin -n openfaas -o jsonpath='{.secrets[0].name}') -n openfaas -o jsonpath='{.data.token}' | base64 --decode) faas-cli login -g https://gateway.openfaas.svc.cluster.local:8080 -p $TOKEN --skip-tls-verify - 验证权限 :
faas-cli list --gateway https://gateway.openfaas.svc.cluster.local:8080应返回空列表(无函数),而非报错。
注意:
--skip-tls-verify是必须的。因为 Gateway 的自签名证书 CN 是gateway.openfaas.svc.cluster.local,而faas-cli login默认校验证书域名。你不能用kubectl port-forward临时暴露 Gateway 来绕过,因为 port-forward 会破坏 TLS SNI 扩展,导致证书校验失败。
3.3 断点三:函数镜像构建必须用 faas-cli build ,禁用 docker build
这是新手最容易犯的致命错误。OpenFaaS 的函数运行时(如 python3 、 node )不是通用容器,而是预置了特定入口点(entrypoint)和健康检查路径的定制镜像。如果你直接 docker build -t myfunc . ,生成的镜像缺少 /healthz 端点和 /function 的 HTTP handler,Gateway 会持续返回 503 Service Unavailable 。
faas-cli build 的核心作用是两件事:
- 注入模板层 :以
python3模板为例,它会将你的handler.py复制到openfaas/python3:latest基础镜像中,并覆盖其默认的index.py; - 重写 Dockerfile :生成的
.dockerignore会排除build/和template/目录,防止体积膨胀;Dockerfile 中的CMD被替换为["/usr/bin/fwatchdog"],这是 OpenFaaS 的守护进程,负责接收 HTTP 请求并转发给你的 handler。
实操命令:
# 初始化函数项目(自动下载 python3 模板)
faas-cli template pull
faas-cli new --lang python3 hello-world
# 修改 handler.py,添加一行 print("Hello from OpenFaaS!")
faas-cli build -f hello-world.yml
构建完成后, faas-cli push -f hello-world.yml 会将镜像推送到你配置的私有 Registry(如 DigitalOcean Container Registry),或公共 Registry(如 Docker Hub)。注意: hello-world.yml 中的 image: 字段必须是完整路径,例如 registry.digitalocean.com/my-registry/hello-world 。
3.4 断点四:部署时的 --annotation 是解决“函数不触发”的唯一钥匙
部署函数后, faas-cli list 能看到函数状态为 Ready ,但 curl -X POST http://gateway.openfaas.svc.cluster.local:8080/function/hello-world 却返回 404 Not Found 。这个问题 90% 的案例都源于一个被忽略的注解(annotation)。
OpenFaaS Gateway 默认只暴露 http 协议的函数。当你在 DigitalOcean 上通过 Ingress 暴露函数时,Ingress Controller 会将 HTTPS 请求降级为 HTTP 转发给 Gateway,但 Gateway 的路由表里没有该函数的 HTTPS 路由条目。解决方案是在部署时添加 com.openfaas.health.http=true 注解:
faas-cli deploy \
-f hello-world.yml \
--annotation com.openfaas.health.http=true \
--gateway https://gateway.openfaas.svc.cluster.local:8080
这个注解的作用是告诉 Gateway:“请为该函数创建一个 HTTP 健康检查端点”,而 Gateway 的内部路由引擎会自动将此函数注册到 http 和 https 两个协议的路由表中。没有它,函数永远处于“半激活”状态——Pod 运行正常,日志无报错,但外部请求就是 404。
3.5 断点五:Ingress 配置的三个不可妥协字段
将 Gateway 暴露给公网,必须通过 DigitalOcean 官方 Nginx Ingress Controller。其 YAML 配置中,以下三个字段是硬性要求,缺一不可:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: openfaas-gateway
namespace: openfaas
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-passthrough: "true" # 关键!透传 TLS,否则证书校验失败
nginx.ingress.kubernetes.io/force-ssl-redirect: "true" # 强制 HTTPS
spec:
tls:
- hosts:
- openfaas.example.com
secretName: openfaas-tls # 必须与你创建的 Secret 名称一致
rules:
- host: openfaas.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gateway # 必须是 gateway Service 的名称
port:
number: 8080 # Gateway 的 HTTP 端口
特别注意 nginx.ingress.kubernetes.io/ssl-passthrough: "true" 。DigitalOcean 的 Nginx Ingress 默认关闭 SSL Passthrough,这意味着它会终止 TLS 连接,用自己的证书响应客户端,再以 HTTP 方式向 Gateway 发起新请求。这会导致两个后果:一是 Gateway 收不到原始 TLS 证书,无法做双向认证;二是 X-Forwarded-Proto 头部丢失,函数内部无法判断请求是否来自 HTTPS。开启 Passthrough 后,Nginx 只做 TCP 层转发,所有 TLS 握手均由 Gateway 完成。
3.6 断点六:函数超时时间不是写死的,而是由三个层级共同决定
OpenFaaS 的函数超时(timeout)是一个典型的“多层叠加”参数,修改任何一个都可能引发连锁反应:
-
第一层:faas-cli deploy 的
--env参数faas-cli deploy --env write_timeout=30s设置的是函数进程的写超时,即函数 handler 执行的最大时长。超过此值,fwatchdog 会发送 SIGTERM 终止进程。 -
第二层:Gateway 的
--set writeTimeout=30s
这是 Gateway 对函数 Pod 的 HTTP 客户端超时。它必须 ≥ 第一层的值,否则 Gateway 会在函数 handler 还未结束时就关闭连接,返回504 Gateway Timeout。 -
第三层:Ingress Controller 的
proxy-read-timeout
DigitalOcean Nginx Ingress 默认proxy-read-timeout为 60 秒。如果函数执行 45 秒,Gateway 正常返回,但 Ingress 在 60 秒前未收到完整响应(因网络抖动),它会主动断开连接。因此,必须在 Ingress 的 ConfigMap 中全局设置:data: proxy-read-timeout: "120" proxy-send-timeout: "120"
三者关系是: Ingress timeout ≥ Gateway timeout ≥ Function timeout 。我们线上统一设为 120s ,既避免了短时抖动误判,又防止恶意函数无限占用资源。
3.7 断点七:日志查看必须用 kubectl logs -c functions , faas-cli logs 是残废
faas-cli logs -f hello-world 在 DigitalOcean 环境下基本不可用。原因在于它依赖 NATS Streaming 的消息队列,而 DO 的默认网络策略会阻止 Pod 间 UDP 流量(NATS 使用 UDP 进行心跳探测)。你执行该命令,只会看到 Error: dial tcp 10.244.1.5:4222: i/o timeout 。
真正可靠的日志方案是直接读取函数 Pod 的容器日志。由于 OpenFaaS 的函数 Pod 有两个容器: hello-world (你的函数)和 queue-worker (处理异步任务),必须指定 -c 参数:
# 查看函数主容器日志(最常用)
kubectl logs -n openfaas-fn deploy/hello-world -c hello-world -f
# 查看 queue-worker 日志(排查异步调用失败)
kubectl logs -n openfaas-fn deploy/hello-world -c queue-worker -f
为了提升效率,我们编写了一个 shell 别名:
alias faas-logs='kubectl logs -n openfaas-fn deploy/$(basename $(pwd))-$(git rev-parse --short HEAD) -c $(basename $(pwd)) -f'
进入函数项目目录后,直接敲 faas-logs 即可实时跟踪日志。
4. 实操过程与核心环节实现:从零开始的完整流水线(含所有 YAML 和命令)
4.1 环境初始化:创建集群、配置 CLI、准备 Registry
第一步:创建 DOKS 集群(命令行方式,确保可复现)
# 登录 DigitalOcean CLI
doctl auth init
# 创建集群(指定版本、节点池、VPC)
doctl kubernetes cluster create openfaas-prod \
--region sgp1 \
--version 1.27.10-do.0 \
--node-pool "name=control;count=2;size=s-2vcpu-4gb;tags=openfaas-role=control" \
--node-pool "name=workers;count=3;size=s-4vcpu-8gb" \
--tag openfaas \
--vpc-uuid $(doctl vpcs list --format ID --no-header | head -n1)
# 获取 kubeconfig 并设置上下文
doctl kubernetes cluster kubeconfig save openfaas-prod
kubectl config use-context do-sgp1-openfaas-prod
第二步:创建 DigitalOcean Container Registry(用于存储函数镜像)
# 创建 registry(区域必须与集群一致)
doctl registry create openfaas-registry --region sgp1
# 登录 registry(需先安装 doctl 和 docker-credential-do)
doctl registry login
# 验证登录
echo '{"auths":{"registry.digitalocean.com":{"auth":"'"$(echo -n "$(doctl registry get openfaas-registry --format '{{.AccessToken}}' | tr -d '\n'):$(doctl registry get openfaas-registry --format '{{.AccessToken}}' | tr -d '\n')"' | base64 -w0)"'}}}' | docker-credential-do store
第三步:安装 faas-cli(macOS 示例,Linux/Windows 类似)
# 下载最新版(截至 2024 年 6 月为 0.14.10)
curl -sL https://cli.openfaas.com | sudo sh
# 验证
faas-cli version
# 输出应为:faas-cli: 0.14.10
4.2 OpenFaaS 核心组件部署:纯 YAML 方式(非 Helm)
创建 openfaas-namespace.yaml :
apiVersion: v1
kind: Namespace
metadata:
name: openfaas
labels:
istio-injection: disabled
---
apiVersion: v1
kind: Namespace
metadata:
name: openfaas-fn
labels:
istio-injection: disabled
创建 openfaas-operator.yaml (精简版,仅保留必要字段):
apiVersion: apps/v1
kind: Deployment
metadata:
name: faas-netesd
namespace: openfaas
spec:
replicas: 1
selector:
matchLabels:
app: faas-netesd
template:
metadata:
labels:
app: faas-netesd
spec:
nodeSelector:
openfaas-role: control
serviceAccountName: openfaas-admin
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: faas-netesd
image: openfaas/faas-netes:0.28.0
args:
- --gateways-url=http://gateway.openfaas:8080
- --nats-address=nats.openfaas:4222
env:
- name: namespaces
value: "openfaas-fn"
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 30
periodSeconds: 10
resources:
requests:
cpu: 100m
memory: 256Mi
limits:
cpu: 200m
memory: 512Mi
创建 openfaas-gateway.yaml :
apiVersion: v1
kind: Service
metadata:
name: gateway
namespace: openfaas
spec:
type: ClusterIP
ports:
- name: http
port: 8080
targetPort: 8080
- name: https
port: 8443
targetPort: 8443
selector:
app: gateway
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gateway
namespace: openfaas
spec:
replicas: 2
selector:
matchLabels:
app: gateway
template:
metadata:
labels:
app: gateway
spec:
serviceAccountName: openfaas-admin
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: gateway
image: openfaas/gateway:0.28.0
args:
- --listen-http=:8080
- --listen-https=:8443
- --enable-auth=true
- --tls-ca-cert=/run/secrets/tls.crt
- --tls-server-cert=/run/secrets/tls.crt
- --tls-server-key=/run/secrets/tls.key
ports:
- containerPort: 8080
- containerPort: 8443
volumeMounts:
- name: tls-certs
mountPath: /run/secrets
readOnly: true
livenessProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 15
periodSeconds: 5
resources:
requests:
cpu: 200m
memory: 512Mi
limits:
cpu: 400m
memory: 1Gi
volumes:
- name: tls-certs
secret:
secretName: openfaas-tls
应用所有 YAML:
kubectl apply -f openfaas-namespace.yaml
kubectl apply -f openfaas-operator.yaml
kubectl apply -f openfaas-gateway.yaml
# 等待所有 Pod Running
kubectl get pods -n openfaas -w
4.3 函数开发与部署全流程(以 Python 函数为例)
初始化项目:
# 创建项目目录
mkdir hello-world && cd hello-world
# 拉取 Python3 模板
faas-cli template pull
# 创建函数定义文件
cat > hello-world.yml << 'EOF'
provider:
name: openfaas
gateway: https://gateway.openfaas.svc.cluster.local:8080
functions:
hello-world:
lang: python3
handler: ./handler
image: registry.digitalocean.com/openfaas-registry/hello-world
environment:
write_timeout: "120s"
annotations:
com.openfaas.health.http: "true"
EOF
# 创建 handler 目录和代码
mkdir handler
cat > handler/handler.py << 'EOF'
def handle(req):
"""Handle function request"""
return f"Hello, {req}! Time: {__import__('time').strftime('%Y-%m-%d %H:%M:%S')}"
EOF
构建、推送、部署:
# 构建镜像(自动推送到 DO Registry)
faas-cli build -f hello-world.yml
# 部署函数(使用内部 gateway 地址)
faas-cli deploy -f hello-world.yml --gateway https://gateway.openfaas.svc.cluster.local:8080
# 验证部署状态
faas-cli list --gateway https://gateway.openfaas.svc.cluster.local:8080
# 输出应显示 hello-world 状态为 Ready
4.4 公网访问配置:Ingress + TLS + DNS
创建 openfaas-ingress.yaml :
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: openfaas-gateway
namespace: openfaas
annotations:
kubernetes.io/ingress.class: nginx
nginx.ingress.kubernetes.io/ssl-passthrough: "true"
nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
spec:
tls:
- hosts:
- openfaas.example.com
secretName: openfaas-tls
rules:
- host: openfaas.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: gateway
port:
number: 8080
应用 Ingress:
kubectl apply -f openfaas-ingress.yaml
# 获取 Ingress 的 EXTERNAL-IP(即 DO LB 的 IP)
kubectl get ingress -n openfaas openfaas-gateway -o wide
# 输出类似:openfaas-gateway openfaas.example.com 159.203.123.45 80,443 10s
配置 DNS(在 DigitalOcean 控制台):
- 添加 A 记录:
openfaas.example.com→159.203.123.45 - 等待 DNS 生效(通常 < 60 秒)
测试公网调用:
# 使用 curl 测试(自动跟随重定向到 HTTPS)
curl -X POST https://openfaas.example.com/function/hello-world -d "World"
# 预期输出:Hello, World! Time: 2024-06-15 14:22:33
4.5 监控与扩缩容:让函数真正“Serverless”
OpenFaaS 原生支持基于 Prometheus 指标的自动扩缩容。DigitalOcean 的 DOKS 集群默认已部署 Prometheus Operator,我们只需配置 HorizontalPodAutoscaler(HPA):
创建 hello-world-hpa.yaml :
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: hello-world
namespace: openfaas-fn
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: hello-world
minReplicas: 1
maxReplicas: 10
metrics:
- type: External
external:
metric:
name: openfaas_function_invocation_rate
selector:
matchLabels:
function_name: hello-world
target:
type: AverageValue
averageValue: 10
应用 HPA:
kubectl apply -f hello-world-hpa.yaml
# 查看 HPA 状态
kubectl get hpa -n openfaas-fn
# 输出应显示 TARGETS 列为 <unknown>/10,表示指标收集正常
验证扩缩容:
# 模拟高并发调用(100 个请求,每秒 10 个)
for i in {1..100}; do
curl -s -X POST https://openfaas.example.com/function/hello-world -d "LoadTest-$i" > /dev/null &
if (( i % 10 == 0 )); then sleep 1; fi
done
# 1 分钟后查看副本数变化
kubectl get deploy -n openfaas-fn hello-world
# 应从 1 副本增长到 3-5 副本
5. 常见问题与排查技巧实录:那些凌晨三点教会我的事
5.1 问题速查表:症状、根因、解决方案
| 症状 | 根因 | 解决方案 |
|---|---|---|
faas-cli list 返回 Unauthorized |
ServiceAccount 权限不足或 Token 过期 | 重新运行 kubectl create clusterrolebinding ,并用新 Token 登录 |
函数状态为 Ready ,但 curl 返回 503 Service Unavailable |
Gateway 的 readinessProbe 失败,通常是 TLS 证书路径错误 |
检查 openfaas-gateway.yaml 中 volumeMounts 的 mountPath 是否为 /run/secrets ,Secret 名称是否匹配 |
faas-cli logs -f func 报 dial tcp ...:4222: i/o timeout |
NATS Streaming Pod 未就绪或网络策略阻止 UDP | kubectl get pods -n openfaas 查看 nats Pod 状态; kubectl get networkpolicy -A 检查是否有拒绝 UDP 的策略 |
函数调用返回 504 Gateway Timeout |
Gateway 的 writeTimeout 小于函数 write_timeout |
在 hello-world.yml 中增加 environment.write_timeout: "120s" ,并 helm upgrade 或 kubectl set env 更新 Gateway |
Ingress 暴露后,HTTPS 访问正常,但 HTTP 返回 308 Permanent Redirect |
nginx.ingress.kubernetes.io/force-ssl-redirect: "true" 导致 |
删除该 annotation,或确保所有客户端都使用 HTTPS |
5.2 独家避坑技巧:来自三次生产事故的总结
技巧一:用 kubectl wait 替代 sleep 做部署同步
很多脚本用 sleep 30 等待 Pod 就绪,这极不可靠。正确做法是:
# 等待 Gateway Pod 全部 Running
kubectl wait --for=condition=ready pod -l app=gateway -n openfaas --timeout=120s
# 等待函数 Deployment 的 rollout 完成
kubectl wait --for=condition=available deploy/hello-world -n openfaas-fn --timeout=120s
kubectl wait 会实时监听 K8s 事件,一旦条件满足立即返回,比固定延时快 3-5 倍,且 100% 可靠。
技巧二:函数镜像 Tag 必须包含 Git Commit SHA
不要用 latest 或 v1.0 这样的模糊 Tag。每次 faas-cli build 前,先执行:
更多推荐



所有评论(0)