Help us understand the problem. What is going on with this article?

そろそろ理解しておきたいのでDockerのソースコードをビジュアルに読む!

More than 5 years have passed since last update.

docker2.png

まずはツールの紹介

昨今、注目を浴びているアプリケーションコンテナを作ったり管理したりできるDocker。
そろそろ中身も理解しておきたいところ。特に最近はlibcontainerというのができて、lxc依存すら切り離されているとの噂。

ただ、結構ソースコードの量も大きくなっているので(2014/4月現在 テストコード除いて70000行弱)、
さっと大まかに理解するためのツールを書いてみました

このツールはgoのastからパッケージの間の依存関係を読んで、graphvizの解釈するdot言語に吐き出すツールです。

たとえば、最近話題のgo-xslateの全体像をつかもうと思ったら、

goviz -i github.com/lestrrat/go-xslate | dot -Tpng -o xslate.png

のようにすることで、
xslate.png
拡大

こんな感じにパッケージ間の依存関係を図示することができます。
これだけでも何となくどんな構造になってるかわかってきそうですね。

不安定性の表示

govizにはdot言語の出力の他にinstabilityの出力ができます。instability(不安定性)とは、詳しい説明はwikipediaに任せるとして、この数値が高い方がアプリケーション本体に近く、低い方が共通ライブラリとしての性質が強いものだとわかります。

goviz -i github.com/dotcloud/docker/docker -m | head                                          (r:0 s:5 j:0)
Inst:1.000 Ca(  0) Ce(  9)  github.com/dotcloud/docker/docker
Inst:0.960 Ca(  1) Ce( 24)  github.com/dotcloud/docker/pkg/libcontainer/nsinit
Inst:0.956 Ca(  2) Ce( 43)  github.com/dotcloud/docker/runtime
Inst:0.950 Ca(  1) Ce( 19)  github.com/dotcloud/docker/api/client
Inst:0.950 Ca(  1) Ce( 19)  github.com/dotcloud/docker/server
Inst:0.909 Ca(  1) Ce( 10)  github.com/dotcloud/docker/api/server
Inst:0.867 Ca(  2) Ce( 13)  github.com/dotcloud/docker/runtime/execdriver/native
Inst:0.857 Ca(  1) Ce(  6)  github.com/dotcloud/docker/runtime/graphdriver/devmapper
Inst:0.833 Ca(  1) Ce(  5)  github.com/dotcloud/docker/runtime/graphdriver/aufs
Inst:0.800 Ca(  1) Ce(  4)  github.com/dotcloud/docker/builtins

ソースコードをトップダウンで理解していく場合、これらの上位にあがっているパッケージを理解していけば大まかな挙動を理解することができるでしょう。

全体像

govizですべてのパッケージを解析する場合、複雑になりすぎてしまうので解析する深さを指定します。

goviz -i github.com/dotcloud/docker/docker -s github.com/dotcloud/docker -d 2 | dot -Tpng -o docker2.png

docker2.png拡大

これをみるとclient、buildins、sysinitがそれぞれトップレベルの依存関係にあることがわかります。
実際、docker.goを読んでみると

    if selfPath := utils.SelfPath(); strings.Contains(selfPath, ".dockerinit") {
        // Running in init mode
        sysinit.SysInit()
        return
    }
    /*中略*/
    if *flDaemon {

        eng, err := engine.New(realRoot)
        builtins.Register(eng)
        /* ry */
    }else {

       /*中略*/
            cli = client.NewDockerCli(os.Stdin, os.Stdout, os.Stderr, protoAddrParts[0], protoAddrParts[1], &tlsConfig)
       /*後略*/
    }

となっていて、

  • sysinitとして機能する場合
  • builtinsとして機能する場合
  • clientとして機能する場合

があることがわかります。

sysinit以下をみる

では、まずsysinitから。

goviz -i github.com/dotcloud/docker/sysinit -s github.com/dotcloud/docker -d 2 | dot -Tpng -o sysinit.png

sysinit.png
拡大

execdriver以下にnativeとlxcがあります。この辺りをプラガブルにすることで、lxcがない環境でも動くようにしているのでしょう。

import (

    "github.com/dotcloud/docker/runtime/execdriver"
    _ "github.com/dotcloud/docker/runtime/execdriver/lxc"
    _ "github.com/dotcloud/docker/runtime/execdriver/native"

)

sysinitが実行されるフェーズでは、
実際にはこのようにlxcとnativeパッケージは読み込まれているだけで、それぞれのinit関数で定義された初期化の処理を行っているようです。

builtins以下をみる

goviz -i github.com/dotcloud/docker/sysinit -s github.com/dotcloud/docker -d 2 | dot -Tpng -o sysinit.png

builtins.png

拡大

これをみるとbuiltinの機能としてはnetworkdriver/bridgeとapi/serverとserverの3つの依存を持っているようです。

builtins.goには次のようなコメントがありました。

builtins.go
// daemon: a default execution and storage backend for Docker on Linux,
// with the following underlying components:
//
// * Pluggable storage drivers including aufs, vfs, lvm and btrfs.
// * Pluggable execution drivers including lxc and chroot.
//
// In practice `daemon` still includes most core Docker components, including:
//
// * The reference registry client implementation
// * Image management
// * The build facility
// * Logging
//
// These components should be broken off into plugins of their own.
//

これをよむとdaemonはプラガブルなストレージと実行環境を提供するもので、
それぞれ選べるようにしたいのだけど、まだ依存が切れてないよというようなことらしいです。

client以下をみる

goviz -i github.com/dotcloud/docker/api/client -s github.com/dotcloud/docker -d 2 | dot -Tpng -o c.png 

c.png

拡大

これをみると、様々なコマンドをdocker/engineを通じて、実行しているのではないかと推察できます。

ボトムアップに調べてみる

これまでちょくちょく出てきたdocker/engineが気になってきました。
govizでは特定のモジュールに関して、依存関係を逆にたどった図を作成することができます。
docker/engineがどのように依存されているのかをたどってみましょう。

goviz -i github.com/dotcloud/docker/docker -s github.com/dotcloud/docker/ -f github.com/dotcloud/docker/engine | dot -Tpng -o d.png

d.png
拡大

このように特定のパッケージがどこから使われているかということをmainまでたどることで、engineがclient、serverをつなぐための仕組みではないかと予想ができます。

engine.go
// The Engine is the core of Docker.
// It acts as a store for *containers*, and allows manipulation of these
// containers by executing *jobs*.

適当に訳すと、
EngineはDockerのコアです。これはコンテナのストレージとして振る舞ったり、ジョブの実行を通じてそれらのコンテナを操作します。

ということらしいです。

execdrivers

そういえば、さきほどの初期化されたexecdriverはどのように実行されるのでしょうか。
native、lxcのファクトリとしてexecdriversがあったのでこれを逆順にたどってみましょう。

d.png

拡大

すごくシンプルです。runtimeを通じて実行されると。
では、nativeとlxcがどのようになっているか見ていきましょう。

execdrivers.go
func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
    switch name {
    case "lxc":
        // we want to five the lxc driver the full docker root because it needs
        // to access and write config and template files in /var/lib/docker/containers/*
        // to be backwards compatible
        return lxc.NewDriver(root, sysInfo.AppArmor)
    case "native":
        return native.NewDriver(path.Join(root, "execdriver", "native"), initPath)
    }
    return nil, fmt.Errorf("unknown exec driver %s", name)
}

ここのコメントの意味つかみかねてるんで、誰か教えてください。

native( libcontainer )

c.png
拡大

ようやく話題のlibcontainerが出てきました。では依存関係を逆にたどってみましょう。

c.png

拡大

lxc

次は現役を退いたlxc。

c.png
拡大

まとめ

比較的規模の大きいツールを読むときには、ソースコード解析ツールを作ると便利。

rector
CTO経験者のみで構成された「技術組織」をよくするための会社です。 パフォーマンスの高い技術組織を作るためのサポートアドバイザリー、コンサルティング、診断パッケージ、研修などを取り扱っています。
http://rector.co.jp
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした