tomcat10用のServletをコマンドライン環境でコンパイル〜起動
動機:ヘッドレスサーバー上で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:*/*
e-mansion 1Gbps化
我が家のインターネット接続が100Mbpsから1Gbpsにアップグレードされました
うちは約10年前に購入した分譲マンションです。
入居時からいわゆる「マンションインターネット」というマンション全戸への共通配信形式のインターネットサービスです。
プロバイダーはこのタイプのサービスでは大手のつなぐネットコミュニケーションズのe-mansionです。
入居時には各戸ベストエフォートで最大100Mbpsでしたが、今週アップグレード工事が行われて1Gbpsになりました。
仕様とか経緯とか
- 帯域
マンションまでの基幹回線は1本でそれを各戸で分け合うのでベストエフォートです。
これまで各戸へは最大100Mbpsで配送されていました。
これが今回1Gbpsにアップグレードされました。
マンションの基幹部分への帯域が合計どの程度で割り当てられているのかは明示されていません。
10年前の入居直後はまだ空き戸が多かった事もあって、スピードテスト系サイトで測定すると割と安定して下り90Mbps以上出ていました。
ただ、入居から2年くらい経過してほぼ入居率が100%に近づいたあたりで、かなり低速になった時期があって、酷い時は下り数Mbpsしか出なかった時期があるですが、苦情が出たのか?自発的に調整したのか、ある時期からだいぶ改善し最近は60Mbpsくらいは安定して出る様になっていました。
おそらくマンション毎の戸数や通信の実績みながら帯域調整かけているのでしょうが、今でも数Mbpsしか出ないとかネットで呟かれているケースがあるのでマンションによって調整に差がある気がします。
ギガビットの時代に60Mbpsって遅いですが、amazon prime video観たり、テレワークでクラウド上の仮想PC使ったり、zoomやteamsでビデオ会議するのには特に困る事は無し、iPhoneやPCのOSアップデートのDL等はそこそこ時間掛かっていました。
各戸へはDHCPでプライベートIP(当然IPv4)が配布されます。
各戸 1IPではなく、最低でも部屋数分のIPは用意される様ですが、正確に調べたことはありません。
我が家だとリビング近くにある集中配電盤の横にイーサーハブが設置されていて、基幹から張られたイーサーをそこで各部屋に振り分けています。
部屋毎のイーサーのポートにPCやルーターを繋ぐと、それぞれにDHCPでプライベートIPが割り振られます。
部屋のルーターでWAN側アドレスの割り当てを見るとNetmask: 255.255.192.0となっているので/18となっています。
とするとサブネットとしては16K個あるのですが、これ1戸毎にサブネット分割されているのだろうか?ちょっと謎。
- アップグレード
正確に覚えていないのですがアップグレードの話を最初に見たのは多分半年くらい前。
マンションの管理組合の理事会の議事録にプロバイダーから回線強化の提案があり、工事が必要だが住人の利便性が上がるので受け入れたと書かれていて、この時点では各戸への配送帯域上限が上がるとは書かれておらず、基幹増強くらいかな?と思っていました。
で、2週間くらい前に工事日にインターネットが使えない時間がありますという事と、ベストエフォートながら各戸 1Gbpsに増強されると書かれていてアップグレードの内容が判明しました。
いまさらながら気づいたのですが、うちのマンションは基幹から各戸への配送はこのタイプのインターネットサービスで良くあるVDSLではなく、イーサーだったのですね。
インターネットの基幹配線を引き込む宅内の集中配電盤には普通のイーサーハブしか設置されておらず、VDSL終端用のモデムが見当たらなかったのでちょっと疑問には思っていました。これがVDSLだと容易には1G化できなかったはずで、ラッキーでした。
なお入居時に設置されていた宅内のイーサー分配用のハブはコレガのいかにも安そうな100Mbps対応の奴で、これを各家庭で1Gbpsのハブに買い換えて下さいとのこと。
我が家は3年くらい前にコレガのハブがぶっ壊れて、その際に自分でギガビット対応ハブに交換したので今回それが活きました。
- アップグレード結果
我が家で一番新しいWi-Fiルーターを設置しているリビングで、有線接続で測定した結果は下り720Mbps、上りが300Mbps強でした。
ただしWi-Fiだと120Mbpsが限界だったので、フル活用するには最新Wi-Fiルーターに買い替えないとダメですね。
WAN側100Mbpsだとあまりお高いルーターを買うモチベが無かったのですが、WAN側が増強されたのが判ると俄然、良いものが欲しくなってきます。