From 4a36a04d82234cfb8ea04cfb65bf45e20c6d9f97 Mon Sep 17 00:00:00 2001 From: Sergey Antropoff Date: Mon, 15 Sep 2025 17:55:23 +0300 Subject: [PATCH] =?UTF-8?q?feat:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B0=20=D0=BF=D0=BE=D0=B4=D0=B4=D0=B5=D1=80=D0=B6?= =?UTF-8?q?=D0=BA=D0=B0=20=D0=BE=D0=B1=D0=BD=D0=B0=D1=80=D1=83=D0=B6=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20Docker-=D0=BA=D0=BE=D0=BD=D1=82=D0=B5?= =?UTF-8?q?=D0=B9=D0=BD=D0=B5=D1=80=D0=BE=D0=B2=20=D0=B2=20=D0=BA=D0=BE?= =?UTF-8?q?=D0=BB=D0=BB=D0=B5=D0=BA=D1=82=D0=BE=D1=80=20proxvmservices?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **Новые возможности:** - Обнаружение сервисов, запущенных в 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 --- docs/collectors/proxvmservices.md | 72 ++++++++---- .../proxvmservices/proxvmservices_linux.go | 106 ++++++++++++++++-- 2 files changed, 145 insertions(+), 33 deletions(-) diff --git a/docs/collectors/proxvmservices.md b/docs/collectors/proxvmservices.md index 04a4372..41a07ad 100644 --- a/docs/collectors/proxvmservices.md +++ b/docs/collectors/proxvmservices.md @@ -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 diff --git a/src/collectors/proxvmservices/proxvmservices_linux.go b/src/collectors/proxvmservices/proxvmservices_linux.go index 35fa94d..13c8415 100644 --- a/src/collectors/proxvmservices/proxvmservices_linux.go +++ b/src/collectors/proxvmservices/proxvmservices_linux.go @@ -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 }