Why does Docker not make editing volumes easy?
I am migrating a small Discord bot I made to Docker (and eventually Kubernetes). This bot’s architecture is roughly like this:
- A scheduler calls a 3rd-party API and stores data in a SQLite database.
- A Discord bot interacts with the database to store user preferences and retrieve cached data from the external API.
This setup has been running for a while. Everything is working fine. I am now migrating it to run with a Docker volume representing the database.
This is fairly straightforward. I can create my volume with this command:
docker volume create persistent_db
Then… how do I put files there?
What's missing?
It feels like a tool is missing—the equivalent of the usual system manipulation commands provided by an OS:
ls
cp
mv
rm
- etc.
I would like to be able to do something like:
# copy sqlite.db to the persistent_db volume
cp sqlite.db persistent_db:sqlite.db
What docker recommends
The normal pattern in Docker is to only access volume files from a container. Volumes are meant to be opaque types that the administrator doesn’t handle manually.
The proper recipe is:
docker run -rm -v persistent_db:/data -v $PWD:/src busybox cp src/sqlite.db data/sqlite.db
In my case, though — as I am developing and migrating to the new environment — I would like to be able to manually edit my containers.
Shell script
You can use this script:
#!/usr/bin/env bash
set -e
COMMAND="$1"
SRC_ARG="$2"
DST_ARG="${3:-}"
# Parse source volume and path (format: volume:path or just path)
if echo "$SRC_ARG" | grep -q ":"; then
SRC_MOUNT="${SRC_ARG%%:*}" # everything before ':'
SRC_PATH="${SRC_ARG#*:}" # everything after ':'
else
SRC_MOUNT="$PWD" # current directory
SRC_PATH="$SRC_ARG" # whole argument
fi
# Parse destination volume and path (if provided)
if echo "$DST_ARG" | grep -q ":"; then
DST_MOUNT="${DST_ARG%%:*}"
DST_PATH="${DST_ARG#*:}"
else
DST_MOUNT="$PWD"
DST_PATH="$DST_ARG"
fi
case "$COMMAND" in
cp|mv)
docker run --rm -v "$SRC_MOUNT":/src -v "$DST_MOUNT":/dst busybox \
"$COMMAND" "/src/$SRC_PATH" "/dst/$DST_PATH"
;;
rm)
docker run --rm -v "$SRC_MOUNT":/src busybox rm -rf "/src/$SRC_PATH"
;;
ls)
docker run --rm -v "$SRC_MOUNT":/src busybox ls -l "/src/$SRC_PATH"
;;
*)
echo "Unsupported command: $COMMAND"
echo "Available commands: cp, mv, rm, ls"
exit 1
;;
esac
You can then use the command as you normally would:
dv cp sqlite.db persitent_db:sqlite.db
dv ls persistent_db
etc...
I know this isn’t the way Docker intends it to be used, but as I am working on my project, I find it extremely convenient.