在聊容器之前,我们先聊聊以前的服务部署。我们部署服务都是直接部署在硬件服务器上,扩容就需要购买服务器,然后进行应用部署,以及各种环境和服务配置。这些流程相当繁琐,并且都是人工操作,不仅浪费时间还很费程序“猿”,因此服务部署以及迁移效率都极其低下。
在早期,用户数以及业务体量还不是很大,人工操作还能够应付得过来。而随着业务规模的不断发展、用户数的爆炸式增长,这样的软件服务生产方式已经无法满足业务高速发展的需求。它主要有以下三个问题:
服务互干扰
一台服务器一般不会只部署一个服务应用,大多部署了多个服务应用。但是由于这些服务都是公用服务器中的 CPU、内存、硬盘以及网络 IO 等服务器资源,必定会存在资源互相争用、服务互相影响的情况。
资源利用低
业务是存在高峰期和低谷期的。就电商平台来说,一般深夜属于业务低谷期,服务器的资源利用率相比业务高峰期的时候要低很多。因此在业务低谷期,实际服务器的资源利用率比较低,不能物尽其用。
迁移扩展难
原有的服务器数量不足以应对高速发展的业务时,就需要不断的进行服务器实例扩充,但是由于服务直接部署在服务器中,在进行服务迁移扩展的时候,需要各种依赖库、环境配置以及网络配置等,步骤复杂,扩展困难。
什么是容器
容器(Container)是一种更轻量级,更灵活的虚拟化处理方式,它将一个应用程序所需的一切打包在一起。容器包括所有代码,各种依赖甚至操作系统,这让应用程序几乎在任何地方都可以运行。因此它的诞生,解决了一个重要问题:如何确保应用程序从一个环境移动到另一个环境的正确运行。它只是虚拟了操作系统,而不像虚拟机一样去虚拟底层计算机。下面我们来详细了解下容器技术的原理。
隔离与共享
在一个多员共用的开发环境或者一台服务器运行多个逻辑隔离的服务器进程。谁的运行环境也不希望影响到另一个谁。也就是一个物理机器需要虚拟化出多个环境或者容器。通过提供一种创建和进入容器的方式,操作系统让应用程序就像在独立的机器上运行一样,但又能共享很多底层的资源。
虚拟化 VS 容器
传统的虚拟化技术是通过硬件模拟或者操作系统软件实现,而容器技术可以比传统虚拟化更轻量级。
容器在提供隔离的同时,还通过共享这些资源节省开销,这意味着容器比真正的虚拟化的开销要小得多。例如,可以有效地共享公共文件(比如 glibc)的页缓存,因为所有容器都使用相同的内核,而且所有容器还常常共享相同的 libc 库(取决于容器配置)。这种共享常常可以扩展到目录中其他不需要写入内容的文件。
和传统虚拟化相比,容器启动很快。由于共享系统资源,一台主机可以运行上千个容器,并且容器镜像用类似 git 分发思想,用户更容易创建,分发,更新存储这些镜像。
容器的前世今生
容器的今生是 docker 大流行的时代,而它的前世是早于 1982 年的 chroot 工具,以及后面经过改进并且现在还在使用的 lxc 技术。早期 docker 的代码实现基于 LXC(0.9之前)。
Linux 容器功能是基于 cgroups 和 Namespace 来实现的,所以要了解 Linux 容器必须先了解 cgroup 和 Namespace。
Namespace
Namespace 是 Linux 用来隔离内核资源的方式。通过 namespace 可以让一些进程只看到与自己相关的资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程感觉不到对方的存在。具体的实现方式是把一个或多个进程的相关资源指定在同一个 namespace 中。
Linux namespaces 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前 namespace 里的进程,对其他 namespace 中的进程没有影响。
可能绝大多数的使用者和我一样,是在使用 docker 后才开始了解 linux 的 namespace 技术的。Linux 内核实现 namespace 的其中一个目的,就是为了实现轻量级虚拟化(容器)服务。在同一个 namespace 下的进程可以感知彼此变化,而对外界进程一无所知。这样就可以让容器中的进程产生错觉,认为自己置身于一个独立系统中,从而达到隔离的目的。可以说,Linux 内核提供的 namespace 技术为 docker 等容器技术的出现和发展提供了基础条件。
如何从 docker 实现者的角度出发,去实现一个资源隔离的容器呢?
- IPC:隔离 System V IPC 和 POSIX 消息队列
- Network:隔离网络资源。
- Mount:隔离文件系统挂载点。
- PID:隔离进程ID。
- UTS:隔离主机名和域名。
- User:隔离用户ID和组ID。
以上这六种隔离能力是实现一个容器的基础,而 linux 内核的 namespace 特性也为我们提供了 IPC、Network、Mount、PID、User、UTS、Cgroup 等隔离能力。下面重点来聊聊 Cgroups。
Cgroups
Cgroups(Control Groups) 是 linux 内核提供的一种机制,这种机制可以根据需求,把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。简单来说,cgroups 可以限制、记录任务组所使用的物理资源。本质上来说,cgroups 是内核附加在程序上的一系列钩子(hook),通过程序运行时对资源的调度,触发相应的钩子以达到资源追踪和限制的目的。
为什么要使用 Cgroups?
其可以做到对 cpu、内存等资源实现精细化的控制,目前越来越火的轻量级容器 Docker 及 k8s 中的 pod 就使用了 cgroups 提供的资源限制能力来完成 cpu、内存等部分的资源控制。
举个例子,在一个既部署了前端 web 服务,也部署了后端计算模块的八核服务器上,可以使用 cgroups 限制 web server 仅可以使用其中的六个核,把剩下的两个核留给后端计算模块。
Cgroups 的主要作用
实现 cgroups 的主要目的,是为不同用户层面的资源管理提供一个统一化的接口。从单个任务的资源控制到操作系统层面的虚拟化,cgroups 提供了四大功能:
- 资源限制:cgroups 可以对任务的资源总额进行限制。比如设定任务运行时使用的内存上限,一旦超出就发 OOM。
- 优先级分配:通过分配的 CPU 时间片数量和磁盘 IO 带宽,实际上就等同于控制了任务运行的优先级。
- 资源统计:cgoups 可以统计系统的资源使用量,比如 CPU 使用时长、内存用量等。这个功能非常适合当前云端产品按使用量计费的方式。
- 任务控制:cgroups 可以对任务执行挂起、恢复等操作。
Cgroups相关概念及其关系
相关概念
- 任务(task):在cgroups中,任务就是系统的一个进程。
- 控制族群(control group):控制族群就是一组按照某种标准划分的进程。Cgroups中的资源控制都是以控制族群为单位实现。一个进程可以加入到某个控制族群,也从一个进程组迁移到另一个控制族群。一个进程组的进程可以使用 cgroups 以控制族群为单位分配的资源,同时受到 cgroups 以控制族群为单位设定的限制。
- 层级(hierarchy):控制族群可以组织成 hierarchical 的形式,既一颗控制族群树。控制族群树上的子节点控制族群是父节点控制族群的孩子,继承父控制族群的特定的属性。
- 子系统(subsytem):一个子系统就是一个资源控制器,比如 cpu 子系统就是控制 cpu 时间分配的一个控制器。子系统必须附加(attach)到一个层级上才能起作用,一个子系统附加到某个层级以后,这个层级上的所有控制族群都受到这个子系统的控制。
相互关系
- 每次在系统中创建新层级时,该系统中的所有任务都是那个层级的默认 cgroup(我们称之为 root cgroup,此 cgroup 在创建层级时自动创建,后面在该层级中创建的cgroup都是此cgroup的后代)的初始成员。
- 一个子系统最多只能附加到一个层级。
- 一个层级可以附加多个子系统
- 一个任务可以是多个 cgroup 的成员,但是这些 cgroup 必须在不同的层级。
- 系统中的进程(任务)创建子进程(任务)时,该子任务自动成为其父进程所在 cgroup 的成员。然后可根据需要将该子任务移动到不同的 cgroup 中,但开始时它总是继承其父任务的 cgroup。
容器的应用场景
容器技术的诞生其实主要解决了 PAAS 层的技术实现,像 OpenStack、Cloudstack 这样的技术是解决 IAAS 层的问题。那么容器技术主要应用在哪些场景呢?目前主流的有以下几种:
- 容器化传统应用
容器不仅能提高现有应用的安全性和可移植性,还能节约成本。每个企业的环境中都有一套较旧的应用来服务于客户或自动执行业务流程。即使是大规模的单体应用,通过容器隔离的增强安全性、以及可移植性特点,也能从 Docker 中获益,从而降低成本。一旦容器化之后,这些应用可以扩展额外的服务或者转变到微服务架构之上。
- 持续集成和持续部署 (CI/CD)
通过 Docker 加速应用管道自动化和应用部署,交付速度提高至少 13 倍。现代化开发流程快速、持续且具备自动执行能力,最终目标是开发出更加可靠的软件。通过持续集成 (CI) 和持续部署 (CD),每次开发人员签入代码并顺利测试之后,IT 团队都能够集成新代码。作为开发运维方法的基础,CI/CD 创造了一种实时反馈回路机制,持续地传输小型迭代更改,从而加速更改,提高质量。CI 环境通常是完全自动化的,通过 git 推送命令触发测试,测试成功时自动构建新镜像,然后推送到 Docker 镜像库。通过后续的自动化和脚本,可以将新镜像的容器部署到预演环境,从而进行进一步测试。
- 微服务
应用架构正在从采用瀑布模型开发法的单体代码库转变为独立开发和部署的松耦合服务。成千上万个这样的服务相互连接就形成了应用。Docker 允许开发人员选择最适合于每种服务的工具或技术栈,隔离服务以消除任何潜在的冲突,从而避免“地狱式的矩阵依赖”。这些容器可以独立于应用的其他服务组件,轻松地共享、部署、更新和瞬间扩展。Docker 的端到端安全功能让团队能够构建和运行最低权限的微服务模型,服务所需的资源(其他应用、涉密信息、计算资源等)会适时被创建并被访问。
- IT 基础设施优化
Docker 和容器有助于优化 IT 基础设施的利用率和成本。优化不仅仅是指削减成本,还能确保在适当的时间有效地使用适当的资源。容器是一种轻量级的打包和隔离应用工作负载的方法,所以 Docker 允许在同一物理或虚拟服务器上毫不冲突地运行多项工作负载。企业可以整合数据中心,将并购而来的IT资源进行整合,从而获得向云端的可迁移性,同时减少操作系统和服务器的维护工作。
作为云原生发展的基石,容器技术的新趋势和新挑战备受关注,以容器为代表的云原生技术正在成为释放云价值的最短路径。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。