Thinking Anew

ARM Your APEX Instance

Embedded ARM chip

In September 2020, Oracle first announced that it was working with Ampere Computing to provide ARM-based chips for its Compute instance offering on the Oracle Cloud Infrastructure. They have delivered!

ARM chips are found in many low-powered devices like smartphones, single-board computers, and IoT devices. So, if you are an Oracle Application Express (APEX) developer, you might be wondering, “What’s in it for me?” Well, remember we talked about running a customer-managed Oracle REST Data Services (ORDS) instance for an Oracle Autonomous Database?

The OCI isn’t the first platform to offer ARM-based compute resources. You will find a similar offering from AWS with their Graviton processors. Instead of custom-designing a processor, Oracle opted to use the Ampere Altra Processor that has 80 cores running at a maximum frequency of 3.3 Ghz. Check out AnandTech’s review of the chip.

If you are interested to read about the performance comparisons between the ARM and x86 architectures, please check out this report. What I understand is that for hosting web applications that are typically single threaded, ARM chips offer a better cost-to-performance ratio, not to mention, lower power consumption. Each processing core is also generally cheaper than its competitors, thus allowing cloud vendors to provision them at cheaper rates. We saw this happening when Oracle first launched the AMD EPYC-based compute shapes that were cheaper than Intel’s at that time.

Let’s Get Started

As with other processor types, OCI customers can opt to provision virtual machines or bare metal machines:

  • VM.Standard.A1.Flex. Like the AMD EPYC Flex shapes, users can create instances with a flexible number of OCPUs and memory. Servers can be provisioned with 1 to 80 OCPUs, and 1 to 512 GB memory.
  • BM.Standard.A1.160. Only one shape for bare metal machines is available at this time. It comes with 160 OCPUs and 1 TB of memory.

Oracle has also provided a generous Oracle Cloud Free Tier allotment of Ampere compute instance. Users can provision an Always Free compute instance with up to 4 OCPUs and 24 GB of memory (see the press release). The Always Free block volume allotment has also increased from 100 to 200 GB. Yes, there are no charges and no end dates for these resources. I will use this to host the customer-managed ORDS instance. Beyond that, according to the cost estimator, each additional OCPU will cost you only $10 per month, and $1 for every gigabyte of memory!

Note. These A1 cores are single-threaded. In x86-based shapes, 1 OCPU has 2 virtual CPUs.

Create an Ampere-shaped Compute instance.

The downside of moving to an ARM chip is the availability of software support. For example, though you can install the Oracle Instant Client on an ARM system, Oracle has not released AArch64 database binaries. Fortunately for us, there already exists a Java port for this architecture, so there should be no issues deploying ORDS or running it standalone. However, at the time of writing, the ORDS package is not available to install through the Oracle Yum repositories. I will instead deploy ORDS on a Tomcat server using Podman. The official Docker image that I will use can be found here. Take note that you will have to ensure that you deploy a version of ORDS that’s compatible to what the ADB is using.

Use Database Actions to check version of ORDS that the ADB is running.

After provisioning the Oracle Enterprise Linux 8 server, login as the user opc and install the following software:

  • OCI Command Line Interface (CLI)
  • Podman
  • [Optional] Support for emulating Docker CLI using podman
1
sudo dnf install python36-oci-cli podman podman-docker

Create the base directory where we will deploy our files to.

1
2
BASE_PATH=/opt/podman/ords && \
mkdir -p $BASE_PATH

Stage the following installer files in the directory $BASE_PATH/files:

  • apex_20.2.zip
  • p32006852_2020_Generic.zip
  • ords-21.1.1.116.2032.zip

Note: At the time of writing, the ADB is still running APEX 20.2 with the latest Patch Set Bundle. To download the patch, you will require a qualified support contract.

Create the following Dockerfile in the directory specified by the environment variable BASE_PATH:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
FROM tomcat:9.0-jdk8-openjdk-slim

ARG INSTALL_FILES_DIR=/tmp
ARG ORDS_HOME=/opt/oracle/ords
ARG ORDS_CONFIG_DIR=$ORDS_HOME/conf
ARG ORDS_INSTALL_FILE=ords-21.1.1.116.2032.zip

