Добавлена полная документация проекта LogBoard+
- Создан основной README.md с описанием проекта - Добавлена подробная документация в папке docs/ - Создан файл LICENSE (MIT) - Обновлен .gitignore - Добавлена документация по безопасности с генерацией ключей - Включены примеры конфигураций и устранение неполадок Автор: Сергей Антропов Сайт: https://devops.org.ru
This commit is contained in:
parent
86a2c44333
commit
5c8efe2644
90
.gitignore
vendored
90
.gitignore
vendored
@ -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/
|
||||
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -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.
|
385
README.md
Normal file
385
README.md
Normal file
@ -0,0 +1,385 @@
|
||||
# LogBoard+
|
||||
|
||||
**Веб-панель для просмотра логов микросервисов**
|
||||
|
||||
[](https://www.python.org/)
|
||||
[](https://fastapi.tiangolo.com/)
|
||||
[](https://www.docker.com/)
|
||||
[](LICENSE)
|
||||
|
||||
**Автор:** Сергей Антропов
|
||||
**Сайт:** https://devops.org.ru
|
||||
**Версия:** 1.0.0
|
||||
|
||||
## Описание
|
||||
|
||||
LogBoard+ - это современная веб-панель для мониторинга и просмотра логов Docker контейнеров в реальном времени. Приложение предоставляет удобный веб-интерфейс для работы с логами микросервисов, поддерживает множественные проекты Docker Compose и включает в себя функции безопасности.
|
||||
|
||||
### Основные возможности
|
||||
|
||||
- **Просмотр логов в реальном времени** - WebSocket соединения для live-логов
|
||||
- **Поддержка множественных проектов** - Фильтрация по проектам Docker Compose
|
||||
- **Безопасность** - JWT аутентификация и авторизация
|
||||
- **Фильтрация контейнеров** - Исключение проблемных контейнеров
|
||||
- **Снимки логов** - Сохранение логов в файлы для анализа
|
||||
- **Статистика** - Анализ уровней логирования
|
||||
- **Адаптивный интерфейс** - Поддержка светлой и темной темы
|
||||
- **WebSocket API** - Для интеграции с внешними системами
|
||||
|
||||
## Скриншоты
|
||||
|
||||
### Светлая тема
|
||||

|
||||
|
||||
### Темная тема
|
||||

|
||||
|
||||
## Быстрый старт
|
||||
|
||||
### Предварительные требования
|
||||
|
||||
- Docker Engine 20.10+
|
||||
- Docker Compose 2.0+
|
||||
- 1 GB RAM
|
||||
- 1 CPU core
|
||||
|
||||
### Установка и запуск
|
||||
|
||||
1. **Клонирование репозитория**
|
||||
```bash
|
||||
git clone <repository-url>
|
||||
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 <repository-url>
|
||||
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+** - Удобный просмотр логов микросервисов в реальном времени.
|
657
docs/api.md
Normal file
657
docs/api.md
Normal file
@ -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 <token>
|
||||
```
|
||||
|
||||
## Аутентификация
|
||||
|
||||
### 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 <token>
|
||||
```
|
||||
|
||||
**Ответ:**
|
||||
|
||||
```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 на запрос
|
503
docs/configuration.md
Normal file
503
docs/configuration.md
Normal file
@ -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
|
||||
```
|
313
docs/index.md
Normal file
313
docs/index.md
Normal file
@ -0,0 +1,313 @@
|
||||
# Документация LogBoard+
|
||||
|
||||
**Автор:** Сергей Антропов
|
||||
**Сайт:** https://devops.org.ru
|
||||
|
||||
## Обзор
|
||||
|
||||
LogBoard+ - это современная веб-панель для мониторинга и просмотра логов Docker контейнеров в реальном времени. Приложение предоставляет удобный веб-интерфейс для работы с логами микросервисов, поддерживает множественные проекты Docker Compose и включает в себя функции безопасности.
|
||||
|
||||
## Быстрый старт
|
||||
|
||||
### Установка
|
||||
|
||||
```bash
|
||||
# Клонирование репозитория
|
||||
git clone <repository-url>
|
||||
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+** - Удобный просмотр логов микросервисов в реальном времени.
|
430
docs/installation.md
Normal file
430
docs/installation.md
Normal file
@ -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)
|
||||
|
750
docs/security.md
Normal file
750
docs/security.md
Normal file
@ -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 <<EOF
|
||||
{
|
||||
"tls": true,
|
||||
"tlscacert": "/etc/docker/ca.pem",
|
||||
"tlscert": "/etc/docker/server-cert.pem",
|
||||
"tlskey": "/etc/docker/server-key.pem",
|
||||
"hosts": ["tcp://0.0.0.0:2376", "unix:///var/run/docker.sock"]
|
||||
}
|
||||
EOF
|
||||
|
||||
# Перезапуск Docker
|
||||
sudo systemctl restart docker
|
||||
```
|
||||
|
||||
#### Метод 2: Docker API с аутентификацией
|
||||
|
||||
```bash
|
||||
# Создание пользователя для API
|
||||
sudo useradd -r -s /bin/false docker-api
|
||||
|
||||
# Настройка прав
|
||||
sudo usermod -aG docker docker-api
|
||||
|
||||
# Конфигурация в .env
|
||||
DOCKER_HOST=tcp://localhost:2375
|
||||
DOCKER_TLS_VERIFY=1
|
||||
DOCKER_CERT_PATH=/path/to/certs
|
||||
```
|
||||
|
||||
## Сетевая безопасность
|
||||
|
||||
### Настройка файрвола
|
||||
|
||||
```bash
|
||||
# Установка UFW
|
||||
sudo apt install ufw
|
||||
|
||||
# Базовые правила
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw default allow outgoing
|
||||
|
||||
# Разрешение SSH
|
||||
sudo ufw allow ssh
|
||||
|
||||
# Разрешение HTTPS
|
||||
sudo ufw allow 443/tcp
|
||||
|
||||
# Разрешение LogBoard+ только для локальной сети
|
||||
sudo ufw allow from 192.168.1.0/24 to any port 9001
|
||||
|
||||
# Включение файрвола
|
||||
sudo ufw enable
|
||||
|
||||
# Проверка статуса
|
||||
sudo ufw status verbose
|
||||
```
|
||||
|
||||
### iptables правила
|
||||
|
||||
```bash
|
||||
# Очистка правил
|
||||
sudo iptables -F
|
||||
sudo iptables -X
|
||||
|
||||
# Установка политик по умолчанию
|
||||
sudo iptables -P INPUT DROP
|
||||
sudo iptables -P FORWARD DROP
|
||||
sudo iptables -P OUTPUT ACCEPT
|
||||
|
||||
# Разрешение локального трафика
|
||||
sudo iptables -A INPUT -i lo -j ACCEPT
|
||||
|
||||
# Разрешение установленных соединений
|
||||
sudo iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
||||
|
||||
# Разрешение SSH
|
||||
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
|
||||
|
||||
# Разрешение HTTPS
|
||||
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT
|
||||
|
||||
# Разрешение LogBoard+ для локальной сети
|
||||
sudo iptables -A INPUT -p tcp --dport 9001 -s 192.168.1.0/24 -j ACCEPT
|
||||
|
||||
# Сохранение правил
|
||||
sudo iptables-save > /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 <<EOF
|
||||
/var/log/logboard/*.log {
|
||||
daily
|
||||
missingok
|
||||
rotate 30
|
||||
compress
|
||||
delaycompress
|
||||
notifempty
|
||||
create 644 $USER $USER
|
||||
postrotate
|
||||
docker compose -f /path/to/logboard/docker-compose.yml restart logboard
|
||||
endscript
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
### Мониторинг доступа
|
||||
|
||||
```bash
|
||||
# Скрипт мониторинга доступа
|
||||
cat > 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
|
||||
- [ ] Настроен файрвол
|
||||
- [ ] Включено логирование
|
||||
- [ ] Настроен мониторинг
|
||||
- [ ] Созданы резервные копии
|
||||
- [ ] Документированы процедуры
|
||||
- [ ] Проведено тестирование безопасности
|
767
docs/troubleshooting.md
Normal file
767
docs/troubleshooting.md
Normal file
@ -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 <container_id> | 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 <container_id> | 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 <container_id>
|
||||
|
||||
# Проверка статуса контейнера
|
||||
docker inspect <container_id> | grep -A 5 "State"
|
||||
|
||||
# Проверка прав доступа к логам
|
||||
docker logs <container_id> 2>&1
|
||||
```
|
||||
|
||||
#### Решения
|
||||
|
||||
1. **Контейнер остановлен**
|
||||
```bash
|
||||
# Запуск контейнера
|
||||
docker start <container_id>
|
||||
|
||||
# Проверка статуса
|
||||
docker ps
|
||||
```
|
||||
|
||||
2. **Проблема с драйвером логирования**
|
||||
```bash
|
||||
# Проверка драйвера логирования
|
||||
docker inspect <container_id> | grep "LogDriver"
|
||||
|
||||
# Изменение драйвера логирования
|
||||
docker run --log-driver=json-file ...
|
||||
```
|
||||
|
||||
3. **Проблема с размером логов**
|
||||
```bash
|
||||
# Очистка логов контейнера
|
||||
docker logs --since 1h <container_id>
|
||||
|
||||
# Ограничение размера логов
|
||||
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'
|
||||
```
|
913
docs/websocket.md
Normal file
913
docs/websocket.md
Normal file
@ -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. **Используйте буферизацию** для больших объемов данных
|
@ -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}
|
||||
<button class="options-btn" id="optionsBtn" title="Показать/скрыть настройки">
|
||||
<i class="fas fa-cog"></i>
|
||||
</button>
|
||||
<button class="help-btn" id="helpBtn" title="Горячие клавиши">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
</button>
|
||||
|
||||
<!-- Tooltip для кнопки помощи -->
|
||||
<div class="help-tooltip" id="helpTooltip">
|
||||
<div class="help-tooltip-header">
|
||||
<div class="help-tooltip-logo">LB</div>
|
||||
<div class="help-tooltip-title">LogBoard+</div>
|
||||
</div>
|
||||
|
||||
<div class="help-tooltip-section">
|
||||
<div class="help-tooltip-section-title">О проекте</div>
|
||||
<div class="help-tooltip-content">
|
||||
Веб-интерфейс для мониторинга логов Docker контейнеров в реальном времени с поддержкой мультивыбора и автоматического обновления.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-tooltip-section">
|
||||
<div class="help-tooltip-section-title">Горячие клавиши</div>
|
||||
<div class="help-tooltip-hotkeys">
|
||||
<div class="help-tooltip-hotkey">[</div>
|
||||
<div class="help-tooltip-description">Предыдущий контейнер</div>
|
||||
<div class="help-tooltip-hotkey">]</div>
|
||||
<div class="help-tooltip-description">Следующий контейнер</div>
|
||||
<div class="help-tooltip-hotkey">Ctrl+R</div>
|
||||
<div class="help-tooltip-description">Обновить логи</div>
|
||||
<div class="help-tooltip-hotkey">Ctrl+B</div>
|
||||
<div class="help-tooltip-description">Свернуть sidebar</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-tooltip-author">
|
||||
Автор: <a href="https://devops.org.ru" target="_blank">Сергей Антропов</a>
|
||||
</div>
|
||||
</div>
|
||||
<button class="logout-btn" id="logoutBtn" title="Выйти">
|
||||
<i class="fas fa-sign-out-alt"></i>
|
||||
</button>
|
||||
@ -2732,6 +2726,46 @@ footer{position:fixed;right:10px;bottom:10px;opacity:.6;font-size:11px}
|
||||
<button class="btn btn-small log-refresh-btn" title="Обновить логи и счетчики">
|
||||
<i class="fas fa-sync-alt"></i> Refresh
|
||||
</button>
|
||||
<button class="help-btn" id="helpBtn" title="Справка">
|
||||
<i class="fas fa-question-circle"></i>
|
||||
</button>
|
||||
|
||||
<!-- Tooltip для кнопки помощи -->
|
||||
<div class="help-tooltip" id="helpTooltip">
|
||||
<div class="help-tooltip-header">
|
||||
<div class="help-tooltip-logo">LB</div>
|
||||
<div class="help-tooltip-title">LogBoard+</div>
|
||||
</div>
|
||||
|
||||
<div class="help-tooltip-section">
|
||||
<div class="help-tooltip-section-title">О проекте</div>
|
||||
<div class="help-tooltip-content">
|
||||
Веб-интерфейс для мониторинга логов Docker контейнеров в реальном времени с поддержкой мультивыбора и автоматического обновления.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-tooltip-section">
|
||||
<div class="help-tooltip-section-title">Горячие клавиши</div>
|
||||
<div class="help-tooltip-hotkeys">
|
||||
<div class="help-tooltip-hotkey">[</div>
|
||||
<div class="help-tooltip-description">Предыдущий контейнер</div>
|
||||
<div class="help-tooltip-hotkey">]</div>
|
||||
<div class="help-tooltip-description">Следующий контейнер</div>
|
||||
<div class="help-tooltip-hotkey">Ctrl+R</div>
|
||||
<div class="help-tooltip-description">Обновить логи</div>
|
||||
<div class="help-tooltip-hotkey">Ctrl+B</div>
|
||||
<div class="help-tooltip-description">Свернуть sidebar</div>
|
||||
<div class="help-tooltip-hotkey">Ctrl+F</div>
|
||||
<div class="help-tooltip-description">Фокус на фильтр</div>
|
||||
<div class="help-tooltip-hotkey">Ctrl+Shift+M</div>
|
||||
<div class="help-tooltip-description">Мультивыбор контейнеров</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="help-tooltip-author">
|
||||
Автор: <a href="https://devops.org.ru" target="_blank">Сергей Антропов</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -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 = ()=>{
|
||||
|
Loading…
x
Reference in New Issue
Block a user