Fluent Bit のメトリックを Mackerel に送る

Fluent Bit では以下のようにHTTP_Serverを有効にすることで内部のメトリックをHTTP経由で /api/v1/metrics というパスからJSON形式で取得できるようになります。*1

[SERVICE]
  HTTP_Server       On
  HTTP_Listen       127.0.0.1
  HTTP_PORT         2020

これで各プラグインで今までに処理したレコード数やバイト数が取得できるわけですが、実はこのJSONはmackerel-plugin-jsonでそのまま扱える形式になっています。

下はoutputのcloudwatch_logsプラグインに対してのメトリックを送信する例です。

$ mackerel-plugin-json -prefix='fluent-bit' -url='http://127.0.0.1:2020/api/v1/metrics' -diff='.?' -include='output.cloudwatch_logs'
fluent-bit.output.cloudwatch_logs.1.retries 0.000000    1653008277
fluent-bit.output.cloudwatch_logs.1.retries_failed  0.000000    1653008277
fluent-bit.output.cloudwatch_logs.1.dropped_records 0.000000    1653008277
fluent-bit.output.cloudwatch_logs.1.retried_records 0.000000    1653008277
fluent-bit.output.cloudwatch_logs.1.proc_records    0.000000    1653008277
fluent-bit.output.cloudwatch_logs.1.proc_bytes  0.000000    1653008277
fluent-bit.output.cloudwatch_logs.1.errors  0.000000    1653008277

このようにdiffオプションを使ってFluent Bitで1分間に処理したログに関するメトリックをMackerelで確認することができるようになります。

ユースケースとしては、Fluent Bit経由でCloudWatch Logsにエラーログを送っているけどエラーの増加はMackerelで通知したいという時にわざわざCloudWatchから集計しなくても良くなるというのがあります。

ちなみにmackerel-container-agentのpluginsイメージにはmackerel-plugin-jsonが最初から同梱されているので、HTTP_Serverを有効にすればあとはすぐに取得できるようになります。

参考

htnosm.hatenablog.com

cohalz.co

distrolessのnonrootイメージを使おう

この記事は はてなエンジニア Advent Calendar 2021 11日目の記事です。

コンテナのベースイメージとしてdistrolessを選択肢にするということがここ最近増えてきました。

そんなdistrolessを非rootユーザで使おうとしたらとても簡単だったのでその紹介です。

どのくらい簡単かというと、Goのアプリケーションであれば以下のように変えるだけで対応できます。(コメントアウト部分は元々のrootユーザで動かしていた場合のもの)

FROM golang:1.17 as builder

WORKDIR /go/src
COPY go.mod go.sum .
RUN go mod download
COPY . .
RUN go build -o /out/myapp .

# FROM gcr.io/distroless/static:latest
FROM gcr.io/distroless/static:nonroot

# COPY --from=builder /out/myapp /usr/local/bin/
COPY --from=builder --chown=nonroot:nonroot /out/myapp /usr/local/bin/

distrolessでは2019年にマージされた以下のプルリクエストで非rootユーザである nonroot ユーザ及びそれを利用するための :nonroot というイメージタグが導入されました。

github.com

:nonroot のタグはstaticに限らずdistrolessの全イメージで使うことができ、簡単には :latest はrootユーザで :nonroot はnonrootユーザという区別がされています。

なので非rootユーザ対応は基本的には :nonroot にするだけで良いですが、マルチステージビルドの場合はbuilderステージで作成されたものはユーザを指定していない限りrootで作成されて実行できなくなってしまうのでCOPY時にchownオプション*1nonroot:nonroot を指定してあげる必要があります。

ちなみにnonrootというユーザは :nonroot イメージに限らずどのイメージでも作成されているのでDockerfileに USER nonroot と書くという方法でも冗長だけど一応実現は可能です。

cohalz@co ~ % docker run -it --rm gcr.io/distroless/static:debug
/ # cat /etc/passwd
root:x:0:0:root:/root:/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/sbin/nologin
nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin

ちなみにdistrolessはnonroot以外にもbusybox入りのdebugイメージやアーキテクチャも指定することができますが、全部使いたい場合は順番としてはdebug-nonroot-arm64 といった順番で指定するという感じになっています。

この :nonroot イメージは少しずつ利用が広がっており、google/koも2020年10月に出たv0.6.0からデフォルトではこのnonrootイメージを使うようになっています。

cloud.google.com

セキュリティ対策をちゃんとしようという流れがある中、コンテナイメージの非root化をまだ全然やれてない人は数行変えるだけで試せるdistrolessからやってみてはどうでしょうかという記事でした。

明日のアドベントカレンダーの担当は id:papix さんです。

Google Cloud Innovators Gym Japan に参加して Professional Cloud Developer に合格した #giggcp

Google Cloud Innovators Gym JapanというGoogle Cloud主催のトレーニングプログラムに第二期生として参加しました。

その結果Professional Cloud Developerに合格することができた*1のでそのレポートです。

cloud.google.com

トレーニングを受ける前の経験

  • Google Cloudの実務経験はなし
    • AWSの経験はあるけど資格は一つも取ってない
  • Kubernetesに関してもPod、ReplicaSet、Deployment、Serviceが何を指しているかはわかるくらいの知識くらいしかない状態

試験に向けて勉強したこと

基本的にはトレーニングプログラムで用意されたものを使って対策しました。

具体的にはpixivさんの記事にあるとおりで Google Cloudのエンジニアが行うセッション+ハンズオンと、Courseraのコースが無償で提供されるのでそれを進めていくという感じです。

inside.pixiv.blog

例えばこんなコースを受けていました。 www.coursera.org

Courseraに関しては動画を見る・Qwiklabsと連携したハンズオンを行う・テストを受けるのサイクルで結構効率よく学べました。

下の記事にもある通り、Qwiklabsのハンズオンが非常に良くできていて、実際にGoogle Cloudの環境を用意してハンズオンをすることができました。 コマンドを打つときも基本的にCloud ShellとCloud Shell Editorを使う手順になっているので手元の環境に依存することはないです。

dekotech.dekokun.info

その他では本を読むとかはしていなくて、Courseraのテストや模試で間違えたところはちゃんとドキュメントを見るというのをやったくらいです。

掛けた時間としてはセッション+ハンズオンとCourseraのコースを8つほど受けて、大体50時間弱くらいかなと思います。

スケジュールとしては6月に入ってからCourseraのコースをやり始めて6/24にAssociate Cloud Engineerに合格、7/6にProfessional Cloud Developerという感じです。

感想

普段業務で触れていないGoogle Cloudについて知る機会になってとても楽しかったです。

試験の内容自体もクラウドネイティブなアプリケーションの構築ができるかどうかを問われている問題も多く、腕試しという感じで楽しく受けられました。

せっかくなのでトレーニングプログラムの期間中にProfessional Cloud Architectも取得目指してみたいなと思いました。

Webサービスのモニタリングについてのチェックリスト

この記事は はてなエンジニア Advent Calendar 2020 11日目の記事です。

はじめに

運用しているWebサービスについて、より良いモニタリングにしていくために普段考えていることをチェックリスト形式でまとめてみました。

全部にチェック付いていないとダメというわけでもなく、Webサービスの性質によっては当てはまらないものもあると思うので参考程度に現状を把握するツールとして使ってください。

チェックリスト

疎通確認

  • それぞれのコンポーネントに対して疎通確認を行っている
  • localhostでなくネットワーク経由でのコンポーネントへの疎通確認を行っている
  • 疎通が取れなかったときに正しくアラートになることを確認している

メトリック

  • 可能な限りチェック監視ではなくメトリック経由での監視にしている
  • ビジネスロジックに関わる値をメトリックにしている

ログ

  • 利用しているログ配送システムの可用性や完全性について理解している
  • ログをメトリックにする際に詰まってしまった場合のことを考慮できている

アラート通知

  • 閾値の根拠をアラート通知から確認できるようにしている
  • アラート発生時にどうすれば良いのかをアラート通知から確認できるようにしている
  • SLOに関係するような重大なものとそうでないものでアラートのレベルを使い分けている
  • 通知の再送を設定している

HTTPリクエストの場合

  • 5xx系だけでなく4xx系もモニタリングしている
  • nginxを使っている場合、499とその他の4xx系を区別してモニタリングしている
  • レイテンシの平均及びパーセンタイル値をモニタリングしている
  • 以上の項目をエンドポイント単位でモニタリングしている

バッチの場合

  • バッチの実行に成功したかどうかをモニタリングしている
  • バッチの実行時間をモニタリングしている
  • 決まった時間に実行されているということを監視している

ジョブキューの場合

  • 正しい結果が返ってくる割合をモニタリングしている
  • 処理の遅延状況をモニタリングしている
  • ジョブキューに入ってきたジョブの数と処理したジョブの数をモニタリングしている

ダッシュボード

  • The Four Golden Signalsに従ってダッシュボードを構築している
  • 単なる数字ではなく割合を確認している
  • 1日前や7日前と比較した数値を確認している
  • リリースタイミングをダッシュボードから確認できるようにしている
  • 開発イテレーションの期間内だけでなく数ヶ月以上での傾向も確認している

開発メンバーとの振り返り

  • リリース・障害に伴って数字に変化は現れたか確認している
  • 監視項目について議論し見直しを行っている
  • 半期や四半期の単位で起きた障害について原因を分類できている

その他

  • サービスの構成図やリクエストのフロー図を用意している
  • 依存している他サービスがあれば、そのサービスが動かなかったときの影響を考慮できている

おわり

他にも色々あったような気がしますが、とりあえず思いついたものはこのあたりです。他にもあれば教えて下さい。

2020年におけるmackerel-plugin-jsonの使い方

この記事は Mackerel Advent Calendar 2020 2日目の記事です。

はじめに

皆さんはmackerel-plugin-jsonを使っていますか?

個人的にはかなり好きなプラグインなのですが、2017年に書かれたMackerel プラグインアドベントカレンダー以外ではあまり解説されていませんでした。

soudai.hatenablog.com

この記事よりも後に追加された機能を紹介している記事も見当たらなかったので、今回はそれらに絞って紹介していきます。

前回実行したときの結果との差分を投稿する-diffオプション

-diff オプションの後に正規表現を書くと、その正規表現にマッチしたメトリックに対して前回の実行時からの差分をMackerelに投稿することができます。

アプリケーション内に単調増加するカウンターとその値をJSONで返すエンドポイントを実装しておくことでプラグイン側が差分を計算し、例えば1分間でのリクエスト数などそれだけで簡単に取得できるようになります。

GoのアプリケーションであればカウンターとJSONで返すエンドポイントの実装を公式のexpvarパッケージを使うことで簡単に実現することができます。

このように書くことでアプリケーション側(今回はapp)で記録していたカウンターをJSONで返すことができます。

func Handler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    fmt.Fprintln(w, expvar.Get("app"))
}

今まではこういった差分を送信したい場合はアプリケーション側で差分管理をきちんと実装してあげる必要がありましたが、プラグイン側で差分を計算するようになったためにそういった必要はなくなりました。

特にステータスコードでは表現しにくいアプリケーション固有のメトリックはログに書いてそれを集計するという方法が一般的だと思いますが、ログはディスクの容量や配送経路などいくつか考える必要が出てくるため準備も少し大変でした。

そういった場合でログから集計する方法を取りづらい場合にも役に立つと思います。

上のブログでもこうしたカウンター値にする機能がないことが欠点として書かれていましたが、その欠点をほぼ解決する機能になっていると思います。

ただし注意点としてmackerel-plugin-jsonはそのままの値をグラフしますのでカウンタ-値に自分で変換したい場合はやはりpluginにする必要があります。

エンドポイントの監視 ~ mackerel-plugin-jsonを読み解く - そーだいなるらくがき帳

このオプションは2018/12/13にリリースされた v1.1.0 以降で使えます。

HTTP リクエストヘッダを指定できる-headerオプション

外形監視や check-http のプラグインと同様に-header オプションを指定することでHTTP リクエストヘッダを設定できるようになりました。

-header オプションは -header=User-Agent:mackerel-http-checker/0.0.1 -header=Cache-Control:no-cache のように複数指定することもできます。

このオプションの使い道の一つに、インターネット上から監視用のエンドポイントにmackerel-plugin-jsonでリクエストしたい場合があります。

今までは一般にアクセスをさせないようにするにはIPアドレスで制限するといったことが多かったと思います。

それがこのオプションによって、リクエストヘッダベースの制限も掛けやすくなると思います。

ではどんな時に良いでしょうか? 1つ目は直接自分の管理下に置いていないServer、つまりはレンタルサーバーなどです。 この場合はmackerel-agentのインストールが難しく、監視対象側から定期的にMackerelのAPIを叩くことが難しいでしょう。 そんな場合はモニタリング用のエンドポイントを作成してそこをmackerel-plugin-json経由で監視すればいいのです。

エンドポイントの監視 ~ mackerel-plugin-jsonを読み解く - そーだいなるらくがき帳

このオプションは2020/11/26にリリースされた v1.2.0 以降で使えます。

インストールの手間が改善されている

2017年の記事の当時では自分でビルドしてホストに配布する必要がありました。

mackerel-plugin-jsonのインストールですがmackerel-plugin-jsonは公式パッケージに同梱されていません。 そのため自分でbuildする必要があります。

エンドポイントの監視 ~ mackerel-plugin-jsonを読み解く - そーだいなるらくがき帳

しかし、現在はmkr plugin install mackerel-plugin-json というコマンドを実行するだけでインストールができるようになっているため、簡単に使い始めることができます。

また、2019/7以降にはmackerel-container-agentのプラグイン同梱版のイメージ(mackerel/mackerel-container-agent:plugins)にはデフォルトで同梱されるようになっています。

mackerel-plugin-json は公式プラグインとしては同梱されてはいないのですが、今回 mackerel-container-agent の同梱プラグインとして追加いたしました。

mackerel-container-agent のアップデートを行いました - Mackerel ブログ #mackerelio

