AWS上でMongoDBを動かす

MongoDBに関する基本的な内容をまとめてみたものです。MongoDBに関する、Web上にすでにある良識な解説コンテンツをまとめたまとめサイトの抜粋です。
AWS上でMongoDBを動かす

AWSであれば、MongoDB環境を容易に構築できる

AWS MarketPlaceには、MongoDB環境を構築するために必要なミドルウェアがプリインストールされたOSイメージが多数あります。
※MongoDBの開発元である 10gen (現MongoDB Inc.)もMongoDB入りイメージを無償提供中

基本的に、それらのイメージをもとにAWSのインスタンスを立ち上げればMongoDB環境ができあがります。英文ですが、下記に具体的な構築手順があります。
http://www.mongodb.org/display/DOCS/AWS+Marketplace

AWSの複数ゾーンとレプリカを組み合わせればDRも容易

MongoDBは、マスタ/スレープ方式のレプリケーションに対応しています。

世界中のデータセンターから選択できる、AWSのさまざまなリージョンやアベイラビリティゾーンを複数選択してMongoDBのレプリカ機能を使用すれば、DR (Disaster Recovery)対応も可能です。

AWSは12のリージョンに、合計で33のアベイラビリティゾーンで運用されています (2016年5月現在)
https://aws.amazon.com/jp/about-aws/global-infrastructure/
AWSのリージョンは、最低でも2つのAZ(アベイラビリティゾーン)をもっていて、それぞれのAZはお互いに地理的・電源的・ネットワーク的に分離されつつも、AZ間は高速専用線で接続されています。

一方のゾーンにMongoDBおよびMongoDBのArbiterインスタンスを立ち上げ、もう一方のゾーンにMongoDBインスタンスを立ち上げれば、片方のゾーンで地震等の災害があっても、データが残り、そのままMongoDBは機能し続けます。

シャードとレプリカを組み合わせたMongoDBクラスタ構成

MongoDBでは、基本的に、シャードを増やすことにより水平スケールさせ、レプリカにより可用性を実現します。各シャードをレプリカセットにすることにより、高い可用性を実現しながら、水平スケールさせた大規模なデータベースを構成することができます。

開発段階では、1 つのレプリカセットから始めることもできますし、本稼働中に 3 つのレプリカセットに移行することもできます。下記URLに、AWS上でMongoDB環境を構築するアーキテクチャ例が示されています。
http://docs.aws.amazon.com/ja_jp/quickstart/latest/mongodb/architecture.html
・図1: レプリケーション係数 3 の MongoDB リファレンスデプロイ
・図2: 3つのレプリカセット2方向シャーディングを使用するAWSのMongoDBクラスター

MongoDBをAWS上で動くようにするまでの手順

AWSにて、MongoDBを動くようにするまでの手順は、概略、下記の通り:

・EC2インスタンスおよびEBSボリュームを用意

・Amazon VPC をセットアップ

*Amazon VPC 内のプライベートサブネットとパブリックサブネット、NAT インスタンス、
セキュリティ グループ、IAM ロールなど、MongoDB のデプロイ中に必要な様々な
ネットワークリソースを作成

・Amazon EBS をセットアップして MongoDB を保存。

・MongoDB 設定をカスタマイズするオプションを指定した後、MongoDBを起動。

MongoDB のバージョン番号 (2.6 または 3.0)、レプリカセットの数 (1 または 3)、シャード数
(0、1、2、または 3)、インスタンスあたりのマイクロシャード数を選択することができます。

※より詳細は、下記を参照ください。
[AWS上の MongoDB] http://docs.aws.amazon.com/ja_jp/quickstart/latest/mongodb/deployment.html

続きを読む

MongoDBのアーキテクチャ

MongoDBに関する基本的な内容をまとめてみたものです。MongoDBに関する、Web上にすでにある良識な解説コンテンツをまとめたまとめサイトの抜粋です。
MongoDBのアーキテクチャ

