SPF+が取れないか、GE-PON <M>F GE-PON-ONU タイプD<1>を分解してみた。もし取れたらSPF+ポートが有効活用できるうえ、邪魔なONUを撤去できるからだ。
ただまぁ結論としては無理だった。
結論としてはSFP+は入っておらず、光ファイバが基板に直付けされていた。
ONUに刺さっているLCケーブルをSFP+をはめ込んでSFP+コネクタのMACアドレスを書き換えれば、ONUとして動く可能性もあるが、色々問題がありそうなのでやめておこうと思う。
OCNバーチャルコネクト環境でIPv6サーバーをポート443で公開した時にやったことを派生させて更にIPv4にも対応させるプラン。
OCNバーチャルコネクトのIPoEでIPv4 over IPv6のMAP-E環境だと、IPv4ではWell-known portsが開けないが、PPPoEを使って取得したIPv4であれば開けるので、これとIPoE側のIPv6の併用でデュアルスタックでWell-known portsの開放を行い、サーバーを運用できるようにするのが目的だ。
| 環境 | 内容 |
|---|---|
| ISP | OCN光 |
| ISP契約 | OCN 光 with フレッツ マンション・スーパーハイスピード 隼・プラン1・西日本 |
| ISP接続方式 | OCNバーチャルコネクト(IPoE, MAP-E) |
| RouterOS | OpenWrt 24.10.0 |
| ServerOS | Ubuntu 24.04.3 LTS |
| HTTPD | nginx 1.26.1 |
| ドメインレジストラ | Value Domain |
ufwではv4にも穴が開いている状態とする
listen 443 ssl;を追記して保存する/etc/iproute2/rt_tablesを開き次のようにpppoeの行を追加、保存
#
# reserved values
#
128 prelocal
255 local
254 main
253 default
300 pppoe
0 unspec
#
# local
#
#1 inr.ruhep
service network restart10、Override IPv4 routing tableをpppoe (300)にする1にするwan_pppoeにし、MasqueradingとMSS clampingにチェックを入れ、Covered networksでwanpppを選択し保存するwan_pppoeを追加し、保存するwan_pppoeが設定されていることを確認する。設定されていなかったら設定するlanに、SourceをサーバーのIPにする(例:192.168.1.5/32)pppoe (300)を設定し、保存するIPv4 only、Source zoneをwan_pppoe、External portを443、Internal IP addressを転送先のサーバーIP、Internal portを443にして、保存するcurl -4 -v https://example.com
/etc/hotplug.d/ifaceに40-pppoeというファイルを755で作り、以下の内容を記述する。
#!/bin/sh
[ -n "$DEVICE" ] || exit 0
[ "$ACTION" = ifup ] || exit 0
[ "$INTERFACE" = wanppp ] || exit 0
vdpw=ここにDDNS更新用のパスワード
pppoeaddr=$(ip -4 addr show pppoe-wanppp | head -2 | tail -1 | awk '{print $2}')
logger -t "DDNS - PPPoE IP" $pppoeaddr
# hoge.example.comに対して更新リクエストを飛ばす例
logger -t "DDNS - DOMAIN: hoge" $(wget -O - "https://dyn.value-domain.com/cgi-bin/dyn.fcg?d=example.com&p=$vdpw$&h=hoge&i=$pppoeaddr")
# When making requests for multiple domains, wait 60 seconds between each request
このスクリプトはhotplugイベントが発生したときにファイルパス順に呼ばれるスクリプトらしい。
ひとまずデバイスでなく、インターフェイスが起動したときで、かつインターフェース名がwanpppの時に実行されるように組んである。
注意点として、このエンドポイントはレートリミットがシビアで、60秒以内に送ると503を返してくるようなので、複数回投げる場合はsleep 61とかして間隔をあけると良さそうだ。数が増えてきたらバリュードメインAPIのDNS設定APIを利用した方が早いだろう。
ただしこのAPIはTTLの制御に難があり、処理方法によっては3600秒に固定される罠が潜んでいるので注意が必要だ。
/var/log/ufw.logにログがあればufwで穴をあける(まずこれはない)PPPoEインターフェース(wanppp)の設定を開き、Advanced SettingsからUse DNS servers advertised by peerのチェックを外すと直る。
IPv4, v6のデュアルスタックに出来たのはいいが、幾つかの端末で繋いだところIPv4側に流れるケースがあった。メインのv6でなく、オプションのv4に行くという状況はやや残念だ。
構成的にもマルチセッションという微妙な形態で、v4とv6でインターフェースが二系統あり、セキュリティもNATとFWがあり、なかなか複雑な状態になってしまった。
Mastodonにおいてはv4IPのインスタンス(兵庫丼)との疎通はTLレベルでは出来たものの、画像が出なくなるという頭が痛い問題に出会った。
Misskey.ioからは疎通できず、こちらからはユーザープロフィールまではアクセスできるがフォローを押しても音沙汰がない。
結論としてはリッチなアプリケーション通信を伴う用途において、この方式は向かない気がした。恐れくこれはルーターより外側がIPoEとPPPoEで別系統になっている関係で、通信が混乱して上手くいかないパターンがあるのではないかと感じている。
DDNSも制約が厳しいし、Value DomainのAPIはあるだけマシではあるものの、超絶使いづらい問題もあり、この環境においてのデュアルスタックはあきらめようと思った。
まぁいい勉強にはなったので、そのうち何かに活かせるだろう、きっと。
OpenWrtからValue-Domainに複数サブドメインのDDNSを行うツールを作ったのもあり、IPv4が変わったときも自動で切り替えることでダウンタイムを減らす運用ができたので、今のところ不満はない。
ただFediverseで繋がらないところは繋がらないので諦めることにした。
7月31日に立てたMastodon作成ロードマップの最後である、自宅サーバーでMastodonをセルフホスティングするのを達成したので、そのログをここに記す。
Ubuntu実機AMD64環境でDockerを使わず、IPv6シングルスタックで構成している。
自宅サーバーを使ってIPv6シングルスタックで通信を待ち受ける方法はOCNバーチャルコネクト環境でIPv6サーバーをポート443で公開した時にやったことの方に書いている。
Ubuntu実機AMD64環境
| Env | Ver |
|---|---|
| Ubuntu | 24.04.3 LTS |
| nginx | 1.26.1 |
| Mastodon | v4.4.3 |
| Ruby | 3.2.3 |
| ruby-build | 20250811 |
| rbenv | 1.3.2-10-gd1a19a3 |
以下のコマンドを流す
sudo apt update
sudo apt install -y \
imagemagick ffmpeg libvips-tools libpq-dev libxml2-dev libxslt1-dev file git-core \
g++ libprotobuf-dev protobuf-compiler pkg-config gcc autoconf \
bison build-essential libssl-dev libyaml-dev libreadline6-dev \
zlib1g-dev libncurses5-dev libffi-dev libgdbm-dev \
nginx redis-server redis-tools postgresql \
certbot python3-certbot-nginx libidn11-dev libicu-dev libjemalloc-dev \
# 最新版を入れないとうまくいかない
wget https://github.com/rbenv/ruby-build/archive/refs/tags/v20250811.tar.gz
tar -xzf v20250811.tar.gz
sudo PREFIX=/usr/local ./ruby-build-20250811/install.sh
rm v20250811.tar.gz
rm -Rf ruby-build-20250811
# aptではサポート切れの化石が入るのとnvmで入れたのは自分しか使えないので新しいのをグローバルに入れる
curl -fsSL https://deb.nodesource.com/setup_24.x -o nodesource_setup.sh
sudo bash nodesource_setup.sh
apt install -y nodejs
rm nodesource_setup.sh
corepack enable
# メンテナンスでめっちゃ使うのでサービスユーザーにはせず、ログイン可能なユーザーとして作る
adduser --disabled-password mastodon
/etc/redis/redis.confを開きrequirepassのコメントを外し、パスワードを設定
sudo service redis restart
sudo -u postgres psqlでポスグレに入り、ユーザーを作る。ポスグレは不慣れで同時にPW設定する方法が判らなかったので別建てで設定している
CREATE USER mastodon CREATEDB;
ALTER ROLE mastodon WITH PASSWORD 'パスワード';
\q
実際にはかなり行ったり来たりしてるので一部の手順には誤りが含まれている可能性がある。
mastodonユーザーにスイッチしてインストール作業を続行
# Mastodon本体の導入
sudo su - mastodon
git clone https://github.com/mastodon/mastodon.git live
# 超重要、cd忘れててもなんかそれっぽく進むが、以後すべて破綻する
cd live
git checkout $(git tag -l | grep '^v[0-9.]*$' | sort -V | tail -n 1)
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
exec bash
# 動かないやつなのでコメントアウト
# 依存関係のインストールとセットアップでやった内容をこっちでやってもいいかもしれない
# git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
# ここでエラーが出たら何かが間違っているのでやり直し
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install
# bundleがないとか、rubyのバージョンが存在しないとかエラーが出たら何かが間違っているのでやり直し
bundle config deployment 'true'
bundle config without 'development test'
bundle install -j$(getconf _NPROCESSORS_ONLN)
yarn install
# 色々尋ねられるので適当に進める
RAILS_ENV=production bin/rails mastodon:setup
デーモン周りの設定をする
# 静的ファイルをnginxに見させるために権限を設定
sudo chmod o+x /home/mastodon
# デーモンの登録と起動
sudo cp /home/mastodon/live/dist/mastodon-*.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now mastodon-web mastodon-sidekiq mastodon-streaming
sudo service mastodon-web start
sudo service mastodon-sidekiq start
sudo service mastodon-streaming start
nginxの設定を書く
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream backend {
server [::1]:3000 fail_timeout=0;
}
upstream streaming {
# Instruct nginx to send connections to the server with the least number of connections
# to ensure load is distributed evenly.
least_conn;
server 127.0.0.1:4000 fail_timeout=0;
# Uncomment these lines for load-balancing multiple instances of streaming for scaling,
# this assumes your running the streaming server on ports 4000, 4001, and 4002:
# server 127.0.0.1:4001 fail_timeout=0;
# server 127.0.0.1:4002 fail_timeout=0;
}
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CACHE:10m inactive=7d max_size=1g;
server {
listen [::]:443 ssl http2;
server_name <ドメイン>;
ssl_certificate /etc/letsencrypt/live/<証明書のパス>/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/<証明書のパス>/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
# You can use https://ssl-config.mozilla.org/ to generate your cipher set.
# We recommend their "Intermediate" level.
ssl_ciphers CDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA84:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA38:DHE-RSA-CHACHA20-POLY1305;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
keepalive_timeout 70;
sendfile on;
client_max_body_size 99m;
root /home/mastodon/live/public;
gzip on;
gzip_disable "msie6";
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xmlrss text/javascript image/svg+xml image/x-icon;
gzip_static on;
location / {
try_files $uri @proxy;
}
# If Docker is used for deployment and Rails serves static files,
# then needed must replace line `try_files $uri =404;` with `try_files $uri @proxy;`.
location = /sw.js {
add_header Cache-Control "public, max-age=604800, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}
location ~ ^/assets/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}
location ~ ^/avatars/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}
location ~ ^/emoji/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}
location ~ ^/headers/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}
location ~ ^/packs/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}
location ~ ^/shortcuts/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}
location ~ ^/sounds/ {
add_header Cache-Control "public, max-age=2419200, must-revalidate";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
try_files $uri =404;
}
location ~ ^/system/ {
add_header Cache-Control "public, max-age=2419200, immutable";
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
add_header X-Content-Type-Options nosniff;
add_header Content-Security-Policy "default-src 'none'; form-action 'none'";
try_files $uri =404;
}
location ^~ /api/v1/streaming {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Proxy "";
proxy_pass http://streaming;
proxy_buffering off;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
tcp_nodelay on;
}
location @proxy {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Proxy "";
proxy_pass_header Server;
proxy_pass http://backend;
proxy_buffering on;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_cache CACHE;
proxy_cache_valid 200 7d;
proxy_cache_valid 410 24h;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
add_header X-Cached $upstream_cache_status;
tcp_nodelay on;
}
error_page 404 500 501 502 503 504 /500.html;
}
なんやかんやあり色々苦戦したが、無事立ち上がってくれて何よりだった。
立ち上がった直後。
そして現在。こうやって動いているのを見ると感慨深い。
実は中学生のころから自宅サーバーを立てるという夢を持っており、こうやって実際に公開できたことは感慨深い。実際のところ、実験用の小規模サーバーや、SHOUTcastサーバーだとか、シンクライアント用のSSHサーバーみたいなのは作った経験があり、いずれも外に出していたのでサーバーを作った経験が全くない、というわけではないのだが、監視環境を整えたり、IPv4が実用困難な環境で、IPv6に挑戦してみたりと、ここまで本格的にやったことはなかったのもあり、個人的には結構達成感がある。
3000番はGrafanaをはじめ、様々なNode.js系のポートと干渉するため変えておく。
/etc/systemd/system/mastodon-web.serviceのEnvironment="PORT=3000"を変えることでWeb側のポートが変えられる。Environment="BIND=[::1]"とすることでIPv6で待ち受けられる/etc/systemd/system/mastodon-streaming.serviceのWants=mastodon-streaming@4000.serviceの4000の部分を変えることで、Streaming側のポートも変えられるsudo systemctl daemon-reload
sudo service mastodon-web restart
sudo service mastodon-streaming restart
sudo service nginx restart
基本的にどこかの手順が間違ってるのでしつこくやり直すことで改善する。
以下の内容で修正すると増やせる。
class StatusLengthValidator < ActiveModel::Validator
- MAX_CHARS = 500
+ MAX_CHARS = 50000
正直このファイルは直さなくても動くし、直しても、ほぼ意味がなさそうなので、直す必要はないかもしれない。
- maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 500),
+ maxChars: state.getIn(['server', 'server', 'configuration', 'statuses', 'max_characters'], 50000),
変更直後は投稿画面の初期表示に500と出ていて、数文字打つと50000文字に変化する状態だったが、一日くらい経過すると初期値が50000に変わっていたのでキャッシュか何かが悪さをしていた可能性がある。
IPv6と疎通してないところは仕方がないね。AAAAレコード持ちでcurl -6 -v https://example.comしても疎通する場所でも起きることがあるが原因は不明。
兵庫丼でも偶に遭遇してたので、そういうものだと割り切るしかない。IPv4滅ぶべし、慈悲はない。
APIのレートリミットにかかっているせいっぽい。5分間に300リクエストまでと、かなり渋い。信用するIPを指定することで回避可能らしい。
.env.productionを開き、前述の資料に書かれている信頼できるIPを列挙した後に、自分のIPv6プレフィクス(aaaa:bbbb:cccc:dddd::/64を自分のプレフィクスに置き換え)を追加し、再起動しておいた。これで緩和されると良いのだが、出先では使えないと思うのでどうするか…。
TRUSTED_PROXY_IP=127.0.0.1/8,::1/128,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,fc00::/7,aaaa:bbbb:cccc:dddd::/64
ドキュメントを読む限り、アカウントIDで縛る処理がどこかにあるようなので、自分のIDを除外できれば出先でも起きなくなり、更に良さそうだが、やり方はよくわからなかった。
調べた限りconfig/initializers/rack_attack.rbの中でやっているようなので、暇なときに見てみたいとは思う。
Mastodonは標準でPrometheusに対応しているので、Exporterの有効化設定を見ながら設定していく。
まず.env.productionを開き、以下の二行を追記し、再起動する。これによってMastodonWebのExporterが有効化され、ローカルホストで動くようになるようだ。Streamingの方は設定せずとも、元から有効になっている模様。
MASTODON_PROMETHEUS_EXPORTER_ENABLED=true
MASTODON_PROMETHEUS_EXPORTER_LOCAL=true
再起動したらcurl http://localhost:{Streamingのポート番号}/metricsとcurl http://localhost:9394/metricsで動いていることを確認。
次にPrometheusのscrape_configsに以下を追記し、Prometheusを再起動する。
- job_name: 'mastodon-web'
static_configs:
- targets: ['localhost:9394']
- job_name: 'mastodon-streaming'
static_configs:
- targets: ['localhost:5001']
Prometheusの管理画面を開き、Target healthに出て動いていればOK。
大まかには、このような感じ。再現したりしなかったりでいい感じの動画が撮れなかった…。
キャッシュ周りで何かの問題が起きている可能性があるが原因はよくわかっていない。3分も待てば改善するので一旦気にしないことにした。
Dockerがあれば一瞬で立ち上がって便利といえば便利なのだが、Ubuntuのネイティブ環境にPrometheusとGrafanaをIPv6スタックで導入した時に、DockerとIPv6の相性が余りにも悪すぎたのが最大の要因。
後はまぁ変なレイヤー隔ててない方が管理楽だし、リソースも抑えられるかなとか。
Mastodon自体に監視の仕組みがあること。Sidekiqという名前自体はよく聞くので知っていたが、今まではなんとなくジョブ管理システムだと思っていた。これはTLで見かけていた管理人の人たちがトラブルが起きた時にジョブを殺したりしているのを見かけたためだ。
とはいえ、公式サイトの説明にはRuby's fastest job systemとあるので、あながち認識としては間違ってなかったようだ。ジョブ管理システムのついでにメトリクスとかも見れる機能があるのかもしれない。
環境変数周りの設定項目が多く、コンソールから設定を操作するイメージが強く、コンソールベースなのかと思っていたが実際にはWebベースのGUI管理コンソールもあり、中々便利だ。管理面の操作や、サーバー本体への影響がない簡単な設定はここで出来るだろう。
アップロードが爆速だったり、画面遷移もかなり早い。家で繋ぐ分にはLAN環境だから早いのかと思っていたが、外から5Gで繋いでも早い。
今のところ大したアクセスや負荷もなく、リソースがしこたま余ってるお陰かもしれない。あとは単純に手前にCDNがないとか、VPSといった共有資源とかと比べた時のスペックの差だろうか?
スペックは結構ある方だと思う。具体的にはCPUはAMD Ryzen 5 5600G、メモリはDDR4 32GB、SSDもCrucial P1 CT500P1SSD8JPでNVMeといった感じで、そんなハチャメチャに高スペックというわけではないが、よくある?安い中古ノートPCを流用したサーバーと比べたら充実している。
主に監視周りの強化をしていきたい。現在のロードマップは以下のような感じだ。
まず、Grafanaをセットアップしたもののnginxのダッシュボードが空っぽなので作っていく必要がある。
次にLokiを入れてログ監視もできた方がいい。個別のログファイルを見るのは割とつらい。
Grafanaで統合管理できると便利なのでMastodonのメトリクスも取れると良いので、これもやっていきたい。
他にも今回のセットアップ中にOpenWrtがOutOfMemoryで落ちるケースがあり、原因不明のネットワーク切断が多発したため、こちらのメトリクス収集も必要だ。ネットワーク自体が死ぬと中身が見れないため、基本的に本体側で、収集して見れるようにしたい。今でも簡単なものは入れているが情報量が少なく、役不足だ。
ストレージ寿命の問題もありeMMCにログを吐くとストレージの寿命がマッハになる恐れがあるため、メモリ管理や追加でストレージを用意するなど、手法についても検討する必要がある。
OpenWrtのメトリクスを鯖側でも取れるようにするのはオプションプランだが、これは別にどうでもいい気はする。平時は統合的に見れて便利かもしれない。
あとは外部環境にステータスページを配置したいと考えている。実用性があるかどうかは謎だが、あるとそれっぽいから。ツールとしてはCachetを採用し、さくらのレンタルサーバー側で運用する予定だ。
簡単な外形監視の導入についても、Cachetで担える可能性がある。
監視面以外ではセルフホスティング方式のWAFであるAnubisに興味があり、これも入れていきたいと考えている。
更に中長期では、現在運用しているlycolia.info配下の全サイトを自宅サーバーへ移行し、メールサーバーやネームサーバーなど、現在あるWebサイトの一切を移行する野望も抱いている。
とにかくやることが沢山あるので、当面は暇になることはなさそうで、充実した生活を送れそうだ。
本来手順になく、私の勘違いで追加して作業してしまっていたOSグローバルのRubyを消しても動くのか試してみた。
# 通常ユーザー作業
ls -la /usr/local/bin | grep ruby
rm /usr/local/bin/ruby-build
sudo rm -Rf /usr/local/bin/ruby-build
ls -la /usr/local/share
sudo rm -Rf /usr/local/share/ruby-build
sudo rm -Rf /usr/local/share/man/man1/ruby-build*
ruby -v
sudo apt remove ruby
sudo su - mastodon
# mastodonユーザー作業
mkdir -p "$(rbenv root)"/plugins/ruby-build
cp ruby-build-20250811/. "$(rbenv root)"/plugins/ruby-build
ls -la "$(rbenv root)"/plugins/ruby-build
cd live
RUBY_CONFIGURE_OPTS=--with-jemalloc rbenv install
ruby -v
exit
# 通常ユーザー作業
sudo service mastodon-web restart
sudo service mastodon-sidekiq restart
sudo service mastodon-streaming restart
一旦ここまでは上手くいったので、上の方にある手順よりこっちでやった方がいいかもしれない。Mastodonのビルドが通るかまでは試してないので、実際に動くかどうかまでは不明だが、まぁ次回考えればよいだろう。
この記事では自宅サーバーの監視環境導入にあたり、ほぼ前提知識なしの状態からPrometheusとGrafanaを導入した経緯を書いている。IPv6シングルスタック対応。
Ubuntu実機AMD64環境
| Env | Ver |
|---|---|
| Ubuntu | 24.04.3 LTS |
| nginx | 1.26.1 |
| Prometheus | 3.5.0 |
| Node exporter | 1.9.1 |
| NGINX Prometheus Exporter | 1.4.2 |
| Grafana | v12.1.1 |
今回の作業はLAN内にある作業機からサーバーマシンに対して行うため、利便性のため各サービスに対してドメインを振っておく。
/etc/hostsを開きPrometheusとGrafanaの設定を書く
prometheus.testとgrafana.testとする/etc/init.d/dnsmasq restartPrometheusとはSoundCloudで開発された時系列のスタンドアロンバイナリのツールで、時系列でメトリクスを収集するツールのようだ。オープンソースソフトウェアとして公開されている。
メトリクスとは数値測定のことで、例えばCPU使用率や、メモリ使用量、HTTPサーバーがこれまでに返したレスポンスコード別のカウントなど、そういった数値のことだ。Prometheusはこれを定期的に収集することで、収集時刻+メトリクスで時系列の数値データを蓄積しているものと思われる。
実際のデータとしてはメトリック名に対して連想配列を格納したデータを保持しているっぽい。イメージとしては以下のような感じだろうか。
<metricName>: { <labelName>: <labelValue>, ... }
メトリクスはExporterと呼ばれるサービスに対してHTTP要求を投げ、その応答を記録しているようだ。つまりExporterがメトリクスを取得、Prometheus向けに情報加工し、PrometheusはExporterからこれを取得、時系列のデータベースにしているのだろう。
またエンドポイントが揮発性であるなど、短命である場合はPushgatewayというプロキシを使い、PushgatewayがPrometheus向けに情報公開する仕組みもあるようだ。
EoLはリリースから一年ほどと、短い。
Prometheusセットアップコマンドを流す
# 作業場所の作成
mkdir temp
cd temp
# バイナリ取得
wget https://github.com/prometheus/prometheus/releases/download/v3.5.0/prometheus-3.5.0.linux-amd64.tar.gz
tar xvfz prometheus-3.5.0.linux-amd64.tar.gz
# Prometheus実行ユーザーの作成
sudo groupadd prometheus
sudo useradd prometheus -g prometheus -d /var/lib/prometheus -s /usr/sbin/nologin -m
ls -la /var/lib/prometheus/
# binを配置
cd prometheus-3.5.0.linux-amd64
sudo cp prometheus promtool /usr/local/bin/
ls -la /usr/local/bin | grep prom
# 設定ファイルを配置
sudo mkdir /etc/prometheus
cat<<'EOF' | sudo tee /etc/prometheus/prometheus.yaml
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
# - "first.rules"
# - "second.rules"
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['[::]:9090']
EOF
sudo chown -R prometheus:prometheus /etc/prometheus
ls -la /etc/prometheus
# デーモン作成
cat <<'EOF' | sudo tee /etc/systemd/system/prometheus.service
[Unit]
Description=Prometheus - Monitoring and alerting toolkit
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=prometheus
Group=prometheus
WorkingDirectory=/var/lib/prometheus
ExecStart=/usr/local/bin/prometheus \
--config.file=/etc/prometheus/prometheus.yaml \
--storage.tsdb.path=/var/lib/prometheus \
--web.listen-address=[::]:9090
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
# デーモン有効化
sudo systemctl daemon-reload
sudo systemctl enable --now prometheus
nginxからPrometheusの管理画面が開けるようにする。/etc/nginx/conf.d/prometheus.confを作成し、以下を記述。
upstream prometheus {
server [::1]:9090;
}
server {
listen [::]:80;
server_name prometheus.test;
access_log /var/log/nginx/prometheus.access.log;
error_log /var/log/nginx/prometheus.error.log;
location / {
proxy_set_header Host $host;
proxy_pass http://prometheus;
}
}
sudo service nginx restart
先ほどの「Prometheusセットアップコマンドを流す」ステップで作成した設定の説明。
First steps with Prometheusに書いてある通り、設定はprometheus.yamlのようなYAMLファイルに定義し、./prometheus --config.file=prometheus.ymlの様にして起動時に読み込んで利用する。
global:
scrape_interval: 15s
evaluation_interval: 15s
rule_files:
# - "first.rules"
# - "second.rules"
scrape_configs:
- job_name: prometheus
static_configs:
- targets: ['[::1]:9090']
globalセクションではポーリング頻度と、ルールの評価頻度が指定されている。
rule_filesではルールファイルが指定される。
scrape_configsではポーリング先のリソースを指定する。この場合はhttp://localhost:9090/metricsに対して行われる。
ログの保持期限はprometheusを蹴るときのコマンドラインオプションで決まる。
デフォルトでは15日だが、--storage.tsdb.retention.timeを指定することで増やせるようだ。
単位はy, w, d, h, m, s, msがあり、--storage.tsdb.retention.time=90dの様にして指定する模様。
How long to retain samples in storage. If neither this flag nor "storage.tsdb.retention.size" is set, the retention time defaults to 15d. Units Supported: y, w, d, h, m, s, ms. Use with server mode only.
個人的にはデフォルトの15日で様子見し、必要に応じて伸ばしていこうと考えている。商業サービスのように可用性が重視され、障害発生などで過去を遡りたい場合には長期記録があると手がかりが増えるので、有用だと思う。
Exporterは実際に数値を収集し、Prometheusに対して情報公開するためのHTTPエンドポイントを提供するサービスで、この章では今回利用するものについて書いていく。
POSIX系システムのOSやマシンの監視にはPrometheus公式のNode exporterを利用する。
# 取得
wget https://github.com/prometheus/node_exporter/releases/download/v1.9.1/node_exporter-1.9.1.linux-amd64.tar.gz
tar xvfz node_exporter-1.9.1.linux-amd64.tar.gz
cd node_exporter-1.9.1.linux-amd64/
# binを配置
sudo cp node_exporter /usr/local/bin/
ls -la /usr/local/bin/ | grep node_exporter
# デーモン作成
cat <<'EOF' | sudo tee /etc/systemd/system/node_exporter.service
Description=Node exporter
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=prometheus
Group=prometheus
WorkingDirectory=/var/lib/prometheus
ExecStart=/usr/local/bin/node_exporter \
--web.listen-address=[::]:9100
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
# デーモン有効化
sudo systemctl daemon-reload
sudo systemctl enable --now node_exporter
/etc/prometheus/prometheus.yamlを開きscrape_configs:に以下を追記
- job_name: 'node'
static_configs:
- targets: ['[::1]:9100']
nginxの監視にはnginx公式のNGINX Prometheus Exporterを利用する。
これはログを監視するわけではなく、接続を許可した数や、現在の接続数、これまでのHTTP要求合計などの数値を公開しているようだ。
/etc/nginx/conf.d/status.confを作ってstub_status_moduleを埋める。
server {
listen [::]:80;
location /stub_status {
stub_status on;
access_log off;
allow ::1;
deny all;
}
}
疎通確認
curl -v http://localhost/stub_status
こんなのが来ればOK
< HTTP/1.1 200 OK
< Server: nginx/1.26.1
< Date: Mon, 18 Aug 2025 10:31:59 GMT
< Content-Type: text/plain
< Content-Length: 97
< Connection: keep-alive
<
Active connections: 1
server accepts handled requests
1 1 1
Reading: 0 Writing: 1 Waiting: 0
* Connection #0 to host localhost left intact
# 取得
wget https://github.com/nginx/nginx-prometheus-exporter/releases/download/v1.4.2/nginx-prometheus-exporter_1.4.2_linux_amd64.tar.gz
tar xvfz nginx-prometheus-exporter_1.4.2_linux_amd64.tar.gz
# binを配置
sudo cp nginx-prometheus-exporter /usr/local/bin/
ls -la /usr/local/bin/ | grep nginx-prometheus-exporter
# デーモン作成
cat <<'EOF' | sudo tee /etc/systemd/system/nginx_exporter.service
[Unit]
Description=NGINX Exporter
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=prometheus
Group=prometheus
ExecStart=/usr/local/bin/nginx-prometheus-exporter \
--nginx.scrape-uri=http://[::]/stub_status \
--no-web.systemd-socket \
--web.listen-address=:9113
ExecReload=/bin/kill -HUP $MAINPID
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
# デーモン有効化
sudo systemctl daemon-reload
sudo systemctl enable --now nginx_exporter
/etc/prometheus/prometheus.yamlを開きscrape_configs:に以下を追記
- job_name: 'nginx'
static_configs:
- targets: ['[::1]:9113']
http://prometheus.testにアクセスできることを確認し、ついでにStatus→Target healthでExporterの死活も見ておくと良い。
Grafanaとはオープンソースのビジュアライザツールである。PrometheusやMySQLなどのデータソースを基にグラフにしたりすることができる。
Lokiというツールを使うことでログも扱うことができ、ログビューワとしても使えるようだ。
次のコマンドでインストールする
# debの取得とインストール
sudo apt-get install -y adduser libfontconfig1
wget https://dl.grafana.com/grafana-enterprise/release/12.1.1/grafana-enterprise_12.1.1_16903967602_linux_amd64.deb
sudo dpkg -i grafana-enterprise_12.1.1_16903967602_linux_amd64.deb
# デーモンの有効化
sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable grafana-server
sudo service grafana-server start
適当にnginxの設定ファイルを切って以下のように書き、アクセスする
# This is required to proxy Grafana Live WebSocket connections.
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
upstream grafana {
server [::1]:3000;
}
server {
listen [::]:80;
server_name grafana.test;
access_log /var/log/nginx/grafana.access.log;
error_log /var/log/nginx/grafana.error.log;
location / {
proxy_set_header Host $host;
proxy_pass http://grafana;
}
# Proxy Grafana Live WebSocket connections.
location /api/live/ {
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_pass http://grafana;
}
}
ID: admin, PW: adminでログインしパスワードを変更する
http://localhost:9090を入れるjobの文字を押すjob = nodeになるようにする(nodeの部分はprometheus.yamlに定義したジョブ名を指定する)Grafana公式のダッシュボードテンプレートに使えるものが見当たらなかったので自作するしかない。
IPv6縛りでやっているとPrometheusのNode exporterがPrometheus本体と疎通できなかったので諦めた。恐らくコンテナの中からIPv6でホストと通信するのが難しいのだと思う。
Node exporterホストのメトリクスを取る場合、Node exporterのネットワークはHost側に行くのため、prometheus.yamlのstatic_configs.targetsをlocalhostにするとコンテナ内を見てしまうから疎通できないし、host.docker.internalを指定してもv4アドレスの127.0.0.11が返ってきてしまうので疎通できない。IPv6をデフォルトにしてもhost.docker.internalは127.0.0.11のままで、AAAAレコードがない。
ググっても当てはまるものは出ず、GPT-5やClaude Opus 4.1に聞いても答えは得られず、消耗戦になるので諦めた。
それに物理サーバー一台の環境だとコンテナ化の恩恵もさほどないので、Docker固有の問題に苦しむくらいならバイナリを直に叩いた方が楽というのもある。
init.dの書き方で冗長だった記述や、誤解を招く記述の整理ができた。例えばNAME変数の影響範囲が広すぎて、本来波及してほしくない場所まで波及してしまう問題や、giteaにべったりで応用の利かない部分を直せたほか、一度しか出現しないのに変数化されていたり、存在しないファイルを参照するコードがあったのに気づけるなど、収穫が多くあった。
修正点としてはNAME変数はサービス名のみに影響するようにして、実行ファイル名に波及しないようにしたり、"$DAEMON -- $DAEMON_ARGS"となっていた場所も$DAEMON $DAEMON_ARGSとするなど、影響範囲を狭めたり、変数側で調整が効くものをべた書きで依存させないようにした。
今回は微妙な記事を参考にinit.dを書いてしまい、結構ハマってしまったが、今回の修正によって今後init.dを書く場合には、より詰まりづらくなることが期待できる。
今までPrometheusのことを色んなアプリケーションのログをフェッチしてDBに放り込んでくれるツールだと思っていたが、全然違った。Prometheusはメトリクスを時系列に集め、それをGrafanaなどのビジュアライザに提供できるツールだ。
この「メトリクス」や「時系列」という言葉が鍵で、「メトリクス」は任意の数値、時系列は取得した時間だ。
例えばCPU使用率が20%、メモリ使用量が24GBという情報をExporterが公開しているとして、10:00にPrometheusが取得すれば10:00のCPU使用率は20%、メモリ使用量は24GBとなる。次回10:11に取得すれば別の数値が取れるだろう。これを積み重ねていくことで時系列に数値を取ることが可能になり、時系列で集計することが可能になる。そうするとグラフにしたときに使用率の急増などが掴めるようになるわけだ。
具体的にはExporterと呼ばれるプログラムが、この値を収集し、http://<ホスト>/metricsのエンドポイントで公開している。なのでprometheus.yamlにはホスト部のみを定義すれば読みに行ってくれる。逆に言えばExporterを自作する場合は、ここに値を置けばよいのだろう。
他にもExporterが置けない、揮発性のものに対してもPushgatewayと呼ばれる外部プログラムにデータをプールさせることで、Exporterの代わりになると言う事も知れた。結局のところ相手が何であろうと、Prometheusはメトリクスエンドポイントをポーリングして情報を収集するだけなのだ。
そしてGrafanaとかは恐らくPrometheusにPromQLで問い合わせてデータを引き出すのだろう。
全体像を理解するにあたりPrometheus公式にある、アーキテクチャ図が非常にわかりやすく、だいぶ助けられた。
名前は聞いたことがあるものの今まであまりよくわかっていなかったPrometheusの理解が深まったことは、今回の作業で最大の収穫だった。
次のエラーが出る場合、もしDocker Composeを使っているのなら、バイナリをホストにインストールすると回避できる。
参考までに一番上と一番下はIPv4なので疎通できず、真ん中はlocalhostがコンテナの中を見ているのでホストに疎通できていない状態と思われる。
数秒~数十秒のラグがあるので少し待つと出てくる。ステータスがUpになるのにも時間がかかるのでゆっくり待つしかない。
参考までに筆者のマシンはCPUがAMD Ryzen 5 5600Gでメモリは32GB積んでいるが、これほど掛かる。
--nginx.scrape-uri=でstub_statusのパスが指定できていないか、パスが間違っていないか確認すると解決するかもしれない。
参考までにPrometheusの管理画面でnginx-upのメトリクスが0の場合は取得に失敗しているので、どこかの設定がおかしい。
以下の手順を試し、原因の切り分けを図り対策する。
ps ax | grep promで上がっているかどうか確認するsudo lsof -i :<ポート番号>でプロセスを特定して殺してから起動する(ゾンビプロセスに塞がれていることがある)curl -v http://localhost:{exporterのport}/metricsで確認可能
諦めて自分で作るしかない。幸い素のnginxはメトリクスが少ない。
Node exporterダッシュボードの作成を参照のこと。
/etc/nginx/conf.d/grafana.confのhttp_portのコメントを外し、任意のポートに変更し、sudo service grafana-server restartで再起動する。
前段にnginxなどがいる場合は、そちらの設定を変えるのも忘れないこと。
3000番はNode.js系がよく使うため変えた方が色々便利だ。
最近OpenWrtカテゴリで10GbE対応ルーターPCであるR86S U1のセットアップを延々しているし、フレッツ光クロス、10Gbps対応に関する覚書でも神戸市の対応状況や、必要なものをまとめたりしているが、費用面はどうだろう?と言う事で調査してみた。
結論から言うと私のケースでは年額4万くらい増えることが分かった。
NTT西日本のサイトを見てみると、なんとフレッツ光ネクスト隼より安く使えるとある。これが事実ならめっちゃ契約したい。以前より劇的に安くなっている気がする!!!
そんなうまい話はなかった。
NTT
マンション・スーパーハイスピードタイプ 隼 プラン1(割引あり)
サイト上は3,575円だが、何かしらの因果で実際は3,388円だった。
OCN
891円
合算
4,279円
NTT
5,720円
OCN
1,815円
合算
7,535円
下の表は月額料金の比較である。
| 事業者 | 隼 | クロス |
|---|---|---|
| NTT | 3,388円 | 5,720円 |
| OCN | 891円 | 1,815円 |
| 合計 | 4,279円 | 7,535円 |
この表で差額を計算した場合、月額で3,256円、年に換算すると39,072円。これはかなり厳しい。仮に導入するとしても実運用で回線が厳しくならないと難しい予算帯だ。
ここ5年くらいはfast.com基準で下り平均360Mbps、上り平均420Mbps、レイテンシ平均4-5msほど出ているため、大きな不満はない。機嫌が良ければ上下ともに500Mbpsを超え、レイテンシが3msになることもあるので、なおさらだ。
光はじめ割の廃止で2026年6月から682円値上げで私の隼契約は4,070円となるようだったが、年8,184円の増加に過ぎないため、依然としてクロスに上げる場合に30,888円の追加が必要で、まだまだ乗り換えるには微妙な感じだ。
とはいえ、差額が縮まったので乗り換えるモチベーションは少しだけ高まった。
また、光はじめ割の廃止に伴い、光はじめ割ネクストが新たにできたようだが、これは無期限であった光はじめ割に対し、最初の二年間のみの適用ということで、なんとも渋い内容になっているようだ。
R86S U1は既にあるのでそれ以外。
| 機材 | 製品 | 費用 | 購入先 |
|---|---|---|---|
| L2スイッチ | MikroTik CRS305-1G-4S+IN | 27,357円 | Amazon |
| NIC * 2 | Mellanox ConnextX-4 | 3,359円 * 2 | AliExpress |
| SPF+ DACケーブル 1.5m | 10Gtek 10G SFP+ ケーブル 黒 1.5m | 2,359円 | Amazon |
| SPF+ DACケーブル 0.5m | 10Gtek 10G SFP+ ケーブル 黒 0.5m | 1,999円 | Amazon |
| SPF+ DACケーブル 0.3m | 10Gtek 10G SFP+ ケーブル 黒 0.3m | 1,799円 | Amazon |
| 合計 | 40,232円 |
そこそこ高いが一度買えば基本的に買い替え不要なので、この程度はおもちゃ代としてみれば個人的に許容範囲だったが、クロスを導入できない以上、購入は見送ることになった。
R86S U1については10GbEは活かせないが、それ以外は活かせているため全然許容している。R86S U1は間違いなく過去最高のルーターだ。
機材費は許容範囲だが、回線費がどうにも厳しかったので、現状の回線でどうしても無理なら契約を検討になると思う。
正直、現状だとIPv6シングルスタック運用は厳しい可能性もあり、仮にするとしてもアクセス数的にトラフィックをそこまで食わない気がするので、現状でも問題ない可能性が多分にある。
R86S U1のSPF+ポートを活かせないのは残念だが、R86S U1自体は先代のRTX830より活躍してくれているし、今のところは現状維持という形にしたいと思う。