おわりに

アプリケーション側で監視用のエンドポイントを持つというのはここ最近かなり一般的になってきたように感じます。

その際に今のmackerel-plugin-jsonでできることを知ることで、そういったエンドポイントをより良く活用できるようになるのではと思っています。

mrubyを触ってみた

最近ngx_mrubyを触っていて、RubyのTime#httpdateを使いたかったもののmrubyにはないということがあった。

そもそもmruby自体にも全然慣れていないという課題もあったので、このメソッドを実装することを目標にライブラリを作りながらmrubyに慣れるということをやってみた。

mrubyをビルドする

ビルド手順は非常に簡単で、mrubyのリポジトリをcloneしてmakeするだけで良い。

git clone https://github.com/mruby/mruby.git
cd mruby
make

bin/ ディレクトリ以下にいくつか実行ファイルが作られて試すことが出来て、irbのmruby版であるmirbというものも使うことが出来る。

~/c/s/g/m/mruby ❯❯❯ bin/mirb
mirb - Embeddable Interactive Ruby Shell

> p Time.now
2020-10-20 22:30:13 +0900
 => 2020-10-20 22:30:13 +0900
>

ライブラリの作り方

これで最低限mrubyを使う準備はできたので、次はライブラリを作っていく。

mrubyのライブラリはmrbgem(mgem)と呼ばれており、 mrbgem.rakemrblib/ など決まったファイル・ディレクトリ構造に従ってファイルを作成していくというものになっている。

~/c/s/g/c/mruby-time-library ❯❯❯ tree
.
├── LICENSE
├── README.md
├── mrbgem.rake
├── mrblib
│   └── mrb_time_library.rb
└── test
    └── mrb_time_library.rb

2 directories, 5 files

実装がある程度できたら動くかどうかテストをする準備をしていく。

テストの実行は少し特殊で、ライブラリのリポジトリではなくmrubyのリポジトリに以下のような build_config.rb というファイルを作成する。 conf.gemには作成しているライブラリのパスを指定する。

MRuby::Build.new do |conf|
  toolchain :gcc
  conf.gembox 'default'
  conf.gem '../mruby-time-library'
  conf.enable_test
end

そしてmrubyのリポジトリで rake test を実行することでライブラリのテストを走らせることが出来る。

そうすれば「ライブラリの実装とテストを修正し続けて rake test を実行する」というサイクルで開発をすすめることができる。

GitHub ActionsでCIする

手元環境の準備が整ったので、今度はリポジトリ上でCIできるようにする。

mrubyのライブラリはTravis CIを使っていることも多いけれど、せっかくなのでGitHub Actionsでやれるようにした。

CIでやる場合もmrubyのリポジトリでbuild_config.rbを使って指定するというのは変わらずだけど、手元のパスではなくGitHub上のリポジトリとブランチを指定するのでそこを工夫して書き換える必要がある。

ただし、ActionsのトリガーではブランチへのコミットとPRとでは作業ブランチの取得方法が微妙に異なるのでビルド前にいい感じにブランチ名を取っておくというのも必要。

      - name: get branch name
        if: github.event_name != 'pull_request'
        run: echo "BRANCH_NAME=$(echo ${GITHUB_REF#refs/heads/})" >> $GITHUB_ENV
      - name: get pull request branch name
        if: github.event_name == 'pull_request'
        run: echo "BRANCH_NAME=$(echo $GITHUB_HEAD_REF)" >> $GITHUB_ENV

この BRANCH_NAME を使ってbuild_config.rbを作ってrake testをしてやれば良い。

      - name: Rake test
        env:
          TEMPLATE: |
            MRuby::Build.new do |conf|
              toolchain :gcc
              enable_debug
              conf.enable_bintest
              conf.enable_test
              conf.gembox 'default'
              conf.gem :github => '${{ github.repository }}', :branch => '${BRANCH_NAME}'
            end
        run: |
          printf "${TEMPLATE}" > build_config.rb
          cd mruby
          env MRUBY_CONFIG=../build_config.rb rake test

そうして完成したワークフローは以下。

https://github.com/cohalz/mruby-time-library/blob/master/.github/workflows/test.yml

ライブラリを作った

以上で、httpdateを含むtimeライブラリの特定のフォーマットの文字列にするメソッド(インスタンスメソッド)を使えるようにするライブラリを作成できた。

github.com

~/c/s/g/m/mruby ❯❯❯ bin/mirb
mirb - Embeddable Interactive Ruby Shell

> Time.now.httpdate
 => "Tue, 20 Oct 2020 14:44:03 GMT"
>

文字列をパースするメソッドに関しては、mrubyにはデフォルトでは正規表現が使えないというのもあり実装を後回しにしているけど、既に欲しくなってきたので近いうちに作りたい。