Docker实战06|深入剖析Docker Run命令

前几篇文章中,重点讲解了Linux Namespace、Cgroups、AUFS的核心原理,同样也是Docker的底层原理实现。目录如下:

  • • 《Docker实战01|容器与开发语言》

  • • 《Docker实战02|Namespace》

  • • 《Docker实战03|Cgroups》

  • • 《Docker实战04|Union File System》

  • • 《Docker实战05|Docker构建流程分析》

有需要的小伙伴可以回顾一下。

核心原理讲完,接下来的内容就是如何构造容器、构造镜像了。首先,先从Docker run命令开始深入剖析。

深入剖析Docker Run命令

获取代码

git clone https://gitee.com/mjreams/docker.git
git checkout code3-1

本章即将开始真正踏上构造自己的容器的道路。我们会基于当前的操作系统创 建一个与宿主机隔离的容器环境,下面就开始吧。

Linux /proc文件介绍

Linux下的/proc文件系统是由内核提供的,它其实不是一个真正的文件系统,只包含了系统运行时的信息(比如系统内存、mount设备信息、一些硬件配直等),它只存在于内存中,而不占用外存空间。它以文件系统的形式,为访问内核数据的操作提供接口。实际上,很多系统工具都是简单地去读取这个文件系统的某个文件内容,比如lsmod,其实就是cat /proc/modules。

当遍历这个目录的时候,会发现很多数字,这些都是为每个进程创建的空间,数字就是它们的PID。

图片

下面介绍几个比较重要的部分:

图片

 run命令实现

首先,实现一个简单的run命令,类似docker run -it [command] 。后续会继续添加network等功能。

目前代码目录结构如下:

图片

  • • main.go 作为项目入口

  • • main_command.go 中包含了所有的 command

  • • run.go 则是 run 命令核心逻辑

  • • container 目录则是一些 container 的核心实现

再来看一下main.go

图片

使用github.com/urfave/cli命令行工具,提供了几个基本的命令。包括initCommand、runCommand。然后在app.Before内初始化一下log的配置。

再来看一下main_command.go中runCommand的具体实现:

图片

Action这里是run命令执行的真正函数:

  1. 1. 判断参数是否包含command

  2. 2. 获取用户制定的command

  3. 3. 调用Run方法去启动容器

Run(createTty, cmdArray, resConf, containerName, volume, imageName, envSlice, network, portmapping)

再来深入看一下Run方法具体做了哪些事情:

图片

NewParentProcess 启动一个新进程

这里是父进程,也就是当前进程执行的内容。

  1. 1. 这里的/proc/se1f/exe调用中,/proc/self/ 指的是当前运行进程自己的环境,exec 其实就是自己调用了自己,使用这种方式对创建出来的进程进行初始化

  2. 2. 后面的args是参数,其中init是传递给本进程的第一个参数,在本例中,其实就是会去调用initCommand去初始化进程的一些环境和资源

  3. 3. 下面的clone参数就是去fork出来一个新进程,并且使用了namespace隔离新创建的进程和外部环境。

  4. 4. 如果用户指定了-it参数,就需要把当前进程的输入输出导入到标准输入输出上

图片

那么,init函数里面做了些什么呢 ?

图片

RunContainerInitProcess 启动容器的init进程

  1. 1. 这里的init函数是在容器内部执行的,也就是说,代码执行到这里后,容器所在的进程其实就已经创建出来了,这是本容器执行的第一个进程。

  2. 2. 使用mount先去挂载proc文件系统,以便后面通过ps等系统命令去查看当前进程资源的情况。

这里 Mount 意思如下:

  • • MS_NOEXEC 在本文件系统 许运行其 程序。

  • • MS_NOSUID 在本系统中运行程序的时候, 允许 set-user-ID set-group-ID

  • • MS_NOD 这个参数是自 Linux 2.4 ,所有 mount 的系统都会默认设定的参数。

本函数最后的syscall.Exec是最为重要的一句黑魔法,正是这个系统调用实现了完成初始化动作并将用户进程运行起来的操作。

首先,使用 Docker 创建起来一个容器之后,会发现容器内的第一个程序,也就是 PID 为 1 的那个进程,是指定的前台进程。但是,我们知道容器创建之后,执行的第一个进程并不是用户的进程,而是 init 初始化的进程。这时候,如果通过 ps 命令查看就会发现,容器内第一个进程变成了自己的 init,这和预想的是不一样的。

有没有什么办法把自己的进程变成 PID 为 1 的进程呢?

这里 execve 系统调用就是用来做这件事情的。

syscall.Exec这个方法,其实最终调用了 Kernel 的 int execve(const char *filename, char *const argv[], char *const envp[]);这个系统函数。

