Taste of Tech Topics

Acroquest Technology株式会社のエンジニアが書く技術ブログ

Kubernetesクラスタ環境を構築してDashboardで見える化を試してみた

こんにちは。

ポケモンGOみなさんやってますか?私は開始1週間くらいで一度止めてしまったのですが、周りが続けているのに触発されて再開。先日ようやくレベル20まで上がりました。

どうも、@です。

さて、今回はKubernetesについてです。
最近Kubernetes(以下k8s)を使うと決めて、k8sの環境構築をしています。
その際、公式のドキュメントやさくらのナレッジさんの記事が非常に参考になったのですが、OSやk8sなどのバージョンが一部異なるため、いろいろと試行錯誤を重ねることになりました。

kubernetesによるDockerコンテナ管理入門 - さくらのナレッジ

Kubernetes - What is Kubernetes?

そこで、同じような環境構築をしている人に、一例として少しでも参考になればと思い、k8sクラスタ環境の構築~Dashboardのインストールまでをまとめました。

0. 環境と条件

  1. 物理マシンでMaster1台、Node2台を用意する。
  2. 各マシンのOSはCentOS 7.2を利用する。
  3. k8sのバージョンは1.2系を利用する。(yum installでインストール可能な版)
  4. プライベートなDocker Registryを立てる(Docker HubにイメージをPushしたくない)
  5. k8s-dashboardをインストールし、Webブラウザからクラスタ管理をできるようにする。

今回のMaster/Nodeの構成では、次のIPアドレスをそれぞれ設定しています。

No マシン IPアドレス
1 Master 192.168.98.47
2 Node1 192.168.98.48
3 Node2 192.168.98.49

またMasterとNodeに今回インストールするものをまとめると次のようになります。

No マシン インストール
1 Master Kubernetes, etcd, flannel, docker-registry
2 Node Kubernetes, flannel, docker

1. 共通の設定

k8sはノード間の通信をする際に、マシンのホスト名を利用するようです。そこで、各マシンには、/etc/hostsに自マシンのIPアドレスとホスト名を登録しておきましょう。(このホスト名は後々k8sの設定で使うことになります。)

192.168.98.47 master

2. Masterの設定

必要なサービスをインストールします。

$ sudo yum install kubernetes
$ sudo yum install etcd
$ sudo yum install flannel
$ sudo yum install docker-registry

※etcd、flannel、docker-registryは他サイトでいくらでも説明されていると思いますので、ここでは説明を割愛します。

2.1. k8s関連の設定

サービスのインストールが完了したら、各種設定ファイルを変更します。全部載せると長いので、変更点のみ載せています。まずはk8sの動作に関する設定ファイルの編集です。Masterサーバとして自マシンのIPアドレスを設定します。
[/etc/kubernetes/config]

#KUBE_MASTER="--master=http://127.0.0.1:8080"
KUBE_MASTER="--master=http://192.168.98.47:8080"

kubeletには、自マシンのホスト名(/etc/hostsに記載したもの)と、API Serverの設定を記述します。
[/etc/kubernetes/kubelet]

#KUBELET_HOSTNAME="--hostname-override=127.0.0.1"
KUBELET_HOSTNAME="--hostname-override=master"

#KUBELET_API_SERVER="--api-servers=http://127.0.0.1:8080"
KUBELET_API_SERVER="--api-servers=http://192.168.98.47:8080"

※このKUBELET_HOSTNAMEの設定が、/etc/hostsに最初に登録したホスト名と同じである必要があります。

次に、API Serverの設定です。今回はローカルでの通信を想定しているので、--insecure-bind-addressを用いてAPI Serverのアドレスは指定します。
[/etc/kubernetes/apiserver]

#KUBE_API_ADDRESS="--insecure-bind-address=127.0.0.1"
KUBE_API_ADDRESS="--insecure-bind-address=192.168.98.47"

#KUBE_ETCD_SERVERS="--etcd-servers=http://127.0.0.1:2379"
KUBE_ETCD_SERVERS="--etcd-servers=http://192.168.98.47:2379"

APIServerへのアクセス用にキーの作成も必要になるので、ここで準備しておきます。

$ sudo openssl genrsa -out /etc/kubernetes/serviceaccount.key 2048

[/etc/kubernetes/apiserver]

#KUBE_API_ARGS=""
KUBE_API_ARGS="--service_account_key_file=/etc/kubernetes/serviceaccount.key"

[/etc/kubernetes/controller-manager]

#KUBE_CONTROLLER_MANAGER_ARGS=""
KUBE_CONTROLLER_MANAGER_ARGS="--service_account_private_key_file=/etc/kubernetes/serviceaccount.key"

これでk8sの設定は完了になります。

2.2. etcdの設定

次はetcdの設定を行います。configファイルでetcdが外部からのアクセスを許可するように編集します。

[/etc/etcd/etcd.conf]

#ETCD_LISTEN_CLIENT_URLS="http://localhost:2379"
ETCD_LISTEN_CLIENT_URLS="http://0.0.0.0:2379"

#ETCD_ADVERTISE_CLIENT_URLS="http://localhost:2379"
ETCD_ADVERTISE_CLIENT_URLS="http://0.0.0.0:2379"

2.3. Dockerの設定

最後にDockerの設定になります。今回Docker Registryはローカル通信で行うので、INSECURE_REGISTRYの設定が必要になります。またREGISTRYとしてMasterサーバのIPアドレスを指定します。
[/etc/sysconfig/docker]

#ADD_REGISTRY='--add-registry 127.0.0.1:5000'
ADD_REGISTRY='--add-registry 192.168.98.47:5000'

# INSECURE_REGISTRY='--insecure-registry'
INSECURE_REGISTRY='--insecure-registry 192.168.98.47:5000'

ここまでで主な設定は完了になります。

etcdctlでflannelが利用するネットワーク周りの設定をしておきます。
[注意]この設定はCentOS 7.2用の設定です。よく他のページで紹介されている「/coreos.com/network/config」というパスは、CoreOS用の設定になります。

$ sudo etcdctl mk /atomic.io/network/config '{"Network":"172.17.0.0/16"}'

2.4. 関連サービスの起動とサービス登録

ファイルの更新が完了したら、サービスの立ち上げと登録を行います。

$ sudo systemctl restart kube-apiserver
$ sudo systemctl restart kube-controller-manager
$ sudo systemctl restart kubelet
$ sudo systemctl restart flanneld
$ sudo systemctl restart etcd
$ sudo systemctl restart docker
$ sudo systemctl restart kube-scheduler
$ sudo systemctl restart kube-proxy
$ sudo systemctl enable kube-apiserver
$ sudo systemctl enable kube-controller-manager
$ sudo systemctl enable kubelet
$ sudo systemctl enable flanneld
$ sudo systemctl enable etcd
$ sudo systemctl enable kube-scheduler
$ sudo systemctl enable kube-proxy

3. Nodeの設定

まず必要なモノのインストールです。

$ sudo yum install kubernetes
$ sudo yum install docker
$ sudo yum install flannel

