りんけーじ - blog

ℹ️本記事は古いコンテンツを変換して表示しています。

表示が崩れたり、リンクが正しくない可能性があります。ご了承ください。

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

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