octane and multistage

This commit is contained in:
Benjamin Brummer
2024-12-31 17:52:48 +01:00
parent e4c26dde5d
commit 8f51f3bc51
3 changed files with 96 additions and 67 deletions

116
debian/Dockerfile vendored
View File

@@ -1,16 +1,40 @@
FROM composer:latest AS composer
RUN curl -s "https://api.github.com/repos/invoiceninja/invoiceninja/releases/latest" | \
grep -o '"browser_download_url": "[^"]*invoiceninja.tar"' | \
cut -d '"' -f 4 | \
xargs curl -sL | \
tar -xz
RUN ln -s ./resources/views/react/index.blade.php ./public/index.html
# Set permissions: directories 755, files 644
RUN chmod -R a=r,u+w,a+X .
# Install dependencies
RUN composer install --no-dev --no-scripts --no-autoloader --ignore-platform-reqs
RUN composer dump-autoload --optimize
RUN php artisan storage:link
# Octane
RUN php artisan octane:install --server=frankenphp
# ==================
# InvoiceNinja image
# ==================
FROM dunglas/frankenphp:1-php8.3-bookworm FROM dunglas/frankenphp:1-php8.3-bookworm
ARG USER=ninja ARG user=ninja
# PHP modules # PHP modules
ARG php_require="bcmath gd pdo_mysql zip" ARG php_require="bcmath gd pdo_mysql zip"
ARG php_suggest="exif imagick intl pcntl soap saxon-12.5.0" ARG php_suggest="exif imagick intl pcntl soap saxon-12.5.0"
ARG php_extra="opcache" ARG php_extra="opcache"
ENV APP_DIR=/app # Create a system user UID/GID=999
RUN useradd -r ${user}
# Create a system user
RUN useradd -r ${USER}
# Allow to bind to privileged ports # Allow to bind to privileged ports
RUN setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp RUN setcap CAP_NET_BIND_SERVICE=+eip /usr/local/bin/frankenphp
@@ -26,19 +50,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
xfonts-wqy \ xfonts-wqy \
# Install google-chrome-stable(amd64)/chromium(arm64) # Install google-chrome-stable(amd64)/chromium(arm64)
&& if [ "$(dpkg --print-architecture)" = "amd64" ]; then \ && if [ "$(dpkg --print-architecture)" = "amd64" ]; then \
mkdir -p /etc/apt/keyrings \ mkdir -p /etc/apt/keyrings \
&& curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | \ && curl -fsSL https://dl.google.com/linux/linux_signing_key.pub | \
gpg --dearmor -o /etc/apt/keyrings/google.gpg \ gpg --dearmor -o /etc/apt/keyrings/google.gpg \
&& echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google.gpg] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \ && echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/google.gpg] https://dl.google.com/linux/chrome/deb/ stable main" > /etc/apt/sources.list.d/google-chrome.list \
&& apt-get update \ && apt-get update \
&& apt-get install -y --no-install-recommends google-chrome-stable \ && apt-get install -y --no-install-recommends google-chrome-stable \
&& mkdir /config/google-chrome \ && mkdir /config/google-chrome \
&& chown ${USER}: /config/google-chrome; \ && chown ${user}: /config/google-chrome; \
elif [ "$(dpkg --print-architecture)" = "arm64" ]; then \ elif [ "$(dpkg --print-architecture)" = "arm64" ]; then \
apt-get install -y --no-install-recommends \ apt-get install -y --no-install-recommends \
chromium \ chromium \
&& mkdir /config/chromium \ && mkdir /config/chromium \
&& chown ${USER}: /config/chromium; \ && chown ${user}: /config/chromium; \
fi \ fi \
# Cleanup # Cleanup
&& apt-get purge -y gpg \ && apt-get purge -y gpg \
@@ -48,46 +72,30 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
# Install PHP extensions # Install PHP extensions
RUN install-php-extensions \ RUN install-php-extensions \
${php_require} \ ${php_require} \
${php_suggest} \ ${php_suggest} \
${php_extra} \ ${php_extra}
@composer
# Configure PHP # Configure PHP
RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" RUN mv "${PHP_INI_DIR}/php.ini-production" "${PHP_INI_DIR}/php.ini"
# Create directory for artisan tinker (init.sh)
RUN mkdir /config/psysh \
&& chown ${user}: /config/psysh
# Change owner for caddy directories
RUN chown -R ${user}: \
/data/caddy \
/config/caddy
ENTRYPOINT ["/usr/local/bin/init.sh"]
CMD ["frankenphp", "php-cli", "artisan", "octane:frankenphp"]
# InvoiceNinja
COPY --from=composer --chown=${user}:${user} /app /app
# Add initialization script # Add initialization script
COPY --chmod=0755 scripts/init.sh /usr/local/bin/init.sh COPY --chmod=0755 scripts/init.sh /usr/local/bin/init.sh
# Prepare app directory USER ${user}
RUN rm -rf ${APP_DIR}/* \
&& chown ${USER}: ${APP_DIR}
# Create directory for artisan tinker (init.sh)
RUN mkdir /config/psysh \
&& chown ${USER}: /config/psysh
# Change owner for caddy directories
RUN chown -R ${USER}: \
/data/caddy \
/config/caddy
USER ${USER}
# Setup InvoiceNinja
RUN curl -s "https://api.github.com/repos/invoiceninja/invoiceninja/releases/latest" | \
grep -o '"browser_download_url": "[^"]*invoiceninja.tar"' | \
cut -d '"' -f 4 | \
xargs curl -sL | \
tar -oxz -C ${APP_DIR} \
&& ln -s ${APP_DIR}/resources/views/react/index.blade.php ${APP_DIR}/public/index.html \
# Set permissions: directories 755, files 644
&& chmod -R a=r,u+w,a+X ${APP_DIR} \
# Install dependencies
&& composer install --working-dir=${APP_DIR} --no-dev --no-scripts --no-autoloader \
&& composer dump-autoload --working-dir=${APP_DIR} --optimize \
&& frankenphp php-cli ${APP_DIR}/artisan storage:link
ENTRYPOINT ["/usr/local/bin/init.sh"]
CMD ["frankenphp", "run", "--config", "/etc/caddy/Caddyfile", "--adapter", "caddyfile"]

View File

@@ -1,3 +1,5 @@
# name: invoiceninja
x-logging: &default-logging x-logging: &default-logging
options: options:
max-size: "10m" max-size: "10m"
@@ -17,14 +19,14 @@ services:
context: . context: .
image: invoiceninja/invoiceninja-debian:${TAG:-latest} image: invoiceninja/invoiceninja-debian:${TAG:-latest}
restart: unless-stopped restart: unless-stopped
# php artisan help octane:frankenphp
command: --log-level=info
ports: ports:
- "80:80" - "80:8000"
env_file: env_file:
- ./.env - ./.env
environment: environment:
LARAVEL_ROLE: app LARAVEL_ROLE: app
# https://frankenphp.dev/docs/production/#preparing-your-app
SERVER_NAME: :80
<<: *volumes <<: *volumes
# HEALTHCHECK from frankenphp image # HEALTHCHECK from frankenphp image
healthcheck: healthcheck:
@@ -43,6 +45,8 @@ services:
app-worker: app-worker:
image: invoiceninja/invoiceninja-debian:${TAG:-latest} image: invoiceninja/invoiceninja-debian:${TAG:-latest}
restart: unless-stopped restart: unless-stopped
# php artisan help queue:work
command: --verbose --sleep=3 --tries=3 --max-time=3600
deploy: deploy:
mode: replicated mode: replicated
replicas: 2 replicas: 2
@@ -59,6 +63,8 @@ services:
app-scheduler: app-scheduler:
image: invoiceninja/invoiceninja-debian:${TAG:-latest} image: invoiceninja/invoiceninja-debian:${TAG:-latest}
restart: unless-stopped restart: unless-stopped
# php artisan help schedule:work
command: --verbose
env_file: env_file:
- ./.env - ./.env
environment: environment:

View File

@@ -1,42 +1,57 @@
#!/bin/sh -eu #!/bin/sh -eu
app_dir=${APP_DIR:-/app} # Fallback to app
role=${LARAVEL_ROLE:-app} role=${LARAVEL_ROLE:-app}
if [ "$*" = 'frankenphp run --config /etc/caddy/Caddyfile --adapter caddyfile' ]; then # Check for default CMD, flag(s) or empty CMD
if [ "$*" = 'frankenphp php-cli artisan octane:frankenphp' ] || [ "${1#-}" != "$1" ] || [ "$#" -eq "0" ]; then
# Run app
if [ "${role}" = "app" ]; then if [ "${role}" = "app" ]; then
cmd="frankenphp php-cli artisan octane:frankenphp"
if [ "$APP_ENV" = "production" ]; then if [ "$APP_ENV" = "production" ]; then
frankenphp php-cli "${app_dir}"/artisan optimize frankenphp php-cli artisan optimize
fi fi
frankenphp php-cli "${app_dir}"/artisan package:discover frankenphp php-cli artisan package:discover
frankenphp php-cli "${app_dir}"/artisan migrate --force # Run migrations (if any)
frankenphp php-cli artisan migrate --force
# If first IN run, it needs to be initialized # If first IN run, it needs to be initialized
if [ "$(frankenphp php-cli "${app_dir}"/artisan tinker --execute='echo Schema::hasTable("accounts") && !App\Models\Account::all()->first();')" = "1" ]; then if [ "$(frankenphp php-cli artisan tinker --execute='echo Schema::hasTable("accounts") && !App\Models\Account::all()->first();')" = "1" ]; then
echo "Running initialization..." echo "Running initialization..."
frankenphp php-cli "${app_dir}"/artisan db:seed --force frankenphp php-cli artisan db:seed --force
if [ -n "${IN_USER_EMAIL}" ] && [ -n "${IN_PASSWORD}" ]; then if [ -n "${IN_USER_EMAIL}" ] && [ -n "${IN_PASSWORD}" ]; then
frankenphp php-cli "${app_dir}"/artisan ninja:create-account --email "${IN_USER_EMAIL}" --password "${IN_PASSWORD}" frankenphp php-cli artisan ninja:create-account --email "${IN_USER_EMAIL}" --password "${IN_PASSWORD}"
else else
echo "Initialization failed - Set IN_USER_EMAIL and IN_PASSWORD in .env" echo "Initialization failed - Set IN_USER_EMAIL and IN_PASSWORD in .env"
exit 1 exit 1
fi fi
fi fi
echo "Production setup completed" echo "Production setup completed"
# Run worker
elif [ "${role}" = "worker" ]; then elif [ "${role}" = "worker" ]; then
exec frankenphp php-cli "${app_dir}"/artisan queue:work -v --sleep=3 --tries=3 --max-time=3600 cmd="frankenphp php-cli artisan queue:work"
# Run scheduler
elif [ "${role}" = "scheduler" ]; then elif [ "${role}" = "scheduler" ]; then
exec frankenphp php-cli "${app_dir}"/artisan schedule:work -v cmd="frankenphp php-cli artisan schedule:work"
# Invalid role
else else
echo "Invalid role: ${role}" echo "Invalid role: ${role}"
exit 1 exit 1
fi fi
# Append flag(s) to role cmd
if [ "${1#-}" != "$1" ]; then
set -- ${cmd} "$@"
else
set -- ${cmd}
fi
fi fi
exec "$@" exec "$@"