观点 | Opinion

Rethink:为什么微服务没有sidecar不行?

作者 蔡芳芳 采访嘉宾 蔡书

几乎是随着Service Mesh概念的普及,“sidecar”这个词被容器从业者提到的频率也越来越高。甚至可以说,sidecar proxy已经是Service Mesh的第一技术特征。今天我们就和Flomesh团队负责人蔡书一起来聊聊为什么会出现sidecar proxy,以及除了proxy还有哪些sidecar,最后展望一下“理想化”的sidecar应该是什么样子。

Rethink系列回顾:《Istio之外,我们需要什么样的服务网格?》

InfoQ:能否先简单解释下什么叫sidecar?

蔡书:Sidecar本意是指三轮摩托旁边的那个挎斗。Kubernetes在形成初期,就把“容器”这个模型演化成了“pod”模型。Pod就是多个容器“编组使用”,或者简单地说,如果把一个“pod”类比成一个“虚拟机”,那么多个容器就是这个虚拟机里边的多个进程。这些进程里边,那些配合业务进程的其他进程我们一般就称为sidecar。比如pod运行的是一个Spring Boot的进程,在这个pod里边我们部署了采集日志的Filebeat进程,部署了代理流量的proxy进程,那么这个采集日志和代理进程都叫做“sidecar”。

在sidecar这个说法被广泛使用之前,其实sidecar这种用法就已经被普遍使用了,比如上边例子说的有些容器玩家会在容器内部署采集日志的进程。但是sidecar这个叫法是随着Service Mesh出现而流行起来的。在几乎所有讨论Serivce Mesh的材料和文章中都会出现sidecar这个词,如果从自然语言分析角度看,“Service Mesh”和“sidecar”是关联度极高的两个词汇。事实上,和Service Mesh一起说的sidecar,是“sidecar proxy”的一个简化说法。所以当讨论的上下文是“Service Mesh”的时候,说到“sidecar”,一般就是指“sidecar proxy”。

InfoQ:这么说sidecar其实不算是个新事物了~

蔡书:虽然这个词汇是最近几年随着服务网格开始在容器圈流行,但是sidecar模式其实早就存在,甚至可以说从操作系统进入了“多任务”以后就出现了。

InfoQ:能详细说说sidecar的历史渊源吗?

蔡书:这个话题非常有趣,我试着从三个方面聊下,一个是操作系统,一个是应用服务器,一个是容器时代的pod,他们之间内在有时间上的先后顺序,逻辑上他们的模型也有共性之处。

这里先做一个假设,就是我的目标是运行一个“业务程序”,比如一个SpringBoot的Java程序,当启动这个SpringBoot程序的时候,我需要用Java springboot.jar之类的命令来启动这个进程。在典型的Linux上,通常我们会把这个命令包装到一个脚本里边,用类似start-springboot.sh之类的脚本来启动。事实上,在我们启动这个Java进程之前,就有很多进程在运行了,典型的比如说监控进程,比如zabbix agent;还有比如日志收集的进程,比如filebeat,等等。

其实除了这些大家常见的进程,还有一些大家可能未必留意的进程,也在运行了。典型的比如crond,大家都用crontab做一些定时任务,crond进程就是管理这个的。记得以前常常有用户问我们“能不能给我一个最小最干净的操作系统”,其实他指的就是在运行业务进程的时候,只有最少的其他进程在运行。这些“其他进程”,从逻辑模型看,都可以认为是业务进程的sidecar。从功能的角度来说,我们会发现,这些sidecar进程可以分为几类,一类是辅助业务的,比如定时任务;一类是管理员需要的,比如日志采集、监控;一类是系统本身需要的,操作系统在启动内核后,会启动一个“超级进程”,称为initd进程,后来的systemd进程是initd的替代品,这个超级进程会启动一些进程用来辅助操作系统的功能,最典型的比如kdump,用来在程序崩溃时候收集信息。概括地说,早期使用单机操作系统运行业务进程的时候,我们有三类“sidecar”,#1是业务自身需要的,#2是管理员需要的,#3是操作系统需要的。

说完操作系统,我们再说应用服务器。千禧年之后,随着Java进入企业领域,也就是当时的J2EE,应用服务器开始普及。以Java应用服务器来说(事实上几乎各种语言都有自己的“应用服务器”),应用服务器有自己的启动过程,在这个过程中,应用服务器会启动很多“线程”,这些线程也是辅助完成业务逻辑或者实现管理功能的,他们的本质也是sidecar。

进入容器时代以后,尤其是K8s提出pod的概念,围绕业务进程的辅助进程模式更加清晰和明确,这些辅助进程就是sidecar。同样,这些sidecar也还是完成着传统的三方面功能,一方面是辅助业务的;一方面是面向管理员的;一方面是面向系统的。容器环境是典型的云环境,所以这里的“面向管理员”和“面向系统”又有了新的含义和进一步的需求。

InfoQ:感觉当前对于Service Mesh、微服务,甚至可以扩大到云原生,似乎都是没有sidecar不行?为什么sidecar这么重要?

蔡书:说“没有sidecar不行”不如说“sidecar的普遍使用是客观现实,是刚需”。这种刚需的产生我觉得有这么几个因素,一个是不同角色需要不同能力的进程协助完成工作,比如业务进程、应用运维、系统运维,彼此工作的和角色的差异,导致了每个角色用自己的sidecar辅助进程是一种分工,也是一种清晰的工作边界。

