背景
在运行mythril的时候,第一次接触到了docker,由于当时的重心在于运行并改造Mythril,因此,我与docker的交互只存在于一句run命令。现在在运行Fabric的时候,docker又一次出现在了我的面前,其作用域也不仅仅在一句run命令了。从安装到运行,再到以后我也不知道会发生什么事情的地方,docker所扮演的角色越来越重要。我觉得有必要好好学习一下docker了。
什么是docker
Docker0.1开源与2013年,是一门年轻但是潜力无限的技术。
使用Go开发实现的,基于Linux内核的,对进程进行封装隔离的,属于操作系统层面的虚拟化技术。由于隔离的进程独立于宿主和其他进程,因此称其为容器。但是Docker不是容器。Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上。
Docker和传统虚拟化技术
传统虚拟化技术,比如VMware是在虚拟出一套硬件之后,再在上面运行一个OS,然后再在这个OS上运行程序。
Docker没有进行硬件虚拟,进程直接运行在宿主的内核之中。
上图是Docker和虚拟机的比较,可由此推出为什么要用Docker。
三大概念
- 镜像image
应用的运行需要一个环境,镜像就是来提供这个环境的。比如,一个镜像可以完全包含了Ubuntu操作系统环境。镜像是只读的,包含了文件系统。
镜像的构建不是堆一起的,而是分层构建。上层以下层为基础,逐步搭建起来,每一层都是只读层。
- 容器container
Docker利用容器来隔离和运行程序,容器从镜像创建的应用运行实例,镜像虽然是只读的,但是在创建容器时会在上面建立一个可写层,镜像依然不变。可以理解为镜像就是我们装系统用的ISO文件,而容器就是我们正在用的系统,已经被我们装上了各种各样的软件。
容器可以被创建、启动、停止、删除和暂停。其实质就是一个进程,只是和宿主的进程不一样,运行在自己的namespace之中,使得自己拥有自己的root文件系统、网络配置、进程空间之类的。
PS:容器的存储层的生命依赖于容器,容器亡则存储亡,因此通常用Volume(数据卷)或者绑定宿主目录直接对宿主发生读写。Volume的生命独立于容器。
- 仓库repository
仓库用来存放镜像的仓库,有一个Registry(注册服务器),是用来存放多个仓库的。通常一个仓库会包含一个软件的不同版本,标签则对应相应的版本,因此我们用<仓库名>:<标签>来指定哪个版本。
我的理解是,一个公司就是一个注册服务器,它有多少种产品就有多少个仓库,每个仓库都是这个产品的不同版本。
镜像的使用:
- 获取镜像
命令为:
docker pull [选项] [Docker Registry 地址[:端口号]/]仓库名[:标签]
镜像是由多层存储所构成。下载也是一层层的去下载,并非单一文件。下载过程中给出了每一层的ID的前12位。并且下载结束后,给出该镜像完整的sha256的摘要,以确保下载一致性。由于官方镜像一直在维护,解决BUG,因此隔一段时间之后,在下载内容可能会得到不一样的ID和sha256值。
- 列出镜像:
docker image ls
列表包含了仓库名、标签、镜像ID、创建时间以及所占用的空间。
- 镜像体积
Docker Hub中的体积大小是压缩后的值,而我们pull之后的值是解压之后的值。但是这些显示的大小是真的占据我们磁盘空间的大小吗?NO。别忘了我们的镜像是分层的,也就是说,如果两个镜像有相同的某个分层的话,那么这些层是只需要下载一份就可以了。下面的命令可以看到真实大小。
docker system df
- 删除本地镜像
docker image rm [选项] <镜像1> [<镜像2> ...]
Untagger 和 Deleted
删除镜像实际是删除标签,因为一个镜像可能有多个标签。我们删去了这个标签后,依然可能有其它标签指向这个镜像。此时只有untagged发生。当一个镜像没有标签指着的时候,就可能(注意是可能)会出现deleted。由于镜像是分层结构,所以删除的时候是从上向下删除,但是上层依赖于下层,当有上层依赖于该层时,虽然该层没有标签了,但依然可以存在。还有就是如果这个镜像的容器存在,即使没有启动也无法删除,因为有个读写层存在使得下层不会出现变化。
自己造镜像太繁琐了,DockerFile可不是入门级别的东西,放在以后再说吧,下面来谈谈怎么玩容器吧。
容器的使用
- 启动容器
启动容器有两种方式,1.基于镜像新建一个容器;2.将存在但是停止的容器启动。为什么有俩方式呢?都怪这个Docker太轻量了,把大家惯坏了,现在的人都养成了用完就删的习惯,现用现造,快的过分。新建并启动命令就是:
docker run 指定镜像 命令
其操作如下:
- 检查本地有没有指定的镜像,没有就从公有仓库下载
- 利用镜像创建一个容器
- 分配文件系统,在镜像外面挂一个读写层
- 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
- 从地址池配置一个ip地址给容器
- 执行用户指定的应用程序
- 执行完毕后容器被终止
- 启动已终止容器的命令:
docker container start
- 终止容器:
docker container stop
除了上面的命令,当容器中指定的程序跑完了,该容器也会终止。
终止状态的容器可以用 docker container ls -a 命令看到。
此外, docker container restart 命令会将一个运行态的容器终止,然后再重新启动它。
- 进入容器
docker exec(推荐)和docker attach
- 导出容器
docker export 7691a814370e > ubuntu.tar这样将导出容器快照到本地文件。
- 导入容器
可以使用 docker import 从容器快照文件中再导入为镜像
- 删除容器
docker container rm
如果这个容器还在运行,可以在后面加 -f。
- 清理终止状态的容器
docker container prune
Docker的数据
前面说了,容器删了,数据就没了,因此用数据卷或者绑定本地目录的方式来保存数据。下面就开始介绍一下这俩。
- 数据卷(Volumes)
这是一个可以供多个容器使用的目录,读写它不会对镜像产生影响,而且它独立于容器,不会因为容器的删除而删除。
创建数据卷
docker volume create my-vol
查看所有数据卷
docker volume ls
查看数据卷的信息
docker volume inspect my-vol
删除数据卷
docker volume rm my-vol
- 绑定主机目录(Bind Mount)
--mount 可以指定挂载一个本地主机的目录到容器之中。
docker run -d -P \
--name web \
--mount type=bind,source=/src/webapp,target=/opt/webapp \
training/webapp \
python app.py
挂载的默认权限是读写,可以加readonly来使之只读。
docker run -d -P \
--name web \
--mount type=bind,source=/src/webapp,target=/opt/webapp,readonly \
training/webapp \
python app.py
参考:
http://www.runoob.com/docker/docker-architecture.html
https://www.cnblogs.com/SzeCheng/p/6822905.html
https://blog.csdn.net/bskfnvjtlyzmv867/article/details/81044217
https://www.jianshu.com/p/2c832c1b8fd5
《Docker 入门白皮书》
《Docker——从入门到实践》