Wednesday 1 July 2020

Deploying .NET code on HANA XS Advanced

SAP has implemented Cloud Foundry (CF) on its SAP Cloud Platform, as a next-generation Platform as a Service (PaaS) as a development and runtime environment. CF offers a set of tools and services to run applications developed with different programming models.

SAP HANA® in-memory database also provides an application platform called SAP HANA® extended application services (XS Advanced or XSA). XSA is available on-premise and follows development paradigm compatible with CF.

We will cover specific aspects on how to prepare your XSA execution environment for Go-Live phase and make sure your applications run safely with given constraints on memory resources.

A Software Developer is free to choose almost any IDE, programming language and development environment to deploy applications using Сloud Foundry on SAP Cloud Platform. SCP is an excellent solution for companies that plan to make cloud services out of existing software running on-premise.

Before doing architecture development your Software Architect should be familiar with Cloud Foundry execution and deployment tools and features to in order to plan the software migration to the cloud.

XSA runtime platform inherits the same architecture as Cloud Foundry and runs on-premise as part of SAP HANA.

In this blog post we will point out some XSA tweaks while configuring the runtime environment for Microsoft .NET and share our experiences gained in a  customer’s implementation project. This concept is applicable for any available runtime environment including Microsoft .NET.

Preparing computing environment for application deployment to XSA


SAP HANA Platform includes both scale-up and scale-out configuration options that provide different benefits for developers and end-users. Basically scale-out configurations provide fault-tolerance by distributing XSA applications among different servers (nodes).

Each node running XSA can hold different roles like xs_worker and xs_standby that help initiate application failover to the standby node in case the worker node fails.

According to the XSA architecture shown below (Figure. 1), there is one master XSA node running xs_controller and multiple xs_worker nodes. XSA scale-out concept details are described in the SAP HANA Administration guide, in the chapter about “Scale-Out Architecture of SAP HANA XS Advanced Runtime Platform” at http://help.sap.com.


Figure 1 – XS Advanced Scale-Out Configuration

XSA Scale-out is capable of doing load balancing by putting Failover Router (Figure .1) in front of XSA nodes. In order to get proper access to the applications, you should plan and configure one of the routing modes:

◉ Port-based routing,
◉ Hostname-based routing.

Hostname-based routing prepares URLS to access the applications by using common domain name, for instance: https://app1.domain.corp, https://app2.domain.corp, etc.

While doing DNS configuration to satisfy hostname-based routing for XSA, you should maintain at least two records:

◉ domain.corp IN A <ip_address_master_hana_xsa_node>,
◉ *.domain.corp IN CNAME domain.corp (where <ip_address_master_hana_xsa_node> is an IP address of XSA node with xs_controller and xsuaaserver processes running).

All network related configurations for XSA Scale-Out require careful planning according to the information described in the  SAP HANA Administration Guide, chapter “Scaling SAP HANA”. Proper network configuration basically helps isolating internal XSA traffic and the network traffic from business users by placing them on different network interfaces.

Plan workload profile for XSA


In order to plan the sizing of XSA nodes you should refer to the SAP HANA Administration guide, chapter “Platform Sizing in XS Advanced” at help.sap.com. There are two profiles available for XSA depending on usage: PlatformUsage and AppUsage. The second one AppUsage is intended to establish reliable service of running applications for business users and sized upon the number of concurrent application requests (check the table below).

Profile size (Short) Profile size (Long)   Max. concurrent application requests 
S Small 100 
Medium  2000 
Large  8000 
XL  Extra Large   32000 

Develop sizing for XSA Scale-Out


What are the benefits of XSA Scale-Out configuration?

◉ Protect HANA memory consumption by isolating XSA on different physical servers
◉ Protect HANA service availability against memory outages caused by application server
◉ Provide load balancing and failover for applications

An example of configuration steps for XSA will be provided in a subsequent blog posts, here we will only cover the basic principles of landscape layout to set up Multi-Host XSA.

In order to prepare the final memory requirements for XSA scale-out nodes, you should take your application requirements into account based on workload profile per user.

For standard memory requirements for XSA you should refer to the SAP note 2618752 «Resource consumption of SAP HANA extended application services, advanced model».

Select hardware platform based on sizing requirements


Supported hardware requirements for XSA can be found at SAP HANA Hardware Directory.

While planning XSA nodes configuration you may select any available hardware platform to distribute applications workload on physically isolated server.

Design and build custom buildpack for Microsoft .NET Runtime


XSA architecture contains a mechanisms for creation of a custom runtime environment for different languages so-called «buildpacks» that help with application deployment and perform the following scenario:

◉ Deploy compiler or runtime environment (like JVM or .Net) in an isolated folder on the server (possibly download the necessary version according to the application settings)

◉ Check necessary dependencies before compilation (required libraries, frameworks, project structure and other settings)

◉ Compile the app from sources and place the result in an isolated folder on the server

◉ Configure the environment variables (like PATH)

◉ Run the application and provide necessary routing for Web requests together with authentication services running as part of CF or XSA

◉ Provide failover and load balancing for the application

The buildpack itself is a set of scripts and binaries that perform the scenario described above and actually can be customized and configured to satisfy specific constraints like compiler’s version, specific memory constraints for JVM, patch dependencies, etc.

Detailed steps to build custom buildpack can be found here -> help.sap.com (SAP HANA Developer Guide for XS Advanced Model, chapter “Create a PHP Buildpack for XS Advanced”).

In order to get buildpack created for  Microsoft .Net Core Runtime 3.1 on XSA 2.0 (HANA 2.0 SP4), you should implement the following steps:

◉ Download .NET Core Runtime 3.1 from Microsoft website and install it to a local directory on the Linux machine running XSA. (by default .NET Core Runtime is installed in a directory .dotnet for the current user).

◉ Create a gzip archive and place the content of .dotnet folder in there.

◉ Prepare shell scripts: compile, detect, release.

◉ Prepare a final buildpack zip archive and place all artifacts created above (1-3) in there.

◉ Install the buildpack by executing the cli command xs create-buildpack (sap.com->SAP HANA Developer Guide for XS Advanced Model, chapter “Create a PHP Buildpack for XS Advanced”)

All these steps described above you can follow by watching the video below.


You can download the buildpack to run .NET Core Runtime 3.1 for XSA using the link.

Below you can find an example of sources for XSA buildpack to run .NET Core Runtime 3.1.

The core logic of buildpack implemented by scripts : compile, detect, release. The following example is tested using .Net Core Runtime 3.1 on XSA 2.0 (HANA 2.0 SP4).

◉ “compile” script should check preliminary requirements to compile an application from sources, check availability of necessary assemblies, frameworks, etc. The current script unpacks the .net core runtime to the directory prepared by HANA XSA during application deployment.

Source code for the compile script>>

#!/bin/bash
# bin/compile <build-dir> <cache-dir>

BUILD_DIR=$1
BIN_DIR=`dirname $0`
BUILD_PACK_DIR=`dirname ${BIN_DIR}`
echo "Extracting .NET runtime into..."
echo ${BUILD_DIR}
cd ${BUILD_DIR}
tar xzvf ${BIN_DIR}/../runtime/runtime.tgz
echo "Extracting .NET runtime Done."

Current CF version does not use compile script, it was replaced by finalize and supply.

◉ “detect” script should check different configurations options and settings.

Source code for detect script>>

#!/bin/bash
# bin/detect <build-dir>
if [ -f $1/appsettings.json ];
    then exit 0
fi
exit 1

◉ “release” script generates yaml file to run the application.

Source code for release script>>

#!/bin/bash
# bin/release

cat <<EOF
---
config_vars:
addons:
default_process_types:
  web: /bin/bash ./start.sh 

EOF

In total you should have the following folder structure for buildpack:

..
bin  bin\detect
bin\compile
bin\release 
Scripts for buildpack
runtime  runtime\runtime.tgz   Binaries for .net core 3.1 runtime 

How to protect applications running by XSA against memory leaks?


Deploying XSA applications requires careful memory planning and usually application runtime supports all means to prevent from memory leaks.

Which options do exist to minimize system downtime due to  application crash caused by memory leaks ? Linux OS supports mechanism called Control Groups (Cgroups) which represents powerful and convenient way to define memory and cpu resource constraints for any application.

It is important to note that Cgroups is part of the Linux kernel and requires root privileges to be executed. Standard Linux user requires sudo access rights to use Cgroups directly. Also Cgroups are widely implemented by systemd, which provides the easiest way to control resource limits.

