Perl Hackers Hub

第37回PerlでInfrastructure as Code―IaaSやSaaSをコードで自動化(1)

本連載では第一線のPerlハッカーが回替わりで執筆していきます。今回のハッカーはpapixこと福本貴之さんで、テーマは「PerlでInfrastructure as Code」です。

本稿のサンプルコードは、WEB+DB PRESS Vol.91のサポートサイトから入手できます。

PerlでInfrastructure as Code!

Infrastructure as Codeという言葉はここ数年とても注目されています。読者のみなさんも一度は目にしたことがあるのではないでしょうか。

Infrastructure as Codeは、サーバの環境構築やデプロイを行う際に用いる手順書をコードに落とし込み、そのコードに対応したツールを使って自動的に実行するしくみです。手順書をコードにすることによって、Gitなどのバージョン管理システムを利用して差分管理が可能になりますし、GitHubやBitbucketなどを利用して、サービス開発と同じPull Requestベースでサービスのインフラを構築できます。

今回は、Perlを利用してInfrastructure as Codeを実現する際に有用なモジュールと、その使い方について解説します。

Perlを使う意味

PerlでInfrastructure as Codeを実現するメリットは、Perlという使い慣れた道具でInfrastructure as Codeを構築できることです。

Infrastructure as Codeを実現するツールはたくさん公開されていますが、それぞれのツール特有の記法や、場合によってはプログラミング言語の知識を習得しなければなりません。たとえばChefやCapistrano、Serverspecを利用するのであれば、Rubyの知識が必要となるでしょう。しかしPerlを使ってサービスを実装しているのであれば、Infrastructure as CodeにもPerlを利用することによって学習コストを低減できます。

Infrastructure as Codeを実現するにあたっては、チームメンバー全員がコードをコントロールでき、サービスそのものやサービスに必要なインフラの変化に追随していかなければなりません。チームメンバーがPerlという使い慣れた道具を持っているのであれば、それを有効に活用して高速に実装することは重要です。

とはいえ、サービスのすべてのインフラをPerlでコード化するのは困難です。Perlという得意な言語を軸にオペレーションをコード化しつつ、たとえばサーバの環境構築であればChefやItamae、Ansibleなどのツールを採用したり、環境構築の結果を検証したいのであればServerspecを採用したりと、Perlにこだわらずに適材適所でツールを選ぶべきでしょう。

Daiku─⁠─Perl版make

PerlでInfrastructure as Codeを実現する場合、オペレーション単位でスクリプトを用意し、そのスクリプトを実行することでオペレーションを実施するのが一般的です。ただ、スクリプトが増えると、どのスクリプトがどのオペレーションに対応するかわからなくなることが多々あります。そんなときは、Daikuを使ってオペレーションを呼び出すようにすると非常にすっきりします。

Daikuとは

Daikuは、makeやRubyのRakeを参考に実装されたビルドツールです。さまざまな処理(タスク)Daikufileにまとめることができ、そのタスクはdaikuコマンドを利用して実行できます。

ここでは、Perlを利用して実装したさまざまなオペレーションを、Daikuでまとめる際に有用な機能に集中して解説します。それ以外の機能については、興味があればDaikuのドキュメントを参照してください。

タスクの登録と実行

Daikuで実行したいタスクは、前述のとおりDaikufileと呼ばれるファイルに記載していきます。例として、Hello, world!という文字列を出力するhelloというタスクを実装してみましょう。この場合、Daikufileは次のようになります。

task hello => sub {
    print "Hello, world!\n";
};

taskの第1引数にあたる文字列helloがタスク名です。そして、第2引数にあたるサブルーチンリファレンスがこのタスクを実行した際に行う処理です。

定義したタスクは、Daikufileが設置されたディレクトリでdaikuコマンドにタスク名をパラメータとして与えることで実行できます。

$ daiku hello
[LOG] Building Task: hello
Hello, world!

namespaceによるタスクの階層化

たとえばappというオペレーション対象に、環境構築を行うbuildと、デプロイを行うdeployというタスクをDaikufileに設定したい場合を考えます。タスク名としてそれぞれapp_buildapp_deployのように、オペレーション対象とタスク名を_で連結して登録することもできますが、このような場合はnamespaceを使ってタスクを階層化したほうが、よりスマートにDaikufileを記述できるでしょう。

namespaceを利用すれば、appというオペレーション対象に対するbuilddeployという名前のタスクは、次のように記述できます。

namespace app => sub {
    task build => sub {
        ...
    };
    task deploy => sub {
        ...
    };
};

namespaceを利用して定義したタスクは、daiku app:buildのように、namespaceで指定した名前空間とtaskで指定したタスク名を:で連結した文字列で呼び出せます。

また、namespaceはネストすることもできます。たとえばappが利用するデータベースのマイグレーションを実行するタスクであれば、次のように記述するとよいでしょう。

namespace app => sub {
    namespace db => sub {
        task migrate => sub {
            ...
        };
    };
};

この場合、タスクはdaiku app:db:migrateで呼び出せます。

複数のタスクをまとめて実行

Daikufileではtaskの第2引数に対して、次のように複数のタスク名を含んだ配列リファレンスを渡すことができます。

task app => ['app:build', 'app:deploy'];
namespace app => sub {
    task build => sub {
        ...
    };
    task deploy => sub {
        ...
    };
};

この場合はappのタスクを実行するだけで、app:buildapp:deployの両方のタスクをまとめて実行できます。

引数付きのタスク

Daikufileで設定するタスクは、次のように引数を受け取ることができます。

task hello => sub {
    my ($task, $name) = @_;
    print "Hello, ${name}!\n";
};

引数付きのタスクは、次のように呼び出すことができます。

$ daiku 'hello[daiku]'
[LOG] Building Task: hello
Hello, daiku!

タスクの説明の記載と確認

Daikufileでは次のように、taskの直前にdescを使うことでタスクの説明を記載できます。

namespace app => sub {
    desc "appに対する環境構築";
    task build => sub {
        ...
    };
    desc "appに対するデプロイ";
    task deploy => sub {
        ...
    };
};

descを利用して説明を記載したタスクは、daiku -Tコマンドで確認できます。

$ daiku -T
daiku app:build # appに対する環境構築
daiku app:deploy # appに対するデプロイ

処理の切り出しによるDaikufileの整理

Daikuを利用することで、オペレーションの定義をDaikufileという1つのファイルに集約できます。しかし、オペレーションのためのコードをすべてDaikufileに記載すると、Daikufileに大量のコードが記載され、メンテナンスしづらくなります。これを防ぐためには、次のようにオペレーションのためのコードをMyApp::Task::Buildなどに切り出し、Daikufileはこれを呼び出すだけにとどめるとよいでしょう。

task build => sub {
    require MyApp::Task::Build;
    MyApp::Task::Build->run();
};

<続きの(2)こちら。>

おすすめ記事

記事・ニュース一覧