docker 原理之 namespace (上)
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 资源的隔离。这三种方式分别是:
- clone(): clone 在创建新进程的同时创建 namespace。
- setns(): setns 加入一个已经存在的 namespace。
- 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 (上)的更多相关文章
- Docker原理:Namespace
目录 Namespace UTS Namespae PID Namespace Mount Namespace User Namespace Network Namespace 参考 Namespac ...
- docker原理
Docker原理11 Linux Namespace 11 AUFS文件系统17 重新理解Docker的各种命令18 Docker原理 Linux Namespace docker是一个容器引擎,容器 ...
- Docker原理(图解+秒懂+史上最全)
背景:下一个视频版本,从架构师视角,尼恩为大家打造高可用.高并发中间件的原理与实操. 目标:通过视频和博客的方式,为各位潜力架构师,彻底介绍清楚架构师必须掌握的高可用.高并发环境,包括但不限于: 高可 ...
- 理解Docker(3):Docker 使用 Linux namespace 隔离容器的运行环境
本系列文章将介绍Docker的有关知识: (1)Docker 安装及基本用法 (2)Docker 镜像 (3)Docker 容器的隔离性 - 使用 Linux namespace 隔离容器的运行环境 ...
- 一篇不一样的docker原理解析
转自:https://zhuanlan.zhihu.com/p/22382728 https://zhuanlan.zhihu.com/p/22403015 在学习docker的过程中,我发现目前do ...
- Docker原理探究
问题思考:-------------------------------------Docker浅显原理理解-------------------------------------P1. ubunt ...
- Docker源码分析(七):Docker Container网络 (上)
1.前言(什么是Docker Container) 如今,Docker技术大行其道,大家在尝试以及玩转Docker的同时,肯定离不开一个概念,那就是“容器”或者“Docker Container”.那 ...
- docker原理(转)
转自:https://zhuanlan.zhihu.com/p/22382728 https://zhuanlan.zhihu.com/p/22403015 在学习docker的过程中,我发现目前do ...
- 一篇文章带你吃透 Docker 原理
容器的实现原理 从本质上,容器其实就是一种沙盒技术.就好像把应用隔离在一个盒子内,使其运行.因为有了盒子边界的存在,应用于应用之间不会相互干扰.并且像集装箱一样,拿来就走,随处运行.其实这就是 Paa ...
- Docker入门与进阶(上)
Docker入门与进阶(上) 作者 刘畅 时间 2020-10-17 目录 1 Docker核心概述与安装 1 1.1 为什么要用容器 1 1.2 docker是什么 1 1.3 docker设计目标 ...
随机推荐
- Spring Framework系统架构
- C++ Qt开发:Slider滑块条组件
Qt 是一个跨平台C++图形界面开发库,利用Qt可以快速开发跨平台窗体应用程序,在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置,实现图形化开发极大的方便了开发效率,本章将重点介绍Slider滑 ...
- 使用axios发送请求的几种方式
1.是什么? axios 它的底层是用了 XMLHttpRequest(xhr)方式发送请求和接收响应,xhr 相对于之前讲过的 fetch api 来说,功能更强大,但由于是比较老的 api,不支持 ...
- 玩转Spring状态机
说起Spring状态机,大家很容易联想到这个状态机和设计模式中状态模式的区别是啥呢?没错,Spring状态机就是状态模式的一种实现,在介绍Spring状态机之前,让我们来看看设计模式中的状态模式. 1 ...
- MySQL运维16-双主双从读写分离
一.双主双从架构介绍 在MySQL多主多从的架构配置中和双主双从是一样的,学会了双主双从的架构部署,多主多从的配置也同样就回了.下面以双主双从作为示例演示.其中一个主机maste1用于处理所有写请求, ...
- 【Redis】一文掌握Redis原理及常见问题
Redis是基于内存数据库,操作效率高,提供丰富的数据结构(Redis底层对数据结构还做了优化),可用作数据库,缓存,消息中间件等.如今广泛用于互联网大厂,面试必考点之一,本文从数据结构,到集群,到常 ...
- Confluence OGNL表达式注入命令执行漏洞(CVE-2022-26134)
Confluence OGNL表达式注入命令执行漏洞(CVE-2022-26134) 简介 Atlassian Confluence是企业广泛使用的wiki系统.2022年6月2日Atlassian官 ...
- 建议收藏备查!MySQL 常见错误代码说明
先给大家看几个实例的错误分析与解决方案. 1.ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/data ...
- 国产Geoscene Server 4.0编译SOE总结
背景:公司一直使用Arcgis Server 10.1.Visual Studio 2010开发SOE功能,随着国产化软件发展大趋势,SOE中的各种分析功能,需要升级到Geoscene Server环 ...
- 最基本的SpringCloud的搭建
对于springcloud而言,模块是按业务进行区分的: 父工程 依赖 <parent> <groupId>org.springframework.boot</group ...