微服务容器化的分工与合作,促进DevOps (一)

 
前言: 开发为主攻,运维为辅助的方式来实行DevOps ,会更加的顺利。
本人为运维,可能对开发的一些概念理解的不是非常透彻。标红的地方,比较认同的方式和理念。
这部分运维理解的可能透彻些。
 
容器的技术原理
关键技术一:Dockerfile
 
例如下面的Dockerfile。
 
 

 

为什么一定要用Dockerfile,而不建议通过保存镜像的方式来生成镜像呢?
 
这样才能实现环境配置和环境部署代码化 ,将Dockerfile维护在Git里面,有版本控制,并且通过自动化的build的过程来生成镜像,而镜像中就是环境的配置和环境的部署,要修改环境应先通过Git上面修改Dockerfile的方式进行,这就是IaC。
 
IaC: 基础设施代码化,是一种使持续交付和DevOps成为可能的基础设施管理方法
 
关键技术二:容器镜像
 
通过Dockerfile可以生成容器镜像,容器的镜像是分层保存,对于Dockerfile中的每一个语句,生成一层容器镜像,如此叠加,每一层都有UUID。
 
容器镜像可以打一个版本号,放入统一的镜像仓库。
 

 

 

 
 
关键技术三:容器运行时
 

 

 

 
容器运行时,是将容器镜像之上加一层可写入层,为容器运行时所看到的文件系统。
 
容器运行时使用了两种隔离的技术。
 
一种是看起来是隔离的技术,称为namespace,也即每个namespace中的应用看到的是不同的IP地址、用户空间、程号等。
 
namespace:可以理解为不同区域间的隔离
 

 

 

 
另一种是用起来是隔离的技术,称为cgroup,也即明明整台机器有很多的CPU、内存,而一个应用只能用其中的一部分。
 
cgroup:限制容器运行时占用的CPU和内存
 

 

 

 
 
 
容器化的本质和容器化最佳实践
 
很多人会将容器当成虚拟机来用,这是非常不正确的,而且容器所做的事情虚拟机都能做到。
 
如果部署的是一个传统的应用,这个应用启动速度慢,进程数量少,基本不更新,那么虚拟机完全能够满足需求。
 
  • 应用启动慢:应用启动15分钟,容器本身秒级,虚拟机很多平台能优化到十几秒,两者几乎看不出差别
  • 内存占用大:动不动32G,64G内存,一台机器跑不了几个。
  • 基本不更新:半年更新一次,虚拟机镜像照样能够升级和回滚
  • 应用有状态:停机会丢数据,如果不知道丢了啥,就算秒级启动有啥用,照样恢复不了,而且还有可能因为丢数据,在没有修复的情况下,盲目重启带来数据混乱。
  • 进程数量少:两三个进程相互配置一下,不用服务发现,配置不麻烦
 
如果是一个传统应用,根本没有必要花费精去容器化,因为白花了力气,享受不到好处。
 
这幅图太到位了,对于运维小兵,10:1 也不过分

 

 

 
什么情况下,才应该考虑做一些改变呢?
 
传统业务突然被互联网业务冲击了,应用老是变,三天两头要更新,而且流量增大了,原来支付系统是取钱刷卡的,现在要互联网支付了,流量扩大了N倍。
 
没办法,一个字:拆
 
拆开了,每个子模块独自变化,少相互影响。
拆开了,原来一个进程扛流量,现在多个进程一起扛。
 
所以称为微服务
 
微服务场景下,进程多,更新快,于是出现100个进程,每天一个镜像。
 
容器乐了,每个容器镜像小,没啥问题,虚拟机哭了,因为虚拟机每个镜像太大了。
 
所以微服务场景下,可以开始考虑用容器了。
 
 

 

 

 
 
 
虚拟机怒了,老子不用容器了,微服务拆分之后,用Ansible自动部署是一样的。
 
这样说从技术角度来讲没有任何问题。
 
然而问题是从组织角度出现的。
 
一般的公司,开发会比运维多的多,开发写完代码就不用管了,环境的部署完全是运维负责,运维为了自动化,写Ansible脚本来解决问题。
 
然而这么多进程,又拆又合并的,更新这么,配置总是变,Ansible脚本也要常,每天都上线,不得累死运维。
 
所以这如此大的工作量情况下,运维很容易出错,哪怕通过自动化脚本。
这个时候,容器就可以作为一个非常好的工具运用起来。
 
除了容器从技术角度,能够使得大部分的内部配置可以放在镜像里面之外,更重要的是从流程角度,将环境配置这件事情,往前推了,推到了开发这里,要求开发完毕之后,就需要考虑环境部署的问题,而不能当甩手掌柜
 
这样做的好处就是,虽然进程多,配置变化多,更新频繁,但是对于某个模块的开发团队来讲,这个量是很小的,因为5-10个人专门维护这个模块的配置和更新,不容易出错。
 
如果这些工作量全交给少数的运维团队,不但信息传递会使得环境配置不一致,部署量会大,非常多。
 
容器是一个非常好的工具,就是让每个开发仅仅多做5%的工作,就能够节约运维200%的工作,并且不容易出错。
 
然而本来原来运维该做的事情开发做了,开发的老大愿意么?开发的老大会投诉运维的老大么?
 
这就不是技术问题了,其实这就是DevOps,DevOps不是不区分开发和运维,而是公司从组织到流程,能够打通,看如何合作,边界如何划分,对系统的稳定性更有好处。
 
所以微服务,DevOps,容器是相辅相成,不可分割的。
 
不是微服务,根本不需要容器,虚拟机就能搞定,不需要DevOps,一年部署一次,开发和运维沟通再慢都能搞定。
 
所以,容器的本质是基于镜像的跨环境迁移。(这只是其中之一,可以所处的环境不同)
 
