广告
返回顶部
首页 > 资讯 > 服务器 >Docker exec 的实现原理介绍
  • 243
分享到

Docker exec 的实现原理介绍

2024-04-02 19:04:59 243人浏览 八月长安
摘要

我使用了 Docker exec 命令进入到了容器当中。在了解了linux Namespace 的隔离机制后,你应该会很自然地想到一个问题:docker exec 是怎么做到进入容器

我使用了 Docker exec 命令进入到了容器当中。在了解了linux Namespace 的隔离机制后,你应该会很自然地想到一个问题:docker exec 是怎么做到进入容器里的呢?
实际上,Linux Namespace 创建的隔离空间虽然看不见摸不着,但一个进程的 Namespace 信息在宿主机上是确确实实存在的,并且是以一个文件的方式存在。

比如,通过如下指令,你可以看到当前正在运行的 Docker 容器的进程号(PID)是 25686:

$ docker inspect --fORMat '{{ .State.Pid }}' 4ddf4638572d
25686

这时,你可以通过查看宿主机的 proc 文件,看到这个 25686 进程的所有 Namespace 对应的文件:

$ ls -l /proc/25686/ns
total 0
lrwxrwxrwx 1 root root 0 Aug 13 14:05 cgroup -> cgroup:[4026531835]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 ipc -> ipc:[4026532278]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 mnt -> mnt:[4026532276]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 net -> net:[4026532281]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 pid_for_children -> pid:[4026532279]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 user -> user:[4026531837]
lrwxrwxrwx 1 root root 0 Aug 13 14:05 uts -> uts:[4026532277]

可以看到,一个进程的每种Linux Namespace,都在它对应的 /proc/[进程号]/ns 下有一个对应的虚拟文件,并且链接到一个真实的 Namespace 文件上。
有了这样一个可以“hold 住”所有 Linux Namespace 的文件,我们就可以对 Namespace 做一些很有意义事情了,比如:加入到一个已经存在的 Namespace 当中。

这也就意味着:一个进程,可以选择加入到某个进程已有的 Namespace 当中,从而达到“进入”这个进程所在容器的目的,这正是 docker exec 的实现原理。

而这个操作所依赖的,乃是一个名叫 setns() 的 Linux 系统调用。它的调用方法,我可以用如下一段小程序为你说明:

#define _GNU_SOURCE
#include <fcntl.h>
#include <sched.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

#define errExit(msg) do { perror(msg); exit(EXIT_FaiLURE);} while (0)

int main(int arGC, char *argv[]) {
int fd;

fd = open(argv[1], O_RDONLY);
if (setns(fd, 0) == -1) {
errExit("setns");
}
execvp(argv[2], &argv[2]);
errExit("execvp");
}

这段代码功能非常简单:它一共接收两个参数,第一个参数是 argv[1],即当前进程要加入的 Namespace 文件的路径,比如/proc/25686/ns/net;而第二个参数,则是你要在这个 Namespace 里运行的进程,比如 /bin/bash。

这段代码的的核心操作,则是通过 open() 系统调用打开了指定的 Namespace 文件,并把这个文件的描述符 fd 交给 setns() 使用。在 setns() 执行后,当前进程就加入了这个文件对应的 Linux Namespace 当中了。

现在,你可以编译执行一下这个程序,加入到容器进程(PID=25686)的 Network Namespace 中:

$ gcc -o set_ns set_ns.c
$ ./set_ns /proc/25686/ns/net /bin/bash
$ ifconfig
eth0 Link encap:Ethernet HWaddr 02:42:ac:11:00:02
inet addr:172.17.0.2 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::42:acff:fe11:2/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:12 errors:0 dropped:0 overruns:0 frame:0
TX packets:10 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:976 (976.0 B) TX bytes:796 (796.0 B)

lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:65536 Metric:1
RX packets:0 errors:0 dropped:0 overruns:0 frame:0
TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:0 (0.0 B) TX bytes:0 (0.0 B)