它的作用是执行当前 filename 对应的程序,它会覆盖当前进程的镜像、数据和堆栈等信息,包括 PID,这些都会被将要运行的进程覆盖掉。

也就是说,调用这个方法,将用户指定的进程运行起来,把最初的 init 进程给替换掉,这样当进入到容器内部的时候,就会发现容器内的第一个程序就是我们指定的进程了。

具体流程如下:

图片

测试

root@mydocker:~/mydocker# go build .
root@mydocker:~/mydocker# ./mydocker run -it /bin/sh
{"level":"info","msg":"init come on","time":"2024-01-07T14:18:35+08:00"}
{"level":"info","msg":"command: /bin/sh","time":"2024-01-07T14:18+08:00"}
{"level":"info","msg":"command:/bin/sh","time":"2024-01-07T14:18:35+08:00"}
# ps -ef
UID          PID    PPID  C STIME TTY          TIME CMD
root           1       0  0 09:47 pts/1    00:00:00 /bin/sh
root           5       1  0 09:47 pts/1    00:00:00 ps -ef

在看一下ubuntu的

[root@docker ~]# docker run -it ubuntu /bin/sh
# ps -ef
UID        PID  PPID  C STIME TTY          TIME CMD
root         1     0  0 01:49 pts/0    00:00:00 /bin/sh
root         7     1  0 01:49 pts/0    00:00:00 ps -ef

几乎是一模一样。

关注「程序员溪昂」带你学习更多云原生知识。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/616293.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件测试公式之如何高质量的做BUG分析?

对于BUG分析,测试人员再熟悉不过了。但如果是面对大量的BUG,要如何有效的分析呢?有什么好的方案和行动项?今天聊聊这个话题。 01 BUG分析简单可以分为两类:宏观BUG分析 和 微观BUG分析。 宏观BUG分析:在…

响应式Web开发项目教程(HTML5+CSS3+Bootstrap)第2版 例3-5 CSS3 动画

代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>CSS3 动画</title> <style> .img {width: 150px; } keyframes rotate { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg);} } img…

【C++】入门与过渡

【C】入门与过渡 文章目录 【C】入门与过渡一、命名空间二、输入输出(cin/cout)三、缺省参数四、函数重载五、引用作用使用注指针和引用的区别 六、内联函数七、auto关键字八、空指针 注&#xff1a;本文为学习笔记&#xff0c;只记录了一些重点&#xff0c;有些比较简单的内容…

使用boost.hana在编译期加密字符串

在当今数字时代&#xff0c;软件安全问题愈发凸显&#xff0c;攻击者利用各种手段对应用程序进行破解和逆向工程的尝试也日益猖獗。其中&#xff0c;通过使用OllyDbg、IDA等软件加载应用程序&#xff0c;分析程序中的字符串&#xff0c;进而找到关键条件判断&#xff0c;实施软…

【python】进阶--->MySQL数据库(一)

一、mysql数据库 关系型数据库 &#xff1a; 一些相关的表和其他数据库对象的集合。 表是由行和列组成。列包含一组命名的属性(也称为字段)。 行包含一组记录&#xff0c;行和列的交集称为数据项(也叫字段值)。 数据库(database) : 存储数据的仓库&#xff0c;本质上就是一个文…

《C++ Primer》第14章 重载运算与类型转换(二)

参考资料&#xff1a; 《C Primer》第5版《C Primer 习题集》第5版 14.8 函数调用运算符&#xff08;P506&#xff09; 如果类重载了函数调用运算符&#xff0c;则我们可以像使用函数一样使用该类的对象。这样的类同时也能存储状态&#xff0c;所以它们比普通函数更加灵活。…

x-cmd pkg | czg - git commit 智能生成工具

目录 简介首次用户功能特点竞品和相关作品进一步探索 简介 czg 源于 commitizen/cz-cli 交互插件中 cz-git 的延伸项目&#xff0c;重新使用 TypeScript 编写的零依赖独立的 Node.js 命令行工具。旨在使用交互友好的方式&#xff0c;辅助用户生成规范的 git commit message 约…

游泳耳机哪种款式好?最值得入手的游泳耳机大全

在如今注重健康和娱乐的生活方式中&#xff0c;游泳作为一项全身性的运动备受欢迎。然而&#xff0c;对于热爱水中活动的人们来说&#xff0c;选择一款出色的游泳耳机至关重要。好的游泳耳机不仅能提供清晰的音质&#xff0c;还能有效防水&#xff0c;让您在水中尽情畅游的同时…

MySQL中约束是什么?

&#x1f389;欢迎您来到我的MySQL基础复习专栏 ☆* o(≧▽≦)o *☆哈喽~我是小小恶斯法克&#x1f379; ✨博客主页&#xff1a;小小恶斯法克的博客 &#x1f388;该系列文章专栏&#xff1a;重拾MySQL &#x1f379;文章作者技术和水平很有限&#xff0c;如果文中出现错误&am…

