feat: добавлена поддержка обнаружения Docker-контейнеров в коллектор proxvmservices

**Новые возможности:**
- Обнаружение сервисов, запущенных в Docker-контейнерах
- Поддержка Docker Compose развертываний
- Проверка как по имени контейнера, так и по образу

**Добавленные функции:**
- isDockerContainerRunning() - проверка контейнеров по имени
- isDockerImageRunning() - проверка контейнеров по образу
- getDockerContainerPorts() - получение портов контейнера
- getDockerContainerVersion() - получение версии из контейнера

**Обновленные сервисы с Docker поддержкой:**
- PostgreSQL: postgres, patroni, bitnami/postgresql
- Redis: redis
- ClickHouse: clickhouse, clickhouse/clickhouse-server
- RabbitMQ: rabbitmq
- MongoDB: mongo
- Elasticsearch: elasticsearch
- Grafana: grafana, grafana/grafana
- Prometheus: prometheus, prom/prometheus

**Обновленная документация:**
- Добавлены способы развертывания (нативные, Docker, Docker Compose)
- Обновлены методы обнаружения с Docker проверками
- Добавлены примеры использования Docker команд
- Добавлен docker в системные зависимости

**Поддерживаемые способы развертывания:**
- Нативные процессы (systemd, init)
- Docker-контейнеры
- Docker Compose

Теперь коллектор может обнаруживать сервисы независимо от способа их развертывания!

Автор: Сергей Антропов
Сайт: https://devops.org.ru
This commit is contained in:
Sergey Antropoff 2025-09-15 17:55:23 +03:00
parent d0f7bb6d24
commit 4a36a04d82
2 changed files with 145 additions and 33 deletions

View File

