2024/04/18 22:04 : NanoPi NEOでFreeBSDする
freebsd@generic:~ % uname -a
FreeBSD generic 14.0-STABLE FreeBSD 14.0-STABLE #0 stable/14-n267114-72c3d91294c4: Thu Apr 4 08:28:31 UTC 2024 root@releng1.nyi.freebsd.org:/usr/obj/usr/src/arm.armv7/sys/GENERIC arm
適当なマシンで、以下のとおり準備する。うっかりSTABLEを使ったが、まあ、なんでもいいだろう。
# pkg install u-boot-nanopi_neo-2024.01
# dd if=FreeBSD-14.0-STABLE-arm-armv7-GENERICSD-20240404-72c3d91294c4-267114.img.xz of=/dev/da0 bs=524288
# dd if=/usr/local/share/u-boot/u-boot-nanopi_neo/u-boot-sunxi-with-spl.bin of=/dev/da0 conv=notrunc,sync bs=1k seek=8
このイメージはデフォルトでSSHが有効で、ユーザ名freebsd
/パスワードfreebsd
でログインできる。さすがにrootユーザでのSSHログインはできないので、suとroot
ユーザの初期パスワードroot
を使ってrootになる。
あとはパスワードを変更して(忘れたままインターネットに接続するようなアホはいないな?)、pkgでやりたい放題すればよい。
WARNING: 32-bit kernels are deprecated and may be removed in FreeBSD 15.0.
とのことなので、15.0になったら64bitにしようね。
2024/04/14 20:04 : FreeBSDでOMRON UPSを監視する
OMRONのUPSを使っているが、長年監視を放置していた。いい加減監視したいと思って設定したので、記録を残しておく。まず、OpenBSD/NetBSDな人は以下を参照のこと。
本記事は上記を参考に、FreeBSD 14.0向けに設定してみた記録になる。
- nutをインストールする
- UPSのVendor IDとProduct IDを調べる
- nutユーザがデバイスを使えるようにdevdを設定する
- nutを設定する(※本稿では全部は扱わない)
諸事情あってproduct IDはすべて伏せている。参考にして設定する奇特な人は、XXXXのような部分は適宜読み替えてほしい。
# pkg install nut
UPS関係だから名前にUPSが入ってるだろうと思って pkg search ups
しても出てこない。nutという名前は覚えて帰ろう。
UPSのVendor IDとProduct IDを調べる
なんでもいいので調べる。ここでは dmesg
で ugenX.X
を調べたあと、usbconfig
の dump_device_desc
を使って調べた。
# usbconfig -d ugenX.X dump_device_desc
ugen0.3: at usbus0, cfg=0 md=HOST spd=LOW (1.5Mbps) pwr=ON (100mA)
(...snip...)
idVendor = 0x0590
idProduct = 0xXXXX
nutユーザがデバイスを使えるようにdevdを設定する
nutはnut
ユーザで動作するので、通常 root:wheel
な /dev/ugenX.X
(の指す /dev/usb/X.X
) を触れない。devd
を設定して、OMRON UPSだったらnutユーザにプレゼントしよう。設定後は service devd restart
をお忘れなく。
/usr/local/etc/devd/nut-usb-omron.conf:
notify 100 {
match "system" "USB";
match "subsystem" "INTERFACE";
match "type" "ATTACH";
match "vendor" "0x0590";
match "product" "0xXXXX";
action "chgrp nut /dev/$cdev; chmod g+rw /dev/$cdev";
};
/usr/local/etc/devd/nut-usb.conf
を参考に書いたが、subsystemはDEVICEではなくINTERFACEになるようだ。うまくいかないなら devd -d
を実行して確認するといい。
こんな感じ。driverは参考資料では blazer_usb
だが、maintenance-onlyだよ、というので nutdrv_qx
に変えた。
/usr/local/etc/nut/ups.conf
:
[XXXXX]
driver = nutdrv_qx
port = auto
desc = "OMRON XXXXX"
vendorid = 0590
productid = XXXX
subdriver = ippon
通信テストは、以下のようにしてドライバを直接実行すればいい。read: (...
のような出力が定期的に出ればOK。
# /usr/local/libexec/nut/nutdrv_qx -DDDD -a XXXXX
あとはUPSがどうなったらどうしたい、の話になるので、man nut.conf
でも実行してnutと仲良くなるといい。
私はドライバ出力を食ってprometheusに出力したかっただけなので、nut自体は使っていない。
2024/02/02 00:02 : gitlab-runnerにrefusing to work with credential missing host fieldと言われたら
GitLabでCIしていたら特定のホストで動いたときだけ fatal: refusing to work with credential missing host field
というエラーが発生した。あまり情報も整理していないのでメモ程度だが、残さないよりマシと思って書き残すことにした。
結論:
- GitLabにHTTPSでアクセスしている、かつ、HTTPSでクライアント証明書の提示を要求していて、gitlab-runner側に使う証明書と鍵を設定している。(この時点でたぶん、大多数の人は当て嵌まらないハズ。)
- クライアント側証明書に使われている署名のハッシュアルゴリズムがSHA-1になっていて、opensslに蹴られていた。
とはいえ、opensslがなんらかの理由でイヤと言えば全部このエラーになってしまう気がするので、調べ方も重要であろう。
gitlab-runnerはいろんな環境変数やらなんやらを設定してgitを叩くので、手動で実行しても同じ問題は再現しづらい。そこで、gitlab-runnerに細工をして実行することにする。具体的には、以下を実施した。
- 環境変数を設定した状態でgitlab-runnerを起動する。
GIT_TRACE=1
, GIT_CURL_VERBOSE=1
あたりが特に有効。詳しくは Git-Internals-Environment-Variables を読まれたし。 - runnerが作る一時ファイルはすぐに消されてしまうものがあるので、必要ならshell scriptなどで書いた偽のgitを作り、PATHを通しておく。
今回だと、GIT_TRACE=1
と GIT_CURL_VERBOSE=1
を設定することで、GitLab側のJob出力で以下を得た。
16:23:14.638471 http.c:845 == Info: could not load PEM client certificate from /var/tmp/gitlab_runner/builds/(中略)/ほげほげ.tmp/CI_SERVER_TLS_CERT_FILE, OpenSSL error error:0A00018E:S
このファイルを調べようとしたが、普段は存在しない。実行中だけ存在するものと考えて偽gitにファイルコピーを仕込んだ結果、この証明書はgitlab-runnerに設定しているものと同じと分かった。また、opensslのエラーコードから、これはどうやらmd too weakというエラーらしいことが分かり、ハッシュアルゴリズムを疑った。(実際、このエラーはFreeBSD 14なシステム上のgitlab-runnerだけで起きていて、13では起きなかった。opensslのバージョン違いによる差であったのだろう。)
謝辞: manを読んでおきながら読み飛ばして GIT_CURL_VERBOSE
に気づいてなかったとき、教えていただいたzunda氏に感謝します。
余談: 今回の問題発生時は最初にGitLabサーバのHTTPSポートに接続してTCP handshakeを行い、直後にgitlab-runner側が切断するという不可解な動作をしていた。どうやら証明書のチェックはTCP connect後にやっているらしい。いずれにせよ、切断(先にFIN送出)しているのがgitlab-runner側なので、gitlab-runner側の問題であることは明白だったのだが、少々混乱もした。
2023/09/18 12:09 : FreeBSDでOpenObserveクラスタ構築
elasticsearchとlogstash(とJava)に嫌気が差したので、MinIO+OpenObserveに引っ越した。
- minioは
go install github.com/minio/minio@latest
でさくっと入る。 - OpenObserveのクラスタ構成のためにetcdが必要。こいつはpkgがあるので楽ちん。
- OpenObserveはソースを拾ってきてビルドする。rust-nightlyが必要。WebUIのコードはFreeBSDでサポートされないCypressが含まれているので、package.jsonをいじって外す。
- 各サーバに全コンポーネントをつっこむなら簡単。そうでない場合はetcd/minioをhaproxyとかで生きているサービスに飛ばすこと。
go install github.com/minio/minio@latest
go install github.com/minio/mc@latest
jail上で起動すると、root disk判定が誤判定するので、 MINIO_ROOTDISK_THRESHOLD_SIZE=1
など環境変数を設定しておく。もちろん保存先を間違えると大変な目に遭うので注意。
pkg install coreos-etcd34
で入る。ちなみにrc scriptがないので、自分で daemon
を呼ぶ。
一応残しておくと、試した設定はこんな感じ。各ホストで調整が必要なURLが多いので注意する。quorumを満たしていないと起動しないことと、一度起動できちゃうと無視される設定があるので注意する。変だなと思ったら rm -rf /var/db/etcd
して起動しなおすほうが早い。
name: openobserve1
data-dir: "/var/db/etcd/data"
wal-dir: "/var/db/etcd/wal"
strict-reconfig-check: true
initial-cluster-state: new
#initial-cluster-state: existing
initial-cluster-token: openobserve-etcd
initial-cluster: "openobserve1=http://10.0.0.1:2380,openobserve2=http://10.0.0.2:2380"
initial-advertise-peer-urls: "http://10.0.0.1:2380"
listen-peer-urls: "http://10.0.0.1:2380"
advertise-client-urls: "http://10.0.0.1:2379"
listen-client-urls: "http://10.0.0.1:2379,http://127.0.0.1:2379"
ビルドはサーバプロセス(rust)とWebUI(node.js)の2本立て。それぞれビルドする。
サーバ側はrustup.rsを使ってrustのnightlyを入れておく。pkg install rust-nightly
でも良さそうだが、どうもいつもcargoがコケるのでrustupを使っている。
rust(とcargo)を入れたらサーバをビルドする。そのままだとsimdjsonのビルドが失敗するので、.cargo/config.toml
に以下を書き加える。
[target.x86_64-unknown-freebsd]
rustflags = ["-C", "target-feature=+sse2,+ssse3,+sse4.1,+sse4.2"]
書き加えたら、cargo build --release
でビルドする。
WebUIはnodeとnpmでビルドする。ちなみにpkg install node npm
したものだが、node18-18.16.0とnpm-node18-9.7.2になっている。まずはビルドに必要がなく、FreeBSDがunsupportedなCypressをpackage.jsonからもぎ取っておく。diffだとこんな感じ:
*** web/package.json.orig Mon Sep 11 08:34:13 2023
--- web/package.json Mon Sep 11 08:37:21 2023
***************
*** 14,21 ****
"copy": "cd dist && mkdir src && cd src && mkdir assets && cd .. && cd .. && cp -r src/assets/* dist/src/assets/",
"test:unit": "vitest --environment jsdom --root src/",
"test:unit:coverage": "vitest --coverage",
- "test:e2e": "start-server-and-test preview :4173 'cypress run --e2e'",
- "test:e2e:dev": "start-server-and-test 'vite dev --port 4173' :4173 'cypress open --e2e'",
"build-only": "vite build",
"build-only-alpha1": "vite build --mode=alpha1",
"build-only-cloud-prod": "vite build --mode=production",
--- 14,19 ----
***************
*** 52,59 ****
"vuex": "^4.0.2"
},
"devDependencies": {
- "@cypress/vue": "^4.0.0",
- "@cypress/webpack-dev-server": "^2.0.0",
"@quasar/vite-plugin": "^1.0.10",
"@rushstack/eslint-patch": "^1.1.0",
"@types/d3-hierarchy": "^3.1.2",
--- 50,55 ----
***************
*** 71,82 ****
"@vue/test-utils": "^2.0.2",
"@vue/tsconfig": "^0.1.3",
"c8": "^7.11.3",
- "cypress": "^10.3.0",
- "cypress-localstorage-commands": "^2.2.1",
"dotenv": "^16.0.3",
"eslint": "^8.5.0",
"eslint-config-prettier": "^8.5.0",
- "eslint-plugin-cypress": "^2.12.1",
"eslint-plugin-vue": "^9.5.1",
"fs-extra": "^11.1.1",
"happy-dom": "^6.0.4",
--- 67,75 ----
変更したら、npm i
、npm run build
でビルドする。distに成果物が入るので、ここをnginxなどで公開する。(後述)
それぞれビルドしたら起動する。WebUI側はstatic contentsなので、nginxなどでserveする。nginx.confはこんな感じだろうか:
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name openobserve.coco.local;
location / {
proxy_pass http://127.0.0.1:5080;
}
location /assets {
alias /openobserve/web/dist/assets;
index index.html;
}
location /web {
alias /openobserve/web/dist;
index index.html;
}
}
}
ZO_LOCAL_MODE="false"
ZO_LOCAL_MODE_STORAGE="s3"
ZO_GRPC_ORG_HEADER_KEY="cluster"
ZO_TELEMETRY="false"
ZO_PROMETHEUS_ENABLED="true"
ZO_ETCD_ADDR="http://127.0.0.1:2379"
ZO_ROOT_USER_EMAIL="user@localhost.local"
ZO_ROOT_USER_PASSWORD="super-complex-password"
ZO_S3_ACCESS_KEY=""
ZO_S3_SECRET_KEY=""
ZO_S3_REGION_NAME="any"
ZO_S3_BUCKET_NAME="openobserve"
ZO_S3_SERVER_URL="http://127.0.0.1:9000"
ZO_S3_PROVIDER="minio"
これらをexportしたら引数なしでopenobserveを起動すればよい。
ところでOpenObserveのクラスタ構成はガイドがなくてやや不親切。ただ、k8s用helm chartがあるので、こいつを見ながら設定していた。
OpenObserveではまったところ
ちなみにZO_GRPC_ORG_HEADER_KEYの値を設定し忘れて検索するとエラーになる。これが非常に分かりづらい。openobserveには以下のログが出ていた。
[2023-09-18T01:38:46Z INFO openobserve::service::search] service:search:enter; org_id="default" stream_type=Logs
[2023-09-18T01:38:46Z INFO openobserve::service::search] service:search:cluster; org_id="default"
[2023-09-18T01:38:46Z INFO openobserve::service::search::sql] service:search:sql:new; org_id="default"
[2023-09-18T01:38:46Z INFO openobserve::service::search] get_file_list; stream_type=Logs time_level=Unset org_id="default" stream_name="default"
[2023-09-18T01:38:46Z INFO openobserve::service::search] search->file_list: time_range: Some((1694914712941000, 1695001112941000)), num: 114, offset: 39
[2023-09-18T01:38:46Z INFO openobserve::service::search] service:search:cluster:grpc_search; org_id="default"
[2023-09-18T01:38:46Z INFO openobserve::service::search] service:search:cluster:grpc_search; org_id="default"
[2023-09-18T01:38:46Z INFO openobserve::service::search] service:search:cluster:grpc_search; org_id="default"
[2023-09-18T01:38:46Z INFO openobserve::handler::grpc::request::search] grpc:search:enter; org_id="default"
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc] service:search:grpc:search; org_id="default"
[2023-09-18T01:38:46Z INFO openobserve::service::search::sql] service:search:sql:new; org_id="default"
[2023-09-18T01:38:46Z ERROR openobserve::service::search] search->grpc: node: 2, search err: Status { code: Unauthenticated, message: "No valid auth token", metadata: MetadataMap { headers: {"c
ontent-type": "application/grpc", "date": "Mon, 18 Sep 2023 01:38:46 GMT"} }, source: None }
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc] service:search:grpc:in_wal; org_id="default" stream_name="default" stream_type=Logs
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc] service:search:grpc:in_storage; org_id="default" stream_name="default" stream_type=Logs
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc::storage] service:search:grpc:storage:enter; org_id="default" stream_name="default"
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc::storage] search->storage: org default, stream default, load file_list num 39
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc::storage] search->storage: org default, stream default, load files 39, scan_size 17390808, compressed_size 1598007
[2023-09-18T01:38:46Z INFO tracing::span] service:search:grpc:storage:cache_parquet_files;
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc::wal] service:search:wal:enter; org_id="default" stream_name="default"
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc::wal] service:search:grpc:wal:get_file_list; org_id="default" stream_name="default"
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc::wal] wal->search: load files 2, scan_size 34366
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc::wal] service:search:grpc:wal:datafusion; org_id="default" stream_name="default" stream_type=Logs
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc::storage] search->storage: org default, stream default, load files 39, into memory cache done
[2023-09-18T01:38:46Z INFO openobserve::service::search::grpc::storage] service:search:grpc:storage:datafusion; org_id="default" stream_name="default" stream_type=Logs
[2023-09-18T01:38:46Z INFO tracing::span] datafusion::storage::memory::list;
[2023-09-18T01:38:46Z INFO openobserve::service::search::datafusion::exec] Query took 0.008 seconds.
[2023-09-18T01:38:46Z INFO openobserve::service::search::datafusion::exec] Query all took 0.009 seconds.
[2023-09-18T01:38:46Z INFO openobserve::service::search::datafusion::exec] Query took 0.011 seconds.
[2023-09-18T01:38:46Z INFO openobserve::service::search::datafusion::exec] Query all took 0.011 seconds.
[2023-09-18T01:38:46Z INFO openobserve::service::search] search->grpc: result node: 3, is_querier: true, total: 0, took: 31, files: 36, scan_size: 22
[2023-09-18T01:38:46Z INFO openobserve::service::search] search->grpc: result node: 1, is_querier: true, total: 0, took: 41, files: 41, scan_size: 16
[2023-09-18T01:38:46Z ERROR openobserve::handler::http::request::search] search error: ErrorCode(ServerInternalError("search node error"))
[2023-09-18T01:38:46Z INFO actix_web::middleware::logger] 10.33.3.28 "POST /api/default/_search?type=logs HTTP/1.0" 500 94 "141" "http://openobserve1.local/web/logstreams/stream-explore?s
tream_name=default&stream_type=logs&org_identifier=default" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:109.0) Gecko/20100101 Firefox/117.0" 0.148536
[2023-09-18T01:38:46Z ERROR openobserve::service::search] search->grpc: node: 2, search err: Status { code: Unauthenticated, message: "No valid auth token", metadata: MetadataMap { headers: {"c
ontent-type": "application/grpc", "date": "Mon, 18 Sep 2023 01:38:46 GMT"} }, source: None }
[2023-09-18T01:38:46Z ERROR openobserve::handler::http::request::search] search error: ErrorCode(ServerInternalError("search node error"))
openobserver-2で検索を要求した場合、openobserve-1側に以下のようなログが残った。
[2023-09-18T01:39:50Z INFO openobserve::handler::grpc::auth] Err authenticating Invalid value provided for the HTTP Authorization header