Linux平臺Oracle開機自啟動設定

潇湘隐者發表於2024-11-14

網上和官方文件已經有不少介紹如何設定開機啟動Oracle例項的文章(Linux平臺),不過以sysvinit和service這種方式居多。最近遇到了UAT環境的伺服器打補丁後需要重啟伺服器的情況, 需要DBA去手工啟動Oracle例項的情形,和同事討論,決定將UAT環境的Oracle例項啟停設定成systemd服務,使其開機自啟動,避免在伺服器打補丁重啟的情況下, 需要DBA去手工啟動Oracle例項。這樣可以大大減少工作量。 下面是設定Linux平臺Oracle開機自啟動的總結性文件,僅供參考。

實驗環境:

作業系統版本: Red Hat Enterprise Linux release 8.10 (Ootpa)

資料庫版本 : Oracle 19c

方案1

1. 修改oratab檔案

修改/etc/oratab的配置,找到如下這樣的配置(不同環境,ORACLE_SID以及Oracle安裝路徑可能不一致,以實際情況為準)

原始值:

gsp:/opt/oracle19c/product/19.3.0/db_1:N

修改後:

gsp:/opt/oracle19c/product/19.3.0/db_1:Y

在oratab檔案中,ORACLE_SID:$ORACLE_HOME:<N|Y>,設定為Y時,表示允許Oracle例項開機自啟動,當設定為N時,則不允許開機自啟動。 這個檔案裡的配置僅僅起一個開關的作用, 其實它並不會具體的執行啟動和關閉Oracle例項操作. 因為後面的指令碼dbstart中會從oratab獲取相關值,它類似一個引數配置檔案。所以這一步至關重要。

[root@OraPrefTest system]# more /etc/oratab
# This file is used by ORACLE utilities. It is created by root.sh
# and updated by either Database Configuration Assistant while creating
# a database or ASM Configuration Assistant while creating ASM instance.

# A colon, ':', is used as the field terminator. A new line terminates
# the entry. Lines beginning with a pound sign, '#', are comments.
#
# Entries are of the form:
# $ORACLE_SID:$ORACLE_HOME:<N|Y>:
#
# The first and second fields are the system identifier and home
# directory of the database respectively. The third field indicates
# to the dbstart utility that the database should , "Y", or should not,
# "N", be brought up at system boot time.
#
# Multiple entries with the same $ORACLE_SID are not allowed.
#
#
gsp:/opt/oracle19c/product/19.3.0/db_1:Y

簡單方式,可以使用下面命令修改

sed -i 's/:N/:Y/' /etc/oratab

2.修改dbstart

dbstart檔案的具體路徑為:$ORACLE_HOME/bin/dbstart

上述指令碼在啟動監聽時,只啟動了預設監聽服務(LISTENER),不會啟動命名的監聽服務,如果你配置的是命名監聽的話, 那麼需要修改指令碼,如下所示,如果你使用的是預設的監聽服務的話,那麼直接跳過這一步。

--修改前

  # Determine location of listener.log
mkdir -p -- $ORACLE_BASE_HOME/network/log
LOG=$ORACLE_BASE_HOME/network/log/listener.log

# Start Oracle Net Listener
if [ -x $ORACLE_HOME/bin/tnslsnr ] ; then
echo "$0: Starting Oracle Net Listener" >> $LOG 2>&1
$ORACLE_HOME/bin/lsnrctl start >> $LOG 2>&1 &
VER10LIST=`$ORACLE_HOME/bin/lsnrctl version | grep "LSNRCTL for " | cut -d' ' -f5 | cut -d'.' -f1`
export VER10LIST
else
echo "Failed to auto-start Oracle Net Listener using $ORACLE_HOME/bin/tnslsnr"
fi

--修改後

此處監聽名為gsp,那麼我們調整為"$ORACLE_HOME/bin/lsnrctl start gsp"

  # Determine location of listener.log
mkdir -p -- $ORACLE_BASE_HOME/network/log
LOG=$ORACLE_BASE_HOME/network/log/listener.log

# Start Oracle Net Listener
if [ -x $ORACLE_HOME/bin/tnslsnr ] ; then
echo "$0: Starting Oracle Net Listener" >> $LOG 2>&1
$ORACLE_HOME/bin/lsnrctl start gsp >> $LOG 2>&1 &
VER10LIST=`$ORACLE_HOME/bin/lsnrctl version | grep "LSNRCTL for " | cut -d' ' -f5 | cut -d'.' -f1`
export VER10LIST
else
echo "Failed to auto-start Oracle Net Listener using $ORACLE_HOME/bin/tnslsnr"
fi

另外,這裡不需要修改其它配置,網上有些文章修改"ORACLE_HOME_LISTNER=$1",其實這個看你的方案與思路,至少這裡無需修改其它任何程式碼。 因為呼叫dbstart指令碼的時候,會傳入引數。

3.設定dbshut

dbshut檔案的具體路徑為:$ORACLE_HOME/bin/dbshut

這裡跟步驟2一致,如果是預設的監聽服務,那麼也請跳過這一步。

修改前:

  # Determine location of listener.log
mkdir -p -- $ORACLE_BASE_HOME/network/log
LOG=$ORACLE_BASE_HOME/network/log/listener.log

# Stop Oracle Net Listener
if [ -x $ORACLE_HOME/bin/tnslsnr ] ; then
echo "$0: Stopping Oracle Net Listener" >> $LOG 2>&1
$ORACLE_HOME/bin/lsnrctl stop >> $LOG 2>&1 &
else
echo "Failed to auto-stop Oracle Net Listener using $ORACLE_HOME/bin/tnslsnr"
fi

修改後:

  # Determine location of listener.log
mkdir -p -- $ORACLE_BASE_HOME/network/log
LOG=$ORACLE_BASE_HOME/network/log/listener.log

# Stop Oracle Net Listener
if [ -x $ORACLE_HOME/bin/tnslsnr ] ; then
echo "$0: Stopping Oracle Net Listener" >> $LOG 2>&1
$ORACLE_HOME/bin/lsnrctl stop gsp >> $LOG 2>&1 &
else
echo "Failed to auto-stop Oracle Net Listener using $ORACLE_HOME/bin/tnslsnr"
fi

4. 配置systemd服務

在/etc/systemd/system/下建立一個新的systemd服務檔案,命名為oracle.service

方式1:

# more oracle.service 
[Unit]
Description=Oracle Database Service
After=network.target

[Service]
Type=forking
User=oracle
Group=oinstall
ExecStart=/opt/oracle19c/product/19.3.0/db_1/bin/dbstart /opt/oracle19c/product/19.3.0/db_1
ExecStop=/opt/oracle19c/product/19.3.0/db_1/bin/dbshut /opt/oracle19c/product/19.3.0/db_1
TimeoutStopSec=5min
Restart=on-failure

[Install]
WantedBy=multi-user.target

注意:/opt/oracle19c/product/19.3.0/db_1為$ORACLE_HOME,根據實際情況進行調整。

配置完成,執行下面命名後,就可以使用systemctl啟動或停止Oracle例項了。

# systemctl daemon-reload
# systemctl enable oracle.service

在測試oracle服務的啟停前,先確保oracle例項和監聽服務已經關閉,如果手工啟動的oracle例項,使用下面命令關閉時會異常。詳情請見下文"問題1"。

# systemctl start oracle.service
# systemctl status oracle.service
# systemctl stop oracle.service

此時,你透過命令"systemctl status oracle.service",可以知道檢視oracle例項是否正常,你也可以透過$ORACLE_HOME/rdbms/log/startup.log日誌檢視 其實這個日誌是在dbshut中生成的。當前測試環境為/opt/oracle19c/product/19.3.0/db_1/rdbms/log/startup.log

方案2

如下所示,我們可以在systemd中引入環境變數,如下所示

# more oracle.service 
[Unit]
Description=Oracle Database Service
After=network.target

[Service]
Type=forking
User=oracle
Group=oinstall
Environment="ORACLE_HOME=/opt/oracle19c/product/19.3.0/db_1"
ExecStart=/opt/oracle19c/product/19.3.0/db_1/bin/dbstart $ORACLE_HOME
ExecStop=/opt/oracle19c/product/19.3.0/db_1/bin/dbshut $ORACLE_HOME
TimeoutStopSec=5min
Restart=no
RemainAfterExit=yes
KillMode=none

[Install]
WantedBy=multi-user.target

我們也可以使用EnvironmentFile來指定環境變數,如下所示,建立檔案/home/oracle/dba_scripts/oracle.env,在檔案中指定環境變數

ORACLE_HOME=/opt/oracle19c/product/19.3.0/db_1
# more oracle.service 
[Unit]
Description=Oracle Database Service
After=network.target

[Service]
Type=forking
User=oracle
Group=oinstall
EnvironmentFile=/home/oracle/dba_scripts/oracle.env
ExecStart=/opt/oracle19c/product/19.3.0/db_1/bin/dbstart $ORACLE_HOME
ExecStop=/opt/oracle19c/product/19.3.0/db_1/bin/dbshut $ORACLE_HOME
TimeoutStopSec=5min
Restart=no
RemainAfterExit=yes
KillMode=none

[Install]
WantedBy=multi-user.target

做完上述操作後,執行下面命令重新載入systemd系統和服務管理器配置

# systemctl daemon-reload

方案3

上面的方案有一個問題,就是如果監聽服務是命名監聽,那麼必須修改dbstart或dbshut指令碼,如果是一臺或兩臺資料庫例項,這樣修改倒也沒有什麼問題。 如果伺服器多了的話,那麼每一臺都要修改,那麼我們應該考慮不修改這些指令碼,透過另外的形式來事項。這個就是方案3的出現的理由。

我們新建一個指令碼oracle_start_stop.sh

#!/bin/bash
###################################################################################################
# Script used to start or stop oracle instance #
#*************************************************************************************************#
# Version Autor Modified Date Description #
#*************************************************************************************************#
# 1.0 Kerry Kong 2019-09-10 create the shell script. #
###################################################################################################
SUCCESS=0
FAILURE=1

