Skip to main content

聊聊 K8S 中的 Sidecar 设计模式·第 1 篇

· 阅读需要 1 分钟

介绍

Sidecar 北京土话叫三蹦子,通俗叫就是带棚子的三轮摩托车。今天我们要聊的 K8S 中三蹦子也称为边三轮车:边三轮车是在摩托车边上挂靠一个拖斗,云原生中的叫法是主容器和边容器。本系列文章将展示 Sidecar 模式的用法,以及如何通过 KCL 等面向配置的编程语言来简化 YAML 的编写。

一个最简单的云原生 Web 服务

首先以最简方式在 Kubernetes 环境启动一个 Web 服务。在下面 pod.yaml 文件中定义一个 Pod,其中只有一个 Nginx 服务,在 80 端口启动一个 web 服务。

apiVersion: v1
kind: Pod
metadata:
name: web-app
spec:
containers:
- image: nginx
name: main-container
ports:
- containerPort: 80

Pod 是云原生中的一个基础原语。Pod 将多个容器包装为一个逻辑单元,Kubernetes 运行时确保 Pod 中的容器运行在一个机器上。因此 Pod 中的所有容器都共享生命周期、共享磁盘卷、共享网络环境等。Sidecar 模式就是在 Pod 中增加其他容器来扩展和增强主容器的能力。

然后通过 kubectl create 命令行工具创建 Pod,然后通过 kubectl get po 查看 Pod 执行状态:

$ kubectl create -f pod.yaml
$ kubectl get po
NAME READY STATUS RESTARTS AGE
web-app 1/1 Running 0 45m

可以看到一个名为 web-appPod 已经正常启动并运行,其中包含 Nginx 服务。为了便于外部访问配置端口转发,将宿主的 3999 端口对应到主容器的 80 端口:

$ kubectl port-forward web-app 3999:80
Forwarding from 127.0.0.1:3999 ->80
Forwarding from [::1]:3999 -> 80

端口转发是一个阻塞程序,保持命令行窗口打开。然后在浏览器打开测试页面:

通过 Sidecar 定扩展页面内容

现在我们尝试在不修改原始 Nginx 容器镜像的前提下,通过 Sidecar 模式为 Nginx 服务增加定制 Web 页面的能力。在开始前先删除之前启动的 Pod

$ kubectl delete po web-app
pod "web-app" deleted

然后在 Pod 中增加第二个 Busybox Sidecar 容器,完整的 pod.yaml 文件如下:

apiVersion: v1
kind: Pod
metadata:
name: web-app
spec:
containers:
- image: nginx
name: main-container
ports:
- containerPort: 80

# --- 以下是新添加的内容 ---

# 和 Sidecar 通过 磁盘卷共享要发布的文件目录
volumeMounts:
- name: var-logs
mountPath: /usr/share/nginx/html

# Sidecar 容器
- image: busybox
command: ["/bin/sh"]
args: ["-c", "while true; do echo $(date -u) 'Hi I am from Sidecar container' > /var/log/index.html; sleep 5;done"]
name: Sidecar-container
volumeMounts: var-logs
mountPath: /var/log

# Pod 中全部容器共享磁盘卷
volumes:
- name: var-logs
emptyDir: {}

Busybox Sidecar 容器执行的命令对应以下 Shell 脚本:

while true; do
echo $(date -u) 'Hi I am from Sidecar container' > /var/log/index.html;
sleep 5;
done

Sidecar 容器只有一个功能:每隔 5 秒钟覆盖一次 /var/log/index.html 文件,这个文件刚好对应 Nginx 的服务的首页页面文件。

然后重新启动 Pod,并重新映射本地宿主机端口到容器端口:

$ kubectl create -f pod.yaml 
pod/web-app created
$ kubectl port-forward web-app 3999:80
Forwarding from 127.0.0.1:3999 -> 80
Forwarding from [::1]:3999 -> 80

重新打开浏览器后将看到以下页面:

Sidecar 模式的工作原理

简单来说,BusyboxSidecar 容器角色,负责生产首页数据;而 Nginx 是主容器,负责消费 Busybox 生产的主页数据;两个容器通过 var-logs 磁盘卷共享空间。如果以 Go 语言的术语类比,Nginx 是主 GoroutineBusybox 是后台干脏活的 Goroutine,而共享的磁盘卷类似 Channel 的作用。

在这个例子中 Nginx 依然是主容器,Sidecar 容器是 BusyBox。我们还可以挂更多 Sidecar 容器,比如网络、监控、日志等等。

这样就通过 Sidecar 模式,在不修改 Nginx 主容器的前提下,扩展出了网络、监控、日志等能力。

Sidecar 模式的优点

现在容器已经成为一种流行的打包技术,各种不同角色的同学可以通过容器以统一的方式构建、发布和运行程序,甚至管理各种资源。因此容器更像一个功能明确的产品,它有自己的运行时、发布周期、文档和 API 等。好的容器/产品只负责解决一个问题,保持了 KISS 原则可以让容器本身具有极高的重用性和可被替代性。正是因为可重用才使得现代化的构建程序的流程更加敏捷和高效。但是可复用的容器一般都功能单一,我们常常需要通过各种手段扩展容器的功能,以及需要更多的容器之间的协同。

三蹦子 Sidecar 可以在不改造主摩托车的前提下增加 N 个拖车功能,相应地云原生 Sidecar 模式可以在无需修改主容器的前提下扩展并增强已有主容器功能。如果将云原生的玩法和面向对象编程联系起来,容器镜像就是 Java 中的 class,而执行中的容器就是 class 的实例。而面向对象的 class 继承就是基于已有的容器镜像做扩展,Sidecar 则是通过类似组合的模式扩展 class 的能力。

面向对象编程中有一个“组合优于继承,多用组合少用继承”的规则,因此 Sidecar 也是推荐使用的模式。正是因为三蹦子模式的优点,最近在云原生场景也被大量使用:比如在边车上架一些类似机关枪的网络服务、监控、跟踪等功能。

总结

这一篇文章我们简要介绍并在 Kubernetes 环境展示了 Sidecar 模式,同时结合传统的面向对象编程思想对比了 Sidecar 和组合编程模式的关系。Sidecar 模式的优势不仅仅体现在无害增强主容器,更灵活的是可以在 apply 时动态调整 Sidecar 能力。

在后面的文章中,我们将尝试结合 KCL 等现代化的云原生配置语言来简化 Sidecar 配置的编写。通过尝试探索通过 KCL 动态注入和修改 Sidecar 来扩展基于已有配置的能力。