ファイル破損でAthena検索ができなくなった時の対処

とある行ログをLogstashでJSONに変換してGzip圧縮後にS3にPUTし、Athenaで検索できるようにしていた。

LogstashのS3 outputプラグインはrestoreオプションをTrue (デフォルト) にしておくと、再起動時に前回途中まで処理したログをPUTしておいてくれる。
しかし、これに何かの原因で失敗すると、S3には不完全なログファイルがPUTされることがある。
そしてこれはGzip的には破損したファイルとなる可能性がある。

検索中に破損したファイルに遭遇すると、Athenaはエラーを出す。

HIVE_CURSOR_ERROR: Unexpected end of input stream

しかし、どのファイルが破損しているのかは教えてくれない

なので、Athenaで検索をかけたい場合、restoreオプションは誠に遺憾ながらFalseにしておくのが良いのではないかと思う。
リストアされないのでログに欠落が出る可能性があるが、とはいえファイルが破損すると検索できないというトレードオフ。
この場合、例えば (今回みたいに) 1時間でローテートする設定にしておくと、最長1時間分のログが欠落する。
1時間「だけ」と考えるか、「も」と考えるかが問題だ。

まぁお金の節約のためにもパーティションとローテーションは適切に設定しておくしかない、ってそれはいつだってそう。

今回はしかたがないので、ローカルでgunzipして破損したログファイルを特定する羽目になった。

How

エラーが出たパーティション下のデータ量を調べる

aws s3 ls log-bucket/log/yearmonth=201707/ --recursive --human --sum

何とかローカルに入りきりそうだったので

aws s3 sync s3://log-bucket/log/yearmonth=201707/ .

適当なスクリプトを作ってエラーチェック

#! /bin/bash

for f in $(ls *.gz); do
  gunzip $f
  if [ $? = 0 ]; then
  rm $(basename $f .gz)
  else
  echo $f
  fi
done

どれかのファイルでUnexpected end of streamが出たりすると思う。
破損ファイルを特定したらリダイレクトで解凍できるところまで解凍する

gunzip < ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt.gz > ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt

JSON行の途中で終わってたりすると思うので、編集

vim ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt

再度圧縮

gzip ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt

アップロードしなおし

aws s3 cp ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt.gz s3://log-bucket/log/yearmonth=201707/ls.s3.1d8fc54c-4ce8-4b29-98be-f323641ae011.2017-07-10T18.00.part225.txt.gz

これで完了。yearmonth=201707パーティションが検索できるようになった。

解凍後のファイルサイズが大きすぎて編集できない場合は、諦めてDELETEするか、「きっと最終行だけがおかしいに違いない」と運を天に任せてシェルで最終行だけ削除してみるとか、何か便利なアレとかコレとか使ってファイルを修復する。

ローカルに入りきらないよ

1個ずつダウンロードして確かめるとか……

いや、ファイル1個がローカルに入りきらないんですよ

そんな巨大な単一ログファイルをAthenaで検索するのはちょっとイケてない気がします。
ファイル分割してパーティション切ったほうがお得だと思います。

続きを読む

AWSのAutoScalingでインスタンスを起動する際にコマンドを実行したい

タイトルの通り、AWSのAutoScalingでスケールアウトさせる際に、起動するインスタンス上でコマンドを実行したい場合についてです。
インスタンスを起動する際に、ソースコードを最新にしたいとか、webサーバーのプロセスを立ち上げたいと言う時ありますよね。
以下の前提のもと、ここでは最もシンプルであろうという方法を記載していきます。

前提

  • 既に利用可能なAMIを作成済み
  • AutoScalingグループも作成済み
  • ブラウザ上のマネジメントコンソールから設定

では、AWSのEC2サービスの中の起動設定の作成を行います。
1.AMIの選択、2.インスタンスタイプの選択を済ませ、3.詳細設定まで進みます。

スクリーンショット 2017-08-16 0.28.36.png

ここのユーザーデータの項目にインスタンス起動時に渡してあげたいコマンドを記載すればOKです。
気を付けなければいけない点は

  • 渡されたコマンドはrootユーザーとして実行される
  • インタラクティブな操作をコマンドの実行途中ではさむようなものは実行できない

です。
このユーザーデータのテキストボックス内に下記のような感じで記述してしまえば完了です。

#!/bin/bash
cd /path/to/myproject
sudo -u ec2-user /usr/bin/git pull origin master
sudo -u ec2-user /usr/local/bin/gunicorn myproject.wsgi -D

ユーザーデータは様々な形式で渡せるようです
https://cloudinit.readthedocs.io/en/latest/topics/format.html

続きを読む

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

目的

 ビットコイン等の仮想通貨の採掘をして不労所得チャリンチャリンというのは夢がありますよね。私もチャレンジしようと思ったのですが、手元の貧相な 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 時点 

続きを読む

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つの対処が必要。

続きを読む

SpotFleet(diversified/lowest)で起動するインスタンスを常にELBにアタッチする

目的

  • SpotFleetで起動しているインスタンスを常に同一のELBにアタッチする
  • 生存戦略はdiversified/lowestで機能する

前提

本構成ではインスタンス1台の運用を想定しているのでインスタンスが停止後、他プールからインスタンスが起動するまで〜2分かかるので、可用性を保つためにはオンデマンドを入れるなり、工夫が必要

方法

スポットインスタンスリクエスト時にユーザーデータを利用する

 イメージ

Untitled (5).png

設定

  1. ELBに対する権限を持つIAMRoleを作成
  2. IAMRoleを使用するスクリプト作成
  3. EC2Resourcesのaws_spot_fleet_requestの引数にuser_dataを記述
spot_fleet_request.tf
# Request a Spot fleet
resource "aws_spot_fleet_request" "aws-simple-console-spotfleet" {
  iam_fleet_role                      = "arn:aws:iam::xxxxxx:role/xxxxxx"
  spot_price                          = "0.0127"
  allocation_strategy                 = "diversified"
  target_capacity                     = 2
  terminate_instances_with_expiration = false

  launch_specification {
    instance_type               = "m3.medium"
    ami                         = "ami-xxxxxx"
    availability_zone           = "ap-northeast-1c"
    subnet_id                   = "subnet-xxxxxx"
    iam_instance_profile        = "xxxxxx"
    key_name                    = "xxxxxx"
    vpc_security_group_ids      = ["sg-xxxxxx"]
    monitoring                  = false
    associate_public_ip_address = true
    user_data                   = "${file("userdata.sh")}"

    root_block_device {
      volume_size = "10"
      volume_type = "gp2"
    }
  }
}
userdata.sh
#!/bin/bash
export INSTANCEID=`curl http://169.254.169.254/latest/meta-data/instance-id`
aws elb register-instances-with-load-balancer --load-balancer-name xxxxxx --instances $INSTANCEID --region ap-northeast-1

続きを読む

[JAWS-UG CLI] Amazon Kinesis Firehose re:入門 (1) 事前準備(Source(Kinesis Agent)およびDestination(S3)の作成)

この記事について

JAWS-UG CLI専門支部 #90 Kinesis Firehose 復習編で実施するハンズオン用の手順書です。

前提条件

必要な権限

作業にあたっては、以下の権限を有したIAMユーザもしくはIAMロールを利用してください。

  • 以下のサービスに対するフルコントロール権限

    • Kinesis Firehose
    • IAM
    • EC2
    • S3
    • CloudWatch Logs
    • STS
    • (Lambda)
      • データの変換を行う場合
    • (KMS)
      • データの暗号化を行う場合

0. 準備

0.1. リージョンを指定

オレゴンリージョンで実施します。(東京マダー?)

コマンド
export AWS_DEFAULT_REGION="us-west-2"

0.2. 資格情報を確認

コマンド
aws configure list

インスタンスプロファイルを設定したEC2インスタンスでアクセスキーを設定せずに実行した場合、以下のようになります。

結果
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                <not set>             None    None
access_key     ****************QSAA         iam-role
secret_key     ****************c1xY         iam-role
    region                us-west-2              env    AWS_DEFAULT_REGION

0.3. バージョン確認

コマンド
aws --version
結果
aws-cli/1.11.129 Python/2.7.12 Linux/4.9.38-16.33.amzn1.x86_64 botocore/1.5.92

0.4. バージョンアップ(必要に応じて)

コマンド
sudo pip install -U awscli

1. 管理対象の構築

CloudFormationを利用して、Source(Kinesis AgentをインストールしたEC2インスタンス)とDestination(S3バケット)を作成します。

1.1. KeyPairの作成

EC2インスタンス用にKeyPairを作成します。

KeyPairの名前を指定

コマンド
AWS_ID=$(aws sts get-caller-identity 
    --query "Account" 
    --output text) 
    && echo ${AWS_ID}
コマンド
KEY_PAIR_NAME="${AWS_ID}_firehose_jawsug_cli"
KEY_MATERIAL_FILE_NAME=${KEY_PAIR_NAME}.pem

同名KeyPairの不存在を確認

コマンド
aws ec2 describe-key-pairs 
    --query "KeyPairs[?KeyName==`${KEY_PAIR_NAME}`]"
結果
[]

KeyPairの作成

コマンド
aws ec2 create-key-pair 
    --key-name ${KEY_PAIR_NAME} 
    --query "KeyMaterial" 
    --output text 
    > ~/.ssh/${KEY_MATERIAL_FILE_NAME} 
    && cat ~/.ssh/${KEY_MATERIAL_FILE_NAME}

KeyPairの存在を確認

コマンド
aws ec2 describe-key-pairs 
    --query "KeyPairs[?KeyName==`${KEY_PAIR_NAME}`]"
結果
[
    {
        "KeyName": "XXXXXXXXXXXX_firehose_jawsug_cli",
        "KeyFingerprint": "xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx:xx"
    }
]

秘密鍵のPermissionを変更

コマンド
chmod 600 ~/.ssh/${KEY_MATERIAL_FILE_NAME}
ls -al ~/.ssh/${KEY_MATERIAL_FILE_NAME}
結果
-rw------- 1 ec2-user ec2-user 1671 Aug  5 18:33 /home/ec2-user/.ssh/788063364413_firehose_jawsug_cli.pem

1.2. CloudFormation テンプレートの生成

テンプレートの作成

コマンド
CF_TEMPLATE_FILE_NAME="firehose_jawsug_cli.yml"
コマンド
cat << EOF > ${CF_TEMPLATE_FILE_NAME}
AWSTemplateFormatVersion: "2010-09-09"
Description: JAWS-UG CLI Kinesis Firehose Hands-on

Parameters: 
  VPCNetworkAddress: 
    Type: String
    Description: "Network Address on AWS"
    MinLength: 9
    MaxLength: 18
    # AllowedPattern: "(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})"
    Default: "10.0.0.0/16"
  PublicSubnetAddr: 
    Type: String
    Description: "Network Address on AWS"
    MinLength: 9
    MaxLength: 18
    # AllowedPattern: "(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/(\d{1,2})"
    Default: "10.0.0.0/24"
  KeyPairName:
    Type: AWS::EC2::KeyPair::KeyName


Resources:
  S3Bucket: 
    Type: "AWS::S3::Bucket"
  IAMRole: 
    Type: "AWS::IAM::Role"
    Properties: 
      RoleName: "service-role-firehose"
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - 
            Effect: "Allow"
            Principal: 
              Service: 
                - "firehose.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      Path: "/"
  IAMPolicy:
    Type: "AWS::IAM::Policy"
    Properties: 
      PolicyName: "service-policy-firehose"
      PolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - 
            Effect: "Allow"
            Action: 
              - "s3:AbortMultipartUpload"
              - "s3:GetBucketLocation"
              - "s3:GetObject"
              - "s3:ListBucket"
              - "s3:ListBucketMultipartUploads"
              - "s3:PutObject"
            Resource:
              - !GetAtt S3Bucket.Arn
              - Fn::Join: 
                - "/"
                - 
                  - !GetAtt S3Bucket.Arn
                  - "*"
          - 
            Effect: "Allow"
            Action: 
              - "logs:PutLogEvents"
            Resource: "*"
      Roles:
        - Ref: IAMRole

  VPC:
    Type: AWS::EC2::VPC
    Properties:
      CidrBlock: !Ref VPCNetworkAddress
      Tags: 
        - 
          Key: "Name"
          Value: "KinesisFirehoseClient"
  IGW:
    Type: AWS::EC2::InternetGateway
    Properties:
      Tags: 
        - 
          Key: "Name"
          Value: "KinesisFirehoseClient"
  AttachIGW:
      Type: AWS::EC2::VPCGatewayAttachment
      Properties:
          VpcId:
              Ref: VPC
          InternetGatewayId:
              Ref: IGW
  PublicSubnet:
    Type: AWS::EC2::Subnet
    Properties: 
      AvailabilityZone: 
        Fn::Select: 
          - 0
          - Fn::GetAZs: ""
      CidrBlock: !Ref PublicSubnetAddr
      MapPublicIpOnLaunch: true
      VpcId: 
        Ref: VPC
      Tags: 
        - 
          Key: "Name"
          Value: "Public"
  PublicRT:
    Type: AWS::EC2::RouteTable
    Properties:
      VpcId:
        Ref: VPC
      Tags: 
        - 
          Key: Name
          Value: Public
  PublicDefaultRoute:
    Type: AWS::EC2::Route
    Properties: 
      DestinationCidrBlock: "0.0.0.0/0"
      GatewayId: 
        Ref: IGW
      RouteTableId: 
        Ref: PublicRT
  PublicSubnetARouteTableAssociation:
    Type: AWS::EC2::SubnetRouteTableAssociation
    Properties:
      SubnetId:
        Ref: PublicSubnet
      RouteTableId:
        Ref: PublicRT
  SecurityGroup:
    Type: "AWS::EC2::SecurityGroup"
    Properties: 
      GroupDescription: WebServer
      SecurityGroupEgress:
        - 
          IpProtocol: "-1"
          CidrIp: "0.0.0.0/0"
      SecurityGroupIngress:
        - 
          IpProtocol: "tcp"
          FromPort: 80
          ToPort: 80
          CidrIp: "0.0.0.0/0"        
        - 
          IpProtocol: "tcp"
          FromPort: 22
          ToPort: 22
          CidrIp: "0.0.0.0/0"
      VpcId:
        Ref: VPC    
  InstanceRole: 
    Type: "AWS::IAM::Role"
    Properties: 
      RoleName: "instance-role"
      AssumeRolePolicyDocument: 
        Version: "2012-10-17"
        Statement: 
          - 
            Effect: "Allow"
            Principal: 
              Service: 
                - "ec2.amazonaws.com"
                - "ssm.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      Path: "/"
      ManagedPolicyArns: 
        - "arn:aws:iam::aws:policy/service-role/AmazonEC2RoleforSSM"
        - "arn:aws:iam::aws:policy/AmazonKinesisFirehoseFullAccess"
  InstanceProfile: 
    Type: "AWS::IAM::InstanceProfile"
    Properties: 
      Path: "/"
      Roles: 
        - !Ref InstanceRole
  Instance:
    Type: "AWS::EC2::Instance"
    Properties:
      KeyName: 
        Ref: KeyPairName
      ImageId: ami-6df1e514
      InstanceType: t2.micro
      SecurityGroupIds: 
        - Ref: SecurityGroup
      SubnetId:
        Ref: PublicSubnet
      IamInstanceProfile:
        Ref: InstanceProfile
      BlockDeviceMappings: 
        - DeviceName: "/dev/sdm"
          Ebs: 
            VolumeType: "gp2"
            DeleteOnTermination: "true"
            VolumeSize: "8"
      UserData:
        Fn::Base64: |
          #!/bin/bash
          cd /tmp
          sudo yum install -y https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/linux_amd64/amazon-ssm-agent.rpm
          sudo yum install -y aws-kinesis-agent
          sudo chkconfig aws-kinesis-agent on
          sudo service aws-kinesis-agent start
          sudo yum install -y httpd
          sudo chkconfig httpd on
          sudo service httpd start
          sudo yum install npm --enablerepo=epel -y
          sudo npm install -g jsonlint

