Reputation: 5507
For example, a deployment yaml file:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
container:
- name: guestbook
image: {{Here want to read value from config file outside}}
There is a ConfigMap
feature with Kubernetes, but that's also write the key/value to the yaml file. Is there a way to set the key to environment variables?
Upvotes: 138
Views: 189569
Reputation: 6761
In OKD (a superset of k8s) Template
objects (docs) are parametrized - see the parameters
section below. You can put any number of objects inside Templates
such as Deployments
/DeploymentConfigs
, Services
, Ingresses
/Routes
, etc.
Note these parameters support not just strings but also numerical values (see the double curly braces around REPLICA_COUNT
below) and even random number generators for auto-generating fresh application credentials at each container startup (see GITHUB_WEBHOOK_SECRET
below):
kind: Template
apiVersion: template.openshift.io/v1
metadata:
name: my-template
objects:
- kind: BuildConfig
apiVersion: build.openshift.io/v1
metadata:
name: cakephp-mysql-example
annotations:
description: Defines how to build the application
spec:
source:
type: Git
git:
uri: "${SOURCE_REPOSITORY_URL}"
ref: "${SOURCE_REPOSITORY_REF}"
contextDir: "${CONTEXT_DIR}"
- kind: DeploymentConfig
apiVersion: apps.openshift.io/v1
metadata:
name: frontend
spec:
replicas: "${{REPLICA_COUNT}}"
parameters:
- name: SOURCE_REPOSITORY_URL
displayName: Source Repository URL
description: The URL of the repository with your application source code
value: https://github.com/sclorg/cakephp-ex.git
required: true
- name: GITHUB_WEBHOOK_SECRET
description: A secret string used to configure the GitHub webhook
generate: expression
from: "[a-zA-Z0-9]{40}"
- name: REPLICA_COUNT
description: Number of replicas to run
value: "2"
required: true
message: "... The GitHub webhook secret is ${GITHUB_WEBHOOK_SECRET} ..."
Upvotes: 1
Reputation: 716
Per YAML's spec, there's no way to do that. but...
This is possible with yq's explode operator.
Namespace, Deployment, ClusterIP, Ingress... Clean and DRY:
cat <<EOF | yq 'explode(.)' | kubectl apply -f -
kind: Namespace
apiVersion: v1
metadata:
name: &namespace example-namespace
---
kind: Deployment
apiVersion: apps/v1
metadata:
name: example-deployment
namespace: *namespace
spec:
replicas: 1
template:
metadata:
labels:
app: &app-label example-app
spec:
containers:
- name: httpecho
image: hashicorp/http-echo:0.2.3
args:
- "-listen=:5678"
- "-text=hello world 1"
ports:
- containerPort: &app-port 5678
selector:
matchLabels:
app: *app-label
---
kind: Service
apiVersion: v1
metadata:
name: &app-cluster-ip example-service
namespace: *namespace
spec:
type: ClusterIP
selector:
app: *app-label
ports:
- name: http
protocol: TCP
port: *app-port
targetPort: *app-port
---
kind: Ingress
apiVersion: networking.k8s.io/v1
metadata:
name: example-ingress
namespace: *namespace
spec:
ingressClassName: caddy
rules:
- host: example.kubernetes.localhost
http:
paths:
- path: /hello
pathType: Prefix
backend:
service:
name: *app-cluster-ip
port:
number: *app-port
EOF
Upvotes: 1
Reputation: 13644
You can also use envsubst
when deploying.
e.g.
cat app/deployment.yaml | envsubst | kubectl apply ...
It will replace all variables in the file with their values.
We are successfully using this approach on our CI when deploying to multiple environments, also to inject the CI_TAG
etc into the deployments.
Upvotes: 123
Reputation: 31
For my deployments, I typically use Helm charts. It requires me to update values.yaml files periodically.
For dynamically updating YAML files, I used 'envsubst' since it is simple and does not require sophisticated configuration. In addition, most of the tools only work with valid Kubernetes manifests, not simple YAML files.
I created a simple script to handle the YAML modification to simplify the usage
https://github.com/alexusarov/vars_replacer
Example:
./vars_replacer.sh -i [input_file] -o [output_file] -p "[key=value] [key=value]"
Upvotes: 0
Reputation: 14429
After trying sed
and envsubst
I found Kustomize the most elegant and Kubernetes-native way. As an alternative also yq comes in handy sometimes.
Install the kustomize
CLI (e.g. on a Mac this is brew install kustomize
) and create a new file called kustomization.yaml
in the same directory as your deployment.yaml
:
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- deployment.yaml
Now use the kustomize edit set image
command to change the image name
# optionally define image name
IMAGE_NAME=ghcr.io/yourrepo/guestbook:c25a74c8f919a72e3f00928917dc4ab2944ab061
# replace image tag
kustomize edit set image $IMAGE_NAME
Finally apply your kustomized deployment.yml
to your cluster using kubectl apply -k directory/where/your/kustomization/file/is
like this:
kubectl apply -k .
For debugging you can see the resulting deployment.yml
if you run kustomize build .
:
$ kustomize build .
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
containers:
- image: ghcr.io/yourrepo/guestbook:c25a74c8f919a72e3f00928917dc4ab2944ab061
name: guestbook
Install the YAML processor yq (e.g. via homebrew brew install yq
), define your variables and let yq do the replacement:
# define image name
IMAGE_NAME=ghcr.io/yourrepo/guestbook:c25a74c8f919a72e3f00928917dc4ab2944ab061
# replace image tag
yq e ".spec.template.spec.containers[0].image = \"$IMAGE_NAME\"" -i deployment.yaml
Now your deployment.yaml
get's the new image version and then looks like this:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
containers:
- image: ghcr.io/yourrepo/guestbook:c25a74c8f919a72e3f00928917dc4ab2944ab061
name: guestbook
FYI: Your deployment.yaml
isn't really valid Kubernetes configuration - the template.spec.container
should not reside under the metadata
tag - and also it is spelled containers
.
Upvotes: 14
Reputation: 21
create a file called kubectl_advance
as below and enjoy calling it just like kubectl commands.
e.g.
EXPORT MY_VAL="my-v1"
kubectl_advance -c -f sample.yaml # -c option is to call create command
kubectl_advance -r -f sample2.yaml # -r option is to call replace command
Assuming the yaml file has the value like ${MY_VAL} to be replaced by the environment variable.
#!/usr/bin/env bash
helpFunction()
{
echo "Supported option is [-f] for file"
exit 1
}
while getopts "f:cr" opt
do
case "$opt" in
f ) yamlFile="$OPTARG" ;;
c ) COMMAND_IS_CREATE="true" ;;
r ) COMMAND_IS_REPLACE="true" ;;
? ) helpFunction ;; # Print helpFunction in case parameter is non-existent
esac
done
echo 'yaml file is : '$yamlFile
YAML_CONTENT=`eval "cat <<EOF
$(<$yamlFile)
EOF
"`
echo 'Final File Content is :=>'
echo '------------------'
echo "$YAML_CONTENT"
if [[ "$COMMAND_IS_CREATE" == "true" ]]; then
COMMAND="create"
fi
if [[ "$COMMAND_IS_REPLACE" == "true" ]]; then
COMMAND="replace"
fi
echo "$YAML_CONTENT" | kubectl $COMMAND -f -
Upvotes: 1
Reputation: 1698
I think the standard - Helm should be used instead of custom scripts to solve this problem nowadays. You don't need to deploy to generate Kubernets yamls on the machine.
An example:
Install helm on your machine so helm
command exists
https://artifacthub.io/packages/helm/pauls-helm-charts/helloworld - Install button
helm repo add pauls-helm-charts http://tech.paulcz.net/charts
helm pull pauls-helm-charts/helloworld --version 2.0.0
tar -zxvf helloworld-2.0.0.tgz && cd helloworld
helm template -f values.yaml --output-dir helloworld . --namespace my-namespace --name-template=my-name
So it created these files from values.yaml
:
wrote helloworld/helloworld/templates/serviceaccount.yaml
wrote helloworld/helloworld/templates/service.yaml
wrote helloworld/helloworld/templates/deployment.yaml
Inside values.yaml
, you can change predefined repository
(or 100% any value can be repeated in Kubernetes yamls as you wish):
image:
repository: paulczar/spring-helloworld
Now if you want to deploy, make sure kubectl
works and just apply these generated files using kubectl apply -f serviceaccount.yaml
, etc.
Upvotes: 3
Reputation: 708
In the jitsi project the tpl
== frep
command is used to substitute values, an extension to envsubst
https://github.com/jitsi/docker-jitsi-meet/issues/65
I keep on using the old shell tools like sed and friends but such code is quickly unreadable when its more than a handful of value to deal with.
Upvotes: 0
Reputation: 31
If you just want to change the image or a tag while your deployment is running, you could set the image of a specific container in your deployment:
kubectl apply -f k8s
kubectl set image deployments/worker-deployment worker=IMAGE:TAG
Upvotes: 2
Reputation: 424
This kind of thing is painfully easy with ytt
:
deployment.yml
#@ load("@ytt:data", "data")
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
container:
- name: guestbook
image: #@ data.values.image
values.yml
#@data/values
image: nginx@sha256:fe2fa7bb1ceb86c6d9c935bc25c3dd8cbd64f2e95ed5b894f93ae7ffbd1e92bb
Then...
$ ytt -f deployment.yml -f values.yml | kubectl apply -f -
or even better, use ytt
's cousin, kapp
for a high-control deployment experience:
$ ytt -f deployment.yml -f values.yml | kapp deploy -a guestbook -f -
Upvotes: 12
Reputation: 2397
yaml does not read values from another yaml file. As an alternative approach you could try this.
kind: Pod
metadata:
creationTimestamp: null
annotations:
namespace: &namespaceId dev
imageId: &imgageId nginx
podName: &podName nginx-pod
containerName: &containerName nginx-container
name: *podName
namespace: *namespaceId
spec:
containers:
- image: *imgageId
name: *containerName
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
Upvotes: 7
Reputation: 2811
I've published a command-line tool ysed that helps exactly with that, in case you plan to script it.
Upvotes: 1
Reputation: 1451
Helm is exactly meant for such things and a lot more. It handle complex set of resource deployment as a group etc.
But if we are still looking for some simple alternative then how about using ant?
If you want to modify the file as part of build process or test process then you can go with ant task as well.
Using ant you can load all environment values as property or you can simply load properties file like:
<property environment="env" />
<property file="build.properties" />
Then you can have a target which converts template files into your desired yaml file.
<target name="generate_from_template">
<!-- Copy task to replaces values and create new file -->
<copy todir="${dest.dir}" verbose="true" overwrite="true" failonerror="true">
<!-- List of files to be processed -->
<fileset file="${source.dir}/xyz.template.yml" />
<!-- Mapper to transform filename. Removes '.template' from the file
name when copying the file to output directory -->
<mapper type="regexp" from="(.*).template(.*)" to="\1\2" />
<!-- Filter chain that replaces the template values with actual values
fetched from properties file -->
<filterchain>
<expandproperties />
</filterchain>
</copy>
</target>
Of course you can use a fileset
instead of file
in case you want to change values dynamically for multiple files (nested or whatever)
Your template file xyz.template.yml
should look like:
apiVersion: v1
kind: Service
metadata:
name: ${XYZ_RES_NAME}-ser
labels:
app: ${XYZ_RES_NAME}
version: v1
spec:
type: NodePort
ports:
- port: ${env.XYZ_RES_PORT}
protocol: TCP
selector:
app: ${XYZ_RES_NAME}
version: v1
env.
property being loaded from environment variables and other from property file
Hope it helped :)
Upvotes: 0
Reputation: 11
I create a script called kubectl_apply. It loads variables from .env, replace ${CUSTOMVAR} in yml and pass it to kubectl command
#!/bin/bash
set -a
source .env
set +a
eval "cat <<EOF
$(<$1)
EOF
" | kubectl apply -f -
Upvotes: 1
Reputation: 3485
I have been using kubetpl
It has three different template flavors and supports ConfigMap/Secret freezing.
Upvotes: 2
Reputation: 13260
You can't do it automatically, you need to use an external script to "compile" your template, or use helm as suggested by @Jakub.
You may want to use a custom bash script, maybe integrated with your CI pipeline.
Given a template yml file called deploy.yml.template
very similar to the one you provided, you can use something like this:
#!/bin/bash
# sample value for your variables
MYVARVALUE="nginx:latest"
# read the yml template from a file and substitute the string
# {{MYVARNAME}} with the value of the MYVARVALUE variable
template=`cat "deploy.yml.template" | sed "s/{{MYVARNAME}}/$MYVARVALUE/g"`
# apply the yml with the substituted value
echo "$template" | kubectl apply -f -
Upvotes: 81
Reputation: 596
My approach:
tools/jinja2-cli.py
:
#!/usr/bin/env python3
import os
import sys
from jinja2 import Environment, FileSystemLoader
sys.stdout.write(Environment(loader=FileSystemLoader('templates/')).from_string(sys.stdin.read()).render(env=os.environ) + "\n")
Make rule:
_GENFILES = $(basename $(TEMPLATES))
GENFILES = $(_GENFILES:templates/%=%)
$(GENFILES): %: templates/%.j2 $(MKFILES) tools/jinja2-cli.py .env
env $$(cat .env | xargs) tools/jinja2-cli.py < $< > $@ || (rm -f $@; false)
Inside the .j2
template file you can use any jinja syntax construct, e.g. {{env.GUEST}}
will be replaced by the value of GUEST
defined in .env
So your templates/deploy.yaml.j2
would look like:
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
container:
- name: guestbook
image: {{env.GUEST}}
Another approach (using just bash
builtins and xargs
) might be
env $(cat .env | xargs) cat <<EOF | kubectl create -f -
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
name: guestbook
spec:
replicas: 2
template:
metadata:
labels:
app: guestbook
spec:
container:
- name: guestbook
image: ${GUEST}
EOF
Upvotes: 3
Reputation: 131
I create a script called kubectl_create
and use it to run the create command. It will substitute any value in the template that is referenced in an environment variable.
#!/bin/bash
set -e
eval "cat <<EOF
$(<$1)
EOF
" | kubectl create -f -
For example, if the template file has:
apiVersion: v1
kind: Service
metadata:
name: nginx-external
labels:
app: nginx
spec:
loadBalancerIP: ${PUBLIC_IP}
type: LoadBalancer
ports:
- name: http
port: 80
targetPort: 80
- name: https
port: 443
targetPort: 443
selector:
app: nginx
Run kubectl_create nginx-service.yaml
and then the environment variable PUBLIC_IP will be substituted before running the actual kubectl create command.
Upvotes: 9
Reputation: 1984
One line:
cat app-deployment.yaml | sed "s/{{BITBUCKET_COMMIT}}/$BITBUCKET_COMMIT/g" | kubectl apply -f -
In yaml:
...
containers:
- name: ulisses
image: niceuser/niceimage:{{BITBUCKET_COMMIT}}
...
Upvotes: 38