connect-go with opentelemetry

お久しぶりです。計装してますか?

突然connect-goで計装したくなったので実装調べたんですがまったくまともなリファレンスがひっかからなかったのでなんとか実装しました。

下記はこのリポジトリの解説記事となります

github.com

 

opentelemetryインフラの準備

コレクターとかエクスポーターとかめっちゃめんどい事前準備が必要だなーめんどくさいなーとか思っていたんですが、HyperDXさんからall-in-oneパッケージが提供されているのでこれに甘えることとします。

www.hyperdx.io

 

https://hub.docker.com/r/hyperdx/hyperdx-all-in-one

 

ドキュメントにhyperdxのパッケージを使う例が多いですが、こちらとしてはhyperdxをローカルで使うと決めたものの最後まで使うかは決めていないので、なるべくopentelemetryの標準パッケージを使うという前提で進めています

 

初期化

import (
	"context"
	"log"
	"log/slog"
	"os"

	"connectrpc.com/connect"
	"go.opentelemetry.io/otel"
	"go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc"
	"go.opentelemetry.io/otel/propagation"
	"go.opentelemetry.io/otel/sdk/resource"
	"go.opentelemetry.io/otel/sdk/trace"
	semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
)

var tracer = otel.Tracer("your-tracer-name")
var grpcHost = "hyperdx:4317
func InitOTLP() (*trace.TracerProvider, error) {
	ctx := context.Background()
	authorizationKey := os.Getenv("OTEL_EXPORTER_AUTHORIZATION_KEY")
	exporter, err := otlptracegrpc.New(
		ctx,
		otlptracegrpc.WithInsecure(), // Use WithTLSCredentials for secure connections
		otlptracegrpc.WithEndpoint(grpcHost),
		otlptracegrpc.WithHeaders(map[string]string{"authorization": authorizationKey}),
	)

	resources, err := resource.New(
		ctx,
		resource.WithProcessPID(),
		resource.WithHost(),
		resource.WithAttributes(
			semconv.ServiceName("acme_service"),
			semconv.ServiceVersion("vX.Y.Z"),
			semconv.DeploymentEnvironment("dev"),
		),
	)

	tp := trace.NewTracerProvider(
		trace.WithBatcher(exporter),
		trace.WithResource(resources),
	)
	otel.SetTracerProvider(tp)
	otel.SetTextMapPropagator(propagation.TraceContext{})
	return tp, nil
}

こんな感じのコードになります。exporterはgrpcのものをメインにしていますがここをほかのプロトコルに変えることもできるのでドキュメントを参照ください

OTEL_EXPORTER_AUTHORIZATION_KEYですが、hyperdxを立ち上げると簡易的なチュートリアルが表示されており、その中でAPI tokenが発行されています。それをdocker composeで指定したり env ファイルを作って入れ込むなどをしてください

 

interceptor

connect-goではリクエストをフックする関数としてinterceptorに登録することで自動的に呼び出すことができるようなので、こんなかんじのコードを初期化後に実行することで対応できます

 

import (
	"connectrpc.com/otelconnect"
	"connectrpc.com/validate"

	"connectrpc.com/connect"
)
func NewAPMTraceInterceptor(logger *slog.Logger) connect.UnaryInterceptorFunc {
	return func(next connect.UnaryFunc) connect.UnaryFunc {
		return func(ctx context.Context, req connect.AnyRequest) (connect.AnyResponse, error) {
			ctx, span := tracer.Start(ctx, "request:"+(req.Spec().Procedure))
			res, err := next(ctx, req)
			if span != nil {
				defer span.End()
			}
			return res, err
		}
	}
}
//register interceptors
otelInterceptor, err := otelconnect.NewInterceptor()
otelAPMInterceptor := NewAPMTraceInterceptor(logger)
interceptors := connect.WithInterceptors(
	otelInterceptor,
	otelAPMInterceptor,
	validate.NewInterceptor(),
)

docker composeでhyperdxの画面を開くとデータが流れているのが確認できるでしょう

それではよい計装ライフを

ChatGPTに関心した話

お久しぶりです。

猫も杓子もGPTですね。

自分も例に漏れず使っているわけですが関心したことがあったのでまとめてみようかなと思いました

動機

pythonを最近よく書くのである程度詳しい認識はあるのですが、

dictを加算しつつ結合するようなメソッドが欲しいと思いました。

dictを結合する場合 | 演算子update() メソッドを用いることがよく知られていますが、それらはこのように動作します

In [2]: _d1 = {1:2, 2:3}

