Docker实战02|Namespace

news2024/11/16 18:28:42

在上一文《Docker实战01|容器与开发语言》中主要介绍了Docker的基本概念与Docker安装、Go语言安装等实战技巧。

本文继续针对Namespace技术展开讲解并利用Go语言进行实践。

本系列所有代码均已经开源。关公众号回复「Go语言实现Docker」即可获得。

目录

  • 2.1.2 UTS Namespace
  • 2.1.3 IPC Namespace
  • 2.1.4 PID Namespace
  • 2.1.5 Mount Namespace
  • 2.1.6 User Namespace
  • 2.1.7 NetWork Namespace

第二章 基础技术

2.1 Linux Namespace 介绍

我们经常听到,Docker是一个使用了LinuxNamespace和Cgroups的虚拟化工具。但是,什么是LinuxNamespace,它在Docker内是怎么被使用的?说到这里,很多人就会迷茫。下面就先来介绍一下LinuxNamespace及它们是如何在容器中使用的。

2.1.1 概念

比如,一家公司向外界出售自己的计算资源。公司有一台性能还不错的服务器,每个用户买到一个tomcat实例用来运行它们自己的应用。有些调皮的客户可能不小心进入了别人的tomcat实例,修改或关闭了其中的某些资源,这样就会导致各个客户之间互相干扰。 也许你会说,我们可以限制不同用户的权限,让用户只能访问自己名下的tomcat实例,但是,有些操作可能需要系统级别的权限,比如root权限。我们不可能给每个用户都授予root权限,也不可能给每个用户都提供一台全新的物理主机让他们互相隔离。因此,LinuxNamespace在这里就派上了用场。**使用Namespace,就可以做到uid级别的隔离,也就是说,可以以uid为n的用户,虚拟化出来一个Namespace,在这个Namespace里面,用户是具有root权限的。但是,在真实 的物理机器上,他还是那个以uid为n的用户,这样就解决了用户之间隔离的问题。**当然这只是Namespace其中的一个简单功能。

除了UserNamespace,PID也是可以被虚拟的。命名空间建立系统的不同视图,从用户的角度来看,每一个命名空间应该像一台单独的Linux计算机一样,有自己的init进程(PID为I),其他进程的PID依次递增,A和B空间都有PID为1的init进程,子命名空间的进程映射到父命名空间的进程上,父命名空间可以知道每一个子命名空间的运行状态,而子命名空间与子命名空间之间是隔离的。PID映射关系图中可以看到,进程3在父命名空间中的PID为3,但是在子命名空间内,它的PID就是1。也就是说用户从子命名空间A内看进程3就像init进程一样,以为这个进程是自己的初始化进程,但是从整个host来看,它其实只是3号进程虚拟化出来的一个空间而己。


当前Linux一共实现了6种不同类型的Namespace。


Namespace的API主要使用如下3个系统调用 。

  • clone() 创建新进程。根据系统调用参数来判断哪些类型的Namespace被创建,而且它们的子进程也会被包含到这些Namespace中。
  • unshare() 将进程移出某个Namespace。
  • setns() 将进程加入到Namespace中。

2.1.2 UTS Namespace

UTS Namespace主要用来隔离nodename和domainname两个系统标识。在UTS Namespace里面,每个Namespace允许有自己的hostname。

下面将使用Go来做一个UTS Namespace的例子。其实对于Namespace这种系统调用,使用C语言来描述是最好的,但是本文的目的是去实现Docker,由于Docker就是使用Go开发的,所以就整体使用Go来讲解。先来看一下如下代码,非常简单。

package namespace

import (
	"log"
	"os"
	"os/exec"
	"syscall"
)

func main() {
	cmd := exec.Command("sh")
	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS,
	}

	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	if err := cmd.Run(); err != nil {
		log.Fatalln(err)
	}
}

执行go run main.go

root@i:~/go/src/docker# go run main.go 
sh-5.1#

查看以下是否真的进入了新的 UTS Namespace。
执行pstree -pl
重点关注:

├─sh(2309337)───node(2309347)─┬─node(2309457)─┬─bash(2325085)───go(2328552)─┬─main(2328610)─┬─bash(2328614)───go(2329013)─┬─main(2329063)─┬─sh(2329067)───pstree(2329644)

main程序pid为2328610,后续新创建的sh pid为2329067,现在查看二者uts是否相同即可:

sh-5.1# readlink /proc/2328610/ns/uts
uts:[4026531838]
sh-5.1# readlink /proc/2329067/ns/uts
uts:[4026532362]

可以看到它们确实不在同一个UTS Namespace中。由于UTS Namespace对hostname做了隔离,所以在这个环境内修改hostname应该不影响外部主机,在这个sh环境内执行如下代码示例。

下面来做一下实验。

sh-5.1# hostname sh
sh-5.1# hostname
sh

新开一个宿主机窗口,执行hostname

root@i# hostname
iZ2zed7lj4oetgoal2c8v7Z

可以看到外部的 hostname 并没有被修改影响,由此可了解 UTS Namespace 的作用。

2.1.3 IPC Namespace

IPC Namespace 用来隔离 sys V IPC和 POSIX message queues。

每个 IPC Namespace 都有自己的 Sys V IPC 和 POSIX message queues。

微调一下程序,只是修改了 Cloneflags,新增了 CLONE_NEWIPC,表示同时创建 IPC Namespace。

// 注: 运行时需要 root 权限。
func main() {
	cmd.SysProcAttr = &syscall.SysProcAttr{
		// Cloneflags: syscall.CLONE_NEWUTS,
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
	}

运行并且进行测试

# 先查看宿主机上的 ipc message queue
root@iZ2zed7Z:~# ipcs -q
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages 

# 然后创建一个 
root@iZ2zed7Z:~# ipcmk -Q
Message queue id: 0
# 再次查看,发现有了
root@iZ2zed7Z:~# ipcs -q
------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages    
0x75c132a7 0          root       644        0            0   

运行程序进入新的 shell


可以发现,在新的 Namespace 中已经看不到宿主机上的 message queue 了。说明 IPC Namespace 创建成功,IPC 已经被隔离。

2.1.4 PID Namespace

PID Namespace是用来隔离进程ID的。

这样就可以理解,在docker container 里面,使用ps -ef经常会发现,在容器内,前台运行的那个进程PID是1,但是在容器外,使用ps -ef会发现同样的进程却有不同的PID,这就是PID Namespace做的事情。

调整程序,增加 PID flags。

cmd.SysProcAttr = &syscall.SysProcAttr{
		// Cloneflags: syscall.CLONE_NEWUTS,
		// Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID,
	}

运行并测试:

root@iZ:~/go/src/docker# go run main.go
sh-5.1# pstree -pl
main(2332072)─┬─sh(2332076)───pstree(2332172)

可以看到mian 的pid为2332072,sh的pid为2332076。
在程序开的sh中执行echo $$

sh-5.1# echo $$
1

发现pid 是1,说明再新开的 PID Namespace 中只有一个 bash 这个进程,而且被伪装成了 1 号进程。

2.1.5 Mount Namespace

Mount Namespace用来隔离各个进程看到的挂载点视图。

在不同Namespace的进程中,看到的文件系统层次是不一样的。 在Mount Namespace中调用mount()和umount()仅仅只会影响当前Namespace内的文件系统,而对全局的文件系统是没有影响的。

需要注意的是,Mount Namespace 的 flag 是CLONE_NEWNS,直接是 NEWNS 而不是 NEWMOUNT,因为 Mount Namespace 是 Linux 中实现的第一个 Namespace,当时也没想到后续会有很多类型的 Namespace 加入。

再次修改代码,增加 Mount Namespace 的 flag。

	cmd.SysProcAttr = &syscall.SysProcAttr{
		// Cloneflags: syscall.CLONE_NEWUTS,
		// Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC,
		// Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID,
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS,
	}

运行并测试:

root@iZ2zed:~/go/src/docker# go run main.go
sh-5.1# ls /proc
1     120      15       1765  225      2309231  2318013  2330980  234   3      419  797  843  95          cpuinfo        iomem        locks         scsi           uptime
10    121      16       1766  2290557  2309337  2323926  2331826  235   30     420  8    844  951         crypto         ioports      mdstat        self           version
100   12477    1630162  1767  230      2309347  2324122  2332069  2359  303    421  80   845  96          devices        irq          meminfo       slabinfo       version_signature
1004  126      1655406  18    2309061  2309457  2325016  2332738  236   304    427  809  85   98          diskstats      kallsyms     misc          softirqs       vmallocinfo
101   13       1655423  1839  2309068  2309605  2325027  2332746  237   31     5    81   86   99          dma            kcore        modules       stat           vmstat
103   1319     1656492  19    2309090  2312349  2325085  2333183  24    32     6    812  87   acpi        driver         keys         mounts        swaps          zoneinfo
104   1399     1659802  2     2309111  2312407  2325297  2333225  240   33     603  82   88   bootconfig  dynamic_debug  key-users    mtrr          sys
105   14       166      20    2309157  2312634  2325563  2333229  25    34     765  820  90   buddyinfo   execdomains    kmsg         net           sysrq-trigger
1087  1449264  1756     21    2309158  2312635  2328552  2333289  253   4      767  83   919  bus         fb             kpagecgroup  pagetypeinfo  sysvipc
11    1449266  1762     22    2309159  2317057  2328610  2333330  26    40269  782  837  92   cgroups     filesystems    kpagecount   partitions    thread-self
117   1452065  1763     223   2309160  2317141  2328614  2333331  27    417    788  84   926  cmdline     fs             kpageflags   pressure      timer_list
12    149      1764     224   2309161  2317400  233      2333332  29    418    796  842  93   consoles    interrupts     loadavg      schedstat     tty

以看到,有一大堆文件,这是因为现在查看到的其实是宿主机上的 /proc 目录。

现在把 proc 目录挂载到当前 Namespace 中来:

sh-5.1# mount -t proc proc /proc
sh-5.1#  /proc
sh: /proc: Is a directory
sh-5.1# ls /proc
1           bus       crypto     dynamic_debug  interrupts  kcore        kpagecount  meminfo  net           scsi      swaps          timer_list         vmallocinfo
5           cgroups   devices    execdomains    iomem       keys         kpageflags  misc     pagetypeinfo  self      sys            tty                vmstat
acpi        cmdline   diskstats  fb             ioports     key-users    loadavg     modules  partitions    slabinfo  sysrq-trigger  uptime             zoneinfo
bootconfig  consoles  dma        filesystems    irq         kmsg         locks       mounts   pressure      softirqs  sysvipc        version
buddyinfo   cpuinfo   driver     fs             kallsyms    kpagecgroup  mdstat      mtrr     schedstat     stat      thread-self    version_signature

可以看到,少了一些文件,少的主要是数字命名的目录,因为当前 Namespace 下没有这些进程,自然就看不到对应的信息了。

此时就可以通过 ps 命令来查看了:


可以看到,在当前 Namespace 中 bash 为 1 号进程。

这就说明,当前 Mount Namespace 中的 mount 和外部是隔离的,mount 操作并没有影响到外部,Docker volume 也是利用了这个特性。

2.1.6 User Namespace

User Narespace 主要是隔离用户的用户组ID。

也就是说,一个进程的 UserID 和GroupID 在不同的 User Namespace 中可以是不同的。

比较常用的是,在宿主机上以一个非 root 用户运行创建一个 User Namespace, 然后在 User Namespace 里面却映射成 root 用户。这意味着,这个进程在 User Namespace 里面有 root 权限,但是在 User Namespace 外面却没有 root 的权限。

再次修改代码,增加 User Namespace 的 flag:

	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | syscall.CLONE_NEWUSER,
	}

