docker 初步理解
1 docker 相关文件
1.1 image 文件
Docker 把应用程序及其依赖,打包在 image 文件里面。只有通过这个文件,才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。
image 是二进制文件。实际开发中,一个 image 文件往往通过继承另一个 image 文件,加上一些个性化设置而生成。例如,可以在 Ubuntu 的 image 基础上,往里面加入 Apache 服务器,形成新的 image。
# 列出本机的所有 image 文件
docker image ls
# 删除 image 文件
docker image rm [imageName]
image 文件是通用的,一台机器的 image 文件拷贝到另一台机器,可以直接使用。一般来说,为了节省时间,我们应该尽量使用别人制作好的 image 文件。如果需要定制,可以基于别人的 image 文件进行加工,不必要从零开始制作。
为了方便共享,image 文件制作完成后,可以上传到网上的仓库。Docker 的官方仓库 Docker Hub 是最重要、最常用的 image 仓库。
1.2 镜像实例
从仓库下载 image 文件,国内访问 Docker 的官方仓库很慢,还经常断线,所以要把仓库网址改成国内的镜像站。
- 打开
/etc/default/docker
文件(需要sudo权限),在文件的底部加上一行。
DOCKER_OPTS="--registry-mirror=https://registry.docker-cn.com"
- 重启 Docker 服务
sudo service docker restart
现在就会自动从镜像仓库下载 image 文件.
运行下面的命令,将 image 文件从仓库抓取到本地
docker image pull library/hello-world
说明:
docker image pull
是抓取 image 文件的命令library/hello-world
是 image 文件在仓库里面的位置,其中library
是 image 文件所在的组hello-world
是 image 文件的名字
Docker 官方提供的 image 文件,都放在library组里面,所以它的是默认组,可以省略。因此,上面的命令可以写成下面这样:
docker image pull hello-world
运行这个 image 文件:
docker container run hello-world
说明:
docker container run
命令会从 image 文件,生成一个正在运行的容器实例。docker container run
命令具有自动抓取 image 文件的功能。如果发现本地没有指定的 image 文件,就会从仓库自动抓取。因此,前面的docker image pull
命令并不是必需的步骤。
如果运行成功,屏幕上会有相应的输出:
Hello from Docker!
This message shows that your installation appears to be working correctly.
输出这段提示以后,hello world
就会停止运行,容器自动终止。
有些容器不会自动终止,因为提供的是服务。比如,安装运行 centos 的 image,就可以在命令行体验 centos 系统。对于那些不会自动终止的容器,必须使用docker container kill
命令手动终止:
docker container kill [containID]
1.3 容器文件
image 文件生成的容器实例,本身也是一个文件,称为容器文件。也就是说,一旦容器生成,就会同时存在两个文件: image 文件和容器文件。而且关闭容器并不会删除容器文件,只是容器停止运行而已。
# 列出本机正在运行的容器
docker container ls
# 列出本机所有容器,包括终止运行的容器
docker container ls --all
终止运行的容器文件,依然会占据硬盘空间,可以使用docker container rm
命令删除。
docker container rm [containerID]
1.4 Dockerfile 文件
Dockerfile 文件是一个文本文件,用来配置 image。Docker 根据该文件生成二进制的 image 文件。
镜像文件可以通过Dockerfile来一步一步的去进行构建,Dockerfile其实这就是一堆的命令和脚本。
1.4.1 dockerfile 基本语法:
- 每个保留关键字(指令)都是必须是大写字母
- 执行从上到下顺序
#
表示注释- 每一个指令都会创建提交一个新的镜像,并提交
1.4.2 DockerFile常用指令
FROM # 基础镜像,一切从这里开始构建 centos
MAINTAINER # 镜像是谁写的, 姓名+邮箱
RUN # 镜像构建的时候需要运行的命令
ADD # 步骤,tomcat镜像,这个tomcat压缩包!添加内容 添加同目录
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 暴露端口配置 和 -p 一样
CMD # 指定这个容器启动的时候要运行的命令,只有最后一个会生效,可被替代。
ENTRYPOINT # 指定这个容器启动的时候要运行的命令,可以追加命令
ONBUILD # 当构建一个被继承 DockerFile 这个时候就会运行ONBUILD的指令,触发指令。
COPY # 类似ADD,将我们文件拷贝到镜像中
ENV # 构建的时候设置环境变量!
2 Docker镜像加载原理
2.1 UnionFS(联合文件系统)
Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,类似于Git中的一层一层提交的一个概念,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
UnionFS 一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把文件系统叠加起来,这样最终的文件系统包含所有底层的文件和目录。因此在下载镜像的时候,在 Terminal 看到的日志是在下载名称类似 git commit ID 那一个个文件。
2.2 Dcoker镜像加载原理
bootfs(boot file system):主要包含bootloader和kernel。bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们电脑的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs(root file system):在bootfs之上。包含的就是典型Linux 系统中的 /dev,/proc,/bin,/etc 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。
前面提到虚拟机的体积比docker占用的资源和存储空间都要大很多,例如安装进虚拟机的CentOS都是好几个G,Docker只有200M。原因是:对于个精简的OS,rootfs可以很小,只需要包合最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就可以了。由此可见对于不同的Linux发行版, bootfs基本是一致的, rootfs会有差別,因此不同的发行版可以公用bootfs。
2.3 分层理解
前面提到,docker 的文件系统是分层的,这样做的目的是为了能够提高资源共享,有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
查看镜像分层的方式可以通过docker image inspect 命令:
docker inspect redis
所有的docker镜像都起始于一个基础镜像层,当进行修改或者增加新的内容时,就会在当前镜像层之上,创建新的镜像层。
在添加额外的镜像层的同时,镜像始终是保持当前所有镜像的组合。例如在一些基础层当中,原本有了一些环境,比如java、tomcat、maven,然后我们需要再去安装一些redis、mysql或者是python等等,那么就只需要在层的概念上,添加上一层需要添加的环境就可以。对于一个新的镜像,如果其他层级不变,都与前面那个镜像相同,可以直接用。如果其他的一些文件发生了一些变化,那只需要操作文件就可以,新增修改这一层的文件。所有的镜像都是一步一步的进行分层的。这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新的镜像层添加到镜像当中。
Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。Linux 上可用的存储引擎有AUFS、Overlay2,Device Maper、Btrfs 以及 ZFS,每种存储引擎都基于Linux中对应的文件系统或者块设备技术。Docker在Windows上仅支持windowsfiler 一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW。
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部。这一层就是通常说的容器层,容器之下的都叫镜像层。
3 容器数据卷
数据卷技术的目的是容器的持久化,实现容器之间数据共享,Docker容器中产生的数据,同步到本地。
3.1 使用数据卷
- 直接使用命令挂载
-v
, -v, --volume list Bind mount a volume
docker run -it -v 主机目录:容器内目录
# 下面将启动一个 centos 容器,并将 centos 容器的 /home 映射到本地的 /home/centosTest 路径
run -it -v /home/centosTest:/home centos /bin/bash
# 在 本地主机查看centos 容器的 ID :
docker ps
# CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
# f8c960030aaa centos "/bin/bash" 4 minutes ago Up 4 minutes quirky_mendel
# 在本地主机通过 docker inspect 命令查看
docker inspect f8c960030aaa
# 可以看到挂载的地址
"HostConfig": {
"Binds": [
"/home/centosTest:/home"
],
cd /home
# 新建 test.txt 并在文件中添加 test,保存退出
# 在本地主机查看 /home/centosTest 会发现下面有一个 test.txt 文件,文件内容为 test, 就是刚才在 centos 容器内新建的
# 在本地主机编辑 /home/centosTest/test.txt 这个文件,在文件末尾添加 test also
# 在 centos 容器内查看 /home/test.txt,看到 文件内容为 test 和 test also
# 在本地主机关闭容器
docker kill f8c960030aaa
# 再在本地主机查看 /home/centosTest,看到 文件内容还是为 test 和 test also
上面的实验说明:
- 通过上面的命令,可以将本地主机的目录挂载在容器内
- 容器内的数据和本地主机的挂载地址的数据是同步的,并且同步是双向的
- 在容器关闭之后,本地的数据还在
3.2 具名和匿名挂载
-v 容器内地址
匿名挂载-v 卷名:容器内地址
具名挂载-v /宿主机路径:容器路径
指定路径挂载
其中具名挂载的卷所在位置可以通过下面的命令来查看:
docker volume inspect valume_name
# 或者
docker inspect nginx_hh valume_name
所有的docker容器内的卷,没有指定目录的情况下都是在 /var/lib/docker/volumes/xxx/_data
- 通过
-v 容器内路径: ro rw
改变读写权限
ro readonly 只读
rw readwrite 可读可写
一旦设置了容器权限,容器对我们挂载出来的内容权限就有限制了。ro
说明这个路径只能通过宿主机来操作,容器内部是无法操作。