LinuxKit で実現する新しい Docker 実行環境

プレイド エンジニア / Hunter
山内 雅浩 @algas

DockerCon 2017 で発表された Moby Project の一部である "LinuxKit" を使ってみました。実際に触ってみることで理解できたことやハマったこと、プロジェクトの思想を少しでも多くの人に共有したいと思って今回の記事を書きました。

対象読者

本記事は主に以下のような方々をターゲットとしています。

  • Docker 環境の構築に不安や不満がある人
  • クラウド環境の設計・構築に携わっている人
  • Moby Project という名前は聞いたことがあるけど中身を理解していない人

LinuxKit とは?

https://github.com/linuxkit/linuxkit

概要

LinuxKit は複数の Docker コンテナを含む軽量の Linux VM を作るためのツールです。
誤解を恐れずに言うと "Docker VM" です。
Docker コンテナやコンテナの作成手段を置き換えるものではありません。

moby os architecture

(https://www.slideshare.net/Docker/dockercon-2017-general-session-day-1-solomon-hykes-75362520 からの引用)

特徴

"LinuxKit, a toolkit for building custom minimal, immutable Linux distributions."

  • Custom
    Dockerコンテナイメージを組み合わせて構成します。自作のコンテナも当然使えます。
  • Minimal
    アプリケーションを実行するための最低限のコンポーネントだけを動かすようにします。不要な機能を入れないことで、容量を小さく実行速度を上げセキュリティレベルを高めます。
  • Immutable
    LinuxKit の構成を変える場合には新しくビルドして差し替えるので全く別のものになります。

以下のプラットフォームに対応しています。

  • ローカル
    • HyperKit (macOS)
    • Hyper-V (Windows)
    • qemu (macOS, Linux, Windows)
    • VMware (macOS, Windows)
  • クラウド
    • Amazon Web Services
    • Google Cloud
    • Microsoft Azure
    • packet.net

LinuxKit は何に使うの?

複数のコンテナを1つのVMとして定義できる

例えば、メインのアプリケーションとロガーやWebサーバ、画像変換アプリケーションを同じホスト上に同居させたくなったとしましょう。
これを Docker コンテナを使って実現するには以下のような方法がありました。

  1. 同じコンテナにまとめてしまう
    コンテナシステムにおける「1プロセス1コンテナの原則」に反する悪手。コンテナ内でプロセスを管理する必要があります。
  2. ホストの機能で実現する
    アプリ以外を Docker コンテナの外側、つまり Docker Engine が動いているホスト上で動作させてしまう方法です。できれば Docker で解決したいですよね?
  3. コンテナ間通信を使う
    標準では docker-compose を使えば実現できます。同じホスト上で複数のコンテナを動作させてコンテナ間を link でつなぐ必要があるのと、複数のコンテナを制御・管理する必要があるのがデメリットです。

例えば "Amazon EC2 Container Service" や "Azure Container Service" のようなホスト型のオーケストレーション機能では実現が困難だったりします。
LinuxKit を使うと設定ファイル1つで簡単に設定できますし、機能が VM 単位でまとめられているので管理するのも楽です。

LinuxKit の設定ファイル yaml を読む

LinuxKit をビルドするための yaml ファイルの例として公式の sshd の yaml を読んでみましょう。
https://github.com/linuxkit/linuxkit/blob/master/examples/sshd.yml

# VM を起動する時にだけ使う
kernel:
  image: "linuxkit/kernel:4.9.35"
  cmdline: "console=ttyS0 page_poison=1"
# init システム用のイメージで root のファイルシステムに直接展開する
init:
  - linuxkit/init:14a38303ee9dcb4541c00e2b87404befc1ba2083
  - linuxkit/runc:2310ad9d266cf5d9c4d07613bd2135ed7eb8a21f
  - linuxkit/containerd:c977f27c234d55b85172813b8451f67ea86be4a3
  - linuxkit/ca-certificates:67acf038c44bb191ebb704ec7bb39a1524052cdf
# 次の services の前に短時間だけ実行する
onboot:
  - name: sysctl
    image: "linuxkit/sysctl:d1a43c7c91e92374766f962dc8534cf9508756b0"
# containerd で長時間実行する
services:
  - name: getty
    image: "linuxkit/getty:5ab31289889d61a5d2ecbeea8e36ce74ac54737c"
    env:
     - INSECURE=true
  - name: rngd
    image: "linuxkit/rngd:1516d5d70683a5d925fe475eb1b6164a2f67ac3b"
  - name: dhcpcd
    image: "linuxkit/dhcpcd:4b7b8bb024cebb1bbb9c8026d44d7cbc8e202c41"
  - name: sshd
    image: "linuxkit/sshd:89b2e91d7d1bf2f40220be0e3ed586e74746cceb"
# 外部からファイルを読む込む
files:
  - path: root/.ssh/authorized_keys
    source: ~/.ssh/id_rsa.pub
    mode: "0600"
    optional: true
# 信頼する Docker コンテナ
trust:
  org:
    - linuxkit

上記は単純な例なので項目数が少なく、把握するのは難しくないと思います。
特徴的な設定は以下の3つです。

  1. getty: コンソールでログインする
  2. sshd: ssh daemon を動かす
  3. files: ssh 公開鍵をローカルからコピーする

詳細は Reference で確認してください。

LinuxKit で sshd を動かしてみる (macOS)

LinuxKit で sshd を動かし、ssh クライアントで接続します。

設定手順

$HOME/.ssh/id_rsa.pub が存在していることを前提としています。
もし上記のファイルがない場合には、ssh-keygen コマンドで公開鍵のペアを作成してください。

  1. moby と linuxkit をインストール
    brew tap linuxkit/linuxkit
    brew install --HEAD moby
    brew install --HEAD linuxkit
  2. sshd をビルドする
    git clone https://github.com/linuxkit/linuxkit.git
    cd linuxkit/example
    moby build sshd.yml
  3. sshd を起動する
    linuxkit run sshd
  4. ip address を確認する
    ifconfig コマンドで eth0 の ipv4 アドレスを記録しておきます。
  5. docker network を作成する
    docker network create --driver bridge --subnet=192.168.65.100/24 --gateway=192.168.65.101 linuxkit
  6. socat コンテナを用意する
    sshd linuxkit に接続するための proxy を用意します。
    https://github.com/linuxkit/linuxkit/issues/1674
    docker run -p 127.0.0.1:8080:8080 --name linuxkit-proxy -it --rm alpine sh
    / # apk -U add socat
    / # socat TCP-LISTEN:8080,fork TCP:[linuxkit_vm_ip]:22
    事前に確認した ipv4 アドレスを [linuxkit_vm_ip] のところに入れてください。
  7. socat コンテナに network を追加する
    docker network connect linuxkit linuxkit-proxy
  8. sshd コンテナに接続する
    ssh -o StrictHostKeyChecking=no localhost -p 8080 -l root

以上です。
linuxkit を終了するには、コンソールで halt コマンドを入力します。

設定でハマったところ

  • コンソールから脱出できない
    halt コマンドで LinuxKit を終了させる方法に気づくまでは、何度も exit しては auto login されるのを繰り返していた。
  • ssh クライアントから sshd に接続できない
    sshd を動かすのは難しくないけど ssh クライアントで接続するのが簡単ではなかった。
  • Docker のネットワーク設定方法がわからない
    普段はほとんどデフォルトのままで使っているので Docker のネットワークをカスタマイズしたことがなかった。linuxkit のデフォルトの ip address レンジと docker のそれを合わせるのがポイントです。

Moby Project という思想

moby project

Docker が Moby Project を作った理由は一体何でしょうか。
Moby Project 以前の Docker に足りなかったもの、それは Docker コンテナを「現実の環境で動作させる仕組み」です。
Docker コンテナを動作させるためには以下のものが必要です。

  1. Docker コンテナを動かす仕組み = Docker Engine
  2. Docker Engine を動かす仕組み = OS
  3. OS または OS が動作するインスタンスを管理する仕組み

しかし、これらを管理・制御するための仕組みとして Docker Engine 以外は Docker の外側の世界のものでした。
Moby Project では上記にそれぞれ対応するシステムがあります。

  1. Docker コンテナを動かす仕組み = Containerd
  2. Containerd を動かす仕組み = LinuxKit
  3. LinuxKit を管理する仕組み = InfraKit

しかも、Moby Project で用意された上の3つは、それぞれ別のソフトウェアに置き換えることが可能です。その Pluggable である性質が、Moby Project の大きな特徴の1つです。
Docker やそれ以外のチームが作った Docker を動かす仕組みはこれまでにもありましたが、Moby Project はコンテナ運用の新しい仕組みによって Docker を再定義するものと言えます。

まとめ

LinuxKit は Docker コンテナを活かしつつ新しいコンテナ実行環境を提供する仕組みです。
新しく Docker を使ったシステム構築する際には LinuxKit を使うのが選択肢の1つになると思います。
出てきたばかりで情報が少ないのが LinuxKit を使う上での一番の障壁ですが、今後普及してくるに連れて改善していくことでしょう。

InfraKit についての記事も書こうと考えているのですが、今回は長くなってしまったので次の機会に書くことにします。

エンジニア募集中

ウェブ接客プラットフォーム「KARTE」を運営するプレイドでは、KARTEを使ってこんなアプリケーションが作りたい! KARTE自体の開発に興味がある!というエンジニア(インターンも!)を募集しています。
詳しくは弊社採用ページ
またはWantedly
をご覧ください。 もしくはお気軽に、下記の「話を聞きに行きたい」ボタンを押してください!