快速理解容器技术的实现原理

简介: 与 Docker 类似的容器技术并不是操作系统与生俱来的能力,而是通过组合一些 Linux 特性,实现**进程组隔离**的一种技术。本篇文章将从介绍容器技术的发展开始,进而说明哪些 Linux 特性组成了容器技术的核心部分。希望您能够借由阅读本篇文章,对 Docker 等容器技术有更深刻的认识。## 1. 为什么我们需要容器容器技术并不是凭空出现的,它来源自时代发展中人们对于如何更高效

与 Docker 类似的容器技术并不是操作系统与生俱来的能力,而是通过组合一些 Linux 特性,实现进程组隔离的一种技术。

本篇文章将从介绍容器技术的发展开始,进而说明哪些 Linux 特性组成了容器技术的核心部分。希望您能够借由阅读本篇文章,对 Docker 等容器技术有更深刻的认识。

1. 为什么我们需要容器

容器技术并不是凭空出现的,它来源自时代发展中人们对于如何更高效地利用计算机资源的思考和工程实践,在本章中,我将遍历容器技术出现之前的各时代,帮助您理解容器技术究竟解决了什么样的问题。

1.1 裸机时代

互联网服务早期,想要架设 Web 服务器,就需要租用某个地方的服务器设备,运行程序代码。只要有充足且称职的人员维护,就能最大限度的发挥服务器性能。

裸机时代的问题在于扩展服务极度缺乏灵活性:如果想要添加设备,就需要找服务器供应商(Dell 或 IBM)购买新的物理设备,并指派专业人员进行安装,调试,启动,这大概需要一两个月的时间。

并且,当部署好一个服务器集群,操作系统与驱动的升级,硬件的替换与维修,网络问题的修复,线材的整理,机房管理权限的设置,数据中心温度的控制以及电费与 IPS 费用的支付...等等这些都需要专业的团队去处理。

1.2 虚拟机时代

于是我们进入了虚拟机时代,虚拟机是介于用户与硬件设备之间的一层抽象。一开始,相较于裸机时代,一台计算机服务于单一的用户主体,现在一台计算机允许多个用户主体登录,使用计算资源运行彼此的服务。只要设备性能充足,用户便可以在需要时快速添加新服务。这使得我们获得了一些服务扩展的灵活性。

但在这种模式下存在着一些问题:

  1. 任何用户都有权限获取其他用户服务存储的数据;
  2. 用户可以通过投放 Fork Bomb(见下方说明) 的方式,掠夺服务器资源;
  3. 一台物理设备上的任何租户都可能无意间使整个服务器崩溃;

为了解决这一问题,出现了虚拟机技术:即当用户创建服务时,在计算机的主操作系统上安装新的操作系统调度硬件资源以达成数据隔离的目标。并且当一个服务崩溃时,最多导致服务所属的操作系统崩溃,服务器设备上的其余租户将不受影响。

虚拟机技术的弊端在于在主操作系统中运行其他操作系统所带来的性能损耗。但只要计算机拥有充足的算力和内存,这些性能损耗都可以被接受。

Fork Bomb 是一种通过不断生成子进程,以达到占用大量系统资源的目的,从而导致系统无法正常工作,甚至停止响应的攻击手段。它通常通过在操作系统中创建大量进程,以消耗系统内存和处理器资源,并导致系统崩溃。

1.3 公有云时代

通过 Microsoft Azure,Amazon Web Services 或阿里云等公有云服务提供商提供的虚拟机服务,用户不再需要管理昂贵且复杂的数据中心,只需要管理自己的应用程序。云服务厂商虽然不会帮助用户更新操作系统,但是会定期更新服务器设备。

但在这种模式下,虚拟机提供商向用户提供的,本质上仍然是计算机的硬件设备(CPU 和内存),用户仍然需要支付调度,维护整个操作系统的开销(例如网络管理,安装与更新软件等),这又需要专业的技术人员负责。

如果能够帮助用户节省掉维护操作系统的开销,让应用程序直接运行,那就太棒了。这种需求催生了下个时代的到来。

1.4 容器时代

容器技术为用户提供了许多虚拟机安全和资源管理的功能,但节省掉了运行整个操作系统的成本。它通过以下三个 Linux 命令成功将进程组之间彼此隔离:

  • chroot:实现目录级别的资源隔离;
  • unshare:实现进程级别的资源隔离;
  • cgroup:限制隔离环境中可调度的资源大小;

下面我们将详细介绍这三个命令。

2. 实现容器技术的三个关键 Linux 命令

2.1 chroot命令

