首页
/ 在 GCP 上用 Terraform 部署 LiteLLM:Cloud Run + Cloud SQL + Memorystore 全栈实践

在 GCP 上用 Terraform 部署 LiteLLM:Cloud Run + Cloud SQL + Memorystore 全栈实践

2026-09-07 12:34:04作者:牧宁李

LiteLLM 的 GCP Terraform 栈(位于 terraform/litellm/gcp)把开源版与组件化版 LiteLLM Proxy 一次性部署到 Google Cloud:通过 Cloud Run v2 运行 gateway / backend / ui 三个服务与一个数据库迁移 Job,搭配 Cloud SQL for PostgreSQL(含只读副本)、Memorystore Redis、GCS Bucket、Secret Manager 和外部 HTTP(S) 负载均衡。阅读本文后,你将掌握如何用 DeployStack 向导完成约 20~25 分钟的首轮部署、正确配置 GHCR 镜像透传、注入 provider API 密钥与 proxy_config、开通 TLS,以及安全地整套卸载。本教程以 examples/default/TUTORIAL.md 为主体骨架,并结合该目录下 README.mdvariables.tf 与各 .tf 实现做源码级纵深解读。

这套栈会为你部署什么

一次 terraform apply(或一次 deploystack install)会在你选定的 GCP 项目中产出如下全栈资源,各 .tf 文件职责可从下表与 README.md 的 Files 表中对应:

组件 说明 对应源码文件
VPC + 私有服务访问 + Serverless VPC connector 让 Cloud Run 能访问内网 IP 资源 network.tf
Cloud SQL for PostgreSQL 主实例 + 跨可用区只读副本,密码鉴权托管于 Secret Manager cloudsql.tf
Memorystore Redis 缓存与限流,仅内网 IP redis.tf
GCS Bucket 私有、版本化、统一 IAM,以 GCS_BUCKET_NAME 暴露 gcs.tf
Secret Manager LITELLM_MASTER_KEYDATABASE_PASSWORD 等敏感项 secrets.tf
Cloud Run v2 服务 gateway(4000)、backend(4001)、ui(3000),共享运行时服务账号 cloudrun.tf
Cloud Run Job litellm-migrations:执行 prisma migrate deploy 后退出 cloudrun.tfbootstrap.tf
外部全局 HTTP(S) LB Serverless NEG + URL map 路径路由 load_balancer.tf

路径路由是这套栈的关键设计,它复刻了 helm chart 的 ingress 转发语义。URL map 将 LLM 数据面前缀路由到 gateway,将 UI 静态资源路径路由到 ui,其余全部交给 backend。路径前缀表在 locals.tf 中逐条列出,源码注释明确说明它镜像自 gateway/routes/allowlist.py 与 helm ingress——例如 /v1/chat/*/v1/completions*/v1/responses*/v1/rerank*/v1/messages*/anthropic/*/azure/* 等进 gateway;//_next/*/assets/*/ui/* 进 UI。

镜像说明:四个组件镜像分别为 litellm-gatewaylitellm-backendlitellm-uilitellm-migrations(后者是精简镜像,仅由一次性 Cloud Run Job 使用,执行 prisma migrate deploy)。升级 LiteLLM 版本时四个镜像应同步更新。

前置准备:项目、计费与鉴权

  1. 打开 examples/default/TUTORIAL.md 对应的部署流程,选择一个目标 GCP 项目,确认该项目已开启结算(billing)。本栈会创建计费资源:Cloud SQL、Memorystore、以及一个 LB 的 anycast IP。
  2. gcloud 需已完成认证(gcloud auth login),并用选定项目作为当前项目。

第一步:启用所需 API

目标项目中需要启用如下 9 个 API,可直接执行:

gcloud services enable \
  run.googleapis.com \
  sqladmin.googleapis.com \
  redis.googleapis.com \
  secretmanager.googleapis.com \
  vpcaccess.googleapis.com \
  compute.googleapis.com \
  servicenetworking.googleapis.com \
  storage.googleapis.com \
  artifactregistry.googleapis.com

这是整个部署的技术前提,README 的 Quick Start 一节同样把"必需 API 已启用"列为硬性前置条件。

第二步:创建指向 GHCR 的 Artifact Registry 透传仓库

Cloud Run 只接受来自 Artifact Registry、gcr.iodocker.io 的镜像,在 apply 阶段就会直接拒绝 ghcr.io URI。而 LiteLLM 的四个镜像上游都托管在 GHCR,因此每个项目需要一次性创建一个指向 GHCR 的 remote 模式的 Artifact Registry 仓库:

gcloud artifacts repositories create litellm \
  --repository-format=docker \
  --location=us-central1 \
  --mode=remote-repository \
  --remote-repo-config-desc="GitHub Container Registry passthrough" \
  --remote-docker-repo=https://ghcr.io
  • 若仓库已存在,命令会以明确错误退出,直接继续即可。
  • 当 DeployStack 提示 image_registry 时,填写 <region>-docker.pkg.dev/<your-project>/litellm/berriai(替换为你的 region 与项目名)。shipped 的默认值里含 PROJECT_ID 占位符,不修改的话 apply 阶段会失败——这一约束也写进了模块侧的 image_registry 变量注释(variables.tf)。

关于镜像拉取的若干重要细节:

  • 四个镜像 URI 由 image_registry + image_tag 组合而成(见 locals.tf);需要单组件覆盖时可分别设置 gateway_image / backend_image / ui_image / migrations_image
  • 栈创建的运行时服务账号不需要 roles/artifactregistry.reader——Cloud Run 拉镜像走的是项目级 serverless agent(service-<project-num>@serverless-robot-prod.iam.gserviceaccount.com),而非运行时 SA。
  • 如需完全隔离(air-gapped)环境,可先把镜像镜像到普通 AR 仓库而不是 remote repo:
for c in gateway backend ui migrations; do
  docker pull ghcr.io/berriai/litellm-$c:<tag>
  docker tag  ghcr.io/berriai/litellm-$c:<tag> \
              us-central1-docker.pkg.dev/$PROJECT/litellm/$c:<tag>
  docker push us-central1-docker.pkg.dev/$PROJECT/litellm/$c:<tag>
done

随后设置 image_registry = "us-central1-docker.pkg.dev/$PROJECT/litellm"(去掉 /berriai 组织段——镜像布局没有 org 段)。

第三步(可选):预设租户密钥与 License

栈在你不提供 LITELLM_MASTER_KEY 时会自动生成一个。如果你有企业 License,或希望使用预先选定的 master key,请在运行安装器之前将它们导出为 TF_VAR_* 环境变量,这样值会进入 Secret Manager,而不会落进 terraform.tfvars

export TF_VAR_litellm_master_key="sk-..."   # 可选;缺省时自动生成
export TF_VAR_litellm_license="lic-..."     # 可选;没有则不启用企业版
export TF_VAR_ui_password="..."             # 可选;缺省时 UI 登录回退用 master_key

试用部署可完全跳过此步。推荐用 TF_VAR_* 环境变量而非 tfvars 文件的原因很实际:写进 tfvars 文件的值会进入 terraform.tfstate,甚至可能被提交到示例文件中。从源码看,secrets.tf 中的实现是:未传 litellm_master_key 时用 random_password 生成 48 位随机串,拼出 sk-${random_password.master_key.result} 写入 master-key 的 Secret 版本;litellm_license 为空则不创建 license Secret,gateway/backend 以纯 OSS 模式运行(不注入 LITELLM_LICENSE);ui_password 为空时同样不创建,UI 登录回退使用 master key。

第四步:运行安装器

DeployStack 会依次询问 project、region、tenant、env、image tag、image_registry 和 TLS posture,然后执行 terraform apply。如果你想先看提示定义,可打开 examples/default/deploystack.json——其中可以看到各提示的校验规则,例如 tenant 要求 ^[a-z][a-z0-9-]{0,20}$(1~21 位小写 kebab-case,字母开头),env 要求 ^[a-z][a-z0-9-]{0,8}$

deploystack install

首轮 apply 大约耗时 20~25 分钟,其中大部分时间是 Cloud SQL 的预配。litellm-migrations 这个 Cloud Run Job 会在数据库就绪后自动触发(由 bootstrap.tf 编排),只有迁移完成后 gateway、backend、ui 才会启动对外服务。apply 返回时,整套栈就已经在服务流量了。

不借助 DeployStack 的手动路径同样可行——examples/default/main.tf 就是一个薄 root:只配置了 google/google-beta provider 并调用 ../../ 的模块。手动 Quick Start 为:

cd terraform/litellm/gcp/examples/default
cp terraform.tfvars.example terraform.tfvars
# 编辑:project_id、region、tenant、env、image_registry、proxy_config、gateway_extra_secrets 等
terraform init
terraform apply

examples/default/terraform.tfvars.example 内含非常完整的分段注释,覆盖了全部常用变量(含 proxy_config、OpenTelemetry 等),是最贴近实操的参考。

第五步:获取 LB 地址并登录 UI

terraform output lb_url

试用部署(allow_plaintext_lb=true)下返回的是 http://<lb-ip>。UI 位于 /ui,用用户名 admin 加 master key 登录;master key 从 Secret Manager 读取:

gcloud secrets versions access latest \
  --secret="$(terraform output -raw master_key_secret_id)"

examples/default/outputs.tf 还暴露了更多输出:lb_ip(LB 全局 anycast IP)、gateway_service_url / backend_service_url / ui_service_url(绕过 LB 直连各 Cloud Run 服务)、cloudsql_writer_ip / cloudsql_reader_ipredis_endpointgcs_bucketdb_password_secret_id,以及用于兜底手动重跑的 migration_run_command(break-glass 手动执行迁移命令)。

第六步:从 HTTP 过渡到 HTTPS(TLS)

Terraform 侧默认 allow_plaintext_lb=false:当 lb_domains 为空时,terraform plan 会直接失败,强制你在"提供域名签发 Google 托管证书"或"显式选择纯 HTTP"之间二选一(相关前提校验见 load_balancer.tf)。

如果你为了引导部署选了 allow_plaintext_lb=true,但线上要 HTTPS,按三步走:

  1. 把域名的 DNS A 记录指向 LB IP(terraform output -raw lb_ip)。
  2. 设置 lb_domains 并移除 allow_plaintext_lb,重新 apply:
terraform apply \
  -var 'lb_domains=["proxy.example.com"]'
  1. 结果:新增一条 443 转发规则 + Google 托管证书(覆盖 lb_domains 所列每个域名);原 80 转发规则被改写为对 HTTPS 的永久 301 跳转,HTTP 客户端被自动升级。

DNS 传播完成后,Google 托管证书会停留在 PROVISIONING 状态约 15~60 分钟,可通过以下命令观察状态:

gcloud compute ssl-certificates describe <tenant>-litellm-<env>-cert

纯 HTTP 模式仅用于短生命周期试用 / 开发栈;不设 allow_plaintext_lb=truelb_domains=[] 时,plan 会以清晰错误中止。

第七步:接入 provider API 密钥并配置模型列表

Provider 密钥(OpenAI、Anthropic 等)应该放在 Secret Manager,而不是 terraform.tfvars。先在 Secret Manager 创建密钥,再在 gateway_extra_secrets 中引用它的资源 ID,最后重新 apply:

echo -n "sk-proj-..." | gcloud secrets create openai-api-key --data-file=-

编辑 terraform.tfvars

gateway_extra_secrets = {
  OPENAI_API_KEY = "projects/<your-project>/secrets/openai-api-key"
}
proxy_config = {
  model_list = [
    {
      model_name = "gpt-4o"
      litellm_params = {
        model   = "openai/gpt-4o"
        api_key = "os.environ/OPENAI_API_KEY"
      }
    },
  ]
}

然后 terraform apply。这里有个关键机制要讲透:proxy_configlocals.tf 中被 yamlencode 序列化,上传到专用 GCS bucket 的 config.yaml,再通过 Cloud Run v2 的 gcsfuse 卷以只读方式挂载到 gateway/backend 的 /etc/litellm,同时自动注入 CONFIG_FILE_PATH=/etc/litellm/config.yaml。此外 YAML 的 md5 哈希被写成环境变量 PROXY_CONFIG_HASH——只改 proxy_config 也会强制生成新的 Cloud Run revision,否则 gcsfuse 要到下次无关的 revision 滚动时才会读到新文件(这个"配置漂移"陷阱在 variables.tf 的 proxy_config 注释 中有完整说明)。

model_list 中的 api_key = "os.environ/OPENAI_API_KEY" 由 LiteLLM 在容器环境变量里解析(os.environ/<NAME> 语法),而 OPENAI_API_KEY 则由 gateway_extra_secrets 从 Secret Manager 注入。general_settings 里同样的语法可写 master_key = "os.environ/LITELLM_MASTER_KEY"database_url = "os.environ/DATABASE_URL"

Extra env 与 secret 的完整姿势

非敏感环境变量直接落地 Cloud Run service spec:

gateway_extra_env = {
  LANGFUSE_HOST = "https://us.cloud.langfuse.com"
}

backend 常用变量(SSO 跳转、文档品牌、UI 管理员名)见 terraform.tfvars.example

backend_extra_env = {
  AUTO_REDIRECT_UI_LOGIN_TO_SSO = "true"
  DOCS_TITLE                    = "Acme LiteLLM"
  UI_USERNAME                   = "admin"
}

敏感值先建 Secret,再引用资源 ID:

gateway_extra_secrets = {
  OPENAI_API_KEY    = "projects/my-gcp-project/secrets/openai-api-key"
  ANTHROPIC_API_KEY = "projects/my-gcp-project/secrets/anthropic-api-key"
}

三条必须记住的约束:

  1. 只传裸的 secret 资源 IDprojects/.../secrets/openai-api-key),绝不能带 /versions/<n> 版本后缀——Cloud Run 的 secret_key_ref 绑定与栈内 IAM 授权都会拒绝版本后缀,版本始终解析为 latest。需要锁定版本时,只能直接改 cloudrun.tf 里的 local.gateway_extra_secret_kv,为该条目设 version = "3"
  2. 每个被引用的 secret,Cloud Run 运行时 SA 都会自动获得 roles/secretmanager.secretAccessor(IAM 授权逻辑在 iam.tf)。
  3. gateway_extra_secretsbackend_extra_secrets 形状完全相同,只是分别施加到 gateway / backend。

进阶一:数据库鉴权为何用密码而非 GCP IAM

LiteLLM 侧的 init_iam_db_url_from_env() 通过 boto3 铸造的是 AWS RDS token,并不支持 GCP IAM。要在 Cloud Run 中对 Cloud SQL 做 IAM 鉴权,需要把 cloud-sql-proxy 作为 sidecar 注入,会让 service spec 复杂化。因此这套栈统一采用密码鉴权

  • 随机密码生成后存入 Secret Manager(<name>-db-password)。
  • 每个 Cloud Run 服务通过 value_source.secret_key_refDATABASE_PASSWORD 方式拿到密码。
  • 容器 entrypoint shim 在 exec uvicorn 前,用 DATABASE_HOST / DATABASE_PASSWORD 组装出 DATABASE_URL(与 DATABASE_URL_READ_REPLICA)——所以密码永远不会出现在 service spec 或日志里。

若你确需 GCP 原生 IAM 鉴权,可在 template.template.containers 下追加 cloud-sql-proxy sidecar 容器(Cloud Run v2 支持多容器),再用代理的 Unix socket 替换基于密码的 URL。

进阶二:OpenTelemetry v2 与 Redis 传输加密

  • OTel v2 完全由 otel_endpoint 门控:为空(默认)时容器环境里不落任何 OTel 相关变量;一旦设置,gateway 和 backend 都会获得 LITELLM_OTEL_V2=true 及完整的 OTEL_* 块,其中 OTEL_SERVICE_NAME 按组件打标为 <tenant>-litellm-<env>-gateway / -backend,确保 span 落在正确的调用段上。可调项见 variables.tfotel_exporterotlp_http / otlp_grpc / console)、otel_environment_name(默认 var.env)、otel_headers_secret(存形如 Authorization=Bearer <token> 的收集器鉴权头)、otel_capture_message_content(默认 no_content,只有审计过落库内容后才建议改为 prompt_and_completion,因为 prompt/补全通常是敏感数据)。
  • Memorystore 以 transit_encryption_mode = "SERVER_AUTHENTICATION" 运行,Proxy 通过 rediss:// 连接;实例的自签 CA 证书(server_ca_certs[0].cert)以 REDIS_CA_PEM_B64 送到 gateway/backend,entrypoint 脚本解码到 /tmp/redis-ca.pem 并让 REDIS_SSL_CA_CERTS 指向它,无需额外配置。若日后换成外部 Redis,覆盖 REDIS_HOST/REDIS_PORT 并调整这些 env 即可(详见 redis.tf 与 README 的 Redis encryption 一节)。
  • 此外模块已自动导出 REDIS_HOST / REDIS_PORT / REDIS_SSL,Proxy 会回退使用这些变量做跨 pod 限流、支出统计与 pod 锁管理;terraform.tfvars.example 中的 coordination_redis 块只在你想协调到模块未托管的 Redis 时才需要设置,会覆盖 REDIS_* 环境变量回退。

进阶三:企业版计费指标(可选)

License 门控的请求计量同样完全由 billing_metrics_endpoint 门控:为空(默认)时容器里不加任何计费 env,存量部署完全不受影响。设置后,gateway 与 backend 会通过 OTLP/HTTP 导出可计费请求计数,并用随部署签发的 mTLS 客户端证书向收集器鉴权。栈把证书 PEM 分别写入各自 Secret Manager 条目、为运行时 SA 授予 accessor,再以 Cloud Run secret env 注入 LITELLM_BILLING_METRICS_CLIENT_CERT / _CLIENT_KEY(以及可选的 _CA_CERT),无需卷挂载:

billing_metrics_endpoint = "https://telemetry.litellm.ai/v1/metrics"
export TF_VAR_billing_metrics_client_cert_pem="$(cat client.crt)"
export TF_VAR_billing_metrics_client_key_pem="$(cat client.key)"

billing_metrics_ca_cert_pem 只对私有/测试收集器需要(其 CA 不在系统信任库中);对 telemetry.litellm.ai 保持留空即可。计量依赖企业 License,需与 litellm_license 配套。调整导出节奏可通过 gateway_extra_env / backend_extra_env 设置 LITELLM_BILLING_METRICS_EXPORT_INTERVAL_MS

多租户与资源命名

栈创建的每个资源都以 ${tenant}-litellm-${env} 命名(或再带资源后缀,由 locals.tf 统一计算),只要 (tenant, env) 组合不同,多个租户、多个环境可以共存于同一项目:

tenant env 示例资源名
acme stage acme-litellm-stage-gateway
acme prod acme-litellm-prod-master-key
globex dev globex-litellm-dev-license

基于示例 root 做单租户实例时,只需改变 tenant slug、env 与两个预签发密钥:

cd terraform/litellm/gcp/examples/default
export TF_VAR_litellm_master_key="sk-..."   # 该租户的 master key
export TF_VAR_litellm_license="lic-..."     # 其 LITELLM_LICENSE

terraform apply \
  -var "project_id=my-gcp-project" \
  -var "region=us-central1" \
  -var "tenant=acme" \
  -var "env=stage"

如果要从单一配置管理多个租户,用 for_each 调用模块而不是每个租户一个 root——之所以可行,正是因为模块没有声明 provider block(provider 配置归调用方所有,见 "Using as a module")。需要注意模块的 versions.tf 声明 google/google-beta 时没有 configuration_aliases,因此 for_each 的每个实例都会使用同一份默认 provider 配置,即同一个项目、region 与凭证;跨项目/跨 region 扇出仍须各自提供独立 root 或 fork 模块加 configuration_aliases

资源标签:模块会把自己固定的 litellm-stackmanaged-by 标签打到所有支持标签的资源上,并合并 var.labels(见 locals.tf),对应 AWS 栈的 tags 输入。

整套卸载与数据保留开关

deploystack uninstall

两个默认开启的"绊线"用于防止 terraform destroy 误删数据:

  • cloudsql_deletion_protection(Cloud SQL 主 + 副本,默认 true)——destroy 会以明确错误失败,而不是悄悄删库。真想让数据库消失,需先在 terraform.tfvars 中翻转为 false 并 apply 一次再卸载。
  • gcs_force_destroy(承载请求日志归档、/v1/files 内容与 GCS 缓存后端的 bucket,默认 false)——对非空 bucket 执行 destroy 会失败。可接受的语义与实现细节见 variables.tf

只有短期 / CI 环境、能接受数据丢失时才翻转这两项。

把它当模块复用的姿势

terraform/litellm/gcp 目录本身即一个无 provider 块的模块,调用方持有 provider 配置。可以从你自己的配置里用 countfor_eachdepends_on 或伪装服务账号/指向不同项目的 provider 来调用:

provider "google" {
  project = "my-gcp-project"
  region  = "us-central1"
}
provider "google-beta" {
  project = "my-gcp-project"
  region  = "us-central1"
}

module "litellm" {
  source = "github.com/BerriAI/litellm//terraform/litellm/gcp?ref=<tag>"

  project = "my-gcp-project"
  region  = "us-central1"
  tenant  = "acme"
  env     = "prod"
  # ...variables.tf 中的任意输入...
}

模块会自动继承调用方默认的 google 与 google-beta provider,两个都需在调用方声明。examples/default/ 只暴露了一组精选变量面;需要更细粒度旋钮(单组件 CPU/内存/实例数、Cloud SQL tier/edition、Memorystore tier、单组件镜像 pin)时,直接在 examples/default/main.tfmodule "litellm" 块上设置,或在自己的配置中调用模块——完整输入清单见 variables.tf,例如 gateway 默认 1000m CPU / 4Gi 内存、实例 1~10、单实例并发 80(低于默认 80 可避免长流式请求钉住 worker 数十秒导致过度扩容);Cloud SQL 默认 db-custom-2-7680 / ENTERPRISE / POSTGRES_16ENTERPRISE_PLUS 仅接受 db-perf-optimized-* tier 且成本约 3 倍,切换时须同步改 db_tier);Memorystore 默认 STANDARD_HA / 1GiB。

小结与延伸阅读

至此,你已经走通了 LiteLLM on GCP 从 API 启用、GHCR 镜像透传、DeployStack 一键安装,到密钥注入、HTTPS 升级与数据安全卸载的完整闭环。本部署栈与同仓库的 AWS 栈、helm chart 在变量语义上刻意对齐(如 min/max 实例数与 helm HPA 的 min/maxReplicas 对应、gcs_force_destroy 对应 AWS 的 s3_force_destroy),理解本教程后迁移其他平台的部署经验成本很低。

更深入的内容建议继续阅读:

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391