IK.AM

@making's tech note


LegacyなJavaアプリをTanzu Application Platformにデプロイするまでのメモ

🗃 {Dev/CaaS/Kubernetes/TAP}
🏷 Kubernetes 🏷 Cartographer 🏷 Java 🏷 Tanzu 🏷 TAP 🏷 Service Binding 
🗓 Updated at 2023-12-20T04:51:30Z  🗓 Created at 2022-06-02T04:06:43Z   🌎 English Page

⚠️ 本記事の内容はVMwareによってサポートされていません。 記事の内容で生じた問題については自己責任で対応し、 VMwareサポート窓口には問い合わせないでください

以下の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のインストールはこちらを参照してください。

目次

ソースコードのダウンロード

レガシーなサンプルアプリはこちらからダウンロード

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.propertieswebapsvr.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 <<EOF > 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 を使用して、任意のスクリプトで起動できるようにします。

cat <<EOF > Procfile
web: bash /workspace/run.sh
EOF

起動スクリプトはrun.shに記述します。

データベース(PostgreSQL)への接続情報は Service Binding で渡されるものとします。 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
<Context>
  <Resource
     name="jdbc/terasolunaTourDataSource"
     type="javax.sql.DataSource"
     driverClassName="org.postgresql.Driver"
     password="${DATABASE_PASSWORD}"
     maxIdle="4"
     maxWait="5000"
     username="${DATABASE_USERNAME}"
     url="${DATABASE_URL}"
     maxActive="20"/>
</Context>
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 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 用のディレクトリ・ファイルを作成します。

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

✒️️ Edit  ⏰ History  🗑 Delete