Skip to main content

1 post tagged with "k8s"

查看所有标签

· 阅读需要 1 分钟

介绍

在前一篇文章 聊聊 K8S 中的Sidecar设计模式·第 1 篇中,我们介绍了土话说的三蹦子的 Sidecar 设计模式。本文尝试用 KCL 这种现代化的类型安全的配置语言,来展示 Pod 资源的描述,在后续的文章中,我们将以 KCL 来介绍 Sidecar 这种模式的实际应用。

1. Pod 的 OpenAPI 定义

先回到最开始最简单的 Nginx 例子,其 YAML 文件几乎是相同的模式:

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

这里的 Pod 数据满足 Kubernetes 规范 中 io.k8s.api.core.v1.Pod 模型的定义。完整的 OpenAPI 定义请 参考。仔细分析可以发现,apiVersion 和 kind 信息都是比较常见的默认配置,我们需要更现代的配置语言来简化。

2. 使用 KCL 定义 Pod 资源

KCL 是一门面向云原生领域配置策略语言,详细资料可参考语言官网

基本的 KCL 配置程序仍然遵循 K=V 的形式, YAML 比较相似。比如,我们可以用下面的 KCL 代码来重写 Nginx 容器的配置:

import k8s.api.core.v1 as k8core

k8core.Pod {
metadata.name = "web-app"
spec.containers = [{
name = "main-container"
image = "nginx"
ports = [{containerPort: 80}]
}]
}

其中 import 导入了 k8s 包中的 k8s/api/core/v1 包,其中的 Pod 结构定义对应 Pod 模型,其中已经包含了 apiVersion 和 kind 的默认值,因此只需要添加 metadata.namespec.containers 属性(KCL 也针对一些多级嵌套的属性提供了的语法糖)。

我们可以通过 kpm 包管理工具,创建一个 KCL 程序来定义一个 Pod 资源。并执行这个程序来得到对应的 YAML。

# 1. 初始化一个 kcl 程序包 hello.
kpm init hello

# 2. 编辑 hello 包内的 main.k 文件,
# 将前文中提到的 KCL 配置代码添加到 hello/main.k 中
cat <<EOF > hello/main.k
import k8s.api.core.v1 as k8core

k8core.Pod {
metadata.name = "web-app"
spec.containers = [{
name = "main-container"
image = "nginx"
ports = [{containerPort: 80}]
}]
}
EOF

# 3. 进入到 hello 包内,添加 k8s 依赖,并且运行 hello 包。
cd hello && kpm add k8s && kpm run

可以得到如下 YAML 输出:

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

3. 小结

这一篇文章我们简要介绍了如何通过 KCL 构建出 Pod 配置来获得更灵活、更健壮的配置。后续文章中我们将通过 KCL 来提炼和抽象最佳的 Sidecar 模式实践,包括使用 KCL 对 Sidecar 模型进行抽象以及使用 KCL 对已存在的上游 YAML 配置进行 Sidecar 注入。

· 阅读需要 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 来扩展基于已有配置的能力。