容器与虚拟机的区别

目录

作者:杨冬 欢迎转载,也请保留这段声明。谢谢!
出处:https://andyyoung01.github.io/http://andyyoung01.16mb.com/

初次接触到容器的人也许会将容器看成是类似于虚拟机的技术。容器不是虚拟机技术。它不模拟任何硬件也不包含操作系统。Docker容器默认情况下并不对任何硬件资源做出限制。如果说Docker虚拟化了什么,它虚拟化了服务运行的环境,而不是主机。

虽然容器不是虚拟机,但是从某点上来说,Docker可以像使用VM那样使用。在Docker中启动一个类似于虚拟机的镜像,运行一个包含多个进程的容器,这在Docker社区中一直被讨论,同时也被社区认为是一种不好的实践。因为容器不是虚拟机,它与VM有许多区别,忽略这些区别会引起疑惑和问题。
容器和虚拟机的主要区别如下:

  • Docker是面向应用的,VMs是面向操作系统的。
  • Docker容器与其它Docker容器共享操作系统。而VMs则相反,每个VMs都有其自己的操作系统,由系统管理程序管理。
  • Docker容器被设计为运行一个主要进程,而不是管理多个进程集合。

不过,运行一个类似于虚拟机的镜像可以向那些拒绝使用Docker的人说明Docker是有用的。随着使用,这些人就能够更好地理解容器范例以及微服务的架构。下面我们就来看看运行一个类似于虚拟机的镜像。
我们使用phusion/baseimage镜像来模拟虚拟机,它被设计为可以运行多个进程的镜像,步骤如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[yangdong@centos7 ~]$ docker run -d --name host_like_container phusion/baseimage
db4c35add76c5c3ebd5860f3fa5bce6655020a4f786615464645d94ff68e4e4e
[yangdong@centos7 ~]$ docker exec -it host_like_container /bin/bash
root@db4c35add76c:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 05:34 ? 00:00:00 /usr/bin/python3 -u /sbin/my_init
root 7 1 0 05:34 ? 00:00:00 /usr/bin/runsvdir -P /etc/service
root 8 7 0 05:34 ? 00:00:00 runsv cron
root 9 7 0 05:34 ? 00:00:00 runsv sshd
root 10 7 0 05:34 ? 00:00:00 runsv syslog-forwarder
root 11 7 0 05:34 ? 00:00:00 runsv syslog-ng
root 12 11 0 05:34 ? 00:00:00 syslog-ng -F -p /var/run/syslog-ng.pid --no-caps
root 14 10 0 05:34 ? 00:00:00 tail -F -n 0 /var/log/syslog
root 15 8 0 05:34 ? 00:00:00 /usr/sbin/cron -f
root 19 0 0 05:35 ? 00:00:00 /bin/bash
root 31 19 0 05:35 ? 00:00:00 ps -ef

上面的命令先启动了一个镜像,然后通过docker exec命令得到容器内的bash。在容器内部,通过ps -ef命令列出了所有正在运行的进程。上面代码列表的第6行说明容器通过Python3启动了一个简单的init进程,该进程用于启动所有其它服务。第7行runsvdir运行所有在/etc/service目录下定义的services。后面四行表明四个服务(cron,sshd,syslog-forward,syslog-ng)通过runsv命令启动。
上面的容器的启动过程非常类似于一个 标准的Linux主机,先启动一个init进程,由它启动所有其它的服务。这种方式在Docker社区中很有争论。有人认为它违背了微服务的关于“每个容器一个服务”的原则,但支持它的人认为整个容器可以是它所在系统中一个单独的、独立的功能。
还是上面同样的镜像phusion/baseimage,如果以另外一种方式启动,如下:

1
2
3
4
5
[yangdong@centos7 ~]$ docker run -it --name container phusion/baseimage /bin/bash
root@df0a9f0c8e0b:/# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:09 ? 00:00:00 /bin/bash
root 14 1 0 08:10 ? 00:00:00 ps -ef

同样的镜像,只是在启动时指定执行bash命令,而没有运行容器镜像默认的启动管理程序my_init,再查询容器中包含的进程时发现,只有一个bash命令在运行。
上面的示例也可以看出,Docker容器和VM的一个关键区别是容器被设计为运行一个进程,当那个进程完成后,容器也随之退出;Linux VM(或任何Linux OS)包含一个启动进程,它以进程ID号1(父进程ID号0)运行,这个启动进程可能是init或者systemd,它的主要职责是管理操作系统上运行的其它进程。
如果你开始体验Docker,也许你想启动多个进程,你可能会写一些shell脚本来管理这些子进程的启动,实际上此脚本就是模拟init进程的功能,不要这样做!进程管理的许多问题已经由别人写好的程序解决了,这样的程序包括Supervisor等。所以,如果没有特别的需求,你可以直接使用Supervisor程序来创建你自己的类似于虚拟机的Docker镜像,也可以以上面的phusion/baseimage镜像为基础,构建你自己的容器虚拟机。