
めちゃくちゃ興味あり〼 https://t.co/gHfAV5rbEU
— うなすけ (@yu_suke1994) 2018年11月30日
頭の中には設計があるんですが、…
— Uchio KONDO 🔫 (@udzura) 2018年11月30日
OCIの仕様を再確認するところから…
とりあえずブログかキータ
— Uchio KONDO 🔫 (@udzura) 2018年11月30日
という訳で、書きました。
Open Container Initiativeによって定められた、コンテナイメージフォーマットの標準仕様です。
詳しくは Open Container Initiativeによるコンテナランタイムとコンテナイメージの最初の標準化作業が完了、「OCI v1.0」発表 - Publickey にて。
現在、OCI ImageをdownloadできるDocker HubのようなWebサイトは知る限りありません。なので、自分でOCI Imageを作成する必要があります。
まず、Docker Hubにて公開されているDocker imageをdownloadするところからです。以下のコマンドで、Docker Hubからダウンロードしたimageをtarballとして扱うことができるようになります。
$ docker image pull ruby:2.5.3-slim-stretch
$ docker save ruby:2.5.3-slim-stretch --output ruby_253_slim_stretch.tar
このtarを展開してみましょう。
$ tar -xf ruby_253_slim_stretch
すると、manifest.json というファイルができているので、見てみます。
[
  {
    "Config":"b1c1603e80c648f3ab902b0259ab846a7779d0780124bf9e417dd4b8c3cea296.json",
    "RepoTags":[
      "ruby:2.5.3-slim-stretch"
    ],
    "Layers":[
      "aeff88bcdbbd12ea45c023c45f97b870492092899651c811b2ef26ae7fdf3120/layer.tar",
      "c61a4dce9ddcebd63027d09811998052c9b2cdb3a379c297277cf755dfcf1420/layer.tar",
      "de2944e57fc93c2f354420cb36210fd1181687a990ffd7123600fdaecba3ee83/layer.tar",
      "49c3631e8651776127d66adb995e78af1e2cfc52b7a10a20df0d92d837258419/layer.tar",
      "eb50b8a8210f1b43ff1571598e66b694844b2dcf6fbaa0691e8af6b7c80dcaa7/layer.tar"
    ]
  }
]
なるほど、これはOCI image specを読むとわかるのですが、OCIに定められている形式のJSONではありませんね。
ここで、containers/skopeo というtoolを使用して、Docker imageをOCI imageに変換してみます。
$ skopeo copy docker://ruby:2.5.3-slim-stretch oci:ruby-oci:latest
すると、 ruby-oci/index.json というファイルができているので、見てみます。
{
  "schemaVersion":2,
  "manifests":[
    {
      "mediaType":"application/vnd.oci.image.manifest.v1+json",
      "digest":"sha256:a3843587af4f3e838f3e1a10649631144d4dcf4391980b64f3b902d81048057c",
      "size":976,
      "annotations":{
        "org.opencontainers.image.ref.name":"latest"
      },
      "platform":{
        "architecture":"amd64",
        "os":"linux"
      }
    }
  ]
}
なるほど、これは OCI Image Specに定められている image manifest fileですね。
自分なりに理解しようと翻訳したもののメモになります。 正確性の保証はないです。誤訳とかあります。最後のほう力尽きてます。
high level image manifest にはcontentsとdependencies of the image including the content-addressable(連想?) identity of one of more file system layer changeset archives、展開すると最終的に実行可能なファイルシステムになる
image configuration にはapplication arguments, environmentsなどの情報
image indexには high level manifest list of manifests and discriptorsのpointが含まれる
それらのmanifestsは異なるimageの実装 ←プラットフォームや他の属性によって変化することができる

一度作成されたOCI imageは名前によって探索(discovered)、ダウンロード、hashによる検証、署名による信頼、OCI Runtime Bundleへの展開ができる