MongoDBにおけるクラスタ構成

MongoDBでは、レプリケーション機能・シャーディング機能を提供しており、両者を組み合わせることにより分散コンピューティング環境にて、負荷分散された冗長構成のクラスタを構築することができます。
(アプリケーション側からは、クラスタ全体をひとつの論理DBとみなすことができます)

・レプリケーション構成においては、一般的な条件下では障害からの自動フェイルオーバー
・シャーディング構成においては、自動バランシング機能を備え、データをそれぞれのノードにできるだけ均等に配分するとともに、動的にノードの追加や削除にも対応

クラスタ構成要素
– primaryサーバ レプリケーションのマスタ。データのマスタを保持
– secondaryサーバ レプリケーションのテレーブ。プライマリ上のデータのコピーを保持
– arbiterサーバ 自動的にフェイルオーバーする際に新しいプライマリノードを選択・指示
– configサーバ シャーディングの構成情報や分散状況を保持
– mongosサーバ シャーディング構成にて、データリクエストに対するルーティングを実施

2方向シャーディングと3レプリカセットのクラスター

MongoDBで、レプリケーション・シャーディング機能を利用して、可用性と水平スケールを実現する構成を実現する場合、最小構成で2方向シャーディングと3つのレプリカセットの構成となります。

MongoDBでは、ひとつのレプリカセットから始めて、本稼働中に3つのレプリカセットに移行することも可能です。

複数のレプリカ構成の場合には、書き込みオペレーションはプライマリノードで行われ、セカンダリノードに非同期にレプリケートされます (読み取りオペレーションでセカンダリノードを選択する場合には、古いノードに注意する必要があります)

MongoDBでは各シャードをレプリカセットにすることができます。2方向シャーディングと3つのレプリカセットの構成の場合には、ひとつのシャードが3つのレプリカセットにレプリケートされます。
AWSを利用する場合には、それぞれのレプリカは別々のアベイラビリティーゾーンに格納されますので、データセンター単位での障害があった場合でも、別のアベイラビリティゾーンにあるレプリカに自動フェールオーバーされてMongoDBは機能し続けることができます。

※参考: MongoDB on AWS >> アーキテクチャ
http://docs.aws.amazon.com/ja_jp/quickstart/latest/mongodb/architecture.html

続きを読む

MongoDB – OOS ドキュメント指向 noSQL DB

MongoDBに関する基本的な内容をまとめてみたものです。MongoDBに関する、Web上にすでにある良識な解説コンテンツをまとめたサイトの抜粋です。
MongoDB – OOS ドキュメント指向 noSQL DB

結果整合性保証モデルとし、高い可用性と水平スケールを実現

MongoDBは、「結果整合性」しか保証しないことにより、水平スケールさせることを目指した、分散コンピューティング環境にマッチしたデータベース。

結果整合性:
誰かがデータを更新し、そのデータが複製されるのに十分な時間が過ぎ、その後更新が加えられなければ、必ずその新しいデータにアクセスできる

つまり、MongoDBでは、データの更新リクエストが完了しても、分散環境にコピーされている全データの更新が終了しているとは限らず、そのタイミングで外部からデータ読み込みをすると、更新後のデータを読み出せるとは限りません。

従って、MongoDBでは、トランザクション処理を行わせることはできません。また、テーブルのJOIN操作もできません (複数のドキュメントをDB側で結合できない)。

しかし、この完全には一貫性を保証しないという割り切りは、RDBSでは実現することが難しいレベルでの水平スケールを可能とするだけでなく、複数のデータコピーを持たせることも容易にし、高い可用性も実現します。

ドキュメント指向のデータベース

MongoDBは、ドキュメント指向のデータベースであり、スキーマレス。つまり、あらかじめ定義された型にデータを登録するのではなく、さまざまな型のデータをそのまま登録していくことが可能です。

