2025年3月11日火曜日

OpenTelemetry のトレース箇所を独自で設定する方法

OpenTelemetry のトレース箇所を独自で設定する方法

概要

前回 OpenTelemetry を Flask アプリに適用する方法を紹介しました
前回の方法はトレースやメトリックス情報を自動で送信してくれる設定です
今回は手動でトレース情報を送信する方法を紹介します

環境

  • macOS 15.3.1
  • Python 3.12.9
    • flask 3.1.0
    • open-telemetry 0.51b0
  • OpenTelemetry Collector 0.121.0
  • Jaeger (all in one) 1.67.0

app.py

自動でトレースする設定は外し自分でトレースしたい箇所に設定します
基本的には tracer.start_as_current_span でトレースしたい箇所にスパンを作成するだけで OK です

以下の設定では2箇所でスパンを作成しています

from random import randint
from flask import Flask

from opentelemetry import trace

# Acquire a tracer
tracer = trace.get_tracer("diceroller.tracer")

app = Flask(__name__)

@app.route("/rolldice")
def roll_dice():
    username = get_username()
    return str(roll(username))

def get_username():
    with tracer.start_as_current_span("get_username") as rollspan:
        username = "hawksnowlog"
        rollspan.set_attribute("username", username)
        return username

def roll(username: str):
    # This creates a new span that's the child of the current one
    with tracer.start_as_current_span("roll") as rollspan:
        res = randint(1, 6)
        rollspan.set_attribute("roll.value", res)
        return res

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

各種起動

アプリ、Jaeger、コレクターを起動します

  • export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true && pipenv run opentelemetry-instrument --logs_exporter otlp --service_name dice-server python app.py
  • docker run --rm --name jaeger -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 -p 16686:16686 -p 14317:4317 -p 14318:4318 -p 14250:14250 -p 14268:14268 -p 14269:14269 -p 9411:9411 jaegertracing/all-in-one:latest
  • docker run -p 4317:4317 -v $(pwd)/otel-collector-config.yaml:/etc/otel-collector-config.yaml otel/opentelemetry-collector:latest --config=/etc/otel-collector-config.yaml

動作確認

localhost:8080/rolldice にアクセスし Jaeger で確認しましょう
以下のように1つのリクエストに対して2つのスパンが作成されていれば OK です

最後に

OpenTelemetry を使って独自のトレース情報を設定する方法を紹介しました
自動の場合はスパンなどが細かく設定できないのでプロダクションでは独自設定を使うことになると思います

参考サイト

2025年3月10日月曜日

OpenTelemetry を使って flask アプリのトレースをしてみる

OpenTelemetry を使って flask アプリのトレースをしてみる

概要

アプリのリクエストがどこ、どのくらいの時間をかけてを通ったかなど可視化することができます

環境

  • macOS 15.3.1
  • Python 3.12.9
    • flask 3.1.0
    • open-telemetry 0.51b0
  • OpenTelemetry Collector 0.121.0
  • Jaeger (all in one) 1.67.0

各種インストール

  • pipenv install flask
  • pipenv install open-telemetry

app.py

from random import randint
from flask import Flask, request
import logging

app = Flask(__name__)
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


@app.route("/rolldice")
def roll_dice():
    player = request.args.get('player', default=None, type=str)
    result = str(roll())
    if player:
        logger.warning("%s is rolling the dice: %s", player, result)
    else:
        logger.warning("Anonymous player is rolling the dice: %s", result)
    return result


def roll():
    return randint(1, 6)


if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

OpenTelemetry 用のログを自動で出力するように設定する

作成したアプリに自動でログを出力してくれるようにします

  • pipenv run opentelemetry-bootstrap -a install

これで再度アプリを起動します
OpenTelemetry 用のログが自動で出力されるようになります

  • export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true && pipenv run opentelemetry-instrument --traces_exporter console --metrics_exporter console --logs_exporter console --service_name dice-server python app.py

これで起動し localhost:8080/rolldice にアクセスするとログが OpenTelemetry 形式になっているが確認できると思いますの