运行并测试:

首先在宿主机上查看一个 user 和 group:


可以看到,此时是 root 用户。

运行程序,进入新的 sh 环境,可以看到,UID 是不同的,说明 User Namespace 生效了。

2.1.7 Network Namespace

Network Namespace 是用来隔离网络设备、IP 地址端口等网络栈的 Namespace。
Network Namespace 可以让每个容器拥有自己独立的(虛拟的)网络设备,而且容器内的应用可以绑定到自己的端口,每个 Namespace 内的端口都不会互相冲突。

在宿主机上搭建网桥后,就能很方便地实现容器之间的通信,而且不同容器上的应用可以使用相同的端口。

再次修改代码,增加 Network Namespace 的 flag:

	cmd.SysProcAttr = &syscall.SysProcAttr{
		Cloneflags: syscall.CLONE_NEWUTS | syscall.CLONE_NEWIPC | syscall.CLONE_NEWPID | syscall.CLONE_NEWNS | 
			syscall.CLONE_NEWUSER | syscall.CLONE_NEWNET,
	}

运行并测试:

首先看一下宿主机上的网络设备:


有 lo、eth0 等10个设备。
然后运行程序:


可以发现,新的 Namespace 中只有1个设备了,说明 Network Namespace 生效了。

小结

  • Docker 使用 namespace 进行隔离。
  • Namespace 的本质:Linux Namespace 是 Linux 提供的一种内核级别环境隔离的方法,本质就是对全局系统资源的一种封装隔离。

未完待续。。。

下一篇将会继续介绍Docker中Cgroup的核心原理。
所有Docker实战内容合集:《Docker就应该这么学》

本文所有内容都是基于「动手写Docker」此书。关注公众号,后台回复“动手写Docker”即可领取。

同时,准备了一份云原生实战大礼包送给大家,关注公众号,后台回复“云原生资料”即可领取。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1352741.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

如何评估 RAG 应用的质量?最典型的方法论和评估工具都在这里了

随着 LLM(Large Language Model)的应用逐渐普及,人们对 RAG(Retrieval Augmented Generation)场景的关注也越来越多。然而,如何定量评估 RAG 应用的质量一直以来都是一个前沿课题。 很显然,简单的几个例子的对比,并不能准确地衡量…

关于对物料计量单位的维护

1、业务背景 一般情况下,在设计产品时,明确了物料的计量单位,并在维护物料主数据时,维护完整单位数据。 但也有例外情况,例如当设计产品时,不明确未来的打包方式,不明确要维护哪些种计量单位&…

JVM虚拟机:各种JVM报错总结

错误 java.lang.StackOverflowError java.lang.OutOfMemoryError:java heap space java.lang.OutOfMemoryError:GC overhead limit exceeded java.lang.OutOfMemoryError:Direct buffer memory java.lang.OutOfMemoryError:unable to create new native thread java.lang.OutOf…

霹雳吧啦Wz《pytorch图像分类》-p2AlexNet网络

《pytorch图像分类》p2AlexNet网络基础及代码 一、零碎知识点1.过拟合2.使用dropout后的正向传播3.正则化regularization4.代码中所用的知识点 二、总体架构分析1.ReLU激活函数2.手算3.模型代码 三、训练花分类课程代码1.model.py2.train.py3.predict.py 一、零碎知识点 1.过拟…

LeetCode刷题---旋转图像

解题思路: 首先对主对角线两边的元素进行交换 接着走一轮遍历,将第1列和第n列进行交换,第2列和第n-1列进行交换,直至得到最终的矩阵。 代码实现: public void rotate(int[][] matrix) {//首先对主对角线的元素进行交换…

企业如何做好客户管理?有哪些关键因素?

客户管理是建立和维护客户关系的重要组成部分,对于企业的发展至关重要。下面就让我们来看看在做好客户管理时有哪些关键因素吧。 第一个关键因素是提供优质的客户服务。无论是线上还是线下,当客户需要帮助时,他们希望能够得到有效且及时的支持…

Spring ApplicationEvent事件处理

Spring的事件 ApplicationEvent以及Listener是Spring为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式,设计初衷也是为了系统业务逻辑之间的解耦,提高可扩展性以及可维护性。 ApplicationEvent就是Spring的事件接口Applic…

南昌找工作用什么APP或者招聘网站

南昌找工作用吉鹿力招聘网 通过吉鹿力招聘网,可以随时查看最新职位,跟踪简历投递动态,与正在进行招聘的CEO、部门负责人、HR在线沟通,查看其他候选人面试该职位后对面试官、公司环境的面试评价等,为求职者提供参考。 …

阿里云2核2G3M服务器能放几个网站?有限制吗?

阿里云2核2g3m服务器可以放几个网站?12个网站,阿里云服务器网的2核2G服务器上安装了12个网站,甚至还可以更多,具体放几个网站取决于网站的访客数量,像阿里云服务器网aliyunfuwuqi.com小编的网站日访问量都很少&#xf…

2023年度最热 AI 应用 TOP 50,除了 ChatGPT 还有这么多宝藏

原文章链接:年度最热 AI 应用 TOP 50,除了 ChatGPT 还有这么多宝藏 - IT之家 更多消息:AI人工智能行业动态,aigc应用领域资讯 在 AI 工具激烈竞争的一年中,尽管ChatGPT在访问量上遥遥领先,但单次使用时长未…

扫码出入库让仓库管理更加智能化

一、扫码出入库管理的概念 扫码出入库管理系统是一种基于条码技术的仓库管理系统。它通过扫描条码的方式,实现货物的自动化入库、存储、打包和出库。条码技术是一种利用光学识别原理对字符进行识别的技术,具有高效、准确、便捷的特点。 二、扫码出入库能…

B端产品经理学习-需求挖掘

B端产品需求挖掘 目录 识别和管理干系人 决策人和负责人需求挖掘 针对用户进行需求挖掘 用户访谈结果整理 B端产品的需求来源是非常复杂的,要考虑多个方面;如果你是一个通用性的产品,要考虑市场、自身优劣势、干系人。而定制型B端产品会…

react useEffect 内存泄漏

componentWillUnmount() {this.setState (state, callback) > {return;};// 清除reactionthis.reaction();}useEffect 使用AbortController useEffect(() > { let abortController new AbortController(); // your async action is here return () > { abortCo…

2024年阿里云优惠券领取及使用教程

阿里云作为国内领先的云计算服务提供商,一直致力于为客户提供优质、高效的服务。为了更好地回馈客户,阿里云经常会推出各种优惠活动,其中就包括阿里云优惠券。本文将详细介绍如何领取及使用阿里云优惠券。 一、阿里云优惠券介绍 阿里云优惠券…

勒索检测能力升级,亚信安全发布《勒索家族和勒索事件监控报告》

评论员简评 近期(12.08-12.14)共发生勒索事件119起,相较之前呈现持平趋势。 与上周相比,本周仍然流行的勒索家族为lockbit3和8base。在涉及的勒索家族中,活跃程度Top5的勒索家族分别是:lockbit3、siegedsec、dragonforce、8base和…

RocketMQ5.0顺序消息设计实现

前言 顺序消息是 RocketMQ 提供的一种高级消息类型,支持消费者按照发送消息的先后顺序获取消息,从而实现业务场景中的顺序处理。 顺序消息的顺序关系通过消息组(MessageGroup)判定和识别,发送顺序消息时需要为每条消息…

whl is not a supported wheel on this platform.解决办法

1.问题: 安装torch产生 2.解决办法: 使用pip debug --verbose查看 对应的torch版本号 Compatible tags字样,这些就是当前Python版本可以适配的标签。例如,我的Python版本是3.11,可以匹配下面这些文件名:…

YOLOv8改进 更换轻量化模型MobileNetV3

一、MobileNetV3论文 论文地址:1905.02244.pdf (arxiv.org) 二、 MobileNetV3网络结构 MobileNetV3引入了一种新的操作单元,称为"Mobile Inverted Residual Bottleneck",它由一个1x1卷积层和一个3x3深度可分离卷积层组成。这个操…

简易电子琴

#include<reg51.h> //包含51单片机寄存器定义的头文件 sbit P14P1^4; //将P14位定义为P1.4引脚 sbit P15P1^5; //将P15位定义为P1.5引脚 sbit P16P1^6; //将P16位定义为P1.6引脚 sbit P17P1^7; //将P17位定义为P1.7引脚 unsigned char keyval; …

Sam Altman的一天被曝光!每天15小时禁食、服用小剂量安眠药,尽可能避免开会

Sam Altman在经历了几天混乱的管理重组后&#xff0c;重新回到了OpenAI的CEO位置。在日常生活中&#xff0c;奥特曼与许多科技行业高管一样&#xff0c;痴迷于延长自己的寿命。 据报道&#xff0c;他还为应对末日场景&#xff08;致命合成病毒的释放、核战争和人工智能攻击等&…