MongoDBでは、以下の概念でデータを操作します。

・DB (データベース)
・コレクション (Collection)
・ドキュメント (Document = Object)

「ドキュメント」と呼ばれる構造的データをJSONライクな形式で表現し、そのドキュメントの集合を「コレクション」として管理します(RDBのテーブルに対応)。コレクションはRDBMSのような固定的なスキーマを持ちません。ドキュメントには複雑な階層構造を持たせることもでき、それらの構造に含まれるフィールドを指定したクエリやインデクス生成も簡単な指定によって行えます。

RDBMSのように高度な結合操作を効率的に行うことはできませんが、データの追加・更新・削除・クエリは高速に行うことができます。

なお、MongoDBはスキーマを定義しなくてもよいのですが、パフォーマンス面を考えると、スキーマを定義することが多く、作りながらスキーマを定義・修正していくという柔軟な開発を可能とするデータベースといえます。

noSQLでありながら、多彩なクエリに対応

Mongoクエリ言語という、SQLライクな操作言語によりデータベースにアクセスします。

データ構造がDB -> コレクション -> Documentとなっていて、取り扱うDocumentデータがJSON形式のデータとはなりますが、JOINを除く、ほとんどのSQLを再現することができます。

また、変数・制御構造なども記述することができますので、集計等の高度なクエリも記述することが可能です。

[制御構造を使用した例]
MongoDBでは、JavaScriptの制御構造を使用することができます。たとえば、以下のようにforループでデータを挿入することができます。
for (var i = 1; i <= 20; i++) db.col1.insert( { x : 4 , j : i } )

レプリカセット: 高い可用性を実現できる仕組み

MongoDBは、マスタ/スレープ方式のレプリケーションに対応しています。簡単な仕組みで高可用性を実現することができます。

MongoDBではマスターのことをプライマリ,スレーブのことをセカンダリと呼びます。セカンダリは,プライマリのデータのコピーを保持します。レプリケーション構成としておけば、マスターノードに障害が起きても、自動フェイルオーバーの機能がありますので、データロストやサービス停止期間を最小にすることが可能です。MongoDBのレプリケーションの最小構成は,3つのノードが必要です。

また、レプリケーションを使用すれば,読み取り(Read)の負荷をノード間で分散させることができます。負荷のほとんどがReadで占められているようなアプリケーションの場合,Readの負荷をスレーブに分散させることによりシステムをスケールさせることが可能です。

オートシャーディング: DBデータを水平スケールさせる

シャーディングとは,データを複数のサーバに分散させる機能です。データを複数のサーバに分散させることにより,CPUやI/O負荷を分散させることが可能となります。

シャード:
実際にデータが格納されているmongodプロセス。1つのドキュメントは1つのシャードに格納され,シャード間でデータの複製は行わない

MongoDBのシャーディングはレンジパーティション方式を採用しています。シャードキーを指定することで,各サーバに格納されるデータの範囲が決定されます。サーバ間で重複データは持たず,1つのデータが格納されるサーバはシャードキーの範囲によって1つに決定されます。

キー範囲の調整と,調整に伴うサーバ間のデータの移動まで全部MongoDBが行う自動バランシングという機能を備えています。自動バランシングにより,サーバ間のデータの偏りをユーザが意識しなくてもよいように設計されています。また,新たにサーバを追加した場合も,同様にデータの移動を行い偏りがなくなるように自動調整します。

MongoDBを構成するコンポーネント

MongoDBは、マスタ/スレーブ方式のレプリケーション機能をもっていますが、MongoDBは自動フェイルオーバーを実装しており、マスタに障害が発生しても自動的にフェイルオーバーとリカバリが可能です。そのための構成要素として、MongoDBでは、マスターである「プライマリ」、フレーブである「セカンダリ」およびフェイルオーバーの際に新しいプライマリノードを選択する役割をはたす「アービター」というコンポーネントがあります。セカンダリは、プライマリのデータのコピーを保持し、アービターはデータのコピーは保持しません。自動的にフェイルオーバーするレプリカセットを構成するためには、最低でも三つのノードが必要となります。

