容器概述

服务器

浏览数:21

2019-3-15

在这里尝试简单回答一些常见的关于容器的疑问,仅供参考。

容器是什么

简单点说,容器就是一个或多个进程以及他们所能访问的资源的集合。

容器技术的本质是对计算机系统资源的隔离和控制,让原来全局的资源变得只能部分进程之间共享,这跟我们常说的虚拟机这种虚拟化技术没有关系,最新的标准在制定过程中,包括镜像的格式,容器运行时的一些规范,具体见Open Container Initiative(OCI)

容器和虚拟机的差别

从技术角度来看,他们是不同的两种技术,没有任何关系,但由于他们的应用场景有重叠的地方,所以人们经常比较他们两个

  • 容器目前只能在Linux上运行,容器里面只能跑Linux

  • 虚拟机可以在所有主流平台上运行,比如Windows,Linux,Mac等,并且能模拟不同的系统平台,如在Windows下安装Linux的虚拟机

  • 容器是Linux下一组进程以及他们所能访问资源的集合,所有容器共享一个内核,要比虚拟机轻量级,占用系统资源少,并且容器比虚拟机要快,包括启动速度,生成快照速度等

  • 虚拟机是一整套的虚拟环境,包括BIOS, 虚拟网卡, 磁盘, CPU,以及操作系统等, 启动慢,占用硬件资源多.

  • 由于虚拟机的和主机只是共享硬件资源,隔离程度要比容器高,所以相对来说虚拟机更安全

什么时候应该用虚拟机,什么时候应该用容器

打个比方,A准备在网上卖东西,是自己搭建一套电商平台,还是直接在淘宝上开个小店呢?这就得看A的需求,自己搭建平台当然好,完全自己控制,想有什么功能都可以,但缺点是成本太高,淘宝上开店成本小,但要受淘宝的很多限制。想想如果淘宝的店子就能满足需求,为什么还费那么大劲去自己搭建平台呢?

虚拟机和容器的关系也差不多,虚拟机当然好,完全控制在自己手中的一套系统,没有条条框框的限制,缺点就是启动慢耗资源;容器启动快耗资源少,但受到的限制比较多(后面介绍docker的时候会提到这些限制)。所以一般的原则是如果容器能满足需求,就用容器,如果容器满足不了就用虚拟机。如何判断容器是否满足需求呢?大致原则是如果应用对硬件和内核没有特殊需求,一般都能使用容器,否则需要咨询专家。

当然这里只是从功能和资源消耗的角度来考虑,实际情况要比这个复杂的多,包括管理是否方便,相关的技术是否成熟等等。

docker和容器的关系

docker是容器管理技术的一种实现,用来管理容器,就像VMware是虚拟机的一种实现一样,除了docker,还有LXC/LXDRocketsystemd-nspawn,只是docker做的最好,所以我们一说容器,就想到了docker。

为什么容器只出现在Linux里面

因为Linux中有资源隔离和管理的机制(Namespace,CGroups),有COW(copy on write)文件系统等容器所需要的基础技术。当然其他平台也有类似的东西,但功能都没有Linux下的完善,不过随着容器技术越来越流行,其他的系统平台也在慢慢的实现和完善类似的这些技术。

为什么容器里面只能运行Linux

因为Linux下的所有容器共享一个Linux内核,所以容器里面只能跑Linux系统

不同的Linux发行版为什么可以共享内核

先看一下Linux下启动一个进程的大概过程,当在Shell里面启动一个程序的时候,会发生如下过程:

  1. Shell运行系统调用,通知内核启动一个指定位置上的程序

  2. 内核加载指定位置上的可执行文件以及它所依赖的动态库

  3. 初始化进程的地址空间,并把可执行文件映射到相应的内存地址(如果文件比较大的话,不会把整个可执行程序一次性加载到内存中,只是映射过去,然后利用内存缺页中断在需要的时候将所需的内容加载到内存中去)

  4. 开始调度进程(简单点说就是内核会让这个进程时不时的运行一会儿)

从上面的过程可以看出,内核唯一需要和应用层达成一致的是可执行程序的文件格式,不然内核就没法加载程序并调度进程。

当进程开始运行后,进程和内核的交互就是系统调用,当进程需要访问由内核管理的资源时,采用软件中断的方式和内核交互,每个系统调用都有一个中断号,并且这个号不会随着内核版本变化而变化。

关于内核所支持的系统调用请参考这里,系统调用因为涉及到用户态到内核态的切换,所以非常耗时,但Linux里面有一项叫做VDSO的技术,可以将内核态的一些地址直接映射到所有进程的用户态空间,于是系统调用就跟访问普通的变量一样了,比如我们常用的gettimeofday函数,就不需要切换到内核态,直接读取内核映射到进程内存空间的内容就可以了,非常快。

从上面可以看出,Linux内核和应用层进程之间的关系是松耦合的,只要保证两个条件:

  • 内核能识别应用层程序的格式

  • 应用层需要的系统调用内核能支持

由于Linux下可执行文件和动态库的格式以及系统调用的接口都比较稳定,所以不同的Linux发行版在大部分情况下都可以共享同一个内核。一般来说,新的内核兼容老的Linux发行版,但太老的内核不一定支持新的Linux发行版。如果你的应用对内核有特殊需求,那么应该考虑一下用容器是否是一个明智的选择。

容器启动为什么那么快?

容器的本质是一个或多个进程以及他们所能访问的资源的集合。启动一个容器的步骤大概就是:

  1. 配置好相关资源,如内存、磁盘、网络等
    配置资源就是往系统中添加一些配置,非常快

  2. 初始化容器所用到的文件目录结构
    由于Linux下有COW(copy on write)的文件系统,如Btrfs、aufs,所以可以很快的根据镜像生成容器的文件系统目录结构。

  3. 启动进程
    和启动一个普通的进程没有区别,对Linux内核来说,所有的应用层进程都是一样的

从上面可以看出启动容器的过程中没有耗时的操作,这也是为什么容器能在毫秒级别启动起来的原因

启动容器会占用很多资源导致系统变慢吗

由于Namespace和CGroups已经是Linux内核的一部分了,所以应用层运行的进程一定会属于某个Namespace和CGroups(如果没有指定,就属于默认的Namespace和CGroups),也就是说,就算我们不用Docker,所有的进程都已经运行在默认容器中了。对内核来说,默认容器中运行的进程和Docker创建的容器中运行的进程没有什么区别,就是他们所属的容器号不一样。

所以说创建新容器会不会影响主机性能完全取决于容器里面运行什么东西。如果运行的是耗资源的进程,那么肯定会对主机性能造成影响,但这种影响可以在一定程度上由CGroups控制住,不至于对主机带来灾难性的影响。如果容器里面运行的是不耗资源的进程,那么对系统就没有影响,只是容器里面的文件系统可能会占用一些磁盘空间。