動機:ヘッドレスサーバー上でJakarta Servletをビルドしてテストしたい
昨年からmicrok8sをVM上のUbuntuサーバーにインストールして、勉強がてら動作確認をしています。
rc30-popo.hatenablog.com
microk8s上で動かすpodの中から周りのIPアドレス等がどう見えるか確認するための簡易テストアプリをpython + flaskで作り動かしていましたが、Javaベースでも動作確認したいと考えテスト環境を作りました。
JavaベースでなるべくシンプルにWebアプリを作る場合、Jakarta Servletをtomcatで動かすのがまずは最小構成かな?と思い、まずはその辺に良く転がっているServlet版のHello Worldをビルドしたいと思ったのですが、tomcat+Servletの入門ドキュメントの類はたいていEclipse環境を前提とした説明になっています。
ちゃんとしたWebアプリを本格的に開発したいわけではなく、kubernetes環境のpod・コンテナ内で動作するJavaプログラムが、クラスタ環境内外と(特にネットワーク的に)どのように繋がっているのか?またJavaプログラムから見て各種の環境条件(環境変数等)はどの様になっているか?をテキストベースでHTTPレスポンスやログに吐き出して確認したいだけであり、テスト環境兼テストアプリ開発環境がヘッドレスのUbuntuサーバーであるため、コマンドラインインターフェイスのみでServletをビルドする方法を調査しました。
ビルドに最低限必要なもの
本エントリー末尾に参考URLとした記載したサイトを含めて、色々な情報ソースをあたった結果、最小構成のServletのビルドにはJDKに加えてtomcatに付属するservlet-api.jarがあれば良いと理解しました。(jspを使う場合はjsp-api.jarも必要)
ビルド環境及びtomcat実行環境の選定
JDKのインストールとtomcatのインストールを手動で一からやるのは面倒くさいので、docker hubを確認したところtomcatの公式docker imageとして、Eclipse temurinベースのJDKとJREにtomcatを加えたイメージがあることが判りました。
これをdocker hubから引っ張ります。
$ docker pull tomcat:jdk11-temurin $ docker pull tomcat:jre11-temurin
(ちなみに上記ではJDK11版を引張っていますが、JDK8版や17版もあります。使いたいServletのスペック等に合わせて選定してください)
docker imageを入手したら、JDK版を起動し素性を確認してみます。
$ docker run -it tomcat:jdk11-temurin /bin/bash root@d2c71f35ad59:/usr/local/tomcat# root@d2c71f35ad59:/usr/local/tomcat# javac --version javac 11.0.14.1 root@d2c71f35ad59:/usr/local/tomcat# bin/version.sh Using CATALINA_BASE: /usr/local/tomcat Using CATALINA_HOME: /usr/local/tomcat Using CATALINA_TMPDIR: /usr/local/tomcat/temp Using JRE_HOME: /opt/java/openjdk Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar Using CATALINA_OPTS: NOTE: Picked up JDK_JAVA_OPTIONS: --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED Server version: Apache Tomcat/10.0.18 Server built: Mar 9 2022 14:30:15 UTC Server number: 10.0.18.0 OS Name: Linux OS Version: 4.15.0-176-generic Architecture: amd64 JVM Version: 11.0.14.1+1 JVM Vendor: Eclipse Adoptium
テストアプリソース
JDKとtomcatが入ったdocker imageが入手出来たのでテストアプリのソースを作成します。
htmlでresponseを出すのが面倒なのでtext/plainにしています。
クライアントのIPとクライアントのRequest headerを全部出力させています。
本ソースのもうひとつのポイントはtomcat10以降ではservlet関連のパッケージ名がjavax.servlet.*からjakarta.servlet.*に変更されている点です。
(正確にはtomcat10からjakarta EE 9対応となり、jakarta EE 9にてパッケージ名が変更)
またweb.xmlを使わずにソースコード上のアノテーション(@WebServlet)でHTTPアクセス時のパス名とServletの紐付けを行っています。
// tomcat servlet sample // import java.io.*; import jakarta.servlet.*; // tomcat10以降、javax.servletではなくjakarta.servletを使用する import jakarta.servlet.http.*; // 同上 import java.util.Enumeration; import jakarta.servlet.annotation.WebServlet; // web.xmlを使わずannotationを使用する場合に指定 @WebServlet("/") public class hello extends HttpServlet { protected void doGet(HttpServletRequest request,HttpServletResponse response) throws ServletException,IOException { response.setContentType("text/plain"); PrintWriter resout = response.getWriter(); resout.println("Servlet test program."); String clientIpAddr = request.getRemoteAddr(); resout.println(" Client IP = " + clientIpAddr); Enumeration headernames = request.getHeaderNames(); int headercount = 0; while(headernames.hasMoreElements()){ String headername = (String)headernames.nextElement(); resout.println("Servlet test program."); String clientIpAddr = request.getRemoteAddr(); resout.println(" Client IP = " + clientIpAddr); Enumeration headernames = request.getHeaderNames(); int headercount = 0; while(headernames.hasMoreElements()){ String headername = (String)headernames.nextElement(); Enumeration headervals = request.getHeaders(headername); while(headervals.hasMoreElements()){ String headerval = (String)headervals.nextElement(); resout.println(" Header("+headercount+")="+headername + ":" + headerval); headercount++; } } } }
テストアプリのコンパイルとWebアプリのパッケージ作成
コンパイル
ソースコードを格納したディレクトリに移動します。
ソースコードのファイル名をhello.javaとすると、下記の様にdocker runコマンドで tomcat:jdk11-temurinをコンテナ起動、javacにclasspathを下記の様に指定してソースをコンパイルします。
下記のdocker runコマンドではコンテナの/homeにホストのカレントディレクトリをマウントし、そこをワークディレクトリとしてコンパイル作業をさせています。
$ docker run -v $PWD:/home -w /home -it tomcat:jdk11-temurin javac -classpath /usr/local/tomcat/lib/servlet-api.jar hello.java
tomcat環境にテストアプリを組み込んで起動
コンテナイメージ tomcat:jre11-temurinに作成したアプリ配置ディレクトリを/usr/local/tomcat/webappsの下にマウント、更にコンテナのポート8080(tomcatがコンテナ内のポート8080で起動します)にホスト側の任意のポートをアサインしてコンテナを起動します。
先ほどのhelloディレクトリにcd後、docker runコマンドを下記の様に指定します。
$ docker run -v $PWD:/usr/local/tomcat/webapps/hello -it -p 8888:8080 tomcat:jre11-temurin Using CATALINA_BASE: /usr/local/tomcat Using CATALINA_HOME: /usr/local/tomcat Using CATALINA_TMPDIR: /usr/local/tomcat/temp Using JRE_HOME: /opt/java/openjdk Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar Using CATALINA_OPTS: NOTE: Picked up JDK_JAVA_OPTIONS: --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED 04-May-2022 15:00:50.372 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version name: Apache Tomcat/10.0.18 04-May-2022 15:00:50.428 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built: Mar 9 2022 14:30:15 UTC 04-May-2022 15:00:50.429 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version number: 10.0.18.0 04-May-2022 15:00:50.430 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name: Linux 04-May-2022 15:00:50.432 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version: 4.15.0-176-generic 04-May-2022 15:00:50.433 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture: amd64 04-May-2022 15:00:50.434 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home: /opt/java/openjdk 04-May-2022 15:00:50.435 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version: 11.0.14.1+1 04-May-2022 15:00:50.435 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor: Eclipse Adoptium 04-May-2022 15:00:50.437 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: /usr/local/tomcat 04-May-2022 15:00:50.438 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: /usr/local/tomcat 04-May-2022 15:00:50.613 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.lang=ALL-UNNAMED 04-May-2022 15:00:50.615 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.io=ALL-UNNAMED 04-May-2022 15:00:50.619 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.util=ALL-UNNAMED 04-May-2022 15:00:50.624 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.util.concurrent=ALL-UNNAMED 04-May-2022 15:00:50.626 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED 04-May-2022 15:00:50.627 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties 04-May-2022 15:00:50.629 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager 04-May-2022 15:00:50.630 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djdk.tls.ephemeralDHKeySize=2048 04-May-2022 15:00:50.632 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.protocol.handler.pkgs=org.apache.catalina.webresources 04-May-2022 15:00:50.635 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dorg.apache.catalina.security.SecurityListener.UMASK=0027 04-May-2022 15:00:50.637 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dignore.endorsed.dirs= 04-May-2022 15:00:50.638 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.base=/usr/local/tomcat 04-May-2022 15:00:50.640 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.home=/usr/local/tomcat 04-May-2022 15:00:50.641 INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/temp 04-May-2022 15:00:50.831 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded Apache Tomcat Native library [1.2.31] using APR version [1.6.5]. 04-May-2022 15:00:50.835 INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true], UDS [true]. 04-May-2022 15:00:50.943 INFO [main] org.apache.catalina.core.AprLifecycleListener.initializeSSL OpenSSL successfully initialized [OpenSSL 1.1.1f 31 Mar 2020] 04-May-2022 15:00:52.241 INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"] 04-May-2022 15:00:52.336 INFO [main] org.apache.catalina.startup.Catalina.load Server initialization in [2930] milliseconds 04-May-2022 15:00:52.562 INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina] 04-May-2022 15:00:52.564 INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet engine: [Apache Tomcat/10.0.18] 04-May-2022 15:00:52.596 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/hello] 04-May-2022 15:00:54.368 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/hello] has finished in [1,772] ms 04-May-2022 15:00:54.388 INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"] 04-May-2022 15:00:54.470 INFO [main] org.apache.catalina.startup.Catalina.start Server startup in [2132] milliseconds
下記のメッセージでtomcatがwebappディレクトリ下に配置したhelloアプリを認識したことが判ります。
04-May-2022 15:00:52.596 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/hello] 04-May-2022 15:00:54.368 INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/hello] has finished in [1,772] ms
同じホスト上からcurlコマンドでtomcatにアクセスした結果です。
$ curl http://localhost:8888/hello/ Servlet test program. Client IP = 172.17.0.1 Header(0)=host:localhost:8888 Header(1)=user-agent:curl/7.61.0 Header(2)=accept:*/*
他のマシンからdockerを起動しているVMのIPアドレス(以下の例では192.168.11.252)を指定してアクセスした結果。
$ curl http://192.168.11.252:8888/hello/ Servlet test program. Client IP = 192.168.11.254 Header(0)=host:192.168.11.252:8888 Header(1)=user-agent:curl/7.65.3 Header(2)=accept:*/*