プライマリ: レプリケーションのマスタ。データのマスタを保持

セカンダリ: レプリケーションのスレーブ。プライマリ上のデータのコピーを保持

アービター: 自動的にフェイルオーバーする際に新しいプライマリノードを選択

MongoDBのシャーディングはサーバ間のデータの移動を自動で行う自動バランシングの機能を備えていますが、そのデータ管理を行うために、configサーバとmongosサーバというコンポーネントがあります。

configサーバ:
シャーディングのメタデータを管理しているmongodプロセスです。単一障害点となるので,複数のconfigサーバで構成することが推奨されています。

mongosサーバ:
シャーディングにおけるルーティングプロセスです。シャードとクライアントを連携させます。必要があれば複数のmongosサーバをたてることが可能です。

続きを読む

NATゲートウェイの設定 / AWSのプライベートサブネットから、インターネットに接続する

◆ 今日やること

AWSのプライベートサブネットから、インターネットに接続する場合、NATゲートウェイが必要です。
プライベートサブネットに配置したEC2インスタンスからyum installとか実行する時に必要ですよね。

NATゲートウェイの設定がないと、インターネットに出て行けません。


$ sudo yum install -y mongodb-org
Loaded plugins: priorities, update-motd, upgrade-helper
amzn-main/latest                                                                          | 2.1 kB     00:00
amzn-updates/latest                                                                       | 2.3 kB     00:00
https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/3.2/x86_64/repodata/repomd.xml: [Errno 12] Timeout on https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/3.2/x86_64/repodata/repomd.xml: (28, 'Connection timed out after 30000 milliseconds')
Trying other mirror.

failure: repodata/repomd.xml from mongodb-org-3.2: [Errno 256] No more mirrors to try.

◆ 実装編

> NATゲートウェイの作成

サブネットの選択は、Publicサブネットにしましょう

NATゲートウェイの作成.png

> ルートテーブルの設定をします

0.0.0.0/0の送信先ターゲットを上記で作成したNATテーブルに変更します。

ルートテーブル.png

> 確認

AWSのプライベートサブネットから、インターネットに接続できるようになったかと思います。
以下を実行して、インストールが完了すればOKです。


$ sudo yum install -y mongodb-org

続きを読む

AWS EC2にMongoDBインストールとレプリケーション設定

MongoDBインストールとレプリケーション設定について、簡易的な抜粋メモとして書いています。
詳細に関しては、記事の最後の参考サイトを確認するようにしてください。

◆ 今日やること

  • MongoDBをEC2にインストール
  • レプリケーションの設定と確認
    • 今回せっていするレプリケーションの形式は、以下の図の通り、「Primary with Secondary Members」です。

mongorepl.png
引用元:Replication — MongoDB Manual 3.4

◆ バージョン

  • MongoDB 3.2
  • Linux 4.4.30-32.54.amzn1.x86_64 #1 SMP Thu Nov 10 15:52:05 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux

◆ 実装編

> EC2へのインストール

sudo yum update -y
sudo vim /etc/yum.repos.d/mongodb-org-3.2.repo

[mongodb-org-3.2]
name=MongoDB Repository
baseurl=https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/3.2/x86_64/
gpgcheck=1
enabled=1
gpgkey=https://www.mongodb.org/static/pgp/server-3.2.asc
sudo yum install -y mongodb-org
sudo service mongod start

sudo chkconfig mongod on

> 設定周り

  • WoredTigerストレージエンジンの主な設定

    • cacheSizeGB: システムメモリの6、7割
    • blockCompressor:デフォルトはsnappy(中間です)
