@ijin

[Michael H. Oshita]

Elastic Beanstalkへの簡単ssh

AWS Elastic Beanstalk上で管理されているinstanceにsshするにはeb sshコマンドを使えば割りと簡単に接続できますが、アクセスキーの設定が必要で、開発者が多い場合にそれを発行してばら撒いて管理するのはかなり面倒です(IAM user/roleの割当にしても)。特に極稀にしか接続する必要がない場合。

そこで、API GatewayとLambdaでinstanceのpublic ipを返却するエンドポイントを作り、SSH configを設定すれば誰でもアクセスできるようにしてみました。

前提条件

  • instanceにはログインユーザーのssh keyが登録積み(.ebextensions等で)
  • ssh localhost可能である
  • Elastic Beanstalkのenvironment名は数値で終わらない
  • Auto ScalingのTermination PolicyがNewestInstance

Lambda

やってる事は単純で自動的に付与されるelasticbeanstalk:environment-nameタグのついたinstanceのipを(パラメータに応じて)返却するだけ。複数ある場合は、起動した順で。

せっかくなので新しくサポートされたPythonで記述。

当然、LambdaのIAM roleでec2:Describe*のAction許可も必要。

Swagger

API Gatewayのresourceをコンソールでポチポチ作るのがイヤだったのでAPIの状態をSwaggerで定義してみた。awscliは生REST APIが辛い

編集しながらドキュメントも見れるonline editorが結構便利。

API Gateway

Swagger上でYAMLで作成した定義書はjsonとしてexportし、aws-apigateway-importerでAPIのresourceやmethodやらを生成する。

import処理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
$ ./aws-api-import.sh -c swagger.json
2015-11-04 02:23:18,507 INFO - Attempting to create API from Swagger definition. Swagger file: swagger.json
reading from swagger.json
2015-11-04 02:23:18,649 INFO - Parsed Swagger with 2 paths
2015-11-04 02:23:18,655 INFO - Creating API with name EB API
2015-11-04 02:23:19,417 INFO - Removing default model Error
2015-11-04 02:23:19,634 INFO - Removing default model Empty
2015-11-04 02:23:19,872 INFO - Creating model for api id nx3r6d6yhh with name IP
2015-11-04 02:23:20,114 INFO - Generated json-schema for model IP: {"type":"object","properties":{"ip":{"type":"string","description":"IP address."}},"definitions":{}}
2015-11-04 02:23:20,329 INFO - Creating model for api id nx3r6d6yhh with name Error
2015-11-04 02:23:20,338 INFO - Generated json-schema for model Error: {"type":"object","properties":{"code":{"type":"integer","format":"int32"},"message":{"type":"string"},"fields":{"type":"string"}},"definitions":{}}
2015-11-04 02:23:20,770 INFO - Creating resource 'eb' on bd5pqsq37h
2015-11-04 02:23:21,420 INFO - Creating resource '{env_name}' on 8rjsbt
2015-11-04 02:23:22,079 INFO - Creating resource 'ip' on 4cg26s
2015-11-04 02:23:22,931 INFO - Creating method response for api nx3r6d6yhh and method GET and status 200
2015-11-04 02:23:23,144 INFO - Creating new model referenced from response: IPaddresses
2015-11-04 02:23:23,144 INFO - Creating model for api id nx3r6d6yhh with name IPaddresses
2015-11-04 02:23:23,153 INFO - Generated json-schema for model IPaddresses: {"type":"string","definitions":{}}
2015-11-04 02:23:23,770 WARN - Default response not supported, skipping
2015-11-04 02:23:23,772 INFO - Creating method parameter for api nx3r6d6yhh and method GET with name method.request.path.env_name
2015-11-04 02:23:23,998 INFO - Creating method for api id nx3r6d6yhh and resource id md6qcm with method get
2015-11-04 02:23:24,822 INFO - Creating resource '{server_num}' on qmd6mc
2015-11-04 02:23:25,689 INFO - Creating method response for api nx3r6d6yhh and method GET and status 200
2015-11-04 02:23:25,896 INFO - Found reference to existing model IPaddresses
2015-11-04 02:23:26,306 WARN - Default response not supported, skipping
2015-11-04 02:23:26,306 INFO - Creating method parameter for api nx3r6d6yhh and method GET with name method.request.path.env_name
2015-11-04 02:23:26,525 INFO - Creating method parameter for api nx3r6d6yhh and method GET with name method.request.path.server_number
2015-11-04 02:23:26,764 INFO - Creating method for api id nx3r6d6yhh and resource id 5udxeo with method get

これでAPI Gateway上に階層が出来上がり。

後は各GET methodでLambdaへの関連付けをし、Integration RequestIntegration Responsemapping templateでパラメータやレスポンスを設定。(多分、この手順もswaggerで定義できるとは思うけど)

integration request

integration response

参考:

endpoint

Deployすれば、以下のAPIでipが取得可能。

/$API_ENDPOINT/prod/eb/$EB_ENV_NAME/ip/[n]

instance ip一覧

1
2
3
$ curl -s https://altvsxa2kj.execute-api.ap-northeast-1.amazonaws.com/prod/eb/my-ebenv-production/ip
54.189.149.5
54.189.168.83

特定のinstance ip

1
2
$ curl -s https://altvsxa2kj.execute-api.ap-northeast-1.amazonaws.com/prod/eb/my-ebenv-production/ip/1
54.189.149.5

ipの部分だけ、PATHじゃなくquery paramterにした方がAPI Gatewayの設定がシンプルだったけど、少しでもRESTfulにしたかったもので。(まあ、そもそもjson返してないけど)

SSH Config

さて、ここからがキモです。

~/.ssh/config

1
2
3
4
Host my-ebenv-production*
  User ec2-user
  StrictHostKeyChecking no
  ProxyCommand ssh -A localhost -W `N=$(grep -o '[0-9]\+$' <<< %h || echo 1); E=$(perl -pe 's/\d+$//' <<< %h); curl -s https://altvsxa2kj.execute-api.ap-northeast-1.amazonaws.com/prod/eb/$E/ip/$N`:%p

上記の設定でssh $EB_ENV_NAME[n]で指定したサーバに接続できるようになります。

仕組みとしては、ホスト名をparseしAPI Gatewayに適切なリクエストを行い、返却されるpublic ipにローカル端末自身を踏み台としてsshするProxyCommandの設定です。

例えば、2番目(に起動した)のサーバに接続する場合は

1
2
3
4
5
6
7
8
9
10
11
12
13
$ ssh my-ebenv-production2
Last login: Wed Nov  4 01:19:14 2015 from ndx6-ppp413.tokyo.sannet.ne.jp
 _____ _           _   _      ____                       _        _ _
