一 镜像基本操作
镜像是一个包含程序运行必要依赖环境和代码的只读文件, 其本质是磁盘上一系列文件的集合. 它采用分层的文件系统, 将每一次改变以读写层的形式增加到原来的只读文件上. 镜像是容器运行的基石.
1.1 搜索镜像
1 root@docker:~# docker search CentOS # 查询 CentOS 共享镜像
docker 命令必须具备 root 权限, 普通用户可是用那个 sudo.
提示: docker 默认的 Docker Hub 网址为: https://hub.docker.com/, 速度很慢, 建议添加国内的阿里云加速器, 参考 004-4.1.
选项说明:
NAME: 镜像仓库源的名称
DESCRIPTION: 镜像的描述
OFFICIAL: 是否 docker 官方发布
AUTOMATED: 是否使用了自动构建
1.2 下载 (拉取) 镜像
1 root@docker:~# docker pull CentOS:7 # 使用 pull 下载镜像
提示:
Registry: 仓库, Registry 包含一个或多个 Repository
Repository: 系列, Repository 包含一个或多个 Image
Tag and Image:Image 用 GUID 表示, 有一个或多个 Tag 与之关联, 版本号.
1.3 列出 (查看) 本地镜像
1 root@docker:~# docker images <特定标签> # 查看本地下载的镜像
选项说明:
REPOSTITORY: 表示镜像的仓库源, 有以下类型:
[namespace/CentOS]: 由命名空间和实际的仓库名称组成.
[CentOS]: 只有仓库名. 属于顶级命名空间, 只用于官方镜像.
[dl.dockerpool.com:5000\CentOS:7]: 指定 URL 路径的方式.
TAG: 镜像的标签
未指定镜像 tag 时, 默认为 latest, 但 latest 没有任何特殊含义, 人为的将 latest 作为最新稳定版本的别名;
一个 repository 可以有多个 tag, 而多个 tag 也可能对应同一个镜像.
IMAGE ID: 镜像 ID
CREATED: 镜像创建时间
SIZE: 镜像大小
1.4 推送镜像
1 root@docker:~# docker push registry.cn-hangzhou.aliyuncs.com/xhy-study-01/xhy-images-01:CentOS-7-xhy
提示: 推送镜像之前必须配置好仓库信息. 建议推送至个人私有仓库, 同时采用国内阿里云私有仓库, 配置方法见《附 001-docker 阿里云 Registry 配置》.
1.5 导出镜像
1 root@docker:~# docker save -o CentOS-7.tar CentOS:7
1.6 导入镜像
1 root@docker:~# docker load -i CentOS-7.tar
1.7 删除镜像
1 root@docker:/study# docker rmi httpd
1.8 设置镜像标签
- root@docker:~# docker tag 6de222aa7640 xhy/centos7:v3
- root@docker:~# docker images
二 docker 文件系统
2.1 Linux 文件系统简介
Linux 文件系统由 bootfs 和 rootfs 组成, bootfs 主要包含 bootloader 和 kernel,bootloader 主要是引导加载 kernel, 当 kernel 被加载到内存之后 bootfs 就被卸载掉了. rootfs 包含的就是典型 Linux 系统中 / dev,/proc,/bin,/etc 等标准目录.
2.2 docker 文件系统
Docker 容器是建立在 Aufs 基础上的, Aufs 支持将不同的目录挂载到同一个虚拟文件系统下, 并实现一种 layer 的概念. Aufs 将挂载到同一虚拟文件系统下的多个目录分别设置成 read-only,read-write 以及 whiteout-able 权限.
read-only 目录只能读, 而写操作只能在 read-write 目录中实现.
写操作是在 read-only 之上的一种增量操作, 不影响 read-only 目录.
docker 镜像中每一层文件系统都是 read-only.
提示: 当挂载目录的时候要严格按照各目录之间的这种增量关系, 将被增量操作的目录优先于在它基础上增量操作的目录挂载, 待所有目录挂载结束了, 继续挂载一个 read-write 目录, 如此便形成了一种层次结构.
2.3 docker 镜像原理
在构建镜像时, 从一个最基本的操作系统开始, 每个构建的操作都相当于做一层修改, 增加了一层文件系统, 一层层往上叠加, 上层的修改会覆盖底层该位置的可见性. 当使用时, 只会看到一个完全的整体, 总共有多少层以及每层所做的修改都是透明的.
docker image 中最基础的两层结构:
不同的 Linux 发行版(如 Ubuntu 和 CentOS ) 在 rootfs 这一层会有所区别, 体现发行版本的差异性:
传统的 Linux 加载 bootfs 时会将 rootfs 设为 read-only, 然后在系统自检后将 rootfs 从 read-only 改为 read-write, 然后可在 rootfs 上进行读写操作.
Docker 相比在 bootfs 自检完毕之后不会将 rootfs 的 read-only 改为 read-write, 而是利用 union mount(UnionFS 的一种挂载机制)将 image 中其他的 layer 加载到之前的 read-only 的 rootfs 层之上, 每一层 layer 都是 rootfs 的结构, 并且是 read-only 的. 因此, 无法修改一个已有镜像里面的 layer 层数据, 只有当创建一个容器, 即将 Docker 镜像进行实例化后, 系统会分配一层空的 read-write 的 rootfs , 用于提供数据修改.
三 镜像结构
3.1 image 结构
docker image 的 layer 组织方式通常由 JSON,layer.tar,VERSION 组成.
1 root@docker:/study# tar -xf CentOS-7.tar
1 root@docker:/study# tree
- root@docker:/study# cat repositories
- {"centos":{"7":"d1ed0d8ec4ec460641430566e9a8cece698e60d4ad4afcf48759ad157d340064"}}
解释: repositories 文件, 里面是一个 JSON 定义, 保存了三个信息: 镜像名字, tag,tag 对应的 layer.
1 root@docker:/study# cat d1ed0d8ec4ec460641430566e9a8cece698e60d4ad4afcf48759ad157d340064/JSON | jq .
解释: 主要关于镜像的配置信息, 简要部分信息如上.
1 root@docker:/study# tar -tf d1ed0d8ec4ec460641430566e9a8cece698e60d4ad4afcf48759ad157d340064/layer.tar
解释: 包括一个类 Linux 文件目录的结构, 保存着这个 layer 所做的修改.
四 Dockerfile,Docker 镜像和 Docker 容器
4.1 关系
Dockerfile 是软件的原材料, Docker 镜像是软件的交付品, 而 Docker 容器则可以认为是软件的运行态. 从应用软件的角度来看, Dockerfile,Docker 镜像与 Docker 容器分别代表软件的三个不同阶段, Dockerfile 面向开发, Docker 镜像成为交付标准, Docker 容器则涉及部署与运维.
Dockerfile 构建出 Docker 镜像, 通过 Docker 镜像运行 Docker 容器.
参考链接: http://dockone.io/article/783
https://blog.csdn.net/xuguokun1986/article/details/79295947
五 docker 存储驱动
Docker 最开始采用 AUFS 作为文件系统, 也得益于 AUFS 分层的概念, 实现了多个 Container 可以共享同一个 image. 但由于 AUFS 未并入 Linux 内核, 且只支持 Ubuntu, 考虑到兼容性问题, 在 Docker 0.7 版本中引入了存储驱动, 目前, Docker 支持 AUFS,Btrfs,Device mapper,OverlayFS,ZFS 五种存储驱动.
5.1 底层技术
写时复制(CoW)
所有驱动都需要用到写时复制(CoW),CoW 就是 copy-on-write, 表示只在需要写时才去复制, 这个是针对已有文件的修改场景. 比如基于一个 image 启动多个 Container, 如果为每个 Container 都去分配一个 image 一样的文件系统, 那么将会占用大量的磁盘空间. 而 CoW 技术可以让所有的容器共享 image 的文件系统, 所有数据都从 image 中读取, 只有当要对文件进行写操作时, 才从 image 里把要写的文件复制到自己的文件系统进行修改.
所以无论有多少个容器共享同一个 image, 所做的写操作都是对从 image 中复制到自己的文件系统中的复本上进行, 并不会修改 image 的源文件, 且多个容器操作同一个文件, 会在每个容器的文件系统里生成一个复本, 每个容器修改的都是自己的复本, 相互隔离, 相互不影响. 使用 CoW 可以有效的提高磁盘的利用率.
用时分配(allocate-on-demand)
用时分配是用在原本没有这个文件的场景, 只有在要新写入一个文件时才分配空间, 这样可以提高存储资源的利用率. 比如启动一个容器, 并不会为这个容器预分配一些磁盘空间, 而是当有新文件写入时, 才按需分配新空间.
5.2 AUFS
AUFS(AnotherUnionFS)是一种 Union FS, 是文件级的存储驱动. AUFS 能透明覆盖一个或多个现有文件系统的层状文件系统, 把多层合并成文件系统的单层表示. 即支持将不同目录挂载到同一个虚拟文件系统下的文件系统.
这种文件系统可以一层一层地叠加修改文件. 无论底下有多少层都是只读的, 只有最上层的文件系统是可写的. 当需要修改一个文件时, AUFS 创建该文件的一个副本, 使用 CoW 将文件从只读层复制到可写层进行修改, 结果也保存在可写层.
在 Docker 中, 底下的只读层就是 image, 可写层就是 Container. 结构如下图所示:
5.3 OverlayFS
Overlay 是 Linux 内核 3.18 后支持的, 也是一种 Union FS, 和 AUFS 的多层不同的是 Overlay 只有两层: 一个 upper 文件系统和一个 lower 文件系统, 分别代表 Docker 的镜像层和容器层. 当需要修改一个文件时, 使用 CoW 将文件从只读的 lower 复制到可写的 upper 进行修改, 结果也保存在 upper 层. 在 Docker 中, 底下的只读层就是 image, 可写层就是 Container.
5.4 Device mapper
Device mapper 是 Linux 内核 2.6.9 后支持的, 提供的一种从逻辑设备到物理设备的映射框架机制, 在该机制下, 用户可根据需要制定实现存储资源的管理策略.
不同于 AUFS 和 OverlayFS 的文件级存储, Device mapper 是块级存储, 所有的操作都是直接对块进行操作, 而不是文件.
Device mapper 驱动会先在块设备上创建一个资源池, 然后在资源池上创建一个带有文件系统的基本设备, 所有镜像都是这个基本设备的快照, 而容器则是镜像的快照.
所以在容器里看到文件系统是资源池上基本设备的文件系统的快照, 并不有为容器分配空间. 当要写入一个新文件时, 在容器的镜像内为其分配新的块并写入数据, 即用时分配. 当要修改已有文件时, 再使用 CoW 为容器快照分配块空间, 将要修改的数据复制到在容器快照中新的块里再进行修改.
Device mapper 驱动默认会创建一个 100G 的文件包含镜像和容器. 每一个容器被限制在 10G 大小的卷内, 大小可配置调整.
5.5 Btrfs
Btrfs 被称为下一代写时复制文件系统, 并入 Linux 内核, 也是文件级级存储, 但可以像 Device mapper 直接操作底层设备.
Btrfs 把文件系统的一部分配置为一个完整的子文件系统, 称之为 subvolume . 那么采用 subvolume, 一个大的文件系统可以被划分为多个子文件系统, 这些子文件系统共享底层的设备空间, 在需要磁盘空间时便从底层设备中分配.
为了灵活利用设备空间, Btrfs 将磁盘空间划分为多个 chunk . 每个 chunk 可以使用不同的磁盘空间分配策略. 比如某些 chunk 只存放 metadata, 某些 chunk 只存放数据. 这种模型有很多优点, 比如 Btrfs 支持动态添加设备.
用户在系统中增加新的磁盘之后, 可以使用 Btrfs 的命令将该设备添加到文件系统中.
Btrfs 把一个大的文件系统当成一个资源池, 配置成多个完整的子文件系统, 还可以往资源池里加新的子文件系统, 而基础镜像则是子文件系统的快照, 每个子镜像和容器都有自己的快照, 这些快照则都是 subvolume 的快照.
当写入一个新文件时, 为在容器的快照里为其分配一个新的数据块, 文件写在这个空间里, 这个叫用时分配. 而当要修改已有文件时, 使用 CoW 复制分配一个新的原始数据和快照, 在这个新分配的空间变更数据, 变结束再更新相关的数据结构指向新子文件系统和快照, 原来的原始数据和快照没有指针指向, 被覆盖.
5.6 ZFS
ZFS 文件系统是一个革命性的全新的文件系统, 它从根本上改变了文件系统的管理方式, ZFS 完全抛弃了 "卷管理", 不再创建虚拟的卷, 而是把所有设备集中到一个存储池中来进行管理, 用 "存储池" 的概念来管理物理存储空间. 过去, 文件系统都是构建在物理设备之上的. 为了管理这些物理设备, 并为数据提供冗余,"卷管理" 的概念提供了一个单设备的映像. 而 ZFS 创建在虚拟的, 被称为 "zpools" 的存储池之上. 每个存储池由若干虚拟设备 (virtual devices,vdevs) 组成. 这些虚拟设备可以是原始磁盘, 也可能是一个 RAID1 镜像设备, 或是非标准 RAID 等级的多磁盘组. 于是 zpool 上的文件系统可以使用这些虚拟设备的总存储容量.
Docker 里 ZFS 的使用. 首先从 zpool 里分配一个 ZFS 文件系统给镜像的基础层, 而其他镜像层则是这个 ZFS 文件系统快照的克隆, 快照是只读的, 而克隆是可写的, 当容器启动时则在镜像的最顶层生成一个可写层. 如下图所示:
当要写一个新文件时, 使用按需分配, 一个新的数据快从 zpool 里生成, 新的数据写入这个块, 而这个新空间存于容器 (ZFS 的克隆) 里.
当要修改一个已存在的文件时, 使用写时复制, 分配一个新空间并把原始数据复制到新空间完成修改.
5.7 存储驱动的对比及适应场景
特点 | 优点 | 缺点 | 使用场景 | |
AUFS | 联合文件系统 未并入内核主线 文件级存储 | 作为 docker 的第一个存储驱动,相对稳定,且在大量的生产中实践,有较强的社区支持。 | 有多层,在做写时复制操作时,如果文件较大且存在比较低的层,可能会慢一些。 | 大并发但少 IO 的场景。 |
OverlayFS | 联合文件系统 并入内核主线 文件级存储 | 只有两层 | 不管修改的内容大小都会复制整个文件,对大文件进行修改显示要比小文件消耗更多的时间。 | 大并发但少 IO 的场景。 |
Device mapper | 并入内核主线 块级存储 | 块级无论是大文件还是小文件都只复制需要修改的块,并不是整个文件。 | 不支持共享存储,即有多个容器读同一个文件时,需要生产多个副本,在多容器启停的情况下可能会导致磁盘溢出。 | 适合 IO 密集的场景。 |
Btrfs | 并入内核主线 文件级存储 | 可如 Device mapper 直接操作底层设备,支持动态添加设备。 | 不支持共享存储,即有多个容器读同一个文件时,需要生产多个副本,在多容器启停的情况下可能会导致磁盘溢出。 | 不适合在高密度容器的 PaaS 平台上使用。 |
ZFS | 把所有设备密集到一个存储池进行管理。 | 支持多个容器共享一个缓存块,适合内存大的环境。 | COW 使碎片化问题更加严重,文件在硬盘上的物理地址会变得不再连续,顺序读会变得性能比较差。 | 适合 PaaS 和高密度的场景。 |
参考链接: http://dockone.io/article/1513
5.8 修改 docker 存储驱动类型
- root@docker:~# vi /etc/docker/daemon.JSON
- {
- "storage-driver": "overlay2"
- }
六 创建镜像
从镜像仓库中下载的镜像若不能满足需求, 可通过以下两种方式对镜像进行更改.
从已经创建的容器中更新镜像, 并且提交这个镜像
使用 Dockerfile 指令来创建一个新的镜像
6.1 更新镜像并提交
运行容器
修改容器
将容器保存为新的镜像
- root@docker:~# docker run --name CentOS-7-01 -it CentOS:7 /bin/bash # 创建容器
- [root@01b2b251e216 /]# yum -y install.NET-tools VIM openssh-clients wget ntp bash-completion #安装软件
- [root@01b2b251e216 /]# exit
- root@docker:~# docker commit -m="has modify" -a="xhy" 01b2b251e216 CentOS-7-01
- root@docker:~# docker images
参数说明:
-m: 提交的描述信息
-a: 指定镜像作者
01b2b251e216 : 容器 ID
CentOS-7-01: 指定要创建的目标镜像名
1 root@docker:~# docker images # 查看镜像
更新现有镜像缺陷
手动创建, 容易出错, 效率低及可重复性弱
使用者并不知道镜像是如何创建出来的, 里面是否有恶意程序, 可能存在案例隐患
6.2 Dockerfile 构建镜像举例
- root@docker:~# mkdir /dockerfiles
- root@docker:~# cd /dockerfiles/
- root@docker:/dockerfiles# vi Dockerfile
- FROM CentOS:7
- MAINTAINER Fisher "xhy@imxhy.cn"
- RUN /bin/echo 'root:x123456' |chpasswd
- RUN useradd xhy
- RUN /bin/echo 'xhy:x123456' |chpasswd
- RUN /bin/echo -e "LANG=\"en_US.UTF-8\"">/etc/default/local
- EXPOSE 22
- EXPOSE 80
- CMD /usr/sbin/sshd -D
语句说明:
第一条 FROM, 指定所采用的镜像源, 每一个指令的前缀都必须是大写的;
RUN 指令表示 docker 在镜像内执行的命令, 更多详见七 Dockerfile 详解.
1 root@docker:/dockerfiles# docker build -t xhy/centos7 /dockerfiles/
参数说明:
-t : 指定要创建的目标镜像名
/dockerfiles/:Dockerfile 文件所在目录
1 root@docker:~# docker images # 查看镜像
1 root@docker:/dockerfiles# docker run -t -i xhy/centos7 /bin/bash # 创建容器
七 Dockerfile 详解
7.1 Dockerfile 典型结构
- From ubutu # 第一行必须指令基于的基础镜像
- MAINTAINER docker_user docker_user@mail.com #维护者信息
- apt/sourcelist.list # 镜像的操作指令
- RUN apt-get update && apt-get install -y ngnix # 镜像的操作指令
- RUN echo "\ndaemon off;">>/etc/ngnix/nignix.conf # 镜像的操作指令
- CMD /usr/sbin/ngnix # 容器启动时执行指令
7.2 Dockerfile 相关指令
指令: From
语法:
- FROM <image>
- FROM <image>:<tag>
- FROM <image>:<digest>
含义: FROM 命令定义构建镜像的基础镜像, 该条必须是 dockerfile 的首个命令. 若同一个 DockerFile 创建多个镜像时, 可使用多个 From 指令(每个镜像一次),FROM 是必备且必须是第一条指令. 在 FROM 指定构建镜像的基础源镜像时, 若本地没有指定的该镜像, 则会自动从 Docker 的公共库 pull 镜像下来.
提示:
FROM 必须是 Dockerfile 中非注释行的第一个指令, 即一个 Dockerfile 从 FROM 语句开始.
如果有需求在一个 Dockerfile 中创建多个镜像, 则 FROM 可以在一个 Dockerfile 中出现多次.
如果 FROM 语句没有指定镜像标签, 则默认使用 latest 标签.
举例: FROM Ubuntu
指令: MAINTAINER
语法:
1 MAINTAINER <name>
- RUN <commands>
- RUN "executable", "param1", "param2"
- RUN echo 'Hello, Docker!'
- RUN ["/bin/bash", "-c","echo hello"]
- CMD "executable","param1","param2"
- CMD "param1","param2"
- CMD command param1 param2 (shell form)
- ENTRYPOINT "executable", "param1", "param2"
- ENTRYPOINT command param1 param2 (shell form)
- ENTRYPOINT ["nginx"]
- CMD ["-g","daemon off;"]
- ENV <key> <value> # 只能设置一个变量
- ENV <key>=<value> ... # 允许一次设置多个变量
- ENV myName="John Doe" myDog=Rex myCat=fluffy
- ENV PG_MAJOR 9.3
- ENV PG_VERSION 9.3.4
- USER <user>[:<group>]
- USER <UID>[:<GID>]
- RUN groupadd -r Redis && useradd -r -g Redis Redis
- USER Redis
- RUN [ "redis-server" ]
- [...]
- ONBUILD ADD . /App/src
- ONBUILD RUN /usr/local/bin/python-build --dir /App/src
- [...]
- ADD . /App/src
- RUN /usr/local/bin/python-build --dir /App/src
- FROM nginx
- RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
- HEALTHCHECK --interval=5s --timeout=3s \
- CMD curl -fs http://localhost/ || exit 1
- RUN apt-get update && apt-get install -y \
- bzr \
- cvs \
- Git \
- mercurial \
- subversion
来源: https://www.cnblogs.com/itzgr/p/10188487.html