2018年10月7日日曜日

Python の prometheus_client を使ってみた

概要

Python の prometheus_client を使ってみました
Python で開発した Web アプリに組み込むことで Prometheus 用の metrics が取得できるようになります
既存の flask アプリを想定した組み込み方法も紹介します

環境

  • macOS 10.14
  • Python 3.7.0
  • prometheus-client 0.3.1

ライブラリインストール

  • pipenv install prometheus_client

Getting Started

  • vim app.py
from prometheus_client import start_http_server, Summary
import random
import time

REQUEST_TIME = Summary('request_processing_seconds', 'Time spent processing request')

@REQUEST_TIME.time()
def process_request(t):
    time.sleep(t)

if __name__ == '__main__':
    start_http_server(8000)
    while True:
        process_request(random.random())

動作確認

  • pipenv run python3 app.py
  • curl localhost:8000

でメトリックが取得できます

# HELP python_info Python platform information
# TYPE python_info gauge
python_info{implementation="CPython",major="3",minor="7",patchlevel="0",version="3.7.0"} 1.0
# HELP request_processing_seconds Time spent processing request
# TYPE request_processing_seconds summary
request_processing_seconds_count 38.0
request_processing_seconds_sum 21.631802963

デフォルトだと python_info という名前のメトリックが実装されているようです
Summary タイプを使うと自動的にメトリック名の後ろに _count と _sum を追加してメトリックを生成してくれます

既存の flask アプリにメトリックを追加する

Counter タイプのメトリックを追加してみます
flask で作ったアプリにアクセスがあった場合にアクセス数をカウントします

  • vim app1.py
from flask import Flask
from werkzeug.wsgi import DispatcherMiddleware
from werkzeug.serving import run_simple
from prometheus_client import make_wsgi_app, Counter

c = Counter('my_counter', 'Description of access counter')

app = Flask(__name__)

@app.route('/')
def index():
    return 'index'

@app.before_request
def before_request():
    c.inc()

app_dispatch = DispatcherMiddleware(app, {
    '/metrics': make_wsgi_app()
})

if __name__ == '__main__':
    run_simple('localhost', 8000, app_dispatch, use_reloader=True, use_debugger=True, use_evalex=True)

まず Counter メトリックの定義は Counter('my_counter', 'Description of access counter') でします
これだけで追加することができます
今回の場合 flask 側のアプリにアクセスがあった場合にこのカウントをインクリメント (c.inc()) します
「flask 側」という表現を使っているのは既存の flask アプリを想定しており、それに対し prometheus_client 側のアプリを追加する感じになります
アプリを追加するには flask の DispatcherMiddleware を使います
これに /metrics には prometheus_client のアプリである make_wsgi_app を設定します
prometheus_client 側のアプリは WSGI で作成されているようです
こうすることで 2 つの異なるアプリを一つのアプリとして扱うことができます

結合したアプリは run_simple を使って起動します

動作確認

  • pipenv run python3 app.py
  • curl localhost:8000/metrics

でメトリックを取得できます
カウンタを増やしたい場合は localhost:8000 にアクセスしてからメトリックを取得するとメトリックが増えていることが確認できると思います

# HELP python_info Python platform information
# TYPE python_info gauge
python_info{implementation="CPython",major="3",minor="7",patchlevel="0",version="3.7.0"} 1.0                                   
# HELP my_counter Description of access counter
# TYPE my_counter counter
my_counter 2.0

最後に

Python の prometheus_client を使ってメトリックを取得できるようにしてみました
既存の flask アプリにも追加することができました

WSGI 側のアプリを直接変更することでメトリック側のページのカウンタを作ることもできるはずですがやり方は不明です

Ruby の prometheus-client ライブラリはデフォルトでレスポンスタイムなども返してくれますが Python の場合はそうではないようです

参考サイトに記載したのですが flask であれば専用のライブラリとして公開している方がいるので最低限のメトリックでいいのであればそっちを使ったほうが無難かなと思います
内部的には今回紹介した prometheus_client を使っているので、ソースを参考にして同じように自分のアプリに組み込むこともできます

参考サイト

2018年10月6日土曜日

prometheus-client を使って独自の exporter を作成する方法

概要

前回 prometheus-client を使ってデフォルトのメトリックを自分のアプリに組み込む方法を紹介しました
今回はカスタム Exporter を作成して独自のメトリック情報も取得できるようにしてみます
なお、組み込むアプリは Rails アプリではなく Sinatra をベースにしたアプリになります

環境

  • macOS 10.14
  • ruby 2.5.1p57
  • prometheus-client 0.8.0

カスタム Controller の作成

まず Controller を作成します
Controller では独自で追加するメトリックのメタデータの登録を行います

  • vim helper/custom_controller.rb
require 'prometheus/client'

module Prometheus
  module Controller
    prometheus = Prometheus::Client.registry
    gauge = Prometheus::Client::Gauge.new(:online_flag, 'Check the flag whether it is online')
    prometheus.register(gauge)
  end
end

とりあえずこんな感じで作ります
Gauge は 1 or 0 のような二値を返却するときに使うタイプになります
新しく Gauge のオブジェクトを作成し引数にメトリック名と説明を与えます
あとは prometheus.register することで Controller を登録します

なおこの状態で config.ru から参照すると /metrics にメトリック名だけ表示することができます

require './helper/custom_controller'

カスタム Exporter の作成

次に Exporter を作成します
Exporter は実際にメトリックに出力するデータを操作する役割になります

  • vim helper/custom_exporter.rb
require 'prometheus/middleware/exporter'

module Prometheus
  module Middleware
    class CustomExporter < Prometheus::Middleware::Exporter

      def respond_with(format)
        guage = @registry.metrics.first
        guage.set({
          name: :my_site
        }, online?)
        super
      end

      def online?
        0
      end

    end
  end
end

まず Prometheus::Middleware::Exporter を継承します
そして respond_with を実装します
ここで先程登録したメトリック項目を @registry で参照できます
@registry には登録したメトリック情報がキュー配列で格納されています
先程登録したメトリック (:online_flag) は先頭に格納されるので .first で取得することができます
ちなみに今回の場合であれば配列は 4 つあり残り 3 つはデフォルトで Exporter に実装されているメトリックが入っています

あとはメトリックにラベルとコールバック用のメソッドを set します
ラベルはハッシュで好きな内容が登録できます
コールバック用のメソッドは実際にメトリックとして出力する内容を取得するメソッドになります
今回は単純に 0 を返却していますが実際はここに例えばサイトがオンラインであるかどうかチェックするロジックを記載します

Tips

もしデフォルトのメトリック情報を削除したい場合は Prometheus::Middleware::Exporter を継承しないで独自メトリック情報の文字列を返却する Exporter を作成する必要があります

@registry.metrics の配列から Prometheus::Client を削除しようとしても readonly なフィールドなので削除することはできません

config.ru から呼び出す

あとは Controller と Exporter を config.ru から参照するだけです

  • vim config.ru
require './helper/custom_controller'
require './helper/custom_exporter'
require 'prometheus/middleware/collector'
# require 'prometheus/middleware/exporter'

use Rack::Deflater
use Prometheus::Middleware::Collector
# use Prometheus::Middleware::Exporter
use Prometheus::Middleware::CustomExporter

Prometheus の部分だけ抜粋しています
Prometheus::Middleware::Exporter は作成したカスタム Exporter が継承しているので不要になります

またあとで気づいたのですが、デフォルトのメトリック情報を削除したいのであれば prometheus/middleware/collector と Prometheus::Middleware::Collector を削除すれば出なくなります

最後に

Ruby の prometheus-client を使って独自のメトリックを作成する方法を紹介しました
Controller でメトリックの項目を登録し、Exporter で登録したメトリックに対して実際に値を取得するロジックを実装する感じになります

今回 Exporter は Prometheus::Middleware::Exporter を継承して実装しました
おそらくこの方法が一番簡単な方法だとは思います
が、実はこの方法にこだわる必要は全くありません
というのも Prometheus は結局 exporter と HTTP で話しができてその出力結果がフォーマットに沿った text/plain であれば良いのです
なので、自分で erb を作成してそれを出力してもいいですし単純にフォーマットに沿ったテキストファイルを読み込んでそれを Sinatra で出力する感じでも全然問題ありません

今回の方法を使うと、その辺りのフォーマット関係を気にせず実装できるという感じになります
あとは Prometheus から exporter にアクセスがあったときに値の集計を行うコールバックが呼ばれるので無駄なメトリック取得のためのアクセスやコールを防ぐことができるというメリットもあります

参考サイト

2018年10月5日金曜日

Prometheus ruby client を自分のアプリに組み込んでみよう

概要

prometheus-client という gem を使うと自分の Web アプリケーションに簡単に Prometheus で監視するためのメトリックを追加することができます
具体的には /metrics にアクセスするとアプリの監視のためのメトリックが取得できるようになります

環境

  • macOS 10.14
  • ruby 2.5.1p57
  • prometheus-client 0.8.0

ライブラリインストール

  • bundle init
  • vim Gemfile
gem 'sinatra'
gem 'prometheus-client'
  • bundle install --path vendor

Web フレームワークは Sinatra を使います
prometheus-client の Rack 用のモジュールを使うためです

メトリック取得用のロジック追加

prometheus-client にはすでにいくつかのメトリックが用意されています
Rack の設定ファイル (config.ru) にモジュールを読み込む設定を追加するだけで簡単にメトリックを取得できます

  • vim config.ru
require 'prometheus/middleware/collector'
require 'prometheus/middleware/exporter'

use Rack::Deflater
use Prometheus::Middleware::Collector
use Prometheus::Middleware::Exporter

上記を既存の config.ru に追加してあげましょう
先頭にでも追加すれば OK です

動作確認

  • bundle exec rackup config.ru

でアプリを起動したら localhost:9292/metrics にアクセスしてみましょう
ブラウザで確認すると以下のようなメトリックが取得できると思います
prometheus_client1.png

基本的にはアクセスに関するメトリックでアクセス数やレスポンスタイムなどを返却してくれます

最後に

Ruby の prometheus-client を使って自分のアプリでメトリックが取得できるようにしてみました
最低限のアプリの QA がしたいのであればこの程度の情報でもいいかもしれません

今回はデフォルトで用意されたメトリックを追加しました
実際のケースでは独自のメトリックも取得したくなると思います
この状態から独自のメトリックを追加する方法は別の記事で紹介したいと思います

参考サイト

2018年10月4日木曜日

Google さんから突如 Google AdMob ad serving has been disabled to your application という連絡が来て広告が表示されなくなったので対処してみた

概要

Googleさん (正確には Admob) からタイトルの連絡がありアプリ内で広告が出せなくなりました
それは困るので対処してみました

環境

  • Admob (2018/10/03 時点)
  • Android Studio 3.2.0

時系列

  • 9月27日 警告の連絡が来る
  • 9月27日 アピール送信
  • 9月28日 アピール送信二回目
  • 10月2日 アピール送信三回目
  • 10月3日 9時、解除連絡が来る
  • 10月3日 10時、広告表示を確認

以下それぞれ詳細を紹介します

内容

まず来たメールは以下の内容でした
admob_disable1.png

メインの内容は

This email is to alert you that one of your applications is not currently in compliance with our AdMob program policies and as a result, ad serving has been disabled to your application.

要するにあなたのアプリから広告を表示しなくしたよということが書いてあります
で、その下あたりに該当のアプリの情報があります

そして一番下にそうなった理由が書いてあります

GOOGLE PLAY REMOVAL: AdMob publishers are not permitted to abuse or promote the abuse of any Google product, such as Google Play, YouTube, or Blogger. This includes circumventing, or providing the means to circumvent, the policies or terms of these or other Google products, such as by allowing users to download YouTube videos.

If your app is removed by Google Play policy enforcement, please contact Google Play about an app removal here. If Google Play reinstates your app, please submit an appeal to our team.

GOOGLE PLAY REMOVAL という規約に引っかかりました
もし対処したりアピールしたい場合はここから連絡してね
ということが書いてあります

この GOOGLE PLAY REMOVAL のところはいろいろと種類があるらしく自分は GOOGLE PLAY REMOVAL でした
要するに Google Play からアプリが非公開になってしまったということらしいです
思い当たる状況を考えてみました

なぜこうなったのか

何の理由もなく Google さんがこんなことをするわけがないと思ったので思い当たることを考えみました

実は少し前にアプリが「プライバシーポリシーの記載がない」ということでリジェクトを食らっていました
しかしそれはすぐに対応して apk も公開し直しその後は問題なく配信されていました
おそらくその時のラグで Google Play から Admob に連絡が行くのが遅れて、まだアプリが非公開で対処されていないと判断され今回の連絡が来たのだと判断しました

つまりすでにアプリはプライバシーポリシーの対処をして公開状態になっているという旨を連絡すれば何とかなりそうだということに気が付きました

で、早速アピールしてみる

メールにもアピール用の URL が記載があります
https://support.google.com/admob/contact/appeal_policy_violation

admob_disable2.png

画面は一部抜粋ですがアプリの ID や今回の Issue の ID、Google Developer のメールアドレスを入力します
そして最後に「What changes have you made to your app or account in order to comply with our program policies? *」という欄がありここにアピールを書きます
文章の内容は残していなかったので記憶からですが以下のような内容を送信しました

  • プライバシーポリシーの警告が来てアプリがリジェクトされたのは間違いない
  • が、それは対処して apk も修正して公開し直した
  • 現在は問題なく公開されている

適当に英文を作成して送信しましたが日本語のページに切り替えることもできるので日本語でもアピール可能です
で、この内容でアピールすると以下のような自動返信が来ました
admob_disable3.png

あとは待つことしかできないかなーということで返信を待つことにしました
が、連絡が来るまでの間は当然広告は表示されません

連絡が来ないので何度か連絡してみた

時系列的に記載したのですが最終的に 3 回もアピールしてしまいました
今思うと必要なかったなと反省しています

土日も挟んだので当然と言えば当然かなと思います
営業日的には木、金、月、火、水 なので 5 営業日かかった計算になります
こちらの非で今回の状況になったのを考えると妥当な数字かなと思います

ただ不確かなのはこれが 3 回連絡したからそうなったのかどうかです
例えば 1 回だった場合には実はもっと遅くなっていたのでは、逆に早くなっていた可能性もあるかも、という点に関しては何とも言えません

さすがに 1 週間経っても何も音沙汰がない場合はお尻をつついてもいいかもしれません (もしちゃんとアプリの対応しているのであれば)

解除連絡到着

以下のメールが来ました
メールが届いてもすぐには広告が表示されることはなく何時間かかかりました
admob_disable4.png

一応自分の場合は 1 時間後に再度確認してみたら問題なく表示されていました

最後に

Admob の広告が突如表示されなくなったので対応してみました
基本はアプリの対応をちゃんとして Admob にアピール連絡するしかないと思います
最近は Google Play もちゃんとアプリの審査をするようになったかなと思います
良いことだと思うので続けて行ってほしいなとは思いますが開発者側にも負担が増えるので頑張らないなとは思います

他の理由で広告が非表示になるケースもあるらしいですが基本は同じ流れかなと思います
本当に何も思い当たる節がないのに連絡が来たのであれば、それもとりあえずアピールしてみると良いかと思います
詳細な理由を教えてくれるかは不明ですが

2018年10月3日水曜日

Prometheus と cAdvisor でコンテナの死活監視をする方法

概要

cAdvisor のメトリックを使って Prometheus でコンテナの死活監視をする方法です
どうやら cAdvisor のバグっぽいのでいずれはこの方法を使わないでも監視できるようになるかもしれないです

環境

  • Ubuntu 16.04 LTS
  • Prometheus 2.4.2
  • Prometheus AlertManager v0.15.2
  • cAdvisor v0.27.4
  • docker 18.03.1-ce

Prometheus の監視ルール

特定のコンテナを死活監視するルールは以下の通りです

groups:
  - name: 'Containers monitoring'
    rules:
    - alert: 'container_1 not running'
      expr: absent(container_tasks_state{name="container_1",state="running"}) == 1
      for: 5s
      annotations:
        summary: "{{ $value }}"

