前言

Docker系列文章:

此篇是Docker系列的第九篇,之前的文章里面或多或少的提到Docker的隔离技术,但是没有很清楚的去聊这个技术,但是经过这么多文章大家一定对Docker使用和概念有了一定的理解,接下来我们聊下底层一些技术,帮助大家解解惑,先从隔离技术开始吧。此外大家一定要按照我做的Demo都手敲一遍,印象会更加深刻的,加油!

  1. 为什么要学习Docker
  2. Docker基本概念
  3. Docker镜像基本原理
  4. Docker容器数据卷
  5. Dockerfile
  6. Docker单机网络上
  7. Docker单机网络下
  8. Docker单机网络实战

如何理解Namespace

进入一个容器内部:

docker exec -it ad9342449b86 /bin/bash

通过ps命令查看容器内部的进程,容器内部只有两个进程在运行,看不到操作系统的其他进程,一个进程执行的/bin/bash,另外一个执行的ps,说明此刻容器被 Docker 隔离在了一个跟宿主机在不同的环境中。


img

对于宿主机来说相当于我们在宿主机上执行一个/bin/bash的进程,宿主机给这个进程起一个独一无二名字,比如叫PID=800,用来区分与其他进程的不同,Docker在运行/bin/bash的时候,会与宿主机上的其他进程进行隔离,让他看不到其他进程运行状况,并且重新计算进程号,也就是我们看到202,但是这个进程实际上是宿主机的进程,这种技术,其实就是对被隔离应用的进程空间做了手脚,使得这些进程只能看到重新计算过的进程编号,这就是Linux里面的Namespace机制,也就是Docker采用的隔离技术。

总结一下,Namespace是Linux内核用来隔离内核资源的方式。通过Namespace可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程根本就感觉不到对方的存在。具体的实现方式是把一个或多个进程的相关资源指定在同一个Namespace中。Linux Namespace是对全局系统资源的一种封装隔离,使得处于不同Namespace的进程拥有独立的全局系统资源,改变一个Namespace中的系统资源只会影响当前Namespace里的进程,对其他Namespace中的进程没有影响。

Namespace类型介绍

目前,Linux 内核实现了6种 Namespace:


img
六种命名空间:
  1. UTS Namespace:

    UTS Namespace 对主机名和域名进行隔离。为什么要隔离主机名?因为主机名可以代替IP来访问。如果不隔离,同名访问会出冲突。

  2. IPC Namespace

    Linux 提供很多种进程通信机制,IPC Namespace 针对 System V 和 POSIX 消息队列,这些 IPC 机制会使用标识符来区别不同的消息队列,然后两个进程通过标识符找到对应的消息队列。IPC namespace使得相同的标识符在两个Namespace代表不同的消息队列,因此两个Namespace 中的进程不能通过 IPC 来通信。

  3. PID Namespace

    PID Namespace 用来隔离进程的 PID 空间,使得不同 PID Namespace 里的进程 PID 可以重复且互不影响。PID Namespace 对容器类应用特别重要, 可以实现容器内进程的暂停/恢复等功能,还可以支持容器在跨主机的迁移前后保持内部进程的 PID 不发生变化。

  4. Mount Namespace

    Mount Namespace 为进程提供独立的文件系统视图。可以这么理解,Mount Namespace 用来隔离文件系统的挂载点,这样进程就只能看到自己的Mount Namespace中的文件系统挂载点。进程的Mount Namespace中的挂载点信息可以在 /proc/[pid]/mounts、/proc/[pid]/mountinfo 和 /proc/[pid]/mountstats 这三个文件中找到。在一个 Namespace 里挂载、卸载的动作不会影响到其他 Namespace。

  5. Network Namespace

    Network Namespace 在逻辑上是网络堆栈的一个副本,它有自己的路由、防火墙规则和网络设备。默认情况下,子进程继承其父进程的 Network Namespace。每个新创建的 Network Namespace 默认有一个本地环回接口 lo,除此之外,所有的其他网络设备(物理/虚拟网络接口,网桥等)只能属于一个 Network Namespace。每个 socket 也只能属于一个 Network Namespace。

  6. User Namespace

    User Namespace 用于隔离安全相关的资源,包括 user IDs and group IDs,keys, 和 capabilities。同样一个用户的 user ID 和 group ID 在不同的User Namespace 中可以不一样(与 PID Namespace 类似)。可以这样理解,一个用户可以在一个User Namespace中是普通用户,但在另一个User Namespace中是root用户。

Namespace原理介绍

Linux Namespace 是 Linux 提供的一种内核级别环境隔离的方法,因此Linux 提供了多个 API 用来操作 Namespace,它们是 clone()、setns() 和 unshare() 函数,简单介绍一下三个系统调用的功能:

  1. clone() : 实现线程的系统调用,用来创建一个新的进程,并可以通过设计上述系统调用参数达到隔离的目的;
  2. unshare() : 使某进程脱离某个 namespace;
  3. setns() : 把某进程加入到某个 namespace;

为了确定隔离的到底是哪项 Namespace,在使用这些 API 时,通常需要指定一些调用参数:CLONE_NEWIPC、CLONE_NEWNET、CLONE_NEWNS、CLONE_NEWPID、CLONE_NEWUSER、CLONE_NEWUTS 和 CLONE_NEWCGROUP。下图是各个Namespace对应的调用参数和Linux内核:


img

如果要同时隔离多个 Namespace,可以使用 | (按位或)组合这些参数。同时我们还可以通过 /proc 下面的一些文件来操作 Namespace。下面就让让我们看看这些接口的用法:

查看进程所属的Namespace

从版本号为 3.8 的内核开始,/proc/[pid]/ns 目录下会包含进程所属的 Namespace 信息,使用下面的命令可以查看当前进程所属的 Namespace 信息:

ls -l /proc/$$/ns

这些 Namespace 文件都是链接文件。链接文件的内容的格式为 xxx:[inode number]。其中的 xxx 为 Namespace 的类型,inode number 则用来标识一个 Namespace,我们也可以把它理解为 Namespace 的 ID。如果两个进程的某个 Namespace 文件指向同一个链接文件,说明其相关资源在同一个 Namespace 中。

clone() 函数

我们可以通过 clone() 在创建新进程的同时创建 Namespace。clone() 在 C 语言库中的声明如下:

#define _GNU_SOURCE
#include <sched.h>
int clone(int (*fn)(void *), void *child_stack, int flags, void *arg);

实际上,clone() 是在 C 语言库中定义的一个封装(wrapper)函数,它负责建立新进程的堆栈并且调用对编程者隐藏的 clone() 系统调用。四个参数代表的意思是:

  1. child_func : 传入子进程运行的程序主函数;
  2. child_stack : 传入子进程使用的栈空间;
  3. flags : 表示使用哪些 CLONE_* 标志位;
  4. args : 用于传入用户参数;

clone() 与 fork() 类似,都相当于把当前进程复制了一份,但 clone() 可以更细粒度地控制与子进程共享的资源(可以通过 flags 来控制),包括虚拟内存、打开的文件描述符和信号量等等。一旦指定了标志位 CLONE_NEW*,相对应类型的 Namespace 就会被创建,新创建的进程也会成为该 Namespace 中的一员。

setns() 函数

setns() 函数可以将当前进程加入到已有的 Namespace 中。setns() 在 C 语言库中的声明如下:

#define _GNU_SOURCE
#include <sched.h>
int setns(int fd, int nstype);

和 clone() 函数一样,C 语言库中的 setns() 函数也是对 setns() 系统调用的封装:

  1. fd:表示要加入 Namespace 的文件描述符。它是一个指向 /proc/[pid]/ns 目录中文件的文件描述符,可以通过直接打开该目录下的链接文件或者打开一个挂载了该目录下链接文件的文件得到;
  2. nstype:参数 nstype 让调用者可以检查 fd 指向的 Namespace 类型是否符合实际要求。若把该参数设置为 0 表示不检查;
unshare() 函数

unshare() 函数可以在原进程上进行 Namespace 隔离。也就是创建并加入新的 Namespace 。unshare() 在 C 语言库中的声明如下:

#define _GNU_SOURCE
#include <sched.h>
int unshare(int flags);

unshare() 函数也是对 unshare() 系统调用的封装。调用 unshare() 的主要作用就是:不启动新的进程就可以起到资源隔离的效果,相当于跳出原先的 Namespace 进行操作。

使用PID Namesapce达到Docker线程隔离状态

Linux下的每个进程都有一个对应的 /proc/PID 目录,该目录包含了大量的有关当前进程的信息。 对一个 PID Namespace 而言,/proc 目录只包含当前 Namespace 和它所有子孙后代 Namespace 里的进程的信息。

创建一个新的 PID Namespace 后,如果想让子进程中的 top、ps 等依赖 /proc 文件系统的命令工作,还需要挂载 /proc 文件系统。使用如下命令创建一个新的PID Namespace:

#查看当前进程的PID
echo $$
#查看该线程对应PID Namespace
readlink /proc/$$/ns/pid
#使用unshare命令创建新的PID Namespace
#该命令会同时创建新的PID和Mount namespace
unshare --pid --mount --fork /bin/bash
#查看创建的线程的PID Namespace
readlink /proc/$$/ns/pid

上图中新生成的PID Namespace的Id并没有发生改变,我们看到在新创建的PID Namespace也可以看到其他的进程的运行情况,显然这个和我们说的隔离是不一致的,接下来我们看下原因是什么:


img
#查看当前进程的PID
echo $$
#查看当前进程的详情
ps 1

这个例子说明当前进程被认为是该 PID namespace 中的 1 号进程了,通过PS命名查看1号进程的详情,发现这个进程是系统的启动相关(CentOS 7开始也由systemd取代了init作为默认的系统进程管理工具)。

造成混乱的原因是当前进程没有正确的挂载 /proc 文件系统,由于我们新的 Mount Namespace 的挂载信息是从老的 Namespace 拷贝过来的,所以这里看到的还是老 Namespace 里面的进程号为 1 的信息。执行下面的命令挂载 /proc 文件系统:

mount -t proc proc /proc

接下来我们再来检查相关的信息,会发现新建的 PID namespace 进程看不到宿主机的进程信息了。


img

我们也可以使用如下命令来创建进程,

unshare --pid --mount-proc --fork /bin/bash

这样在创建了 PID 和 Mount Namespace 后,会自动挂载 /proc 文件系统,就不需要我们手动执行 mount -t proc proc /proc 命令了。

总结

Docker容器是在创建容器进程时,指定了这个进程所需要启用的一组Namespace参数,这样容器就只能看到到当前Namespace所限定的资源、文件、设备、状态,或者配置。而对于宿主机以及其他不相关的程序,它就完全看不到了,因此容器本质上就是一个特殊的进程。

结束

欢迎大家点点关注,点点赞!

