1. namespace 资源隔离

namespace 是内核实现的一种资源隔离技术,docker 使用 namespace 实现了资源隔离。

Liunx 内核提供 6 种 namespace 隔离的系统调用,如下表所示:

namespace 系统调用参数 隔离内容
UTS CLONE_NEWUTS 主机名与域名
IPC CLONE_NEWIPC 信号量,消息队列和共享内存
PID CONE_NEWPID 进程编号
Network CLONE_NEWNET 网络设备,网络栈,端口等
Mount CLONE_NEWNS 挂载点(文件系统)
User CLONE_NEWUSER 用户和用户组

Liunx 提供了对 namespace API 调用的三种方式,通过这几种方式可以调用系统调用参数实现对应 namespace 资源的隔离。这三种方式分别是:

  1. clone(): clone 在创建新进程的同时创建 namespace。
  2. setns(): setns 加入一个已经存在的 namespace。
  3. unshare(): unshare 在原先进程上进行 namespace 隔离。

有了系统调用参数和调用 namespace API 的方式,我们就可以构造各种类型的 namespace 了。

1.1 UTS namespace

UTS(UNIX Time-sharing System) namespace 提供主机名和域名的隔离,这里使用 unshare 实现 UTS namespace:

root@chunqiu:~# hostname
chunqiu
root@chunqiu:~# echo $$
31124
root@chunqiu:~# readlink /proc/$$/ns/uts
uts:[4026531838] root@chunqiu:~# unshare --uts /bin/bash root@chunqiu:~# hostname
chunqiu
root@chunqiu:~# hostname demo
root@chunqiu:~# hostname
demo
root@chunqiu:~# echo $$
31332 root@chunqiu:~# readlink /proc/$$/ns/uts
uts:[4026532208] root@chunqiu:~# ps -ef | grep 31332 | grep -v grep
root 31332 31124 0 07:51 pts/0 00:00:00 /bin/bash
root 31345 31332 0 07:52 pts/0 00:00:00 ps -ef
root@chunqiu:~# exit
exit
root@chunqiu:~# hostname
chunqiu

1.2 IPC namespace

IPC(Inter-Process Communication) 进程间通信涉及的 IPC 资源包括信号量,消息队列和共享内存。这里使用 clone() 实现 IPC namespace 的隔离:

/* ipc.c */
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sched.h>
#include <signal.h>
#include <unistd.h> #define STACK_SIZE (1024 * 1024)
static char container_stack[STACK_SIZE]; char* const container_args[] = {
"/bin/bash",
NULL
}; int container_main(void* arg)
{
printf("Container - inside the container\n");
execv(container_args[0], container_args);
printf("Something's wrong!\n");
return 1;
} int main()
{
printf("start a container:\n");
int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWIPC | SIGCHLD, NULL);
waitpid(container_pid, NULL, 0);
printf("container stopped!\n");
return 0;
}

编译并运行 ipc.c:

root@chunqiu:~/chunqiu/docker/container# echo $$
31124
root@chunqiu:~/chunqiu/docker/container# readlink /proc/$$/ns/ipc
ipc:[4026531839]
root@chunqiu:~/chunqiu/docker/container# ipcmk -Q
Message queue id: 0
root@chunqiu:~/chunqiu/docker/container# ipcs -q ------ Message Queues --------
key msqid owner perms used-bytes messages
0xad26491d 0 root 644 0 0 root@chunqiu:~/chunqiu/docker/container# gcc ipc.c -Wall -o ipc.o && ./ipc.o
start a container:
Container - inside the container
root@chunqiu:~/chunqiu/docker/container# echo $$
31961
root@chunqiu:~/chunqiu/docker/container# readlink /proc/$$/ns/ipc
ipc:[4026532208]
/* different ipc namesapce */
root@chunqiu:~/chunqiu/docker/container# ipcs -q ------ Message Queues --------
key msqid owner perms used-bytes messages

1.3 PID namespace

1.3.1 父子 PID namespace

PID namespace 对进程 PID 重新标号实现隔离效果,使用 clone 方式实现 PID namespace 的隔离:

/* 代码与 IPC namespace 基本一模一样,只将系统调用参数换成 CLONE_NEWPID */
int container_pid = clone(container_main, container_stack+STACK_SIZE, CLONE_NEWPID | SIGCHLD, NULL);

编译并运行 pid.c:

root@chunqiu:~/chunqiu/docker/container# echo $$
32090
root@chunqiu:~/chunqiu/docker/container# readlink /proc/$$/ns/pid
pid:[4026531836] root@chunqiu:~/chunqiu/docker/container# gcc pid.c -Wall -o pid.o && ./pid.o
start a container:
Container - inside the container
root@chunqiu:~/chunqiu/docker/container# echo $$
1
root@chunqiu:~/chunqiu/docker/container# readlink /proc/$$/ns/pid
pid:[4026531836] root@chunqiu:~/chunqiu/docker/container# mount -t proc proc /proc root@chunqiu:~/chunqiu/docker/container# readlink /proc/$$/ns/pid
pid:[4026532208]
root@chunqiu:~/chunqiu/docker/container# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:41 pts/0 00:00:00 /bin/bash
root 14 1 0 08:42 pts/0 00:00:00 ps -ef
root@chunqiu:~/chunqiu/docker/container#

