無印吉澤

Site Reliability Engineering(SRE)、ソフトウェア開発、クラウドコンピューティングなどについて、吉澤が調べたり試したことを書いていくブログです。

Ansible の --extra-vars 引数を安全に使うためのラッパーを書いてみた

f:id:muziyoshiz:20160313233740p:plain

最近は、仕事でも趣味でも、サーバ構築を自動化したいときは Ansible を使ってます。

アプリケーションのデプロイには Capistrano も使うんですが、つい最近 Ansistrano という便利な Ansible role の使い方を覚えてしまったので、そこも Ansible で済むようになりました。

recruit.gmo.jp

ansible-playbook の --extra-vars 引数

ところで、Ansible の playbook を書いていると、「普段の動作は決まっているけど、ごくまれに違う動作をさせたい」ことがたまにありませんか? そういうとき、僕は ansible-playbook の --extra-vars 引数(-e 引数)を良く使います。

例えば、自作のアプリケーションをデプロイするための playbook で、「普段は master ブランチをデプロイするけど、たまに違うブランチをデプロイしたい」ことがあったとします。そういう場合、playbook のなかで version 変数を、

- hosts: apservers
  vars:
    version: master

のように定義しておいて、master ブランチをデプロイしたい場合は --extra-vars 引数なしで実行します。

$ ansible-playbook -i inventory deploy-app.yml

そして master 以外のブランチをデプロイしたい場合だけ、以下のように --extra-vars 引数を使ってブランチ名を指定します。

$ ansible-playbook -i inventory deploy-app.yml --extra-vars="version=develop"

Ansible 公式の Variables にある通り、あらゆる変数指定のなかで --extra-vars の指定は最優先されます。そのため、このような上書きが可能なわけです。

--extra-vars のデメリット

この方法はだいたいうまくいくのですが、「変数名を間違えても何のアラートも出ない」というデメリットがあります。

例えばつい最近、僕が書いた playbook を使ってアプリをデプロイした同僚から、「develop ブランチを指定したのに master ブランチがデプロイされてる。playbook がバグってないか?」と言われたことがありました。何か実装間違えたかな、と思って彼が実行したコマンドを見たら、こうなっていました。

$ ansible-playbook -i inventory deploy-app.yml --extra-vars="develop"

調べてみたところ、これは無効な変数指定として ansible-playbook に無視されるようです。その結果、version 変数はデフォルトの master のままで、master ブランチがデプロイされました。その同僚はどうも「引数でブランチ名を変えられる」程度の理解だったようです。

そのときは同僚に --extra-vars 引数の使い方を解説して終わったのですが、あとから考えてみると、--extra-vars で動作を変更させること自体が危険なんじゃないか? という気がしてきました。今回は引数の使い間違えでしたが、引数の使い方を理解していても version を versoin と書き間違えてしまうくらいは、普通にありそうです。

--extra-vars の代替案としての vars_prompt

--extra-vars の代替案としては、Prompts に説明のある vars_prompt を使うという方法があります。

vars_prompt は、playbook の実行後に、変数入力のプロンプトを表示するためのオプションです。例えば、playbook に、

- hosts: apservers
  vars_prompt:
    - name: "version"
      prompt: "Branch name?"
      default: master

と書いておくと、playbook の実行時に以下のプロンプトが表示されます。そのまま Enter キーを押せば version 変数に "master" が代入され、ブランチ名を入れればそちらに変わります。

Branch name? [master]:

これでまあ確かに安全になるので、多くの場合は vars_prompt を使うのがよいと思います。

ただ、master ブランチをデプロイするのが大半、というケースでは Enter キーを無駄にぺちぺち押さないといけないのが面倒です。また、カスタマイズ可能な変数がもっと多い場合は、その変数の数だけ Enter キーをぺちぺち押さないといけなくなります。何も考えずに Enter キーをぺちぺち押す習慣ができるのは、またなにか別の障害の原因になりそうで嫌な感じです。

--extra-vars を安全に使うためのラッパー ansible-playbook-se

さっきの同僚の例について考え直してみると、要するにあれは "develop" という僕の想定しない変数名を、ansible-playbook コマンドが受け入れてしまったのがいけなかったわけです。

それなら、--extra-vars 引数に指定できる変数名を限定できれば、それで十分安全になるのでは? そう考えて、ansible-playbook コマンドのラッパー "ansible-playbook-se" を試作してみました。最近覚えた Python 3 で実装し、GitHub にソースコードを置いておきました。

github.com

