Quantcast
Channel: りんけーじ
Viewing all 62 articles
Browse latest View live

Pandora FMSで遊ぶ(1)

$
0
0

Zabbixもいい加減古い。
UIも古臭いから、なにか試そうと思って見つけたPandora FMSをFreeBSD環境に入れてみた。
今回はインストールの話だけ。

FreeBSDにもpkgで簡単に入れられる。
portsだとダメだのなんだの、古い情報が山のように残ってるけど、今はpkgで一発。

# pkg install pandora-console pandora-server
(お茶を淹れてケーキを食べても終わらないインストールを待つ)
# pkg info | wc -l
     172

一発ではあるが、恐ろしい量の依存パッケージがあるので、テストしたい人は仮想マシンでも立てて試すことをお勧めする。(※1)
そして、一発でインストールはできるが動作はしない。
pkgでphp-fpmなモノを入れるのは初めてだったので知らないが、LoadModuleなどを手書きする必要がある。
jailで専用に作っているので、ほかのモジュールのことなど考えず、以下を作る。

/usr/local/etc/apache24/Includes/pandora_console_fpm.conf:

LoadModule proxy_module libexec/apache24/mod_proxy.so
LoadModule proxy_fcgi_module libexec/apache24/mod_proxy_fcgi.so

<LocationMatch ^/pandora_console/(.*\.php(/.*)?)$>
    ProxyPassMatch fcgi://10.0.0.2:9000/usr/local/www/pandora_console/$1
    DirectoryIndex /index.php index.php
</LocationMatch>

もちろん10.0.0.2:9000のところは、自分のIPアドレスにする。jailでないなら127.0.0.1でいい。

あとはrc.confを書いて、全部起こす。(もちろん再起動でもいいしservice startでもいい。)
pandora_serverはまだ設定が済んでいないので、起動に失敗するが気にしない。

# sysrc apache24_enable=YES
# sysrc php_fpm_enable=YES
# sysrc pandora_server_enable=YES

これでWebブラウザから http:///pandora_console/ で初期設定画面が見えるようになる。
あとは画面にしたがってmysqlmariadbサーバ(材料に含まない)のアドレスやユーザを入れてやれば、データベースを作ってくれる。

ちなみに、ここで使うユーザはmysqlのデータベースを作成して、GRANTを実行できるような非常に強い権限を持ったユーザが必要なことに注意する。
もっと言うと、mysqlのGRANT ALL PRIVILEGESはGRANT OPTIONを含まないので、pandoraのセットアップ用に一時ユーザを作る場合はこの点に注意しておかないといけない。
もっともっと言うと、GRANTが失敗した場合、初期設定画面のSTEP 6にまともなエラーが出ない。こんな風になる。

この画面を見ると「なにも失敗してないのに失敗してるの?」と思う。「全部緑になってるじゃないか」と。
これを書いた時点で気づくだろうが、右側が赤くなっているあいつが失敗を表している。
重要だからよく読め、ということではない。あのステップが失敗している。エラーメッセージなど書いていないが。

さて、途中でデータベースのパスワードが作られるので、それを /usr/local/etc/pandora_server/pandora_server.conf に書いてやる。
その後、mariadbサーバとpandorafmsのサーバのtimezoneを確認して、pandora_serverを起こす。

# tzsetup Asia/Tokyo
# service pandora_server start

これで動くようになった。あとは煮るなり焼くなりする。
セットアップに4日ほどかかったので、実際の運用はこれから。

※1 量が多いだけ?甘く見ないほうがいい。

# zfs list dryad_local/jail/pandorafms_1
NAME                          USED  AVAIL  REFER  MOUNTPOINT
dryad_local/jail/pandorafms_1 2.24G  11.2G  2.70G  /local/jail/pandorafms_1

このzfs datasetはFreeBSD 11.0-RELEASEに最小限のpkgを入れたものからcloneしている。(REFER-USED=0.56Gくらいがbaseの分であろう。)
なので、pandora-{console,server}と依存パッケージだけで2.24Gを食っている。


Pandora FMSで遊ぶ(2)

$
0
0

前回インストールしたPandora FMSで遊んでみた感想。

前回も書いたが、今はzabbixを使っている。その前はicinga、そのさらに前はnagiosだ。

zabbixの(nagios/icingaに対して)よいところは、とにかく設定がwebからできるところだと思っている。
逆に設定ファイルベースのほうがやりやすいこと(監視項目の自動生成とか)もあるが、その辺をカバーするようなAPI・機能があるのもよいところ。
そういった意味で、上位互換だと思って使ってきた。

一方で不満もあった。

  • UIが古臭い。ことあるごとに画面遷移する。
    特に問題なのがLatest Data(最新データ)の自動リロード。データ一覧を眺めている最中にリロードされて、スクロール位置が吹っ飛ぶ。(improve扱いで登録されているが、もはやbugであろう。)
  • 複数の計測値をまとめたグラフが一発で作れない。
    たとえば24portもある(しかない)スイッチの全ポートのtrafficをグラフにするのに、手動で24回も操作しないといけない。しかも、検索ボタンを押して、ポップアップウィンドウで検索して、また戻って、の操作だ。
  • Screen、Mapの機能が貧弱。あんなに使いづらくて貧弱なUIならXMLかなんかで書けたほうがいい。
  • PHP製である。ソースを読まなくて済むなら別に気にしないので、言いがかりに近い。(その点で、Zabbixは問題なかった)

設定が済んでしまえばあまり問題はないが、とにかく設定が面倒。
それでもNagios/Icingaと比較すれば、Webから設定できるだけマシに感じた。

ではPandora FMSはどうか。

  • UIはまあまあきれい。ただ、初期のWebアプリという”匂い”がして、モバイル端末には優しくない。
  • グラフの作成は、複数項目をリストから選んで一発で追加できる。同一ホスト内の複数監視項目、複数ホスト内の同一監視項目のどちらもできてよい。
  • MapやScreenの代わりはReportになるのだろうか。まだ良く分かってない。

さて、この辺まで試して嫌な感じがしてきた。実はほかの事情もあって、いったんここで評価を止めている。
ここまで見た感じでは、インストールに苦労する割りに、Zabbix比較で、運用で楽をできる気配が感じられない。
グラフの作成など便利になっているところはあるが、あって当然、zabbixに抜けている部分が埋まったに過ぎず、気が利いているというレベルの機能ではなかった。

というわけで、現状での最終結論として以下を得た。

zabbixで十分。UIはどうせ異常がなければ見ないからどうでもいい。気が利いてない部分はAPIを叩くなど工夫しよう。
zabbixユーザが改良版を求めているなら、もっと別のものを探したほうがいい。Sensuとか。

GitLabでmermaidを使う

$
0
0

GitLabでmermaidを使いたい。

PlantUMLが使える?なんで絵をWikiに入れたいだけなのにJava VMなんて重いものを動かさなきゃならないんだ。

ベースはpkgで入れたGitLab 9.2.10。(もちろんFreeBSDだよ!)
webpackもRuby on Railsも知らないので、とても適当。GitLabもコードをいじるのは初めてなので、割と適当。

https://github.com/2510/gitlabhq/commit/f42938716d74342a2077b2ff410d7b9d31358bce

pkgで入れたものに変更を適用するなら、変更したファイルを持ち込んであげて、yarn install --deploymentrake gitlab:assets:compileすればいい。

以下はあとで忘れないようにするためのメモ。

まずは準備。mermaidはyarnで入れる。

# su - git
$ cd /usr/local/www/gitlab
$ yarn add mermaid --production
  1. mermaidの定義を書く。banzaiってなんだよ、と思いながらいろいろ書く。
    まずrougeのlexerを書かないと、```に続けて書いた文字列(lang)が無視されてplaintextになって悲しい。(試行錯誤してたから違うかも?)
    続いてfilterで普通のcode blockとして扱われないように避ける。
    あとはfilterを使うようにpipelineの定義に足してやる。せっかく書いたclass指定がsanitizerに消されないように、sanitizerより後ろに置く。
    • lib/banzai/filter/mermaid_filter.rb
    • lib/banzai/pipeline/gfm_pipeline.rb
    • lib/banzai/pipeline/markup_pipeline.rb
    • lib/rouge/lexers/mermaid.rb
  2. mermaid.jsを読ませる。本体とCSS。
    CSSはnode_modules以下にいるcssを無理やりimportする。行儀が悪いように見えるが、すでにGitLabが別のとこでやってるので、これを真似ちゃう。
    Rails的にはRailsにnode_modules以下のassetsを認識させるという方法があるようだが、設定したら不思議なエラーを吐いたのでやめた。
    • app/assets/
    • app/assets/stylesheets/framework.scss
    • app/assets/stylesheets/framework/mermaid.scss
  3. wikiのプレビューで使えるようにする。
    • app/assets/javascripts/main.js
    • app/assets/javascripts/preview_markdown.js

assetsを再生成して、GitLabを再起動。

$ RAILS_ENV=production NODE_ENV=production bundle exec rake gitlab:assets:compile
$ service gitlab restart

できあがり。

寝正月2018

$
0
0

新年明けましておめでとうございます。
寝正月万歳です。

去年はGo Langに手を染め、しかし見た目の汚さが許容できず、LispやらSchemeに逃げました。
長年親しんだC/Perl/Pythonのようには自由にかけませんが、いずれはメイン言語にしていきたいものです。

さて、寝ぼけた頭で、いま頭に詰まっている内容を吐き出します。
Schemeまわりは以下の件でどこかにこぼれ落ちていったので、またそのうち。

1. SSL設定の変更

ごろごろしながら、やっとLet’s Encryptを使ってまともな証明書に切り替えましたよ。

SSL Server Test: linkage.white-void.net (Powered by Qualys SSL Labs)

2. Synapse Homeserver起動

仲間うちとの会話用に、がんばって立ち上げました。
FreeBSDでも簡単に立ち上がり、portableでよいです。これも詳しくはそのうち。

Matrix (SynapseはMatrixの実装)については、matrix.orgを参照ください。FAQはこちら。
近代的なUIのチャットアプリであるriot.imがあるので、なかなか使えます。

いたるところでセキュリティについて書かれているところは、非常に好感が持てます。
読み物としてはこのへんが楽しいです。

興味のある人は、遊んだり、募金してあげてください。ぜひ。

matrix(の実装であるsynapse)をFreeBSDで動かす

$
0
0

気がつけば2週間。
decentralizedなコミュニケーションツール “matrix” の実装である “synapse” をFreeBSDで動かした。
メモを見ながら書くので抜けている可能性大。

How To: Install a Matrix.org Home Serverを参考に。

まずインストールする。
実はpkg install py27-matrix-synapseで入るらしいが、試してない。bleeding-edgeを試したいから、ということにしておこう。

# pkg install python27 py27-pip py27-pillow py27-sqlite3 py27-lxml ca_root_nss
# pip install incremental cffi
# pip install https://github.com/matrix-org/synapse/tarball/master

設定ファイルを作る。

# mkdir /path/to/homeserver
# cd /path/to/homeserver
# python2.7 -m synapse.app.homeserver
            --server-name synapse.example.org \
            --config-path homeserver.yaml \
            --generate-config \
            --report-stats=no

homeserver.yamlを好きにいじる。server_nameは一度起動すると変更できない(データベースを初期化しないといけない)ので、ここでしっかり決めておく。
ちなみにFederation(外部matrixサーバとの相互接続)するなら、外部から到達可能な名前にするか、DNSのSRVレコードを書く必要がある。

うちはreverse proxyの後ろにいるので、no_tls: Trueにした。
tls_fingerprintsの値については、外向けの証明書のコピーを置いてtls_certificate_pathに指定する(tls_fingerprintsは空リストのまま)。
homeserver.yamlのコメントにも書いてあるとおり、以下でも取得できる。FreeBSDにbase64はないので、openssl sha256までで止めておけばいい。(どうせbase64で出てくる)
Let’s Encryptを使っていると期限が短いので、この周りも含めて自動更新するスクリプトでも書いておいたほうがいい。

# You can calculate a fingerprint from a given TLS listener via:
# openssl s_client -connect $host:$port < /dev/null 2> /dev/null |
#   openssl x509 -outform DER | openssl sha256 -binary | base64 | tr -d '='
# or by checking matrix.org/federationtester/api/report?server_name=$host

これで設定おわり。起動する。

# /usr/local/bin/synctl start /path/to/homeserver/homeserver.yaml

初期ユーザを作る。
register_new_matrix_userコマンドは、homeserver.yamlに書いてあるsecretを使ってユーザを作るようだ。
reverse proxyの後ろにいる場合、末尾のhttps://localhost:8448のをhttp://localhost:8008などに読み替える必要があるので注意。

# /usr/local/bin/register_new_matrix_user -c homeserver.yaml https://localhost:8448

あとはブラウザでアクセスしたり、riot.imからアクセスしたりする。
Federationは http://matrix.org/federationtester/api/report?server_name=サーバ名 でチェックできる。ConnectionReports→サーバのIPアドレス→Checks→AllChecksOKがtrueならばっちり。

あとはbotを書くなりなんなりと。

早速zabbixからの監視はbot送りにして、discordとのブリッジをさせてみたが、特に大きな問題はなく動いている。
「特に大きな問題はなく」のところはまた今度。きっとそのうち。

WiFi設定用QRコードの生成

$
0
0

自分用のメモ。
アプリで生成だのなんだの、面倒そうなことばっかり。
Webサイトで生成できる。

pure JS WiFi QR Code Generator

“pure JS”でブラウザ上だけで動く。通信しないので安心。

検索すると上のほうに出てくる mqr.kr (当然リンクしないよ)はSSIDやパスワードをURLに含めて送っちゃうので、とても推奨できない。というかダメだろう、これ。
怪しいなと思って、ダミーのデータを入れつつチェックしていてよかった。

追記:
zxing.appspot.com/generator も同じくGETで送ってるのでダメ。
みんな気にしないのか、堂々と罠をかけてるのか。

FreeBSDのバージョンを取得する

$
0
0

そんなものはfreebsd-versionコマンドで取れるのは分かってる。
でも、どうしてもコマンドを叩けない、叩きたくない、というときのためのメモ。
主にjailの管理用で、User Land側のバージョン取得が目的。

目的のUser Landバージョンはfreebsd-version -uで取れる。
なんと答えはこいつ自身が持っていた。シェルスクリプトにハードコード。

cat /bin/freebsd-version | grep USERLAND_VERSION=
USERLAND_VERSION="11.2-RELEASE-p2"

もちろんこれは実装依存なので、できるだけ依存すべきではない。が、こちらの要求もやや無茶なのでしょうがない。
あと、freebsd-versionは10.0で導入されたようなので、9.xにはないことにも注意。(とっくにEOLだけど)

HISTORY
     The freebsd-version command appeared in FreeBSD 10.0.

おまけ(Kernelバージョンの取得)

Kernelバージョンはきっとkernelが持っているだろうと思ってstrings /boot/kernel/kernelすれば、11.2-RELEASEとか出てくる。

以下、カーネルバージョンの取得スクリプト。無駄にがんばった。
64bit intの計算が曲者。結局10進数に変換してからbcを使った。

#!/bin/sh

data_address=0x$(objdump -h /boot/kernel/kernel | awk '{ if ($2 == ".data") { print $4 }}')
data_offset=0x$(objdump -h /boot/kernel/kernel | awk '{ if ($2 == ".data") { print $6 }}')

version_address=0x$(objdump -j .data -t /boot/kernel/kernel | awk '{ if ($6 == "version") { print $1 } }')
version_length=0x$(objdump -j .data -t /boot/kernel/kernel | awk '{ if ($6 == "version") { print $5 } }')

# convert to decimal
data_address=$(printf "%u" $data_address)
data_offset=$(printf "%u" $data_offset)
version_address=$(printf "%u" $version_address)
version_length=$(printf "%u" $version_length)

version_offset=$(bc -e "$data_offset + $version_address - $data_address" < /dev/null)
dd if=/boot/kernel/kernel iseek=$version_offset bs=1 count=$version_length 2>/dev/null | tr -d '\00'
$ sh get_kernel_version
FreeBSD 11.2-RELEASE-p2 #0: Tue Aug 14 21:45:40 UTC 2018
    root@amd64-builder.daemonology.net:/usr/obj/usr/src/sys/GENERIC

おまけ2

awkで64-bit intを計算してみる。途中からおかしい。

% echo 0x81448760 0x8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'
0x13c660
% echo 0xf81448760 0xf8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                                 
0x13c660
% echo 0xff81448760 0xff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                               
0x13c660
% echo 0xfff81448760 0xfff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                             
0x13c660
% echo 0xffff81448760 0xffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                           
0x13c660
% echo 0xfffff81448760 0xfffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                         
0x13c660
% echo 0xffffff81448760 0xffffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                       
0x13c660
% echo 0xfffffff81448760 0xfffffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                     
0x13c680
% echo 0xffffffff81448760 0xffffffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                   
0x13c800
% echo 0xfffffffff81448760 0xfffffffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                                 
0x138000
% echo 0xffffffffff81448760 0xffffffffff8130c100 | awk '{ printf "0x%x\n", $1 - $2 }'                                                                                                                               
0x180000

floatっぽいなと思ってpythonで試す。予想通り。

Python 2.7.12 (default, Jul  7 2016, 02:34:32) 
[GCC 4.2.1 20070831 patched [FreeBSD]] on freebsd9
Type "help", "copyright", "credits" or "license" for more information.
>> print("0x%x" % int(float(0xffffffffff81448760) - float(0xffffffffff8130c100)))                                                                                                                                                
0x180000

shは?

$ printf "0x%x\n" $(( 0xF000000000000000 - 0x1 ))
0x7ffffffffffffffe

POSIX.1な範囲で(unsigned) 64-bit intの計算ができる子はbc以外にいないものか……。

Raspberry PI 4 Model B + FreeBSD 13 BETA3 + ZFS

$
0
0

試してうまくいったのでメモとして残しておく。
後から手順にしているので、余計な手順や、抜けている手順があるかもしれない。(が、参考くらいにはなるはずと思っている)

なお、初期設定ほかではこちらを参考にさせてもらった。
本稿執筆時点(2021/02/23)では、シリアルコンソールなしで、USB+HDMIディスプレイというごく普通の装備だけで動かせている。

FreeBSD on RaspberryPi 4 Model B 発動篇

必要なもの

  • Raspberry PI 4 Model B+ (以下、RPI。なお、2Gモデルで実験している。)
  • FreeBSD-13.0-BETA3-arm64-aarch64-RPI.img
  • microSDカード (4GB以上)
  • USBメモリ (4GB以上、ZFSを認識させるために使用)
  • 適当な作業用FreeBSDマシン + USB-SDカードアダプタ

手順

  1. 適当な方法でイメージをSDカードに焼く。
    # dd if=/tmp/FreeBSD-13.0-BETA3-arm64-aarch64-RPI.img of=/dev/da0 bs=65536
  2. 念の為、この時点でRPIが起動することを確かめておく。
  3. SDカードを作業用PCに接続する。(以下、SDカードはda0になっているとする)
  4. 現在のUFS内データをバックアップする。
    # mkdir /mnt/raspi
    # mount -t ufs /dev/da0s2a /mnt/raspi
    # cd /mnt/raspi
    # tar -cf /tmp/raspi-rootfs.tar .
    # cd /
    # umount /mnt/raspi
  5. UFSを潰してZFSを作る。バックアップから中身を復元する。
    # gpart delete -i 2 da0
    # gpart add -t freebsd -a 1M da0
    # gpart create -s BSD da0s2
    # gpart add -t freebsd-zfs -s 4G da0s2
    # zpool create -t piroot -m none zroot /dev/da0s2a
    # zfs create piroot/ROOT
    # zfs create -o mountpoint=/ -u piroot/ROOT/default
    # zpool set bootfs=piroot/ROOT/default piroot
    # mount -t zfs piroot/ROOT/default /mnt/raspi
    # cd /mnt/raspi
    # tar -xf /tmp/raspi-rootfs.tar
  6. /boot/loader.conf(もちろん /mnt/raspi/boot/loader.conf のことだよ!) を書き変える。hw.usb.template, umodem_load, boot_multicons, boot_serialは無効化した。どれが犯人か分からないが、bcm_dmaでエラーが起きたので無効化して逃げている。
    #hw.usb.template=3
    #umodel_load="YES"
    #boot_multicons="YES"
    #boot_serial="YES"
    beastie_disable="YES"
    loader_color="NO"
    
    zfs_load="YES"
    vfs.root.mountfrom="zfs:zroot/ROOT/default"
  7. /etc/fstabからrootfsをマウントする記述を取り除く。/dev/ufs/rootfsの部分。忘れると酷い目に遭う。遭った。(おまけ参照)
  8. zpool exportする。
    # zpool export piroot
  9. 作業用PCでもう一仕事。USBメモリに起動用イメージを焼く。もっとも、必要なのはrootfs部分だけだが。 USBメモリを作業PCに挿して、SDカードと全く同じようにして書き込む。例えばこう。
    # dd if=/tmp/FreeBSD-13.0-BETA3-arm64-aarch64-RPI.img of=/dev/da1 bs=65536
  10. SDカードとUSBメモリをRPIに挿して、起動させる。kernelが読まれた後、プロンプトに入るためにEnter以外(SPACEとか)のキーを押す。vfs.root.mountfromを書き変えて、一時的にUSBメモリから起動させる。
    Hit [Enter] to boot immediately, or any other key for command prompt.
    Booting [/boot/kernel/kernel] in 10 seconds...
    
    Type '?' for a list of commands, 'help' for more detailed help.
    OK set vfs.root.mountfrom=ufs:/dev/da0s2a
    OK boot
  11. 通常起動する(はず)なので、起動したらzpoolのcacheを作ってコピーして、shutdownする。
    # kldload /boot/kernel/zfs.ko
    # zpool import -f -o cachefile=/tmp/zpool.cache -R /mnt/raspi zroot
    # cp /tmp/zpool.cache /mnt/raspi/boot/zfs/zpool.cache
    # shutdown -p now
  12. USBメモリは抜いて起動する。
  13. おつかれさまでした。

おまけ

ある手順を忘れると、起動中に

Warning: no time-of-day clock registered, system time will not be set accurately

が出て止まる。
このとき、loaderで boot -v すると、start_init: trying /sbin/init で止まる。
(正常起動時は lo0: link state changed to UPgenet0: link state changed to DOWN が出るはず。)

これがまともなエラーを吐かないので調査に手間取った。
途中で作ったUSBメモリを挿していると、これは発生しない。(/dev/ufs/rootfs があるから)
適当なUSBメモリでは代わりにならない。(/dev/ufs/rootfs がないから)
適当に同じようなパーティションを書いたUSBメモリでもダメ。(ラベルを付け忘れたので以下略)


起動用zpoolでzpool removeしてはいけない

$
0
0

死ぬので。(@FreeBSD 13.0-RELEASE, 2021/07/13時点)

そもそも何故zpool removeしたのか

まずディスクを交換したかった。
大体の場合は、mirrorを作って古いディスクを外せばいい。(zpool attach + zpool remove)
ただ今回はboot領域にうっかりでっかいディスクを使ってしまっていて、SSDに交換しようとしたら容量が足りなかった。

調べたところ、最近のzpoolはzpool addしたあとremoveできて、これを使うと、poolのshrinkができる。
てきとーにやってみたら、簡単にできてしまった。(ぎりぎりに縮小することはできなくて、ものすごく余裕を持たせる必要はあったが)

感心&安心していた。数日後にrebootするまでは。

panicする

停電etc.でrebootすることもあるが、そのときに(今回みたいな)障害に遭いたくない。というわけで時間があるときにやる。
rebootしたら、こんなpanicが出てrebootループに入ってしまった。

余談1: その前にbootcodeの書き替え忘れもあった。
余談2: rebootが早すぎて読めなかったので、iPhoneで動画撮影して見る羽目になった。

Root mount waiting for: usbus0
uhub2: 4 ports with 4 removable, self powered
panic: VERIFY(nvlist_lookup_uint64(configs[i], ZPOOL_CONFIG_POOL_TXG, &txg) == 0) failed

困る

ZFS絡みなのが分かったので、すぐに別のマシンに繋いでテストしたりしたが、まったく問題が見つからない。
zpool importは正常だし、zpool scrubしても何も出ない。

解決

実は大分前に、ZIL(log)を取り外したときに同じ現象だったことを(奇跡的に)思い出した。
このときにどうしたかは思い出せなかったが、つまりpoolが悪い。
どうするかは簡単で、zfs sendしてzfs receiveすればいい。

boot用なので5GB程度しかなく、すぐに復旧できた。

余談3: cat ... | zfs receiveは遅い。dd if=... bs=262144 | zfs receiveがいい。

ng_bridge+CARPは危ない(かもしれない)

$
0
0

注意: ng_bridgeを使った場合の話です。ngctlする人向けです。ifconfigでbridgeする人には関係ない(はず)の内容です。

CARPを設定したら60秒おきに切れたり繋ったりする。
対策としては、CARPをGRE/VXLANなどに通すか、ng_bridgeのループ判定を無効化する。

なんで「かも」かというと、これを書いた前日に散々発生したくせに、調査を始めたら全然起きなくなったから。
でも起きると60秒間通信不能になるので、危険ではあるはず。

なにが問題か

ng_bridgeは簡易ループ判定を持っている。
この判定はパケット受信時に「記憶しているMACアドレスとポートの対が異なる」現象がminStableAge(デフォルト1秒)より短い間隔で発生すると、ループありと判定するもの。
参考: ng_bridgeの判定部分

carp(ucarp)は、送信元MACアドレスを 00:00:5E:00:00:<vhid> にして送るため、ng_bridgeからすると同じMACアドレスからのパケットがいろんなポートから出入りすることになって、上記ループ判定に引っかかることがある。

解決策その1

ng_bridgeにバレなければいいので、encapsuleすればいい。GREとかVXLANとか。
GREだとpeer-to-peerしかできないので、3台以上でcarpしたい場合には向かないが、multicast(=ethernetからすればbroadcast)を減らせるというメリットがある。

解決策その2

encapsuleすると障害の原因が増えかねない。ループは自己責任で、というならこちら。
setconfigでloopTimeout=0にする。set後にgetconfigするとloopTimeoutがいなくなるが、効いている模様。

# ngctl msg br0: getconfig
Rec'd response "getconfig" (2) from "[5]:":                                                                                          │     performing TSO on the inner frames in hardware: cxgbe(4).
Args:   { debugLevel=1 loopTimeout=60 maxStaleness=900 minStableAge=1 }
# ngctl msg br0: setconfig '{ debugLevel=1 loopTimeout=0 maxStaleness=900 minStableAge=1 }'
# ngctl msg br0: getconfig
Rec'd response "getconfig" (2) from "[5]:":
Args:   { debugLevel=1 maxStaleness=900 minStableAge=1 }

ところで……

CARPはMASTERがadvertise中にBACKUPはadvertiseしないため、この事象はあまり発生しないように思える。
しかし、CARPが動作するような状況、つまり中間にある機器(switch, netif)の一時障害が発生すると、両者がMASTERになり、この一時障害の解消時に問題を引き起こす可能性がある。

ちなみに、CARPのパスワードを間違えると相互にadvertiseする状態になるので、すぐにこの現象を引き起こすことになる。

macOSでWindowsインストール用USBメモリを作る

$
0
0

めもめも。
先人達の情報もいろいろありそうだが読まずにやってみた。

  1. 起動用とインストール用の2つのパーティションを作る。1つはFAT32, もう1つはExFATにする。
    diskutil partitionDisk /dev/diskX 2 MBR FAT32 EFISYS 1024M ExFAT INSTALLER R
    
  2. Windowsのインストール用ISOをマウントする。
  3. ISO中のboot, bootmgr, bootmbr.efi, efi, sources/boot.wimを、EFISYSボリュームにコピーする。
  4. ISO中の全ファイルをINSTALLERボリュームにコピーする。
    ※たぶんboot系は要らないが、選別するほうが面倒なので全部コピーした。

ダメだったやつ一覧

  • ISOをddで書く: 起動用の一部のパーティション(?)しか見えず、インストーラは起動するがインストール用の物件が見えないと怒られる。(ドライバのインストールが要るよ、とわけのわからないことを言われる)
  • balenaEtcherでISOを書く: どうやら上と同じ。
  • GPTでフォーマットする: インストール開始できるが、すぐに “Windows could not prepare the computer to boot into the next phase of installation” というエラーになる。

CP2112をFreeBSDで使う

$
0
0

ArduinoでI2Cをいじって遊んでいたところ、そもそも最初のトリガーをEthernetかなにかで送らねばならず。
結局Raspberry Piにするか、もしくはArduino Yunにするのかと思っていたところ、いいものが見つかった。

サンハヤト: USB・I2C(SMBus)変換モジュール MM-CP2112B

CP2112というチップが載ったちんまいボードで、USBからI2C 1本とGPIO 8本が使える素敵な子。
しかもFreeBSDはCP2112のドライバを標準で積んでいて、kldloadするだけ。

買おうと思ったら、なぜか押し入れから発掘されたので、早速試してみた。(いつ買ったんだろうね??)

i2cデバイスは /dev/iic0 とかとして出現する。
制御には /usr/sbin/i2c コマンドが用意されている。(man i2c(8))
試してみよう。

# kldload cp2112
# kldload iic
# <ここでデバイスを挿す>
# dmesg
...
ugen0.6:  at usbus0
cp2112hid0 on uhub0
cp2112hid0:  on usbus0
cp2112hid0: part number 0x0c, version 0x03
gpio0:  on cp2112hid0
gpiobus0:  on gpio0
gpioc0:  on gpio0
iichb0:  on cp2112hid0
iicbus0:  on iichb0
iichb0: unsolicited interrupt, ignored
iic0:  on iicbus0
# i2c -s -f /dev/iic0
Hardware may not support START/STOP scanning; trying less-reliable read method.
Scanning I2C devices on /dev/iic0: 10 

アドレス 0x10 のデバイスがあっさり検出された。
データを書いてみる。が、世の中そう甘くない。

# printf "\000" | i2c -a 0x10 -d w -c 1
ioctl: error sending start condition

ちなみに -a 0x10 はアドレス指定、-d wは書き込みモード指定、-c 1 は書き込むバイト数、だ。

解決のヒントは先のスキャンで出ている Hardware may not support START/STOP scanning というメッセージで、CP2112のドライバが一部のIOCTLに対応していないことにある。
manによるところの、

Some I2C bus hardware does not provide control over the individual start, repeat-start, and stop operations.

というやつに該当するのだ。(該当部分の処理)
なので、これもmanの通りに tr モードを指定すればいい。

# printf "\000" | i2c -a 0x10 -d w -m tr -w 0 -c 1

これで成功。readも似たようなものなので、やりたい人はmanを読んでほしい。

※アドレスを間違えると i2c: ioctl(I2CRDWR) failed: No such file or directory と言われる。
FileでもDirectoryでも無いよ!と叫びたいが、エラーコードは ENOENT なので、メッセージが悪いと言えよう。

ちなみに。
CP2112さんを引っこ抜くと、panicする。とても辛い。


※これはVMware Fusionでの実験結果。実機でも同様。

本稿を書くにあたって、Vadim Zaigrin氏の Working with I2C in FreeBSD on Raspberry Pi を参考にさせてもらった。
実はこの方と同じ原因(ドライバが一部のIOCTLに非対応) なのだが、割と最近(May 2019)になってtr モードが追加されて、標準ツールでも使えるようになっていた。

もちろん、依然としてC言語で制御もできる。
i2cコマンドの実装や、Vadim Zaigrin氏の記事を参考にしてほしい。

Compressorで出力したビデオに日付を付けてPhotosに認識させたい

$
0
0

Compressor でなにも考えずに圧縮したビデオを Photos に入れると、作成日がビデオの日付として使われる。
昔のビデオを加工したときに期待通りに並ばずに腹立たしいので、日付をつけたい。

そんなときは、Job Annotations で Date を付けるとよい。
Job Annotations は Job を選んでいるときに設定できる。

Date の形式は任意の文字列になっていて、どんな文字列も受け付けてくれる。
が、適当な形式では Photos 側が認識してくれない。
いくつか試したところ、少なくとも 2022-07-07T23:59:59+09:00 のような ISO 8601 形式は受けつけてくれることが分かった。

これで、整理していなかったビデオも整理して Photos に入れられる。

bhyveでPXE Bootしたい

$
0
0

簡単だった。

言うまでもないけど、ff:ff:ff:ff:ff:ff は好きなMACアドレスに書き換えてほしい。ほかのパラメータも、ね。

# pkg install bhyve-firmware
# bhyve -AHP -s 0:0,hostbridge -s 1:0,lpc -s 2:0,virtio-net,tap0,mac=ff:ff:ff:ff:ff:ff -c 1 -m 256M -l bootrom,/usr/local/share/uefi-firmware/BHYVE_UEFI.fd -l com1,stdio guest

参考はこちら。

ポイントは BHYVE_UEFI.fd は放っておけば IPv4 PXE Boot してくれるということ。
なので、PXE Bootしたいからオプションを探して……と悩む必要はない。起動可能なドライブを接続しなければいいのだ。

zpoolがstate: FAULTEDした話

$
0
0

知見でもなんでもないが、記録のために。

TL;DR

  • 寝ている間にストレージサーバがpanicした。
  • 自動再起動したが、zpoolがFAULTEDとなっていた。
  • 5秒分のデータを失ってもいいならimportできるよ、という親切なメッセージが出た。
  • zpool import -Fだけで無事直った。

まずpanicした。これは起きてほしくはないが、仕方がない。
とくにこれといった特徴はない。

kernel trap 12 with interrupts disabled

Fatal trap 12: page fault while in kernel mode
cpuid = 0; apic id = 00
fault virtual address = 0xfffffe0011e246f0
fault code            = supervisor read data, page not present
instruction pointer   = 0x20:0xffffffff80c393f4
stack pointer         = 0x28:0xfffffe0084ef2800
frame pointer         = 0x28:0xfffffe0084ef2800
code segment          = base 0x0, limit 0xfffff, type 0x1b
                      = DPL 0, pres 1, long 1, def32 0, gran 1
processor eflags      = resume, IOPL = 0
current process               = 4 (doneq0)
trap number           = 12

The pool metadata is corrupted とか怖いことを言われる。

# zpool import                                                           
                                                                                          
   pool: sakura                                                                           
     id: 16271490741989414414                                                             
  state: FAULTED
status: The pool metadata is corrupted.
 action: The pool cannot be imported due to damaged devices or data.                      
        The pool may be active on another system, but can be imported using               
        the '-f' flag.                                                                    
   see: https://openzfs.github.io/openzfs-docs/msg/ZFS-8000-72                            
 config:                                                                                  
                                                                                          
        sakura                    FAULTED  corrupted data                                 
          raidz1-0                ONLINE                                                  
            gpt/sakura-1          ONLINE                                                  
            gpt/sakura-2          ONLINE                                                  
            gpt/sakura-3          ONLINE  

恐る恐るimportしようとすると、拒否はされるが、5秒分のデータを失っていいなら zpool import -F でいけるよ、と教えてくれる。

# zpool import sakura        
cannot import 'sakura': I/O error                                                         
        Recovery is possible, but will result in some data loss.                          
        Returning the pool to its state as of Fri Dec 16 00:33:39 2022                    
        should correct the problem.  Approximately 5 seconds of data                      
        must be discarded, irreversibly.  Recovery can be attempted                       
        by executing 'zpool import -F sakura'.  A scrub of the pool                       
        is strongly recommended after recovery.                                                                                                                                      

というわけで zpool import -F を実行して、無事 HEALTHY に回復。
recommendation通りに zpool scrub も実行。これもなにもエラーを検出せずで、ひと安心。

5秒分のロス程度なら、自動で復旧してくれて構わない気もするが、そういう設定があるかは調べていない。
また起きたら考えるつもり。


SambaにINTERNAL_ERRORされた

$
0
0

ついうっかりSambaを更新したら、Firefoxでファイルをダウンロードしたときに失敗するようになってしまった。

ネットワークからの取得は終わっていて.partファイルはちゃんとできている。しかし、その後.partファイルから本来のファイルに差し替えるところで失敗しているように見える。
あとに残るのは .zip.part みたいなファイル(中身はOK)と、0バイトな .zip ファイル。

うんうん唸りながらログを集めていたところ、Sambaがrenameに対してINTERNAL_ERRORを返しているのを見つけた。
前後のログからどうもoplockが怪しい、ということで oplock = no を指定したら解消した。
少なくとも、20回やって毎回失敗していたのが成功して、続けて2件のダウンロードも成功したので、大丈夫であろう。

oplockを有効にすると30%ほどもパフォーマンスが上がるよ!とあるが、失敗するのでは話にならないので、とりあえずこれで。

Wiresharkで見た様子:

log.smbdの様子: (↑とは違うタイミング)

[2023/02/11 12:49:27.186686,  5, pid=63759, effective(1001, 0), real(0, 0), class=locking] ../../lib/dbwrap/dbwrap.c:180(dbwrap_lock_order_unlock)
  dbwrap_lock_order_unlock: release lock order 1 for /var/db/samba4/locking.tdb
[2023/02/11 12:49:27.186922, 10, pid=63759, effective(1001, 0), real(0, 0), class=locking] ../../source3/lib/dbwrap/dbwrap_watch.c:1027(dbwrap_watched_watch_state_destructor_fn)
  dbwrap_watched_watch_state_destructor_fn: Watcher 63759:6 not found
[2023/02/11 12:49:27.187162,  3, pid=63759, effective(1001, 0), real(0, 0), class=smb2] ../../source3/smbd/smb2_server.c:3957(smbd_smb2_request_error_ex)
  smbd_smb2_request_error_ex: smbd_smb2_request_error_ex: idx[5] status[NT_STATUS_INTERNAL_ERROR] || at ../../source3/smbd/smb2_server.c:2053
[2023/02/11 12:49:27.187387, 10, pid=63759, effective(1001, 0), real(0, 0), class=smb2] ../../source3/smbd/smb2_server.c:3849(smbd_smb2_request_done_ex)
  smbd_smb2_request_done_ex: mid [5705] idx[5] status[NT_STATUS_INTERNAL_ERROR] body[8] dyn[yes:1] at ../../source3/smbd/smb2_server.c:4005

MastodonからFedibirdに移行する

$
0
0

Mastodon 3.1.2からFedibirdに乗り換えた。(on FreeBSD 13.2-RELEASE)
久しぶりに地獄を味わった気がする。

とりあえずメモを残しておく。きっと、もっとエレガントな方法があるはずだが、それでも記録は記録なのだ。

  • Rubyは2.xである必要あり。3.xでもナントナクは動くけど、時々 **dict の扱い違いが原因のエラーが出るので危険。
  • ruby27はpkgがあるけど、ruby-gemsはpkgがないのでソースコードから ./configure && make install が必要。
  • idn-ruby gemはinclude pathの都合があるので、 bundle install の前に gem install が必要。
  • db:migrateは気合と根性が必要。かもしれない。
  • tootctl cache clearしないとあるはずのデータ(列)が見えなかったりして大混乱する。

追記は末尾にて。

ruby 2.7

https://cache.ruby-lang.org/pub/ruby/2.7/ruby-2.7.7.tar.gz から拾ってきて、 ./configure && make install すればいいだけ。

idn-ruby

$ pkg install libidn2
$ gem install idn-ruby -v 0.1.2 -- --with-idn-dir=/usr/local

db:migrate

ここが一番しんどかったところ。rake db:migrate してもいろいろコケる。
ここはruby 3.xでやっていたのが悪かったかもしれないが、今となっては確かめる気にもならない。
あと、mastodon側も以前migrateが失敗して誤魔化したことがあるので、一部はソレかもしれない。

ざっくり以下の2手順。

  • db:migrateを気合で通す
  • 余分なテーブルと列を葬る

db:migrateを気合いで通す

PG::DuplicateColumn: ERROR:  column "list_id" of relation "keyword_subscribes" already exists
/home/mastodon/db/migrate/20191025190919_add_list_to_keyword_subscribes.rb:5:in `change'

