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: 
mariusobert
Developer Advocate
Developer Advocate
This post shows how to deploy SAP Fiori apps via the Kyma runtime to the HTML5 Application Repository. The deployment flow in this runtime follows the same philosophy as the flow of the Cloud Foundry runtime and achieves the same result.

The SAP HTML5 Application Repository Service for SAP BTP provides secure and performant storage space for all your web applications. In the previous posts, I explained how this service is used in the Cloud Foundry environment of SAP BTP. This makes much sense when the other components of your project run in the Cloud Foundry runtime or when there are no other components, and you don’t want to consume any runtime quota. But the situation changes when you want to run different project components in another runtime, such as the Kyma runtime. It doesn’t make much sense to use an MTA project for a “pseudo Cloud Foundry deployment” that will only create the service instances, and the “rest of the party” is going on in a different runtime. In this post, I’ll show you how to deploy any web application via the Kyma runtime. This web application can then be added to the SAP Launchpad service.



Uploading Web Apps via the Kubernetes Environment


 

This approach works analogously to the deployment in the Cloud Foundry environment:

  • The SAP HTML5 Application Repository Service stores the web application and exposes them to the managed application router.

  • We leverage the SAP Launchpad Service as a managed application router to lower the project's memory footprint (and the total cost of ownership).

  • The SAP Destination Service and the UAA Service are required by the managed application router to “find” the web app in the HTML5 application repository. Besides this aspect, the services can also guard the web app against unauthorized access and build connections to registered backend systems.



Watch this SAP Tech Bytes video to see me build a sample project



Revisiting the deployment artifacts for the Cloud Foundry runtime


What do we need in the Kubernetes environment to deploy web apps? Let's find out by having a look at the MTA project descriptor to see what exactly happens in the Cloud Foundry environment:
ID: managed-fiori
_schema-version: 3.2.0
version: 1.0.0
parameters:
enable-parallel-deployments: true
modules:
- name: HTML5Module
type: html5
path: HTML5Module
build-parameters:
builder: custom
commands:
- npm run build
supported-platforms: []
- name: webapp-deployer
type: com.sap.application.content
path: .
requires:
- name: managed-fiori-html5-repo-host
parameters:
content-target: true
build-parameters:
build-result: resources
requires:
- artifacts:
- HTML5Module-content.zip
name: HTML5Module
target-path: resources/
- name: managed-fiori-destination-content
type: com.sap.application.content
build-parameters:
no-source: true
requires:
- name: managed-fiori-uaa
parameters:
service-key:
name: managed-fiori-uaa-key
- name: managed-fiori-html5-repo-host
parameters:
service-key:
name: managed-fiori-html5-repo-host-key
- name: managed-fiori-destination
parameters:
content-target: true
parameters:
content:
instance:
existing_destinations_policy: update
destinations:
- Name: managed-fiori-destination-html5
ServiceInstanceName: managed-fiori-html5-repo-host
ServiceKeyName: managed-fiori-html5-repo-host-key
sap.cloud.service: cloud.service
- Name: managed-fiori-destination-uaa
Authentication: OAuth2UserTokenExchange
ServiceInstanceName: managed-fiori-uaa
ServiceKeyName: managed-fiori-uaa-key
sap.cloud.service: cloud.service

resources:
- name: managed-fiori-destination
type: org.cloudfoundry.managed-service
parameters:
service-plan: lite
service: destination
path: ./destination.json
- name: managed-fiori-html5-repo-host
type: org.cloudfoundry.managed-service
parameters:
service-plan: app-host
service: html5-apps-repo
config:
sizeLimit: 2
- name: managed-fiori-uaa
type: org.cloudfoundry.managed-service
parameters:
path: ./xs-security.json
service-plan: application
service: xsuaa

Sample project descriptor for the Cloud Foundry environment


