世界線航跡蔵

Mad web programmerのYuguiが技術ネタや日々のあれこれをお送りします。

2014年06月14日

Dockerで何が変わるのか

DockerCon 2014に行ってきた。 この会期中には各社からいくつもの製品が紹介/発表された。そして、それによってクラウドという技術は次のステージに移行したと言っても過言ではないだろう。 より自由にユーザーがクラウドベンダーを選べる時代へ。どうやってクラウドにうまくデプロイするかではなく、アプリケーションそのものに注力できる時代へ。

Dockerとは

Dockerとはいわゆるコンテナ技術の1つで、Linuxホスト環境の中に隔離された別のLinux環境を作ってくれる技術だ。 軽量仮想マシンと呼ばれたりもする。Solaris Containerとも似ている。

新しくないDocker

1つ述べておくとDockerは技術的には新しくない。Dockerの価値は技術以外にある(とDockerのCEOもDockerConで言ってた)。 技術的にはSolarisにはSolaris 10の頃からあるし(ruby-lang.orgも使ってた)、LinuxにだってLXCがKernel 2.6の頃からある。そもそもの話Dockerの初期のバージョンはLXCのラッパーみたいなもんだった。

仮想マシンに似ているという観点から述べるなら、コンテナ技術というのはマシンやOSそのものを仮想的に実現してその上でアプリケーションを動かす代わりにマシンとカーネルはホストのものをそのまま使って、ユーザーランド+αだけを仮想化する、みたいなもんだ。

別の視点から述べると、これって要するにchroot+αだ。「ちょっとすごいJail」と言ってもいいだろう。Chrootだとファイルシステムだけが隔離されるけど、その他にDockerではプロセスIDのネームスペースや、ユーザーIDやネットワークポートの空間も隔離される。Dockerの中のrootはホスト環境のrootとは違うユーザーだし、プロセス番号は同じでも違うプロセスだし、TCP portも同様。要は、これぐらい一通りホスト環境から隔離してやればユーザーランドから見ると仮想マシンに隔離されてるも同然だよね、という話。実際にはカーネルはホスト環境のやつを使ってるけど、ユーザーランドからそれを知るすべはほとんどない筈。

新しいDocker

Dockerが新しいのは、コンテナ環境で走らせるアプリケーションを簡単にパッケージする標準的な方法を確立したことにある。 Dockerは、ユーザーランドで使われるGNU/Linuxディストリビューションのファイルシステムひと揃いをメタデータと一緒に固めた形式を定義している。要するに仮想ディスクイメージなんだけど。

また、イメージはしばしば他のイメージから派生する。その場合は元のイメージからの差分だけが保存されるので軽量である。 更に、イメージはgitのコミットと同じ要領で内容のハッシュで識別されることになっている。

これらが合わさると、こういうことになる。アプリケーションを含まないOSだけのbaseイメージ(例えばUbuntu preciseイメージ)はみんなが大抵手元に持っているし、必要であれば公式のリポジトリから簡単にダウンロードできる。 そのベースイメージから派生した自分のアプリケーションインスール済み環境を配布したいとする。 受け取る相手は、今自分が受け取ろうとしているイメージの派生元を辿って行く。既に持っている分は改めて取得する必要はなくて、そこからの差分だけを転送すればよい。同じ内容のイメージは必ず同じハッシュを持つので、既に持っているかどうかは簡単に判別できる。この辺もgitと同じだ。 つまり、ディスクイメージの転送が軽量にできる。

更に、こうして作成された差分ディスクイメージを走らせるときもホスト環境のディスクを無駄に使わず、起動時にマージなんてアホなことをすることもなく、効率的にインスタンス化できるようになっている。

こういう便利なパッケージの仕組みをオープンソースで作った。そしてこの仕組みをみんなが使い始めた。みんなというのはGoogle, Amazon, IBM, Microsoft, Rackspace, ...を含む。

新しいクラウドの話

Dockerはそれ単体で使っても便利なものだ。プロセスを隔離したいときのchrootの不便を解消できる。 Chrootだと、chroot環境のファイルシステムを作るのが結構面倒でみんな自作のスクリプトで頑張ったりしたものだった。Dockerなら簡単にUbuntuでもCentOSでも好きな環境のイメージを作れる。*1

しかし、Dockerが本当に便利なのはクラウド環境との組み合わせで、イメージを他のマシンに転送するときだ。手元のマシンでDockerイメージを作り込んでプロダクション環境に転送、実行してやる。すると、手元で設定したアプケーションがそのままプロダクションで動く。ファイルを配備して、ユーザーを作成して、設定ファイルを弄って、みたいな成果物をそのままの状態で転送できる。もう苦労してプロダクション環境を適切にセットアップしてやる必要はない。つまり、ProvisioningやDeploymentの手順が劇的に簡略化される。 Dockerさえ設定しておけば、あとはイメージをポイッと配布するとそれがそのまま動くのだ。

最初にDockerを軽量仮想マシンぽいものだと言ったけれど、ここに至っては仮想化であるということは手段の1つでしかなくてあまり重要なことではない。重要なのは設定済みのアプケーション動作環境をユーザーランドまるごとパッケージして軽量に移動できるということのほうだ。

そして、これがクラウドの主要ベンダーたちによってサポートされた。

  • GoogleはManaged VMを発表した。ユーザーがアップロードしたDockerイメージがAppEngine相当のmanaged環境で実行される。AppEngine同様の「Googleが運用の面倒を見てくれる」手軽さがありながら、Dockerイメージには任意の言語で任意のアプケーションを入れられる。
  • AmazonはElastic BeanstalkでDockerをサポートした。話としてはManaged VMと同じだ。
  • ...

こうして各社がDockerをサポートした。つまり、クラウド環境で動かすためにアプケーションをパッケージングし転送する標準的な方法が生まれた。もうベンダーロックインを心配しなくていい。他社のサービスへ移りたくなったらそのDockerイメージを移動先に改めてアップロードすれば良い。

libswarm

今回のDockerConでは、もう1つベンダーロックインをなくすための製品が発表された。

なるほど、一個のサービスはDockerで簡単に配備できるし移動もできるかもしれない。しかし、今どきのクラウド上のアプケーションというのは1つのサービスだけで動く訳ではない。データベースに、キャッシュに、バックエンドサーバー、フロントエンドwebアプケーションサーバーなどなど沢山のroleを為すそれぞれの種類のサーバーインスタンスがあって、それぞれを適切に繋げてやらねばならない。しかもクラウド上の話だからしばしばサーバーのIPはクラウドベンダーに割り振られる。IP決めうちであらかじめ設定してからイメージを作るという訳にもいかない。つまり、依存する各サーバーが他のサービスを発見して繋ぎにいくというサービスディスカバリとオーケストレーションの問題が発生する。

この問題に対しては既にいくつものソリューションがある。fleet+etcdだとか、gearだとか。うまく使えばDNSも解になり得るだろう。クラウドベンダーも思い思いに解決法を提供している。 真の問題は、解決法が多すぎることだ。未だに標準的な方法はないし、どこかのベンダーの機能を使えば、サービスのオーケストレーションという要の部分を握られてロックインされる。

そこでDockerはこの度libswarmを発表した。libswarmはこの様々な実装を抽象化したAPIを提供するライブラリだ。 個人的にはlibvirtのオーケストレーション版、という理解をしている。

libswarmによっていよいよクラウドベンダー間の移動は楽になる。もっと安いところ、もっとスケールするところ、などなど必要に応じて他のベンダーに自由に移動できる時代がやってくる。

Kubernetes

そしてKubernetesは更にその一歩先の話、だと思うけどその話は後で書く

Disclaimer このブログはYuguiの個人的なものです。ここで述べられていることはDockerConで聞いた話の個人的理解、および個人的な意見に基づくものであり、私の雇用者には一切の関係はありません。


*1隔離されていて予測可能な状態にある環境というのはビルドやなんかにはとても便利だ。だからDockerのビルドスクリプトはDocker環境の中でDockerをビルドする


トラックバック

http://yugui.jp/articles/880/ping

現在のところトラックバックはありません

コメント閲覧/投稿

2014年02月02日

fluentdを勉強中

最近はlog collectionというとfluentdが話題らしいというので、少し触り始めている。 多少は分かってきたように思うので理解したことと、理解できていないところをまとめてみようと思う。

fluentdとは何か

オフィシャルサイトには"tool to collect events and log"と書いてある。 要するに、サーバー群から継続的に情報を吸い上げるための仕組みを提供するdaemonである。

この「情報をかき集める」という枠組みは今時のサーバーサイドでは頻出パターンだ。 データベースサーバ、アプリケーションサーバー、フロントエンドキャッシュサーバー, ...と役割分担をするのが普通だし、 アプリケーションサーバーだって負荷分散のために複数インスタンスを持つのが当たり前だ。 そしてこれらサーバー群をきちんと管理するためには様々な情報をかき集めてきて一カ所に保存したり集約したりする必要がある。 たとえば、

  • アプリケーションサーバーのログを全インスタンスから集めて一覧可能にする
  • キャッシュサーバーのヒット情報を集めて解析する
  • 各マシンのディスク空き容量を定期的に集めて不足しそうなら警告を出す

