docker php image use php:8.2-fpm + volume backup export path

This commit is contained in:
xiaomlove
2025-05-17 13:20:42 +07:00
parent 21bb095683
commit 0b35fe662d
6 changed files with 89 additions and 55 deletions
+54 -44
View File
@@ -1,70 +1,79 @@
# 👷‍♀️ 第一阶段:构建阶段,包含所有开发依赖 # 👷‍♀️ 第一阶段:构建阶段,包含所有开发依赖
FROM php:8.2-fpm-alpine AS builder FROM php:8.2-fpm AS builder
RUN apk add --no-cache \ # 安装依赖
$PHPIZE_DEPS \ RUN apt-get update && apt-get install -y \
git \
unzip \
libzip-dev \ libzip-dev \
libpng-dev \ libpng-dev \
libjpeg-turbo-dev \ libjpeg-dev \
freetype-dev \
icu-dev \
libxml2-dev \
libwebp-dev \ libwebp-dev \
gmp-dev \ libfreetype6-dev \
oniguruma-dev \ libicu-dev \
linux-headers libxml2-dev \
libgmp-dev \
RUN docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp libonig-dev \
curl \
RUN docker-php-ext-install -j$(nproc) \ wget \
bcmath \ rsync \
pdo_mysql \ pkg-config \
mysqli \ build-essential \
gd \ && docker-php-ext-configure gd --with-freetype --with-jpeg --with-webp \
pcntl \ && docker-php-ext-install -j$(nproc) \
sockets \ bcmath \
gmp \ pdo_mysql \
zip \ mysqli \
intl \ gd \
opcache pcntl \
sockets \
gmp \
zip \
intl \
opcache
# 检查 pecl.php.net 可用性 # 检查 pecl.php.net 可用性
RUN curl -Is https://pecl.php.net | head -n 1 | grep "200" \ RUN curl -Is https://pecl.php.net | head -n 1 | grep "200" \
|| (echo "❌ pecl.php.net unreachable, aborting build." && exit 1) || (echo "❌ pecl.php.net unreachable, aborting build." && exit 1)
# 安装 redis 扩展 # 安装 redis 扩展
RUN pecl channel-update pecl.php.net \ RUN pecl channel-update pecl.php.net \
&& pecl install redis \ && pecl install redis \
&& docker-php-ext-enable redis && docker-php-ext-enable redis
# 👨‍🍳 第二阶段:运行阶段,仅包含必要运行环境 # 👨‍🍳 第二阶段:运行阶段,仅包含必要运行环境
FROM php:8.2-fpm-alpine FROM php:8.2-fpm
# 复制已构建的扩展 # 安装运行所需依赖(无需 dev 工具)
COPY --from=builder /usr/local/lib/php/extensions /usr/local/lib/php/extensions RUN apt-get update && apt-get install -y \
COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/ libzip4 \
libpng-dev \
# 安装运行时所需依赖 libjpeg-dev \
RUN apk add --no-cache \ libwebp-dev \
libfreetype6 \
libicu-dev \
libxml2 \
libgmp-dev \
libonig-dev \
git \
curl \
wget \ wget \
rsync \ rsync \
libzip \ unzip \
libpng \ bash \
libjpeg-turbo \ netcat-openbsd \
libwebp \ default-mysql-client \
freetype \ && apt-get clean && rm -rf /var/lib/apt/lists/*
icu \
libxml2 \ # 复制构建好的 PHP 扩展配置
gmp \ COPY --from=builder /usr/local/lib/php/extensions /usr/local/lib/php/extensions
oniguruma \ COPY --from=builder /usr/local/etc/php/conf.d/ /usr/local/etc/php/conf.d/
git
# 配置 www.conf # 配置 www.conf
RUN sed -i \ RUN sed -i \
-e 's/;catch_workers_output = .*/catch_workers_output = yes/g' \ -e 's/;catch_workers_output = .*/catch_workers_output = yes/g' \
-e 's/;php_admin_flag\[log_errors\] = .*/php_admin_flag\[log_errors\] = on/g' \ -e 's/;php_admin_flag\[log_errors\] = .*/php_admin_flag[log_errors] = on/g' \
-e 's/;php_admin_value\[error_log\] = .*/php_admin_value\[error_log\] = \/dev\/stderr/g' \ -e 's/;php_admin_value\[error_log\] = .*/php_admin_value[error_log] = \/dev\/stderr/g' \
/usr/local/etc/php-fpm.d/www.conf /usr/local/etc/php-fpm.d/www.conf
# 配置 PHP 错误日志输出到 stderr # 配置 PHP 错误日志输出到 stderr
@@ -75,6 +84,7 @@ RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/local
ENV LOG_CHANNEL=stderr ENV LOG_CHANNEL=stderr
# 复制入口脚本
COPY entrypoint.sh /usr/local/bin/entrypoint.sh COPY entrypoint.sh /usr/local/bin/entrypoint.sh
RUN chmod +x /usr/local/bin/entrypoint.sh RUN chmod +x /usr/local/bin/entrypoint.sh
+13 -5
View File
@@ -1,4 +1,4 @@
#!/bin/sh #!/bin/bash
# 定义颜色 # 定义颜色
COLOR_RED='\033[0;31m' COLOR_RED='\033[0;31m'
@@ -36,15 +36,27 @@ VENDOR_DIR="${ROOT_PATH}/vendor"
chown -R www-data:www-data $ROOT_PATH chown -R www-data:www-data $ROOT_PATH
mysql_timeout=30
until nc -z mysql 3306; do until nc -z mysql 3306; do
echo_info "Waiting for MySQL to be ready..." echo_info "Waiting for MySQL to be ready..."
sleep 2 sleep 2
((mysql_timeout--))
if [ $mysql_timeout -le 0 ]; then
echo "❌ MySQL connection timeout."
exit 1
fi
done done
echo_success "MySQL is ready." echo_success "MySQL is ready."
redis_timeout=30
until nc -z redis 6379; do until nc -z redis 6379; do
echo_info "Waiting for Redis to be ready..." echo_info "Waiting for Redis to be ready..."
sleep 2 sleep 2
((redis_timeout--))
if [ $redis_timeout -le 0 ]; then
echo "❌ Redis connection timeout."
exit 1
fi
done done
echo_success "Redis is ready." echo_success "Redis is ready."
@@ -107,7 +119,3 @@ else
echo_error "Unknown SERVICE_NAME: $SERVICE_NAME, exiting." echo_error "Unknown SERVICE_NAME: $SERVICE_NAME, exiting."
exit 1 exit 1
fi fi
@@ -10,6 +10,7 @@ use App\Models\SearchBox;
use App\Models\Setting; use App\Models\Setting;
use App\Models\User; use App\Models\User;
use App\Repositories\TokenRepository; use App\Repositories\TokenRepository;
use App\Repositories\ToolRepository;
use Filament\Facades\Filament; use Filament\Facades\Filament;
use Filament\Resources\Pages\Page; use Filament\Resources\Pages\Page;
use Filament\Forms; use Filament\Forms;
@@ -122,7 +123,7 @@ class EditSetting extends Page implements Forms\Contracts\HasForms
// Forms\Components\TextInput::make('backup.google_drive_client_secret')->label(__('label.setting.backup.google_drive_client_secret')), // Forms\Components\TextInput::make('backup.google_drive_client_secret')->label(__('label.setting.backup.google_drive_client_secret')),
// Forms\Components\TextInput::make('backup.google_drive_refresh_token')->label(__('label.setting.backup.google_drive_refresh_token')), // Forms\Components\TextInput::make('backup.google_drive_refresh_token')->label(__('label.setting.backup.google_drive_refresh_token')),
// Forms\Components\TextInput::make('backup.google_drive_folder_id')->label(__('label.setting.backup.google_drive_folder_id')), // Forms\Components\TextInput::make('backup.google_drive_folder_id')->label(__('label.setting.backup.google_drive_folder_id')),
Forms\Components\TextInput::make('backup.export_path')->label(__('label.setting.backup.export_path'))->helperText(new HtmlString(__('label.setting.backup.export_path_help', ['default_path' => sys_get_temp_dir()]))), Forms\Components\TextInput::make('backup.export_path')->label(__('label.setting.backup.export_path'))->helperText(new HtmlString(__('label.setting.backup.export_path_help', ['default_path' => ToolRepository::getBackupExportPathDefault()]))),
Forms\Components\Radio::make('backup.via_ftp')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.via_ftp'))->helperText(new HtmlString(__('label.setting.backup.via_ftp_help'))), Forms\Components\Radio::make('backup.via_ftp')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.via_ftp'))->helperText(new HtmlString(__('label.setting.backup.via_ftp_help'))),
Forms\Components\Radio::make('backup.via_sftp')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.via_sftp'))->helperText(new HtmlString(__('label.setting.backup.via_sftp_help'))), Forms\Components\Radio::make('backup.via_sftp')->options(self::$yesOrNo)->inline(true)->label(__('label.setting.backup.via_sftp'))->helperText(new HtmlString(__('label.setting.backup.via_sftp_help'))),
])->columns(2); ])->columns(2);
+18 -4
View File
@@ -32,7 +32,7 @@ class ToolRepository extends BaseRepository
$webRoot = base_path(); $webRoot = base_path();
$dirName = basename($webRoot); $dirName = basename($webRoot);
$excludes = self::BACKUP_EXCLUDES; $excludes = self::BACKUP_EXCLUDES;
$baseFilename = sprintf('%s/%s.web.%s', Setting::getBackupExportPath() ?: sys_get_temp_dir(), $dirName, date('Ymd.His')); $baseFilename = sprintf('%s/%s.web.%s', $this->getBackupExportPath(), $dirName, date('Ymd.His'));
if (command_exists('tar') && ($method === 'tar' || $method === null)) { if (command_exists('tar') && ($method === 'tar' || $method === null)) {
$filename = $baseFilename . ".tar.gz"; $filename = $baseFilename . ".tar.gz";
$command = "tar"; $command = "tar";
@@ -89,9 +89,9 @@ class ToolRepository extends BaseRepository
{ {
$connectionName = config('database.default'); $connectionName = config('database.default');
$config = config("database.connections.$connectionName"); $config = config("database.connections.$connectionName");
$filename = sprintf('%s/%s.database.%s.sql', Setting::getBackupExportPath() ?: sys_get_temp_dir(), basename(base_path()), date('Ymd.His')); $filename = sprintf('%s/%s.database.%s.sql', $this->getBackupExportPath(), basename(base_path()), date('Ymd.His'));
$command = sprintf( $command = sprintf(
'mysqldump --user=%s --password=%s --host=%s --port=%s --single-transaction --no-create-db %s >> %s 2>&1', 'mysqldump --user=%s --password=%s --host=%s --port=%s --single-transaction --no-create-db --no-tablespaces %s >> %s 2>&1',
$config['username'], $config['password'], $config['host'], $config['port'], $config['database'], $filename, $config['username'], $config['password'], $config['host'], $config['port'], $config['database'], $filename,
); );
$result = exec($command, $output, $result_code); $result = exec($command, $output, $result_code);
@@ -115,7 +115,7 @@ class ToolRepository extends BaseRepository
if ($backupDatabase['result_code'] != 0) { if ($backupDatabase['result_code'] != 0) {
throw new \RuntimeException("backup database fail: " . json_encode($backupDatabase)); throw new \RuntimeException("backup database fail: " . json_encode($backupDatabase));
} }
$baseFilename = sprintf('%s/%s.%s', Setting::getBackupExportPath() ?: sys_get_temp_dir(), basename(base_path()), date('Ymd.His')); $baseFilename = sprintf('%s/%s.%s', $this->getBackupExportPath(), basename(base_path()), date('Ymd.His'));
if (command_exists('tar') && ($method === 'tar' || $method === null)) { if (command_exists('tar') && ($method === 'tar' || $method === null)) {
$filename = $baseFilename . ".tar.gz"; $filename = $baseFilename . ".tar.gz";
$command = sprintf( $command = sprintf(
@@ -149,6 +149,20 @@ class ToolRepository extends BaseRepository
return $this->transfer($filename, $result_code); return $this->transfer($filename, $result_code);
} }
private function getBackupExportPath(): string
{
$path = Setting::getBackupExportPath();
if (empty($path)) {
$path = self::getBackupExportPathDefault();
}
return $path;
}
public static function getBackupExportPathDefault(): string
{
return sys_get_temp_dir() . "/nexusphp_backup";
}
/** /**
* do backup cronjob * do backup cronjob
* *
+1
View File
@@ -8,6 +8,7 @@ services:
SERVICE_NAME: php SERVICE_NAME: php
volumes: volumes:
- .:/var/www/html - .:/var/www/html
- ${NP_BACKUP_EXPORT_PATH:-/tmp/nexusphp_backup}:${NP_BACKUP_EXPORT_PATH:-/tmp/nexusphp_backup}
depends_on: depends_on:
- mysql - mysql
- redis - redis
+1 -1
View File
@@ -1,6 +1,6 @@
<?php <?php
defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.9.0'); defined('VERSION_NUMBER') || define('VERSION_NUMBER', '1.9.0');
defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-05-16'); defined('RELEASE_DATE') || define('RELEASE_DATE', '2025-05-17');
defined('IN_TRACKER') || define('IN_TRACKER', false); defined('IN_TRACKER') || define('IN_TRACKER', false);
defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP"); defined('PROJECTNAME') || define("PROJECTNAME","NexusPHP");
defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org"); defined('NEXUSPHPURL') || define("NEXUSPHPURL","https://nexusphp.org");