Перейти к основному содержимому

Docker развертывание

Контейнеризация приложения Arisweb Marketplace для упрощения развертывания и масштабирования.

Обзор Docker архитектуры

Структура контейнеров

services:
frontend:
image: arisweb-marketplace-frontend
container_name: marketplace-frontend

supabase:
image: supabase/postgres
container_name: marketplace-db

redis:
image: redis:alpine
container_name: marketplace-cache

nginx:
image: nginx:alpine
container_name: marketplace-proxy

Dockerfile для фронтенда

Многоэтапная сборка

# Этап сборки
FROM node:18-alpine AS builder

WORKDIR /app

# Копирование файлов зависимостей
COPY package*.json ./
COPY bun.lockb ./

# Установка зависимостей
RUN npm ci --only=production

# Копирование исходного кода
COPY . .

# Сборка приложения
RUN npm run build

# Производственный этап
FROM nginx:alpine AS production

# Копирование собранного приложения
COPY --from=builder /app/dist /usr/share/nginx/html

# Копирование конфигурации Nginx
COPY nginx.conf /etc/nginx/nginx.conf

# Экспозиция порта
EXPOSE 80

# Запуск Nginx
CMD ["nginx", "-g", "daemon off;"]

Оптимизированный Dockerfile

FROM node:18-alpine AS base

# Установка глобальных зависимостей
RUN apk add --no-cache libc6-compat
RUN corepack enable && corepack prepare pnpm@latest --activate

WORKDIR /app

FROM base AS deps
# Установка зависимостей
COPY package.json pnpm-lock.yaml ./
RUN pnpm install --frozen-lockfile

FROM base AS builder
COPY --from=deps /app/node_modules ./node_modules
COPY . .

# Сборка приложения
RUN pnpm run build

FROM nginx:alpine AS runner
WORKDIR /usr/share/nginx/html

