https://blog.naskya.net/
Clone
HTTPS:
git clone https://code.naskya.net/repos/wzWnj
SSH:
git clone USERNAME@code.naskya.net:wzWnj
Branches
Tags
6kic0tebueju.md
{{< toc >}}
これはなに
これは Firefish Advent Calendar 2023 の 12/11 の記事です。
この記事では、私なりの Firefish サーバーの立て方を紹介します。必ず以下の注意点を念頭に置いて読んでください。
- 私は今年になるまでサーバーなんて立てたことが無かったまるっきりの初心者なので、この記事の内容にはきっと誤りがあります。誤りの指摘は大歓迎です。
- 一人で使う(または非常に小規模な)サーバーを想定しています。
- 例えば、メンテナンスの際にサーバーにダウンタイムが生じることを全く気にしません。
- 解説記事のつもりで書いてはいないので、読みづらい部分があるかもしれません。
- そもそもこの記事はこの通りにサーバーをインストールすることをおすすめするものではありません。特に Arch Linux を一切触ったことが無い方にはおすすめできません{{< note
「Firefish サーバーの立て方メモ」という題名は釣りにならないか悩みました
>}}。
- そもそもこの記事はこの通りにサーバーをインストールすることをおすすめするものではありません。特に Arch Linux を一切触ったことが無い方にはおすすめできません{{< note
- この記事の通りにインストールをして問題が起きてもなんとかしてください。
- ドメイン名の購入は済んでいて、DNS の設定にアクセスできるものとします。
- スクリーンショットを多数貼っていますがダークモード用の画像を用意していないため、ダークモードで読んでいて画像が明るすぎて目が痛くなったらごめんなさい。
とはいえ、サーバーを立てている皆様やこれからサーバーを立てようと思っている方にとって少しは参考になるかもしれません。ゆっくりしていってね!
環境
立てるサーバーの環境はこんな感じです。
項目 | 内容 |
---|---|
自分の手元のマシンの OS {{< note これはあまり重要ではないですが、環境によって VPS に接続するときに用いるコマンドなどが変わるかもしれません >}} |
Arch Linux |
VPS | ConoHa VPS |
オブジェクトストレージ | MinIO を同じサーバーに立てて使う |
サーバーの OS | Arch Linux |
JavaScript ランタイム | Node.js v21 |
データベース | PostgreSQL v16 |
インメモリデータベース | Redis v7 {{< note 他にも DragonflyDB, KeyDB などを使えるらしい。使ったことはない。 >}}{{< note Redis v4 でも動かせるらしい。どうやるのかは知らない。https://sudo.mkdir.uk/notes/9mgqy1z3viqs07y4 >}} |
Web サーバー | Caddy |
サーバーへのアクセスのフィルター{{< note この手のソフトウェアをなんて呼ぶべきなのか分からない >}} |
nftables |
ファイヤーウォール | fail2ban |
rustc に使わせるリンカー | mold |
インストールする Firefish のフレーバー | https://code.naskya.net/naskya/firefish |
全文検索エンジン | PGroonga |
バックアップの暗号化 | GnuPG |
バックアップの管理 | rclone, systemd タイマー |
パスワード管理ツール{{< note 多用するので何かしら使ってください >}} |
Bitwarden |
Astrophysics の The First Sound of The Future Past{{< link https://astrophysicsbrazil.bandcamp.com/album/the-first-sound-of-the-future-past
>}}{{< link https://open.spotify.com/album/7sNxOLcGgeSCWlTDnoeblU
>}} を聴きながら作業します。
VPS の契約
VPS インスタンスの追加
日本の会社であることと、500 JPY/mo 程度でそこそこのスペックのインスタンスを使えることから ConoHa VPS を選びました。それ以外に理由は特にありません。もっと良いサービスを知っていたら教えてください!
ConoHa VPS にアクセスし、アカウントの登録などを済ませてから、ダッシュボードの左側にある「セキュリティ」フォルダー内にある「セキュリティグループ」を開きます{{< note この表示が無い場合には下にある「バージョン切替」から v3.0 のコントロールパネルに切り替えてください
>}}。
するとこのような画面が出てくるので、
右上のセキュリティグループ追加のボタンを押して Firefish 用のセキュリティグループを追加します。
すると「Firefish」というタブが一番上に出てくるので、それを開いて左下のプラスのマークのボタンから
- イーサタイプ: IPv4, ポート範囲: 80
- イーサタイプ: IPv4, ポート範囲: 443
- イーサタイプ: IPv6, ポート範囲: 80
- イーサタイプ: IPv6, ポート範囲: 443
- イーサタイプ: IPv6, ポート範囲: 22
- イーサタイプ: IPv6, ポート範囲: 好きな整数
を一つずつ追加します。通信方向は In, プロトコルは TCP とします。
最後の「好きな整数」は ssh 接続に使うポート番号で、1024 以上 49151 以下の整数を指定します。好きな整数といっても 5432 は PostgreSQL に、6379 は Redis に、9000 と 9001 は MinIO に使ってもらうことにするのでそれと被らないようにします。大きめの値にしておくとよいと思います。今回は 25252{{< link https://dic.pixiv.net/a/%E3%81%AB%E3%81%A3%E3%81%93%E3%81%AB%E3%81%A3%E3%81%93%E3%81%AB%E3%83%BC
>}} としました。以下、25252 という値が出てきたら自分で設定した値に読み替えて解釈してください。
これにより、これら以外のポートへのアクセスは弾かれます。念のため後にフィルターを設定して同様に不正なアクセスを遮断しますが、ここでも設定をしているのは ConoHa VPS がもっと早い段階で(もっと低いレイヤーで)通信を弾いてくれるのではないかと期待してのことです{{< note そんな効果があるのかは知りません
>}}。
80 番は http の通信に{{< link https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=http#table-service-names-port-numbers
>}}、443 番は https の通信に{{< link https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=https#table-service-names-port-numbers
>}}、25252 番は ssh の通信に使用します。ssh はこの後 IPv6 で接続するように設定するので{{< note 私自身は VPN を常用していて IPv6 の通信がよくブロックされるため IPv4 で接続しています
>}}わざわざ IPv4 のぶんまで開ける必要がありません。
ssh のポート番号を変更するまではデフォルトの 22 番{{< link https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.xhtml?search=ssh#table-service-names-port-numbers
>}}で接続する必要があるため、そのための設定も入れています(後で 22 番の設定は削除します)。
セキュリティーグループを追加したら左上の「サーバー追加」のボタンから VPS インスタンスを契約します。
サービスは VPS を、イメージタイプは OS から Arch Linux を選びます。
メモリは頑張れば 1 GB でも動かせますが、 2 GB にしておくと心に余裕ができそうです。2 GB の契約にすればコア数も増えますし、12 か月契約にするとそこまで大きな価格差は無い{{< note 頑張って 12 か月続けようね
>}}ので私は 3 vCPUs, 2 GB RAM, 100 GB SSD の 12 か月契約をしています。
root パスワードには英数字と記号からなる長くて複雑なものを設定し、パスワード管理ツールに記録させましょう。ネームタグにはお好きな名前をどうぞ。ここでは Firefish としました。
ssh 鍵の作成
ここで一旦手元のマシンのターミナルに戻り、ssh-keygen
コマンドを用いて VPS に接続するための ssh 鍵を作ります。
パスフレーズを設定してパスワードマネージャーに記録したり、YubiKey や Nitrokey などに鍵を載せたりすると更によいです。既に好きな ssh 鍵の管理方法を確立している方はお好きなようにどうぞ。
ここでは鍵の名前を ~/.ssh/conoha_vps
としました(ので、適宜読み替えて解釈してください)。
さて、ConoHa VPS のページに戻って作業を再開します。「オプションを見る」というタブを開いてさっき作った Firefish のセキュリティグループを選びます。
SSH Key は登録方法を「インポート」にして作った公開鍵を貼り付けて追加します。
それ以外の設定はいじらない(自動バックアップは無効、追加ストレージ・スタートアップスクリプトは使用しない)でよいでしょう。最初の 2 つはお金掛かっちゃうし。
最後に「次へ」を押して支払い情報などの設定をします。学生だとちょっと安くできたりするかもしれません{{< note 私は学生なのに学割を適用し損ねた😭
>}}。12 か月分を一気に払うのでいいお値段になってしまいます。
それからしばらく(数分は掛かるかも!)待つと、VPS にアクセスできるようになります。
ネームタグをクリックして、VPS 設定のフォルダーを開いて削除ロックを有効にします。
これで VPS の準備は完了です🎉
VPS の基本設定
VPS への ssh 接続
「ネットワーク情報」のタブを開いて一番上の IPv6 アドレスをコピーします。
ここではコピーした IPv6 アドレスを仮に 2400:8500:1001:2002:3003:4004:5005:6006
とするので、以降はあなたの得たアドレスに読み替えて解釈してください。
まずは、さっき作った鍵を用いて root
アカウントで VPS に接続します。
The authenticity of host '2400:8500:1001:2002:3003:4004:5005:6006 (2400:8500:1001:2002:3003:4004:5005:6006)' can't be established.
ED25519 key fingerprint is SHA256:1oMiR9+uvPAsC/iTsb+YGEMXxBOWnYNu6WHW5OFWaNM.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
のように訊かれるので yes
と答え、設定したパスフレーズを入力するなりして VPS に接続します。
適当に whoami
などのコマンドを入力すると root
と返ってきます。わーい!
tmpfs の無効化
さて、df
を実行して記憶域の使用状況を確認してみると、どうやら /tmp
というフォルダには tmpfs
というファイルシステムが使用されているらしいことが分かります。
tmpfsはUnix系OSにおける一時ファイルのためのファイルシステム名。tmpfsはファイルシステムとしてマウントされることを意図しており、これによりHDDをはじめとする永続性をもつ記憶装置の代わりに揮発性メモリに保存されるようにできる。RAMディスク(仮想的なディスクドライブ)とは異なり内部に別のファイルシステムを作成せずに使用することができる。
tmpfs - Wikipedia (https://ja.wikipedia.org/wiki/Tmpfs), 2023/12/04 版
そう、/tmp
ディレクトリに書き込むとなけなしの計 2 GB のメモリの一部が使われてしまうのです!そこで、おまじない程度の省メモリ効果を期待して tmpfs は使わせないようにします。
ちなみに、Arch Linux 以外の多くのディストリビューション(例えば Ubuntu とか)はこのような仕様になっていません。systemd の元々の挙動では tmpfs を使用するので、他の systemd ベースの多くのディストリビューションではカスタムが行われているということでしょう{{< link https://www.kofuk.org/blog/20230128-ubuntu-tmp/
>}}。
ArchWiki の記述{{< link https://wiki.archlinux.jp/index.php/Tmpfs#.E8.87.AA.E5.8B.95.E3.83.9E.E3.82.A6.E3.83.B3.E3.83.88.E3.81.AE.E7.84.A1.E5.8A.B9.E5.8C.96
>}}に従い、以下のようにします。
/etc/tmpfiles.d/tmp.conf
には以下の内容を貼り付けます。
# see tmpfiles.d(5)
# always enable /tmp folder cleaning
D! /tmp 1777 root root 0
# remove files in /var/tmp older than 10 days
D /var/tmp 1777 root root 10d
# namespace mountpoints (PrivateTmp=yes) are excluded from removal
x /tmp/systemd-private-*
x /var/tmp/systemd-private-*
X /tmp/systemd-private-*/tmp
X /var/tmp/systemd-private-*/tmp
以下では vim をエディターとして使いますが{{< note nano がお好みなら下記の "vim" を全て "nano" と読み替えてください。
>}}、nano が入っていることは必ず確認しておいてください(nano /etc/tmpfiles.d/tmp.conf
ができたのなら大丈夫)。なぜなら ssh 接続がうまくいかずに ConoHa VPS のコントロールパネルのコンソールから VPS を操作することになったとき、vim で使う :
などのキーがうまく入力できなくて困るからです{{< note これは私が少々特殊な配列のキーボードを使用しているからなのでしょうか……?でも「テキスト送信」の機能を使ってもコロンがちゃんと入力できないんですよね……どうして……
>}}。
システムの完全アップグレード
再起動には 1 分間も掛からないはずです。さっきと同じコマンドで再度 ssh 接続をしたら、まず /etc/pacman.conf
を編集し、その後システムをアップグレードします。
デフォルトでは
# Misc options
#UseSyslog
#Color
#NoProgressBar
CheckSpace
#VerbosePkgLists
#ParallelDownloads = 5
となっている部分がありますが、Color
と VerbosePkgLists
と ParallelDownloads
のコメントアウトを外して ParallelDownloads
の右辺の値をもっと大きくしておきます。以下、このように行頭の #
を消してコメントアウトを解除することを「アンコメントする」と表現します{{< note これを表すのに使える和語または漢語を募集中です
>}}。
# Misc options
#UseSyslog
Color
#NoProgressBar
CheckSpace
VerbosePkgLists
ParallelDownloads = 20
以上の操作が終わったらシステムをアップグレードしてまた再起動です。初期の状態では sudo
コマンドすらインストールされていないので、ついでに base-devel
パッケージもインストールしておきましょう。
ちなみに pacman -Sy
は危険な操作なので{{< link https://wiki.archlinux.jp/index.php/%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%83%A1%E3%83%B3%E3%83%86%E3%83%8A%E3%83%B3%E3%82%B9#.E9.83.A8.E5.88.86.E7.9A.84.E3.81.AA.E3.82.A2.E3.83.83.E3.83.97.E3.82.B0.E3.83.AC.E3.83.BC.E3.83.89.E3.81.AF.E3.82.B5.E3.83.9D.E3.83.BC.E3.83.88.E3.81.95.E3.82.8C.E3.81.A6.E3.81.84.E3.81.BE.E3.81.9B.E3.82.93
>}}普段は使ってはいけません(今は数少ない pacman -Sy
の使いどころです)。
作業用ユーザーの作成
再起動を終えてまた VPS に ssh 接続できたら、sudo
コマンドが入ったので作業用のユーザーを作って root
ユーザーで作業するのをやめましょう。まず、以下のコマンドで作業用のユーザーを作ります。naskya
の部分は好きなユーザー名に変えて{{< note 絶対に変えてください!面倒くさいからというだけの理由で他人に私のハンドルネームを使われたら嫌です(記事を書き終えてからここの部分は別の名前にしておけばよかったと後悔しました……)
>}}ください。
以下、naskya
というユーザー名が出てきたら自分が設定したユーザー名に読み替えてください{{< note 出てくるコマンドを何も考えずにコピペすることはせず、ちゃんとコマンドを読んで置換すべき場所を探したりそのコマンドが何をするのか理解するように努めてください
>}}。
次に、作ったユーザーのパスワードを設定します。このパスワードは英数字(記号を含まない)で構成された長い(100 文字以上など)ものにすることをおすすめします。
こんな感じの表示が出れば🆗です。
[root@localhost ~]# useradd --create-home --groups wheel naskya
[root@localhost ~]# passwd naskya
New password:
Retype new password:
passwd: password updated successfully
記号を含めないのは ConoHa VPS のコントロールパネルのコンソールから操作することになった場合を考えてのことです。このパスワードは最初の数回しか使いません。頭で覚える必要なんかありませんから、長いものを設定してパスワード管理をに記録させてとっとと忘れましょう。
さて、このままでは naskya
さんは sudo
コマンドでスーパーパワーを行使できないので、/etc/sudoers
ファイルを編集しなければなりません。/etc/sudoers
はとっても大事なファイルなので visudo
コマンドで編集する必要があります{{< link https://wiki.archlinux.jp/index.php/Sudo#visudo_.E3.82.92.E4.BD.BF.E3.81.86
>}}。visudo
コマンドはデフォルトで vi
というエディターを使うので、頭に EDITOR=vim
をつけて vim
を使わせるようにしましょう。
ファイルを開いたら、
# %wheel ALL=(ALL:ALL) ALL
と書かれている部分を探してアンコメントしてください:
%wheel ALL=(ALL:ALL) ALL
この下に以下のような記述もありますが、それらはアンコメントしないように気をつけてください。
# %wheel ALL=(ALL:ALL) NOPASSWD: ALL
# %sudo ALL=(ALL:ALL) ALL
後でもう一度このファイルを編集する必要があるのでここでそれもしてしまいます。ファイルの末尾などに
Defaults env_keep += "SSH_AUTH_SOCK"
を追記しておいてください。
作業用ユーザーで ssh 接続できるようにする
今は ~/.ssh/conoha_vps
という鍵で root
アカウントに ssh 接続できますが、naskya
アカウントには接続できません。少々雑な方法ですが、root
向けの .ssh
フォルダの内容を作業用ユーザー向けにコピーしてしまってこれを解決しましょう。
これをしたら Ctrl+D
で ssh 接続を終了し、手元のマシンから
で naskya
アカウントとして接続できることを確認してください。接続したら、sudo id
などを実行して sudo
コマンドがちゃんと使えるか確かめましょう(さっき設定したパスワードの入力が求められます)。
[naskya@localhost ~]$ sudo id
We trust you have received the usual lecture from the local System
Administrator. It usually boils down to these three things:
#1) Respect the privacy of others.
#2) Think before you type.
#3) With great power comes great responsibility.
For security reasons, the password you type will not be visible.
[sudo] password for naskya:
uid=0(root) gid=0(root) groups=0(root)
作業用ユーザーでしか ssh 接続できないようにする
ssh の設定を変更します。
ポート番号を変更し、IPv6 のみの接続に変更しておきます。すなわち、
#Port 22
#AddressFamily any
を
Port 25252
AddressFamily inet6
に変更します。 IPv4 による接続のみを許可する場合には inet6
ではなく inet
とします。次に、
PermitRootLogin yes
PasswordAuthentication yes
UsePAM yes
をそれぞれ
PermitRootLogin no
PasswordAuthentication no
UsePAM no
に変更して root
アカウントへのアクセスやパスワードでのログイン、PAM の使用{{< link https://askubuntu.com/a/1323703
>}}を無効にします。最後に、
#AllowAgentForwarding yes
をアンコメントして
AllowAgentForwarding yes
として保存します。これができたら ssh のサービスを再起動して
Ctrl+D
で接続を終了し、root
アカウントでのログインや 22 番ポートでのログインができなくなっていることを確認してください。
# root アカウントだしポート番号を変えていないので弾かれる
ssh -i ~/.ssh/conoha_vps root@2400:8500:1001:2002:3003:4004:5005:6006
# root アカウントなので弾かれる
ssh -p 25252 -i ~/.ssh/conoha_vps root@2400:8500:1001:2002:3003:4004:5005:6006
# ポート番号を変えていないので弾かれる
ssh -i ~/.ssh/conoha_vps naskya@2400:8500:1001:2002:3003:4004:5005:6006
# パスワード認証は禁止したので鍵を指定しないと弾かれる
ssh -p 25252 naskya@2400:8500:1001:2002:3003:4004:5005:6006
# これは通る
ssh -p 25252 -i ~/.ssh/conoha_vps naskya@2400:8500:1001:2002:3003:4004:5005:6006
そうしたらもう一度 Ctrl+D
で接続を切り、手元のマシンの ~/.ssh/config
に以下の設定を追記します(ファイルが無ければ新規作成してください)。
Host firefish
Hostname 2400:8500:1001:2002:3003:4004:5005:6006
User naskya
Port 25252
AddressFamily inet6
IdentityFile ~/.ssh/conoha_vps
IdentitiesOnly yes
ForwardAgent yes
AddKeysToAgent yes
また、シェルの rcfile(~/.bashrc
や ~/.zshrc
など)に以下を追記してください。
手元のマシンのシェルを再起動すると、以下のコマンドでサーバーに接続できるようになっています!
ConoHa VPS のページに戻り、Firefish のセキュリティーグループから 22 番ポートを受け入れる設定を削除してしまいましょう。
sudo の認証を ssh 鍵で行う
せっかく ssh 鍵で認証してサーバーに接続しているのに、sudo
コマンドを使うときにいちいちパスワードの入力を求められては面倒です。これを解決するために、pam_ssh_agent_auth というパッケージを入れましょう。
ただしこれは Arch User Repository にあるパッケージなのでインストールやアップデートに少し手間が掛かります。そこで、先に AUR ヘルパーの paru をインストールすることにします{{< note AUR ヘルパーを使いたくない方や他にお好きな AUR ヘルパーがある方は好きなようにしてください
>}}。
sudo pacman -S git
git clone https://aur.archlinux.org/paru-bin.git
cd paru-bin
makepkg -si
cd ..
rm --recursive --force paru-bin
これで paru
コマンドを用いて AUR のパッケージを入れられますが、その前に /etc/paru.conf
を少し編集しておきましょう。以下のような箇所があるので BottomUp
と NewsOnUpgrade
をアンコメントしておきます。
#AurOnly
#BottomUp
#RemoveMake
#SudoLoop
#UseAsk
#SaveChanges
#CombinedUpgrade
#CleanAfter
#UpgradeMenu
#NewsOnUpgrade
これで満を持して pam_ssh_agent_auth
のインストールです。
途中、画面下部に :
が表示されて入力待ちで止まったら q
キーを押せば進めます。pam_ssh_agent_auth
パッケージが入ったら、/etc/pam.d/sudo
の(#
から始まるコメントを除いた)最初の行に追記をします{{< link https://wiki.gentoo.org/wiki/Pam_ssh_agent_auth#PAM_sudo_file
>}}:
#%PAM-1.0
auth sufficient pam_ssh_agent_auth.so file=~/.ssh/authorized_keys
auth include system-auth
account include system-auth
session include system-auth
/etc/sudoers
の編集は既に行っているのでおっけーです。一度 ssh 接続をし直して sudo id
などのコマンドを実行し、パスワードの入力が要求されなくなったことを確かめてください。
[naskya@localhost ~]$ sudo id
uid=0(root) gid=0(root) groups=0(root)
ネットワークの設定
今はやらなくていいことたち
最近の ConoHa VPS では仕様が変わったのか、/etc/resolv.conf
のシンボリックリンクが勝手に設定されるようになっているのでここは触らなくて大丈夫そうです。
[naskya@localhost ~]$ ls -l /etc/resolv.conf
lrwxrwxrwx 1 root root 32 Dec 4 11:23 /etc/resolv.conf -> /run/systemd/resolve/resolv.conf
Cloudflare や Google といった大企業のアドレスが指定されているので、これが気に入らない方は他のサーバー{{< note 例えば https://njalla.social/@njalla/109544720312978601
>}}を利用すると良いでしょう。
sudo unlink /etc/resolv.conf
sudo bash -c 'echo "nameserver 95.215.19.53" > /etc/resolv.conf'
sudo bash -c 'echo "nameserver 2001:67c:2354:2::53" >> /etc/resolv.conf'
sudo systemctl restart systemd-resolved
あと、以前は /etc/systemd/network/10-gmo-vps.network
というファイルを作成して IPv6 向けの設定をしなければならなかったのですが、ConoHa VPS のアップデートにより不要となった気がする(最初からこのファイルがある)のでこれも触らなくて大丈夫そうです。
一応、以前やる必要があった設定を備忘録として書いておきます:
[Match]
Name=インターフェース名
[DHCP]
DUIDType=link-layer
[Network]
DHCP=yes
[Network]
Address=2400:8500:1001:2002:3003:4004:5005:6006/プレフィックス長
Gateway=ゲートウェイ
DNS=DNSサーバー1 DNSサーバー2
ただし インターフェース名
には ip link
を実行すると出てくる lo
ではない方の名前を、プレフィックス長
, ゲートウェイ
, DNSサーバー1
, DNSサーバー2
には VPS のコントロールパネルのネットワーク情報に書かれているものを使います{{< note それから 2400:8500:1001:2002:3003:4004:5005:6006 を自分の IPv6 アドレスに置き換えるのも忘れずに
>}}。
例えば ip link
の実行結果が
のようになっていて、VPS のコントロールパネルに
と表示されている場合には
[Match]
Name=eth0
[DHCP]
DUIDType=link-layer
[Network]
DHCP=yes
[Network]
Address=2400:8500:1001:2002:3003:4004:5005:6006/64
Gateway=2400:8500:2002:3171::1
DNS=2400:8500:7703:502:150:95:10:8 2400:8500:7703:502:150:95:10:9
とします(以前やる必要があった設定はここまで)。
フィルタリング
nftables
をインストールします。
設定方法がよく分からないし調べる気も起きません。nftables wiki にある Simple ruleset for a server という例{{< link https://wiki.nftables.org/wiki-nftables/index.php/Simple_ruleset_for_a_server
>}}を真似します。ただし、ssh のために書かれている 22 番ポートを許可する設定は削除して IPv6 の 25252 番を許可するように書き換えます。
flush ruleset
table inet firewall {
chain inbound_ipv4 {
}
chain inbound_ipv6 {
# Accept neighbour discovery otherwise connectivity breaks
icmpv6 type { nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
# Allow ssh
tcp dport 25252 accept
}
chain inbound {
# By default, drop all traffic unless it meets a filter
# criteria specified by the rules that follow below.
type filter hook input priority 0; policy drop;
# Allow traffic from established and related packets, drop invalid
ct state vmap { established : accept, related : accept, invalid : drop }
# Allow loopback traffic.
iifname lo accept
# Jump to chain according to layer 3 protocol using a verdict map
meta protocol vmap { ip : jump inbound_ipv4, ip6 : jump inbound_ipv6 }
# Allow HTTP(S) TCP/80 and TCP/443 for IPv4 and IPv6.
tcp dport {80, 443} accept
}
chain forward {
# Drop everything (assumes this device is not a router)
type filter hook forward priority 0; policy drop;
}
}
設定を適用します。
念のため ssh 接続を切ってちゃんと再接続ができるか試します{{< note もしここで再接続ができなくなっていたらコントロールパネルのコンソールから操作する羽目になる😇
>}}。
Firefish をインストールする準備
使い方にもよるとは思いますが、私は小規模サーバーにオブジェクトストレージは不要だと思っています。私一人のサーバーは半年間続けてオブジェクトストレージを 400 MB も使わなかったし、お金掛かっちゃうし。
しかし、オブジェクトストレージ無しでサーバーを動かすと万が一後でオブジェクトストレージを使いたくなったときにサーバー上にある既存のファイルを移行させられなくて困ります{{< link https://mk.yopo.work/notes/9khu1cht1w
>}}。そんなわけで、MinIO というオブジェクトストレージのサービスをセルフホストします。
ここでは仮に
- 購入したドメイン名は
example.com
- Firefish サーバーに使うドメイン名は
firefish.example.com
- オブジェクトストレージのドメイン名は
storage.firefish.example.com
- MinIO の管理画面に使うドメイン名は
minio.firefish.example.com
とします。以降、これらのドメイン名が出てきたら自分が使うものに読み替えてください。
DNS の設定
サーバーの IPv4 アドレスをコピーし、使用する 3 つのサブドメインそれぞれに対して同じ IPv4 アドレスを用いて A レコードを設定します。
次にコントロールパネルから IPv6 アドレスをコピーし、同様に 3 つのサブドメインそれぞれに対して AAAA レコードを設定します。
リバースプロキシを立てる
Caddy の設定
Caddy をインストールします。
Caddy の設定ファイルである /etc/caddy/Caddyfile
を削除し、同名のファイルを新規作成して以下のように書きます。
firefish.example.com {
@blocked header_regexp User-Agent .*Bytespider.*
handle @blocked {
abort
}
@proxy path /storage/*
redir @proxy https://storage.firefish.example.com/firefish{uri}
reverse_proxy http://127.0.0.1:3000
log {
output file /var/log/caddy/firefish.log
}
}
storage.firefish.example.com {
@blocked header_regexp User-Agent .*Bytespider.*
handle @blocked {
abort
}
reverse_proxy http://127.0.0.1:9000
log {
output file /var/log/caddy/storage.log
}
}
minio.firefish.example.com {
@blocked header_regexp User-Agent .*Bytespider.*
handle @blocked {
abort
}
reverse_proxy http://127.0.0.1:9001
log {
output file /var/log/caddy/minio.log
}
}
この Caddyfile は 3 つのブロック (firefish.example.com
, storage.firefish.example.com
, minio.firefish.example.com
) に分かれています。これは 1 台のサーバーで 3 つのドメインを扱っているためで、このサーバーへのアクセスはドメイン名によって振り分けられます。
例えば 2 つ目のブロックは storage.firefish.example.com
宛てにきた通信を処理するためのものです。
storage.firefish.example.com {
@blocked header_regexp User-Agent .*Bytespider.*
handle @blocked {
abort
}
reverse_proxy http://127.0.0.1:9000
log {
output file /var/log/caddy/storage.log
}
}
2 行目から 5 行目までの記述により、私は robots.txt
の内容を無視する Bytespider というクローラー{{< link https://im-in.space/@Yuki/110911419969276600
>}}をブロックしています。やるかどうかはご自由に。
真ん中の reverse_proxy
は、この通信をローカルの 9000 番のポートで動くサービス(オブジェクトストレージ)に渡しています。
最後の log
はログをファイルに出力する設定です{{< link https://caddyserver.com/docs/caddyfile/directives/log#examples
>}}。
3 つ目のブロックも同様で、minio.firefish.example.com
宛ての通信を 9001 番のポートで動くサービス(MinIO の管理画面)に渡しています。
1 つ目のブロック (firefish.example.com
) を見ると、ここには少し違う処理が入っています。
@proxy path /storage/*
redir @proxy https://storage.firefish.example.com/firefish{uri}
これは、firefish.example.com
宛てにきた通信のうち、firefish.example.com/storage/*
(*
はワイルドカード)に来たものを https://storage.firefish.example.com/firefish
に転送するというものです。
これにより、https://storage.firefish.example.com/firefish/hoge.png
という場所にあるファイルを https://firefish.example.com/storage/hoge.png
という URL で見られるようになります。これがわざわざ MinIO を立てた理由です。
もし今後サーバーの記憶域がいっぱいになったりしてオブジェクトストレージを(例えば Vultr に)お引越ししようと思った場合、引っ越し先のオブジェクトストレージに MinIO にあった全てのファイルを移してから Caddyfile のこの部分を
@proxy path /storage/*
redir @proxy https://bucket.sgp1.vultrobjects.com/firefish{uri}
などに変えれば(bucket
はオブジェクトストレージのバケット名)今まで使っていた https://firefish.example.com/storage/hoge.png
という URL が今度は https://bucket.sgp1.vultrobjects.com/firefish/hoge.png
を参照するようになるので、URL をそのままに(つまりファイルのリンク切れを起こさずに)オブジェクトストレージを引っ越せるようになります。
Caddy を起動します。
オブジェクトストレージを立てる
では、実際にオブジェクトストレージを動かしましょう。
MinIO の設定
MinIO をインストールします。
/etc/minio.conf
を以下のように編集します。ただし RandomUserName
と VeryStrongPassword
はそれぞれ好きなユーザー名と強いパスワードに置き換えます。このユーザー名とパスワードは MinIO の管理画面 (https://minio.firefish.example.com
) へのログインで使うので、パスワード管理ツールに覚えさせておくとよいです。最後の 2 行も使用するドメイン名に合わせてください。
9000 番のポートでオブジェクトストレージのサーバーを、9001 番のポートで管理画面の Web サーバーを動かす設定になっています。
# Local export path.
MINIO_VOLUMES="/srv/minio/data/"
# Server user.
MINIO_ROOT_USER="RandomUserName"
# Server password.
MINIO_ROOT_PASSWORD="VeryStrongPassword"
# Use if you want to run Minio on a custom port.
MINIO_OPTS="--address :9000 --console-address :9001"
MINIO_SERVER_URL="https://storage.firefish.example.com"
MINIO_BROWSER_REDIRECT_URL="https://minio.firefish.example.com"
MinIO を起動します。
バケットの作成
これで MinIO の管理画面 (https://minio.firefish.example.com
) にアクセスできるようになるので、ここに /etc/minio/minio.conf
で指定したユーザー名とパスワードを入れてログインします。
firefish という新しいバケットを作成します。
このままではストレージの中身が公開されないので、アクセスポリシーを public に設定します。
また、ストレージに勝手に書き込まれないようにするために Anonymous Access は readonly に設定しておきます。
ファイヤーウォールの設定
fail2ban をインストールします。
/etc/fail2ban/filter.d/caddy-status.conf
というファイルを作成し、以下の内容を書き込みます{{< link https://muetsch.io/how-to-integrate-caddy-with-fail2ban.html
>}}。
[Definition]
failregex = ^.*"remote_ip":"<HOST>",.*?"status":(?:401|403|500),.*$
ignoreregex =
datepattern = LongEpoch
/etc/fail2ban/jail.conf
を /etc/fail2ban/jail.local
という名前でコピーします{{< link https://wiki.archlinux.jp/index.php/Fail2ban#.E3.83.87.E3.83.95.E3.82.A9.E3.83.AB.E3.83.88_jail
>}}。
[sshd]
という項目を探し、ポート番号を変更して enabled = true
を追記します:
Caddy 向けの設定を追記します:
[caddy-status]
enabled = true
port = http,https
filter = caddy-status
logpath = /var/log/caddy/*.log
maxretry = 10
あと、今回は nftables を使っているのでデフォルトの iptables
向けの設定を nftables
向けに変更します{{< link https://wiki.archlinux.org/title/Fail2ban#Firewall_and_services
>}}:
正直これでいいのかはさっぱりです 取りあえず起動します
Firefish のインストール
Firefish のリポジトリの複製
Firefish を実行するためのユーザーを作成します。ホームディレクトリは /opt/firefish
にしておきます。
一時的にこのユーザーで作業するときと元のユーザーに戻るときはそれぞれ
sudo --user=firefish bash # firefish ユーザーとして bash を実行する
cd ~ # ホームディレクトリ(この場合 Firefish のリポジトリのディレクトリ)に移る
などとします{{< note 可読性のためにこの記事では長いオプション (--user=firefish) を表記していますが、実際に使うときは -u firefish としてもよいです
>}}。
/opt/firefish
にリポジトリをクローンします。本家の Firefish を使うなら https://git.joinfirefish.org/firefish/firefish.git
をクローンします。
sudo git clone https://code.naskya.net/naskya/firefish /opt/firefish
sudo chown --recursive firefish: /opt/firefish
安全性を高めるため Firefish を動かす際にこのディレクトリは読み取り専用にしますが、/files
フォルダへは書き込める必要があるため /opt/firefish/files
は /var/lib/firefish/files
のシンボリックリンクとしておきます。
sudo mkdir --parents /var/lib/firefish/files
sudo ln --symbolic /var/lib/firefish/files /opt/firefish/files
firefish ユーザーになって設定ファイル{{< note 名前が .config/default.yml なのはなんで?ってずっと思っています
>}}を編集します。
url
とデータベースの名前・ユーザー名・パスワードを編集します(ExamplePassword
と書いたところはランダムなパスワードにしてください)。
url: https://firefish.example.com <--
...
db:
host: localhost
port: 5432
#ssl: false
# Database name
db: firefish_db <--
# Auth
user: firefish <--
pass: ExamplePassword <--
さらに、投稿の最大文字数の設定やリモートのファイルをプロキシする設定{{< note これは「リモートのファイルをキャッシュ」する設定とは違います。あの設定を無効にするならこちらは有効にしておくべきです。
>}}、サーバーのアドレスの指定を追記します。
一旦作業用のユーザーに戻ります。
データベースの準備
PostgreSQL のインストールとデータベースの作成
PostgreSQL をインストールし、初期化して起動します。en_US.UTF-8
や ja_JP.UTF-8
といった特定のロケールは指定せずに --no-locale
(または --locale=C.UTF-8
)を用いることでパフォーマンスが向上するようです{{< link https://www.postgresql.org/docs/current/locale.html#LOCALE-BEHAVIOR
>}}{{< link https://qiita.com/fujii_masao/items/2a715fb5a3f718d22ab4
>}}。
paru -S postgresql
sudo --user=postgres \
initdb --no-locale --encoding='UTF8' --pgdata='/var/lib/postgres/data'
sudo systemctl enable --now postgresql
PostgreSQL に firefish
というユーザーと firefish_db
というデータベースを作ります。最初のコマンドでパスワードを訊かれたらさっき .config/default.yml
に書いたものを答えます。
sudo --user=postgres \
createuser --no-createdb --no-createrole --no-superuser --encrypted --pwprompt firefish
sudo --user=postgres \
createdb --encoding='UTF8' --owner=firefish firefish_db
PGroonga のインストールと有効化
Firefish の私のフレーバーには PGroonga が必要なので、インストールして有効化します。インストールには少し時間が掛かります。本家 Firefish を使う場合にこの作業は不要です。
paru -S pgroonga
sudo --user=postgres \
psql --command='CREATE EXTENSION pgroonga;' --dbname=firefish_db
Redis のインストール
Redis をインストールして起動します。
Rust のインストール
Rust, Clang, mold をインストールします。mold v2.3.3 にはまずいバグがあるようなのですが{{< link https://calc.cune.moe/notes/9mmwfg61hismiyx3
>}}、これを書いている時点で既に v2.4.0 が入るようになっていました。
Clang と mold の場所を確認してから /opt/firefish/.cargo/config.toml
に mold をリンカとして使わせる設定を追記します{{< link https://stackoverflow.com/a/70378019
>}}。
sudo --user=firefish bash
cd ~
mkdir .cargo
which clang # /usr/bin/clang
which mold # /usr/bin/mold
vim .cargo/config.toml
exit
[target.x86_64-unknown-linux-gnu]
linker = "/usr/bin/clang"
rustflags = ["-C", "link-arg=--ld-path=/usr/bin/mold"]
Node.js と npm のインストール
pacman で nodejs
パッケージをインストールしても npm
はついてこないようなので{{< link https://github.com/misskey-dev/misskey/issues/11425#issuecomment-1657209913
>}}、両方をインストールします。さらに pnpm
も使えるようにします。
FFmpeg のインストール
動画処理に必要な FFmpeg をインストールします。
アセットファイルの配置
サーバーのアイコンをカスタムしたい場合には custom
フォルダに以下の画像を配置します。結構面倒……。
custom/assets/favicon.png
- サーバーのページの favicon(一辺 32 px から 192 px くらいの正方形の画像)
custom/assets/favicon.ico
- ICO 形式の favicon(
ffmpeg -i favicon.png favicon.ico
などで変換可能)
- ICO 形式の favicon(
custom/assets/apple-touch-icon.png
- 一辺 256 px 程度の正方形の画像
custom/assets/icons/192.png
- 一辺 192 px の正方形の画像
custom/assets/icons/512.png
- 一辺 512 px の正方形の画像
custom/assets/icons/maskable.png
- 一辺 512 px 程度の正方形の画像(端が切り取られて使われる可能性があるため余白があるといい)
custom/assets/icons/monochrome.png
- 一辺 512 px 程度の正方形の画像(白黒であるといい)
Firefish のビルド
Firefish をビルドします!Rust 製の部分があるので少し時間が掛かります。
# firefish ユーザーで作業する
sudo --user=firefish bash
cd ~
# ビルドする(初回インストール時のみ --install をつける)
./update.sh --install
# 元のユーザーに戻る
exit
( ^-^) < Enjoy your sabakan life~
が出たらビルドはおしまいです。
readelf
コマンドを使えば mold が使われていることが分かります。
本家版 Firefish を使う場合には
git checkout main # or beta
corepack prepare pnpm@latest --activate
pnpm install --frozen-lockfile
NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run build
NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run migrate
などとします。
Firefish を起動
以下の内容の /etc/systemd/system/firefish.service
を作成します。水無麻那さんの systemd サービス{{< link https://github.com/mizunashi-mana/firefish-dist-pkg/blob/main/debian/firefish/lib/systemd/system/firefish.service
>}}と MCtek さんの systemd サービス{{< link https://key.kubiwa.moe/notes/9d7hb1xwbt
>}}および DynamicUser
の説明記事{{< link https://0pointer.net/blog/dynamic-users-with-systemd.html
>}}を参考にしています。
メモリアロケーターを jemalloc にする{{< link https://github.com/misskey-dev/misskey/issues/10984#issuecomment-1672495555
>}}のは効果を感じなかった{{< note サーバーの規模が小さいから?
>}}のでしていませんが、jemalloc は Redis のインストール時にくっついてくるので使いたい人は systemd サービスのファイルに 1 行足すだけで使えます{{< note でも使うならちゃんと明示的にインストールしましょう
>}}。共有オブジェクトのパスは以下のコマンドなどで調べられます。
[Unit]
Description=Firefish daemon
Requires=redis.service minio.service caddy.service postgresql.service
After=redis.service minio.service caddy.service postgresql.service network-online.target
[Service]
Type=simple
User=firefish
Group=firefish
UMask=0027
ExecStart=/usr/bin/pnpm --filter backend run start
WorkingDirectory=/opt/firefish
StateDirectory=firefish/files
Environment="NODE_ENV=production"
Environment="npm_config_cache=/tmp"
# jemalloc を使うならアンコメント
# Environment="LD_PRELOAD=/usr/lib/libjemalloc.so.2"
StandardOutput=journal
StandardError=journal
SyslogIdentifier=firefish
TimeoutSec=60
Restart=always
CapabilityBoundingSet=
DevicePolicy=closed
DynamicUser=true
NoNewPrivileges=true
LockPersonality=true
PrivateDevices=true
PrivateIPC=true
PrivateMounts=true
PrivateUsers=true
ProtectClock=true
ProtectControlGroups=true
ProtectHostname=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectKernelLogs=true
ProtectProc=invisible
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true
SecureBits=noroot-locked
SystemCallArchitectures=native
SystemCallFilter=~@chown @clock @cpu-emulation @debug @ipc @keyring @memlock @module @mount @obsolete @privileged @raw-io @reboot @resources @setuid @swap
SystemCallFilter=capset pipe pipe2 setpriority
[Install]
WantedBy=multi-user.target
さて、いよいよ Firefish を起動します。起動直後に不審なエラーが出ないか確認するために journalctl
で見守ります。
ちなみに、サーバーの ~/.bashrc
に
alias sudo='sudo '
alias start="bash -c 'systemctl start \$0; journalctl --catalog --pager-end --follow --unit=\$0'"
と書いておくと
とするだけでこの 2 つのコマンド(systemctl
と journalctl
)を実行できて便利です{{< link https://fedibird.com/@monaco_koukoku/111448970041094623
>}}{{< link https://fedi.sup39.dev/notes/9mbv5gcrtddik35g
>}}{{< link https://syo.bar/objects/b2f243b8-16b8-4002-b1e9-09645a2642cc
>}}。
Dec 05 01:49:29 localhost firefish[142300]: DONE * [core boot] All workers started
Dec 05 01:49:29 localhost firefish[142300]: DONE * [core boot] Now listening on port 3000 on https://firefish.example.com
というようなログが出たら、自鯖 (https://firefish.example.com
) にアクセスできるはずです!
Firefish サーバーの初期設定
もうちょっと我慢
サーバーが立って安心したい気持ちは山々ですが、「サーバー立ったよ!」とインターネットに書くのはまだ早いです。
本家 Firefish を使っている人は、まずはコントロールパネルから新規登録の不許可と
非公開モードの設定をオンにしましょう。取りあえずこれで勝手に他人が登録してきたりリモートサーバーと通信したりしないのでゆっくり設定できます。今回入れている Firefish のフレーバーでは最初からこの設定になっているのでここはスルーします。
オブジェクトストレージの設定
一度 MinIO の管理画面 (https://minio.firefish.example.com
) に戻り、オブジェクトストレージのアクセスキーを作成します。
そうしたら、Firefish のコントロールパネルに戻ってオブジェクトストレージの設定をします。「中身は storage.firefish.example.com
にアップロードしつつ、外部に公開する URL は firefish.example.com/storage
の下にしたい」という需要を叶えるために少し特殊な設定が必要です{{< link https://qiita.com/atsu1125/items/8e870ec7c9932210b152
>}}。
Base URL: https://firefish.example.com
Bucket: firefish
Prefix: storage
Endpoint: storage.firefish.example.com
Region: us-east-1
として、さっき取得したアクセスキーとシークレットキーを入力して下部にある 4 つのトグルを全て有効にして右上のチェックマークで設定を保存します。
画像をアップロードしてみて、動作を確認しましょう。
- 画像がちゃんと表示され、画像の URL が
https://firefish.example.com/storage
で始まっている
- オブジェクトストレージの
firefish
バケットにstorage
というフォルダができていて、その中に画像がアップロードされている
確認ができたらオブジェクトストレージの設定は終了です🎉
その他のサーバー設定
サーバー名・サーバーの説明文・サーバーのテーマやテーマカラーなどを設定しましょう。
私はサーバーメトリクスと identicon{{< note アイコン画像が無いときに表示されるランダムなアイコン
>}} の生成を切っています。サーバーメトリクスなんか見ても健康に悪いし、ランダムなアイコンは多くの場合かわいくないし……
Summaly Proxy の設定は要らないかな……って思っています。同じものが内蔵されていてユーザーの IP アドレスはバレないようになっているので{{< link https://forum.misskey.io/d/9-what-is-a-summaly-proxy
>}}{{< note お一人様サーバーの場合識別可能性の意味ではサーバーの IP アドレスもユーザーの IP アドレスも大して変わらないかもしれませんが
>}}。
それから、プッシュ通知を有効にするために以下のコマンドを実行して{{< link https://hide.ac/articles/Y504SIabp#title-8
>}}生成した鍵のペアを Service Worker の公開鍵と秘密鍵の欄に入力します{{< note ここでは説明のためにスクリーンショット内で秘密鍵を公開していますが、秘密鍵を公開してはいけません
>}}。
また、リモートのファイルをキャッシュする設定が無効になっていることを確認しましょう。
まだ念の為非公開モードは外さず、一度スナップショットを取ることにします{{< note もしこの後で何かを壊してしまって最初からやり直しになったら地獄なので
>}}。
サーバーの管理
基本事項
エラーの確認
たまに気が向いたときでよいので、失敗している systemd サービスやエラーログがあるかを確認しましょう{{< link https://wiki.archlinux.jp/index.php/%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0%E3%83%A1%E3%83%B3%E3%83%86%E3%83%8A%E3%83%B3%E3%82%B9#.E3.82.A8.E3.83.A9.E3.83.BC.E3.81.AE.E7.A2.BA.E8.AA.8D
>}}。
ログの削除
サーバーをそのまま動かし続けているとログファイルがどんどん溜まっていくので、古いログは適度に削除させましょう。/etc/systemd/journald.conf
の [Journal]
というセクションに
#SystemMaxUse=
という設定がコメントアウトされているので、これを解除して最大で保存するログの量を 500 MB くらいにしておきましょう。RuntimeMaxUse
も同様に設定しておくといいかもです。
SystemMaxUse=500M
設定を反映させます:
pacman の設定
paccache を用いて pacman のキャッシュを適度に削除するようにしましょう{{< link https://wiki.archlinux.jp/index.php/Pacman#.E3.83.91.E3.83.83.E3.82.B1.E3.83.BC.E3.82.B8.E3.82.AD.E3.83.A3.E3.83.83.E3.82.B7.E3.83.A5.E3.81.AE.E5.89.8A.E9.99.A4
>}}。
また、Reflector を使ってミラーリストを適度に更新するようにするのもよいでしょう{{< link https://zenn.dev/ohno418/articles/13e3472860881d
>}}。
paru -S reflector
sudo reflector --country Japan,Australia --age 24 --protocol https --sort rate --save /etc/pacman.d/mirrorlist
paru -Syyu
sudo vim /etc/xdg/reflector/reflector.conf # 好きなように編集
sudo systemctl enable --now reflector.timer
メンテナンス中に出すページの用意
サーバーのメンテナンス中にはその旨を知らせるページを出すとよいでしょう{{< note 以前、メンテナンス中のページを出さずに 1 時間サーバーを止めていたらアクティビティーの配送を停止されたことがあります
>}}。サーバーが落ちているときに自動でそのようなページを出すことも考えられますが、それではサーバーが不具合よって落ちているのかサーバーを意図的に落としているのかの区別がつかないので、ここではちゃんと手動でメンテナンス中にこのページを出すことにします。
HTML や CSS を頑張って凝ったページを作るのも一興ですが、例えば以下のようなテキストファイルを置くだけでも十分でしょう。HTML のページを作った場合にも同様に /srv/firefish.example.com
の下に置いておきます。
firefish.example.com は現在メンテナンス中です。
メンテナンスは最長でも数時間で終了する予定です。
firefish.example.com is currently under maintenance.
This will only take a few hours at the most.
連絡先/Contacts
Matrix: @example:matrix.example.com
XMPP: example@xmpp.example.com
HAM Radio: JA1XYZ (CW, any frequency within 21150-21450 kHz)
Email: example@mail.example.com
/etc/caddy/Maintenance.caddyfile
というもう一つの Caddy の設定ファイル{{< note caddyfile という拡張子は一般的ではありません
>}}を作り、以下のように書きます:
firefish.example.com {
@blocked header_regexp User-Agent .*Bytespider.*
handle @blocked {
abort
}
@proxy path /storage/*
redir @proxy https://storage.firefish.example.com/firefish{uri}
root * /srv/firefish.example.com
try_files maintenance.txt
file_server {
status 503
}
log {
output file /var/log/caddy/maintenance.log
}
}
storage.firefish.example.com {
@blocked header_regexp User-Agent .*Bytespider.*
handle @blocked {
abort
}
reverse_proxy http://127.0.0.1:9000
log {
output file /var/log/caddy/storage.log
}
}
minio.firefish.example.com {
@blocked header_regexp User-Agent .*Bytespider.*
handle @blocked {
abort
}
reverse_proxy http://127.0.0.1:9001
log {
output file /var/log/caddy/minio.log
}
}
Firefish サーバーを落としている間もオブジェクトストレージへのリダイレクトは継続しておくことで投稿の添付画像などがリモートサーバーからずっと見えるようになります{{< note minio のメンテナンスをするときにはこれも停止しなければなりませんが、私は今のところ paru によるアップデート以外に minio をいじったことがありません。なんなら管理画面にもずっとアクセスしていないから管理画面は閉じておいたほうがいいのかも
>}}。
以下のコマンドでページの表示を切り替えます。
# メンテナンス中のページを出す
caddy reload --config /etc/caddy/Maintenance.caddyfile
# メンテナンスおしまい
caddy reload --config /etc/caddy/Caddyfile
PostgreSQL の調整
PostgreSQL の初期の設定は Firefish サーバーを動かすのに適していません。
PGTune という Web アプリで、より良い設定を教えてもらえます{{< link https://pgtune.leopard.in.ua/
>}}。今回の例だとこんな感じのサーバー向けの設定を教えてもらうとよいでしょう:
PostgreSQL のバージョン: 16
OS ファミリー: Linux
データベースの種類: Data warehouse
メインメモリの容量: 1500 MB
(論理)CPU の数: 3
ストレージ: SSD storage
メインメモリの容量を 2 GB ではなく 1500 MB としているのは、Redis や MinIO やファイヤーウォールなどの他のソフトウェアもメモリを食べることを考えてのことです{{< note 私は以前ここの単位を変えられることに気づかなくて、1 GB のときの結果と 2 GB のときの結果の算術平均を取って使っていました……
>}}。
設定のうちの該当する部分を変更し、PostgreSQL を再起動します:
sudo vim /var/lib/postgres/data/postgresql.conf
sudo systemctl stop firefish
sudo systemctl restart postgresql
sudo systemctl start firefish
また、気が向いた際にデータベースの VACUUM を行うとよいです。少し時間が掛かります。
# サーバーを止めてメンテナンス中のページを出す
sudo systemctl stop firefish
caddy reload --config=/etc/caddy/Maintenance.caddyfile
# データベースの VACUUM をする
sudo --user=postgres \
psql --dbname=firefish_db --command="VACUUM FULL VERBOSE ANALYZE;"
# メンテナンス中の表示を解除してサーバーを起動する
caddy reload --config=/etc/caddy/Caddyfile
sudo systemctl start firefish
バックアップ
スナップショット
スナップショットを取ると立てた環境をまるまるバックアップできます。アップデートの前とかにやっておくと便利です。
スナップショットを取る前には Firefish の systemd サービスを無効化し(つまりサーバーが起動したときに自動で Firefish が立ち上がらないようにし)、サーバーをシャットダウンしましょう。なぜなら、スナップショットを使うときというのはサーバーがヤバい状態になって心臓がバクバクしているときですから、スナップショットから復元してサーバーを立ち上げたときに Firefish が自動で立ち上がってリモートサーバーから投稿をどんどん受け取るようになっていたら焦るからです{{< note 少なくとも私なら
>}}。
ここまでさんざん systemctl
コマンドを使っておいて今いきなりする話ではありませんが、systemctl disable firefish
は Firefish を停止しませんし、systemctl enable firefish
は Firefish を起動しません。disable
/enable
はあくまで自動起動の設定を変えるだけです。
Firefish サービスの起動と停止は
で行います。start
と enable
を両方とも行いたければ
を使えます。
シャットダウンしたら、コントロールパネルの「イメージ保存」のボタンをクリックしてスナップショットを保存します。
スナップショットの名前を設定することを求められますが、ここに日付などを入れても全く意味が無いことに注意してください(スナップショット一覧に日付が表示されるため)。名前にピリオド (.
) を含められないのが少し残念。
左のメニューから「イメージ」のページに飛んで、保存中のスナップショットのステータスが「利用可能」になったらスナップショットの作成は終了し、サーバーをまた起動できます。スナップショットの作成には最大で 10 分間くらい掛かる気がします。
サーバーを立ち上げた後、Firefish が自動起動しない設定になっていることを忘れずに。
スナップショットの肥大化防止
VPS のスナップショットの仕組みはよく知りませんが、スナップショットの大きさが記憶域の実際の使用量より大きくなるという現象が色々な VPS で起きるようです{{< link https://qiita.com/CloudRemix/items/5c6c285af11bea36a270
>}}。
0 で埋められた巨大なファイルで記憶域の空いた部分を埋め、そのファイルを削除するとこの問題を解消できるようなので、それを行うスクリプトを作って{{< link https://code.naskya.net/repos/q32v3/source-by/main/disk_fill_cleanup
>}}作業用ユーザーのホームディレクトリなどに置いておきます:
#!/bin/sh
set -eu
say() {
tput setaf 3
printf '%s\n' "$1"
tput setaf 7
}
run() {
tput setaf 5
printf '[run] $ %s\n' "$1"
tput setaf 7
/bin/sh -c "$1"
}
free_space=$(df -m --output=avail "$(pwd)" | tail -1)
say "[info] ${free_space} MiB are available"
tmpfile="$(mktemp --tmpdir=. --dry-run)"
run "dd if=/dev/zero of=${tmpfile} status=progress bs=1M count=$((free_space - 300))"
run "rm ${tmpfile}"
run 'df --human-readable'
free_space=$(df -m --output=avail "$(pwd)" | tail -1)
say "[info] ${free_space} MiB are available"
このスクリプトは途中で失敗すると作ったファイルが削除されずに記憶域がいっぱいになってしまう可能性があるため、自動化せずに必ず手で実行の様子を見守ることにしましょう。
また、サーバーの動作に不具合が起きた場合に「取りあえず再起動」するのはやめましょう。再起動は様々な問題を解決する可能性のある強力な方法ですが、記憶域がいっぱいであることが不具合の原因であった場合にはサーバーが再び起動してこなくなってしまう可能性があります。何も考えずに再起動をする前に記憶域の使用量を確認しましょう。
データベースのバックアップ
VPS のスナップショットは環境を壊したときにサクっと元の状態に戻せるから便利ですが、
- ConoHa VPS は 50 GB までしか無料でスナップショットを保存させてくれないから過去の複数のスナップショットを保持しておけないし、これに頼ろうとすると契約した VPS の容量いっぱいまで使えない
- ConoHa VPS がおしまいになったら{{< note
または自分が BAN されたら
>}} VPS もバックアップも一緒におしまいになる - (別のサーバーを使わない限り)スナップショットを取っている間にはメンテナンス中のページを出せない
- 自動化が難しい
などの理由からあんまり頼りにしてはいません。
ここでは systemd タイマーを使って pg_dump
を定期的に行い、外部のストレージ(ここでは例として MEGA を使用します{{< note 私は実際には手元の PC にデータを保存しています。しかしこれは PC が壊れたり物理的にアクセスできなくなったりしたらバックアップを救出できなくなる可能性があるのでおすすめしません。もちろんクラウドストレージにも BAN されるリスクがあるのでなんとも言い難いですが……
>}})にデータベースを暗号化して保存することにします。
MEGA にはファイルを操作するための MEGAcmd という便利なツールがありますが、ここでは外部のストレージの一例として MEGA を用いているだけなので他のストレージを使う場合も考えてより一般的に使える{{< link https://rclone.org/#providers
>}}方法を用います。
例えばここではデータベースのバックアップを 1 時間に 1 回行い、 - 1 時間ごとのバックアップ 12 個 - 1 日間ごとのバックアップ 6 個 - 1 週間ごとのバックアップ 3 個 - 1 か月間ごとのバックアップ 3 個
をそれぞれ保持することにします。
この場合、例えばサーバーの状態がマズいことが 4 日後に発覚した場合に最悪 4 日前のバックアップを用いればよいということになります。まずい状態のサーバーを 3 か月間放置してしまったらおしまいです{{< note 急病で長期間入院してしまった場合などにはあり得るかもしれません。その際にはどうにかして VPS かクラウドストレージにアクセスしてバックアップを救出しましょう……
>}}。
手でやってみる
いきなり自動化する前に、まずは手でデータベースをバックアップしてみましょう。バックアップの暗号化に使う長くて複雑なパスフレーズを生成してパスワード管理ツールに記録させ、~/backup_firefish/passphrase
という場所に保存しておきます。
cd ~
pwd # /home/naskya
mkdir backup_firefish
cd backup_firefish
vim passphrase && chmod 400 passphrase
pg_dump
を実行してデータベースをダンプします。このとき Firefish サーバーを止める必要はありません{{< link https://www.postgresql.org/docs/current/app-pgdump.html#PG-DUMP-DESCRIPTION
>}}。
データベースのダンプはこれでできるのですが、後に自動化する際には sudo
コマンドが入ってくると厄介です。
としてデータベースをダンプできるように、~/.pgpass
というファイルを作っておきます{{< link https://www.postgresql.org/docs/current/libpq-pgpass.html#LIBPQ-PGPASS
>}}。
.pgpass
ファイルには以下の内容を記述します。最後に書くデータベースのパスワードは /opt/firefish/.config/default.yml
に書いてあります。
localhost:5432:firefish_db:firefish:PASSWORD
データベースのダンプが終わるとディレクトリの中身はこのようになります。
[naskya@localhost backup_firefish]$ ls -l --human-readable
total 288K
-rw-r--r-- 1 naskya naskya 283K Dec 7 07:02 firefish_db.sql
-r-------- 1 naskya naskya 129 Dec 7 07:01 passphrase
このバックアップは平文で書かれているので、中身を確認できます:
--
-- PostgreSQL database dump
--
-- Dumped from database version 16.1
-- Dumped by pg_dump version 16.1
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
...
これを gpg2
コマンドで暗号化します:
暗号化の前にファイルの圧縮が行われるので{{< link https://security.stackexchange.com/a/84083
>}}、できた firefish_db.sql.gpg
は元のファイルよりも小さくなります:
[naskya@localhost backup_firefish]$ ls -l --human-readable
total 336K
-rw-r--r-- 1 naskya naskya 283K Dec 7 07:02 firefish_db.sql
-rw-r--r-- 1 naskya naskya 45K Dec 7 07:04 firefish_db.sql.gpg
-r-------- 1 naskya naskya 129 Dec 7 07:01 passphrase
ここで、passphrase
ファイルではなくパスワード管理ツールに保存したパスフレーズを入力して暗号化されたファイルを元に戻せるか確認しておきます。ちゃんと復号できなければバックアップを保存してもタンスの肥やしになるだけです。
復号したファイルと元のファイルが完全に一致していれば問題ありません。
diff --report-identical-files firefish_db.sql firefish_db_decrypted.sql
# Files firefish_db.sql and firefish_db_decrypted.sql are identical と出ればよい
そうしたら、暗号化されていない平文のデータは消してしまいます{{< link https://atmarkit.itmedia.co.jp/flinux/rensai/linuxtips/662delfile.html
>}}。
これで、残った firefish_db.sql.gpg
を外部のストレージに転送しておけばよいというわけです。
自動でやる
rclone をインストールし、rclone のドキュメントを参考に MEGA に接続します{{< link https://rclone.org/mega/
>}}。他のストレージを使う場合にはそのサービスに対応するページを参照してください。最初の設定(ユーザー名やパスワードの入力など)が終わればその後の操作はほとんど同じです。接続先には名前をつける必要がありますが、ここでは remote
としています。
試しに test.txt
というファイルを作って転送してみましょう。
echo Hello > test.txt # Hello という中身の test.txt を作る
rclone copy test.txt remote: # remote に test.txt をコピーする
クラウドストレージにファイルが転送されます。
手元のマシンのデータを手元のマシンの別のドライブにバックアップするような用途では rsnapshot というツールがおすすめなのですが、バックアップ先がリモートのストレージの場合に rsnapshot は適していない{{< link https://serverfault.com/a/430774
>}}{{< link https://askubuntu.com/a/35199
>}}ようなので別のツールを探します。
調べてみるとこのようなファイルのバックアップには Kopia, Duplicati, restic, duplicity などのいくつかのツールが使えるようですが、なんだかどれも機能過多な気がしたので自分でスクリプトを書いて使うことにします。
バックアップ用の外部のストレージに以下のようなフォルダの階層を作ります{{< note backup_firefish と firefish_backup というディレクトリがありますが、前者は「Firefish をバックアップするための道具の場所」で後者は「Firefish のバックアップの保存場所」という気持ちによる命名です。紛らわしい場合には名前を揃えてもよいです。
>}}:
.
└─ firefish_backup
├─ object_storage
└─ database
├─ hourly
├─ daily
├─ weekly
└─ monthly
rclone mkdir remote:firefish_backup
for name in object_storage database; do
rclone mkdir "remote:firefish_backup/${name}"
done
for name in hourly daily weekly monthly; do
rclone mkdir "remote:firefish_backup/database/${name}"
done
作戦はこうです:
- 毎時 0 分
- バックアップを作成して
hourly
フォルダにアップロードする hourly
フォルダにあるファイルが 12 個を超えていたら古いものを削除する
- バックアップを作成して
- 毎日 05:30 {{< note
あまり負荷が高くなさそうないい時刻が良いと思ったため。また毎時 0 分の作業と被らないように余裕をもって 30 分間ずらしている。
>}}hourly
フォルダにある最も古いファイルをdaily
フォルダに移動するdaily
フォルダにあるファイルが 6 個を超えていたら古いものを削除する
- 毎週日曜日の 04:30 {{< note
05:30 に上の工程で削除されてしまうファイルを救うためにそれより前の時刻にしている。
>}}daily
フォルダにある最も古いファイルをweekly
フォルダに移動するweekly
フォルダにあるファイルが 3 個を超えていたら古いものを削除する
- 毎月第一日曜日の 03:30
weekly
フォルダにある最も古いファイルをmonthly
フォルダに移動するmonthly
フォルダにあるファイルが 3 個を超えていたら古いものを削除する
本当はもっと工夫の余地があるダサいバックアップ方法ですが、まぁ一人ぼっちサーバーのバックアップなんてこれくらいでいいよね……
使うクラウドストレージのサービスによって仕様が微妙に違う可能性があるので気をつけてください。例えば MEGA では rclone delete
でファイルを消してもファイルがゴミ箱に移動するだけで記憶域の使用量が変わらないので、rclone cleanup
を用いてゴミ箱の中にあるファイルも消す必要があります。S3 互換のストレージを用いる場合にも追加の設定が必要になることがあるようです{{< link https://post.sup39.dev/notes/9o2qa0iuvamguwax
>}}。
./backup_firefish/database.sh hourly
./backup_firefish/database.sh daily
./backup_firefish/database.sh weekly
./backup_firefish/database.sh monthly
のように(hourly
などの)引数を与えると上記の内容を実行するようなスクリプトを作ります{{< note 説明のために冗長な書き方をしています
>}}。
#!/bin/sh
set -eu
take_snapshot() {
# ファイル名は 20231224010000.sql のようにする(ソートしやすいため)
DUMP_FILE=$(printf '%s.sql' "$(date +'%Y%m%d%H%M%S')")
# さっき手でやったことをここでやる
pg_dump --format=plain --user=firefish --dbname=firefish_db --file="${DUMP_FILE}"
gpg2 --symmetric --passphrase-file passphrase --pinentry-mode loopback "${DUMP_FILE}"
shred --remove "${DUMP_FILE}"
# 最終的にできるファイル名は 20231224010000.sql.gpg のようになっていることに注意
printf '%s.gpg' "${DUMP_FILE}"
}
hourly() {
BACKUP_DIR='firefish_backup/database/hourly'
MAX_NUM_FILES='12'
# スナップショットを取り、最後に printf で出力される名前を取得する
BACKUP_FILE=$(take_snapshot)
# hourly ディレクトリにコピーする
rclone copy "${BACKUP_FILE}" "remote:${BACKUP_DIR}"
# コピーが終わったらローカルにあるバックアップファイルは消してしまう
shred --remove "${BACKUP_FILE}"
# ディレクトリの中身はどうなってるかな
FILES=$(rclone ls "remote:${BACKUP_DIR}" | awk '{ print $2 }' | sort)
# ファイルはいくつあるかな
NUM_FILES=$(printf '%s' "${FILES}" | wc --lines)
# もし保存する最大数を超えていたら古いやつ(ソートして最初にくるやつ)を消そうね
while [ "${NUM_FILES}" -gt "${MAX_NUM_FILES}" ]; do
FILE_TO_BE_DELETED=$(printf '%s' "${FILES}" | head -1)
rclone delete "remote:${BACKUP_DIR}/${FILE_TO_BE_DELETED}"
# ゴミ箱の中も消す必要がある場合には以下をコメントアウト
# rclone cleanup remote:
# 念のためディレクトリをまた確認
FILES=$(rclone ls "remote:${BACKUP_DIR}" | awk '{ print $2 }' | sort)
NUM_FILES=$(printf '%s' "${FILES}" | wc --lines)
done
}
daily() {
FROM_DIR='firefish_backup/database/hourly'
TO_DIR='firefish_backup/database/daily'
MAX_NUM_FILES='6'
# hourly ディレクトリにある一番古いファイルは何かな
TARGET=$(rclone ls "remote:${FROM_DIR}" | awk '{ print $2 }' | sort | head -1)
# hourly ディレクトリに何もなかったらおしまい
if [ "${TARGET}" = '' ]; then
exit 0
fi
# hourly ディレクトリにある一番古いファイルを daily ディレクトリに動かそう
rclone move "remote:${FROM_DIR}/${TARGET}" "remote:${TO_DIR}/"
# daily ディレクトリの中身はどうなってるかな
FILES=$(rclone ls "remote:${TO_DIR}" | awk '{ print $2 }' | sort)
# ファイルはいくつあるかな
NUM_FILES=$(printf '%s' "${FILES}" | wc --lines)
# もし保存する最大数を超えていたら古いやつ(ソートして最初にくるやつ)を消そうね
while [ "${NUM_FILES}" -gt "${MAX_NUM_FILES}" ]; do
FILE_TO_BE_DELETED=$(printf '%s' "${FILES}" | head -1)
rclone delete "remote:${TO_DIR}/${FILE_TO_BE_DELETED}"
# ゴミ箱の中も消す必要がある場合には以下をコメントアウト
# rclone cleanup remote:
# 念のためディレクトリをまた確認
FILES=$(rclone ls "remote:${TO_DIR}" | awk '{ print $2 }' | sort)
NUM_FILES=$(printf '%s' "${FILES}" | wc --lines)
done
}
weekly() {
FROM_DIR='firefish_backup/database/daily'
TO_DIR='firefish_backup/database/weekly'
MAX_NUM_FILES='3'
TARGET=$(rclone ls "remote:${FROM_DIR}" | awk '{ print $2 }' | sort | head -1)
if [ "${TARGET}" = '' ]; then exit 0; fi
rclone move "remote:${FROM_DIR}/${TARGET}" "remote:${TO_DIR}/"
FILES=$(rclone ls "remote:${TO_DIR}" | awk '{ print $2 }' | sort)
NUM_FILES=$(printf '%s' "${FILES}" | wc --lines)
while [ "${NUM_FILES}" -gt "${MAX_NUM_FILES}" ]; do
FILE_TO_BE_DELETED=$(printf '%s' "${FILES}" | head -1)
rclone delete "remote:${TO_DIR}/${FILE_TO_BE_DELETED}"
# rclone cleanup remote:
FILES=$(rclone ls "remote:${TO_DIR}" | awk '{ print $2 }' | sort)
NUM_FILES=$(printf '%s' "${FILES}" | wc --lines)
done
}
monthly() {
FROM_DIR='firefish_backup/database/weekly'
TO_DIR='firefish_backup/database/monthly'
MAX_NUM_FILES='3'
TARGET=$(rclone ls "remote:${FROM_DIR}" | awk '{ print $2 }' | sort | head -1)
if [ "${TARGET}" = '' ]; then exit 0; fi
rclone move "remote:${FROM_DIR}/${TARGET}" "remote:${TO_DIR}/"
FILES=$(rclone ls "remote:${TO_DIR}" | awk '{ print $2 }' | sort)
NUM_FILES=$(printf '%s' "${FILES}" | wc --lines)
while [ "${NUM_FILES}" -gt "${MAX_NUM_FILES}" ]; do
FILE_TO_BE_DELETED=$(printf '%s' "${FILES}" | head -1)
rclone delete "remote:${TO_DIR}/${FILE_TO_BE_DELETED}"
# rclone cleanup remote:
FILES=$(rclone ls "remote:${TO_DIR}" | awk '{ print $2 }' | sort)
NUM_FILES=$(printf '%s' "${FILES}" | wc --lines)
done
}
if [ "$#" != '1' ]; then exit 1;
elif [ "$1" = 'hourly' ]; then hourly;
elif [ "$1" = 'daily' ]; then daily;
elif [ "$1" = 'weekly' ]; then weekly;
elif [ "$1" = 'monthly' ]; then monthly;
else exit 1; fi
このスクリプトを ~/backup_firefish/database.sh
などに保存し、systemd タイマーで決まった時刻にこれを呼ぶ設定をしていきます{{< link https://wiki.archlinux.jp/index.php/Systemd/%E3%82%BF%E3%82%A4%E3%83%9E%E3%83%BC
>}}。
このスクリプトを実行するための systemd サービスを作ります。
# backup_firefish_database@.service
[Unit]
Description=Backup Firefish database (%I)
Requires=postgresql.service
After=postgresql.service network-online.target
[Service]
Type=oneshot
User=naskya
WorkingDirectory=/home/naskya/backup_firefish
ExecStart=/home/naskya/backup_firefish/database.sh %i
sudo vim /etc/systemd/system/backup_firefish_database@hourly.timer
sudo vim /etc/systemd/system/backup_firefish_database@daily.timer
sudo vim /etc/systemd/system/backup_firefish_database@weekly.timer
sudo vim /etc/systemd/system/backup_firefish_database@monthly.timer
[Unit]
Description=Firefish database hourly backup
[Timer]
OnCalendar=*-*-* *:00:00
Persistent=false
[Install]
WantedBy=timers.target
[Unit]
Description=Firefish database daily backup
[Timer]
OnCalendar=*-*-* 05:30:00
Persistent=false
[Install]
WantedBy=timers.target
[Unit]
Description=Firefish database weekly backup
[Timer]
OnCalendar=Sun 04:30:00
Persistent=false
[Install]
WantedBy=timers.target
[Unit]
Description=Firefish database monthly backup
[Timer]
OnCalendar=Sun *-*-1..7 03:30:00
Persistent=false
[Install]
WantedBy=timers.target
用意した全てのタイマーを有効化すると、データベースが自動的にバックアップされます。
sudo systemctl enable --now \
backup_firefish_database@hourly.timer \
backup_firefish_database@daily.timer \
backup_firefish_database@weekly.timer \
backup_firefish_database@monthly.timer
オブジェクトストレージのバックアップ
今回はオブジェクトストレージもセルフホストしているので、その中身もバックアップしておくべきです。ただしこちらはドライブのファイルだけなので、常に最新版のバックアップだけ持っていれば大丈夫だと思っています(心配ならこちらもバージョン管理してよいです)。
rclone の接続先にローカルの MinIO を追加する
MinIO は Amazon S3 互換のオブジェクトストレージなので、rclone の接続先にそのまま登録できます。rclone config
から local_minio
という名前で AWS S3 API
対応のオブジェクトストレージとして登録します。エンドポイントの URL として http://localhost:9000
を用いることに注意します{{< link https://docs.openio.io/latest/source/integrations/cookbook_rclone.html
>}}。
アクセスキーとシークレットキーは MinIO の管理画面 (https://minio.firefish.example.com
) で生成して使用します。
region
, locacion_constraint
, acl
, server_side_encryption
など心配になるほど大量な情報の入力を求められますが、全て何も入力せずに Enter キーを押して進んでよいです。
暗号化してバックアップする
登録が完了したら試しに以下のコマンドを実行してみると、firefish
バケットの中身が全て手元にコピーされます。
この storage
ディレクトリを丸ごと暗号化してバックアップ用のストレージに転送すればオブジェクトストレージのバックアップは完了です!
#!/bin/sh
set -eu
# オブジェクトストレージの中身を手元にコピー
rclone copy local_minio:firefish .
# storage ディレクトリが手元に来るので tar でまとめる
tar --gzip --create --file=storage.tar.gz storage/
# 暗号化
gpg2 --symmetric --passphrase-file passphrase --pinentry-mode loopback storage.tar.gz
# 元々ある storage.tar.gz.gpg を storage.tar.gz.gpg.bak に改名
rclone moveto remote:firefish_backup/object_storage/storage.tar.gz.gpg \
remote:firefish_backup/object_storage/storage.tar.gz.gpg.bak
# クラウドストレージにアップロード
rclone copy storage.tar.gz.gpg remote:firefish_backup/object_storage
# クラウドの storage.tar.gz.gpg.bak を削除
rclone delete remote:firefish_backup/object_storage/storage.tar.gz.gpg.bak
# ゴミ箱の中のファイルを消す
# rclone cleanup remote:
# 手元のファイルを削除
rm --recursive --force storage/ storage.tar.gz storage.tar.gz.gpg
このスクリプトはバックアップ用のストレージに既に storage.tar.gz.gpg
というファイルがあることを仮定して書かれているので、初回実行の前にこの名前のテキストファイルなどを適当に作って置いておく必要があります{{< note 雑でごめん
>}}。
これも systemd タイマーで定期的に自動実行します:
sudo vim /etc/systemd/system/backup_firefish_object_storage.service
sudo vim /etc/systemd/system/backup_firefish_object_storage.timer
sudo systemctl enable --now backup_firefish_object_storage.timer
# backup_firefish_object_storage.service
[Unit]
Description=Backup Firefish object storage
Requires=minio.service
After=minio.service network-online.target
[Service]
Type=oneshot
User=naskya
WorkingDirectory=/home/naskya/backup_firefish
ExecStart=/home/naskya/backup_firefish/object_storage.sh
# backup_firefish_object_storage.timer
[Unit]
Description=Firefish object storage hourly backup
[Timer]
OnCalendar=*-*-* *:20:00
Persistent=false
[Install]
WantedBy=timers.target
これでオブジェクトストレージのバックアップは完了です。
Redis のデータは消えてもジョブキューの内容とアンテナに引っ掛かった投稿とレートリミットの情報が飛ぶだけなのでバックアップはしません。
アップデート
Firefish のアップデート
アップデートの前には必ずサーバーを止め、バックアップを取りましょう。ダウンタイム無しのアップデートは難しいらしいです{{< note 私は興味が無いので方法を調べたことがありません。知ってたら教えてください(やらないけど)
>}}。
今回インストールした Firefish のフレーバーはローリングリリースを採用しているので、気が向いたときにアップデートスクリプトを回せばよいです。
# サーバーを止めてメンテナンス中のページを出す
sudo systemctl stop firefish
caddy reload --config /etc/caddy/Maintenance.caddyfile
# firefish ユーザーになってアップデートする
sudo --user=firefish bash
cd ~
./update.sh # 表示される指示に従う
exit
# メンテナンス中のページを出すのをやめてサーバーを起動する
caddy reload --config /etc/caddy/Caddyfile
sudo systemctl start firefish
本家 Firefish をお使いなら、新バージョンがリリースされたらアップデートしましょう。アップデートの部分のコマンドは例えば以下のようにするとよいです:
git pull --ff --no-edit --autostash --strategy-option theirs
corepack prepare pnpm@latest --activate
pnpm install --frozen-lockfile
NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run build
NODE_ENV='production' NODE_OPTIONS='--max_old_space_size=3072' pnpm run migrate
OS やその他のソフトウェアのアップデート
Arch Linux もローリングリリースを採用しているので、気が向いたときにインストールした他のプログラムと一緒にアップデートしましょう。
これはなに (revisited)
なにこれ?
まず目次が長すぎて引きました。
サーバー管理なんて本当にやりたくないです。
でもまぁここまで整備しちゃえばメンテはめちゃ楽だしアップデートなんて何も怖くありません。
おしまい。