YAMAGUCHI::weblog

海水パンツとゴーグルで、巨万の富を築きました。カリブの怪物、フリーアルバイター瞳です。

OpenCensus + Stackdriver Trace で分散トレース上にログを表示する

はじめに

こんにちは、Stackdriver担当者です。いま出張でアメリカ西海岸に来ていますが、時差ボケで破滅しています。

GCPUG Stackdriver Day January 2019でStackdriverを使った分散トレースにログを埋め込む話をしたんですが、スライドだけだともったいないと言われたのでブログの記事にもしておこうかと思います。

OpenCensusとはなにか

そもそもOpenCensusを知らないという人もまだ多いと思うので、まずそこから紹介します。OpenCensusは分散アプリケーションのメトリクスとトレースを取得するためのライブラリ群です。

opencensus.io

分散トレースのライブラリは各種APMサービスがそれぞれクライアントライブラリを出していますが、OpenCensusが特徴的なのは、TraceやStatsを取得する部分と、取得されたデータをバックエンドに送信する部分(exporter)が別れているので、バックエンドを切り替える際もexporterのインスタンスの初期化だけ書き換えれば動作するようになっていることです。また自分で独自のexporter(例: 標準出力に記録するだけのexporter)を書くこともできます。

現在 OpenCensus をサポートしているAPMツールはこちらに一覧として載っています。

opencensus.io

また今後の方向性としては OpenCensus Agent を通じてデータを送信するように変更し、これによって OpenCensus Agent 側で exporter を切り替えるだけでバックエンドが変更できるようになり、切り替えのためにソースコードを変更しなくて良くなるようにしていく予定になっています。

Stackdriver Trace

Stackdriver Trace は Google Cloud Platform が提供する APM (Application Performance Management) ツールの一つですが、特に分散アプリケーションのトレース(分散トレース)を主力機能として提供しています。Stackdriver Trace はクライアントライブラリとして OpenCensus を利用するように推奨しています。推奨しているということは当然 exporter もあります。

github.com

OpenCensusを使ってStackdriver Traceを利用する場合、Goではinstrumentationは次のようになります。

Stackdriver Trace のタイムライン内にログを埋め込む

上のサンプルのように各アプリケーション(例えばGKEクラスタで動かしている各サービス)で Trace と exporter の設定をしてからリクエストを投げてやると、次のような形で分散トレースを生成できます。

f:id:ymotongpoo:20190212082459p:plain

これでもマイクロサービス内のどこで処理時間がかかっているか簡単にわかりますが、このタイムラインの中で各Span(各サービスで行われる処理のまとまり)内で起きたイベントを確認できると便利そうです。Stackdriver Trace では Stackdriver Logging へ送信された構造化ログの中に次の2つのフィールドに適切な値が入っていた場合、そのログを Stackdriver Trace の中に表示させることができます。

  • logging.googleapis.com/trace
  • logging.googleapis.com/spanId

これら2つのフィールドに必要な Trace ID および Span ID はそれぞれHTTPヘッダやgRPCの特殊フィールドに埋め込まれていて、通常は OpenCensus のライブラリがよしなに取得できるようにしてくれています。再び Go の例で書けば次のような操作で取得できます。

func (ap *arrayParseServiceServer) Parse(ctx context.Context, pr *pb.ParseRequest) (*pb.ParsedArray, error) {
    span := trace.FromContext(ctx)
    sc := span.SpanContext()
    l := logger.WithFields(logrus.Fields{
        "logging.googleapis.com/trace": sc.TraceID.String(),
        "logging.googleapis.com/spanId":  sc.SpanID.String(),
    })
    ...
}

GKEではStackdriver Logging のエージェントが自動設定されるので、標準出力に構造化ログを出力するだけで Stackdriver Logging に送信されます。上記のフィールドに正しい値が入ったログが Stackdriver Logging に送信されると、Stackdriver Trace がそのログの中にある Trace ID と Span ID をトレースに紐づけ、タイムライン内に表示します。(タイムライン右上に [Show/Hide Logs] というボタンが表示され、ログの表示/非表示を切り替えられるようになります。)

f:id:ymotongpoo:20190212083746p:plain

またタイムライン内のログをクリックすると、画面右下に構造化ログ全体が表示されます。

f:id:ymotongpoo:20190212084113p:plain

この機能はGAEを使っていた方にはおなじみだったかもしれませんが、GKEを使っている場合においてもちょっと手を加えるだけで使えるようになるので、GKEで分散トレースを考えている場合にはぜひ利用してみてください。

参照

skaffoldのdefault repoの設定はグローバルでなくkubectlのコンテキスト依存

何が起きたか

GKEで新たなクラスタを建てて skaffold dev をしたら次のエラーで止まった。

exiting dev mode because first run failed: build failed: building [xxxxxx]: tagging: pushing: denied: requested access to the resource is denied

後半はDockerでレポジトリのアクセスが出来ない時のエラーなので、ログをちょっと遡ると原因となるものがあった。

The push refers to repository [docker.io/library/xxxxxx]

本当なら docker.io/library ではなくて gcr.io/<MY REPOSITORY> を見ていないといけない。

どうしてこうなったか

まず skaffold.yamlartifacts でターゲットのレポジトリの設定をしていないから。

しかし設定していない理由があって、skaffold.yaml でレポジトリの設定をしてしまうと、テストとかで動かすときにいちいち書き換えなければならないので面倒。 固定でいいならはじめから artifacts にレポジトリを明示的に指定しておけば良い。こんな具合に。

apiVersion: skaffold/v1alpha1
kind: Config
build:
  artifacts:
  - imageName: gcr.io/<MY REPOSITORY>/foo
    workspace: ./foo
...

skaffold には新しいオプションが加わって、最近レポジトリを動的に差し込めるようになった。

skaffold.dev

どうせ自分のテスト環境はレポジトリ1個しかないので「グローバルの設定」で済まそうと思って3つめのオプションを選択。 最初に設定したときはこれで問題なかったが、テスト用に新たにGKEクラスターを作って古い方を消したときに上記の問題が起きた。

再度手順を振り返って一つずつコマンドを叩いて確認してみると

$ skaffold config set default-repo gcr.io/yoshifumi-cloud-demo
set value default-repo to gcr.io/yoshifumi-cloud-demo for context gkc_yoshifumi-cloud-demo_<ZONE>_<CLUSTER_NAME> 

これは普通に kubectl のコンテキストに紐付いていて、実際に skaffold の設定ファイル(~/.skaffold/config)を見るとそうなっている。

kubeContexts:
- kube-context: gke_yoshifumi-cloud-demo_<ZONE>_<CLUSTER_NAME>
  default-repo: gcr.io/yoshifumi-cloud-demo

というわけで

  • skaffold.yamlartifacts でイメージ名内に明示的に指定していなかったこと(これは意図的)
  • グローバルに設定されると思ってたがそれがドキュメントバグらしいということ

の2つが原因だった。

どうするのがいいか

とりあえず2つめのオプションである SKAFFOLD_DEFAULT_REPO を都度渡すというのが良さそう。つまり

$ SKAFFOLD_DEFUALT_REPO=gcr.io/yoshifumi-cloud-demo skaffold dev

のような形にするということ。この辺りの処理はこの辺で実装されている。(2019.01.23現在 master)

github.com

しかしこのドキュメントは明らかにミスリードなので報告しとこう。。。

Change the explanation of "default repo" setting in the doc to reflect actual implementation · Issue #1516 · GoogleContainerTools/skaffold · GitHub

YAMAGUCHI::weblogの2018年を振り返る

はじめに

こんにちは、Stackdriver担当者です。今年も年の瀬の本日に一年歳を重ねました。毎年この時期になると「今年もおしまいですねー、早く新年にならないかなー」みたいな感じで、すごく消化試合感を出されるんですが、僕の誕生日が残ってるんでほんとにそういう雰囲気やめてください!あと例のやつを貼りました。よろしくお願いします。

毎年やってるので今年も1年振り返ります。

関連エントリ

ここまで続けると毎年やらないわけにいかない感じになってる。

ymotongpooの2018年

昨年立てた目標

  • FP技能検定2級
  • 自作キーボード
  • ジム
  • 書籍

