从零开始写 Docker(十五)---实现 mydocker run -e 支持环境变量传递

mydocker-run-e.png

本文为从零开始写 Docker 系列第十五篇,实现 mydocker run -e, 支持在启动容器时指定环境变量,让容器内运行的程序可以使用外部传递的环境变量。


完整代码见:https://github.com/lixd/mydocker
欢迎 Star


推荐阅读以下文章对 docker 基本实现有一个大致认识:

  • 核心原理:深入理解 Docker 核心原理:Namespace、Cgroups 和 Rootfs
  • 基于 namespace 的视图隔离:探索 Linux Namespace:Docker 隔离的神奇背后
  • 基于 cgroups 的资源限制
    • 初探 Linux Cgroups:资源控制的奇妙世界
    • 深入剖析 Linux Cgroups 子系统:资源精细管理
    • Docker 与 Linux Cgroups:资源隔离的魔法之旅
  • 基于 overlayfs 的文件系统:Docker 魔法解密:探索 UnionFS 与 OverlayFS
  • 基于 veth pair、bridge、iptables 等等技术的 Docker 网络:揭秘 Docker 网络:手动实现 Docker 桥接网络

开发环境如下:

root@mydocker:~# lsb_release -a
No LSB modules are available.
Distributor ID:	Ubuntu
Description:	Ubuntu 20.04.2 LTS
Release:	20.04
Codename:	focal
root@mydocker:~# uname -r
5.4.0-74-generic

注意:需要使用 root 用户

1. 概述

本节实现mydocker run -e flag,支持在启动容器时指定环境变量,让容器内运行的程序可以使用外部传递的环境变量。

2. 实现

实现也比较简单,就是在构建 cmd 的时候指定 Env 参数。

  • 1)run 命令增加 -e 参数
  • 2)cmd 中指定 Env 参数

run 命令增加 -e flag

在原来的基础上,增加 -e 选项指定环境变量,由于可能存在多个环境变量,因此允许用户通过多次使用 -e 选项来传递多个环境变量。

var runCommand = cli.Command{
	Name: "run",
	Usage: `Create a container with namespace and cgroups limit
			mydocker run -it [command]
			mydocker run -d -name [containerName] [imageName] [command]`,
	Flags: []cli.Flag{
    // 省略其他内容
		cli.StringSliceFlag{ // 增加 -e flag
			Name:  "e",
			Usage: "set environment,e.g. -e name=mydocker",
		},
	},

	Action: func(context *cli.Context) error {
		if len(context.Args()) < 1 {
			return fmt.Errorf("missing container command")
		}

		envSlice := context.StringSlice("e") // 获取 env 并传递
		Run(tty, cmdArray, envSlice, resConf, volume, containerName, imageName)
		return nil
	},
}

注意到这里的类型是cli. StringSliceFlag,即字符串数组参数,因为这是针对传入多个环境变量的情况。

然后增加对环境变量的解析,并且传递给 Run 函数。

cmd 对象指定 Env 参数

由于原来的 command 实际就是容器启动的进程,所以只需要在原来的基础上,增加一下环境变量的配置即可。

默认情况下,新启动进程的环境变量都是继承于原来父进程的环境变量,但是如果手动指定了环境变量,那么这里就会覆盖掉原来继承自父进程的变量

由于在容器的进程中,有时候还需要使用原来父进程的环境变量,比如 PATH 等,因此这里会使用 os.Environ() 来获取宿主机的环境变量,然后把自定义的变量加进去。

func NewParentProcess(tty bool, volume, containerId, imageName string, envSlice []string) (*exec.Cmd, *os.File) {
    // 省略其他内容
	cmd.Env = append(os.Environ(), envSlice...)
	return cmd, writePipe
}

到此,环境变量的实现就完成了。

3. 测试

通过 -e 注入两个环境变量测试一下

$ go build .
./mydocker run -it -name c1 -e user=17x -e name=mydocker busybox sh

然后在容器中查看环境变量

/ # env|grep user
user=17x
/ # env|grep name
name=mydocker