有一点需要注意的是:在 PID namespace 中 init 进程号为 1,但是 readlink 显示还是和父进程一样,这是因为没有对文件系统挂载点进行隔离。在 PID namespace 内使用 mount 挂载 proc 文件系统,重新执行 readlink 发现 pid 不一样了。

那么,PID namespace 中的 init 进程对应到父 PID namespace 中的哪个进程呢?查看父 PID namespace:

root@chunqiu:~# ps -ef
Error, do this: mount -t proc proc /proc
/* proc 文件系统在子 PID namespace 中,需要重新 mount */
root@chunqiu:~# mount -t proc proc /proc root@chunqiu:~# ps -ef | grep 32090 | grep -v grep
root 32090 32075 0 08:23 pts/0 00:00:00 -bash
root 32659 32090 0 08:41 pts/0 00:00:00 ./pid.o
root@chunqiu:~# ps -ef | grep 32659 | grep -v grep
root 32659 32090 0 08:41 pts/0 00:00:00 ./pid.o
root 32660 32659 0 08:41 pts/0 00:00:00 /bin/bash

可以看到 PID 为 32660 的 bash 进程即为子 PID namespace 的 init 进程。

1.3.2 init 进程与 dockerfile

在 PID namespace 中,init 进程负责收养成为孤儿进程的子进程。因此,init 进程应该是具有资源监控与回收等管理能力的进程,如 bash 进程。

dockerfile 中执行 CMD 命令产生的进程将会成为 PID namespace 中的 init 进程。以 httpd container 为例,其 dockerfile 如下:

...
COPY httpd-foreground /usr/local/bin/ EXPOSE 80
CMD ["httpd-foreground"]

运行 httpd container,ps 查看进程号:

$ ps -ef | grep httpd | grep -v grep
root 7469 7446 0 09:50 ? 00:00:00 httpd -DFOREGROUND
$ ps -ef | grep 7446 | grep -v grep
root 7446 842 0 09:50 ? 00:00:00 containerd-shim -namespace moby -workdir ...
root 7469 7446 0 09:50 ? 00:00:00 httpd -DFOREGROUND
$ ps -ef | grep 842 | grep -v grep
root 842 1 0 09:32 ? 00:00:00 /usr/bin/containerd

可以看到进程号为 7469 的 httpd -DFOREGROUND 进程即为 httpd container 中的 init 进程。

PID namespace 嵌套

PID namespace 是一种层级体系,这里构造一种在子 PID namespace 中嵌套 PID namespace 的场景,如下:

root@chunqiu:~/chunqiu/docker/container# echo $$
32090
root@chunqiu:~/chunqiu/docker/container# readlink /proc/$$/ns/pid
pid:[4026531836] root@chunqiu:~/chunqiu/docker/container# ./pid.o
start a container:
Container - inside the container
root@chunqiu:~/chunqiu/docker/container# echo $$
1
root@chunqiu:~/chunqiu/docker/container# mount -t proc proc /proc
root@chunqiu:~/chunqiu/docker/container# readlink /proc/$$/ns/pid
pid:[4026532209] /* 子 PID namespace 中创建 PID namespace */
root@chunqiu:~/chunqiu/docker/container# ./pid.o
start a container:
Container - inside the container
root@chunqiu:~/chunqiu/docker/container# echo $$
1
root@chunqiu:~/chunqiu/docker/container# readlink /proc/$$/ns/pid
pid:[4026532209] root@chunqiu:~/chunqiu/docker/container# mount -t proc proc /proc
root@chunqiu:~/chunqiu/docker/container# readlink /proc/$$/ns/pid
pid:[4026532214] root@chunqiu:~/chunqiu/docker/container# exit
exit
container stopped!
root@chunqiu:~/chunqiu/docker/container# exit
exit
container stopped!

1.4 Network namespace

network namespace 参看 这里

