立即领取|华润集团、宁德核电、东风岚图等 20+ 标杆企业数字化人才培养实践案例 了解详情
写点什么

《Kubernetes 与云原生应用》系列之实践案例“单节点多容器模式”

  • 2016-11-02
  • 本文字数:6906 字

    阅读完需:约 23 分钟

《Kubernetes 与云原生应用》专栏是 InfoQ 向轻元科技首席架构师王昕约稿的系列文章。本专栏包含 8 篇内容,将会从介绍和分析 Kubernetes 系统以及云原生应用入手,逐步推出基于 Kubernetes 的容器设计模式实践案例,希望对计划应用 Kubernetes 的朋友有所帮助。本文是该专栏的第四篇,阅读本系列全部内容请在细说云计算微信公众号(CloudNote)回复 K8s。

1. Kubernetes 系统架构与设计理念
2. 云原生应用的设计理念与挑战
3. Kubernetes 与云原生应用的容器设计模式
4. Kubernetes 容器设计模式实践案例 - 单节点多容器模式
5. Kubernetes 容器设计模式实践案例 - 多节点选举模式
6. Kubernetes 容器设计模式实践案例 - 工作队列模式
7. Kubernetes 容器设计模式实践案例 - 分散收集模式
8. 云原生应用的容器设计模式综述与展望

K8s 与容器设计模式

目前 K8s 社区推出的容器设计模式主要分为三大类:

第一类,单容器管理模式;
第二类,单节点多容器模式;
第三类,多节点多容器模式;

一类比一类更复杂。根据复杂性的不同,本系列文章给出不同篇幅的实践案例介绍。

对于第一类,只在本文中用一小节给与介绍;对于第二类,在本文中,针对每一种典型设计模式分一个小节给与介绍;对于较复杂的第三类,每一种典型设计模式将用一篇文章给与介绍。

单容器管理模式

K8s 的最大特色是支持多容器的微服务实例。当然,单容器的模式也是支持的,只不过这种模式并不能突出 K8s 的特色和强大。很多人对 K8s 一直以来的印象是:功能强大,但入门较难。其实,单单就启动一个单容器微服务实例,K8s 的命令行操作跟 Docker 原生命令一样简单。

运行一个 nginx 容器

复制代码
[root@demo-k8s ~]# kubectl run nginx --image=nginx
deployment "nginx" created
[root@demo-k8s ~]# kubectl get deployment
NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE
nginx 1 1 1 1 24s
[root@demo-k8s ~]# kubectl get rs
NAME DESIRED CURRENT AGE
nginx-3137573019 1 1 1m

由上面的例子可以看到,K8s 只要一个命令既可以启动以 nginx 为镜像的一个微服务实例。与此同时,K8s 的强大之处在于,方便用户用一个命令的同时,仍然保证了 K8s 应用体系的完整性和规范性。也就是说,虽然用户只运行了一个命令,但 K8s 为用户自动创建了四种 API 对象,包括:Deployment,ReplicaSet(RS),Pod 和 Container 要想扩展伸缩同一服务的实例个数也非常简单。

复制代码
[root@demo-k8s ~]# kubectl scale deployment nginx --replicas=3
deployment "nginx" scaled
[root@demo-k8s ~]# kubectl get rs
NAME DESIRED CURRENT AGE
nginx-3137573019 3 3 22m

依靠这种兼顾易用性和模型一致性的设计理念,K8s 使自己既适合简单场景也适合复杂场景。

单节点多容器模式

从单节点多容器模式开始的容器设计模式,是真正体现 K8s 设计特点的地方,也就是基于多容器微服务模型的分布式应用模型。在 K8s 体系中,Pod 是一个轻量级的节点,同一个 Pod 中的容器可以共享同一块存储空间和同一个网络地址空间,这使得我们可以实现一些组合多个容器在同一节点工作的模式。既然 Pod 的特点是共享存储空间和网络地址,那么单节点多容器模式一定是利用这两种特性的。

挎斗模式(Sidecar pattern)

第一种单节点多容器模式是挎斗模式。这种模式主要是利用在同一 Pod 中的容器可以共享存储空间的能力。

一个典型的挎斗应用场景如图所示:一个工具容器写文件到共享的文件目录,应用主容器从共享的文件目录读文件。例如,我们可以用 Nginx 构建一个代码发布仓库,简单的将代码放到某个本地目录即可。为了保持同步,我们同时用一个装有 Git 客户端的容器定时到原始代码仓库同步下拉最新的代码。这种模式的好处是,工具容器的镜像,也就是打包有 Git 客户端的镜像可以重用,而不需要跟应用的容器打包在一起。同样的应用,应用主容器不用 Nginx 也可以用 Apache Httpd,都可以跟工具容器组合起来形成微服务。