记录由客户端http请求原因引起的5xx响应问题排查过程

看到 http 状态码 5xx&#xff0c;很多开发者第一感觉就是服务端的问题&#xff0c;其实并不全是。下面我遇到的问题就是一个例外。 问题描述 最近在为反向代理 nginx 配置 auth_request 后&#xff0c;出现了请求504错误。 504状态码是HTTP协议中的一种服务器错误状态码。当…

Windows压缩包的MySQL安装方式

1.下载压缩包 https://cdn.mysql.com//Downloads/MySQL-8.0/mysql-8.0.35-winx64.zip 2.解压压缩包&#xff08;建议将解压到非C盘&#xff0c;路径不要出现特殊符号&#xff09; 3.在MySQL主目录下&#xff0c;创建my.ini空文件&#xff08;先创建一个txt文件&#xff0c;进…

如何改造现有文件为 CMD 模块

如何改造现有文件为 CMD 模块 经过一段考察&#xff0c;我们终于要在项目中引入模块机制和 Sea.js 了&#xff0c;那么如何将现有的文件改造成 CMD 模块就成了重要的问题。下面针对一些典型场景来说明包装的方式。 首先还是请大家详细了解下 CMD 模块定义规范&#xff0c;只要…

JavaScript删除数组中指定元素的5种方法

文章目录 目录 文章目录 前言 一、数组是什么&#xff1f; 二、讲解数组 总结 前言 在JavaScript开发中&#xff0c;处理数组是一项非常常见的任务。有时候我们需要从数组中删除特定的元素&#xff0c;以便对数组进行进一步操作或者满足特定的需求。幸运的是&#xff0c;JavaS…

Unity中URP下实现能量罩(外发光)

文章目录 前言一、实现菲涅尔效果1、求 N ⃗ \vec{N} N 2、求 V ⃗ \vec{V} V 3、得出菲涅尔效果4、得出菲涅尔相反效果5、增加菲涅尔颜色二、能量罩 交接处高亮 和 外发光效果结合1、修改混合模式,使能量罩透明2、限制 0 ≤ H i g h L i g h t C o l o r ≤ 1 0\leq HighL…

【字符串】贝贝的车牌问题(UPC)

题目描述 广州市车管所为每一辆入户的汽车都发放一块车牌&#xff0c;车牌的号码由六个字符组成&#xff0c;如A99452、B88888等&#xff0c;这个字符串从左边数起的第一个字符为大写英文字母&#xff0c;如A、B、C等&#xff0c;表示这辆车是属于广州市区内的汽车还是郊区的汽…

Swoft - Bean

一、Bean 在 Swoft 中&#xff0c;一个 Bean 就是一个类的一个对象实例。 它(Bean)是通过容器来存放和管理整个生命周期的。 最直观的感受就是省去了频繁new的过程&#xff0c;节省了资源的开销。 二、Bean的使用 1、创建Bean 在【gateway/app/Http/Controller】下新建一个名为…

7.vue学习笔记(模板引用+组件组成+组件嵌套关系)

文章目录 1.模板引用2.组件组成3.组件嵌套关系3.1.App.vue3.1.1.Header.vue3.1.2.Main.vue3.1.3.Aside.vue 1.模板引用 在Vue直接读取DOM 虽然Vue的声明性渲染模型为你抽象了大部分对DOM的直接操作&#xff08;事件&#xff0c;内容&#xff0c;属性&#xff09;&#xff0c; …

持久双向通信网络协议-WebSocket-入门案例实现demo

1 介绍 WebSocket 是基于 TCP 的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手&#xff0c;两者之间就可以创建持久性的连接&#xff0c; 并进行双向数据传输。 HTTP协议和WebSocket协议对比&#xff1a; HTTP是短连接&#xff0…

Edge 浏览器设置自动刷新

要在 Microsoft Edge 浏览器中设置自动刷新&#xff0c;您可以使用第三方扩展来实现这一功能。目前&#xff0c;Edge 浏览器本身并没有内置的自动刷新功能。以下是启用自动刷新的一般步骤&#xff1a; 打开 Microsoft Edge 扩展商店&#xff1a;首先&#xff0c;在 Edge 浏览器…

【MATLAB】小波_LSTM神经网络时序预测算法

有意向获取代码&#xff0c;请转文末观看代码获取方式~也可转原文链接获取~ 1 基本定义 小波-LSTM神经网络时序预测算法是一种结合了小波变换和长短期记忆神经网络&#xff08;LSTM&#xff09;的时间序列预测方法。 小波变换是一种信号处理方法&#xff0c;能够将信号分解为…