nginx / unit

NGINX Unit - universal web app server - a lightweight and versatile open source server that simplifies the application stack by natively executing application code across eight different programming language runtimes.
https://unit.nginx.org
Apache License 2.0
5.37k stars 323 forks source link

[PHP] ALPINE Linux - Unit doesn't seem to load modules built with docker-php-ext-install #862

Open kawaii opened 1 year ago

kawaii commented 1 year ago

docker-php-ext-install is a binary shipped with the underlying official PHP image. It is used to compile various PHP extensions an application might require as a runtime dependency. If my understanding is correct, the unit-php81 package I have installed in my Dockerfile actually ships with it's own kind of embedded PHP, and doesn't use modules built by docker-php-ext-install?

I have included my Dockerfile below, to show an example of what I'm trying to accomplish. If I exec into the resulting container, I can see my modules such as redis, postgresql, and memcached are shown as available - but my web application itself is reporting them as absent when served via Unit.

Could anyone shed some light on if what I'm doing will not work as intended with Unit? Does it require that I install the additional PHP modules from the package manager instead?

FROM php:8.1-cli-alpine

RUN apk add --no-cache unit unit-php81

RUN set -ex; \
    \
    apk add --no-cache --virtual .build-deps \
        $PHPIZE_DEPS \
        freetype-dev \
        icu-dev \
        imagemagick-dev \
        libmemcached-dev \
        libjpeg-turbo-dev \
        libpng-dev \
        libwebp-dev \
        libzip-dev \
        postgresql-dev \
    ; \
    \
    docker-php-ext-configure gd \
        --with-freetype \
        --with-jpeg \
        --with-webp \
    ; \
    docker-php-ext-install -j "$(nproc)" \
        gd \
        intl \
        pdo_pgsql \
        pgsql \
        zip \
    ; \
    pecl install igbinary-3.2.7 imagick-3.6.0 memcached-3.1.5 redis-5.3.7; \
    docker-php-ext-enable igbinary imagick memcached redis; \
    rm -r /tmp/pear; \
    \
    out="$(php -r 'exit(0);')"; \
    [ -z "$out" ]; \
    err="$(php -r 'exit(0);' 3>&1 1>&2 2>&3)"; \
    [ -z "$err" ]; \
    \
    extDir="$(php -r 'echo ini_get("extension_dir");')"; \
    [ -d "$extDir" ]; \
    runDeps="$( \
        scanelf --needed --nobanner --format '%n#p' --recursive "$extDir" \
            | tr ',' '\n' \
            | sort -u \
            | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \
    )"; \
    apk add --no-network --virtual .mybb-phpexts-rundeps $runDeps; \
    apk del --no-network .build-deps; \
    \
    ! { ldd "$extDir"/*.so | grep 'not found'; }; \
    err="$(php --version 3>&1 1>&2 2>&3)"; \
    [ -z "$err" ]

CMD ["unitd", "--no-daemon", "--control", "unix:/var/run/control.unit.sock"]
kawaii commented 1 year ago

I think my assumption that the Alpine package unit-php81 is shipping with an internal/embedded PHP is correct after some debugging inside the container itself - is there any way to override the PHP binaries used by the Unit PHP module?

meezaan commented 1 year ago

I think my assumption that the Alpine package unit-php81 is shipping with an internal/embedded PHP is correct after some debugging inside the container itself - is there any way to override the PHP binaries used by the Unit PHP module?

I assume you are building your own image because you want to use Alpine instead of Debian? Why don't you look at how the packages are built by the Unit team themselves @ https://github.com/nginx/unit/blob/master/pkg/docker/Dockerfile.php8.1. In the Debian images which also use the official php images from Docker Hub, docker-php-ext-install works just fine. For instance - https://github.com/islamic-network/php/blob/master/8/8.1/Dockerfile.nunit.

You can just use the new official Debian images from https://hub.docker.com/_/unit/tags. Older ones are available on https://hub.docker.com/r/nginx/unit/.

tippexs commented 1 year ago

@kawaii thanks for reporting this to us. Your investigation is correct. What happens here is that we are shipping the unit-php package with a runtime dependency. This will install php8 while building the the container.

Looking into the container we can see two different installation of PHP. The one coming from the Docker Base Image in /usr/local/etc/php/ and the one added by us using apk add unit unit-php in /etc/php81/.

The docker-php-ext-install will work with the default version shipped with the base image. Not with the one we add during the installation of unit-php. I am working on a new Dockerfile that will solve this and share it with you here.

We will also discuss internally to add alpine as a supported image variant.

** Update: The unit and unit-php81 packages are community created. Looking into the build-logs it makes it very clear using the apk add approach for Unit and the docker-php-ext approach for extension does not work together.

What you can do is using the alpine packages for example https://pkgs.alpinelinux.org/package/edge/community/armhf/php81-gd for GD or https://pkgs.alpinelinux.org/packages?name=php81*pgsql*&branch=edge&repo=&arch=&maintainer= for PGSQL.

This will work.

tippexs commented 1 year ago

I have just tested it with your Dockerfile. Issuing

apk add php81-intl php81-pgsql and restarting the app will load the modules.

/ # curl --unix-socket /var/run/control.unit.sock localhost/control/applications/php/restart
{
        "success": "Ok"
}
/ # curl localhost
Array
(
    [0] => Core
    [1] => date
    [2] => libxml
    [3] => pcre
    [4] => zlib
    [5] => filter
    [6] => hash
    [7] => json
    [8] => readline
    [9] => Reflection
    [10] => standard
    [11] => SPL
    [12] => unit
    [13] => intl
    [14] => pgsql
)

My index.php file

<?php

print_r(get_loaded_extensions());

print_r($_SERVER);

print_r(php_ini_loaded_file());

My Unit conf.json.

{
  "listeners": {"*:80": {"pass": "applications/php"}},
  "applications": {
    "php": {
      "type": "php",
      "root": "/tmp/"
      }
    }
}