使い方はまず、このリンク先にある ansible-playbook-se と extra-vars-cheker を、どこかパスが通ったところに置きます。

次に、playbook が置いてあるのと同じディレクトリに、以下のような内容で extra-vars.yml という名前のファイルを作ります。このファイルには、playbook のファイル名と、各ファイルが --extra-vars で指定するのを許す変数名を書きます。

---
deploy-app.yml:
  - version

そして、いつも使っている ansible-playbook の代わりに、ansible-playbook-se を使うようにします。そうすると、想定外の変数が指定された場合、ansible-playbook-se は以下のようなエラーを出して、処理を中断します。

$ ansible-playbook-se -i inventory deploy-app.yml --extra-vars="develop"
ERROR: Invalid extra-var format: develop
ERROR: extra-vars-checker Failed.

安全になりましたね? なりましたよね? でも、これはこれで何だか面倒な気がしますね……。

試しに作ってはみたのですが、まだ実際の環境には導入していません。こういうニーズって、他の人にもあるもんでしょうか? もしニーズがあればもう少しちゃんと作ろうと思うので、ご意見お待ちしています。

機械学習初心者が『Python 機械学習プログラミング』(速習コース)を読んだメモ

f:id:muziyoshiz:20160924124311p:plain

きっかけ

機械学習の重要性は、それこそ「ビッグデータ」という言葉が出てきた頃からいろいろな人が訴えていますが、最近は特にツールが充実して、敷居が下がってきたように感じています。

そろそろ自分でも機械学習関係のツールを使えるようになりたいと思っていたのですが、そんなときに「具体的なコード例が多くて読みやすい」という本書の評判を聞いて、読み始めました。

Python機械学習プログラミング 達人データサイエンティストによる理論と実践 (impress top gear)

Python機械学習プログラミング 達人データサイエンティストによる理論と実践 (impress top gear)

まだ、ツールを使いこなせるレベルには全然届いていないのですが、試したことを記録しておかないと忘れてしまいそうなので、一旦メモをまとめておきます。

この記事を書いた人のレベル

私のレベルはこんな感じです。

  • 大学は情報科学科で、線形代数とか勉強したはずなのに、数学のことはほとんど忘れている
  • プログラミング言語の知識はあるが、Python のコードは書いたことがなく「ブロックをインデントで表現するやつ」程度の認識

今回の読書プラン

読み始める前に書評を色々検索して読んでたのですが(気後れしていたとも言う)、そのなかに、翻訳の福島さん自身が、本書の読み方をアドバイスする記事がありました。

thinkit.co.jp

この記事の3ページ目で、「速習コース」と「特訓コース」という2通りの読書プランを紹介されています。「全部読まなくて良い」というので少し気が楽になり、まずは速習コースに従って一通り読みました。

f:id:muziyoshiz:20160924124421p:plain:w600
速習コースのフロー
f:id:muziyoshiz:20160924124447p:plain:w600
特訓コースのフロー

Python 環境の構築

本書にコード例がふんだんに含まれており、すべて 本書の GitHub (rasbt/python-machine-learning-book) で公開されてます。しかも Jupyter Notebook の .ipynb ファイルで公開されているので、Web ブラウザ上からすぐに実行できます。今風ですね。

github.com

そこで、まずは Jupyter Notebook を動かすために必要なソフトをインストールします。Python 本体はもちろん、科学計算向けのライブラリもいくつかインストールする必要があります。

本書にはインストール方法はあまり書かれておらず、『Python機械学習プログラミング』学び方ガイド で紹介されている データサイエンティストを目指す人のpython環境構築 2016 - Qiita にも、Mac での手順はあまり詳しく書かれていませんでした。つまづいたポイントがいくつかあるので、自分の環境でのインストール手順をメモしておきます。

インストール先の環境

今回のインストール先は以下の通りです。次の MacBook Pro が出たら OS を乗り換えよう、と思っていたら、未だに Yosemite を使ってしまってる始末……。

  • OS X Yosemite 10.10.5
  • MacBook Pro (Mid 2014)

Anaconda (Python 3) のインストール

本書にも「科学計算に特に推奨したい Python ディストリビューションの1つに Anaconda がある」と書いてあったので、Anaconda をインストールしました。Mac に元々入ってる Python は使いません。

Mac上にデータ分析で使用するAnacondaをインストールし、IDEを起動するまで - Qiita を参考に、pyenv を入れてから、その上に Anaconda をインストールします。

Homebrew が入っていれば、以下のコマンドで pyenv がインストールされます。

% brew install pyenv

ただ、この状態で、

% pyenv install --list | grep anaconda

を実行したところ Anaconda 4.0.0 までしか選択できませんでした。Anaconda のダウンロードページ を確認したところ、その時点での最新版は Anaconda 4.1.1 だったのに……。

で、ここ最近 brew update を実行してなかったことに気づき、以下のコマンドを実行。

% brew update && brew upgrade pyenv

これで Anaconda 4.1.0 まで選択できるようになりました。最新ではないのが若干気になりますが、まあ Homebrew 対応に時間差があるのだろうと考えて、以下のようにインストール。デフォルトで Anaconda が使われるように設定しました。

% pyenv install anaconda3-4.1.0
% pyenv global anaconda3-4.1.0
% pyenv rehash

そして、Anaconda のなかでのパッケージ管理に使われる conda をアップデート。何かの記事を参考に実行したと思うのですが、これは必要なかったかもしれません(参考元ページを探し直したのですが見つからず)。

% conda update conda

科学計算に関するライブラリのインストール

本書 p.14 には、以下のパッケージが必要と書かれていました。

  • NumPy
  • SciPy
  • scikit-learn
  • matplotlib
  • pandas

これらを以下のコマンドでインストールします。元々 Anaconda に同梱されているものもありましたが、最新版にアップデートされました。

% conda install numpy scipy scikit-learn matplotlib pandas

サンプルの実行に必要なライブラリのインストール

本書を読みながらコード例を実行したところ、12〜13章のコードを実行するのに、以下のパッケージも必要になりました。

  • Theano
  • Keras

これらも conda install theano keras でインストールできると思いきや、インストールできませんでした。anaconda search -t conda theano を実行しろ、というメッセージが表示されますが、osx-64 向けに個人がビルドしたと思われる Threano が43個も見つかる始末。

最終的には Installing Theano — Theano 0.8.2 documentation の手順に従って、以下のコマンドを実行してインストールしました。

% pip install Theano

Theano のページには conda install pydot-ng も実行するように、と書いてあるのですが、私の環境では以下のエラーが出てインストールできませんでした。ただ、これをインストールしなくてもサンプルは動いたので、そのまま使っています。

% conda install pydot-ng
Using Anaconda Cloud api site https://api.anaconda.org
Fetching package metadata .......
Solving package specifications: ....

The following specifications were found to be in conflict:
  - pydot-ng
  - python 3.5*
Use "conda info <package>" to see the dependencies for each package.

Keras のほうは以下のコマンドでインストールできました。

% pip install keras

ただ、このインストール後にサンプルコードを実行すると、

from keras.utils import np_utils

の行で、ImportError: No module named 'tensorflow' というエラーが発生してしまいました。サンプルコードで TensorFlow は使ってないはずなんですが……。TensorFlow のダウンロードページ を参考に、以下のコマンドで TensorFlow をインストールしたところ、このエラーは出なくなりました。

% conda install -c conda-forge tensorflow

あと、サンプルの実行に必須ではないですが、notebook の先頭でソフトウェアのバージョン番号を表示したい場合は watermark のインストールも必要です。

% pip install watermark

サンプルの実行

サンプルは GitHub で公開されているので、git clone でコピーします。

% git clone https://github.com/rasbt/python-machine-learning-book.git

あとは jupyter notebook で Jupyter Notebook を起動し、code ディレクトリ以下にある ipynb ファイルを開けばコードを実行できます。

Jupyter Notebook の使い方については、本書の付録A「Jupyter Notebook の基本的な使用方法」と、Jupyter事始め - Qiita を参考にしました。メニューバーから Cell → Run All で動く、ということさえわかれば普通に使えると思います。私は、コードが書かれたセルを選んだ状態でないと Run Cells を押しても何も起こらない、ということに気づくのに結構時間がかかりました……。

それ以外の方法としては、jupyter console で CLI を開いて、ここにコードをコピーして実行するのも良いと思います。変数の中身を1個ずつ確認しながら実行するのは、GUI よりも CLI のほうが楽な気がしました。

例えば、サンプルコードを実行してると、これは Python 標準のオブジェクトなのか、科学計算ライブラリ固有のオブジェクト(例えば NumPy 固有の、多次元配列を表す ndarray など)なのか、知りたくなることがありました。そういうときは、コンソールで変数名の後に "?" を付けると、以下のようにオブジェクトの説明が表示されて便利です。

% jupyter console
Jupyter Console 4.1.1


In [1]: from sklearn import datasets

In [2]: iris = datasets.load_iris()

In [3]: X = iris.data[:, [2, 3]]

In [4]: X?

Type:            ndarray
String form:
[[ 1.4  0.2]
           [ 1.4  0.2]
           [ 1.3  0.2]
           [ 1.5  0.2]
           [ 1.4  0.2]
           [ 1.7  0.4]
           [ 1.4  0.3]
           [ 1.5 <...>  1.9]
           [ 5.9  2.3]
           [ 5.7  2.5]
           [ 5.2  2.3]
           [ 5.   1.9]
           [ 5.2  2. ]
           [ 5.4  2.3]
           [ 5.1  1.8]]
Length:          150
File:            ~/.pyenv/versions/anaconda3-4.1.0/lib/python3.5/site-packages/numpy/__init__.py
Docstring:       <no docstring>
Class docstring:
ndarray(shape, dtype=float, buffer=None, offset=0,
        strides=None, order=None)
(以下、ndarray の docstring)

サンプルコードを実行していて引っかかったところ

速習コースのサンプルコードを実行していて、ライブラリのインストール不足以外では、以下のポイントでエラーが出ました。DeprecationWarning もいくつか出ましたが、そちらについては今回は触れません。

3章

Notebook の冒頭で以下のエラーが出ました。

DistributionNotFound: The 'sklearn' distribution was not found and is required by the application

これはパッケージ名の記載ミスのようで、

%watermark -a 'Sebastian Raschka' -u -d -v -p numpy,pandas,matplotlib,sklearn

を以下に書き換えたところ、エラーが出なくなりました。ちなみに、他の章の notebook は元々こうなってました。

%watermark -a 'Sebastian Raschka' -u -d -v -p numpy,pandas,matplotlib,scikit-learn

12〜13章

以下の場所で FileNotFoundError が出ます。

X_train, y_train = load_mnist('mnist', kind='train')
print('Rows: %d, columns: %d' % (X_train.shape[0], X_train.shape[1]))

git clone でファイルをコピーしていれば、code/datasets/mnist ディレクトリに gz ファイルがあるので、このディレクトリを丸ごと code/ch12/mnist(または code/ch13/mnist)にコピーし、gunzip で解凍しておけば OK です。

Python の勉強

コードを一通り動かしてみたものの、コードを読んでいるうちに

  • このコードはどこまでが Python の標準機能で書かれているのか?
  • やたらたくさんライブラリを使っているが、どの部分がどのライブラリの機能なのか?
  • というかこのやたら出てくるコロンは何なの?
  • カギ括弧のなかに for だの in だの書いてあるのは何なの?

などなど、機械学習の理論よりもコード自体のほうが気になってくる始末(逃避とも言う)。で、本屋に行って Python の本をいくつか眺めてみて、読みやすそうだった「入門 Python 3」を買ってきました。

入門 Python 3

入門 Python 3

サンプルコードに関する疑問点は、これの1〜7章と、付録C「科学におけるPy」を一通り読み終わったあたりで、ある程度解決できました。例えば以下のようなあたり。

  • 多次元配列にカンマ区切りの a[1, 2] でアクセスできるのは、Python 標準のリストではなくて、NumPy の ndarray を使ってるから。標準のリストを使っていたら a[1][2] のようにアクセスする必要がある。
  • a[:] のようにコロンを使ってるのは Python 標準の「スライス」。list[start:end:step] という文法でリストの一部を取得できる。返り値に end の要素は含まれない。
  • カギ括弧のなかに for だの in だのあるのは Python 標準の「リスト内包表記」。for 文でイテレータを回した結果をリストで返す、ということを短縮形で書いているだけで、for 文の入れ子も書ける。

1〜7章だけなら読むのに1日もかからない分量なので、Python 初心者にはお薦めです。それと、付録Cに ipython3 コマンド(jupyter console コマンド)の便利機能の紹介があったのは、個人的には非常に助かりました。

あとは、本書に載っていた参考 URL(下記)も読まなきゃ、と思いつつ、こちらはまだほとんど読めてません。

速習コースを読んでみた感想

説明が平易に書かれており、図も豊富なので、数式を読み飛ばしても、何ができるのかはなんとなく理解できました。また、Jupyter Notebook でコードを動かしながら確認できるので、数式だけ追うよりは、だいぶ気楽に読めました。

Python は科学計算関係のライブラリが充実しているとは聞いていましたが、短いコードでこんなにたくさんのことができるのか、と驚きました。例えば、3章に、

y_pred = ppn.predict(X_test_std)
print('Misclassified samples: %d' % (y_test != y_pred).sum())

というコードがあって、この (y_test != y_pred).sum() ってなんだ?と思ったら、これは2つの行列を比較した結果を bool の行列として取得し、さらに True の要素数を数えて返してるんですね。短いコードでこんなにたくさんのことができるのか、と驚きました。

ただ、一通り読み終えたものの、理解が中途半端なので、現状はこんな感じです。

  • いろいろな手法が紹介されているが、どういうときにどの手法を使えばよいかが、まだよくわからない
  • 実際の業務に使うとしたら、数式の部分をどこまで理解していれば十分なのか不安

なにか、身近の現実的な問題にツールを適用して、ツールにもう少し慣れたいところです。あと、最近職場でこの本の読書会をやろうという話が出てるので、何度か読み返して理解を深めたいと思います。

とりあえずの目標は、scikit-learn のサイトにある Machine Learning Map の範囲を、ツールとして一応使えるレベルかな……。

f:id:muziyoshiz:20160926132822p:plain:w800

Rails 5 で艦これアーケードのプレイデータ管理ツール "Admiral Stats" を開発中

f:id:muziyoshiz:20160828002458p:plain:w600

開発のきっかけ

このブログでゲームのことは書いたことなかったと思いますが、個人的には、長い期間かけてチマチマやるゲームが好きで、ここ数年は Ingress と艦これをやってます。

今年の4月には、艦これのアーケード版(艦これアーケード)もリリースされて、これも週1くらいのペースでゲーセンに通ってプレイしてたりします。

この艦これアーケードは筐体がネットワーク接続されており、自分のプレイデータをあとから SEGA の公式サイト で閲覧できるようになってます。このサイトで結構細かいデータまで見られるのですが、見られるデータはアクセス時の最新状態のみです。

このプレイデータを過去の分まで記録して時系列データとして可視化したら面白そう

と思いついたのと、

どうせ作るなら6月末にリリースされた Ruby on Rails 5 でも使ってみようか

ということで、この夏休みを使ってプレイデータ管理ツールを作ってみました。今回の記事は、このツール "Admiral Stats" の開発中間報告です。

2016-09-03追記

この記事の公開後、9/3にサービスリリースしました。Twitter アカウントでログインして使えます。ぜひお試しください。

www.admiral-stats.com

艦これアーケードとは?

艦これを全く知らない人向けに説明すると、艦これアーケードとは、艦娘と呼ばれるキャラのカードを集めて、選りすぐりのデッキを作成し、ステージを攻略していくアクションゲームです。ステージをクリアするたびに、ランダムで新たなカードが排出されます。

どのステージでどのカードが出やすいか、などのカード排出に関する法則性は全く公開されていないため、「自分(たち)が試したらこうだった」という情報が Wiki などで頻繁にやりとりされています。この法則性をつかむために(あるいは自分の不運をなぐさめるために)プレイデータを記録している人も多いと思います。

ちなみに、サービス開始直後は何時間も待たないとプレイできないほど人気でしたが、最近は少し待てばプレイできる程度に空いてきています。秋にゲーム内イベントがあるらしいので、それまでは空いてるんじゃないでしょうか。

Admiral Stats とは?

今回開発した Admiral Stats は、この艦これアーケードのプレイデータを可視化するサイトです。SEGA 公式のプレイヤーズサイト が対応していない、時系列での可視化に対応しています。

Ingress を知っている人なら Agent Stats の艦これアーケード版」 という説明が一番分かりやすいと思います。実際、Agent Stats からの連想で Admiral Stats を作ることを思いつきましたし、名前も Agent Stats からの連想で付けました*1

f:id:muziyoshiz:20160828004335p:plain
Agent Stats の画面例

Admiral Stats の画面サンプル

サンプル 1:カードの入手履歴

Admiral Stats にまずログインすると、最近のプレイで入手したカードの一覧が表示されます。ずっとプレイしていてカードが増えてくると、「あれ、このカードって前にゲットしたっけ? 今日が初めてだっけ?」とわからなくなってくるのですが(自分はそうでした)、そういう場合を想定した機能です。

f:id:muziyoshiz:20160828003035p:plain

サンプル 2:カードの入手数・入手率のグラフ

カードの種類(ノーマル、レアなど)ごとの入手数、入手率のグラフです。Admiral Stats の内部に各カードのリリース時期のデータを登録してあるため、入手率は減少することもあります。

f:id:muziyoshiz:20160828003052p:plain

サンプル 3:レベル・経験値のグラフ

艦娘のレベル・経験値だけでなく、艦種(駆逐艦とか)や艦隊全体の累計レベル・経験値も表示できます。

f:id:muziyoshiz:20160828003101p:plain

サンプル 4:カード入手状況の一覧表示

公式サイトでも見られる情報なのですが、Admiral Stats では情報量を絞る代わりに、1ページにまとめて表示します。

f:id:muziyoshiz:20160828003114p:plain

データのアップロード方法

艦これアーケードの公式サイトは、残念ながら、プレイデータのダウンロード機能を提供していません。ただ、このサイトはとても綺麗に作られていて、プレイデータはすべて API 用の URL から JSON で取得し、Web ブラウザ側で画面を描画しています。

そのため、今回はこの JSON をそのままファイルに出力する admiral_stats_exporter というエクスポートツールを作りました。このツールが出力した JSON ファイルを Admiral Stats にアップロードすると、上記のサンプルのような画面が表示されます。

f:id:muziyoshiz:20160828002458p:plain:w600

Admiral Stats へのログイン方法

メールアドレスの管理をしたくなかったので、Twitter アカウントでログインする方法を採用しました。 Admiral Stats から SEGA のサイトに直接アクセスすることはないので、SEGA ID などの登録は必要ありません。

Admiral Stats の公開予定

実装は一通り終わりました。ローカルの仮想マシンで動かせば、自分1人で使う分には実用的に使えています。

ただ、どうせなら元ネタの Agent Stats の 「全ユーザとの比較」ページ のように統計情報を表示できると、もっと面白くなるんじゃないかと思ってます。Agent Stats ではレベルや経験値の分布、プレイ傾向がわかる指標(攻撃重視か構築重視か、など)の分布が公開されています。艦これの場合、レアカードの所有率の分布とかでしょうか。

そこで他のユーザのデータもアップロードしてもらえるように、Admiral Stats を設置したサイトを公開するための準備中です。ただ、以下のような作業がまだ残っていて、公開できるのは1〜2週間先になる見込みです。

  • サーバのレンタル
  • SSL 証明書導入(Let's Encrypt)と HTTPS 対応
  • デプロイ自動化スクリプトの作成(場当たり的に開発環境を作ったので、必要な手順や設定を整理できてなくて……)
  • production 設定での動作確認
  • 最低限のテスト

もし、Admiral Stats を使ってみたい方は、admiral_stats_exporter で事前にプレイデータをエクスポートしておいてください。ただ、こちらはあくまで非公式のツールなので、リンク先の説明を理解したうえで、利用は自己責任でお願いします。

あと、このエクスポータは突貫で実装したツールなので、使いづらいのはご容赦ください……。本当は Agent Stats のように、スマホだけでエクスポートからインポートまで完結できると良いと思うんですけどね。そこまで手が回りませんでした。

Admiral Stats についての紹介はここまでで、これ以降は Rails 5 での実装に関する細かい話です。

実装の詳細

最近は PHP や Java で Web アプリを作っていたので、rails でまともにアプリを作るのは、Ruby on Rails 2 以来だったりします。そのため、Rails を使い慣れている人には当たり前の話が多いかもしれません。

開発環境

コーディングはホストOS(Mac OS X Yosemite)、実行はゲストOS(Vagrant + VirtualBox + CentOS 7.2)で行いました。IDE は、最近 IntelliJ に慣れてきたので RubyMine にしました。

  • IDE: RubyMine 2016.2.1
  • Ruby: ruby 2.3.1p112
  • Ruby on Rails: Rails 5.0.0.1

プラグイン

画面は Bootstrap のデフォルトのデザインをほぼそのまま採用し、グラフは Highcharts、表は Datatables で作りました。いずれも gem でインストールできました。便利ですね。

自分で明示的に導入したプラグインと、導入方法、参考にしたページなどは以下の通りです。

bootstrap-sass (3.3.7)

  • twbs/bootstrap-sass: Official Sass port of Bootstrap 2 and 3.
  • rails new を実行した時点で、Gemfiles に gem 'sass-rails', '~> 5.0' が入っていた。そのため、追加したのは gem 'bootstrap-sass', '~> 3.3.6' のみ。
  • application.css のファイル名を application.scss に変更し、以下の行を追加。
@import "bootstrap-sprockets";
@import "bootstrap";
  • application.scss にした時点で、元の CSS ファイルにあった *= require_tree . の文法は使えなくなる。そのため、rails generate controller <controller_name> で自動生成される <controller_name>.scss は、自動的には読み込まれない。もし読み込みたければ、各ファイルを明示的に @import で指定するか、css - Proper SCSS Asset Structure in Rails - Stack Overflow の回答(日本語訳)にあるような手段を使う必要がある。
  • Sass 自体については、後述する書籍と、Sass + Railsの基礎 - Qiita を主に参考にした。
  • Bootstrap の使い方については、公式サイトの Getting Started の Examples と、Components および CSS を参考にした。

jquery-datatables-rails (3.4.0)

  • jquery-datatables-rails の "Twitter Bootstrap 3 Installation" の手順に従ってインストール。ただし、Sass 版の Bootstrap をインストールしたので、application.scss には以下のように記載する。
@import "dataTables/bootstrap/3/jquery.dataTables.bootstrap";

highcharts-rails (4.2.5)

//= require highcharts
//= require highcharts/highcharts-more

// チャート画像のダウンロード機能
//= require highcharts/modules/exporting
//= require highcharts/modules/offline-exporting

omniauth (1.3.1), omniauth-twitter (1.2.1)

google-analytics-rails (1.1.0)

Ruby on Rails 5 を使ってみた感想

Rails 2 時代の知識のアップデートするために、まずは本屋で Rails 4 の本をいくつか流し読みしてから、そのうちの1冊を買ってきて読みました。これは内容が網羅的で、かつ読みやすい良書でした。

Ruby on Rails 4 アプリケーションプログラミング

Ruby on Rails 4 アプリケーションプログラミング

また、Rails 5 に関するページをいくつか流し読みしました。主に参考にしたページはこのあたりです。

今回の開発の範囲では、基本的な機能しか使わなかったせいか、Rails 5 だからという理由でつまづくことは特にありませんでした。本当に何もなくて、拍子抜けしたくらいです。

Rails 5 からデフォルトの開発用 Web サーバが Webrick から Puma に変わったとのことですが、特に意識せずに使えました。また、プラグインも、Rails 5 だから動かない、というものはありませんでした。

強いて言えば、いままでは rake db:migrate のように rake で実行していたコマンドが、rails db:migrate で実行できるようになったので、新しいやり方に慣れるためになるべく rails の方を使っていました。まあ、rake の方も使えるので、無理に rails を使う必要はなさそうですけど。

今後、Admiral Stats に機能を追加する機会があれば、API mode など、Rails 5 の新機能をうまく入れ込んでみたいと思います。

*1:Ingress ではプレイヤーのことを Agent と呼び、艦これでは提督(Admiral)と呼ぶため。

Treasure Data Tech Talk 201607 レポート(古橋さんと成瀬さんの講演メモのみ)

f:id:muziyoshiz:20160724153127p:plain:w320

先週末に、Treasure Data Tech Talk に参加してきました。このイベントは毎回濃い話を聞けるので、行けるときはなるべく参加するようにしています。

今回は、古橋さんによる Digdag での YAML 利用の話と、成瀬さんによる PerfectQueue の話が特に面白かったです。以下、講演内容のメモと、公開済みのスライドです。

講演内容

DigdagはなぜYAMLなのか? (Sadayuki Furuhashi, @frsyuki)

  • Digdag とは何か?

    • Workflow automation system
    • Digdag で一番やりたいのはバッチデータ解析の自動化
  • Digdag の競合

    • OSS, Proprietary それぞれに競合がある
    • Workflow automation system は、ワークフローの定義方法によって3つに分類できる
      • プログラミング言語型:Luigi など
      • GUI型:Rundeck など
      • 定義ファイル+スクリプト型:Azkaban など
    • ワークフローの作りやすさと、カスタマイズの柔軟性のトレードオフ
  • Digdag

    • Digdag は定義ファイル+スクリプト型
    • 定義ファイル+スクリプト+俺たちのYAML
    • YAMLは便利だが、include できない、変数の埋め込みができない、(言語内DSLのように)プログラムが書けない、という欠点がある
    • Digdag では、YAML の仕様に従ったうえで、これらの欠点を克服した
  • include できる

    • YAML の仕様では、値(scalar)の前に "!" から始まる文字(タグと呼ばれる)を付与できる
    • 通常、YAMLパーサは、正規表現によるマッチでタグを決定して、自動的にタグを付与している
    • Digdag では "!include : filename" という表記を、ファイルインクルードの文法として使っている
    • !include の後ろに、" " を書く必要がある。このスペースが大事。このスペースのおかげで、通常の YAML パーサでも、キーが " "、値が filename のハッシュとして読み込める
    • ただし、この !include を複数書くと、キーが " " のハッシュが重複してしまう。Digdag の YAML パーサでは、複数の !include を書けるように、内部的にキーを UUID に書き換えている
  • 変数の埋め込みができる、プログラムが書ける

    • Java 8 は、Nashorn(ナスホーン)という JavaScript Engine を同梱している。これを使って ${} 内を評価している
    • だから Digdag は Java8 必須
  • Q) 何故YAMLをベースにした?

    • A) 比較的書きやすい、読みやすい。YAMLとして既存のプログラムから扱える。
  • Q) YAMLはグラフ構造を表現するのに適さないのでは?

    • A) YAMLはDAG、グラフを扱うわけではない。ツリーを扱っている。
  • Q) YAMLにコードを書けるとのことだが、悪さはできないのか?

    • A) 悪さできないように対策している。JavaScript はサンドボックス内でしか動作しない。そのためにJavaScriptを採用した。

PerfectQueueはいかにパーフェクトか、あるいはRubyとMySQLでジョブキューを作る試みについて (Yui Naruse, @nalsh)

  • Who is naruse

    • nkfメンテナ
    • Rubyコミッタ
    • Treasure DataではバックエンドのRubyを担当
  • そもそもジョブキューとは

    • FIFO
    • フロントエンドとバックエンドを疎結合化
  • PerfectQueue の特徴

    • MySQL で実装
    • At-least-once を優先(at-least-once と at-most-once はトレードオフの関係)
  • キューのデータ構造

CREATE TABLE `queue` (
    /* unique key (-> at most once) */
    id VARCHAR(255) NOT NULL,
    /* for FIFO's timeline */
    timeout INT NOT NULL,
    /* opaque data */
    data LONGBLOB NOT NULL,
    /* alive or finished */
    created_at INT,
    PRIMARY KEY (id)
)
  • タスクのライフサイクル

    • タスクの投入時に、timeout, created_at を現在時刻にする
    • タスクの取得時は、timeout が小さいものから優先的に取得し、timeout を 300 秒後に更新
    • タスクの実行中は、ハートビートとして、timeout を定期的に 300 秒後に更新
    • タスクの完了後は、created_at を NULL にして、一定時間保存するために timeout を 720 秒後に更新(タスクの重複を検出するため)
    • 前述の retention time を過ぎたら物理削除
  • タスクの取得、削除時の排他制御が大変

    • 排他処理のために、最初期は FOR UPDATE を使っていた
      • しかし、頻繁にデッドロックする
      • デッドロックを避けるには MySQL の気持ちになってクエリを書く必要がある
    • LOCK TABLES を使うと、テーブル全体をロックしてしまうので、SELECT にも影響
    • GET_LOCK が一番安全、でもクエリの書き方によってはデッドロックが発生した
      • ロックの delete クエリと acquire のクエリがバッティングしないように、タイミングの調整が必要(間隔を乱数で変える)
      • 調整しないと性能問題が起きた
      • さらに、ネットワーク遅延が発生すると影響大
        • GET_LOCK から RELEASE LOCK まで 3 RTT かかるので、影響大
    • queue テーブルに owner カラムを追加したうえで、FOR UPDATE を使うクエリに変更し、テーブルロックを不要にした
      • MySQL の気持ちになって書いたので安全
  • 結論

    • PerfectQueue はより完璧になった
  • Q) 何故 MySQL をキューに選んだのか?

    • A) 当時、Amazon RDS で PostgreSQL が使えなかった。RDBMS を選んだ理由は、Amazonで提供されている、フェイルオーバーがある、など。新しいジョブキューを開発したのは、ジョブキューにフェアスケジューリングの機能(Treasure Dataのサービスで必要)を付けたかったから。(古橋)

感想

Digdag での YAML の拡張(いや、標準の仕様に従っているので拡張と言うのは不適切か?)については、話としては面白かったんですが、そもそも YAML でプログラミングするのは辛そう、というのが第一の感想でした。Ansible もそんな感じのつらみがありますしね。AnsibleSpec みたいな、Digdag で書いたワークフローをテストするツールとかが、いずれ出てきたりするんでしょうか。

また、マークアップ言語として YAML にこだわる必要があるんだろうか、とも思ったのですが、じゃあ代替手段として何があるのか、と考えてみると、なかなか難しそうです。XML よりもマシな選択肢となると、いまは YAML なのかな……。HashiCorp の HCL のように、独自方式を作る方向もあったと思いますが、Digdag に独自方式を作るほどの要件はなかったんですかね。