Posted at

OpenCensusでStackdriver Monitoringにカスタム指標を記録する


TL;DR


  • OpenCensus(Stats)でStackdriver monitoringにCustom Metricを送信した


概要

エラー調査の記事を書こうとしたのですが、前提とするシステムの構成要素が多いため前半と後半に分割しました。この記事はその前半部分です。

前半は、OpenCensusを利用してGCPのStackdriver Monitoringにpythonアプリケーションの測定値を収集する方法を説明します。

後半では、前半のアプリケーションをGKEに乗せたときに出た次のエラーについて原因と解決方法を記載します。

grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with:

status = StatusCode.INVALID_ARGUMENT
details = "One or more TimeSeries could not be written: The set of resource labels is incomplete. Missing labels: (container_name namespace_name).: timeSeries[0-5]"
debug_error_string = "{"created":"@1568274913.190983719","description":"Error received from peer ipv4:172.217.25.106:443","file":"src/core/lib/surface/call.cc","file_line":1052,"grpc_message":"One or more TimeSeries could not be written: The set of resource labels is incomplete. Missing labels: (container_name namespace_name).: timeSeries[0-5]","grpc_status":3}"


やりたいこと

アプリケーション固有の測定情報を活用したい。そのために以下の技術要素を利用する。


Monitoring

面倒くさくて後回しにされがちな気がしますが、堅牢なシステムを作る上で監視は重要な要素です。

基本的にはCPUやメモリ, Disk使用量、ネットワークトラフィックの時系列データを蓄積し、グラフ表示したりしきい値を設定してアラートを飛ばしたりします。また従来の用途の他には、SRE的なアプローチを取る場合にもまずはシステムの状態が監視できていないとお話になりませんし、ABテストの結果も監視のデータを元に行います。

Monitoringの関心事は大きく収集・集約・蓄積・利活用に分けられます。


OpenCensus

OpenCensus is a set of libraries for various languages that allow you to collect application metrics and distributed traces, then transfer the data to a backend of your choice in real time.

https://opencensus.io/

OpenCensusは測定値をPrometheusなどのバックエンドに送信する処理を様々な言語で実装しているライブラリです。Monitoringのうちの収集・集約を担います。

2019/9現在次の言語がサポートされています。

https://opencensus.io/language-support/


  • Go

  • Java

  • C#

  • C++

  • Node.js

  • Ruby

  • Erlang/Elixir

  • Python

  • PHP

バックエンドとデータをやり取りする部分はExporterとして言語ごとバックエンドごとに実装されます(大変そう)。

ちなみにOpenCensus + OpenTracing => OpenTelemetryになることが発表されていますが、OpenTelemetryとしてのソフトウェアがまだないためしばらくはOpenCensus, OpenTracingを利用することになるようです。


OpenTelemetry will offer backwards compatibility with existing OpenCensus integrations, and we will continue to make security patches to existing OpenCensus libraries for two years.

https://opencensus.io/



Stackdriver Monitoring

Stackdriver MonitoringではMonitoringのうち収集・集約(・可視化)する基盤を提供しています。

GCP上のリソースの基本的なMetricは自動で収集されます。

https://cloud.google.com/monitoring/api/metrics_gcp

また、Metric収集のためのAPIを提供しており、GCP metric以外の測定値もstackdriverに収集することができます。

このときmetricは何でもよくて、例えば自分の心拍データとかAndroidからセンサーデータを飛ばすとかテストの点数とか本当に何でもいいです。

https://cloud.google.com/monitoring/custom-metrics/


環境


mac

$ sw_vers

ProductName: Mac OS X
ProductVersion: 10.14.5
BuildVersion: 18F132


python

$ python --version

Python 3.7.3


実装

OpenCensus-pythonのexampleを参考に小さなアプリケーションを作成します。

https://github.com/census-instrumentation/opencensus-python/blob/master/contrib/opencensus-ext-stackdriver/examples/stackdriver.py


main.py

import os

import time
import json
import random
import logging
import google.auth
from opencensus.stats import view as view_module
from opencensus.stats import stats as stats_module
from opencensus.stats import measure as measure_module
from opencensus.stats import aggregation as aggregation_module
from opencensus.ext.stackdriver import stats_exporter as stackdriver

INTERVAL_SECONDS = os.getenv('INTERVAL_SECONDS', 60)
logger = logging.getLogger(__name__)
DEBUG = os.getenv('DEBUG', False)
if DEBUG:
logging.basicConfig(level=logging.INFO)

def verify_credential():
if 'GOOGLE_APPLICATION_CREDENTIALS' in os.environ:
# You can specify the path of a service account key.
with open(os.getenv('GOOGLE_APPLICATION_CREDENTIALS'), 'r') as f:
credential = json.load(f)
project_id = credential.project_id
else:
# If there is no clue, get credential by OAuth.
try:
_, project_id = google.auth.default()
except google.auth.exceptions.DefaultCredentialsError:
raise ValueError("Couldn't find Google Cloud credentials, set the "
"project ID with 'gcloud set project'")

return project_id

def init(project_id):
# measure defines metricKind and ValueType in stackdriver
# measurement is data Point in TimeSeries in stackdriver
measure = measure_module.MeasureInt('metric_name', 'measure description', 's')

