业务容器化后,运维面对的不再是一台台实实在在的物理机或者虚拟机,而是一个个 Docker 容器,它们可能都没有固定的 IP,要想发布服务,需要用一个面向容器的新型运维平台。一个容器运维平台通常包含以下几个组成部分:镜像仓库、资源调度、容器调度、调度策略、服务编排。
1、镜像仓库
-
权限控制
镜像仓库都设有两层权限控制:一是必须登录才可以访问,这是最外层的控制,它规定了哪些人可以访问镜像仓库;二是对镜像按照项目的方式进行划分,每个项目拥有自己的镜像仓库目录,并且给每个项目设置项目管理员、开发者及客人 3 个角色,只有项目管理员和开发者拥有自己镜像仓库目录下镜像的修改权限,而客人只拥有访问权限,项目管理员可以为这个项目设置哪些人是开发者。
-
镜像同步
在实际的生产环境中,往往需要把镜像同时发布到几十台或者上百台集群节点上,单个镜像仓库实例往往受带宽限制无法同时满足大量节点的下载需求,此时就需要配置多个镜像仓库实例来做负载均衡,同时也会产生镜像在多个镜像仓库实例之间同步的问题。一般来说,有两种解决方案,一种是一主多从,主从复制的方案,比如开源镜像仓库 Harbor 采用了这种方案;另一种是 P2P 的方案,如阿里的容器镜像分发系统蜻蜓就采用了 P2P 方案。
-
高可用性
一般而言,高可用性设计无非就是把服务部署在多个 IDC,这样即使有 IDC 出现问题,也可以把服务迁移到其他正常的 IDC 中去。
2、资源调度
为了解决资源调度的问题,Docker 官方提供了 Docker Machine 功能,通过 Docker Machine 可以在企业内部的物理机集群,或者虚拟机集群(如 OpenStack 集群),又或者公有云集群(如 AWS 集群)等上创建机器并且直接部署容器。Docker Machine 的功能虽然很好,但是对于大部分已经发展了一段时间的业务团队来说,并不能直接拿来使用。
-
物理机集群
大部分中小团队应该都拥有自己的物理机集群,并且大多按照集群—服务池—服务器这种模式进行运维。
-
虚拟机集群
很多业务团队在使用物理机集群后,发现物理机集群存在使用率不高、业务迁移不灵活的问题,因此纷纷转向了虚拟化方向,构建自己的私有云。
-
公有云集群
现在越来越多的业务团队,尤其是初创公司,因为公有云快速灵活的特性,纷纷在公有云上搭建自己的业务。公有云最大的好处除了快速灵活、分钟级即可实现上百台机器的创建,还有一个优点就是配置统一、便于管理,不存在机器配置碎片化问题。
3、容器调度
容器调度是指,假如现在集群里有一批可用的物理机或者虚拟机,当服务需要发布时,该选择哪些机器部署容器。
比如集群里只有 10 台机器,并且已经有 5 台机器运行着其他容器,剩余 5 台机器空闲着,如果此时有一个服务要发布,但只需要 3 台机器即可。这时可以靠运维人为地从 5 台空闲的机器中选取 3 台机器,然后把服务的 Docker 镜像下载下来,再启动 Docker 容器服务即可完成发布。
这时如果集群中有上百台机器,就需要有专门的容器调度系统,为此也诞生了不少基于 Docker 的容器调度系统,最后还是 Kubernetes 一统江湖了。
4、调度策略
调度策略主要是为了解决容器创建时选择哪些主机最合适的问题,一般都是通过给主机打分来实现的。具体选择哪种调度策略,还要综合实际的业务场景,通常的场景有以下几种。
-
各主机的配置基本相同,并且使用也比较简单,一台主机上只创建一个容器。这样的话,每次创建容器时,直接从还没有创建过容器的主机中随机选择一台即可。
-
在某些在线、离线业务混布的场景下,为了达到主机资源使用率最高的目标,需要综合考量容器中跑的任务的特点,比如在线业务主要使用 CPU 资源,而离线业务主要使用磁盘和 I/O 资源,这两种业务的容器大部分情况下适合混跑在一起。
-
还有一种业务场景,主机上的资源都是充足的,每个容器只要划定了所用的资源限制,理论上跑在一起是没有问题的,但是有些时候会出现对某个资源的抢占,比如都是 CPU 密集型或者 I/O 密集型的业务,就不适合容器混跑在一台主机上。
5、服务编排
-
服务依赖
大部分情况下,微服务之间是相互独立的,在进行容器调度时不需要考虑彼此。但有时也会存在一些场景,比如服务 A 调度的前提必须是先有服务 B,这就要求在进行容器调度时,还需要考虑服务之间的依赖关系。
Docker 官方提供了 Docker Compose 的解决方案,它允许用户通过一个单独的 docker-compose.yaml 文件来定义一组相互关联的容器组成一个项目,从而以项目的形式来管理应用。比如要实现一个 Web 项目,不仅要创建 Web 容器(如 Tomcat 容器),还需要创建数据库容器(如 MySQL 容器)、负载均衡容器(如 Nginx 容器)等,此时就可以通过 docker-compose.yaml 来配置这个 Web 项目里包含的 3 个容器。
-
服务发现
容器调度完成以后,容器就可以启动了,但此时容器还不能对外提供服务,服务消费者并不知道这个新的节点,所以必须具备服务发现机制,使得新的容器节点能够加入到线上服务中去。
基于 Nginx 的服务发现主要是针对提供 HTTP 服务的,当有新的容器节点时,修改 Nginx 的节点列表配置,然后利用 Nginx 的重新加载机制,会重新读取配置,从而把新的节点加载进来。
基于注册中心的服务发现主要是针对提供 RPC 服务的,当有新的容器节点时,需要调用注册中心提供的服务注册接口。在使用这种方式时,如果服务部署在多个 IDC,就要求容器节点分 IDC 进行注册,以便实现同 IDC 内就近访问。
-
自动扩缩容
容器完成调度后,仅仅做到有容器不可用时故障自愈还不够,有时还需要根据实际服务的运行状况,做到自动扩缩容。
一个很常见的场景就是,大部分互联网业务的访问呈现出访问时间的规律性。以微博业务为例,白天和晚上的使用人数远远大于凌晨的使用人数;而白天和晚上的使用人数也不是平均分布的,午高峰 12 点半和晚高峰 10 点半是使用人数最多的时刻。这时就需要根据实际使用需求,在午高峰和晚高峰时刻,增加容器的数量,确保服务的稳定性;在凌晨以后减少容器的数量,减少服务使用的资源成本。