Windows11ドメイン環境でOllama社内チャット:IIS ARRの502と“Windowsサービス化”失敗からの現実解

こんにちは、yukiです。
今回は「Ollama を LAN 内共有チャットにする」ために、Windows 11(ドメイン参加)+ IIS 逆プロキシ + Node/Express + Ollama の構成を組んだときの記録を書きます。

狙いは成功談というより、失敗と切り分けのログを残すことです。特に、IIS 絡みで出がちな 502、Windows 常駐化の 7009/7000、URL Rewrite の {R:1} など、「同じ地雷を踏む人が減る」ことを優先します。


やりたかったことと要件

目的はシンプルで、社内LAN内から使える Web チャットを用意することでした。

要件はこんな感じです。

  • Ollama はローカル実行(外に出さない)
  • 公開するのは IIS(Webサーバ)のみ
  • 同時実行制御あり(将来の利用増を見据える)
  • 将来的に Windows 認証対応したい
  • 常駐稼働(再起動しても戻る)

最終的な構成(ここが結論)

先に結論です。Windows で Node を「ちゃんと」常駐させるのは意外と面倒で、今回の現実解はこうなりました。

  • IIS(ARR + URL Rewrite)で Reverse Proxy
  • Node は 127.0.0.1:3000 で待ち受け
  • Ollama は 11434
  • Node の常駐は Windowsサービス化を諦めてタスクスケジューラ
    • 起動時実行
    • 最上位特権
    • 再起動設定あり
  • /health エンドポイントで死活監視(切り分けが爆速になる)

構成イメージはこれです。

Browser(LAN内)
→ IIS (Reverse Proxy)
→ Node (127.0.0.1:3000)
→ Ollama (11434)

「IIS だけ公開」「Ollama はローカル」の要件を満たしつつ、運用も回せる形に落ちました。


ハマりポイントと切り分けログ

ここからが本題で、実際に詰まった順に書きます。
結論としては「IIS が悪いと思ったら Node が止まってただけ」みたいな話が何度か出ます。


① Node が起動していないのに IIS 502

症状
ブラウザで叩くと、いきなりこれ。

  • HTTP 502 - Bad Gateway

IIS 逆プロキシの設定を疑いがちなんですが、この時点ではまずバックエンドの疎通を見ます。

切り分け
Node 側に健康診断のためのエンドポイントを用意して、ローカルで叩きます。

  • curl http://127.0.0.1:3000/health
    • Failed to connect

これで「IIS の問題」ではなく「Node が死んでる」が確定しました。
IIS 502 は原因が広いので、IIS から追うと時間が溶けます。まず 127.0.0.1 直叩きが最短でした。

学び

  • 502 を見たら、最初に バックエンドの直叩き
  • /health は運用のためというより、切り分け速度のために価値がある

② PowerShell で sc が動かない罠(エイリアス問題)

次に「Node を Windows サービス化したい」と思って、これを打ちました。

  • sc create OllamaChat binPath= ...

症状
見覚えないエラー。

  • Set-Content : 引数 'binPath=' を受け入れる位置指定パラメーターが見つかりません。

一瞬「sc の構文ミス?」と思ったんですが、原因はもっとくだらないやつでした。

原因
PowerShell では scSet-Content のエイリアスになっていて、Windows の Service Control(sc.exe)じゃない

対策

  • sc.exe create ...拡張子付きで呼ぶ

学び

  • PowerShell は「同じコマンド名でも別物」が起きる
  • サービス周りは sc.exe を明示したほうが事故らない

③ サービス起動できない(7009 / 7000)— “サービス化は意外と難しい”

sc.exe でサービス登録はできました。でも起動が安定しません。

イベントログ

  • 7009 / 7000 系のエラーが出て、起動に失敗します。

