Enhance Docker Configuration and Health Checks (#761)
* Enhance Docker Configuration and Health Checks - Added PHP configuration settings in `docker-compose.dev.yml` and `docker-compose.yml` to improve memory management and execution limits, ensuring better performance for PHP applications. - Introduced health checks for various services including `api`, `api-worker`, `api-scheduler`, `ui`, `redis`, and `db` to ensure service availability and reliability. - Updated environment variables in `.env.docker` and `client/.env.docker` to include new keys for H-Captcha and reCAPTCHA, enhancing security features. - Refactored the PHP-FPM entrypoint script to apply PHP configurations dynamically based on environment variables, improving flexibility in deployment. - Removed outdated PHP configuration files to streamline the Docker setup. These changes aim to enhance the overall stability, performance, and security of the application in a Dockerized environment. * Refactor Dockerfile for Improved Build Process - Changed the Dockerfile to utilize a multi-stage build approach, separating the build and runtime environments for better efficiency. - Introduced a builder stage using the PHP CLI image to install dependencies and extensions, optimizing the final image size. - Removed unnecessary installation steps and combined related commands to streamline the Dockerfile, enhancing readability and maintainability. - Updated the runtime stage to use the PHP FPM Alpine image, ensuring a smaller and more secure production environment. These changes aim to improve the build process, reduce image size, and enhance the overall performance of the Dockerized application.
This commit is contained in:
parent
b2b04d7f2a
commit
ae21cae8cd
|
|
@ -10,6 +10,7 @@ LOG_CHANNEL=errorlog
|
|||
LOG_LEVEL=debug
|
||||
|
||||
FILESYSTEM_DRIVER=local
|
||||
LOCAL_FILESYSTEM_VISIBILITY=public
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
|
||||
|
|
@ -36,5 +37,17 @@ AWS_BUCKET=
|
|||
|
||||
JWT_TTL=1440
|
||||
JWT_SECRET=
|
||||
JWT_SKIP_IP_UA_VALIDATION=true
|
||||
|
||||
OPEN_AI_API_KEY=
|
||||
|
||||
H_CAPTCHA_SITE_KEY=
|
||||
H_CAPTCHA_SECRET_KEY=
|
||||
|
||||
RE_CAPTCHA_SITE_KEY=
|
||||
RE_CAPTCHA_SECRET_KEY=
|
||||
|
||||
TELEGRAM_BOT_ID=
|
||||
TELEGRAM_BOT_TOKEN=
|
||||
|
||||
SHOW_OFFICIAL_TEMPLATES=true
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Carbon;
|
||||
|
||||
class SchedulerStatusCommand extends Command
|
||||
{
|
||||
private const MODE_CHECK = 'check';
|
||||
private const MODE_RECORD = 'record';
|
||||
private const CACHE_KEY_LAST_RUN = 'scheduler_last_run_timestamp';
|
||||
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'app:scheduler-status {--mode=' . self::MODE_CHECK . ' : Mode of operation ("' . self::MODE_CHECK . '" or "' . self::MODE_RECORD . '")} {--max-minutes=3 : Maximum minutes since last run for check mode}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Records or checks the scheduler last run timestamp, conditional on self-hosted mode.';
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*
|
||||
* @return int
|
||||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$isSelfHosted = config('app.self_hosted', false);
|
||||
|
||||
if (!$isSelfHosted) {
|
||||
$this->error('This command is only functional in self-hosted mode. Please set SELF_HOSTED=true in your .env file.');
|
||||
Log::warning('SchedulerStatusCommand: Attempted to run in non-self-hosted mode.');
|
||||
return \Illuminate\Console\Command::FAILURE;
|
||||
}
|
||||
|
||||
$mode = $this->option('mode');
|
||||
|
||||
if ($mode === self::MODE_RECORD) {
|
||||
Cache::put(self::CACHE_KEY_LAST_RUN, Carbon::now()->getTimestamp(), Carbon::now()->addHours(2));
|
||||
$this->info('Scheduler last run timestamp recorded.');
|
||||
Log::info('SchedulerStatusCommand: Recorded last run timestamp.');
|
||||
return \Illuminate\Console\Command::SUCCESS;
|
||||
}
|
||||
|
||||
// Default to 'check' mode (this covers explicit 'check' or any other value for mode)
|
||||
|
||||
$lastRunTimestamp = Cache::get(self::CACHE_KEY_LAST_RUN);
|
||||
if (!$lastRunTimestamp) {
|
||||
$this->error('Scheduler last run timestamp not found.');
|
||||
Log::warning('SchedulerStatusCommand: Last run timestamp not found during check.');
|
||||
return \Illuminate\Console\Command::FAILURE;
|
||||
}
|
||||
|
||||
$maxMinutes = (int) $this->option('max-minutes');
|
||||
if (Carbon::now()->getTimestamp() - $lastRunTimestamp > $maxMinutes * 60) {
|
||||
$this->error("Scheduler last ran more than {$maxMinutes} minutes ago. Last run: " . Carbon::createFromTimestamp($lastRunTimestamp)->diffForHumans());
|
||||
Log::warning("SchedulerStatusCommand: Health check failed. Last ran more than {$maxMinutes} minutes ago.");
|
||||
return \Illuminate\Console\Command::FAILURE;
|
||||
}
|
||||
|
||||
$this->info('Scheduler is healthy. Last ran: ' . Carbon::createFromTimestamp($lastRunTimestamp)->diffForHumans());
|
||||
return \Illuminate\Console\Command::SUCCESS;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,9 @@ class Kernel extends ConsoleKernel
|
|||
{
|
||||
$schedule->command('forms:database-cleanup')->hourly();
|
||||
$schedule->command('forms:integration-events-cleanup')->daily();
|
||||
if (config('app.self_hosted')) {
|
||||
$schedule->command('app:scheduler-status --mode=record')->everyMinute();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
// Base controller
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Redis;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Throwable;
|
||||
|
||||
class HealthCheckController extends Controller
|
||||
{
|
||||
public function apiCheck(): JsonResponse
|
||||
{
|
||||
// This controller action should only be reachable if config('app.self_hosted') is true
|
||||
// due to the routing configuration.
|
||||
|
||||
$checks = [
|
||||
'database' => false,
|
||||
'redis' => false,
|
||||
];
|
||||
$overallStatus = true;
|
||||
|
||||
try {
|
||||
DB::connection()->getPdo();
|
||||
$checks['database'] = true;
|
||||
} catch (Throwable $e) {
|
||||
Log::error('Health check: Database connection failed', ['exception' => $e->getMessage()]);
|
||||
$overallStatus = false;
|
||||
}
|
||||
|
||||
try {
|
||||
Redis::ping();
|
||||
$checks['redis'] = true;
|
||||
} catch (Throwable $e) {
|
||||
Log::error('Health check: Redis connection failed', ['exception' => $e->getMessage()]);
|
||||
$overallStatus = false;
|
||||
}
|
||||
|
||||
if ($overallStatus) {
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
'dependencies' => $checks,
|
||||
]);
|
||||
}
|
||||
|
||||
return response()->json([
|
||||
'status' => 'error',
|
||||
'dependencies' => $checks,
|
||||
], 503);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,7 @@ use Illuminate\Foundation\Http\Middleware\HandlePrecognitiveRequests;
|
|||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use App\Http\Controllers\HealthCheckController;
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
@ -42,6 +43,10 @@ use Illuminate\Support\Facades\Storage;
|
|||
|
|
||||
*/
|
||||
|
||||
if (config('app.self_hosted')) {
|
||||
Route::get('/healthcheck', [HealthCheckController::class, 'apiCheck']);
|
||||
}
|
||||
|
||||
Route::group(['middleware' => 'auth:api'], function () {
|
||||
Route::post('logout', [LoginController::class, 'logout'])->name('logout');
|
||||
Route::post('update-credentials', [ProfileController::class, 'updateAdminCredentials'])->name('credentials.update');
|
||||
|
|
|
|||
|
|
@ -2,3 +2,6 @@ NUXT_PUBLIC_APP_URL=/
|
|||
NUXT_PUBLIC_API_BASE=/api
|
||||
NUXT_PRIVATE_API_BASE=http://ingress/api
|
||||
NUXT_PUBLIC_ENV=dev
|
||||
NUXT_PUBLIC_H_CAPTCHA_SITE_KEY=
|
||||
NUXT_PUBLIC_RE_CAPTCHA_SITE_KEY=
|
||||
NUXT_PUBLIC_ROOT_REDIRECT_URL=
|
||||
|
|
@ -8,7 +8,6 @@
|
|||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@codemirror/lang-html": "^6.4.9",
|
||||
"@gtm-support/vue-gtm": "^3.1.0",
|
||||
"@iconify-json/material-symbols": "^1.2.4",
|
||||
"@nuxt/ui": "^2.19.2",
|
||||
"@pinia/nuxt": "^0.11.0",
|
||||
|
|
@ -1448,32 +1447,6 @@
|
|||
"integrity": "sha512-5DGmA8FTdB2XbDeEwc/5ZXBl6UbBAyBOOLlPuBnZ/N1SwdH9Ii+cOX3tBROlDgcTXxjOYnLMVoKk9+FXAw0CJw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@gtm-support/core": {
|
||||
"version": "3.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@gtm-support/core/-/core-3.0.1.tgz",
|
||||
"integrity": "sha512-SctcoqvvAbGAgZzOb7DZ4wjbZF3ZS7Las3qIEByv6g7mzPf4E9LpRXcQzjmywYLcUx2jys7PWJAa3s5slvj/0w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@gtm-support/vue-gtm": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@gtm-support/vue-gtm/-/vue-gtm-3.1.0.tgz",
|
||||
"integrity": "sha512-kGUnCI+Z5lBeCKd7rzgU7UoFU8Q0EkJfh17SgeyAyx8cLdISqeq54BNJKZrME3WXersoigLZVJ1GLs0buYD3lA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@gtm-support/core": "^3.0.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"vue-router": ">= 4.4.1 < 5.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"vue": ">= 3.2.26 < 4.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"vue-router": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@headlessui/tailwindcss": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/tailwindcss/-/tailwindcss-0.2.2.tgz",
|
||||
|
|
|
|||
|
|
@ -25,6 +25,11 @@ services:
|
|||
# Storage settings
|
||||
FILESYSTEM_DISK: local
|
||||
LOCAL_FILESYSTEM_VISIBILITY: public
|
||||
# PHP Configuration
|
||||
PHP_MEMORY_LIMIT: "1G"
|
||||
PHP_MAX_EXECUTION_TIME: "600"
|
||||
PHP_UPLOAD_MAX_FILESIZE: "64M"
|
||||
PHP_POST_MAX_SIZE: "64M"
|
||||
# Development settings
|
||||
PHP_IDE_CONFIG: serverName=Docker
|
||||
XDEBUG_MODE: ${XDEBUG_MODE:-off}
|
||||
|
|
@ -86,6 +91,7 @@ services:
|
|||
environment:
|
||||
NGINX_HOST: localhost
|
||||
NGINX_PORT: 80
|
||||
NGINX_MAX_BODY_SIZE: 64m
|
||||
ports:
|
||||
- "80:80"
|
||||
depends_on:
|
||||
|
|
|
|||
|
|
@ -15,54 +15,75 @@ services:
|
|||
DB_USERNAME: ${DB_USERNAME:-forge}
|
||||
DB_PASSWORD: ${DB_PASSWORD:-forge}
|
||||
DB_CONNECTION: ${DB_CONNECTION:-pgsql}
|
||||
# Storage settings
|
||||
FILESYSTEM_DISK: local
|
||||
LOCAL_FILESYSTEM_VISIBILITY: public
|
||||
# PHP Configuration
|
||||
PHP_MEMORY_LIMIT: "1G"
|
||||
PHP_MAX_EXECUTION_TIME: "600"
|
||||
PHP_UPLOAD_MAX_FILESIZE: "64M"
|
||||
PHP_POST_MAX_SIZE: "64M"
|
||||
env_file:
|
||||
- ./api/.env
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
condition: service_healthy # Depend on redis being healthy too
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "php /usr/share/nginx/html/artisan about || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 15s
|
||||
retries: 3
|
||||
start_period: 60s
|
||||
|
||||
api-worker:
|
||||
<<: *api-environment
|
||||
container_name: opnform-api-worker
|
||||
command: ["php", "artisan", "queue:work"]
|
||||
volumes: *api-environment-volumes
|
||||
environment:
|
||||
<<: *api-env
|
||||
APP_ENV: production
|
||||
IS_API_WORKER: "true"
|
||||
env_file:
|
||||
- ./api/.env
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pgrep -f 'php artisan queue:work' > /dev/null || exit 1"]
|
||||
interval: 60s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 30s
|
||||
|
||||
api-scheduler:
|
||||
<<: *api-environment
|
||||
container_name: opnform-api-scheduler
|
||||
command: ["php", "artisan", "schedule:work"]
|
||||
volumes: *api-environment-volumes
|
||||
environment:
|
||||
<<: *api-env
|
||||
APP_ENV: production
|
||||
IS_API_WORKER: "true"
|
||||
# Scheduler settings
|
||||
IS_API_WORKER: "true" # This might not be strictly true for scheduler, but consistent with setup
|
||||
CONTAINER_ROLE: scheduler
|
||||
PHP_MEMORY_LIMIT: 512M
|
||||
PHP_MAX_EXECUTION_TIME: 60
|
||||
env_file:
|
||||
- ./api/.env
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "php /usr/share/nginx/html/artisan app:scheduler-status --mode=check --max-minutes=3 || exit 1"]
|
||||
interval: 60s
|
||||
timeout: 30s
|
||||
retries: 3
|
||||
start_period: 70s # Allow time for first scheduled run and cache write
|
||||
|
||||
ui:
|
||||
image: jhumanj/opnform-client:latest
|
||||
container_name: opnform-client
|
||||
env_file:
|
||||
- ./client/.env
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "wget --spider -q http://localhost:3000/login || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 10s
|
||||
retries: 3
|
||||
start_period: 45s
|
||||
|
||||
redis:
|
||||
image: redis:7
|
||||
container_name: opnform-redis
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "redis-cli ping | grep PONG"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
|
||||
db:
|
||||
image: postgres:16
|
||||
|
|
@ -73,9 +94,8 @@ services:
|
|||
POSTGRES_PASSWORD: ${DB_PASSWORD:-forge}
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "pg_isready -U ${DB_USERNAME:-forge}"]
|
||||
interval: 5s
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
volumes:
|
||||
- postgres-data:/var/lib/postgresql/data
|
||||
|
||||
|
|
@ -86,11 +106,19 @@ services:
|
|||
- ./docker/nginx.conf:/etc/nginx/templates/default.conf.template
|
||||
ports:
|
||||
- 80:80
|
||||
environment:
|
||||
- NGINX_MAX_BODY_SIZE=64m
|
||||
depends_on:
|
||||
api:
|
||||
condition: service_started
|
||||
ui:
|
||||
condition: service_started
|
||||
healthcheck:
|
||||
test: ["CMD-SHELL", "nginx -t && curl -f http://localhost/ || exit 1"]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 3
|
||||
start_period: 10s
|
||||
|
||||
volumes:
|
||||
postgres-data:
|
||||
|
|
|
|||
|
|
@ -1,54 +1,64 @@
|
|||
FROM php:8.3-fpm
|
||||
# Build stage - using PHP CLI image with extensions
|
||||
FROM php:8.3-cli AS builder
|
||||
|
||||
# Install system dependencies
|
||||
# Install composer and extensions needed for dependency installation
|
||||
RUN apt-get update && apt-get install -y \
|
||||
libzip-dev \
|
||||
libpng-dev \
|
||||
postgresql-client \
|
||||
libpq-dev \
|
||||
unzip \
|
||||
gcc \
|
||||
make \
|
||||
autoconf \
|
||||
libc-dev \
|
||||
pkg-config \
|
||||
&& apt-get clean \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
git \
|
||||
&& docker-php-ext-install -j$(nproc) \
|
||||
bcmath \
|
||||
gd \
|
||||
zip \
|
||||
&& curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer
|
||||
|
||||
# Install composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
|
||||
ENV COMPOSER_ALLOW_SUPERUSER=1
|
||||
WORKDIR /app
|
||||
|
||||
# Install PHP extensions
|
||||
ENV CFLAGS="-O1 -D_GNU_SOURCE" \
|
||||
CXXFLAGS="-O1 -D_GNU_SOURCE"
|
||||
# Copy the entire source code for proper installation
|
||||
COPY api/ .
|
||||
|
||||
# Install basic extensions first
|
||||
RUN set -eux; \
|
||||
docker-php-ext-install -j$(nproc) pdo zip && \
|
||||
php -m | grep -E 'pdo|zip'
|
||||
# Install dependencies including dev dependencies
|
||||
RUN composer install --optimize-autoloader --no-interaction \
|
||||
--ignore-platform-req=php \
|
||||
--ignore-platform-req=ext-bcmath \
|
||||
--ignore-platform-req=ext-gd
|
||||
|
||||
# Install GD
|
||||
RUN set -eux; \
|
||||
docker-php-ext-install -j$(nproc) gd && \
|
||||
php -m | grep -E 'gd'
|
||||
# Final stage - smaller runtime image
|
||||
FROM php:8.3-fpm-alpine
|
||||
|
||||
# Install PostgreSQL related extensions
|
||||
RUN set -eux; \
|
||||
docker-php-ext-configure pgsql && \
|
||||
docker-php-ext-install -j$(nproc) pgsql pdo_pgsql && \
|
||||
php -m | grep -E 'pgsql|pdo_pgsql'
|
||||
# Install runtime dependencies
|
||||
RUN apk add --no-cache \
|
||||
libzip \
|
||||
libpng \
|
||||
postgresql-client \
|
||||
libpq \
|
||||
procps \
|
||||
unzip \
|
||||
bash \
|
||||
icu-libs \
|
||||
&& rm -rf /var/cache/apk/*
|
||||
|
||||
# Install bcmath with optimization flags for stability
|
||||
RUN set -eux; \
|
||||
docker-php-ext-install -j1 bcmath && \
|
||||
php -m | grep -E 'bcmath'
|
||||
|
||||
# Install Redis extension
|
||||
RUN set -eux; \
|
||||
pecl install -f --configureoptions 'enable-redis-igbinary="no" enable-redis-lzf="no" enable-redis-zstd="no" enable-redis-msgpack="no" enable-redis-lz4="no"' redis && \
|
||||
docker-php-ext-enable redis && \
|
||||
php -m | grep -E 'redis'
|
||||
# Install build dependencies and PHP extensions
|
||||
RUN apk add --no-cache --virtual .build-deps \
|
||||
$PHPIZE_DEPS \
|
||||
libzip-dev \
|
||||
libpng-dev \
|
||||
postgresql-dev \
|
||||
oniguruma-dev \
|
||||
icu-dev \
|
||||
&& docker-php-ext-configure pgsql \
|
||||
&& docker-php-ext-configure gd \
|
||||
&& docker-php-ext-install -j$(nproc) \
|
||||
pdo \
|
||||
zip \
|
||||
gd \
|
||||
pgsql \
|
||||
pdo_pgsql \
|
||||
bcmath \
|
||||
&& pecl install redis \
|
||||
&& docker-php-ext-enable redis \
|
||||
&& apk del .build-deps
|
||||
|
||||
WORKDIR /usr/share/nginx/html/
|
||||
|
||||
|
|
@ -62,26 +72,8 @@ RUN mkdir -p storage/framework/sessions \
|
|||
&& chown -R www-data:www-data storage bootstrap/cache \
|
||||
&& chmod -R 775 storage bootstrap/cache
|
||||
|
||||
# Copy composer files and helpers.php first
|
||||
COPY api/composer.json api/composer.lock ./
|
||||
COPY api/app/helpers.php ./app/helpers.php
|
||||
|
||||
# Default to production settings unless overridden during build
|
||||
ARG APP_ENV=production
|
||||
ARG COMPOSER_FLAGS=--no-dev --optimize-autoloader --no-interaction
|
||||
|
||||
# Install dependencies without running scripts
|
||||
RUN composer install ${COMPOSER_FLAGS} --no-scripts --ignore-platform-req=php
|
||||
|
||||
# Copy the rest of the application
|
||||
COPY api/ .
|
||||
|
||||
# Run composer scripts and clear cache
|
||||
RUN composer dump-autoload -o \
|
||||
&& php artisan package:discover --ansi \
|
||||
&& composer clear-cache \
|
||||
&& chmod -R 775 storage \
|
||||
&& chown -R www-data:www-data storage
|
||||
# Copy the entire application from the builder stage
|
||||
COPY --from=builder /app/ ./
|
||||
|
||||
# Setup entrypoint
|
||||
COPY docker/php-fpm-entrypoint /usr/local/bin/opnform-entrypoint
|
||||
|
|
|
|||
|
|
@ -20,8 +20,15 @@ RUN npm cache clean --force && \
|
|||
# RUN npm install esbuild@0.21.5
|
||||
|
||||
ADD ./client/ /app/
|
||||
# Increase Node memory limit to prevent out of memory error during build
|
||||
RUN NODE_OPTIONS="--max-old-space-size=4096" npm run build
|
||||
|
||||
# Optimize memory usage during build
|
||||
ENV NODE_OPTIONS="--max-old-space-size=4096"
|
||||
# Set production mode to reduce memory usage
|
||||
ENV NODE_ENV=production
|
||||
# Disable source maps to reduce memory usage
|
||||
ENV GENERATE_SOURCEMAP=false
|
||||
# Run the build
|
||||
RUN npm run build
|
||||
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ server {
|
|||
listen 80;
|
||||
server_name opnform;
|
||||
root /usr/share/nginx/html/public;
|
||||
client_max_body_size ${NGINX_MAX_BODY_SIZE};
|
||||
|
||||
access_log /dev/stdout;
|
||||
error_log /dev/stderr error;
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ server {
|
|||
listen 80;
|
||||
server_name localhost;
|
||||
root /usr/share/nginx/html/public;
|
||||
client_max_body_size ${NGINX_MAX_BODY_SIZE};
|
||||
|
||||
access_log /dev/stdout;
|
||||
error_log /dev/stderr error;
|
||||
|
|
|
|||
|
|
@ -3,42 +3,82 @@
|
|||
main() {
|
||||
if [ "$IS_API_WORKER" = "true" ]; then
|
||||
# This is the API worker, skip setup and just run the command
|
||||
apply_php_configuration
|
||||
exec "$@"
|
||||
else
|
||||
# This is the API service, run full setup
|
||||
# This is the API service or scheduler, run full setup
|
||||
apply_php_configuration
|
||||
prep_file_permissions
|
||||
prep_storage
|
||||
wait_for_db
|
||||
apply_db_migrations
|
||||
run_init_project
|
||||
optimize_application
|
||||
|
||||
if [ "$CONTAINER_ROLE" = "scheduler" ]; then
|
||||
echo "Initializing scheduler status for first run (entrypoint)"
|
||||
./artisan app:scheduler-status --mode=record
|
||||
fi
|
||||
|
||||
run_server "$@"
|
||||
fi
|
||||
}
|
||||
|
||||
apply_php_configuration() {
|
||||
echo "Applying PHP configuration from environment variables"
|
||||
|
||||
# Create custom PHP config file
|
||||
PHP_CUSTOM_CONFIG_FILE="/usr/local/etc/php/conf.d/99-custom.ini"
|
||||
|
||||
# Apply memory limit if provided
|
||||
if [ -n "$PHP_MEMORY_LIMIT" ]; then
|
||||
echo "memory_limit = $PHP_MEMORY_LIMIT" >> $PHP_CUSTOM_CONFIG_FILE
|
||||
fi
|
||||
|
||||
# Apply max execution time if provided
|
||||
if [ -n "$PHP_MAX_EXECUTION_TIME" ]; then
|
||||
echo "max_execution_time = $PHP_MAX_EXECUTION_TIME" >> $PHP_CUSTOM_CONFIG_FILE
|
||||
fi
|
||||
|
||||
# Apply upload max filesize if provided
|
||||
if [ -n "$PHP_UPLOAD_MAX_FILESIZE" ]; then
|
||||
echo "upload_max_filesize = $PHP_UPLOAD_MAX_FILESIZE" >> $PHP_CUSTOM_CONFIG_FILE
|
||||
fi
|
||||
|
||||
# Apply post max size if provided
|
||||
if [ -n "$PHP_POST_MAX_SIZE" ]; then
|
||||
echo "post_max_size = $PHP_POST_MAX_SIZE" >> $PHP_CUSTOM_CONFIG_FILE
|
||||
fi
|
||||
|
||||
# Log applied configuration
|
||||
echo "Applied PHP configuration:"
|
||||
cat $PHP_CUSTOM_CONFIG_FILE
|
||||
}
|
||||
|
||||
prep_file_permissions() {
|
||||
chmod a+x ./artisan
|
||||
}
|
||||
|
||||
prep_storage() {
|
||||
# Create Laravel-specific directories
|
||||
mkdir -p /persist/storage/framework/cache/data
|
||||
mkdir -p /persist/storage/framework/sessions
|
||||
mkdir -p /persist/storage/framework/views
|
||||
local app_storage_path="/usr/share/nginx/html/storage"
|
||||
|
||||
# Create Laravel-specific directories directly in the mounted volume
|
||||
mkdir -p "$app_storage_path/app/public"
|
||||
mkdir -p "$app_storage_path/framework/cache/data"
|
||||
mkdir -p "$app_storage_path/framework/sessions"
|
||||
mkdir -p "$app_storage_path/framework/views"
|
||||
mkdir -p "$app_storage_path/logs"
|
||||
|
||||
# Set permissions for the entire storage directory
|
||||
chown -R www-data:www-data /persist/storage
|
||||
chmod -R 775 /persist/storage
|
||||
|
||||
# Create symlink to the correct storage location
|
||||
ln -sf /persist/storage /usr/share/nginx/html/storage
|
||||
chown -R www-data:www-data "$app_storage_path"
|
||||
chmod -R 775 "$app_storage_path"
|
||||
|
||||
touch /var/log/opnform.log
|
||||
chown www-data /var/log/opnform.log
|
||||
|
||||
# Ensure proper permissions for the storage directory
|
||||
chown -R www-data:www-data /usr/share/nginx/html/storage
|
||||
chmod -R 775 /usr/share/nginx/html/storage
|
||||
# Run Laravel's storage link command (ensure script is run from app root or adjust path to artisan)
|
||||
echo "Creating public storage symlink"
|
||||
./artisan storage:link
|
||||
}
|
||||
|
||||
wait_for_db() {
|
||||
|
|
|
|||
|
|
@ -1,15 +0,0 @@
|
|||
[www]
|
||||
user = www-data
|
||||
group = www-data
|
||||
listen = 9000
|
||||
|
||||
pm = dynamic
|
||||
pm.max_children = 20
|
||||
pm.start_servers = 2
|
||||
pm.min_spare_servers = 1
|
||||
pm.max_spare_servers = 3
|
||||
pm.max_requests = 1000
|
||||
|
||||
clear_env = no
|
||||
catch_workers_output = yes
|
||||
decorate_workers_output = no
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
; PHP Configuration
|
||||
memory_limit = 512M
|
||||
max_execution_time = 60
|
||||
upload_max_filesize = 64M
|
||||
post_max_size = 64M
|
||||
max_input_vars = 3000
|
||||
|
||||
; Error reporting
|
||||
error_reporting = E_ALL
|
||||
display_errors = Off
|
||||
display_startup_errors = Off
|
||||
log_errors = On
|
||||
error_log = /dev/stderr
|
||||
|
||||
; Date
|
||||
date.timezone = UTC
|
||||
|
||||
; Session
|
||||
session.save_handler = redis
|
||||
session.save_path = "tcp://redis:6379"
|
||||
|
||||
; OpCache
|
||||
opcache.enable=1
|
||||
opcache.memory_consumption=256
|
||||
opcache.max_accelerated_files=20000
|
||||
opcache.validate_timestamps=1
|
||||
opcache.revalidate_freq=0
|
||||
Loading…
Reference in New Issue