From 87020de9176a11fdb5e9ec0fc909269e763708b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sascha=20I=C3=9Fbr=C3=BCcker?= Date: Sun, 5 Nov 2023 14:18:26 +0100 Subject: [PATCH] Add Alpine based Docker image (experimental) (#570) * use alpine as base image * try fix missing mime types * Extract separate Dockerfile * Restore playwright dev dependency * Add info to README.md --- .dockerignore | 46 +++++-------- README.md | 20 +++++- docker/alpine.Dockerfile | 89 +++++++++++++++++++++++++ Dockerfile => docker/default.Dockerfile | 16 +++-- scripts/build-docker.sh | 7 ++ scripts/run-docker.sh | 4 +- 6 files changed, 145 insertions(+), 37 deletions(-) create mode 100644 docker/alpine.Dockerfile rename Dockerfile => docker/default.Dockerfile (89%) diff --git a/.dockerignore b/.dockerignore index 123097d..fe71012 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,34 +1,22 @@ -# Remove project files, data, tmp files, build files -/.env -/.idea -/data -/node_modules -/tmp -/docs -/static -/scripts -/build -/out -/.git -/.devcontainer +# Ignore everything +* -/.dockerignore -/.gitignore -/.gitattributes -/Dockerfile -/docker-compose.yml -/*.sh -/*.iml -/*.patch -/*.md -/*.js -/*.log -/*.pid +# Include files required for build or at runtime +!/bookmarks +!/siteroot -# Whitelist files needed in build or prod image -!/rollup.config.js -!/bootstrap.sh !/background-tasks-wrapper.sh +!/bootstrap.sh +!/LICENSE.txt +!/manage.py +!/package.json +!/package-lock.json +!/requirements.prod.txt +!/requirements.txt +!/rollup.config.js +!/supervisord.conf +!/uwsgi.ini +!/version.txt -# Remove development settings + # Remove dev settings /siteroot/settings/dev.py diff --git a/README.md b/README.md index 8ce6e75..7ea3475 100644 --- a/README.md +++ b/README.md @@ -58,9 +58,27 @@ The name comes from: linkding is designed to be run with container solutions like [Docker](https://docs.docker.com/get-started/). The Docker image is compatible with ARM platforms, so it can be run on a Raspberry Pi. -By default, linkding uses SQLite as a database. +linkding uses an SQLite database by default. Alternatively linkding supports PostgreSQL, see the [database options](docs/Options.md#LD_DB_ENGINE) for more information. +
+ +🧪 Alpine-based image + +The default Docker image (`latest` tag) is based on a slim variant of Debian Linux. +Alternatively, there is an image based on Alpine Linux (`latest-alpine` tag) which has a smaller size, resulting in a smaller download and less disk space required. +The Alpine image is currently about 45 MB in compressed size, compared to about 130 MB for the Debian image. + +To use it, replace the `latest` tag with `latest-alpine`, either in the CLI command below when using Docker, or in the `docker-compose.yml` file when using docker-compose. + +> [!WARNING] +> The image is currently considered experimental in order to gather feedback and iron out any issues. +> Only use it if you are comfortable running experimental software or want to help out with testing. +> While there should be no issues with creating new installations, there might be issues when migrating existing installations. +> If you plan to migrate your existing installation, make sure to create proper [backups](https://github.com/sissbruecker/linkding/blob/master/docs/backup.md) first. + +
+ ### Using Docker To install linkding using Docker you can just run the [latest image](https://hub.docker.com/repository/docker/sissbruecker/linkding) from Docker Hub: diff --git a/docker/alpine.Dockerfile b/docker/alpine.Dockerfile new file mode 100644 index 0000000..e367436 --- /dev/null +++ b/docker/alpine.Dockerfile @@ -0,0 +1,89 @@ +FROM node:18.18.0-alpine AS node-build +WORKDIR /etc/linkding +# install build dependencies +COPY rollup.config.js package.json package-lock.json ./ +RUN npm install +# copy files needed for JS build +COPY bookmarks/frontend ./bookmarks/frontend +# run build +RUN npm run build + + +FROM python:3.10.13-alpine3.18 AS python-base +RUN apk update && apk add alpine-sdk linux-headers libpq-dev pkgconfig icu-dev sqlite-dev +WORKDIR /etc/linkding + + +FROM python-base AS python-build +# install build dependencies +COPY requirements.txt requirements.txt +# remove playwright from requirements as there is not always a distro and it's not needed for the build +RUN sed -i '/playwright/d' requirements.txt +RUN pip install -U pip && pip install -Ur requirements.txt +# copy files needed for Django build +COPY . . +COPY --from=node-build /etc/linkding . +# run Django part of the build +RUN python manage.py compilescss && \ + python manage.py collectstatic --ignore=*.scss && \ + python manage.py compilescss --delete-files + + +FROM python-base AS prod-deps +COPY requirements.prod.txt ./requirements.txt +RUN mkdir /opt/venv && \ + python -m venv --upgrade-deps --copies /opt/venv && \ + /opt/venv/bin/pip install --upgrade pip wheel && \ + /opt/venv/bin/pip install -Ur requirements.txt + + +FROM python-base AS compile-icu +# Defines SQLite version +# Since this is only needed for downloading the header files this probably +# doesn't need to be up-to-date, assuming the SQLite APIs used by the ICU +# extension do not change +ARG SQLITE_RELEASE_YEAR=2023 +ARG SQLITE_RELEASE=3430000 + +# Compile the ICU extension needed for case-insensitive search and ordering +# with SQLite. This does: +# - Download SQLite amalgamation for header files +# - Download ICU extension source file +# - Compile ICU extension +RUN wget https://www.sqlite.org/${SQLITE_RELEASE_YEAR}/sqlite-amalgamation-${SQLITE_RELEASE}.zip && \ + unzip sqlite-amalgamation-${SQLITE_RELEASE}.zip && \ + cp sqlite-amalgamation-${SQLITE_RELEASE}/sqlite3.h ./sqlite3.h && \ + cp sqlite-amalgamation-${SQLITE_RELEASE}/sqlite3ext.h ./sqlite3ext.h && \ + wget https://www.sqlite.org/src/raw/ext/icu/icu.c?name=91c021c7e3e8bbba286960810fa303295c622e323567b2e6def4ce58e4466e60 -O icu.c && \ + gcc -fPIC -shared icu.c `pkg-config --libs --cflags icu-uc icu-io` -o libicu.so + + +FROM python:3.10.13-alpine3.18 AS final +# install runtime dependencies +RUN apk update && apk add bash curl icu libpq mailcap +# create www-data user and group +RUN set -x ; \ + addgroup -g 82 -S www-data ; \ + adduser -u 82 -D -S -G www-data www-data && exit 0 ; exit 1 +WORKDIR /etc/linkding +# copy prod dependencies +COPY --from=prod-deps /opt/venv /opt/venv +# copy output from build stage +COPY --from=python-build /etc/linkding/static static/ +# copy compiled icu extension +COPY --from=compile-icu /etc/linkding/libicu.so libicu.so +# copy application code +COPY . . +# Expose uwsgi server at port 9090 +EXPOSE 9090 +# Activate virtual env +ENV VIRTUAL_ENV /opt/venv +ENV PATH /opt/venv/bin:$PATH +# Allow running containers as an an arbitrary user in the root group, to support deployment scenarios like OpenShift, Podman +RUN chmod g+w . && \ + chmod +x ./bootstrap.sh + +HEALTHCHECK --interval=30s --retries=3 --timeout=1s \ +CMD curl -f http://localhost:${LD_SERVER_PORT:-9090}/${LD_CONTEXT_PATH}health || exit 1 + +CMD ["./bootstrap.sh"] diff --git a/Dockerfile b/docker/default.Dockerfile similarity index 89% rename from Dockerfile rename to docker/default.Dockerfile index 8f2111b..88ceb0d 100644 --- a/Dockerfile +++ b/docker/default.Dockerfile @@ -1,11 +1,11 @@ FROM node:18.18.0-alpine AS node-build WORKDIR /etc/linkding # install build dependencies -COPY package.json package-lock.json ./ -RUN npm install -g npm && \ - npm install -# compile JS components -COPY . . +COPY rollup.config.js package.json package-lock.json ./ +RUN npm install +# copy files needed for JS build +COPY bookmarks/frontend ./bookmarks/frontend +# run build RUN npm run build @@ -17,9 +17,13 @@ WORKDIR /etc/linkding FROM python-base AS python-build # install build dependencies COPY requirements.txt requirements.txt +# remove playwright from requirements as there is not always a distro and it's not needed for the build +RUN sed -i '/playwright/d' requirements.txt RUN pip install -U pip && pip install -Ur requirements.txt -# run Django part of the build +# copy files needed for Django build +COPY . . COPY --from=node-build /etc/linkding . +# run Django part of the build RUN python manage.py compilescss && \ python manage.py collectstatic --ignore=*.scss && \ python manage.py compilescss --delete-files diff --git a/scripts/build-docker.sh b/scripts/build-docker.sh index dc0dbac..9311dac 100755 --- a/scripts/build-docker.sh +++ b/scripts/build-docker.sh @@ -3,6 +3,13 @@ version=$(