Stuck
Stuck

Reputation: 12292

can `bootBuildImage` create writeable volumes?

Given a spring boot app that writes files to /var/lib/app/files.

I create an docker image with the gradle task:

./gradlew bootBuildImage --imageName=app:latest

Then, I want to use it in docker-compose:

version: '3.5'

services:

  app:
    image: app:latest
    volumes:
      - app-storage:/var/lib/app/files
    // ...ports etc

volumes:
  app-storage:

This will fail, because the folder is created during docker-compose up and is owned by root and the app, hence, has no write access to the folder.

The quick fix is to run the image as root by specifying user: root:

version: '3.5'

services:

  app:
    image: app:latest
    user: root # <------------ required
    volumes:
      - app-storage:/var/lib/app/files
    // ...ports etc

volumes:
  app-storage:

This works fine, but I do not want to run it as root. I wonder how to achieve it? I normally could create a Dockerfile that creates the desired folder with correct ownership and write permissions. But as far as I know build packs do not use a custom Dockerfile and hence bootBuildImage would not use it - correct? How can we create writable volumes then?

Upvotes: 2

Views: 1267

Answers (1)

Stuck
Stuck

Reputation: 12292

By inspecting the image I found that the buildpack uses /cnb/lifecycle/launcher to launch the application. Hence I was able to customize the docker command and fix the owner of the specific folder before launch:

version: '3.5'

services:

  app:
    image: app:latest
    # enable the app to write to the storage folder (docker will create it as root by default)
    user: root
    command: "/bin/sh -c 'chown 1000:1000 /var/lib/app/files && /cnb/lifecycle/launcher'"
    volumes:
      - app-storage:/var/lib/app/files
    // ...ports etc

volumes:
  app-storage:

Still, this is not very nice, because it is not straight forward (and hence my future self will need to spent time on understand it again) and also it is very limited in its extensibility.

Update 30.10.2020 - Spring Boot 2.3

We ended up creating another Dockerfile/layer so that we do not need to hassle with this in the docker-compose file:

# The base_image should hold a reference to the image created by ./gradlew bootBuildImage
ARG base_image
FROM ${base_image}

ENV APP_STORAGE_LOCAL_FOLDER_PATH /var/lib/app/files

USER root

RUN mkdir -p ${APP_STORAGE_LOCAL_FOLDER_PATH}

RUN chown ${CNB_USER_ID}:${CNB_GROUP_ID} ${APP_STORAGE_LOCAL_FOLDER_PATH}

USER ${CNB_USER_ID}:${CNB_GROUP_ID}

ENTRYPOINT /cnb/lifecycle/launcher

Update 25.11.2020 - Spring Boot 2.4

Note that the above Dockerfile will result in this error:

ERROR: failed to launch: determine start command: when there is no default process a command is required

The reason is that the default entrypoint by the paketo builder changed. Changing the entrypoint from /cnb/lifecycle/launcher to the new one fixes it:

ENTRYPOINT /cnb/process/web

See also this question: ERROR: failed to launch: determine start command: when there is no default process a command is required

Upvotes: 1

Related Questions