正如上所示,当我们执行 ifconfig 命令查看网络设备时,我会发现能看到的网卡“变少”了:只有两个。而我的宿主机则至少有四个网卡。这是怎么回事呢?
实际上,在 setns() 之后我看到的这两个网卡,正是我在前面启动的 Docker 容器里的网卡。也就是说,我新创建的这个 /bin/bash 进程,由于加入了该容器进程(PID=25686)的 Network Namepace,它看到的网络设备与这个容器里是一样的,即:/bin/bash 进程的网络设备视图,也被修改了。
而一旦一个进程加入到了另一个 Namespace 当中,在宿主机的 Namespace 文件上,也会有所体现。

在宿主机上,你可以用 ps 指令找到这个 set_ns 程序执行的 /bin/bash 进程,其真实的 PID 是 28499:

# 在宿主机上
ps aux | grep /bin/bash
root 28499 0.0 0.0 19944 3612 pts/0 S 14:15 0:00 /bin/bash

这时,如果按照前面介绍过的方法,查看一下这个 PID=28499 的进程的 Namespace,你就会发现这样一个事实:

$ ls -l /proc/28499/ns/net
lrwxrwxrwx 1 root root 0 Aug 13 14:18 /proc/28499/ns/net -> net:[4026532281]

$ ls -l /proc/25686/ns/net
lrwxrwxrwx 1 root root 0 Aug 13 14:05 /proc/25686/ns/net -> net:[4026532281]

/proc/[PID]/ns/net 目录下,这个 PID=28499 进程,与我们前面的 Docker 容器进程(PID=25686)指向的 Network Namespace 文件完全一样。这说明这两个进程,共享了这个名叫net:[4026532281] 的 Network Namespace。
此外,Docker 还专门提供了一个参数,可以让你启动一个容器并“加入”到另一个容器的 Network Namespace 里,这个参数就是 -net,比如:

$ docker run -it --net container:4ddf4638572d busybox ifconfig

这样,我们新启动的这个容器,就会直接加入到 ID=4ddf4638572d 的容器,也就是我们前面的创建的应用容器(PID=25686)的Network Namespace 中。所以,这里 ifconfig 返回的网卡信息,跟我前面那个小程序返回的结果一模一样,你也可以尝试一下。
而如果我指定–net=host,就意味着这个容器不会为进程启用 Network Namespace。这就意味着,这个容器拆除了 Network Namespace 的“隔离墙”,所以,它会和宿主机上的其他普通进程一样,直接共享宿主机的网络栈。这就为容器直接操作和使用宿主机网络提供了一个渠道。

到此这篇关于Docker exec 的实现原理介绍的文章就介绍到这了,更多相关Docker exec 的实现内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Docker exec 的实现原理介绍

