--- title: LegacyなJavaアプリをTanzu Application Platformにデプロイするまでのメモ tags: ["Kubernetes", "Cartographer", "Java", "Tanzu", "TAP", "Service Binding"] categories: ["Dev", "CaaS", "Kubernetes", "TAP"] date: 2022-06-02T04:06:43Z updated: 2023-12-20T04:51:30Z --- 以下のStackを使用したレガシーなJavaアプリをTanzu Application Platform (TAP) 1.7にデプロイしてみます。 * Apache Struts 1.2.x * iBatis 2.3.x * Spring Framework 3.2.x * Apache Ant TAPのインストールは[こちら](/entries/778)を参照してください。 **目次** ### ソースコードのダウンロード レガシーなサンプルアプリは[こちら](https://ftp.iij.ad.jp/pub/osdn.jp/terasoluna/66350/terasoluna-server4jweb-toursample_2.0.6.2.zip)からダウンロード ``` wget https://ftp.iij.ad.jp/pub/osdn.jp/terasoluna/66350/terasoluna-server4jweb-toursample_2.0.6.2.zip unzip terasoluna-server4jweb-toursample_2.0.6.2.zip cd terasoluna-server4jweb-toursample_2.0.6.2 unzip toursample-javaweb.zip cd toursample-javaweb ``` ### サンプルアプリのビルド MavenやGradleを使用している場合は、Cloud Native Buildpacksを使用してTAP上でソースコードのビルドから行えますが、 Antはサポートされていないので、事前にwarをビルドして、warをTAPにデプロイするようにします。 TAPで利用できる再旧バージョンであるJava 8でビルドします。antも久々にインストールします。 ``` brew install ant ``` `ant/build.xml`を見るとTomcatがビルドに必要なため、Tomcatをダウンロードします。 サンプルでTomcat 6が使用されていましたが、ここではTomcat 9を使用します。 Tomcat 10以降ではServlet APIのpackage名が変わっているので使用できません。 ``` wget https://dlcdn.apache.org/tomcat/tomcat-9/v9.0.84/bin/apache-tomcat-9.0.84.zip unzip apache-tomcat-9.0.84.zip -d /opt ``` `ant/build.properties`の`webapsvr.home`をダウンロードしたディレクトリに変えます。またwarのファイル名を`ROOT.war`になるように変更します。 ``` cat <<'EOF' > ant/build.properties # Keep source.dir=./sources web.inf.dir=./webapps/WEB-INF lib.dir=./webapps/WEB-INF/lib zip.dir=./terasoluna/src ## Change according to your tomcat env webapsvr.home=/opt/apache-tomcat-9.0.84 webapsvr.lib.dir=${webapsvr.home}/lib deploy.dir=${webapsvr.home}/webapps ## Change context.name=ROOT EOF ``` ログの出力先を標準出力にするために`sources/log4j.properties`を次のように変更します。 ``` cat < sources/log4j.properties # Keep log4j.rootCategory=INFO, consoleLog, logfile log4j.category.jp.terasoluna=INFO log4j.category.org.springframework=INFO log4j.category.org.apache.struts=INFO log4j.appender.consoleLog=org.apache.log4j.ConsoleAppender log4j.appender.consoleLog.Target = System.out log4j.appender.consoleLog.layout = org.apache.log4j.PatternLayout log4j.appender.consoleLog.layout.ConversionPattern=[%d{yyyy/MM/dd HH:mm:ss}][%p][%C{1}] %m%n # Remove # log4j.appender.logfile=... EOF ``` これでantでビルドします。Java 8を使用してください。 ``` ant -f ant/build.xml ``` ビルドできました。 ``` Buildfile: /Users/toshiaki/blog/legacy/terasoluna-server4jweb-toursample_2.0.6.2/toursample-javaweb/ant/build.xml clean: [delete] Deleting: /opt/apache-tomcat-9.0.84/webapps/ROOT.war compile: [javac] /Users/toshiaki/blog/legacy/terasoluna-server4jweb-toursample_2.0.6.2/toursample-javaweb/ant/build.xml:69: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds [javac] Compiling 97 source files to /Users/toshiaki/blog/legacy/terasoluna-server4jweb-toursample_2.0.6.2/toursample-javaweb/webapps/WEB-INF/classes [copy] Copying 10 files to /Users/toshiaki/blog/legacy/terasoluna-server4jweb-toursample_2.0.6.2/toursample-javaweb/webapps/WEB-INF/classes native2ascii: [native2ascii] Converting 16 files from /Users/toshiaki/blog/legacy/terasoluna-server4jweb-toursample_2.0.6.2/toursample-javaweb/sources to /Users/toshiaki/blog/legacy/terasoluna-server4jweb-toursample_2.0.6.2/toursample-javaweb/webapps/WEB-INF/classes build: deploy: [jar] Building jar: /Users/toshiaki/blog/legacy/terasoluna-server4jweb-toursample_2.0.6.2/toursample-javaweb/ROOT.war [copy] Copying 1 file to /opt/apache-tomcat-9.0.84/webapps [delete] Deleting: /Users/toshiaki/blog/legacy/terasoluna-server4jweb-toursample_2.0.6.2/toursample-javaweb/ROOT.war BUILD SUCCESSFUL Total time: 1 second ``` ### Flywayの導入 DBマイグレーションを手動で行いたくないのでFlywayを導入します。 と言ってもソースコードには手を入れたくないので、アプリの起動前にコマンドラインで実行するようにします。 ``` wget https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/5.2.4/flyway-commandline-5.2.4-linux-x64.tar.gz tar xzvf flyway-commandline-5.2.4-linux-x64.tar.gz mv flyway-5.2.4/ flyway ``` SQLの文字コードがSHIFT_JISX0213なのでUTF-8に変換しちゃいます。変換後のファイル名はFlywayの命名規則に合わせます。 ``` iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/create_all_sequences.sql > flyway/sql/V1__create_all_sequences.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/create_all_tables.sql > flyway/sql/V2__create_all_tables.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/create_all_index.sql > flyway/sql/V3__create_all_index.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/insert_departure.sql > flyway/sql/V4__insert_departure.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/insert_arrival.sql > flyway/sql/V5__insert_arrival.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/insert_accommodation.sql > flyway/sql/V6__insert_accommodation.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/insert_age.sql > flyway/sql/V7__insert_age.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/insert_employee.sql > flyway/sql/V8__insert_employee.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/insert_customer.sql > flyway/sql/V9__insert_customer.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/insert_tourinfo.sql > flyway/sql/V10__insert_tourinfo.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/insert_tourcon.sql > flyway/sql/V11__insert_tourcon.sql iconv -f SHIFT_JISX0213 -t UTF-8 sqls/postgres/insert_reserve.sql > flyway/sql/V12__insert_reserve.sql ``` ### Procfileの作成 Tomcat起動前にFlywayの実行や、`META-INF/context.xml`に設定されているJNDIの設定を書き換えたいので、 [`Procfile`](https://paketo.io/docs/howto/configuration/#procfiles) を使用して、任意のスクリプトで起動できるようにします。 ``` cat < Procfile web: bash /workspace/run.sh EOF ``` 起動スクリプトは`run.sh`に記述します。 データベース(PostgreSQL)への接続情報は [Service Binding](https://github.com/servicebinding/spec) で渡されるものとします。 [Well-known Secret Entries](https://github.com/servicebinding/spec#well-known-secret-entries) のフォーマットが使用されていることを前提にします。 ``` cat <<'EOD' > run.sh #!/bin/bash set -e BINDING_NAME=tour-db DATABASE_HOST=$(cat ${SERVICE_BINDING_ROOT}/${BINDING_NAME}/host) DATABASE_PORT=$(cat ${SERVICE_BINDING_ROOT}/${BINDING_NAME}/port) DATABASE_USERNAME=$(cat ${SERVICE_BINDING_ROOT}/${BINDING_NAME}/username) DATABASE_PASSWORD=$(cat ${SERVICE_BINDING_ROOT}/${BINDING_NAME}/password) DATABASE_NAME=$(cat ${SERVICE_BINDING_ROOT}/${BINDING_NAME}/database) DATABASE_URL="jdbc:postgresql://${DATABASE_HOST}:${DATABASE_PORT}/${DATABASE_NAME}" cat << EOF > ./META-INF/context.xml EOF ${JAVA_HOME}/bin/java \ -cp `ls WEB-INF/lib/postgresql-*.jar`:./flyway/lib/community/* \ org.flywaydb.commandline.Main migrate \ -url=${DATABASE_URL} \ -user=${DATABASE_USERNAME} \ -password=${DATABASE_PASSWORD} export CATALINA_OUT=/dev/stdout export CATALINA_PID=/tmp/catalina.pid bash ${CATALINA_HOME}/bin/catalina.sh start JAVA_PID=$(cat $CATALINA_PID) stop_java_app() { kill -SIGTERM $JAVA_PID } trap stop_java_app SIGINT while [ -e /proc/$JAVA_PID ] do sleep 1 done EOD chmod +x run.sh ``` ### warの更新 ビルドしたwarにflywayやProcfileなどを詰め込みます。またソースコードにPostgreSQLのJDBCドライバーが含まれていないので、ドライバーもダウンロードして詰め込みます。 ``` mkdir -P WEB-INF/lib wget https://repo1.maven.org/maven2/org/postgresql/postgresql/42.3.6/postgresql-42.3.6.jar -P WEB-INF/lib cp /opt/apache-tomcat-9.0.84/webapps/ROOT.war ./ ``` 次のコマンドで `ROOT.war` にファイルを追加します。 ``` jar -uvf ROOT.war flyway/lib flyway/jars flyway/sql WEB-INF Procfile run.sh ``` ### pack CLIでコンテナイメージビルド TAPにデプロイする前に [`pack`](https://github.com/buildpacks/pack) CLIでコンテナイメージをビルドしてローカルで起動確認します。 ``` pack build toursample --builder paketobuildpacks/builder-jammy-base:latest --path ./ROOT.war -e BP_JVM_VERSION=8 ``` ### ラップトップ上のDockerで起動 ビルドしたイメージをDockerで起動してみます。 その前にPostgreSQLを起動します。 ``` docker run --rm \ -p 5432:5432 \ -e POSTGRES_DB=tour \ -e POSTGRES_USER=tour \ -e POSTGRES_PASSWORD=tour \ bitnami/postgresql:14 ``` [Service Binding](https://paketo.io/docs/howto/configuration/#bindings) 用のディレクトリ・ファイルを作成します。 ``` mkdir -p bindings/tour-db echo postgresql > bindings/tour-db/type echo host.docker.internal > bindings/tour-db/host echo 5432 > bindings/tour-db/port echo tour > bindings/tour-db/username echo tour > bindings/tour-db/password echo tour > bindings/tour-db/database ``` アプリを起動します。 ``` docker run --rm \ --name toursample \ -p 8080:8080 \ -v ${PWD}/bindings:/bindings \ -e SERVICE_BINDING_ROOT=/bindings \ -m 768m \ toursample ``` http://localhost:8080 にアクセスするとトップ画面に遷移します。 image 会員ID `00000001`, パスワード `password` でログインできます。 image アプリとPostgreSQLをCtrl+Cで終了してください。 ### TAPにデプロイ いよいよTAPにデプロイします。 その前にPostgreSQLを用意します。ここではBitnami Serviceを使用します。 次のコマンドでPostgreSQLのインスタンスを作成します。 ``` tanzu service class-claim create tour-db --class postgresql-unmanaged --parameter storageGB=1 -n demo ``` 次のような状態になるまで待ちます。 ``` $ tanzu services class-claims get tour-db --namespace demo Name: tour-db Namespace: demo Claim Reference: services.apps.tanzu.vmware.com/v1alpha1:ClassClaim:tour-db Class Reference: Name: postgresql-unmanaged Parameters: storageGB: 1 Status: Ready: True Claimed Resource: Name: e18f1e5a-6533-449c-b0ea-5cb4317f4c07 Namespace: demo Group: Version: v1 Kind: Secret ``` いよいよデプロイします。 ``` tanzu apps workload apply toursample \ --app toursample \ --local-path ./ROOT.war \ --type web \ --build-env BP_JVM_VERSION=8 \ --annotation autoscaling.knative.dev/minScale=1 \ --service-ref tour-db=services.apps.tanzu.vmware.com/v1alpha1:ClassClaim:tour-db \ -n demo \ -y ``` > ℹ️ testingを含むSupply Chainを使用している場合は、`--label apps.tanzu.vmware.com/has-tests=true` を追加してください。 > ℹ️ Local Source Proxyを使用しない場合は、 `--source-image` オプションで、ソースコードをアップロードするコンテナレジストリのレポジトリ名を指定してください。 進捗は次のコマンドで確認できます。 ``` tanzu apps workload tail -n demo toursample --since 5m ``` しばらくするとデプロイが完了します。 ``` $ tanzu apps workload get -n demo toursample 📡 Overview name: toursample type: web namespace: demo 💾 Source type: source image image: 532912407632.dkr.ecr.ap-northeast-1.amazonaws.com/tap-lsp:demo-toursample@sha256:52e10a5ab3777384d104402dec839bc9017e53b8ae961fb0aa2a867e637a975b 📦 Supply Chain name: source-test-scan-to-url NAME READY HEALTHY UPDATED RESOURCE source-provider True True 7m15s imagerepositories.source.apps.tanzu.vmware.com/toursample source-tester True True 6m37s runnables.carto.run/toursample image-provider True True 5m45s images.kpack.io/toursample image-scanner True True 5m14s imagescans.scanning.apps.tanzu.vmware.com/toursample config-provider True True 5m11s podintents.conventions.carto.run/toursample app-config True True 5m11s configmaps/toursample service-bindings True True 5m11s configmaps/toursample-with-claims api-descriptors True True 5m11s configmaps/toursample-with-api-descriptors config-writer True True 5m3s taskruns.tekton.dev/toursample-config-writer-4t7rp 🚚 Delivery name: delivery-basic NAME READY HEALTHY UPDATED RESOURCE source-provider True True 4m11s imagerepositories.source.apps.tanzu.vmware.com/toursample-delivery deployer True True 3m17s apps.kappctrl.k14s.io/toursample 💬 Messages No messages found. 🔁 Services CLAIM NAME KIND API VERSION tour-db tour-db ClassClaim services.apps.tanzu.vmware.com/v1alpha1 🛶 Pods NAME READY STATUS RESTARTS AGE scan-toursample-5w5tv-pod 0/6 Completed 0 5m45s toursample-00001-deployment-5d955b64b6-bdfn9 2/2 Running 0 4m9s toursample-build-1-build-pod 0/1 Completed 0 6m37s toursample-config-writer-4t7rp-pod 0/1 Completed 0 5m10s toursample-jlsmz-test-pod 0/1 Completed 0 6m59s 🚢 Knative Services NAME READY URL toursample Ready https://toursample.demo.tap.57.180.147.144.sslip.io To see logs: "tanzu apps workload tail toursample --namespace demo --timestamp --since 1h" ``` URLにアクセスすれば、画面が表示されます。 image ログインもできます。 image ### アプリの削除 ``` tanzu apps workload delete -n demo toursample -y tanzu service class-claim delete tour-db -n demo ```