We noticed the following things here:

  1. The resources section defines the UAA, HTML5 application repository host, and the destination service instance referenced in the modules.

  2. The module “HTML5Module” contains the static resources of the web application. It won’t be part of the built archive, and it is only here to be referenced by the deployer module.

  3. The module “webapp-deployer” depends on the module above to copy the built resources in this module. The deployer uploads these resources to the HTML5 application repository when during the project deployment. This also explains why there is a service binding to the HTML5 application repository service. This module does not require any runtime memory as it leverages a built-in feature of the Cloud Foundry environment (“com.sap.application.content”).

  4. The module “managed-fiori-destination-content” creates new service keys for the required UAA and HTML5 application repository host service instances. It also uses information from these service keys to creating new instance-level destinations in the bound destination service. All of this happens during deploy-time and doesn’t consume any memory at all.


Deployment artifacts for the Kyma runtime


Defining the service instances


Let’s start with the first item of the list – the service instance definitions: Kubernetes-based environments have a similar concept for services as Cloud Foundry environments; Both environments have the notion of service instances and bindings because they implement the Open Service Broker API). Kubernetes projects declare these objects as YAML documents in the project manifest, just like deployment and pods. I wrote a blog post about this mechanism a few weeks ago; Here’s a short recap of that post:
---
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceInstance
metadata:
name: kyma-destination-instance
spec:
clusterServiceClassExternalName: destination
clusterServicePlanExternalName: lite
parameters:
HTML5Runtime_enabled: true
version: "1.0.0"

---
apiVersion: servicecatalog.k8s.io/v1beta1
kind: ServiceBinding
metadata:
name: kyma-destination-binding
spec:
instanceRef:
name: kyma-destination-instance

An example of the service instance and service binding definition for a Kyma project


The two YAML documents from the sample above define the needed destination service instance and the service binding. This scheme can be repeated for the XSUAA and HTML5 application repository services as well. Service bindings can then be mounted as volumes to attach them to pods:
containers:
- image: ghcr.io/sap-samples/kyma-html5-app-deployer
imagePullPolicy: Always
name: html5appdeployer
volumeMounts:
- name: destination-volume
mountPath: /etc/secrets/sapcp/destination/kyma-destination-instance
readOnly: true
...
volumes:
- name: destination-volume
secret:
secretName: kyma-destination-binding

Mounting the service binding of the previous snippet to a pod



Web App Deployer


The Cloud Foundry project uses three modules to upload the web app to the HTML5 application repository and to create the destinations for the managed application router. Due to this convenient separation, SAP BTP, Cloud Foundry Environment does everything during deploy-time and doesn't require any runtime artifacts that consume runtime memory. As the Kubernetes Environment is not customized (in the sense of no built-in application content deployer) by SAP, we must go a different path. But there's also an advantage to this path: We only need one pod, the "Kubernetes-equivalent" of a CF module, that takes care of the deployment and the creation of the destinations.

Build the deployer app


deployer
+-- resources
| +-- ui5-app
| | +-- index.html
| | +-- manifest.json
| | +-- xs-app.json
+-- package.json
+-- Dockerfile

The directory structure of the Kyma web app deployer application


Your deployer application's directory structure should contain a resources directory that contains one sub-folder per web app that needs to be uploaded. As usual, each web app needs to include at least the manifest.json and the xs-app.jsonfiles. On the project root, you need the well-known package.jsonfile, which has a dependency on the html5-app-deployer package:
{
"name": "kyma-html5-app-deployer",
"version": "1.0.0",
"dependencies": {
"@sap/html5-app-deployer": "2.3.1"
},
"scripts": {
"start": "node node_modules/@sap/html5-app-deployer/index.js"
}
}

Do you recall how we used to upload web apps in the Cloud Foundry environment about 1-2 years ago? If so, this approach might be quite familiar to you. In addition to the mentioned files, you need a Dockerfile in the application root to describe how the Docker image is built.
FROM node:14-alpine

RUN mkdir -p /app && \
chown node.node /app

# Create app directory
WORKDIR /app

# Bundle app source
COPY . .
RUN npm install --production

CMD [ "npm", "start" ]

Build and upload the image


As with every Kubernetes application, we first need to dockerize the application before it can be deployed. To do so, run the following commands to build the image and upload it to your registry.  I'll use DockerHub, but you can also use your preferred registry for this.
docker build -t <docker id>/kyma-html5-app-deployer 
docker push <docker id>/kyma-html5-app-deployer

Reference the image in the pod


Now it's time to connect all the loose ends. Add a Kubernetes object deployment to the project manifest. This pod is based on the Docker image of the previous step. It not only uploads the web app to the HTML5 application repository but also creates all the needed instance-level destinations. For this, we need to mount the existing service binding via volumes. The final deployment.yaml should look as follows:
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: html5appdeployer
labels:
app: html5appdeployer
spec:
replicas: 1
selector:
matchLabels:
app: html5appdeployer
template:
metadata:
labels:
app: html5appdeployer
spec:
containers:
- image: <docker id>/kyma-html5-app-deployer
imagePullPolicy: Always
name: html5appdeployer
volumeMounts:
- name: html5-repo-app-host-volume
mountPath: /etc/secrets/sapcp/html5-apps-repo/kyma-app-host-instance
readOnly: true
- name: xsuaa-volume
mountPath: /etc/secrets/sapcp/xsuaa/kyma-xsuaa-instance
readOnly: true
- name: destination-volume
mountPath: /etc/secrets/sapcp/destination/kyma-destination-instance
readOnly: true
env:
- name: SAP_CLOUD_SERVICE
value: "business.service"
volumes:
- name: html5-repo-app-host-volume
secret:
secretName: kyma-app-host-binding
- name: xsuaa-volume
secret:
secretName: kyma-xsuaa-binding
- name: destination-volume
secret:
secretName: kyma-destination-binding

You can also add this YAML document to the same project manifest that contains the previously mentioned documents (that define the service instances and bindings).

Tip: I found it useful to set the imagePullPolicy toAlways. This makes it easier to replace the current web app in the development process as you just need to upload the new image to the registry. In the next step, you can then remove the old pod from the cluster. Kyma will automatically start a new pod based on the most recent image. There's no need to modify the deployment itself.

Deploy to SAP BTP, Kyma runtime


Have you already enabled your Kyma runtime and connected to it the kubectl CLI? If so, run the following command to trigger the deployment:
kubectl apply -f deployment.yaml

This command will invoke all the needed actions to get your SAP Fiori app up and running via the Kyma runtime 🚀.

Access the SAP Fiori App


Access the web app via the SAP BTP cockpit or assemble the URL according to the following pattern:

https://<subaccount id>.launchpad.cfapps.eu10.hana.ondemand.com/<destination service instance ID>.businessservice.tokendisplay/index.html

You can find your subaccount id in SAP BTP Cockpit and the destination instance ID in the Kyma console when you inspect the secret of the destination service instance:


Find the destination instance ID in the Kyma Console.


The final URL will then look like this one:

https://43de072btrial.launchpad.cfapps.eu10.hana.ondemand.com/8aebc2e1-2234-4bd1-8da4-e15231138dbf.b...

Full end-to-end sample


A full end-to-end sample of this technique is available in the GitHub Repo "Examples of HTML5 Applications for SAP Business Technology Platform Multi-Cloud Environments". This sample also contains a backend component that decodes passed-in JWT token value to create a rich sample scenario. The Fiori application that is baked in the Docker image ghcr.io/sap-samples/kyma-html5-app-deployer:latestcontains the following web app:


SAP Fiori app of the end-to-end sample



Summary


In this post, you have learned:

  • how to use the managed application router from the Kyma runtime

  • how to mimic service bindings in the Kubernetes deployment descriptor

  • my personal tips for the deployer pod configuration

  • how to build a docker image that contains the web app resources

  • where to find HTML5 application samples for any SAP BTP runtime

4 Comments
anubhavpandey
Advisor
Advisor
0 Kudos

Hi Marius,

 

Thanks for a very inofrmative blog. I am facing issue post depoyment to Kyma runtme.

When accessing the service, I am getting 503 Service unavailable issue for the same app and service.

 

Nt sure, what am I missing.

mariusobert
Developer Advocate
Developer Advocate
Mhh, strange. Can you please share more information?
Do you see a success message in the Kyma logs? And do you see the app in the HTML5 panel of the BTP Cockpit?
anubhavpandey
Advisor
Advisor
Thanks Marius for pointing out the issue in deployment file.

The code repo you shared works perfectly. However, in y case there was already an API service running with "backend" host name and that caused the conflict.

Great blog to kick start your journey on Kyma!
LuizGomes
Participant
0 Kudos

Is there an updated approach to this scenario?