/etc/mongod.conf
# Where and how to store data.
storage:
  dbPath: /data
  journal:
    enabled: true
  wiredTiger:
    engineConfig:
       cacheSizeGB: 1
       journalCompressor: snappy
    collectionConfig:
       blockCompressor: snappy

> EBSボリュームをAttach

  • EBSボリュームにmongoのデータを溜めるようにする。

Amazon EBS ボリュームを使用できるようにする – Amazon Elastic Compute Cloudに従って、EBSボリュームをアタッチする

  • パーミッションを変更するのを忘れないように。
sudo chown -R mongod:mongod /data
  • /etc/mongod.conf
/etc/mongod.conf
# Where and how to store data.
storage:
  dbPath: /data

> レプリケーションの設定

MongoDBではマスターのことをプライマリ,スレーブのことをセカンダリと呼びます。
MongoDBのレプリケーションの最小構成は,3つのノードが必要です。

  • ネットワークインターフェイスの設定で、レプリケーションを組むサーバのIPを記述しておくこと

レプリケーション設定前には、お互いに通信できることを確認しないといけません
Troubleshoot Replica Sets — MongoDB Manual 3.4

mongodb が listen するIPアドレスはデフォルトでは 127.0.0.1 に設定されており、ローカルからのみアクセスを許可するようになっています
mongod.conf の bind_ip に設定されたIPアドレスで listen するのでこれを変更することで外部からの接続を許可します
ここに 0.0.0.0 を設定すると全てのIPアドレスからの接続を許可します

/etc/mongod.conf
# network interfaces
net:
  port: 27017
  bindIp: [127.0.0.1,10.1.52.111,10.1.51.227,10.1.51.68]


  • レプリケーション名を決める
  • Oplogのサイジングと設定サイズを決める
/etc/mongod.conf
replication:
   oplogSizeMB: 500
   replSetName: testRepl

第4回 MongoDBのレプリケーションを構築してみよう:MongoDBでゆるふわDB体験|gihyo.jp … 技術評論社

OplogはCapped Collectionなので,作成時以外にサイズ変更することができません。 デフォルトのOplogのサイズは「1GBまたは,ディスクの空き領域の5%」
Staleを避けるために,Oplogのサイジングは非常に重要となります。
Oplogのサイズは,mongodの初回起動時にoplogSizeオプションで変更可能です。
Oplogの適切なサイズを見積もる方法のひとつに,本番を想定した書き込みテストを実施し,作成されたOplogのサイズを取得する方法があります。
1時間程度,本番想定と同程度の書き込みテストを行った後,以下のコマンドでレプリケーションの最新情報を取得します。

> db.getReplicationInfo()

1時間で作成されたOplogのサイズがわかれば,Oplogのサイジングの目安となります。
少なくとも8時間のセカンダリのダウンタイムに耐えられるようにしておくことが推奨されています。

  • レプリケーションの設定

どれかのサーバに入って、以下のコマンドを実行

config = {
  _id : "testRepl",
  members : [
    { _id : 0, host : "10.1.51.227:27017" },
    { _id : 1, host : "10.1.51.68:27017" },
    { _id : 2, host : "10.1.52.111:27017"} ] }

rs.initiate(config)
  • スレーブの読み取り専用動作設定

そのままだと、スレーブが読み取り専用としてアクセスできないので以下の設定を行っておく。

スレーブノードで、以下のコマンドを実行

 db.getMongo().setSlaveOk()
  • レプリケーションステータスの確認
testRepl:PRIMARY> rs.status();
{
    "set" : "connobaRepl",
    "date" : ISODate("2017-01-12T07:03:05.556Z"),
    "myState" : 1,
    "term" : NumberLong(6),
    "heartbeatIntervalMillis" : NumberLong(2000),
    "members" : [
        {
            "_id" : 0,
            "name" : "10.1.51.227:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 100310,
            "optime" : {
                "ts" : Timestamp(1484182286, 1),
                "t" : NumberLong(6)
            },
            "optimeDate" : ISODate("2017-01-12T00:51:26Z"),
            "electionTime" : Timestamp(1484104344, 1),
            "electionDate" : ISODate("2017-01-11T03:12:24Z"),
            "configVersion" : 3,
            "self" : true
        },
        {
            "_id" : 1,
            "name" : "10.1.51.68:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 100245,
            "optime" : {
                "ts" : Timestamp(1484182286, 1),
                "t" : NumberLong(6)
            },
            "optimeDate" : ISODate("2017-01-12T00:51:26Z"),
            "lastHeartbeat" : ISODate("2017-01-12T07:03:04.017Z"),
            "lastHeartbeatRecv" : ISODate("2017-01-12T07:03:04.020Z"),
            "pingMs" : NumberLong(0),
            "syncingTo" : "10.1.51.227:27017",
            "configVersion" : 3
        },
        {
            "_id" : 2,
            "name" : "10.1.52.47:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 99751,
            "optime" : {
                "ts" : Timestamp(1484182286, 1),
                "t" : NumberLong(6)
            },
            "optimeDate" : ISODate("2017-01-12T00:51:26Z"),
            "lastHeartbeat" : ISODate("2017-01-12T07:03:05.025Z"),
            "lastHeartbeatRecv" : ISODate("2017-01-12T07:03:05.022Z"),
            "pingMs" : NumberLong(2),
            "syncingTo" : "10.1.51.227:27017",
            "configVersion" : 3
        }
    ],
    "ok" : 1
}

> mongoログインの時の警告メッセージを消す方法

[ec2-user@ip-10-1-52-47 ~]$ mongo
MongoDB shell version: 3.2.11
connecting to: test
Server has startup warnings:
2017-01-11T03:10:18.308+0000 I CONTROL  [initandlisten]
2017-01-11T03:10:18.308+0000 I CONTROL  [initandlisten] ** WARNING: /sys/kernel/mm/transparent_hugepage/defrag is 'always'.
2017-01-11T03:10:18.308+0000 I CONTROL  [initandlisten] **        We suggest setting it to 'never'
2017-01-11T03:10:18.308+0000 I CONTROL  [initandlisten]
testRepl:SECONDARY>

◆ 参考サイト

続きを読む

秒間500件以上のLOGフローアーキテクチャ

今年の9月、社内でLOGのアーキテクチャの改善を計るチームに参加する事になりました。
その際に経験させていただいた、知見をここでまとめてみようと思います。

背景

チームが作られた経緯としては、社内のLOGアーキテクチャを利用している際、問題が発生したためです。
問題発生した際の簡単なLOGアーキテクチャ図です。

AWS Design (1).png

  • backendのmongodbがLOGの流量に耐えきれず落ちた。
  • log dispatcherまで派生し、他のbackend_serviceへのLOG流入にまで影響を及ぼした。
  • 各種log dispatcherサーバの設定時、backend毎に流量の微調整を行わなくては行けないので障害発生時やサーバ構築時の対応にてインフラチームの人員コストが増大していた。

今回の目標

上記問題をふまえて、今回のLOGアーキテクチャ検討チームの目標は

  • 秒間500件以上を各backendまで流し込むアーキテクチャ

    • 流量に応じて各種サービスのスケールアウトを行えば、数倍以上の速度を実現。
  • 各backendサービス毎に影響範囲を分けたアーキテクチャ
    • 一つのbackendが落ちても他のbackendへのフローに可能な限り影響を及ぼさない。
  • インフラ側のコスト削減
    • インフラチームの人員コスト(log dispatcherサーバ構築やtd-agentへの流量調整等の煩雑な作業)を削減。

LOGアーキテクチャ案 – 1st サーバレスアーキテクチャ –

上記の問題を解決するために検討された、LOGアーキテクチャ案がこちらです。

アーキテクチャ図

AWS Design.png

アーキテクチャ概要

  • 前提条件

    • LOG 1recordの平均容量は1KB。
    • 検証対象となる秒間件数は「総秒間処理レコード数1」。
    • 検証範囲としてはkinesis〜backendサービスの間つまりlambdaが担当するフロー部分全てを範囲とする。
    • kinesisの流入性能は目標秒間件数以上の性能を満たしているため検証範囲から外す。
  • LOGについて
    • 流入されるLOGはplayerの行動ログ。
    • LOG内にはplayerのidが入っている事を前提とする。
      • また、そのplayer_idは32文字の16進数文字列とする。
  • app serverについて
    • EC2インスタンスのm4.2xlargeを1つ利用しtd-agentサーバからkinesisへLOGを流し込む。
    • 「td-agent fluent-plugin-kinesis」のKPLを利用してkinesisに流し込む。
  • kinesisについて
    • Kinesis Streamsは1シャードに秒間1000recordもしくは秒間1MBまで受け付けられる。
    • kinesis Streamsのshard数は2つで検証を行う。
      • ただ、計測結果の内容は1lambda containerでの処理内容を計測する。
  • lambdaについて
    • 使用言語はpythonを使用。
    • lambdaの使用メモリは256MB。
    • kinesis streamsよりrecordsを取得するblueprint「kinesis-process-record-python」を利用して実装。
      • kinesisのシーケンス番号を独自に管理してくれる。
      • lambda内でエラーが発生した場合シーケンス番号を進めずリトライしてくれる機能も備わっている。
      • shard数を増やすとlambdaもshard数にあわせてlambda containerをスケールして起動する。
  • フロー解説
    1. app serverのtd-agentからkinesisへLOGを流し込む。
    2. lambdaが定期的にKinesisよりrecordを取得して各backendへ流し込む。
      • S3には取得したrecordをgzで圧縮して1起動lambda毎に1ファイル書き出す。
      • Bigqueryにはplayer_id毎のtable作成しplayerのLOGを流し込む。
        • player_id毎のテーブルには検索容量を削減し料金を低減させる目的がある。
        • 弊社各アプリのplayer情報全てを1テーブルに格納して検索するとコスト面がかかりすぎてしまうため分割する。
        • Bigqueryのdate-partitionedを利用して、teble内では日付毎にpartitionされている。
        • テーブルがplayer数だけ生成されるため、GCP社様に確認を行い「問題ない」旨の返答を頂いた。
  • 撤去したbackend
    • mongoDBでは、コストに見合ったインスタンスでは流量に耐えられないため撤去。

計測結果

実行lambdaのメモリ量は全て256MBです。

backend形式 最大メモリ 総平均時間1 平均時間2 平均間隔時間3 平均レコード数4 秒間処理レコード数5 総秒間処理レコード数6 エラー発生率(%)7
Bigquery 119MB 868.86秒 72.22秒 32.79秒 38件 3.10件/秒 0.04件/秒 85.7%
S3 156MB 6.35秒 6.08秒 0.46秒 4389件 721.24件/秒 691.11件/秒 0%

達成内容

  • S3への流入について1lambda(256MB)で約秒間500件以上を達成。

問題点

  • Bigqueryへ投げるリクエストに時間がかかっている。
  • Bigqueryへのcreate_tableが正常に終了した後数分間はinsertを行っても「Not found Table」のエラーが発生。

問題点の考察

Bigqueryへ投げるリクエストに時間がかかっている。

1lambda内で複数リクエストを投げているが、その部分は直列で処理を実行しており、並列で処理を実行する事でスループットをあげられる可能性があります。

Bigqueryへのcreate_tableが正常に終了した後数分間はinsertを行っても「Not found Table」のエラーが発生。

「create tableの性能があまり良くない」 or 「table生成にラグが発生する仕様」だと思われるため、tableを無制限に作成する方式では限界があると考えられます。テーブルの作成数を最適化する方法でこの問題に対処します。

LOGアーキテクチャ案 – 2nd gevent並列処理 –

「1st案」の問題点を元に続いての案を実行してみました。

アーキテクチャ図

Untitled.png

アーキテクチャ概要

  • geventを使ってBigqueryへのリクエストを並列処理。

    • 並列処理でリクエストを投げる事でlambda containerのパフォーマンスを最大限利用。
  • テーブルの仕様を変更。
    • player_id毎のテーブルでは無く、player_idを元に16分割したテーブルへデータを流し込む。

計測結果

実行lambdaのメモリ量は全て256MBです。

backend形式 最大メモリ 総平均時間1 平均時間2 平均間隔時間3 平均レコード数4 秒間処理レコード数5 総秒間処理レコード数6 エラー発生率(%)7
Bigquery 256MB 24.63秒 20.69秒 3.23秒 4078件 197.09件/秒 165.56件/秒 3.8%

達成内容

  • 1stよりも高速化を実現。

問題点

  • 秒間500件以上は未達成。

問題点の考察

秒間500件以上は未達成。

256MBのlambdaでpythonを利用し高速化を計る場合は、これが限界だと考えられます。
ただ、lambda container上で実行出来る実行ファイルもしくは共有ライブラリを利用する事で高速化を実現出来るかもしれません。

LOGアーキテクチャ案 – 3rd Golang共有ライブラリ –

アーキテクチャ図

Untitled.png

アーキテクチャ概要

  • golangを利用してbigqueryへrequestを投げる共有ライブラリを作成し利用。

    • インタプリタ言語とは異なるコンパイル言語であるgolangで作成した共有ライブラリを利用。
    • goroutineを利用して並列処理を行う。

計測結果

実行lambdaのメモリ量は全て256MBです。

backend形式 最大メモリ 総平均時間1 平均時間2 平均間隔時間3 平均レコード数4 秒間処理レコード数5 総秒間処理レコード数6 エラー発生率(%)7
Bigquery 158MB 11.11秒 10.50秒 0.57秒 4711件 448.25件/秒 424.01件/秒 0.3%

256MBでは秒間500件に後少しの所で達成出来ませんでした。

ただ、kinesis shard数を2本から1本へ変更すると

backend形式 最大メモリ 総平均時間1 平均時間2 平均間隔時間3 平均レコード数4 秒間処理レコード数5 総秒間処理レコード数6 エラー発生率(%)7
Bigquery 146MB 7.62秒 6.86秒 0.86秒 4640件 676.11件/秒 608.63件/秒 1.3%

目標秒間件数を満たす事が出来ました。
なぜこのような事が発生するのかは、別で記事にしたいと思います。

達成結果

  • 共有ライブラリを利用する事で高速化を実現し、限定的な条件ではあるモノの目標秒間件数を達成。
  • サーバレスアーキテクチャのため、インフラチームの運用コストも削減を実現した。

あとがき

この構成に関しては下記資料を参考にさせて頂きましたが、AWS以外のbackendサービスへの流入についての記事は見当たらなかったため、検証し記事にさせて頂きました。

今回の記事関連で聞いてみたい事がありましたら、何なりとコメント頂ければわかる範囲でお答えします。それでは、この記事を最後まで読んで頂きありがとうございました。

参考資料

https://speakerdeck.com/kanny/miao-jian-shu-mo-falseroguwoiigan-zinisuruakitekutiya

注釈


  1. (lambda総時間 + error総時間 + lambda実行間隔総時間) / lambda成功処理回数 

  2. 1起動lambdaあたり処理する平均処理時間 

  3. lambdaとlambdaの間の実行間隔の平均時間 

  4. 1起動lambdaあたり処理する平均record数 

  5. 平均レコード数 / lambda平均時間 

  6. 平均レコード数 / 総平均時間 

  7. エラー終了lambda回数 / 総実行lambda回数 

続きを読む