OCI Runtime Specificationを読んだので概要を書く

はじめに

OCI Runtime Specificationを読んだので概要を書きました。読んだのは仕様を理解して、簡単なところだけ実装しようと思ったからです。

Dockerとの関連を簡単に説明します*1。Docker Clientはdockerコマンドを提供するCLIです。Docker ClientはDocker Engineに要求を出してDocker Engineがcontainedを操作し、containedはruncを操作します。runcというのはLinuxの機能を利用してコンテナを実際に作ったり操作するプログラムです。今回はコンテナを実際に作るruncにあたるレイヤーの仕様の話です。コンテナを作るレイヤーの仕様なので、コンテナとは?のような話が含まれます。

f:id:gkuga:20200126143521p:plain

OCI (Open Container Initiative)

OCIとはOpen Container InitiativeのことでLinux Foundationの傘下の組織です。この組織はコンテナのイメージ形式やランタイムなどについて業界標準を作る管理機構です。公式サイトはこちらです。

現在以下の仕様が決められています。

Runtime Specificationはコンテナのランタイムについて、Image Specificationはコンテナのイメージ形式、Distribution Specificationはコンテナのレジストリの仕様です。それぞれの関係は次のような感じです。

  1. OCI RegistryからOCI Imageをダウンロードします
  2. 次にOCI Imageをディスク上に展開します。
    • 展開されたディレクトリにはコンテナ実行に必要なファイル群を含みます。これをOCI Runtime Filesystem Bundleと呼びます。
  3. 最後にOCI RuntimeでOCI Runtime Filesystem Bundleを実行するとコンテナが動きます。

The 5 principles of Standard Containers

Runtime specではStandard Containersという概念を定義しています。これはコンテナを実行するインフラやコンテナの中身に関係なくコンテナを実行するための定義です。

Standard Containersの5つの原則は以下のとおりです。

  1. Standard operations - 基本的な操作ができること。
  2. Content-agnostic - コンテナの中身に関わらず同じ操作は同じ影響を与える。
  3. Infrastructure-agnostic - インフラに関わらず同じように操作を行える。
  4. Designed for automation - 自動化のためにデザインされている。コンテナの中身やインフラに関わらず同じ操作が行えるというのは自動化に向いている。
  5. Industrial-grade delivery - 企業で実運用ができるレベルのソフトウェアデリバリができる。

Standard Containersのための仕様というのは以下を定義します。

  • Runtime Specification - 3つの仕様
    1. 設定ファイルのフォーマット
    2. 基本的な操作
    3. 実行環境

Standard Containersのための仕様というのはOCI Runtime Specification自体のことです。つまり、OCI Runtime Specificationで定義されるコンテナというのがStandard Containersということでしょう。

f:id:gkuga:20200126144711p:plain

OCI Runtime Filesystem Bundle

コンテナのイメージをレジストリからダウンロードして、ディスクに展開したとき、展開されたディレクトリにはコンテナを実行するのに必要なデータが含まれます。このディレクトリをOCI Runtime Filesystem Bundleと呼びます。コンテナはプロセスとして動いているものですが、OCI Runtime Filesystem Bundleというのはコンテナのファイルシステム上における形式です。以降ではOCI bundleもしくはバンドルと呼びます。バンドルには次のものを含みます。

  • config.json
    • 設定ファイルです。このファイルはバンドルのディレクトリのルートに配置する必要があります。ファイル名はconfig.jsonです。
  • container's root filesystem
    • コンテナのルートファイルシステムで、設定ファイルのroot.pathプロパティで指定されます。

では実際にOCI Bundleを作りましょう。OCI Runtime Specificationの実装であるruncのREADMEを参考にします。runcのインストールはREADMEを参考にしてください。

以下rootユーザで実行しています。プロンプトを>で表します。

# create the top most bundle directory
> mkdir /mycontainer
> cd /mycontainer

# create the rootfs directory
> mkdir rootfs

# export busybox via Docker into the rootfs directory
> docker export $(docker create busybox) | tar -C rootfs -xvf -

以上のコマンドで、コンテナ(busybox)のルートファイルシステム/mycontainer/rootfsに展開されました。ここではmycontainerがバンドルということになります。config.jsonを配置します。

