This commit is contained in:
jamesfalkner
2019-07-29 18:05:27 -04:00
parent 87bffc039b
commit 0f02f2ee18
10 changed files with 85 additions and 39 deletions

View File

@@ -127,7 +127,7 @@ hello quarkus-on-openshift from people-1-9sgsm
Your hostname (the Kubernetes _pod_ in which your app runs) name will be different from the above.
====
So now our app is deployed to OpenShift. You can also see it in the {{ CONSOLE_URL}}[OpenShift Console,target="_blank"]. Login with your assigned username and password (e.g. `user4/pass4`):
So now our app is deployed to OpenShift. You can also see it in the {{ CONSOLE_URL}}[OpenShift Console,window=_blank]. Login with your assigned username and password (e.g. `user4/pass4`):
image::ocplogin.png[login,600]
@@ -157,12 +157,11 @@ You can click on the route link to open up the default Quarkus page that's packa
== Connect MicroProfile health check
Earlier you implemented a series of MicroProfile health checks. To make OpenShift aware of these available health checks and begin using them, run the following two commands:
Earlier you implemented a series of MicroProfile health checks. To make OpenShift aware of these available health checks and begin using them, run the following commands:
[source,sh,role="copypaste"]
----
oc set probe dc/people --readiness --initial-delay-seconds=30 --get-url=http://:8080/health/ready
oc set probe dc/people --liveness --initial-delay-seconds=30 --get-url=http://:8080/health/live
oc set probe dc/people --readiness --initial-delay-seconds=30 --get-url=http://:8080/health/ready && oc set probe dc/people --liveness --initial-delay-seconds=30 --get-url=http://:8080/health/live
----
This configures both a _readiness_ probe (is the app initialized and ready to serve requests?) and a _liveness_ probe (is the app still up and ready to serve requests) with default timeouts. OpenShift will not route any traffic to pods that don't respond successfully to these probes. By editing these, it will trigger a new deployment so make sure the app comes up with its new probes in place:
@@ -172,6 +171,8 @@ This configures both a _readiness_ probe (is the app initialized and ready to se
oc rollout status -w dc/people
----
At this point, the probes will be accessed periodically to ensure the app is healthy.
== Congratulations!
This step covered the deployment of a native Quarkus application on OpenShift. However, there is much more, and the integration with these cloud native platforms (through health checks, configuration management, and monitoring which we'll cover later) has been tailored to make Quarkus applications execution very smooth.

View File

@@ -45,7 +45,7 @@ The result of this command is a new `<dependency>` added to our `pom.xml` which
[source,sh,role="copypaste"]
----
git diff pom.xml
git --no-pager diff pom.xml
----
[source, none]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 239 KiB

View File

@@ -295,11 +295,16 @@ Our application should be up and running in a few seconds after the build comple
[source,sh,role="copypaste"]
----
clear; echo; echo http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/names.html ; echo
echo; echo http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/names.html ; echo
----
Open a separate browser tab and go to that URL and you should see a cloud of names updating every 5 seconds (it may take a few seconds for it to start!):
[NOTE]
====
It takes a few seconds to establish the connection to Kafka. If you don't see new names generated every 5 seconds, reload the browser page to re-initialize the SSE stream.
====
image::names.png[names,800]
These are the original names streamed through Kafka, altered to add a random honorific like "Sir" or "Madam", and displayed in a "word cloud" for you to enjoy!

View File

@@ -96,7 +96,14 @@ With our endpoint, confirm it fails using the Terminal to execute:
curl -X POST http://localhost:8080/person/joe
----
**This will fail** with a hang/timeout - we posted the message to the Vert.x event bus, but there's nothing to receive it!
**This will fail** with an `Internal Server Error`. If you look at the Live Coding terminal, you'll also see the reason:
[source,none]
----
ERROR [org.jbo.res.res.i18n] (executor-thread-1) RESTEASY002020: Unhandled asynchronous exception, sending back 500: (NO_HANDLERS,-1) No handlers for address add-person
----
We posted the message to the Vert.x event bus at the `add-person` address, but there's nothing to receive it!
== Create consumer
@@ -150,7 +157,7 @@ You should get back the name you put in (`joe`). Now let's confirm Joe is presen
[source,sh,role="copypaste"]
----
curl http://localhost:8080/person/name/joe | jq
curl -s http://localhost:8080/person/name/joe | jq
----
You should get back Joe!

View File

@@ -191,14 +191,14 @@ You'll need to trigger the methods that we've instrumented, so first run this co
[source,sh,role="copypaste"]
----
echo http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/names.html
echo; echo http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/names.html ; echo
----
Within about 15-30 seconds, Prometheus should start scraping the metrics. Run this command to output the URL to the Prometheus GUI:
[source,sh,role="copypaste"]
----
echo http://$(oc get route prometheus -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})
echo; echo http://$(oc get route prometheus -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %}) ; echo
----
Open a separate browser tab and navigate to that URL. This is the Prometheus GUI which lets you issue queries to retrieve metrics Prometheus has gathered. Start typing in the query box to look for 'acme':
@@ -258,7 +258,7 @@ Obtain the URL to the Grafana dashboard using this command:
[source,sh,role="copypaste"]
----
echo http://$(oc get route grafana -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})
echo; echo http://$(oc get route grafana -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %}) ; echo
----
Open that URL in your browser, and login using the default credentails:

View File

@@ -165,7 +165,7 @@ Add a few annotations:
[source,java,role="copypaste"]
----
@Parameter(description = "Cutoff year for searching for people", required = true)
@Parameter(description = "Cutoff year for searching for people", required = true, name="year")
----
Again, _Assistant > Organize Imports_ (and make sure to import the right `Parameter` class: `org.eclipse.microprofile.openapi.annotations.parameters.Parameter`)
@@ -187,7 +187,7 @@ The final method should look like:
@Path("/birth/before/{year}")
@Produces(MediaType.APPLICATION_JSON)
public List<Person> getBeforeYear(
@Parameter(description = "Cutoff year for searching for people", required = true)
@Parameter(description = "Cutoff year for searching for people", required = true, name="year")
@PathParam(value = "year") int year) {
return Person.getBeforeYear(year);

View File

@@ -193,7 +193,7 @@ With the app running, let's try out our first RESTful endpoint to retrieve all t
[source,sh,role="copypaste"]
----
curl http://localhost:8080/person | jq
curl -s http://localhost:8080/person | jq
----
We call the endpoint with `curl` then send the output through `jq` to make the output prettier. You should see:
@@ -224,6 +224,8 @@ We call the endpoint with `curl` then send the output through `jq` to make the o
It's working! We'll leave it running and use Quarkus' Live Reload feature to automatically update our app as we make changes. Note that the `id` and `persistent` fields were added to our entity, but never appear in our query APIs and can be safely ignored most of the time.
[NOTE]
====
Advanced use cases may require a custom ID strategy, which can by done by extending `PanacheEntityBase` instead of `PanacheEntity`, and declaring a public `id` field with the necessary policy. For example (do not copy this code into your app):
[source,java]
@@ -237,6 +239,7 @@ Advanced use cases may require a custom ID strategy, which can by done by extend
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "personSequence")
public Integer id;
----
====
== Add Basic Queries
@@ -291,7 +294,7 @@ Check that it works as expected by testing the new endpoints. Let's find all the
[source,sh,role="copypaste"]
----
curl http://localhost:8080/person/eyes/BLUE | jq
curl -s http://localhost:8080/person/eyes/BLUE | jq
----
You should only see **one** person with BLUE eyes:
@@ -313,7 +316,7 @@ And let's find people born in 1990 or earlier:
[source,sh,role="copypaste"]
----
curl http://localhost:8080/person/birth/before/1990 | jq
curl -s http://localhost:8080/person/birth/before/1990 | jq
----
You should see **two** people born in 1990 or earlier:
@@ -386,7 +389,7 @@ Open the `PersonResource` resource class and add the following code below the `/
Here we are using JAX-RS `@QueryParam` values to specify the incoming parameters and be able to use them when the frontend calls the `GET /person/datatable` endpoint.
We'll fill in the `TODO`s to build this method.
We'll fill in the `TODO` comments to build this method.
=== Implement `/datatable` endpoint
@@ -446,7 +449,7 @@ Let's test out our new endpoint using `curl` to search for names with `yan` in t
[source,sh,role="copypaste"]
----
curl "http://localhost:8080/person/datatable?draw=1&start=0&length=10&search\[value\]=yan" | jq
curl -s "http://localhost:8080/person/datatable?draw=1&start=0&length=10&search\[value\]=yan" | jq
----
This should return a single entity (since in our 3-person sample data, only one has `yan` in their name), embedded in the return object that DataTable is expecting (with the `draw`, `recordsFiltered`, `recordsTotal` etc):
@@ -469,6 +472,9 @@ This should return a single entity (since in our 3-person sample data, only one
}
----
The `data`, `draw`, `recordsFiltered` and `recordsTotal` values are what the DataTables frontend will be expecting when it calls this endpoint.
=== Add lifecycle hook
You often need to execute custom actions when the application starts and clean up everything when the application stops. In this case we'll add an action that will pre-generate a lot of fake data.
@@ -502,7 +508,7 @@ Although our lifecycle code is listening for `StartupEvent`, and our application
[source,sh,role="copypaste"]
----
curl "http://localhost:8080/person/datatable?draw=1&start=0&length=2&search\[value\]=F" | jq
curl -s "http://localhost:8080/person/datatable?draw=1&start=0&length=2&search\[value\]=F" | jq
----
[NOTE]
@@ -539,11 +545,11 @@ Note the values for `recordsFiltered` (the number of records with the letter `F`
== Cleanup
We're done with development, so go back to the first Terminal tab and press kbd:[CTRL+C] (or just close the running tab) to stop our running application.
We're done with development, so go back to the Live Coding tab and press kbd:[CTRL+C] (or just close the tab) to stop our running application.
== Deploy Postgres database
In previous steps we deployed our sample application as a native binary. Now let's switch to a JVM-based deployment. Our production environment needs a "real" database so let's deploy a Postgres database. Run the following command to startup a database in our cluster:
Our production environment needs a "real" database so let's deploy a Postgres database. Run the following command to startup a database in our cluster:
[source,sh,role="copypaste"]
----
@@ -555,16 +561,19 @@ oc new-app \
openshift/postgresql
----
You should get a `--> Success` message.
== Rebuild and redeploy app
With our database running, re-build the application as an executable JAR using the command palette and selecting **Create Executable JAR**.
In previous steps we deployed our sample application as a native binary. Now let's switch to a JVM-based deployment from here on out.
Re-build the application as an executable JAR using the command palette and selecting **Create Executable JAR**.
Next, re-define the container build to use the OpenJDK image using these commands:
[source,sh,role="copypaste"]
----
oc delete bc/people
oc new-build registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.5 --binary --name=people
oc delete bc/people && oc new-build registry.access.redhat.com/redhat-openjdk-18/openjdk18-openshift:1.5 --binary --name=people
----
And now start the build using our executable JAR:
@@ -589,7 +598,7 @@ And now we can access using `curl` once again to find everyone born in or before
[source,sh,role="copypaste"]
----
curl $(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/person/birth/before/2000 | jq
curl -s $(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/person/birth/before/2000 | jq
----
Now that we have our app running on OpenShift, let's see what we can do.

View File

@@ -23,7 +23,7 @@ Some configuration of the extension is required. Add this to your `application.p
mp.jwt.verify.publickey.location={{KEYCLOAK_URL}}/auth/realms/quarkus/protocol/openid-connect/certs # <1>
mp.jwt.verify.issuer={{KEYCLOAK_URL}}/auth/realms/quarkus # <2>
quarkus.smallrye-jwt.auth-mechanism=MP-JWT # <3>
quarkus.smallrye-jwt.realmName=quarkus
quarkus.smallrye-jwt.realm-name=quarkus
quarkus.smallrye-jwt.enabled=true
----
<1> Sets public key location for JWT authentication. Keycloak exports this for you at the URL.
@@ -164,12 +164,11 @@ Get a token for user `alice` with this command:
[source,sh,role="copypaste"]
----
export ALICE_TOKEN=$(\
curl -X POST {{KEYCLOAK_URL}}/auth/realms/quarkus/protocol/openid-connect/token \
curl -s -X POST {{KEYCLOAK_URL}}/auth/realms/quarkus/protocol/openid-connect/token \
--user backend-service:secret \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'username=alice&password=alice&grant_type=password' | jq --raw-output '.access_token' \
)
echo $ALICE_TOKEN
) && echo $ALICE_TOKEN
----
This issues a `curl` command to Keycloak (using `backend-service` credentials which is a special user that is allowed acess to the Keycloak REST API), and fetches a token for Alice using their credentials.
@@ -229,11 +228,11 @@ Obtain an Admin token:
[source,sh,role="copypaste"]
----
export ADMIN_TOKEN=$(\
curl -X POST {{KEYCLOAK_URL}}/auth/realms/quarkus/protocol/openid-connect/token \
curl -s -X POST {{KEYCLOAK_URL}}/auth/realms/quarkus/protocol/openid-connect/token \
--user backend-service:secret \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'username=admin&password=admin&grant_type=password' | jq --raw-output '.access_token' \
)
) && echo $ADMIN_TOKEN
----
And try again with your new token:
@@ -310,7 +309,6 @@ Create a new class called `KeycloakResource` in the `org.acme.people.rest` packa
----
package org.acme.people.rest;
import javax.enterprise.context.RequestScoped;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@@ -397,18 +395,18 @@ To access the confidential endpoint, you should obtain an access token for user
[source,sh,role="copypaste"]
----
export JDOE_TOKEN=$(\
curl -X POST {{KEYCLOAK_URL}}/auth/realms/quarkus/protocol/openid-connect/token \
curl -s -X POST {{KEYCLOAK_URL}}/auth/realms/quarkus/protocol/openid-connect/token \
--user backend-service:secret \
-H 'content-type: application/x-www-form-urlencoded' \
-d 'username=jdoe&password=jdoe&grant_type=password' | jq --raw-output '.access_token' \
)
) && echo $JDOE_TOKEN
----
And access the confidential endpoint with your new token:
[source,sh,role="copypaste"]
----
curl -X GET \
curl -i -X GET \
http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/secured/confidential \
-H "Authorization: Bearer $JDOE_TOKEN"
----