ざっくり言うと、

  • 「指定時間内に応答しない」
  • タイムアウト(30000ms

原因(ここが重要)
Node を cmd /c でラップして起動すると、サービスコントロールマネージャ(SCM)に対して“起動完了”を正しく通知できない
その結果、30秒で「起動してない扱い」になって落とされます。

つまり、sc.exe 単体で Node を “正しい Windows サービス” にするのは難しい、という話でした。

学び

  • 7009/7000 は「アプリが重い」だけじゃなく、サービスとしての起動条件を満たしてないケースがある
  • Node の常駐を「サービス」としてやるなら、それ用の仕組み(サービスラッパー等)が必要になる

④ URL Rewrite の “パス消失” 問題({R:1} がないと死ぬ)

IIS の ARR + URL Rewrite を入れて、Reverse Proxy の設定もできた。
…と思ったら API が 404 を返します。

症状

  • POST /api/chat404

Node 側は /api/chat を生やしているのに、IIS 経由だと消える。

原因
IIS の転送先がこうなっていました。

  • http://127.0.0.1:3000

これだと、元のパス /api/chat が落ちて、常に / に飛ばされる感じになります。

正解

  • http://127.0.0.1:3000/{R:1}

{R:1} を付けて、元のパスを保ったままバックエンドへ渡す必要がありました。

学び

  • 逆プロキシで「404」は、アプリ側より先に Rewrite でパスが消えてないか疑う
  • {R:1} は “知ってるかどうか” の罠

⑤ 502 の真因は IPv6/localhost ではなく Node 停止だった(ありがちな誤診)

IIS 502 は「localhost/IPv6 の罠」として語られがちです。
なので自分も以下を疑って切り分けしました。

典型チェック

  • localhost ではなく 127.0.0.1 固定
  • ARR の Enable Proxy 確認
  • Rewrite のパターン確認

それでも 502 のとき、結局の真因はこれでした。

  • Node が止まってる

学び

  • “典型原因” は潰しつつ、最後は プロセス生存確認に戻ってくる
  • 502 は IIS のエラーに見えて、実態は「上流が死んでる」ことが多い

最終構成の運用ポイント(現実的に回る形)

最終的に安定したのはこの組み合わせです。

  • Node は node.exe server.js直起動
  • タスクスケジューラで常駐相当を作る
    • 起動時実行
    • 最上位特権
    • 失敗時再起動(リトライ)
  • IIS Reverse Proxy は 127.0.0.1 固定
  • Rewrite は {R:1} を必ず付ける
  • /health で死活監視(IIS 502 の切り分けが速い)

「Windows サービスとして正しく作る」より、「業務要件を満たして堅く動く」を取りました。


同じことをやる人向けチェックリスト

最後に、今回のログから「ここだけ見れば詰まりにくい」チェックリストを置きます。

疎通・502 切り分け

  • curl http://127.0.0.1:3000/health が通る(Node 生存確認)
  • IIS の転送先は localhost ではなく 127.0.0.1
  • ARR の Enable Proxy が有効
  • 502 が出たら IIS より先に Node のプロセスを見る

URL Rewrite

  • 転送先が http://127.0.0.1:3000/{R:1} になっている
  • /api/chat などのパスが消えていないか確認

常駐化

  • PowerShell で sc を打つなら sc.exe を使う
  • sc.exe だけで Node をサービス化しようとして 7009/7000 にハマったら、設計を疑う
  • 社内導入制約があるなら、タスクスケジューラを最初から候補に入れる
    • 起動時
    • 最上位特権
    • 再起動設定

おわりに

今回の学びは「IIS 逆プロキシの設定」よりも、「Windows で Node を常駐させる難しさ」と「502 を見たときの切り分け順」でした。

  • 502 は IIS のせいに見えるけど、まず Node の生存確認
  • Rewrite の {R:1} は知らないとハマる
  • Windows サービス化は “動けばOK” ではなく、SCM との握りが必要
  • 社内運用を考えると、タスクスケジューラが一番早くて堅い場合がある

同じ構成を作ろうとしている人の「時間が溶けるポイント」を、少しでも減らせたら嬉しいです。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)