feat(docker): add Docker configuration and production environment setup

This commit is contained in:
2026-06-05 17:19:39 +03:30
parent 1c408130d0
commit 6eae917540
10 changed files with 492 additions and 1 deletions

28
.dockerignore Normal file
View File

@@ -0,0 +1,28 @@
.git
.github
.idea
.vscode
.env
.env.*
!.env.example
!.env.production.example
node_modules
vendor
npm-debug.log
yarn-error.log
storage/logs/*
storage/framework/cache/data/*
storage/framework/sessions/*
storage/framework/views/*
bootstrap/cache/*.php
tests
coverage
.phpunit.cache
Dockerfile
docker-compose*.yml
README.md

68
.env.production.example Normal file
View File

@@ -0,0 +1,68 @@
APP_NAME=Hoshpoint
APP_ENV=production
APP_KEY=
APP_DEBUG=false
APP_URL=https://example.com
HTTP_PORT=8080
APP_LOCALE=fa
APP_FALLBACK_LOCALE=en
APP_FAKER_LOCALE=fa_IR
APP_MAINTENANCE_DRIVER=file
BCRYPT_ROUNDS=12
LOG_CHANNEL=stderr
LOG_STACK=stderr
LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=info
DB_CONNECTION=mariadb
DB_HOST=mariadb
DB_PORT=3306
DB_DATABASE=hoshpoint_backend
DB_USERNAME=hoshpoint
DB_PASSWORD=change-me
MARIADB_DATABASE=hoshpoint_backend
MARIADB_USER=hoshpoint
MARIADB_PASSWORD=change-me
MARIADB_ROOT_PASSWORD=change-root-password
SESSION_DRIVER=database
SESSION_LIFETIME=120
SESSION_ENCRYPT=false
SESSION_PATH=/
SESSION_DOMAIN=null
SESSION_SECURE_COOKIE=true
SESSION_HTTP_ONLY=true
SESSION_SAME_SITE=lax
BROADCAST_CONNECTION=log
FILESYSTEM_DISK=local
QUEUE_CONNECTION=database
CACHE_STORE=database
MEMCACHED_HOST=memcached
REDIS_CLIENT=phpredis
REDIS_HOST=redis
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=log
MAIL_SCHEME=null
MAIL_HOST=127.0.0.1
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS=hello@example.com
MAIL_FROM_NAME="${APP_NAME}"
AWS_ACCESS_KEY_ID=
AWS_SECRET_ACCESS_KEY=
AWS_DEFAULT_REGION=us-east-1
AWS_BUCKET=
AWS_USE_PATH_STYLE_ENDPOINT=false
VITE_APP_NAME="${APP_NAME}"

86
Dockerfile Normal file
View File

@@ -0,0 +1,86 @@
# syntax=docker/dockerfile:1.7
ARG PHP_VERSION=8.4
FROM composer:2 AS composer
FROM node:24-alpine AS assets
WORKDIR /app
COPY package.json package-lock.json ./
RUN npm ci
COPY resources ./resources
COPY public ./public
COPY vite.config.js ./
RUN npm run build
FROM php:${PHP_VERSION}-fpm-bookworm AS app
WORKDIR /var/www/html
ENV COMPOSER_ALLOW_SUPERUSER=1
ADD --chmod=0755 https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/install-php-extensions
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
curl \
default-mysql-client \
unzip \
&& install-php-extensions \
bcmath \
dom \
intl \
mbstring \
opcache \
pcntl \
pdo_mysql \
xml \
xmlreader \
zip \
&& apt-get purge -y --auto-remove \
&& rm -rf /var/lib/apt/lists/*
COPY docker/php/php.ini /usr/local/etc/php/conf.d/99-production.ini
COPY docker/php/opcache.ini /usr/local/etc/php/conf.d/99-opcache.ini
COPY --from=composer /usr/bin/composer /usr/bin/composer
COPY composer.json composer.lock ./
RUN composer install \
--no-dev \
--no-autoloader \
--no-scripts \
--prefer-dist \
--no-interaction \
&& composer check-platform-reqs --no-dev
COPY . .
COPY --from=assets /app/public/build ./public/build
RUN composer dump-autoload --no-dev --optimize --no-scripts \
&& php artisan package:discover --ansi \
&& mkdir -p \
storage/app/public \
storage/framework/cache/data \
storage/framework/sessions \
storage/framework/views \
storage/logs \
bootstrap/cache \
&& rm -rf public/storage \
&& ln -s ../storage/app/public public/storage \
&& chown -R www-data:www-data storage bootstrap/cache
COPY docker/entrypoint.sh /usr/local/bin/docker-entrypoint
RUN chmod +x /usr/local/bin/docker-entrypoint
EXPOSE 9000
ENTRYPOINT ["docker-entrypoint"]
CMD ["php-fpm"]
FROM nginx:1.27-alpine AS nginx
COPY docker/nginx/default.conf /etc/nginx/conf.d/default.conf
COPY --from=app /var/www/html/public /var/www/html/public
EXPOSE 80

106
README.md
View File

@@ -1 +1,107 @@
### Hoshpoint
## اجرای پروژه با Docker در Production
ابتدا Docker Desktop را اجرا کنید و فایل env پروداکشن را بسازید:
```bash
cp .env.production.example .env.production
php artisan key:generate --show
```
خروجی را کپی کنید و داخل `.env.production` بگذارید (خط `APP_KEY` نباید خالی بماند):
```env
APP_KEY=base64:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx=
```
سپس `APP_URL`، `HTTP_PORT`، `DB_PASSWORD`، `MARIADB_PASSWORD` و `MARIADB_ROOT_PASSWORD` را هم تغییر دهید. مقدار پیش‌فرض `HTTP_PORT=8080` است.
اگر قبلاً بدون `APP_KEY` کانتینرها را بالا آورده‌اید، بعد از پر کردن کلید این‌ها را اجرا کنید:
```bash
docker compose --env-file .env.production exec app php artisan config:clear
docker compose --env-file .env.production restart app queue scheduler
```
برای build کردن imageها:
```bash
docker compose --env-file .env.production build
```
اول دیتابیس را بالا بیاورید:
```bash
docker compose --env-file .env.production up -d mariadb
```
بعد migrationها را اجرا کنید:
```bash
docker compose --env-file .env.production run --rm app php artisan migrate --force --seed
```
حالا همه سرویس‌ها را بالا بیاورید:
```bash
docker compose --env-file .env.production up -d
```
سایت از طریق پورت تنظیم‌شده در `HTTP_PORT` در دسترس است. مقدار پیش‌فرض:
```text
http://localhost:8080
```
برای تغییر پورت، مقدار زیر را در `.env.production` عوض کنید و سرویس `nginx` را دوباره بالا بیاورید:
```env
HTTP_PORT=8081
```
```bash
docker compose --env-file .env.production up -d --force-recreate nginx
```
برای دیدن وضعیت سرویس‌ها:
```bash
docker compose --env-file .env.production ps
```
برای دیدن لاگ‌ها:
```bash
docker compose --env-file .env.production logs -f
```
برای اجرای دستورهای Artisan داخل کانتینر:
```bash
docker compose --env-file .env.production exec app php artisan about
```
برای متوقف کردن سرویس‌ها:
```bash
docker compose --env-file .env.production down
```
برای حذف کامل دیتای دیتابیس و volumeها، فقط وقتی مطمئن هستید:
```bash
docker compose --env-file .env.production down -v
```
## خطای `No application encryption key has been specified`
یعنی `APP_KEY` در `.env.production` خالی است یا بعد از تغییر env، cache قدیمی مانده.
1. مقدار `APP_KEY` را در `.env.production` تنظیم کنید.
2. cache را پاک و سرویس‌ها را restart کنید:
```bash
docker compose --env-file .env.production exec app php artisan config:clear
docker compose --env-file .env.production restart app queue scheduler
```

100
docker-compose.yml Normal file
View File

@@ -0,0 +1,100 @@
services:
app:
build:
context: .
target: app
image: hoshpoint-backend-app:production
restart: unless-stopped
env_file:
- ${APP_ENV_FILE:-.env.production}
environment:
APP_ENV: production
APP_DEBUG: "false"
LOG_CHANNEL: stderr
DB_HOST: mariadb
DB_PORT: 3306
depends_on:
mariadb:
condition: service_healthy
volumes:
- app-storage:/var/www/html/storage
networks:
- hoshpoint
nginx:
build:
context: .
target: nginx
image: hoshpoint-backend-nginx:production
restart: unless-stopped
depends_on:
- app
ports:
- "${HTTP_PORT:-8080}:80"
volumes:
- app-storage:/var/www/html/storage:ro
networks:
- hoshpoint
queue:
image: hoshpoint-backend-app:production
restart: unless-stopped
env_file:
- ${APP_ENV_FILE:-.env.production}
environment:
APP_ENV: production
APP_DEBUG: "false"
LOG_CHANNEL: stderr
DB_HOST: mariadb
DB_PORT: 3306
command: php artisan queue:work database --sleep=3 --tries=3 --timeout=90
depends_on:
mariadb:
condition: service_healthy
volumes:
- app-storage:/var/www/html/storage
networks:
- hoshpoint
scheduler:
image: hoshpoint-backend-app:production
restart: unless-stopped
env_file:
- ${APP_ENV_FILE:-.env.production}
environment:
APP_ENV: production
APP_DEBUG: "false"
LOG_CHANNEL: stderr
DB_HOST: mariadb
DB_PORT: 3306
command: sh -c "while true; do php artisan schedule:run --no-interaction; sleep 60; done"
depends_on:
mariadb:
condition: service_healthy
volumes:
- app-storage:/var/www/html/storage
networks:
- hoshpoint
mariadb:
image: mariadb:11.4
restart: unless-stopped
env_file:
- ${APP_ENV_FILE:-.env.production}
volumes:
- mariadb-data:/var/lib/mysql
healthcheck:
test: ["CMD", "healthcheck.sh", "--connect", "--innodb_initialized"]
interval: 10s
timeout: 5s
retries: 5
networks:
- hoshpoint
volumes:
app-storage:
mariadb-data:
networks:
hoshpoint:
driver: bridge

33
docker/entrypoint.sh Normal file
View File

@@ -0,0 +1,33 @@
#!/usr/bin/env sh
set -eu
mkdir -p \
storage/app/public \
storage/framework/cache/data \
storage/framework/sessions \
storage/framework/views \
storage/logs \
bootstrap/cache
if [ ! -L public/storage ]; then
rm -rf public/storage
ln -s ../storage/app/public public/storage
fi
if [ "$(id -u)" = "0" ]; then
chown -R www-data:www-data storage bootstrap/cache
fi
if [ -z "${APP_KEY:-}" ]; then
echo "ERROR: APP_KEY is empty. Set it in .env.production, then restart containers." >&2
echo " php artisan key:generate --show" >&2
exit 1
fi
if [ "${APP_ENV:-production}" = "production" ]; then
php artisan config:cache --no-interaction
php artisan event:cache --no-interaction
php artisan view:cache --no-interaction
fi
exec "$@"

49
docker/nginx/default.conf Normal file
View File

@@ -0,0 +1,49 @@
server {
listen 80;
server_name _;
root /var/www/html/public;
index index.php;
charset utf-8;
client_max_body_size 20M;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location = /favicon.ico {
access_log off;
log_not_found off;
}
location = /robots.txt {
access_log off;
log_not_found off;
}
location ~* \.(?:css|js|jpg|jpeg|gif|png|svg|ico|webp|woff|woff2|ttf)$ {
expires 30d;
add_header Cache-Control "public, immutable";
access_log off;
try_files $uri /index.php?$query_string;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_pass app:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
fastcgi_param DOCUMENT_ROOT $realpath_root;
include fastcgi_params;
fastcgi_hide_header X-Powered-By;
}
location ~ /\.(?!well-known).* {
deny all;
}
}

8
docker/php/opcache.ini Normal file
View File

@@ -0,0 +1,8 @@
opcache.enable = 1
opcache.enable_cli = 0
opcache.memory_consumption = 192
opcache.interned_strings_buffer = 16
opcache.max_accelerated_files = 20000
opcache.validate_timestamps = 0
opcache.save_comments = 1
opcache.fast_shutdown = 1

12
docker/php/php.ini Normal file
View File

@@ -0,0 +1,12 @@
expose_php = Off
memory_limit = 256M
max_execution_time = 60
max_input_time = 60
upload_max_filesize = 20M
post_max_size = 20M
variables_order = EGPCS
realpath_cache_size = 4096K
realpath_cache_ttl = 600
log_errors = On
error_log = /proc/self/fd/2
date.timezone = UTC

View File

@@ -1 +1,2 @@
// JavaScript entry point required by vite.config.js.
//