镜像是容器的根本性发明,是封装和运行的标准,其他什么namespace,cgroup,早就有了。这是技术方面。
 
在流程方面,镜像是DevOps的良好工具。
 
容器是为了跨环境迁移的,第一种迁移的场景是开发,测试,生产环境之间的迁移。如果不需要迁移,或者迁移不频繁,虚拟机镜像也行,但是总是要迁移,带着几百G的虚拟机镜像,太大了。
 
第二种迁移的场景是跨云迁移,跨公有云,跨Region,跨两个OpenStack的虚拟机迁移都是非常麻烦,甚至不可能的,因为公有云不提供虚拟机镜像的下载和上传功能,而且虚拟机镜像太大了,一传传一天。
 
 

 

 

 
 
所以如图为将容器融入持续集成的过程中,形成DevOps的流程。
 
通过这一章,再加上第一章微服务化的基石——持续集成就构成了微服务,DevOps,容器化三位一体的统一。
 

 

 

 
Dockerfile 分工构建
对于容器镜像,我们应该充分利用容器镜像分层的优势,将容器镜像分层构建,在最里面的OS和系统工具层,由运维来构建,中间层的JDK和运行环境,由核心开发人员构建,而最外层的Dockerfile就会非常简单,只要将jar或者war放到指定位置就可以了。
 
这样可以降低Dockerfile和容器化的门槛,促进DevOps的进度。
 
容器平台的最佳实践
 
容器化好了,应该交给容器平台进行管理,从而实现对于容器的自动化管理和编排。
 
容器平台:Docker Swarm,Kubernetes
 
例如一个应用包含四个服务A,B,C,D,她们相互引用,相互依赖,如果使用了容器平台,则服务之间的服务发现就可以通过服务名进行了。例如A服务调用B服务,不需要知道B服务的IP地址,只需要在配置文件里面写入B服务服务名就可以了。如果中间的节点宕机了,容器平台会自动将上面的服务在另外的机器上启动起来。容器启动之后,容器的IP地址就变了,但是不用担心,容器平台会自动将服务名B和新的IP地址映射好,A服务并无感知。这个过程叫做自修复和自发现。如果服务B遭遇了性能瓶颈,三个B服务才能支撑一个A服务,也不需要特殊配置,只需要将服务B的数量设置为3,A还是只需要访问服务B,容器平台会自动选择其中一个进行访问,这个过程称为弹性扩展和负载均衡。
 
 
Docker Swarm Mode
当容器平台规模不是很大的时候,Docker Swarm Mode还是比较好用的:
  • 集群的维护不需要Zookeeper,不需要Etcd,自己内置
  • 命令行和Docker一样的,用起来顺手
  • 服务发现和DNS是内置的
  • Docker Overlay网络是内置的
 
总之Docker帮你料理好了一切,你不用太关心细节,很容易就能够将集群运行起来。
 
而且可以通过docker命令,像在一台机器上使用容器一样使用集群上的容器,可以随时将容器当虚拟机来使用,这样对于中等规模集群,以及运维人员还是比较友好的。
 
当然内置的太多了也有缺点,就是不好定制化,不好Debug,不好干预。当你发现有一部分性能不行的时候,你需要改整个代码,全部重新编译,当社区更新了,合并分支是很头疼的事情。当出现了问题的时候,由于Manager大包大揽干了很多活,不知道哪一步出错了,反正就是没有返回,停在那里,如果重启整个Manager,影响面又很大。
 
 
 
Kubernetes
当规模比较大,应用比较复杂的时候,则推荐Kubernetes。
 
Kubernetes模块划分得更细,模块比较多,而且模块之间完全的松耦合,可以非常方便地进行定制化。
 

 

 

 
 
而且Kubernetes的数据结构的设计层次比较细,非常符合微服务的设计思想。例如从容器->Pods->Deployment->Service,本来简单运行一个容器,被封装为这么多的层次,每次层有自己的作用,每一层都可以拆分和组合,这样带来一个很大的缺点,就是学习门槛高,为了简单运行一个容器,需要先学习一大堆的概念和编排规则
 
这不是学习门槛高的问题,这是向新领域,新技术的开拓和进取。
 
但是当需要部署的业务越来越复杂时,场景越来越多时,你会发现Kubernetes这种细粒度设计的优雅,使得你能够根据自己的需要灵活的组合,而不会因为某个组件被封装好了,从而导致很难定制。例如对于Service来讲,除了提供内部服务之间的发现和相互访问外,还灵活设计了headless service,这使得很多游戏需要有状态的保持长连接有了很好的方式,另外访问外部服务时,例如数据库、缓存、headless service相当于一个DNS,使得配置外部服务简单很多。很多配置复杂的大型应用,更复杂的不在于服务之间的相互配置,可以有Spring Cloud或者Dubbo去解决,复杂的反而是外部服务的配置,不同的环境依赖不同的外部应用,External Name这个提供和很好的机制。
 
包括统一的监控cadvisor,统一的配置confgMap,都是构建一个微服务所必须的。
 
然而Kubernetes当前也有一个瓶颈——集群规模还不是多么大,官方说法是几千个节点,所以超大规模的集群,还是需要有很强的IT能力进行定制化。但是对于中等规模的集群也足够了。
 
而且Kubernetes社区的热度,可以使得使用开源Kubernetes的公司能够很快地找到帮助,等待到新功能的开发和Bug的解决。
 
 

《互联网高并发微服务化架构实践》系列课程的第四篇

前三篇为:

微服务化的基石——持续集成

微服务的接入层设计与动静资源隔离

微服务化的数据库设计与读写分离

posted @ 2020-05-16 11:17  carl007  阅读(574)  评论(0编辑  收藏  举报