『docker』 容器虚拟化技术之空间隔离实战

文章目录

  • [容器虚拟化基础之 NameSpace](#容器虚拟化基础之 NameSpace)
  • [NameSpace 隔离实战](#NameSpace 隔离实战)
    • 实战目的
    • 基础知识
      • [dd 命令详解](#dd 命令详解)
      • [mkfs 命令详解](#mkfs 命令详解)
      • [df 命令详解](#df 命令详解)
      • [mount 命令详解](#mount 命令详解)
      • [unshare 命令详解](#unshare 命令详解)
    • [实战操作一(PID 隔离)](#实战操作一(PID 隔离))
    • [实战操作二(Mount 隔离)](#实战操作二(Mount 隔离))

容器虚拟化基础之 NameSpace

什么是 Namespace(命名空间)

namespace 是 Linux 内核用来隔离内核资源的方式。通过 namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程根本就感觉不到对方的存在。具体的实现方式是把一个或多个进程的相关资源指定在同一个 namespace 中。

Linux namespaces 是对全局系统资源的一种封装隔离,使得处于不同 namespace 的进程拥有独立的全局系统资源,改变一个 namespace 中的系统资源只会影响当前namespace 里的进程,对其他 namespace 中的进程没有影响。

Linux 提供了多个 API 用来操作 namespace,它们是 clone()、setns() 和 unshare() 函数,为了确定隔离的到底是哪项 namespace,在使用这些 API 时,通常需要指定一些调用参数:CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER、CLONE_NEWUTS 和CLONE_NEWCGROUP。如果要同时隔离多个 namespace,可以使用 | (按位或)组合这些参数。

举个例子

三年一班的小明和三年二班的小明,虽说他们名字是一样的,但是所在班级不一样,那么,在全年级排行榜上面,即使出现两个名字一样的小明,也会通过各自的学号来区分。对于学校来说,每个班级就相当于是一个命名空间,这个空间的名称是班级号。班级号用于描述逻辑上的学生分组信息,至于什么学生分配到 1 班,什么学生分配到2 班,那就由学校层面来统一调度。

namespace 系统调用参数 被隔离的全局系统资源 引入内核版本
UTC CLONE_NEWUTS 主机名和域名 2.6.19
IPC CLONE_NEWIPC 进程间通信(信号量、消息队列、共享内存) 2.6.19
PID CLONE_NEWPID 进程编号 2.6.24
Network CLONE_NEWNET 网络设备、网络栈、端口等 2.6.29
Mount CLONE_NEWNS 文件系统挂载点 2.4.19
User CLONE_NEWUSER 用户和用户组 3.8

以上命名空间在容器环境下的隔离效果:

  • UTS:每个容器能看到自己的 hostname,拥有独立的主机名和域名。
  • IPC:同一个 IPC namespace 的进程之间能互相通讯,不同的 IPC namespace 之间不能通信。
  • PID:每个 PID namespace 中的进程可以有其独立的 PID,每个容器可以有其 PID 为1 的 root 进程。
  • Network:每个容器用有其独立的网络设备,IP 地址,IP 路由表,/proc/net 目录,端口号。
  • Mount:每个容器能看到不同的文件系统层次结构。
  • User:每个 container 可以有不同的 user 和 group id。

想想以下如果我们要隔离两个进程需要怎么办?

(1)首先容器进程与进程之间需要隔离,所以需要 PID 隔离;

(2)首先容器 A 进程不能读取容器 B 进程通讯内容需要隔离信号量等,所以需要 IPC 隔离;

(3)首先容器 A 进程不能读取容器 B 进程的文件,所以需要 Mount 隔离;

(4)首先容器 A 进程不能读取容器 B 进程的 socket,所以需要网络隔离、主机隔离;

(5)Docker 允许用户在主机和容器间共享文件夹,同时不需要限制容器的访问权限,这就容易让容器突破资源限制。需要借助用户空间来完成用户之间的隔离。

NameSpace 隔离实战

实战目的

通过 namespace 隔离实战我们就会知道隔离能力并不是 docker 提供的,而是操作系统内核提供的基本能力。

基础知识

dd 命令详解

Linux dd 命令用于读取、转换并输出数据。dd 可从标准输入或文件中读取数据,根据指定的格式来转换数据,再输出到文件、设备或标准输出。

  • 语法
bash 复制代码
$ dd OPTION
  • 参数
    • if=文件名:输入文件名,默认为标准输入。即指定源文件。
    • of=文件名:输出文件名,默认为标准输出。即指定目的文件。
    • ibs=bytes:一次读入 bytes 个字节,即指定一个块大小为 bytes 个字节。
    • obs=bytes:一次输出 bytes 个字节,即指定一个块大小为 bytes 个字节。
    • bs=bytes:同时设置读入/输出的块大小为 bytes 个字节。
    • cbs=bytes:一次转换 bytes 个字节,即指定转换缓冲区大小。
    • skip=blocks:从输入文件开头跳过 blocks 个块后再开始复制。
    • seek=blocks:从输出文件开头跳过 blocks 个块后再开始复制。
    • count=blocks:仅拷贝 blocks 个块,块大小等于 ibs 指定的字节数。
    • conv=<关键字>,关键字可以有以下 11 种:
      • conversion:用指定的参数转换文件。
      • ascii:转换 ebcdic 为 ascii
      • ebcdic:转换 ascii 为 ebcdic
      • ibm:转换 ascii 为 alternate ebcdic
      • block:把每一行转换为长度为 cbs,不足部分用空格填充
      • unblock:使每一行的长度都为 cbs,不足部分用空格填充
      • lcase:把大写字符转换为小写字符
      • ucase:把小写字符转换为大写字符
      • swap:交换输入的每对字节
      • noerror:出错时不停止
      • notrunc:不截短输出文件
      • sync:将每个输入块填充到 ibs 个字节,不足部分用空(NUL)字符补齐。
    • --help:显示帮助信息
    • --version:显示版本信息

示例

bash 复制代码
# 生成 1 个镜像文件
$ dd if=/dev/zero of=fdimage.img bs=8k count=10240
# 查看镜像文件
$ ls
fdimage.img
bash 复制代码
#将 testfile_1 文件中的所有英文字母转换为大写,然后转成为 testfile_2 文件
$ echo hello docker > testfile_1
$ dd if=testfile_2 of=testfile_1 conv=ucase 
$ cat testfile_1
hello docker
$ cat testfile_2
HELLO DOCKER

mkfs 命令详解

用于在设备上创建 Linux 文件系统,俗称格式化,比如我们使用 U 盘的时候可以格式化。

  • 语法
bash 复制代码
$ mkfs [-V] [-t fstype] [fs-options] filesys [blocks]
  • 参数
    • -t fstype:指定要建立何种文件系统;如 ext3,ext4;
    • filesys :指定要创建的文件系统对应的设备文件名;
    • blocks:指定文件系统的磁盘块数;
    • -V : 详细显示模式;
    • fs-options:传递给具体的文件系统的参数;

示例

bash 复制代码
#将 sda6 分区格式化为 ext4 格式
#$ mkfs -t ext4 /dev/sda6
#格式化镜像文件为 ext4
$ mkfs -t ext4 ./fdimage.img
mke2fs 1.42.9 (28-Dec-2013)
./fdimage.img is not a block special device.
Proceed anyway? (y,n) y
Discarding device blocks: done                            
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
20480 inodes, 81920 blocks
4096 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=33685504
10 block groups
8192 blocks per group, 8192 fragments per group
2048 inodes per group
Superblock backups stored on blocks: 
	8193, 24577, 40961, 57345, 73729

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done

df 命令详解

Linux df(英文全拼:disk free) 命令用于显示目前在 Linux 系统上的文件系统磁盘使用情况统计。

  • 语法
bash 复制代码
$ df [OPTION]... [FILE]...
  • 常见参数
    • -a, --all 包含所有的具有 0 Blocks 的文件系统;
    • -h, --human-readable 使用人类可读的格式(预设值是不加这个选项的...);
    • -H, --si 很像 -h, 但是用 1000 为单位而不是用 1024;
    • -t, --type=TYPE 限制列出文件系统的 TYPE;
    • -T, --print-type 显示文件系统的形式;

示例

bash 复制代码
#查看磁盘的系统类型
$ df -Th
Filesystem     Type      Size  Used Avail Use% Mounted on
devtmpfs       devtmpfs  909M     0  909M   0% /dev
tmpfs          tmpfs     919M     0  919M   0% /dev/shm
tmpfs          tmpfs     919M   97M  822M  11% /run
tmpfs          tmpfs     919M     0  919M   0% /sys/fs/cgroup
/dev/vda1      ext4       40G   30G  7.4G  81% /
tmpfs          tmpfs     184M     0  184M   0% /run/user/1003

mount 命令详解

mount 命令用于加载文件系统到指定的加载点。此命令的也常用于挂载光盘,使我们可以访问光盘中的数据,因为你将光盘插入光驱中,Linux 并不会自动挂载,必须使用 Linux mount 命令来手动完成挂载。

Linux 系统下不同目录可以挂载不同分区和磁盘设备,它的目录和磁盘分区是分离的,可以自由组合(通过挂载);

不同的目录数据可以跨越不同的磁盘分区或者不同的磁盘设备。挂载的实质是为磁盘添加入口(挂载点)。

  • mount 的常见用法
bash 复制代码
$ mount [-l]
$ mount [-t vfstype] [-o options] device dir
  • 常见参数

    • -l:显示已加载的文件系统列表;
    • -t: 加载文件系统类型支持常见系统类型的 ext3,ext4,iso9660,tmpfs,xfs 等,大部分情况可以不指定,mount 可以自己识别;
    • -o options 主要用来描述设备或档案的挂接方式。
      • loop:用来把一个文件当成硬盘分区挂接上系统
      • ro:采用只读方式挂接设备
      • rw:采用读写方式挂接设备
    • device: 要挂接(mount)的设备;
    • dir: 挂载点的目录;

示例

bash 复制代码
#将 /dev/hda1 挂在 /mnt 之下。
#$ mount /dev/hda1 /mnt

#将镜像挂载到/mnt/testext4 下面,需要确保挂载点也就是目录存在
sudo mkdir -p /mnt/testext
sudo mount ./fdimage.img /mnt/testext4

unshare 命令详解

unshare 主要能力是使用与父程序不共享的名称空间运行程序。

  • 语法
bash 复制代码
$ unshare [options] program [arguments]
  • 常用参数
    • -i, --ipc:不共享 IPC 空间
    • -m, --mount:不共享 Mount 空间
    • -n, --net:不共享 Net 空间
    • -p, --pid:不共享 PID 空间
    • -u, --uts:不共享 UTS 空间
    • -U, --user:不共享用户
    • -V, --version:版本查看
    • --fork:执行 unshare 的进程 fork 一个新的子进程,在子进
      程里执行 unshare 传入的参数
    • --mount-proc:执行子进程前,将 proc 优先挂载过去

示例

bash 复制代码
#hostname 隔离
[hxy@hcss-ecs-4c0e dockerTest]$ sudo unshare -u /bin/bash
[root@hcss-ecs-4c0e dockerTest]# hostname test1
[root@hcss-ecs-4c0e dockerTest]# hostname
test1
[root@hcss-ecs-4c0e dockerTest]# exit
exit
[hxy@hcss-ecs-4c0e dockerTest]$ hostname
hcss-ecs-4c0e

实战操作一(PID 隔离)

  1. 在主机上执行 ps -ef,可以看到进程列表如下,其中启动进程 PID 1 为 init 进程;

  2. 我们打开另外一个 shell ,执行下面命令创建一个 bash 进程,并且新建一个 PID Namespace:

    • --fork 新建了一个 bash 进程,是因为如果不建新进程,新的 namespace 会用 unshare的 PID 作为新的空间的父进程,而这个 unshare 进程并不在新的 namespace 中,所以会报个错 Cannot allocate memory;
    • --pid 表示我们的进程隔离的是 pid,而其他命名空间没有隔离;
    • mount-proc 是因为 Linux 下的每个进程都有一个对应的 /proc/PID 目录,该目录包含了大量的有关当前进程的信息。 对一个 PID namespace 而言,/proc 目录只包含当前namespace 和它所有子孙后代 namespace 里的进程的信息。创建一个新的 PIDnamespace 后,如果想让子进程中的 top、ps 等依赖 /proc 文件系统的命令工作,还需要挂载 /proc 文件系统。而文件系统隔离是 mount namespace 管理的,所以 linux 特意提供了一个选项--mount-proc 来解决这个问题。如果不带这个我们看到的进程还是系统的进程信息。
bash 复制代码
$ sudo unshare --fork --pid --mount-proc /bin/bash
  1. 执行 ps -ef 查看进程信息,我们可以看到此时进程空间内的内容已经变了,而且启动进程也变成了我们的 bash 进程。说明我们已经看不到主机上的进程空间了,我们的进程空间发生了隔离。
  1. 执行 exit 退出进程。

实战操作二(Mount 隔离)

  1. 打开第一个 shell 窗口 A,执行命令, df -h ,查看主机默认命名空间的磁盘挂载情况;
bash 复制代码
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        909M     0  909M   0% /dev
tmpfs           919M     0  919M   0% /dev/shm
tmpfs           919M   97M  822M  11% /run
tmpfs           919M     0  919M   0% /sys/fs/cgroup
/dev/vda1        40G   30G  7.4G  81% /
tmpfs           184M     0  184M   0% /run/user/1003
/dev/loop0       74M  1.6M   67M   3% /mnt/testext4
  1. 打开一个新的 shell 窗口 B,执行 Mount 隔离命令;
bash 复制代码
# --mount 表示我们要隔离 Mount 命名空间了
# --fork 表示新建进程
[hxy@hcss-ecs-4c0e dockerTest]$ sudo unshare --mount --fork /bin/bash
  1. 在窗口 B 中添加新的磁盘挂载;
bash 复制代码
[root@hcss-ecs-4c0e dockerTest]# dd if=/dev/zero of=fdimage.img bs=8k count=10240
10240+0 records in
10240+0 records out
83886080 bytes (84 MB) copied, 0.0731584 s, 1.1 GB/s
[root@hcss-ecs-4c0e dockerTest]# mkdir -p /data/tmpmount
[root@hcss-ecs-4c0e dockerTest]# mkfs -t ext4 ./fdimage.img 
mke2fs 1.42.9 (28-Dec-2013)
./fdimage.img is not a block special device.
Proceed anyway? (y,n) y
Discarding device blocks: done                            
Filesystem label=
OS type: Linux
Block size=1024 (log=0)
Fragment size=1024 (log=0)
Stride=0 blocks, Stripe width=0 blocks
20480 inodes, 81920 blocks
4096 blocks (5.00%) reserved for the super user
First data block=1
Maximum filesystem blocks=33685504
10 block groups
8192 blocks per group, 8192 fragments per group
2048 inodes per group
Superblock backups stored on blocks: 
	8193, 24577, 40961, 57345, 73729

Allocating group tables: done                            
Writing inode tables: done                            
Creating journal (4096 blocks): done
Writing superblocks and filesystem accounting information: done
[root@hcss-ecs-4c0e dockerTest]# mount ./fdimage.img /data/tmpmount/
  1. 在窗口 B 挂载的磁盘中添加文件;
bash 复制代码
[root@hcss-ecs-4c0e dockerTest]# echo "Helo world" > /data/tmpmount/hello.txt
  1. 查看窗口 B 中的磁盘挂载信息;
bash 复制代码
[root@hcss-ecs-4c0e dockerTest]# df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/vda1        40G   30G  7.4G  81% /
devtmpfs        909M     0  909M   0% /dev
tmpfs           919M     0  919M   0% /dev/shm
tmpfs           919M     0  919M   0% /sys/fs/cgroup
tmpfs           919M   97M  822M  11% /run
tmpfs           184M     0  184M   0% /run/user/1003
tmpfs           184M     0  184M   0% /run/user/0
/dev/loop0       74M  1.6M   67M   3% /mnt/testext4
/dev/loop1       74M  1.6M   67M   3% /data/tmpmount
  1. 查看窗口 A 中的磁盘挂载信息;
bash 复制代码
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
devtmpfs        909M     0  909M   0% /dev
tmpfs           919M     0  919M   0% /dev/shm
tmpfs           919M   97M  822M  11% /run
tmpfs           919M     0  919M   0% /sys/fs/cgroup
/dev/vda1        40G   30G  7.4G  81% /
tmpfs           184M     0  184M   0% /run/user/1003
/dev/loop0       74M  1.6M   67M   3% /mnt/testext4
tmpfs           184M     0  184M   0% /run/user/0
  1. 查看窗口 B 中的文件信息;
bash 复制代码
[root@hcss-ecs-4c0e dockerTest]# ll /data/tmpmount/
total 13
-rw-r--r-- 1 root root    11 Apr 23 11:09 hello.txt
drwx------ 2 root root 12288 Apr 23 11:08 lost+found
[root@hcss-ecs-4c0e dockerTest]# cat /data/tmpmount/hello.txt 
Helo world
  1. 查看窗口 A 中的文件信息,可以看到窗口 B 中新建的文件和磁盘挂载在主机的窗口中并没有,说明我们实现了文件系统隔离;
bash 复制代码
$ ll /data/tmpmount/
total 0
  1. 窗口 B 执行 exit,退出;
bash 复制代码
[root@hcss-ecs-4c0e dockerTest]# exit
exit
[hxy@hcss-ecs-4c0e dockerTest]$ 
相关推荐
Q_Q196328847520 分钟前
python的家教课程管理系统
开发语言·spring boot·python·django·flask·node.js·php
THMAIL1 小时前
mac M芯片运行docker-desktop异常问题
macos·docker·容器
两点王爷2 小时前
IDEA中springboot项目中连接docker
spring boot·docker·intellij-idea
lanbing2 小时前
PHP 与 面向对象编程(OOP)
开发语言·php·面向对象
家庭云计算专家2 小时前
还没用过智能文档编辑器吗?带有AI插件的ONLYOFFICE介绍
服务器·人工智能·docker·容器·编辑器
云手机管家4 小时前
CDN加速对云手机延迟的影响
运维·服务器·网络·容器·智能手机·矩阵·自动化
孤的心了不冷5 小时前
【Docker】CentOS 8.2 安装Docker教程
linux·运维·docker·容器·eureka·centos
头疼的程序员6 小时前
docker学习与使用(概念、镜像、容器、数据卷、dockerfile等)
学习·docker·容器
IT小郭.6 小时前
使用 Docker Desktop 安装 Neo4j 知识图谱
windows·python·sql·docker·知识图谱·database·neo4j
淡水猫.6 小时前
hbit资产收集工具Docker(笔记版)
运维·docker·容器