Docker 容器存储
Docker 容器文件系统
联合文件系统是一种把多个目录(不同层的文件系统)联合挂载成一个目录来使用的技术。
对使用者来说,好像只有一个文件系统,实际上,背后可能有很多层。
Docker 镜像和容器就依赖这种机制。
Docker 默认使用 overlay2 版本的联合文件系统:
[root@remote-host ~]# docker info -f json | jq .Driver
"overlay2"
查看 overlay2 联合文件系统挂载:
[root@remote-host ~]# mount | grep overlay
overlay on /var/lib/docker/overlay2/600c849ce398f4c0bfdd894c21400c25aa9f18c0682e46ded23594d07cd0ae43/merged type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/l/ROCQDGOGAQVFZVRHDRENQJZDLH:/var/lib/docker/overlay2/l/TRZ4RD3VYWEPDRTC2JCSE6FKPM:/var/lib/docker/overlay2/l/NMVX6ZGAZZQBPHXRGXEBN6HVL7:/var/lib/docker/overlay2/l/ORNLAV6RV3O4KISZHEJNETEUOT:/var/lib/docker/overlay2/l/6ELSV2ZTA5AHGJN2R7I34PZRZT:/var/lib/docker/overlay2/l/GWUXXNTGSISMOCLYRSPSUF2KJH:/var/lib/docker/overlay2/l/SUKDJBIVPXG2ESK32VOJYEXAP4:/var/lib/docker/overlay2/l/3ZTCGC6FBKFQFRNW4U55DTDXT7,upperdir=/var/lib/docker/overlay2/600c849ce398f4c0bfdd894c21400c25aa9f18c0682e46ded23594d07cd0ae43/diff,workdir=/var/lib/docker/overlay2/600c849ce398f4c0bfdd894c21400c25aa9f18c0682e46ded23594d07cd0ae43/work)
各字段含义
| 字段 | 含义 |
|---|---|
overlay |
使用的是 Linux 的 overlay 文件系统 |
on /var/lib/docker/overlay2/.../merged |
这是最终的挂载点,也就是容器内部看到的根目录 / |
lowerdir |
只读层(多个镜像层),按冒号分隔顺序叠加,最底层镜像在最后 |
upperdir |
可写层,即该容器启动之后写入的所有更改(copy-on-write)都在这里 |
workdir |
overlay2 的工作目录(系统操作时使用) |
rw,relatime |
挂载参数,表示读写挂载 + 相对时间更新(节省 I/O) |
以下是 overlay2 联合文件系统的简化图:
容器看到的是 merged(合并)目录:
↓
+------------------------------------+
| /merged = 用户视图 |
|------------------------------------|
| upperdir = 容器的写入层 |
|------------------------------------|
| lowerdir = 镜像的多个只读层 |
+------------------------------------+
所以 Docker 容器的存储就是一些只读层加顶层的可写层组合到一块的,下边展示一下这个文件系统:
# 创建一个容器
[root@remote-host ~]# docker run --rm -itd --name nginx -v /data:/data nginx:latest
e7d5ab25d7249a718db1d7f4776dd11306e1897679e653a78a986ef1873192cc
# 检查容器存储信息
[root@remote-host ~]# docker inspect nginx -f json | jq .[0].GraphDriver
{
"Data": {
"ID": "e7d5ab25d7249a718db1d7f4776dd11306e1897679e653a78a986ef1873192cc",
"LowerDir": "/var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9-init/diff:/var/lib/docker/overlay2/ca46c57b83d9ba56d86baba9ef751e7eeda120d5d12ae99a01bf409b2762e475/diff:/var/lib/docker/overlay2/c869e1af9132a71a52dba999dd403f913b98977f3d69821b17d49da2e5485e49/diff:/var/lib/docker/overlay2/c1994238aee78b95e17f4b934f98d71474d30ca5dee6ba2822d2f1fd3b601e3f/diff:/var/lib/docker/overlay2/7361e60e95891cc7f6deb9276ab17c85fb1bcb0dceb3ea78f26414d982541d3f/diff:/var/lib/docker/overlay2/8ac950d7be44412b43c3a7d3b8ff49215f2b148805ec86c962726119c6d16efa/diff:/var/lib/docker/overlay2/e34c03364361edfdf542c32578b5ed053aeb85db5d3d4b1456ae62c53f0f1f17/diff:/var/lib/docker/overlay2/12d65b3c0db6cfe2c0fc80ae5795ac5fe1bc802433d7e7c0328124533e62a29a/diff",
"MergedDir": "/var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/merged",
"UpperDir": "/var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/diff",
"WorkDir": "/var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/work"
},
"Name": "overlay2"
}
[root@remote-host ~]# docker inspect nginx -f json | jq .[0].Mounts
[
{
"Type": "bind",
"Source": "/data",
"Destination": "/data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]
# 检查容器文件系统目录树
[root@remote-host ~]# tree /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9-init/
/var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9-init/
├── committed
├── diff
│ ├── dev
│ │ ├── console
│ │ ├── pts
│ │ └── shm
│ └── etc
│ ├── hostname
│ ├── hosts
│ ├── mtab -> /proc/mounts
│ └── resolv.conf
├── link
├── lower
└── work
└── work
└── #13f0
7 directories, 9 files
[root@remote-host ~]# tree -L 2 /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/
/var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/
├── diff
│ ├── data
│ ├── etc
│ ├── run
│ └── var
├── link
├── lower
├── merged
│ ├── bin -> usr/bin
│ ├── boot
│ ├── data
│ ├── dev
│ ├── docker-entrypoint.d
│ ├── docker-entrypoint.sh
│ ├── etc
│ ├── home
│ ├── lib -> usr/lib
│ ├── lib64 -> usr/lib64
│ ├── media
│ ├── mnt
│ ├── opt
│ ├── proc
│ ├── root
│ ├── run
│ ├── sbin -> usr/sbin
│ ├── srv
│ ├── sys
│ ├── tmp
│ ├── usr
│ └── var
└── work
└── work
29 directories, 3 files
# 进入容器添加和删除数据
[root@remote-host ~]# docker exec -it nginx bash
root@e7d5ab25d724:/# touch /tmp/testfile
root@e7d5ab25d724:/# rm -rf /mnt/
root@e7d5ab25d724:/# mkdir /tmp/testdir
root@e7d5ab25d724:/# rm -rf /etc/issue
root@e7d5ab25d724:/# exit
exit
# 再次检查目录树
[root@remote-host ~]# tree /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9-init/
/var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9-init/
├── committed
├── diff
│ ├── dev
│ │ ├── console
│ │ ├── pts
│ │ └── shm
│ └── etc
│ ├── hostname
│ ├── hosts
│ ├── mtab -> /proc/mounts
│ └── resolv.conf
├── link
├── lower
└── work
└── work
└── #13f0
7 directories, 9 files
[root@remote-host ~]# tree -L 2 /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/
/var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/
├── diff
│ ├── data
│ ├── etc
│ ├── mnt
│ ├── root
│ ├── run
│ ├── tmp
│ └── var
├── link
├── lower
├── merged
│ ├── bin -> usr/bin
│ ├── boot
│ ├── data
│ ├── dev
│ ├── docker-entrypoint.d
│ ├── docker-entrypoint.sh
│ ├── etc
│ ├── home
│ ├── lib -> usr/lib
│ ├── lib64 -> usr/lib64
│ ├── media
│ ├── opt
│ ├── proc
│ ├── root
│ ├── run
│ ├── sbin -> usr/sbin
│ ├── srv
│ ├── sys
│ ├── tmp
│ ├── usr
│ └── var
└── work
└── work
30 directories, 4 files
# 检查几个目录
[root@remote-host ~]# ls -l /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/diff/
total 0
drwxr-xr-x 2 root root 6 Aug 3 22:28 data
drwxr-xr-x 3 root root 32 Aug 3 22:34 etc
c--------- 3 root root 0, 0 Aug 3 22:34 mnt
drwx------ 2 root root 27 Aug 3 22:34 root
drwxr-xr-x 2 root root 23 Aug 3 22:28 run
drwxrwxrwt 3 root root 37 Aug 3 22:34 tmp
drwxr-xr-x 3 root root 19 Jul 21 08:00 var
[root@remote-host ~]# ls -l /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/diff/tmp/
total 0
drwxr-xr-x 2 root root 6 Aug 3 22:34 testdir
-rw-r--r-- 1 root root 0 Aug 3 22:33 testfile
[root@remote-host ~]# ls -l /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/diff/etc/
total 0
c--------- 3 root root 0, 0 Aug 3 22:34 issue
drwxr-xr-x 3 root root 20 Jul 22 09:13 nginx
[root@remote-host ~]# ls -l /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/merged/tmp/
total 0
drwxr-xr-x 2 root root 6 Aug 3 22:34 testdir
-rw-r--r-- 1 root root 0 Aug 3 22:33 testfile
[root@remote-host ~]# ls -l /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/merged/etc/issue
ls: cannot access '/var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/merged/etc/issue': No such file or directory
[root@remote-host ~]# ls -l /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/merged/mnt
ls: cannot access '/var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/merged/mnt': No such file or directory
[root@remote-host ~]# cat /var/lib/docker/overlay2/65e25eb592560c44234f2a7412e53292c9125eec207bfaef3051fec9c3f971a9/lower
l/WYB2EPGYJ7BFVUV7WQWUAY6UIL:l/TRZ4RD3VYWEPDRTC2JCSE6FKPM:l/NMVX6ZGAZZQBPHXRGXEBN6HVL7:l/ORNLAV6RV3O4KISZHEJNETEUOT:l/6ELSV2ZTA5AHGJN2R7I34PZRZT:l/GWUXXNTGSISMOCLYRSPSUF2KJH:l/SUKDJBIVPXG2ESK32VOJYEXAP4:l/3ZTCGC6FBKFQFRNW4U55DTDXT7
可以看到 diff 目录下会显示文件差异,lower 文件下记录只读目录的架构,merged 目录是容器做过修改后的实际目录,也就是 lower 和 diff 合并的结果。
为容器设置卷挂载
Docker 创建容器的时候可以通过 -v 将本地目录挂载到容器里,这样可以实现数据持久化挂载:
[root@remote-host ~]# docker run -itd --rm --name busybox -v /data:/data busybox:latest
aea1d937b943f27567fd1dd901f661f134a058aa00191243700a7f295edce74f
[root@remote-host ~]# docker inspect busybox -f json | jq .[0].Mounts
[
{
"Type": "bind",
"Source": "/data",
"Destination": "/data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
]