Automated way to create multiple kubernetes Job manifests - kubernetes

Cron template
kind: CronJob
metadata:
name: some-example
namespace: some-example
spec:
schedule: "* 12 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: some-example
image: gcr.io/some-example/some-example
imagePullPolicy: Always
env:
- name: REPO_URL
value: https://example.com/12/some-example
I need to create multiple Job files with different URLs of REPO_URL over 100s save in a file. I am looking for a solution where I can set Job template and get the required key:value from another file.
so far I've tried https://kustomize.io/, https://ballerina.io/, and https://github.com/mikefarah/yq. But I am not able to find a great example to fit the scenario.

That would be pretty trivial with yq and a shell script. Assuming
your template is in cronjob.yml, we can write something like this:
let count=0
while read url; do
yq -y '
.metadata.name = "some-example-'"$count"'"|
.spec.jobTemplate.spec.template.spec.containers[0].env[0].value = "'"$url"'"
' cronjob.yml
echo '---'
let count++
done < list_of_urls.txt | kubectl apply -f-
E.g., if my list_of_urls.txt contains:
https://google.com
https://stackoverflow.com
The above script will produce:
[...]
metadata:
name: some-example-0
namespace: some-example
spec:
[...]
env:
- name: REPO_URL
value: https://google.com
---
[...]
metadata:
name: some-example-1
namespace: some-example
spec:
[...]
env:
- name: REPO_URL
value: https://stackoverflow.com
You can drop the | kubectl apply -f- if you just want to see the
output instead of actually creating resources.
Or for more structured approach, we could use Ansible's k8s
module:
- hosts: localhost
gather_facts: false
tasks:
- k8s:
state: present
definition:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: "some-example-{{ count }}"
namespace: some-example
spec:
schedule: "* 12 * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: some-example
image: gcr.io/some-example/some-example
imagePullPolicy: Always
env:
- name: REPO_URL
value: "{{ item }}"
loop:
- https://google.com
- https://stackoverflow.com
loop_control:
index_var: count
Assuming that the above is stored in playbook.yml, running this with
ansible-playbook playbook.yml would create the same resources as the
earlier shell script.

Related

Kubernetes cronjob doesn't use env variables

