第 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 不是默认选择,而是为了解决特定问题。哪些问题?

  1. 扩展或增强主容器功能,但不修改主应用代码------比如自动采集日志、统一监控指标格式。

  2. 在主容器和外部系统之间做代理或适配------比如连接外部数据库时需要客户端证书认证,用一个边车容器专门处理 TLS。

  3. 将辅助进程与主进程紧密绑定------它们必须共享同一个网络命名空间、同一个 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 以便演示):

bash 复制代码
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: 5000
      env:
        - 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: true
      command:
        - /bin/bash
        - -c
        - |
          echo "日志采集 Sidecar 已启动,监听 /var/log/flask"
          tail -f /var/log/flask/*.log 2>/dev/null || echo "等待日志文件..."

解释一下数据流:emptyDir 卷在 Pod 创建时自动生成,Pod 删除时随之销毁。Flask 向 /app/logs 写日志,log-collector/var/log/flask 读日志------两个路径指向同一个 Volume。readOnly: true 确保 Sidecar 只能读取不能修改日志文件。

部署并验证:

bash 复制代码
kubectl apply -f flask-sidecar.yaml
kubectl get pods
# NAME                READY   STATUS    RESTARTS   AGE
# flask-sidecar-demo  2/2     Running   0          30s

# 模拟日志写入
kubectl exec flask-sidecar-demo -c flask-app -- sh -c "echo 'test log' > /app/logs/access.log"

# 检查 Sidecar 是否读到了日志
kubectl logs flask-sidecar-demo -c log-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 动手体验

bash 复制代码
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: 5000
      env:
        - name: REDIS_HOST
          value: redis-service
    - name: metrics-adapter
      image: alpine
      command:
        - /bin/sh
        - -c
        - |
          while true; do
            # 模拟从 Flask 的 /metrics 抓取 JSON 并转换为 Prometheus 格式
            echo "# HELP flask_requests_total Total request count"
            echo "# TYPE flask_requests_total counter"
            echo "flask_requests_total $(curl -s http://localhost:5000/health | grep -o '"status":"ok"' | wc -l)"
            sleep 5
          done

Adapter 容器通过 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 动手体验

bash 复制代码
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: 5000
      env:
        - 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 容器使用 socatlocalhost: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 思维 !

相关推荐
专业白嫖怪9 小时前
什么是docker
运维·docker·容器
Plastic garden14 小时前
Docker(1)
运维·docker·容器
gs8014015 小时前
网络隐形杀手:从 Could not connect to SMTP host 报错深度剖析 Docker MTU 黑洞理论与实战
网络·docker·容器
程序猿阿伟16 小时前
《一套完整方法论:搞定图形应用的Docker镜像优化》
数据库·docker·容器
java_logo17 小时前
2026 Docker 国内镜像加速配置教程
运维·docker·容器·docker镜像·docker镜像源·docker镜像加速·docker镜像国内库
IT策士17 小时前
Docker从0到1再到 Kubernetes 实战:第15篇Compose 中的服务依赖、健康检查与启动顺序
docker·容器·kubernetes
Waay18 小时前
K8s Deployment 滚动更新与回滚深度详解(含踩坑实录+生产选型原理)
云原生·容器·kubernetes
顾默@18 小时前
双系统Ubuntu18.04升级22.04,安装docker进行openclaw安装
运维·docker·容器
qq_2975746718 小时前
设计模式系列文章(基础篇第 11 篇):模板方法模式——定义算法骨架,实现代码复用与流程统一
算法·设计模式·模板方法模式