Skip to content

Commit 0d934d4

Browse files
committed
[major] Converted all of the PHP base images to distroless, saving 500 MB per image.
1 parent a1b61c1 commit 0d934d4

File tree

3 files changed

+171
-0
lines changed

3 files changed

+171
-0
lines changed

docker/build-distroless.sh

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
#!/bin/bash
2+
#####################################################################
3+
# The Dockerize PHP Project #
4+
# https://github.com/PHPExpertsInc/docker-php #
5+
# License: MIT #
6+
# #
7+
# Copyright © 2020-2025 PHP Experts, Inc. <sales@phpexperts.pro> #
8+
# Author: Theodore R. Smith <theodore@phpexperts.pro> #
9+
# PGP Sig: 4BF826131C3487ACD28F2AD8EB24A91DD6125690 #
10+
#####################################################################
11+
12+
set -o pipefail
13+
14+
PHP_VERSIONS="5.6 7.0 7.1 7.2 7.3 7.4 8.0 8.1 8.2 8.3 8.4"
15+
#PHP_VERSIONS="7.4 8.0 8.1 8.2 8.3 8.4"
16+
#PHP_VERSIONS="8.0 8.1 8.2 8.3 8.4"
17+
18+
# Create the apt cache volume
19+
docker volume create apt-cache
20+
21+
export BUILDKIT_STEP_LOG_MAX_SIZE=104857600
22+
23+
# Build the base linux image first.
24+
export DOCKER_BUILDKIT=1
25+
26+
27+
for VERSION in ${PHP_VERSIONS}; do
28+
# Check if the base image exists and get its size
29+
if ! docker image inspect "phpexperts/php:${VERSION}" >/dev/null 2>&1; then
30+
echo "Docker image phpexperts/php:${VERSION} does not exist. Skipping..."
31+
continue
32+
fi
33+
34+
# Get image size in bytes and convert to MB
35+
IMAGE_SIZE_BYTES=$(docker image inspect "phpexperts/php:${VERSION}" --format='{{.Size}}')
36+
IMAGE_SIZE_MB=$((IMAGE_SIZE_BYTES / 1024 / 1024))
37+
38+
if [ $IMAGE_SIZE_MB -le 200 ]; then
39+
echo "Docker image phpexperts/php:${VERSION} is ${IMAGE_SIZE_MB}MB (≤200MB). Skipping..."
40+
continue
41+
fi
42+
43+
echo "Building distroless image for PHP ${VERSION} (base image size: ${IMAGE_SIZE_MB}MB)..."
44+
45+
# Build the distroless image
46+
if docker build distroless --tag="phpexperts/php:${VERSION}-distroless" --build-arg VOLUME="apt-cache:/var/lib/apt" --build-arg PHP_VERSION=$VERSION --no-cache --progress=plain; then
47+
echo "Successfully built distroless image for PHP ${VERSION}"
48+
49+
# Remove the existing image (force)
50+
docker rmi -f "phpexperts/php:${VERSION}"
51+
52+
# Tag the distroless image as the main version
53+
docker tag "phpexperts/php:${VERSION}-distroless" "phpexperts/php:${VERSION}"
54+
55+
# Remove the distroless tagged image
56+
docker rmi "phpexperts/php:${VERSION}-distroless"
57+
58+
echo "Successfully replaced phpexperts/php:${VERSION} with distroless version"
59+
else
60+
echo "Failed to build distroless image for PHP ${VERSION}"
61+
fi
62+
done

docker/distroless/Dockerfile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# PROMPT: Create an intermediary Dockerfile from phpexperts/php:8.3 that copies the "grab_files.sh" bash script
2+
# created previously. RUN the /grab_files.sh multiple times with the args /usr/bin/bash, /usr/bin/env, /usr/bin/php,
3+
# /usr/bin/composer /usr/bin/ls and /etc/php/8.3, /usr/share/zoneinfo, /etc/ssl/certs, each on its own line.
4+
#
5+
# Then using a FROM scratch final image, COPY the path /tmp/distroless to / of the final image.
6+
# Also COPY /usr/local/bin/entrypoint-php.sh to the final image.
7+
# Set the CMD to /usr/bin/php and the ENTRYPOINT to /usr/local/bin/entrypoint-php.sh.
8+
ARG PHP_VERSION
9+
10+
FROM phpexperts/php:$PHP_VERSION AS intermediate
11+
12+
COPY grab_files.sh /grab_files.sh
13+
14+
RUN /grab_files.sh "/usr/bin/bash" || true
15+
RUN /grab_files.sh "/usr/bin/env"; \
16+
/grab_files.sh "/usr/bin/ldd"; \
17+
/grab_files.sh "/usr/bin/php"; \
18+
/grab_files.sh "/usr/local/bin/composer"; \
19+
/grab_files.sh "/usr/bin/ls"; \
20+
/grab_files.sh "/usr/lib/php"; \
21+
/grab_files.sh "/etc/php/$PHP_VERSION"; \
22+
/grab_files.sh "/usr/share/zoneinfo"; \
23+
/grab_files.sh "/etc/ca-certificates/" || true
24+
25+
FROM scratch
26+
27+
COPY --from=intermediate /tmp/distroless /
28+
COPY --from=intermediate /usr/local/bin/entrypoint-php.sh /usr/bin/entrypoint-php.sh
29+
30+
WORKDIR /workdir
31+
32+
ENTRYPOINT ["/usr/bin/entrypoint-php.sh"]

