This is my 14th #DaysOfArm article that tracks some of the experiences that I’ve had so far. And just to recap from the first post (here) on June 12 2021.
It’s been just over 2 weeks since the launch of Ampere Arm deployed in Oracle Cloud Infrastructure (OCI). Check this article out to learn more (here). And it’s been about one week since I started looking into the new architecture and deployment, since I started provisioning the VM.Standard.A1.Flex Compute Shape on OCI and since I started migrating a specific application that has many different variations to it to test it all out.
This is my next learning where I’ve deployed successfully openrouteservice – an open-source routing / direction API all deployed on an 4 OCPU with 24 GB of RAM in an Always Free Tier tenancy.
Over the past couple of weeks, I’ve been working in the background with some open-source projects. I published a recent one where I deployed Pelias (here) a open-source geocoding platform.- Pelias (here). The purpose of the geocoding was to simplify the ability to create a set of directions. For this, I opted for openrouteservice (here).
Similar to geocoding, there are numerous existing solutions and APIs out there for routes and directions but they have different terms and conditions that limit their use :- as either a trial; requires a paid account; or restricts the use of the geocode API with their other services including maps or routes.
I wanted to research and create something that aligned with the purpose of Oracle Cloud Always Free Tier.
Here is a high-level detail of how to get this going on OCI.
1. Provision Compute / Network
I provisioned a Virtual Cloud Network (VCN) using the VCN Wizard (noting that there is the service limit of 1 VCN in Always Free Tier).

