2020年になって再び Tomcat を触る事になるとは思いませんでしたが、とあるプロジェクトで以下の構成のシステムを立ち上げることになりました。
- CentOS 7
- Tomcat 9
- Apache 2.4
yum
コマンドでインストールできる Tomcat は7系なので、Tomcat 9 は公式サイトからダウンロードしてきてインストールしました。
簡単だろうと思ってたのですが、意外なところでハマって2時間くらい費やしてしまったので、念のためメモしておきます。
手順概要
まずは、やったことを大まかにまとめておきます。
- SELinux を無効化
- 各種ソフトのインストール
- Apache、Java を yum でインストール
- Tomcat をダウンロードして適当な場所に配置
- Tomcat の設定
tomcat
ユーザー・グループの作成- systemd への登録
server.xml
を修正
- Apache の設定
- サービスの起動設定
手順詳細
SELinux の無効化
仕事でLinuxを使い始めて20年になりますが、いまだに SELinux を有効化している商用システムに関わったことがありません。
何はともあれ、今回のプロジェクトでも SELinux を使う要件はありませんので、以下のファイルを修正してサクッと無効化します。
sudo vi /etc/sysconfig/selinux
各種ソフトのインストール
yum でインストールできるものは、以下の通りインストールします。
sudo yum install httpd wget java-1.8.0-openjdk
Tomcat は、公式サイトから落としてきて、/opt
配下に配置します。
wget https://downloads.apache.org/tomcat/tomcat-9/v9.0.33/bin/apache-tomcat-9.0.33.tar.gz
tar zxvf apache-tomcat-9.0.33.tar.gz
sudo mv apache-tomcat-9.0.33 /opt/
cd /opt/
sudo ln -s apache-tomcat-9.0.33/ tomcat9
Tomcat の設定
RPM でインストールする時は気にする必要がありませんが、手動でインストールする場合には各種設定を自分でやる必要があります。
まずは、ユーザーとグループを作成します。また、/opt 配下に配置したファイルの所有者も変更しておきます。
sudo groupadd --system tomcat
sudo useradd -M -d /opt/tomcat9 -g tomcat tomcat
sudo chown -R tomcat: /opt/apache-tomcat-9.0.33/
次に、/etc/systemd/system/tomcat9.service
というファイルを、以下の内容で作成します。
[Unit]
Description=Tomcat 9 servlet container
After=network.target
[Service]
Type=forking
User=tomcat
Group=tomcat
Environment="JAVA_HOME=/usr/lib/jvm/jre"
Environment="JAVA_OPTS=-Djava.security.egd=file:///dev/urandom"
Environment="CATALINA_BASE=/opt/tomcat9"
Environment="CATALINA_HOME=/opt/tomcat9"
Environment="CATALINA_PID=/opt/tomcat9/temp/tomcat.pid"
Environment="CATALINA_OPTS=-Xms512M -Xmx1024M -server -XX:+UseParallelGC"
ExecStart=/opt/tomcat9/bin/startup.sh
ExecStop=/opt/tomcat9/bin/shutdown.sh
[Install]
WantedBy=multi-user.target
次に、server.xml
で、AJPリスナーを有効化し、普通の HTTP リスナーは不要なので無効化します。変更前後の diff を以下に示します。
--- /opt/tomcat9/conf/server.org.xml 2020-03-26 02:09:44.739733134 +0000
+++ /opt/tomcat9/conf/server.xml 2020-03-26 02:49:31.395688240 +0000
@@ -66,9 +66,11 @@
APR (HTTP/AJP) Connector: /docs/apr.html
Define a non-SSL/TLS HTTP/1.1 Connector on port 8080
-->
+ <!--
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
+ -->
<!-- A "Connector" using the shared thread pool-->
<!--
<Connector executor="tomcatThreadPool"
@@ -113,12 +115,11 @@
-->
<!-- Define an AJP 1.3 Connector on port 8009 -->
- <!--
<Connector protocol="AJP/1.3"
+ secretRequired="false"
address="::1"
port="8009"
redirectPort="8443" />
- -->
<!-- An Engine represents the entry point (within Catalina) that processes
every request. The Engine implementation for Tomcat stand alone
ポイントは secretRequires="false"
というところです。
Apache の設定
今回のプロジェクトでは OpenAM というソフトウェアを Tomcat で動かしたのですが、/openam
にアクセスすると Tomcat の /openam
を見に行くようにしたいので、/etc/httpd/conf.d/openam.conf
というファイルを以下の内容で作成しました。
<VirtualHost *:80>
ServerName openam.example.com
ProxyPass /openam ajp://localhost:8009/openam
</VirtualHost>
起動設定
最後に、Apache と Tomcat を自動的に起動するように設定し、reboot すれば完了です。
sudo systemctl daemon-reload
sudo systemctl enable tomcat9
sudo systemctl enable httpd
sudo shutdown -r now
途中で出たエラーの内容
上の方で「ポイントは secretRequires="false"
というところです」と書きましたが、その記述が無いと、以下のようなエラーが出ました。
26-Mar-2020 02:11:55.791 SEVERE [main] org.apache.catalina.util.LifecycleBase.handleSubClassException Failed to start component [Connector[AJP/1.3-8009]]
org.apache.catalina.LifecycleException: Protocol handler start failed
at org.apache.catalina.connector.Connector.startInternal(Connector.java:1038)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardService.startInternal(StandardService.java:438)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.core.StandardServer.startInternal(StandardServer.java:930)
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:183)
at org.apache.catalina.startup.Catalina.start(Catalina.java:633)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:343)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:474)
Caused by: java.lang.IllegalArgumentException: The AJP Connector is configured with secretRequired="true" but the secret attribute is either null or "". This combination is not valid.
at org.apache.coyote.ajp.AbstractAjpProtocol.start(AbstractAjpProtocol.java:264)
at org.apache.catalina.connector.Connector.startInternal(Connector.java:1035)
... 12 more
エラーメッセージで検索したところ、以下のサイトが引っかかって、そこで解決策を知りました。
Upgrading Apache Tomcat to version 8.5.51 can s… | BMC Communities
参照したログファイルは /opt/tomcat9/logs/catalina.log
です。
secretRequires
の説明などは、公式サイトの以下のページを参照して下さい。
Apache Tomcat 9 Configuration Reference (9.0.33) – The AJP Connector
まとめ
Tomcat 9 + Apache 2.4 on CentOS 7 というのはあまり人気の無い組み合わせなのでしょう。今回のエラーの原因となった secretRequires
に関して記載しているサイトは少なかったため、最初は動かなくて迷いました。
もし同じような問題ではまっている人の参考になれば、と思い本記事を書きました。
最後に完全な余談ですが、古参開発者にとって、Catalina といえば macOS ではなくて Tomcat です。