FP技能検定2級

無事にFP技能検定の3級、2級と続けて合格し、晴れて「二級ファイナンシャル・プランニング技能士」となりました。*1

去年は、長いことやろうと思ってやっていなかった資産運用を考え始め、自分がFPの方に相談をし、実際に組んだポートフォリオ計画を実践し、ということを行った1年でしたが、今年はそれに飽き足らず資格を取りました。

ファイナンシャル・プランニング業務を行っている職種というのは非常に多く、たとえば金融商品の営業の一環として行っている方も多くいます。しかし、自分が興味があるのが純粋にファイナンシャル・プランニングを行う独立系の業務で、資格を取ったこともあり、興味が高じて友人や知人のファイナンシャル・プランニングのお手伝いを数多く行っていました。

そして、いろいろな活動を行っていた結果、ついに副業としてファイナンシャル・プランナー業務を始めることとなりました。(ファイナンシャルプランナーとして独立系の会社から業務委託を受けることとなりました。)これを読んでいる方でもファイナンシャル・プランニングや資産運用に関して相談をしたいけれど、どこに相談していいかわからないという方がいらしたらお気軽にご連絡ください。(TwitterのDMとか)相談料は応相談です。

自作キーボード

Let's Splitを作ったあと、購入してだけして作業に取り掛かれていない基盤がいくつか積まれた状態になってしまいました。

来年はfoobarをいくつか組んで、極小キーボードに慣れていこうかなと思います。

ジム

自作キーボードもそうなんですが、諸々なイベントが重なったことで今年はいろいろなことがあったので、だいぶこうした細かな活動を継続的に行うのが大変でした。しかしジムワークは生活に欠かせないものになっていたので、なるべく時間をとって、最低でも週1回、通常時は週2回、行けるときは週3回行くようにしていました。

おかげで体のサイズもだいぶ大きくなり、4年前は日本のサイズでMサイズのTシャツを着ていた体ですが、いまはUSサイズのXLでないと肩や胸周りにゆとりがないほどにサイズアップしました。来年はここからネジをキリキリと締めていくように絞りたいたいという思いがあり、現在計画中です!

書籍

翻訳書籍が無事発刊に至りました。

Go言語による並行処理

Go言語による並行処理

ymotongpoo.hatenablog.com

Go言語のユーザーもだいぶ増え、プロダクションでGo言語を使っている会社もだいぶ増えましたが、そんな状況においてもいまだにGo言語の特徴の一つである並行処理の方法に関してまとまった書籍がなかったため、この原著が市場にでたことは一定の影響があったと思っていました。そんな本の邦訳に携われたことはとてもありがたいことでした。

4年ぶりの書籍の翻訳でしたが、翻訳特有の2段階の正誤確認(翻訳そのものの正誤、原文の内容自体の正誤)はなかなかに大変な作業で、残念ながら今回の翻訳でも多くのerrataが登録されてしまいました。次回がいつあるかはわからないですが、今回の経験をもってよりよいものにしたいです。書籍翻訳のお話がありましたらぜひご連絡ください。

また翻訳とは別に書きたいと思っている内容があるので、こちらについては一度内容を詰めて企画を練ろうと思います。来年書き始められるといいな。

今年やっていたこと

仕事など

今年の上半期は去年に引き続きGoogleアシスタント関連の仕事をメインにしつつ、 ウェブ関連やAndroid関連の担当をしていました。

そして今年の6月にこれまで4年在籍したチームを離れて、7月からGoogle Cloud DevRelのチームに移り、オブザーバビリティ(担当製品としてはStackdriver)の担当となりました。ここ数年のインフラ周りの動きは個人的に非常に面白と思っていて、もともと5年ほどGoの普及などもやっていたこともあり、クラウドに移りたいなと考えていたところ、チームを拡大するということだったので手を挙げて移動しました。

久々にどっぷりインフラ系の領域に戻れるというのは自分としても嬉しい限りです。またオブザーバビリティの領域はこれから確実に盛り上がってくる領域なので、来年は温めていた企画などをできればと思っています。新年一発目、GCPUGでStackdriverの回があります!

