Ускоряем Graphite-web

Я уже писал про graphite, я все еще считаю, что для time series нет ничего лучше. А так как начиная с версии 1.1 graphite поддерживает теги, использовать его стало еще удобнее.
Однако, в go-реализации graphite еще не реализована поддержка функций извлечения данных по тегам, например таких, как seriesByTag или groupByTags. Поэтому, чтобы использовать 100% возможностей graphite, на текущий момент необходимо использовать graphite-web. Далее небольшая заметка о том, как повысить производительность в 2-3 раза без каких-либо значимых усилий.

Установка PyPy

Как вы уже могли догадаться, ускорение будет обеспечиваться за счет PyPy. PyPy — это альтернативная реализация языка python, которая включает в себя JIT-компилятор для трансляции кода в машинные инструкции во время исполнения программы. Как раз за счет JIT-компилятора обычно и обеспечивается прирост производительности.
На момент написания даннной заметки актуальная версия PyPy 6.0 (соответсвует python 3.5.3). Установка PyPy на актуальные версии linux не составляет труда и сводится к скачиванию архива с прекомпилированными бинарниками:

$ wget https://bitbucket.org/pypy/pypy/downloads/pypy3-v6.0.0-linux64.tar.bz2
$ tar -C /opt -xf pypy3-v6.0.0-linux64.tar.bz2
$ export PATH=$PATH:/opt/pypy3-v6.0.0-linux64/bin
$ pypy3 -m ensurepip
$ pip3.5 install --upgrade pip

Установка gunicorn и graphite-web

Для запуска graphite-web используем gunicorn+nginx. Будем считать, что nginx уже установлен и настроен, установим оставшееся:

$ pip3.5 install gunicorn gevent
$ pip3.5 install https://github.com/graphite-project/graphite-web/tarball/1.1.3 \
 --install-option="--prefix=/opt/graphite" --install-option="--install-lib=/opt/graphite/webapp"

Если у вас все прошло без ошибок, наслаждаемся жизнью и следуем инструкции далее. Если же во время установки scandir произошла ошибка, фикс можно взять из PR #102.

После установки graphite-web его необходим сконфигурировать. Например, в моем случае, при интеграции с graphite-clickhouse конфигурация выглядит следующим образом:

ALLOWED_HOSTS = [ '*' ]
TIME_ZONE = 'Europe/Moscow'

LOG_ROTATION = True
LOG_ROTATION_COUNT = 1

FIND_TIMEOUT = 10.0
FETCH_TIMEOUT = 60.0

STORAGE_FINDERS = (
    'graphite.finders.remote.RemoteFinder',
)

CLUSTER_SERVERS = ["127.0.0.1:9090"]
USE_WORKER_POOL = True
POOL_MAX_WORKERS = 4
REMOTE_RETRY_DELAY = 0.0
REMOTE_EXCLUDE_LOCAL = False
REMOTE_BUFFER_SIZE = 1024 * 1024 * 16

Далее потребуется создать systemd unit:

$ cat > /etc/systemd/system/graphite-web.service <<EOF
[Unit]
Description=graphite-web daemon

[Service]
Environment=PYTHONPATH=/opt/graphite/webapp/

Type=simple
WorkingDirectory=/opt/graphite/conf
ExecStart=/opt/pypy3-v6.0.0-linux64/bin/gunicorn -w 4 -k gevent \
 --max-requests 10000 --bind unix:/run/.graphite-web.sock \
 --user graphite --group nginx --chdir /opt/graphite/conf graphite.wsgi:application
Restart=always

[Install]
WantedBy=multi-user.target
EOF

Вот и все. Запускаем сервис

$ systemctl enable graphite-web.service
$ systemctl start graphite-web.service