はじめに
Apache+Tomcatの構成において、Tomcat側で静的リソースを処理させずに、Apacheに静的リソースを処理させたい。
Tomcat側ではJavaアプリケーションを動かすのみとしたいので、前段のApacheで可能な限り静的リソースのリクエストを処理させてあげることでTomcatへのリクエストを減らすことを目指す。
環境
1
2
3
4
5
| MacOS sonoma 14.2.1
Homebrew 4.2.4
Docker 24.0.6
Docker Composer v2.22.0-desktop.2
|
準備
準備段階として、実際にApache+Tomcatを動作させる環境を用意した。
https://github.com/katsuobushiFPGA/apache-tomcat-docker
Docker+Docker composeが入ってる環境であれば動作するはずである。
1
2
3
| git clone https://github.com/katsuobushiFPGA/apache-tomcat-docker.git
docker compose up -d
|
で構築は完了する。
ローカルの80ポートを使用するため、他に使用しているソフトがある場合は終了しておく必要がある。
目指すべき形
今回は、Tomcatのsample.war
の中にある、images
ディレクトリが静的リソースである。
現状、AJPでApacheから全てのリクエストをTomcatに流しているが、静的リソースに関しては、Tomcatの方に流さずにApacheの方で処理するということを行う。
今回のケースで具体的な例を挙げると、リクエストされたパスが images
の場合は、Apacheで処理させる。
それ以外の場合は、Tomcatに処理をさせる。
効果確認
効果確認として以下ができていれば問題ない。
手順
1. AJPでTomcatにリクエストを流している箇所の確認
今回作成したサンプルのDockerプロジェクトでは、/etc/httpd/conf.d/httpd-ajp.conf
が該当する。
1
2
3
| <Location />
ProxyPass ajp://localhost:8009/
</Location>
|
中身は、全てのパスをajp
でlocalhost:8009に流していることがわかる。
2. 特定のURLのみProxyPassを使用しない
段階を踏んで設定を適用していく。
まずは、images
のパスをTomcatに処理させないようにする。
そのため、httpd-ajp.conf
に以下を追記する。
1
2
3
4
5
6
7
| <Location />
ProxyPass ajp://localhost:8009/
</Location>
+<Location /sample/images>
+ ProxyPass !
+</Location>
|
設定を反映し、Dockerをビルドし直してみる。
修正前
修正後
このように、gif
画像がリンク切れとなる。
また、この時の Tomcat
のlocalhost_access_logを覗いてみる。
1
2
3
4
5
6
7
| docker compose exec web bash
bash-4.2# cat /usr/local/tomcat/logs/localhost_access_log.2024-01-18.txt
192.168.65.1 - - [18/Jan/2024:18:47:48 +0000] "GET /sample HTTP/1.1" 302 -
192.168.65.1 - - [18/Jan/2024:18:47:48 +0000] "GET /sample/ HTTP/1.1" 304 -
192.168.65.1 - - [18/Jan/2024:18:49:57 +0000] "GET /sample HTTP/1.1" 302 -
192.168.65.1 - - [18/Jan/2024:18:49:57 +0000] "GET /sample/ HTTP/1.1" 200 636
|
このような感じになっており、 gif
画像の処理はこちら側ではしていない。
Apache側はというと、
1
2
3
4
5
6
7
| bash-4.2# cat /var/log/httpd/access_log
192.168.65.1 - - [18/Jan/2024:18:47:48 +0000] "GET /sample HTTP/1.1" 302 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
192.168.65.1 - - [18/Jan/2024:18:47:48 +0000] "GET /sample/ HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
192.168.65.1 - - [18/Jan/2024:18:49:57 +0000] "GET /sample HTTP/1.1" 302 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
192.168.65.1 - - [18/Jan/2024:18:49:57 +0000] "GET /sample/ HTTP/1.1" 200 636 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
192.168.65.1 - - [18/Jan/2024:18:49:57 +0000] "GET /sample/images/tomcat.gif HTTP/1.1" 404 196 "http://localhost/sample/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
1
| "GET /sample/images/tomcat.gif HTTP/1.1" 404
|
となっており、gif
画像を処理しようとしているが、404エラーとなっているログが確認できる。
つまり、ここまででApche側で処理させるというところまでは完了できた。
補足
複数のディレクトリを指定したい場合は、LocationMatch
が使える。
※ Apacheに処理させる場合は、DocumentRootから見に行くのでこれを修正する必要がある。
とはいえ、DocumentRootは極力変えたくない。
3. Apacheに静的リソースを処理させる
Apacheに静的リソースを処理させるために、Aliasを使用して ファイルシステムのディレクトリパスとマッチさせる必要がある。
ということなので、以下を設定する。
1
2
3
4
5
6
7
8
9
10
11
12
| <Location />
ProxyPass ajp://localhost:8009/
</Location>
<Location /sample/images>
ProxyPass !
</Location>
+Alias /sample/images /usr/local/tomcat/webapps/sample/images
+<Directory /usr/local/tomcat/webapps/sample/images>
+ Require all granted
+</Directory>
|
特に、Alias を DocumentRoot ディレクトリの外側に配置した場合は、行き先のディレクトリに対する アクセス権限を明示的に制限しなければならないでしょう。
https://httpd.apache.org/docs/trunk/ja/mod/mod_alias.html
とあるので、Direcotry
セクションを含めている。
設定を反映し、Dockerをビルドし直してみる。
Apacheのエラーログを見ると下記が出ていた。
1
| [core:error] [pid 27] (13)Permission denied: [client 192.168.65.1:21435] AH00035: access to /sample/images/tomcat.gif denied (filesystem path '/usr/local/tomcat/webapps/sample/images') because search permissions are missing on a component of the path, referer: http://localhost/sample/
|
Apacheプロセスが読み取り権限を持っていないことが原因のようなので。
1
2
| chmod +x /usr/local/tomcat/webapps/sample
chmod -R +r+x /usr/local/tomcat/webapps/sample/images/
|
とすることで権限を与える。
表示できていることを確認できた。
Apacheのアクセスログ
1
2
3
4
5
| docker compose exec web tail -n 10 /var/log/httpd/access_log
192.168.65.1 - - [18/Jan/2024:20:00:29 +0000] "GET /sample/images/tomcat.gif HTTP/1.1" 403 199 "http://localhost/sample/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
192.168.65.1 - - [18/Jan/2024:20:01:12 +0000] "GET /sample/ HTTP/1.1" 304 - "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
192.168.65.1 - - [18/Jan/2024:20:01:12 +0000] "GET /sample/images/tomcat.gif HTTP/1.1" 200 1441 "http://localhost/sample/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36"
|
Tomcatのアクセスログ
1
2
3
4
5
6
7
8
9
10
11
12
| docker compose exec web tail -n 10 /usr/local/tomcat/logs/localhost_access_log.2024-01-18.txt
192.168.65.1 - - [18/Jan/2024:19:59:38 +0000] "GET /sample/ HTTP/1.1" 304 -
192.168.65.1 - - [18/Jan/2024:19:59:38 +0000] "GET /sample/ HTTP/1.1" 304 -
192.168.65.1 - - [18/Jan/2024:19:59:38 +0000] "GET /sample/ HTTP/1.1" 304 -
192.168.65.1 - - [18/Jan/2024:20:00:25 +0000] "GET /sample/ HTTP/1.1" 304 -
192.168.65.1 - - [18/Jan/2024:20:00:26 +0000] "GET /sample/ HTTP/1.1" 304 -
192.168.65.1 - - [18/Jan/2024:20:00:28 +0000] "GET /sample/ HTTP/1.1" 304 -
192.168.65.1 - - [18/Jan/2024:20:00:28 +0000] "GET /sample/ HTTP/1.1" 304 -
192.168.65.1 - - [18/Jan/2024:20:00:29 +0000] "GET /sample/ HTTP/1.1" 304 -
192.168.65.1 - - [18/Jan/2024:20:00:29 +0000] "GET /sample/ HTTP/1.1" 304 -
192.168.65.1 - - [18/Jan/2024:20:01:12 +0000] "GET /sample/ HTTP/1.1" 304 -
|
静的リソースの処理はしていないのでOK
今回の例とは異なる設定例
よくある設定として、ROOT以下にアプリケーションをデプロイしており、なおかつ静的リソースが複数箇所にある場合の例を記載する。
AJPの設定に加え、下記を設定すれば良い。
静的リソースのディレクトリは、 css
, images
, js
としている。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
| +<LocationMatch "^/(css|images|js)/">
+ ProxyPass !
+</LocationMatch>
+
+AliasMatch ^/(css|images|js)(.*) /usr/share/tomcat9/webapps/ROOT/$1$2$3
+<DirectoryMatch ^/usr/share/tomcat9/webapps/ROOT/(css|images|js)>
+ AllowOverride None
+ Require all granted
+</DirectoryMatch>
+
+AliasMatch ^/(resource1|resource2)(.*) /var/static-resource/$1$2
+<DirectoryMatch ^/var/static-resource/(resource1|resource2)>
+ AllowOverride None
+ Require all granted
+</DirectoryMatch>
|
とすることで、複数の静的リソースをまとめて処理できる。
場所が異なる場合は、Alias, Directoryのペアを都度書く必要があるが。
今回の記事について
今回の記事はChatGPTを使用して作成を行なった。
ChatGPTにプロンプトとして
1
2
3
| Apache + Tomcatの構成において、AJPで全てのリクエストをTomcatに流しております。
これを特定のパスのみApacheで処理するようにしたいです。
どのように設定すれば良いでしょうか
|
を入力することで設定が出てくるので、それを今回の動作試験用に書き直し記事にしたものである。
参考
おわりに
Apache+Tomcatの構成で静的リソースの処理のさせ方を変えるということを実践した。
元々業務で、先輩のアドバイスを頂いてこれを実践したのだが、確かにTomcatはTomcatにしかできないことのみやらせるのがベストだなと思った。
Apacheはそのためにあるのでこういう構成にしているのだろうとも思う。