Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Quarkus native build support #2536

Merged
merged 37 commits into from
Sep 9, 2021
Merged

feat: Quarkus native build support #2536

merged 37 commits into from
Sep 9, 2021

Conversation

astefanutti
Copy link
Member

@astefanutti astefanutti commented Jul 29, 2021

This PR adds Quarkus native build support.

I've tested it successfully on Minikube, CRC and OpenShift @ IBM Cloud, with a simple YAML Integration:

2021-07-29 10:20:37,254 INFO  [org.apa.cam.k.Runtime] (main) Apache Camel K Runtime 1.8.0
2021-07-29 10:20:37,254 INFO  [org.apa.cam.qua.cor.CamelBootstrapRecorder] (main) Bootstrap runtime: org.apache.camel.quarkus.main.CamelMainRuntime
2021-07-29 10:20:37,255 INFO  [org.apa.cam.k.lis.SourcesConfigurer] (main) Loading routes from: SourceDefinition{name='camel-k-embedded-flow', language='yaml', type='source', location='file:/etc/camel/sources/camel-k-embedded-flow.yaml', }
2021-07-29 10:20:37,259 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main) Routes startup summary (total:1 started:1)
2021-07-29 10:20:37,259 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main)     Started route1 (timer://tick)
2021-07-29 10:20:37,259 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main) Apache Camel 3.11.0 (camel-1) started in 2ms (build:0ms init:1ms start:1ms)
2021-07-29 10:20:37,259 INFO  [io.quarkus] (main) camel-k-integration 1.6.0-SNAPSHOT native (powered by Quarkus 2.0.0.Final) started in 0.025s. 
2021-07-29 10:20:37,259 INFO  [io.quarkus] (main) Profile prod activated. 
2021-07-29 10:20:37,259 INFO  [io.quarkus] (main) Installed features: [camel-bean, camel-core, camel-k-core, camel-k-runtime, camel-log, camel-support-common, camel-timer, camel-yaml-dsl, cdi]
2021-07-29 10:20:38,260 INFO  [info] (Camel (camel-1) thread #0 - timer://tick) Exchange[ExchangePattern: InOnly, BodyType: String, Body: Hello Yaml !!!]

The first build duration is about 6m36s (including Maven dependencies download):

$ kubectl get builds.camel.apache.org
NAME                       PHASE       AGE     STARTED   DURATION          ATTEMPTS
kit-c41sr2vfurqkciod4mtg   Succeeded   9m25s   9m25s     6m36.745787046s

Also the build image size is reduced to 73.2MB when using the quay.io/quarkus/quarkus-distroless-image base image.

TODO:

  • Proper configurability (Quarkus trait)
  • Disable the JVM trait automatically
  • Disable incremental image build automatically
  • Support all build strategies
  • Parallel build scheduling
  • Add package type tag to build metrics
  • Prioritized multi-kit support
  • Automatic rollout from JVM to native kit
  • e2e tests
  • Documentation

Fixes #991.

Release Note

feat: Quarkus native build support

@astefanutti
Copy link
Member Author

astefanutti commented Jul 30, 2021

Tested on OCP 4.7.19, hosted on IBM Cloud, for a simple YAML Integration:

The build time (including Maven dependencies download) is around 6m36s:

$ kubectl get builds.camel.apache.org
NAME                       PHASE       AGE     STARTED   DURATION          ATTEMPTS
kit-c41sr2vfurqkciod4mtg   Succeeded   9m25s   9m25s     6m36.745787046s   

Operator Pod, running the native build:

Screenshot 2021-07-30 at 12 21 12

Integration Pod (Native):

Screenshot 2021-07-30 at 12 15 02

Integration Pod (JVM):

Screenshot 2021-07-30 at 11 44 08

@lburgazzoli
Copy link
Contributor

@astefanutti adding js to the mix is gogin to require much more time and resources, I remember it required ~12 GB

btw, we can probably add the required additional-build-args as part of the build step for the js dsl

@astefanutti
Copy link
Member Author

@astefanutti adding js to the mix is gogin to require much more time and resources, I remember it required ~12 GB

btw, we can probably add the required additional-build-args as part of the build step for the js dsl

Yes, I've noticed the build time increases by a couple of minutes. I agree the --language:js build configuration should only be added for the JS DSL. I'm doing a bunch of preliminary tests to sample the space, before working on the complete implementation. I've also noticed some issues with the YAML DSL, like with simple: "${body.toUpperCase()}". I'll report them here.

@davsclaus
Copy link
Contributor

Whao nice to see.

So all the extra memory and cpu you pay for the native build, is maybe saved after 1000 years of running ;)

