diff --git a/alpine/5/rootfs/usr/local/bin/invoiceninja-init.sh b/alpine/5/rootfs/usr/local/bin/invoiceninja-init.sh index b4b2ee5..51d4741 100755 --- a/alpine/5/rootfs/usr/local/bin/invoiceninja-init.sh +++ b/alpine/5/rootfs/usr/local/bin/invoiceninja-init.sh @@ -1,8 +1,10 @@ #!/bin/sh -# usage: docker_process_init_files [file [file [...]]] -# ie: docker_process_init_files /always-initdb.d/* -# process initializer files, based on file extensions +in_log() { + local type="$1"; shift + printf '%s [%s] [Entrypoint]: %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$type" "$*" +} + docker_process_init_files() { echo local f diff --git a/debian/.env b/debian/.env index a321d43..a3196eb 100644 --- a/debian/.env +++ b/debian/.env @@ -1,6 +1,7 @@ # IN application vars APP_URL=http://in.localhost:8003 -APP_KEY= +APP_KEY=base64:RR++yx2rJ9kdxbdh3+AmbHLDQu+Q76i++co9Y8ybbno= +APP_ENV=production APP_DEBUG=true REQUIRE_HTTPS=false PHANTOMJS_PDF_GENERATION=false @@ -10,19 +11,20 @@ TRUSTED_PROXIES='*' QUEUE_CONNECTION=database # DB connection -DB_HOST=db +DB_HOST=mysql DB_PORT=3306 DB_DATABASE=ninja DB_USERNAME=ninja DB_PASSWORD=ninja DB_ROOT_PASSWORD=ninjaAdm1nPassword +DB_CONNECTION=mysql # Create initial user # Default to these values if empty -# IN_USER_EMAIL=admin@example.com -# IN_PASSWORD=changeme! -IN_USER_EMAIL= -IN_PASSWORD= +IN_USER_EMAIL=admin@example.com +IN_PASSWORD=changeme! +# IN_USER_EMAIL= +# IN_PASSWORD= # Mail options MAIL_MAILER=log diff --git a/debian/Dockerfile b/debian/Dockerfile index 03ac2c8..e9bbcae 100644 --- a/debian/Dockerfile +++ b/debian/Dockerfile @@ -1,4 +1,4 @@ -FROM php:8.2-fpm as base +FROM php:8.2-fpm AS base # Install system dependencies RUN apt-get update && apt-get install -y \ @@ -9,14 +9,19 @@ RUN apt-get update && apt-get install -y \ libxml2-dev \ zip \ unzip \ + gosu \ + default-mysql-client \ supervisor \ && apt-get clean \ && rm -rf /var/lib/apt/lists/* -# Install PHP extensions +# Copy Install PHP extensions installer COPY --from=mlocati/php-extension-installer /usr/bin/install-php-extensions /usr/local/bin/ + +# Install Required PHP extensions. RUN install-php-extensions \ pdo_mysql \ + mysqli \ mbstring \ exif \ pcntl \ @@ -24,6 +29,10 @@ RUN install-php-extensions \ gd \ opcache \ redis \ + soap \ + imagick \ + curl \ + gmp \ zip \ @composer @@ -31,11 +40,14 @@ RUN install-php-extensions \ COPY php/php.ini /usr/local/etc/php/conf.d/app.ini COPY php/php-fpm.conf /usr/local/etc/php-fpm.d/www.conf +# Copy scripts +COPY rootfs / + # Set working directory WORKDIR /var/www/html -# Download and extract application in a single layer -RUN set -eux; \ +# Download and extract application +RUN set -eux; \ 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 -L "$DOWNLOAD_URL" | tar -xvz -C /var/www/html && \ @@ -57,8 +69,14 @@ COPY supervisor/supervisord.conf /etc/supervisor/conf.d/supervisord.conf # Add initialization script COPY scripts/init.sh /usr/local/bin/init.sh + +# Make executable RUN chmod +x /usr/local/bin/init.sh +# Configure PHP-FPM +RUN sed -i "s/user = www-data/user = www-data/g" /usr/local/etc/php-fpm.d/www.conf \ + && sed -i "s/group = www-data/group = www-data/g" /usr/local/etc/php-fpm.d/www.conf + # Create volume directories RUN mkdir -p \ /var/www/html/storage/app/public \ @@ -67,6 +85,7 @@ RUN mkdir -p \ /var/www/html/storage/framework/views \ /var/www/html/storage/logs \ /var/www/html/public/uploads \ + /var/run \ /var/log/supervisor # Set permissions @@ -74,15 +93,15 @@ RUN chown -R www-data:www-data \ /var/www/html/storage \ /var/www/html/bootstrap/cache \ /var/www/html/public/uploads \ + /var/run \ /var/log/supervisor \ && chmod -R 775 \ /var/www/html/public/uploads \ /var/www/html/storage \ /var/www/html/bootstrap/cache \ + /var/run \ /var/log/supervisor -USER www-data - # Health check HEALTHCHECK --interval=30s --timeout=5s --start-period=30s --retries=3 \ CMD php -v || exit 1 diff --git a/debian/docker-compose.yml b/debian/docker-compose.yml index 6977639..aae4fdf 100644 --- a/debian/docker-compose.yml +++ b/debian/docker-compose.yml @@ -8,15 +8,16 @@ x-logging: &default-logging services: app: - image: deb6:latest - user: "www-data:www-data" # Set user explicitly + image: deb39b:latest restart: unless-stopped env_file: - - .env + - ./.env volumes: - - app_storage:/var/www/html/storage:rw - - app_public:/var/www/html/public/uploads:rw - - app_cache:/var/www/html/bootstrap/cache:rw + - ./.env:/var/www/html/.env + - app_storage:/var/www/html/storage + - app_cache:/var/www/html/bootstrap/cache + - public_files:/var/www/html/public + networks: - app-network depends_on: @@ -36,9 +37,12 @@ services: ports: - "8012:80" volumes: - - app_public:/var/www/html/public/uploads:ro - ./nginx/conf.d:/etc/nginx/conf.d:ro - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro + - type: volume + source: public_files + target: /var/www/html/public + read_only: true networks: - app-network depends_on: @@ -53,7 +57,7 @@ services: image: mysql:8.0 restart: unless-stopped env_file: - - .env + - ./.env environment: MYSQL_DATABASE: ${DB_DATABASE} MYSQL_USER: ${DB_USERNAME} @@ -64,7 +68,7 @@ services: networks: - app-network healthcheck: - test: ["CMD", "mysqladmin", "ping", "-p${DB_PASSWORD}"] + test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u${MYSQL_USER}", "-p${MYSQL_PASSWORD}"] interval: 10s timeout: 5s retries: 5 @@ -106,4 +110,6 @@ volumes: mysql_data: driver: local redis_data: + driver: local + public_files: driver: local \ No newline at end of file diff --git a/debian/nginx/conf.d/default.conf b/debian/nginx/conf.d/default.conf index 70b45fb..758d675 100644 --- a/debian/nginx/conf.d/default.conf +++ b/debian/nginx/conf.d/default.conf @@ -1,12 +1,18 @@ server { - listen 80; - server_name localhost; + + error_log /var/log/nginx/error.log debug; + access_log /var/log/nginx/access.log; + + listen 80 default_server; + server_name _; + + server_tokens off; + + client_max_body_size 100M; + root /var/www/html/public; index index.php; - charset utf-8; - client_max_body_size 100M; - location / { try_files $uri $uri/ /index.php?$query_string; } @@ -14,21 +20,19 @@ server { 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 ~* /storage/.*\.php$ { + return 503; + } location ~ \.php$ { + fastcgi_split_path_info ^(.+\.php)(/.+)$; fastcgi_pass app:9000; - fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name; + 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; } - - location ~ /\.(?!well-known).* { - deny all; - } - - # Enable browser caching for static assets - location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { - expires 30d; - add_header Cache-Control "public, no-transform"; - } -} \ No newline at end of file +} diff --git a/debian/php/php-fpm.conf b/debian/php/php-fpm.conf index 48ba99a..19dbeb1 100644 --- a/debian/php/php-fpm.conf +++ b/debian/php/php-fpm.conf @@ -1,7 +1,7 @@ [www] user = www-data group = www-data -listen = 9000 +listen = 0.0.0.0:9000 pm = dynamic pm.max_children = 5 pm.start_servers = 2 diff --git a/debian/rootfs/docker-entrypoint-init.d/10-init-in.sh b/debian/rootfs/docker-entrypoint-init.d/10-init-in.sh new file mode 100644 index 0000000..3ff5032 --- /dev/null +++ b/debian/rootfs/docker-entrypoint-init.d/10-init-in.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +php artisan db:seed --force + +# Build up array of arguments... +if [[ ! -z "${IN_USER_EMAIL}" ]]; then + email="--email ${IN_USER_EMAIL}" +fi + +if [[ ! -z "${IN_PASSWORD}" ]]; then + password="--password ${IN_PASSWORD}" +fi + +php artisan ninja:create-account $email $password \ No newline at end of file diff --git a/debian/scripts/init.sh b/debian/scripts/init.sh index e96b124..0e42cad 100755 --- a/debian/scripts/init.sh +++ b/debian/scripts/init.sh @@ -1,6 +1,34 @@ #!/bin/sh set -e + +in_log() { + local type="$1"; shift + printf '%s [%s] [Entrypoint]: %s\n' "$(date -u '+%Y-%m-%dT%H:%M:%SZ')" "$type" "$*" +} + +docker_process_init_files() { + echo + local f + for f; do + case "$f" in + *.sh) + # https://github.com/docker-library/postgres/issues/450#issuecomment-393167936 + # https://github.com/docker-library/postgres/pull/452 + if [ -x "$f" ]; then + in_log INFO "$0: running $f" + "$f" + else + in_log INFO "$0: sourcing $f" + . "$f" + fi + ;; + *) in_log INFO "$0: ignoring $f" ;; + esac + echo + done +} + # Create directories if they don't exist mkdir -p \ /var/www/html/storage/app/public \ @@ -19,9 +47,27 @@ chmod -R 775 \ # Clear and cache config in production if [ "$APP_ENV" = "production" ]; then php artisan config:cache - php artisan route:cache - php artisan view:cache + php artisan optimize + php artisan package:discover + php artisan migrate --force + + echo "Checking initialization status..." + + # If first IN run, it needs to be initialized + echo "Checking initialization status..." + IN_INIT=$(php artisan tinker --execute='echo Schema::hasTable("accounts") && !App\Models\Account::all()->first();') + echo "IN_INIT value: $IN_INIT" + + if [ "$IN_INIT" = "1" ]; then + echo "Running initialization scripts..." + docker_process_init_files /docker-entrypoint-init.d/* + fi + + echo "Production setup completed" + echo "IN_INIT value: $IN_INIT" + fi -# Start supervisord -exec "$@" \ No newline at end of file +echo "Starting supervisord..." +# Start supervisord in the foreground +exec /usr/bin/supervisord -n -c /etc/supervisor/conf.d/supervisord.conf \ No newline at end of file diff --git a/debian/supervisor/supervisord.conf b/debian/supervisor/supervisord.conf index ac43a85..9370baf 100644 --- a/debian/supervisor/supervisord.conf +++ b/debian/supervisor/supervisord.conf @@ -1,17 +1,30 @@ +[unix_http_server] +file=/var/run/supervisor.sock +chmod=0700 + [supervisord] nodaemon=true -user=www-data +user=root logfile=/var/log/supervisor/supervisord.log pidfile=/var/run/supervisord.pid +[rpcinterface:supervisor] +supervisor.rpcinterface_factory=supervisor.rpcinterface:make_main_rpcinterface + +[supervisorctl] +serverurl=unix:///var/run/supervisor.sock + [program:php-fpm] -command=php-fpm +command=/usr/local/sbin/php-fpm -F autostart=true autorestart=true -stderr_logfile=/var/log/supervisor/php-fpm.err.log -stdout_logfile=/var/log/supervisor/php-fpm.out.log +priority=5 +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 -[program:laravel-worker] +[program:queue-worker] process_name=%(program_name)s_%(process_num)02d command=php /var/www/html/artisan queue:work --sleep=3 --tries=3 --max-time=3600 autostart=true @@ -21,13 +34,19 @@ killasgroup=true user=www-data numprocs=2 redirect_stderr=true -stdout_logfile=/var/log/supervisor/worker.log +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 stopwaitsecs=3600 -[program:laravel-scheduler] -command=php /var/www/html/artisan schedule:work +[program:scheduler] +command=/bin/sh -c "while [ true ]; do (php /var/www/html/artisan schedule:run --verbose --no-interaction &); sleep 60; done" autostart=true autorestart=true user=www-data redirect_stderr=true -stdout_logfile=/var/log/supervisor/scheduler.log \ No newline at end of file +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 \ No newline at end of file