Copying cmd/kustomize

This commit is contained in:
Jeffrey Regan
2018-05-11 14:01:01 -07:00
55 changed files with 3344 additions and 0 deletions

36
CONTRIBUTING.md Normal file
View File

@@ -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].

201
LICENSE Normal file
View File

@@ -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.

4
OWNERS Normal file
View File

@@ -0,0 +1,4 @@
# See https://github.com/kubernetes/community/blob/master/community-membership.md
approvers:
- kustomize-admins
- kustomize-maintainers

12
OWNERS_ALIASES Normal file
View File

@@ -0,0 +1,12 @@
aliases:
kustomize-admins:
- grodrigues3
- monopole
- pwittrock
kustomize-maintainers:
- droot
- justinsb
- liujingfang1
- mengqiy
- monopole
- pwittrock

64
README.md Normal file
View File

@@ -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`:
<!-- @installkustomize @test -->
```
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].

29
build/.goreleaser.yml Normal file
View File

@@ -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

16
build/README.md Normal file
View File

@@ -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
```

42
build/build.sh Executable file
View File

@@ -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

25
build/cloudbuild.yaml Normal file
View File

@@ -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==

View File

@@ -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==

6
code-of-conduct.md Normal file
View File

@@ -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].

24
demos/README.md Normal file
View File

@@ -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.

124
demos/breakfast.md Normal file
View File

@@ -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:
<!-- @makeWorkplace @test -->
```
DEMO_HOME=$(mktemp -d)
```
Make a place to put the base breakfast configuration:
<!-- @baseDir @test -->
```
mkdir -p $DEMO_HOME/breakfast/base
```
Make a `kustomization` to define what goes into
breakfast. This breakfast has coffee and pancakes:
<!-- @baseKustomization @test -->
```
cat <<EOF >$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:
<!-- @coffee @test -->
```
cat <<EOF >$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_:
<!-- @pancakes @test -->
```
cat <<EOF >$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:
<!-- @aliceOverlay @test -->
```
mkdir -p $DEMO_HOME/breakfast/overlays/alice
cat <<EOF >$DEMO_HOME/breakfast/overlays/alice/kustomization.yaml
commonLabels:
who: alice
bases:
- ../../base
patches:
- temperature.yaml
EOF
cat <<EOF >$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:
<!-- @bobOverlay @test -->
```
mkdir -p $DEMO_HOME/breakfast/overlays/bob
cat <<EOF >$DEMO_HOME/breakfast/overlays/bob/kustomization.yaml
commonLabels:
who: bob
bases:
- ../../base
patches:
- topping.yaml
EOF
cat <<EOF >$DEMO_HOME/breakfast/overlays/bob/topping.yaml
kind: Pancakes
metadata:
name: comfort
stacksize: 5
topping: strawberries
EOF
```
One can now generate the configs for Alices breakfast:
<!-- @generateAlice @test -->
```
kustomize build $DEMO_HOME/breakfast/overlays/alice
```
Likewise for Bob:
<!-- @generateBob @test -->
```
kustomize build $DEMO_HOME/breakfast/overlays/bob
```

298
demos/configGeneration.md Normal file
View File

@@ -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).
<!--
secretGenerator:
- name: app-tls
commands:
tls.crt: "cat tls.cert"
tls.key: "cat tls.key"
type: "kubernetes.io/tls"
EOF
-->
## 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:
<!-- @makeWorkplace @test -->
```
DEMO_HOME=$(mktemp -d)
```
### Create the base
<!-- kubectl create configmap BOB --dry-run -o yaml --from-file db. -->
Make a place to put the base configuration:
<!-- @baseDir @test -->
```
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.
<!-- @baseKustomization @test -->
```
cat <<EOF >$DEMO_HOME/base/common.properties
color=blue
height=10m
EOF
cat <<EOF >$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 @test -->
```
OVERLAYS=$DEMO_HOME/overlays
```
Create the files that define the _development_ overlay:
<!-- @developmentFiles @test -->
```
mkdir -p $OVERLAYS/development
cat <<EOF >$OVERLAYS/development/plumbing.properties
port=30000
EOF
cat <<EOF >$OVERLAYS/development/secret.properties
dbpassword=mothersMaidenName
EOF
cat <<EOF >$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:
<!-- @runDev @test -->
```
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:
<!-- @productionFiles @test -->
```
mkdir -p $OVERLAYS/production
cat <<EOF >$OVERLAYS/production/plumbing.properties
port=8080
EOF
cat <<EOF >$OVERLAYS/production/secret.properties
dbpassword=thisShouldProbablyBeInASecretInstead
EOF
cat <<EOF >$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:
<!-- @runProd @test -->
```
kustomize build $OVERLAYS/production
```
A CICD process could apply this directly to
the cluser using:
> ```
> kustomize build $OVERLAYS/production | kubectl apply -f -
> ```

View File

@@ -0,0 +1,7 @@
apiVersion: v1
kind: ConfigMap
metadata:
name: the-map
data:
altGreeting: "Good Morning!"
enableRisky: "false"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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: {}

View File

@@ -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.

View File

@@ -0,0 +1,7 @@
resources:
- deployment.yaml
- service.yaml
configMapGenerator:
- name: ldap-configmap
files:
- env.startup.txt

View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
labels:
app: ldap
name: ldap-service
spec:
ports:
- port: 389
selector:
app: ldap

View File

@@ -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 <<EOF >$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

View File

@@ -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

View File

@@ -0,0 +1,5 @@
bases:
- ../../base
patches:
- deployment.yaml
namePrefix: production-

View File

@@ -0,0 +1,2 @@
DB_USERNAME=admin
DB_PASSWORD=somepw

View File

@@ -0,0 +1,7 @@
apiVersion: apps/v1beta2
kind: Deployment
metadata:
name: ldap
spec:
replicas: 2

View File

@@ -0,0 +1,9 @@
bases:
- ../../base
patches:
- deployment.yaml
nameprefix: staging-
configMapGenerator:
- name: env-config
files:
- config.env

View File

@@ -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: {}

View File

@@ -0,0 +1,9 @@
apiVersion: v1
kind: Secret
metadata:
creationTimestamp: null
name: mysql-pass-d2gtcm2t2k
type: Opaque
data:
# Default password is "admin".
password: YWRtaW4=

View File

@@ -0,0 +1,11 @@
apiVersion: v1
kind: Service
metadata:
name: mysql
labels:
app: mysql
spec:
ports:
- port: 3306
selector:
app: mysql

View File

@@ -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"

View File

@@ -0,0 +1,12 @@
apiVersion: v1
kind: Service
metadata:
name: sbdemo
labels:
app: sbdemo
spec:
ports:
- port: 8080
selector:
app: sbdemo
type: LoadBalancer

View File

@@ -0,0 +1,5 @@
app.name=Staging Kinflate Demo
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://<production_db_ip>:3306/db_example
spring.datasource.username=root
spring.datasource.password=admin

View File

@@ -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

View File

@@ -0,0 +1,7 @@
bases:
- ../../base
patches:
- patch.yaml
- healthcheck_patch.yaml
- memorylimit_patch.yaml
namePrefix: production-

View File

@@ -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

View File

@@ -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://<production_db_ip>:3306/db_example
spring.datasource.username=root
spring.datasource.password=admin
server.tomcat.max-threads=20
server.tomcat.min-spare-threads=3

View File

@@ -0,0 +1,5 @@
app.name=Staging Kinflate Demo
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://<staging_db_ip>:3306/db_example
spring.datasource.username=root
spring.datasource.password=admin

View File

@@ -0,0 +1,11 @@
bases:
- ../../base
namePrefix: staging-
configMapGenerator:
- name: demo-configmap
behavior: merge
files:
- application.properties
literals:
- foo=bar
env: staging.env

View File

@@ -0,0 +1 @@
staging

432
demos/helloWorld.md Normal file
View File

@@ -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:
<!-- @makeWorkplace @test -->
```
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:
<!-- @downloadBase @test -->
```
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:
<!-- @runTree @test -->
```
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:
<!-- @showKustomization @test -->
```
more $BASE/kustomization.yaml
```
Optionally, run `kustomize` on the base to emit
customized resources to `stdout`:
<!-- @buildBase @test -->
```
kustomize build $BASE
```
### Customize the base
A first customization step could be to change the _app
label_ applied to all resources:
<!-- @addLabel @test -->
```
sed -i 's/app: hello/app: my-hello/' \
$BASE/kustomization.yaml
```
See the effect:
<!-- @checkLabel @test -->
```
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.
<!-- @overlayDirectories @test -->
```
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.
<!-- @makeStagingKustomization @test -->
```
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.
<!-- @stagingMap @test -->
```
cat <<EOF >$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.
<!-- @makeProductionKustomization @test -->
```
cat <<EOF >$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).
<!-- @productionDeployment @test -->
```
cat <<EOF >$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:
<!-- @listFiles @test -->
```
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:
<!-- @compareOutput -->
```
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:
<!-- @buildStaging @test -->
```
kustomize build $OVERLAYS/staging
```
<!-- @buildProduction @test -->
```
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:
<!-- @showDeployment @test -->
```
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]:
<!-- @showMapPatch @test -->
```
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:
<!-- @showMapBase @test -->
```
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:
<!-- @grepStagingName @test -->
```
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_:
<!-- @grepStagingHash @test -->
```
kustomize build $OVERLAYS/staging | grep hhhhkfmgmk
```
Now modify the map patch, to change the greeting
the server will use:
<!-- @changeMap @test -->
```
sed -i 's/pineapple/kiwi/' $OVERLAYS/staging/map.yaml
```
Run kustomize again to see the new names:
<!-- @grepStagingName @test -->
```
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:
<!-- @countHashes @test -->
```
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.

284
demos/ldap.md Normal file
View File

@@ -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:
<!-- @makeWorkplace @test -->
```
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:
<!-- @downloadBase @test -->
```
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:
<!-- @runTree @test -->
```
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:
<!-- @showKustomization @test -->
```
more $BASE/kustomization.yaml
```
Optionally, run `kustomize` on the base to emit
customized resources to `stdout`:
<!-- @buildBase @test -->
```
kustomize build $BASE
```
### Customize the base
A first customization step could be to set the name prefix to all resources:
<!-- @namePrefix @test -->
```
cd $BASE
kustomize edit set nameprefix "my-"
```
See the effect:
<!-- @checkNameprefix @test -->
```
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.
<!-- @overlayDirectories @test -->
```
OVERLAYS=$DEMO_HOME/overlays
mkdir -p $OVERLAYS/staging
mkdir -p $OVERLAYS/production
```
#### Staging Kustomization
Download the staging customization and patch.
<!-- @downloadStagingKustomization @test -->
```
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.
<!-- @downloadProductionKustomization @test -->
```
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:
<!-- @listFiles @test -->
```
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:
<!-- @compareOutput -->
```
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:
<!-- @buildStaging @test -->
```
kustomize build $OVERLAYS/staging
```
<!-- @buildProduction @test -->
```
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 -
> ```

203
demos/mySql.md Normal file
View File

@@ -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:
<!-- @makeDemoHome @test -->
```
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:
<!-- @downloadResources @test -->
```
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:
<!-- @kustomizeYaml @test -->
```
touch $DEMO_HOME/kustomization.yaml
```
### Add the resources
<!-- @addResources @test -->
```
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):
<!-- @customizeLabel @test -->
```
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.
<!-- @genNamePrefixConfig @test -->
```
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:
<!-- @customizeLabels @test -->
```
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.
<!-- @createPatchFile @test -->
```
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`:
<!-- @specifyPatch @test -->
```
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.
<!-- @finalInflation @test -->
```
kustomize build $DEMO_HOME # | kubectl apply -f -
```

306
demos/springboot.md Normal file
View File

@@ -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:
<!-- @makeDemoHome @test -->
```
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:
<!-- @downloadResources @test -->
```
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:
<!-- @kustomizeYaml @test -->
```
touch $DEMO_HOME/kustomization.yaml
```
### Add the resources
<!-- @addResources @test -->
```
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
<!-- @addConfigMap @test -->
```
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-<profile>.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.
<!-- @customizeConfigMap @test -->
```
cat <<EOF >$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 <<EOF >$DEMO_HOME/application-prod.properties
spring.jpa.hibernate.ddl-auto=update
spring.datasource.url=jdbc:mysql://<prod_database_host>: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):
<!-- @customizeLabel @test -->
```
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:
<!-- @build1 @test -->
```
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:
<!-- @customizeLabels @test -->
```
cat <<EOF >>$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`:
<!-- @build2 @test -->
```
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.
<!-- @downloadPatch @test -->
```
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.
<!-- @downloadPatch @test -->
```
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:
<!-- @addPatch @test -->
```
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.
<!-- @finalBuild @test -->
```
kustomize build $DEMO_HOME # | kubectl apply -f -
```

BIN
docs/base.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

318
docs/glossary.md Normal file
View File

@@ -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.

115
docs/kustomization.yaml Normal file
View File

@@ -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

BIN
docs/overlay.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
docs/workflowBespoke.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
docs/workflowOts.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

127
docs/workflows.md Normal file
View File

@@ -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
> ```

34
kustomize.go Normal file
View File

@@ -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)
}

46
test/main.sh Executable file
View File

@@ -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

67
version/version.go Normal file
View File

@@ -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)
},
}
}