UBIマイクロからのOpen Libertyコンテナイメージの作成

# はじめに
Open Libertyをコンテナとして実行するとき、Docker Hubで公開されている公式イメージを元にするのが容易です。ただ、Java実行環境とOpen Libertyを個別に変更したいこともあります。本記事では、UBIマイクロイメージを元とし、Java実行環境とOpen Libertyを追加した軽量なイメージを作成して「Red Hat OpenShift on IBM Cloud」にデプロイします。

■ Open Liberty公式イメージ

https://hub.docker.com/_/open-liberty

# 1. Open Libertyパッケージング
## 1.1. Open Liberty用Gradleプラグイン設定
Open Liberty用Gradleプラグインには、Open Libertyランタイムとサーバー構成、ビルド済のWebアプリケーション(warファイル)を複数種類の形式でパッケージングする「libertyPackage」タスクがあり、同タスクではOpen Libertyのサイズを縮小することも可能です。

> The libertyPackage task is used to create a ZIP, TAR, TAR.GZ or JAR archive of your Liberty runtime and server.

https://github.com/OpenLiberty/ci.gradle/blob/main/docs/libertyPackage.md#libertypackage-task

以前の[記事](https://qiita.com/y-akio/items/155ae44188a6df02138c)で利用したbuild.gradleにて、Open Libertyバージョンを最新の「21.0.0.11」とし『minify』でパッケージする設定を追加します。デフォルト動作は『all』です。

```gradle:build.gradle
plugins {
	id 'org.springframework.boot' version '2.5.2'
	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
	id 'io.openliberty.tools.gradle.Liberty' version '3.2'
	id 'java'
	id 'war'
}

group = 'dummy'
version = ''
sourceCompatibility = '11'

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	providedRuntime 'org.springframework.boot:spring-boot-starter-tomcat'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'

    libertyRuntime group: 'io.openliberty', name: 'openliberty-runtime', version: '21.0.0.11'
}

test {
	useJUnitPlatform()
}

liberty {
    server {
        name = 'defaultServer'
        deploy {
            apps = [file('build/libs/spring-liberty-plain.war')]
        }
	packageLiberty {
            include = "minify"
            os = "Linux"
            packageType = "tar.gz"
        }
    }
}

clean.dependsOn 'libertyStop'
```

## 1.2. Open Libertyパッケージタスク実行
『**gradle libertyPackage**』によりパッケージされ「build/libs/spring-liberty.zip」が作成されます。

```bash:VSCodeターミナル
gradle libertyPackage
### 標準出力↓
> Task :installFeature


> Task :deploy
Application spring-liberty-plain.war was installed as a file as specified. To install as a loose application, specify the plugin or task generating the archive.   

BUILD SUCCESSFUL in 38s
10 actionable tasks: 10 executed
```

オプションによるパッケージサイズ等の差異は下表の通りです。『minify』は『all』と比較して、Open Libertyランタイムのサイズがかなり小さくなっていることが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/fe8f3e7b-6e81-cc81-4757-6ed8f10c9065.png)

# 2. Open Libertyコンテナイメージ作成
## 2.1. Java実行環境
Java実行環境として、下記URL記載の「IBM Semeru 11」をダウンロードしておきます。

https://www.openliberty.io/docs/21.0.0.11/java-se.html

```
ls -l
### 標準出力↓
-rw-r--r--. 1 root root 43966499 11月 20 17:44 ibm-semeru-open-jre_x64_linux_11.0.13_8_openj9-0.29.0.tar.gz
```

## 2.2. UBIマイクロコンテナイメージへのパッケージ追加
UBIマイクロコンテナイメージに、Java実行環境とOpen Libertyの実行に必要なパッケージを追加し、タグ「liberty-base」としてDocker Hubにプッシュします。Java実行環境とOpen Libertyを追加したコンテナイメージの作成は次章で行います。

```bash:classicノード
microcontainer=$(buildah from redhat/ubi8-micro)
micromount=$(buildah mount $microcontainer)

yum install \
     --installroot $micromount \
     --releasever 8 \
     --setopt install_weak_deps=false \
     --nodocs -y \
     libstdc++-devel sed grep which
yum clean all \
    --installroot $micromount

buildah umount $microcontainer
buildah commit $microcontainer liberty-base

podman tag liberty-base <アカウント名>/<リポジトリ名>:liberty-base
podman login docker.io
podman push <アカウント名>/<リポジトリ名>:liberty-base
```

https://access.redhat.com/documentation/ja-jp/red_hat_enterprise_linux/8/html/building_running_and_managing_containers/proc_using-the-ubi-micro-images_assembly_types-of-container-images


# 3. OpenShiftへのデプロイ
## 3.1. コンテナイメージのビルド
OpenShiftでコンテナイメージをビルドします。結果としてイメージストリームが作成されます。
コンテナイメージサイズを小さくするために、マルチステージ・ビルドとしています。

```bash:classicノード
ls -l
### 標準出力
-rw-r--r--. 1 root root      961 11月 21 22:06 Dockerfile
-rw-r--r--. 1 root root 43966499 11月 20 17:44 ibm-semeru-open-jre_x64_linux_11.0.13_8_openj9-0.29.0.tar.gz
-rw-r--r--. 1 root root 45347557 11月 21 22:14 spring-liberty.tar.gz

oc new-project spring-liberty
oc new-build --name=spring-liberty --strategy=docker --binary
oc start-build spring-liberty --from-dir=. --follow
### 標準出力↓
Uploading directory "." as binary input for the build ...
…
STEP 1: FROM <アカウント名>/<リポジトリ名>:liberty-base AS builder
STEP 2: ADD ibm-semeru-open-jre_x64_linux_11.0.13_8_openj9-0.29.0.tar.gz /opt
--> 1fee53489ae
STEP 3: ADD spring-liberty.tar.gz /opt
--> 1a1f57daba2
…
Push successful

oc get is
### 標準出力↓
NAME             IMAGE REPOSITORY                                                                 TAGS     UPDATED
spring-liberty   image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty   latest   7 seconds ago
```

```Dockerfile:Dockerfile
FROM <アカウント名>/<リポジトリ名>:liberty-base as builder

ADD ibm-semeru-open-jre_x64_linux_11.0.13_8_openj9-0.29.0.tar.gz /opt
ADD spring-liberty.tar.gz /opt

WORKDIR /opt
RUN mkdir -p /opt/wlp/output/defaultServer           \
 && chmod -R g+rw /opt                               \
 && chmod +x /opt/wlp/bin/server                     \
 && mkdir /logs                                      \
 && chmod -R g+rw /logs


FROM <アカウント名>/<リポジトリ名>:liberty-base

ENV PATH=/opt/wlp/bin:/opt/jdk-11.0.13+8-jre/bin:$PATH \
    LOG_DIR=/logs \
    WLP_OUTPUT_DIR=/opt/wlp/output

COPY --from=builder /opt/wlp /opt/wlp
COPY --from=builder /opt/jdk-11.0.13+8-jre /opt/jdk-11.0.13+8-jre
COPY --from=builder /logs /logs

RUN ln -s /opt/wlp/output/defaultServer /output      \
 && ln -s /opt/wlp/usr/servers/defaultServer /config

EXPOSE 9080

CMD ["/opt/wlp/bin/server", "run", "defaultServer"]
```

## 3.2. コンテナのデプロイ
Deployment「spring-liberty」としてコンテナをデプロイします。

```bash:classicノード
oc apply -f spring-liberty.yaml
oc get pod,svc,ing
### 標準出力↓
NAME                                  READY   STATUS      RESTARTS   AGE
pod/spring-liberty-1-build            0/1     Completed   0          7m24s
pod/spring-liberty-84c44ffc5b-gqpnp   1/1     Running     0          68s
pod/spring-liberty-84c44ffc5b-lzccq   1/1     Running     0          68s
pod/spring-liberty-84c44ffc5b-x88bj   1/1     Running     0          68s

NAME                     TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE
service/spring-liberty   ClusterIP   172.21.255.127   <none>        80/TCP    68s

NAME                                       CLASS    HOSTS                               ADDRESS
                                                  PORTS   AGE
ingress.networking.k8s.io/spring-liberty   <none>   spring-liberty.apps.ocp.cloud.vpc   router-default.roks-jp-tok-3-bx2-4x16-db921bfba21f21a261dcbdfd
d8987265-i000.jp-tok.containers.appdomain.cloud   80      68s
```

```yaml:spring-liberty.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-liberty
  labels:
    app: spring-liberty
spec:
  replicas: 3
  selector:
    matchLabels:
      app: spring-liberty
  template:
    metadata:
      labels:
        app: spring-liberty
    spec:
      containers:
        - name: spring-liberty
          image: image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty
          ports:
            - containerPort: 9080
          readinessProbe:
            httpGet:
              path: /healthz
              port: 9080
---
apiVersion: v1
kind: Service
metadata:
  name: spring-liberty
spec:
  selector:
    app: spring-liberty
  type: ClusterIP
  ports:
   - protocol: TCP
     port: 80
     targetPort: 9080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: spring-liberty
spec:
  rules:
    - host: spring-liberty.apps.ocp.cloud.vpc
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: spring-liberty
                port:
                  number: 80
```

# 4. コンテナサイズについて
## 4.1. OpenShiftで作成したコンテナサイズの確認
OpenShiftにデプロイしたコンテナのイメージは、各workerノードのCRI-Oコンテナエンジンがダウンロードしています。crictlコマンドで確認したところ、「spring-liberty」コンテナイメージのサイズは「238MB」でした。

```bash:classicノード
oc debug node/10.244.32.4
### 標準出力↓
Starting pod/10244324-debug ...
To use host binaries, run `chroot /host`

chroot /host
### 標準出力↓
Pod IP: 10.244.32.4
If you do not see a command prompt, try pressing enter.

crictl images | grep "spring-liberty" | grep "latest"
### 標準出力↓
image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty   latest                                     6250bd030d7e1       238MB
```

## 4.2. Podmanで作成したコンテナサイズの比較
Podmanでコンテナをビルドしたときのサイズを下表にまとめます。
Open Liberty公式イメージを元にした場合と比較して、UBIマイクロを元にした場合は半分以下のサイズになりました。サイズ差異は、OSライブラリが異なること(ubunts⇔ubi8-maicro)、Open Libertyに含まれるライブラリが異なること(すべて⇔必要最小限)から生じています。

■ UBIマイクロを元にした場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/b7cb96a7-7d47-5e84-3677-2ec1f341cbed.png)
■ 参考情報
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/f26bf491-c417-564e-3360-a4c6fa327c01.png)`
■ Open Libertyを元にした場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1413476/754ec464-46f5-7c4c-0437-16dea87444c6.png)

```bash:classicノード
podman images | egrep "localhost|ubi8-micro|open-liberty"
### 標準出力
localhost/spring-liberty-official  latest                        cc8e92f49b93  3 minutes ago  540 MB
localhost/spring-liberty-multi     latest                        8ca620024c71  4 minutes ago  238 MB
localhost/spring-liberty-single    latest                        82b26d37234a  6 minutes ago  416 MB
localhost/liberty-base             latest                        009a113d13db  6 minutes ago  59.7 MB
docker.io/library/open-liberty     21.0.0.11-full-java11-openj9  37333a74cc7b  2 weeks ago    527 MB
docker.io/redhat/ubi8-micro        latest                        c8efdc89ddd2  2 weeks ago    38.7 MB
```

## 4.3. UBIマイクロを元にしたマルチステージ・ビルドについて
シングルステージ・ビルドの場合、liberty-base:59.7MBにJava実行環境:123MBとOpen Liberty・アプリ:49MBを追加します。合計すると231.7MBですが、実際に作成されたコンテナイメージのサイズは416MBでした。イメージのヒストリーを確認したところ、ディレクトリ作成・権限変更をしている箇所で178MB増加していました。ディレクトリ作成・権限変更を別ステージで実行=マルチステージ・ビルドすることで、ほぼ想定通りの238MBのイメージが作成されました。

> <missing>     8 minutes ago  /bin/sh -c mkdir -p /opt/wlp/output/defaul...  178 MB

Dockerfileとヒストリーは以下の通りです。

```bash:ubi-microベース(シングル)
cat ./single/Dockerfile
### 標準出力↓
FROM <アカウント名>/<リポジトリ名>:liberty-base

ENV PATH=/opt/wlp/bin:/opt/jdk-11.0.13+8-jre/bin:$PATH \
    LOG_DIR=/logs \
    WLP_OUTPUT_DIR=/opt/wlp/output

ADD ibm-semeru-open-jre_x64_linux_11.0.13_8_openj9-0.29.0.tar.gz /opt
ADD spring-liberty.tar.gz /opt

WORKDIR /opt
RUN mkdir -p /opt/wlp/output/defaultServer           \
 && chmod -R g+rw /opt                               \
 && chmod +x /opt/wlp/bin/server                     \
 && ln -s /opt/wlp/output/defaultServer /output      \
 && ln -s /opt/wlp/usr/servers/defaultServer /config \
 && mkdir /logs                                      \
 && chmod -R g+rw /logs

