Part 1 NixOSをinstall
Part 2 deply-rsでNixOS Configurationを適用
Part 3 ragenixでsecret管理
Part 4 opentelemetry-collectorとopenobserveでmetricsを取得(👈 この記事)
Part 5 CPUの温度をmetricsとして取得
Part 4ではraspi上でopentelemetry-collector(contrib destribution)を動かしてmetricsをとれることを目指します。 metricsのexport先はopenobserveを利用します。今回はcloud版を利用します。
現在のところ、openobserveのcloud版は月200GB ingestion, 15日間保持までならfree planで利用できます。
またcluster構成でなければ自前でhostするのもやりやすいのでいずれはraspi上で動かせればと考えていますが、CHANGELOGをみているとまだまだ開発中といった感じなのでもうすこし安定してからにしようかと思っています。
openobserveへのmetricsのexportには組織情報やcredentialが必要です。これはPart 3で準備してあり、既にdeploy済である前提です。
利用するopentelemetry-collectorのversionはcontrib版のv0.78.0です。
systemdでopentelemetry-collectorを動かす
基本的にやることは簡単で、systemdからopentelemetry-collectorを起動する設定を行うだけです。
自分はsystemdがよくわかっていなかったので、Linux Service Management Made Easy with systemdを読んでみました。
こちらの本は非常に参考になり別で感想を書こうと思っています。
opentelemetryやcollectorとはそもそもなにかについては以前、記事を書いたのでよければ読んでみてください。
ということでnixの設定に戻ります。
まず、opentelemetry-collecotr用にmodules/opentelemetry-collector/ directoryを作成して、そこにdefault.nixを以下のように定義します。
{ pkgs, config, mysecrets, ... }:
let
otelColUser = "opentelemetry-collector";
otelColGroup = otelColUser;
in
{
# Create user statically for age to execute chown
users = {
groups."opentelemetry-collector" = {
name = "${otelColGroup}";
};
users."opentelemetry-collector" = {
name = "${otelColUser}";
isSystemUser = true;
group = "${otelColGroup}";
};
};
# Credential for opentelemetry-collector to export telemetry to openobserve cloud
age.secrets."openobserve" = {
file = "${mysecrets}/openobserve.age";
mode = "0440";
owner = "${otelColUser}";
group = "${otelColGroup}";
};
# Put opentelemetry-collector config file
environment.etc = {
"opentelemetry-collector/config.yaml" = {
mode = "0440";
user = "${otelColUser}";
group = "${otelColGroup}";
text = builtins.readFile ./config.yaml;
};
};
# Enable opentelemetry-collecotr service
systemd.services.opentelemetry-collector = {
description = "Opentelemetry Collector Serivice";
wantedBy = [ "multi-user.target" ];
serviceConfig = let
conf =
"${config.environment.etc."opentelemetry-collector/config.yaml".source.outPath}";
ExecStart =
"${pkgs.opentelemetry-collector-contrib}/bin/otelcontribcol --config=file:${conf}";
in {
inherit ExecStart;
EnvironmentFile = [
# referenced by environment variable substitution in config file like '${env:FOO}'
config.age.secrets.openobserve.path
];
# age executes chown on secret files, so user and group should exists in advance
DynamicUser = false;
User = "${otelColUser}";
Group = "${otelColGroup}";
Restart = "always";
ProtectSystem = "full";
DevicePolicy = "closed";
NoNewPrivileges = true;
WorkingDirectory = "/var/lib/opentelemetry-collector";
StateDirectory = "opentelemetry-collector";
};
};
}このmoduleをimportしてdeployすればopentelemetry-collectorが起動して、openobserveにmetricsをexportしてくれます。
実行コマンドはcontrib版なのでotelcontribcolです。
opentelemetry-collectorの設定fileはmodules/opentelemetry-collector/config.yamlに定義しており、
environment.etc = {
"opentelemetry-collector/config.yaml" = {
mode = "0440";
user = "${otelColUser}";
group = "${otelColGroup}";
text = builtins.readFile ./config.yaml;
};
};とすることで、raspi上に/etc/opentelemetry-collector/config.yamlが作成されます。
参照する際は
systemd.services.opentelemetry-collector = {
# ...
serviceConfig = let
conf =
"${config.environment.etc."opentelemetry-collector/config.yaml".source.outPath}";
ExecStart =
"${pkgs.opentelemetry-collector-contrib}/bin/otelcontribcol --config=file:${conf}";
in {
inherit ExecStart;
# ...
};
};のようにconfig.environment.etc."opentelemetry-collector".config.yaml.source.outPathを参照できます。
opentelemetry-collector config.yaml
続いてopentelemetry-collectorの設定fileですが、以下のように定義しました。
receivers:
hostmetrics:
collection_interval: 1m
scrapers:
cpu:
metrics:
system.cpu.time: { enabled: false }
system.cpu.utilization: { enabled: true }
memory:
metrics:
system.memory.usage: { enabled: false }
system.memory.utilization: { enabled: true }
network:
include:
interfaces: ["end0"]
match_type: strict
metrics:
system.network.connections: { enabled: true }
system.network.dropped: { enabled: false }
system.network.errors: { enabled: false }
system.network.io: { enabled: true }
system.network.packets: { enabled: false }
hostmetrics/fs:
collection_interval: 60m
scrapers:
filesystem:
exclude_mount_points:
mount_points: ["/nix/store"]
match_type: strict
metrics:
system.filesystem.inodes.usage: { enabled: false }
system.filesystem.usage: { enabled: false }
system.filesystem.utilization: { enabled: true }
processors:
memory_limiter:
check_interval: 10s
# hard limit
limit_mib: 500
# sort limit 400
spike_limit_mib: 100
resourcedetection/system:
detectors: ["system"]
override: false
attributes: ["host.name"]
system:
hostname_sources: ["os"]
filter/metrics:
error_mode: propagate
metrics:
datapoint:
- >-
metric.name == "system.cpu.utilization"
and
(
attributes["state"] == "nice"
or
attributes["state"] == "softirq"
or
attributes["state"] == "steal"
or
attributes["state"] == "interrupt"
)
- >-
metric.name == "system.network.connections"
and
attributes["state"] != "ESTABLISHED"
and
attributes["state"] != "LISTEN"
batch/metrics:
send_batch_size: 8192
timeout: 3s
send_batch_max_size: 16384
exporters:
logging:
verbosity: "normal"
prometheusremotewrite/openobserve:
endpoint: https://api.openobserve.ai/api/${env:OPEN_OBSERVE_ORG}/prometheus/api/v1/write
headers:
Authorization: Basic ${env:OPEN_OBSERVE_TOKEN}
resource_to_telemetry_conversion:
enabled: true
extensions:
memory_ballast:
size_mib: 64
service:
extensions: [memory_ballast]
pipelines:
metrics:
receivers:
- hostmetrics
- hostmetrics/fs
processors:
- memory_limiter
- resourcedetection/system
- filter/metrics
- batch/metrics
exporters:
- logging
- prometheusremotewrite/openobserve
まずmetricsはhostmetricsを利用しました。
processorsに関しては
processors:
memory_limiter:
check_interval: 10s
# hard limit
limit_mib: 500
# sort limit 400
spike_limit_mib: 100まずmemory_limiterを設定し
resourcedetection/system:
detectors: ["system"]
override: false
attributes: ["host.name"]
system:
hostname_sources: ["os"]次にresourcedetectionで、metricsにhost情報を付与しました。
host名は、nixosの設定で定義しています。
続いて
filter/metrics:
error_mode: propagate
metrics:
datapoint:
- >-
metric.name == "system.cpu.utilization"
and
(
attributes["state"] == "nice"
or
attributes["state"] == "softirq"
or
attributes["state"] == "steal"
or
attributes["state"] == "interrupt"
)
- >-
metric.name == "system.network.connections"
and
attributes["state"] != "ESTABLISHED"
and
attributes["state"] != "LISTEN"filterでcpuのいくつかの状態とconnectionで知りたいものを取得するようにしました。
最後にbatchを設定しました。
batch/metrics:
send_batch_size: 8192
timeout: 3s
send_batch_max_size: 16384exporterに関してはopenobserveはmetricsの書き込みにpromethesremotewriteを要求するので、指定された通りに行います。
exporters:
logging:
verbosity: "normal"
prometheusremotewrite/openobserve:
endpoint: https://api.openobserve.ai/api/${env:OPEN_OBSERVE_ORG}/prometheus/api/v1/write
headers:
Authorization: Basic ${env:OPEN_OBSERVE_TOKEN}
resource_to_telemetry_conversion:
enabled: true${env:OPEN_OBSERVE_ORG}のように書くと環境変数で置換してくれます。
環境変数は、systemdのEnvironmentFile経由で、復号したage fileを渡しています。
まとめ
この設定をdeployし、openobserveでdashboardを設定することで以下のようなmetricsを確認できるようになりました。
openobserveではmetricsにPromQLが利用できるので、sum without(cpu,state) (system_cpu_utilization_ratio{host_name=~"rpi4",state!="idle"}) * 100のようにして簡単にdashboardが作れます。
ここまでで、NixOSの設定をraspi上に反映し、metricsを取得できるようになりました。
次はRustでraspiのcpu温度を取得し、opentelemetryのmetricsとしてexportするようなapplicationを作ってみようと思っています。
ここまでお読みいただき、ありがとうございました。