Prometheus. Сбор метрик php-fpm.

Подготовка к сборке метрик php-fpm делится на несколько этапов:
— редактирование конфиг пула;
— пробросить доступ к статусам PHP-FPM;
— запуск экспортера.

Настройка пула.
Настройки пула хранятся в файле www.conf. Хочу обратить ваше внимание, что файл может располагаться в разных места, например:
— /etc/php-fpm.d/www.conf
— /etc/php/7.3/fpm/pool.d/www.conf

Первым делом открываем к статистике, для этого нужно задать параметр pm.status_path. Раскомментируем строку с этим параметром:

# vim www.conf
...
pm.status_path = /status
...
ping.path = /ping
...

Задавать параметр ping.path необязательно.

Важно проверить проверить адрес, по которому принимаются запросы FastCGI, за это отвечает параметр listen:

# cat /etc/php-fpm.d/www.conf |grep -e 'listen =' 

listen = 127.0.0.1:9000

Адрес может быть в формате ip.address:port, либо ссылка на php-socket:

# cat /etc/php/7.3/fpm/pool.d/www.conf |grep -e 'listen ='
listen = /run/php/php7.3-fpm.sock

От значения параметра listen будет зависеть следующий пункт настройки.

Настройка Nginx.
Опустим основную настройку Nginx, в данном случаем нам нужно лишь пробросить страницу со статусами для экспортера.

для этого достаточно добавить в конфиг следующие строки:

location ~ ^/(status|ping)$ {
allow all;
fastcgi_pass 127.0.0.1:9000;
#fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
include fastcgi_params;
}

Если пул был настрое на сокет, то параметр fastcgi_pass будет иметь значение: fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
Важно отметить, если вы используете это на проде, то заменить параметр allow all; на необходимые адреса или подсети, а остальным запретить доступ параметром deny all;

В целом примерный конфиг будет выглядеть так:

server {
    server_name localhost;
    listen   9000; 

    location ~ ^/(status|ping)$ {
        allow 172.0.0.0/32;
        allow 127.0.0.1/32;
        deny all;
        fastcgi_pass 127.0.0.1:9000;
        #fastcgi_pass unix:/var/run/php/php7.3-fpm.sock;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param SCRIPT_NAME $fastcgi_script_name;
        fastcgi_buffer_size 128k;
        fastcgi_buffers 256 16k;
        fastcgi_busy_buffers_size 256k;
        fastcgi_temp_file_write_size 256k;
        include fastcgi_params;
    }

}

Убедимся что все работает, открываем станицу или кидаем curl для проверки промежуточного результата:

Вывод curl на соответствующий адрес:

# curl localhost:9000/status
...
pool:                 www
process manager:      dynamic
start time:           12/Dec/2019:13:43:47 +0300
start since:          78
accepted conn:        4
listen queue:         0
max listen queue:     0
listen queue len:     128
idle processes:       4
active processes:     1
total processes:      5
max active processes: 1
max children reached: 0
slow requests:        0

Все работает, остался финальный этап.

Запуск экспортера.

Официальном сайт Prometheus предлагает php-fpm-exporter за авторством bakins, его и будем использовать.

Рассмотрим параметры запуска php-fpm-exporter:

# php-fpm-exporter --help

usage: php-fpm-exporter [<flags>]

Flags:
  -h, --help                   Show context-sensitive help (also try --help-long and --help-man).
      --addr="127.0.0.1:8080"  listen address for metrics handler
      --endpoint="http://127.0.0.1:9000/status"  
                               url for php-fpm status
      --fastcgi=FASTCGI        fastcgi url. If this is set, fastcgi will be used instead of HTTP
      --web.telemetry-path="/metrics"  
                               Path under which to expose metrics. Cannot be /

Нужно определиться как запускать эксопртер:
— в контейнере;
— локально запустить как процесс.

Для запуска в докер контейнере рекомендую пример от Флант.
При запуске контейнера пробрасываем порты с 8080 на нужный:

docker run -d -p 9000:8080 --name php-fpm-exporter php-fpm-exporter

Запуск как процесс более хлопотный.
Можно скачать уже готовый экспортер с репозитория bakins. Мы же пройдем весь путь сначала.

