lf-edge / edge-containers

Android has APKs, Docker has OCIs - Edge now has Edge Containers

Home Page:https://www.lfedge.org/projects/eve/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

ECI Distribution

This repository contains a golang library and CLI for VM images, manage disk images, kernel, initrd and other bootable artifacts in OCI registries. It can build, push and pull those images.

It is inspired directly by ORAS and leverages it, but is opinionated to the ECI use case. As such, it uses elements of OCI Artifacts.

It can store the images in multiple formats:

  • artifacts (default): leverage full artifacts mime types, with each layer a different artifact
  • legacy: standard mime-types and configs, with each layer optionally a different artifact; if the standard type is .tar, then the single file is tarred; if the standard type is tar+gzip, then the single file is tarred and gzipped.

Note that the legacy format actually looks identical to putting the artifacts in a filesystem in an OCI container image. We simply leverage annotations to indicate where each artifact is. Ideally, each artifact - kernel, initrd, root disk, other disks, etc. - are their own layers. However, this is not required.

Because the legacy format replicates a standard OCI container image, you can create it using standard docker tools as well. An example is shown below.

In all cases, annotations are used as well.

Usage

Pushing an ECI

To push an ECI to a registry, you need the following items in a directory:

  • a root disk image in any supported format: raw, vhd, vmdk, iso
  • a Linux kernel (optional)
  • a Linux initrd (optional)
  • additional disks (optional)
  • a config file, whose contents provide the desired OCI manifest config

Note: If you do not provide a config file, a default will be created, using the following information. It can be overridden via options; run with --help to see the options.

  • OS: current platform OS
  • Arch: current platform arch
  • Author: lfedge/edge-containers

You can push the image as follows:

eci push --root path/to/root.img:raw --kernel path/to/kernel --initrd path/to/initrd --disk path/to/disk1:iso --disk path/to/disk2:vmdk ... --config path/to/config lfedge/eci-nginx:ubuntu-1804-11715

The above uses the default artifacts format, which assumes that the registry fully supports Artifacts and will use specialized mime types.

If you wish to use one of the other formats, or do not have a choice as you are using a registry that does not yet support artifacts, select an alternate format with the --format flag.

For the legacy format:

eci push --format legacy --root path/to/root.img:raw --kernel path/to/kernel --initrd path/to/initrd --disk path/to/disk1:iso --disk path/to/disk2:vmdk ... --config path/to/config lfedge/eci-nginx:ubuntu-1804-11715

The eci command will take care of setting the correct mime types and annotations on all of the objects.

Note that disks, both root and additional, must have the file name, following by a : and the disk type, so that consumers know how to interpret them, e.g. to send a disk file whose name is mydisk and is of type qcow2:

--disk mydisk:qcow2

Using Standard Docker

Standard docker tools do not support the artifacts format. However, you can build and push using the legacy format with standard docker tools. However, docker does not support adding annotations to the manifest, except using experimental tools.

To support standard docker tools, we support reading the annotations from the image labels, which are stored in the config that is generated by docker build.

To use standard docker tools, you need to do two things:

  1. Place the artifacts, such as disks or kernel, in your container image filesystem
  2. Add appropriate labels to the container image

The following Dockerfile is equivalent to the above:

FROM scratch
COPY path/to/root.img /root.img
COPY path/to/kernel /kernel
COPY path/to/initrd /initrd
COPY path/to/disk1 /disk1.iso
COPY path/to/disk2 /disk2.vmdk

The above Dockerfile places each artifact in its own layer. You can place them all in a single layer as well:

FROM scratch
COPY path/to/ /

In order to inform any consumer where the artifacts are in the filesystem, aply the appropriate labels when building the image. Run:

docker build -t lfedge/eci-nginx:ubuntu-1804-11715 --label "org.lfedge.eci.artifact.root"="/root.img" --label "org.lfedge.eci.artifact.kernel"="/kernel" --label "org.lfedge.eci.artifact.initrd"="/initrd" --label "org.lfedge.eci.artifact.disk-1"="/disk1.iso" --label "org.lfedge.eci.artifact.disk-2"="/disk2.vmdk" .

The above line is somewhat messy, so you can include the labels in the Dockerfile, which makes sense, by adding LABEL commands:

FROM scratch
COPY path/to/root.img /root.img
COPY path/to/kernel /kernel
COPY path/to/initrd /initrd
COPY path/to/disk1 /disk1.iso
COPY path/to/disk2 /disk2.vmdk

LABEL "org.lfedge.eci.artifact.root"="/root.img"
LABEL "org.lfedge.eci.artifact.kernel"="/kernel"
LABEL "org.lfedge.eci.artifact.initrd"="/initrd"
LABEL "org.lfedge.eci.artifact.disk-1"="/disk1.iso"
LABEL "org.lfedge.eci.artifact.disk-2"="/disk2.vmdk"

