在传统的 Kubernetes 部署中,即使应用长时间处于闲置状态,也会保持至少一个或多个 Pod 运行,这导致了计算资源的浪费。Serverless 架构的核心优势之一就是能够按需启动和关闭应用,实现“零副本”待机。Knative,作为构建在 Kubernetes 之上的 Serverless 平台,完美地解决了这一问题。
本文将聚焦于 Knative Serving 组件,演示如何部署一个服务,使其在没有流量时自动缩容到零副本,并在接收到请求时快速拉起。
1. Knative 简介与核心机制
Knative 是 Google 开源的一套 K8s 扩展,主要包含 Serving、Eventing 和 Functions 三个部分。Knative Serving 提供了请求驱动的计算模型,通过以下关键机制实现零副本伸缩:
- Activator (激活器): 当服务缩容到零时,所有流量会首先被路由到 Activator。Activator 会缓存请求,并向 Autoscaler 发出信号,要求启动 Pod。
- Autoscaler (自动伸缩器): 负责监控服务的请求并发量,并根据配置策略(如目标并发数)动态调整 K8s Deployment 的副本数。
2. 环境准备与 Knative 安装(使用 Kourier 网络)
假设您已拥有一个可用的 Kubernetes 集群(v1.24+)。我们将使用 Knative 官方推荐的 Kourier 作为网络层,因为它比 Istio 更轻量。
步骤 2.1:安装 Knative Serving Core
# 1. 安装 Knative Serving CRDs
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.13.0/serving-crds.yaml
# 2. 安装 Knative Serving Core Components
kubectl apply -f https://github.com/knative/serving/releases/download/knative-v1.13.0/serving-core.yaml
步骤 2.2:安装 Kourier Networking
# 3. 安装 Kourier Network Layer
kubectl apply -f https://github.com/knative/net-kourier/releases/download/knative-v1.13.0/kourier.yaml
# 4. 配置 Knative 使用 Kourier 作为默认 Ingress
kubectl patch configmap/config-network \n --namespace knative-serving \n --type merge \n --patch '{"data": {"default-ingress-class": "kourier.ingress.networking.knative.dev"}}'
# 5. 等待所有 Knative Pod 启动完毕
kubectl wait --for=condition=ready pod -l '!job-name' -n knative-serving --timeout=300s
3. 部署零副本待机应用示例
我们使用一个简单的 Python Flask 应用作为示例。
步骤 3.1:创建 Dockerfile 和应用代码
创建一个简单的 app.py:
# app.py
from flask import Flask
import os
app = Flask(__name__)
@app.route('/')
def hello():
return f"Hello from Knative! Pod: {os.environ.get('KUBERNETES_POD_NAME', 'Unknown')}"
if __name__ == '__main__':
# Knative 通常使用 PORT 环境变量指定的端口,默认为 8080
app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
创建一个 Dockerfile:
# Dockerfile
FROM python:3.10-slim
WORKDIR /app
COPY app.py .
RUN pip install flask gunicorn
CMD ["python", "app.py"]
将镜像构建并推送到容器仓库(例如 docker.io/your_user/knative-zero-scale:latest)。
步骤 3.2:定义 Knative Service
Knative Service 是其核心抽象。创建一个 service.yaml 文件。
注意: Knative 默认就支持缩容到零,除非您明确设置 minScale: 1。下面的配置确保其默认行为。
# service.yaml
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
name: zero-scale-demo
namespace: default
spec:
template:
metadata:
annotations:
# 关键注解:设置目标并发数,用于控制何时扩容。默认 100
autoscaling.knative.dev/target: "10"
# 关键注解:定义从零启动时的最小实例数 (这里默认为 0,但可以显式设置)
autoscaling.knative.dev/minScale: "0"
spec:
containers:
- image: docker.io/your_user/knative-zero-scale:latest # 替换为您的镜像地址
步骤 3.3:部署与验证
应用 Knative Service:
kubectl apply -f service.yaml
获取 Kourier Ingress 网关地址和 Knative Service URL:
# 获取 Knative Ingress IP/Hostname
export KNATIVE_INGRESS_IP=$(kubectl -n kourier-system get service kourier -o jsonpath='{.status.loadBalancer.ingress[0].ip}' || \
kubectl -n kourier-system get service kourier -o jsonpath='{.status.loadBalancer.ingress[0].hostname}')
# 获取 Knative Service 域名
export SERVICE_URL=$(kubectl get ksvc zero-scale-demo -o jsonpath='{.status.url}')
echo "Ingress IP: $KNATIVE_INGRESS_IP"
echo "Service URL: $SERVICE_URL"
验证零副本待机:
几分钟后,查看 Pod 状态。您会发现该服务的 Pod 数量为 0。
kubectl get pods -l serving.knative.dev/service=zero-scale-demo
# 预期输出:No resources found in default namespace.
触发拉起 (Scale Up):
发送请求到 Knative Service (需要将请求头的 Host 设置为 Knative Service 的域名,路由到 Kourier 网关 IP)。
curl -H "Host: ${SERVICE_URL#*//}" http://$KNATIVE_INGRESS_IP
# 第一次请求可能经历短暂的冷启动延迟。
立即查看 Pod 状态:
kubectl get pods -l serving.knative.dev/service=zero-scale-demo
# 预期输出:会看到一个新的 Pod 正在 Running 状态 (e.g., zero-scale-demo-xxxxx-deployment-xxxxx-xxxxx 1/1 Running)
验证自动缩容 (Scale Down):
停止发送请求。如果 Pod 在一段时间(默认为 300 秒,可配置)内没有接收到新的流量,Knative Autoscaler 会指示 K8s 将副本数再次降为零。
4. 总结
通过 Knative Serving,我们成功地将 Serverless 的核心能力(按需启动、缩容到零)引入到了标准的 Kubernetes 环境中。这使得团队能够更有效地管理资源,仅在实际需要时才消耗计算资源,完美地结合了 K8s 的容器编排能力与 Serverless 的弹性优势。
汤不热吧