Perl Hackers Hub

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

この記事を読むのに必要な時間:およそ 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で作るシステム運用ツール」です。お楽しみに。

著者プロフィール

福本貴之(ふくもとたかゆき・papix)

株式会社ガイアックスのエンジニア。2014年に入社して以来,主にPerlを使った社内基盤ツールの開発や,エンジニアリング環境の改善,技術広報,そして採用などに取り組んでいる。

2012年にPerlの初心者向け勉強会「Perl入学式」を立ち上げて以降,エンジニアに対する教育や成長に関心を持っている。

趣味は鉄道を使った旅行と地域のPerlコミュニティへの参加,そして食べ歩き。寿司とラーメンには目がない。

コメント

コメントの記入