運維開發必知——容器診斷工具集合

0x00 前言

隨着微服務架構和雲計算的普及,愈來愈多企業的應用都上了雲,不只是雲基礎設施 IaaS ,如 kubernetes 等 PaaS 項目也是愈來愈熱門。但新的技術會帶來新的架構複雜,同時也會使排查問題更加困難,所以不少運維和開發同窗都以爲用平時用的順手的工具和手段在容器裏排查問題很差使了。工欲善其事必先利其器,正是因爲這樣的狀況,因此咱們排查容器問題的時候,須要引入新的工具和手段。java

0x10 容器基礎

在學習工具的使用前,咱們首先須要簡單的瞭解下容器的原理。假如一臺機器是一間房子,那麼進程就是住在裏面的一個個的人,在單體應用的時代,全部人都住在一間房子裏,而容器技術就是經過一些手段把這些人都隔開,讓每一個人都覺得本身住上有獨衛(網絡,IPC,namespace 等資源)的單間。而
這些隔離和限制的主要使用的以下技術:node

  • cgroups 資源限制
  • namespace 資源隔離
  • rootfs 文件系統隔離

在單體應用的時代,全部的進程都在同一個命名空間裏,且啓動的進程都沒有隔離命名空間,那麼天然調試工具進程也在同一個命名空間,也就能夠 debug 其餘進程。而容器技術因爲分割成一個個的小房間,若是想要查看單個房間的狀況,雖然在大管家(宿主機)的上帝視角同樣能夠看到, 但爲了減小干擾並更加符合咱們平時的使用習慣,咱們就須要進入到房間(命名空間)裏面查看。python

例如咱們能夠經過 docker inspect CONTAINER_ID 獲取到某個容器資源隔離的文件的地址,以下"SandboxKey": "/var/run/docker/netns/50def85bf6e2"就是。linux

@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 命名空間等)。
在初步瞭解了容器的原理後,咱們即可進入工具的介紹了。git

0x20 netshoot

首先介紹的第一個工具是netshoot,netshoot 的自我定位就是容器網絡診斷的瑞士軍刀,簡單來講,netshoot 其實就是一個裝滿了各類工具的鏡像,他用起來也很簡單。github

  • 執行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 這個命令進入宿主機的網絡。別怕這個命令長,我來一一解釋下這條命令的各個選項的做用docker

  • 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 演示

下面我來演示幾個例子:ubuntu

  1. 使用 tcpdump 抓容器的包並拷貝 pcap 文件出來,便於用 wireshark 分析api

    @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目錄tomcat

  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 的調試工具介紹完成了。

相關文章
相關標籤/搜索