講真,OpenObserve 挺好用的!
OpenObserve(簡(jiǎn)稱 O2)是一個(gè)用 Rust 開發(fā)的開源云原生可觀測(cè)平臺(tái),專為日志、指標(biāo)、追蹤而構(gòu)建,設(shè)計(jì)用于 PB 級(jí)工作。
與需要理解和調(diào)整大量配置置的 Elasticsearch 相比,它簡(jiǎn)單且易于操作。在 2 分鐘內(nèi)即可啟動(dòng)并運(yùn)行 OpenObserve。對(duì)于使用 API 獲取數(shù)據(jù)并執(zhí)行搜索的用戶來說,OpenObserve 可以無縫替代 Elasticsearch。OpenObserve 帶有自己的用戶界面,無需單獨(dú)安裝。與 Elasticsearch 相比,使用 OpenObserve 可以將日志存儲(chǔ)成本降低約 140 倍。
與 ES 對(duì)比
相關(guān)概念
在詳細(xì)了解 O2 之前,我們需要了解一些相關(guān)概念,這些概念是 O2 的基礎(chǔ)。
Organizations(組織)
組織是 OpenObserve 中對(duì)各種流、用戶、功能進(jìn)行分組的邏輯實(shí)體。組織可以代表一個(gè)企業(yè)、企業(yè)的一個(gè)部門或一個(gè)應(yīng)用程序。所有流、用戶、功能等都限定在一個(gè)組織范圍內(nèi)。
Streams(流)
OpenObserve 中的流是共享相同源的事件序列(日志/指標(biāo)/跟蹤),例如來自特定應(yīng)用程序的日志或來自企業(yè)的日志。比如日志就是一種流,用于記錄來自應(yīng)用程序的事件。
Functions(函數(shù))
OpenObserve 中的函數(shù)可在攝取和查詢期間使用,以幫助增強(qiáng)高級(jí)功能,如豐富、編輯、日志縮減、合規(guī)性等。函數(shù)是使用 VRL 腳本定義的。
Parquet
O2 中的數(shù)據(jù)以 parquet 格式存儲(chǔ),這是一種列式存儲(chǔ)格式,查詢和存儲(chǔ)效率很高。
Timestamp(時(shí)間戳)
_timestamp 在 OpenObserve 中被視為時(shí)間戳列,如果 _timestamp 或 @timestamp 不存在于正在攝取的數(shù)據(jù)中,我們會(huì)將 _timestamp 添加到每個(gè)記錄,其 NOW 值為微秒精度。
對(duì)于鍵為 _timestamp/@timestamp 的輸入數(shù)據(jù),對(duì)于值我們支持以下數(shù)據(jù)類型/格式:
- 微秒
- 字符串值
- RFC 3339 和 ISO 8601 日期和時(shí)間字符串,例如 1996-12-19T16:39:57-08:00
- RFC 2822 日期和時(shí)間字符串,例如 Tue, 1 Jul 2003 10:52:37 +0200
如果用戶想要支持 _timestamp/@timestamp 以外的鍵,用戶可以使用 ZO_COLUMN_TIMESTAMP 配置來指定時(shí)間戳鍵。
User Roles(用戶角色)
OpenObserve 中的用戶可以具有 admin 或 member 角色。與具有 member 角色的用戶相比,具有 admin 角色的用戶擁有更大的權(quán)限,例如,具有 admin 角色的用戶可以將其他用戶添加到組織中。
架構(gòu)
OpenObserve 可以在單節(jié)點(diǎn)下運(yùn)行,也可以在集群中以 HA 模式運(yùn)行。
單節(jié)點(diǎn)模式
單節(jié)點(diǎn)模式也分幾種架構(gòu),主要是數(shù)據(jù)存儲(chǔ)的方式不同,主要有如下幾種:
SQLite 和本地磁盤模式
如果你只需要進(jìn)行簡(jiǎn)單使用和測(cè)試,或者對(duì)高可用性沒有要求,可以使用此模式。當(dāng)然你仍然可以在一臺(tái)機(jī)器上每天處理超過 2 TB 的數(shù)據(jù)。在我們的測(cè)試中,使用默認(rèn)配置,Mac M2 的處理速度為約 31 MB/秒,即每分鐘處理 1.8 GB,每天處理 2.6 TB。該模式也是運(yùn)行 OpenObserve 的默認(rèn)模式。
SQLite本地模式
SQLite 和對(duì)象存儲(chǔ)模式
該模式和 OpenObserve 的默認(rèn)模式基本上一致,只是數(shù)據(jù)存在了對(duì)象存儲(chǔ)中,這樣可以更好的支持高可用性,因?yàn)閿?shù)據(jù)不會(huì)丟失。
SQLite對(duì)象存儲(chǔ)模式
高可用模式
HA 模式不支持本地磁盤存儲(chǔ),該模式下,OpenObserve 主要包括 Router、Querier、Ingester、Compactor 和 AlertManager 節(jié)點(diǎn)(組件),這些節(jié)點(diǎn)都可以水平擴(kuò)展以適應(yīng)更高的流量。另外使用 Etcd 或 NATS 用作集群協(xié)調(diào)器并存儲(chǔ)節(jié)點(diǎn)信息,還用于集群事件。MySQL/PostgreSQL 用于存儲(chǔ)組織、用戶、函數(shù)、報(bào)警規(guī)則、流模式和文件列表(parquet 文件的索引)等元數(shù)據(jù)。對(duì)象存儲(chǔ)(例如 s3、minio、gcs 等)存儲(chǔ) parquet 文件的數(shù)據(jù)。
Etcd對(duì)象存儲(chǔ)
Ingester
Ingester 用于接收攝取請(qǐng)求并將數(shù)據(jù)轉(zhuǎn)換為 parquet 格式然后存儲(chǔ)在對(duì)象存儲(chǔ)中,它們?cè)趯?shù)據(jù)傳輸?shù)綄?duì)象存儲(chǔ)之前將數(shù)據(jù)臨時(shí)存儲(chǔ)在 WAL 中。
OpenObserve 中攝取的數(shù)據(jù)默認(rèn)根據(jù)年月日和小時(shí)進(jìn)行分區(qū),我們還可以指定用于對(duì)數(shù)據(jù)進(jìn)行分區(qū)的分區(qū)鍵。
數(shù)據(jù)攝取流程如下所示:
Ingester Flow
- 從 HTTP / gRPC API 請(qǐng)求接收數(shù)據(jù)。
- 逐行解析數(shù)據(jù)。
- 檢查是否有任何用于轉(zhuǎn)換數(shù)據(jù)的函數(shù)(攝取函數(shù)),按函數(shù)順序調(diào)用每個(gè)攝取函數(shù)
- 檢查時(shí)間戳字段,將時(shí)間戳轉(zhuǎn)換為微秒;如果記錄中不存在時(shí)間戳字段,則設(shè)置當(dāng)前時(shí)間戳。
- 檢查流 schema 以確定 schema 是否需要演變。在這里,如果我們發(fā)現(xiàn)需要更新 schema 以添加新字段或更改現(xiàn)有字段的數(shù)據(jù)類型,則獲取 lock 來更新 schema。
- 評(píng)估實(shí)時(shí)報(bào)警(如果為流定義了任何報(bào)警)。
- 按小時(shí)桶中的時(shí)間戳寫入 WAL 文件,然后將請(qǐng)求中的記錄轉(zhuǎn)換為 Arrow RecordBatch 并寫入 Memtable。
- 為每個(gè) organization/stream_type 創(chuàng)建 Memtable,如果僅為日志攝取數(shù)據(jù),則只有一個(gè) Memtable。
- WAL 文件和 Metable 是成對(duì)創(chuàng)建的,一個(gè) WAL 文件有一個(gè) Memtable 。WAL 文件位于 data/wal/logs。
- 當(dāng) Memtable 大小達(dá)到 ZO_MAX_FILE_SIZE_IN_MEMORY = 256MB 或 WAL 文件達(dá)到 ZO_MAX_FILE_SIZE_ON_DISK = 128MB 時(shí),我們將 Memtable 移動(dòng)到 Immutable 并創(chuàng)建一個(gè)新的 Memtable 和 WAL 文件用于寫入數(shù)據(jù)。
- 每 ZO_MEM_PERSIST_INTERVAL=5 秒會(huì)將 Immutable 轉(zhuǎn)儲(chǔ)到本地磁盤。一個(gè) Immutable 將生成多個(gè) parquet 文件,因?yàn)樗赡馨鄠€(gè)流和多個(gè)分區(qū),parquet 文件位于 data/wal/files。
- 每 ZO_FILE_PUSH_INTERVAL=10 秒,我們檢查本地 parquet 文件,如果任何分區(qū)總大小超過 ZO_MAX_FILE_SIZE_ON_DISK=128MB 或任何文件已在 ZO_MAX_FILE_RETENTION_TIME=600 秒前,分區(qū)中的所有此類小文件將被合并為一個(gè)大文件(每個(gè)大文件將最大為 ZO_COMPACT_MAX_FILE_SIZE=256MB) ,它將被移動(dòng)到對(duì)象存儲(chǔ)。
Ingester 包含三部分?jǐn)?shù)據(jù):
- Memtable 中的數(shù)據(jù)
- Immutable 中的數(shù)據(jù)
- WAL 中的 parquet 文件尚未上傳到對(duì)象存儲(chǔ)。
這些數(shù)據(jù)都需要能夠被查詢到。
Router
Router(路由器)將請(qǐng)求分發(fā)給 ingester 或 querier,它還通過瀏覽器提供 UI 界面,Router 實(shí)際上就是一個(gè)非常簡(jiǎn)單的代理,用于在攝取器和查詢器之間發(fā)送適當(dāng)?shù)恼?qǐng)求。
Querier
Querier(查詢器)用于查詢數(shù)據(jù),查詢器節(jié)點(diǎn)是完全無狀態(tài)的。數(shù)據(jù)查詢流程如下:
Querier Flow
- 使用 http API 接收搜索請(qǐng)求。接收到查詢請(qǐng)求的節(jié)點(diǎn)成為該查詢的 LEADER 查詢器。其他查詢器都是 WORKER 查詢器,用于查詢。
- LEADER 解析并驗(yàn)證 SQL。
- LEADER 找到數(shù)據(jù)時(shí)間范圍并從文件列表索引中獲取文件列表。
- LEADER 從集群元數(shù)據(jù)中獲取查詢器節(jié)點(diǎn)。
- LEADER 對(duì)每個(gè)查詢器要查詢的文件列表進(jìn)行分區(qū)。例如如果需要查詢 100 個(gè)文件,并且有 5 個(gè)查詢器節(jié)點(diǎn),則每個(gè)查詢器可以查詢 20 個(gè)文件,LEADER 處理 20 個(gè)文件,WORKERS 各處理 20 個(gè)文件。
- LEADER 調(diào)用每個(gè) WORKER 查詢器上運(yùn)行的 gRPC 服務(wù),將搜索查詢分派到查詢器節(jié)點(diǎn)。查詢器間通信使用 gRPC 進(jìn)行。
- LEADER 收集、合并并將結(jié)果發(fā)送回用戶。
提示:
- 默認(rèn)情況下,查詢器將在內(nèi)存中緩存 parquet 文件。我們可以使用環(huán)境變量 ZO_MEMORY_CACHE_MAX_SIZE 配置查詢器用于緩存的內(nèi)存大小。默認(rèn)緩存是使用特定查詢器可用內(nèi)存的 50% 來完成的。
- 在分布式環(huán)境中,每個(gè)查詢器節(jié)點(diǎn)只會(huì)緩存一部分?jǐn)?shù)據(jù)。
- 我們還可以選擇在內(nèi)存中緩存最新的 parquet 文件。當(dāng) Ingester 生成新的 Parquet 文件并將其上傳到對(duì)象存儲(chǔ)時(shí),Ingester 將通知查詢器緩存該文件。
Compactor
Compactor(壓縮器)會(huì)將小文件合并成大文件,使搜索更加高效。Compactor 還處理數(shù)據(jù)保留策略、full stream 刪除和文件列表索引更新。
AlertManager
AlertManager 運(yùn)行標(biāo)準(zhǔn)報(bào)警查詢、報(bào)告作業(yè)并發(fā)送通知。
安裝
由于 O2 用到的各個(gè)存儲(chǔ)可選方案較多,這里我們選擇使用 PostgreSQL 作為元數(shù)據(jù)存儲(chǔ),Minio 作為對(duì)象存儲(chǔ),Nats 作為集群協(xié)調(diào)器(建議使用 Nats,為了向后兼容,目前仍然支持 Etcd)。
接下來同樣我們使用 Helm 來安裝 O2:
helm repo add openobserve https://charts.openobserve.ai
helm repo update
官方的這個(gè) Helm Chart 默認(rèn)會(huì)部署 PostgreSQL,但是需要提前安裝對(duì)應(yīng)的 cloudnative-pg operator:
kubectl apply --server-side -f \
https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/release-1.23/releases/cnpg-1.23.1.yaml
然后我們就可以在 Helm Chart 中使用 PostgreSQL 了,此外為了啟用 Minio 和 Nats,我們需要在 values.yaml 中進(jìn)行配置,修改后的 values.yaml 如下所示:
# o2-values.yaml
# auth:
# ZO_ROOT_USER_EMAIL: "root@example.com" # default user email
# ZO_ROOT_USER_PASSWORD: "Complexpass#123" # default user password
ingester:
headless:
enabled: true
persistence:
enabled: true
storageClass: "nfs-client"
querier:
persistence: # If enabled it will be used for disk cache. Highly recommend to enable this for production
enabled: true
storageClass: "nfs-client"
service:
type: NodePort
nats:
enabled: true
container:
image:
repository: library/nats
registry: dhub.kubesre.xyz
config:
cluster:
enabled: true
replicas: 3
jetstream:
enabled: true
fileStore:
enabled: true
pvc:
enabled: true
size: 20Gi
storageClassName: "nfs-client"
natsBox:
container:
image:
registry: dhub.kubesre.xyz
reloader:
image:
registry: dhub.kubesre.xyz
minio:
enabled: true # if true then minio will be deployed as part of openobserve
mode: standalone # or distributed
persistence:
storageClass: "nfs-client"
consoleService:
type: NodePort
nodePort: 32001
# Postgres is used for storing openobserve metadata.
# Make sure to install cloudnative-pg operator before enabling this
postgres:
enabled: true
pgadmin:
enabled: false
spec:
instances: 2 # creates a primary and a replica. replica will become primary if the primary fails
storage:
size: 10Gi
pvcTemplate:
storageClassName: "nfs-client"
然后我們就可以安裝 O2 了:
$ helm upgrade --install o2 openobserve/openobserve -f o2-values.yaml --namespace openobserve --create-namespace
Release "o2" does not exist. Installing it now.
NAME: o2
LAST DEPLOYED: Sat Aug 24 17:29:23 2024
NAMESPACE: openobserve
STATUS: deployed
REVISION: 1
NOTES:
1. Get the application URL by running these commands:
export NODE_PORT=$(kubectl get --namespace openobserve -o jsnotallow="{.spec.ports[0].nodePort}" services o2-openobserve)
export NODE_IP=$(kubectl get nodes --namespace openobserve -o jsnotallow="{.items[0].status.addresses[0].address}")
echo http://$NODE_IP:$NODE_PORT
安裝完成后,我們可以通過 kubectl get pods -n openobserve 查看所有的 Pod 是否正常運(yùn)行。
$ kubectl get pods -n openobserve
NAME READY STATUS RESTARTS AGE
o2-minio-5ff8c55559-g6255 1/1 Running 0 43m
o2-nats-0 2/2 Running 0 32m
o2-nats-1 2/2 Running 0 33m
o2-nats-2 2/2 Running 0 33m
o2-nats-box-588fb755c4-6qxl2 1/1 Running 0 43m
o2-openobserve-alertmanager-95796f856-c6xlg 1/1 Running 0 43m
o2-openobserve-compactor-7f9b8cdb6b-kr8sp 1/1 Running 0 31m
o2-openobserve-ingester-0 1/1 Running 0 32m
o2-openobserve-postgres-1 1/1 Running 0 43m
o2-openobserve-postgres-2 1/1 Running 0 42m
o2-openobserve-querier-0 1/1 Running 0 43m
o2-openobserve-router-58dc4b8fd7-vl42p 1/1 Running 0 31m
$ kubectl get svc -n openobserve
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
o2-minio ClusterIP 10.96.2.46 <none> 9000/TCP 43m
o2-minio-console NodePort 10.96.0.196 <none> 9001:32001/TCP 43m
o2-nats ClusterIP 10.96.2.18 <none> 4222/TCP 43m
o2-nats-headless ClusterIP None <none> 4222/TCP,6222/TCP,8222/TCP 43m
o2-openobserve-alertmanager ClusterIP 10.96.1.79 <none> 5080/TCP,5081/TCP,5082/TCP 43m
o2-openobserve-compactor ClusterIP 10.96.0.207 <none> 5080/TCP,5081/TCP 43m
o2-openobserve-ingester ClusterIP 10.96.2.154 <none> 5080/TCP,5081/TCP 43m
o2-openobserve-ingester-headless ClusterIP None <none> 5080/TCP,5081/TCP 43m
o2-openobserve-postgres-r ClusterIP 10.96.0.123 <none> 5432/TCP 43m
o2-openobserve-postgres-ro ClusterIP 10.96.3.208 <none> 5432/TCP 43m
o2-openobserve-postgres-rw ClusterIP 10.96.0.165 <none> 5432/TCP 43m
o2-openobserve-querier ClusterIP 10.96.3.13 <none> 5080/TCP,5081/TCP 43m
o2-openobserve-router NodePort 10.96.3.228 <none> 5080:31984/TCP,5081:32212/TCP 43m
使用
安裝完成后,我們可以通過 http://<NODE_IP>:31984 訪問 O2 的 Web 界面,然后就可以開始使用了。
O2 Web
使用默認(rèn)的用戶名 root@example.com 和密碼 Complexpass#123 登錄,然后就可以看到 O2 的主界面了。
O2 Home
因?yàn)楝F(xiàn)在我們還沒有向 O2 中發(fā)送任何數(shù)據(jù),所以暫時(shí)沒有任何數(shù)據(jù)。我們可以切換到采集頁面,里面就有各種遙測(cè)數(shù)據(jù)的采集方式。
O2 采集
示例數(shù)據(jù)導(dǎo)入
這里我們可以先使用 JSON API 來加載一些示例日志數(shù)據(jù)來了解一下 OpenObserve 的基本使用方法。先使用下面命令下載示例日志數(shù)據(jù):
$ curl -L https://zinc-public-data.s3.us-west-2.amazonaws.com/zinc-enl/sample-k8s-logs/k8slog_json.json.zip -o k8slog_json.json.zip
$ unzip k8slog_json.json.zip
然后使用下面命令將示例日志數(shù)據(jù)導(dǎo)入到 OpenObserve 中:
$ curl http://<NodeIP>:31984/api/default/default/_json -i -u "root@example.com:Complexpass#123" -d "@k8slog_json.json"
HTTP/1.1 100 Continue
HTTP/1.1 200 OK
content-length: 71
content-type: application/json
x-api-node: o2-openobserve-ingester-0
access-control-allow-credentials: true
date: Sat, 24 Aug 2024 10:31:05 GMT
vary: accept-encoding
{"code":200,"status":[{"name":"default","successful":3846,"failed":0}]}%
收據(jù)導(dǎo)入成功后,刷新頁面即可看到有數(shù)據(jù)了:
OpenObserve Web
在數(shù)據(jù)流頁面可以看到我們導(dǎo)入的數(shù)據(jù)元信息:
Stream流
然后切換到日志頁面,從左側(cè)的下拉列表中選擇索引為 default,就可以看到日志數(shù)據(jù)了:
O2 Logs
現(xiàn)在我們就可以去根據(jù)直接的需求去查詢?nèi)罩玖耍热缫褂玫古潘饕阉靼瑔卧~ error 的所有字段:
- 在查詢編輯器中使用 match_all('error')
- match_all 僅搜索配置為全文搜索的字段。默認(rèn)字段集包括 log、message、content、data、events、json。如果你希望全文搜索時(shí)掃描更多字段,可以在數(shù)據(jù)流頁面進(jìn)行配置。在特定字段中進(jìn)行全文搜索應(yīng)該使用 str_match 函數(shù)。
要在沒有倒排索引的情況下搜索包含單詞 error 的所有字段:
- match_all_raw('error'),區(qū)分大小寫。
- match_all_raw_ignore_case(error),這是不區(qū)分大小寫的。
- match_all_raw 函數(shù)僅搜索配置為全文搜索的字段。默認(rèn)字段集是 log、message、content、data、events、json。如果希望全文搜索時(shí)掃描更多字段,可以在數(shù)據(jù)流設(shè)置下進(jìn)行配置。應(yīng)該使用 str_match 在指定字段中進(jìn)行全文搜索。
如果僅搜索 log 字段中的 error,可以使用 str_match(log, 'error'),這比 match_all_raw 更有效,因?yàn)樗趩蝹€(gè)字段中搜索。
同樣如果我們要搜索包含 code 為 200 的所有日志條目,則可以直接使用 code=200 查詢即可,其中 code 是一個(gè)數(shù)字字段。如果要搜索 code 字段不包含任何值的所有日志條目,可以使用 code is null,但是需要注意使用 code=' ' 不會(huì)產(chǎn)生正確的結(jié)果;同樣的搜索 code 字段具有某個(gè)值的所有日志條目,則可以使用 code is not null,但是不能使用 code!=' '。另外也可以使用 code > 399 或者 code >= 400 來搜索 code 大于 399 或者大于等于 400 的所有日志條目。
此外我們還可以勾選 SQL 模式 來使用 SQL 查詢語句來查詢?nèi)罩緮?shù)據(jù):
SQL Model
同樣現(xiàn)在我們可以去 Minio 中查看數(shù)據(jù)是否已經(jīng)被正確的存儲(chǔ),使用用戶名 rootuser 和密碼 rootpass123 即可登錄成功。
minio console
可以看到對(duì)象存儲(chǔ)中已經(jīng)有了我們的 parquet 文件數(shù)據(jù)。
采集 Kubernetes 觀測(cè)數(shù)據(jù)
O2 還支持通過 OpenTelemetry 采集器導(dǎo)入數(shù)據(jù)。這里我們可以使用 OpenTelemetry 來采集 Kubernetes 集群的觀測(cè)數(shù)據(jù),首先我們需要安裝 OpenTelemetry 的 Operator,由于 OpenTelemetry 的 Operator 依賴 cert-manager,所以我們需要先安裝 cert-manager:
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.1/cert-manager.yaml
等待 cert-manager 安裝完成后,我們就可以安裝 OpenTelemetry 的 Operator 了:
$ kubectl get pods -n cert-manager
NAME READY STATUS RESTARTS AGE
cert-manager-cainjector-65c7bff89d-rxjts 1/1 Running 0 64s
cert-manager-cbcf9668d-tlz2k 1/1 Running 0 64s
cert-manager-webhook-594cb9799b-c5sd8 1/1 Running 0 64s
$ kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml
$ kubectl get pods -n opentelemetry-operator-system
NAME READY STATUS RESTARTS AGE
opentelemetry-operator-controller-manager-fd5689558-lchr7 2/2 Running 0 61s
然后我們就可以來安裝 OpenTelemetry 的 Collector 了,為了方便使用 O2 官方的 Helm Chart 已經(jīng)提供了一個(gè)定制的 Chart 包,我們可以直接使用它,該采集器可以:
- 從 K8s 集群中捕獲日志、指標(biāo)和 Events 事件并將其發(fā)送到 O2
- 在集群中配置 auto-instrumentation 自動(dòng)埋點(diǎn),以捕獲 Java、.Net、nodejs、python 和 Go 語言編寫的應(yīng)用程序的鏈路追蹤
使用我們這里安裝的 O2 集群的認(rèn)證信息覆蓋默認(rèn)的認(rèn)證信息:
# o2c-values.yaml
exporters:
otlphttp/openobserve:
endpoint: http://o2-openobserve-router.openobserve.svc.cluster.local:5080/api/default
headers:
Authorization: Basic cm9vdEBleGFtcGxlLmNvbTpScmtORWxpenhCWWV4Qzhr # 替換成自己的
stream-name: k8s_logs # 替換成自己的數(shù)據(jù)流名稱
otlphttp/openobserve_k8s_events:
endpoint: http://o2-openobserve-router.openobserve.svc.cluster.local:5080/api/default
headers:
Authorization: Basic cm9vdEBleGFtcGxlLmNvbTpScmtORWxpenhCWWV4Qzhr # 替換成自己的
stream-name: k8s_events
agent:
enabled: true
tolerations: # 如果需要在 master 節(jié)點(diǎn)上運(yùn)行,需要添加這個(gè)容忍
- key: "node-role.kubernetes.io/control-plane"
operator: "Exists"
effect: "NoSchedule"
其中 endpoint 地址為 O2 的 router 的 FQDN 地址,Authorization 中 Basic 后面 的值為用戶名:TOKEN的 base64 編碼(可以在 O2 采集頁面獲?。?,stream-name 為數(shù)據(jù)流名稱。然后直接使用下面命令安裝 OpenTelemetry 的 Collector 即可:
$ helm upgrade --install o2c openobserve/openobserve-collector -f o2c-values.yaml --namespace openobserve-collector --create-namespace
Release "o2c" does not exist. Installing it now.
NAME: o2c
LAST DEPLOYED: Sun Aug 25 10:30:59 2024
NAMESPACE: openobserve-collector
STATUS: deployed
REVISION: 1
TEST SUITE: None
NOTES:
If everything proceeded without errors then your cluster is now sending logs and metrics to OpenObserve.
You can add following to your pods/namespaces to auto instrument your applications to send traces:
1. Java: instrumentation.opentelemetry.io/inject-java: "openobserve-collector/openobserve-java"
2. NodeJS: instrumentation.opentelemetry.io/inject-nodejs: "openobserve-collector/openobserve-nodejs"
3. Python: instrumentation.opentelemetry.io/inject-python: "openobserve-collector/openobserve-python"
4. DotNet: instrumentation.opentelemetry.io/inject-dotnet: "openobserve-collector/openobserve-dotnet"
5. Go: instrumentation.opentelemetry.io/inject-go: "openobserve-collector/openobserve-go" , instrumentation.opentelemetry.io/otel-go-auto-target-exe: "/path/to/container/executable"
6. OpenTelemetry SDK environment variables only: instrumentation.opentelemetry.io/inject-sdk: "true"
我們可以查看 OpenTelemetry 的 Collector 是否正常運(yùn)行:
$ kubectl get pods -n openobserve-collector
NAME READY STATUS RESTARTS AGE
o2c-openobserve-collector-agent-collector-46r82 1/1 Running 0 10m
o2c-openobserve-collector-agent-collector-cbtnn 1/1 Running 0 10m
o2c-openobserve-collector-agent-collector-kkczz 1/1 Running 0 10m
o2c-openobserve-collector-gateway-collector-0 1/1 Running 0 10m
o2c-openobserve-collector-gateway-targetallocator-59c4468dxmfts 1/1 Running 0 10m
其實(shí)這里我們部署的采集器就是一個(gè) agent 和一個(gè) gateway,在前面的額 OpenTelemetry 章節(jié)已經(jīng)學(xué)習(xí)過了,只是這里我們是將數(shù)據(jù)導(dǎo)出到了 O2 中。
現(xiàn)在我們就可以去 O2 中查看我們的數(shù)據(jù)了,切換到數(shù)據(jù)流頁面,可以看到我們的數(shù)據(jù)流已經(jīng)被成功的導(dǎo)入了:
K8s 數(shù)據(jù)流
可以看到現(xiàn)在我們的數(shù)據(jù)流中已經(jīng)有了 k8s_logs 和 k8s_events 兩個(gè)日志數(shù)據(jù)流,分別對(duì)應(yīng)我們的日志和事件數(shù)據(jù)。除此之外還有很多 metrics 類型的數(shù)據(jù)流,可以看到指標(biāo)中的每一個(gè)標(biāo)簽都是一個(gè)獨(dú)立的 stream 數(shù)據(jù)流。
我們可以切換到日志頁面,選擇數(shù)據(jù)流 k8s_logs,就可以看到我們采集的 K8s 集群的日志數(shù)據(jù)了:
K8s Logs
同樣我們選擇數(shù)據(jù)流 k8s_events,就可以看到我們采集的 K8s 集群的事件數(shù)據(jù)了:
K8s Events
對(duì)于指標(biāo)數(shù)據(jù)流,我們可以切換到指標(biāo)頁面,選擇數(shù)據(jù)流 k8s_metrics,就可以看到我們采集的 K8s 集群指標(biāo)數(shù)據(jù)了:
K8s Metrics
左側(cè)我們可以選擇一個(gè)指標(biāo),然后就可以看到這個(gè)指標(biāo)的標(biāo)簽列表,然后我們就可以在右側(cè)填寫 PromQL 語句來查詢數(shù)據(jù)了,比如我們這里查詢每個(gè)命名空間的內(nèi)存使用情況,則可以使用下面的 PromQL 語句:
sum(k8s_pod_memory_usage) by (k8s_namespace_name)
查詢結(jié)果如下圖所示:
PromQL 查詢
如果我們需要經(jīng)常查詢這個(gè)指標(biāo),這可以將其添加到儀表盤中,點(diǎn)擊頁面中的添加到儀表盤按鈕即可,然后在儀表盤頁面就可以看到我們的儀表盤了:
儀表盤
和 Grafana 類似,我們也可以編輯面板,在面板右上角點(diǎn)擊Edit Panel,就可以進(jìn)入面板編輯頁面了,點(diǎn)擊最右側(cè)的配置按鈕,就可以編輯面板了,比如我們這里可以選擇圖例的單位、圖例的顯示名稱等,編輯后可以點(diǎn)擊應(yīng)用按鈕預(yù)覽,如果滿意可以點(diǎn)擊保存按鈕保存:
編輯面板
同樣我們也可以和 Grafana 一樣配置一個(gè)變量,然后在面板上展示這個(gè)變量,比如我們這合理可以添加一個(gè) namespace 變量,其值可以從 k8s_namespace_phase 這個(gè)數(shù)據(jù)流中獲取,如下圖所示:
變量設(shè)置
當(dāng)然現(xiàn)在我們需要去修改下面板的 PromQL 查詢語句,需要將變量 namespace 傳入,更改成:sum(k8s_pod_memory_usage{k8s_namespace_name =~ "$namespace"}) by (k8s_namespace_name) 即可:
修改PromQL
現(xiàn)在我們就可以在面板上通過篩選 namespace 變量來查看不同命名空間的內(nèi)存使用情況了:
篩選面板
接下來還有鏈路追蹤的數(shù)據(jù),因?yàn)樯厦嫖覀儼惭b的 OpenTelemetry 的 Collector 已經(jīng)自動(dòng)為我們創(chuàng)建了自動(dòng)埋點(diǎn)的 Instrumentation 對(duì)象,如下所示:
$ kubectl get instrumentation -n openobserve-collector
NAME AGE ENDPOINT SAMPLER SAMPLER ARG
openobserve-dotnet 99m http://o2c-openobserve-collector-gateway-collector.openobserve-collector.svc.cluster.local:4318 parentbased_traceidratio 1
openobserve-go 99m http://o2c-openobserve-collector-gateway-collector.openobserve-collector.svc.cluster.local:4318 parentbased_traceidratio 1
openobserve-java 99m http://o2c-openobserve-collector-gateway-collector.openobserve-collector.svc.cluster.local:4318 parentbased_traceidratio 1
openobserve-nodejs 99m http://o2c-openobserve-collector-gateway-collector.openobserve-collector.svc.cluster.local:4317 parentbased_traceidratio 1
openobserve-python 99m http://o2c-openobserve-collector-gateway-collector.openobserve-collector.svc.cluster.local:4318 parentbased_traceidratio 1
可以看到上面的 Instrumentation 對(duì)象已經(jīng)配置了端點(diǎn)地址、采樣器和采樣器參數(shù),地址就是我們的 OpenTelemetry 的 Collector 的地址(gateway 類型的),采樣器是 parentbased_traceidratio,參數(shù)是 1,表示采樣率為 1。
針對(duì)上面的這些語言編寫的應(yīng)用,我們就可以進(jìn)行自動(dòng)埋點(diǎn),只需要在應(yīng)用的 Pod 中添加對(duì)應(yīng)的 Annotation 即可:
- Java: instrumentation.opentelemetry.io/inject-java: "openobserve-collector/openobserve-java"
- DotNet: instrumentation.opentelemetry.io/inject-dotnet: "openobserve-collector/openobserve-dotnet"
- NodeJS: instrumentation.opentelemetry.io/inject-nodejs: "openobserve-collector/openobserve-nodejs"
- Python: instrumentation.opentelemetry.io/inject-python: "openobserve-collector/openobserve-python"
- Go (Uses eBPF):
- instrumentation.opentelemetry.io/inject-go: "openobserve-collector/openobserve-go"
- instrumentation.opentelemetry.io/otel-go-auto-target-exe: "/path/to/container/executable"
這里我們可以部署一個(gè)微服務(wù) HOT Commerce 來演示在 Kubernetes 環(huán)境中如何使用 OpenTelemetry Operator 進(jìn)行自動(dòng)埋點(diǎn),并將數(shù)據(jù)發(fā)送到 OpenObserve 中。
這個(gè)應(yīng)用包含 5 個(gè)簡(jiǎn)單的微服務(wù),每個(gè)微服務(wù)都是用不同的編程語言編寫的:
/-> review (python)
/
frontend (go) -> shop (nodejs) -> product (java)
\
\-> price (dotnet)
該應(yīng)用對(duì)應(yīng)的 Kubernetes 資源清單文件如下:
# hotcommerce.yaml
apiVersion: v1
kind: Service
metadata:
name: price
namespace: hotcommerce
spec:
selector:
app: price
ports:
- protocol: TCP
port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: price
namespace: hotcommerce
spec:
replicas: 1
selector:
matchLabels:
app: price
template:
metadata:
labels:
app: price
annotations:
instrumentation.opentelemetry.io/inject-dotnet: "openobserve-collector/openobserve-dotnet"
spec:
containers:
- name: price
image: public.ecr.aws/zinclabs/sample-price-service-dotnet:3
imagePullPolicy: Always
ports:
- containerPort: 80
resources:
limits:
cpu: "1"
memory: "544Mi"
requests:
cpu: "100m"
memory: "448Mi"
---
apiVersion: v1
kind: Service
metadata:
name: review
namespace: hotcommerce
spec:
selector:
app: review
ports:
- protocol: TCP
port: 80
targetPort: 8004
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: review
namespace: hotcommerce
spec:
replicas: 1
selector:
matchLabels:
app: review
template:
metadata:
labels:
app: review
annotations:
instrumentation.opentelemetry.io/inject-python: "openobserve-collector/openobserve-python"
spec:
containers:
- name: review
image: public.ecr.aws/zinclabs/sample-review-service:51
imagePullPolicy: Always
ports:
- containerPort: 8004
resources:
limits:
cpu: "1"
memory: "544Mi"
requests:
cpu: "100m"
memory: "448Mi"
---
apiVersion: v1
kind: Service
metadata:
name: product
namespace: hotcommerce
spec:
selector:
app: product
ports:
- protocol: TCP
port: 80
targetPort: 8003
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: product
namespace: hotcommerce
spec:
replicas: 1
selector:
matchLabels:
app: product
template:
metadata:
labels:
app: product
annotations:
instrumentation.opentelemetry.io/inject-java: "openobserve-collector/openobserve-java"
spec:
containers:
- name: product
image: public.ecr.aws/zinclabs/sample-product-service-java:53
imagePullPolicy: Always
env:
- name: REVIEW_SERVICE_URL
value: "http://review.hotcommerce.svc.cluster.local"
- name: PRICE_SERVICE_URL
value: "http://price.hotcommerce.svc.cluster.local"
ports:
- containerPort: 8003
resources:
limits:
cpu: "1"
memory: "1048Mi"
requests:
cpu: "100m"
memory: "1048Mi"
---
apiVersion: v1
kind: Service
metadata:
name: shop
namespace: hotcommerce
spec:
selector:
app: shop
ports:
- protocol: TCP
port: 80
targetPort: 8002
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: shop
namespace: hotcommerce
spec:
replicas: 1
selector:
matchLabels:
app: shop
template:
metadata:
labels:
app: shop
annotations:
instrumentation.opentelemetry.io/inject-nodejs: "openobserve-collector/openobserve-nodejs"
spec:
containers:
- name: shop
image: public.ecr.aws/zinclabs/sample-shop-service-nodejs:51
imagePullPolicy: Always
env:
- name: PRODUCT_SERVICE_URL
value: "http://product.hotcommerce.svc.cluster.local"
ports:
- containerPort: 8002
resources:
limits:
cpu: "1"
memory: "544Mi"
requests:
cpu: "100m"
memory: "448Mi"
---
apiVersion: v1
kind: Service
metadata:
name: frontend
namespace: hotcommerce
spec:
type: NodePort
selector:
app: frontend
ports:
- protocol: TCP
port: 80
targetPort: 8001
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
namespace: hotcommerce
spec:
replicas: 1
selector:
matchLabels:
app: frontend
template:
metadata:
labels:
app: frontend
annotations:
instrumentation.opentelemetry.io/inject-go: "openobserve-collector/openobserve-go"
instrumentation.opentelemetry.io/otel-go-auto-target-exe: "/app"
spec:
containers:
- name: frontend
image: public.ecr.aws/zinclabs/sample-frontend-service-go:51
imagePullPolicy: Always
env:
- name: SHOP_SERVICE_URL
value: "http://shop.hotcommerce.svc.cluster.local"
ports:
- containerPort: 8001
resources:
limits:
cpu: "1"
memory: "543Mi"
requests:
cpu: "100m"
memory: "438Mi"
注意上面的每個(gè) Deployment 應(yīng)用中,我們都添加了對(duì)應(yīng)的 instrumentation.opentelemetry.io/inject-xxx 的 Annotation,這樣就可以實(shí)現(xiàn)自動(dòng)埋點(diǎn)了。
直接使用下面命令部署 HOT Commerce 應(yīng)用即可:
kubectl create ns hotcommerce
kubectl apply -f hotcommerce.yaml
部署完成后,我們先查看所有的服務(wù)是否正常運(yùn)行:
$ kubectl get pods -n hotcommerce
NAME READY STATUS RESTARTS AGE
frontend-57c47854f7-8cwbp 1/1 Running 0 6m56s
price-5d99b949b8-gzqr6 1/1 Running 0 6m56s
product-85f94894f-7s8hw 1/1 Running 0 6m56s
review-98bc7596f-ptbhk 1/1 Running 0 6m57s
shop-966895886-hgpxt 1/1 Running 0 6m56s
$ kubectl get svc -n hotcommerce
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
frontend NodePort 10.96.3.25 <none> 80:30826/TCP 4m42s
price ClusterIP 10.96.1.147 <none> 80/TCP 4m43s
product ClusterIP 10.96.3.134 <none> 80/TCP 4m42s
review ClusterIP 10.96.1.22 <none> 80/TCP 4m43s
shop ClusterIP 10.96.2.4 <none> 80/TCP 4m42s
我們可以通過 http://<NodeIP>:30826 訪問 frontend 服務(wù)了,當(dāng)我們?cè)L問 frontend 服務(wù)時(shí),OpenTelemetry 會(huì)自動(dòng)為我們的應(yīng)用程序生成鏈路追蹤數(shù)據(jù),并將其發(fā)送到 OpenTelemetry Collector,然后 Collector 會(huì)將數(shù)據(jù)發(fā)送到 OpenObserve 中。
$ curl http://192.168.0.111:30826/item/1
{"description":"This is a sample product.","id":1,"in_stock":10,"name":"Sample Product","price":100,"rating":5,"review":"This is a great product!","warehouse_location":"A1-B2"}
然后我們就可以在 O2 中查看到有 traces 類型的數(shù)據(jù)流了。切換到追蹤頁面,就可以看到我們采集到的鏈路追蹤數(shù)據(jù)了:
K8s Traces
點(diǎn)擊一個(gè) Trace ID,就可以查看這個(gè)鏈路追蹤的詳細(xì)信息了:
Trace Detail
這里我們可以看到這個(gè)鏈路追蹤的詳細(xì)信息,包括每個(gè)服務(wù)的調(diào)用時(shí)間、調(diào)用耗時(shí)、調(diào)用結(jié)果等信息。
到這里我們就實(shí)現(xiàn)了在 OpenObserve 中查看我們的 K8s 集群的觀測(cè)數(shù)據(jù),當(dāng)然同樣的對(duì)于非 K8s 集群的應(yīng)用也可以直接將數(shù)據(jù)采集到 OpenObserve 中,比如 Fluentd、Vector、Prometheus、OpenTelemetry Collector 等。
此外 O2 還有很多其他功能,比如報(bào)警、函數(shù)、前端性能監(jiān)控等,可以關(guān)注后續(xù)文章。
參考文檔
- https://openobserve.ai/docs/。