docker 联合文件系统 深入解读

[toc]

作用

获取容器open in new window/镜像的元数据(JSON格式)

命令格式

docker inspect [OPTIONS] NAME|ID [NAME|ID...]

options 说明

option说明
-f指定返回值的模板文件
-s如果类型为容器,则显示文件总大小
--type返回指定类型的JSON

筛选

获取 IP 地址:

docker inspect tomcat7 | grep IPAddress

获取 Dir :

我主要是希望很熟悉 Data 字段,所以后面重点说明~

[root@VM-4-6-centos 123]# docker  inspect -s 40da9cff87a0 |grep "Dir"
                "LowerDir": "/var/lib/docker/overlay2/0a0abda0165a1342ddac4ba3263352d79564e9a56dfdb497ac66e25030bb5b57-init/diff:/var/lib/docker/overlay2/2e6f81e45b3db01494296c925185d77831443eca0dd3cbf119f8d32190f90d5c/diff:/var/lib/docker/overlay2/ebf60447144fb679348fc4534bc30b68ce19618aac23b39ee93e5f941d6b13bc/diff:/var/lib/docker/overlay2/eb6694dc2ad08ab91f8f4e3f00bf7481d2c59df52c3a1553f0b7f3c3512179ed/diff:/var/lib/docker/overlay2/c4acacb9aa7f0e60af79eeb6cb7506d736df1789710da7f7e53747feffaead95/diff:/var/lib/docker/overlay2/29165236db2b92b69ae83bc679d17cf7237880dde09e5d3c1f5890ca6e907861/diff:/var/lib/docker/overlay2/3b17651312652668a9c2ef61df2a957ca7ceeb6307166a23a5726ca8dbaf1c23/diff:/var/lib/docker/overlay2/23a2a19d74c1945307e606a0df77496e2bdca76b85ea9058bd76a1cb9e1be205/diff:/var/lib/docker/overlay2/2916a80e9370af849f411871cad64cbb13d04617c91e66f45a16cbe979ffe88f/diff:/var/lib/docker/overlay2/d78b6c08a6749d7b88e7eb4fac33de1355cb1d926fcb01a8b1c72361d1867cbc/diff:/var/lib/docker/overlay2/be49972d15fe94755593cfd846c231d2a18e625a256fa86fe62e131e711f200a/diff",
                "MergedDir": "/var/lib/docker/overlay2/0a0abda0165a1342ddac4ba3263352d79564e9a56dfdb497ac66e25030bb5b57/merged",
                "UpperDir": "/var/lib/docker/overlay2/0a0abda0165a1342ddac4ba3263352d79564e9a56dfdb497ac66e25030bb5b57/diff",
                "WorkDir": "/var/lib/docker/overlay2/0a0abda0165a1342ddac4ba3263352d79564e9a56dfdb497ac66e25030bb5b57/work"
            "WorkingDir": "/application",

docker GraphDriver

这里我们了解一下 docker 的镜像存储,在Docker中,一个很重要的概念就是 GraphDriver,它主要用于管理和维护镜像,包括把镜像从仓库下载下来,到运行时把镜像挂载起来可以被容器访问等,都是GraphDriver去完成的。

我们以 Golang 镜像为准:

 docker  inspect -s golang 

