☰
Knative Bookstore 起步工程实战:基于 Knative 事件驱动架构的书店应用从零搭建指南
2026/10/14 16:38:33 网站建设 项目流程
  • 文档
  • 教程
  • 云原生

【免费下载链接】docs

User documentation for Knative components.

项目地址:https://gitcode.com/gh_mirrors/docs1/docs
点击查看免费下载

本文围绕 Knative 官方示例项目 Bookstore 的起步工程(start目录)展开,系统拆解一个事件驱动书店应用在搭建初期的全部要素:如何用setup.sh一键安装 Knative Serving/Eventing,如何用 StatefulSet 部署带持久化的 PostgreSQL 书评数据库,如何理解 Next.js 前端与 Node.js 后端在 CloudEvents 协议下的协作方式,以及如何为后续接入 Apache Camel K 的 Slack 通知和 Knative Sequence 工作流编排打好基础。读完本文,你将掌握该起步工程的目录结构、每个组件的初始代码与部署配置,并能顺利沿官方教程路线将其演进为完整的事件驱动应用。

起步工程全景:start目录的组成与定位

start目录(code-samples/eventing/bookstore-sample-app/start)是整个 Bookstore 教程的起点。它不像solution目录那样包含全部功能的完整实现,而是提供了初始代码结构与占位文件,供你边学边补齐。目录中每个子目录都是一类独立服务:

目录职责起始状态
db-service/存储书评与评论的数据库服务含完整 PostgreSQL 部署 YAML 与样例 SQL
frontend/基于 Next.js 的书店用户界面含完整前端工程与容器化配置
node-server/处理后端操作的 Node.js 服务含 CloudEvents 接入与 WebSocket 推送代码
slack-sink/基于 Apache Camel 的 Slack 通知集成含配置说明与 Pipe 示例
setup.sh一键安装 Knative 并部署前端、后端可直接执行

目录中并不存在sequence/子目录——Knative Sequence 工作流编排的完整实现位于 solution/sequence,它是教程后半段的目标之一。README 提到该组件,是为了让你提前了解整个应用的最终形态。

一键环境准备:setup.sh做了什么

起步工程在start目录根部提供了 setup.sh,它把"安装 Knative + 部署书店前后端"整合为一个交互式脚本。整体流程如下:

  1. 安装 Knative Serving:依次应用官方发布的 Serving CRD、核心组件清单,以及 Kourier 网络层清单;
  2. 配置默认 Ingress:通过kubectl patch将knative-serving命名空间下config-network配置映射的ingress-class合并为kourier.ingress.networking.knative.dev,使 Knative 默认使用 Kourier 作为流量入口;
  3. 安装 Knative Eventing:应用 Eventing 的 CRD 与核心组件清单;
  4. 安装消息基础设施:应用in-memory-channel.yaml(InMemoryChannel 通道)与mt-channel-broker.yaml(MT-Channel-Broker 代理),这是后续事件流传递的基础;
  5. 检查funcCLI:如果系统里没有func命令,脚本会提示按 Knative Functions 文档安装后退出;
  6. 等待组件就绪:使用kubectl wait分别等待knative-serving与knative-eventing命名空间下所有 Pod 进入ready状态(超时 300 秒);
  7. 部署前端:应用frontend/config目录中的 Deployment/Service,等待bookstore-frontend-svc的 LoadBalancer 地址就绪,并打印访问地址提示你确认页面;
  8. 部署后端:应用node-server/config/100-deployment.yaml,同样等待负载均衡就绪并确认返回 "Hello World!"。

脚本中SCRIPT_DIR通过$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )定位自身所在目录,保证kubectl apply -f "${SCRIPT_DIR}/frontend/config"这类相对引用在任何工作目录下都能正确解析。脚本还要求本地集群的 LoadBalancer 能力可用——注释里特别提到,使用 Kind 时需运行kind cloud-provider-kind之类的云控制器才能为 Service 分配外部 IP。

对初学者而言,setup.sh的价值在于把最容易出错的安装环节固化成了可复现的脚本;当你希望逐步理解每一层组件(Serving、Eventing、Broker/Channel)时,也可以拆开其中命令逐条手动执行。

db-service:用 StatefulSet 搭建持久化 PostgreSQL 书评库