components of the specは以下を含む
optional featureとしてSignaturesやNamingが仕様に含まれるかもしれない。
application/vnd.oci.descriptor.v1+json
application/vnd.oci.layout.header.v1+json
application/vnd.oci.image.index.v1+json
application/vnd.oci.image.manifest.v1+json
application/vnd.oci.image.config.v1+json
application/vnd.oci.image.layer.v1.tar
application/vnd.oci.image.layer.v1.tar+gzip
application/vnd.oci.image.layer.nondistributable.v1.tar
application/vnd.oci.image.layer.nondistributable.v1.tar+gzip
HTTP responseのContent-Typeで上の値を返すなどのように、typeを返すなにかしらの方法を実装してもよい(MAY)、また実装はmedia typeとdigestを期待してよい? 実装は返却されたmedia typeを尊重する必要がある(SHOULD)
前方・後方互換を可能な限り維持する必要がある。
似た、または関連するmedia typeは以下
application/vnd.oci.image.index.v1+json
application/vnd.docker.distribution.manifest.list.v2+json  (mediaTypeが違う?)application/vnd.oci.image.manifest.v1+json
application/vnd.docker.distribution.manifest.v2+jsonapplication/vnd.oci.image.layer.v1.tar+gzip
application/vnd.docker.image.rootfs.diff.tar.gzip 互換性がある(入れ替え可能)application/vnd.oci.image.config.v1+json
application/vnd.docker.container.image.v1+jsonImage indexは複数のImage manifestを持つ。Image manifestとImage JSON(config)は1対1。Image manifestはLayerのtar archiveを複数持つ。 Discriptorは全ての参照を持つ。
mediaType
digest
size
urls
annotations
data
こういう形式
sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3bsha512:401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b372742...sha256とsha512がRegistered algorithmsとされている。sha256はMUSTでsha512はMAY。
{
  "mediaType": "application/vnd.oci.image.manifest.v1+json",
  "size": 7682,
  "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
  "urls": ["https://example.com/example-manifest" ]
}
OCI Image LayoutはOCI content-addressable blobs と location-addressable references のための directory構造を表す(?) Layoutではtarやzipなどのarchive formats、nfsなどの共有ファイルシステム、http、ftp、rsyncなどのネットワークによるファイル取得を使用してもよい。
あるimage layoutと参照は、manifestと指定された順序で適用されるfilesystem layerとOCI runtime specificationのconfig.jsonへ変換できるimage configurationがあればOCI Runtime Specification bundleを何らかのtoolによって作成できる。(?)
blobs directory
oci-layout file
imageLayoutVersion というfieldを持つ必要がある。index.json
$ cd example.com/app/
$ find . -type f
./index.json
./oci-layout
./blobs/sha256/3588d02542238316759cbf24502f4344ffcc8a60c803870022f335d1390c13b4
./blobs/sha256/4b0bc1c4050b03c95ef2a8e36e25feac42fd31283e8c30b3ee5df6b043155d3c
./blobs/sha256/7968321274dc6b6171697c33df7815310468e694ac5be0ec03ff053bb135e768
$ shasum -a 256 ./blobs/sha256/afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51
afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51 ./blobs/sha256/afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51
blobs のサブディレクトリ以下にあるディレクトリは、各ハッシュアルゴリズムごとにまとめられている。そのディレクトリ以下に、実際のファイルが格納されている。
blobs/<alg>/<encoded><alg>:<encoded> と対応している$ cat ./blobs/sha256/9b97579de92b1c195b85bb42a11011378ee549b02d7fe9c17bf2a6b35d5cb079 | jq
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    },
...
$ cat ./blobs/sha256/afff3924849e458c5ef237db5f89539274d5e609db5db935ed3959c90f1f2d51 | jq
{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "size": 7023,
    "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270"
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 32654,
      "digest": "sha256:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0"
    },
