---
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)を参照してください。

**目次**
<!-- toc -->

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

レガシーなサンプルアプリは[こちら](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 <<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`](https://paketo.io/docs/howto/configuration/#procfiles) を使用して、任意のスクリプトで起動できるようにします。

```
cat <<EOF > 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
<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`](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 にアクセスするとトップ画面に遷移します。

<img width="1024" alt="image" src="https://user-images.githubusercontent.com/106908/171448958-57858932-49aa-4dc6-a708-7d71727c8f92.png">

会員ID `00000001`, パスワード `password` でログインできます。

<img width="1024" alt="image" src="https://user-images.githubusercontent.com/106908/171449055-017bb7c5-8512-4587-80f1-11e5121bf7a9.png">

アプリと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にアクセスすれば、画面が表示されます。

<img width="1024" alt="image" src="https://user-images.githubusercontent.com/106908/171548275-93d36ac1-198d-4293-a92d-2dbe557a9d1d.png">

ログインもできます。

<img width="1024" alt="image" src="https://user-images.githubusercontent.com/106908/171548341-131a0166-fe37-4853-b43e-b046f76e048b.png">


### アプリの削除

```
tanzu apps workload delete -n demo toursample -y
tanzu service class-claim delete tour-db -n demo
```
