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. JQuery_2

    1.动画:    1.三种方式显示和隐藏元素       1.默认方式       1.show([speed,[easing],[fn]])         1.参数:             1. ...

  2. 8、switch语句

    1.switch语句:"开关" switch是一个条件语句,它计算表达式并将其与可能匹配的列表进行比较,并根据匹配执行代码块.它可以被认为是一种惯用的方式来写多个if else子句 ...

  3. Salesforce LWC学习(四十七) 标准页面更新以后自定义页面如何捕捉?

    本篇参考: https://developer.salesforce.com/docs/atlas.en-us.platform_events.meta/platform_events/platfor ...

  4. 【经典问题】mysql和redis数据一致性问题

    前言 MySQL和Redis数据一致性算是个很经典的问题,在之前也看到过很多相关的文章,最近心血来潮,想把一致性问题的解决方案和存在问题都总结一下. 不推荐方案 1 先更新MySQL,再更新Redis ...

  5. Windows Server 2019/2016 配置自动更新和更换大陆更新服务器

    文章原地址: 运行 > gpedit.msc -> 计算机配置 -> 管理模板 -> Windows 组件 -> Windows 更新 下面中右侧三个选项是本篇教程中会介 ...

  6. 非工程师指南: 训练 LLaMA 2 聊天机器人

    引言 本教程将向你展示在不编写一行代码的情况下,如何构建自己的开源 ChatGPT,这样人人都能构建自己的聊天模型.我们将以 LLaMA 2 基础模型为例,在开源指令数据集上针对聊天场景对其进行微调, ...

  7. springboot整合hibernate(非JPA)(二)

    springboot整合hibernate(非JPA)(二) springboot整合hibernate,非jpa,若是jpa就简单了,但是公司项目只有hibernate,并要求支持多数据库,因此记录 ...

  8. VSCode C++开发环境配置:CMake 调试配置 launch.json

    相关内容 VSCode C++开发环境配置: LLVM clang clangd 安装 cmake sudo apt install cmake 安装 VSCode 插件 CMake CMakeToo ...

  9. LAS Spark 在 TPC-DS 的优化揭秘

    更多技术交流.求职机会,欢迎关注字节跳动数据平台微信公众号,回复[1]进入官方交流群 文章主要介绍了火山引擎湖仓一体分析服务 LAS Spark(下文以 LAS Spark 指代)在 TPC-DS 上 ...

  10. Mac 复制文件名目录路径

    Mac快速复制文件路径 在Mac电脑上,我们经常需要复制文件的路径,其实,Mac系统提供了快速复制文件路径的方法.下面我们来详细介绍. 方法一:使用菜单栏 首先,打开Finder,然后选择你要复制路径 ...