array(2) { ["docs"]=> array(10) { [0]=> array(10) { ["id"]=> string(3) "428" ["text"]=> string(77) "Visual Studio 2017 单独启动MSDN帮助(Microsoft Help Viewer)的方法" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(8) "DonetRen" ["tagsname"]=> string(55) "Visual Studio 2017|MSDN帮助|C#程序|.NET|Help Viewer" ["tagsid"]=> string(23) "[401,402,403,"300",404]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400964" ["_id"]=> string(3) "428" } [1]=> array(10) { ["id"]=> string(3) "427" ["text"]=> string(42) "npm -v;报错 cannot find module "wrapp"" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "zzty" ["tagsname"]=> string(50) "node.js|npm|cannot find module "wrapp“|node" ["tagsid"]=> string(19) "[398,"239",399,400]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400760" ["_id"]=> string(3) "427" } [2]=> array(10) { ["id"]=> string(3) "426" ["text"]=> string(54) "说说css中pt、px、em、rem都扮演了什么角色" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(12) "zhengqiaoyin" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511400640" ["_id"]=> string(3) "426" } [3]=> array(10) { ["id"]=> string(3) "425" ["text"]=> string(83) "深入学习JS执行--创建执行上下文(变量对象,作用域链,this)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "Ry-yuan" ["tagsname"]=> string(33) "Javascript|Javascript执行过程" ["tagsid"]=> string(13) "["169","191"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511399901" ["_id"]=> string(3) "425" } [4]=> array(10) { ["id"]=> string(3) "424" ["text"]=> string(30) "C# 排序技术研究与对比" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "vveiliang" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(8) ".Net Dev" ["catesid"]=> string(5) "[199]" ["createtime"]=> string(10) "1511399150" ["_id"]=> string(3) "424" } [5]=> array(10) { ["id"]=> string(3) "423" ["text"]=> string(72) "【算法】小白的算法笔记:快速排序算法的编码和优化" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(9) "penghuwan" ["tagsname"]=> string(6) "算法" ["tagsid"]=> string(7) "["344"]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511398109" ["_id"]=> string(3) "423" } [6]=> array(10) { ["id"]=> string(3) "422" ["text"]=> string(64) "JavaScript数据可视化编程学习(二)Flotr2,雷达图" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "chengxs" ["tagsname"]=> string(28) "数据可视化|前端学习" ["tagsid"]=> string(9) "[396,397]" ["catesname"]=> string(18) "前端基本知识" ["catesid"]=> string(5) "[198]" ["createtime"]=> string(10) "1511397800" ["_id"]=> string(3) "422" } [7]=> array(10) { ["id"]=> string(3) "421" ["text"]=> string(36) "C#表达式目录树(Expression)" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(4) "wwym" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(4) ".NET" ["catesid"]=> string(7) "["119"]" ["createtime"]=> string(10) "1511397474" ["_id"]=> string(3) "421" } [8]=> array(10) { ["id"]=> string(3) "420" ["text"]=> string(47) "数据结构 队列_队列实例:事件处理" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(7) "idreamo" ["tagsname"]=> string(40) "C语言|数据结构|队列|事件处理" ["tagsid"]=> string(23) "["246","247","248",395]" ["catesname"]=> string(12) "数据结构" ["catesid"]=> string(7) "["133"]" ["createtime"]=> string(10) "1511397279" ["_id"]=> string(3) "420" } [9]=> array(10) { ["id"]=> string(3) "419" ["text"]=> string(47) "久等了,博客园官方Android客户端发布" ["intro"]=> string(288) "目录 ECharts 异步加载 ECharts 数据可视化在过去几年中取得了巨大进展。开发人员对可视化产品的期望不再是简单的图表创建工具,而是在交互、性能、数据处理等方面有更高的要求。 chart.setOption({ color: [ " ["username"]=> string(3) "cmt" ["tagsname"]=> string(0) "" ["tagsid"]=> string(2) "[]" ["catesname"]=> string(0) "" ["catesid"]=> string(2) "[]" ["createtime"]=> string(10) "1511396549" ["_id"]=> string(3) "419" } } ["count"]=> int(200) } 222 Docker namespace技术(九) - 爱码网

1. UTS namespace

UTS(UNIX Time-sharing System)namespace提供了主机名和域名的隔离,这样每个Docker容器就可以拥有独立的主机名和域名了,在网络上可以被视为一个独立的节点,而非宿主机上的一个进程。Docker中,每个镜像基本都以只身所提供的服务名称来命名镜像的hostname,且不会对宿主机产生任何影响,其原理就是利用了UTS namespace。

2. IPC namespace

进程间通信(Inter-Process Communication,IPC)涉及的IPC资源包括常见的信号量、消息队列和共享内存。申请IPC资源就申请了一个全局唯一的32位ID,所以IPC namespace中实际上包含了系统IPC标识符以及实现POSIX消息队列的文件系统。在同一个IPC namespace下的进程彼此可见,不同IPC namespace下的进程则互相不可见。

3. PID namespace

PID namespace隔离非常实用,对进程PID重新标号,即两个不同 namespace的进程可以有相同的PID。每个 PID namespace都有自己的计数程序。内核为所有的 PID namespace维护了一个树状结构,最顶层的是系统初始时创建的,被称为 root namespace它创建的新 PID namespace被称为 child namespace(树的子节点),而原先的 PID namespace就是新创建的 namespace的parent namespace(树的父节点)通过这种方式,不同的 PID namespaces会成一个层级体系。所属的父节点可以看到子节点中的进程,并可以通过信号等方式对子节点中的进程产生影响。反过来,子节点却不能看到父节点 PID namespace中的任何内容,由此产生如下结论

  1. 每个 PID namespace中的第一个进程“PID1”,都会像传统 Linux中的init进程一样拥有特权,起特殊作用。
  2. 一个 namespace中的进程,不可能通过kill或 ptrace影响父节点或者兄弟节点中的进程,因为其他节点的PID在这 namespace个中没有任何意义。
  3. 如果你在新的 PID namespace中重新挂载/proc文件系统,会发现其下只显示同属一个pid namespace中的其他进程。
  4. 在 root namespace中可以看到所有的进程,并且递归包含所有子节点中的进程。

到这里,读者可能已经联想到一种在外部监控Docker中运行程序的方法了,就是监控Docker daemon所在的PID namespace下的所有进程及其子进程,再进行筛选即可。

4. mount namespace

mount namespace通过隔离文件系统挂载点对隔离文件系统提供支持,它是历史上第一个 Linux namespace,所以标识位比较特殊,就是 CLONENEWNS。隔离后,不同 mount namespace中的文件结构发生变化也互不影响。可以通过/proc/[pid]/mounts看到所有挂载在当前中的文件系统,还可以通过proc/[pid]/mountstats到 mount namespace中文件设备的统计信息,包括挂载文件的名字、文件系统类型、挂载位置等。

进程在创建 mount namespace时,会把当前的文件结构复制给新的 namespace。新 namespace中的所有 mount操作都只影响自身的文件系统,对外界不会产生任何影响。这种做法非常严格地实现了隔离,但对某些情况可能并不适用,比如父节点 namespace 中的进程挂载了一张CD-ROM, 这时子节点 namespace复制的目录结构是无法动挂载上这张CD-ROM的,因为这种操作会影响到父节点的文件系统

2006年引入的挂载传播( mount propagation)解决了这个问题,挂载传播定义了挂载对象( mount object)之间的关系,这样的关系包括共享关系和从属关系,系统用这些关系决定任何挂 载对象中的挂载事件如何传播到其他挂载对象。

  • 共享关系( share relationship)。如果两个载对象具有共享关系,那么一个挂载对象中 的挂载事件会传播到另一个挂载对象,反之亦然。
  • 从属关系( slave relationship)。如果两个挂载对象形成从属关系,那么一个挂载对象中的接收者。 的挂载事件会传播到另一个挂载对象,但是反之不行;在这种关系中,从属对象是事件的接收者。

一个挂载状态可能为以下一种:

  • 共享挂载(share)
  • 从属挂载(slave)
  • 共享从属挂载(shared and slave)
  • 私有挂载(private)
  • 不可绑定挂载(unbendable)

传播事件的挂载对象称为共享挂载;接收传播事件的挂载对象称为从属挂载;同时兼有前述两者特征的挂载对象称为共享从属挂載;既不传播也不接收传播事件的挂载对象称为私有挂载;另一种特殊的挂载对象称为不可绑定的挂载,它们与私有挂载相似,但是不允许执行绑定挂载,即创建 mount namespace时这块文件对象不可被复制。通过下图可以更好地了解它们的状态变化。

mount各类挂载状态示意图:

Docker namespace技术(九)

5. network namespace

当我们了解完各类 namespace,兴致勃勃地构建出一个容器,并在容器中启动一个 Apache进程时,却出现了“80端口已被占用”的错误,原来主机上已经运行了一个 Apache进程,这时就要借助 Network namespace技术进行网络隔离。

network namespace主要提供了关于网络资源的隔离,包括网络设备、IPv4和IPv6协议栈、IP路由表、防火墙、/proc/net目录、/sys/ Class/net目录、套接字( socket)等。一个物理的网络设备最多存在于一个 network namespace中,可以通过创建 veth pair(虚拟网络设备对:有两端类似管道,如果数据从一端传入另一端也能接收到,反之亦然)在不同的 network namespace间创建通道,以达到通信目的。

一般情况下,物理网络设备都分配在最初的 root namespace(表示系统默认的 namespace)中。但是如果有多块物理网卡,也可以把其中一块或多块分配给新创建的 network namespace。需要注意的是,当新创建的 network namespace被释放时(所有内部的进程都终止并且 namespace文件没 有被挂载或打开),在这个 namespace中的物理网卡会返回到 root namespace,而非创建该进程的父 进程所在的 network namespace 。

当说到 network namespace时,指的未必是真正的网络隔离,而是把网络独立出来,给外部用户一种透明的感觉,仿佛在与一个独立网络实体进行通信。为了达到该目的,容器的经典做法就是创建一个 veth pair,一端放置在新的 namespace中,通常命名为eth0,一端放在原先的 namespace 中连接物理网络设备,再通过把多个设备接入网桥或者进行路由转发,来实现通信的目的。

也许读者会好奇,在建立起 veth pair之前,新旧 namespace该如何通信呢?答案是pipe(管道)。以 Docker daemon,启动容器的过程为例,假设容器内初始化的进程称为 init docker daemon在宿主机上负责创建这个 veth pair,把一端绑定到 docker网桥上,另一端接入新建的 network namespace进程中。这个过程执行期间,Docker daemon和init就通过pipe进行通信。具体来说,就是在 Docker daemon完成 veth pair的创建之前,init在管道的另一端循环等待,直到管道另一端传来 Dockerdaemon关于veth设备的信息,并关闭管道。init才结束等待的过程,并把它的“eth0”启动起来。

6. user namespaces

user namespace主要隔离了安全相关的标识符( identifier)和属性( attribute),包括用户ID用户组D、root目录、key(指**)以及特殊权限。通俗地讲,一个普通用户的进程通过 clone()创建的新进程在新 user namespace中可以拥有不同的用户和用户组。这意味着一个进程在容器外属于一个没有特权的普通用户,但是它创建的容器进程却属于拥有所有权限的超级用户,这个技术为容器提供了极大的自由。

相关文章: