Dockerコンテナの稼働状況をシステムコールレベルで掘り下げて確認できるSysdigを使ってみた

先日開催された[Sysdig meetup Tokyo #1](https://connpass.com/event/77957/)に参加してきました。かなりマニアックで面白そうなツールだったので、早速使ってみました。
基本的な部分しか触れてないですが紹介します。

# Sysdigとは？

https://www.sysdig.org
コンテナ環境でのトラブルシューティングに使えるモニタリングツールです。
コンテナが稼働するホストに導入し、ホスト側のsystem callレイヤの情報を収集し、分析できるようになります。straceとtcpdumpを組み合わせたような情報がもりもり取れます。
収集した情報をグラフィカルに表示するUIも用意されています。
今のところあまり日本語情報は少ないですが、5年ほど前から提供はされているようです。

# Sysdigの基本構成

## Sysdig

これがもっとも基礎となるコマンドで、情報を集める役割を担います。
sysdig -w でsystem callの情報や通信情報をキャプチャしてファイル保存します。
sysdig -r で保存したファイルから情報を読み取り出力します。

## Csysdig

csysdigは、コマンドライン上でリアルタイムに状況を表示するツール。topコマンドで稼働状態をみるようなイメージのツールです。
リアルタイムで状況を見れるだけでなく、sysdigで収集したファイルの情報を見る目的でも使えます。

## Sysdig inspect

sysdigで収集した情報をGUIで表示するツール。
デスクトップアプリケーションで、Linux、MacOSに対応しています。Windows版は現時点ではなさそうな感じです。

## Chisels

sysdigで収集したデータに対して、値の計算とか統計情報を算出するスクリプト群です。
詳細は後述します。

## Tracers

sysdigの収集データに対して特定の処理をトレースできるよう、収集イベントに開始点と終了点を定義して管理できるようにする仕組みです。

## Sysdig falco

sysdigで収集可能なイベントに対してルールを定義して、特定の事象が発生したことを検知して通知するagent型のツールです。(これは次回の記事で別途紹介予定)

# インストール

以下の環境に導入してみました。
詳細は[こちら](https://github.com/draios/sysdig/wiki/How-to-Install-Sysdig-for-Linux)

* Dockerホスト　← sysdig
    * Amazon Linux 
* クライアントPC ← sysdig inspect
    * MacOSX

## sysdigインストール

sysdigは各ホストOS上にインストールする必要があるので、Dockerホスト環境のAmazonLinuxに導入。

基本的には以下のコマンド一発で入るようです。

```shell-session
curl -s https://s3.amazonaws.com/download.draios.com/stable/install-sysdig  | sudo bash
```
しかし、今回試したAmazonLinux環境では、kernelのバージョンと、kernel-headersのバージョンが異なるためにエラーが出て動かなかったです。

```
error opening device /dev/sysdig0. Make sure you have root credentials and that the sysdig-probe module is loaded.
```

[このWiki](https://support.sysdig.com/hc/en-us/articles/204187225-Why-does-the-agent-install-fail-with-kernel-headers-cannot-be-found-)にある通り、kernelのバージョンとkernel-header,kernel-develのバージョンが違う場合、sysdigの実行ができません。
なので、kernelをバージョンアップする、もしくはkernel-headerの古いバージョンを入れる等で対処する必要あるようです。

AmazonLinux上だと、以下のようにひとまずアップデート

```
yum install kernel
shutdown -r now
```

再起動後はちゃんと使えるようになります。
これで完了です。

ちなみに、MacOSも対応しているということで導入してみたのですが、後述のsysdig -wで情報を収集して書き出す処理は対応してないようでした。

```shell-session
% sysdig                                                                                                                                                      
live capture not supported on Darwin
```

ちなみに、sysdigをインストールするとcsysdigコマンドも使える状態になっています。

## sysdig inspect

次に、GUIのsysdig inspectを導入してみます。
詳細は[こちら](https://github.com/draios/sysdig-inspect/)
インストーラが用意されているので基本的には導入するだけです。
LinuxについてはGUIを使うことになるのでデスクトップ系のパッケージが必要です。
今回はMacOS上にインストーラを使ってインストールしました。

インストール完了後、sysdig inspectを起動できるのですが、何故かうまくデータの読み込み表示ができなかったのでMacOS自体を再起動させると使える状態になりました。


# 使ってみる

## sysdig

まずはDockerホスト上でsysdigを実行してキャプチャしてみます。

```shell-session
$ sysdig -w sample.scap &
```

これだけで全情報をsample.scapというファイルに吐き出します。キャプチャファイルは、数秒で数十MBぐらいに膨れ上がるので、実際使うときは、以下のようにオプションを指定して、永遠にデータが蓄積されてしまわないように調整が必要です。

```shell-session
$ sysdig -w sample.scap -C 10 -W 5 &
```

* -C: 1ファイルのサイズを10MBまでにリミットをかける
* -W: 保持するファイルは5ファイルまでにリミットをかける

できあがったファイルはこんな感じになります。

```shell-session
# ls -lh sample.*
total 330M
-rw-r--r-- 1 root root 5.9M Feb 27 23:54 sample.scap0
-rw-r--r-- 1 root root 9.6M Feb 27 23:53 sample.scap1
-rw-r--r-- 1 root root 9.6M Feb 27 23:54 sample.scap2
-rw-r--r-- 1 root root 9.6M Feb 27 23:54 sample.scap3
-rw-r--r-- 1 root root 9.6M Feb 27 23:54 sample.scap4
```

5ファイルがいっぱいになると、0から順に上書いて吐き出し続けます。

次に出力されたファイルを読み込んで特定の情報だけを取り出してみます。
例えば、javaのプロセスの情報のみを取り出す場合は以下のように実行

```shell-session
$ sysdig -r sample.scap0 proc.name=java
・・・略
149277 00:11:22.148062767 0 java (4153) > futex addr=7FEFA111D8E4 op=137(FUTEX_PRIVATE_FLAG|FUTEX_WAIT_BITSET) val=1
149278 00:11:22.148067328 0 java (4153) > switch next=28576(rpm) pgft_maj=0 pgft_min=58 vm_size=3225664 vm_rss=456352 vm_swap=0
158972 00:11:22.172189133 0 java (3529) < futex res=-110(ETIMEDOUT)
158973 00:11:22.172193827 0 java (3529) > futex addr=7FEFA0105128 op=129(FUTEX_PRIVATE_FLAG|FUTEX_WAKE) val=1
158974 00:11:22.172194639 0 java (3529) < futex res=0
158978 00:11:22.172207287 0 java (3529) < clock_gettime
158979 00:11:22.172208134 0 java (3529) > clock_gettime
・・・略
```

proc.name=javaの部分がフィルタリングの指定です。
sysdig -wで情報収集・書き出し時にもこのフィルタリングは有効です。
どんなフィルタリングが可能かについては、以下のコマンドで確認可能です。

```shell-session
$ sysdig -l
----------------------
Field Class: fd

fd.num          the unique number identifying the file descriptor.
fd.type         type of FD. Can be 'file', 'directory', 'ipv4', 'ipv6', 'unix',
                 'pipe', 'event', 'signalfd', 'eventpoll', 'inotify' or 'signal
                fd'.
fd.typechar     type of FD as a single character. Can be 'f' for file, 4 for IP
                v4 socket, 6 for IPv6 socket, 'u' for unix socket, p for pipe,
                'e' for eventfd, 's' for signalfd, 'l' for eventpoll, 'i' for i
                notify, 'o' for unknown.
fd.name         FD full name. If the fd is a file, this field contains the full
                 path. If the FD is a socket, this field contain the connection
                 tuple.
・・・略
```

例えば、特定のコンテナ(以下例の場合、rancher-agentという名称のコンテナ)の情報だけを見たければ、

```shell-session
$ sysdig -r sample.scap0 container.name=rancher-agent
22478 00:11:21.131303307 0 agent (5419) < futex res=-110(ETIMEDOUT)
22479 00:11:21.131307832 0 agent (5419) > clock_gettime
22480 00:11:21.131308741 0 agent (5419) < clock_gettime
22481 00:11:21.131310968 0 agent (5419) > futex addr=F76358 op=1(FUTEX_WAKE) val=1
22482 00:11:21.131315083 0 agent (5419) < futex res=1
22483 00:11:21.131316546 0 agent (5419) > clock_gettime
22484 00:11:21.131316850 0 agent (5419) < clock_gettime
22485 00:11:21.131321364 0 agent (5419) > futex addr=C420282110 op=1(FUTEX_WAKE) val=1
22486 00:11:21.131323507 0 agent (5419) < futex res=1
22487 00:11:21.131324067 0 agent (5419) > clock_gettime
```

こんな具合です。

## csysdig

コマンドライン上からcsysdigを起動してみます。

```shell-session
$ csysdig
```

![csysdig1.png](https://qiita-image-store.s3.amazonaws.com/0/45851/03206fee-952a-4eae-edac-db2ca7383d24.png)


topコマンドで見るような感じの画面が表示されます。
この状態で、F2でViewの選択ができます。

![csysdig2.png](https://qiita-image-store.s3.amazonaws.com/0/45851/e96779e9-63d1-48a4-3dec-9d78e078bdc4.png)


ここで例えば、Containersを選択すると、以下の図のように、このホスト上で稼働するコンテナの情報だけが表示されます。

![csysdig3.png](https://qiita-image-store.s3.amazonaws.com/0/45851/211c9fde-bb30-d70a-536d-f5955e4e0dc8.png)

さらに、特定のコンテナの中の状態を確認したければ該当のコンテナの情報を選択してEnter

![csysdig4.png](https://qiita-image-store.s3.amazonaws.com/0/45851/4d17d5bb-4a2e-c416-f65e-311eb169ebeb.png)


コンテナで稼働するプロセスの情報が見れます。
こんな感じで情報を深掘りして調査できます。

## sysdig inspect

MacOSにインストールしたsysdig inspectのアプリを起動すると以下の図のような画面が表示されます。

![inspect1.png](https://qiita-image-store.s3.amazonaws.com/0/45851/769f6bd0-df19-443e-7155-426be6b90cf7.png)


sysdig inspectはscapのファイルを読み込んでグラフィカルに表示してくれます。
先程取得したsample.scap0のファイルをオープンしてみるとこんな感じで表示されます。

![inspect2.png](https://qiita-image-store.s3.amazonaws.com/0/45851/e73b389d-2e63-8204-3d75-d03957068651.png)

GUI上で状況の全体が把握できます。

左側のView欄からみたいものを選択すると様々な情報が見れます。基本的には先程のcsysdigで見れる情報をそのままGUIで表示できるような感じです。

![inspect3.png](https://qiita-image-store.s3.amazonaws.com/0/45851/269ae193-611f-f081-1e90-92947c31d1f8.png)

I/Oの流れ、システムコールの流れもそれぞれ見れます。

![inspect5.png](https://qiita-image-store.s3.amazonaws.com/0/45851/c384e9fc-3a2e-99d0-ff2f-d819f610101f.png)

![inspect4.png](https://qiita-image-store.s3.amazonaws.com/0/45851/9abca758-de4e-570e-89f7-d351191f7216.png)

## Chisels

sysdigコマンドのオプションで-cをつけることでChiselsの様々なスクリプト処理が実現できます。

例えば、fileの読み書きのバイト数の多い対象ファイルを集計して出力する***topfiles_bytes***を使う場合は以下のような感じです。

```shell-session
$ sysdig -r sample.scap0 -c topfiles_bytes
Bytes               Filename
--------------------------------------------------------------------------------
15.97M              /usr/share/sysdig-inspect/libnode.so;5a95f3a2
6.94M               /root/sysdig-inspect-latest.x86_64.rpm
5.50M               /var/lib/mysql/ibdata1
475.34KB            /usr/share/sysdig-inspect/resources/app/ember-electron/backend/node_modules/mocha/mocha.js;5a95f3a2
333.13KB            /usr/share/sysdig-inspect/resources/app/ember-electron/backend/node_modules/chai/chai.js;5a95f3a2
326.02KB            /usr/share/sysdig-inspect/resources/app/ember-electron/backend/node_modules/nodemon/package-lock.json;5a95f3a2
```

フィルタ条件で絞り込んだ上での集計も可能です。
上記のような結果に対して、ファイル名にinspectを含まないものだけの読み書きバイト数を出力してみます。

```shell-session
$ sysdig -r sample.scap0 -c topfiles_bytes　"not fd.name contains inspect"
Bytes               Filename
--------------------------------------------------------------------------------
5.50M               /var/lib/mysql/ibdata1
35.71KB             /proc/cmdline
33.98KB             /dev/.udev/queue.tmp
12.50KB             /var/lib/mysql/ib_logfile0
1.62KB              /usr/lib/x86_64-linux-gnu/libnetfilter_conntrack.so.3
1.62KB              /lib/x86_64-linux-gnu/libc.so.6
```

Chiselsでどんなものが使えるかについては以下のコマンドで確認可能。

```shell-session
$ sysdig -cl
Category: Application
---------------------
httplog         HTTP requests log
httptop         Top HTTP requests
memcachelog     memcached requests log

Category: CPU Usage
-------------------
spectrogram     Visualize OS latency in real time.
subsecoffset    Visualize subsecond offset execution time.
topcontainers_cpu
                Top containers by CPU usage
topprocs_cpu    Top processes by CPU usage

Category: Errors
----------------
topcontainers_error
                Top containers by number of errors
topfiles_errors Top files by number of errors
topprocs_errors top processes by number of errors

Category: I/O
-------------
echo_fds        Print the data read and written by processes.
fdbytes_by      I/O bytes, aggregated by an arbitrary filter field
fdcount_by      FD count, aggregated by an arbitrary filter field
fdtime_by       FD time group by
iobytes         Sum of I/O bytes on any type of FD
iobytes_file    Sum of file I/O bytes
spy_file        Echo any read/write made by any process to all files. 
・・・略
```

## Tracers

開始点と終了点を任意でユーザがマーキングできます。
記録方法がおもしろいです。指定のフォーマットのテキスト情報を***/dev/null***に吐き出せば勝手にsysdigがtrace情報だと認識してくれます。
フォーマットの詳細は[こちら](https://github.com/draios/sysdig/wiki/Tracers)を参照。

例えば、ID 123、タグ名 tagsampleといった情報でマーキングしてみます。

sysdigで処理を記録中に、以下のコマンドを実行します。

```shell-session
$ echo ">:123:tagsample::"　> /dev/null
少し間をおいて
$ echo "<:123:tagsample::" > /dev/null
```

結果をcsysdigで見てみます。Viewで***「Traces List」***をすると、先程/dev/nullに吐き出したtagsampleのtrace情報が出てきます。
「＞」で開始した点と「＜」で終了した点が1つの区間(span)として扱われているのがわかります。

![tracer1.png](https://qiita-image-store.s3.amazonaws.com/0/45851/11c7a765-827e-d603-ed1b-b93a3ed73821.png)

この機能を使えば、トラブル時の調査で特定の処理の箇所にマーキングしてたどるのが容易になりそうです。

# まとめ

今回はここまで。
sysdigでの情報の収集のやり方と、その結果の見方の基礎を試してみました。今回紹介した機能の範囲だと、Zabbixとかで行うような「監視」というよりは「調査」用のツールという印象が強いです。
今回紹介できなかったSysdig Falcoはあらかじめルールを決めて、何か発生したときに通知を行うことができるツールなので、定常的な運用に仕込むにはSysdig Falcoが重要なポイントになってきそうです。
Sysdig Falcoは次回試してみます。

ここまで大量のデータが集まると、全ての挙動がわかりそうですが、どこをどう見てどう判断していくかはかなりレベル高そうです。。