Первым делом клонируем репозиторий и собираем экспортер:
Для сборки потребуется пакет golang

# git clone https://github.com/bakins/php-fpm-exporter.git
# cd php-fpm-exporter
# ./script/build
...
+ NAME=php-fpm-exporter
+ ARCH=amd64
+ export GOFLAGS=-mod=vendor
+ GOFLAGS=-mod=vendor
+ export GO111MODULE=on
+ GO111MODULE=on
+ for OS in darwin linux
+ FILE=php-fpm-exporter.darwin.amd64
+ CGO_ENABLED=0
+ GOOS=darwin
+ GOARCH=amd64
+ go build -o php-fpm-exporter.darwin.amd64 ./cmd/php-fpm-exporter
++ openssl sha256 php-fpm-exporter.darwin.amd64
++ awk '{print $2}'
+ SHA=b9b5203821aa158221002abed322572a9d0a95df58d26cb35d74b7dc60d6d907
+ echo 'b9b5203821aa158221002abed322572a9d0a95df58d26cb35d74b7dc60d6d907 php-fpm-exporter.darwin.amd64'
+ for OS in darwin linux
+ FILE=php-fpm-exporter.linux.amd64
+ CGO_ENABLED=0
+ GOOS=linux
+ GOARCH=amd64
+ go build -o php-fpm-exporter.linux.amd64 ./cmd/php-fpm-exporter
++ openssl sha256 php-fpm-exporter.linux.amd64
++ awk '{print $2}'
+ SHA=e53c1392bb5ef2297b1b8487a75fa87c9c1f06c9f76d1da7b35523d94922a6d8
+ echo 'e53c1392bb5ef2297b1b8487a75fa87c9c1f06c9f76d1da7b35523d94922a6d8 php-fpm-exporter.linux.amd64'
...

Теперь запустим экспортер

# chmod +x php-fpm-exporter.linux.amd64
# cp php-fpm-exporter.linux.amd64 /usr/share/php-fpm-exporter
# php-fpm-exporter

Если во втором пункте при настройки страницы со статусом вы указали нестандартный порт(порт по умолчанию 9000), то при запуске экспортера задаем ключ —endpoint. Если вы хотите отрыть доступ к станице с метриками по нестандартному порту запускаем экспортер с ключем —addr:

# php-fpm-exporter --addr=192.168.122.10:10202 --endpoint http://localhost:10101/status > /dev/null &

После всех действий проверяем доступность метрик, заходим на страницу http://192.168.122.10:10202/metrics:

Вывод curl страницы с метриками:

# curl 192.168.122.10:10202/metrics
...
# HELP phpfpm_accepted_connections_total Total number of accepted connections
# TYPE phpfpm_accepted_connections_total counter
phpfpm_accepted_connections_total 12
# HELP phpfpm_active_max_processes Maximum active process count
# TYPE phpfpm_active_max_processes counter
phpfpm_active_max_processes 1
# HELP phpfpm_listen_queue_connections Number of connections that have been initiated but not yet accepted
# TYPE phpfpm_listen_queue_connections gauge
phpfpm_listen_queue_connections 0
# HELP phpfpm_listen_queue_length_connections The length of the socket queue, dictating maximum number of pending connections
# TYPE phpfpm_listen_queue_length_connections gauge
phpfpm_listen_queue_length_connections 0
# HELP phpfpm_listen_queue_max_connections Max number of connections the listen queue has reached since FPM start
# TYPE phpfpm_listen_queue_max_connections counter
phpfpm_listen_queue_max_connections 0
# HELP phpfpm_max_children_reached_total Number of times the process limit has been reached
# TYPE phpfpm_max_children_reached_total counter
phpfpm_max_children_reached_total 0
# HELP phpfpm_processes_total process count
# TYPE phpfpm_processes_total gauge
phpfpm_processes_total{state="active"} 1
phpfpm_processes_total{state="idle"} 1
# HELP phpfpm_scrape_failures_total Number of errors while scraping php_fpm
# TYPE phpfpm_scrape_failures_total counter
phpfpm_scrape_failures_total 0
# HELP phpfpm_slow_requests_total Number of requests that exceed request_slowlog_timeout
# TYPE phpfpm_slow_requests_total counter
phpfpm_slow_requests_total 0
# HELP phpfpm_up able to contact php-fpm
# TYPE phpfpm_up gauge
phpfpm_up 1

Возможные ошибки.

Если страница метрик отдает следующее содержание:

#curl 192.168.122.22:10202/metrics 
...
# HELP phpfpm_scrape_failures_total Number of errors while scraping php_fpm
# TYPE phpfpm_scrape_failures_total counter
phpfpm_scrape_failures_total 2
# HELP phpfpm_up able to contact php-fpm
# TYPE phpfpm_up gauge
phpfpm_up 0

Из вывода видно, что мы получили phpfpm_up 0, это значит, что экспортер не может получить статистику php-fpm со страницы, которую мы настраивали в Nginx.

Проверяем работу php-fpm, открывается ли localhost:9000/status. Если страница /status не на 9000, смотрим как запустить экспортер ссылкой на нужный адрес.

Полезные ссылки:
flant/php-fpm-exporter
bakins/php-fpm-exporter
Список рекомендуемых экспортеров

0

сurl. Кинуть запрос на определенный адрес.

Иногда возникают ситуации, когда нам надо кинуть запрос на адрес, но с условием, чтобы имя резолвилось на определенный адрес.
Это можно сделать с помощью утилиты curl с ключом —resolve:

~ curl --help
...
--resolve <host:port:address[,address]...> Resolve the host+port to this address
...

 ~ ping google.com
PING google.com (74.125.205.101) 56(84) bytes of data.

 ~ ping google.com
PING google.com (74.125.205.102) 56(84) bytes of data.

~ curl --resolve google.com:443:74.125.205.102 -k  'https://google.com/' 

<HTML><HEAD><meta http-equiv="content-type" content="text/html;charset=utf-8">
<TITLE>301 Moved</TITLE></HEAD><BODY>
<H1>301 Moved</H1>
The document has moved
<A HREF="https://www.google.com/">here</A>.
</BODY></HTML>

Используя ключ —resolve , curl может сделать запросы, используя заданный адрес и предотвращая перезапись нормально разрешенных адресов. Это некоторая альтернатива /etc/hosts в командной строке. Порт должен позволять работать с необходимым протоколом.

Допустим у Вас есть сервис https://myAPP.srv.local/ за балансировщиком и резолвится по нескольким адресам:
192.168.1.20
192.168.1.21

У сервиса есть healthcheck, который отдает информацию о состоянии. Однако же при попадании на https://health.myAPP.srv.local/ иногда мы получаем ошибку, например, «ERR_SSL_UNRECOGNIZED_NAME_ALERT».

Отлавливаем curl’ом эту ошибку:

 ~ curl -k -X GET 'https://health.myAPP.srv.local/' 
...
OK
...

 ~ curl -k -X GET 'https://health.myAPP.srv.local/'
curl: (35) error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name

Мы знаем на какие адреса резолвится имя, проверяем конкретно по адресам:

 ~ curl --resolve health.myAPP.srv.local:443:192.168.1.20 -k -X GET 'https://health.myAPP.srv.local/' 
...
OK
...

 ~ curl --resolve health.myAPP.srv.local:443:192.168.1.21 -k -X GET 'https://health.myAPP.srv.local/'
curl: (35) error:14094458:SSL routines:ssl3_read_bytes:tlsv1 unrecognized name

Таким образом мы выявили на каком именно узле у нас проблема, правим сертификат на хосте 192.168.1.21 и можно считать траблшутинг успешным.

0

Kubernetes. Taints and Tolerations. Запуск подов на мастере.

При первых опытах с Kubernetes возникает проблема, когда Вы разворачиваете одну или несколько нод-мастеров в кластере, служебные поды запускаются и работают, но при попытки развернуть pod с каким-либо сервисом, он зависает в статусе Pending и мы получаем ошибку: «0/1 nodes are available: 1 node(s) had taints that the pod didn’t tolerate.»

И так, что мы имеем?

Есть кластер, с одной нодой:

# kubectl get nodes
NAME           STATUS   ROLES    AGE   VERSION
k8s-master-1   Ready    master   16h   v1.16.3

Служебные поды работают:

# kubectl get po -n kube-system
NAME                                   READY   STATUS    RESTARTS   AGE
coredns-5644d7b6d9-bqsfm               1/1     Running   0          16h
coredns-5644d7b6d9-h5rr5               1/1     Running   0          16h
etcd-k8s-master-1                      1/1     Running   0          16h
kube-apiserver-k8s-master-1            1/1     Running   0          16h
kube-controller-manager-k8s-master-1   1/1     Running   0          16h
kube-flannel-ds-amd64-nmszk            1/1     Running   0          16h
kube-proxy-sw84w                       1/1     Running   0          16h
kube-scheduler-k8s-master-1            1/1     Running   0          16h

Однако же поды которые вы создаете сами остаются в статусе Pending:

# kubectl get po
NAME          READY   STATUS    RESTARTS   AGE
kubia-b2cgm   0/1     Pending   0          15h

Смотрим состояние пода:

# kubectl describe po kubia-b2cgm 
...
Type     Reason            Age                From               Message
----     ------            ----               ----               -------
Warning  FailedScheduling  51s (x15 over 3m)  default-scheduler  0/1 nodes are available: 1 node(s) had taints that the pod didn't tolerate.

В чем же дело?

Ответ на этот вопрос изложен в официальной документации в статье «Taints and Tolerations».

Вкратце, в Kubernetes реализована политика разграничение ролей узлов кластера, ограничивающая установку тех или иных подов на «зараженные» поды.

Как быть, что делать?

Первый делом проверяем свойство ноды на предмет наличия установленной политики:

# kubectl describe nodes k8s-master-1 |grep Taints
...
Taints:             node-role.kubernetes.io/master:NoSchedule
...

Согласно вышеупомянутой документации, мы можем сделать вывод, что нода k8s-master-1 в статусе master:NoSchedule

Это означает, что ни один под не сможет планировать развертывание на узел k8s-master-1, если у него нет соответствующего допуска.

И тут у нас есть два варианта развития событий:
— Предоставить соответствующий допуск самому поду.
— Отключить эту политику для данного узла.

Вариант 1. Выдать допуск поду.

В манифесте указываем допуск:

tolerations:
- key: node-role.kubernetes.io/master
  operator: "Exists"
  effect: NoSchedule 

Пример создание Replication Controller для подов с допуском:

apiVersion: v1
kind: ReplicationController
metadata:
  name: kubia
spec:
  replicas: 3
  selector:
    app: kubia
  template:
    metadata:
      labels:
        app: kubia
    spec:
      containers:
      - name: kubia
        image: luksa/kubia
        ports:
        - containerPort: 8080
      tolerations:
      - key: node-role.kubernetes.io/master
        operator: "Exists"
        effect: NoSchedule 

Этот способ подходи только в том случае, если Вы выносите самописные поды. При установки подов с помощью Helm нужно указывать tolerations отдельным ключом.

Вариант 2. Отключить политику в Taints.

Отключаем политику на ноде k8s-master-1:

# kubectl taint nodes k8s-master-1 node-role.kubernetes.io/master- 

Проверяем состояние ноды:

# kubectl describe nodes k8s-master-1 |grep Taints
...
Taints:             <none>
...

Данный способ более универсальный и не привязывает нас к модификациям манифестов.

Первоисточник:
— статья «Taints and Tolerations».
— Книга «Kubernetes в действии» автор Лукша Марко.

0

Gitlab. Mirroring repositories.

При необходимости переноса проекта из стороннего репозиторияв в Ваш локальный, Gitlab предлагает инструмент Mirroring repositories.

Зеркалирование в Gitlab поддерживает два режима:
Push: зеркалирование проекта из локального Gilbab в удаленный репозиторий.
Pull: зеркалирование проекта из удаленного репозитория в локальный Gilbab.

Рассмотрим функциональность инструмента Mirroring repositories на примере зеркалирования проекта из удаленного Atlassian Bitbucket в локальный Gitlab.

Создаем новый проект, далее переходим в настройки этого проекта и добавляем уделенный репозиторий:

Projects > Setting > Repository > Mirroring repositories

Git repository URL: адрес удаленного репозитория
Mirror direction: в нашем случае это Pull
Authentication method: в нашем случае это аутентификация по паролю, но можно и по SSH-ключу.
Password: сам пароль.
Mirror user: пользователь от которого осуществляется настройка.
Так же стоит активировать функцию Overwrite diverged branches, она позволит перезаписывать проект, если версии веток разошлись между репозиториями.

