diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000000..6ce71ef843 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,36 @@ +# Contributing guidelines + +[Contributor License Agreement]: https://git.k8s.io/community/CLA.md +[github workflow guide]: https://github.com/kubernetes/community/blob/master/contributors/guide/github-workflow.md +[CNCF code of conduct]: https://github.com/cncf/foundation/blob/master/code-of-conduct.md + +## Contributing a Patch + +1. Kubernetes projects require contributors to sign the + [Contributor License Agreement] before pull requests + can be considered. +1. Submit an issue describing your proposed change to + the repo in question. +1. The [repo owners](OWNERS) will respond to your issue + promptly. +1. Fork the repo, develop and test your code. + See the [github workflow guide]. +1. For _new features_, provide a markdown-based demo following + the pattern established in the [demos](demos) directory. + Run `bin/pre-commit.sh` to test your demo. +1. Submit a pull request. + +## Community, discussion, contribution, and support + +Learn how to engage with the Kubernetes community on +the [community page](http://kubernetes.io/community/). + +You can reach the maintainers of this project at: + +- [Slack](http://slack.k8s.io/) +- [Mailing List](https://groups.google.com/forum/#!forum/kubernetes-kustomize) + +## Code of conduct + +Participation in the Kubernetes community is governed +by the [CNCF code of conduct]. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000000..8dada3edaf --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright {yyyy} {name of copyright owner} + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/OWNERS b/OWNERS new file mode 100644 index 0000000000..439aa2aa59 --- /dev/null +++ b/OWNERS @@ -0,0 +1,4 @@ +# See https://github.com/kubernetes/community/blob/master/community-membership.md +approvers: + - kustomize-admins + - kustomize-maintainers diff --git a/OWNERS_ALIASES b/OWNERS_ALIASES new file mode 100644 index 0000000000..1688ed754d --- /dev/null +++ b/OWNERS_ALIASES @@ -0,0 +1,12 @@ +aliases: + kustomize-admins: + - grodrigues3 + - monopole + - pwittrock + kustomize-maintainers: + - droot + - justinsb + - liujingfang1 + - mengqiy + - monopole + - pwittrock diff --git a/README.md b/README.md new file mode 100644 index 0000000000..0221977ab8 --- /dev/null +++ b/README.md @@ -0,0 +1,64 @@ +# kustomize + +[applied]: docs/glossary.md#apply +[base]: docs/glossary.md#base +[declarative configuration]: docs/glossary.md#declarative-application-management +[demo]: demos/README.md +[demos]: demos/README.md +[imageBase]: docs/base.jpg +[imageOverlay]: docs/overlay.jpg +[kubernetes style]: docs/glossary.md#kubernetes-style-object +[kustomization]: docs/glossary.md#kustomization +[overlay]: docs/glossary.md#overlay +[resources]: docs/glossary.md#resource +[sig-cli]: https://github.com/kubernetes/community/blob/master/sig-cli/README.md +[workflows]: docs/workflows.md + +`kustomize` is a command line tool supporting +template-free customization of YAML (or JSON) objects +that conform to the [kubernetes style]. If your +objects have a `kind` and a `metadata` field, +`kustomize` can patch them to support configuration +sharing and re-use. + +For more details, try a [demo]. + +## Installation + +This assumes [Go](https://golang.org/) (v1.10.1 or higher) +is installed and your `PATH` contains `$GOPATH/bin`: + + +``` +go get k8s.io/kubectl/cmd/kustomize +``` + +## Usage + +#### 1) Make a base + +A [base] configuration is a [kustomization] file listing a set of +k8s [resources] - deployments, services, configmaps, +secrets that serve some common purpose. + +![base image][imageBase] + +#### 2) Customize it with overlays + +An [overlay] customizes your base along different dimensions +for different purposes or different teams, e.g. for +_development, staging and production_. + +![overlay image][imageOverlay] + +#### 3) Run kustomize + +Run `kustomize` on your overlay. The result +is printed to `stdout` as a set of complete +resources, ready to be [applied] to a cluster. +See the [demos]. + + +## About + +This project sponsored by [sig-cli]. diff --git a/build/.goreleaser.yml b/build/.goreleaser.yml new file mode 100644 index 0000000000..fc91ac734d --- /dev/null +++ b/build/.goreleaser.yml @@ -0,0 +1,29 @@ +# This is an example goreleaser.yaml file with some sane defaults. +# Make sure to check the documentation at http://goreleaser.com +project_name: kustomize +builds: +- main: ./cmd/kustomize + binary: kustomize + ldflags: -s -X k8s.io/kubectl/cmd/kustomize/version.kustomizeVersion={{.Version}} -X k8s.io/kubectl/cmd/kustomize/version.gitCommit={{.Commit}} -X k8s.io/kubectl/cmd/kustomize/version.buildDate={{.Date}} + goos: + - darwin + - linux + - windows + env: + - CGO_ENABLED=0 +archive: + wrap_in_directory: true +checksum: + name_template: 'checksums.txt' +snapshot: + name_template: "{{ .Tag }}-next" +changelog: + sort: asc + filters: + exclude: + - '^docs:' + - '^test:' +release: + github: + owner: droot + name: kubectl diff --git a/build/README.md b/build/README.md new file mode 100644 index 0000000000..95ba60c790 --- /dev/null +++ b/build/README.md @@ -0,0 +1,16 @@ +## Steps to run build locally + +Install container-builder-local from github and define GOOS, GOARCH, OUTPUT env +variables and run following command + +```sh +container-builder-local --config=cmd/kustomize/build/cloudbuild_local.yaml --dryrun=false --substitutions=_GOOS=$GOOS,_GOARCH=$GOARCH --write-workspace=$OUTPUT . +``` + +## Steps to submit build to Google container builder + +You need to be at the repo level to be able to run the following command + +``` +gcloud container builds submit . --config=cmd/kustomize/build/cloudbuild.yaml --substitutions=_GOOS=$GOOS,_GOARCH=$GOARCH +``` diff --git a/build/build.sh b/build/build.sh new file mode 100755 index 0000000000..1ceba94ccf --- /dev/null +++ b/build/build.sh @@ -0,0 +1,42 @@ +#!/bin/bash +# +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -e +set -x + +# Google Container Builder automatically checks out all the code under the /workspace directory, +# but we actually want it to under the correct expected package in the GOPATH (/go) +# - Create the directory to host the code that matches the expected GOPATH package locations +# - Use /go as the default GOPATH because this is what the image uses +# - Link our current directory (containing the source code) to the package location in the GOPATH + +export PKG=k8s.io +export REPO=kubectl +export CMD=kustomize + +GO_PKG_OWNER=$GOPATH/src/$PKG +GO_PKG_PATH=$GO_PKG_OWNER/$REPO + +mkdir -p $GO_PKG_OWNER +ln -s $(pwd) $GO_PKG_PATH + +# When invoked in container builder, this script runs under /workspace which is +# not under $GOPATH, so we need to `cd` to repo under GOPATH for it to build +cd $GO_PKG_PATH + +/goreleaser release --config=cmd/$CMD/build/.goreleaser.yml --debug --rm-dist +# --skip-validate +# --snapshot --skip-publish diff --git a/build/cloudbuild.yaml b/build/cloudbuild.yaml new file mode 100644 index 0000000000..3df58ecd86 --- /dev/null +++ b/build/cloudbuild.yaml @@ -0,0 +1,25 @@ +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# TODO(droot): add instructions for running in production. +steps: +- name: "ubuntu" + args: ["mkdir", "-p", "/workspace/_output"] +- name: "gcr.io/kustomize-199618/golang_with_goreleaser:1.10-stretch" + args: ["bash", "cmd/kustomize/build/build.sh"] + secretEnv: ['GITHUB_TOKEN'] +secrets: +- kmsKeyName: projects/kustomize-199618/locations/global/keyRings/github-tokens/cryptoKeys/gh-release-token + secretEnv: + GITHUB_TOKEN: CiQAyrREbPgXJOeT7M3t+WlxkhXwlMPudixBeiyWTjmLOMLqdK4SUQA0W+xUmDJKAhyfHCcwqSEzUn9OwKC7XAYcmwe0CCKTCbPbDgmioDK24q3LVapndXNvnnHvCjhOJNEr1o+P1DCF+LlzYV2YL8lP09rrKrslPg== diff --git a/build/cloudbuild_local.yaml b/build/cloudbuild_local.yaml new file mode 100644 index 0000000000..6de58c4dfb --- /dev/null +++ b/build/cloudbuild_local.yaml @@ -0,0 +1,32 @@ +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Instructions to run locally: +# Download google container builder: https://github.com/kubernetes-sigs/container-builder-local +# Set you GOOS and GOARCH vars to match your system +# Set OUTPUT to the location to write the directory containing the tar.gz +# $ container-builder-local --config=build/cloudbuild_local.yaml --dryrun=false \ +# --substitutions=_GOOS=$GOOS,_GOARCH=$GOARCH --write-workspace=$OUTPUT . +# Release tar will be in $OUTPUT + +steps: +- name: "ubuntu" + args: ["mkdir", "-p", "/workspace/_output"] +- name: "gcr.io/kustomize-199618/golang_with_goreleaser:1.10-stretch" + args: ["bash", "cmd/kustomize/build/build.sh"] + secretEnv: ['GITHUB_TOKEN'] +secrets: +- kmsKeyName: projects/kustomize-199618/locations/global/keyRings/github-tokens/cryptoKeys/gh-release-token + secretEnv: + GITHUB_TOKEN: CiQAyrREbPgXJOeT7M3t+WlxkhXwlMPudixBeiyWTjmLOMLqdK4SUQA0W+xUmDJKAhyfHCcwqSEzUn9OwKC7XAYcmwe0CCKTCbPbDgmioDK24q3LVapndXNvnnHvCjhOJNEr1o+P1DCF+LlzYV2YL8lP09rrKrslPg== diff --git a/code-of-conduct.md b/code-of-conduct.md new file mode 100644 index 0000000000..98e34bc24b --- /dev/null +++ b/code-of-conduct.md @@ -0,0 +1,6 @@ +[Kubernetes Community Code of Conduct]: https://git.k8s.io/community/code-of-conduct.md + +# Code of Conduct + +This project has adopted the +[Kubernetes Community Code of Conduct]. diff --git a/demos/README.md b/demos/README.md new file mode 100644 index 0000000000..aef195f517 --- /dev/null +++ b/demos/README.md @@ -0,0 +1,24 @@ +# Demos + +These demos assume that `kustomize` is on your `$PATH`. +They are covered by pre-submit tests. + + * [hello world](helloWorld.md) - Deploy multiple + (differently configured) variants of a simple Hello + World server. + + * [LDAP](ldap.md) - Deploy multiple + (differently configured) variants of a LDAP server. + + * [mySql](mySql.md) - Create a MySQL production + configuration from scratch. + + * [springboot](springboot.md) - Create a Spring Boot + application production configuration from scratch. + + * [configGeneration](configGeneration.md) - + Mixing configuration data from different owners + (e.g. devops/SRE and developers). + + * [breakfast](breakfast.md) - Customize breakfast for + Alice and Bob. diff --git a/demos/breakfast.md b/demos/breakfast.md new file mode 100644 index 0000000000..de902c8855 --- /dev/null +++ b/demos/breakfast.md @@ -0,0 +1,124 @@ +[kubernetes API object style]: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields +[variant]: ../docs/glossary.md#variant + +# Demo: configure breakfast + + +Define a place to work: + + +``` +DEMO_HOME=$(mktemp -d) +``` + +Make a place to put the base breakfast configuration: + + +``` +mkdir -p $DEMO_HOME/breakfast/base +``` + +Make a `kustomization` to define what goes into +breakfast. This breakfast has coffee and pancakes: + + +``` +cat <$DEMO_HOME/breakfast/base/kustomization.yaml +resources: +- coffee.yaml +- pancakes.yaml +EOF +``` + +Here's a _coffee_ type. Give it a `kind` and `metdata/name` field +to conform to [kubernetes API object style]; no other +file or definition is needed: + + +``` +cat <$DEMO_HOME/breakfast/base/coffee.yaml +kind: Coffee +metadata: + name: morningCup +temperature: lukewarm +data: + greeting: "Good Morning!" +EOF +``` + +The `name` field merely distinguishes this instance of +coffee from others (if there were any). + +Likewise, define _pancakes_: + +``` +cat <$DEMO_HOME/breakfast/base/pancakes.yaml +kind: Pancakes +metadata: + name: comfort +stacksize: 3 +topping: none +EOF +``` + +Make a custom [variant] of breakfast for Alice, who +likes her coffee hot: + + +``` +mkdir -p $DEMO_HOME/breakfast/overlays/alice + +cat <$DEMO_HOME/breakfast/overlays/alice/kustomization.yaml +commonLabels: + who: alice +bases: +- ../../base +patches: +- temperature.yaml +EOF + +cat <$DEMO_HOME/breakfast/overlays/alice/temperature.yaml +kind: Coffee +metadata: + name: morningCup +temperature: hot! +EOF +``` + +And likewise a [variant] for Bob, who wants _five_ pancakes, with strawberries: + + +``` +mkdir -p $DEMO_HOME/breakfast/overlays/bob + +cat <$DEMO_HOME/breakfast/overlays/bob/kustomization.yaml +commonLabels: + who: bob +bases: +- ../../base +patches: +- topping.yaml +EOF + +cat <$DEMO_HOME/breakfast/overlays/bob/topping.yaml +kind: Pancakes +metadata: + name: comfort +stacksize: 5 +topping: strawberries +EOF +``` + +One can now generate the configs for Alice’s breakfast: + + +``` +kustomize build $DEMO_HOME/breakfast/overlays/alice +``` + +Likewise for Bob: + + +``` +kustomize build $DEMO_HOME/breakfast/overlays/bob +``` diff --git a/demos/configGeneration.md b/demos/configGeneration.md new file mode 100644 index 0000000000..67524feb5a --- /dev/null +++ b/demos/configGeneration.md @@ -0,0 +1,298 @@ +[overlay]: ../docs/glossary.md#overlay +[target]: ../docs/glossary.md#target + +# Demo: combining config data from devops and developers + +Scenario: you have a Java-based server storefront in +production that various internal development teams +(signups, checkout, search, etc.) contribute to. + +The server runs in different environments: +_development_, _testing_, _staging_ and _production_, +accepting configuration parameters from java property +files. + +Using one big properties file for each environment is +difficult to manage. The files change frequently, and +have to be changed by devops exclusively because + + 1. the files must at least partially agree on certain + values that devops cares about and that developers + ignore and + 1. because the production + properties contain sensitive data like production + database credentials. + +## Property sharding + +With some study, we notice that the properties are +separable into categories. + +### Common properties + +E.g. internationalization data, static data like +physical constants, location of external services, etc. + +_Things that are the same regardless of environment._ + +Only one set of values is needed. + +Place them in a file called + + * `common.properties` + +(relative location defined below). + +### Plumbing properties + +E.g. serving location of static content (HTML, CSS, +javascript), location of product and customer database +tables, ports expected by load balancers, log sinks, +etc. + +_The different values for these properties are +precisely what sets the environments apart._ + +Devops or SRE will want full control over the values +used in production. Testing will have fixed +databases supporting testing. Developers will want +to do whatever they want to try scenarios under +development. + +Places these values in + + * `development/plumbing.properties` + * `staging/plumbing.properties` + * `production/plumbing.properties` + + +### Secret properties + +E.g. location of actual user tables, database +credentials, decryption keys, etc. + +_Things that are a subset of devops controls, that +nobody else has (or should want) access to._ + +Places these values in + + * `development/secret.properties` + * `staging/secret.properties` + * `production/secret.properties` + +[kubernetes secret]: https://kubernetes.io/docs/tasks/inject-data-application/distribute-credentials-secure/ + +and control access to them with (for example) unix file +owner and mode bits, or better yet, put them in +a server dedicated to storing password protected +secrets, and use a field called `secretGenerator` +in your _kustomization_ to create a kubernetes +secret holding them (not covering that here). + + + +## A mixin approach to management + +The way to create _n_ cluster environments that share +some common information is to create _n_ overlays of a +common base. + +For the rest of this example, we'll do _n==2_, just +_development_ and _production_, since adding more +environments follows the same pattern. + +A cluster environment is created by +running `kustomize build` on a [target] that happens to +be an [overlay]. + +[helloworld]: helloworld.md + +The following example will do that, but will focus on +configMap construction, and not worry about how to +connect the configMaps to deployments (that is covered +in the [helloworld] example). + + +All files - including the shared property files +discussed above - will be created in a directory tree +that is consistent with the base vs overlay file layout +defined in the [helloworld] demo. + +It will all live in this work directory: + + +``` +DEMO_HOME=$(mktemp -d) +``` + +### Create the base + + + +Make a place to put the base configuration: + + +``` +mkdir -p $DEMO_HOME/base +``` + +Make the data for the base. This direction by +definition should hold resources common to all +environments. Here we're only defining a java +properties file, and a `kustomization` file that +references it. + + +``` +cat <$DEMO_HOME/base/common.properties +color=blue +height=10m +EOF + +cat <$DEMO_HOME/base/kustomization.yaml +configMapGenerator: +- name: my-configmap + files: + - common.properties +EOF +``` + + +### Create and use the overlay for _development_ + +Make an abbreviation for the parent of the overlay +directories: + + +``` +OVERLAYS=$DEMO_HOME/overlays +``` + +Create the files that define the _development_ overlay: + + +``` +mkdir -p $OVERLAYS/development + +cat <$OVERLAYS/development/plumbing.properties +port=30000 +EOF + +cat <$OVERLAYS/development/secret.properties +dbpassword=mothersMaidenName +EOF + +cat <$OVERLAYS/development/kustomization.yaml +bases: +- ../../base +namePrefix: dev- +configMapGenerator: +- name: my-configmap + behavior: merge + files: + - plumbing.properties + - secret.properties +EOF +``` + +One can now generate the configMaps for development: + + +``` +kustomize build $OVERLAYS/development +``` + +#### Check the ConfigMap name + +The name of the generated `ConfigMap` is visible in this +output. + +The name should be something like `dev-my-configmap-b5m75ck895`: + + * `"dev-"` comes from the `namePrefix` field, + * `"my-configmap"` comes from the `configMapGenerator/name` field, + * `"-b5m75ck895"` comes from a deterministic hash that `kustomize` + computes from the contents of the configMap. + +The hash suffix is critical. If the configMap content +changes, so does the configMap name, along with all +references to that name that appear in the YAML output +from `kustomize`. + +The name change means deployments will do a rolling +restart to get new data if this YAML is applied to the +cluster using a command like + +> ``` +> kustomize build $OVERLAYS/development | kubectl apply -f - +> ``` + +A deployment has no means to automatically know when or +if a configMap in use by the deployment changes. + +If one changes a configMap without changing its name +and all references to that name, one must imperatively +restart the cluster to pick up the change. + +The best practice is to treat configMaps as immutable. + +Instead of editing configMaps, modify your declarative +specification of the cluster's desired state to +point deployments to _new_ configMaps with _new_ names. +`kustomize` makes this easy with its +`configMapGenerator` directive and associated naming +controls. A GC process in the k8s master eventually +deletes unused configMaps. + + +### Create and use the overlay for _production_ + +Next, create the files for the _production_ overlay: + + + +``` +mkdir -p $OVERLAYS/production + +cat <$OVERLAYS/production/plumbing.properties +port=8080 +EOF + +cat <$OVERLAYS/production/secret.properties +dbpassword=thisShouldProbablyBeInASecretInstead +EOF + +cat <$OVERLAYS/production/kustomization.yaml +bases: +- ../../base +namePrefix: prod- +configMapGenerator: +- name: my-configmap + behavior: merge + files: + - plumbing.properties + - secret.properties +EOF +``` + +One can now generate the configMaps for production: + + +``` +kustomize build $OVERLAYS/production +``` + +A CICD process could apply this directly to +the cluser using: + +> ``` +> kustomize build $OVERLAYS/production | kubectl apply -f - +> ``` diff --git a/demos/data/helloWorld/configMap.yaml b/demos/data/helloWorld/configMap.yaml new file mode 100644 index 0000000000..e335ab8cc8 --- /dev/null +++ b/demos/data/helloWorld/configMap.yaml @@ -0,0 +1,7 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: the-map +data: + altGreeting: "Good Morning!" + enableRisky: "false" diff --git a/demos/data/helloWorld/deployment.yaml b/demos/data/helloWorld/deployment.yaml new file mode 100644 index 0000000000..6e79409080 --- /dev/null +++ b/demos/data/helloWorld/deployment.yaml @@ -0,0 +1,30 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: the-deployment +spec: + replicas: 3 + template: + metadata: + labels: + deployment: hello + spec: + containers: + - name: the-container + image: monopole/hello:1 + command: ["/hello", + "--port=8080", + "--enableRiskyFeature=$(ENABLE_RISKY)"] + ports: + - containerPort: 8080 + env: + - name: ALT_GREETING + valueFrom: + configMapKeyRef: + name: the-map + key: altGreeting + - name: ENABLE_RISKY + valueFrom: + configMapKeyRef: + name: the-map + key: enableRisky diff --git a/demos/data/helloWorld/kustomization.yaml b/demos/data/helloWorld/kustomization.yaml new file mode 100644 index 0000000000..6ad61c295d --- /dev/null +++ b/demos/data/helloWorld/kustomization.yaml @@ -0,0 +1,9 @@ +# Example configuration for the webserver +# at https://github.com/monopole/hello +commonLabels: + app: hello + +resources: +- deployment.yaml +- configMap.yaml +- service.yaml diff --git a/demos/data/helloWorld/service.yaml b/demos/data/helloWorld/service.yaml new file mode 100644 index 0000000000..e238f70021 --- /dev/null +++ b/demos/data/helloWorld/service.yaml @@ -0,0 +1,12 @@ +kind: Service +apiVersion: v1 +metadata: + name: the-service +spec: + selector: + deployment: hello + type: LoadBalancer + ports: + - protocol: TCP + port: 8666 + targetPort: 8080 diff --git a/demos/data/ldap/base/deployment.yaml b/demos/data/ldap/base/deployment.yaml new file mode 100644 index 0000000000..9570f5a3d5 --- /dev/null +++ b/demos/data/ldap/base/deployment.yaml @@ -0,0 +1,46 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: ldap + labels: + app: ldap +spec: + replicas: 1 + selector: + matchLabels: + app: ldap + template: + metadata: + labels: + app: ldap + spec: + containers: + - name: ldap + image: osixia/openldap:1.1.11 + args: ["--copy-service"] + volumeMounts: + - name: ldap-data + mountPath: /var/lib/ldap + - name: ldap-config + mountPath: /etc/ldap/slapd.d + - name: ldap-certs + mountPath: /container/service/slapd/assets/certs + - name: configmap-volume + mountPath: /container/environment/01-custom + - name: container-run + mountPath: /container/run + ports: + - containerPort: 389 + name: openldap + volumes: + - name: ldap-data + emptyDir: {} + - name: ldap-config + emptyDir: {} + - name: ldap-certs + emptyDir: {} + - name: "configmap-volume" + configMap: + name: "ldap-configmap" + - name: container-run + emptyDir: {} diff --git a/demos/data/ldap/base/env.startup.txt b/demos/data/ldap/base/env.startup.txt new file mode 100644 index 0000000000..b29c230c8a --- /dev/null +++ b/demos/data/ldap/base/env.startup.txt @@ -0,0 +1,61 @@ +# This is the default image startup configuration file +# this file define environment variables used during the container **first start** in **startup files**. + +# This file is deleted right after startup files are processed for the first time, +# after that all these values will not be available in the container environment. +# This helps to keep your container configuration secret. +# more information : https://github.com/osixia/docker-light-baseimage + +# Required and used for new ldap server only +LDAP_ORGANISATION: Example Inc. +LDAP_DOMAIN: example.org +LDAP_BASE_DN: #if empty automatically set from LDAP_DOMAIN + +LDAP_ADMIN_PASSWORD: admin +LDAP_CONFIG_PASSWORD: config + +LDAP_READONLY_USER: false +LDAP_READONLY_USER_USERNAME: readonly +LDAP_READONLY_USER_PASSWORD: readonly + +LDAP_RFC2307BIS_SCHEMA: false + +# Backend +LDAP_BACKEND: hdb + +# Tls +LDAP_TLS: true +LDAP_TLS_CRT_FILENAME: ldap.crt +LDAP_TLS_KEY_FILENAME: ldap.key +LDAP_TLS_CA_CRT_FILENAME: ca.crt + +LDAP_TLS_ENFORCE: false +LDAP_TLS_CIPHER_SUITE: SECURE256:+SECURE128:-VERS-TLS-ALL:+VERS-TLS1.2:-RSA:-DHE-DSS:-CAMELLIA-128-CBC:-CAMELLIA-256-CBC +LDAP_TLS_VERIFY_CLIENT: demand + +# Replication +LDAP_REPLICATION: false +# variables $LDAP_BASE_DN, $LDAP_ADMIN_PASSWORD, $LDAP_CONFIG_PASSWORD +# are automaticaly replaced at run time + +# if you want to add replication to an existing ldap +# adapt LDAP_REPLICATION_CONFIG_SYNCPROV and LDAP_REPLICATION_DB_SYNCPROV to your configuration +# avoid using $LDAP_BASE_DN, $LDAP_ADMIN_PASSWORD and $LDAP_CONFIG_PASSWORD variables +LDAP_REPLICATION_CONFIG_SYNCPROV: binddn="cn=admin,cn=config" bindmethod=simple credentials=$LDAP_CONFIG_PASSWORD searchbase="cn=config" type=refreshAndPersist retry="60 +" timeout=1 starttls=critical +LDAP_REPLICATION_DB_SYNCPROV: binddn="cn=admin,$LDAP_BASE_DN" bindmethod=simple credentials=$LDAP_ADMIN_PASSWORD searchbase="$LDAP_BASE_DN" type=refreshAndPersist interval=00:00:00:10 retry="60 +" timeout=1 starttls=critical +LDAP_REPLICATION_HOSTS: + - ldap://ldap.example.org # The order must be the same on all ldap servers + - ldap://ldap2.example.org + + +# Do not change the ldap config +# - If set to true with an existing database, config will remain unchanged. Image tls and replication config will not be run. +# The container can be started with LDAP_ADMIN_PASSWORD and LDAP_CONFIG_PASSWORD empty or filled with fake data. +# - If set to true when bootstrapping a new database, bootstap ldif and schema will not be added and tls and replication config will not be run. +KEEP_EXISTING_CONFIG: false + +# Remove config after setup +LDAP_REMOVE_CONFIG_AFTER_SETUP: true + +# ssl-helper environment variables prefix +LDAP_SSL_HELPER_PREFIX: ldap # ssl-helper first search config from LDAP_SSL_HELPER_* variables, before SSL_HELPER_* variables. diff --git a/demos/data/ldap/base/kustomization.yaml b/demos/data/ldap/base/kustomization.yaml new file mode 100644 index 0000000000..6cd10dfadd --- /dev/null +++ b/demos/data/ldap/base/kustomization.yaml @@ -0,0 +1,7 @@ +resources: +- deployment.yaml +- service.yaml +configMapGenerator: +- name: ldap-configmap + files: + - env.startup.txt diff --git a/demos/data/ldap/base/service.yaml b/demos/data/ldap/base/service.yaml new file mode 100644 index 0000000000..5ea479b54e --- /dev/null +++ b/demos/data/ldap/base/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + app: ldap + name: ldap-service +spec: + ports: + - port: 389 + selector: + app: ldap diff --git a/demos/data/ldap/base/tests/test.sh b/demos/data/ldap/base/tests/test.sh new file mode 100755 index 0000000000..f7b0c42f4b --- /dev/null +++ b/demos/data/ldap/base/tests/test.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ---------------------------------------------------- +# +# This script tests the ldap kustomization demo +# against a real cluster. +# +# - deploy a ldap server by the output of kustomize +# - add a user +# - query a user +# - delete a user +# +# This script is a test that (passes|fails) if exit +# code is (0|1). + +set -x + +function exit_with { + local msg=$1 + echo >&2 ${msg} + exit 1 +} + +# make sure kustomize and kubectl are available +command -v kustomize >/dev/null 2>&1 || \ + { exit_with "Require kustomize but it's not installed. Aborting."; } +command -v kubectl >/dev/null 2>&1 || \ + { exit_with "Require kubectl but it's not installed. Aborting."; } + +# set namespace to default +kubectl config set-context $(kubectl config current-context) --namespace=default + +echo Kustomizing \"$1\" +ls $1 +kustomize build $1 > generatedResources.yaml +[[ $? -eq 0 ]] || { exit_with "Failed to kustomize build"; } +cat generatedResources.yaml +kubectl apply -f generatedResources.yaml +[[ $? -eq 0 ]] || { exit_with "Failed to run kubectl apply"; } +sleep 20 + +# get the pod and namespace +pod=$(kubectl get pods -l app=ldap -o jsonpath='{.items[0].metadata.name}') +namespace=$(kubectl get pods -l app=ldap -o jsonpath='{.items[0].metadata.namespace}') +container="ldap" +[[ -z ${pod} ]] && { exit_with "Pod is not started successfully"; } +[[ -z ${namespace} ]] && { exit_with "Couldn't get namespace for Pod ${pod}"; } + +# create a user ldif file locally +ldiffile="user.ldif" +cat <$ldiffile +dn: cn=The Postmaster,dc=example,dc=org +objectClass: organizationalRole +cn: The Postmaster +EOF +[[ -f ${ldiffile} ]] || { exit_with "Failed to create ldif file locally"; } + +# add a user +pod_ldiffile="/tmp/user.ldif" +kubectl cp $ldiffile ${namespace}/${pod}:${pod_ldiffile} || \ + { exit_with "Failed to copy ldif file to Pod ${pod}"; } + +kubectl exec ${pod} -c ${container} -- \ + ldapadd -x -H ldap://localhost -D "cn=admin,dc=example,dc=org" -w admin \ + -f ${pod_ldiffile} || { exit_with "Failed to add a user"; } + +# query the added user +r=$(kubectl exec ${pod} -c ${container} -- \ + ldapsearch -x -H ldap://localhost -b dc=example,dc=org \ + -D "cn=admin,dc=example,dc=org" -w admin) + +user_count=$(echo ${r} | grep "cn: The Postmaster" | wc -l) +[[ ${user_count} -eq 0 ]] && { exit_with "Couldn't find the new added user"; } + +# delete the added user +kubectl exec ${pod} -c ${container} -- \ + ldapdelete -v -x -H ldap://localhost "cn=The Postmaster,dc=example,dc=org" \ + -D "cn=admin,dc=example,dc=org" -w admin || \ + { exit_with "Failed to delete the user"; } + +r=$(kubectl exec ${pod} -c ${container} -- \ + ldapsearch -x -H ldap://localhost -b dc=example,dc=org \ + -D "cn=admin,dc=example,dc=org" -w admin) +user_count=$(echo ${r} | grep "cn: The Postmaster" | wc -l) +[[ ${user_count} -ne 0 ]] && { exit_with "The user hasn't been deleted."; } + +# kubectl delete +kubectl delete -f generatedResources.yaml +rm $ldiffile diff --git a/demos/data/ldap/overlays/production/deployment.yaml b/demos/data/ldap/overlays/production/deployment.yaml new file mode 100644 index 0000000000..3e535c4921 --- /dev/null +++ b/demos/data/ldap/overlays/production/deployment.yaml @@ -0,0 +1,13 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: ldap +spec: + replicas: 6 + template: + spec: + volumes: + - name: ldap-data + emptyDir: null + gcePersistentDisk: + pdName: ldap-persistent-storage diff --git a/demos/data/ldap/overlays/production/kustomization.yaml b/demos/data/ldap/overlays/production/kustomization.yaml new file mode 100644 index 0000000000..a8dda51ca6 --- /dev/null +++ b/demos/data/ldap/overlays/production/kustomization.yaml @@ -0,0 +1,5 @@ +bases: + - ../../base +patches: + - deployment.yaml +namePrefix: production- diff --git a/demos/data/ldap/overlays/staging/config.env b/demos/data/ldap/overlays/staging/config.env new file mode 100644 index 0000000000..c4032090a0 --- /dev/null +++ b/demos/data/ldap/overlays/staging/config.env @@ -0,0 +1,2 @@ +DB_USERNAME=admin +DB_PASSWORD=somepw diff --git a/demos/data/ldap/overlays/staging/deployment.yaml b/demos/data/ldap/overlays/staging/deployment.yaml new file mode 100644 index 0000000000..792f4d0a29 --- /dev/null +++ b/demos/data/ldap/overlays/staging/deployment.yaml @@ -0,0 +1,7 @@ + +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: ldap +spec: + replicas: 2 diff --git a/demos/data/ldap/overlays/staging/kustomization.yaml b/demos/data/ldap/overlays/staging/kustomization.yaml new file mode 100644 index 0000000000..7688bea730 --- /dev/null +++ b/demos/data/ldap/overlays/staging/kustomization.yaml @@ -0,0 +1,9 @@ +bases: + - ../../base +patches: + - deployment.yaml +nameprefix: staging- +configMapGenerator: + - name: env-config + files: + - config.env diff --git a/demos/data/mySql/deployment.yaml b/demos/data/mySql/deployment.yaml new file mode 100644 index 0000000000..6aa7bc72bf --- /dev/null +++ b/demos/data/mySql/deployment.yaml @@ -0,0 +1,35 @@ +apiVersion: apps/v1beta2 # for versions before 1.9.0 use apps/v1beta2 +kind: Deployment +metadata: + name: mysql + labels: + app: mysql +spec: + selector: + matchLabels: + app: mysql + strategy: + type: Recreate + template: + metadata: + labels: + app: mysql + spec: + containers: + - image: mysql:5.6 + name: mysql + env: + - name: MYSQL_ROOT_PASSWORD + valueFrom: + secretKeyRef: + name: mysql-pass + key: password + ports: + - containerPort: 3306 + name: mysql + volumeMounts: + - name: mysql-persistent-storage + mountPath: /var/lib/mysql + volumes: + - name: mysql-persistent-storage + emptyDir: {} diff --git a/demos/data/mySql/secret.yaml b/demos/data/mySql/secret.yaml new file mode 100644 index 0000000000..935f2305e6 --- /dev/null +++ b/demos/data/mySql/secret.yaml @@ -0,0 +1,9 @@ +apiVersion: v1 +kind: Secret +metadata: + creationTimestamp: null + name: mysql-pass-d2gtcm2t2k +type: Opaque +data: + # Default password is "admin". + password: YWRtaW4= diff --git a/demos/data/mySql/service.yaml b/demos/data/mySql/service.yaml new file mode 100644 index 0000000000..aa3f970b6a --- /dev/null +++ b/demos/data/mySql/service.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: Service +metadata: + name: mysql + labels: + app: mysql +spec: + ports: + - port: 3306 + selector: + app: mysql diff --git a/demos/data/springboot/base/deployment.yaml b/demos/data/springboot/base/deployment.yaml new file mode 100644 index 0000000000..62fb2e343d --- /dev/null +++ b/demos/data/springboot/base/deployment.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: sbdemo + labels: + app: sbdemo +spec: + selector: + matchLabels: + app: sbdemo + template: + metadata: + labels: + app: sbdemo + spec: + containers: + - name: sbdemo + image: jingfang/sbdemo + ports: + - containerPort: 8080 + volumeMounts: + - name: demo-config + mountPath: /config + volumes: + - name: "demo-config" + configMap: + name: "demo-configmap" diff --git a/demos/data/springboot/base/service.yaml b/demos/data/springboot/base/service.yaml new file mode 100644 index 0000000000..88f3cdd489 --- /dev/null +++ b/demos/data/springboot/base/service.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: Service +metadata: + name: sbdemo + labels: + app: sbdemo +spec: + ports: + - port: 8080 + selector: + app: sbdemo + type: LoadBalancer diff --git a/demos/data/springboot/overlays/production/application.properties b/demos/data/springboot/overlays/production/application.properties new file mode 100644 index 0000000000..253ffd85ed --- /dev/null +++ b/demos/data/springboot/overlays/production/application.properties @@ -0,0 +1,5 @@ +app.name=Staging Kinflate Demo +spring.jpa.hibernate.ddl-auto=update +spring.datasource.url=jdbc:mysql://:3306/db_example +spring.datasource.username=root +spring.datasource.password=admin diff --git a/demos/data/springboot/overlays/production/healthcheck_patch.yaml b/demos/data/springboot/overlays/production/healthcheck_patch.yaml new file mode 100644 index 0000000000..c8a0cfaec6 --- /dev/null +++ b/demos/data/springboot/overlays/production/healthcheck_patch.yaml @@ -0,0 +1,22 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: sbdemo +spec: + replicas: 2 + 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 diff --git a/demos/data/springboot/overlays/production/kustomization.yaml b/demos/data/springboot/overlays/production/kustomization.yaml new file mode 100644 index 0000000000..121f384b09 --- /dev/null +++ b/demos/data/springboot/overlays/production/kustomization.yaml @@ -0,0 +1,7 @@ +bases: +- ../../base +patches: +- patch.yaml +- healthcheck_patch.yaml +- memorylimit_patch.yaml +namePrefix: production- diff --git a/demos/data/springboot/overlays/production/memorylimit_patch.yaml b/demos/data/springboot/overlays/production/memorylimit_patch.yaml new file mode 100644 index 0000000000..a46d333114 --- /dev/null +++ b/demos/data/springboot/overlays/production/memorylimit_patch.yaml @@ -0,0 +1,20 @@ +apiVersion: apps/v1beta2 +kind: Deployment +metadata: + name: sbdemo +spec: + replicas: 2 + template: + spec: + containers: + - name: sbdemo + resources: + limits: + memory: 1250Mi + requests: + memory: 1250Mi + env: + - name: MEM_TOTAL_MB + valueFrom: + resourceFieldRef: + resource: limits.memory diff --git a/demos/data/springboot/overlays/production/patch.yaml b/demos/data/springboot/overlays/production/patch.yaml new file mode 100644 index 0000000000..16dcd086a2 --- /dev/null +++ b/demos/data/springboot/overlays/production/patch.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: demo-configmap +data: + application.properties: | + app.name=Staging Kinflate Demo + spring.jpa.hibernate.ddl-auto=update + spring.datasource.url=jdbc:mysql://:3306/db_example + spring.datasource.username=root + spring.datasource.password=admin + server.tomcat.max-threads=20 + server.tomcat.min-spare-threads=3 diff --git a/demos/data/springboot/overlays/staging/application.properties b/demos/data/springboot/overlays/staging/application.properties new file mode 100644 index 0000000000..5ed56eb974 --- /dev/null +++ b/demos/data/springboot/overlays/staging/application.properties @@ -0,0 +1,5 @@ +app.name=Staging Kinflate Demo +spring.jpa.hibernate.ddl-auto=update +spring.datasource.url=jdbc:mysql://:3306/db_example +spring.datasource.username=root +spring.datasource.password=admin diff --git a/demos/data/springboot/overlays/staging/kustomization.yaml b/demos/data/springboot/overlays/staging/kustomization.yaml new file mode 100644 index 0000000000..f2f34a742b --- /dev/null +++ b/demos/data/springboot/overlays/staging/kustomization.yaml @@ -0,0 +1,11 @@ +bases: +- ../../base +namePrefix: staging- +configMapGenerator: +- name: demo-configmap + behavior: merge + files: + - application.properties + literals: + - foo=bar + env: staging.env diff --git a/demos/data/springboot/overlays/staging/staging.env b/demos/data/springboot/overlays/staging/staging.env new file mode 100644 index 0000000000..9f854e54d9 --- /dev/null +++ b/demos/data/springboot/overlays/staging/staging.env @@ -0,0 +1 @@ +staging \ No newline at end of file diff --git a/demos/helloWorld.md b/demos/helloWorld.md new file mode 100644 index 0000000000..731e7ce92a --- /dev/null +++ b/demos/helloWorld.md @@ -0,0 +1,432 @@ +[base]: ../docs/glossary.md#base +[config]: https://github.com/kinflate/example-hello +[gitops]: ../docs/glossary.md#gitops +[hello]: https://github.com/monopole/hello +[kustomization]: ../docs/glossary.md#kustomization +[original]: https://github.com/kinflate/example-hello +[overlay]: ../docs/glossary.md#overlay +[overlays]: ../docs/glossary.md#overlay +[variant]: ../docs/glossary.md#variant +[variants]: ../docs/glossary.md#variant + +# Demo: hello world with variants + +Steps: + + 1. Clone an existing configuration as a [base]. + 1. Customize it. + 1. Create two different [overlays] (_staging_ and _production_) + from the customized base. + 1. Run kustomize and kubectl to deploy staging and production. + +First define a place to work: + + +``` +DEMO_HOME=$(mktemp -d) +``` + +Alternatively, use + +> ``` +> DEMO_HOME=~/hello +> ``` + +## Establish the base + +Let's run the [hello] service. + +To use [overlays] to create [variants], we must +first establish a common [base]. + +To keep this document shorter, the base resources are +off in a supplemental data directory rather than +declared here as HERE documents. Download them: + + +``` +BASE=$DEMO_HOME/base +mkdir -p $BASE + +resources="https://raw.githubusercontent.com/kubernetes/kubectl\ +/master/cmd/kustomize/demos/data/helloWorld\ +/{configMap,deployment,kustomization,service}.yaml" + +curl -s $resources -o "$BASE/#1.yaml" +``` + +Look at the directory: + + +``` +tree $DEMO_HOME +``` + +Expect something like: + +> ``` +> /tmp/tmp.IyYQQlHaJP +> └── base +> ├── configMap.yaml +> ├── deployment.yaml +> ├── kustomization.yaml +> └── service.yaml +> ``` + + +One could immediately apply these resources to a +cluster: + +> ``` +> kubectl apply -f $DEMO_HOME/base +> ``` + +to instantiate the _hello_ service. `kubectl` +would only recognize the resource files. + +### The Base Kustomization + +The `base` directory has a [kustomization] file: + + +``` +more $BASE/kustomization.yaml +``` + +Optionally, run `kustomize` on the base to emit +customized resources to `stdout`: + + +``` +kustomize build $BASE +``` + +### Customize the base + +A first customization step could be to change the _app +label_ applied to all resources: + + +``` +sed -i 's/app: hello/app: my-hello/' \ + $BASE/kustomization.yaml +``` + +See the effect: + +``` +kustomize build $BASE | grep -C 3 app: +``` + +## Create Overlays + +Create a _staging_ and _production_ [overlay]: + + * _Staging_ enables a risky feature not enabled in production. + * _Production_ has a higher replica count. + * Web server greetings from these cluster + [variants] will differ from each other. + + +``` +OVERLAYS=$DEMO_HOME/overlays +mkdir -p $OVERLAYS/staging +mkdir -p $OVERLAYS/production +``` + +#### Staging Kustomization + +In the `staging` directory, make a kustomization +defining a new name prefix, and some different labels. + + +``` +cat <<'EOF' >$OVERLAYS/staging/kustomization.yaml +namePrefix: staging- +commonLabels: + variant: staging + org: acmeCorporation +commonAnnotations: + note: Hello, I am staging! +bases: +- ../../base +patches: +- map.yaml +EOF +``` + +#### Staging Patch + +Add a configMap customization to change the server +greeting from _Good Morning!_ to _Have a pineapple!_ + +Also, enable the _risky_ flag. + + +``` +cat <$OVERLAYS/staging/map.yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: the-map +data: + altGreeting: "Have a pineapple!" + enableRisky: "true" +EOF +``` + +#### Production Kustomization + +In the production directory, make a kustomization +with a different name prefix and labels. + + +``` +cat <$OVERLAYS/production/kustomization.yaml +namePrefix: production- +commonLabels: + variant: production + org: acmeCorporation +commonAnnotations: + note: Hello, I am production! +bases: +- ../../base +patches: +- deployment.yaml +EOF +``` + + +#### Production Patch + +Make a production patch that increases the replica +count (because production takes more traffic). + + +``` +cat <$OVERLAYS/production/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: the-deployment +spec: + replicas: 10 +EOF +``` + +## Compare overlays + + +`DEMO_HOME` now contains: + + - a _base_ directory - a slightly customized clone + of the original configuration, and + + - an _overlays_ directory, containing the kustomizations + and patches required to create distinct _staging_ + and _production_ [variants] in a cluster. + +Review the directory structure and differences: + + +``` +tree $DEMO_HOME +``` + +Expecting something like: + +> ``` +> /tmp/tmp.IyYQQlHaJP1 +> ├── base +> │   ├── configMap.yaml +> │   ├── deployment.yaml +> │   ├── kustomization.yaml +> │   └── service.yaml +> └── overlays +> ├── production +> │   ├── deployment.yaml +> │   └── kustomization.yaml +> └── staging +> ├── kustomization.yaml +> └── map.yaml +> ``` + +Compare the output directly +to see how _staging_ and _production_ differ: + + +``` +diff \ + <(kustomize build $OVERLAYS/staging) \ + <(kustomize build $OVERLAYS/production) |\ + more +``` + +The first part of the difference output should look +something like + +> ```diff +> < altGreeting: Have a pineapple! +> < enableRisky: "true" +> --- +> > altGreeting: Good Morning! +> > enableRisky: "false" +> 8c8 +> < note: Hello, I am staging! +> --- +> > note: Hello, I am production! +> 11c11 +> < variant: staging +> --- +> > variant: production +> 13c13 +> (...truncated) +> ``` + + +## Deploy + +The individual resource sets are: + + +``` +kustomize build $OVERLAYS/staging +``` + + +``` +kustomize build $OVERLAYS/production +``` + +To deploy, pipe the above commands to kubectl apply: + +> ``` +> kustomize build $OVERLAYS/staging |\ +> kubectl apply -f - +> ``` + +> ``` +> kustomize build $OVERLAYS/production |\ +> kubectl apply -f - +> ``` + +## Rolling updates + +### Review + +The _hello-world_ deployment running in this cluster is +configured with data from a configMap. + +The deployment refers to this map by name: + + + +``` +grep -C 2 configMapKeyRef $DEMO_HOME/base/deployment.yaml +``` + +Changing the data held by a live configMap in a cluster +is considered bad practice. Deployments have no means +to know that the configMaps they refer to have +changed, so such updates have no effect. + +The recommended way to change a deployment's +configuration is to + + 1. create a new configMap with a new name, + 1. patch the _deployment_, modifying the name value of + the appropriate `configMapKeyRef` field. + +This latter change initiates rolling update to the pods +in the deployment. The older configMap, when no longer +referenced by any other resource, is eventually garbage +collected. + +### How this works with kustomize + +[patch]: ../docs/glossary.md#patch + +The _staging_ [variant] here has a configMap [patch]: + + +``` +cat $OVERLAYS/staging/map.yaml +``` + +This patch is by definition a named but not necessarily +complete resource spec intended to modify a complete +resource spec. + +The resource it modifies is here: + + +``` +cat $DEMO_HOME/base/configMap.yaml +``` + +For a patch to work, the names in the `metadata/name` +fields must match. + +However, the name values specified in the file are +_not_ what gets used in the cluster. By design, +kustomize modifies these names. To see the names +ultimately used in the cluster, just run kustomize: + + +``` +kustomize build $OVERLAYS/staging |\ + grep -B 8 -A 1 staging-the-map +``` + +The configMap name is prefixed by _staging-_, per the +`namePrefix` field in +`$OVERLAYS/staging/kustomization.yaml`. + +The suffix to the configMap name is generated from a +hash of the maps content - in this case the name suffix +is _hhhhkfmgmk_: + + +``` +kustomize build $OVERLAYS/staging | grep hhhhkfmgmk +``` + +Now modify the map patch, to change the greeting +the server will use: + + +``` +sed -i 's/pineapple/kiwi/' $OVERLAYS/staging/map.yaml +``` + +Run kustomize again to see the new names: + + +``` +kustomize build $OVERLAYS/staging |\ + grep -B 8 -A 1 staging-the-map +``` + +Confirm that the change in configMap content resulted +in three new names ending in _khk45ktkd9_ - one in the +configMap name itself, and two in the deployment that +uses the map: + + +``` +test 3 == \ + $(kustomize build $OVERLAYS/staging | grep khk45ktkd9 | wc -l) +``` + +Applying these resources to the cluster will result in +a rolling update of the deployments pods, retargetting +them from the _hhhhkfmgmk_ maps to the _khk45ktkd9_ +maps. The system will later garbage collect the +unused maps. + +## Rollback + +To rollback, one would undo whatever edits were made to +the configuation in source control, then rerun kustomize +on the reverted configuration and apply it to the +cluster. diff --git a/demos/ldap.md b/demos/ldap.md new file mode 100644 index 0000000000..ae286c3c72 --- /dev/null +++ b/demos/ldap.md @@ -0,0 +1,284 @@ +[base]: ../docs/glossary.md#base +[gitops]: ../docs/glossary.md#gitops +[kustomization]: ../docs/glossary.md#kustomization +[overlay]: ../docs/glossary.md#overlay +[overlays]: ../docs/glossary.md#overlay +[variant]: ../docs/glossary.md#variant +[variants]: ../docs/glossary.md#variant + +# Demo: LDAP with variants + +Steps: + + 1. Clone an existing configuration as a [base]. + 1. Customize it. + 1. Create two different [overlays] (_staging_ and _production_) + from the customized base. + 1. Run kustomize and kubectl to deploy staging and production. + +First define a place to work: + + +``` +DEMO_HOME=$(mktemp -d) +``` + +Alternatively, use + +> ``` +> DEMO_HOME=~/ldap +> ``` + +## Establish the base + +To use [overlays] to create [variants], we must +first establish a common [base]. + +To keep this document shorter, the base resources are +off in a supplemental data directory rather than +declared here as HERE documents. Download them: + + +``` +BASE=$DEMO_HOME/base +mkdir -p $BASE + +resources="https://raw.githubusercontent.com/kubernetes/kubectl\ +/master/cmd/kustomize/demos/data/ldap/base\ +/{deployment.yaml,kustomization.yaml,service.yaml,env.startup.txt}" + +curl -s $resources -o "$BASE/#1" +``` + +Look at the directory: + + +``` +tree $DEMO_HOME +``` + +Expect something like: + +> ``` +> /tmp/tmp.IyYQQlHaJP +> └── base +> ├── deployment.yaml +> ├── env.startup.txt +> ├── kustomization.yaml +> └── service.yaml +> ``` + + +One could immediately apply these resources to a +cluster: + +> ``` +> kubectl apply -f $DEMO_HOME/base +> ``` + +to instantiate the _ldap_ service. `kubectl` +would only recognize the resource files. + +### The Base Kustomization + +The `base` directory has a [kustomization] file: + + +``` +more $BASE/kustomization.yaml +``` + +Optionally, run `kustomize` on the base to emit +customized resources to `stdout`: + + +``` +kustomize build $BASE +``` + +### Customize the base + +A first customization step could be to set the name prefix to all resources: + + +``` +cd $BASE +kustomize edit set nameprefix "my-" +``` + +See the effect: + +``` +kustomize build $BASE | grep -C 3 "my-" +``` + +## Create Overlays + +Create a _staging_ and _production_ [overlay]: + + * _Staging_ adds a configMap. + * _Production_ has a higher replica count and a persistent disk. + * [variants] will differ from each other. + + +``` +OVERLAYS=$DEMO_HOME/overlays +mkdir -p $OVERLAYS/staging +mkdir -p $OVERLAYS/production +``` + +#### Staging Kustomization + +Download the staging customization and patch. + +``` +resources="https://raw.githubusercontent.com/kubernetes/kubectl\ +/master/cmd/kustomize/demos/data/ldap/overlays/staging\ +/{config.env,deployment.yaml,kustomization.yaml}" + +curl -s $resources -o "$OVERLAYS/staging/#1" +``` +The staging customization adds a configMap. +> ```cat $OVERLAYS/staging/kustomization.yaml +> (...truncated) +> configMapGenerator: +> - name: env-config +> files: +> - config.env +> ``` +as well as 2 replica +> ```cat $OVERLAYS/staging/deployment.yaml +> apiVersion: apps/v1beta2 +> kind: Deployment +> metadata: +> name: ldap +> spec: +> replicas: 2 +> ``` + +#### Production Kustomization + +Download the production customization and patch. + +``` +resources="https://raw.githubusercontent.com/kubernetes/kubectl\ +/master/cmd/kustomize/demos/data/ldap/overlays/production\ +/{deployment.yaml,kustomization.yaml}" + +curl -s $resources -o "$OVERLAYS/production/#1" +``` + +The production customization adds 6 replica as well as a consistent disk. +> ```cat $OVERLAYS/production/deployment.yaml +> apiVersion: apps/v1beta2 +> kind: Deployment +> metadata: +> name: ldap +> spec: +> replicas: 6 +> template: +> spec: +> volumes: +> - name: ldap-data +> emptyDir: null +> gcePersistentDisk: +> pdName: ldap-persistent-storage +> ``` + +## Compare overlays + + +`DEMO_HOME` now contains: + + - a _base_ directory - a slightly customized clone + of the original configuration, and + + - an _overlays_ directory, containing the kustomizations + and patches required to create distinct _staging_ + and _production_ [variants] in a cluster. + +Review the directory structure and differences: + + +``` +tree $DEMO_HOME +``` + +Expecting something like: + +> ``` +> /tmp/tmp.IyYQQlHaJP1 +> ├── base +> │   ├── deployment.yaml +> │   ├── env.startup.txt +> │   ├── kustomization.yaml +> │   └── service.yaml +> └── overlays +> ├── production +> │   ├── deployment.yaml +> │   └── kustomization.yaml +> └── staging +> ├── config.env +> ├── deployment.yaml +> └── kustomization.yaml +> ``` + +Compare the output directly +to see how _staging_ and _production_ differ: + + +``` +diff \ + <(kustomize build $OVERLAYS/staging) \ + <(kustomize build $OVERLAYS/production) |\ + more +``` + +The difference output should look something like + +> ```diff +> (...truncated) +> < name: staging-my-ldap-configmap-kftftt474h +> --- +> > name: production-my-ldap-configmap-k27f7hkg4f +> 85c75 +> < name: staging-my-ldap-service +> --- +> > name: production-my-ldap-service +> 97c87 +> < name: staging-my-ldap +> --- +> > name: production-my-ldap +> 99c89 +> < replicas: 2 +> --- +> > replicas: 6 +> (...truncated) +> ``` + + +## Deploy + +The individual resource sets are: + + +``` +kustomize build $OVERLAYS/staging +``` + + +``` +kustomize build $OVERLAYS/production +``` + +To deploy, pipe the above commands to kubectl apply: + +> ``` +> kustomize build $OVERLAYS/staging |\ +> kubectl apply -f - +> ``` + +> ``` +> kustomize build $OVERLAYS/production |\ +> kubectl apply -f - +> ``` diff --git a/demos/mySql.md b/demos/mySql.md new file mode 100644 index 0000000000..e9504a054a --- /dev/null +++ b/demos/mySql.md @@ -0,0 +1,203 @@ +# Demo: MySql + +This example takes some off-the-shelf k8s resources +designed for MySQL, and customizes them to suit a +production scenario. + +In the production environment we want: + +- MySQL resource names to be prefixed by 'prod-'. +- MySQL resources to have 'env: prod' labels. +- MySQL to use persistent disk for storing data. + +First make a place to work: + +``` +DEMO_HOME=$(mktemp -d) +``` + +### Download resources + +To keep this document shorter, the base resources +needed to run MySql on a k8s cluster are off in a +supplemental data directory rather than declared here +as HERE documents. + +Download them: + + +``` +curl -s -o "$DEMO_HOME/#1.yaml" \ + "https://raw.githubusercontent.com/kubernetes/kubectl\ +/master/cmd/kustomize/demos/data/mySql\ +/{deployment,secret,service}.yaml" +``` + +### Initialize kustomization.yaml + +The `kustomize` program gets its instructions from +a file called `kustomization.yaml`. + +Start this file: + + +``` +touch $DEMO_HOME/kustomization.yaml +``` + +### Add the resources + + +``` +cd $DEMO_HOME + +kustomize edit add resource secret.yaml +kustomize edit add resource service.yaml +kustomize edit add resource deployment.yaml + +cat kustomization.yaml +``` + +`kustomization.yaml`'s resources section should contain: + +> ``` +> resources: +> - secret.yaml +> - service.yaml +> - deployment.yaml +> ``` + +### Name Customization + +Arrange for the MySQL resources to begin with prefix +_prod-_ (since they are meant for the _production_ +environment): + + +``` +cd $DEMO_HOME + +kustomize edit set nameprefix 'prod-' + +cat kustomization.yaml +``` + +`kustomization.yaml` should have updated value of namePrefix field: + +> ``` +> namePrefix: prod- +> ``` + +This `namePrefix` directive adds _prod-_ to all +resource names. + + +``` +kustomize build $DEMO_HOME +``` + +The output should contain: + +> ``` +> apiVersion: v1 +> data: +> password: YWRtaW4= +> kind: Secret +> metadata: +> .... +> name: prod-mysql-pass-d2gtcm2t2k +> --- +> apiVersion: v1 +> kind: Service +> metadata: +> .... +> name: prod-mysql +> spec: +> .... +> --- +> apiVersion: apps/v1beta2 +> kind: Deployment +> metadata: +> .... +> name: prod-mysql +> spec: +> selector: +> .... +> ``` + +### Label Customization + +We want resources in production environment to have +certain labels so that we can query them by label +selector. + +`kustomize` does not have `edit set label` command to add +a label, but one can always edit `kustomization.yaml` directly: + + +``` +sed -i 's/app: helloworld/app: prod/' \ + $DEMO_HOME/kustomization.yaml +``` + +At this point, running `kustomize build` will +generate MySQL configs with name-prefix 'prod-' and +labels `env:prod`. + +### Storage customization + +Off the shelf MySQL uses `emptyDir` type volume, which +gets wiped away if the MySQL Pod is recreated, and that +is certainly not desirable for production +environment. So we want to use Persistent Disk in +production. kustomize lets you apply `patches` to the +resources. + + +``` +cat <<'EOF' > $DEMO_HOME/persistent-disk.yaml +apiVersion: apps/v1beta2 # for versions before 1.9.0 use apps/v1beta2 +kind: Deployment +metadata: + name: mysql +spec: + template: + spec: + volumes: + - name: mysql-persistent-storage + emptyDir: null + gcePersistentDisk: + pdName: mysql-persistent-storage +EOF +``` + +Add the patch file to `kustomization.yaml`: + + +``` +cat <<'EOF' >> $DEMO_HOME/kustomization.yaml +patches: +- persistent-disk.yaml +EOF +``` + +Lets break this down: + +- In the first step, we created a YAML file named + `persistent-disk.yaml` to patch the resource defined + in deployment.yaml + +- Then we added `persistent-disk.yaml` to list of + `patches` in `kustomization.yaml`. `kustomize build` + will apply this patch to the deployment resource with + the name `mysql` as defined in the patch. + + +The output of the following command can now be applied +to the cluster (i.e. piped to `kubectl apply`) to +create the production environment. + + +``` +kustomize build $DEMO_HOME # | kubectl apply -f - +``` diff --git a/demos/springboot.md b/demos/springboot.md new file mode 100644 index 0000000000..cac5fc2564 --- /dev/null +++ b/demos/springboot.md @@ -0,0 +1,306 @@ +# Demo: SpringBoot + +In this tutorial, you will learn - how to use `kustomize` 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. + +First make a place to work: + +``` +DEMO_HOME=$(mktemp -d) +``` + +### Download resources + +To keep this document shorter, the base resources +needed to run springboot on a k8s cluster are off in a +supplemental data directory rather than declared here +as HERE documents. + +Download them: + + +``` +CONTENT="https://raw.githubusercontent.com/kubernetes/kubectl\ +/master/cmd/kustomize/demos/data/springboot" + +curl -s -o "$DEMO_HOME/#1.yaml" \ + "$CONTENT/base/{deployment,service}.yaml" +``` + +### Initialize kustomization.yaml + +The `kustomize` program gets its instructions from +a file called `kustomization.yaml`. + +Start this file: + + +``` +touch $DEMO_HOME/kustomization.yaml +``` + +### Add the resources + + +``` +cd $DEMO_HOME + +kustomize edit add resource service.yaml +kustomize edit add resource deployment.yaml + +cat kustomization.yaml +``` + +`kustomization.yaml`'s resources section should contain: + +> ``` +> resources: +> - service.yaml +> - deployment.yaml +> ``` + +### Add configMap generator + + +``` +echo "app.name=Kustomize Demo" >$DEMO_HOME/application.properties + +kustomize edit add configmap demo-configmap \ + --from-file application.properties + +cat kustomization.yaml +``` + +`kustomization.yaml`'s configMapGenerator section should contain: + +> ``` +> configMapGenerator: +> - files: +> - application.properties +> name: demo-configmap +> ``` + +### 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 + +kustomize edit add patch patch.yaml + +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 + +kustomize edit add configmap \ + demo-configmap --from-file application-prod.properties + +cat kustomization.yaml +``` + +`kustomization.yaml`'s configMapGenerator section should contain: +> ``` +> configMapGenerator: +> - files: +> - application.properties +> - application-prod.properties +> name: demo-configmap +> ``` + +### Name Customization + +Arrange for the resources to begin with prefix +_prod-_ (since they are meant for the _production_ +environment): + + +``` +cd $DEMO_HOME +kustomize edit set nameprefix 'prod-' +``` + +`kustomization.yaml` should have updated value of namePrefix field: + +> ``` +> namePrefix: prod- +> ``` + +This `namePrefix` directive adds _prod-_ to all +resource names, as can be seen by building the +resources: + + +``` +kustomize build $DEMO_HOME | grep prod- +``` + +### Label Customization + +We want resources in production environment to have +certain labels so that we can query them by label +selector. + +`kustomize` does not have `edit set label` command to +add a label, but one can always edit +`kustomization.yaml` directly: + + +``` +cat <>$DEMO_HOME/kustomization.yaml +commonLabels: + env: prod +EOF +``` + +Confirm that the resources now all have names prefixed +by `prod-` and the label tuple `env:prod`: + + +``` +kustomize build $DEMO_HOME | grep -C 3 env +``` + +### 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. + + +``` +curl -s -o "$DEMO_HOME/#1.yaml" \ + "$CONTENT/overlays/production/{memorylimit_patch}.yaml" + +cat $DEMO_HOME/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. + + +``` +curl -s -o "$DEMO_HOME/#1.yaml" \ + "$CONTENT/overlays/production/{healthcheck_patch}.yaml" + +cat $DEMO_HOME/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 patches + +Add these patches to the kustomization: + + +``` +cd $DEMO_HOME +kustomize edit add patch memorylimit_patch.yaml +kustomize edit add patch healthcheck_patch.yaml +``` + +`kustomization.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. + + +``` +kustomize build $DEMO_HOME # | kubectl apply -f - +``` diff --git a/docs/base.jpg b/docs/base.jpg new file mode 100644 index 0000000000..f345362894 Binary files /dev/null and b/docs/base.jpg differ diff --git a/docs/glossary.md b/docs/glossary.md new file mode 100644 index 0000000000..35445b4f83 --- /dev/null +++ b/docs/glossary.md @@ -0,0 +1,318 @@ +# Glossary + +[DAM]: #declarative-application-management +[JSON]: https://www.json.org/ +[Resource]: #resource +[YAML]: http://www.yaml.org/start.html +[application]: #application +[apply]: #apply +[apt]: https://en.wikipedia.org/wiki/APT_(Debian) +[base]: #base +[bases]: #base +[bespoke]: #bespoke-configuration +[gitops]: #gitops +[k8s]: #kubernetes +[kubernetes]: #kubernetes +[kustomize]: #kustomize +[kustomization]: #kustomization +[off-the-shelf]: #off-the-shelf +[overlay]: #overlay +[overlays]: #overlay +[patch]: #patch +[patches]: #patch +[proposal]: https://github.com/kubernetes/community/pull/1629 +[rebase]: https://git-scm.com/docs/git-rebase +[resource]: #resource +[resources]: #resource +[rpm]: https://en.wikipedia.org/wiki/Rpm_(software) +[target]: #target +[variant]: #variant +[variants]: #variant +[workflow]: workflows.md + +## application + +An _application_ is a group of k8s resources related by +some common purpose, e.g. a load balancer in front of a +webserver backed by a database. +[Resource] labelling, naming and metadata schemes have +historically served to group resources together for +collective operations like _list_ and _remove_. + +This [proposal] describes a new k8s resource called +_application_ to more formally describe this idea and +provide support for application-level operations and +dashboards. + +[kustomize] configures k8s resources, and the proposed +application resource is just another resource. + + +## apply + +The verb _apply_ in the context of k8s refers to a +kubectl command and an in-progress [API +endpoint](https://goo.gl/UbCRuf) for mutating a +cluster. + +One _applies_ a statement of what one wants to a +cluster in the form of a complete resource list. + +The cluster merges this with the previously applied +state and the actual state to arrive at a new desired +state, which the cluster's reconcilation loop attempts +to create. This is the foundation of level-based state +management in k8s. + +## base + +A _base_ is a [target] that some [overlay] modifies. + +Any target, including an overlay, can be a base to +another target. + +A base has no knowledge of the overlays that refer to it. + +A base is usable in isolation, i.e. one should +be able to [apply] a base to a cluster directly. + +For simple [gitops] management, a base configuration +could be the _sole content of a git repository +dedicated to that purpose_. Same with [overlays]. +Changes in a repo could generate a build, test and +deploy cycle. + +Some of the demos for [kustomize] will break from this +idiom and store all demo config files in directories +_next_ to the `kustomize` code so that the code and +demos can be more easily maintained by the same group +of people. + +## bespoke configuration + +A _bespoke_ configuration is a [kustomization] and some +[resources] created and maintained internally by some +organization for their own purposes. + +The [workflow] associated with a _bespoke_ config is +simpler than the workflow associated with an +[off-the-shelf] config, because there's no notion of +periodically capturing someone else's upgrades to the +[off-the-shelf] config. + +## declarative application management + +_Declarative Application Management_ (DAM) is a [set of +ideas](https://goo.gl/T66ZcD) aiming to ease management +of k8s clusters. + + * Works with any configuration, be it bespoke, + off-the-shelf, stateless, stateful, etc. + * Supports common customizations, and creation of + [variants] (dev vs. staging vs. production). + * Exposes and teaches native k8s APIs, rather than + hiding them. + * No friction integration with version control to + support reviews and audit trails. + * Composable with other tools in a unix sense. + * Eschews crossing the line into templating, domain + specific languages, etc., frustrating the other + goals. + +## gitops + +Devops or CICD workflows that use a git repository as a +single source of truth and take action (e.g., build, +test or deploy) when that truth changes. + +## kustomization + +A _kustomization_ is a file called `kustomization.yaml` that +describes a configuration consumable by [kustomize]. + + +Here's an [example](kustomization.yaml). + +A kustomization contains fields falling into these categories: + + * Immediate customization declarations, e.g. + _namePrefix_, _commonLabels_, etc. + * Resource _generators_ for configmaps and secrets. + * References to _external files_ in these categories: + * [resources] - completely specified k8s API objects, + e.g. `deployment.yaml`, `configmap.yaml`, etc. + * [patches] - _partial_ resources that modify full + resources defined in a [base] + (only meaningful in an [overlay]). + * [bases] - path to a directory containing + a [kustomization] (only meaningful in an [overlay]). + * (_TBD_) Standard k8s API kind-version fields. + +## kubernetes + +[Kubernetes](https://kubernetes.io) is an open-source +system for automating deployment, scaling, and +management of containerized applications. + +It's often abbreviated as _k8s_. + +## kubernetes-style object + +[fields required]: https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields + +An object, expressed in a YAML or JSON file, with the +[fields required] by kubernetes. Basically just a +`kind` field to identify the type, a `metadata/name` +field to identify the variant, and an `apiVersion` +field to identify the version (if there's more than one +version). + +## kustomize + +_kustomize_ is a command line tool supporting template-free +customization of declarative configuration targetted to +k8s-style objects. + +_Targetted to k8s means_ that kustomize may need some +limited understanding of API resources, k8s concepts +like names, labels, namespaces, etc. and the semantics +of resource patching. + +kustomize is an implementation of [DAM]. + + +## off-the-shelf configuration + +An _off-the-shelf_ configuration is a kustomization and +resources intentionally published somewhere for others +to use. + +E.g. one might create a github repository like this: + +> ``` +> github.com/username/someapp/ +> kustomization.yaml +> deployment.yaml +> configmap.yaml +> README.md +> ``` + +Someone could then _fork_ this repo (on github) and +_clone_ their fork to their local disk for +customization. + +This clone could act as a [base] for the user's +own [overlays] to do further customization. + +## overlay + +An _overlay_ is a [target] that modifies (and thus +depends on) another target. + +The [kustomization] in an overlay refers to (via file +path, URI or other method) _some other kustomization_, +known as its [base]. + +An overlay is unusable without its base. + +An overlay supports the typical notion of a +_development_, _QA_, _staging_ and _production_ +environment variants. + +The configuration of these environments is specified in +individual overlays (one per environment) that all +refer to a common base that holds common configuration. +One configures the cluster like this: + +> ``` +> kustomize build someapp/overlays/staging |\ +> kubectl apply -f - +> +> kustomize build someapp/overlays/production |\ +> kubectl apply -f - +> ``` + +Usage of the base is implicit (the overlay's kustomization +points to the base). + +An overlay may act as a base to another overlay. + +## package + +The word _package_ has no meaning in kustomize, as +kustomize is not to be confused with a package +management tool in the tradition of, say, [apt] or +[rpm]. + +## patch + +A _patch_ is a partially defined k8s resource with a +name that must match a resource already known per +traversal rules built into [kustomize]. + +_Patch_ is a field in the kustomization, distinct from +resources, because a patch file looks like a resource +file, but has different semantics. A patch depends on +(modifies) a resource, whereas a resource has no +dependencies. Since any resource file can be used as a +patch, one cannot reliably distinguish a resource from +a patch just by looking at the file's [YAML]. + +## resource + +A _resource_, in the context of kustomize, is a path to +a [YAML] or [JSON] file that completely defines a +functional k8s API object, like a deployment or a +configmap. + +More generally, a resource can be any correct YAML file +that [defines an object](https://kubernetes.io/docs/concepts/overview/working-with-objects/kubernetes-objects/#required-fields) +with a `kind` and a `metadata/name` field. + + +A _resource_ in the content of a REST-ful API is the +target of an HTTP operation like _GET_, _PUT_ or +_POST_. k8s offers a RESTful API surface to interact +with clients. + + +## sub-target / sub-application / sub-package + +A _sub-whatever_ is not a thing. There are only +[bases] and [overlays]. + +## target + +The _target_ is the argument to `kustomize build`, e.g.: + +> ``` +> kustomize build $target +> ``` + +`$target` must be a path to a directory that +immediately contains a file called +`kustomization.yaml` (i.e. a [kustomization]). + +The target contains, or refers to, all the information +needed to create customized resources to send to the +[apply] operation. + +A target is a [base] or an [overlay]. + +## variant + +A _variant_ is the outcome, in a cluster, of applying +an [overlay] to a [base]. + +> E.g., a _staging_ and _production_ overlay both modify some +> common base to create distinct variants. +> +> The _staging_ variant is the set of resources +> exposed to quality assurance testing, or to some +> external users who'd like to see what the next +> version of production will look like. +> +> The _production_ variant is the set of resources +> exposed to production traffic, and thus may employ +> deployments with a large number of replicas and higher +> cpu and memory requests. diff --git a/docs/kustomization.yaml b/docs/kustomization.yaml new file mode 100644 index 0000000000..19762dd787 --- /dev/null +++ b/docs/kustomization.yaml @@ -0,0 +1,115 @@ +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# ---------------------------------------------------- +# Example kustomization.yaml content. +# +# This file declares the customization provided by +# the kustomize program. +# +# Since customization is, by definition, _custom_, +# there are no sensible default values for the fields +# in this file. +# +# The field values used below are merely examples, not +# to be copied literally. The values won't work if +# they happen to be references to external files that +# don't exist. +# +# In practice, fields with no value should simply be +# omitted from kustomize.yaml to reduce the content +# visible in configuration reviews. +# ---------------------------------------------------- + + +# Value of this field is prepended to the +# names of all resources, e.g. a deployment named +# "wordpress" becomes "alices-wordpress". +namePrefix: alices- + +# Labels to add to all resources and selectors. +commonLabels: + someName: someValue + owner: alice + app: bingo + +# Annotations (non-identifying metadata) +# to add to all resources. Like labels, +# these are key value pairs. +commonAnnotations: + oncallPager: 800-555-1212 + +# Each entry in this list must resolve to an existing +# resource definition in YAML. These are the resource +# files that kustomize reads, modifies and emits as a +# YAML string, with resources separated by document +# markers ("---"). +resources: +- some-service.yaml +- ../some-dir/some-deployment.yaml + +# Each entry in this list results in the creation of +# one ConfigMap resource (it's a generator of n maps). +# The example below creates a ConfigMap with the +# names and contents of the given files. +configMapGenerator: +- name: myJavaServerProps + files: + - application.properties + - more.properties + +# Each entry in this list results in the creation of +# one Secret resource (it's a generator of n secrets). +# A command can do anything to get a secret, +# e.g. prompt the user directly, start a webserver to +# initate an oauth dance, etc. +secretGenerator: +- name: app-tls + commands: + tls.crt: "cat secret/tls.cert" + tls.key: "cat secret/tls.key" + type: "kubernetes.io/tls" + +# Each entry in this list should resolve to a directory +# containing a kustomization file, else the +# customization fails. +# +# The presence of this field means this file (the file +# you a reading) is an _overlay_ that further +# customizes information coming from these _bases_. +# +# Typical use case: a dev, staging and production +# environment that are mostly identical but differing +# crucial ways (image tags, a few server arguments, +# etc. that differ from the common base). +bases: +- ../../base + +# Each entry in this list should resolve to +# a partial or complete resource definition file. +# +# The names in these (possibly partial) resource files +# must match names already loaded via the `resources` +# field or via `resources` loaded transitively via the +# `bases` entries. These entries are used to _patch_ +# (modify) the known resources. +# +# Small patches that do one thing are best, e.g. modify +# a memory request/limit, change an env var in a +# ConfigMap, etc. Small patches are easy to review and +# easy to mix together in overlays. +patches: +- service_port_8888.yaml +- deployment_increase_replicas.yaml +- deployment_increase_memory.yaml diff --git a/docs/overlay.jpg b/docs/overlay.jpg new file mode 100644 index 0000000000..7298d637ad Binary files /dev/null and b/docs/overlay.jpg differ diff --git a/docs/workflowBespoke.jpg b/docs/workflowBespoke.jpg new file mode 100644 index 0000000000..9987285763 Binary files /dev/null and b/docs/workflowBespoke.jpg differ diff --git a/docs/workflowOts.jpg b/docs/workflowOts.jpg new file mode 100644 index 0000000000..044b09aac9 Binary files /dev/null and b/docs/workflowOts.jpg differ diff --git a/docs/workflows.md b/docs/workflows.md new file mode 100644 index 0000000000..ef0f84e8cf --- /dev/null +++ b/docs/workflows.md @@ -0,0 +1,127 @@ +[OTS]: glossary.md#off-the-shelf +[apply]: glossary.md#apply +[applying]: glossary.md#apply +[base]: glossary.md#base +[fork]: https://guides.github.com/activities/forking/ +[variants]: glossary.md#variant +[kustomization]: glossary.md#kustomization +[off-the-shelf]: glossary.md#off-the-shelf +[overlays]: glossary.md#overlay +[patch]: glossary.md#patch +[patches]: glossary.md#patch +[rebase]: https://git-scm.com/docs/git-rebase +[resources]: glossary.md#resources +[workflowBespoke]: workflowBespoke.jpg +[workflowOts]: workflowOts.jpg + +# workflows + +A _workflow_ is the sequence of steps one takes to +use and maintain a configuration. + +## Bespoke configuration + +In this workflow, all configuration files are owned by +the user. No content is incorporated from version +control repositories owned by others. + +![bespoke config workflow image][workflowBespoke] + +#### 1) create a directory in version control + +> ``` +> git init ~/ldap +> ``` + +#### 2) create a [base] + +> ``` +> mkdir -p ~/ldap/base +> ``` + +In this directory, create and commit a [kustomization] +file and a set of [resources]. + +#### 3) create [overlays] + +> ``` +> mkdir -p ~/ldap/overlays/staging +> mkdir -p ~/ldap/overlays/production +> ``` + +Each of these directories needs a [kustomization] +file and one or more [patches]. + +The _staging_ directory might get a patch +that turns on an experiment flag in a configmap. + +The _production_ directory might get a patch +that increases the replica count in a deployment +specified in the base. + +#### 4) bring up [variants] + +Run kustomize, and pipe the output to [apply]. + +> ``` +> kustomize ~/ldap/overlays/staging | kubectl apply -f - +> kustomize ~/ldap/overlays/production | kubectl apply -f - +> ``` + + +## Off-the-shelf configuration + +In this workflow, all files are owned by the user and +maintained in a repository under their control, but +they are based on an [off-the-shelf] configuration that +is periodically consulted for updates. + + +![off-the-shelf config workflow image][workflowOts] + +#### 1) find and [fork] an [OTS] config + +#### 2) clone it as your [base] + +The [base] directory is maintained in a repo whose +upstream is an [OTS] configuration, in this case +https://github.com/kinflate/ldap. + +> ``` +> mkdir ~/ldap +> git clone https://github.com/$USER/ldap ~/ldap/base +> cd ~/ldap/base +> git remote add upstream git@github.com:kustomize/ldap +> ``` + +#### 3) create [overlays] + +As in the bespoke case above, create and populate +an _overlays_ directory. + +The [overlays] are siblings to each other and to the +[base] they depend on. + +> ``` +> mkdir -p ~/ldap/overlays/staging +> mkdir -p ~/ldap/overlays/production +> ``` + + +#### 4) bring up [variants] + +> ``` +> kustomize ~/ldap/overlays/staging | kubectl apply -f - +> kustomize ~/ldap/overlays/production | kubectl apply -f - +> ``` + +#### 5) (optionally) capture changes from upstream + +The user can optionally [rebase] their [base] to +capture changes made in the upstream repository. + +> ``` +> cd ~/ldap/base +> git fetch upstream +> git rebase upstream/master +> ``` diff --git a/kustomize.go b/kustomize.go new file mode 100644 index 0000000000..7940c4dd09 --- /dev/null +++ b/kustomize.go @@ -0,0 +1,34 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "os" + + "github.com/golang/glog" + + "k8s.io/kubectl/pkg/kustomize/commands" +) + +func main() { + defer glog.Flush() + + if err := commands.NewDefaultCommand().Execute(); err != nil { + os.Exit(1) + } + os.Exit(0) +} diff --git a/test/main.sh b/test/main.sh new file mode 100755 index 0000000000..f93bef9781 --- /dev/null +++ b/test/main.sh @@ -0,0 +1,46 @@ +#!/bin/bash + +# Copyright 2018 The Kubernetes Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +function exit_with { + local msg=$1 + echo >&2 ${msg} + exit 1 +} + +base_dir="$( cd "$(dirname "$0")/../../.." && pwd )" +cd "$base_dir" || { + echo "Cannot cd to '$base_dir'. Aborting." >&2 + exit 1 +} + +# Install kustomize to $GOPATH/bin and export PATH +go install ./cmd/kustomize || { exit_with "Failed to install kustomize"; } +export PATH=$GOPATH/bin:$PATH + +home=`pwd` +example_dir="./cmd/kustomize/demos/data/ldap/base" +if [ ! -d ${example_dir} ]; then + exit_with "directory ${example_dir} doesn't exist" +fi + +if [ -x "${example_dir}/tests/test.sh" ]; then + ${example_dir}/tests/test.sh ${example_dir} + if [ $? -eq 0 ]; then + echo "testing ${example_dir} passed." + else + exit_with "testing ${example_dir} failed." + fi +fi \ No newline at end of file diff --git a/version/version.go b/version/version.go new file mode 100644 index 0000000000..36aa6b52bd --- /dev/null +++ b/version/version.go @@ -0,0 +1,67 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package version + +import ( + "fmt" + "io" + + "github.com/spf13/cobra" +) + +var ( + kustomizeVersion = "unknown" + goos = "unknown" + goarch = "unknown" + gitCommit = "$Format:%H$" // sha1 from git, output of $(git rev-parse HEAD) + + buildDate = "1970-01-01T00:00:00Z" // build date in ISO8601 format, output of $(date -u +'%Y-%m-%dT%H:%M:%SZ') +) + +type Version struct { + KustomizeVersion string `json:"kustomizeVersion"` + GitCommit string `json:"gitCommit"` + BuildDate string `json:"buildDate"` + GoOs string `json:"goOs"` + GoArch string `json:"goArch"` +} + +func GetVersion() Version { + return Version{ + kustomizeVersion, + gitCommit, + buildDate, + goos, + goarch, + } +} + +func (v Version) Print(w io.Writer) { + fmt.Fprintf(w, "Version: %+v\n", v) +} + +// NewCmdVersion makes version command. +func NewCmdVersion(w io.Writer) *cobra.Command { + return &cobra.Command{ + Use: "version", + Short: "Prints the kustomize version", + Example: `kustomize version`, + Run: func(cmd *cobra.Command, args []string) { + GetVersion().Print(w) + }, + } +}