docker exec的实现原理是什么-mile米乐体育
docker exec的实现原理是什么
本篇内容主要讲解“dockerexec的实现原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“dockerexec的实现原理是什么”吧!
我使用了 docker exec
命令进入到了容器当中。在了解了linux namespace
的隔离机制后,你应该会很自然地想到一个问题:docker exec 是怎么做到进入容器里的呢?
实际上,linux namespace
创建的隔离空间虽然看不见摸不着,但一个进程的 namespace
信息在宿主机上是确确实实存在的,并且是以一个文件的方式存在。
比如,通过如下指令,你可以看到当前正在运行的 docker 容器的进程号(pid)是 25686:
$dockerinspect--format'{{.state.pid}}'4ddf4638572d25686
这时,你可以通过查看宿主机的 proc 文件,看到这个 25686 进程的所有 namespace 对应的文件:
$ls-l/proc/25686/nstotal0lrwxrwxrwx1rootroot0aug1314:05cgroup->cgroup:[4026531835]lrwxrwxrwx1rootroot0aug1314:05ipc->ipc:[4026532278]lrwxrwxrwx1rootroot0aug1314:05mnt->mnt:[4026532276]lrwxrwxrwx1rootroot0aug1314:05net->net:[4026532281]lrwxrwxrwx1rootroot0aug1314:05pid->pid:[4026532279]lrwxrwxrwx1rootroot0aug1314:05pid_for_children->pid:[4026532279]lrwxrwxrwx1rootroot0aug1314:05user->user:[4026531837]lrwxrwxrwx1rootroot0aug1314:05uts->uts:[4026532277]
可以看到,一个进程的每种linux namespace
,都在它对应的 /proc/[进程号]/ns 下有一个对应的虚拟文件,并且链接到一个真实的 namespace 文件上。
有了这样一个可以“hold 住”所有 linux namespace 的文件,我们就可以对 namespace 做一些很有意义事情了,比如:加入到一个已经存在的 namespace 当中。
这也就意味着:一个进程,可以选择加入到某个进程已有的 namespace 当中,从而达到“进入”这个进程所在容器的目的,这正是 docker exec 的实现原理。
而这个操作所依赖的,乃是一个名叫 setns() 的 linux 系统调用。它的调用方法,我可以用如下一段小程序为你说明:
#define_gnu_source#include
这段代码功能非常简单:它一共接收两个参数,第一个参数是 argv[1],即当前进程要加入的 namespace 文件的路径,比如/proc/25686/ns/net
;而第二个参数,则是你要在这个 namespace
里运行的进程,比如 /bin/bash。
这段代码的的核心操作,则是通过 open() 系统调用打开了指定的 namespace
文件,并把这个文件的描述符 fd 交给 setns() 使用。在 setns() 执行后,当前进程就加入了这个文件对应的 linux namespace
当中了。
现在,你可以编译执行一下这个程序,加入到容器进程(pid=25686)的 network namespace 中:
$gcc-oset_nsset_ns.c$./set_ns/proc/25686/ns/net/bin/bash$ifconfigeth0linkencap:ethernethwaddr02:42:ac:11:00:02inetaddr:172.17.0.2bcast:0.0.0.0mask:255.255.0.0inet6addr:fe80::42:acff:fe11:2/64scope:linkupbroadcastrunningmulticastmtu:1500metric:1rxpackets:12errors:0dropped:0overruns:0frame:0txpackets:10errors:0dropped:0overruns:0carrier:0collisions:0txqueuelen:0rxbytes:976(976.0b)txbytes:796(796.0b)lolinkencap:localloopbackinetaddr:127.0.0.1mask:255.0.0.0inet6addr:::1/128scope:hostuploopbackrunningmtu:65536metric:1rxpackets:0errors:0dropped:0overruns:0frame:0txpackets:0errors:0dropped:0overruns:0carrier:0collisions:0txqueuelen:1000rxbytes:0(0.0b)txbytes:0(0.0b)
正如上所示,当我们执行 ifconfig
命令查看网络设备时,我会发现能看到的网卡“变少”了:只有两个。而我的宿主机则至少有四个网卡。这是怎么回事呢?
实际上,在 setns() 之后我看到的这两个网卡,正是我在前面启动的 docker 容器里的网卡。也就是说,我新创建的这个 /bin/bash 进程,由于加入了该容器进程(pid=25686)的 network namepace,它看到的网络设备与这个容器里是一样的,即:/bin/bash 进程的网络设备视图,也被修改了。
而一旦一个进程加入到了另一个 namespace 当中,在宿主机的 namespace 文件上,也会有所体现。
在宿主机上,你可以用 ps 指令找到这个 set_ns 程序执行的 /bin/bash 进程,其真实的 pid 是 28499:
#在宿主机上psaux|grep/bin/bashroot284990.00.0199443612pts/0s14:150:00/bin/bash
这时,如果按照前面介绍过的方法,查看一下这个 pid=28499 的进程的 namespace,你就会发现这样一个事实:
$ls-l/proc/28499/ns/netlrwxrwxrwx1rootroot0aug1314:18/proc/28499/ns/net->net:[4026532281]$ls-l/proc/25686/ns/netlrwxrwxrwx1rootroot0aug1314:05/proc/25686/ns/net->net:[4026532281]
在 /proc/[pid]/ns/net
目录下,这个 pid=28499 进程,与我们前面的 docker 容器进程(pid=25686)指向的 network namespace
文件完全一样。这说明这两个进程,共享了这个名叫net:[4026532281] 的 network
namespace。
此外,docker 还专门提供了一个参数,可以让你启动一个容器并“加入”到另一个容器的 network namespace 里,这个参数就是 -net,比如:
$dockerrun-it--netcontainer:4ddf4638572dbusyboxifconfig
这样,我们新启动的这个容器,就会直接加入到 id=4ddf4638572d
的容器,也就是我们前面的创建的应用容器(pid=25686)的network namespace
中。所以,这里 ifconfig 返回的网卡信息,跟我前面那个小程序返回的结果一模一样,你也可以尝试一下。
而如果我指定–net=host,就意味着这个容器不会为进程启用 network namespace
。这就意味着,这个容器拆除了 network namespace
的“隔离墙”,所以,它会和宿主机上的其他普通进程一样,直接共享宿主机的网络栈。这就为容器直接操作和使用宿主机网络提供了一个渠道。
到此,相信大家对“dockerexec的实现原理是什么”有了更深的了解,不妨来实际操作一番吧!这里是恰卡编程网网站,更多相关内容可以进入相关频道进行查询,关注mile米乐体育,继续学习!