前言
上一次,我们说到了docker-for-mac
已经内置了最小化的k8s。我们在本地开发的时候,或多或少希望yaml配置
是最接近线上环境的配置。
因此,上一次,教会了大家,如何在mac上开启nfs,把我们的pv和本地mac的nfs进行一个通信。
这一次,我们来聊聊docker-for-mac
磁盘挂在的相关内容。
虚拟机
Docker for Mac
是一个原生的苹果应用程序,被安装到 /Application 目录
,安装时会创建 /usr/local/bin
目录下的 docker、docker-compose
符号链接。
Docker for Mac
使用通过 Hypervisor.framework 提供的轻量级的 xhyve 虚拟化技术Docker for Mac
不使用
docker-machine
管理虚拟机Docker for Mac
不通过 TCP 端口通信
,反而使用 docker.sock
套接字文件通信(实际上是将 /var/tmp 目录挂载到了虚拟机中,虚拟机在其中生成套接字文件)
但是尽管如此,你可以理解为还是存在虚拟机的。这个虚拟机的作用就是允许在macos上运行docker。
docker for mac
这个就是我们的docker-for-mac
的桌面版客户端。
根据上面这个图,我们可以发现,所有的镜像都存储在。
/Users/caiwenhui/Library/Containers/com.docker.docker/Data/vms/0/data
(记得修改为自己的路径,就是我这里的caiwenhui
)
1 | ➜ ~ ll /Users/caiwenhui/Library/Containers/com.docker.docker/Data/vms/0/data |
我们这里可以看到,这个文件,占用了96G
,就是我所分配的磁盘大小。并且这是一个经过了字节压缩的二进制文件。
这张图,算得上是我们今天的重点。
使用文件共享允许 Mac 上的本地目录与 Linux 容器共享。默认情况下/Users
,/Volume
、/private
、/tmp
和/var/folders
目录是共享的。如果您的项目在此目录之外,则必须将其添加到列表中。否则,您可能会在运行时得到Mounts denied
或cannot start service
出错。
因此,我们可以看到在默认情况下,有几个目录是已经被共享的了。我们重点需要关注的是/Users
目录,因为我们常常把我们的所有个人用户相关的东西,都会放在对应用户下,就我而言,我会把所有的代码都在 /Users/caiwenhui/www
下,这也意味着,我的所有代码,都将会被虚拟机
同步到linux系统
中,也正是因此,当你在MAC系统下,执行docker run -v $(PWD):/www xxx
之类的命令的时候,你可以成功的挂载到容器中。如果你把挂载的目录放在上述的几个目录之外,docker命令将会挂载失败
。
也是因为这个原因,你会发现,当你的代码,在下载大量依赖,或者在构建一堆索引的时候,或者你的/Users
目录下有很多文件在变动的时候,你会发现你的CPU
变成异常的高,而且会特别卡,可能你会觉得怎么那么卡。如果你不去查看哪个进程占用那么多cpu的话,你永远不会知道,其实大多数,显示出来都是docker的虚拟机
占用的cpu为大头就是因为这个原因。
获取虚拟机shell
既然,我们知道了上述的共享文件了,那么我们就会想知道,我要怎么去看,怎么去调试,或者我有什么更深的理解呢?
其实是有的,例如,我想看看,整个虚拟化技术,都挂载了什么数据卷构成我们的文件系统。
我们知道MacOS
上的Docker Desktop for mac
实际上是在Linux虚拟机中运行的Docker容器,这对于macOS主机上使用Docker多了一层虚拟化
。有些情况下,我们需要能够访问这个Linux虚拟机,以便实现一些hack
操作。
netcat
1 | ➜ ~ ll ~/Library/Containers/com.docker.docker/Data/debug-shell.sock |
我们可以看到,这里有一个名字叫debug-shell.sock
的文件,这是一个可执行的sock文件。
使用 nc
命令连接Docker的debug-shell socket
文件:
1 | ➜ ~ nc -U ~/Library/Containers/com.docker.docker/Data/debug-shell.sock |
显示的提示符比较奇怪,不过不影响使用
我们使用 df -h
命令可以看到Docker虚拟机的存储挂载:
1 | / # ^[[49;5Rdf -h |
使用命令 exit
或者^C
可以退出这个shell
进入shell,可以执行 . /etc/profile
获得环境
nsenter
使用nsenter从容器内部进入host主机的名字空间(namespace)
,但是对文件系统是只读
另外一种巧妙的方法是运行一个debian容器,然后在这个debian
容器中执行 nsenter
通过 pid=host
来实现进入到运行 Docker4Mac
的mini VM
的进程空间,这样就相当于进入了macOS的Docker虚拟机
在这个运行的debian容器中通过 nsenter 进入到host主机,也就是Docker VM名字空间以后,就可以看到虚拟机的提示符:
1 | ➜ ~ docker run -it --rm --privileged --pid=host debian nsenter -t 1 -m -u -n -i bash |
我们可以在这个Docker VM中执行网络检查
1 | bash-5.0# ip addr |
因为信息比较多,我们重点关注几个网卡信息
1 | 2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000 |
上面的信息,结合来看,我们在docker-for-mac
客户端设置了我们的虚拟机的网段 192.168.65.0/24
,结合eth0
和docker0
这2个网卡信息,我们会发现,eth0
的 192.168.65.3
, 就是我们这是的网段的信息,所以,这个虚拟机和macOS物理主机上对应的IP地址 192.168.65.1
对应,也就是说,如果我们使用 NFS 方式
挂载物理主机上的NFS卷,访问的NFS服务器端地址就是这样获得的。
这里还可以看到在Docker VM
上运行的Docker网络
是 172.17.xx.xx/16
,是一个NAT网络
,我们可以看到在Docker VM端
分配的IP地址是 172.17.0.1
。这也验证了我们的Docker VM
上实际上有2个网络
。
192.168.65.x/24
=> 和物理主机macOS
连接的NAT网络,用于虚拟机172.17.x.x/16
=> 和Docker0
连接的NAT网络,用于容器
在Docker容器中,通过两层NAT,依然可以访问外界Internet。不过,反过来,外部需要访问Docker容器就比较麻烦了,需要做端口映射
。
挂载
我们前面说到了,当你通过 docker run -v
的时候,你可以指定在共享目录
下的所有文件下,进行挂载进去。那么我们反过来想一下,既然是共享目录的话,那么容器是如何做到查找这些目录的呢?
通过mount -l
,我们可以看到挂载点。
1 | bash-5.0# mount -l |
可以看到,我们这里有大量的挂载点,这些挂载点,组合成了一个独立的文件系统,或者各种命名空间。
现在有一个需求。例如,我们知道在使用k8s的时候,当我们用pv
的type
为hostPath
的时候,是可以做到持久化数据磁盘的,那么我能不能做到,数据存放在Docker VM 非共享目录
中呢?
就是我的物理主机,并不希望同步这些数据。这些数据仅仅只需要存在Docker VM
中就好了。
当然,这个场景就是我们经常说,我们想要看一些容器的配置或者目录的时候,会发现输出的路径,在我们的宿主机上找不到,为什么会这样子?难道是出错了吗?其实只是这些文件都在Docker VM
中,其实就是因为这个原因导致的。只要我们进入到 Docker VM
就可以看到这些文件。
例如,以我目前的例子为例子,我需要采集我本地所有k8s-pods
的输出在终端的日志信息,我想通过promtail
来进行日志采集,然后通过loki
作为存储和查询服务器,再通过grafna
来进行展示。我们的服务以前台的方式进行启动,所以我们那么首先我需要解决的第一个问题,就是pods的标准输出到哪里?后来发现/var/log/pods/
。
1 | bash-5.0# ls -l /var/log/pods/ |
我们随便找一个pods,去查看日志。
1 | bash-5.0# tail -n 10 /var/log/pods/default_dev-grafana-7cd4c89fd4-wdkpb_cf869741-be0d-44d2-b776-3239dd276069/grafana/0.log |
现在,我们知道了日志是存储在这里了,那么我们就可以知道,在Docker VM
中,我们只需要挂载/var/log/pods
到我们的容器promtail
中,然后进行采集再推送到loki
,就可以通过grafna
查询了。
这个思路是没问题的,那么我们再往深程度的角度想,那么这个时候,我们的k8s-pv-type
应该填什么呢?刚才不是说了hostPath
是可以持久化到本地吗?但是那是针对物理主机macos
来说的,况且这个目录,我们并非在Docker VM
之下,这就回到了我们上面说的,在Docker VM
存在,在物理主机
不存在的需求。
那么我们这个时候,其实还是可以使用hostPath
的,理由很简单,因为k8s会识别路径,如果是在共享目录下的话,那么他会从共享目录的数据卷中找到对应的磁盘路径,如果不在共享目录下的,则从Docker VM
中查找,因此,这就是解开了我们这个疑惑了。可以大胆的放心使用hostPath
来创建pv
资源。
最后,附上一张最终的效果图: