visit
The Open Container Initiative is a non-profit open governance organization whose sole goal is to create open industry standards for container formats and runtimes. With that industry aligning around a set of open, universal container technology standards.
# OCI container image layout
├── blobs
│ └── sha256
│ ├── DIFFID (image.manifest)
│ └── DIFFID (image.config)
│ └── DIFFID (image.rootfs)
│ └── DIFFID (image.application)
│ └── DIFFID (image .... )
│ └── DIFFID (image .... +n)
├── index.json
└── oci-layout
Name | Description |
---|---|
architecture | Name of the compilation/CPU architecture. |
os | Name of the OS. (Linux, windows) |
os version | Version of the operating system targeted by the referenced blob. |
manifests | List of manifests for specific platforms. |
~$ skopeo inspect --raw docker://yorek/multiarch-hello-world
{
"schemaVersion": 2,
"mediaType": "application/vnd.docker.distribution.manifest.list.v2+json",
"manifests": [
{
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 428,
"digest": "sha256:9b874ccdb73e1aaf29e1c0fd3a550ddfa486d73525f2d77b452e30a09d3cb417",
"platform": {
"architecture": "amd64",
"os": "linux"
}
},
.....
]
}
~$ cat blobs/sha256/dfddba39ed871b3994726d34b0f33b313101c8262567590c02d3763f9f5a27f6 | jq
{
"schemaVersion": 2,
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:97895c15ee91c7d4957334444c1a449c52fc6c2f90a7113b49d77c8718f7d8c4",
"size": 3058
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:45337b3433fa437664bcf9edca3230f1f37043cc61dab03199d6d",
"size": 26692286
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:36d0aa647669adaa137c59f52b7e91018ef63c75a0daee690963133217ee47b3",
"size": 54120976
},
.......
]
}
These hashes are represented in Digests format to enable and uniquely identify image resources.
Digest format is
digest ::= algorithm ":" encoded sha256:6c3c624b58dbbcd3c0dd82b4c53f04194d1247c6eebdaab7c610cf7d66709b3b
Since we're talking about hashes, we need to distinguish between content hashes and distribution hashes.
Content hashes | Distribution hashes |
---|---|
A content hash is used for uncompressed data present in the layers. | A distribution artifact hash is simply a hash of the compressed blob. |
| To hash a gzipped item, compression must first occur. |
| Check the downloaded layers' integrity without first uncompressing them. |
| The use of distribution artifact hashes eliminates the requirement to maintain a mapping of content hashes. |
The root file system is the hierarchical file tree. It contains the files and directories critical for system operation, including the mounting device directory and programs for booting the container processes.
But how to build this hierarchical file order?
Well, the answer is image layers, also known as achieve files. We record file system changes (addition of files, removal of files, or updates) as achieve files. The first achieve file is the base layer; all the other achieve files contain only the changes to its base. Together these achieve files, in chronological order, are what make up the final Root Filesystem.
But ….. I still have questions
{
"created": "2021-01-07T01:29:27.650294696Z",
"architecture": "amd64",
"os": "linux",
"config": {
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/hello"
]
},
"rootfs": {
"type": "layers",
"diff_ids": [
"sha256:c6f988f4874bb0add23a778f753c65efe992244e148a1d2ec2a8b664fb66bbd1",
"sha256:5f70bf18a086007016e948b04aed3b82103a36bea41755b6cddfaf10ace3c6ef"
],
.....
},
"history": [
.....
],
.....
.....
}
$ ALGORITHM="sha256"
$ BUILD_DIR="build"
$ BLOBS_DIR="$BUILD_DIR/blobs/$ALGORITHM"
$ mkdir -p $BLOBS_DIR
# GET AUTH TOKEN
$ TOKEN=$(curl "//auth.docker.io/token?service=registry.docker.io&scope=repository:library/busybox:pull" | jq -r .token)
# PULL LATEST MANIFEST (DOCKER V2) OF BUSYBOX FROM THE REGISTRY
$ curl //registry.hub.docker.com/v2/library/busybox/manifests/latest \
-H "Accept: application/vnd.docker.distribution.manifest.v2+json" \
-H "Authorization: Bearer $TOKEN" \
-L \
-o manifest.json
# PARSE LAYERS REFs FROM THE MANIFEST
$ CONFIG_LAYER=$(cat manifest.json | jq -r '.config.digest')
$ ROOTFS_LAYER_DIGEST=$(cat manifest.json | jq -r '.layers[0].digest')
$ ROOTFS_LAYER_SIZE=$(cat manifest.json | jq -r '.layers[0].size')
# PULL CONFIG LAYER OF BUSYBOX FROM THE REGISTRY
$ curl //registry.hub.docker.com/v2/library/busybox/blobs/$CONFIG_LAYER \
-H "Authorization: Bearer $TOKEN" \
-L \
-o config.json
$ ROOTFS_LAYER_DIFF=$(cat config.json | jq -r '.rootfs.diff_ids[0]')
# PULL ROOT FILE SYTEM LAYER OF BUSYBOX FROM THE REGISTRY
$ curl //registry.hub.docker.com/v2/library/busybox/blobs/$ROOTFS_LAYER_DIGEST \
-H "Authorization: Bearer $TOKEN" \
-L \
-o $BLOBS_DIR/${ROOTFS_LAYER_DIGEST//$ALGORITHM:/}
# CREATE APPLICATION LAYER LOCALLY
$ APP_LAYER="app-layer.tar"
$ GZIP_APP_LAYER="$APP_LAYER.gz"
$ echo 'echo "Hello World"' > hello.sh && chmod +x hello.sh
$ tar -cvf $APP_LAYER hello.sh
# CALCULATE LAYER'S DIGEST, DIFFID AND SIZE
$ APP_LAYER_DIFF="$(sha256sum < $APP_LAYER | sed 's/\s*-//g')"
$ APP_LAYER_DIGEST="$(gzip < $APP_LAYER > $GZIP_APP_LAYER | sha256sum | sed 's/\s*-//g')"
$ APP_LAYER_SIZE="$(stat -c%s $GZIP_APP_LAYER)"
# MOVE APPLICATION LAYER TO BLOBS FOLDER
$ cp $GZIP_APP_LAYER "$BLOBS_DIR/$APP_LAYER_DIGEST"
# OCI container image layout
# ├── blobs
# │ └── sha256
# │ ├── DIFFID (image.manifest)
# │ └── DIFFID (image.config)
# │ └── DIFFID (image.rootfs)
# │ └── DIFFID (image.application)
# ├── index.json
# └── oci-layout
# CREATE OCI-LAYOUT FILE
$ echo > "$BUILD_DIR/oci-layout" << EOF
{
"imageLayoutVersion": "1.0.0"
} EOF
##
## CREATE OCI-CONFIG LAYER
$ CONFIG_LAYER="image-config.json"
$ echo > $CONFIG_LAYER << EOF
{
"created": "2020-04-07T01:29:27.650294696Z",
"architecture": "amd64",
"os": "linux",
"config": {
"Env": [
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
],
"Cmd": [
"/hello"
]
},
"rootfs": {
"type": "layers",
"diff_ids": [
"$ROOTFS_LAYER_DIFF",
"$APP_LAYER_DIFF"
]
},
"history": []
}
EOF
$ CONFIG_LAYER_DIGEST="$(sha256sum < $CONFIG_LAYER | sed 's/\s*-//g')"
$ CONFIG_LAYER_SIZE="$(stat -c%s $CONFIG_LAYER)"
$ cp $CONFIG_LAYER "$BLOBS_DIR/$CONFIG_LAYER_DIGEST"
# CREATE OCI-CONFIG LAYER
$ MANIFEST_LAYER="image-manifest.json"
$ cat > $MANIFEST_LAYER << EOF
{
"schemaVersion": 2,
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "$CONFIG_LAYER_DIGEST",
"size": $CONFIG_LAYER_SIZE
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "$ROOTFS_LAYER_DIGEST",
"size": $ROOTFS_LAYER_SIZE
},
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "$APP_LAYER_DIGEST",
"size": $APP_LAYER_SIZE
}
]
}
EOF
$ MANIFEST_LAYER_DIGEST="$(sha256sum < $MANIFEST_LAYER | sed 's/\s*-//g')"
$ MANIFEST_LAYER_SIZE="$(stat -c%s $MANIFEST_LAYER)"
$ cp $MANIFEST_LAYER "$BLOBS_DIR/$MANIFEST_LAYER_DIGEST"
# CREATE INDEX FILE
$ MANIFESTS_INDEX="index.json"
$ cat > "$BUILD_DIR/$MANIFESTS_INDEX" << EOF
{
"schemaVersion": 2,
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "$MANIFEST_LAYER_DIGEST",
"size": $MANIFEST_LAYER_SIZE,
"annotations": {
"org.opencontainers.image.ref.name": "latest"
}
}
]
}
EOF
# CREATE OCI-LAYOUT FOLDER STRUCTURE
$ tree .
├── blobs
│ └── sha256
│ ├── 01ba4719c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b
│ ├── 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa
│ ├── 9710b9c0c7c956ca8d2884ee73d7fb578921f127f1eae05078965b2e3125b8de
│ └── e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
├── index.json
└── oci-layout
2 directories, 6 files
# $
# ├── blobs
# │ └── sha256
# │ ├── 5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa
# │ └── e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
# STEP0: CREATE ROOTFS FOLDER
cd $BUNDLE_DIR
BUNDLE_DIR="runtime_bundle"
mkdir $BUNDLE_DIR && cd $BUNDLE_DIR
# STEP1: EXTRACT BUSYBOX LAYER INTO ROOTFS FOLDER
$ tar -vxzf ../$BUNDLE_DIR/$BLOBS_DIR/5cc84ad355aaa64f46ea9c7bbcc319a9d808ab15088a27209c9e70ef86e5a2aa -C rootfs
# STEP2: EXTRACT APPLICATION LAYER INTO ROOTFS FOLDER
$ tar -vxzf ../$BUNDLE_DIR/$BLOBS_DIR/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 -C rootfs
# STEP3: ALLOW RUNC TO CREATE A DUMMY CONFIGURE FILE FOR YOU AND RUN
# runc spec
# runc run test
# ls
bin dev etc hello.sh home proc root sys tmp usr var
# sh hello.sh
Hello World