Gtk+のRustラッパーであるgtk-rsの入門を書いた:RustとGtk+で開発するGUIアプリケーション
ほんとはもう少しやるといいことがあるのだけど(GResource、スレッド対応など)、まぁとりあえずここまで書いてあればGUIアプリケーション書いたことある人であれば感じは掴めるのかなと。ということでRev0.1。
(少なくともいまのところは)ニーズはほとんどないと思うけど、やる気があるうちにある程度形にしておかないと絶対書かないので。自分がこれから書いていくアプリケーションを読み進めるにあたっての基礎知識となれば。
2016-04-10
メタプログラミングについて話してきました
ゆるスタック勉強会 #20 で「メタプログラミングとは」という内容で話してきました。「メタプログラミングって何?イメージがつかない」という話を受けて、イメージをつくようにするのがこの話の目的です。
本当はそれぞれの言語のやりかたについて手順を書けばよかったのですが、力つきたので、実際のライブラリから例を持ってくる方向性にしました。話している間はコードや別の資料も見ながら話していたので、このスライド自体というよりは、各スライドでポイントしているライブラリの実装あるいは類似するライブラリの実装を読んでもらうのがイメージをつかみやすいんじゃないかと思います。
それぞれの言語でニワカなところがあるので、全方位からツッコミを受けてもおかしくない内容だと思っています。何かあれば補足していきたいのでぜひどうぞ。
本当はそれぞれの言語のやりかたについて手順を書けばよかったのですが、力つきたので、実際のライブラリから例を持ってくる方向性にしました。話している間はコードや別の資料も見ながら話していたので、このスライド自体というよりは、各スライドでポイントしているライブラリの実装あるいは類似するライブラリの実装を読んでもらうのがイメージをつかみやすいんじゃないかと思います。
それぞれの言語でニワカなところがあるので、全方位からツッコミを受けてもおかしくない内容だと思っています。何かあれば補足していきたいのでぜひどうぞ。
2016-04-01
Rust と C言語 をコールバックで行き来する(Cブリッジが必要なVer)
Rustは C FFI が強いので、はっきりしたstructとCの関数を呼び出すなんていうケースでは、それっぽいstructとexternでの関数の定義を書けば呼び出せる。コールバックもできる。
→他言語関数インターフェイス
しかし、以下のようなケースではちょっとやりにくい。
自分が必要だったところがC世界からのコールバック呼び出しだったので、それについても触れる。基本的には、RustのアプリケーションがCライブラリを使うという想定。コールバックなので、行ったり来たりなのだけどね。
上記がsrc/bridge.cに置かれているとする。
まず、Cargo.tomlにビルドスクリプトが「build.rs」にあるよ、ということを指示する。またコンパイラの実行の面倒を見てくれる「gcc」crateをbuild dependenciesに入れる(gccと言っているが、MSVCも対応しているらしい。試してないが)。
dependenciesのlibcは、ビルドスクリプト的には必要ないが、C FFIではほぼ使うのでここで入れておく。
上記で定義した「build.rs」に、コンパイラの呼び出しを定義する。
このあたりの記述、詳しくはCargoのドキュメント「Build Script Support」を参照。
本来ライブラリのリンクの指示も必要だが、ここでは一旦省く。詳しくは上記ドキュメントを参照(下でもすこし触れる)。
Cargoの流儀的には、FFIのためのRustらしくない定義はなんちゃら-sysというcrateに定義するほうがそれらしい。この部分だけの差し替えがしやすいから、とのこと。
必ずしもこうでなくてよいが、こうしておくことで「内側」をBox(ヒープ)で持つことが出来、ポインタに変換してC関数のポインタに渡すことができる。
git2-rsのsrc/transport.rsでやっていたので参考にした。
→他言語関数インターフェイス
しかし、以下のようなケースではちょっとやりにくい。
- 必要な関数の呼び出しにCマクロが必要
- opaqueなstructの下のほうのメンバにアクセスが必要
自分が必要だったところがC世界からのコールバック呼び出しだったので、それについても触れる。基本的には、RustのアプリケーションがCライブラリを使うという想定。コールバックなので、行ったり来たりなのだけどね。
やりかたの概要
やりかたは概ね以下の通り。- ラッパー関数をCで用意する
- CargoのビルドスクリプトでCソースのコンパイル方法を指示する
- ラッパー関数をexternで定義する
- ラッパー関数を呼び出すstruct & traitを定義する
- 実際に呼び出す
やりかた
ラッパー関数の準備
Rustから呼び出したい関数を、普通の関数呼び出しにできる形で定義する。本来は何かのコードのラッパーの想定なのだが、そっち側の知識が必要となると話がややこしくなるので、ここでは単なるCソースを例として使う。上記がsrc/bridge.cに置かれているとする。
Cargo.toml + build.rsにCソースのコンパイル方法の指示する
ここがこの記事のキモ。cargoに、置いたCソースのコンパイル方法を指示する必要がある。まず、Cargo.tomlにビルドスクリプトが「build.rs」にあるよ、ということを指示する。またコンパイラの実行の面倒を見てくれる「gcc」crateをbuild dependenciesに入れる(gccと言っているが、MSVCも対応しているらしい。試してないが)。
[package] (略) build = "build.rs" [build-dependencies] gcc = "0.3" [dependencies] libc = "0.2"
dependenciesのlibcは、ビルドスクリプト的には必要ないが、C FFIではほぼ使うのでここで入れておく。
上記で定義した「build.rs」に、コンパイラの呼び出しを定義する。
extern crate gcc;
fn main() {
gcc::compile_library("libbridgesample.a", &["src/bridge.c"]);
}
このあたりの記述、詳しくはCargoのドキュメント「Build Script Support」を参照。
本来ライブラリのリンクの指示も必要だが、ここでは一旦省く。詳しくは上記ドキュメントを参照(下でもすこし触れる)。
ラッパー関数をexternで定義する(いわゆる-sys部分)
ここからはもうRust onlyでのFFIと一緒。externでfnとstructを定義する。opaqueなstructをenumを定義するのがイディオムだそうだ。Cargoの流儀的には、FFIのためのRustらしくない定義はなんちゃら-sysというcrateに定義するほうがそれらしい。この部分だけの差し替えがしやすいから、とのこと。
ラッパー用のstructを用意する
ffi層を叩く、Rust 的構造にあわせた呼び出し部を用意する。今回は、コールバックをクロージャでもらい、それを実際のCコールバックから呼ぶ形にした。「外側」と「内側」の2つのstructを作る
Rust世界のコード用のデータを入れておく「内側」と、その「内側」とC関数からもらったポインタを格納しておく「外側」を用意する。必ずしもこうでなくてよいが、こうしておくことで「内側」をBox(ヒープ)で持つことが出来、ポインタに変換してC関数のポインタに渡すことができる。
git2-rsのsrc/transport.rsでやっていたので参考にした。
Drop traitを実装しポインタが自動で捨てられるようにする
後始末大事。コールバックとしてC側に渡す関数をexternで定義する
今回はライフタイムをRustが持っているので、キャストするだけ。ここでは、Rust側がライフタイムを握っているのでポインタを渡しているが、もしC側がライフタイムを握るようであれば、Box::into_rawとBox::from_rawをうまく使うといいと思う。ライフタイムの制御から外せる。mem::forget というのもあるかな。ラッパーを実装
あとはffi関数を呼び出すようMyStructにimplする。呼び出しコードを書く
実際呼ぶコードはこんなかんじになる。 結果はこんな感じ。mystruct_new Hello, World: 11 Hello Result: 12345 mystruct_freeこれでだいたい使えるはず。
おまけ:pkg-configでライブラリを参照させる
本来ラップする対象があるはずなので、リンク対象やincludeパスを指定する必要がある。基本的には、println!()で、cargoが必要な情報を渡してあげればよいが、build-dependenciesでpkg-config crateを使うと、pkg-configの結果を使える。例えばこんな感じ。 with-glib branchで使ってみてある。実際のところは、panicはやっつけなのでちゃんと分岐させたり、pkg-configに関わらず環境変数で設定できるようにしたりと、工夫がされていることが多いので、詳しくはそこらのライブラリのコードを参照。例えば、glib-sys crateはこんな感じ。2016-03-15
Rust の開発環境を作る (Ubuntu + Visual Studio Code)
今回はRustとその開発環境のセットアップの仕方について。Ubuntu 14.04環境に、Visual Studio Codeをインストールして、コード補完ができるようになるまで。Ubuntu Makeのところ以外は、Macでも大体同じじゃないかなと思う。
rustc的にはこれでおしまい。
便利なので本来のコマンド名codeでシンボリックリンクを貼っておく。
visual-studio-codeのリンクが壊れているようだけど、Gitでは直っているようなので、少しするとvisual-studio-codeでも起動できるようになると思う。
その後racerを実行するとわかるが、racerには参照用にRustのソースが必要なので、ダウンロードして適当なところに置いておく。
racer用にソースの配置場所を環境変数に設定する(~/.bashrcなどに書く)
racerコマンドを叩いてusageが出ればOK。
インストールはcargoで。
src/main.rsを適当に編集したら、Ctrl+Shift+Rか、コマンドパレット(Ctrl+Shift+p)からRun Debugで実行できる。
Rust コンパイラまわり
multirustのインストール
multirustは、stableやnightlyなどrust環境を切り替えられるようにするツール。普通にrustcをインストールしてもよいが、便利なのでmultirustで入れておく。$ curl -sf https://raw.githubusercontent.com/brson/multirust/master/blastoff.sh | sh (略) $ rustc --version rustc 1.7.0 (a5d1e7a59 2016-02-29)
rustc的にはこれでおしまい。
Visual Studio Codeまわり
アーカイブを展開するだけでも良いのだけど、便利なUbuntu Makeツールを使う。Ubuntu Makeのインストール
$ sudo add-apt-repository ppa:ubuntu-desktop/ubuntu-make $ sudo apt-get update $ sudo apt-get install ubuntu-make新しいUbuntuではubuntu-makeのppaはいらないかもしれない。
Visual Studio Codeのインストール
$ umake ide visual-studio-code (インストールパスを聞かれるけどデフォルトでこんな感じに。) Choose installation path: /home/yoichi/.local/share/umake/ide/visual-studio-code
便利なので本来のコマンド名codeでシンボリックリンクを貼っておく。
$ ln -s ~/.local/share/umake/ide/visual-studio-code/code ~/.local/share/umake/bin/code
visual-studio-codeのリンクが壊れているようだけど、Gitでは直っているようなので、少しするとvisual-studio-codeでも起動できるようになると思う。
RustyCode extensionのインストール
VSCodeのRust言語用拡張のRustyCodeをインストールする。codeで起動後、Ctrl+Shift+pでコマンドパレットを起動し、install RustyCodeでインストール。開発支援ツールまわり(racer, rustfmt)
Codeを起動してRustyCodeで書こうとすると、以下のようにエラーが出るので、必要なツールを入れる。The "racer" command is not available. Make sure it is installed.
racer (コード補完ツール) のインストール
$ cargo install racer (略) Installing /home/yoichi/.multirust/toolchains/stable/cargo/bin/racer be sure to add `/home/yoichi/.multirust/toolchains/stable/cargo/bin` to your PATH to be able to run the installed binariescargoに言われる通りPATHを~/.bashrcなどに設定する
export PATH=$PATH:~/.multirust/toolchains/stable/cargo/bin
その後racerを実行するとわかるが、racerには参照用にRustのソースが必要なので、ダウンロードして適当なところに置いておく。
$ racer RUST_SRC_PATH environment variable must be set to point to the src directory of a rust checkout. E.g. "/home/foouser/src/rust/src" $ mkdir ~/src $ wget https://static.rust-lang.org/dist/rustc-1.7.0-src.tar.gz $ tar zxvf rustc-1.7.0-src.tar.gz
racer用にソースの配置場所を環境変数に設定する(~/.bashrcなどに書く)
export RUST_SRC_PATH=~/src/rustc-1.7.0/src
racerコマンドを叩いてusageが出ればOK。
rustfmt (ソースコードフォーマッター) のインストール
なくてもエラーは出ないが、入れておくとCtrl+Shift+I でコードフォーマットできる。cargo fmtでもOK.インストールはcargoで。
$ cargo install rustfmt
Hello Worldプロジェクトの作成
試すには、適当にプロジェクトを作成してCodeで開く。$ cargo new hello --bin $ code hello &
src/main.rsを適当に編集したら、Ctrl+Shift+Rか、コマンドパレット(Ctrl+Shift+p)からRun Debugで実行できる。
2016-02-08
PHPが出力するsyslogの「ool www」の謎を追う
PHPからerror_log()でsyslogを吐いたときに出力される「ool www」という謎の文字列が一体なんなのか調べた。いいから回避策が知りたいという人はこちらへ。おまけが本編という人もいるかもしれない。
……この「ool www」は一体なんなんだ? Out-of-L...うーん。ここの文字列だけなら実害はないが、なんでこんなことになっているのかわからないので調べることにした。
環境はUbuntu 14.04にnginx + php5-fpm。設定値「error_log」を「syslog」にしておく。あまりバージョンに拘っていないので、リンクが適当だったりするが気になるかたは必要なバージョンのソースを見に行ってほしい。
まず、error_logのPHP_FUNCTIONの記述がext/standard/basic_functions.cにある。ここが実体である_php_error_log_ex()を呼び出す。
ext/standard/basic_functions.cの_php_error_log_ex()は、switchで引数に応じたログ出力(メールなど)を行う。今回のmessageのみの呼び出しではdefault: にたどり着き、php_log_err()を呼び出す。
php_log_err()は、main/main.cにあり、ここで「error_log」変数のチェックを行い、値が「syslog」のときはphp_syslog()関数でsyslogに出力している。
基本的にメッセージを引き回しているだけで、ここまでに変わった文字列処理は見受けられない。
なんとphp_syslog()はsyslog()であった。つまり、PHP内で何か特別な処理をすることなく、そのままsyslog()関数にメッセージを投げているだけである。
プロトタイプとしては、こうなっているようだ。
呼び出しとあわせると、priorityがLOG_NOTICEで、formatが"%s"、%s部分としてlog_messageが渡ることになる。
説明を読むと
処理の中でとくにopenlog()する処理は見受けられなかったので
openlog()は内部のopenlog_internal()を呼び、ここでidentはLogTag変数に代入される。
LogTagがNULLのときの処理を探してみると、syslog()のログ処理の実体である__vsyslog_chk()に以下の記述があった。
__prognameは何かと見ると、頭のほうでexternで宣言されていた。
php-fpmは明らかにargv[0]を書き換えている。勘のいい人ならもうお分かりだろう。一応検証するために、PHPを適当に展開してfpm.cのfpm_init()の頭と末尾(argvが書き換わる前と後)に以下のようにログを仕込んでみる(ヘッダやexternは適宜挿入)と...
こんな風になる。
そう、元々あった最後の/までの文字数分、新しい文字列の開始からスキップされている。ログ出力がだるいので例ではmaster processになっているが、pool wwwでどうなるかを想像するのはたやすいと思う。
結論:syslog tagに使われる__prognameを、プログラムパスであるargv[0]からディレクトリ部分を取り除いた文字列へのポインタとしてlibcが保存したあと、php-fpmがargv[0]を書き換えたため、謎の文字列「ool www」が生まれた。
一応Bug reportは作っておいた。PHPが対処すべき問題のように感じるけど、libcが文字列をコピーしておかないのが悪いって言われるとそうすかって感じなので、正直期待できない:https://bugs.php.net/bug.php?id=71544
具体的には、以下のようなファイルを例えば/etc/php5/fpm/fixsyslog.phpと置いて……
php.iniのauto_prepend_fileで参照する。
これで常にopenlog()が呼ばれるので現象は起こらない。
ざっくり言うと「environがargvとメモリ空間上に連続で置いてあるから、一応確認しつつenvironにどいてもらって、そこをargv[0]として使いましょう」である。グロい!コメントによると、nginxやpureftpdもそうしているらしい。
この記事も最後なので該当コードを貼り付けておく。
発端
とあるPHPアプリケーションがエラーログをPHP標準関数error_log()で吐く。それをフィルタしたくなったのだが、error_log()にはたいしたフックポイントもなく、アプリケーション自体には手を入れたくないので、とりあえずsyslogで受けてSyslogサーバーがなんとかするのがよいかと思って「error_log」の設定を「syslog」にしてみると、なぜかこんな感じに……Feb 7 22:02:36 phptest ool www: LOG CONTENT
……この「ool www」は一体なんなんだ? Out-of-L...うーん。ここの文字列だけなら実害はないが、なんでこんなことになっているのかわからないので調べることにした。
テストファイル
当該のPHPアプリケーションは置いといて、1ファイルでも再現するので、以下のPHPプログラムでテストすることにする。<?php error_log("ERRORTEST"); ?>
環境はUbuntu 14.04にnginx + php5-fpm。設定値「error_log」を「syslog」にしておく。あまりバージョンに拘っていないので、リンクが適当だったりするが気になるかたは必要なバージョンのソースを見に行ってほしい。
ソースを追う
error_log()関数の流れ
くだんのerror_log()関数をPHPから呼び出すと、こんな感じで動く。まず、error_logのPHP_FUNCTIONの記述がext/standard/basic_functions.cにある。ここが実体である_php_error_log_ex()を呼び出す。
ext/standard/basic_functions.cの_php_error_log_ex()は、switchで引数に応じたログ出力(メールなど)を行う。今回のmessageのみの呼び出しではdefault: にたどり着き、php_log_err()を呼び出す。
php_log_err()は、main/main.cにあり、ここで「error_log」変数のチェックを行い、値が「syslog」のときはphp_syslog()関数でsyslogに出力している。
if (!strcmp(PG(error_log), "syslog")) {
php_syslog(LOG_NOTICE, "%s", log_message);
PG(in_error_log) = 0;
return;
}
基本的にメッセージを引き回しているだけで、ここまでに変わった文字列処理は見受けられない。
php_syslog()関数とは
であればポイントはphp_syslog()であろうと、php_syslog()の定義をphp_syslog.hに見つけるも、こうなっていた。なんとphp_syslog()はsyslog()であった。つまり、PHP内で何か特別な処理をすることなく、そのままsyslog()関数にメッセージを投げているだけである。
syslog関数の使い方
こうなってくるとsyslog(3)が大事になる。manがあり、日本語が古くないようなので日本語のsyslog(3)を見てみる。プロトタイプとしては、こうなっているようだ。
#include <syslog.h> void openlog(const char *ident, int option, int facility); void syslog(int priority, const char *format, ...); void closelog(void);
呼び出しとあわせると、priorityがLOG_NOTICEで、formatが"%s"、%s部分としてlog_messageが渡ることになる。
説明を読むと
ident で指定した文字列は各メッセージの前に付与される。とあるので「ool www」の部分はidentのようだ。
処理の中でとくにopenlog()する処理は見受けられなかったので
openlog() は必須ではなく、必要に応じて syslog() から呼び出される。 syslog() が呼び出した場合、 ident のデフォルト値は NULL になる。の部分に該当し、
通常は ident にはプログラム名が設定される。 ident が NULL の場合、プログラムが ident として使用されるとあるので「ool www」はプログラム名のようだ。しかし「ool www」なんてプログラムはいないぞ?
syslog()関数の実装
こうなると、identがNULLだったときのopenlog()の動きを知りたくなる。そこでglibcを見たところ、openlog()の実体はmisc/syslog.cにある。openlog()は内部のopenlog_internal()を呼び、ここでidentはLogTag変数に代入される。
LogTagがNULLのときの処理を探してみると、syslog()のログ処理の実体である__vsyslog_chk()に以下の記述があった。
if (LogTag == NULL)
LogTag = __progname;
__prognameは何かと見ると、頭のほうでexternで宣言されていた。
extern char *__progname; /* Program name, from crt0. */だいぶあやしい。
「__progname」とは
で、誰が__prognameをセットしているのかと探してみると「misc/init-misc.c」にあった。void
__init_misc (int argc, char **argv, char **envp)
{
if (argv && argv[0])
{
char *p = strrchr (argv[0], '/');
if (p == NULL)
__progname = argv[0];
else
__progname = p + 1;
__progname_full = argv[0];
}
}
__prognameは「argv[0]の最後の'/'より後」のようだ。なるほどargv[0]の役目を考えると妥当そうに見える。__progname_fullとしてargv[0]も保持されている。php-fpmのargv[0]ってなんだっけ?
phpが動作するのは、php-fpmプロセス。そういえばphp-fpmは独自設定した文字列がpsで見えてたよなと見てみると、こうなっている。$ ps -ef | grep '[p]hp-fpm' root 2664 1 0 22:02 ? 00:00:00 php-fpm: master process (/etc/php5/fpm/php-fpm.conf) www-data 2666 2664 0 22:02 ? 00:00:00 php-fpm: pool www www-data 2667 2664 0 22:02 ? 00:00:00 php-fpm: pool www
php-fpmは明らかにargv[0]を書き換えている。勘のいい人ならもうお分かりだろう。一応検証するために、PHPを適当に展開してfpm.cのfpm_init()の頭と末尾(argvが書き換わる前と後)に以下のようにログを仕込んでみる(ヘッダやexternは適宜挿入)と...
zlog(ZLOG_NOTICE, "argv[0]: %s", argv[0]);
zlog(ZLOG_NOTICE, "__progname: %s", __progname); zlog(ZLOG_NOTICE, "__progname_full: %s", __progname_full);
こんな風になる。
$ sudo ./sapi/fpm/php-fpm -F -y /etc/php5/fpm/php-fpm.conf [08-Feb-2016 00:25:23] NOTICE: argv[0]: ./sapi/fpm/php-fpm [08-Feb-2016 00:25:23] NOTICE: fpm is running, pid 24381 [08-Feb-2016 00:25:23] NOTICE: __progname: ster process (/etc/php5/fpm/php-fpm.conf) [08-Feb-2016 00:25:23] NOTICE: __progname_full: php-fpm: master process (/etc/php5/fpm/php-fpm.conf)もう少しわかりやすくすると、こう。
そう、元々あった最後の/までの文字数分、新しい文字列の開始からスキップされている。ログ出力がだるいので例ではmaster processになっているが、pool wwwでどうなるかを想像するのはたやすいと思う。
結論:syslog tagに使われる__prognameを、プログラムパスであるargv[0]からディレクトリ部分を取り除いた文字列へのポインタとしてlibcが保存したあと、php-fpmがargv[0]を書き換えたため、謎の文字列「ool www」が生まれた。
一応Bug reportは作っておいた。PHPが対処すべき問題のように感じるけど、libcが文字列をコピーしておかないのが悪いって言われるとそうすかって感じなので、正直期待できない:https://bugs.php.net/bug.php?id=71544
回避策:identに妥当な値を設定するには?
暗黙のopenlog()をさせなければいい。つまり、期待のプログラムが動く前にopenlog()すればよい。具体的には、以下のようなファイルを例えば/etc/php5/fpm/fixsyslog.phpと置いて……
php.iniのauto_prepend_fileで参照する。
auto_prepend_file = /etc/php5/fpm/fixsyslog.php
これで常にopenlog()が呼ばれるので現象は起こらない。
感想
原因に関しては、ソースを追うことですっきりさせることができた。オープンソースのありがたみって、やっぱりこういうところにあると思う。解決策は、もやもやが残るままだけど…おまけ:php-fpmはどのようにargv[0]を書き換えているか?
php-fpmは見ての通りargv[0]を書き換えるのだけど、argvを全部足しても期待の文字数に足りないなんてこともあるはず。そんなときの処理がfpm_env.cに書かれていた。ざっくり言うと「environがargvとメモリ空間上に連続で置いてあるから、一応確認しつつenvironにどいてもらって、そこをargv[0]として使いましょう」である。グロい!コメントによると、nginxやpureftpdもそうしているらしい。
この記事も最後なので該当コードを貼り付けておく。
2016-01-25
Ansibleを複数ホストへのコマンド投げツールとしてad-hocに使う
Ansibleは普通「playbook」と呼ばれるファイルを記述し、それを使って動かすのだけど、もっとてきとーに使っても便利なときは便利なので、今日はその使い方の紹介。実行する側は、ansibleがインストールされている必要はあるが(apt-get install ansibleでよい)、対象ホストはsshでアクセスできればOK。
-aにコマンドを指定し、-iで対象ホストをカンマ区切りで並べ、-uにユーザを指定し、allをつける。
ユーザの指定なくsshできる状態なら、-uはなくてもいい。また、playbookを書いていてinventory fileを持っているなら、そのファイルを-iで指定したほうが当然楽。この例ではありがたみが全然ないが、もし1ホストにしたければ「-i hostname,」のように最後に,をつける。
-mでモジュール名を指定し、-aでモジュールの引数を指定する。実は-aはいつでもモジュールの引数なのだが、-mのデフォルトが「command」なので、コマンドを実行できるというわけだ。
他にも例がいくつかあるので、参考にされたい。
複数ホストにコマンドを投げる
シンプルな例: 適当なファイルをcatする
もっとも理解が簡単なのは、権限だとかは気にせずにただコマンドが実行できればよい、という使い方。サーバがたくさんあるんだけど、ひとつひとつ実行するのはダルい。そんなときは、こんな風に実行すると、実行して結果を並べてくれる。$ ansible -a "cat /etc/hostname" -i host1,host2 -u yoichi all host1 | success | rc=0 >> host1 host2 | success | rc=0 >> host2
-aにコマンドを指定し、-iで対象ホストをカンマ区切りで並べ、-uにユーザを指定し、allをつける。
ユーザの指定なくsshできる状態なら、-uはなくてもいい。また、playbookを書いていてinventory fileを持っているなら、そのファイルを-iで指定したほうが当然楽。この例ではありがたみが全然ないが、もし1ホストにしたければ「-i hostname,」のように最後に,をつける。
sudoする例: crontabに設定された内容を見る
Ansibleをansible-playbookコマンドで使っている人にはお馴染みの、-sでsudo(-Kでパスワード)、-Sでsuさせることもできる。例えば、crontabに設定された項目を見たい、とか。$ ansible -s -a "crontab -u root -l" -i host1,host2 all host1 | FAILED | rc=1 >> no crontab for root host2 | success | rc=0 >> # Edit this file to introduce tasks to be run by cron. (略) # m h dom mon dow command 30 5 * * * /usr/local/bin/rotate.sh
切り離す例: 一斉シャットダウン
停電準備で一斉シャットダウンしたいです、とかいうこともあると思う。shutdownのケースでは、結果を待てないので-Bオプションを使ってバックグラウンドで切り離す。$ ansible -s -K -B 1 -a "shutdown -h now" -i host1,host2 all
SUDO password:
background launch...
host1 | success >> {
"ansible_job_id": "867312552566.13878",
"results_file": "/root/.ansible_async/867312552566.13878",
"started": 1
}
host1 | success >> {
"ansible_job_id": "867312552566.2418",
"results_file": "/root/.ansible_async/867312552566.2418",
"started": 1
}
Ansibleのモジュールを活用する
上述の例はすべてコマンドだったが、当然ながら、Ansibleのモジュールも活用できる。モジュール活用例1: サービスを再起動させる
例えば、サービス再起動では、serviceコマンドでもいいが、serviceモジュールを使うとこのようになる。$ ansible -s -m service -a "name=myservice state=restarted" -i host1,host2 all
host1 | success >> {
"changed": true,
"name": "myservice",
"state": "started"
}
host2 | success >> {
"changed": true,
"name": "myservice",
"state": "started"
}
-mでモジュール名を指定し、-aでモジュールの引数を指定する。実は-aはいつでもモジュールの引数なのだが、-mのデフォルトが「command」なので、コマンドを実行できるというわけだ。
モジュール活用例2: copyモジュールでファイルを配置する
ad-hocに使う例ではない気がするが、その気になればファイルを持っていくことも可能。$ echo hoge > hogefile
$ ansible -m copy -a "src=hogefile dest=/tmp/hogefile1" -i host1,host2 all
host1 | success >> {
"changed": true,
(略)
}
host2 | success >> {
"changed": true,
(略)
}
注意
あくまで一回限りで使うようなものであって、残したいものだったり、ansibleコマンドを3つも4つも打つようであれば、playbookを作るのがよい。上述のコマンドを羅列すればyaml書かなくても呼べるじゃん?っていうのはナシで。ベストプラクティス構成にしておくのがよいと思うが、1ファイルplaybookでもまぁansibleコマンドがたくさん書いてあるよりは良いと思う。参考
こういうのをad-hoc commandsと呼ぶらしい→Introduction To Ad-Hoc Commands他にも例がいくつかあるので、参考にされたい。
おわりに
「ansible」というソフトウェア名がついているコマンドがこの動作であるところから、もともとはこういう風に使うように始まったのではないかと私は勝手に思っている。現時点ではメインではない使い方になった(と思う)ので、知らなくても全然使えないことはないが、ベースの考え方はこのエージェントなしにリモートホストに任意にモジュールを投げ込んで実行するツールだと思うと、理解も応用もしやすいと思うので、触ってみておいて損はないかなと思う。2016-01-10
avahi-browse で mDNS/DNS-SD のホスト・サービスの探索をする
apt-get install avahi-utilsして「avahi-browse -alr」とすればよい。たいした話ではないけれど、多分忘れるので。
出力例はこんな感じ。
出力例はこんな感じ。
$ avahi-browse -alr + eth0 IPv6 sample [00:11:22:33:44:55] Workstation local + eth0 IPv4 sample [00:11:22:33:44:55] Workstation local + eth0 IPv4 Jenkins Web Site local + eth0 IPv4 jenkins _jenkins._tcp local + eth0 IPv4 jenkins _hudson._tcp local = eth0 IPv6 sample [00:11:22:33:44:55] Workstation local hostname = [sample.local] address = [fe80::82ee:1234:5678:9999] port = [9] txt = [] = eth0 IPv4 sample [00:11:22:33:44:55] Workstation local hostname = [sample.local] address = [192.168.1.152] port = [9]h txt = [] = eth0 IPv4 Jenkins Web Site local hostname = [jenkins.local] address = [192.168.1.101] port = [8005] txt = ["version=1.643" "url=http://jenkins.example.local:8005/" "slave-port=46298" "path=/" "server-id=5ca870f7e5eb41888299af4853b2021b"] = eth0 IPv4 jenkins _jenkins._tcp local hostname = [jenkins.local] address = [192.168.1.101] port = [8005] txt = ["version=1.643" "url=http://jenkins.example.local:8005/" "slave-port=46298" "path=/" "server-id=5ca870f7e5eb41888299af4853b2021b"] = eth0 IPv4 jenkins _hudson._tcp local hostname = [jenkins.local] address = [192.168.1.101] port = [8005] txt = ["version=1.643" "url=http://jenkins.example.local:8005/" "slave-port=46298" "path=/" "server-id=5ca870f7e5eb41888299af4853b2021b"]
登録:
投稿 (Atom)