docker 原理之 namespace (上)的更多相关文章

  1. Docker原理:Namespace

    目录 Namespace UTS Namespae PID Namespace Mount Namespace User Namespace Network Namespace 参考 Namespac ...

  2. docker原理

    Docker原理11 Linux Namespace 11 AUFS文件系统17 重新理解Docker的各种命令18 Docker原理 Linux Namespace docker是一个容器引擎,容器 ...

  3. Docker原理(图解+秒懂+史上最全)

    背景:下一个视频版本,从架构师视角,尼恩为大家打造高可用.高并发中间件的原理与实操. 目标:通过视频和博客的方式,为各位潜力架构师,彻底介绍清楚架构师必须掌握的高可用.高并发环境,包括但不限于: 高可 ...

  4. 理解Docker(3):Docker 使用 Linux namespace 隔离容器的运行环境

    本系列文章将介绍Docker的有关知识: (1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 ...

  5. 一篇不一样的docker原理解析

    转自:https://zhuanlan.zhihu.com/p/22382728 https://zhuanlan.zhihu.com/p/22403015 在学习docker的过程中,我发现目前do ...

  6. Docker原理探究

    问题思考:-------------------------------------Docker浅显原理理解-------------------------------------P1. ubunt ...

  7. Docker源码分析(七):Docker Container网络 (上)

    1.前言(什么是Docker Container) 如今,Docker技术大行其道,大家在尝试以及玩转Docker的同时,肯定离不开一个概念,那就是“容器”或者“Docker Container”.那 ...

  8. docker原理(转)

    转自:https://zhuanlan.zhihu.com/p/22382728 https://zhuanlan.zhihu.com/p/22403015 在学习docker的过程中,我发现目前do ...

  9. 一篇文章带你吃透 Docker 原理

    容器的实现原理 从本质上,容器其实就是一种沙盒技术.就好像把应用隔离在一个盒子内,使其运行.因为有了盒子边界的存在,应用于应用之间不会相互干扰.并且像集装箱一样,拿来就走,随处运行.其实这就是 Paa ...

  10. Docker入门与进阶(上)

    Docker入门与进阶(上) 作者 刘畅 时间 2020-10-17 目录 1 Docker核心概述与安装 1 1.1 为什么要用容器 1 1.2 docker是什么 1 1.3 docker设计目标 ...

随机推荐

  1. IIS下使用SSL证书

    IIS下使用SSL证书 本文介绍windowsServer下SSL证书配置及IIS站点配置 1.    生成SSL证书 在阿里云申请免费SSL证书 登录阿里云管理控制台,打开SSL证书管理 选择免费证 ...

  2. 【C#】【IO】【实例】统计多个文件夹下的图片

    因工作需要繁琐的进行同一目录多个文件夹下的图片统计,便使用代码来解决. 需求:统计的是多少个文件夹包含了图片,并非是统计有多少张图. 我们先用Python来创建一个现场环境(巩固巩固py知识): 1 ...

  3. ElasticSearch之文件描述符的数量

    ElasticSearch在运行过程中,涉及大量文件的打开.关闭.读.写等操作.因此当ElasticSearch进程的文件描述符数量不足时可能导致丢失数据等故障现象. 因此为保障ElasticSear ...

  4. Python——第一章:数据类型介绍

    数据类型: 区分不同的数据.不同的数据类型应该有不同的操作 数字: 做加减乘除+-*/ 整数,int 小数,float a= 10 #整数 b = 20 print(a + b) #加法运算 c = ...

  5. kafka源码阅读之MacBook Pro M1搭建Kafka2.7版本源码运行环境

    原创/朱季谦 最近在阅读Kafka的源码,想可以在阅读过程当中,在代码写一些注释,便决定将源码部署到本地运行. 日常开发过程中,用得比较多一个版本是Kafka2.7版本,故而在MacBook Pro笔 ...

  6. 斯坦福 UE4 C++ ActionRoguelike游戏实例教程 10.5.作业五 为游戏添加一个积分系统,随机生成增益道具

    斯坦福课程 UE4 C++ ActionRoguelike游戏实例教程 0.绪论 概述 本篇文章将解决作业五提出的问题,使用PlayerState,在原本游戏的基础上引入积分系统,实现击杀敌人得分,拾 ...

  7. 在线编辑Excel——插入图表

    本文内容介绍如何通过Excel在线编辑器--Spire.Cloud Excel来实现图表插入,插入图表时,可插入常见的柱状图.饼图.折线图.条形图.面积图.散点图.股价图等.这里挑选几种图表来展示插入 ...

  8. 实践案例丨ACL2020 KBQA 基于查询图生成回答多跳复杂问题

    摘要:目前复杂问题包括两种:含约束的问题和多跳关系问题.本文对ACL2020 KBQA 基于查询图生成的方法来回答多跳复杂问题这一论文工作进行了解读,并对相关实验进行了复现. 1.摘要 1.1 复杂问 ...

  9. 云图说|AppCube零代码,开启无码新生活

    阅识风云是华为云信息大咖,擅长将复杂信息多元化呈现,其出品的一张图(云图说).深入浅出的博文(云小课)或短视频(云视厅)总有一款能让您快速上手华为云.更多精彩内容请单击此处. 摘要: 应用魔方 App ...

  10. 一文你带快速认识Vue-Router路由

    摘要:Vue Router是Vue.js 官方的路由管理器.它和Vue.js的核心深度集成,可以非常方便的用于SPA应用程序的开发. 本文分享自华为云社区<Vue-Router路由快速了解与应用 ...