1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SONiCのsystemdを読みながら好きなツールを動かしてみる

Posted at

はじめに

さくらインターネット Advent Calendar 2025 の22日目 シリーズ2となります。

この記事では、ホワイトボックスで用いるネットワークOS SONiCへ好きなツールを仕込みたいという欲求の元、内部で動くsystemdの仕組みを読み解いていきます。

なぜやるのか

私は普段さくらインターネットでGPUインフラのネットワーク設計に携わっており、その中で、SONiCを駆使する機会がありました。

SONiC自体オープンソースで日々成長中ということで、監視や運用に関する部分は、別途自分たちで手を入れたいという気持ちがありました。
今回は、その一環でツール(世にいうミドルウェア)を仕込むため、この取り組みが必要でした。

(お約束ですが、この記事は個人的な知識を元に再構築したメモのため、不明点があるときは個別にご連絡ください)

そもそもSONiCとは

SONiC(Software for Open Networking in the Cloud) は、MicrosoftがAzureのために開発したオープンソースのネットワークOSです。

現在は Microsoft から The Linux Foundation に寄贈され、コミュニティ主導で開発が続けられています。その特徴をざっくりまとめると下記のようになります。

  • Debian Linux をベースにしたネットワークOS
  • FRR / SWSS / redisなど複数のDockerコンテナで構成されている
  • ASIC を抽象化する SAI(Switch Abstraction Interface) によってマルチベンダのスイッチに対応

入手方法は大きく分けて3つとなります。

systemdを読んでみる

SONiCはDebianをベースとしているため、ソフトウェアの起動順序もLinuxを由来としたsystemdベースで管理がされています。
実際に下記コマンドでその起動順(依存関係)を確認することができます。

$ systemctl list-dependencies
default.target
● ├─display-manager.service
● ├─e2scrub_reap.service
● ├─kexec-load.service
● ├─kexec.service
● ├─monit.service
● ├─sysfsutils.service
● ├─systemd-update-utmp-runlevel.service
● └─multi-user.target
~snip~
●   └─sonic.target
●     ├─aaastatsd.timer
●     ├─backend-acl.service
●     ├─bgp.service
●     ├─caclmgrd.service
●     ├─copp-config.service
●     ├─ctrmgrd.service
●     ├─dhcp_relay.service
●     ├─eventd.service
●     ├─gbsyncd.service
●     ├─hostcfgd.timer
●     ├─hostname-config.service
●     ├─iccpd.service
●     ├─interfaces-config.service
●     ├─macsec.service
●     ├─mux.service
●     ├─nat.service
●     ├─node_exporter.service
●     ├─ntp-config.service
●     ├─pmon.service
●     ├─procdockerstatsd.service
●     ├─radv.service
●     ├─rsyslog-config.service
●     ├─service-restart.service
●     ├─sflow.service
●     ├─stp.service
●     ├─swss.service
●     ├─syncd.service
●     ├─tacacs-config.timer
●     └─teamd.service

例としてbgp.service(ルーティング処理をするFRR)のunit fileを開くと、下記の通り記載されています。

$ cat /etc/systemd/system/sonic.target.wants/bgp.service
[Unit]
Description=BGP container
Requires=database.service
After=database.service
Requires=updategraph.service
After=updategraph.service
BindsTo=sonic.target
After=sonic.target
Before=ntp-config.service
After=swss.service
After=pmon.service
After=interfaces-config.service
StartLimitIntervalSec=1200
StartLimitBurst=3

[Service]
User=admin
ExecStartPre=/usr/local/bin/bgp.sh start
ExecStart=/usr/local/bin/bgp.sh wait
ExecStop=/usr/local/bin/bgp.sh stop
ExecStopPost=/usr/local/bin/write_standby.py --shutdown bgp

RestartSec=30

[Install]
WantedBy=sonic.target

この結果を見ると、sonic.targetにFRRを始めとした内部サービスがぶら下がっていることが、なんとなく判ります。
(実際にはこのbgp.shの中身も読んで判断しています)

言い換えれば、sonic.target配下のサービスが起動するまで、起動は終わっていないということです。
そのため、新しいツールの起動は終わるまで待った方が無難そうです。

今回追加するツールもsonic.targetの後に起動するようにします。

systemdにnode exporterを登録

今回、例としてPrometheusのnode exporterを新規サービスとして登録してみます。
/usr/local/bin/にbinaryを配置した前提で、systemdで起動の設定をします。

  • Before: 事前起動
  • After : 該当サービスが起動後に起動

上記を元にして、下記のunit fileを作成してみます。

$ cat /etc/systemd/system/node_exporter.service
[Unit]
Description=Prometheus Node Exporter
After=sonic.target

[Service]
ExecStart=/usr/local/bin/node_exporter
Restart=always

[Install]
WantedBy=sonic.target

sonic関連のサービス(sonic.target)の後に起動してあげる、という記載です。

上記を作成したら、systemdのunitの再読み込みをすることで、登録したサービスをsystemdで管理できるようになります。

以下で、サービスの起動、bootup時の起動を有効にします。


$ sudo systemctl daemon-reload
$ sudo systemctl start node_exporter
$ sudo systemctl enable node_exporter

念のためstatusを確認すると、下記のように無事登録されているのがわかります。

$ sudo systemctl status node_exporter
● node_exporter.service - Prometheus Node Exporter
     Loaded: loaded (/etc/systemd/system/node_exporter.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2025-12-17 09:16:43 JST; 26s ago
   Main PID: 2915419 (node_exporter)
      Tasks: 9 (limit: 18504)
     Memory: 11.9M
     CGroup: /system.slice/node_exporter.service
             └─2915419 /usr/local/bin/node_exporter

ちなみに

先ほど例として出したbgp.serviceをはじめ、各ユニットファイルは下記の jinja templateを元にして生成されています。

この記事を読んだそこのあなたが、より深くSONiCのsystemdについて理解したいと思ったなら、そのときはここを読むと理解が早いかもしれません。

sonic-buildimage(files/build_templates)

動作確認

最後にprometheus相当のLinuxサーバーからcurlでアクセス確認をしてみました。

無事動作してnode exporterによる監視が実現できることがわかります!

$ curl http://a.b.c.d:9100/metrics
# HELP bfd_peers Metric read from /var/tmp/textfile/bfd.prom
# TYPE bfd_peers gauge
bfd_peers{Interface="Ethernet0",Local_Addr="fe80::e201:a6ff:fedc:9f03",Peer_Addr="fe80::922d:77ff:fe14:3100",State="up",Type="dynamic",Vrf="default"} 1
bfd_peers{Interface="Ethernet12",Local_Addr="fe80::e201:a6ff:fedc:9f03",Peer_Addr="fe80::1644:8fff:febc:d9c8",State="up",Type="dynamic",Vrf="default"} 1
bfd_peers{Interface="Ethernet4",Local_Addr="fe80::e201:a6ff:fedc:9f03",Peer_Addr="fe80::922d:77ff:fe14:2a00",State="up",Type="dynamic",Vrf="default"} 1
bfd_peers{Interface="Ethernet8",Local_Addr="fe80::e201:a6ff:fedc:9f03",Peer_Addr="fe80::922d:77ff:fe14:2600",State="up",Type="dynamic",Vrf="default"} 1
# HELP bgp_neighbors Metric read from /var/tmp/textfile/bgp.prom
# TYPE bgp_neighbors gauge
bgp_neighbors{Neighbhor="Ethernet0",NeighborName="NotAvailable",RemoteAS="4200000101",StatePfxRcd="1",UpDown="12w6d19h",Version="4"} 1
bgp_neighbors{Neighbhor="Ethernet100",NeighborName="NotAvailable",RemoteAS="0",StatePfxRcd="Idle",UpDown="never",Version="4"} 1
bgp_neighbors{Neighbhor="Ethernet104",NeighborName="NotAvailable",RemoteAS="0",StatePfxRcd="Idle",UpDown="never",Version="4"} 1
bgp_neighbors{Neighbhor="Ethernet108",NeighborName="NotAvailable",RemoteAS="0",StatePfxRcd="Idle",UpDown="never",Version="4"} 1
bgp_neighbors{Neighbhor="Ethernet112",NeighborName="NotAvailable",RemoteAS="0",StatePfxRcd="Idle",UpDown="never",Version="4"} 1
bgp_neighbors{Neighbhor="Ethernet116",NeighborName="NotAvailable",RemoteAS="0",StatePfxRcd="Idle",UpDown="never",Version="4"} 1
~snip~
# HELP pmon_service pmon service
# TYPE pmon_service gauge
pmon_service{status="active"} 1
# HELP process_cpu_seconds_total Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds_total counter
process_cpu_seconds_total 6.86
# HELP process_max_fds Maximum number of open file descriptors.
# TYPE process_max_fds gauge
process_max_fds 524288
# HELP process_open_fds Number of open file descriptors.
# TYPE process_open_fds gauge
process_open_fds 10
# HELP process_resident_memory_bytes Resident memory size in bytes.
# TYPE process_resident_memory_bytes gauge
process_resident_memory_bytes 2.3662592e+07
# HELP process_start_time_seconds Start time of the process since unix epoch in seconds.
# TYPE process_start_time_seconds gauge
process_start_time_seconds 1.7659313628e+09
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 1.27123456e+09
# HELP process_virtual_memory_max_bytes Maximum amount of virtual memory available in bytes.
# TYPE process_virtual_memory_max_bytes gauge
process_virtual_memory_max_bytes 1.8446744073709552e+19
# HELP promhttp_metric_handler_errors_total Total number of internal errors encountered by the promhttp metric handler.
# TYPE promhttp_metric_handler_errors_total counter
promhttp_metric_handler_errors_total{cause="encoding"} 0
promhttp_metric_handler_errors_total{cause="gathering"} 0
# HELP promhttp_metric_handler_requests_in_flight Current number of scrapes being served.
# TYPE promhttp_metric_handler_requests_in_flight gauge
promhttp_metric_handler_requests_in_flight 1
# HELP promhttp_metric_handler_requests_total Total number of scrapes by HTTP status code.
# TYPE promhttp_metric_handler_requests_total counter
promhttp_metric_handler_requests_total{code="200"} 34
promhttp_metric_handler_requests_total{code="500"} 0
promhttp_metric_handler_requests_total{code="503"} 0
# HELP radv_service radv service
# TYPE radv_service gauge
radv_service{status="active"} 1
# HELP routesTotal Metric read from /var/tmp/textfile/route-summary.prom
# TYPE routesTotal untyped
routesTotal{vrf="default"} 6
# HELP routesTotalFib Metric read from /var/tmp/textfile/route-summary.prom
# TYPE routesTotalFib untyped
routesTotalFib{vrf="default"} 6
# HELP sflow_service sflow service
# TYPE sflow_service gauge
sflow_service{status="active"} 1
# HELP snmp_service snmp service
# TYPE snmp_service gauge
snmp_service{status="active"} 1
# HELP swss_service swss service
# TYPE swss_service gauge
swss_service{status="active"} 1
# HELP syncd_service syncd service
# TYPE syncd_service gauge
syncd_service{status="active"} 1
# HELP teamd_service teamd service
# TYPE teamd_service gauge
teamd_service{status="active"} 1

なお端折っていますが、予め社内からACL(iptables)の許可はしていますので、ここは各自の環境に合わせてご注意ください。

まとめ

SONiCのsystemdを眺めて自分たちでツールを追加することができました。

この取り組みにより、node exporterをはじめ、監視関連のツールを自分たちで追加できるようになったのはよかったです。
今回は、SNMPではちょっと足りない、けど、TelemetryのCollectorを立てるほどではない、そんな中間地点の監視例として、node exporterで実現してみました。

ですが、本質的なベネフィットは、SONiCのベースとなっている Debianで動くツールであれば、Linuxの流儀で追加できる と判ったことだと思います。

今回は事情あってbinaryを動かす例を挙げましたが、せっかくのSONiCなので、Dockerコンテナ中心としたツール実装を今後検討できればと思います。

併せて、SONiCのredisを直接読んで、node exporterでは対応しきれないようなツールを作れたら面白そうです。

もしいい活用法など思いつかれましたら、是非コメントまでお寄せください!

おまけ

実は、この記事はネタが絞り切れず書いてしまった2本目の記事です。(後から満席になっているのを見て申し訳ない限り)

せっかくなので、もう一本の記事もよろしければご覧ください。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?