2014-03-01

ApexからEC2を触ってみる。

恒例の触ってみるシリーズ、今回はAWSの大黒柱(だと勝手に思っている)EC2です!

 

SalesforceでEC2を触ることのモチベーションの一つとしては

「Apexでは不可能な処理をEC2を一時的に立てて処理を行わせる」ということ。

 

例えば、Salesforceの全データをバックアップする!といったバッチ処理の場合は

force.com内部で行わせるよりは別サーバからデータローダCLI等のAPI経由でやってもらった方が

ガバナ制限から開放される上に、プログラミング言語がサポートする範囲の

自由な形式でデータを出力することができます。

しかし、常時稼働のサーバを立ててしまうとコストが大きいため

こういったデータバックアップのような処理はバッチ実行時のみのスポット稼働が求められます。

 

そこでEC2の出番ということです!

 

ということで今回のサンプル↓

/**
 * EC2コモンクラス
 */
public with sharing class EC2Common {
    /**
     * AccessKey
     */
    public String AWSAccessKeyId = EnvSetting__c.getOrgDefaults().AWSAccessKeyId__c;

    /**
     * SecretKey
     */
    public String AWSSecretKeyId = EnvSetting__c.getOrgDefaults().AWSAccessKeySecret__c;

    private final String REGION = 'us-east-1';

    /**
     * インスタンスのStart
     */
    public String startInstances(String instanceId) {
        Map<String, String> params = new Map<String, String>{
            'Action' => 'StartInstances',
            'AWSAccessKeyId' => this.AWSAccessKeyId,
            'SignatureMethod' => 'HmacSHA256',
            'SignatureVersion' => '2',
            'InstanceId.1' => instanceId,
            'Version' => '2013-10-15',
            'Timestamp' => DateTime.now().formatGmt('YYYY-MM-dd') + 'T' + DateTime.now().formatGmt('HH:mm:ssZ')
        };

        params.put('Signature', this.createSignature('POST', 'ec2.' + REGION + '.amazonaws.com', '/', params));

        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://ec2.' + REGION + '.amazonaws.com');
        req.setMethod('POST');

        req.setBody(Utility.getSortedParam(params));
        Http http = new Http();
        HTTPResponse res = http.send(req);

        return res.getBody();
    }

    /**
     * インスタンスのStop
     */
    public String stopInstances(String instanceId) {
        Map<String, String> params = new Map<String, String>{
            'Action' => 'StopInstances',
            'AWSAccessKeyId' => this.AWSAccessKeyId,
            'SignatureMethod' => 'HmacSHA256',
            'SignatureVersion' => '2',
            'InstanceId.1' => instanceId,
            'Version' => '2013-10-15',
            'Timestamp' => DateTime.now().formatGmt('YYYY-MM-dd') + 'T' + DateTime.now().formatGmt('HH:mm:ssZ')
        };

        params.put('Signature', this.createSignature('POST', 'ec2.' + REGION + '.amazonaws.com', '/', params));

        HttpRequest req = new HttpRequest();
        req.setEndpoint('https://ec2.' + REGION + '.amazonaws.com');
        req.setMethod('POST');

        req.setBody(Utility.getSortedParam(params));
        Http http = new Http();
        HTTPResponse res = http.send(req);

        return res.getBody();
    }

    private String createSignature(
        String method,
        String hostname,
        String resource,
        Map<String, String> params
    ) {
        String signature = 
            method + Constants.LF +
            hostname.toLowerCase() + Constants.LF +
            resource + Constants.LF +
            Utility.getSortedParam(params);
        return String.valueOf(EncodingUtil.base64Encode(
            Crypto.generateMac(
                'hmacSHA256', 
                Blob.valueOf(signature),
                Blob.valueOf(this.AWSSecretKeyId)
            )
        ));
    }
}

 

REST APIのリファレンスはこちらから。

Signature Version2なのでSignature生成はSimpleDBのコードのコピペです。

 

また、例としてはインスタンスのスタートとストップだけですが、

Salesforceで利用することを考えるとこれで十分な気がします。

 

Salesforceの利用フローとしては以下のようなイメージ

  1. Salesforceからインスタンスを立ち上げ(Start)。

  2. EC2でバッチ実行(/etc/init.d内に起動スクリプト置いたり、cron実行させたり)

  3. バッチ終了時にEC2内のスクリプトでシャットダウン(shutdownコマンド使うか、インスタンスStopのAPIを叩く)

※shutdown時の振る舞いがterminateになっているとshutdownするとインスタンスが消えるので注意

 

Apexバッチを使用すれば、スケジューリングされた自動バッチ処理ができます。

さらに、AWSのAuto Scalingを使えばApexバッチを利用すること無く自動バッチ処理ができます。

 

ということで、次回はAuto Scaling!

 

このエントリーをはてなブックマークに追加