# Удаление дефолтных файлов nginx
RUN rm -rf ./*

# Копирование собранного приложения
COPY --from=builder /app/dist .
COPY --from=builder /app/nginx.conf /etc/nginx/nginx.conf

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

Docker Compose конфигурация

Полная конфигурация

version: '3.8'

services:
# Фронтенд приложение
frontend:
build:
context: .
dockerfile: Dockerfile
target: production
image: arisweb-marketplace:latest
container_name: marketplace-frontend
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
- ./ssl:/etc/nginx/ssl:ro
environment:
- NODE_ENV=production
depends_on:
- postgres
- redis
restart: unless-stopped
networks:
- marketplace-network

# База данных PostgreSQL
postgres:
image: supabase/postgres:15.1.0.117
container_name: marketplace-postgres
ports:
- "5432:5432"
environment:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD}
POSTGRES_DB: marketplace
volumes:
- postgres_data:/var/lib/postgresql/data
- ./migrations:/docker-entrypoint-initdb.d
restart: unless-stopped
networks:
- marketplace-network

# Redis для кеширования
redis:
image: redis:7-alpine
container_name: marketplace-redis
ports:
- "6379:6379"
command: redis-server --appendonly yes --requirepass ${REDIS_PASSWORD}
volumes:
- redis_data:/data
restart: unless-stopped
networks:
- marketplace-network

# Supabase Auth
auth:
image: supabase/gotrue:v2.99.0
container_name: marketplace-auth
depends_on:
- postgres
restart: unless-stopped
environment:
GOTRUE_API_HOST: 0.0.0.0
GOTRUE_API_PORT: 9999
GOTRUE_DB_DRIVER: postgres
GOTRUE_DB_DATABASE_URL: postgres://postgres:${POSTGRES_PASSWORD}@postgres:5432/marketplace
GOTRUE_SITE_URL: ${SITE_URL}
GOTRUE_URI_ALLOW_LIST: ${ADDITIONAL_REDIRECT_URLS}
GOTRUE_JWT_SECRET: ${JWT_SECRET}
GOTRUE_JWT_EXP: ${JWT_EXPIRY}
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
GOTRUE_JWT_ADMIN_ROLES: service_role
GOTRUE_JWT_AUD: authenticated
GOTRUE_JWT_DEFAULT_GROUP_NAME: authenticated
networks:
- marketplace-network

# Supabase Realtime
realtime:
image: supabase/realtime:v2.25.35
container_name: marketplace-realtime
depends_on:
- postgres
restart: unless-stopped
environment:
PORT: 4000
DB_HOST: postgres
DB_PORT: 5432
DB_USER: postgres
DB_PASSWORD: ${POSTGRES_PASSWORD}
DB_NAME: marketplace
DB_AFTER_CONNECT_QUERY: 'SET search_path TO _realtime'
DB_ENC_KEY: supabaserealtime
API_JWT_SECRET: ${JWT_SECRET}
SECRET_KEY_BASE: ${SECRET_KEY_BASE}
command: >
bash -c "
./prod/rel/realtime/bin/realtime eval Realtime.Release.migrate &&
./prod/rel/realtime/bin/realtime start"
networks:
- marketplace-network

# Nginx Load Balancer
nginx:
image: nginx:alpine
container_name: marketplace-nginx
ports:
- "80:80"
- "443:443"
volumes:
- ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
- ./nginx/ssl:/etc/ssl:ro
- nginx_logs:/var/log/nginx
depends_on:
- frontend
restart: unless-stopped
networks:
- marketplace-network

volumes:
postgres_data:
driver: local
redis_data:
driver: local
nginx_logs:
driver: local

networks:
marketplace-network:
driver: bridge

Nginx конфигурация

Основная конфигурация

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

# Логирование
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;
error_log /var/log/nginx/error.log warn;

# Основные настройки
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
client_max_body_size 50M;

# Сжатие
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/json
application/javascript
application/xml+rss
application/atom+xml
image/svg+xml;

# Кеширование
map $sent_http_content_type $expires {
default off;
text/html 1h;
text/css 1y;
application/javascript 1y;
~image/ 1y;
}

# Основной сервер
server {
listen 80;
server_name _;
expires $expires;

# Основная локация для SPA
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;

# Безопасность
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;
}

# API проксирование
location /api/ {
proxy_pass http://backend:3000/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache_bypass $http_upgrade;
}

# Статические ресурсы
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
expires 1y;
add_header Cache-Control "public, immutable";
}

# Проверка здоровья
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}

# HTTPS сервер
server {
listen 443 ssl http2;
server_name _;

ssl_certificate /etc/ssl/certificate.crt;
ssl_certificate_key /etc/ssl/private.key;
ssl_session_timeout 1d;
ssl_session_cache shared:MozTLS:10m;
ssl_session_tickets off;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;

# HSTS
add_header Strict-Transport-Security "max-age=63072000" always;

# Остальная конфигурация аналогична HTTP серверу
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
}
}

Environment файлы

.env.production

# Основные настройки
NODE_ENV=production
VITE_APP_TITLE=Arisweb Marketplace

# Supabase
SUPABASE_URL=http://localhost:8000
SUPABASE_ANON_KEY=your_anon_key
SUPABASE_SERVICE_ROLE_KEY=your_service_role_key

# База данных
POSTGRES_PASSWORD=your_secure_password
DATABASE_URL=postgresql://postgres:password@postgres:5432/marketplace

# JWT
JWT_SECRET=your_jwt_secret_at_least_32_characters
JWT_EXPIRY=3600

# Redis
REDIS_PASSWORD=your_redis_password
REDIS_URL=redis://:password@redis:6379

# Домен
SITE_URL=https://your-domain.com
ADDITIONAL_REDIRECT_URLS=https://your-domain.com/**

# API ключи
RESEND_API_KEY=your_resend_api_key
YUKASSA_SECRET_KEY=your_yukassa_secret
OPENAI_API_KEY=your_openai_key

Скрипты управления

build.sh

#!/bin/bash

set -e

echo "Building Arisweb Marketplace..."

# Создание production образа
docker build -t arisweb-marketplace:latest .

# Создание тега с версией
VERSION=$(cat package.json | grep version | head -1 | awk -F: '{ print $2 }' | sed 's/[",]//g' | tr -d '[[:space:]]')
docker tag arisweb-marketplace:latest arisweb-marketplace:$VERSION

echo "Build completed. Version: $VERSION"

deploy.sh

#!/bin/bash

set -e

echo "Deploying Arisweb Marketplace..."

# Остановка существующих контейнеров
docker-compose down

# Обновление образов
docker-compose pull

# Запуск сервисов
docker-compose up -d

# Ожидание готовности сервисов
echo "Waiting for services to be ready..."
sleep 30

# Проверка здоровья
if curl -f http://localhost/health; then
echo "Deployment successful!"
else
echo "Deployment failed!"
exit 1
fi

backup.sh

#!/bin/bash

set -e

BACKUP_DIR="/backups"
DATE=$(date +%Y%m%d_%H%M%S)

echo "Creating backup..."

# Создание директории для бэкапов
mkdir -p $BACKUP_DIR

# Бэкап базы данных
docker exec marketplace-postgres pg_dump -U postgres marketplace > $BACKUP_DIR/db_backup_$DATE.sql

# Бэкап Redis
docker exec marketplace-redis redis-cli --rdb $BACKUP_DIR/redis_backup_$DATE.rdb

# Сжатие бэкапов
tar -czf $BACKUP_DIR/marketplace_backup_$DATE.tar.gz $BACKUP_DIR/*_$DATE.*

# Удаление несжатых файлов
rm $BACKUP_DIR/*_$DATE.sql $BACKUP_DIR/*_$DATE.rdb

echo "Backup completed: marketplace_backup_$DATE.tar.gz"

Мониторинг и логирование

Docker Health Checks

# В Dockerfile
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD curl -f http://localhost/health || exit 1

Логирование

# В docker-compose.yml
services:
frontend:
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"

Масштабирование

Docker Swarm

version: '3.8'

services:
frontend:
image: arisweb-marketplace:latest
deploy:
replicas: 3
restart_policy:
condition: on-failure
delay: 5s
max_attempts: 3
resources:
limits:
cpus: '0.5'
memory: 512M
reservations:
cpus: '0.25'
memory: 256M
networks:
- marketplace-overlay

networks:
marketplace-overlay:
driver: overlay
attachable: true

Kubernetes готовность

# k8s/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: marketplace-frontend
spec:
replicas: 3
selector:
matchLabels:
app: marketplace-frontend
template:
metadata:
labels:
app: marketplace-frontend
spec:
containers:
- name: frontend
image: arisweb-marketplace:latest
ports:
- containerPort: 80
resources:
requests:
memory: "256Mi"
cpu: "250m"
limits:
memory: "512Mi"
cpu: "500m"
livenessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health
port: 80
initialDelaySeconds: 5
periodSeconds: 5