vagrant global-status の代替方法、あるいは jq コマンドの使い方

この記事は書かれてから1年以上が経過しており、最新の情報とは異なる可能性があります

VM(Docker も含む)が立ち上がっているかどうかを tmux の右下に表示しておくのはけっこう便利です。

tmux の set-option -g status-right で指定できるスクリプトは 一定間隔で常に呼ばれるような仕組みなのですが、 (更新間隔は set-option -g status-interval で設定可能) あまり間隔を空けすぎると確認しづらくなってしまうので 5 秒くらいに指定しています。

このスクリプト内で Vagrant 経由で起動している VM 名と Docker サービスが立ち上がっているかどうかを 集約して表示しているのですが、 この中で vagrant global-status を呼ぶコストが意外にも高い ことに今更ながらに気づきました。 (たぶんディレクトリツリーを全走査とかしてるんですかね・・・?)

top とか ps とかで詳しく調べてみるとすげー CPU を使ってたっぽかったので、 (5 秒間隔で常に動作してるわけじゃなかったので気づかずにスルーしてました) これはなんとかしなくてはと思い、代替方法を検討しました。

~/vagrant.d 内の設定ファイル

後の Vagrant のバージョンアップで変わるかもしれませんが、 以下の場所に仮想マシンの一覧情報がまとめられていて、 Vagrant の仮想マシンのステータスが変更されるたびにこのファイルが更新されます。

~/.vagrant.d/data/machine-index/index

こちらはただのテキストファイルなので、 5 秒間隔で tmux から重いコマンドを実行するよりは、 こちらの設定ファイルを読みにいった方がはるかに軽いです。

ただ、こちらのファイルは JSON ファイルを圧縮したようなファイルになっているので、 こちらを(あまりコストかけずに)さくっとフィルタリングして 必要な情報だけ抜き出したいですね。

jq は JSON ファイルをフィルタリングするコマンド

https://stedolan.github.io/jq/

ずいぶん前から簡易的に利用してはいたのですが、 JSON ファイルから必要な情報をフィルタリングなどするコマンドとして jq コマンド というのがあります。

これは jq コマンドを使うところだ!と思い、 この Vagrant の設定ファイルから必要な情報を jq コマンドを使って抜き出してみました。

{"version":1,"machines":{"aaabbbccc":{"local_data_path":"/path/to/vagrant/.vagrant","name":"alpha","provider":"virtualbox","state":"poweroff","vagrantfile_name":null,"vagrantfile_path":"/path/to/vagrant","updated_at":null,"extra_data":{"box":{"name":"bento/centos-7.2","provider":"virtualbox","version":"2.2.7"}}},"dddeeefff":{"local_data_path":"/path/to/vagrant/.vagrant","name":"bravo","provider":"virtualbox","state":"running","vagrantfile_name":null,"vagrantfile_path":"/path/to/vagrant","updated_at":null,"extra_data":{"box":{"name":"http://cloud.centos.org/centos/7/vagrant/x86_64/images/CentOS-7-x86_64-Vagrant-1605_01.VirtualBox.box","provider":"virtualbox","version":"0"}}}}}

ちょっと固有パス、ID などは修正してますが、だいたいこんな感じで入っています。

ここから、machines オブジェクト内に入っているものを抜き出したいので、 jq ".machines" でパイプをつなげてやることで、フィルタリングされた出力結果が出ます。

