根据PID号判断所属的容器

最近有个需求是根据PID号判断所属的容器,也就是说首先要根据docker创建的容器的名字获取namespace序号。原本的想法是从docker的源码中创建namspace的代码,并且打印出来。事后发现这部分代码有点难找的样子。。。然后不得不查找资料看看有没有办法能够直接通过命令来找到这种映射关系

目标

如何在容器外根据PID知道该进程处于哪个容器

原理

docker采用内核的namespace进行隔离,PID namespace隔离对进程PID重新标号,即两个不同namespace下的进程可以有同一个PID。每个PID namespace都有自己的计数程序。内核为所有的PID namespace维护了一个树状结构,最顶层的是系统初始时创建的,我们称之为root namespace。他创建的新PID namespace就称之为child namespace(树的子节点),而原先的PID namespace就是新创建的PID namespace的parent namespace(树的父节点)。通过这种方式,不同的PID namespaces会形成一个等级体系。所属的父节点可以看到子节点中的进程,并可以通过信号等方式对子节点中的进程产生影响。反过来,子节点不能看到父节点PID namespace中的任何内容。

—> 对于每个容器里的PID,在本机上也对其进行了编号,因而能够在/proc下找到

Libcontainer 是Docker中用于容器管理的包,它基于Go语言实现,通过管理namespaces、cgroups、capabilities以及文件系统来进行容器控制。

Linux基金会于2015年6月成立OCI(Open Container Initiative)组织,旨在围绕容器格式和运行时制定一个开放的工业化标准。runC就是Docker贡献出来的,按照该开放容器格式标准(OCF, Open Container Format)制定的一种具体实现。runC的前身实际上是Docker的libcontainer项目,目前runC也是对libcontainer包进行调用,去除了Docker包含的诸如镜像、Volume等高级特性,以最朴素简洁的方式达到符合OCF标准的容器管理实现。

OCF容器标准格式要求容器把自身运行时的状态持久化到磁盘中,这样便于外部的其他工具对此信息使用和演绎。该运行时状态以JSON格式编码存储。基于Linux内核的操作系统,该信息应该统一地存储在/run/opencontainer/containers目录,该目录结构下以容器ID命名的文件夹(/run/opencontainer/containers//state.json)中存放容器的状态信息并实时更新。有了这样默认的容器状态信息存储位置以后,外部的应用程序就可以在系统上简便地找到所有运行着的容器了。

state.json文件中包含的具体信息需要有:

  • 版本信息:存放OCI标准的具体版本号。
  • 容器ID:通常是一个哈希值,也可以是一个易读的字符串。在state.json文件中加入容器ID是为了便于之前提到的运行时hooks只需载入state.json就可以定位到容器,然后检测state.json,发现文件不见了就认为容器关停,再执行相应预定义的脚本操作。
  • PID:容器中运行的首个进程在宿主机上的进程号。
  • 容器文件目录:存放容器rootfs及相应配置的目录。外部程序只需读取state.json就可以定位到宿主机上的容器文件目录

—>runC基于libcontainer,runC会在state.json中保存容器中运行的首个进程在宿主机上的进程号,因而猜测只要找到docker起的容器对应的state.json,能够获取该容器的首个进程号。

从3.8版本的内核开始,用户就可以在/proc/[pid]/ns文件下看到指向不同namespace号的文件,效果如下所示,形如[4026531839]者即为namespace号。

$ ls -l /proc/$$/ns         <<-- $$ 表示应用的PID
total 0
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 ipc -> ipc:[4026531839]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 mnt -> mnt:[4026531840]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 net -> net:[4026531956]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 pid -> pid:[4026531836]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 user->user:[4026531837]
lrwxrwxrwx. 1 mtk mtk 0 Jan  8 04:12 uts -> uts:[4026531838]

—>获取到容器的首个进程号之后就能够找到对应的各种namespace ID。

实验

ps:命令见最后的脚本吧~

step1:启动容器(名字为:dry-run-test),获取该容器的ID

这里写图片描述

step2: 获取该容器对应的state.json,可以看到第一个进程是27749

这里写图片描述

step3:获取该容器对应的namespace序号,可以看到PID对应的namespace标号是4026532543

这里写图片描述

step4:在容器中再启动一个bash进程,猜测PID,并且看其namespace号。如下图,在执行/bin/bash前后发现/proc下多了一个27826进程,查看下对应的namespace,发现其依旧是4026532543

这里写图片描述

step5:为了便于区分,再在容器外开一个终端,发现多了进程27847,看下该bash对应的namespace,结果与容器的不一样

这里写图片描述

脚本

写个脚本来找到容器对应的namespace序号吧~

# coding=utf-8

import commands
import json

class executeException(BaseException):
    pass

def execute(cmd):
    status, output = commands.getstatusoutput(cmd)
    if status != 0:
        raise executeException
    return output

if __name__ == '__main__':
    name = "dry-run-test"

    namespaces = ["ipc", "mnt", "net", "pid", "user", "uts"]
    short_id = execute("docker ps | grep %s | awk '{print $1}'" % name)
    #print short_id
    full_id = execute("docker inspect -f '{{.Id}}' %s" % short_id)
    state = execute("cat /run/runc/%s/state.json" % full_id)
    f = json.loads(state)
    start_pid = f['init_process_pid']
    print "[INFO] the init_process_pid is %s for container %s" % (start_pid, name)
    ns_index = execute("ls -l /proc/%s/ns | awk '{print $11}'" % start_pid).split('\n')

    # save this ans into dict
    res = {}
    for index in ns_index:
        spr = index.find(":")
        name = index[:spr]
        num = index[spr+2: -1]
        #print name, num
        res[name] = num

    # print
    for ns in namespaces:
        print ns, res[ns]

运行之后(第一个进程与之前实验的不一致是因为我重启了那个容器)。。。

这里写图片描述

这样,可以维护一个容器名字与namespace序号之间的映射,根据PID获取namespace序号,进行获取其所属的容器,任务完成~~~~

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值