这里可以看到,手动指定的环境变量 user=17xname=mydocker 都已经可以在容器内可见了。

说明,-e flag 基本 ok。

下面创建一个后台运行的容器,查看一下是否可以。

root@mydocker:~/feat-run-e/mydocker# ./mydocker run -d -name c2 -e user=17x -e name=mydocker busybox top

查看 ID

root@mydocker:~/feat-run-e/mydocker# ./mydocker ps
ID           NAME        PID         STATUS      COMMAND     CREATED
9250006592   c2          228185      running     top         2024-02-27 10:37:09

然后通过 exec 命令进入容器,查看环境变量

root@mydocker:~/feat-run-e/mydocker# ./mydocker exec 9250006592 sh
# 容器内
/ # env|grep user
/ #

可以发现,并没有看到创建时指定的环境变量。

这里看不到环境变量的原因是:exec 命令其实是 mydocker 创建
的另外一个进程,这个进程的父进程其实是宿主机的的进程,并不是容器进程的。

因为在 Cgo 里面使用了 setns 系统调用,才使得这个进程进入到了容器内的命名空间

由于环境变量是继承自父进程的,因此这个 exec 进程的环境变量其实是继承自宿主机的,所以在 exec 进程内看到的环境变量其实是宿主机的环境变量

因此需要修改一下 exec 命令实现,使其能够看到容器中的环境变量。

4. 修改 mydocker exec 命令

首先提供了一个函数,可以根据指定的 PID 来获取对应进程的环境变量。

// getEnvsByPid 读取指定PID进程的环境变量
func getEnvsByPid(pid string) []string {
	path := fmt.Sprintf("/proc/%s/environ", pid)
	contentBytes, err := os.ReadFile(path)
	if err != nil {
		log.Errorf("Read file %s error %v", path, err)
		return nil
	}
	// env split by \u0000
	envs := strings.Split(string(contentBytes), "\u0000")
	return envs
}

由于进程存放环境变量的位置是/proc/<PID>/environ,因此根据给定的 PID 去读取这个文件,便可以获取环境变量。

在文件的内容中,每个环境变量之间是通过\u0000分割的,因此以此为标记来获取环境变量数组。

然后再启动 exec 进程时把容器中的环境变量也一并带上:

func ExecContainer(containerName string, comArray []string) {
	// 省略其他内容
	// 把指定PID进程的环境变量传递给新启动的进程,实现通过exec命令也能查询到容器的环境变量
	containerEnvs := getEnvsByPid(pid)
	cmd.Env = append(os.Environ(), containerEnvs...)
}

这样,exec 到容器内之后就可以看到所有的环境变量了。

再次测试一下,使用通用的 exec 命令进入容器,查看能否看到环境变量

root@mydocker:~/feat-run-e/mydocker# ./mydocker exec 9250006592 sh
/ # env|grep user
user=17x
/ # env|grep name
name=mydocker

ok,mydocker exec 已经可以获取到容器中的环境变量了。

5. 小结

本章实现了mydocker run -e flag 的添加,支持启动容器时传递环境变量到容器中。

核心实现就是启动 cmd 时指定 Env 参数,具体如下:

	cmd := exec.Command("/proc/self/exe", "init")
	cmd.Env = append(os.Environ(), envSlice...)

同时修改了 exec 命令,将容器中的环境变量append 到 exec 进程,便于查看。


**【从零开始写 Docker 系列】**持续更新中,搜索公众号【探索云原生】订阅,文章。


完整代码见:https://github.com/lixd/mydocker
欢迎关注~

相关代码见 refactor-isolate-rootfs 分支,测试脚本如下:

需要提前在 /var/lib/mydocker/image 目录准备好 busybox.tar 文件,具体见第四篇第二节。

# 克隆代码
git clone -b feat-run-e https://github.com/lixd/mydocker.git
cd mydocker
# 拉取依赖并编译
go mod tidy
go build .
# 测试 
./mydocker run -d -name c1 -e user=17x -e name=mydocker busybox top
# 查看容器 Id
./mydocker ps
# stop 停止指定容器
./mydocker exec ${containerId} sh

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【开发 | 环境配置】解决 VSCode 编写 eBPF 程序找不到头文件

