Docker简介
- 什么是虚拟化、容器化
- 为什么要虚拟化、容器化?
- 虚拟化实现
什么是虚拟化、容器化
物理机:
实际的服务器或者计算机。相对于虚拟机而言的对实体计算机的称呼。物理机提供给虚拟机以硬件环境,有时也称为“寄主”或“宿主”。
虚拟化:
是指通过虚拟化技术将一台计算机虚拟为多台逻辑计算机。在一台计算机上同时运行多个逻辑计算机,每个逻辑计算机可运行不同的操作系统,并且应用程序都可以在相互独立的空间内运行而互不影响,从而显著提高计算机的工作效率。
容器化:
容器化是一种虚拟化技术,又称操作系统层虚拟化(Operating system level virtualization),这种技术将操作系统内核虚拟化,可以允许用户空间软件实例(instances)被分割成几个独立的单元,在内核中运行,而不是只有一个单一实例运行。这个软件实例,也被称为是一个容器(containers)。对每个实例的拥有者与用户来说,他们使用的服务器程序,看起来就像是自己专用的。容器技术是虚拟化的一种。docker 是现今容器技术的事实标准。
为什么要虚拟化、容器化?
- 提高资源利用率
将利用率较低的服务器资源进行整合,用更少的硬件资源运行更多业务,降低IT支出和运维管理成本。
- 环境标准化
一次构建,随处执行。实现执行环境的标准化发布,部署和运维。开发过程中一个常见的问题就是环境一致性问题。由于开发环境、测试环境、生产环境不一致,导致有些bug并未在开发过程中发现。而Docker的镜像提供了除内核外完整的运行时环境,确保了应用运行环境的一致性,从而不会再出现“这段代码在我机器上没问题啊”这类问题。
- 资源弹性伸缩
根据业务情况,动态调整计算、存储、网络等硬件及软件资源。比如遇到双 11 了,把服务扩容 100 个,双 11 过去了, 把扩容的 100 个收回去。
- 差异化环境提供
同时提供多套差异化的执行环境,限制环境使用资源。比如我的服务一个以来 Ubuntu 操作系统,一个服务依赖 CentOS 操作系统,但是没有预算购买两个物理机,这个时候容器化就能很好的提供多种不同的环境.
- 沙箱安全
为避免不安全或不稳定软件对系统安全性、稳定性造成影响,可使用虚拟化技术构建虚拟执行环境。比如我在容器里面执行 rm -rf /* 不会把整个服务器搞死,也不影响其他人部署的程序使用。
- 容器对比于虚拟机跟轻量,启动更快
传统的虚拟机技术启动应用服务往往需要数分钟,而 Docker 容器应用,由于直接运行于宿主内核,无需启动完整的操作系统,因此可以做到秒级、甚至毫秒级的启动时间。大大的节约了开发、测试、部署的时间。docker 不需要虚拟内核,所以启动可以更快,相当于 windows 的开机时间省去了。
- 维护和扩展容易
Docker 使用的分层存储以及镜像的技术,使得应用重复部分的复用更为容易,也使得应用的维护更新更加简单,基于基础镜像进一步扩展镜像也变得非常简单。此外,Docker 团队同各个开源项目团队一起维护了一大批高质量的 官方镜像,既可以直接在生产环境使用,又可以作为基础进一步定制,大大的降低了应用服务的镜像制作成本。比如 docker hub 提供了很多镜像,各个系统的一个命令就可以拿到了,研发也可以自己定制镜像分享给各个产品。
虚拟化实现
应用程序执行环境分层:
硬件层:提供硬件抽象,包括指令集架构、硬件设备及硬件访问接口
操作系统层 :提供系统调用接口,管理硬件资源
程序库层:提供数据结构定义及函数调用接口
虚拟化常见类别:
- 虚拟机
存在于硬件层和操作系统层间的虚拟化技术。虚拟机通过“伪造”一个硬件抽象接口,将一个操作系统以及操作系统层以上的层嫁接到硬件上,实现和真实物理机几乎一样的功能。比如我们在一台 Windows 系统的电脑上使用 Android 虚拟机,就能够用这台电脑打开 Android 系统上的应用。
- 容器
存在于操作系统层和函数库层之间的虚拟化技术。容器通过“伪造”操作系统的接口,将函数库层以上的功能置于操作系统上。以 Docker 为例,其就是一个基于 Linux 操作系统的 Namespace 和 Cgroup 功能实现的隔离容器,可以模拟操作系统的功能。简单来说,如果虚拟机是把整个操作系统封装隔离,从而实现跨平台应用的话,那么容器则是把一个个应用单独封装隔离,从而实现跨平台应用。所以容器体积比虚拟机小很多,理论上占用资源更少。容器化就是应用程序级别的虚拟化技术。容器提供了将应用程序的代码、运行时、系统工具、系统库和配置打包到一个实例中的标准方法。容器共享一个内核(操作系统),它安装在硬件上。
常见虚拟化实现:
主机虚拟化的原理是通过在物理服务器上安装一个虚拟化层来实现。这个虚拟化层可以在物理服务器和客户操作系统之间建立虚拟机,使得它们可以独立运行。从软件框架的角度上,根据虚拟化层是直接位于硬件之上还是在一个宿主操作系统之上,将虚拟化划分为 Type1 和 Type2.Type1 类的 Hypervisor(Hypervisor 是一种系统软件,它充当计算机硬件和虚拟机之间的中介,负责有效地分配和利用由各个虚拟机使用的硬件资源,这些虚拟机在物理主机上单独工作,因此,Hypervisor 也称为虚拟机管理器。)直接运行在硬件之上,没有宿主机操作系统,Hypervisor 直接控制硬件资源和客户机。典型框架为 Xen、VmwareESX
ype2 类的 Hypervisor 运行在一个宿主机操作系统之上(Vmware Workstation)或者系统里面,Hypervisor 作为宿主机操作系统中的一个应用程序,客户机就是在宿主机操作系统上的一个进程。
容器虚拟化实现原理:
容器虚拟化,有别于主机虚拟化,是操作系统层的虚拟化。通过 namespace 进行各程序的隔离,加上 cgroups 进行资源的控制,以此来进行虚拟化。
什么是namespace?
namespace 是 Linux 内核用来隔离内核资源的方式。通过 namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程根本就感觉不到对方的存在。具体的实现方式是把一个或多个进程的相关资源指定在同一个 namespace 中。Linux namespaces 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前namespace 里的进程,对其他 namespace 中的进程没有影响。
空间隔离实战:
- dd命令
语法:
dd option
功能: 可以从标准输入或文件中读取数据,根据指定格式来转换数据,在输出到文件/设备/标准输出。
参数: if=filename: 输入文件名,默认为标准输出
of=filename: 输出文件名,默认为标准输出
ibs=bytes: 一次读入bytes个字节,即指定一个块大小为bytes字节;
obs=bytes: 一次写入bytes个字节,即指定一个块的大小为bytes个字节;
bs: 同时设置读入/输出的块大小为 bytes 个字节。
count=blocks:仅拷贝 blocks 个块,块大小等于 ibs 指定的字节数。
cbs=bytes:一次转换 bytes 个字节,即指定转换缓冲区大小。
skip=blocks:从输入文件开头跳过 blocks 个块后再开始复制。
seek=blocks:从输出文件开头跳过 blocks 个块后再开始复制。
conv=<关键字>,关键字可以有以下 11 种:
▪ conversion:用指定的参数转换文件。
▪ ascii:转换 ebcdic 为 ascii
▪ ebcdic:转换 ascii 为 ebcdic
▪ ibm:转换 ascii 为 alternate ebcdic
▪ block:把每一行转换为长度为 cbs,不足部分用空格填充
▪ unblock:使每一行的长度都为 cbs,不足部分用空格填充
▪ lcase:把大写字符转换为小写字符
▪ ucase:把小写字符转换为大写字符
▪ swap:交换输入的每对字节
▪ noerror:出错时不停止
▪ notrunc:不截短输出文件
▪ sync:将每个输入块填充到 ibs 个字节,不足部分用空(NUL)字符补齐。
实战案例:
- 从文件/dev/zero 每次读取8k的数据,总共读取10240次到data.txt
- 将t1.txt文件中的数据,全部转换为大写,写入t2.txt文件中
- mkfs命令
语法:
mkfs [-V] [-t fileSystem] [fs-options] filesys [blocks]
功能:用于在设备上创建 Linux 文件系统,俗称格式化,比如我们使用 U 盘的时候可以格式化;
参数:-t fileSystem: 指定要建立何种文件系统: 如ext3、ext4
filesys: 要被初始化的设备文件名
blocks: 指定文件系统的磁盘块数
-V: 详细显示模式
fs-options: 传递给具体的文件系统的参数;
实战:
- 将data.txt文件格式化为ext4文件系统:
- df命令
语法:
df [options]
功能:Linux df(英文全拼:disk free) 命令用于显示目前在 Linux 系统上的文件系统磁盘使
用情况统计。
参数:
○ -a, --all: 包含所有的具有 0 Blocks 的文件系统
○ -h, --human-readable: 使用人类可读的格式(预设值是不加这个选项的…)
○ -H, --si :很像 -h, 但是用 1000 为单位而不是用 1024
○ -t, --type=TYPE :限制列出文件系统的 TYPE
○ -T, --print-type: 显示文件系统的形式
实战:
- mount命令
语法:
mount [-t vfstype] [-o options] device dir
功能: mount 命令用于加载文件系统到指定的加载点。此命令的也常用于挂载光盘,使我们可以访问光盘中的数据,因为你将光盘插入光驱中,Linux 并不会自动挂载,必须使用Linux mount 命令来手动完成挂载。Linux 系统下不同目录可以挂载不同分区和磁盘设备,它的目录和磁盘分区是分离的,可以自由组合(通过挂载)不同的目录数据可以跨越不同的磁盘分区或者不同的磁盘设备。挂载的实质是为磁盘添加入口(挂载点)。
参数:
-l:显示已加载的文件系统列表;
-t: 加载文件系统类型支持常见系统类型的 ext3,ext4,iso9660,tmpfs,xfs 等,大部分情况
可以不指定,mount 可以自己识别
-o options 主要用来描述设备或档案的挂接方式。
loop:用来把一个文件当成硬盘分区挂接上系统
ro:采用只读方式挂接设备
rw:采用读写方式挂接设备
device: 要挂接(mount)的设备。
dir: 挂载点的目录
实战:
- 将/home/ikun/Data/mount/ 当作data.txt文件的挂载点
- unshare命令
语法:
unshare [options] program [arguments]
功能:创建一个新的namespace,然后在这个新的namespace中启动program程序;
参数:
-i,–ipc: 不共享IPC的空间
-m,–mount: 不共享mount的空间
-n,–net: 不共享Net的空间
-p,–pid: 不共享PID的空间;
-u,–uts: 不共享UTS的空间
-U,–user: 不共享用户的空间
-V,–version: 版本查看
–fork: 配合-p选项使用,unshare这个父进程创建一个新的子进程,然后将这个子进程放入新的namespace中,由这个子进程在新的namespace中启动程序;
–mount-proc: 因为 Linux 下的每个进程都有一个对应的 /proc/PID 目录,该目录包含了大量的有关当前进程的信息。 对一个 PID namespace 而言,/proc 目录只包含当前namespace 和它所有子孙后代 namespace 里的进程的信息。创建一个新的 PIDnamespace 后,如果想让子进程中的 top、ps 等依赖 /proc 文件系统的命令工作,还需要挂载 /proc 文件系统。而文件系统隔离是 mount namespace 管理的,所以 linux特意提供了一个选项–mount-proc 来解决这个问题。如果不带这个我们看到的进程还是系统的进程信息
实战
- PID隔离:
经过实验发现,我们直接使用unshare命令进行PID隔离出现了报错,出现这个的主要原因是因为:在unshare这个命令创建出新的PID namespace过后,unshare会去启动这个/bin/bash程序,于是/bin/bash会将unshare作为自己的父进程,记录它的pid,但是由于unshare和/bin/bash处于两个不同的PID namespace 这显然是不成立的,因此出现了报错;
为了解决这个错误,我们可以带上–fork选项:
很幸运,这次没有出现错误,为了验证是否出现了PID隔离,我们使用ps命令查看以下两次的bash的PID信息:
我们通过使用ps命令发现,在两个PID namesoace 中的进程信息都是一样的,这是为什么?
在一个新的PID namespace 中进程信息不就应该只有新程序自己和它的后代进程吗?
如果想让子进程中的 top、ps 等依赖 /proc 文件系统的命令工作,还需要挂载 /proc 文件系统。而文件系统隔离是 mount namespace 管理的,所以 linux特意提供了一个选项–mount-proc 来解决这个问题。如果不带这个我们看到的进程还是系统的进程信息。
因此我们加上–mount-proc选项:
- 主机名隔离: