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. DRF过滤器

    https://www.django-rest-framework.org/api-guide/filtering 一般情况下,我们可以重写DRF视图类的get_queryset()方法来实现查询结果 ...

  2. 使用kubeadm在Centos8上部署kubernetes1.18

    // 查看系统版本 cat /etc/centos-release CentOS Linux release 8.1.1911 (Core) // 如果系统环境为8.0(云服务器默认最大安装环境为8. ...

  3. Python——第三章:函数的返回值

    函数的返回值: 函数执行之后. 会给调用方一个结果. 这个结果就是返回值 关于return:        函数只要执行到了return. 函数就会立即停止并返回内容. 函数内的return的后续的代 ...

  4. Spring Boot入坑-3-Maven简介

    概述 Maven是一个自动化构建工具 能够将支持Maven项目的依赖包从远程仓库拉取到本地仓库,并进行依赖管理 项目中通过pom.xml来描述项目,对项目依赖进行管理与配置 给项目提供编译.打包.安装 ...

  5. Java面试必考:什么是字节码?采用字节码的好处?

    Java面试必考:什么是字节码?采用字节码的好处? 于哥你好,最近在java面试中被问答到什么是字节码?采用字节码的好处是什么?瞬间懵了,如果你连这个都不知道,我保证你面试GG! 首先说下Java的优 ...

  6. 如何使用loki查询日志中大于某一数字的值的日志

    简介 loki是一款轻量级的日志收集中间件,比elk体系占用的内存更小,采用go语言开发,可以利用grafana来查询loki中存储的日志,loki存储日志只对提前预设的标签做索引,所以日志存储空间占 ...

  7. Java 展开或折叠PDF中的书签

    PDF中的书签功能可快速定位到指定阅读位置.对多层书签可根据阅读喜好设置层级展开或折叠.本文将通过Java程序代码介绍如何来实现PDF书签展开或折叠. 程序环境 Spire.Pdf.jar( 免费版3 ...

  8. 技术驱动,数据赋能,华为云GaussDB给世界一个更优选择

    摘要:5月16日,"数智深耕 让美好发生 2023华为云城市峰会广州站"成功举行. 5月16日,"数智深耕 让美好发生 2023华为云城市峰会广州站"成功举行. ...

  9. 应用架构步入“无服务器”时代 Serverless技术迎来新发展

    摘要:以"原生蓄力,云领未来"为主题的2021年云原生产业大会上,华为云Serverless函数服务产品经理分享了"华为云Serverless函数服务,让开发上云极简高效 ...

  10. Scala Http请求工具类

    import java.io.IOException import java.util import org.apache.http.client.ClientProtocolException im ...