今回はすべてのログを自動で OpenTelemetry 形式にしているので flask の起動ログも OpenTelemetry 形式になっています

コレクターの起動

標準出力に吐かれているログをコレクターに送ることで可視化できます
まずはコレクターを起動します
コレクターは docker イメージが用意されているのでそれを使います

otel-collector-config.yaml

receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
exporters:
  # NOTE: Prior to v0.86.0 use `logging` instead of `debug`.
  debug:
    verbosity: detailed
processors:
  batch:
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [debug]
      processors: [batch]
    metrics:
      receivers: [otlp]
      exporters: [debug]
      processors: [batch]
    logs:
      receivers: [otlp]
      exporters: [debug]
      processors: [batch]

コンテナ起動

  • docker run -p 4317:4317 -v $(pwd)/otel-collector-config.yaml:/etc/otel-collector-config.yaml otel/opentelemetry-collector:latest --config=/etc/otel-collector-config.yaml

これで localhost:4317 にアクセスするとコレクタの画面が確認できます

ログをコレクターに送る

あとは先程標準出力に吐かれていた OpenTelemetry のログを起動したコレクターのエンドポイントに向けます

コレクターに送るためのプラグインが必要になるのでインストールします

  • pipenv install opentelemetry-exporter-otlp

そして再度アプリを OpenTelemetry ログ付きで起動します
console 時にくらべコマンドは短くなっています

  • export OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED=true && pipenv run opentelemetry-instrument --logs_exporter otlp --service_name dice-server python app.py

すると今度はアプリ側ではなくコレクター側に OpenTelemetry の情報が表示されるのが確認できると思います

トレース情報を Jaeger で可視化する

OpenTelemetry Collector は集めたデータを更に別の場所にエクスポートすることができます
トレースデータを Jaeger にエクスポートして WebUI で確認できるようにしてみます

まずは Jaeger を起動します

  • docker run --rm --name jaeger -e COLLECTOR_ZIPKIN_HOST_PORT=:9411 -p 16686:16686 -p 14317:4317 -p 14318:4318 -p 14250:14250 -p 14268:14268 -p 14269:14269 -p 9411:9411 jaegertracing/all-in-one:latest

いろいろポートを開いていますが今回必要なのは OTLP 用の 4317 (ホスト側は 14317) と WebUI 用の 16686 になります
4317 と 4318 はすでにコレクターで使っているので同一ホストで動作させる場合は別ポートをしていします

次に OpenTelemetry Collector の設定ファイルを編集して exporters を追加します

  • vim otel-collector-config.yaml
receivers:
  otlp:
    protocols:
      grpc:
        endpoint: 0.0.0.0:4317
      http:
        endpoint: 0.0.0.0:4318
exporters:
  debug:
    verbosity: detailed
  otlp/jaeger:
    endpoint: 192.168.1.88:14317
    tls:
      insecure: true
processors:
  batch:
service:
  pipelines:
    traces:
      receivers: [otlp]
      exporters: [otlp/jaeger]
      processors: [batch]
    metrics:
      receivers: [otlp]
      exporters: [debug]
      processors: [batch]
    logs:
      receivers: [otlp]
      exporters: [debug]
      processors: [batch]

追加したのは exporters の部分です
ここに起動した Jaeger の OTLP ポートを指定しています
コレクターはコンテナで動いているので localhost ではなく Jaeger コンテナが動作しているホストの IP を指定しましょう
そして service.pipelines.traces.exporters を debug から otlp/jaeger に変更します

そしてコレクターを再起動しましょう

動作確認

Flask アプリ、OpenTelemetry Collector、Jaeger をそれぞれ起動し動作確認します
すべて起動できたら localhost:8080/rolldice にアクセスしましょう
そしてトレース情報を Jaeger で確認するため localhost:16686 にアクセスしてみましょう

今回追加した dice-server のトレースが見れるようになっていれば OK です

最後に

Flask アプリに OpenTelemetry を導入してトレース情報を Jaeger で可視化してみました
OpenTelemetry でトレースを可視化する流れは理解できたかなと思います

