Kubeless 架构与使用

Kubeless 是最早的开源 Serverless 框架之一,它的核心思想是:用 Kubernetes 的 Custom Resources 定义函数

2016 年发布时,Kubeless 的设计非常前卫——它比 Knative 早了两年提出的类似概念。但随着 Knative 的崛起,Kubeless 的影响力逐渐下降。今天它更多是作为 Serverless 架构演进的历史见证,而非生产环境推荐。

设计理念

Kubeless 的核心设计理念是将函数视为 Kubernetes 的一等公民

flowchart TB
    subgraph Kubeless["Kubeless"]
        Function[Function CRD]
        Runtime[Runtime 控制器]
    end

    subgraph K8s["Kubernetes"]
        Deployment[Deployment]
        Service[Service]
        ConfigMap[ConfigMap]
    end

    Function --> Runtime
    Runtime --> Deployment
    Runtime --> Service
    Runtime --> ConfigMap

Function CRD

function_crd.yaml
apiVersion: kubeless.io/v1
kind: Function
metadata:
  name: my-function
spec:
  handler: handler.my_handler  # handler 文件名.函数名
  runtime: python3.8
  function: |
    def my_handler(event, context):
        print("Hello from kubeless")
        return event['data']
  deps: |
    requests==2.28.0
  timeout: 10
  runtime: python3.8

架构组件

核心控制器

flowchart TB
    subgraph 输入["Kubeless API"]
        Func[Function 定义]
        Trigger[Trigger 定义]
    end

    subgraph 控制器["Kubeless Controllers"]
        FuncCtrl[Function Controller]
        TriggerCtrl[Trigger Controller]
    end

    subgraph 资源["Kubernetes 资源"]
        Deploy[Deployment]
        Svc[Service]
        HPA[HorizontalPodAutoscaler]
    end

    Func --> FuncCtrl
    Trigger --> TriggerCtrl

    FuncCtrl --> Deploy
    FuncCtrl --> Svc
    TriggerCtrl --> Deploy

    Deploy --> HPA

运行时支持

语言运行时镜像特点
Pythonkubeless/python:1.0支持 2.7、3.4、3.6、3.8
Node.jskubeless/nodejs:1.0支持 6、8、10、12
Rubykubeless/ruby:1.0支持 2.4
Gokubeless/go:1.0编译型,性能最佳
PHPkubeless/php:1.0支持 7.2
.NETkubeless/dotnet:1.0支持 2.0

函数开发

Python 函数

python_handler.py
import requests

def hello(event, context):
    """
    Kubeless Python 函数签名
    - event: 事件数据
    - context: 运行时上下文
    """
    # 获取事件数据
    data = event['data']

    # 获取头部信息
    headers = event.get('headers', {})

    # 从上下文获取请求 ID
    request_id = context.RequestID()

    # 日志输出
    print(f"Processing request: {request_id}")

    return {
        'message': f"Hello, {data.get('name', 'World')}!",
        'request_id': request_id,
        'timestamp': context.Timestamp()
    }

Node.js 函数

node_handler.js
module.exports = {
  // Kubeless Node.js 函数签名
  handler: function(event, context) {
    const data = event.data;

    console.log('Received event:', data);

    // 获取函数名称
    const functionName = context.functionName;

    // 获取超时时间
    const timeout = context.getTimeout();

    return {
      message: `Hello, ${data.name || 'World'}!`,
      function: functionName,
      timestamp: new Date().toISOString()
    };
  }
};

Go 函数

go_handler.go
package main

import (
    "fmt"
    "github.com/kubeless/kubeless/pkg/events"
    "github.com/kubeless/kubeless/pkg/functions"
)

// Kubeless Go 函数签名
func Handler(event events.Event, context functions.Context) (interface{}, error) {
    data := event.Data

    // 类型转换
    name, ok := data.(map[string]interface{})["name"].(string)
    if !ok {
        name = "World"
    }

    fmt.Printf("Processing event: %v\n", event)

    return map[string]interface{}{
        "message": fmt.Sprintf("Hello, %s!", name),
        "function": context.FnName,
        "runtime": context.Runtime,
    }, nil
}

func main() {
    events.Start(Handler)
}

触发器

HTTP 触发器

http_trigger.yaml
apiVersion: kubeless.io/v1
kind: HTTPTrigger
metadata:
  name: my-http-trigger
spec:
  functionName: my-function
  gateway: nginx
  # 路由规则
  path: /api/my-function
  methods:
    - GET
    - POST
  # 认证
  authName: basic-auth
  # TLS
  tls: secret-tls

