« AWS Lambdaを使ってEC2料金を節約する | メイン | AWS EC2 "悪い" EIPにご用心 »

2015年11月16日 (月)

AWS Lambdaを使ってRedshift料金を節約する

エンジニアの佐藤です。こんにちは。 前回AWS Lambdaを使ってEC2料金を節約するで、監視サーバを待機させずにEC2夜間自動停止を実装する手法をお話させていただきましたが、今回は同様の手法をRedshiftに適用してみましょう。
Redshiftは、カラム駆動型のデータ配置、圧縮、PostgreSQLに近いSQL言語などが特徴の高速分散DBMSと筆者は理解しているのですが、その課金体系はCPUとストレージのセット(ノード)の利用時間の従量課金です(長期事前予約による割引適用もありますが)。EC2と同様、利用が無い時間帯がある場合はリソースの自動解放・復旧を仕掛けることにより利用料金の節約が期待できます。

いくつかの問題と対処

ただしEC2のように単純にはいきません。以下の問題に対処する必要があります。

  • ノードを解放するとデータも消去されてしまうので、事前にスナップショットを作成する必要がある。復旧のときは、スナップショットからリソースを復元する。
  • 再起動した場合はエンドポイントが変更になる可能性がある。

幸い、いずれの場合についても少々の工夫で対処できました。前回同様にNodeJSとAWS SDK for Javascriptを使って実装してみましょう。今回もサンプルコード全体は以下の弊社公開リポジトリで公開しています。

https://github.com/FLECT-DEV-TEAM/cloudblog20151110

スナップショットの使用

稼働中のRedshiftをスナップショットにエクスポートして削除するには、以下のようにクラスターIDとスナップショットIDを指定してdeleteClusterを呼び出します。

var params = {
    ClusterIdentifier:              ctx.ClusterId,
    FinalClusterSnapshotIdentifier: ctx.SnapshotId
};

REDSHIFT.deleteCluster(
    params, 
    function(err, data) {
        if (err) {
            return cb(err);
        }
        cb(null, ctx);
    }
); // end of deleteCluster

このスナップショットから復元するには、クラスターIDとスナップショットのIDを指定してrestoreFromClusterSnapshotを呼び出します。(今回はVPC内部にRedshiftを設定しますので、設定先サブネットやセキュリティグループも設定する必要があります。)ノード数やマスターユーザ設定などは、特に指定しなければスナップショット作成時の設定が引き継がれます。

var params = {
    ClusterIdentifier:      ctx.ClusterId,
    SnapshotIdentifier:     ctx.SnapshotId,
    AvailabilityZone:       ctx.AvailabilityZone,
    ClusterSubnetGroupName: 'default',
    VpcSecurityGroupIds:    ctx.VpcSecurityGroupIds,
    PubliclyAccessible:     false,
    AutomatedSnapshotRetentionPeriod: 0
};

REDSHIFT.restoreFromClusterSnapshot(
    params, 
    function(err, data) {
        if (err) {
            return cb(err);
        }
        cb(null, ctx);
    }
); // end of restoreFromClusterSnapshot

エンドポイント(接続先)

Redshiftのクラスターを起動すると、以下のようなエンドポイントが払い出され、このエンドポイントにPosgreSQLプロトコルで接続することでクラスタを利用することができます。

<クラスターID>.<ランダム文字列>.<リージョン>.redshift.amazonaws.com:<ポート番号>

このエンドポイントは「クラスターIDやノード数を変更した場合に変更される」とSDKでは説明されていますが、「そうしない限り、保存される」とは書いてありません。つまりスナップショットの保存と復元の前後で同じエンドポイントが使える保証は無いようです。このままでは、エンドポイントの都度調査という面倒なことになります。
最も簡単な解決方法はElastic IP AddressでグローバルIPアドレスを設定してしまうことでしょう。しかし今回はVPC内での利用を想定しており、本来グローバルIPアドレスは不要です。
筆者が選択した方法は、「Route 53の内部DNSを設定する」というものです。Route 53ではVPCに接続された内部用のHosted Zoneを設定することができます。このHosted ZoneにCNAMEレコードをAPIで設定し、アプリケーションからはCNAME名でアクセスしてもらいます。具体的には、以下のようなレコードを設定します。

redshift.internal. 60 CNAME cluster-id01.aaaabbbbcccc.ap-northeast-1.redshift.amazonaws.com.

実装にあたっては、最初にクラスターのエンドポイントをクエリします。

var params = {
    ClusterIdentifier: ctx.ClusterId
};
REDSHIFT.describeClusters(
    params,
    function(err, data) {
        var clusters = data.Clusters;
        var c = clusters[0];
        ctx.Endpoint = c.Endpoint.Address;
        cb(null, ctx);
    }
); // end of describeClusters

次にこのエンドポイントを内部DNSのCNAMEレコードとして設定します。

var params = {
    HostedZoneId: ctx.HostedZoneId,
    ChangeBatch: {
        Changes: [{
            Action: 'UPSERT',
            ResourceRecordSet: {
                Name: 'redshift.internal',
                Type: 'CNAME',
                TTL: 60,
                ResourceRecords: [{Value: ctx.Endpoint}]
            }
        }] // end of Changes
    } // end of ChangeBatch
};
ROUTE53.changeResourceRecordSets(
    params,
    function(err, data) {
        if (err) {
            return cb(err);
        }
        cb(null, ctx);
    }
); // end of changeResourceRecordSets

Route 53 Hosted Zoneは以下のようになっています。

Screen_shot_20151116_at_155423


Screen_shot_20151116_at_155700


タイミングの問題

実は筆者はこの段階で一つの壁にぶつかってしまいました。 Redshiftクラスターをスナップショットへ保存する処理も、またスナップショットから復元する処理も、時に長い処理時間を要します。この処理時間は課金対象になりませんが(課金はクラスターが「available」になってから)、長いと復元処理を開始してからエンドポイントが参照可能になるまでの所要時間が、AWS Lambdaの実行時間上限(5分)を超えてしまうのです。また、Lambdaを5分も待機させるのは、そもそもナンセンスにも思えます。
筆者はこの内部DNS設定処理を、スナップショットからの復元処理が完了しそうな時間帯に10分おきに繰り返し実行することにしました。何回かは空打ちとなりますが、エンドポイントが利用可能になってから10分以内にはCNAMEレコードが設定されるはずです。1回の試行時間はわずかで待機中は課金されませんので、リーズナブルです。
最終的には以下のように3つのLambda Functionをスケジュールしました。

  • スナップショットからの復元:午前7時
  • DNS設定試行:午前7時から8時まで、10分おきに実行
  • スナップショット作成とクラスター削除:午前1時

Lambda Functionの設定方法については前回投稿をご参照ください。なお、Lambda Functionの設定にあたっては、前回同様Description項目に設定情報をJSON形式で記載することを前提にしています。以下はこのDescription文字列の例です。

{ "ClusterId": "xxxxxxxx", "SnapshotId": "xxxxxxxx", "AvailabilityZone": "ap-northeast-1c", "VpcSecurityGroupIds": ["sg-xxxxxxxx"], "HostedZoneId": "XXXXXXXXXXXXX" }

まとめ

実装が若干複雑ですが、Redshiftという重量級コンポーネントについても「夜間自動停止」が実装できました。今回ご紹介した手法は、RDSにも容易に転用可能でしょう。

コメント

コメントを投稿

採用情報

株式会社フレクトでは、事業拡大のため、
・Salesforce/Force.comのアプリケーション開発
・HerokuやAWSなどのクラウドプラットフォーム上での
Webアプリケーション開発
エンジニア、マネージャーを募集中です。

未経験でも、これからクラウドをやってみたい方、
是非ご応募下さい。

フレクト採用ページへ

会社紹介

株式会社フレクトは、
認定コンサルタント
認定上級デベロッパー
認定デベロッパー
が在籍している、セールスフォースパートナーです。
また、heroku partnersにも登録されています。
herokuパートナー
株式会社フレクトのSalesforce/Force.com
導入支援サービス
弊社の認定プロフェッショナルが支援致します。
・Visualforce/Apexによるアプリ開発
・Salesforceと連携するWebアプリ開発
も承っております。
セールスフォースご検討の際は、
お気軽にお問合せください。

2022年5月

1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
ブログ powered by TypePad