本文链接: https://www.lsjlt.com/news/144828.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Docker exec 的实现原理介绍
    我使用了 docker exec 命令进入到了容器当中。在了解了Linux Namespace 的隔离机制后,你应该会很自然地想到一个问题:docker exec 是怎么做到进入容器...
    99+
    2022-11-13
  • Docker exec的实现原理是什么
    本篇内容主要讲解“Docker exec的实现原理是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Docker exec的实现原理是什么”吧!我使用了 docker ex...
    99+
    2023-06-29
  • Docker 文件系统-AUFS 原理介绍
    目录什么是联合文件系统如何配置 Docker 的 AUFS 模式AUFS 工作原理AUFS 是如何存储文件的?AUFS 是如何工作的?1. 读取文件修改文件或目录AUFS 演示准备演...
    99+
    2022-11-13
  • Docker 隔离与限制原理介绍
    目录一、为什么 Docker 比虚拟机受欢迎1、优点2、不足二、资源限制三、总结一、为什么 Docker 比虚拟机受欢迎 在上一篇文章中,详细介绍了 Linux 容器中用来实现&ld...
    99+
    2022-11-13
  • Vue指令的实现原理介绍
    这篇文章主要介绍“Vue指令的实现原理介绍”,在日常操作中,相信很多人在Vue指令的实现原理介绍问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Vue指令的实现原理介绍”的疑惑有所帮助!接下来,请跟着小编一起来...
    99+
    2023-06-20
  • Docker容器实现原理及容器隔离性踩坑介绍
    正如Docker官方的口号:“Build once,Run anywhere,Configure once,Run anything”,Docker被贴上了如下标签:轻巧、秒级启动、版本管理、可移植性等等,这些优点让它出现之初就收...
    99+
    2023-06-04
  • Java中枚举的实现原理介绍
    目录基本概述使用方式条件选择循环遍历集合映射常用方法总结基本概述 在 JDK1.5 之前,通过定义常量使用的都是:public static fianl。而枚举的设计,就是把相关的常...
    99+
    2022-11-12
  • PythonCountingBloomFilter原理与实现详细介绍
    目录前言原理一、BF 为什么不支持删除二、什么是 Counting Bloom Filter三、Counter 大小的选择简单的实现总结前言 标准的 Bloom Filter 是一种...
    99+
    2022-11-11
  • Redis分布式锁的实现原理介绍
    这篇文章主要讲解了“Redis分布式锁的实现原理介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Redis分布式锁的实现原理介绍”吧!一、写在前面现在面试,一般都会聊聊分布式系统这块的东西...
    99+
    2023-06-02
  • python pow函数的底层实现原理介绍
    一、最朴素的方法和pow比较 python中求两个a的b次方,常见的方法有:pow(a,b),a**b。那么这两个是否有区别,而且他们底层是怎么实现的呢? 最容易想到的方法就是:循环...
    99+
    2022-11-11
  • MySQL 存储引擎 - InnoDB 实现原理介绍
    存储结构   内存结构 内存结构主要包括 Buffer Pool、Change Buffer、Adaptive Hash Index和 Log Buffer 四大组件 Buffer Pool 缓冲池,简称BP。BP以 Page 页为单位,...
    99+
    2023-10-04
    mysql 数据库
  • Spring的工作原理介绍
    本篇内容主要讲解“Spring的工作原理介绍”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Spring的工作原理介绍”吧!spring原理内部最核心的就是IOC了,动态注入,让一个对象的创建不用...
    99+
    2023-05-30
    spring
  • chatgpt的算法原理介绍
    chatgpt的算法原理是基于GPT-3,先通过人工标注方式训练出强化学习的冷启动模型与reward反馈模型,最后通过强化学习的方式...
    99+
    2023-02-09
    chatgpt
  • mysql复制的原理介绍
    这篇文章主要讲解了“mysql复制的原理介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“mysql复制的原理介绍”吧! 图中m...
    99+
    2022-10-18
  • GoldenGate的基本原理介绍
    本篇内容介绍了“GoldenGate的基本原理介绍”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!什么是Go...
    99+
    2022-10-18
  • PPython的原理和架构介绍
    本篇内容主要讲解“PPython的原理和架构介绍”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“PPython的原理和架构介绍”吧!介绍Python 与 PHP 都是广泛使用的语言,各有所长,让人...
    99+
    2023-06-16
  • MySQL的锁机制原理介绍
    这篇文章主要讲解了“MySQL的锁机制原理介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“MySQL的锁机制原理介绍”吧! M...
    99+
    2022-10-18
  • 网络爬虫的原理介绍
    这篇文章主要介绍“网络爬虫的原理介绍”,在日常操作中,相信很多人在网络爬虫的原理介绍问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”网络爬虫的原理介绍”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!认识浏览器...
    99+
    2023-06-02
  • Java GC的工作原理介绍
    本篇内容主要讲解“Java GC的工作原理介绍”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java GC的工作原理介绍”吧!JVM内存结构由堆、栈、本地方法栈、方法区等部分组成,结构图如下所示...
    99+
    2023-06-17
  • TiDB-Wasm 原理与实现 | Hackathon 优秀项目介绍
    作者:Ti-Cool 上周我们推送了《让数据库运行在浏览器里?TiDB + WebAssembly 告诉你答案》,向大家展示了 TiDB-Wasm 的魅力:TiDB-Wasm 项目是 TiDB Hackathon 2019 中诞生的二等奖...
    99+
    2019-08-25
    TiDB-Wasm 原理与实现 | Hackathon 优秀项目介绍
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作