Laravel & Inertia & Docker環境でSSRを動かしてハマったところまとめ（WSL）

WSL環境のDockerを使った形でLaravelとInertiaのReact環境を作るのはできたが、SSRを行おうと思ったら割りとハマった。
ようやくなんとかなったので、そのまとめ。

## これまでの内容

Ineratiaの導入（not SSR）

https://qiita.com/miyawa-tarou/items/1b3ec0344e1b39ce9158

## 状況

以下のようにWSLにDockerがあり、PHP、nginx、mysql、mysqladminのコンテナがそれぞれ立っている状態です。
WSL以外ではもしかしたら問題なく動くかもしれません。

```mermaid
graph LR
  subgraph "Local"
    A-1["LocalPC(WSL)"]
  end
  subgraph "Docker Containers"
    A-1 --> B-1["nginx"]
    A-1 --> B-2[php]
    A-1 --> B-3[mysql]
    A-1 --> B-4[mysqlmyadmin]
    B-1 --> B-2
    B-2 --> B-3
    B-4 --> B-3
  end
```

SSRのサーバー（Inertia SSR server）はLocalPC上でnodeで立ち上げます。

## 先に解決策

### 接続先の問題

基本的な設定は公式サイト通りで動くはずだと思います、

https://inertiajs.com/server-side-rendering

Docker利用の場合は少し特殊で、InertiaのSSRではデフォルトで `http://127.0.0.1:13714` に接続しようとしますが、接続を試みるPHPコンテナから見て、`127.0.0.1` は自分（PHPコンテナ）なので、ローカルPCにたどり着けません。
そのため下記設定を入れます（ファイルはなければ新規追加）。
（私は本番はコンテナ運用ではない状態なので、GitHubには上げないようにしています。）

```php:config/inertia.php
<?php
return [
    'ssr' => [
        'enabled' => true,
        'url' => 'http://host.docker.internal:13714',
    ],
]
```

`host.docker.internal` については下記

https://docs.docker.jp/desktop/networking.html#desktop-networking-i-want-to-connect-from-a-container-to-a-service-on-the-host

https://gotohayato.com/content/561/

### ポートの問題

上の設定だけではエラーが出ます。PHPのコンテナからローカルのInertia SSR serverに接続できていなかったのが原因で、接続できない理由はWindowsがポートを受けつけていなかったからのようです。
そのためポートを開ける（受け付けるようにする）ことで解消します。

下記はPowershellなので、Windows限定です。

```powershell:powershell
# 受けつけていないことの確認（ポート13714）
> netstat -a
アクティブな接続

  プロトコル  ローカル アドレス      外部アドレス           状態
  TCP         0.0.0.0:80             DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:135            DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:445            DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:3306           DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:5040           DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:5357           DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:7680           DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:8080           DESKTOP-2PSEFKJ:0      LISTENING
（略

# WSLのIPの確認
> wsl -e hostname -I
***.***.***.***

# ポートを開ける（要管理者権限）　***.***.***.***は上のIP
> netsh interface portproxy add v4tov4 listenaddress=* listenport=13714 connectaddress=***.***.***.*** connectport=13714

# 確認
> netsh interface portproxy show all

ipv4 をリッスンする:         ipv4 に接続する:

Address         Port        Address         Port
--------------- ----------  --------------- ----------
*               13714       ***.***.***.*** 13714
```

```:PHPコンテナ
# curl host.docker.internal:13714
{"status":"NOT_FOUND","timestamp":1681899352326}
```
サーバーからちゃんとNot Foundが返ってきています（このNotFoundはサーバーが正しく返している形です）。これで通常通りSSRの実装を行えばSSRの形で表示されるようになります。

## 調査

### これまで

https://qiita.com/miyawa-tarou/items/2ca3f9585cb71a1891c1

上の記事に書いたことですが、下記のようなことが起きていました

- 設定はしたがSSRにならない
- SSR用サーバーにログを仕込むと、サーバーにアクセスが行っていない
- SSR用サーバーを叩くところにログを仕込むと、叩く際に `Failed to connect` で失敗している

### PHPコンテナ内で動かせば動く

PHPのコンテナ内でyarn installし、そこで
```
$ php artisan inertia:start-ssr
```
とすればPHPからこのSSRサーバーには接続できるので動かすことができます。（ここまで上の記事）
しかし、ファイルの操作の面で手間だったり、ファイルの権限の面で怪しい挙動をしていたのでもう少し調査しました。

### コンテナからローカルに接続できない？

```mermaid
graph LR
  subgraph "Local"
    A-1["LocalPC(WSL)+Ineria SSR server"]
  end
  subgraph "Docker Containers"
    A-1 --> B-1["nginx"]
    A-1 --> B-2[php]
    A-1 --> B-3[mysql]
    A-1 --> B-4[mysqlmyadmin]
    B-1 --> B-2
    B-2 --> B-3
    B-4 --> B-3
  end
```

やりたいことはphpコンテナからLocalPCに立てたnode port13714のサーバーに接続することです。

```:ローカル
// 両方取れる
# curl http://localhost:80

# curl http://localhost:13714
{"status":"NOT_FOUND","timestamp":1681905338769}
↑このエラーが出るのが正しい
```

ローカルでこういう状態であればWSLでは普通コンテナ側からは　`host.docker.internal` で取れるはずです。
80ポートのHTTPのものは取得ができますが、13714では取得できません。

```:PHPコンテナ
// これは取れる
# curl http://host.docker.internal:80

// これが取れない
# curl http://host.docker.internal:13714
curl: (7) Failed to connect to host.docker.internal port 13714: Connection refused
```

どうして……

```:PHPコンテナ
// これも同じように返る
# curl http://host.docker.internal:11111
curl: (7) Failed to connect to host.docker.internal port 11111: Connection refused
```

この適当なポートで叩いても同じように返る状態で、結局は受けつけているところまで達していないのでは？
というので初めに書いた解決策へ移っていきます。

```powershell:powershell
> netstat -a

アクティブな接続

  プロトコル  ローカル アドレス      外部アドレス           状態
  TCP         0.0.0.0:80             DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:135            DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:445            DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:3306           DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:5040           DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:5357           DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:7680           DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:8080           DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:9000           DESKTOP-2PSEFKJ:0      LISTENING
  TCP         0.0.0.0:28252          DESKTOP-2PSEFKJ:0      LISTENING

```
たとえばここに存在してる28252ポートを叩くと違う返り方をする、というか返ってきません。

```:PHPコンテナ
// これは返ってこない
# curl http://host.docker.internal:28252

```

というので動きが変わったので、やはりここが怪しそうですね。
下記を参考に設定したところ、つながるようになりました

https://arubeh.com/archives/1749

```powershell:powershell
# WSLのIPの確認
> wsl -e hostname -I
***.***.***.***

# ポートを開ける（要管理者権限）
> netsh interface portproxy add v4tov4 listenaddress=* listenport=13714 connectaddress=***.***.***.*** connectport=13714
```

これを行うことで無事取れました↓

```
# curl host.docker.internal:13714
{"status":"NOT_FOUND","timestamp":1681899352326}
```

これでinertiaの向き先を `host.docker.internal` にすれば動きますね。
inertiaの接続周りは特に設定ファイルはないのですが、自分で足せば動きます
config下に適当にinertia.phpを作ります

```php:config/inertia.php
<?php
return [
    'ssr' => [
        'enabled' => true,
        'url' => 'http://host.docker.internal:13714',
    ],
]
```

ちなみにデフォルト値等の設定は `\vendor\inertiajs\inertia-laravel\config\inertia.php` にあります。