Online Boutique Helm Chart 部署实战:从默认安装到 Service Mesh 安全加固与 Spanner 后端切换
【免费下载链接】microservices-demoSample cloud-first application with 10 microservices showcasing Kubernetes, Istio, and gRPC.项目地址: https://gitcode.com/GitHub_Trending/mi/microservices-demo
本篇技术指南基于 helm-chart/README.md 展开,介绍如何在 Kubernetes 上通过 Helm 一键部署 Google Cloud 官方示例应用 Online Boutique(10 个微服务、gRPC、Istio 服务网格),覆盖默认安装、高级安全场景(Service Accounts、AuthorizationPolicies、NetworkPolicies、Sidecar)、cartservice 后端从 Redis 切换至 Cloud Spanner 等核心配置,并结合 helm-chart/values.yaml 与 helm-chart/templates 源码逐项讲解参数语义与底层渲染逻辑。读完本文你将掌握:如何用一条命令完成 Online Boutique 的默认部署,如何通过--set组合出面向生产的安全加固拓扑,以及如何通过cartservice.database相关参数将购物车数据层无缝迁移到 Spanner。
注意:Online Boutique 的 Helm chart 当前标记为实验性(experimental)。如遇到问题,可通过 GitHub Issue 反馈(原文档指向 Issue #1319 及新建 Issue 入口)。文章中的所有示例均以当前仓库内
helm-chart目录的实际内容为准,chart 版本与 app 版本均为v0.10.6(见 helm-chart/Chart.yaml)。
一、准备工作:chart 从哪来、如何获取
Online Boutique 的 Helm chart 已经打包并发布到公开的 Artifact Registry(OCI Registry),因此你无需从源码构建,直接用 Helm 3 的 OCI 拉取能力即可安装:
# 默认安装 Online Boutique helm upgrade onlineboutique oci://us-docker.pkg.dev/online-boutique-ci/charts/onlineboutique \ --installhelm upgrade ... --install是一个幂等用法:若 releaseonlineboutique不存在则执行安装,已存在则升级,配合 OCI 引用天然适合后续的迭代更新。
如果你希望了解 chart 是如何被发布到这个 Registry 的,可以查看仓库中的发布脚本 docs/releasing/make-helm-chart.sh:它读取TAG环境变量,用gsed同步改写 helm-chart/Chart.yaml 中的version与appVersion,然后依次执行helm package .与helm push onlineboutique-<version>.tgz oci://us-docker.pkg.dev/online-boutique-ci/charts。也就是说,本文安装命令中的镜像 tag 默认取自Chart.yaml的appVersion: "v0.10.6"。
二、Chart 结构速览:一个 chart 管住 12 类工作负载
在深入参数之前,先建立对 chart 布局的整体认知。当前仓库的 chart 目录结构如下:
helm-chart/ ├── Chart.yaml # chart 元数据:apiVersion: v2,type: application,version/appVersion: v0.10.6 ├── values.yaml # 全部可配置项的默认值(核心参数清单见下一节) └── templates/ ├── NOTES.txt # 安装成功后的提示信息(如何获取前端地址) ├── common.yaml # 全局 deny-all NetworkPolicy / AuthorizationPolicy ├── adservice.yaml / cartservice.yaml / checkoutservice.yaml ├── currencyservice.yaml / emailservice.yaml / frontend.yaml ├── loadgenerator.yaml / opentelemetry-collector.yaml ├── paymentservice.yaml / productcatalogservice.yaml ├── recommendationservice.yaml / shippingservice.yamlChart 遵循 Helm 2.x/v2 规范(apiVersion: v2,应用型 chart),values.yaml中为每个服务都提供了create开关,因此可以按需裁剪组件;同时每个服务的模板都遵循"资源 → 网络策略 → Sidecar → 授权策略"的统一四段式结构,为安全场景的批量开启提供了基础。
三、核心配置参数全解(values.yaml 逐项拆解)
完整的参数清单以 helm-chart/values.yaml 为准,下面按功能域分类讲解,并结合模板源码说明每个参数的实际影响。
3.1 镜像与发布
| 参数 | 默认值 | 说明 |
|---|---|---|
images.repository | us-central1-docker.pkg.dev/online-boutique-ci/microservices-demo | 所有微服务镜像的仓库前缀,模板中用{{ .Values.images.repository }}/{{ .Values.<service>.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}拼出完整镜像地址(见各服务模板)。使用私有或自建镜像仓库时务必覆盖此值 |
images.tag | "" | 覆盖镜像 tag;为空时回退到 chart 的appVersion(即v0.10.6) |
从 helm-chart/templates/adservice.yaml 可以看到典型的镜像渲染逻辑:image: {{ .Values.images.repository }}/{{ .Values.adService.name }}:{{ .Values.images.tag | default .Chart.AppVersion }}。因此覆盖images.repository后,全部 10 个微服务镜像将统一从新仓库拉取,这是离线部署与私有化环境的关键开关。
3.2 安全加固开关(服务账号 / 网络策略 / 授权策略 / Sidecar)
| 参数 | 默认值 | 说明 |
|---|---|---|
serviceAccounts.create | true | 是否为每个应用创建独立的 ServiceAccount(模板中未开启时回退到serviceAccountName: default) |
serviceAccounts.annotations | {} | 附加到 ServiceAccount 上的注解,典型用途是注入 Workload Identity 注解iam.gke.io/gcp-service-account |
serviceAccounts.annotationsOnlyForCartservice | false | 注解仅施加到 cartservice 的 ServiceAccount 上。遵循最小权限原则:只有 cartservice 需要连接外部数据库(如通过 Workload Identity 访问 Spanner),避免把云上高权限注解扩散到所有服务 |
networkPolicies.create | false | 为每个应用生成一条细粒度 NetworkPolicy(见各模板文件),开启时同时生成全局 deny-all 策略(见 helm-chart/templates/common.yaml) |
sidecars.create | false | 为每个应用生成细粒度 Istio Sidecar 资源(白名单式声明可访问的 egress 目标,见各模板文件) |
authorizationPolicies.create | false | 为每个应用生成细粒度 Istio AuthorizationPolicy,与全局 deny-all 搭配形成"默认拒绝、按需放行"的授权模型 |
以 helm-chart/templates/cartservice.yaml 为例,开启authorizationPolicies.create后,cartservice 的 AuthorizationPolicy 只允许来自frontend与checkoutservice两个 ServiceAccount 的调用,且仅放行/hipstershop.CartService/AddItem、/hipstershop.CartService/GetCart、/hipstershop.CartService/EmptyCart三个 gRPC 路径的 POST 请求、端口 7070。这体现了从"进程级网络可达"到"方法级调用授权"的纵深防御思路。
3.3 可观测性:OpenTelemetry Collector 与 Google Cloud Operations
| 参数 | 默认值 | 说明 |
|---|---|---|
opentelemetryCollector.create | false | 是否部署 OTel Collector 网关(Deployment + ClusterIP Service,端口 4317/gRPC-OTLP) |
opentelemetryCollector.name | opentelemetrycollector | Collector 服务名 |
opentelemetryCollector.projectId | "PROJECT_ID" | GCP 项目 ID;保持默认值时,会通过 initContainer 从 metadata server 自动获取 |
googleCloudOperations.profiler | false | 开启后向前端/结算服务注入ENABLE_PROFILER=1 |
googleCloudOperations.tracing | false | 开启后注入ENABLE_TRACING=1 |
googleCloudOperations.metrics | false | 指标开关(预留) |
从 helm-chart/templates/opentelemetry-collector.yaml 可以看到一个精巧的机制:当projectId等于"PROJECT_ID"时,chart 会注入一个 busybox initContainer,执行sed "s/PROJECT_ID/$(curl -H 'Metadata-Flavor: Google' http://metadata.google.internal/computeMetadata/v1/project/project-id)/",把配置模板中的占位符替换为真实项目 ID 后写入共享卷,Collector 主容器再以--config=/conf/collector-gateway-config.yaml启动。Collector 配置里receivers.otlp+exporters.googlecloud形成 traces/metrics 两条直接上云的数据管道。
3.4 运行时安全上下文
| 参数 | 默认值 | 说明 |
|---|---|---|
securityContext.enable | true | 为 Pod 注入fsGroup/runAsGroup/runAsNonRoot/runAsUser = 1000的 Pod 级 securityContext |
seccompProfile.enable | false | 是否启用 seccompProfile |
seccompProfile.type | RuntimeDefault | seccomp 类型(仅在 enable 时生效) |
此外,各服务容器本身都写死了安全加固字段(见 helm-chart/templates/cartservice.yaml):allowPrivilegeEscalation: false、capabilities.drop: [ALL]、privileged: false、readOnlyRootFilesystem: true。这些是开箱即用的容器最小权限基线。
3.5 各微服务组件开关与资源配置
values.yaml为每个服务提供了create、name与resources三件套,默认全部开启(create: true)。默认资源请求/限制如下(requests/limits,CPU/内存):
| 服务 | name | CPU 请求/限制 | 内存请求/限制 |
|---|---|---|---|
adService | adservice | 200m / 300m | 180Mi / 300Mi |
cartService | cartservice | 200m / 300m | 128Mi / 256Mi |
checkoutService | checkoutservice | 100m / 200m | 64Mi / 128Mi |
currencyService | currencyservice | 100m / 200m | 128Mi / 256Mi |
emailService | emailservice | 100m / 200m | 64Mi / 128Mi |
frontend | frontend | 100m / 200m | 64Mi / 128Mi |
loadGenerator | loadgenerator | 300m / 500m | 256Mi / 512Mi |
paymentService | paymentservice | 100m / 200m | 128Mi / 256Mi |
productCatalogService | productcatalogservice | 100m / 200m | 64Mi / 128Mi |
recommendationService | recommendationservice | 100m / 200m | 220Mi / 450Mi |
shippingService | shippingservice | 100m / 200m | 64Mi / 128Mi |
两个值得注意的专项参数:
productCatalogService.extraLatency(默认""):给 productcatalogservice 的每次请求注入额外延迟,用于压测/演示超时与重试行为,默认无额外延迟;loadGenerator.checkFrontendInitContainer(默认true):loadgenerator 启动前先通过 busybox initContainer 用wget --server-response轮询前端(最多 12 次、间隔 10 秒,见 helm-chart/templates/loadgenerator.yaml),确保压测流量不会打到尚未就绪的前端。压测参数在模板中硬编码为USERS=10、RATE=1(每秒 1 个请求/用户)。
3.6 前端访问与 Istio 路由
| 参数 | 默认值 | 说明 |
|---|---|---|
frontend.externalService | true | 是否额外创建frontend-external的 LoadBalancer Service(保留frontendClusterIP Service 用于网格内访问) |
frontend.cymbalBranding | false | 是否启用 Cymbal 品牌标识(对应注入CYMBAL_BRANDING环境变量) |
frontend.platform | local | 部署平台标识,取值local/gcp/aws/azure/onprem/alibaba之一;对应注入ENV_PLATFORM环境变量,前端 main.go 据此渲染部署详情。默认local,GKE 上运行时自动切换为gcp |
frontend.singleSharedSession | false | 是否启用单共享会话模式(对应ENABLE_SINGLE_SHARED_SESSION) |
frontend.virtualService.create | false | 是否创建指向 ingress gateway 的 Istio VirtualService |
frontend.virtualService.hosts | ["*"] | VirtualService 的 host 列表 |
frontend.virtualService.gateway.name | asm-ingressgateway | 目标 Gateway 名称 |
frontend.virtualService.gateway.namespace | asm-ingress | 目标 Gateway 所在命名空间 |
frontend.virtualService.gateway.labelKey | asm | 用于从 ASM 命名空间中挑选网关 Pod 的标签键 |
frontend.virtualService.gateway.labelValue | ingressgateway | 对应标签值 |
关键关联:当frontend.externalService=false且frontend.virtualService.create=true时,流量路径变为"Ingress Gateway → VirtualService → frontend:80",NetworkPolicy 会额外放行来自网关命名空间(kubernetes.io/metadata.name匹配frontend.virtualService.gateway.namespace)的入站请求(见 helm-chart/templates/frontend.yaml)。
3.7 购物车数据库:Redis 与 Spanner 双后端
| 参数 | 默认值 | 说明 |
|---|---|---|
cartDatabase.type | redis | 购物车数据层类型,可选redis或spanner |
cartDatabase.connectionString | redis-cart:6379 | 连接串;Redis 模式为host:port,Spanner 模式为projects/<project>/instances/<instance>/databases/<db> |
cartDatabase.inClusterRedis.create | true | 是否随 chart 部署一个内置 Redis(Deployment + Service,端口 6379) |
cartDatabase.inClusterRedis.name | redis-cart | 内置 Redis 名称 |
cartDatabase.inClusterRedis.publicRepository | true | 为 true 时使用 Docker Hub 的redis:alpine官方镜像(固定 digest 拉取),否则改用images.repository中的 redis |
cartDatabase.externalRedisTlsOrigination.enable | false | 是否对外部 Redis 启用 TLS 发起(配合 Istio ServiceEntry/DestinationRule) |
cartDatabase.externalRedisTlsOrigination.name | exernal-redis-tls-origination | TLS 资源命名前缀(注意原值拼写) |
cartDatabase.externalRedisTlsOrigination.endpointAddress | "" | 外部 Redis 端点 IP |
cartDatabase.externalRedisTlsOrigination.endpointPort | "" | 外部 Redis 端点端口 |
cartDatabase.externalRedisTlsOrigination.certificate | "" | 用于 TLS 校验的 PEM 证书内容 |
模板层的实际效果(见 helm-chart/templates/cartservice.yaml):
- 当
cartDatabase.type == "spanner"时,注入环境变量SPANNER_CONNECTION_STRING;否则注入REDIS_ADDR,值均取cartDatabase.connectionString; - 开启
externalRedisTlsOrigination.enable时,chart 会同时生成:保存 PEM 证书的 Secret、mode: SIMPLE的 DestinationRule(caCertificates 指向/etc/certs/<name>.pem)、location: MESH_EXTERNAL的 ServiceEntry(resolution: STATIC),并在 cartservice Pod 上通过sidecar.istio.io/userVolumeMount注解把证书卷挂到 sidecar 的/etc/certs,同时设置proxy.istio.io/config: {"holdApplicationUntilProxyStarts": true}保证 sidecar 就绪后再启动应用容器。
3.8 尚未纳入 Helm 的组件
shoppingAssistantService(购物助手)当前在values.yaml中以create: false占位并标注 TODO,尚未随 Helm 部署(可参考 kustomize/components/shopping-assistant 的独立安装方式)。
四、部署命令实战:默认安装与高级场景
4.1 场景一:默认部署
helm upgrade onlineboutique oci://us-docker.pkg.dev/online-boutique-ci/charts/onlineboutique \ --install该命令会以默认值部署全部 10 个微服务 + 内置 Redis + loadgenerator,并创建一个frontend-externalLoadBalancer Service 对外暴露前端。安装完成后,chart 的 NOTES.txt 会给出获取访问地址的方法:
# 观察 frontend-external 的 LoadBalancer IP 就绪状态 kubectl get --namespace <release-namespace> svc -w frontend-external # 提取外部 IP 并打印访问地址 export SERVICE_IP=$(kubectl get svc --namespace <release-namespace> frontend-external --template "{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}") echo http://$SERVICE_IP若开启了frontend.virtualService.create,NOTES.txt 还会提示通过 ingress gateway 的地址访问(同理用kubectl get svc -n <gateway-namespace> <gateway-name>提取 IP)。
4.2 场景二:高级安全拓扑(Service Mesh + Spanner + Workload Identity)
原文档给出了一条完整的进阶部署命令,它同时解决了镜像私有化、关闭外部直连、购物车后端切换、服务账号/授权/网络策略/Sidecar 全量开启、Istio 网关路由与 Workload Identity 注解等问题:
helm upgrade onlineboutique oci://us-docker.pkg.dev/online-boutique-ci/charts/onlineboutique \ --install \ --create-namespace \ --set images.repository=us-docker.pkg.dev/my-project/microservices-demo \ --set frontend.externalService=false \ --set redis.create=false \ --set cartservice.database.type=spanner \ --set cartservice.database.connectionString=projects/my-project/instances/onlineboutique/databases/carts \ --set serviceAccounts.create=true \ --set authorizationPolicies.create=true \ --set networkPolicies.create=true \ --set sidecars.create=true \ --set frontend.virtualService.create=true \ --set 'serviceAccounts.annotations.iam\.gke\.io/gcp-service-account=spanner-db-user@my-project.iam.gserviceaccount.com' \ --set serviceAccounts.annotationsOnlyForCartservice=true \ -n onlineboutique逐项解读这条命令的意图:
| 参数 | 作用 |
|---|---|
--create-namespace+-n onlineboutique | 自动创建并指定 release 命名空间 |
images.repository | 指向你私有项目下的镜像仓库(如 GCR/Artifact Registry 镜像迁移后) |
frontend.externalService=false | 关闭 LoadBalancer 直连,对外流量统一走 Istio Ingress Gateway |
redis.create=false | 不部署内置 Redis(Redis 已不再需要,因为后端切换为 Spanner) |
cartservice.database.type=spanner | 将购物车数据层切换为 Cloud Spanner |
cartservice.database.connectionString | 指定 Spanner 数据库连接串(项目/实例/数据库三级路径) |
serviceAccounts.create=true | 每个服务独立 ServiceAccount,为方法级授权提供身份基础 |
authorizationPolicies.create=true | 开启 Istio 方法级授权策略(配合 common.yaml 的 deny-all 形成默认拒绝模型) |
networkPolicies.create=true | 开启细粒度 Kubernetes NetworkPolicy(同样配合 deny-all 基线) |
sidecars.create=true | 开启 Istio Sidecar 资源,收敛每个 Pod 的 egress 白名单 |
serviceAccounts.annotations.iam\.gke\.io/gcp-service-account=... | 为 ServiceAccount 注入 Workload Identity 注解(注意键中的点需要用\.转义) |
serviceAccounts.annotationsOnlyForCartservice=true | 注解只落在 cartservice 上,最小权限原则:只有 cartservice 需要以spanner-db-user身份访问外部 Spanner |
这个场景同时印证了源码中的几处设计:Spanner 模式仅注入SPANNER_CONNECTION_STRING(见 helm-chart/templates/cartservice.yaml);annotationsOnlyForCartservice控制注解是否只加到 cartservice 的 ServiceAccount(其余服务模板中均以{{- if not .Values.serviceAccounts.annotationsOnlyForCartservice }}守卫);cartservice 的 AuthorizationPolicy 只放行 frontend/checkoutservice 的特定 gRPC 方法。
4.3 与源码的对应关系:为什么cartservice.database.type=spanner会生效
cartservice 的底层实现也支持双后端:仓库中 src/cartservice/src/cartstore/ICartStore.cs 定义了存储抽象,RedisCartStore.cs 与 SpannerCartStore.cs 分别实现两种后端,其中 SpannerCartStore 通过读取SPANNER_CONNECTION_STRING(以及SPANNER_PROJECT、SPANNER_INSTANCE、SPANNER_DATABASE)完成连接初始化。这正是 Helm 模板注入SPANNER_CONNECTION_STRING环境变量后能够无缝切换的底层依据——配置驱动、代码无感知。
五、从源码本地安装 chart(离线 / 调试场景)
如果你希望基于当前仓库的 chart 源码直接部署(例如验证模板改动或离线环境),可以省略 OCI 拉取,直接引用本地目录:
helm upgrade onlineboutique ./helm-chart \ --install \ -n onlineboutique --create-namespace也可以先用helm template ./helm-chart渲染 YAML 检查输出,再决定是否执行。chart 的发布流程(版本号同步、helm package、helm push)见 docs/releasing/make-helm-chart.sh。
六、验证与卸载
- 健康检查:chart 为各服务配置了 Kubernetes 1.24+ 支持的 gRPC 探针(
readinessProbe/livenessProbe中的grpc:字段,见 adservice/checkoutservice/cartservice 等模板),例如 cartservice 的 gRPC 探针位于端口 7070,initialDelaySeconds: 15;前端则使用带 Cookie 的 HTTP 探针访问/_healthz(见 helm-chart/templates/frontend.yaml)。 - 查看 release 状态:
helm status onlineboutique -n onlineboutique;查看资源:kubectl get pods,svc -n onlineboutique。 - 卸载:
helm uninstall onlineboutique -n onlineboutique。
七、小结与延伸阅读
本文完整覆盖了 Online Boutique Helm chart 的默认安装、全部核心配置参数(镜像、安全加固、可观测性、数据库后端、Istio 路由)与高级安全场景实战,并逐层对应到 helm-chart/values.yaml 与 helm-chart/templates 的模板源码。你可以在此基础上继续探索仓库内与之互补的部署方案:
- 面向 GitOps 的 kustomize 变体:kustomize/README.md 及 kustomize/components 下的
spanner、memorystore、service-mesh-istio、google-cloud-operations等组件; - 手写清单版本:kubernetes-manifests 与 istio-manifests(含 frontend gateway 与 VirtualService);
- 底层服务实现:cartservice 的 C# 实现见 src/cartservice,购物车双后端存储类见 src/cartservice/src/cartstore;
- Terraform 基础设施:terraform/README.md(GKE 集群与 Memorystore 的 IaC 定义)。
原文档还给出了三篇延伸博客主题:借助 Helm chart 简化 Service Mesh 与 GitOps 下的高级安全场景配置、Kubernetes 1.24+ 的 gRPC 健康探针、以及为 Online Boutique 接入 Cloud Spanner。结合本仓库源码阅读这些主题,可以更深入理解 chart 中每个开关背后的设计动机。
【免费下载链接】microservices-demoSample cloud-first application with 10 microservices showcasing Kubernetes, Istio, and gRPC.项目地址: https://gitcode.com/GitHub_Trending/mi/microservices-demo
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考