直接 OpenTelemetry 情報を Jaeger に送る方法はどうやら廃止されるらしく今後はコレクターを経由して Jaeger など別の可視化アプリなどに連携するのが主流になるようです

今回はすべてのログに自動で OpenTelemetry 化する方法を採用しましたがプロダクションなどであれば必要な箇所でスパンなどを定義して送信する必要があります
そのあたりの個別のスパンの作成方法なども機会があれば紹介したいと思います

参考サイト

2025年3月9日日曜日

Flask + distroless 超入門

Flask + distroless 超入門

概要

Flask を distroless 上で動かしてみました
ビルドは 3.12 ですが distroless の python は 3.11.2 なのでそこのバージョンは合わせたほうがいいかもしれません

環境

app.py

from flask import Flask

app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello, Distroless with Pipenv!"

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

Pipfile 作成

  • pipenv install flask

Dockerfile

# ビルド用のステージ
FROM python:3.12.9-slim-bookworm AS builder

WORKDIR /app

# Pipenv をインストール
RUN pip install --no-cache-dir pipenv

# Pipfile と Pipfile.lock をコピーしてインストール
COPY Pipfile Pipfile.lock ./
RUN PIPENV_VENV_IN_PROJECT=1 pipenv install --deploy --ignore-pipfile

# Flask アプリをコピー
COPY app.py .

# Distroless を使用する実行環境
FROM gcr.io/distroless/python3

WORKDIR /app

# Pipenv でインストールした仮想環境のライブラリをコピー
COPY --from=builder /app/.venv /app/.venv
COPY --from=builder /app /app

# 環境変数を設定して Python が `.venv` を認識できるようにする
ENV PYTHONUNBUFFERED=1 \
    PIPENV_VENV_IN_PROJECT=1 \
    PYTHONPATH="/app/.venv/lib/python3.12/site-packages"

# Flask アプリを起動
CMD ["app.py"]

ビルド

  • docker build -t flask-distroless-pipenv .

起動

  • docker run --rm -p 8080:8080 flask-distroless-pipenv

動作確認

  • curl localhost:8080
Hello, Distroless with Pipenv!
  • docker images
REPOSITORY                  TAG       IMAGE ID       CREATED         SIZE
flask-distroless-pipenv     latest    9adef9f7e0d0   4 minutes ago   86.2MB

最後に

ビルドしたイメージは軽くなりますがシェルがないのでトラシュは大変そうです
そもそもビルド時には distroless なイメージは使えないのでビルド時のイメージを管理しなければいけないので結局ディストリビューションは必要かもしれません
また distroless で提供している Python のバージョンが 3.11.2 とかなり古いのでそこも微妙な感じはします

参考サイト

2025年3月7日金曜日

Google Cloud Associate Cloud Engineer 合格メモ

Google Cloud Associate Cloud Engineer 合格メモ

概要

勉強方法などを紹介しておきます
基本は過去ゲーです

環境

  • GCP ACE (2025/01/27時点)
  • テストセンターにて受験

料金

  • 137ドル (19702円)

対策

基本的には教科書と過去問です
ただ教科書だけでは合格するのはほぼ不可能だと思います (実務経験がかなりあれば別かも)

過去問を完璧にこなせば基本的には大丈夫かなと思います
教科書にも模擬試験的なのがあるのでお守り程度に持っておくといいかなと思います
過去問の問題も実際の試験で30-50%ほど出題された気がします (もう少しあったかも)
なお問題はすべて選択式で4択から1つ選択するだけでした
複数回答を求める問題は出題されませんでした

教科書

模擬試験

Udemy で 400 問ほど解きました
すべて有料なのでご注意ください
むしろ有料以外の模擬試験はほぼなくあっても微妙な問題集しかなかったです

試験範囲

一応確認しておくといいかもです

https://services.google.com/fh/files/misc/associate_cloud_engineer_exam_guide_japanese.pdf