...
$ cat ./blobs/sha256/5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270 | jq
{
  "architecture": "amd64",
  "author": "Alyssa P. Hacker <alyspdev@example.com>",
  "config": {
    "Hostname": "8dfe43d80430",
    "Domainname": "",
    "User": "",
    "AttachStdin": false,
    "AttachStdout": false,
    "AttachStderr": false,
    "Tty": false,
    "OpenStdin": false,
    "StdinOnce": false,
    "Env": [
      "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
    ],
    "Cmd": null,
    "Image": "sha256:6986ae504bbf843512d680cc959484452034965db15f75ee8bdd1b107f61500b",
...
$ cat ./blobs/sha256/9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0
[gzipped tar stream]
これ。
{
    "imageLayoutVersion": "1.0.0"
}
必須。image-layoutの参照、descriptorsのentry pointになる。/index.json に置かれる。
"org.opencontainers.image.ref.name" にイメージのtagが格納される?
{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.index.v1+json",
      "size": 7143,
      "digest": "sha256:0228f90e926ba6b96e4f39cf294b2586d38fbb5a1e385c05cd1ee40ea54fe7fd",
      "annotations": {
        "org.opencontainers.image.ref.name": "stable-release"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      },
      "annotations": {
        "org.opencontainers.image.ref.name": "v1.0"
      }
    },
    {
      "mediaType": "application/xml",
      "size": 7143,
      "digest": "sha256:b3d63d132d21c3ff4c35a061adf23cf43da8ae054247e32faa95494d904a007e",
      "annotations": {
        "org.freedesktop.specifications.metainfo.version": "1.0",
        "org.freedesktop.specifications.metainfo.type": "AppStream"
      }
    }
  ],
  "annotations": {
    "com.example.index.revision": "r124356"
  }
}
imageと、そのコンポーネントのために生成された一意なIDからハッシュ可能なimageのconfigurationimage modelをサポートした参照可能なimageを作成すること、platform固有のmanifestを含んだ"fat manifest"による複数architecture対応のimageの実現、OCI Runtime Specificationへの変換の3つを目標にしている。
image indexはarchitectureやOSごとに展開可能なそれぞれのimageの情報を持つが、image manifestは特定のarchitecture、OSに対する単一のcontainer imageにおけるconfigurationとlayerの集合を提供する。
schemaVersion
mediaType
config
layers
annotations
{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "size": 7023,
    "digest": "sha256:b5b2b2c507a0944348e0303114d8d93aaaa081732b86451d9bce1f432a537bc7"
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 32654,
      "digest": "sha256:9834876dcfb05cb167a5c24953eba58c4ac89b1adf57f28f2f9d09af107ee8f0"
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 16724,
      "digest": "sha256:3c3a4604a545cdc127456d94e421cd355bca5b528f4a9c1905b15da2eb4a4c6b"
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
      "size": 73109,
      "digest": "sha256:ec4b8955958665577945c89419d1af06b5f7636b4ac3da7f12184802ad867736"
    }
  ],
    "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}
application/vnd.oci.image.manifest.v1+json ← これ{
  "schemaVersion": 2,
  "manifests": [
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7143,
      "digest": "sha256:e692418e4cbaf90ca69d05a66403747baa33ee08806650b51fab815ad7fc331f",
      "platform": {
        "architecture": "ppc64le",
        "os": "linux"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.manifest.v1+json",
      "size": 7682,
      "digest": "sha256:5b0bcabd1ed22e9fb1310cf6c2dec7cdef19f0ad69efa1f392e94a4333501270",
      "platform": {
        "architecture": "amd64",
        "os": "linux"
      }
    }
  ],
  "annotations": {
    "com.example.key1": "value1",
    "com.example.key2": "value2"
  }
}
application/vnd.oci.image.layer.v1.tar はtar archiveでなくてはならず、またtar archiveの結果となるpathに重複したエントリを含んではいけないrootfs-c9d-v1/
    etc/
        my-app-config
    bin/
        my-app-binary
        my-app-tools
