Docker笔记

344 阅读3分钟

容器与虚拟机的区别

虚拟机: 通过硬件虚拟化功能,模拟出了运行一个操作系统需要的各种硬件,比如 CPU、内存、I/O 设备等等。然后,它在这些虚拟的硬件上安装了一个新的操作系统,即 Guest OS。

容器: 只是一种特殊的进程,多个容器之间使用的就还是同一个宿主机的操作系统内核。不需要包含整个客户操作系统,容器可以秒级启动和停止。

用户运行在容器里的应用进程,跟宿主机上的其他进程一样,都由宿主机操作系统统一管理,只不过这些被隔离的进程拥有额外设置过的 Namespace 参数。

容器的组成

容器 = cgroup + namespace + rootfs + 容器引擎(用户态工具)

docker架构图:

cgroup

资源限制(cpu,memory,device,freezer,blkio)

blkio,为 块 设 备 设 定 I/O 限 制,一般用于磁盘等设备;

cpuset,为进程分配单独的 CPU 核和对应的内存节点;

memory,为进程设定内存使用的限制。

namespace

资源隔离(Mount/UTS/IPC/PID/Net/User Namespace)

容器就只能“看”到当前 Namespace 所限定的资源、文件、设备、状态,或者配置。

PID Namespace

int pid = clone(main_function, stack_size, CLONE_NEWPID | SIGCHLD, NULL);

宿主机创建一个进程,进程id为100,而该进程的进程空间,看到自己的进程id是1.

看到当前正在运行的 Docker 容器的进程号(PID)是 25686:

$ docker inspect --format '{{ .State.Pid }}' 4ddf4638572d

25686

通过查看宿主机的 proc 文件,看到这个 25686 进程的所有 Namespace 对应的文件:

$ ls -l /proc/25686/ns

total 0
lrwxrwxrwx 1 root root 0 Aug 13 14:05 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 ipc -> ipc:[4026532278]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 mnt -> mnt:[4026532276]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 net -> net:[4026532281]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid_for_children -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 uts -> uts:[4026532277]

一个进程的每种 Linux Namespace,都在它对应的 /proc/[进程号]/ns 下有一个对应的虚拟文件,并且链接到一个真实的 Namespace 文件上。

这也就意味着:一个进程,可以选择加入到某个进程已有的 Namespace 当中,从而达到“进入”这个进程所在容器的目的,这正是 docker exec 的实现原理。

rootfs

文件系统隔离

rootfs 里打包的不只是应用,而是整个操作系统的文件和目录

Docker 在镜像的设计中,引入了层(layer)的概念。也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量 rootfs。

UnionFS,最主要的功能是将多个不同位置的目录联合挂载(union mount)到同一个目录下

可读写层: 它以 Copy-on-Write 的方式存放任何对只读层的修改,容器声明的 Volume 的挂载点,也出现在这一层。

Init 层: 用来存放被临时修改过的 /etc/hosts 等文件。

只读层: 存放Docker 镜像

容器引擎

生命周期控制