但是一个关键性问题还没有搞清楚——容器,到底是怎么一回事?
接下来我将通过两个关键词,给大家揭开容器技术的神秘面纱
前面的文章提到过,容器其实是一种”沙盒“技术
”沙盒“就像是一个集装箱一样,把你的应用”装起来“,这样,应用与应用之间,就因为有了边界而不至于相互干扰;而被装进集装箱的应用,也可以被方便地搬来搬去,这不就是 PaaS 最理想的状态嘛
Docker 公司的标志,是一头鲸鱼上背着集装箱,那一个个集装箱,代表着一个个容器
应用之间互不干扰,说起来简单,但要用技术手段去实现他们,可能大多数人就无从下手了
我们知道,程序其实就是存储在磁盘上的一个个二进制文件,一旦被加载到内存中交给 CPU 去执行,就成了一个个进程
总的来说,程序是存储在磁盘上的二进制文件、是静态的,而进程就是程序的运行时,是动态的
容器技术的核心功能,就是通过约束和修改进程的动态表现,从而为其创造一个”边界“,将进程隔离起来
那么怎么才能将进程隔离起来呢?答案是——Namespace 技术
Namesapce 技术通过修改进程视图来实现进程间的隔离
举个例子:
在我的 Linux 操作系统上有 Docker 项目正在运行,我的操作系统环境是 CentOS 7
我们先以终端形式启动一个容器,并在容器里执行 /bin/bash
docker exec -it myApp /bin/sh/
这样,我的 CentOS 7 就变成了一个宿主机,而这个我启动的容器,就在宿主机上面运行着
我们在容器里执行 ps 命令
PID USER TIME COMMAND
1 root 0:00 /bin/sh
10 root 0:00 ps
我们可以看到,容器里只有两个进程在运行,一个是最开始执行的 /bin/bash (PID=1),另一个是刚刚执行的 ps
可以看到,容器里面的进程被 Docker 隔离在了一个跟宿主机完全不同的世界当中
究竟是怎么做到的呢?
一般情况下每当我们在宿主机上运行一个 /bin/bash 程序,操作系统都会为其分配一个 PID(例如 PID=100),PID 是进程的唯一标识。(PS:PID为1则意味着这个进程是第一个进程,是后面所有进程的爹)
当我们通过 Docker 把这个 /bin/bash 程序(假设 PID =100)运行在一个容器当中的时候,Docker就会给这个 /bin/bash 施加一个“障眼法”——让它看不到前面 99 个进程,让它误以为自己就是第一个进程(即 PID=1)
这种技术,就是 Linux 里面的 Namespace 机制
这种机制,其实就是对被隔离应用的进程空间做了手脚,使得这些进程只能看到重新计算过的进程编号,比如 PID=1。可实际上,他们在宿主机的操作系统真实的进程空间里,还是原来的第 100 号进程
而每个 Namespace 里的应用进程,都会认为自己是当前容器里的第 1 号进程,它们既看不到宿主机里真正的进程空间,也看不到其他 PID Namespace 里的具体情况
除了我们介绍的 PID Namespace ,Linux 操作系统还提供了 Mount 、UTS、IPC、Network和User 这些 Namespace,用来对各种不同的进程上下文进行“障眼法”
比如,Mount Namespace,用于让被隔离进程只看到当前 Namespace 里的挂载点信息;Network Namespace,用于让被隔离进程看到当前 Namespace 里的网络设备和配置
以上这些,便是 Linux 容器技术最基本的实现原理之一
总结:
我们知道,Linux 通过 Namespace 机制将容器进程隔离起来,让它们只能再他们的空间里运行
但对于宿主机来说,这些被“隔离”的进程(容器)跟其他进程并没有太大的区别
我们站在宿主机的角度上来看,这些被“隔离”的进程(容器)本质上跟其他进程没有什么区别,这些容器使用的是同一个宿主机的操作系统内核
这就会导致一个问题——隔离得不彻底
尽管你在容器里通过 Mount Namespace 单独挂载其他不同版本的操作系统,比如 CentOS 或 Ubuntu,但这并不能改变容器共享宿主机内核的事实
这表明你想在 Windows 宿主机上运行 Linux 容器,或者在低版本的 Linux 宿主机上运行高版本的 Linux 容器,都是行不通的
其次,还会导致另一个问题——在 Linux 内核中,有很多资源和对象是不能被 Namespace 化的,最典型的例子就是:时间
如果你在容器里修改了时间,那么整个宿主机的时间都会被修改。所以说,在容器里部署应用的时候,“什么能做,什么不能做”是需要考虑的一个问题
所以说,我们除了对容器要进行**“隔离”,我们还需要对容器进行“限制”**
我们接着引用上面的例子,虽然容器里的 1 号进程在“障眼法”的干扰下只能看到容器里的情况,但是在宿主机上,它是 100 号进程,它去宿主机上的其他进程之间是平等的竞争关系
即虽然 100 号进程被隔离起来,但是它能够使用到的资源(CPU、内存等)是可以随时被宿主机上的其他进程(或者其他容器)占用的,当然,这个 100 号进程自己也可能把所有资源吃光
Linux Cgroups 就是 Linux 内核中用来为进程设置资源限制的一个重要功能
想了解更多关于 Linux Cgroups 的知识 ,请翻阅下面的文档:
Linux资源管理之cgroups简介 - 美团技术团队 (meituan.com)
总结: