Technology Blogs by SAP
Learn how to extend and personalize SAP applications. Follow the SAP technology blog for insights into SAP BTP, ABAP, SAP Analytics Cloud, SAP HANA, and more.
cancel
Showing results for 
Search instead for 
Did you mean: 
DenisDuev
Product and Topic Expert
Product and Topic Expert
461

Abstract

The SAP Job Scheduling service can be consumed both from CF and Kyma environments. You may have seen the blogs about CF - if not, you can find them in the Overview of Blogs.

What are the differences between using the Job Scheduling service for CF and for Kyma:

  • The way you provision the service instance
  • The way you bind the service instance to your application
  • Adding permissions to your user in the BTP cockpit to access the Job Scheduling service dashboard

In addition you will learn:

  • how to use a Dockerfile to build a Docker image for your node app
  • how to publish this image to a docker registry
  • how to define a Kubernetes yaml file with everything needed to deploy your solution - including resources like ServiceInstance, ServiceBinding, APIRule

Prerequisites 📋

  • You have enabled the Kyma environment
  • You have entitlements and quota for the Job Scheduling service
  • kubectl
  • docker (or podman - replace "docker" with "podman" in the commands below)
  • A registration on DockerHub (or some other docker repository)

Here is what the file structure will look like at the end:

kyma-part-1/
├── Dockerfile
├── manifest.yaml
├── package.json
└── server.js 

Steps 🛠

1. Application 📦

We will start by preparing our application for deployment in Kyma.

We can reuse one of the applications provided in the tutorials for Cloud Foundry.

We define our dependencies in package.json:

{
  "main": "server.js",
  "dependencies": {
    "express": "^4.16.3"
  }
}

Define our endpoint logic in server.js using the express library

const express = require('express');
const app = express();

app.get('/runjob', function(req, res){
   console.log('==> [APP JOB LOG] Job is running . . .');
   res.send('Finished job');
});

const port = process.env.PORT || 3000;
app.listen(port, function(){
   console.log('listening');
})

2. Build and publish a docker image 🐳

In CF we've defined a manifest file and used cf push to deploy it to the platform. In Kubernetes we have one additional step. We need to prepare a docker image before applying our configurations to the cluster.

2.1 Create a Dockerfile 📄

# Use NodeJS base image
FROM node:20

# Create app directory
WORKDIR /usr/src/app

# Install app dependencies by copying package.json and package-lock.json
COPY package*.json ./

# Install dependencies
RUN npm install

# Setting env variables
ENV PORT 80

# Copy app source
COPY . .

# Bind the port on which a container will listen for traffic
EXPOSE 80

# Define the Docker image's behavior at runtime
CMD ["npm","run","start"]

2.2 Build a Docker image 🔨

docker build . -t jobapp:part1

2.3 Create repository on Dockerhub 🗂

These instructions are for Dockerhub, but you can use any other docker repository you want.

We need this repository to upload the image we've build in the previous step.

  1. Open Dockerhub
  2. After logging in, go to the "Repositories" tab
  3. press "Create Repository" and give it a name jobapp
  4. press "Create"

dockerhub-create-repo.png

2.4 Upload the image to a Docker repository ⬆️

  1. Log in to Dockerhub
docker login -u <your-user>

Alternatively: podman login docker.io -u <your-user>

  1. Push the image to your docker registry
docker push <your-user>/jobapp:part1

Alternatively:  podman push jobapp:part1 <your-user>/jobapp:part1

docker-push.png

3. Apply to Kyma cluster ☁️

In order to work with Kyma (which is in fact, a Kubernetes cluster with additional custom resource definitions) we need a Kubeconfig to authenticate and the kubectl CLI

3.1 Download and setup Kubeconfig 📥

Open the BTP Cockpit, under your subaccount go to "Overview" and find the section "Kyma Environment" and click on "KubeconfigURL". This will download the kubeconfig file to your local PC.

kyma-cockpit.png

Then open your terminal (Mac, Linux)

export KUBECONFIG=<location of the downloaded file>

For windows use: Powershell: $env:KUBECONFIG = "…\kubeconfig.yaml", to verify: echo $env:KUBECONFIG 

