🍥
Windows から Blazor Web App をデプロイする Debian Server の構成
はじめに
これは、本論の課題を解決した際のメモ書きで、未来の自分に宛てたものです。
未完成のもので、随時更新される可能性があります。
概要
- Windowsで開発し、Debianでデプロイします。
- いったん、Ubuntu Serverで実施したのですが、後に、Debianでやり直しました。
- 特に断りがなければ、Debianについて記載しています。
- 経緯から、Ubuntuについては網羅されていません。
前提
- Windows 11 Pro 22H2
- Debian 12.2.0
- ASP.NET Core 7.0.14, 8.0.0
- nginx 1.22.1
- MariaDB 10.11.4-MariaDB-1
- PHP 8.2.7
- phpMyAdmin 5.2.1deb1
- Ubuntu 22.04.3 LTS
- ASP.NET Core 7.0.13
- nginx 1.18.0
- MariaDB 10.6.12
- PHP 8.1.2-1ubuntu2.14
- phpMyAdmin 5.1.1
サーバの準備
OS導入
ダウンロード
ブータブルUSBメモリ作成
- ついでに、SSH用の公開鍵を入れておきます。
USBブートしてインストール
- 環境による文字化けを避けるため、ロケールは英語にしておきます。
- ロケーション日本でロケール英語だと、選択が少し面倒になります。
- デスクトップは入れません。
- Webサーバは、nginxを使うので入れません。
- SSHサーバにします。
OSのバージョン確認
bash
$ cat /etc/os-release
$ cat /etc/debian_version
sudo
-
root
でログインするか、あるいは、su
コマンドで昇格します。
導入
bash
# apt install sudo
グループへの参加
bash
# adduser <user> sudo
-
user
アカウントでsudo
が使えるようになったので、そちらでログインし直します。-
su
コマンドを使った場合は、単にexit
します。
-
ssh 公開鍵認証
認証キー生成
コマンドプロンプト
>ssh-keygen -t ed25519 -f "%USERPROFILE%\.ssh\<keyfile>"
パスフレーズを入力 ⇒ 指定の秘密鍵ファイルに加えて公開鍵ファイル(.pub
)が生成
現在の認証キーを確認
bash
$ cat ~/.ssh -l
- 初期状態でディレクトリが存在しません。
認証キー置き換え
- USBメモリから公開鍵を取り出してコピーします。
bash
$ cp /media/<keyfile>.pub ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys
公開鍵認証の強制 (パスワード認証の禁止)
bash
$ sudoedit /etc/ssh/sshd_config
/etc/ssh/sshd_config
PubkeyAuthentication yes
PasswordAuthentication no
USBメモリからの取り出し
SCSIデバイスを一覧
bash
$ ls /dev/sd*
接続デバイスを特定
接続前
$ ls /dev/sd*
/dev/sda /dev/sdb /dev/sdb1 /dev/sdc /dev/sdc1 /dev/sdc2
接続後
$ ls /dev/sd*
/dev/sda /dev/sdb /dev/sdb1 /dev/sdc /dev/sdc1 /dev/sdc2 /dev/sdd /dev/sdd1
接続前後の相違から、ドライブ名/dev/sdd
とパーティション名/dev/sdd1
を得る
マウント
sudo mount <device> <mountpoint>
bash
$ sudo mount /dev/sdd1 /media
アンマウント
sudo umount <mountpoint>
bash
$ sudo umount /media
IP/MACアドレス確認
bash
$ ip address show
$ ip address
$ ip a
※いずれも同等
DHCPによる静的IP割り当て
- 別途、DHCPサーバー側の設定が必要です。
設定追記
bash
$ sudoedit /etc/dhcp/dhclient.conf
/etc/dhcp/dhclient.conf
#~ ~ ~
send dhcp-client-identifier = hardware;
#~ ~ ~
ubuntsu
より大きな番号の~.yaml
を追加することで上書き
bash
$ sudoedit /etc/netplan/01-dhcp-identifier-mac.yaml
/etc/netplan/01-dhcp-identifier-mac.yaml
# This is the network config by user
network:
ethernets:
enp2s0:
dhcp4: true
dhcp-identifier: mac
version: 2
追加ファイルのパーミッション設定
bash
$ sudo chmod 600 /etc/netplan/01-dhcp-identifier-mac.yaml
設定反映
bash
$ sudo rm /var/lib/dhcp/*
$ sudo systemctl restart networking
ubuntsu
bash
$ sudo netplan apply
ターミナルからログイン
コマンドプロンプト
>ssh -i "%USERPROFILE%\.ssh\<keyfile>" <user>@<address>
Windowsターミナル設定
プロファイル
設定 > 新しいプロファイル > コマンドライン
コマンドライン
%SystemRoot%\System32\OpenSSH\ssh.exe -i "%USERPROFILE%\.ssh\<keyfile>" <user>@<address>
ショートカット
エクスプローラー > 新規作成 > ショートカット > 項目の場所
項目の場所
%USERPROFILE%\AppData\Local\Microsoft\WindowsApps\wt.exe nt -p "<profilename>"
ルート権限が必要なファイルの編集 (sudoedit)
bash
$ sudoedit <targetfile>
- デフォルトでは
nano
が起動します。-
sudo update-alternatives --config editor
で、設定を選択できます。 -
sudo update-alternatives --display editor
で、現在の設定を確認できます。
-
- セキュリティ上は、
sudoedit
が望ましいようです。-
sudoedit
では、- ルート権限で作られた一時ファイルをユーザ権限で編集し、編集後にルート権限で更新されます。
- Ubuntuでは、生成される一時ファイルに拡張子が付かないためか、エディタの設定にかかわらず、シンタックスハイライトが働きませんでした。(Debianでは問題ありません。)
-
sudo nano
とすると、- 不必要に権限が付与されます。
- ユーザの設定でなくルートの設定が使われます。
-
使用するエディタの変更
bash
$ sudo update-alternatives --config editor
nano の設定
共有設定
$ sudoedit /usr/share/nano/default.nanorc
個別設定
$ nano ~/.nanorc
~/.nanorc
# private settings
set mouse
set nowrap
set linenumbers
set autoindent
set matchbrackets "(<[{)>]}"
set tabsize 2
キー操作
Alt
+ M
: マウスモード切替
Alt
+ N
: 行番号切替
Alt
+ I
: 自動インデント切替
vim の設定
.vimrc
"set showcmd " Show (partial) command in status line.
"set showmatch " Show matching brackets.
set ignorecase " Do case insensitive matching
"set smartcase " Do smart case matching
set incsearch " Incremental search
"set autowrite " Automatically save before commands like :next and :make
"set hidden " Hide buffers when they are abandoned
set mouse=a " Enable mouse usage (all modes)
set number " 行番号
set hlsearch " 検索結果のハイライト
"set expandtab " タブをスペースに展開
set tabstop=2 " タブのサイズ
set shiftwidth=0 " 自動インテントのサイズ (0ならtabstopに従う)
set cursorline " カーソル行
set clipboard+=unnamed " クリップボードをOSと共有
set laststatus=2 " ステータス行のサイズ
set showmatch matchtime=1 " 対応する括弧やブレースを表示
set whichwrap=b,s,h,l,<,>,[,],~ " 行をまたいで移動
set nowrap " 折り返さない
環境変数
bash の設定
bash
$ nano ~/.bashrc # or ~/.bash_profile
- 自分用の設定です。
- 共通設定は
/etc/profile
にあります。
永続的環境変数の設定
~/.bashrc
# ~ ~ ~
export <key>="<value>"
ファイル転送
- 以降では、Windows側の操作を記載しますが、Linux側で操作する場合も基本的には同様です。
Windows側操作
WindowsからLinuxへ
コマンドプロンプト
>scp -i "%USERPROFILE%\.ssh\<keyfile>" -r <source> <user>@<address>:<destination>
LinuxからWindowsへ
コマンドプロンプト
>scp -i "%USERPROFILE%\.ssh\<keyfile>" -r <user>@<address>:<source> <destination>
システム操作
シャットダウン
bash
$ sudo shutdown now
再起動
bash
$ sudo shutdown -r now
自分だけなら
bash
$ sudo reboot
追加HDDをマウントする
ドライブを一覧
bash
$ lsblk
bash
$ fdisk -l
マウント状況を一覧
bash
$ mount -v
パーティションのUUIDを得る
sudo blkid <device>
bash
$ sudo blkid /dev/sda1
起動時マウント設定
bash
$ sudoedit /etc/fstab
/etc/fstab
#<file system> <mount point> <type> <options> <dump> <pass>
#~ ~ ~
UUID=<uuid> /mnt/hdd1 ext4 defaults 0 1
# or
/dev/disk/by-uuid/<uuid> /mnt/hdd1 ext4 defaults 0 1
ハードウェア確認
dmidecode
ハードウェア全般情報
bash
$ sudo dmidecode
項目一覧・選択
bash
$ sudo dmidecode -t [type]
lspci
PCIデバイス情報
bash
$ lspci
最大限詳細
bash
$ sudo lspci -vv
スリープ抑止
bash
$ sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
パッケージ管理
一覧の更新
bash
$ sudo apt update
導入済み一覧
bash
$ apt list --installed
更新可能一覧
bash
$ apt list --upgradable
導入済みパッケージの更新
更新後に不要になったパッケージを除去
bash
$ sudo apt full-upgrade
除去しない
bash
$ sudo apt upgrade
不要になったパッケージの除去
bash
$ sudo apt autoremove
パッケージの探索
完全一致
bash
$ apt list <package>
部分一致
bash
$ apt search <package>
導入
bash
$ sudo apt install <package>
除去
依存先を含める
bash
$ sudo apt --purge remove <package>
含めない
bash
$ sudo apt remove <package>
.NET Core Runtime
Microsoft パッケージ署名キーを追加
bash
$ wget https://packages.microsoft.com/config/debian/12/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
$ sudo dpkg -i packages-microsoft-prod.deb
$ rm packages-microsoft-prod.deb
$ sudo apt update
導入
bash
$ sudo apt install aspnetcore-runtime-8.0
バージョン確認
bash
$ dotnet --info
nginx
インストール
bash
$ sudo apt install nginx
apache 排除
$ sudo apt --purge remove apache2
バージョン確認
bash
$ /usr/sbin/nginx -v
設定ディレクトリ確認
bash
$ ls /etc/nginx/
conf.d fastcgi_params koi-win modules-available nginx.conf scgi_params sites-enabled uwsgi_params
fastcgi.conf koi-utf mime.types modules-enabled proxy_params sites-available snippets win-utf
設定確認
bash
$ sudoedit /etc/nginx/sites-available/default
サーバ名を設定
/etc/nginx/sites-available/default
#~ ~ ~
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name <servername>.<domainname>;
#~ ~ ~
root /var/www/html;
#~ ~ ~
公開ディレクトリ確認
bash
$ ls /var/www/html
index.nginx-debian.html
SSL を設定
DNS とファイアウォールを設定
- DNSとファイアウォールを設定し、サーバにアクセスできるようにします。
- インターネットからサーバへ
http
でアクセスできることを確認します。
証明書の取得と設定、更新
certbot 導入
bash
$ sudo apt install certbot
$ sudo apt install python3-certbot-nginx
証明書の取得
bash
$ sudo certbot certonly --nginx
- いくつか質問に答えます。途中でキャンセルした場合は、続きから再開できます。
- 完了すると、証明書と鍵の所在が表示されます。
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/<servername>.<domainname>/fullchain.pem
Key is saved at: /etc/letsencrypt/live/<servername>.<domainname>/privkey.pem
This certificate expires on <date>.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
- エラーする場合は、レポートの指示に従って見直せば、大抵、なんとかなります。
証明書を設定
- 取得した証明書と鍵を設定します。
/etc/nginx/sites-available/default
#~ ~ ~
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name <servername>.<domainname>;
return 301 https://$host$request_uri;
}
server {
# SSL configuration
#
listen 443 ssl default_server;
listen [::]:443 ssl default_server;
server_name <servername>.<domainname>;
ssl_certificate <Lets Encrypt Certificate>;
ssl_certificate_key <Lets Encrypt Key>;
#
#~ ~ ~
-
http
でのアクセスは、https
へリダイレクトされます。-
return
の行をコメントアウトすることでキャンセルできます。
-
証明書の更新
- 完了メッセージに示されていたように、既に自動更新が設定されています。
bash
$ cat /etc/cron.d/certbot
- 以下で自動更新をテストできます。
bash
$ sudo certbot renew --dry-run
証明書の確認
- 取得されている証明書の状態を確認します。
bash
$ sudo certbot certificates
開発用自己署名証明書
鍵作成
bash
$ sudo openssl genrsa -out <privatekey>.pem 2048
署名要求作成
bash
$ sudo openssl req -new -key <privatekey>.pem -out <signingrequest>.pem
証明書作成
bash
$ sudo openssl x509 -days 365 -req -key <privatekey>.pem -in <signingrequest>.pem -out <certificate>.pem
リバースプロキシーを構成
- Blazor Web Appに独立したサーバ名を与えておき、そこへのアクセスをアプリのポートに振り向けます。
設定ファイルを作成
bash
$ sudoedit /etc/nginx/sites-available/<project>.conf
/etc/nginx/sites-available/<project>.conf
map $http_connection $connection_upgrade {
"~*Upgrade" $http_connection;
default keep-alive;
}
server {
listen 80;
listen [::]:80;
server_name <servername>.<domainname>;
return 302 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
server_name <servername>.<domainname>;
ssl_certificate <certificate>.pem;
ssl_certificate_key <privatekey>.pem;
location / {
proxy_pass http://127.0.0.1:5000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $http_host;
proxy_cache_bypass $http_upgrade;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
-
http
でのアクセスは、https
へリダイレクトされます。- サーバー名ごとに、証明書
ssl_certificate
と秘密鍵ssl_certificate_key
の用意が必要です。
- サーバー名ごとに、証明書
- "
server_name
:listen
"へのアクセスをproxy_pass
のBlazor Web Appへ転送します。- nginxと外部の接続が
https
であるのに対して、アプリとの接続はhttp
です。 - アプリ側で以下の警告が出ます。
- nginxと外部の接続が
warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware`
Failed to determine the https port for redirect.
- なお、下記ドキュメントでは、上記全体が
http { ~ }
で括られていますが、/etc/nginx/sites-enabled
へ配置したものは、既にhttp { ~ }
内とみなされるため、外しておかないとエラーします。 - 同じく、
proxy_set_header Host $host;
だと、OAuthのリダイレクトURIからポート指定が欠落して、listen
ポートを標準(80
/443
)以外にした場合に認証に失敗するため、HTTPヘッダからHost
を取得して使用します。- 標準ポートを使用する場合は
Host $host;
で支障ありません。
- 標準ポートを使用する場合は
組み込み変数
-
$http_<name>
とすることで、HTTPヘッダから任意のname
を取り出せます。-
name
は、全て小文字にして、-
を_
に置き換えた名前を指定します。- 例えば、
User-Agent
なら$http_user_agent
とします。
- 例えば、
-
有効化 (シンボリックリンクを配置)
bash
$ sudo ln -s /etc/nginx/sites-available/<project>.conf /etc/nginx/sites-enabled/
無効化 (シンボリックリンクを削除)
bash
$ sudo rm /etc/nginx/sites-enabled/<project>.conf
構成のテスト
bash
$ sudo nginx -t
再読み込み
bash
$ sudo nginx -s reload
状態の確認
bash
$ sudo systemctl status nginx
ログの確認
bash
$ ls -l /var/log/nginx/
$ sudo cat /var/log/nginx/access.log
$ sudo cat /var/log/nginx/error.log
再起動
bash
$ sudo systemctl restart nginx
特定サイトのキャッシュをクリア (Google Chrome)
- 開発ツール(F12)を開いた状態で、リロードボタンを長押しして、出てきたメニューから「キャッシュの消去」を選ぶと、確認なしで消えます。
RDBMS/PHP
mariadb
導入
bash
$ sudo apt install mariadb-server
初期設定
bash
$ sudo mysql_secure_installation
-
unix_socket
にすることで、DBのrootパスワードを不要にできます。(OSのrootアカウントが使うと認証される) - 間違えた場合はやり直すことができますが、状況によって質問が変化するので、注意深く読んで答える必要があります。
バージョン確認
bash
$ mariadb --version
稼働状況確認
bash
$ sudo systemctl status mariadb
● mariadb.service - MariaDB 10.6.12 database server
Loaded: loaded (/lib/systemd/system/mariadb.service; enabled; vendor preset: enabled)
Active: active (running) since Mon 2023-10-30 12:19:52 JST; 25min ago
#~ ~ ~
アカウント確認
bash
$ sudo mariadb
#~ ~ ~
MariaDB [(none)]> show grants;
アカウントの権限変更
付与
bash
MariaDB [(none)]> grant ALL on *.* to '<username>'@localhost;
確認
bash
MariaDB [(none)]> show grants for '<username>'@localhost;
新規作成
bash
MariaDB [(none)]> create user '<username>'@localhost identified by '<password>';
パスワード変更
bash
MariaDB [(none)]>set password for '<username>'@localhost = '<password>'
終了
bash
MariaDB [(none)]> exit;
php
導入
bash
$ sudo apt install php
バージョン確認
bash
$ php -v
php-fpm
導入
bash
$ sudo apt install php-fpm
参照
bash
$ tail /etc/php/8.2/fpm/php-fpm.conf
/etc/php/8.2/fpm/php-fpm.conf
#~ ~ ~
include=/etc/php/8.2/fpm/pool.d/*.conf
bash
$ ls /etc/php/8.2/fpm/pool.d/*.conf
/etc/php/8.2/fpm/pool.d/www.conf
bash
$ cat /etc/php/8.2/fpm/pool.d/www.conf
/etc/php/8.2/fpm/pool.d/www.conf
#~ ~ ~
listen = /run/php/php8.2-fpm.sock
#~ ~ ~
Basic認証
導入
bash
$ sudo apt install apache2-utils
パスワード生成
bash
# use '-c' to create it
$ sudo htpasswd -c /var/www/<path>/.htpasswd <user>
phpMyAdmin
導入
bash
$ sudo apt install phpmyadmin
- Webサーバの種別を尋ねられた際は、選択肢に
nginx
がないので、何もチェックしません。 - ここで尋ねられるパスワードは、
phpMyAdmin
のものです。 - 間違った際は、いったん(DBの消去を含め)
remove
して、再度install
することで、やり直すことができます。
設定
アカウントの権限変更
bash
$ sudo mariadb
#~ ~ ~
MariaDB [(none)]> grant all on *.* to phpmyadmin@localhost;
#~ ~ ~
MariaDB [(none)]> show grants for phpmyadmin@localhost;
#~ ~ ~
MariaDB [(none)]> exit;
nginxのルートディレクトリに配置
bash
$ sudo ln -s /usr/share/phpmyadmin /var/www/html/<path>
nginxへ設定
bash
$ sudoedit /etc/nginx/sites-available/default
/etc/nginx/sites-available/default
#~ ~ ~
index index.php index.html index.htm index.nginx-debian.html;
#~ ~ ~
location ~ \.php$ {
include snippets/fastcgi-php.conf;
fastcgi_pass unix:/run/php/php8.2-fpm.sock;
# Basic Authentication
auth_basic "private area";
auth_basic_user_file /var/www/html/<path>/.htpasswd;
}
#~ ~ ~
リロード
bash
$ sudo nginx -s reload
dotnet アプリのデプロイ
発行
- パブリッシュを実施すると、構成
Release
が使われます。 - Blazer Web App / Serverは、
<project>\bin\Release\net<version>\pulish\
に出力されます。 - Blazor WebAssemblyは、
<project>\bin\Release\net<version>\browser-wasm\publish\
に出力されます。- 静的サイトの場合は、
<project>\bin\Release\net<version>\wwwroot\
を使います。
- 静的サイトの場合は、
展開
- パブリッシュしたフォルダをサーバに転送します。
- アプリをオペレータのアカウントで実行する前提で、プロジェクトディレクトリをオペレータのホーム(
/home/<user>/<project>
)に配置します。
- アプリをオペレータのアカウントで実行する前提で、プロジェクトディレクトリをオペレータのホーム(
コマンドプロンプト
>scp -i "%USERPROFILE%\.ssh\<keyfile>" -r "<project>\bin\Release\net<version>\pulish\*" <user>@<address>:"~/<project>"
- フォルダには一部に含まれないデータがあります。
- 例えば、sqlite
.db
は含まれないので、appsettings.json
に"Data Source=Data\\project.db"
と設定されている場合、Data\project.db
をコピーして、プロジェクトディレクトリに'Data\project.db'
というファイル名で配置することになります。- Windows 11 の
scp
は、quoteしても、リモートパス中の\
を/
に置換してしまうようなので、いったんproject.db
を転送してからリモートでリネームするのが無難です。
- Windows 11 の
- ちなみに、
project.db
ファイルが無いことで、なぜか、wwwroot/css/site.css
が404エラーになったりします。
- 例えば、sqlite
手動起動
bash
$ cd <projectdir>
$ dotnet <project>.dll
- カレントディレクトリで起動する必要があることに留意してください。
- サービスとして登録済みの場合は、あらかじめ、サービスの停止・抹消が必要です。
ポート指定
bash
$ cd <projectdir>
$ dotnet <project>.dll --urls http://+:<port>
自動起動 (アプリのサービス化)
サービスを構成
bash
$ sudoedit /lib/systemd/system/<project>.service
/lib/systemd/system/<project>.service
[Unit]
Description=<title> .NET Web API App running on Debian
[Service]
WorkingDirectory=/home/<user>/<projectdir>
ExecStart=/bin/dotnet <project>.dll
Type=simple
Restart=always
# Restart service after 10 seconds if the dotnet service crashes:
RestartSec=10
KillSignal=SIGINT
SyslogIdentifier=<project>
User=<user>
Environment=ASPNETCORE_HTTP_PORTS=<port>;<port...>
Environment=ASPNETCORE_ENVIRONMENT=Production
Environment=DOTNET_PRINT_TELEMETRY_MESSAGE=false
[Install]
WantedBy=multi-user.target
-
ExecStart
の.dll
ファイル名はパスを指定しません。- パスは上の
WorkingDirectory
に書きます。 - アプリは、カレントディレクトリで起動する必要があります。
- パスは上の
-
Environment=<key>='<value>'
で、環境変数を定義します。 - 再起動するように構成されているので、構成に誤りがあると無限に再起動し続けます。
- 再編集の際は、あらかじめ、サービスの停止・抹消が必要です。
- あるいは、
sudo systemctl daemon-reload
で再読み込みします。
- あるいは、
- 下記ドキュメントでは、
User=www-data
を設定しています。- 設定を「プロジェクトディレクトリ全体を所有するユーザ」に変更するか、「プロジェクトディレクトリ全体の所有者とグループ」を
www-data
にする必要があります。 - 設定をコメントアウトすると
root
が指定されたことになり、プロジェクトディレクトリの所有者を気にしなくて良くなります。(セキュリティ的な問題は不詳) - なお、
nginx
はwww-data
のプロセスです。
- 設定を「プロジェクトディレクトリ全体を所有するユーザ」に変更するか、「プロジェクトディレクトリ全体の所有者とグループ」を
サービスの操作
プロセスの確認
bash
$ ps auxf
サービスを登録
bash
$ sudo systemctl enable <project>
サービスを開始
bash
$ sudo systemctl start <project>
サービスを停止
bash
$ sudo systemctl stop <project>
サービスの状態を確認
- 直近のログが一部表示されます。
bash
$ sudo systemctl status <project>
起動からの全てのログを表示
bash
$ journalctl -b
サービスを抹消
bash
$ sudo systemctl disable <project>
サービスを確認
bash
$ systemctl list-units *.service
複数アプリの構成
アプリの識別手法
- ひとつのサイトで、複数の独立したアプリをホストする場合の手法を模索します。
- 基本的には、nginxで仮想サイトを作り、アプリ(サービス)ごとに待ち受けポートを別にして、リバースプロキシで振り分けます。
サブディレクトリ
- サービス名を使って、仮想サイトにサブディレクトリを割り当てます。
- アプリに関連付けた名前が使えるので利用の際に分かり易いです。
-
App.razor
の<base href="/<directory>" />
を設定することで、https://<domain>/<directory>/
に配置できます。- ビルドの時点で識別子を確定させる必要があるので、変更時には再ビルドが必要です。
- ただし、これだけでは、一部のファイルを
https://<domain>/
から取得しようとするため、課題が生じます。 - なお、
base
はルートのままで、仮想サイトのURLをrewrite
する方法も考えられますが、サイト内のリンクにサブディレクトリが付かないため破綻します。
ポート
- アプリ毎に仮想サイトとサービスのポートを用意し、仮想サイトを
https://<domain>:<port>/
に配置して、サービスのポートに転送します。 - サービスのユニットファイル(
systemd
)毎に、環境変数を使ってポートを指定します。- サービス開始の時点まで番号を不確定にでき、変更が容易です。
- この方法であれば、コンテンツがアプリ毎に完全に分離できます。
- アドレスバーの見た目が悪いです。
- 「番号」では、アプリと結びつけづらいです。
- ブックマーク(ショートカット)を作ることで緩和できます。
- 当プロジェクトでは、現時点ではこちらを採用しています。
バックアップ
timeshift
導入
bash
$ sudo apt install timeshift
ディスクの空きをチェック
bash
$ df -h
Filesystem Size Used Avail Use% Mounted on
tmpfs 1.6G 1.7M 1.6G 1% /run
/dev/sdc2 146G 11G 128G 8% /
tmpfs 7.8G 8.0K 7.8G 1% /dev/shm
tmpfs 5.0M 4.0K 5.0M 1% /run/lock
/dev/sdb1 229G 19M 217G 1% /mnt/hdd1
tmpfs 1.6G 44K 1.6G 1% /run/user/1000
tmpfs 1.6G 56K 1.6G 1% /run/user/131
構成の確認 (初回実行)
bash
$ sudo timeshift --check
First run mode (config file not found)
Selected default snapshot type: RSYNC
Mounted '/dev/sdb1' at '/run/timeshift/backup'
Selected default snapshot device: /dev/sdb1
Scheduled snapshots are disabled - Nothing to do!
------------------------------------------------------------------------------
- 条件が成立したファイルがあると削除されます。
-
/dev/sdb1
(/dev/sdb1
)が自動的にバックアップ先として登録されました。
自動生成された構成
bash
$ cat /etc/timeshift/timeshift.json
/etc/timeshift/timeshift.json
{
"backup_device_uuid" : "",
"parent_device_uuid" : "",
"do_first_run" : "true",
"btrfs_mode" : "false",
"include_btrfs_home" : "false",
"stop_cron_emails" : "true",
"schedule_monthly" : "false",
"schedule_weekly" : "false",
"schedule_daily" : "false",
"schedule_hourly" : "false",
"schedule_boot" : "false",
"count_monthly" : "2",
"count_weekly" : "3",
"count_daily" : "5",
"count_hourly" : "6",
"count_boot" : "5",
"snapshot_size" : "0",
"snapshot_count" : "0",
"exclude" : [
],
"exclude-apps" : [
]
}
最初のスナップショット
bash
$ sudo timeshift --create --comments "First Snapshot" [--snapshot-device <path>]
- バックアップ先の指定を省略すると、
/run/timeshift/backup
になります。
自動更新された構成
/etc/timeshift/timeshift.json
{
"backup_device_uuid" : "481df198-449c-438a-b0a9-ef94d22efc55",
"parent_device_uuid" : "",
"do_first_run" : "false",
"btrfs_mode" : "false",
"include_btrfs_home_for_backup" : "false",
"include_btrfs_home_for_restore" : "false",
"stop_cron_emails" : "true",
"btrfs_use_qgroup" : "true",
"schedule_monthly" : "false",
"schedule_weekly" : "false",
"schedule_daily" : "false",
"schedule_hourly" : "false",
"schedule_boot" : "false",
"count_monthly" : "2",
"count_weekly" : "3",
"count_daily" : "5",
"count_hourly" : "6",
"count_boot" : "5",
"snapshot_size" : "12443299415",
"snapshot_count" : "178824",
"date_format" : "%Y-%m-%d %H:%M:%S",
"exclude" : [
"/root/**",
"/home/<user>/**"
],
"exclude-apps" : []
}
スケジュールを有効化
bash
$ sudoedit /etc/timeshift/timeshift.json
/etc/timeshift/timeshift.json
{
//~ ~ ~
"schedule_monthly" : "true",
"schedule_weekly" : "true",
"schedule_daily" : "true",
"schedule_hourly" : "true",
"schedule_boot" : "true",
//~ ~ ~
}
構成確認
bash
$ sudo timeshift --check
/dev/sdb1 is mounted at: /run/timeshift/backup, options: rw,relatime
Boot snapshots are enabled
Last boot snapshot not found
Tagged snapshot '2023-11-02_18-26-03': boot
Hourly snapshots are enabled
Last hourly snapshot not found
Tagged snapshot '2023-11-02_18-26-03': hourly
Daily snapshots are enabled
Last daily snapshot not found
Tagged snapshot '2023-11-02_18-26-03': daily
Weekly snapshots are enabled
Last weekly snapshot not found
Tagged snapshot '2023-11-02_18-26-03': weekly
Monthly snapshot are enabled
Last monthly snapshot not found
Tagged snapshot '2023-11-02_18-26-03': monthly
------------------------------------------------------------------------------
Added cron task: /etc/cron.d/timeshift-hourly
Added cron task: /etc/cron.d/timeshift-boot
- 条件が成立したファイルがあると削除されます。
-
cron
でスケジュールされています。
スナップショット確認
bash
$ sudo timeshift --list
[sudo] password for <user>:
/dev/sdb1 is mounted at: /run/timeshift/backup, options: rw,relatime
Device : /dev/sdb1
UUID : 481df198-449c-438a-b0a9-ef94d22efc55
Path : /run/timeshift/backup
Mode : RSYNC
Status : OK
3 snapshots, 236.5 GB free
Num Name Tags Description
------------------------------------------------------------------------------
0 > 2023-11-02_18-26-03 O B H D W M First Snapshot
1 > 2023-11-02_19-00-01 B
2 > 2023-11-02_19-01-04 B
mariabackup
導入
bash
$ sudo apt install mariadb-backup
基本のバックアップ
フルバックアップ
bash
$ sudo mariabackup --backup --target-dir=<full backup directory> --user=root [--password=<password>]
増分バックアップ
bash
$ sudo mariabackup --backup --incremental-basedir=<prev backup directory> --target-dir=<new backup directory> --user=root [--password=<password>]
スケジュールの構成例
- 毎時フルバックアップを行い、時間毎に24時間、日毎に7日、月毎に365日分を残します。
機構
- 月毎、日毎、時間毎のディレクトリを用意します。
- 時間毎にバックアップを作成します。
- バックアップは、いずれかひとつのディレクトリへ格納されます。
- 毎日0時のバックアップは、日毎に格納されるので、他には存在しません。
- 毎月1日0時のバックアップは、付き毎に格納されるので、他には存在しません。
- ディレクトリ毎に期限切れのバックアップを削除します。
シェルスクリプト
bash
$ sudo mkdir /etc/mariabackup
$ sudoedit /etc/mariabackup/backup.sh
/etc/mariabackup/backup.sh
#!/bin/bash
MINUTES_TO_KEEP=60
HOURS_TO_KEEP=24
DAYS_TO_KEEP=7
MONTHS_TO_KEEP=12
BACKUP_DIR="/mnt/hdd1/mariabackup"
LOG_FILE="$BACKUP_DIR/backup.log"
LOG_LINES=100
# Logging function
log () {
echo "$LOGGED_DATETIME $FREQUENCY $1" >> "$LOG_FILE"
mv "$LOG_FILE" "$LOG_FILE.tmp"
tail -n $LOG_LINES "$LOG_FILE.tmp" > "$LOG_FILE"
rm "$LOG_FILE.tmp"
}
# Removing founds function
remove () {
find "$BACKUP_DIR/$1" -mindepth 1 -maxdepth 1 $2 $3 -exec rm -rf "{}" \;
}
# Checking directory function
notanydir () {
for dir in $@; do if [[ -d "${dir}" ]]; then return 1; else return 0; fi; done
}
# Run every hour
LOGGED_DATETIME=$(date +"%Y-%m-%d %H:%M:%S")
TARGET_DIR=$(date +%Y%m%d%H%M%S -d "$LOGGED_DATETIME")
# Determine frequency
if notanydir "$BACKUP_DIR/monthly/${TARGET_DIR:0:6}*"; then
FREQUENCY="monthly"
elif notanydir "$BACKUP_DIR/daily/${TARGET_DIR:0:8}*"; then
FREQUENCY="daily"
elif notanydir "$BACKUP_DIR/hourly/${TARGET_DIR:0:10}*"; then
FREQUENCY="hourly"
else
FREQUENCY="temporary"
fi
# Backup
if mariabackup --backup --user=root --target-dir="$BACKUP_DIR/$FREQUENCY/$TARGET_DIR"; then
# Remove expired
remove temporary -cmin +$MINUTES_TO_KEEP
remove hourly -cmin +$(($HOURS_TO_KEEP * 60 - 30))
remove daily -ctime +$DAYS_TO_KEEP
remove monthly -ctime +$(($MONTHS_TO_KEEP * 31 - 15))
log "success"
else
log "error"
fi
bash
$ chmod 700 /etc/mariabackup/backup.sh
$ sudo mkdir -p /mnt/hdd1/mariabackup/temporary
$ sudo mkdir /mnt/hdd1/mariabackup/hourly
$ sudo mkdir /mnt/hdd1/mariabackup/daily
$ sudo mkdir /mnt/hdd1/mariabackup/monthly
スケジューリング
bash
$ sudo crontab -e
crontab
#~ ~ ~
# m h dom mon dow command
0 * * * * /etc/mariabackup/backup.sh
運用
ログの確認
bash
$ cat /mnt/hdd1/mariabackup/backup.log
バックアップの確認
bash
$ tree -L 2 /mnt/hdd1/mariabackup/
臨時バックアップ
bash
$ sudo /etc/mariabackup/backup.sh
おわりに
お読みいただきありがとうございました。
未完成の記事ですが、細かなtypoから根本に関わる思い違いまで、あるいは説明不足や蛇足など、お気づきの点があればコメントしていただけると助かります。
よろしくお願いいたします。
Discussion