# Check the number of parameter.
if [ $# -lt 2 ]; then
echo "please check the scirpt's parameter"
exit $FAILURE
fi

STATUS=$1
LISTENER_NAME=$2

if [ $STATUS == 'start' ];then
$ORACLE_HOME/bin/lsnrctl start $LISTENER_NAME
$ORACLE_HOME/bin/dbstart $ORACLE_HOME
exit $SUCCESS
elif [ $STATUS == 'stop' ]; then
$ORACLE_HOME/bin/lsnrctl stop $LISTENER_NAME
$ORACLE_HOME/bin/dbshut $ORACLE_HOME
exit $SUCCESS
else
echo "the parameter is not correct"
fi

新建一個oracle.service的systemd服務檔案,如下所示,ExecStart和ExecStop中去呼叫執行oracle_start_stop.sh檔案

# vi oracle.service 
[Unit]
Description=Oracle Database Service
After=network.target

[Service]
Type=forking
User=oracle
Group=oinstall
ExecStart=/home/oracle/dba_scripts/oracle_start_stop.sh start gsp
ExecStop=/home/oracle/dba_scripts/oracle_start_stop.sh stop gsp
TimeoutStopSec=5min
Restart=on-failure

[Install]
WantedBy=multi-user.target

配置完成,執行下面命名後,就可以使用systemctl啟動或停止Oracle例項了。

# systemctl daemon-reload
# systemctl enable oracle.service

問題彙總

問題1: 手工啟動oracle例項,使用systemctl stop oracle關閉不了。這是為什麼呢?

systemctl是systemd系統和服務管理器的命令列工具,主要用於控制systemd管理的服務。對於那些不是透過 systemd 啟動的服務或程序, systemctl 預設情況下是無法直接控制的。例如,如果Oracle例項是透過sqlplus手工啟動的話,指令碼中不做一些特殊控制或修改,預設情況下, systemctl將無法控制它(關閉它)。

問題2 在oracle.service中使用變數問題

[Unit]
Description=Oracle Database Service
After=network.target

[Service]
Type=forking
User=oracle
Group=dba
ExecStart=$ORACLE_HOME/bin/dbstart
ExecStop=$ORACLE_HOME/bin/dbshut
Restart=on-failure

[Install]
WantedBy=multi-user.target

這種情況下,無法獲取系統變數$ORACLE_HOME的值。會報如下錯誤,如下所示:

# systemctl start oracle.service
Failed to start oracle.service: Unit oracle.service has a bad unit file setting.
See system logs and 'systemctl status oracle.service' for details.

# systemctl status oracle.service
● oracle.service - Oracle Database Service
Loaded: bad-setting (Reason: Unit oracle.service has a bad unit file setting.)
Active: inactive (dead)

Oct 21 08:28:30 OraPrefTest systemd[1]: /etc/systemd/system/oracle.service:9: Neither a valid executable name nor an absolute path:$ORACLE_HOME/bin/dbstart
lines 1-5/5 (END)

分析如下:

# systemd-analyze verify oracle.service
/etc/systemd/system/./oracle.service:10: Neither a valid executable name nor an absolute path: $ORACLE_HOME/bin/dbstart

其實出現這個問題,是因為在systemd中指令碼必須使用絕對路徑。這個歸因於systemd的工作方式和路徑解析機制,出於安全(避免路徑遍歷攻擊、防止命令注入等) 等方面的原因。

但是可以在指令碼後面使用環境變數,如下所示

ExecStart=/opt/oracle19c/product/19.3.0/db_1/bin/dbstart $ORACLE_HOME
ExecStop=/opt/oracle19c/product/19.3.0/db_1/bin/dbshut $ORACLE_HOME

問題3

關於有些版本的一些bug問題,systemd服務裡面可能需要設定一些系統變數,如下所示。具體參考官方文件Automatic Stop of Database (dbshut) not working in OL 7 with systemd (Doc ID 2229679.1)。

[Unit]
Description=Oracle Database Start/Stop Service
After=syslog.target network.target local-fs.target remote-fs.target
[Service]
# systemd, by design does not honor PAM limits
# See: https://bugzilla.redhat.com/show_bug.cgi?id=754285
LimitNOFILE=65536
LimitNPROC=16384
LimitSTACK=32M
LimitMEMLOCK=infinity
LimitCORE=infinity
LimitDATA=infinity

Type=simple
User=oracle
Group=oinstall
Restart=no
ExecStartPre=/bin/rm -rf /u01/app/oracle/product/12.2.0/dbhome_1/listener.log
ExecStartPre=/bin/rm -rf /u01/app/oracle/product/12.2.0/dbhome_1/startup.log
ExecStart=/bin/bash /u01/app/oracle/product/12.2.0/dbhome_1/bin/dbstart /u01/app/oracle/product/12.2.0/dbhome_1
RemainAfterExit=yes
ExecStop=/bin/rm -rf /u01/app/oracle/product/12.2.0/dbhome_1/shutdown.log
ExecStop=/bin/bash /u01/app/oracle/product/12.2.0/dbhome_1/bin/dbshut /u01/app/oracle/product/12.2.0/dbhome_1
TimeoutStopSec=5min

[Install]
WantedBy=multi-user.target

相關文章