Docker
Docker原理是什么
Docker 技术使用 Linux 内核和内核功能(例如 Cgroups 和 namespaces)来分隔进程,以便各进程相互独立运行。这种独立性正是采用容器的目的所在;它可以独立运行多种进程、多个应用,更加充分地发挥基础设施的作用,同时保持各个独立系统的安全性。
虚拟化技术
指令集架构(Instruction Set Architecture,ISA)是计算机体系结构中与程序设计有关的部分,包含了基本数据类型,指令集,寄存器,寻址模式,存储体系,中断,异常处理以及外部 I/O。指令集架构包含一系列的 Opcode 操作码(即通常所说的机器语言),以及由特定处理器执行的基本命令。
应用二进制接口(Application Binary Interface,ABI)是应用程序与操作系统之间或其他依赖库之间的低级接口。ABI 涵盖了各种底层细节,如数据类型的宽度大小、对象的布局、接口调用约定等等。
- 指令集虚拟化(ISA Level Virtualization)。通过软件来模拟不同 ISA 架构的处理器工作过程,将虚拟机发出的指令转换为符合本机 ISA 的指令,代表为QEMU和Bochs。指令集虚拟化就是仿真,能提供了几乎完全不受局限的兼容性,甚至能做到直接在 Web 浏览器上运行完整操作系统这种令人惊讶的效果,但由于每条指令都要由软件来转换和模拟,它也是性能损失最大的虚拟化技术。
- 硬件抽象层虚拟化(Hardware Abstraction Level Virtualization)。以软件或者直接通过硬件来模拟处理器、芯片组、内存、磁盘控制器、显卡等设备的工作过程。既可以使用纯软件的二进制翻译来模拟虚拟设备,也可以由硬件的Intel VT-d、AMD-Vi这类虚拟化技术,将某个物理设备直通(Passthrough)到虚拟机中使用,代表为VMware ESXi和Hyper-V。如果没有预设语境,一般人们所说的“虚拟机”就是指这一类虚拟化技术。
- 操作系统层虚拟化(OS Level Virtualization)。无论是指令集虚拟化还是硬件抽象层虚拟化,都会运行一套完全真实的操作系统来解决 ABI 兼容性和环境兼容性问题,虽然 ISA 兼容性是虚拟出来的,但 ABI 兼容性和环境兼容性却是真实存在的。而操作系统层虚拟化则不会提供真实的操作系统,而是采用隔离手段,使得不同进程拥有独立的系统资源和资源配额,看起来仿佛是独享了整个操作系统一般,其实系统的内核仍然是被不同进程所共享的。
容器化仅仅是虚拟化的一个子集,只能提供操作系统内核以上的部分 ABI 兼容性与完整的环境兼容性。容器化牺牲了一定的隔离性与兼容性,换来的是比前两种虚拟化更高的启动速度、运行性能和更低的执行负担。 - 运行库虚拟化(Library Level Virtualization)。与操作系统虚拟化采用隔离手段来模拟系统不同,运行库虚拟化选择使用软件翻译的方法来模拟系统,它以一个独立进程来代替操作系统内核来提供目标软件运行所需的全部能力,这种虚拟化方法获得的 ABI 兼容性高低,取决于软件是否能足够准确和全面地完成翻译工作,其代表为WINE和WSL1。
- 语言层虚拟化(Programming Language Level Virtualization)。由虚拟机将高级语言生成的中间代码转换为目标机器可以直接执行的指令,代表为 Java 的 JVM 和.NET 的 CLR。本质上这种虚拟化并不直接解决任何 ABI 兼容性和环境兼容性问题。
基本概念
镜像
Docker 镜像 是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像 不包含 任何动态数据,其内容在构建之后也不会被改变。
容器
镜像(Image
)和容器(Container
)的关系,就像是面向对象程序设计中的 类 和 实例
一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。容器内的进程是运行在一个隔离的环境里,使用起来,就好像是在一个独立于宿主的系统下操作一样。
数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此,使用数据卷后,容器删除或者重新运行之后,数据却不会丢失。
仓库
一个 Docker Registry 中可以包含多个 仓库(Repository);每个仓库可以包含多个 标签(Tag);每个标签对应一个镜像。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本。我们可以通过 <仓库名>:<标签>
的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest
作为默认标签。
常用命令
使用镜像
获取镜像
从 Docker 镜像仓库获取镜像的命令是 docker pull
。其命令格式为:
1 | docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签] |
运行
1 | $ docker run -it --rm ubuntu:18.04 bash |
-it
:这是两个参数,一个是 -i
:交互式操作,一个是 -t
终端。我们这里打算进入 bash
执行一些命令并查看返回结果,因此我们需要交互式终端。
--rm
:这个参数是说容器退出后随之将其删除。默认情况下,为了排障需求,退出的容器并不会立即删除,除非手动 docker rm
。我们这里只是随便执行个命令,看看结果,不需要排障和保留结果,因此使用 --rm
可以避免浪费空间。
ubuntu:18.04
:这是指用 ubuntu:18.04
镜像为基础来启动容器。
bash
:放在镜像名后的是 命令,这里我们希望有个交互式 Shell,因此用的是 bash
。
列出镜像
docker image ls
命令
包含了 仓库名
、标签
、镜像 ID
、创建时间
以及 所占用的空间
。
通过 docker system df
命令来便捷的查看镜像、容器、数据卷所占用的空间。
中间层镜像
1 | $ docker image ls -a |
列出部分镜像
根据仓库名列出镜像
1 | $ docker image ls ubuntu |
删除本地镜像
docker image rm
命令
操作容器
启动
新建并启动
docker run
1 | docker run ubuntu:18.04 /bin/echo 'Hello world' |
当利用 docker run
来创建容器时,Docker 在后台运行的标准操作包括:
- 检查本地是否存在指定的镜像,不存在就从 registry 下载
- 利用镜像创建并启动一个容器
- 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个 ip 地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
启动已终止容器
利用 docker container start
命令,直接将一个已经终止(exited
)的容器启动运行
利用 ps
或 top
来查看进程信息。
守护态运行
需要让 Docker 在后台运行而不是直接把执行命令的结果输出在当前宿主机下。此时,可以通过添加 -d
参数来实现。
容器是否会长久运行,是和 docker run
指定的命令有关,和 -d
参数无关。
终止
可以使用 docker container stop
来终止一个运行中的容器。
此外,当 Docker 容器中指定的应用终结时,容器也自动终止。
进入容器
docker exec
后边可以跟多个参数, -i
-t
参数可以进入容器bash
删除
可以使用 docker container rm
来删除一个处于终止状态的容器。
用 docker container ls -a
命令可以查看所有已经创建的包括终止状态的容器
用可以清理掉所有处于终止状态的容器docker container prune
数据管理
数据卷
数据卷
是一个可供一个或多个容器使用的特殊目录,它绕过 UFS,可以提供很多有用的特性:
数据卷
可以在容器之间共享和重用- 对
数据卷
的修改会立马生效 - 对
数据卷
的更新,不会影响镜像 数据卷
默认会一直存在,即使容器被删除
创建一个数据卷
1 | docker volume create my-vol |
查看所有的 数据卷
1 | docker volume ls |
在主机里使用以下命令可以查看指定 数据卷
的信息
1 | docker volume inspect my-vol |
启动一个挂载数据卷的容器
在用 docker run
命令的时候,使用 --mount
标记来将 数据卷
挂载到容器里。在一次 docker run
中可以挂载多个 数据卷
。
下面创建一个名为 web
的容器,并加载一个 数据卷
到容器的 /usr/share/nginx/html
目录。
1 | docker run -d -P \ |
删除数据卷
1 | docker volume rm my-vol |
数据卷
是被设计用来持久化数据的,它的生命周期独立于容器,Docker 不会在容器被删除后自动删除 数据卷
,并且也不存在垃圾回收这样的机制来处理没有任何容器引用的 数据卷
。如果需要在删除容器的同时移除数据卷。可以在删除容器的时候使用 docker rm -v
这个命令。
无主的数据卷可能会占据很多空间,要清理请使用以下命令
1 | docker volume prune |
挂载主机目录
挂载一个主机目录作为数据卷
使用 --mount
标记可以指定挂载一个本地主机的目录到容器中去。
1 | docker run -d -P \ |
挂载一个本地主机文件作为数据卷
--mount
标记也可以从主机挂载单个文件到容器中
1 | docker run --rm -it \ |
这样就可以记录在容器输入过的命令了。
使用网络
外部访问容器
当使用 -P
标记时,Docker 会随机映射一个端口到内部容器开放的网络端口。
-p
则可以指定要映射的端口,并且,在一个指定端口上只可以绑定一个容器。支持的格式有 ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort
。
查看映射端口配置
使用 docker port
来查看当前映射的端口配置,也可以查看到绑定的地址
容器互联
Docker Compose
Docker Compose
是 Docker 官方编排(Orchestration)项目之一,负责快速的部署分布式应用。
Compose
中有两个重要的概念:
- 服务 (
service
):一个应用的容器,实际上可以包括若干运行相同镜像的容器实例。 - 项目 (
project
):由一组关联的应用容器组成的一个完整业务单元,在docker-compose.yml
文件中定义。