Macでdnsmasqを使ってお手軽名前解決

最近興味があってBoxenを調べています。BoxenはMacで開発するための
環境をよしなに整えてくれるツールなんですが、その中で気になったのが下記のヤツ

dnsmasq w/ .dev resolver for localhost

via https://github.com/boxen/our-boxen

はて?dnsmasqで.dev resolver for localhostてどんな事してくれるんじゃろかい?

何してくれんの?

端的に言うと

  • dnsmasqでローカル向けにDNSサーバを立てる
  • .dev のドメインはローカル(127.0.0.1)で名前解決できるようにする

あぁ上に書いてある説明どおりですね。ていう事をやってくれます。

もう少し噛み砕くと、ローカルでの開発用に hoge.dev, fuga.devみたいなドメインが欲しくなった時って、/etc/hostsに一個一個定義してたりしましたが、もうこんな事しなくてもいいんですぜ旦那。「*.dev」のドメインであれば、勝手に127.0.0.1に向く用にできまっせ。という話

# /etc/hosts

127.0.0.1   hoge.dev
127.0.0.1   fuga.dev

ではboxenが自動でやってくれるような事をわざわざ手動でやってみます。アナログ最高。ヒャッハー!!!!

ちなみにboxenのpuppetのヤツはここです。

dnsmasq のインスコ

$ brew install dnsmasq

設定ファイルの用意

# 設定ファイルのコピペ
$ cp $(brew list dnsmasq | grep /dnsmasq.conf.example$) /usr/local/etc/dnsmasq.conf

# 自動起動の設定ファイルコピペ
$ sudo cp $(brew list dnsmasq | grep /homebrew.mxcl.dnsmasq.plist$) /Library/LaunchDaemons/
$ sudo launchctl load /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist

dnsmasq.confの設定変更

# /usr/local/etc/dnsmasq.conf

# wildcardでの名前解決を許す?
bind-interfaces 
# launch deamonで動かすために常にforgroundで起動
keep-in-foreground  
#  /etc/resolve.confを見ない
no-resolv 

#  *.dev ドメインへのアクセスは全てlocalに。
address=/dev/127.0.0.1 
#  名前解決する時にローカルを見るためには自動的にloopbackしてくれんので、明示的に設定するとかなんとか?
listen-address=127.0.0.1 

/etc/resolver/devの設置

  • Macは特定のドメインDNSだけを切り替える事ができる
  • /etc/resolver配下にそのドメインと同じ名前でファイルを設置するればおk
  • 書式は /etc/resolve.confと一緒
# /et/resolver/devを作成
$ sudo mkdir -p /etc/resolver
$ sudo touch /etc/resolver/dev

# 中身はこの一行のみ
nameserver 127.0.0.1

DNSのキャッシュクリア

一応キャッシュのクリア

$ dscacheutil -flushcache

確認

ここまで作業は終わりあとは確認

# *.dev のドメインは全て127.0.0.1が返ってくればOK
$ ping -c 1 hoge.dev
$ ping -c 1 hoge.fuga.dev
$ ping -c 1 hoge.fuga.piyo.dev

# 他の名前解決に支障がないか一応確認
$ ping -c 1 www.google.com

余談

  • /etc/hostsを弄る必要が無くなったのは良い。
  • dev以外に対応するドメインを増やしたかったら? 例えば *.hogeみたいな。
    • /usr/loca/etc/dnsmasq.conf に "address=/hoge/127.0.0.1" の行を追加
    • /etc/resolver以下にhogeファイルを置けば良い。
  • もうちょっと修行しないとオジサンダメです。

Andorid標準ブラウザでiframeのスクロールがおかしくて久しぶりにキレちまった件

Androidのブラウザでiframe利用してコンテンツを表示した時に困った時の話。別にキレてないです。

前提

  • Android OSは 4.0.3
  • iframeで読み込むコンテンツはサブドメイン
  • iframeのコンテンツは親frameに収まらずにスクロールする。
  • iframeのコンテンツはフォーム的なもんが表示

現象

  • スマホ(Android 4.0.3)でiframeが存在するページを表示してフォームが表示されていた。
  • 画像の通り、radioボタンが並んだ縦長のフォームを表示している。


  • 初期表示では、radioボタンをポチポチチェックできる。


  • 送信ボタンを押そうと下に引っ張ると、iframeの中がスクロールする。


  • ここで問題が発生する。送信ボタンが押せないどころか、その状態でradioボタンを押すとチェックが入らない!!!

