AWS CloudWatch(2)システム運用における監視項目

AWSでシステム構築を行う場合、基本的にはこのCloudWawtchのみの監視で十分なことが多い。様々な監視項目が存在するが、システム運用を行う際に特に監視する必要のある項目は以下の通り。

DynamoDB

多くの項目がデフォルトで 1 分ごとにデータを送信するが、5 分間隔で送信されるデータも存在する。事前に設定した容量(スループット)が十分であるかを監視しておくと良い。

  • ProvisionedReadCapacityUnits
  • ConsumedReadCapacityUnits
  • ProvisionedWriteCapacityUnits
  • ConsumedWriteCapacityUnits

EC2

Amazon EC2 はデフォルトで 5 分ごとに CloudWatch にデータを送信する。インスタンスを作成する際に、「詳細モニタリングを有効」にチェックを入れることでこれを 1 分ごとに変更することができるCPU使用率を監視しておくと良い。メモリやディスクのメトリクスを取得するためには、CloudWatch Agentをインストールする必要がある

  • CPUUtilization

Kinesis

事前に設定した容量(シャード数)が十分であるかを監視しておくと良い。

  • PutRecords.Records
  • IncomingRecords
  • GetRecords.Bytes
  • IteratorAge

AWS CloudWatch(1)CloudWatchの概要

CloudWatchとは

CloudWatchは、AWSの各リソースを監視することのできるサービスである。AWSでシステム構築を行う場合、基本的にはこのCloudWawtchのみの監視で十分なことが多く、Zabbixなどの他の監視環境を構築する必要がない。フルマネージドのサービスの場合は自ら監視環境を構築することができないため、CloudWatchによる監視が必須となる。また、CloudWatch Logsを用いることで、各リソースのLogデータをCloudWatchで監視することが可能になる。CloudWatch Logsは、ログデータを無期限で保存する

CloudWatchの概要

CloudWatchは、NameSpace(AWS/service)と呼ばれるAWSサービス単位の項目とMetricsと呼ばれる時系列のデータポイントセットから構成され、これらを組み合わせて指定することで任意の項目を確認することが可能となる。CloudWatch上のデータ(CloudWatch Logsのデータを除く)は削除することはできず、15ヶ月経過したデータポイントは自動的に削除される。データポイントに付随するタイムスタンプは、UTC時刻であることが望ましく、データの更新間隔は最短1分となっている。

開発環境か本番環境か、実行しているリージョンはどこかなど、同じMetricsであっても環境によってデータが異なる。同一名称のMetricsの中から特定のMetricsを一意に識別するためにDimentionと呼ばれる名前と値のペアを持つことができる。CloudWatchは、ソースが異なっていてもNameSpaceとMetricsが同一のMetricsは1つのMetricsとして扱う

データポイントの種類 保持期間
60秒間隔未満 3時間
1分 15日間
5分 63日
1時間 455日

アラームを使用することで、単一のMetricsを監視し、一定期間における閾値とMetrics値に応じてアクションを実行することができる。アクション機能を用いることで、各アラームに対する通知やAutoRecovery, AutoScaleなどのアクションを規定することも可能である。

CloudWatch Agent

統合 CloudWatch エージェントを使用することで、EC2やオンプレミスサーバからより多くのメトリクスを取得することができる。例えば、通常のCloudWatchでは計測できないサーバのメモリ量ディスク量などのメトリクスを追加で取得できる。また、メトリクスだけでなくサーバ内のログを収集して、CloudWatch Logsに送信することもできる。

EC2には、デフォルトではCloudWatchエージェントはインストールされていないため、Systems ManagerやCloudFormationなどを用いて追加でインストールする必要がある。なお、過去にはCloudWatch Logs エージェントが使用されていたが、現行バージョンは統合 CloudWatch エージェントであるため、特段の理由がなければ統合 CloudWatch エージェントを使用する。

WKWebViewの使い方

WKWebView

WKWebViewはiOS8から新たに使用できるようになったWebViewで、WebKitFrameworkの中に含まれている。これまでのUIWebViewと比べると速くて高機能とのこと。使用方法は以下の通り。

WKWebViewの初期化

最初に初期化しておく。

private var webView = WKWebView()

各種設定

AutoLayout、Delegateなどの設定をviewDidLoad()で行う。デフォルトではジェスチャーが無効なので、有効にするためにはコードで明示する必要があることに注意が必要である。

    override func viewDidLoad() {
        // Autolayoutを設定
        webView.translatesAutoresizingMaskIntoConstraints = false
        // 親ViewにWKWebViewを追加
        self.view.addSubview(webView)
        // Delegateの設定
        self.webView.UIDelegate = self
        self.webView.navigationDelegate = self
        // WKWebViewを最背面に移動
        self.view.sendSubviewToBack(webView)
        // レイアウトを設定(後述)
        setWebViewLayoutWithConstant(0.0)
        // ジェスチャーを許可
        webView.allowsBackForwardNavigationGestures = true
        // ページのロード
        self.webView.loadRequest(NSURLRequest(URL: NSURL(string: url)!))
    }

終了時

親ビューから削除する。Autolayoutの値もリセットされる。

    override func viewDidDisappear(animated: Bool) {
        webView.removeFromSuperview()
    }

レイアウト設定

WKWebViewは、StoryBoard上で割り付けることが出来ない。他のパーツをStoryBoard上でAutolayout設定している場合は、Swift上でWKWebViewにAutolayout設定を行う。ConstraintはWebViewに対して指定するのではなく、親Viewに対して指定することに注意が必要である。

    func setWebViewLayoutWithConstant(constant: CGFloat){
        // Constraintsを一度削除する
        for constraint in self.view.constraints {
            let secondItem: WKWebView? = constraint.secondItem as? WKWebView
            if secondItem == self.webView {
                self.view.removeConstraint(constraint)
            }
        }
        // Constraintsを追加
        self.view.addConstraint(NSLayoutConstraint(item: self.view, attribute: NSLayoutAttribute.Width, relatedBy: NSLayoutRelation.Equal, toItem: webView, attribute: NSLayoutAttribute.Width, multiplier: 1.0, constant: 0.0))
        self.view.addConstraint(NSLayoutConstraint(item: self.view, attribute: NSLayoutAttribute.CenterX, relatedBy: NSLayoutRelation.Equal, toItem: webView, attribute: NSLayoutAttribute.CenterX, multiplier: 1.0, constant: 0.0))
        self.view.addConstraint(NSLayoutConstraint(item: self.topLayoutGuide, attribute: NSLayoutAttribute.Bottom, relatedBy: NSLayoutRelation.Equal, toItem: webView, attribute: NSLayoutAttribute.Top, multiplier: 1.0, constant: 0.0))
        self.view.addConstraint(NSLayoutConstraint(item: self.bottomLayoutGuide, attribute: NSLayoutAttribute.Top, relatedBy: NSLayoutRelation.Equal, toItem: webView, attribute: NSLayoutAttribute.Bottom, multiplier: 1.0, constant: constant))
    }

以上により、ViewController上でWKWebViewを表示させることができる。goBack()やreload()はUIWebViewと同様に使用できる。

戻るボタンの実装

前のページに戻るためには、goForward()ではなぜかうまく動かなかった。以下のようにするとうまく動く。

let url = webView.backForwardList.forwardItem?.URL
webView.loadRequest(NSURLRequest(URL: url!))

プログレスビューの実装

ページ読み込みの進捗状況を表示するプログレスビューを表示することもできる。プログレスビュー自体のレイアウトや表示位置は、StoryBoard上で設定し、表示処理をSwiftで記述すると簡単である。

    // プログレスビュー描画間隔
    private let timerDur = 0.1

    override func viewDidAppear(animated: Bool) {
        // プログレスビューの描画
        progressView.setProgress(0.0, animated: false)
        progressView.hidden = false
        timer = NSTimer.scheduledTimerWithTimeInterval(timerDur, target: self, selector: "updateProgressView", userInfo: nil, repeats: true)
    }

    override func viewDidDisappear(animated: Bool) {
        if timer?.valid == true {
            timer?.invalidate()
        }
    }

    /**
        プログレスビューを更新する
    */
    func updateProgressView() {
        progressView.setProgress(Float(webView.estimatedProgress), animated: true)
        if webView.estimatedProgress == 1.0 && timer?.valid == true {
            timer?.invalidate()
            progressView.hidden = true
        }
    }

新規タブで開く

WKWebviewでURL読み込み時に任意の処理を実行したい場合には、decidePolicyForNavigationAction関数内に処理を記述する。新規タブで開く(target = _blank)の指定があった場合にデフォルトでは動作しないので、以下の記述が必要である。

if navigationAction.navigationType == WKNavigationType.LinkActivated{
                if targetFrame == nil {
                    webView.loadRequest(NSURLRequest(URL: url))
                }
            }

‘openssl/pkcs7.h’ file not found への対応

‘openssl/pkcs7.h’ file not found

XCode 7からOpenSSLライブラリを使用する場合に、‘openssl/pkcs7.h’ file not foundという警告が出ることがある。これはOpenSSLのライブラリがプロジェクト内に不足しているためで、XCode7でOpenSSLを使用する場合は、CocoaPodsからOpenSSLをダウンロードして使用すると良い。

CocoaPodsのインストール

  • まずは、Xcodeバージョン管理ツールの1つであるCocoaPodsをMacにインストールする。

gemのアップデート

sudo gem update --system

CocoaPodsのインストール

sudo gem install cocoapods
    ERROR:  While executing gem ... (Errno::EPERM)
        Operation not permitted - /usr/bin/xcodeproj
  • そこで、インストールフォルダを/usr/local/binに変更してインストールする
sudo gem install -n /usr/local/bin cocoapods

CocoaPodsのセットアップ

pod setup

以上で、XCodeプロジェクト管理ツールCocoaPodsのインストールが完了する。

OpenSSLのインストール

CocoaPodsはPodfileと呼ばれる設定ファイルに、インストールの内容を設定することでインストールを実行することが可能である。XCodeでSSLを使用するには、CocoaPodsからOpenSSL-Classicをインストールする。

プロジェクトファイルに移動

cd PROJECT_DIRECTORY

Podfileを作成

source 'https://github.com/CocoaPods/Specs.git'
pod 'OpenSSL-Classic', '1.0.1.j'

OpenSSLライブラリをインストール

pod install
  • ここで当該のXcode Projectを開いていると以下のように警告されるので、Xcodeは閉じてからインストールする
[!] Please close any current Xcode sessions and use `XXXXX.xcworkspace` for this project from now on.

CocoaPodsからライブラリをインストールするとXXXXX.xcworkspaceというファイルが作成されるので、XXXXX.xcodeprojではなくXXXXX.xcworkspaceをクリックして起動する。また、ターゲットの設定で、HEADER_SEARCH_PATHSに$(inherited)を追加する。

UIActivityの使い方

UIActivityとは

UIActivityは、画面下からModal(CoverVertical)で出現する、FacebookやTwitterなどのSNS共有やメール添付の機能をアイコン一覧で表示するActivityである。任意の機能やアイコンを追加する場合は、UIActivityを継承したクラスを作成する必要がある。

UIActivity

カスタムUIActivityの作成

UIActivityを継承したクラスには、「表示するタイトル」や「アイコンイメージ」、「ボタンを押したときの動作」などを記述する。下の例は、「Safariで開く」というアイコンと機能をUIActivityに追加した場合のコードである。

import UIKit

class SafariActivity: UIActivity {

    var url: NSURL? = nil

    // 表示するタイトル
    override func activityTitle() -> String? {
        return "Safariで開く"
    }

    // 表示するアイコン    
    override func activityImage() -> UIImage? {
        return nil
    }

    // どのようなアイテムが投入された場合に機能が働くかを定義する
    // 機能が動作する条件の場合には true を返す
    override func canPerformWithActivityItems(activityItems: [AnyObject]) -> Bool {
        for activityItem in activityItems {
            // NSURLかつURLを開くことが出来る場合は動作する
            if activityItem.isKindOfClass(NSURL) && UIApplication.sharedApplication().canOpenURL(activityItem as! NSURL) {
                    return true
            }
        }
        return false
    }

    // 機能が動作する直前の処理を記す    
    override func prepareWithActivityItems(activityItems: [AnyObject]) {
        for activityItem in activityItems {
            if activityItem.isKindOfClass(NSURL){
                url = activityItem as? NSURL
            }
        }
    }

    // 機能のふるまいを記述する
    override func performActivity() {
        // SafariでURLを開く
        UIApplication.sharedApplication().openURL(url!)
        self.activityDidFinish(true)
    }

}

上記機能を含んだUIActivityを表示するには、以下のように定義したクラスを呼び出す。

// Data Item
let items = [NSURL(string: webView.URL!.description)!]
// Application Activity
let activities = [SafariActivity()]
// 上記2つを引数に指定しUIActivityViewControllerを生成
let activityController :UIActivityViewController = UIActivityViewController(activityItems: items, applicationActivities: activities)
UIActivityViewControllerを表示
self.presentViewController(activityController, animated: true, completion: nil)

以上でカスタムのUIActivityを表示することが可能となった。