CMD: set KUBECONFIG=…\kubeconfig.yaml, to verify: echo %KUBECONFIG%

3.2 Create a namespace 🏷

It's a good practice to use separate namespaces, which is why we will create one for this exercise.

kubectl create namespace jobapp-part1

3.3 Create manifest.yaml 📜

The manifest.yaml is like the mtad.yaml (MultiTarget Application), but for Kubernetes

This YAML file contains all of the configuration for the Kyma cluster to deploy our application, expose an endpoint, create a service instance and bindings and mount them to the deployment of our application.

Note that you need to fill:

image: <your-username>/jobapp:part1
(...)
host: jobapp-part1-<your-suffix>.<kyma-apps-domain>

You can find help as comments in the manifest.yaml.

# defining the service instance
apiVersion: services.cloud.sap.com/v1
kind: ServiceInstance
metadata:
  # this will be the name of your service instance
  name: jobscheduler-instance
  labels:
    app.kubernetes.io/name: jobscheduler-instance
  annotations: {}
  namespace: jobapp-part1
spec:
  # the values supplied here are the ones from the Service Marketplace
  # serviceOfferingName is the "Technical name" of the Job Scheduling Service
  serviceOfferingName: jobscheduler
  servicePlanName: standard
---
# defining the Service Binding
apiVersion: services.cloud.sap.com/v1
kind: ServiceBinding
metadata:
  # this will be the name of your service binding
  name: jobscheduler-binding
  labels:
    app.kubernetes.io/name: jobscheduler-binding
  annotations: {}
  namespace: jobapp-part1
spec:
  # specify the name of the service-instance
  serviceInstanceName: jobscheduler-instance
---
apiVersion: v1
kind: Service
metadata:
  namespace: jobapp-part1
  name: jobapp
  labels:
    run: jobapp
spec:
  type: ClusterIP
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
      name: http
  selector:
    app: jobapp
---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: jobapp-part1
  name: jobapp
  labels:
    app: jobapp
    version: nodejs
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jobapp
      version: nodejs
  template:
    metadata:
      labels:
        app: jobapp
        version: nodejs
    spec:
      containers:
        - name: jobapp
          # replace <your-username> with your Dockerhub image namespace/user
          image: <your-username>/jobapp:part1
          imagePullPolicy: Always
          ports:
            - containerPort: 80
          env:
            - name: SERVICE_BINDING_ROOT
              value: /bindings
          # this mounts a volume from the service-binding    
          volumeMounts:
            - mountPath: /bindings/jobscheduler-instance
              name: jobscheduler-volume
              readOnly: true
      volumes:
          # creates volume for the binding secret
        - name: jobscheduler-volume
          secret:
            defaultMode: 420
            secretName: jobscheduler-binding
---
apiVersion: gateway.kyma-project.io/v1beta1
kind: APIRule
metadata:
  namespace: jobapp-part1
  name: jobapp
  labels:
    app.kubernetes.io/name: jobapp
spec:
  gateway: kyma-gateway.kyma-system.svc.cluster.local
  rules:
    - accessStrategies:
        - handler: allow
          config: {}
      methods:
        - GET
        - POST
        - PUT
        - DELETE
      path: /.*
  # host is the address that your app will be accessible over
  # you can get <kyma-apps-domain> from the APIServerURL in the cockpit by removing the https://api or
  # by executing: kubectl config view --minify --output 'jsonpath={.clusters[0].cluster.server}' | sed 's#^https://api.##'
  # <your-suffix> can whatever you want - as long the the DNS is unique
  host: jobapp-part1-<your-suffix>.<kyma-apps-domain>
  service:
    name: jobapp
    port: 80

3.4 Apply changes to the cluster 🚀

kubectl apply -f manifest.yaml

kubectl-appy.png

4. Validate the application

Now that your application is deployed it's time to test it

