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 127.0.0.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側のポートが変えられる
/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
nginxの設定も対応する箇所を変える
nginxを再起動する
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分も待てば改善するので一旦気にしないことにした。
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本体でメトリクスをとれるようにしたい
本体が死ぬとテレメトリ取れないので鯖側のPrometheusだと役不足
問題はストレージどうするか…
寿命の関係でeMMCには書けないからNVMe拡張するのか
OpenWrtのメトリクスを鯖側でも取れるようにする(いるかこれ?
外部ステータスページの用意
簡単な外形監視の導入?(やるならさくらに乗せる)
まず、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のビルドが通るかまでは試してないので、実際に動くかどうかまでは不明だが、まぁ次回考えればよいだろう。