And then run:

docker build -t lfedge/eci-nginx:ubuntu-1804-11715 .

Note: if you use the legacy format, your config file needs to be in a specific format for docker to recognize it. This utility builds it for you, and it is recommended you accept the default. However, if you provide --config, you can override it. Use at your own risk.

Pulling an ECI

To pull an ECI, you simply need a registry where the components will be downloaded:

eci pull lf-edge/eci-nginx:ubuntu-1804-11715

The above will default to placing artifacts in the current directory. To place them in a different directory:

eci pull --dir foo/bar/ lf-edge/eci-nginx:ubuntu-1804-11715

The eci command knows how to read the manifest and annotations and determine how to extract the data.

Note that whatever format it is in, it can be pulled "as is" by docker, containerd, go-containerregistry, img or any other tool that knows how to pull OCI images.

Media Types and Annotations

The specific standard media types are at docs/mediatypes.md.

In addition to the types, eci always will add annotations to the layer and config in the manifest describing its purpose.

The specific standard annotations are at docs/annotations.md.

File Names

ECI is highly opinionated about the file names. No matter what names you pass to it, it will give the files particular names. These are listed in docs/filenames.md.

Sample Manifest

A sample manifest for an actual pushed image is below. This is a manifest on docker hub, so the media types are the legacy types, while the annotations provide the purpose.

{
  "schemaVersion": 2,
  "config": {
    "mediaType": "application/vnd.oci.image.config.v1+json",
    "digest": "sha256:ffb3941df4fe37f22165b124d66e966d93b3dbf2765b736818b57a4516aed94e",
    "size": 14,
    "annotations": {
      "org.lfedge.eci.mediaType": "application/vnd.lfedge.eci.config.v1+json",
      "org.opencontainers.image.title": "config.json"
    }
  },
  "layers": [
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar",
      "digest": "sha256:edeaaff3f1774ad2888673770c6d64097e391bc362d7d6fb34982ddf0efd18cb",
      "size": 4,
      "annotations": {
        "org.lfedge.eci.mediaType": "application/vnd.lfedge.eci.kernel.layer.v1+kernel",
        "org.lfedge.eci.role": "kernel",
        "org.opencontainers.image.title": "kernel"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar",
      "digest": "sha256:da1464fd7ceaf38ff56043bc1774af4fb5cb83ef5358981d78de0b8be5a6fbcb",
      "size": 4,
      "annotations": {
        "org.lfedge.eci.mediaType": "application/vnd.lfedge.eci.initrd.layer.v1+cpio",
        "org.lfedge.eci.role": "initrd",
        "org.opencontainers.image.title": "initrd"
      }
    },
    {
      "mediaType": "application/vnd.oci.image.layer.v1.tar",
      "digest": "sha256:deb055d836e44a1dcf0317b0cacac2dbdd36301f82abf787f7849d3f5b916750",
      "size": 5,
      "annotations": {
        "org.lfedge.eci.mediaType": "application/vnd.lfedge.disk.layer.v1+raw",
        "org.lfedge.eci.role": "disk-root",
        "org.opencontainers.image.title": "disk-root-root.raw"
      }
    }
  ]
}

Differentiation Specification

How does the pull client - and anything else that might want to pull the artifacts - know what it has in hand? It could be one of:

  • an artifacts format image from this library/utility
  • a legacy format image from this library/utility, with each artifact as a layer but in tar+gzip format
  • a normal OCI container image, with VM artifacts inside at arbitrary paths

The parsing process is as follows:

  1. Retrieve the manifest.
  2. If the layers have the special mediaType, it is an ECI in artifacts format; use them as is.
  3. If the layers have the special annotations, it is an ECI in legacy format, one artifact per layer; extract using tar/gzip
  4. If the config has the special annotations, it is an ECI in legacy format, with files at arbitrary locations; extract using tar/gzip
  5. It is a regular OCI image.

Go Library

The go library is github.com/lf-edge/edge-containers/pkg/registry. Docs are available at godoc.org/github.com/lf-edge/edge-containers/pkg/registry.

Build

The eci tool can be built via make build, which will deposit the build artifact in dist/bin/eci-<os>-<arch>, e.g. dist/bin/eci-darwin-amd64 or dist/bin/eci-linux-arm64. To build it for alternate OSes or architectures, run:

make build OS=<target> ARCH=<target>

e.g.

make build OS=linux ARCH=amd64
make build OS=linux ARCH=amd64

About

Android has APKs, Docker has OCIs - Edge now has Edge Containers

https://www.lfedge.org/projects/eve/

License:Apache License 2.0


Languages

Language:Go 95.3%Language:Makefile 4.7%