| ____| | __ _ ___| |_(_) ___| __ )  ___  __ _ _ __  ___| |_ __ _| | | __
|  _| | |/ _` / __| __| |/ __|  _ \ / _ \/ _` | '_ \/ __| __/ _` | | |/ /
| |___| | (_| \__ \ |_| | (__| |_) |  __/ (_| | | | \__ \ || (_| | |   <
|_____|_|\__,_|___/\__|_|\___|____/ \___|\__,_|_| |_|___/\__\__,_|_|_|\_\
                                       Amazon Linux AMI

This EC2 instance is managed by AWS Elastic Beanstalk. Changes made via SSH 
WILL BE LOST if the instance is replaced by auto-scaling. For more information 
on customizing your Elastic Beanstalk environment, see our documentation here: 
http://docs.aws.amazon.com/elasticbeanstalk/latest/dg/customize-containers-ec2.html

1番目の場合は明示的に指定する必要はありません。

1
$ ssh my-ebenv-production

また、Auto Scalingでinstanceが増減せずに1台のみの場合はもっとシンプルに記述できます。

1
  ProxyCommand ssh -A localhost -W `curl -s https://altvsxa2kj.execute-api.ap-northeast-1.amazonaws.com/prod/eb/$E/ip`:%p

簡単!

でも、よくよく考えたら普通のAuto Scalingの場合でも利用できる事に気付きました。

まとめ

  • Swaggerなかなか面白い
  • API GatewayとLambda便利
  • Lambda pythonも良い。けど、rubyサポートも早う。。

AWS re:Invent 2015に参加してきた

今年で3度目の参加となるAWS re:Invent。 忘れない内に記録を残しておきます。

Day 0

Game Day

Unicornを貸し出すサービスを展開する仮想のスタートアップ企業にDevOpsチームとして最近入社したという設定。前任者が退職しており、資料が少ない中でサービスオープンに立ち会いつつ、様々な困難に直面するというフルデイ・イベント。 今までのGame Dayと違って面白いのはパフォーマンス・チューニングをしつつも、コストも意識しながらチーム間でスコアを競争するところ。アプリは触れないので、ISUCONよりは昔やったチューニンガソンに近い感じ。

スコアは累積の損益。アーキテクチャによっては利益が出たり損失が出たりする。例えば、多くのリクエストが処理できると利益は増すが、AWSのリソースが多いと費用が掛かって損失になりうる。 当然最初は各チームは赤字から始まり、時間とともに積算した利益によって黒転して行く様が目新しかった。

結果、48チーム中で6位。(上位2チームはチートで失格となったので実質は4位

ちなみに最速レスポンスタイムはうちのチームが叩きだした。

詳細は今度日本で開催されるかも知れないので控えておくが、非常に楽しめたので次回は運営側に回って手伝おうかと思います!

Day 1

Keynote

Andy Jessy副社長による発表。今年のテーマは「自由

  • Amazon QuickSight
  • Amazon Kinesis Firehose
  • Amazon Snowball
  • MariaDB for RDS
  • AWS Database Migration Service
  • AWS Schema Conversion Tool
  • AWS Config Rules
  • Amazon Inspector

Oracleからの自由、解放!

WRK306 - AWS Professional Services Architecting Workshop

実在した、ある企業のクラウド移行案件。RFP的なモノがあり、アーキテクチャをチーム内で議論し、最後にそれぞれ各チームが発表していく流れ。 かつてjawsugで主催を手伝ったワールドカフェ形式とほぼ同じだったので、チームメンバーを先導してCacooでさくさく構成図を起こしていく。 他のチームが模造紙にラフスケッチで発表する中、我らは綺麗に正本して、プロジェクターで登壇しながら発表。

最後に実際にどう移行したかというAWSチームからの回答。まず、移行フェーズを段階的に分け、最初はシステムをほぼそのままクラウド上に乗せた後に部分的に最適化してコンポーネントを置き換えて行ったという話。最後にLambdaになっていた部分があったのが興味深かった。

早く新サービスに対応したAWS Simple Iconsのアップデートが待たれるところ。

今回提案した内容。

WRK305 - Zombie Apocalypse Survival: Building Serverless Microservices

Zombie Apocalypseが起こって、人類存亡の危機!途中まで実装されたチャットルームの機能を拡張・実装して危機を救え!というシナリオの元、LambdaとAPI Gatewayとjavascript sdkで実装されたサーバーレスアーキテクチャのワークショップ。

機能拡張の為に実装が必要なので、設計しながらチーム内で作業分担し、コードをせっせと書いていく。 ゾンビ出現のアラート通知、ヒートマップ描画、アンデッドカウンター、緊急食料倉庫の位置情報配信等、面白い機能要求が盛り沢山。

ささっとSlack部屋を作り、githubでコードを共有しながらのチームワーク作業。多分、うちらのチームが一番多く実装できた感触。

このワークショップはかなりの人気で、開始30分前にすでに長蛇の列が。 運良くぎりぎり最後の参加者として入れたけど、皆どれだけゾンビが好きなんだ。。

Day 2

Keynote

Wernerl Vogels CTOの発表。

  • Amazon Kinesis Analytics
  • X1 instance (100 cores, 1TB RAM)
  • t2.nano instance
  • Amazon EC2 Container Registry
  • Lambda
    • VPC support
    • Long running Functions (300s)
    • Scheduled Functions
    • Custom Retry Logic
    • Python
  • AWS IOT

前日にAndyが7つの自由を語って、当日はWernerが7つの法則を語る。

WRK308 - AWS + ASK: Teaching Amazon Echo New Skills

Amazon Echoを使った、Alexaのプログラミングワークショップ。音声によって、Echo経由でLambdaイベントを呼び出し、Alexaサービスと連携するカスタマイズしたスキルセットを実装して行く。

例えば、Alexaに好きな色を覚えさせて、後ほど聞くと答えてくれる機能とか。全てボイスコントロール。吉田さんの英語でも通じたので、かなり優秀。

新品のEchoを開封して使ったので、最後に貰える物かとささやかに期待したものの、$15のAWSクーポン配布のみ。さすがケチFrugalなAmazonさん。

re:Play

EDMの若きプリンスDJ、Zeddをシークレットゲストとして呼んでのアフター。

もう完全にWernerのパーティー。

Zeldaのremixが良かった。

終わりに

結局セッションは一つも出なかったです。まあ、ビデオやスライドは公開されるので内容自体は後で把握可能なので別にいいかな。授業を聞きに来た分けでもないし。 それよりも、現地に来ているエンジニアと交流したり、実装まで含んだハンズオンのワークショップをやった方が楽しいし、糧となる。後はトレンドを肌感覚として体感するには良い場所なので行った事ない人には是非オススメしておきたい。

#HashiConf 2015に参加してきた

Portlandで開催されたHashiConf 2015に参加してきました。

Day 1

Keynote

Founder & CEOのMitchell Hashimotoが登壇。 今までのHashiCorpプロダクトを紹介した上で、新プロダクトの紹介!

Nomad

コンテナスケジューラ。ちょうどあるサービスの一部のDocker化でスケジューリング方法を考えていたので、グッドタイミング。

Otto

開発からデプロイまで対応したmicroservicesを念頭に入れたVagrantの後継。

Operating Consul as an Early Adopter

  • 初期の頃からconsulを運用した苦労話
  • v0.2から始めた
  • consule-template
    • スケールしない。v0.6ではマシに
    • 大きいクラスタで自身をDDOSする
    • watchesを使わないでcronで-once
  • consulのDNSが不安定な時期があったので、15秒毎にzone fileを書き出し内部DNSで運用
  • その他初期バージョンで諸々不具合を発見してArmonと一緒に修正
  • ご苦労様

HashiCorp Tools in the Modern Enterprise

  • エンプラだって頑張ってDevOpsしてるもん!
  • 文字多めで眠くなっちゃった。。
  • Izanamee イザナミ?

Resillient Infrastructure with Serf

  • Pagerdutyの人
  • SerfとChefと自社ツールを組み合わせて堅牢なインフラを構築
  • Blender - a modular remote command execution framework
  • SerfX - a bare minimum ruby client for serf

Genesis: Terraforming a new Firefox crash stat infrastructure

  • Mozillaの人達による漫才ベースのプレゼン
  • Firefoxのクラッシュ統計データを受信するインフラの構築について
  • 結構笑いがとれてた

The future of management, and how we’ll get there

Puppetの作者Lukeさん。未来に観するディープで知見溢れるトーク

Day 2

Keynote

HashiCorpの哲学とAtlasの説明(2日目は新製品の発表はなし)

Dockerizing all the Things

  • Docker社内でのCIでconsul使ってるよって話
  • あまり面白くなかった
  • 20分も残して終了。。

Automate your Infrastructure

以下のツールでいろいろ自動化してるよ(フーン)

  • Terraform
  • CircleCI
  • Docker (ECS)
  • Datadog
  • Slack

Managing Applications at Scale

  • CoreOSの人
  • Nomad vs Kubernetes
  • CLI demo

Repeatable, Extensible Infra at Yelp

  • YelpのSite Reliability Engineer
  • 内部ツールで起動、puppetでプロビジョニング
  • Packerやpuppet managed VPCだったけど、今はTerraform
  • Smartstack (Service discovery)
    • Nerve
    • Synapse
    • Hacheck
    • qdisc_tools
  • Sensu
  • nsone
  • terraform community modulesをたくさん書いたよ

Virtual Machines, Containers, Lambdas? Oh my!

  • AWSの中の人(DevOps担当)
  • Microservicesについて
  • 導入の判断基準
  • VM(EC2, Packer, Terraform/Cloudformatio, Consul)
  • Container (ECS, Nomad)
  • Lambda (サーバレスアーキテクチャ。例:SquirrelBin
  • さらっと自社サービスをたっくさん紹介した後、結論はLambdaとAPI Gateway押し
  • そんなにサービスを詰めこめなくても。。

感想

Nomadの発表が一番エキサイティングだったけど、こういうカンファレンスは中の人やスピーカーや他のユーザーと直接話せるのが醍醐味。プロダクトの担当者に直接フィードバックを伝えたり、展望を聞いたりできて楽しかったです。また、会場で話した参加者も多種多様でアメリカ以外だとスペイン、オーストラリア、ニュージーランド、インド、日本等がいて、世界中から注目されている感じでした。

第一回目ということで約300人の中規模だけど、このぐらいが皆の距離感近くて良い雰囲気。

その他

  • 懇親会で飛び込みLTしたかったが会場設備が対応できなく、断念
  • Portland心地良くて過ごしやすい!
  • 記念にもらったHashiCorpとハシ(箸)をかけたジュークは誰も理解してなかった (Mitchellが言うには日本人参加者が理解してくれるから良いんだと)

さて、来週はre:Inventだ!

ISUCON5の予選に記念参加してきた

とあるエンジニアが言い放ちました。

上位に届かないチームはただの記念参加だよねー。

というわけで、今年のISUCONは見事に予選落ちしました。

お題

ISUxi」というどこか懐かしい響きの「高負荷に耐えられるSNSコミュニティサイト」

友達、記事、コメント、足あと機能が揃っており、以前より複雑度が増したなかなかの凝った作り。

起こったこと

これはESIによるpartial cachingが出来るので、Varnishでやってみよう!

と意気込んだものの、エラーが解決できず結局は間に合わずに終了ーーっ。

やりたかったこと

  • Varnish + ESI + Redis & MySQL

やれたこと

具体的な作業内容は今手元にないので、ぼんやりと。

  • MySQLのクエリー修正、パラメータ、Indexの追加
  • nginxのパラメータ追加
  • kernelのパラメータ追加
  • 一部 redis化
  • rubyプロセスの調整
  • sidekiqのワーカーで遅延書き込み

はまったとこ

今回はいろいろ初めてがあったので無駄に時間を潰した感じ。

  • systemd - 最新のUbuntu 15.04はsystemdなんですね。ずっと14.04 LTSのupstartに慣れていたので、こっちは初めて触わった。
  • apparmor - 設定ファイルをsymlinkでやろうとすると読み込まれず。。こいつのせいだと気づくのにちょっと手間取った。いらない子には怒りのapt-get purgeを。
  • mysql - なんか設定ファイルが/etc/mysql/my.cnf単体じゃなくて、symlinkされたりいろんなディレクトリに散らばってたりで若干戸惑った。後、上記のapparmor関連で設定ファイルがしばらく全然反映されずに。。
  • varnish - こちらもvclやパラメータ指定が本来/etc/default/varnishで出来るはずが、なかなか反映されずに苦労した。Varnish 4.0のパッケージはapt-getですんなり入るんだけど、起動スクリプトが/etc/init.d/varnishに設置されるのに、そちらは無視され、systemdの方が優先される模様。カスタマイズするには/lib/systemd/system/varnish.serviceをコピって編集してdaemon-reloadが必要。詳しくはこちら

終わりに

競技内容が確実に進化していて、非常ーーに楽しめました!その分、出題者の苦労が計り知れず。。こんどビールでも奢ります。今年の本戦は残念ながら行けないけど、外野として楽しくポップコーンでも食べながら観戦したい所ですね。

おまけ

現在はHashiConfに向けての飛行機の中だけど、悔しいので機内で復習しておきます!

YAPC::Asia Tokyo 2015に参加してきた

例年この時期は海外にいるので、なかなか行けなかったYAPC::Asia Tokyo 2015にやっと参加してきた。

スケジュール上、今年は参加できるぞって意気込んでいたら、まさかのSold Out…

それをtwitter上で嘆いていたら、@kenjiskywalkerさんから救いの手が!

感謝!

さて、一口コメント

Consulと自作OSSを活用した100台規模のWebサービス運用] - @fujiwara

mamiyaAWS CodeDeployにインスパイアされたstretcherを使ったイベントベースのdeploy周りが面白かった。s3からの取得失敗時の処理は考えてないといけない。

Conway’s Law of Distributed Work - Casey West

最近はリモートワークが殆どなので、なかなか共感できる部分が多かった。コミュニケーション大事。

大規模でも小中規模サービスでも捗る microservices な Web サービスのつくりかた - @Yappo

どのタイミングでコンポーネントの分割をするか。ちゃんと設計しようねって話

ISUCONの勝ち方 - @kazeburo

ポイントがよく纏められていて、うちらのチームでも多くを実践していた。まだ勝ててないけど。 CPUやB+ Treeの気持ちになって考える事が大事。

我々はどのように冗長化を失敗したのか - @kenjiskywalker

Networkが不安定だと、様々な冗長化に仕組みがうまく作動しない。検証はProductionと同じ環境でやりましょう。

Profiling & Optimizing in Go - Brad Fitzpatrick

Bradさんのライブコーディング。奥が深かった。

LT

どれもこれもハイレベル!

特に素晴らしかったのが、テンポの良い@yoku0825さんのMySQL 5.7の罠があなたを狙っている と超早業を披露したCONBUさんのCONBUの道具箱


いやー、非常に楽しめました。運営の皆様、ありがとうございました!!

さて、これから出国ー。

GitHubのeventをLambdaで処理してSlackへ通知

ちょっと前にGitHubのeventをAWS Lambdaで処理して、GitHubやSlackのAPIを叩く仕組みを作ったので、メモ。

材料

  • Github
  • SNS
  • KMS
  • Lambda
  • Slack

やりたいことはこんな感じ。

あるGitHubリポジトリのissuesに特定のコメントが書き込まれると、そのユーザはプロジェクトのteamに自動で追加されて、Slackへ通知が流れるというモノです。

SNS

まずは媒介となるSNSの作成。

1
2
3
4
$ aws sns create-topic --name github --region ap-northeast-1
{
    "TopicArn": "arn:aws:sns:ap-northeast-1:123456789012:github"
}

次にSNSに対してpublishできるIAM userを作成

IAM Policy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "sns:Publish"
      ],
      "Sid": "Stmt0000000000000",
      "Resource": [
        "arn:aws:sns:ap-northeast-1:123456789012:github"
      ],
      "Effect": "Allow"
    }
  ]
}

GitHub

  • organization: my_org
  • repository: test
  • team: reader

SNS連携

Webhooks & Services からAmazonSNSと連携

AWS KEYには先ほど作成したIAMユーザのを利用。SNS topicのarnにはregionが書かれているにも関わらず、GitHubの方では明示的に指定が必要。

通知するeventの有効化

さて、GitHubではSNSの場合、hooksのjsonにある通り、push時のeventにしか対応していないので、

hooks link
1
2
3
4
"name": "amazonsns",
"events": [
  "push"
],

GitHubのWebhook APIに従ってissue_commentを追加してやります。

先ほどのSNS連携のhook idを取得するにはGET /orgs/:org/hooks

1
2
export HOOK_ID=$(curl -H 'Uer-Agent: ijin' -X GET -s -H "Authorization: token xxxxxxxxxx" \
https://api.github.com/repos/my_org/test/hooks | jq '.[].id')

編集するにはPATCH /orgs/:org/hooks/:id

1
2
curl -H 'Uer-Agent: ijin' -X PATCH -s -H "Authorization: token xxxxxxxxxx" \
https://api.github.com/repos/my_org/test/hooks/$HOOK_ID -d '{"add_events": ["issue_comment"]}' | jq .

Web UIからは分からないので、ついでにreader teamのIDも取得しておく

1
2
curl -H 'Uer-Agent: ijin' -X GET -s -H "Authorization: token xxxxxxxxxx" \
https://api.github.com/orgs/my_org/teams | jq '.[] | select(.name=="reader")'

(※)User-Agentは必須

これで、誰かがコメントをした時にもSNSが飛びます。

KMS

lambdaでは以下の認証情報を使うので、予めKMSで暗号化しておく。

  • GitHub token
  • Slack webhook

rubyで暗号化する場合

1
2
3
4
require 'aws-sdk'
require 'base64'
kms = Aws::KMS::Client.new(region: 'us-east-1')
Base64.encode64 kms.encrypt(key_id: "alias/ijin", plaintext: 'my plain text code').ciphertext_blob

javascriptの場合

1
2
3
4
5
6
7
var aws = require('aws-sdk');
var kms = new aws.KMS({ region: 'us-east-1' });
var text = 'my plain text code';
kms.encrypt({KeyId: 'alias/ijin', Plaintext: text}, function(err, data) {
  if (err) console.log(err, err.stack);
  else console.log(data.CiphertextBlob.toString('base64'));
});

こうしておくと、SCMに入れても安心。

また、KMSキーの実行権限もlambdaのroleに紐づけておく。

Lambda

Lambda function作成時にはSNSをevent sourceとして指定。

(※) KMSは現在us-eastにしかないので、そこ以外のregionでlambdaを実行する場合は、timeoutは若干長めに指定して置くと良さげ

コードはこんな感じ。

  1. GitHubからSNS hookを受け取って
  2. コメントした内容がjoinの場合
  3. KMSによってGitHubのtokenを復号化し
  4. そのユーザをteamへ追加する
  5. その後、別lambda functionでslackへ通知する

nodeのlibraryを使うともっとスッキリ書けたけど、1 function 1 fileで纏めたかったのでやや冗長なコードになっちゃいました。。

実行

GitHubでコメント

Lambda発動

1
2
3
4
5
6
7
8
9
10
11
2015-08-11T15:42:53.108Z   a09e3c80-403f-1e15-bbb5-55b693433c0e    user: ijin2
2015-08-11T15:42:53.108Z  a09e3c80-403f-1e15-bbb5-55b693433c0e    comment: join
2015-08-11T15:42:55.417Z  a09e3c80-403f-1e15-bbb5-55b693433c0e    status code: 200
2015-08-11T15:42:55.418Z  a09e3c80-403f-1e15-bbb5-55b693433c0e    response:
{
    "state": "pending",
    "url": "https://api.github.com/teams/1234567/memberships/ijin2"
}
2015-08-11T15:42:55.418Z  a09e3c80-403f-1e15-bbb5-55b693433c0e    Added to the team
END RequestId: a09e3c80-403f-1e15-bbb5-55b693433c0e
REPORT RequestId: a09e3c80-403f-1e15-bbb5-55b693433c0e Duration: 5211.48 ms Billed Duration: 5300 ms Memory Size: 128 MB  Max Memory Used: 14 MB   
1
2
3
4
5
6
7
8
9
10
11
2015-08-11T15:42:56.058Z   a25c66fc-403f-11e5-b291-25d4ee441689    Received event:
{
    "username": "ijin2",
    "icon_url": "https://avatars.githubusercontent.com/u/12809425?v=3",
    "text": "Added to the team"
}
2015-08-11T15:42:58.310Z  a25c66fc-403f-1e15-b291-25d4ee441689    200
2015-08-11T15:42:58.311Z  a25c66fc-403f-1e15-b291-25d4ee441689    ok
2015-08-11T15:42:58.311Z  a25c66fc-403f-1e15-b291-25d4ee441689    Successfully posted to Slack!
END RequestId: a25c66fc-403f-11e5-b291-25d4ee441689
REPORT RequestId: a25c66fc-403f-1e15-b291-25d4ee441689 Duration: 2268.06 ms Billed Duration: 2300 ms Memory Size: 128 MB  Max Memory Used: 14 MB   

teamへの追加(招待)

Slackへ通知

結論

簡単にできますね。実はこんな風に作っちゃった2週間後ぐらいにAWSの人も似たようなブログを書いていた事を発見しましたが。まあ、こっちはKMSとSlack使ってるので。。

後、KMSのアイコンが欲しい!

Data PipelineによるDynamoDBのexport

ちょっとハマったのでメモ。

DynamoDBにはRDSみたいなスナップショットによるバックアップ機能がなく、データを一括でexportするにはフルスキャンしかありません。 AWSではData Pipelineによるs3へのexportテンプレートがあって、それを使うとEMRクラスタが立ち上がりHive経由で大量の処理をして、s3へ書き出してくれます。

1000万件程度の小さな件数だとデフォルトのテンプレートがそのまま使えるけど、1億件近くになると失敗したりタイムアウトしたりするので、パラメータの調整が必要になってきます。

前提

  • 約1億件
  • 20GB
  • Provisioned Throughput (reads): 1000
  • Read Throughput Percent: 1.0
  • 2時間以内のexport

エラー

具体的には以下のようなエラーに遭遇しました。

1
2
3
4
5
6
7
8
9
10
11
12
13
Caused by: org.apache.hadoop.hive.ql.metadata.HiveException: Hive Runtime Error
 while processing row {"item":{"id_list":"{\"l\":[{\"n\":\"958\"},
{\"n\":\"704\"},{\"n\":\"847\"},{\"n\":\"232\"},{\"n\":\"72\"}]}",
"code":"{\"s\":\"adarea9\"}","user_area":"{\"s\":\"91657-adarea9\"}",
"user":"{\"s\":\"91657\"}","app_code":"{\"s\":\"xxx\"}","last_seen_at":
"{\"s\":\"2010-06-23 22:57:49 +0000\"}","target_id":"{\"n\":\"395\"}",
"count":"{\"n\":\"44\"}","promo_id":"{\"n\":\"125\"}"}} at 
org.apache.hadoop.hive.ql.exec.MapOperator.process(MapOperator.java:550) at 
org.apache.hadoop.hive.ql.exec.mr.ExecMapper.map(ExecMapper.java:177) 
... 8 more Caused by: org.apache.hadoop.hive.ql.metadata.HiveException: 
java.io.IOException: All datanodes 10.160.102.191:9200 are bad. Aborting... at 
org.apache.hadoop.hive.ql.exec.FileSinkOperator.processOp(FileSinkOperator.java:651) at 
org.apache.hadoop.hive.ql.exec.Operator.forward(Operator.java:793) at org.apach
1
2
3
4
5
6
7
8
9
10
11
12
2015-06-29 13:33:31,610 Stage-1 map = 100%, reduce = 0% MapReduce Total cumulative 
CPU time: 14 minutes 19 seconds 0 msec Ended Job = job_1435578726935_0002 with 
errors Error during job, obtaining debugging information... 
Examining task ID: task_1435578726935_0002_m_000008 (and more) from job job_1435578726935_0002 
Examining task ID: task_1435578726935_0002_m_000000 (and more) from job job_1435578726935_0002 
Examining task ID: task_1435578726935_0002_m_000000 (and more) from job job_1435578726935_0002 
Task with the most failures(4): 
----- Task ID: task_1435578726935_0002_m_000003 URL: http://ip-10-160-25-23.ap-northeast-1.compute.
internal:9026/taskdetails.jsp?jobid=job_1435578726935_0002&tipid=task_1435578726935_0002_m_000003
----- Diagnostic Messages for this Task: Error: GC overhead limit exceeded FAILED: 
Execution Error, return code 2 from org.apache.hadoop.hive.ql.exec.mr.MapRedTask 
MapReduce Jobs Launched: Job 0: Map: 10 Cumulative CPU: 859.0 sec HDFS Read: 0 HDFS Write: 0 FAIL Total MapRed
1
2
3
4
5
6
7
8
9
10
11
12
2015-06-30 02:32:17,929 Stage-1 map = 100%, reduce = 0%, Cumulative CPU 1605.94 sec 
MapReduce Total cumulative CPU time: 26 minutes 45 seconds 940 msec Ended Job = 
job_1435627213542_0001 with errors Error during job, obtaining debugging information... 
Examining task ID: task_1435627213542_0001_m_000004 (and more) from job job_1435627213542_0001 
Examining task ID: task_1435627213542_0001_m_000004 (and more) from job job_1435627213542_0001 
Examining task ID: task_1435627213542_0001_m_000004 (and more) from job job_1435627213542_0001 
Task with the most failures(4): ----- Task ID: task_1435627213542_0001_m_000000 URL: 
http://ip-10-150-205-59.ap-northeast-1.compute.internal:9026/taskdetails.jsp?
jobid=job_1435627213542_0001&tipid=task_1435627213542_0001_m_000000 ----- 
Diagnostic Messages for this Task: AttemptID:attempt_1435627213542_0001_m_000000_3 
Timed out after 600 secs FAILED: Execution Error, return code 2 from 
org.apache.hadoop.hive.ql.exec.mr.MapRedTask MapReduce Jobs Launched: Job 0: Map: 10 Cu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Error: java.lang.RuntimeException: Hive Runtime Error while closing operators at 
org.apache.hadoop.hive.ql.exec.mr.ExecMapper.close(ExecMapper.java:260) at 
org.apache.hadoop.mapred.MapRunner.run(MapRunner.java:81) at 
org.apache.hadoop.mapred.MapTask.runOldMapper(MapTask.java:432) at 
org.apache.hadoop.mapred.MapTask.run(MapTask.java:343) at 
org.apache.hadoop.mapred.YarnChild$2.run(YarnChild.java:175) at 
java.security.AccessController.doPrivileged(Native Method) at 
javax.security.auth.Subject.doAs(Subject.java:415) at 
org.apache.hadoop.security.UserGroupInformation.doAs(UserGroupInformation.java:1548) at 
org.apache.hadoop.mapred.YarnChild.main(YarnChild.java:170) 
Caused by: org.apache.hadoop.hive.ql.metadata.HiveException: 
java.io.IOException: All datanodes 10.186.28.181:9200 are bad. 
Aborting... at org.apache.hadoop.hive.ql.exec.FileSinkOperator$FSPaths.closeWriters(FileSinkOperator.java:168) at 
org.apache.hadoop.hive.ql.exec.FileSinkOperator.closeOp(FileSinkOperator.java:882) at org.apache.hadoop.

Hive Runtime ErrorやGCエラー等が出てますね。

原因

通常のexportテンプレートではEMRはなんとm1.mediumのcore taskが一台のみ起動して処理が走ります。 各種ヒープサイズの設定(YARN_RESOURCEMANAGER_HEAPSIZE 等)はinstance typeによって決まっており、この件数とデータサイズではHEAPSIZEが不足し、GCエラー等が発生してOut of Memory状態になって処理が落ちるようです。 そこで、メモリをもっと搭載している大きめのインスタンスでHEAPSIZEを確保してあげる必要があります。

解決策

こんな感じ。

parameter default value new value
core & master instance type m1.medium m3.xlarge
core instance count 1 2
AMI version 3.3.2 3.8.0

m3.xlargeにした事で処理が落ちる事なくスムーズに実行されるようになりました。core countを増やしたのは、exportテンプレートのデフォルト設定だとcountが1なので、mapperが不足し、DynamoDBで設定したthroughput (1000)をフルに使い切る事ができなく、デフォルトのタイムアウト時間(2時間)に達して処理自体がキャンセルされてしまうからです。EMR側のスループットも上げる為に必要な変更でした。また、HadoopのAMI versionも古い3.3.2から最新の3.8.0にしてあります。

これらによって、export処理がだいたい1時間ちょいで完了します。

その時の試行錯誤がこれ。

計算

provisioned throughputに対するバックアップ時間の目安を計算するには以下の通り。

20GBのデータ、1億件のレコード、1000 throughputとして、

平均item size = 20*1024*1024*1024/100000000 =~ 215 byte

4KB以下なので4KB blockのreadとなります。 Hive Queryのread処理はeventually consistentになるので、1 IOPSに対して8KBのデータが読み込めます。 そうすると

DynamoDB scan時間 = (20*1024*1024*1024)/(1000*8*1024*60) = 43.7分

実際にはEMRクラスタのオーバーヘッドが20分弱程度あるので、これに若干加算します。

図にするとこんな風になります。

バックアップのタイミングでDynamoDBのthroughputをガツンと上げれば、件数によっては短時間で済む場合もあるので、参考にでも!

(※ Production Trafficに影響がないように、Read Throughput Percentは適切に設定する必要があります)

EC2 Auto Recoveryの注意点

先日、EC2のAuto Recoveryでちょっとハマったのでメモ。

CloudwatchのStatusCheckFailed_Systemアラームを設定すると、インスタンスを自動的に復旧してくれるEC2 Auto Recoveryという機能があり、使うためには条件がいくつかあります。

  • 特定のリージョン
  • C3, C4, M3, R3, T2 instances
  • VPC
  • 共有tenancy
  • EBSストレージのみのサーバ

Recover Your Instance

しかし、上記を満たしているのに、特定のAMIをCLI経由で起動するとEC2 Auto Recoveryを設定できなくなります。

(AWSコンソールでラジオボタンが押せない。CLIで設定しても効かない)

原因はAMIにephemeral disk等のblock device mappingが設定されていて、T2やC4等のEBS onlyなinstance typeで起動しているにも関わらず、AWS側がephemeral diskが付与されていると認識してしまう所にあります。なお、AWSコンソールからの起動だとこの現象は発生しません。

AMIでblock device mappingが埋め込まれている

aws ec2 describe-images --image-ids ami-e74b60e6 --region ap-northeast-1

本来はEBS onlyなinstance typeだとblock device mappingの設定如何に関わらず、付与自体が不可能なので、全く関係ないはずです。

解決方法は現時点(2015/5/1)では3通り

  • extraなmappingが設定されていないAMIを使う(Amazon Linux等)
  • AWSコンソールから起動する
  • 明示的に--block-device-mappingsパラメータでNoDeviceと指定 aws ec2 run-instances --image-id ami-e74b60e6 --instance-type t2.small --subnet-id subnet-xxxxxxxx --block-device-mappings "[{\"DeviceName\": \"/dev/sdb\", \"NoDevice\": \"\"}, {\"DeviceName\": \"/dev/sdc\", \"NoDevice\": \"\"}]"

これは明らかにAWS側のバグなので早く治って欲しいものですね。

Auto Scalingによる自動復旧(AWS Lambda+SNS編)

先週のAWS Summit San Fransiscoにて、ついにLambdaがSNSに対応しました。 様々なサービスが発表された中、個人的にはこれが一番のヒットです!というのも、この機能によってAWS間のサービスがより連携しやすくなり、新しいリアクティブなアーキテクチャをどんどん実現できそうだからです。

というわけで、少し試してみました。

お題は去年12月に試したAutoScaling + Lambda。(当時はLambdaはまだこの機能がなかったのでSNS→SQSにてイベントをプロセスする仕組みを作りました。)

SNS連携によって前回のこれが

こうなります。(Lambdaのアイコンが出たので差し替えてます)

うーん、シンプル!

設定

前回とほぼ同様。

SNS作成

1
2
3
4
$ aws sns create-topic --name instance-alert --region ap-northeast-1
{
    "TopicArn": "arn:aws:sns:ap-northeast-1:123456789012:instance-alert"
}

LambdaとSNS連携できるようにポリシーを付与

1
2
3
4
5
6
$ aws lambda add-permission --function-name makeASUnhealty --statement-id sns-instance-alert \
--action "lambda:invokeFunction" --principal sns.amazonaws.com \
--source-arn arn:aws:sns:ap-northeast-1:123456789012:instance-alert --region us-west-2                                                                                                                                                                                                  
{
    "Statement": "{\"Condition\":{\"ArnLike\":{\"AWS:SourceArn\":\"arn:aws:sns:ap-northeast-1:123456789012:instance-alert\"}},\"Resource\":\"arn:aws:lambda:us-west-2:123456789012:function:makeASUnhealty\",\"Action\":[\"lambda:invokeFunction\"],\"Principal\":{\"Service\":\"sns.amazonaws.com\"},\"Sid\":\"sns-instance-alert\",\"Effect\":\"Allow\"}"
}

Subscribe

1
2
3
4
5
$ aws sns subscribe --topic-arn arn:aws:sns:ap-northeast-1:123456789012:instance- --protocol lambda \
--notification-endpoint arn:aws:lambda:us-west-2:123456789012:function:makeASUnhealty --region ap-northeast-1
{
    "SubscriptionArn": "arn:aws:sns:ap-northeast-1:123456789012:as-test:4b22eec6-aeb5-4421-7a2f-99ca33a4b8ab"
}

aws cliはのヘルプにはまだSNSをLambdaへsubscribeするやり方は書いてませんが、上記のようにやればできます。 Thanks Eric!

EC2 Status Check

1
2
3
4
5
6
7
$ export INSTANCE=i-xxxxxxxx
$ aws cloudwatch put-metric-alarm --alarm-name StatusCheckFailed-Alarm-for-$INSTANCE \
--alarm-description "Instance $INSTANCE has failed" --metric-name StatusCheckFailed \
--namespace AWS/EC2 --statistic Maximum --dimensions Name=InstanceId,Value=$INSTANCE \
--period 60 --unit Count --evaluation-periods 2 --threshold 1 --comparison-operator \
  GreaterThanOrEqualToThreshold --alarm-actions arn:aws:sns:ap-northeast-1:560336700862:instance-alert \
--region ap-northeast-1

Lambda Function

自動復旧

通信を遮断し、Status Check Failを発動させる

1
2
$ date; sudo ifdown eth0
Fri Apr 17 03:08:39 UTC 2015

EC2 Status Check。2分でfail検知

1
2
3
4
5
6
7
Fri Apr 17 12:10:25 JST 2015
ok
DETAILS reachability    passed

Fri Apr 17 12:10:31 JST 2015
impaired
DETAILS 2015-04-17T03:10:00.000Z        reachability    failed

SNS通知。さらに2分ちょい。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Alarm Details:
- Name:                       StatusCheckFailed-Alarm-for-i-xxxxxxxx
- Description:                Instance i-xxxxxxxx has failed
- State Change:               OK -> ALARM
- Reason for State Change:    Threshold Crossed: 2 datapoints were greater than or equal to the threshold (1.0). The most recent datapoints: [1.0, 1.0].
- Timestamp:                  Friday 17 April, 2015 03:13:09 UTC
- AWS Account:                123456789012

Threshold:
- The alarm is in the ALARM state when the metric is GreaterThanOrEqualToThreshold 1.0 for 60 seconds.

Monitored Metric:
- MetricNamespace:            AWS/EC2
- MetricName:                 StatusCheckFailed
- Dimensions:                 [InstanceId = i-xxxxxxxx]
- Period:                     60 seconds
- Statistic:                  Maximum
- Unit:                       Count

Lambdaログ

1
2
3
4
2015-04-17T03:13:14.504Z  ac9ed52f-e4af-1e14-b826-ee9008a99db9   Received event:..
2015-04-17T03:13:14.504Z  ace9d52f-e4af-1e14-b826-ee9008a99db9    Changing instance health for: i-xxxxxxxx
2015-04-17T03:13:15.682Z  ace9d25f-e4af-1e14-b826-ee9008a99db9    { ResponseMetadata: { RequestId: 'b0194dfb-e4af-1e14-895f-abdf96b0b593' } }
2015-04-17T03:13:15.684Z  ace9d25f-e4af-1e14-b826-ee9008a99db9    result: ""

タイムスタンプによるとSNS発砲されてたからLambda発火まで5秒!

Auto ScalingのHealth Status

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Fri Apr 17 12:13:10 JST 2015
as-sg   ap-northeast-1a HEALTHY i-ce02563d      as-lc   InService

Fri Apr 17 12:13:15 JST 2015
as-sg   ap-northeast-1a UNHEALTHY       i-ce02563d      as-lc   InService

Fri Apr 17 12:13:20 JST 2015
as-sg   ap-northeast-1a UNHEALTHY       i-ce02563d      as-lc   InService

Fri Apr 17 12:13:26 JST 2015
as-sg   ap-northeast-1a UNHEALTHY       i-ce02563d      as-lc   InService

Fri Apr 17 12:13:31 JST 2015
as-sg   ap-northeast-1a UNHEALTHY       i-ce02563d      as-lc   InService

Fri Apr 17 12:13:37 JST 2015
as-sg   ap-northeast-1a UNHEALTHY       i-ce02563d      as-lc   Terminating

Fri Apr 17 12:13:43 JST 2015
as-sg   ap-northeast-1a UNHEALTHY       i-ce02563d      as-lc   Terminating

Fri Apr 17 12:13:49 JST 2015
as-sg   ap-northeast-1a UNHEALTHY       i-ce02563d      as-lc   Terminating

Fri Apr 17 12:13:54 JST 2015
as-sg   ap-northeast-1a UNHEALTHY       i-ce02563d      as-lc   Terminating

Fri Apr 17 12:13:59 JST 2015

Fri Apr 17 12:14:05 JST 2015
as-sg   ap-northeast-1a HEALTHY i-525601a1      as-lc   Pending

Fri Apr 17 12:14:10 JST 2015
as-sg   ap-northeast-1a HEALTHY i-525601a1      as-lc   Pending

Fri Apr 17 12:14:16 JST 2015
as-sg   ap-northeast-1a HEALTHY i-525601a1      as-lc   Pending

Fri Apr 17 12:14:21 JST 2015
as-sg   ap-northeast-1a HEALTHY i-525601a1      as-lc   Pending

Fri Apr 17 12:14:26 JST 2015
as-sg   ap-northeast-1a HEALTHY i-525601a1      as-lc   Pending

Fri Apr 17 12:14:32 JST 2015
as-sg   ap-northeast-1a HEALTHY i-525601a1      as-lc   Pending

Fri Apr 17 12:14:37 JST 2015
as-sg   ap-northeast-1a HEALTHY i-525601a1      as-lc   InService

ちゃんとTerminateされてリプレースされてますね。

AutoScalingの通知

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Service: AWS Auto Scaling
Time: 2015-04-17T03:13:59.367Z
RequestId: efa97137-fa15-4aa4-9c8c-5241961a2d0e
Event: autoscaling:EC2_INSTANCE_TERMINATE
AccountId: 123456789012
AutoScalingGroupName: as-sg
AutoScalingGroupARN: arn:aws:autoscaling:ap-northeast-1:123456789012:autoScalingGroup:c395c157-3a7e-4d56-287b-5ad9b26eb464:autoScalingGroupName/as-sg
ActivityId: efa97137-fa15-4aa4-9c8c-5241961a2d0e
Description: Terminating EC2 instance: i-xxxxxxxx
Cause: At 2015-04-17T03:13:36Z an instance was taken out of service in response to a user health-check.
StartTime: 2015-04-17T03:13:36.342Z
EndTime: 2015-04-17T03:13:59.367Z
StatusCode: InProgress
StatusMessage:
Progress: 50
EC2InstanceId: i-xxxxxxxx
Details: {"Availability Zone":"ap-northeast-1a","Subnet ID":"subnet-bbbbbbbb"}

通常はCausean instance was taken out of service in response to a EC2 health check indicating it has been terminated or stopped.となるのがan instance was taken out of service in response to a user health-check.となっているのでAutoScalingのEC2 Health Checkより前にアクションが起こされた事が分かります。

障害発生からInstanceがリプレースされてInServiceになるAuto Healingのトータル時間は6分ちょいになりました。 EC2 Auto Recoveryを使えば済む場合もありますが、あちらはAWS側の障害に起因するStatusCheckFailed_SystemのみでStatusCheckFailed_Instanceはトリガー対象じゃないのと、特定のインスタンスタイプやVPC等若干制限があります。

終わりに

ちなみに今回はinstanceやSNSは東京リージョン(ap-northeast-1)、Lambdaはオレゴンリージョン(us-west-2)というリージョンを跨いだ連携も可能という事が分かりました。まだ東京に来てないけど、既にproduction readyなのでもう普通に使っていけます。

いやー。SNS連携によって夢は広がりますねぇ。

サバフェス!2015に参加してきた

サバフェス!2015 Springに参加してきました。 やった内容を少々。

前回と同じくチーム@ijinとして1人で参戦して、順位は「4位」。

スコアは44988.867 TpmCでした。

お題

Mysql on ioDriveでtpcc-mysqlベンチマークのtransaction throughput競争。

第1陣と第2陣に分かれていて、今回は後者での参戦。

はじめに

競技期間は5日間あったものの、第1陣で結構な地雷があったのと、Fusion-IO(現SanDisk)のioDriveとtpcc-mysqlは2年前に触ったので最初はあんまりやる気が起きなくて困ってました。後は前回と違って施せる施策がかなり限定されるというので、正直5日間は長過ぎるのではないかという印象でした。(結果的に第1陣のトラブルとかを鑑みると長さ的には良かったのかも知れないけど)

という事で初日はログインとベンチで基準値を取るだけして終了。

その後、最近のMySQLやioDrive周りの情報収集を軽ーくして数日が経過。。

そして最終日の夜中になって、なんとか本腰を入れてチューニング開始。まあ、やりだしたら楽しいんですけどね。

マシンスペック

  • CentOS 6.4
  • Intel(R) Xeon(R) CPU E5-2650 v2 @ 2.60GHz x 32
  • 32GB RAM
  • 40G HDD
  • 320GB ioDrive

制限

innodb_doublewrite = 1
innodb_flush_log_at_trx_commit = 1

その他レギューレーションはこちら

方針

今までのtwitterのタイムラインからして、どうもベンチをウェブ画面から要求しても並列実行の数とキューイング、及び非常に長い実行時間からして1時間に一回実行できるかどうかも怪しかったのでローカルでこまめに回す方向に。

本番データはtpccの1000 warehouse(70GB+)、16GB Buffer Pool、900sの実行時間だったのでそれを250 warehouse, 4GB Buffer Poolと縮小し120sと短めに設定。こうすることによってローカルでのデータコピー時間やベンチ実行時感が短縮され、時間がない中での細かいパラメータのチューニングに専念できる。Buffer Poolはどうせ一番効くと分かりきってるので、あえて後回し。DBは6年前(MySQL 5.0時代)から手に滲んで愛用しているPercona Serverの5.6版を選択。

スコアの推移はこんな感じ。データセットが小さい分、スコアは大きめ。

終盤になってやっと本番に近いデータセットや実行時感で細かいチューニング。

最終的にローカルでのベストスコアは53420.332 TpmCでした。

設定ファイル

効果があったもの

細かいおまじないレベルでのパラメータも他にいくつあったけど、割と効いたのをピックアップ。また、既に設定されていたパラメータは除外(innodb_io_capacity等)

mysql

datadir=/fioa/mysql
tmpdir=/fioa/tmp
sync_binlog=0
innodb_buffer_pool_size = 28150M
innodb_buffer_pool_instances=16
innodb_log_file_size=4G
innodb_log_files_in_group=3
innodb_log_group_home_dir=/var/log/mysql
innodb_log_buffer_size=64M
innodb_data_file_path=ibdata1:76M;../../var/log/mysql/ibdata2:500M:autoextend
innodb_checksum_algorithm=0
innodb_max_dirty_pages_pct=90
innodb_lru_scan_depth=2000
numa_interleave=1
flush_caches
malloc-lib=/usr/lib64/libjemalloc.so.1

etc

vm.swappiness=1
mount option (noatime,nodiratime,  max_batch_time=0,nobarrier,discard)
cache warmup

基本的にはioDriveにIOをなるべく発生させない、もしくは遅延させる関連のパラメータが効いた感じ。この辺は割と正統なチューニング方法。一つ、若干工夫したのは、doublewrite buffer fileの指定方法。シーケンシャルなIOが発生するログ周りの処理はHDDに逃がした方がioDrive/SSD的には負荷低減になるけど、Percona 5.5まではinnodb_doublewrite_fileというパラメータでいつも指定していたのが、5.6ではなんと未実装!なのでベンチマーク前にコピーされるibdata1の予めのサイズを計っておいて、以降の書き込みをHDD側へ逃がすようにinnodb_data_file_pathで調整。

他はNUMAによるメモリの偏り調整とmallocライブラリを使う指定。この辺もPerconaやMariaDB専用オプション。

後は一応初動のスコアをちょっとだけ稼ぐためにmysqlのstartup script内にてテーブルをcount(*)してindexをbuffer poolに乗せたぐらい。

効果が微妙だったもの

mysql

innodb_flush_method=ALL_O_DIRECT
innodb_support_xa=0
innodb_thread_concurrency=N
innodb_flush_neighbors=0
query_cache_size=0
large-pages

etc

echo 'noop' > /sys/block/fioa/queue/scheduler
echo 4096 > /sys/block/fioa/queue/nr_requests
echo 2 > /sys/block/fioa/queue/rq_affinity
renice -n19 -p `ps auxf | grep mackerel | grep -v grep | awk ‘{print $2}’`

large-pagesでページサイズを大きく設定すればメモリ効率は向上するはずが、少なくともベンチマークにおいては効果なし。また、スケジューラをnoopやdeadlineと変えたり、nr_requestsやrq_affinityを調整してみたけど、デフォルトのOS bypass設定と比べてあまり変化なし。

試したかったもの

  • ioDrvieのblock sizeのリサイズ
  • C0 state(CPUのC stateを制御して変動させずにioDriveの処理向上の期待)
  • numa_node_forced_local(IO処理をメモリに一番近接しているCPUで行う)
  • IRQ pinning(ioDriveのIRQを固定)
  • network tuning

この中で最後やることリストに乗っけていながらやらなかったネットワーク周りのチューニング。多分、これが敗因。(1位のチームzzz(@ttkzwさん)はローカルベンチマークで51000ぐらいだったので)もっとリモートからベンチが実行される事に意識を向ければ良かったのかもしれないですね。とはいえ、最後はキュー待ちが8チームだったり、結果が不具合で見れなかったりと、バダバタしてたのでそっちに気を取られたのもまた事実。まあ、半日ちょいのチューニングにしてはそこそこ行った感じでしょうか。

終わりに

最初は運営側が「目下実装中です!」といいながら#トラしゅをしているような感じでちょっと不安でしたが、最後は(第2陣という事もあり)それなりに楽しめました。ベンチマーク時間の短縮と並列実行数がもうちょっと増やせたらよかったですかね。運営側の皆様、大変お疲れさまでした!

おまけ

賞品として3DマッサージピローとSSDを頂きました!

個人敵には飛び込みLTした人がもらったDroneの方が良さそうだったけど、ピローは家族に好評でした。