Add Docker support with Dockerfile, docker-compose, and entrypoint script; update .gitignore and add .dockerignore

This commit is contained in:
I-Fun
2026-04-01 11:44:46 +07:00
parent e09c8a5fa9
commit 1126877ef5
8 changed files with 403 additions and 0 deletions

51
.dockerignore Normal file
View File

@@ -0,0 +1,51 @@
# ── Git ──────────────────────────────────────
.git
.gitignore
.gitattributes
# ── Env files (use .env.docker instead) ─────
.env
.env.local
.env.*.local
# ── Node / build artifacts ───────────────────
node_modules
npm-debug.log*
# ── Laravel local storage (mounted as volume)
storage/logs/*
storage/framework/cache/*
storage/framework/sessions/*
storage/framework/views/*
storage/app/private/*
# ── Laravel bootstrap cache ──────────────────
bootstrap/cache/*.php
# ── SQLite database (mounted as volume) ──────
database/database.sqlite
# ── Compiled assets (rebuilt in Dockerfile) ──
public/build
public/hot
# ── Tests / dev tooling ──────────────────────
tests
phpunit.xml
.editorconfig
.php-cs-fixer*
# ── IDE / OS ─────────────────────────────────
.idea
.vscode
*.DS_Store
Thumbs.db
# ── Docker config itself ─────────────────────
Dockerfile
docker-compose*.yml
docker/
# ── Claude / project meta ────────────────────
.claude
README.md

1
.gitignore vendored
View File

@@ -3,6 +3,7 @@
.env
.env.backup
.env.production
.env.docker
.phpactor.json
.phpunit.result.cache
/.fleet

118
Dockerfile Normal file
View File

@@ -0,0 +1,118 @@
# ─────────────────────────────────────────────
# Stage 1 – Build frontend assets (Node)
# ─────────────────────────────────────────────
FROM node:22-alpine AS node-builder
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci --ignore-scripts
COPY vite.config.js ./
COPY resources/ resources/
COPY public/ public/
RUN npm run build
# ─────────────────────────────────────────────
# Stage 2 – PHP runtime
# ─────────────────────────────────────────────
FROM php:8.3-fpm-alpine AS runtime
# ── System packages ──────────────────────────
RUN apk add --no-cache \
nginx \
supervisor \
sqlite \
curl \
git \
unzip \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev \
libxml2-dev \
libzip-dev \
icu-dev \
oniguruma-dev
# ── PHP extensions ───────────────────────────
# GD (needed by dompdf for image rendering)
RUN docker-php-ext-configure gd \
--with-freetype \
--with-jpeg \
&& docker-php-ext-install -j"$(nproc)" \
pdo \
pdo_sqlite \
mbstring \
fileinfo \
xml \
dom \
bcmath \
zip \
intl \
gd \
opcache \
pcntl
# ── Composer ─────────────────────────────────
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
# ── App directory ────────────────────────────
WORKDIR /var/www/html
# Install PHP dependencies first (better layer caching)
COPY composer.json composer.lock ./
RUN composer install \
--no-dev \
--no-interaction \
--no-scripts \
--optimize-autoloader \
--prefer-dist
# Copy full application
COPY . .
# Inject compiled frontend assets from Stage 1
COPY --from=node-builder /app/public/build public/build/
# ── Docker helper files ───────────────────────
COPY docker/nginx.conf /etc/nginx/nginx.conf
COPY docker/supervisord.conf /etc/supervisord.conf
COPY docker/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
# ── File system ──────────────────────────────
RUN mkdir -p \
storage/app/private/talent-assets \
storage/logs \
storage/framework/cache \
storage/framework/sessions \
storage/framework/views \
bootstrap/cache \
database \
&& chown -R www-data:www-data \
storage \
bootstrap/cache \
database \
&& chmod -R 775 \
storage \
bootstrap/cache \
database
# ── PHP-FPM: run as www-data ─────────────────
RUN sed -i 's/user = www-data/user = www-data/' /usr/local/etc/php-fpm.d/www.conf 2>/dev/null || true \
&& sed -i 's/group = www-data/group = www-data/' /usr/local/etc/php-fpm.d/www.conf 2>/dev/null || true
# ── OPcache tuning ───────────────────────────
RUN { \
echo "opcache.enable=1"; \
echo "opcache.memory_consumption=128"; \
echo "opcache.interned_strings_buffer=8"; \
echo "opcache.max_accelerated_files=10000"; \
echo "opcache.validate_timestamps=0"; \
echo "opcache.fast_shutdown=1"; \
} > /usr/local/etc/php/conf.d/opcache.ini
EXPOSE 80
ENTRYPOINT ["/entrypoint.sh"]

6
How-To.txt Normal file
View File

@@ -0,0 +1,6 @@
git clone this repository
composer install
#php artisan key:generate
touch database/database.sqlite && php artisan migrate
npm install
npm run build

38
docker-compose.yml Normal file
View File

@@ -0,0 +1,38 @@
services:
app:
build:
context: .
dockerfile: Dockerfile
image: kol-manager:latest
container_name: kol-manager
restart: unless-stopped
ports:
- "${APP_PORT:-8080}:80"
# Mount .env.docker as the runtime .env
env_file:
- .env.docker
volumes:
# Persist the SQLite database across container rebuilds
- kol_database:/var/www/html/database
# Persist uploaded files (talent assets, etc.)
- kol_storage:/var/www/html/storage
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost/up"]
interval: 30s
timeout: 10s
retries: 3
start_period: 30s
volumes:
# Named volume for SQLite database
kol_database:
driver: local
# Named volume for all file uploads / logs / cache
kol_storage:
driver: local

59
docker/entrypoint.sh Normal file
View File

@@ -0,0 +1,59 @@
#!/bin/sh
set -e
APP_DIR=/var/www/html
echo "──────────────────────────────────────────"
echo " KOL Manager – Container Start"
echo "──────────────────────────────────────────"
cd "$APP_DIR"
# ── 1. Bootstrap .env ───────────────────────
if [ ! -f .env ]; then
if [ -f .env.docker ]; then
cp .env.docker .env
echo "[env] Copied .env.docker → .env"
else
echo "[env] WARNING: No .env or .env.docker found!"
fi
fi
# ── 2. Generate APP_KEY if empty ────────────
if grep -qE '^APP_KEY=$' .env 2>/dev/null; then
echo "[key] Generating application key…"
php artisan key:generate --force --quiet
fi
# ── 3. Ensure SQLite database file exists ───
if [ ! -f database/database.sqlite ]; then
echo "[db] Creating SQLite database file…"
touch database/database.sqlite
fi
# ── 4. Fix ownership / permissions ──────────
chown -R www-data:www-data \
storage \
bootstrap/cache \
database
chmod -R 775 \
storage \
bootstrap/cache \
database
# ── 5. Run migrations ────────────────────────
echo "[migrate] Running database migrations…"
php artisan migrate --force --no-interaction
# ── 6. Cache config / routes / views ────────
echo "[optimize] Caching configuration…"
php artisan config:cache --quiet
php artisan route:cache --quiet
php artisan view:cache --quiet
echo "──────────────────────────────────────────"
echo " All checks passed. Starting services…"
echo "──────────────────────────────────────────"
# ── 7. Hand off to Supervisor ────────────────
exec supervisord -c /etc/supervisord.conf

74
docker/nginx.conf Normal file
View File

@@ -0,0 +1,74 @@
user www-data;
worker_processes auto;
pid /run/nginx.pid;
error_log /dev/stderr warn;
events {
worker_connections 1024;
multi_accept on;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# Logging to stdout/stderr so Docker captures it
access_log /dev/stdout;
error_log /dev/stderr warn;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
# Upload size limit (matches file uploads in the app)
client_max_body_size 64M;
server {
listen 80;
server_name _;
root /var/www/html/public;
index index.php;
charset utf-8;
# Laravel health-check endpoint
location = /up {
try_files $uri /index.php?$query_string;
}
# Main catch-all → Laravel router
location / {
try_files $uri $uri/ /index.php?$query_string;
}
# PHP-FPM handler
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
# Increase timeouts for long-running requests (PDF export, uploads)
fastcgi_read_timeout 300;
fastcgi_connect_timeout 60;
fastcgi_send_timeout 300;
}
# Deny access to hidden files (.env, .git, etc.)
location ~ /\.(?!well-known).* {
deny all;
}
# Cache static assets
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|otf)$ {
expires 1y;
add_header Cache-Control "public, immutable";
try_files $uri =404;
}
}
}

56
docker/supervisord.conf Normal file
View File

@@ -0,0 +1,56 @@
[supervisord]
nodaemon=true
user=root
logfile=/dev/null
logfile_maxbytes=0
pidfile=/run/supervisord.pid
[unix_http_server]
file=/run/supervisor.sock
[supervisorctl]
serverurl=unix:///run/supervisor.sock
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
; ── Nginx ─────────────────────────────────────────────────────────────────
[program:nginx]
command=nginx -g "daemon off;"
autostart=true
autorestart=true
priority=10
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; ── PHP-FPM ───────────────────────────────────────────────────────────────
[program:php-fpm]
command=php-fpm --nodaemonize
autostart=true
autorestart=true
priority=5
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0
; ── Laravel Queue Worker ──────────────────────────────────────────────────
[program:queue]
command=php /var/www/html/artisan queue:work \
--sleep=3 \
--tries=3 \
--max-time=3600 \
--queue=default
directory=/var/www/html
user=www-data
autostart=true
autorestart=true
priority=20
numprocs=1
startsecs=5
stdout_logfile=/dev/stdout
stdout_logfile_maxbytes=0
stderr_logfile=/dev/stderr
stderr_logfile_maxbytes=0