edwin1986
作者edwin19865天前
系统架构师, 上汽通用汽车

某汽车主机厂容器最佳实践——可行性分析

字数 7204阅读 3174评论 1赞 6

1 技术可行性分析

1.1容器技术背景

虚拟化技术作为云计算的前提之一,不断伴随引发着深远的技术变更。早期的虚拟机虚拟化,将传统物理计算资源虚拟化为若干虚拟服务器,可各自运行各自的操作系统,从而提高整体计算资源利用率。但随着业务敏捷需求逐步显现,业务服务颗粒度不断变小,微服务架构便随之产生。微服务架构的细颗粒度隔离需求和管理需求使得容器技术逐渐成为对云计算领域具有深远影响的变革技术。容器技术的发展和应用,将为各行业应用云计算提供了新思路,同时容器技术也将对云计算的交付方式、效率、PaaS平台的构建等方面产生深远的影响,具体体现在以下几个方面:
• 部署:容器可以将应用打包成单一可运行、可储存的组件,并且在任何时间任何地点进行部署。
• 启动:容器共享宿主机操作系统kernel,使得容器中的进程启动和在操作系统中类似,不必像虚拟机一样额外启动操作系统,故启动速度将变快。
• 组装:容器的细颗粒度和轻量化实现导致运行在容器中的应用可以轻易进行服务组合和再封装。
• 迁移透明:容器技术最重要的价值就是为在不同宿主机上运行服务提供一个轻便的、一致的格式。容器的标准化和CNCF等组织的成立,使得相关技术逐步标准化和透明化,避免局限于单一的平台提供商。

1.1.1 相关术语及缩略语

术语 定义/解释
镜像 容器运行的必备软件集合,可用于运行容器实例。
微服务架构 微服务架构是一种特定的软件应用程序设计方式——将大型软件拆分为多个独立可部署服务组合而成的套件方案。
DEVOPS 可定义为是一种过程、方法、文化、运动或实践,主要是为了通过一条高度自动化的流水线来加强开发和其他IT职能部门之间的沟通和协作,加速软件和服务的交付。
CI/CD Continuous Integration/Continuous Delivery,即持续集成和持续交付。通过自动化手段持续开发集成测试并交付应用服务,到达快速交付的目的。
CaaS Container as a Service ,即容器即服务。
CLI command-line interface,即命令行界面。
DNS Domain Name System,即域名系统
IaaS Infrastructure as a Service,即基础设施即服务
PaaS Platform as a Service,即平台即服务
SaaS Software as a Service,即软件即服务
SDN Software Defined Network,即软件定义网络
LXC Linux Container,即Linux容器
VM Virtual Machine,虚拟机



1.1.2 Docker容器优势及原理



Docker是一个开源项目,诞生于2013年初。最初由dotCloud公司开发,其基于Go语言实现。后续该项目加入了Linux基金会,遵从了Apache2.0协议,代码在GitHub上进行维护。
Docker项目的目标是实现轻量级的操作系统虚拟化解决方案。其隔离的基础是基于Linux Kernel实现。无论是早期使用的LXC还是后续自开发的libcontainer,Docker都基于底层的隔离进行了进一步的封装,使得对应用用户透明,不必去关注底层实现,操作就像一个快速轻量级的虚拟机一样简单。
传统虚拟机虚拟化和容器虚拟化的差异,参考下图。
xtpu0zo1mssr
传统虚拟机通过Hypervisor层进行虚拟化隔离,以此运行半虚拟化操作系统。应用软件再运行再虚拟机之上。而容器隔离则依赖宿主机操作系统Kernel,通过其API的隔离性功能实现虚拟化,并直接复用本地主机的操作系统。

对比项 虚拟机 容器
虚拟化级别 运行在虚拟硬件上,应用运行在虚机上 宿主机的一个进程,直接运行在宿主机上。
运行环境 必须是物理机 物理机、虚机、甚至是容器内
启动速度 分钟级 秒级
大小 独立 Kernel,附加各类服务 共享 Kernel, 一项服务
弹性计算 纵向扩容,但需重启 横向扩容

libcontainer功能实现上涵盖了包括对Linux Kernel中namespaces的使用、cgroups管理、Rootfs的配置启动、默认的Linux capability权限集、以及进程运行的环境变量配置。
1ezwh3xob4sq
cgroups(Control Groups)最早由Google工程师于2006年提出,并再2007年整合进Linux内核。顾名思义就是把进程放到一个虚拟组中方便统一控制。其作为Linux内核提供的一种机制,可以把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。通俗的来说,其可以限制、记录、隔离进程组所使用的物理资源(包括:CPU、memory、IO等),为容器实现虚拟化提供了基本保证,是构建Docker等一系列虚拟化管理工具的基石。
对于操作系统Kernel隔离而言,隔离意味着可以抽象出多个轻量级的内核(容器进程),这些进程可以充分利用宿主机的资源,宿主机有的资源容器进程都可以享有,但彼此之间是隔离的,同样,不同容器进程之间使用资源也是隔离的,这样,彼此之间进行相同的操作,都不会互相干扰,安全性得到保障。为了支持这些特性,Linux namespace 实现了 6 项资源隔离,基本上涵盖了一个小型操作系统的运行要素,包括主机名、用户权限、文件系统、网络、进程号、进程间通信。这 6 项资源隔离分别对应 6 种系统调用,通过传入对应参数,调用Linux API clone() 函数来完成。

1.1.3 Docker劣势及不足

容器虽然通过操作系统Kernel实现了虚拟化,但其毕竟没有物理上的Hypervisor层,无法实现如果虚拟机这样完全的隔离。同时,类似于原先虚拟机中vcenter、nova等调度方案,容器也需调度编排方案用于降低平台管理成本。综上,其劣势主要涉及以下三方面:
隔离性:Namespaces 和 cgroups 虽然允许一个进程以及它的子进程从共享的内核资源(入网络栈和进程列表)里获得一个仅自己可见的视图。虽然这样占用更低的内存,启动速度更快,但是会降低操作系统Kernel的安全性、稳定性和兼容性。一个极端的情况就是 Linux 内核接口,在内核和命名空间中运行不相容的或者未经测试的 glibc 可能产生一些无法预测的操作。
安全性:从设计上来说,Docker过于依赖Linux Kernel中Namespaces的能力,但Namespaces相比普通的Hypervisor有太多的漏洞。对于企业内部而言,可以通过一系列外部约束手段降低该风险,但对于在公有云多租户环境中却很难实现如上要求。一旦Namespaces漏洞导致宿主机被攻击,那么运行在其之上的所有容器实例将变得非常危险。
编排方案:通常一个应用程序在其架构中通常是多层的,可能包含若干应用服务组件即若干中间件组件,同时各层之间可能存在非常复杂的互相依赖场景。于此同时,容器的轻量及稳定性特点意味着其生命周期并非像传统虚拟机这么持久。因此需要通过一种编排手段来管理容器和容器,容器和外部依赖,容器和下层基础资源的依赖关系。原生的Docker只包含容器本身,但对于容器编排调度等管理工作则需要依赖其他方案完成。

1.2当前技术路线选择

通常一个集群编排调度平台需要实现如下功能:提高集群资源利用率、支持用户配置约束、及时管理以此保证彼此状态可知、“公平”调度,服务具有高可用性等。根据Google发布的关于其内部使用的Omega容器管理平台的白皮书中所描述的,实现上述功能主要可通过如下三种调度架构:
r78j6swhza3s

巨石架构(Monolithic)通过单一的调度代理来处理所有请求,通常用于高性能计算。巨石架构通常实现了一个单一的算法来处理所有的job,因此根据job的类型来运行不同的调度逻辑是困难的。
两级调度(Two-level)通过中央协调器来动态地决定各个调度模块可以调用多少资源。在这类架构里,分配器会将给定的资源一次性分配个一个调度模块,因此避免了资源使用的冲突,并且通过控制资源分配的顺序和大小来实现一种相对的资源公平分配。但是由于一个资源单元同一时间只能被一个调度模块使用,其使用的是悲观并发策略,速度相对较慢,且整体资源利用率不高。
共享状态调度(Shared-state)赋予了每一个调度模块对整个集群的全部访问权限,其之间可以自由地竞争。没有了中央策略引擎,每一个调度器能够自己做决定。通过支持独立的调度器实现和公布整个资源分配的状况。在Google Omega中不仅支持扩展多个调度器,还能够支持它们自己的调度策略。
对企业而言,容器编排调度工具的主要任务就是负责在最合适的宿主机上启动容器,并且将它们关联起来。同时其需要支持自动的故障转移来处理局部的底层宿主机资源错误。当某个容器实例服务能力不足响应业务需求之时,其能够扩展容器来解决问题。我司在2016年开始进行Docker容器的平台构建准备工作,期间主要调研了Docker Swarm 和Kubernetes 两种调度工具。

