From d07185693ceecdab9e0fab0ce872092f9fe01359 Mon Sep 17 00:00:00 2001 From: Jakub Kaczmarzyk Date: Tue, 8 Sep 2020 14:58:25 -0400 Subject: [PATCH] Update Dockerfile and add README.docker - Fix permissions in `/opt` to allow non-root user to compile Anki (which requires reading and/or creating files below `/opt`). - Create `pythonbuilder` stage, which runs only `make build`. The wheels from this stage are copied into the final stage. - Add README.docker --- Dockerfile | 41 ++++++++++++++++----- README.docker | 99 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+), 8 deletions(-) create mode 100644 README.docker diff --git a/Dockerfile b/Dockerfile index 163a9f5aa..51031c5bf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,12 +1,17 @@ -FROM python:3.8 AS builder +ARG PYTHON_VERSION="3.8" -ARG DEBIAN_FRONTEND="noninteractive" +FROM python:$PYTHON_VERSION AS dependencies + +# Allow non-root users to install things and modify installations in /opt. +RUN chmod 777 /opt && chmod a+s /opt # Install rust. ENV CARGO_HOME="/opt/cargo" \ RUSTUP_HOME="/opt/rustup" ENV PATH="$CARGO_HOME/bin:$PATH" -RUN curl -fsSL --proto '=https' --tlsv1.2 https://sh.rustup.rs \ +RUN mkdir $CARGO_HOME $RUSTUP_HOME \ + && chmod a+rws $CARGO_HOME $RUSTUP_HOME \ + && curl -fsSL --proto '=https' --tlsv1.2 https://sh.rustup.rs \ | sh -s -- -y --quiet --no-modify-path \ && rustup update \ && cargo install ripgrep @@ -16,6 +21,16 @@ RUN apt-get update \ && apt-get install --yes --no-install-recommends \ gettext \ lame \ + libnss3 \ + libxcb-icccm4 \ + libxcb-image0 \ + libxcb-keysyms1 \ + libxcb-randr0 \ + libxcb-render-util0 \ + libxcb-xinerama0 \ + libxcb-xkb1 \ + libxkbcommon-x11-0 \ + libxcomposite1 \ mpv \ portaudio19-dev \ rsync \ @@ -34,14 +49,24 @@ RUN curl -fsSL --proto '=https' -O https://github.com/protocolbuffers/protobuf/r && rm protoc-3.11.4-linux-x86_64.zip ENV PATH="/opt/protoc/bin:$PATH" -# Build anki. +# Allow non-root users to install toolchains and update rust crates. +RUN chmod 777 $RUSTUP_HOME/toolchains $RUSTUP_HOME/update-hashes $CARGO_HOME/registry \ + && chmod -R a+rw $CARGO_HOME/registry \ + # Necessary for TypeScript. + && chmod a+w /home + +# Build anki. Use a separate image so users can build an image with build-time +# dependencies. +FROM dependencies AS builder WORKDIR /opt/anki COPY . . -RUN make develop \ - && make build +RUN make develop + +FROM builder AS pythonbuilder +RUN make build # Build final image. -FROM python:3.8-slim +FROM python:${PYTHON_VERSION}-slim # Install system dependencies. RUN apt-get update \ @@ -64,7 +89,7 @@ RUN apt-get update \ && rm -rf /var/lib/apt/lists/* # Install pre-compiled Anki. -COPY --from=builder /opt/anki/dist/ /opt/anki/ +COPY --from=pythonbuilder /opt/anki/dist/ /opt/anki/ RUN python -m pip install --no-cache-dir \ PyQtWebEngine \ /opt/anki/*.whl \ diff --git a/README.docker b/README.docker new file mode 100644 index 000000000..e098b9c4b --- /dev/null +++ b/README.docker @@ -0,0 +1,99 @@ +Anki in Docker +============== + +Docker provides a standard method of installing software. This is particularly helpful +when software requires a complex set of dependencies, like Anki. + +## Running Anki in Docker + +Build and then run the image. The `docker run` command below runs the image as the +current user, and it mounts the user's `$HOME` directory, which is where Anki stores +its local files. + +``` +docker build --tag anki . +xhost +local:root # Undo when done with `xhost -local:root` +docker run \ + --rm -it \ + --user 1000:1000 \ + --volume $HOME/.local/share:$HOME/.local/share:rw \ + --volume /etc/passwd:/etc/passwd:ro \ + --volume /tmp/.X11-unix:/tmp/.X11-unix:rw \ + --env DISPLAY=$DISPLAY \ + anki +xhost -local:root +``` + +## Developing Anki in Docker + +Build your local source tree in Docker. + +1. Build the Docker image with build-time dependencies. The Anki Dockerfile uses +multi-stage builds, so the target is the first stage, which includes only the +dependencies. + + ``` + docker build --tag anki:dependencies --target dependencies . + ``` + +2. Compile your source tree + + Start the image with dependencies in the background. It is important to run the + image as the current user, because otherwise, some files in the source tree will be + owned by root. Find user id with `id -u` and group ID with `id -g`. These values + are passed to `--user` as in `--user $(id -u):$(id -g)`. + + ``` + docker run --rm -it \ + --name ankibuilder \ + --detach \ + --workdir /work + --volume "$PWD":/work:rw \ + --user 1000:1000 \ + --volume /etc/passwd:/etc/passwd:ro \ + --volume /tmp/.X11-unix:/tmp/.X11-unix:rw \ + --env DISPLAY=$DISPLAY \ + anki:dependencies bash + ``` + + Allow the Docker container to use the local X server and show the GUI. + + ``` + xhost +local:root + ``` + + (Undo this when done with `xhost -local:root`) + + Compile. + + ``` + docker exec -it ankibuilder make run + ``` + + The Anki graphical user interface should appear. The first run will take some time + because Rust code has to be compiled and Python dependencies have to be downloaded, + etc. The following runs will be much faster. + + To compile without running the GUI, use `make develop`. + +3. Other common operations + + If system packages need to be installed, use `apt-get` as below. The Docker image + is based on a Debian Stable image. + + ``` + docker exec -it --user root ankibuilder apt-get update + docker exec -it --user root ankibuilder apt-get install PACKAGES + ``` + + An interactive bash shell can be started with + + ``` + docker exec -it ankibuilder bash + ``` + + or as root user + + ``` + docker exec -it --user root ankibuilder bash + ```