图:工具容器写文件应用容器读文件

另一个典型的挎斗模式如图所示:一个工具容器读文件,应用容器写文件。例如:一个基于 Nginx 的 Web 应用向系统文件系统写入日志,而一个收集日志的容器从共享目录读出日志,并输出到集群的日志系统。这一模式的好处在于,工具容器的镜像是可以重用的,不需要在每次更新应用容器打包的时候,把工具容器的执行文件打包进去。

图:工具容器读文件应用容器写文件

外交官模式 (Ambassador pattern)

第二种单节点多容器模式是外交官模式。这种模式主要利用同一 Pod 中的容器可以共享网络地址空间的特性。如图所示,在一个 Pod 中给应用容器搭配一个工具容器作为代理服务器。工具容器帮助应用容器访问外部服务,使得应用容器访问服务时不需要使用外网的 IP 地址,而只需要用 localhost 访问本地服务。在这种模式下,作为代理服务器的工具容器好像外部服务派驻在 Pod 中的“外交官”,使得应用容器办理业务时只需要跟本 Pod 的外交官打交道,而不需要出国了,因此而得名。

图:外交官模式的逻辑结构

基于外交官模式的 Redis 访问演示案例

我们这里用一个访问 Redis 服务的简单案例,来实践体验一下 Ambassador 模式和 K8s 单节点多容器模式的应用细节。本案例的文件清单在 Github 上。

图:基于外交官模式的 Redis 访问演示案例架构

1. 创建一个初始的 Redis Master 实例

先创建一个 Redis Master 节点的 Pod 用于初始化 Redis 集群。

复制代码
kubectl create -f examples/redis/redis-master.yaml

2. 创建 redis 的服务

创建一个 redis 的服务,这个服务可以在前段作为多个 redis 的 Pod 节点的负载均衡。

复制代码
kubectl create -f examples/redis/redis-service.yaml

3. 创建 redis 的 replication controller

创建控制多个 redis 服务 Pod 的 RC,当然也可以用 Deployment 或 ReplicaSet 来创建。

复制代码
kubectl create -f examples/redis/redis-controller.yaml
复制代码
[root@demo-k8s ~]# kubectl get rc
NAME DESIRED CURRENT AGE
redis 1 1 19h

创建完后可以用 kubectl get 命令查看 rc 和 Pod,会发现并没有产生新的 Redis Pod,这是因为原来的 Pod redis-master 已经满足了 replica=1 的要求。

4. 创建 redis-sentinel 的服务

创建 redis sentinel 服务。

复制代码
kubectl create -f examples/redis/redis-sentinel-service.yaml

5. 将 redis-sentinel 的 replication controller

创建控制多个 redis sentinel 服务 Pod 的 RC。

复制代码
kubectl create -f examples/redis/redis-sentinel-controller.yaml

6. 将 redis 实例和 redis-sentinel 实例扩展成 3 个

复制代码
kubectl scale rc redis --replicas=3
kubectl scale rc redis-sentinel --replicas=3

7. 删除掉手工启动的 redis 实例 redis-master

删除掉我们已开始手工创建的 redis master 的 Pod,redis 的 rc 会自动启动新的 redis 以满足 replicas=3 的要求。同时,Redis sentinel 节点会选举出一个新的节点作为 master 节点。

复制代码
kubectl delete pods redis-master

8.Redis 集群的验证方法

查询 redis 集群中所有 redis 实例的 IP。

复制代码
[root@demo-k8s ~]# kubectl describe pod -l name=redis | grep IP
IP: 10.120.44.3
IP: 10.120.63.3
IP: 10.120.80.5

我们知道,这 3 个 redis 实例中,只有一个是 Master 节点,是可写的,可以调用 SET 命令和 GET 命令;其他两个节点是 Slave 节点,是只读的,只能调用 GET 命令。我们可以用下面的命令测试三个 redis 节点。

复制代码
[root@demo-k8s ~]# echo -e "SET test1 SET test1 8\r\nQUIT\r\n" | curl telnet://10.120.44.3:6379
-READONLY You can't write against a read only slave.
+OK
[root@demo-k8s ~]# echo -e "SET test1 8\r\nQUIT\r\n" | curl telnet://10.120.63.3:6379
-READONLY You can't write against a read only slave.
+OK
[root@demo-k8s ~]# echo -e "SET test1 8\r\nQUIT\r\n" | curl telnet://10.120.80.5:6379
+OK
+OK

