複数アプリケーション(WAR)を配置したLibertyの構成方法について整理する

複数のWARファイルで構成されている業務システムをLiberty上で運用しようとしたとき，どのような単位で構成を分けるかを考える必要があります。Libertyの場合，いくつか選択肢があります。この記事では，それぞれのメリット・デメリットについてまとめています。コンテナではない通常のOS環境が対象です。

# Libertyの基本的な構成

一つのOS環境に，一つのLibertyランタイムが導入され，一つのサーバー，一つのWARアプリケーションが構成されている環境を概略すると以下のようになります。

```
wlp
└── usr
    └── servers
        └── defaultServer
            ├── apps
            │   └── myapp.war
            └── server.xml
```

このOS環境で，複数のWARを実行しようとした場合，選択肢は三つあります。

## A. server.xmlに複数の\<application>を定義する

appsディレクトリの下においたWARファイルは，server.xmlに定義を記述しますが，この定義は複数記述することができます。

```xml:server.xml
<application location="myapp1.war" name="MyApp1" />
<application location="myapp2.war" name="MyApp2" />
<application location="myapp3.war" name="MyApp3" />
```

この時の配置は以下のようになります。

```
wlp
└── usr
    └── servers
        └── defaultServer
            ├── apps
            │   ├── myapp1.war
            │   ├── myapp2.war
            │   └── myapp3.war
            └── server.xml
```

## B. 複数のサーバーを作成する

サーバー構成は，`server create`コマンドを複数回実行すれば，いくつでも作れます。これによって，WARファイルごとに実行するサーバー構成を作ることもできます。

この時の配置は以下のようになります。

```
wlp
└── usr
    └── servers
        ├── server1
        │   ├── apps
        │   │   └── myapp1.war
        │   └── server.xml
        ├── server2
        │   ├── apps
        │   │   └── myapp2.war
        │   └── server.xml
        └── server3
            ├── apps
            │   └── myapp3.war
            └── server.xml
```

## C. 複数のLibertyランタイムを導入

Libertyランタイムは，一つの環境にディレクトリを変えていくつでも導入することができます。

この時の配置は以下のようになります。

```
├── app1
│   └── wlp
│       └── usr
│           └── servers
│               └── defaultServer
│                   ├── apps
│                   │   └── myapp1.war
│                   └── server.xml
├── app2
│   └── wlp
│       └── usr
│           └── servers
│               └── defaultServer
│                   ├── apps
│                   │   └── myapp2.war
│                   └── server.xml
└── app3
   └── wlp
       └── usr
           └── servers
               └── defaultServer
                   ├── apps
                   │   └── myapp3.war
                   └── server.xml
```

# それぞれの構成のメリット・デメリット

この「A. 複数\<application>」「B. 複数サーバー」「C. 複数ランタイム」のそれぞれについて，いろいろな観点からメリデメを考えていきたいと思います。

## 実行時の独立性

「A. 複数\<application>」は，アプリケーションは単一のJVMで実行されますが，「B. 複数サーバー」「C. 複数ランタイム」では，アプリケーションはそれぞれ個別のJVMで実行されます。A.では，特定のアプリケーションがメモリリークを起こしてOutOfMemoryErrorが発生した場合などは，おなじJVMで稼働しているアプリケーションも巻き込まれてしまいます。B.とC.は，一つのアプリケーションが問題を起こしても，それによって別のアプリケーションの実行が妨げられることはない，というメリットがあります。また，ログも個別に生成されるため，どのアプリケーションから出されたエラーかの判別も容易です。

## メンテナンスの独立性

「B. 複数サーバー」「C. 複数ランタイム」では，他のアプリケーションに影響を与えることなく，サーバーの停止や再起動が行えます。「A. 複数\<application>」では，一つのアプリケーションが原因でサーバーを停止しないといけないときに，他のアプリケーションも停止してしまいます。また，問題判別のためにプロセスのダンプを取ったりトレースを取得するような場合も，目的のアプリケーション以外にも影響を与えますし，取得した資料に障害を起こしたアプリケーション以外の情報ものってくるため，解析が困難になるケースもあります。