chroot是一个 Linux 命令,允许为一个新进程创建根目录。当为一个容器设置一个新目录后,容器内的进程将无法访问到任何根目录外的数据,这就消除了数据泄露的安全隐患。

运行以下命令开始实践:

  1. docker run -it --name docker-host --rm --privileged ubuntu:bionic

命令解析:

    • docker run:在容器中运行一些命令;
    • -it:令 shell 保持可交互状态;
  1. 创建新目录并进入: mkdir /my-new-root && cd $_
  2. 创建一些秘密文件:echo "my super secret thing" >> /my-new-root/secret.txt
  3. 运行命令:chroot /my-new-root bash

此时,程序会报错:

chroot: failed to run command 'bash': No such file or directory

这是因为新的根目录 /my-new-root 内并未包含 bash程序,执行以下命令修复:

  1. mkdir /my-new-root/bin
  2. cp /bin/bash /bin/ls /my-new-root/bin/
  3. chroot /my-new-root bash

此时程序依然会报错,因为我们尚未安装 bash的依赖。(通过 ldd命令可查看):

linux-vdso.so.1 (0x00007ffe5705a000)
libtinfo.so.5 => /lib/x86_64-linux-gnu/libtinfo.so.5 (0x00007fb89f047000)
libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fb89ee43000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb89ea52000)
/lib64/ld-linux-x86-64.so.2 (0x00007fb89f58b000)

接着运行以下命令:

  1. mkdir /my-new-root/lib{,64}
  2. 将 bash 的依赖项拷贝至新建的根目录:
    • cp /lib/x86_64-linux-gnu/libtinfo.so.5 /lib/x86_64-linux-gnu/libdl.so.2 /lib/x86_64-linux-gnu/libc.so.6 /my-new-root/lib
    • cp /lib64/ld-linux-x86-64.so.2 /my-new-root/lib64
  1. ls 的依赖项如法炮制的安装:cp /lib/x86_64-linux-gnu/libselinux.so.1 /lib/x86_64-linux-gnu/libpcre.so.3 /lib/x86_64-linux-gnu/libpthread.so.0 /my-new-root/lib

此时,运行 chroot /my-new-root bash命令将成功运行。在 bash shell 中使用 pwd命令可见,当前根目录为 /。至此,我们完成了目录级别的资源隔离。

2.2 unshare命令

chroot命令使操作系统可以使用户彼此无法访问目录下的文件,但用户依然可以通过查看系统进程了解计算机的运行情况。通过杀死进程,卸载文件系统等手段,恶意用户依然会对计算机的安全造成威胁。

2.2.1 chroot 命令的问题

  1. 开启一个新的终端,并运行 docker exec -it docker-host bash 命令进入操作系统;
  2. 运行 tail -f /my-new-root/secret.txt & 命令,持久化一个后台进程;
  3. 运行 ps 命令查看进程 ID(PID);
  4. 在原先的终端中执行 kill <PID> 命令,可见终端 2 的持久化进程已经被杀死了;

由此可见,仅仅做到文件系统的隔离是不够的,因此需要通过 unshare命令,隐藏进程,让进程之间彼此不透明。

2.2.2 unshare 命令

unshare命令将从父进程中创建一个独立的命名空间。代码操作如下:

exit # from our chroot'd environment if you're still running it, if not skip this

# install debootstrap
apt-get update -y
apt-get install debootstrap -y
debootstrap --variant=minbase bionic /better-root

# head into the new namespace'd, chroot'd environment
unshare --mount --uts --ipc --net --pid --fork --user --map-root-user chroot /better-root bash # this also chroot's for us
mount -t proc none /proc # process namespace
mount -t sysfs none /sys # filesystem
mount -t tmpfs none /tmp # filesystem

再重复一次我们刚才的实验会发现,此时终端 #1 已经无法再访问和杀死终端 #2 的持久化进程了。

2.3 cgroups命令

即使通过 chroot 命令隔离文件系统,通过 unshare隔离进程,每个隔离环境依然可以访问服务器的所有物理资源,这使得当服务器中的一个租户运行大量计算占满计算资源时,其他租户的服务将无以为继。

这时候就需要用到 cgroups(control groups) 命令。它使得每个隔离单元只能够有限地使用系统资源。

具体操作如下:

# outside of unshare'd environment get the tools we'll need here
apt-get install -y cgroup-tools htop

# create new cgroups
cgcreate -g cpu,memory,blkio,devices,freezer:/sandbox

# add our unshare'd env to our cgroup
ps aux # grab the bash PID that's right after the unshare one
cgclassify -g cpu,memory,blkio,devices,freezer:sandbox <PID>

