Flatt Engineering Blog

We are software engineers working in Flatt. We share our product updates and tech tips.

Googleが発表したコンテナランタイムサンドボックスgVisorについて発表してきたのでまとめてみた

はじめに

はじめまして、Flatt., Inc. でCEO兼週末エンジニアをやっている @niconegoto です。

今回扱うgVisorはKubeCon2018でローンチが発表された、従来のコンテナよりも安全性を高めたコンテナランタイムであるrunscを含むプロジェクトのことです。 Googleの基盤技術と目されており、Java8/Node用の新たなGAEはgVisor上で動いていることが発表されているなど、普段業務でGAEを使用しているFlattとしても注目の技術です。

以下のツイートのようなgVisorがGoogleの基盤技術であることを匂わせる発言も数々出ており、またGoogle社内で長期間運用された技術が「やっと時代がGoogleに追いついてきたか」という形で世に出てきた事になります。

*テクニカルな内容については追って論文を発表するからそれを待ってね、と書いてあったので論文以外で現状分かっている範囲での情報になります。

github.com

今回はそんなgVisorについてgolnag.tokyo#18にて発表してきたので、補足をしながらまとめていきたいと思います。

資料は以下です。

speakerdeck.com

間違いや表現が悪い部分などもあるかと思いますので見つけたらぜひ @niconegoto までお知らせください。

gVisorとは

READMEには以下のように説明されています。

gVisor is a user-space kernel, written in Go, that implements a substantial portion of the Linux system surface. It includes an Open Container Initiative (OCI) runtime called runsc that provides an isolation boundary between the application and the host kernel. The runsc runtime integrates with Docker and Kubernetes, making it simple to run sandboxed containers.

gVisor takes a distinct approach to container sandboxing and makes a different set of technical trade-offs compared to existing sandbox technologies, thus providing new tools and ideas for the container security landscape.

gVisorは、Goで書かれたユーザスペースカーネルであり、Linuxシステムの大部分を実装しています。 runscというOCI(Open Container Initiative)ランタイムが含まれており、アプリケーションとホストカーネルの間の分離境界を提供します。 runscランタイムはDockerとKubernetesと統合されており、サンドボックス化されたコンテナを簡単に実行できます。

gVisorはコンテナのサンドボックス化とは明確なアプローチをとり、既存のサンドボックステクノロジとは異なる技術的なトレードオフを行い、コンテナセキュリティの新しいツールやアイデアを提供します。

この文章を理解するためには、まず従来のコンテナがどうであったかを理解する必要があります。

f:id:niconegoto:20180602191229p:plain

セキュリティ的に課題があったのが上記の図における赤い部分です。 従来のコンテナ技術では単一のホストカーネルを使用することによってパフォーマンスを向上させていたのですが、それによって単一の脆弱性からホストが攻撃されてしまう危険性があり、多くの問題が報告されていました。

issue.k8s.io

ホストのファイルシステムをマウントされてしまうバグ
>

runc

次に runc という単語が気になるかと思います。 上記の図における runccontainerdはDockerを小さくて再利用可能なコンポーネントに分解するために導入されたもので、 Open Container Initiative (OCI)が策定したコンテナに関する標準規格に基づいています。

f:id:niconegoto:20180527135227p:plain 引用元: Docker 1.11: The first runtime built on containerd and based on OCI technology - Docker Blog

runc というものが今デフォルトで利用されているランタイムなのですが、 runc の部分をよりセキュアに(ホストカーネルと隔離して)実行させようとしているのが runsc であり、それを含むのが今回発表されたgVisorです。

OCIの策定した規格については以下の資料が詳しいのでぜひ。

github.com

ホストカーネルとの分離

よりセキュアに実行するためにはホストカーネルとの分離が必要となったわけなのですが、これまでもいくつかのアプローチが存在しました。 それが

  • マシンレベル仮想化(Machine-level virtualization)
  • ルールに基づく実行(Rule-based execution)

です。

マシンレベル仮想化(Machine-level virtualization)

例) KVMXen

仮想マシンモニタ(VMM)を介して仮想化されたハードウェアをゲストカーネルに公開する方法です。 非常にセキュアですが、パフォーマンス面で大きなオーバーヘッドが存在してしまいます。

f:id:niconegoto:20180527113237p:plain

ルールに基づく実行(Rule-based execution)

例) seccomp、SELinux、AppArmor

アプリケーションまたはコンテナの細かいセキュリティポリシーを指定できます。 しっかりセキュリティーポリシーが設定できれば非常に協力なのですが、初見のアプリケーションなどでそれを完璧に定義することはほぼ不可能です。 上記のような理由から、Rule-based executionは単体で用いられることは少なく、複合的に使用してさらにセキュアにすることに用いられており、実際に今回のgVisorでもこのRule-based executionの機構を併せ持っています。

f:id:niconegoto:20180527113409p:plain

gVisor

ではgVisorはどうなのでしょうか。

gVisorはユーザーアプリケーションのシステムコールを受け取り、仮想化されたハードウェアを通すことなく、ゲストカーネルとして機能します。 このアーキテクチャによって、仮想化によるコストを削減しながら、柔軟なリソースフットプリント(スレッドおよびメモリマッピングに基づく意味での)を提供することができます。 (gVisorのアプローチはUML(User Mode Linux)と似ていますが、UMLはハードウェアを内部的に仮想化しています。)

ただし、注意すべきはgVisorが万能なわけでなく、これまでとは違った形での技術的トレードオフを行っているということです。 gVisorは上記の様なメリットを享受する代わりに、アプリケーションの互換性の低下とシステムコールのオーバーヘッドの増加という犠牲を払っています。

gVisorが苦手とするシステムコールの負荷の大きいワークロードに関してはパフォーマンスが低下する可能性があるため、マシンレベルの仮想化を使用した方が良い可能性があるという事です。

gVisorは何をしているのか

f:id:niconegoto:20180608112319p:plain

簡単にまとめると以下の様になります

① アプリケーションのシステムコールインターセプト(ptrace)

システムコールをフィルターして制限(seccomp)

システムコールを置換(gVisor)

gVisorは、アプリケーションによって行われたすべてのシステムコールインターセプトし、それらを処理するために必要な作業を行います。gVisorは単にアプリケーションシステムコールをホストカーネルにリダイレクトするだけではなく、システムコールを置換します。 VMMと同様に、ユーザーアプリケーションがシステムコールを直接制御することはできません。

コンテナランタイムやオーケストレータを非rootで動かす流れは以前からあり、runcは2016年からrootlessを採用していましたがユーザー空間での実行ができていませんでした。

SentryとGofer

f:id:niconegoto:20180608120345p:plain

gVisorコンテナランタイムは

  • Sentry
  • Gofer

の2つの別々のプロセスに分割されます。

まず、Sentryプロセスはカーネルを含み、ユーザコードの実行とシステムコールの処理を担当します。 Sentryは空のユーザー名前空間で実行され、gVisorによってホストに対して行われたシステムコールは、seccompフィルタを使用して制限されています。

サンドボックスを越えたファイルシステム操作は、9P(Plan 9 Filesystem Protocol)接続を介してGoferと呼ばれるプロキシに送られます。 GopherとGofer発音一緒だしなんでこんな名前にしたんだ…

Goferは、アプリケーションに代わってホストファイルを開き、ホストファイルアクセス自体を持たないSentryプロセスに渡すことによって、ファイルシステムのプロキシとして機能します。

ネットワークアクセス

Sentryは、netstackという独自のネットワークスタックを実装しています。

github.com

ネットワークスタックは全てSentry内部で処理され、ホストネットワークスタックから隔離されます。 データリンク層のパケットは、DockerまたはKubernetesによって設定されたネームスペース内の仮想デバイスに直接書き込まれます。

プラットフォーム

Sentryは、基本的なコンテキスト切り替えとメモリマッピング機能を実装するプラットフォームを必要としており、PtraceとKVMに対応しています。 ptraceのパフォーマンスがネックになっているので、現在試験的に導入されているKVMが主流のプラットフォームになっていくのではないかと言われており、このあたりはまだ改善の余地があるところでしょう

そのほかにもLinuxシステムコールで未実装のものがあるなど、発展途上のgVisorですが、この記事を読んだ皆さんが1人1つシステムコールを実装していけばあっとういう間にその問題は解決することでしょう。OSSってすばらしい。

さいごに

もっと知りたいという方はGoogle独自のクラスタマネージャとして既に発表されていたBorgの論文も併せて読んでおくといいかもしれません。

参考資料

Verma, Abhishek, et al. "Large-scale cluster management at Google with Borg." Proceedings of the Tenth European Conference on Computer Systems. ACM, 2015.

[表示が崩れる場合ダウンロードしてご覧ください] 2018年のDocker・Moby

Linux標準の仮想化技術「KVM」の仕組み (1/2):知って見るみるKVM(1) - @IT

お茶がしたい…!!

  • もっとgVisorについて議論したい
  • Flatt何やってるのか気になる…!
  • はあ…Dockerすき…

と思った方は以下のフォームを送っていただければ、ランチなどごちそうさせていただきます。