1.2.1 Docker Swarm

Docker Swarm是一个Docker内部包含的调度框架,其API集成在Docker API中。Swarm的架构由两部分组成:Swarm管理节点和Swarm 工作节点。
0gadfxqxnnbd3

Docker Swarm管理节点负责调度容器,其本身也可以通过集群部署实现高可用。由于Docker Swarm API与Docker标准API类似,开发者在使用Swarm的同时并不需要过多改变其原先使用的Docker工作方式。
Docker Swarm可包含若干个宿主机工作节点。这些宿主机在启动Docker daemon的时候就会打开相应的端口,以通过Docker远程API供Swarm管理节点调度。
关于调度策略,Docker Swarm采用了三个策略:
spread:最少的容器,并且忽视它们的状态
binpack:最拥挤(比如说,拥有最少数量的CPU/RAM)
random:随机选择
关于节点和容器选择过滤策略,Docker Swarm提供了两个节点过滤器和三个容器配置过滤器:
约束过滤器:每个节点可标记特定的key=value值,在运行容器时根据这个特定值进行查找。如果没有节点满足要求,这个容器将不会运行。
健康状态过滤器:用来防止调度不健康的节点。
Affinity过滤器:对于容器Affinity,在运行一个新的容器时指定Affinity容器,这些容器就会互相链接。如果其中一个容器停止运行了,剩下的容器都会停止运行。镜像Affinity会把想要运行的容器调度到已经拥有该镜像的节点上。标签Affinity会和容器的label一起查找容器宿主机进行运行。
依赖过滤器:其能够用来运行一个依赖于其他容器的容器。依赖意味着和其他容器共享磁盘卷,或者是链接到其他容器,亦或者和其他容器在同一个网络栈上。
端口过滤器可支持容器集群在特定端口上暴露服务端点。如果集群中没有任何一个节点该端口可用的话,系统就会给出一个错误的提示信息。

1.2.2 Kubernetes

Kubernetes是由Google开源的容器的编排系统,Docker可作为其容器的实现。其使用pod的概念来将多个容器划分为软件最小可执行逻辑单元。这些pod被共同部署和调度,形成了一个服务。相比于其他容器调度方式,这个方法简化了应用颗粒度对容器集群管理的复杂度需求。
Kubernetes调度器查找NodeName为空的pod,通过nodeSelector和Affinity来选择在哪台宿主机上运行。
rlut6l64q744
nodeSelector通过匹配所有宿主机是否包含指定的label字段来决定新pod运行在哪台宿主机节点。如果没有任何机器满足nodeSelector要求,则该pod会处于挂起状态,直到有机器能够满足条件。默认可用的nodeSelector Label如下所示:
- kubernetes.io/hostname
- failure-domain.beta.kubernetes.io/zone
- failure-domain.beta.kubernetes.io/region
- beta.kubernetes.io/instance-type
- beta.kubernetes.io/os
- beta.kubernetes.io/arch
- Affinity/anti-affinity node 则相对于nodeSelector机制更加的灵活和丰富:

灵活的语法:支持In,NotIn,Exists,DoesNotExist,Gt,Lt等语法定义。
支持定义软性要求和硬性要求。所谓硬性要求表示pod 调度到某个宿主机节点必须满足Affinity要求。软性要求表示scheduler的时候,无法满足调度要求的时候,会选择非nodeSelector匹配的节点。
如果在nodeAffinity的基础上添加多个nodeSelectorTerms字段,则调度的时候宿主机只需要在nodeSelectorTerms满足一条即满足nodeAffinity的规则。如果在nodeSelectorTerms中添加matchExpressions,则需要满足matchExpressions中的所有规则。

1.2.3 技术路线选择

