A Day In The Boy's Life -83ページ目

A Day In The Boy's Life

とあるエンジニアのとある1日のつぶやき。

expectコマンドは、対話形式のコマンドを自動で実行したいといった場合に便利です。

例えば、SSHでサーバーに接続する際、パスワード認証が用いられている場合は当然パスワードを入力しないとログインできません。

または、何らかのソフトウェアをコマンドラインからインストールする場合に、その過程で幾つか環境の状態により質問されたり(パスを求められたり、ディレクトリ名を指定するなど)します。


こういったコマンドを自動化したい場合は、予めその実行コマンドにスキップするためのオプションなどが用意されていないと対応が難しかったりもしますが、expectコマンドを利用すればそれらのほとんどを回避することが可能になります。



SSHで接続してリモートサーバーのディスク容量を確認する


例えばサーバー運用者があるサーバーにログインしてディスク容量を定期的に確認しているとします。(あまり目視監視するってことも無いとは思いますが・・・)

サーバーにログインして、また別のサーバーにログインし・・・、となるとかなり手間にもなったりしますのでexpectコマンドでそれらの流れをまとめ、自動化するためのスクリプトを作ってみます。


#!/bin/bash

expect -c "
set timeout 10
spawn ssh -l work srv01
expect \"Are you sure you want to continue connecting (yes/no)?\" {
    send \"yes\n\"
    expect \"work@srv01's password:\"
    send \"password\n\"
} \"work@srv01's password:\" {
    send \"password\n\"
}
expect \"\[work@srv01 ~\]$\"
send \"df -k\n\"
send \"exit\n\"
interact
"


流れを簡単に説明すると、最初のexpect -cオプション以降でこれから実行するコマンドを指定します。

実際にコマンドを実行する箇所はspawnを記述した箇所(SSH接続)になります。


spawn ssh -l work srv01

上記で、workユーザーでサーバーsrv01へ接続しています。

次のexpectは、コマンドを実行した際に標準出力に表示されるメッセージを指定しています。


expect \"Are you sure you want to continue connecting (yes/no)?\" {

最初にSSH接続した場合は、リモートサーバーの公開鍵を保存するか質問されるので、もしこのメッセージが表示されたら、sendオプションで「yes」を送信するように指定しています。

公開鍵の取り込みが終わったらパスワードが求められるので


expect \"work@srv01's password:\"

と、workユーザーのパスワード求めるプロンプトが表示されたら


send \"password\n\"

sendオプションでworkユーザーのパスワードを自動で渡すように指定しています。

このようにexpectコマンドは、基本的にexpectで期待するメッセージ、そしてsendオプションでそれに対しての入力内容をセットしていきます。

また、2度目以降のSSH接続の場合は、いきなりパスワードを求められるのでその場合のパターンも用意しています。


} \"work@srv01's password:\" {
    send \"password\n\"
}

これは、プログラムのIF文と同様で条件分岐させて、幾つかの質問されるパターンを予め用意しておくことで、spawnで実行したコマンドの自動処理に柔軟性を持たせることが可能になります。


[work@localhost workspace]$ ./expect.sh
spawn ssh -l work srv01
work@srv01's password:
Last login: Tue Oct 19 23:50:09 2010 from localhost.localdomain
df -k
exit
[work@srv01 ~]$ df -k
Filesystem           1K-ブロック    使用   使用可 使用% マウント位置
/dev/vzfs             10485760   1036888   9448872  10% /
none                   4150972         4   4150968   1% /dev
[work@srv01 ~]$ exit
logout

サーバー切断までの処理を自動化できるため、リモートサーバーに接続してコマンド入力などの一連の流れを一つのシェルスクリプト実行だけで済ませることができます。



SCPを使ったファイル転送の自動処理


定期的に特定のファイルやディレクトリをリモートサーバーに転送(または取得)したいという場合の一連のコマンドを自動化してみます。


#!/bin/bash

USER=work
PASS=password
HOST=srv01

FROM_DIR=/home/work/workspace/messages.log
TO_DIR=/home/backup/

expect -c "
set timeout 10
spawn scp $USER@$HOST:$FROM_DIR $TO_DIR
expect \"Are you sure you want to continue connecting (yes/no)?\" {
    send \"yes\n\"
    expect \"$USER@$HOST's password:\"
    send \"$PASS\n\"
} \"$USER@$HOST's password:\" {
    send \"$PASS\n\"
}
interact
"


リモートサーバー(srv01)上にあるmessages.logというファイルをローカルサーバーにダウンロードします。

シェルスクリプトと組み合わせて実行することもできるため、今回はユーザー名やパスワード、転送先などの情報を変数に格納して実行しています。

逆のパターン(リモートサーバーへファイルを転送したい)は、scpのオプションを入れ替えるだけで対応できるでしょう。


