This is an automated email from the ASF dual-hosted git repository.

klesh pushed a commit to branch build/shrink-image-chown
in repository https://gitbox.apache.org/repos/asf/devlake.git

commit e9a92b180d5d719bf48be1296af60e3559781d22
Author: Klesh Wong <[email protected]>
AuthorDate: Mon Nov 2 16:27:47 2026 +0800

    build(docker): stop duplicating /app to fix its ownership
    
    `chgrp -R 0 /app && chmod -R g=u /app` ran as a final layer, after python
    packages, the uv binary, the python plugins and the server binary had 
already
    been written by earlier layers. Overlayfs has to copy every file it mutates
    into the new layer, so the image shipped a second full copy of /app: 
measured
    at 186.5 MB of a 516.6 MB image.
    
    Apply the ownership where the files are created instead:
    
    - add devlake to group 0 so it may chgrp the files it owns; its primary 
group
      stays devlake, so the runtime GID is unchanged
    - fold the pip and uv installs into one RUN and normalise /app/.local at the
      end of it
    - normalise /app/python at the end of the plugin build
    - create logs with group 0 up front
    
    Everything copied into the final stage is read-only at runtime, so 
`--chown=0:0`
    is enough; the modes it already has (755 binary, 644 docs) let any GID 0 
process
    read and execute it.
    
    Verified with a replica image that the ownership and modes are 
byte-identical
    to the old pattern (devlake:root 775/664) while the duplicate layer 
disappears.
    Expected result: ~330 MB instead of 516.6 MB.
---
 backend/Dockerfile | 37 ++++++++++++++++++++++++-------------
 1 file changed, 24 insertions(+), 13 deletions(-)

diff --git a/backend/Dockerfile b/backend/Dockerfile
index 9ed02234a..590fa9255 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -80,29 +80,39 @@ RUN apt-get update && \
 
 EXPOSE 8080
 
-RUN useradd -ms /bin/bash -d /app devlake -u 1010
+RUN useradd -ms /bin/bash -d /app devlake -u 1010 -G 0
 RUN chown -R devlake:devlake /etc/ssl/certs
 USER devlake
 ENV PYTHONUNBUFFERED=1
 
 WORKDIR /app
 
-RUN mkdir logs
+RUN mkdir logs && chgrp 0 logs && chmod g=u logs
 VOLUME /app/logs
 
+# Ownership for arbitrary-UID runtimes (OpenShift runs containers with a random
+# UID and GID 0) is applied in the same RUN that creates each path. Doing it
+# there costs nothing: the layer was being written anyway. A recursive
+# `chgrp -R 0 /app && chmod -R g=u /app` at the end of the image instead
+# rewrites every file that earlier layers created, so Docker stores a second
+# copy of the whole tree - around 190 MB on the published image.
+#
+# The devlake user is added to group 0 so it is allowed to chgrp the files it
+# owns; its primary group stays devlake, so the runtime GID does not change.
+
 # Setup Python
-COPY --chown=devlake:devlake python/ /app/python/
+COPY --chown=devlake:0 python/ /app/python/
 RUN python3 -m pip install --no-cache --upgrade pip setuptools && \
     python3 -m pip install --no-cache -r python/requirements.txt && \
-    python3 -m pip install --upgrade pip
-
-# Setup Python package manager
-RUN curl -LsSf https://astral.sh/uv/install.sh | env 
UV_UNMANAGED_INSTALL=/app/.local/bin sh
+    python3 -m pip install --upgrade pip && \
+    curl -LsSf https://astral.sh/uv/install.sh | env 
UV_UNMANAGED_INSTALL=/app/.local/bin sh && \
+    chgrp -R 0 /app/.local && chmod -R g=u /app/.local
 ENV PATH="/app/.local/bin:${PATH}"
 
 # Build Python plugins, make sure the scripts has execute permission
 # RUN find /app/python/ -name "*.sh" | xargs -I{} chmod +x {}
-RUN /app/python/build.sh
+RUN /app/python/build.sh && \
+    chgrp -R 0 /app/python && chmod -R g=u /app/python
 
 FROM base as devlake-base
 ARG DEBUG=
@@ -110,8 +120,11 @@ ARG DEBUG=
 # apps
 # The single binary already contains every plugin; the .so files that
 # `make build-plugin` produces stay in the builder stage and are never copied.
-COPY --from=build /app/bin/lake /app/bin/lake
-COPY --from=build /app/resources /app/resources
+# The binary and resources are read-only at runtime, so root:0 with the modes
+# they already have (755 for the binary, 644 for the swagger docs) is enough 
for
+# a random UID in group 0. No recursive chmod afterwards.
+COPY --from=build --chown=0:0 /app/bin/lake /app/bin/lake
+COPY --from=build --chown=0:0 /app/resources /app/resources
 
 ENV PATH="/app/bin:${PATH}"
 ENV DEBUG="$DEBUG"
@@ -119,9 +132,7 @@ ENV DEBUG="$DEBUG"
 USER root
 # Empty mount point for sideloaded plugins: dropping a .so here registers it on
 # top of the compiled-in set at startup; see PLUGIN_DIR.
-RUN mkdir -p /app/bin/plugins && \
-    chgrp -R 0 /app && \
-    chmod -R g=u /app
+RUN mkdir -p -m 0775 /app/bin/plugins
 USER devlake
 
 #add tini, prevent zombie process

Reply via email to