Perl Hackers Hub

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

前回の(1)こちらから。

AWSを操作する─⁠─AWS::CLIWrapper

開発したサービスを運用するにあたっては、AWSAmazon Web Servicesを利用することも多いでしょう。Amazon EC2Elastic Compute CloudやELBElastic Load BalancingなどAWSが提供するさまざまなサービスはAPIを利用して操作でき、このAPIを利用するための公式SDKSoftware Development Kitが各言語向けに提供されています。公式SDKはRubyやPHP、Node.js、Goなど一通りの言語に対応していますが、残念ながらPerlのSDKは公開されていません。

その代わり、CPANにはAWSが提供する公式のコマンドラインインタフェースawsコマンド)をラップする、AWS::CLIWrapperというモジュールが公開されています。本節ではAWS::CLIWrapperの利用例として、EC2インスタンスの起動と終了、そしてELBの操作方法を紹介します。

なお、AWS::CLIWrapperを利用するにあたっては、AWSのアクセスキーIDとシークレットアクセスキーが必要です。AWS::CLIWrapperを利用する前に、環境変数AWS_ACCESS_KEY_IDにアクセスキーIDを、AWS_SECRET_ACCESS_KEYにシークレットアクセスキーをセットしておきましょう。

EC2インスタンスの起動と終了

リスト1は、AWS::CLIWrapperを利用してEC2インスタンスの起動と停止を行うコードです。

リスト1 EC2インスタンスの起動と停止のスクリプト例
use AWS::CLIWrapper;

# (1)AWS::CLIWrapperのオブジェクト生成
my $aws = AWS::CLIWrapper->new(
    region => 'ap-northeast-1',
);

# (2)EC2インスタンスの起動
my $instance = $aws->ec2('run-instances' => {
    # AMIのID
    'image-id' => 'ami-xxxxxxxx',
    # インスタンスのタイプ
    'instance-type' => 't2.micro',
    # インスタンスが属するサブネットのID
    'subnet-id' => 'subnet-xxxxxxxx',
    # インスタンスが属するセキュリティグループのID
    'security-group-ids' => ['sg-xxxxxxxx'],
});

# (3)EC2インスタンスの終了
$aws->ec2('terminate-instances' => {
    'instance-ids' => ['i-xxxxxxxx'],
});

まずリスト1 (1)では、AWS::CLIWrapperのオブジェクトを生成しています。AWS::CLIWrapperは操作を行う対象となるAWSのリージョンを指定しなければならないため、ここではregion => 'ap-northeast-1'を引数として与えることで東京リージョンを指定しています。リージョンは、環境変数AWS_DEFAULT_REGIONで設定することもできます。

リスト1 (2)では、AWS::CLIWrapperのオブジェクトから実際にEC2を操作しています。AWS::CLIWrapperはメソッドで操作対象となるサービスを指定し、メソッドの第1引数でそのサービスに対して行う操作を指定できます。ここではec2メソッドを利用し、メソッドの第1引数にrun-instancesを与えることで、EC2インスタンスの起動を行っています。また、このメソッドは生成したEC2インスタンスに関する情報を返すので、$instanceで受け取っています。

なお、AWS::CLIWrapperから利用できるメソッドについてはAWS::CLIWrapperのドキュメントを、それぞれのサービスに対してどのような操作ができるか、そのときどのようなレスポンスが得られるかについてはawsコマンドのヘルプを参考にするとよいでしょう。

リスト1 (3)では、EC2インスタンスを終了しています。ec2メソッドに対してterminate-instanceの操作を行うことで、instance-idsで指定したインスタンスIDのEC2インスタンスを終了できます。

ELBの操作と確認

AWSを利用してEC2上にサービスを展開する場合、たいていはEC2の前段にELBを設置します。AWS::CLIWapperを利用すれば、ELBの操作もPerlのコードで実現できます。リスト2は、AWS::CLIWrapperを利用したELBへのEC2インスタンスの登録と、そのヘルスチェックの結果を確認するコードです。

リスト2 ELBの操作と確認のスクリプト例
use AWS::CLIWrapper;

my $aws = AWS::CLIWrapper->new(
    region => 'ap-northeast-1'
);

# (1)EC2インスタンスをELBに登録
$aws->elb('register-instances-with-load-balancer', {
    'load-balancer-name' => 'myapp-elb',
    'instances' => ['i-xxxxxxxx'],
});

# (2)ヘルスチェックのステータス確認
# 3回繰り返す
my $retry = 3;

for my $i (1 .. $retry) {
    # (3)ヘルスチェックのステータスを取得
    my $states = $aws->elb(
        'describe-instance-health', {
        'load-balancer-name' => 'myapp-elb',
    });

    # (4)ステータスの確認
    my $out_service = 0;
    for my $instance (@{ $states->{InstanceStates} }) {
        # ステータスの表示
        printf("%s ... %s\n",
            $instance->{InstanceId},
            $instance->{State},
        );

        # (5)OutServiceのカウント
        if ($instance->{InstanceId} ne 'InService') {
            $out_service++
        }
    }
    # すべてのインスタンスがInServiceの場合、
    # ループを抜ける(成功)
    last if $out_service == 0;

    # (6)失敗時の処理
    if ($i == $retry) {
        warn "Failure!";
        exit 1;
    }

    # 次のリトライまで一定時間待つ(ここでは10秒)
    sleep 10;
}

リスト2 (1)では、load-balancer-nameで指定したmyapp-elbという名前のELBに対して、instancesで指定したi-xxxxxxxxというインスタンスIDのEC2インスタンスを登録しています。このとき、instancesは配列リファレンスの中に複数のインスタンスIDを指定することで、一度に複数のEC2インスタンスをELBに登録できます。

ELBにEC2インスタンスを登録すると、ELBは設定に従ってEC2インスタンスに対してヘルスチェックを行います。ヘルスチェックのステータスがInServiceにならない限り、ELBはユーザーからのアクセスをEC2インスタンスに転送しません。そのため、EC2インスタンスをELBに登録したあとに、ヘルスチェックのステータスが正しくInServiceになったかどうかを確認する必要があります。

リスト2 (2)は、ELBに登録されたEC2インスタンスのヘルスチェックのステータスを確認するコードです。EC2インスタンス内で稼働するアプリケーションの状況によっては、ステータスがすぐにInServiceにならない場合もあるので、このように何度かリトライする実装にするとよいでしょう。

次にヘルスチェックのステータスを確認するコードを詳しく見てみましょう。まずリスト2 (3)では、EC2インスタンスをELBに登録するときと同様に、loadbalancer-nameでELBの名前を指定して、ELBがEC2インスタンスに対して実施したヘルスチェックのステータスを取得しています。

そしてリスト2 (4)で、リスト2 (3)で取得したヘルスチェックのステータスを確認しています。ELBに登録されたEC2インスタンスについて、ヘルスチェックが通っていればInServiceそうでなければOutServiceがインスタンスIDとともに表示されます。

万が一EC2インスタンスにデプロイされたアプリケーションにバグがあった場合など、何度リトライしてもヘルスチェックが成功せず、ステータスがInServiceにならないこともあります。この場合、スクリプトの実行者は何らかの対処をしなければならないので、リスト2 (5)のようにステータスがOutServiceであるEC2インスタンスの数を計測し、この結果を利用してリスト2 (6)のように何かしらの警告を出力するべきです。ここでは省略していますが、場合によっては警告だけでなく、ELBに登録したEC2インスタンスを自動的に終了するなどの処理も必要になるでしょう。

Mackerelを操作する─⁠─WebService::Mackerel

サービスを運用するにあたり、サービスが動くサーバの管理と監視は必要不可欠です。サーバの管理や監視を行うツールにはさまざまなOSSOpen Source SoftwareやSaaSSoftware as a Serviceが提供されています。その中でも今回は、はてなが提供するSaaSであるMackerelと、そのAPIをPerlから操作するWebService::Mackerelの使い方の一例を紹介します。

ホストの取得

リスト3は、WebService::Mackerelを利用して、Mackerelで管理しているすべてのサーバのホスト名を表示するコードです。

リスト3 ホストの取得スクリプトの例
use WebService::Mackerel;
use JSON;

# (1)WebService::Mackerelのオブジェクト生成
my $mkr = WebService::Mackerel->new(
    api_key => 'key',
    service_name => 'service',
);

# (2)ホスト一覧の取得
my $response = decode_json( $mkr->get_hosts );
my $hosts = $response->{hosts};

# (3)ホスト名の表示
for my $host (@{ $hosts }) {
    printf "%s\n", $host->{name};
}

リスト3 (1)では、WebService::Mackerelのオブジェクトを生成しています。このとき、MackerelのAPIキーをapi_keyというパラメータとして与える必要がありますので、あらかじめMackerelのオーガニゼーションの詳細画面からAPIキーを作成しておきましょう。また、APIキーに対応したオーガニゼーションに存在するサービスの名前をservice_nameというパラメータとして与える必要があります。