Outputs:
  S3BucketName:
    Value:
      Ref: S3Bucket
  IAMRoleARN:
    Value: !GetAtt IAMRole.Arn
  PublicIP:
    Value: !GetAtt Instance.PublicIp
EOF

cat ${CF_TEMPLATE_FILE_NAME}

CloudFormation テンプレートの検証

コマンド
aws cloudformation validate-template 
    --template-body file://${CF_TEMPLATE_FILE_NAME}
結果
{
    "CapabilitiesReason": "The following resource(s) require capabilities: [AWS::IAM::InstanceProfile, AWS::IAM::Role]",
    "Description": "JAWS-UG CLI Kinesis Firehose Hands-on",
    "Parameters": [
        {
            "NoEcho": false,
            "ParameterKey": "KeyPairName"
        },
        {
            "DefaultValue": "10.0.0.0/16",
            "NoEcho": false,
            "Description": "Network Address on AWS",
            "ParameterKey": "VPCNetworkAddress"
        },
        {
            "DefaultValue": "10.0.0.0/24",
            "NoEcho": false,
            "Description": "Network Address on AWS",
            "ParameterKey": "PublicSubnetAddr"
        }
    ],
    "Capabilities": [
        "CAPABILITY_NAMED_IAM"
    ]
}

1.3. CloudFormation Stackの作成

CloudFormation Stack名の指定

コマンド
CF_STACK_NAME="firehose-jawsug-cli"

同名CloudFormation Stackの不存在を確認

コマンド
aws cloudformation describe-stacks 
    --query "Stacks[?StackName==`${CF_STACK_NAME}`]"
結果
[]

CloudFormation Stackの作成

コマンド
aws cloudformation create-stack 
    --stack-name ${CF_STACK_NAME} 
    --template-body file://${CF_TEMPLATE_FILE_NAME} 
    --capabilities "CAPABILITY_NAMED_IAM" 
    --parameters ParameterKey=KeyPairName,ParameterValue=${KEY_PAIR_NAME},UsePreviousValue=false
結果
{
    "StackId": "arn:aws:cloudformation:us-west-2:XXXXXXXXXXXX:stack/firehose-jawsug-cli/8812e540-7a0e-11e7-aac3-50a68d01a68d"
}

CloudFormation Stackの作成完了を待機

5分程度で作成が完了すると思います。

コマンド
aws cloudformation wait stack-create-complete 
    --stack-name ${CF_STACK_NAME}
結果
(返値無し)

CloudFormation Stackの存在を確認

“StackStatus”が”CREATE_COMPLETE”になっていることを確認します。

コマンド
aws cloudformation describe-stacks 
    --stack-name ${CF_STACK_NAME}
結果
{
    "Stacks": [
        {
            "StackId": "arn:aws:cloudformation:us-west-2:XXXXXXXXXXXX:stack/firehose-jawsug-cli/4043bde0-7a16-11e7-8701-50a686be73ba",
            "Description": "JAWS-UG CLI Kinesis Firehose Hands-on",
            "Parameters": [
                {
                    "ParameterValue": "XXXXXXXXXXXX_firehose_jawsug_cli",
                    "ParameterKey": "KeyPairName"
                },
                {
                    "ParameterValue": "10.0.0.0/16",
                    "ParameterKey": "VPCNetworkAddress"
                },
                {
                    "ParameterValue": "10.0.0.0/24",
                    "ParameterKey": "PublicSubnetAddr"
                }
            ],
            "Tags": [],
            "Outputs": [
                {
                    "OutputKey": "PublicIP",
                    "OutputValue": "54.191.102.113"
                },
                {
                    "OutputKey": "IAMRoleARN",
                    "OutputValue": "arn:aws:iam::XXXXXXXXXXXX:role/service-role-firehose"
                },
                {
                    "OutputKey": "S3BucketName",
                    "OutputValue": "firehose-jawsug-cli-s3bucket-134czh3hcofqz"
                }
            ],
            "CreationTime": "2017-08-05T19:42:44.440Z",
            "Capabilities": [
                "CAPABILITY_NAMED_IAM"
            ],
            "StackName": "firehose-jawsug-cli",
            "NotificationARNs": [],
            "StackStatus": "CREATE_COMPLETE",
            "DisableRollback": false
        }
    ]
}

1.4. パラメータの確認

以降の手順で必要になるパラメータを抽出します。

  • IAMロールARN
  • S3バケット名
  • パブリックIPアドレス
コマンド
OUTPUTKEY_ROLE_ARN="IAMRoleARN"
OUTPUTKEY_BUCKET_NAME="S3BucketName"
OUTPUTKEY_PUBLIC_IP_ADDRESS="PublicIP"
コマンド
ROLE_ARN=$(aws cloudformation describe-stacks 
    --stack-name ${CF_STACK_NAME} 
    --query "Stacks[].Outputs[?OutputKey==`${OUTPUTKEY_ROLE_ARN}`].OutputValue[]" 
    --output text) 
    && echo ${ROLE_ARN}
結果
arn:aws:iam::XXXXXXXXXXXX:role/service-role-firehose
コマンド
BUCKET_NAME=$(aws cloudformation describe-stacks 
    --stack-name ${CF_STACK_NAME} 
    --query "Stacks[].Outputs[?OutputKey==`${OUTPUTKEY_BUCKET_NAME}`].OutputValue[]" 
    --output text) 
    && echo ${BUCKET_NAME}
結果
firehose-jawsug-cli-s3bucket-134czh3hcofqz
コマンド
PUBLIC_IP_ADDRESS=$(aws cloudformation describe-stacks 
    --stack-name ${CF_STACK_NAME} 
    --query "Stacks[].Outputs[?OutputKey==`${OUTPUTKEY_PUBLIC_IP_ADDRESS}`].OutputValue[]" 
    --output text) 
    && echo ${PUBLIC_IP_ADDRESS}
結果
54.191.***.***

動作確認

パブリックIPアドレスにブラウザでアクセスします。

以上

続きを読む

EC2にNginx + Gunicorn + SupervisorでDjangoアプリケーションをデプロイする

Nginx + Gunicorn + Supervisorの組み合わせでDjangoアプリケーションを立ち上げたので手順のメモ
今回はOSに何も入っていない状態から始めていきます

環境

OS: Amazon Linux AMI
Python: 3.6.1
Django: 1.11.4
Nginx: 1.10.3
Gunicorn: 19.7.1
Supervisor: 3.3.3

Nginxのインストール

nginxのインストール

$ sudo yum install nginx

nginx起動する

$ sudo nginx

nginx自動起動設定

$ sudo chkconfig --add nginx
$ sudo chkconfig nginx o

自動起動設定確認
以下のようになっていればok

$ chkconfig | grep nginx
nginx           0:off   1:off   2:on    3:on    4:on    5:on    6:off

http://ipアドレスにアクセスしちゃんと起動しているか確認する
以下の通りになっていればOK
スクリーンショット 2017-08-03 13.40.56.png

Python環境の構築

今回はAnacondaで構築した
こちらからPython 3.6 versionをダウンロードする
ダウンロードしたパッケージをCyberduckなどのFTPツールで/home/ec2-userにアップロードする

アップロード完了したら下記コマンドでAnacondaインストールする

$ bash Anaconda3-4.4.0-Linux-x86_64.sh

インストール完了後、Anacondaのコマンドが使えるようにPATHを通す

$ export PATH="$PATH:/home/ec2-user/anaconda3/bin"

condaのコマンドを打って確認

$ conda info -e
# conda environments:
#
root                  *  /home/ec2-user/anaconda3

良さげです

pythonも3.6になっている

$ python --version
Python 3.6.1 :: Anaconda 4.4.0 (64-bit)

Djangoプロジェクトの作成

今回は直接EC2上でプロジェクトを作成します
本来はローカルで開発したDjangoアプリケーションをgit cloneすべき
また、DBもデフォルトのSQliteを使用しますが、実際のサービスを公開するにはPostgresqlやMariaDBを使う

まずはDjangoのインストール
root環境で動かすかどうかは少し議論の分かれるところで、別に環境を作ってDjangoを動かした方がいいんじゃないか、と思ったりもしますが、とりあえず今回はroot環境でインストールしてしまいます

$ pip install django

問題なければプロジェクトを作成

$ django-admin startproject test_project

プロジェクトが作られていることを確認

$ ls -ltr
total 511032
-rw-rw-r--  1 ec2-user ec2-user 523283080 Aug  3 04:50 Anaconda3-4.4.0-Linux-x86_64.sh
drwxrwxr-x 20 ec2-user ec2-user      4096 Aug  3 04:53 anaconda3
drwxrwxr-x  3 ec2-user ec2-user      4096 Aug  3 05:05 test_project

/test_project/test_project/settings.pyのALLOW HOSTを下記の通り編集しておく

settings.py
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True

ALLOWED_HOSTS = ["サーバのIPアドレス"]

下記のコマンドでDjangoを起動
デフォルトだと127.0.0.1:8000がbindアドレスとして使用されているため、オプションで0.0.0.0:8000を追加する必要があります
また、事前にAWSのセキュリティグループで8000番ポートを解放しておく必要があります

$ cd test_project
$ python manage.py runserver 0.0.0.0:8000

そして、http://IPアドレス:8000にアクセスすると以下の通りDjangoアプリケーションにアクセスできる
スクリーンショット 2017-08-03 15.23.25.png

Gunicornのインストール

GunicornはPython製のWSGIサーバ
WSGIサーバというのはWebサーバとWebアプリケーションをつなぐサーバのこと
なので、Nginx <-> Gunicorn <-> Djangoというような構成をイメージしていただければと

まずはGunicornのインストールをやっていく

$ pip install gunicorn

インストールされたらGunicornでDjangoを起動させる

$ gunicorn test_project.wsgi --bind=0.0.0.0:8000

先程と同様、http://IPアドレス:8000にアクセスするとDjangoアプリケーションに接続できる

Nginxの設定の変更

/etc/nginx.confを以下の通り編集する

/etc/nginx.conf

〜中略〜

http {
    〜中略〜

    upstream app_server {
        server 127.0.0.1:8000 fail_timeout=0;
    }

    server {
        #以下4行はコメントアウト
        #listen       80 default_server;
        #listen       [::]:80 default_server;
        #server_name  localhost;
        #root         /usr/share/nginx/html;

        # 以下3行を追加
        listen    80;
        server_name     IPアドレス or ドメイン;
        client_max_body_size    4G;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
            # 以下4行を追加
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header Host $http_host;
            proxy_redirect off;
            proxy_pass   http://app_server;
        }

    〜以下略〜

編集したら下記コマンドでnginxを再起動する

$ sudo service nginx restart
Stopping nginx:                                            [  OK  ]
Starting nginx:                                            [  OK  ]

これでNginxでのリバースプロキシの設定は完了
今回はnginx.confを直接編集したけれど、設定ファイルをどこか別のところに書いてそれを読み込ませる、という方法でもOK

その後、DjangoをGunicornで立ち上げる

$ gunicorn test_project.wsgi --bind=0.0.0.0:8000

次はhttp://IPアドレスにアクセスするとDjangoの画面が表示されるはず

Supervisorでプロセスをデーモン化する

今の状態だとGunicornのコマンドを中止したり、サーバからログアウトするとアプリケーションが停止してしまう
これを解消するためにSupervisorでGunicornのプロセスをデーモン化する

早速、Supervisorをインストール、としたいところだけれど、SupervisorはPython2系でしか動作しない
そのためAnacondaでPython2系の仮想環境を構築し、その環境にSupervisorをインストールしていく

まずは下記コマンドでSupervisor用のPython2系の仮想環境を作る

$ conda create -n supervisor python=2.7

python2系の環境に切り替えてpipでsupervisorをインストール

$ source activate supervisor
$ pip install supervisor

問題なくインストールできたら、supervisorの設定ファイルを作成し、それを/etc配下に配置する

$ echo_supervisord_conf > supervisord.conf
$ sudo mv supervisord.conf /etc

次にsupervisorの設定を行うため、supervisord.confを下記の通り編集

supervisord.conf
〜中略〜
[supervisord]
logfile=/var/log/supervisord.log ; ログの場所を変更
;logfile=/tmp/supervisord.log ; main log file; default $CWD/supervisord.log #コメントアウト
logfile_maxbytes=50MB        ; max main logfile bytes b4 rotation; default 50MB
logfile_backups=10           ; # of main logfile backups; 0 means none, default 10
loglevel=info                ; log level; default info; others: debug,warn,trace
pidfile=/var/run/supervisord.pid ; 追記
;pidfile=/tmp/supervisord.pid ; supervisord pidfile; default supervisord.pid #コメントアウト

〜中略〜
# includeはコメントアウトされているのでコメント外す
[include]
files = supervisord.d/*.conf ; 起動するプロセスのconfファイルの配置場所
;files = relative/directory/*.ini

ログファイルは作っておき、パーミッションも設定しておく

$ sudo touch /var/log/supervisord.log
$ sudo chown ec2-user /var/log/supervisord.log
$ sudo chgrp ec2-user /var/log/supervisord.log
$ sudo chmod 774 /var/log/supervisord.log

あと、ログローテションも設定しておく

$ sudo sh -c "echo '/var/log/supervisord.log {
       missingok
       weekly
       notifempty
       nocompress
}' > /etc/logrotate.d/supervisor"

次にデーモン化するプロセスのコマンドを記載したファイルを作っていく
まずはそれらのファイルを配置するディレクトリを作成

$ sudo mkdir /etc/supervisord.d

/etc/supervisord.d配下にdjango_app.confを作成
ここに、Gunicornのプロセスをデーモン化するための設定を以下のように書く

django_app.conf
[program:django_app]
directory=/home/ec2-user/test_project
command=gunicorn test_project.wsgi --bind=0.0.0.0:8000
numprocs=1
autostart=true
autorestart=true
user=ec2-user
redirect_stderr=true

directoryに実行するディレクトリを指定、commandのところにプロセスを起動するためのコマンドを指定する

ここまでできたら下記のコマンドでsupervisorを立ち上げる

$ supervisord

次に、confファイルを読み込ませる
こちらは仮にconfを修正などする場合は必ず実行する

$ supervisorctl reread

なお、下記のコマンドでデーモンを再起動し、そのタイミングでもconfが読み込まれたりする

$ supervisorctl reload

下記のコマンドでGunicornのプロセスをデーモン化する

$ supervisorctl start django_app

仮にdjango_app: ERROR (already started)というメッセージが出た場合は、以下のコマンドでプロセスの再起動をしたり、停止をしてからstartしたりする

$ supervisorctl stop django_app # 停止
$ supervisorctl restart django_app # 再起動

さて、この状態でサーバからログアウトしてみる
そして、http://IPアドレスにアクセスすると、Djangoの画面が表示される
GunicornのプロセスがSupervisorによりデーモン化されていることになる

よかったですね

続きを読む

Amazon Inspectorを試してみた

Amazon Inspectorとは

EC2で実行されているアプリケーションのセキュリティ状態をテストできるものです。

利用可能なリージョン

us-east-1,us-west-2,eu-west-1,ap-northeast-1

利用可能なOS

Amazon Inspector でサポートされているオペレーティングシステムとリージョン

Amazon Linux (2015.03、2015.09、2016.03、2016.09、2017.03)
Ubuntu (14.04 LTS、16.04 LTS)
Red Hat Enterprise Linux (6.2、6.3、6.4、6.5、6.6、6.7、6.8、6.9、7.2、7.3)
CentOS (6.2、6.3、6.4、6.5、6.6、6.7、6.8、6.9、7.2、7.3)

設定

EC2にエージェントインストール

今回はAmazon Linuxで試しました。

$ cat /etc/system-release
Amazon Linux AMI release 2017.03

ダウンロードしてインストールします。
リンクの手順に沿ってやるだけです:point_up::dizzy:
AWS エージェントをインストールするには

$ wget https://d1wk0tztpsntt1.cloudfront.net/linux/latest/install

$ sudo bash install

Inspectorの設定

  1. サービスから「Inspector」を選択
  2. 左ペインから「評価ターゲット」を選択し、「作成」をクリック
  3. 名前とキーを入力して「保存」
    スクリーンショット 2017-08-01 10.13.44.png

  4. EC2にタグ付けする
    スクリーンショット 2017-08-01 13.16.58.png

  5. Inspectorで先ほど作成した評価ターゲットの左にある矢印をクリックして、「プレビュー」をクリックするとタグ付けされたEC2が出力されます
    スクリーンショット 2017-08-01 13.19.39.png
    →これが評価ターゲットになります。

  6. 左ペインから「評価テンプレート」をクリックして「作成」をクリックします。

  7. ターゲット名は先ほど作成した評価ターゲットを選択します。
    ルールパッケージは以下から選択可能です。今回はCVEを選択してみました。
    Amazon Inspector のルール パッケージとルール
    ・ 共通脆弱性識別子(CVE)
    ・ Center for Internet Security (CIS) ベンチマーク
    ・ セキュリティのベストプラクティス
    ・ 実行時の動作の分析
    所要時間は長ければ長いほど、より精密な結果が得られるとのことです。
    AWS推奨は1時間。
    SNSと連携してメール飛ばせたりしますが、ここでは省略。
    「作成および実行」を押すと調査が始まります。

スクリーンショット 2017-08-01 13.34.48.png

結果

スクリーンショット 2017-08-01 13.47.29.png
指定した時間が経過すると、調査結果が出力されます。
重要度の左にある矢印をクリックすると詳細が確認で、CVEのページのリンクも載っています。

脆弱性が自分のインスタンスに影響しているのか否か判断できて便利ですね:baby:

料金

料金は月あたり 1 エージェント 1 評価あたり 0.30 USD から始まり、従量制割引により月あたり 1 エージェント 1 評価あたり 0.05 USD の低価格でご利用いただけます。

Amazon Inspector料金

Amazon Inspectorの価格体系について

評価実行中のインスタンスへの影響

評価実行プロセス中のパフォーマンスへの影響を最小限に抑えるように設計されています
Amazon Inspector のよくある質問

CloudWatchで確認してみましたが、CPU使用率が高騰することはありません(上がっても1%未満)でした。

関連資料

BlackBelt

Amazon Inspector とは

Amazon Inspector のよくある質問

続きを読む

AWS Rekognitionを使ってみた(設定編)

こんにちは〜〜!
AWSの人工知能サービスのRekognition
Cloud9(ブラウザ上の統合開発環境)上で使ってみました。
この記事では、私が行なった環境設定を記録していきます。

Rekognitionとは

Rekognitionとは、画像の分析をアプリケーションに追加できるサービスです。
このサービスでは、画像内の物体やシーン、顔の検出が行えます。
また、顔の比較なども実行可能です。

Rekognitionの実行環境を設定してみた

Rekognitionの利用開始には、次の2つが必要です!

1. AWS CLI(Command Line Interface)の設定
2. AWS SDKの設定

まず、Webブラウザ上でCloud9を開きます。
次に、Create a new workspaceでWorkspaceを作成します。
今回、私は、以下のようにWorkspaceを作成しました。
workspaceImage.png
Workspace nameは aws_rekognition 、Templateは Python を選択しました。

1. AWS CLI(Command Line Interface)の設定

Cloud9にAWS CLIをインストールするのに参考にした記事はコチラです。

http://yuki-dev.hatenablog.com/entry/20170206/1486375678

ここに記載してあった通りにコマンドを打ったらいけました!
Cloud9のbashに打ったコマンドは以下です。

$ sudo apt-get update //apt-getのアップデート
$ sudo apt-get install python2.7-dev //python2.7-devのインストール
$ sudo pip install awscli //CLIのインストール

AWS CLIのインストールが完了したら、$ aws --versionで確認できるようなのですが、、
私の場合はコマンドを叩いても表示されませんでした。。。
なので、$ awsと打ち込んでみたところ。。

botocore-error.png

botocoreっていうモジュールがないよと言われました(泣)
そこで、$ sudo pip install botocoreでbotocoreをインストール。

botocore-error2.png
なんかもうすでにあるっぽい、、?
ので、$ sudo pip install awscli --force-reinstall --upgradeでCLIを再インストール。

$ awsで確認してみたところ。

reinstall.png
ImportErrorは消えてました!なんかいけたっぽい(?)わーい!

ということで、次へ。

2. AWS SDKの設定

AWS SDKは、Amazon Rekognition APIを呼び出すのに必要みたいです。
そこで、今回はAWS SDK for Pythonのインストール。
打ち込むコマンドは、以下です。

$ sudo pip install boto3

インストールが完了したら、設定に移ります。
設定をするためには、$ aws configureを打ちます。

aws-configure.png
こんな感じのが出てくるので、自分の [credentials.csv] に記載されてある
Access Key ID,Secret Access Keyregion,output formatを設定します。

あとは、設定されているか確認できたら実行環境の設定は完了です!
AWS SDK for Python インストールの参考HPはコチラ。

https://aws.amazon.com/jp/sdk-for-python/

実行してみた記事はコチラです。

続きを読む