AWS Kinesis(5)Kinesis Client Libraryでマルチスレッド処理を行う

マルチスレッド処理

AWSが公開しているKinesis Client Library(KCL)のサンプルプログラムでは、取得した各レコードをシングルスレッドで順次処理している。しかしこれでは、前のRecord処理が終了しないと次のRecord処理が実行できないため、Kinesis Client LibraryのDEFAULT_MAX_RECORDの値を上げたとしても性能が十分出ない。

そこで、Record処理を単純にRunnaleなどでマルチスレッド化してしまうとスレッド数が制御できなくなり、例えばKinesis Recordから取得したデータをDynamoDBに順次書き込むという制御を記述していた場合は、

com.amazonaws.http.AmazonHttpClient executeHelper
INFO: Unable to execute HTTP request: Timeout waiting for connection from pool
org.apache.http.conn.ConnectionPoolTimeoutException: Timeout waiting for connection from pool

など、DynamoDBにアクセスするためのHTTPのリソースが枯渇してエラーが発生し、Record処理に漏れが生じてしまう。そこで、ExecutorServiceを用いてスレッド数を制御しながらマルチスレッド化する。

    /**
     * Process records performing retries as needed. Skip "poison pill" records.
     *
     * @param records Data records to be processed.
     */
    private void processRecordsWithRetries(List<Record> records) {
        ExecutorService exec = Executors.newFixedThreadPool(NUM);
        try {	        
            for (Record record : records) {
                boolean processedSuccessfully = false;
                for (int i = 0; i < NUM_RETRIES; i++) {
                    try {
                        // スレッドタスクを実行
                        exec.submit(new My_Method(this, record));
                        // 略...
                    }
                }
            }
        } finally {
            // スレッドタスクを終了
            exec.shutdown();
            if(!exec.awaitTermination(60, TimeUnit.SECONDS)){
                exec.shutdownNow();
            }
        }
    }

ExecutorServiceは、スレッドプールを用いて、マルチスレッドタスクを管理しながら実行できる仕組みで、submit()によりタスクが生成されて、ブロッキングキューに挿入され、shutdown()もしくはshutdownNow()メソッドにより処理を終了させることができる。ExecutorServiceを用いると生成するスレッド数を指定できることから、無尽蔵にスレッドが生成される心配がない。

ExecutorServiceは、終了処理を必ず明示的に実装しておく必要があるサービスである。生成済みのタスクは、shutdown()メソッド実行後も処理が継続されるが、shutdownNow()メソッドの場合は、強制的に処理がキャンセルされる。すなわち、shutdown()メソッド実行を実行したあとも、処理が継続したままである場合があることに注意が必要である。したがって、上記のように、shutdown()メソッドを実行した後に、awaitTermination()メソッドによりタイムアウトの時間を設定しておき、この時間を超えても処理が継続している場合には、shutdownNow()メソッドで強制的に処理を終了させるという実装にすることが望ましい。

ExecutorServiceで設定したスレッド数(NUM)が多すぎると以下のエラーが発生するため、スレッド数の上限を設定する際は注意が必要である。

java.lang.OutOfMemoryError: unable to create new native thread 

プログラムがどれほどスレッドを消費しているかは、以下のコマンドで確認が可能である。

ls -l /proc/[プログラムのプロセスNo.]/task | wc -l

また、Linuxの最大スレッド数は、

cat /proc/sys/kernel/threads-max

ユーザ1人あたりの制限は、

ulimit -a

で確認することが可能である。

AWS EC2(3)AMIとインスタンス

AMI

AMI(Amazon Machine Image)は、EC2インスタンスの起動に必要な情報を提供するイメージファイルで、

  • OSやアプリケーションなどのルートボリュームのテンプレート
  • 起動を許可するAWSアカウント
  • インスタンスにアタッチするボリュームのマッピング

などの情報が含まれている。

AMIは、AWSや様々な組織が公開しており、これを利用することができる。特にAmazon Linuxは、AWSが提供しているRHEL系のAMIで、AWS CLI Amazon EC2 API 等がパッケージに含まれているため便利で使い勝手が良い。また、自身が作成したインスタンスから生成したAMI(カスタムAMI)を登録/利用することもできる

なお、AMIは同一リージョン内や別のリージョンにコピーを作成することや、他のAWSアカウントとイメージを共有することが可能である。別リージョンにバックアップシステムを構築する場合、AMIのコピーはシステム構築の有用な手段となる。AMIの共有は、公開範囲をパブリックに指定するか、「イメージパーミッションの変更」設定から、許可するAWSアカウントの追加を行う。他者が公開しているAMIを利用する際は、機密データが第3者に送信されていないかや、認証情報が事前に設定されていないかなどを十分に検証する必要がある。

リージョンをまたがるコピー

作成したAMIを別リージョンで使用する際は、AWS CLIのリージョン設定やプログラムのデフォルトリージョン設定をハードコーディングせずに、シェルスクリプト等で変更できるようにしておくと良い。自身のリージョン情報を取得するには、AWS CLIから以下のコマンドを入力する。

 $ curl -s https://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/.$//g' 

AMIの作成

既存のインスタンス、もしくはスナップショットからAMIを作成することが可能である。AMIの作成は、インスタンス上のすべての動作を停止し、作成プロセス中に一貫した状態が保たれるようにするために、インスタンスをシャットダウンしてから行う。自身で作成したAMIを共有する場合は、以下のように、共有キーやコマンド履歴の削除等の対策を行う。

$ sudo shred -u /etc/ssh/*_key /etc/ssh/*_key.pub
$ shred -u ~/.*history
$ history -c
$ exit

インスタンスからの Linux AMI の作成

ストレージ

ストレージは以下の2種類が存在する。どちらのデバイスから起動させるか選択することも可能であるが、高速で永続的なEBS-basedの起動方法が奨励されている。

Instance Store

  • EC2と不可分の内蔵ディスクで、EC2をTERMINATEするとクリアされる。ただし、再起動ではクリアされない
  • インスタンス停止(STOP)することができない。
  • 無料

instance store-backed のインスタンス

Elastic Block Store

  • ネットワークで接続されたディスクで、EC2とは独立しており、別途課金される。同じアベイラビリティゾーン内でレプリケーションされるため、高い可用性を有している。
  • デフォルトでは、DeleteOnTermination フラグが true に設定されているので、TERMINATEすると消去されてしまうことに注意が必要である。消去されたボリュームは、ゼロで上書きされて他のアカウントで使用される。機密データを有している場合は、暗号化の検討が必要である。
  • スナップショットを取ることが可能である。スナップショットは、過去のスナップショットとの差分として記録される。スナップショットを取得する際には、EBSをアンマウントすることが望ましい。一方で、スナップショットが開始されれば、スナップショット取得処理中であってもEBSをアタッチして使用して問題ない
  • インスタンスとEBSボリュームはネットワーク経由で接続されているため、他の通信の影響を受ける。EBS最適化インスタンスを利用することで、インスタンスとEBSとの間のスループットが保証され、他の通信の影響を受けない。

Amazon EBS-backed インスタンス

容量と性能

汎用SSD(gp2)のパフォーマンスは、ボリュームサイズに比例する。つまり、小さい容量のEBSの場合は、I/O性能も低い。したがって、高頻度のI/Oが発生する可能性があるアプリケーションが動作する場合などは、使用する容量が低くても、I/Oの性能を向上させるために、大きな容量を設定しておく必要がある。

各ボリュームは、最初に540万I/Oクレジットを受け取り、標準性能を超えたI/O処理(バースト)は、このクレジットから消費されていく。一方、標準性能を下回るI/Oであった場合はクレジットに加算されていく。ただし、加算できるクレジットは、初期値と同じ540万I/Oクレジットまで。クレジットが枯渇するとバースト性能を利用できなくなり、標準性能で頭打ちとなる。なお、汎用SSD(gp2)は、

  • 容量が33.4GB以下であれば、標準性能100IOPS
  • 容量が1000GB以下であれば、3000IOPSのバースト性能を利用可能
  • 容量が3333GBに標準性能10000IOPSに達して、容量16TBまでこの性能を維持

と定義されている。また、スループットは、170GB以下では128 MB/秒170GBを超えると160MB/秒である。

インスタンス

概要

  • EC2は、1500MTUに加えてジャンボフレームをサポートする。
  • ルートデバイスがEBSの場合は、インスタンスタイプを変更することが可能である。このときインスタンスIDは変更されない。パブリックアドレスは変更される。インスタンスタイプを変更する場合は、インスタンスを一度停止する必要がある。

インスタンスのライフサイクル

インスタンスは、以下のライフサイクルで動作する。インスタンスがStopのときは課金されない。ただし、EBSボリュームに対する課金は継続される。時期やタイミングによっては、EC2のリソースが枯渇し新たなインスタンスが起動できない場合もあるので、リソースを確保しておきたい場合は、インスタンスを作成し、使用するまで停止しておくとよい。

インスタンスのライフサイクル

インスタンスを再起動した場合は、同一ハードウェアで起動し、ボリュームやネットワーク設定等は前回起動の情報を保持する。一方、インスタンスを一度停止し起動した場合は、別のハードウェアで起動し、これらの情報は引き継がれない。ハードウェア障害が発生した場合など、明示的に別のハードウェアでインスタンスを起動する必要がある場合には、再起動ではなく停止と開始を行う

インスタンスの公開鍵

EC2に使用するSSHキーペアは、リージョンごとに別管理であるため、他のリージョンでEC2を使用する場合、通常は別途新たにキーペアを作成する必要がある。ただし、これでは管理する際に問題が生じるので、共通のキーペアで運用すると良い。手元の秘密鍵から公開鍵を生成し、AWSマネージメントコンソールから登録を行う。

キーペアのインポート

インスタンスの時刻

デフォルトでは UTC(協定世界時間)時間帯に設定されている。他のタイムゾーンを設定する場合は、起動時に以下を実行する。

vi /etc/rc.local
ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime

インスタンスの復旧

ハードウェア障害が発生した場合に、自動的に再起動を行うことが可能なCloudWatchアラームを作成することができる。復旧したインスタンスは、ボリュームやネットワーク設定等は前回起動の情報を保持する。自動復旧は、1インスタンスにつき1日3回までである。

また、AWSがインスタンスの再起動や停止/開始、メンテナンスを事前に予告して実施する場合がある。再起動には、自身でスケジューリング可能なインスタンスリブートと、強制的に再起動されてしまうシステムリブートがある。また、ネットワークや電源のメンテナンスが実施される場合もある。

インスタンスメタデータ

実行中のインスタンスのメタデータは、以下のURLから取得することが可能である。AMIのIDや、ホスト名、リージョン、ネットワーク設定、セキュリティグループ名などを取得することができる。

https://169.254.169.254/latest/meta-data/

AWS Cognito(5)スロットリング

スロットリングとは

AWSは各サービスにサービス制限(スロットリングを行っており、各アカウントごとに使えるリソースの上限が設定されている。例えば、EC2は初期状態では最大20台までしかインスタンスを起動できない設定となっている。フルマネージドのサービスに対してもこれらの制限が設定されており、「フルマネージド」という名称でありながら負荷に合わせて際限なくスケールするわけではない。また、サービスによっては、サービス制限一覧にこの上限値が明記されていないものもある。

上限値以上にリソースを使用する場合はサポートプランに加入の上、サポートに必要なリソース数とその理由を付けて上限値の引き上げや撤廃を申請する必要がある。上限値の変更が必要な理由が不明確であったり必要以上のリソースを要求すると、要望が叶えられないこともあるので注意が必要である。理由を明確にした上で申請する必要がある。

なお基本的にAWSのマネージドサービスは、「定常的な利用」や「一時的なサービスを簡単に作成するため」に提供するシステムであるという考え方のようで、バースト的なアクセスや複雑な処理、定常的に大きな負荷が掛かる処理については、EC2上にシステムを構築して利用すべきであるというのがAmazonの方針のようである。大きな負荷の掛かる処理の場合は、上限引き上げ申請するだけでなく、EC2を用いて実現できないかについても検討すべきであろう。

Cognitoのスロットリング

AWSのサービスは日々拡張されていっているので、スロットリングの値は随時変更されている可能性がある。その前提のもと現時点で、

  • Cognito Identity: 数百/毎秒程度
  • Cognito Sync : 数千/毎秒程度

でスロットリングされており、それ以上の負荷が掛かる可能性があったりそれ以上のリソースを必要とする場合は、サポートに上限引き上げの申請を行う必要があるようだ。また、申請を行ったとしても標準値の数倍程度までしか拡張できないようである。Cognitoは昨年の9月に東京リージョンにきたばかりのサービスであるので、用意されているリソースにも限りがあるのかもしれない。今後のリソース拡張に期待したい。

AWS EC2(2)使用する上で注意すること

EC2は、AWSマネージメントコンソールからクリック1つでインスタンスを起動できる、非常に便利なクラウドサービスである。一方でとても簡単に利用できることから、全ての制限が取っ払われ、自分の思い通りのまま自由にリソースを使用できるサービスだというような錯覚に陥りがちだ。EC2も元を辿ればデータセンターの中にある物理マシンである。使用する際にはいくつかの注意すべき点が存在する。

サービスで使用する際にTインスタンスは使わない

T2インスタンスは、AWSの公式説明によると「ベースラインを超えてバーストする能力がある CPU パフォーマンスのベースラインを提供する、バーストパフォーマンスインスタンスです」とある。しかし実際は、データセンター内の同一物理サーバ上に収納されている他のインスタンスが実験用途等で使用され、バースト上の負荷がそのインスタンスに掛かった場合は、自分のインスタンスにも影響が出る可能性がある。したがって商用サービス等の安定した運用が求められる利用方法の場合にTインスタンスを使用することはおすすめしない。

バックアップを用意しよう

インスタンスが落ちたり、リージョンごとサービスダウンする可能性もゼロではない。24時間365日稼動しているように見えるクラウドサービスにもダウンタイムは確実に存在する。少なくともアベイラビリティゾーンを分ける、可能であれば他のリージョンにもバックアップを取るなどの障害対策はしておこう。

古いインスタンスは使わない

仮想環境、仮想OSといえど元はデータセンターにある物理サーバである。古くなれば電源やHDDなどが故障するリスクも高まる。実際にハードウェアに異常が起こり、EC2のインスタンスが急に応答しない、インスタンスが落ちるなどの現象が発生することがあるようだ。したがって、古いインスタンスはなるべく使わず、M3を使うぐらいならM4を、C3を使うくらいならC4を使おう。ちなみにインスタンスに障害が発生した場合、Auto Recovery機能をONにしていれば、自動で再起動される。

リソースは枯渇する

仮想環境、仮想OSといえど元はデータセンターにある物理サーバである。用意しているリソース量が全て使用されればリソースは枯渇する。実際に東京リージョンでは、年末年始などの多くの企業がイベント利用を行うシーズンに、特定のインスタンスタイプが起動できないことがあるようだ。またリソースの枯渇だけでなく、メンテナンスが原因で新たなリソースが確保できないということもあり得る。どうしてもリソースの確保が必要であれば事前に確保しておき、実際に使用するまでインスタンスをStopしておくなどの対応が必要である。リザーブドインスタンスの利用も検討したい。

バージニアリージョンは諸刃の剣

バージニアリージョンは、AWS創業の地。新しいサービスが一番に投入されるリージョンでもあり、リソース量も他のリージョンと桁違いに大きい。しかし一方で施設の老朽化が進んでいるからか異常なほどサービスダウンが多い。日本でEC2を使ったサービスを展開をする際に、わざわざバージニアリージョンを使用するメリットはあまり無いが、リソース量が大きいためバックアップリージョンとして利用する価値はあるだろう。レイテンシーを気にするのであれば、リソース量は劣るが物理距離が近い、シンガポール等のアジアパシフィック地域のリージョンも候補に入れたい。

上限引き上げはお早めに

AWSのサービスには、それぞれサービス制限(スロットリング)が適用されており、例えば、EC2は初期状態では最大20台までしかインスタンスを起動できない設定となっている。上限値以上にリソースを使用する場合はサポートプランに加入の上、サポートに必要なリソース数とその理由を付けて上限値の引き上げや撤廃を申請する必要があるが、上限値の変更が必要な理由が不明確であったり必要以上のリソースを要求すると断られる場合もあるので、少なくとも処理に3営業日以上掛かることを見越して、早めに上限引き上げを申請しておいたほうがよいだろう。

Auto Scaleは過信しない

Auto Scaleはリソース量に合わせてインスタンス量を自動で調整してくれるとても便利なサービスである。しかし、閾値を超えたことを検知して新たなインスタンスを立ち上げるまで数分を要する。したがってバースト的な負荷には対応できない。瞬間的なアクセスが予想されるのであれば、Auto Scaleは使わずにあらかじめ手動で、一定数のインスタンスを立ち上げておこう。

CPU使用率が定常的に30%を超えるのであれば代替策を考えよう

CPU使用率が定常的に30%を超えるのであればもう1つ上のインスタンスに変更するか、同じインスタンスをもう1つ用意しよう。

性能の良いインスタンスで台数少なく運用する方がラク

どのような処理をするかにもよるが、低い性能のインスタンスを大量に並べるよりかは、性能の良いインスタンスを台数少なく並べたほうが運用がラクなことが多い。

EBSのボリュームサイズによってI/O性能は変化する

ディスク容量あまり使わないからと最小限のディスク容量しか確保していない状態で、大量のディスクアクセスが発生した場合に期待したディスクの読み書き性能が出ないことがある。汎用(SSD) ボリュームのパフォーマンスはボリュームサイズによって変化するように設計されており、ボリュームサイズが大きければ大きいほど、良いI/O性能が与えられる。一定量のディスクアクセスが発生する可能性がある場合は、1TBなど大きめのボリュームサイズを割り当てておくほうがよい。

とりあえずCPU利用率を見ていればOK

CloudWatchはいろいろな項目があるので、どの項目を確認すればEC2が正常であるのか判断に困るかもしれない。そんなときは取り敢えずはCPU使用率だけでも見ておこう。

「なんか遅い」というときは…

まずはCloudWatchを参照してサチっている項目を探そう
+ Tインスタンスを使用している場合は、CPUクレジットが枯渇していないか確認する
+ 汎用SSDを使用している場合は、ボリュームサイズとインスタンスに割り当てられたIOPSの上限を確認する
+ AZ間の通信が発生している場合は、AZ間のレイテンシが発生している場合がある。また、インスタンスタイプによるネットワーク帯域の上限も確認する。

あと、よくある質問をきちんと読む!
4年間SAをやった中で、よくされる10の質問と回答

そのほかに注意すること

  • インスタンスを停止状態から実行状態に移行するたびに 1 時間分のインスタンス時間が課金される
    • [追記] 2017年10月12日から秒課金がスタートしている。
  • インスタンス起動後に設定変更できない項目が存在する。VPCやサブネット、Roleなどは後から変更できない。Roleは使う予定がなくても取り敢えず作成しておいたほうが良い
    • VPC
    • サブネット
    • プライベートIP
  • Amazon Linux AMIには、SWAPは用意されていない。

AWS Lambda(2)LambdaでKinesis Recordを取得する

Kinesis Process Records

Kinesis Event 発生毎にLambdaを実行し、Kinesis Recordを取得することが可能である。AWS マネジメントコンソールから関数を作成する場合は、kinesis-process-recordを選択。

Kinesis関数の設定

Stream名など必要事項を記入しプログラムをアップロードすることでLambdaを実行することが可能となる。

lambda_kinesis_function_2

Eclipseで開発する場合は、EclipseのAWSプラグインから直接アップロードや設定を行うことも可能である。Eclipseプロジェクトを右クリックして、「Amazon Web Services」から「Upload function to AWS Lambda…」を選択、「アップロードするリージョン」と「関数名」を指定し、

Kinesis関数の設定

「Description」や「Role」、プログラム一式を格納する「S3バケット」を指定して「Finish」を押すとEclipseで作成したLambda関数がAWS上にアップロードされる。

Eclipse設定

JavaによるLambda関数の実装

RequestHandler<KinesisEvent, Object>を実装することで、Kinesisからデータを取得し処理を行うことが可能となる。1回の処理で複数のKinesis Recordを取得するのでfor文で廻してそれぞれのRecordの値を取得することができる。Kinesis Recordには、ApproximateArrivalTimestampというRecord入力時に自動付与されるタイムスタンプが存在し、この値を取得するためのgetApproximateArrivalTimestamp()というメソッドも用意されているが、現時点では値の取得に対応していおらず、このメソッドを実行してもnullしか返えらないので注意が必要である。

public class KinesisEventHandler implements RequestHandler<KinesisEvent, Object> {

    @Override
    public Object handleRequest(KinesisEvent input, Context context) {

        context.getLogger().log("Input: " + input);

        for(KinesisEventRecord rec : input.getRecords()) {
        	// Recordを取得
        	Record record = rec.getKinesis();
        	// JSONを取得
        	byte[] byteArray = new byte[record.getData().remaining()];
        	record.getData().get(byteArray);
        	String json = new String(byteArray);
        	// タイムスタンプが取得できない
        	String timestamp = rec.getKinesis().getApproximateArrivalTimestamp();
        }
    }
}

Lambdaのスロットリング

Lambdaは、1つの関数につき1秒あたり同時処理数100でスロットリングされている。また、上限値の引き上げを申請しても1秒あたり同時接続数1000程度が限界のようである。したがって、Kinesisに大量のRecordが入力された場合はLambdaの上限を超えてしまいRecordを取りこぼしてしまう可能性がある。Kinesis Recordを確実に取得し処理する必要がある場合は、EC2上でKinesis Client Libraryを用いてKinesis Appを実装する必要がある。