另一方面,有些需求是在业务上线后出现的,那么这个时候,用一个附加的sidecar进程完成管理工作就很顺理成章。还有一点,进程是现代Unix和类Unix系统的基本管理单位,进程级的管理是非常成熟的技术。作为一个对比,其实上一代应用服务器,比如JEE应用服务器,尝试把很多辅助管理能力放到线程级,但是后来又都逐渐分离到外部进程了。所谓“一个快速、简洁的Java进程”。这些客观的需求,使得sidecar辅助进程成为一种刚需。

InfoQ:使用sidecar模式的优势是什么?

蔡书:就像上一个问题里说到的,sidecar辅助进程的出现很多时候是顺理成章、水到渠成的,是一种最佳选择。简单来说,sidecar本质就是分而治之,并且天然具有“分工”的属性,还可以用来解决很多“上线时候还没有的管理需求”,扮演了管理上的补丁角色。从sidecar proxy的角度看,proxy提供一种“切片管理”的能力,这个是非常经典的设计模式。

InfoQ:前面回顾sidecar历史渊源的时候,您最后提到容器环境带来的“新的含义和进一步需求”,这个怎么理解?能不能具体说说?

蔡书:容器和K8s的普及,使得“云”距离用户更近了一步,这里,我总结了一些和sidecar相关的技术变化,以及“云”本身带来的一些变化。很多时候,我认为这些变化和特征也是“云原生”的特征和驱动力的一部分。

具体包括这么几个部分:

  1. 容器启动的时候,有一个PID=1的进程。通常而言,这个进程就是“业务进程”(实际上pod启动还有个init进程,叫做pause)。当我们想启动sidecar进程的时候,目前的做法是通过hook,在启动过程中启动sidecar。实际上,这个时候K8s(或者具体的说Kubelet)其实是一个超级进程,它负责启动业务进程和sidecar。很多时候,sidecar和业务进程,甚至是sidecar之间,是有启动顺序的。但是目前在K8s的体系内,缺少对这种依赖的管理。如果大家熟悉Linux的initd和systemd,会知道系统的启动过程是由一堆脚本控制的,也包含了一些优先关系。在操作系统时代,这些sidecar的启动是个“准标准”,也是可以通过Shell脚本“定制化”的。总结来说:pod的init过程在标准化、定制化、扩展方面比较简单,在一个pod内容器多且有依赖和关联的时候,当前的机制不够灵活。所以第一个“新”需求就是pod内多进程的启动依赖及顺序,目前容器平台没有提供相应的机制。
  2. 容器快速被认可,背后最主要的原因是容器“轻量化”,而轻量化是“敏捷”的前提。当我们需要通过sidecar进程来完成某一种辅助功能的时候,我们需要这个辅助进程尽可能轻量化,否则就会有一种头重脚轻的感觉。前几天有个技术分享,提到他们业务容器的资源配置是4C8G,然后sidecar资源配置是4C4G。对这种配置,我只能感慨“有钱真好”。大多数用户还是希望sidecar是很轻量化的。概括地说,第二个“进阶”需求是,pod内的sidecar辅助进程需要比传统主机上更轻量化。
  3. 权限。当我们在虚拟机或者物理机上部署sidecar这些辅助进程的时候,有时候我们需要特权,因为读写一些系统信息需要高权限。在主机(虚拟机、物理机)时代,超级用户加上业务用户的两级权限很好地满足了不同用户运行不同进程权限管理的需求。但是在容器时代,这个事情变难了。容器环境里,除了宿主机的管理(通常是特权用户)和业务进程(通常是普通用户)之外,多了这些sidecar辅助进程。这些sidecar辅助进程,有些也需要特权、更多的最好是用普通用户。相当于在容器环境和pod里,其实出现了3个等级的权限需求:宿主机的root、容器的“root”、容器的普通用户。这层权限的出现,本质上是云的多租户导致的。目前来看,这个中间层的权限比较难处理,因为业务进程和辅助sidecar进程是共享namespace的。概括起来,第三个“新”需求是sidecar辅助进程通常要求一种介于宿主机root和业务进程之间的权限能力。
  4. 扩展性。主机上传统的sidecar辅助进程,一般都有自己的配置方式(配置文件格式),以及一些扩展方式,比如zabbix可以运行脚本等。这些传统的辅助进程进入到容器后,管理其实更难了。之前积累的很多管理工具,可以通过配置工具如ansible之类下发,而容器一般提供的方式是configMap,这种配置和扩展的变化导致了严重的碎片化,而且方式改变也带来了新的困难。Sidecar辅助进程的功能,很多是需要这种客户化和现场定制化的,这部分需要通用、简单的扩展机制和方式。因此,第四个“进阶”需求是sidecar辅助进程通常需要比传统主机上更简单、易用、且强大的扩展能力。

大概总结这么几个,其实还有一些,以后想起来再讨论。

InfoQ:针对上面这些问题和需求,业内现在有什么可行的解决办法吗?

蔡书:这是个好问题,在我看来,整个行业都发现了这些新问题——就是说,pod里正在被塞进越来越多的sidecar。因此整个行业也都在探索可行的解决办法。这里介绍下我们开源项目pipy的探索:我们通过infra container的方式为每个容器提供了PID=1的sidecar,这个pipy进程类似Linux中的initd或者systemd,它的作用是根据需要引入和执行其他的功能;通过pipy repo和pipy js的能力,pipy可以提供目前我们已经识别的各种sidecar所需要的功能,比如服务注册/注销、服务发现、日志采集、指标采集、主动健康检查、定时任务、提供网络代理(也就是sidecar proxy能力)等。这是一种尝试,也欢迎大家一起来探讨。