AtlassianのLocalStackを使ってみてなんとなく理解するまでのお話

Atlassianが「LocalStack」なんてとても便利そうなものを出していたけど、なかなか使い方を解説しているページが見つからなかったので、とりあえず使いながらなんとなく中身を理解するまでのお話。

https://github.com/atlassian/localstack
スクリーンショット 2017-04-23 17.53.59.png

起動

いくつかGithubで利用方法が紹介されていますが、今回はdockerでの利用をしてみます。

$ docker run -it -p 4567-4578:4567-4578 -p 8080:8080 atlassianlabs/localstack
2017-04-23 08:50:15,876 INFO supervisord started with pid 1
2017-04-23 08:50:16,879 INFO spawned: 'dashboard' with pid 7
2017-04-23 08:50:16,885 INFO spawned: 'infra' with pid 8
(. .venv/bin/activate; bin/localstack web --port=8080)
. .venv/bin/activate; exec localstack/mock/infra.py
Starting local dev environment. CTRL-C to quit.
 * Running on http://0.0.0.0:8080/ (Press CTRL+C to quit)
 * Restarting with stat
Starting local Elasticsearch (port 4571)...
Starting mock ES service (port 4578)...
Starting mock S3 server (port 4572)...
Starting mock SNS server (port 4575)...
Starting mock SQS server (port 4576)...
Starting mock API Gateway (port 4567)...
Starting mock DynamoDB (port 4569)...
Starting mock DynamoDB Streams (port 4570)...
Starting mock Firehose (port 4573)...
Starting mock Lambda (port 4574)...
Starting mock Kinesis (port 4568)...
Starting mock Redshift server (port 4577)...
 * Debugger is active!
2017-04-23 08:50:18,537 INFO success: dashboard entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
2017-04-23 08:50:18,538 INFO success: infra entered RUNNING state, process has stayed up for > than 1 seconds (startsecs)
 * Debugger PIN: 844-652-544
Ready.

とりあえず起動はしたみたい。で、http://localhost:8080/にアクセスしてみたけど、こんな感じで何も表示されず。

スクリーンショット 2017-04-23 17.59.04.png

使ってみてわかりましたが、要は本当に各種サービスが以下のアドレスで利用できるようにしてくれたもののようです。

全部試すのもアレなので、とりあえず馴染み深いDynamoDBとS3を使ってみる。

DynamoDBのテーブル作成

全然関係ないですが、http://localhost:4569/にアクセスすると以下のレスポンスをもらえます。デフォルトはus-east-1で動いている想定のようで。(Githubページにも書いてあった。バインドすればいくつか設定を変更できるようですね。)

healthy: dynamodb.us-east-1.amazonaws.com 

では早速テーブルをCLIから作ってみます。一応作成前にlist-tablesをしてみますが、もちろん何も登録されていません。

$ aws --endpoint-url=http://localhost:4569 dynamodb list-tables
{
    "TableNames": []
}

こちらを参考にさせていただいて、create-tableコマンドを発行します。

$ aws --endpoint-url=http://localhost:4569 dynamodb create-table --table-name test --attribute-definitions AttributeName=testId,AttributeType=S --key-schema AttributeName=testId,KeyType=HASH --provisioned-throughput ReadCapacityUnits=1,WriteCapacityUnits=1
{
    "TableDescription": {
        "TableArn": "arn:aws:dynamodb:us-east-1:000000000000:table/test", 
        "AttributeDefinitions": [
            {
                "AttributeName": "testId", 
                "AttributeType": "S"
            }
        ], 
        "ProvisionedThroughput": {
            "NumberOfDecreasesToday": 0, 
            "WriteCapacityUnits": 1, 
            "ReadCapacityUnits": 1
        }, 
        "TableSizeBytes": 0, 
        "TableName": "test", 
        "TableStatus": "CREATING", 
        "KeySchema": [
            {
                "KeyType": "HASH", 
                "AttributeName": "testId"
            }
        ], 
        "ItemCount": 0, 
        "CreationDateTime": 1492937089.534
    }
}

なんか作られたっぽいですね。もう一度list-tablesをしてみます。

$ aws --endpoint-url=http://localhost:4569 dynamodb list-tables
{
    "TableNames": [
        "test"
    ]
}