@astefanutti
Copy link
Member Author

I've just redone the same test after upgrading to GraalVM 21.2.0, using the quay.io/quarkus/ubi-quarkus-native-image:21.2.0-java11 image, and the build time is down to 4m34s:

$ kubectl get builds.camel.apache.org
NAME                       PHASE       AGE     STARTED   DURATION          ATTEMPTS
kit-c41t7vodp88kihuqro0g   Succeeded   6m44s   6m44s     4m34.391482247s   

Operator Pod:

Screenshot 2021-07-30 at 12 36 56

We'll have to do some more testing to see whether that's consistent. There may also be some ways to speed up the build, like scaling vertically.

@lburgazzoli
Copy link
Contributor

@astefanutti adding js to the mix is gogin to require much more time and resources, I remember it required ~12 GB
btw, we can probably add the required additional-build-args as part of the build step for the js dsl

Yes, I've noticed the build time increases by a couple of minutes. I agree the --language:js build configuration should only be added for the JS DSL. I'm doing a bunch of preliminary tests to sample the space, before working on the complete implementation. I've also noticed some issues with the YAML DSL, like with simple: "${body.toUpperCase()}". I'll report them here.

Yeah, this could be because the type of the body, should be registered for reflection, as far as I remember adding something like:

quarkus.camel.native.reflection.include-patterns = java.lang.String

Should fix the issue

@johnpoth
Copy link
Member

Whao nice to see.

So all the extra memory and cpu you pay for the native build, is maybe saved after 1000 years of running ;)

may be worth adding a log or something about resource usage when doing native builds :D

@davsclaus
Copy link
Contributor

@astefanutti adding js to the mix is gogin to require much more time and resources, I remember it required ~12 GB
btw, we can probably add the required additional-build-args as part of the build step for the js dsl

Yes, I've noticed the build time increases by a couple of minutes. I agree the --language:js build configuration should only be added for the JS DSL. I'm doing a bunch of preliminary tests to sample the space, before working on the complete implementation. I've also noticed some issues with the YAML DSL, like with simple: "${body.toUpperCase()}". I'll report them here.

Yeah, this could be because the type of the body, should be registered for reflection, as far as I remember adding something like:

quarkus.camel.native.reflection.include-patterns = java.lang.String

Should fix the issue

Or if we get support for csimple with camel-k, then it can be compiled as plain java code, like the Java DSL.

@astefanutti
Copy link
Member Author

Whao nice to see.
So all the extra memory and cpu you pay for the native build, is maybe saved after 1000 years of running ;)

may be worth adding a log or something about resource usage when doing native builds :D

I wanted to add a quantum node to the cluster to achieve a quantum Quarkus native build, but I did not found the option. Maybe it's somewhere and somewhere else at the same time, like I need to solve the wave function 😄.

@lburgazzoli
Copy link
Contributor

the --language:js build configuration should only be added for the JS DSL.

apache/camel-quarkus#2964

@astefanutti
Copy link
Member Author

astefanutti commented Jul 30, 2021

@astefanutti adding js to the mix is gogin to require much more time and resources, I remember it required ~12 GB
btw, we can probably add the required additional-build-args as part of the build step for the js dsl

Yes, I've noticed the build time increases by a couple of minutes. I agree the --language:js build configuration should only be added for the JS DSL. I'm doing a bunch of preliminary tests to sample the space, before working on the complete implementation. I've also noticed some issues with the YAML DSL, like with simple: "${body.toUpperCase()}". I'll report them here.

Yeah, this could be because the type of the body, should be registered for reflection, as far as I remember adding something like:

quarkus.camel.native.reflection.include-patterns = java.lang.String

Should fix the issue

I'm still getting:

error processing exchange. Exchange[5FD5BA4206A71CA-000000000000001F]. Caused by: [org.apache.camel.language.bean.RuntimeBeanExpressionException - Failed to invoke method: toUpperCase() on null due to: org.apache.camel.component.bean.MethodNotFoundException: Method with name: toUpperCase() not found on bean: Hello Yaml !!! of type: java.lang.String on the exchange: Exchange[5FD5BA4206A71CA-000000000000001F]]: org.apache.camel.language.bean.RuntimeBeanExpressionException: Failed to invoke method: toUpperCase() on null due to: org.apache.camel.component.bean.MethodNotFoundException: Method with name: toUpperCase() not found on bean: Hello Yaml !!! of type: java.lang.String on the exchange: Exchange[5FD5BA4206A71CA-000000000000001F]
	at org.apache.camel.language.bean.BeanExpression.invokeOgnlMethod(BeanExpression.java:453)
	at org.apache.camel.language.bean.BeanExpression.evaluate(BeanExpression.java:199)
	at org.apache.camel.language.bean.BeanExpression.evaluate(BeanExpression.java:214)
	at org.apache.camel.language.simple.SimpleExpressionBuilder$28.evaluate(SimpleExpressionBuilder.java:757)
	at org.apache.camel.support.ExpressionAdapter.evaluate(ExpressionAdapter.java:45)
	at org.apache.camel.processor.TransformProcessor.process(TransformProcessor.java:47)
	at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$SimpleTask.run(RedeliveryErrorHandler.java:463)
	at org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.schedule(DefaultReactiveExecutor.java:179)
	at org.apache.camel.impl.engine.DefaultReactiveExecutor.scheduleMain(DefaultReactiveExecutor.java:64)
	at org.apache.camel.processor.Pipeline.process(Pipeline.java:184)
	at org.apache.camel.impl.engine.CamelInternalProcessor.process(CamelInternalProcessor.java:398)
	at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:210)
	at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:76)
	at java.util.TimerThread.mainLoop(Timer.java:556)
	at java.util.TimerThread.run(Timer.java:506)
	at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:567)
	at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192)
Caused by: org.apache.camel.component.bean.MethodNotFoundException: Method with name: toUpperCase() not found on bean: Hello Yaml !!! of type: java.lang.String on the exchange: Exchange[5FD5BA4206A71CA-000000000000001F]
	at org.apache.camel.component.bean.BeanInfo.createInvocation(BeanInfo.java:263)
	at org.apache.camel.component.bean.AbstractBeanProcessor.process(AbstractBeanProcessor.java:126)
	at org.apache.camel.impl.engine.DefaultAsyncProcessorAwaitManager.process(DefaultAsyncProcessorAwaitManager.java:83)
	at org.apache.camel.support.AsyncProcessorSupport.process(AsyncProcessorSupport.java:41)
	at org.apache.camel.language.bean.BeanExpression.invokeBean(BeanExpression.java:347)
	at org.apache.camel.language.bean.BeanExpression.invokeOgnlMethod(BeanExpression.java:449)
	... 16 more

But maybe I've added the build property in the wrong place...

@lburgazzoli
Copy link
Contributor

lburgazzoli commented Jul 30, 2021

Yes, I've noticed the build time increases by a couple of minutes. I agree the --language:js build configuration should only be added for the JS DSL. I'm doing a bunch of preliminary tests to sample the space, before working on the complete implementation. I've also noticed some issues with the YAML DSL, like with simple: "${body.toUpperCase()}". I'll report them here.

Yeah, this could be because the type of the body, should be registered for reflection, as far as I remember adding something like:

quarkus.camel.native.reflection.include-patterns = java.lang.String

Should fix the issue

I'm still getting:

error processing exchange. Exchange[5FD5BA4206A71CA-000000000000001F]. Caused by: [org.apache.camel.language.bean.RuntimeBeanExpressionException - Failed to invoke method: toUpperCase() on null due to: org.apache.camel.component.bean.MethodNotFoundException: Method with name: toUpperCase() not found on bean: Hello Yaml !!! of type: java.lang.String on the exchange: Exchange[5FD5BA4206A71CA-000000000000001F]]: org.apache.camel.language.bean.RuntimeBeanExpressionException: Failed to invoke method: toUpperCase() on null due to: org.apache.camel.component.bean.MethodNotFoundException: Method with name: toUpperCase() not found on bean: Hello Yaml !!! of type: java.lang.String on the exchange: Exchange[5FD5BA4206A71CA-000000000000001F]
	at org.apache.camel.language.bean.BeanExpression.invokeOgnlMethod(BeanExpression.java:453)
	at org.apache.camel.language.bean.BeanExpression.evaluate(BeanExpression.java:199)
	at org.apache.camel.language.bean.BeanExpression.evaluate(BeanExpression.java:214)
	at org.apache.camel.language.simple.SimpleExpressionBuilder$28.evaluate(SimpleExpressionBuilder.java:757)
	at org.apache.camel.support.ExpressionAdapter.evaluate(ExpressionAdapter.java:45)
	at org.apache.camel.processor.TransformProcessor.process(TransformProcessor.java:47)
	at org.apache.camel.processor.errorhandler.RedeliveryErrorHandler$SimpleTask.run(RedeliveryErrorHandler.java:463)
	at org.apache.camel.impl.engine.DefaultReactiveExecutor$Worker.schedule(DefaultReactiveExecutor.java:179)
	at org.apache.camel.impl.engine.DefaultReactiveExecutor.scheduleMain(DefaultReactiveExecutor.java:64)
	at org.apache.camel.processor.Pipeline.process(Pipeline.java:184)
	at org.apache.camel.impl.engine.CamelInternalProcessor.process(CamelInternalProcessor.java:398)
	at org.apache.camel.component.timer.TimerConsumer.sendTimerExchange(TimerConsumer.java:210)
	at org.apache.camel.component.timer.TimerConsumer$1.run(TimerConsumer.java:76)
	at java.util.TimerThread.mainLoop(Timer.java:556)
	at java.util.TimerThread.run(Timer.java:506)
	at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:567)
	at com.oracle.svm.core.posix.thread.PosixJavaThreads.pthreadStartRoutine(PosixJavaThreads.java:192)
Caused by: org.apache.camel.component.bean.MethodNotFoundException: Method with name: toUpperCase() not found on bean: Hello Yaml !!! of type: java.lang.String on the exchange: Exchange[5FD5BA4206A71CA-000000000000001F]
	at org.apache.camel.component.bean.BeanInfo.createInvocation(BeanInfo.java:263)
	at org.apache.camel.component.bean.AbstractBeanProcessor.process(AbstractBeanProcessor.java:126)
	at org.apache.camel.impl.engine.DefaultAsyncProcessorAwaitManager.process(DefaultAsyncProcessorAwaitManager.java:83)
	at org.apache.camel.support.AsyncProcessorSupport.process(AsyncProcessorSupport.java:41)
	at org.apache.camel.language.bean.BeanExpression.invokeBean(BeanExpression.java:347)
	at org.apache.camel.language.bean.BeanExpression.invokeOgnlMethod(BeanExpression.java:449)
	... 16 more

But maybe I've added the build property in the wrong place...

There is a chance that java.lang. classes are not included as not found by jandex, @ppalaga may know more

@astefanutti
Copy link
Member Author

Here is the exception that's thrown at start time, with a Java Integration:

$ kamel run examples/rest/RestDSL.java

ERROR [org.apa.cam.qua.mai.CamelMainRuntime] (main) Failed to start application: org.apache.camel.RuntimeCamelException: org.joor.ReflectException: Error while compiling RestDSL
	at org.apache.camel.k.support.SourcesSupport.load(SourcesSupport.java:170)
	at org.apache.camel.k.support.SourcesSupport.loadSources(SourcesSupport.java:84)
	at org.apache.camel.k.listener.SourcesConfigurer.accept(SourcesConfigurer.java:80)
	at org.apache.camel.k.listener.AbstractPhaseListener.accept(AbstractPhaseListener.java:32)
	at org.apache.camel.k.quarkus.Application$ListenerAdapter.invokeListeners(Application.java:135)
	at org.apache.camel.k.quarkus.Application$ListenerAdapter.beforeConfigure(Application.java:97)
	at org.apache.camel.main.BaseMainSupport.postProcessCamelContext(BaseMainSupport.java:531)
	at org.apache.camel.quarkus.main.CamelMain.initCamelContext(CamelMain.java:116)
	at org.apache.camel.quarkus.main.CamelMain.doInit(CamelMain.java:86)
	at org.apache.camel.support.service.BaseService.init(BaseService.java:83)
	at org.apache.camel.quarkus.main.CamelMain.startEngine(CamelMain.java:137)
	at org.apache.camel.quarkus.main.CamelMainRuntime.start(CamelMainRuntime.java:49)
	at org.apache.camel.quarkus.core.CamelBootstrapRecorder.start(CamelBootstrapRecorder.java:45)
	at io.quarkus.deployment.steps.CamelBootstrapProcessor$boot-173480958.deploy_0(CamelBootstrapProcessor$boot-173480958.zig:101)
	at io.quarkus.deployment.steps.CamelBootstrapProcessor$boot-173480958.deploy(CamelBootstrapProcessor$boot-173480958.zig:40)
	at io.quarkus.runner.ApplicationImpl.doStart(ApplicationImpl.zig:799)
	at io.quarkus.runtime.Application.start(Application.java:101)
	at io.quarkus.runtime.ApplicationLifecycleManager.run(ApplicationLifecycleManager.java:101)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:66)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:42)
	at io.quarkus.runtime.Quarkus.run(Quarkus.java:119)
	at io.quarkus.runner.GeneratedMain.main(GeneratedMain.zig:29)
