自己动手写Docker系列 -- 3.2增加容器资源限制

本文介绍如何在Docker中实现容器的内存限制,通过实例演示限制stress命令内存使用,阐述Cgroup的运用过程,包括参数接收改造、进程启动后的资源限制,并提供了资源抽象类和管理器类的实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

继上篇的Run命令容器的实现,本章节中将实现增加容器资源限制,已内存限制为例,展示如果现在容器的内存使用

源码说明

同时放到了Gitee和Github上,都可进行获取

本章节对应的版本标签是:3.2,防止后面代码过多,不好查看,可切换到标签版本进行查看

本次只实现了内存的限制,CPU的实现同理

结果演示

 进程号 USER      PR  NI    VIRT    RES    SHR    %CPU  %MEM     TIME+ COMMAND
  46805 lw        20   0  208668 204988    336 R 100.0   0.6   2:20.89 stress
  48004 root      20   0  208668  99520    272 D  37.9   0.3   0:04.68 stress

如上所示,第一个是没有使用资源限制100M的stress命令: stress --vm-bytes 200m --vm-keep -m 1

第二个是使用自己的docker限制了100M内存的stress命令:./main run -ti -mem 100m stress --vm-bytes 200m --vm-keep -m 1

后者是前者的一半,可以看出了资源限制起到了作用

编码实现

这边的Cgroup卡了很久才解决,照抄代码有一定的风险,还是理解原理后,自己一步一步的实现最终才完成了

参数接收改造

我们需要使用docker运行stress命令:

上面的参数比较多,原本的是不支持的,我们需要进行改造,代码如下:

var RunCommand = cli.Command{
   
   
	Name:  "run",
	Usage: `Create a container with namespace and cgroups limit mydocker run -ti [command]`,
	Flags: []cli.Flag{
   
   
		cli.BoolFlag{
   
   
			Name:  "ti",
			Usage: "enable tty",
		},
		// 增加内存等限制参数
		cli.StringFlag{
   
   
			Name:  "mem",
			Usage: "memory limit",
		},
		cli.StringFlag{
   
   
			Name:  "cpuset",
			Usage: "cpuset limit",
		},
		cli.StringFlag{
   
   
			Name:  "cpushare",
			Usage: "cpushare limit",
		},
	},
	/*
		这里是run命令执行的真正函数
		1.判断参数是否包含command
		2.获取用户指定的command
		3.调用Run function 去准备启动容器
	*/
	Action: func(context *cli.Context) error {
   
   
		if len(context.Args()) < 1 {
   
   
			return fmt.Errorf("missing container command")
		}
		// 这个是获取启动容器时的命令
		// 如果本次中的 stress --vm-bytes 200m --vm-keep -m 1,空格分隔后会存储在下面的cmdArray中
		var cmdArray []string
		for _, arg := range context.Args() {
   
   
			cmdArray = append(cmdArray, arg)
		}
		tty := context.Bool("ti")
		resConfig := &subsystem.ResourceConfig{
   
   
			MemoryLimit: context.String("mem"),
			CpuShare:    context.String("cpuShare"),
			CpuSet:      context.String("cpuSet"),
		}
		run.Run(tty, cmdArray, resConfig)
		return nil
	},
}

在init的时候也需要支持,在init命令执行的时候需要传入对应的参数

func NewParentProcess(tty bool, cmdArray []string) *exec.Cmd {
   
   
	commands := strings.Join(cmdArray, " ")
	log.Infof("conmand all is %s", commands)
	// 传入,cmdArray[0]是启动的程序,比如本篇中的是stress,command是完整的命令:stress --vm-bytes 200m --vm-keep -m 1
	cmd := exec.Command("/proc/self/exe", "init", cmdArray[0], commands)
	cmd.SysProcAttr = &syscall.SysProcAttr{
   <
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值