% echo '{"version":1,"machines":{"aaabbbccc":{"local_data_path":"/path/to/vagrant/.vagrant","name":"alpha","provider":"virtualbox","state":"poweroff","vagrantfile_name":null,"vagrantfile_path":"/path/to/vagrant","updated_at":null,"extra_data":{"box":{"name":"bento/centos-7.2","provider":"virtualbox","version":"2.2.7"}}},"dddeeefff":{"local_data_path":"/path/to/vagrant/.vagrant","name":"bravo","provider":"virtualbox","state":"running","vagrantfile_name":null,"vagrantfile_path":"/path/to/vagrant","updated_at":null,"extra_data":{"box":{"name":"http://cloud.centos.org/centos/7/vagrant/x86_64/images/CentOS-7-x86_64-Vagrant-1605_01.VirtualBox.box","provider":"virtualbox","version":"0"}}}}}' | jq ".machines"
{
  "aaabbbccc": {
    "local_data_path": "/path/to/vagrant/.vagrant",
    "name": "alpha",
    "provider": "virtualbox",
    "state": "poweroff",
    "vagrantfile_name": null,
    "vagrantfile_path": "/path/to/vagrant",
    "updated_at": null,
    "extra_data": {
      "box": {
        "name": "bento/centos-7.2",
        "provider": "virtualbox",
        "version": "2.2.7"
      }
    }
  },
  "dddeeefff": {
    "local_data_path": "/path/to/vagrant/.vagrant",
    "name": "bravo",
    "provider": "virtualbox",
    "state": "running",
    "vagrantfile_name": null,
    "vagrantfile_path": "/path/to/vagrant",
    "updated_at": null,
    "extra_data": {
      "box": {
        "name": "http://cloud.centos.org/centos/7/vagrant/x86_64/images/CentOS-7-x86_64-Vagrant-1605_01.VirtualBox.box",
        "provider": "virtualbox",
        "version": "0"
      }
    }
  }
}

さらに、1つずつのオブジェクトごとに分けつつ、name と state だけ配列形式にして引っこ抜きます。

% echo '{"version":1,"machines":{"aaabbbccc":{"local_data_path":"/path/to/vagrant/.vagrant","name":"alpha","provider":"virtualbox","state":"poweroff","vagrantfile_name":null,"vagrantfile_path":"/path/to/vagrant","updated_at":null,"extra_data":{"box":{"name":"bento/centos-7.2","provider":"virtualbox","version":"2.2.7"}}},"dddeeefff":{"local_data_path":"/path/to/vagrant/.vagrant","name":"bravo","provider":"virtualbox","state":"running","vagrantfile_name":null,"vagrantfile_path":"/path/to/vagrant","updated_at":null,"extra_data":{"box":{"name":"http://cloud.centos.org/centos/7/vagrant/x86_64/images/CentOS-7-x86_64-Vagrant-1605_01.VirtualBox.box","provider":"virtualbox","version":"0"}}}}}' | jq ".machines | .[] | [.name, .state]"
[
  "alpha",
  "poweroff"
]
[
  "bravo",
  "running"
]

これを半角スペースでつないで1行ごとに出力したかったので、[] で囲んで join で繋ぎ直しました。

% echo '{"version":1,"machines":{"aaabbbccc":{"local_data_path":"/path/to/vagrant/.vagrant","name":"alpha","provider":"virtualbox","state":"poweroff","vagrantfile_name":null,"vagrantfile_path":"/path/to/vagrant","updated_at":null,"extra_data":{"box":{"name":"bento/centos-7.2","provider":"virtualbox","version":"2.2.7"}}},"dddeeefff":{"local_data_path":"/path/to/vagrant/.vagrant","name":"bravo","provider":"virtualbox","state":"running","vagrantfile_name":null,"vagrantfile_path":"/path/to/vagrant","updated_at":null,"extra_data":{"box":{"name":"http://cloud.centos.org/centos/7/vagrant/x86_64/images/CentOS-7-x86_64-Vagrant-1605_01.VirtualBox.box","provider":"virtualbox","version":"0"}}}}}' | jq "[ .machines | .[] | [.name, .state] | join(\" \") ] | join(\"\n\")"
"alpha poweroff\nbravo running"

これで目的の文字列になったので、running がある行だけ簡易的に引っこ抜いて tmux に表示すれば OK です。

jq のマニュアルページには色々な機能が載っていますが、 実際に使うのはそこまで多くないんじゃないかなーという印象です。 正直見ながらじゃないと使えないです・・・。

https://stedolan.github.io/jq/manual/

まとめ

そのまま vagrant global-status を使っていたころよりも圧倒的に軽くなり、 排熱もほとんどなくなりました。 (もしかして最近の排熱の主因はほぼこれだったんじゃあ・・・)

jq コマンドは、こういった使い方以外にも外部の API を直接叩いてそのまま jq コマンドに通して使う、 といった使い方も多いので、もう少しさらっと使いこなせるようになりたいですね。

この記事は書かれてから1年以上が経過しており、最新の情報とは異なる可能性があります

もし記事内に誤りなどございましたら、 @girigiribauer @girigiribauer.com までご一報いただけると助かります。

Related articles