CronJob 触发器

cron_trigger.yaml
apiVersion: kubeless.io/v1
kind: CronJobTrigger
metadata:
  name: my-cron-trigger
spec:
  functionName: my-function
  schedule: "*/5 * * * *"  # 每 5 分钟
  # 并发策略
  concurrencyPolicy: Forbid  # Forbid / Allow / Replace

Kafka 触发器

kafka_trigger.yaml
apiVersion: kubeless.io/v1
kind: KafkaTrigger
metadata:
  name: my-kafka-trigger
spec:
  functionName: my-function
  topic: my-topic
  # Kafka 配置
  kafkaBroker:
    - kafka-svc:9092
  # 消费者配置
  consumerGroup: kubeless-consumer

Pub/Sub 触发器

pubsub_trigger.yaml
apiVersion: kubeless.io/v1
kind: PubSubTrigger
metadata:
  name: my-pubsub-trigger
spec:
  functionName: my-function
  topic: my-topic
  subscription: my-subscription

部署与管理

使用 Kubeless CLI 部署

kubeless_cli.sh
# 部署 Python 函数
kubeless function deploy my-function \
  --handler handler.my_handler \
  --runtime python3.8 \
  --from-file python_handler.py \
  --dependencies requirements.txt

# 部署 Node.js 函数
kubeless function deploy my-node-function \
  --handler node_handler.handler \
  --runtime nodejs12 \
  --from-file node_handler.js

# 更新函数
kubeless function deploy my-function \
  --from-file updated_handler.py

# 查看函数列表
kubeless function list

# 删除函数
kubeless function delete my-function

使用 kubectl 部署

kubectl_deploy.yaml
apiVersion: kubeless.io/v1
kind: Function
metadata:
  name: my-function
spec:
  handler: handler.my_handler
  runtime: python3.8
  function: |
    def my_handler(event, context):
        return {"message": "Hello"}
  deps: |
    requests==2.28.0
  resources:
    requests:
      memory: "128Mi"
      cpu: "100m"
    limits:
      memory: "256Mi"
      cpu: "500m"
  secrets:
    - my-secrets

函数调用

invoke.sh
# 调用函数
kubeless function call my-function \
  --data '{"name": "World"}'

# 查看日志
kubeless function logs my-function

# 暴露为外部服务
kubeless function expose my-function \
  --port 8080 \
  --target-port 8080

与 Knative 对比

维度KubelessKnative
发布时间2016 年2018 年
CRD 设计Function + Trigger 分立Service 统一抽象
扩缩容基础 HPAKPA(支持零缩容)
事件处理有限事件源完整的 Eventing
网络层无内置Istio 集成
维护状态有限维护活跃维护(CNCF 毕业)
学习曲线平缓陡峭但完整

迁移到 Knative

如果已经在使用 Kubeless,迁移到 Knative 的基本步骤:

migration.yaml
# Kubeless Function
apiVersion: kubeless.io/v1
kind: Function
metadata:
  name: my-function
spec:
  handler: handler.my_handler
  runtime: python3.8
  function: |
    def my_handler(event, context):
        return {"message": "Hello"}
knative_service.yaml
# Knative Service(等效实现)
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: my-function
spec:
  template:
    spec:
      containers:
        - image: python:3.8-slim
          command:
            - python
            - -c
            - |
              from flask import Flask, request, json
              app = Flask(__name__)

              def handler(event, context):
                  return {"message": "Hello"}

              @app.route("/", methods=["POST"])
              def main():
                  return handler(
                      {"data": request.get_json()},
                      type("Context", (), {"function_name": "my-function"})()
                  )

              if __name__ == "__main__":
                  app.run(host="0.0.0.0", port=8080)

术语表

术语英文解释
FunctionFunction CRDKubeless 的函数定义资源
TriggerTrigger事件触发器
RuntimeRuntime函数运行时的容器镜像
HandlerHandler函数的入口方法
CronJob TriggerCronJob Trigger定时触发的触发器类型

延伸思考

Kubeless 的历史价值大于实用价值。它的设计理念——将函数作为 Kubernetes 资源——直接影响了 Knative 的设计。

对于新项目,不推荐使用 Kubeless;对于已有项目,建议考虑迁移到 Knative 或其他更活跃维护的框架。

但理解 Kubeless 的设计有助于理解 Serverless 在 Kubernetes 上的演进历程:为什么 Knative 采用了不同的设计,以及 Knative 的设计改进了哪些方面。

接下来我们将对比主流 Serverless 框架,帮助你在实际项目中做出选择。