数据库是书店应用的地基。db-service子目录(db-service/README.md)完整说明了如何用 Kubernetes 原生资源部署一个预置样例数据的 PostgreSQL 实例。

表结构与样例数据

核心表book_reviews的字段如下:

字段类型说明
idserial主键,自增
post_timetimestamp评论发布时间
contenttext评论正文
sentimenttext情感分析结果,取值限定为positive、neutral、negative

样例数据共 4 条:

idpost_timecontentsentiment
12020-01-01 00:00:00This book is great!positive
22020-01-02 00:02:00This book is terrible!negative
32020-01-03 00:01:30This book is okay.neutral
42020-01-04 00:00:00Mehneutral

数据库与表的定义(含sentiment取值约束)完整地写在 sample.sql 中,并通过 ConfigMap 注入初始化流程。

六个 YAML 文件的分工

db-service目录下按数字前缀编号的 6 个 YAML 文件各司其职,数字同时决定了kubectl apply -f db时的应用顺序:

  • 100-create-configmap.yaml:创建sql-configmapConfigMap,将sample.sql内容作为键值内嵌,供初始化 Job 挂载使用;
  • 100-create-secret.yaml:创建postgresql-secret,以 base64 形式保存POSTGRES_DB(mydatabase)、POSTGRES_USER(myuser)、POSTGRES_PASSWORD(mypassword)三个环境变量,供 StatefulSet 与 Job 通过secretKeyRef引用,避免密码明文出现在 Pod 定义中;
  • 100-create-volume.yaml:创建postgresql-pvc持久卷声明,accessModes为ReadWriteOnce,请求 1Gi 存储,这是数据库数据落盘的保障;
  • 200-create-postgre.yaml:创建名为postgresql的 StatefulSet(replicas: 1),镜像为quay.io/enterprisedb/postgresql,从 Secret 注入数据库名/用户/密码环境变量,监听 5432 端口,并把 PVCpostgresql-pvc挂载到/var/lib/postgresql/data——PostgreSQL 数据目录因此获得持久化;
  • 300-expose-service.yaml:创建无头 Service(clusterIP: None),通过标签选择器app: postgresql将 5432 端口暴露给集群内部,StatefulSet 的稳定网络标识(postgresql-0.postgresql)依赖它;
  • 400-create-job.yaml:创建一次性 Jobpostgresql-job,用同一个 PostgreSQL 镜像启动psql -h postgresql -U myuser -d mydatabase -f /sql/sample.sql,把 ConfigMap 中的 SQL 灌入数据库完成建表与数据初始化。Job 通过PGPASSWORD/PGUSER/PGDATABASE环境变量从 Secret 取值,restartPolicy: Never、backoffLimit: 5,并在完成后 50 秒自动清理(ttlSecondsAfterFinished: 50)。

部署与验证

在code-sample目录(注意不是db子目录内)执行:

kubectl apply -f db

随后监控 Pod 状态:

kubectl get pods

预期输出中postgresql-0处于Running(READY 1/1)。接着进入 Pod 并连接数据库:

kubectl exec -it postgresql-0 -- /bin/bash psql -U myuser -d mydatabase

连接成功后命令行提示符变为mydatabase=#,执行\dt应看到:

List of relations Schema | Name | Type | Owner --------+--------------+-------+-------- public | book_reviews | table | myuser (1 row)

为什么不用 Knative Service 部署数据库

db-serviceREADME 专门讨论了一个关键设计问题:数据库用 StatefulSet 而不是 Knative Service。理由有两点:

  1. 有状态 vs 无状态:数据库必须持久记忆数据。StatefulSet 提供稳定的网络标识(如postgresql-0)与持久卷绑定,Pod 重建后数据依然存在;而 Knative Service 面向的是"用完即弃"的请求型负载,可随时缩容到零、随时重建,天然不适合承载必须长期保留状态的数据库;
  2. 协议差异:Knative Serving 主要面向 HTTP 流量,而数据库通常使用自身的原生协议(如 PostgreSQL 的 TCP 5432),直接接入 Knative 并不顺滑。

值得补充的是:Knative Service 本身也支持挂载 Volume 与 PersistentVolume 来存储数据,官方 Serving 文档有专门讲解。但对于需要稳定网络身份、长生命周期和强一致性的数据库,StatefulSet 仍是更稳妥的默认选择。这一对比也揭示了 Knative 的适用边界——它不是要取代 Kubernetes 原生工作负载,而是与其各司其职。

