👨‍👦

オリジナルのスマートスピーカーを作ってみる 7 デプロイ自動化

2024/04/11に公開

この記事は?

この記事は オリジナルのスマートスピーカーを作ってみる シリーズの続き「その7」です。

デプロイを自動化する

まだデプロイを自動化していなかったので、します。
シンプルにスクリプト (powershell) を作成しようと思います。

スクリプトの概要

  1. アプリケーションのビルド
  2. リモートのアプリケーションサーバーのアプリを停止
  3. リモートのアプリケーションサーバーへビルド成果物をアップロード
  4. リモートのアプリケーションサーバーのアプリを起動

諸環境

スクリプト実行マシン 兼 ビルドマシン

OS Windows 11 (x64)

アプリケーション情報

アプリケーションプラットフォーム .NET 8
言語 C# 12
IDE Visual Studio Community 2022
プロジェクト WorkerService

アプリケーションサーバー

製品名 Raspberry Pi 5
OS Raspberry Pi OS (ベースは Debian GNU/Linux 12 (bookworm))
CPU アーキテクチャ aarch64/arm64
メモリ 8GB
  • アプリケーションサーバーへの接続方法
    • SSH プロトコルを利用
    • 認証方法は公開鍵認証方式
    • SSH クライアントソフトは OpenSSH_for_Windows
    • リモートマシン上でコマンド実行は以下の方法を利用
      • $ ssh remote_user@remote_ip "実行したいコマンド"
      • リモートマシンのデフォルトシェルは bash です
  • ファイルのアップロード方法
    • SFTP プロトコルを利用
    • SFTP クライアントソフトは WinSCP
    • 認証方法
      • SFTP は SSH の拡張プロトコルなので設定は不要

デプロイのスクリプト (Powershell)

デプロイのスクリプト (Powershell) を以下に紹介します。

deployment.ps1
# ソリューションフォルダに移動後、以下のコマンドでこのスクリプトを実行できます。
# powershell -NoProfile -ExecutionPolicy Unrestricted .\MyProject\shellscripts\deployment.ps1

Write-Host "----- start deployment.ps1 -----";

# エラーが発生したときにスクリプトの実行を停止する
$ErrorActionPreference="Stop"

~~~各設定値の取得、変数宣言は省略~~~

#ビルドを実行
dotnet publish -c Release -r linux-arm64 --self-contained true;

#Serviceを停止する
Write-Host "●${myapp_systemmd_service_name}の停止を始めます";

$service_stop_result = ssh $server_user@$server_ip "sudo systemctl stop ${myapp_systemmd_service_name}";
if($service_stop_result -ne $null)
{
    Write-Host "service_stop_result: ${service_stop_result}";
}

#古い成果物の削除処理
Write-Host "●古い成果物の削除をはじめます";

#デプロイ先フォルダ内をファイルのリストを取得
$ls_result = ssh $server_user@$server_ip "ls ${publish_dir}";
$fileList = $ls_result -split " ";

#古いビルド成果物が存在する場合、古いファイルを削除
if($fileList -contains $myapp_service_dll)
{
    Write-Host "${myapp_service_dll}が存在します。log以外の削除を実行します。";

    $delete_all_without_log = "find ${publish_dir} -mindepth 1 -maxdepth 1 -type d ! -name `"log`" -exec rm -rf {} \; && find ${publish_dir} -mindepth 1 -maxdepth 1 -type f -delete";
	
    $delete_result = ssh $server_user@$server_ip "${delete_all_without_log}";

    if($delete_result -ne $null)
    {
        Write-Host "delete_result: ${delete_result}";
    }
}
else
{
    Write-Host "${myapp_service_dll}が存在しません。削除は実行しません。";
}

# WinSCPスクリプトを作成
$script = @"
# 接続情報を設定。秘密鍵を指定しています。
open sftp://$server_user@$server_ip -privatekey=$ppk_dir
# リモートディレクトリに移動
cd ${publish_dir}
# フォルダ内のすべてのファイルをアップロード
put $local_publish_dir\*
# appsettings.deploy.jsonをアップロードし、ファイル名を変更
put $deploy_app_serrings_file $app_settings_filename
# service_settings.deploy.json をアップロードし、ファイル名を変更
put $deploy_service_settings_file $service_settings_filename
# 接続を閉じる
close
exit
"@

# 一時的なWinSCPスクリプトファイルを作成
$tempFile = [System.IO.Path]::GetTempFileName()
$script | Out-File -FilePath $tempFile

# デプロイ
# WinSCP.comにスクリプトを渡し、デプロイを実行
Write-Host "WinSCP.com により成果物をデプロイしています...";

# 末尾に "> `$null" を追加すると、WinSCP.comの出力を非表示にできる
$cmd = "${winscpcom_file} -script=`"${tempFile}`" > `$null"
Invoke-Expression $cmd;

# 一時的なスクリプトファイルを削除
Remove-Item -Path $tempFile

# chomod の実行
Write-Host "●${myapp_service_exe_filename}の実行権限を付与します";

$chmod_result = $cmd = ssh $server_user@$server_ip "chmod +x ${publish_dir}/${myapp_exe_filename}";
if($chmod_result -ne $null)
{
    Write-Host "chmod_result: ${chmod_result}";
}

# systemd ユニットファイルのシンボリックリンクの作成
Write-Host "●${myapp_systemmd_service_name}のシンボリックリンクを作成します";

$symbol_result = $cmd = ssh $server_user@$server_ip "sudo ln -sf ${publish_dir}/${myapp_systemmd_service_name} ${systemd_unitfile_dir}";

if($symbol_result -ne $null)
{
    Write-Host "symbol_result: ${symbol_result}";
}

#デーモンのリロード
Write-Host "●systemctl daemon-reloadを実行します";
$daemon_reload_result = $cmd = ssh $server_user@$server_ip "sudo systemctl daemon-reload";
if($daemon_reload_result -ne $null)
{
    Write-Host "daemon_reload_result: ${daemon_reload_result}";
}

#サービスを起動する
Write-Host "●${myapp_systemmd_service_name}の起動を始めます";
$service_start_result = $cmd = ssh $server_user@$server_ip "sudo systemctl start ${myapp_systemmd_service_name}";
if($service_start_result -ne $null)
{
    Write-Host "service_start_result: ${service_start_result}";
}

Write-Host "Now listening on: ${web_app_url}";

#ブラウザでダッシュボードを開く
cd $browser_directory_path;
$cmd = "Start-Process .\chrome.exe `"${web_app_url}`"";
Invoke-Expression $cmd;

Write-Host "----- finish deployment.ps1 -----";

Discussion