EXPOSE 9080

CMD ["/opt/wlp/bin/server", "run", "defaultServer"]

podman history localhost/spring-liberty-single
### 標準出力↓
ID            CREATED        CREATED BY                                     SIZE        COMMENT
cdebeab35069  8 minutes ago  /bin/sh -c #(nop) CMD ["/opt/wlp/bin/serve...  0 B
<missing>     8 minutes ago  /bin/sh -c #(nop) EXPOSE 9080                  0 B
<missing>     8 minutes ago  /bin/sh -c mkdir -p /opt/wlp/output/defaul...  178 MB
91bbe4104f1b  8 minutes ago  /bin/sh -c #(nop) WORKDIR /opt                 0 B
<missing>     8 minutes ago  /bin/sh -c #(nop) ADD file:e0cf85c990987ef...  49.6 MB
fce619af2bd1  8 minutes ago  /bin/sh -c #(nop) ADD file:07363d12ccff3d1...  128 MB
4767b35696bf  8 minutes ago  /bin/sh -c #(nop) ENV PATH=/opt/wlp/bin:/o...  0 B         FROM docker.io/<アカウント名>/<リポジトリ名>:liberty-base
<missing>     2 days ago     /bin/sh                                        21.5 MB     FROM registry.access.redhat.com/ubi8/ubi-micro:latest
c8efdc89ddd2  2 weeks ago                                                   38.7 MB
<missing>     2 weeks ago                                                   1.02 kB     Imported from -
```

```bash:ubi-microベース(マルチ)
cat ./multi/Dockerfile
### 標準出力↓
FROM <アカウント名>/<リポジトリ名>:liberty-base as builder

ADD ibm-semeru-open-jre_x64_linux_11.0.13_8_openj9-0.29.0.tar.gz /opt
ADD spring-liberty.tar.gz /opt

WORKDIR /opt
RUN mkdir -p /opt/wlp/output/defaultServer           \
 && chmod -R g+rw /opt                               \
 && chmod +x /opt/wlp/bin/server                     \
 && mkdir /logs                                      \
 && chmod -R g+rw /logs


FROM <アカウント名>/<リポジトリ名>:liberty-base

ENV PATH=/opt/wlp/bin:/opt/jdk-11.0.13+8-jre/bin:$PATH \
    LOG_DIR=/logs \
    WLP_OUTPUT_DIR=/opt/wlp/output

COPY --from=builder /opt/wlp /opt/wlp
COPY --from=builder /opt/jdk-11.0.13+8-jre /opt/jdk-11.0.13+8-jre
COPY --from=builder /logs /logs

RUN ln -s /opt/wlp/output/defaultServer /output      \
 && ln -s /opt/wlp/usr/servers/defaultServer /config

EXPOSE 9080

CMD ["/opt/wlp/bin/server", "run", "defaultServer"]

podman history localhost/spring-liberty-multi
### 標準出力↓
ID            CREATED        CREATED BY                                     SIZE        COMMENT
aaf23c9e895b  6 minutes ago  /bin/sh -c #(nop) CMD ["/opt/wlp/bin/serve...  0 B
<missing>     6 minutes ago  /bin/sh -c #(nop) EXPOSE 9080                  0 B
<missing>     6 minutes ago  /bin/sh -c ln -s /opt/wlp/output/defaultSe...  4.61 kB
bb5cd35f2328  6 minutes ago  /bin/sh -c #(nop) COPY dir:5f70bf18a086007...  1.54 kB
d5f7639bf283  7 minutes ago  /bin/sh -c #(nop) COPY dir:7c77887630b5c9a...  128 MB
0e3bf830c855  7 minutes ago  /bin/sh -c #(nop) COPY dir:d919f6f91df129a...  49.6 MB
4767b35696bf  9 minutes ago  /bin/sh -c #(nop) ENV PATH=/opt/wlp/bin:/o...  0 B         FROM docker.io/<アカウント名>/<リポジトリ名>:liberty-base
<missing>     2 days ago     /bin/sh                                        21.5 MB     FROM registry.access.redhat.com/ubi8/ubi-micro:latest
c8efdc89ddd2  2 weeks ago                                                   38.7 MB
<missing>     2 weeks ago                                                   1.02 kB     Imported from -
```