出来上がってますね。なるほど、本当にAWS上でやる操作をEndpointURLを変更するだけで操作できてしまうようです。これは思っていたよりも便利かも。

S3のバケットを作成してみる

S3のバケットも作成できるのか試してみます。まずバケットのリストを見てみますが、当然何もありません。

$ aws --endpoint-url=http://localhost:4572 s3 ls
(何も表示されず)

では作成してみます。

$ aws --endpoint-url=http://localhost:4572 s3 mb s3://kojiisd-test/
make_bucket: s3://kojiisd-test/
$ aws --endpoint-url=http://localhost:4572 s3 ls
2006-02-04 01:45:09 kojiisd-test

まじか、これは便利。ただdockerでサービス起動したら停止時に中身が消えてしまうから、できれば作成したものが残るような起動方法の方が色々試そうとしたら適していそうですね。
(作成時間がはちゃめちゃですが、とりあえずそこまで問題にはならないかな)

ちなみにDynamoDBのテーブルとS3のバケットを作成してから気づきましたが、http://localhost:8080/にアクセスしたら、作成したものが表示されていました。なるほど、そのためのDashBoardだったのか。素敵。

スクリーンショット 2017-04-23 18.20.02.png

まとめ

どれくらいどこまで何ができるのかは気になりますが、一般的なことであればだいたいローカルでできるような気がします。しかもEndpointURLを変更するだけで良さそうなので、これはかなり便利かも。今度作成したアプリを全てLocalStackで動かしてみるとかやってみようかな。

とりあえず、もっとこれは知られるべきと、思いました。

続きを読む

TensorFlow with GPU on Docker を AWS で起動する

構成

https://github.com/NVIDIA/nvidia-docker にある以下の図が分かりやすい。

図

今回、Server は AWS の p2 インスタンス (GPU インスタンス)。
Host OS は Ubuntu 16.04 を利用する。

手動でインストールが必要なものは以下の通り。

  • CUDA Toolkit / CUDA Driver

    • NVIDIA GPU をコントロールするために必要
    • 2つ同時にインストールされる
  • Docker Engine
  • nvidia-docker
    • Docker コンテナ内から CUDA Toolkit 経由で GPU を触るために必要

1. AWS インスタンス起動

GPU インスタンスの p2 系を起動する。

AMI
Ubuntu Server 16.04 LTS (HVM), SSD Volume Type
備考
ディスクサイズは 100 GB に変更する (デフォルトは 8 GB、足りない)

2. CUDA のインストール

公式ドキュメント 通りに進める。
ただ、ドキュメントが長いので読まない方が良い。ハマると果てしなくハマって辛い。

実際に必要なのは3箇所のみ。

  • “2. Pre-installation Actions” > “2.6. Download the NVIDIA CUDA Toolkit”
  • “3. Package Manager Installation” > “3.6. Ubuntu”
  • “6. Post-installation Actions” > “6.1.1. Environment Setup”

実際のコマンドは以下の通り。

## 2.6 Pre-installation Actions (Download the NVIDIA CUDA Toolkit)
$ wget https://developer.nvidia.com/compute/cuda/8.0/Prod2/local_installers/cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64-deb

## 3.6 Package Manager Installation (Ubuntu)
$ sudo dpkg -i cuda-repo-ubuntu1604-8-0-local-ga2_8.0.61-1_amd64-deb
$ sudo apt-get update
$ sudo apt-get install cuda

## 6.1.1 Post-installation Actions (Environment Setup)
$ echo 'export PATH=/usr/local/cuda-8.0/bin${PATH:+:${PATH}}' >> ~/.bashrc
$ source ~/.bashrc

nvcc が入れば成功。

$ nvcc --version
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2016 NVIDIA Corporation
Built on Tue_Jan_10_13:22:03_CST_2017
Cuda compilation tools, release 8.0, V8.0.61

3. Docker のインストール

公式ドキュメント (Install using the repository) 通りに、Docker CE をインストールする。

インストール完了したら、sudo 無しで動作するよう ubuntu ユーザを docker グループに追加して、SSH ログインし直す。

$ sudo usermod -aG docker ubuntu

hello-world が動けば完了。

$ docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
78445dd45222: Pull complete
Digest: sha256:c5515758d4c5e1e838e9cd307f6c6a0d620b5e07e6f927b07d05f6d12a1ac8d7
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.
...

4. nvidia-docker のインストール

公式ドキュメント 通りに進める。

“Quick start” > “ubuntu distributions” のコマンドを実行すればOK。

$ wget -P /tmp https://github.com/NVIDIA/nvidia-docker/releases/download/v1.0.1/nvidia-docker_1.0.1-1_amd64.deb
$ sudo dpkg -i /tmp/nvidia-docker*.deb && rm /tmp/nvidia-docker*.deb

以下のコマンドで Docker コンテナがホスト (p2 インスタンス) の GPU を認識していることが確認できる。

$ nvidia-docker run --rm nvidia/cuda nvidia-smi
Sun Apr 23 06:10:55 2017
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 375.39                 Driver Version: 375.39                    |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  Tesla K80           Off  | 0000:00:1E.0     Off |                    0 |
| N/A   47C    P8    28W / 149W |      0MiB / 11439MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID  Type  Process name                               Usage      |
|=============================================================================|
|  No running processes found                                                 |
+-----------------------------------------------------------------------------+

TensorFlow

あとは TensorFlow でもなんでもコンテナ内から GPU が触れる。

https://hub.docker.com/r/tensorflow/tensorflow/

$ nvidia-docker run -it -p 8888:8888 tensorflow/tensorflow:latest-gpu

続きを読む

Amazon Dash ButtonをHackするのはJust do itでしょ、と思って手を出したら落とし穴にどハマった件

Exective Summary

そもそもやりたかったこと:

Amazon Dash Buttonを押して、AWS上の全EC2を停止する。
(利用料が個人レベルでない上に、お小遣いが少ないもので…)

前提・制約:

(甲) 我が家にはWindows10マシンしかない(Linuxマシンはない、という意味)。
(乙) 一方、AWS Dash ButoonをHackするツールとしては、Linux/OS X向けNode.js系ツールが主流らしい([1],[3],[5],[9])。
⇒Amazon Dash Buttonは、ボタンを押すとARPリクエストを送出(ブロードキャスト)する。それを同じLANで待ち受けて検知できるのがDasher。その検知をトリガーにして任意の処理(WebAPIを呼び出す、等)を起動できる。
(丙) 我が家にはAWS Dash Buttonが2つある。それは、購入商品の指定/設定を、
  (A) 意図的に途中でやめてあるモノ
  (B) 無邪気に最期まで完了した(知らずに済ませてしまった)モノ
  の2種類だ。
(丁) 呼び出されたら「全EC2を停止する」というAPIは、AWS IoT/API Gateway/Lambdaのサーバレスアーキで作成済み。
⇒世間の情報も豊富だし、小1時間程度でできるっしょーとたかをくくる。

俺的に考え得た・試した選択肢

①Windows10+Dasher・・・×
②Windows10+Bash on Windows+Dasher・・・×
③Windows10+Cygwin+Dasher・・・×
④Windows10+win-node-dash-button・・・×
⑤Windows10+Vagrant+VirtualBox+Ubuntu+Dasher・・・○
⑥Windows10+Docker-Machine+VirtualBox+Boot2Docker+Dasher・・・×
⑦Windows10+Docker-Machine+VirtualBox+Boot2Docker+Docker+Ubuntu+Dasher・・・未

紆余曲折の結果が…コレだ…ワン・ツー・スリー…

⑤Windows10+Vagrant+VirtualBox+Ubuntu+Dasher

学べる(学べた)コト

・Git
・node.js/npm/node-gyp/node_pcap
・python/pip
・Bash on Windows/Cygwin/Ubuntu
・Docker-Machine/Vagrant/VirtualBox
・Wireshark/tcpdump
・NW/ARP/DNS

嵐の後の所感

やってみた結果として、Amazon Dash Buttonは幅広く技術を体験するのに良いテーマだったという実感。
(「フェルマーの最終定理」と同じ構図?定理の意味の理解はトリビアルだが、その証明を理解するには色々な数学的知識が必要!)
次は『Dash Button(丙)-(B)』のHackを試みる…(方向性としては『DNSサーバをポジティブに偽装する』)
…まあ、AWS IoT Buttonが日本でも発売されたら、それを使うけどね…

俺が落ちた落とし穴の各論

以下、詳細:(細々と続く)

参考情報

[1] Amazon Dash ButtonをただのIoTボタンとして使う
[2] node-dash-button@GitHub
[3] Amazon Dashボタンのハックにおける期待と落胆(Wi-Fi編)
[4] dasher@GitHub
[5] Amazon Dash Buttonを(正しくない方向で)使ってみた
[6] Dash Button for Node@GitHub
[7] node_pcap
[8] node_pcapのWindows7対応Folk
[9] win-node-dash-button
[10] Windowsでnpm installしてnode-gypでつまずいた時対処方法
[11] これはすごい!分解とパケット解析で分かったAmazon Dash Buttonの「本気度」
[12] これはすごい!Amazon Dash Buttonをプレゼンに使う
[13] これはすごい!Amazon Dash Buttonでツイートできた

続きを読む

インフラチームによくある極小サーバーやバッチジョブをECSで集約したい

すごい。この記事には文字しかない。

話の発端

ほとんどアクセスが無く、あっても昼間しかなく、しかし消すわけにはいかない社内サービスのようなデモサービスのようなサーバーが豪勢にもEC2単独インスタンスを持っていると、CPUを使わなすぎてリソースがもったいなかったり、たまったCPUクレジットを捨ててしまったりしてもったいない。日時バッチサーバーみたいなものも似たような状況で、バッチ稼働時以外は上と同じ無駄を今も発生させている。

では、全部Elastic Container Serviceでコンテナにしてインスタンスを集約して、昼間ためたクレジットを夜中の日時バッチで使うのはどうか、という手を考えた。外国リージョンではAWS Batchってやつが使えるようになっているので、そいつが東京リージョンに来たときにうまいことECSから移行できると最高に素敵だ。

各ミニサービスのECSへの集約

上述ミニサーバー群はまがいなりにもサーバーで、しかもSSLなので、IPを固定したい。

が、コンテナにEIPを付けることはできず、Global IPを持たせたい場合は、かわりにELBを使うのが定石1

なのだが、さして重要でもないのにELBを使うのはオーバースペック、というかお金の無駄なので、Unmanagedなコンテナインスタンスを作成し、その中でnginxを動かして、コンテナにリバースプロキシすればーーー、ということを考えていた。コンテナインスタンス障害時にはまるごと作り直せばいいようなサービスレベルだし、それが簡単にできちゃうのがコンテナの長所だ。

が、じゃあそもそもnginxもコンテナでよくね、っていうことに気付いた。コンテナ間でリバースプロキシしちゃえばいいじゃん。

ELB無しでSSLできるコンテナインスタンスの作成

つまりコンテナインスタンスにEIPをつけたい、ということ。ECSのウィザードからクラスタを作成する場合、コンテナインスタンスのカスタマイズ可能項目は少ない。カスタム可能 (つまりコンテナインスタンス作成時に指定可能) なものは

  • インスタンスタイプ
  • ディスク容量
  • VPC
  • Security Group

だけで、Public IPとPrivate IPは自動的に割り当てられるため、コンテナインスタンス作成時にIPやEIPの指定はできない。なので、このあとに、作成されたEC2インスタンスに対して

  1. EIPを作成
  2. EIPをコンテナインスタンスのインターフェースに割り当て

ってやるとコンテナインスタンスのIPを安全に固定できる。

実際にEIPを付けた直後からコンテナに疎通可能になっていて、しかもECSから見えるコンテナインスタンス情報も自動で更新され、Global IPは新しいEIPになっていることが確認できる。便利な世の中になったもんだ。

コンテナ間リンク

nginxもコンテナなので、リバースプロキシ先はコンテナのリンクでOK、と思っていたのだが、なんとコンテナのリンクは同じタスク定義に属したコンテナ間のみで可能とのこと2。つまりexternal_linkは書けない。でも今回のユースケースだと、コンテナごとにタスク定義を変えるタイミング (Docker imageの更新とか) は異なるので、コンテナごとにタスク定義を作ることになる。

ってことは、コンテナごとに80:10080443:10443みたいなPort Mappingを書き、nginxはこのポートとコンテナインスタンスのローカルアドレスで、各コンテナへリバースプロキシする。まぁPort Mappingは複数コンテナインスタンスにまたがってコンテナを配置をしたい場合とかに結局必要だよね、たぶん。

Unmanaged Container Instanceを作りたい場合

ちなみに、どうしてもオレオレカスタムなUnmanagedコンテナインスタンスを作りたいときは

  1. 自力でEC2インスタンスを作成する

    • AWSが用意しているECS用のAMIを使う
    • か、それ以外を使う場合は自分でAgentのインストールなどが必要
    • めんどくさいけどEC2でインスタンスをいろいろいじれる
  2. ECSクラスタ作成時に空のクラスタを作る
  3. さっきのインスタンスをクラスタに追加する

という手順で可能らしい3

でもnginxもコンテナにしちゃうなら、ECSウィザード経由でデフォルトのAMI使う方が再作成と運用が楽な気がする。できるだけ外の世界へ出て行こうとしないのがパブリッククラウドの鉄則。

AWS Batch

ドキュメントを†熟読† (3分) した感じ、ECSにジョブキューを付けて、順番やリトライ、キューごとの計算環境の割り当て、実行時間やリソースの制限、優先度とかを定義できるようにしたものっぽい。†熟読†して思ったことは、

  • 要はPBS4サービスみたいなもんじゃね? PBSのインストールと運用は意外にめんどくさいらしい。というか需要がニッチすぎて活発には開発されてないらしい。通常SQSとかAMQPが実装された何かを使うんじゃないかしら。
  • GPUインスタンスが使えたりとか、AWSをスパコン的に使いたい人にはグッとくるのかもしれない5が、いるのかそんな人?並列性能上げると青天井でお金かかるし、1週間かかる機械学習だとやっぱり青天井でお金かかるし、そういう人は京とかどっかのスパコン借りた方が安いんじゃ……ぶつぶつ……

というわけで、AWS Lambdaで時間足りない人向け、という位置づけが現実的。

ECSからAWS Batchへの移行を考える

東京リージョンで今でも使えるECSから、今後東京リージョンにも展開されるであろうAWS Batchへの移行を考えると、既存のECSクラスタをAWS BatchのCompute Environment (CE) に変換できるとうれしいぞ。ドキュメントを読んでみると

By default, AWS Batch managed compute environments use the latest Amazon ECS-optimized AMI for compute resources.6

どうやらCEはECSのAMIをそのまま使うだけっぽいので、ECSクラスタを作ったあとにCEに追加できるのでは?と思ったが、 (画面上からは) ECSクラスタをCEに変換することはできなさそう。逆はできる、つまりCEをECSクラスタとして扱うことはできる。というか、CEを作った時点で空のECSクラスタが作成されている。末尾にUUIDとかついた、とてもゴチャったECSクラスタが作成される。

AWSというサービスの哲学として、Manageされるもの (e.g. ECSクラスタ) はManageするもの (e.g. CE) にはできない。これは言われれば分かるけど、気付かないと「不便だ」と不要な精神の不衛生を招く。

というわけで、ECSからそのままBatchに移行はできなさそうなので、今回のように特定のEIPを使い続けたいような場合、ECSコンテナインスタンスのEIPを、CEから生まれたECSコンテナインスタンスに移行時に付け替えることになる。

API使っても瞬断が出るが、まぁそもそもそこまでサービスレベルは高くない。はずだ。

でもこれって、ECSクラスタ間でコンテナインスタンスの移動ができれば解決するんじゃ?

と思って調べてみると

各コンテナインスタンスには、それぞれに固有の状態情報がコンテナインスタンスにローカルで保存され、Amazon ECS にも保存されているため、コンテナインスタンスを 1 つのクラスターから登録解除して別のクラスターに再登録しないでください。コンテナインスタンスリソースを再配置するには、1 つのクラスターからコンテナインスタンスを終了し、新しいクラスター内の Amazon ECS に最適化された最新の AMI で、新しいコンテナインスタンスを起動することをお勧めします。7

ぐはっ……。

サーバー系はECSに残し、バッチ系だけAWS Batchに、という住み分けが清潔なのかもしれないけど、それじゃ集約にならないなぁ。

Refs.

続きを読む

個人的mastodonの出来事(技術編)

何の話か

結果的に中規模インスタンス(一応某リストで上位100以内にはいっているから現状間違ってないはず)を4台立てることになったので、その経緯とかそこで知ったこととか書きます。

得られた知見

総括として、

  • 100-300人程度であればDBあたりはまだネックにならずメモリが真っ先に悲鳴をあげた(1GBしかない場合)
  • 100人超えると何もいじってないt2.microだときつい
  • 300人超えたくらいでもt2.smallでとりあえずスペック的には動く

ただし、金銭的な都合でRDSやElastiCache、複数EC2による冗長構成などをとっていないために、インスタンスサイズを変更する時など絶対にサービスが止まるので、そこは今回妥協しています。
また、CloudFrontやS3を使うこともできるのですがこちらも金銭的事情と今の所なんとかなっているので使ってないです。

環境としては

  • mailにmailgun
  • SSL証明書にLet’s Encrypt
  • サーバーはだいたいAWS
  • 最低限のメトリクス監視はmackerel

です。

mastodonをはじめたところ

4/12に多分Twitterでみかけてmastodonを始めた。現mstdn.jpインスタンスでアカウントを作りました。
しばらく楽しくやってたのですが、どんどん人が増えるのでコンセンプト的には分散させたほうがいいよなーという思いとか、mastodonで知り合った人にボドゲインスタンスほしいって言われたとか、この勢いなら人くるんじゃね?という企みとかそういうのがあって立てようかなと考え始めました。
結果、4/12は見送ったものの、4/13にmstdn.jpが不安定なこともあり分散する流れになるだろうと判断してそれなら急ごうとその夜から構築を開始することにしました。

1台目 tablegame.mstdn.cloud

どうせAWSとか使うと金銭的に維持できないと思い、家にあったサーバーを流用することに。
帰りにLANカードとバカHUBを買って帰宅。
マシン構成としては

  • ubuntu 14.04 LTS
  • CPU: Sempron 145
  • RAM: 4GB

と非常に貧弱ながらも、ジャンルがニッチなのでこれで試そうと突っ込みました。
ちなみに、Dockerは使っておらず、理由としてはDockerにそこまで明るくないので死活監視とかDB周りをいじる必要が出た時にうまく弄れる自信がなかったためです。

構築で苦労したことは、LANとFletsのPPPoE直の物理インタフェース2本構成になったので、そのroutingとかiptablesわすれたとかその辺です。
また、設定を間違えたままresorceをbuildしたところ、アプリの設定など以外の動的に変わる部分(メインのタイムラインですね)が何も表示されない事態に陥り、そこで困ったりしました。
消してrebuildしたら治りましたが。
あとは、ubuntu 14.04なのでデフォルトのdaemon化する設定が使えず、現状nohupで動かしています。streamのnodeがよく落ちるのでそこだけpythonで監視して落ちてれば再起動するスクリプトをcronで回すという手荒い対応です。

結果として現在300人弱ですが、快適に動いています。

2台目 socialgame.mstdn.cloud

思ったよりうまく動いて、昼休みに先輩と喋っていた勢いで2台目を作りました。
こちらはジャンル的に人が集まる可能性もあったので、AWSを使って、でもまずは最小構成でということでt2.microにすべて載せる構成で作りました。

こちらはUbuntu 16.04なので公式ドキュメント通りに構築しました。(ただしこちらも非Dockerで)

ちゃんと動いたのは良かったものの、その日の夜にメモリを使いきりswapし始めた途端にアクセスさえ困難な状況に。
この時、sshで入れない状況だったのですが公式ドキュメント通りに作っていたのが功をなし、AWSコンソールから直接停止、インスタンスサイズアップで難を逃れました。

結果t2.small一台で現状300人超えたくらいですが、快適に動いています。

3台目, 4台目

2台目を参考に構築しました。4台目のR18可能なソシャゲインスタンス構築の話についてはできれば別の記事で書きます。

技術的タスク

  • バージョンアップが早いのでバージョンアップをどう実施していくか。
  • SSL証明書の更新やLet’s Encryptについてもっと知る
  • mailgunが一部制限かかっているのでそれの対応

最後に

タスク整理とかもあったので書いてみたんですが、これ、得られる情報ありますかね。。。
とりあえずお一人様や100人未満のインスタンスなら多分t2.microで動くと思うので無料枠でお試しとかするといいですよ、くらいかもしれないです。

続きを読む

knife zeroをawsで扱う際にsshが切断されてしまう問題と解決策

awsでknife zero convergeを使うと起きる問題

awsでknife zeroを使う場合、shellが長い間応答しないコマンドが走るとフリーズしてしまいます。具体的にはdokkuをインストールしようとしましたが、この辺でsshが応答しなくなり、Ctrl+Cでキルするしかなくなります。