gcpug-tokyo.connpass.com

出張/旅行

今年も多くの出張や旅行に行きました。

とくに7月後半からは一週間おきに出張だったので結構体力的にしんどいものがありました。来年もすでにいくつか出張が決まっていますが、うまくインターネットを使って解決できるところはしたいものです。

資格

上にファイナンシャル・プランニング技能士の資格を取得したことは書きましたが、ファイナンシャル・プランナーというのは別に資格を取らなくても名乗れる業種です。*2 さらに関連する細かな業務を行おうとすると、税理士、宅建士、保険外交員、証券外務員といった資格を取らなければなりません。もちろんファイナンシャル・プランニング技能士としてだけでもできることは十分ありますが、やはり自分が色々なことを知るほど、資格がないことでできないというのは残念で仕方がありません。

来年からまた新たに資格の勉強などを進めつつ、この領域での経験も積んでいければと思います。

来年に向けて

今年もゆるい目標を立てておきます。

  • ログに関する知見の向上
    • オブザーバビリティ領域でも特に来年はログの領域の知見を高めていきたいと思っています。
  • ジム
    • 2年半以上続けているので来年も頑張ります。来年は絞りたい。
  • 部屋の片付け
    • いろいろとものが増えてきたので不必要なものは捨てつつ、デッドスペースの有効活用などをしたいと思います。

*1:公に記載する場合の表記方法が定められています

*2:「ファイナンシャル・プランニング技能士」自体は有資格者のみが名乗れる資格ですが、「ファイナンシャル・プランナー」はだれでも名乗れます。

2018年に買ってよかったもの

はじめに

こんにちは、Stackdriver担当者です。今年も残すところあと2日ですがみなさんいかがお過ごしですか。さて、ここ2年続けて書いていたので今年も1年で買ってよかったものを書いていこうかと思います。

2018年に買ってよかったもの

KARCHER K3 サイレントベランダ

いまの家は住み始めて4年弱になりますが、これを買うまではベランダの掃除がなかなか面倒で、水道もないため水を撒いてはデッキブラシでこするというようなことをして頑張ってきれいにしていました。しかし、3年も過ぎた頃からベランダの壁や、窓のサッシ等々、ブラシで擦ったり雑巾で拭いてもなかなか取れない汚れが溜まってきました。

そこではじめはレンタルでケルヒャーを借りようと思ったんですが、1回数千円する上に返送の手間もあり面倒だなと思ったので、10回使えば元が取れるということで買ってしまいました。このモデルは日本の集合住宅で使うことを想定しているので、従来モデルと比較してもかなり音が静かになっていました。(K2とか、線路のガード下にいるくらいの音がする)

買ってみて、まずベランダがあっという間にきれいになったことに驚きました。本当にありきたりな感想になってしまいましたが、ベランダの3年半分のこびりついた汚れがあっという間に取れ、気持ちいいくらいにきれいな表面が見えるようになりました。またブラシで届かなかった壁や天井もきれいにできたのはおもったよりも達成感がありました。 更にベランダだけでなく、風呂場も取りづらかったカビや排水溝周りの汚れも一気に落とせたので、なかなかに使い所があります。

先に書いたようにベランダには水道の蛇口がないため、ため水を使って水を供給していますが、ケルヒャーのホースやガンの収納も兼ねて、次のような収納ボックスを使って水をためています。蛇口から直接汲んできた水の場合は問題はないはずですが、風呂場の水の再利用も考えてフィルターも使っています。

モレスキン ノートブックツールベルト + フリクションボールスリム ビズ

2018年は手書きメモをちょくちょく取るようになったんですが、その際にノート以外のものをたくさんバラバラと持つのが面倒だったので、モレスキン純正のツールベルトを使い始めました。ここにペンやら付箋やらを詰め込んでいます。

ボールペンなのに消せるというフリクションは結構好きで使っていて、最近はノベルティーとしてもらったフリクションはすべてインクがなくなるまで使い切ってしまいました。そんな経緯もあって、ノートにメモをとるためのペンはノベルティーのペンを使い切ってからもフリクションを使っていたのですが、ペンの太さが携帯するには少し太い印象があったので、このスリムを使い始めたのですが、重さといい細さといいすごく使いやすくて満足です。

Raspberry Pi 3 B+ +ハードディスクケース

玄人志向 3.5型HDDケース SATA接続 電源連動 USB2.0対応 マットブラック GW3.5AA-SUP/MB

玄人志向 3.5型HDDケース SATA接続 電源連動 USB2.0対応 マットブラック GW3.5AA-SUP/MB

Raspberry Piがかんたんなサーバーとして便利であることは言うまでもないのですが、唯一のネックは永続化ディスクがmicro SDカードであるため、ログの書き込み等々の設定をきちんと行わないとSDカードの書き込み回数の限界をすぐに超えてしまい常用サーバーとして利用することはできません。

そこで外部ストレージで起動できるように設定を変更してその心配をなくしました。起動速度がクリティカルになったりすることはないので、家に転がっていた3.5'' HDDを利用しました。今のところまったく問題なく安定して稼働しています。

iPad Pro 11'' 256GB + Apple Pencil

Apple 11インチ iPad Pro Wi-Fiモデル 256GB スペースグレイ MTXQ2J/A

Apple 11インチ iPad Pro Wi-Fiモデル 256GB スペースグレイ MTXQ2J/A

ノートを書き始めると同時に、自分が資料の作成時に使う挿絵も手描きで作りたくなりました。はじめは手でラフに書いてから、スライドの作図機能を使って書いていたのですが、どうせなら直接貼れる画像を作りたいと思うようになり、ちょうど発売になったiPad ProをApple Pencilとともに購入しました。

今回のiPadを使う前はiPad miniをずっと使っていたのですが、家にある常用端末のうちでLightningケーブルが必要になるものがそれしかなかったため、非常に不便に感じていました。しかし今回のアップデートでついに他社製品と共有できるUSB-Cケーブルの利用となったことは買うにあたって非常に大きなモチベーションとなりました。

またApple Pencilの充電方法も初代のものから進化し、本体に磁石で取り付けるだけで充電できるようになったのも、収納や安全性の観点から買うにあたっての安心感が大きくなりました。久々に文句なく使えるApple製ハードウェアです。挿絵だけでなく、PDFの添削など快適に利用しています。

メタルラック

自分は片付けが苦手なタイプです。サーバーとかアンプなんかもリビングの床にそのまま置いたりしていたんですが、いよいよ目障りになってきたので一箇所にまとめるべくメタルラックを購入しました。こうしたラックはいくつか有名な製品があって、エレクター「ホームエレクター」シリーズドウシシャ「ルミナス」シリーズ などが有名ですが、アイリスオーヤマのメタルラックシリーズを選んだのは、その値段の安さと製品の展開がちょうどよかったのが決め手でした。

おわりに

今年は去年ほどデジタルグッズを買いませんでしたが、この年末で大掃除をしていたらまた新たに自動化したりスマートホーム化したくなってきたので、来年は細かな購入が増えそうです。

golang.org/x/text/messageでI18N

はじめに

こんにちは、Stackdriver担当者です。この記事は Go Advent Calendar 2018 *1の最終日のエントリです。昨日は @yasuo-ozuさんの「Go言語は沼」 でした。

ところで今日はクリスマスですね。自分宛も含めてまだプレゼントを送っていない方はこの本を送るのがおすすめです。

Go言語による並行処理

Go言語による並行処理

年末年始休暇に読んでもらってGo言語による並行処理への理解を深めてもらいましょう!

さて、今日は準標準パッケージの "golang.org/x/text/message" の紹介です。本文に出てくる雑なサンプルのリンクを貼っておきます。

"golang.org/x/text/message" とは

godoc.org

Go準標準パッケージ内にある、国際化のためのパッケージです。履歴を見ればおわかりの通り、非常に地味に更新が続いているパッケージです。

Log - master - text - Git at Google

このパッケージは大きく分けて2つの使い方があって

  • フォーマットのローカライゼーション
  • メッセージの翻訳

の2種類があります。どちらもメッセージの出力をする際に fmt パッケージでなく、 message.Printer を使うようにするところがポイントです。

