In a previous post, we saw how to dockerize the build plan of an application. Typically, you’ll want the build to run tasks like linting and unit tests, and then publish the results of these operations as XML reports that the build server can consume and present in a human friendly way.
Since these tasks now run within a Docker container, the XML reports will be generated within the container’s filesystem. The build agent can’t access that, unless we use a Docker volume. With the volume, the reports are accessible but they are owned by the root user. This is a problem because the build agent is not running as root (hopefully) and won’t be able to delete these reports when it needs to cleanup (e.g. in order to run the next build in a clean workspace).
The easiest approach is what we did in the post about dockerizing the build plan, simply run the chown command after we use the image, to make sure permissions are back to normal.
Coincidentally, this is exactly what TeamCity does as well (so you don’t need to do it yourself). From the documentation:
After the build step with the Docker wrapper, a build agent will run the chown command to restore access of the buildAgent user to the checkout directory. This mitigates a possible problem when the files from a Docker container are created with the 'root' ownership and cannot be removed by the build agent later.
If your CI server doesn’t support this, you can also try another trick: use a custom entrypoint in the Docker image.
Let’s see the old Dockerfile:
FROM node:8.9-alpine RUN mkdir -p /app ADD package.json /app ADD package-lock.json /app WORKDIR /app RUN npm install ADD . /app
Wouldn’t it be great if the permissions got automatically fixed whenever we run a command with this image? We can do that with a custom entrypoint:
FROM node:8.9-alpine RUN mkdir -p /app ADD package.json /app ADD package-lock.json /app WORKDIR /app RUN npm install ADD . /app ENV HOST_USER_ID= ENV HOST_GROUP_ID= ADD npm-entrypoint.sh /usr/local/bin/npm-entrypoint.sh ENTRYPOINT ["/usr/local/bin/npm-entrypoint.sh"] CMD ["--help"]
And the entrypoint script itself looks like this:
#!/bin/sh npm run $@ if [ -n "$HOST_USER_ID" -a -n "$HOST_GROUP_ID" ]; then chown -R $HOST_USER_ID:$HOST_GROUP_ID . fi
And using the image changes into this:
docker run --rm \ -e HOST_USER_ID=$(id -u) \ -e HOST_GROUP_ID=$(id -g) \ blog-helm-ci lint
(it used to be just
docker run --rm blog-helm-ci npm run lint)
What’s going on? We use the environment variables
HOST_GROUP_ID to pass the user id and group id of the build agent
into the Docker container. We retrieve these with
$(id -u) and
With the entrypoint, we’re able to run the custom shell script
npm-entrypoint.sh upon every usage of the Docker image. The script
npm run with whatever extra arguments we pass and then
it corrects the permissions on the current folder, setting the ownership to the
user and group of the build agent’s user. This will permit the build agent to
clean up these files later.
This is a slightly more complicated approach than just running chown ourselves after using the image, but the advantage is that we don’t need to worry about it, as the image itself now takes care of it. Additionally, we are able to lock down the image so that it only runs npm commands.