Caused by: org.joor.ReflectException: Error while compiling RestDSL
	at org.joor.Compile.compile(Compile.java:158)
	at org.joor.Reflect.compile(Reflect.java:104)
	at org.joor.Reflect.compile(Reflect.java:79)
	at org.apache.camel.dsl.java.joor.JavaRoutesBuilderLoader.doLoadRouteBuilder(JavaRoutesBuilderLoader.java:50)
	at org.apache.camel.dsl.support.RouteBuilderLoaderSupport.loadRoutesBuilder(RouteBuilderLoaderSupport.java:59)
	at org.apache.camel.impl.engine.DefaultRoutesLoader.findRoutesBuilders(DefaultRoutesLoader.java:100)
	at org.apache.camel.spi.RoutesLoader.findRoutesBuilders(RoutesLoader.java:66)
	at org.apache.camel.k.support.SourcesSupport.load(SourcesSupport.java:163)
	... 21 more
Caused by: java.lang.NullPointerException
	at org.joor.Compile.compile(Compile.java:66)
	... 28 more

I'm not sure JOOR is supposed to work in combination of native builds.

@lburgazzoli
Copy link
Contributor

I'm not sure JOOR is supposed to work in combination of native builds.

no, only xml, yaml, js are supposed to work in native mode

@ppalaga
Copy link

ppalaga commented Jul 30, 2021

There is a chance that java.lang. classes are not included as not found by jandex, @ppalaga may know more

Yep, that's perhaps the case. Could you try to register via @RegisterForReflection(targets={ String.class}) in Java? https://quarkus.io/guides/writing-native-applications-tips#registering-for-reflection

@astefanutti
Copy link
Member Author

There is a chance that java.lang. classes are not included as not found by jandex, @ppalaga may know more

Yep, that's perhaps the case. Could you try to register via @RegisterForReflection(targets={ String.class}) in Java? https://quarkus.io/guides/writing-native-applications-tips#registering-for-reflection

Thanks, I'm going to try quarkus.native.additional-build-args =-H:ReflectionConfigurationFiles=reflection-config.json as described later in the documentation.

@astefanutti
Copy link
Member Author

astefanutti commented Aug 2, 2021

So generating the following file during the project generation src/main/resources/reflection-config.json:

[
  {
    "name" : "java.lang.String",
    "allPublicMethods" : true
  }
]

And adding the quarkus.native.additional-build-args=-H:ReflectionConfigurationFiles=reflection-config.json build property works:

$ kamel run examples/languages/routes.yaml --logs
Integration "routes" created
Condition "IntegrationPlatformAvailable" is "True" for Integration routes: astefanu/camel-k
Integration routes in phase "Initialization"
Integration routes in phase "Building Kit"
Condition "IntegrationKitAvailable" is "False" for Integration routes: creating a new integration kit
Integration routes subresource kit-c43sgj6vs72714dt3jsg (Integration Kit) changed phase to "Build Submitted"
Integration routes subresource kit-c43sgj6vs72714dt3jsg (Build) changed phase to "Scheduling"
Integration routes subresource kit-c43sgj6vs72714dt3jsg (Build) changed phase to "Pending"
Integration routes subresource kit-c43sgj6vs72714dt3jsg (Build) changed phase to "Running"
Integration routes subresource kit-c43sgj6vs72714dt3jsg (Integration Kit) changed phase to "Build Running"
Integration routes subresource kit-c43sgj6vs72714dt3jsg (Build) changed phase to "Succeeded"
Integration routes subresource kit-c43sgj6vs72714dt3jsg (Integration Kit) changed phase to "Ready"
Condition "IntegrationKitAvailable" is "True" for Integration routes: kit-c43sgj6vs72714dt3jsg
Integration routes in phase "Deploying"
Condition "DeploymentAvailable" is "True" for Integration routes: deployment name is routes
Condition "CronJobAvailable" is "False" for Integration routes: different controller strategy used (deployment)
Condition "KnativeServiceAvailable" is "False" for Integration routes: different controller strategy used (deployment)
Integration routes in phase "Running"
Condition "Ready" is "False" for Integration routes
[1] Monitoring pod routes-66886d9564-swf9n
[1] 2021-08-02 10:30:48,198 INFO  [org.apa.cam.k.Runtime] (main) Apache Camel K Runtime 1.8.0
[1] 2021-08-02 10:30:48,199 INFO  [org.apa.cam.qua.cor.CamelBootstrapRecorder] (main) Bootstrap runtime: org.apache.camel.quarkus.main.CamelMainRuntime
[1] 2021-08-02 10:30:48,199 INFO  [org.apa.cam.k.lis.SourcesConfigurer] (main) Loading routes from: SourceDefinition{name='camel-k-embedded-flow', language='yaml', type='source', location='file:/etc/camel/sources/camel-k-embedded-flow.yaml', }
[1] 2021-08-02 10:30:48,203 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main) Routes startup summary (total:1 started:1)
[1] 2021-08-02 10:30:48,203 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main)     Started route1 (timer://tick)
[1] 2021-08-02 10:30:48,203 INFO  [org.apa.cam.imp.eng.AbstractCamelContext] (main) Apache Camel 3.11.0 (camel-1) started in 1ms (build:0ms init:1ms start:0ms)
[1] 2021-08-02 10:30:48,204 INFO  [io.quarkus] (main) camel-k-integration 1.6.0-SNAPSHOT native (powered by Quarkus 2.0.0.Final) started in 0.012s. 
[1] 2021-08-02 10:30:48,204 INFO  [io.quarkus] (main) Profile prod activated. 
[1] 2021-08-02 10:30:48,204 INFO  [io.quarkus] (main) Installed features: [camel-bean, camel-core, camel-k-core, camel-k-runtime, camel-log, camel-support-common, camel-timer, camel-yaml-dsl, cdi]
Condition "Ready" is "True" for Integration routes
[1] 2021-08-02 10:30:49,205 INFO  [info] (Camel (camel-1) thread #0 - timer://tick) Exchange[ExchangePattern: InOnly, BodyType: String, Body: HELLO YAML !!!]
[1] 2021-08-02 10:30:54,203 INFO  [info] (Camel (camel-1) thread #0 - timer://tick) Exchange[ExchangePattern: InOnly, BodyType: String, Body: HELLO YAML !!!]
[1] 2021-08-02 10:30:59,203 INFO  [info] (Camel (camel-1) thread #0 - timer://tick) Exchange[ExchangePattern: InOnly, BodyType: String, Body: HELLO YAML !!!]

The native build duration seems stable around 4m30s:

$ kubectl get builds.camel.apache.org
NAME                       PHASE       AGE     STARTED   DURATION          ATTEMPTS
kit-c43s8si2qa460fs1tumg   Succeeded   4m53s   4m53s     4m28.894542898s 

The reflection-config.json build configuration file may eventually be generated based on language inspection. Also, it seems there is no need for quarkus.camel.native.reflection.include-patterns in that case.

@lburgazzoli
Copy link
Contributor

lburgazzoli commented Aug 2, 2021

The reflection-config.json build configuration file may eventually be generated based on language inspection. Also, it seems there is no need for quarkus.camel.native.reflection.include-patterns in that case.

This is a topic we have since the beginning of the camel-quarkus project :) however based on experience, is not always possible to determine the classes that have to be registered for reflection so I think quarkus.camel.native.reflection.include-patterns should work also for this case.

@ppalaga I think that if the class the pattern given to the quarkus.camel.native.reflection.include-patterns is not a regular/ant expression, the value should be taken as it is, WDYT?

@ppalaga
Copy link

ppalaga commented Aug 2, 2021

I have filed apache/camel-quarkus#2969 and apache/camel-quarkus#2970

@ppalaga
Copy link

ppalaga commented Aug 2, 2021

There is a PR apache/camel-quarkus#2973 for apache/camel-quarkus#2970

@astefanutti
Copy link
Member Author

astefanutti commented Aug 3, 2021

It turns out Mandrel does not include any polyglot support, so the --language:<languageId> option is not available: https://github.com/graalvm/mandrel#how-does-mandrel-differ-from-graal.

I've successfully built a YAML Integration with the quay.io/quarkus/ubi-quarkus-mandrel:21.2.0.0-Final-java11 image as operator/builder image.

On the pros side, the Mandrel image is almost half the size that of GraalVM, that is around 780MB/323MB (uncompressed/compressed). That adds about 300MB (uncompressed) compared to the existing operator image. The cons is that it doesn't support building polyglot integrations, but the toll is so important w.r.t. memory requirement and build duration, that we may not want to support it anyway.

Noted that Java would not be supported in any case as JOOR is used for compilation at start time.

@ppalaga
Copy link

ppalaga commented Aug 4, 2021

@astefanutti does that mean that the js-dsl extension is not that important for you anymore?

@astefanutti
Copy link
Member Author

@ppalaga I'd say polyglot native build has too high a price at the moment to be included: +700MB operator/builder image size, x2 memory requirement, x2 build duration. I'm inclined to favour Mandrel, as it targets Quarkus specifically, and works with the YAML DSL.

@davsclaus
Copy link
Contributor

yeah focus on native for kamelets via yaml - and then later java pre-compiled (not to use joor) and also pre compiled csimple language to reduce overhead

@astefanutti astefanutti added kind/feature New feature or request area/build-operator Related to the internal image build operator area/quarkus Related to the Quarkus runtime labels Aug 19, 2021
@astefanutti astefanutti marked this pull request as ready for review September 1, 2021 14:05
@astefanutti
Copy link
Member Author

I think it's ready. Users can use the quarkus.package-type option to run native integration, e.g.:

$ kamel run -t quarkus.package-type=native

It's possible to run both standard and native builds in parallel, and have a rollout deployment to the native one when it's ready, by running:

$ kamel run -t quarkus.package-type=fast-jar -t quarkus.package-type=native

A native build of a simple REST integration completes successfully in about 5 minutes on a typical node, inside a Pod with 4GiB memory limit.

The good news is native build e2e tests pass in GH Actions hosted VMs, that have only 7GB of memory.

The native integration base image is set to quay.io/quarkus/quarkus-distroless-image, which reduces the average image size, and contributes to faster startup time.

For the implementation / technical details, this PR:

  • Switches the operator base image to quay.io/quarkus/ubi-quarkus-mandrel:21.2.0.0-Final-java11 to inherit the Mandrel build stack, however the operator image can still be built on plain JDK base image, and it'll work just as before if needed. Using Mandrel implies that native compilation is only supported for YAML and XML integrations.
  • Upgrades Maven to version 3.8.2, to benefit from Concurrent-safe access to local Maven repository, and enable parallel builds.
  • Switches from zip to tar.gz archive for S2I to preserve file permissions.
  • Introduces multi-kit matching for integrations, whose resolution is ordered by the camel.apache.org/kit.priority label, as a solution to implement the automatic rollout deployment from the JVM to the native kit.
  • Adds the type label to build metrics, so that it's possible to distinguish between standard and native builds.

A couple of improvements may be done later:

  • Configuration of the native image base image (it's currently hard-coded)
  • Per build type configuration: some parameters, like the build timeout, may be better configured per build type.

@davsclaus
Copy link
Contributor

Is there documentation included, if not I think we should have a new page in the docs about this (also if we have in some trait, then this functionality does not stand out, and it may be worth to have a new "build" page (or better name) that introduces this, and can link to the trait for more details etc).

@astefanutti
Copy link
Member Author

Is there documentation included, if not I think we should have a new page in the docs about this (also if we have in some trait, then this functionality does not stand out, and it may be worth to have a new "build" page (or better name) that introduces this, and can link to the trait for more details etc).

Right, it's only documented in the Quarkus trait page at the moment, so it's a bit "hidden". I need to think a bit to find what would be the best place. There are a couple of other build-related pages in the Configuration section, so that may be a good place, and have all them grouped under a Build section with a new page about the native build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/build-operator Related to the internal image build operator area/quarkus Related to the Quarkus runtime kind/feature New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

support for quarkus in native mode
5 participants