通过上面的测试我们可以知道只有 IP 为 10.120.80.5 的 redis pod 是 Master 节点,其他两个是 slave 节点。

对于只读的操作,我们可以利用 redis 的 service IP,通过 K8s 的 kube-proxy 来访问,如下,我们得到 redis 的 CLUSTER-IP 为 10.123.248.129,可以用这个 IP 来读取 Redis 数据。那么,如果用这个 IP 来写数据将怎么样呢,后面将看到。

复制代码
[root@demo-k8s ~]# kubectl get svc -l name=redis
NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE
redis 10.123.248.129 <none> 6379/TCP 5d
[root@demo-k8s ~]# echo -e "GET test1\r\nQUIT\r\n" | curl telnet://10.123.248.129:6379
$1
8
+OK

9. 制作 Ambassador 容器镜像

截至目前,我们还没有用到外交官模式。下面我们用 Haproxy 制作一个外交官代理,用来访问 Redis 服务,使得跟该容器在同一个 Pod 里的容器,在访问 Redis 读写服务的时候,都只需要访问本地 localhost 服务。本例中的文件在 Github 上可以找到。

Dockerfile 文件清单:Dockerfile

复制代码
FROM haproxy:1.5
COPY tmpl-haproxy.cfg /
COPY starthaproxy.sh /
CMD ["sh"Node.js "-c"Node.js "/starthaproxy.sh"]

启动 haproxy 的文件清单:starthaproxy.sh

复制代码
echo
"Proxying localhost ${INPUT_PORT} to ${TARGET_IP}:
${TARGET_PORT}"
复制代码
cat tmpl-haproxy.cfg |
sed -e "s/INPUT_PORT/${INPUT_PORT}/" -e "s/TARGET_IP/${TARGET_IP}/" -
e "s/TARGET_PORT/${TARGET_PORT}/" > /haproxy.cfg
haproxy -f /haproxy.cfg

通过这里的 Dockerfile 构建的代理服务器容器,根据输入的环境变量 INPUT_PORT、TARGET_IP 和 TARGET_PORT,可以将发向本地 INPUT_PORT 的服务请求转发到目标 TARGET_IP:TARGET_PORT 上去。
因此这个容器可以作为一个简单方便的本地代理服务器使用。

10. 使用 Ambassador 代理服务器的 pod

使用 Ambassador 代理服务的 Pod 文件清单:demo-redis-amb-centos.yaml

复制代码
apiVersion: v1
kind: Pod
metadata:
labels:
name: demo-redis-amb-centos
name: demo-redis-amb-centos
spec:
containers:
- name: app-main-container
image: centos:7
args:
- sleep
- "1000000"
- name: redis-amb-read
image: xwangqingyuan/port-forward-haproxy
env:
- name: INPUT_PORT
value: "6379"
- name: TARGET_IP
value: "10.123.248.129"
- name: TARGET_PORT
value: "6379"
ports:
- containerPort: 6379
- name: redis-amb-write
image: xwangqingyuan/port-forward-haproxy
env:
- name: INPUT_PORT
value: "16379"
- name: TARGET_IP
value: "10.120.80.5"
- name: TARGET_PORT
value: "6379"
ports:
- containerPort: 16379
volumes:
- name: data
emptyDir: {}

在这个 Pod 中,我们用一个 CentOS 容器作为应用的主容器来使用 Ambassador 工具容器,当然也可以用实际的应用容器 Tomcat、Node.js、PHP 等等,因为这里只是为了演示测试 Ambassador 代理,我们用 CentOS 作为应用容器。其中我们有两个外交官容器,一个用来从 Redis 集群读数据,容器的名字为 redis-amb-read,它将发向本地 6379 端口的请求转发到 redis 服务的 CLUSTER-IP,最终会轮训地发送给任意一个 redis 实例;另一个用来向 Redis 集群写数据,容器的名字为 redis-amb-write,它将发向本地 16379 端口的请求转发到 redis master Pod 的 IP。

11. 测试一个使用 Ambassador 模式的 pod

创建一个使用 Ambassador 代理的 Pod,并登陆到主容器进行测试。

复制代码
kubectl create -f /home/centos/worktemp/redis/demo-redis-amb-centos.yaml
kubectl exec -it demo-redis-amb-centos /bin/bash

测试对读容器本地 Ambassador 的访问。

复制代码
echo -e "SET test1 10\r\nQUIT\r\n" | curl telnet://localhost:6379
-READONLY You can't write against a read only slave.
+OK
echo -e "SET test1 10\r\nQUIT\r\n" | curl telnet://localhost:6379
-READONLY You can't write against a read only slave.
+OK
echo -e "SET test1 10\r\nQUIT\r\n" | curl telnet://localhost:6379
+OK
+OK
echo -e "GET test1\r\nQUIT\r\n" | curl telnet://localhost:6379
$2
10
+OK
echo -e "GET test1\r\nQUIT\r\n" | curl telnet://localhost:6379
$2
10
+OK
echo -e "GET test1\r\nQUIT\r\n" | curl telnet://localhost:6379
$2
10
+OK

从测试结果可以发现,对读容器执行 GET 操作时,操作总是成功的,说明 3 个 redis pod 都可以读取数据。而对读容器执行 SET 操作时,3 个操作只有一个是成功的,也就是说只有负载均衡将请求发给 redis master pod 时,操作能够成功。

测试对写容器本地 Ambassador 的访问。

复制代码
echo -e "SET test1 10\r\nQUIT\r\n" | curl telnet://localhost:16379
+OK
+OK
echo -e "SET test1 10\r\nQUIT\r\n" | curl telnet://localhost:16379
+OK
+OK
echo -e "SET test1 10\r\nQUIT\r\n" | curl telnet://localhost:16379
+OK
+OK

从测试结果可以发现,对写容器执行 SET 操作时,操作总是成功的,说明 redis-amb-write 容器将所有请求都转发给了 redis master pod。

适配器模式(Adapter pattern)

第三种单节点多容器模式是适配器模式。这种模式对于监控和管理分布式系统尤为重要。对分布式系统的一种理想设计目标,就是能够实现“分布地执行和存储,统一的监控和管理”。要想实现“统一的监控和管理”,应用和监控管理交互的接口需要是统一的,而且其接口是依照“统一的监控服务”的接口模式来实现。这和面向对象设计模式中的“适配器模式”也非常相似。

图:统一监控服务和监控适配器模式

图:利用 Prometheus 作为监控服务的分布式系统

一个典型的可以采用适配器模式的系统,是利用 Prometheus 作为监控服务的分布式系统。在 Prometheus 周边项目中,有诸多适用于不同应用系统的监控数据输出器(Exporter),负责收集跟特定应用相关的监控数据,使得 Prometheus 服务可以以统一的数据模式收集不同应用系统的监控数据,每个 Exporter 同时也都是一个适配器模式的实现。

总结

本文主要介绍了 K8s 集群中,单节点单容器模式和基于 Pod 模型所支持的几种单节点多容器模式,包括挎斗模式、外交官模式和适配器模式。并且对于外交官模式,利用一个 redis 集群案例演示了如何利用外交官模式以访问本地服务的模式访问网络服务。

后续,K8s 社区肯定还会发展出其他的容器设计模式,但不论如何,单节点多容器模式主要是利用同一 Pod 中的容器可以共享存储空间和网络地址空间的特点。本文案例中的代码只能用来演示,还比较简单,有很多的地方可以增强。例如:用 Replication Controller 的地方,可以用 Deployment 或 Replica Set 来部署,这是 K8s 社区更为推荐的方式;对于案例中的外交官容器,Master Pod 的 IP 和 Redis 服务的 IP 未必需要写死,而是可以通过脚本从指定的服务读取;此外,演示中所用的代理服务器容器也可以增强为支持多个端口映射的代理服务器。

答疑环节

问:考虑到性能,分布式文件存储可能不会适合,K8s 有没有针对 database 的略微完美的部署方案?

王昕:完美的应该没有。有两个 K8s 功能可能跟 database 的存储有关,一个是 nodeAffinity 一个是 PetSet。

nodeAffinity 的作用是让你可以为 Pod 选定特定的节点运行,例如你想要的高性能的挂载了 SSD 的节点。PetSet 的作用是让 Pod 可用于有状态服务,可以固定跟某个存储绑定。这两个功能都处于非常早的阶段还很不成熟,但可以肯定这些功能是 K8s 社区专门解决有状态服务和存储问题的方案。

在 nodeAffinity 和 PetSet 设计稳定之前,还有一个 nodeSelector 也是可以利用的。它可以让用户选定特定的 node 部署 Pod,同样,你可以选定挂有高性能存储的节点。

问:希望更深层次了解一下 Overlay 的网络原理,能否介绍一下?

王昕:Overlay 也就是覆盖网络,跟隧道技术是紧密相关的,通过网络封装技术模拟二层网络,跟 VPN 所采用的技术是类似的。

简单说,比如 A 和 B 之间是没有网络直接用以太网连接的,就是所谓的二层不连通。但是通过三层的 IP 层可以连通,也就可以建立四层的连接,比如 UDP 可以连通,也就是四层连通,二层不连通。但是我可以,通过四层的连通传输二层以太网的数据包,模拟出一个二层连通的以太网来。这个模拟出来的连通的二层网络就叫 Overlay,下面真实的物理网络叫做 Underlay。

所以说,Overlay 的技术每一层封装会增加一些开销,会影响一定性能。此外,各个层次对网络丢包延迟的问题处理不好,也会影响性能。K8s 比较流行的基于 Overlay 的组网技术有 Flannel 和 Weave,Weave 目前看性能是比较差的。Flannel 有两种隧道封装技术,UDP 和 VxLAN,一般来说 VxLAN 性能比 UDP 好很多。Overlay 有一定的性能劣势,也有优势,主要是降低对下面物理网络的依赖,很容易搭建。


感谢魏星对本文的策划和审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2016-11-02 17:563011

评论

发布
暂无评论
发现更多内容

爆款元服务!教你如何设计高使用率卡片

HarmonyOS开发者

HarmonyOS

ABBYY FineReader PDF 15 for Mac中文激活版

iMac小白

pycharm pro 中文永久破解版下载

iMac小白

G口服务器有多快

Geek_f19a80

服务器

2023年好用的远程协同运维工具当属行云管家!

行云管家

IT运维 远程运维 远程连接 远程系统

MAMP Pro for Mac激活版下载(PHP/MySQL开发环境)

iMac小白

MAMP PRO for Mac MAMP Pro破解 MAMP Pro Mac下载

SnailSVNPro激活专业版:Mac电脑专业的SVN客户端

iMac小白

SnailSVN Pro SnailSVN Pro下载 SnailSVN Pro mac

即时通讯技术文集(第23期):IM安全相关文章(Part12) [共15篇]

JackJiang

网络编程 即时通讯 IM

人工智能视频增强软件Topaz Video AI for mac 完美激活版下载

iMac小白

大模型训练:推动自然语言处理发展的强大引擎

百度开发者中心

自然语言处理 深度学习 大模型

热换站2D组态 热换机组监测控制系统

2D3D前端可视化开发

物联网 组态软件 智慧供暖 城市换热站 换热机组

Milvus性能优化提速之道:揭秘优化技巧,避开十大误区,确保数据一致性无忧,轻松实现高性能

汀丶人工智能

人工智能 Milvus 向量数据库 检索系统

飞码LowCode前端技术系列:如何便捷快速验证实现投产及飞码探索 | 京东云技术团队

京东科技开发者

Vue 前端 低代码 企业号11月PK榜

大模型训练:量化策略与优质数据集的重要性

百度开发者中心

人工智能 大模型

Excel LTSC 2021 中文破解版 Excel LTSC 2021 最新激活下载

iMac小白

Excel2021 Excel激活版 Excel破解版 Excel下载

Java基础面试题【分布式】组件

派大星

Java 面试题

数据库性能优化新选择:NineData慢查询分析

NineData

数据库 架构 数据分析 服务器 优化

图形化探索:快速改造单实例为双主、MGR、读写分离等架

GreatSQL

greatsql

用强数据类型保护你的表单数据-基于antd表单的类型约束 | 京东云技术团队

京东科技开发者

数据类型 表单设计 数据类型位数 企业号11月PK榜

记一次线上问题引发的对 Mysql 锁机制分析 | 京东物流技术团队

京东科技开发者

MySQL 数据库 死锁 间隙锁 企业号11月PK榜

云服务器一年低至81元!2023双十一云服务器降价大盘点

学IT的小树叶

服务器 云服务器 阿里云服务器 海外云服务器

率先支持Kuasar!iSulad Sandbox API 简化调用链,沙箱管理能力增强

华为云开发者联盟

云原生 后端 华为云 华为云开发者联盟

ChatGLM3-6B:新一代开源双语对话语言模型,流畅对话与低部署门槛再升级

汀丶人工智能

人工智能 自然语言处理 深度学习 大语言模型 chatglm3

定做舞台租赁LED显示屏要注意什么

Dylan

LED LED显示屏 led显示屏厂家 户内led显示屏

最强大模型训练芯片H200发布!141G大内存,AI推理最高提升90%,还兼容H100

Openlab_cosmoplat

深入跨域 - 从初识到入门 | 京东物流技术团队

京东科技开发者

前端 跨域 WMS仓库管理 企业号11月PK榜

《Kubernetes与云原生应用》系列之实践案例“单节点多容器模式”_语言 & 开发_王昕_InfoQ精选文章