第 23篇 k8s之Pod:多容器 Pod 与设计模式(Sidecar 等)
IT策士 10余年一线大厂经验,专注 IT 思维、架构、职场进阶。我会在各个平台持续发布最新文章,助你少走弯路。
在第 21 篇中我们学习了 Pod 的 YAML 结构和多容器共享网络,第 22 篇深入了 Pod 的生命周期和重启策略。回顾第 21 篇,我们部署过一个包含 Flask 和 Redis 两个容器的 Pod,通过localhost共享网络。那篇文章的核心是“怎么用”,今天我们要回答的是“为什么要这样设计”——把多个容器放进同一个 Pod,到底能解决哪些实际问题?
这背后,是 Kubernetes 社区经过大量生产实践总结出的三种经典设计模式:Sidecar(边车)、Adapter(适配器)、Ambassador(大使)。这三种模式在 Compose 时代你几乎不会用到,因为单机编排天然缺乏 Pod 这种“多个容器共享命运”的抽象。而到了 K8s 阶段,合理地组织多容器 Pod 直接决定了你的应用架构是否优雅、可维护、可观测。
一、为什么要在 Pod 里放多个容器?
先明确一点:大部分 Pod 只需要一个容器。多容器 Pod 不是默认选择,而是为了解决特定问题。哪些问题?
扩展或增强主容器功能,但不修改主应用代码——比如自动采集日志、统一监控指标格式。
在主容器和外部系统之间做代理或适配——比如连接外部数据库时需要客户端证书认证,用一个边车容器专门处理 TLS。
将辅助进程与主进程紧密绑定——它们必须共享同一个网络命名空间、同一个 IP、同一组存储卷。
Kubernetes 官方文档总结了三种经典模式:Sidecar(边车)、Adapter(适配器)、Ambassador(大使)。它们本质上都是把辅助容器和主应用容器放在同一个 Pod 中,利用共享网络和存储的特性,实现功能增强或代理。
在正式开始之前,有必要澄清 Sidecar 的一个术语歧义,因为它在 K8s 生态中有双重含义:
Sidecar 模式(设计模式):泛指所有与主容器并行的辅助容器,本篇与 Adapter、Ambassador 并列的就是这个广义概念。
Sidecar 容器(Kubernetes 特性,v1.29+ beta):是
initContainers中的一种特殊类型,设置restartPolicy: Always后,它会在整个 Pod 生命周期内持续运行,并在普通容器启动前先行启动。本篇的实战部分将采用这个特性来部署日志采集 Sidecar。
二、Sidecar 模式:增强主容器能力
2.1 什么是 Sidecar 模式?
Sidecar 模式是最常见的多容器 Pod 设计。核心思想是:主容器只做自己的业务,Sidecar 容器负责附加值——比如日志采集、文件同步、配置热加载等。它们共享同一个 Pod 的网络和存储卷,通过localhost或共享文件进行协作。
想象一下摩托车比赛的边车(Sidecar)——主驾驶员专注于操控方向,边车里的助手可以观察路况、传递信息,两者共享同一台车的速度和路线。这就是 Kubernetes 中 Sidecar 模式的形象比喻。
2.2 实战:为 Flask 应用添加日志采集 Sidecar
我们贯穿案例的 Flask 应用会输出访问日志,但日志只在容器内部。生产环境中需要把日志收集到外部系统(如 Elasticsearch、Loki)。直接修改 Flask 代码接入日志系统会增加业务代码的复杂度,更好的做法是加一个 Sidecar 容器专门采集日志。
以下 YAML 演示了一个典型的 Sidecar 部署——Flask 容器将日志写入共享 Volume,Filebeat Sidecar 读取并转发到 Elasticsearch(此处简化为输出到 stdout 以便演示):
apiVersion: v1 kind: Pod metadata: name: flask-sidecar-demo labels: app: flask-sidecar spec: volumes: - name: shared-logs emptyDir:{}containers:# ---- 主容器:Flask 应用 ----- name: flask-app image: flask-redis-counter:2.0 ports: - containerPort:5000env: - name: REDIS_HOST value: redis-service volumeMounts: - name: shared-logs mountPath: /app/logs# ---- Sidecar 容器:Filebeat 日志采集 ----- name: log-collector image: docker.elastic.co/beats/filebeat:8.15.0 volumeMounts: - name: shared-logs mountPath: /var/log/flask readOnly:truecommand: - /bin/bash --c-|echo"日志采集 Sidecar 已启动,监听 /var/log/flask"tail-f/var/log/flask/*.log2>/dev/null||echo"等待日志文件..."解释一下数据流:emptyDir卷在 Pod 创建时自动生成,Pod 删除时随之销毁。Flask 向/app/logs写日志,log-collector从/var/log/flask读日志——两个路径指向同一个 Volume。readOnly: true确保 Sidecar 只能读取不能修改日志文件。
部署并验证:
kubectl apply-fflask-sidecar.yaml kubectl get pods# NAME READY STATUS RESTARTS AGE# flask-sidecar-demo 2/2 Running 0 30s# 模拟日志写入kubectlexecflask-sidecar-demo-cflask-app --sh-c"echo 'test log' > /app/logs/access.log"# 检查 Sidecar 是否读到了日志kubectl logs flask-sidecar-demo-clog-collector# 日志采集 Sidecar 已启动,监听 /var/log/flask# test log业务容器只写了一行echo,Sidecar 容器就通过共享 Volume 实时读取到了日志内容。实际的 Filebeat 配置会更复杂(包括定义输出目标为 Elasticsearch 或 Kafka),但核心原理完全一致。
Sidecar 容器的 K8s 原生特性:从 v1.29 开始,你可以在
initContainers中设置restartPolicy: Always来声明一个 Sidecar 容器。这种 Sidecar 会在普通容器启动前就绪,并在整个 Pod 生命周期内持续运行。上面的示例中我们仍使用普通容器模式,两种方式功能等效,但原生 Sidecar 特性提供了更好的生命周期保证。
三、Adapter 模式:统一监控数据格式
Adapter 模式的核心思想是:主应用输出的监控指标格式不符合 Prometheus 规范,Adapter 容器负责转换格式,对外暴露标准接口。它同样共享 Pod 网络,但工作重点不是采集文件,而是提供实时、可抓取的监控端点。
3.1 场景
你的 Flask 应用暴露了一个自定义的/metrics端点,但输出的 JSON 格式 Prometheus 无法直接识别。Adapter 容器读取这个端点并转换为 Prometheus 格式。
3.2 动手体验
apiVersion: v1 kind: Pod metadata: name: flask-adapter-demo labels: app: flask-adapter spec: containers: - name: flask-app image: flask-redis-counter:2.0 ports: - containerPort:5000env: - name: REDIS_HOST value: redis-service - name: metrics-adapter image: alpine command: - /bin/sh --c-|whiletrue;do# 模拟从 Flask 的 /metrics 抓取 JSON 并转换为 Prometheus 格式echo"# HELP flask_requests_total Total request count"echo"# TYPE flask_requests_total counter"echo"flask_requests_total$(curl-shttp://localhost:5000/health|grep-o'"status":"ok"'|wc-l)"sleep5doneAdapter 容器通过localhost:5000访问 Flask 主容器,读取健康检查端点,将其转换为 Prometheus 风格的指标输出到 stdout(实际生产中会暴露一个 HTTP 端口供 Prometheus 抓取)。这里curl连接localhost:5000能成功,正是因为 Adapter 和主容器共享 Pod 的网络命名空间。
四、Ambassador 模式:代理外部服务
Ambassador 模式的核心思想是:主应用访问外部服务时不需要关心复杂的连接逻辑(如 TLS 证书、连接池),由 Ambassador 容器作为本地代理,处理这些复杂性后转发给主容器。主应用只连接localhost上的代理端口。
4.1 场景
你的 Flask 应用需要连接外部的 Redis 集群,而该集群要求 TLS 客户端证书认证。你的 Flask 镜像不想内置证书,也不想修改连接逻辑。
4.2 动手体验
apiVersion: v1 kind: Pod metadata: name: flask-ambassador-demo labels: app: flask-ambassador spec: containers: - name: flask-app image: flask-redis-counter:2.0 ports: - containerPort:5000env: - name: REDIS_HOST value: localhost# 连接本地 Ambassador 代理- name: REDIS_PORT value:"6379"- name: redis-ambassador image: alpine/socat args: -"TCP-LISTEN:6379,fork,reuseaddr"-"TCP:real-redis-cluster.example.com:6379"Ambassador 容器使用socat在localhost:6379上建立一个 TCP 代理,将流量转发到外部的real-redis-cluster.example.com:6379。Flask 容器通过REDIS_HOST=localhost连接这个本地代理,完全不需要知道真实 Redis 的地址和证书细节。真实的生产场景中,Ambassador 还可以集成 TLS 握手、认证令牌注入等逻辑——这些都封装在 Ambassador 容器中,主应用完全无感知。
五、三种模式总结与选择指南
选择原则:一个容器做好一件事,多容器通过共享机制协作。如果辅助功能和主应用的生命周期紧密耦合(必须一起调度、一起销毁),就应该放在同一个 Pod 里。如果两个组件需要独立的扩缩容策略(比如 Web 服务需要 5 个副本,但日志采集只需要跟随节点部署),则应该放在不同的 Pod 中。
六、命令速查表
七、本篇总结
多容器 Pod 的设计哲学:不是默认选择,而是为了解决“功能增强”和“外部代理”的特定问题。
Sidecar 模式:增强主容器,最常见——日志采集、文件同步。
Adapter 模式:适配器转换格式,面向监控和可观测性。
Ambassador 模式:代理外部访问,封装连接复杂性。
核心机制:三种模式都依赖 Pod 内的共享网络和共享存储,这是与 Docker Compose 多容器协作最本质的区别。
这三种模式为我们后续学习更高级的 K8s 能力做了重要铺垫。比如下一篇——第 24 篇:健康检查:探针机制详解,我们将深入 liveness、readiness、startup 三种探针的配置和实战,让 Pod 真正具备生产级的自愈和流量控制能力。
想了解更多还可以去各个平台搜索「IT策士」,一起升级 IT 思维 !