Docker隔离技术的更多相关文章

  1. 微服务架构:基于微服务和Docker容器技术的PaaS云平台架构设计(微服务架构实施原理)

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! 基于微服务架构和Docker容器技术的PaaS云平台建设目标是给我们的开发人员提供一套服务快速开发.部署.运维管理.持续开发持续集成的流程 ...

  2. k8s 重要概念 - 每天5分钟玩转 Docker 容器技术(117)

    在实践之前,必须先学习 Kubernetes 的几个重要概念,它们是组成 Kubernetes 集群的基石. Cluster Cluster 是计算.存储和网络资源的集合,Kubernetes 利用这 ...

  3. Docker 基础技术之 Linux cgroups 详解

    PS:欢迎大家关注我的公众号:aCloudDeveloper,专注技术分享,努力打造干货分享平台,二维码在文末可以扫,谢谢大家. 推荐大家到公众号阅读,那里阅读体验更好,也沉淀了很多篇干货. 前面两篇 ...

  4. Network Policy - 每天5分钟玩转 Docker 容器技术(171)

    Network Policy 是 Kubernetes 的一种资源.Network Policy 通过 Label 选择 Pod,并指定其他 Pod 或外界如何与这些 Pod 通信. 默认情况下,所有 ...

  5. Docker容器技术的PaaS云平台架构设计***

    基于微服务架构和Docker容器技术的PaaS云平台建设目标是给我们的开发人员提供一套服务快速开发.部署.运维管理.持续开发持续集成的流程.平台提供基础设施.中间件.数据服务.云服务器等资源,开发人员 ...

  6. 当公有云Azure拥抱Docker容器技术

    本文转载至 http://3387405.blog.51cto.com/3377405/1598977 预见未来看似是一件不太可能的事情,然而现在企业科技高速发展的态势完全超乎想象. 就在几周前Inf ...

  7. Docker容器技术的核心原理

    目录 1 前言 2 docker容器技术 2.1 隔离:Namespace 2.2 限制:Cgroup 2.3 rootfs 2.4 镜像分层 3 docker容器与虚拟机的对比 1 前言 上图是百度 ...

  8. 庐山真面目之十四微服务架构的Docker虚拟技术深入探究

    庐山真面目之十四微服务架构的Docker虚拟技术深入探究 一.我的开场白 曾几何时,分布式的发展也影响了后来的微服务架构的实现方式.到了现在,只要涉及到互联网技术领域,就会设计一个概念,那就是微服务. ...

  9. docker容器技术基础之联合文件系统OverlayFS

    我们在上篇介绍了容器技术中资源隔离与限制docker容器技术基础之linux cgroup.namespace 这篇小作文我们要尝试学习容器的另外一个重要技术之联合文件系统之OverlayFS,在介绍 ...

随机推荐

  1. ES6中的Generator函数

    今天小编发现一个es6中的新概念,同时也接触到了一个新关键字yeild,下面我就简单和大家聊聊es6中的generator函数.大家还可以关注我的微信公众号,蜗牛全栈. 一.函数声明:在functio ...

  2. 音视频点播服务基础系列(Fmpeg常用命令)

    前言 公司业务中有一些场景需要用到服务端音视频剪辑技术,最开始为了快速上线使用的是某公有云的商用解决方案,但由于费用太高所以我们团队经过一个星期的冲刺,给出了一个FFmpeg+Serverless的解 ...

  3. 你的电脑适合升级 Win11 吗?「GitHub 热点速览 v.21.26」

    作者:HelloGitHub-小鱼干 WhyNotWin11 是个有意思的项目,本以为是从 360 度"抨击" Windows 11 的不好用之处,但它是一个实实在在地从硬件角度告 ...

  4. Unity 消消乐开发思路

    以简单的方式讲述游戏开发思路,暂时没有实践,如有错误,欢迎各位大佬指错 关卡数据保存方式 数据保存我选用json,可读性强,解析快 消消乐物体处理方式 消消乐物体我将以预制体的方式使用(把物品拖到As ...

  5. 诸多改进!Superset 1.2.0 正式发布!

    Apache Superset 是一个现代的.企业级的轻量BI平台,提供了大量数据可视化组件. 距离superset上一个版本发布已经过了近三个月的时间,我们终于等到了1.2.0版本. 之前就曾提到过 ...

  6. 生产环境部署Django项目

    生产环境部署Django项目 1.  部署架构 IP地址 安装服务 172.16.1.251 nginx uwsgi(sock方式) docker mysql5.7 redis5 Nginx 前端We ...

  7. 8、ITSM基本概念(1)

    ITSM即是信息技术服务管理: 8.1.什么是服务: 8.2.RACI模型: 谁负责(R =n Resposible),即负责执行任务的角色,他/她具体负责操控项目.解决问题. 谁批准(A = Acc ...

  8. 12、Linux磁盘设备基础知识(1)

    GB TB PB EP ZB YB BB:

  9. 24、mysql数据库优化

    24.1.如何判断网站慢的排查顺序: 客户端->web->nfs->数据库: 24.2.uptime命令详解: [root@backup ~]#uptime 13:03:23 up ...

  10. 用阻塞队列实现一个生产者消费者模型?synchronized和lock有什么区别?

    多线程当中的阻塞队列 主要实现类有 ArrayBlockingQueue是一个基于数组结构的有界阻塞队列,此队列按FIFO原则对元素进行排序 LinkedBlockingQueue是一个基于链表结构的 ...