その他
    ホーム 技術発信 DoRuby [Ruby] 複数サーバの同じファイルをtail -fするプログラムを作ってみよう

    [Ruby] 複数サーバの同じファイルをtail -fするプログラムを作ってみよう

    この記事はアピリッツの技術ブログ「DoRuby」から移行した記事です。情報が古い可能性がありますのでご注意ください。

    複数サーバの同じファイルをtail -fするプログラムを作ってみよう

     この記事について

    サーバでの作業にはかかせないログの監視。

    tail -fは多くの方が利用されていると思います。

    台数が少なければいいのですが、増えてくると一台ずつ

    サーバにログインして→”tail -f <パス名>”とタイプして・・・

    が面倒になってきます。

    今回はこの手間を省くスクリプトを作成しようというのがテーマです。

     net/ssh/multi

    今回使うrubygemsライブラリはnet-ssh-multiです。

    複数のサーバにログインして同じ操作を行うことができるものです。

     プログラム作成

    いきなりですが、以下のように作成しました。

    #!/usr/bin/env ruby
    #-*- coding: utf-8 -*-
    require 'rubygems'
    require "net/ssh/multi"
    SSH_CONFIG = File.join(ENV['HOME'], ".ssh/config")
    
    def main(hosts, paths)
      Net::SSH::Multi.start do |session|
        hosts.each do |host|
          connect_host(session, host)
        end
        tail_f(session, paths)
        session.loop
      end
    rescue Interrupt
    end
    
    def connect_host(session, host)
      option = {}
      option[:config] = SSH_CONFIG if SSH_CONFIG && File.exists?(SSH_CONFIG)
      session.use host, option
    end
    
    def tail_f(session, paths)
      session.open_channel do |channel|
        channel.on_data do |ch, data|
          data.each_line do |line|
            puts ["[#{ch[:host]}]", line].join("\t")
          end
        end
        channel.exec "tail -f #{paths.join(" ")}"
      end
    end    
    
    if __FILE__ == $0
      raise "usage: mtailf <host ,host,...> <file file file...>" if ARGV.length < 2
      hosts = ARGV[0].split(/\s*,\s*/)
      paths = ARGV[1 .. -1]
      main(hosts, paths)
    end
    

    ポイントを説明していきます。

    SSH_CONFIG = File.join(ENV['HOME'], ".ssh/config")
    

    ssh_configの設定が使えるようにssh/configファイルの場所を指定しておきます。

    def main(hosts, paths)
      Net::SSH::Multi.start do |session|
        hosts.each do |host|
          connect_host(session, host)
        end
        tail_f(session, paths)
        session.loop
      end
    rescue Interrupt
    end
    

    mainメソッドです。

    Net::SSH::Multiのブロック内でサーバへのコネクションをはり、

    tailコマンドを実行しています。

    tail -f はctrl+cを押すまで処理が続くので

    session.loop

    を実行しています。

    def connect_host(session, host)
      option = {}
      option[:config] = SSH_CONFIG if SSH_CONFIG && File.exists?(SSH_CONFIG)
      session.use host, option
    end
    

    サーバへの接続です。

    session.use で接続します。

    def tail_f(session, paths)
      session.open_channel do |channel|
        channel.on_data do |ch, data|
          data.each_line do |line|
            puts ["[#{ch[:host]}]", line].join("\t")
          end
        end
        channel.exec "tail -f #{paths.join(" ")}"
      end
    end
    

    リモートホストに対してtail -fを実行している部分です。

    channel.execでコマンドを実行して

    ホストから応答があったら

    channel.on_data

    のブロックが実行されるかんじです。

    JavaScriptっぽいですね。

    ch変数にはホスト名も含まれているので

    コンソールに出力する際には

    puts [“[#{ch[:host]}]”, line].join(“\t”)

    として、どのホストからの出力なのかがわかるようにしています。

     使い方

    上記のプログラムを作成したら

    ファイル名はmtailf とします。

    そして

    chmod 755 mtailf

    と実行可能なようにパーミッションを設定します。

    net-ssh-multiをインストールします。

    gem install net-ssh-multi

    ここまでが初期設定で、実際に使うときには

    mtailf user@host01,user@host02 /path/to/file

    のようにします。

    引数は

    第一引数→ホスト名をカンマ区切りで

    第二引数以降→表示するファイルパス

    以下は出力イメージです。

    [host01]   ファイル行・・・・・・・・・・・
    [host02]   ファイル行・・・・・・・・・・・
    ・
    ・
    ・
    ・
    

    終了方法は

    tail -fと同様に

    ctrl+c です。

    このコマンド打つのも面倒!となったら・・・・・・

    aliasとか設定しましょう!

    記事を共有