気軽に楽しくプログラムと遊ぶ

自分が興味があってためになるかもって思う情報を提供しています。

OpenTelemetryとは

OpenTelemetryとは

近年、様々な状況に対して強く、柔軟なシステムが求められ、その中で重要な要素として「Observability」(可観測性:オブザーバビリティ)が求められています。

OpenTelemetryは、Observabilityにおいて必要不可欠なシステムの状態を示すさまざまなシグナル(ログやCPU、メモリなどのメトリクス)の収集や送信を標準化する、Cloud Native Computing Foundation(CNCF)プロジェクトです。

Observabilityを実現するためにシステムの状態を可視化するためのシグナルである「テレメトリー」が用いられます。

テレメトリー

テレメトリーの要素としては、ログ、トレース、メトリクスが重要。
加えてプロファイル、ダンプも含めたものを「Primary Signals」と呼んでいる。

  • ログ
    • アプリやサーバで発生している個別のイベント(エラーログ、アクセスログなど)
  • トレース
  • メトリクス
    • サーバのリソース状況(CPU使用率など)やサービス状況(レイテンシ、トランザクション量、エラーレートなど)といった、特定の時間間隔で測定された数値データ

参考URL

報連相の改善について

考えるキッカケ

以下のようなことを思って、少し今の報連相の課題について考えてみる

  • 伝えることの漏れ、一度で伝わらないなどがある
  • 相手の立場に立って、相手のことを考えてわかりやすい言葉を使う、事前情報を伝えるなどが必要と感じた
  • 効率的な会話で時間を生み出し、より大きな貢献や成果を生み出したい

課題と改善

  • 伝えることの漏れ、一度で伝わらないなどがある
    • 伝えることリストアップ、結論、詳細と頭の中で整理
    • 一呼吸で伝える。子供に話すように伝える
    • 具体的な用語を扱い、単語はいつも同じ単語を使って理解をシンプルにする
  • 相手の立場に立って、相手のことを考えてわかりやすい言葉を使う、事前情報を伝えるなどが必要と感じた
    • 背景や前提を伝える
    • どこのなんの話かを冒頭伝えて、話を聞く土壌を作る

進捗報告の型

話種類 → タイトル → 前提 → [結論]聞きたいこと → [詳細]やってみたこと、対応案

わかりやすくなる伝え方

  • 伝えることが何点あるか
  • これから伝えることの方向性を示す
    • 端的に言うと
  • 話の確実性を示す
    • 検討段階ですが。 ざっと見た感じ
  • 話を論理的に分ける
    • 〜視点では
      • 運用目線では。開発目線では。ユーザ目線では。
    • 不具合対応
      • 暫定対応。恒久対応

伝えることのイメージを作っておく

  • 頭の中で概念や伝えることのイメージを描いておいてその流れで話す
  • ヌケモレを防ぎ、相手にもイメージしてもらいやすくなる

これは避けたほうがいい

  • 主語なしで話す
  • 話のタイトルなしで、詳細から話し始める
  • 同じことを複数の用語を使って話す
  • 思いつきで話を追加で話し始める
    • 一つ一つ話を完結する。話題がズレると何がいいたかったかわかりずらくなる

gRPCとは

gRPCの概要

  • RPC (Remote Procedure Call) を実現するためにGoogleが開発したプロトコルの1つ
  • IDL(インターフェース定義言語)を使ってあらかじめAPI仕様を .proto ファイルとして定義し、.protoファイルからサーバー側&クライアント側に必要なソースコードのひな形を生成できる
  • 言語に依存せず、一つの.protoファイルで12言語の実装を生成できる(Go, Java, Python, C, Ruby等など。主要なプログラミング言語はだいたい網羅されてる!)
  • SSL通信がデフォルトで適応されており、安全性が高い
  • スケーラビリティが高い (数百万のリクエストを並列でさばける)

マイクロサービスのAPIを用いる規模が大きく、サービス連携があるシステムでよく利用されている

  • モダン、通信が早くて低遅延、データ送信が効率的
  • 異なる言語間での通信を簡単にできる

gRPCはREST APIよりも高速な通信が可能

HTTP/2とProtocol Buffersを使っているので高速通信が可能とのこと。

HTTP/2の利用

HTTP/2を用いていることで4つのAPIタイプを作成でき、様々な種類/用途のAPIを開発できる

Protocol Buffers(Protobuf)の利用

構造化されたデータのシリアライズ方式の一種だそう。
プロセス内部の簡素なデータ構造をシリアライズするためにスキーマ定義用のドメイン固有言語があり、これが優秀なのでよく利用される。

以下のような形式らしい。

syntax = "proto3";
package example.protobuf;

message SimpleMessage {
    message HeaderItem {
        string name = 1;
        string value = 2;
    }
    enum Type {
        START = 0;
        BLOB = 1;
        END = 2;
    }

    uint64 id = 1;
    Type message_type = 2;
    repeated HeaderItem headers = 3;
    bytes blob = 4; 
}

Protocol Buffersの採用頻度が上がっている理由

  • シンプルで可読性が高い
  • プログラミング言語から独立で、任意のデータを表現できるわけではないが十分に適用可能範囲が広い
  • すべてのニーズを満たそうとして仕様が複雑化していないため、実装間の意図せぬ非互換性で苦しめられることが少ない
  • 周辺ツールを簡単に開発して不足仕様を自分で補える

buf

概要:protoファイルを扱うライブラリ

できること

  • protoファイルのチェック:lint、フォーマット
  • BSRへの登録/更新
    • BSR(Buf Schema Registry)
      • Protobuf(Repository)とプラグインレジストリ
      • プラグインはBSRに登録されていないことがあります。例えば、protoc-gen-validateのRepositoryはありますが、2022年1月現在プラグインは提供されていないので、プラグインが実行可能なDockerfileを自分で用意、BSRに登録し、使えるようにする必要があり

設定ファイル

  • buf.yaml:モジュールの定義。名前、依存関係、lint や breaking の設定の定義
  • buf mod init で作成
  • buf.work.yaml:高度なローカル開発機能であるワークスペースの定義

    コマンド

Lint & Format

buf lint
buf format -w

protoを別リポジトリで運用する場合

  • vendor配下にsparse checkout した他チームの proto配置
  • これにより同レポジトリでレビューも可能
  • parse checkout

その他

小耳に挟んだ特徴

  • APIが手で叩けない。
  • デバッグは、armeria使うと良い?
  • 設定ファイルのprotoが重要
  • nullが書けないので、nullをどう扱うかを検討する必要あり

参考URL

DB分割(シャーディング)時のMySQLのID採番について

ID採番方法

  • ✗ auto_increment
    • idが重複する問題があり
  • ◯ sequence tableの作成
  • ◯ UUIDカラムの追加
    • ◯ ULID(時系列な並びのID)
    • ◯ UUIDv6, UUIDv7, UUIDv8も時系列順にソート可能

Seaquence

データが大規模になると、シーケンス発行がボトルネックなりすい

UUIDを利用する

メリット

  • 個別ノードが自律的にキー発行可能
    • 冪等性(べきとうせい)を担保可能
    • キーを事前決定できると、ロジックシンプル化・キーの意味付け排除が可能
  • セキュリティ向上
    • 利用者が次のキーを予測しにくい特性あり

デメリット

  • InnoDB のテーブルはクラスタインデックス構造のため、インデックス全体のランダム位置に読み込みが必要
  • 1レコード INSERT のたびに、もしヒットしなければストレージに対するI/Oが発生

UUIDv6, UUIDv7, UUIDv8

  • UUIDv6, UUIDv7, UUIDv8は、時系列順にソート可能
    • テーブルのキーとして使えば、ソートしなくても順番にならび、書き込む際も順々に書き込めて、データアクセスが局所的になる

参考URL

Kotlin Coroutines 非同期処理

非同期処理で用いるコレクション Sequence

val numberSeq = (1..3).asSequence()
val resultSeq = numberSeq
    .map { println("1st map: $it"); it + 1 }
    .map { println("2nd map: $it"); it + 2 }
    .toList()
println(resultSeq)

Sequenceは、遅延リストのためtoList()が処理される段階で処理が出力される。 mapの処理がいくら呼ばれてもprintlnは実行されず、toListが呼ばれたときにはじめて実行される
以下のような出力を行う。

1st map: 1
2nd map: 2
1st map: 2
2nd map: 3
1st map: 3
2nd map: 4
[4, 5, 6]

一つ目のmapの一つ目の要素、2つ目のmapの一つ目の要素という ように処理が進んでいく。
Sequenceでなく、Listだと、一つ目のmapの全要素の出力後に2つ目のmapの要素が表示される

非同期に計算される複数の値を返却する Flowを使った実装例

flowOnで、Coroutinesコンテキストを割り当てて、新たなスレッドで処理を開始する。 bufferで、前処理結果をためつつ、並列して重めの処理を捌いていく。

findHistory(param) // flowを返却するメソッド
            .flowOn(Dispatchers.IO)
            .mapNotNull { history ->
                runCatching {
                    service.getSampleInfo(history.Id)
                }.fold(
                    onSuccess = {
                        history to it
                    },
                    onFailure = {
                        log.error("error")
                        null
                    },
                )
            }
            .filter { (history,  _) -> // 条件に合致しないものをフィルタする
                when {
                    history.isA -> {
                        false
                    }
                    history.isB -> {
                        false
                    }
                   else -> true
                }
            }
            .buffer() // 上記処理をバッファしつつ、下記処理と並列実行
            .map { (history, order, shop) ->
                       // 重めの処理
                    },
                )
            }
            .toList()

その他 非同期処理での留意点

  • withContext
    • コルーチン内のコンテキスト(Dispachers.IOに切り替える)
    • 時間のかかるブロッキング処理を内部で処理する。
    • コルーチンのsuspendメソッドなどノンブロッキング処理になっている場合は、この中に書く必要はない
  • スレッドローカルに保存するライブラリに注意

参考URL

Terraform moduleの構造と使い方

moduleとは?

インフラ構成管理ツールのTerraformで共通処理を記述するファイル群

モジュールを呼び出すコード

module "リソースの名前" {
    source = "モジュールを定義したフォルダのパス"
    パラメーター名 = "パラメーター値"
}

moduleの構造

  • 入力:variable
  • 本体:resource
  • 出力:output

実際のファイルは以下

sample.tf

variable "content" {
}

variable "filename" {
}

resource "local_file" "helloworld" {
  content  = var.content
  filename = var.filename
}

output "debug_print" {
  value = "${var.content} to ${var.filename}"
}

パラメータを利用する

resource内の変数 content、filenameは、入力パラメータとしてvariableに定義したものを設定している。
moduleを呼び出す親ファイルmain.tfなどで、この入力パラメータを設定する。

resource "local_file" "helloworld" {
  content  = var.content
  filename = var.filename
}

moduleの結果を出力する

contentに指定された内容をfilenameのファイルに出力する

output "debug_print" {
  value = "${var.content} to ${var.filename}"
}

terraformの実行

以下で構成ファイルを実行、実際の環境に適用することが可能

terraform plan
terraform apply

参考URL

モジュールの使い方 - Terraformのきほんと応用