An OCI hook for archiving overlay mount upperdir after container is done
For some OCI tools like podman, it allows you to mount a container image in either readonly or read-write mode. Like this:
podman run --mount type=image,source=my-data-image,destination=/data,rw=true -it alpine
A read-write mount in the OCI spec config may look like this
{
"destination": "/data",
"type": "overlay",
"source": "/home/user/.local/share/containers/storage/overlay-containers/f6e6a7a7eaeb695bb433da1e057d92d9c2e376fb9920792c809d2c1af49e5709/userdata/overlay/3190055391/merge",
"options": [
"lowerdir=/home/user/.local/share/containers/storage/overlay/7877ad4aca46f49c306c8044f0d2a1528b642db9aed165f8b022f2b59fc9c237/merged",
"upperdir=/home/user/.local/share/containers/storage/overlay-containers/f6e6a7a7eaeb695bb433da1e057d92d9c2e376fb9920792c809d2c1af49e5709/userdata/overlay/3190055391/upper",
"workdir=/home/user/.local/share/containers/storage/overlay-containers/f6e6a7a7eaeb695bb433da1e057d92d9c2e376fb9920792c809d2c1af49e5709/userdata/overlay/3190055391/work",
"private",
"userxattr"
]
}
As you can see it's an overlayfs mount.
Then you can modify the content files of the image at the mounted directory in the container.
For many usecases, one could be to capture the changes made to the mounted from the container.
Given the example shown above, the changes made to the merge
will result in the upper
directory:
/home/user/.local/share/containers/storage/overlay-containers/f6e6a7a7eaeb695bb433da1e057d92d9c2e376fb9920792c809d2c1af49e5709/userdata/overlay/3190055391/upper
With the changes to the files in the original mounted image captured, you can then make a layer-based file system with revision history.
You can think about using it like Docker images or OCI container images but with just data changes in each layers.
The job of this hook is to archive the upperdir
after the container stops.
To use this hook, you need to add annotations to your container to tell it which upperdir of the desired overlay mount point and a given destination path to copy to after the container stops. There are two types of annotation you can add, one for the mount point and another for the destination of archive.
- com.launchplatform.oci-hooks.archive-overlay.<ARCHIVE_NAME>.mount-point
- com.launchplatform.oci-hooks.archive-overlay.<ARCHIVE_NAME>.archive-to
- com.launchplatform.oci-hooks.archive-overlay.<ARCHIVE_NAME>.success (optional)
- com.launchplatform.oci-hooks.archive-overlay.<ARCHIVE_NAME>.method (optional)
- com.launchplatform.oci-hooks.archive-overlay.<ARCHIVE_NAME>.tar-content-owner (optional)
The ARCHIVE_NAME
can be any valid annotation string without a dot in it.
The mount-point
and archive-to
annotations with the same archive name need to appear in pairs, otherwise it will be ignored.
For example, to archive the upperdir of mount point at /data
to /path/to/my-archive
, you can add annotations like this
com.launchplatform.oci-hooks.archive-overlay.data.mount-point=/data
com.launchplatform.oci-hooks.archive-overlay.data.archive-to=/path/to/my-archive
Please note that the mount-point
path should be a destination
field of the mount, i.e, it's in the container namespace.
And the archive-to
should be a valid path in the runtime namespace.
Here's an example command with podman:
podman run \
--annotation=com.launchplatform.oci-hooks.archive-overlay.data.mount-point=/data \
--annotation=com.launchplatform.oci-hooks.archive-overlay.data.archive-to=/tmp/my-archive \
--mount type=image,source=my-data-image,destination=/data,rw=true \
-it alpine
# Change /data folder in the container then exit
ls /tmp/my-archive
The success
is a path to the empty file to be created as an indicator of a successful archive.
The method
option by default is copy
, if you want to archive the upperdir as a tar.gz file, you can set it to tar.gz
instead.
If you want to change the content file owner of the tar file, you can set tar-content-owner
value, such as 2000
or 2000:3000
.
Please note that only integer uid and gid supported, username won't work.
There are different ways of running a container, if you are generating OCI spec yourself and running OCI runtimes such as crun yourself, you can add the poststop
hook directly into the spec file like this:
{
"//": "... other OCI spec content ...",
"hooks": {
"poststop": [
{
"path": "/usr/bin/archive_overlay"
}
]
}
}
For more information about the OCI spec schema, please see the document here.
Another way to add the OCI hook is to create a OCI hook config file. Here's an example:
{
"version": "1.0.0",
"hook": {
"path": "/usr/bin/archive_overlay"
},
"when": {
"annotations": {
"com\\.launchplatform\\.oci-hooks\\.archive-overlay\\.([^.]+)\\.mount-point": "(.+)",
"com\\.launchplatform\\.oci-hooks\\.archive-overlay\\.([^.]+)\\.archive-to": "(.+)"
}
},
"stages": ["poststop"]
}
For more information about the OCI hooks schema, please see the document here.
To debug the hook, you can add --log-level=debug
(or trace
if you need more details) argument for the archive_overlay
executable, it will print debug information.
With OCI runtimes like crun, you can also add an annotation like this:
run.oci.hooks.stderr=/path/to/stderr
to make the runtime redirect the stderr from the hook executable to specific file. Please note that podman invokes poststop hook instead of delegating it to crun, so the annotation won't work for podman.
You can also pass --syslog
option to make the hook omits log messages to syslog.
However, please ensure that you have syslog daemon running on your system otherwise the hook still runs but no log messages will be sent.