diff --git a/cmd/kinflate/demos/README.md b/cmd/kinflate/demos/README.md index 8fdf2ead..e3818f1b 100644 --- a/cmd/kinflate/demos/README.md +++ b/cmd/kinflate/demos/README.md @@ -8,3 +8,6 @@ These demos are covered by presubmit tests. * [mysql](mySql.md) - Create a mySql production configuration from scratch. + + * [springboot](springboot.md) - Create a Spring Boot application production + configuration from scratch. diff --git a/cmd/kinflate/demos/springboot.md b/cmd/kinflate/demos/springboot.md new file mode 100644 index 00000000..c8fd8b2f --- /dev/null +++ b/cmd/kinflate/demos/springboot.md @@ -0,0 +1,350 @@ +# Demo: SpringBoot + +In this tutorial, you will learn - how to use `kinflate` to customize a basic Spring Boot application's +k8s configuration for production use cases. + +In the production environment we want to customize the following: + +- add application specific configuration for this Spring Boot application +- configure prod DB access configuration +- resource names to be prefixed by 'prod-'. +- resources to have 'env: prod' labels. +- JVM memory to be properly set. +- health check and readiness check. + +### Download resources + +Download `deployment.yaml`, `service.yaml`. These are plain k8s resources files one +could add to a k8s cluster to run sbdemo. + + +``` +DEMO_HOME=$(mktemp -d) +cd $DEMO_HOME + +# Get SpringBoot configs +for f in service deployment; do \ + wget https://raw.githubusercontent.com/kinflate/example-springboot/master/$f.yaml ; \ +done +``` + +### Initialize a manifest + +A _manifest_ groups these resources together. + +Create one: + + +``` +cd $DEMO_HOME +kinflate init +``` + +The above step will create a `kinflate` configuration file called `Kube-manifest.yaml` in current directory. + + +``` +cat $DEMO_HOME/Kube-manifest.yaml +``` + +containing something like: + + +> ``` +> apiVersion: manifest.k8s.io/v1alpha1 +> kind: Manifest +> metadata: +> name: helloworld +> # description: helloworld does useful stuff. +> namePrefix: some-prefix +> # Labels to add to all objects and selectors. +> # These labels would also be used to form the selector for apply --prune +> # Named differently than “labels” to avoid confusion with metadata for this object +> objectLabels: +> app: helloworld +> objectAnnotations: +> note: This is a example annotation +> resources: +> - deployment.yaml +> - service.yaml +> # There could also be configmaps in Base, which would make these overlays +> configMapGenerator: [] +> # There could be secrets in Base, if just using a fork/rebase workflow +> secretGenerator: [] +> ``` + + +### Add the resources to the manifest + + +``` +cd $DEMO_HOME + +kinflate edit add resource service.yaml +kinflate edit add resource deployment.yaml + +cat Kube-manifest.yaml +``` + +`Kube-manifest.yaml`'s resources section should contain: + +> ``` +> apiVersion: manifest.k8s.io/v1alpha1 +> .... +> resources: +> - service.yaml +> - deployment.yaml +> ``` + +### Add configmap to the manifest + +``` +cd $DEMO_HOME +wget https://raw.githubusercontent.com/kinflate/example-springboot/master/application.properties +kinflate edit add configmap demo-configmap --from-file application.properties + +cat Kube-manifest.yaml +``` +`Kube-manifest.yaml`'s configMapGenerator section should contain: +> ``` +> configMapGenerator: +> - files: +> - application.properties +> name: demo-configmap +> kind: Manifest +> ``` + +### Customize configmap +We want to add database credentials for the prod environment. In general, these credentials can be put into the file `application.properties`. +However, for some cases, we want to keep the credentials in a different file and keep application specific configs in `application.properties`. + With this clear separation, the credentials and application specific things can be managed and maintained flexibly by different teams. +For example, application developers only tune the application configs in `application.properties` and operation teams or SREs +only care about the credentials. + +For Spring Boot application, we can set an active profile through the environment variable `spring.profiles.active`. Then +the application will pick up an extra `application-.properties` file. With this, we can customize the configmap in two +steps. Add an environment variable through the patch and add a file to the configmap. + +``` +cat <$DEMO_HOME/patch.yaml +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: sbdemo +spec: + template: + spec: + containers: + - name: sbdemo + env: + - name: spring.profiles.active + value: prod +EOF + +cat <>$DEMO_HOME/Kube-manifest.yaml +patches: +- patch.yaml +EOF + +cat <$DEMO_HOME/application-prod.properties +spring.jpa.hibernate.ddl-auto=update +spring.datasource.url=jdbc:mysql://:3306/db_example +spring.datasource.username=root +spring.datasource.password=admin +EOF + +kinflate edit add configmap demo-configmap --from-file application-prod.properties + +cat Kube-manifest.yaml +``` +`Kube-manifest.yaml`'s configMapGenerator section should contain: +> ``` +> configMapGenerator: +> - files: +> - application.properties +> - application-prod.properties +> name: demo-configmap +> kind: Manifest +> ``` + +### Name Customization + +Arrange for the resources to begin with prefix +_prod-_ (since they are meant for the _production_ +environment): + + +``` +cd $DEMO_HOME + +kinflate edit set nameprefix 'prod-' + +cat Kube-manifest.yaml +``` + +`Kube-manifest.yaml` should have updated value of namePrefix field: + +> ``` +> apiVersion: manifest.k8s.io/v1alpha1 +> .... +> namePrefix: prod- +> objectAnnotations: +> note: This is a example annotation +> ``` + +This `namePrefix` directive adds _prod-_ to all +resource names. + + +``` +kinflate build $DEMO_HOME +``` + +The output should contain: +> ``` +> apiVersion: v1 +> data: +> .... +> kind: ConfigMap +> metadata: +> .... +> name: prod-demo-configmap-7746248cmc +> --- +> apiVersion: v1 +> kind: Service +> metadata: +> .... +> name: prod-sbdemo +> spec: +> .... +> --- +> apiVersion: apps/v1beta2 +> kind: Deployment +> metadata: +> .... +> name: prod-sbdemo +> spec: +> selector: +> .... +> ``` + +### Label Customization + +We want resources in production environment to have +certain labels so that we can query them by label +selector. + +`kinflate` does not have `edit set label` command to add +label, but we can edit `Kube-manifest.yaml` file under +`prod` directory and add the production labels under +`objectLabels` fields as highlighted below. + + +``` +sed -i 's/app: helloworld/app: prod/' \ + $DEMO_HOME/Kube-manifest.yaml +``` + +At this point, running `kinflate build` will +generate MySQL configs with name-prefix 'prod-' and +labels `env:prod`. + + +### Download Patch for JVM memory +When a Spring Boot application is deployed in a k8s cluster, the JVM is running inside a container. We want to set memory limit for the container and make sure +the JVM is aware of that limit. In K8s deployment, we can set the resource limits for containers and inject these limits to +some environment variables by downward API. When the container starts to run, it can pick up the environment variables and +set JVM options accordingly. + +Download the patch `memorylimit_patch.yaml`. It contains the memory limits setup. + +``` +cd $DEMO_HOME +wget https://raw.githubusercontent.com/kinflate/example-springboot-instances/master/production/memorylimit_patch.yaml + +cat memorylimit_patch.yaml +``` +The output contains +> ``` +> apiVersion: apps/v1beta2 +> kind: Deployment +> metadata: +> name: sbdemo +> spec: +> template: +> spec: +> containers: +> - name: sbdemo +> resources: +> limits: +> memory: 1250Mi +> requests: +> memory: 1250Mi +> env: +> - name: MEM_TOTAL_MB +> valueFrom: +> resourceFieldRef: +> resource: limits.memory +> ``` + +### Download Patch for health check +We also want to add liveness check and readiness check in the production environment. Spring Boot application +has end points such as `/actuator/health` for this. We can customize the k8s deployment resource to talk to Spring Boot end point. + +Download the patch `healthcheck_patch.yaml`. It contains the liveness probes and readyness probes. + +``` +cd $DEMO_HOME +wget https://raw.githubusercontent.com/kinflate/example-springboot-instances/master/production/healthcheck_patch.yaml + +cat healthcheck_patch.yaml +``` +The output contains +> ``` +> apiVersion: apps/v1beta2 +> kind: Deployment +> metadata: +> name: sbdemo +> spec: +> template: +> spec: +> containers: +> - name: sbdemo +> livenessProbe: +> httpGet: +> path: /actuator/health +> port: 8080 +> initialDelaySeconds: 10 +> periodSeconds: 3 +> readinessProbe: +> initialDelaySeconds: 20 +> periodSeconds: 10 +> httpGet: +> path: /actuator/info +> port: 8080 +> ``` + +### Add patch to Manifest +Currently `kinflate` doesn't provide a command to add a file as a patch, but we can edit the file `Kube-manifest.yaml` to +include this patch. + +``` +mv $DEMO_HOME/Kube-manifest.yaml $DEMO_HOME/tmp.yaml +sed '/patches:$/{N;s/- patch.yaml/- patch.yaml\n- memorylimit_patch.yaml\n- healthcheck_patch.yaml/}' $DEMO_HOME/tmp.yaml >& $DEMO_HOME/Kube-manifest.yaml +``` +`Kube-manifest.yaml` should have patches field: +> ``` +> patches +> - patch.yaml +> - memorylimit_patch.yaml +> - healthcheck_patch.yaml +> ``` + +The output of the following command can now be applied +to the cluster (i.e. piped to `kubectl apply`) to +create the production environment. + + +``` +kinflate build $DEMO_HOME # | kubectl apply -f - +```