はじめに
Linuxに関する知識を身につけたくて、LinuxサーバのレンタルからJDK, Apache, Tomcatのインストール、自分のローカルPCで作ったJavaアプリケーション(Hello Worldレベル)のデプロイを自力でやってみました。その際にやったこと、詰まったことをどうやって解消したかを書き残したのがこの記事です。
最近はクラウドが圧倒的に主流だけど、クラウドはいろいろブラックボックスすぎると感じています。自分はLinux(インフラ)に関してはとにかく基礎ができていないので、まずはクラウドではなくプレーンなLinuxサーバをレンタルし、そこにアプリケーションをデプロイする作業を自分でやってみる。さらに今後は、アプリケーションを拡張して運用も自分でやってみる。このような試みを通じて、Linuxの基礎を身につけたい。そう思ってこんなことをやってみました。今回はアプリケーションをデプロイするまでで、拡張や運用は今後の話です。デプロイしたのは、アプリケーションと言ってもHello Worldレベルなので、Javaについては書きません。LinuxサーバをレンタルしてApache、Tomcatをインストールして設定してURL叩いたらHello Worldが出た!っていう程度のものです。
実際にやったこと
個人向けレンタルサーバをレンタル
Javaが使える個人向けレンタルサーバをレンタル。今回はさくらインターネットを使うことにした。いろいろあるので自分で探して適切なものを選択しましょう。
OSの確認
CentOS のバージョン確認
これからレンタルしたサーバにJavaやApacheやTomcatをインストールしていくのですが、その前にOSの基本情報を確認。まずは、以下のコマンドで CentOS のバージョンを確認します。
cat /etc/redhat-release
CentOS Linux release 7.7.1908 (Core)
CentOSのバージョン7であることがわかる。
アーキテクチャ(OSが32bit, 64bitどちらなのか)を確認
以下のコマンドでOSが32bit, 64bitどちらなのかを確認します。
arch
X86_64
これは64bitであることを示しています。32bitの場合は以下のように表示されます。
i686
以下のコマンドでも確認することができます。
uname -a
CentOS 7 の場合は以下のように表示されます。
Linux ik1-344-32350.vs.sakura.ne.jp 3.10.0-957.10.1.el7.x86_64 #1 SMP Mon Mar 18 15:06:45 UTC 2019 x86_64 x86_64 x86_64 GNU/Linux
参考ですが、CentOS 6 の場合は以下のように表示されます。
Linux localhost.localdomain 2.6.32-279.2.1.el6.x86_64 #1 SMP Fri Jul 20 01:55:29 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux
参考:CentOSのバージョン確認コマンドとアーキテクチャ確認コマンド
パッケージのアップデート
以下コマンドでパッケージをアップデートします。
yum update
本来であれば、現在動作中のプログラムに影響を与えないようにするため、パッケージを指定してアップデートします。しかし今回はレンタルしたばかりで特に何のアプリケーションも稼働していないので、雑にyum update
ですべてのパッケージを更新しました。
パッケージを指定するコマンドは以下の通りです。普通はこっちを使って稼働中プログラムに影響を与えないようにしてください。
yum update [パッケージ]
参考:【yum update】yumのパッケージを安全にアップデートする
Java 8 (OpenJDK) インストール
ランタイムだけのインストールで良ければ java-1.8.0-openjdk を、OpenJDK の開発環境もインストールしたければ java-1.8.0-openjdk-devel もインストールします。
Java 8 (JDK) ランタイムのインストール
以下のコマンドを実行してJava 8 (JDK) ランタイムをインストールします。
yum install java-1.8.0-openjdk
java 8 (JDK) 開発環境のインストール
開発環境が必要であれば、以下のコマンドを実行してjava 8 (JDK) 開発環境をインストールします。私は自分のPCで開発してそれをサーバにデプロイするので開発環境はインストールしませんでした。
yum install java-1.8.0-openjdk-devel
インストール完了確認
念のためインストールが完了したのかを確認してみましょう。次のコマンドを打って、インストールした Java のバージョンが表示されれば確認終了です。
java -version
openjdk version "1.8.0_242"
OpenJDK Runtime Environment (build 1.8.0_242-b08)
OpenJDK 64-Bit Server VM (build 25.242-b08, mixed mode)
参考:CentOS 7 に Java 8 (OpenJDK) を yum インストールする手順
Apacheインストール
Apacheをインストールしてサービス登録する
以下コマンドでApacheをインストールします。
yum -y install httpd
サービス登録し、起動します。
systemctl enable httpd.service
systemctl start httpd.service
以下コマンドでサービスが起動していることを確認します。
systemctl status httpd.service
active (running)
が表示されればOKです。
最後に、テストページ(http://<IPアドレス>
)が表示されることを確認します。
参考:さくらクラウドのCentOS7でApacheとTomcatの環境構築した時のまとめ
Tomcatインストール
Apache Tomcat 9 のインストール
まず、Linux 上で Tomcat を動かすための専用ユーザとして tomcat を追加します。
useradd -s /sbin/nologin tomcat
次に、curl コマンドで Apache Tomcat 9 のダウンロードページから、tar.gz 形式の Apache Tomcat 9 本体をダウンロードします。2020年05月現在はバージョン 9.0.34 が最新版なので次のURLを指定します。最新のバージョンのURLを確認の上で実行してください。
cd ~
curl -O http://ftp.riken.jp/net/apache/tomcat/tomcat-9/v9.0.34/bin/apache-tomcat-9.0.34.tar.gz
ダウンロードした tar.gz ファイルを解凍して配置します。次のように tar コマンドで解凍し、/opt に配置します。また、解凍した Apache Tomcat の所有者を、先ほど作成した tomcat ユーザの所有とします。
tar -xzvf ~/apache-tomcat-9.0.34.tar.gz
mv ~/apache-tomcat-9.0.34 /opt
chown -R tomcat:tomcat /opt/apache-tomcat-9.0.34
サービスの作成と登録
CentOS 7 からはサービスを systemd が管理するようになりました。ここでは Apache Tomcat 9 をサービスとして登録します。まずは /etc/systemd/system/tomcat.service
を新たに作成して、次のように記述して保存します。これはサービスの定義ファイルです。
[Unit]
Description=Apache Tomcat 9
After=network.target
[Service]
User=tomcat
Group=tomcat
Type=oneshot
PIDFile=/opt/apache-tomcat-9.0.34/tomcat.pid
RemainAfterExit=yes
ExecStart=/opt/apache-tomcat-9.0.34/bin/startup.sh
ExecStop=/opt/apache-tomcat-9.0.34/bin/shutdown.sh
ExecReStart=/opt/apache-tomcat-9.0.34/bin/shutdown.sh;/opt/apache-tomcat-9.0.34/bin/startup.sh
[Install]
WantedBy=multi-user.target
次のコマンドで、作成した定義ファイルの権限を 755 に変更します。
chmod 755 /etc/systemd/system/tomcat.service
定義ファイルの作成が完了したら、次のコマンドでサービスを有効にします。
systemctl enable tomcat
以下が表示されればOKです。
Created symlink from /etc/systemd/system/multi-user.target.wants/tomcat.service to /etc/systemd/system/tomcat.service.
最後に、http://<IPアドレス>:8080
にアクセスし、Tomcatのテストページが表示されることを確認します。
このような画面が表示されればTomcatのインストール、およびサービス登録完了です。
参考:Apache Tomcat 9 を CentOS 7 にインストールする手順
ApacheとTomcatの連携
AJP (Apache JServ Protocol)を使ってApacheとTomcatを連携させます。AJP を利用するためには mod_proxy_ajp というモジュールを利用します。
参考:Apache httpd と Tomcat を連携させる方法
apacheの設定
proxy_moduleとproxy_ajp_moduleをLoad
私が apache をインストールしたとき、httpd.conf
はこの状態でした。
~中略~
#
# Dynamic Shared Object (DSO) Support
#
# To be able to use the functionality of a module which was built as a DSO you
# have to place corresponding `LoadModule' lines at this location so the
# directives contained in it are actually available _before_ they are used.
# Statically compiled modules (those listed by `httpd -l') do not need
# to be loaded here.
#
# Example:
# LoadModule foo_module modules/mod_foo.so
#
Include conf.modules.d/*.conf
~中略~
ここに記載されている「/etc/httpd/conf.modules.d」に行ってみると、以下の7個のconfファイルが確認できます。
01-cgi.conf
00-systemd.conf
00-proxy.conf
00-mpm.conf
00-lua.conf
00-dav.conf
00-base.conf
この中の「00-proxy.conf」を開くと以下の設定があります。
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
AJP を利用するためには mod_proxy_ajp が必要で、mod_proxy_ajp は mod_proxy モジュールに依存しています。そのため、これら2つのモジュールが必要です。00-proxy.confに上記設定があれば必要なmoduleがちゃんとLoadできています。
ちなみに、こられのモジュール「mod_proxy.so」と「mod_proxy_ajp.so」は、/usr/lib64/httpd/modules
ディレクトリに行くと確認できます。
(/etc/httpd配下に/usr/lib64/httpd/modulesへのショートカットがあります)
ProxyPassの設定
httpd.confの一番最後の行に以下のような設定があるため、/etc/httpd/conf.d
ディレクトリにconfファイルを作成すればその内容が読み込まれるようになっています。
~中略~
# Supplemental configuration
#
# Load config files in the "/etc/httpd/conf.d" directory, if any.
IncludeOptional conf.d/*.conf
以下の内容で/etc/httpd/conf.d
ディレクトリにproxy-ajp.conf
ファイルを作成します。
cat /etc/httpd/conf.d/proxy-ajp.conf
<Location /docs/>
ProxyPass ajp://127.0.0.1:8009/docs/
</Location>
portを8009にしているのは、TomcatがAJP 1.3の通信をport 8009で受け付けているからです。Tomcatの設定はこの後出てきます。
Apacheの設定はこれで完了です。
Tomcatの設定
server.xmlの修正
/opt/apache-tomcat-9.0.34/conf/server.xml
を修正します。修正前は以下のようになっています。
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
~中略~
<Connector protocol="AJP/1.3"
address="::1"
port="8009"
redirectPort="8443" />
インターネットからのすべてのアクセスをapache経由にするのであればport 8080での待ち受けは不要になるため、コメントアウトします。
修正後は以下の通りとなります。
<!--
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
-->
~中略~
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector protocol="AJP/1.3"
address="::1"
port="8009"
redirectPort="8443" />
これでapacheとTomcatを連携するための設定は完了です。
はまったこと1
上記作業が終わり、http://<IPアドレス>/docs
をブラウザに入力したのですが、503エラーがでてアクセスできませんでした。apacheのログ(error_log)を確認したところ、以下のログが出ていました。
[Sun Apr 26 14:33:39.753098 2020] [proxy:error] [pid 30144] (111)Connection refused: AH00957: AJP: attempt to connect to 127.0.0.1:8009 (127.0.0.1) failed
[Sun Apr 26 14:33:39.753136 2020] [proxy:error] [pid 30144] AH00959: ap_proxy_connect_backend disabling worker for (127.0.0.1) for 60s
[Sun Apr 26 14:33:39.753141 2020] [proxy_ajp:error] [pid 30144] [client 220.100.106.105:51467] AH00896: failed to make connection to backend: 127.0.0.1
最初はこのログの文言を元にいろいろググったのですが、どうやったら解決できるのかまったく見当がつきませんでした。
次にTomcatのログ(catalina.out)を確認したところ、Tomcatを起動する度に以下のログが出力されていることが確認できました。
18-Apr-2020 16:36:49.322 SEVERE [main] org.apache.catalina.util.LifecycleBase.handleSubClassException Failed to initialize component [Connector[AJP/1.3-8009]]
org.apache.catalina.LifecycleException: Protocol handler initialization failed
at org.apache.catalina.connector.Connector.initInternal(Connector.java:1041)
at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:136)
at org.apache.catalina.core.StandardService.initInternal(StandardService.java:533)
at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:136)
at org.apache.catalina.core.StandardServer.initInternal(StandardServer.java:1057)
at org.apache.catalina.util.LifecycleBase.init(LifecycleBase.java:136)
at org.apache.catalina.startup.Catalina.load(Catalina.java:584)
at org.apache.catalina.startup.Catalina.load(Catalina.java:607)
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.load(Bootstrap.java:303)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:473)
Caused by: java.net.SocketException: Protocol family unavailable
at sun.nio.ch.Net.bind0(Native Method)
at sun.nio.ch.Net.bind(Net.java:433)
at sun.nio.ch.Net.bind(Net.java:425)
at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:220)
at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:85)
at org.apache.tomcat.util.net.NioEndpoint.initServerSocket(NioEndpoint.java:228)
at org.apache.tomcat.util.net.NioEndpoint.bind(NioEndpoint.java:211)
at org.apache.tomcat.util.net.AbstractEndpoint.bindWithCleanup(AbstractEndpoint.java:1141)
at org.apache.tomcat.util.net.AbstractEndpoint.init(AbstractEndpoint.java:1154)
at org.apache.coyote.AbstractProtocol.init(AbstractProtocol.java:581)
at org.apache.catalina.connector.Connector.initInternal(Connector.java:1038)
... 13 more
「Protocol family unavailable」とか「Connector[AJP/1.3-8009]」でググったらこのサイトにたどり着きました。どうやらIPv6をサポートしていないサーバでIPv6のアドレス"::1"が指定されていると起きる事象らしい。
"::1"を"0.0.0.0"に書き換えることで解決しました。
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector protocol="AJP/1.3"
address="0.0.0.0"
port="8009"
redirectPort="8443" />
はまったこと2
上記「はまったこと1」を対処した後Tomcatを再起動したら、次はこんなログが出力されました。
org.apache.catalina.LifecycleException: Protocol handler start failed
at org.apache.catalina.connector.Connector.startInternal(Connector.java:1066)
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:1063)
... 12 more
調べてみると、server.xml の Connector の属性にsecretRequired
というものがあり、これがデフォルトtrueになっている。trueの場合、secretという属性で合言葉的なものを指定する必要があるらしい。(公式サイト)
このサイトでは、「secretRequired="false"」を明示的に指定して解決していたのでこれを真似してみたら解決しました。
<!-- Define an AJP 1.3 Connector on port 8009 -->
<Connector protocol="AJP/1.3"
address="0.0.0.0"
port="8009"
redirectPort="8443"
secretRequired="false" />
これでTomcatの設定は完了です。
warのデプロイ
最後に、自分のローカルPCで作成したwarファイルをレンタルサーバにデプロイします。warファイルを/opt/apache-tomcat-9.0.34/webapps
に格納すると、Tomcatが自動でデプロイしてくれます。
デプロイが完了したらアクセスしてHello Worldが出ることを確認して終わりです。
以上。