> runc spec
> ls
config.json  rootfs

自分で作成してもいいですがrunc specでconfig.jsonが作成されます。デフォルトでroot.pathプロパティはrootfsという名前のディレクトリが指定されています。

> cat config.json | jq .root
{
  "path": "rootfs",
  "readonly": true
}

これでOCI bundleの作成が完了です。

OCI bundleの実行

ではこのバンドルを実行しましょう*2

> runc run mycontainerid
/ # ls
bin   dev   etc   home  proc  root  sys   tmp   usr   var

これでバンドルが実行されました。コンテナ*3の中に入ってプロンプト#が表示されます。

Operations

これはRuntime Specification - 3つの仕様2. 基本的な操作のことです。Runtimeがサポートすべき操作は以下の通りです。

  • Query State - state <container-id>
  • Create - create <container-id> <path-to-bundle>
  • Start - start <container-id>
  • Kill - kill <container-id> <signal>
  • Delete - delete <container-id>

runc run mycontaineridで使ったrunがないので、runcreatestartの組み合わせということでしょう。以下にrunc --helpの一部抜粋を載せます。定義されたものは全部ありますね。

COMMANDS:
     checkpoint  checkpoint a running container
     create      create a container
     delete      delete any resources held by the container often used with detached container
     events      display container events such as OOM notifications, cpu, memory, and IO usage statistics
     exec        execute new process inside the container
     init        initialize the namespaces and launch the process (do not call it outside of runc)
     kill        kill sends the specified signal (default: SIGTERM) to the container's init process
     list        lists containers started by runc with the given root
     pause       pause suspends all processes inside the container
     ps          ps displays the processes running inside a container
     restore     restore a container from a previous checkpoint
     resume      resumes all processes that have been previously paused
     run         create and run a container
     spec        create a new specification file
     start       executes the user defined process in a created container
     state       output the state of a container
     update      update container resource constraints
     help, h     Shows a list of commands or help for one command

Hooks

基本的な操作において、操作前と後に何かしらのアクションを実行できます。この仕組みをHooksと呼びます。

Lifecycle

コンテナの生成から停止、削除までのライフサイクルの定義です。ドキュメントに詳細に書いてあります。

Errors・Warnings

エラーワーニングに関する定義です。詳細は割愛します。

State

コンテナは以下のプロパティを持ちます

  • ociVersion (REQUIRED)
  • id (REQUIRED)
  • status (REQUIRED)
  • pid (REQUIRED)
  • bundle (REQUIRED)
  • annotations (OPTIONAL)

annotationsだけはなくても良いようです。runc run mycontaineridでコンテナを実行して別のターミナルでStateを確認してみます。

> runc state mycontainerid
{
  "ociVersion": "1.0.0",
  "id": "mycontainerid",
  "pid": 2231,
  "status": "running",
  "bundle": "/mycontainer",
  "rootfs": "/mycontainer/rootfs",
  "created": "2020-01-19T16:30:29.441186722Z",
  "owner": ""
}

REQUIREDであるociVersion,id,status,pid,bundleは全部揃っています。

ここまでのOCI bundle、Hooks、Lifecycle、Errors・Warnings、StateがRuntime Specification - 3つの仕様3. 実行環境にあたるところでしょうか。

Configuration

Runtime Specification - 3つの仕様1. 設定ファイルのフォーマットにあたる部分です。設定ファイルであるconfig.jsonについてです。詳細はドキュメントを参照ください。見るとわかると思いますが、共通の設定と、プラットフォーム依存の設定があります。恐らくStandard Containersの理想的には共通の設定だけですましたいところですが、現実的には各プラットフォーム依存の設定が必要になってきてしまうようです。

まとめ

仕様だけを読むとイメージがつきにくいのですが、runcを動かしながらだと「あぁ、なるほど。」と理解できました。runcはOCIが開発元なので実装を見て仕様の解釈がわかるのは良いですね。しばらくランタイムを触って、次にImage Specificationを見ていこうと思います。

*1:ココらへんはちゃんと調べていません。ネットにたくさん情報があるので調べてください。すみません。

*2:「バンドルを実行する」 この表現は合っているのでしょうか?

*3:プロセスになるとバンドルというよりコンテナと言った方が良いですよね?