ブログのしゅーくりーむ

技術的なメモとかライフログとか。

Kubernetes 上で動作するコンテナから安全に FUSE を利用したかった

本題の通りの気持ちがあったのですが、結論としては手軽にできる良い方法は無いようでした。 備忘録的に挑戦した事を記録しておきます。

背景: FUSE 利用のモチベーション

言うまでもなくファイル I/O はシステム開発においてよく使われる機構であり、多くのプログラミング言語において標準ライブラリまたはそれに類するライブラリでファイル I/O をサポートしていると思われます。

さて Kubernetes 上でマイクロサービスを実装していく事が多々あるこのご時世、各マイクロサービスで実装言語非依存で使えるデータの読み書きのロジックがあると、例えば何らかの設定ファイルの動的受け渡しやロギングにおいて便利な可能性があります。 これに似た課題を抱えて解決に向かったプロダクトとして Envoy Proxy があるかと思われます。 これは元々ネットワーキングやトレーシングの課題などを各言語ごとのライブラリで対応していたもののつらくなり、 HTTP や gRPC を解釈するプロキシを導入することで緩和に成功しています。

前述の通りファイル I/O は HTTP や gRPC をしゃべるより更にリーズナブルな共通プロトコルであると考えられもしそうです。 さらに FUSE を使えばファイルシステムとしてインタフェースを提供して、かつ裏側で複雑なロジックを動かすこともできるように考えられます。 そんなわけで、 Kubernetes 上で動作するコンテナからいい感じに FUSE で実装したファイルシステムをマウントして利用できると良いかもと着想した次第です。

FUSE 利用の限界

同じような思考をしたり、あるいは既存 FUSE 資産を利用したい人たちが多々居たようで、 Kubernetes 公式リポジトリの Issue でも議論がされていたりします。

github.com

残念ながらこの 2015 年に open した issue は 2018 年も終わりを迎える今日においても close されていません。 どういう点がネックになるかというと

  • /dev/fuse を open するのに特権が必要
  • mount, umount するのに SYS_ADMIN ケーパビリティが必要

が挙げられ、一応利用できなくは無いものの利用条件を満たすため現状は privileged mode でコンテナを動かす羽目になり、やや安全面でリスクがあるように思えます。

FUSE 利用パターンいくつか

前述の Issue の中で幾つかのアイデアも提示されています。少しこれらを実際に試してみることにしました。 多少このリスクを緩和するため、大きく分けて以下の方針がありそうです。

  • コンテナ外でなんとかするパターン
  • 特権を持つコンテナをアプリケーションを動作させるコンテナと分離するパターン

前者は場合によっては可搬性を損なうか導入が非常に困難になる恐れがあり、また後者であればコンテナ内の世界で完結させることができそうです。 従ってまずは後者のアイデアを試してみます。

以下に検証用に用意した Dockerfile や設定ファイルを配置しました。 FUSE でマウントするのは libfuse に含まれる、ファイルを read すると "Hello, World!" を返す hello.c を利用しておきます。

github.com

こちらのリポジトリでは initContainers で mount するのと、 postStart , preStopサイドカーコンテナで mount/umount する案を試してみました。 が、結局マウント先を参照するアプリケーションコンテナからはマウントした先は参照できなくなります。 結局この例だと特権を渡さざるを得ませんでした。

次の一手のアイデア

今回特権を要求されたのが、コンテナ内から /dev/fuse が open できない点なので、この点を何とかできれば 多少課題は緩和されるかもです。 Kubernetes は Device Plugin という機構を最近サポートしているようで、これで何とかできる可能性があるような全く無いような気がしています。 また Kubernetes v1.10 からベータになったというストレージ接続インタフェース CSI は将来も見据えてこの問題に取り組むのに、もしかしたら良い機構かもしれません。

いずれにせよ Kubernetes 自体の調査をほとんどやっていないので次の一手はこれら周辺技術の調査からかと考えております。