3.1. k8s関連の設定

次にk8s関連の設定ファイルの編集です。Master-Node間の通信のため、KUBE_MASTERにはMasterサーバのIPアドレスを指定します。
[/etc/kubernetes/config]

#KUBE_MASTER="--master=http://127.0.0.1:8080"
KUBE_MASTER="--master=http://192.168.98.47:8080"

対象となるマシンのKubeletのIPアドレスを指定します。またKUBELET_HOSTNAMEには/etc/hostsに指定した名前を使ってください。KUBELET_API_SERVERにはAPI Serverをインストールしたマシン(今回の場合はMasterのマシン)のIPアドレスを記述します。
[/etc/kubernetes/kubelet]

#KUBELET_ADDRESS="--address=127.0.0.1"
KUBELET_ADDRESS="--address=192.168.98.48"

#KUBELET_HOSTNAME="--hostname-override=127.0.0.1"
KUBELET_HOSTNAME="--hostname-override=node1"

#KUBELET_API_SERVER="--api-servers=http://127.0.0.1:8080"
KUBELET_API_SERVER="--api-servers=http://192.168.98.47:8080"

※192.168.98.47はMasterのIPアドレス、192.168.98.48はNodeのIPアドレスになります。

3.2. flannelの設定

k8sのMaster-Node間の通信を実現するためにFlanneldを利用しています。FLANNELD_ETCDには、ETCDをインストールしたマシンのIPアドレスを記述します。
[/etc/sysconfig/flanneld]

#FLANNEL_ETCD="http://127.0.0.1:2379"
FLANNEL_ETCD="http://192.168.98.47:2379"

3.3. Dockerの設定

最後にDockerの設定になります。こちらにも、INSECURE_REGISTRY、ADD_REGISTRYの設定が必要になります。
[/etc/sysconfig/docker]

#ADD_REGISTRY='--add-registry 127.0.0.1:5000'
ADD_REGISTRY='--add-registry 192.168.98.47:5000'

# INSECURE_REGISTRY='--insecure-registry'
INSECURE_REGISTRY='--insecure-registry 192.168.98.47:5000'

3.4. 関連サービスの起動とサービス登録

ファイルの更新が完了したら、サービスの立ち上げと登録を行います。

$ sudo systemctl restart kubelet
$ sudo systemctl restart flanneld
$ sudo systemctl restart docker
$ sudo systemctl restart kube-scheduler
$ sudo systemctl restart kube-proxy
$ sudo systemctl enable kubelet
$ sudo systemctl enable flanneld
$ sudo systemctl enable etcd
$ sudo systemctl enable kube-scheduler
$ sudo systemctl enable kube-proxy

同じ設定をもう一台のNodeマシンでも実施してください。(ホスト名とIPアドレスはマシンに合わせて変更してください)

4. 設定結果の確認

設定が完了したら、うまくMasterがNodeを認識しているか確認しましょう。kubectlコマンドをMasterマシンで実行すると確認ができます。

$ kubectl get nodes
NAME      STATUS    AGE
master    Ready     3d
node1     Ready     2d
node2     Ready     2d

上記のように出力されれば、一旦k8sの基本的な環境の設定は完了になります。

5. ダッシュボードのインストール

次にダッシュボードをインストールします。今回の環境はdokcer-registryをMasterに立てており、

  1. 開発環境でDockerイメージを作成
  2. Masterのdocker-registryにイメージをPush
  3. k8sにデプロイする際はNodeがdocker-registryからイメージをPull

という流れになります。

kubernetes-dashboardはdocke hubにimageが存在するため、次のように、開発環境にpullをしてきて、そのimageをMasterのregistryにpushします。
では、まずk8s-dashboardのローカルへのイメージの登録です。

$ docker pull gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.0
$ docker tag gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.0 192.168.98.47:5000/kubernetes-dashboard-amd64:v1.4.0
$ docker push 192.168.98.47:5000/kubernetes-dashboard-amd64:v1.4.0

これでDocker側の準備は整いましたので、k8s側でダッシュボードを作成します。すでにyamlファイルが公開されているので、そちらを取得し、中身をローカル用に変更してから作成します。

https://raw.githubusercontent.com/kubernetes/dashboard/master/src/deploy/kubernetes-dashboard.yaml

上記のファイルの中身を更新します。

        #image: gcr.io/google_containers/kubernetes-dashboard-amd64:v1.4.0
        image: 192.168.98.47:5000/kubernetes-dashboard-amd64:v1.4.0

        # - --apiserver-host=http://192.168.98.47:8080
          - --apiserver-host=http://192.168.98.47:8080

(最下に追記)
  externalIPs:
  -  192.168.98.47

このファイルをkubectlで読み込ませてダッシュボードを作成します。ダッシュボードはkube-systemというnamespaceを利用するので、事前にnamespaceを作成します。

$ kubectl create ns kube-system
$ kubectl create -f kubernetes-dashboard.yaml

以下のコマンドでdashboardのSTATUSがRunningになっていることを確認します。

$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                    READY     STATUS    RESTARTS   AGE
kube-system   kubernetes-dashboard-4061540060-9gzk0   1/1       Running   0          2d

6. 結果の確認

問題なくdashboardのインストールができれば、以下のアドレスからダッシュボードが確認できるはずです。
http://192.168.98.47:8080/ui
f:id:acro-engineer:20161004083220p:plain

7. まとめ

今回k8sクラスタ環境を構築して、ダッシュボードのインストールを行いました。同じように嵌ってしまってなかなかインストールが進まない人は、ぜひ参考にしてみてください。また、よりよいやり方があるなど知っている人がいましたら、教えてもらえるとうれしいです。


それでは!

Acroquest Technologyでは、キャリア採用を行っています。


  • 日頃勉強している成果を、AWSHadoop、Storm、NoSQL、Elasticsearch、SpringBoot、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
  • 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
  • OSSの開発に携わりたい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
 
データ分析で国内に新規市場を生み出す新サービス開発者WANTED! - Acroquest Technology株式会社の新卒・インターンシップ - Wantedlywww.wantedly.com

PyConJP2016に参加してきました!

こんにちは、@です。

なんと、私、先日PyConJPに参加してきました!
ちなみに、Python歴4年目にして初参加です。

実はこのカンファレンス知ってからずっと参加したいと思っていて
やっと参加することができました!

そこで参加したセッションや感想を書きました。

f:id:acro-engineer:20160923231415p:plain:w300:left

セッションについて

今回は全体的にデータを分析するセッションが多かったように感じますが
多種多様ですばらしいセッションの数々です。

今回、私が参加したセッションは次のとおりです。

1日目

2日目

  • PythonでもPythonじゃなくても使える汎用的なMicroservice実行環境
  • Pythonで実現する4コマ漫画の分析・評論
  • Pythonで入門するApache Spark
  • PythonではじめるOpenAI Gymトレーニング
  • Building a data preparation pipeline with Pandas and AWS Lambda

