当前位置:首页 > 技术 > 正文内容

实现容器后台运行功能

访客 技术 2026年7月16日 1

本文介绍如何通过实现一个简易的容器管理工具,支持容器在后台运行的功能。我们将模拟 Docker 的 run -d 命令,使容器能够脱离前台运行。

开发环境

以下是测试所用的开发环境:

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 用户。

背景与需求

在之前的实现中,我们已经完成了一个基础的容器管理系统,但目前还缺少以下功能:

  • 支持容器后台运行(detach 模式)。
  • 查看当前运行中的容器状态。
  • 获取容器的日志输出。
  • 进入已运行的容器进行交互。

本文将专注于实现容器后台运行功能。

原理分析

早期版本的 Docker 中,所有容器的初始化进程都是从 Docker 守护进程 fork 出来的。这种方式虽然简单,但也存在一个问题:如果守护进程崩溃,所有容器会变成孤儿进程并被 init 进程接管。

为了解决这一问题,现代 Docker 使用了 containerd 来管理容器生命周期,并为每个容器启动一个独立的 containerd-shim 进程,确保即使 Docker 守护进程崩溃,容器仍能继续运行。

在我们的实现中,采用更简单的方案:

  • 主程序(如 mydocker)作为父进程。
  • 容器作为子进程,由父进程 fork 创建。
  • 当父进程退出时,容器会被 init 进程接管,从而实现后台运行。

代码实现

首先,在命令解析部分添加对 -d 参数的支持:

var runCommand = cli.Command{
    Name: "run",
    Usage: "创建一个带命名空间和资源限制的容器",
    Flags: []cli.Flag{
        cli.BoolFlag{
            Name:  "it",
            Usage: "启用 tty 模式",
        },
        cli.BoolFlag{
            Name:  "d",
            Usage: "以分离模式运行容器",
        },
    },
    Action: func(context *cli.Context) error {
        if len(context.Args()) < 1 {
            return fmt.Errorf("缺少容器命令")
        }

        cmdArgs := context.Args()
        isTTY := context.Bool("it")
        isDetach := context.Bool("d")

        if isTTY && isDetach {
            return fmt.Errorf("无法同时指定 it 和 d 参数")
        }

        resConfig := &subsystems.ResourceConfig{
            MemoryLimit: context.String("mem"),
            CpuSet:      context.String("cpuset"),
            CpuCfsQuota: context.Int("cpu"),
        }
        volume := context.String("v")

        Run(isTTY, isDetach, cmdArgs, resConfig, volume)
        return nil
    },
}

接下来调整容器启动逻辑,确保在分离模式下父进程不会等待子进程结束:

func Run(isTTY bool, isDetach bool, cmdArgs []string, resConfig *subsystems.ResourceConfig, volume string) {
    parentProc, writePipe := container.NewParentProcess(isTTY, volume)
    if parentProc == nil {
        log.Errorf("创建父进程失败")
        return
    }

    if err := parentProc.Start(); err != nil {
        log.Errorf("启动父进程错误:%v", err)
        return
    }

    cgroupMgr := cgroups.NewCgroupManager("custom-cgroup")
    defer cgroupMgr.Destroy()

    _ = cgroupMgr.Set(resConfig)
    _ = cgroupMgr.Apply(parentProc.Process.Pid)

    sendInitCommand(cmdArgs, writePipe)

    if !isDetach { // 如果不是分离模式,则等待子进程结束
        _ = parentProc.Wait()
        container.DeleteWorkSpace("/root/", volume)
    }
}

测试验证

编译并运行以下命令:

root@mydocker:~/mydocker# go build .
root@mydocker:~/mydocker# ./mydocker run -d top

可以看到,mydocker 命令直接返回,没有阻塞。

检查宿主机上的进程列表,确认容器是否正常运行:

root@mydocker:~/mydocker# ps -ef | grep -e PPID -e top
UID          PID    PPID  C STIME TTY          TIME CMD
root      166637       1  0 16:5 pts/8    00:00:00 top

可以看到,top 命令对应的进程正在运行,且其父进程为 1(即 init 进程),说明容器成功进入了后台运行模式。

总结

本文通过让父进程在启动子进程后立即退出的方式,实现了容器的后台运行功能。然而,当前实现还存在一个问题:后台运行的容器无法被追踪。因此,下一步将实现类似 ps 的功能,用于查看运行中的容器。

标签: containerdGo

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

linux screen 用法详情 (nohup 的替代方案)

一、screen 是什么?能干嘛?screen 是一个终端复用器,可以:在一个 SSH 会话中开多个“虚拟终端”SSH 断线后,程序仍然在后台运行随时重新连接到原来的会话特别适合:nohup 的替代方案跑脚本 / 爬虫 / 训练模型运维、远程开发二、安装 screen# CentOS / Rocky / Almayum install -y screen# Debian / Ubuntuapt i...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。