解き方のポイント

  • 回答から問題が何関するものなのか先に推測する
    • 問題はダラダラ長いが最終的に聞きたいことはただの権限の問題だったりする
  • 消去法
    • 知らない問題でもあり得ない回答がわかればそこから2択などに絞り込む
  • 「実務ではこうする」の考えは完全に消したほうがいい
    • その方法じゃなくてもできるんだけどなー
    • そっちのほうが実際は面倒なんだよなー
    • と言った実務経験は完全にすてたほうがいいです
    • Google Cloud 推奨およびベストプラクティスを探す問題がほとんどなので注意しましょう
    • 実務経験が長い人ほど陥りやすいかもです
  • 定番の問題はおさえる
    • クラウドストレージのライフサイクル
    • ロールの最小原則とグループ割り当て
    • VPC のサブネット内の CIDR の扱い

実際に出題された問題の記憶

  • 過去問(および過去問に似た問題)が30-50%ほど出題された
  • 全く勉強していなかった部分はほぼなかった
    • UDP + ネットワークロードバランサは知らなかったが消去法で対処した
  • クラウドストレージに Cloud Interconnect 経由で接続する方法
    • 内部の DNS に CNAME 貼るやつ
  • サブネットの CIDR の IP レンジ拡張
    • ネットワーク部分の数字を減らして拡張するので間違えそう
  • 日本語がおかしい
    • Coldline -> コールドストレージ
    • それ以外にも問題文章自体の翻訳がおかしい部分が多々ある
  • 時間はかなりあまる
    • 自分は60分で終了

スコアレポートはでない

受験直後でも受験後の certmetrics の履歴でも確認することはできませんでした

暫定合格を確認する方法もない

ネットで調べると受験直後の画面に「暫定合格」なる文言が表示されるらしいのですが自分のときはありませんでした
最新の受験だとなくなったんだと思います
また受験直後に webassessor を確認しても完了した試験の項目がなくそこでも確認できませでした

なので現状だと受験直後に合否を確認する手段はありません

合否通知はいつくるか

自分は翌日の朝6:00にメールで合格通知が来ていました
なのでそんなに焦って合否を確認しなくても大丈夫かなと思います

最後に

スコアレポートが出ないのがかなり不満でした
また模擬試験が有料しかほぼないので個人でかつ実費で受験しなければいけない人には正直向かない試験です

2025年3月5日水曜日

(できなかったメモ) Mac で azw to pdf する

(できなかったメモ) Mac で azw to pdf する

概要

azw9.res + kfx 形式のファイルは以下の方法ではできなかったので一応メモしておきます
azw8 + kfx 形式であれば一応いけました
おそらく azw8 形式は自分で作成した pdf をKindleライブラリに追加した本なので変換できるのだと思います

kindle にダウンロードしたファイルを pdf に復号化する方法を紹介します
calibre を使う方法を紹介します

環境

  • macOS 15.3.1
  • calibre 7.26.0

インストール

  • brew install calibre

起動

アプリケーションの一覧にある calibre を起動します
本のタイプを選択する部分があるので Kindle を選択します

KFX input プラグインの追加

  • 環境設定 -> calibre を拡張するプラグイン取得 -> KFX input を検索してインストール

DeDRM プラグインの追加

これはプラグインの一覧にないので手動でインストールします

https://github.com/noDRM/DeDRM_tools/releases/tag/v10.0.3

zip をダウンロードしたら解凍します
DeDRM_plugin.zip というファイルがあれば OK です

  • 環境設定 -> 高度な設定 -> プラグイン -> ファイルからプラグインを読み込む

で先程の zip ファイルを指定します
インストールできたら calibre を再起動します

本を追加する

kindle でダウンロードした kfx ファイルを追加します
本を追加から kindle でダウンロードした本があるディレクトリにある kfx ファイルを選択します

pdf へ変換

本を変換で書き出します
右上に出力形式があるので PDF を選択しましょう (azw9.res 形式の場合ここで DRM を復号化できずにエラーになります)

変換が成功するとディスクに書き出しができるようになるので「ディスクに保存」で保存するパスを選択すれば OK です

