Perl Hackers Hub

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

(1)こちら⁠2)こちらから。

データベースのマイグレーション

狭義のInfrastructure as Codeには含まれないかもしれませんが、オペレーションのコード化という意味では、データベースのマイグレーションの実施も立派なオペレーションです。そのために役立つモジュールは、CPANにもいくつか公開されています。本節では、DBIx::Schema::DSLとSQL::Translator::Diffを利用した、データベースのマイグレーション方法について解説します。

なお、データベースのマイグレーションを実現するモジュールとしては、ほかにもGitDDLやGitDDL::Migratorなどが存在します。データベースのマイグレーションについて考えるときは、これらのモジュールも参考にするとよいでしょう。

スキーマをPerlのDSLで表現する─⁠─DBIx::Schema::DSL

DBIx::Schema::DSLは、データベースのスキーマをPerlのDSLDomain Specific Languageドメイン特化言語)で記述できるモジュールです。このモジュールは、DSLで定義したデータベースのスキーマをMySQLやSQLiteなどの各種RDBMSRelational Database Management SystemのDDLData Definition Languageに変換できるだけでなく、SQL::Translatorのオブジェクトに変換できます。これを次項で紹介するSQL::Translator::Diffと組み合わせると、DBIx::Schema::DSLで定義したスキーマと、現在のRDBMSのスキーマの差分の取得などを簡単に実装できます。

リスト6は、DBIx::Schema::DSLを利用したデータベースのスキーマの例です。bookというテーブルと、このテーブルに含まれるidnameというカラムを定義しています。idカラムはプライマリキーでかつオートインクリメントが、nameカラムはNOT NULL制約が、それぞれ指定されています。

リスト6 DBIx::Schema::DSLを利用したスキーマ定義
package My::Schema;
use DBIx::Schema::DSL;

create_table 'book' => columns {
    integer 'id', primary_key, auto_increment;
    varchar 'name', not_null;
};

1;

DBIx::Schema::DSLを利用して定義したスキーマからDDLを取得する場合、次のようなスクリプトを実装するとよいでしょう。

use My::Schema;

print My::Schema->output;

このスクリプトを実行すると、次のようなDDLを取得できます(コメント部分以外を抜粋しています⁠⁠。

SET foreign_key_checks=0;

CREATE TABLE `book` (
  `id` INTEGER NOT NULL auto_increment,
  `name` VARCHAR(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARACTER SET utf8;

SET foreign_key_checks=1;

なお、このDDLはリスト6でRDBMSの指定をしていないので、DBIx::Schema::DSLのデフォルトであるMySQL向けのDDLが生成されます。ほかのRDBMSのDDLを生成したい場合、次のようにスキーマ定義時にdatabaseを利用してRDBMSを指定しなければなりません。次のコードでは、RDBMSとしてSQLiteを指定しています。

package My::Schema;
use DBIx::Schema::DSL;

database 'SQLite';

このほかDBIx::Schema::DSLでは、任意のカラムに対する制約や外部キーなど、さまざまな設定をDSLで定義できます。詳しくは、DBIx::Schema::DSLのドキュメントを参照してください。

スキーマの差分を取得する─⁠─SQL::Translator::Diff

先述したとおり、DBIx::Schema::DSLで定義したスキーマは、SQL::Translatorのオブジェクトに変換できます。そしてSQL::Translatorは、SQL::Translator::Diffを利用することで、任意の2つのSQL::Translatorオブジェクトの差分を取得できます。これを利用した、スキーマの差分の取得とマイグレーションの実行を行うスクリプトの例がリスト7です。

リスト7 (1)は、DBIx::Schema::DSLを利用してスキーマを定義したMy::Schemaから、SQL::Trasnaltorのオブジェクトを取得するコードです。さらにSQL::Translatorは、リスト7 (2)に示すコードで任意のデータベースに接続し、そのデータベースの現在のスキーマ情報をSQL::Translatorのオブジェクトとして取得できます。

リスト7 スキーマの差分表示とマイグレーションのスクリプト例
use DBI;
use SQL::Translator;
use SQL::Translator::Diff;
use My::Schema;

# (1)DBIx::Schema::DSLで定義したスキーマの取得
my $target = My::Schema->translator();

# (2)データベースからのスキーマの取得
my $dbh = DBI->connect( ... );
my $source = SQL::Translator->new(
    parser => 'DBI',
    parser_args => { dbh => $dbh },
)->translate();

# (3)スキーマの差分の取得
my $diff = SQL::Translator::Diff->new({
    output_db => 'MySQL',
    target_schema => $target->schema(),
    source_schema => $source,
})->compute_differences->produce_diff_sql;

# (4)差分の適用(マイグレーション)
for my $stmt (split /;/, $diff) {
    next unless $stmt =~ /\S/;
    $dbh->do($stmt) or die $dbh->errstr();
}

これで、2つのSQL::Translatorのオブジェクトを取得できました。次に、SQL::Translator::Diffを利用して、この2つのオブジェクトの差分を取得します。リスト7 (3)は、DBIx::Schema::DSLから取得した$targetと、SQL::Translatorを利用してRDBMSから取得した$sourceという2つのSQL::Translatorのオブジェクトを利用して、データベースのスキーマの差分を取得するコードです。

SQL::Translator::Diffは、source_schemaで与えた現在の状態のスキーマが、target_schemaで与えたあるべき状態のスキーマになるために必要な変更ALTER TABLECREATE TABLEなどのクエリの実行)を差分として表示します。

そのため、得られたクエリをRDBMSに対して直接適用したり、あるいはリスト7 (4)に示すコードでPerlからクエリを実行すれば、データベースのマイグレーションを行うことができます。

とはいえ、SQL::TranslatorやSQL::Translator::Diffを利用してマイグレーションのしくみを構築するのであれば、これらのモジュールがどの程度信頼できるかを考慮しなければなりません。一例として、マイグレーションを自動的に実行するのではなく、あらかじめ実行するクエリを確認できるようなマイグレーションフローを構築するという方法もあるでしょう。

チャットツールへの通知

Infrastructure as Codeを実施するにあたり、チャットツールへの通知は重要な要素です。たとえば、ここまで紹介してきたIaaSの操作やデータベースのマイグレーションを実施する際、その進捗や成否をチャットツールに自動で通知できれば、オペレーションの様子をチャットツールを通してチーム全員に共有できます。

本節では、最近利用者が増えているチャットツールのHipChatとSlackのための通知モジュールの利用方法を紹介します。

HipChatへの通知─⁠─WebService::HipChat

リスト8は、WebService::HipChatを利用したHipChatへの通知のコードです。

リスト8 HipChatへの通知のスクリプト例
use WebService::HipChat;

# (1)WebService::HipChatのオブジェクト生成
my $hc = WebService::HipChat->new(
    auth_token => 'token',
);

# (2)HipChatへの通知の実施
$hc->send_notification($room, {
    color => 'green',
    message => 'hello!',
});

まず、リスト8 (1)でWebService::HipChatのオブジェクトを生成しています。このとき、auth_tokenでHipChatのAPIトークンを与える必要があります。注意点として、HipChatにはAPI v1とAPI v2という2種類のAPIがあり、それぞれ異なるトークンを取得できます。WebService::HipChatはAPI v2に対応したAPIクライアントですので、API v2のトークンを生成して与えるようにしましょう。

実際の通知の実施は、リスト8 (2)のようにsend_notificationメソッドで行います。第1引数には通知を行うHipChat上のルームのAPI IDを、第2引数にはハッシュリファレンスで通知内容を設定します。ここでは、メッセージの色を緑色で、メッセージとしてhello!という文字列を通知しています。

Slackへの通知─⁠─WebService::Slack::WebApi

一方、Slackに通知を行うためのモジュールとしては、CPANにWebService::Slack::WebApiが公開されています。リスト9は、このモジュールを利用してSlackに通知を送るコードです。

HipChatの例と同じく、リスト9 (1)tokenを与えてWebService::Slack::WebApiのオブジェクトを生成し、リスト9 (2)でメッセージを送信しています。メッセージを送信する際は、メッセージの通知先となるチャンネルを指定するchannelと、メッセージ本文のtextが必須です。

リスト9 Slackへの通知のスクリプト例
use WebService::Slack::WebApi;

# (1)WebService::Slack::WebApiのオブジェクト生成
my $slack = WebService::Slack::WebApi->new(
    token => 'token',
);

# (1)Slackへの通知の実施
$slack->chat->post_message(
    channel => 'channel id',
    text => 'hello!',
);

まとめ

今回は、PerlでInfrastructure as Codeを構築する際に役立つモジュールを紹介しました。ここで紹介したモジュール以外にも、CPANにはInfrastructure as Codeに役立つモジュールがたくさん公開されています。これらのモジュールを駆使して、ぜひPerlを使ったInfrastructure as Codeを楽しんでください。

さて、次回の執筆者は丸山晋平さんで、テーマは「Perlで作るシステム運用ツール」です。お楽しみに。

おすすめ記事

記事・ニュース一覧