Upgrade to Pro — share decks privately, control downloads, hide ads and more …

overview of overlayfs

akachochin
October 29, 2016

overview of overlayfs

This slide share the overview of overlayfs.
- what feature it privides with some images.
- go through Linux filessytem code.
It would be useful to get outlines of overlayfs.
(This slide is written in Japanese, but images and codes in this slide would make it easy to get outlines.)

akachochin

October 29, 2016
Tweet

More Decks by akachochin

Other Decks in Technology

Transcript

  1. 使用上の注意です。 • 読んだソースは、 linux_4.4.0-21.37 (Ubuntu16.04) です。 自宅で主に使っているのが Ubuntu16.04 で、 ftrace

    した結果 と突き合わせるといろいろはかどるからです。 • プレゼンの時間的な制約などから、細かいことは説明しきれま せん。なので、厳密さよりもわかりやすさを優先したり、話を 端折ったりしている箇所が結構あります。ご了承のほどを。
  2. Overlayfs って何? この章を書くにあたり、「 LXC で学ぶコンテナ入門 -軽量仮 想化環境を実現する技術第 18 回  Linux

    カーネルのコンテナ 機能 [7]overlayfs 」という記事を大いに参考としました。 記事を書かれた加藤さん、大変感謝です。
  3. Overlayfs って何? Overlayfs は Union filesystem の一種です。 Union filesystem は複数のファイルシステムをひとつの場所で

    マウントし、仮想的にまとめてあたかもひとつのファイルシス テムであるかのように見せる技術です。 Linux 3.18 のときに Linux カーネルにマージされました。 また、 Docker では 1.4.0 で overlayfs に対応しました。
  4. Overlayfs って何? とはいっても、イメージしづらいので、 Docker のコンテナを 例に説明します。 ・ Docker の Image

    Layers は ReadOnly なファイルシステム 層を参照しています。 ・ Docker がコンテナを作ると き、 Image の上に Writeable な層 ( コンテナ層 ) が追加さ れます。 ※https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/ より図を引用
  5. Overlayfs って何? Overlayfs に戻ります。 Overlayfs 視点では、各レイヤなどの 名称は以下のとおりとなります。 ※https://docs.docker.com/engine/userguide/storagedriver/overlayfs-driver/ より図 を引用

    Docker での呼び名 overlayfs での呼び名 下の Read Only な層 Image Layer lowerdir 上の writable な層 Container Layer upperdir マージした結果 Container mount merged(Overlayfs から 見たときに見えるもの )
  6. Overlayfs って何? lower から upper へコピーして・・ ・ (copy up) lower

    upper A overlay B A B B Write Write upper 側へ 書き込み overlayfs 経由でファイルへの書き込みを行うと・・・ • upper 側→ upper 側のファイルが書き換わる • lower 側→ lower 側から upper 側にファイルがコピーされ、 upper 側が書き換 わる
  7. Overlayfs って何? overlayfs 経由でファイルの削除を行うと・・・ • upper 側→ upper 側のファイルが削除される •

    lower 側→ upper 側に「削除」を示すファイルを生成、 lower 側ファイルを「覆 う」。 lower upper A overlay B A B 削除 削除 「削除」を示すファイルが作成さ れ、 lower のファイルが「見えなく なる」。 (whiteout)
  8. Overlayfs って何? ここまで見たことをまとめます • overlayfs は、 lowerdir と upperdir をひとつのマウントポイ

    ントでマウントし、その中で仮想的にマージして見せます。 • 下に Read Only なファイルシステム層 (lowerdir) を積み重ね ます。 • lowerdir の上に書き込み可能なファイルシステム層 (upperdir) をひとつ置きます。 • ファイルシステムへの変更は upperdir で吸収します。
  9. Overlayfs を使う準備 Overlayfs をマウントする際、以下のようなコマンドを実行し ます。 mount -t overlayfs -o \

    lowerdir=lower,upperdir=upper,workdir=work \ overlayfs mountpointdir 少し複雑ですので、ひとつずつ見ていきましょう ( 「 \ 」は行変更を示します。念のため。 )
  10. Overlayfs の使い方 -o \ lowerdir=lower,upperdir=upper,workdir=work オプションをカンマ区切りで指定します。指定するオプショ ンは以下のとおりです。 オプション 概要 lowerdir

    lowerdir として重ねあわせるファイルシステムイメージ ( ディレクトリ ) のパスを指定。複数指定可能。 upperdir upperdir として重ねあわせるファイルシステムイメージ ( ディレクトリ ) のパスを指定。書き込み可能なローカ ルのファイルシステムの必要がある。 workdir overlayfs が使う作業ディレクトリ。 upperdir と同じフ ァイルシステムの名前空間内に存在する必要あり。 upperdir を指定する際は、セットで指定。
  11. Overlayfs の動き 今回は以下のディレクトリ構成で動きを見ます。 lowerdir upperdir upfile updir workdir overlay lowfile

    lowdir 注:今回は実験を簡単にするた めに、すべてのディレクトリ ・ファイルは同一の名前空間 内の ext4 内にあります。
  12. Overlayfs の動き - ls( ディレクトリを読む ) - overlayfs のマウントをしたあとで、 ls

    コマンドを使 い、 overlay の中を見ます。 すると、 upperdir と lowerdir がマージされたものが見えま す。 lowerdir upperdir upfile updir overlay lowfile lowdir upfile updir lowfile lowdir
  13. Overlayfs の動き - ls( ディレクトリを読む ) - ls を行う際、ディレクトリの中身を読むために libc

    の readdir() が呼ばれます。 glibc の場合、 readdir() を経由して、 getdents というシス テムコールが呼ばれます。 getdents は最終的に iterate_dir() という関数を呼び出しま す。この関数は 2 つの引数を受け取ります。
  14. Overlayfs の動き - ls( ディレクトリを読む ) - int iterate_dir(struct file

    *file, struct dir_context *ctx) { /* パラメータチェックなどは割愛 */ /* ディレクトリ内の最後のエントリに到達するまで */ if (!IS_DEADDIR(inode)) { /* 前処理は割愛 */ res = file->f_op->iterate(file, ctx); /* 後処理も割愛 */ } return res; } ここで、 dir_context 構造体 (ctx) は以下 2 点が役目です。 - ディレクトリの中をたどるごとに呼び出す処理を関数ポインタで渡す - 関数ポインタ経由で渡した処理が都度の結果を格納する
  15. Overlayfs の動き - ls( ディレクトリを読む ) - ここで、 file->f_op->iterate() 経由で

    ovl_iterate() が呼ばれます。こ れは Overlayfs 依存の関数です。 最初に、マージの必要がないケースを処理します。 static int ovl_iterate(struct file *file, struct dir_context *ctx) { /* 略 */ /* * 該当ディレクトリは重ねあわせたファイルシステム間で * 重複していなくて、マージの必要がないディレクトリである * この場合、そのディレクトリの実体をそのまま読みだす。 */ if (od->is_real) { /* 略 */ return iterate_dir(od->realfile, ctx); }
  16. Overlayfs の動き - ls( ディレクトリを読む ) - 次に、 upper 側と

    lower 側で同名のディレクトリがあり、マージが必要な ケースを処理します。該当ディレクトリに対するマージがこれまでにされ ておらず、結果が再利用できないケースの処理です。 /* 前ページからの続き */ /* マージが必要だが、マージ結果のキャッシュがない */ if (!od->cache) { struct ovl_dir_cache *cache; /* マージを行い、その結果を「キャッシュ」とする */ cache = ovl_cache_get(dentry); /* 略 */ }
  17. Overlayfs の動き - ls( ディレクトリを読む ) - 最後に、得られた結果 ( キャッシュ

    ) をたどり、ユーザから渡されたバッ ファにコピーします。コピー自体は ctx の関数ポインタで渡された filldir で実施します。 /* 前ページの続き */ /* 辿れるだけディレクトリの中身をたどる */ while (od->cursor != &od->cache->entries) { p = list_entry(od->cursor, struct ovl_cache_entry, l_node); /* 「消された」ことになっていない場合、 */ if (!p->is_whiteout) /* 呼び出し元から渡された ctx の中の関数ポインタを呼ぶ */ if (!dir_emit(ctx, p->name, p->len, p->ino, p->type)) break; /* 略 */
  18. Overlayfs の動き - ls( ディレクトリを読む ) - 関数ポインタ (filldir) があるため、呼び出し構造がややわかりにくいで

    す。そこで、ここまでの話を以下の概要図でまとめました。 ユーザ側 ユーザ側 iterate_dir iterate_dir filldir filldir ovl_iterate ovl_iterate 該当レイヤの iterate 該当レイヤの iterate マージの必要が ないケースの呼び出し マージの必要が あるケース ディレクトリの中身を ユーザ側バッファに コピー ディレクトリの中身を ユーザ側バッファに コピー
  19. Overlayfs の動き - upper 側 file への write - upper

    側にあるファイルを Overlayfs 経由で書き換える と、 upper 側のファイルが直接書き換わります。 では、これをどうやって実現しているのでしょうか。 ここで、カーネルのソースを読んでみたいと思います。
  20. Overlayfs の動き - upper 側 file への write - upper

    側にあるファイルを書き込み可能で open() します。 open() からシステムコールを経由して、 vfs_open() が呼び出され、関数 ポインタ経由で ovl_d_select_inode() が呼ばれます。 int vfs_open(const struct path *path, struct file *file, const struct cred *cred) { /* 略 */ if (dentry->d_flags & DCACHE_OP_SELECT_INODE) { inode = dentry->d_op->d_select_inode(dentry, file- >f_flags); if (IS_ERR(inode)) return PTR_ERR(inode); }
  21. Overlayfs の動き - upper 側 file への write - ovl_d_select_inode()

    の中で ovl_path_real() を呼び出します。これによ り、実体 ( ここでは upper 側のファイル ) のパス情報 (struct path) を取 得します。 struct inode *ovl_d_select_inode(... 引数省略 ...) { /* 略 */ type = ovl_path_real(dentry, &realpath); if (ovl_open_need_copy_up(... 引数省略 ...)) { /* 後述 */ } /* 略 */ /* upper 側の inode を返すことで、呼び出し元で upper 側のファイル を open() する。これで以後のアクセスは upper 側に行われる */ return d_backing_inode(realpath.dentry); }
  22. Overlayfs の動き - lower 側 file への write - lower

    側にあるファイルを overlayfs 経由で書き換える と、 upper 側に書き換え対象と同名のファイルが生成さ れ、 upper 側のファイルが書き換わります。 これにより、以後は upper 側の「書き換えられた」ファイル が見え、 lower 側ファイルは書き換わりません。 では、これをどうやって実現しているのでしょうか。 ここで、カーネルのソースを読んでみたいと思います。
  23. Overlayfs の動き - lower 側 file への write - vfs_open()

    自体の動きは、先に説明した upper 側にあるファ イルへの書き込みと同様です。 しかし、 d_select_inode 経由で呼ばれる ovl_d_select_inode の挙動が異なります。 それは、 copy_up という処理が行われることです。
  24. Overlayfs の動き - lower 側 file への write - struct

    inode *ovl_d_select_inode(... 引数略 ...) { /* 略 */ type = ovl_path_real(dentry, &realpath); if (ovl_open_need_copy_up(... 引数略 ...)) { /* 略 */ /* ここで lower 側にあるファイルを upper 側にコピーします */ err = ovl_copy_up(dentry); /* 略 */ /* * ファイルを upper 側にコピーした後でパス情報を返し、 * 書き込み処理が upper 側ファイルに行われるようにする */ ovl_path_upper(dentry, &realpath);
  25. Overlayfs の動き - lower 側 file への write - ovl_copy_up

    では以下の図のようなことを実施します。 わざわざ workdir を中継する理由は、変更をアトミック ( 「完全失敗で何 もない」か、「完全成功でファイルが upper 側に生成される」かのどちら か ) にしたいためと推定します。 ( 煩雑さを避けるため、詳細については 相当略しています。 ) lower upper 手順 2. workdir 内のファイル をリネームする 手順 1. 一旦 workdir にコピー workdir upper と lower は 異なるファイルシステム。 そのため、コピー失敗の リスクもある。 そこで、 workdir を中継。 そうすることで失敗時の 変更は workdir にとどまり、 upper から残骸などが 見えない。
  26. Overlayfs の動き - lower 側 file の削除 - lower 側にあるファイルを

    overlayfs 経由で削除しても lower 側のファイルは消えません。その代わりに、「 whiteout 」と いう処理が upper 側に行われ、あたかも該当ファイルが消え たかのように見せかけます。 では、これをどうやって実現しているのでしょうか。 ここで、カーネルのソースを読んでみたいと思います。
  27. Overlayfs の動き - lower 側 file の削除 - ファイルを削除する際、 unlinkat()

    システムコールを呼びま す。この関数は最終的に vfs_unlink() を呼び出します。 vfs_unlink() では関数ポインタ経由で ovl_unlink() を呼び出 します。 さらに ovl_unlink() では ovl_do_remove() を呼び出します。
  28. Overlayfs の動き - lower 側 file の削除 - Overlayfs を経由したファイル削除には、以下の

    3 パターンが あります。 lower upper A overlay B A B C 削除 削除 C 削除 パターン1 パターン 2 パターン 3 C
  29. Overlayfs の動き - lower 側 file の削除 - static int

    ovl_do_remove(struct dentry *dentry, bool is_dir) { /* 略 ( 大切な処理も含むが、煩雑さを避けるため略 */ type = ovl_path_type(dentry); /* upper 側にだけあるファイルは、 upper 側ファイルを直接消す */ if (OVL_TYPE_PURE_UPPER(type)) { err = ovl_remove_upper(dentry, is_dir); /* lower 側にも該当ファイルがある場合、 whiteout 処理を実行 */ } else { /* 略 */ err = ovl_remove_and_whiteout(dentry, is_dir); /* 略 */ } パターン 1. upper 側を直接削除
  30. Overlayfs の動き - lower 側 file の削除 - whiteout 処理とは、「

    upper 側に特殊なファイルを作成し、 あたかも lower 側にあるファイルが消されたかのように見せ かける」処理です。先のパターン 2,3 の場合、必要です。 やり方は Overlayfs のバージョンによって異なります。 Overlayfs のバージョ ン 処理概要 V1 消そうとしているファイルと同名のシンボリック リンクを upper 側に作り、拡張属 性 "trusted.overlay.whiteout=y" を付与する。 v2 消そうとしているファイルと同名の charcter device file(major0:minor0) を upper 側に作成す る
  31. Overlayfs の動き - lower 側 file の削除 - static int

    ovl_remove_and_whiteout(struct dentry *dentry, bool is_dir) { /* 略 */ if (is_dir) { /* ディレクトリを消す場合の処理。今回は略 */ } /* 略 */ /* ここで whiteout 処理を実施 */ whiteout = ovl_whiteout(workdir, dentry); /* 略 */ upper = ovl_dentry_upper(dentry);
  32. Overlayfs の動き - lower 側 file の削除 - /* lower

    側にのみ消去対象のファイルがある場合 ( パターン 2) */ if (!upper) { /* * upper 側にディレクトリエントリを作成する。 * "lookup" なのだが、名前とディレクトリで検索して見つ * からない場合、 inode が空のディレクトリエントリが返る。 * ( ここでは新規に whiteout のためのファイルを生成したと * 考えれば良い ) */ upper = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len); /* 略 */ err = ovl_do_rename(wdir, whiteout, udir, upper, 0); /* 略 */
  33. Overlayfs の動き - lower 側 file の削除 - /* *

    少なくとも upper 側に消去対象のファイルがある場合 * (upper 側と lower 側の双方にあるケース。パターン 3.) * 単純に upper 側のファイルを消すと、重ね合わせにより lower * 側のファイルが見えてしまい「消せていない」状態になる。 * よって、 upper 側のファイルを whiteout のファイルに置き換える */ } else { /* 略 */ err = ovl_do_rename(wdir, whiteout, udir, upper, flags); /* 略 */ } /* 略 */
  34. ソースを自分で読みたい人のために いくつかアドバイスです。 1.overlayfs 内に現れる dentry 構造体がどこのレイヤの何の ファイル / ディレクトリを指しているのか意識しましょう。 2.Linux

    のファイルシステム層について基本的な事項を学んで みましょう。日本語で入手できる資料では、「 Linux カーネ ル解読室」第 15 〜 17 章が最も助けになると思います。 3.ftrace を使って、カーネル内関数呼び出しを追いかけまし ょう。非常にはかどります。
  35. ソースを自分で読みたい人のために 4.Linux ファイルシステム層のよく使われる関数 ( 特に lookup 系 ) に慣れましょう。 そして、挙動がわからなければ、ソースを読んでみましょ

    う。そうすることによってファイルシステムの実装に慣れて きます。 5. データ構造をメモりながら読みましょう。今回紹介しませ んでしたが、 mount 処理 ,lookup 系処理を読むと、全体感が 捉えやすくなるでしょう。
  36. 参考文献・ Web ページ Web ページ • 「 LXC で学ぶコンテナ入門 -軽量仮想化環境を実現する技術

    第 18 回  Linux カーネルのコンテナ機能 [7]overlayfs 」 http://gihyo.jp/admin/serial/01/linux_containers/0018 • 「 Understand images, containers, and storage drivers 」 https://docs.docker.com/engine/userguide/storagedriver/imagesandcontainers/ • そのファイル、安全に更新できていますか?(アトミックなフ ァイル操作:前編) https://heartbeats.jp/hbblog/2013/10/atomic01.html
  37. 参考文献・ Web ページ 文献 ( 敬称略 ) • 「 Linux

    カーネル 2.6 解読室」 高橋浩和 / 小田逸郎 / 山幡為佐久 著 • 「プログラマのための Docker 教科書」 阿佐志保 著 • 「 Understanding the Linux Kernel(3rd edition) 」 DANIEL P.BOVET & MARCO CESATI 著 • 「 Linux Kernel Development(3rd Edition) 」 Robert Love 著