调试 Docker 容器内部进程

首发于官方博客:https://nebula-graph.com.cn/posts/debug-nebula-graph-processes-docker/
摘要:本文以 Nebula Graph 进程为例,讲解如何不破坏原有容器的内容,也不用在其中安装任何的工具包前提下,像在本地一样来调试进程
需求
在开发或者测试过程中,我们经常会用到 vesoft-inc/nebula-docker-compose 这个 repo 下的部署方式,因为当初为了尽可能的压缩每个 Nebula Graph 服务的 docker 镜像的体积,所以开发过程中常用的一切工具都没有安装,甚至连编辑器 VIM 都没有。
这给我们在容器内部定位问题带来一定的难度,因为每次只能去 install 一些工具包,才能开展接下来的工作,甚是费事。其实调试容器内部的进程还有另外一种方式,不需要破坏原有容器的内容,也不用在其中安装任何的工具包就能像在本地一样来调试。
这种技术在 k8s 环境下其实已经挺常用,就是 sidecar 模式。原理也比较朴素就是再起一个容器然后让这个容器跟你要调试的容器共享相同的 pid/network 的 namespace。这样原容器中的进程和网络空间在调试容器中就能“一览无余”,而在调试容器中安装了你想要的一切顺手工具,接下来的舞台就是留于你发挥了。
演示
接下来我就演示一下如何操作:
我们先用上述的 docker-compose 方式在本地部署一套 Nebula Graph 集群,教程见 repo 中的 README。部署好后的结果如下:
$ docker-compose up -d
Creating network "nebula-docker-compose_nebula-net" with the default driver
Creating nebula-docker-compose_metad1_1 ... done
Creating nebula-docker-compose_metad2_1 ... done
Creating nebula-docker-compose_metad0_1 ... done
Creating nebula-docker-compose_storaged2_1 ... done
Creating nebula-docker-compose_storaged1_1 ... done
Creating nebula-docker-compose_storaged0_1 ... done
Creating nebula-docker-compose_graphd_1 ... done
$ docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
nebula-docker-compose_graphd_1 ./bin/nebula-graphd --flag ... Up (health: starting) 0.0.0.0:32907->13000/tcp, 0.0.0.0:32906->13002/tcp, 0.0.0.0:3699->3699/tcp
nebula-docker-compose_metad0_1 ./bin/nebula-metad --flagf ... Up (health: starting) 0.0.0.0:32898->11000/tcp, 0.0.0.0:32896->11002/tcp, 45500/tcp, 45501/tcp
nebula-docker-compose_metad1_1 ./bin/nebula-metad --flagf ... Up (health: starting) 0.0.0.0:32895->11000/tcp, 0.0.0.0:32894->11002/tcp, 45500/tcp, 45501/tcp
nebula-docker-compose_metad2_1 ./bin/nebula-metad --flagf ... Up (health: starting) 0.0.0.0:32899->11000/tcp, 0.0.0.0:32897->11002/tcp, 45500/tcp, 45501/tcp
nebula-docker-compose_storaged0_1 ./bin/nebula-storaged --fl ... Up (health: starting) 0.0.0.0:32901->12000/tcp, 0.0.0.0:32900->12002/tcp, 44500/tcp, 44501/tcp
nebula-docker-compose_storaged1_1 ./bin/nebula-storaged --fl ... Up (health: starting) 0.0.0.0:32903->12000/tcp, 0.0.0.0:32902->12002/tcp, 44500/tcp, 44501/tcp
nebula-docker-compose_storaged2_1 ./bin/nebula-storaged --fl ... Up (health: starting) 0.0.0.0:32905->12000/tcp, 0.0.0.0:32904->12002/tcp, 44500/tcp, 44501/tcp
这时我们分两个场景来演示,一个是进程空间,一个是网络空间。首先我们要先有一个顺手的调试镜像,我们就不自己构建了,从 docker hub 中找个已经打包好的用作演示,后期觉得不够用,我们可以维护一份 nebula-debug 的镜像,安装我们想要的所有调试工具,此处先借用社区内的方案 nicolaka/netshoot。我们先把镜像拉取到本地:
$ docker pull nicolaka/netshoot
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
vesoft/nebula-graphd nightly c67fe54665b7 36 hours ago 282MB
vesoft/nebula-storaged nightly 5c77dbcdc507 36 hours ago 288MB
vesoft/nebula-console nightly f3256c99eda1 36 hours ago 249MB
vesoft/nebula-metad nightly 5a78d3e3008f 36 hours ago 288MB
nicolaka/netshoot latest 6d7e8891c980 2 months ago 352MB
我们先看看直接执行这个镜像会是什么样:
$ docker run --rm -ti nicolaka/netshoot bash
bash-5.0# ps
PID USER TIME COMMAND
1 root 0:00 bash
8 root 0:00 ps
bash-5.0#
上面显示这个容器看不到任何 Nebula Graph 服务进程的内容,那么我们给其加点参数再看看:
$ docker run --rm -ti --pid container:nebula-docker-compose_metad0_1 --cap-add sys_admin nicolaka/netshoot bash
bash-5.0# ps
PID USER TIME COMMAND
1 root 0:03 ./bin/nebula-metad --flagfile=./etc/nebula-metad.conf --daemonize=false --meta_server_addrs=172.28.1.1:45500,172.28.1.2:45500,172.28.1.3:45500 --local_ip=172.28.1.1 --ws_ip=172.28.1.1 --port=45500 --data_path=/data/meta --log_dir=/logs --v=15 --minloglevel=0
452 root 0:00 bash
459 root 0:00 ps
bash-5.0# ls -al /proc/1/net/
total 0
dr-xr-xr-x 6 root root 0 Sep 18 07:17 .
dr-xr-xr-x 9 root root 0 Sep 18 06:55 ..
-r--r--r-- 1 root root 0 Sep 18 07:18 anycast6
-r--r--r-- 1 root root 0 Sep 18 07:18 arp
dr-xr-xr-x 2 root root 0 Sep 18 07:18 bonding
-r--r--r-- 1 root root 0 Sep 18 07:18 dev
...
-r--r--r-- 1 root root 0 Sep 18 07:18 sockstat
-r--r--r-- 1 root root 0 Sep 18 07:18 sockstat6
-r--r--r-- 1 root root 0 Sep 18 07:18 softnet_stat
dr-xr-xr-x 2 root root 0 Sep 18 07:18 stat
-r--r--r-- 1 root root 0 Sep 18 07:18 tcp
-r--r--r-- 1 root root 0 Sep 18 07:18 tcp6
-r--r--r-- 1 root root 0 Sep 18 07:18 udp
-r--r--r-- 1 root root 0 Sep 18 07:18 udp6
-r--r--r-- 1 root root 0 Sep 18 07:18 udplite
-r--r--r-- 1 root root 0 Sep 18 07:18 udplite6
-r--r--r-- 1 root root 0 Sep 18 07:18 unix
-r--r--r-- 1 root root 0 Sep 18 07:18 xfrm_stat
这次有点不一样了,我们看到 metad0 的进程了,并且其 pid 还是 1。看到这个进程再想对其做点啥就好办了,比如能不能直接在 gdb 中 attach 它,由于手边没有带 nebula binary 的对应 image,就留给大家私下探索吧。
我们已经看到 pid 空间通过指定 --pid container:<container_name|id> 可以共享了,那么我们接下来看看网络的情况,毕竟有时候需要抓个包,执行如下的命令:
bash-5.0# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
啥也没有,跟预想的有点不一样,我们有 metad0 这个进程不可能一个连接都没有。要想看到这个容器内的网络空间还要再加点参数,像如下方式再启动调试容器:
$ docker run --rm -ti --pid container:nebula-docker-compose_metad0_1 --network container:nebula-docker-compose_metad0_1 --cap-add sys_admin nicolaka/netshoot bash
bash-5.0# netstat -tulpn
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 172.28.1.1:11000 0.0.0.0:* LISTEN -
tcp 0 0 172.28.1.1:11002 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:45500 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:45501 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.11:33249 0.0.0.0:* LISTEN -
udp 0 0 127.0.0.11:51929 0.0.0.0:* -
这回就跟上面的输出不一样了,加了 --network container:nebula-docker-compose_metad0_1 运行参数后,metad0 容器内的连接情况也能看到了,那么想抓包调试就都可以了。
总结
通过运行另外一个容器,并让其跟想要调试的容器共享 pid/network namespace 是我们能像本地调试的关键。社区里甚至还有人基于上述想法开发了一些小工具进一步方便使用:
推荐阅读
调试 Docker 容器内部进程的更多相关文章
- 理解Docker容器的进程管理
摘要: Docker在进程管理上有一些特殊之处,如果不注意这些细节中的魔鬼就会带来一些隐患.另外Docker鼓励"一个容器一个进程(one process per container)&qu ...
- pycharm远程调试docker容器内程序
文章链接: https://blog.csdn.net/hanchaobiao/article/details/84069299 参考链接: https://blog.csdn.net/github_ ...
- 从Docker容器内部,如何连接到本机的本地主机?
原文 从Docker容器内部,如何连接到本机的本地主机? 编辑:如果您使用的是Docker-for-mac或Docker-for-Windows 18.03+,只需使用主机连接到您的mysql服务即可 ...
- 在宿主机上执行docker容器内部的shell或程序
为了避免反复进入docker容器内部操作,可以将一系列容器内部的指令由宿主机来操作完成. 在宿主机(作者主机为windows7)上执行centos容器(name为centos-1)中/usr目录下的“ ...
- Jenkins(4)docker容器内部修改jenkins容器时间
前言 用docker搭建的Jenkins环境时间显示和我们本地时间相差8个小时,需修改容器内部的系统时间 查看时间 查看系统时间 date-R 进入docker容器内部,查看容器时间 docker e ...
- Docker容器内部端口映射到外部宿主机端口的方法小结
转自:https://www.cnblogs.com/kevingrace/p/9453987.html Docker允许通过外部访问容器或者容器之间互联的方式来提供网络服务.容器启动之后,容器中可以 ...
- 如何进入到Docker容器内部
启动Docker容器后,对应的服务(例如tomcat启动)也通过dockerfile文件命令运行起来了,这个时候如何进行容器内部观察容器的运行状态. 1.docker attach 这个命令在创建一个 ...
- Docker容器内部端口映射到外部宿主机端口 - 运维笔记
Docker允许通过外部访问容器或者容器之间互联的方式来提供网络服务.容器启动之后,容器中可以运行一些网络应用,通过-p或-P参数来指定端口映射. 注意:宿主机的一个端口只能映射到容器内部的某一个端口 ...
- Docker容器内部端口映射到外部宿主机端口
Docker允许通过外部访问容器或者容器之间互联的方式来提供网络服务.容器启动之后,容器中可以运行一些网络应用,通过-p或-P参数来指定端口映射. 注意:宿主机的一个端口只能映射到容器内部的某一个端口 ...
- 【docker】 docker容器内部安装vi命令
有时会需要在docker容器内使用vi命令,但是新启动的docker容器内并没有vi命令,那就需要自己安装一个 1.使用命令 apt-get update 2.进行安装 apt-get install ...
随机推荐
- 4.7 C++ Boost 多线程并发库
Boost 库是一个由C/C++语言的开发者创建并更新维护的开源类库,其提供了许多功能强大的程序库和工具,用于开发高质量.可移植.高效的C应用程序.Boost库可以作为标准C库的后备,通常被称为准标准 ...
- Python 检测PE所启用保护方式
Python 通过pywin32模块调用WindowsAPI接口,实现对特定进程加载模块的枚举输出并检测该PE程序模块所启用的保护方式,此处枚举输出的是当前正在运行进程所加载模块的DLL模块信息,需要 ...
- 植物大战僵尸:寻找阳光掉落Call
本次实验内容:本次实验将接触到Call调用这个概念,什么是Call调用? Call相当于你在编程时所编写的函数,而高级语言中的函数最终也是会被编译器转换为汇编格式的Call调用,这些关键Call普遍都 ...
- ChatGPT 火了,一大批开源平替也来了
ChatGPT 凭一己之力让全球为 AI 沸腾,引发了科技巨头之间的聊天机器人军备竞赛. 众所周知,ChatGPT 的背后技术是 GPT(Generative Pre-trained Transf ...
- 面试官:Sentinel是如何实现限流的?
限流是一种通过控制系统对外提供的资源.服务或接口的访问数量或速率,以保护系统免受过载的一种策略. 它的目的是确保系统能够在承受范围内提供稳定和可靠的服务,避免因过多的请求而导致系统崩溃.资源耗尽或响应 ...
- Exadata存储节点的CPU限制成功了没?
上篇随笔谈到刷1/8 rack时,日志显示存储节点已经成功限制CPU的,可如果使用mpstat命令看貌似还是64 CPU,难道实际没有成功吗? [root@dbm08celadm03 ~]# mpst ...
- ASP.NET Core分布式项目实战(Consent 代码重构)--学习笔记
任务23:Consent 代码重构 新建一个 Sercices 文件夹,在文件夹下新建一个 ConsentService,专门用于处理 Consent 的逻辑,我们会把 controller 中不是 ...
- 文心一言 VS 讯飞星火 VS chatgpt (198)-- 算法导论14.3 6题
六.用go语言,说明如何来维护一个支持操作MIN-GAP的一些数的动态集Q,使得该操作能给出Q中两个最接近的数之间的差值.例如,Q=(1,5,9,15,18,22),则MIN-GAP返回18-15=3 ...
- 2024年,提升Windows开发和使用体验的实践经验 - RIME输入法
前言 上一篇文章介绍了 Windows 下的包管理器,本文继续介绍输入法. 事实上 Windows 的输入法生态比 Linux/Mac 丰富很多,不过很多国产输入法存在窃取隐私.植入广告.乱安装流氓软 ...
- Linux中作业控制命令
开门见山,最近.NET劝退师要在linux上写些长时间运行的脚本,获取Azure BlobStorage存储的数据. 记录一下Linux中后台执行作业的命令. Linux作业 作业(Job)是shel ...