整体含义是宿主机的dockremap账号一共有65536个从属用户,用户ID从10000-165535。这个从事用户的ID不是真实的,只是用来分配,它会从这个范围里拿一个ID映射到容器进程里的用户,比如容器进程还是用root用户,其UID实0,那么我们就可以从dockremap这个从属ID中拿一个来映射容器进程中的root。这样容器中看起来是root且具有root权限,但是在宿主机上它就是一个普通账号dockremap的权限。配置好后重启dockerd进程。配置好重新启动POD,如下:
同一个POD中的User Namespace是共享的,但此时它与宿主机的进程就已经不共享User Namespace了。再看一下uid_map
容器中的UID0映射到容器外的从属ID 10000。
不过这样虽然安全但是有些容器进程无论在容器内还是在容器外都需要root账号,比如prometheus的node_explorer,它是以DaemonSet形式运行的需要共享宿主机的网络名称空间,如果以上的用户来运行则会启动失败,如下图:
其实这个和DaemonSet没关系,主要是在docker上启用User Namspace后会有一些限制,userns-remap ,也就是说启用了User Namespace后容器将不能共享宿主机的PID和NET名称空间。所以我想因为有一些限制所以docker默认才不开启User Namespace。
实验证明:在默认情况下同一个POD是共享User Namespace的。
最简单的办法来验证一下在宿主机上找到该POD中的2个容器的容器ID,
通过docker inspect CONTINER_ID --format {{.State.Pid}}查看两个容器在宿主机上的进程号
通过进程ID查看每个进程的ns情况,左侧红色的是被查看进程名称空间文件,右侧则是该文件指向的具体的Namespace文件,中括号里面的是具体Namespace文件号,如果两个进程的指向的Namespace文件号相同,则说明它们处在同一名称空间。
红色箭头编号相同的就是当前POD中2个容器所共享的名称空间。不过在这里我也有些不明白,uts是共享的可是上图中看到的编号确不一样。因为在宿主机的当前终端运行unshare --uts /bin/bash命令将会在一个新的uts名称空间打开一个bash程序,这个bash进程和之前那个就是在不同的uts中,看下图:
进行namespace的api操作对Namespace的API操作包括clone()、setns()和unshare()。它们有一些不同:
clone():创建新进场的同时可以创建namespace,通过在这个函数中加入不同的名称空间标志来完成。
setns():它是加入一个已经存在的namespace,需要给它传递具体的namespace文件描述符。通常是在调用该函数之后调用clone(),其目的就是让一个新进程在一个已经存在的namespace中运行。docker exec就是利用这种机制让你指定的命令在容器中运行。
unshare():对当前的进程进行namespace隔离,换句话说它不启动新进程,而是让当前进程或者调用它的进程进入到一个新的namespace中。系统命令unshare就是利用这个调用来实现的。
注意在使用unshare系统调用或者命令或者setns系统调用的时候当涉及到PID Namespace的时候它的处理有些特殊,并不是让调用者进入新的PID Namespace,而是让子进程进入,成为该PID Namespace的1号进程。为什么为这样呢?因为一个进程的PID在系统中是常量,一但一个进程运行它的PID就确定了从而它的父子进程也会被确定,所以不能让它在调用setns或者unshare的时候发生变化,一但变化系统就无法维护这个进程表。
Namespace的资源隔离
Docker背后的内核知识1
Linux Namespace User
理解Docker容器的UID和GID
隔离Docker容器中的用户