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的说明文档,继续做下相关的测试和验证。

相关推荐
longgggggggggggggggg2 小时前
curl -fsSL https://get.docker.com|sh 解释命令
docker
LuiChun3 小时前
docker django uwsgi 报错记录
docker·容器·django
tingting01193 小时前
docker 释放磁盘空间--常用清理命令
运维·docker·容器
杨浦老苏3 小时前
轻量级安全云存储方案Hoodik
docker·群晖·网盘
dessler3 小时前
Docker-Dockerfile案例(一)
linux·运维·docker
香吧香4 小时前
已有docker镜像构建过程分析
docker
小安运维日记7 小时前
CKA认证 | Day7 K8s存储
运维·云原生·容器·kubernetes·云计算
AR_xsy7 小时前
K8S--“ Failed to create pod sandbox: nameserver list is empty“
云原生·容器·kubernetes
码农炎可7 小时前
K8S 黑魔法之如何从 Pod 拿到节点的命令行
安全·云原生·容器·kubernetes
Just_Do_IT_OK7 小时前
Docker--MySql
mysql·docker·容器