После настройки первая синхронизация займет некоторое время.

Готово, теперь репозиторий будет обновляться каждые ~30 минут. Так же можно форсировать синхронизацию кнопкой «Update now»

Как это работает согласно официальной документации:

Как только функция Mirroring repositories включена для хранилища, хранилище добавляется в очередь.

Раз в минуту задание Sidekiq cron планирует обновления зеркал репозитория на основе:
— Доступности Sidekiq.
— Количество зеркал репозитория, уже находящихся в очереди, которые должны быть обновлены. Срок выполнения зависит от того, когда зеркало хранилища последний раз обновлялось, и сколько раз оно повторялось.

Зеркала репозитория обновляются, когда Sidekiq становится доступным для их обработки. Если процесс обновления зеркала репозитория:
— Успешно, обновление будет снова поставлено в очередь, по крайней мере, через 30 минут.
— Сбой (например, ветвь отклонена от восходящего направления), попытка будет предпринята позже. Зеркала могут выходить из строя до 14 раз, прежде чем они не будут поставлены в очередь для обновления снова (об этом моменте сы поговорим чуть позже).

Возможные ошибки.

При синхронизации проектов я столкнулся с проблемой наличия недопустимых имен веток в удаленном репозитории.

Репозитории синхронизировались, данные внутри обновлялись, но я получал ошибку «Branch name is invalid».

Как быть, что делать?
Смотрим ветки в нашем локальном репозитории, смотрим ветки в удаленном репозитории в Bitbucket. Оказалось у нас не хватает одной ветки с именем «-readmemd-1572439278772», которая не прошла проверку на доступность имени и не синхронизировалась в наш Gilbab

 ~ git branch -a
...
remotes/origin/------------/-readmemd-1572439278772
...

В нашем случае мы попросили удалить эту ветку, т.к. она была не актуальна.

Первоисточник: https://docs.gitlab.com/ee/user/project/repository/repository_mirroring.html
Тикет на ошибку «Branch name is invalid: https://gitlab.com/gitlab-org/gitlab/issues/35804

0

Интеграция GitLab и Jenkins. Развертывание по событию.

Настройка непрерывной интеграции между GitLab и Jenkins. Необходимо запускать job в Jenkins всякий раз, когда в ветке master в GitLab происходят изменения.

Шаг 1. Настройка Webhooks в GitLab.

GitLab > Projects > Settings > Integrations

В нашем случае, настраиваем Webhooks на push и merge события в ветке master.

URL: указываем проект в Jenkins
Push events: указываем необходимую ветки, если пусто, то действия распространяются на все ветки.
Merge request events: активно
SSL verification: активно

Шаг 2. Настройка Build Triggers в проекте в Jenkins

Предварительно необходимо установить плагин GitLab Plugin:
Jenkins > Manage Jenkins > Plugin Manager > Available > install GitLab Plugin

Jenkins > Ваш Проект > Configure > Build Triggers

Необходимо активировать пункт Build when a change is pushed to GitLab. GitLab webhook URL: https://localhost/jenkins/project/TestJenkinsProjekt

Шаг 3. Тестирование интеграции.

Выбираем необходимый Trigger

После имитации триггера стартует job в Jenkins с припиской: Started by GitLab push by Username

Первоисточник: https://docs.gitlab.com/ee/user/project/integrations/webhooks.html

1+

Исключения для двухфакторной аутентификации OpenVpn

В статье Двухфакторная аутентификация OpenVpn клиентов (OpenVpn + Google Authenticator) на CentOs 7/8 подробно описано как настроить двухфакторную аутентификацию для клиентов open-vpn.

Ниже описано как сделать исключения.

Допустим, что не требуется использовать двухфакторную аутентификацию для клиента с именем client1 и для любого клиента, который подключается с публичного ip 111.111.111.111


1. Для удобства создадим группу пользователей, для которых будет отключена двухфакторная аутентификация и добавим пользователя client1 в неё:

groupadd no2fa
usermod -g no2fa client1

2. Создадим файл, в котором опишем правила исключения:

nano /etc/security/no2fa.conf
# Содержимое файла:
# No 2fa:
+ : (no2fa) : ALL # Входящие в группу no2fa с любым IP
+ : ALL : 111.111.111.111 # Любые пользователи с IP 111.111.111.111
# All other hosts need two step verification
- : ALL : ALL

3. В файл /etc/pam.d/openvpn добавляем первой строкой:

auth [success=done default=ignore] pam_access.so accessfile=/etc/security/no2fa.conf

Перезапускаем openvpn-server и правила исключений будут работать.

Использованы материалы — https://serverfault.com/questions/799657/ssh-google-authenticator-ignore-whitelist-ips

0

Двухфакторная аутентификация OpenVpn клиентов (OpenVpn + Google Authenticator) на CentOs 7/8

1. Устанавливаем необходимые компоненты для работы и настройки OpenVpn:

yum install epel-release
yum -y --enablerepo=epel install openvpn easy-rsa nano
cd /etc/openvpn/
mkdir ccd
mkdir clients

Для более удобной работы с easy-rsa копируем все его файлы в /etc/openvpn/easy-rsa/

cp -a /usr/share/easy-rsa/3.0.3 /etc/openvpn/easy-rsa

Важно! Если у вас CentOs 8 то в репозитории нет easy-rsa, устанавливается так:

yum -y install unzip zip wget
cd ~
wget https://github.com/OpenVPN/easy-rsa/archive/master.zip
unzip master.zip
cp -a easy-rsa-master/easyrsa3/ /etc/openvpn/easy-rsa/

Читать далее

0

Cisco 5585 Multicontext — OID для оценки утилизации CPU

SNMP OID для 5585 работающей в мультиконтекстном режиме с версией ASA 9.1

Для получения утилизации CPU за 1 минуту отдельным контекстом (собирается с каждого контекста):

1.3.6.1.4.1.9.9.109.1.1.1.1.4

Для получения утилизации CPU всей ASA за 1 минуту (собирается с админского контекста):

1.3.6.1.4.1.9.9.109.1.1.1.1.4.3

За 5 секунд и 5 минут OIDы находятся рядом с этими, при необходимости выбирается snmp-walkом

0

PostgreSQL компиляция библиотеки libzbxpgsql и пересборка RPM-пакета

Библиотека libzbxpgsql используется для мониторинга PostgreSQL в Zabbix. Последний релиз библиотеки был в 2017 году и свежий пока не вышел, в то же время, последняя версия libzbxpgsql не работает с zabbix agent версий 4+. В официальном репозитории https://github.com/cavaliercoder/libzbxpgsql есть фикс этой проблемы https://github.com/cavaliercoder/libzbxpgsql/pull/141, фикс проблемы с DEBUG логированием zabbix агентом https://github.com/cavaliercoder/libzbxpgsql/pull/142.
Для того чтобы можно было использовать исправления, необходима виртуалка с CentOS 7+ и возможностью поставить на нее пакеты для компиляции исходников (make, gcc и т.д.).
Так же ставим пакет rpm-build, он потребуется для пересборки rpm-пакета:

yum install rpm-build

Устанавливаем последнюю доступную версию пакета libzbxpgsql (из репозитория или можно перекинуть на машину свой пакет и поставить локально):

yum install libzbxpgsql

Читать далее

0

Ansible включение тайминга выполнения задач

В Ansible можно включить тайминг времени запуска и выполнения тасок, в результате ход выполнения плейбуков приобретет подобный вид:

TASK [restore_postgres : Select target DB size (MB)]
********************************************************************************************************************
Thursday 26 September 2019 10:40:20 +0300 (0:00:00.061) 0:12:12.391 ****
ok: [bufferdb]
TASK [restore_postgres : Set DBSize to variable]
************************************************************************************************************************
Thursday 26 September 2019 10:40:21 +0300 (0:00:00.558) 0:12:12.949 ****
ok: [bufferdb]
TASK [restore_postgres : debug]
*****************************************************************************************************************************************
Thursday 26 September 2019 10:40:21 +0300 (0:00:00.060) 0:12:13.010 ****
ok: [bufferdb] => {
"msg": "Database size in MB is: 33189.0"
}

Читать далее

0