COPY files/$ORDS_INSTALL_FILE $INSTALL_FILES_DIR/

RUN apt update -y && \
apt install -y unzip && \
mkdir -p $ORDS_HOME $ORDS_CONFIG_DIR && \
unzip $INSTALL_FILES_DIR/$ORDS_INSTALL_FILE -d $ORDS_HOME && \
java -jar $ORDS_HOME/ords.war configdir $ORDS_CONFIG_DIR && \
cp $ORDS_HOME/ords.war /usr/local/tomcat/webapps/ && \
sed -i -r 's/Connector port=\"8080\" protocol=\"HTTP\/1\.1\"/& scheme=\"https\"/' \
/usr/local/tomcat/conf/server.xml && \
rm -f $INSTALL_FILES_DIR/$ORDS_INSTALL_FILE && \
apt clean -y

Alternatively, run the following Bash script to create the Dockerfile:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cd $BASE_PATH && \
cat << EOF > Dockerfile
FROM tomcat:9.0-jdk8-openjdk-slim

ARG INSTALL_FILES_DIR=/tmp
ARG ORDS_HOME=/opt/oracle/ords
ARG ORDS_CONFIG_DIR=\$ORDS_HOME/conf
ARG ORDS_INSTALL_FILE=ords-21.1.1.116.2032.zip

COPY files/\$ORDS_INSTALL_FILE \$INSTALL_FILES_DIR/

RUN apt update -y && \\
apt install -y unzip && \\
mkdir -p \$ORDS_HOME \$ORDS_CONFIG_DIR && \\
unzip \$INSTALL_FILES_DIR/\$ORDS_INSTALL_FILE -d \$ORDS_HOME && \\
java -jar \$ORDS_HOME/ords.war configdir \$ORDS_CONFIG_DIR && \\
cp \$ORDS_HOME/ords.war /usr/local/tomcat/webapps/ && \\
sed -i -r 's/Connector port=\\"8080\\" protocol=\\"HTTP\/1\\.1\\"/& scheme=\\"https\\"/' \\
/usr/local/tomcat/conf/server.xml && \\
rm -f \$INSTALL_FILES_DIR/\$ORDS_INSTALL_FILE && \\
apt clean -y
EOF

Build the container image.

1
2
cd $BASE_PATH && \
podman build -t ords .

Set the following environment variables based on your tenancy, data region, and ADB:

1
2
3
4
5
6
7
8
ADB_OCID=ocid1.autonomousdatabase.oc1.phx.abyh...4pggq
ADB_NAME=myadb
ORDS_USER=ORDS_PUBLIC_USER2
ORDS_PASSWORD="avoidsimplepasswords"
WALLET_PASSWORD="atalltimes"
SERVICE_NAME=${ADB_NAME}_low
WALLET_FILE_PATH=/tmp/wallet_${ADB_NAME}.zip
ORDS_CONFIG_DIR=$BASE_PATH/conf

Use the OCI CLI to generate the required ADB wallet, and then create the base64-encoded string needed for configuring ORDS to access the database.

1
2
3
4
5
6
7
export OCI_CLI_AUTH=instance_principal && \
oci db autonomous-database generate-wallet \
--autonomous-database-id $ADB_OCID \
--file $WALLET_FILE_PATH \
--password WALLET_PASSWORD && \
WALLET_BASE64=`base64 -w 0 $WALLET_FILE_PATH` && \
rm -f ${WALLET_FILE_PATH}

Create the database user named in the environment variable ORDS_USER and asssign the necessary privileges. Refer to the section User Accounts in the previous post if you require help. Then create the ORDS configuration files:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
mkdir -p $ORDS_CONFIG_DIR/ords/conf && \
cat << EOF > $ORDS_CONFIG_DIR/ords/conf/apex_pu.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="db.username">$ORDS_USER</entry>
<entry key="db.password">!$ORDS_PASSWORD</entry>
<entry key="db.wallet.zip.service">$SERVICE_NAME</entry>
<entry key="db.wallet.zip"><![CDATA[$WALLET_BASE64]]></entry>
</properties>
EOF

cat << EOF > $ORDS_CONFIG_DIR/ords/defaults.xml
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<entry key="plsql.gateway.enabled">true</entry>
<entry key="jdbc.InitialLimit">5</entry>
<entry key="jdbc.MaxLimit">10</entry>
</properties>
EOF

Prepare to stage the APEX images files by first setting the following environment variables:

1
2
3
4
INSTALL_FILES_DIR=$BASE_DIR/files
APEX_INSTALL_FILE=apex_20.2.zip
APEX_VERSION=20.2.0.00.20
APEX_IMAGES_DIR=$BASE_PATH/apex_images

Execute the snippet below to extract the necessary files from the APEX installation file and deploy them to the Tomcat container.

1
2
3
4
5
mkdir -p $APEX_IMAGES_DIR && \
touch $APEX_IMAGES_DIR/index.html && \
unzip $INSTALL_FILES_DIR/$APEX_INSTALL_FILE "apex/images/*" -d /tmp/ && \
mv /tmp/apex/images $APEX_IMAGES_DIR/$APEX_VERSION && \
rm -rf /tmp/apex

If your instance is running a version of APEX that has a patch available, then be sure to apply it. Again, you will need access to Oracle Support with the rights to download patches. Set the environment variables:

1
2
PATCH_FILE=p32006852_2020_Generic.zip
PATCH_NUMBER=32006852

Then run the snippet below to deploy the changes:

1
2
3
4
mkdir -p /tmp/$APEX_VERSION && \
unzip /tmp/$PATCH_FILE "$PATCH_NUMBER/images/*" -d /tmp/$APEX_VERSION && \
yes | cp -R /tmp/$APEX_VERSION/$PATCH_NUMBER/images/* $APEX_IMAGES_DIR/$APEX_VERSION && \
rm -rf /tmp/$APEX_VERSION

Create a utility run.sh script to run the container. The script allows one argument to set the port number to map to, and also used to set the container’s name.

1
2
3
4
5
6
7
8
9
10
11
12
13
cd $BASE_PATH && \
cat << EOF > run.sh
#!/usr/bin/env bash

PORT_NUMBER=\${1:-8080}

podman run -d --name=ords_port_\${PORT_NUMBER} \\
-p \$PORT_NUMBER:8080 \\
-v \$PWD/conf:/opt/oracle/ords/conf:z \\
-v $APEX_IMAGES_DIR:/usr/local/tomcat/webapps/i \\
--restart unless-stopped \\
ords
EOF

Finally, since we are running this as a rootless container, the firewall rules must be updated to allow communication on port 8080. Execute the following commands:

1
2
sudo firewall-cmd --zone=public --add-port 8080/tcp --permanent && \
sudo firewall-cmd --reload

Run the container that’s listening on port 8080.

1
cd $BASE_PATH && bash run.sh 8080

Finally, setup a load balancer for the customer-managed ORDS instance. This was discussed in an earlier post.

Bonus Task

If you are also interested in testing out some “redundancy”, you might like to run a second container:

1
2
3
4
PORT_NUMBER=8081 && \
sudo firewall-cmd --zone=public --add-port $PORT_NUMBER/tcp --permanent && \
sudo firewall-cmd --reload && \
cd $BASE_PATH && bash run.sh $PORT_NUMBER

And then add this as a backend server that’s listening on port 8081 to the set. Just be really careful about exceeding the maximum number of concurrent database sessions if this is an Always Free ADB.

Add a backend server to simulate redundancy.

To be clear, this is really for experimentation and simulation use only. As a best practice, the second container should be deployed on a separate host, preferably in a different availability domain.

Summary

This is a great addition to the family of OCI resources that Oracle provides. Besides deploying customer-managed ORDS to complement the Autonomous Database, APEX developers can also use them to host other workloads to supercharge web applications. For example, running a WebSocket server using Socket.IO to display realtime updates, or hosting a REST service using Spring Boot. A choice of provisioning low-cost and highly-efficient servers, and the expanded Always Free limits. What’s there not to love?