目录
User/UTS/IPC/Network/... Namespace
什么是虚拟化、容器化
虚拟化技术最初起源于20世纪60年代末,主要是将计算机硬件(物理机)虚拟分区成一个或多个虚拟机,并提供多用户对大型计算机的交互访问。
容器具有极其轻量、秒级部署、易于移植、敏捷弹性伸缩等多种优势。
容器化和虚拟化是互补的。虚拟化是用来进行硬件资源划分的完美解决方案,通过hypervisor层来实现对资源的彻底隔离;而容器化是对OS级别的虚拟化,利用内核cgroup namespace特性,仅仅是进程本身的隔离。
因虚拟化是OS系统级隔离,而容器则是进程级隔离,相对于硬件虚拟化来说,容器的安全性更弱一些,因此需要一些额外的安全技术或安全容器方案来弥补。
关于容器技术(容器虚拟化)
容器技术的诞生,其主要目的是为解决PaaS层的技术实现,就像OpenStack、Cloudstack等技术为解决IaaS层的问题而诞生一样。是一种操作系统虚拟化,属于轻量级虚拟化技术。
容器技术之所以受欢迎,一是因为即使没有虚拟化的技术背景,也可以单独来学习容器虚拟化。二是因为它已经集成到了Linux内核中,已经被当做Linux内核原生提供的特性。
然而对于容器本身,并没有一个严格的定义,一般来说,它必须是一个相对独立的运行环境,这点上类似虚拟机的概念,但是又没有虚拟机那样彻底。另外,在一个容器环境内,应该最小化对外界(宿主)的影响,比如资源控制等等
一般来说容器技术主要包括Namespace 和 Cgroup两个内核特性
-
Namespace:
-
主要做访问隔离。针对一类资源进行抽象,并将其封装在一个独立的容器内,彼此不可见
-
-
Cgroup
-
主要做资源控制。
-
Namespce技术
Mount Namespace
Mount Namespace 是 Namespace 的一种类型,它允许不同的进程看到不同的文件系统视图。这意味着在一个 Mount Namespace 中进行的挂载和卸载操作仅在该 Namespace 中可见,对其他 Namespace 无影响。
以下实验:
-
如何使用unshare 创建mnt ns
-
如何进入某个mnt ns
-
验证mnt ns之间是隔离的
-
如何清理mnt ns
-
mnt ns是和某个进程绑定的,也就是该PID不存在了,所在mnt也会被清理掉
-
# 1. 登陆Linux,本测试使用CentOS7.5
# 2. 查看当前系统所有的mnt ns
> lsns -t mnt
NS TYPE NPROCS PID USER COMMAND
4026531840 mnt 87 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 21
4026531856 mnt 1 18 root kdevtmpfs
4026532121 mnt 1 406 root /usr/lib/systemd/systemd-udevd
4026532122 mnt 1 503 chrony /usr/sbin/chronyd
# 3. 查看当前所在mnt ns ----> 4026531840 ---> PID=1 系统默认mnt ns
# $$ --> 表示当前执行命令的PID,即ls -l 这条命令本身 --> PID=30775
> ls -l /proc/$$/ns/mnt
/proc/30775/ns/mnt -> mnt:[4026531840]
# 4. 打开另一个shell-B,同样查看mnt ns
# 5. 使用unshare创建一个mnt ns
# unshare 是一个 Linux 命令,用于在创建新的进程时从父进程分离出(或"unshare")一些特定的资源,例如命名空间(namespaces)
# --fork 表示创建创建一个ns之后立刻执行某个命令
# 默认会继承父进程所有的资源情况,包括mnt,当新的子命令是在新的隔离空间执行的
> unshare --mount --fork /bin/bash
# 6. 查看当前所有的mnt ns. 发现多了新的mnt ns 其ID --> 4026532493
> lsns -t mnt
NS TYPE NPROCS PID USER COMMAND
4026531840 mnt 92 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 21
4026531856 mnt 1 18 root kdevtmpfs
4026532121 mnt 1 406 root /usr/lib/systemd/systemd-udevd
4026532122 mnt 1 503 chrony /usr/sbin/chronyd
4026532493 mnt 3 5179 root unshare --mount --fork /bin/bash
# 6.1 在另一个Terminal上也查看一次
# 7. 查看当前所在mnt ns ----> 4026532493 ---> PID=5179 在新创建的mnt ns下
# $$ --> 表示当前执行命令的PID,即ls -l 这条命令本身 --> PID=5180
# 即,上述的unshare命令,默认创建一个新的mnt ns,并且进入到该mnt ns下。
# 也可以显示的进入某个mnt ns下 -- 执行把PID替换成目标mnt ns下的任何一个PID
# nsenter --mount=/proc/${PID}/ns/mnt -- findmnt
> ls -l /proc/$$/ns/mnt
/proc/5180/ns/mnt -> mnt:[4026532493]
# 8. 查看当前mnt ns中 mount的情况, 因为继承了PID=1的mount情况
> df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/nvme0n1p1 20G 4.6G 16G 23% /
devtmpfs 1.9G 0 1.9G 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
tmpfs 1.9G 193M 1.7G 11% /run
tmpfs 374M 0 374M 0% /run/user/1001
# 9. 查看当前mnt ns 多一个了mount点 tmpfs -> /tmp/xxx_tmpfs
> mkdir /tmp/xxx_tmpfs
> mount -t tmpfs -o size=20m tmpfs /tmp/xxx_tmpfs
> df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/nvme0n1p1 20G 4.6G 16G 23% /
devtmpfs 1.9G 0 1.9G 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
tmpfs 1.9G 193M 1.7G 11% /run
tmpfs 374M 0 374M 0% /run/user/1001
tmpfs 20M 0 20M 0% /tmp/xxx_tmpfs
# 4.1 登陆Linux,本测试使用CentOS7.5
# 4.2 查看当前系统所有的mnt ns
> lsns -t mnt
NS TYPE NPROCS PID USER COMMAND
4026531840 mnt 87 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 21
4026531856 mnt 1 18 root kdevtmpfs
4026532121 mnt 1 406 root /usr/lib/systemd/systemd-udevd
4026532122 mnt 1 503 chrony /usr/sbin/chronyd
# 4.3 查看当前所在mnt ns ----> 4026531840 ---> PID=1 系统默认mnt ns
# $$ --> 表示当前执行命令的PID,即ls -l 这条命令本身 --> PID=4594
> ls -l /proc/$$/ns/mnt
/proc/4594/ns/mnt -> mnt:[4026531840]
# 6.1 查看当前所有的mnt ns. 发现多了新的mnt ns 其ID --> 4026532493
> lsns -t mnt
NS TYPE NPROCS PID USER COMMAND
4026531840 mnt 93 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 21
4026531856 mnt 1 18 root kdevtmpfs
4026532121 mnt 1 406 root /usr/lib/systemd/systemd-udevd
4026532122 mnt 1 503 chrony /usr/sbin/chronyd
4026532493 mnt 2 5179 root unshare --mount --fork /bin/bash
# 7.1 进入新创建的mnt ns下,
> nsenter --mount=/proc/5179/ns/mnt
# 7.2 查看当前的mnt ns 是否为新创建的mnt ns
> ls -l /proc/$$/ns/mnt
/proc/5873/ns/mnt -> mnt:[4026532493]
# 7.3 进入PID=1mnt ns下, ---> 4026531840
> nsenter --mount=/proc/1/ns/mnt
# 8.1 查看当前PID=1中 mount的情况, 和 4026532493 是一样的。
> df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/nvme0n1p1 20G 4.6G 16G 23% /
devtmpfs 1.9G 0 1.9G 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
tmpfs 1.9G 193M 1.7G 11% /run
tmpfs 374M 0 374M 0% /run/user/1001
# 9.1 查看PID=1中的mount情况,并不能看到 4026532493 这个mnt ns中的新增的mount点
# 可以得出,不同mnt ns之间是隔离的。
> df -h
文件系统 容量 已用 可用 已用% 挂载点
/dev/nvme0n1p1 20G 4.6G 16G 23% /
devtmpfs 1.9G 0 1.9G 0% /dev
tmpfs 1.9G 0 1.9G 0% /dev/shm
tmpfs 1.9G 0 1.9G 0% /sys/fs/cgroup
tmpfs 1.9G 193M 1.7G 11% /run
tmpfs 374M 0 374M 0% /run/user/1001
# 10. PID=5179 是 /bin/bash那个命令
> kill -9 5179
PID Namespace
Linux宿主机上运行着成千上万的进程,默认情况下,容器内无法看到宿主机的进程表,因此,你的应用在容器启动时,pid为1。
#1. 查看当前所有pid ns
> lsns -t pid
NS TYPE NPROCS PID USER COMMAND
4026531836 pid 96 1 root /usr/lib/systemd/systemd --switched-root --system
--deserialize 21
#2. 创建新的pid空间 ---> 可以看到新的pid空间的,pid=1,
>unshare --pid --mount-proc --fork /bin/bash
>echo $$
1
>ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.0 115576 2048 pts/1 S 05:40 0:00 /bin/bash
root 10 0.0 0.0 155364 1864 pts/1 R+ 05:40 0:00 ps aux
#1.1 查看当前所有pid ns
> lsns -t pid
NS TYPE NPROCS PID USER COMMAND
4026531836 pid 96 1 root /usr/lib/systemd/systemd --switched-root --system
--deserialize 21
#2.1 再次查看当前所有pid ns ---> 多了一个 4026532494 其PID=9751
> lsns -t pid
NS TYPE NPROCS PID USER COMMAND
4026531836 pid 97 1 root /usr/lib/systemd/systemd --switched-root --system --deserialize 21
4026532494 pid 1 9751 root /bin/bash
#2.2 查看PID=9751 的进程树
>ps -ef |grep 9751
root 9751 9750 0 05:40 pts/1 00:00:00 /bin/bash
>ps -ef |grep 9750
root 9750 9014 0 05:40 pts/1 00:00:00 unshare --pid --mount-proc --fork /bin/bash
root 9751 9750 0 05:40 pts/1 00:00:00 /bin/bash
#2.3 即在新的pid ns中,虽然PID=1,但实际在宿主机器上PID=9751。在新的pid ns是看不到其他pid空间的进程的,实现了进程隔离
User/UTS/IPC/Network/... Namespace
用unshare命令,类似构建独立的ns,相互是隔离。大家可以自己试试