问题描述&#xff1a; 在使用 vscode 编写 eBPF 程序时&#xff0c;如果不做一些头文件定位的操作&#xff0c;默认情况下头文件总是带有“红色下划线”&#xff0c;并且大部分的变量不会有提示与补全。 在编写代码文件较小时&#xff08;或者功能需求小时&#xff09;并不会…

42-2 应急响应之计划任务排查

一、进程排查 进程排查是指通过分析系统中正在运行的进程,以识别和处理恶意程序或异常行为。在Windows和Linux系统中,进程是操作系统的基本单位,因此对于发现和处理恶意软件或异常活动至关重要。恶意程序通常会以进程的形式在系统中运行,执行各种恶意操作,比如窃取信息、破…

CSAPP(datalab)解析

howManyBits /* howManyBits - 返回用二进制补码表示x所需的最小位数* 示例: howManyBits(12) 5* howManyBits(298) 10* howManyBits(-5) 4* howManyBits(0) 1* howManyBits(-1) 1* howManyBits(0x80000000) …

零部件销售|基于SSM+vue的轻型卡车零部件销售平台系统的设计与实现(源码+数据库+文档)

轻型卡车零部件销售平台 目录 基于SSM&#xff0b;vue的轻型卡车零部件销售平台系统的设计与实现 一、前言 二、系统设计 三、系统功能设计 1 系统功能模块 2 管理员功能模块 3 用户后台功能模块 四、数据库设计 五、核心代码 六、论文参考 七、最新计算机毕设选题…

Leetcode—2769. 找出最大的可达成数字【简单】

2024每日刷题&#xff08;139&#xff09; Leetcode—2769. 找出最大的可达成数字 实现代码 class Solution { public:int theMaximumAchievableX(int num, int t) {return num t * 2;} };运行结果 之后我会持续更新&#xff0c;如果喜欢我的文章&#xff0c;请记得一键三连…

Unity Assembly Definition Dotween 引用

原理&#xff1a; 具体Unity程序集原理用法&#xff0c;暂时留坑&#xff0c;不介绍了&#xff0c;相信有很多人也写过了 这里简单放个官方API链接 https://docs.unity3d.com/cn/current/Manual/ScriptCompilationAssemblyDefinitionFiles.html 现象 &#xff1a;Dotween引用…

二十七篇:未来掌控:嵌入式系统的革命性进展

未来掌控&#xff1a;嵌入式系统的革命性进展 1. 引言&#xff1a;嵌入式系统的重要性及其在未来科技中的角色 在当今这个数字化迅速发展的时代&#xff0c;嵌入式系统已成为推动现代科技进步的基石。从智能手机到智能家居&#xff0c;从自动驾驶汽车到复杂的工业控制系统&…

四元数学习总结(1)

导语&#xff1a;相比矩阵&#xff0c;用四元数处理3D旋转的优势是毋庸置疑的&#xff0c;但由于概念复杂&#xff0c;难于理解&#xff0c;一直令我摸不着头脑。最近学习更是发现在机器人、无人机、SLAM等先进领域&#xff0c;四元数被当成实数、整数这样的基础&#xff0c;所…

详解CSS(一)

目录 1.CSS是什么 2.基本语法规范 3.引入方式 3.1内部样式表 3.2行内样式表 3.3外部样式表 4.选择器 4.1基础选择器 4.1.1标签选择器 4.1.2类选择器 4.1.3id选择器 4.1.4通配符选择器 4.2复合选择器 4.2.1后代选择器 4.2.2子选择器 4.2.3并集选择器 4.2.4伪类选择…

虚拟化技术[3]之网络虚拟化

网络虚拟化 网络虚拟化简介核心层网络虚拟化接入层网络虚拟化虚拟机网络虚拟化案例: VMware网络虚拟化技术虚拟网络接口卡虚拟交换机vSwitch分布式交换机端口组VLAN 网络虚拟化简介 传统的数据中心&#xff1a;服务器之间操作系统和上层软件异构、接口与数据格式不统一&#x…

@JsonFormat注解出现日期序列化以及反序列化问题(日期比实际日期少一天)

文章目录 前言一、场景如下所示二、问题分析三、JsonFormat注解是什么以下是 JsonFormat 注解的一些常用属性&#xff1a; 四、解决问题解决方式&#xff1a;只需要指定对应的时区就好效果如下&#xff1a; 五、JsonFormat 注解时出现日期问题总结 前言 在一次的偶然机会下发现…

二进制中1的个数c++

题目描述 计算鸭给定一个十进制非负整数 NN&#xff0c;求其对应 22 进制数中 11 的个数。 输入 输入包含一行&#xff0c;包含一个非负整数 NN。(N < 10^9) 输出 输出一行&#xff0c;包含一个整数&#xff0c;表示 NN 的 22 进制表示中 11 的个数。 样例输入 100 …

【算法题】520 钻石争霸赛 2024 全解析

都是自己写的代码&#xff0c;发现自己的问题是做题速度还是不够快 520-1 爱之恒久远 在 520 这个特殊的日子里&#xff0c;请你直接在屏幕上输出&#xff1a;Forever and always。 输入格式&#xff1a; 本题没有输入。 输出格式&#xff1a; 在一行中输出 Forever and always…

Ubuntu 如何根据NVIDIA显卡型号确定对应的显卡驱动版本并安装

目录 一、查询推荐安装的驱动版本 二、安装推荐版本的驱动 1. 通过终端安装&#xff0c;只安装 nvidia 驱动&#xff08;亲测可用&#xff01;&#xff09; 2. 通过 software & Updates 安装&#xff0c;安装 nvidia 驱动。 三、查询能安装的最新的显卡驱动版本 1. 方…

洛谷P3574 [POI2014] FAR-FarmCraft(树形dp)

洛谷 P 3574 [ P O I 2014 ] F A R − F a r m C r a f t &#xff08;树形 d p &#xff09; \Huge{洛谷P3574 [POI2014] FAR-FarmCraft&#xff08;树形dp&#xff09;} 洛谷P3574[POI2014]FAR−FarmCraft&#xff08;树形dp&#xff09; 文章目录 题意题目说明 思路标程 题目…

『Stable Diffusion 』AI绘画,不会写提示词怎么办?

提示词 有没有想过&#xff0c;为什么你用 SD 生成的猫是长这样的。 而其他人可以生成这样的猫。 虽然生成的都是猫&#xff0c;但猫与猫之间还是有差距的。 如果你的提示词只是“cat”&#xff0c;那大概率就会出现本文第一张图的那个效果。而如果你加上一些形容词&#xff…

如何选择一款安全高效的数据自动同步工具?

随着科技的不断发展&#xff0c;企业处理的数据量愈发庞大。数字化浪潮的涌现使得数据在业务活动和决策中的角色变得日益重要&#xff0c;然而这些数据往往分布在不同的位置&#xff0c;需要进行同步和分类&#xff0c;以便更有效地利用。以下是一些常见的数据自动同步场景&…

【Linux安全】iptables防火墙(二)

目录 一.iptables规则的保存 1.保存规则 2.还原规则 3.保存为默认规则 二.SNAT的策略及应用 1.SNAT策略的典型应用环境 2.SNAT策略的原理 2.1.未进行SNAT转换后的情况 2.2.进行SNAT转换后的情况 3.SNAT策略的应用 3.1.前提条件 3.2.实现方法 三.DNAT策略及应用 1…

QT教程-一,初识QT

目录 一,QT是什么&#xff1f;能够使用它做什么&#xff1f; 二&#xff0c;Qt 能够使用的语言 三&#xff0c;Qt主要用于什么领域&#xff1f; 四&#xff0c;Qt开发的软件 一,QT是什么&#xff1f;能够使用它做什么&#xff1f; Qt是一个跨平台的 C 开发库&#xff0c;主…