色々とfilesystemについての解説が続く
application/vnd.oci.image.layer.nondistributable.v1.tar というmediaTypeでないといけないapplication/vnd.oci.image.config.v1+jsonsha256:a9561eb1b190625c9adb5a9513e72c4dedafc1cb2d4c5236c9a6957ec7dfd5a9ChainID(L₀) =  DiffID(L₀)ChainID(L₀|...|Lₙ₋₁|Lₙ) = Digest(ChainID(L₀|...|Lₙ₋₁) + " " + DiffID(Lₙ))VARNAME=VARVALUE という形式で格納される。
layers である必要がある。実装は不明な値が入っていた場合はエラーを出す必要がある。{
    "created": "2015-10-31T22:22:56.015925234Z",
    "author": "Alyssa P. Hacker <alyspdev@example.com>",
    "architecture": "amd64",
    "os": "linux",
    "config": {
        "User": "alice",
        "ExposedPorts": {
            "8080/tcp": {}
        },
        "Env": [
            "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
            "FOO=oci_is_a",
            "BAR=well_written_spec"
        ],
        "Entrypoint": [
            "/bin/my-app-binary"
        ],
        "Cmd": [
            "--foreground",
            "--config",
            "/etc/my-app.d/default.cfg"
        ],
        "Volumes": {
            "/var/job-result-data": {},
            "/var/log/my-app-logs": {}
        },
        "WorkingDir": "/home/alice",
        "Labels": {
            "com.example.project.git.url": "https://example.com/project.git",
            "com.example.project.git.commit": "45a939b2999782a3f005621a8d0f29aa387e1d6b"
        }
    },
    "rootfs": {
      "diff_ids": [
        "sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
        "sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
      ],
      "type": "layers"
    },
    "history": [
      {
        "created": "2015-10-31T22:22:54.690851953Z",
        "created_by": "/bin/sh -c #(nop) ADD file:a3bc1e842b69636f9df5256c49c5374fb4eef1e281fe3f282c65fb853ee171c5 in /"
      },
      {
        "created": "2015-10-31T22:22:55.613815829Z",
        "created_by": "/bin/sh -c #(nop) CMD [\"sh\"]",
        "empty_layer": true
      }
    ]
}
com.example.myKey みたいなorg.opencontainers はOCIによって予約済みなので使用してはならない。org.opencontainers.image はOCIによって予約済みなので使用してはならない。(他のOCI仕様についても同様)org.opencontainers.image.created
org.opencontainers.image.authors
org.opencontainers.image.url
org.opencontainers.image.documentation
org.opencontainers.image.source
org.opencontainers.image.version
org.opencontainers.image.revision
org.opencontainers.image.vendor
org.opencontainers.image.licenses
org.opencontainers.image.ref.name
org.opencontainers.image.title
org.opencontainers.image.description
org.opencontainers.image | 
org.label-schema | 
Compatibility notes | 
|---|---|---|
created | 
build-date | 
Compatible | 
url | 
url | 
Compatible | 
source | 
vcs-url | 
Compatible | 
version | 
version | 
Compatible | 
revision | 
vcs-ref | 
Compatible | 
vendor | 
vendor | 
Compatible | 
title | 
name | 
Compatible | 
description | 
description | 
Compatible | 
documentation | 
usage | 
URLの場合にはCompatible | 
authors | 
Label Schemaにはない要素 | |
licenses | 
Label Schemaにはない要素 | |
ref.name | 
Label Schemaにはない要素 | |
schema-version | 
OCI Image Specにはない要素 | |
docker.*, rkt.*
 | 
OCI Image Specにはない要素 | 
application/vnd.oci.image.config.v1+json を OCI Runtime configuration blobに変換する方法を定義するConfig.WorkingDir → process.cwd
Config.Env → process.env ※1Config.Entrypoint → process.args ※2Config.Cmd → process.args ※2org.opencontainers.image.author に指定する必要があるorg.opencontainers.image.created に指定する必要があるorg.opencontainers.image.stopSignal に指定する必要があるConfig.User → process.user.*
/etc/passwd をパースすることによってprocess.user.uid や process.user.gid に値を格納するConfig.ExposedPorts → annotations ※1Config.Vlumes → mounts ※2org.opencontainers.image.exposedPorts  に値をセットしなければならない