diff --git a/Dockerfile-unit.template b/Dockerfile-unit.template new file mode 100644 index 0000000000..c64e6d14e4 --- /dev/null +++ b/Dockerfile-unit.template @@ -0,0 +1,153 @@ +FROM nginx/unit:%%UNIT_VERSION%%-php%%PHP_VERSION%% + +# entrypoint.sh and cron.sh dependencies +RUN set -ex; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + rsync \ + bzip2 \ + busybox-static \ + ; \ + rm -rf /var/lib/apt/lists/*; \ + \ + mkdir -p /var/spool/cron/crontabs; \ + echo '*/%%CRONTAB_INT%% * * * * php -f /var/www/html/cron.php' > /var/spool/cron/crontabs/www-data + +# install the PHP extensions we need +# see https://docs.nextcloud.com/server/stable/admin_manual/installation/source_installation.html +ENV PHP_MEMORY_LIMIT 512M +ENV PHP_UPLOAD_LIMIT 512M +RUN set -ex; \ + \ + savedAptMark="$(apt-mark showmanual)"; \ + \ + apt-get update; \ + apt-get install -y --no-install-recommends \ + libcurl4-openssl-dev \ + libevent-dev \ + libfreetype6-dev \ + libicu-dev \ + libjpeg-dev \ + libldap-common \ + libldap2-dev \ + libmcrypt-dev \ + libmemcached-dev \ + libpng-dev \ + libpq-dev \ + libxml2-dev \ + libmagickwand-dev \ + libzip-dev \ + libwebp-dev \ + libgmp-dev \ + ; \ + \ + debMultiarch="$(dpkg-architecture --query DEB_BUILD_MULTIARCH)"; \ + if [ ! -e /usr/include/gmp.h ]; then ln -s /usr/include/$debMultiarch/gmp.h /usr/include/gmp.h; fi;\ + docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp; \ + docker-php-ext-configure gmp --with-gmp="/usr/include/$debMultiarch"; \ + docker-php-ext-configure ldap --with-libdir="lib/$debMultiarch"; \ + docker-php-ext-install -j "$(nproc)" \ + bcmath \ + exif \ + gd \ + intl \ + ldap \ + opcache \ + pcntl \ + pdo_mysql \ + pdo_pgsql \ + zip \ + gmp \ + ; \ + \ +# pecl will claim success even if one install fails, so we need to perform each install separately + pecl install APCu-%%APCU_VERSION%%; \ + pecl install memcached-%%MEMCACHED_VERSION%%; \ + pecl install redis-%%REDIS_VERSION%%; \ + pecl install imagick-%%IMAGICK_VERSION%%; \ + \ + docker-php-ext-enable \ + apcu \ + memcached \ + redis \ + imagick \ + ; \ + rm -r /tmp/pear; \ + \ +# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies + apt-mark auto '.*' > /dev/null; \ + apt-mark manual $savedAptMark; \ + ldd "$(php -r 'echo ini_get("extension_dir");')"/*.so \ + | awk '/=>/ { print $3 }' \ + | sort -u \ + | xargs -r dpkg-query -S \ + | cut -d: -f1 \ + | sort -u \ + | xargs -rt apt-mark manual; \ + \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ + rm -rf /var/lib/apt/lists/* + +# set recommended PHP.ini settings +# see https://docs.nextcloud.com/server/stable/admin_manual/configuration_server/server_tuning.html#enable-php-opcache +RUN { \ + echo 'opcache.enable=1'; \ + echo 'opcache.interned_strings_buffer=8'; \ + echo 'opcache.max_accelerated_files=10000'; \ + echo 'opcache.memory_consumption=128'; \ + echo 'opcache.save_comments=1'; \ + echo 'opcache.revalidate_freq=1'; \ + } > /usr/local/etc/php/conf.d/opcache-recommended.ini; \ + \ + echo 'apc.enable_cli=1' >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini; \ + \ + { \ + echo 'memory_limit=${PHP_MEMORY_LIMIT}'; \ + echo 'upload_max_filesize=${PHP_UPLOAD_LIMIT}'; \ + echo 'post_max_size=${PHP_UPLOAD_LIMIT}'; \ + } > /usr/local/etc/php/conf.d/nextcloud.ini; \ + \ + mkdir /var/www/data; \ + chown -R www-data:root /var/www; \ + chmod -R g=u /var/www + +VOLUME /var/www/html +%%VARIANT_EXTRAS%% + +ENV NEXTCLOUD_VERSION %%VERSION%% + +RUN set -ex; \ + fetchDeps=" \ + gnupg \ + dirmngr \ + "; \ + apt-get update; \ + apt-get install -y --no-install-recommends $fetchDeps; \ + \ + curl -fsSL -o nextcloud.tar.bz2 \ + "%%BASE_DOWNLOAD_URL%%/nextcloud-${NEXTCLOUD_VERSION}.tar.bz2"; \ + curl -fsSL -o nextcloud.tar.bz2.asc \ + "%%BASE_DOWNLOAD_URL%%/nextcloud-${NEXTCLOUD_VERSION}.tar.bz2.asc"; \ + export GNUPGHOME="$(mktemp -d)"; \ +# gpg key from https://nextcloud.com/nextcloud.asc + gpg --batch --keyserver keyserver.ubuntu.com --recv-keys 28806A878AE423A28372792ED75899B9A724937A; \ + gpg --batch --verify nextcloud.tar.bz2.asc nextcloud.tar.bz2; \ + tar -xjf nextcloud.tar.bz2 -C /usr/src/; \ + gpgconf --kill all; \ + rm nextcloud.tar.bz2.asc nextcloud.tar.bz2; \ + rm -rf "$GNUPGHOME" /usr/src/nextcloud/updater; \ + mkdir -p /usr/src/nextcloud/data; \ + mkdir -p /usr/src/nextcloud/custom_apps; \ + chmod +x /usr/src/nextcloud/occ; \ + \ + apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false $fetchDeps; \ + rm -rf /var/lib/apt/lists/* + +COPY *.sh upgrade.exclude / +COPY config/* /usr/src/nextcloud/config/ + +COPY nextcloud-unit.json /docker-entrypoint.d/ + +ENTRYPOINT ["/entrypoint.sh"] +CMD ["%%CMD%%"] diff --git a/docker-entrypoint.sh b/docker-entrypoint.sh index b6da893b07..52a3bf9a08 100755 --- a/docker-entrypoint.sh +++ b/docker-entrypoint.sh @@ -47,9 +47,29 @@ if expr "$1" : "apache" 1>/dev/null; then if [ -n "${APACHE_DISABLE_REWRITE_IP+x}" ]; then a2disconf remoteip fi +elif expr "$1" : "unitd" 1>/dev/null; then + echo "Launching Unit daemon to perform initial configuration..." + unitd --control unix:/var/run/control.unit.sock + + while [ ! -S /var/run/control.unit.sock ]; do echo "Waiting for control socket to be created..."; sleep 0.5; done + # Even when the control socket exists, it does not mean unit has finished initialisation + # This curl call will get a reply once unit is fully launched + curl -s -X GET --unix-socket /var/run/control.unit.sock http://localhost/ + + RET=$(/usr/bin/curl -s -w '%{http_code}' -X PUT --data-binary @/docker-entrypoint.d/nextcloud-unit.json --unix-socket /var/run/control.unit.sock http://localhost/config) + RET_BODY=$(echo $RET | head -c -4) + RET_STATUS=$(echo $RET | tail -c 4) + if [ "$RET_STATUS" -ne "200" ]; then + echo "Error: HTTP response status code is '$RET_STATUS'" + echo "$RET_BODY" + return 1 + fi + + echo "Stopping Unit daemon after initial configuration..." + kill -TERM `/bin/cat /var/run/unit.pid` fi -if expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ] || [ "${NEXTCLOUD_UPDATE:-0}" -eq 1 ]; then +if expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ] || expr "$1" : "unitd" 1>/dev/null || [ "${NEXTCLOUD_UPDATE:-0}" -eq 1 ]; then if [ -n "${REDIS_HOST+x}" ]; then echo "Configuring Redis as session handler" @@ -191,4 +211,14 @@ if expr "$1" : "apache" 1>/dev/null || [ "$1" = "php-fpm" ] || [ "${NEXTCLOUD_UP fi fi +if expr "$1" : "unitd" 1>/dev/null; then + # Ensure the unitd daemon is stopped after initial configuration + while [ -S /var/run/control.unit.sock ]; do + echo "Waiting for control socket from config load to be removed..." + sleep 0.5 + done + + exec $@ +fi + exec "$@" diff --git a/nextcloud-unit.json b/nextcloud-unit.json new file mode 100644 index 0000000000..8bbef64b8d --- /dev/null +++ b/nextcloud-unit.json @@ -0,0 +1,133 @@ +{ + "listeners": { + "*:80": { + "pass": "routes" + } + }, + + "routes": [ + { + "match": { + "uri": [ + "/.well-known/carddav", + "/.well-known/caldav" + ] + }, + + "action": { + "return": 301, + "location": "/remote.php/dav" + } + }, + { + "match": { + "uri": [ + "/.well-known/*" + ] + }, + + "action": { + "pass": "applications/nextcloud/index" + } + }, + { + "match": { + "uri": [ + "/build/*", + "/tests/*", + "/config/*", + "/lib/*", + "/3rdparty/*", + "/templates/*", + "/data/*", + "/.*", + "/autotest*", + "/occ*", + "/issue*", + "/indie*", + "/db_*", + "/console*" + ] + }, + + "action": { + "return": 404 + } + }, + { + "match": { + "uri": [ + "/core/ajax/update.php*", + "/cron.php*", + "/index.php*", + "/ocm-provider*.php*", + "/ocs-provider*.php*", + "/ocs/v1.php*", + "/ocs/v2.php*", + "/public.php*", + "/remote.php*", + "/status.php*", + "/updater*.php*" + ] + }, + + "action": { + "pass": "applications/nextcloud/direct" + } + }, + { + "match": { + "uri": "/ocm-provider*" + }, + + "action": { + "pass": "applications/nextcloud/ocm" + } + }, + { + "match": { + "uri": "/ocs-provider*" + }, + + "action": { + "pass": "applications/nextcloud/ocs" + } + }, + { + "action": { + "share": "/var/www/html/", + "fallback": { + "pass": "applications/nextcloud/index" + } + } + } + ], + + "applications": { + "nextcloud": { + "type": "php", + "user": "www-data", + "processes": {}, + "targets": { + "direct": { + "root": "/var/www/html/" + }, + + "index": { + "root": "/var/www/html/", + "script": "index.php" + }, + + "ocm": { + "root": "/var/www/html/ocm-provider/", + "script": "index.php" + }, + + "ocs": { + "root": "/var/www/html/ocs-provider/", + "script": "index.php" + } + } + } + } +} diff --git a/update.sh b/update.sh index e293a80068..071b0c3459 100755 --- a/update.sh +++ b/update.sh @@ -11,24 +11,36 @@ declare -A cmd=( [apache]='apache2-foreground' [fpm]='php-fpm' [fpm-alpine]='php-fpm' + [unit]='unitd --no-daemon --control unix:/var/run/control.unix.sock' ) declare -A base=( [apache]='debian' [fpm]='debian' [fpm-alpine]='alpine' + [unit]='unit' ) declare -A extras=( [apache]='\nRUN a2enmod headers rewrite remoteip ;\\\n {\\\n echo RemoteIPHeader X-Real-IP ;\\\n echo RemoteIPTrustedProxy 10.0.0.0/8 ;\\\n echo RemoteIPTrustedProxy 172.16.0.0/12 ;\\\n echo RemoteIPTrustedProxy 192.168.0.0/16 ;\\\n } > /etc/apache2/conf-available/remoteip.conf;\\\n a2enconf remoteip' [fpm]='' [fpm-alpine]='' + [unit]='' ) declare -A crontab_int=( [default]='5' ) +unit_version="$( + git ls-remote --tags https://github.com/nginx/unit.git \ + | cut -d/ -f3 \ + | grep -v -- '-1' \ + | sed -E 's/^v//' \ + | sort -V \ + | tail -1 +)" + apcu_version="$( git ls-remote --tags https://github.com/krakjoe/apcu.git \ | cut -d/ -f3 \ @@ -76,6 +88,7 @@ variants=( apache fpm fpm-alpine + unit ) min_version='20' @@ -90,6 +103,11 @@ function create_variant() { phpVersion=${php_version[$version]-${php_version[default]}} crontabInt=${crontab_int[$version]-${crontab_int[default]}} + if [[ "$(printf '8.0\n%s\n' "$phpVersion" | sort -V | head -n 1)" != "8.0" ]] && [[ "$variant" == "unit" ]]; then + return 0 + fi + + # Create the version+variant directory with a Dockerfile. mkdir -p "$dir" @@ -106,11 +124,12 @@ function create_variant() { s/%%VERSION%%/'"$fullversion"'/g; s/%%BASE_DOWNLOAD_URL%%/'"$2"'/g; s/%%CMD%%/'"${cmd[$variant]}"'/g; - s|%%VARIANT_EXTRAS%%|'"${extras[$variant]}"'|g; + s/%%VARIANT_EXTRAS%%/'"${extras[$variant]}"'/g; s/%%APCU_VERSION%%/'"${pecl_versions[APCu]}"'/g; s/%%MEMCACHED_VERSION%%/'"${pecl_versions[memcached]}"'/g; s/%%REDIS_VERSION%%/'"${pecl_versions[redis]}"'/g; s/%%IMAGICK_VERSION%%/'"${pecl_versions[imagick]}"'/g; + s/%%UNIT_VERSION%%/'"${unit_version}"'/g; s/%%CRONTAB_INT%%/'"$crontabInt"'/g; ' "$dir/Dockerfile" @@ -133,6 +152,10 @@ function create_variant() { cp "docker-$name.sh" "$dir/$name.sh" done + if [ "$variant" == "unit" ]; then + cp nextcloud-unit.json $dir/nextcloud-unit.json + fi + # Copy the upgrade.exclude cp upgrade.exclude "$dir/"