# list tasks associated to the sandbox cpu group, we should see the above PID
cat /sys/fs/cgroup/cpu/sandbox/tasks

# show the cpu share of the sandbox cpu group, this is the number that determines priority between competing resources, higher is is higher priority
cat /sys/fs/cgroup/cpu/sandbox/cpu.shares

# kill all of sandbox's processes if you need it
# kill -9 $(cat /sys/fs/cgroup/cpu/sandbox/tasks)

# Limit usage at 5% for a multi core system
cgset -r cpu.cfs_period_us=100000 -r cpu.cfs_quota_us=$[ 5000 * $(getconf _NPROCESSORS_ONLN) ] sandbox

# Set a limit of 80M
cgset -r memory.limit_in_bytes=80M sandbox
# Get memory stats used by the cgroup
cgget -r memory.stat sandbox

# in terminal session #2, outside of the unshare'd env
htop # will allow us to see resources being used with a nice visualizer

# in terminal session #1, inside unshared'd env
yes > /dev/null # this will instantly consume one core's worth of CPU power
# notice it's only taking 5% of the CPU, like we set
# if you want, run the docker exec from above to get a third session to see the above command take 100% of the available resources
# CTRL+C stops the above any time

# in terminal session #1, inside unshare'd env
yes | tr \n x | head -c 1048576000 | grep n # this will ramp up to consume ~1GB of RAM
# notice in htop it'll keep the memory closer to 80MB due to our cgroup
# as above, connect with a third terminal to see it work outside of a cgroup

3. 小结

通过综合使用 chrootunsharecgroups 命令,我们能够基于操作系统,有效地创建一个隔离单元,隔离文件系统,进程并设置计算机资源的使用上限。这就是容器技术的核心。它帮助用户节省掉了维护操作系统的开销,可以使用户专注于应用程序的开发和部署。

希望各位读者有所收获,后会有期:)

目录
相关文章
|
1月前
|
运维 Kubernetes 监控
提升运维效率:容器化技术在现代IT基础设施中的应用
本文将探讨容器化技术如何优化企业的IT基础设施,提高部署效率和资源利用率。我们将深入分析容器技术的优势、实现步骤以及在实际运维中的应用场景。通过实例展示,帮助读者更好地理解并应用这一前沿技术,助力企业实现高效运维。
|
4天前
|
Java 应用服务中间件 Linux
【Docker容器化技术】docker安装与部署、常用命令、容器数据卷、应用部署实战、Dockerfile、服务编排docker-compose、私有仓库
本文主要讲解了Docker的安装与部署、常用命令、容器数据卷、应用部署实战、Dockerfile、服务编排docker-compose、私有仓库以及Docker容器虚拟化与传统虚拟机比较。
【Docker容器化技术】docker安装与部署、常用命令、容器数据卷、应用部署实战、Dockerfile、服务编排docker-compose、私有仓库
|
1天前
|
存储 Kubernetes 调度
基于容器化技术的性能优化实践
基于容器化技术的性能优化实践
8 3
|
11天前
|
存储 持续交付 虚拟化
|
21天前
|
存储 应用服务中间件 云计算
深入解析:云计算中的容器化技术——Docker实战指南
【10月更文挑战第14天】深入解析:云计算中的容器化技术——Docker实战指南
50 1
|
23天前
|
弹性计算 Kubernetes 网络协议
阿里云弹性网络接口技术的容器网络基础教程
阿里云弹性网络接口技术的容器网络基础教程
阿里云弹性网络接口技术的容器网络基础教程
|
26天前
|
运维 JavaScript 虚拟化
探索容器化技术:Docker的实践与应用
【10月更文挑战第9天】探索容器化技术:Docker的实践与应用
47 3
|
8天前
|
人工智能 Anolis 开发者
|
17天前
|
运维 Kubernetes 开发者
构建高效后端服务:微服务架构与容器化技术的结合
【10月更文挑战第18天】 在数字化转型的浪潮中,企业对后端服务的要求日益提高,追求更高的效率、更强的可伸缩性和更易于维护的系统。本文将探讨微服务架构与容器化技术如何结合,以构建一个既灵活又高效的后端服务体系。通过分析当前后端服务面临的挑战,介绍微服务和容器化的基本概念,以及它们如何相互配合来优化后端服务的性能和管理。本文旨在为开发者提供一种实现后端服务现代化的方法,从而帮助企业在竞争激烈的市场中脱颖而出。
21 0
|
20天前
|
存储 Kubernetes 监控
深入探索Docker容器化技术的奥秘
【10月更文挑战第15天】深入探索Docker容器化技术的奥秘
17 0