Tech Sketch Bucket of Technical Chips by TIS Inc.

CapstranoでAWSのAPIを使って実行時にroleを設定する

Pocket

この記事はeXcale Developer's Blogから移転されたものです。

eXcale開発チームの西谷です。

Capistranoを利用するときに実行時にroleを決めたいときってありませんか?
今回はそのあたりについてです。

eXcaleではインフラとしてAWSを使っています。
AWSの魅力を最大限に活かして、必要なときに必要なインスタンスをマスターのAMIから起動したり、不要になったら破棄したりということをAPIを利用してアプリケーションで制御しています。 ユーザのアプリケーションのインスタンスを稼働させるEC2インスタンスの数は常に変動していて増えたり減ったりを繰り返しています。このような中で何かサーバの設定を全台更新する必要が発生した場合、AMIに反映することでそれ以降に起動されたEC2インスタンスは変更が反映された状態ですがそれ以前に起動されているものには反映されていないので別途全台に反映していく必要があるんですね。こういったことを我々はCapistranoを利用して行なっています。
※Capistranoはそれ以外にもデプロイ処理などeXcaleでは多様な場面で利用しています。

問題は実行時点にならないと対象サーバ(EC2インスタンス)が何台あるかも、そのIPもわからないということです。

Capistranoでどうやるか

そもそも、roleに限らずタスク内で使用する値を実行時に指定したい場合にやり方は3パターンあります。

パターン1: 実行時に-Sオプションを付ける

この場合、受けとる時は#{変数名}で渡された値を取得できます

例)
以下のようなタスクを用意します。

以下のように-Sで変数名と値を指定して実行します。

すると、targetという変数が展開されてサーバ側ではtraceroute 10.10.10.10が実行されます。

パターン2: 実行時にENVとして渡す

実行時の引数として変数 = 値の形式で渡すと内部からはENV['変数名']でアクセスできます。

例)
以下のタスクを用意

実行時、タスク名の後に変数名と値を指定して実行します。
結果はパターン1と同様です。違うのはENV['target']で値を取り出している点です。

パターン1、パターン2ともにCapistranoの実行時に外部から値を渡す方法です。
こうやって実行時に指定した値を受け取ってタスクの内部でセットすることで実行時にroleを指定することが可能です。

次に、パターン2のやり方でroleに実行対象のサーバをセットするサンプルです。基本的にパターン1でも変わりません。
このサンプルでは少しわかりやすくするため先ほどまでのtracerouteではなくhostnameコマンドを実行しています。

コマンド実行時に指定したtargetsの値をENV['targets']で取得してカンマ区切りで分割した後1つ1つをroleにセットしています。

パターン3: タスク内部で生成する

eXcaleではこのパターンです。
引数なしでCapistranoのタスクを実行し、タスク内部でAPIを利用して条件に合致するEC2インスタンスのIPを取得してroleにセットしています。

サンプルなので例外処理はきちんとやってません。
アクセスキーとシークレットキーは利用しているAWSのアカウントのものに置き換えてください。また、実行前にaws-sdkのgemをインストールしておいてください。

今回は対象となるサーバ絞込みやすくするため、あらかじめEC2インスタンスにNameというタグで名前をつけてます(実際は起動時に名前を生成して付与)。
その上で、 API使って取得したリストをeachで回して条件に合致すればプライベートIPの値をtargetという変数名でroleとして登録する処理をset_roleというメソッドとして用意しています。
タスク自体はシンプルにhostnameコマンドを実行するだけにして、処理の一番最初にset_roleを呼んでいます。なお、今回はtaskの宣言時に実行対象のroleを指定していません。Capistranoではroleを指定しないと定義されているrole全てに対して処理が行われます。

サンプルではAWSのSDKを使ってますが、それ以外でもroleに登録する処理自体は同じです。

エンジニア採用中!私たちと一緒に働いてみませんか?