フォーマットのローカライゼーション

これはドキュメントのサンプルにあるとおりで、プリセット(CLDR, Common Locale Data Repositoryに準拠)で用意されているフォーマットを利用して、数字の3桁区切りやその区切り文字、通貨記号の取扱も可能です。*2

これらはパッケージの目的として golang.org/x/text 以下にあるデータフォーマットにはすべて対応するという目標があるようです。(まだやってない)

import (
    "golang.org/x/text/currency"
    "golang.org/x/text/language"
    "golang.org/x/text/message"
)

func localization() {
    jp := language.Japanese
    p := message.NewPrinter(jp)
    cur, _ := currency.FromTag(jp)
    // クリスマスには¥ 120000のiPadを買いました。(フォーマット引数が%dなのに通貨記号が入っているのがミソ。しかしカンマ区切りが反映されない)
    p.Printf("クリスマスには%dのiPadを買いました。\n", currency.NarrowSymbol(cur.Amount(120000.0)))
    // お年玉は10,000円あげるつもりです。(カンマ区切りが反映されている)
    p.Printf("お年玉は%d円あげるつもりです。\n", 10000)
}

メッセージの翻訳

正直ローカライゼーションのほうはまだまだ改善の余地ありという感じですが、一番良く使われるのはこちらのメッセージの翻訳機能の方でしょう。こちらは使い方が単純です。

ベースとなるフォーマット文字列をキーとして、各言語ごとに翻訳版のフォーマット文字列を指定するという形です。(message.Setmessage.SetString を使う) 指定した文字列は golang.org/x/text/message/catalog#Catalog に追加されていくだけので、アプリケーションなどで使う場合には独自のカタログを作っておくと良いでしょう。(例: エラーログメッセージなど)

import (
    "golang.org/x/text/language"
    "golang.org/x/text/message"
)

func init() {
    message.SetString(language.Japanese, "%d days to the new year day.\n",
        "新年まであと%d日\n")
    message.SetString(language.Japanese, "%s, I wish you a happy new year.\n",
        "%s、良いお年を\n")
    // フォーマット引数の順番を入れ替える場合には"[]"を使って指定する
    message.SetString(language.Japanese, "%s, %s\n", "%[2]s%[1]s\n")
}

func translation() {
    p := message.NewPrinter(language.Japanese)
    local, _ := time.LoadLocation("Local")
    nyd := time.Date(2019, 1, 1, 0, 0, 0, 0, local)
    days := nyd.Sub(time.Now()) / (time.Hour * 24)
    // 新年まであと6日
    p.Printf("%d days to the new year day.\n", days)
    // みなさん、良いお年を
    p.Printf("%s, I wish you a happy new year.\n", "みなさん")
    // 世界、こんにちは
    p.Printf("%s, %s\n", "こんにちは", "世界")
}

ただこちらもドキュメントにフォーマット引数の順番に関する記述がなかったり、plural.Selectf の動作がいまいち怪しかったりと、まだまだ改善の余地ありな状況なので、もし本番に使うなら message.SetString に限るなど限定的な使用方法が良いかもしれません。逆にContributeチャンスでもありますね。

おわりに

個人的な感想として golang.org/x/text 以下は需要の割に作りが甘い感じがしていて、今回も調べるにあたってつまずく部分がいくつかありました。このあたりのパッケージは技術的な困難さという部分よりも、テストケースをいかに作るかという部分が大きいと思いますので、これを機会にこのパッケージを使ってGo製アプリケーションの日本語化をどんどん行って、どんどんIssue登録がされると良いなと思いました。

今年も1年お疲れ様でした。Gopherのみなさん、良いお年を。

*1:ところで、オーバーフローしたGoのアドベントカレンダーが ”Go 2〜" "Go 3〜" となってるのに違和感を感じたのは僕だけでしょうか。"Go〜 その2” とかならわかるけど、最初 "Go 2〜" を見たときに、Go 2 に関するアドベントカレンダーかと思いました。

*2:ただ、このエントリを書いていてところどころ挙動が怪しいところを見つけてしまったので、これはcontributeチャンスですね。