当前位置: 首页 > Linux

Linux容器与Namespace

时间:2023-04-06 06:57:32 Linux

云原生技术可以说是互联网上最火的概念之一。作为云原生技术的重要基石——“容器”技术,从业者想必很早以前就已经了解,并将其运用到工作和日常生活中进行开发。如果有人问你“什么是容器?”,你会怎么回答?容器只是Docker吗?它与虚拟化技术有什么区别和联系?它的实现原理是什么?本文主要通过容器与虚拟化技术的比较,容器与LinuxNamespace的关系,以及Namespace的初体验,让大家进一步了解什么是容器以及容器实现的底层技术支撑——LiunxNamespace。有Linux容器和虚拟化学习经验的同学,一定有使用VMware、VirtualBox等软件安装Linux虚拟机的经验。一般是这样的流程。我们先在windows机器上安装VMware或者VirtualBox等Hypervisor虚拟化软件,然后利用这类虚拟化软件在Linux虚拟机中安装一个Linux发行版镜像,这样我们就有了一个Linux操作系统。这种虚拟化软件是运行在基础物理服务器和操作系统之间的一个中间软件层,它允许多个操作系统和应用程序共享硬件。这样,我们就得到了一个虚拟机,包括所有的硬件、软件和内核,可以说是一个功能完备的操作系统,与宿主机是隔离的。我们可以放心大胆地对操作系统进行任何改动,而不用担心损坏宿主机。资源的隔离类型和功能的相似,可以说和容器的外观特别相似。刚开始接触Linux容器时,习惯性地将容器与虚拟机进行类比,认为容器是一种轻量级的虚拟化技术,只是没有硬件资源的虚拟化,加上运行应用系统所必需的最小文件。不知道有多少人和我有过相同的理解。不得不说,这种认识对于刚接触容器的人理解容器确实有帮助,但是这种说法是不恰当的,因为容器的实现机制、Hypervisor等虚拟化技术只有精髓,没有别的。容器和命名空间容器本质上是运行在Linux上的特殊进程。它们之所以特别,是因为它们与操作系统上的其他进程环境隔离。就像一个容器,站在容器中,你只能看到它自己。容器技术的基础是Linux命名空间和cgroups内核特性。隔离特性保证了不同容器进程之间互不干扰,有自己独立的运行环境。我们知道PID在操作系统上是唯一的,不能同时有一个PID=1的进程A,和一个PID=1的进程B;端口号也是唯一的,我们不能让多个进程同时监听同一个端口(fork子进程的特殊方式除外)。我们也不能同时让操作系统主机名是server1和主机名是server2。但是我们可以在容器A中有一个PID=1的进程,在容器B中有一个PID也等于1的进程,这就是容器隔离的体现。它是自己的世界,开辟了任意空间(自己的文件系统),有自己的网络设备,有自己的主机名。这种隔离依赖于内核特性——命名空间,它允许一个进程运行在一个独立的命名空间中。命名空间中的进程与系统进程是隔离的,不同命名空间中的进程之间是相互隔离的,所以我们要了解容器,首先要了解命名空间。命名空间是内核2.4开始的特性。Namespace有以下几种:Namespace隔离资源CgroupCgroup根目录IPCSystemVIPC、POSIX消息队列Network网络设备、端口Mount挂载点PID进程IDTimeclockUser用户和用户组IDUTS主机名、域名需要注意的是以上类型的命名空间是从内核2.4开始逐渐加入的,2.4实现了mount,2.6加入了IPC、Network、PID、UTS,User是2.6开始出现的,直到3.8才公布。Cgroup仅在4.6中可用。上面我们提到,由于PID的隔离特性,不同的容器可以有相同的PID,比如namespace、User、UTS。隔离特性的命名空间允许同一主机上的不同容器拥有相同的用户id、组id和主机名。Namespace的内核特性是让一组进程运行在一个独立的空间中,从而与操作系统上的进程隔离开来。命名空间的初体验相信很多朋友在刚接触命名空间的时候都是一头雾水。在系统开始补全之前需要说明一下,Linux提供了clone、setns、unshare、ioctl等系统调用API来实现对Namespace及其进程的操作,不过这个是后话了,这个初体验我们使用LinuxA与系统调用函数unshare同名的命令行工具(实际上调用的是系统调用unshare)查看其用法:#unshare-hUsage:unshare[options][[...]]运行一个程序,其中一些命名空间从父级取消共享。选项:-m,--mount[=]unsharemountsnamespace-u,--uts[=]unshareUTSnamespace(hostnameetc)-i,--ipc[=]unshareSystemVIPCnamespace-n,--net[=]unsharenetworknamespace-p,--pid[=]unsharepidnamespace-U,--user[=]unshareusernamespace-C,--cgroup[=]unsharecgroupnamespace-f,--forkforkbeforelaunching--mount-proc[=

]mountproc文件系统优先(隐含--mount)-r,--map-root-user将当前用户映射到root(隐含--user)--propagationslave|shared|private|unchanged修改挂载命名空间中的挂载传播-s,--setgroupsallow|deny控制用户命名空间中的setgroupssyscall-h,--help显示这个帮助-V,--version显示版本UTSNamespace初体验UTSNamespace可以实现主机名和域名的隔离#hostname#查看当前主机名isserver0server0#unshare-u/bin/sh#创建一个新进程并在UTS命名空间中#hostnamenew-hostname#修改主机名#hostname#当前进程的主机名已经改变new-hostname#exit#退出UTS命名空间#hostname#可以确认原来的主机名没有改变server0。通过上面的例子,我们可以看到UTSNamespace的隔离特性,它允许进程拥有自己独立的hostname。对于位于UTSNamespace的进程,修改hostname不会影响原来的hostname.host的PIDNamespacePIDNamespace初体验可以实现PID隔离#ps-aux|wc-l#系统原有进程数为146#unshare-u/bin/sh#没有使用-p参数#ps-aux|wc-l#UTSNamespacenopidisolation147#exit#unshare-fp--mount-proc/bin/bash#PIDNamespace,--mount-proc选项确保shell在新创建的命名空间中获得PID1,-f标志从unshare下面的位置导出shell#ps-aux|wc-l4通过上面的例子,我们特意对比了UTS和PID这两个namespace,这样可以更直观的了解PIDNamespace的隔离特性。位于PIDNamespace中的进程,可以有与主机具有相同PID的进程。NetworkNamespace初体验Network可以隔离网络#ifconfig-a|grepflags#原主机的网卡数br-0218842a6d4f:flags=4099mtu1500br-9a4009fc0074:flags=4099mtu1500br-ccc197daa6b7:flags=4099mtu1500cni0:flags=4163mtu1450docker0:flags=4099mtu1500enp0s8:flags=4163mtu1500flannel.1:flags=4163mtu1450lo:标志=73mtu65536vethb960cbb3:标志=4163mtu1450vethe6e2d00a:flags=4163mtu1450#unshareName-n/bin/bash#Startifconfig-a#只在这个网络设备上启动网络命名空间lo:flags=8mtu65536looptxqueuelen1000(LlocalLoopback)RXpackets0bytes0(0.0B)RXerrors0dropped0overruns0frame0TXpackets0bytes0(0.0B)TXerrors0dropped0overruns0carrier0collisions0通过上面的例子我们可以看出host有多个网卡,但是NetworkNamespace中的进程只有一个lo网卡,网络设备与宿主机的网络设备是隔离的。通过上面的例子,相信大家对Namespace的隔离特性有了一个非常直观的认识。容器技术命名空间的隔离性是基于Namespace的内核特性实现的,是内核级的特性。通过上面的unshare命令,我们对Namespace有了初步的体验,相信大家对容器的隔离实现会有不同的理解。下一章我会用代码来操作Namespace,让大家对Namespace有更深入的了解。感兴趣的朋友请关注公众号“Stubang”,及时接收更新。当然,我们知道除了资源隔离之外,容器的另一大特点就是资源限制。资源限制取决于cgroups的内核特性。我也会在后面的章节中解释cgroups。感谢您的关注。公众号:感谢阅读,有什么问题或者需要补充的,欢迎在评论区留言。