我司着重调研了Docker Swarm和Kubernetes平台。
从架构的完整性上看由于Docker Swarm较为轻量,且与Docker原生API集成,使用较为简单。但其在容器模型抽象和集群调度的复杂需求满足度上就远比Kubernetes轻量。相比于Swarm,Kubernetes添加了pod和replica的逻辑。这个更加复杂的结构为调度器和编排工具提供了更加丰富的功能,比如说负载均衡,扩展或者收缩应用的能力。
从开源社区的支持力度看,CNCF协会的成立促使Kubernetes及其生态圈衍生项目逐步完整化,并逐步构建成云原生基础架构蓝图。相较之下Docker Swarm则逐步向封闭靠拢,Docker后续改名为Moby更是导致开源社区嘘声一片。
从容器云调度平台市场占有率来看,Kubernetes始终时排名第一。
ri9yu60oa5bn
因此,我司最终选择Kubernetes作为容器云平台调度方案。

1.3 项目目标

1.3.1 当前痛点

传统行业的第一个痛点就是“剪不断、理不清的业务流程”。由于传统企业更注重线下业务和流程,其业务模式与互联网企业有较大差异。以我司为例,下图描述了从客户购车前至二手车卖家出售车整个客户生命周期中整车厂对客户的触点。而每个客户触点均对应了一个完整的业务流程。因此如何将这些业务流程解耦变为系统?这就是第一个痛点。
epv1191j9tp4
传统行业的第二个痛点在于架构。由于业务模式更恒定,导致整个架构更加偏向于传统“稳态”。即业务可预测,集中式,迭代交付周期较长的巨石架构。此类架构的系统作为一个黑盒子,通过测试等手段保证对外可用,但其内部对外完全不可知,这样也无疑增加了企业潜在的不可控风险。同时,由于巨石架构的状态性使得应用横向扩展较难实现,一旦出现黑天鹅样的系统压力波峰,系统将难以提供服务。而相比之下互联网由于业务不可预测,更倾向于使用敏态去中心化的架构。
mxhd8b7aoxma
以上两点,进一步引发了传统行业的第三个痛点:交付模式和交付效率。由于“剪不断”的业务和没人敢打开的“巨石架构”黑盒子,导致传统行业更倾向于手工控制整个交付过程。一般新项目上线,需要进行8小时的环境初始化及准备工作。项目部署过程通过手工方式进行代码提交、打包、发布等环节,一旦出现问题则需要全流程返工。这样,整个交付效率都由于人为工作而出现一定不确定性,整个交付模式是效率低下的。
dx1ea4kpx7tu

1.3.2 目标规划

针对上文企业痛点分析,我们希望通过容器平台实施需要实现如下目标:

  • 3年容器化80%以上B2C相关服务,满足2C业务峰值需求。由于2C端业务服务无状态化,根据Kubernetes本身特点,可通过HPA(Horizontal Pod Autoscaler)实现弹性伸缩。
  • 3年容器化30%以上DMS相关系统及服务,满足DMS复杂性交付。由于DMS相关应用正处在二代系统重构过程中,故可以通过Docker的黑盒交付模式标准化应用交付,配合CI/CD工具标准化整个交付过程。
  • 改善系统交付发布周期,降低50%以上发布时间。传统的应用手工部署需要较长的软硬件准备时间、应用配置时间和应用部署时间。通过自动化CI/CD工具的整合,配合容器标准化环境,可加快整个发布时间。
  • 减缓IT运维复杂度,降低50%以上MTTR(重启)。通过Kubernetes Readiness Probe/Liveness Probe等容器监控方案的应用,可自动实现容器异常及重启。配合Docker Images管理,可快速进行容器实例生命周期管理。
  • 提升基层架构资源利用率,CPU利用率相比原先非容器化提升1倍。基于Bare Metal运行容器平台,提升基础资源利用率。

因原文较长,故将文章拆分成三篇发布,此篇为第一部分,后两部分将于下两周发布。

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

6
{}

添加新评论1 条评论

#wuwenpin软件开发工程师, 南京
4天前
好资料,感谢
Ctrl+Enter 发表

本文隶属于专栏

最佳实践
不同的领域,都有先行者,实践者,用他们的最佳实践来加速更多企业的建设项目落地。
167 文章

作者其他文章

相关文章

关于TWT  使用指南  社区专家合作  厂商入驻社区  企业招聘  投诉建议  版权与免责声明  联系我们
© 2018  talkwithtrend — talk with trend,talk with technologist 京ICP备09031017号-30