这是一篇以问答形式针对 linux 运维技术中“一致性”问题的总结。
适合的读者包括一般运维人员和全栈工程师,以及希望了解运维技术的 IT 工作人员。
本文重点在于软件层面的抽象方法,而不涉及硬件层面的网络拓扑。
尺度上说,以测试集群 10~100 机器为例,我也没有更大规模的运维经验。生产集群取决于开发人员配备,而生产集群取决于业务类型。以测试集群来衡量更容易体现运维方法的合理性和可扩展性。
简单说,“一致性”问题是运维工作中,为保持开发、测试和生产集群的环境一致所涉及的难点以及解决方法,包括但不限于硬件、网络、操作系统和软件环境。
目的在于在保证可用性的前提下,简化从开发到测试再到部署的时间以及人力成本。
不能保证一致性的情况下,可用性也不能保证,由此带来的人力和时间成本浪费也非常严重。
无论底层实现如何,最终产品运行状态呈现保证二进制的一致性(byte-identical),同时此种保持一致性的方法具有可重现性(reproducible),以方便移植和二次部署。
对于软件构建而言,一致性的目标是:对于确定的输入来源给出二进制一致的构建结果。
一般来说,硬件和网络的一致性不强求。硬件仅仅需要 cpu 架构一致即可,而网络一般以部署时参数作为一致性控制手段。
操作系统的一致性强调内核、工具链、运行时依赖和包管理工具等方面,不强调发行版本身。(尽管多数情况下,发行版决定了一致性的众多因素的选择范围,但选择发行版并不足以保证一致性。)
软件环境的一致性通常依赖版本控制系统实现,偏向开发范畴。以二进制形式发布可以减少对于操作系统一致性的依赖。(比如采用容器技术)
从硬件基础到最终的产品呈现,有着过多的层级和变量,维持每个层级的一致和由此引入的管理复杂度都是难点所在。
实际生产中,最大的非一致来源是操作系统层面的。
确定性的来源有:
不确定性的来源也很多:
首先确定发行版,以及发行版的版本。
内核可以以内核配置文件的形式保持一致,在官方内核源的基础上进行编译。
工具链系统可以依赖 Bootstrapping) 进行二次构建。
运行时依赖主要依靠发行版提供的工具进行切换,或者需要手工介入。动态链接库靠 LD_ 相关参数来调节,动态解释器如 python 通过环境变量等调节。
包管理需要提供基于源代码的构建方式,最好有官方源支持。当然也可以人工操作,只是在大规模集群的管理上,工作量可能不亚于重新实现一套包管理系统。
使用容器技术可以封装包括工具链、运行时依赖以及包管理工具在内的系统组件,可以大大简化生产系统二进制发布的一致性问题。但仍然需要一整套具有一致性的开发和测试系统来生成具有一致性的容器本身。
简单总结是:基于二进制的发行版目前连自身官方源的一致性都不能解决。
Debian ReproducibleBuilds
Debian 这个可能是应用最广泛的发行版仅仅从 2014 年底才开始重视一致性构建,经过两次大规模重构之后,约有 75% 的官方二进制包符合一致性构建标准。(即还有 25% 的二进制包不能确定是由官方源生成。)
目前仅在 unstalbe 分支进行工具链相关的一致性构建测试。由于一般 Debian 用户会由于稳定性原因使用 stable 分支,依赖 Debian 官方二进制源的一致性构建还需要运维人员自行解决。
openSUSE open-build-service
open-build-service 是一套构建平台,Reproducible 是它的特性之一,该项目的应用相对广泛。
主要适用于纯二进制程序发布,后端实现是脚本化的一致性构建流程,依赖非常复杂。
官方构建平台的二进制源可用性和可靠性尚可,但不解决运维层面的一致性问题。
Fedora(RHEL, CentOS) BuildId
提出了一套“记录-重现-构建-验证”的流程,但并没有在官方源大规模应用。
该流程建立在可信工具链的基础上,对于运维来说,依旧不解决问题。
Arch
无官方支持,官方源依赖 openSUSE 的构建平台
尽管容器技术是近一年才成为主流,而且容器技术的主要目标并非解决一致性问题,但它确实从另一个角度解决了 linux 集群运维的问题。
传统解决办法是将“操作系统--工具链--运行依赖--软件”中的除了软件的部分全部交给运维,结果是无论效率还是可靠性都不能保证。
使用容器技术可以将一致性问题重新解构,运维的工作变成“操作系统--容器宿主--一致容器“,软件自身依赖交给开发人员。
容器技术解决了一个问题:即使无法重建完全一致的依赖,也可以将当前状态以容器的形式进行二进制发布。而运维层面,解决可重建问题比起解决一致性问题要容易得多。
真正具有生产意义的基于源代码的发行版,可能仅有 Gentoo 一家。
基于源代码的发行版,天然地解决了“操作系统--工具链--运行依赖”的一致性问题。
Gentoo 系统的构建相当于在单一系统上完成了一次交叉编译,最终系统的工具链、基础系统和运行依赖全部由本地参数控制生成。假如系统中任何一个环节需要更新安全补丁,可以在任何时间重建从内核到编译器到二进制软件的全部依赖。
基于 Gentoo+docker 的一致性构建引入了一个新的问题:如何构建具有一致性的容器本身?
Docker 一直没有解决的一个问题是 Nested builds,通常用来解决一致性问题的办法就是,在 docker 容器中运行 docker,换句话说是构建一个“可以生成其它容器的”容器。
不得不说,这种办法引入的问题和管理复杂度不比它解决的问题少。
使用 Gentoo 有一个相对优雅的解决方案,既然 Gentoo 可以非常容易地构建一个一致的系统,也可以构建一个一致的 Gentoo 容器。这个容器既可以作为构建平台,也可以作为生产平台。
更进一步,这个构建平台可以持久化,保留构建缓存,有其它项目需要构建容器的时候可以重用。另外生产平台可以除去用不到依赖以减小体积,而生产平台的内容可以以二进制的形式直接嵌入到生产平台中,而不需要任何改动。
wking/dockerfile 就是这样一种思想下的实践。
edannenberg/gentoo-bb 在前者的基础上针对生产环境做了前述的优化。
NixOS 也可以作为 Gentoo 替代。
相比 Gentoo,NixOS 在内核支持、官方包数量和基于源的编译支持上都有所欠缺,发行版配套工具也不够全面和易用,社区支持相对也弱。
不过 NixOS 在灵活性和管理思路上要领先很多。对于全栈或者 DevOps 更加友好。
由于 NixOS 本身并不专注与源代码发布,所以在一致性问题上,需要人工介入比较多。
1
vietor 2015-07-28 07:29:42 +08:00 via Android
我们现在,一部分源码编译,一部分官方二进制,自建DNS。
|
2
clongbupt 2015-08-27 00:20:16 +08:00
基于操作系统发行版如何解决分布式系统一致性中的差异性:如根据系统分工不同,前端服务器,后端服务器,数据库服务器,日志系统,监控系统等系统,我们如何对操作系统的发行版进行考虑?
|
3
zhuang OP @clongbupt
这个问题太笼统了。笼统地回答的话,有两个方面: 一是数据和应用(容器)分离,简单的做法就是使用 docker data-only volume 或者其它第三方作为存储后端; 二是配置和应用(容器)分离,在容器镜像的构建上嵌入某种自动化配置管理,比如基于服务注册和发现的方法和模型。 帖子里的重点在于 Reproducible ,你的问题侧重于 Stateless ,目前来说两个问题都没有成熟的解决方案。 |
5
zhuang OP @fool 这个事情实践大于理论,我写的东西主要是理论。如果你要考别人,可以分两个层次,一个是基础层面的,基本的版本控制工作流程,二是经验方面的,大规模部署技术。
个人层面可以从常见问题开始,比如是否遇到过开发机正常,测试通过然而上线出错的情况,进而如何排查,如何改进降低出错可能等等。 技术层面我也不是专家,毕竟工作在一线有大规模运维经验的很少。可以侧重问控制侧重点是什么,策略流程中自动化的程度等等。 |
6
fool 2016-11-18 18:46:15 +08:00
|