4. Configuring your Application
So far we’ve installed Kubernetes, run our first app, learned how to scale that app and exposed that application via a service.
You may want to pass configuration settings into your application, you can use Config Maps and Secrets to do this.
Do
Create a basic website
Create a HTML file to display hello world:
echo '<html><head><title>Hello World</title></head><body>Hello World</body></html>' > /tmp/index.html
Create a Config Map
Create a Kubernetes Config Map from that file:
$kubectl create configmap hello --from-file=/tmp/index.html
configmap/hello created
Create a Secret
Create a Kubernetes Secret from that file:
$ kubectl create secret generic hello --from-file=/tmp/index.html
secret/hello created
View the resultant Config Map:
$ kubectl get configmap hello -o yaml
apiVersion: v1
data:
index.html: |
<html><head><title>Hello World</title></head><body>Hello World</body></html>
kind: ConfigMap
metadata:
creationTimestamp: "2019-08-15T20:09:43Z"
name: hello
namespace: default
resourceVersion: "591027"
selfLink: /api/v1/namespaces/default/configmaps/hello
uid: a47a6058-a482-40be-99d3-1aaf5390af85
View the resultant Secret:
Note: the secret is unreadable. It is not encrypted, simple base64 encoded.
apiVersion: v1
data:
index.html: PGh0bWw+PGhlYWQ+PHRpdGxlPkhlbGxvIFdvcmxkPC90aXRsZT48L2hlYWQ+PGJvZHk+SGVsbG8gV29ybGQ8L2JvZHk+PC9odG1sPgo=
kind: Secret
metadata:
creationTimestamp: "2019-08-15T20:09:49Z"
name: hello
namespace: default
resourceVersion: "591038"
selfLink: /api/v1/namespaces/default/secrets/hello
uid: ead44f8c-041f-4db7-bb98-ee56dd1f2e3e
type: Opaque
You can map either of these into your Pod as an environment variable, or as a file in a volume. Since the example application is nginx it makes sense to mount it as a file in a volume.
To mount it as a volume you would need to edit the actual underlying Kubernetes manifest defining the Deployment. You could do this live with the kubectl edit
command, however it will be easier if we create a new pod from a simpler manifest.
Create a new deployment
Create the following file as /tmp/hello.yaml
:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: hello-world
spec:
selector:
matchLabels:
app: hello-world
template:
metadata:
labels:
app: hello-world
spec:
containers:
- name: hello
image: nginx:latest
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
configMap:
name: hello
Apply that manifest using the declarative command kubectl apply
:
$ kubectl apply -f /tmp/hello.yaml
deployment.extensions/hello-world created
Test the new deployment
Use kubectl port-forward
to enable access to the new deployment:
$ kubectl port-forward deployment/hello-world 8080:80
Forwarding from 127.0.0.1:8080 -> 80
Handling connection for 8080
Access the new port-forward from another terminal (or from a web browser):
$ curl localhost:8080
<html><head><title>Hello World</title></head><body>Hello World</body></html>
Learn
Not only did you create a ConfigMap and a Secret, but you also created a new Deployment using the declarative kubectl apply
command using a YAML based Kubernetes manifest.
Config Map
Config Maps are A Kubernetes resource to store key/value pairs that can be used by other Kubernetes resources. These key/value pairs can be loaded into Pods as environment variables or as files in a volume.
This allows you to decouple configuration from image content with is an integral part of building Cloud Native Applications.
You can create a Config Map from a manifest, or you can use the declarative kubectl create configmap
. You can also combine the two and build a manifest using the kubectl create configmap
command like so:
$ kubectl create configmap example --from-file /tmp/index.html --dry-run -o yaml
apiVersion: v1
data:
index.html: |
<html><head><title>Hello World</title></head><body>Hello World</body></html>
kind: ConfigMap
metadata:
creationTimestamp: null
name: example
Note: the
--dry-run -o yaml
flags are incredibly useful, you can create a Kubernetes manifest for almost any type of Kubernetes resource viakubectl create
to be used later withkubectl apply -f
.
Environment Variables
As well as files, Values in a Config Map can be loaded into environment variables for your application to use inside of a Pod like so:
apiVersion: v1
data:
username: myApp
kind: ConfigMap
metadata:
name: mysql
---
apiVersion: v1
kind: Pod
metadata:
name: mysql
spec:
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_USERNAME
valueFrom:
configMapKeyRef:
# The ConfigMap containing the value you want to assign to MYSQL_USERNAME
name: mysql
# Specify the key associated with the value
key: username
You can also load an entire Config Map into environment variables like so:
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql
data:
username: myApp
host: my.database.server
---
apiVersion: v1
kind: Pod
metadata:
name: mysql
spec:
containers:
- name: mysql
image: mysql
envFrom:
- configMapRef:
name: mysql
Secret
Secrets are like Config Maps but with a few differences. Secrets are base64 encoded which means unless you’re a math genius you can’t read them with the naked eye. This is not however for any security reason, its to ensure that the contents of the secret are safe to store in a text based format.
Secrets can be loaded into a Pod just like a Config Map as files in a volume or as environment variables. The syntax is roughly the same as you can see below.
Files in a Volume
In the example above you created a Secret from a index.html
file, you could load this into a Pod like so:
apiVersion: v1
kind: Pod
metadata:
name: mysql
spec:
containers:
- name: hello
image: nginx:latest
volumeMounts:
- name: html
mountPath: /usr/share/nginx/html
volumes:
- name: html
secret:
secretName: hello
Environment Variables
You can load Secrets into a Pod as environment variables much in the same way as you can Config Maps.
Just a specific Key:
apiVersion: v1
data:
password: YmFjb24taXMtZGVsaWNpb3VzCg==
kind: Secret
metadata:
name: mysql
---
apiVersion: v1
kind: Pod
metadata:
name: mysql
spec:
containers:
- name: mysql
image: mysql
env:
- name: MYSQL_USERNAME
valueFrom:
secretKeyRef:
# The Secret containing the value you want to assign to MYSQL_USERNAME
name: mysql
# Specify the key associated with the value
key: password
You can also load an entire Secret into environment variables like so:
apiVersion: v1
kind: Secret
metadata:
name: mysql
data:
MYSQL_PASSWORD: YmFjb24taXMtZGVsaWNpb3VzCg==
MYSQL_ROOT_PASSWORD: YnV0LXNvLWlzLXRvZnUK
---
apiVersion: v1
kind: Pod
metadata:
name: mysql
spec:
containers:
- name: mysql
image: mysql
envFrom:
- secretRef:
name: mysql
Declarative Kubernetes
The best way to create resources with kubectl
is to use Declarative commands like kubectl apply
. When you use a declarative command you provide a desired state (usually in the form of a YAML based manifest like above) and let Kubernetes sweat the details on how to make the actual state match that desired state.
These manifests are text based artifacts that can (and should) be stored in source control and provide a great foundation on which you can build out Infrastructure as Code and follow devops practices such as gitops.