最後に

古い kindle 形式であればこの方法でもいけるっぽいですが最新の kindle 形式 (azw9.res) などはこの方法だとダメなようです

2025年3月3日月曜日

mineoからNuroモバイルVMプランに変更したのでメモ

mineoからNuroモバイルVMプランに変更したのでメモ

概要

流れを簡単にメモしておきます
事務手数料で4000円ほど取られるのでそこは注意しましょう

環境

  • mineo (Dプラン、5G、音声通話あり)
  • Nuroモバイル (VMプラン、D回線、音声通話あり)
  • iPhone14

mineoでMNP予約番号を発行

https://support.mineo.jp/notes_cancel.html の「MNP予約番号発行」から発行します

Nuroモバイルの登録

https://mobile.nuro.jp/ 公式ページの「お申し込み」から申し込みます
キャンペーンなどの専用ページがある場合はそちらから必ず申し込みましょう
でないとキャンペーンが適用になりません

登録に必要になる情報は以下です

  • MNP予約番号
  • 携帯の番号
  • 身分証明書のデータ

あとは自分の好きなプランが決まっていれば OK です
SIM のサイズなども選択する画面があるのでそこは自身の端末に合わせて選択してください
iPhone14 の場合は nanoSIM です

NuroモバイルのSIM到着待ち

自分は登録から2日で届きました

Nuroモバイル切り替え

基本は説明書が入っているのでそれに従うだけですが Wifi 環境必須なのでそこだけ注意しましょう

ログイン

https://mobile.nuro.jp/r/use/mnp にログインします
ログインパスワードは郵送されてきた SIM カードに記載されているのでそれを使ってログインします
初回ログイン時はデフォルトのパスワードを変更するように促されるので変更します

MNPによる回線切替日の予約

ログインできたら「料金、お知らせ」タブから開通予約希望日時を選択します
日時は現在日時から選択できるのですぐに切り替えたい場合には最短を選択すれば OK です
(選択できる日時に深夜がなかったような気がするのでそこはご注意ください)

SIM カード入れ替え

iPhone の SIM カードを入れ替えます

NuroモバイルのAPNインストール

https://mobile.nuro.jp/support/settings/apn 基本的な流れはここにあるのでここから APN をダウンロードしインストールすれば OK です
ポイントは以下の通りです

  • 上記URLのスクショが古いの注意
  • 先に mineo のプロファイルを削除しておく必要がある
  • NuroモバイルのAPNインストール後は再起動はせずにフライトモードのオンオフだけでも OK

動作確認

Wifi をオフにしてモバイル回線だけで通信できることを確認しましょう
https://www.ugtop.com/spill.shtml などにアクセスしてゲートウェイが ap.nuro.jp になっているか確認してもいいかなと思います
また mineo 側にログインすると契約情報が消えていることが確認できます (そのうちログインもできなくなるのだろうか、卒業証書なるものがメールで届いたら eoID 自体を削除して問題なさそう)

5Gオプションの有効化

「ご契約オプション」のタブから5Gオプションを有効化できるので有効にしましょう
これを有効にしないと mineo 同様電話を受け取ることができない可能性があります

最後に

mineo から Nuro モバイルに切り替える方法を紹介しました
マイネ王側にアカウントがある方はそちらも忘れずに退会しておきましょう

2025年3月2日日曜日

error: Eager macro-expansion failure: (error "Shortdoc f function ‘f-older-p’: bad keyword ‘:noeval’")

error: Eager macro-expansion failure: (error "Shortdoc f function ‘f-older-p’: bad keyword ‘:noeval’")

概要

emacs をアップデートした場合に出ることがあるようです

環境

  • macOS 15.3.1
  • emacs 30.1

対策

package を最新にしましょう

  • M-x package-list-packages
  • 大文字 U ですべてを更新対象にする
  • x を押して更新を実行

最後に

emacs 自体の更新は Homebrew で管理できますが emacs にインストールしているパッケージは自分で定期的に更新する必要があるので注意しましょう