RDSとS3でファイルのやり取りを行う

データベースサーバ上にファイルを置いて、PL/SQLのUTL_FILE経由で読み書きするような処理があった場合、RDSに移行しようとすると、データベースサーバにファイルが置けないなあ・・・などという場合に、S3を間に置く方法があります。

前提

EC2 <-file-> S3 <-file-> RDS上のファイル
というやり取りについて記載しています。
また、以下の情報は2017年8月時点のものです。

Oracle on Amazon RDSでの制限

前提として、Oracle on RDSでできることを整理しましょう。S3にアクセスするにはUTL_HTTPパッケージが必要です。ユーザーガイドの「utl_http、utl_tcp、utl_smtp の使用」にサポートされる旨が記載されています。
次に、UTL_FILEによるアクセスです。UTL_FILEを用いるにはディレクトリオブジェクトを扱える必要があります。こちらについてもユーザーガイドの「主要データストレージ領域で新しいディレクトリを作成する」にて、rdsadmin.rdsadmin_util.create_directoryプロシージャを使用して可能なことが記載されています。

使用するライブラリ

UTL_HTTPを使ったS3へのアクセスを全て自分で書くのは大変なので、alexandria-plsql-utilsのAMAZON_AWS_S3_PKGを用いることにします。

準備手順

EC2, S3のバケット、RDSをすべて新規で構成する手順を見ていきましょう。順序としては以下のようになります。
1. RDSを作成する。
2. S3にバケットを作成する。
3. バケットに含まれるオブジェクトへのアクセス権を持つポリシーを作成する。
4. 3で作成したポリシーを持つロールを付与したEC2を作成する。
5. 3で作成したポリシーを持つユーザを作成する。
6. RDSにディレクトリを作成し、S3へのアクセスに必要な権限と5で作成したユーザのアクセス情報を設定する。
7. EC2とS3のやり取りを行ってみる。
8. RDSとS3のやり取りを行ってみる。

1. RDSの作成

まず、いきなりRDSを作成するのではなく、先にRDSのメニューから「オプショングループ」を選択し、「apex」という名前でAPEX及びAPEX-DEVを含むオプショングループを作成して下さい。これは、AMAZON_AWS_S3_PKGが内部でデコード関連でAPEXのライブラリを使用しているためです(APEXを実際に起動する必要はありません)。以下のようになります。
スクリーンショット 2017-08-19 17.32.40.png

apexオプショングループを用いてRDSを作成して下さい。指定箇所はパラメータグループの下にあります。次のようになります。
スクリーンショット 2017-08-19 17.34.53.png
あとは通常のRDSの作成と同様です。RDSの作成については以下を参照して下さい。
RDSユーザーガイド-Oracle DB インスタンスを作成して Oracle DB インスタンス上のデータベースに接続する

2. S3バケットの作成

特に特筆すべきことはありません。グローバルで一意になる名前でS3にバケットを作成しましょう。
S3入門ガイド-バケットの作成

3. ポリシーの作成

IAMから2で作成したバケットに含まれるオブジェクトへのアクセス権限を持つポリシーを作成します。
IAM -> ポリシーで「ポリシーの作成」を押したら、「独自のポリシーの作成」を選びましょう。
スクリーンショット 2017-08-19 17.46.37.png
ポリシー名には「allow-rds-s3-policy」などとし、ポリシードキュメントには以下のJSONを記述します。

S3-rds-policy
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::<手順2で作成したS3バケットの名前>"
            ]
        },
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject",
                "s3:PutObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::<手順2で作成したS3バケットの名前>/*"
            ]
        }
    ]
}

このポリシーは2で作成したS3バケット、及びオブジェクトに対する権限を付与します。これをEC2、及びRDSのPL/SQLアクセス時に有効にすれば、EC2 <-> S3 <-> RDS上のPL/SQLでファイルをやり取りできます。

4. EC2インスタンスの作成

3の手順で作成したポリシーを付与したEC2用のIAMロールを作成します。IAMサービスから
ロール -> 新しいロールの作成 -> EC2ロールタイプ と選択し、
スクリーンショット 2017-08-20 0.33.30.png

3の手順で作成したポリシーを付与して
スクリーンショット 2017-08-20 0.34.37.png

名前をつければ完了です。
スクリーンショット 2017-08-20 0.37.17.png

あとはこのロールを指定してEC2インスタンスを作成します。
スクリーンショット 2017-08-20 0.39.29.png
1の手順で作成したRDSに1521ポートで接続可能なサブネットに作成して下さい。
EC2インスタンスの作成については以下も参照して下さい。
インスタンスの作成

5. PL/SQL用IAMユーザの作成

PL/SQLにIAMロールは付与できないので3の手順で作成したポリシーを付与したユーザをPL/SQL用に作成します。IAMサービスから
ユーザー -> ユーザーの追加
を選択し、ユーザー名を入力して「プログラムによるアクセス」を有効にします。
スクリーンショット 2017-08-20 0.45.29.png
アクセス権限の設定画面では、「既存のポリシーを直接アタッチ」を選択し、3の手順で作成したポリシーをチェックします。
スクリーンショット 2017-08-20 0.48.06.png
作成が完了した際に得られる「アクセスキー ID」と「シークレットアクセスキー」をPL/SQL側で使用することになります。

6. RDS上での設定

RDS上ではディレクトリの作成と、アクセス権限の設定を行います。
設定を行う前に、4の手順で作成したEC2インスタンスにログインし、必要なツールを入れます。

SQL*Plus

OTNのInstant Client Downloads for Linux x86-64から、basic及びsqlplusの2つのパッケージをブラウザ経由でダウンロードし、EC2インスタンスに転送してインストールして下さい(オラクル社のSSOログインが要求されますので、ブラウザで実施する必要があります)。
以下のように12.2のrpmをインストールした場合には、OCIライブラリやSQL*Plusのバイナリは/usr/lib/oracle/12.2/client64にインストールされています。

SQL*Plusのインストール
$ sudo rpm -i oracle-instantclient12.2-basic-12.2.0.1.0-1.x86_64.rpm
$ sudo rpm -i oracle-instantclient12.2-sqlplus-12.2.0.1.0-1.x86_64.rpm
$ ls /usr/lib/oracle/12.2/client64/bin/
adrci  genezi  sqlplus
$ ls /usr/lib/oracle/12.2/client64/lib/
glogin.sql             libmql1.so       libocijdbc12.so   libsqlplusic.so
libclntsh.so.12.1      libnnz12.so      libons.so         ojdbc8.jar
libclntshcore.so.12.1  libocci.so.12.1  liboramysql12.so  xstreams.jar
libipc1.so             libociei.so      libsqlplus.so
$

以下のように.bash_profileを設定しておきましょう。これでいつでもRDSにログインできます。

~/.bash_profile
...
ORACLIENT=/usr/lib/oracle/12.2/client64
export PATH=$PATH:$HOME/.local/bin:$HOME/bin:$ORACLIENT/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLIENT/lib

alias sql="sqlplus '<DBユーザー>@(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=<DB名>.<エンドポイント>.ap-northeast-1.rds.amazonaws.com)(PORT=1521))(CONNECT_DATA=(SID=<DBのSID>)))'"

alexandria-plsql-utils

Gitでクローンし、AMAZON_AWS_S3_PKGをインストールします。

alexandria-plsql-utilsのインストール
$ sudo yum install git
...
完了しました!
$ git clone https://github.com/mortenbra/alexandria-plsql-utils.git
Cloning into 'alexandria-plsql-utils'...
remote: Counting objects: 447, done.
remote: Total 447 (delta 0), reused 0 (delta 0), pack-reused 447
Receiving objects: 100% (447/447), 382.00 KiB | 0 bytes/s, done.
Resolving deltas: 100% (184/184), done.
Checking connectivity... done.
$ cd alexandria-plsql-utils/
$ ls
README.md  alexandria-logo.jpg  demos  doc  extras  ora  setup
$ ls setup/
$ sql      #前節で設定したエイリアスでRDSへ接続
...
SQL> @install_core
...
SQL> show errors
No errors
SQL> @install_inet
...
SQL> show errors
No errors
SQL> @install_amazon
...
SQL> show errors
No errors
SQL> exit
$

ACLの設定

明示的にACLを設定しない限りUTL_HTTPによるアウトバウンドのアクセスはOracleにより全て拒否されます。次のようにDBMS_NETWORK_ACL_ADMINパッケージを用いて自ユーザから手順2で作成したs3バケットに対してのみアクセスを許可します。

create_acl.sql
declare
   l_myuser varchar(32);
begin
   select user into l_myuser from dual;
   dbms_network_acl_admin.create_acl(
     acl         => 's3',
     description => 's3 acl',
     principal   => l_myuser,
     is_grant    => true,
     privilege   => 'connect'
   );
   dbms_network_acl_admin.add_privilege(
     acl         => 's3',
     principal   => l_myuser,
     is_grant    => true,
     privilege   => 'resolve'
   );
   dbms_network_acl_admin.assign_acl(
     acl         => 's3',
     host        => '<手順2で作成したバケット名>.s3.amazonaws.com'
   );
end;
/

ディレクトリの作成

RDS側の格納先であるメインデータストレージ領域上のディレクトリを作成します。以下では2つ作成しています。テーブル名などと同じく、Oracleのデータベース・オブジェクト名となるので引用符で囲まなければ大文字となります。
データベース・オブジェクト名および修飾子

create_directory.sql
begin
  rdsadmin.rdsadmin_util.create_directory('EC2');
  rdsadmin.rdsadmin_util.create_directory('S3');
end;
/

アクセス

EC2からS3へのアクセス

テスト用ファイルとしてAWSのEC2オファーファイルを使い、EC2とS3のオファーファイルをそれぞれ異なるディレクトリに配置しておきましょう。

テスト用ファイルのダウンロード
$ mkdir ~/files
$ cd ~/files
$ mkdir ec2 s3
$ wget -O ec2/ec2-price.csv https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonEC2/current/index.csv
...
ec2/ec2-price.csv   100%[===================>]  92.92M  29.9MB/s    in 3.1s    
...
$ wget -O s3/s3-price.csv https://pricing.us-east-1.amazonaws.com/offers/v1.0/aws/AmazonS3/current/index.csv
...
s3/s3-price.csv     100%[===================>] 870.02K  --.-KB/s    in 0.05s   
...
$

EC2からS3へのコピー

EC2ではAWS CLIが使え、手順4でS3への権限をロールで与えているので、以下のコマンドを打てば完了です。

S3へのupload
$ cd ~/files
$ aws s3 cp ec2/ec2-price.csv s3://<手順2で作成したバケット名>/ec2/ec2-price.csv
upload: ec2/ec2-price.csv to s3://<手順2で作成したバケット名>/ec2/ec2-price.csv
$ aws s3 cp s3/s3-price.csv s3://<手順2で作成したバケット名>/s3/s3-price.csv
upload: s3/s3-price.csv to s3://<手順2で作成したバケット名>/s3/s3-price.csv
$

S3からEC2へのコピー

以下のコマンドを打てば完了です。

S3からのdownload
$ cd ~/files
$ rm -R */*.csv #ファイルを消しておきます
$ aws s3 cp s3://<手順2で作成したバケット名>/ec2/ec2-price.csv ec2/ec2-price.csv
download: s3://<手順2で作成したバケット名>/ec2/ec2-price.csv to ec2/ec2-price.csv
$ aws s3 cp s3://<手順2で作成したバケット名>/s3/s3-price.csv s3/s3-price.csv
download: s3://<手順2で作成したバケット名>/s3/s3-price.csv to s3/s3-price.csv       
$ ls -R
.:
ec2  s3

./ec2:
ec2-price.csv

./s3:
s3-price.csv
$

RDSからS3へのアクセス

AMAZON_AWS_S3_PKGはBLOBとS3オブジェクトをインタフェースします。
認証とやり取りのためのBLOBとして1つテーブルを用意しておきます。

rds_s3_config.sql
create table rds_s3_config (
  key varchar2(32) primary key,
  value varchar2(128),
  tmpblob blob
);
insert into rds_s3_config (key, value) values ('aws_id', '<手順5で得たアクセスキーID>');
insert into rds_s3_config (key, value) values ('aws_key', '<手順5で得たシークレットアクセスキー>');
insert into rds_s3_config (key, value) values ('aws_s3_bucket', '<手順2で作成したS3バケット名>');
insert into rds_s3_config (key, tmpblob) values ('temporary_blob', empty_blob());
commit;
実行結果
SQL> @rds_s3_config

Table created.


1 row created.


1 row created.


1 row created.


1 row created.


Commit complete.

SQL> 

S3からRDSへのコピー

S3からオブジェクトをBLOBで取り出し、BLOBをファイルに書き込みます。次のようなプロシージャを作成しておきます。

copy_s3_to_local.sql
create or replace procedure copy_s3_to_local(
  p_s3_bucket varchar2,
  p_s3_key varchar2,
  p_local_dir varchar2,
  p_local_file varchar2
) is
  l_aws_id      varchar2(128);
  l_aws_key     varchar2(128);

  l_blob        blob;
  l_length      integer;
  l_index       integer := 1;
  l_bytecount   integer;
  l_tempraw     raw(32767);
  l_file        utl_file.file_type;
  l_dir         varchar2(128);
begin
  select value into l_aws_id from rds_s3_config where key = 'aws_id';
  select value into l_aws_key from rds_s3_config where key = 'aws_key';
  amazon_aws_auth_pkg.init(l_aws_id, l_aws_key);

  l_blob := amazon_aws_s3_pkg.get_object(p_s3_bucket, p_s3_key);
  -- エラーレスポンスかどうかを粗く判定
  if utl_raw.cast_to_varchar2(dbms_lob.substr(l_blob,256,1)) like '%<Error>%' then
    raise NO_DATA_FOUND;
  end if;

  l_length := dbms_lob.getlength(l_blob);
  l_file  := utl_file.fopen(p_local_dir, p_local_file, 'wb', 32767);

  while l_index <= l_length
  loop
      l_bytecount  := 32767;
      DBMS_LOB.read(l_blob, l_bytecount, l_index, l_tempraw);
      utl_file.put_raw(l_file, l_tempraw);
      l_index      := l_index + l_bytecount;
  end loop;
  utl_file.fflush(l_file);
  utl_file.fclose(l_file);
end;
/
show errors

実行結果
SQL> @copy_s3_to_local

Procedure created.

No errors.
SQL> 

テストしてみましょう。

copy_s3_to_local_test.sql
set serveroutput on
begin
  copy_s3_to_local('<手順2で作成したS3バケット名>', 'ec2/ec2-price.csv', 'EC2', 'ec2-price.csv');
  copy_s3_to_local('<手順2で作成したS3バケット名>', 's3/s3-price.csv', 'S3', 's3-price.csv');
end;
/
テスト:S3からRDSへのダウンロード
SQL> @copy_s3_to_local_test

PL/SQL procedure successfully completed.

SQL> 

RDSからS3へのコピー

ファイルからテーブル上のBLOBに書き込み、S3にアップロードします。次のようなプロシージャを作成しておきます。

copy_local_to_s3.sql
create or replace procedure copy_local_to_s3(
  p_local_dir varchar2,
  p_local_file varchar2,
  p_s3_bucket varchar2,
  p_s3_key varchar2
) is
  l_aws_id      varchar2(128);
  l_aws_key     varchar2(128);

  l_blob        blob;
  l_handle      bfile;
  l_dir         varchar2(128);
  l_doffset     pls_integer := 1;
  l_soffset     pls_integer := 1;
begin
  select value into l_aws_id from rds_s3_config where key = 'aws_id';
  select value into l_aws_key from rds_s3_config where key = 'aws_key';
  amazon_aws_auth_pkg.init(l_aws_id, l_aws_key);

  select tmpblob into l_blob from rds_s3_config where key = 'temporary_blob' for update;
  l_handle := bfilename(p_local_dir, p_local_file);
  dbms_lob.fileopen(l_handle, dbms_lob.file_readonly);
  dbms_lob.loadblobfromfile(l_blob, l_handle, dbms_lob.getlength(l_handle), l_doffset, l_soffset);
  -- このサンプルはContent-TypeをCSVに固定
  amazon_aws_s3_pkg.new_object(p_s3_bucket, p_s3_key, l_blob, 'text/csv');
  dbms_lob.fileclose(l_handle);
  rollback;
end;
/
show errors

テストしてみましょう。

copy_local_to_s3_test.sql
set serveroutput on
begin
  copy_local_to_s3('EC2', 'ec2-price.csv', '<手順2で作成したS3バケット名>', 'ec2/ec2-price.csv');
  copy_local_to_s3('S3', 's3-price.csv', '<手順2で作成したS3バケット名>', 's3/s3-price.csv');
end;
/
テスト
$ aws s3 rm s3://<手順2で作成したS3バケット名>/ec2/ec2-price.csv #ファイルを削除
delete: s3://<手順2で作成したS3バケット名>/ec2/ec2-price.csv
$ aws s3 rm s3://<手順2で作成したS3バケット名>/s3/s3-price.csv   #ファイルを削除
delete: s3://<手順2で作成したS3バケット名>/s3/s3-price.csv
$ aws s3 ls s3://<手順2で作成したS3バケット名>/ec2/ #空であることを確認
$ aws s3 ls s3://<手順2で作成したS3バケット名>/s3/  #空であることを確認
$ sql
... 
SQL> @copy_local_to_s3_test

PL/SQL procedure successfully completed.

SQL> exit
...
$ aws s3 ls s3://<手順2で作成したS3バケット名>/ec2/ #アップロードされたことを確認
2017-08-21 13:44:18   97438744 ec2-price.csv
$ aws s3 ls s3://<手順2で作成したS3バケット名>/s3/  #アップロードされたことを確認
2017-08-21 13:44:20     890903 s3-price.csv
$

まとめ

以上、EC2とS3のファイルのやり取り、そしてS3とRDSのファイルのやり取りについて見てきました。
より本格的に処理するには、特に紹介したPL/SQLプロシージャにおいて、S3へのアップロードのContent-Typeを適切に選択したり、エラーレスポンス(XMLドキュメントが返される)の判定を厳密にしたりなどが必要となるでしょう。

続きを読む

Running Kubernetes Cluster on AWS

Following this guide https://kubernetes.io/docs/getting-started-guides/kops/, set up Kubernetes cluster on AWS using kops.

1. Install kops

According to the guide, kops is:

kops helps you create, destroy, upgrade and maintain production-grade, hig… 続きを読む

クラウドの力を借りて無限収入システムを構築する(はずだった)

目的

 ビットコイン等の仮想通貨の採掘をして不労所得チャリンチャリンというのは夢がありますよね。私もチャレンジしようと思ったのですが、手元の貧相な PC では電気代すらペイできないのは明らかです。
 ではクラウドの力を借りて、GPU でやってみたらどうなのか。スポット価格をうまく使えば、もしかしてクラウド利用料を賄えて夢の無限収入システムを構築できるのではないか、というチャレンジをしてみたので、その記録です。
 なお、実際に稼げるとは思ってなくて、AWS スポットインスタンス使ってみる、GPUインスタンスを使ってみる、ブロックチェーンに触れてみる、など一石三鳥なお勉強をすることが目的です。

概念図

 インスタンス利用料が安いときに仮想通貨をマイニングすれば黒字転嫁するのでは、というアイデアです。
image.png

結果

 すごい赤字になる。スポットインスタンスを活用しても インスタンス利用料の半分まかなえる程度の採掘量にとどまり利益が生まれることはなかった、通貨マイナーの世界はきびしい。
 FPGA 使えれば結果は変わるのかなー。

構築の流れ

採掘環境構築~試しに採掘まで

環境情報

  • 採掘対象

    • Ethereum
  • OS 1
    • Ubuntu Linux 16.04 / 64bit
  • インスタンスタイプ
    • g3.4xlarge or p2.xlarge
  • リージョン
    • us-east-1(インスタンス費用がたぶんいちばん安いので)
  • マイナークライアント
    • Claymore’s Dual GPU Miner 9.7
  • ウォレット
    • coincheck

ウォレットの準備

  • Coincheck にアカウント作成

    • その他、大手のbitFlyer、Zaif 等なんでもよさそうです、手数料や取り扱い通貨が違います
  • 身分証明書を送り取引可能な状態にする
    • 最大2営業日必要との記載ですが、今回は数時間で本人確認が完了しました
  • Ether入金用のアドレスを作成 する

    • アドレス:0x0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X

マイニング用AMIを作成する

Ubuntu インスタンスを作成、Calymore9.7 を導入する。

console
$ wget https://github.com/nanopool/Claymore-Dual-Miner/releases/download/v9.7/Claymore.s.Dual.Ethereum.Decred_Siacoin_Lbry_Pascal.AMD.NVIDIA.GPU.Miner.v9.7.-.LINUX.tar.gz
$ mkdir Calymore9.7
$ tar xvzf Claymore.s.Dual.Ethereum.Decred_Siacoin_Lbry_Pascal.AMD.NVIDIA.GPU.Miner.v9.7.-.LINUX.tar.gz -C ./Calymore9.7/
$ cd Calymore9.7/

不足しているライブラリをインストール

console
sudo apt-get install ocl-icd-opencl-dev
sudo apt-get install libcurl3

nvidiaのドライバをインストール

console
sudo apt-get install -y nvidia-367

以下のshを作成

Claymore9.7/miningStarter.sh
#!/bin/sh

workername1=`hostname`
workername2=`date +"%y%m%d%H%M%S"`

#export GPU_FORCE_64BIT_PTR=0 # must be comment out for amdgpu-pro
export GPU_MAX_HEAP_SIZE=100
export GPU_USE_SYNC_OBJECTS=1
export GPU_MAX_ALLOC_PERCENT=100
export GPU_SINGLE_ALLOC_PERCENT=100

export ETH_ADDR=0x0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X0X # Ether入金アドレス
export ETH_WORKER_NAME=$workername1$workername2 # ワーカーの名前=ホスト名+起動日時
export ETH_POOL_HOST=us1.ethermine.org:4444 #AWSリージョンと同じusを指定

cd /home/ubuntu/Claymore9.7/ #SHおいたとこ

./ethdcrminer64 
  -epool $ETH_POOL_HOST 
  -ewal $ETH_ADDR.$ETH_WORKER_NAME 
  -epsw x 
  -mode 0 
  -ftime 10 
  -etha 2 
  -allpools 1 
  -wd 0 
  -eres 4 
  -gser 2

で、sh 起動、なんかうごく。

console
sudo bash miningStarter.sh

image.png

こちらで Ethereum入金アドレス で検索すると、進捗が確認できる。
ethermine.org – The fastest way to mine Ether

ためしに採掘&オンデマンドでの結果

数時間後…
image.png

ちゃんと掘れてるみたいですが、いくらになったんでしょう…

  • AWS利用料(g3.xlarge@us-east-1)

    • $1.14 / H = $27.4 / Day
  • マイニング結果 23
    • 0.002659 ETH/Day (10 MH/s) = $0.8 / Day
  • 収支
    • -98%($-26.56/Day)の赤字

 オンデマンドインスタンス価格では赤字を垂れ流す結果となりました。自動起動設定をしたあと、AMI をとっておきます。

/etc/rc.local
#!/bin/sh -e
#
# rc.local
#
# This script is executed at the end of each multiuser runlevel.
# Make sure that the script will "exit 0" on success or any other
# value on error.
#
# In order to enable or disable this script just change the execution
# bits.
#
# By default this script does nothing.

bash /home/ubuntu/Claymore9.7/miningStarter.sh #SHのありか

exit 0
  • AMI-ID

    • ami-0X0X0X0X

スポットインスタンスで起動まで

 バージニア北部のここ一週間のスポットインスタンスの価格設定履歴より、スポットの最安値とオンデマンド比の割引率を求めました。4

インスタンスタイプ オンデマンド($/H) スポット最安($/H) 割引率
g3.4xlarge 1.14 0.23 80%
g3.4xlarge 2.28 0.4 82%
g3.4xlarge 4.56 1.04 77%
p2.xlarge 0.9 0.1 89%
p2.8xlarge 7.2 1 86%
p2.16xlarge 14.4 2.2 85%

 g3とp2はどちらがマイニングに適しているのか計測しないとわかりませんが、とりあえず「p2.xlarge」でいいかなと感覚で決め、自動入札とAutoScaleの設定をします。この割引率を見る限り、負けが見えていますが。。。

  • 起動設定:MiningLaunchConfig

    • インスタンスタイプ

      • p2.xlarge
    • スポット価格
      • $ 0.1
    • AMI ID
      • ami-0X0X0X0X
  • AutoScalingGroup設定:MiningAutoScaleGroup

    • 起動設定

      • MiningLaunchConfig
    • 希望
      • 3
    • 最小
      • 3
    • 最大
      • 3

 これで、スポットインスタンスが 0.1 以下の場合自動で入札し、その価格帯であれば常時3台 起動する設定ができました。

勝手に起動する!
image.png

勝手に採掘する!
image.png

無限にお金が、、、!!

最終結果

  • AWS利用料(p2.xlarge@us-east-1)

    • $0.098 / H = $2.35 / Day
  • マイニング結果(1台あたり)
    • 0.003244 ETH/Day (12.2 MH/s) = $ 0.97 / Day
  • 収支
    • -59%($-1.38/Day)の赤字

 無限にお金が減っていく!
 残念ながら、スポットインスタンスでも無限の収入は実現できませんでした。もっと値下げしてくれれば黒字転換するんですが、そんなうまい話ないですよね。
 無念!!

参考リンク

イーサリアムを効率良くマイニングできるClaymore’s Dual Minerの使い方・設定 | トレードステーションと株・FX自動売買で暮らす
技術者向け Ethereumの基礎知識 (イーサリアム、エセリウム) – Qiita
【2017年度版】イーサリアム(ETH)でお金を稼ごう! ~マイニング編~ | デジモノ達人
イーサリウムを GPU マイニングしてみる(2017/04/21 時点) – Qiita
GPU関連でよく使うコマンドまとめ – Qiita
ETH-USD電卓:ETH/USD Ethereum Price Calculator | CoinGecko
Mining収支電卓:Ethereum Mining Profitability Calculator
採掘量確認:Balances – ethermine.org – The fastest way to mine Ether

構築時エラー集

ocl-icd-opencl-dev ないとエラー

ethdcrminer64
./ethdcrminer64: error while loading shared libraries: libOpenCL.so.1: cannot open shared object file: No such file or directory

libcurl3 ないとエラー

ethdcrminer64
./ethdcrminer64: error while loading shared libraries: libcurl.so.4: cannot open shared object file: No such file or directory

nvidiaのドライバ ないとエラー

ethdcrminer64
ETH: 1 pool is specified
Main Ethereum pool is asia1.ethermine.org:4444
DCR: 4 pools are specified
Main Decred pool is pasc-eu2.nanopool.org:15555
AMD OpenCL platform not found 
No NVIDIA CUDA GPUs detected.
No AMD OPENCL or NVIDIA CUDA GPUs found, exit

補足


  1. GPU は Windows に最適化されているものが多く、マイナーの世界では Windows を使うのがメジャーみたいです 

  2. 1 ETH = $ 299.96 で計算しています 

  3. 数時間しか稼働させていないので、インスタンスタイプにおける MH/s は正しい平均値が取れていません 

  4. 2017/8 時点 

続きを読む

ElasticseharchのEC2 Discovery Pluginでクラスタ組むのが捗るはなし

こんばんはー

今回、ElasticsearchのクラスタをAWS上に組む際に便利なブラグインを使ってみたいと思いますー
その名も「EC2 Discovery Plugin」☆彡

調べているとAWS Cloud Pluginの使い方を書いている人は結構いるのですが、5系から提供されているEC2 Discovery Pluginはあんまいないのかなーって思いました。
なので、EC2 Discovery Pluginについてつらつら書きたいと思います( ゚Д゚)ゞビシッ

そもそもEC2 Discovery Pluginは何が便利なん?

Elasticsearchのデフォルトで用意されているZen Discoveryを利用してクラスタを組む際は、Unicastでホスト名やIPアドレス指定で組みます。
ただ、クラウド上の運用を考えるとUnicastはつらい。。
そこで、EC2 Discovery Pluginを使うとSecurityGroupを元にクラスタが組まれるのですー(d゚ω゚d)オゥイェー
Multicast Discovery pluginというのもあったのですが、5系から廃止されました

早速ですが、構成の説明したら実装方法を説明したいと思いますー

インスタンス構成

  • InstanceType:t2.medium
  • OS:Amazon Linux AMI release 2017.03
  • Node: 3台
  • ClusterName: es-cluster

こんな感じの流れで書いちゃいますー

  1. AWS準備

  2. Install Oracle JDK 1.8

  3. Install Elasticsearch

  4. Install Kibana(1台のみ)

  5. Install X-pack Elasticsearch & kibana(1台のみ)

  6. Install EC2 Discovery Plugin

  7. 動作確認

AWS準備

SecurityGroup

ElasticsearchをインストールするインスタンスにアタッチするSecurityGroupです。
ソースは、環境に合わせて設定してください。

Type Protocol Port Source Description
Custom TCP 9200 xx Elasticsearchアクセスポート
Custom TCP 9300 xx ノード間のコミュニケーションポート
Custom TCP 5601 xx KibanaのHTTP用ポート
Custom TCP 22 xx SSH用ポート

IAM Role

以下のPolicyを作成し、インスタンスにアタッチしているIAM RoleにPolicyをアタッチしてください。

{
    "Statement": [
        {
            "Action": [
                "ec2:DescribeInstances"
            ],
            "Effect": "Allow",
            "Resource": [
                "*"
            ]
        }
    ],
    "Version": "2012-10-17"
}

AWS Configure

サーバにログインしたらAWS Configureの実行をします。
Access KeyとSecret keyは利用しないため、何も入力しません。

$ aws configure
AWS Access Key ID [None]:
AWS Secret Access Key [None]:
Default region name [None]: ap-northeast-1
Default output format [None]: json

下準備が終わったので、ここからElasticsearch周りを進めたいと思いますー

Install Oracle JDK 1.8

$ java -version
java version "1.7.0_141"
OpenJDK Runtime Environment (amzn-2.6.10.1.73.amzn1-x86_64 u141-b02)
OpenJDK 64-Bit Server VM (build 24.141-b02, mixed mode)

### Install Java1.8
$ sudo yum -y install java-1.8.0-openjdk-devel

### alternativesコマンドでJavaのバージョンを切り替える
$ sudo alternatives --config java

There are 2 programs which provide 'java'.

  Selection    Command
-----------------------------------------------
*+ 1           /usr/lib/jvm/jre-1.7.0-openjdk.x86_64/bin/java
   2           /usr/lib/jvm/jre-1.8.0-openjdk.x86_64/bin/java

Enter to keep the current selection[+], or type selection number: 2

### バージョン確認
$ java -version
openjdk version "1.8.0_141"
OpenJDK Runtime Environment (build 1.8.0_141-b16)
OpenJDK 64-Bit Server VM (build 25.141-b16, mixed mode)

Install Elasticsearch

Elasticsearch5.5.1を入れちゃいたいと思いますー

Install Elasticsearch

### PGP Keyをダウンロード&インストール
$ sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

### リポジトリの登録
$ sudo vim /etc/yum.repos.d/elastic.repo
[elasticsearch-5.x]
name=Elasticsearch repository for 5.x packages
baseurl=https://artifacts.elastic.co/packages/5.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md

### Elasticsearchインストール
$ sudo yum -y install elasticsearch

### サービス起動登録
$ sudo chkconfig --add elasticsearch

### 確認
$ sudo chkconfig --list | grep elasticsearch
elasticsearch   0:off   1:off   2:on    3:on    4:on    5:on    6:off

### elasticsearch.yml設定変更
$ sudo vim /etc/elasticsearch/elasticsearch.yml
+ cluster.name: es-cluster
+ network.host: 0.0.0.0
+ http.port: 9200

### サービス起動
$ sudo service elasticsearch start
Starting elasticsearch:                                    [  OK  ]

### 起動確認
$ curl http://localhost:9200
{
  "name" : "fDpNQ4m",
  "cluster_name" : "es",
  "cluster_uuid" : "mayxoDENThSmrUltkXyRWg",
  "version" : {
    "number" : "5.5.1",
    "build_hash" : "19c13d0",
    "build_date" : "2017-07-18T20:44:24.823Z",
    "build_snapshot" : false,
    "lucene_version" : "6.6.0"
  },
  "tagline" : "You Know, for Search"
}

Install Kibana

モニタリングしたいのでKibana5.5.1をインストールします。

### Install Kibana
$ sudo yum install -y kibana
### サービス登録
$ sudo chkconfig --add kibana
$ chkconfig --list | grep kibana
kibana          0:off   1:off   2:on    3:on    4:on    5:on    6:off
### Kibana設定
$ sudo vim /etc/kibana/kibana.yml
+ server.host: 0.0.0.0
+ elasticsearch.url: "http://ES_IP_ADDR"
### サービス停止状態のままにしておきます(X-Pack入れたあとに設定変更後に起動します)
$ service kibana status
kibana is not running

Install X-Pack on Elasticsearch

Elasticsearchのノード状態などをモニタリングしたいため、X-Pack5.5をインストールします。

Install X-Pack on Elasticsearch

$ sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install x-pack
-> Downloading x-pack from elastic
[=================================================] 100%  
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@     WARNING: plugin requires additional permissions     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
* java.io.FilePermission \.pipe* read,write
* java.lang.RuntimePermission accessClassInPackage.com.sun.activation.registries
* java.lang.RuntimePermission getClassLoader
* java.lang.RuntimePermission setContextClassLoader
* java.lang.RuntimePermission setFactory
* java.security.SecurityPermission createPolicy.JavaPolicy
* java.security.SecurityPermission getPolicy
* java.security.SecurityPermission putProviderProperty.BC
* java.security.SecurityPermission setPolicy
* java.util.PropertyPermission * read,write
* java.util.PropertyPermission sun.nio.ch.bugLevel write
* javax.net.ssl.SSLPermission setHostnameVerifier
See http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html
for descriptions of what these permissions allow and the associated risks.

Continue with installation? [y/N]y
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@        WARNING: plugin forks a native controller        @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
This plugin launches a native controller that is not subject to the Java
security manager nor to system call filters.

Continue with installation? [y/N]y
-> Installed x-pack

動作確認するのですが、Securityが有効になったため、エラーが返ってきます。
なので、今回は、Securityを無効にします。
(モニタリング用途で使いたいだけなので―)

ちなみに、X-Packについては、クラスメソッドさんが詳しく書いてますー
クラスメソッド:Elastic Stack X-Pack

X-Pack Security無効化

無効化することで、レスポンスが返ってきます。

### Security無効化
$ vim /etc/elasticsearch/elasticsearch.yml
+ xpack.security.enabled: false

### 動作確認
$ curl -u elastic http://localhost:9200
{
  "name" : "fDpNQ4m",
  "cluster_name" : "es",
  "cluster_uuid" : "mayxoDENThSmrUltkXyRWg",
  "version" : {
    "number" : "5.5.1",
    "build_hash" : "19c13d0",
    "build_date" : "2017-07-18T20:44:24.823Z",
    "build_snapshot" : false,
    "lucene_version" : "6.6.0"
  },
  "tagline" : "You Know, for Search"
}

### 再起動
$ service kibana restart
kibana stopped.
kibana started

Install X-Pack on Kibana

KibanaのX-Pack5.5もインストールします。

Install X-Pack on Kibana

$ sudo /usr/share/kibana/bin/kibana-plugin install x-pack
Attempting to transfer from x-pack
Attempting to transfer from https://artifacts.elastic.co/downloads/kibana-plugins/x-pack/x-pack-5.5.1.zip
Transferring 119276972 bytes....................
Transfer complete
Retrieving metadata from plugin archive
Extracting plugin archive
Extraction complete

Install EC2 Discovery Plugin

ここからEC2 Discovery Pluginをインストールします。
やっとここまできましたね!

$ sudo /usr/share/elasticsearch/bin/elasticsearch-plugin install discovery-ec2
-> Downloading discovery-ec2 from elastic
[=================================================] 100%  
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@     WARNING: plugin requires additional permissions     @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
* java.lang.RuntimePermission accessDeclaredMembers
* java.lang.RuntimePermission getClassLoader
See http://docs.oracle.com/javase/8/docs/technotes/guides/security/permissions.html
for descriptions of what these permissions allow and the associated risks.

Continue with installation? [y/N]y
-> Installed discovery-ec2

Elasticsearch.ymlに設定します。

$ sudo vim /etc/elasticsearch/elasticsearch.yml
cluster.name: es-cluster
discovery.zen.hosts_provider: ec2
discovery.ec2.groups: "SECURITY_GROUP_NAME" or "SECURITY_GROUP_ID"
discovery.ec2.availability_zones: [ "ap-northeast-1a", "ap-northeast-1c" ]
cloud.aws.region: ap-northeast-1

### サービス再起動
service elasticsearch restart

### クラスタ状態確認
$ curl http://localhost:9200/_cluster/health?pretty
{
  "cluster_name" : "es-cluster",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 1,
  "number_of_data_nodes" : 1,
  "active_primary_shards" : 0,
  "active_shards" : 0,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

2台目&3台目いくよ!!

2台目も上記と同じで設定を実施しますので割愛しちゃいますm(_ _)m
SecurityGroupは、1台目と同じのを使ってください。
また、ElasticsearchのX-Packは、3台すべてに入れてください。
Kibanaは、インストールしないです。

もろもろ設定して、Elasticsearchを再起動するとログにクラスタが組み込まれたことがわかります。

クラスタに組み込まれているかをログから確認

### Master側のログ
[20xx-xx-xxxx:xx:xx][INFO ][o.e.c.s.ClusterService   ] [fDpNQ4m] added {{gdRR6r1}{gdRR6r17T5iaGPmaO9wgTA}{6l3QNArhSkyBVu08jTfwYQ}{IP_ADDR}{IP_ADDR:9300},}, reason: zen-disco-receive(from master [master {dKgmQfLLg}{IP_ADDR}{IP_ADDR:9300} committed version [15]])

### Node側のログでマスタを検出したことがわかる
$ tail -f /var/log/elasticsearch/es.log
[20xx-xx-xxxx:xx:xx][INFO ][o.e.c.s.ClusterService   ] [gdRR6r1] detected_master {dKgmQfL}{dKgmQfLASM-35x0X0ulSXg}{XE4jgXEzQwqgkSfRypgoLg}{IP_ADDR}{IP_ADDR:9300}, added {{fDpNQ4m}{fDpNQ4mrRR2vN7wiTVjfIg}{aqPbmbhbTkm1X40gH1tylw}{IP_ADDR}{IP_ADDR:9300},{dKgmQfL}{dKgmQfLASM-35x0X0ulSXg}{XE4jgXEzQwqgkSfRypgoLg}{IP_ADDR}{IP_ADDR:9300},}, reason: zen-disco-receive(from master [master {dKgmQfL}{dKgmQfLASM-35x0X0ulSXg}{XE4jgXEzQwqgkSfRypgoLg}{IP_ADDR}{IP_ADDR:9300} committed version [15]])

Kibanaのモニタリングで確認するよ

Kibana [http://KIBANA_IP_ADDR:5601] にアクセスします。

「Monitoring」をクリックし、ElasticsearchにNodeが3台あることがわかりますね。
ステータス:Green

kibana01.png

「Nodes」をクリックし、Nodeの状態を確認できます。

kibana02.png

無事にクラスタを組むことができましたヽ(*゚д゚)ノ

補足#01

AWS Configureの設定とIAM Roleにポリシーをアタッチしないと以下のエラーがでます。
ちなみに、作成したクラスタ名でログが作成されちゃいます。

$ sudo /var/log/elasticsearch/es-cluster.log
[20xx-xx-xxxx:xx:xx][INFO ][o.e.d.e.AwsEc2UnicastHostsProvider] [node_id] Exception while retrieving instance list from AWS API: Unable to execute HTTP request: connect timed out

補足#02

既に存在しているNodeIDのAMIから起動すると、NodeIDがかぶっているためクラスタに組み込むことができないです。。
その際のログが以下です。

### AMIから起動したノード
$ tail -f /var/log/elasticsearch/es.log
[20xx-xx-xxxx:xx:xx][INFO ][o.e.d.z.ZenDiscovery     ] [gdRR6r1] failed to send join request to master [{fDpNQ4m}{fDpNQ4mrRR2vN7wiTVjfIg}{eHfV5HLkRrKo8_FXfgyHDA}{IP_ADDR}{IP_ADDR:9300}{ml.enabled=true}], reason [RemoteTransportException[[fDpNQ4m][IP_ADDR:9300][internal:discovery/zen/join]]; nested: IllegalArgumentException[can't add node {gdRR6r1}{gdRR6r17T5iaGPmaO9wgTA}{hzzXQXB8TM-xQVn9Uj8e2A}{IP_ADDR}{IP_ADDR:9300}{ml.enabled=true}, found existing node {gdRR6r1}{gdRR6r17T5iaGPmaO9wgTA}{9pWjYpL5Tq23yIte7WzMiw}{IP_ADDR}{IP_ADDR:9300}{ml.enabled=true} with the same id but is a different node instance]; ]

さいごに

EC2 Discovery Pluginいかがでしたか?
簡単にクラスタを組むことができたんじゃないかなと思います。

ただ、個人的には、運用を考えると自動復旧できる構成にする必要があると思ってます。
今の状態だとElasticsearchのクラスタ化する時にNodeIDがかぶっちゃうので、AMIからの自動復旧がむずかしい状態です。
なので、Elasticsearchをインストールする前のAMIからプロビジョニングして、クラスタに組み込む必要があるかなと。
(Elasticsearchインストール時にNodeIDが振られる)

うーん。。NodeIDを変更する方法が他にあればいいのですが、誰か知っている人いたら教えてくださいm(_ _)m

てことで、今回は、ElasticsearchのプラグインのEC2 Discovery Pluginについて書かせて頂きました。
ありがとうございましたー

続きを読む

Amazon Inspectorを使ってみたメモ

Amazon Inspectorを使ってみた

業務で脆弱性診断ツールの選定を任されたので、実際に使ってみた
(使ってみないと何がいいのかわからんよね)

サマリ

  1. IAMロール作成
  2. EC2インスタンスへのタグつけ
  3. AWSエージェントをインストール
  4. 評価ターゲットの定義
  5. 評価テンプレートの定義
  6. 評価の実行

IAMロールを作成

Amazon InspectorがAWS EC2インスタンスのタグ情報を取得できる必要があるため、
下記権限で作成する

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "ec2:DescribeInstances"
            ],
            "Resource": [
                "*"
            ]
        }
    ]
}

EC2インスタンスへのタグつけ

EC2インスタンスに対して適当なタグをつける

AWSエージェントをインストール

脆弱性診断対象のEC2インスタンスに対してSSHし、エージェントをインストールする

[ec2-user@ip-10-0-1-41 ~]$ sudo su -
Last login: Wed Aug  2 22:31:53 JST 2017 on pts/0
[root@ip-10-0-1-41 ~]# cd /usr/src/
[root@ip-10-0-1-41 src]# wget https://d1wk0tztpsntt1.cloudfront.net/linux/latest/install
:
:
install                                            100%[================================================================================================================>]  26.60K  --.-KB/s    in 0s      

2017-08-12 15:40:42 (269 MB/s) - ‘install’ saved [27238/27238]

[root@ip-10-0-1-41 src]# 
[root@ip-10-0-1-41 src]# bash install -u false
Forced update specified as argument is : false
Distribution of the machine is Amazon Linux AMI.
Distribution type of the machine is amzn.
:
:
Current running agent reports version as: 1.0.1041.1
This install script was created to install agent version:1.0.1041.1
In most cases, these version numbers should be the same.
[root@ip-10-0-1-41 src]# 

評価ターゲットの定義

ここで、
1. どのインスタンスに対して診断をするのか(複数可)
2. 対象インスタンスのまとまりに対して名前つけ
を実施する
今回はこんな感じにしてみた
image.png

評価テンプレートの定義

何をもってして評価するのかをここで定義する。具体的には、
1. 評価基準(ルールテンプレートなるもの)
2. 診断時間(15分〜24時間)
3. 上記設定をした評価基準に対しての名前つけ
を実施する。今回はこんな感じにしてみた
image.png

評価の実行

1〜5までの設定が全て完了したら最後は実行するのみ!
image.png
※すでに実行後なため実行ボタンが選択できなくなっています。。。

結果などがでてきたらまた続きをかきたいと思います!

続きを読む

EC2(スポット)インスタンス上でChainerMNのマルチノード分散学習

EC2(スポット)インスタンスでChainerMNを使う(マルチノード分散学習)

概要

  • EC2(スポット)インスタンスでChainerMNのマルチノード分散学習をする方法

    • 環境変数の設定方法
    • sshにStrictHostChecking noを追加
    • セキュリティグループの設定(VPC内からの全アクセスを許可)
  • EC2上でマルチノード分散学習する場合の注意点
    • p2.xlargeを使ってマルチノード分散学習は性能がでない
    • g3.4xlargeを利用すると良い
  • マルチノード学習した際の性能の簡単な評価
    • ImageNetの学習ではp2.8xlargeを使う時と同等かそれ以上のコストパフォーマンス

やりたかったこと

スポットインスタンスの価格が比較的安いGPU1個のインスタンス(p2.xlargeg3.4xlarge)を複数使って、ディープラーニングの学習を高速化させたかった。

学習を高速にする手段としては、マルチノードで分散する以外に、そもそも1台あたりのGPU数を増やす、という選択肢もある。
しかし、GPUを複数個積んでいるEC2のインスタンスはどれも高いし、スポットインスタンスで価格を抑えられないことがある。例えば、p2.8xlargeはオンデマンドインスタンスの場合、\$7.2/hかかる。スポットインスタンスの価格は、ここ1週間くらいはp2.8xlargeが\$2.5/h弱のようだが、ちょっと前は\$72/hに張り付いていた。
あるいは、自前で学習用計算機用意する手もあるが、GPU複数台積むマシンとなるとかなり高くつくことになる。個人の趣味の範囲内だと、電気代を抜いてもAWSを使うより高くなる可能性が高そう。

なので、p2.xlargeなどのスポットインスタンスでの値段が低め(〜\$0.3/h)で抑えられているインスタンスを複数利用して、学習を高速化させるという方針に至った。オンデマンドのp2.8xlargeと比べて、スポットインスタンスのp2.xlargeg3.4xlargeは1GPU当たりの値段で1/3ほどなので、マルチノードの分散学習の複雑さや効率の悪さはGPUの台数で補えるという目論見。

ChainerMNを使った分散学習 in AWS

環境の準備

ChainerMNのインストール

ChainerMNをインストールする方法自体は、もう多数の記事・情報があるので、詳細は省く。自分はここChainerMNのチュートリアルを参考にした。
やったことを列挙すると、以下の通り。

  • CUDA 8.0のインストール
  • cuDNN 6.0のインストール
  • NCCL 1.xのインストール
    • GitHubのページにはno longer maintainedとあるが、まだNCCL2は使えかった
  • OpenMPIのビルド・インストール
  • Chainer、ChainerMNのインストール

この作業はGPUを積んでいる中で安いインスタンス(p2.xlarge)を利用すると良い。

環境変数の設定

sshに非対話モードで入った時に、CPATHLD_LIBRARY_PATHが適切な値(具体的にはcudaのパスを含む)になっていないと学習スクリプトがうまく動かない。
/etc/bash.bashrcを以下のようにした。

/etc/bash.bashrc
export PATH=/usr/local/cuda-8.0/bin:${PATH}
export LD_LIBRARY_PATH=/usr/local/cuda-8.0/lib64:${LD_LIBRARY_PATH}
export CPATH=/usr/local/cuda-8.0/targets/x86_64-linux/include:${CPATH}

以下のコマンドを叩いた時、PATHLD_LIBRARY_PATHが適切に設定されていれば良い。

$ ssh localhost 'env'

sshの設定

マルチノード分散学習をする際、インタラクティブな操作なしに別ノードへsshで接続できる必要がある。したがって、鍵認証の設定をする。また、デフォルトでは最初に接続しようとすると、Are you sure you want to continue connecting (yes/no)?のメッセージが出て、yes/noの入力を求められるので、手間を考えるとこれも対処する必要がある。

まず、鍵認証の設定をする。

$ ssh-keygen #パスフレーズなし、~/.ssh/id_rsaに置く
$ cat ~/.ssh/id_rsa.pub >> ~/.ssh/authorized_keys

次に、.ssh/configを以下のとおりにして、yes/no入力をなくす

~/ssh/.config
Host *
    StrictHostKeyChecking no

どちらもセキュリティ上良いとは言えないが、最終的にはAWSのセキュリティグループで外部ネットワークからのインバウンドを遮断して運用すれば許容範囲と思っている。

ENAの有効化

必要なのかはわからないが、UbuntuはデフォルトではENAが有効になっていないようだったので、有効にする。最新の手順はここにあるので、これの通りに行う。
やるべきことは以下の3つ

  1. インスタンス上で、ENAのモジュールを追加
  2. インスタンスを停止
  3. ローカルからaws CLIでENAを有効化

AWS上のリソースの準備

1. VPC、サブネット、プレイスメントグループの準備

それぞれ適当な名前で準備する。VPCとサブネットは一度EC2インスタンスを起動すればついでにできるし、プレイスメントグループは、EC2のコンソールから、ネットワーク&セキュリティ → プレイスメントグループのページに行って作成すれば良い。
なお、プレイスメントグループはいるのかどうか分からないが、ネットワークの帯域幅をフルに出すには必要らしいので自分は作成した。

2. 学習ノード用のセキュリティグループの準備

セキュリティグループの準備も必要。インバウンドルールでは、「すべてのトラフィック・すべてのポート範囲へのVPCからのアクセス」を許可する。本来はもっと絞りこめると思うが、調べるのが面倒だったのでVPC内に全部公開した。
EC2コンソール上では、すべてのトラフィック すべて 0-65535 カスタム <VPCのCIDR>となっていれば良い。

3. (Optional) AMIの作成

必要はないが、ここまで終えた時点でAMIを作っておくと別のことをしたい時に無駄な出費を防げる。
AMIの作成方法は省略。

学習スクリプトなどの準備

最後に、学習用のスクリプト、データセットなどを準備する。

今回、自分はchainermnについているImageNetのサンプルを使った。
git clone --depth 1 https://github.com/chainer/chainermn.gitとして、chainermnのソースを落とすとchainermn/examples/imagenetの下にImageNetのサンプルがあるのでこれを用いる。また、自分の場合、models_v2/nin.pyをchainerのexamples/imagenet/nin.pyに置き換えないと動かなかったので、chainerのソースも落としてきてcpした。

次に、データセットを準備する。データセットの準備方法は、ここここなどが参考になる。

ここまで終えたら、インスタンスを止めてAMIを作成する。

実行方法(1ノード)

テストも兼ねて1ノードで学習を走らせる場合は、インスタンスを起動した後、sshでログインして、

$ mpiexec -n 1 python3 ~/chainermn/examples/imagenet/train_imagenet.py train.txt test.txt

などとすれば良い。ここで、train.txt、test.txtはそれぞれ準備したデータセットのパス

参考: ChainerMN チュートリアル

実行方法(マルチノード)

上で作成した学習スクリプトの入ったAMIを利用し、スポットインスタンスを適当に何個か立ち上げる。この時、VPC、プレイスメントグループ、セキュリティグループは上で準備したものを忘れず利用する。
なお、別にスポットインスタンスでなくてもいいが、費用を抑えて実験してみたいだけならスポットインスタンスの方が適していると思う。ただし、スポットインスタンスが突然中断するリスクを減らすため、高めに価格を設定しておくと安心。

また、多少値段は上がるが、p2.xlargeでなく、g3.4xlargeを使うと良い (理由は”注意点”で後述)。

以下では、2台のg3.4xlargeインスタンスを立ち上げ、それぞれのプライベートIPが172.31.41.13172.31.41.14となったとする。
まず、どちらか1台(以下では172.31.41.13の方とする)にsshでログインする。ログインしたら、以下の内容のホストファイルを~/hostfileに作成する(パスはどこでも良い)。

~/hostfile
172.31.41.13 cpu=1
172.31.41.14 cpu=1

(プライベートIPは、その時立ち上げたスポットインスタンスを見て適宜修正する必要あり。)

次に、以下のコマンドを叩くと、2台のマシンで分散学習される。

$ mpiexec -n 2 --hostfile ~/hostfile python3 ~/chainermn/examples/imagenet/train_imagenet.py train.txt test.txt

参考: ChainerMN チュートリアル

注意点(ネットワークの帯域幅を考慮する必要あり)

GPU付きインスタンスの中ではp2.xlargeが値段は安いのだが、ネットワークの帯域幅が小さく、性能が出なかった。iperfを使ってはかった結果では、1.44Gbps。一方、g3.4xlarge10Gbpsでるというスペックだし、実際iperfではかると10Gbpsでた(情報提供:https://twitter.com/grafi_tt/status/895274632177000449 )。

いくら安く分散学習させたいと言っても、p2.xlargeだと性能向上が見られなかったので、g3.4xlargeを使う方が良いと思う。

性能確認

学習が高速化できるのか確認するため簡単な性能測定をした。なお、どれも1回しか計測してないし、真面目に条件を揃えたわけではないので、数字は参考程度に。

以下のパターンで、ImageNetの学習にかかる時間を測定した。

  1. g3.4xlarge1台で、ChainerMNを利用
  2. g3.4xlarge複数台(2, 4, 6, 8, 10, 12)で、ChainerMNを利用
  3. p2.8xlarge(8GPU)で、ChainerMNを利用

結果

以下の通り。
分散すればちゃんと高速化されるし、p2.8xlargeと比べても安いまたは同等程度の値段でほぼ同じ性能を出せている。ただ、この辺は学習させるネットワークやデータセットによって色々異なるんだろうな。

表1: 1エポック当たりの時間

条件 1エポックあたりの平均時間 (sec)
g3.4xlarge*1 34.4
g3.4xlarge*2 21.8
g3.4xlarge*4 12.5
g3.4xlarge*6 9.2
g3.4xlarge*8 7.9
g3.4xlarge*10 6.3
g3.4xlarge*12 5.2
p2.8xlarge 7.9

ちゃんと分散するにつれて短い時間で学習できている。


表2: 値段 – 総実行時間

条件 値段 (\$/h) 総実行時間 (sec)
g3.4xlarge*1 0.3 344.3
g3.4xlarge*2 0.6 217.8
g3.4xlarge*4 1.2 125.2
g3.4xlarge*6 1.8 92.4
g3.4xlarge*8 2.4 79.2
g3.4xlarge*10 3.0 63.0
g3.4xlarge*12 3.6 51.7
p2.8xlarge 7.2(オンデマンド) / 2.5(スポットインスタンス利用時) 79.1

備考:g3.4xlargeのスポットインスタンスの値段は\$0.3/hとして計算

p2.8xlargeをオンデマンドで利用する場合に比べると、より安く高速な学習ができる。p2.8xlargeがスポットインスタンスの場合と比べても、ほぼ同等の性能が今回の例では出た。


グラフ1: epoch – elapsed_time
graph1.png


グラフ2: epoch-validation/main/accuracy
graph2.png

epochが少なすぎてわかりやすいデータにならなかったが、分散させるほど同エポックでの精度は悪化する傾向にあるらしい。直感的にもそんな気はする。とはいえ、マルチノードの場合とp2.8xlargeでノード内で分散した場合では大きな精度の差は見つけられない。分散学習するなら、エポックを大きめに設定する必要があるようだが、それはマルチノード分散学習の問題というより、現在のChainerMN全体の問題の可能性が高い。


その他備考
分散学習では、最初の1回のmpiexecは時間がかかるらしい。上記計測は、2回目のmpiexecで行っている。原因は、ノード間の接続を確立する時間が追加されているからではないかと思うが、詳細は不明。ただし、学習時間が長くなるにつれて、その時間は無視できるものになると思われる。

まとめとか

少なくともImageNetでは、マルチノードの分散学習でも相当の学習時間の短縮が見込める。また、8/7からChainerMNを初めて5日でここまでできたので、非常に難しい作業が必要というわけでもない。
そのため、AWS上でのディープラーニング学習を高速化させたい時、選択肢に入れる価値はあると思う。最初に書いたような、複数GPUを積んだスポットインスタンスが高い時にも使えるし、あるいはp2.8xlargeを複数使ってさらに高速化する、という使い方もマルチノードの分散学習はできるはず。

一方で、データセットが増えた時どうなるのか、モデルが複雑になった時どうなるのか、などは調べてない。実際に使ってみるとたいして高速化されなかった、みたいなケースはありそう。

要改善点

とりあえずテストするだけなら上記手順でもできたが、実際にディープラーニングを利用するプロジェクトに組み込むとなると以下の点を改善しないといけない。

学習スクリプトの実行方法

本来は、aws CLIとかSDKからスポットインスタンスを立ち上げて、自動で学習を回したい(ここみたいに)。
そのためには、UserDataのスクリプトで学習スクリプトを実行する必要があるが、以下の点に注意が必要。

  1. mpiexecをするインスタンスの決定方法
  2. ホストファイルの作成方法
  3. すべてのインスタンスが立ち上がるまでの待ち合わせ処理

1については、特定のタグを全インスタンスに付けておき、aws ec2 describe-instancesで全インスタンスのプライベートIPを取得、辞書順最小のインスタンスでmpiexecすれば解決しそう。
2は、describe-instancesした時に全部のプライベートIPがわかるんだからホストファイルもついでに生成できる。
3は、ポーリングなりなんなりでやればできるはず。この時、ついでに学習パラメータの環境変数への展開やS3からデータセットのダウンロードも待ち合わせ処理すると色々便利そう。

中断時の対処

スポットインスタンスなので、たまに強制終了させられることがある。

  1. 定期的なS3へのスナップショットアップロード(systemd-timer)
  2. 1台でも終了したら全台終了して無駄な出費の削減
  3. 学習開始時にスナップショットがあればそれを読み込み

の3つの対処が必要。

続きを読む

dynamodb-autoscaling検証

概要

先日のアップデートでdynamodbでautoscalingの機能が実装されました。
https://aws.amazon.com/jp/blogs/news/new-auto-scaling-for-amazon-dynamodb/

NoSQLデータベースのベンチマークツールであるycsbを用いてdynamodb-autoscalingの動作を検証できます。
今回は新規に追加された項目「ターゲット使用率」がどのような影響を及ぼすかについて検証します。

環境準備

環境準備に伴い、下記を参考にさせていただきました。

http://qiita.com/noralife/items/35a956f682b1aca475f6
http://dev.classmethod.jp/cloud/aws/attack_to_dynamodb_using_ycsb/
http://imai-factory.hatenablog.com/entry/2013/04/05/010858

amazon linux version

[ec2-user@dynamo-ec2 ycsb-0.12.0]$ cat /etc/system-release
Amazon Linux AMI release 2017.03

openjdk-develインストール

$ sudo yum -y install java-1.7.0-openjdk-devel

ycsb導入

・ダウンロード
$ wget https://github.com/brianfrankcooper/YCSB/releases/download/0.12.0/ycsb-0.12.0.tar.gz

・展開、移動
$ tar xfz ycsb-0.12.0.tar.gz
$ cd ycsb-0.12.0/

・ファイル用のディレクトリ準備
$ mkdir -p dynamodb/conf/

・ベンチマーク対象テーブル(testtable)のプライマリキーを確認しておく
$ aws dynamodb describe-table --table-name testtable | jq -r '.Table.AttributeDefinitions[].AttributeName'
name

YCSB概要

テストデータをdynamodbに読み込ませて

./bin/ycsb.sh load dynamodb -P workloads/dyamodb -P dynamodb/conf/dynamodb.properties

テストデータを使って負荷をかける。

./bin/ycsb.sh run dynamodb -P workloads/dyamodb -P dynamodb/conf/dynamodb.properties

設定ファイルは下記の3つ

■dynamodb/conf/dynamodb.properties
対象のDynamoDBの情報を設定。変更する部分はプライマリキーくらいかと。
クレデンシャル情報が記載されたファイルもこの中で指定する。

$ cat dynamodb/conf/dynamodb.properties
dynamodb.awsCredentialsFile = dynamodb/conf/AWSCredentials.properties
dynamodb.primaryKey = name
dynamodb.endpoint = http://dynamodb.ap-northeast-1.amazonaws.com

■dynamodb/conf/AWSCredentials.properties
クレデンシャル情報を記載。

$ cat dynamodb/conf/AWSCredentials.properties
accessKey = XXXXXXXXXXXXXXXXXXXX
secretKey = XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

■workloads/(任意のファイル名)
投入するデータ、実施するベンチマークのステータスを設定。
値はテンプレートworkloads/workload_templateを参考。
※operationcountは1,000,000以上が良いかと。10,000で実行したらスケーリングする前に終わってしまいました……

$ cat workloads/dyamodb
workload=com.yahoo.ycsb.workloads.CoreWorkload #デフォルト

recordcount=1000 #テーブルにセットするレコード数
operationcount=2000000 #load時のオペレーション数

insertstart=0 #デフォルト
fieldcount=10 #フィールド数、デフォルト
fieldlength=100 #レコードの長さ、デフォルト

readallfields=true #デフォルト
writeallfields=false #デフォルト

table=testtable #テーブル名

fieldlengthdistribution=constant #デフォルト

#load時のオペレーション比率(read80%, update15%, insert5%)
readproportion=0.8
updateproportion=0.15
insertproportion=0.05

#以下テンプレートの値のまま設定
readmodifywriteproportion=0
scanproportion=0
maxscanlength=1000
scanlengthdistribution=uniform
insertorder=hashed
requestdistribution=zipfian
hotspotdatafraction=0.2
hotspotopnfraction=0.8

measurementtype=histogram
histogram.buckets=1000
timeseries.granularity=1000

検証開始

初期のdynamodbキャパシティ設定

1000レコード書き込むので、書き込み容量ユニット(以下WCU)を10確保。
WCU1だとスロットルが発生して時間がかかります……。

初期設定.PNG

テーブルにテストデータを読み込ませる

1回だけ。10分ほどかかります。

$ ./bin/ycsb.sh load dynamodb -P workloads/dyamodb -P dynamodb/conf/dynamodb.properties

auto-scaling設定

いよいよauto-scaling設定です。
RCU/WCUを下げるオペレーションは最大9回/日になっていますが条件があります。
↓の記事を参考にさせていただきました。
http://qiita.com/mokrai/items/6864b8a723a2728565fc
検証する場合は無駄に下げないように初期値を考慮しましょう。

as1.PNG

・ターゲット使用率
スケーリングの基準になる値です。以降で検証します。

・IAMロール
[新しいロール]を選択すれば問題ありません。
下記の管理ポリシーが付与されたロールが作成されます。

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "dynamodb:DescribeTable",
                "dynamodb:UpdateTable",
                "cloudwatch:PutMetricAlarm",
                "cloudwatch:DescribeAlarms",
                "cloudwatch:GetMetricStatistics",
                "cloudwatch:SetAlarmState",
                "cloudwatch:DeleteAlarms"
            ],
            "Resource": "*"
        }
    ]
}

ロールからもわかるように、auto-scalingはcloudwatchの値を評価の基準としています。
auto-scaling設定中は動的なcloudwatchメトリクスが作成され、auto-scaling設定解除後に削除されます。

ターゲット使用率70%の場合

下記設定でcloudwatchの推移を確認します。

auto-scaling設定 RCU WCU
ターゲット使用率(%) 70 70
min(ユニット) 10 10
max(ユニット) 10000 10000

キャパシティの推移

推移は下記の通り

RCU
70rcu.PNG

WCU
70wcu.PNG

ターゲット使用率20%の場合

下記設定でcloudwatchの推移を確認します。

auto-scaling設定 RCU WCU
ターゲット使用率(%) 20 20
min(ユニット) 10 10
max(ユニット) 10000 10000

20rcu.PNG

20wcu.PNG

終わりに

負荷のかけ方が一定の場合はキャパシティーの増減が安定したら変化しないので、
そのあたりの調整が必要だと感じました。
cloudwatchメトリクスが動的な値で作成され、それらが基準になってスケーリングされています。
サポートに確認したところ、閾値を超えてのキャパシティ設定もできるとのことですが、その場合は次のタイミングでスケーリングされてしまうとのことです(未検証)。

続きを読む

CloudGarageの特徴と料金をAWSと比較

CloudGarageとは?

CloudGarage: https://cloudgarage.jp

CloudGarageはNHNテコラス株式会社が提供する定額型パブリッククラウドです。

特徴

  • 月単位の定額課金

    • プランはCPU/RAM/SSDのスペックとインスタンス上限数で決まる
    • 選んだプランに応じたスペックのインスタンスを上限数の範囲で自由に作成・削除できる
  • データ転送が無料・無制限(10Gbps共用・各インスタンスあたり1Gbps制限)
  • ロードバランサーも無料
  • DNSサーバは10ドメインまで無料

基本的には「VPSのような料金体系のクラウド」といった具合でしょうか。請求額はプランの金額から追加されるものがないので、非常にシンプルです。

AWSと比較

CPUやSSDのスペックが不明なので単純比較は難しいのですが、参考までにカタログスペックで簡単に見比べてみましょう。

CloudGarageの無料お試し枠の1GB/BOX3インスタンスは、CPUx1、RAM 1GB、SSD 50GBです。CPUx1、RAM 1GBは、AWSではt2.micro相当です(AWSの無料お試し枠でもある)。両者を比較してみます。

CloudGarage 1GB/BOX3 AWS t2.micro(northeast-1) オンデマンドインスタンス/Linux OSの場合
料金に関する詳細ページ CloudGarage AWS EC2
課金体系 月額固定 インスタンス稼働時間単位での課金
1インスタンスの料金(/h)1 0.66円 $0.016
(2017/08/10時点で約1.76円)
CPU 不明 詳細:T2インスタンスのCPUクレジットについて
バースト機能があり瞬間的なパフォーマンスは高い
インスタンスストレージ SSD 50GBが付属
性能は不明
インスタンスストレージなし(EBSが必須)
永続ボリューム なし EBSが最低1つ必要
汎用SSD50GBの場合 $6/Mo
固定IP なし(追加予定?) EIPが使用可能
インスタンスに関連付けられている場合は無料
ネットワーク料金 無料 インは例外を除いて無料、アウトは最初の1GBは無料で以後段階的にGB辺りの請求
データ転送 10Gbps共用・各インスタンスあたり1Gbps制限 共用ネットワーク(速度の保証なし)
ロードバランサー 無料 $0.027/実行時間+$0.008/GB
イメージ イメージ10個まで無料 保存先・データ量に依存(無料枠で済む場合もままあるが一概には言えない)
CLI API公開予定 CLI有り

なお、EC2にはリザーブドインスタンスというものがあり、1年または3年の契約をすることで料金が割引されます。期間や支払い方法にも依りますが、年間で40-60%ほど割引されます。詳細はこちらを見てください。

所感

ここからは個人の感想です。

料金体系が非常にシンプルで良い

AWSは請求額を算出するのがとにかくめんどくさいです・・・。CloudGarageの場合は月額固定で他に料金がかからないので見積もりが楽です。多くの場合1インスタンスだけってことはまずないでしょうし、最低でも3インスタンスの契約になるのはあまり気にはならない気がします。

「料金とかめんどくさいことを考えたくない個人の技術者」とかのニーズにすごく合っているのではないでしょうか。VPSっぽさがあります。

月単位での契約なので、常時稼働サーバは割安の可能性が高い

言うまでもないですが、AWSを含めた様々なクラウドベンダーのオンデマンドインスタンスをフル稼働するよりかなり安いです。t2.microですら1ヶ月フル稼働すると3500円くらいになります。1インスタンスで。

AWSのリザーブドインスタンスは最低でも年契約な上、途中でインスタンスタイプを変えられないクラスもあります。月単位での契約ですので途中でスケールしたくなっても対応しやすいですし、オンデマンドよりも安いので、ある程度要求スペックが安定しているような案件ではいい感じに使えるのではないでしょうか。

性能の詳細が分からない件

CPUやSSDがどういった性能なのか公式で言及されていません。

AWSにはストレージへの最適化インスタンスや、コンピューティングへ最適化されたインスタンスなど、様々なラインナップがあり、詳細なハードウェア要件を定めています。故に、要件に合わせて最適なインスタンスを選べます。要求がシビアな場合はCloudGarageよりもAWSの方がいいでしょう。

ただ、要求スペックとかなくてとりあえずCPUとSSDとメモリさえ最低限あれば細かいことを気にしない、というのなら関係ないです。少なくとも下手なレンタルサーバを借りてホームページ作るくらいなら1GB/BOX3の方が確実に安くて便利だと思います。

複合BOXが欲しい

超個人的な意見ですが、同じスペックのまとまりのBOXじゃなくて、BOX内のインスタンスごとにスペックを変えられたらいいなあと思いました。痒いところに手が届かない印象です。雑な例ですが、8GB/BOX10とかでサービス走らせてて、監視用のインスタンスを走らせたい時に、4Core/8GB/200GBとか要らんじゃないですか。かといって別途1GB/BOX3で新しくインスタンス借りるのもなあ・・・って感じです。

料金体系がシンプルな反面、柔軟性に乏しい感じです。

とりあえず試してみては?

1GB/BOX3が無料で試せるので、privateなgit鯖とか、VPNとか、余裕があればマイクロサービスでも構築してみたい感じです。


  1. 1ヶ月辺り31日で計算・税抜き 

続きを読む

ec2-userをころす

EC2インスタンスを立てて、一通りユーザー作ったりログインの設定したりした後にセキュリティ的にec2-userというみんなが知ってるユーザーがリモートログインできると心配になることがあると思います。
そんなときの対処法です。

※他のユーザーで問題なくリモートログインできることを確認した後に行って下さい!!

ころす

$ ssh $host # EC2インスタンスにログイン
$ sudo usermod -s /sbin/nologin ec2-user

確認

$ ssh -i /path/to/key-file -l ec2-user $host # 目的のインスタンスじゃないマシンからリモートログインを試みる
Last login: Mon Aug  7 03:56:27 2017 from xxxx.xxxx.xxxx.xxxx
       __|  __|_  )
       _|  (     /   Amazon Linux AMI
      ___|\___|___|
https://aws.amazon.com/amazon-linux-ami/2017.03-release-notes/
This account is currently not available.
Connection to yyyy.yyyy.yyyy.yyyy closed.

以上です。

続きを読む