0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

DockerでApache+Tomcat構成を作る!

Posted at

概要

Dockerを使って、ApacheとTomcatを分離したコンテナ構成を作成し、JavaのWebアプリ(WARファイル)をTomcatにデプロイして、Apache経由で動かすまでの手順をまとめました。

メリット

オンプレ構成に沿うと、Apacheが入っている所謂WebサーバーとTomcatが入っているAPサーバーで分けることがあると思います。
その点コンテナにすれば1台で済むのが一つメリットです。
その他メリットを挙げるとこのようになります。

項目 サーバーを2台に分けた構成 Dockerコンテナ構成
コスト サーバーを2台分 サーバーは1台
環境構築の手間 各サーバーに手動インストール Dockerfile と docker-compose.yml で自動構築
通信 サーバー間のセキュリティ担保 同一ホスト内で localhost 通信
スケーラビリティ インスタンスごとの個別スケール サービス単位で水平スケールしやすい
再現性 手動構築でブレやすい どこでも同じコンテナが動作
障害切り分け ネットワークやサーバー自体の問題も絡む コンテナ単位でログ・原因が明確
メンテナンス OSごとアップデート・管理 コンテナ単位で最小構成&短時間で更新

環境

・OS:Amazon Linux 2 または RHEL系(今回はパブリックIPを持つEC2インスタンス)
・Docker / Docker Compose v2
・Java Webアプリを .war 形式でデプロイ
・サーブレット + JSP(最低限のサンプル)

ディレクトリ構成

apache-tomcat-docker/
├── apache/
│     ├── Dockerfile
│     └── httpd.conf
├── tomcat/
│     ├── Dockerfile
│     └── webapps/
│           └── app.war(←ここに自作WAR)
├── docker-compose.yml

手順

まずはディレクトリ作成を行います。

mkdir apache-tomcat-docker
cd apache-tomcat-docker
mkdir apache tomcat
mkdir -p tomcat/webapps

続いて必要なファイルを作成していきます。

vi apache/Dockerfile」で以下ファイルを作成。

🐳 Apache コンテナ

FROM httpd:2.4
COPY httpd.conf /usr/local/apache2/conf/httpd.conf

vi tomcat/Dockerfile」で以下ファイルを作成。

🐱 Tomcat コンテナ

FROM tomcat:9.0
COPY webapps/ /usr/local/tomcat/webapps/

Dockerfileでそれぞれのコンテナで何を使うかを定義しています。
Apacheは自身のhttpd.conf(Dockerfileが置いてある場所と同じ場所にあるファイル)をコンテナ内にコピーして使用します。

Tomcatは同じく自身のwarファイル(今回でいうapp.war)をコンテナ内にコピーして使用します。

次に、それぞれのコンテナで使用するポートやネットワークを定義した
docker-compose.ymlを作成します。

⚙️ docker-compose.yml

services:
  apache:
    build: ./apache
    ports:
      - "80:80"
    depends_on:
      - tomcat
    networks:
      - webnet

  tomcat:
    build: ./tomcat
    ports:
      - "8080:8080"
    networks:
      - webnet
networks:
  webnet:

以下の記載は、/apache配下のDockerfileを指定してコンテナを作成するという意味です。

    build: ./apache

以下の記載は、tomcat用コンテナが作成されるのを待ってから作成するという意味です。

    depends_on:
      - tomcat

以下はネットワークにwebnetを指定しています。

    networks:
      - webnet

前述で指定しているネットワークはここで定義しており、apacheとtomcatのそれぞれのコンテナにこのネットワークを指定することで、ローカル通信を可能にしています。

networks:
  webnet:

次に、Apache用のDockerfileに書いた、httpd.confを準備します。

ServerRoot "/usr/local/apache2"
Listen 80
ServerName localhost

# --- Load 必須モジュール ---
LoadModule mpm_event_module modules/mod_mpm_event.so
LoadModule mime_module modules/mod_mime.so
LoadModule dir_module modules/mod_dir.so
LoadModule log_config_module modules/mod_log_config.so
LoadModule unixd_module modules/mod_unixd.so
LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_http_module modules/mod_proxy_http.so
LoadModule authn_core_module modules/mod_authn_core.so
LoadModule authz_core_module modules/mod_authz_core.so

# --- Proxy設定 ---
ProxyPreserveHost On
ProxyPass /app http://tomcat:8080/app
ProxyPassReverse /app http://tomcat:8080/app

DocumentRoot "/usr/local/apache2/htdocs"
<Directory "/usr/local/apache2/htdocs">
    AllowOverride None
    Require all granted
</Directory>

Dockerfile内で、「/usr/local/apache2」にファイルコピーをしているので、
ServerRootには同様のパスを記載します。

また、docker-compose.ymlにてポート80を指定しているので、httpd.confのListenにも80を指定します。

モジュールは最低限必要なものを入れています。

プロキシ設定に関しては以下です。
ProxyPreserveHost On=元のHostヘッダをそのまま保持し、バックエンド(Tomcat)に渡す
ProxyPass /app http://tomcat:8080/app = Apacheに来た /app パスへのリクエストを、Tomcat の http://tomcat:8080/app に転送する設定
ProxyPassReverse /app http://tomcat:8080/app = Tomcatから返された レスポンスの中に含まれるリダイレクトURLなどを、Apache経由のURLに書き換える ための設定

その下の記述のDocumentRootは、外から例 http://サーバーIP/ でアクセスされたときにApacheが参照する場所
AllowOverride Noneは、.htaccess ファイルによる設定の「上書き」を許可しない設定
Require all grantedは、全てのアクセス元に対して許可する設定(接続元IPを絞ることもできる)

あとはwarファイルの作成です。

☕ WARファイルの作成

内容は以下です。

app/
├── index.jsp
└── WEB-INF/
      ├── web.xml
      └── classes/
          └── HelloServlet.class

warファイルに関しては作成したことがなかったので生成AIに作ってもらってます。
中身はあまり分かっていないので解説は割愛します。

まずはディレクトリ作成をします。

mkdir -p app/WEB-INF/classes
cd app

続いてファイル作成をしていきます。

vi index.jsp

<!-- index.jsp -->
<html>
  <body>
    <h1>Hello from JSP!</h1>
    <a href="hello">Servletへ</a>
  </body>
</html>

vi WEB-INF/web.xml

<!-- WEB-INF/web.xml -->
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" version="3.1">
  <servlet>
    <servlet-name>HelloServlet</servlet-name>
    <servlet-class>HelloServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>HelloServlet</servlet-name>
    <url-pattern>/hello</url-pattern>
  </servlet-mapping>
</web-app>

vi HelloServlet.java

// HelloServlet.java
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;

public class HelloServlet extends HttpServlet {
  protected void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
    response.setContentType("text/html");
    PrintWriter out = response.getWriter();
    out.println("<h1>Hello from Servlet!</h1>");
  }
}

コンパイルをするためにjavacコマンドを使用するため、JDKバイナリをインストールします。

cd /opt
curl -LO https://github.com/adoptium/temurin17-binaries/releases/download/jdk-17.0.10+7/OpenJDK17U-jdk_x64_linux_hotspot_17.0.10_7.tar.gz

tar -xvf OpenJDK17U-jdk_x64_linux_hotspot_17.0.10_7.tar.gz
mv jdk-17.0.10+7 temurin-17

パスを通すため以下ファイルを作成します。

vi /etc/profile.d/java.sh

tee /etc/profile.d/java.sh > /dev/null <<'EOF'
export JAVA_HOME=/opt/temurin-17
export PATH=$JAVA_HOME/bin:$PATH
EOF

source /etc/profile.d/java.sh

「source /etc/profile.d/java.sh」を打って反映

それぞれコマンドが通ることを確認

java -version
javac -version

cd

コンパイルを行います。

javac -classpath ~/lib/javax.servlet-api-4.0.1.jar HelloServlet.java
mv HelloServlet.class WEB-INF/classes/

これでwarファイルを作成する準備が整ったので、warファイルを作成します。

cd ..
jar cvf app.war -C app .
## warファイル移動
mv app.war tomcat/webapps/

Dockerを使用するために、必要なパッケージをインストールします。

# Dockerインストール
dnf install -y docker
systemctl enable docker
systemctl start docker

# docker-composeインストール
mkdir -p $HOME/.docker/cli-plugins
curl -SL https://github.com/docker/compose/releases/latest/download/docker-compose-linux-x86_64 -o $HOME/.docker/cli-plugins/docker-compose
chmod +x $HOME/.docker/cli-plugins/docker-compose

docker-composeが使用できることを確認。

docker compose version

コンテナイメージをビルドして起動します。

docker compose up --build -d

## 確認
docker compose ps

以下のように2つコンテナが起動していれば成功です。

NAME                            IMAGE                         COMMAND              SERVICE   CREATED         STATUS         PORTS
apache-tomcat-docker-apache-1   apache-tomcat-docker-apache   "httpd-foreground"   apache    4 seconds ago   Up 3 seconds   0.0.0.0:80->80/tcp, [::]:80->80/tcp
apache-tomcat-docker-tomcat-1   apache-tomcat-docker-tomcat   "catalina.sh run"    tomcat    4 seconds ago   Up 4 seconds   0.0.0.0:8080->8080/tcp, [::]:8080->8080/tcp

起動に失敗する場合は、
「docker compose logs <サービス>」で原因が見れます。

以下でTomcatへの疎通確認を行います。

## Apache⇒Tomcat
curl http://<EC2のパブリックIP>/app/

## Apache⇒Tomcatのサーブレット
curl http://<EC2のパブリックIP>/app/hello

Tomcatへの疎通は以下が返ってこれば成功です。

<!-- index.jsp -->
<html>
  <body>
    <h1>Hello from JSP!</h1>
    <a href="hello">Servletへ</a>
  </body>
</html>

サーブレットへの疎通は以下が返ってこれば成功です。

<h1>Hello from Servlet!</h1>

まとめ

warファイルは生成AIに言われるがまま設定しているので中身はほとんど分かっていませんが、
それでもApache⇒Tomcatへの疎通確認は行うことが出来ました。

コンテナを活用した構築はどんどんチャレンジしていきたいと思います。
この記事が誰かの助けになれば幸いです。

以上!!

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?