调试 Docker 容器内部进程

本文涉及的产品
容器服务 Serverless 版 ACK Serverless,952元额度 多规格
容器镜像服务 ACR,镜像仓库100个 不限时长
容器服务 Serverless 版 ACK Serverless,317元额度 多规格
简介: 本文以 Nebula Graph 进程为例,讲解如何不破坏原有容器的内容,也不用在其中安装任何的工具包前提下,像在本地一样来调试进程

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 是我们能像本地调试的关键。社区里甚至还有人基于上述想法开发了一些小工具进一步方便使用:

推荐阅读

目录
相关文章
|
8天前
|
运维 Cloud Native 虚拟化
一文吃透云原生 Docker 容器,建议收藏!
本文深入解析云原生Docker容器技术,涵盖容器与Docker的概念、优势、架构设计及应用场景等,建议收藏。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
一文吃透云原生 Docker 容器,建议收藏!
|
4天前
|
运维 Cloud Native 云计算
云原生之旅:Docker容器化实战
本文将带你走进云原生的世界,深入理解Docker技术如何改变应用部署与运维。我们将通过实际案例,展示如何利用Docker简化开发流程,提升应用的可移植性和伸缩性。文章不仅介绍基础概念,还提供操作指南和最佳实践,帮助你快速上手Docker,开启云原生的第一步。
|
2天前
|
关系型数据库 MySQL Java
【Docker最新版教程】一文带你快速入门Docker常见用法,实现容器编排和自动化部署上线项目
Docker快速入门到项目部署,MySQL部署+Nginx部署+docker自定义镜像+docker网络+DockerCompose项目实战一文搞定!
|
5天前
|
运维 持续交付 虚拟化
深入解析Docker容器化技术的核心原理
深入解析Docker容器化技术的核心原理
22 1
|
7天前
|
机器学习/深度学习 数据采集 Docker
Docker容器化实战:构建并部署一个简单的Web应用
Docker容器化实战:构建并部署一个简单的Web应用
|
7天前
|
JavaScript 开发者 Docker
Docker容器化实战:构建并部署一个简单的Web应用
Docker容器化实战:构建并部署一个简单的Web应用
|
7天前
|
持续交付 开发者 Docker
Docker容器化技术实战指南
Docker容器化技术实战指南
23 1
|
7天前
|
安全 Docker 微服务
深入理解Docker容器技术:从基础到实践
深入理解Docker容器技术:从基础到实践
|
9天前
|
缓存 监控 开发者
掌握Docker容器化技术:提升开发效率的利器
在现代软件开发中,Docker容器化技术成为提升开发效率和应用部署灵活性的重要工具。本文介绍Docker的基本概念,并分享Dockerfile最佳实践、容器网络配置、环境变量和秘密管理、容器监控与日志管理、Docker Compose以及CI/CD集成等技巧,帮助开发者更高效地利用Docker。
|
1天前
|
开发者 Docker Python
从零开始:使用Docker容器化你的Python Web应用
从零开始:使用Docker容器化你的Python Web应用
8 1
下一篇
无影云桌面