5分钟Docker容器原理 | namespace 观察和实践篇

《5分钟Docker容器原理 | 开篇》 提到,Docker容器 的隔离性是通过 Linux namespace 技术来实现的,本篇详细介绍下这个技术,并以一个Docker容器 作为切入点,验证和实践下Linux namespace的隔离性。

Linux namespace 的类型

名称 隔离对象 描述 内核版本
MNT namespace Mount points 提供磁盘挂载点和文件系统的隔离 2.4.19
IPC namespace System V IPC, POSIX message queues 提供进程间通信的隔离 2.6.19
UTS namespace Hostname and NIS domain name 提供主机名和域名的隔离 2.6.19
PID namespace Process IDs 提供进程的隔离 2.6.24
Net namespace Network devices, stacks, ports, etc. 提供网络的隔离 2.6.29
User namespace User and group IDs 提供用户和权限的隔离 3.8

Docker容器 本质上是一个进程,为了在分布式的环境下进行通信和定位,Docker容器必须拥有自己独立的IP、端口、路由,这个时候就需要用到Net namespace 提供的网络隔离能力。网络通信需要隔离,进程间通信也需要隔离,所以需要用IPC namespace 。还有用户权限、用户组权限,也需要和宿主机区分开,就用到了User namespace 。容器里面的进程也拥有自己的PID,这个是PID namespace 起到的隔离作用......

所以,Docker容器 本质上是一个带有不同的namespace 参数的进程,正是这些namespace 的存在, 容器内部就只能看到自己当前namespace 限定的资源、文件、设备、状态、配置等。

上面这几种namespace 类型,我们可以通过指令unshare --help来查看。

sql 复制代码
$ uname -r
3.10.0-1160.el7.x86_64
$ unshare --help

Usage:
 unshare [options] <program> [<argument>...]

Run a program with some namespaces unshared from the parent.

Options:
 -m, --mount               unshare mounts namespace
 -u, --uts                 unshare UTS namespace (hostname etc)
 -i, --ipc                 unshare System V IPC namespace
 -n, --net                 unshare network namespace
 -p, --pid                 unshare pid namespace
 -U, --user                unshare user namespace
 -f, --fork                fork before launching <program>
     --mount-proc[=<dir>]  mount proc filesystem first (implies --mount)
 -r, --map-root-user       map current user to root (implies --user)
     --propagation <slave|shared|private|unchanged>
                           modify mount propagation in mount namespace
 -s, --setgroups allow|deny  control the setgroups syscall in user namespaces

 -h, --help     display this help and exit
 -V, --version  output version information and exit

可以看到,当前OS的内核版本是3.10.0, 目前unshare支持的namespace 类型是6种,跟上面的表格是一致的。

大家如果想看到更具体的信息,可以使用man unshare指令看看详细的文档。

《离线环境部署docker及私有镜像仓库》 中,我们启动了一个Docker容器,接下来我们来观察下这个容器,以PID namespace为例,来进行简单的验证和实践。

PID namespace的观察

首先我们找到并使用sh进入容器

bash 复制代码
$ docker ps
CONTAINER ID   IMAGE             COMMAND                  CREATED      STATUS          PORTS                                       NAMES
98cb5c25871b   registry:latest   "/entrypoint.sh /etc..."   4 days ago   Up 39 minutes   0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   private-registry
$ docker exec -it 98cb5c25871b sh
/ # 

接下来我们打印当前容器运行的所有进程。可以看到容器内主进程registry的PID为1

arduino 复制代码
/ # ps -ef
PID   USER     TIME  COMMAND
    1 root      0:00 registry serve /etc/docker/registry/config.yml
   11 root      0:00 sh
   18 root      0:00 ps -ef

我们打开另外一个终端,在宿主机中查看docker相关进程

bash 复制代码
$ ps -ef | grep docker
......
root      1917     1  0 07:26 ?        00:00:01 /usr/bin/containerd-shim-runc-v2 -namespace moby -id 98cb5c25871b94420418094efb19f36c45c7fbe18274229e9f8c4b00328ec180 -address /var/run/docker/containerd/containerd.sock
root      1945  1917  0 07:26 pts/0    00:00:01 registry serve /etc/docker/registry/config.yml
......

由于显示的内容非常多,在这里我只截取了2个进程信息。可以看到registry进程在宿主机中的PID是1945,而且它的父进程ID正是容器进程的PID1917。这正是PID namespace的作用。

PID namespace的实践

我们可以用unshare指令来创建一个PID隔离的bash进程。如下

bash 复制代码
$ sudo unshare --pid --fork --mount-proc /bin/bash
# 
# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 08:16 pts/0    00:00:00 /bin/bash
root        10     1  0 08:16 pts/0    00:00:00 ps -ef
# sleep 1024 &
# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 08:16 pts/0    00:00:00 /bin/bash
root        13     1  0 08:22 pts/0    00:00:00 sleep 1024
root        14     1  0 08:22 pts/0    00:00:00 ps -ef

可以看到,我们的bash 进程,已经成为了1号进程,并且我们创建的sleep静默进程,此时的父进程ID为1

接下来我们在宿主机打印出相关进程

perl 复制代码
$ ps -ef | grep sleep
root     17301 15805  0 08:22 pts/0    00:00:00 sleep 1024
......
$ ps -ef | grep 15805
root     15805 15803  0 08:16 pts/0    00:00:00 /bin/bash
root     17301 15805  0 08:22 pts/0    00:00:00 sleep 1024
......

可以看到,宿主机中运行的bash进程PID为15805,而它的子进程sleep,在宿主机中的PID为17301

以上就是对PID namespace 的观察与实践,大家如果对其他Linux namespace 也感兴趣,可以参考下unshare的说明文档,继续做下相关的测试和验证。

相关推荐
__lll_3 小时前
手把手教你用 Docker 部署 Vue 项目(含国内镜像加速 + 踩坑指南)
docker
科大饭桶8 小时前
C++入门自学Day11-- String, Vector, List 复习
c语言·开发语言·数据结构·c++·容器
程思扬10 小时前
Nextcloud容器化部署革新:Docker+Cpolar构建高效私有云远程访问新架构
docker·容器·架构
豆芽脚脚12 小时前
docker compose再阿里云上无法使用的问题
阿里云·docker·容器
十行代码九行报错14 小时前
Docker基础学习笔记
笔记·学习·docker
Agome991 天前
Docker之自定义jkd镜像上传阿里云
阿里云·docker·容器
无能百分百1 天前
阿里云服务器ECS安装Docker(CentOS 7.x)
docker
deeper_wind1 天前
k8s-单主机Master集群部署+单个pod部署lnmp论坛服务(小白的“升级打怪”成长之路)
云原生·容器·kubernetes
zhenshanrenhao1 天前
#买硬盘欲安装k8s记
云原生·容器·kubernetes
tb_first1 天前
k8sday09
linux·云原生·容器·kubernetes