プログラマーのメモ書き

伊勢在住のプログラマーが気になることを気ままにメモったブログです

Windows 10 20H2 にアップデートしたら iSCSI ドライブのマウントが消えてしまったので修復した件

4月に入り、2020年度の仕事も一段落してきたので、次の仕事がスタートする前に手元の環境をいろいろと更新しようと思い立ちました。

で、その一環で WIndows 10 も 1909 だったので 20H2 にアップデートしました。

そうしたら、なんとまあ、iSCSI 領域を Windows 上の2か所でマウントしていたのですが、なぜか片一方だけ消えてしまいました。もっとも、 20H2 にアップデートしたタイミングで要らなくなったアプリなどもあれこれ削除していたので、Windows アップデートではなく、どれかのアンインストーラが悪さをした可能性もありますが、今となっては不明です。

一応、備忘録として、再マウントした手順を書いておきます。

再マウント

まず、 iSCSI イニシエータの設定が無事か確認します。

『スタート』->『Windows システムツール』->『コントロールパネル』->『管理ツール』->『iSCSIイニシエータ』を開くと、

f:id:junichim:20210428145629p:plain

のように問題なく接続されていました。ここは問題なさそうです。

次に、『管理ツール』に戻り、『コンピュータの管理』を開き、『ディスクの管理』を開きます。

f:id:junichim:20210428150117p:plain

ここの『ディスク1』というのが、iSCSI ドライブになります。既に、 D ドライブのドライブレターが割り当てられています。

次に、ボリューム(D:) を選択して、『ドライブ文字とパスの変更』を選択すると、

f:id:junichim:20210428150352p:plain

f:id:junichim:20210428144710p:plain

のようになっています。

最初に書いたように、もともとは、一つの同じ iSCSI 領域を2か所にマウントしており、この画面で2つのパスが存在していました。一連のアップデート作業で、ドライブではないパスのほうのマウントが消えてしまってます。

ということで、もう一度マウントしておきます。上記画面で『追加』を選択し、『次の空のNTFSフォルダにマウントする』を選び、

f:id:junichim:20210428144651p:plain

『参照』ボタンを使い、マウント先を指定し、

f:id:junichim:20210428144631p:plain

『OK』とします。

f:id:junichim:20210428144803p:plain

もう一度、『OK』を押し、エラー等がでなければ、マウントされます。

これで、無事に一つの iSCSI 領域を2か所にマウントできました。 無事に復旧しましたが、この手のトラブルは焦りますね。

参考

『ディスクの管理』で空のNTFSフォルダにマウントしたものは、ジャンクションとしてマウントされています。

パスに割り当てたフォルダを dir コマンドでみると、

C:\Users\mor>dir
 ドライブ C のボリューム ラベルがありません。
 ボリューム シリアル番号は 8824-7400 です

 C:\Users\mor のディレクトリ

2021/04/28  14:49    <DIR>          .
2021/04/28  14:49    <DIR>          ..
(中略)
2021/04/28  14:46    <JUNCTION>     home [\??\Volume{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}\]
(中略)
2021/04/28  14:49    <JUNCTION>     jmtest [d:\]
(後略)

となり、ジャンクションとして扱っているのがわかります。

なお、通常、ジャンクションを作成する場合に使う mklink /j コマンドで作成した場合(上記の jmtest )は、フォルダを指定する形になっているので、直接ボリュームにジャンクションを貼っているのがちょっと違うところですね。

EC2 の自動起動と自動停止

こちらの記事で書いたように、オフラインマップサーバーをEC2上に移しましたが、このサーバーは常時稼働する必要はありません。 EC2は動かしておくと、課金もされるので、必要な時だけ、自動的に起動して、自動的に停止させたいと思います。

ネットを調べると、いろいろな方法で実現できることがわかります。今回は楽をしたかったので、 CloudWatchEvent + AWS System Manager(旧称 SSM) による処理としました。

元ネタは下記になります。

IAM ロールの作成

IAM ロールを作成する際、最初にサービスとして、 System Manager を選択します。

f:id:junichim:20210419103437p:plain f:id:junichim:20210419103521p:plain

そのうえで、ポリシーとして SSMAutomationRole をアタッチします。

f:id:junichim:20210419103608p:plain

ロール名を指定して、ロールを作成します。

f:id:junichim:20210419103737p:plain

IAMロール作成後、信頼関係を表示して、

f:id:junichim:20210419103920p:plain

『信頼関係の編集』を行い、信頼されたエンティティとして、

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ssm.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

とあるのを、

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "events.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

のように変更します。

EC2 起動用の CloudWatchEvent ルールの作成

IAM ロールが作成できれば、次は CloudWatchEvent のルールを作成します。ルールは、 Cron 式で指定します。 『ターゲット』として、『SSM Automation』を選択し、ドキュメントから『AWS-StartEC2Instance』を選択します。

f:id:junichim:20210419104354p:plain

ドキュメントを選択すると、インスタンスID入力欄が出てくるので、対象となる EC2 インスタンスのインスタンスIDを入力します。

f:id:junichim:20210419104700p:plain

最後に、ロールとして、先ほど作ったロールを設定すればOKです。

f:id:junichim:20210419104903p:plain

EC2 停止用の CloudWatchEvent ルールの作成

EC2 停止用の CloudWatchEvent ルールも起動用と同様に作成します。停止用の場合は、ドキュメントして、『AWS-StopEC2Instance』を選択するだけです。

テスト

作成したルールの Cron 式で起動する時間を適当に調整してテストします。指定した時間になると、EC2インスタンスが起動していることが確認できればOKです。停止も同様に確認しておきます。正しく動作していれば、これでOKです。

もし、EC2が期待したように起動しない場合は、ルールそのものが呼び出されたのか、ルールは呼ばれたけどEC2の起動に失敗したか、などは、CloudWatch メトリクスを見ればわかります。

f:id:junichim:20210419105347p:plain

まとめ

SSMを使えば、EC2の起動停止の制御が非常に簡単にできることがわかりました。

これ、いままで動かしっぱなしだった他の EC2 でも応用できますね。うまくすればずいぶんと、お安く運用できそうです。

避難所検索@伊勢 v1.0 をリリースしました

以前ベータ版としてリリースしていた 避難所検索@伊勢 ですが、オフラインマップ回りの問題などが解決できたので、若干手を入れて 1.0 としてリリースしました。

play.google.com

v1.0 リリースに関する作業で、気に合ったところをメモしておきます。

β版からの修正点

機能的な変更はほとんどありません。主な修正点としては、

  • 経路検索時に現在位置が地図の中心に来るように変更
  • 各種ライブラリを更新
  • オフラインマップの更新確認をメニューに追加

というあたりになります。

ライブラリの更新について

今回、v1.0としてリリースするにあたり、各種ライブラリを更新しています。

Android support Library

ご存知の方も多いと思いますが、サポートライブラリはすでに非推奨になっていて、 AndroidX を使うことが推奨されています。

なので、この機に、AndroidXに移行しました。

AndroidX への移行  |  Android デベロッパー  |  Android Developers

移行作業はトラブルもなく、上記のURLの説明通りに AndroidStudio の機能を使って作業すれば、すぐに完了しました。

Graphhopper

こちらのオフラインマップサーバーの記事でも触れたように、オフラインマップ生成時に作成する graphhopper 用ファイル(経路データ)が v1.0 から新しいものになりました。

それに伴い、クライアント側の経路検索時の処理も若干変更になっています(詳しくは graphhopper のドキュメントなどを調べてみてください)。

最初にgrahhopperを初期化する部分ですが、このアプリの場合 com.mori_soft.escape.model.GraphHopperWrapper クラスにて、初期化していますが、

0.13.0 の時は

    private static GraphHopper prepareGraphHopper(Context context) {
        Log.d(TAG, "prepareGraphHopper");
        try {
            GraphHopper tmp = new GraphHopper().forMobile();
            tmp.load(getGraphHopperFolder(context));
            return tmp;
        } catch (Exception e) {
            Log.e(TAG, "Graphhopper file load failed" , e);
            // 読み込みに失敗した場合は、ファイルを消去する
            clearGhzFiles(context);
            return null;
        }
    }

