计算机科学是一个不断以非常快的速度发展的领域,要求该行业的每个人都跟上最新的技术进步。在最近的历史中,该行业欢迎新技术,例如分布式计算、并行处理、虚拟化、云计算,以及最近的物联网 (IoT)。每项技术都为下一项技术铺平了道路,并有助于为其他技术奠定坚实的基础。例如,虚拟化彻底改变并构建了云计算的基础。从计算机发明之初,无论是通过分时、多任务处理还是最近的虚拟化趋势,使用资源利用率最高的计算机一直是一种常见的做法。
自 1990 年代初 Linux 内核出现以来,许多操作系统发行版都围绕 Linux 内核发展。然而,直到最近,GNU/Linux 才被具有配置和维护技能的高级用户广泛使用。随着几家 GNU/Linux 供应商引入用户友好界面,情况发生了变化,现在 GNU/Linux 被台式机、笔记本电脑等的消费者用户更广泛地采用。随着基于 Linux 内核的 Android 手机的出现,Linux 的使用在更多的受众中变得无处不在。
容器化是虚拟化的下一个合乎逻辑的步骤,并且围绕这项技术引起了巨大的轰动。容器可以在操作系统级别和应用程序级别提供虚拟化。本书重点介绍容器化技术与 Linux 内核的结合使用。
容器的一些可能性如下:
-
提供完整的沙盒(隔离)操作系统环境
-
允许打包和隔离应用程序及其整个运行时环境
-
提供可移植的轻量级环境
-
帮助最大限度地提高数据中心的资源利用率
-
协助不同的开发、测试和生产部署工作流
容器定义
一个容器可以定义为一个单一的操作系统镜像,捆绑了一组隔离的应用程序及其依赖的资源,以便它们与主机分开运行。可能有多个这样的容器在同一台主机中运行。
容器可以分为两类:
-
操作系统级别 :整个操作系统在主机内的隔离空间中运行,与主机共享相同的内核。
-
应用程序级别 :应用程序或服务以及该应用程序所需的最少进程在主机内的隔离空间中运行。
容器不同于传统意义上的虚拟化技术,并再很多方面优于传统虚拟化 :
-
与传统虚拟机相比,容器是轻量级的。
-
与容器不同,虚拟机需要仿真层(软件或硬件),这会消耗更多资源并增加额外开销。
-
容器与底层主机共享资源,用户空间和进程隔离。
-
由于容器的轻量级特性,更多的容器可以每台主机不是每个虚拟机上运行的主机 。
-
与较慢的虚拟机启动过程相比,启动容器几乎是立即发生的。
-
容器是可移植的,无论底层主机操作系统如何,都可以使用所需的软件包可靠地重新生成系统环境。
图1-1说明了虚拟机、Linux 容器 (LXC) 或操作系统级容器以及应用程序级容器在主机操作系统中的组织方式的差异。
图 1-1。
比较虚拟机、LXC 或操作系统级容器和应用程序级容器
容器历史
开发虚拟化 是为了充分利用可用的计算资源。虚拟化使多个虚拟机能够在单个主机上运行,用于具有自己隔离空间的不同目的。虚拟化使用虚拟机管理程序、位于主机操作系统和来宾或虚拟机操作系统之间的计算机软件实现了这种隔离的操作系统环境。正如介绍中提到的,容器化是虚拟化的下一个逻辑步骤,在操作系统级别和应用程序级别提供虚拟化。
容器技术已经以不同的形式存在了很长时间,但最近随着 Linux 内核中原生容器化支持的引入,它在 Linux 世界中得到了显着的普及。表1-1列出了一些早期的相关技术,它们使我们达到了当前的技术状态。
表 1-1。
容器技术时间表
年
|
技术
|
首次在操作系统中引入
|
---|---|---|
1982年
| Chroot |
类 Unix 操作系统
|
2000年
| Jail |
FreeBSD
|
2000年
|
Virtuozzo 容器
|
Linux、Windows(Parallels Inc. 版本)
|
2001年
| Linux VServer |
Linux, 视窗
|
2004年
|
Solaris 容器(zones
)
|
Sun Solaris,Open
Solaris
|
2005年
|
OpenVZ
|
Linux(开源版 Virtuozzo)
|
2008年
|
LXC
|
Linux
|
2013年
|
Docker
|
Linux、FreeBSD、Windows
|
笔记
表1-1 中涵盖的某些技术可能在比所列操作系统更多的操作系统中得到支持。大多数技术可用于各种形式的 Unix 操作系统,包括 Linux。
表1-1 中列出的一些容器技术具有非常具体的用途,例如 chroot,它通过切换运行进程及其子进程的根目录来提供文件系统隔离。列出的其他技术提供完整的操作系统级虚拟化,例如 Solaris 容器(区域)和 LXC。
常见的现代容器源自于 2008 年首次引入的 LXC。由于从 2.6.24 版本开始向 Linux 内核添加了一些关键特性,因此 LXC 成为可能,如下一节所述。
启用容器的功能
容器依靠 Linux 内核中的以下功能在主机内获得一个包含或隔离的区域。该区域与虚拟机密切相关,但不需要管理程序。
-
控制组 (cgroups)
-
命名空间
-
文件系统或 rootfs
控制组 (Cgroups)
要理解 cgroups 的重要性,请考虑一个常见的场景:运行在系统上的进程在特定实例向系统请求某些资源,但不幸的是当前资源不可用,因此系统决定推迟进程,直到请求的资源满足可用的。当其他进程释放它们时,请求的资源可能变得可用。这会延迟流程执行,这在许多应用程序中可能是不可接受的。当恶意进程消耗了系统上的全部或大部分可用资源并且不允许其他进程执行时,就会发生这种资源不可用。
Google 在 2007 年的 cgroups 项目中提出了一种新的通用方法来解决资源控制问题。控制组允许基于进程组来控制和计算资源。主线 Linux 内核在 2008 年首次包含 cgroups 实现,这为 LXC 铺平了道路。
Cgroups 提供了一种将任务或进程集及其未来子项聚合到分层组中的机制。这些组可以根据需要配置为具有专门的行为。
列出 Cgroup
Cgroup列 在目录/sys/fs/cgroup的伪文件系统子系统 中,它提供了当前运行系统中可用或挂载的所有 cgroup 子系统的概览:
stylesen@harshu:∼$ ls -alh /sys/fs/cgroup
total 0
drwxr-xr-x 12 root root 320 Mar 24 20:40 .
drwxr-xr-x 8 root root 0 Mar 24 20:40 ..
dr-xr-xr-x 6 root root 0 Mar 24 20:40 blkio
lrwxrwxrwx 1 root root 11 Mar 24 20:40 cpu -> cpu,cpuacct
lrwxrwxrwx 1 root root 11 Mar 24 20:40 cpuacct -> cpu,cpuacct
dr-xr-xr-x 6 root root 0 Mar 24 20:40 cpu,cpuacct
dr-xr-xr-x 3 root root 0 Mar 24 20:40 cpuset
dr-xr-xr-x 6 root root 0 Mar 24 20:40 devices
dr-xr-xr-x 4 root root 0 Mar 24 20:40 freezer
dr-xr-xr-x 7 root root 0 Mar 24 20:40 memory
lrwxrwxrwx 1 root root 16 Mar 24 20:40 net_cls -> net_cls,net_prio
dr-xr-xr-x 3 root root 0 Mar 24 20:40 net_cls,net_prio
lrwxrwxrwx 1 root root 16 Mar 24 20:40 net_prio -> net_cls,net_prio
dr-xr-xr-x 3 root root 0 Mar 24 20:40 perf_event
dr-xr-xr-x 6 root root 0 Mar 24 20:40 pids
dr-xr-xr-x 7 root root 0 Mar 24 20:40 systemd
内存子系统层次结构
我们来看一个 cgroup 的内存子系统层次结构的例子。它在以下位置可用:
/sys/fs/cgroup/内存
内存子系统层次结构由以下文件组成:
root@harshu:/sys/fs/cgroup/memory# ls
cgroup.clone_children memory.memsw.failcnt
cgroup.event_control memory.memsw.limit_in_bytes
cgroup.procs memory.memsw.max_usage_in_bytes
cgroup.sane_behavior memory.memsw.usage_in_bytes
init.scope memory.move_charge_at_immigrate
lxc memory.numa_stat
memory.failcnt memory.oom_control
memory.force_empty memory.pressure_level
memory.kmem.failcnt memory.soft_limit_in_bytes
memory.kmem.limit_in_bytes memory.stat
memory.kmem.max_usage_in_bytes memory.swappiness
memory.kmem.slabinfo memory.usage_in_bytes
memory.kmem.tcp.failcnt memory.use_hierarchy
memory.kmem.tcp.limit_in_bytes notify_on_release
memory.kmem.tcp.max_usage_in_bytes release_agent
memory.kmem.tcp.usage_in_bytes system.slice
memory.kmem.usage_in_bytes tasks
memory.limit_in_bytes user
memory.max_usage_in_bytes user.slice
root@harshu:/sys/fs/cgroup/memory#
列出的每个文件都 包含有关为其创建的控制组的信息。例如,以字节为单位的最大内存使用量由以下命令给出(由于这是顶级层次结构,它列出了当前主机系统的默认设置):
root@harshu:/sys/fs/cgroup/memory# cat memory.max_usage_in_bytes
15973715968
前面的值以字节为单位;它对应于大约 14.8GB 的内存,可供当前运行的系统使用。您可以在/sys/fs/cgroup 中创建自己的cgroup并控制每个子系统。
命名空间
在 2006 年举行的渥太华 Linux 研讨会上,Eric W. Bierderman 发表了他的论文“Multiple Instances of the Global Linux Namespaces”(可在https://www.kernel.org/doc/ols/2006/ols2006v1-pages-101- 112.pdf)。这篇论文提出向 Linux 内核添加十个命名空间。他对这些额外命名空间的灵感来自于 2002 年引入的用于挂载的现有文件系统命名空间。提议的命名空间如下:
-
文件系统挂载命名空间 (mnt)
-
UTS 命名空间
-
IPC 命名空间 (ipc)
-
网络命名空间 (net)
-
进程 ID 命名空间 (pid)
-
用户和组 ID 命名空间
-
安全模块和命名空间
-
安全密钥命名空间
-
设备命名空间
-
时间命名空间
命名空间提供了对全局系统资源的抽象,对于定义的命名空间内的进程来说,该资源将作为其自己的特定全局资源的独立实例出现。命名空间用于实现容器;它们提供了容器和主机系统之间所需的隔离。
随着时间的推移,Linux 内核中实现了不同的命名空间。在撰写本文时,Linux 内核中 实现了七个命名空间,列于表1-2 中。
表 1-2。
现有的Linux 命名空间
命名空间
|
持续的
|
隔离
|
---|---|---|
Cgroup |
CLONE_NEWCGROUP
|
C组根目录
|
IPC |
CLONE_NEWIPC
|
System V IPC、POSIX 消息队列
|
Network | CLONE_NEWNET |
网络设备、堆栈、端口等。
|
Mount |
CLONE_NEWNS
|
挂载点
|
PID
|
CLONE_NEWPID
|
进程 ID
|
User |
CLONE_NEWUSER
|
用户和组 ID
|
UTS |
CLONE_NEWUTS
|
主机名和 NIS 域名
|
让我们借助一个使用网络命名空间的简单示例来检查命名空间是如何工作的。
简单网络 命名空间
命名空间是通过将适当的克隆标志传递给clone()系统调用来创建的。有一个网络命名空间的命令行界面,可以用来说明一个简单的网络命名空间,如下:
笔记
创建网络命名空间需要 root 权限。
1、创建一个名为stylesen-net的网络命名空间:
# ip netns add stylesen-net
2、要列出新创建的网络命名空间中存在的所有设备,请发出以下命令。此示例显示默认环回设备。
# ip netns exec stylesen-net ip link list
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN mode DEFAULT group default qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
3、尝试 ping 回环设备:
# ip netns exec stylesen-net ping 127.0.0.1
connect: Network is unreachable
4、
虽然环回设备可用,但尚未启动。启动回送设备并再次尝试 ping:
# ip netns exec stylesen-net ip link set dev lo up
# ip netns exec stylesen-net ping 127.0.0.1PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.045 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.097 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.084 ms
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.095 ms
^C
--- 127.0.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4082ms
rtt min/avg/max/mdev = 0.045/0.076/0.097/0.020 ms
因此,我们可以创建 网络命名空间并向其中添加设备。可以创建任意数量的网络命名空间,然后可以在这些单独命名空间中可用的设备之间设置不同的网络配置。
文件系统或 rootfs
容器所需的下一个组件是磁盘映像,它为容器提供根文件系统 (rootfs)。rootfs 由一组文件组成,其结构类似于安装在任何基于 GNU/Linux 的机器上的根文件系统。rootfs 的大小小于典型的 OS 磁盘映像,因为它不包含内核。容器与主机共享相同的内核。
通过使其仅包含应用程序并将其配置为共享主机的 rootfs,可以进一步减小 rootfs 的大小。使用写时复制 (COW) 技术,可以在多个容器之间共享单个简化的只读磁盘映像。
概括
本章通过容器与使用虚拟机的传统虚拟化技术的比较,向您介绍了容器技术的世界。您还了解了容器技术的简史以及为支持现代容器技术而引入的重要 Linux 内核功能。本章最后概述了支持容器化的三个基本特性(cgroups、命名空间和 rootfs)。