Windows から Blazor Web App をデプロイする Debian Server の構成と管理
はじめに
これは、本論の課題を解決した際のメモ書きで、未来の自分に宛てたものです。
未完成のもので、随時更新される可能性があります。
概要
- Windowsで開発し、Debianでデプロイします。
- いったん、Ubuntu Serverで実施したのですが、後に、Debianでやり直しました。
- 特に断りがなければ、Debianについて記載しています。
- 経緯から、Ubuntuについては網羅されていません。
前提
- Windows 11 Pro 22H2
- Debian 12.6
- ASP.NET Core 8.0.8
- nginx 1.22.1
- MariaDB 10.11.6
- 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のバージョン確認
$ cat /etc/os-release
$ cat /etc/debian_version
sudo の準備
-
root
でログインするか、あるいは、su
コマンドで昇格します。
導入
# apt install sudo
グループへの参加
# adduser <user> sudo
または、
# gpasswd -a <user> sudo
-
user
アカウントでsudo
が使えるようになったので、そちらでログインし直します。-
su
コマンドを使った場合は、単にexit
します。
-
ssh 公開鍵認証
認証キー生成
>ssh-keygen -t ed25519 -f "%USERPROFILE%\.ssh\<keyfile>"
パスフレーズを入力 ⇒ 指定の秘密鍵ファイルに加えて公開鍵ファイル(.pub
)が生成
現在の認証キーを確認
$ cat ~/.ssh -l
- 初期状態でディレクトリが存在しません。
認証キー置き換え
- USBメモリから公開鍵を取り出してコピーします。
$ cp /media/<keyfile>.pub ~/.ssh/authorized_keys
$ chmod 600 ~/.ssh/authorized_keys
公開鍵認証の強制 (パスワード認証の禁止)
$ sudoedit /etc/ssh/sshd_config
- #PubkeyAuthentication yes
+ PubkeyAuthentication yes
- #PasswordAuthentication yes
+ PasswordAuthentication no
設定を反映
$ sudo systemctl restart ssh
USBメモリからの取り出し
usbデバイスを一覧
$ lsusb
- デバイスの認識状態を確認できます。
ブロックデバイスを一覧
$ lsblk
SCSIデバイスを一覧
$ 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>
$ sudo mount /dev/sdd1 /media
アンマウント
sudo umount <mountpoint>
$ sudo umount /media
IP/MACアドレス確認
$ ip address show
$ ip address
$ ip a
※いずれも同等
DHCPによる静的IP割り当て
- 別途、DHCPサーバー側の設定が必要です。
設定追記
$ sudoedit /etc/dhcp/dhclient.conf
#~ ~ ~
send dhcp-client-identifier = hardware;
#~ ~ ~
ubuntsu
より大きな番号の~.yaml
を追加することで上書き
$ sudoedit /etc/netplan/01-dhcp-identifier-mac.yaml
# This is the network config by user
network:
ethernets:
enp2s0:
dhcp4: true
dhcp-identifier: mac
version: 2
追加ファイルのパーミッション設定
$ sudo chmod 600 /etc/netplan/01-dhcp-identifier-mac.yaml
設定反映
$ sudo rm /var/lib/dhcp/*
$ sudo systemctl restart networking
ubuntsu
$ 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)
$ sudoedit <targetfile>
- デフォルトでは
nano
が起動します。-
sudo update-alternatives --config editor
で、設定を選択できます。 -
sudo update-alternatives --display editor
で、現在の設定を確認できます。
-
- セキュリティ上は、
sudoedit
が望ましいようです。-
sudoedit
では、- ルート権限で作られた一時ファイルをユーザ権限で編集し、編集後にルート権限で更新されます。
- Ubuntuでは、生成される一時ファイルに拡張子が付かないためか、エディタの設定にかかわらず、シンタックスハイライトが働きませんでした。(Debianでは問題ありません。)
-
sudo nano
とすると、- 不必要に権限が付与されます。
- ユーザの設定でなくルートの設定が使われます。
-
使用するエディタの変更
$ sudo update-alternatives --config editor
nano の設定
$ sudoedit /usr/share/nano/default.nanorc
$ nano ~/.nanorc
# private settings
set mouse
set nowrap
set linenumbers
set autoindent
set matchbrackets "(<[{)>]}"
set tabsize 2
キー操作
Alt
+ M
: マウスモード切替
Alt
+ N
: 行番号切替
Alt
+ I
: 自動インデント切替
vim の設定
"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)
基本的な操作
操作 | 機能 | 備考 |
---|---|---|
← ‖ Ctrl + b | 左の文字 | |
→ ‖ Ctrl + f | 右の文字 | |
Ctrl + ← ‖ Alt + b | 左の単語 | |
Ctrl + → ‖ Alt + f | 右の単語 | |
Home ‖ Ctrl + a | 行頭 | |
End ‖ Ctrl + e | 行末 | |
BackSpace ‖ Ctrl + h | 左の文字を削除 | |
Delete ‖ Ctrl + d | 右の文字を削除 | |
Ctrl + BackSpace | 左の単語を削除 | |
Ctrl + Delete | 右の単語を削除 | |
Ctrl + u | 行頭まで削除 | |
Ctrl + k | 行末まで削除 | |
Ctrl + _ | Undo | |
Tab ‖ Ctrl + i | 自動補完 / 入力候補 | |
↑ ‖ PageUp | 前の履歴 | |
↓ ‖ PageDown | 後の履歴 | |
Esc , < | 最古の履歴 | |
Esc , > | 最新の履歴 | |
Ctrl + r | 前の履歴の検索 | |
Ctrl + s | 後の履歴の検索 | ※ |
Ctrl + g | 履歴検索の取消 |
※Ctrl+S
でスクリーンがロック(stop
)される設定では使えません。ロックの解除(start
)はCtrl+Q
です。
その他の操作
- 以下で確認できます。
$ bind -p
ジョブ管理
-
Ctrl+Z
で中断したジョブを制御します。
ジョブ一覧
$ jobs
フォアグラウンドへ
$ fg <job_spec>
バックグラウンドへ
$ bg <job_spec>
強制終了
$ kill -s SIGKILL %<job_spec>
設定
$ nano ~/.bashrc # or ~/.bash_profile
- 自分用の設定です。
- 共通設定は
/etc/profile
にあります。
スクリーンロック設定の切替
- ロックありに設定されます。
$ stty -ixoff
- あり/なしが切り替わります。
$ stty -ixon
- 設定を確認します。
$ stty -a
環境変数の永続的設定
# ~ ~ ~
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>
システム操作
シャットダウン
$ sudo shutdown now
再起動
$ sudo shutdown -r now
自分だけなら
$ sudo reboot
追加HDDをマウントする
ドライブを一覧
$ lsblk
$ fdisk -l
マウント状況を一覧
$ mount -v
パーティションのUUIDを得る
sudo blkid <device>
$ sudo blkid /dev/sda1
起動時マウント設定
$ sudoedit /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
ハードウェア全般情報
$ sudo dmidecode
項目一覧・選択
$ sudo dmidecode -t [type]
lspci
PCIデバイス情報
$ lspci
最大限詳細
$ sudo lspci -vv
スリープ抑止
$ sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target
ファイアウォール
nftables
- 初期状態で、導入済み、かつ、不活性でした。
- sshで使用中に操作を失敗すると、切断されて再接続できなくなる可能性があるので、慎重に行う必要があります。
設定
$ sudoedit /etc/nftables.conf
初期状態でファイルは存在しません。
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority filter; policy drop;
# established/related connections
ct state established,related accept
# early drop of invalid connections
ct state invalid drop
# loopback interface
iif lo accept
# ICMP
icmpv6 type { echo-request, echo-reply, nd-neighbor-solicit, nd-router-advert, nd-neighbor-advert } accept
icmp type { echo-request, echo-reply } accept
# SSH (LANからの接続だけ 'ip/subnetmask')
ip saddr <local_network> tcp dport ssh accept
# HTTP/HTTPS
tcp dport { http, https } accept
# Blazor Web Apps
tcp dport { <port>, ... } accept
}
chain forward {
type filter hook forward priority filter; policy drop;
}
chain output {
type filter hook output priority filter; policy accept;
}
}
設定の反映
$ sudo nft -f /etc/nftables.conf
活殺制御
有効化
$ sudo systemctl enable nftables
無効化
$ sudo systemctl disable nftables
状態確認
$ sudo systemctl status nftables
パッケージ管理
一覧の更新
$ sudo apt update
導入済み一覧
$ apt list --installed
更新可能一覧
$ apt list --upgradable
導入済みパッケージの更新
更新後に不要になったパッケージを除去
$ sudo apt full-upgrade
除去しない
$ sudo apt upgrade
不要になったパッケージの除去
$ sudo apt autoremove
パッケージの探索
完全一致
$ apt list <package>
部分一致
$ apt search <package>
導入
$ sudo apt install <package>
除去
依存先を含める
$ sudo apt --purge remove <package>
含めない
$ sudo apt remove <package>
自動セキュリティ更新
.NET Core Runtime
Microsoft パッケージ署名キーを追加
$ 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
導入
$ sudo apt install aspnetcore-runtime-8.0
バージョン確認
$ dotnet --info
nginx
導入
$ sudo apt install nginx
apache 排除
$ sudo apt --purge remove apache2
バージョン確認
$ /usr/sbin/nginx -v
設定ディレクトリ確認
$ 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
設定確認
$ sudoedit /etc/nginx/sites-available/default
サーバ名を設定
#~ ~ ~
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name <servername>.<domainname>;
#~ ~ ~
root /var/www/html;
#~ ~ ~
公開ディレクトリ確認
$ ls /var/www/html
index.nginx-debian.html
Basic認証
導入
$ sudo apt install apache2-utils
パスワード生成
# use '-c' to create it
$ sudo htpasswd -c /var/www/<path>/.htpasswd <user>
パスワード使用
location <location> {
#~ ~ ~
# Basic Authentication
auth_basic "message";
auth_basic_user_file /var/www/<path>/.htpasswd;
}
SSL を設定
DNS とファイアウォールを設定
- DNSとファイアウォールを設定し、サーバにアクセスできるようにします。
- インターネットからサーバへ
http
でアクセスできることを確認します。
証明書の取得と設定、更新
certbot 導入
$ sudo apt install certbot
$ sudo apt install python3-certbot-nginx
証明書の取得
$ 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.
- エラーする場合は、レポートの指示に従って見直せば、大抵、なんとかなります。
証明書を設定
- 取得した証明書と鍵を設定します。
#~ ~ ~
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
の行をコメントアウトすることでキャンセルできます。
-
証明書の更新
- 完了メッセージに示されていたように、既に自動更新が設定されています。
$ cat /etc/cron.d/certbot
- 以下で自動更新をテストできます。
$ sudo certbot renew --dry-run
-
Timeout during connect (likely firewall problem)
と出て失敗するようなら、書かれている通りIPフィルタリングを見直しましょう。- Let's Encryptのアドレスは非公開で、かつ、変化する可能性があるため、相手先を制限せずに80番ポート(In)を開けておく必要があります。
- 自身のファイアウォールだけでなく、ルータの確認もお忘れなく。
- なお、自動更新に失敗していると、期限切れ前に、
Let's Encrypt Expiry Bot <expiry@letsencrypt.org>
からLet's Encrypt certificate expiration notice
というメールが送られてきます。
証明書の確認
- 取得されている証明書の状態を確認します。
$ sudo certbot certificates
開発用自己署名証明書
鍵作成
$ sudo openssl genrsa -out <privatekey>.pem 2048
署名要求作成
$ sudo openssl req -new -key <privatekey>.pem -out <signingrequest>.pem
証明書作成
$ sudo openssl x509 -days 365 -req -key <privatekey>.pem -in <signingrequest>.pem -out <certificate>.pem
リバースプロキシーを構成
- Blazor Web Appに独立したサーバ名を与えておき、そこへのアクセスをアプリのポートに振り向けます。
設定ファイルを作成
$ sudoedit /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
とします。
- 例えば、
-
有効化 (シンボリックリンクを配置)
$ sudo ln -s /etc/nginx/sites-available/<project>.conf /etc/nginx/sites-enabled/
無効化 (シンボリックリンクを削除)
$ sudo rm /etc/nginx/sites-enabled/<project>.conf
構成のテスト
$ sudo nginx -t
再読み込み
$ sudo nginx -s reload
状態の確認
$ sudo systemctl status nginx
ログの確認
$ ls -l /var/log/nginx/
$ sudo cat /var/log/nginx/access.log
$ sudo cat /var/log/nginx/error.log
再起動
$ sudo systemctl restart nginx
特定サイトのキャッシュをクリア (Google Chrome)
- 開発ツール(F12)を開いた状態で、リロードボタンを長押しして、出てきたメニューから「キャッシュの消去」を選ぶと、確認なしで消えます。
MariaDB
導入
$ sudo apt install mariadb-server
初期設定
$ sudo mysql_secure_installation
-
unix_socket
にすることで、DBのrootパスワードを不要にできます。(OSのrootアカウントが使うと認証される) - 間違えた場合はやり直すことができますが、状況によって質問が変化するので、注意深く読んで答える必要があります。
稼働状況確認
$ 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
#~ ~ ~
バージョン確認
$ mariadb --version
アカウントと権限
$ sudo mariadb
アカウント一覧
MariaDB [(none)]> select Host,User from mysql.user;
新規作成
MariaDB [(none)]> create user <username>@<hostname> identified by '<password>';
削除
MariaDB [(none)]> drop user <username>@<hostname>;
パスワード変更
MariaDB [(none)]> set password for <username>@<hostname> = '<password>'
権限確認
MariaDB [(none)]> show grants for <username>@<hostname>;
権限付与
MariaDB [(none)]> grant <privileges> on <database>.<table> to <username>@<hostname>;
privileges
all previleges
== create,drop,delete,sinsert,select,update,grant option
database.table
*.*
all tables of all database
<database>.*
all tables of the database
剥奪
MariaDB [(none)]> revoke <privileges> on <database>.<table> from <username>@<hostname>;
権限反映
MariaDB [(none)]> flush privileges;
終了
MariaDB [(none)]> exit;
その他確認など
データベース一覧
MariaDB [(none)]> show databases;
データベース切り替え
MariaDB [(none)]> use <database>;
テーブル一覧
MariaDB [(database)]> show tables;
テーブルスキーマ
MariaDB [(database)]> show create table <table>;
カラム一覧
MariaDB [(database)]> show columns from <table>;
プロセス一覧
MariaDB [(none)]> show processlist;
-
kill Id;
できます。
変数一覧
MariaDB [(database)]> show valiables;
設定
MariaDB [(database)]> set <valiablename>=<value>;
例
MariaDB [(none)]> show variables like 'collation%';
+----------------------+--------------------+
| Variable_name | Value |
+----------------------+--------------------+
| collation_connection | utf8mb3_general_ci |
| collation_database | utf8mb4_general_ci |
| collation_server | utf8mb4_general_ci |
+----------------------+--------------------+
3 rows in set (0.002 sec)
MariaDB [(none)]> set collation_server=utf8mb4_bin;
Query OK, 0 rows affected (0.000 sec)
MariaDB [(none)]> set collation_database=utf8mb4_bin;
Query OK, 0 rows affected (0.000 sec)
MariaDB [(none)]> show variables like 'collation%';
+----------------------+--------------------+
| Variable_name | Value |
+----------------------+--------------------+
| collation_connection | utf8mb3_general_ci |
| collation_database | utf8mb4_bin |
| collation_server | utf8mb4_bin |
+----------------------+--------------------+
3 rows in set (0.002 sec)
- この変更はセッション内でのみ有効です。
-
set global ~
(またはset @@global.~
)とすれば、大域化できます。
-
- 恒久化する場合は、例えば以下のようにします。
[client]
default-character-set = 'utf8mb4'
[mysqld]
character-set-server = 'utf8mb4'
collation-server = 'utf8mb4_bin'
$ sudo systemctl restart mariadb
$ sudo mariadb
MariaDB [(none)]> show variables like 'character%';
+--------------------------+----------------------------+
| Variable_name | Value |
+--------------------------+----------------------------+
| character_set_client | utf8mb4 |
| character_set_connection | utf8mb4 |
| character_set_database | utf8mb4 |
| character_set_filesystem | binary |
| character_set_results | utf8mb4 |
| character_set_server | utf8mb4 |
| character_set_system | utf8mb3 |
| character_sets_dir | /usr/share/mysql/charsets/ |
+--------------------------+----------------------------+
8 rows in set (0.001 sec)
MariaDB [(none)]> show variables like 'collation%';
+----------------------+--------------------+
| Variable_name | Value |
+----------------------+--------------------+
| collation_connection | utf8mb4_general_ci |
| collation_database | utf8mb4_bin |
| collation_server | utf8mb4_bin |
+----------------------+--------------------+
3 rows in set (0.001 sec)
- MySQLの場合は、
set @@persist.~
で恒久化できるみたいです。
ダンプ
スキーマのみ
$ sudo mariadb-dump --no-data <database> > <sqlfile>
データ込み (バイナリを含む)
$ sudo mariadb-dump --hex-blob <database> > <sqlfile>
復元 (SQLファイルの実行)
$ sudo mariadb <database> < <sqlfile>
-
ERROR: ASCII '\0' appeared in the statement, but this is not allowed unless option --binary-mode is enabled and mysql is run in non-interactive mode.
のようなエラーが出る場合は、読み込ませたファイルがUTF-8
であることを確認してください。- 一部に、
UTF-16
を出力するアプリがあります。
- 一部に、
- 同様に、改行文字が原因でエラーになる場合があります。
- 一部に、
CHAR(13)
だけを出力するアプリがあります。
- 一部に、
設定編集
$ sudoedit /etc/mysql/mariadb.conf.d/50-server.cnf
リモートアクセスを許可
# localhost which is more compatible and is not less secure.
- bind-address = 127.0.0.1
+ #bind-address = 127.0.0.1
- SSHトンネルを使えば、この許可は不要です。
管理クライアント
当初は、phpMyAdminを使いましたが、以下に切り替えました。
HeidiSQL
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
展開の自動化
- Visual Studioを前提に「発行」の処理を拡張します。
- CLIで入力できないので、前述の
scp
を使う方法だと、パスワードの手入力ができません。手入力するには、GUIアプリが必要になります。
- CLIで入力できないので、前述の
- WinSCPを起動して、「ログイン」ウィンドウの「新しいサイト」から、
scp
のセッションを登録します。- ディレクトリの設定を
/home/<user>/<project>
にしておきます。
- ディレクトリの設定を
- Visual Studioのプロジェクトファイル
<project>.csproj
に発行後ターゲットを追加します。- コマンド名を
WinSCP.exe
にすることでGUIで起動します。WinSCP
やWinSCP.com
だとコンソールモードで起動します。 - 別プロセスとして起動することで、転送の完了を待たずに発行が完了します。
- コマンド名を
<Target Name="CustomAfterPublish" AfterTargets="Publish">
<Exec Command="powershell -File $(ProjectDir)deploy.ps1" Condition="Exists('$(ProjectDir)deploy.ps1')" />
</Target>
$command = "$env:LOCALAPPDATA\Programs\WinSCP\WinSCP.exe"
$arguments = '<SessionName> /upload "<ProjectPath>\bin\Release\net8.0\publish"'
Start-Process -FilePath $command -ArgumentList $arguments
- 展開先ディレクトリが
/home/<user>/<project>/publish/
になるので、起動ディレクトリもそれに合わせます。
手動起動
$ cd <projectdir>
$ dotnet <project>.dll
- カレントディレクトリで起動する必要があることに留意してください。
- サービスとして登録済みの場合は、あらかじめ、サービスの停止・抹消が必要です。
ポート指定
$ cd <projectdir>
$ dotnet <project>.dll --urls http://+:<port>
自動起動 (アプリのサービス化)
サービスを構成
$ sudoedit /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
のプロセスです。
- 設定を「プロジェクトディレクトリ全体を所有するユーザ」に変更するか、「プロジェクトディレクトリ全体の所有者とグループ」を
サービスの操作
プロセスの確認
$ ps auxf
サービスを登録
$ sudo systemctl enable <project>
サービスを開始
$ sudo systemctl start <project>
サービスを停止
$ sudo systemctl stop <project>
サービスの状態を確認
- 直近のログが一部表示されます。
$ sudo systemctl status <project>
起動からの全てのログを表示
$ journalctl -b
サービスを抹消
$ sudo systemctl disable <project>
サービスを確認
$ systemctl list-units *.service
複数アプリの構成
バックアップ
timeshift
導入
$ sudo apt install timeshift
ディスクの空きをチェック
$ 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
構成の確認 (初回実行)
$ 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
)が自動的にバックアップ先として登録されました。
自動生成された構成
$ cat /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" : [
]
}
最初のスナップショット
$ sudo timeshift --create --comments "First Snapshot" [--snapshot-device <path>]
- バックアップ先の指定を省略すると、
/run/timeshift/backup
になります。
自動更新された構成
{
"backup_device_uuid" : "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
"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" : []
}
特定ユーザのホームフォルダをバックアップする
"exclude" : [
"/root/**",
- "/home/<user>/**"
+ "+ /home/<user>/**"
],
スケジュールを有効化
$ sudoedit /etc/timeshift/timeshift.json
- "schedule_monthly" : "false",
+ "schedule_monthly" : "true",
- "schedule_weekly" : "false",
+ "schedule_weekly" : "true",
- "schedule_daily" : "false",
+ "schedule_daily" : "true",
- "schedule_hourly" : "false",
+ "schedule_hourly" : "true",
- "schedule_boot" : "false",
+ "schedule_boot" : "true",
構成確認
$ 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
でスケジュールされています。
スナップショット確認
$ sudo timeshift --list
[sudo] password for <user>:
/dev/sdb1 is mounted at: /run/timeshift/backup, options: rw,relatime
Device : /dev/sdb1
UUID : xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
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
メールレポート
cron のメール送信を停止しない
timeshiftは、デフォルトでcronのメール送信を止めていました。
$ sudoedit /etc/timeshift/timeshift.json
- "stop_cron_emails" : "true",
+ "stop_cron_emails" : "false",
cron メール設定
この時点で、この宛先を指定する行は存在しませんでした。
$ sudoedit /etc/crontab
+ MAILTO=root
mariabackup
導入
$ sudo apt install mariadb-backup
基本のバックアップ
フルバックアップ
$ sudo mariabackup --backup --target-dir=<full backup directory> --user=root [--password=<password>]
増分バックアップ
$ sudo mariabackup --backup --incremental-basedir=<prev backup directory> --target-dir=<new backup directory> --user=root [--password=<password>]
スケジュールの構成例
- 毎時フルバックアップを行い、時間毎に24時間、日毎に7日、月毎に365日分を残します。
機構
- 月毎、日毎、時間毎のディレクトリを用意します。
- 時間毎にバックアップを作成します。
- バックアップは、いずれかひとつのディレクトリへ格納されます。
- 毎日0時のバックアップは、日毎に格納されるので、他には存在しません。
- 毎月1日0時のバックアップは、付き毎に格納されるので、他には存在しません。
- ディレクトリ毎に期限切れのバックアップを削除します。(世代管理)
シェルスクリプト
-
/mnt/hdd1/mariabackup
をバックアップ先とします。
$ sudo mkdir /etc/mariabackup
$ sudoedit /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
$ sudo 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
スケジューリング
$ sudo crontab -e
#~ ~ ~
# m h dom mon dow command
0 * * * * /etc/mariabackup/backup.sh
運用
ログの確認
$ cat /mnt/hdd1/mariabackup/backup.log
バックアップの確認
$ tree -L 2 /mnt/hdd1/mariabackup/
$ sudo mariabackup --prepare --target-dir /mnt/hdd1/mariabackup/<targetdatetime>
臨時バックアップ
$ sudo /etc/mariabackup/backup.sh
リストア
$ sudo systemctl stop mariadb
$ sudo mariabackup --copy-back --target-dir /mnt/hdd1/mariabackup/<targetdatetime>
$ sudo chown -R mysql:mysql /var/lib/mysql/
$ sudo systemctl start mariadb
関連記事
おわりに
お読みいただきありがとうございました。
未完成の記事ですが、細かなtypoから根本に関わる思い違いまで、あるいは説明不足や蛇足など、お気づきの点があればコメントしていただけると助かります。
よろしくお願いいたします。
Discussion