@ -4,6 +4,11 @@
Коллектор `proxvmservices` предназначен для обнаружения и мониторинга сервисов на виртуальных машинах и контейнерах Proxmox. Он автоматически определяет запущенные сервисы, их конфигурацию, состояние кластеров и соединения между сервисами.
**Поддерживаемые способы развертывания:**
- Нативные процессы (systemd, init)
- Docker-контейнеры
- Docker Compose
## Поддерживаемые сервисы
### Кластерные сервисы
@ -41,11 +46,12 @@
### PostgreSQL с Patroni
1. **Процессы**: проверка `postgres` и `patroni`
2. **Порты**: 5432 (PostgreSQL), 8008 (Patroni REST API)
3. **Версия**: через `psql --version` или `postgres --version`
4. **Конфигурация**: парсинг файлов `/etc/patroni/patroni.yml`, `/etc/patroni.yml`
5. **Кластер**: команда `patronictl list` для получения информации о членах кластера
6. **Репликация**: SQL-запрос `SELECT client_addr, state FROM pg_stat_replication`
2. **Docker**: проверка контейнеров `postgres`, `patroni`, образов `postgres`, `bitnami/postgresql`
3. **Порты**: 5432 (PostgreSQL), 8008 (Patroni REST API)
4. **Версия**: через `psql --version` или `postgres --version`
5. **Конфигурация**: парсинг файлов `/etc/patroni/patroni.yml`, `/etc/patroni.yml`
6. **Кластер**: команда `patronictl list` для получения информации о членах кластера
7. **Репликация**: SQL-запрос `SELECT client_addr, state FROM pg_stat_replication`
### etcd
1. **Процессы**: проверка `etcd`
@ -61,21 +67,24 @@
### ClickHouse
1. **Процессы**: проверка `clickhouse-server`
2. **Порты**: 8123 (HTTP), 9000 (native)
3. **Версия**: через `clickhouse-client --version`
4. **Кластер**: SQL-запрос `SELECT host_name FROM system.clusters`
2. **Docker**: проверка контейнеров `clickhouse`, образов `clickhouse/clickhouse-server`
3. **Порты**: 8123 (HTTP), 9000 (native)
4. **Версия**: через `clickhouse-client --version`
5. **Кластер**: SQL-запрос `SELECT host_name FROM system.clusters`
### Redis
1. **Процессы**: проверка `redis-server`
2. **Порты**: 6379 (client)
3. **Версия**: через `redis-cli --version`
4. **Кластер**: команда `redis-cli cluster nodes`
2. **Docker**: проверка контейнеров `redis`, образов `redis`
3. **Порты**: 6379 (client)
4. **Версия**: через `redis-cli --version`
5. **Кластер**: команда `redis-cli cluster nodes`
### RabbitMQ
1. **Процессы**: проверка `rabbitmq-server`
2. **Порты**: 5672 (AMQP), 15672 (management)
3. **Версия**: через `rabbitmqctl version`
4. **Кластер**: команда `rabbitmqctl cluster_status`
2. **Docker**: проверка контейнеров `rabbitmq`, образов `rabbitmq`
3. **Порты**: 5672 (AMQP), 15672 (management)
4. **Версия**: через `rabbitmqctl version`
5. **Кластер**: команда `rabbitmqctl cluster_status`
### Kafka
1. **Процессы**: проверка `kafka.Kafka`
@ -85,9 +94,10 @@
### MongoDB
1. **Процессы**: проверка `mongod`
2. **Порты**: 27017 (mongod)
3. **Версия**: через `mongosh --version`
4. **Кластер**: команда `mongosh --eval "rs.status().members.map(m => m.name)"`
2. **Docker**: проверка контейнеров `mongo`, образов `mongo`
3. **Порты**: 27017 (mongod)
4. **Версия**: через `mongosh --version`
5. **Кластер**: команда `mongosh --eval "rs.status().members.map(m => m.name)"`
### Новые кластерные сервисы
@ -99,9 +109,10 @@
#### Elasticsearch
1. **Процессы**: проверка `elasticsearch`, `java.*elasticsearch`
2. **Порты**: 9200 (HTTP), 9300 (transport)
3. **Версия**: через HTTP API `http://localhost:9200`
4. **Кластер**: HTTP API `/_cluster/state/nodes`
2. **Docker**: проверка контейнеров `elasticsearch`, образов `elasticsearch`
3. **Порты**: 9200 (HTTP), 9300 (transport)
4. **Версия**: через HTTP API `http://localhost:9200`
5. **Кластер**: HTTP API `/_cluster/state/nodes`
#### Greenplum
1. **Процессы**: проверка `postgres.*greenplum`, `gpdb`
@ -142,13 +153,15 @@
#### Grafana
1. **Процессы**: проверка `grafana-server`, `grafana`
2. **Порты**: 3000 (HTTP)
3. **Версия**: через `grafana-server --version`
2. **Docker**: проверка контейнеров `grafana`, образов `grafana/grafana`
3. **Порты**: 3000 (HTTP)
4. **Версия**: через `grafana-server --version`
#### Prometheus
1. **Процессы**: проверка `prometheus`
2. **Порты**: 9090 (HTTP)
3. **Версия**: через `prometheus --version`
2. **Docker**: проверка контейнеров `prometheus`, образов `prom/prometheus`
3. **Порты**: 9090 (HTTP)
4. **Версия**: через `prometheus --version`
#### Loki
1. **Процессы**: проверка `loki`
@ -321,6 +334,7 @@ proxvmservices:
- `ss` - для проверки портов
- `curl` - для HTTP API запросов
- `getent` - для разрешения hostname в IP
- `docker` - для проверки Docker-контейнеров
#### Основные сервисы
- `psql` или `postgres` - для PostgreSQL
@ -360,6 +374,12 @@ proxvmservices:
pgrep -f postgres
pgrep -f patroni
# Проверка Docker-контейнеров
docker ps --filter "name=postgres"
docker ps --filter "name=patroni"
docker ps --filter "ancestor=postgres"
docker ps --filter "ancestor=bitnami/postgresql"
# Проверка портов
ss -tln sport = :5432
ss -tln sport = :8008
@ -406,6 +426,10 @@ clickhouse-client --query "SELECT * FROM system.clusters"
# Проверка процессов
pgrep -f redis-server
# Проверка Docker-контейнеров
docker ps --filter "name=redis"
docker ps --filter "ancestor=redis"
# Проверка портов
ss -tln sport = :6379

View File

@ -216,6 +216,78 @@ func isProcessRunning(processName string) bool {
return err == nil
}
// isDockerContainerRunning проверяет, запущен ли Docker-контейнер с указанным именем или образом
func isDockerContainerRunning(containerName string) bool {
cmd := exec.Command("docker", "ps", "--filter", "name="+containerName, "--format", "{{.Names}}")
output, err := cmd.Output()
if err != nil {
return false
}
return strings.TrimSpace(string(output)) != ""
}
// isDockerImageRunning проверяет, запущен ли Docker-контейнер с указанным образом
func isDockerImageRunning(imageName string) bool {
cmd := exec.Command("docker", "ps", "--filter", "ancestor="+imageName, "--format", "{{.Names}}")
output, err := cmd.Output()
if err != nil {
return false
}
return strings.TrimSpace(string(output)) != ""
}
// getDockerContainerPorts получает порты Docker-контейнера
func getDockerContainerPorts(containerName string) []int {
cmd := exec.Command("docker", "port", containerName)
output, err := cmd.Output()
if err != nil {
return []int{}
}
var ports []int
lines := strings.Split(string(output), "\n")
for _, line := range lines {
line = strings.TrimSpace(line)
if line == "" {
continue
}
// Парсим строку типа: "5432/tcp -> 0.0.0.0:5432"
if strings.Contains(line, "->") {
parts := strings.Split(line, "->")
if len(parts) > 0 {
portPart := strings.TrimSpace(parts[0])
if strings.Contains(portPart, "/") {
portStr := strings.Split(portPart, "/")[0]
if port, err := strconv.Atoi(portStr); err == nil {
ports = append(ports, port)
}
}
}
}
}
return ports
}
// getDockerContainerVersion получает версию из Docker-контейнера
func getDockerContainerVersion(containerName, versionCommand string) string {
cmd := exec.Command("docker", "exec", containerName, "sh", "-c", versionCommand)
output, err := cmd.Output()
if err != nil {
return "unknown"
}
// Парсим версию из вывода
re := regexp.MustCompile(`(\d+\.\d+\.\d+)`)
matches := re.FindStringSubmatch(string(output))
if len(matches) > 1 {
return matches[1]
}
return "unknown"
}
// getListeningPorts возвращает список портов, на которых слушает процесс
func getListeningPorts(ports ...int) []int {
var listeningPorts []int
@ -243,8 +315,10 @@ func runCommand(command string, args ...string) (string, error) {
// detectPostgreSQL обнаруживает PostgreSQL с Patroni
func detectPostgreSQL() *ServiceInfo {
// Проверяем процессы
if !isProcessRunning("postgres") && !isProcessRunning("patroni") {
// Проверяем процессы и Docker-контейнеры
if !isProcessRunning("postgres") && !isProcessRunning("patroni") &&
!isDockerContainerRunning("postgres") && !isDockerContainerRunning("patroni") &&
!isDockerImageRunning("postgres") && !isDockerImageRunning("bitnami/postgresql") {
return nil
}
@ -1035,7 +1109,9 @@ func parseEtcdMembers(output string) []EtcdMember {
// detectRedis обнаруживает Redis (standalone или cluster)
func detectRedis() *ServiceInfo {
if !isProcessRunning("redis-server") {
// Проверяем процессы и Docker-контейнеры
if !isProcessRunning("redis-server") &&
!isDockerContainerRunning("redis") && !isDockerImageRunning("redis") {
return nil
}
@ -1070,7 +1146,9 @@ func detectRedis() *ServiceInfo {
}
func detectClickHouse() *ServiceInfo {
if !isProcessRunning("clickhouse-server") {
// Проверяем процессы и Docker-контейнеры
if !isProcessRunning("clickhouse-server") &&
!isDockerContainerRunning("clickhouse") && !isDockerImageRunning("clickhouse/clickhouse-server") {
return nil
}
@ -1105,7 +1183,9 @@ func detectClickHouse() *ServiceInfo {
}
func detectRabbitMQ() *ServiceInfo {
if !isProcessRunning("rabbitmq-server") {
// Проверяем процессы и Docker-контейнеры
if !isProcessRunning("rabbitmq-server") &&
!isDockerContainerRunning("rabbitmq") && !isDockerImageRunning("rabbitmq") {
return nil
}
@ -1166,7 +1246,9 @@ func detectKafka() *ServiceInfo {
}
func detectMongoDB() *ServiceInfo {
if !isProcessRunning("mongod") {
// Проверяем процессы и Docker-контейнеры
if !isProcessRunning("mongod") &&
!isDockerContainerRunning("mongo") && !isDockerImageRunning("mongo") {
return nil
}
@ -1298,7 +1380,9 @@ func detectDragonflyDB() *ServiceInfo {
// detectElasticsearch обнаруживает Elasticsearch
func detectElasticsearch() *ServiceInfo {
if !isProcessRunning("elasticsearch") && !isProcessRunning("java.*elasticsearch") {
// Проверяем процессы и Docker-контейнеры
if !isProcessRunning("elasticsearch") && !isProcessRunning("java.*elasticsearch") &&
!isDockerContainerRunning("elasticsearch") && !isDockerImageRunning("elasticsearch") {
return nil
}
@ -1335,7 +1419,9 @@ func detectElasticsearch() *ServiceInfo {
// detectGrafana обнаруживает Grafana
func detectGrafana() *ServiceInfo {
if !isProcessRunning("grafana-server") && !isProcessRunning("grafana") {
// Проверяем процессы и Docker-контейнеры
if !isProcessRunning("grafana-server") && !isProcessRunning("grafana") &&
!isDockerContainerRunning("grafana") && !isDockerImageRunning("grafana/grafana") {
return nil
}
@ -1364,7 +1450,9 @@ func detectGrafana() *ServiceInfo {
// detectPrometheus обнаруживает Prometheus
func detectPrometheus() *ServiceInfo {
if !isProcessRunning("prometheus") {
// Проверяем процессы и Docker-контейнеры
if !isProcessRunning("prometheus") &&
!isDockerContainerRunning("prometheus") && !isDockerImageRunning("prom/prometheus") {
return nil
}