天天看点

极客时间云原生训练营

 docker跟以往的vm虚拟机有什么区别?

为什么我们经常说docker是轻量级的虚拟机,它跟我们之前用的vmware,virtualbox等虚拟机有什么区别,轻在哪儿呢?让我们首先看看两者对比图:

vm虚拟机 vs docker

从上图我们可以很直观地看到这两者的区别:

vm虚拟机:从下到上,分别是server(物理机,如macbook)、host os(物理机的操作系统,如macos)、hypervisor(用于运行虚拟机负责虚拟化的管理软件,如virtualbox)、guest os(在虚拟机上安装的操作系统,如ubuntu,跟上层应用服务没有明显绑定关系)、bins/libs(app依赖的各种类库)、apps(应用程序)。

其中我们可以很明显地看出来,app跟依赖库(libs)以及虚拟机操作系统之间没有很明显的绑定关系,app在能够到虚拟机操作系统上面运行之前,需要提前准备环境,增加不少运维成本,更重要的是,部署应用程序的时候,不能保证依赖的运行环境必然充分。

docker:从下往上,分别是server、host os(前二相同)、docker engine(docker的运行引擎,守护进程,我们待会也会着重讲解这块的核心设计)、containers(多个运行容器)。

docker的精髓主要体现在docker engine层的资源隔离设计以及container层的一体化打包方式上。另外,docker比vm虚拟机轻的地方在于,由docker engine抽象出来的虚拟化技术,让上层的container服务可以设计得非常轻量:稳定的镜像+空间节约+秒级启动。

docker engine

docker基本思想

基础设施即代码(infrastructure as code)

docker的出现,主要是解决在开发、测试、线上运维的各个阶段,需要一种虚拟化技术来解决环境不一致的问题。

docker把基础设施的构建过程通过dockerfile让我们将应用程序的运行环境依赖跟业务代码一起纳入到版本控制中,避免因为环境不一致造成运行结果不符预期的可能。这种方式就是我们常说的基础设施即代码的思想。

不可变基础设施(immutable infrastructure)

通过版本控制 + ci/cd过程 + docker技术,我们可以在每次发布时构建出来一个不可变的镜像,这个镜像不管在什么平台上面运行,都能借助docker engine的能力,维持应用程序的环境稳定,减少运维维护成本。这就是“不可变基础设施(immutable infrastructure)”。

docker的出现确实是应时代所需,那么,docker的这种虚拟化技术的出现有哪些核心技术的支撑呢?

docker核心实现

docker 技术架构

docker的虚拟化技术借助了linux系统的基础技术,如使用namespace来隔离资源,使用cgroups来隔离执行单元,使用ufs来组合不同文件系统。

下面我们逐个对这些概念做下了解。

namespace

docker虚拟化的第一个问题,是如何在同一台机器上,把进程、内存、文件系统、网络这些基本要素隔离起来,让每一个容器之间互不影响?

解决方案是namespace,目前,linux内核里面提供了7种不同类型的namespace:

名称        宏定义             隔离内容

cgroup      clone_newcgroup   cgroup root directory (since linux 4.6)

ipc         clone_newipc      system v ipc, posix message queues (since linux 2.6.19)

network     clone_newnet      network devices, stacks, ports, etc. (since linux 2.6.24)

mount       clone_newns       mount points (since linux 2.4.19)

pid         clone_newpid      process ids (since linux 2.6.24)

user        clone_newuser     user and group ids (started in linux 2.6.23 and completed in linux 3.8)

uts         clone_newuts      hostname and nis domain name (since linux 2.6.19)

其中,docker主要使用了其中的cgroups, ipc, network, mount, pid:

1、进程隔离(pid namespace)

我们运行一个redis的docker容器,通过docker exec的方式进入容器内部,查看进程列表,可以看到:

# ps -ef

uid        pid  ppid  c stime tty          time cmd

redis        1     0  0  2018 ?        09:01:49 redis-server 0.0.0.0:6379      

root     61599     0  1 15:18 ?        00:00:00 /bin/bash

root     61604 61599  0 15:18 ?        00:00:00 ps -ef

看到里面展示的只有这个container的进程列表,却对宿主机的其他进程一无所知,而且这个进程列表的第一条进程的pid是1,也就是init进程,这就不是简单的进程过滤了,而是通过clone_newpid这个namespace实现的,完全创建出来了一套独立的进程管理体系来实现进程隔离。

2、网络隔离(network namespace)

docker中的服务,大部分是需要通过网络来实现与外界通信的,那如何让container有自己的网络地址避免端口冲突,又能通过宿主机跟外界交互呢?

linux network namespace能让进程拥有一个完全独立的网络协议栈视图,而docker利用它为每个container提供一个独立的虚拟网卡,并提供了4种网络隔离的方式给container使用,它们分别是:

host模式,--net=host,使用跟宿主机一样的网络,不会分配独立的network namespace。

container模式,--net=containerid,指定跟其他container共同使用同一个已创建的network namespace。

none模式,--net=none,拥有空的独立network namespace,但不会创建独立虚拟网卡。

bridge模式,--net=bridge,是docker的默认方式,拥有独立的network namespace及虚拟网卡、独立ip,并通过虚拟网桥的方式连接到宿主机对外通信。