原理

docker和宿主机共享内核的,进程在宿主机中也会显示的,如果一个进程是在docker容器中的进程,那么他的pid_namespace和docker容器中的pid_namespace是相同的。

具体实现

获取pid

遍历/proc 获取进程pid

获取namespace_pid

遍历获取 /proc/[pid]/ns 的信息,ns目录下对应有这几个信息 “cgroup”, “ipc”, “mnt”, “net”, “pid”, “user”, “uts”

获取docker namespace_pid

curl –unix-socket /var/run/docker.sock http://localhost/containers/bd54cf2711e0/json?strem=false

img

这是手动执行docker api获取的结果,那我们可以在代码中封装个docker api函数。

利用/var/run/docker.sock来获取docker的数据中的结果,只不过请求方式进行了封装,不是直接通过unix domain socket的方式来获取数据.其实dockerApi()本质上是利用了 docker remote api.通过/containers/json获取所有的镜像的信息,包括镜像名字,容器名称,容器id等等信息。

获取container_pid后,通过/proc/[pid]/ns/pid 获取namespace的pid,然后前面获取了进程所有的namespace_pid了,所以如果相等就可以得到哪些进程是属于container的了。

完整过程示例:

  1. 首先通过var/run/docker.sock http://localhost/containers/bd54cf2711e0/json?strem=false获取到container_pid是39138,查看对应的namespace_pid

    img

  2. 如图所示是docker里启动的进程

img

查看两个进程pid对应的namespace_pid

img

  1. 上面两步得到的namespace_pid相等,说明进程120493和进程120465是属于container进程39138创建的

如果把他们看成sql表,使用类似如下的语法就可以获取他们关联的信息了。

1
Select * from processes as p left join process_namespaces as pn on p.pid=pn.pid left join docker_containers as dc on pn.pid_namespace=dc.pid_namespace;

参考:

https://blog.spoock.com/2019/07/19/using-osquery-monitor-docker/