diff --git a/Dockerfile-fpm-alpine b/Dockerfile-fpm-alpine new file mode 100644 index 0000000..38c94d5 --- /dev/null +++ b/Dockerfile-fpm-alpine @@ -0,0 +1,135 @@ +# syntax=docker/dockerfile:experimental +FROM php:7.4-fpm-alpine3.11 AS base + +# Build-time metadata as defined at http://label-schema.org +LABEL org.label-schema.name="wyrihaximusnet/php" \ + org.label-schema.description="Opinionated ReactPHP optimised PHP Docker images" \ + org.label-schema.url="https://github.com/wyrihaximusnet/docker-php" \ + org.label-schema.vcs-url="https://github.com/wyrihaximusnet/docker-php" \ + org.label-schema.vendor="WyriHaximus.net" \ + org.label-schema.schema-version="1.0" + +RUN apk update \ + && apk upgrade \ + && set -x \ + && addgroup -g 1000 app \ + && adduser -u 1000 -D -G app app --home /opt/app \ + && touch /.you-are-in-a-wyrihaximus.net-php-docker-image + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ + +FROM base AS base-build +RUN apk add --no-cache $PHPIZE_DEPS git libuv-dev + +FROM base-build AS build-uv +RUN git clone https://github.com/amphp/ext-uv uv +WORKDIR uv +RUN git fetch \ + && git pull \ + && phpize \ + && ./configure \ + && make install \ + && EXTENSION_DIR=`php-config --extension-dir 2>/dev/null` \ + && cp "$EXTENSION_DIR/uv.so" /uv.so \ + && sha256sum /uv.so + +FROM base AS fpm-slim-root + +COPY --from=build-uv /uv.so /uv.so + +# Install docker help scripts +COPY src/php/utils/docker/ /usr/local/bin/ + +COPY src/php/conf/ /usr/local/etc/php/conf.d/ +COPY src/php/cli/conf/*.ini /usr/local/etc/php/conf.d/ +COPY src/php/fpm/conf/*.conf.* /usr/local/etc/php-fpm.d/ + +RUN EXTENSION_DIR=`php-config --extension-dir 2>/dev/null` && \ + mv /*.so "$EXTENSION_DIR/" && \ + apk add --no-cache \ + libuv-dev \ + icu-dev \ + libevent-dev \ + openssl-dev \ + coreutils \ + procps \ + git \ + $PHPIZE_DEPS \ + ## Install PECL + && wget -q pear.php.net/go-pear.phar && php go-pear.phar \ + && install-php-extensions pcntl pgsql pdo pdo_pgsql bcmath zip gmp iconv opcache intl sockets \ + && (pecl install eio || pecl install eio-beta) \ + && docker-php-ext-enable eio \ + && pecl install event \ + # To ensure ext-socket loads before ext-event + && docker-php-ext-enable --ini-name zzzzz-event.ini event \ + && docker-php-ext-enable uv \ + && apk del $PHPIZE_DEPS \ + && wget -q -O - https://raw.githubusercontent.com/eficode/wait-for/master/wait-for > /bin/wait-for \ + && chmod +x /bin/wait-for \ + && rm -rf /var/cache/apk/* \ + && rm -rf /tmp/* + +STOPSIGNAL SIGTERM + +ENTRYPOINT ["docker-php-entrypoint"] + +## FPM-DEV STAGE ## +FROM fpm-slim-root AS fpm-root + +# Install ext-gd and ext-vips +COPY src/php/utils/docker/alpine/install-gd-and-vips /usr/local/bin/install-gd-and-vips +RUN if [ $(php -v | grep "alpha\|ALPHA\|beta\|BETA\|rc\|RC" | wc -l) != 0 ] ; then true ; else install-gd-and-vips; fi \ + && rm -rf /usr/local/bin/install-gd-and-vips + +## FPM-DEV STAGE ## +FROM fpm-slim-root AS fpm-slim-dev-root + +RUN touch /.you-are-in-a-wyrihaximus.net-php-docker-image-dev + +# Install docker help scripts +COPY src/php/utils/docker/alpine/dev-mode /usr/local/bin/dev-mode +COPY src/php/utils/docker/alpine/docker-php-dev-mode /usr/local/bin/docker-php-dev-mode +RUN if [ $(php -v | grep "alpha\|ALPHA\|beta\|BETA\|rc\|RC" | wc -l) != 0 ] ; then true ; else dev-mode; fi \ + && rm -rf /usr/local/bin/dev-mode \ + && rm -rf /usr/local/bin/docker-php-dev-mode \ + && rm /usr/local/etc/php/conf.d/jit.ini + +# Install composer +COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer + + +ENTRYPOINT ["docker-php-entrypoint"] + +## FPM-DEV STAGE ## +FROM fpm-root AS fpm-dev-root + +RUN touch /.you-are-in-a-wyrihaximus.net-php-docker-image-dev + +# Install docker help scripts +COPY src/php/utils/docker/alpine/dev-mode /usr/local/bin/dev-mode +COPY src/php/utils/docker/alpine/docker-php-dev-mode /usr/local/bin/docker-php-dev-mode +RUN if [ $(php -v | grep "alpha\|ALPHA\|beta\|BETA\|rc\|RC" | wc -l) != 0 ] ; then true ; else dev-mode; fi \ + && rm -rf /usr/local/bin/dev-mode \ + && rm -rf /usr/local/bin/docker-php-dev-mode \ + && rm /usr/local/etc/php/conf.d/jit.ini + +# Install composer +COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer + + +ENTRYPOINT ["docker-php-entrypoint"] + +## FPM-DEV stage ## +FROM fpm-slim-dev-root AS fpm-slim-dev +USER app + +FROM fpm-dev-root AS fpm-dev +USER app + +## FPM stage ## +FROM fpm-slim-root AS fpm-slim +USER app + +FROM fpm-root AS fpm +USER app diff --git a/Dockerfile-fpm-debian b/Dockerfile-fpm-debian new file mode 100644 index 0000000..06bca70 --- /dev/null +++ b/Dockerfile-fpm-debian @@ -0,0 +1,147 @@ +# syntax=docker/dockerfile:experimental +FROM php:7.4-fpm-buster AS base + +# Build-time metadata as defined at http://label-schema.org +LABEL org.label-schema.name="wyrihaximusnet/php" \ + org.label-schema.description="Opinionated ReactPHP optimised PHP Docker images" \ + org.label-schema.url="https://github.com/wyrihaximusnet/docker-php" \ + org.label-schema.vcs-url="https://github.com/wyrihaximusnet/docker-php" \ + org.label-schema.vendor="WyriHaximus.net" \ + org.label-schema.schema-version="1.0" + +RUN apt-get update \ + && yes | apt-get upgrade \ + && set -x \ + && addgroup --gid 1000 app \ + && adduser --uid 1000 --gid 1000 --disabled-password app --home /opt/app \ + && touch /.you-are-in-a-wyrihaximus.net-php-docker-image + +COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ + +FROM base AS base-build +RUN yes | apt-get install $PHPIZE_DEPS git libuv1-dev + +FROM base-build AS build-uv +RUN git clone https://github.com/amphp/ext-uv uv +WORKDIR uv +RUN git fetch \ + && git pull \ + && phpize \ + && ./configure \ + && make install \ + && EXTENSION_DIR=`php-config --extension-dir 2>/dev/null` \ + && cp "$EXTENSION_DIR/uv.so" /uv.so \ + && sha256sum /uv.so + +FROM base AS fpm-slim-root + +COPY --from=build-uv /uv.so /uv.so + +# Patch CVE-2018-14618 (curl), CVE-2018-16842 (libxml2), CVE-2019-1543 (openssl) +RUN yes | apt-get upgrade curl libxml2 openssl + +# Install docker help scripts +COPY src/php/utils/docker/debian/ /usr/local/bin/ + +COPY src/php/conf/ /usr/local/etc/php/conf.d/ +COPY src/php/cli/conf/*.ini /usr/local/etc/php/conf.d/ + +RUN EXTENSION_DIR=`php-config --extension-dir 2>/dev/null` && \ + mv /*.so "$EXTENSION_DIR/" && \ + yes | apt-get install \ + libgmp-dev \ + zlib1g-dev \ + libpq-dev \ + libzip-dev \ + libuv1-dev \ + libicu-dev \ + libevent-dev \ + libssl-dev \ + make \ + git \ + openssh-client \ + bash \ + coreutils \ + procps \ + git \ + wget \ + gdb \ + $PHPIZE_DEPS \ + && docker-php-ext-install -j$(nproc) pcntl pgsql pdo pdo_pgsql bcmath zip gmp iconv intl sockets \ + && (pecl install eio || pecl install eio-beta) \ + && docker-php-ext-enable eio \ + && pecl install event \ + # To ensure ext-socket loads before ext-event + && docker-php-ext-enable --ini-name zzzzz-event.ini event \ + && docker-php-ext-enable uv \ + && wget -q -O - https://raw.githubusercontent.com/vishnubob/wait-for-it/master/wait-for-it.sh > /bin/wait-for \ + && yes | apt-get purge wget $PHPIZE_DEPS \ + && yes | apt-get install make \ + && chmod +x /bin/wait-for \ + && rm -rf /var/cache/apk/* \ + && rm -rf /tmp/* + +STOPSIGNAL SIGTERM + +ENTRYPOINT ["docker-php-entrypoint"] + +## FPM-DEV STAGE ## +FROM fpm-slim-root AS fpm-root + +# Install ext-gd and ext-vips +COPY src/php/utils/docker/debian/install-gd-and-vips /usr/local/bin/install-gd-and-vips +RUN if [ $(php -v | grep "alpha\|ALPHA\|beta\|BETA\|rc\|RC" | wc -l) != 0 ] ; then true ; else install-gd-and-vips; fi \ + && rm -rf /usr/local/bin/install-gd-and-vips + +## FPM-DEV STAGE ## +FROM fpm-slim-root AS fpm-slim-dev-root + +RUN touch /.you-are-in-a-wyrihaximus.net-php-docker-image-dev + +# Install docker help scripts +COPY src/php/utils/docker/debian/dev-mode /usr/local/bin/dev-mode +COPY src/php/utils/docker/debian/docker-php-dev-mode /usr/local/bin/docker-php-dev-mode +RUN if [ $(php -v | grep "alpha\|ALPHA\|beta\|BETA\|rc\|RC" | wc -l) != 0 ] ; then true ; else dev-mode; fi \ + && rm -rf /usr/local/bin/dev-mode \ + && rm -rf /usr/local/bin/docker-php-dev-mode \ + && yes | apt-get install make \ + && rm /usr/local/etc/php/conf.d/jit.ini + +# Install composer +COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer + + +ENTRYPOINT ["docker-php-entrypoint"] + +## FPM-DEV STAGE ## +FROM fpm-root AS fpm-dev-root + +RUN touch /.you-are-in-a-wyrihaximus.net-php-docker-image-dev + +# Install docker help scripts +COPY src/php/utils/docker/debian/dev-mode /usr/local/bin/dev-mode +COPY src/php/utils/docker/debian/docker-php-dev-mode /usr/local/bin/docker-php-dev-mode +RUN if [ $(php -v | grep "alpha\|ALPHA\|beta\|BETA\|rc\|RC" | wc -l) != 0 ] ; then true ; else dev-mode; fi \ + && rm -rf /usr/local/bin/dev-mode \ + && rm -rf /usr/local/bin/docker-php-dev-mode \ + && yes | apt-get install make \ + && rm /usr/local/etc/php/conf.d/jit.ini + +# Install composer +COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer + +ENTRYPOINT ["docker-php-entrypoint"] + +## FPM-DEV stage ## +FROM fpm-slim-dev-root AS fpm-slim-dev +USER app + +FROM fpm-dev-root AS fpm-dev +USER app + +## NTS stage ## +FROM fpm-slim-root AS fpm-slim +USER app + +FROM fpm-root AS nts +USER app diff --git a/Makefile b/Makefile index 95b34d4..000bc77 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ build-all: xargs -I {} -t bash -c './build-php.sh {}' -test: test-cli test-fpm test-http +test: test-nts test-fpm test-zts test-nts: ./docker-image/image.tags xargs -I % ./test-nts.sh % < ./docker-image/image.tags @@ -33,6 +33,9 @@ test-nts: ./docker-image/image.tags test-zts: ./docker-image/image.tags xargs -I % ./test-zts.sh % < ./docker-image/image.tags +test-fpm: ./docker-image/image.tags + xargs -I % ./test-fpm.sh % < ./docker-image/image.tags + scan-vulnerability: cat ./docker-image/image.tags | xargs -I % sh -c 'docker run -v /tmp/trivy:/var/lib/trivy -v /var/run/docker.sock:/var/run/docker.sock -t aquasec/trivy:latest --cache-dir /var/lib/trivy image --exit-code 1 --no-progress --format table % || echo "% is vulnerable"' diff --git a/src/php/fpm/conf/www.conf b/src/php/fpm/conf/www.conf new file mode 100644 index 0000000..885b1c8 --- /dev/null +++ b/src/php/fpm/conf/www.conf @@ -0,0 +1,9 @@ +[www] +user = nobody +group = nobody +listen = 0.0.0.0:9000 +pm = dynamic +pm.max_children = 5 +pm.start_servers = 2 +pm.min_spare_servers = 1 +pm.max_spare_servers = 3 diff --git a/test-fpm.sh b/test-fpm.sh new file mode 100644 index 0000000..fe28683 --- /dev/null +++ b/test-fpm.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# +# A simple script to start a Docker container +# and run Testinfra in it +# Original script: https://gist.github.com/renatomefi/bbf44d4e8a2614b1390416c6189fbb8e +# Author: @renatomefi https://github.com/renatomefi +# + +set -eEuo pipefail + +# The first parameter is a Docker tag or image id +declare -r DOCKER_TAG="$1" + +declare TEST_SUITE + +TEST_SUITE="php_$IMAGE_ARCH" + +if [[ $DOCKER_TAG == *"-dev"* && $IMAGE_BASE_VERSION != *"alpha"* && $IMAGE_BASE_VERSION != *"beta"* && $IMAGE_BASE_VERSION != *"rc"* && $IMAGE_BASE_VERSION != *"ALPHA"* && $IMAGE_BASE_VERSION != *"BETA"* && $IMAGE_BASE_VERSION != *"RC"* ]]; then + TEST_SUITE="php or php_fpm or php_dev" +else + TEST_SUITE="php_fpm or php_no_dev and not php_dev" +fi + +if [[ $DOCKER_TAG == *"-slim"* ]]; then + TEST_SUITE="php_slim or php_slim_$IMAGE_ARCH or $TEST_SUITE" +else + if [[ $IMAGE_BASE_VERSION != *"alpha"* && $IMAGE_BASE_VERSION != *"beta"* && $IMAGE_BASE_VERSION != *"rc"* && $IMAGE_BASE_VERSION != *"ALPHA"* && $IMAGE_BASE_VERSION != *"BETA"* && $IMAGE_BASE_VERSION != *"RC"* ]]; then + TEST_SUITE="$TEST_SUITE" + else + TEST_SUITE="php_not_slim and php_not_slim_$IMAGE_ARCH or $TEST_SUITE" + fi +fi + +printf "Starting a container for '%s'\\n" "$DOCKER_TAG" + +DOCKER_CONTAINER=$(docker run --rm -t -d "$DOCKER_TAG") +readonly DOCKER_CONTAINER + +# Let's register a trap function, if our tests fail, finish or the script gets +# interrupted, we'll still be able to remove the running container +function tearDown { + docker rm -f "$DOCKER_CONTAINER" &>/dev/null & +} +trap tearDown EXIT TERM ERR + +# Finally, run the tests! +docker run --rm -t \ + -v "$(pwd)/test:/tests" \ + -v "$(pwd)/tmp/test-results:/results" \ + -v /var/run/docker.sock:/var/run/docker.sock:ro \ + renatomefi/docker-testinfra:5 \ + -m "$TEST_SUITE" --junitxml="/results/php-fpm-$DOCKER_TAG.xml" \ + --verbose --hosts="docker://$DOCKER_CONTAINER" diff --git a/test/container/test_in_docker.py b/test/container/test_in_docker.py index d9c4ef6..7836dd3 100644 --- a/test/container/test_in_docker.py +++ b/test/container/test_in_docker.py @@ -2,6 +2,7 @@ @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_in_docker_file_exists(host): output = host.run('php -r "exit(file_exists(\'/.you-are-in-a-wyrihaximus.net-php-docker-image\') ? 0 : 255);"') assert output.rc == 0 diff --git a/test/container/test_php.py b/test/container/test_php.py index 355ee5a..6f7170f 100644 --- a/test/container/test_php.py +++ b/test/container/test_php.py @@ -18,6 +18,7 @@ def test_php_runs_as_root(host): @pytest.mark.php_nts @pytest.mark.php_zts +@pytest.mark.php_fpm def test_php_pcntl_is_enabled(host): output = host.run('php -r "exit(function_exists(\'pcntl_signal\') ? 0 : 255);"') assert output.rc == 0 @@ -27,6 +28,7 @@ def test_php_pcntl_is_enabled(host): @pytest.mark.php_nts @pytest.mark.php_zts +@pytest.mark.php_fpm def test_php_ext_uv_is_enabled(host): output = host.run('php -r "exit(function_exists(\'uv_loop_new\') ? 0 : 255);"') assert output.rc == 0 @@ -43,6 +45,7 @@ def test_php_ext_parallel_is_enabled(host): assert output.rc == 0 @pytest.mark.php_nts +@pytest.mark.php_fpm def test_php_ext_parallel_is_not_enabled(host): output = host.run('php -r "exit(class_exists(\'parallel\\Runtime\') ? 0 : 255);"') assert output.rc == 255 @@ -60,6 +63,7 @@ def test_php_ext_parallel_is_functional(host): @pytest.mark.php_nts @pytest.mark.php_zts +@pytest.mark.php_fpm def test_php_ext_uv_is_functional(host): output = host.run('php /tests/container/functional/event-timer.php') assert output.stdout == '0123finished' @@ -67,6 +71,7 @@ def test_php_ext_uv_is_functional(host): @pytest.mark.php_nts @pytest.mark.php_zts +@pytest.mark.php_fpm def test_php_ext_uv_is_functional(host): output = host.run('php /tests/container/functional/uv-timer.php') assert output.stdout == '0123finished' @@ -74,6 +79,7 @@ def test_php_ext_uv_is_functional(host): @pytest.mark.php_nts @pytest.mark.php_zts +@pytest.mark.php_fpm def test_php_ext_eio_is_functional(host): output = host.run('php /tests/container/functional/eio-file-size.php') assert output.stdout == '114' diff --git a/test/container/test_php_ext.py b/test/container/test_php_ext.py index c54aa51..5f41239 100644 --- a/test/container/test_php_ext.py +++ b/test/container/test_php_ext.py @@ -2,16 +2,19 @@ @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_bcmath_is_loaded(host): assert 'bcmath' in host.run('php -m').stdout @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_eio_is_loaded(host): assert 'eio' in host.run('php -m').stdout @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_event_is_loaded(host): assert 'event' in host.run('php -m').stdout @@ -21,16 +24,19 @@ def test_gd_is_loaded(host): @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_gmp_is_loaded(host): assert 'gmp' in host.run('php -m').stdout @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_iconv_is_loaded(host): assert 'iconv' in host.run('php -m').stdout @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_iconv_is_loaded(host): assert 'intl' in host.run('php -m').stdout @@ -39,16 +45,19 @@ def test_parallel_is_loaded(host): assert 'parallel' in host.run('php -m').stdout @pytest.mark.php_nts +@pytest.mark.php_fpm def test_parallel_is_not_loaded(host): assert 'parallel' not in host.run('php -m').stdout @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_pcntl_is_loaded(host): assert 'pcntl' in host.run('php -m').stdout @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_pgsql_is_loaded(host): assert 'pdo' in host.run('php -m').stdout assert 'pgsql' in host.run('php -m').stdout @@ -56,6 +65,7 @@ def test_pgsql_is_loaded(host): @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_uv_is_loaded(host): assert 'uv' in host.run('php -m').stdout @@ -73,5 +83,6 @@ def test_xdebug_is_not_loaded(host): @pytest.mark.php_zts @pytest.mark.php_nts +@pytest.mark.php_fpm def test_zip_is_loaded(host): assert 'zip' in host.run('php -m').stdout diff --git a/test/container/test_wait-for.py b/test/container/test_wait-for.py index 54ee6df..92297b6 100644 --- a/test/container/test_wait-for.py +++ b/test/container/test_wait-for.py @@ -2,6 +2,7 @@ @pytest.mark.php_nts @pytest.mark.php_zts +@pytest.mark.php_fpm def test_wait_for_is_functional(host): output = host.run('wait-for google.com:443 -- wait-for hub.docker.com:443 -- echo 1000') assert output.stdout == '1000\n'