docker/distroless/grab_files.sh

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
#!/bin/bash
2+
3+
# PROMPT: Create a bash script with the following:
4+
# If a CLI arg is not passed, fail with the mesage "Error: Pass the full path of the file/executable you want to include."
5+
#
6+
# If the CLI arg is neither a file nor a directory, fail with the message "Error: File not found."
7+
#
8+
# If the /tmp/distroless directory does not exist, create it. Then
9+
# cd into /tmp/distroless and create the POSIX standard Linux directories, using Ubuntu 22.04 as the exact model to copy.
10+
# set chmod 0777 for tmp var/{cache,log,tmp}
11+
# set chmod 0750 for root
12+
# Create ln -s for /bin /sbin from /usr/bin
13+
# Create ln -s for /lib /lib64 from /usr/lib
14+
#
15+
# Then if the CLI arg is a directory, use the following prompt:
16+
# If the SOURCE directory (via the CLI arg) is "/etc/php/8.3",
17+
# Copy this to /tmp/distroless/ while maintaining the relative directory path:
18+
# Example: /etc/php/8.3 -> /tmp/distroless/etc/php/8.3/
19+
#
20+
# If it is not a directory:
21+
# cp -v "$1" "/tmp/distroless$1"
22+
# Test if the file is an executable (via bash's -x).
23+
# If it is an executable:
24+
# Then run `ldd "$1" | awk '{print $1}'` and use `cp` to copy the files to /usr/lib
25+
# Ignore any errors from the `cp` command, as they are likely false-positives.
26+
#
27+
# Include this exact prompt as a source code comment at the beginning of the Bash script.
28+
# Do not bother with inline comments.
29+
if [ -x "$1" ]; then
30+
cd /usr/lib/x86_64-linux-gnu
31+
cp -vf $(ldd "$1" | awk '{print $1}') /tmp/distroless/usr/lib/
32+
fi
33+
34+
35+
36+
if [ -z "$1" ]; then
37+
echo "Error: Pass the full path of the file/executable you want to include."
38+
exit 1;
39+
fi
40+
41+
if [ ! -f "$1" ] && [ ! -d "$1" ]; then
42+
echo "Error: File not found."
43+
exit 2;
44+
fi
45+
46+
if [ ! -d /tmp/distroless ]; then
47+
mkdir -p /tmp/distroless
48+
cd /tmp/distroless
49+
mkdir -p dev etc home/user media mnt opt proc root run sys tmp usr var/{cache,lib,log,spool,tmp}
50+
mkdir -p /tmp/distroless/usr/{bin,lib,local/bin}
51+
52+
chmod 0777 tmp var/{cache,log,tmp}
53+
chmod 0750 root
54+
55+
ln -s usr/bin bin
56+
ln -s usr/bin sbin
57+
ln -s usr/lib lib
58+
ln -s usr/lib lib64
59+
fi
60+
61+
if [ -d "$1" ]; then
62+
63+
cp -avf --parents "$1" /tmp/distroless
64+
65+
for each in $(find "$1" -name \*.so\* -type f); do
66+
cd /usr/lib/x86_64-linux-gnu
67+
cp -v $(ldd $each | awk '{print $1}') /tmp/distroless/usr/lib
68+
done
69+
70+
exit 0
71+
fi
72+
73+
cp -v "$1" "/tmp/distroless$1"
74+
if [ -x "$1" ]; then
75+
cd /usr/lib/x86_64-linux-gnu
76+
cp -vf $(ldd "$1" | awk '{print $1}') /tmp/distroless/usr/lib/
77+
fi

0 commit comments

Comments
 (0)