0
1

More than 3 years have passed since last update.

OpenShift用のシンプルなアプリケーションの作成

Last updated at Posted at 2021-08-07

はじめに

この記事では、OpenShiftへのデプロイを目的として、Spring BootとOpen Libertyによるシンプルなアプリケーションを作成します。デプロイ先のOpenShift環境は下記の記事で紹介しています。

1. Visual Studio Code準備

アプリケーションはWindows 10のVisual Studio Code(VScode)で作成します。
事前にJavaやGradleと以下のVSCode用プラグインを導入しておきます。

  • Java Extension Pack
  • Spring Boot Extension Pack

1.1. Spring Bootプロジェクトの作成

(1) コマンドパレットから「Spring Initializer: Create a Gradle Project..」を選択します。
image.png
(2) Javaを選択します。
image.png
(3) Group Idを「dummy」とします。
image.png
(4) Artifact Idを「spring-liberty」とします。
image.png
(5) packaging typeを「War」にします。
image.png
(6) Java versionを「11」にします。
image.png
(7) dependenciesとして「Spring Web」と「Thymeleaf」を選択します。
image.png

1.2. ソースコード作成

下表のソースコードを「src/main」以下のディレクトリに追加します。

ディレクトリ ファイル 用途
java/dummy/springliberty SpringLibertyController.java 要求「/」にindex.htmlを応答。
要求「/healthz」にhealthz.htmlを応答。
resources/templates index.html ユーザーエージェントとホスト名を表示。
resources/templates healthz.html OKを表示。
SpringLibertyController.java
package dummy.springliberty;

import java.net.InetAddress;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.ui.Model;

@Controller
public class SpringLibertyController {
    @RequestMapping("/")
    public String index(Model model, @RequestHeader("User-Agent") String userAgent) {
        model.addAttribute("userAgent", userAgent);

        try {
            InetAddress ia = InetAddress.getLocalHost();
            model.addAttribute("hostName", ia.getHostName());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "index";
    }

    @RequestMapping("/healthz")
    public String healthz() {
        return "healthz";
    }
}
index.html
<html><head><title>Spring Boot + Liberty</title></head><body>
    <table border="1" style="font-size: 20pt" cellpadding="10">
        <tr><th>Version</th><th>20210716_01</th></tr>
        <tr><th>User Agent</th><th><p th:text="${userAgent}"></p></th></tr>
        <tr><th>Pod</th><th><p th:text="${hostName}"></p></th></tr>
    </table>
</body></html>
healthz.html
OK

1.3. Open Liberty用Gradleプラグイン追加

Open Liberty開発支援プラグインを使用するためにbuild.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.7'
}

test {
    useJUnitPlatform()
}

liberty {
    server {
        name = 'defaultServer'
        deploy {
            apps = [file('build/libs/spring-liberty-plain.war')]
        }
    }
}

clean.dependsOn 'libertyStop'

2. Spring Bootアプリケーションのビルドと実行

2.1. Spring Bootアプリケーションのビルド

gradle build』によりコンパイルされ「build/libs/spring-liberty-plain.war」が作成されます。

VSCodeターミナル
gradle build
### 標準出力↓
Starting a Gradle Daemon, 2 busy and 3 incompatible and 1 stopped Daemons could not be reused, use --status for details

BUILD SUCCESSFUL in 8s
7 actionable tasks: 7 up-to-date

2.2. Open Liberty上でアプリケーションを実行

gradle libertyRun』により以下の処理が実行されます。

  • build.gradleで指定されたバージョンのOpen Libertyが存在しないときはダウンロードする。
  • Open Libertyのディレクトリ「build/wlp」を作成する。
  • src/main/liberty/server.xmlを「build/wlp/user/servers/defaultServer」にコピーする。
  • build/libs/spring-liberty-plain.warを「build/wlp/usr/servers/defaultServer/apps」にコピーする。
  • Open Liberty をフォアグラウンドで実行する。
VSCodeターミナル
gradle libertyRun
### 標準出力↓
> 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.

> Task :installFeature


> Task :libertyRun
OpenJDK 64-Bit Server VM バージョン 11.0.11+9-LTS (ja_JP) で、defaultServer (Open Liberty 21.0.0.7/wlp-1.0.54.cl210720210629-1900) を起動しています
[監査      ] CWWKE0001I: サーバー defaultServer が起動されました。
[監査      ] CWWKZ0058I: アプリケーションの dropins をモニター中です。
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.ibm.ws.util.ThreadContextAccessor (file:/C:/workspace/source/spring-liberty/build/wlp/lib/com.ibm.ws.container.service_1.0.54.jar) to field java.lang.Thread.contextClassLoader
WARNING: Please consider reporting this to the maintainers of com.ibm.ws.util.ThreadContextAccessor
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[監査      ] CWWKT0016I: Web アプリケーションが使用可能です (default_host): http://xxx.xxx.xxx.xxx:9080/
  .   ____          _            __ _ _  
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \ 
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)
2021-08-07 10:03:09.803  INFO 11352 --- [cutor-thread-22] dummy.springliberty.ServletInitializer   : Starting ServletInitializer using Java 11.0.11 on mark2 with PID 11352 (C:\workspace\source\spring-liberty\build\wlp\usr\servers\defaultServer\apps\expanded\spring-liberty-plain.war\WEB-INF\classes 
started by root in C:\workspace\source\spring-liberty\build\wlp\usr\servers\defaultServer)
2021-08-07 10:03:09.803  INFO 11352 --- [cutor-thread-22] dummy.springliberty.ServletInitializer   : No active profile set, falling back to default profiles: default
2021-08-07 10:03:10.649  INFO 11352 --- [cutor-thread-22] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 796 m 
2021-08-07 10:03:11.092  INFO 11352 --- [cutor-thread-22] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2021-08-07 10:03:11.244  INFO 11352 --- [cutor-thread-22] dummy.springliberty.ServletInitializer   : Started ServletInitializer in 1.996 seconds (JVM running for 10.896)
[監査      ] CWWKZ0001I: アプリケーション spring-liberty-plain が 4.134 秒で開始しました。
[監査      ] CWWKF0012I: サーバーは次のフィーチャーをインストールしました。[el-3.0, jsp-2.3, servlet-3.1]。
[監査      ] CWWKF0011I: defaultServer サーバーは、Smarter Planet に対応する準備ができました。defaultServer サーバーは 10.786 秒で始動しました。
src/main/liberty/server.xml
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
    <!-- Enable features -->
    <featureManager>
        <feature>jsp-2.3</feature>
    </featureManager>

    <!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
    <httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" />

    <!-- Automatically expand WAR files and EAR files -->
    <applicationManager autoExpand="true"/>

    <webApplication contextRoot="/" location="spring-liberty-plain.war" />
</server>

また、『gradle libertyDev』した場合は、Open Libertyがバックグラウンドで実行され、Javaのソースコードが更新されると自動的にOpen Libertyが再起動するようになります。

Open Libertyを起動した状態でブラウザでhttp://localhost:9080/を開くと以下のような画面が表示されます。OpenShiftにデプロイしていないので、Pod名ではなくホスト名が表示されます。
image.png
※上記ではOpen Libertyで「build/libs/spring-liberty-plain.war」を実行しています。「build/libs/spring-liberty.war」に含まれるTomcatでアプリケーションを実行することも可能です。

■ 起動方法
java -jar build/libs/spring-liberty.war

■ URL
http://localhost:8080/

2.3. GitHubの利用

ソースコードをGitHubに公開しました。gitコマンドとJava 11が導入されていれば、下記の手順でSpring BootアプリケーションをOpen Liberty上で実行することがでできます。Gradleも自動的にダウンロードされます。
https://github.com/y-akio/source.git

CentOSでの実行例
git clone https://github.com/y-akio/source.git
cd source/spring-liberty
chmod +x gradlew
./gradlew build
### 標準出力↓
Downloading https://services.gradle.org/distributions/gradle-7.1.1-bin.zip
..........10%...........20%...........30%..........40%...........50%...........60%..........70%...........80%...........
90%...........100%
Starting a Gradle Daemon (subsequent builds will be faster)
> Task :compileJava
> Task :processResources
> Task :classes
> Task :bootWarMainClassName
> Task :bootWar
> Task :war
> Task :assemble
> Task :compileTestJava
> Task :processTestResources NO-SOURCE
> Task :testClasses
> Task :test
> Task :check
> Task :build

BUILD SUCCESSFUL in 1m 18s
7 actionable tasks: 7 executed

./gradlew libertyRun
### 標準出力↓
> Task :compileJava UP-TO-DATE
> Task :processResources UP-TO-DATE
> Task :classes UP-TO-DATE
> Task :bootWarMainClassName UP-TO-DATE
> Task :bootWar UP-TO-DATE
> Task :installLiberty
> Task :libertyCreate
> Task :war UP-TO-DATE

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

> Task :installFeature


> Task :libertyRun
OpenJDK Server VM バージョン 11.0.11+9-LTS (ja_JP) で、defaultServer (Open Liberty 21.0.0.7/wlp-1.0.54.cl210720210629-1900) を起動しています
[監査      ] CWWKE0001I: サーバー defaultServer が起動されました。
[監査      ] CWWKZ0058I: アプリケーションの dropins をモニター中です。
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.ibm.ws.util.ThreadContextAccessor (file:/tmp/source/spring-liberty/build/wlp/lib/com.ibm.ws.container.service_1.0.54.jar) to field java.lang.Thread.contextClassLoader
WARNING: Please consider reporting this to the maintainers of com.ibm.ws.util.ThreadContextAccessor
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
[監査      ] CWWKT0016I: Web アプリケーションが使用可能です (default_host): http://haproxy:9080/
  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.2)