私の興味関心は仕事がらもあり、データ分析系が多いのですが
ところどころで自分が本来、関わりのないような領域も聞くことができて、
新鮮な気持ちといい刺激になり、とても良かったと思っています。

個人的には、「確率的ニューラルネットの学習と Chainer による実装」が特に面白かったです。

参加してよかったこと改善してほしいと思ったこと

参加してよかったこと

1.参加者同士の交流が盛ん
参加者同士の交流が盛んで、ポスターセッションや夜の懇親会など色々なところで
話すことができ、非常に楽しいカンファレンスです。

2.多種多様のセッション
様々なセッションがあるので、必ずといっていいほど、自分が関心のある
セッションを探すことができます。

3.おやつ
凄く想定外だったのですが、おやつの配布がありました。
疲れるタイミングでのおやつの配布だったので非常に良かったです。
おいしいお菓子の図です。

f:id:acro-engineer:20160922144417j:plain:w400

4.会場までの交通の便が良かった。
西早稲田駅だったので、個人的にも会場までの交通自体もよく
会場の場所もわかりやすかったので、行きやすかったです。

5.講演のビデオはあとで見ることができる。
講演のビデオについてですが、あとで見ることができます。
そのため、万が一見逃しても安心です。

PyCon Japan 2016 - YouTube

6.サポートが手厚い
このカンファレンス参加費が1万円です。
しかし、この1万円で朝食(2日間)、昼食(2日間)、おやつ(大事・2日間)、懇親会(1日目)が出ました。
(1日目の朝食は食いそびれましたがorz)
また、シャツや様々なグッズがついてきてお得感もあり、非常に満足しています。

改善してほしいと思ったこと

1.招待講演は混雑が予想されるので広い会場が良いです。
今回のカンファレンスでは招待講演で部屋に入れないといったことがあったので
予め混雑が予想されそうなセッションは広い部屋を使うと良かったと思います。

2.講演会場に電源を
会場の確保の話ですが、PC持っている人が多いので、電源があると嬉しいです。
休憩時間に行っても良いのですが、他のイベントもあって、電源エリアに行くのが難しいので・・

最後に

PyConJP凄く楽しかったです。
スタッフ、発表者、参加者、関係者の皆さん楽しいカンファレンスをありがとうございました!
来年も行くと思いますので、よろしくお願いします。

Acroquest Technologyでは、キャリア採用を行っています。


  • 日頃勉強している成果を、AWSHadoop、Storm、NoSQL、Elasticsearch、SpringBoot、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
  • 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
  • OSSの開発に携わりたい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
 
データ分析で国内に新規市場を生み出す新サービス開発者WANTED! - Acroquest Technology株式会社の新卒・インターンシップ - Wantedlywww.wantedly.com

API Gatewayから、AWS Lambdaを使わずにDynamoDBにアクセスする

こんにちは、ツカノ(@)です。

クラウド上にアプリケーションを構築することがアドバンテージとなり、AWSを利用したサーバレスな事例を多く目にするようになりました。
例えば、DBアクセスする場合などは、EC2のようにサーバ管理が必要なサービスを使わずに構築できるようになりました。

DynamoDBにアクセスする例は、様々な方がWeb上に掲載していますが、
f:id:acro-engineer:20160923092042p:plain
API GatewayAWS Lambda → DynamoDB
という流れで呼び出している例を多く目にします。

こういった例の中には、AWS Lambdaでは特に他の処理をせず、単にDynamoDBのAPIを呼び出しているだけのものもあります。
実はAPI Gatewayにはマッピングテンプレートという機能があり、httpボディのjsonフォーマットを変換して、AWSの様々なサービスと連携できます。

docs.aws.amazon.com

この機能を利用すれば、フォーマット変換が簡単なケースではAWS Lambdaを利用せずとも、
f:id:acro-engineer:20160923092111p:plain
API Gateway → DynamoDB
という流れで呼び出すことができます。
AWS Lambdaを利用しなくてよいため、ある程度利用料金も安くなりますね。

ただ、API Gatewayマッピングテンプレート(Velocityを利用している)を実際に書いてみると、多少の慣れが必要だと感じます。
そこで、API GatewayからDynamoDBのAPIを呼び出すためのマッピングテンプレートを具体的にまとめてみました。

今回利用したデータ

RDBMSでよく例に登場するemployeesのデータを利用しました。

ここから、employeesテーブルだけ利用します。
http://dev.mysql.com/doc/employee/en/sakila-structure.html

emp_no INT(11)
birth_date DATE
first_name VARCHAR(14)
last_name VARCHAR(16)
gender ENUM('M', 'F')
hire_date DATE
  • データ

先ほどのテーブルに入る具体的なデータは、このサイトにサンプルがあります。
https://launchpad.net/test-db/

何行か見てみると、このようなデータになっています。

(10001,'1953-09-02','Georgi','Facello','M','1986-06-26'),
(10002,'1964-06-02','Bezalel','Simmel','F','1985-11-21'),
(10003,'1959-12-03','Parto','Bamford','M','1986-08-28'),
(10004,'1954-05-01','Chirstian','Koblick','M','1986-12-01'),
(10005,'1955-01-21','Kyoichi','Maliniak','M','1989-09-12'),
(10006,'1953-04-20','Anneke','Preusig','F','1989-06-02')
(10007,'1957-05-23','Tzvetan','Zielinski','F','1989-02-10'),
(10008,'1958-02-19','Saniya','Kalloufi','M','1994-09-15'),
(10009,'1952-04-19','Sumant','Peac','F','1985-02-18'),
(10010,'1963-06-01','Duangkaew','Piveteau','F','1989-08-24'),

API GatewayAPIについて

API GatewayAPIについては以下のサイトを参考にしました。

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/HowItWorks.API.html
http://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/Programming.LowLevelAPI.html

GetItemのリクエスト・レスポンスの例が載っていますが、ちょっと癖がありますね。

{
    "TableName": "Pets",
    "Key": {
        "AnimalType": {"S": "Dog"},
        "Name": {"S": "Fido"}
    }
}
{
    "Item": {
        "Age": {"N": "8"},
        "Colors": {
            "L": [
                {"S": "White"},
                {"S": "Brown"},
                {"S": "Black"}
            ]
        },
        "Name": {"S": "Fido"},
        "Vaccinations": {
            "M": {
                "Rabies": {
                    "L": [
                        {"S": "2009-03-17"},
                        {"S": "2011-09-21"},
                        {"S": "2014-07-08"}
                    ]
                },
                "Distemper": {"S": "2015-10-13"}
            }
        },
        "Breed": {"S": "Beagle"},
        "AnimalType": {"S": "Dog"}
    }
}

httpリクエストをAPI Gatewayに送信するとき、ボディのjsonに型を記載しないことが多いと思いますが、DynamoDBのAPIでは型指定が必要になります。
そのため、API Gatewayでボディのjsonを変換する必要があります。

API Gatewayの設定方法について

API Gatewayの設定方法については、こちらのサイトが分かりやすいです。
dev.classmethod.jp

このサイトに「Integration Response の設定」が記載されています。
DynamoDBのいくつかのAPIに対して、実際にマッピングテンプレートを記載してみましょう。

PutItemの例

#set($item = $input.path('$'))
{
  "TableName": "employees",
  "Item": { 
    "emp_no": {
      "N": "$item.emp_no"
    },
    "birth_date": {
      "S": "$item.birth_date"
    },
    "first_name": {
      "S": "$item.first_name"
    },
    "last_name": {
      "S": "$item.last_name"
    },
    "gender": {
      "S": "$item.gender"
    },
    "hire_date": {
      "S": "$item.hire_date"
    }
  }
}

応答にボディは不要とすると、レスポンスのマッピングテンプレートは不要です。

Scanの例

全件取得のため、リクエストのマッピングテンプレートは不要です。

#set($items = $input.path('$.Items'))
{
  [
#foreach($item in $items)
    {
      "emp_no": $item.emp_no.N,
      "birth_date": "$item.birth_date.S",
      "first_name": "$item.first_name.S",
      "last_name": "$item.last_name.S",
      "gender": "$item.gender.S",
      "hire_date": "$item.hire_date.S"
    }#if($foreach.hasNext),#end

#end
  ]
}

emp_noは数値型であるため、JSONのレスポンスの値はダブルコーテーションで囲いません

GetItemの例

リクエストするURLにemp_noが入っていることを前提とします。

{
  "TableName": "employees",
  "Key": { 
    "emp_no": {
      "N": "$input.params('emp_no')"
    }
  }
}
#set($item = $input.path('$.Item'))
{
  "emp_no": $item.emp_no.N,
  "birth_date": "$item.birth_date.S",
  "first_name": "$item.first_name.S",
  "last_name": "$item.last_name.S",
  "gender": "$item.gender.S",
  "hire_date": "$item.hire_date.S"
}

emp_noは数値型であるため、jsonのレスポンスの値はダブルコーテーションで囲いません

DeleteItemの例

リクエストするURLにemp_noが入っていることを前提とします。

{
  "TableName": "employees",
  "Key": { 
    "emp_no": {
      "N": "$input.params('emp_no')"
    }
  }
}

応答にボディは不要とすると、レスポンスのマッピングテンプレートは不要です。

最後に

書き方に慣れるとそうでもないのですが、初めて書くときは苦労しますよね。
今回は、ちょっとした実践例ですが、初めてマッピングテンプレートを書く方のお役に立てば幸いです。
それではまた。

Acroquest Technologyでは、キャリア採用を行っています。


  • 日頃勉強している成果を、AWSHadoop、Storm、NoSQL、Elasticsearch、SpringBoot、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
  • 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
  • OSSの開発に携わりたい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
 
データ分析で国内に新規市場を生み出す新サービス開発者WANTED! - Acroquest Technology株式会社の新卒・インターンシップ - Wantedlywww.wantedly.com

ElasticのGraph機能でコンビニ商品を特徴分析

こんにちは、このブログにデビューしました、@です。

世の中ではポケットモンスターが流行していますが、
私の中では以前からモンスターエナジーが流行しています。

私は会社の近くのコンビニで、モンスターエナジーを購入することが多いのですが、
海外と日本では成分が異なるという話があったり、類似商品も多く出回っていたりするため、
中身が気になります。
(モンスターエナジー好きの私にとって、それ以外はすべて類似商品です)

実際、棚を眺めてみると、売り場面積の関係でコンビニで扱える商品は限られており、
工夫を凝らしたうたい文句の売れ筋商品がしのぎを削っています。

そんな折、@さんによるElasticsearchのGraph機能紹介記事もあり、
acro-engineer.hatenablog.com

同じコンビニエンスストアのデータセットを使って、もう少し違う観点から分析を深めたいと思いました。
※Graph機能の基本的な使い方は上のページを参考にしてもらえれば良いと思います。

自然言語処理も使い、販売商品の特徴をつかんで、分析してみましょう。

Graphの対象データ

今回も、The 2nd BigData Analysisのデータを使用します。
The 2nd Big Data Analysis Contest

この中にある商品データを使って、商品と説明文(概要)や商品と素材から
何か新しいこと、どんなことが見えるのかを検証してみます。

次の表に実際のレコードを2件表示しています。
本レコードにはカラムが更に多くありますが、今回は利用したカラムを抜粋しました。
pnameが商品名、abstractが商品の概要、materialsが素材名の一覧を示します。

pname abstract materials
うましおポテト(ノンフライ) ザク切りポテトを、カリッと焼き上げました。 馬鈴薯(遺伝子組換えでない)、植物油、砂糖、食塩、デキストリン食物繊維ブドウ糖、たんぱく加水分解物、調味料(アミノ酸等)、リン酸塩(Na)、甘味料(ステビア)、香料、(原材料の一部に鶏肉を含む)
くるみとココナッツのキャラメリゼ素焼きアーモンド入り くるみとココナッツに、キャラメルのやさしい甘さが広がります。 クルミ、ココナッツ、アーモンド、グラニュー糖、砂糖、ショ糖、でん粉、ぶどう糖デキストリン、食塩、乳等を主要成分とする食品、香料

解析

設定

今回の設定は次のとおりです。

  1. Sampling size:2000
  2. Certainty:1
  3. Diversity field: No diversification
  4. Timeout[ms]:5000
  5. Significant link: off

MeCabや特定の区切り文字(、)で分解したデータを投入しています。

商品の概要と商品名のデータ

The 2nd BigData Analysisには、商品の概要データがあります。

実際の加工したレコードを3件表示してみます。(形式はcsvです。)

概要の単語 商品名
ザク うましおポテト(ノンフライ)
切り うましおポテト(ノンフライ)
ポテト うましおポテト(ノンフライ)

商品の概要を解析することで概要のつながりからどのような商品が
似ているのか、思いがけない情報を取得できるかもしれません。

早速やってみましょう。
まず、大雑把に表示をすると次の図になります。

f:id:acro-engineer:20160911150319p:plain

例えば、このGraphを見ると
「キヌアと8種のグラノーラチョコレート」の食感が「サクッ」としており、
「サクッ」としているものとしては他にも「ミニ歌舞伎揚」や「小さな海老の華」、「京挽ききなこくるみ」があることがわかります。

つまり、「サクッ」としているものを取ることができます。この「サクッ」としているものといった概念をベースに
他の商品を検索することに応用することができます。

また、気になった箇所にはGraph機能でエッジを追加することができます。
「厚切りザクッとポテトうすしお味」にはどのような概要があって、それに近いものが何かを探すこともできます。

例えば、今現れているGraphを少々操作し、「厚切りザクッとポテトうすしお味」に近いものを出すと
次のようになります。

f:id:acro-engineer:20160912210428p:plain

「厚切りザクッとポテトコンソメ味」や「うましおポテト(ノンフライ)」といったノード(商品)が出てきました。
これで類似する商品を発見することができました。

このように、概要の繋がりから類似している商品をGraphの機能から検索することができます。

商品の素材と商品名のデータ

商品の素材データからも同じように検索することができます。
こちらも同様に、実際の加工したレコードを3件表示してみます。

素材名 商品名
馬鈴薯(遺伝子組換えでない) うましおポテト(ノンフライ)
植物油 うましおポテト(ノンフライ)
砂糖 うましおポテト(ノンフライ)

試しにGraphを使った表示をしてみましょう。

f:id:acro-engineer:20160911150324p:plain

注目ポイントを赤枠で囲ってみます。

f:id:acro-engineer:20160912211430p:plain

この図から、砂糖が
「お好み海鮮」、「いかみりん」、「いかの姿フライ」、「あんドーナッツ」、「キヌアと8種のグラノーラチョコレート」
の素材として使われていることがわかりました。

これを応用するとモンスターエナジーに近いものが分かるのでは!
と思ったのですが、よくよく見てみると、このデータセットにはモンスターエナジーは入っていなかったorz

Graphでわかること

ElasticsearchのGraph機能を使うと次のことがわかります。

1.要素と要素の直接的なつながり
基本的な機能ではありますが、今回解析したように、砂糖はどの商品に使われているのか、
といった関係をひと目で可視化することができます。

2.特定の要素とそれに関係した要素を可視化できる。
要素と要素から新しい要素を発見した場合、その要素と他の要素がどのように関係しているのか
といったことをひと目で可視化することができます。
今回の場合だと、「厚切りザクッとポテトうすしお味」にはどのような概要があって、
それに近いものが何かを探すことがそれを指しています。
これを応用すれば、商品の概要と売れ筋商品の関連性もつかむことができそうです。

Graph機能はこれらの分析を可能とし、関係性を容易につかむことができます。

まとめ

Graphを使うとつながりを見ることができます。
ElasticsearchのGraph機能をつかって、身近なものをどんどん可視化してみましょう。

今度はモンスターエナジーと他商品の関係を調べてみたい。

Acroquest Technologyでは、キャリア採用を行っています。


  • 日頃勉強している成果を、AWSHadoop、Storm、NoSQL、Elasticsearch、SpringBoot、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
  • 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
  • OSSの開発に携わりたい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
 
データ分析で国内に新規市場を生み出す新サービス開発者WANTED! - Acroquest Technology株式会社の新卒・インターンシップ - Wantedlywww.wantedly.com

位置情報付きツイートをElasticsearch,Kibanaで可視化して、ポケモンGOを攻略!

こんにちは@です

世間に一大ブームを起こしたポケモンGOですが、みなさんやってますか?
新機能が追加されたり、第二世代のポケモンが登場するという噂もあり、
さらに流行るかもしれないですね。

さて、今回は位置情報付きのツイートを地図上にマッピングして可視化してみます。
キーワードで絞り込むことで、そのキーワードを含むツイートが「どこで」「どのくらい」つぶやかれているかを知ることができます。
例えば「ピカチュウ」と検索すればポケモンGOピカチュウが出現する場所が簡単に特定できるかもしれません。

今回行うのは、logstashで取得したツイートをelasticsearchに入れて、それをkibanaで可視化するという流れです。

それでは実際にやってみます。

1.logstashでツイートを取得する

logstashのinputプラグインとして用意されているtwitterプラグインを用いて、ツイートの情報を収集します。
locations(ツイートした位置情報を表すパラメータ)またはkeywords(この文字列を含むツイートを検索する)が必須パラメータとなっています。
keywordに関してはkibana上でフィルタリングすれば良いので、locationsを指定してざっくり日本国内のツイートを集めます。
(twitterプラグインの詳しい使い方についてはhttps://www.elastic.co/guide/en/logstash/5.0/plugins-inputs-twitter.html参照)

locationsは経度と緯度をカンマ区切りのペアとして指定することができます。
下記の設定をすることで経度123〜154、緯度20〜46という範囲が設定されます。
また、keywordsを使えば指定したキーワードを含むツイートを集めることができます。

locationsでは、経度と緯度のペアをカンマ区切りで書くことでbounding boxを定義できます。
例えば
locations => 123,20,154,46
と設定すれば
経度123度、緯度20度の点と、経度154度、緯度46度の点によって作られる矩形
内の位置情報をもつツイートを検索できます。

TwitterAPIの使い方については次のURLを見てください
参考(https://dev.twitter.com/streaming/overview/request-parameters#locations)


以下に示すのが今回使用するlogstashの設定ファイルになります。
inputにtwitterプラグインで、locations => "123,20,154,46"と指定しています。
この設定で日本全国のツイートをカバーできるはずです(朝鮮半島なども一部入りますが)

 input { 
   twitter {
     consumer_key => "XXX"
     consumer_secret => " XXX"
     oauth_token => "XXX"
     oauth_token_secret => "XXX"
     locations => "123,20,154,46"
    #keywords => ""
     full_tweet => true
   }
 }
 filter{
 }
 output{
   elasticsearch{
     hosts => localhost
     index => twitter
   }
 }

この設定ファイルを使ってデータ投入!...と行きたいのですが、
この設定のみだと、ツイートの位置情報がnumber型となるのでKibanaの地図上で可視化することができません。
Kibanaの地図上でデータを可視化するためには、geo_pointという型でデータを保持する必要があります。

2.マッピングの設定をする

位置情報をgeo_point型として扱うためのマッピング定義を行います。
下記の内容をKibanaのConsoleを使って設定します。
参考(https://www.elastic.co/guide/en/elasticsearch/reference/current/geo-point.html)

 PUT _template/twitter_template
 {
   "template" : "twitter",
   "mappings" : {
     "_default_" : {
       "_all" : {"enabled" : true, "omit_norms" : true},
       "dynamic_templates" : [ {
         "message_field" : {
           "match" : "message",
           "match_mapping_type" : "string",
           "mapping" : {
             "type" : "string", "index" : "analyzed","omit_norms" : true
            }
         }
       }, {
         "string_fields" : {
           "match" : "*",
           "match_mapping_type" : "string",
           "mapping" : {
             "type" : "string", "index" : "analyzed", "omit_norms" : true,
           "fields" : {
             "raw" : {"type": "string", "index" : "not_analyzed",  "ignore_above" : 256}
           }
         }
       }
     } ],
     "properties" : {
     "@version": { "type": "string", "index": "not_analyzed"         },
       "coordinates" : {
         "type" : "object",
         "dynamic" : true,
         "properties" : {
           "coordinates" : { "type" : "geo_point" }
         }
       }
     }
   }
  }
 }

3.Kibana上で可視化する

まず初めに画面左にあるメニューからvisualizeをクリックして、
visualizeの種類としてtile map を選択します。
f:id:acro-engineer:20160830094038p:plain

index patternとして "twitter"を選択します。
f:id:acro-engineer:20160830094109p:plain

Optionsをクリックして以下のパラメータを設定します。

f:id:acro-engineer:20160830094114p:plain

Dataをクリックして位置情報を表すfieldとしてcoordinates.coordinatesを選択します。

f:id:acro-engineer:20160831173912p:plain
地図上にデータが表示されました!
いまの状態だと、キーワードによる絞り込みは行わずに、収集した全データが表示されていることになります。
やはり東京近辺のツイート数が多そうですね。

それでは幾つかキーワードで絞り込みを行ってみましょう。

例1:金閣寺
f:id:acro-engineer:20160831173947p:plain

例2:大阪
f:id:acro-engineer:20160831174001p:plain

例3:富士山
f:id:acro-engineer:20160831174052p:plain

特徴的なワードだと分布がはっきりしてわかりやすいですね。

この機能を使ってポケモンの名前を調べれば出現場所がわかるかもしれません。
早速"ピカチュウ"で絞り込んでみましょう!
f:id:acro-engineer:20160831174140p:plain
ある程度場所がわかりますね。さらに拡大してみましょう。
f:id:acro-engineer:20160906103310p:plain
対応する地名をGoogleMapで見てみると、、、
f:id:acro-engineer:20160831174228p:plain
出ました!ピカチュウはみなとみらい近辺の赤レンガ倉庫やワールドポーターズのあたりにいる可能性が高いですね!


今回はツイートの位置情報をKibanaで可視化しました。
Twitterデータを常に収集し続けておけば、リアルタイムに色々な情報を得られそうですね。

それじゃあちょっとピカチュウ捕まえてきます!


(※実際にピカチュウが出現する場所を保証するものではありません)

Acroquest Technologyでは、キャリア採用を行っています。


  • 日頃勉強している成果を、AWSHadoop、Storm、NoSQL、Elasticsearch、SpringBoot、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
  • 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
  • OSSの開発に携わりたい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
 
データ分析で国内に新規市場を生み出す新サービス開発者WANTED! - Acroquest Technology株式会社の新卒・インターンシップ - Wantedlywww.wantedly.com

Elastic Stack 5.0.0-alpha5がリリース。そろそろGraphを試してみる #elasticsearch

Pokemon Goトレーナーの @ です。
今月頭にラスベガスのモンテカルロに宿泊したところ、ニャースマンキー、ガーディの巣になっていて、アメを100個以上ためることができました!

そんなPokemon Goとは関係ないですが、Elasticsearch Go の alpha Goがリリースされました! Go! Go! 5! 5!

・・・はい、ちょっと無理ありましたね。知ってます。


Elastic Stack 5.0.0-alpha5の新機能は、Elasticのブログエントリーを参照してください。
https://www.elastic.co/jp/blog/elastic-stack-release-5-0-0-alpha-5

私としては、Beatsのテンプレート読み込み機能の辺りがちょっと注目ポイントでした。


さて、alpha5の話題はこれぐらいにして、今日の主なテーマは「Graph」です。
日本語で「グラフ」と言うと、Kibanaでも作れる棒グラフや折れ線グラフのイメージになりますが、それらは英語圏では「Chart」と呼ばれることが多いです。
一方「Graph」と言えば、点(ノード)を線(エッジ)で繋いで関連を表す図のことになります。日本語だと「グラフ理論のほうのグラフ」と言ったりするやつですね。
言ったりしますよね?

Elasticsearchにも、バージョン2.3と5.0にこの機能が追加されたので、今回は5.0.0-alpha5で試したいと思います。
alpha版のうちは、このGraph機能を制限なく使うことができます。

そもそもGraphって?

ElasticsearchのGraph機能は、データ「関連」を示すものなのですが、そもそも、関連というのはどういう事なのでしょうか?
Elasticsearchの公式サイトにあるGraphは、このような図になっています。

f:id:acro-engineer:20160819114148j:plain:w800
(公式サイトの画像より)


これは好きなアーティストのアンケートを図示したもので、ここではカナダの人が好きなアーティストと、ブラジルの人が好きなアーティストを図示しています。線が太いアーティストほど、出現数が多い(人気が高い)と言えます。また、図中の「metric」や「our lady peace」などは、両方の国の人が好きである、ということが分かります。

これがデータの関連です。ElasticsearchのGraph機能は、このような複数項目の関連を図示できます。この場合は「(アンケートに回答した人の)出身国」と「好きなアーティスト」の関連を示しています。


ところで、図の左のほうにある「mattew good band」と「matthew good」にも線が引かれています。アンケートでは好きなアーティストは複数回答が可能であったため、このように「好きなアーティスト」同士にも関連があるわけです。平たく言えば「このアーティストが好きな人は、このアーティストも好きです」というアレです。
ElasticsearchのGraph機能は、このような単一の項目(配列)の関連も図示できるのです。


データの関連がどういうものか、イメージを掴めたでしょうか?

なお、私はこういうものを見ると「ネットワーク構成の可視化」とか「マイクロサービスの可視化」などしたくなるのですが、少なくとも今のバージョンのGraph機能では、そのような可視化は難しいようです。あくまでもデータの関連を可視化することを主眼とし、多段構成となるノードの関連はあまり目的とされていないようです。


Graph機能を試す環境を作る

それでは実際にGraph機能を利用するための手順を紹介します。


Graph機能を試すには、Elasticsearch、Kibana、X-Packの5.0.0-alpha5をインストールします。
インストール方法は、前回のエントリーを参照してください。
Elastic Stack 5.0.0-alpha4リリース。CSVインポート機能とKibanaのモニタリングがお目見え! #elasticsearch - Taste of Tech Topics

現時点でこの手順に従ってインストールすると、5.0.0-alpha4ではなく、alpha5がインストールされます。


Graph機能は、Kibanaの左メニューにある「Graph」から開くことができます。
f:id:acro-engineer:20160819114757p:plain:w800
こんな感じで、Graphの画面が開きます。

Graph機能を試す

Graphを描画するためには、何らかのデータが必要です。
アクセスログなり、天気データなり、データの種類は特に問いません。

今回は、Big Data Analysis Contestのデータを投入してみました。
The 2nd Big Data Analysis Contest

このサイトから「ナチュラルローソンのお菓子」の売上データをダウンロードすることが可能です(要登録)

ただしデータは正規化されているため、Elasticsearchに投入する際には非正規化したり、少し加工してGraph機能で利用しやすくするような加工を行ないました。本題ではないので、ここでは加工の詳細は割愛します。


さて、データの登録が終わったらGraphの作成に移ります。

まず、左上のボックスからindexを選択します。
次に、1つの項目(配列)内のデータの関連を見たい場合には「Select a field」となっているボックスから、関連を見たい項目を選択します。
そして検索ボックスに * とでも入力して検索ボタンを押せば、グラフが描画されるでしょう。

2つの項目の相関を見たい場合には、Graph画面の右上にあるフラスコのアイコン(Advanced Mode)をクリックします。
次に、「Fields」から関連を見たい項目を選択し、検索ボックスに * など入力して検索ボタンを押します。


ここでは試しに、年齢層と商品名の関連を見てみます。
フラスコをクリックしてAdvanced Modeにして、Fieldsから「segment.keyword」(年齢層)と、「pname.keyword」(商品名)を選びました。
それで検索してみたのですが、、、
f:id:acro-engineer:20160819120108p:plain:w800
あんまり良い感じのGraphになりませんね。


そうなのです、データの内容によっては、これではほとんどGraphが描画されなかったり、ばらばらのグラフが描画されてしまうことがあります。
そのような場合には、右上にあるフラスコの隣のギアのアイコンをクリックして、描画をカスタマイズします。
f:id:acro-engineer:20160819115811p:plain:w800


設定項目は次の通りです。

  • Sample:
    • Graphの描画に用いるデータの数(サンプリング数) この値が2000であれば、データ数が10万あっても、そのうちの2000だけが利用される。
  • Significant links:
    • Graphに描画する対象データをSignificant Searchで抽出するかどうか。これにチェックが入っていると、特徴的なデータでGraphを描画する。チェックを外すことで、特徴がないデータも含めたGraphを描画する。
  • Certainty:
    • データ同士に関連があると判断するための最小出現数。この値が3であれば、データが2個未満の場合は関連なしとみなされる。
  • Diversity field:
    • データの偏りをなくすために、サンプリング対象を制限するためのフィールド。このフィールドと値を選択することで、たとえばアクセスログの「同じセッションIDは10個までしかサンプルに含めない」などができる。
  • Timeout(ms):
    • タイムアウト。この値が5000であれば、5秒以上かかる検索はエラーとなりGraphに追加されない。


この中で、元データの件数が多い場合には「Sample」で対象データを増やしたり、「Diversity field」を上手く使って同件のデータ数を抑制すると、効果があります。
また特徴の抽出よりも全体の概要を掴みたい時には、「Significant links」のチェックを外すことで、全体的な関連が描画されやすくなります。


この設定をカスタマイズしながらGraphを描いてみると、こんな結果が得られました。
f:id:acro-engineer:20160819121828p:plain:w800


えっと、未成年男性(m00_19)にはヨーグルトマシュマロがよく売れていて、50歳以上の女性(w50_)にはチアシードといちじくのクッキーがよく売れていて、20歳から49歳の男女(m20_49、w20_49)には共通してエンゼルパイが売れている・・・なんて。

もちろんここから更なる詳しい調査が必要となりますが、
まずはこのGraph機能を使ってまずインサイト(閃き)を得て、次にそれを裏付けるためにデータを詳細に調査する、という流れが良いのではないかと思います。


alpha版のうちは自由に試せるGraph機能。
ぜひ今のうちに使ってみてください!

Graph Go!

Acroquest Technologyでは、キャリア採用を行っています。


  • 日頃勉強している成果を、AWSHadoop、Storm、NoSQL、Elasticsearch、SpringBoot、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
  • 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
  • OSSの開発に携わりたい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
 
データ分析で国内に新規市場を生み出す新サービス開発者WANTED! - Acroquest Technology株式会社の新卒・インターンシップ - Wantedlywww.wantedly.com

AWS Lambdaの処理性能を言語毎に測ってみた

こんにちは、@です。

この間AWS Summit Tokyoに参加してきたのですが、皆一様に「AWS Lambda」を、
これからのサーバレスアーキテクチャを支える技術として紹介していましたね。

資料でも言葉でも多分に見聞きしており、軽いゲシュタルト崩壊を起こしている今日この頃、
皆さんはいかがお過ごしでしょうか。

さて、今回はAWS Lambdaです。

AWS Lambdaの処理はJavaやNode.js、Pythonなどの言語で記述することができますが、その性能差がどの程度あるのか?測ってみました。

構成

今回の構成は次の様なシンプルなものにしています。

[計測対象(言語)]

  1. Python
  2. Node.js
  3. Java

[計測対象(カテゴリ)]

  1. 処理速度
  2. 使用メモリ

[Lambdaでの処理内容]

  1. API Gatewayでリクエストを受け付け
  2. Lambda内でDynamoDBから1件データを取り出す
  3. 取り出したデータをログに出力する

図で示すとこんな感じです。

f:id:acro-engineer:20160714104306p:plain

[計測条件]

  1. 次に示すサンプルコードをそれぞれLambdaで定義して実行する。
  2. クライアントにはApache JMeterを利用。
  3. 各言語とも200回計測し、それぞれ100、90、80、50パーセンタイル値、最小値(もっとも速い/軽い)を取る。
  4. メモリは192MB指定。(Javaが192MBでないと動かないので、他も合わせてスペックアップ)

[サンプルコード]

各言語のソースコードは次の通りです。

[Python]

#!/usr/bin/python2.7
# -*- coding:utf-8 -*-
# Python
import boto3
import logging
from boto3.dynamodb.conditions import Key

logger = logging.getLogger()
logger.setLevel(logging.INFO)

dynamodb = boto3.resource('dynamodb')
table = dynamodb.Table('mytest')

def handle_request(event, context):
    response = table.query(
        KeyConditionExpression=Key('id').eq('test')
    )
    logger.info(response)
    logger.info("SUCCESS")

[Node.js]

// Node.js
var AWS = require('aws-sdk');
var dynamodb = new AWS.DynamoDB();

exports.handler = function(event, context) {
    var params = {
        Key: {id: {"S": "test"}},
        TableName: "mytest"
    };

    dynamodb.getItem(params, function (err, data) {
        if (err) {
            console.log(err);
            context.fail('Failed');
        } else {
            console.log(data);
            context.succeed('Succeeded');
        }
    });
};

[Java]

package jp.co.acroquest.aws.bench;

import com.amazonaws.auth.EnvironmentVariableCredentialsProvider;
import com.amazonaws.regions.Region;
import com.amazonaws.regions.Regions;
import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient;
import com.amazonaws.services.dynamodbv2.document.DynamoDB;
import com.amazonaws.services.dynamodbv2.document.Item;
import com.amazonaws.services.dynamodbv2.document.Table;
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;
import com.amazonaws.services.lambda.runtime.RequestHandler;

public class DynamoDBBench implements RequestHandler<Object, Object> {
	AmazonDynamoDBClient client;
	Table table;
	DynamoDB dynamoDB;

	public DynamoDBBench() {
		client = new AmazonDynamoDBClient(
				new EnvironmentVariableCredentialsProvider());
		client.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1));
		dynamoDB = new DynamoDB(client);
		table = dynamoDB.getTable("mytest");
	}

	public String handleRequest(Object input, Context context) {
		LambdaLogger logger = context.getLogger();
		Item item = table.getItem("id", "test");
		if (item == null) {
			logger.log("Item is null.");
			return "Failure";
		}
		logger.log("Item obtained: " + item);
		return "Success";
	}
}

結果

200回計測した結果の値を取ってみたところ、このようになりました。

[100パーセンタイル(最大)]
f:id:acro-engineer:20160718084625p:plain

[90パーセンタイル]
f:id:acro-engineer:20160718084624p:plain

[80パーセンタイル]
f:id:acro-engineer:20160718084623p:plain

[50パーセンタイル]
f:id:acro-engineer:20160718084622p:plain

[最小]
f:id:acro-engineer:20160718084626p:plain

  • Javaは初回アクセスからしばらくの間、オーバーヘッドが大きいことがわかります。
  • 使用メモリは予想通りJavaが一番大きく、PythonとNode.jsはほぼ同程度の使用率であることがわかります。
  • 実行回数を重ねるごとに、Javaのパフォーマンスが上がっていることがわかります。
  • 処理時間が最小の結果では、Javaが他の言語に追いついていることがわかります。

今回の結果の生データは次のリンクに置いておきました。

taste-of-tech-topics/measure_result.xlsx at master · kojiisd/taste-of-tech-topics · GitHub

Java性能のパフォーマンス向上における確認実験

ちなみに回数を重ねるごとにJavaの性能が大きく上がりはするものの、実行開始時の処理時間がかかっている部分をもう少し詳しく知るために、
AWS Lambda上でフィボナッチ数列を求める計算を簡易的にですが各言語毎に調べてみました。今回は40番目の数値を求める計算をしました。

たった5回の計測でしたが、次の様な平均値が取れました。Javaが他言語に比べてオーバーヘッドなど関係なく圧倒的に早いことがわかります。
Pythonは2分のタイムアウト設定を設けてもタイムアウトしてしまったので、今回はそれ以上は計測していません。

Java Node.js Python
平均処理時間[ms] 4308.3 12387.0 2分以上

個人的には(シンプルな実装なので最適とは言えないが)V8のJavaScriptエンジンを搭載しているNode.jsの3倍もの性能をJavaが出せたのは意外でした。
以上の結果から、今回のDynamoDBにアクセスして値を取得してくる処理に関しては、Javaライブラリの読み込みに時間がかかってしまっており、実行を繰り返す中で最適化がされていったため、初動は遅いが繰り返すたびにJavaの性能が上がってきた、と推測することはできそうです。

今回のフィボナッチ数列計算を試した際のコードは以下になります。

[Java]

package jp.co.acroquest.aws.bench;

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.LambdaLogger;

public class Fibonacci {
	private static final int INDEX = 40;
	LambdaLogger logger;

	public String handleRequest(Object input, Context context) {
		logger = context.getLogger();
		long calcResult;
		calcResult = calcFib(INDEX);
		logger.log("result: " + calcResult + "\n");
		return "Success";
	}

	public long calcFib(int number) {
		if (number == 1 || number == 2)
			return 1;
		return calcFib(number - 2) + calcFib(number - 1);
	}

}

[Node.js]

exports.handler = (event, context, callback) => {
    var result = calcFib(40);
    context.succeed('result: ' + result)
    context.succeed('Success');
};

function calcFib(num) {
  if(num == 1 || num == 2) {
    return 1;
  }
  return calcFib(num - 1) + calcFib(num - 2);
}

[Python]

import logging
def lambda_handler(event, context):
    result = calcFib(40)
    logging.info('result: ' + result)
    return 'Success'

def calcFib(num):
  if num <= 2:
    return 1
  else :
    return calcFib(num - 1) + calcFib(num - 2)

言語毎の実行ログは以下になります。

[Java]

REPORT RequestId: b0afcdb2-5715-11e6-8d3c-537f664ac4cd	Duration: 4626.36 ms	Billed Duration: 4700 ms Memory Size: 128 MB	Max Memory Used: 34 MB	 
REPORT RequestId: b77533b9-5715-11e6-8d3c-537f664ac4cd	Duration: 4211.39 ms	Billed Duration: 4300 ms Memory Size: 128 MB	Max Memory Used: 34 MB	 
REPORT RequestId: bc0c9611-5715-11e6-8d14-ed04d7d6b375	Duration: 4239.18 ms	Billed Duration: 4300 ms Memory Size: 128 MB	Max Memory Used: 34 MB	 
REPORT RequestId: c2ecb0b3-5715-11e6-ac28-fd403fb9707b	Duration: 4229.15 ms	Billed Duration: 4300 ms Memory Size: 128 MB	Max Memory Used: 34 MB	 
REPORT RequestId: c8944a4a-5715-11e6-b5a7-bb8b3c4b93fe	Duration: 4235.18 ms	Billed Duration: 4300 ms Memory Size: 128 MB	Max Memory Used: 34 MB	

[Node.js]

REPORT RequestId: e3125256-5714-11e6-a4f7-85ff9fd7c6c7	Duration: 12458.36 ms	Billed Duration: 12500 ms Memory Size: 128 MB	Max Memory Used: 16 MB	 
REPORT RequestId: 04db011d-5715-11e6-b7c7-15170a43b8ce	Duration: 12367.85 ms	Billed Duration: 12400 ms Memory Size: 128 MB	Max Memory Used: 16 MB	 
REPORT RequestId: 72e8fccb-5715-11e6-9523-3f458c891180	Duration: 12367.47 ms	Billed Duration: 12400 ms Memory Size: 128 MB	Max Memory Used: 16 MB	 
REPORT RequestId: 7c31b1ac-5715-11e6-8ff0-2d80e2cd4b43	Duration: 12374.82 ms	Billed Duration: 12400 ms Memory Size: 128 MB	Max Memory Used: 16 MB	 
REPORT RequestId: 842b529d-5715-11e6-ad82-7103149e1efd	Duration: 12366.68 ms	Billed Duration: 12400 ms Memory Size: 128 MB	Max Memory Used: 16 MB	

[Python]

REPORT RequestId: 474c2f4b-571a-11e6-8a89-4bca1cc5593f	Duration: 120000.15 ms	Billed Duration: 120000 ms Memory Size: 128 MB	Max Memory Used: 15 MB	 
2016-07-31T12:30:25.971Z 474c2f4b-571a-11e6-8a89-4bca1cc5593f Task timed out after 120.00 seconds

考察

今までの実験を踏まえると、AWS Lambdaで言語を選択する際には、次のようなことを考えるとよいのかと思いました。

  • 継続的にアクセスされず、かつCPUもあまり使わない処理であれば、Pythonが速い。
  • 継続的にアクセスされ、かつCPUをあまり使わない処理であれば、どの言語も大差ない。
  • CPUを大量に使うのであれば、Javaが速い。

実際には上記に加えて書き慣れている言語であるかなど開発者側のスキルなども多分に関係すると思いますが、一旦結果のみから判断しようとすると、こうなるのかな、という感じです。


今回はシーケンシャルかつシングルスレッドで処理されるプログラムで実験をしましたが、複数同時リクエストの場合や、内部で複数スレッドを使って処理をするような場合での性能比較も、どこかで実験してみたいと思います。

それでは!

Acroquest Technologyでは、キャリア採用を行っています。


  • 日頃勉強している成果を、AWSHadoop、Storm、NoSQL、Elasticsearch、SpringBoot、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
  • 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
  • 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
  • OSSの開発に携わりたい。

 
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
 
データ分析で国内に新規市場を生み出す新サービス開発者WANTED! - Acroquest Technology株式会社の新卒・インターンシップ - Wantedlywww.wantedly.com