WebGLビルドしたUnityのApacheコンテナ実行

# 背景
Unityで作成したhtmlをWebに公開したいので、試しにローカルのApacheコンテナで実行できるようにした。
UnityでWebGLビルドしたhtmlは、ホスティングしないでChromeで開くとエラーになる。

# TL;DR

* Unityでビルドしたgz圧縮のWebGLファイルをApacheコンテナで実行
* Unity公式ドキュメントに従って、WebGLを利用するためのサーバ設定を.htaccessに記述
* Apacheコンテナのデフォルト設定では、htdocs配下がAllowOverride Noneになっており、.htaccessが無効なので注意が必要

# 前提

## Ver
Unity 2021.2.7f1 personal
Docker Engine 20.10.11
Chrome  98.0.4758.80（Official Build） （arm64）

## WebGLの圧縮形式
Brotli圧縮(拡張子は.br)は圧縮率が向上していながら、圧縮/伸長速度は同程度を維持しているなどメリットがあるが、SSL/TLSが必須となっているため、今回のローカル実行でのお試しはgz圧縮で実施。
Unityでの設定は Edit → ProjectSetttings → Playerで WebGLタブを選択して、Cpmpression FormatでGzipを選択する。

# 手順

## 1. UnityでWebGL形式ビルド
Unityでゲームを作成してWebGL形式でビルドする。
Unityによるゲーム作成は [Unityの教科書](https://www.amazon.co.jp/dp/4815611343) がとても参考になるのでおすすめ。

## 2. Apacheの起動

ここではhtmlを配置せずにApacheのコンテナを試しに起動するまで。

### 2.1 Apacheコンテナの起動

Docker Desktopなどがインストールされ、Docker Engineがインストールされている前提。
Apacheのコンテナイメージを取得して、起動する。

```
% docker pull httpd
Using default tag: latest
・・・略

% docker image ls   
REPOSITORY   TAG       IMAGE ID       CREATED      SIZE
httpd        latest    1c2ff9e4eb7d   3 days ago   136MB

% docker run -d -p 8080:80 httpd
```

Chromeで localhost:8080 にアクセスして _It Woks!_ が表示されることを確認する。
起動を確認したら、_docker ps_ でコンテナIDを調べて、 _docker stop_ と _docker rm_ の引数にコンテナIDを指定して実行して、コンテナを削除する。

## 3 WebGLファイルと.htaccessの配置

Apacheコンテナに対してUnityでビルドしたWebGLを配置しChromeでゲームができるようにする。

### 3.1 htdocsの作成とWebGLファイルの配置
ローカルPCの適当なディレクトリに htdocsを作成する。
ビルドしたWebGLファイル一式をhtdocs配下に配置する。

### 3.2 .htaccessファイルの配置

3.1 で作成した htdocs/Build 直下 .htaccessファイルを配置する。

:::note warn
Unity公式ドキュメントに記載された圧縮形式によるWebGL対応の手順を試しているだけで、.htaccessの使用を推奨しているわけではない。
:::

.htaccessファイルの中身はUnity公式ドキュメントを参照して作成する。
https://docs.unity3d.com/ja/2021.2/Manual/webgl-server-configuration-code-samples.html
拡張子brの部分はBrotli圧縮を使用しないので、コメントしているが、そのままでも問題なし。
_AddEncoding gzip .unityweb_ の部分はgzip圧縮のローディングパフォーマンスが改善するとあるのでコメントアウトを実施。

```
# This configuration file should be uploaded to the server as "<Application Folder>/Build/.htaccess"
# NOTE: "mod_mime" Apache module must be enabled for this configuration to work.
# <IfModule mod_mime.c>

# The following lines are required for builds without decompression fallback, compressed with gzip
RemoveType .gz
AddEncoding gzip .gz
AddType application/octet-stream .data.gz
AddType application/wasm .wasm.gz
AddType application/javascript .js.gz
AddType application/octet-stream .symbols.json.gz

# The following lines are required for builds without decompression fallback, compressed with Brotli
# RemoveType .br
# RemoveLanguage .br
# AddEncoding br .br
# AddType application/octet-stream .data.br
# AddType application/wasm .wasm.br
# AddType application/javascript .js.br
# AddType application/octet-stream .symbols.json.br

# The following line improves loading performance for uncompressed builds
AddType application/wasm .wasm

# Uncomment the following line to improve loading performance for gzip-compressed builds with decompression fallback
AddEncoding gzip .unityweb

# Uncomment the following line to improve loading performance for brotli-compressed builds with decompression fallback
# AddEncoding br .unityweb
```

#### 3.3 作成したhtdocsディレクトリをマウントして起動
3.1と3.2 で作成したhtdocsディレクトリをマウントしてdockerを起動する。

```
% docker run -d -p 8080:80 -v [適当なディレクトリ]/htdocs:/usr/local/apache2/htdocs httpd 
```

## 4 エラーのデバッグ

### 4.1 Build/XXXX_WebGL.framework.js.gzのエラー
この状態でChromeで localhost:8080 にアクセスして起動確認をすると以下のようなエラーがブラウザに表示される。

```
Unable to parse Build/XXXX_WebGL.framework.js.gz! This can happen if build compression was enabled but web server hosting the content was misconfigured to not serve the file with HTTP Response Header "Content-Encoding: gzip" present. Check browser Console and Devtools Network tab to debug.
```

３.1 で htdocs に配置した　"_Build/XXXX_WebGL.framework.js.gz_ を解析できません。" というエラーが表示されている。そして最後の行に "ブラウザの[コンソール]と[Devtoolsネットワーク]タブを確認してデバッグします。" ということが書かれている。

### 4.2 Chromeのデベロッパーツールによるデバッグ
エラーメッセージに書かれている通り、デベロッパーツールでネットワークタブを見ると、XXXX_WebGL.framework.js.gz の Responseヘッダーにある Content-typeがapplication/x-gzipになっている。
<img width="400" alt="デベロッパーツールによるデバッグ"  src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2533391/542f8549-4952-169c-499c-d60f575d2854.png">
3.2 で指定した "_AddType application/javascript .js.gz_" が効いてなくて、.js.gzの拡張子が jsファイルとして解釈されていない。

### 4.3 httpd.confの確認
.htaccessを有効にするためには、 対象のディレクトリに対して、 "_AllowOverride All_" の設定が必要だ。
以下のコマンドでコンテナにSSHして、Apacheの httpd.conf の中身を確認する。

```
% docker exec -it [コンテナID] /bin/bash

# httpd.conf に対して AllowOverride でgrepして、行のあたりをつけてから中身を確認
% root@[コンテナID]:/usr/local/apache2# cat conf/httpd.conf | grep AllowOverride -n
249:    AllowOverride none
282:    # AllowOverride controls what directives may be placed in .htaccess files.
284:    #   AllowOverride FileInfo AuthConfig Limit
286:    AllowOverride None
399:    AllowOverride None

root@[コンテナID]:/usr/local/apache2# cat conf/httpd.conf -n
260	#
261	# DocumentRoot: The directory out of which you will serve your
262	# documents. By default, all requests are taken from this directory, but
263	# symbolic links and aliases may be used to point to other locations.
264	#
265	DocumentRoot "/usr/local/apache2/htdocs"
266	<Directory "/usr/local/apache2/htdocs">
267	    #
268	    # Possible values for the Options directive are "None", "All",
269	    # or any combination of:
270	    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
271	    #
272	    # Note that "MultiViews" must be named *explicitly* --- "Options All"
273	    # doesn't give it to you.
274	    #
275	    # The Options directive is both complicated and important.  Please see
276	    # http://httpd.apache.org/docs/2.4/mod/core.html#options
277	    # for more information.
278	    #
279	    Options Indexes FollowSymLinks
280	
281	    #
282	    # AllowOverride controls what directives may be placed in .htaccess files.
283	    # It can be "All", "None", or any combination of the keywords:
284	    #   AllowOverride FileInfo AuthConfig Limit
285	    #
286	    AllowOverride None
287	
288	    #
289	    # Controls who can get stuff from this server.
290	    #
291	    Require all granted
292	</Directory>
```

やはり "_/usr/local/apache2/htdocs_" に対して"_AllowOverride None_"(286行目) になっている。

## 5 httpd.confの修正

コンテナのhttpd.confは直接編集できないので、ローカルにいったん取得して、修正してマウントして起動する。

### 5.1 コンテナのhttpd.confをローカルに取得

```
docker run --rm httpd cat /usr/local/apache2/conf/httpd.conf > conf/httpd.conf
```
ローカルに取得したファイルに対して、"_AllowOverride None_"(286行目)を"_AllowOverride All_"に変更

### 5.2 httpd.confもマウントしてコンテナを起動

起動していたApacheコンテナを削除してhttpd.confもマウントに追加して起動する。

```
docker run -d -p 8080:80 -v [適当なディレクトリ]/htdocs:/usr/local/apache2/htdocs -v [適当なディレクトリ2]/conf/httpd.conf:/usr/local/apache2/conf/httpd.conf httpd 
```

### 5.3 Chromeで確認

まだエラーが出る場合はキャッシュが残っている可能性あり。
Chrome右上の設定 → セキュリティとプライバシー → 閲覧履歴データの削除でCookieやキャッシュされた画像とファイルなどを削除する。

以上

# 感想

* 普段Apacheを触ってないのに、Webサーバを立てようとするからこういうことになる
* 設定の中身を大して理解せずに、とりあえずエラーメッセージでググったせいで、数時間を無駄に費やした
* UnityのWebGLビルドのエラーメッセージ通りにデバッグすればすぐに解決できた
    * 英語のエラーメッセージも軽く読み流す悪いクセが出た