としていたのを 1.0 では、

    // graphhopper ファイルを作成した際に config.yaml で指定した情報
    //   ファイルから読み込んだだけでは設定されない
    public static final String GH_PROFILE = "escape_by_foot";
    private static final String GH_PROFILE_VEHICLE = "foot";
    private static final String GH_PROFILE_WEIGHTING = "fastest";

    (略)

    private static GraphHopper prepareGraphHopper(Context context) {
        Log.d(TAG, "prepareGraphHopper");
        try {
            GraphHopper tmp = new GraphHopper().forMobile();
            tmp.setProfiles(new Profile(GH_PROFILE).setVehicle(GH_PROFILE_VEHICLE).setWeighting(GH_PROFILE_WEIGHTING));
            tmp.getCHPreparationHandler().setCHProfiles(new CHProfile(GH_PROFILE));
            tmp.load(getGraphHopperFolder(context));
            return tmp;
        } catch (Exception e) {
            Log.e(TAG, "Graphhopper file load failed" , e);
            // 読み込みに失敗した場合は、ファイルを消去する
            clearGhzFiles(context);
            return null;
        }
    }

のように、プロファイルを指定する必要があります。コメントにも書いてありますが、ここで指定する profile 名は graphhopper の経路データを作成する際に config.yaml に設定した profile 名になります。

また、経路検索時も 0.13.0 の時は

    private PathWrapper calcPath(double lat1, double lon1, double lat2, double lon2) {

        GHRequest req = new GHRequest(lat1, lon1, lat2, lon2).setAlgorithm(Parameters.Algorithms.DIJKSTRA_BI); // TODO アルゴリズム
        req.getHints().put(Parameters.Routing.INSTRUCTIONS, "false");

のように呼び出していたところが、

    private ResponsePath calcPath(double lat1, double lon1, double lat2, double lon2) {

        GHRequest req = new GHRequest(lat1, lon1, lat2, lon2).setProfile(GraphHopperWrapper.GH_PROFILE);
        req.getHints().putObject(Parameters.Routing.INSTRUCTIONS, "false");

のように、profile 名を追加で指定する必要がありました。

これは勝手な推測なのですが、たぶん、経路データ作成時は、いくつかのパターンで経路データ(それらを区別するために profile 名を与えます)を作成し、初期化時および経路検索時に、どれを使うか指定するような使い方になったように思えます。

また、そのほかに、経路検索結果を扱っていた PathWrapper クラスの名前が、 ResponsePath に代わっていたりします。

そのあたりを修正することで v1.0 で動作するようになりました。

Play App Signing

ライブラリではありませんが、今回のリリースに合わせて、 Play App Signing を使うようにしました。

ポイントになるのは、今までは単にアプリに署名して Google Play にアップロードしていたものが、Play App Signing では、

  • アプリを公開する際の署名鍵(アプリ署名鍵)
  • アプリを Google Play にアップロードする際に署名する鍵(アップロード鍵)

の2つの鍵が必要になるという点です。

で、既存アプリの場合は、開発者の手元にあるキーストアを使って署名して公開している(はずな)ので、これを Play App Signing のアプリ署名鍵として登録してやる必要がある、というのがポイントになります(アプリ公開時の鍵は変更できませんからね)。

そのうえで、別途、アップロード鍵を用意して、それで署名したアプリを Google Play にアップロードする、という形になります。なお、このアップロード鍵はアプリ署名鍵と同一でもよいらしいのですが、せっかく Play App Signing を使うなら分けたほうが無難かと思います。

Google への連絡が必要ですが、アップロード鍵は交換することも可能ですからね。

上記の点だけ理解しておけば、実際の作業は詳しいドキュメントも公開されているので、それに従って作業するだけです。

最後に

最初のβ版リリースからずいぶんと時間が経ちましたが、ある程度形になったかと思います。 まだ、避難所情報の更新がおこなえないので、その機能だけおいおい追加したいと思います。

その他、気になる点などありましたら、ご意見いただければと思います。