また，C.はFixpackや個別Fixの適用などもアプリケーション単位で行えます。障害でFixを適用する場合，事前のテストが必要になることも多いですが，A.やB.では，問題を起こしたアプリケーションだけでなく，ランタイムを共有する他のアプリケーションのテストも必要です。

## ディスクスペースの節約

「A. 複数\<application>」および「B. 複数サーバー」は，Libertyのランタイムは一つしか導入されていませんので，必要最小限のディスクしか消費しません。ですが「C. 複数ランタイム」では，複数のLibertyランタイムが導入されますので，その分のディスクは消費されます。

ただ，その影響はあまり大きくはありません。Libertyランタイムのサイズは，一般のアプリケーションサーバーに比べると非常に小さいからです。またLibertyでは，Kernelだけがはいった最小限のインストールイメージを使用し，[サーバーで使用するFeatureをネットワーク経由でダウンロードして追加](https://www.openliberty.io/docs/latest/reference/command/featureUtility-installServerFeatures.html)する，という機能があります。このような方法で導入すれば，ディスクの消費サイズは更に小さくすることができます。

## 異なるバージョンのAPIの共存

Libertyでは，アプリケーションから利用するAPIはバージョン単位でFeatureというモジュールで提供されています。そして，新しいバージョンでAPIの新バージョンに対応しても，従来のFeatureの提供が続く，という特徴があります。これがLiberty固有のゼロマイグレーションポリシーで，Libertyではバージョンを上げても原則的にアプリケーションのマイグレーションは必要ありません。アプリケーションで使用するAPIをserver.xmlに記述すれば，必要なバージョンのAPIを利用することができます。

ですが，このFeatureの指定はサーバー単位でしか指定できません。一つのサーバーで動いている複数のアプリケーションでは，おなじFeatureが利用されます。そのため，一つのサーバーにservlet-4.0を使用するアプリケーションとservlet-5.0を使用するアプリケーションを同居させる，というようなことはできません。

「A. 複数\<application>」は，全てのアプリケーションが同じバージョンのAPIを使用する必要があります。「B. 複数サーバー」と「C. 複数ランタイム」では，アプリケーションごとに使用するAPIのバージョンを変えることができます。

## ポートの競合を避けるための構成

デフォルトでは，LibertyはHTTPリクエストを9080番，HTTPSリクエストを9443番ポートでLISTENします。「A. 複数\<application>」では，サーバープロセスは一つですので，ポートの設定を意識して変える必要はありません。ですが，「B. 複数サーバー」「C. 複数ランタイム」ではポート番号が競合しないように構成を調節する必要があります。ポート番号などは環境変数として外部化しておき，Libertyの構成を変更することなく調整できるようにしておくと便利です。

```xml:server.xml
<variable name="wlp.app1.http.port" defaultValue="9080" />
<variable name="wlp.app1.https.port" defaultValue="9443" />

<httpEndpoint id="defaultHttpEndpoint" host="*"
              httpPort="${wlp.app1.http.port}"
              httpsPort="${wlp.app1.https.port}"/>

<application location="myapp1.war" name="MyApp1" />

```

このように構成しておくと，環境変数`WLP_APP1_HTTP_PORT`および`WLP_APP1_HTTPS_PORT`で指定された番号のポートをLISTENするようになります。環境変数が指定されていない開発者のPCなどでは，9080/9443番ポートがLISTENされます。[変数の指定方法についての詳細はこちら](https://openliberty.io/docs/22.0.0.9/reference/config/server-configuration-overview.html#variable-substitution)を参照してください。

## DevOpsのやりやすさ

通常のアプリケーションサーバーにおいては，アプリケーションのコードを作成しビルドしただけでは，実働環境で実行することはできません。アプリケーションサーバーを事前に導入し，構成を済ませ，そこにビルドしたアプリケーションをデプロイして，はじめて実行することができるようになります。この従来型のアプリケーションサーバーの運用形式だと，いろいろな面でDevOpsがやりにくいと言われています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60041/e812af32-5d41-33ed-4406-d3e9dbf0fde6.png)

Libertyでは，可搬性のある実行環境のパッケージを作成することができ，事前のサーバー導入や構成などを不要にすることができます。また，サーバーで直接実行可能なJARファイルを作成することも可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60041/6c3068da-3ab2-1a37-f0a8-374e9fa26e2e.png)

Libertyであれば，サーバーの構成をアプリケーションとは別に手動で管理するのではなく，アプリケーションと一緒にコードとして管理すること，つまりPlatform as Codeが実現可能です。これにより，サーバーの運用の効率化・高品質化や運用の負荷軽減が可能になります。またパッケージによる導入は，各種ツールを使用した自動化などDevOpsの推進にも適しています。

このLibertyのパッケージ作成は，サーバーごとにおこなわれます。そのため，一つのランタイムに一つのサーバーしか定義されていない「A. 複数\<application>」は普通にパッケージできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60041/3c6fa2ee-3f83-98fa-f75a-74af81c8046b.png)

「C. 複数ランタイム」も，それぞれ一つのサーバーずつしか定義されていないため，問題なく個別にパッケージすることができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60041/e574b0b3-dcae-dd8a-d139-0cd6f061c50a.png)

ですが，複数のサーバーが定義されている「B. 複数サーバー」では，この機能を利用することは困難です。そのため，この構成を取ったときにはサーバーのパッケージングを利用した自動化などがやりにくくなるというデメリットがあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/60041/c35fffb2-f7b0-f43e-dfa1-b5c0b11631e4.png)

「A. 複数\<application>」で，複数のアプリケーションとサーバー構成をプロジェクトとしてコードで管理する方法については，[こちらのQiitaの文章](https://qiita.com/TTakakiyo/items/bab60631f114e73d0087)などを参照してください。

## コンテナへの移行

ここまでの議論は，VM環境でLibertyを使用すること前提としたものです。ただ，アプリケーション実行環境を将来的にはコンテナへ移行することを検討しているケースも多いでしょう。

「A. 複数\<application>」の場合は，サーバー構成なども含めてコンテナ環境にほとんどそのまま移行することができます。

ですが，「B. 複数サーバー」の形式は移行が困難です。通常，一つのコンテナインスタンスでは一つのプロセスしか実行しません。同じコンテナ内のLibertyのランタイムを共有する形で複数のアプリケーションサーバーを実行することには多くの困難がともないます。すくなくとも，IBMから提供されている公式のイメージは使用できず，独自のイメージを構築する必要があります。これはあまりに多くの困難がともなうため，コンテナ化にあたってはLibertyのランタイムも分離しC.の方式に移行する必要があります。

「C. 複数ランタイム」は，それぞれを個別のコンテナイメージに分離する必要がありますが，その構成をそのままコンテナ化することが可能です。ポートの分離などを考える必要がなくなる分だけ，運用はむしろ楽になると思います。

# まとめ

以上をまとめると以下のようになります。

|  | 複数<br/>\<application> | 複数<br/>サーバー | 複数<br/>ランタイム |
|:--|:-:|:-:|:-:|
|実行時の独立性|低い|高い|高い|
|メンテナンスの独立性|低い|やや低い|高い|
|ディスクスペースの節約|最低|最低|ある程度消費|
|アプリごとに異なるバージョンのAPIの使用|不可|可能|可能|
|ポートの競合を避けるための構成|不要|必要|必要|
|DevOpsのやりやすさ|容易|困難|容易|
|コンテナ化|容易|困難|わりと容易|

導入する複数のアプリケーションの関連性が強い場合，つまり同一のチームにより開発され，協調して動作するアプリケーションの場合には複数\<application>を，そうでない場合には複数ランタイムを選択するのをお薦めします。複数サーバーのメリットは実行時の独立をたもったままディスクスペースの節約が可能ということですが，サイズの小さいLibertyランタイムでは，そのメリットはあまり大きくはありません。