Open your endpoint in the browser (for example https://jobapp-part1-<your-suffix>.<kyma-apps-domain>/runjob)

You should have specified this URL in the manifest.yaml (host)

test-app.png

5. Testing the Job Scheduling Service 🕒

5.1 Permissions for Dashboard 🔑

To view the Dashboard your user has to have the SAP_Job_Scheduling_Service_Admin Role assigned from the BTP cockpit.

  1. In your subaccount, from the "Security" menu, choose "Role Collections".
  2. Press "Create".
  3. In the dialog add a name SAP Job Scheduling Service Admin and description Assign it to access Job Scheduling Service Dashboard and manage jobs, then press "Create".
  4. Press on the newly created role collection "SAP Job Scheduling Service Admin".
  5. Press "Edit" and add "Roles".
  6. Find SAP_Job_Scheduling_Service_Admin, mark it in the list and press "Add".
  7. Press "Save" on the role collection,
  8. From the "Security" menu, go to "Users".
  9. Find your user, press on it, then press "Assign Role Collection".
  10. From the list find "SAP Job Scheduling Service Admin", mark it, and then press "Assign Role Collection".

kyma-jobscheduling-roles.gif

5.2 Link to the Dashboard 🔗

The Dashboard URL can be found from the BTP cockpit -> Instances and Subscriptions -> click on the "jobscheduler" Service Instance

dashboard.png

5.3 Create job 📝

  1. From the Job Scheduler Dashboard, choose “Jobs” on the left hand menu.
  2. Then press the button “Create Job”.
  3. In the creation dialog, specify the required information:
    • Name - this is the technical name, for example: jobapp_get_runjob 
    • Action - this is the endpoint of your service that we want to call (from step 4) https://jobapp-part1-<your-suffix>.<kyma-apps-domain>/runjob 
    • HTTP Method - in our case GET is correct
  4. Press "Save" and the dialog will be closed
  5. Then press on the "Name" of the newly created job. The Overview page will open.
  6. From the left hand menu choose "Schedules" and press "Create Schedule" button
  7. For "value" enter now and press "Save" (this means that the scheduler will be executed only one time - now)
  8. Press on the "Description" of the newly created schedule. The Overview page will open.
  9. From the left hand menu choose "Run Logs" to see the executions for this schedule
  10. If you press on "Runlog ID" you can see more details about the specific run (execution)

create-job-kyma.gif

🎉 That's it! 🎉

🥳 You have done it - you have successfully called your newly developed Kyma application using SAP Job Scheduling Service.


6. Cleanup

After you are ready you can delete all of your resources using

kubectl delete -f manifest.yaml

so that you are ready for our 🔝 next tutorial 🔝.

kubectl-delete.png

 

Troubleshooting🛠

Q: Error on kubectl apply?

kubectl-apply-no-namespace.png

A: Have you created your namespace? If not use

kubectl create namespace jobapp-part1

Q: kubectl apply is successful, but the application is not working?

A: You can open the Kyma UI to validate that all resources are correctly provisioned.

OR You can use following and check for the READY field

kubectl -n jobapp-part1 get all,serviceinstance,servicebindings,APIRule

kubectl-get-all.png

Q: Is my service instance created?

A: You can check using the 

kubectl -n jobapp-part1 describe serviceinstance.services.cloud.sap.com/jobscheduler-instance or in the Kyma 

UI:

kyma-ui-service-instance-created.png

Q: How to remove my resources?

A: Use CLI

kubectl delete -f manifest.yaml

Or manually in the Kyma UI


Q: When I call the application endpoint I get: no healthy upstream

Option 1: Check your pods, especially your Docker image - have you added your username?

  1. kubectl -n jobapp-part1 get pods
  2. kubectl -n jobapp-part1 describe jobapp-<unique-id-from-previous-step>

Option 2: Do you have enough quota for the Job Scheduling Service? Execute:

kubectl -n jobapp-part1 describe serviceinstance.services.cloud.sap.com/jobscheduler-instance

If you see something like the following, your quota is not enough and you need to increase it.

Status:
  Conditions:
    Last Transition Time:  2024-09-13T10:07:26Z
    Message:               BrokerError:, Status: 400, Description: Subaccount quota limit for specified service plan has exceeded. Please contact service administrator.
    Observed Generation:   1
    Reason:                CreateInProgress
    Status:                False
    Type:                  Succeeded
    Last Transition Time:  2024-09-13T10:07:26Z
    Message:
    Reason:                NotProvisioned