WinSW - 任意のプログラムをWindowsサービス化

# この記事の概要

独自でWindowsサービスとして登録する機能を持っていない、任意のコマンドをWindowsサービス化したい。

とくに [Quarkus版Keycloak](https://qiita.com/k2ok/items/c681f1ad25d75f6af4d5) をターゲットにするけど、ほかにも候補があれば、それらもサービス化する。


# [WinSW] の紹介

[WinSW]:<https://github.com/winsw/winsw/>

Windows Service Wrapper の略だそうです。

[Java Service Wrapper] への対抗だそうで。。
（[Project manifest](https://github.com/winsw/winsw/blob/v3/MANIFEST.md) に気付くのが遅かった）

[Java Service Wrapper]:<http://wrapper.tanukisoftware.org/doc/english/download.jsp>

単にKeycloakをサービス化するなら、 [Java Service Wrapper] でも良かったわ。

.net ベースで開発されている。

[リリースバイナリ](https://github.com/winsw/winsw/releases) がちょっと面白くて、.netの [マネージドコード] ベースのものに加えて、x86とx64の実行ファイル（.netが未インストールでも動く）が提供されている。

x86とx64の建付けがどうなっているかというと、.NET Coreを含んたバイナリになっているそうな。

[マネージドコード]:<https://docs.microsoft.com/ja-jp/dotnet/standard/managed-code>

稼働環境にインストールされている.netのバージョンが明らかで変わる予定ないならマネージドコード版を使えばよいと思う。


# インストール

例によってscoopで。

```winsw3-net4.json
{
    "version": "3.0.0-alpha.10",
    "description": "A wrapper executable that can be used to host any executable as a Windows service",
    "homepage": "https://github.com/winsw/winsw",
    "license": "MIT",
    "url": "https://github.com/winsw/winsw/releases/download/v3.0.0-alpha.10/WinSW-net461.exe#/WinSw3.exe",
    "hash": "3c0671e7b6914512062895b5c96f6b9cf16a83889e235a2948379e6e329296e7",
    "bin": "WinSw3.exe",
    "checkver": "github",
    "autoupdate": {
        "url": "https://github.com/winsw/winsw/releases/download/v$version/WinSW.NET461.exe#/WinSw3.exe"
    }
}
```

```
> scoop install winsw3-net4
```

v3系がalpha.10で放置されているようだけど、[v2系](https://github.com/winsw/winsw/tree/master) は「実行ファイルと同名のxmlファイルを参照する」って「いけてない」つくりになっている。

馬鹿にならないサイズなのに、サービスの個数ぶんだけexeを準備するのも馬鹿らしいので、v3系を使う。
exe増やすのにシンボリックリンク使えばいい[^99]という話は却下。

[^99]:この記事書いたあとで試してみたけど、実行ファイルパスや実行ファイル名の解決で支障があるのかサービス登録の時点で失敗した。


# Keycloakをサービスとして登録

WinSWサービス用のxmlファイルを作成する。

```keycloak.xml
<service>
  <id>keycloak</id>
  <name>Keycloak</name>
  <description>Keycloak</description>
  <workingdirectory>W:\my-service</workingdirectory>
  <executable>cmd.exe</executable>
  <arguments>/C start-keycloak.cmd</arguments>
  <log mode="rotate" />
  <onfailure action="restart" delay="300 sec" />
  <serviceaccount>
    <username>MYDOMAIN\k2ok-app</username>
    <allowservicelogon>false</allowservicelogon>
  </serviceaccount>
  <logpath>W:\my-service\keycloak\logs</logpath>
</service>
```

サービスとしてインストール。

```
> winsw3 install keycloak.xml
```

あとはコマンドラインで起動するのと同様にサービス起動できる。

また、マシン起動時に自動起動させることも可能。遅延開始設定も可能。

ログの出力方法も凝れるけど、とりあえず出力先を指定するくらいで触っていない。


# SSHトンネル用コマンドをサービス登録

上記のとおり、XMLさえ記述すればサービス化できるので、sshのトンネルコマンドをサービス化することもできる。

```MyTunnel.xml
<service>
  <id>MyTunnel</id>
  <name>My tunnel to some host</name>
  <description>My tunnel to some host using SSH</description>
  <executable>ssh</executable>
  <arguments>-N -g -R7161:127.0.0.1:7161 tunnel@some-host</arguments>
  <log mode="rotate" />
  <onfailure action="restart" delay="300 sec" />
  <serviceaccount>
    <username>MYDOMAIN\k2ok-app</username>
    <allowservicelogon>false</allowservicelogon>
  </serviceaccount>
  <logpath>W:\my-service\MyTunnel\logs</logpath>
</service>
```


# 課題

OSの再起動時に、起動してくれない場合があった。

（遅延起動は設定しているが、数時間経っても起動してくれないので意図する動作ではない）

システムログを追ってみると、Service Control Managerでサービス起動時のタイムアウト(30000 ミリ秒)になったと出ている。

非力なマシンで、起動直後の応答が悪いのも当然なので、タイムアウトの閾値を延ばす方法を探す…あった。

`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control` へ `ServicesPipeTimeout` 追加するとのこと。

参考: [How to extend the timeout for services if they fail to start or stop](https://www.codetwo.com/kb/how-to-extend-the-timeout-for-services-if-they-do-fail-to-start/)

うまく起動できるようになりました。

グループポリシーでもいいそうだけど、ポリシーとして設定・配信するには躊躇する項目だ。