We've got a CronJob that is utilising Indexed Jobs1 for processing orders in parallel based on a modulus, i.e.
// Skip this order if it doesn't match our thread
if ($threaded && ($d['id'] % $mod) != $cnt) continue;
where:
$mod is the JOB_COMPLETION_INDEX
$cnt is the spec.completions value (total number of completions)
spec.completions == spec.parallelism
I've worked with pulling in pod / deployment metadata into environment variables like this before:
containers:
- env:
- name: GKE_SERVICE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.labels['app']
- name: GKE_REVISION
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
And setting spec.completionMode = Indexed already sets the environment variable JOB_COMPLETION_INDEX with the exeuction index, but I also need the spec.completions value.
Is there a way to put spec.completions into an environment variable without doing it manually?
We'd like to have it pull from the spec to avoid the possibility for mistakes, if the completions count is modified.
Related
I have the following environment variable in my Kubernetes template:
envFrom:
- configMapRef:
name: configmap
env:
- name: MACHINENAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
I would like to use the value from 'fieldRef' in a config map instead. Would this kind of modification be possible?
In other words, I want to add the 'MACHINENAME' environment variable to the config map, so I don't have to use the 'env:' block.
You cannot do this in the way you describe.
A ConfigMap only contains fixed string-key/string-value pairs. You cannot embed a more complex structure into a ConfigMap, or say that a ConfigMap value will be resolved using the downward API when a Pod is created. The node name of the pod, and most of the other downward API information, will be different for each pod using the ConfigMap (and likely even for each replica of the same deployment) and so there is no fixed value you can put into a ConfigMap.
You tagged this question with the Helm deployment tool. If you're using Helm, and you're simply trying to avoid repeating this boilerplate in every Deployment spec, you can write a helper template that includes this definition
{{/* templates/_helpers.tpl */}}
{{- define "machinename" -}}
- name: MACHINENAME
valueFrom:
fieldRef:
fieldPath: spec.nodeName
{{- end -}}
Now in your Deployment spec, you can include this template rather than retyping the whole YAML block.
containers:
- envFrom:
- configMapRef:
name: configmap
env:
{{ include "machinename . | indent 6 }}
(The exact indent value will depend on the context where you include it, and should be two more than the number of spaces at the start of the env: line. It is important that the line containing indent not itself be indented.)
Yes, using a ConfigMap would be possible. This Stack Overflow post is quite old, but has some good information in:
Advantage of using configmaps for environment variables with Kubernetes/Helm
You would need to either mount the ConfigMap as a volume or consume via environment variables by using envFrom. This guide provides both examples:
https://matthewpalmer.net/kubernetes-app-developer/articles/ultimate-configmap-guide-kubernetes.html
You can use the volume mount option and merge different configmap or env
volumes:
- name: all-in-one
projected:
sources:
- secret:
name: mysecret
items:
- key: username
path: my-group/my-username
- downwardAPI:
items:
- path: "labels"
fieldRef:
fieldPath: metadata.labels
- path: "cpu_limit"
resourceFieldRef:
containerName: container-test
resource: limits.cpu
- configMap:
name: myconfigmap
items:
- key: config
path: my-group/my-config
Ref : https://kubernetes.io/docs/concepts/storage/projected-volumes/
initContainer
There is another alternative you can follow if you want to merge those values.
Either you merge configmap & fieldRef with InitContainer as it's your Node name so, have to get value first and edit/add value to configmap with initContainer.
Inside my container I have in my spec.jobTemplate.spec.template.spec:
containers:
- name: "run"
env:
{{ include "schedule.envVariables" . | nindent 16 }}
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_CREATION_TIMESTAMP
valueFrom:
fieldRef:
fieldPath: metadata.creationTimestamp
However I just get the error:
CronJob.batch "schedule-3eb71b12d3" is invalid: spec.jobTemplate.spec.template.spec.containers[0].env[19]
When changing to:
- name: POD_CREATION_TIMESTAMP
value: ""
I get no errors. Any idea?
The reason is that fieldRef doesn't support the use of metadata.creationTimestamp.
$ kubectl explain job.spec.template.spec.containers.env.valueFrom
...
fieldRef <Object>
Selects a field of the pod: supports metadata.name, metadata.namespace,
`metadata.labels['<KEY>']`, `metadata.annotations['<KEY>']`, spec.nodeName,
spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.
...
Kubernetes lacks that capability. When I try to add that field using fieldPath, I see:
field label not supported: metadata.creationTimestamp
The only guaranteed way of getting that value (that I can think of) would be to give your cronjob RBAC access to request that info at runtime. Then, you could run this from inside the pod to get that value:
kubectl get pod ${POD_NAME} -o=jsonpath='{.metadata.creationTimestamp}'
Quite new to Helm. Currently, I create an env variable in a way that when I deploy my pod, I am able to see the pod name in the environment variables list. This can be done like so in the template file:
containers:
- name: my_container
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
Is it possible to do something similar in the values.yaml file (maybe in an extraEnv field?) and then use this value in the .tpl? Other configurations, like configmap names, depend on it, in order to be unique between pods and I want to easily retrieve the value like so:
volumes:
- name: my_vol
configMap:
name: {{ .Values.pathto.extraEnv.podname }}
Thanks in advance!
I have a template and a deployment in it, One of my deployment's env variables is a value from a secret that contains the name of the namespace that just used the template.
The secret must contain the name of the namespace, I can't change it.
I know that it is possible to use env variables as value of different env variables. Looks like this:
env:
- name: MY_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: FULL_NAME
value: ${MY_NAME}.$(MY_NAMESPACE)
This way, if my namespace's name is app and the MY_NAME parameter is dan:
echo $FULL_NAME
> dan.app
But I want to use that name as a reference to a secret name. Like this:
env:
- name: MY_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: FULL_NAME_SECRET
valueFrom:
secretKeyRef:
name: ${MY_NAME}.$(MY_NAMESPACE)
key: app_name
I get an event that says:
Pod "test-0" is invalid[spec.containers[0].env[1].valueFrom.secretKeyRef.name: Invalid value: "dan.$(MY_NAMESPACE)": a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start with an alphanumeric character
Well basically it does not translate the $(MY_NAMESPACE) to the actual value of its env variable.
The secretKeyRef: secret name must be a fixed string. Variable expansion only happens in a couple of places in the pod spec (in env: values, command:, and args:, but nowhere else).
For your immediate problem, it may help to recognize that a Pod and any matching Secret must be in the same namespace. You don't need to include the namespace name in the Secret name, and there's no way to refer to a Secret in a different namespace if you needed to.
- name: FULL_NAME_SECRET
valueFrom:
secretKeyRef:
name: dan # always in the same namespace
key: app_name
More broadly, you can use a tool like Helm to fill in some of these values from templates. These can use data known at install time to construct "fixed strings" that can be submitted to Kubernetes.
{{/* In a Helm chart's templates/deployment.yaml */}}
- name: FULL_NAME_SECRET
valueFrom:
secretKeyRef:
name: {{ include "dan.name" . }}.{{ .Release.Namespace }}
key: app_name
In this the parts in the curly braces {{ ... }} are expanded by Helm's template engine. .Release.Namespace is the namespace into which you're installing. include "dan.name" . calls a helper template that constructs the "dan" name, in a way that supports multiple installations in the same namespace.
I'm trying to use different secrets on a StatefulSet, based on the index o the pods.
Here is the things I tried:
env:
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: SECRET_KEY
valueFrom:
secretKeyRef:
key: key
name: mysecret-$(POD_NAME)
- name: SECRET_HOST
value: myhost-$(POD_NAME)
However, mysecret-$(POD_NAME) is not correctly sobstituted as parameter, while myhost-$(POD_NAME) acts correcly.
How can I solve this problem? The goal is to set different variables from secret/configmaps on different replicas of the StatefulSet.
AFAIK this is not supported. The only volumes you can have differ are the PVs. Instead you would use a single secret with keys or whatnot based on the pod index, and write your software to read from the correct key.