Note: If you want this service available outside of the network, then add ingress rule for destination port of 8080/TCP
in the network security list.
I provisioned a Compute instance with the following configuration.
- VM.Standard.A1.Flex Compute Shape
- 4 OCPUs with 24 GB of RAM (which is the Always Free Tier service limit)
- ~150GB for the block storage for the book volume
- Oracle Linux 8 (with the latest update)
If you want more assistance, there is a tutorial that you can follow (here).
2. Install Software Requirements
Once you have provisioned and connected to the instance using either ssh or putty, here is a brief list of commands to run as root. I am assuming you just logged into the VM as opc.
sudo su -
dd iflag=direct if=/dev/sda of=/dev/null count=1
echo "1" | sudo tee /sys/class/block/sda/device/rescan
cd /usr/libexec
./oci-growfs
yum update -y && yum install -y git python3-devel
yum install -y automake autoconf
python3 -m pip install --upgrade pip
dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
yum install docker-ce
service docker start
systemctl enable docker
useradd giscience
usermod -a -G docker giscience
firewall-cmd --add-port 8080/tcp --permanent --zone=public
firewall-cmd --add-port 443/tcp --permanent --zone=public
firewall-cmd --add-port 80/tcp --permanent --zone=public
firewall-cmd --reload
In summary, this is what this does.
- Expands the default boot volume size to the ~150 GB that has been provisioned.
- Updates the packages and installs git and additional python3 packages.
- Also install automake and autoconf (for building osmctools).
- Upgrades pip (python installer).
- Install docker (community edition) repository and then docker itself.
- Configure docker as a service and start docker.
- Create a user called giscience and add the docker group to that user.
- Update the firewall for openrouteservice.
3. Download openrouteservice (from github)
Once this has been executed as root, then the following can be executed as giscience (the application user). This will install openrouteservice build the docker images and store them locally. I am assuming you just logged into the VM as opc.
sudo su - giscience
mkdir repos
cd repos
git clone https://github.com/GIScience/openrouteservice && cd openrouteservice
cp openrouteservice/src/main/resources/app.config.sample openrouteservice/src/main/resources/app.config
docker build -t openrouteservice/openrouteservice:latest .
This is relatively easy part of the process to build the docker image to run.
(Updated 3rd August)
In addition, docker-compose is used to manage the whole stack. This needs to be installed as well.
python3 -m pip install --user docker-compose
4. Start Importing Datasets
There are a few setup tasks required. I am assuming you still logged into the VM as giscience.
4.1 Setup the environment
This is once-off setup to configure the environment to where the maps will be stored and referenced as well updating the Java memory configuration.
# create maps directory for the maps to download and process
cd ~/repos/openrouteservice/docker
mkdir maps
# update the docker-compose file
sed -i 's/\#- .\/your_osm.pbf:\/ors-core\/data\/osm_file.pbf/- \/home\/giscience\/repos\/openrouteservice\/docker\/maps\/osm_file.pbf:\/ors-core\/data\/osm_file.pbf/g' docker-compose.yml
sed -i 's/-Xms1g -Xmx2g/-Xms2g -Xmx16g/g' docker-compose.yml
4.2 Download maps
OpenStreetMaps (here) data is used to create the graphs required. The source of this data that I used for this is Geofabrik (here). The specific type that I used was the OSM PBF format. In addition, I would suggest being as specific as possible with the maps that you want to use to ensure you have sufficient block volume storage for the graph data generated.
cd ~/repos/openrouteservice/docker/maps
wget https://download.geofabrik.de/australia-oceania-latest.osm.pbf
wget https://download.geofabrik.de/asia/malaysia-singapore-brunei-latest.osm.pbf
If you are only needing a single region, rename it to be osm_file.pbf aligned to the docker-compose file updates.
4.3 Merge maps (if required)
The service supports a single OSM PBF file. If you have multiple data files downloaded, you will need to merge the files into a single file. I found osmctools that enabled this process.
# clone the osmctools repository
cd ~/repos
git clone https://gitlab.com/osm-c-tools/osmctools
# build the osmctools
cd osmctools
autoreconf --install
./configure
make all
# copy these tools locally so they can be referred to
cd src/
cp osmconvert osmfilter osmupdate ~/.local/bin
There is a series of conversion steps to merge these files. Here is an example where I merged two datasets – one from Australia and one from Singapore.
cd ~/repos/openrouteservice/docker/maps
osmconvert australia-oceania-latest.osm.pbf -o=australia-oceania-latest.o5m
osmconvert malaysia-singapore-brunei-latest.osm.pbf -o=malaysia-singapore-brunei-latest.o5m
osmconvert `ls *.o5m` -o=osm_file.o5m
osmconvert osm_file.o5m -o=osm_file.pbf
You can remove the original and the o5m format files if you are requiring additional storage.
4.3 Start the service
The technology stack is relatively simple with a single docker container. The following command starts up the instance.
# cd into the docker directory
cd ~/repos/openrouteservice/docker
docker-compose up -d
The docker container ors-app
creates a log file that can be accessed.
docker logs -f ors-app
There are additional directories generated by the container that are created and owned by the root
user. The ownership of these files and directories can be changed to the application user.
The lifecycle of the ors-app
has two distinct phases described below.
4.3.1 Startup with Maven
The service is deployed to Tomcat and uses Apache Maven for managing the dependencies. Upon the first startup, the container will initialise the Maven repository which may take minutes to complete. You will likely to see the following warning log entries. Through my observations, I have not seen any impact due to these warnings.
### Package openrouteservice and deploy to Tomcat ###
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by com.google.inject.internal.cglib.core.$ReflectUtils$1 (file:/usr/share/maven/lib/guice.jar) to method java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain)
WARNING: Please consider reporting this to the maintainers of com.google.inject.internal.cglib.core.$ReflectUtils$1
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release
4.3.2 Graph Processing
The service processes the OSM data to create the graph data required. This can be a complex process and does take time to do. A couple of things that you can monitor.
Watch the disk utilisation growth
The graph data is processed and stored on disk in the elevation_cache and graph directories. I recommend monitoring the disk utilisation.
Watch the process utilisation
This process is multi-threaded and hence it is typically to see the Java process running at 100%+.
Process Recovery
This processing may fail (I’ve seen a couple of scenarios where the processing stopped). This is ok. You can fix the issue and restart the process.
cd ~/repos/openrouteservice/docker
docker-compose down
docker-compose up -d
I had issues with corrupt zip files (which I had to delete first). Also I had issues with OutOfMemory errors (which a restart helps). I have had times where I had restarted the process multiple times before it finished the processing.
Watch the log files
These log messages will indicate that the processing is complete and the service is ready.
10 Jul 00:38:56 INFO [routing.RoutingProfileManager] - ========================================================================
10 Jul 00:38:56 INFO [routing.RoutingProfileManager] - ====> Recycling garbage...
10 Jul 00:38:56 INFO [routing.RoutingProfileManager] - Before: Total - 15.84 GB, Free - 8.34 GB, Max: 16 GB, Used - 7.50 GB
10 Jul 00:38:57 INFO [routing.RoutingProfileManager] - After: Total - 15.84 GB, Free - 10.98 GB, Max: 16 GB, Used - 4.86 GB
10 Jul 00:38:57 INFO [routing.RoutingProfileManager] - ========================================================================
10 Jul 00:38:57 INFO [routing.RoutingProfileManager] - ====> Memory usage by profiles:
10 Jul 00:38:57 INFO [routing.RoutingProfileManager] - [1] 1.92 GB (39.4%)
10 Jul 00:38:57 INFO [routing.RoutingProfileManager] - [2] 1.17 GB (24.1%)
10 Jul 00:38:57 INFO [routing.RoutingProfileManager] - Total: 3.09 GB (63.5%)
10 Jul 00:38:57 INFO [routing.RoutingProfileManager] - ========================================================================
4.4 Service Status
There are two URLs that are available to help with the service status. These can also give you an indicator of the startup process.
This URL pattern shows the status or the service as well as some metadata.
Note: The IP address here is the public IP address of the Compute instance.
http://<ipaddress>:8080/ors/status
This URL pattern shows the health of the service. This is useful for a health status URL for load-balancing.
http://<ipaddress>:8080/ors/health
5. Test the APIs
From here, we can test it out … The base URL of the service is:
http://<ipaddress>:8080/ors
and much of the documentation of the APIs (including the resource path) can be appended to this base URL including the version (which is v2). To interactively learn more about the APIs, you can try out the Open Route Service API Playgound (here).
To give it a test, here is a sample format for getting the directions for a start point and an end point.
http://<ipaddress>:8080/ors/v2/directions/driving-car?start=<long>,<lat>&end=<long>,<lat>
eg.
http://<ipaddress>:8080/ors/v2/directions/driving-car?start=-0.134994,51.497613&end=-0.138234,51.500472
and the output looks something like this:
{"type":"FeatureCollection","features":[{"bbox":[-0.140865,51.497495,-0.134987,51.501275],"type":"Feature","properties":{"segments":[{"distance":861.5,"duration":142.7,"steps":[{"distance":46.9,"duration":5.6,"type":11,"instruction":"Head west on Victoria Street, A302","name":"Victoria Street, A302","way_points":[0,2]},{"distance":487.1,"duration":81.1,"type":1,"instruction":"Turn right onto Buckingham Gate, B323","name":"Buckingham Gate, B323","way_points":[2,23]},{"distance":111.1,"duration":20.4,"type":5,"instruction":"Turn slight right onto Buckingham Palace Road","name":"Buckingham Palace Road","way_points":[23,28]},{"distance":153.6,"duration":25.6,"type":13,"instruction":"Keep right","name":"-","way_points":[28,36]},{"distance":62.8,"duration":9.9,"type":0,"instruction":"Turn left onto Birdcage Walk","name":"Birdcage Walk","way_points":[36,39]},{"distance":0.0,"duration":0.0,"type":10,"instruction":"Arrive at Birdcage Walk, on the left","name":"-","way_points":[39,39]}]}],"summary":{"distance":861.5,"duration":142.7},"way_points":[0,39]},"geometry":{"coordinates":[[-0.134987,51.497596],[-0.135432,51.49752],[-0.135633,51.497495],[-0.13567,51.497594],[-0.135801,51.49785],[-0.135905,51.497976],[-0.136017,51.498107],[-0.136164,51.498237],[-0.136351,51.498361],[-0.136492,51.498438],[-0.136602,51.498489],[-0.13705,51.498675],[-0.13725,51.498758],[-0.137345,51.498789],[-0.13787,51.498992],[-0.137976,51.499028],[-0.138134,51.499081],[-0.138401,51.49916],[-0.138654,51.499235],[-0.139485,51.499513],[-0.14018,51.499736],[-0.140343,51.49981],[-0.140797,51.500099],[-0.140865,51.500193],[-0.140829,51.500424],[-0.140555,51.500945],[-0.140538,51.500979],[-0.140465,51.501128],[-0.140452,51.501154],[-0.140352,51.501211],[-0.140254,51.501263],[-0.140072,51.501275],[-0.139998,51.501265],[-0.13962,51.500929],[-0.139274,51.500588],[-0.139153,51.500409],[-0.139125,51.500366],[-0.138985,51.500383],[-0.138809,51.500398],[-0.138231,51.500459]],"type":"LineString"}}],"bbox":[-0.140865,51.497495,-0.134987,51.501275],"metadata":{"attribution":"openrouteservice.org, OpenStreetMap contributors","service":"routing","timestamp":1626734820551,"query":{"coordinates":[[-0.134994,51.497613],[-0.138234,51.500472]],"profile":"driving-car","format":"json"},"engine":{"version":"6.6.1","build_date":"2021-07-18T19:17:07Z","graph_date":"2021-07-18T20:09:17Z"}}}
It is now ready !!!
There are a couple of things that I’m continuing to work on.
- Embedding this into other solutions like Oracle APEX.
- Creating an Infrastructure as Code implementation for this stack.