Importing herokuish into docker (around 5 minutes)

原因

awsはsshで長い間通信されない場合にインフラ側で自動的にルーティングを切ってしまうようです。ssh-clientやsshdは生きていますが通信が通らないという状態に陥ります。

この状態を回避するために、~/.ssh/configに定期的に通信して切られないよう設定を追加するという方法が提案されています。
http://qiita.com/euno7/items/00ef56244b7b193d2455

しかし、knife zeroではこの方法が働きません。

解決策

knife zeroで~/.ssh/configが働かない課題には2つの要因があり、それらの解決策について解説します。

knife sshでコンソールのssh_configが反映されていない

chefのソースコード上(chef/lib/chef/knife/ssh.rb)で、~/.ssh/configを読み込む処理がありますが、user,forward_agent,portくらいしか使われていませんでした。そこでServerAliveIntervalを働かせるように修正します。

https://github.com/kackyt/chef/pull/1/commits/8b6bfd586774b754108b0a1d03f9db21e8543553

Net::SSH::Multiでkeepaliveパケットが流れない

これでうまくいったーと正直私も思いましたが上だけでは解決しませんでした。Net::SSHでは動作しますが、knife zero(knife ssh)ではNet::SSHから派生したNet::SSH:Multiというclassが使われています。これにはNet::SSHでは実装されているServerAliveIntervalに対応するkeepaliveパケットを送信する実装が入っていませんでした。そこでNet::SSH:MultiにNet::SSH同様の実装を追加しました。

https://github.com/kackyt/net-ssh-multi/commit/19c3efba675c61c50a6468f5200f7e52efdfcc04

これでようやくcookbookが長引いてもちゃんと処理が完了するようになりました。やったね。

TODO

もろもろ落ち着いたらpull request書きます

続きを読む

JAWS DAYS 2017 ワークショップ Docker on Elastic Beanstalk 〜Days after tomorrow〜 (2)

この記事では、複数のDockerコンテナで構成される環境を構築するための設定例を具体的に解説します。ワークショップのフォローアップではありますが、一般的な事柄を扱いますので、ワークショップに参加されていない方にもお読み頂ける内容です。

前回の記事: JAWS DAYS 2017 ワークショップ Docker on Elastic Beanstalk 〜Days after tomorrow〜 (1)

春ですね

こんにちは。Emotion Techの子安です。ようやく暖かくなってきましたね。やっぱり春といえば桜ですかね!

sakura.jpg

恐縮です。

前回のつづき

さて、前回はテーマとする環境の構成を説明し、利用するコンテナ定義ファイル docker-compose.yml の枠組みを解説しました。更に web app db コンテナのうち、 web db コンテナの設定を見ました。今回はアプリケーションそのものである app コンテナを見ていきます。

各コンテナの設定 2

appコンテナ

app:
  image: phusion/passenger-ruby23:0.9.20
  environment:
    APPLICATION_ENV: development
    APPLICATION_ROLE: app
    PASSENGER_APP_ENV: development
  labels:
    eb.workshop.role: app
  networks:
    - eb.workshop
  depends_on:
    - db
  volumes:
    - ./app/init/40_setup.sh:/etc/my_init.d/40_setup.sh:ro
    - ./app/passenger/passenger.conf:/etc/nginx/sites-available/default:ro
    - ./app/rails-app:/var/www/rails-app

Ruby on Railsのアプリを実行するappコンテナです。phusion/passenger-dockerイメージを利用しています。このイメージはさまざまな機能を持っていますが、まずは設定内容を見ましょう。

  • image: phusion/passenger-dockerイメージを指定しています。
  • environment: イメージ側で PASSENGER_APP_ENV 環境変数を読んでRailsの環境を切り替えてくれる機能があります。
  • volumes: ここで3つのマウントを指定しています。コンテナ側のパスを見てください。

    • /etc/my_init.d/40_setup.sh:ro: /etc/my_init.d 配下に置いたスクリプトを初期化時に実行してくれる機能があります。実行権限を忘れずにつけるようにします。 :ro は読み取り専用の意味です。
    • /etc/nginx/sites-available/default:ro: ここにnginxの設定ファイルが置かれています。
    • /var/www/rails-app: Railsのアプリケーションをここに置きます。これは任意の場所で構いません。上記のnginx設定ファイルに記述しています。

phusion/passenger-docker

phusion/passenger-dockerは、Phusion社がメンテナンスしているDockerイメージです。このイメージは、さらに別のイメージ phusion/baseimage-docker をベースとして作られています。このphusion/baseimage-dockerには、いくつかの有用な機能が含まれています。

  • 正しいinitプロセス: /sbin/my_init にinitプロセスを内蔵しています。本来initプロセスには、親プロセスのいなくなった子プロセス(孤児)を里親として引き受ける役割があります。加えて SIGTERM を受け取った際に、他のサービスを終了させる機能を持ちます。
  • スーパーバイザ: 軽量なスーパーバイザ runit を内蔵しています。
  • ログマネージャ: syslogデーモン syslog-ng を内蔵しています。logrotateも設定済みです。
  • cron: cronデーモンを内蔵しています。

重要なことは、これらの機能が協調動作するように設定されていることです。 /sbin/my_init がrunitを立ち上げ、runitがsyslog-ngやcronを管理します。デフォルトのコンテナ起動コマンドが /sbin/my_init に設定されていますので、

docker run phusion/baseimage

として起動することで、最小限のコンテナ化されたLinux(Ubuntu)環境を手に入れることができます。他にも、今回も利用している /etc/my_init.d 配下のスクリプトを初期化時に実行する機能、root以外でコマンドを実行するための setuser コマンドなど、様々な機能や仕組みを備えています。詳しくはWebページを参照ください。

phusion/passenger-dockerは、このphusion/baseimage-dockerの上に、nginxやrubyのアプリケーションサーバである Phusion Passenger をインストールしてあります。nginxはrunitの配下に設定されていますので、すぐにスーパーバイズされたデーモンとして稼働させることができます。

コンテナ初期化スクリプト

可能なことはコンテナ自身に判断させるのが良い設計です。そのためにコンテナ起動時に実行するスクリプトを組み込むことがよく行われます。今回 app コンテナで設定している初期化スクリプトの内容を見てみましょう。 /app/init/40_setup.sh を参照ください。

このスクリプトの主な役割は4つです。

  • nginxを起動可能にする
  • Railsアプリのセットアップ
  • 環境変数の引き渡し
  • DBマイグレーション

ハンズオンのため、本来はコンテナ初期化スクリプトに記述すべきでない処理もここに入っていますが、続けて詳しく説明します。

/app/init/40_setup.sh
rm -f /etc/service/nginx/down

これはrunitの機能です。サービスを定義するディレクトリ(今回は /etc/service/nginx )の直下に down という名前のファイルがあると、自動起動しません。phusion/passenger-dockerイメージ側で配置されているものですが、自動起動してほしいのでファイルを削除しています。

/app/init/40_setup.sh
chown -R app:app /var/www
cd /var/www/rails-app
setuser app bundle install --path=../vendor/bundle
if [ "$PASSENGER_APP_ENV" = 'production' ]; then
  setuser app bin/rails assets:precompile
  setuser app bundle install --path=../vendor/bundle --without test development --deployment --clean
fi

このあたりは実環境であればCIで、ビルドの一処理として適切に実施すべきものです。アプリケーションのファイルパーミッションを適切に設定し、ライブラリのインストールを行います。更にproductionモードであれば、アセットのプリコンパイルと不要なライブラリの削除を行います。

/app/init/40_setup.sh
echo "passenger_env_var 'SECRET_KEY_BASE' '$SECRET_KEY_BASE';" >> /etc/nginx/conf.d/10_setenv.conf
echo "passenger_env_var 'RDS_HOSTNAME' '$RDS_HOSTNAME';" >> /etc/nginx/conf.d/10_setenv.conf

ここは環境変数をpassengerへ引き渡すための処理です。コンテナでは環境変数を扱う機会が多くなりますので、利用するミドルウェアに合わせて設定が必要です。

/app/init/40_setup.sh
RAILS_ENV=$PASSENGER_APP_ENV setuser app bin/rails db:create db:migrate

こちらはDBのマイグレーションです。この処理も実環境であればコンテナ初期化スクリプトではなく、デプロイの手順の一貫として実施すべきものです。

次回へ

ここまでが app コンテナの設定でした。長くなりましたので、つづきは次回にしたいと思います。次回はElastic Beanstalkの設定ファイル、Dockerrun.aws.jsonを解説する予定です。

続きを読む