原因

  • 詳細な原因までは正直わからないが、要はこういう事らしい。

1. 初期表示の状態ではradioボタンを押せる
2. 一度スクロールすると、押せなくなる。

これは押せなくなってるんじゃなくて、初期表示の位置をタップしていると同義らしい。
実際に少しだけスクロールさせてradioボタンをクリックすると、上の方のradioボタンにチェックが入る。


対応

さてこれに対応するにはどうしたんもんかという話だが、要は「スクロールなんてしなくてもいいくらいにiframeの高さを調節すれば良い」という事だった。

その対応は、mixiさんが提示してるようにjsでiframe内の高さを取得して、iframeの高さを自動的に設定すれば良い。

ところがどっこいiframeはSameOriginPolicyによってサブドメインだろうが、port付きだろうが、httpsだろうが別ドメイン扱いなので、iframe内の情報を取得するのは無理ゲーだった。

はーマジなんなの?

f:id:tell-k:20140426132610p:plain

というわけで、単純にiframeのheight属性をスクロールが発生しないであろう高めに設定するだけにした。もうiframeなんてなかったんやと思いたくなる珠玉の一品。

余談

どういう了見でこんな事になってるかまでは、ソース的なものが見つからなかったので、もし知ってたら誰か教えてください。

他に方法を無いかなと探した結果、yet antother的にcross domainのiframeの情報を取得する方法があったけど、どれもBKな感じがいなめないので、まだ試していない。

ちなみに Androidの2.3.x系、Android4.2.x系では、この現象は発生しなかった。

夏までに痩せたい!厳選!超オススメダイエット商品10選!!!

さて本日は、数々のダイエット商品を使い続けて来た私が「絶対に」購入したい、超厳選ダイエット商品をランキング形式でご紹介したいと思います!今回はコンビニで買えるものばかり集めてみましたので、ネットや専門店でしか入手できないものは後日またご紹介したいと思います(^^)

それでは、ご覧ください!

↓↓↓




















第10位

http://www.amazon.co.jp/exec/obidos/ASIN/B00EOI6XVY/study02-22/


まずは定番、トクホ系のお茶「特茶」ですね。知る人ぞ知るお茶です。

お茶の記事では無いので詳しい説明は避けますが、特茶は「ケルセチン配糖体」という脂肪分解酵素を活性化させ、体脂肪を減らす効果がある特徴をもっています。日本でも根強いファンは多いです。ダイエットを検討してる方は購入すべきと言える商品でしょう。



















第9位

http://www.amazon.co.jp/exec/obidos/ASIN/B00EOI6XVY/study02-22/
お茶と言えばサントリーサントリーといえば飲み物、飲み物といえば食事です。 そう、せっかくお茶を買ったからには、食事を楽しまなければいけません。

食事楽しむためには、焼肉甲子園などでもおなじみの「烏龍茶」でもよいのですが、ここは食事を楽しみながら、さらに健康的な感じをアピールしつつクールさを演出したい所です。そんなあなたにオススメなのが、この「特茶」!

なんと飲んでるだけで脂肪がみるみるウチに分解されるお茶なのです!特茶は他のトクホ系の飲料(ヘルシア緑茶等)に比べて値段はほぼ同じ感じでペットボトルのサイズが大きいのでかなりお得感ありますね(かなり)。














第8位

http://www.amazon.co.jp/exec/obidos/ASIN/B00EOI6XVY/study02-22/

さて、お次にオススメしたいのが、特茶というお茶です。このお茶はいわゆる「特定保健用食品」で、特定をの保険の目的が期待することができます。
健康を維持するためのなんかすごい効用があるので、健康的で効率的にダイエットしていくことができます。そして、効率的で健康的ににダイエットしていくことができます。















第7位

http://www.amazon.co.jp/exec/obidos/ASIN/B00EOI6XVY/study02-22/

さて、世界中どこでも売られてる現代の必須飲料、それは「お茶」ですね。お茶はヨーロッパでは「紅茶」が古くから飲まれていると思いますがヨーロッパで最初に伝わったお茶は、17世紀にオランダ商人が平戸(長崎県)から輸入した「日本茶」だと言われてます。意外でしょう(笑)

日本では古来より茶筅で混ぜて飲む、いわゆる「抹茶スタイル」がありますが、普段から、茶筅を持ち歩いてる人もいませんし、そんなに時間に余裕がある人もいないので気軽にコンビニで美味しいお茶を買いたいし、飲みたいですよね。そこで特茶というお茶です(^^)

普通にコンビニで、お金を出せば、簡単に本格的なお茶が楽しめますよね!あと、効率的で健康的ににダイエットしていくことができます!













第6位

http://www.amazon.co.jp/exec/obidos/ASIN/B00EOI6XVY/study02-22/
えーと、トクホのお茶です。










第5位

http://www.amazon.co.jp/exec/obidos/ASIN/B00EOI6XVY/study02-22/
お茶です。サントリーの。














第4位

http://www.amazon.co.jp/exec/obidos/ASIN/B00EOI6XVY/study02-22/
お茶












第3位

http://www.amazon.co.jp/exec/obidos/ASIN/B00EOI6XVY/study02-22/
第3位は「特茶」です!世界には数多くお茶の種類がありますが、全て同じ「茶の木」という植物の葉から作られることは有名ですが、これは「特茶」です!サントリーのトクホのお茶です。

さて、この第3位の「特茶」というお茶なのですが、第4位の特茶と、どちらを3位にしようか、最後まですっご〜〜〜〜〜く悩みました!(笑)(^^;) それくらい、「特茶」と「特茶」、どちらもすごくいいお茶なんです!













第2位

http://www.amazon.co.jp/exec/obidos/ASIN/B00EOI6XVY/study02-22/
さて、惜しくも1位を逃すこととなった第2位は、「特茶」です!「特茶」、中には初めて耳にした方も居るかもしれませんね。一般的にトクホのお茶を購入した場合その用途の9割がダイエットで残り4割がダイエットだと言われています。

そう、つまり、ダイエットをするにあたって、どのようなトクホのお茶を選ぶべきかは非常に重要なのです。トクホのお茶は多く存在しますが、あなたにあった最適なお茶を選ぶという事はあなたの健康ライフをそのものを最適化することに他なりません。

そこで私がおすすめしたいのは、やはり「特茶」です。最終的なお茶として、例えばヘルシア緑茶などを選んだとしても、「効率的にダイエットする事」を突き詰め、ダイエット方法としての新しいパラダイムを実現したとも言える「特茶」を経験しておくことは非常に大切でしょう。













第1位

そして栄えある第一位は....

















http://www.amazon.co.jp/exec/obidos/ASIN/B00EOI6XVY/study02-22/
特茶です!!!!!!!!!!!!!!

で本当は?

  • 2014年1月中旬の時点で65kgでしたが現時点(2014/04/13)で55kgです。
  • 周りの人間に「太ってる」事を連続して強調されたため、ついカッとなって飲み始めた。反省はしてない。
  • 日々増減はしてるけど、少しずつ痩せっていってるイメージです。
  • 特茶以外にも、ストレッチやら、食事を気をつけるなどは以前からしてました。
  • 本当に効いてるかどうかは定かではないが、特茶を飲み始めた時期から痩せ始めたので何となくそのまま飲んでます。
  • ヘルシア緑茶よりはは苦くないので割と味的にも気にいってます。じゃなきゃ毎日飲めないし。
  • 特茶愛が極まって書きました。
  • 目下の心配は、飲むのやめたらリバウンドするのかなてのと、どこで下げ止まるのかなて所。

で効くの?

  • ググればすぐ分かると思いますが、万人に効果があるというものでも無いようです。
  • 割と眉唾なんじゃないか的なネガティブな記事はいくつか見つかると思います。
  • 真偽に関してはご自身で判断してください。私は一切責任を負う事はできません。ご自身の健康状態を考えた上で、ご購入、ご利用を検討してください。
  • 私と似た体質、生活リズムのヒトには効果があるかもしれないので。以下何となく思いつく特徴を列挙します。
    • そもそも小食である。(三食きっちり食べない、1回の量が少なめ)
    • 通常は殆ど運動しない。通勤やら、腰痛、肩こり軽減のためのストレッチ程度。
    • 社会人になって、大して生活スタイルが変わってないのに徐々に太り、徐々に痩せにくくなった。
    • 「食っても食っても太らないんだけど」そんな風に考えてる時期が私にもありました。
    • 水分を多く取る。1日に3〜5本の500mlのペットボトル位は取ってます。現在はその内2〜3本が「特茶」になってる感じです。
    • 最近は甘いもの控えてます。元々好きだけど、我慢してるしオジサン偉い。

結論

「齢三十も半ばを過ぎ、大した努力もせずに、10kg痩せられるの最高便利!!!」

P.S. サントリー社員のみなさん

朗報です。偶然なんですが、わたくし、本日誕生日でして、しかも特茶のストック切らしております!そしてここにはAmazonの欲しいものリストがあります!!!いやーもう本当に全然そういうんじゃないので、一緒に奥義「ステマ」を完成させましょう!!!

Amazon.co.jp: tell-k: 誕生日なので特茶が欲しいと思うヒトのリスト

いやー本当偶然てすごいなー、奇遇だなー、いやー本当に特茶サイコー!サントリー社員じゃなくても、送ってくれていいんですよ!特茶を送るのは全人類の自由意志です!あと明日以降でも大丈夫です、エブリディまってます!!

"^L" is 何?

割とどうでもいい話。python2.7 の 標準ライブラリのemail/message.pyの中で"^L"ていう制御コードを見つけた時の話。

f:id:tell-k:20140309224747p:plain

そもそも "^L"てなんだっけ。。。から始まり、調べてみたら改ページだった。

vimエディタの印刷で改ページ : サイト更新停滞ちうっ [ 名無しのVIM使い ]

それは良いとしてなんでこんな所に改ページの制御コード入ってるんだろうと思ってさらに調べたら、下記のような同じような疑問をもったヒトが書いたスレを見つけた。

Issue 7513: many source files contaminated with form feed (\f) characters - Python tracker

こんな事が書いてあった。

  • ivank 「Python 2.7の r76831 をチェックアウトしたら、大量のファイルに改ページが入ってるんやけど(震え声) ... 」
  • lemburg 「俺の覚えてる限り、Barryがそれ好きなんだよね。理由はよー知らんけど ;-) 」
  • barry 「それEmacsの次ページ、前ページへのナビゲーションコマンドなんだよね。 Pythonは空白扱いするけど、何か問題ある? :)」
  • amaury.forgeotdarc 「それPEP8でも許容されてっから。。。」

えw マジで? そんな事書いてありましたっけ?とPEP8を見に行く。

Python accepts the control-L (i.e. ^L) form feed character as whitespace; Many tools treat these characters as page separators, so you may use them to separate pages of related sections of your file. Note, some editors and web-based code viewers may not recognize control-L as a form feed and will show another glyph in its place.

via http://legacy.python.org/dev/peps/pep-0008/

Python は Control-L (^L: From Feed) 文字を空白文字として受け入れます。 多くのツールはこの文字をページ区切りに使うので、この文字をファイル内の セクションを分けるページに使うことができるかもしれません。 しかし、いくつかのエディターや Web ベースのコードビューアは Control-L を認識せず、その場所に別の文字を表示するかもしれません。

via https://dl.dropboxusercontent.com/u/555254/pep-0008.ja.html

ナルホディウス。

なんどかPEP8に目を通してるつもりだったけど、まったくもって頭に入ってないという事が良くわかったオジサンなのでした。

おわり。

Pythonのloggingで手出しできない所のhandlerを差し替える

元ネタ。コレをを見た時、確かにどうやったらいいんだろう? と思ってちょっと調べてみた。

やりたい事

  • ライブラリやらなんやらで書き出してるロギング部分を、自前で用意したロガーというかハンドラーに差し替えたい。

手出し出来ない例

例えば下記のような、hoge.py は手出しできないスクリプトだったとして、以下のようにログ取りになっていたとします。

# hoge.py 
# -*- coding: utf-8 -*-

from logging import warn

spam = u"スパム"
ham = u"ハムの人"

warn("%s %s", spam, ham)

handlerを差し替える

loggingモジュールの中を漁った結果、どうやら logging.root.handlersを弄れば、上記のような場合でも好きな LoggingHandlerに差し替えられるらしい。

1. root.handlers を強引に書き換える

# -*- coding: utf-8 -*-
import logging
import ltsvlogger

formatter = ltsvlogger.LTSVFormatter(fields={
    'asctime': 'time',
    'message': 'message',
})
ltsv_handler = logging.StreamHandler()
ltsv_handler.setFormatter(formatter)
logging.root.handlers = [ltsv_handler] # root.handlersを上書きする

# hogeが呼ばれる前にloggingの設定をする
import hoge # => message:スパム ハムの人 time:2014-02-10T23:41:54+09:00

行儀が悪い感じがとても怒られそうです。addHandlerというメソッドがあるのでそちらにしましょう。

2. addHandlerを使う

# -*- coding: utf-8 -*-
import logging
import ltsvlogger

formatter = ltsvlogger.LTSVFormatter(fields={
    'asctime': 'time',
    'message': 'message',
})
ltsv_handler = logging.StreamHandler()
ltsv_handler.setFormatter(formatter)
logging.root.addHandler(ltsv_handler) # addHandlerに変えた。

# hogeが呼ばれる前にloggingの設定をする
import hoge  # => message:スパム ハムの人 time:2014-02-10T23:41:54+09:00

というかそもそも、logging.getLogger()ていう風にやるとrootロガーだったのでそいつに対してaddHandlerすれば良かったという事実

3. rootロガーにaddHandlerを使う

# -*- coding: utf-8 -*-
import logging
import ltsvlogger

formatter = ltsvlogger.LTSVFormatter(fields={
    'asctime': 'time',
    'message': 'message',
})
ltsv_handler = logging.StreamHandler()
ltsv_handler.setFormatter(formatter)
root_logger = logging.getLogger()
root_logger.addHandler(ltsv_handler) # logging.getLogger() => rootロガー

# hogeが呼ばれる前にloggingの設定をする
import hoge  # => message:スパム ハムの人 time:2014-02-10T23:41:54+09:00

ここまで書いて気がつきましたが、fileConfigでrootロガーを書き換えたらいいのでは???

4. fileConfigを使う

  • logger.iniを用意
[loggers]
keys = root

[handlers]
keys = ltsvhdr

[formatters]
keys = ltsvfmt

[logger_root]
level = DEBUG
handlers = ltsvhdr

[handler_ltsvhdr]
class = StreamHandler
args = (sys.stderr,)
level = DEBUG
formatter = ltsvfmt

[formatter_ltsvfmt]
format = time:%(asctime)s\tmessage:%(message)s
datefmt = %Y-%m-%dT%H:%M:%S%z
class = ltsvlogger.LTSVFormatter

script側ではfileConfigするだけ。

# -*- coding: utf-8 -*-
import logging.config
logging.config.fileConfig('logger.ini')

# hogeが呼ばれる前にloggingの設定をする
import hoge  # => message:スパム ハムの人 time:2014-02-10T23:41:54+09:00

大体終わりです。

いろいろ怒られそうなので、書こうか迷うましたが、hogeを利用するスコープが限定的なら、mock.patchすればいいのでは!???とかいう愚行を一番最初に思いついた事を告白しておきます。石は投げないでください。

# -*- coding: utf-8 -*-
import mock
import logging
import ltsvlogger

formatter = ltsvlogger.LTSVFormatter(fields={
    'asctime': 'time',
    'message': 'message',
})
logger = logging.getLogger("nonrootlogger")
ltsv_handler = logging.StreamHandler()
ltsv_handler.setFormatter(formatter)
logger.addHandler(ltsv_handler)

with mock.patch("logging.warn", side_effect=logger.warn):
   import hoge # => message:スパム ハムの人 time:2014-02-10T23:41:54+09:00

ではではアデュー

Dockerをドカッと触ってみた(Macで)

Dockerが0.8でMac OSXのサポートをしたみたいな事をどっかでみたので、思考を停止してやってみた。触ってみただけなので全然ドカッと触れてない事に留意してください。言ってみたかっただけです。

あ。あけましておめでとうございます。

http://blog.docker.io/2014/02/docker-0-8-quality-new-builder-features-btrfs-storage-osx-support/

Dockerはそれまでは、Dockerを動かすためのVagrantfileを配布していて、VagrantVM起動、そのVM上でdockerを使ってくれよな!よろしくー!的な感じだった。

それがMac上から直接dockerコマンドを使えるようになったぜ!ヒャッハー!ていう話らしい。

0. 前提

1. boot2dockerとdocker client をセットアップ

マニュアルとほぼ同じような事をやる

$ cd /usr/local/bin

$ curl -o boot2docker https://raw.github.com/steeve/boot2docker/master/boot2docker
$ chmod +x boot2docker

$ curl -o docker http://get.docker.io/builds/Darwin/x86_64/docker-latest
$ chmod +x docker

$ export DOCKER_HOST=tcp://

$ boot2docker init
$ boot2docker up
# コマンド一覧の確認
$ boot2docker 
  • boot2dockerなんなの? - TinyCoreLinuxをベースにDockerを動かす設定をしたVMVirtualBoxに作ってくれる。それをコマンドラインで操作できるようにしてくれる。
  • Mac用のdockerて何さ? - 上記のVM経由でdockerコマンドを実行してくれるクライアント。Mac側で叩いたdockerコマンドをVMで実行してくるショートカットみたいなもん
  • 結局VM上でdockerを動かしてるのは、今までのVagrantFileを配布していた時と大して変わらない感じだった。

2. dockerコマンドを叩く

既にこの時点でdockerが利用可能になってる

 # versionの確認
 $ docker version
 Client version: 0.8.0
 Go version (client): go1.2
 Git commit (client): cc3a8c8
 Server version: 0.8.0
 Git commit (server): cc3a8c8
 Go version (server): go1.2

3. ssh出来るコンテナを作る

完全に目的なくセットアップしてたので何していいか分からなくなった

http://blog.ryotarai.info/blog/2013/07/25/docker-sshd-container/

とりあえずココを参考にsshできるコンテナを作成する。

下記のようなopenssh-serverをインスコしてsshdを立てて、rootてパスでログインできるようなイメージを作成する「Dockerfile」を置く

 FROM ubuntu:12.04
 MAINTAINER tell-k "ffk2005@gmail.com"

 RUN apt-get update
 RUN apt-get install -y openssh-server
 RUN mkdir /var/run/sshd
 RUN bash -c 'echo "root:root" | chpasswd'

 CMD /usr/sbin/sshd -D
 EXPOSE 22

ビルドする

 $ docker build ./
 Uploading context 1.842 GB
 Uploading context
 Step 0 : FROM ubuntu:12.04
  ---> 9cd978db300e
 Step 1 : MAINTAINER tell-k "ffk2005@gmail.com"
  ---> Using cache
  ---> f39bc83bc3a1
 Step 2 : RUN apt-get update
  ---> Using cache
  ---> 43349868d941
 Step 3 : RUN apt-get install -y openssh-server
  ---> Using cache
  ---> 71e6832c0d64
 Step 4 : RUN mkdir /var/run/sshd
  ---> Using cache
  ---> 14d8d5b361c1
 Step 5 : RUN bash -c 'echo "root:root" | chpasswd'
  ---> Using cache
  ---> d1ccd560aa9e
 Step 6 : CMD /usr/sbin/sshd -D
  ---> Using cache
  ---> fdf4c6068540
 Step 7 : EXPOSE 22
  ---> Running in 2b0a51afe8e3
  ---> 4aa9bed82ea4
 Successfully built 4aa9bed82ea4

ビルドしたイメージをtagとして保存する

 $ docker tag 4aa9bed82ea4 tellk/sshd

よーしパパ。コンテナつくっちゃうぞーハハハー。なんつってコンテナ作る

 $ docker run -d -p 22 tellk/sshd
 a1023b856c14286886e44485435cfda51d6f35a31c48a5fe76ccbac7aac9d3c3

コンテナIDから、SSHすべきportを調べる

 $ docker ps 
 CONTAINER ID        IMAGE               COMMAND                CREATED             STATUS              PORTS                   NAMES
 a1023b856c14        tellk/sshd:latest   /bin/sh -c /usr/sbin   50 seconds ago      Up 48 seconds       0.0.0.0:49154->22/tcp   evil_franklin       

 $ docker port a1023b856c14 22
 0.0.0.0:49154
 # 49154にSSHすればいいらしい

4. sshで繋いでみる

まずはMac -> boot2docker_vmsshする

 # とりあえず鍵作成
 $ ssh-keygen -t rsa
 
 # なんやかんや聞かれるのでnopassで鍵を作成して、
 # 下記のような感じで、鍵を置く。
 ~/.ssh/id_rsa_boot2docker_vm
 ~/.ssh/id_rsa_boot2docker_vm.pub

 # 公開鍵をboot2docker_vmにコピー
 $ ssh-copy-id -i ~/.ssh/id_rsa_boot2docker_vm.pub localhost -p 2022
 # パスが求められる「tcuser」がデフォのパス

 # ~/.ssh/configに以下を追加
 Host localhost
   User docker
   StrictHostKeyChecking no  
   UserKnownHostsFile /dev/null 
   IdentityFile /Users/tell_k/.ssh/id_rsa_boot2docker_vm
   HostName 127.0.0.1

 # sshをしてみる。パス無しでログインできる。
 $ ssh localhost -p 2022
 # boot2docker ssh でも パス無しでログイン可
 $ boot2docker ssh 

boot2docker_vmにログインしたらコンテナが起動してるIPを調べる

 docker@boot2docker:~$ ifconfig | grep -A4 docker0
 docker0   Link encap:Ethernet  HWaddr FE:17:B0:FF:B4:70  
             inet addr:172.17.42.1  Bcast:0.0.0.0  Mask:255.255.0.0
             inet6 addr: fe80::a055:fff:fe78:9134/64 Scope:Link
             UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1

 # 172.17.42.1 として起動してるらしい

IPとポートが分かったんで、boot2docker_vm -> Dockerコンテナ(192.17.42.1:49154) にsshする

  docker@boot2docker:~ $ ssh root@172.17.42.1 -p 49154
  # passは「root」になってる
  root@172.17.42.1's password:

  Welcome to Ubuntu 12.04 LTS (GNU/Linux 3.12.1-tinycore64 x86_64)

   * Documentation:  https://help.ubuntu.com/
     Last login: Sun Feb  9 13:44:12 2014 from 172.17.42.1

  root@a1023b856c14:~#
  # 無事にログインできたぽい

こんどはSSHポートフォーディングで Mac -> (boot2docker_vm)-> Dockerコンテナ(192.17.42.1:49154) 的な感じでMacからDokcerコンテナに直ログインする。

 # Macに戻って ~/.ssh/configを以下の内容を追加
 Host container01
   ProxyCommand ssh localhost -p 2022 nc %h %p
   StrictHostKeyChecking no  
   User root
   Port 49154
   HostName 172.17.42.1

 # これで直接 Dockerコンテナにログインできる
 $ ssh container01 

 # ここでもパス入力省きたいから、boot2docker_vmと同じ公開鍵をコンテナに設置する。
 $ ssh-copy-id -i ~/.ssh/id_rsa_boot2docker_vm.pub container01

 # 上のssh-configに下記一行追加
   IdentityFile /Users/tell_k/.ssh/id_rsa_boot2docker_vm

 # これでnopassで鍵つかってsshできる
 $ ssh container01

 root@a1023b856c14:~#

とりあえず終わり

まとめ

  • boot2dockerとそれに対応したdockerによって,Macから透過的にVM上のdockerを操作出来る事が分かった。
  • コンテナの作成(docker run)は一瞬で終わるので、Vagrantよりはストレスフリーだった。
  • もうちょっとうまい事こなれてみたいと思った。
  • 複数コンテナを立ち上げて、chef-soloやら、ansibleやらでゴニョゴニョしてみたいと思った。

Django で unittest を高速化する(主にDBの話し)

yuheiさんが書き残してくれたありがたい記事を参考にゴニョゴニョしてて気になって事があったので、調べてみた時のメモ

この記事には以下の対応が書かれていました。

  • 対応2 sqlite3のin-memory databaseにしてみる
  • 対応3 southでテスト時にmigrateを使わず、syncdbでテーブル作成を行わせる

この記事の結果を見た時に、あれ?これってそんなに効果ないんだっけ?て気になったのでちょっと調べてみた次第。

前提&環境

  • Djangoは1.4系
  • South(DBマイグレーションツール)を利用
  • Migrationファイルは79ファイルある
  • テストランナーはカスタマイズして、Django本体&サードパーティライブラリのテストは動かないようにしている。
  • MySQL5.6.13を使用(homebrewでインスコ)..my.cnfとか特にチューニングしたりしてない。
  • SQLite3.7.12を使用(homebrewでインスコ)
  • テーブル数は70位
  • 使用マシン
    • OS: Mac OSX 10.8.5
    • CPU: 3.2GHz Core i3
    • メモリ: 4GB
    • HDD: 1TB

やりたい事

  • ローカルでテスト全体を走らせた時に速く終らせたい(コミット&プッシュ前とかに良くするので)
  • 主に利用するDBを変えてみる事でどれくらい実行時間が変わるか知りたい
  • Southのマイグレーションをテスト時にオフする事で実行時間が短縮されるか知りたい

1. MySQL(Migration有り)

  • まずは素のMySQLでテストを走らせる
  • manage.py testで出力されるテスト実行時間と体感時間にかなり差があるのでtimeコマンド付きで実行する。
 $ time python manage.py test --settings=hoge.settings.test

結果

# manage.py testが吐き出す結果
Ran 265 tests in 29.224s

# timeコマンドで計測した結果
real    3m10.899s
user    0m17.511s
sys     0m1.450s
  • テスト実行は29秒で終ってんのに、実際は3分もかかってやがる。
  • Djangoはテスト時にtest_で始まるテスト用のDB作成/破棄してる。多分その時間はmanage.py testで吐き出される時間にはインクルードされてない。

2. MySQL(Migration無し)

  • 次にSouthのマイグレーションをOFFにしてやってみる
  • 設定ファイル(settings/test.py)に以下の設定を書くだけ
SOUTH_TESTS_MIGRATE=False

結果

# manage.py test 
Ran 265 tests in 18.015s

# time
real    1m57.275s
user    0m10.954s
sys     0m1.049s
  • テストの実行自体も早くなったが、コマンドの実行時間自体も1分以上早くなった。体感的に全然ちがう。

3. MySQL on ramdisk (Migration有り)

 $ hdid -nomount ram://10670000
 $ newfs_hfs /dev/disk2 # -> 大体6GBのヤツできる
 $ mkdir /tmp/mnt
 $ mount -t hfs /dev/disk2 /tmp/mnt
 $ cp -pr /usr/local/var/mysql /tmp/mnt/

 $ mysql.server stop
 $ mysql.server start --datadir=/tmp/mnt/mysql

結果

# manage.py test 
Ran 265 tests in 17.435s

# time
real    0m34.779s
user    0m19.037s
sys     0m1.342s
  • やだー速いじゃないですかーやだーもー。
  • テスト実行自体は大して速くなってないけど、コマンドの実行時間は大分短くなってストレスなくなってきた。

4. MySQL on ramdisk (Migration無し)

  • さっきと同じようにMigrationをオフってみる

結果

# manage.py test 
Ran 265 tests in 16.330s

# time
real    0m28.271s
user    0m13.639s
sys     0m1.067s
  • 僅かではあるがMigration有りの時より速くなってる。

5. SQLite(Migration有り)

  • こんどは単純にSQLiteにしてやってみる
  • 設定ファイル(settings/test.py)でDATABASEの設定をSQLiteにするだけ
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'unittest.db',
    },
}

結果

# manage.py test 
Ran 265 tests in 12.166s

# time
real    0m22.653s
user    0m14.788s
sys     0m0.810s
  • MigrationがあってもMySQLより速くなった

6. SQLite(Migration無し)

  • 例によってMigrationをOFFる

結果

# manage.py test 
Ran 265 tests in 11.209s

# time
real    0m17.247s
user    0m14.788s
sys     0m0.810s
  • 今までと同様にMigration無い方が実行時間は短くなった

7. SQLite in-memory (Migration有り)

  • 最終形態。SQLiteをin-memoryで利用する
  • さっきのDATABASE設定のNAMEを":memory:"にするだけ
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': ':memory:',
    },
}

結果

# manage.py test 
Ran 265 tests in 10.511s

# time
real    0m16.613s
user    0m14.579s
sys     0m0.647s
  • SQLiteのMigration無しと大差ない結果だった。

8. SQLite in-memory (Migration無し)

  • 最後にMigration無しの試す

結果

# manage.py test 
Ran 265 tests in 10.208s

# time
real    0m11.963s
user    0m10.202s
sys     0m0.456s
  • テスト実行時間自体は大して変わらんけど、コマンド実行時間は10秒前後にまで縮まって、ほぼテスト実行時間とイコールになった。

まとめ

8パターンの計測を行った、最後にそれぞれのパターンを複数回(10回程度)実行して、その中央値を表にしてみた。

f:id:tell-k:20131010154753p:plain

  • mange.py test が出してるテスト実行時間には、テストDBの作成/破棄の時間は含まれない
  • 同様にMigrationしてる間の時間も含まれないのでMigrationをOFFにした方が実際の時間は短縮される
  • テーブル数、Migration数が多いほど、SQLite in-memoryでテスト実行時間が短くなってるのが実感できるのでオヌヌメ
  • ストレスフリーになってよかったな > 俺

余談

  • MySQLSQLiteに変更する事でテストが失敗するケースがあった。
    • Modelのテストで自動で日付を挿入するようなDatetime型のカラムをorder byしてselectしようとした。
    • その時テストデータ作成時に連続して createしたりすると、MySQLだと全く同じ時間になってしまって、結果の順序がSQLiteと違ってたりした。
    • テストで順序を保証したかったりしたら工夫する必要がある。
  • 実環境に近い所でテストがしたかったら、mysqld_multiとかでテスト用インスタンスramdiskに作るとかでもいいかもね。

おわり。