Skip to content

Commit 04a48ca

Browse files
committed
finish native image and add initial pipeline improv
1 parent f7bb6b6 commit 04a48ca

File tree

7 files changed

+785
-42
lines changed

7 files changed

+785
-42
lines changed

docs/jenkinsx/builder-image.md

+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
title: Jenkins X - Create Builder Image
2+
description: How to create a Jenkins X builder Image
3+
4+
# Create Jenkins X Builder Image
5+
6+
Jenkins X leverages [Tekton pipelines](https://github.com/tektoncd/pipeline) to create a Kubernetes native Pipeline experience. Every step is run in its own container.
7+
8+
People comonly start using Jenkins X via its pre-defined build packs.
9+
These build packs already have a default Container Image defined, and use some specific containers for certain specific tasks - such as Kaniko for building Container Images. We call these Container Images: ***Builders***.
10+
11+
## Create Custom Builder
12+
13+
Sometimes you need to use a different container for a specific step. First, [look at the available builder](https://github.com/jenkins-x/jenkins-x-builders). If what you need does not exist yet, you will have to create one yourself.
14+
15+
Jenkins X [has a guide on how to create a custom Builder](https://jenkins-x.io/docs/guides/managing-jx/common-tasks/create-custom-builder/).
16+
17+
In essence, you create a Container Image that extends from `gcr.io/jenkinsxio/builder-base:0.0.81`, includes your tools and packages of choice, and may or may not include the `jx` binary.
18+
19+
What comes after, is your choice. You can add [your Builder to Jenkins X's list of Builders](https://jenkins-x.io/docs/guides/managing-jx/common-tasks/create-custom-builder/#install-the-builder), or directly use it in your Jenkins X Pipeline by FQN.
20+
21+
The main difference, is that when you add the Builder to Jenkins X you can include default configuration for the entire Pod. Otherwise, you have to specify any unique configuration in the Jenkins X Pipeline where you use the image.
22+
23+
## Dockerfile Example
24+
25+
!!! example "Dockerfile"
26+
27+
```Dockerfile
28+
FROM gcr.io/jenkinsxio/builder-base:0.0.81
29+
30+
RUN yum install -y java-11-openjdk-devel && yum update -y && yum clean all
31+
32+
# Maven
33+
ENV MAVEN_VERSION 3.6.3
34+
RUN curl -f -L https://repo1.maven.org/maven2/org/apache/maven/apache-maven/$MAVEN_VERSION/apache-maven-$MAVEN_VERSION-bin.tar.gz | tar -C /opt -xzv
35+
ENV M2_HOME /opt/apache-maven-$MAVEN_VERSION
36+
ENV maven.home $M2_HOME
37+
ENV M2 $M2_HOME/bin
38+
ENV PATH $M2:$PATH
39+
40+
# GraalVM
41+
ARG GRAAL_VERSION=20.0.0
42+
ENV GRAAL_CE_URL=https://github.com/graalvm/graalvm-ce-builds/releases/download/vm-${GRAAL_VERSION}/graalvm-ce-java11-linux-amd64-${GRAAL_VERSION}.tar.gz
43+
ARG INSTALL_PKGS="gzip"
44+
45+
ENV GRAALVM_HOME /opt/graalvm
46+
ENV JAVA_HOME /opt/graalvm
47+
48+
RUN yum install -y ${INSTALL_PKGS} && \
49+
### Installation of GraalVM
50+
mkdir -p ${GRAALVM_HOME} && \
51+
cd ${GRAALVM_HOME} && \
52+
curl -fsSL ${GRAAL_CE_URL} | tar -xzC ${GRAALVM_HOME} --strip-components=1 && \
53+
### Cleanup
54+
yum clean all && \
55+
rm -f /tmp/graalvm-ce-amd64.tar.gz && \
56+
rm -rf /var/cache/yum
57+
###
58+
59+
ENV PATH $GRAALVM_HOME/bin:$PATH
60+
RUN gu install native-image
61+
62+
# jx
63+
ENV JX_VERSION 2.1.30
64+
RUN curl -f -L https://github.com/jenkins-x/jx/releases/download/v${JX_VERSION}/jx-linux-amd64.tar.gz | tar xzv && \
65+
mv jx /usr/bin/
66+
67+
CMD ["mvn","-version"]
68+
```
69+
70+
## Usage
71+
72+
The example above is my [Jenkins X Builder for Maven + JDK 11 + GraalVM](https://github.com/joostvdg/jenkins-x-builders/tree/master/graalvm-maven-jdk11). My Dockerhub ID is `caladreas`, and the image is `jx-builder-graalvm-maven-jdk11`.
73+
74+
### Override Default Container
75+
76+
To use this Container Image as the default container:
77+
78+
!!! example "jenkins-x.yml"
79+
80+
```yaml
81+
buildPack: maven-java11
82+
pipelineConfig:
83+
agent:
84+
image: caladreas/jx-builder-graalvm-maven-jdk11:v0.7.0
85+
```
86+
87+
### Override Step Container
88+
89+
!!! example "jenkins-x.yml"
90+
91+
```yaml hl_lines="10"
92+
buildPack: maven-java11
93+
pipelineConfig:
94+
pipelines:
95+
overrides:
96+
- pipeline: pullRequest
97+
stage: build
98+
name: mvn-install
99+
steps:
100+
- name: mvn-deploy
101+
image: caladreas/jx-builder-graalvm-maven-jdk11:v0.9.0
102+
```

docs/jenkinsx/java-native-prod/01-intro.md

+15-22
Original file line numberDiff line numberDiff line change
@@ -56,42 +56,35 @@ If you want to focus on a stable production ready cluster, I can also recommend
5656
* [CloudBees Jenkins X Distribution](https://docs.cloudbees.com/docs/cloudbees-jenkins-x-distribution/latest/)
5757
* [Youtube video with installation and maintenance guidance](https://www.youtube.com/watch?v=rQlP_3iXvRE)
5858

59+
!!! important
60+
61+
A little spoiler, but the Native Image build requires at least 6GB of memory but works best with about 8GB.
62+
This means your Kubernetes worker node that your build runs on, needs have at least about 10-12GB memory.
63+
64+
If you're in GKE, as the guide assumes, the following machine types work:
65+
66+
* `e2-highmem-2`
67+
* `n2-highmem-2`
68+
* `e2-standard-4`
69+
* `n2-standard-4`
70+
71+
Keep in mind, you can use more than one Node Pool. You don't have to run all your nodes on these types, you need at least to be safe. Having autoscaling enabled for this Node Pool is recommended.
72+
5973
## Why Quarkus
6074

6175
Before we start, I'd like to make the case, why I chose to use Quarkus for this.
6276

6377
Wanting to build a Native Image with Java 11 is part of the reason, we'll dive into that next.
6478

6579
Quarkus has seen an tremendous amount of updates since its inception.
66-
It is a really active framework, which does not require you to forget everything you've learned in other Java frameworks such as Spring and Spring Boot.
80+
It is a really active framework, which does not require you to forget everything you've learned in other Java frameworks such as Spring and Spring Boot. I like to stay up-to-date with what happens in the Java community, so spending some time with Quarkus was on my todo list.
6781

6882
It comes out of the same part from RedHat that is involved with OpenShift - RedHat's Kubernetes distribution.
6983
This ensures the framework is created with running Java on Kubernetes in mind.
7084
Jenkins X starts from Kubernetes, so this makes it a natural fit.
7185

7286
Next, the capabilities for making a Native Image and work done to ensure you - the developer - do not have to worry (too much) about how to get from a Spring application to a Native Image is staggering. This makes the Native Image experience pleasant and involve little to no debugging.
7387

74-
## Why Native Image
75-
76-
Great that Quarkus helps with making a Native Image. What is a Native Image?
77-
In short, its makes your Java code into a runnable executable build for a specific environment.
78-
79-
You might wonder, what is wrong with using a runnable Jar - such as Spring Boot - or using a JVM?
80-
Nothing in and on itself. However, there are cases where having a long running process with a slow start-up time hurts you.
81-
82-
In a Cloud Native world, including Kubernetes, this is far more likely than in traditional - read, VM's - environments. With the advent of creating many smaller services that may or may not be stateless, and should be capable of scaling horizontally from 0 to infinity, different characteristics are required.
83-
84-
Some of these characterics:
85-
86-
* minimal resource use as we pay per usage (to a degree)
87-
* fast startup time
88-
* perform as expected on startup (JVM needs to warm up)
89-
90-
A Native Image performs better on the above metrics than a classic Java application with a JVM.
91-
Next to that, when you have a fixed runtime, the benefit of Java's "build once, run everywhere" is not as useful. When you always run your application in the same container in similar Kubernetes environments, a Native Image is perfectly fine.
92-
93-
Now, wether a Native Image performs better for your application depends on your application and its usage. The Native Image is no silver bullet. So it is still on you to do load and performance tests to ensure you're not degrading your performance for no reason!
94-
9588
## Resources
9689

9790
* https://quarkus.io/guides/writing-native-applications-tips

docs/jenkinsx/java-native-prod/04-jx-import.md

+32-14
Original file line numberDiff line numberDiff line change
@@ -53,25 +53,43 @@ Your build might fail, but no worry, just ensure a build started. If that worked
5353

5454
## Dockerfile
5555

56-
This the Dockerfile generated by Quarkus, designed for a Java Native Image application build with Quarkus.
56+
This the Dockerfile generated by Quarkus, designed for a runnable Jar application build with Quarkus. By default, it is available in `src/main/docker/Dockerfile.jvm`. Jenkins X expects a `Dockerfile` at the root, so update that `Dockerfile` with the contents below.
57+
5758
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.
5859

59-
```Dockerfile
60-
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1
61-
WORKDIR /work/
62-
COPY target/*-runner /work/application
60+
!!! example "Dockerfile"
6361

64-
# set up permissions for user `1001`
65-
RUN chmod 775 /work /work/application \
66-
&& chown -R 1001 /work \
67-
&& chmod -R "g+rwX" /work \
68-
&& chown -R 1001:root /work
62+
```Dockerfile
63+
FROM registry.access.redhat.com/ubi8/ubi-minimal:8.1
6964

70-
EXPOSE 8080
71-
USER 1001
65+
ARG JAVA_PACKAGE=java-11-openjdk-headless
66+
ARG RUN_JAVA_VERSION=1.3.5
7267

73-
CMD ["./application", "-Dquarkus.http.host=0.0.0.0", "-Xmx64m"]
74-
```
68+
ENV LANG='en_US.UTF-8' LANGUAGE='en_US:en'
69+
70+
# Install java and the run-java script
71+
# Also set up permissions for user `1001`
72+
RUN microdnf install curl ca-certificates ${JAVA_PACKAGE} \
73+
&& microdnf update \
74+
&& microdnf clean all \
75+
&& mkdir /deployments \
76+
&& chown 1001 /deployments \
77+
&& chmod "g+rwX" /deployments \
78+
&& chown 1001:root /deployments \
79+
&& 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 \
80+
&& chown 1001 /deployments/run-java.sh \
81+
&& chmod 540 /deployments/run-java.sh \
82+
&& echo "securerandom.source=file:/dev/urandom" >> /etc/alternatives/jre/lib/security/java.security
83+
84+
# Configure the JAVA_OPTIONS, you can add -XshowSettings:vm to also display the heap size.
85+
ENV JAVA_OPTIONS="-Dquarkus.http.host=0.0.0.0 -Djava.util.logging.manager=org.jboss.logmanager.LogManager"
86+
87+
COPY target/lib/* /deployments/lib/
88+
COPY target/*-runner.jar /deployments/app.jar
89+
90+
EXPOSE 8080
91+
USER 1001
92+
```
7593

7694
## Health Check
7795

docs/jenkinsx/java-native-prod/05-jx-secrets.md

+82
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,88 @@ For the username, this is all we have to do, as we directly inject this variable
309309
* add environment injection from secret for password
310310
* updated `charts/Name-Of-Your-Application/values.yaml` with placeholders for the secrets
311311

312+
??? example "templates/deployment.yaml"
313+
314+
Your deployment should now look like this:
315+
316+
```yaml
317+
{{- if .Values.knativeDeploy }}
318+
{{- else }}
319+
apiVersion: apps/v1
320+
kind: Deployment
321+
metadata:
322+
name: {{ template "fullname" . }}
323+
labels:
324+
draft: {{ default "draft-app" .Values.draft }}
325+
chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
326+
spec:
327+
selector:
328+
matchLabels:
329+
app: {{ template "fullname" . }}
330+
{{- if .Values.hpa.enabled }}
331+
{{- else }}
332+
replicas: {{ .Values.replicaCount }}
333+
{{- end }}
334+
template:
335+
metadata:
336+
labels:
337+
draft: {{ default "draft-app" .Values.draft }}
338+
app: {{ template "fullname" . }}
339+
annotations:
340+
prometheus.io/port: "8080"
341+
prometheus.io/scrape: "true"
342+
{{- if .Values.podAnnotations }}
343+
{{ toYaml .Values.podAnnotations | indent 8 }} #Only for pods
344+
{{- end }}
345+
spec:
346+
containers:
347+
- name: cloudsql-proxy
348+
image: gcr.io/cloudsql-docker/gce-proxy:1.16
349+
command: ["/cloud_sql_proxy",
350+
"-instances={{.Values.secrets.sql_connection}}=tcp:3306",
351+
"-credential_file=/secrets/cloudsql/credentials.json"]
352+
volumeMounts:
353+
- name: cloudsql-instance-credentials
354+
mountPath: /secrets/cloudsql
355+
readOnly: true
356+
- name: {{ .Chart.Name }}
357+
envFrom:
358+
- secretRef:
359+
name: {{ template "fullname" . }}-sql-secret
360+
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
361+
imagePullPolicy: {{ .Values.image.pullPolicy }}
362+
env:
363+
{{- range $pkey, $pval := .Values.env }}
364+
- name: {{ $pkey }}
365+
value: {{ $pval }}
366+
{{- end }}
367+
ports:
368+
- containerPort: {{ .Values.service.internalPort }}
369+
livenessProbe:
370+
httpGet:
371+
path: {{ .Values.probePath }}
372+
port: {{ .Values.service.internalPort }}
373+
initialDelaySeconds: {{ .Values.livenessProbe.initialDelaySeconds }}
374+
periodSeconds: {{ .Values.livenessProbe.periodSeconds }}
375+
successThreshold: {{ .Values.livenessProbe.successThreshold }}
376+
timeoutSeconds: {{ .Values.livenessProbe.timeoutSeconds }}
377+
readinessProbe:
378+
httpGet:
379+
path: {{ .Values.probePath }}
380+
port: {{ .Values.service.internalPort }}
381+
periodSeconds: {{ .Values.readinessProbe.periodSeconds }}
382+
successThreshold: {{ .Values.readinessProbe.successThreshold }}
383+
timeoutSeconds: {{ .Values.readinessProbe.timeoutSeconds }}
384+
resources:
385+
{{ toYaml .Values.resources | indent 12 }}
386+
terminationGracePeriodSeconds: {{ .Values.terminationGracePeriodSeconds }}
387+
{{- end }}
388+
volumes:
389+
- name: cloudsql-instance-credentials
390+
secret:
391+
secretName: {{ template "fullname" . }}-sql-sa
392+
```
393+
312394
### Summary of Changes Made To Staging Environment
313395

314396
* created a new file, called `jx-requirements.yml` at the root

0 commit comments

Comments
 (0)