In [3]: _d1.update({3:4})

In [4]: _d1
Out[4]: {1: 2, 2: 3, 3: 4}

In [5]: {1:2, 2:3} | {3:4}
Out[5]: {1: 2, 2: 3, 3: 4}

これらはキーが重複していない状態では想定通りの挙動をしますが、キーがかぶっているときには安全側に倒すような挙動となります(エラーなどでずに後ろの値で上書き)

キーが重複していたときに加算してほしいなあと思ったのですがそういった実装はないのでChatGPTに聞いてみることにしました

聞いてみた

解説

だいたいコメントを読んでもらうと想定内のことが書いてありますね

なんで copy() が入っているのかというと、primitive typeを除いてpythonでは引数に参照渡しでくるのでそのまま copy() せずに代入すると引数で渡したp1の値が関数の外で想定外の値になってしまうからです

だいたいのケースでcopyせずに問題なく動くと思いますがあるとなしでは安全さが違う

これらを受けてこういうコードになった

T = TypeVar("T")

def merge_dict(src: dict[T, int], *args: Any) -> dict[T, int]:
    dest = src.copy()
    for arg in args:
        for k, v in arg.items():
            if k in dest:
                dest[k] += v
            else:
                dest[k] = v
    return dest

こういう感じにすることで

merge_dict({1: 2, 3:4}, {3:5}, {1: 8})

みたいな書き方でもちゃんと結合させることが可能

感想

  • 日本語的に若干ツッコミどころがあるのにちゃんと出力するGPTすごいぞ
    • 言語モデルがすごい。
    • AIが翻訳者の仕事を奪うとかネットの記事で書いてるのが信じられる出来
  • ていうかエラーなく動くコードがでてえらい
    • copyするのえらすぎる。これ意識して書けるのは明らかに「pythonちゃんと書いてる」やつ

multi memcached in upstart

やりたいこと

check

$ ps -ax | grep memcached
 8501 ?        Ssl    0:00 /usr/bin/memcached -m 64 -p 11211 -u memcache -l 127.0.0.1

これを21211でも起動したい

やり方をぐるる

/etc/init.d/memcachedにヒントがあるとのこと

# Usage:
# cp /etc/memcached.conf /etc/memcached_server1.conf
# cp /etc/memcached.conf /etc/memcached_server2.conf
# start all instances:
# /etc/init.d/memcached start
# start one instance:
# /etc/init.d/memcached start server1
# stop all instances:
# /etc/init.d/memcached stop
# stop one instance:
# /etc/init.d/memcached stop server1
# There is no "status" command.

設定をコピー

$ sudo cp /etc/memcached.conf /etc/memcached_server1.conf

コピーしたほうのconfを編集してポートを21211に変更

で、起動しなかった

ぐぐったらupstartだしね無理だよねという話に遭遇。

stackoverflow.com

なのでsystemctlでそれっぽく動きそうなリファレンスを捜索

これがヒット

yomon.hatenablog.com

これベースでやるとよさそう

service をコピー

$ cd /lib/systemd/system
$ sudo cp memcached.service memcached.21211.service

コピーした方を新しい設定ファイルを読みに行くように修正する

起動

$ sudo systemctl start memcached.21211

$ ps -ax | grep memcached
 8501 ?        Ssl    0:00 /usr/bin/memcached -m 64 -p 11211 -u memcache -l 127.0.0.1
10125 ?        Ssl    0:00 /usr/bin/memcached -m 64 -p 21211 -u memcache -l 127.0.0.1

めでたしめでたし

x in alpine linux

TL, DR;

  • 成功しませんでした

やりたいこと

ref

XFCE Setup - Alpine Linux

ここの通りに入れると結構すんなり進む。そらマニュアルなんだからそうだという話はさておき、

$ Xorg -configure

がこけるはずだ Xorg.0.logを読むとなんかわかるんじゃないかなという話なのでエラーっぽい単語でぐぐる

Bug #3691: Xorg fails to launch due to module symbols not found - Alpine Linux - Alpine Linux Development

Yes, Xorg -configure will disable the /etc/X11/xorg.conf.d/ which has the config for manually loading the modules in proper order.

The problem is that musl libc does not support lazy loading. I don't have any good ideas how to properly solve this.

Does it work if you skip the Xorg -configure step and just do startxfce4?

えっ叩いちゃだめなのこれ!!!? ということでstartxfce4を直接たたく

f:id:rane-hs:20160426123606p:plain うーん真っ黒画面だ

