From 5c8efe264476bc7bb8040814ea03b72f49046c58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=A1=D0=B5=D1=80=D0=B3=D0=B5=D0=B9=20=D0=90=D0=BD=D1=82?= =?UTF-8?q?=D1=80=D0=BE=D0=BF=D0=BE=D0=B2?= Date: Tue, 19 Aug 2025 01:06:23 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5?= =?UTF-8?q?=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=BB=D0=BD=D0=B0=D1=8F=20=D0=B4?= =?UTF-8?q?=D0=BE=D0=BA=D1=83=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=86=D0=B8?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20LogBoard+?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Создан основной README.md с описанием проекта - Добавлена подробная документация в папке docs/ - Создан файл LICENSE (MIT) - Обновлен .gitignore - Добавлена документация по безопасности с генерацией ключей - Включены примеры конфигураций и устранение неполадок Автор: Сергей Антропов Сайт: https://devops.org.ru --- .gitignore | 90 +++- LICENSE | 21 + README.md | 385 +++++++++++++++++ docs/api.md | 657 +++++++++++++++++++++++++++++ docs/configuration.md | 503 ++++++++++++++++++++++ docs/index.md | 313 ++++++++++++++ docs/installation.md | 430 +++++++++++++++++++ docs/security.md | 750 +++++++++++++++++++++++++++++++++ docs/troubleshooting.md | 767 +++++++++++++++++++++++++++++++++ docs/websocket.md | 913 ++++++++++++++++++++++++++++++++++++++++ templates/index.html | 193 +++++---- 11 files changed, 4932 insertions(+), 90 deletions(-) create mode 100644 LICENSE create mode 100644 README.md create mode 100644 docs/api.md create mode 100644 docs/configuration.md create mode 100644 docs/index.md create mode 100644 docs/installation.md create mode 100644 docs/security.md create mode 100644 docs/troubleshooting.md create mode 100644 docs/websocket.md diff --git a/.gitignore b/.gitignore index 51e4ff9..5478292 100644 --- a/.gitignore +++ b/.gitignore @@ -13,16 +13,9 @@ logs/ log/ -# Временные файлы -*.tmp -*.temp -*.swp -*.swo -*~ - # Снимки логов -snapshots/ -*.snapshot +snapshots/*.log +snapshots/*.txt # Python __pycache__/ @@ -73,7 +66,82 @@ Thumbs.db # Docker .dockerignore -# Backup files +# Temporary files +*.tmp +*.temp *.bak *.backup -*.old + +# Database +*.db +*.sqlite +*.sqlite3 + +# Certificates +*.pem +*.key +*.crt +*.csr + +# Backup files +backup/ +backups/ + +# Test coverage +htmlcov/ +.tox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Jupyter Notebook +.ipynb_checkpoints + +# pyenv +.python-version + +# pipenv +Pipfile.lock + +# PEP 582 +__pypackages__/ + +# Celery +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# Local development +local/ +dev/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..339de82 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Сергей Антропов + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..c74b81b --- /dev/null +++ b/README.md @@ -0,0 +1,385 @@ +# LogBoard+ + +**Веб-панель для просмотра логов микросервисов** + +[![Python](https://img.shields.io/badge/Python-3.11-blue.svg)](https://www.python.org/) +[![FastAPI](https://img.shields.io/badge/FastAPI-0.104.1-green.svg)](https://fastapi.tiangolo.com/) +[![Docker](https://img.shields.io/badge/Docker-6.1.3-blue.svg)](https://www.docker.com/) +[![License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru +**Версия:** 1.0.0 + +## Описание + +LogBoard+ - это современная веб-панель для мониторинга и просмотра логов Docker контейнеров в реальном времени. Приложение предоставляет удобный веб-интерфейс для работы с логами микросервисов, поддерживает множественные проекты Docker Compose и включает в себя функции безопасности. + +### Основные возможности + +- **Просмотр логов в реальном времени** - WebSocket соединения для live-логов +- **Поддержка множественных проектов** - Фильтрация по проектам Docker Compose +- **Безопасность** - JWT аутентификация и авторизация +- **Фильтрация контейнеров** - Исключение проблемных контейнеров +- **Снимки логов** - Сохранение логов в файлы для анализа +- **Статистика** - Анализ уровней логирования +- **Адаптивный интерфейс** - Поддержка светлой и темной темы +- **WebSocket API** - Для интеграции с внешними системами + +## Скриншоты + +### Светлая тема +![Светлая тема](screenshots/light.png) + +### Темная тема +![Темная тема](screenshots/dark.png) + +## Быстрый старт + +### Предварительные требования + +- Docker Engine 20.10+ +- Docker Compose 2.0+ +- 1 GB RAM +- 1 CPU core + +### Установка и запуск + +1. **Клонирование репозитория** + ```bash + git clone + cd logboard + ``` + +2. **Настройка переменных окружения** + ```bash + make setup + # Отредактируйте файл .env под свои нужды + ``` + +3. **Запуск приложения** + ```bash + make up + ``` + +4. **Доступ к веб-интерфейсу** + ``` + http://localhost:9001 + ``` + +### Учетные данные по умолчанию + +- **Пользователь:** `admin` +- **Пароль:** `admin` + +**Важно:** Обязательно измените пароль в продакшене! + +## Архитектура + +### Технологический стек + +- **Backend:** Python 3.11, FastAPI 0.104.1 +- **Web Server:** Uvicorn с uvloop +- **Docker Integration:** Docker SDK for Python 6.1.3 +- **Authentication:** JWT с PyJWT +- **Frontend:** HTML5, CSS3, JavaScript (Vanilla) +- **Templates:** Jinja2 +- **Containerization:** Docker, Docker Compose + +### Структура проекта + +``` +logboard/ +├── app.py # Основное приложение FastAPI +├── docker-compose.yml # Конфигурация Docker Compose +├── Dockerfile # Образ Docker +├── requirements.txt # Зависимости Python +├── Makefile # Команды управления +├── env.example # Пример переменных окружения +├── excluded_containers.json # Исключенные контейнеры +├── templates/ # HTML шаблоны +│ ├── index.html # Главная страница +│ ├── login.html # Страница входа +│ └── error.html # Страницы ошибок +├── snapshots/ # Снимки логов +└── screenshots/ # Скриншоты интерфейса +``` + +## API Документация + +### REST API + +#### Аутентификация + +- `POST /api/auth/login` - Вход в систему +- `POST /api/auth/logout` - Выход из системы +- `GET /api/auth/me` - Информация о текущем пользователе + +#### Контейнеры и сервисы + +- `GET /api/services` - Список контейнеров +- `GET /api/projects` - Список проектов Docker Compose +- `GET /api/logs/{container_id}` - Логи контейнера +- `GET /api/logs/stats/{container_id}` - Статистика логов + +#### Управление + +- `GET /api/settings` - Настройки приложения +- `GET /api/excluded-containers` - Список исключенных контейнеров +- `POST /api/excluded-containers` - Обновление исключенных контейнеров +- `POST /api/snapshot` - Создание снимка логов + +### WebSocket API + +- `ws://host:port/ws/logs/{container_id}` - Логи отдельного контейнера +- `ws://host:port/ws/fan/{service_name}` - Логи сервиса (все реплики) +- `ws://host:port/ws/fan_group` - Логи группы сервисов + +## Конфигурация + +### Переменные окружения + +Основные настройки приложения: + +| Переменная | Описание | По умолчанию | +|------------|----------|--------------| +| `LOGBOARD_PORT` | Порт веб-интерфейса | `9001` | +| `LOGBOARD_USER` | Имя пользователя | `admin` | +| `LOGBOARD_PASS` | Пароль пользователя | `admin` | +| `LOGBOARD_TAIL` | Количество строк логов | `500` | +| `SECRET_KEY` | Секретный ключ JWT | `your-secret-key-here` | + +### Настройка проектов + +Для фильтрации контейнеров по проектам Docker Compose: + +```bash +# Один проект +COMPOSE_PROJECT_NAME=myproject + +# Несколько проектов +LOGBOARD_PROJECTS=project1,project2,project3 +``` + +### Исключение контейнеров + +Создайте файл `excluded_containers.json`: + +```json +{ + "excluded_containers": [ + "noisy-container-1", + "noisy-container-2" + ], + "description": "Контейнеры с избыточным логированием" +} +``` + +## Управление + +### Команды Makefile + +```bash +make help # Справка по командам +make setup # Настройка переменных окружения +make build # Сборка Docker образа +make up # Запуск сервисов +make down # Остановка сервисов +make restart # Перезапуск сервисов +make logs # Просмотр логов +make clean # Очистка проекта +make status # Статус сервисов +make shell # Подключение к контейнеру +``` + +### Docker Compose команды + +```bash +# Запуск +docker compose up -d + +# Остановка +docker compose down + +# Просмотр логов +docker compose logs -f + +# Пересборка +docker compose build --no-cache +``` + +## Безопасность + +### Рекомендации для продакшена + +1. **Измените пароли по умолчанию** + ```bash + LOGBOARD_PASS=your-secure-password + ``` + +2. **Настройте секретные ключи** + ```bash + SECRET_KEY=your-very-secure-secret-key + ENCRYPTION_KEY=your-encryption-key + ``` + +3. **Используйте HTTPS** + - Настройте reverse proxy (nginx, traefik) + - Включите SSL/TLS сертификаты + +4. **Ограничьте доступ к Docker socket** + ```bash + # Создайте группу docker и добавьте пользователя + sudo usermod -aG docker $USER + ``` + +5. **Настройте файрвол** + ```bash + # Ограничьте доступ к порту 9001 + sudo ufw allow from 192.168.1.0/24 to any port 9001 + ``` + +### Аутентификация + +Приложение использует JWT токены для аутентификации: + +- Токены хранятся в HTTP-only cookies +- Время жизни токена настраивается через `SESSION_TIMEOUT` +- Поддерживается автоматическое обновление токенов + +## Мониторинг и логирование + +### Health Check + +```bash +curl http://localhost:9001/healthz +# Ответ: ok +``` + +### Логирование + +Приложение логирует: +- Ошибки подключения к Docker +- Проблемы с контейнерами +- Ошибки аутентификации +- WebSocket соединения + +### Метрики + +Доступные метрики: +- Количество активных WebSocket соединений +- Статистика по уровням логирования +- Количество исключенных контейнеров +- Время ответа API + +## Разработка + +### Локальная разработка + +1. **Клонирование и настройка** + ```bash + git clone + cd logboard + make setup + ``` + +2. **Запуск в режиме разработки** + ```bash + make up + ``` + +3. **Просмотр логов** + ```bash + make logs + ``` + +### Тестирование + +```bash +# Проверка конфигурации +make validate + +# Проверка переменных окружения +make env-check + +# Тестирование API +curl -X GET "http://localhost:9001/api/services" \ + -H "Authorization: Bearer YOUR_TOKEN" +``` + +## Устранение неполадок + +### Частые проблемы + +1. **Ошибка подключения к Docker** + ```bash + # Проверьте права доступа к Docker socket + ls -la /var/run/docker.sock + sudo usermod -aG docker $USER + ``` + +2. **Контейнеры не отображаются** + ```bash + # Проверьте фильтры проектов + docker ps --format "table {{.Names}}\t{{.Labels}}" + ``` + +3. **Ошибки аутентификации** + ```bash + # Проверьте переменные окружения + make env-check + ``` + +4. **WebSocket соединения не работают** + ```bash + # Проверьте настройки прокси + # Убедитесь, что WebSocket поддерживается + ``` + +### Логи и отладка + +```bash +# Просмотр логов приложения +make logs + +# Подключение к контейнеру +make shell + +# Проверка статуса +make status +``` + +## Лицензия + +MIT License - см. файл [LICENSE](LICENSE) для подробностей. + +## Поддержка + +- **Автор:** Сергей Антропов +- **Сайт:** https://devops.org.ru +- **Issues:** Создавайте issues в репозитории проекта +- **Документация:** [./docs/](./docs/) + +## Вклад в проект + +1. Fork репозитория +2. Создайте feature branch (`git checkout -b feature/amazing-feature`) +3. Commit изменения (`git commit -m 'Add amazing feature'`) +4. Push в branch (`git push origin feature/amazing-feature`) +5. Откройте Pull Request + +## Changelog + +### v1.0.0 (2024-01-XX) +- Первый релиз LogBoard+ +- Поддержка множественных проектов Docker Compose +- JWT аутентификация +- WebSocket API для live-логов +- Адаптивный веб-интерфейс +- Система исключения контейнеров +- Снимки логов +- Статистика логирования + +--- + +**LogBoard+** - Удобный просмотр логов микросервисов в реальном времени. diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..c4eca52 --- /dev/null +++ b/docs/api.md @@ -0,0 +1,657 @@ +# API Документация LogBoard+ + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +## Содержание + +1. [Обзор API](#обзор-api) +2. [Аутентификация](#аутентификация) +3. [REST API](#rest-api) +4. [WebSocket API](#websocket-api) +5. [Коды ошибок](#коды-ошибок) +6. [Примеры использования](#примеры-использования) + +## Обзор API + +LogBoard+ предоставляет REST API и WebSocket API для работы с логами Docker контейнеров. + +### Базовый URL + +``` +http://localhost:9001 +``` + +### Форматы данных + +- **Content-Type:** `application/json` +- **Кодировка:** UTF-8 +- **Временные метки:** ISO 8601 (UTC) + +### Аутентификация + +Все API endpoints (кроме `/healthz` и `/api/auth/login`) требуют JWT токен в заголовке: + +``` +Authorization: Bearer +``` + +## Аутентификация + +### POST /api/auth/login + +Вход в систему и получение JWT токена. + +**Запрос:** + +```json +{ + "username": "admin", + "password": "your-password" +} +``` + +**Ответ:** + +```json +{ + "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", + "token_type": "bearer" +} +``` + +**Пример curl:** + +```bash +curl -X POST "http://localhost:9001/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"your-password"}' +``` + +### POST /api/auth/logout + +Выход из системы (удаление токена из cookies). + +**Ответ:** + +```json +{ + "message": "Успешный выход из системы" +} +``` + +### GET /api/auth/me + +Получение информации о текущем пользователе. + +**Заголовки:** +``` +Authorization: Bearer +``` + +**Ответ:** + +```json +{ + "username": "admin" +} +``` + +## REST API + +### Контейнеры и сервисы + +#### GET /api/services + +Получение списка всех контейнеров. + +**Параметры запроса:** + +| Параметр | Тип | Описание | По умолчанию | +|----------|-----|----------|--------------| +| `projects` | string | Фильтр по проектам (через запятую) | Все проекты | +| `include_stopped` | boolean | Включить остановленные контейнеры | false | + +**Пример запроса:** + +```bash +curl -X GET "http://localhost:9001/api/services?projects=myproject&include_stopped=true" \ + -H "Authorization: Bearer YOUR_TOKEN" +``` + +**Ответ:** + +```json +[ + { + "id": "abc123def456", + "name": "myproject_web_1", + "status": "running", + "image": "nginx:latest", + "service": "web", + "project": "myproject", + "health": "healthy", + "ports": ["80/tcp"], + "url": "http://localhost:8080", + "host_port": "8080" + }, + { + "id": "def456ghi789", + "name": "myproject_db_1", + "status": "running", + "image": "postgres:13", + "service": "db", + "project": "myproject", + "health": "healthy", + "ports": ["5432/tcp"], + "url": null, + "host_port": null + } +] +``` + +#### GET /api/projects + +Получение списка всех проектов Docker Compose. + +**Ответ:** + +```json +[ + "myproject", + "another-project", + "standalone" +] +``` + +### Логи + +#### GET /api/logs/{container_id} + +Получение логов контейнера. + +**Параметры пути:** +- `container_id` - ID контейнера (первые 12 символов) + +**Параметры запроса:** + +| Параметр | Тип | Описание | По умолчанию | +|----------|-----|----------|--------------| +| `tail` | string | Количество строк или 'all' | 500 | +| `since` | string | Время начала (ISO 8601 или относительное) | null | + +**Пример запроса:** + +```bash +curl -X GET "http://localhost:9001/api/logs/abc123def456?tail=100&since=2024-01-15T10:00:00Z" \ + -H "Authorization: Bearer YOUR_TOKEN" +``` + +**Ответ:** + +```json +{ + "container": { + "id": "abc123def456", + "name": "myproject_web_1", + "status": "running", + "image": "nginx:latest", + "created": "2024-01-15T09:00:00.000000000Z", + "state": { + "Status": "running", + "Running": true, + "Paused": false, + "Restarting": false, + "OOMKilled": false, + "Dead": false, + "Pid": 1234, + "ExitCode": 0, + "Error": "", + "StartedAt": "2024-01-15T09:00:00.000000000Z", + "FinishedAt": "0001-01-01T00:00:00Z" + } + }, + "logs": [ + { + "timestamp": "2024-01-15T10:30:15.123456789Z", + "message": "2024/01/15 10:30:15 [notice] 1#1: start worker processes", + "raw": "2024-01-15T10:30:15.123456789Z 2024/01/15 10:30:15 [notice] 1#1: start worker processes" + }, + { + "timestamp": "2024-01-15T10:30:15.123456789Z", + "message": "2024/01/15 10:30:15 [notice] 1#1: start worker process 1234", + "raw": "2024-01-15T10:30:15.123456789Z 2024/01/15 10:30:15 [notice] 1#1: start worker process 1234" + } + ], + "total_lines": 2, + "tail": "100", + "since": "2024-01-15T10:00:00Z", + "timestamp": "2024-01-15T10:30:15.123456789Z" +} +``` + +#### GET /api/logs/stats/{container_id} + +Получение статистики логов контейнера. + +**Ответ:** + +```json +{ + "debug": 15, + "info": 42, + "warn": 8, + "error": 3 +} +``` + +### Управление + +#### GET /api/settings + +Получение настроек приложения. + +**Ответ:** + +```json +{ + "ajax_update_interval": 2000, + "default_tail": 500, + "skip_unhealthy": true +} +``` + +#### GET /api/excluded-containers + +Получение списка исключенных контейнеров. + +**Ответ:** + +```json +{ + "excluded_containers": [ + "noisy-container-1", + "noisy-container-2" + ] +} +``` + +#### POST /api/excluded-containers + +Обновление списка исключенных контейнеров. + +**Запрос:** + +```json +[ + "noisy-container-1", + "noisy-container-2", + "another-noisy-container" +] +``` + +**Ответ:** + +```json +{ + "status": "success", + "message": "Список исключенных контейнеров обновлен" +} +``` + +#### POST /api/snapshot + +Создание снимка логов. + +**Запрос:** + +```json +{ + "container_id": "abc123def456", + "service": "web", + "content": "Логи контейнера..." +} +``` + +**Ответ:** + +```json +{ + "file": "web-20240115-103015.log", + "url": "/snapshots/web-20240115-103015.log" +} +``` + +### Системные + +#### GET /healthz + +Health check endpoint. + +**Ответ:** + +``` +ok +``` + +## WebSocket API + +### Подключение + +Все WebSocket endpoints требуют JWT токен в параметре `token`. + +### ws://host:port/ws/logs/{container_id} + +Получение логов отдельного контейнера. + +**Параметры:** +- `container_id` - ID контейнера +- `tail` - Количество строк (по умолчанию 500) +- `token` - JWT токен +- `service` - Имя сервиса (опционально) +- `project` - Имя проекта (опционально) + +**Пример подключения:** + +```javascript +const token = "your-jwt-token"; +const containerId = "abc123def456"; +const ws = new WebSocket(`ws://localhost:9001/ws/logs/${containerId}?token=${token}&tail=100`); + +ws.onmessage = function(event) { + console.log('Получены логи:', event.data); +}; + +ws.onerror = function(error) { + console.error('WebSocket ошибка:', error); +}; +``` + +### ws://host:port/ws/fan/{service_name} + +Получение логов сервиса (все реплики). + +**Параметры:** +- `service_name` - Имя сервиса Docker Compose +- `tail` - Количество строк (по умолчанию 500) +- `token` - JWT токен +- `project` - Имя проекта (опционально) + +**Пример подключения:** + +```javascript +const token = "your-jwt-token"; +const serviceName = "web"; +const ws = new WebSocket(`ws://localhost:9001/ws/fan/${serviceName}?token=${token}&tail=100`); + +ws.onmessage = function(event) { + console.log('Логи сервиса:', event.data); +}; +``` + +### ws://host:port/ws/fan_group + +Получение логов группы сервисов. + +**Параметры:** +- `services` - Имена сервисов через запятую +- `tail` - Количество строк (по умолчанию 500) +- `token` - JWT токен +- `project` - Имя проекта (опционально) + +**Пример подключения:** + +```javascript +const token = "your-jwt-token"; +const services = "web,db,redis"; +const ws = new WebSocket(`ws://localhost:9001/ws/fan_group?services=${services}&token=${token}&tail=100`); + +ws.onmessage = function(event) { + console.log('Логи группы сервисов:', event.data); +}; +``` + +## Коды ошибок + +### HTTP коды состояния + +| Код | Описание | +|-----|----------| +| 200 | Успешный запрос | +| 400 | Неверный запрос | +| 401 | Не авторизован | +| 403 | Доступ запрещен | +| 404 | Ресурс не найден | +| 500 | Внутренняя ошибка сервера | + +### Формат ошибок + +```json +{ + "error": "error_type", + "message": "Описание ошибки", + "details": "Дополнительная информация" +} +``` + +### Примеры ошибок + +#### 401 Unauthorized + +```json +{ + "error": "unauthorized", + "message": "Требуется авторизация", + "details": "Для доступа к этому API необходимо войти в систему." +} +``` + +#### 404 Not Found + +```json +{ + "error": "http_404", + "message": "Контейнер не найден", + "details": "URL: /api/logs/invalid-id" +} +``` + +#### 500 Internal Server Error + +```json +{ + "error": "http_500", + "message": "Ошибка подключения к Docker", + "details": "URL: /api/services" +} +``` + +## Примеры использования + +### Python + +```python +import requests +import json + +# Базовый URL +BASE_URL = "http://localhost:9001" + +# Аутентификация +def login(username, password): + response = requests.post(f"{BASE_URL}/api/auth/login", json={ + "username": username, + "password": password + }) + return response.json()["access_token"] + +# Получение списка контейнеров +def get_containers(token): + headers = {"Authorization": f"Bearer {token}"} + response = requests.get(f"{BASE_URL}/api/services", headers=headers) + return response.json() + +# Получение логов контейнера +def get_logs(token, container_id, tail=100): + headers = {"Authorization": f"Bearer {token}"} + params = {"tail": tail} + response = requests.get(f"{BASE_URL}/api/logs/{container_id}", + headers=headers, params=params) + return response.json() + +# Использование +token = login("admin", "your-password") +containers = get_containers(token) +print(f"Найдено контейнеров: {len(containers)}") + +for container in containers: + logs = get_logs(token, container["id"], tail=10) + print(f"Контейнер {container['name']}: {len(logs['logs'])} строк логов") +``` + +### JavaScript + +```javascript +// Класс для работы с LogBoard+ API +class LogBoardAPI { + constructor(baseUrl, token) { + this.baseUrl = baseUrl; + this.token = token; + this.headers = { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json' + }; + } + + // Получение списка контейнеров + async getContainers(projects = null, includeStopped = false) { + const params = new URLSearchParams(); + if (projects) params.append('projects', projects); + if (includeStopped) params.append('include_stopped', 'true'); + + const response = await fetch(`${this.baseUrl}/api/services?${params}`, { + headers: this.headers + }); + return await response.json(); + } + + // Получение логов контейнера + async getLogs(containerId, tail = 100, since = null) { + const params = new URLSearchParams(); + params.append('tail', tail); + if (since) params.append('since', since); + + const response = await fetch(`${this.baseUrl}/api/logs/${containerId}?${params}`, { + headers: this.headers + }); + return await response.json(); + } + + // Получение статистики логов + async getLogStats(containerId) { + const response = await fetch(`${this.baseUrl}/api/logs/stats/${containerId}`, { + headers: this.headers + }); + return await response.json(); + } + + // WebSocket для live-логов + createLogsWebSocket(containerId, tail = 100) { + const ws = new WebSocket( + `ws://${this.baseUrl.replace('http://', '')}/ws/logs/${containerId}?token=${this.token}&tail=${tail}` + ); + return ws; + } +} + +// Использование +async function main() { + // Получение токена + const loginResponse = await fetch('http://localhost:9001/api/auth/login', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ + username: 'admin', + password: 'your-password' + }) + }); + const {access_token} = await loginResponse.json(); + + // Создание API клиента + const api = new LogBoardAPI('http://localhost:9001', access_token); + + // Получение контейнеров + const containers = await api.getContainers(); + console.log('Контейнеры:', containers); + + // Получение логов первого контейнера + if (containers.length > 0) { + const logs = await api.getLogs(containers[0].id, 50); + console.log('Логи:', logs); + + // WebSocket для live-логов + const ws = api.createLogsWebSocket(containers[0].id, 100); + ws.onmessage = (event) => { + console.log('Новые логи:', event.data); + }; + } +} + +main().catch(console.error); +``` + +### cURL + +```bash +#!/bin/bash + +# Переменные +BASE_URL="http://localhost:9001" +USERNAME="admin" +PASSWORD="your-password" + +# Получение токена +TOKEN=$(curl -s -X POST "$BASE_URL/api/auth/login" \ + -H "Content-Type: application/json" \ + -d "{\"username\":\"$USERNAME\",\"password\":\"$PASSWORD\"}" \ + | jq -r '.access_token') + +echo "Токен получен: ${TOKEN:0:20}..." + +# Получение списка контейнеров +echo "Получение списка контейнеров..." +curl -s -X GET "$BASE_URL/api/services" \ + -H "Authorization: Bearer $TOKEN" \ + | jq '.[] | {name: .name, status: .status, project: .project}' + +# Получение логов первого контейнера +CONTAINER_ID=$(curl -s -X GET "$BASE_URL/api/services" \ + -H "Authorization: Bearer $TOKEN" \ + | jq -r '.[0].id') + +echo "Получение логов контейнера $CONTAINER_ID..." +curl -s -X GET "$BASE_URL/api/logs/$CONTAINER_ID?tail=10" \ + -H "Authorization: Bearer $TOKEN" \ + | jq '.logs[] | .message' +``` + +## Ограничения и рекомендации + +### Ограничения + +- Максимальное количество строк логов: 10000 +- Таймаут WebSocket соединения: 60 секунд +- Максимальный размер снимка логов: 10 MB + +### Рекомендации + +1. **Используйте фильтрацию по времени** для больших объемов логов +2. **Настройте исключение контейнеров** с избыточным логированием +3. **Используйте WebSocket** для real-time мониторинга +4. **Регулярно очищайте снимки логов** для экономии места + +### Производительность + +- REST API: до 1000 запросов в минуту +- WebSocket: до 100 одновременных соединений +- Размер ответа: до 1 MB на запрос diff --git a/docs/configuration.md b/docs/configuration.md new file mode 100644 index 0000000..94cd009 --- /dev/null +++ b/docs/configuration.md @@ -0,0 +1,503 @@ +# Конфигурация LogBoard+ + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +## Содержание + +1. [Обзор конфигурации](#обзор-конфигурации) +2. [Переменные окружения](#переменные-окружения) +3. [Настройка Docker](#настройка-docker) +4. [Настройка безопасности](#настройка-безопасности) +5. [Настройка производительности](#настройка-производительности) +6. [Настройка логирования](#настройка-логирования) +7. [Примеры конфигураций](#примеры-конфигураций) + +## Обзор конфигурации + +LogBoard+ использует переменные окружения для настройки всех аспектов работы приложения. Конфигурация разделена на несколько категорий для удобства управления. + +### Файлы конфигурации + +- `.env` - Основной файл конфигурации (создается из `env.example`) +- `docker-compose.yml` - Конфигурация Docker Compose +- `excluded_containers.json` - Список исключенных контейнеров + +### Приоритет настроек + +1. Переменные окружения системы +2. Файл `.env` +3. Значения по умолчанию в коде + +## Переменные окружения + +### Основные настройки приложения + +| Переменная | Описание | По умолчанию | Обязательная | +|------------|----------|--------------|--------------| +| `LOGBOARD_PORT` | Порт веб-интерфейса | `9001` | Нет | +| `LOGBOARD_TAIL` | Количество строк логов по умолчанию | `500` | Нет | +| `LOGBOARD_USER` | Имя пользователя для входа | `admin` | Нет | +| `LOGBOARD_PASS` | Пароль пользователя | `admin` | Да | +| `LOGBOARD_SNAPSHOT_DIR` | Директория для снимков логов | `/app/snapshots` | Нет | +| `LOGBOARD_INDEX_HTML` | Путь к HTML шаблону | `./templates/index.html` | Нет | +| `TZ_TS` | Временная зона | `Europe/Moscow` | Нет | + +### Настройки Docker + +| Переменная | Описание | По умолчанию | Обязательная | +|------------|----------|--------------|--------------| +| `COMPOSE_PROJECT_NAME` | Фильтр по проекту Docker Compose | - | Нет | +| `LOGBOARD_PROJECTS` | Множественные проекты (через запятую) | - | Нет | +| `DOCKER_HOST` | Путь к Docker socket | `unix:///var/run/docker.sock` | Нет | +| `DOCKER_TLS_VERIFY` | Проверка TLS для Docker | - | Нет | +| `DOCKER_CERT_PATH` | Путь к сертификатам Docker | - | Нет | +| `DOCKER_NETWORKS` | Внешние сети Docker | `iaas,infrastructure_iaas` | Нет | + +### Настройки безопасности + +| Переменная | Описание | По умолчанию | Обязательная | +|------------|----------|--------------|--------------| +| `SECRET_KEY` | Секретный ключ для JWT | `your-secret-key-here` | Да | +| `ENCRYPTION_KEY` | Ключ шифрования | `your-encryption-key-here` | Да | +| `AUTH_ENABLED` | Включить аутентификацию | `true` | Нет | +| `AUTH_METHOD` | Метод аутентификации | `jwt` | Нет | +| `SESSION_TIMEOUT` | Время жизни сессии (секунды) | `3600` | Нет | + +### Настройки производительности + +| Переменная | Описание | По умолчанию | Обязательная | +|------------|----------|--------------|--------------| +| `MAX_CONNECTIONS` | Максимальное количество соединений | `100` | Нет | +| `CONNECTION_TIMEOUT` | Таймаут подключения (секунды) | `30` | Нет | +| `READ_TIMEOUT` | Таймаут чтения (секунды) | `60` | Нет | +| `LOGBOARD_AJAX_UPDATE_INTERVAL` | Интервал AJAX обновления (мс) | `2000` | Нет | + +### Настройки фильтрации контейнеров + +| Переменная | Описание | По умолчанию | Обязательная | +|------------|----------|--------------|--------------| +| `LOGBOARD_SKIP_UNHEALTHY` | Пропускать нездоровые контейнеры | `true` | Нет | +| `LOGBOARD_CONTAINER_LIST_TIMEOUT` | Таймаут получения списка контейнеров | `10` | Нет | +| `LOGBOARD_CONTAINER_INFO_TIMEOUT` | Таймаут получения информации о контейнере | `3` | Нет | +| `LOGBOARD_HEALTH_CHECK_TIMEOUT` | Таймаут health check | `2` | Нет | + +### Настройки логирования + +| Переменная | Описание | По умолчанию | Обязательная | +|------------|----------|--------------|--------------| +| `LOG_LEVEL` | Уровень логирования | `INFO` | Нет | +| `LOG_FORMAT` | Формат логов | `json` | Нет | + +### Настройки веб-интерфейса + +| Переменная | Описание | По умолчанию | Обязательная | +|------------|----------|--------------|--------------| +| `WEB_TITLE` | Заголовок веб-интерфейса | `LogBoard+` | Нет | +| `WEB_DESCRIPTION` | Описание веб-интерфейса | `Веб-панель для просмотра логов микросервисов` | Нет | +| `WEB_VERSION` | Версия веб-интерфейса | `1.0.0` | Нет | + +### Настройки уведомлений + +| Переменная | Описание | По умолчанию | Обязательная | +|------------|----------|--------------|--------------| +| `NOTIFICATIONS_ENABLED` | Включить уведомления | `false` | Нет | +| `SMTP_HOST` | SMTP сервер | - | Нет | +| `SMTP_PORT` | Порт SMTP | `587` | Нет | +| `SMTP_USER` | Пользователь SMTP | - | Нет | +| `SMTP_PASS` | Пароль SMTP | - | Нет | +| `SMTP_FROM` | Email отправителя | - | Нет | + +## Настройка Docker + +### Подключение к Docker daemon + +#### Локальный Docker + +```bash +# Стандартная конфигурация для локального Docker +DOCKER_HOST=unix:///var/run/docker.sock +``` + +#### Удаленный Docker + +```bash +# Подключение к удаленному Docker daemon +DOCKER_HOST=tcp://192.168.1.100:2375 + +# С TLS сертификатами +DOCKER_HOST=tcp://192.168.1.100:2376 +DOCKER_TLS_VERIFY=1 +DOCKER_CERT_PATH=/path/to/certs +``` + +### Фильтрация проектов + +#### Один проект + +```bash +# Показать только контейнеры из одного проекта +COMPOSE_PROJECT_NAME=myproject +``` + +#### Несколько проектов + +```bash +# Показать контейнеры из нескольких проектов +LOGBOARD_PROJECTS=project1,project2,project3 +``` + +#### Все проекты + +```bash +# Показать все контейнеры (по умолчанию) +# Оставьте переменные пустыми или не указывайте их +``` + +### Настройка сетей + +```bash +# Подключение к внешним Docker сетям +DOCKER_NETWORKS=iaas,infrastructure_iaas,frontend,backend +``` + +## Настройка безопасности + +### Генерация секретных ключей + +```bash +# Генерация секретного ключа для JWT +openssl rand -hex 32 +# 8a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6 + +# Генерация ключа шифрования +openssl rand -hex 32 +# 1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7 +``` + +### Настройка аутентификации + +```bash +# Включение аутентификации +AUTH_ENABLED=true +AUTH_METHOD=jwt + +# Настройка времени жизни сессии (1 час) +SESSION_TIMEOUT=3600 + +# Настройка пользователей +LOGBOARD_USER=admin +LOGBOARD_PASS=your-secure-password +``` + +### Настройка HTTPS + +Для продакшена рекомендуется использовать HTTPS через reverse proxy: + +```nginx +# Nginx конфигурация +server { + listen 443 ssl http2; + server_name your-domain.com; + + ssl_certificate /path/to/certificate.crt; + ssl_certificate_key /path/to/private.key; + + location / { + proxy_pass http://localhost:9001; + 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; + + # WebSocket поддержка + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +``` + +## Настройка производительности + +### Ограничения соединений + +```bash +# Максимальное количество одновременных подключений +MAX_CONNECTIONS=100 + +# Таймауты +CONNECTION_TIMEOUT=30 +READ_TIMEOUT=60 +``` + +### Настройка AJAX обновления + +```bash +# Интервал обновления логов (в миллисекундах) +LOGBOARD_AJAX_UPDATE_INTERVAL=2000 + +# Количество строк логов по умолчанию +LOGBOARD_TAIL=500 +``` + +### Оптимизация Docker + +```bash +# Таймауты для операций с Docker +LOGBOARD_CONTAINER_LIST_TIMEOUT=10 +LOGBOARD_CONTAINER_INFO_TIMEOUT=3 +LOGBOARD_HEALTH_CHECK_TIMEOUT=2 + +# Пропуск нездоровых контейнеров +LOGBOARD_SKIP_UNHEALTHY=true +``` + +## Настройка логирования + +### Уровни логирования + +```bash +# Доступные уровни: DEBUG, INFO, WARNING, ERROR +LOG_LEVEL=INFO + +# Форматы: json, text +LOG_FORMAT=json +``` + +### Настройка логов приложения + +```bash +# Создание директории для логов +sudo mkdir -p /var/log/logboard +sudo chown $USER:$USER /var/log/logboard + +# Обновление docker-compose.yml +volumes: + - /var/log/logboard:/app/logs +``` + +## Исключение контейнеров + +### Файл excluded_containers.json + +```json +{ + "excluded_containers": [ + "noisy-container-1", + "noisy-container-2", + "debug-container", + "test-container" + ], + "description": "Контейнеры с избыточным логированием, исключенные из отображения" +} +``` + +### Управление через API + +```bash +# Получение списка исключенных контейнеров +curl -X GET "http://localhost:9001/api/excluded-containers" \ + -H "Authorization: Bearer YOUR_TOKEN" + +# Обновление списка +curl -X POST "http://localhost:9001/api/excluded-containers" \ + -H "Authorization: Bearer YOUR_TOKEN" \ + -H "Content-Type: application/json" \ + -d '["container1", "container2"]' +``` + +## Примеры конфигураций + +### Разработка + +```bash +# .env для разработки +LOGBOARD_PORT=9001 +LOGBOARD_USER=admin +LOGBOARD_PASS=dev-password +SECRET_KEY=dev-secret-key-change-in-production +ENCRYPTION_KEY=dev-encryption-key-change-in-production + +# Docker настройки +DOCKER_HOST=unix:///var/run/docker.sock +LOGBOARD_PROJECTS=dev-project + +# Производительность +LOGBOARD_TAIL=100 +LOGBOARD_AJAX_UPDATE_INTERVAL=1000 +LOG_LEVEL=DEBUG + +# Безопасность +AUTH_ENABLED=true +SESSION_TIMEOUT=7200 +``` + +### Тестирование + +```bash +# .env для тестирования +LOGBOARD_PORT=9002 +LOGBOARD_USER=test +LOGBOARD_PASS=test-password +SECRET_KEY=test-secret-key +ENCRYPTION_KEY=test-encryption-key + +# Docker настройки +DOCKER_HOST=unix:///var/run/docker.sock +LOGBOARD_PROJECTS=test-project + +# Производительность +LOGBOARD_TAIL=50 +LOGBOARD_AJAX_UPDATE_INTERVAL=500 +LOG_LEVEL=INFO + +# Безопасность +AUTH_ENABLED=true +SESSION_TIMEOUT=1800 +``` + +### Продакшен + +```bash +# .env для продакшена +LOGBOARD_PORT=9001 +LOGBOARD_USER=admin +LOGBOARD_PASS=very-secure-password-here +SECRET_KEY=8a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6 +ENCRYPTION_KEY=1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7 + +# Docker настройки +DOCKER_HOST=unix:///var/run/docker.sock +LOGBOARD_PROJECTS=prod-project,monitoring + +# Производительность +LOGBOARD_TAIL=1000 +LOGBOARD_AJAX_UPDATE_INTERVAL=5000 +MAX_CONNECTIONS=200 +CONNECTION_TIMEOUT=60 +READ_TIMEOUT=120 + +# Безопасность +AUTH_ENABLED=true +SESSION_TIMEOUT=3600 + +# Логирование +LOG_LEVEL=WARNING +LOG_FORMAT=json + +# Уведомления +NOTIFICATIONS_ENABLED=true +SMTP_HOST=smtp.company.com +SMTP_PORT=587 +SMTP_USER=logboard@company.com +SMTP_PASS=email-password +SMTP_FROM=logboard@company.com +``` + +### Масштабированная установка + +```bash +# .env для масштабированной установки +LOGBOARD_PORT=9001 +LOGBOARD_USER=admin +LOGBOARD_PASS=very-secure-password +SECRET_KEY=8a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6 +ENCRYPTION_KEY=1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7 + +# Docker настройки +DOCKER_HOST=tcp://docker-swarm-manager:2376 +DOCKER_TLS_VERIFY=1 +DOCKER_CERT_PATH=/etc/docker/certs +LOGBOARD_PROJECTS=app1,app2,app3,monitoring + +# Производительность +LOGBOARD_TAIL=2000 +LOGBOARD_AJAX_UPDATE_INTERVAL=3000 +MAX_CONNECTIONS=500 +CONNECTION_TIMEOUT=120 +READ_TIMEOUT=300 + +# Безопасность +AUTH_ENABLED=true +SESSION_TIMEOUT=7200 + +# Логирование +LOG_LEVEL=INFO +LOG_FORMAT=json + +# Уведомления +NOTIFICATIONS_ENABLED=true +SMTP_HOST=smtp.company.com +SMTP_PORT=587 +SMTP_USER=logboard@company.com +SMTP_PASS=email-password +SMTP_FROM=logboard@company.com +``` + +## Проверка конфигурации + +### Валидация переменных окружения + +```bash +# Проверка конфигурации +make env-check + +# Проверка Docker Compose +make validate +``` + +### Тестирование подключения + +```bash +# Проверка подключения к Docker +docker ps + +# Проверка доступности API +curl http://localhost:9001/healthz + +# Проверка аутентификации +curl -X POST "http://localhost:9001/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"your-password"}' +``` + +### Мониторинг конфигурации + +```bash +# Просмотр текущих настроек +curl -X GET "http://localhost:9001/api/settings" \ + -H "Authorization: Bearer YOUR_TOKEN" + +# Проверка списка контейнеров +curl -X GET "http://localhost:9001/api/services" \ + -H "Authorization: Bearer YOUR_TOKEN" +``` + +## Рекомендации по безопасности + +### Обязательные настройки для продакшена + +1. **Измените пароли по умолчанию** +2. **Используйте сильные секретные ключи** +3. **Настройте HTTPS через reverse proxy** +4. **Ограничьте доступ к Docker socket** +5. **Настройте файрвол** +6. **Регулярно обновляйте зависимости** +7. **Мониторьте логи доступа** + +### Мониторинг безопасности + +```bash +# Проверка прав доступа к Docker socket +ls -la /var/run/docker.sock + +# Проверка пользователей в группе docker +getent group docker + +# Проверка открытых портов +netstat -tlnp | grep 9001 + +# Проверка логов аутентификации +grep "authentication" /var/log/logboard/app.log +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..a3fd0fa --- /dev/null +++ b/docs/index.md @@ -0,0 +1,313 @@ +# Документация LogBoard+ + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +## Обзор + +LogBoard+ - это современная веб-панель для мониторинга и просмотра логов Docker контейнеров в реальном времени. Приложение предоставляет удобный веб-интерфейс для работы с логами микросервисов, поддерживает множественные проекты Docker Compose и включает в себя функции безопасности. + +## Быстрый старт + +### Установка + +```bash +# Клонирование репозитория +git clone +cd logboard + +# Настройка переменных окружения +make setup + +# Запуск приложения +make up +``` + +### Доступ + +- **URL:** http://localhost:9001 +- **Пользователь:** `admin` +- **Пароль:** `admin` (обязательно измените в продакшене!) + +## Документация + +### Основные разделы + +1. **[Установка и настройка](./installation.md)** + - Предварительные требования + - Пошаговая установка + - Настройка для продакшена + - Устранение проблем установки + +2. **[API Документация](./api.md)** + - REST API endpoints + - WebSocket API + - Примеры использования + - Коды ошибок + +3. **[WebSocket API](./websocket.md)** + - Подключение к WebSocket + - Real-time логи + - Обработка ошибок + - Лучшие практики + +4. **[Конфигурация](./configuration.md)** + - Переменные окружения + - Настройка Docker + - Настройка безопасности + - Примеры конфигураций + +5. **[Безопасность](./security.md)** + - Генерация ключей + - Аутентификация + - Настройка HTTPS + - Мониторинг безопасности + +6. **[Устранение неполадок](./troubleshooting.md)** + - Общие проблемы + - Проблемы с Docker + - Проблемы с аутентификацией + - Диагностика и логи + +### Дополнительные ресурсы + +- **[README.md](../README.md)** - Основная документация проекта +- **[env.example](../env.example)** - Пример конфигурации +- **[Makefile](../Makefile)** - Команды управления + +## Архитектура + +### Технологический стек + +- **Backend:** Python 3.11, FastAPI 0.104.1 +- **Web Server:** Uvicorn с uvloop +- **Docker Integration:** Docker SDK for Python 6.1.3 +- **Authentication:** JWT с PyJWT +- **Frontend:** HTML5, CSS3, JavaScript (Vanilla) +- **Templates:** Jinja2 +- **Containerization:** Docker, Docker Compose + +### Основные возможности + +- **Просмотр логов в реальном времени** - WebSocket соединения для live-логов +- **Поддержка множественных проектов** - Фильтрация по проектам Docker Compose +- **Безопасность** - JWT аутентификация и авторизация +- **Фильтрация контейнеров** - Исключение проблемных контейнеров +- **Снимки логов** - Сохранение логов в файлы для анализа +- **Статистика** - Анализ уровней логирования +- **Адаптивный интерфейс** - Поддержка светлой и темной темы + +## API Endpoints + +### REST API + +#### Аутентификация +- `POST /api/auth/login` - Вход в систему +- `POST /api/auth/logout` - Выход из системы +- `GET /api/auth/me` - Информация о текущем пользователе + +#### Контейнеры и сервисы +- `GET /api/services` - Список контейнеров +- `GET /api/projects` - Список проектов Docker Compose +- `GET /api/logs/{container_id}` - Логи контейнера +- `GET /api/logs/stats/{container_id}` - Статистика логов + +#### Управление +- `GET /api/settings` - Настройки приложения +- `GET /api/excluded-containers` - Список исключенных контейнеров +- `POST /api/excluded-containers` - Обновление исключенных контейнеров +- `POST /api/snapshot` - Создание снимка логов + +### WebSocket API + +- `ws://host:port/ws/logs/{container_id}` - Логи отдельного контейнера +- `ws://host:port/ws/fan/{service_name}` - Логи сервиса (все реплики) +- `ws://host:port/ws/fan_group` - Логи группы сервисов + +## Конфигурация + +### Основные переменные окружения + +| Переменная | Описание | По умолчанию | +|------------|----------|--------------| +| `LOGBOARD_PORT` | Порт веб-интерфейса | `9001` | +| `LOGBOARD_USER` | Имя пользователя | `admin` | +| `LOGBOARD_PASS` | Пароль пользователя | `admin` | +| `LOGBOARD_TAIL` | Количество строк логов | `500` | +| `SECRET_KEY` | Секретный ключ JWT | `your-secret-key-here` | + +### Настройка проектов + +```bash +# Один проект +COMPOSE_PROJECT_NAME=myproject + +# Несколько проектов +LOGBOARD_PROJECTS=project1,project2,project3 +``` + +## Управление + +### Команды Makefile + +```bash +make help # Справка по командам +make setup # Настройка переменных окружения +make build # Сборка Docker образа +make up # Запуск сервисов +make down # Остановка сервисов +make restart # Перезапуск сервисов +make logs # Просмотр логов +make clean # Очистка проекта +make status # Статус сервисов +make shell # Подключение к контейнеру +``` + +### Docker Compose команды + +```bash +# Запуск +docker compose up -d + +# Остановка +docker compose down + +# Просмотр логов +docker compose logs -f + +# Пересборка +docker compose build --no-cache +``` + +## Безопасность + +### Рекомендации для продакшена + +1. **Измените пароли по умолчанию** +2. **Настройте секретные ключи** +3. **Используйте HTTPS** +4. **Ограничьте доступ к Docker socket** +5. **Настройте файрвол** + +### Аутентификация + +Приложение использует JWT токены для аутентификации: +- Токены хранятся в HTTP-only cookies +- Время жизни токена настраивается через `SESSION_TIMEOUT` +- Поддерживается автоматическое обновление токенов + +## Мониторинг и логирование + +### Health Check + +```bash +curl http://localhost:9001/healthz +# Ответ: ok +``` + +### Логирование + +Приложение логирует: +- Ошибки подключения к Docker +- Проблемы с контейнерами +- Ошибки аутентификации +- WebSocket соединения + +## Примеры использования + +### Python + +```python +import requests + +# Аутентификация +response = requests.post('http://localhost:9001/api/auth/login', json={ + 'username': 'admin', + 'password': 'your-password' +}) +token = response.json()['access_token'] + +# Получение списка контейнеров +headers = {'Authorization': f'Bearer {token}'} +containers = requests.get('http://localhost:9001/api/services', headers=headers).json() +``` + +### JavaScript + +```javascript +// Получение токена +const response = await fetch('http://localhost:9001/api/auth/login', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ + username: 'admin', + password: 'your-password' + }) +}); +const {access_token} = await response.json(); + +// WebSocket подключение +const ws = new WebSocket(`ws://localhost:9001/ws/logs/container-id?token=${access_token}`); +``` + +### cURL + +```bash +# Получение токена +TOKEN=$(curl -s -X POST "http://localhost:9001/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"your-password"}' \ + | jq -r '.access_token') + +# Получение списка контейнеров +curl -X GET "http://localhost:9001/api/services" \ + -H "Authorization: Bearer $TOKEN" +``` + +## Устранение неполадок + +### Частые проблемы + +1. **Ошибка подключения к Docker** + ```bash + sudo usermod -aG docker $USER + newgrp docker + ``` + +2. **Контейнеры не отображаются** + ```bash + docker ps --format "table {{.Names}}\t{{.Labels}}" + ``` + +3. **Ошибки аутентификации** + ```bash + make env-check + ``` + +### Диагностика + +```bash +# Проверка статуса +make status + +# Просмотр логов +make logs + +# Проверка конфигурации +make validate +``` + +## Поддержка + +- **Автор:** Сергей Антропов +- **Сайт:** https://devops.org.ru +- **Issues:** Создавайте issues в репозитории проекта +- **Документация:** [./docs/](./docs/) + +## Лицензия + +MIT License - см. файл [LICENSE](../LICENSE) для подробностей. + +--- + +**LogBoard+** - Удобный просмотр логов микросервисов в реальном времени. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..d8d55da --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,430 @@ +# Установка и настройка LogBoard+ + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +## Содержание + +1. [Предварительные требования](#предварительные-требования) +2. [Установка](#установка) +3. [Настройка](#настройка) +4. [Первый запуск](#первый-запуск) +5. [Проверка установки](#проверка-установки) +6. [Настройка для продакшена](#настройка-для-продакшена) + +## Предварительные требования + +### Системные требования + +- **Операционная система:** Linux (Ubuntu 20.04+, CentOS 8+, RHEL 8+) +- **Docker Engine:** 20.10.0 или выше +- **Docker Compose:** 2.0.0 или выше +- **RAM:** Минимум 1 GB, рекомендуется 2 GB +- **CPU:** Минимум 1 ядро, рекомендуется 2 ядра +- **Дисковое пространство:** Минимум 2 GB свободного места + +### Проверка системы + +```bash +# Проверка версии Docker +docker --version +# Docker version 20.10.21, build baeda1f + +# Проверка версии Docker Compose +docker compose version +# Docker Compose version v2.12.2 + +# Проверка доступной памяти +free -h +# total used free shared buff/cache available +# Mem: 7.7Gi 2.1Gi 4.2Gi 0.0Ki 1.4Gi 5.3Gi + +# Проверка свободного места +df -h / +# Filesystem Size Used Avail Use% Mounted on +# /dev/sda1 100G 20G 80G 20% / +``` + +## Установка + +### Метод 1: Клонирование из Git + +```bash +# Клонирование репозитория +git clone https://github.com/your-username/logboard.git +cd logboard + +# Проверка структуры проекта +ls -la +``` + +### Метод 2: Скачивание архива + +```bash +# Скачивание и распаковка +wget https://github.com/your-username/logboard/archive/refs/tags/v1.0.0.tar.gz +tar -xzf v1.0.0.tar.gz +cd logboard-1.0.0 +``` + +## Настройка + +### 1. Создание файла переменных окружения + +```bash +# Копирование примера конфигурации +make setup + +# Или вручную +cp env.example .env +``` + +### 2. Редактирование конфигурации + +Откройте файл `.env` и настройте основные параметры: + +```bash +# Основные настройки +LOGBOARD_PORT=9001 +LOGBOARD_USER=admin +LOGBOARD_PASS=your-secure-password + +# Безопасность (обязательно измените!) +SECRET_KEY=your-very-secure-secret-key-here +ENCRYPTION_KEY=your-encryption-key-here + +# Настройки Docker +DOCKER_HOST=unix:///var/run/docker.sock +``` + +### 3. Настройка прав доступа к Docker + +```bash +# Создание группы docker (если не существует) +sudo groupadd docker + +# Добавление пользователя в группу docker +sudo usermod -aG docker $USER + +# Проверка прав доступа +ls -la /var/run/docker.sock +# srw-rw-rw- 1 root docker 0 Jan 15 10:00 /var/run/docker.sock + +# Перезагрузка группы (или перелогин) +newgrp docker +``` + +### 4. Создание директорий + +```bash +# Создание директории для снимков логов +mkdir -p snapshots + +# Установка прав доступа +chmod 755 snapshots +``` + +## Первый запуск + +### 1. Сборка Docker образа + +```bash +# Сборка образа +make build + +# Или вручную +docker compose build --no-cache +``` + +### 2. Запуск приложения + +```bash +# Запуск в фоновом режиме +make up + +# Или вручную +docker compose up -d +``` + +### 3. Проверка статуса + +```bash +# Проверка статуса контейнеров +make status + +# Просмотр логов +make logs +``` + +## Проверка установки + +### 1. Проверка доступности веб-интерфейса + +```bash +# Проверка HTTP ответа +curl -I http://localhost:9001 +# HTTP/1.1 200 OK +# Content-Type: text/html; charset=utf-8 + +# Проверка health check +curl http://localhost:9001/healthz +# ok +``` + +### 2. Проверка API + +```bash +# Получение токена аутентификации +curl -X POST "http://localhost:9001/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"your-secure-password"}' + +# Ответ должен содержать access_token +``` + +### 3. Проверка Docker интеграции + +```bash +# Проверка списка контейнеров +curl -X GET "http://localhost:9001/api/services" \ + -H "Authorization: Bearer YOUR_TOKEN_HERE" + +# Проверка списка проектов +curl -X GET "http://localhost:9001/api/projects" \ + -H "Authorization: Bearer YOUR_TOKEN_HERE" +``` + +### 4. Проверка WebSocket соединений + +```bash +# Тестирование WebSocket (требует специального клиента) +# Используйте браузер или wscat для тестирования +``` + +## Настройка для продакшена + +### 1. Безопасность + +#### Изменение паролей и ключей + +```bash +# Генерация секретного ключа +openssl rand -hex 32 +# 8a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6 + +# Генерация ключа шифрования +openssl rand -hex 32 +# 1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7 + +# Обновление .env файла +LOGBOARD_PASS=your-very-secure-password +SECRET_KEY=8a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6 +ENCRYPTION_KEY=1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7 +``` + +#### Настройка HTTPS + +Создайте файл `nginx.conf`: + +```nginx +server { + listen 80; + server_name your-domain.com; + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name your-domain.com; + + ssl_certificate /path/to/your/certificate.crt; + ssl_certificate_key /path/to/your/private.key; + + location / { + proxy_pass http://localhost:9001; + 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; + + # WebSocket поддержка + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + } +} +``` + +### 2. Мониторинг + +#### Настройка логирования + +```bash +# Создание директории для логов +sudo mkdir -p /var/log/logboard +sudo chown $USER:$USER /var/log/logboard + +# Обновление docker-compose.yml +volumes: + - /var/log/logboard:/app/logs +``` + +#### Настройка systemd сервиса + +Создайте файл `/etc/systemd/system/logboard.service`: + +```ini +[Unit] +Description=LogBoard+ Docker Compose +Requires=docker.service +After=docker.service + +[Service] +Type=oneshot +RemainAfterExit=yes +WorkingDirectory=/path/to/logboard +ExecStart=/usr/bin/docker compose up -d +ExecStop=/usr/bin/docker compose down +TimeoutStartSec=0 + +[Install] +WantedBy=multi-user.target +``` + +```bash +# Включение автозапуска +sudo systemctl enable logboard.service +sudo systemctl start logboard.service +``` + +### 3. Резервное копирование + +#### Скрипт резервного копирования + +Создайте файл `backup.sh`: + +```bash +#!/bin/bash +# Скрипт резервного копирования LogBoard+ + +BACKUP_DIR="/backup/logboard" +DATE=$(date +%Y%m%d_%H%M%S) + +# Создание директории для резервных копий +mkdir -p $BACKUP_DIR + +# Резервное копирование конфигурации +tar -czf $BACKUP_DIR/logboard_config_$DATE.tar.gz \ + .env \ + excluded_containers.json \ + docker-compose.yml + +# Резервное копирование снимков логов +tar -czf $BACKUP_DIR/logboard_snapshots_$DATE.tar.gz \ + snapshots/ + +# Удаление старых резервных копий (старше 30 дней) +find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete + +echo "Резервное копирование завершено: $DATE" +``` + +```bash +# Установка прав на выполнение +chmod +x backup.sh + +# Добавление в cron (ежедневно в 2:00) +crontab -e +# 0 2 * * * /path/to/logboard/backup.sh +``` + +### 4. Обновление + +#### Скрипт обновления + +Создайте файл `update.sh`: + +```bash +#!/bin/bash +# Скрипт обновления LogBoard+ + +echo "Начинаем обновление LogBoard+..." + +# Остановка сервисов +make down + +# Получение обновлений +git pull origin main + +# Пересборка образов +make build + +# Запуск сервисов +make up + +# Проверка статуса +make status + +echo "Обновление завершено!" +``` + +```bash +# Установка прав на выполнение +chmod +x update.sh +``` + +## Устранение проблем установки + +### Частые ошибки + +1. **Ошибка доступа к Docker socket** + ```bash + # Решение: добавление пользователя в группу docker + sudo usermod -aG docker $USER + newgrp docker + ``` + +2. **Ошибка порта уже используется** + ```bash + # Решение: изменение порта в .env + LOGBOARD_PORT=9002 + ``` + +3. **Ошибка недостаточно памяти** + ```bash + # Решение: увеличение лимитов Docker + sudo systemctl edit docker + # Добавьте: + # [Service] + # MemoryLimit=2G + ``` + +4. **Ошибка SSL сертификатов** + ```bash + # Решение: использование самоподписанных сертификатов для тестирования + openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout private.key -out certificate.crt + ``` + +### Логи для диагностики + +```bash +# Просмотр логов приложения +make logs + +# Просмотр логов Docker +docker logs logboard + +# Проверка статуса контейнера +docker inspect logboard +``` + +## Следующие шаги + +После успешной установки: + +1. [Настройте безопасность](./security.md) +2. [Изучите API документацию](./api.md) +3. [Настройте конфигурацию](./configuration.md) +4. [Изучите WebSocket API](./websocket.md) + diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 0000000..a6f4dd3 --- /dev/null +++ b/docs/security.md @@ -0,0 +1,750 @@ +# Безопасность LogBoard+ + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +## Содержание + +1. [Обзор безопасности](#обзор-безопасности) +2. [Генерация ключей](#генерация-ключей) +3. [Аутентификация](#аутентификация) +4. [Настройка HTTPS](#настройка-https) +5. [Доступ к Docker](#доступ-к-docker) +6. [Сетевая безопасность](#сетевая-безопасность) +7. [Мониторинг безопасности](#мониторинг-безопасности) +8. [Рекомендации для продакшена](#рекомендации-для-продакшена) + +## Обзор безопасности + +LogBoard+ включает в себя несколько уровней безопасности для защиты доступа к логам и системной информации. Правильная настройка безопасности критически важна для продакшена. + +### Уровни безопасности + +1. **Аутентификация** - JWT токены для доступа к API +2. **Шифрование** - Защита чувствительных данных +3. **Сетевая безопасность** - HTTPS, файрвол, ограничение доступа +4. **Docker безопасность** - Ограничение прав доступа к Docker socket +5. **Мониторинг** - Логирование событий безопасности + +## Генерация ключей + +### SECRET_KEY + +SECRET_KEY используется для подписи JWT токенов. Это критически важный ключ для безопасности аутентификации. + +#### Генерация SECRET_KEY + +**Метод 1: OpenSSL (рекомендуется)** + +```bash +# Генерация 32-байтового ключа в hex формате +openssl rand -hex 32 +# Пример вывода: 8a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6 + +# Генерация 64-байтового ключа (для максимальной безопасности) +openssl rand -hex 64 +# Пример вывода: 8a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6... +``` + +**Метод 2: Python** + +```bash +# Использование Python для генерации +python3 -c "import secrets; print(secrets.token_hex(32))" +# Пример вывода: 8a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6 + +# Генерация через Python скрипт +python3 -c " +import secrets +import base64 +key = secrets.token_bytes(32) +print('Hex:', key.hex()) +print('Base64:', base64.b64encode(key).decode()) +" +``` + +**Метод 3: Node.js** + +```bash +# Использование Node.js +node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" +# Пример вывода: 8a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6 +``` + +#### Требования к SECRET_KEY + +- **Длина:** Минимум 32 байта (64 символа в hex) +- **Энтропия:** Используйте криптографически стойкий генератор случайных чисел +- **Уникальность:** Каждая установка должна иметь уникальный ключ +- **Хранение:** Храните в безопасном месте, не в коде + +### ENCRYPTION_KEY + +ENCRYPTION_KEY используется для шифрования чувствительных данных в приложении. + +#### Генерация ENCRYPTION_KEY + +**Метод 1: OpenSSL (рекомендуется)** + +```bash +# Генерация 32-байтового ключа шифрования +openssl rand -hex 32 +# Пример вывода: 1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7 + +# Генерация ключа для AES-256 +openssl rand -base64 32 +# Пример вывода: 1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7 +``` + +**Метод 2: Python с Fernet** + +```bash +# Генерация ключа для Fernet (Base64) +python3 -c "from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())" +# Пример вывода: 1a2b3c4d5e6f7g8h9i0j1k2l3m4n5o6p7q8r9s0t1u2v3w4x5y6z7 + +# Генерация через Python скрипт +python3 -c " +import secrets +import base64 +key = secrets.token_bytes(32) +print('Hex:', key.hex()) +print('Base64:', base64.b64encode(key).decode()) +print('Fernet:', base64.urlsafe_b64encode(key).decode()) +" +``` + +**Метод 3: Скрипт генерации ключей** + +Создайте файл `generate_keys.sh`: + +```bash +#!/bin/bash +# Скрипт генерации ключей безопасности для LogBoard+ +# Автор: Сергей Антропов +# Сайт: https://devops.org.ru + +echo "=== Генерация ключей безопасности для LogBoard+ ===" +echo "" + +# Проверка наличия OpenSSL +if ! command -v openssl &> /dev/null; then + echo "❌ OpenSSL не найден. Установите OpenSSL для генерации ключей." + exit 1 +fi + +echo "🔐 Генерация SECRET_KEY..." +SECRET_KEY=$(openssl rand -hex 32) +echo "SECRET_KEY=$SECRET_KEY" +echo "" + +echo "🔐 Генерация ENCRYPTION_KEY..." +ENCRYPTION_KEY=$(openssl rand -hex 32) +echo "ENCRYPTION_KEY=$ENCRYPTION_KEY" +echo "" + +echo "🔐 Генерация пароля пользователя..." +USER_PASSWORD=$(openssl rand -base64 16 | tr -d "=+/" | cut -c1-16) +echo "LOGBOARD_PASS=$USER_PASSWORD" +echo "" + +echo "📝 Обновление .env файла..." +if [ -f .env ]; then + # Создание резервной копии + cp .env .env.backup.$(date +%Y%m%d_%H%M%S) + echo "✅ Создана резервная копия .env" + + # Обновление ключей + sed -i "s/^SECRET_KEY=.*/SECRET_KEY=$SECRET_KEY/" .env + sed -i "s/^ENCRYPTION_KEY=.*/ENCRYPTION_KEY=$ENCRYPTION_KEY/" .env + sed -i "s/^LOGBOARD_PASS=.*/LOGBOARD_PASS=$USER_PASSWORD/" .env + + echo "✅ Ключи обновлены в .env" +else + echo "⚠️ Файл .env не найден. Создайте его из env.example" +fi + +echo "" +echo "=== Рекомендации по безопасности ===" +echo "1. Сохраните ключи в безопасном месте" +echo "2. Не передавайте ключи через незащищенные каналы" +echo "3. Регулярно обновляйте ключи" +echo "4. Используйте разные ключи для разных окружений" +echo "" +echo "✅ Генерация ключей завершена!" +``` + +```bash +# Установка прав на выполнение +chmod +x generate_keys.sh + +# Запуск генерации +./generate_keys.sh +``` + +#### Требования к ENCRYPTION_KEY + +- **Длина:** 32 байта для AES-256 +- **Формат:** Hex или Base64 +- **Энтропия:** Криптографически стойкий генератор +- **Уникальность:** Уникальный для каждой установки + +### Проверка качества ключей + +```bash +# Проверка энтропии ключа +python3 -c " +import secrets +import base64 + +def check_entropy(key_hex): + # Преобразование hex в байты + key_bytes = bytes.fromhex(key_hex) + + # Подсчет уникальных байтов + unique_bytes = len(set(key_bytes)) + total_bytes = len(key_bytes) + + # Вычисление энтропии + entropy = unique_bytes / total_bytes + + print(f'Длина ключа: {total_bytes} байт') + print(f'Уникальных байтов: {unique_bytes}') + print(f'Энтропия: {entropy:.2%}') + + if entropy > 0.8: + print('✅ Ключ имеет хорошую энтропию') + else: + print('⚠️ Ключ может иметь низкую энтропию') + +# Проверка вашего ключа +key = '8a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6' +check_entropy(key) +" +``` + +## Аутентификация + +### JWT токены + +LogBoard+ использует JWT (JSON Web Tokens) для аутентификации. + +#### Структура JWT токена + +```json +{ + "header": { + "alg": "HS256", + "typ": "JWT" + }, + "payload": { + "sub": "admin", + "exp": 1642234567, + "iat": 1642230967 + }, + "signature": "HMACSHA256(base64UrlEncode(header) + '.' + base64UrlEncode(payload), SECRET_KEY)" +} +``` + +#### Настройка JWT + +```bash +# В .env файле +SECRET_KEY=your-secret-key-here +SESSION_TIMEOUT=3600 # Время жизни токена в секундах +``` + +#### Безопасность JWT + +1. **Используйте сильный SECRET_KEY** +2. **Установите разумное время жизни токена** +3. **Используйте HTTPS для передачи токенов** +4. **Регулярно обновляйте токены** + +### Пароли пользователей + +#### Генерация безопасного пароля + +```bash +# Генерация пароля с OpenSSL +openssl rand -base64 16 | tr -d "=+/" | cut -c1-16 +# Пример вывода: aB3cD4eF5gH6iJ7k + +# Генерация пароля с Python +python3 -c " +import secrets +import string + +def generate_password(length=16): + alphabet = string.ascii_letters + string.digits + '!@#$%^&*' + return ''.join(secrets.choice(alphabet) for _ in range(length)) + +print('Пароль:', generate_password(16)) +" + +# Генерация пароля с pwgen +pwgen -s 16 1 +``` + +#### Требования к паролям + +- **Длина:** Минимум 12 символов +- **Сложность:** Буквы, цифры, специальные символы +- **Уникальность:** Не используйте пароли по умолчанию +- **Хранение:** Храните в безопасном месте + +## Настройка HTTPS + +### Самоподписанные сертификаты (для тестирования) + +```bash +# Генерация приватного ключа +openssl genrsa -out private.key 2048 + +# Генерация сертификата +openssl req -x509 -new -nodes -key private.key -sha256 -days 365 -out certificate.crt + +# Или одной командой +openssl req -x509 -nodes -days 365 -newkey rsa:2048 \ + -keyout private.key -out certificate.crt \ + -subj "/C=RU/ST=Moscow/L=Moscow/O=Company/CN=localhost" +``` + +### Let's Encrypt сертификаты (для продакшена) + +```bash +# Установка certbot +sudo apt install certbot python3-certbot-nginx + +# Получение сертификата +sudo certbot --nginx -d your-domain.com + +# Автоматическое обновление +sudo crontab -e +# Добавить строку: +# 0 12 * * * /usr/bin/certbot renew --quiet +``` + +### Nginx конфигурация с HTTPS + +```nginx +server { + listen 80; + server_name your-domain.com; + return 301 https://$server_name$request_uri; +} + +server { + listen 443 ssl http2; + server_name your-domain.com; + + # SSL сертификаты + ssl_certificate /path/to/certificate.crt; + ssl_certificate_key /path/to/private.key; + + # SSL настройки + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384; + ssl_prefer_server_ciphers off; + ssl_session_cache shared:SSL:10m; + ssl_session_timeout 10m; + + # Безопасные заголовки + add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; + add_header X-Frame-Options DENY always; + add_header X-Content-Type-Options nosniff always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "strict-origin-when-cross-origin" always; + + location / { + proxy_pass http://localhost:9001; + 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; + + # WebSocket поддержка + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + + # Таймауты + proxy_connect_timeout 60s; + proxy_send_timeout 60s; + proxy_read_timeout 60s; + } +} +``` + +## Доступ к Docker + +### Ограничение прав доступа + +```bash +# Создание группы docker +sudo groupadd docker + +# Добавление пользователя в группу +sudo usermod -aG docker $USER + +# Проверка прав +ls -la /var/run/docker.sock +# Должно быть: srw-rw-rw- 1 root docker 0 Jan 15 10:00 /var/run/docker.sock + +# Перезагрузка группы +newgrp docker +``` + +### Альтернативные методы + +#### Метод 1: Docker API с TLS + +```bash +# Генерация CA ключа +openssl genrsa -aes256 -out ca-key.pem 4096 + +# Генерация CA сертификата +openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -out ca.pem + +# Генерация серверного ключа +openssl genrsa -out server-key.pem 4096 + +# Генерация серверного сертификата +openssl req -subj "/CN=$HOST" -sha256 -new -key server-key.pem -out server.csr + +# Подпись серверного сертификата +openssl x509 -req -days 365 -sha256 \ + -in server.csr -CA ca.pem -CAkey ca-key.pem \ + -CAcreateserial -out server-cert.pem + +# Настройка Docker daemon +sudo mkdir -p /etc/docker +sudo cp ca.pem server-cert.pem server-key.pem /etc/docker/ + +# Конфигурация Docker daemon +sudo tee /etc/docker/daemon.json < /etc/iptables/rules.v4 +``` + +### Настройка Docker сетей + +```yaml +# В docker-compose.yml +networks: + logboard_network: + driver: bridge + ipam: + config: + - subnet: 172.20.0.0/16 + driver_opts: + com.docker.network.bridge.name: logboard_br0 + +services: + logboard: + networks: + - logboard_network + # Ограничение доступа к Docker socket + volumes: + - /var/run/docker.sock:/var/run/docker.sock:ro +``` + +## Мониторинг безопасности + +### Логирование событий безопасности + +```bash +# Настройка логирования в .env +LOG_LEVEL=INFO +LOG_FORMAT=json + +# Создание директории для логов +sudo mkdir -p /var/log/logboard +sudo chown $USER:$USER /var/log/logboard + +# Настройка ротации логов +sudo tee /etc/logrotate.d/logboard < monitor_access.sh <<'EOF' +#!/bin/bash +# Мониторинг доступа к LogBoard+ +# Автор: Сергей Антропов + +LOG_FILE="/var/log/logboard/access.log" +ALERT_FILE="/var/log/logboard/security_alerts.log" + +# Функция логирования +log_alert() { + echo "$(date '+%Y-%m-%d %H:%M:%S') - $1" >> "$ALERT_FILE" +} + +# Мониторинг неудачных попыток входа +failed_logins=$(grep -c "authentication failed" "$LOG_FILE" 2>/dev/null || echo "0") + +if [ "$failed_logins" -gt 10 ]; then + log_alert "Обнаружено $failed_logins неудачных попыток входа" +fi + +# Мониторинг подозрительных IP +suspicious_ips=$(grep -oE "\b([0-9]{1,3}\.){3}[0-9]{1,3}\b" "$LOG_FILE" | sort | uniq -c | awk '$1 > 100 {print $2}') + +if [ -n "$suspicious_ips" ]; then + log_alert "Подозрительная активность с IP: $suspicious_ips" +fi + +echo "Мониторинг завершен: $(date)" +EOF + +chmod +x monitor_access.sh + +# Добавление в cron +crontab -e +# Добавить строку: +# */5 * * * * /path/to/logboard/monitor_access.sh +``` + +### Алерты безопасности + +```bash +# Скрипт отправки алертов +cat > security_alerts.sh <<'EOF' +#!/bin/bash +# Отправка алертов безопасности +# Автор: Сергей Антропов + +ALERT_FILE="/var/log/logboard/security_alerts.log" +EMAIL="admin@company.com" + +# Проверка новых алертов +if [ -f "$ALERT_FILE" ]; then + new_alerts=$(tail -n 10 "$ALERT_FILE") + + if [ -n "$new_alerts" ]; then + echo "$new_alerts" | mail -s "LogBoard+ Security Alert" "$EMAIL" + fi +fi +EOF + +chmod +x security_alerts.sh + +# Добавление в cron +crontab -e +# Добавить строку: +# 0 */1 * * * /path/to/logboard/security_alerts.sh +``` + +## Рекомендации для продакшена + +### Обязательные настройки + +1. **Измените все пароли по умолчанию** +2. **Используйте сильные секретные ключи** +3. **Настройте HTTPS** +4. **Ограничьте доступ к Docker socket** +5. **Настройте файрвол** +6. **Включите мониторинг безопасности** + +### Регулярные проверки + +```bash +# Скрипт проверки безопасности +cat > security_check.sh <<'EOF' +#!/bin/bash +# Проверка безопасности LogBoard+ +# Автор: Сергей Антропов + +echo "=== Проверка безопасности LogBoard+ ===" +echo "Дата: $(date)" +echo "" + +# Проверка паролей по умолчанию +if grep -q "LOGBOARD_PASS=admin" .env; then + echo "❌ Пароль по умолчанию не изменен!" +else + echo "✅ Пароль изменен" +fi + +# Проверка секретных ключей +if grep -q "SECRET_KEY=your-secret-key-here" .env; then + echo "❌ SECRET_KEY не изменен!" +else + echo "✅ SECRET_KEY изменен" +fi + +if grep -q "ENCRYPTION_KEY=your-encryption-key-here" .env; then + echo "❌ ENCRYPTION_KEY не изменен!" +else + echo "✅ ENCRYPTION_KEY изменен" +fi + +# Проверка HTTPS +if curl -s -I https://localhost:9001 > /dev/null 2>&1; then + echo "✅ HTTPS доступен" +else + echo "⚠️ HTTPS недоступен" +fi + +# Проверка прав Docker +if ls -la /var/run/docker.sock | grep -q "srw-rw-rw-"; then + echo "✅ Docker socket доступен" +else + echo "❌ Проблемы с доступом к Docker socket" +fi + +# Проверка файрвола +if command -v ufw > /dev/null && sudo ufw status | grep -q "Status: active"; then + echo "✅ Файрвол активен" +else + echo "⚠️ Файрвол не настроен" +fi + +echo "" +echo "=== Рекомендации ===" +echo "1. Регулярно обновляйте ключи" +echo "2. Мониторьте логи доступа" +echo "3. Обновляйте зависимости" +echo "4. Проверяйте права доступа" +echo "5. Тестируйте резервные копии" +EOF + +chmod +x security_check.sh + +# Запуск проверки +./security_check.sh +``` + +### План реагирования на инциденты + +1. **Немедленные действия** + - Отключение подозрительных IP + - Блокировка скомпрометированных учетных записей + - Анализ логов + +2. **Расследование** + - Определение источника атаки + - Оценка ущерба + - Документирование инцидента + +3. **Восстановление** + - Обновление ключей + - Патчинг уязвимостей + - Восстановление из резервных копий + +4. **Профилактика** + - Усиление безопасности + - Обновление процедур + - Обучение персонала + +### Контрольный список безопасности + +- [ ] Изменены пароли по умолчанию +- [ ] Сгенерированы сильные секретные ключи +- [ ] Настроен HTTPS +- [ ] Ограничен доступ к Docker +- [ ] Настроен файрвол +- [ ] Включено логирование +- [ ] Настроен мониторинг +- [ ] Созданы резервные копии +- [ ] Документированы процедуры +- [ ] Проведено тестирование безопасности diff --git a/docs/troubleshooting.md b/docs/troubleshooting.md new file mode 100644 index 0000000..e5c7d24 --- /dev/null +++ b/docs/troubleshooting.md @@ -0,0 +1,767 @@ +# Устранение неполадок LogBoard+ + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +## Содержание + +1. [Общие проблемы](#общие-проблемы) +2. [Проблемы с Docker](#проблемы-с-docker) +3. [Проблемы с аутентификацией](#проблемы-с-аутентификацией) +4. [Проблемы с WebSocket](#проблемы-с-websocket) +5. [Проблемы с производительностью](#проблемы-с-производительностью) +6. [Проблемы с сетью](#проблемы-с-сетью) +7. [Диагностика и логи](#диагностика-и-логи) + +## Общие проблемы + +### Приложение не запускается + +#### Симптомы +- Ошибка при выполнении `make up` +- Контейнер не стартует +- Ошибки в логах Docker + +#### Диагностика + +```bash +# Проверка статуса контейнеров +make status + +# Просмотр логов +make logs + +# Проверка конфигурации +make validate + +# Проверка переменных окружения +make env-check +``` + +#### Решения + +1. **Проблема с портом** + ```bash + # Проверка занятости порта + netstat -tlnp | grep 9001 + + # Изменение порта в .env + LOGBOARD_PORT=9002 + ``` + +2. **Проблема с правами доступа** + ```bash + # Проверка прав на директории + ls -la snapshots/ + + # Установка правильных прав + chmod 755 snapshots/ + chown $USER:$USER snapshots/ + ``` + +3. **Проблема с Docker** + ```bash + # Проверка статуса Docker + sudo systemctl status docker + + # Перезапуск Docker + sudo systemctl restart docker + ``` + +### Ошибки конфигурации + +#### Симптомы +- Ошибки при парсинге переменных окружения +- Неправильная работа функций +- Ошибки в логах приложения + +#### Диагностика + +```bash +# Проверка синтаксиса .env файла +cat .env | grep -v "^#" | grep -v "^$" + +# Проверка обязательных переменных +grep -E "^(LOGBOARD_PASS|SECRET_KEY|ENCRYPTION_KEY)=" .env +``` + +#### Решения + +1. **Отсутствуют обязательные переменные** + ```bash + # Создание .env из примера + make setup + + # Редактирование .env + nano .env + ``` + +2. **Неправильный формат переменных** + ```bash + # Проверка формата + # Переменные должны быть в формате KEY=value + # Без пробелов вокруг знака равенства + ``` + +## Проблемы с Docker + +### Ошибка подключения к Docker socket + +#### Симптомы +- Ошибка "Permission denied" при доступе к Docker +- Контейнеры не отображаются в интерфейсе +- Ошибки в логах приложения + +#### Диагностика + +```bash +# Проверка прав доступа к Docker socket +ls -la /var/run/docker.sock + +# Проверка группы docker +getent group docker + +# Проверка пользователя в группе +groups $USER +``` + +#### Решения + +1. **Добавление пользователя в группу docker** + ```bash + # Создание группы docker (если не существует) + sudo groupadd docker + + # Добавление пользователя в группу + sudo usermod -aG docker $USER + + # Перезагрузка группы + newgrp docker + + # Проверка + docker ps + ``` + +2. **Изменение прав на Docker socket** + ```bash + # Временное решение (не рекомендуется для продакшена) + sudo chmod 666 /var/run/docker.sock + ``` + +3. **Перезапуск Docker daemon** + ```bash + sudo systemctl restart docker + ``` + +### Контейнеры не отображаются + +#### Симптомы +- Пустой список контейнеров в интерфейсе +- Ошибки при получении списка контейнеров +- Контейнеры видны в `docker ps`, но не в LogBoard+ + +#### Диагностика + +```bash +# Проверка списка контейнеров +docker ps -a + +# Проверка меток контейнеров +docker inspect | grep -A 10 "Labels" + +# Проверка фильтров проектов +echo $COMPOSE_PROJECT_NAME +echo $LOGBOARD_PROJECTS +``` + +#### Решения + +1. **Проблема с фильтрами проектов** + ```bash + # Очистка фильтров в .env + # COMPOSE_PROJECT_NAME= + # LOGBOARD_PROJECTS= + + # Перезапуск приложения + make restart + ``` + +2. **Проблема с метками контейнеров** + ```bash + # Проверка меток Docker Compose + docker inspect | grep "com.docker.compose" + + # Перезапуск контейнеров с правильными метками + docker compose down && docker compose up -d + ``` + +3. **Проблема с исключенными контейнерами** + ```bash + # Проверка файла исключений + cat excluded_containers.json + + # Очистка исключений + echo '{"excluded_containers": []}' > excluded_containers.json + ``` + +### Ошибки получения логов + +#### Симптомы +- Логи не загружаются +- Ошибки при запросе логов +- Пустые логи в интерфейсе + +#### Диагностика + +```bash +# Проверка логов контейнера напрямую +docker logs + +# Проверка статуса контейнера +docker inspect | grep -A 5 "State" + +# Проверка прав доступа к логам +docker logs 2>&1 +``` + +#### Решения + +1. **Контейнер остановлен** + ```bash + # Запуск контейнера + docker start + + # Проверка статуса + docker ps + ``` + +2. **Проблема с драйвером логирования** + ```bash + # Проверка драйвера логирования + docker inspect | grep "LogDriver" + + # Изменение драйвера логирования + docker run --log-driver=json-file ... + ``` + +3. **Проблема с размером логов** + ```bash + # Очистка логов контейнера + docker logs --since 1h + + # Ограничение размера логов + docker run --log-opt max-size=10m ... + ``` + +## Проблемы с аутентификацией + +### Ошибка входа в систему + +#### Симптомы +- Не удается войти в систему +- Ошибка "Неверное имя пользователя или пароль" +- Бесконечная перезагрузка страницы входа + +#### Диагностика + +```bash +# Проверка переменных аутентификации +grep -E "^(LOGBOARD_USER|LOGBOARD_PASS)=" .env + +# Проверка API аутентификации +curl -X POST "http://localhost:9001/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"admin"}' +``` + +#### Решения + +1. **Неправильные учетные данные** + ```bash + # Проверка и исправление .env + LOGBOARD_USER=admin + LOGBOARD_PASS=your-correct-password + + # Перезапуск приложения + make restart + ``` + +2. **Проблема с JWT токенами** + ```bash + # Проверка SECRET_KEY + grep "SECRET_KEY" .env + + # Генерация нового ключа + openssl rand -hex 32 + + # Обновление .env + SECRET_KEY=your-new-secret-key + ``` + +3. **Проблема с cookies** + ```bash + # Очистка cookies браузера + # Или использование режима инкогнито + ``` + +### Ошибки авторизации API + +#### Симптомы +- Ошибка 401 при запросах к API +- "Требуется авторизация" в ответах +- Не работает WebSocket API + +#### Диагностика + +```bash +# Проверка токена +curl -X GET "http://localhost:9001/api/auth/me" \ + -H "Authorization: Bearer YOUR_TOKEN" + +# Проверка времени жизни токена +echo $SESSION_TIMEOUT +``` + +#### Решения + +1. **Истекший токен** + ```bash + # Увеличение времени жизни токена + SESSION_TIMEOUT=7200 # 2 часа + + # Перезапуск приложения + make restart + ``` + +2. **Неправильный формат токена** + ```bash + # Получение нового токена + curl -X POST "http://localhost:9001/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"your-password"}' + ``` + +3. **Проблема с часовыми поясами** + ```bash + # Установка правильного часового пояса + TZ_TS=UTC + + # Перезапуск приложения + make restart + ``` + +## Проблемы с WebSocket + +### WebSocket соединения не устанавливаются + +#### Симптомы +- Ошибки WebSocket в консоли браузера +- Логи не обновляются в реальном времени +- Ошибка "WebSocket connection failed" + +#### Диагностика + +```bash +# Проверка WebSocket endpoint +curl -I "http://localhost:9001/ws/logs/test" + +# Проверка логов приложения +make logs | grep -i websocket + +# Проверка в браузере +# Открыть Developer Tools -> Console +``` + +#### Решения + +1. **Проблема с прокси** + ```nginx + # Настройка Nginx для WebSocket + location /ws/ { + proxy_pass http://localhost:9001; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header Host $host; + } + ``` + +2. **Проблема с токеном** + ```javascript + // Проверка токена в WebSocket URL + const token = "your-valid-token"; + const ws = new WebSocket(`ws://localhost:9001/ws/logs/container-id?token=${token}`); + ``` + +3. **Проблема с CORS** + ```bash + # Добавление CORS заголовков в конфигурацию + # (если используется) + ``` + +### WebSocket соединения разрываются + +#### Симптомы +- Соединения закрываются через некоторое время +- Ошибки "Connection closed unexpectedly" +- Логи перестают обновляться + +#### Диагностика + +```bash +# Проверка таймаутов +grep -E "TIMEOUT|timeout" .env + +# Проверка нагрузки на систему +top +free -h +``` + +#### Решения + +1. **Увеличение таймаутов** + ```bash + # Увеличение таймаутов в .env + CONNECTION_TIMEOUT=120 + READ_TIMEOUT=300 + + # Перезапуск приложения + make restart + ``` + +2. **Проблема с нагрузкой** + ```bash + # Ограничение количества соединений + MAX_CONNECTIONS=50 + + # Увеличение ресурсов контейнера + # В docker-compose.yml + deploy: + resources: + limits: + memory: 1G + cpus: '1.0' + ``` + +3. **Проблема с сетевыми таймаутами** + ```bash + # Настройка keepalive + # В конфигурации прокси или балансировщика + ``` + +## Проблемы с производительностью + +### Медленная загрузка логов + +#### Симптомы +- Долгая загрузка страниц +- Медленное получение логов +- Высокая нагрузка на CPU + +#### Диагностика + +```bash +# Проверка ресурсов системы +htop +iostat 1 +df -h + +# Проверка настроек производительности +grep -E "TAIL|INTERVAL" .env +``` + +#### Решения + +1. **Оптимизация настроек** + ```bash + # Уменьшение количества строк логов + LOGBOARD_TAIL=100 + + # Увеличение интервала обновления + LOGBOARD_AJAX_UPDATE_INTERVAL=5000 + + # Перезапуск приложения + make restart + ``` + +2. **Ограничение ресурсов** + ```bash + # Настройка лимитов в docker-compose.yml + deploy: + resources: + limits: + memory: 512M + cpus: '0.5' + reservations: + memory: 256M + cpus: '0.25' + ``` + +3. **Оптимизация Docker** + ```bash + # Очистка неиспользуемых ресурсов + docker system prune -f + + # Ограничение логов контейнеров + docker run --log-opt max-size=10m --log-opt max-file=3 ... + ``` + +### Высокое потребление памяти + +#### Симптомы +- Контейнер LogBoard+ потребляет много памяти +- Ошибки "Out of memory" +- Медленная работа системы + +#### Диагностика + +```bash +# Проверка использования памяти +docker stats logboard + +# Проверка процессов в контейнере +docker exec logboard ps aux + +# Проверка логов приложения +make logs | grep -i memory +``` + +#### Решения + +1. **Ограничение памяти** + ```bash + # Установка лимитов памяти + # В docker-compose.yml + deploy: + resources: + limits: + memory: 512M + ``` + +2. **Оптимизация кода** + ```bash + # Уменьшение размера буферов + # Уменьшение количества одновременных соединений + MAX_CONNECTIONS=50 + ``` + +3. **Очистка кэша** + ```bash + # Перезапуск приложения + make restart + + # Очистка Docker кэша + docker system prune -f + ``` + +## Проблемы с сетью + +### Недоступность веб-интерфейса + +#### Симптомы +- Не удается открыть веб-интерфейс +- Ошибка "Connection refused" +- Таймаут при подключении + +#### Диагностика + +```bash +# Проверка статуса контейнера +make status + +# Проверка портов +netstat -tlnp | grep 9001 + +# Проверка доступности +curl -I http://localhost:9001 +``` + +#### Решения + +1. **Проблема с портом** + ```bash + # Проверка занятости порта + sudo lsof -i :9001 + + # Изменение порта + LOGBOARD_PORT=9002 + make restart + ``` + +2. **Проблема с сетевыми настройками** + ```bash + # Проверка Docker сетей + docker network ls + + # Пересоздание сетей + docker compose down + docker compose up -d + ``` + +3. **Проблема с файрволом** + ```bash + # Проверка файрвола + sudo ufw status + + # Разрешение порта + sudo ufw allow 9001 + ``` + +### Проблемы с внешним доступом + +#### Симптомы +- Доступ только с localhost +- Не работает с внешним IP +- Проблемы с reverse proxy + +#### Диагностика + +```bash +# Проверка привязки к интерфейсам +netstat -tlnp | grep 9001 + +# Проверка конфигурации Docker +docker inspect logboard | grep -A 10 "NetworkSettings" +``` + +#### Решения + +1. **Настройка привязки к интерфейсам** + ```yaml + # В docker-compose.yml + ports: + - "0.0.0.0:9001:9001" + ``` + +2. **Настройка reverse proxy** + ```nginx + # Nginx конфигурация + location / { + proxy_pass http://localhost:9001; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + } + ``` + +3. **Настройка файрвола** + ```bash + # Разрешение внешнего доступа + sudo ufw allow from 192.168.1.0/24 to any port 9001 + ``` + +## Диагностика и логи + +### Просмотр логов приложения + +```bash +# Логи в реальном времени +make logs -f + +# Логи с фильтрацией +make logs | grep ERROR + +# Логи последних 100 строк +make logs | tail -100 +``` + +### Просмотр логов Docker + +```bash +# Логи контейнера LogBoard+ +docker logs logboard + +# Логи с временными метками +docker logs -t logboard + +# Логи последних 50 строк +docker logs --tail 50 logboard +``` + +### Проверка состояния системы + +```bash +# Статус сервисов +make status + +# Использование ресурсов +docker stats logboard + +# Информация о контейнере +docker inspect logboard +``` + +### Отладка API + +```bash +# Проверка health check +curl http://localhost:9001/healthz + +# Проверка аутентификации +curl -X POST "http://localhost:9001/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"admin"}' + +# Проверка API с токеном +TOKEN="your-token" +curl -X GET "http://localhost:9001/api/services" \ + -H "Authorization: Bearer $TOKEN" +``` + +### Создание отчета о проблеме + +```bash +# Создание диагностического отчета +cat > diagnostic_report.txt << EOF +=== LogBoard+ Diagnostic Report === +Date: $(date) +Version: 1.0.0 + +=== System Information === +OS: $(uname -a) +Docker: $(docker --version) +Docker Compose: $(docker compose version) + +=== LogBoard+ Status === +$(make status) + +=== Configuration === +$(grep -v "^#" .env | grep -v "^$") + +=== Recent Logs === +$(make logs | tail -50) + +=== Docker Information === +$(docker inspect logboard | jq '.[] | {State: .State, NetworkSettings: .NetworkSettings}') + +=== System Resources === +$(free -h) +$(df -h /) +EOF + +echo "Diagnostic report saved to diagnostic_report.txt" +``` + +### Частые команды для диагностики + +```bash +# Быстрая диагностика +make env-check && make validate && make status + +# Проверка подключения к Docker +docker ps + +# Проверка доступности API +curl -s http://localhost:9001/healthz + +# Проверка аутентификации +curl -s -X POST "http://localhost:9001/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"admin"}' | jq . + +# Проверка списка контейнеров (требует токен) +TOKEN=$(curl -s -X POST "http://localhost:9001/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"admin"}' | jq -r '.access_token') +curl -s -X GET "http://localhost:9001/api/services" \ + -H "Authorization: Bearer $TOKEN" | jq 'length' +``` diff --git a/docs/websocket.md b/docs/websocket.md new file mode 100644 index 0000000..1e67dee --- /dev/null +++ b/docs/websocket.md @@ -0,0 +1,913 @@ +# WebSocket API LogBoard+ + +**Автор:** Сергей Антропов +**Сайт:** https://devops.org.ru + +## Содержание + +1. [Обзор WebSocket API](#обзор-websocket-api) +2. [Аутентификация](#аутентификация) +3. [Endpoints](#endpoints) +4. [Примеры использования](#примеры-использования) +5. [Обработка ошибок](#обработка-ошибок) +6. [Лучшие практики](#лучшие-практики) + +## Обзор WebSocket API + +WebSocket API LogBoard+ предоставляет возможность получения логов Docker контейнеров в реальном времени. Это позволяет создавать интерактивные приложения для мониторинга и отладки микросервисов. + +### Преимущества WebSocket + +- **Real-time данные** - мгновенное получение новых логов +- **Эффективность** - меньше накладных расходов по сравнению с polling +- **Масштабируемость** - поддержка множественных соединений +- **Фильтрация** - получение логов только нужных контейнеров + +### Поддерживаемые протоколы + +- **ws://** - для HTTP соединений +- **wss://** - для HTTPS соединений (рекомендуется для продакшена) + +## Аутентификация + +Все WebSocket endpoints требуют JWT токен для аутентификации. Токен передается как параметр запроса `token`. + +### Получение токена + +```bash +# Получение JWT токена через REST API +curl -X POST "http://localhost:9001/api/auth/login" \ + -H "Content-Type: application/json" \ + -d '{"username":"admin","password":"your-password"}' +``` + +### Использование токена + +```javascript +const token = "your-jwt-token"; +const ws = new WebSocket(`ws://localhost:9001/ws/logs/container-id?token=${token}`); +``` + +## Endpoints + +### ws://host:port/ws/logs/{container_id} + +Получение логов отдельного контейнера в реальном времени. + +#### Параметры + +| Параметр | Тип | Обязательный | Описание | +|----------|-----|--------------|----------| +| `container_id` | string | Да | ID контейнера (первые 12 символов) | +| `token` | string | Да | JWT токен аутентификации | +| `tail` | number | Нет | Количество начальных строк (по умолчанию 500) | +| `service` | string | Нет | Имя сервиса (для информации) | +| `project` | string | Нет | Имя проекта (для информации) | + +#### Пример подключения + +```javascript +const containerId = "abc123def456"; +const token = "your-jwt-token"; + +const ws = new WebSocket(`ws://localhost:9001/ws/logs/${containerId}?token=${token}&tail=100`); + +ws.onopen = function() { + console.log('WebSocket соединение установлено'); +}; + +ws.onmessage = function(event) { + console.log('Получены логи:', event.data); +}; + +ws.onerror = function(error) { + console.error('WebSocket ошибка:', error); +}; + +ws.onclose = function(event) { + console.log('WebSocket соединение закрыто:', event.code, event.reason); +}; +``` + +#### Формат сообщений + +При подключении сервер отправляет приветственное сообщение: + +``` +Connected to container: myproject_web_1 +``` + +Затем отправляются логи в формате: + +``` +2024-01-15T10:30:15.123456789Z 2024/01/15 10:30:15 [notice] 1#1: start worker processes +2024-01-15T10:30:15.123456789Z 2024/01/15 10:30:15 [notice] 1#1: start worker process 1234 +``` + +### ws://host:port/ws/fan/{service_name} + +Получение логов всех реплик сервиса Docker Compose (fan-in). + +#### Параметры + +| Параметр | Тип | Обязательный | Описание | +|----------|-----|--------------|----------| +| `service_name` | string | Да | Имя сервиса Docker Compose | +| `token` | string | Да | JWT токен аутентификации | +| `tail` | number | Нет | Количество начальных строк (по умолчанию 500) | +| `project` | string | Нет | Имя проекта (для фильтрации) | + +#### Пример подключения + +```javascript +const serviceName = "web"; +const token = "your-jwt-token"; + +const ws = new WebSocket(`ws://localhost:9001/ws/fan/${serviceName}?token=${token}&tail=100`); + +ws.onmessage = function(event) { + // Логи приходят с префиксом ID контейнера + console.log('Логи сервиса:', event.data); +}; +``` + +#### Формат сообщений + +Логи приходят с префиксом короткого ID контейнера: + +``` +[abc123de] 2024-01-15T10:30:15.123456789Z 2024/01/15 10:30:15 [notice] 1#1: start worker processes +[def456gh] 2024-01-15T10:30:16.123456789Z 2024/01/15 10:30:16 [notice] 1#1: start worker processes +``` + +### ws://host:port/ws/fan_group + +Получение логов группы сервисов одновременно. + +#### Параметры + +| Параметр | Тип | Обязательный | Описание | +|----------|-----|--------------|----------| +| `services` | string | Да | Имена сервисов через запятую | +| `token` | string | Да | JWT токен аутентификации | +| `tail` | number | Нет | Количество начальных строк (по умолчанию 500) | +| `project` | string | Нет | Имя проекта (для фильтрации) | + +#### Пример подключения + +```javascript +const services = "web,db,redis"; +const token = "your-jwt-token"; + +const ws = new WebSocket(`ws://localhost:9001/ws/fan_group?services=${services}&token=${token}&tail=100`); + +ws.onmessage = function(event) { + // Логи приходят с префиксом ID контейнера и имени сервиса + console.log('Логи группы сервисов:', event.data); +}; +``` + +#### Формат сообщений + +Логи приходят с префиксом ID контейнера и имени сервиса: + +``` +[abc123de web] 2024-01-15T10:30:15.123456789Z 2024/01/15 10:30:15 [notice] 1#1: start worker processes +[def456gh db] 2024-01-15T10:30:16.123456789Z 2024/01/15 10:30:16 [info] database system is ready to accept connections +[ghi789jk redis] 2024-01-15T10:30:17.123456789Z 2024/01/15 10:30:17 [notice] Ready to accept connections +``` + +## Примеры использования + +### JavaScript (Browser) + +```javascript +class LogBoardWebSocket { + constructor(baseUrl, token) { + this.baseUrl = baseUrl; + this.token = token; + this.connections = new Map(); + } + + // Подключение к логам контейнера + connectToContainer(containerId, options = {}) { + const { tail = 100, onMessage, onError, onClose } = options; + + const url = `ws://${this.baseUrl.replace('http://', '')}/ws/logs/${containerId}?token=${this.token}&tail=${tail}`; + const ws = new WebSocket(url); + + ws.onopen = () => { + console.log(`Подключение к контейнеру ${containerId} установлено`); + }; + + ws.onmessage = (event) => { + if (onMessage) onMessage(event.data); + }; + + ws.onerror = (error) => { + console.error(`Ошибка WebSocket для контейнера ${containerId}:`, error); + if (onError) onError(error); + }; + + ws.onclose = (event) => { + console.log(`Соединение с контейнером ${containerId} закрыто:`, event.code, event.reason); + if (onClose) onClose(event); + }; + + this.connections.set(containerId, ws); + return ws; + } + + // Подключение к логам сервиса + connectToService(serviceName, options = {}) { + const { tail = 100, project, onMessage, onError, onClose } = options; + + let url = `ws://${this.baseUrl.replace('http://', '')}/ws/fan/${serviceName}?token=${this.token}&tail=${tail}`; + if (project) url += `&project=${project}`; + + const ws = new WebSocket(url); + + ws.onopen = () => { + console.log(`Подключение к сервису ${serviceName} установлено`); + }; + + ws.onmessage = (event) => { + if (onMessage) onMessage(event.data); + }; + + ws.onerror = (error) => { + console.error(`Ошибка WebSocket для сервиса ${serviceName}:`, error); + if (onError) onError(error); + }; + + ws.onclose = (event) => { + console.log(`Соединение с сервисом ${serviceName} закрыто:`, event.code, event.reason); + if (onClose) onClose(event); + }; + + this.connections.set(serviceName, ws); + return ws; + } + + // Подключение к группе сервисов + connectToServiceGroup(services, options = {}) { + const { tail = 100, project, onMessage, onError, onClose } = options; + + let url = `ws://${this.baseUrl.replace('http://', '')}/ws/fan_group?services=${services}&token=${this.token}&tail=${tail}`; + if (project) url += `&project=${project}`; + + const ws = new WebSocket(url); + + ws.onopen = () => { + console.log(`Подключение к группе сервисов ${services} установлено`); + }; + + ws.onmessage = (event) => { + if (onMessage) onMessage(event.data); + }; + + ws.onerror = (error) => { + console.error(`Ошибка WebSocket для группы сервисов ${services}:`, error); + if (onError) onError(error); + }; + + ws.onclose = (event) => { + console.log(`Соединение с группой сервисов ${services} закрыто:`, event.code, event.reason); + if (onClose) onClose(event); + }; + + this.connections.set(`group:${services}`, ws); + return ws; + } + + // Закрытие соединения + disconnect(identifier) { + const ws = this.connections.get(identifier); + if (ws) { + ws.close(); + this.connections.delete(identifier); + } + } + + // Закрытие всех соединений + disconnectAll() { + for (const [identifier, ws] of this.connections) { + ws.close(); + } + this.connections.clear(); + } +} + +// Использование +async function main() { + // Получение токена + const loginResponse = await fetch('http://localhost:9001/api/auth/login', { + method: 'POST', + headers: {'Content-Type': 'application/json'}, + body: JSON.stringify({ + username: 'admin', + password: 'your-password' + }) + }); + const {access_token} = await loginResponse.json(); + + // Создание WebSocket клиента + const wsClient = new LogBoardWebSocket('http://localhost:9001', access_token); + + // Подключение к контейнеру + wsClient.connectToContainer('abc123def456', { + tail: 100, + onMessage: (data) => { + console.log('Логи контейнера:', data); + // Добавление в UI + appendToLogView(data); + }, + onError: (error) => { + console.error('Ошибка:', error); + } + }); + + // Подключение к сервису + wsClient.connectToService('web', { + tail: 100, + onMessage: (data) => { + console.log('Логи сервиса:', data); + // Парсинг префикса для группировки + const match = data.match(/^\[([a-f0-9]+)\]\s+(.+)$/); + if (match) { + const [, containerId, logMessage] = match; + appendToServiceLogView(containerId, logMessage); + } + } + }); + + // Подключение к группе сервисов + wsClient.connectToServiceGroup('web,db,redis', { + tail: 100, + onMessage: (data) => { + console.log('Логи группы:', data); + // Парсинг префикса для группировки + const match = data.match(/^\[([a-f0-9]+)\s+(\w+)\]\s+(.+)$/); + if (match) { + const [, containerId, serviceName, logMessage] = match; + appendToGroupLogView(serviceName, containerId, logMessage); + } + } + }); +} + +// Функции для обновления UI +function appendToLogView(message) { + const logContainer = document.getElementById('log-container'); + const logLine = document.createElement('div'); + logLine.className = 'log-line'; + logLine.textContent = message; + logContainer.appendChild(logLine); + logContainer.scrollTop = logContainer.scrollHeight; +} + +function appendToServiceLogView(containerId, message) { + // Реализация для отображения логов сервиса +} + +function appendToGroupLogView(serviceName, containerId, message) { + // Реализация для отображения логов группы сервисов +} + +main().catch(console.error); +``` + +### Python + +```python +import asyncio +import websockets +import json +import requests + +class LogBoardWebSocket: + def __init__(self, base_url, token): + self.base_url = base_url.replace('http://', 'ws://') + self.token = token + self.connections = {} + + async def connect_to_container(self, container_id, tail=100, callback=None): + """Подключение к логам контейнера""" + uri = f"{self.base_url}/ws/logs/{container_id}?token={self.token}&tail={tail}" + + try: + async with websockets.connect(uri) as websocket: + print(f"Подключение к контейнеру {container_id} установлено") + + async for message in websocket: + if callback: + await callback(container_id, message) + else: + print(f"[{container_id}] {message}") + + except websockets.exceptions.ConnectionClosed: + print(f"Соединение с контейнером {container_id} закрыто") + except Exception as e: + print(f"Ошибка подключения к контейнеру {container_id}: {e}") + + async def connect_to_service(self, service_name, tail=100, project=None, callback=None): + """Подключение к логам сервиса""" + uri = f"{self.base_url}/ws/fan/{service_name}?token={self.token}&tail={tail}" + if project: + uri += f"&project={project}" + + try: + async with websockets.connect(uri) as websocket: + print(f"Подключение к сервису {service_name} установлено") + + async for message in websocket: + if callback: + await callback(service_name, message) + else: + print(f"[{service_name}] {message}") + + except websockets.exceptions.ConnectionClosed: + print(f"Соединение с сервисом {service_name} закрыто") + except Exception as e: + print(f"Ошибка подключения к сервису {service_name}: {e}") + + async def connect_to_service_group(self, services, tail=100, project=None, callback=None): + """Подключение к логам группы сервисов""" + uri = f"{self.base_url}/ws/fan_group?services={services}&token={self.token}&tail={tail}" + if project: + uri += f"&project={project}" + + try: + async with websockets.connect(uri) as websocket: + print(f"Подключение к группе сервисов {services} установлено") + + async for message in websocket: + if callback: + await callback(services, message) + else: + print(f"[{services}] {message}") + + except websockets.exceptions.ConnectionClosed: + print(f"Соединение с группой сервисов {services} закрыто") + except Exception as e: + print(f"Ошибка подключения к группе сервисов {services}: {e}") + +# Функции обратного вызова +async def container_log_callback(container_id, message): + """Обработка логов контейнера""" + print(f"[{container_id}] {message}") + +async def service_log_callback(service_name, message): + """Обработка логов сервиса""" + print(f"[{service_name}] {message}") + +async def group_log_callback(services, message): + """Обработка логов группы сервисов""" + print(f"[{services}] {message}") + +async def main(): + # Получение токена + response = requests.post('http://localhost:9001/api/auth/login', json={ + 'username': 'admin', + 'password': 'your-password' + }) + token = response.json()['access_token'] + + # Создание WebSocket клиента + ws_client = LogBoardWebSocket('http://localhost:9001', token) + + # Создание задач для одновременного подключения + tasks = [ + ws_client.connect_to_container('abc123def456', tail=100, callback=container_log_callback), + ws_client.connect_to_service('web', tail=100, callback=service_log_callback), + ws_client.connect_to_service_group('web,db,redis', tail=100, callback=group_log_callback) + ] + + # Запуск всех подключений одновременно + await asyncio.gather(*tasks) + +if __name__ == "__main__": + asyncio.run(main()) +``` + +### Node.js + +```javascript +const WebSocket = require('ws'); +const axios = require('axios'); + +class LogBoardWebSocket { + constructor(baseUrl, token) { + this.baseUrl = baseUrl.replace('http://', 'ws://'); + this.token = token; + this.connections = new Map(); + } + + // Подключение к логам контейнера + connectToContainer(containerId, options = {}) { + const { tail = 100, onMessage, onError, onClose } = options; + + const url = `${this.baseUrl}/ws/logs/${containerId}?token=${this.token}&tail=${tail}`; + const ws = new WebSocket(url); + + ws.on('open', () => { + console.log(`Подключение к контейнеру ${containerId} установлено`); + }); + + ws.on('message', (data) => { + const message = data.toString(); + if (onMessage) onMessage(message); + else console.log(`[${containerId}] ${message}`); + }); + + ws.on('error', (error) => { + console.error(`Ошибка WebSocket для контейнера ${containerId}:`, error); + if (onError) onError(error); + }); + + ws.on('close', (code, reason) => { + console.log(`Соединение с контейнером ${containerId} закрыто:`, code, reason); + if (onClose) onClose(code, reason); + }); + + this.connections.set(containerId, ws); + return ws; + } + + // Подключение к логам сервиса + connectToService(serviceName, options = {}) { + const { tail = 100, project, onMessage, onError, onClose } = options; + + let url = `${this.baseUrl}/ws/fan/${serviceName}?token=${this.token}&tail=${tail}`; + if (project) url += `&project=${project}`; + + const ws = new WebSocket(url); + + ws.on('open', () => { + console.log(`Подключение к сервису ${serviceName} установлено`); + }); + + ws.on('message', (data) => { + const message = data.toString(); + if (onMessage) onMessage(message); + else console.log(`[${serviceName}] ${message}`); + }); + + ws.on('error', (error) => { + console.error(`Ошибка WebSocket для сервиса ${serviceName}:`, error); + if (onError) onError(error); + }); + + ws.on('close', (code, reason) => { + console.log(`Соединение с сервисом ${serviceName} закрыто:`, code, reason); + if (onClose) onClose(code, reason); + }); + + this.connections.set(serviceName, ws); + return ws; + } + + // Закрытие соединения + disconnect(identifier) { + const ws = this.connections.get(identifier); + if (ws) { + ws.close(); + this.connections.delete(identifier); + } + } + + // Закрытие всех соединений + disconnectAll() { + for (const [identifier, ws] of this.connections) { + ws.close(); + } + this.connections.clear(); + } +} + +async function main() { + try { + // Получение токена + const loginResponse = await axios.post('http://localhost:9001/api/auth/login', { + username: 'admin', + password: 'your-password' + }); + const token = loginResponse.data.access_token; + + // Создание WebSocket клиента + const wsClient = new LogBoardWebSocket('http://localhost:9001', token); + + // Подключение к контейнеру + wsClient.connectToContainer('abc123def456', { + tail: 100, + onMessage: (message) => { + console.log('Логи контейнера:', message); + } + }); + + // Подключение к сервису + wsClient.connectToService('web', { + tail: 100, + onMessage: (message) => { + console.log('Логи сервиса:', message); + } + }); + + // Обработка сигналов завершения + process.on('SIGINT', () => { + console.log('Закрытие соединений...'); + wsClient.disconnectAll(); + process.exit(0); + }); + + } catch (error) { + console.error('Ошибка:', error.message); + } +} + +main(); +``` + +## Обработка ошибок + +### Коды ошибок WebSocket + +| Код | Описание | +|-----|----------| +| 1000 | Нормальное закрытие | +| 1001 | Удаленная сторона закрыла соединение | +| 1002 | Ошибка протокола | +| 1003 | Неподдерживаемый тип данных | +| 1006 | Аномальное закрытие | +| 1011 | Внутренняя ошибка сервера | + +### Обработка ошибок аутентификации + +```javascript +ws.onmessage = function(event) { + if (event.data.startsWith('ERROR:')) { + const error = event.data.substring(6); + console.error('Ошибка аутентификации:', error); + + if (error.includes('token required') || error.includes('invalid token')) { + // Переподключение с новым токеном + reconnectWithNewToken(); + } + } else { + // Обработка обычных логов + console.log('Логи:', event.data); + } +}; +``` + +### Автоматическое переподключение + +```javascript +class ReconnectingWebSocket { + constructor(url, options = {}) { + this.url = url; + this.options = options; + this.reconnectAttempts = 0; + this.maxReconnectAttempts = options.maxReconnectAttempts || 5; + this.reconnectInterval = options.reconnectInterval || 1000; + this.connect(); + } + + connect() { + this.ws = new WebSocket(this.url); + + this.ws.onopen = () => { + console.log('WebSocket соединение установлено'); + this.reconnectAttempts = 0; + }; + + this.ws.onclose = (event) => { + console.log('WebSocket соединение закрыто:', event.code, event.reason); + + if (this.reconnectAttempts < this.maxReconnectAttempts) { + this.reconnectAttempts++; + console.log(`Попытка переподключения ${this.reconnectAttempts}/${this.maxReconnectAttempts}`); + + setTimeout(() => { + this.connect(); + }, this.reconnectInterval * this.reconnectAttempts); + } else { + console.error('Превышено максимальное количество попыток переподключения'); + } + }; + + this.ws.onerror = (error) => { + console.error('WebSocket ошибка:', error); + }; + } + + send(data) { + if (this.ws.readyState === WebSocket.OPEN) { + this.ws.send(data); + } + } + + close() { + this.ws.close(); + } +} +``` + +## Лучшие практики + +### 1. Управление соединениями + +```javascript +// Создание пула соединений +class WebSocketPool { + constructor() { + this.connections = new Map(); + this.maxConnections = 10; + } + + connect(identifier, url, options = {}) { + if (this.connections.size >= this.maxConnections) { + console.warn('Достигнут лимит соединений'); + return null; + } + + const ws = new WebSocket(url); + this.connections.set(identifier, ws); + + ws.onclose = () => { + this.connections.delete(identifier); + }; + + return ws; + } + + disconnect(identifier) { + const ws = this.connections.get(identifier); + if (ws) { + ws.close(); + this.connections.delete(identifier); + } + } +} +``` + +### 2. Обработка больших объемов данных + +```javascript +// Буферизация сообщений +class LogBuffer { + constructor(maxSize = 1000) { + this.buffer = []; + this.maxSize = maxSize; + } + + add(message) { + this.buffer.push(message); + + if (this.buffer.length > this.maxSize) { + this.buffer.shift(); // Удаляем старые сообщения + } + } + + getMessages() { + return [...this.buffer]; + } + + clear() { + this.buffer = []; + } +} + +// Использование +const logBuffer = new LogBuffer(1000); + +ws.onmessage = function(event) { + logBuffer.add(event.data); + + // Обновление UI каждые 100ms + if (!updateScheduled) { + updateScheduled = setTimeout(() => { + updateUI(logBuffer.getMessages()); + updateScheduled = null; + }, 100); + } +}; +``` + +### 3. Фильтрация логов + +```javascript +// Фильтр по уровню логирования +class LogFilter { + constructor() { + this.filters = { + level: 'all', // debug, info, warn, error, all + service: null, + container: null, + text: null + }; + } + + setFilter(type, value) { + this.filters[type] = value; + } + + shouldDisplay(message) { + // Фильтр по уровню + if (this.filters.level !== 'all') { + const levelMatch = message.toLowerCase().includes(`level=${this.filters.level}`); + if (!levelMatch) return false; + } + + // Фильтр по сервису + if (this.filters.service) { + const serviceMatch = message.includes(`[${this.filters.service}]`); + if (!serviceMatch) return false; + } + + // Фильтр по контейнеру + if (this.filters.container) { + const containerMatch = message.includes(`[${this.filters.container}]`); + if (!containerMatch) return false; + } + + // Фильтр по тексту + if (this.filters.text) { + const textMatch = message.toLowerCase().includes(this.filters.text.toLowerCase()); + if (!textMatch) return false; + } + + return true; + } +} + +// Использование +const logFilter = new LogFilter(); +logFilter.setFilter('level', 'error'); + +ws.onmessage = function(event) { + if (logFilter.shouldDisplay(event.data)) { + console.log('Отфильтрованные логи:', event.data); + } +}; +``` + +### 4. Мониторинг состояния соединений + +```javascript +class ConnectionMonitor { + constructor() { + this.stats = { + totalMessages: 0, + totalBytes: 0, + connectionTime: null, + lastMessageTime: null, + errors: 0 + }; + } + + onConnect() { + this.stats.connectionTime = new Date(); + this.stats.totalMessages = 0; + this.stats.totalBytes = 0; + this.stats.errors = 0; + } + + onMessage(message) { + this.stats.totalMessages++; + this.stats.totalBytes += message.length; + this.stats.lastMessageTime = new Date(); + } + + onError() { + this.stats.errors++; + } + + getStats() { + return { + ...this.stats, + uptime: this.stats.connectionTime ? + Date.now() - this.stats.connectionTime.getTime() : 0, + messagesPerSecond: this.stats.totalMessages / + (this.stats.uptime / 1000) || 0 + }; + } +} +``` + +## Ограничения и рекомендации + +### Ограничения + +- **Максимальное количество соединений:** 100 одновременных WebSocket соединений +- **Таймаут соединения:** 60 секунд бездействия +- **Размер сообщения:** до 1 MB на сообщение +- **Частота сообщений:** до 1000 сообщений в секунду + +### Рекомендации + +1. **Используйте переподключение** при разрыве соединений +2. **Ограничивайте количество соединений** для одного клиента +3. **Фильтруйте логи** на стороне клиента для снижения нагрузки +4. **Мониторьте состояние соединений** для диагностики проблем +5. **Используйте буферизацию** для больших объемов данных diff --git a/templates/index.html b/templates/index.html index 2b2fd6f..883a004 100644 --- a/templates/index.html +++ b/templates/index.html @@ -783,34 +783,9 @@ a{color:var(--link)} justify-content: center; } -/* Help button: visible only when sidebar is collapsed */ -.sidebar:not(.collapsed) .help-btn { display: none; } -.sidebar.collapsed .help-btn { display: flex; } - -/* При свернутом сайдбаре фиксируем help-btn снизу по центру */ -.sidebar.collapsed { position: relative; } -.sidebar.collapsed .help-btn { - position: absolute; - left: 50%; - bottom: 10px; - transform: translateX(-50%); - z-index: 2; -} - /* Компактные контролы в header: по умолчанию скрыты */ .header-compact-controls { display: none; align-items: center; gap: 6px; } - - - - -.help-btn:hover, -.logout-btn:hover { - background: var(--tab-active); - color: var(--fg); - border-color: var(--accent); -} - /* Tooltip для кнопки помощи */ .help-tooltip { position: absolute; @@ -929,6 +904,28 @@ a{color:var(--link)} text-decoration: underline; } + + + + +.help-btn:hover { + background: #a78bfa; + color: #0b0d12; + border-color: #a78bfa; + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(187, 154, 247, 0.5); +} + +.logout-btn:hover { + background: var(--tab-active); + color: var(--fg); + border-color: var(--accent); +} + + + + + /* Специальный hover эффект для кнопки options с цветом accent */ .options-btn:hover { background: var(--accent) !important; /* Цвет логотипа */ @@ -1367,6 +1364,39 @@ a{color:var(--link)} box-shadow: 0 2px 4px rgba(0,0,0,0.2); } +/* Стили для кнопки помощи в header */ +.help-btn { + background: var(--accent); + color: #0b0d12; + border: 1px solid var(--accent); + border-radius: 6px; + padding: 6px 8px; + font-size: 11px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + justify-content: center; + min-width: 32px; + text-align: center; + box-shadow: 0 2px 6px rgba(187, 154, 247, 0.3); +} + +.help-btn i { + font-size: 12px; + animation: help-pulse 2s ease-in-out infinite; +} + +@keyframes help-pulse { + 0%, 100% { + transform: scale(1); + } + 50% { + transform: scale(1.1); + } +} + /* Стили для счетчиков-кнопок */ .counter-btn { display: inline-flex; @@ -2514,42 +2544,6 @@ footer{position:fixed;right:10px;bottom:10px;opacity:.6;font-size:11px} - - - -
-
- -
LogBoard+
-
- -
-
О проекте
-
- Веб-интерфейс для мониторинга логов Docker контейнеров в реальном времени с поддержкой мультивыбора и автоматического обновления. -
-
- -
-
Горячие клавиши
-
-
[
-
Предыдущий контейнер
-
]
-
Следующий контейнер
-
Ctrl+R
-
Обновить логи
-
Ctrl+B
-
Свернуть sidebar
-
-
- - -
@@ -2732,6 +2726,46 @@ footer{position:fixed;right:10px;bottom:10px;opacity:.6;font-size:11px} + + + +
+
+ +
LogBoard+
+
+ +
+
О проекте
+
+ Веб-интерфейс для мониторинга логов Docker контейнеров в реальном времени с поддержкой мультивыбора и автоматического обновления. +
+
+ +
+
Горячие клавиши
+
+
[
+
Предыдущий контейнер
+
]
+
Следующий контейнер
+
Ctrl+R
+
Обновить логи
+
Ctrl+B
+
Свернуть sidebar
+
Ctrl+F
+
Фокус на фильтр
+
Ctrl+Shift+M
+
Мультивыбор контейнеров
+
+
+ + +
@@ -4023,6 +4057,8 @@ function positionTooltip(event, tooltip) { }, 10); } + + // Функции для help tooltip function showHelpTooltip() { const helpBtn = document.getElementById('helpBtn'); @@ -6940,10 +6976,7 @@ if (els.sidebarToggle) { els.sidebarToggle.onclick = toggleSidebar; } -// Help button -if (els.helpBtn) { - els.helpBtn.onclick = toggleHotkeysModal; -} + // Modal close button if (els.hotkeysModalClose) { @@ -7090,6 +7123,22 @@ document.addEventListener('DOMContentLoaded', () => { }); } + + + // Инициализируем стили логов при загрузке страницы + updateLogStyles(); + + // Применяем настройки wrap text при загрузке + applyWrapSettings(); + + // Дополнительная проверка для multi-view логов при загрузке + setTimeout(() => { + if (state.multiViewMode) { + console.log('Initialization: Force fixing multi-view styles'); + forceFixMultiViewStyles(); + } + }, 1000); + // Обработчик для кнопки помощи if (els.helpBtn) { const helpTooltip = document.getElementById('helpTooltip'); @@ -7125,20 +7174,6 @@ document.addEventListener('DOMContentLoaded', () => { }); } - // Инициализируем стили логов при загрузке страницы - updateLogStyles(); - - // Применяем настройки wrap text при загрузке - applyWrapSettings(); - - // Дополнительная проверка для multi-view логов при загрузке - setTimeout(() => { - if (state.multiViewMode) { - console.log('Initialization: Force fixing multi-view styles'); - forceFixMultiViewStyles(); - } - }, 1000); - }); if (els.snapshotBtn) { els.snapshotBtn.onclick = ()=>{