docker学习 linux资源隔离 namespace
一、Namespace简介
namespace是Linux内核的一项功能,该功能对内核资源进行分区,以使一组进程看到一组资源,而另一组进程看到另一组资源。
Namespace 的工作方式通过为一组资源和进程设置相同的 Namespace 而起作用,但是这些 Namespace 引用了不同的资源。资源可能存在于多个 Namespace 中。这些资源可以是进程 ID、主机名、用户 ID、文件名、与网络访问相关的名称和进程间通信。
Docker利用namespace特性,实现了每个容器的资源相互隔离,从而保证容器内部只能访问到自己的namespace资源。
docker使用了6中namespace,Mount Namespace、PID Namespace、Net Namespace、IPC Namespace、UTS Namespace、User Namespace。
二、Linux对Namespace操作方法
- clone
在创建新进程的系统调用时,可以通过 flags 参数指定需要新建的 Namespace 类型
- setns
该系统调用可以让调用进程进入某个已经存在的Namespace中
- unshare
该系统调用可以将调用进程移动到新的Namespace下
namespace的常用命令
查看当前系统的namespace
lsns -t <type>
[root@jay ~]# lsns -t net
NS TYPE NPROCS PID USER NETNSID NSFS COMMAND
4026531992 net 110 1 root unassigned /usr/lib/systemd/systemd --switched-root --system --deserialize 18
4026532203 net 2 30304 root unassigned unshare --net --fork /bin/bash
[root@jay ~]# unshare --net --fork /bin/bash
[root@jay ~]# lsns -t net
NS TYPE NPROCS PID USER NETNSID NSFS COMMAND
4026531992 net 109 1 root unassigned /usr/lib/systemd/systemd --switched-root --system --deserialize 18
4026532203 net 2 30304 root unassigned unshare --net --fork /bin/bash
4026532258 net 3 35768 root unassigned unshare --net --fork /bin/bash
查看某进程的namespace:
ls -la /proc/<pid>/ns/
进入某namespace运行命令:
nsenter -t <pid> -n ip addr
三、Namespaces介绍
1、Mount Namespace
用于实现 在不通的进程中看到不通的挂载目录。
使用Mount Namespace 可以实现容器内只能看到自己的挂载信息,在容器内挂载操作不会影响主机的挂载目录。
使用unshare命令可以创建并访问不通类型的Namespace。
# 创建一个 bash 进程并且新建一个 Mount Namespace
$ sudo unshare --mount --fork /bin/bash
# 在 /tmp 目录下创建一个目录
[root@centos7 centos]# mkdir /tmp/tmpfs
# 使用 mount 命令挂载一个 tmpfs 类型的目录
[root@centos7 centos]# mount -t tmpfs -o size=20m tmpfs /tmp/tmpfs
# df 命令查看一下已经挂载的目录信息
# 可以看到 /tmp/tmpfs 目录已经被正确挂载
[root@centos7 centos]# df -h
Filesystem Size Used Avail Use% Mounted on
/dev/vda1 500G 1.4G 499G 1% /
devtmpfs 16G 0 16G 0% /dev
tmpfs 16G 0 16G 0% /dev/shm
tmpfs 16G 0 16G 0% /sys/fs/cgroup
tmpfs 16G 57M 16G 1% /run
tmpfs 3.2G 0 3.2G 0% /run/user/1000
tmpfs 20M 0 20M 0% /tmp/tmpfs
# 新打开一个命令行窗口, df 命令查看主机的挂载信息
[centos@centos7 ~]$ df -h
Filesystem Size Used Avail Use% Mounted on
devtmpfs 16G 0 16G 0% /dev
tmpfs 16G 0 16G 0% /dev/shm
tmpfs 16G 57M 16G 1% /run
tmpfs 16G 0 16G 0% /sys/fs/cgroup
/dev/vda1 500G 1.4G 499G 1% /
tmpfs 3.2G 0 3.2G 0% /run/user/1000
# 主机上并没有挂载 /tmp/tmpfs
# 在当前命令行窗口查看一下当前进程的 Namespace 信息
[root@centos7 centos]# ls -l /proc/self/ns/
total 0
lrwxrwxrwx. 1 root root 0 Sep 4 08:20 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 root root 0 Sep 4 08:20 mnt -> mnt:[4026532239]
lrwxrwxrwx. 1 root root 0 Sep 4 08:20 net -> net:[4026531956]
lrwxrwxrwx. 1 root root 0 Sep 4 08:20 pid -> pid:[4026531836]
lrwxrwxrwx. 1 root root 0 Sep 4 08:20 user -> user:[4026531837]
lrwxrwxrwx. 1 root root 0 Sep 4 08:20 uts -> uts:[4026531838]
# 新打开一个命令行窗口,使用相同的命令查看一下主机上的 Namespace 信息
[centos@centos7 ~]$ ls -l /proc/self/ns/
total 0
lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 net -> net:[4026531956]
lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 pid -> pid:[4026531836]
lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 user -> user:[4026531837]
lrwxrwxrwx. 1 centos centos 0 Sep 4 08:20 uts -> uts:[4026531838]
# 除了 Mount Namespace 的 ID 值不一样外,其他Namespace 的 ID 值均一致。
2、PID Namespace
用于隔离进程。在不同的 PID Namespace 中,进程可以拥有相同的 PID 号。利用 PID Namespace 可以实现每个容器的主进程为 1 号进程,而容器内的进程在主机上却拥有不同的PID。例如一个进程在主机上 PID 为 122,使用 PID Namespace 可以实现该进程在容器内看到的 PID 为 1。
# 创建一个 bash 进程,并且新建一个 PID Namespace
$ sudo unshare --pid --fork --mount-proc /bin/bash
# 在当前的命令行窗口使用 ps aux 命令查看一下进程信息
[root@centos7 centos]# ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 115544 2004 pts/0 S 10:57 0:00 bash
root 10 0.0 0.0 155444 1764 pts/0 R+ 10:59 0:00 ps aux
# 当前 Namespace 下 bash 为 1 号进程,而且我们也看不到主机上的其他进程信息
# 打开新的窗口执行ps ef查看进程
[root@centos7 centos]# ps -ef |grep /bin/bash
root 1103524 1103189 0 10:03 pts/0 00:00:00 unshare --pid --fork --mount-proc /bin/bash
root 1103525 1103524 0 10:03 pts/0 00:00:00 /bin/bash
root 1103718 1103631 0 10:04 pts/1 00:00:00 grep --color=auto /bin/bash
#可以看到PID实际上是1103525
3、UTS Namespace
UTS Namespace 主要是用来隔离主机名的,它允许每个 UTS Namespace 拥有一个独立的主机名。例如主机名称为 docker,使用 UTS Namespace 可以实现在容器内的主机名称为 abc 或者其他任意自定义主机名。
# 创建一个 bash 进程,并且新建一个 UTS Namespace
$ sudo unshare --uts --fork /bin/bash
# 使用 hostname 命令(hostname 可以用来查看主机名称)设置一下主机名
[root@centos7 centos]# hostname -b lagoudocker
# 查看一下主机名
[root@centos7 centos]# hostname
abc
# 新打开一个命令行窗口,查看一下主机名
[centos@centos7 ~]$ hostname
centos7
4、IPC Namespace
IPC Namespace 主要是用来隔离进程间通信的。例如 PID Namespace 和 IPC Namespace 一起使用可以实现同一 IPC Namespace 内的进程彼此可以通信,不同 IPC Namespace 的进程却不能通信。
# 创建一个 bash 进程,并且新建一个 IPC Namespace
$ sudo unshare --ipc --fork /bin/bash
# ipcs -q 命令查看一下当前 IPC Namespace 下的系统通信队列列表
[centos@centos7 ~]$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
# 当前无任何系统通信队列
# ipcmk -Q 命令创建一个系统通信队列
[root@centos7 centos]# ipcmk -Q
Message queue id: 0
# 再次使用 ipcs -q 命令查看当前 IPC Namespace 下的系统通信队列列表
[root@centos7 centos]# ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
0x73682a32 0 root 644 0 0
# 新打开一个命令行窗口,使用ipcs -q 命令查看一下主机的系统通信队列:
[centos@centos7 ~]$ ipcs -q
------ Message Queues --------
key msqid owner perms used-bytes messages
# IPC Namespace 内创建的系统通信队列在主机上无法看到
5、User Namespace
User Namespace 主要是用来隔离用户和用户组的。一个比较典型的应用场景就是在主机上以非 root 用户运行的进程可以在一个单独的 User Namespace 中映射成 root 用户。使用 User Namespace 可以实现进程在容器内拥有 root 权限,而在主机上却只是普通用户。
# 创建一个 bash 进程,并且新建一个 User Namespace
[centos@centos7 ~]$ unshare --user -r /bin/bash
# CentOS7 默认允许创建的 User Namespace 数量为 0,如果执行上述命令失败( unshare 命令返回的错误为 unshare: unshare failed: Invalid argument ),需要使用以下命令修改系统允许创建的 User Namespace 数量,命令为:echo 65535 > /proc/sys/user/max_user_namespaces,然后再次尝试创建 User Namespace。
# 执行 id 命令查看一下当前的用户信息
[root@centos7 ~]# id
uid=0(root) gid=0(root) groups=0(root),65534(nfsnobody) context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
# 可以看到,在新的 User Namespace 内已经是 root 用户了
# 使用只有主机 root 用户才可以执行的 reboot 命令来验证一下,在当前命令行窗口执行 reboot 命令
[root@centos7 ~]# reboot
Failed to open /dev/initctl: Permission denied
Failed to talk to init daemon.
# 在隔离的 User Namespace 中,并不能获取到主机的 root 权限
6、Net Namespace
Net Namespace 是用来隔离网络设备、IP 地址和端口等信息的。Net Namespace 可以让每个进程拥有自己独立的 IP 地址,端口和网卡信息。例如主机 IP 地址为 172.16.4.1 ,容器内可以设置独立的 IP 地址为 192.168.1.1。
# 创建一个 bash 进程,并且新建一个 Net Namespace
$ sudo unshare --net --fork /bin/bash
# 使用 ip a 命令查看一下网络信息:
[root@centos7 centos]# ip a
1: lo: <LOOPBACK> mtu 65536 qdisc noop state DOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
# 新打开一个命令行窗口,使用 ip a 命令查看一下主机上的网络信息
$ ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP group default qlen 1000
link/ether 02:11:b0:14:01:0c brd ff:ff:ff:ff:ff:ff
inet 172.20.1.11/24 brd 172.20.1.255 scope global dynamic eth0
valid_lft 86063337sec preferred_lft 86063337sec
inet6 fe80::11:b0ff:fe14:10c/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN group default
link/ether 02:42:82:8d:a0:df brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:82ff:fe8d:a0df/64 scope link
valid_lft forever preferred_lft forever
#宿主机上有 lo、eth0、docker0 等网络设备,而新建的 Net Namespace 内则与主机上的网络设备不同。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
2022-03-20 python lambda函数