работа с контейнерами
This commit is contained in:
@@ -0,0 +1,22 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
||||||
|
.idea
|
||||||
|
.vscode
|
||||||
|
node_modules
|
||||||
|
vendor
|
||||||
|
public/build
|
||||||
|
public/hot
|
||||||
|
public/storage
|
||||||
|
bootstrap/cache/*.php
|
||||||
|
storage/*.key
|
||||||
|
storage/backups
|
||||||
|
storage/logs/*
|
||||||
|
storage/framework/cache/data/*
|
||||||
|
storage/framework/sessions/*
|
||||||
|
storage/framework/views/*
|
||||||
|
tests
|
||||||
|
compose*.yaml
|
||||||
|
README.md
|
||||||
@@ -77,6 +77,23 @@ chmod +x scripts/deploy-ubuntu.sh
|
|||||||
./scripts/deploy-ubuntu.sh --skip-cache
|
./scripts/deploy-ubuntu.sh --skip-cache
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Production без Sail
|
||||||
|
|
||||||
|
Для production используется отдельный стек `compose.prod.yaml`:
|
||||||
|
|
||||||
|
- `web` — непривилегированный nginx со статикой;
|
||||||
|
- `app` — PHP-FPM с кодом и Composer-зависимостями внутри образа;
|
||||||
|
- `mysql` — постоянная база на томе `sail-mysql`.
|
||||||
|
|
||||||
|
Node.js, Playwright, Xdebug, dev-зависимости Composer и bind-mount исходного кода в production-образы не входят.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
chmod +x scripts/deploy-production.sh
|
||||||
|
./scripts/deploy-production.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
Скрипт сначала собирает и проверяет новый PHP-FPM, выполняет миграции и прогрев кэша, а затем переключает порт с legacy Sail-контейнера на production nginx. Если HTTP-проверка не проходит, legacy-контейнер запускается обратно.
|
||||||
|
|
||||||
## Как это связано с AzerothCore
|
## Как это связано с AzerothCore
|
||||||
|
|
||||||
По умолчанию Sail-контейнер подключается к auth-базе core-сервера так:
|
По умолчанию Sail-контейнер подключается к auth-базе core-сервера так:
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
services:
|
||||||
|
app:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/production/Dockerfile
|
||||||
|
target: app
|
||||||
|
args:
|
||||||
|
APP_UID: '${WWWUSER:-1000}'
|
||||||
|
APP_GID: '${WWWGROUP:-1000}'
|
||||||
|
image: moonwell-web-app:production
|
||||||
|
restart: unless-stopped
|
||||||
|
init: true
|
||||||
|
env_file:
|
||||||
|
- .env
|
||||||
|
environment:
|
||||||
|
APP_ENV: production
|
||||||
|
APP_DEBUG: 'false'
|
||||||
|
DB_HOST: mysql
|
||||||
|
DB_PORT: 3306
|
||||||
|
volumes:
|
||||||
|
- './storage:/var/www/html/storage'
|
||||||
|
tmpfs:
|
||||||
|
- '/var/www/html/bootstrap/cache:uid=${WWWUSER:-1000},gid=${WWWGROUP:-1000},mode=0770'
|
||||||
|
networks:
|
||||||
|
- sail
|
||||||
|
depends_on:
|
||||||
|
mysql:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
- CMD-SHELL
|
||||||
|
- 'SCRIPT_NAME=/healthz SCRIPT_FILENAME=/healthz REQUEST_METHOD=GET cgi-fcgi -bind -connect 127.0.0.1:9000 2>/dev/null | grep -q pong'
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 15s
|
||||||
|
|
||||||
|
web:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: docker/production/Dockerfile
|
||||||
|
target: web
|
||||||
|
args:
|
||||||
|
APP_UID: '${WWWUSER:-1000}'
|
||||||
|
APP_GID: '${WWWGROUP:-1000}'
|
||||||
|
image: moonwell-web-nginx:production
|
||||||
|
restart: unless-stopped
|
||||||
|
init: true
|
||||||
|
ports:
|
||||||
|
- '127.0.0.1:${APP_PORT:-8080}:8080'
|
||||||
|
volumes:
|
||||||
|
- './storage/app/public:/var/www/html/public/storage:ro'
|
||||||
|
networks:
|
||||||
|
- sail
|
||||||
|
depends_on:
|
||||||
|
app:
|
||||||
|
condition: service_healthy
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'wget', '--quiet', '--tries=1', '--spider', 'http://127.0.0.1:8080/healthz']
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 10s
|
||||||
|
read_only: true
|
||||||
|
tmpfs:
|
||||||
|
- /tmp
|
||||||
|
security_opt:
|
||||||
|
- no-new-privileges:true
|
||||||
|
cap_drop:
|
||||||
|
- ALL
|
||||||
|
|
||||||
|
mysql:
|
||||||
|
image: mysql:8.4
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- '${FORWARD_DB_PORT:-3307}:3306'
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: '${DB_PASSWORD}'
|
||||||
|
MYSQL_ROOT_HOST: '%'
|
||||||
|
MYSQL_DATABASE: '${DB_DATABASE}'
|
||||||
|
MYSQL_USER: '${DB_USERNAME}'
|
||||||
|
MYSQL_PASSWORD: '${DB_PASSWORD}'
|
||||||
|
MYSQL_ALLOW_EMPTY_PASSWORD: 1
|
||||||
|
MYSQL_EXTRA_OPTIONS: '${MYSQL_EXTRA_OPTIONS:-}'
|
||||||
|
volumes:
|
||||||
|
- 'sail-mysql:/var/lib/mysql'
|
||||||
|
- './docker/mysql/create-testing-database.sh:/docker-entrypoint-initdb.d/10-create-testing-database.sh'
|
||||||
|
networks:
|
||||||
|
- sail
|
||||||
|
healthcheck:
|
||||||
|
test:
|
||||||
|
- CMD
|
||||||
|
- mysqladmin
|
||||||
|
- ping
|
||||||
|
- '-p${DB_PASSWORD}'
|
||||||
|
retries: 3
|
||||||
|
timeout: 5s
|
||||||
|
|
||||||
|
networks:
|
||||||
|
sail:
|
||||||
|
driver: bridge
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
sail-mysql:
|
||||||
|
driver: local
|
||||||
@@ -24,6 +24,12 @@ services:
|
|||||||
- sail
|
- sail
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
- mysql
|
||||||
|
healthcheck:
|
||||||
|
test: ['CMD', 'curl', '--fail', '--silent', 'http://127.0.0.1/healthz']
|
||||||
|
interval: 30s
|
||||||
|
timeout: 5s
|
||||||
|
retries: 3
|
||||||
|
start_period: 15s
|
||||||
mysql:
|
mysql:
|
||||||
image: 'mysql:8.4'
|
image: 'mysql:8.4'
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
|||||||
@@ -0,0 +1,116 @@
|
|||||||
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
|
FROM php:8.3-fpm-bookworm AS php-base
|
||||||
|
|
||||||
|
ARG APP_UID=1000
|
||||||
|
ARG APP_GID=1000
|
||||||
|
|
||||||
|
ENV APP_ENV=production \
|
||||||
|
APP_DEBUG=false \
|
||||||
|
COMPOSER_ALLOW_SUPERUSER=1
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y --no-install-recommends \
|
||||||
|
libfcgi-bin \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libicu-dev \
|
||||||
|
libjpeg62-turbo-dev \
|
||||||
|
libpng-dev \
|
||||||
|
libzip-dev \
|
||||||
|
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
|
||||||
|
&& docker-php-ext-install -j"$(nproc)" \
|
||||||
|
bcmath \
|
||||||
|
exif \
|
||||||
|
gd \
|
||||||
|
intl \
|
||||||
|
pcntl \
|
||||||
|
pdo_mysql \
|
||||||
|
sockets \
|
||||||
|
zip \
|
||||||
|
&& apt-mark manual \
|
||||||
|
libfcgi-bin \
|
||||||
|
libfreetype6 \
|
||||||
|
libicu72 \
|
||||||
|
libjpeg62-turbo \
|
||||||
|
libpng16-16 \
|
||||||
|
libzip4 \
|
||||||
|
&& apt-get purge -y --auto-remove \
|
||||||
|
libfreetype6-dev \
|
||||||
|
libicu-dev \
|
||||||
|
libjpeg62-turbo-dev \
|
||||||
|
libpng-dev \
|
||||||
|
libzip-dev \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* \
|
||||||
|
&& groupadd --gid "$APP_GID" app \
|
||||||
|
&& useradd --uid "$APP_UID" --gid "$APP_GID" --create-home --shell /usr/sbin/nologin app
|
||||||
|
|
||||||
|
COPY docker/production/php.ini /usr/local/etc/php/conf.d/99-production.ini
|
||||||
|
COPY docker/production/php-fpm-pool.conf /usr/local/etc/php-fpm.d/zz-production.conf
|
||||||
|
|
||||||
|
FROM php-base AS vendor
|
||||||
|
|
||||||
|
COPY --from=composer:2 /usr/bin/composer /usr/local/bin/composer
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
RUN composer install \
|
||||||
|
--no-dev \
|
||||||
|
--prefer-dist \
|
||||||
|
--optimize-autoloader \
|
||||||
|
--no-interaction \
|
||||||
|
--no-progress \
|
||||||
|
&& composer clear-cache
|
||||||
|
|
||||||
|
FROM node:24-bookworm-slim AS frontend
|
||||||
|
|
||||||
|
WORKDIR /var/www/html
|
||||||
|
|
||||||
|
COPY package.json package-lock.json ./
|
||||||
|
RUN npm ci --no-audit --no-fund
|
||||||
|
|
||||||
|
COPY resources ./resources
|
||||||
|
COPY public ./public
|
||||||
|
COPY vite.config.* ./
|
||||||
|
|
||||||
|
ARG VITE_APP_NAME=Moonwell
|
||||||
|
ENV VITE_APP_NAME="$VITE_APP_NAME"
|
||||||
|
|
||||||
|
RUN npm run build
|
||||||
|
|
||||||
|
FROM php-base AS app
|
||||||
|
|
||||||
|
COPY --from=vendor --chown=app:app /var/www/html /var/www/html
|
||||||
|
COPY --from=frontend --chown=app:app /var/www/html/public/build /var/www/html/public/build
|
||||||
|
|
||||||
|
RUN mkdir -p \
|
||||||
|
bootstrap/cache \
|
||||||
|
storage/app/public \
|
||||||
|
storage/framework/cache/data \
|
||||||
|
storage/framework/sessions \
|
||||||
|
storage/framework/views \
|
||||||
|
storage/logs \
|
||||||
|
&& chown -R app:app bootstrap/cache storage
|
||||||
|
|
||||||
|
EXPOSE 9000
|
||||||
|
|
||||||
|
CMD ["php-fpm", "--nodaemonize"]
|
||||||
|
|
||||||
|
FROM nginx:1.27-alpine AS web
|
||||||
|
|
||||||
|
ARG APP_UID=1000
|
||||||
|
ARG APP_GID=1000
|
||||||
|
|
||||||
|
RUN addgroup -S -g "$APP_GID" app \
|
||||||
|
&& adduser -S -D -H -u "$APP_UID" -G app app
|
||||||
|
|
||||||
|
COPY docker/production/nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY --from=vendor --chown=app:app /var/www/html/public /var/www/html/public
|
||||||
|
COPY --from=frontend --chown=app:app /var/www/html/public/build /var/www/html/public/build
|
||||||
|
|
||||||
|
USER app
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["nginx", "-g", "daemon off;"]
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
worker_processes auto;
|
||||||
|
pid /tmp/nginx.pid;
|
||||||
|
error_log /dev/stderr warn;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
access_log /dev/stdout;
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
client_max_body_size 100m;
|
||||||
|
|
||||||
|
client_body_temp_path /tmp/client_temp;
|
||||||
|
proxy_temp_path /tmp/proxy_temp;
|
||||||
|
fastcgi_temp_path /tmp/fastcgi_temp;
|
||||||
|
uwsgi_temp_path /tmp/uwsgi_temp;
|
||||||
|
scgi_temp_path /tmp/scgi_temp;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 8080 default_server;
|
||||||
|
server_name _;
|
||||||
|
root /var/www/html/public;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /healthz {
|
||||||
|
allow 127.0.0.1;
|
||||||
|
deny all;
|
||||||
|
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME /healthz;
|
||||||
|
fastcgi_pass app:9000;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /index.php {
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME /var/www/html/public/index.php;
|
||||||
|
fastcgi_param HTTP_PROXY "";
|
||||||
|
fastcgi_pass app:9000;
|
||||||
|
fastcgi_read_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
return 404;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
[www]
|
||||||
|
user = app
|
||||||
|
group = app
|
||||||
|
|
||||||
|
listen = 0.0.0.0:9000
|
||||||
|
|
||||||
|
pm = dynamic
|
||||||
|
pm.max_children = 8
|
||||||
|
pm.start_servers = 2
|
||||||
|
pm.min_spare_servers = 1
|
||||||
|
pm.max_spare_servers = 4
|
||||||
|
pm.max_requests = 500
|
||||||
|
|
||||||
|
request_terminate_timeout = 60s
|
||||||
|
request_slowlog_timeout = 10s
|
||||||
|
slowlog = /proc/self/fd/2
|
||||||
|
|
||||||
|
ping.path = /healthz
|
||||||
|
ping.response = pong
|
||||||
|
|
||||||
|
clear_env = no
|
||||||
|
catch_workers_output = yes
|
||||||
|
decorate_workers_output = no
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
[PHP]
|
||||||
|
expose_php = Off
|
||||||
|
display_errors = Off
|
||||||
|
display_startup_errors = Off
|
||||||
|
log_errors = On
|
||||||
|
memory_limit = 256M
|
||||||
|
max_execution_time = 60
|
||||||
|
post_max_size = 100M
|
||||||
|
upload_max_filesize = 100M
|
||||||
|
variables_order = EGPCS
|
||||||
|
|
||||||
|
[Date]
|
||||||
|
date.timezone = UTC
|
||||||
|
|
||||||
|
[opcache]
|
||||||
|
opcache.enable = 1
|
||||||
|
opcache.enable_cli = 0
|
||||||
|
opcache.memory_consumption = 128
|
||||||
|
opcache.interned_strings_buffer = 16
|
||||||
|
opcache.max_accelerated_files = 20000
|
||||||
|
opcache.validate_timestamps = 0
|
||||||
|
opcache.save_comments = 1
|
||||||
@@ -56,6 +56,11 @@ RUN apt-get update && apt-get upgrade -y \
|
|||||||
&& apt-get clean \
|
&& apt-get clean \
|
||||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
|
RUN apt-get update \
|
||||||
|
&& apt-get install -y nginx php8.3-fpm \
|
||||||
|
&& apt-get clean \
|
||||||
|
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
||||||
|
|
||||||
RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.3
|
RUN setcap "cap_net_bind_service=+ep" /usr/bin/php8.3
|
||||||
|
|
||||||
RUN userdel -r ubuntu
|
RUN userdel -r ubuntu
|
||||||
@@ -65,7 +70,10 @@ RUN git config --global --add safe.directory /var/www/html
|
|||||||
|
|
||||||
COPY start-container /usr/local/bin/start-container
|
COPY start-container /usr/local/bin/start-container
|
||||||
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
|
||||||
|
COPY nginx.conf /etc/nginx/nginx.conf
|
||||||
|
COPY php-fpm-pool.conf /etc/php/8.3/fpm/pool.d/www.conf
|
||||||
COPY php.ini /etc/php/8.3/cli/conf.d/99-sail.ini
|
COPY php.ini /etc/php/8.3/cli/conf.d/99-sail.ini
|
||||||
|
COPY php.ini /etc/php/8.3/fpm/conf.d/99-sail.ini
|
||||||
RUN chmod +x /usr/local/bin/start-container
|
RUN chmod +x /usr/local/bin/start-container
|
||||||
|
|
||||||
EXPOSE 80/tcp
|
EXPOSE 80/tcp
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
user sail;
|
||||||
|
worker_processes auto;
|
||||||
|
pid /run/nginx.pid;
|
||||||
|
error_log /dev/stderr warn;
|
||||||
|
|
||||||
|
events {
|
||||||
|
worker_connections 1024;
|
||||||
|
}
|
||||||
|
|
||||||
|
http {
|
||||||
|
include /etc/nginx/mime.types;
|
||||||
|
default_type application/octet-stream;
|
||||||
|
|
||||||
|
access_log /dev/stdout;
|
||||||
|
sendfile on;
|
||||||
|
tcp_nopush on;
|
||||||
|
keepalive_timeout 65;
|
||||||
|
client_max_body_size 100m;
|
||||||
|
|
||||||
|
server {
|
||||||
|
listen 80 default_server;
|
||||||
|
server_name _;
|
||||||
|
root /var/www/html/public;
|
||||||
|
index index.php;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
try_files $uri $uri/ /index.php?$query_string;
|
||||||
|
}
|
||||||
|
|
||||||
|
location = /healthz {
|
||||||
|
allow 127.0.0.1;
|
||||||
|
deny all;
|
||||||
|
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME /dev/null;
|
||||||
|
fastcgi_pass 127.0.0.1:9000;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ \.php$ {
|
||||||
|
try_files $uri =404;
|
||||||
|
include fastcgi_params;
|
||||||
|
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
|
||||||
|
fastcgi_param HTTP_PROXY "";
|
||||||
|
fastcgi_pass 127.0.0.1:9000;
|
||||||
|
fastcgi_read_timeout 60s;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~ /\. {
|
||||||
|
deny all;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
[www]
|
||||||
|
user = sail
|
||||||
|
group = sail
|
||||||
|
|
||||||
|
listen = 127.0.0.1:9000
|
||||||
|
listen.owner = sail
|
||||||
|
listen.group = sail
|
||||||
|
|
||||||
|
pm = dynamic
|
||||||
|
pm.max_children = 8
|
||||||
|
pm.start_servers = 2
|
||||||
|
pm.min_spare_servers = 1
|
||||||
|
pm.max_spare_servers = 4
|
||||||
|
pm.max_requests = 500
|
||||||
|
|
||||||
|
request_terminate_timeout = 60s
|
||||||
|
request_slowlog_timeout = 10s
|
||||||
|
slowlog = /proc/self/fd/2
|
||||||
|
|
||||||
|
ping.path = /healthz
|
||||||
|
ping.response = pong
|
||||||
|
|
||||||
|
clear_env = no
|
||||||
|
catch_workers_output = yes
|
||||||
|
decorate_workers_output = no
|
||||||
@@ -4,10 +4,29 @@ user=root
|
|||||||
logfile=/var/log/supervisor/supervisord.log
|
logfile=/var/log/supervisor/supervisord.log
|
||||||
pidfile=/var/run/supervisord.pid
|
pidfile=/var/run/supervisord.pid
|
||||||
|
|
||||||
[program:php]
|
[program:php-fpm]
|
||||||
command=%(ENV_SUPERVISOR_PHP_COMMAND)s
|
command=/usr/sbin/php-fpm8.3 --nodaemonize --fpm-config /etc/php/8.3/fpm/php-fpm.conf
|
||||||
user=%(ENV_SUPERVISOR_PHP_USER)s
|
user=root
|
||||||
environment=LARAVEL_SAIL="1"
|
priority=10
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
startsecs=2
|
||||||
|
stopasgroup=true
|
||||||
|
stopsignal=QUIT
|
||||||
|
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;"
|
||||||
|
user=root
|
||||||
|
priority=20
|
||||||
|
autostart=true
|
||||||
|
autorestart=true
|
||||||
|
startsecs=2
|
||||||
|
stopasgroup=true
|
||||||
|
stopsignal=QUIT
|
||||||
stdout_logfile=/dev/stdout
|
stdout_logfile=/dev/stdout
|
||||||
stdout_logfile_maxbytes=0
|
stdout_logfile_maxbytes=0
|
||||||
stderr_logfile=/dev/stderr
|
stderr_logfile=/dev/stderr
|
||||||
|
|||||||
Executable
+120
@@ -0,0 +1,120 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ROOT_DIR="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
PROD_COMPOSE="${ROOT_DIR}/compose.prod.yaml"
|
||||||
|
DEV_COMPOSE="${ROOT_DIR}/compose.yaml"
|
||||||
|
ENV_FILE="${ROOT_DIR}/.env"
|
||||||
|
LEGACY_WAS_RUNNING=0
|
||||||
|
|
||||||
|
log() {
|
||||||
|
printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
|
||||||
|
}
|
||||||
|
|
||||||
|
fail() {
|
||||||
|
printf '[error] %s\n' "$*" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
env_value() {
|
||||||
|
local key="$1"
|
||||||
|
grep -E "^${key}=" "$ENV_FILE" | tail -n 1 | cut -d '=' -f 2- | sed -e 's/^"//' -e 's/"$//'
|
||||||
|
}
|
||||||
|
|
||||||
|
prod() {
|
||||||
|
docker compose -f "$PROD_COMPOSE" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
legacy() {
|
||||||
|
docker compose -f "$DEV_COMPOSE" "$@"
|
||||||
|
}
|
||||||
|
|
||||||
|
wait_for_health() {
|
||||||
|
local service="$1"
|
||||||
|
local container_id
|
||||||
|
local status
|
||||||
|
|
||||||
|
container_id="$(prod ps -q "$service")"
|
||||||
|
[[ -n "$container_id" ]] || fail "Контейнер ${service} не создан"
|
||||||
|
|
||||||
|
for _ in $(seq 1 45); do
|
||||||
|
status="$(docker inspect -f '{{if .State.Health}}{{.State.Health.Status}}{{else}}{{.State.Status}}{{end}}' "$container_id")"
|
||||||
|
if [[ "$status" == healthy ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
if [[ "$status" == exited || "$status" == dead ]]; then
|
||||||
|
docker logs --tail 100 "$container_id" >&2 || true
|
||||||
|
fail "Контейнер ${service} завершился со статусом ${status}"
|
||||||
|
fi
|
||||||
|
sleep 2
|
||||||
|
done
|
||||||
|
|
||||||
|
docker logs --tail 100 "$container_id" >&2 || true
|
||||||
|
fail "Контейнер ${service} не перешёл в healthy"
|
||||||
|
}
|
||||||
|
|
||||||
|
rollback_legacy() {
|
||||||
|
log "Production web не прошёл проверку, выполняю откат"
|
||||||
|
prod stop web app >/dev/null 2>&1 || true
|
||||||
|
if (( LEGACY_WAS_RUNNING )); then
|
||||||
|
legacy start laravel.test >/dev/null 2>&1 || true
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
main() {
|
||||||
|
command -v docker >/dev/null 2>&1 || fail "Не найден Docker"
|
||||||
|
docker compose version >/dev/null 2>&1 || fail "Не найден Docker Compose Plugin"
|
||||||
|
[[ -f "$ENV_FILE" ]] || fail "Не найден ${ENV_FILE}"
|
||||||
|
|
||||||
|
cd "$ROOT_DIR"
|
||||||
|
|
||||||
|
local app_uid app_gid app_port legacy_id
|
||||||
|
app_uid="$(env_value WWWUSER)"
|
||||||
|
app_gid="$(env_value WWWGROUP)"
|
||||||
|
app_port="$(env_value APP_PORT)"
|
||||||
|
app_uid="${app_uid:-1000}"
|
||||||
|
app_gid="${app_gid:-1000}"
|
||||||
|
app_port="${app_port:-8080}"
|
||||||
|
|
||||||
|
mkdir -p storage/app/public storage/framework/cache/data storage/framework/sessions storage/framework/views storage/logs
|
||||||
|
if [[ "$(id -u)" -eq 0 ]]; then
|
||||||
|
chown -R "${app_uid}:${app_gid}" storage
|
||||||
|
fi
|
||||||
|
chmod -R ug+rwX storage
|
||||||
|
|
||||||
|
log "Собираю production-образы"
|
||||||
|
prod build app web
|
||||||
|
|
||||||
|
log "Запускаю новый PHP-FPM"
|
||||||
|
prod up -d --no-deps app
|
||||||
|
wait_for_health app
|
||||||
|
|
||||||
|
log "Выполняю миграции и прогреваю кэш"
|
||||||
|
prod exec -T --user "${app_uid}:${app_gid}" app php artisan migrate --force
|
||||||
|
prod exec -T --user "${app_uid}:${app_gid}" app php artisan optimize
|
||||||
|
|
||||||
|
legacy_id="$(legacy ps -q laravel.test 2>/dev/null || true)"
|
||||||
|
if [[ -n "$legacy_id" ]] && [[ "$(docker inspect -f '{{.State.Running}}' "$legacy_id")" == true ]]; then
|
||||||
|
LEGACY_WAS_RUNNING=1
|
||||||
|
log "Останавливаю legacy Sail-контейнер"
|
||||||
|
legacy stop laravel.test
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Переключаю nginx на production web-контейнер"
|
||||||
|
if ! prod up -d --no-deps web || ! wait_for_health web; then
|
||||||
|
rollback_legacy
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! curl --fail --silent --max-time 15 "http://127.0.0.1:${app_port}/" >/dev/null; then
|
||||||
|
rollback_legacy
|
||||||
|
fail "HTTP-проверка production web завершилась ошибкой"
|
||||||
|
fi
|
||||||
|
|
||||||
|
log "Production-деплой завершён: app и web healthy"
|
||||||
|
prod ps app web
|
||||||
|
}
|
||||||
|
|
||||||
|
main "$@"
|
||||||
Reference in New Issue
Block a user