SICP(計算機プログラムの構造と解釈)1.1
はじめに
『計算機プログラムの構造と解釈』を読む。動機は以下。
- いわゆる情報系の勉強をしていないので、基礎を身につけたい
- Lispインタープリタを実装してみたい
- ストリーム、遅延評価、末尾再帰最適化、構文・字句解析器など、なんとなくしか知らないものを理解したい
- すごいエンジニアがみんな読んでる
年単位でかかるかもしれないが、それでも終わらない可能性・挫折する可能性があるので、練習問題は無理に全部やらない。
資料
html版
訳にかなり癖があるので、意味を掴みにくい場合は、原著を確認するとよいかもしれない。また、コード集はこちらにしかないので、適宜見るとよい。
HTML版は、スタイルが適用されていないので、読みにくい。epub化を考えたけど、自分がやる前に既にepubおよびmobiで公開してくれている方がいたので、ありがたく使わせていただく。
環境
環境はOSXにLisp/Scheme派生の言語Racketをバイナリからインストールして使っている。DrRacket
というIDEが同梱されているので、そちらを利用するか、/Applications/Racket\ v6.2/bin
にPATHを通せば$ racket
で対話型コンソールを起動できる。
Emacsの使用経験がないため、エディタは検討中。vimでやるか、これを期にemacsを覚えるか。。。
本文
1.1.7
平方根について。数学的な関数とコンピュータの記述について。 数学では平叙文的(何であるか)記述をするのに対して、コンピュータは命令文的(どうするか)記述をする。どう計算するかというアプローチに対して、通常は次々と近似をとるニュートン法を用いる。
> (define (sqrt-iter guess x) (if (good-enough? guess x) guess (sqrt-iter (improve guess x) x))) > (define (improve guess x) (average guess (/ x guess))) > (define (average x y) (/ (+ x y) 2)) > (define (good-enough? guess x) (< (abs (- (square guess) x)) 0.001)) > (define (sqrt x) (sqrt-iter 1.0 x)) > (sqrt 2) 1.4142156862745097 > (sqrt 3) 1.7321428571428572
1.1.8
手続きを抽象化してブロック構造をとる方法、パラメータのスコープについて。外の入れ子にある束縛されたパラメータを内部で利用する(レキシカルスコープ)。
(define (sqrt x) (define (good-enough? guess) (< (abs (- (square guess) x)) 0.001)) (define (improve guess) (average guess (/ x guess))) (define ( sqrt-iter guess) (if (good-enough? guess) guess (sqrt-iter (improve guess)))) (sqrt-iter 1.0))
問題
EXSERCISE 1.3
三つの数を引数としてとり , 大きい二つの数の二乗の和を返す手続き
> (define (square a) (* a a))
EXERCISE 1.4
schemeの評価モデルは、演算子が合成式である組み合わせでも使える
> (define (a-plus-b a b) ((if (> b 0) + -) a b)) > (define (sum a b) (+ a b)) > (define (larger-square-sum a b c) (cond ((and (< a b) (< a c)) (sum (square b) (square c))) ((and (< b a) (< b c)) (sum (square a) (square c))) (else (sum (square a) (square b))))) > (larger-square-sum 3 4 5) 41
EXERCISE 1.5
作用的順序の評価と正規順序の評価について
EXSERCISE 1.6
特殊形式として定義されているif
を通常の手続きとして再実装して、1.1.7における平方根の手続きを行った場合、どうなるか。
> (define (new-if predicate then-clause else-clause) (cond (predicate then-clause) (else else-clause))) > (define (sqrt-iter guess x) (new-if (good-enough? guess x) guess (sqrt-iter (improve guess x) x)))
結果、無限ループする。これは、Schemeにおける通常の手続きが作用的順序で行われることに起因する。作用的順序での評価は、以下の通り。
- 組み合わせの部分式を評価する
- 最左部分式の値である手続き(演算子)を残りの部分式の値である引数に作用させる
つまり、一般的なSchemeの評価規則で定義されたnew-if
の場合だと、先に部分式が評価されるため、
(good-enough? guess x)
が真であったとしても
(sqrt-iter (improve guess x) x
が評価されるため、無限ループする
EXERCISE 1.7
曖昧。 平方根の手続きにおいて、入力が非常に小さい値もしくは大きい値にテストすっとが失敗する。大きい値の場合は、浮動小数点の比較における誤差によるところ。桁数の増大によって仮数が計算機に無視されるため、無限ループする。値が小さい場合、予測値が基準値より下回ると真を返すため、値にかなりのずれがあっても再帰が終了してしまう。改良版未着手。
EXERCISE 1.8
未着手。立方根の問題。ニュートン法の実装を改良する。
linuxにおけるメモリと関連コマンド(free, vmstat, top, sar)
linuxにおけるメモリの扱いを中心として、関連する統計情報の閲覧、監視ツールの見方についてまとめる。下に挙げた技評の2冊および学びの多かった記事を基にしています。
ページキャッシュ
free
とかtop
とかの説明に入る前に、まず、OSのキャッシュの話から始める。大規模なデータを扱う際、Linuxはキャッシュの仕組みによって効率的にメモリ管理を行うことで、ディスクへのアクセスを減らし、データアクセスを高速化している。これは「ページキャッシュ」と呼ばれる仕組みで実現している。
Linuxには仮想アドレスと呼ばれる機能を提供している。論理的なアドレス(リニアアドレス)をOSが提供することによって、各プロセスはメモリ(あるいはスワップ)の物理的なアドレスを気にすることなく扱うことができるようになる。この際、Linuxはメモリ領域をアドレス単位ではなく1つのまとまったブロックとして提供しており、その単位が「ページ」と呼ばれる。サイズは4KB。このページをOSはメモリ上にずっと確保しており、別のプロセスが再び同じデータにアクセスしようとしたときに、ディスクへのアクセスを行うことなく高速に処理できる、という仕組みが、ページキャッシュの大まかな仕組み。Linuxはメモリが空いている限り全てページキャッシュに回そうとする。
基本的にはメモリに空きがあれば全てキャッシュに乗せるが、開いていない場合は古いキャッシュから破棄して新しいキャッシュと入れ替える。sar -r
で確認すると、memused
の比率におけるkbcached
の値が確認できる。
06:00:01 AM kbmemfree kbmemused %memused kbbuffers kbcached kbswpfree kbswpused %swpused kbswpcad 06:10:01 AM 161280 3824360 95.95 6672 2008004 4174336 18620 0.44 380 06:20:01 AM 171652 3813988 95.69 18132 1995240 4174336 18620 0.44 380
メモリとディスク
メモリへのアクセスとディスクへのアクセスには、105から106(10万〜100万)倍程度の速度差が生じる。これは、メモリが電気的な部品であって、探索速度に物理的制約を受けることがないのに対して、ディスクの場合はヘッドの移動、ディスク回転という物理的な動作を伴う必要があることに起因する。を必要なデータがメモリ内に乗っていれば、多少アプリケーションが非効率であっても、ある程度パフォーマンスが担保できる。アプリケーションを構成する上で、iowaitを避けて極力全てのアクセスをメモリで行いたいのは、こうした理由からである。また、OSのキャッシュで捌ききれなくなったときに、分散の考え方が必要になる。
メモリの使用状況を確認する
メモリ使用量を計測する各種コマンドについて。環境はvagrantのubuntu14.04 LTS。
free
現在のメモリ使用量を確認できる。この出力は、/proc/meminfo
ファイルを元にしている。
オプションの-t
はTotal
行の出力、-m
はMB単位での出力を行う。
vagrant@vagrant-ubuntu-trusty-64:~$ free -tm total used free shared buffers cached Mem: 489 336 153 0 12 211 -/+ buffers/cache: 112 377 Swap: 0 0 0 Total: 489 336 153
Mem
行は、物理的なメモリの総量。489MBのメモリが存在し、336MBが既に使用され、空きメモリは153MBということが分かる。buffers
とcached
はOSによって予約されている領域。buffers
およびcache
は、メモリが不足する場合に開放して利用できるようにする。よって、実質的なメモリの使用量および利用可能量を見るためには、2行目の-/+ buffers/cache
行を見るのが正しい。2行目のused
はMem
行のused
からbuffers/cached
を引いた使用量、free
は足した量を表示している。ここではSwap
行に出力はないが、メモリに載せるデータが増えてくると、スワップ領域がディスクに作成され、一部のデータがそちらに移される。
スワップ
ディスクを利用して使用可能なメモリ領域を増やす。メモリ容量が不足している場合に、現在使われていないデータを一時的にスワップ領域に退避させてメモリを開放する。 スワップ領域に退避させたデータの参照をプロセスが要求したときに、再度メモリ上に読み込まれる。ディスクへのアクセスがあるので、もちろん遅い。スワップ領域への退避・アクセスが頻発する条件は、要改善項目。メモリが不足しているため、以下のようなことを検討する必要がある。
- 極端に大きなプロセス(メモリリークなどの異常含む)がないか
- プログラム内に非効率にメモリを使う箇所がないか
- メモリを増設できないか
- 分散を検討できないか
ボトルネックの特定には、下で挙げるvmstat
やsar
を使うとよい。
vmstat
メモリやCPUの情報だけでなく、スワップやプロセスの情報を出力できる。
vagrant@vagrant-ubuntu-trusty-64:~$ vmstat 1 procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu----- r b swpd free buff cache si so bi bo in cs us sy id wa st 3 0 0 144368 23220 200692 0 0 54 20 21 327 0 1 99 0 0 0 0 0 144360 23220 200692 0 0 0 0 32 66 0 0 100 0 0 0 0 0 144360 23220 200692 0 0 0 0 24 52 0 0 100 0 0
si
(スワップイン), so
(スワップアウト)が頻繁に動いている場合はスワップが発生している。他の項目としては、r
は実行待ち状態のプロセス数、b
は割り込み不可能なスリープ状態にあるプロセス数。memory
の項目はfree
と同様、io
はデバイスとのブロックの転送量、system
はコンテキストの毎秒の切り替えレート(割り込み回数・コンテキストスイッチの回数)、cpu
はCPU使用率である。
top
topコマンドは、現在のシステム全体の負荷情報を閲覧するのに利用する。プロセス・CPU・メモリ・スワップが確認できる。主なオプションは、以下の通り。
vagrant@vagrant-ubuntu-trusty-64:~$ top # CPU使用率順にソート vagrant@vagrant-ubuntu-trusty-64:~$ top -o %MEM # メモリ使用率順にソート. -o fieldnameでソート順を切り替えられる。centosでは-a? vagrant@vagrant-ubuntu-trusty-64:~$ top -p [PID] # 特定のプロセスを監視 vagrant@vagrant-ubuntu-trusty-64:~$ top -d3 # 3秒ごとに更新
topではshift
でソート順の切り替えができる。
shift + m // メモリ使用率順 shift + p // CPU使用率順
ヘッダについて
vagrant@vagrant-ubuntu-trusty-64:~$ top top - 19:56:06 up 2:12, 2 users, load average: 0.00, 0.01, 0.05 Tasks: 75 total, 1 running, 74 sleeping, 0 stopped, 0 zombie %Cpu(s): 0.0 us, 0.0 sy, 0.0 ni,100.0 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st KiB Mem: 501748 total, 140316 used, 361432 free, 5252 buffers KiB Swap: 0 total, 0 used, 0 free. 24128 cached Mem
1行目は、uptime
コマンドと同じ出力。現在時刻、システム稼働時間、ログインユーザ数、LoadAverageと並ぶ。LAは左から、1分、5分、15分を単位時間としている。2行目は、タスクの統計。上の例では、75のタスクがあり、稼働中のものは1、待機中のものが74となっている。3行目はCPU。us
がユーザ、sy
がシステム、ni
がnice、id
がidle、wa
がiowait、hi
はhardware interrupt、si
はsoftware interrupt、st
がsteal。
3行目、4行目はメモリ統計。これはfree
で解説したものと同じ。
プロセス一覧
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 1789 vagrant 20 0 107696 1352 348 S 0.3 0.3 0:01.28 sshd 8523 vagrant 20 0 23536 1560 1116 R 0.3 0.3 0:00.02 top 1 root 20 0 33608 1716 276 S 0.0 0.3 0:00.86 init
ここでのCPUは1コアを100%とした値が出力されるため、仮に何かのバッチスクリプトのプロセスが100%になっていたからといって焦る必要はない(効率はよくないだろうけど)。
PID // プロセスid USER // 実行ユーザ PR // 優先度 NI // ナイス値 VIRT // 割り当てられた仮想メモリのサイズ RES // スワップのない物理メモリのサイズ SHR // 割り当てられた共有メモリのサイズ S // プロセスのステータス。R: 実行可能,S: 停止,D: 割り込み不可の停止,T: 停止またはトレース中,Z: ゾンビ・プロセス,W: スワップ・アウトしたプロセス, N: ナイス値が正 %CPU // CPU使用率 %MEM // メモリ使用率 TIME // プロセスの経過時間 COMMAND // タスクのコマンド名
LoadAverage
1CPUにおける単位時間あたりの実行待ち、ディスクI/Oの完了待ちをしているタスク。これが高ければ高いほどCPUかIO起因で高負荷といえる。雑な例えをすると、手元のPCでchromeとfirefoxとxcodeを立ち上げて更にでかいExcelファイル読み込もうとすると、マルチタスクとはいえちょっと重いよねって話。
sar
sarコマンドは、CPU使用率およびディスクI/Oを出力できる。他のコマンドと異なる点としては、過去に遡れることが挙げられる。また、topやfreeと異なり定期的に1行ずつ出力されるので、負荷試験時などに計測を行いやすい。sysstat
パッケージをインストールする必要があるので、sarコマンドがない場合はインストールする。
vagrant@vagrant-ubuntu-trusty-64:~$ sudo apt-get install sysstat
デフォルトでは10分間隔でログ出力されている。必要に応じて変更する場合は、以下のファイルを編集する。また、ログの保存期間も必要に応じて伸ばしておく。centosであれば/etc/sysconfig/sysstat
あたり。ubuntuであれば/etc/default/sysstat
?(未検証)
vagrant@vagrant-ubuntu-trusty-64:~$ cat /etc/cron.d/sysstat ~ # Activity reports every 10 minutes everyday 5-55/10 * * * * root command -v debian-sa1 > /dev/null && debian-sa1 1 1
オプション
vagrant@vagrant-ubuntu-trusty-64:~$ sar -q loadaverage vagrant@vagrant-ubuntu-trusty-64:~$ sar -u CPU使用率 vagrant@vagrant-ubuntu-trusty-64:~$ sar -b I/O vagrant@vagrant-ubuntu-trusty-64:~$ sar -r メモリとスワップ使用率 vagrant@vagrant-ubuntu-trusty-64:~$ sar -s time 時間以降のデータ vagrant@vagrant-ubuntu-trusty-64:~$ sar -e time 時間までのデータ
オプションなしでsar 1 5
等の指定も可能。この場合は、1秒を単位時間として5秒間CPUの使用率を出力する。
vagrant@vagrant-ubuntu-trusty-64:~$ sar 1 5 Linux 3.13.0-55-generic (vagrant-ubuntu-trusty-64) 07/06/2015 _x86_64_ (1 CPU) 08:47:12 PM CPU %user %nice %system %iowait %steal %idle 08:47:13 PM all 0.99 0.00 0.00 0.00 0.00 99.01 08:47:14 PM all 0.00 0.00 0.00 0.00 0.00 100.00 08:47:15 PM all 0.00 0.00 1.00 0.00 0.00 99.00 08:47:16 PM all 0.00 0.00 0.00 0.00 0.00 100.00 08:47:17 PM all 0.00 0.00 0.00 0.00 0.00 100.00 Average: all 0.20 0.00 0.20 0.00 0.00 99.60
sar
は過去に遡って出力できるため、ボトルネックの特定に重宝する。-q
でLoadAverageが高かった場合は、-u -e
で該当時間帯のCPU使用率をチェック、user
が高ければCPU負荷、iowait
が高ければディスクI/O負荷と切り分けることができる。
vagrant@vagrant-ubuntu-trusty-64:~$ sar -u -s 04:00:00 04:00:01 AM CPU %user %nice %system %iowait %steal %idle 04:10:01 AM all 4.78 0.01 1.38 2.84 0.00 90.99 04:20:01 AM all 3.03 0.00 1.10 0.02 0.00 95.85 04:30:01 AM all 2.99 0.00 1.06 0.02 0.00 95.93
監視ツールの見方
まず、監視ツールを導入する。ここではXymon
(旧hobbit
)を利用して、メモリに関する部分のみ見ていくことにする。Xymon
はオープンソースのネットワーク・サーバ監視ツール。読み方はサイモンorシモン。恐らくサイモンだろう。シモンとガーファンクルとは言わないし。。。GPL2ライセンス。
これが監視ツールで満たときのメモリ使用率。
free
コマンドを元にグラフを出力している。
actual
は、バッファとキャッシュ分を差し引いた値。free
でいうところの-/+ buffers/cache
行のused
のことである。real
は、Mem
行、つまりバッファとキャッシュを含めた使用率の値である。そのため、real
よりもactual
の方が低い値となっている。ページキャッシュがあまり乗っていない起動直後は、actual
とreal
に大きな変化が見られない。swap
はその名の通りスワップです。
言い回しが違うだけで確信が掴めなかったけど、以下のMLに書いてあった。
インストール手順は、こちらの記事が参考になります。
おわり
Linuxにおけるメモリの扱いを中心としてざっと見てきたが(CPUとかディスクとかが中途半端に入ったけど)、メモリ管理の仕組みそのものや、メモリ管理を意識したスケールアウト時の考え方、各コマンドの詳細、実践に則した使い方、監視ツールの多項目等、もっと掘り下げたいところ。間違い等あれば指摘ください。
参考資料
マネジメントは難しい
マネジメントは難しい。
得体の知れないプロジェクトという怪物に対して、有用なアプローチを見出し、いかなる手段を用いても遂行しなければならない。考えるべきことは山ほどある。プロジェクトのスケジュール管理、非エンジニアとの企画検討、要件のヒアリング、仕様策定。差し込まれる割り込み案件、実装すべき機能のタスク化、ブレイクダウン。設計・コードレビュー、チームの技術力底上げ、メンバのモチベーション管理、その他雑用、ドサ回りに至るまでもがマネジメントに含まれる。
『Team Geek』を読んで共感する部分もあったが(HRT原則は本当に心がけるべき)、違和感を拭えないのは、メンバを「信じて、任せるべき」であるとする部分だ。チームとしての総合力が突出して高くない且つスケジュールがタイトな現場に於いては、これは当てはまらないと感じている。
マネジメントには頭と時間を使う。仕様策定やアプリケーションの設計など、考えるべきことは山積みであり、これらに考慮漏れや曖昧さが含まれると、プロジェクトは滞り、メンバは曖昧な要求に対して不満を持ち、最終的な成果物の価値も落とす結果になる。マネジメントを行う者は「考える事」と「論理的思考に基づき決定すること」に頭と時間を可能な限り費やすべきだ。また、睡眠不足や疲労が蓄積している場面では、適切な判断が下せないので、自分は手を動かさず、極力休息を取る必要がある。マネジメントと実際に手を動かす作業をそれぞれ充分に遂行するのは、非凡な人材にしかできない。ので、「信じて、任せるべき」なのである。
しかし、それではうまく回らない。プロジェクトに対して、決められた納期(あるいは、求められるスピード感)で曖昧な要件の中で仕様を固め、品質のよいコードを書くことができる人間ばかりではない。むしろ、自分も含め大抵の人が充分にそれをこなすことができない。原因は無数に存在する。曖昧な仕様を積極的に解決しない、スケジュールを意識せず永遠に設計について思案する、そもそも要求を満たしていない、エラー処理など、考慮漏れがあまりにも多い、等。
「ただ単にお前が無能なだけではないか」といった意見はごもっともである。これらは、冒頭で挙げたマネジメントが全て解決する。メンバ個々人の性質に応じて、仕様を細かく固めてから依頼することであったり、当事者意識を持たせるためのモチベーション向上であったり、経験不足や細部の曖昧さをテストコードを書かせることで補ったり、等のアプローチが考えられる。だが、同時に複数ラインで様々なことが進行する中で、全てに対して適切な処置を施し、判断を下すことは中々に難しい。毎日、気がつけば深夜だ。
マネジメントする者自身が充分な実力をつければ、様々なことが解決する。必要なのは、自分の頭が充分に冴えたコンディションを維持できる時間を確保することだ。仕様決定や設計は、論理的思考とソフトウェア設計の知識および経験を培えば時間は削減できるし、数時間かかるようなコードレビューも、テスト環境が充分に整備されていればコードレベルのバグ等は担保され、人間が気にするべき設計や要件を満たしているか、といった部分に注力できる。しかしながら、中〜大現場なソフトウェア開発の現場では、古いライブラリや複雑なコードに縛られてそういった改善を行うのが大きなコストになっていたり、次々に来る要望と既存システムの保守運用に対応するのに精いっぱいだったりして、中々うまくいかないのも現実である。そういった事情の上で、「信じて、任せる」ことができるのなら喜んでお願いするのだが、実際には厳しかったりする。
故に、結局は自分の手を動かすことで解決する。
これは純粋に人が足りない、ということの証左でもあると思うが、「人が足りない」という言葉の裏には一部甘えだったり、自分の未熟さを認めるような響きを感じていて、敗北感を感じる。簡単には音を上げたくない、といった個人的心情もあるのかもしれない。また、コードを書く時間を確保したい、という心情もそれに交錯する。弊害として、作業内容が曖昧なままだったり、仕様の考慮漏れなどが発生して、メンバの作業を停滞させるし、今ひとつチームの開発効率が高まらない結果を産む。
結局のところ、問題はマネジメントする者自身にあるのだろうと思う。適切なマネジメントと、コードを存分に書くことは両立しない。両立させるためには、頭と時間、確かな技術力が全て必要なのだ。それを持たざる者は、コードを書くことを優先してはならない。プロジェクトを成功に導くことが至上命題だからだ。しかしながら、自分がマネジメント専業となるには、まだ早過ぎる。
Team Geek ―Googleのギークたちはいかにしてチームを作るのか
- 作者: Brian W. Fitzpatrick,Ben Collins-Sussman,角征典
- 出版社/メーカー: オライリージャパン
- 発売日: 2013/07/20
- メディア: 単行本(ソフトカバー)
- この商品を含むブログ (20件) を見る
mysqlコンソールでプロンプトを変更する
mysqlコマンドの出力結果を貼ったりするときに、見る人に「このサーバ内での作業なんですよー」みたいなのを示しておきたかったりする。もちろんデフォルトは以下
mysql>
そういうときはPROMPTを使うとよい。作業日時やログインユーザ、ホスト名などの表示変更が可能だ。例えば作業ユーザとホスト名を指定したい場合は以下。
mysql> PROMPT \u@\h >
>
とスペースを最後につけておいた方が個人的には見栄えがよい。
プロンプトは次のような表示となる。
user@database01 >
以下はよく使うシーケンス。
\c カウンタ。複数のSQLを実行する場合にナンバリングに使える \D 日時 \u ログインユーザ名 \h ホスト名
特定のプロンプトを毎回打つのが面倒なので、その場合は$HOME/.my.cnf
にPROMPTを設定しておけば、デフォルトで設定できる。
グループプロジェクトから学んだこと
学びだ。
Ansibleで冪等性を保つためにはfailed_whenとかstatを使うと便利
構成管理ツールとしてAnsibleを使って開発環境を作っているんだけど(本番でも使えるようにとかはまだできてない)、特定のコマンドの実行結果によって次の処理をスキップするかどうかみたいなのを制御したい場面がある。
例えばcentos6.xでnginxの設定を行いたいとすると、以下のようになる。
- name: get nginx rpm get_url: url="http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm" dest="/var/tmp/nginx-release-centos-6-0.el6.ngx.noarch.rpm" - name: add nginx rpm yum: name=/var/tmp/nginx-release-centos-6-0.el6.ngx.noarch.rpm state=present - name: install nginx yum: name=nginx state=present - name: register service service: name=nginx enabled=yes - name: add conf file copy: src=../files/extra.conf dest=/etc/nginx/conf.d - name: move default conf shell: mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.backup
確かにこれでもnginxのインストールと必要な設定は行えるが、このままでは冪等性が担保されない。Ansibleのコアモジュールの多くは冪等性をチェックしているが、shellモジュールなどの冪等性は担保されない。対象のサーバに対してこのスクリプトを実行した場合、2回目移行もdefault.confをmvしようとしてエラーになるだろう。
幸いAnsibleにはregisterという出力を変数に格納できる仕組みがあるので、それを利用するとよい。
- name: is nginx already installed shell: which nginx register: which_nginx # (中略) - name: move default conf shell: mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.backup
しかし、これではエラーとなってしまう。Ansibleは終了ステータスが0以外のものを原則エラーとして解釈するからだ。which nginxでnginxが存在しなかった場合、no nginx foundのようなものがエラー出力に出てしまうから、ハンドリングする必要がある。
結果、以下のように書くのがよい。
- name: is nginx already installed shell: which nginx register: which_nginx failed_when: which_nginx not in [0, 1] # (中略) - name: move default conf shell: mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.backup when: which_nginx.rc == 1
failed_whenは値に記述した結果のBooleanでタスクの成功可否を判定する。failed_whenを使うことで終了ステータスの正常・エラーに関わらずタスク自体は通過させ、以後はそこで宣言した変数のステータスや出力内容を見ながらタスクを実行するかスキップするか決めるようにするとよいと思う。
余談だが、特定ファイルの存在確認をするためには以下のようなやり方がある
- name: check default conf stat: path=/etc/nginx/conf.d/default.conf register: is_exists - name: move default conf shell: mv /etc/nginx/conf.d/default.conf /etc/nginx/conf.d/default.conf.backup when: is_exists.stat.md5 is defined