Simple Spring Boot Service to Kubernetes Application: Step 19
Lets take the docker image we just created and wrap it with helm so that we have an easy way to deploy our application.
Create Helm Chart Templates
We’ve created helm charts before, so lets create a helm
directory at the frontend root. Then run helm create in that directory
helm create medium-customer-manager
That should build out more than we need. We really only need a few files:
templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: "{{ .Chart.Name }}-deployment"
labels:
chart: '{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}'
spec:
replicas: {{ .Values.replicaCount }}
strategy:
type: RollingUpdate
rollingUpdate:
maxUnavailable: 0
maxSurge: 1
selector:
matchLabels:
app: "{{ .Chart.Name }}-selector"
version: "current"
template:
metadata:
labels:
app: "{{ .Chart.Name }}-selector"
version: "current"
spec:
imagePullSecrets:
- name: regcred
containers:
- name: "{{ .Chart.Name }}"
image: "{{ .Values.image.repository }}:{{ .Values.image.tag }}"
imagePullPolicy: {{ .Values.image.pullPolicy }}
volumeMounts:
- name: data
mountPath: /data
{{- if and (eq .Values.config.type "file") (hasKey .Values.config "content") }}
- name: config-file
mountPath: /usr/share/nginx/html/config.js
subPath: config.js
{{- end }}
ports:
- name: http
containerPort: {{ .Values.containerPort }}
protocol: TCP
env:
- name: PORT
value : "{{ .Values.service.servicePort }}"
volumes:
- name: data
emptyDir: {}
{{- if and (eq .Values.config.type "file") (hasKey .Values.config "content") }}
- name: config-file
configMap:
name: {{ .Chart.Name }}-config
{{- end }}
template/service.yaml
apiVersion: v1
kind: Service
metadata:
annotations:
prometheus.io/scrape: 'true'
name: "{{ .Chart.Name }}-service"
labels:
chart: "{{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }}"
spec:
type: {{ .Values.service.type }}
ports:
- name: http
port: {{ .Values.service.httpPort }}
targetPort: http
{{- if hasKey .Values.service "nodePort" }}
nodePort: {{ .Values.service.nodePort }}
{{- end }}
selector:
app: "{{ .Chart.Name }}-selector"
template/customer-manager-configmap.yaml
{{- if and ( eq .Values.config.type "file") (hasKey .Values.config "content") }}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Chart.Name }}-config
namespace: {{ .Release.Namespace | quote }}
data:
config.js:
{{ toYaml .Values.config.content | indent 4 }}
{{- end }}
Chart.yaml
apiVersion: v2
name: medium-customer-manager
description: A Helm chart for Kubernetes
type: application
version: 1.0.0
values.yaml
# This is a YAML-formatted file.
# Declare variables to be passed into your templates.
replicaCount: 1
image:
repository: r.cfcr.io/docketdynamics/medium-customer-manager
tag: "5556008"
pullPolicy: IfNotPresent
containerPort: 80
service:
enabled: true
httpPort: 80
nodePort: 30002
type: NodePort
config:
## Currently only supports file
type: file
## Contents of config in YAML
content: |-
window.REACT_APP_CUSTOMER_HOST='http://172.18.51.90/:30001'
Again, you can see that we’re hardcoding in our customer host, which isn’t ideal, but it will get us moving forward. We’ll eventually replace that with an ingress.
The only interesting part is how we’re writing the configuration. We’re following a similar pattern to the spring app where we write a configmap and then use the configmap to write out a configuration file into the pod.
Which reminds me that we need to update our front end application to support that.
Create public/config.js
window.REACT_APP_CUSTOMER_HOST='HTTP://localhost:10000'
which is a placeholder file that will eventually be overwritten. Again, this isn’t how we’re going to solve our long term problem, but it will allow us to deploy and test.
Deploy and Test
Deploy our front end with
helm install cust-mgr helm/medium-customer-manager
Which should find our image we just build in the last section. We can hit our application at http://<minikube ip>:30002/
Helm Chart Packaging and Publishing
Lets add the automation to the pipeline to package the helm chart and publish to our chart registry. Add these steps to the pipeline:
stages:
...
- "helmpublish"... HelmChartGetVersion:
title: Get Helm Chart Version
stage: helmpublish
image: codefresh/cfstep-helm
working_directory: '/codefresh/volume/medium-customer-manager'
environment:
- CHART_PATH=helm/medium-customer-manager
- CHART_NAME=medium-customer-manager
commands:
- export ACTION=auth
- source /opt/bin/release_chart
- helm repo add default ${{CF_CTX_CF_HELM_DEFAULT_URL}}
- yq .version ${CHART_PATH}/Chart.yaml
- export CURRENT_CHART_VERSION=`helm search default/${CHART_NAME} | awk 'FNR==2{print $2}' || yq .version ${CHART_PATH}/Chart.yaml`
- echo $CURRENT_CHART_VERSION
- cf_export NEW_CHART_VERSION=`echo "${CURRENT_CHART_VERSION}" | awk -F. '{$NF = $NF + 1;} 1' | sed 's/ /./g'`
- echo $NEW_CHART_VERSION
HelmChartUpdate:
title: Update Helm Chart Version
stage: helmpublish
image: gksoftware/yq
working_directory: '/codefresh/volume/medium-customer-manager'
environment:
- CHART_PATH=helm/medium-customer-manager
- YAML_PATH=image.tag
commands:
- echo $NEW_CHART_VERSION
- yq w -i ${CHART_PATH}/Chart.yaml version ${NEW_CHART_VERSION}
- echo $CF_SHORT_REVISION
- yq w -i ${CHART_PATH}/values.yaml ${YAML_PATH} '"${{CF_SHORT_REVISION}}"'
HelmChartPush:
title: Push Helm Chart to Chart Repository
stage: helmpublish
image: codefresh/cfstep-helm
working_directory: '/codefresh/volume/medium-customer-manager'
environment:
- CHART_REF=helm/medium-customer-manager/
- ACTION=push
Make sure to add the helm registry as and environment variable to your build so that the pipeline can publish the chart.
Commit
git checkout -b helm
git add .
git commit -m "helm chart"
git push
git checkout master
git merge helm
git push