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