# The stats recorder
view_manager = stats_module.stats.view_manager
stats_recorder = stats_module.stats.stats_recorder

# view is MetricDescriptor in stackdriver
view = view_module.View(
'metric_name',
'metric description',
[],
measure,
aggregation_module.LastValueAggregation())

# Enable metrics
# you need credential. use google.auth or set environment variable instead.
exporter = stackdriver.new_stats_exporter(
stackdriver.Options(project_id=project_id))
view_manager.register_exporter(exporter)

view_manager.register_view(view)
mmap = stats_recorder.new_measurement_map()

return mmap, measure

def main():
project_id = verify_credential()
mmap, measure = init(project_id)

while True:
time.sleep(INTERVAL_SECONDS)

metric = random.randint(1, 100)

mmap.measure_int_put(measure, metric)
mmap.record()

logger.info('put value {}'.format(str(metric)))

if __name__ == '__main__':
main()



verify_credential


抜粋

    if 'GOOGLE_APPLICATION_CREDENTIALS' in os.environ:

# You can specify the path of a service account key.
with open(os.getenv('GOOGLE_APPLICATION_CREDENTIALS'), 'r') as f:
credential = json.load(f)
project_id = credential.project_id
else:
# If there is no clue, get credential by OAuth.
try:
_, project_id = google.auth.default()
except google.auth.exceptions.DefaultCredentialsError:
raise ValueError("Couldn't find Google Cloud credentials, set the "
"project ID with 'gcloud set project'")


GCPのStackdriver monitoringに書き込みをするため、roles/monitoring.metricWriterの権限が必要です。

https://cloud.google.com/monitoring/access-control

アプリケーションでこの権限を利用する方法は4通りありますが、今回はService Account, OAuth 2.0のどちらかを利用することにします。

https://cloud.google.com/docs/authentication/

環境変数GOOGLE_APPLICATION_CREDENTIALSにサービスアカウント鍵(jsonファイル)のPathを設定していればサービスアカウントを利用、そうでなければOAuth2.0を利用してユーザ権限でアクセスします。


init

OpenCensusとStackdriverは異なるので、利用する際にはそれぞれどの概念がどの概念に対応しているのかを意識すると良いです。

https://cloud.google.com/monitoring/custom-metrics/open-census#mapping-models

厳密に1:1の対応にはなっていないですが、およそ次の通りの対応になっています。

Stackdriver Monitoring
OpenCensus
Description

MetricDescriptor
view
how to collect and aggregate individual measurements

LabelDescriptor
tag
contextual information that can be used to filter and group metrics

ValueType
measure
metric data to be recorded

MetricKind
aggregation
a function applied to data used to summarize it

(data) Point
measurement
a data point collected for measure

TimeSeries
view data
aggregated data


メインループ


抜粋

    while True:

time.sleep(INTERVAL_SECONDS)

metric = random.randint(1, 100)

mmap.measure_int_put(measure, metric)
mmap.record()


メインループで測定値(この場合はランダムな整数)を記録しています。

mmap.recordで値を記録していますがこのタイミングでStackdriver APIが呼ばれるわけではなく、裏でThreadで一定期間ごとに送信しています。


実行

参考実装をこちらのrepositoryに上げています。

https://github.com/ymaki/opencensus-stackdriver-sample

こんな感じで実行してください。

$ git clone https://github.com/ymaki/opencensus-stackdriver-sample

$ cd opencensus-stackdriver-sample
$ pip install -r requirements.txt
$ python main.py
/Users/hoge/playground/opencensus-stackdriver-sample/lib/python3.7/site-packages/google/auth/_default.py:66: UserWarning: Your application has authenticated using end user credentials from Google Cloud SDK. We recommend that most server applications use service accounts instead. If your application continues to use end user credentials from Cloud SDK, you might receive a "quota exceeded" or "API not enabled" error. For more information about service accounts, see https://cloud.google.com/docs/authentication/
warnings.warn(_CLOUD_SDK_CREDENTIALS_WARNING)

OAuth2.0でユーザ認証していることを警告されています。気になる場合はService Accountを作成して鍵情報を適切に渡してください。

また、ログに何も出ないのでちゃんと動いているかわからないという場合は次のようにプログラムを起動してINFOレベルを出力しましょう。

$ DEBUG=True python main.py


確認

GCP consoleのStackdriver monitoringから値が入っているかを確認します。

スクリーンショット 2019-09-19 14.23.32.png

良さそう🎉

metric情報は以下のようになり、custom.googleapis.com/opencensus/のprefixがつくことがわかります。

Metric: custom.googleapis.com/opencensus/metric_name

Description: metric description
Resource type: global
Unit: s Kind: Gauge Value type: Int64


活用

Stackdriver Monitoring自体にデータをグラフで表示する機能やアラートなど基本的な機能が揃っています。

API経由でgrafanaなどの外部ツールで可視化することも可能です。

収集したデータは6週間で消えてしまうため、必要であればBigQueryなどに蓄積すると良いでしょう。 https://cloud.google.com/monitoring/quotas#data_retention_policy


まとめ

アプリケーション固有の時系列データを収集するために、OpenCensusを利用してStackdriver Monitoringに測定データを送ることができました。