This would not be possible for the countless people and organisations that contribute to these projects. Here is a list of them based upon the specific projects that I’ve forked or used. If I’ve missed someone, please let me know.
- The team from openrouteservice (here).
- The team from OpenStreetMap (here) where some of the data is sourced.
- The team from Geofabrik (here) where some of the data is sourced.
- The team from osmctools (here) which helps merging the OSM PBF files.
It is important to attribute everyone in this process.
If you want to try this out yourself or work on your own application, sign-up (here) for the free Oracle Cloud Trial. I’d be interested to hear your experiences and learn from others as well. Leave a comment or contact me at jason.lowe@oracle.com if you want to collaborate.
There’s plenty of work to make this more achievable for everyone. And hence sharing this knowledge is the reason why I’m writing this series – #XDaysOfArm. I’ll keep documenting as long as I keep learning.
It looks like there have been some developments since this post was written, and the guide no longer works for me. It would appear that everything installs, but when I start the docker image I get some errors, and I’m not actually to visit anything on the front end.
I would appreciate if you’re able to update this guide for 2022.
Thanks!
LikeLike
Yes. Jason. There has been changes in pelias that has moved on from last year and seeing some of the native support for arm64 as well (hence not needing to do some of the custom work). I’m in the process of revisiting this for a different purpose and will update with the learning.
LikeLike