frontend:Next.js 书店前端与容器化

前端工程位于 frontend,基于 Next.js 16 与 TailwindCSS,并引入next-themes(主题切换)、framer-motion(动效)与next-ws(WebSocket 支持)。

项目结构

  • app/:Next.js App Router 的主入口,含layout.tsx、page.tsx与全局样式。page.tsx通过ThemeProvider包裹Main组件;
  • client/:业务组件层,pages/Main.js负责页面骨架(书籍详情、评论表单、评论列表、连接状态徽标),components/下拆分出BookDetail、CommentForm、CommentList、CommentDisplay、Emoji、Toggle、Header等组件;
  • public/images/:书籍封面、头像等静态资源;
  • 工程级配置文件:next.config.mjs、package.json、postcss.config.js、tailwind.config.ts、tsconfig.json。

Main.js中内置了一个示例书籍对象(书名Building serverless applications on Knative,作者 Evan Anderson),用于渲染页面;页面顶部还有"Connected to node server"等连接状态徽标,以及提示"自己动手实现评论删除功能"的练习提醒——这正是教程刻意留出的扩展点。

本地运行与容器化

本地安装依赖并启动开发服务器:

npm install npm run dev

浏览器访问http://localhost:3000即可预览。构建镜像使用 Dockerfile 的多阶段方案:builder 阶段基于node:lts-alpine安装依赖并npm run build;runner 阶段仅拷贝public、.next/standalone与.next/static,以精简镜像体积,最终以PORT=8080启动。本地容器化命令:

docker build -t frontend . docker run -d -p 3000:3000 frontend

集群部署

100-front-end-deployment.yaml 定义了bookstore-frontendDeployment(副本数 1,容器端口 8080,并通过fieldRef注入POD_NAMESPACE环境变量)与bookstore-frontend-svcService(端口 80 → 8080,type: LoadBalancer)。setup.sh会先部署前端并等待其外部 IP 就绪,再提示你访问验证。

node-server:Node.js 后端与 CloudEvents 接入

后端服务位于 node-server,核心实现见 index.js。它把 Express 与 CloudEvents 协议绑定在一起,是理解"Knative 事件如何在应用侧落地"的最佳范本。

依赖方面,package.json声明了cloudevents(CloudEvents 编解码)、express(HTTP 框架)、express-ws(WebSocket 扩展)、pg(PostgreSQL 驱动)与cors。启动时,连接池指向集群内 PostgreSQL 的稳定 DNS 地址:

const pool = new Pool({ host: 'postgresql.default.svc.cluster.local', port: 5432, database: 'mydatabase', user: 'myuser', password: 'mypassword', });

服务暴露了三个关键端点:

  1. GET /:返回Hello, world!,用于部署后的连通性验证;
  2. WS /comments:WebSocket 端点。连接建立后立即查询SELECT * FROM book_reviews ORDER BY post_time DESC;并推送全部评论,之后每 1 秒推送一次(setInterval(sendComments, 1000)),断开时清理定时器——这是前端"实时评论列表"的数据来源;
  3. POST /insert:接收一个 CloudEvent,用HTTP.toEvent()解析请求头与请求体,从中取出data.reviewText与data.sentimentResult,生成post_time后插入book_reviews表,最后以 CloudEvent(type: com.example.reviews.inserted)形式返回确认;
  4. POST /add:接收 CloudEvent,当type === 'new-review-comment'时,通过process.env.K_SINK(Knative 注入到订阅服务环境变量中的 Broker 地址)把事件转发给 Broker。转发时显式携带ce-specversion: 1.0、ce-type: new-review-comment、ce-source: bookstore-eda等 CloudEvent 头,用fetch以 HTTP 方式投递,并根据响应码决定返回 200 还是 500。

/add端点正是整条事件链的起点:前端提交评论 → 后端以 CloudEvent 投递到 Broker → 后续由 Trigger/Sequence 分流处理(情感分析、坏词过滤、Slack 通知)。node-server的部署配置见 100-deployment.yaml:Deployment 副本数 1、容器端口 8000,配套node-server-svcLoadBalancer(80 → 8000)。

