diff --git a/alpine/5/Dockerfile b/alpine/5/Dockerfile index cf5dea6..d66a086 100644 --- a/alpine/5/Dockerfile +++ b/alpine/5/Dockerfile @@ -1,65 +1,60 @@ -ARG PHP_VERSION=8.2 +ARG PHP_VERSION=8.3 +ARG ALPINE_VERSION=3.20 ARG BAK_STORAGE_PATH=/var/www/app/docker-backup-storage/ ARG BAK_PUBLIC_PATH=/var/www/app/docker-backup-public/ # Get Invoice Ninja and install nodejs packages -FROM --platform=$BUILDPLATFORM node:lts-alpine as nodebuild +FROM --platform=$BUILDPLATFORM php:${PHP_VERSION}-fpm-alpine${ALPINE_VERSION} AS nodebuild # Download Invoice Ninja ARG INVOICENINJA_VERSION ARG REPOSITORY=invoiceninja/invoiceninja ARG FILENAME=invoiceninja.tar -RUN set -eux; apk add curl unzip grep - -RUN DOWNLOAD_URL=$(curl -s "https://api.github.com/repos/invoiceninja/invoiceninja/releases/latest" | grep -o '"browser_download_url": "[^"]*invoiceninja.tar"' | cut -d '"' -f 4) && \ - curl -LJO "$DOWNLOAD_URL" && \ - mv invoiceninja.tar /tmp/ninja.tar +RUN mkdir -p /var/www/app # Extract Invoice Ninja -RUN mkdir -p /var/www/app \ - && tar -xvf /tmp/ninja.tar -C /var/www/app/ \ - && mkdir -p /var/www/app/public/logo /var/www/app/storage - +RUN apk add --no-cache curl \ + && curl -s "https://api.github.com/repos/invoiceninja/invoiceninja/releases/latest" | \ + grep -o '"browser_download_url": "[^"]*invoiceninja.tar.gz"' | \ + cut -d '"' -f 4 | \ + xargs curl -sL | \ + tar -xz --strip-components=1 -C /var/www/app/ \ + && apk --purge del curl + +RUN mkdir -p /var/www/app/public/logo /var/www/app/storage + WORKDIR /var/www/app # Prepare php image -FROM php:${PHP_VERSION}-fpm-alpine as phpbuild +FROM php:${PHP_VERSION}-fpm-alpine${ALPINE_VERSION} AS phpbuild LABEL maintainer="David Bomba " -# Adding caching_sha2_password.so -# With this we get native support for caching_sha2_password -RUN apk add --no-cache mariadb-connector-c +ARG php_require="bcmath gd pdo_mysql zip" +ARG php_suggest="exif imagick intl pcntl soap" +ARG php_extra="opcache" -RUN mv /usr/local/etc/php/php.ini-production /usr/local/etc/php/php.ini - -# Install PHP extensions -# https://hub.docker.com/r/mlocati/php-extension-installer/tags -COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ +RUN ln -s "${PHP_INI_DIR}/php.ini-production" "${PHP_INI_DIR}/php.ini" # Install chromium RUN set -eux; \ apk add --no-cache \ font-isas-misc \ supervisor \ - mysql-client \ + mariadb-client \ + mariadb-connector-c \ chromium \ + # font-noto-cjk-extra \ + # font-wqy-zenhei \ ttf-freefont \ ttf-dejavu -RUN install-php-extensions \ - bcmath \ - exif \ - gd \ - gmp \ - mysqli \ - opcache \ - pdo_mysql \ - zip \ - intl \ - @composer \ - && rm /usr/local/bin/install-php-extensions +# Install PHP extensions +RUN ( curl -sSLf https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions -o - || echo 'return 1' ) | sh -s \ + ${php_require} \ + ${php_suggest} \ + ${php_extra} # Copy files COPY rootfs / @@ -87,8 +82,6 @@ ENV BAK_STORAGE_PATH $BAK_STORAGE_PATH ENV BAK_PUBLIC_PATH $BAK_PUBLIC_PATH COPY --from=nodebuild --chown=$INVOICENINJA_USER:$INVOICENINJA_USER /var/www/app /var/www/app -RUN rm -rf /var/www/app/ui - USER $UID WORKDIR /var/www/app @@ -106,7 +99,7 @@ ARG BAK_PUBLIC_PATH RUN mv /var/www/app/storage $BAK_STORAGE_PATH \ && mv /var/www/app/public $BAK_PUBLIC_PATH -FROM phpbuild as prod +FROM phpbuild AS prod COPY --from=dependencybuild --chown=$INVOICENINJA_USER:$INVOICENINJA_USER /var/www/app /var/www/app diff --git a/alpine/5/rootfs/etc/supervisord.conf b/alpine/5/rootfs/etc/supervisord.conf index 3ca2d92..2ed26a7 100644 --- a/alpine/5/rootfs/etc/supervisord.conf +++ b/alpine/5/rootfs/etc/supervisord.conf @@ -1,37 +1,36 @@ [supervisord] nodaemon=true -pidfile=/tmp/supervisord.pid -logfile=/dev/null ; nodaemon will cause logs to go to stdout +logfile=/dev/null logfile_maxbytes=0 -loglevel=info + +[rpcinterface:supervisor] +supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface [program:php-fpm] -redirect_stderr=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 command=php-fpm +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true [program:scheduler] -autorestart=true -redirect_stderr=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 command=php artisan schedule:work +autorestart=true +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true [program:queue-worker] process_name=%(program_name)s_%(process_num)02d +command=php artisan queue:work --sleep=3 --tries=1 --timeout=3600 +autostart=true autorestart=true -redirect_stderr=true -stdout_logfile=/dev/stdout -stdout_logfile_maxbytes=0 -stderr_logfile=/dev/stderr -stderr_logfile_maxbytes=0 +stopasgroup=true +killasgroup=true numprocs=2 -command=php artisan queue:work --sleep=3 --tries=1 --memory=256 --timeout=3600 +stdout_logfile=/dev/fd/1 +stdout_logfile_maxbytes=0 +redirect_stderr=true +stopwaitsecs=3600 [eventlistener:shutdown] command=shutdown.sh diff --git a/alpine/5/rootfs/usr/local/bin/invoiceninja-init.sh b/alpine/5/rootfs/usr/local/bin/invoiceninja-init.sh index b4b2ee5..f4a21ba 100755 --- a/alpine/5/rootfs/usr/local/bin/invoiceninja-init.sh +++ b/alpine/5/rootfs/usr/local/bin/invoiceninja-init.sh @@ -30,7 +30,7 @@ php artisan optimize php artisan package:discover # Check if DB works, if not crash the app. -DB_READY=$(php artisan tinker --execute='echo app()->call("App\Utils\SystemHealth@dbCheck")["success"];') +DB_READY=$(php -d opcache.preload='' artisan tinker --execute='echo app()->call("App\Utils\SystemHealth@dbCheck")["success"];') if [ "$DB_READY" != "1" ]; then php artisan migrate:status # Print verbose error in_error "Error connecting to DB" @@ -39,7 +39,7 @@ fi php artisan migrate --force # If first IN run, it needs to be initialized -IN_INIT=$(php artisan tinker --execute='echo Schema::hasTable("accounts") && !App\Models\Account::all()->first();') +IN_INIT=$(php -d opcache.preload='' artisan tinker --execute='echo Schema::hasTable("accounts") && !App\Models\Account::all()->first();') if [ "$IN_INIT" == "1" ]; then docker_process_init_files /docker-entrypoint-init.d/* fi diff --git a/alpine/5/rootfs/usr/local/etc/php-fpm.d/invoiceninja.conf b/alpine/5/rootfs/usr/local/etc/php-fpm.d/invoiceninja.conf new file mode 100644 index 0000000..63bbada --- /dev/null +++ b/alpine/5/rootfs/usr/local/etc/php-fpm.d/invoiceninja.conf @@ -0,0 +1 @@ +pm.max_children = 10 diff --git a/alpine/5/rootfs/usr/local/etc/php/conf.d/in-php.ini b/alpine/5/rootfs/usr/local/etc/php/conf.d/in-php.ini deleted file mode 100644 index 1ecaf93..0000000 --- a/alpine/5/rootfs/usr/local/etc/php/conf.d/in-php.ini +++ /dev/null @@ -1,17 +0,0 @@ -; How often (in seconds) to check file timestamps for changes to the shared -; memory storage allocation. ("1" means validate once per second, but only -; once per request. "0" means always validate) -;opcache.revalidate_freq=2 -opcache.revalidate_freq=60 - -# http://symfony.com/doc/current/performance.html -; Duration of time, in seconds for which to cache realpath information for a given -; file or directory. For systems with rarely changing files, consider increasing this -; value. -; http://php.net/realpath-cache-ttl -;realpath_cache_ttl = 120 -realpath_cache_ttl = 600 - -; Maximum allowed size for uploaded files. -; http://php.net/upload-max-filesize -upload_max_filesize = 8M diff --git a/alpine/5/rootfs/usr/local/etc/php/conf.d/invoiceninja.ini b/alpine/5/rootfs/usr/local/etc/php/conf.d/invoiceninja.ini new file mode 100644 index 0000000..b9f3c58 --- /dev/null +++ b/alpine/5/rootfs/usr/local/etc/php/conf.d/invoiceninja.ini @@ -0,0 +1,22 @@ +[core] +; https://www.php.net/manual/en/ini.core.php +post_max_size=10M +upload_max_filesize=10M + +[opcache] +; https://www.php.net/manual/en/opcache.installation.php#opcache.installation.recommended +opcache.enable_cli=1 + +[jit] +; https://wiki.php.net/rfc/jit_config_defaults +opcache.jit=tracing +opcache.jit_buffer_size=64M + +[extra] +; http://symfony.com/doc/current/performance.html +opcache.memory_consumption=256 +opcache.max_accelerated_files=20000 +opcache.preload=/var/www/app/preload.php +opcache.validate_timestamps=0 +realpath_cache_size=4096K +realpath_cache_ttl=600 diff --git a/config/nginx/in-vhost.conf b/config/nginx/in-vhost.conf deleted file mode 100644 index cf262f7..0000000 --- a/config/nginx/in-vhost.conf +++ /dev/null @@ -1,34 +0,0 @@ -server { - listen 80 default_server; - server_name _; - - server_tokens off; - - client_max_body_size 100M; - - root /var/www/app/public/; - index index.php; - - location / { - try_files $uri $uri/ /index.php?$query_string; - } - - location = /favicon.ico { access_log off; log_not_found off; } - location = /robots.txt { access_log off; log_not_found off; } - - - location ~* /storage/.*\.php$ { - return 503; - } - - location ~ \.php$ { - fastcgi_split_path_info ^(.+\.php)(/.+)$; - fastcgi_pass app:9000; - fastcgi_index index.php; - include fastcgi_params; - fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; - fastcgi_intercept_errors off; - fastcgi_buffer_size 16k; - fastcgi_buffers 4 16k; - } -} diff --git a/config/nginx/invoiceninja.conf b/config/nginx/invoiceninja.conf new file mode 100644 index 0000000..78add9d --- /dev/null +++ b/config/nginx/invoiceninja.conf @@ -0,0 +1,14 @@ +# https://nginx.org/en/docs/http/ngx_http_core_module.html +client_max_body_size 10M; +client_body_buffer_size 10M; +server_tokens off; + +# https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html +fastcgi_buffers 32 16K; + +# https://nginx.org/en/docs/http/ngx_http_gzip_module.html +gzip on; +gzip_comp_level 2; +gzip_min_length 1M; +gzip_proxied any; +gzip_types *; diff --git a/config/nginx/laravel.conf b/config/nginx/laravel.conf new file mode 100644 index 0000000..5edb43a --- /dev/null +++ b/config/nginx/laravel.conf @@ -0,0 +1,32 @@ +# https://laravel.com/docs/master/deployment#nginx +server { + listen 80 default_server; + server_name _; + root /var/www/app/public; + + add_header X-Frame-Options "SAMEORIGIN"; + add_header X-Content-Type-Options "nosniff"; + + index index.php; + + charset utf-8; + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location = /favicon.ico { access_log off; log_not_found off; } + location = /robots.txt { access_log off; log_not_found off; } + + error_page 404 /index.php; + + location ~ \.php$ { + fastcgi_pass app:9000; + fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + include fastcgi_params; + } + + location ~ /\.(?!well-known).* { + deny all; + } +} diff --git a/config/php/php-cli.ini b/config/php/php-cli.ini deleted file mode 100644 index c66bcfb..0000000 --- a/config/php/php-cli.ini +++ /dev/null @@ -1,18 +0,0 @@ -session.auto_start = Off -short_open_tag = Off - -error_reporting = E_ALL & ~E_NOTICE & ~E_WARNING & ~E_STRICT & ~E_DEPRECATED - -; opcache.enable_cli=1 -; opcache.fast_shutdown=1 -; opcache.memory_consumption=256 -; opcache.interned_strings_buffer=8 -; opcache.max_accelerated_files=4000 -; opcache.revalidate_freq=60 -; # http://symfony.com/doc/current/performance.html -; realpath_cache_size = 4096K -; realpath_cache_ttl = 600 - -memory_limit = 2G -post_max_size = 60M -upload_max_filesize = 50M \ No newline at end of file diff --git a/config/php/php-fpm.conf b/config/php/php-fpm.conf new file mode 100644 index 0000000..63bbada --- /dev/null +++ b/config/php/php-fpm.conf @@ -0,0 +1 @@ +pm.max_children = 10 diff --git a/config/php/php.ini b/config/php/php.ini index f5298a6..b9f3c58 100644 --- a/config/php/php.ini +++ b/config/php/php.ini @@ -1,21 +1,22 @@ -session.auto_start = Off -short_open_tag = Off +[core] +; https://www.php.net/manual/en/ini.core.php +post_max_size=10M +upload_max_filesize=10M -error_reporting = E_ALL & ~E_NOTICE & ~E_WARNING & ~E_STRICT & ~E_DEPRECATED +[opcache] +; https://www.php.net/manual/en/opcache.installation.php#opcache.installation.recommended +opcache.enable_cli=1 -; opcache.enable=1 -; opcache.preload=/var/www/app/preload.php -; opcache.preload_user=www-data +[jit] +; https://wiki.php.net/rfc/jit_config_defaults +opcache.jit=tracing +opcache.jit_buffer_size=64M -; ; The OPcache shared memory storage size. -; opcache.max_accelerated_files=300000 -; opcache.validate_timestamps=1 -; opcache.revalidate_freq=30 -; opcache.jit_buffer_size=256M -; opcache.jit=1205 -; opcache.memory_consumption=1024M - - -post_max_size = 60M -upload_max_filesize = 50M -memory_limit=512M +[extra] +; http://symfony.com/doc/current/performance.html +opcache.memory_consumption=256 +opcache.max_accelerated_files=20000 +opcache.preload=/var/www/app/preload.php +opcache.validate_timestamps=0 +realpath_cache_size=4096K +realpath_cache_ttl=600 diff --git a/docker-compose.yml b/docker-compose.yml index 187a742..338facf 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,14 +1,12 @@ -version: '3.7' - services: - server: + nginx: image: nginx restart: always env_file: env volumes: # Vhost configuration #- ./config/caddy/Caddyfile:/etc/caddy/Caddyfiledocker-com - - ./config/nginx/in-vhost.conf:/etc/nginx/conf.d/in-vhost.conf:ro + - ./config/nginx:/etc/nginx/conf.d:ro - ./docker/app/public:/var/www/app/public:ro depends_on: - app @@ -17,28 +15,27 @@ services: ports: - "80:80" #- "443:443" - networks: - - invoiceninja - extra_hosts: - - "in5.localhost:192.168.0.124 " #host and ip + # extra_hosts: + # - "in5.localhost:192.168.0.124 " #host and ip app: - image: invoiceninja/invoiceninja:5 + build: + context: ./alpine/5 + image: invoiceninja/invoiceninja:${TAG:-5} env_file: env restart: always volumes: - - ./config/hosts:/etc/hosts:ro + # - ./config/hosts:/etc/hosts:ro - ./docker/app/public:/var/www/app/public:rw,delegated - ./docker/app/storage:/var/www/app/storage:rw,delegated - - ./config/php/php.ini:/usr/local/etc/php/php.ini - - ./config/php/php-cli.ini:/usr/local/etc/php/php-cli.ini + # - ./config/php/php.ini:/usr/local/etc/php/conf.d/invoiceninja.ini + # - ./config/php/php-fpm.conf:/usr/local/etc/php-fpm.d/invoiceninja.conf depends_on: - db - networks: - - invoiceninja - extra_hosts: - - "in5.localhost:192.168.0.124 " #host and ip + - valkey + # extra_hosts: + # - "in5.localhost:192.168.0.124 " #host and ip db: image: mysql:8 @@ -57,10 +54,8 @@ services: #- ./config/mysql/backup-script:/etc/cron.daily/daily:ro #- ./config/mysql/backup-script:/etc/cron.weekly/weekly:ro #- ./config/mysql/backup-script:/etc/cron.monthly/monthly:ro - networks: - - invoiceninja - extra_hosts: - - "in5.localhost:192.168.0.124 " #host and ip + # extra_hosts: + # - "in5.localhost:192.168.0.124 " #host and ip # THIS IS ONLY A VALID CONFIGURATION FOR IN 4. DO NOT USE FOR IN 5. # cron: @@ -82,6 +77,9 @@ services: # networks: # - invoiceninja # - -networks: - invoiceninja: + valkey: + image: valkey/valkey:8 + restart: unless-stopped + healthcheck: + test: [ "CMD", "valkey-cli", "ping" ] + start_period: 10s diff --git a/env b/env index eb49e7d..9d99e59 100644 --- a/env +++ b/env @@ -7,8 +7,13 @@ PHANTOMJS_PDF_GENERATION=false PDF_GENERATOR=snappdf TRUSTED_PROXIES='*' +CACHE_DRIVER=redis +QUEUE_CONNECTION=redis +SESSION_DRIVER=redis -QUEUE_CONNECTION=database +REDIS_HOST=valkey +REDIS_PASSWORD=null +REDIS_PORT=6379 # DB connection DB_HOST=db