Add docker production files
This commit is contained in:
parent
0f041983dd
commit
ace0db0446
5 changed files with 343 additions and 0 deletions
85
docker/production/Dockerfile
Normal file
85
docker/production/Dockerfile
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
# Multi-stage build for FFR Laravel application
|
||||
FROM node:20 AS frontend-builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install Node dependencies
|
||||
RUN npm ci
|
||||
|
||||
# Copy frontend source
|
||||
COPY resources/ resources/
|
||||
COPY public/ public/
|
||||
COPY vite.config.js ./
|
||||
COPY tsconfig.json ./
|
||||
|
||||
# Build frontend assets
|
||||
RUN npm run build
|
||||
|
||||
# PHP runtime stage
|
||||
FROM php:8.4-fpm-alpine
|
||||
|
||||
# Install system dependencies
|
||||
RUN apk add --no-cache \
|
||||
git \
|
||||
curl \
|
||||
libpng-dev \
|
||||
libxml2-dev \
|
||||
zip \
|
||||
unzip \
|
||||
oniguruma-dev \
|
||||
mysql-client \
|
||||
nginx \
|
||||
supervisor \
|
||||
autoconf \
|
||||
gcc \
|
||||
g++ \
|
||||
make
|
||||
|
||||
# Install PHP extensions
|
||||
RUN docker-php-ext-install \
|
||||
pdo_mysql \
|
||||
mbstring \
|
||||
exif \
|
||||
pcntl \
|
||||
bcmath \
|
||||
gd \
|
||||
&& pecl install redis \
|
||||
&& docker-php-ext-enable redis
|
||||
|
||||
# Install Composer
|
||||
COPY --from=composer:latest /usr/bin/composer /usr/bin/composer
|
||||
|
||||
# Set working directory
|
||||
WORKDIR /var/www/html
|
||||
|
||||
# Copy application code first
|
||||
COPY . .
|
||||
|
||||
# Install PHP dependencies after copying all files
|
||||
RUN composer install --no-dev --optimize-autoloader --no-interaction
|
||||
|
||||
# Copy built frontend assets from builder stage
|
||||
COPY --from=frontend-builder /app/public/build/ ./public/build/
|
||||
|
||||
# Copy nginx and supervisor configurations
|
||||
COPY docker/production/nginx.conf /etc/nginx/http.d/default.conf
|
||||
COPY docker/production/supervisord.conf /etc/supervisord.conf
|
||||
COPY docker/production/start-app.sh /usr/local/bin/start-app
|
||||
|
||||
# Set proper permissions
|
||||
RUN chown -R www-data:www-data storage bootstrap/cache public/build \
|
||||
&& chmod -R 755 storage bootstrap/cache \
|
||||
&& chmod +x /usr/local/bin/start-app
|
||||
|
||||
# Expose port 80 for nginx
|
||||
EXPOSE 80
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
||||
CMD php artisan --version || exit 1
|
||||
|
||||
# Start the application
|
||||
CMD ["/usr/local/bin/start-app"]
|
||||
130
docker/production/docker-compose.yml
Normal file
130
docker/production/docker-compose.yml
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
services:
|
||||
app:
|
||||
image: codeberg.org/lvl0/ffr:latest
|
||||
# build:
|
||||
# context: ../..
|
||||
# dockerfile: docker/production/Dockerfile
|
||||
container_name: ffr-app
|
||||
restart: unless-stopped
|
||||
working_dir: /var/www/html
|
||||
environment:
|
||||
- APP_ENV=production
|
||||
- APP_DEBUG=false
|
||||
- DB_CONNECTION=mysql
|
||||
- DB_HOST=db
|
||||
- DB_PORT=3306
|
||||
- DB_DATABASE=ffr
|
||||
- DB_USERNAME=ffr_user
|
||||
- DB_PASSWORD=ffr_password
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- CACHE_DRIVER=redis
|
||||
- SESSION_DRIVER=redis
|
||||
- QUEUE_CONNECTION=redis
|
||||
volumes: []
|
||||
ports:
|
||||
- "8000:80"
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
networks:
|
||||
- ffr-network
|
||||
|
||||
queue:
|
||||
image: codeberg.org/lvl0/ffr:latest
|
||||
container_name: ffr-queue
|
||||
restart: unless-stopped
|
||||
working_dir: /var/www/html
|
||||
environment:
|
||||
- APP_ENV=production
|
||||
- APP_DEBUG=false
|
||||
- DB_CONNECTION=mysql
|
||||
- DB_HOST=db
|
||||
- DB_PORT=3306
|
||||
- DB_DATABASE=ffr
|
||||
- DB_USERNAME=ffr_user
|
||||
- DB_PASSWORD=ffr_password
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- CACHE_DRIVER=redis
|
||||
- SESSION_DRIVER=redis
|
||||
- QUEUE_CONNECTION=redis
|
||||
command: ["php", "artisan", "horizon"]
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
networks:
|
||||
- ffr-network
|
||||
|
||||
scheduler:
|
||||
image: codeberg.org/lvl0/ffr:latest
|
||||
container_name: ffr-scheduler
|
||||
restart: unless-stopped
|
||||
working_dir: /var/www/html
|
||||
environment:
|
||||
- APP_ENV=production
|
||||
- APP_DEBUG=false
|
||||
- DB_CONNECTION=mysql
|
||||
- DB_HOST=db
|
||||
- DB_PORT=3306
|
||||
- DB_DATABASE=ffr
|
||||
- DB_USERNAME=ffr_user
|
||||
- DB_PASSWORD=ffr_password
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- CACHE_DRIVER=redis
|
||||
- SESSION_DRIVER=redis
|
||||
- QUEUE_CONNECTION=redis
|
||||
command: ["sh", "-c", "while true; do php artisan schedule:run --verbose --no-interaction; sleep 60; done"]
|
||||
depends_on:
|
||||
db:
|
||||
condition: service_healthy
|
||||
redis:
|
||||
condition: service_started
|
||||
networks:
|
||||
- ffr-network
|
||||
|
||||
db:
|
||||
image: docker.io/library/mysql:8.0
|
||||
container_name: ffr-db
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
- MYSQL_DATABASE=ffr
|
||||
- MYSQL_USER=ffr_user
|
||||
- MYSQL_PASSWORD=ffr_password
|
||||
- MYSQL_ROOT_PASSWORD=root_password
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
ports:
|
||||
- "3306:3306"
|
||||
healthcheck:
|
||||
test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "ffr_user", "-pffr_password"]
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
interval: 3s
|
||||
start_period: 30s
|
||||
networks:
|
||||
- ffr-network
|
||||
|
||||
redis:
|
||||
image: docker.io/library/redis:7-alpine
|
||||
container_name: ffr-redis
|
||||
restart: unless-stopped
|
||||
volumes:
|
||||
- redis_data:/data
|
||||
networks:
|
||||
- ffr-network
|
||||
|
||||
networks:
|
||||
ffr-network:
|
||||
driver: bridge
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
driver: local
|
||||
redis_data:
|
||||
driver: local
|
||||
69
docker/production/nginx.conf
Normal file
69
docker/production/nginx.conf
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
server {
|
||||
listen 80;
|
||||
server_name localhost;
|
||||
root /var/www/html/public;
|
||||
index index.php index.html;
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN";
|
||||
add_header X-Content-Type-Options "nosniff";
|
||||
add_header X-XSS-Protection "1; mode=block";
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location ~ \.php$ {
|
||||
fastcgi_pass 127.0.0.1:9000;
|
||||
fastcgi_index index.php;
|
||||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||
include fastcgi_params;
|
||||
|
||||
# Increase timeouts for long-running requests
|
||||
fastcgi_read_timeout 300;
|
||||
fastcgi_send_timeout 300;
|
||||
}
|
||||
|
||||
# Deny access to hidden files
|
||||
location ~ /\.ht {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# Deny access to sensitive files
|
||||
location ~ /\.(env|git) {
|
||||
deny all;
|
||||
}
|
||||
|
||||
# Static assets with far-future expiry
|
||||
location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot|map)$ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Laravel specific optimizations
|
||||
location = /favicon.ico {
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
location = /robots.txt {
|
||||
access_log off;
|
||||
log_not_found off;
|
||||
}
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_min_length 1024;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types
|
||||
text/plain
|
||||
text/css
|
||||
text/xml
|
||||
text/javascript
|
||||
application/javascript
|
||||
application/xml+rss
|
||||
application/json;
|
||||
}
|
||||
34
docker/production/start-app.sh
Normal file
34
docker/production/start-app.sh
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Create .env file if it doesn't exist
|
||||
if [ ! -f /var/www/html/.env ]; then
|
||||
cp /var/www/html/.env.example /var/www/html/.env 2>/dev/null || touch /var/www/html/.env
|
||||
fi
|
||||
|
||||
# Wait for database to be ready
|
||||
echo "Waiting for database..."
|
||||
while ! mysql -h db -u ffr_user -pffr_password --connect-timeout=2 -e "SELECT 1" >/dev/null 2>&1; do
|
||||
echo "Database not ready, waiting..."
|
||||
sleep 1
|
||||
done
|
||||
echo "Database connection established!"
|
||||
|
||||
# Generate app key if not set
|
||||
if ! grep -q "APP_KEY=base64:" /var/www/html/.env; then
|
||||
php artisan key:generate --force
|
||||
fi
|
||||
|
||||
# Laravel optimizations for production
|
||||
php artisan config:cache
|
||||
php artisan route:cache
|
||||
php artisan view:cache
|
||||
|
||||
# Run migrations
|
||||
php artisan migrate --force
|
||||
|
||||
# Run seeders (only if needed for production data)
|
||||
php artisan db:seed --force --class=PlatformInstanceSeeder
|
||||
php artisan db:seed --force --class=SettingsSeeder
|
||||
|
||||
# Start supervisor to manage nginx and php-fpm
|
||||
supervisord -c /etc/supervisord.conf
|
||||
25
docker/production/supervisord.conf
Normal file
25
docker/production/supervisord.conf
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
[supervisord]
|
||||
nodaemon=true
|
||||
user=root
|
||||
logfile=/var/log/supervisord.log
|
||||
pidfile=/var/run/supervisord.pid
|
||||
|
||||
[program:nginx]
|
||||
command=nginx -g "daemon off;"
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
priority=10
|
||||
|
||||
[program:php-fpm]
|
||||
command=php-fpm --nodaemonize
|
||||
autostart=true
|
||||
autorestart=true
|
||||
stdout_logfile=/dev/stdout
|
||||
stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
priority=10
|
||||
Loading…
Reference in a new issue