Claude Code × GCP Cloud Functions 完全指南 | 极速开发无服务器函数
用 Claude Code 高效开发 GCP Cloud Functions。从 HTTP/Pub/Sub/Firestore 触发器实现到本地测试、部署自动化,基于 Masa 的实战经验,附完整可运行代码示例。
Cloud Run 太过复杂 — 所以我选择了 Cloud Functions
我是 Masa,claudecode-lab.com 的开发者。
当我开始使用 GCP Serverless 时,考虑过 Cloud Run。灵活且基于容器——但对于**“仅接收一个 Webhook”** 或**“每晚运行一次批处理”**这样的任务来说,明显过于复杂。因此我转向了 Cloud Functions Gen2。
借助 Claude Code,我超过 90% 的 Cloud Functions 代码都是自动生成的。
Step 1: HTTP 触发器函数实现
import { http, HttpFunction } from "@google-cloud/functions-framework";
import { Request, Response } from "express";
import { Firestore } from "@google-cloud/firestore";
const db = new Firestore();
const VALID_TOKEN = process.env.API_SECRET_TOKEN;
// 请求体类型定义
interface ActionRequest {
userId: string;
action: string;
}
const handleAction: HttpFunction = async (req: Request, res: Response) => {
// 设置 CORS 头
res.set("Access-Control-Allow-Origin", "*");
if (req.method === "OPTIONS") {
res.set("Access-Control-Allow-Methods", "POST");
res.set("Access-Control-Allow-Headers", "Authorization, Content-Type");
res.status(204).send("");
return;
}
// 只允许 POST 请求
if (req.method !== "POST") {
res.status(405).json({ success: false, message: "方法不允许" });
return;
}
// Bearer Token 验证
const authHeader = req.headers.authorization ?? "";
if (!authHeader.startsWith("Bearer ") || authHeader.slice(7) !== VALID_TOKEN) {
res.status(401).json({ success: false, message: "未授权" });
return;
}
const body = req.body as Partial<ActionRequest>;
if (!body.userId || !body.action) {
res.status(400).json({ success: false, message: "userId 和 action 为必填项" });
return;
}
try {
// 写入日志到 Firestore
const logRef = await db.collection("action_logs").add({
userId: body.userId,
action: body.action,
timestamp: new Date(),
ip: req.ip,
});
res.status(200).json({ success: true, message: "操作已记录", logId: logRef.id });
} catch (err) {
console.error("Firestore 写入错误:", err);
res.status(500).json({ success: false, message: "服务器内部错误" });
}
};
http("handleAction", handleAction);
Step 2: Pub/Sub 触发器函数(异步事件处理)
import { cloudEvent, CloudEvent } from "@google-cloud/functions-framework";
import { MessagePublishedData } from "@google/events/cloud/pubsub/v1/MessagePublishedData";
import { Storage } from "@google-cloud/storage";
const storage = new Storage();
interface ImageUploadedMessage {
bucketName: string;
filePath: string;
}
cloudEvent<MessagePublishedData>("handleImageUploaded", async (event: CloudEvent<MessagePublishedData>) => {
const base64Data = event.data?.message?.data;
if (!base64Data) {
console.warn("未收到消息数据,跳过");
return;
}
// base64 → JSON 解码
const rawJson = Buffer.from(base64Data, "base64").toString("utf-8");
let payload: ImageUploadedMessage;
try {
payload = JSON.parse(rawJson) as ImageUploadedMessage;
} catch {
console.error("Pub/Sub 消息中的无效 JSON:", rawJson);
return; // 解析错误重试也无效,直接丢弃
}
const { bucketName, filePath } = payload;
try {
const [metadata] = await storage.bucket(bucketName).file(filePath).getMetadata();
console.log("文件元数据:", { 名称: metadata.name, 大小: metadata.size });
} catch (err) {
// 抛出异常会让 Pub/Sub 重试消息
console.error(`处理 ${filePath} 失败:`, err);
throw err;
}
});
Step 3: Firestore 触发器函数(响应文档变更)
import { onDocumentWritten, Change, FirestoreEvent } from "firebase-functions/v2/firestore";
import { QueryDocumentSnapshot } from "firebase-admin/firestore";
import * as admin from "firebase-admin";
admin.initializeApp();
const db = admin.firestore();
export const onUserWrite = onDocumentWritten(
"users/{userId}",
async (event: FirestoreEvent<Change<QueryDocumentSnapshot> | undefined, { userId: string }>) => {
const userId = event.params.userId;
const before = event.data?.before;
const after = event.data?.after;
// 创建事件
if (!before?.exists && after?.exists) {
const userData = after.data();
await db.collection("email_queue").add({
to: userData?.email,
template: "welcome",
createdAt: admin.firestore.FieldValue.serverTimestamp(),
status: "待发送",
});
return;
}
// 删除事件 — 复制到归档集合
if (before?.exists && !after?.exists) {
await db.collection("deleted_users").doc(userId).set({
...before.data(),
deletedAt: admin.firestore.FieldValue.serverTimestamp(),
});
}
}
);
Step 4: 本地测试与部署
# 本地启动
npm run build
npx @google-cloud/functions-framework --target=handleAction --port=8080
# 发送测试请求
curl -X POST http://localhost:8080 \
-H "Content-Type: application/json" \
-H "Authorization: Bearer test-token" \
-d '{"userId": "user-123", "action": "login"}'
# 部署到 GCP
gcloud functions deploy handleAction \
--gen2 \
--runtime=nodejs22 \
--region=asia-east1 \
--source=. \
--entry-point=handleAction \
--trigger-http \
--allow-unauthenticated \
--memory=512Mi \
--timeout=60s \
--set-secrets="API_SECRET_TOKEN=api-secret-token:latest"
4 个常见陷阱
| 陷阱 | 问题 | 解决方案 |
|---|---|---|
| 冷启动延迟 | 空闲实例启动需要 2–5 秒 | 关键函数设置 --min-instances=1 |
| 超时限制 | 最长 9 分钟(540 秒) | 通过 Pub/Sub 将大任务分块并行处理 |
| Secret Manager 权限 | 运行时 Permission denied | 赋予 secretmanager.secretAccessor 角色 |
| 内存不足 | 重型处理 OOM | 设置 --memory=2Gi --cpu=2 |
总结
| 触发器 | 使用场景 | 关键注意点 |
|---|---|---|
| HTTP | Webhook、API 端点 | 身份验证、CORS |
| Pub/Sub | 异步事件处理 | base64 解码、重试设计 |
| Firestore | 响应数据变更 | 避免无限循环 |
| Cloud Scheduler | 定时批处理 | OIDC 验证、时区管理 |
相关文章
#claude-code
#gcp
#cloud-functions
#typescript
#serverless
#pubsub
免费
免费 PDF:5 分钟看懂 Claude Code 速查表
只需留下邮箱,我们就会立即把这份 A4 一页速查表 PDF 发送给你。
我们会严格保护你的个人信息,绝不发送垃圾邮件。
本文作者
Masa
深度使用 Claude Code 的工程师。运营 claudecode-lab.com——一个涵盖 10 种语言、超过 2,000 页内容的科技媒体。
相关文章
Use Cases
Codex Automations 是什么?让 AI 在你睡觉时完成内容运营
用 Codex Automations 自动查看流量、选择主题、写文章、改善转化路径并部署网站的实用指南。
Use Cases
用 Claude Code 设计 Firestore:先从查询开始,而不是集合
使用 Claude Code 进行 Firestore query-first 设计:schema、索引、成本、安全规则与 TypeScript 示例。
Use Cases
Claude Code × GCP Cloud Run 完全指南 | 无服务器容器自动部署
使用 Claude Code 加速 GCP Cloud Run 部署。完整指南含真实代码示例:Dockerfile 生成、自动扩缩容、CI/CD 流水线和 Secret Manager 集成。