はじめに
この記事では、OpenShiftへのデプロイを目的として、Quarkusによるシンプルなネイティブアプリケーションを作成します。デプロイ先のOpenShift環境は下記の記事で紹介しています。
■ 参考
1. 環境準備
アプリケーションはWindows 10のVisual Studio Code(VScode)で作成します。
VScodeにはJavaとGradleのプラグインを、Windows 10には下記を導入しています。
OpenShiftにデプロイするネイティブアプリケーションのLinux用バイナリはDockerで作成します。
- Docker Desktop 4.0.0
- Maven 3.8.2
- Gradle 7.1.1
- Java 11
1.1. ソースコード作成
gradle用のプロジェクトをmavenで作成し、拡張「quarkus-resteasy-qute」を追加します。
自動生成されたbuild.gradleのversion行のみ修正しました。
mvn io.quarkus:quarkus-maven-plugin:2.2.1.Final:create `
-DcodestartsEnabled -DprojectGroupId=dummy `
-DprojectArtifactId=quarkus-native -DbuildTool=GRADLE
cd quarkus-native
gradle listExtensions
### 標準出力↓
…
RESTEasy Qute quarkus-resteasy-qute
…
gradle addExtension --extensions="quarkus-resteasy-qute"
下表のソースコードを「src/main」以下のディレクトリに追加します。
ディレクトリ | ファイル | 用途 |
---|---|---|
java/dummy | IndexResource.java | 要求「/index」にindex.htmlを応答。 |
java/dummy | HealthzResource.java | 要求「/healthz」にhezlthz.htmlを応答。 |
resources/templates | index.html | ユーザーエージェントとホスト名を表示。 |
resources/templates | healthz.html | OKを表示。 |
package dummy;
import java.net.InetAddress;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import org.jboss.resteasy.annotations.jaxrs.HeaderParam;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.Template;
@Path("/index")
public class IndexResource {
@Inject
Template index;
@GET
@Produces(MediaType.TEXT_HTML)
public TemplateInstance get(@HeaderParam("User-Agent") String userAgent) {
String hostName = "";
try {
InetAddress ia = InetAddress.getLocalHost();
hostName = ia.getHostName();
} catch (Exception e) {
e.printStackTrace();
}
return index.data("userAgent", userAgent, "hostName", hostName);
}
}
package dummy;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import io.quarkus.qute.TemplateInstance;
import io.quarkus.qute.Template;
@Path("/healthz")
public class HealthzResource {
@Inject
Template healthz;
@GET
@Produces(MediaType.TEXT_HTML)
public TemplateInstance get() {
return healthz.instance();
}
}
<html><head><title>Quarkus Native</title></head><body>
<table border="1" style="font-size: 20pt" cellpadding="10">
<tr><th>Version</th><th>20210904</th></tr>
<tr><th>User Agent</th><th>{userAgent}</th></tr>
<tr><th>Pod</th><th>{hostName}</th></tr>
</table>
</body></html>
OK
plugins {
id 'java'
id 'io.quarkus'
}
repositories {
mavenCentral()
mavenLocal()
}
dependencies {
implementation 'io.quarkus:quarkus-resteasy-qute'
implementation enforcedPlatform("${quarkusPlatformGroupId}:${quarkusPlatformArtifactId}:${quarkusPlatformVersion}")
implementation 'io.quarkus:quarkus-arc'
implementation 'io.quarkus:quarkus-resteasy'
testImplementation 'io.quarkus:quarkus-junit5'
testImplementation 'io.rest-assured:rest-assured'
}
group 'dummy'
version '1.0'
java {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
compileJava {
options.encoding = 'UTF-8'
options.compilerArgs << '-parameters'
}
compileTestJava {
options.encoding = 'UTF-8'
}
■ 参考資料
2. Quarkusアプリケーションのビルドと実行
2.1. Quarkusアプリケーションのビルド
『gradle build』によりコンパイルされ「build/quarkus-app/quarkus-run.jar」、「build/quarkus-app/lib/~」が作成されます。これらのファイルはQuarkusアプリケーションをJava仮想マシンで実行する場合に使用します。
gradle build
### 標準出力↓
> Task :quarkusBuild
building quarkus jar
BUILD SUCCESSFUL in 7s
8 actionable tasks: 3 executed, 5 up-to-date
『gradle build』にネイティブイメージをコンテナで作成するオプションを追加することで、Docker Desktop上にコンテナが起動され、Linux用のネイティブコード「build/quarkus-native-1.0-runner」が作成されます。
gradle build -D quarkus.package.type=native -D quarkus.native.container-build=true
### 標準出力↓
building quarkus jar
21.2-java11: Pulling from quarkus/ubi-quarkus-native-image
Digest: sha256:2b32e2494199c6096e68c66ffd7dc8b4924735e1e237c8d821deb5d2209b1da9
Status: Image is up to date for quay.io/quarkus/ubi-quarkus-native-image:21.2-java11
quay.io/quarkus/ubi-quarkus-native-image:21.2-java11
[quarkus-native-1.0-runner:25] classlist: 3,238.74 ms, 0.96 GB
[quarkus-native-1.0-runner:25] (cap): 555.31 ms, 0.96 GB
[quarkus-native-1.0-runner:25] setup: 2,063.57 ms, 0.96 GB
The bundle named: messages, has not been found. If the bundle is part of a module, verify the bundle name is a fully qualified class name. Otherwise verify the bundle path is accessible in the classpath.
13:10:11,346 INFO [org.jbo.threads] JBoss Threads version 3.4.2.Final
[quarkus-native-1.0-runner:25] (clinit): 489.94 ms, 4.25 GB
[quarkus-native-1.0-runner:25] (typeflow): 9,909.48 ms, 4.25 GB
[quarkus-native-1.0-runner:25] (objects): 12,342.45 ms, 4.25 GB
[quarkus-native-1.0-runner:25] (features): 877.11 ms, 4.25 GB
[quarkus-native-1.0-runner:25] analysis: 24,646.53 ms, 4.25 GB
[quarkus-native-1.0-runner:25] universe: 1,195.41 ms, 4.25 GB
[quarkus-native-1.0-runner:25] (parse): 2,535.66 ms, 4.25 GB
[quarkus-native-1.0-runner:25] (inline): 4,183.78 ms, 4.63 GB
[quarkus-native-1.0-runner:25] (compile): 18,716.19 ms, 4.36 GB
[quarkus-native-1.0-runner:25] compile: 27,282.70 ms, 4.36 GB
[quarkus-native-1.0-runner:25] image: 3,782.32 ms, 4.36 GB
[quarkus-native-1.0-runner:25] write: 1,512.34 ms, 4.40 GB
[quarkus-native-1.0-runner:25] [total]: 64,244.58 ms, 4.40 GB
# Printing build artifacts to: /project/quarkus-native-1.0-runner.build_artifacts.txt
BUILD SUCCESSFUL in 1m 22s
6 actionable tasks: 1 executed, 5 up-to-date
2.2. Quarkusアプリケーションの実行
『gradle quarkusDev』でQuarkusアプリケーションを起動することができます。
JBoss上で動作しているようです。Windowsネイティブではありません。
gradle quarkusDev
### 標準出力↓
> Task :quarkusDev
Listening for transport dt_socket at address: 5005
SLF4J: Class path contains multiple SLF4J bindings.
SLF4J: Found binding in [jar:file:/C:/.gradle/caches/modules-2/files-2.1/org.jboss.slf4j/slf4j-jboss-logmanager/1.1.0.Final/5f1c0e3f5082c21f6b4964b97fe5b1d5f8c42f53/slf4j-jboss-logmanager-1.1.0.Final.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: Found binding in [jar:file:/C:/.m2/repository/org/jboss/slf4j/slf4j-jboss-logmanager/1.1.0.Final/slf4j-jboss-logmanager-1.1.0.Final.jar!/org/slf4j/impl/StaticLoggerBinder.class]
SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation.
SLF4J: Actual binding is of type [org.slf4j.impl.Slf4jLoggerFactory]
Press [h] for more options>NG [8s]
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2021-09-04 17:11:57,608 INFO [io.quarkus] (Quarkus Main Thread) quarkus-native 1.0 on JVM (powered by Quarkus 2.2.1.Final) started in 2.003s. Listening on: http://localhost:8080
2021-09-04 17:11:57,621 INFO [io.quarkus] (Quarkus Main Thread) Profile dev activated. Live Coding activated.
2021-09-04 17:11:57,622 INFO [io.quarkus] (Quarkus Main Thread) Installed features: [cdi, qute, resteasy, resteasy-qute, smallrye-context-propagation]
http://localhost:8080/index
を開くと以下のような画面が表示されます。
OpenShiftにデプロイしていないので、Pod名ではなくホスト名が表示されています。
2.3. GitHubの利用
ソースコードをGitHubに公開しました。gitコマンドとJava 11とDocker(あるいはPodman)が導入されていれば、下記の手順でネイティブのQuarkusアプリケーションをビルド・実行することができます。Linux用のバイナリであるため、CentOSの場合は「./build/quarkus-native-1.0-runner」を直接実行可能です。Gradleも自動的にダウンロードされます。
https://github.com/y-akio/source.git
git clone https://github.com/y-akio/source.git
cd source/quarkus-native
chmod +x gradlew
./gradlew build -D quarkus.package.type=native -D quarkus.native.container-build=true
### 標準出力↓
> Task :quarkusGenerateCode
preparing quarkus application
> Task :quarkusGenerateCodeTests
preparing quarkus application
> Task :quarkusBuild
building quarkus jar
Trying to pull quay.io/quarkus/ubi-quarkus-native-image:21.2-java11...
Getting image source signatures
Copying blob sha256:6f9adeed15adce73e9f73519fb1f314eb58ed175594daabaa944f26b787daa67
Copying blob sha256:c2c17d84f25a8380da8196e4173249e5324e4f653231b00d2e95a6efaac5687a
Copying blob sha256:46cdcde062b27f6c24775848f60eeb6a7216a98d4d6d7617927f5024fc8edfc3
Copying config sha256:2dffe88ed345adf89d68f8a1312bf1a405a4da1b857ba7dc052f1d82640f6bd0
Writing manifest to image destination
Storing signatures
2dffe88ed345adf89d68f8a1312bf1a405a4da1b857ba7dc052f1d82640f6bd0
[quarkus-native-1.0-runner:25] classlist: 3,844.03 ms, 0.96 GB
[quarkus-native-1.0-runner:25] (cap): 580.38 ms, 0.96 GB
[quarkus-native-1.0-runner:25] setup: 2,631.16 ms, 0.96 GB
The bundle named: messages, has not been found. If the bundle is part of a module, verify the bundle name is a fully qualified class name. Otherwise verify the bundle path is acces
sible in the classpath.
09:00:43,724 INFO [org.jbo.threads] JBoss Threads version 3.4.2.Final
[quarkus-native-1.0-runner:25] (clinit): 571.91 ms, 2.95 GB
[quarkus-native-1.0-runner:25] (typeflow): 18,347.68 ms, 2.95 GB
[quarkus-native-1.0-runner:25] (objects): 18,567.88 ms, 2.95 GB
[quarkus-native-1.0-runner:25] (features): 860.43 ms, 2.95 GB
[quarkus-native-1.0-runner:25] analysis: 39,571.10 ms, 2.95 GB
[quarkus-native-1.0-runner:25] universe: 1,631.93 ms, 2.96 GB
[quarkus-native-1.0-runner:25] (parse): 5,390.79 ms, 2.96 GB
[quarkus-native-1.0-runner:25] (inline): 6,300.51 ms, 3.17 GB
[quarkus-native-1.0-runner:25] (compile): 30,355.53 ms, 3.38 GB
[quarkus-native-1.0-runner:25] compile: 44,119.20 ms, 3.38 GB
[quarkus-native-1.0-runner:25] image: 4,570.07 ms, 3.36 GB
[quarkus-native-1.0-runner:25] write: 616.21 ms, 3.36 GB
[quarkus-native-1.0-runner:25] [total]: 97,073.20 ms, 3.36 GB
# Printing build artifacts to: /project/quarkus-native-1.0-runner.build_artifacts.txt
BUILD SUCCESSFUL in 1m 45s
6 actionable tasks: 6 executed
./build/quarkus-native-1.0-runner
### 標準出力↓
__ ____ __ _____ ___ __ ____ ______
--/ __ \/ / / / _ | / _ \/ //_/ / / / __/
-/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/
2021-09-04 04:44:08,871 INFO [io.quarkus] (main) quarkus-native 1.0 native (powered by Quarkus 2.2.1.Final) started in 0.014s. Listening on: http://0.0.0.0:8080
2021-09-04 04:44:08,871 INFO [io.quarkus] (main) Profile prod activated.
2021-09-04 04:44:08,871 INFO [io.quarkus] (main) Installed features: [cdi, qute, resteasy, resteasy-qute, smallrye-context-propagation]
3. OpenShiftへのデプロイ
3.1. コンテナイメージのビルド
OpenShiftでコンテナイメージをビルドします。結果としてイメージストリームが作成されます。
ls -l
### 標準出力
-rw-r--r--. 1 root root 273 9月 4 17:19 Dockerfile
-rw-r--r--. 1 root root 42787352 9月 4 17:31 quarkus-native-1.0-runner
-rw-r--r--. 1 root root 1097 9月 4 17:20 quarkus-native.yaml
oc new-project quarkus-native
oc new-build --name=quarkus-native --strategy=docker --binary
oc start-build quarkus-native --from-dir=. --follow
### 標準出力↓
Uploading directory "." as binary input for the build ...
.
Uploading finished
build.build.openshift.io/quarkus-native-1 started
Receiving source from STDIN as archive ...
Caching blobs under "/var/cache/blobs".
Pulling image registry.access.redhat.com/ubi8/ubi-minimal:8.4 ...
・・・
STEP 1: FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4
STEP 2: RUN mkdir /work
--> bd1f6bd04b5
STEP 3: COPY quarkus-native-1.0-runner /work/
--> 1153e514fc7
STEP 4: RUN chmod +x /work/quarkus-native-1.0-runner
--> 5e90ecd40a9
STEP 5: EXPOSE 8080
--> 4373d6396f7
STEP 6: CMD ["/work/quarkus-native-1.0-runner","-Dquarkus.http.host=0.0.0.0"]
--> 98a1c910c10
STEP 7: ENV "OPENSHIFT_BUILD_NAME"="quarkus-native-1" "OPENSHIFT_BUILD_NAMESPACE"="quarkus-native"
--> 1770b08a74a
STEP 8: LABEL "io.openshift.build.name"="quarkus-native-1" "io.openshift.build.namespace"="quarkus-native"
STEP 9: COMMIT temp.builder.openshift.io/quarkus-native/quarkus-native-1:775e898a
・・・
Successfully pushed image-registry.openshift-image-registry.svc:5000/quarkus-native/quarkus-native@sha256:fcb197e10922265a8e84fe9d184b80064cc4192327860d9a9e60114f1bc7eaf4
Push successful
oc get is
### 標準出力↓
NAME IMAGE REPOSITORY TAGS UPDATED
quarkus-native image-registry.openshift-image-registry.svc:5000/quarkus-native/quarkus-native latest 22 seconds ago
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.4
RUN mkdir /work
COPY quarkus-native-1.0-runner /work/
RUN chmod +x /work/quarkus-native-1.0-runner
EXPOSE 8080
CMD ["/work/quarkus-native-1.0-runner", "-Dquarkus.http.host=0.0.0.0"]
3.2. コンテナのデプロイ
デプロイメント「quarkus-native」としてコンテナをデプロイします。
oc apply -f quarkus-native.yaml
oc get pod,svc,ing
### 標準出力↓
NAME READY STATUS RESTARTS AGE
pod/quarkus-native-1-build 0/1 Completed 0 2m54s
pod/quarkus-native-65d6c4fbd-2zfxz 1/1 Running 0 95s
pod/quarkus-native-65d6c4fbd-b45ns 1/1 Running 0 95s
pod/quarkus-native-65d6c4fbd-z6zbw 1/1 Running 0 95s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/quarkus-native ClusterIP 172.30.233.121 <none> 80/TCP 95s
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress.networking.k8s.io/quarkus-native <none> quarkus-native.apps.ocp.cloud.vpc router-default.apps.ocp.cloud.vpc 80 95s
apiVersion: apps/v1
kind: Deployment
metadata:
name: quarkus-native
labels:
app: quarkus-native
spec:
replicas: 3
selector:
matchLabels:
app: quarkus-native
template:
metadata:
labels:
app: quarkus-native
spec:
containers:
- name: quarkus-native
image: image-registry.openshift-image-registry.svc:5000/quarkus-native/quarkus-native
ports:
- containerPort: 8080
readinessProbe:
httpGet:
path: /healthz
port: 8080
---
apiVersion: v1
kind: Service
metadata:
name: quarkus-native
spec:
selector:
app: quarkus-native
type: ClusterIP
ports:
- protocol: TCP
port: 80
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: quarkus-native
spec:
rules:
- host: quarkus-native.apps.ocp.cloud.vpc
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: quarkus-native
port:
number: 80
ブラウザでhttp://quarkus-native.apps.ocp.cloud.vpc/index
を開くと以下のような画面が表示されます。端末で実行した時と違いPod名が表示されています。