Sharing volumes to Docker as the right user

docker
development
Published

August 6, 2014

Yesterday’s post provided a couple of minimal Docker images for spinning up containers to do Scala and Java builds and tests. The second of the images had a pre-loaded Ivy cache for Apache Spark’s dependencies installed in /root. The setup I suggested for launching these images included mounting a directory from your host inside the container using Docker’s -v option, but this is less than ideal: any files that are written or modified during a build or test run will be owned by root instead of by whoever owned the directory on the host!

As I understand it, Docker will be able to map container users to host users in the future, but for now we can work around this problem with a quick hack. The docker run command lets us pass environment variables that will be set inside the container, and we can use this to make sure that any files written in the container get owned by the right user on the host. Here’s what I did:

  1. I created another image based on my Spark development image, which I called willb/java-dev:centos7-spark-uid
  2. This new image adds a normal user called dockerdev and copies the Ivy, Maven, and sbt caches from /root to that user’s home directory.
  3. The new image runs a small script on container launch that fixes up the user ID and group ID of the dockerdev user and its home directory based on the DEV_UID and DEV_GID environment variables supplied to docker run before substituting to the dockerdev user and executing a shell.

So to launch a container for Spark development in which your ~/devel directory is mapped to /devel and in which all changes to that directory wind up owned by you on the host, you’d do something like this:

docker run --privileged=true -e DEV_UID=$UID -e DEV_GID=$GID -i -t -v ${HOME}/devel:/devel willb/java-dev:centos7-spark-uid

Again, this is a hack – use it at your own risk! –, but it’s useful until Docker supports user mapping more cleanly. I’m interested to hear about your approaches to solving this problem as well!




Appendix: boot.sh, the user-mapping boot script

#!/bin/sh

export ORIGPASSWD=$(cat /etc/passwd | grep dockerdev)
export ORIG_UID=$(echo $ORIGPASSWD | cut -f3 -d:)
export ORIG_GID=$(echo $ORIGPASSWD | cut -f4 -d:)

export DEV_UID=${DEV_UID:=$ORIG_UID}
export DEV_GID=${DEV_GID:=$ORIG_GID}

ORIG_HOME=$(echo $ORIGPASSWD | cut -f6 -d:)

sed -i -e "s/:$ORIG_UID:$ORIG_GID:/:$DEV_UID:$DEV_GID:/" /etc/passwd
sed -i -e "s/dockerdev:x:$ORIG_GID:/dockerdev:x:$DEV_GID:/" /etc/group

chown -R ${DEV_UID}:${DEV_GID} ${ORIG_HOME}

exec su - dockerdev