{"id":818,"date":"2020-03-30T19:00:00","date_gmt":"2020-03-30T19:00:00","guid":{"rendered":"http:\/\/bullyrooks.com\/index.php\/2020\/04\/10\/simple-spring-boot-service-to-kubernetes-application-step-19-944a76415384\/"},"modified":"2021-02-04T02:14:54","modified_gmt":"2021-02-04T02:14:54","slug":"simple-spring-boot-service-to-kubernetes-application-step-19-944a76415384","status":"publish","type":"post","link":"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-19-944a76415384\/","title":{"rendered":"Put the UI in to Helm"},"content":{"rendered":"\n<p class=\"graf graf--p graf-after--h3 graf--trailing\" id=\"b22a\">Lets take the docker image we just created and wrap it with helm so that we have an easy way to deploy our application.<\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf--leading wp-block-heading\" id=\"6f41\">Create Helm Chart Templates<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"8c08\">We\u2019ve created helm charts before, so lets create a <code class=\"markup--code markup--p-code\">helm <\/code>directory at the frontend root. Then run helm create in that directory<\/p>\n\n\n\n<pre id=\"8aa0\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>helm create medium-customer-manager<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"22ae\">That should build out more than we need. We really only need a few files:<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"1d99\"><code class=\"markup--code markup--p-code\">templates\/deployment.yaml<\/code><\/p>\n\n\n\n<pre id=\"633f\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: \"{{  .Chart.Name }}-deployment\"\n  labels:\n    chart: '{{ .Chart.Name }}-{{ .Chart.Version | replace \"+\" \"_\" }}'\nspec:\n  replicas: {{ .Values.replicaCount }}\n  strategy:\n    type: RollingUpdate\n    rollingUpdate:\n      maxUnavailable: 0\n      maxSurge: 1\n  selector:\n    matchLabels:\n      app: \"{{  .Chart.Name }}-selector\"\n      version: \"current\"\n  template:\n    metadata:\n      labels:\n        app: \"{{  .Chart.Name }}-selector\"\n        version: \"current\"\n    spec:\n      imagePullSecrets:\n        - name: regcred\n      containers:\n        - name: \"{{  .Chart.Name  }}\"\n          image: \"{{ .Values.image.repository }}:{{ .Values.image.tag }}\"\n          imagePullPolicy: {{ .Values.image.pullPolicy }}\n          volumeMounts:\n            - name: data\n              mountPath: \/data\n            {{- if and (eq .Values.config.type \"file\") (hasKey .Values.config \"content\") }}\n            - name: config-file\n              mountPath: \/usr\/share\/nginx\/html\/config.js\n              subPath: config.js\n            {{- end }}\n          ports:\n            - name: http\n              containerPort: {{ .Values.containerPort }}\n              protocol: TCP\n          env:\n            - name: PORT\n              value : \"{{ .Values.service.servicePort }}\"\n      volumes:\n        - name: data\n          emptyDir: {}\n        {{- if and (eq .Values.config.type \"file\") (hasKey .Values.config \"content\") }}\n        - name: config-file\n          configMap:\n            name: {{ .Chart.Name }}-config\n        {{- end }}<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"9e16\"><code class=\"markup--code markup--p-code\">template\/service.yaml<\/code><\/p>\n\n\n\n<pre id=\"84ff\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>apiVersion: v1\nkind: Service\nmetadata:\n  annotations:\n    prometheus.io\/scrape: 'true'\n  name: \"{{  .Chart.Name }}-service\"\n  labels:\n    chart: \"{{ .Chart.Name }}-{{ .Chart.Version | replace \"+\" \"_\" }}\"\nspec:\n  type: {{ .Values.service.type }}\n  ports:\n  - name: http\n    port: {{ .Values.service.httpPort }}\n    targetPort: http\n  {{- if hasKey .Values.service \"nodePort\" }}\n    nodePort: {{ .Values.service.nodePort }}\n  {{- end }}\n  selector:\n    app: \"{{  .Chart.Name }}-selector\"<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"1025\"><code class=\"markup--code markup--p-code\">template\/customer-manager-configmap.yaml<\/code><\/p>\n\n\n\n<pre id=\"e6d5\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>{{- if and ( eq .Values.config.type \"file\") (hasKey .Values.config \"content\") }}\napiVersion: v1\nkind: ConfigMap\nmetadata:\n  name: {{ .Chart.Name }}-config\n  namespace: {{ .Release.Namespace | quote }}\ndata:\n  config.js:\n    {{ toYaml .Values.config.content | indent 4  }}\n{{- end }}<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"f612\"><code class=\"markup--code markup--p-code\">Chart.yaml<\/code><\/p>\n\n\n\n<pre id=\"f727\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>apiVersion: v2\nname: medium-customer-manager\ndescription: A Helm chart for Kubernetes\ntype: application\nversion: 1.0.0<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"7c50\">values.yaml<\/p>\n\n\n\n<pre id=\"7f90\" class=\"wp-block-code graf graf--pre graf-after--p\"><code><em class=\"markup--em markup--pre-em\"># This is a YAML-formatted file.\n# Declare variables to be passed into your templates.\n<\/em>replicaCount: 1\nimage:\n  repository: r.cfcr.io\/docketdynamics\/medium-customer-manager\n  tag: \"5556008\"\n  pullPolicy: IfNotPresent\ncontainerPort: 80\nservice:\n  enabled: true\n  httpPort: 80\n  nodePort: 30002\n  type: NodePort\n\nconfig:\n<em class=\"markup--em markup--pre-em\">## Currently only supports file\n<\/em>type: file\n<em class=\"markup--em markup--pre-em\">## Contents of config in YAML\n<\/em>content: |-\n    window.REACT_APP_CUSTOMER_HOST='http:\/\/172.18.51.90\/:30001'<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"a9bd\">Again, you can see that we\u2019re hardcoding in our customer host, which isn\u2019t ideal, but it will get us moving forward. We\u2019ll eventually replace that with an ingress.<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"782a\">The only interesting part is how we\u2019re writing the configuration. We\u2019re 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.<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"0191\">Which reminds me that we need to update our front end application to support that.<\/p>\n\n\n\n<p class=\"graf graf--p graf-after--p\" id=\"1a21\">Create <code class=\"markup--code markup--p-code\">public\/config.js<\/code><\/p>\n\n\n\n<pre id=\"1e67\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>window.REACT_APP_CUSTOMER_HOST='HTTP:\/\/localhost:10000'<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"f022\">which is a placeholder file that will eventually be overwritten. Again, this isn\u2019t how we\u2019re going to solve our long term problem, but it will allow us to deploy and test.<\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf-after--p wp-block-heading\" id=\"0348\">Deploy and&nbsp;Test<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"fdaf\">Deploy our front end with<\/p>\n\n\n\n<pre id=\"3974\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>helm install cust-mgr helm\/medium-customer-manager<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"38e4\">Which should find our image we just build in the last section. We can hit our application at <code class=\"markup--code markup--p-code\">http:\/\/&lt;minikube ip&gt;:30002\/<\/code><\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf-after--p wp-block-heading\" id=\"d7c0\">Helm Chart Packaging and Publishing<\/h3>\n\n\n\n<p class=\"graf graf--p graf-after--h3\" id=\"7e66\">Lets add the automation to the pipeline to package the helm chart and publish to our chart registry. Add these steps to the pipeline:<\/p>\n\n\n\n<pre id=\"7b87\" class=\"wp-block-code graf graf--pre graf-after--p\"><code>stages:\n...\n- \"helmpublish\"<\/code><\/pre>\n\n\n\n<pre id=\"2746\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>...<\/code><\/pre>\n\n\n\n<pre id=\"49ed\" class=\"wp-block-code graf graf--pre graf-after--pre\"><code>  HelmChartGetVersion:\n    title: Get Helm Chart Version\n    stage: helmpublish\n    image: codefresh\/cfstep-helm\n    working_directory: '\/codefresh\/volume\/medium-customer-manager'\n    environment:\n      - CHART_PATH=helm\/medium-customer-manager\n      - CHART_NAME=medium-customer-manager\n    commands:\n      - export ACTION=auth\n      - source \/opt\/bin\/release_chart\n      - helm repo add default ${{CF_CTX_CF_HELM_DEFAULT_URL}}\n      - yq .version ${CHART_PATH}\/Chart.yaml\n      - export CURRENT_CHART_VERSION=`helm search default\/${CHART_NAME} | awk 'FNR==2{print $2}' || yq .version ${CHART_PATH}\/Chart.yaml`\n      - echo $CURRENT_CHART_VERSION\n      - cf_export NEW_CHART_VERSION=`echo \"${CURRENT_CHART_VERSION}\" | awk -F. '{$NF = $NF + 1;} 1' | sed 's\/ \/.\/g'`\n      - echo $NEW_CHART_VERSION\n  HelmChartUpdate:\n    title: Update Helm Chart Version\n    stage: helmpublish\n    image: gksoftware\/yq\n    working_directory: '\/codefresh\/volume\/medium-customer-manager'\n    environment:\n      - CHART_PATH=helm\/medium-customer-manager\n      - YAML_PATH=image.tag\n    commands: \n      - echo $NEW_CHART_VERSION\n      - yq w -i ${CHART_PATH}\/Chart.yaml version ${NEW_CHART_VERSION}\n      - echo $CF_SHORT_REVISION\n      - yq w -i ${CHART_PATH}\/values.yaml ${YAML_PATH} '\"${{CF_SHORT_REVISION}}\"'\n  HelmChartPush:\n      title: Push Helm Chart to Chart Repository\n      stage: helmpublish\n      image: codefresh\/cfstep-helm\n      working_directory: '\/codefresh\/volume\/medium-customer-manager'\n      environment:\n        - CHART_REF=helm\/medium-customer-manager\/\n        - ACTION=push<\/code><\/pre>\n\n\n\n<p class=\"graf graf--p graf-after--pre\" id=\"ccaf\">Make sure to add the helm registry as and environment variable to your build so that the pipeline can publish the chart.<\/p>\n\n\n\n<h3 class=\"graf graf--h3 graf-after--p wp-block-heading\" id=\"dedb\">Commit<\/h3>\n\n\n\n<pre id=\"66ab\" class=\"wp-block-preformatted graf graf--pre graf-after--h3\">git checkout -b helm\ngit add .\ngit commit -m \"helm chart\"\ngit push\ngit checkout master\ngit merge helm\ngit push<\/pre>\n","protected":false},"excerpt":{"rendered":"<div class=\"entry-summary\">\nLets take the docker image we just created and wrap it with&hellip;\n<\/div>\n<div class=\"link-more\"><a href=\"https:\/\/bullyrooks.com\/index.php\/2020\/03\/30\/simple-spring-boot-service-to-kubernetes-application-step-19-944a76415384\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &ldquo;Put the UI in to Helm&rdquo;<\/span>&hellip;<\/a><\/div>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41],"tags":[79,81],"course":[40],"class_list":["post-818","post","type-post","status-publish","format-standard","hentry","category-software-development","tag-helm","tag-helm-chart","course-spring-with-kubernetes","entry"],"jetpack_featured_media_url":"","jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/818","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/comments?post=818"}],"version-history":[{"count":3,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/818\/revisions"}],"predecessor-version":[{"id":913,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/posts\/818\/revisions\/913"}],"wp:attachment":[{"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/media?parent=818"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/categories?post=818"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/tags?post=818"},{"taxonomy":"course","embeddable":true,"href":"https:\/\/bullyrooks.com\/index.php\/wp-json\/wp\/v2\/course?post=818"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}