From 88912ee8e35b40c960c5bd123b79865f37cbaf88 Mon Sep 17 00:00:00 2001 From: Frank John Begornia Date: Tue, 12 Aug 2025 00:28:17 +0800 Subject: [PATCH] Add Docker and Nginx configuration for Cloud Run deployment --- .dockerignore | 18 +++++++ Dockerfile | 101 +++++++++++++++++++++++------------ cloudrun/Dockerfile.cloudrun | 72 +++++++++++++++++++++++++ cloudrun/nginx.conf | 42 +++++++++++++++ cloudrun/supervisord.conf | 24 +++++++++ docker-compose.yml | 43 ++++++++++++--- nginx/conf.d/app.conf | 29 ++++++++-- run.sh | 68 +++++++++++++++++++++++ 8 files changed, 353 insertions(+), 44 deletions(-) create mode 100644 .dockerignore create mode 100644 cloudrun/Dockerfile.cloudrun create mode 100644 cloudrun/nginx.conf create mode 100644 cloudrun/supervisord.conf create mode 100755 run.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..26a1e0b --- /dev/null +++ b/.dockerignore @@ -0,0 +1,18 @@ +.git +.gitignore +.env +.env.* +.editorconfig +.idea +.vscode +*.md +docker-compose.yml +Dockerfile +vendor/ +node_modules/ +storage/framework/cache/** +storage/framework/sessions/** +storage/framework/views/** +storage/logs/** +tests/ +phpunit.xml diff --git a/Dockerfile b/Dockerfile index 8a08879..04afe3d 100755 --- a/Dockerfile +++ b/Dockerfile @@ -1,46 +1,81 @@ -# Use the official PHP image based on Alpine Linux -FROM php:5.6-fpm-alpine +# Build stage +FROM php:5.6-fpm-alpine as composer -# Install system dependencies and PHP extensions -RUN apk --update --no-cache add \ - nginx \ - libpng-dev \ - libjpeg-turbo-dev \ - freetype-dev \ - libzip-dev \ +# Install system dependencies and PHP extensions required for Composer +RUN apk add --no-cache \ + git \ + curl \ zip \ unzip \ + libmcrypt \ libmcrypt-dev \ - && docker-php-ext-configure gd --with-freetype --with-jpeg \ - && docker-php-ext-install gd pdo pdo_mysql zip mcrypt + zlib-dev \ + libzip-dev \ + autoconf \ + make \ + gcc \ + g++ \ + && docker-php-ext-install mcrypt mbstring zip \ + && curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ + && composer self-update --1 + +ENV COMPOSER_ALLOW_SUPERUSER=1 +WORKDIR /app +COPY composer.* ./ + +# Install dependencies with Composer (optimize later after full copy) +RUN composer config platform.php 5.6.40 \ + && composer install --prefer-dist --no-dev --no-scripts --no-autoloader + +# Copy the rest of the application and optimize autoload +COPY . . +RUN composer dump-autoload --optimize --no-dev --classmap-authoritative + +# Production stage +FROM php:5.6-fpm-alpine + +# Install runtime dependencies & build PHP extensions +RUN apk add --no-cache \ + nginx \ + curl \ + libpng \ + libjpeg-turbo \ + freetype \ + libzip \ + libmcrypt \ + zlib \ + && apk add --no-cache --virtual .build-deps \ + autoconf make gcc g++ \ + libpng-dev libjpeg-turbo-dev freetype-dev libzip-dev libmcrypt-dev zlib-dev \ + && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ + && docker-php-ext-install -j"$(nproc)" gd pdo pdo_mysql zip mcrypt mbstring opcache \ + && docker-php-ext-enable mcrypt \ + && apk del .build-deps \ + && php -m | grep -i mcrypt + +# Configure PHP for production +RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" \ + && echo "opcache.enable=1" >> /usr/local/etc/php/conf.d/opcache.ini \ + && echo "opcache.memory_consumption=128" >> /usr/local/etc/php/conf.d/opcache.ini \ + && echo "opcache.interned_strings_buffer=8" >> /usr/local/etc/php/conf.d/opcache.ini \ + && echo "opcache.max_accelerated_files=4000" >> /usr/local/etc/php/conf.d/opcache.ini \ + && echo "opcache.revalidate_freq=60" >> /usr/local/etc/php/conf.d/opcache.ini \ + && echo "opcache.fast_shutdown=1" >> /usr/local/etc/php/conf.d/opcache.ini -# Set the working directory in the container WORKDIR /var/www -# Clear cache -# RUN apt-get clean && rm -rf /var/lib/apt/lists/* - -# Copy the Laravel application files to the container +# Copy vendor & app code from build stage +COPY --from=composer /app/vendor ./vendor COPY . . -# Set appropriate permissions for Laravel storage and bootstrap cache -RUN chown -R www-data:www-data storage bootstrap +# Set appropriate permissions and create required directories +RUN chown -R www-data:www-data storage bootstrap \ + && mkdir -p /run/php \ + && chown www-data:www-data /run/php \ + && php artisan key:generate || true -# Install Composer -RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer +# Healthcheck (FPM listens on 9000; adjust as needed if behind nginx) +HEALTHCHECK --interval=30s --timeout=5s CMD php -m > /dev/null || exit 1 -# Install Laravel dependencies -RUN composer install --no-plugins --no-scripts - -# Generate Laravel application key -RUN php artisan key:generate - -# Create directory for the socket and set permissions -RUN mkdir -p /run/php && chown www-data:www-data /run/php - -# Copy the www.conf file to PHP-FPM pool.d directory -# COPY www.conf /usr/local/etc/php-fpm.d/www.conf - -# Expose port 9000 and start php-fpm server EXPOSE 9000 CMD ["php-fpm"] \ No newline at end of file diff --git a/cloudrun/Dockerfile.cloudrun b/cloudrun/Dockerfile.cloudrun new file mode 100644 index 0000000..46a2b00 --- /dev/null +++ b/cloudrun/Dockerfile.cloudrun @@ -0,0 +1,72 @@ +# Combined image for Cloud Run: nginx + php-fpm (PHP 5.6) for legacy Laravel 5 +# NOTE: PHP 5.6 is EOL; use only for legacy maintenance. Consider upgrading. + +FROM php:5.6-fpm + +# Set build args/env +ARG APP_ENV=production +ENV APP_ENV=${APP_ENV} \ + APP_DEBUG=false \ + OPCACHE_VALIDATE_TIMESTAMPS=0 \ + COMPOSER_ALLOW_SUPERUSER=1 \ + PORT=8080 \ + PATH="/var/www/artisan:$PATH" + +WORKDIR /var/www + +# Install system deps (Debian variant easier than alpine for mixed services) +RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends \ + git curl unzip zip supervisor nginx \ + libmcrypt4 libmcrypt-dev \ + libpng-dev libjpeg62-turbo-dev libfreetype6-dev libzip-dev zlib1g-dev \ + && docker-php-ext-configure gd --with-freetype-dir=/usr/include/ --with-jpeg-dir=/usr/include/ \ + && docker-php-ext-install gd mcrypt mbstring pdo pdo_mysql zip opcache \ + && rm -rf /var/lib/apt/lists/* + +# Install Composer (v1) +RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local/bin --filename=composer \ + && composer self-update --1 + +# Copy composer files & install deps first (cache layer) +COPY composer.* ./ +RUN composer config platform.php 5.6.40 \ + && composer install --no-dev --no-scripts --no-autoloader --prefer-dist + +# Copy application code +COPY . . +RUN composer dump-autoload --optimize --no-dev --classmap-authoritative || true + +# Nginx config +COPY cloudrun/nginx.conf /etc/nginx/nginx.conf + +# Supervisord config +COPY cloudrun/supervisord.conf /etc/supervisor/conf.d/supervisord.conf + +# Remove default nginx site configs if present +RUN rm -f /etc/nginx/sites-enabled/default /etc/nginx/conf.d/default.conf || true + +# Create runtime dirs +RUN mkdir -p /run/php /var/log/supervisor /var/www/storage /var/www/bootstrap/cache \ + && chown -R www-data:www-data /var/www/storage /var/www/bootstrap/cache + +# PHP production ini tweaks +RUN { \ + echo "opcache.enable=1"; \ + echo "opcache.memory_consumption=128"; \ + echo "opcache.interned_strings_buffer=8"; \ + echo "opcache.max_accelerated_files=4000"; \ + echo "opcache.revalidate_freq=60"; \ + echo "opcache.fast_shutdown=1"; \ + echo "date.timezone=UTC"; \ +} > /usr/local/etc/php/conf.d/zz-custom.ini + +# Generate app key if missing (non-fatal if artisan fails early) +RUN php artisan key:generate || true + +# Cloud Run listens on $PORT +EXPOSE 8080 + +# Health check path suggestion: /healthz (configure in Cloud Run if desired) + +# Start supervisor (manages php-fpm + nginx) +CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] diff --git a/cloudrun/nginx.conf b/cloudrun/nginx.conf new file mode 100644 index 0000000..dc941f6 --- /dev/null +++ b/cloudrun/nginx.conf @@ -0,0 +1,42 @@ +user nginx; +worker_processes auto; +error_log /dev/stderr warn; +pid /var/run/nginx.pid; + +events { worker_connections 1024; } + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + access_log /dev/stdout main; + sendfile on; + keepalive_timeout 65; + server_tokens off; + + server { + listen 8080 default_server; + listen [::]:8080 default_server; + root /var/www/public; + index index.php index.html; + + location /healthz { return 200 'ok'; add_header Content-Type text/plain; } + + location / { + try_files $uri $uri/ /index.php?$query_string; + } + + location ~ \.php$ { + include fastcgi_params; + fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; + fastcgi_pass 127.0.0.1:9000; # php-fpm + fastcgi_index index.php; + fastcgi_buffers 16 16k; + fastcgi_buffer_size 32k; + } + + location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ { + expires 30d; + access_log off; + } + } +} diff --git a/cloudrun/supervisord.conf b/cloudrun/supervisord.conf new file mode 100644 index 0000000..7c7b00f --- /dev/null +++ b/cloudrun/supervisord.conf @@ -0,0 +1,24 @@ +[supervisord] +nodaemon=true +logfile=/dev/stdout +logfile_maxbytes=0 + +[program:php-fpm] +command=/usr/sbin/php-fpm5.6 -F +priority=10 +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 + +[program:nginx] +command=/usr/sbin/nginx -g 'daemon off;' +priority=20 +autostart=true +autorestart=true +stdout_logfile=/dev/stdout +stdout_logfile_maxbytes=0 +stderr_logfile=/dev/stderr +stderr_logfile_maxbytes=0 diff --git a/docker-compose.yml b/docker-compose.yml index 1107cbf..9f3361c 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,4 +1,3 @@ -version: '3' services: #PHP Service @@ -6,34 +5,64 @@ services: build: context: . dockerfile: Dockerfile + args: + - APP_ENV=production image: digitalocean.com/php container_name: app restart: unless-stopped - tty: true + deploy: + resources: + limits: + cpus: '0.50' + memory: 512M + reservations: + cpus: '0.25' + memory: 256M environment: - SERVICE_NAME: app - SERVICE_TAGS: dev + APP_ENV: production + APP_DEBUG: 'false' + PHP_OPCACHE_VALIDATE_TIMESTAMPS: 0 working_dir: /var/www volumes: - - ./:/var/www + - ./storage:/var/www/storage - ./php/local.ini:/usr/local/etc/php/conf.d/local.ini networks: - app-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:9000/"] + interval: 30s + timeout: 3s + retries: 3 #Nginx Service webserver: image: nginx:alpine container_name: webserver restart: unless-stopped - tty: true + deploy: + resources: + limits: + cpus: '0.30' + memory: 256M + reservations: + cpus: '0.10' + memory: 128M ports: - "10003:80" - "10443:443" volumes: - - ./:/var/www + - ./public:/var/www/public - ./nginx/conf.d/:/etc/nginx/conf.d/ networks: - app-network + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:80/"] + interval: 30s + timeout: 3s + retries: 3 + depends_on: + app: + condition: service_healthy #Docker Networks networks: diff --git a/nginx/conf.d/app.conf b/nginx/conf.d/app.conf index 501fb8a..b6fc252 100755 --- a/nginx/conf.d/app.conf +++ b/nginx/conf.d/app.conf @@ -1,9 +1,30 @@ server { listen 80; - index index.php index.html; - error_log /var/log/nginx/error.log; - access_log /var/log/nginx/access.log; - root /var/www; + server_tokens off; + + # Compression + gzip on; + gzip_comp_level 5; + gzip_min_length 256; + gzip_proxied any; + gzip_vary on; + gzip_types + application/javascript + application/json + application/x-javascript + application/xml + text/css + text/javascript + text/plain + text/xml; + + client_max_body_size 100M; + fastcgi_read_timeout 1800; + + error_log /var/log/nginx/error.log warn; + access_log /var/log/nginx/access.log combined buffer=512k flush=1m; + + root /var/www/public; index index.php index.html; location / { diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..74358bd --- /dev/null +++ b/run.sh @@ -0,0 +1,68 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Helper script for docker compose workflow +# Usage examples: +# ./run.sh up # build (if needed) and start in background +# ./run.sh rebuild # force full rebuild (no cache) and start +# ./run.sh down # stop and remove containers +# ./run.sh logs # follow logs +# ./run.sh artisan migrate +# ./run.sh sh # shell into PHP app container +# ./run.sh restart # restart containers +# ./run.sh build # build images (uses cache) + +CMD=${1:-up} +shift || true + +case "$CMD" in + build) + echo "[build] Building images (with cache)..." + docker compose build + ;; + up) + echo "[up] Starting services (build if needed)..." + docker compose up -d --build + ;; + rebuild) + echo "[rebuild] Full rebuild without cache..." + docker compose down || true + docker compose build --no-cache + docker compose up -d + ;; + down) + echo "[down] Stopping and removing services..." + docker compose down + ;; + restart) + echo "[restart] Restarting services..." + docker compose restart + ;; + logs) + echo "[logs] Following logs (Ctrl+C to exit)..." + docker compose logs -f --tail=200 "$@" + ;; + artisan) + echo "[artisan] php artisan $*" + docker compose exec app php artisan "$@" + ;; + sh) + echo "[sh] Opening shell in app container..." + docker compose exec app sh + ;; + *) + cat < [args] +Commands: + up Build (if needed) and start containers + build Build images using cache + rebuild Rebuild images without cache then start + down Stop and remove containers + restart Restart running containers + logs Follow logs (pass service names optionally) + artisan Run php artisan + sh Shell into app container +EOF + exit 1 + ;; + esac