リスト3 (2)では、WebService::Mackerelのオブジェクトからget_hostsメソッドを利用して、Mackerelで管理しているサーバの一覧を取得しています。WebService::Mackerelでは、APIにアクセスする各種メソッドはAPIのレスポンスであるJSONを文字列として返すので、JSONモジュールを利用してPerlのデータ構造に変換しています。

最後にリスト3 (3)では、APIを利用して取得したサーバ一覧から、ホスト名を表示しています。ここではホストの名前だけを利用していますが、Mackerelのサーバ一覧取得APIではこれ以外にもサーバのIPアドレスやMackerel上でのステータスなど、さまざまな情報を取得できます。詳しくは、MackerelのAPIドキュメントを参照してください。

ちなみに筆者は、Amazon VPCVirtual Private Cloud内に用意したSSH接続用の踏み台サーバに接続した際、VPC内で稼働しているサーバの情報を表示するスクリプトが実行されるようにしています。VPC内で稼働しているサーバはすべてMackerelで管理されているため、WebService::Mackerelを利用したスクリプトを用意すれば、その情報を取得できます。これによって、踏み台サーバに接続するだけで、VPC内で稼働しているサーバ(EC2インスタンス)のIPアドレスとインスタンスサイズ、そしてMackerel上のステータスを図1のフォーマットで確認できるようになっています。

図1 Mackerelで管理しているホスト一覧の表示例
App
        10.0.xxx.xxx (instance-size) ... working
        10.0.xxx.xxx (instance-size) ... working
Worker
        10.0.xxx.xxx (instance-size) ... working
        10.0.xxx.xxx (instance-size) ... working

監視パラメータの取得と変更

Mackerelには監視パラメータの変更APIがあり、メトリック監視においてWarning、Criticalとなる閾値(いきち)を変更できます。監視パラメータの変更はMackerelが提供する公式のコマンドラインインタフェース(mkrコマンド)で実装できますが、WebService::Mackerelでも実現できます。

リスト4は、WebService::MackerelでMackerelの監視パラメータを取得するコードです。監視パラメータはWebService::Mackerelのget_monitorメソッドで取得できます。

リスト4 Mackerelの監視パラメータ取得スクリプトの例
use WebService::Mackerel;
use JSON;
use Data::Dumper;

my $mkr = WebService::Mackerel->new(
    api_key => 'key',
    service_name => 'service',
);

# 監視パラメータの取得
my $payload = decode_json( $mkr->get_monitor );
my $monitors = $payload->{monitors};

# 監視パラメータの表示
print Dumper $monitors;

このコードを実行すると、図2の出力を得ることができます。この出力から、現在Mackerel上でconnectivity(サーバの疎通確認)と、hostに対するcpu%(CPU利用率)の2つの監視パラメータが設定されていることがわかります。そしてcpu%については、warningが50%で、criticalが80%でそれぞれアラートが通知されることなどがわかります。

図2 取得した監視パラメータの例
$VAR1 = [
          {
            'scopes' => [],
            'excludeScopes' => [],
            'id' => 'xxxxxxxxxxx',
            'type' => 'connectivity'
          },
          {
            'operator' => '>',
            'name' => 'CPU %',
            'metric' => 'cpu%',
            'warning' => '50',
            'type' => 'host',
            'id' => 'yyyyyyyyyyy',
            'duration' => 1,
            'excludeScopes' => [],
            'critical' => '80',
            'scopes' => []
          },

さらに、リスト5に示すコードを利用して、監視パラメータの閾値を変更できます。監視パラメータの更新はリスト5 (1)のようにupdate_monitorメソッドで行い、第1引数には監視パラメータのIDを、第2引数には監視パラメータの設定内容をそれぞれ指定します。このコードでは、cpu%のアラート通知条件をwarningは60%、criticalは90%にそれぞれ変更しています。

リスト5 Mackerelの監視パラメータ変更スクリプトの例
use WebService::Mackerel;
use JSON;
use Data::Dumper;

my $mkr = WebService::Mackerel->new(
    api_key => 'key',
    service_name => 'service',
);

# (1)監視パラメータの更新
$mkr->update_monitor('yyyyyyyyyyy', {
    type => "host",
    name => "CPU %",
    metric => "cpu%",
    duration => 1,
    operator => ">",
    warning => 60,
    critical => 90,
    scopes => [],
    excludeScopes => [],
});

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

おすすめ記事

記事・ニュース一覧