ポイントは absent を使う点です
本来 container_tasks_state は state に応じて適切な値を返却するはずなのですがどの state も常に 0 を返却するバグがあるようです
そしてコンテナが停止した場合には何もデータが入ってきません

absent はデータがない場合に 1 を返却してくれる関数になります
つまりデータがない = コンテナが停止しているときにアラートを上げる監視ルールを追加して上げれば OK です

Issue を見ると bug のラベルが振られているのでいずれ修正される問題かなと思います

参考サイト

2018年10月2日火曜日

Prometheus のアラート機能を試してみた

概要

Prometheus には蓄積したデータからアラートを発泡することができます
アラートの種類はさまざまあり Slack や Gmail などに通知することができます
アラートを通知するには AlertManager というプロセスを立ち上げる必要があります
今回は AlertManager の構築方法と構築した Prometheus サーバとの連携方法を紹介します
また簡単な通知のテストを Slack を使ってやってみました

環境

  • Ubuntu 16.04 LTS
  • Prometheus 2.4.2
  • Prometheus AlertManager v0.15.2
  • docker 18.03.1-ce

事前準備

Prometheus サーバの構築と Slack の Incomming Webhook のインテグレーションの設定は済ませておいてください

Prometheus サーバに AlertManager を登録する

まずは Prometheus サーバに通知を依頼する AlertManager を登録します
登録するには YAML ファイルを編集して Prometheus サーバを再起動します

  • vim prometheus.yml
alerting:
  alertmanagers:
  - static_configs:
    - targets:
      - '192.168.100.20:9093'

alertmanagers ディレクティブを使います
192.168.100.20 は AlertManager です
あとでこのサーバ上で AlertManager を構築します
ポートは AlertManager デフォルトの 9093 を使用します

Prometheus サーバに監視ルールを登録する

Prometheus に蓄積データを使って通知を飛ばすには監視ルールを作成する必要があります
ここで言う監視ルールとは単純に「CPU 使用率 80%」などのルールになります
ルールを登録するには例のごとく YAML ファイルを編集します
ちなみにルールを UI から追加することはできないので新規で監視ルールを追加する場合はルール用の設定ファイルを毎度編集する必要があります

  • vim prometheus.yml
rule_files:
  - 'alerts.rule'

alerts.rule ファイルを別途作成しここに監視のルールを記載します
ルールも YAML で定義します
Prometheus のバージョン 2 からルールも YAML で定義できるようになりました
バージョン 1 を使っている場合は監視ルールの YAML 定義が使えないので注意してください

groups:
  - name: 'Containers monitoring'
    rules:
    - alert: 'container_1 cpu usage alert'
      expr: container_cpu_usage_seconds_total{name=~"container_1",cpu="cpu01"} > 80
      for: 5s
      annotations:
        summary: 'container_cpu_usage_seconds_total:container_1 > 80'

ポイントは expr と for と annotatins.summary になります

expr は監視する式を記載します
container_cpu_usage_seconds_total は cAdvisor の exporter を使って取得できるデータになります
cAdvisor と Prometheus を連携する方法はこちらを参照してください
あとは引数に監視するコンテナ名と CPU が複数あるので 1 つ分の CPU を指定します
この値が > 80 になったときにアラートが発泡されます
> 80 は単純に CPU 使用率が 80% を越えたらという条件になります

for は > 80 の条件がどれだけの期間続いたらアラートを発泡するかの指定になります
つまり 5s を指定した場合は 1 秒や 2 秒だけ 80% を越えてもアラートは来ません

annotations.summary はアラート時のメッセージになります
今回は使っていませんが $value などの変数が使えます
CPU の使用率などが変数に展開されるのでそれを通知内容に含めることができます

AlertManager 用の設定ファイルを作成する

AlertManager 用の設定ファイルには主に通知先の設定を記載します
今回は Slack に通知するので Slack の Incomming Webhook の URL やチャネルなどを定義します

  • vim alertmanager.yml
route:
  receiver: 'containers_notification'
receivers:
- name: 'containers_notification'
  slack_configs:
    - api_url: 'https://hooks.slack.com/services/xxxxxxxx/xxxxxxxxx/xxxxxxxxxxxxxxxxxxxxxxxx'
      channel: '#general'
      text: "{{ .CommonAnnotations.summary }}"
      send_resolved: true

