Running a Job

The simplest workload you can set up in RAIL is running a Job. It will schedule a Pod that runs its container to completion and then stops.

In this example we will run a Job that runs a Python program that computes plenty of digits of 𝜋. We will use the standard Dockerhub Python container and we will use a ConfigMap to provide the Python script that we want to run. We will also use the kustomize tool as a simpler way to create the ConfigMap.

We have prepared the example in the <https://git.app.uib.no/gisle/k8s-pi-job> repo. Here you can inspect the content of the kustomization.yaml file which results in two resources a Job and a ConfigMap that you can view by running this on your workstation:

kubectl kustomize git@git.app.uib.no:gisle/k8s-pi-job.git?ref=f1f411f93f953c01b24b9b0e0b172afd2185d743

This will output the Kubernetes resources you need for this job.

This output can then be piped to kubectl apply -f - to create the objects in the cluster.

You can also apply this directly with:

kubectl apply -k git@git.app.uib.no:gisle/k8s-pi-job.git?ref=f1f411f93f953c01b24b9b0e0b172afd2185d743

Then inspect the state of the job with:

kubectl describe job pi

When the job finishes you can read the output generated with:

kubectl logs job/pi

The job and the pod are automatially deleted after 10 minutes (as specified by the ttlSecondsAfterFinished setting). If you want to clean up before this time run this command:

kubectl delete -k git@git.app.uib.no:gisle/k8s-pi-job.git?ref=f1f411f93f953c01b24b9b0e0b172afd2185d743

For reference, this is the resource specification file describing the job above:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  ttlSecondsAfterFinished: 600
  template:
    spec:
      securityContext:
        runAsUser: 10000
      restartPolicy: Never
      containers:
      - name: pi
        image: python:3
        command: ["python", "/app/pi.py", "10000"]
        volumeMounts:
        - name: app
          mountPath: /app

        # RAIL requires us to specify how resource hungry each container is
        resources:
          requests:
            cpu: 200m
            memory: 5Mi
          limits:
            cpu: 200m
            memory: 20Mi

        # This states the defaults for the securityContext and will get rid of
        # the warning that you should set these values.  These values can not be
        # set at the Pod-level, so they need to be specified here.
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          runAsNonRoot: true
          seccompProfile:
            type:
              RuntimeDefault

In order to comply with the demands of the RAIL platform we had to use the ":3" tag for the image instead of defaulting to ":latest" which is not allowed, and we hade to override the user that it runs under with securityContext.runAsUser since this container by default runs as root. The id 10000 is just conventional, any number other that 0 will do. You can put the securityContext.runAsUser specification either at the Pod-level as done here, or inside the container record.

The resources block must be specified for each container in RAIL. This specifies the resources the container normally will consume as well as upper limits. More information available from Container Resources Specification.

Another approach

Instead of using the base Python container and providing the script using a ConfigMap we might want to build a custom container that contains all we need and run that. This require a Dockerfile and a system that can build and publish the image to a container registry – and our git.app.uib.no service provide just this. The <https://git.app.uib.no/gisle/k8s-pi-job> repository is set up with both the Dockerfile and the build instructions for producing the image in the .gitlab-ci.yml file.

This is the Dockerfile required:

FROM python:3-alpine
RUN mkdir /app
COPY pi.py /app
WORKDIR /app
USER 10000

The essence of the .gitlab-ci.yml is simply:

build_container:
  stage: build
  variables:
    dir: .
    image_name: pi-job
    dockerfile: Dockerfile
  extends:
  - .build_container_image
  rules:
  - if: '$REUSE_CONTAINER == null'

This will cause the container image pi-job to be built and published to the git.app.uib.no:4567 registry. The full address to the image in the repo will then be git.app.uib.no:4567/gisle/k8s-pi-job/pi-job:<tag>.

To actually run this job we now require this one Kubernetes resource file:

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  ttlSecondsAfterFinished: 600
  template:
    spec:
      restartPolicy: Never
      containers:
      - name: pi
        image: git.app.uib.no:4567/gisle/k8s-pi-job/pi-job:f32c04f6
        command: ["python", "pi.py", "10000"]

        # RAIL requires us to specify how resource hungry each container is
        resources:
          requests:
            cpu: 200m
            memory: 5Mi
          limits:
            cpu: 200m
            memory: 20Mi

        # This states the defaults for the securityContent and will get rid of
        # the warning that you should set these values.  These values can not be
        # set at the Pod-level, so they need to be specified here.
        securityContext:
          allowPrivilegeEscalation: false
          capabilities:
            drop:
            - ALL
          runAsNonRoot: true
          seccompProfile:
            type:
              RuntimeDefault

It’s the same as above, but simplified since we now don’t have to mess with volumes to mount the ConfigMap and we don’t have to override the securityContext.runAsUser either since our custom container has already overridden the default user id.

To run this job just pipe this file to kubectl apply -f- and to clean up the job pipe this file to kubectl delete -f-.

The resource file above works when the registry is connected to a public Git project, which also gives you a public registry that can be fetched from without providing authentication information. If you publish from a protected or private project, then you need to provide authentication information by setting up the imagePullSecrets list. We have a separate document on UiB GitLab as Container Image Registry that explains how to get an access token and configure authentication.