I have a Cron job in Kubernetes that does not use the urls in env variables to go to another api's find information to use in him, returning errors like will be using the urls of the appsettings/launchsettings from the console application project.
When I executed the cronjob, it returned an error ex: "Connection refused (20.210.70.20:80)"
My Cron job:
`
apiVersion: batch/v1
kind: CronJob
metadata:
name: productintegration-cronjob
spec:
schedule: "0 3 * * *"
concurrencyPolicy: Forbid
jobTemplate:
spec:
template:
spec:
restartPolicy: Never
containers:
- name: productintegration-cronjob
image: reconhece.azurecr.io/ms-product-integration:9399
command:
- /bin/sh
- -c
- echo dotnet - $(which dotnet);
echo Running Product Integration;
/usr/bin/dotnet /app/MsProductIntegration.dll
env:
- name: DatabaseProducts
value: "http://catalog-api:8097/api/Product/hash/{0}/{1}"
- name: DatabaseCategory
value: "http://catalog-api:8097/api/Category"
`
My catalogApi deployment where my cron job needs to go:
`
apiVersion: apps/v1
kind: Deployment
metadata:
name: catalog-api-deployment
labels:
app: catalog-api
spec:
replicas: 1
selector:
matchLabels:
app: catalog-api
template:
metadata:
labels:
app: catalog-api
spec:
containers:
- name: catalog-api
image: test.azurecr.io/ms-catalog-api:6973
ports:
- containerPort: 80
env:
- name: DatabaseSettings__ConnectionString
value: "String Connection" - I removed
- name: DatabaseSettings__DatabaseName
value: "DbCatalog"
``
The minikube works fine.
How do I fix this error ?
I already changed the port from my catalogApi but without success.
I tried changing the name of the env variable but without success too.

How to kustomize resouce in k8s?

Assume I have cronjob 、service and deployment just like below:
# Create a directory to hold the base
mkdir base
# Create a base/deployment.yaml
cat <<EOF > base/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-nginx
spec:
selector:
matchLabels:
run: my-nginx
replicas: 2
template:
metadata:
labels:
run: my-nginx
spec:
containers:
- name: my-nginx
image: nginx
EOF
# Create a base/service.yaml file
cat <<EOF > base/service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-nginx
labels:
run: my-nginx
spec:
ports:
- port: 80
protocol: TCP
selector:
run: my-nginx
EOF
# Create a base/cronjob.yaml file
cat <<EOF > base/cronjob.yaml
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
EOF
# Create a base/kustomization.yaml
cat <<EOF > base/kustomization.yaml
resources:
- deployment.yaml
- service.yaml
- cronjob.yaml
EOF
Normally, everything is working fine, but sometime my project don't require to run a cronjob, so I want to disable the cronjob.yaml import.
So, Is there a way to do that? for example like the jinja2,
- deployment.yaml
- service.yaml
{{ IF set CRONJOB }}
- cronjob.yaml
{{ ENDIF }}
I Understand #AniAggarwal posted, i can use a filter before run my kubectl apply -f but it no a very good for my project.
Any suggestions are welcome.
Assume the following is your file that gets generated after kustomize command. And the service is supposed to be conditional like your cronjob.
kind: Pod
apiVersion: v1
metadata:
name: echo-app
labels:
app: demo
spec:
containers:
- name: nginx
image: nginx
## if/end data.values.service.enabled:
---
kind: Service
apiVersion: v1
metadata:
name: echo-service
spec:
selector:
labels:
app: name
ports:
- name: port
port: 80
You can pipe the output of Your kustomize command to ytt to add or remove the service.
kustomize build | ytt --data-value service.enabled=false -f - | kubectl apply -f -
Checkout the project playground for another example
https://carvel.dev/ytt/#playground
https://github.com/vmware-tanzu/carvel-ytt/blob/develop/examples/data-values/run.sh
Hope it is the solution you're looking for. Good luck!

Trying to create a simple CronJob

$ kubectl api-versions | grep batch
batch/v1
batch/v1beta1
When attempting to create this CronJob object which has a single container and an empty volume, I get this error:
$ kubectl apply -f test.yaml
error: error parsing test.yaml: error converting YAML to JSON: yaml: line 19: did not find expected key
The YAML
$ cat test.yaml
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: dummy
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: app
image: alpine
command:
- echo
- Hello World!
volumeMounts:
- mountPath: /data
name: foo
restartPolicy: OnFailure
volumes:
- name: foo
emptyDir: {}
Based on my reading of the API, I believe my schema is legit. Any ideas or help would be greatly appreciated.
I think it's indentation issue. Below yaml should work.
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: dummy
spec:
schedule: "*/1 * * * *"
jobTemplate:
spec:
template:
spec:
containers:
- name: app
image: alpine
command:
- echo
- Hello World!
volumeMounts:
- mountPath: /data
name: foo
restartPolicy: OnFailure
volumes:
- name: foo
emptyDir: {}

How to set the result of shell script into arguments of Kubernetes Cronjob regularly

I have trouble setting the result value of a shell script to arguments for Kubernetes Cronjob regularly.
Is there any good way to set the value refreshed everyday?
I use a Kubernetes cronjob in order to perform some daily task.
With the cronjob, a Rust application is launched and execute a batch process.
As one of arguments for the Rust app, I pass target date (yyyy-MM-dd formatted string) as a command-line argument.
Therefore, I tried to pass the date value into the definition yaml file for cronjob as follows.
And I try setting ${TARGET_DATE} value with following script.
In the sample.sh, the value for TARGET_DATE is exported.
cat sample.yml | envsubst | kubectl apply -f sample.sh
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: some-batch
namespace: some-namespace
spec:
schedule: "00 1 * * 1-5"
jobTemplate:
spec:
template:
spec:
containers:
- name: some-container
image: sample/some-image
command: ["./run"]
args: ["${TARGET_DATE}"]
restartPolicy: Never
I expected that this will create TARGET_DATE value everyday, but it does not change from the date I just set for the first time.
Is there any good way to set result of shell script into args of cronjob yaml regularly?
Thanks.
You can use init containers for that https://kubernetes.io/docs/concepts/workloads/pods/init-containers/
The idea is the following: you run your script that setting up this value inside init container, write this value into shared emptyDir volume. Then read this value from the main container. Here is example:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: some-batch
namespace: some-namespace
spec:
schedule: "00 1 * * 1-5"
jobTemplate:
spec:
template:
spec:
initContainers:
- name: init-script
image: my-init-image
volumeMounts:
- name: date
mountPath: /date
command:
- sh
- -c
- "/my-script > /date/target-date.txt"
containers:
- name: some-container
image: sample/some-image
command: ["./run"]
args: ["${TARGET_DATE}"] # adjust this part to read from file
volumeMounts:
- name: date
mountPath: /date
restartPolicy: Never
volumes:
- name: date
emptyDir: {}
You can overwrite your docker entrypoint/ k8s container cmd and do this in one shot:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: some-batch
namespace: some-namespace
spec:
schedule: "00 1 * * 1-5"
jobTemplate:
spec:
template:
spec:
containers:
- name: some-container
image: sample/some-image
command: ["/bin/sh"]
args:
- -c
- "./run ${TARGET_DATE}"
restartPolicy: Never

CronJob: unknown field "configMapRef"

I'm applying a Kubernetes CronJob.
So far it works.
Now I want to add the environment variables. (env: -name... see below)
While tryng to apply I get the error
unknown field "configMapRef" in io.k8s.api.core.v1.EnvVarSource
I don't like to set all singles variables here. I prefer to link the configmap to not to double the variables. How is it possible set a link to the configmap.yaml variables in a CronJob file, how to code it?
Frank
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: ad-sync
creationTimestamp: 2019-02-15T09:10:20Z
namespace: default
selfLink: /apis/batch/v1beta1/namespaces/default/cronjobs/ad-sync
spec:
concurrencyPolicy: Allow
failedJobsHistoryLimit: 1
successfulJobsHistoryLimit: 3
suspend: false
schedule: "0 */1 * * *"
jobTemplate:
metadata:
labels:
job: ad-sync
spec:
template:
spec:
containers:
- name: ad-sync
image: foo.azurecr.io/foobar/ad-sync
command: ["dotnet", "AdSyncService.dll"]
args: []
env:
- name: AdSyncService
valueFrom:
configMapRef:
name: ad-sync-service-configmap
restartPolicy: OnFailure
There is no such field configMapRef in env field instead there is a field called configMapKeyRef
in order to get more detail about kubernetes objects, its convenient to use kubectl explain --help
for example if you would like to check all of the keys and their types you can use following command
kubectl explain cronJob --recursive
kubectl explain cronjob.spec.jobTemplate.spec.template.spec.containers.env.valueFrom.configMapKeyRef
You should use configMapKeyRef for single value or configMapRef with envFrom
It works this way:
apiVersion: batch/v1beta1
kind: CronJob
metadata:
...
spec:
...
jobTemplate:
metadata:
...
spec:
template:
spec:
containers:
- name: ad-sync
...
envFrom:
- configMapRef:
name: ad-sync-service-configmap
command: ["dotnet", "AdSyncService.dll"]
There are two approaches, using valueFrom for individual values or envFrom for multiple values.
valueFrom is used inside the env attribute, like this:
spec:
template:
spec:
containers:
- name: ad-sync
image: foo.azurecr.io/foobar/ad-sync
command: ["dotnet", "AdSyncService.dll"]
args: []
env:
- name: AdSyncService
valueFrom:
configMapKeyRef:
name: ad-sync-service-configmap
key: log_level
envFrom is used direct inside the container attribure like this:
spec:
template:
spec:
containers:
- name: ad-sync
image: foo.azurecr.io/foobar/ad-sync
command: ["dotnet", "AdSyncService.dll"]
envFrom:
- configMapRef:
name: ad-sync-service-configmap
ConfigMap for reference:
apiVersion: v1
kind: ConfigMap
metadata:
name: ad-sync-service-configmap
namespace: default
data:
log_level: INFO
The main difference on both is:
valueFrom will inject the value of a a key from the referenced configMap
envFrom will inject All configMap keys as environment variables
The main issue with you example is that you used the configMapRef from envFrom inside the valueFrom where should actually be configMapKeyRef.
Also, the configMapKeyRef need a key attribute to identify where the data is comming from.
For more details, please check in this docs.