2021-08-07 10:23:03.049  INFO 14369 --- [ecutor-thread-4] dummy.springliberty.ServletInitializer   : Starting ServletInitializer using Java 11.0.11 on haproxy with PID 14369 (/tmp/
source/spring-liberty/build/wlp/usr/servers/defaultServer/apps/expanded/spring-liberty-plain.war/WEB-INF/classes started by root in /tmp/source/spring-liberty/build/wlp/usr/servers
/defaultServer)
2021-08-07 10:23:03.067  INFO 14369 --- [ecutor-thread-4] dummy.springliberty.ServletInitializer   : No active profile set, falling back to default profiles: default
2021-08-07 10:23:08.991  INFO 14369 --- [ecutor-thread-4] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 5318 ms
2021-08-07 10:23:11.566  INFO 14369 --- [ecutor-thread-4] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2021-08-07 10:23:12.485  INFO 14369 --- [ecutor-thread-4] dummy.springliberty.ServletInitializer   : Started ServletInitializer in 12.681 seconds (JVM running for 35.805)
[監査      ] CWWKZ0001I: アプリケーション spring-liberty-plain が 24.971 秒で開始しました。
[監査      ] CWWKF0012I: サーバーは次のフィーチャーをインストールしました。[el-3.0, jsp-2.3, servlet-3.1]。
[監査      ] CWWKF0011I: defaultServer サーバーは、Smarter Planet に対応する準備ができました。defaultServer サーバーは 35.766 秒で始動しました。

3. OpenShiftへのデプロイ

DockerfileによりOpen Liberty上でSpring Bootアプリケーションを実行するコンテナのイメージを作成し、コンテナイメージからデプロイメント「spring-liberty」を作成します。

3.1. コンテナイメージのビルド

OpenShiftでコンテナイメージをビルドします。結果としてイメージストリームが作成されます。

classicノード
ls -l
### 標準出力
-rw-r--r--. 1 root root      139  8月  7 10:22 Dockerfile
-rw-r--r--. 1 root root      669  8月  7 10:22 server.xml
-rw-r--r--. 1 root root 13138834  8月  7 10:06 spring-liberty-plain.war
-rw-r--r--. 1 root root     1097  8月  7 10:22 spring-liberty.yaml

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 ...
.
Uploading finished
build.build.openshift.io/spring-liberty-4 started
Receiving source from STDIN as archive ...
Caching blobs under "/var/cache/blobs".

Pulling image open-liberty:21.0.0.7-full-java11-openj9 ...
・・・
STEP 1: FROM open-liberty:21.0.0.7-full-java11-openj9
STEP 2: COPY server.xml /config/
--> bb126b57c0d
STEP 3: COPY spring-liberty-plain.war /config/apps/
--> 5b9a3b886cd
STEP 4: USER 1001
--> fde4d2230c0
STEP 5: EXPOSE 9080
・・・
Successfully pushed image-registry.openshift-image-registry.svc:5000/spring-liberty/spring-liberty@sha256:1ab62ce3019906
cf896185b701053189d01528603f55c7004f103b469354a672
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

3.2. コンテナのデプロイ

デプロイメント「spring-liberty」としてコンテナをデプロイします。

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          2m28s
pod/spring-liberty-84c44ffc5b-24w8n   0/1     Running     0          25s
pod/spring-liberty-84c44ffc5b-js6cg   0/1     Running     0          25s

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

NAME                                       CLASS    HOSTS                               ADDRESS                             PORTS   AGE
ingress.networking.k8s.io/spring-liberty   <none>   spring-liberty.apps.ocp.cloud.vpc   router-default.apps.ocp.cloud.vpc   80      25s
Dockerfile
FROM open-liberty:21.0.0.7-full-java11-openj9

COPY server.xml /config/
COPY spring-liberty-plain.war /config/apps/

EXPOSE 9080
server.xml
<?xml version="1.0" encoding="UTF-8"?>
<server description="new server">
    <!-- Enable features -->
    <featureManager>
        <feature>jsp-2.3</feature>
    </featureManager>

    <!-- To access this server from a remote client add a host attribute to the following element, e.g. host="*" -->
    <httpEndpoint id="defaultHttpEndpoint" host="*" httpPort="9080" accessLoggingRef="accessLogging" />
    <httpAccessLogging id="accessLogging" filePath="/logs/http_access.log"/>

    <!-- Automatically expand WAR files and EAR files -->
    <applicationManager autoExpand="true"/>

    <webApplication contextRoot="/" location="spring-liberty-plain.war" />
</server>
spring-liberty.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-liberty
  labels:
    app: spring-liberty
spec:
  replicas: 2
  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

ブラウザでhttp://spring-liberty.apps.ocp.cloud.vpc/を開くと以下のような画面が表示されます。端末で実行した時と違いPod名が表示されています。
image.png

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1