By Franck Pachot

.
If you want to run an Oracle Database in the Microsoft Azure cloud, you will install it yourself on a VM. And then, you can expect the same as when you install it in your premises, in a virtual environment. Except that Oracle makes it two times more expensive by accepting to license the processor metric on vCPUs at the condition that the Intel core factor is not applied. And in addition to that there is a weird limitation on multitenant. I create a VM with the 19c image provided by Azure. I’m not sure there’s a big advantage to have this image. You have an Oracle Linux OS, with all prerequisites (which is like having installed the preinstall rpm), and a 19.3 Oracle Home (which is like downloading it, unzip and runInstaller). But you will still have to apply the latest Release Update and create a database.

Here is what I’ve run after the creation of the VM (can be done through CustomData)


# download latest OPatch and RU (requires the URLs in OPatch and ReleaseUpdate tags)
(
mkdir -p /var/tmp/OPatch && cd /var/tmp/OPatch
wget -qc $( curl 'http://169.254.169.254/metadata/instance/compute/tags?api-version=2018-02-01&format=text' -H Metadata:true -s | awk -F";" '/OPatch:/{sub(/(^|;).*OPatch:/,"");print $1}')
mkdir -p /var/tmp/RU && cd /var/tmp/RU
wget -qc $(curl 'http://169.254.169.254/metadata/instance/compute/tags?api-version=2018-02-01&format=text' -H Metadata:true -s | awk -F";" '/ReleaseUpdate:/{sub(/(^|;).*ReleaseUpdate:/,"");print $1}')
unzip -qo p*_*_Linux-x86-64.zip
rm -f p*_*_Linux-x86-64.zip
chown -R oracle:oinstall /var/tmp/OPatch /var/tmp/RU
)
# fix the missing sar directory
mkdir -p /var/log/sa

I set the OPatch and ReleaseUpdate tags for the VM with the URL of the binaries in the Object Store. I download it there. And also fix the missing /var/log/sa.


# get Oracle Home from inventory
ORACLE_HOME=$(sudo xmllint --xpath 'string(/INVENTORY/HOME_LIST[1]/HOME/@LOC)' $(awk -F= '/^inventory_loc/{print $2}' /etc/oraInst.loc)/ContentsXML/inventory.xml)
# create autostart service
sudo tee /etc/systemd/system/oracledb.service <<CAT
[Unit]
Description=Oracle Database start/stop
Before=shutdown.target
After=network-online.target
[Service]
Type=idle
#LimitMEMLOCK=infinity
#LimitNOFILE=65535
#EnvironmentFile=/etc/oracle.env
User=oracle
Group=oinstall
ExecStart=$ORACLE_HOME/bin/dbstart $ORACLE_HOME
ExecStop=$ORACLE_HOME/bin/dbshut $ORACLE_HOME
RemainAfterExit=yes
Restart=no
[Install]
WantedBy=multi-user.target
CAT
sudo systemctl enable oracledb

This will automatically stop and start the database flagged with ‘Y’ in /etc/oratab


# Now connecting as Oracle user
sudo su - oracle <<'SU'
# set read only Oracle Home
roohctl -enable
# update to latest RU
unzip -qo -d $ORACLE_HOME /var/tmp/OPatch/p*_*_Linux-x86-64.zip
$ORACLE_HOME/OPatch/opatch lspatches
cd /var/tmp/RU/*
$ORACLE_HOME/OPatch/opatch apply --silent
cd ; rm -rf /var/tmp/RU/*
SU

I set the Oracle Home in “ROOH” mode where the configuration files are separate from the binaries. And apply the downloaded Release Update.


sudo su - oracle <<'SU'
# put the passwords in the wallet
mkdir -p       /u01/app/oracle/dbc_wallet
 yes "W4Lle7p422w0RD" | mkstore -wrl  /u01/app/oracle/dbc_wallet -create
for user in sys system pdbAdmin ; do
 yes "W4Lle7p422w0RD" | mkstore -wrl /u01/app/oracle/dbc_wallet -createEntry oracle.dbsecurity.${user}Password "Oracle19c$RANDOM"
done
# delete existing databases (just in case because we will re-create)
for i in $(awk -F: '/^.*:/{print $1}' /etc/oratab | sort -r) ; do $ORACLE_HOME/bin/dbca -silent -deleteDatabase -sourceDB $i -forceArchiveLogDeletion -useWalletForDBCredentials true -dbCredentialsWalletLocation /u01/app/oracle/dbc_wallet ; done ; rm -rf /u01/ora*
# create the DB
cdb_name=CDB
pdb_name=PDB
unique_name=${cdb_name}_$( curl 'http://169.254.169.254/metadata/instance/compute/name?api-version=2018-02-01&format=text' -H Metadata:true -s )
grep ^CDB1: /etc/oratab || $ORACLE_HOME/bin/dbca -silent \
 -createDatabase -gdbName CDB1 -sid CDB1 -createAsContainerDatabase true -numberOfPdbs 1 -pdbName PDB1 \
 -useWalletForDBCredentials true -dbCredentialsWalletLocation /u01/app/oracle/dbc_wallet \
 -datafileDestination /u01/oradata -useOMF true -storageType FS \
 -recoveryAreaDestination /u01/orareco -recoveryAreaSize 4096 -enableArchive true \
 -memoryMgmtType AUTO_SGA -totalMemory 4096 \
 -createListener LISTENER:1521 -emConfiguration EMEXPRESS -emExpressPort 443 \
 -templateName General_Purpose.dbc -databaseType OLTP -sampleSchema true -redoLogFileSize 100 \
 -initParams db_unique_name=CDB1,user_large_page=AUTO,shared_pool_size=600M
# old habit to run datapatch
$ORACLE_HOME/OPatch/datapatch
# enable database flashback and block change tracking.
rman target / <<<'set echo on; alter database flashback on; alter database enable block change tracking;'
# create a service for the application
sqlplus / as sysdba <'ORCL',network_name=>'ORCL');
exec dbms_service.start_service(service_name=>'ORCL');
alter pluggable database save state;
SQL
SU

This is the creation of the database, CDB1 here with a PDB1 pluggable database.


sudo su - oracle <<'SU'
# define the crontab to backup locally (recovery area)
t=0 ;for i in $(awk -F: '/^.*:/{print $1}' /etc/oratab | sort -r) ; do t=$(($t+1))
cat <<CRONTAB
30 $t * * 6   ( PATH="/usr/local/bin:$PATH" ; . oraenv -s << /tmp/crontab_backup.log 2>&1 <<<'set echo on; backup as compressed backupset incremental level 0 database tag "ORACLE_CRONTAB_WEEKLY";'
05 $t * * 1-5 ( PATH="/usr/local/bin:$PATH" ; . oraenv -s << /tmp/crontab_backup.log 2>&1 <<<'set echo on; backup incremental as compressed backupset level 1 database tag "ORACLE_CRONTAB_DAILY";'
${t}0 * * * * ( PATH="/usr/local/bin:$PATH" ; . oraenv -s << /tmp/crontab_backup.log 2>&1 <<<'set echo on; backup as compressed backupset archivelog all tag "ORACLE_CRONTAB_HOURLY";'
CRONTAB
done | crontab
SU

This sets a very basic backup strategy into the recovery area: full on week-end, incremental on nights, archivelog every hour. To be customized (maybe backup the recovery area to NFS – see Tim Gorman answer on AskTOM) and monitored (I like to run a “report need backup” independantly, and, of course, monitor the non-reclaimable v$recovery_area_usage).

Multitenant

I blogged about a strange limitation of multitenant when running on AWS and here I’m showing that the same exists on Azure and even with the latest Release Update. Let’s be clear, I’ve no idea if this limitation is intentional or not (bug or feature ;)) and why it has not been removed yet if considered as a bug. But if you Bring You Own License and have paid for the multitenant option, you probably want to create several pluggable databases rather than several instances in a VM.


[oracle@o19c ~]$ sql / as sysdba

SQLcl: Release 19.1 Production on Mon Feb 22 07:51:23 2021

Copyright (c) 1982, 2021, Oracle.  All rights reserved.

Connected to:
Oracle Database 19c Enterprise Edition Release 19.0.0.0.0 - Production
Version 19.10.0.0.0


SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           READ WRITE NO
SQL>

SQL> show parameter max_pdbs

NAME     TYPE    VALUE
-------- ------- -----
max_pdbs integer 5

I have one PDB that I have created at CDB creation time. And MAX_PDBS is set to five. That reminds me what I have seen on AWS. Let’s see how many PDBs I can create.


SQL> create pluggable database PDB2 from PDB1;

Pluggable database created.

SQL> c/2/3
  1* create pluggable database PDB3 from PDB1;
SQL> /

Pluggable database created.

SQL> c/3/4
  1* create pluggable database PDB4 from PDB1;
SQL> /

create pluggable database PDB4 from PDB1
                          *
ERROR at line 1:
ORA-65010: maximum number of pluggable databases created

SQL> show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB1                           READ WRITE NO
         4 PDB2                           MOUNTED
         5 PDB3                           MOUNTED
SQL>

I cannot reach this MAX_PDBS (it is supposed to be the number of user created PDBS, not the number of containers). This is definitely not the correct behaviour. It looks like a MAX_PDBS=3 as when you don’t have the multitenant option.


SQL> oradebug setmypid
Statement processed.
SQL> oradebug call kscs_cloudprov_ut
kscs_cloudprov_ut: Detecting cloud provider...
This is a non oracle cloud database instance
This is a Azure instance
CloudDB(10), CloudDetect(10), CloudQuery(ff)
Function returned BF5A07ED
SQL>

The Oracle binaries have a function that detects on which cloud provider they run (“This is a Azure instance”) because some features are limited when you are not on the Oracle Cloud (“This is a non oracle cloud database instance”). But the binaries are the same. The detection is done through each cloud provider metadata API.


[oracle@cloud ~]$ strings $ORACLE_HOME/bin/oracle | grep "http://169.254.169.254"
http://169.254.169.254/computeMetadata/v1/instance/id
http://169.254.169.254/latest/meta-data/services/domain
http://169.254.169.254/opc/v1/instance/id
http://169.254.169.254/metadata/instance/compute/vmId?api-version=2017-04-02&format=text

The first one is valid in GCP, the second one AWS, third one OCI, and last one looks like Azure. Oracle binaries have a directory of the main cloud providers metadata endpoint to detect where it runs.


[azureuser@o19c ~]$ sudo iptables -A OUTPUT -d 169.254.169.254  -j REJECT
[azureuser@o19c ~]$ curl 'http://169.254.169.254/metadata/instance/compute/vmId?api-version=2018-02-01&format=text' -H Metadata:true -s
[azureuser@o19c ~]$

I’ve blocked this endpoint.


SQL> create pluggable database PDB4 from PDB1;

Pluggable database created.

and that works, I can create my additional PDB (note that I had to restart the instance as the detection is done at startup time)


SQL> oradebug setmypid
Statement processed.
SQL> oradebug call kscs_cloudprov_ut
kscs_cloudprov_ut: Detecting cloud provider...
This is an on-premise database instance
CloudDB(0), CloudDetect(0), CloudQuery(ff)
Function returned E31011D7

You see, I’m not on-premises as no cloud metadata is available…


[azureuser@o19c ~]$ sudo iptables -D OUTPUT -d 169.254.169.254  -j REJECT
[azureuser@o19c ~]$ curl 'http://169.254.169.254/metadata/instance/compute/vmId?api-version=2018-02-01&format=text' -H Metadata:true -s
4967bff7-591f-4583-bd57-5eb0969b9ff0[azureuser@o19c ~]$

I re-open the metadata by deleting my iptables rule. Because a Cloud VM has some cloud provider software running there which require this.

Workaround


SQL> alter system set "_cdb_disable_pdb_limit"=true scope=spfile;
System altered.

SQL> startup force
ORACLE instance started.

Total System Global Area 3439327576 bytes
Fixed Size                  9140568 bytes
Variable Size             704643072 bytes
Database Buffers         2717908992 bytes
Redo Buffers                7634944 bytes
Database mounted.
Database opened.

SQL> oradebug call kscs_cloudprov_ut
kscs_cloudprov_ut: Detecting cloud provider...
This is a non oracle cloud database instance
This is a Azure instance
CloudDB(10), CloudDetect(10), CloudQuery(ff)
Function returned BF5A07ED
SQL>

I’ve disabled this PDB limit by setting “_cdb_disable_pdb_limit”=true and check again that the instance detects that I’m running on Azure.


SQL> create pluggable database PDB5 from PDB1;
Pluggable database created.

SQL> c/5/6
  1* create pluggable database PDB6 from PDB1
SQL> /
Pluggable database created.

Ok, now I was able to create an additional PDB. But do you remember that MAX_PDBS was 5? How was I able to create a 6th one?


SQL> show parameter max_pdbs

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
max_pdbs                             integer     4098
SQL>

When disabling the limit, the MAX_PDBS was raised. So be careful because you are not allowed to create more than 252 PDBs (4096 on Oracle platforms and never 4098 – I tried it)


SQL> alter system set max_pdbs=6;
System altered.

SQL> show parameter max_pdbs

NAME                                 TYPE        VALUE
------------------------------------ ----------- ------------------------------
max_pdbs                             integer     6
SQL> create pluggable database PDB7 from PDB1;
create pluggable database PDB7 from PDB1
*
ERROR at line 1:
ORA-65010: maximum number of pluggable databases created

Fortunately, I can set the MAX_PDBS limit now that “_cdb_disable_pdb_limit”=true.

Why does it make sense?

Because when you bring your own license you are already penalized for not choosing the Oracle Cloud and one processor license that covered two cores on-premises now cover only one. Then, you want at least to optimize by consolidation: PDBs in a CDB rather than instances in a VM, and rather than multiple VMs. Note that this arithmetic on the number of cores behind the Oracle definition of a processor is subject to discussions. The documents mentioning it are “for educational purpose” and “does not constitute a contract or a commitment to any specific term”.

Anyway, you Bring Your Own License, where you can run 3 PDBs per CDB when you don’t have the multitenant option (and then you want to set MAX_PDBS=3 to be safe) or up to 252 when you have the option (and then you don’t want to be limited to 3). So in all cases, you have something to change. And Pluggable Database options can be useful to clone or relocate PDBs so that you can change the size of your VM (requires a restart) and balance the services on them.

Maybe all images are not in all region, so here is how to check:


[opc@a demo]$ az vm image list-publishers --location $(az account list-locations | jq -r '.[] | select ( .metadata.physicalLocation == "Geneva") .name ')
Command group 'vm' is experimental and under development. Reference and support levels: https://aka.ms/CLI_refstatus
(NoRegisteredProviderFound) No registered resource provider found for location 'switzerlandwest' and API version '2020-06-01' for type 'locations/publishers'. The supported api-versions are '2015-05-01-preview, 2015-06-15, 2016-03-30, 2016-04-30-preview, 2016-08-30, 2017-03-30, 2017-12-01, 2018-04-01, 2018-06-01, 2018-10-01, 2019-03-01, 2019-07-01, 2019-12-01, 2020-06-01, 2020-09-30, 2020-12-01'. The supported locations are 'eastus, eastus2, westus, centralus, northcentralus, southcentralus, northeurope, westeurope, eastasia, southeastasia, japaneast, japanwest, australiaeast, australiasoutheast, australiacentral, brazilsouth, southindia, centralindia, westindia, canadacentral, canadaeast, westus2, westcentralus, uksouth, ukwest, koreacentral, koreasouth, francecentral, southafricanorth, uaenorth, switzerlandnorth, germanywestcentral, norwayeast'.

[opc@a demo]$ az vm image list-publishers --location $(az account list-locations | jq -r '.[] | select ( .metadata.physicalLocation == "Zurich") .name ') | jq '.[] | select( .name == "Oracle" ) '
WARNING: Command group 'vm' is experimental and under development. Reference and support levels: https://aka.ms/CLI_refstatus
{
  "id": "/Subscriptions/bfaaad07-4a06-45e1-9e93-4eb58fa52f87/Providers/Microsoft.Compute/Locations/SwitzerlandNorth/Publishers/Oracle",
  "location": "SwitzerlandNorth",
  "name": "Oracle",
  "tags": null
}

[opc@a demo]$ az vm image list --all --location "SwitzerlandNorth" --publisher Oracle --output table --offer database
Command group 'vm' is experimental and under development. Reference and support levels: https://aka.ms/CLI_refstatus
Offer                 Publisher    Sku                      Urn                                                         Version
--------------------  -----------  -----------------------  ----------------------------------------------------------  ---------
oracle-database-19-3  Oracle       oracle-database-19-0904  Oracle:oracle-database-19-3:oracle-database-19-0904:19.3.1  19.3.1

So for me, nothing in Geneva, but I have the list of regions, and available in Zurich.

A final note. This VM for “oracle-database-19-3” image runs on Oracle Linux. Until Jan 6th, 2021 Oracle did not allow for “Basic Limited” and “Premier Limited” on VMs larger than 8 vCPUs. This has changed to a limit of 64vCPU (and one system license can even cover two VMs within this limit). So there are good news in licensing documents sometimes…