slack-sink:Apache Camel K 与 Slack 事件通知

slack-sink子目录(slack-sink/README.md)演示了 Knative Eventing 最典型的"连接第三方服务"场景:每当有类型为new-review-comment的 CloudEvent 进入 Broker,自动向指定 Slack 频道推送一条通知。这里用到的技术是 Apache Camel K 的Pipe与内置slack-sinkKamelet。

前置条件

  1. 安装 Camel K CLI(kamel):装完运行kamel version若报错,通常是把kamel二进制加入 PATH:export PATH=$PATH:<path-to-kamel-binary>;
  2. 在集群安装 Camel K:
kamel install --registry docker.io --organization <your-organization> --registry-auth-username <your-username> --registry-auth-password <your-password>

安装成功会看到类似Camel K installed in namespace default的输出;使用其他容器镜像仓库时需按官方 registry 文档调整参数; 3.准备 Slack Webhook:创建 Slack 工作区、频道与 App,启用 Incoming Webhook 并生成形如https://hooks.slack.com/services/XXXXXXX的 Webhook URL。create-workspace.md 给出了从建工作区、建频道到开启 Incoming Webhook 的完整操作路径,application.properties 中预留了slack.channel=#bookstore-owner与slack.webhook.url=两个占位配置。

创建测试 Broker

该步骤的 Broker 仅用于本小节测试:

kn broker create book-review-broker

成功会提示Broker 'book-review-broker' successfully created in namespace 'default'。也可以写 YAML:

apiVersion: eventing.knative.dev/v1 kind: Broker metadata: name: book-review-broker namespace: default

保存为new-knative-broker.yaml后执行kubectl apply -f new-knative-broker.yaml。

用 Pipe 连接 Broker 与 Slack

Pipe 是 Camel K 中连接事件源与目标的资源。下面这份slack-sink.yaml中,source指向book-review-broker并设置type: new-review-comment过滤(只处理该类型事件);sink引用内置slack-sinkKamelet,并传入目标频道与 Webhook URL:

apiVersion: camel.apache.org/v1 kind: Pipe metadata: name: bookstore-notification-service spec: source: ref: kind: Broker apiVersion: eventing.knative.dev/v1 name: book-review-broker properties: type: new-review-comment sink: ref: kind: Kamelet apiVersion: camel.apache.org/v1 name: slack-sink properties: channel: "#bookstore-owner" webhookUrl: "https://hooks.slack.com/services/YOUR/SLACK/WEBHOOK"

应用后:

kubectl apply -f slack-sink.yaml kubectl get pipe slack-sink-pipe

当输出显示PHASE: Ready、REPLICAS: 1时,Pipe 已就绪。注意把webhookUrl替换为你自己的地址,channel换成真实频道名。

触发通知验证

由于集群网络与安全策略通常不允许外部机器直接向 Broker 发 CloudEvent,需要先起一个临时 Pod 作为事件发射器:

kubectl run curler --image=radial/busyboxplus:curl -it --restart=Never

进入[root@curler:/]$提示符后,先查询 Broker 地址:

kubectl get broker book-review-broker

会得到形如http://broker-ingress.knative-eventing.svc.cluster.local/default/book-review-broker的 URL。然后发送带 CloudEvent 头的 POST 请求:

curl -v "<The URI to your broker>" \ -X POST \ -H "Ce-Id: review1" \ -H "Ce-Specversion: 1.0" \ -H "Ce-Type: new-review-comment" \ -H "Ce-Source: bookstore-web-app" \ -H "Content-Type: application/json" \ -d 'Hello from Knative!'

几秒后,Slack 频道中即会出现通知消息。这一步验证了 Knative Eventing 与外部 SaaS 服务的松耦合集成能力——事件生产者与消费者完全解耦,接入新服务只需要一个 Pipe 配置。

sequence:Knative Sequence 工作流编排(solution 中的完整形态)

start目录中该组件尚待实现,完整形态见 solution/sequence。Sequence 是 Knative 提供的顺序执行编排原语:事件按步骤列表依次流经各订阅服务,最后可把结果回发给 Broker。

100-create-sequence.yaml 定义了名为sequence的编排:

apiVersion: flows.knative.dev/v1 kind: Sequence metadata: name: sequence spec: channelTemplate: apiVersion: messaging.knative.dev/v1 kind: InMemoryChannel steps: - ref: apiVersion: serving.knative.dev/v1 kind: Service name: bad-word-filter - ref: apiVersion: serving.knative.dev/v1 kind: Service name: sentiment-analysis-app reply: ref: kind: Broker apiVersion: eventing.knative.dev/v1 name: bookstore-broker
  • channelTemplate声明 Sequence 底层为每个步骤创建 InMemoryChannel;
  • steps依次为bad-word-filter(坏词过滤,Knative Function)与sentiment-analysis-app(情感分析,Knative Function),事件按声明顺序流经两者;
  • reply把最后一步的输出回发到bookstore-broker,形成"处理完再回流 Broker"的闭环。

配套的 200-create-trigger.yaml 用 Trigger 把事件接入 Sequence:

apiVersion: eventing.knative.dev/v1 kind: Trigger metadata: name: sequence-trigger spec: broker: bookstore-broker filter: attributes: type: new-review-comment subscriber: ref: apiVersion: flows.knative.dev/v1 kind: Sequence name: sequence

Trigger 的filter只放行type: new-review-comment的事件,subscriber指向 Sequence。于是整条链路成为:评论提交(/add)→ Broker → Trigger → 坏词过滤 → 情感分析 → 结果回流 Broker → 再由其他 Trigger/Sink 消费(如 Slack 通知、数据库落库)。这正是事件驱动架构中"先加工、后分发"的典型范式。

从 start 到 solution:后续演进路线

对照 solution/README.md,完整版在起步工程之上还包含:

  • bad-word-filter/:坏词过滤 Knative Function(Sequence 第一步);
  • sentiment-analysis-app/:情感分析 Knative Function(Sequence 第二步);
  • sequence/:上述 Sequence 与 Trigger 编排;
  • 各组件完整版的solution.sh:一条命令安装并部署整个解决方案。

推荐的演进顺序与教程路线一致:先借助setup.sh跑通 Serving/Eventing 与前后端;随后按 db-service 指南补齐数据库;接着通过 Trigger/Sequence 接入两个 Knative Function;最后用 Camel K Pipe 打通 Slack 通知。完成全部步骤后,即可获得一个覆盖"事件生产 → 事件路由 → 事件加工 → 外部通知"全链路的事件驱动书店应用,并可随时与 solution 目录对照、验证自己的实现。

常见问题与排错提示

  • kamel version报命令找不到:kamel二进制不在 PATH 中,用export PATH=$PATH:<path-to-kamel-binary>补齐,或把二进制移动到 PATH 已有目录;
  • LoadBalancer 没有外部 IP:setup.sh等待bookstore-frontend-svc的loadBalancer.ingress超时会失败,通常是因为 Kind 等本地集群缺少云控制器,需先运行kind cloud-provider-kind之类的负载均衡器控制器;
  • kubectl wait超时:Knative Serving/Eventing 首次安装需拉取多个镜像,适当调大--timeout(脚本默认 300 秒)并确认网络可达容器镜像仓库;
  • 数据库初始化 Job 未完成:检查postgresql-0是否已Running且无头 Service 可解析,Job 依赖psql -h postgresql访问 StatefulSet 服务名,Pod 未就绪时 Job 会持续重试(backoffLimit: 5);
  • Slack 没收到通知:确认 Broker URL 正确、事件Ce-Type与 Pipe 过滤的type: new-review-comment完全一致、webhookUrl未被占位符替换。

小结

Knative Bookstore 起步工程把"事件驱动应用从零开始"所需的环境、数据层、服务层与集成层完整呈现在一个目录中:setup.sh固化环境搭建,db-service展示有状态服务在 Kubernetes 中的正确姿势,node-server示范 CloudEvents 的生产与消费,slack-sink演示与第三方服务的松耦合集成,sequence则揭示了事件加工编排的最终形态。逐一吃透这些组件的起始代码与配置,你就能沿着教程路线,亲手把它演进为一个生产可用的事件驱动书店应用,同时深入理解 Knative Serving 与 Eventing 的分工协作方式。

  • 文档
  • 教程
  • 云原生

【免费下载链接】docs

User documentation for Knative components.

项目地址:https://gitcode.com/gh_mirrors/docs1/docs
点击查看免费下载

相关推荐

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询