api_url や channel に関しは Slack の情報を入力してください
ポイントは {{ .CommonAnnotations.summary }} でルールで定義した annotatins.summary の内容を参照することができます

AlertManager を起動する

今回は Docker で起動します

  • docker run -d -p 9093:9093 --name alertmanager -v $(pwd):/alertmanager prom/alertmanager --config.file=/alertmanager/alertmanager.yml

で 9093 で LISTEN すれば OK です
ブラウザでアクセスすると簡単な管理画面を確認することができます

alertmanager: error: unknown short flag '-c', try --help というエラーが出る場合は引数を見直してください
--config.file でハイフンが 2 つ指定が必要です
-config.file でハイフンが 1 つだと上記のエラーが発生します

Prometheus サーバを起動する

すでにコンテナが起動している場合は docker restart だけで OK です
まだコンテナが起動していない場合は以下で起動します

  • docker run -d -p 9090:9090 --name prometheus -v $(pwd):/prometheus-data prom/prometheus --config.file=/prometheus-data/prometheus.yml

9090 にアクセスすると管理画面が確認できます

動作確認

まずはちゃんと監視ルールが Prometheus サーバに認識されているか確認しましょう
Alert を開いて監視ルールが存在するか確認します
prometheus_alertmanager1.png

現状は CPU 使用率が 80% 以下なのでアラートが飛びません
適当にコンテナの CPU 使用率を上げると AlertManager に対して通知依頼おリクエストが飛びます

Slack には以下のようにアラートが来ると思います
prometheus_alertmanager2.png

最後に

Prometheus のアラート機能を試してみました
ポイントは監視ルールの定義と AlertManager の通知設定かなと思います
まずは監視ルールだけ追加して問題なくルールが動作しているようであれば AlertManager を追加する流れが良いかなと思います

通知先と AlertManager の登録は一度行えば OK なので後は監視ルールの変更をチューニングしましょう

参考サイト

2018年10月1日月曜日

Prometheus と cAdvisor を連携してコンテナごとのメトリックを取得する方法

概要

前回 dockerd の Daemon metrics を使って docker コンテナのメトリック情報を監視してみました
ただ Daemon metrics の場合各コンテナごとのメトリック情報は取得できませんでした
今回は代わりに cAdvisor を使い各コンテナごとのメトリック情報を取得してみました

環境

  • Ubuntu 16.04 LTS
  • Prometheus 2.4.2
  • docker 18.03.1-ce
  • cAdvisor v0.27.4

cAdvisor のインストール

監視対象の docker ホストで以下のコマンドを実行します
普通に cAdvisor をインストールしています
ホスト側は 8080 ポートを使っているのですでに 8080 ポートを使っている場合は別のポートにしてください

docker run \
  --volume=/:/rootfs:ro \
  --volume=/var/run:/var/run:ro \
  --volume=/sys:/sys:ro \
  --volume=/var/lib/docker/:/var/lib/docker:ro \
  --volume=/dev/disk/:/dev/disk:ro \
  --publish=8080:8080 \
  --detach=true \
  --name=cadvisor \
  google/cadvisor:latest

この時点で cAdvisor はインストールされているので localhost:8080 にアクセスすると cAdvisor の UI でメトリック情報を確認することができます

Prometheus に監視対象を追加する

  • vim prometheus.yml

scrape_configs に以下の job_name を追加しましょう

scrape_configs:
  - job_name: 'worker_cadvisor'
    static_configs:
    - targets: ['192.168.100.12:8080']

追記したら Prometheus のコンテナを再起動します

  • docker restart prometheus

prometheus の部分は Prometheus のコンテナ名またはコンテナ ID になります

動作確認

Status -> Targets に監視対象の exporter が追加されていると思います
スタータスが UP になっていることを確認しましょう
メトリック名は container_ から始める項目になります

最後に

Prometheus と cAdvisor を連携してみました
これで各コンテナごとのメトリックが Prometheus で監視できるようになります
目的のコンテナのデータを取得するには promQL を駆使する必要があります

参考サイト