View File

@@ -87,7 +87,7 @@ You'll need to trigger some HTTP endpoints to generate traces, so let's re-visit
[source,sh,role="copypaste"]
----
echo http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/datatable.html
echo ; echo http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/datatable.html ; echo
----
Open that URL in a separate browser tab, and exercise the table a bit by paging through the entries to force several RESTful calls back to our app:
@@ -100,7 +100,7 @@ Use this command to generate the URL to Jaeger:
[source,sh,role="copypaste"]
----
echo http://$(oc get route jaeger-query -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})
echo; echo http://$(oc get route jaeger-query -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %}) ; echo
----
Open that URL in a separate tab to arrive at the Jaeger Tracing console (leave the tab open as we'll use it later)
@@ -208,7 +208,31 @@ Having this configuration means that all requests performed using our code will
Note that `org.acme.people.service.StarWarsService` must match the fully qualified name of the StarWarsService interface we created in the previous section.
Using the configuration above, calling the `getPerson(int)` method of StarWarsService with a value of `1` would result in an HTTP GET request being made to `https://swapi.co/api/people/1/` (you can use `curl https://swapi.co/api/people/1/ | jq` to verify this URL produces a real character)
Using the configuration above, calling the `getPerson(int)` method of StarWarsService with a value of `1` would result in an HTTP GET request being made to `https://swapi.co/api/people/1/`. Confirm you can access the Star Wars API using curl:
[source,sh,role="copypaste"]
----
curl -s https://swapi.co/api/people/1/ | jq
----
You should get Luke Skywalker back:
[source,json]
----
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue",
"birth_year": "19BBY",
"gender": "male",
"homeworld": "https://swapi.co/api/planets/1/",
....<more here>....
}
----
=== Final step: add endpoint
@@ -234,6 +258,8 @@ public List<StarWarsPerson> getCharacters() {
<3> For each of the integers, call the `StarWarsService::getPerson` method
<4> Collect the results into a list and return it
Don't forget to _Assistant > Organize Imports_ to import the new classes.
== Test it out
First, re-build the app using the command palette and selecting **Create Executable JAR**. Once that's done, run the following command to re-deploy:
@@ -258,7 +284,7 @@ Try out our new endpoint by running the following command:
[source,sh,role="copypaste"]
----
curl http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/person/swpeople
curl -s http://$(oc get route people -o=go-template --template={% raw %}'{{ .spec.host }}'{% endraw %})/person/swpeople | jq
----
You should see: