世界線航跡蔵 2020-06-20T09:54:43+09:00 yugui Hatena::Blog hatenablog://blog/10328749687202014984 新型コロナウイルス接触確認アプリのソースコードを請求してみた hatenablog://entry/26006613587392024 2020-06-20T09:54:43+09:00 2020-07-29T22:10:36+09:00 厚生労働省の「新型コロナウイルス接触確認アプリ」が公開された。 かねて話題になっていたように、ある程度匿名性を保ったままbluetoothで他のデバイスが近隣に留まったことを認識する方式らしく、割と安心できそうかと思う。 またITMediaの記事によればCOVID-19 Japanという有志によるオープンソースプロジェクトを元にしているそうだ。 ただし、記事を読む限りでは完全にオープンソースプロジェクトそのままというわけではなく「COVID-19 Radar」の技術を核として厚生労働省がベンダーに開発を委託したとある。 そうなると、いくつか気になる点がある。 「COVID-19 Radar」の… <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%B8%FC%C0%B8%CF%AB%C6%AF%BE%CA">厚生労働省</a>の「<a href="https://www.mhlw.go.jp/stf/seisakunitsuite/bunya/cocoa_00138.html">新型コロナウイルス接触確認アプリ</a>」が公開された。</p> <p>かねて話題になっていたように、ある程度匿名性を保ったまま<a class="keyword" href="http://d.hatena.ne.jp/keyword/bluetooth">bluetooth</a>で他のデ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D0%A5%A4%A5%B9">バイス</a>が近隣に留まったことを認識する方式らしく、割と安心できそうかと思う。</p> <p>また<a href="https://www.itmedia.co.jp/news/articles/2006/19/news100.html">ITMediaの記事</a>によれば<a href="https://lp-covid-19radarjapan.studio.design/">COVID-19 Japan</a>という有志による<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%D7%A5%F3%A5%BD%A1%BC%A5%B9">オープンソース</a>プロジェクトを元にしているそうだ。 ただし、記事を読む限りでは完全に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%D7%A5%F3%A5%BD%A1%BC%A5%B9">オープンソース</a>プロジェクトそのままというわけではなく「COVID-19 Radar」の技術を核として<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B8%FC%C0%B8%CF%AB%C6%AF%BE%CA">厚生労働省</a>がベンダーに開発を委託したとある。</p> <p>そうなると、いくつか気になる点がある。</p> <ul> <li>「COVID-19 Radar」のソースは公開されているからプライバシー等への懸念がある場合にはそれを読んで確認すれば良い、というような意見もあるが、「COVID-19 Radar」と「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BF%B7%B7%BF%A5%B3%A5%ED%A5%CA%A5%A6%A5%A4%A5%EB%A5%B9">新型コロナウイルス</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%DC%BF%A8">接触</a>確認アプリ」がその点において同一であるという確証はない。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/%CD%F8%CD%D1%B5%AC%CC%F3">利用規約</a>には「COVID-19 Radar」の<a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>へのポインタとそのライセンス上の要請に従う旨は書いてあるが、修正の有無や修正版のソースの所在は明確でない。</li> <li>有志が集ってTech Giants各社が支援して<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%D7%A5%F3%A5%BD%A1%BC%A5%B9">オープンソース</a>で作り上げたという折角の良い話なので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%D7%A5%F3%A5%BD%A1%BC%A5%B9">オープンソース</a>ライセンスへの尊重は守りたい。</li> </ul> <p>そういうわけなので、「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BF%B7%B7%BF%A5%B3%A5%ED%A5%CA%A5%A6%A5%A4%A5%EB%A5%B9">新型コロナウイルス</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%DC%BF%A8">接触</a>確認アプリ」の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>を請求してみた。 「COVID-19 Radar」は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Mozilla">Mozilla</a> Public License 2.0で公開されているので、「<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BF%B7%B7%BF%A5%B3%A5%ED%A5%CA%A5%A6%A5%A4%A5%EB%A5%B9">新型コロナウイルス</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%DC%BF%A8">接触</a>確認アプリ」のうち「COVID-19 Radar」を元につくった部分については利用者が<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>を得られるはずだ。</p> <blockquote class="twitter-tweet"><p lang="ja" dir="ltr">MPL 2.0の3.2項に基づく<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>の開示請求をしてみた。<br>なんか修正をしてるような報道があるので「<a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a>見ろ」ではない回答が来ると期待してるんだけど。<a href="https://twitter.com/hashtag/%E6%8E%A5%E8%A7%A6%E7%A2%BA%E8%AA%8D%E3%82%A2%E3%83%97%E3%83%AA?src=hash&amp;ref_src=twsrc%5Etfw">#接触確認アプリ</a> <a href="https://twitter.com/hashtag/%E6%8E%A5%E8%A7%A6%E7%A2%BA%E8%AA%8D%E3%82%A2%E3%83%97%E3%83%AACOCOA?src=hash&amp;ref_src=twsrc%5Etfw">#接触確認アプリCOCOA</a></p>&mdash; <a class="keyword" href="http://d.hatena.ne.jp/keyword/Yuki">Yuki</a> Yugui Sonoda (@yugui) <a href="https://twitter.com/yugui/status/1274085258598572032?ref_src=twsrc%5Etfw">June 19, 2020</a></blockquote> <p> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script></p> <p>アプリ内には特に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>取得方法については言及がなかったので、問い合わせ窓口として記述されているメールアドレスに開示請求メールを送った。しばらく返事を待ってみようと思う。</p> <h1>追記</h1> <p>こういう、ほぼそのまま使い物になる<a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>プロダクトに手を入れてリリースするっていう場合、ロゴやプライバシーポリシーを<a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>にしないで済ませるためのメインラインのごく近くを走り続けるforkみたいなのを想像する。ただ、COVID-19 Radarの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>を見ると既に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B8%FC%C0%B8%CF%AB%C6%AF%BE%CA">厚生労働省</a>のロゴやプライバシーポリシーが入っているのでそうではないらしい。</p> <h1>更に追記</h1> <p>更新が少し遅くなったが、その後問い合わせ窓口から次のような返信を貰った</p> <blockquote><p>本アプリにつきましては<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%D7%A5%F3%A5%BD%A1%BC%A5%B9">オープンソース</a>で開発をしており、[Covid-19 Radar]としては既に各種情報は公開済で誰もが確認できる状況になっております。 今後開発元であった[Covid-19 Radar]から国に主幹が移管されていきますが、今回のアプリケーションとしての公開方法や場所については現在検討中となります。</p></blockquote> <p>この返信自体に疑いを持つ人もいるようだが、ひとまず個人的には信頼できると感じている。</p> yugui 巨大数論の面白み ―― 現代思想 「巨大数の世界」 hatenablog://entry/26006613491773378 2020-01-01T19:56:31+09:00 2020-01-01T22:08:52+09:00 現代思想 2019年12月号 特集=巨大数の世界 ―アルキメデスからグーゴロジーまで―作者:鈴木真治,フィッシュ,小林銅蟲,詩野うら出版社/メーカー: 青土社発売日: 2019/11/28メディア: ムック 『現代思想2019年12月号 特集=巨大数の世界』を読んだ。かのフィッシュ氏による巨大数論解説に始まり、巨大基数や組合せ論や、古代ギリシャ以来もしくは仏教における巨大数、巨大数や数学的実体の存在論、永遠についての時間論、はたまたジンバブエドルなどを網羅している。巨大な概念を愛好する向きにはたまらない一冊である。またそれらのトピックが単に巨大であるという理由で任意に集められたわけではなく一定… <p><div class="hatena-asin-detail"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/4791713893/idm-22/"><img src="https://images-fe.ssl-images-amazon.com/images/I/41vod0aWsRL._SL160_.jpg" class="hatena-asin-detail-image" alt="現代思想 2019年12月号 特集=巨大数の世界 ―アルキメデスからグーゴロジーまで―" title="現代思想 2019年12月号 特集=巨大数の世界 ―アルキメデスからグーゴロジーまで―"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="https://www.amazon.co.jp/exec/obidos/ASIN/4791713893/idm-22/">現代思想 2019年12月号 特集=巨大数の世界 ―アルキメデスからグーゴロジーまで―</a></p><ul><li><span class="hatena-asin-detail-label">作者:</span><a href="http://d.hatena.ne.jp/keyword/%CE%EB%CC%DA%BF%BF%BC%A3" class="keyword">鈴木真治</a>,<a href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A3%A5%C3%A5%B7%A5%E5" class="keyword">フィッシュ</a>,<a href="http://d.hatena.ne.jp/keyword/%BE%AE%CE%D3%C6%BC%EA%B5" class="keyword">小林銅蟲</a>,<a href="http://d.hatena.ne.jp/keyword/%BB%ED%CC%EE%A4%A6%A4%E9" class="keyword">詩野うら</a></li><li><span class="hatena-asin-detail-label">出版社/メーカー:</span> <a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%C4%C5%DA%BC%D2">青土社</a></li><li><span class="hatena-asin-detail-label">発売日:</span> 2019/11/28</li><li><span class="hatena-asin-detail-label">メディア:</span> ムック</li></ul></div><div class="hatena-asin-detail-foot"></div></div></p> <p><a href="http://www.seidosha.co.jp/book/index.php?id=3363">『現代思想2019年12月号 特集=巨大数の世界』</a>を読んだ。かのフィッシュ氏による巨大数論解説に始まり、巨大基数や組合せ論や、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B8%C5%C2%E5%A5%AE%A5%EA%A5%B7%A5%E3">古代ギリシャ</a>以来もしくは仏教における巨大数、巨大数や数学的実体の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C2%B8%BA%DF%CF%C0">存在論</a>、永遠についての時間論、はたまた<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B8%A5%F3%A5%D0%A5%D6%A5%A8%A5%C9%A5%EB">ジンバブエドル</a>などを網羅している。巨大な概念を愛好する向きにはたまらない一冊である。またそれらのトピックが単に巨大であるという理由で任意に集められたわけではなく一定の相互関係を持っている点は注目に値する。</p> <p>思想史ないし数学史の延長上における巨大数の位置付けは「情報社会にとって『数』とは何か」という<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C2%E7%B9%F5%B3%D9%C9%A7">大黒岳彦</a>氏の論で述べられていて、これも大変勉強になった。現代の、なかんずく<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D2%A5%EB%A5%D9%A5%EB%A5%C8">ヒルベルト</a>以降の数学が経験的世界から離陸して形相を契機とするようになったというのは確かにそうだし、その流れの中で数学が閉じた自己言及的円環をなす過程で<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BF%F4%B3%D8%B4%F0%C1%C3%CF%C0">数学基礎論</a>が生まれたというのはきっとそうなのだろう。これに対して一般に巨大数への関心が質料的契機を持つというのもまた間違いない。</p> <p>一方で巨大数論のファンとしてはやはり、フィッシュ氏の意味での巨大数論が興味深いのは円環を経験的世界にある意味で接地させる奇妙なバイパスとして機能している点にある。 大きな数が面白いのはそれが経験的世界から見て著しく大きいからに他ならないが、その大きな数を競うというア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DE%A5%C1%A5%E5%A5%A2">マチュア</a>の遊びが比較的すぐに<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BD%E3%BF%E8%BF%F4%B3%D8">純粋数学</a>の自己言及的円環を成立させる要たる基礎論の言葉によって理論化されるというのは感動的というほかない。これは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BD%E3%BF%E8%BF%F4%B3%D8">純粋数学</a>の力強さを象徴するようでもあり、離陸した円環が落とす影が経験的世界において意味を持つ(おそらくは論で言うところの「下への超越性」によって)ことが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%C8%A5%F3">プラトン</a>的な数と経験的世界の紐帯をほそぼそと復活させるようでもあり、そうして興味深いとともにア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DE%A5%C1%A5%E5%A5%A2">マチュア</a>の営みがそこへ繋がることがいささか小気味よくもある。</p> <h1>巨大数論の面白み</h1> <p>私にとっては巨大数論の面白みは<a href="https://comic.pixiv.net/viewer/stories/6997">「寿司 虚空編」004</a> p.11の「ωだ」の一言に集約される。まさにこの1コマを見たために巨大数論のファンになった。</p> <blockquote><p><span itemscope itemtype="http://schema.org/Photograph"><img src="https://cdn-ak.f.st-hatena.com/images/fotolife/y/yugui/20200101/20200101170913.png" alt="&#x300C;&omega;&#x300D;&#x3060;" title="f:id:yugui:20200101170913p:plain" class="hatena-fotolife" itemprop="image"></span> <a class="keyword" href="http://d.hatena.ne.jp/keyword/%BE%AE%CE%D3%C6%BC%EA%B5">小林銅蟲</a>, 2015, <a href="https://comic.pixiv.net/viewer/stories/6997">寿司 虚空編 004</a>, p.11</p></blockquote> <p>この感動を伝えるには、まず巨大数論のある種の退屈さを述べなければならないだろう。</p> <p>巨大数が数学の主流から見てトリヴィアルであるのは確かである。任意に大きな<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BC%AB%C1%B3%BF%F4">自然数</a>は存在するのだし、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BD%E3%BF%E8%BF%F4%B3%D8">純粋数学</a>の円環の一部としての必然性も無しにたかが大きな数を作って何が面白いのだろうか。実際、私は現在の日本の巨大数論が生まれた場のすぐ近くまで寄りながらさしたる関心を払わなかった記憶がある ―― 本書におけるp.19「巨大数論発展の軌跡」によれば、現在の日本の巨大数論は<a class="keyword" href="http://d.hatena.ne.jp/keyword/2%A4%C1%A4%E3%A4%F3%A4%CD%A4%EB">2ちゃんねる</a>数学板の「一番でかい数出した奴が優勝」スレに始まっているそうだ。私も同時期に数学板に出入りしていたからこの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%EC%A5%BF%A5%A4">スレタイ</a>は見覚えがあるが、敢えて深く読み込もうとは思わなかった。</p> <p>一方でそこには素朴な楽しみがあったはずだ。p.82「歴史的に見た巨大数の位置付け」やp.109「巨大数の経験」で仏典の例が挙げられているように、大きな数はそれが擬似的に経験の限界を超越するゆえに人の興味を引く。こうして生まれた大きな数を挙げる遊びが、まさに遊びであるがためにその後につながっていく。</p> <p>任意に大きな<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BC%AB%C1%B3%BF%F4">自然数</a>は存在するが、この遊びで勝つためには有限の手続きでその数を述べられなければならない。また、グラハム数Gを挙げることは容易だがこれはG+1に負ける。しかしまたG+1+1+1と後者を挙げていく戦略はもちろんG+Gに負ける。そしてそれらのどれよりもG<sup>G</sup>が強い。 こうして、大きな数を挙げる遊びはより急速に増大する関数を構成することにすぐさま帰着する。そして、後者から加法を、加法から乗法を、乗法から指数を構成していく過程は<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B6%F5%BD%B8%B9%E7">空集合</a>から順序数を構成する過程と同型となり、言われてみればほとんど当たり前なのだが、このように巨大数を挙げたり2つの巨大数を比較することは順序数のそれと対応付けられるようだ。そして遂に、現在の巨大数論の先端はωを超えてε<sub>0</sub>に対応する関数に至る。 巨大数論はそれがア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DE%A5%C1%A5%E5%A5%A2">マチュア</a>の遊びに由来することによってむしろ必然的に、順序数や、計算可能関数や、ラムダ計算といった非自明な説明を必要とする。</p> <p>つまりは、p.14の対談における鈴木真治氏のコメントにまったく同感で、たかだか有限の数――それも大きなものを挙げて勝つという他愛もない遊び――が超限順序数のような無限の世界のものと結びつくというただその一点が何とも愛おしくてならない。</p> yugui 転職エントリ(1年後) hatenablog://entry/26006613434418506 2019-10-23T20:50:12+09:00 2019-10-26T13:11:04+09:00 Nianticに転職して1年あまりが過ぎた。 2018年の9月からNianticで働いているのだが、そういえば転職エントリーを書きそびれていた。 転職して以来、相変わらずサーバーサイドの開発をしていている。なお、開発しているのはIngressでもPokémon GOでもハリー・ポッター:魔法同盟でもない。 Nianticとの関わり Ingressは2014年12月から続けていて、Pokémon GOも日本での正式リリース日からぼちぼちやってきているものの、それにしてもNianticで働くようになるとは思ってもみなかった。 Pokémon GOがなんかえらく流行り始めたときも、自分とは関係ない話… <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Niantic">Niantic</a>に転職して1年あまりが過ぎた。</p> <p>2018年の9月から<a href="https://nianticlabs.com/ja/about/">Niantic</a>で働いているのだが、そういえば転職エントリーを書きそびれていた。 転職して以来、相変わらずサーバーサイドの開発をしていている。なお、開発しているのは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ingress">Ingress</a>でもPokémon GOでも<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%CF%A5%EA%A1%BC%A1%A6%A5%DD%A5%C3%A5%BF%A1%BC">ハリー・ポッター</a>:魔法同盟でもない。</p> <h1><a class="keyword" href="http://d.hatena.ne.jp/keyword/Niantic">Niantic</a>との関わり</h1> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Ingress">Ingress</a>は2014年12月から続けていて、Pokémon GOも日本での正式リリース日からぼちぼちやってきているものの、それにしても<a class="keyword" href="http://d.hatena.ne.jp/keyword/Niantic">Niantic</a>で働くようになるとは思ってもみなかった。 Pokémon GOがなんかえらく流行り始めたときも、自分とは関係ない話だと思いつつ<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google%20Maps">Google Maps</a>時代の知り合いが何人か関わっているのを思い出して無責任に祝福していたぐらいである。知り合いへのご祝儀のつもりでポケコインを1万円分ぐらい買って、行動圏内にルアーモジュールを設置しまくったのを覚えている。</p> <p>転職後に変わったことといえば、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ingress">Ingress</a>のイベントに参加するようになったりPokémon GOのプレイ頻度が増えたことぐらいだろうか。やはり社内でこれらの話題に触れる機会が増えると自然と関心が湧いてくるので。最近も<a href="/entry/2019/09/16/202938">Field Test福井に参加</a>してきた。</p> <h1>転職時に思ったこと</h1> <p>こうしてみると以前から<a class="keyword" href="http://d.hatena.ne.jp/keyword/Niantic">Niantic</a>に若干の縁はあったのだが、もちろん転職時には他の会社も検討した。検討してみて、いくつかの点に魅力を感じて<a class="keyword" href="http://d.hatena.ne.jp/keyword/Niantic">Niantic</a>を選ぶことになった。たとえば、東京オフィスにいる人達の半分くらいは顔見知りで、彼らと一緒に働くのは楽しそうだと思ったこと。色々と興味深い解くべき技術的課題を持っていて面白そうだったこと。 なかんずく、見知らぬ土地に出かけたり歩いたり、人と関わることを促進するような未来像・理想を<a href="https://nianticlabs.com/blog/nrwp-update/">語っている</a>こと。こういう性質はひょっとしたらエコーチェンバー現象を抑制する1つの解になるかもしれない。</p> <p>1年経ってみた感想としても、こういう自らミッション<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%C6%A1%BC%A5%C8%A5%E1%A5%F3%A5%C8">ステートメント</a>を掲げてきちんと計画して、真面目にミッションに向けて取り組んでいる組織ってのはやはり良いもんだと思う。</p> <p>もとより私は<a href="/entry/818">オフラインのように豊穣でオンラインのように便利で安全な世界</a>ってものを望んできた。 リンク先の文書を書いたときに比べれば、なるほど状況はやや変わっていて最早オンラインとオフラインを分けることが無意味になってきてはいる。何しろ今日では常時のインターネット接続が実質的な<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B4%F0%CB%DC%C5%AA%BF%CD%B8%A2">基本的人権</a>とすらなりつつあるのだから。それにしても、「ネットワーク化済み実在とネットワーク未接続実在」とでも読み替えればもうしばらくは通じるだろう。 いずれにせよ<a class="keyword" href="http://d.hatena.ne.jp/keyword/Niantic">Niantic</a>の語る未来像は私が望むそれと通じるし、前掲記事中でも触れられているARのような技術は当然にその有力な構成要素であると思われる。</p> <p>これまでの私の経験則によれば、こういうなんとなく自分の好みな方向の美意識や社会的問題意識に基づいた未来像に協力しておくと、そのうちに自らリードしたいと思うようなすごくのめり込めるものに行き当たって、しばらくは楽しめるはずだ。そういうわけで、当面はこういう未来像を実現する上で私ができる役割を果たしてみたいと思ったのだった。</p> <p>念のため: この記事は筆者の個人的な経験および意見について述べており、筆者の雇用主とは一切関係ありません。</p> yugui 福井行 hatenablog://entry/26006613434392390 2019-09-16T20:29:38+09:00 2019-09-16T20:33:49+09:00 IngressのField Testがあるので福井に行ってきた。 Field Testは国内でも数か所で実施されるのでどこに行こうか結構迷ったのだけど、結果としては福井で正解であった。なんだか、自分でも思った以上に福井と縁があったので、終始それを感じて楽しめたのである。 さて、まず福井につくなり駅の階段で迎えてくれるのが永和システムマネジメントの広告である。Rubyといえばesm, esmといえば福井が本社なので、最初からそこはかとない安心感と親近感を覚えさせてくれる。 福井だからなぁ pic.twitter.com/s9fs4Bl3U7— Yuki Yugui Sonoda (@yugui)… <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Ingress">Ingress</a>の<a href="https://ingress.com/eventdescription/hexathlon-20190914">Field Test</a>があるので福井に行ってきた。 Field Testは国内でも数か所で実施されるのでどこに行こうか結構迷ったのだけど、結果としては福井で正解であった。なんだか、自分でも思った以上に福井と縁があったので、終始それを感じて楽しめたのである。</p> <p>さて、まず福井につくなり駅の階段で迎えてくれるのが永和システムマネジメントの広告である。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>といえばesm, esmといえば福井が本社なので、最初からそこはかとない安心感と親近感を覚えさせてくれる。</p> <blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">福井だからなぁ <a href="https://t.co/s9fs4Bl3U7">pic.twitter.com/s9fs4Bl3U7</a></p>&mdash; <a class="keyword" href="http://d.hatena.ne.jp/keyword/Yuki">Yuki</a> Yugui Sonoda (@yugui) <a href="https://twitter.com/yugui/status/1172709677617926144?ref_src=twsrc%5Etfw">September 14, 2019</a></blockquote> <p>次に、階段を降りたところにいるのが見慣れた恐竜博士の像。ながらく表参道で働いていたのだけれども、昼休みによく歩いたあたりに福井のアンテナショップがあって、その前にも<a href="https://goo.gl/maps/7NmZ4QSQXdWCgiNi8">恐竜博士がいた</a>ものだった。見慣れた姿を本場で見ることができて感激する他ない。</p> <blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">恐竜博士、表参道の福井の店の前に居たのと同じだ。 <a href="https://t.co/0txmFi6iYT">pic.twitter.com/0txmFi6iYT</a></p>&mdash; <a class="keyword" href="http://d.hatena.ne.jp/keyword/Yuki">Yuki</a> Yugui Sonoda (@yugui) <a href="https://twitter.com/yugui/status/1172710283594235904?ref_src=twsrc%5Etfw">September 14, 2019</a></blockquote> <p>聞けば、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A1%B0%E6%B1%D8">福井駅</a>には恐竜博士があと3体いるそうな。改札口をでたところにも確かに居た。他にも<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%A8%A4%C1%A4%BC%A4%F3%C5%B4%C6%BB">えちぜん鉄道</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A1%B0%E6%B1%D8">福井駅</a>にもいるのを後に発見するが、残り1体は所在がわからなかった。</p> <p>さて、メインのField Test中にポータルを周る際には福井のエージェントのかたがたとご一緒できて(途中ではぐれたけど)、とてもありがたかった。なんとかメダルを獲得できたけれども、時間内に3000m歩くという項目はかなりギリギリだった。記録上は3017mとなっている。</p> <p>天気にも恵まれて適度に曇って暑すぎず、良い<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ingress">Ingress</a>日よりだったように思う。FT福井の中の人には改めて感謝したい。</p> <blockquote class="twitter-tweet"><p lang="ja" dir="ltr">フィールドテスト福井に参加された皆様お疲れ様でした!スタート前の集合写真をご査収ください<a class="keyword" href="http://d.hatena.ne.jp/keyword/%28%A1%AD%A1%A6%A6%D8%A1%A6%60%29">(´・ω・`)</a><a href="https://twitter.com/hashtag/FT%E7%A6%8F%E4%BA%95?src=hash&amp;ref_src=twsrc%5Etfw">#FT福井</a><a href="https://twitter.com/hashtag/ingress?src=hash&amp;ref_src=twsrc%5Etfw">#ingress</a> <a href="https://twitter.com/hashtag/fieldtests?src=hash&amp;ref_src=twsrc%5Etfw">#fieldtests</a> <a href="https://t.co/5BlO60Yl0k">pic.twitter.com/5BlO60Yl0k</a></p>&mdash; フィールドテストField Tests福井20190914 (@ingressfukui) <a href="https://twitter.com/ingressfukui/status/1172837090062503937?ref_src=twsrc%5Etfw">September 14, 2019</a></blockquote> <p>FT福井の翌日、この日は当初からあちこち見て回るつもりでいた。さて福井といって思い出すのは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A1%B0%E6%B8%A9">福井県</a>民が活躍する『ミカるんX』であろう。いやかなり偏った見解かもしれないけど、あの作品のファンなので。そういうわけで<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%BB%C3%CF%BD%E4%CE%E9">聖地巡礼</a>として<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%AB%A5%C4%D0%A7">ソースカツ丼</a>を食べて、恐竜博物館に行かなければならない。</p> <blockquote class="twitter-tweet" data-conversation="none"><p lang="ja" dir="ltr">恐竜博物館にも居た。まあ、そうか。 <a href="https://t.co/4A4lb4Y6jE">pic.twitter.com/4A4lb4Y6jE</a></p>&mdash; <a class="keyword" href="http://d.hatena.ne.jp/keyword/Yuki">Yuki</a> Yugui Sonoda (@yugui) <a href="https://twitter.com/yugui/status/1173096100451848193?ref_src=twsrc%5Etfw">September 15, 2019</a></blockquote> <p>恐竜博物館については良い評判を聞いていたものの、実際にはその期待をもはるかに上回った。常設展示場にはいるとすぐに圧倒的な物量の化石<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B9%FC%B3%CA%C9%B8%CB%DC">骨格標本</a>が目に入る。よく見渡せば古生物学だけに限らず地質学全般の展示もあり、標本の種類も解説も凝っており、完全に堪能しようと思ったら1日ではまるで足りないことが分かってくる。 残念ながらそこまで時間はないのでめぼしいところだけ見たものの、ぜひまた来たいものである。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Ingress">Ingress</a>ポータルを巡りながら<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BE%A1%BB%B3%B1%D8">勝山駅</a>まで歩き、えち鉄で福井に戻る。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%AB%A5%C4%D0%A7">ソースカツ丼</a>は<a href="http://yo-roppaken.gourmet.coocan.jp/">ヨーロッパ軒</a>を薦められたので、その晩に行ってみた。</p> <blockquote class="twitter-tweet"><p lang="ja" dir="ltr">I&#39;m at ヨーロッパ軒 総本店 in <a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A1%B0%E6%BB%D4">福井市</a>, <a class="keyword" href="http://d.hatena.ne.jp/keyword/%CA%A1%B0%E6%B8%A9">福井県</a> <a href="https://t.co/7EHzqBBexU">https://t.co/7EHzqBBexU</a> <a href="https://t.co/ElNRe97y8d">pic.twitter.com/ElNRe97y8d</a></p>&mdash; <a class="keyword" href="http://d.hatena.ne.jp/keyword/Yuki">Yuki</a> Yugui Sonoda (@yugui) <a href="https://twitter.com/yugui/status/1173181425555857409?ref_src=twsrc%5Etfw">September 15, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> yugui 時間管理研修の話 hatenablog://entry/17391345971656349677 2018-06-21T20:53:46+09:00 2018-09-08T07:20:25+09:00 ある社内研修の一コマだった。 私は受講対象者ではないんだけど、良く覚えていない謎の理由によりオブザーバーとして参加していた。 ちょうど講師が「重要でなく緊急でもないもの」「重要でないが緊急のもの」「重要だが緊急でないもの」「重要で緊急のもの」って例の話*1をしていたときだった。話がだいぶすすんでそろそろ次の話題に行こうかというところで、はたと気づいた。私を含めて参加者の半分ぐらいがラップトップを開いてメールやSlackをチェックしながら聴いている。これはどうしたことだろう。 この手のメールチェックってのは往々にして「重要でないが緊急なもの」か「重要でも緊急でもないもの」だ。少なくとも私がやって… <p>ある社内研修の一コマだった。 私は受講対象者ではないんだけど、良く覚えていない謎の理由によりオブザーバーとして参加していた。 ちょうど講師が「重要でなく緊急でもないもの」「重要でないが緊急のもの」「重要だが緊急でないもの」「重要で緊急のもの」って<a href="http://www.itmedia.co.jp/bizid/articles/0909/14/news002.html">例の話</a><a href="#f-450035a5" name="fn-450035a5" title="ビジネス書で引用されすぎていてどこが元ネタなのかよく把握してなかったんだけど、今調べた限りでは『[asin:4863940246:title=7つの習慣]』なのかな?">*1</a>をしていたときだった。話がだいぶすすんでそろそろ次の話題に行こうかというところで、はたと気づいた。私を含めて参加者の半分ぐらいがラップトップを開いてメールやSlackをチェックしながら聴いている。これはどうしたことだろう。</p> <p>この手のメールチェックってのは往々にして「重要でないが緊急なもの」か「重要でも緊急でもないもの」だ。少なくとも私がやってたのはそうだった。 そこで、その場で参加者に訊いてみた。「私も含めてみんなメールチェックしてたけど、それって重要で緊急なものですか? もしそうだったらお忙しいところ参加頂いていて大変申し訳ないんだけど、全員がそうって訳でもないんでは?」 すると、全員が「重要でないが緊急なもの」をやっていた。</p> <p>一方で研修なんてものはおおよそ緊急でなく、でも会社はそれが重要だと思ってなけなしの研修予算と研修時間枠を使って投資している。つまりこれは「重要だが緊急でないもの」だ。 仮に参加者が「こんな聞き飽きた話はくだらねー」と思っていたとしても、会社はそう思ってないのだから、ならば速やかに「この研修くだらねー」という適切なフィードバックを与えるのは<em>重要</em>な仕事だ。</p> <p>なんということだろう。我々は「重要でないが緊急なもの」より「重要だが緊急でないもの」を大切にしよう、という話をまさに聞き、うんうんそうだよなと思いながら、最後の最後まで疑問を持つことなくその逆をやっていた。つまり今まさに間違った優先順位を付けて、「重要だが緊急でないもの」について成果を出してなかった。</p> <p>おそらくこれこそが、「重要だが緊急でないもの」に時間を割こうという話なんだろう。この話はきちんと説明されれば当たり前のことを言っている。漫然と聞いていたら誰もそこにさしたる疑問を抱かない。ただし、それを我がこととして引きつけて考えるには集中が必要だ。どんなに当たり前の話に聞こえたとしても、それを理解するの思考リソースを割く必要を感じなかったとしても、受講した時間から真に価値を引き出すためにはそれを真剣に考えることが必要だった。</p> <p>「<a href="https://www.yamdas.org/column/technique/managerj.html">バックグラウンドで思考する</a>」ってやりかたもあるが、そのために必要なフォアグラウンドタスクは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%EB%A5%AD%A5%E1%A5%C7%A5%B9">アルキメデス</a>の昔から典型的には入浴とか散歩とか落ちものゲーとかそういうやつで、メールチェックや定例会議ではない。 「重要だが緊急でないもの」にきちんと取り組むには、「重要でないが緊急なもの」を明確に排除して存分に取り組む時間を意識的に作らねばならない。</p> <p>我々は、図らずもその意味を研修の中で実証したのだった。</p> <div class="footnote"> <p class="footnote"><a href="#fn-450035a5" name="f-450035a5" class="footnote-number">*1</a><span class="footnote-delimiter">:</span><span class="footnote-text">ビジネス書で引用されすぎていてどこが元ネタなのかよく把握してなかったんだけど、今調べた限りでは『<a href="http://d.hatena.ne.jp/asin/4863940246/idm-22">7つの習慣</a>』なのかな?</span></p> </div> yugui ブロックによるRuby内DSLの起源 hatenablog://entry/17391345971619549873 2018-02-25T15:25:25+09:00 2018-02-26T14:37:28+09:00 時代とともにRubyの使われ方は変わってきましたが、いつの頃からか発生したDSLのホスト言語としての役割にはずっとお世話になってます。人に優しく、故にプログラマ以外にも優しく、これがとてもRubyらしくて好きな点です。 #ruby25th— Yuki Yugui Sonoda (@yugui) 2018年2月24日 僕にとってRubyはfluentdやchefのための言語ですが、プラガブルなOSSエコシステムであったり、強力なDSLを作りやすかったりするところにその魅力を感じます。特にfluentdは大好きなOSSで、Mackerel運営でもかなり参考にさせてもらっています。 #ruby25t… <blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">時代とともに<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の使われ方は変わってきましたが、いつの頃からか発生した<a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>のホスト言語としての役割にはずっとお世話になってます。人に優しく、故に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%ED%A5%B0%A5%E9%A5%DE">プログラマ</a>以外にも優しく、これがとても<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>らしくて好きな点です。 <a href="https://twitter.com/hashtag/ruby25th?src=hash&amp;ref_src=twsrc%5Etfw">#ruby25th</a></p>&mdash; <a class="keyword" href="http://d.hatena.ne.jp/keyword/Yuki">Yuki</a> Yugui Sonoda (@yugui) <a href="https://twitter.com/yugui/status/967211472542511104?ref_src=twsrc%5Etfw">2018年2月24日</a></blockquote> <blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">僕にとって<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>はfluentdやchefのための言語ですが、プラガブルな<a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>エコシステムであったり、強力な<a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>を作りやすかったりするところにその魅力を感じます。特にfluentdは大好きな<a class="keyword" href="http://d.hatena.ne.jp/keyword/OSS">OSS</a>で、Mackerel運営でもかなり参考にさせてもらっています。 <a href="https://twitter.com/hashtag/ruby25th?src=hash&amp;ref_src=twsrc%5Etfw">#ruby25th</a> おめでとうございます!</p>&mdash; songmu (@songmu) <a href="https://twitter.com/songmu/status/967410899777552385?ref_src=twsrc%5Etfw">2018年2月24日</a></blockquote> <blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr"><a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>は本当に感動したところなんだけど、<a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>開発ツールとしての<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>が「発見」されたものなのか意図されたものなのか気になる <a href="https://twitter.com/hashtag/ruby25th?src=hash&amp;ref_src=twsrc%5Etfw">#ruby25th</a></p>&mdash; WAKASUGI 5T111111 (@5t111111) <a href="https://twitter.com/5t111111/status/967290236702146560?ref_src=twsrc%5Etfw">2018年2月24日</a></blockquote> <p>言語内宣言的<a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>を構築する<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の能力がいつからできたものなのか、ふと疑問に思った。</p> <p>この手の記法で一番古くからあるものとして思いつくのはTk bindingだが、これはいつ頃からあったんだろう。 そう思って古い<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>を見てみた。<a href="https://groups.google.com/d/msg/fj.sources/bCc2JimcmAo/ONqCnbyG7LMJ">fj.sourcesにて最初に世界に向けて流された</a>という<a class="keyword" href="http://d.hatena.ne.jp/keyword/ruby">ruby</a> 0.9.5を見てみると、<code>sample/tkhello.rb</code>に次のような例が既にある。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synType">TkButton</span>.new { text <span class="synSpecial">'</span><span class="synConstant">hello</span><span class="synSpecial">'</span> command <span class="synStatement">proc</span>{print <span class="synSpecial">&quot;</span><span class="synConstant">hello</span><span class="synSpecial">\n&quot;</span>} pack(<span class="synSpecial">'</span><span class="synConstant">fill</span><span class="synSpecial">'</span>=&gt;<span class="synSpecial">'</span><span class="synConstant">x</span><span class="synSpecial">'</span>) } </pre> <p>Matzが<a class="keyword" href="http://d.hatena.ne.jp/keyword/Lisp">Lisp</a>を意識していて<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a></sup><a class="keyword" href="http://d.hatena.ne.jp/keyword/Lisp">Lisp</a>で<a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>が盛んに構築されていたことを考えると、これはMatz単独の発明ではないかもしれない。でも、この時点で既にブロックという<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の機能を用いて宣言的にものを記述するという発想はあったことは分かる。</p> <p>たとえブロックという制御構文を自作するための記法があったとしても、宣言的語彙を構築するという意思がなければ<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>はこんな感じの筈だからだ。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synType">TkButton</span>.new {|<span class="synIdentifier">btn</span>| btn.text = <span class="synSpecial">'</span><span class="synConstant">hello</span><span class="synSpecial">'</span> btn.command = <span class="synStatement">proc</span>{print <span class="synSpecial">&quot;</span><span class="synConstant">hello</span><span class="synSpecial">\n&quot;</span>} btn.pack = {<span class="synSpecial">'</span><span class="synConstant">fill</span><span class="synSpecial">'</span>=&gt;<span class="synSpecial">'</span><span class="synConstant">x</span><span class="synSpecial">'</span>} } </pre> <p>Chefや<code>Vagrantfile</code>や<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSpec">RSpec</a>といった現代の<a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>を知っているとすこし物足りないのは確かであるものの、基本的な形は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>が最初に一般に投稿された1995年の時点で完成していたことが分かる。</p> <p>ただし、この発想が更に一歩進んで臨界点を超えて広まり出すにはRakeを待つ必要がある。そして、最後の部品である表現能力のさらなる探索は<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSpec">RSpec</a>によってもたらされたと言うべきであろう。</p> <blockquote class="twitter-tweet" data-conversation="none" data-lang="ja"><p lang="ja" dir="ltr">Rakeが潮目だったという認識でおります</p>&mdash; Kakutani Shintaro (@kakutani) <a href="https://twitter.com/kakutani/status/967305171419017217?ref_src=twsrc%5Etfw">2018年2月24日</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script> <div class="footnotes"> <hr/> <ol> <li id="fn:1"> <p>そもそも<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>にはMatzLispというあだ名があったわけだけど、割とあとのほうになるまで、<code>nil</code>が空配列と同等に扱われるケースが広く残っていたりというあたりが思い当たる。<a href="#fnref:1" rev="footnote">&#8617;</a></p></li> </ol> </div> yugui Corporate Reliability Engineering hatenablog://entry/8599973812328497014 2017-12-21T11:15:28+09:00 2017-12-22T12:23:48+09:00 最近、SRE - Site Reliability EngineeringのアナロジーとしてCorporate Reliability Engineeringというのを考えられないかみたいな話を社内でしている。 SREは伝統的なシステム運用チームの仕事を再定義したものだ。SREはシステムの安定だけを至上目的にするのではなく、際限なく増え続ける運用タスクを真面目にこなすことを目的にするのではない。開発チームを巻き込んで適切なシステム安定度を見いだし、より少ない手間でシステムを安定運用するための開発を自ら行なう。運用のルーチンワークに費やす時間を全体の50%に抑えるように努力する。そして残り50%… <p>最近、SRE - Site Reliability EngineeringのアナロジーとしてCorporate Reliability Engineeringというのを考えられないかみたいな話を社内でしている。</p> <p>SREは伝統的なシステム運用チームの仕事を再定義したものだ。SREはシステムの安定だけを至上目的にするのではなく、際限なく増え続ける運用タスクを真面目にこなすことを目的にするのではない。開発チームを巻き込んで適切なシステム安定度を見いだし、より少ない手間でシステムを安定運用するための開発を自ら行なう。運用の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EB%A1%BC%A5%C1%A5%F3%A5%EF%A1%BC%A5%AF">ルーチンワーク</a>に費やす時間を全体の50%に抑えるように努力する。そして残り50%で今後もその配分を保つ為の開発や、更に上を目指すための仕組み作りを行なう。</p> <p>同じ発想は業務システムや社内セキュリティ、更にはコーポレート部門にも適用できるのではないか。つまり、業務システムを運用して社内業務を回すとか社内セキュリティを保つ業務、あるいは人材を募集したり経費を処理したり業務についても、際限なく増え続けるタスクを間違いなくこなすことを目的にしないやり方があるのではないか。 相対する部門と共同でより少ない手間で適切なエラーレートを保って回していく仕組みを作り続けるのが重要で、それに時間の50%を使うべきではないか。</p> <p>うちの会社はWeb系スタートアップ文化から出発している一方で、急速に規模を拡大している関係上で管理すべき物はしっかり管理する必要に迫られている。そこで、管理が物事を堅苦しくしたり管理コストが線形以上のオーダーで爆発したりしないためには、SREの思想が必要なんではないか、という話を最近している。</p> <p><div class="hatena-asin-detail"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873117917/idm-22/"><img src="https://images-fe.ssl-images-amazon.com/images/I/51Ybz%2B6kIsL._SL160_.jpg" class="hatena-asin-detail-image" alt="SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム" title="SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4873117917/idm-22/">SRE サイトリライアビリティエンジニアリング ―Googleの信頼性を支えるエンジニアリングチーム</a></p><ul><li><span class="hatena-asin-detail-label">作者:</span> 澤田武男,関根達夫,細川一茂,矢吹大輔,Betsy Beyer,Chris Jones,Jennifer Petoff,Niall Richard Murphy,Sky株式会社玉川竜司</li><li><span class="hatena-asin-detail-label">出版社/メーカー:</span> <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A5%E9%A5%A4%A5%EA%A1%BC%A5%B8%A5%E3%A5%D1%A5%F3">オライリージャパン</a></li><li><span class="hatena-asin-detail-label">発売日:</span> 2017/08/12</li><li><span class="hatena-asin-detail-label">メディア:</span> 単行本(ソフトカバー)</li><li><a href="http://d.hatena.ne.jp/asin/4873117917/idm-22" target="_blank">この商品を含むブログを見る</a></li></ul></div><div class="hatena-asin-detail-foot"></div></div></p> yugui はてなブログに引っ越した hatenablog://entry/8599973812325084015 2017-12-09T20:52:12+09:00 2017-12-09T20:52:12+09:00 ここ何年か自作のブログシステムrhianoletheでブログをホストしていたが、とうとう耐えきれなくなったので、はてなブログに引っ越した。 主なリソースについてはすべて旧URLからのリダイレクトを提供しているつもりだが、取りこぼしがあったらアクセスログを見ながら適宜修正していこうと思う。 耐えきれなくなったのは、この数年のwebフロントエンドの発展と私の専門領域の乖離が原因だ。まず第一に、数年というもの私が専門にしてきたのはバックエンドだった。 一方、この期間においてデバイスの多様化やフロントエンド技術の発展がめざましく、こちらはこちらで専門化が進んでいったため、もはや個人が片手間に商用水準の… <p>ここ何年か自作のブログシステムrhianoletheでブログをホストしていたが、とうとう耐えきれなくなったので、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%D6%A5%ED%A5%B0">はてなブログ</a>に引っ越した。 主なリソースについてはすべて旧URLからのリダイレクトを提供しているつもりだが、取りこぼしがあったら<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%AF%A5%BB%A5%B9%A5%ED%A5%B0">アクセスログ</a>を見ながら適宜修正していこうと思う。</p> <p>耐えきれなくなったのは、この数年のwebフロントエンドの発展と私の専門領域の乖離が原因だ。まず第一に、数年というもの私が専門にしてきたのはバックエンドだった。 一方、この期間においてデ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D0%A5%A4%A5%B9">バイス</a>の多様化やフロントエンド技術の発展がめざましく、こちらはこちらで専門化が進んでいったため、もはや個人が片手間に商用水準のフロントエンドをこなせる時代ではなくなってきている。少なくとも私には無理になってきたし、そこは私にとって重要な注力領域ではなくなった。</p> <p>そういうわけなので、しばらく<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%D6%A5%ED%A5%B0">はてなブログ</a>でやっていこうと思う。 それにしても、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%D6%A5%ED%A5%B0">はてなブログ</a>はカスタム<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C9%A5%E1%A5%A4%A5%F3">ドメイン</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/HTTPS">HTTPS</a>対応がいつできるようになるんだろう。</p> yugui Jsonnetでwebアプリケーションの設定ファイルをテスト可能に hatenablog://entry/8599973812317119000 2016-07-30T13:22:47+09:00 2017-11-12T18:06:33+09:00 最近、Jsonnetを用いてwebアプリケーションをデプロイする際に適用する設定群をテストしようとしている。今やInfrastructure as a Codeと言われて久しく、アプリケーション配備の基盤構成もそ... <p>最近、Jsonnetを用いてwebアプリケーションをデプロイする際に適用する設定群をテストしようとしている。</p> <p>今やInfrastructure as a Codeと言われて久しく、アプリケーション配備の基盤構成もその上で走るwebアプリケーションをどうやって設定するのかもコードとして書かれるのが一般的になった。 それらはコードとして書かれているのでバージョン管理可能であり、テストも可能である。だから何かが間違っていれば実際に配備する前に気づくし、それでも間違えていれば差し戻せる。</p> <p>「どうやって設定するか」はコードになった。では「何が設定されるか」はコードになっているだろうか。やっている人もいるだろうけれども、そんなに簡単な話でもない。 実際Chefで設定ファイルを扱う際は <code>templates</code> なり <code>files</code> なりが第一の選択であるとは思うが、これらはよほどヘルパーメソッドを充実でもさせない限りそこまでモジュール性を提供してくれない。</p> <p>そこでJsonnetなのである。詳細は <a href="http://qiita.com/yugui/items/f4a5e0e9dbd8ddffa48e">別記事</a> に譲るが、Jsonnetは<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>やINI形式の設定ファイルを生成するための純粋<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B4%D8%BF%F4%B7%BF%B8%C0%B8%EC">関数型言語</a>である。これを用いれば、たとえば運用環境ではログローテーションを合わせて設定するが開発環境ではしない、といった差異を抽象化しておいて複数のアプリケーションに一貫して適用することができる。最近試みているケースでは、あるアプリケーションの設定 <code>config</code> が与えられたとき、次のように書くと開発環境の設定を出力する</p> <pre class="code" data-lang="" data-unlink>util.environment.dev(config)</pre> <p>これを運用環境用に差し替えるには次のようにする。</p> <pre class="code" data-lang="" data-unlink>util.environment.live(config)</pre> <p>開発環境と運用環境の設定は同じ <code>config</code> から派生しているので、ユーティリティ関数が生成する差異を除いて両者が一貫していることが保証される。 また、 <code>util.environment.dev</code> や <code>util.environment.live</code> がしっかり書かれてテストされている限りにおいて、特定のアプリケーション用設定でうっかり運用環境用の何かを設定し忘れるということもない。</p> <p>さて、ここでユーティリティ関数群をしっかりテストしなければならないという問題が発生する。そこでJsonnet用のunit testライブラリ <a href="https://github.com/yugui/jsonnetunit">JsonnetUnit</a> を書いた。</p> <p>アプリケーションの実装をテストするのは当たり前だ。どうやって設定を適用するかをテストするのも当たり前だ。今後は、どんな設定が書かれるか、運用環境と開発環境で何が違っていて良いのかをテストしていきたい。</p> yugui 情報技術による変革の道のりを思う hatenablog://entry/8599973812317119006 2016-07-17T01:36:31+09:00 2017-11-12T18:06:35+09:00 祖父母と話していて、ちょっと調べて本を読んで考えれば分かることに対してなぜいつまでも堂々巡りの思考を巡らしているのだろうと思うことが良くあった。 学生時代に工場生産や農... <p>祖父母と話していて、ちょっと調べて本を読んで考えれば分かることに対してなぜいつまでも堂々巡りの思考を巡らしているのだろうと思うことが良くあった。 学生時代に工場生産や農業にかり出されてあまり教育を受けられなかったというのはあるが、それにしても祖父は戦後に大学を出ている。時代的な教育の問題だけとも考えづらい。</p> <p>しかし、よくよく考えてみるとちょっと調べるために私が使うのはインターネットの情報だ。書籍は書籍の価値があるので同様に情報源とする(今は過渡期で境界が曖昧とは言え、やはり書籍の形に手間暇掛けてまとめ上げた有料情報ならではの価値というのはある)が、それにしたって書籍に到達するのはweb上の書評経由であることも多い。入手経路も半数は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Amazon">Amazon</a>からだ。これらを通じて学んだ情報や考え方、情報の取捨選択の方法に基づいて私は考えている。</p> <p>これらはどれも祖父母が持っていない物だった。自分が何を知らないのか俯瞰しようとすれば、いくつもの紙の書籍を図書館で読むしかない。しかしこれらはちょっとした話題についての検索効率ではwebに満たない。司書に検索支援を頼もうにも我々がwebで検索するほど気軽にできることではないし、地方の図書館員に占める司書資格者は多くないし、そもそも足が悪くてはそうそう図書館にも行けない。webがなくては、webのみならず書籍や雑誌にすらろくにアクセスできない。情報を得るための情報や情報を抽出するための情報からも隔絶されていく。</p> <p>ほんの二十数年ほど前まで、インターネット技術は一般的ではなく、社会の殆どはこんな状況だった。知らない間に我々はずいぶん遠くまで来たようだ。</p> yugui APIデザインケーススタディ —— Rubyライブラリを移植する前に読む本 hatenablog://entry/8599973812317119012 2016-01-06T12:04:22+09:00 2017-11-12T22:40:47+09:00 『APIデザインケーススタディ』という本を頂戴したので読んでみた。この本はRuby標準ライブラリを題材にして、分かりやすく、多様な機能をサポートして、互換性を保つAPIの設計をする... <p>『 <a href="http://d.hatena.ne.jp/asin/4774178020/idm-22">APIデザインケーススタディ</a> 』という本を頂戴したので読んでみた。</p> <h1>ライブラリ作者に向けて</h1> <p>この本は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>標準ライブラリを題材にして、分かりやすく、多様な機能をサポートして、互換性を保つ<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>の設計をするにはどのように考えるべきかを教えてくれる。</p> <p>ここで<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>と言っているのは、一般的な<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>のクラスとオブジェクトとメソッドから成るライブラリをどうデザインするか、という話である。 別にChef Recipeや<a class="keyword" href="http://d.hatena.ne.jp/keyword/RSpec">RSpec</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>のようなちょっと変わった<a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>を設計するとかそういう話ではない。確かにその種の言語内<a class="keyword" href="http://d.hatena.ne.jp/keyword/DSL">DSL</a>のデザインには固有のセンスが必要とされるし、 <a href="http://clean-ruby.com/dsl">Ruby DSL Handbook</a> なんて本が書かれているように実装にあたってもある種のテクニックが必要なのも確かだ。でも、それ以外の「ふつう」のライブラリのデザインは果たして簡単だろうか。 適切な粒度のクラスを定義する。必要な機能に合わせてメソッドを定義する。メソッドの引数は何であるべきか。引数の順序は? 引数が多すぎて使うのが大変になっていないか。そのメソッド名は他の似た機能から類推可能だろうか。機能を追加するとき、どうやって<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B8%E5%CA%FD%B8%DF%B4%B9">後方互換</a>性を保てば良いのか。互換性を破ってでも改善すべきと判断するにあたっては何を考慮すれば良いのか。 本書が扱うのはそうした話題だ。</p> <p>ちなみに、著者の<a class="keyword" href="http://d.hatena.ne.jp/keyword/akr">akr</a>(田中哲)さんは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>ライブラリ設計の第一人者と言っても良いだろう。 長くに渡って<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>コアおよび標準添付ライブラリの開発を幅広く手がけており、今の<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>コミュニティにあるライブラリ設計の慣習を基礎づけた人々の1人だ。 中でも本書で題材に上がっている <code>IO</code> クラスや <code>Time</code> クラス、および <code>socket</code> ライブラリの設計において、いくつもの困難な課題を解決してきた。</p> <p>本書を読むことで題材になっている<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の標準ライブラリへの理解は深まるだろうし、自分で<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>ライブラリを書くときの考え方の参考にもなるだろう。 もっと一般に任意の言語でライブラリの<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>をデザインするときの助けにもなるだろう。</p> <h1>ライブラリ移植者に向けて</h1> <p>本書はまた、ライブラリを移植する物語として読むこともできる。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の標準ライブラリ、ことに <code>IO</code> や <code>socket</code> はC標準ライブラリや<a class="keyword" href="http://d.hatena.ne.jp/keyword/UNIX">UNIX</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%B9%A5%C6%A5%E0%A5%B3%A1%BC%A5%EB">システムコール</a>を意識してデザインされている。 よってこれらのライブラリをCから<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>への移植と捉えることもできるだろう。</p> <p>このようなライブラリの移植に当たっては、幾つものデザインの選択肢がある。一番単純なのは、元のライブラリの<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を引き写すことだろう。 利点としては元のライブラリを知っていれば移植先の学習が容易なことが挙げられる。それにデザインにあまり頭を使わなくて済む。 一方で、これは一般にあまり使いやすい<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>にはならない。 言語ごとに望ましいエラーの返し方も関数の命名慣習も異なるし、そもそも慣習を支える言語の特性自体が静的型付けの有り無しから真偽値の定義から何もかも違うのだ。</p> <p>その対極に当たるアプローチは、ほぼ同じ機能を提供するライブラリをゼロからデザインすることだ—— もはや「移植」とは言いがたいかもしれないが。 利点としては、もし適切にデザインできれば移植先の言語の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%ED%A5%B0%A5%E9%A5%DE">プログラマ</a>にとって理解しやすく使いやすい<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>が得られる。 一方で、移植元のライブラリデザインからの類推が働かないので学習コストがかえって高く付く可能性もある。</p> <p>では<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>は、<a class="keyword" href="http://d.hatena.ne.jp/keyword/akr">akr</a>さんは、この両極の間でどのような選択をしてきただろうか。本書はその歴史を語っている。</p> <h2>よりよい移植をめぐって</h2> <p>話は変わるが、私はこの数年というもの、業務では<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>以外の言語で開発をすることが多い。 もっぱら<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>や<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>やGo, 時には<a class="keyword" href="http://d.hatena.ne.jp/keyword/Java">Java</a>を触り、やむを得ず<a class="keyword" href="http://d.hatena.ne.jp/keyword/PHP">PHP</a>を触ることもある。</p> <p>これらの言語で開発する過程でしばしば<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>のライブラリや<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF">フレームワーク</a>、ツールから影響を受けたと思しきものを目にすることもあった。 ことに各所への<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>や<a class="keyword" href="http://d.hatena.ne.jp/keyword/ActiveRecord">ActiveRecord</a>の影響は著しいものがある。昨今では<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>が常にWebアプリケーション開発の第一選択肢とは限らなくなってきているのも、ひとえに<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>の良さが他の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF">フレームワーク</a>に広く受け入れられてもはや現代webアプリケーション<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF">フレームワーク</a>では当たり前の性質となっているからである。</p> <p>このように<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>コミュニティの成果が他から参考にされるのは誇らしくもあるが、一方でうんざりさせられることもある。 うんざりするのは、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>のライブラリや<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF">フレームワーク</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CE%F4%B2%BD%A5%B3%A5%D4%A1%BC">劣化コピー</a>を目にするときだ。</p> <p>たとえば<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%CE%F4%B2%BD%A5%B3%A5%D4%A1%BC">劣化コピー</a>である。 いまどき<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>に影響を受けた<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF">フレームワーク</a>は珍しくないが、さて、果たしてその<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>の機能は本当にその言語に向いているだろうか。 そもそも<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>は<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EB%A5%B9%A5%BF">フルスタ</a>ックかつ密結合な極端な<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF">フレームワーク</a>として登場した。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>には(当時のwebアプリケーションの)すべてが揃っていて、その構成要素が密に結合している代わりに「Railに乗っている」限りにおいては極小のコード量であらゆることが簡単にできた。 普通に考えれば構成要素が密結合なのは悪だが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>は密結合由来の難点をうまく補うような機能を提供していて、また<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>自体の柔軟性をもって(いざとなればモンキーパッチしてでも)問題を解決することができる。 あんなにも密結合な<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF">フレームワーク</a>がまともに使い物になること自体が信じがたいけれども、うまいバランスと<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の力で不思議と使いやすいのが<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>である。</p> <p>だとすれば、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>よりも柔軟性に劣る大抵の言語にとって<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>の採った選択は適切でない。また、「<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>のサブセットを移植した使いやすい<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF">フレームワーク</a>」というのも難しい。全体が揃ってうまくバランスが取れているのが<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>だからだ。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>よりも実行時の柔軟性に欠けるその言語には<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>よりもよい型安全性があるかもしれない。その言語では標準的なORマッパーが<a class="keyword" href="http://d.hatena.ne.jp/keyword/ActiveRecord">ActiveRecord</a>と異なるデザインかもしれない。それならば、おそらく<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>とは違うデザインが向いているはずだ。 それにも関わらず 「<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>をgoで書きました」「<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>を<a class="keyword" href="http://d.hatena.ne.jp/keyword/PHP">PHP</a>で書きました」という類のデザインにはうんざりさせられる。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>のactive_recordライブラリもまた多くのフォロワーを持つ。 しかし思い出して欲しいのは、そもそもの <a href="http://d.hatena.ne.jp/asin/4798105538/idm-22">PofEAA</a> ではActive Recordの適用領域は限定的だとされているし、active_recordライブラリはActive Recordパターンより遥かに多くのものを提供しているということだ。 active_recordライブラリやそれに依存する<a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>が複雑なアプリケーションを書くのにも使えるのは、そうした<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B3%C8%C4%A5%B5%A1%C7%BD">拡張機能</a>や様々な細かい配慮、定義済みのRakeタスクのお陰である。 これらなくして、ただactive_recordに影響を受けて<a class="keyword" href="http://d.hatena.ne.jp/keyword/ActiveRecord">ActiveRecord</a>パターンを複雑なアプリケーションに採用するのは無謀きわまる。</p> <p>さて、ではどうすれば良かったのだろうか。そのヒントを本書に見ることができるだろう。 本書は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>のライブラリは何を考えてデザインされているのかを解説している。またライブラリ移植の事例を豊富に提供している。 よって、何が<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>固有で、何かを移植するときは何を考慮しなければならないのかを学ぶのにうってつけである。</p> <p>ああ、Goのあのライブラリの、<a class="keyword" href="http://d.hatena.ne.jp/keyword/PHP">PHP</a>のあの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF">フレームワーク</a>の、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>のあのツールの作者が安易に<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>のやり方を流用する前に本書を読んでいてくれたら、あれらもまともに使える代物であったろうにと思わずにはいられないのだ。</p> yugui 次世代webカンファレンス hatenablog://entry/8599973812317119024 2015-09-18T14:15:55+09:00 2017-11-12T18:06:37+09:00 次世代webカンファレンスのserver_archセッションでお話することになった。 イベント趣旨にある「答えの出ていない問題を登壇者が話し合う」というのが面白そうであり、当日はどのよう... <p><a href="http://nextwebconf.connpass.com/event/19699/">次世代webカンファレンス</a> のserver_archセッションでお話することになった。 イベント趣旨にある「答えの出ていない問題を登壇者が話し合う」というのが面白そうであり、当日はどのように話が進むのか私も楽しみにしている。</p> <p>私としてはやはり最大の持ちネタはgRPCで、gRPCを使うようなサーバーサイド<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%AD%A5%C6%A5%AF%A5%C1%A5%E3">アーキテクチャ</a>はいかなる物なのかというところから切り込んでいくことになるだろう。</p> yugui Ruby-Jsonnet hatenablog://entry/8599973812317119037 2015-09-09T10:16:27+09:00 2017-11-12T18:06:38+09:00 最近Jsonnetを触っていて、とりあえず公式のC++ライブラリをラップしたRuby拡張ライブラリを書いてみた。 合わせて、QiitaにJsonnetの解説記事も書いてみた。 <p>最近 <a href="https://google.github.io/jsonnet/doc/">Jsonnet</a> という設定ファイル言語を触っていて、とりあえず公式の<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>ライブラリをラップした <a href="https://rubygems.org/gems/jsonnet">Ruby拡張ライブラリ</a> を書いてみた。 本当はpure-Go実装を書きたいのだが、当分先になりそうだ。</p> <p>合わせて、Qiitaに <a href="http://qiita.com/yugui/items/f4a5e0e9dbd8ddffa48e">Jsonnetの解説記事</a> も書いてみた。</p> yugui grpc-gateway機能ひとめぐり hatenablog://entry/8599973812317119056 2015-08-24T12:34:32+09:00 2017-11-12T18:06:40+09:00 次のような項目が設定できる。 HTTP request pathのパターン(+ HTTP method)とgRPCメソッドの対応関係 例では全部POSTで受けているが、任意のHTTPメソッドを利用できる Protocol Buffers messageのどの部... <p><a href="https://twitter.com/vvakame">@vvakame</a> さんが <a href="https://techbooster.github.io/c88/">TechBooster</a> の新刊"JavaScriptoon"の中でgRPCを解説していて、その中で <a href="https://github.com/gengo/grpc-gateway">grpc-gateway</a> にも触れている。これはとてもよい記事だったので、みんなこの本の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C5%C5%BB%D2%BD%F1%C0%D2">電子書籍</a>版を買えば良いと思う。 ただし、grpc-<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>は記事中で使われているだけで主題ではないので、すべてのトピックをカバーしてくれているわけではない。それは仕方が無いが、そろそろgrpc-<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>の機能を見渡す日本語記事が欲しいと思ったので自分で書くことにする。</p> <h1>grpc-<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>とは</h1> <p><a href="http://grpc.io">gRPC</a> (HTTP/2 + ProtocolBuffers)をwrapして古典的な<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a> (HTTP 1.1 + <a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>)を提供するリバースプロキシを生成するコード生成機だ。 <a href="http://yugui.jp/articles/889">別記事</a> にも書いた。</p> <h1>何ができるのか</h1> <p>gRPCで使うサービス定義(IDLみたいなやつ)を元にリバースプロキシの中核部分を実装する<a class="keyword" href="http://d.hatena.ne.jp/keyword/golang">golang</a>のHTTP handlerライブラリを生成する。 リバースプロキシは<a class="keyword" href="http://d.hatena.ne.jp/keyword/golang">golang</a>で書かれているが、これはwrapされるgRPCサービスとは別プロセスで動作するためサービス自体を<a class="keyword" href="http://d.hatena.ne.jp/keyword/golang">golang</a>で書く必要はない。<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>, <a class="keyword" href="http://d.hatena.ne.jp/keyword/Java">Java</a>, <a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>, <a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>, <a class="keyword" href="http://d.hatena.ne.jp/keyword/PHP">PHP</a>, <a class="keyword" href="http://d.hatena.ne.jp/keyword/C%23">C#</a>, Node.jsなどgRPCがサポートする任意の言語で書いて良い。</p> <p>では、生成されたレバースプロキシは何ができるのか。 基本的にはHTTP/1.1 リク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トをサービス定義ファイルに埋め込まれた情報に基づいて解釈し、gRPC用のリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トメッセージを構築したうえでgRPCエンドポイントに転送、 その後はエンドポイントからレスポンスを受け取ってから<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>に変換し直してクライアントに転送する。</p> <h2>Request parameter</h2> <p>もうちょっと詳しく見てみよう。まず、RPCのパラメータは次の3種類の形で受け付けて、3つを任意に組み合わせることもできる。</p> <ul> <li><p>Body parameter</p> <p>プロキシが受けたHTTP request bodyをProtocol Buffers messageの<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>表現として解釈し、そのmessageをgRPCエンドポイントに転送する gRPCに引き渡すmessageの一部分だけをrequest bodyから受け取ることもできる。その場合は、他の部分はpath parameterやquery parameterとして受け取る必要がある。</p> <p>勿論、HTTP methodがGETやDELETEなどの場合はbody parameterは受け取れないので、path parameterやquery parameterで受け取る必要がある。</p></li> <li><p>Path parameter</p> <p>プロキシが受けるHTTP request pathパターンの中に変数部分を埋め込んでおくことができる。この変数部分の値を対応するmessageのフィールドに反映してからgRPCエンドポイントに転送する</p></li> <li><p>Query parameter</p> <p>同じく、query stringをmessageのフィールドに反映する</p></li> </ul> <p>これらの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DE%A5%C3%A5%D4%A5%F3%A5%B0">マッピング</a>の詳細は、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>が公開している <a href="https://github.com/google/googleapis/blob/master/google/api/http.proto">google.api.http</a> というカスタムオプションを用いてサービス定義ファイルの中に指定する。下記はその例である。</p> <pre class="code lang-proto" data-lang="proto" data-unlink><span class="synStatement">service</span> EchoService { <span class="synStatement">rpc</span> Echo(SimpleMessage) <span class="synStatement">returns</span> (SimpleMessage) { <span class="synPreProc">option</span> (google.api.http) = { post: <span class="synConstant">&quot;/v1/example/echo/{id}&quot;</span> }; } <span class="synStatement">rpc</span> EchoBody(SimpleMessage) <span class="synStatement">returns</span> (SimpleMessage) { <span class="synPreProc">option</span> (google.api.http) = { post: <span class="synConstant">&quot;/v1/example/echo_body&quot;</span> body: <span class="synConstant">&quot;*&quot;</span> }; } } </pre> <p>次のような項目が設定できる。</p> <ul> <li><p>HTTP request pathのパターン(+ HTTP method)とgRPCメソッドの対応関係</p> <ul> <li>例では全部POSTで受けているが、任意のHTTPメソッドを利用できる</li> </ul> </li> <li><p>Protocol Buffers messageのどの部分を(または全部を)bodyとして受け取るのか</p> <ul> <li>例の <code>EchoBody</code> では <code>SimpleMessage</code> 内の全fieldをbodyで受けている</li> </ul> </li> <li><p>Path pattern内の可変部分とmessage fieldの対応</p> <ul> <li>例の <code>Echo</code> ではパスの最後にある可変部分を <code>SimpleMessage</code> の <code>id</code> フィールドに対応させている</li> </ul> </li> <li><p>Bodyやpathで受け付けなかったパラメータはすべて省略可能のquery parameterであると仮定される</p></li> </ul> <h2>Header</h2> <p>gRPCはリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トを受け取る際にリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トメッセージ本体の他にmetadataという名前=値ペアの列を受け取ることができる。 これをHTTP/1.1で提供するため、以下のようにHTTP headerをmetadataに変換する</p> <ul> <li>Authorizationヘッダ: そのままAuthorization metadataとして転送する</li> <li>Grpc-metadata-(varname)ヘッダ: (varname) metadataとして転送する</li> </ul> <p>ところで今 <a href="http://tools.ietf.org/html/rfc2617">RFC 2617</a> を見ていたらWWW-Authenticateヘッダを完全に忘れていたことに気がついたので、あとでサポートした方が良いかもしれない。</p> <h2>Streaming</h2> <p>gRPCにはstreamingという機能がある。1つリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トを受け取って1つレスポンスを返す代わりに、リク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トのストリームやレスポンスのストリームを受け付けるものだ。 更に言うと次の3パターンがある。</p> <ul> <li>client streaming: 徐々に、あるいは散発的に送られてくるリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トメッセージの列が完了してからレスポンスを1つ返す</li> <li>server streaming: リク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トを1つ受け取ってから徐々に、あるいは散発的にレスポンスの列を返す</li> <li>bidirectional streaming: リク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トとレスポンスを任意のタイミングで任意回送り合う</li> </ul> <p>grpc-<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>は最初の2つをサポートし、メッセージの列は<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a> Streamとして表現される。 bidirectionalなstreamingも限定的にサポートしているが、すべてのリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トを受け取ってからレスポンスを返し始めるという使い方しかできない。本当にbidirectionalにしようと思ったらWebSocketsにugpradeとかしなければならないだろう。現在はそこまでする予定はない。</p> <p>なお、client streamingとbidirectional streamingではすべてのリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トパラメータをbodyで渡す必要がある。リク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トメッセージが複数個である以上、特定のリク<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%B9">エス</a>トメッセージに紐付かない形のパラメータは解釈が曖昧になるからだ。</p> <h2>エラー処理</h2> <p>gRPCサービスがエラーを返した場合はエラーコードを適切なHTTP response statusに対応させて返す。またエラー時のresponse bodyにはgRPCサービスが返したエラーメッセージを含む<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a> objectが入っている。</p> yugui Automation Account Pattern hatenablog://entry/8599973812317119067 2015-07-01T12:49:59+09:00 2017-11-12T22:38:24+09:00 ContextThe service you are developing provides APIs for users. When an user calls an API, (s)he must show a short-living authorization credential, e.g. an OAuth2 token.The user creates, owns, updates or deletes resources in the service on behalf of th... <h1>Context</h1> <p>The service you are developing provides APIs for users. When an user calls an <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>, (s)he must show a short-living authorization credential, e.g. an OAuth2 token.</p> <p>The user creates, owns, updates or deletes resources in the service on behalf of the account to which the credential is associated. Here, the `user' might be (an user-agent operated by) a human but also it might be an automated program running in background.</p> <p>Also you want to use <a class="keyword" href="http://d.hatena.ne.jp/keyword/captcha">captcha</a> to prevent huge number of accounts from being registered for some fraud purpose.<br/> And you might even want to let users register their billing address for charging.</p> <h1>Problem</h1> <p>It is hard for users to securely manage accounts which automated programs use.</p> <p>"manage" here basically means to create/update credentials as necessary and handle bills. A human (or a specialized billing-system, at least) must handle these things instead of the automated program because such a program tends to be bad at solving <a class="keyword" href="http://d.hatena.ne.jp/keyword/captcha">captcha</a> -- it is by design and raison d'être of <a class="keyword" href="http://d.hatena.ne.jp/keyword/captcha">captcha</a> --, and it is even worse at receiving bills by mail and going to bank to pay.</p> <p>Then, how can (s)he manage the account for the program? Although some easy solutions come to the mind of the user, they do not work fine enough.</p> <ul> <li><p>Reuse the account of the human for the program</p> <p>Individual members in the team which manage the program can leave the team.</p> <p>So suppose that the owner of the account leaves the team. Then the resources owned by the person can be deleted, or <a class="keyword" href="http://d.hatena.ne.jp/keyword/access">access</a> to the resources can be restricted. At least, it would get hard to manage authentication credentials of the account.</p></li> <li><p>Create an account only for the program</p> <p>In this approach, (s)he creates a new account for the program in the same way as (s)he creates an account for human. Then the person <a class="keyword" href="http://d.hatena.ne.jp/keyword/lets">lets</a> the program use the new account. This account is shared by the team. So even if someone leaves the team, someone else in the team can manage the account.</p> <p>There are, however, some scenarios in which this approach will not work fine. First, members in the team must share the authentication credential, e.g. passwords, to manage the account. But it is hard to track usage of the credential for audit. Second, even though it would get harder to keep the authentication credential secret as the team gets larger and the team needs to update the credential when necessary, it would also get harder to notify the update to all members in the team. Finally, even if they solve the issues above, it still tends to happen that someone in the team creates an account but (s)he leaves the team without sharing the account with others. Then none can manage the account.</p> <p>To make things worse, they need to keep the account for the program having the minimum rights for security. It means that they will have more number of accounts per usage and the issues above becomes more serious.</p></li> </ul> <h2>Examples</h2> <h3>1. <a class="keyword" href="http://d.hatena.ne.jp/keyword/Github">Github</a> - <a class="keyword" href="http://d.hatena.ne.jp/keyword/Travis">Travis</a> integration</h3> <ul> <li><p>System</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a></p></li> <li><p>Resource</p> <p>private repositories</p></li> <li><p>Account</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a> account</p></li> <li><p>short-living authorization credential</p> <p>OAuth2 token issued by <a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a></p></li> <li><p>Authentication information to manage the account</p> <p>Password of the <a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a> account</p></li> <li><p>Automated Program</p> <p>Build processes running on <a class="keyword" href="http://d.hatena.ne.jp/keyword/Travis">Travis</a> CI.</p></li> </ul> <h3>2. <a class="keyword" href="http://d.hatena.ne.jp/keyword/Cookpad">Cookpad</a> - <a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a></h3> <ul> <li><p>System</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Amazon%20Web%20Services">Amazon Web Services</a></p></li> <li><p>Resource</p> <p>S3 bucket</p></li> <li><p>Account</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a> account</p></li> <li><p>short-living authorization credential</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/access">access</a>-key</p></li> <li><p>Authentication information to manage the account</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a> password</p></li> <li><p>Authomated program</p> <p>The feature that allows users of <a class="keyword" href="http://d.hatena.ne.jp/keyword/Cookpad">Cookpad</a> to upload their pictures of dishes.</p></li> </ul> <h1>Solution</h1> <ol> <li><p>Define another kind of accounts ( <strong>Automation Account</strong> ), which is only for automated programs.</p></li> <li><p>Distinguish this kind of accounts from ordinal accounts for humans.</p></li> <li><p>Do not assign any passwords to automation accounts. Instead, associate the automation account to a list of human accounts who can manage the automation account.</p></li> </ol> <p>Humans in the list just need to login to the system with their own human accounts when they manage the automation account. So they don't need to share the password of the automation account. Moreover, the automation account does not need to have its own password.</p> <p><a href="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-automation-account.png"><img alt="Diagram of Automation Account" src="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-automation-account.png" width="318" height="155" /></a></p> <h2>Implementatinos</h2> <h3>1. Service Accounts in <a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> Cloud Platform (<a class="keyword" href="http://d.hatena.ne.jp/keyword/GCP">GCP</a>)</h3> <ul> <li>Most of resources provided by APIs in <a class="keyword" href="http://d.hatena.ne.jp/keyword/GCP">GCP</a> belong to a unit called "project".</li> <li><p>There are two kinds of accounts: ordinal (human) accounts and service accounts</p> <ul> <li>User accounts for humans (<a class="keyword" href="http://d.hatena.ne.jp/keyword/Gmail">Gmail</a> accounts or accounts of <a class="keyword" href="http://d.hatena.ne.jp/keyword/Google%20Apps">Google Apps</a> for Work) are independent from projects, and those accounts can join arbitrary number of projects as read-only, writable or owners.</li> <li>On the other hand, service accounts are accounts only for automated programs. A service account uniquely belongs to a project. And when the project is deleted, the service account is also deleted together with other resources in the project.</li> </ul> </li> <li><p>Since service accounts do not have passwords for login, you can not use service accounts when you log into web pages but you can use them only for <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a> calls with their OAuth2 tokens. Also you have to use your own human account and the the account must be an owner of the project when you manage the service account.</p></li> <li>Billing information is registered per project. Any operations on resources in a project are charged to the billing information of the project.</li> </ul> <p><a href="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-service-account.png"><img alt="Diagram of Service Account" src="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-service-account.png" width="350" height="187" /></a></p> <h3>2. <a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a> Identity and <a class="keyword" href="http://d.hatena.ne.jp/keyword/Access">Access</a> Management (IAM)</h3> <ul> <li>Resources provided by APIs in <a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a> belong to an <a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a> user account (root account).</li> <li><p>You can create arbitrary number of IAM accounts under the root account.</p> <ul> <li>You can assign an IAM account to human, but also you can assign an IAM account to an automated program without giving any password to the account.</li> </ul> </li> <li><p>You have to use the root account or an IAM account for human when you need to login to the web console to manage IAM accounts.</p></li> <li>Billing information is registered per root account. Any operations done by IAM accounts are charged to their root accounts.</li> </ul> <p><a href="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-iam.png"><img alt="Diagram of Service Account" src="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-iam.png" width="348" height="193" /></a></p> <p>Let's compare the two implementations.</p> <p>The approach of <a class="keyword" href="http://d.hatena.ne.jp/keyword/GCP">GCP</a> is a bit more complicated for hobby developers because <a class="keyword" href="http://d.hatena.ne.jp/keyword/GCP">GCP</a> requires the developer to create a "project" in addition to his/her human account even though (s)he is the only developer in the development team. On the other hand, (s)he would be able to do everything just with his/her root accounts in <a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a>.</p> <p>Next, let's think about larger development projects. <a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a> is a bit less secure because root account itself is an account with username and password and the dev team must keep the account secure in addition to their IAM accounts. On the other hand, <a class="keyword" href="http://d.hatena.ne.jp/keyword/GCP">GCP</a> is secure because it does not have this kind of extra authentication credential which is not usually used.</p> yugui Automation Accountパターン hatenablog://entry/8599973812317119082 2015-06-07T11:02:02+09:00 2017-11-12T22:37:29+09:00 コンテキストあなたが開発しているサービスはユーザー向けにAPIを提供している。そして、APIを利用するにはユーザーは短寿命の認可情報(たとえばOAuth2トークン)を提示しなければならな... <h1>コンテキスト</h1> <p>あなたが開発しているサービスはユーザー向けに<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を提供している。そして、<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を利用するにはユーザーは短寿命の認可情報(たとえばOAuth2<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ン)を提示しなければならない。</p> <p>ユーザーは認可情報が紐付いているアカウントの権限でリソースを読み取ったり、作成したり、所有したり、編集または削除したりする。 ここで、ユーザーは人間(が操作するユーザーエージェント)であることもあるが、人間の手を離れてバックグラウンドで自動実行されるプログラムかもしれない。</p> <p>また、あなたは悪用目的でアカウントが大量登録されるのを防ぐために<a class="keyword" href="http://d.hatena.ne.jp/keyword/Captcha">Captcha</a>を利用したいと思っている。 さらに、課金目的で請求書送付先を登録させたいとも思っているかもしれない。</p> <h1>問題</h1> <p>プログラムが利用するアカウントを安全に管理するのがユーザーにとって困難である。</p> <p>プログラムは自分で<a class="keyword" href="http://d.hatena.ne.jp/keyword/Captcha">Captcha</a>を解いたりEメールを受け取るのが苦手だし(そうでないと<a class="keyword" href="http://d.hatena.ne.jp/keyword/Captcha">Captcha</a>の意味がない)、 また郵便物で請求書を受け取って銀行に振り込みに行くのは更に苦手である。 よって、プログラムが利用するアカウントを作成し、管理し、対応する請求書を処理するのは人間(か、少なくともそれ専用の別のシステム)でなければならない。</p> <p>ではその人間はどのようにそのプログラムが利用するアカウントを管理すれば良いのか。幾つかの簡単な解決策を思いつくが、いずれも十分とは言えない。</p> <ul> <li><p>人間のアカウントを使い回す</p> <p>プログラムを管理しているチームの誰かのアカウントを使う、という解決法。</p> <p>しかし、個々の人間は自動プログラムを管理ししているチームを離れることがある。このアカウントを所有していた人物がいなくなると、 アカウントの元に所有されていたリソースは消滅したり、アクセスが制限されたりする可能性がある。 最低でも、そのアカウントの認可情報を更新するのは困難となる。</p></li> <li><p>プログラム専用のアカウントを共有する</p> <p>人間用のアカウントを作るのと同様にして、人間がプログラム専用のアカウントを作成する。そしてそのアカウントをプログラムに利用させる、という解決法。 アカウントは自動プログラムを管理する人間たちによって共有されており、たとえ誰かがチームから居なくなっても 他の者がアカウントの管理、認可情報の更新などを行える。</p> <p>しかし、これもまた十分とは言えない。人間たちはアカウントを管理するためのパスワードなどの認証情報を共有しなければならないが、 その周知の情報をいつ誰が利用したのか監査記録を付けるのは大きなチームでは難しい。 また大きくなるほど認証情報漏洩のリスクは高まって適時の更新が必要とされるが、大きくなるほど更新したという事実を全員に伝えるのも難しい。 苦労してこれらの問題を解決したところで、誰かが開発用にアカウントを作ったが良いが周知するのを忘れたままチームを去って、結局誰も管理できないアカウントになったりするのもありがちな問題である。</p> <p>更に、セキュリティ上はプログラム専用アカウントは最小限の権限を持つのが望ましいが、最小化するために権限ごとにアカウントを設定すると 管理すべきアカウントの数が増えて管理にまつわる問題は酷くなっていく。</p></li> </ul> <h2>具体例</h2> <h3>1. <a class="keyword" href="http://d.hatena.ne.jp/keyword/Github">Github</a> - <a class="keyword" href="http://d.hatena.ne.jp/keyword/Travis">Travis</a>連携</h3> <ul> <li><p>システム</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a></p></li> <li><p>リソース</p> <p>プライベー<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%EA%A5%DD%A5%B8">トリポジ</a>トリ</p></li> <li><p>アカウント</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a>アカウント</p></li> <li><p>短寿命認可情報</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a>のOAuth2 token</p></li> <li><p>管理のための認証情報</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/github">github</a>のパスワード</p></li> <li><p>自動化プログラム</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/travis">travis</a>によるビルドプロセス</p></li> </ul> <h3>2. <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%C3%A5%AF%A5%D1%A5%C3%A5%C9">クックパッド</a> - <a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a></h3> <ul> <li><p>システム</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Amazon%20Web%20Services">Amazon Web Services</a></p></li> <li><p>リソース</p> <p>S3 bucket</p></li> <li><p>アカウント</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a>アカウント</p></li> <li><p>短寿命認可情報</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a>アクセスキー</p></li> <li><p>管理のための認証情報</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a>パスワード</p></li> <li><p>自動化プログラム</p> <p>料理の画像をアップロードする機能</p></li> </ul> <h1>解決策</h1> <p>自動化プログラム用のアカウント種別(Automation Account)を作って、人間用のアカウントとは区別する。 また、Automation Accountには管理用のパスワードを割り当てず、代わりに「このAutomation Accountを管理できる人間用アカウントのリスト」を対応させておく。</p> <p>人間たちは自分自身の人間用アカウントでログインするだけで(リストに載っていれば)Automation Accountを管理できる。 このため、Automation Accountのパスワードを共有する必要はない。 それどころか、プログラム用のアカウントはどうせ人間の誰かが自分のアカウントで管理するのだからAutomation Accountにはパスワードがなくても差し支えない。</p> <p><a href="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-automation-account.png"><img alt="Automation Accountの図解" src="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-automation-account.png" width="424" height="206" /></a></p> <h2>実現例</h2> <h3>1. <a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> Cloud Platform (<a class="keyword" href="http://d.hatena.ne.jp/keyword/GCP">GCP</a>)のサービスアカウント</h3> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/GCP">GCP</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>が提供するほとんどのリソースは「プロジェクト」という単位に所属する。</p> <p>ユーザーアカウントはプロジェクトとは独立したアカウント(<a class="keyword" href="http://d.hatena.ne.jp/keyword/Gmail">Gmail</a>アカウントまたは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google%20Apps">Google Apps</a> for Workアカウント)で、 任意の数のプロジェクトに読み取り専用、書き込み可、またはオーナーとして参加できる。</p> <p>これに対してサービスアカウントはプログラム専用のアカウントで、ただ1つのプロジェクトに一意に属し、プロジェクトが削除されると他のリソースと同様サービスアカウントも削除される。</p> <p>サービスアカウントはログイン用のパスワードを持たないためWebページのログインには利用できず、OAuth2<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A1%BC%A5%AF">トーク</a>ンを用いてプログラムから<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を呼ぶためにしか利用できない。 サービスアカウントを管理するには、そのプロジェクトのオーナーになっている人間のアカウントを利用する。</p> <p>課金先情報はプロジェクトごとに登録管理される。サービスアカウントであれ一般のユーザーアカウントであれ、プロジェクト内のリソースに対する操作はプロジェクトに対して課金される。</p> <p><a href="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-service-account.png"><img alt="Service Accountの図解" src="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-service-account.png" width="408" height="220" /></a></p> <h3>2. <a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a> Identity and <a class="keyword" href="http://d.hatena.ne.jp/keyword/Access">Access</a> Management (IAM)</h3> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>が提供するリソースは特定の<a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a>ユーザーアカウント(rootアカウント)に属する。</p> <p>権限を分割するために、ユーザーアカウント内に任意個のIAMアカウントを作成できる。 IAMアカウントはパスワードを設定して人間に割り振ることもできるし、パスワードを与えないでおいてプログラムに利用させることもできる。 プログラム用のIAMアカウントを管理するためにwebコンソールにアクセスする場合は、パスワードを割り振られた人間用のIAMアカウントまたはrootアカウントを利用する。</p> <p>課金情報はrootアカウントごとに登録管理される。IAMアカウントが利用したリソースはその属するrootアカウントに対して課金される。</p> <p><a href="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-iam.png"><img alt="IAMの図解" src="http://assets.blog.yugui.jp.storage.googleapis.com/images/20150607-iam.png" width="397" height="220" /></a></p> <p>2つの実現例を比べてみよう。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/GCP">GCP</a>のアプローチは一個人が趣味で開発しているような小規模開発においても、開発者のアカウントの他にプロジェクトという単位を作成する必要があるのでやや煩雑である。 一方<a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a>のアプローチは個人の小規模開発であれば人間用にはIAMアカウントを発行せず、rootアカウントだけですべてを済ませることも可能なので単純である。</p> <p>大規模な場合に目を移すと、<a class="keyword" href="http://d.hatena.ne.jp/keyword/AWS">AWS</a>のアプローチはrootアカウントそれ自体がユーザー名とパスワードを持つアカウントであるため、守るべき対象が1つ多い分だけほんの少しセキュアでない。 またそもそもこのrootアカウントのパスワードをどう管理するかという点において上記と類似の問題が残されたままである。 これに対して<a class="keyword" href="http://d.hatena.ne.jp/keyword/GCP">GCP</a>では、この通常使われることのない余分な認証情報はそもそも存在しないのでセキュアである。</p> yugui grpc-gateway更新 hatenablog://entry/8599973812317119098 2015-05-04T12:44:16+09:00 2017-11-12T18:06:45+09:00 先日公開したgrpc-gatewayだが、gRPCのメーリングリストで宣伝したらGoogleからフィードバックがあったので対応した。実はGoogleは社内にgrpc-gatewayに似た仕組みを持っていて、RESTful APIとgRPCの... <p><a href="http://yugui.jp/articles/889">先日公開した</a> grpc-<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>だが、gRPCの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E1%A1%BC%A5%EA%A5%F3%A5%B0%A5%EA%A5%B9%A5%C8">メーリングリスト</a>で宣伝したら<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>からフィードバックがあったので対応した。</p> <p>実は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>は社内にgrpc-<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>に似た仕組みを持っていて、RESTful <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>とgRPCの変換にはそれを使っている。 んで、今回<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>はその変換の設定に使っている<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AD%A1%BC%A5%DE">スキーマ</a>を <a href="https://github.com/google/googleapis/tree/master/google/api">https://github.com/google/googleapis/tree/master/google/api</a> に公開してくれたのだ。</p> <p>これを受けてgrpc-<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>独自のカスタムオプションを捨てて<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>が公開したオプションを使うことにした( <a href="https://github.com/gengo/grpc-gateway/pull/12">grpc-gateway#12</a> )。これには幾つか理由がある。</p> <ul> <li><p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>の長年の経験を踏まえて設計された語彙のほうが、私がとりあえずプロトタイプとして適当に設計した語彙より拡張性と十分性において信頼に足る</p> <ul> <li>実際、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>のカスタムオプションのほうが表現できる幅は広い</li> </ul> </li> <li><p>似たような<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AD%A1%BC%A5%DE">スキーマ</a>がいくつもあるとユーザーとっては不便であるから統一した方が良い。</p> <ul> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>の既存資産を変更させて統一するよりは、こっちのプロトタイプ段階の<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>を変更した方が早く確実に統一できる。</li> </ul> </li> </ul> <p>なお、古い独自オプションのサポートはバッサリ切り捨てた。</p> <p>それから、HTTP Request pathのテンプレートの文法もちょっと複雑になって従来の <a href="https://github.com/zenazn/goji">goji</a> のルーティング設定に頼る方式では限界が来たので、 独自にtemplate parserと実行時にマッチするためのスタックマシンを書いた。 ASTそのままで<a class="keyword" href="http://d.hatena.ne.jp/keyword/Interpreter">Interpreter</a>パターンすれば十分かとも思ったのだが、よく考えたらparseするのは<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>のコードを生成するときで、パターン照合が走るのは<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>のコードが実行される時だ。 よってparse結果を何らかの形で<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%BD%A1%BC%A5%B9%A5%B3%A1%BC%A5%C9">ソースコード</a>として保存しなければならないのだが、ASTを表すGoの式を生成しつつ将来の拡張もできる符号化とかも考えると面倒になってきたので スタックマシンのopcodeとして符号化することにしてしまった。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/Rails">Rails</a>の経験からもルーティングは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DC%A5%C8%A5%EB%A5%CD%A5%C3%A5%AF">ボトルネック</a>になりがちなので、この意味でもASTを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BA%C6%B5%A2">再帰</a>的に辿るよりはopcodeでloop + switchのほうが良いかもしれぬ。</p> yugui gRPC-JSON proxy hatenablog://entry/8599973812317119111 2015-04-07T04:15:43+09:00 2017-11-12T22:35:36+09:00 grpc-gatewayというgRPCからJSON APIへの変換プロキシ生成機を書いた。 これを使えば社内のmicroservicesはgRPCで通信しつつ公開APIはJSON APIで提供する、みたいなことが簡単になる。背景gRPCの良い... <p><a href="https://github.com/gengo/grpc-gateway">grpc-gateway</a> という <a href="https://github.com/grpc/grpc-common">gRPC</a> から<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>への変換プロキシ生成機を書いた。</p> <p>これを使えばシステム内部ののmicroservicesはgRPCで通信しつつ公開<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>は<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>で提供する、みたいなことが簡単になる。</p> <p>なお、gRPCそのものについては <a href="http://mattn.kaoriya.net/software/lang/go/20150227144125.htm">mattnさんの記事</a> が参考になる。</p> <h1>背景</h1> <p>gRPCの良い点はいくつもある。</p> <ul> <li>データはデフォルトでprotocol buffersで直列化される。ベストではないにせよ十分にコンパクト且つ高速だし、サイズで言えば<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>とは比べるべくもない。</li> <li>簡単に複数の言語でサーバーのテンプレートやクライアントを生成できる。通信の詳細はgRPCにまかせて開発者はサーバーロジックの実装に注力できる。</li> <li>design by <a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>という安心感。</li> </ul> <p>gRPCの素晴らしさは認めるものの、一方では欠点もある。まず、クライアントライブラリの多くはCで書かれたバイナリ拡張を含む。</p> <p>これは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>界ではあまり歓迎されていないし、多くのユーザーに<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を使ってもらうということを考えればどうしたってクライアントは利用言語ネイティブで書かれていた方が良い。</p> <p>また、そもそもgRPCはまだそんなに普及していない。gRPCがサポートしていない言語だってなくはない。</p> <p>だからコン<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C8%A5%ED%A1%BC%A5%EB">トロール</a>の効く範囲のシステム内部の通信にgRPCを使うのは極めて妥当な選択だが、現時点で公開<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>をgRPCだけで提供するっていう決断は<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>提供者としてはなかなか勇気が要る。</p> <p>だったら公開<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>は<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>のままでいきたいというのは自然な発想だ。</p> <p>gRPCの<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>シ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%A2%A5%E9">リアラ</a>イゼーション機能を使っても良いのだけど、それでもHTTP 2.0必須だし、呼び出しパスもちょっと独特なのでちょっとまだ敷居が高い。</p> <p>そこで、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%E2%A5%C7%A5%A3%A5%C6%A5%A3">コモディティ</a>としての従来型のRESTful <a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a> over HTTP 1.xがやっぱり必要なわけだ。grpc-<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>はgRPCで書いたサービスを簡単にそうしたRESTful <a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>に変換してくれる。</p> <h1>実装</h1> <p>grpc-<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a>はgRPCそのものと同じく、protoc (protocol buffers compiler)の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>として実装されている。</p> <p>gRPCサービスを定義してある.protoファイルに専用のカスタムオプションでちょっとだけ情報を足すと、protoc-gen-grpc-<a class="keyword" href="http://d.hatena.ne.jp/keyword/gateway">gateway</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>がgRPCと<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を仲立ちするreverse proxyを生成してくれるようになる。</p> <p>カスタムオプションには次のような情報を指定できる</p> <ul> <li>path: このメソッド呼び出しを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DE%A5%C3%A5%D4%A5%F3%A5%B0">マッピング</a>するHTTP requestのpath</li> <li>method: 同じくHTTP method</li> <li>description: 備考</li> </ul> <p>生成されるproxyは<a class="keyword" href="http://d.hatena.ne.jp/keyword/golang">golang</a>だが、バックエンドのgRPCサービスとはgRPCで通信さえ出来れば良い。よってgRPCサービスのほうは普段通り好きな言語で書けば良い。</p> <p><del>現時点ではgRPCのstreaming機能はサポートしていないが、これはすぐにサポートする予定だ。</del><ins>サポートした</ins></p> <p>そのほかにもREADME.mdに書いてあるようないくつかの機能をサポートしていきたいと思っている。</p> <p>中でもSwagger <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a> definitionの出力は是非対応したいポイントだ。protoから<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a> definitionは作るからあとは<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a> clientはswaggerで勝手に作れ、という風にしたい。</p> yugui Managed VMsに移行 hatenablog://entry/8599973812317119117 2015-03-30T04:54:38+09:00 2017-11-12T18:06:48+09:00 今までこのblogはVPS上で動作させてきたが、この度Managed VMsに移行した。 Wikiと全文検索は一時的に利用不能になっているが、他はそのまま動作しているはずだ。主な動機はいまどき個人blo... <p>今までこのblogは<a class="keyword" href="http://d.hatena.ne.jp/keyword/VPS">VPS</a>上で動作させてきたが、この度 <a href="https://cloud.google.com/appengine/docs/managed-vms/">Managed VMs</a> に移行した。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/Wiki">Wiki</a>と<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C1%B4%CA%B8%B8%A1%BA%F7">全文検索</a>は一時的に利用不能になっているが、他はそのまま動作しているはずだ。</p> <p>主な動機はいまどき個人blogのためだけにシステムを管理するのは厭ということである。 慣れているのと安いのでManaged VMsにしたが、システムをDockerizeしてあるので、必要であれば少しの手間でElastic Beanstalkでもどこでも移動できる。</p> <p>難点としては、このblogには<a class="keyword" href="http://d.hatena.ne.jp/keyword/VM">VM</a>がオーバースペックということだ。Managed VMs(elastic beanstalkも同じだった筈だが)はdockerコンテナとそれを走らせる<a class="keyword" href="http://d.hatena.ne.jp/keyword/VM">VM</a>を1:1にmapするので、この程度の負荷だとリソースが余ってしまう。複数のコンテナ(モジュール)でシステムを構成しようとすると<a class="keyword" href="http://d.hatena.ne.jp/keyword/VM">VM</a>の利用料がかなり無駄である。本当はN:Mにマップして5個ぐらいに分割したモジュールを2個ぐらいの<a class="keyword" href="http://d.hatena.ne.jp/keyword/VM">VM</a>で走らせたいんだけど。</p> <p>そのへんの柔軟性についてはKubernetesを使いたかったが今回は諦めた。 というのは、Kubernetes<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%B9%A5%BF">クラスタ</a>を走らせたままKubernetes管理<a class="keyword" href="http://d.hatena.ne.jp/keyword/daemon">daemon</a>をバージョンアップする仕組みがまだできていないので、本業ならともかく片手間に管理するには<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DF%A5%C9%A5%EB%A5%A6%A5%A7%A5%A2">ミドルウェア</a>の更新コストが高いためだ。 いちいち<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%B9%A5%BF">クラスタ</a>をもう一個立ち上げて、システムをre-deployして<a class="keyword" href="http://d.hatena.ne.jp/keyword/DNS">DNS</a>を更新して古いやつを消すのは面倒すぎる。 この問題( <a href="https://github.com/GoogleCloudPlatform/kubernetes/issues/6075">kubernetes#6075</a> )もいずれは解決するであろうから、そのときは <a href="https://cloud.google.com/container-engine/">Google Container Engine</a> に再度移行するだろう。</p> yugui 言語処理100本ノックを敢えてRubyで (3) hatenablog://entry/8599973812317119132 2015-03-24T12:56:03+09:00 2017-11-12T18:06:49+09:00 20. JSONデータの読み込みrequire 'json' uk = File.foreach("jawiki-country.json"). map(&amp;JSON.method(:parse)). find {|article| article['title'] == 'イギリス' } raise "no article about イギリス" unless uk puts uk['text'] 感覚的なものだけど、この場合は&amp;obj.method(:mid)形... <p>引き続き、 <a href="http://www.cl.ecei.tohoku.ac.jp/nlp100/">言語処理100本ノック</a> を敢えて<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>で解いている。</p> <p>第3章は<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>がテーマだが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>はそこまで得意ではないので辛い。マッチの効率とか深く考えていない。</p> <h1>20. <a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>データの読み込み</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">require</span> <span class="synSpecial">'</span><span class="synConstant">json</span><span class="synSpecial">'</span> uk = <span class="synType">File</span>.foreach(<span class="synSpecial">&quot;</span><span class="synConstant">jawiki-country.json</span><span class="synSpecial">&quot;</span>). map(&amp;<span class="synType">JSON</span>.method(<span class="synConstant">:parse</span>)). find {|<span class="synIdentifier">article</span>| article[<span class="synSpecial">'</span><span class="synConstant">title</span><span class="synSpecial">'</span>] == <span class="synSpecial">'</span><span class="synConstant">イギリス</span><span class="synSpecial">'</span> } <span class="synStatement">raise</span> <span class="synSpecial">&quot;</span><span class="synConstant">no article about イギリス</span><span class="synSpecial">&quot;</span> <span class="synStatement">unless</span> uk puts uk[<span class="synSpecial">'</span><span class="synConstant">text</span><span class="synSpecial">'</span>] </pre> <p>感覚的なものだけど、この場合は <code>&amp;obj.method(:mid)</code> 形式の簡潔さが見慣れない分のデメリットを上回る気がした。</p> <h1>21. カテゴリ名を含む行を抽出</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synType">File</span>.foreach(<span class="synSpecial">'</span><span class="synConstant">uk.txt</span><span class="synSpecial">'</span>).grep(<span class="synSpecial">/\[\[</span><span class="synConstant">Category:</span><span class="synSpecial">/</span>) <span class="synStatement">do</span> |<span class="synIdentifier">line</span>| puts line <span class="synStatement">end</span> </pre> <p>この辺は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>のほうが<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>より<a class="keyword" href="http://d.hatena.ne.jp/keyword/sed">sed</a>や<a class="keyword" href="http://d.hatena.ne.jp/keyword/awk">awk</a>の影響が色濃く残っていて簡潔に書けるかなぁ。</p> <h1>22. カテゴリ名の抽出</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>pattern = <span class="synSpecial">/</span> <span class="synConstant"> </span><span class="synSpecial">\[\[</span> <span class="synConstant"> Category:</span> <span class="synConstant"> </span><span class="synSpecial">([^</span><span class="synConstant">|</span><span class="synSpecial">\]]+)</span> <span class="synConstant"> </span><span class="synSpecial">(?:\|[^\]]*)?</span> <span class="synConstant"> </span><span class="synSpecial">\]\]</span> <span class="synSpecial">/x</span> <span class="synType">File</span>.read(<span class="synSpecial">&quot;</span><span class="synConstant">uk.txt</span><span class="synSpecial">&quot;</span>).scan(pattern) <span class="synStatement">do</span> |<span class="synIdentifier">cat</span>,| puts cat <span class="synStatement">end</span> </pre> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>がとても読みづらくなったのでせめて <code>x</code> オプションで複数行に分けてみた。</p> <h1>23. セクション構造</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synType">File</span>.foreach(<span class="synSpecial">&quot;</span><span class="synConstant">uk.txt</span><span class="synSpecial">&quot;</span>) <span class="synStatement">do</span> |<span class="synIdentifier">line</span>| <span class="synStatement">next</span> <span class="synStatement">unless</span> m = <span class="synSpecial">/^(</span><span class="synConstant">=</span><span class="synSpecial">{2,})\s*([^</span><span class="synConstant">=</span><span class="synSpecial">]+)\s*\1/o</span>.match(line) puts <span class="synSpecial">&quot;</span><span class="synConstant">level=</span><span class="synSpecial">#{</span>m[<span class="synConstant">1</span>].size - <span class="synConstant">1</span><span class="synSpecial">}</span><span class="synConstant">, name=</span><span class="synSpecial">#{</span>m[<span class="synConstant">2</span>]<span class="synSpecial">}&quot;</span> <span class="synStatement">end</span> </pre> <h1>24. ファイル参照の抽出</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>pattern = <span class="synSpecial">/</span> <span class="synConstant"> </span><span class="synSpecial">\[\[</span> <span class="synConstant"> File:</span> <span class="synConstant"> </span><span class="synSpecial">([^</span><span class="synConstant">|</span><span class="synSpecial">\]]+)</span> <span class="synConstant"> </span><span class="synSpecial">(?:\|[^\]]*)*</span> <span class="synConstant"> </span><span class="synSpecial">\]\]</span> <span class="synSpecial">/x</span> <span class="synType">File</span>.read(<span class="synSpecial">&quot;</span><span class="synConstant">uk.txt</span><span class="synSpecial">&quot;</span>).scan(pattern) <span class="synStatement">do</span> |<span class="synIdentifier">cat</span>,| puts cat <span class="synStatement">end</span> </pre> <p>Categoryの場合とほとんど変わらないと思うんだが、出題意図を読み違えてないよね?</p> <h1>25. テンプレートの抽出</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">def</span> <span class="synIdentifier">params</span> templatePattern = <span class="synSpecial">/</span> <span class="synConstant"> {{基礎情報</span><span class="synSpecial">\s+</span><span class="synConstant">国</span><span class="synSpecial">\s+</span> <span class="synConstant"> </span><span class="synSpecial">(?&lt;params&gt;</span> <span class="synConstant"> </span><span class="synSpecial">(?&lt;markup&gt;</span> <span class="synConstant"> {{</span><span class="synSpecial">\g&lt;markup&gt;</span><span class="synConstant">}} </span><span class="synSpecial">|</span> <span class="synConstant"> </span><span class="synSpecial">[^</span><span class="synConstant">{}</span><span class="synSpecial">]*</span> <span class="synConstant"> </span><span class="synSpecial">)*</span> <span class="synConstant"> </span><span class="synSpecial">)</span> <span class="synConstant"> }}</span> <span class="synConstant"> </span><span class="synSpecial">/x</span> article = <span class="synType">File</span>.read(<span class="synSpecial">&quot;</span><span class="synConstant">uk.txt</span><span class="synSpecial">&quot;</span>) <span class="synStatement">raise</span> <span class="synSpecial">&quot;</span><span class="synConstant">no 基礎情報 found</span><span class="synSpecial">&quot;</span> <span class="synStatement">unless</span> m = templatePattern.match(article) base = m[<span class="synConstant">:params</span>] <span class="synType">Hash</span>.new.tap {|<span class="synIdentifier">result</span>| base.split(<span class="synSpecial">/^\|/</span>).each <span class="synStatement">do</span> |<span class="synIdentifier">entry</span>| <span class="synStatement">next</span> <span class="synStatement">if</span> entry.empty? name, value = entry.split(<span class="synSpecial">/\s*</span><span class="synConstant">=</span><span class="synSpecial">\s*/</span>, <span class="synConstant">2</span>) result[name] = value.chomp <span class="synStatement">end</span> } <span class="synPreProc">end</span> <span class="synStatement">if</span> <span class="synIdentifier">$0</span> == <span class="synConstant">__FILE__</span> p params <span class="synStatement">end</span> </pre> <p>String#scanで切り出すのとnamed captureはあまり相性が良くないことが判明。 最初に基礎情報を取り出す部分は田中哲<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%DA%A5%B7%A5%E3">スペシャ</a>ルのおかげで簡潔に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%BA%C6%B5%A2">再帰</a>構造をサポートできている。</p> <p>一方、テンプレートパラメータの抽出は<a class="keyword" href="http://d.hatena.ne.jp/keyword/MediaWiki">MediaWiki</a>の文法を正確に表してはいない。だが、今回はパラメータ区切りは常に行頭から始まるのでとりあえず十分である。</p> <h1>26. 強調<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DE%A1%BC%A5%AF%A5%A2%A5%C3%A5%D7">マークアップ</a>の除去</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">require_relative</span> <span class="synSpecial">'</span><span class="synConstant">25.rb</span><span class="synSpecial">'</span> <span class="synPreProc">def</span> <span class="synIdentifier">params2</span> <span class="synType">Hash</span>.new.tap {|<span class="synIdentifier">result</span>| params.each <span class="synStatement">do</span> |<span class="synIdentifier">name</span>, <span class="synIdentifier">value</span>| result[name] = value.gsub(<span class="synSpecial">/(</span><span class="synConstant">'</span><span class="synSpecial">{2,3}|</span><span class="synConstant">'</span><span class="synSpecial">{5})([^</span><span class="synConstant">'</span><span class="synSpecial">]+)\1/</span>, <span class="synSpecial">'</span><span class="synConstant">\2</span><span class="synSpecial">'</span>) <span class="synStatement">end</span> } <span class="synPreProc">end</span> <span class="synStatement">if</span> <span class="synIdentifier">$0</span> == <span class="synConstant">__FILE__</span> p params2 <span class="synStatement">end</span> </pre> <h1>27. 内部リンクの除去</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">require_relative</span> <span class="synSpecial">'</span><span class="synConstant">26.rb</span><span class="synSpecial">'</span> <span class="synPreProc">def</span> <span class="synIdentifier">params3</span> <span class="synType">Hash</span>.new.tap {|<span class="synIdentifier">result</span>| params2.each <span class="synStatement">do</span> |<span class="synIdentifier">name</span>, <span class="synIdentifier">value</span>| result[name] = value.gsub(<span class="synSpecial">/\[\[</span><span class="synConstant"> </span><span class="synSpecial">([^\|\]</span><span class="synConstant">:</span><span class="synSpecial">]+)</span><span class="synConstant"> </span><span class="synSpecial">(?:\|([^\]]+))?</span><span class="synConstant"> </span><span class="synSpecial">\]\]/x</span>) <span class="synStatement">do</span> <span class="synIdentifier">$2</span> || <span class="synIdentifier">$1</span> <span class="synStatement">end</span> <span class="synStatement">end</span> } <span class="synPreProc">end</span> <span class="synStatement">if</span> <span class="synIdentifier">$0</span> == <span class="synConstant">__FILE__</span> p params3 <span class="synStatement">end</span> </pre> <h1>28. <a class="keyword" href="http://d.hatena.ne.jp/keyword/MediaWiki">MediaWiki</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DE%A1%BC%A5%AF%A5%A2%A5%C3%A5%D7">マークアップ</a>の除去</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">require_relative</span> <span class="synSpecial">'</span><span class="synConstant">27.rb</span><span class="synSpecial">'</span> <span class="synPreProc">def</span> <span class="synIdentifier">params4</span> <span class="synType">Hash</span>.new.tap {|<span class="synIdentifier">result</span>| params3.each <span class="synStatement">do</span> |<span class="synIdentifier">name</span>, <span class="synIdentifier">value</span>| value = value.gsub(<span class="synSpecial">/</span><span class="synConstant">{{</span><span class="synSpecial">([^\|</span><span class="synConstant">}</span><span class="synSpecial">]+)(?:\|([^\|</span><span class="synConstant">}</span><span class="synSpecial">]+)?)*</span><span class="synConstant">}}</span><span class="synSpecial">/</span>) { <span class="synIdentifier">$2</span> || <span class="synIdentifier">$1</span> } value = value.gsub(<span class="synSpecial">%r!</span><span class="synConstant">&lt;ref</span><span class="synSpecial">(?:\s[^</span><span class="synConstant">/&gt;</span><span class="synSpecial">]+)?</span><span class="synConstant">&gt;</span><span class="synSpecial">.*?</span><span class="synConstant">&lt;/ref&gt;</span><span class="synSpecial">!m</span>, <span class="synSpecial">''</span>) value = value.gsub(<span class="synSpecial">%r!</span><span class="synConstant">&lt;ref</span><span class="synSpecial">(?:\s[^</span><span class="synConstant">/&gt;</span><span class="synSpecial">]+)?</span><span class="synConstant">/&gt;</span><span class="synSpecial">!</span>, <span class="synSpecial">''</span>) value = value.gsub(<span class="synSpecial">%r!</span><span class="synConstant">&lt;</span><span class="synSpecial">[[:alpha:]]+\s*</span><span class="synConstant">/&gt;</span><span class="synSpecial">!</span>, <span class="synSpecial">''</span>) value = value.gsub(<span class="synSpecial">%r!</span><span class="synConstant">&amp;pound;</span><span class="synSpecial">!</span>, <span class="synSpecial">'</span><span class="synConstant">£</span><span class="synSpecial">'</span>) result[name] = value <span class="synStatement">end</span> } <span class="synPreProc">end</span> <span class="synStatement">if</span> <span class="synIdentifier">$0</span> == <span class="synConstant">__FILE__</span> p params4 <span class="synStatement">end</span> </pre> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/MediaWiki">MediaWiki</a>記法は結構いろいろあるのでやり出すとキリが無い。とりあえずこんな感じだろうか。 遊びでやるにはそろそろ辛くなってきた。</p> <p>実務でやるなら、目的を満たす程度のminimalな<a class="keyword" href="http://d.hatena.ne.jp/keyword/MediaWiki">MediaWiki</a> parserを書いてちゃんとunit testを書くべきだろう。</p> <h1>29. 国旗画像のURLを取得する</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">require</span> <span class="synSpecial">'</span><span class="synConstant">json</span><span class="synSpecial">'</span> <span class="synPreProc">require</span> <span class="synSpecial">'</span><span class="synConstant">open-uri</span><span class="synSpecial">'</span> <span class="synPreProc">require_relative</span> <span class="synSpecial">'</span><span class="synConstant">28.rb</span><span class="synSpecial">'</span> name = params4[<span class="synSpecial">'</span><span class="synConstant">国旗画像</span><span class="synSpecial">'</span>] url = <span class="synSpecial">&quot;</span><span class="synConstant">http://ja.wikipedia.org/w/api.php?format=json&amp;action=query&amp;continue=&amp;titles=Image:%s&amp;prop=imageinfo&amp;iiprop=url</span><span class="synSpecial">&quot;</span> % <span class="synType">URI</span>.escape(name) result = <span class="synType">JSON</span>.parse(open(url) {|<span class="synIdentifier">f</span>| f.read }) puts result[<span class="synSpecial">'</span><span class="synConstant">query</span><span class="synSpecial">'</span>][<span class="synSpecial">'</span><span class="synConstant">pages</span><span class="synSpecial">'</span>][<span class="synSpecial">'</span><span class="synConstant">-1</span><span class="synSpecial">'</span>][<span class="synSpecial">'</span><span class="synConstant">imageinfo</span><span class="synSpecial">'</span>].first[<span class="synSpecial">'</span><span class="synConstant">url</span><span class="synSpecial">'</span>] </pre> <p>むぅ。<a class="keyword" href="http://d.hatena.ne.jp/keyword/MediaWiki">MediaWiki</a> <a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>ってのがあるのか。</p> yugui 言語処理100本ノックを敢えてRubyで (2) hatenablog://entry/8599973812317119156 2015-03-22T12:46:16+09:00 2017-11-12T18:06:54+09:00 10. 行数のカウントp $&lt;.count !sh wc hightemp.txt $&lt;の仕様はまさにこういう処理を書くために考えられている。 が、今回に関しては普通ならわざわざ書かずにwcを使う。11. タブをスペースに置換$&lt;.each_line do |line| puts line.gsub(/\t/, ' ') end !sh sed "s/^I/ /g" hightemp.txt 12. 1列目をcol1.txtに,2列目をcol2.txtに保... <h1>10. 行数のカウント</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>p <span class="synIdentifier">$&lt;</span>.count </pre> <p>確認</p> <pre class="code lang-sh" data-lang="sh" data-unlink>wc hightemp.txt </pre> <p><code>$&lt;</code> の仕様はまさにこういう処理を書くために考えられている。 が、今回に関しては普通ならわざわざ書かずにwcを使う。</p> <h1>11. タブをスペースに置換</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synIdentifier">$&lt;</span>.each_line <span class="synStatement">do</span> |<span class="synIdentifier">line</span>| puts line.gsub(<span class="synSpecial">&quot;\t&quot;</span>, <span class="synSpecial">'</span><span class="synConstant"> </span><span class="synSpecial">'</span>) <span class="synStatement">end</span> </pre> <p>確認</p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synStatement">sed</span> <span class="synStatement">&quot;</span><span class="synConstant">s/^I/ /g</span><span class="synStatement">&quot;</span> hightemp.txt </pre> <h1>12. 1列目をcol1.txtに,2列目をcol2.txtに保存</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synType">File</span>.open(<span class="synSpecial">&quot;</span><span class="synConstant">col1.txt</span><span class="synSpecial">&quot;</span>, <span class="synSpecial">&quot;</span><span class="synConstant">w</span><span class="synSpecial">&quot;</span>) {|<span class="synIdentifier">f1</span>| <span class="synType">File</span>.open(<span class="synSpecial">&quot;</span><span class="synConstant">col2.txt</span><span class="synSpecial">&quot;</span>, <span class="synSpecial">&quot;</span><span class="synConstant">w</span><span class="synSpecial">&quot;</span>) {|<span class="synIdentifier">f2</span>| <span class="synIdentifier">$&lt;</span>.each_line <span class="synStatement">do</span> |<span class="synIdentifier">line</span>| cols = line.split(<span class="synSpecial">&quot;\t&quot;</span>) f1.puts cols[<span class="synConstant">0</span>] f2.puts cols[<span class="synConstant">1</span>] <span class="synStatement">end</span> } } </pre> <p>確認</p> <pre class="code lang-sh" data-lang="sh" data-unlink>cut <span class="synSpecial">-f1</span> hightemp.txt cut <span class="synSpecial">-f2</span> hightemp.txt </pre> <p>普通で面白くない。 今回はデータが小さいことが分かっているので、オンメモリに構築してから <code>File::write</code> で書いた方がインデントは少なくなって綺麗だろう。 複数のファイルを同じ寿命で開くことはままあるので、複数まとめて開いてまとめて閉じるラッパーメソッドを書いたことはある。</p> <p>こうしてみると<a class="keyword" href="http://d.hatena.ne.jp/keyword/golang">golang</a>の <code>defer</code> がちょとうらやましいなぁ。 <code>Object.instance_eval</code> で似たようなのを構築するのは易しいが、 <code>self</code> がすり替わってしまうし完全ではない。</p> <h1>13. col1.txtとcol2.txtをマージ</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synType">File</span>.open(<span class="synSpecial">&quot;</span><span class="synConstant">col1.txt</span><span class="synSpecial">&quot;</span>) {|<span class="synIdentifier">f1</span>| <span class="synType">File</span>.open(<span class="synSpecial">&quot;</span><span class="synConstant">col2.txt</span><span class="synSpecial">&quot;</span>) {|<span class="synIdentifier">f2</span>| f1.zip(f2).each <span class="synStatement">do</span> |<span class="synIdentifier">col1</span>, <span class="synIdentifier">col2</span>| puts <span class="synSpecial">&quot;#{</span>col1.chomp<span class="synSpecial">}\t#{</span>col2<span class="synSpecial">}&quot;</span> <span class="synStatement">end</span> } } </pre> <p>性能的に見ると <code>Enumerable.zip</code> は <code>Array</code> を返すのでメモリを食ってよろしくない。今回はデータが小さいから良いものの、 <code>lazy</code> を使っても全般的に<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>はこの手の処理が苦手である。 この辺の失敗体験がStreemにつながっているんだろうか。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>paste col1.txt col2.txt </pre> <h1>14. 先頭からN行を出力</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>n = <span class="synIdentifier">ARGV</span>.shift.to_i <span class="synIdentifier">$&lt;</span>.each_with_index <span class="synStatement">do</span> |<span class="synIdentifier">line</span>, <span class="synIdentifier">i</span>| <span class="synStatement">break</span> <span class="synStatement">if</span> i &gt;= n puts line <span class="synStatement">end</span> </pre> <p>別解。こちらのほうが今風だ。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink>n = <span class="synIdentifier">ARGV</span>.shift.to_i <span class="synIdentifier">$&lt;</span>.lazy.take(n).each{|<span class="synIdentifier">line</span>| puts line } </pre> <p>確認</p> <pre class="code lang-sh" data-lang="sh" data-unlink>head <span class="synConstant">-10</span> hightemp.txt </pre> <h1>15. 末尾のN行を出力</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">class</span> <span class="synType">Ring</span> <span class="synPreProc">include</span> <span class="synType">Enumerable</span> <span class="synType">ABSENT</span> = <span class="synType">Object</span>.new.freeze <span class="synPreProc">def</span> <span class="synIdentifier">initialize</span>(n) <span class="synIdentifier">@buf</span> = <span class="synType">Array</span>.new(n, <span class="synType">ABSENT</span>) <span class="synIdentifier">@pos</span> = <span class="synConstant">0</span> <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">add</span>(item) <span class="synIdentifier">@buf</span>[<span class="synIdentifier">@pos</span>] = item <span class="synIdentifier">@pos</span> += <span class="synConstant">1</span> <span class="synIdentifier">@pos</span> %= <span class="synIdentifier">@buf</span>.size <span class="synPreProc">end</span> <span class="synPreProc">def</span> <span class="synIdentifier">each</span> (<span class="synConstant">0</span>...<span class="synIdentifier">@buf</span>.size).each <span class="synStatement">do</span> |<span class="synIdentifier">i</span>| item = <span class="synIdentifier">@buf</span>[(i + <span class="synIdentifier">@pos</span>) % <span class="synIdentifier">@buf</span>.size] <span class="synStatement">next</span> <span class="synStatement">if</span> item == <span class="synType">ABSENT</span> <span class="synStatement">yield</span> item <span class="synStatement">end</span> <span class="synPreProc">end</span> <span class="synPreProc">end</span> n = <span class="synIdentifier">ARGV</span>.shift.to_i ring = <span class="synType">Ring</span>.new(n) <span class="synIdentifier">$&lt;</span>.each(&amp;ring.method(<span class="synConstant">:add</span>)) ring.each <span class="synStatement">do</span> |<span class="synIdentifier">line</span>| puts line <span class="synStatement">end</span> </pre> <p>まじめに後方からバッファリングしつつEOLを探索すると面倒いので先頭からなめた。 <kbd>tail</kbd> (1)の実装は結構うまくできていて、まねしようとするとそれなりに難しい。</p> <p>あと、 <code>&amp;obj.method(:mid)</code> は見慣れない割には簡潔でないので実務では避けるべき。</p> <p><code>Ring</code> もあまりまじめな実装ではない。</p> <p>そういえば、Ring bufferが標準ライブラリに入っていて気軽に使えると割と便利なことが<a class="keyword" href="http://d.hatena.ne.jp/keyword/golang">golang</a>の経験で分かった。 しかし、今からコンテナを<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の標準添付ライブラリに足す線はなかなか無いだろうなぁ。</p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synStatement">tail</span> <span class="synConstant">-10</span> hightemp.txt </pre> <h1>16. ファイルをN分割する</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>n = <span class="synIdentifier">ARGV</span>.shift.to_i lines = <span class="synIdentifier">$&lt;</span>.to_a per_chunk = <span class="synStatement">if</span> lines.size % n == <span class="synConstant">0</span> lines.size / n <span class="synStatement">else</span> lines.size / n + <span class="synConstant">1</span> <span class="synStatement">end</span> suffix = <span class="synSpecial">'</span><span class="synConstant">aa</span><span class="synSpecial">'</span> lines.each_slice(per_chunk) <span class="synStatement">do</span> |<span class="synIdentifier">chunk</span>| <span class="synType">File</span>.open(<span class="synSpecial">&quot;</span><span class="synConstant">x</span><span class="synSpecial">#{</span>suffix<span class="synSpecial">}&quot;</span>, <span class="synSpecial">&quot;</span><span class="synConstant">w</span><span class="synSpecial">&quot;</span>) {|<span class="synIdentifier">f</span>| chunk.each <span class="synStatement">do</span> |<span class="synIdentifier">line</span>| f.puts line <span class="synStatement">end</span> } suffix.succ! <span class="synStatement">end</span> </pre> <p>出力ファイル名は <kbd>split</kbd> (1)のデフォルトに合わせた。</p> <p>「 <kbd>split</kbd> (1)で実現せよ」という設問については、<a class="keyword" href="http://d.hatena.ne.jp/keyword/BSD">BSD</a>の <kbd>split</kbd> には <kbd>-n</kbd> が無いから難しいんだが。</p> <pre class="code lang-sh" data-lang="sh" data-unlink>gsplit <span class="synSpecial">-n</span> <span class="synConstant">5</span> hightemp.txt </pre> <h1>17. 1列目の文字列の異なり</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>puts <span class="synIdentifier">$&lt;</span>.map {|<span class="synIdentifier">line</span>| line.split(<span class="synSpecial">&quot;\t&quot;</span>)[<span class="synConstant">0</span>] }.uniq </pre> <p>確認</p> <pre class="code lang-sh" data-lang="sh" data-unlink>cut <span class="synSpecial">-f1</span> hightemp.txt | <span class="synStatement">sort</span> | uniq </pre> <h1>18. 各行を3コラム目の数値の降順にソート</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synIdentifier">$&lt;</span>.map {|<span class="synIdentifier">line</span>| line.split(<span class="synSpecial">&quot;\t&quot;</span>) }.sort_by {|<span class="synIdentifier">_</span>, <span class="synIdentifier">_</span>, <span class="synIdentifier">temp</span>, <span class="synIdentifier">_</span>| -temp.to_f }.each <span class="synStatement">do</span> |<span class="synIdentifier">data</span>| puts data.join(<span class="synSpecial">&quot;\t&quot;</span>) <span class="synStatement">end</span> </pre> <p>確認</p> <pre class="code lang-sh" data-lang="sh" data-unlink><span class="synStatement">sort</span> <span class="synSpecial">-rnk3</span> hightemp.txt </pre> <h1>19. 各行の1コラム目の文字列の出現頻度を求め,出現頻度の高い順に並べる</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>freq = <span class="synType">Hash</span>.new(<span class="synConstant">0</span>) <span class="synIdentifier">$&lt;</span>.map {|<span class="synIdentifier">line</span>| pref, *rest = line.split(<span class="synSpecial">&quot;\t&quot;</span>) freq[pref] += <span class="synConstant">1</span> [pref, *rest] }.sort_by {|<span class="synIdentifier">pref</span>,| [-freq[pref], pref] }.each <span class="synStatement">do</span> |<span class="synIdentifier">data</span>| puts data.join(<span class="synSpecial">&quot;\t&quot;</span>) <span class="synStatement">end</span> </pre> <p>同じ出現頻度が多くて分かりづらい。あと、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C5%D4%C6%BB">都道</a>府県でグルーピングもしてみた。</p> <p>shellコマンドでの確認指示については、こういう意図かなぁ?</p> <pre class="code lang-sh" data-lang="sh" data-unlink>cut <span class="synSpecial">-f1</span> hightemp.txt | <span class="synStatement">sort</span> | uniq <span class="synSpecial">-c</span> | <span class="synStatement">sort</span> <span class="synSpecial">-rnk1</span> <span class="synSpecial">-t</span><span class="synStatement">'</span><span class="synConstant"> </span><span class="synStatement">'</span> </pre> yugui 言語処理100本ノックを敢えてRubyで (1) hatenablog://entry/8599973812317119187 2015-03-22T10:43:37+09:00 2017-11-12T18:07:00+09:00 言語処理100本ノックを(飽きるまで)やってみるにあたり、敢えてRubyで書いてみる。基本的にはPythonを想定しているらしいし、そもそもNLPライブラリの充実度から言ってもPythonを使うのが極めて妥当な選択といえるだろう。そこを敢えてRubyで。00. 文字列の逆順puts "stressed".reverse RubyでもPythonでも大差ない。01. 「パタトクカシーー」str = "パタトクカシーー" puts 1.step(7, 2).map{|i| str[i]}.join ... <p><a href="http://www.cl.ecei.tohoku.ac.jp/nlp100/">言語処理100本ノック</a> を(飽きるまで)やってみるにあたり、敢えて<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>で書いてみる。</p> <p>基本的には<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>を想定しているらしいし、そもそも<a class="keyword" href="http://d.hatena.ne.jp/keyword/NLP">NLP</a>ライブラリの充実度から言っても<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>を使うのが極めて妥当な選択といえるだろう。そこを敢えて<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>で。</p> <h1>00. 文字列の逆順</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>puts <span class="synSpecial">&quot;</span><span class="synConstant">stressed</span><span class="synSpecial">&quot;</span>.reverse </pre> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>でも<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>でも大差ない。</p> <h1>01. 「パタトクカシーー」</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>str = <span class="synSpecial">&quot;</span><span class="synConstant">パタトクカシーー</span><span class="synSpecial">&quot;</span> puts <span class="synConstant">1</span>.step(<span class="synConstant">7</span>, <span class="synConstant">2</span>).map{|<span class="synIdentifier">i</span>| str[i]}.join </pre> <p>やはりスライスにstepを指定できる<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>のほうが楽である。</p> <h1>02. 「パトカー」+「タクシー」=「パタトクカシーー」</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>strs = <span class="synSpecial">%w[</span><span class="synConstant">パトカー タクシー</span><span class="synSpecial">]</span> puts strs.map(&amp;<span class="synConstant">:chars</span>).inject(&amp;<span class="synConstant">:zip</span>).flatten.join </pre> <p>別解</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink>strs = <span class="synSpecial">%w[</span><span class="synConstant">パトカー タクシー</span><span class="synSpecial">]</span> puts strs.map(&amp;<span class="synConstant">:chars</span>).transpose.flatten.join </pre> <p><code>zip</code> と <code>transpose</code> と <code>flatten</code> を基本装備している<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>に死角はなかった。</p> <p>しかし、interleave操作ってのは割と使うのでArrayにあっても良い気はするけど、そう思う人は自分で定義できるのが<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の良いところである。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/ActiveSupport">ActiveSupport</a>みたいな外部ライブラリを通じて「そう思う人」が一般的であることやライブラリデザインの善し悪しが実証されれば、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>本体に取り込まれることもある。ここでも登場している <code>Symbol.to_proc</code> がその好例である。</p> <p>ただ、 <a href="http://d.hatena.ne.jp/nurse/20141107#1415355181">RubyのStringが「文字の列」でない</a> のが地味に辛い。 StringがEnumerableでないのは、「何を単位としてiterateしたいのかは場合によるからcodepoints, chars, linesなどを明示的に使おう」「昔はlinesがデフォルトだったけど、少なくともこれは違うんでは?」「文字列を文字の列として扱うのが基本であるかのような風潮を助長するのは良くない」などが複合していると思われる。</p> <h1>03. 円周率</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>sentence = <span class="synSpecial">&quot;</span><span class="synConstant">Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics.</span><span class="synSpecial">&quot;</span> p sentence.split(<span class="synSpecial">/\W+/</span>).map(&amp;<span class="synConstant">:size</span>) </pre> <h1>04. <a class="keyword" href="http://d.hatena.ne.jp/keyword/%B8%B5%C1%C7%B5%AD%B9%E6">元素記号</a></h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink>sentence = <span class="synSpecial">&quot;</span><span class="synConstant">Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can.</span><span class="synSpecial">&quot;</span> words = sentence.split(<span class="synSpecial">/\W+/</span>) result = {} len = -&gt;(i){ [<span class="synConstant">0</span>, <span class="synConstant">4</span>, <span class="synConstant">5</span>, <span class="synConstant">6</span>, <span class="synConstant">7</span>, <span class="synConstant">8</span>, <span class="synConstant">14</span>, <span class="synConstant">15</span>, <span class="synConstant">18</span>].include?(i) ? <span class="synConstant">1</span> : <span class="synConstant">2</span> } words.each_with_index <span class="synStatement">do</span> |<span class="synIdentifier">word</span>, <span class="synIdentifier">i</span>| key = word[<span class="synConstant">0</span>, len[i]] result[key] = i+<span class="synConstant">1</span> <span class="synStatement">end</span> p result </pre> <p>リスト内包表記がある分<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>のほうが綺麗にいくだろうか。 あと、出題通りだと"Might"から"Mi"を切り出すことになるけど良いの?</p> <h1>05. <a class="keyword" href="http://d.hatena.ne.jp/keyword/n-gram">n-gram</a></h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">module</span> <span class="synType">Enumerable</span> <span class="synPreProc">def</span> <span class="synIdentifier">ngram</span>(n) each_cons(n).to_a <span class="synPreProc">end</span> <span class="synPreProc">end</span> <span class="synStatement">if</span> <span class="synIdentifier">$0</span> == <span class="synConstant">__FILE__</span> str = <span class="synSpecial">&quot;</span><span class="synConstant">I am an NLPer</span><span class="synSpecial">&quot;</span> p str.chars.ngram(<span class="synConstant">2</span>) p str.split(<span class="synSpecial">/\W+/</span>).ngram(<span class="synConstant">2</span>) <span class="synStatement">end</span> </pre> <p>文字bi-gramのほうは空白は無視した方が良かったんだろうか。</p> <h1>06. 集合</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">require_relative</span> <span class="synSpecial">&quot;</span><span class="synConstant">05.rb</span><span class="synSpecial">&quot;</span> strs = <span class="synSpecial">%w[</span><span class="synConstant">paraparaparadise paragraph</span><span class="synSpecial">]</span> x = strs[<span class="synConstant">0</span>].chars.ngram(<span class="synConstant">2</span>) y = strs[<span class="synConstant">1</span>].chars.ngram(<span class="synConstant">2</span>) puts <span class="synSpecial">&quot;</span><span class="synConstant">union:</span><span class="synSpecial">\t#{</span>x | y<span class="synSpecial">}&quot;</span> puts <span class="synSpecial">&quot;</span><span class="synConstant">intersection:</span><span class="synSpecial">\t#{</span>x &amp; y<span class="synSpecial">}&quot;</span> puts <span class="synSpecial">&quot;</span><span class="synConstant">diff:</span><span class="synSpecial">\t#{</span>x - y<span class="synSpecial">}&quot;</span> puts <span class="synSpecial">&quot;</span><span class="synConstant">X includes 'se'?: </span><span class="synSpecial">#{</span>x.include?(<span class="synSpecial">%w[</span><span class="synConstant">s e</span><span class="synSpecial">]</span>)<span class="synSpecial">}&quot;</span> puts <span class="synSpecial">&quot;</span><span class="synConstant">Y includes 'se'?: </span><span class="synSpecial">#{</span>y.include?(<span class="synSpecial">%w[</span><span class="synConstant">s e</span><span class="synSpecial">]</span>)<span class="synSpecial">}&quot;</span> </pre> <p>Arrayクラスが集合演算を持っているのでそのままである。実用的にはSetを使うべきかもしれない。</p> <h1>07. テンプレートによる文生成</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">def</span> <span class="synIdentifier">run_template</span>(x, y, z) <span class="synSpecial">&quot;#{</span>x<span class="synSpecial">}</span><span class="synConstant">時の</span><span class="synSpecial">#{</span>y<span class="synSpecial">}</span><span class="synConstant">は</span><span class="synSpecial">#{</span>z<span class="synSpecial">}&quot;</span> <span class="synPreProc">end</span> puts run_template(<span class="synConstant">12</span>, <span class="synSpecial">&quot;</span><span class="synConstant">気温</span><span class="synSpecial">&quot;</span>, <span class="synConstant">22.4</span>) </pre> <p>やっぱり引数が短い場合は文字列interpolationのほうが <code>String::%</code> より便利である。</p> <p>ちなみに<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の <code>String::%</code> は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Python">Python</a>が元ネタだったはず。</p> <h1>08. 暗号文</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synType">CIPHER_TABLE</span> = (<span class="synConstant">?a</span> .. <span class="synConstant">?z</span>).map{|<span class="synIdentifier">c</span>| (<span class="synConstant">219</span> - c.ord).chr }.join.freeze <span class="synPreProc">def</span> <span class="synIdentifier">cipher</span>(str) str.tr(<span class="synSpecial">'</span><span class="synConstant">a-z</span><span class="synSpecial">'</span>, <span class="synType">CIPHER_TABLE</span>) <span class="synPreProc">end</span> <span class="synStatement">if</span> <span class="synIdentifier">$0</span> == <span class="synConstant">__FILE__</span> sentence= &lt;&lt;-<span class="synSpecial">EOS</span> <span class="synConstant">He then led me to the frame, about the sides, whereof all his pupils stood in ranks. It was twenty feet square, placed in the middle of the room. The superfices was composed of several bits of wood, about the bigness of a die, but some larger than others. They were all linked together by slender wires. These bits of wood were covered, on every square, with paper pasted on them; and on these papers were written all the words of their language, in their several moods, tenses, and declensions; but without any order. The professor then desired me “to observe; for he was going to set his engine at work.” The pupils, at his command, took each of them hold of an iron handle, whereof there were forty fixed round the edges of the frame; and giving them a sudden turn, the whole disposition of the words was entirely changed. He then commanded six-and-thirty of the lads, to read the several lines softly, as they appeared upon the frame; and where they found three or four words together that might make part of a sentence, they dictated to the four remaining boys, who were scribes. This work was repeated three or four times, and at every turn, the engine was so contrived, that the words shifted into new places, as the square bits of wood moved upside down.</span> <span class="synConstant"> </span><span class="synSpecial">EOS</span> puts cipher(sentence) <span class="synStatement">raise</span> <span class="synStatement">unless</span> cipher(cipher(sentence)) == sentence <span class="synStatement">end</span> </pre> <h1>09. Typoglycemia</h1> <pre class="code lang-ruby" data-lang="ruby" data-unlink><span class="synPreProc">def</span> <span class="synIdentifier">randomize</span>(str) str.split(<span class="synSpecial">/</span><span class="synConstant"> </span><span class="synSpecial">/</span>).map {|<span class="synIdentifier">word</span>| <span class="synStatement">if</span> word.size &lt;= <span class="synConstant">4</span> word <span class="synStatement">else</span> [word[<span class="synConstant">0</span>], word[<span class="synConstant">1</span>..<span class="synConstant">-2</span>].chars.shuffle, word[<span class="synConstant">-1</span>]].flatten.join <span class="synStatement">end</span> }.join(<span class="synSpecial">'</span><span class="synConstant"> </span><span class="synSpecial">'</span>) <span class="synPreProc">end</span> <span class="synStatement">if</span> <span class="synIdentifier">$0</span> == <span class="synConstant">__FILE__</span> puts randomize(<span class="synSpecial">&quot;</span><span class="synConstant">I couldn't believe that I could actually understand what I was reading : the phenomenal power of the human mind .</span><span class="synSpecial">&quot;</span>) <span class="synStatement">end</span> </pre> yugui アンダースタンディング・コンピュテーション hatenablog://entry/8599973812317119216 2015-02-21T03:54:12+09:00 2017-11-12T18:07:04+09:00 『アンダースタンディング・コンピュテーション』を監訳者の笹田さんから頂戴したので、読んだ感想をまとめてみる。この本はRubyを用いて計算理論を紹介しようというものだ。具体的には次のようなトピックを扱っている。 形式的意味論 操作的意味論 表示的意味論 オートマトンとチョムスキー階層 ラムダ計算 停止性問題と計算不能性 静的意味論と型システム サンプルが丁寧に書かれていて簡単に動作させられること。これが本書の強みだと言える。 正直なところ今までオートマトンはともかく形式的意味論は他の本を何度を読... <p>『 <a href="http://d.hatena.ne.jp/asin/487311697X/idm-22">アンダースタンディング コンピュテーション ―単純な機械から不可能なプログラムまで</a> 』を監訳者の笹田さんから頂戴したので、読んだ感想をまとめてみる。</p> <p>この本は<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>を用いて計算理論を紹介しようというものだ。具体的には次のようなトピックを扱っている。</p> <ul> <li><p>形式的意味論</p> <ul> <li>操作的意味論</li> <li>表示的意味論</li> </ul> </li> <li><p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%C8%A5%DE%A5%C8%A5%F3">オートマトン</a>と<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C1%A5%E7%A5%E0%A5%B9%A5%AD%A1%BC%B3%AC%C1%D8">チョムスキー階層</a></p></li> <li>ラムダ計算</li> <li>停止性問題と計算不能性</li> <li>静的意味論と型システム</li> </ul> <p>サンプルが丁寧に書かれていて簡単に動作させられること。これが本書の強みだと言える。 正直なところ今まで<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%C8%A5%DE%A5%C8%A5%F3">オートマトン</a>はともかく形式的意味論は他の本を何度を読んでもいまいちピンと来なかったのだが、本書のサンプルプログラムを動かして初めて理解が進んだ気がする。</p> <p>そもそも本文にも書かれているように、ミニ言語の形式的意味論をより強く(曖昧な)言語である<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>で書いたところで理論的には意味は無い。 意味の定義が<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の形式的意味論に依存するようになるだけで、問題をより難しくしているに過ぎないことになる <a href="#f-805918c4" name="fn-805918c4" title="一応Rubyの操作的意味論はISO/IEC 30170に定義されてはいるが、本書のサンプル言語の意味論より明らかに大きく複雑だ">*1</a> 。 それでも、読みやすく処理系の入手が容易な言語でサンプルを動かすのは学習上の利点は大きいと感じた。</p> <p>いままで理解がいまいちだった理由にも思い当たった。 意味論にせよ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%C8%A5%DE%A5%C8%A5%F3">オートマトン</a>、ラムダ計算いずれにしても、きちんと理解しようと思ったら結局は実際にステップを追って意味の評価を進めてみるしかないのだろう。 顧みれば、実務で<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%C8%A5%DE%A5%C8%A5%F3">オートマトン</a>を書くのはたまにあることだし、そもそも私は<a class="keyword" href="http://d.hatena.ne.jp/keyword/%C0%B5%B5%AC%C9%BD%B8%BD">正規表現</a>エンジンを書きたくてプログラミングを覚えたようなものなのでその辺の経験はいくらかあった。 これに対してスモールステップ意味論を意識しながら<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B9%BD%CA%B8%B2%F2%C0%CF">構文解析</a>することは少ないし、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Yacc">Yacc</a>のような具体的なパーザージェネレータと教科書に出てくる数学的な表記の乖離は<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%C8%A5%DE%A5%C8%A5%F3">オートマトン</a>のそれよりも大きい。 これが理解が進まない原因だったに違いない。</p> <p>その点、本書はある程度数学的厳密さを放棄してでも読者が手を動かすことを促している。 定義を一つ一つ追って評価してみる手順を紙と鉛筆でやるのはなかなか億劫だが、幸いなことに我々の前には高性能な計算機械があるのでこれを補助に使えばそんなに大変でもない。 そこで<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>を用いた実装なのだ。いろんな入力に反応して実際に意味論が評価され抽象機械が動作するのは見ていて楽しい。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>という言語の選択も悪くない。余分な構文要素や非本質的な処理なしに簡潔にプログラムの意図を書き下せるという<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の利点が良く活かされている。 ラムダ計算そのものを出自とするいくつかの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B4%D8%BF%F4%B7%BF%B8%C0%B8%EC">関数型言語</a>とどちらが良いかは議論の余地があるが、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の良い点を見いだすとすれば「ラムダ計算と異質な表記」であることが挙げられる。 つまり、記述されているものと記述しているものの区別が容易である。</p> <p>理解をさらに進めるとすればこの後改めて、数学的により厳密な教科書を当たるべきなのだろう。ただ、初心者がまずは手軽に試して概要をつかむための本として良い本だと思った。 本書を読むならば是非サンプルプログラムはすべて実際に動かしてみるべきである。私もまだ動かしていない残りの部分をぼちぼち試してみようと思う。</p> <div class="footnote"> <p class="footnote"><a href="#fn-805918c4" name="f-805918c4" class="footnote-number">*1</a><span class="footnote-delimiter">:</span><span class="footnote-text">一応<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>の操作的意味論はISO/IEC 30170に定義されてはいるが、本書のサンプル言語の意味論より明らかに大きく複雑だ</span></p> </div> yugui Dockerイメージのフォーマット hatenablog://entry/8599973812317119229 2014-11-28T01:05:30+09:00 2017-11-12T18:07:06+09:00 問: Dockerイメージはどういうフォーマットなのか 答: 特定のフォーマットはないが転送時はtar 以前の記事では、Dockerはアプリケーション動作環境のファイルステムをまとめて差分転送できると書いた。 ではこのディスクイメージはどういうフォーマットなのだろう。実装こそ違うが実態としては仮想マシン環境に似ているという点を鑑みると、qcowとか何かその類いのファイルフォーマットなのかもしれないという想像も成り立つ。 しかし実際には特定のフォーマットというものはない。Dockerホスト環境で... <ul> <li>問: Dockerイメージはどういうフォーマットなのか</li> <li>答: 特定のフォーマットはないが転送時はtar</li> </ul> <p><a href="http://yugui.jp/articles/880#label-2">以前の記事</a> では、Dockerはアプリケーション動作環境のファイルステムをまとめて差分転送できると書いた。 ではこのディスクイメージはどういうフォーマットなのだろう。</p> <p>実装こそ違うが実態としては<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BE%C1%DB%A5%DE%A5%B7%A5%F3">仮想マシン</a>環境に似ているという点を鑑みると、qcowとか何かその類いのファイルフォーマットなのかもしれないという想像も成り立つ。 しかし実際には特定のフォーマットというものはない。</p> <p>Dockerホスト環境では、ディスクイメージは動作に適した形で保存されている。このため保存形式はdocker <a class="keyword" href="http://d.hatena.ne.jp/keyword/daemon">daemon</a>の <kbd>-storage-driver</kbd> オプションによって異なる。 つまり <kbd>--storage-driver=aufs</kbd> で走っているならローカル<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A1%A5%A4%A5%EB%A5%B7%A5%B9%A5%C6%A5%E0">ファイルシステム</a>のサブ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リ内に展開されているし、 <kbd>--storage-driver=devicemapper</kbd> ならblock deviceとして格納されている。 このためdockerコンテナを走らせるときにはただaufsなり何なりをmountするだけで即座にコンテナの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A1%A5%A4%A5%EB%A5%B7%A5%B9%A5%C6%A5%E0">ファイルシステム</a>を用意できる。イメージを展開したりそのコンテナ用に複製したりする作業は不要なわけだ。</p> <p>一方、イメージを転送する際には格納方法がまちまちでは困る。そこで転送時は単に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A1%A5%A4%A5%EB%A5%B7%A5%B9%A5%C6%A5%E0">ファイルシステム</a>内のエントリを(正確に言うと親イメージからの差分を)tarで固めて転送する。 なおdockerイメージには親イメージのIDやコンテナ実行時のデフォルトの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B3%A5%DE%A5%F3%A5%C9%A5%E9%A5%A4%A5%F3">コマンドライン</a>など<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E1%A5%BF%A5%C7%A1%BC%A5%BF">メタデータ</a>も含まれるので、これは別途<a class="keyword" href="http://d.hatena.ne.jp/keyword/JSON">JSON</a>形式で転送する。 詳しいことは <a href="https://docs.docker.com/reference/api/registry_api/">Docker Registry API reference</a> に書いてある。</p> yugui Heroku本を読んだ hatenablog://entry/8599973812317119238 2014-09-23T09:25:03+09:00 2017-11-12T18:07:07+09:00 『プロフェッショナルのための実践Heroku入門』をざっと読んだ。コンパクトにまとまっていて良い本だと思う。 Herokuの概要、各言語の開発環境セットアップ、Herokuを用いたアプリケーションの開発サイクル、デプロイの仕組み、Addon, Herokuアーキテクチャの外観などに順に触れている。 web開発者ならこれだけ一冊読めばとりあえずHerokuにアプリケーションを立ち上げられるようになるだろう。 ベストプラクティスやアンチパターンについても折々触れているので、読んでおけば先々楽にもな... <p>『プロフェッショナルのための実践Heroku入門』をざっと読んだ。</p> <p>コンパクトにまとまっていて良い本だと思う。 私が以前1日がかりで調べて回ったこと+αぐらいが書かれているので、読んでおけばそういう調べ回る時間を節約してくれるし。</p> <p>インストール手順に結構な紙面を割いたりするあたりはあまり好みの構成ではないが、薄い割に触れているトピックが豊富なのは良い。 Herokuの概要、各言語の開発環境セットアップ、Herokuを用いたアプリケーションの開発サイクル、デプロイの仕組み、Addon, Heroku<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%AD%A5%C6%A5%AF%A5%C1%A5%E3">アーキテクチャ</a>の外観などに順に触れている。 web開発者ならこれだけ一冊読めばとりあえずHerokuにアプリケーションを立ち上げられるようになるだろう。 ベストプラクティスや<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%F3%A5%C1%A5%D1%A5%BF%A1%BC%A5%F3">アンチパターン</a>についても折々触れているので、読んでおけば先々楽にもなる。</p> <p>残念なのは、workerプロセスについては名前ぐらいであまり触れていないことだろうか。workerがあればmicroservicesも可能だ思うのだけれども、web dynoからworker dynoにアクセスするにはどうしたら良いのかわからなかった。これは前にHerokuのhelpを調べたときも良くわからなかった。ambasadorパターンしてくれてたりするのだろうか。</p> <p>これは愚痴だけれども、冒頭の概説における<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> Cloud Platformの扱いが悲しい。</p> <ul> <li>IaaSの例としては<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> Compute Engineは無視された。紙面には限りもあるので落とされるものがあるのは仕方がないが、ごく個人的な希望としては入れてくれたら嬉しかった。</li> <li><p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a> AppEngineでは<a class="keyword" href="http://d.hatena.ne.jp/keyword/RDB">RDB</a>が使えない、ということはない。 <a href="https://developers.google.com/cloud-sql/?hl=ja">Cloud SQL</a> というのがあって、これは普通の<a class="keyword" href="http://d.hatena.ne.jp/keyword/MySQL">MySQL</a>として使える。</p> <ul> <li>AppEngineではBigDataというKVSしか使えないと書いてあるのは <a href="https://developers.google.com/appengine/docs/python/blobstore/?hl=ja">Blobstore</a> (もしくは <a href="https://cloud.google.com/products/cloud-storage/">Google Cloud Storage</a> )と <a href="https://developers.google.com/datastore/">Datastore</a> が混ざってないだろうか。BigDataという製品は今のところ無い。</li> <li>Blobstore/Cloud Storageは要するに <a href="http://aws.amazon.com/jp/s3/">S3</a> みたいなものなのでKVSといえばKVSだし、特にBlobstoreの<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>はKVSっぽい。DatastoreはEventually Consistentじゃなくて厳密にtransactionalな <a href="http://hbase.apache.org/">HBase</a> みたいなものなのでKVSとはちょっと違うNoSQL DBだ。</li> </ul> </li> </ul> <p><div class="hatena-asin-detail"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4048915134/idm-22/"><img src="https://images-fe.ssl-images-amazon.com/images/I/51GpKqXfUrL._SL160_.jpg" class="hatena-asin-detail-image" alt="プロフェッショナルのための 実践Heroku入門 プラットフォーム・クラウドを活用したアプリケーション開発と運用 (書籍)" title="プロフェッショナルのための 実践Heroku入門 プラットフォーム・クラウドを活用したアプリケーション開発と運用 (書籍)"></a><div class="hatena-asin-detail-info"><p class="hatena-asin-detail-title"><a href="http://www.amazon.co.jp/exec/obidos/ASIN/4048915134/idm-22/">プロフェッショナルのための 実践Heroku入門 プラットフォーム・クラウドを活用したアプリケーション開発と運用 (書籍)</a></p><ul><li><span class="hatena-asin-detail-label">作者:</span> 相澤歩,arton,鳥井雪,織田敬子</li><li><span class="hatena-asin-detail-label">出版社/メーカー:</span> <a class="keyword" href="http://d.hatena.ne.jp/keyword/KADOKAWA">KADOKAWA</a>/<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%B9%A5%AD%A1%BC%A1%A6%A5%E1%A5%C7%A5%A3%A5%A2%A5%EF%A1%BC%A5%AF%A5%B9">アスキー・メディアワークス</a></li><li><span class="hatena-asin-detail-label">発売日:</span> 2014/09/19</li><li><span class="hatena-asin-detail-label">メディア:</span> 大型本</li><li><a href="http://d.hatena.ne.jp/asin/4048915134/idm-22" target="_blank">この商品を含むブログ (3件) を見る</a></li></ul></div><div class="hatena-asin-detail-foot"></div></div></p> yugui libchanを読んだ hatenablog://entry/8599973812317119254 2014-07-22T22:22:39+09:00 2017-11-12T18:07:08+09:00 libchanを読んだのでまとめてみる。 <p><a href="https://github.com/docker/libchan">libchan</a> を読んだのでまとめてみる。</p> <h1>libchanとは</h1> <p>libchanはdockerに使われているライブラリの1つで、先月の <a href="http://dockercon.com">DockerCon</a> で発表された。 非同期かつ一方向の通信チャネルをインプロセスでもネットワーク越しでも扱えるというGoライブラリである。 一方向とはいうものの、チャネル自体をデータに添えて他のチャネル越しに送れる。なので、返信や待ち合わせが必要ならば自分宛のチャネルを送って相手に使ってもらい、自分はそのチャネルの上で待機していれば良い。</p> <p>早い話がGo言語の機能であるチャネルをネットワーク対応したようなものだ、と書いてある。</p> <p>DockerはこのDockerConではDocker 1.0に加えてlibcontainer, libchan, libswarm, Docker Hubを発表していて一応キーノートの話題の1つではあったものの、 個人的にはlibswarmやKubernetesに比べると<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%D1%A5%AF">インパク</a>トは小さいなという感想であった。</p> <h1>実装</h1> <p>何らかのトランスポート機構の上に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B7%A5%EA%A5%A2%A5%E9%A5%A4%A5%BA">シリアライズ</a>されたメッセージを送る仕組みで、割とシンプルな造りである。</p> <p>トランスポートとしては下記をサポートするとドキュメントに書いてある。</p> <blockquote><ul> <li>In-memory Go channel</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/Unix">Unix</a> socket</li> <li>Raw <a class="keyword" href="http://d.hatena.ne.jp/keyword/TCP">TCP</a></li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/TLS">TLS</a></li> <li>HTTP2/SPDY</li> <li>Websocket</li> </ul> </blockquote> <p>ただし、現在実装されているのは下記のみに見える。</p> <ul> <li>In-memory Go channel</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/Unix">Unix</a> socket</li> <li>SPDY</li> </ul> <p>また"In-memory Go channel"とはいうものの、実際にはmutexと変数共有で実装されていてchanは使っていない。</p> <p>送信可能なメッセージの定義は次のようになっている。</p> <pre class="code lang-go" data-lang="go" data-unlink><span class="synStatement">type</span> Message <span class="synStatement">struct</span> { Data []<span class="synType">byte</span> Fd *os.File Ret Sender } </pre> <p><code>Data</code> は任意の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DA%A5%A4%A5%ED%A1%BC%A5%C9">ペイロード</a>、 <code>Ret</code> は返信用に相手に送るチャネルである。</p> <p><code>Ret</code> には特別な値である <code>libchan.RetPipe</code> を指定できる。これを使うとチャネルの実装が勝手に返信用の逆方向チャネルを作って送ってくれるので、位置透過性を保ったまま「送信元に返信してね」と指定できる。</p> <p><code>Fd</code> はよく分からない。一見すると<a class="keyword" href="http://d.hatena.ne.jp/keyword/Unix">Unix</a>ソケットのFD passingみたいなものを実現するつもりなのかとも思うが、"file attachment <a class="keyword" href="http://d.hatena.ne.jp/keyword/not%20yet">not yet</a> implemented in <a class="keyword" href="http://d.hatena.ne.jp/keyword/unix">unix</a> transport"とか書いてあるし、"file attachment"というのは何か別のものなのかもしれない。</p> <p><code>unix.Receiver</code> の定義が下記のようになっているあたり、やはりFD passingか、とも思うのだが。</p> <pre class="code lang-go" data-lang="go" data-unlink><span class="synStatement">type</span> Receiver <span class="synStatement">interface</span> { Receive() ([]<span class="synType">byte</span>, *os.File, <span class="synType">error</span>) } </pre> <h1>感想</h1> <p>デザインは魅力的だ。ただし、位置づけがまだ不安定であるし、実装も開発途上で未実装機能が多い。</p> <p>チャネル上でチャネルを送れるということの強力さはGoでchanを使っているとよく分かる。 それをネットワーク分散システムで利用できるのはとても楽しみだ。ただしSPDYトランスポートでのチャネル転送が <code>libchan.RetPipe</code> を除いて未実装なので、その魅力は現時点では半減する。</p> <h2>チャネル再転送の困難</h2> <p>実際のところリモートマシン宛の任意のチャネル転送をサポートするとなると厄介な問題に出会うだろう。 転送されてきたチャネルを再転送できるというのがこの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D1%A5%E9%A5%C0%A5%A4%A5%E0">パラダイム</a>を強力たらしめるのだが、そのトランスポートレベルでの実装は面倒になり得る。</p> <p>ノードA, B, Cがあって、Aが生成したA宛のチャネルをBに送り、BはそれをCに再転送し、Cがそのチャネルに書き込んだとしよう。素朴には2通りの実装が考えられる。</p> <ul> <li>CはAと直接通信する。Cはチャネルを受け取った時点でAへの通信を確立し、その上に受け取ったチャネルを再現する</li> <li>BがCとAの通信を中継する。</li> </ul> <p>複雑なシステムでチャネルがあちこちでやり取りされると、中継モデルは破綻することが予想される。 チャネルの流通経路を逆に辿ってシステム内のノードをたらい回しされ、無駄に転送コストを支払うメッセージが見えるようだ。</p> <p>一方で直接通信モデルはではAとCが直接通信可能とは限らない。firewall越えやNAT越えの問題があるし、Raw <a class="keyword" href="http://d.hatena.ne.jp/keyword/TCP">TCP</a>経由でWebsocketチャネルを送った場合どっちの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%ED%A5%C8%A5%B3%A5%EB">プロトコル</a>が妥当とも言えない。</p> <p>結局は直接通信と中継のハイブリッドが妥当であろうと考えられるが、どうハイブリッドするのか、通信パスをどう最適化するのかというルーティング問題が残る。</p> <h2>将来性</h2> <p>Dockerを実装するには便利なんだろうと思う。問題はどこまで汎用化してくれるだろうかというところにある。</p> <p>一応は任意の言語でライブラリを実装可能と書いてあるので、Docker以外が使うことも想定はしているんだろう。それでも、NAT越えとかルーティングとかをどこまで真剣にサポートしてくれるのか。 その辺のDockerには必要かどうか分からない機能のためにパッチを継続的に受け付けてくれるのか。誰がそういうパッチを書くのか。</p> <h2>競合</h2> <p>多様なトランスポートをサポートした通信ライブラリという点でlibchanはあからさまにZeroMQと競合する。</p> <p><a href="https://news.ycombinator.com/item?id=7873961">中の人のコメント</a> では「ZeroMQは機能過剰だし、ローレベル過ぎるし、要らない機能を外すのも大変」だからlibchanを作ったとある。</p> <p>個人的にはこれに賛同する。チャンネル自体の転送ができるのは圧倒的な魅力だし、これで十分に強力だ。 ZeroMQがサポートするPublisher-Subscriberモデルや同期通信, 双方向通信はlibchanに欠けているが特に問題とも思わない。 バイトストリーム上にチャネル転送を構築することに比べると、PubSubや同期、双方向通信を非同期一方向+チャネル転送で実装するほうが楽だし、抽象化としてまともに思える。</p> <p>しかし、皆がそう思わなければ使われないだろう。どれだけユーザー層が成熟していくのか未知数である。</p> <h2>結論</h2> <p>流行ったらいいなと思うし、ネットワーク越しのチャネル転送の仕様が固まってきたら<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ruby">Ruby</a>や<a class="keyword" href="http://d.hatena.ne.jp/keyword/C%2B%2B">C++</a>で実装するのもの面白いかもしれない。 でも今は使うときではないと思う。</p> yugui Dockerで何が変わるのか hatenablog://entry/8599973812317119266 2014-06-14T06:27:37+09:00 2017-11-12T22:35:08+09:00 DockerCon 2014に行ってきた。 この会期中には各社からいくつもの製品が紹介発表された。そして、それによってクラウドという技術は次のステージに移行したと言っても過言ではないだろう。 あのサンフランシスコのホテルで、サーバーサイドの歴史は変わった。DockerとはDockerとはいわゆるコンテナ技術の1つで、Linuxホスト環境の中に、隔離された別のLinux環境を作ってくれる技術だ。 軽量仮想マシンと呼ばれたりもする。Solaris Containerとも似ている。新しくないDock... <p><a href="http://dockercon.com">DockerCon 2014</a> に行ってきた。</p> <p>この会期中には各社からいくつもの製品が紹介/発表された。そして、それによって<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>という技術は次のステージに移行したと言っても過言ではないだろう。</p> <p>より自由にユーザーが<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>ベンダーを選べる時代へ。どうやって<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>にうまくデプロイするかではなく、アプリケーションそのものに注力できる時代へ。</p> <h1>Dockerとは</h1> <p><a href="http://docker.io">Docker</a> とはいわゆるコンテナ技術の1つで、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Linux">Linux</a>ホスト環境の中に隔離された別の<a class="keyword" href="http://d.hatena.ne.jp/keyword/Linux">Linux</a>環境を作ってくれる技術だ。</p> <p>軽量<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BE%C1%DB%A5%DE%A5%B7%A5%F3">仮想マシン</a>と呼ばれたりもする。 <a href="http://www.oracle.com/technetwork/jp/server-storage/solaris10/containers-169727-ja.html">Solaris Container</a> とも似ている。</p> <h2>新しくないDocker</h2> <p>1つ述べておくとDockerは技術的には新しくない。Dockerの価値は技術以外にある(とDockerのCEOもDockerConで言ってた)。</p> <p>技術的には<a class="keyword" href="http://d.hatena.ne.jp/keyword/Solaris">Solaris</a>には<a class="keyword" href="http://d.hatena.ne.jp/keyword/Solaris">Solaris</a> 10の頃からあるし(<a class="keyword" href="http://d.hatena.ne.jp/keyword/ruby">ruby</a>-lang.orgも使ってた)、<a class="keyword" href="http://d.hatena.ne.jp/keyword/Linux">Linux</a>にだってLXCがKernel 2.6の頃からある。そもそもの話Dockerの初期のバージョンはLXCのラッパーみたいなもんだった。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BE%C1%DB%A5%DE%A5%B7%A5%F3">仮想マシン</a>に似ているという観点から述べるなら、コンテナ技術というのはマシンやOSそのものを仮想的に実現してその上でアプリケーションを動かす代わりにマシンと<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AB%A1%BC%A5%CD%A5%EB">カーネル</a>はホストのものをそのまま使って、ユーザーランド+αだけを仮想化する、みたいなもんだ。</p> <p>別の視点から述べると、これって要するに<a class="keyword" href="http://d.hatena.ne.jp/keyword/chroot">chroot</a>+αだ。「ちょっとすごいJail」と言ってもいいだろう。<a class="keyword" href="http://d.hatena.ne.jp/keyword/Chroot">Chroot</a>だと<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A1%A5%A4%A5%EB%A5%B7%A5%B9%A5%C6%A5%E0">ファイルシステム</a>だけが隔離されるけど、その他にDockerではプロセスIDのネームスペースや、ユーザーIDやネットワークポートの空間も隔離される。Dockerの中のrootはホスト環境のrootとは違うユーザーだし、プロセス番号は同じでも違うプロセスだし、<a class="keyword" href="http://d.hatena.ne.jp/keyword/TCP">TCP</a> portも同様。要は、これぐらい一通りホスト環境から隔離してやればユーザーランドから見ると<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BE%C1%DB%A5%DE%A5%B7%A5%F3">仮想マシン</a>に隔離されてるも同然だよね、という話。実際には<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AB%A1%BC%A5%CD%A5%EB">カーネル</a>はホスト環境のやつを使ってるけど、ユーザーランドからそれを知るすべはほとんどない筈。</p> <h2>新しいDocker</h2> <p>Dockerが新しいのは、コンテナ環境で走らせるアプリケーションを簡単にパッケージする標準的な方法を確立したことにある。</p> <p>Dockerは、ユーザーランドで使われる<a class="keyword" href="http://d.hatena.ne.jp/keyword/GNU/Linux">GNU/Linux</a><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%B9%A5%C8%A5%EA%A5%D3%A5%E5%A1%BC%A5%B7%A5%E7%A5%F3">ディストリビューション</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A1%A5%A4%A5%EB%A5%B7%A5%B9%A5%C6%A5%E0">ファイルシステム</a>ひと揃いを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%E1%A5%BF%A5%C7%A1%BC%A5%BF">メタデータ</a>と一緒に固めた形式を定義している。要するに仮想ディスクイメージなんだけど。</p> <p>また、イメージはしばしば他のイメージから派生する。その場合は元のイメージからの差分だけが保存されるので軽量である。</p> <p>更に、イメージはgitのコミットと同じ要領で内容のハッシュで識別されることになっている。</p> <p>これらが合わさると、こういうことになる。アプリケーションを含まないOSだけのbaseイメージ(例えば<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ubuntu">Ubuntu</a> preciseイメージ)はみんなが大抵手元に持っているし、必要であれば公式の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EA%A5%DD%A5%B8%A5%C8%A5%EA">リポジトリ</a>から簡単にダウンロードできる。</p> <p>そのベースイメージから派生した自分のアプリケーションインスール済み環境を配布したいとする。</p> <p>受け取る相手は、今自分が受け取ろうとしているイメージの派生元を辿って行く。既に持っている分は改めて取得する必要はなくて、そこからの差分だけを転送すればよい。つまり、ディスクイメージの転送が軽量にできる。</p> <p><del>同じ内容のイメージは必ず同じハッシュを持つので、既に持っているかどうかは簡単に判別できる。この辺もgitと同じだ。</del></p> <p>更に、こうして作成された差分ディスクイメージを走らせるときもホスト環境のディスクを無駄に使わず、起動時にマージなんてアホなことをすることもなく、効率的に<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>化できるようになっている。</p> <p>こういう便利なパッケージの仕組みを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%D7%A5%F3%A5%BD%A1%BC%A5%B9">オープンソース</a>で作った。そしてこの仕組みをみんなが使い始めた。みんなというのは<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>, <a class="keyword" href="http://d.hatena.ne.jp/keyword/Amazon">Amazon</a>, <a class="keyword" href="http://d.hatena.ne.jp/keyword/IBM">IBM</a>, <a class="keyword" href="http://d.hatena.ne.jp/keyword/Microsoft">Microsoft</a>, Rackspace, ...を含む。</p> <h1>新しい<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>の話</h1> <p>Dockerはそれ単体で使っても便利なものだ。プロセスを隔離したいときの<a class="keyword" href="http://d.hatena.ne.jp/keyword/chroot">chroot</a>の不便を解消できる。</p> <p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Chroot">Chroot</a>だと、<a class="keyword" href="http://d.hatena.ne.jp/keyword/chroot">chroot</a>環境の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A1%A5%A4%A5%EB%A5%B7%A5%B9%A5%C6%A5%E0">ファイルシステム</a>を作るのが結構面倒でみんな自作の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>で頑張ったりしたものだった。Dockerなら簡単に<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ubuntu">Ubuntu</a>でも<a class="keyword" href="http://d.hatena.ne.jp/keyword/CentOS">CentOS</a>でも好きな環境のイメージを作れる。 <a href="#f-5c35c9c0" name="fn-5c35c9c0" title="隔離されていて予測可能な状態にある環境というのはビルドやなんかにはとても便利だ。だからDockerのビルドスクリプトはDocker環境の中でDockerをビルドする">*1</a></p> <p>しかし、Dockerが本当に便利なのは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>環境との組み合わせで、イメージを他のマシンに転送するときだ。手元のマシンでDockerイメージを作り込んでプロダクション環境に転送、実行してやる。すると、手元で設定したアプケーションがそのままプロダクションで動く。ファイルを配備して、ユーザーを作成して、設定ファイルを弄って、みたいな成果物をそのままの状態で転送できる。もう苦労してプロダクション環境を適切にセットアップしてやる必要はない。つまり、ProvisioningやDeploymentの手順が劇的に簡略化される。</p> <p>Dockerさえ設定しておけば、あとはイメージをポイッと配布するとそれがそのまま動くのだ。</p> <p>最初にDockerを軽量<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BE%C1%DB%A5%DE%A5%B7%A5%F3">仮想マシン</a>ぽいものだと言ったけれど、ここに至っては仮想化であるということは手段の1つでしかなくてあまり重要なことではない。重要なのは設定済みのアプケーション動作環境をユーザーランドまるごとパッケージして軽量に移動できるということのほうだ。</p> <p>そして、これが<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>の主要ベンダーたちによってサポートされた。</p> <ul> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>はManaged <a class="keyword" href="http://d.hatena.ne.jp/keyword/VM">VM</a>を発表した。ユーザーがアップロードしたDockerイメージがAppEngine相当のmanaged環境で実行される。AppEngine同様の「<a class="keyword" href="http://d.hatena.ne.jp/keyword/Google">Google</a>が運用の面倒を見てくれる」手軽さがありながら、Dockerイメージには任意の言語で任意のアプケーションを入れられる。</li> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/Amazon">Amazon</a>はElastic BeanstalkでDockerをサポートした。話としてはManaged <a class="keyword" href="http://d.hatena.ne.jp/keyword/VM">VM</a>と同じだ。</li> <li>...</li> </ul> <p>こうして各社がDockerをサポートした。つまり、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>環境で動かすためにアプケーションをパッケージングし転送する標準的な方法が生まれた。もうベンダーロックインを心配しなくていい。他社のサービスへ移りたくなったらそのDockerイメージを移動先に改めてアップロードすれば良い。</p> <h1>libswarm</h1> <p>今回のDockerConでは、もう1つベンダーロックインをなくすための製品が発表された。</p> <p>なるほど、一個のサービスはDockerで簡単に配備できるし移動もできるかもしれない。しかし、今どきの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>上のアプケーションというのは1つのサービスだけで動く訳ではない。データベースに、キャッシュに、バックエンドサーバー、フロントエンドwebアプケーションサーバーなどなど沢山のroleを為すそれぞれの種類のサーバー<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>があって、それぞれを適切に繋げてやらねばならない。しかも<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>上の話だからしばしばサーバーのIPは<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>ベンダーに割り振られる。IP決めうちであらかじめ設定してからイメージを作るという訳にもいかない。つまり、依存する各サーバーが他のサービスを発見して繋ぎにいくというサービスディスカバリと<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%B1%A5%B9%A5%C8%A5%EC%A1%BC%A5%B7%A5%E7%A5%F3">オーケストレーション</a>の問題が発生する。</p> <p>この問題に対しては既にいくつものソリューションがある。fleet+etcdだとか、gearだとか。うまく使えば<a class="keyword" href="http://d.hatena.ne.jp/keyword/DNS">DNS</a>も解になり得るだろう。<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>ベンダーも思い思いに解決法を提供している。</p> <p>真の問題は、解決法が多すぎることだ。未だに標準的な方法はないし、どこかのベンダーの機能を使えば、サービスの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%B1%A5%B9%A5%C8%A5%EC%A1%BC%A5%B7%A5%E7%A5%F3">オーケストレーション</a>という要の部分を握られてロックインされる。</p> <p>そこでDockerはこの度libswarmを発表した。libswarmはこの様々な実装を抽象化した<a class="keyword" href="http://d.hatena.ne.jp/keyword/API">API</a>を提供するライブラリだ。</p> <p>個人的には<a class="keyword" href="http://d.hatena.ne.jp/keyword/libvirt">libvirt</a>の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AA%A1%BC%A5%B1%A5%B9%A5%C8%A5%EC%A1%BC%A5%B7%A5%E7%A5%F3">オーケストレーション</a>版、という理解をしている。</p> <p>libswarmによっていよいよ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AF%A5%E9%A5%A6%A5%C9">クラウド</a>ベンダー間の移動は楽になる。もっと安いところ、もっとスケールするところ、などなど必要に応じて他のベンダーに自由に移動できる時代がやってくる。</p> <h1>Kubernetes</h1> <p>そしてKubernetesは更にその一歩先の話、だと思うけどその話は後で書く</p> <p>Disclaimer このブログはYuguiの個人的なものです。ここで述べられていることはDockerConで聞いた話の個人的理解、および個人的な意見に基づくものであり、私の雇用者には一切の関係はありません。</p> <h1>注</h1> <p>上で間違ってdocker imageのidがcontent hashかのように書いたが、それは次期イメージフォーマットの話で現在はイメージ作成時にランダムに決まる256bitだった。イメージ生成時にはキャッシュが効くので続けざまに二回同じイメージをビルドすると同じIDが帰ってくるが、別のホストで独立に同じイメージをビルドしても別のIDになる。</p> <div class="footnote"> <p class="footnote"><a href="#fn-5c35c9c0" name="f-5c35c9c0" class="footnote-number">*1</a><span class="footnote-delimiter">:</span><span class="footnote-text">隔離されていて予測可能な状態にある環境というのはビルドやなんかにはとても便利だ。だからDockerのビルド<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>はDocker環境の中でDockerをビルドする</span></p> </div> yugui fluentdを勉強中 hatenablog://entry/8599973812317119277 2014-02-02T12:10:31+09:00 2017-11-12T18:07:12+09:00 最近はlog collectionというと&lt;URL:http://www.fluentd.org/|fluentd&gt;が話題らしいというので、少し触り始めている。 多少は分かってきたように思うので理解したことと、理解できていないところをまとめてみようと思う。fluentdとは何かオフィシャルサイトには"tool to collect events and log"と書いてある。 要するに、サーバー群から継続的に情報を吸い上げるための仕組みを提供するdaemonである。この「情報をかき集... <p>最近はlog collectionというと <a href="http://www.fluentd.org/">fluentd</a> が話題らしいというので、少し触り始めている。 多少は分かってきたように思うので理解したことと、理解できていないところをまとめてみようと思う。</p> <h1>fluentdとは何か</h1> <p>オフィシャルサイトには"tool to collect events and log"と書いてある。 要するに、サーバー群から継続的に情報を吸い上げるための仕組みを提供する<a class="keyword" href="http://d.hatena.ne.jp/keyword/daemon">daemon</a>である。</p> <p>この「情報をかき集める」という枠組みは今時のサーバーサイドでは頻出パターンだ。 データベースサーバ、<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3%A5%B5%A1%BC%A5%D0">アプリケーションサーバ</a>ー、フロントエンドキャッシュサーバー, ...と役割分担をするのが普通だし、 <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3%A5%B5%A1%BC%A5%D0">アプリケーションサーバ</a>ーだって負荷分散のために複数<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>を持つのが当たり前だ。 そしてこれらサーバー群をきちんと管理するためには様々な情報をかき集めてきて一カ所に保存したり集約したりする必要がある。 たとえば、</p> <ul> <li><a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A5%D7%A5%EA%A5%B1%A1%BC%A5%B7%A5%E7%A5%F3%A5%B5%A1%BC%A5%D0">アプリケーションサーバ</a>ーのログを全<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A4%A5%F3%A5%B9%A5%BF%A5%F3%A5%B9">インスタンス</a>から集めて一覧可能にする</li> <li>キャッシュサーバーのヒット情報を集めて解析する</li> <li>各マシンのディスク空き容量を定期的に集めて不足しそうなら警告を出す</li> </ul> <p>これらはいずれも一見するとそれほど難しい処理には思えない。scpとcronでもできそうだ。 しかしデータ転送の際の失敗をどうやって扱うのかを考えだすと、自明ではなくなる。 定時<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D0%A5%C3%A5%C1%BD%E8%CD%FD">バッチ処理</a>ではなくリアルタイムに処理し続けるようにしたいと考えると問題はさらに難しくなる。 失敗時は適切な時間を置いて再試行するように。通信エラーで情報が失われたりしないように。</p> <p>fluentdはこの頻出パターンを司り、上記のような困難を処理してくれる。 そして、パターンの個々のバリエーションに対応できるようにデータ入力、加工、出力のそれぞれを<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D7%A5%E9%A5%B0%A5%A4%A5%F3">プラグイン</a>などで拡張可能になっている。</p> <p>また、異なるデータソースからの情報をバラバラにあつめるのではなくfluentdという1つのシステムにつなぐことそのものにも意味がある。 データソースをまたいだリアルタイムの解析も容易になる。</p> <h1>よく分かってないこと1: システム監視</h1> <p>fluentdだけでモニタリングシステムを構築できるだろうか。すべきだろうか。</p> <p>情報をかき集めて変換するという点でシステム監視はfluentdのカバーする範囲に思える。 一方でシステム監視の世界では <a href="http://www.nagios.org/">nagios</a> や <a href="http://www.zabbix.com/jp/">ZABBIX</a>)が長く使われている。 仮にこうした「fluentd以前」からのソリューションを置くとしても最近出た((&lt;Sensu|URL:sensuapp.org) ももた話題になっているようだ。 こういうのとfluentdは棲み分けるべきなんだろうか。それともfluentdに集約してしまうべきなんだろうか。</p> <p>可能ならシステムの中に似たようなモジュールは2つ置きたくない。fluentdを中心としたモニタリングは可能そうだし、 <a href="http://y-ken.hatenablog.com/entry/fluentd-monitoring-and-automatic-alerting-suite">実際に構想している人もいる</a> 。 でも、もし餅は餅屋としてシステム監視専門のモジュールならではのfluentdでカバーできない利点があるならそれは知りたい。</p> <h1>よく分かってないこと2: <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A2%A1%BC%A5%AD%A5%C6%A5%AF%A5%C1%A5%E3">アーキテクチャ</a></h1> <p>Sensuやなんかだと、データ転送をreliableにするためにRabbitMQを使っている。 一方fluentdのforwardingは特にそういう<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DF%A5%C9%A5%EB%A5%A6%A5%A7%A5%A2">ミドルウェア</a>は使ってない。fluentd自身ががんばってエラー対応している。</p> <p>こういうのは別途<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%DF%A5%C9%A5%EB%A5%A6%A5%A7%A5%A2">ミドルウェア</a>でqueueを使うものだと言う思い込みがあったのでfluentdのやり方は見慣れない感じがする。 この違いはどういう設計判断によるものなんだろう。</p> <h1>お返事</h1> <p>疑問を書いてみたら偉い人から <a href="http://tagomoris.hatenablog.com/entry/2014/02/03/154616">お返事</a> 来た。</p> <p>MQ使わないのはやっぱりそういうことか。モニタリングは、うん、もうちょっと考えてみよう。</p> yugui 環境に優しい難読コード hatenablog://entry/8599973812317119291 2013-11-30T13:48:26+09:00 2017-11-12T18:07:13+09:00 ぼちぼち忘年会やクリスマス会のシーズンとなってきた。そんなパーティーの1つに出欠の連絡をしなければならない場面があったのだけど、どうやらネタ回答を期待されているらしかったので次のように回答した。puts [[-&gt;(&amp;f){-&gt;(n){f[f[f[f[f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]]]]]}},-&gt;(&amp;f){-&gt;(n){f[f[f[f[f[n]]]]]}},-&gt;(&amp;f){-&gt;(n){f[f[f[f[f[f[f[... <p>ぼちぼち忘年会やクリスマス会のシーズンとなってきた。</p> <p>そんなパーティーの1つに出欠の連絡をしなければならない場面があったのだけど、どうやらネタ回答を期待されているらしかったので次のように回答した。</p> <pre class="code lang-ruby" data-lang="ruby" data-unlink>puts [[-&gt;(&amp;f){-&gt;(n){f[f[f[f[f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]]]]]}},-&gt;(&amp;f){-&gt;(n){f[f[f[f[f[n]]]]]}}, -&gt;(&amp;f){-&gt;(n){f[f[f[f[f[f[f[f[n]]]]]]]]}},-&gt;(&amp;f){-&gt;(n){f[f[f[f[f[f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]]]]]]}}, -&gt;(&amp;f){-&gt;(n){f[f[f[f[f[f[f[f[n]]]]]]]]}},-&gt;(&amp;f){-&gt;(n){f[f[n]]}},-&gt;(&amp;f){-&gt;(n){f[f[f[f[f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]]]]]}}, -&gt;(&amp;f){-&gt;(n){f[f[f[f[f[n]]]]]}},-&gt;(&amp;f){-&gt;(n){f[f[f[f[f[f[f[f[n]]]]]]]]}},-&gt;(&amp;f){-&gt;(n){f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]}}, -&gt;(&amp;f){-&gt;(n){f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]}},-&gt;(&amp;f){-&gt;(n){n}}].map{|<span class="synIdentifier">x</span>|x[&amp;<span class="synConstant">:succ</span>][<span class="synConstant">0</span>]}.map(&amp;(<span class="synSpecial">&quot;</span><span class="synConstant">%x</span><span class="synSpecial">&quot;</span>.method(<span class="synSpecial">&quot;</span><span class="synConstant">%</span><span class="synSpecial">&quot;</span>))).join]. pack(<span class="synSpecial">&quot;</span><span class="synConstant">H*</span><span class="synSpecial">&quot;</span>).unpack(<span class="synSpecial">&quot;</span><span class="synConstant">U*</span><span class="synSpecial">&quot;</span>).pack(<span class="synSpecial">&quot;</span><span class="synConstant">U*</span><span class="synSpecial">&quot;</span>).encode(<span class="synType">Encoding</span>.default_external) </pre> <p>難解コードとかゴルフとかは苦手なんだけど、相手のためを思って知恵を絞ればできるもんだね。</p> <p>難読コーディングとしては割と定番の素材を使っていて読解難度はそう高くない。ただ、呼び出し可能オブジェクト技法の数々や、表示側の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%A8%A5%F3%A5%B3%A1%BC%A5%C7%A5%A3%A5%F3%A5%B0">エンコーディング</a>に依存しない環境(env(1)的な意味で)に優しい設計でありつつ"Encoding::UTF_8"と書くのはあくまでも避けたあたりについては我ながら満足している。</p> yugui Ubuntu 12.04 preciseをaufs rootにした hatenablog://entry/8599973812317119300 2013-11-09T04:28:02+09:00 2017-11-12T18:07:15+09:00 表題のようにaufsをroot filesystemとしてマウントしてみたので、手順をメモする。経緯最近DNA940という面白い機器を手に入れた。なんでも本来は産業用機器のベースとして使用することを想定した機体で、中身はx86ベースの基板にGbE×4やらコンソールポートやらいろいろついている。 Debianが動いたという話も耳にする。これにOpenFlowを組み合わせて自宅ネットワークのルーターを好きなように組む、という野望を抱いたのがそもそもの動機である。ところで、DNA940は2.5in HD... <p>表題のようにaufsをroot filesystemとしてマウントしてみたので、手順をメモする。</p> <h1>経緯</h1> <p>最近 <a href="http://www.nexcom.com/Products/network-and-communication-solutions/desktop-appliance/desktop-appliance/communication-gateway-dna-940">DNA940</a> という面白い機器を手に入れた。なんでも本来は産業用機器のベースとして使用することを想定した機体で、中身は<a class="keyword" href="http://d.hatena.ne.jp/keyword/x86">x86</a>ベースの基板に<a class="keyword" href="http://d.hatena.ne.jp/keyword/GbE">GbE</a>×4やらコンソールポートやらいろいろついている。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/Debian">Debian</a>が動いたという話も耳にする。</p> <p>これにソフトウェア<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EB%A1%BC%A5%BF%A1%BC">ルーター</a>を組み合わせて自宅ネットワークの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%EB%A1%BC%A5%BF%A1%BC">ルーター</a>を好きなように組む、という野望を抱いたのがそもそもの動機である。</p> <p>ところで、DNA940は2.5in HDDを組み込めるのでそれをメインディスクにしても良いのだけど、可動部品は可能な限りなくしたいのでCFのほうが嬉しい。しかしながら、普通に<a class="keyword" href="http://d.hatena.ne.jp/keyword/Debian">Debian</a>や<a class="keyword" href="http://d.hatena.ne.jp/keyword/Ubuntu">Ubuntu</a>のシステムをCFで動かすとすぐに書き込み上限に引っかかるのは目に見えている。 そこでaufsで<a class="keyword" href="http://d.hatena.ne.jp/keyword/ext4">ext4</a>の上にtmpfsをオーバーレイしたものをroot fsにしたい、というのが今回の話だ。</p> <p>いきなり実機は怖いので、まずは<a class="keyword" href="http://d.hatena.ne.jp/keyword/VMWare%20Fusion">VMWare Fusion</a>上の<a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BE%C1%DB%A5%DE%A5%B7%A5%F3">仮想マシン</a>でやってみる。</p> <h1>手順</h1> <p>同じような需要は各所に有ると見えて、手順は <a href="https://help.ubuntu.com/community/aufsRootFileSystemOnUsbFlash">Ubuntuのヘルプサイト</a> にまとまっている。意外と簡単だった。</p> <ol> <li><p><a class="keyword" href="http://d.hatena.ne.jp/keyword/%B2%BE%C1%DB%A5%DE%A5%B7%A5%F3">仮想マシン</a>を作成する。メモリを1GB、ディスクを2GB割り振って、 ディスクは丸ごと<a class="keyword" href="http://d.hatena.ne.jp/keyword/ext4">ext4</a>のルート<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%D5%A5%A1%A5%A4%A5%EB%A5%B7%A5%B9%A5%C6%A5%E0">ファイルシステム</a>とした。Swap領域は作成しない。</p></li> <li><p><a class="keyword" href="http://d.hatena.ne.jp/keyword/Ubuntu">Ubuntu</a> Precise (<a class="keyword" href="http://d.hatena.ne.jp/keyword/x86-64">x86-64</a> Server Edition)をインストールする。 前述のヘルプページではLive CDからインストールするように書いてあるが、 これだとデスクトップ版がインストールされるような気がした。 そこで、普通にサーバー版のインストールディスクを用いた。</p></li> <li><p>以下はヘルプページの手順に従った。</p> <ul> <li><code>% sudo aptitude install aufs-tools</code></li> <li><code>% sudo bash -c "echo aufs &gt;&gt; /etc/initramfs-tools/modules"</code></li> </ul> <p>ここでヘルプページに書いてあるrootaufs Scriptを <kbd>/etc/initramfs-tools/scripts/init-bottom/__rootaufs</kbd> に保存した上で、</p> <ul> <li><code>% sudo chown root:root /etc/initramfs-tools/scripts/init-bottom/__rootaufs</code></li> <li><code>% sudo chmod 0755 /etc/initramfs-tools/scripts/init-bottom/__rootaufs</code></li> <li><code>% sudo update-grub</code></li> <li><code>% sudo-initramfs -u</code></li> </ul> <p>とする。</p> <p>initrdまわりはあまり詳しくなかったのだけど、要はこの <kbd>scripts</kbd> <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%C7%A5%A3%A5%EC%A5%AF%A5%C8">ディレクト</a>リ以下にある<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>群はinitrd.imgの中にコピーされて、起動時にinitrdで動いている段階で実行されるということらしい。 <a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>を見る感じ、実行時の <kbd>${rootmnt}</kbd> 変数とかが見慣れないものの大体やってることは予想通りだ。tmpfsをマウントして、元のrootを他のマウントポイントにどかしてreadonlyでremountして、それからaufsで両者を重ねる。</p></li> <li><p>再起動する。 さっきの<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%B9%A5%AF%A5%EA%A5%D7%A5%C8">スクリプト</a>は<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AB%A1%BC%A5%CD%A5%EB">カーネル</a>の引数にaufs=tmpfsを渡したときのみ発動するように書かれているので、起動時に引数を手入力した。</p></li> <li><p>起動後 <kbd>mount</kbd> が期待通りであることを確認する。</p> <p>なお、ヘルプページにも書かれているように、rootaufsだとdhclient3がapparmorに引っかかって動かないようだ。おまけに<a class="keyword" href="http://d.hatena.ne.jp/keyword/DHCP">DHCP</a>を何度か再試行する間、起動を待たされる。私の用途では<a class="keyword" href="http://d.hatena.ne.jp/keyword/DHCP">DHCP</a>は必要ないのでIPはstaticに割り振ることにした。 ログを見る限り、この問題って起動タイミングの関係上rootfsの移動によってdhclient3のリンクされるライブラリのパスがapparmorが思ってるやつとずれてしまうのが原因みたいね。dhclient3をprelinkとかすれば解決するんだろうか。まー今回は関係ない。</p></li> <li><p>次は、常にaufs=tmpが渡るように<a class="keyword" href="http://d.hatena.ne.jp/keyword/Grub">Grub</a>を設定する。</p> <p>ヘルプページに書いてあるのはたぶんGrub1の設定だ。今回の環境の場合はGrub2なので少し手順が異なる。 といっても、やることは簡単で <kbd>/boot/<a class="keyword" href="http://d.hatena.ne.jp/keyword/grub">grub</a>/menu.lst</kbd> の代わりに <kbd>/etc/default/<a class="keyword" href="http://d.hatena.ne.jp/keyword/grub">grub</a></kbd> の <code>GRUB_CMDLINE_LINUX_DEFAULT</code> に"aufs-tmpfs"を設定するだけだ。 設定した後は <kbd>sudo update-<a class="keyword" href="http://d.hatena.ne.jp/keyword/grub">grub</a></kbd> で <kbd>/boot/<a class="keyword" href="http://d.hatena.ne.jp/keyword/grub">grub</a>/<a class="keyword" href="http://d.hatena.ne.jp/keyword/grub">grub</a>.cfg</kbd> を再生成する。</p> <p> 以上により、起動時に自動的にaufs-tmpfsが引数に渡るように <kbd>/boot/<a class="keyword" href="http://d.hatena.ne.jp/keyword/grub">grub</a>/<a class="keyword" href="http://d.hatena.ne.jp/keyword/grub">grub</a>.cfg</kbd> が設定され、かつ<a class="keyword" href="http://d.hatena.ne.jp/keyword/%A5%AB%A1%BC%A5%CD%A5%EB">カーネル</a>を入れ替えたりして <kbd><a class="keyword" href="http://d.hatena.ne.jp/keyword/grub">grub</a>.cfg</kbd> が再生成されても設定が保たれるようになった。</p></li> </ol> yugui