Ad Widget

Collapse

Спам nodata от php-fpm статус страниц

Collapse
X
 
  • Time
  • Show
Clear All
new posts
  • flustex
    Junior Member
    • Jan 2017
    • 4

    #1

    Спам nodata от php-fpm статус страниц

    Добрый день! Ситуация следующая.
    Zabbix 3.2.3. Проблема: nodata замучила уже
    Дебаг:
    Code:
     29381:20170111:130209.640 Requested [php-fpm["http_code",http://uteka.ua/php-fpm-status]]
     29381:20170111:130209.640 In zbx_popen() command:'/var/lib/zabbix/php-fpm/scripts/php-fpm.sh "http_code" "http://uteka.ua/php-fpm-status"'
     29381:20170111:130212.259 EXECUTE_STR() command:'/var/lib/zabbix/php-fpm/scripts/php-fpm.sh "http_code" "http://uteka.ua/php-fpm-status"' len:114 cmd_result:'/var/lib/zabbix/php-'
     29381:20170111:130212.259 Sending back [/var/lib/zabbix/php-fpm/scripts/php-fpm.sh: line 36: 10#200
    301: syntax error in expression (error token is "301")]
    И есть триггер nodata
    HTML Code:
    {uteka.ua:php-fpm["http_code",{$UTEKA}{$UTEKA_PAGE}].nodata(80)}=1
    Code:
    #!/bin/bash
    ### OPTIONS VERIFICATION
    if [[ -z "$1" ]]; then
            exit 1
    fi
    ##### PARAMETERS #####
    METRIC="$1"
    STATSURL="$2"
    #
    if [[ -z "$STATSURL" ]]; then
            STATSURL="http://localhost/php-fpm-status"
    fi
    
    umask 066
    
    CURL=`which curl`
    TTLCACHE="55"
    FILECACHE="/tmp/zabbix.php-fpm.`echo $STATSURL | md5sum | cut -d" " -f1`.cache"
    TIMENOW=`date '+%s'`
    
    ##### RUN #####
    if [ -s "$FILECACHE" ]; then
            TIMECACHE=`stat -c"%Z" "$FILECACHE"`
    else
            TIMECACHE=0
    fi
    
    if [ "$(($TIMENOW - $TIMECACHE))" -gt "$TTLCACHE" ]; then
        RESP=`$CURL -L --no-keepalive --insecure -s "$STATSURL" -m 2 -w "%{http_code}" -o  FILECACHE --connect-timeout 2`
        echo "http_code: $RESP" >> $FILECACHE
    fi
    
    if [ "$METRIC" ]; then
            param=`cat $FILECACHE | grep -P "^$METRIC:" | tr -s " " | sed 's/[^0-9]*//g' | uniq`
            [[ ! -z $param ]] && echo $((10#$param)) || echo 0
    fi
    exit 0
    При этом проблема не есть постоянная, 98% ответов успешны.
    Как данную искать откуда ноги растут дальше?
  • Kos
    Senior Member
    Zabbix Certified SpecialistZabbix Certified Professional
    • Aug 2015
    • 3404

    #2
    Я не очень понимаю, что Вы хотите сделать, что при этом делаете и что при этом получается.

    У Вас скрипт, содержимое которого приведено, запускается на сервере как external script? Или на агенте через UserParameter? Чей кусок лога Вы привели?

    В чём именно проблема? Скрипт запускается регурлярно (кстати, с каким интервалом?) и при этом в 98% случаев данные отдаёт успешно, а в 2% не возвращает ничего и срабатывает nodata()?

    А что именно этот скрипт должен писать в файл, имя которого хранится в переменной $FILECACHE и кто этот файл должен очищать? Пока что, как я вижу, в него дописывается после каждого запуска curl-а полученный код ответа (переменная $RESP), при этом файл только растёт (идёт добавление в конец через ">>", файл нигде не очищается). Содержимое самой странички, которая скачивается curl-ом, идёт в другой файл (FILECACHE в текущей директории); возможно, это из-за опечатки (пропущенного знака доллара).

    Кроме того, от меня ускользает смысл последней пары строк, где присваивается и затем проверяется значение переменной $param. Из файла вырезается параграф, где есть строка "http_code", затем оттуда удаляются пробелы, после чего удаляется вообще всё, кроме цифр, после чего оставшиеся строки ещё прогоняются через uniq... Наверняка это можно упростить, если понимать, чего хочется добиться. Но реальный результат - это то, что переменная получает значение "200<перевод_строки>301", из-за чего становится некорректным выражение "$((10#$param))". Это можно сэмулировать такими командами в bash-е:
    Code:
    ~$ A='200
    > 301'
    ~$ [[ ! -z $A ]] && echo $((10#$A)) || echo 0
    bash: 10#200
    301: syntax error in expression (error token is "301")
    ~$
    Собственно, эти строки Вы и видите в логе (как результат работы скрипта).

    Comment

    • flustex
      Junior Member
      • Jan 2017
      • 4

      #3
      Понимаю, что не best practice

      Спасибо за ответ. Итемы выполняются раз в минуту, дергают почти все поля с этой таблицы. Nodata настроена отлавливать потерю соединения по http_code ответу. Для того чтобы не нагружать пул лишними служебными запросами от забикса я втулил в скрипт алгоритм кеширования. его я списал почти 1 в 1 с темплейта для мониторинга апача. Логика задумывалась такая: если разница меньше чем TTLCACHE нужно просто грепнуть файл, иначе - взять курлом новую страницу, положить заместо старого и достать нужные итемы. флаг curl -o при попадании в if перезаписывает "$FiLECACHE", в stdout видим только 200, что есть http_code переменной "$RESP"
      Работает через забикс агент и UserParameter.
      Содержимое самой странички, которая скачивается curl-ом, идёт в другой файл (FILECACHE в текущей директории); возможно, это из-за опечатки (пропущенного знака доллара)
      Верно, там ошибка копипаст при создании темы.

      Кроме того, от меня ускользает смысл последней пары строк, где присваивается и затем проверяется значение переменной $param
      param=`cat $FILECACHE | grep -P "^$METRIC:" | tr -s " " | sed 's/[^0-9]*//g' | uniq`
      [[ ! -z $param ]] && echo $((10#$param)) || echo 0
      Пришлось закостылить таким образом, потому что забикс по непонятным причинам до этого также возвращал:
      Code:
      200 200
      Code:
      200
      200
      и срабатывала отдельная проверка на bad response
      Code:
      {uteka.ua:php-fpm["http_code",{$UTEKA_MAIL}{$UTEKA_MAIL_PAGE}].last()}<>200
      Вот "$FILECACHE"
      Code:
      pool:                 air_admin
      process manager:      dynamic
      start time:           04/Jan/2017:13:42:41 +0200
      start since:          687979
      accepted conn:        24323
      listen queue:         0
      max listen queue:     0
      listen queue len:     0
      idle processes:       2
      active processes:     1
      total processes:      3
      max active processes: 2
      max children reached: 0
      slow requests:        0
      http_code: 200

      Comment

      • Kos
        Senior Member
        Zabbix Certified SpecialistZabbix Certified Professional
        • Aug 2015
        • 3404

        #4
        Originally posted by flustex
        Верно, там ошибка копипаст при создании темы.
        OK, тогда сначала должен (пере)создаваться файл, а следующей командой в него добавляться строчка "http_code: " с кодом.

        Originally posted by flustex
        Пришлось закостылить таким образом, потому что забикс по непонятным причинам до этого также возвращал:
        Code:
        200 200
        Code:
        200
        200
        А вот это уже непонятно. Если в файле строчка "http_code: " только одна, то откуда тогда берётся второе значение? Может ли быть такое, что curl получает ответ с кодом 301 (перенаправление), делает повторый запрос (по тому адресу, куда перенаправили) и записывает в файл результаты обоих запросов?
        В любом случае, строка "http_code: " должна же быть только одна (она дописывается самим скриптом уже после того как curl отработал).

        По-любому, я бы "навороченную" строку упростил бы.
        Нагляднее всего, наверное, вариант с awk:
        Code:
        param=`awk '/^http_code:/ {print $2}' $FILECACHE`
        Но эффективнее вместо "тяжёлого" awk вызывать более "лёгкий" sed:
        Code:
        param=`sed -n '/^http_code:/s/[^0-9]*//p' $FILECACHE`
        А в последней строчке зачем такая конструкция с двойными скобками (как квадратными, так и круглыми)? Вот это не будет ли давать такой же результат, только нагляднее?
        Code:
        [ "$param" ] && echo $param || echo 0
        А по теме есть ещё одна мысль. Запуск проверок через агента (в том числе скриптов, запускаемых через UserParameter) должно завершаться в отведённый тайм-аут, который по умолчанию довольно короткий (3-4 секунды, в зависимости от версии). Если Ваш скрипт (в первую очередь, вызов curl) иногда в этот тайм-аут не укладывается, то будут пропуски (и, как следствие, срабатывание триггера по условию "nodata()"). Может, это как раз Ваш случай? Это довольно легко выяснить, если дополнить Ваш bash-скрипт парой команд, которые будут выводить время работы curl-а в отдельный лог примерно таким образом (результат - в наносекундах):
        Code:
        T1=$(date '+%s%N')
        ...прежний вызов curl-а...
        echo $(date '+%Y-%m-%d %T') $(( $(date '+%s%N') - $T1 )) >>/tmp/my_script.log
        Last edited by Kos; 12-01-2017, 17:26.

        Comment

        • flustex
          Junior Member
          • Jan 2017
          • 4

          #5
          К таким же идеям пришел я и сам.
          1. bash скрипт отрабатывает не корректно потому что я не умею писать на bash
          2. отдельно таймауты
          3. отдельно алгоритм кеширования
          4. процесс php-fpm упирается в что-то
          5. процесс агента упирается в что-то

          В связи с чем переписал скрипт на пайтоне, добавил дисковери скрипт и написал новый тепмлейт. В баш скрипте карлу таймауты были заданы (2 сек). Как мне показалось, алгоритм начал отрабатывать стабильнее, но, увы, нодата теперь приходит синхронно по нескольким пулам. Это в свою очередь склоняет меня уже поискать проблему в пхп. А еще нодата триггеру поднял значение с 80 секунд до 90.
          Для общественности выложу готовые решения для мониторинга отдельно нескольких пулов в атач. Какие еще таймауты могут быть причиной? Может выложить это дело на zabbix templates wiki https://www.zabbix.org/wiki/Zabbix_Templates ?

          Собственно новый код вот:
          Code:
          #!/usr/bin/python
          import json
          
          php_fpm_pools = {
          'example'  : 'http://example/php-fpm-status' ,
          'mail'      : 'http://example/mail-php-fpm-status' ,
          'upload'    : 'http://upload.example/php-fpm-status' ,
          'admin'     : 'http://admin.example/php-fpm-status' ,
          }
          
          if __name__ == "__main__" :
              poolname = [{ "{#POOLNAME}" : pool, "{#POOLADDR}" : php_fpm_pools[pool] } for pool in php_fpm_pools]
              print (json.dumps({"data" : poolname}))
          Code:
          #!/usr/bin/env python
          import requests
          import sys
          import os.path
          import time
          import json
          import hashlib
          
          cache_delta     = 55
          cache_file      = '/var/lib/zabbix/php-fpm/scripts/my.txt_' + hashlib.sha224(sys.argv[2]).hexdigest()
          
          def write_cache():
                  data = get_status()
                  with open(cache_file, 'w') as f:
                          json.dump((data), f)
                  
          def read_cache(key):
                  with open(cache_file, 'r') as f:
                          try:
                                  data = json.load(f)
                          except ValueError:
                                  data = {}
                          return data[key]
                      
          def touch(fname, times=None):
              with open(fname, 'a'):
                  os.utime(fname, times)
                      
          def check_cache(key):                                                                                                                                                   
                  if not os.path.isfile(cache_file):                                                                                                                              
                          touch(cache_file)                                                                                                                                       
                  try:                                                                                                                                                            
                          cache_time = os.path.getctime(cache_file)
                          now_time = time.time()
                          if now_time - cache_time > cache_delta or os.path.getsize(cache_file) == 0:
                                  write_cache()
                  except OSError as err:
                                  print "Error: {0}".format(err)
                  finally:
                                  return read_cache(key)
          
          params = {}
          
          def get_status():
              url = sys.argv[2] + '?json'
              r = requests.post(url=url,timeout=2.001)
              data = eval(r.text)
              data['http_code'] = r.status_code
              return data
          
          if __name__ == '__main__':
              if len(sys.argv) != 3:
                  print "Usage: %s <statuspage> <key>" % sys.argv[0]
              else:
                  if '-' in str(sys.argv[1]):
                      key = str(sys.argv[1]).replace('-', ' ')
                  else:
                      key = str(sys.argv[1])
                  print check_cache(key)
          Attached Files

          Comment

          • Kos
            Senior Member
            Zabbix Certified SpecialistZabbix Certified Professional
            • Aug 2015
            • 3404

            #6
            С Пайтоном не помогу, извините - не силён.

            С Башевским скриптом, вроде бы, всё было понятно, кроме одного: появления дублированных записей с кодом от curl-а.
            В баш скрипте карлу таймауты были заданы (2 сек).
            Точно. Так, может, это оно и есть?
            Может ли быть такое, что при завершении curl-а по тайм-ауту он не перезаписывает выходной файл? Т.е. содержимое файла $FILECACHE остаётся от предыдущего запуска, после чего командой
            Code:
            echo "http_code: $RESP" >> $FILECACHE
            к нему добавляется новая строка (уже вторая)?
            Кстати, а какой именно код в этом случае вернёт curl?

            Comment

            • flustex
              Junior Member
              • Jan 2017
              • 4

              #7
              Проблема решена

              Проблема, как выяснилось, в скорости чтения/записи винта. На сервере установлен зеркальный рейд и один с винтов начал деградировать. Хотя смарт показывает Ок. Разобрал рейд - сразу песня, полет ровный. Заказали у хетцнер замену винта, заменили, собрали рейд. Замена на новый винт - услуга платная. Поставили они, кароче, винт б.у., чутка лучше, но проблема осталась. Будем снова менять винт. Оба винта - Crucial_CT256MX100SSD1.
              В этом плане nodata триггер очень помог.
              Last edited by flustex; 25-01-2017, 15:51. Reason: В этом плане nodata триггер очень помог

              Comment

              Working...