字段 GraphDriver:

 "GraphDriver": {
            "Name": "overlay2",
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/0a0abda0165a1342ddac4ba3263352d79564e9a56dfdb497ac66e25030bb5b57-init/diff:/var/lib/docker/overlay2/2e6f81e45b3db01494296c925185d77831443eca0dd3cbf119f8d32190f90d5c/diff:/var/lib/docker/overlay2/ebf60447144fb679348fc4534bc30b68ce19618aac23b39ee93e5f941d6b13bc/diff:/var/lib/docker/overlay2/eb6694dc2ad08ab91f8f4e3f00bf7481d2c59df52c3a1553f0b7f3c3512179ed/diff:/var/lib/docker/overlay2/c4acacb9aa7f0e60af79eeb6cb7506d736df1789710da7f7e53747feffaead95/diff:/var/lib/docker/overlay2/29165236db2b92b69ae83bc679d17cf7237880dde09e5d3c1f5890ca6e907861/diff:/var/lib/docker/overlay2/3b17651312652668a9c2ef61df2a957ca7ceeb6307166a23a5726ca8dbaf1c23/diff:/var/lib/docker/overlay2/23a2a19d74c1945307e606a0df77496e2bdca76b85ea9058bd76a1cb9e1be205/diff:/var/lib/docker/overlay2/2916a80e9370af849f411871cad64cbb13d04617c91e66f45a16cbe979ffe88f/diff:/var/lib/docker/overlay2/d78b6c08a6749d7b88e7eb4fac33de1355cb1d926fcb01a8b1c72361d1867cbc/diff:/var/lib/docker/overlay2/be49972d15fe94755593cfd846c231d2a18e625a256fa86fe62e131e711f200a/diff",
                "MergedDir": "/var/lib/docker/overlay2/0a0abda0165a1342ddac4ba3263352d79564e9a56dfdb497ac66e25030bb5b57/merged",
                "UpperDir": "/var/lib/docker/overlay2/0a0abda0165a1342ddac4ba3263352d79564e9a56dfdb497ac66e25030bb5b57/diff",
                "WorkDir": "/var/lib/docker/overlay2/0a0abda0165a1342ddac4ba3263352d79564e9a56dfdb497ac66e25030bb5b57/work"
            }
        },

📜 对上面的解释

image-20221125163549549

  • LowerDir:包含容器内所有层的文件系统,最后一层除外
  • UpperDir:容器最上层的文件系统。这也是反映任何运行时修改的地方。
  • MergedDir:文件系统所有层的组合视图。
  • WorkDir:用于管理文件系统的内部工作目录。

容器内

 docker exec -it golang:latest /bin/bash 

根目录:

root@cbd9c47e79e3:/# cd /; mkdir 111;cd 111; echo "this a test file" >> test.txt; ls
test.txt
go version go1.19.3 linux/amd64
root@cbd9c47e79e3:/111# whereis go
go: /usr/local/go /usr/local/go/bin/go

Go语言工作目录:

root@cbd9c47e79e3:/usr/local/go/bin# ls -al go
-rwxr-xr-x 1 root root 15301490 Oct 31 20:20 go
root@cbd9c47e79e3:/usr/local/go/bin# pwd 
/usr/local/go/bin
root@cbd9c47e79e3:/usr/local/go/bin# ./go version
go version go1.19.3 linux/amd64

lower

最后一层除外:这个意思就是我们的可写层,我们对容器的修改,都是在这层上面进行的,即我们容器里面的 cd /; mkdir 111;cd 111; echo "this a test file" >> test.txt; 这个文件在LowerDir里看不到。

MergedDir

在这里我们可以看到整个容器里面的文件系统,包括修改的(注意是容器)

[root@VM-4-6-centos merged]# cd /var/lib/docker/overlay2/4ff3e2faabfa51f38c884c1321e20609352f5e009239d146161ec75bc806c3c5/merged
[root@VM-4-6-centos merged]# ls
111  bin  boot  dev  etc  go  home  lib  lib64  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var

测试里面的环境变量

是不是可以将里面的 Go语言 环境变量位置配置到主机中???我觉得这样好玩

当前位置:

[root@VM-4-6-centos bin]# ls
go  gofmt
[root@VM-4-6-centos bin]# pwd
/var/lib/docker/overlay2/4ff3e2faabfa51f38c884c1321e20609352f5e009239d146161ec75bc806c3c5/merged/usr/local/go/bin
[root@VM-4-6-centos bin]# cp go golangtext

加入环境变量:

[root@VM-4-6-centos bin]# cp go golangtext

# 加入环境变量
[root@VM-4-6-centos root]# cat /etc/profile.d/mypath 
export GO_TEST=$"/var/lib/docker/overlay2/4ff3e2faabfa51f38c884c1321e20609352f5e009239d146161ec75bc806c3c5/merged/usr/local/go"
export PATH=$PATH:$GO_TEST/bin

测试:

[root@VM-4-6-centos root]# golangtext version
go: cannot find GOROOT directory: /root/.g/go

UpperDir

WorkDir:用于管理文件系统的内部工作目录

我们在 Dockerfile 中可指定 workspaces,即使在 GitHub 的 codespaces 中,workspaces 也是非常重要的。

[root@VM-4-6-centos root]# cd /var/lib/docker/overlay2/4ff3e2faabfa51f38c884c1321e20609352f5e009239d146161ec75bc806c3c5/work
[root@VM-4-6-centos work]# ls
work
[root@VM-4-6-centos work]# cd work/
[root@VM-4-6-centos work]# ls

/proc/<pid>/root

我们到现在知道了 Pid 在 docker 中起到了多么重要的作用,这个关乎着我们未来构建 docker 的计划~

有一种更简单的方法可以从主机找到容器的文件系统。使用容器内进程的主机 PID,您可以简单地运行:

sudo ls /proc/<pid>/root

Linux 已经负责为您提供进程的挂载命名空间视图。

获取命名空间

提示

容器进程就像 Linux 主机上的其他进程一样,只在命名空间内运行,以使它们与系统的其余部分隔离。

因此,您可以使用 nsenteropen in new window 命令输入目标容器的命名空间

# Get the host PID of the process in the container
PID=$(docker container inspect mycontainer | jq '.[0].State.Pid')

# Use nsenter to go into the container’s mount namespace.
sudo nsenter -m -t $PID /bin/bash

获取 PID

前提:我们知道了 容器 ID

docker inspect -f '{{.State.Pid}}'

💡简单的一个案例如下:

[root@VM-4-6-centos work]# docker ps
CONTAINER ID        IMAGE                          COMMAND                  CREATED             STATUS                      PORTS                    NAMES
cbd9c47e79e3        golang                         "bash"                   About an hour ago   Up About an hour                                     jolly_chandrasekhar
40da9cff87a0        docker.io/halohub/halo:1.6.0   "/bin/sh -c 'java ..."   11 days ago         Restarting (1) 2 days ago                            halo-next
0b35d2dcb578        3293172751/halo_myblog:2.0     "/bin/sh -c 'java ..."   3 weeks ago         Up 11 days                  0.0.0.0:8090->8090/tcp   halo2
[root@VM-4-6-centos work]# docker inspect -f '{{.State.Pid}}' cbd9c47e79e3
1050

或者你还可以通过container 获取

docker container top

查看/proc/<pid>/root

[root@VM-4-6-centos work]# ll /proc/1050/root/
total 76
drwxr-xr-x   2 root root 4096 Nov 25 16:10 111
drwxr-xr-x   1 root root 4096 Nov 15 18:23 bin
drwxr-xr-x   2 root root 4096 Sep  3 20:10 boot
drwxr-xr-x   5 root root  360 Nov 25 15:28 dev
drwxr-xr-x   1 root root 4096 Nov 25 15:28 etc
drwxrwxrwx   4 root root 4096 Nov 16 12:16 go
drwxr-xr-x   2 root root 4096 Sep  3 20:10 home
drwxr-xr-x   1 root root 4096 Nov 16 12:16 lib
drwxr-xr-x   2 root root 4096 Nov 14 08:00 lib64
drwxr-xr-x   2 root root 4096 Nov 14 08:00 media
drwxr-xr-x   2 root root 4096 Nov 14 08:00 mnt
drwxr-xr-x   2 root root 4096 Nov 14 08:00 opt
dr-xr-xr-x 159 root root    0 Nov 25 15:28 proc
drwx------   2 root root 4096 Nov 14 08:00 root
drwxr-xr-x   1 root root 4096 Nov 25 15:28 run
drwxr-xr-x   1 root root 4096 Nov 15 18:23 sbin
drwxr-xr-x   2 root root 4096 Nov 14 08:00 srv
dr-xr-xr-x  13 root root    0 Nov 25 15:28 sys
drwxrwxrwt   1 root root 4096 Nov 16 12:16 tmp
drwxr-xr-x   1 root root 4096 Nov 14 08:00 usr
drwxr-xr-x   1 root root 4096 Nov 14 08:00 var

容器进程目录下有什么?

[root@VM-4-6-centos work]# cd /proc/1050/
[root@VM-4-6-centos 1050]# ls
attr        comm             fd        map_files   net            pagemap      schedstat  statm    wchan
autogroup   coredump_filter  fdinfo    maps        ns             patch_state  sessionid  status
auxv        cpuset           gid_map   mem         numa_maps      personality  setgroups  syscall
cgroup      cwd              io        mountinfo   oom_adj        projid_map   smaps      task
clear_refs  environ          limits    mounts      oom_score      root         stack      timers
cmdline     exe              loginuid  mountstats  oom_score_adj  sched        stat       uid_map

查看 /mountinfo

[root@VM-4-6-centos 1050]# cat mountinfo | head -n 10
659 493 0:157 / / rw,relatime - overlay overlay rw,lowerdir=/var/lib/docker/overlay2/l/PIDEMVBEIEE3VONRURI4Z4EUJX:/var/lib/docker/overlay2/l/BWR7ZEPY4UYAKRS3ZCHZZ6EGTP:/var/lib/docker/overlay2/l/34HR5PAPLZ5473EG5FNPRQLLYQ:/var/lib/docker/overlay2/l/AGIW6BJEBXJNUEBZI67DHV73UC:/var/lib/docker/overlay2/l/6XDUP6AULW7Z4AT5NPHVTGD4SH:/var/lib/docker/overlay2/l/WRKLDD6PZE5FYTSPKPED7F3N7L:/var/lib/docker/overlay2/l/PLPE7MKV5TXKVZFBCSK7ZPA7FD:/var/lib/docker/overlay2/l/BT7MBYLOPX66TO6QWWYBELSZN2,upperdir=/var/lib/docker/overlay2/4ff3e2faabfa51f38c884c1321e20609352f5e009239d146161ec75bc806c3c5/diff,workdir=/var/lib/docker/overlay2/4ff3e2faabfa51f38c884c1321e20609352f5e009239d146161ec75bc806c3c5/work
660 659 0:160 / /proc rw,nosuid,nodev,noexec,relatime - proc proc rw
661 659 0:161 / /dev rw,nosuid - tmpfs tmpfs rw,mode=755
662 661 0:162 / /dev/pts rw,nosuid,noexec,relatime - devpts devpts rw,gid=5,mode=620,ptmxmode=666
663 659 0:163 / /sys ro,nosuid,nodev,noexec,relatime - sysfs sysfs ro
664 663 0:164 / /sys/fs/cgroup ro,nosuid,nodev,noexec,relatime - tmpfs tmpfs rw,mode=755
665 664 0:22 /system.slice/docker-cbd9c47e79e39457d638b8d0552aeb2c646dbd7ade72f41f8ffc48ea91188322.scope /sys/fs/cgroup/systemd ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd
666 664 0:24 /system.slice/docker-cbd9c47e79e39457d638b8d0552aeb2c646dbd7ade72f41f8ffc48ea91188322.scope /sys/fs/cgroup/blkio ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,blkio
667 664 0:25 /system.slice/docker-cbd9c47e79e39457d638b8d0552aeb2c646dbd7ade72f41f8ffc48ea91188322.scope /sys/fs/cgroup/hugetlb ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,hugetlb
668 664 0:26 /system.slice/docker-cbd9c47e79e39457d638b8d0552aeb2c646dbd7ade72f41f8ffc48ea91188322.scope /sys/fs/cgroup/net_prio,net_cls ro,nosuid,nodev,noexec,relatime - cgroup cgroup rw,net_prio,net_cls

可以看到容器已挂载了一个覆盖文件系统作为其根。它还报告与 docker inspect报告相同类型的信息,包括容器文件系统的 LowerDirUpperDir。它不直接显示 MergedDir,但您只需使用 UpperDir 并将 diff 更改为merged,您就可以查看容器的文件系统。

最后

希望我们都可以深入理解容器,能尝试其中的一些技术。一旦你体验到不再受容器代码限制的自由,你可能再也回不去了。它所需要的只是简单地访问 //proc/<pid>/root~