これらはいずれも一見するとそれほど難しい処理には思えない。scpとcronでもできそうだ。 しかしデータ転送の際の失敗をどうやって扱うのかを考えだすと、自明ではなくなる。 定時バッチ処理ではなくリアルタイムに処理し続けるようにしたいと考えると問題はさらに難しくなる。 失敗時は適切な時間を置いて再試行するように。通信エラーで情報が失われたりしないように。

fluentdはこの頻出パターンを司り、上記のような困難を処理してくれる。 そして、パターンの個々のバリエーションに対応できるようにデータ入力、加工、出力のそれぞれをプラグインなどで拡張可能になっている。

また、異なるデータソースからの情報をバラバラにあつめるのではなくfluentdという1つのシステムにつなぐことそのものにも意味がある。 データソースをまたいだリアルタイムの解析も容易になる。

よく分かってないこと1: システム監視

fluentdだけでモニタリングシステムを構築できるだろうか。すべきだろうか。

情報をかき集めて変換するという点でシステム監視はfluentdのカバーする範囲に思える。 一方でシステム監視の世界ではnagiosZABBIXももた話題になっているようだ。 こういうのとfluentdは棲み分けるべきなんだろうか。それともfluentdに集約してしまうべきなんだろうか。

可能ならシステムの中に似たようなモジュールは2つ置きたくない。fluentdを中心としたモニタリングは可能そうだし、実際に構想している人もいる。 でも、もし餅は餅屋としてシステム監視専門のモジュールならではのfluentdでカバーできない利点があるならそれは知りたい。

よく分かってないこと2: アーキテクチャ

Sensuやなんかだと、データ転送をreliableにするためにRabbitMQを使っている。 一方fluentdのforwardingは特にそういうミドルウェアは使ってない。fluentd自身ががんばってエラー対応している。

こういうのは別途ミドルウェアでqueueを使うものだと言う思い込みがあったのでfluentdのやり方は見慣れない感じがする。 この違いはどういう設計判断によるものなんだろう。

お返事

疑問を書いてみたら偉い人からお返事来た。

MQ使わないのはやっぱりそういうことか。モニタリングは、うん、もうちょっと考えてみよう。

トラックバック

http://yugui.jp/articles/879/ping

現在のところトラックバックはありません

コメント閲覧/投稿

2013年11月30日

環境に優しい難読コード

ぼちぼち忘年会やクリスマス会のシーズンとなってきた。

そんなパーティーの1つに出欠の連絡をしなければならない場面があったのだけど、どうやらネタ回答を期待されているらしかったので次のように回答した。

puts [[->(&f){->(n){f[f[f[f[f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]]]]]}},->(&f){->(n){f[f[f[f[f[n]]]]]}},
->(&f){->(n){f[f[f[f[f[f[f[f[n]]]]]]]]}},->(&f){->(n){f[f[f[f[f[f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]]]]]]}},
->(&f){->(n){f[f[f[f[f[f[f[f[n]]]]]]]]}},->(&f){->(n){f[f[n]]}},->(&f){->(n){f[f[f[f[f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]]]]]}},
->(&f){->(n){f[f[f[f[f[n]]]]]}},->(&f){->(n){f[f[f[f[f[f[f[f[n]]]]]]]]}},->(&f){->(n){f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]}},
->(&f){->(n){f[f[f[f[f[f[f[f[f[f[n]]]]]]]]]]}},->(&f){->(n){n}}].map{|x|x[&:succ][0]}.map(&("%x".method("%"))).join].
pack("H*").unpack("U*").pack("U*").encode(Encoding.default_external)

難解コードとかゴルフとかは苦手なんだけど、相手のためを思って知恵を絞ればできるもんだね。

難読コーディングとしては割と定番の素材を使っていて読解難度はそう高くない。ただ、呼び出し可能オブジェクト技法の数々や、表示側のエンコーディングに依存しない環境(env(1)的な意味で)に優しい設計でありつつ"Encoding::UTF_8"と書くのはあくまでも避けたあたりについては我ながら満足している。

トラックバック

http://yugui.jp/articles/878/ping

現在のところトラックバックはありません

コメント閲覧/投稿

ご案内

最近の記事

タグ一覧

過去ログ

  1. 2014年06月
  2. 2014年02月
  3. 2013年11月
  4. 2013年08月
  5. 過去ログ一覧

フィード

フィードとは

その他

Powered by "rhianolethe" the blog system