Anyway we decided to have implemented Cgroups directly, a working example of script “start.sh” that runs by buildpack for .NET Core 3.1 shown below:

Source code of start.sh script>>

#!/bin/bash


if [ -z "$CGNAME" ]
then
echo "[ERR]Please check CGNAME to place cgroup name limits."
exit 1
else
echo "[OK] CGNAME is set to '$CGNAME'"
fi

if [ -z "$MemoryLimitInMB" ]
then
echo "[ERR] Please check memory limit settings."
exit 1
else
echo "[OK] MemoryLimitInMB is set to '$MemoryLimitInMB'."
fi

if [[ ! $(sudo echo 0) ]]; then echo "[ERR] sudo rights for user '$USER' needed.";exit 1; else echo "[OK] sudo rights for user '$USER' exist"; fi

create_cgroup_for_space(){
if [[ $(sudo mkdir /sys/fs/cgroup/memory/$CGNAME) ]]; then echo "[ERR] can not create cgroup with name '$CGNAME'."; exit 1; else echo "[OK] cgroup '$CGNAME' successfully created."; fi
}

if [[ ! $(sudo ls /sys/fs/cgroup/memory/$CGNAME 2>/dev/null) ]]
then 
echo "[WRN] cgroup '$CGNAME' is not set."
create_cgroup_for_space
else 
echo "[OK] cgroup '$CGNAME' is set." 
fi

if [[ $(sudo sh -c "echo $MemoryLimitInMB > /sys/fs/cgroup/memory/$CGNAME/memory.limit_in_bytes") ]]
then
echo "[ERR] Can not set memory limit for app."
exit 1
else
echo "[OK] Successfully set memory limit to '$MemoryLimitInMB'."
fi

ATTACH=`awk '/^__MS_DOT_NET_RUN__/ {print NR + 1; exit 0; }' $0`

tail -n+$ATTACH $0 > ./run.sh

_PID=`echo $$`

if [[ ! $(sudo sh -c "echo $_PID > /sys/fs/cgroup/memory/$CGNAME/tasks") ]]
then
chmod +x ./run.sh && /bin/bash ./run.sh
else
echo "[ERR] Unable to start app"
exit 1
fi

exit 0

__MS_DOT_NET_RUN__

#!/bin/bash

trap "exit" INT TERM ERR
trap "kill 0" EXIT

export ASPNETCORE_URLS="http://localhost:$VCAP_APP_PORT"
echo "ASPNETCORE_URLS: '$ASPNETCORE_URLS'";
# Getting runnable dll.
runtimeconfigs=($(ls *.runtimeconfig.json));
if [ ${#runtimeconfigs[@]} -gt 1 ] ;
then
    echo "Error! Found '${#runtimeconfigs[@]}' *.runtimeconfig.json files! Expecting only one. Files: '${runtimeconfigs[@]}'.'" 1>&2;
    exit 1;
fi;
if [[ ${#runtimeconfigs[@]} -eq 0 ]] ;
then
    echo "Error! Can't find *.runtimeconfig.json file!" 1>&2;
    exit 1;
fi;
exeFile=$(echo "${runtimeconfigs[0]//.runtimeconfig.json/}")
if [ -f $exeFile ]
then
    echo "starting '$exeFile'...";
    exec "./$exeFile" --urls $ASPNETCORE_URLS;
    exit;
fi
dllFile=$(echo "${runtimeconfigs[0]//.runtimeconfig.json/.dll}")
if [[ !(-e $dllFile) ]] ;
then
    echo "File '$dllFile' is not found." 1>&2;
    exit 1;
fi
exec ./dotnet $dllFile --urls $ASPNETCORE_URLS;

In total the following example uses shell script start.sh to run by buildpack during application deployment when running command – “ xs push -f manifest.yaml”. The script “start.sh” should be placed in the root folder containing .NET application code.

The following example of manifest.yaml given below contains two parameters: “MemoryLimitInMB” and “CGNAME”, where CGNAME stands for Linux control group name and MemoryLimitInMB stands for upper memory limit constraint for the application.

During “push” operation XSA reads manifest.yaml and sets two environment variables $CGNAME and $MemoryLimitInMB for reading by start.sh script.


Basically the start.sh helper script is just an example on how to set up memory usage constraints for your application and minimize system downtime due to application crash caused by memory leaks.

No comments:

Post a Comment