This project is a simple Spring Boot Hello World application to demonstrate how a CI/CD pipeline might make best use of the Helm package manager.
The Helm chart was initialised using a starter chart for Spring Boot made by a Pivotal employee.
Script to create a project and install the sample spring-hello-world-app Helm chart to it. It takes the name of a project and a "Release name"
. All Helm chart installations are known as "Releases"
and the resulting resources will be known as "$RELEASE_NAME-$CHART_NAME"
. The script will run the app through 3 different stages, performing a dev, test and prod deployment.
Usage: ./helm-pipeline.sh $PROJECT_NAME $RELEASE_NAME
Example: # Install the sanmanager chart in "my-new-project" ./helm-pipeline my-new-project testing-new-feature
This example will create a chart release in "my-new-project"
called
"testing-new-feature-spring-hello-world-app"
To clean up the helm deployments created by the pipeline run the following.
Usage: ./helm-pipeline.sh $PROJECT_NAME $RELEASE_NAME cleanup
Example: # Uninstall the sanmanager chart in "my-new-project" ./helm-pipeline my-new-project testing-new-feature cleanup
This example will perform a `helm uninstall` on each deployment.
.
├── build-and-push.sh
├── Dockerfile
├── helm
│ └── spring-hello-world-app
│ ├── Chart.yaml
│ ├── envs
│ │ ├── values-dev.yaml
│ │ ├── values-prod.yaml
│ │ └── values-test.yaml
│ ├── templates
│ │ ├── configmap.yaml
│ │ ├── deployment.yaml
│ │ ├── _helpers.tpl
│ │ ├── ingress.yaml
│ │ ├── NOTES.txt
│ │ ├── rbac.yaml
│ │ ├── service.yaml
│ │ └── tests
│ │ └── test-connection.yaml
│ └── values.yaml
├── helm-pipeline.sh
├── mvnw
├── mvnw.cmd
├── pom.xml
├── README.adoc
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── demo
│ │ │ └── DemoApplication.java
│ │ └── resources
│ │ ├── application-dev.properties
│ │ ├── application-prod.properties
│ │ ├── application.properties
│ │ └── application-test.properties
│ └── test
│ └── java
│ └── com
│ └── example
│ └── demo
│ └── DemoApplicationTests.java
└── target
export APP=spring-hello-world-app
export REG=<Your image repo URL>
mvn clean package -DskipTests
docker build -t ${APP} .
docker tag ${APP}:latest ${REG}/${APP}:latest
docker push ${REG}/${APP}:latest
The current implementation of the hello-world-app uses an image currently located at docker.io/mikecroft/spring-hello-world-app
. If this endpoint is inaccessible, it may be beneficial to re-build the app, and push it to a local internal registry. The build-and-push.sh
script does this for you, with the user only needing to update the REG
variable to their internal image repository.
Helm helps you manage Kubernetes applications — Helm Charts help you define, install, and upgrade even the most complex Kubernetes application.
Three Big Concepts
A Chart is a Helm package. It contains all of the resource definitions necessary to run an application, tool, or service inside of a Kubernetes cluster. Think of it like the Kubernetes equivalent of a Homebrew formula, an Apt dpkg, or a Yum RPM file.
A Repository is the place where charts can be collected and shared. It’s like Perl’s CPAN archive or the Fedora Package Database, but for Kubernetes packages.
A Release is an instance of a chart running in a Kubernetes cluster. One chart can often be installed many times into the same cluster. And each time it is installed, a new release is created. Consider a MySQL chart. If you want two databases running in your cluster, you can install that chart twice. Each one will have its own release, which will in turn have its own release name.
With these concepts in mind, we can now explain Helm like this:
Helm installs charts into Kubernetes, creating a new release for each installation. And to find new charts, you can search Helm chart repositories.
Source: helm.sh
Following is the Helm chart used to deploy the hello-world-app.
├── helm
│ └── spring-hello-world-app
│ ├── Chart.yaml
│ ├── envs
│ │ ├── values-dev.yaml
│ │ ├── values-prod.yaml
│ │ └── values-test.yaml
│ ├── templates
│ │ ├── configmap.yaml
│ │ ├── deployment.yaml
│ │ ├── _helpers.tpl
│ │ ├── ingress.yaml
│ │ ├── NOTES.txt
│ │ ├── rbac.yaml
│ │ ├── service.yaml
│ │ └── tests
│ │ └── test-connection.yaml
│ └── values.yaml
apiVersion: v2 #1
appVersion: 0.1.0 #2
description: A Helm chart for Kubernetes #3
name: spring-hello-world-app #4
type: application #5
version: 0.1.0 #6
chart.yml
defines some metadata about our helm chart. The chart.yaml
file is required for a chart and can be populated using the fields found here. The fields we’re using are described below.
-
The chart API version
-
The name of the chart
-
A single-sentence description of this project
-
The name of the chart
-
The type of the chart
-
A Semantic Versioning 2 version
The Values.yml
provides default configuration data for the helm templates to use and is available in a structured format.
The envs
directory contains our unique values-${stage}.yaml
files for each deployment stage. The global values.yaml
however contains the default values file. When running a helm install it is possible to specifiy a particular values.yaml
, and if no specific file is specified, the global one will be used.
You can specify a values file using helm install -f custom-values-file.yml
The values.yml
used for this deployment is as follows.
# Set how many application instances to run.
replicaCount: 1
# Override these settings and use your container image.
image:
repository: mikecroft/spring-hello-world-app
tag: latest
pullPolicy: Always
# Set image pull secrets (in case you're using a private container registry).
imageCredentials:
registry: # gcr.io
username: # oauth2accesstoken
password: # $(gcloud auth print-access-token)
# Set service type: LoadBalancer, ClusterIP, NodePort
service:
type: LoadBalancer
port: 8080
ingressBase: apps.openshift.ebms.tv
ingress:
enabled: true #1
annotations: {}
hosts:
- host: spring-hello-world
paths: ["/"]
tls: []
# Set to false to disable Prometheus support.
monitoring: true
# Set to false to disable Spring Cloud Kubernetes support.
sck: true
# Set configuration properties.
config:
foo: bar
Important
|
Make sure to change the image repository and tag if you’ve rebuilt and pushed the image to your local image repository. |
-
If
ingress.enabled
is set to true, an openshift Route/ Ingress object will be made. This will expose the app endpoint and make it resolvable outside the cluster.
values-${stage}.yml files
The main difference between the default values.yml
and the specific stage files is the springProfilesActive
value is set. This value sets the spring profile within the app itself to the desired stage.
# Set Spring active profile
springProfilesActive: dev || test || prod
Templates generate manifest files, which are YAML-formatted resource descriptions that Kubernetes can understand. The hello-world-app creates several manifests from templates which allow the app to be functional on Openshift.
-
configmap.yaml
- Builds a configmap which specifies the Application.yml file. This contains our config for our spring app. (similar to application.properties). -
deployment.yaml
- Builds our app deployment template. The Deployment describes a desired state, and the Deployment Controller changes the actual state to the desired state at a controlled rate. -
ingress.yaml
- Builds the apps ingress object. This will manage the apps external access to the services in a cluster. -
rbac.yaml
Specifies the Role Based Access Control for the app. This specifies the permissions and accessibility our app has within the cluster. RBAC rules are generally applied to a serviceaccount. For this deployment we use thedefault
serviceaccount. -
service.yaml
- Builds the apps service object. This will manage the apps internal access within the cluster.
The pipeline begins by taking the project name and project release name as arguments. It will then initialise 3 projects to simulate 3 clusters (a dev, test and prod cluster). An installation flow goes as follows.
-
Execute the shell script and takes the project name and release name as arguments
-
Sets enviornment variables
-
Runs the init function which switches to the correct project (or creates a new one if it doesn’t exist), creates the docker-registry pull secret, links the pull secret with the deafult service account and finally sets the kubeconfig to the correct context.
-
Runs the install function with an argument of dev.
-
Runs the chart_test function on dev to check the chart has deployed correctly. This uses
helm test
to achieve this. -
Runs the app_test function to check the expected deployed string against the actual.
-
Repeats step 4,5,6 with the arguments test and prod
The hello world app exposes the '/hello' endpoint mapping with a message from a specified environment. eg: dev, test or prod.
package com.example.demo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.*;
import org.springframework.boot.autoconfigure.*;
import org.springframework.web.bind.annotation.*;
@SpringBootApplication
@RestController
public class DemoApplication {
@Value("${environment.type}") #1
private String environment;
@GetMapping("/hello") #2
String home() {
return "hello from " + environment;
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
-
Sets the environment type based on the value set in application properties.
-
Returns the string "Hello from ${environment}" to the '/hello' endpoint