目录
一、虚拟化、容器化
1.1 概念
物理机:实际的服务器或者计算机。相对于虚拟机而言的,对实体计算机的称呼。物理机给虚拟机提供硬件环境,有时也称为"寄主"或"宿主"
虚拟化:是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率
容器化:容器化是一种虚拟化技术,又称操作系统层虚拟化(Operating system level virtualization),这种技术将操作系统内核虚拟化,可以允许用户空间软件实例(instances)被分割成几个独立的单元,在内核中运行,而不是只有一个单一实例运行。这个软件实例,也被称为是一个容器(containers)。对每个实例的拥有者与用户而言,其使用的服务器程序,就像是自己专用的。容器技术是虚拟化的一种。 docker 是现今容器技术的事实标准
1.2 优点
资源利用率高: 将利用率较低的服务器资源进行整合,用更少硬件资源运行更多业务,降低IT支出和运维管理成本
环境标准化:一次构建,随处执行。实现执行环境的标准化发布,部署和运维。开发过程中一个常 见的问题是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些bug并未在开发过程中被发现。而 Docker 的镜像提供了除内核外完整的运行时环境, 确保了应用运行环境一致性,从而不会再出现《这段代码在我机器上没问题啊》这类问题
资源弹性伸缩:根据业务情况,动态调整计算、存储、网络等硬件及软件资源。如:双11了,将服务扩容100个,双11结束了, 将扩容的100个收回
差异化环境提供:同时提供多套差异化的执行环境,限制环境使用资源。 如:一个服务依赖Ubuntu操作系统,一个服务依赖CentOS操作系统,但是没有预算购买两个物理机,此时容器化就能很好的提供多种不同的环境
沙箱安全:为避免不安全或不稳定软件对系统安全性、稳定性造成影响,可使用虚拟化技术构建 虚拟执行环境。 如:在容器里面执行 rm -rf /* 不会将整个服务器搞死,也不影响其他人部署的程序使用
容器比虚拟机更轻量,启动更快:传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。docker不需要虚拟内核,所以启动可以更快,相当于windows的开机时间省去了
维护和扩展容易:Docker使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。如:docker hub提供了很多镜像,各个系统的一个命令就可以拿到了,研发也可以自己定制镜像分享给各个产品
1.3 虚拟化常见类别
虚拟机:存在于硬件层和操作系统层间的虚拟化技术。虚拟机通过“伪造”一个硬件抽象接口,将一个操作系统以及操作系统层以上的层嫁接到硬件上,实现和真实物理机几乎一样的功能。如:在一台Windows系统的电脑上使用Android虚拟机,就能够用这台电脑打开Android系统上的应用
容器:存在于操作系统层和函数库层之间的虚拟化技术。容器通过"伪造"操作系统的接口,将函数库层以上的功能置于操作系统上。以Docker为例,其就是一个基于 Linux 操作系统的Namespace和Cgroup功能实现的隔离容器,可以模拟操作系统的功能。简单来说,若虚拟机是把整个操作系统封装隔离,从而实现跨平台应用的话,那么容器则是把一个个应用单独封装隔离,从而实现跨平台应用。所以容器体积比虚拟机小很多,理论上占用资源更少。容器化就是应用程序级别的虚拟化技术。容器提供了将应用程序的代码、运行时、系统工具、系统库和配置打包到一个实例中的标准方法。容器共享一个内核(操作系统)
JVM 之类的虚拟机:存在于函数库层和应用程序之间的虚拟化技术。Java 虚拟机同样具有跨平台特性,所谓跨平台特性实际上也就是虚拟化的功劳。Java语言是调用操作系统函数库的,JVM 就是在应用层与函数库层之间建立一个抽象层,对下通过不同的版本适应不同的操作系统函数库,对上提供统一的运行环境交给程序和开发者,使开发者能够调用不同操作系统的函数库
1.4 常见的虚拟化实现
1.4.1 主机虚拟化实现(虚拟机)
主机虚拟化的原理是通过在物理服务器上安装一个虚拟化层来实现。这个虚拟化层可以在物理服务器和客户操作系统之间建立虚拟机,使得其可以独立运行
从软件框架的角度上,根据虚拟化层是直接位于硬件之上还是在一个宿主操作系统之上,将虚拟化划分为 Type1 和 Type2
Type1类的Hypervisor(Hypervisor是一种系统软件,其充当计算机硬件和虚拟机之间的中介,负责有效地分配和利用由各个虚拟机使用的硬件资源,这些虚拟机在物理主机上单独工作。因此,Hypervisor也称为虚拟机管理器)直接运行在硬件之上,没有宿主机操作系统,Hypervisor直接控制硬件资源和客户机。典型框架为 Xen、Vmware ESX
Type2类的Hypervisor运行在一个宿主机操作系统之上(Vmware Workstation)或者系统里面,Hypervisor作为宿主机操作系统中的一个应用程序,客户机就是在宿主机操作系统上的一个进程
14.2 容器虚拟化实现
容器虚拟化有别于主机虚拟化,是操作系统层的虚拟化。通过 namespace 进行各程序的隔离,加上 cgroups 进行资源的控制,以此来进行虚拟化
二、容器虚拟化实现
2.1 基础命令补充
2.1.1 dd命令
Linux dd 命令用于读取、转换并输出数据。dd可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出
dd OPTION
if=文件名:输入文件名,默认为标准输入。即指定源文件
of=文件名:输出文件名,默认为标准输出。即指定目的文件
ibs=bytes:一次读入 bytes 个字节,即指定一个块大小为 bytes 个字节
obs=bytes:一次输出 bytes 个字节,即指定一个块大小为 bytes 个字节
bs=bytes:同时设置读入/输出的块大小为 bytes 个字节
cbs=bytes:一次转换 bytes 个字节,即指定转换缓冲区大小
skip=blocks:从输入文件开头跳过 blocks 个块后再开始复制
seek=blocks:从输出文件开头跳过 blocks 个块后再开始复制
count=blocks:仅拷贝 blocks 个块,块大小等于 ibs 指定的字节数
conv=<关键字>,关键字有以下:
- conversion:用指定的参数转换文件
- ascii:转换 ebcdic 为 ascii
- ebcdic:转换 ascii 为 ebcdic
- ibm:转换 ascii 为 alternate ebcdic
- block:每个换行结束的记录填充到指定的 cbs 大小,若记录长度不足 cbs,则用空格填充
- unblock:将已经使用 block 处理过的记录进行反转。会去除每个记录末尾的空格,并在 cbs 大小的记录末尾加上换行符,将其还原为原始的换行分隔格式
- lcase:把大写字符转换为小写字符
- ucase:把小写字符转换为大写字符
- swap:交换输入的每对字节
- noerror:出错时不停止
- notrunc:不截短输出文件
- sync:将每个输入块填充到 ibs 个字节,不足部分用空(NUL)字符补齐。
--help:显示帮助信息
--version:显示版本信息
# 生成 1 个镜像文件
dd if=/dev/zero of=fdimage.img bs=8k count=10240
#将 testfile_2 文件中的所有英文字母转换为大写,然后转成为 testfile_1 文件
dd if=testfile_2 of=testfile_1 conv=ucase
2.1.2 mkfs命令
用于在设备上创建 Linux 文件系统,俗称格式化,如:使用 U 盘时可以格式化
mkfs [-V] [-t fstype] [fs-options] filesys [blocks]
-t fstype:指定要建立何种文件系统;如 ext3,ext4
filesys:指定要创建的文件系统对应的设备文件名
blocks:指定文件系统的磁盘块数
-V:详细显示模式
fs-options:传递给具体的文件系统的参数
#格式化镜像文件为 ext4
sudo mkfs -t ext4 ./fdimage.img
2.1.3 df命令
Linux df(disk free)命令用于显示目前在 Linux 系统上的文件系统磁盘使用情况统计
df [OPTION]... [FILE]...
- -a, --all 包含所有的具有 0 Blocks 的文件系统
- -h, --human-readable 使用人类可读的格式(预设值是不加这个选项的...)
- -H, --si 很像 -h, 但是用 1000 为单位而不是用 1024
- -t, --type=TYPE 限制列出文件系统的 TYPE
- -T, --print-type 显示文件系统的形式
2.1.4 mount命令
mount 命令用于加载文件系统到指定的加载点。此命令的也常用于挂载光盘,使得可以访问光盘中的数据,因为将光盘插入光驱中后,Linux 并不会自动挂载,必须使用 Linux mount 命令来手动完成挂载
Linux 系统下不同目录可以挂载不同分区和磁盘设备,其目录和磁盘分区是分离的,可以自由组合(通过挂载)。不同的目录数据可以跨越不同的磁盘分区或者不同的磁盘设备。挂载的实质是为磁盘添加入口(挂载点)
mount [-l]
mount [-t vfstype] [-o options] device dir
- -l:显示已加载的文件系统列表
- -t: 加载文件系统类型支持常见系统类型的 ext3,ext4,iso9660,tmpfs,xfs 等,大部分情况可以不指定,mount 可以自己识别
- -o options 主要用来描述设备或档案的挂接方式
- loop:用来把一个文件当成硬盘分区挂接上系统
- ro:采用只读方式挂接设备
- rw:采用读写方式挂接设备
- device: 要挂接(mount)的设备
- dir: 挂载点的目录
#将镜像挂载到/mnt/testext4 下面,需要确保挂载点即目录存在
mkdir -p /mnt/testext4
mount ./fdimage.img /mnt/testext4
2.1.5 unshare命令
unshare 主要能力是使用与父程序不共享的名称空间运行程序
unshare [options] program [arguments]
参数 | 含义 |
-i,--ipc | 不共享IPC空间 |
-m,--mount | 不共享Mount空间 |
-n,--net | 不共享Net空间 |
-p,--pid | 不共享PID空间 |
-u,--uts | 不共享UTS空间 |
-U,--user | 不共享用户 |
-V,--version | 版本查看 |
--fork | 执行unshare的进程fork一个新的子进程,在子进程中执行unshare传入的参数 |
--mount-proc | 执行子进程前,先将proc优先挂载过去 |
2.1.6 pidstat命令
pidstat 是 sysstat 的一个命令,用于监控全部或指定进程的 CPU、内存、线程、设备IO 等系统资源的占用情况。 pidstat 第一次采样显示自系统启动开始的各项统计信息,后续采样将显示自上次运行命令后的统计信息。用户可以通过指定统计的次数和时间来获得所需的统计信息
pidstat [ 选项 ] [ <时间间隔> ] [ <次数> ]
- -u:默认参数,显示各进程的 CPU 使用统计
- -r:显示各进程的内存使用统计
- -d:显示各进程的 IO 使用情况
- -p:指定进程号,ALL 表示所有进程
- -C:指定命令
- -l:显示命令名和所有参数
2.1.7 stress命令
stress 是 Linux 的一个压力测试工具,可以对 CPU、Memory、IO、磁盘进行压力测试
stress [OPTION [ARG]]
- -c, --cpu N:产生 N 个进程,每个进程都循环调用 sqrt 函数产生 CPU 压力
- -i, --io N:产生 N 个进程,每个进程循环调用 sync 将内存缓冲区内容写到磁盘上,产生 IO 压力。通过系统调用 sync 刷新内存缓冲区数据到磁盘中,以确保同步。如果缓冲区内数据较少,写到磁盘中的数据也较少,不会产生 IO 压力。在 SSD 磁盘环境中尤为明显,很可能 iowait 总是 0,却因为大量调用系统调用 sync,导致系统 CPU 使用率 sys 升高
- -m, --vm N:产生 N 个进程,每个进程循环调用 malloc/free 函数分配和释放内存。
- --vm-bytes B:指定分配内存的大小
- --vm-keep:一直占用内存,区别于不断的释放和重新分配(默认是不断释放并重新分配内存)
- -d, --hdd N:产生 N 个不断执行 write 和 unlink 函数的进程(创建文件,写入内容,删除文件)
- --hdd-bytes B:指定文件大小
- -t, --timeout N:在 N 秒后结束程序
- -q, --quiet:程序在运行的过程中不输出信息
2.2 NameSpace
2.2.1 概念
namespace 是 Linux 内核用来隔离内核资源的方式。通过 namespace 可以让一些进程只能看到与自身相关的一部分资源,而另外一些进程也只能看到与它们自身相关的资源,这两部分进程感觉不到对方的存在。具体的实现方式是把一个或多个进程的相关资源指定在同一个 namespace 中
Linux 提供了多个API来操作namespace:clone()、setns()和unshare()函数。为了确定隔离的到底是哪项namespace,在使用这些API时,通常需要指定一些调用参数。若要同时隔离多个namespace,可以使用 | (按位或)组合这些参数
namespace | 系统调用参数 | 被隔离的全局系统资源 | 引入内核版本 |
---|---|---|---|
UTS | CLONE_NEWUTS | 主机名和域名 | 2.6.19 |
IPC | CLONE_NEWIPC | 信号量、消息队列和共享内存 - 进程间通信 | 2.6.19 |
PID | CLONE_NEWPID | 进程编号 | 2.6.24 |
Network | CLONE_NEWNET | 网络设备、网络栈、端口等 | 2.6.29 |
Mount | CLONE_NEWNS | 文件系统挂载点 | 2.4.19 |
User | CLONE_NEWUSER | 用户和用户组 | 3.8 |
- UTS:每个容器能看到自己的 hostname,拥有独立的主机名和域名
- IPC:同一个IPC namespace的进程之间可通讯,不同的IPC namespace之间不能通信
- PID:每个PID namespace中的进程可以有其独立的PID,每个容器可以有PID为1的root进程
- Network:每个容器有其独立的网络设备,IP地址,IP路由表,/proc/net目录,端口号
- Mount:每个容器能看到不同的文件系统层次结构
- User:每个 container 可以有不同的 user 和 group id
如何隔离两个进程?
- 进程与进程之间需要隔离,所以需要 PID 隔离
- 容器 A 进程不能读取容器 B 进程通讯内容需要隔离信号量等,所以需要 IPC 隔离
- 容器 A 进程不能读取容器 B 进程的文件,所以需要 Mount 隔离
- 容器 A 进程不能读取容器 B 进程的 socket,所以需要网络隔离、主机隔离
- Docker 允许用户在主机和容器间共享文件夹,同时不需要限制容器的访问权限, 这就容易让容器突破资源限制。需要借助用户空间来完成用户之间的隔离
2.2.2 实战案例
PID隔离
在主机上执行 ps -ef,可以看到进程列表如下,其中启动进程 PID 1 为 init 进程
打开另外一个 shell ,执行下面命令创建一个 bash 进程,并且新建一个 PID Namespace:
unshare --fork --pid --mount-proc /bin/bash
- --fork 新建了一个 bash 进程,是因为若不建新进程,新的 namespace 会用 unshare 的 PID 作为新的空间的父进程,而这个 unshare 进程并不在新的 namespace 中,所以会报个错 Cannot allocate memory
- --pid 表示进程隔离的是 pid,而其他命名空间没有隔离
- mount-proc 是因为 Linux 下的每个进程都有一个对应的 /proc/PID 目录,该目录包含了大量的有关当前进程的信息。 对一个 PID namespace 而言, /proc 目录只包含当前 namespace 和其所有子孙后代 namespace 里的进程的信息。创建一个新的 PID namespace 后,若想让子进程中的 top、 ps 等依赖 /proc 文件系统的命令工作,还需要挂载 /proc 文件系统。而文件系统隔离是 mount namespace 管理的,所以 linux特意提供了一个选项--mount-proc 来解决这个问题。若不带这个选项,看到的进程还是系统的进程信息
执行 ps -ef 查看进程信息,可以看到此时进程空间内的内容已经变换,而且启动进程也变成了新创建的 bash 进程。说明已经看不到主机上的进程空间了,进程空间发生了隔离
执行exit退出进程
Mount隔离
打开第一个 shell 窗口A,执行命令df -h,查看主机默认命名空间的磁盘挂载情况
打开一个新的 shell 窗口 B,执行 Mount 隔离命令
# --mount 表示要隔离 Mount 命名空间了
# --fork 表示新建进程
unshare --mount --fork /bin/bash
mkdir -p /data/tmpmount
在窗口 B 中添加新的磁盘挂载
dd if=/dev/zero of=fdimage.img bs=8k count=10240
mkfs -t ext4 ./fdimage.img
mount ./fdimage.img /data/tmpmount
在窗口 B 挂载的磁盘中添加文件
echo "Hello world" > /data/tmpmount/hello.txt
查看窗口 B 中的磁盘挂载信息
查看窗口 A 中的磁盘挂载信息
查看窗口 B 中的文件信息
查看窗口 A 中的文件信息,可以看到窗口 B 中新建的文件和磁盘挂载在主机的窗口中并没有,说明实现了文件系统隔离
窗口 B 执行 exit 退出
2.3 cgroups
2.3.1 概念
cgroups(Control Groups) 是 linux 内核提供的一种机制,这种机制可以根据需求把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组内,从而为系统资源管理提供一个统一的框架。即cgroups可以限制、记录任务组所使用的物理资源。本质上来说,cgroups 是内核附加在程序上的一系列钩子(hook),通过程序运行时对资源的调度触发相应的钩子以达到资源追踪和限制的目的
用途
- Resource limitation: 限制资源使用,如:内存使用上限/cpu 的使用限制
- Prioritization: 优先级控制,例:CPU 利用/磁盘 IO 吞吐
- Accounting: 一些审计或一些统计
- Control: 挂起进程/恢复执行进程
cgroups可以控制的子系统
blkio | 对块设备的IO进行设置 |
cpu | 对CPU时间片进行限制 |
apuacct | 生成 cgroup 中的任务占用 CPU 资源的报告,与 cpu 挂载在同一目录 |
cpuset | 给 cgroup 中的任务分配独立的 CPU(多处理器系统) 和内存节点 |
devices | 限制设备文件的创建以及对设备文件的读写 |
freezer | 暂停/恢复 cgroup 中的任务 |
memory | 对 cgroup 中的任务的可用内存进行限制,并自动生成资源占用报告 |
perf_event | 允许 perf 观测 cgroup 中的 task |
net_cls | cgroup 中的任务创建的数据报文的类别标识符,这让 Linux 流量控制器(tc 指令)可以识别来自特定 cgroup 任务的数据包,并进行网络限制 |
hugetlb | 限制使用的内存页数量 |
pids | 限制任务的数量 |
rdma | 限制RDMA 资源(Remote Direct Memory Access,远程直接数据存取) |
2.3.2 实战案例
查看cgroups信息
cgroups版本查看
cgroups子系统查看
cgroups挂载信息查看(默认存储位置为 /sys/fs/cgroup)
查看一个进程上的cgroup限制
以当前 shell 进程为例,查看进程的 cgroup
如: cpu 在 user.slice,可以找到这个目录,里面有对 init 进程的详细限制信息
使用cgroups对内存进行控制
创建内存的 cgroup,先进入到 cgroup 的内存控制目录/sys/fs/cgroup/memory,创建目录test_memory
内存限制文件已经自动在 test_memory 中创建完成了,cgroups 文件系统会在创建文件目录的时候自动创建相应的配置文件
配置 cgroup 的策略为最大使用 20M 内存
启动 1 个消耗内存的进程,每个进程占用 50M 内存
打开一个新的 shell 窗口 B 窗口,使用 pidstat 查看状态
打开一个新的 shell C 窗口,将进程 ID 移动到新建的 cgroup 策略
进程无法申请到足够内存退出
使用cgroups对CPU进行控制
创建内存的 cgroup,先进入到 cgroup 的内存控制目录 /sys/fs/cgroup/cpu,创建目录 test_cpu,可以看到系统会自动创建 cgroup 的 cpu 策略
打开新的 shell 窗口 B 窗口,使用 stress 模拟一个任务,压测CPU
可以看到 cpu 的使用率为 100%
设置cgroup的cpu使用率为30%,cpu 使用率的计算公式:cfs_quota_us/cfs_period_us
- cfs_period_us:cfs_period_us 表示一个 cpu 带宽,单位为微秒。系统总 CPU 带宽 ,默认值 100000
- cfs_quota_us:cfs_quota_us 表示cgroup可以使用的cpu的带宽,单位为微秒。cfs_quota_us为-1,表示使用的CPU不受cgroup限制。cfs_quota_us 的最小值为1ms(1000),最大值为1s
所以将 cfs_quota_us 的值设置为30000 ,从理论上讲就可以限制test_cpu控制的进程的cpu利用率最多是30%
进程的PID为27499,将该进程放到tasks文件进行控制
可以发现监控的 cpu 的使用率由 100%降低为 30%
2.4 LXC
2.4.1 概念
LXC(LinuX Containers)Linux 容器,一种操作系统层虚拟化技术,为 Linux 内核容器功能的一个用户空间接口。将应用软件系统打包成一个软件容器(Container),内含应用软件本身的代码,以及所需要的操作系统核心和库。透过统一的名字空间和共享 API 来分配不同软件容器的可用硬件资源,创造出应用程序的独立沙箱运行环境, 使得 Linux 用户可以容易的创建和管理系统或应用容器
LXC 是最早一批真正把完整的容器技术用一组简易使用的工具和模板来极大的简化了容器技术使用的一个方案。LXC 虽然极大的简化了容器技术的使用,但比起直接通过内核调用来使用容器技术, 其复杂程度其实并没有多大降低,因为必须要学会 LXC 的一组命令工具,且由于内核的创建都是通过命令来实现的,通过批量命令实现数据迁移并不容易。其隔离性也没有虚拟机那么强大。 后来就出现了 docker,所以从一定程度上来说,docker 就是 LXC 的增强版
2.4.2 常用命令
命令 | 功能 | 格式 |
---|---|---|
lxc-checkconfig
| 检查系统环境是否满足容器使用要求 | lxc-checkconfig |
lxc-create
| 创建LXC容器 |
lxc-create -n NAME -t TEMPLATE_NAME [-- template-options]
|
lxc-start | 启动容器 |
lxc-start -n NAME -d
|
lxc-ls | 列出所有容器,-f表示打印常用的信息 | lxc-ls -f |
lxc-info | 查看容器的相关信息 | lxc-info -n NAME |
lxc-attach | 进入容器执行命令 | lxc-attach --name=NAME[-- COMMAND] |
lxc-stop | 停止容器 | lxc-stop -n NAME |
lxc-destory | 删除处于停机状态的容器 | lxc-destory -n NAME |
三、认识Docker
3.1 Docker的本质
Docker本质其实是LXC之类的增强版,其本身不是容器,而是容器的易用工具。容器是 linux 内核中的技术,Docker只是将这种技术在使用上简易普及了。Docker 在早期的版本其核心就是LXC的二次封装发行版
Docker 是基于 Go 语言实现的一个开源项目,其主要目标是“Build,Ship and Run Any APP,Anywhere”,即通过对组件的封装、分发、部署、运行等生命周期的管理,使得用户的应用及其运行环境能够做到“一次封装,到处运行”。
早期 Docker 利用 LXC 做容器管理引擎,但是在创建容器时,不再使用模板去安装生成,而是通过镜像技术(把一个操作系统用户空间所需要使用到的组件事先编排好,并整体打包成一个文件,image文件),镜像文件集中放在一个仓库中。当需要创建容器时,Docker调用LXC的工具lxc-create,但不再通过LXC的模板去安装,而是连接到镜像服务器上下载匹配的镜像文件,而后基于镜像启动容器。所以,Docker极大的简化了容器的使用难度。以后创建启动容器,只需要一个命令,docker-run,docker-stop 就可以启动停止一个容器了
Docker早期是基于LXC容器管理引擎实现,成熟之后,Docker 自建了一个容器引擎libcontainer,后来CNCF的介入,Docker又研发了一个工业化标准的容器引擎runC,目前所使用的新版Docker,所使用的容器引擎就是RunC
3.2 Docker与虚拟机的区别
传统虚拟机 | Docker容器 | |
---|---|---|
磁盘占用 | 几GB到几十GB左右 | 几十MB到几百MB左右 |
CPU内存占用 | 虚拟操作系统非常占用CPU和内存,需要通过虚拟层调用占用率高 | Docker引擎占用资源极低,直接作用于硬件资源占用少 |
启动速度 | (从开机到运行项目)几分钟 | (从开启容器到运行项目)几秒 |
安装管理 | 需要专门的运维技术 | 安装、管理方便 |
应用部署 | 手动部署、速度慢 | 体系化部署、可以自动化、速度快 |
隔离性 | 系统级别 | 进程级别 |
封装程度 | 打包整个操作系统 | 打包项目代码和依赖信息 |
Docke 为什么比虚拟机资源利用率高,启动快?
docker有比虚拟机更少的抽象层。docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的是实际物理机的硬件资源。因此在cpu、内存利用率上docker将会在效率上有明显的优势。docker利用的是宿主机的内核,而不需要Guest OS,节省了Guest OS占用的资源
docker不需要Guest OS,创建一个容器时,不需要和虚拟机一样重新加载一个操作系统内核。从而避免引寻、加载操作系统内核返回时耗时耗资源的过程。当新建一个虚拟机时,虚拟机软件需要加载Guest OS,返回新建过程是分钟级别的。而新建一个docker容器只需要几秒钟
3.3 Docker和JVM虚拟化的区别
JVM | Docker容器 | |
---|---|---|
性能 | JVM需要占用一定的CPU和内存 | 基本没有损失 |
虚拟层面 | 基于JVM虚拟机,更加上层 | 基于操作系统,更加通用 |
代码无关性 | 一个特定的代码执行平台,在运行时才存在,只能支持特定代码的运行,并且必须是在JVM进程内 | 模拟一整个操作系统,是静态存在的,可以支撑任何相同平台的应用程序 |
主机隔离性 | 不隔离主机 | 通过命名空间进行隔离 |
3.4 Docker架构
Docker 使用客户端-服务器(C/S) 架构模式,使用远程API来管理和创建Docker容器。Docker 容器通过Docker 镜像来创建
- Docker仓库(Registry)
Docker仓库保存镜像,可以理解为代码控制中的代码仓库。Docker Hub提供了庞大的镜像集合
- Docker daemon
Docker daemon是服务器组件,是Docker最核心的后台进程,也将其称为守护进程
- Docker 客户端(Client)
Docker 客户端通过命令行或者其他工具使用 Docker API 与 Docker 的守护进程通信
- Docker主机(Host)
一个物理或者虚拟的机器用于执行 Docker 守护进程和容器
- Docker镜像(Images)
Docker 镜像是用于创建 Docker 容器的模板
- Docker 容器(Container)
容器是独立运行的一个或一组应用