Docker 的核心

容器和虚拟机的区别

一直在使用 Docker 来开发一些东西,编排一些环境, 但是没有做一个深入到底层的了解与总结,被问到 容器与虚拟机的区别的时候, 没法系统的去总结, 这里做个笔记。

容器与虚拟机

容器和虚拟机它们的目的很相似:即将应用程序和它的依赖放到一个可以在任何环境运行的自足单元中。

此外,容器和虚拟机消除了对物理硬件的需求,从而在能源消耗和成本效益方面能让我们更有效地使用计算资源,

容器和虚拟机的主要区别在于它们的架构方式

虚拟机

虚拟机 是共享一个服务器的物理资源的操作系统.它是主机硬件上的Guest,因此也被称为Guest虚拟机。虚拟机由几层组成。支持虚拟化的层是hypervisor。

hypervisor 是能让虚拟机在其上运行的软件,固件或者硬件, 虚拟机管理程序本身会在物理机上运行,称为”主机”。主机为虚拟机提供思源,包括 RAM和CPU.这些资源在虚拟机之间被划分并且可以根据需要进行分配。所以如果一个虚拟机上运行了资源占用更大的应用程序, 相较于其他运行在同一个主机的虚拟机可以给其分配更多的资源.

运行在主机上的虚拟机包含了应用以及运行这个应用所需要的全部依赖,它还带有一个自己的完整虚拟硬件栈,包括虚拟化的网络适配器,储存和CPU。从虚拟机内部看,访客机的操作都认为其使用的都是自己的专用资源.从外部看,我们知道他是一个虚拟机和其他虚拟机一起共享主机提供的资源.

由于虚拟机拥有自己的操作系统,管理程序为虚拟机管理和执行访客操作系统提供了一个平台。它允许主机与作为客户端运行的虚拟机之间共享资源.

容器

与提供硬件虚拟化的虚拟机不同,容器通过抽象”用户空间”来提供操作系统级别的虚拟化.不依赖与操作系统,运行应用程序的环境,它通过Linux的NamespacesCgroups 技术对应用程序进程进行隔离和限制的, Namespace 的作用是隔离, 它让应用进程只能看到该 Namespaces 内的世界;而 Cgroups的作用是限制分配给进程的宿主机资源.但对于宿主机来说 这些被隔离了的进程跟其他进程并没有太大的区别.

容器只是运行在宿主机上的一种特殊的进程,多个容器之间使用的还是同一个宿主机的操作系统内核。

Namespaces

命名空间(namespaces) 是Linux用于分离进程树,网络借口,挂载点以及进程间通信等资源的方法.在日常时候, 并没有运行多个完全分离的服务的需要.但是如果在服务器上启动了多个服务,这些服务其实会互相影响的,每一个服务都能看到其他服务的进程.也可以访问宿主机器上的任意文件.这是不愿意看到的,而Docker容器其实就通过Linux的Namespaces 对不同的容器实现了隔离。

Linux 的命名空间机制 提供了以下七种不同的命名空间:

名称 宏定义
IPC CLONE_NEWIPC
Network CLONE_NEWNET
Mount CLONE_NEWNS
PID CLONE_NEWPID
User CLONE_NEWUSER
UTS CLONE_NEWUTS
Cgroup CLONE_NEWCGROUP

这七个选项在创建新的进程时设置新进程应该在哪些资源上与宿主机器进行隔离。

  • IPC: 用于隔离进程间通讯所需的资源,PID命名空间和IPC命名空间可以组合起来用,同一个IPC名字空间内的进程可以彼
    此看见,允许进行交互,不同空间进程无法交互;

  • Network: 为进程提供一个完全独立的网络协议栈的试图.包括网络设备接口, IPv4和IPv6 协议栈, IP 路由表,防火墙规则,sockets 等等.一个Network Namespace 提供了一份独立的网络环境, 就跟一个独立的系统一样

  • Mount: 每个进程都存在与一个 mount Namespace 里面, mount Namespace 为进程提供了一个文件层视图. 如果不设定这个flag,子进程和父进程将共享一个 mount Namespace。其后子进程调用mount或umount将会影响到所有该Namespace内的进程。如果子进程在一个独立的mount Namespace里面,就可以调用mount或umount建立一份新的文件层次视图;

  • PID::linux通过命名空间管理进程号,同一个进程,在不同的命名空间进程号不同!进程命名空间是一个父子结构,子空
    间对于父空间可见;

  • User:用于隔离用户;

  • UTS:用于隔离主机名;

/proc/[pid]/ns 目录下会包含进程所属的 namespace 信息,可以使用 ll /proc/$$/ns 命令查看当前进程所属namespace信息

进程

进程是 Linux 以及现在操作系统中非常重要的概念,它表示一个正在执行的程序,也是在现代分时系统中的一个任务单元。

在 Linux系统的进程中 有两个特殊的进程,一个是 pid 为1 的 /sbin/init 进程, 另一个是 pid为2 的 kthreadd 进程,这两个进程都是 被Linux 中的上帝进程 idle 创建出来的 其中pid 1 负责执行内核的一部分初始化工作和系统配置, 也会创建一些类似 getty 的注册进程 pid 2 负责管理和调度其他的内核进程

网络

docker run 启动的容器其实都具有单独的网络命名空间 Docker有四种不同的网络模式,HOST,Container,None Bridage 模式. 默认是网桥模式

在网桥模式下 当Docker 在主机上启动之后会创建新的虚拟网桥 docker0,随后在该主机上启动全部服务在默认情况下与该网桥相连接.

默认情况下, 每一个容器在创建时都会创建一对虚拟网卡,两个虚拟网卡组成了数据通道,其中一个会放在创建的容器中, 会加入到名为 docker0网桥中。可以使用 brctl show 来查看当前网桥的接口。

docker0 会为每一个容器分配一个新的 IP 地址并将 docker0 的 IP 地址设置为默认的网关。网桥 docker0 通过 iptables 中的配置与宿主机器上的网卡相连,所有符合条件的请求都会通过 iptables 转发到 docker0 并由网桥分发给对应的机器。

Docker 通过 Linux 的命名空间实现了网络的隔离,又通过 iptables 进行数据包转发,让 Docker 容器能够优雅地为宿主机器或者其他容器提供服务。