再度Xorg.0.logを見るとvboxなんちゃらというのが出るのでvirtualbox用アドオンを入れようとする

# apk add virtualbox-additions-grsec
# apk add virtualbox-guest-modules-grsec

うん?両方こけるぞ?と思ってパッケージ検索を実行

Alpine packages

Branchがedgeになっているので読めるようにしておきます。 /etc/apk/repositoriessudo vim

grsecまわりが必要そうなのでlinux-grsecもadd

$ cat /etc/apk/repositories
#/media/cdrom/apks
http://dl-3.alpinelinux.org/alpine/v3.3/main
http://dl-3.alpinelinux.org/alpine/v3.3/community
http://dl-3.alpinelinux.org/alpine/edge/main
http://dl-3.alpinelinux.org/alpine/edge/community
http://dl-3.alpinelinux.org/alpine/edge/testing

で、

# apk del virtualbox-additions-grsec
# apk del virtualbox-guest-modules-grsec
# apk add linux-grsec
# apk add virtualbox-additions-grsec
# apk add virtualbox-guest-modules-grsec

からの再起動して、startxfce4

。。。。同じ画面じゃないか

続く(かも)

マウスカーソルが出てる時点でおそらく半分くらい目的は達成されているはずなので、なぜxfceの画面が立ち上がらないかを調べたほうがよさそうだ

メモ書き

やりたいこと

tools

電灯

www.amazon.co.jp

  • とくに理由はないけどUSBだと結線がイメージしやすそうという理由によりチョイス
  • (外で置くには何かしらのガワが必要になるのでそれはおいおい)

被覆材

www.monotaro.com

  • このへんから適当に

コントローラ

akizukidenshi.com

  • Cではない(これはC++。Lazurite ってのがあってこれも省電力を売りにしてたんだけどCしか使えなさそうなのでやめました)
  • 省電力
  • 安い

という点からチョイス

給電

www.amazon.co.jp

  • どうやって使おうかというイメージがまだできてない

docker & alpine

いまdocker熱いですね。

どうもそれとセットでalpine linuxってのが熱いらしいです

何がいいのそれっていうと、コンテナサイズが劇的に軽くなるということのようです

くわしくはこのあたりに:

Docker、オフィシャルイメージのOSをAlpine Linuxへ切り替える計画が明らかに。OSの軽量化に傾倒するDocker - Publickey

で、alpineベースでmemcachedとredisのコンテナがあるんですけど両方くっついたコンテナの方がほしくなったので、dockerの勉強がてら作ってみました

github.com

使い方は

$ git clone git@github.com:rane-hs/memcached-and-redis.docker.git
$ cd memcached-and-redis.docker
$ docker build -t memcached-redis-alpine .
$ docker run --restart always --name mcdocker -i -t -d memcached-redis-alpine
$ docker inspect mcdocker | grep IPAddr   # コンテナのIPを入手

で、↑で入手したアドレスにログインします

$ telnet 123.45.67.89 11211   # telnet
$ redis-cli 123.45.67.89      # redis

docker commands

使ったコマンドたち。

以下は↑のリンクをgit cloneしてきて1個中に入った状態で叩かれていることを想定しています

build container

docker build -t memcached-redis-alpine .
  • memcached-redis-alpine という名前でコンテナイメージを作ります

run container

docker run --restart always --name mcdocker -i -t -d memcached-redis-alpine
  • mcdocker という名前でコンテナを走らせます。イメージは↑で作った名前です

check running container

docker ps
  • いまローカルにあるイメージの一覧はdocker images
  • 両方 -aがoptionalで使えます

remove container (-f : force)

docker rm -f mcdocker
  • イメージ削除はrmi

remove unused container image (1liner)

docker rmi -f $(sudo docker images -a | awk '/^<none>/ { print $3 }')
  • 全部きえるわけではないです

check container setting

docker inspect mcdocker

run container shell

docker exec -it mcdocker /bin/ash
  • attachdocker runしたスクリプトにアタッチしちゃうので、Ctrl-Cとかexitしちゃうとコンテナごと閉じます。
  • ここではexecを使ってシェルを立ち上げています。alpine linuxではbashがないのでashとしましたがubuntuではbashでよさそう

お役立ちリンク

qiita.com

Docker pull issue (ApplyLayer exit status 1 stdout: stderr: chmod /bin/mount: permission denied) | Alpine Linux forums

  • alpine linuxdocker pull mysqlできないのは上記を見て対応してください

speakerdeck.com

  • dockerオススメTIPS15選。
  • sudo dockerプレフィックスをdockerにする方法もあります