PG::DuplicateColumn: ERROR:  column "list_id" of relation "account_subscribes" already exists
/home/mastodon/db/migrate/20191026110416_add_list_to_account_subscribes.rb:5:in `change'

PG::DuplicateColumn: ERROR:  column "list_id" of relation "follow_tags" already exists
/home/mastodon/db/migrate/20191026110502_add_list_to_follow_tags.rb:5:in `change'

このへんは既に存在する列の追加なので無視。scriptを空にしてスルーさせる。

NameError: uninitialized constant AddIndexURLToStatuses
Did you mean?  AddIndexUrlToStatuses

これは……なんだろうね?動けばよろしいの精神で直す。

db/migrate/20200312020757_add_index_url_to_statuses.rb:
- class AddIndexUrlToStatuses < ActiveRecord::Migration[5.2]
+ class AddIndexURLToStatuses < ActiveRecord::Migration[5.2]

db/migrate/20200827204602_add_inbox_url_to_conversations.rb:
- class AddInboxUrlToConversations < ActiveRecord::Migration[5.2]
+ class AddInboxURLToConversations < ActiveRecord::Migration[5.2]
No such column: custom_filters.whole_word
/home/mastodon/db/migrate/20230106051103_change_default_of_whole_wotd_in_custom_filter.rb:3:in `change'

これはウチの環境が悪いであろうもの。手動で直す。

(echo 'require("./db/migrate/20180707154237_add_whole_word_to_custom_filter.rb")'; echo "AddWholeWordToCustomFilter.new.change" ) | RAILS_ENV=production bundle exec rails runner -
== 20230221031206 AddWidthHeightToCustomEmoji: migrated (0.0017s) =============

rake aborted!
NameError: uninitialized constant CreateNodeInfo

そんなバカな?これも動けばいいので直す。

db/migrate/20230310214919_create_nodeinfo.rb

- class CreateNode < ActiveRecord::Migration[6.1]
+ class CreateNodeInfo < ActiveRecord::Migration[6.1]

記録してあるのはこのへんまで。これで rake db:migrate は成功するが、まだまだおかしい。

余分なテーブルと列を葬る

db/schema.rb の通りにすればいいんだけど、手動でこれをやるのは困難。特にindexとか。

まずは差分をチェックする。
今のデータベースからschema.rbを作って、diffで見ていく。

$ mv db/schema.rb db/schema.rb.fedibird
$ RAILS_ENV=production bundle exec rake db:schema:dump
$ mv db/schema.rb db/schema.rb.old
$ sdiff --width=240 db/schema.rb.old db/schema.rb.fedibird
$ cp db/schema.rb.fedibird db/schema.rb

fedibird側で追加されているものはdefaultに期待してスルー。
逆にfedibird側でないデータはrestore時にコケるので、1つずつDROP TABLEまたはDROP COLUMNしていく。

余計なものがなくなったら、SQL形式でdumpを作る。

$ pg_dump --data -U mastodon mastodon > /でかいよ/mastodon-db-data-only.sql

ここでDBを完全に消して(別途DROP DATABASE)、db:setupで作り直す。先の手順でdb/schema.rbを戻し忘れていると悲しいので注意。

$ RAILS_ENV=production bundle exec rake db:setup

初期用のデータが入るので消す。ただし、schema_migrationは必要なのでとっておく。

$ pg_dump --data --schema=schema_migrations -U mastodon mastodon > /これは大きくない/schema-migrations.sql
$ cat << EOM | psql -U mastodon mastodon
TRUNCATE accounts CASCADE;
TRUNCATE ar_internal_metadata CASCADE;
TRUNCATE oauth_applications CASCADE;
TRUNCATE schema_migrations;
EOM

まっさらチェック。

$ pg_dump -U mastodon --data --insert mastodon | grep INSERT

データを復元する。ここで、循環参照のあるforeign keyを復元させるため、一時的にmastodonユーザにsuperuser権限が必要。終わったら権限は戻しておく。

$ (echo "set session_replication_role = replica;"; cat /でかいね/mastodon-db-data-only.sql) | psql -U mastodon mastodon

schema_migrationsを上書きする。

$ echo "TRUNCATE schema_migrations;" | psql -U mastodon mastodon
$ psql -U mastodon mastodon < /これは大きくない/schema-migrations.sql

途中、何度か時間や手間のかかる処理があるので、適宜バックアップと取らないと辛いことになる。(なった)

追記分

db:migrate関連の失敗で、過去分のmedia_attachments.shortcodeがNULLになっていた。
これがあると、プロフィールのページが開けなかったりする。
対応するのは 20170105224407_add_shortcode_to_media_attachments.rb
SQLを直接実行して解決した。

UPDATE media_attachments SET shortcode = id WHERE shortcode IS NULL AND remote_url = '';

FreeBSDでOpenObserveクラスタ構築

$
0
0

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とかで生きているサービスに飛ばすこと。
  • MinIO

    特に書くことはない。やることはこれだけ。

go install github.com/minio/minio@latest
go install github.com/minio/mc@latest

jail上で起動すると、root disk判定が誤判定するので、 MINIO_ROOTDISK_THRESHOLD_SIZE=1 など環境変数を設定しておく。
もちろん保存先を間違えると大変な目に遭うので注意。

etcd

pkg install coreos-etcd34 で入る。
ちなみにrc scriptがないので、自分で daemon を呼ぶ。

起こし方は素直にhttps://etcd.io/docs/v3.5/op-guide/clustering/のガイドを読むのがいい。

一応残しておくと、試した設定はこんな感じ。
各ホストで調整が必要な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"

OpenObserve

ビルドはサーバプロセス(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 inpm 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;
        }
    }
}

サーバプロセスは環境変数を設定して起こす。
一覧はこちら: https://openobserve.ai/docs/environment-variables/
たとえばこんな感じ。
ZO_LOCAL_MODE_STORAGEはたぶん要らない。ZO_TELEMETRYとZO_PROMETHEUS_ENABLEDはお好みで。
今回はminio/etcd/openobserveを全サーバにそれぞれ入れたので127.0.0.1になっているが、そこは環境に合わせて適宜調整。

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があるので、こいつを見ながら設定していた。

https://github.com/openobserve/openobserve-helm-chart/blob/main/values.yaml

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

gitlab-runnerにrefusing to work with credential missing host fieldと言われたら

$
0
0

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=1GIT_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側の問題であることは明白だったのだが、少々混乱もした。

FreeBSDのVMでHDD容量を拡張

$
0
0

ESXiなどでディスクサイズを拡張したときどうするか。
再起動すれば認識するのは分かっているが、再起動したくない。

というときは、以下でよい模様。

$ sudo camcontrol reprobe da0

あとはgpart resizeしたりgrowfsしたり、zpool online -eしたりすればよい。

Viewing all 62 articles
Browse latest View live