$ ./scp-get.sh
spawn scp work@srv01:/home/work/workspace/messages.log /home/backup/
work@srv01's password:
messages.log                                     100%   26KB  25.5KB/s   00:00

もちろんこのシェルスクリプトは、パスワードを含むためにセキュリティ上あまりよろしくありません。

SSHやSCPをパスワードなしで転送したい場合は、リモートホストとホストベース認証や公開鍵認証方式を設定しておきパスワードなしでログインできるようにしておいたほうが良いかもしれません。

(そもそもホストベース認証ができるのであれば、わざわざexpectコマンドを利用する必要はありませんけど・・・)

設定方法は、関連記事内のエントリを参考にしてみてください。


いつもやっているあの作業だけど、幾つか環境の状態によって入力する情報が違うんだけど・・・って場合なんかに便利に使えますね。

運用・保守のいつもの作業もかなり効率化できるのではないでしょうか。





「原因は何なんですか?で、いつ復旧するんですか?」と電話の向こう(ならマシかもしれない)から聞こえてくる冷たい声に頭を垂れながらトラブルの復旧の対応に追われるというのは、エンジニアにとって経験のあることだと思います。

システムを構築・運用するうえでは、業務(サービス)の主管とシステム主管という2つの立場があったりしますが、それぞれ起きているトラブルに対しての焦り方というのは違っていて、冷静に見てみると結構面白いもんだなとか思ったりします。(もちろんその現場ではそんな余裕はないんですけどね)



何に対して焦っているのか?


システムがトラブルを起し、サービスが継続できない状況になると当然クレームとか言われるわけで、業務主管の方はそのサービスの利用者の矢面に立たされることとなり、システム主管は冒頭にあったようにその業務主管の担当者からグサグサと指される立場になったりします。


謎のシステムトラブルというものもありますけど、その原因を追究する上で徐々に解明されてきますし、最終的には業務主管に障害報告しないといけなかったりもするので、当然システム主管はそのトラブルの最終的な原因というものはわかったりします。


ただ、その原因がわかる事で生まれる焦りというものがあります。

