运维开发必知——容器诊断工具集合

标签: kubernetes docker | 发表时间:2019-12-01 14:30 | 作者:N0mansky
出处:https://segmentfault.com/blogs

0x00 前言

随着微服务架构和云计算的普及,越来越多企业的应用都上了云,不仅是云基础设施 IaaS ,如 kubernetes 等 PaaS 项目也是越来越热门。但新的技术会带来新的架构复杂,同时也会使排查问题更加困难,因此很多运维和开发同学都觉得用平时用的顺手的工具和手段在容器里排查问题不好使了。工欲善其事必先利其器,正是由于这样的情况,所以我们排查容器问题的时候,需要引入新的工具和手段。

0x10 容器基础

在学习工具的使用前,我们首先需要简单的了解下容器的原理。假如一台机器是一间房子,那么进程就是住在里面的一个个的人,在单体应用的时代,所有人都住在一间房子里,而容器技术就是通过一些手段把这些人都隔开,让每个人都以为自己住上有独卫(网络,IPC,namespace 等资源)的单间。而
这些隔离和限制的主要使用的如下技术:

  • cgroups 资源限制
  • namespace 资源隔离
  • rootfs 文件系统隔离

在单体应用的时代,所有的进程都在同一个命名空间里,且启动的进程都没有隔离命名空间,那么自然调试工具进程也在同一个命名空间,也就可以 debug 其他进程。而容器技术由于分割成一个个的小房间,如果想要查看单个房间的情况,虽然在大管家(宿主机)的上帝视角一样可以看到, 但为了减少干扰并更加符合我们平时的使用习惯,我们就需要进入到房间(命名空间)里面查看。

例如我们可以通过 docker inspect CONTAINER_ID 获取到某个容器资源隔离的文件的地址,如下 "SandboxKey": "/var/run/docker/netns/50def85bf6e2"就是。

  @ubuntu ➜ k8s-debug  docker inspect 0dde03166e02
...
            "SandboxKey": "/var/run/docker/netns/50def85bf6e2",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "7faa1e764317cdfadf7f31b7ed2fecff62b458211f79fadb3362c8e22755f326",
            "Gateway": "172.17.0.1",                    
...

而容器的诊断工具就是自带了部分调试工具的镜像,并能根据容器 ID 帮我们自动地进入到房间(网络,IPC 命名空间等)。
在初步了解了容器的原理后,我们便可进入工具的介绍了。

0x20 netshoot

首先介绍的第一个工具是 netshoot,netshoot 的自我定位就是容器网络诊断的瑞士军刀,简单来说,netshoot 其实就是一个装满了各种工具的镜像,他用起来也很简单。

  • 执行 docker run -it --net container:<container_name|container_id> nicolaka/netshoot 就行,这里的 --net是 docker 命令指定该容器要联结到哪个容器的网络命名空间
  • 如果要进入宿主机的命名空间则指定 --net host就行了
  • 如果要诊断 docker NIC设备的网络 情况,则可以用工具 nsenter进入NIC 设备的命名空间排查,后面我会介绍这个工具

另外,如果是在 kubernetes 里面,我们可以通过执行 kubectl run test-lab --generator=run-pod/v1 --rm -i --tty --overrides='{"spec": {"hostNetwork": true}}' --image nicolaka/netshoot -- /bin/bash 这个命令进入宿主机的网络。别怕这个命令长,我来一一解释下这条命令的各个选项的作用

  • kubectl run test_lab --generator=pod/v1 --rm -i --tty意思是创建一个一次性的名叫 test_lab的 Pod 资源并且使用标准输入输出交互
  • --overrides='{"spec": {"hostNetwork": true}}'意思是使用宿主机网络,具体哪台宿主机要看这个 Pod 调度到哪个节点。
  • --image nicolaka/netshoot指定 Pod 的镜像
  • -- 这是 bash 的内置命令选项,是标志命令的结束的意思,举个例子:如果我想要在文件里用 grep搜索 -v字符串, grep -v filename-v会被视为选项,但我如果使用 grep -- -v filename那么就可以正常搜索了

0x21 演示

下面我来演示几个例子:

  1. 使用 tcpdump 抓容器的包并拷贝 pcap 文件出来,便于用 wireshark 分析

        @ubuntu ➜ k8s-debug  mkdir -p /tmp/netshoot
    @ubuntu ➜ k8s-debug  docker ps
    CONTAINER ID        IMAGE                      COMMAND             CREATED             STATUS              PORTS                                          NAMES
    0dde03166e02        jumpserver/jms_all:1.5.4   "entrypoint.sh"     3 weeks ago         Up 3weeks          ...   mystifying_williamson
    @ubuntu ➜ k8s-debug  docker run -it -v /tmp/netshoot:/tmp --net container:0dde03166e02  nicolaka/netshoot
    Welcome to Netshoot! (github.com/nicolaka/netshoot)               
    root @ / 
    [1]   → tcpdump -nn -i any -w /tmp/pkg.pcap
    [2]   → exit

    具体命令使用与之前的差不多,只不过把宿主机上的 /tmp/netshoot目录 bind-mount 到了容器的 /tmp目录

  2. 有时候我们还需要调试 bridge 或者 overlay 网络,可以使用 nsenter,nsenter 可以进入任何命名空间

        @ubuntu ➜ ~  docker network ls
    NETWORK ID          NAME                DRIVER              SCOPE
    ...
    0ipu2p43c6jh        ingress             overlay             swarm
    697402c52a87        none                null                local
    @ubuntu ➜ ~   docker run -it --rm -v /var/run/docker/netns:/var/run/docker/netns --privileged=true nicolaka/netshoot
    Welcome to Netshoot! (github.com/nicolaka/netshoot)
    root @ /
    [1]   → ls /var/run/docker/netns/
    1-0ipu2p43c6  50def85bf6e2  83f9ffa847d7  default       ingress_sbox
    root @ /var/run/docker/netns
    [6]   → nsenter --net=/var/run/docker/netns/1-0ipu2p43c6 sh
    root @ /run/docker/netns
    [#]   → ifconfig
    br0     Link encap:Ethernet  HWaddr 02:61:F2:E4:26:3B
            inet addr:10.255.0.1  Bcast:10.255.255.255  Mask:255.255.0.0
            UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
    ...
    vxlan0  Link encap:Ethernet  HWaddr 02:61:F2:E4:26:3B
            UP BROADCAST RUNNING MULTICAST  MTU:1450  Metric:1
            RX packets:0 errors:0 dropped:0 overruns:0 frame:0
            TX packets:0 errors:0 dropped:0 overruns:0 carrier:0
            collisions:0 txqueuelen:0
            RX bytes:0 (0.0 B)  TX bytes:0 (0.0 B)
    root @ /run/docker/netns
    [#]   → bridge fdb show br0
    33:33:00:00:00:01 dev br0 self permanent
    01:00:5e:00:00:01 dev br0 self permanent
    02:61:f2:e4:26:3b dev vxlan0 master br0 permanent
    ...

    上面的命令首先我们是进入了 1-0ipu2p43c6的命名空间,即那个叫 ingress 的 overlay 网络,然后可以通过查看这个 NIC 设备上的fdb 表

  3. 我们也可以通过挂载 docker 的 unix sock 文件查看容器的 metrics

        docker run -it --rm -v /var/run/docker.sock:/var/run/docker.sock nicolaka/netshoot ctop

    如下图所示
    1.png

netshoot 工具非常强大,还有很多功能可以自行去探索, netshoot上有详细的说明。

0x30 docker-debug

上面介绍的 netshoot 主要定位于 docker 网络的诊断,从名字就可以看出来。而我们现在介绍的工具 docker-debug可以说是 netshoot 的升级版,他不仅可以进入目标容器的网络命名空间,还可以进入 pid,user,filesystem,ipc 的命名空间,所以我们可以操作的空间就更大了。话不多说,我们开始演示。

0x31 安装

首先我们要下载 docker-debug 的二进制文件

  @ubuntu ➜ docker-debug  wget docker-debug https://github.com/zeromake/docker-debug/releases/download/0.6.2/docker-debug-linux-amd64 -O docker-debug
@ubuntu ➜ docker-debug  chmod +x docker-debug
@ubuntu ➜ docker-debug  mv docker-debug /usr/bin
@ubuntu ➜ docker-debug  docker-debug info
Version:    0.6.2
Platform:    TravisLinux
Commit:        cf4cc41
Time:        2019-06-20 05:40:52 +0000

然后我赋予了文件执行权限并移动到 /usr/bin目录下,如果执行 docker-debug info看到有正确输出,则说明安装成功了

0x32 使用

使用就很简单了,首先我们获取到容器的名字或者容器 ID

  @ubuntu ➜ docker-debug  docker ps
CONTAINER ID        IMAGE                      COMMAND             CREATED             STATUS              PORTS                                          NAMES
0dde03166e02        jumpserver/jms_all:1.5.4   "entrypoint.sh"     3 weeks ago         Up 3 weeks          ...   mystifying_williamson

然后执行 docker-debug <CONTAINER_ID|CONTAINER_NAME> COMMAND就可以了

  @ubuntu ➜ docker-debug  docker-debug 0dde03166e02 bash
bash-5.0# netstat -lntp
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
...
tcp        0      0 0.0.0.0:8080            0.0.0.0:*               LISTEN      52/python3.6
tcp        0      0 0.0.0.0:8081            0.0.0.0:*               LISTEN      110/java
...
bash-5.0# ifconfig
eth0      Link encap:Ethernet  HWaddr 02:42:AC:11:00:02
          inet addr:172.17.0.2  Bcast:172.17.255.255  Mask:255.255.0.0
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:32776325 errors:0 dropped:0 overruns:0 frame:0
...
bash-5.0# ls /mnt/container/
anaconda-post.log  dev                lib                mnt                root               srv                usr
bin                etc                lib64              opt                run                sys                var
config             home               media              proc               sbin               tmp

我们可以看到已经进入了目标容器的 ipc,network,filesystem,pid 的命名空间了,而目标容器的root则挂载在了 /mnt/container目录下。
此外,我们还可以通过设置 docker-debug 在 ~/.docker-debug/config.toml的配置文件使用自定义的诊断镜像

  version = "0.6.1"
image = "nicolaka/netshoot:latest"
mount_dir = "/mnt/container"
timeout = 10000000000
config_default = "default"

[config]
  [config.default]
    host = "unix:///var/run/docker.sock"
    tls = false
    cert_dir = ""
    cert_password = ""

其他就不做过多介绍了

0x40 kubectl-debug

在介绍了 docker 的 debug 的工具后,我们了解了容器诊断工具的原理和使用,接下来我们要学习 kubernetes 的容器诊断工具。虽然 kubernetes 上也可以用我上面介绍的那些工具,但 kubernetes 上的容器毕竟运行在不同的 node 上,用起来就不太方便,所以就要用到 kubectl-debug 这个工具了。

kubectl-debug 其实就是一个 kubectl 的插件,他的原理和 docker 容器诊断工具大同小异。kubectl-debug 可以帮我们在 某个 Pod 的节点上起一个容器,并将这个容器加入到目标容器的pid,network,user,icp 的命名空间。kubectl-debug 架构主要可以分为两部分:

  • 客户端:kubectl-debug 二进制文件
  • 服务端:agent 容器

客户端通过控制 node 上的 agent 服务端与容器运行时通信,从而启动一个容器并进入到指定 Pod 的命名空间,可以说 agent 就是一个 debug 容器与客户端之间的中继。而从 kubectl-debug 的工作模式来看,可以分为两种模式:

  • 非常驻服务端:agentless
  • 常驻服务端: DaemonSet

简单来说就是 agentless 模式只有在每次 kubectl-debug 进行调试 Pod 的时候才会启动一个 agent 服务端,调试完成后自动清理 agent,此模式的优点是不那么占用 kubernetes 集群资源,而 DaemonSet 模式就是在每个节点上都会常驻一个 DaemonSet 的 agent, 好处就是启动快。
此外针对 node 节点无法直接访问的情况,kubectl-debug 还有一个 port-forward 模式,这里就不多介绍了。

由于 kubectl-debug 可能还不太完善,agentless 模式我这里用不了,所以我用的是 DaemonSet 模式,下面开始演示。

0x41 安装客户端

安装过程和 docker-debug 差不多

  1. 下载二进制文件: wget https://github.com/aylei/kubectl-debug/releases/download/v0.1.0/kubectl-debug_0.1.0_linux_amd64.tar.gz -O kubectl-debug.tar.gz
  2. 解压文件: tar -zxvf kubectl-debug.tar.gz kubectl-debug

0x42 安装 agent 服务端

  1. 下载 DaemonSet 的 yaml 文件: wget -f https://raw.githubusercontent.com/aylei/kubectl-debug/master/scripts/agent_daemonset.yml
  2. 修改 agent_daemonset.yml 文件

          ...
      18       hostNetwork: true  # 需要加上 hostNetwork: true,hostPort:10027 才会生效
      19       hostPID: true
      20       tolerations:
      21         - key: node-role.kubernetes.io/master
      22           effect: NoSchedule
      23       containers:
      24         - name: debug-agent
      25           image: aylei/debug-agent:v0.1.1 # 老版本镜像有问题,使用 v0.1.1新版本
      ...
      39           ports:
      40             - containerPort: 10027
      41               hostPort: 10027
      ...
  3. 创建 DaemonSet: kubectl apply -f agent_daemonset.yaml, 接下来我们可以看到每个节点上都创建了 debug-agent 的 DaemonSet,并且宿主机上都监听了10027端口。

         root @ master ➜  k8s-debug   kubectl get pods
     NAME                                   READY     STATUS    RESTARTS   AGE
     debug-agent-5gfk6                      1/1       Running   0          22h
     ...
     root @ master ➜  k8s-debug  netstat -lntp | grep 10027
     tcp6       0      0 :::10027                :::*                LISTEN      15510/debug-agent
  4. 执行命令 kubectl-debug <POD_NAME>就可以进行调试了

        root @ master ➜  k8s-debug  kubectl get pods
    NAME                             READY     STATUS             RESTARTS   AGE
    licai-gwapi-77465b4c66-hdjlb     1/1       Running            0          3d
    ...
    root @ master ➜  k8s-debug  kubectl-debug licai-gwapi-77465b4c66-hdjlb --agentless=false --port-forward=false
    pulling image nicolaka/netshoot:latest...
    ...
    bash-5.0# ps -ef | grep java
    1 root     23:24 /usr/local/openjdk-8/bin/java -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties...
    192 root      0:00 grep java
    bash-5.0# exit
    exit
    root @ master ➜  k8s-debug

    我们可以看到已经进入了目标容器的命名空间了,而kubectl-debug 客户端正是与每个 node 上的 10027 端口通信来控制 agent 对 Pod 的调试。

除了这些之外,kubectl-debug 还有很多配置可以自定义, kubectl-debug页面也有详细的介绍,至此从 docker 到 kubernetes 的调试工具介绍完成了。

相关 [运维 开发 容器] 推荐:

运维开发必知——容器诊断工具集合

- - SegmentFault 最新的文章
随着微服务架构和云计算的普及,越来越多企业的应用都上了云,不仅是云基础设施 IaaS ,如 kubernetes 等 PaaS 项目也是越来越热门. 但新的技术会带来新的架构复杂,同时也会使排查问题更加困难,因此很多运维和开发同学都觉得用平时用的顺手的工具和手段在容器里排查问题不好使了. 工欲善其事必先利其器,正是由于这样的情况,所以我们排查容器问题的时候,需要引入新的工具和手段.

容器运维最佳实践

- - IT瘾-tuicool
本文介绍了一组使容器更易于运维的最佳实践. 这些实践涉及安全性、监控和日志记录等广泛的主题,旨在使应用程序更容易在 Kubernetes Engine和一般的容器中运行. 这里讨论的许多实践都受到 12因子方法的启发 ,12因素方法是一个构建云原生应用程序的优质资源. 这些最佳实践的重要等级不一样.

[译] 谷歌团队的容器运维最佳实践

- - 编程学习网
谷歌大神们带你进行容器运维最佳实践. 本文介绍了一组使容器更易于运维的最佳实践. 这些实践涉及安全性、监控和日志记录等广泛的主题,旨在使应用程序更容易在 Kubernetes Engine 和一般的容器中运行. 这里讨论的许多实践都受到 12 因素方法的启发 ,12 因素方法是一个构建云原生应用程序的优质资源.

Docker:Swarm + Stack 一站式部署容器集群_运维_Lunex的博客-CSDN博客

- -
Docker:Swarm + Stack 一站式部署容器集群. Docker将应用沙盒化,将程序的运行与主机的环境隔离开,本身是十分适合进行应用程序的堆砌、组合、互动的. 新推出的(还算新吧)swarm功能将不同的docker容器集成为管理节点-工作节点的集群模式,建立联系的同时也可以分配计算资源,实现以docker容器为单元的并行任务.

每秒百万级流式日志处理架构的开发运维调优笔记 | Cloud

- -
荣幸之至,我们团队在实时日志分析、搜索项目中曾经应对过百万级的挑战,在这方面有长足的进步. 本文以笔记和问答的形式记录了我们曾经遇到过的实际问题及解决方案,而非小白式的大数据科普文章. 相信只有真正做过每秒近百万以上的实时日志处理业务,遇到过棘手问题,才能深刻感受我们当时越不过高坎的窘境与解决问题后的狂喜.

Java应用运维

- - BlueDavy之技术blog
对于互联网产品或长期运行的产品而言,运维工作非常重要,尤其是在产品复杂了以后,在这篇blog中就来说下Java应用的运维工作(ps:虽然看起来各种语言做的系统的运维工作都差不多,但细节上还是会有很多不同,so本文还是只讲Java的). 苦逼的码农按照需求开发好了一个全新的Java Web应用,该发布上线给用户用了,要把一个Java Web应用发布上线,首先需要搭建运行的环境,运行的环境需要有JDK、APPServer,在已经装好了os的机器上装上JDK和APPServer,开发好的Java Web应用可以用maven直接打成war或ear,将这个打好的包scp或其他方式到目标机器上,准备妥当,就差启动了.

ZooKeeper运维经验

- - Juven Xu
ZooKeeper 是分布式环境下非常重要的一个中间件,可以完成动态配置推送、分布式 Leader 选举、分布式锁等功能. 在运维 AliExpress ZooKeeper 服务的一年多来,积累如下经验:. 3台起,如果是虚拟机,必须分散在不同的宿主机上,以实现容灾的目的. 如果长远来看(如2-3年)需求会持续增长,可以直接部署5台.

hadoop运维笔记1

- - 企业架构 - ITeye博客
hadoop使用中的几个小细节(二). 1 某次正常运行mapreduce实例时,抛出错误. 经查明,问题原因是linux机器打开了过多的文件导致. 用命令ulimit -n可以发现linux默认的文件打开数目为1024,修改/ect/security/limit.conf,增加hadoop soft 65535.

运维工具体系

- - SegmentFault 最新的文章
发布变更流程管理工具:做为系统接口与其他角色的工作衔接. 并提供审批环节控制发布变更的风险. 流程管理工具并不负责具体的业务操作的执行,只是作为单据系统跟踪流程和确保闭环. 告警和突发管理工具:体现业务受损的告警自动建单管理. 通过建单管理告警和突发确保流程的闭环,以及每次故障都能够总结出经验,并未度量业务的可用性提供KPI.

ES运维--快速重启_运维_SouthPark的专栏-CSDN博客

- -
修改es配置,重启集群成本巨大. ES集群已有25T数据,27个节点,24个数据节点(热盘12和hot节点,慢盘12个stale节点,3个mater节点),数据节点的启动,加入集群后需要初始化全部索引,这个过程过程很慢. 全部重启一次可能要一天,非常耗时. 重启后经常遇到少量索引一直处于unassigned状态,导致集群一直是red状态.