Jenkins X Import¶
Awesome, by now we have a Kubernetes cluster with Jenkins X, HashiCorp Vault for secrets, a MySQL database, and a Quarkus Application ready to import.
If you don't have a working version of the application coming out of Chapter 03 Quarkus
, You can find the source code in the branch 03 Quarkus of my repository.
Jenkins X Basics¶
Just in case you're relatively new to Jenkins X, let me give you a bit of an introduction.
If you prefer to see it in action and watch a video instead of reading more text, watch one of the video's from Viktor Farcic.
Jenkins X provides pipeline automation, built-in GitOps, and preview environments to help teams collaborate and accelerate their software delivery at any scale.
Ok, that might be a bit too vague.
Jenkins X is built on top of four pillars:
- Jenkins X Pipelines: Jenkins X is a full CI/CD engine, which has pipelines to build, test, and deploy your applications on and to Kubernetes. As is popular these days, Jenkins X Pipelines are written in YAML.
- Manage Environments By GitOps: GitOps is a way of working, where you apply everything from Configuration as Code to your enviromnents. The assumption is that we all use Git nowadays, so we store this code in Git. We then leverage the Jenkins X Pipelines - environments have their own pipelines - to manage the environment.
- Preview Environments: when you make a PR, wouldn't it be awesome if you would get a temporary deployment so you can test if the PR not only works, but works as we want it? That's what preview environments are.
- Build Packs: not be confused with other Build Packs, these are Jenkins X's build packs and they ensure that you don't have to write any of the above mentioned workflows yourself. You can off course, or extend them where needed. The idea is that you get a full CI/CD Developer Experience with batteries included, but easily replaced.
Code Start¶
If you do not have a working version after the previous chapter, you can find the complete working code in the branch 03-quarkus.
Import¶
Oke, so now we're kinda getting to the point want to start running our new Quarkus application on this shiny new Jenkins X thingy. While Jenkins X has quickstarts (see the list here) to get you up and running fast, we already have an application.
So we're going to use Jenkins X's import feature. This feature uses the mentioned build packs to generate everything our application is currently missing. After that, it imports the application in Jenkins X, by creating the required resources in the cluster to ensure Jenkins X now watches your application. This means, that after this, every push to your github repository will trigger a build.
One of the things generated during the jx import
process, is your Jenkins X Pipeline. This will be derived from an existing pipeline, you can see the list here, so the file in your repository will only state buildpack: <name of the build pack>
.
Run The Import¶
In our case, we want to build with Maven and Java 11, there is no Quarkus Java 11 yet, so we will run jx import --pack maven-java11
.
The import process also generates - if absent - an appropriate Dockerfile, and our Helm charts. The Helm chart creation is mandatory, so the import only succeeds if there is no charts/
folder present.
Acitvity & Build Logs¶
Once your application is succesfully imported, you will get a message explaining several Jenkins X commands.
Two of the most important ones, are jx get build log
and jx get activity
. The message should give you a copyable command, so I'll leave that up to you to copy from there - it includes a filter with the name, which I can only guess.
The activity log gives you an always running overview of pipeline activity for a given filter. If you're used to Jenkins, it's aching to seeing the classic Pipeline Overview page in console form.
The build log gives you the log of the build while it is happening. As soon as a build is started, you can run jx get build log
, and you should see your build as the top item.
Once the application's pipeline finishes, it should trigger a promotion to the staging environment. To see that pipeline, you can run jx get build log
again, and select its pipeline run.
End Of Successful Build Log¶
Promoting app quarkus-fruits version 1.0.5 to namespace jx-staging
Created Pull Request: https://github.com/joostvdg/env.../pull/22
Added label updatebot to Pull Request https://github.com/joostvdg/env.../pull/22
got git provider status pending from PR https://github.com/joostvdg/env.../pull/22
Application In Staging¶
To verify the application landed successfully in the staging environment, you can run jx get applications
.
Which should give you a result like this:
APPLICATION STAGING PODS URL
quarkus-fruits 1.0.1 https://quarkus-fruits-jx-staging.staging.Your.Domain.com
As you can see, the PODS
section is empty. This is because the application cannot talk to its Database yet. No worries, we resolve this later.
Dockerfile¶
This the Dockerfile below, is created by the Quarkus team, designed for a runnable Jar application build with Quarkus. If you did not start from the quickstart, but generated a new Quarkus project, it is available in src/main/docker/Dockerfile.jvm
.
Either way, Jenkins X expects a Dockerfile
at the root, so update that Dockerfile
with the contents below.
I'd recommend using it, as it is well tested and does everything we need in minimal fashion and according to Docker's best practices.
Dockerfile
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1
ARG JAVA_PACKAGE=java-11-openjdk-headless
ARG RUN_JAVA_VERSION=1.3.5
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
# Install java and the run-java script
# Also set up permissions for user `1001`
RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
&& microdnf update \
&& microdnf clean all \
&& mkdir /deployments \
&& chown 1001 /deployments \
&& chmod "g+rwX" /deployments \
&& chown 1001:root /deployments \
&& curl https://repo1.maven.org/maven2/io/fabric8/run-java-sh/${RUN_JAVA_VERSION}/run-java-sh-${RUN_JAVA_VERSION}-sh.sh -o /deployments/run-java.sh \
&& chown 1001 /deployments/run-java.sh \
&& chmod 540 /deployments/run-java.sh \
&& echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security
# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
COPY target/lib/* /deployments/lib/
COPY target/*-runner.jar /deployments/app.jar
EXPOSE 8080
USER 1001
ENTRYPOINT [ "/deployments/run-java.sh" ]
Health Check¶
One of the most important aspects of your application running in Kubernetes, is giving Kubernetes enough information about the state of your application. Kubernetes does this via Liveness and Readiness probes.
What Are These Probes¶
If you don't know much about these probes, or want to understand them better, read the Kubernetes docs. For the lazy, I've added quotes for this docs page.
livenessProbe: Indicates whether the Container is running. If the liveness probe fails, the kubelet kills the Container, and the Container is subjected to its restart policy. If a Container does not provide a liveness probe, the default state is Success.
readinessProbe: Indicates whether the Container is ready to service requests. If the readiness probe fails, the endpoints controller removes the Pod’s IP address from the endpoints of all Services that match the Pod. The default state of readiness before the initial delay is Failure. If a Container does not provide a readiness probe, the default state is Success.
We recommend servicing each probe with a distinct endpoint if you can.
Quarkus Health Checks¶
Luckily, Quarkus has a dependency that gives us exactly that: quarkus-smallrye-health
. quarkus-smallrye-health
is an implementation of MicroProfile's Health specification, and they have a guide on using and extending it.
For those wanting to skip the line, for the minimal implementation we add one dependency to our pom.xml
, namely, the mentioned quarkus-smallrye-health
. This dependency automatically gives us three endpoints for health checks:
/health/live
: The application is up and running./health/ready
: The application is ready to serve requests./health
: Accumulating all health check procedures in the application.
Add Dependency¶
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-smallrye-health</artifactId>
</dependency>
Update Our Kubernetes Configuration¶
We now have the ability to give Kubernetes the information it needs to understand the status of our application. We still have to ensure Kubernetes knows how to get this information.
To do so, we have two choices.
- Update the
values.yaml
of our Chart, it has one entry for both checks, so not optimal, but minimal effort - Update the
templates/deployment.yaml
of our Chart, where we have to set both endpoints
Update Values¶
By default, the Helm chart that is generated has one variable for both the liveness and readyness probes: probePath
.
The value is specified between resources
and livenessProbe
:
We set the value of probePath
to /health
:
charts/Name-Of-Your-App/values.yaml
Update Deployment¶
The second way of make the required change, is to update our Deployment definition. You can choose to set the values directly in the template, or better, change the variables used in the probe paths and set the values of these variables in the values.yaml
.
We will do the latter here:
charts/Name-Of-Your-App/templates/deployment.yaml
livenessProbe:
httpGet:
path: {{ .Values.livenessProbePath }}
port: {{ .Values.service.internalPort }}
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
successThreshold: {{ .Values.livenessProbe.successThreshold }}
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
readinessProbe:
httpGet:
path: {{ .Values.readinessProbePath }}
port: {{ .Values.service.internalPort }}
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
successThreshold: {{ .Values.readinessProbe.successThreshold }}
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
charts/Name-Of-Your-App/values.yaml
Next Steps¶
We are going to do a lot more things, but this concludes the initial steps to import our application in Jenkins X. Our application still can't run, because it doesn't have the information to connect to the database. This is our next stop.