システムのバグだけどそのバグの修正するためのロジックはどう組み立てればいいんだ?とか、バックアップ取れていなかったよとか、フェイルオーバーするはずが何でしないんだよとか、そんな使い方するなよとか、だから最初に予算捻出して冗長構成とるように薦めてたんだ(ry・・・とか。


要は原因がわかっているからこそ焦ったりもするわけです。

原因と対策はわかったけどそれには想像以上の復旧時間がかかるだろうから業務主管の担当者はそれでまた怒るだろうなぁとか、「やばい・・・。このトラブルは想定外だったぞ・・・。」とか見えるからこそ感じる恐怖ってあるわけですよね。


一方で、業務主管の担当者(非システムエンジニア)にとってみれば、小難しい事いわれたってわからないわけで、「で、結局直るの?直らないの?いつ直るの?」と当然その一点に集中砲火を浴びせたりもします。

自分では対応のしようが無く、それを人任せにするしかない状況なので、それが見えないことに対して恐怖を感じるでしょうし、焦りも生まれます。


なので、システムエンジニアはわかっていることに対して焦り、非システムエンジニアはわかっていない事に対して焦るのかなと。



システムエンジニアの開き直り


システムトラブルが勃発したときに、システムエンジニアが働く現場では怒号が飛び交い、皆が青ざめながらピリピリした環境で仕事をしていると想像している人もいるかもしれませんが、中にはそんなことも無いケースもあります。

そういったのはシステムトラブルが発生した結構初期の段階で、原因と対策がわかってきたらある程度の開き直りも生まれてきます


というのも対策がわかったんならそれを講じるだけになるのですが、それは自分たちの手だけではどうしようもないケースもあったりするからです。

ハードウェア障害が発生した場合は、保守業者に連絡をしてそのテクニカルエンジニアの到着待ちということもあるでしょうし、サポートセンターに送ったログとかダンプファイルからその原因の解析待ちということもあります。

バックアップデータからのリストア作業では、バックアップソフトウェアの動作やデバイス間のデータ転送に時間をとられ、それ以上早める事はできないといった場合もありますし、プログラムのリビルドを行うステータスバーを恨めしそうに眺めるだけで手の打ちようがないこともあるわけです。


業務主管から「まだ終わらないの?」と言われたところで、「全力でやってます(サーバーが)」としか答えようがないこともあり、開き直って落ち着いて対処しようと冷静さを取り戻すような雰囲気に持っていったりもします。

当然、焦って二次災害生んでも仕方ないですから、手を尽くしたんだから後は時間が来るのを待つだけだと、本当にこれがトラブル中の現場かな?と思うような冷静な場面も出てきます。

もちろん、そんな時間は早く終わらせて帰路に着きたいと誰しもが思っていますけどね。



まとめ


もちろん、双方怒られることは嫌なので、トラブル時には善処を尽くすようにするわけでけど、双方の視点や持っている考えの違いから、焦りが出てきたりその中で生まれる怒りもその矛先がかなり違うんだなと感じたりします。

わかっているからこそ焦り、わかっているから開き直れるエンジニアの現場と、わかっていないから焦り、わかっていないから開き直れもしない非システムエンジニアの現場ができるのかなと。


少し面白おかしく書いては見ましたけど、当然システムトラブルの現場ではそんなこと考える余裕というのはありませんけどね。




以前に「Twitterのお気に入りをTumblrへ投稿するプログラム書いたよ 」にて、Twitterでお気に入り登録したツイートをTumblrへ取り込むPHPスクリプトを書いたりしたんですが、わざわざそんなことしなくても取り込む方法がありました・・・。


タイムライン上に流れてきた面白かったりなるほどと個人的に唸ったツイートをTwitterのお気に入りに登録するようにしていて、やはりそうれは後で見直したりもしたいですし、その他の人からも簡単に閲覧できるようにしたいなと思ってたりしていて、それをTumblrへ取り込むことで実現しようとPHPスクリプトを書いたりしていました。

ただ、今年の8月でBasic認証が廃止されたことで先ほどのスクリプトも動かなくなり、重い腰をあげて改修しようかなと思ってたら、わざわざそんなことしなくても取り込むことができました。


もし、スクリプトを書きたければ上記のスクリプトロジックを踏まえて下記と組み合わせることで実現できると思います(多分)。


OAuthを使ってPHPからTwitterへ投稿する


OAuthを通してPHPからタイムラインの情報を取得する


OAuth経由でTumblrの情報を取得・投稿するPHPスクリプト



フィードを取り込んでTumblrへ取り込む


前置きが長くなりましたが、Tumblrはフィードを取り込んで自動投稿してくれる機能があります。

そして、Twitterのお気に入りってRSSフィードが存在するんですね。今まで知りませんでしたけど。

それらを活用することでTwitterのお気に入りをTumblrへ取り込むことができます。


まず最初に、Twitterのお気に入りのRSSフィードのURLを取得します。

はっきりしたRSSフィードのリンク先が用意されていないので、取得方法が多少面倒なのですがFireFoxでアクセスするとURL欄からRSSの購読機能があるので、それでお気に入りのフィードを登録しようとするとフィードのURLがわかったりします。


- RSSフィードの登録
A Day In The Boy's Life-RSS購読


- RSSフィードのURL
A Day In The Boy's Life-Twitter-RSS


上記のURLをコピーしておきます。


または、HTMLのソースの中から見つけ出すこともできます。

ソース内に下記のように記載されています。(下記は私のRSSフィード)


<link title="Your Twitter Favorites" rel="alternate" type="application/rss+xml" href="http://twitter.com/favorites/18074077.rss" />


RSSフィードがわかれば、お次はTumblrの管理画面へアクセスします。

管理画面で右のメニューにあるcustomizeをクリックします。


- Tumblrのcustomizeメニュー
A Day In The Boy&#39;s Life-Tumblr-customize


カスタマイズメニューに遷移しますので、上部のServicesメニューをクリックし、その中にある「Automatically import my...」の「RSS feed」を選択し、「Links」を選択します。


- Tumblrのcustomize
A Day In The Boy&#39;s Life-Tumblr-Service

次に、Feed URL欄に先ほどコピーしたお気に入りのRSSフィードのURLを貼り付けます。

そして、あとは右上の「SAVE+CLOSE」をクリックして保存します。


これで、あとは勝手にTumblr側でお気に入りのフィードから新着情報をTumblrへ自動投稿してくれます。

すぐに取り込まれなかったり(登録開始後1時間以上待つ必要あり)、頻度を変更することはできないようですが、これだけで後は、Twitter側でつけたお気に入りをTumblrへ投稿することができます。


ちなみに「RSS feed」の部分にはTwitterという選択肢がありますが、ここで自分のIDを入力しておけばTwitterへ投稿した自分のツイートをTumblrへ投稿(バックアップ)しておくこともできます。

また、リンク形式以外にもテキスト形式で投稿することもできます。

ただ、テキスト形式にすると誰が発言したツイートかがわからなくなりますけどね・・・。


ということで、プログラム書くより全然楽々にTwitterのお気に入りをTumblrへ取り込むことができました。


2010.10.11 追記

Tumblrへ投稿したイメージは下記のような感じです。


A Day In The Boy&#39;s Life-Tumblr-Image


タイトルがそのままリンクになっており、クリックするとTwitterのその発言へジャンプすることができます。

個人的には、依然作ったスクリプトの方が見やすくていいかなぁと思ってたりするのですが、わざわざスクリプト書く必要ないので贅沢はいえませんね・・・。
もっと詳細に見たければ、私のTumblr へどうぞ。