網上和官方文檔已經有不少介紹如何設置開機啟動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
- 修改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:
O R A C L E S I D : ORACLE_SID: ORACLES?ID: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 – O R A C L E B A S E H O M E / n e t w o r k / l o g L O G = ORACLE_BASE_HOME/network/log LOG= ORACLEB?ASEH?OME/network/logLOG=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 >> KaTeX parse error: Expected 'EOF', got '&' at position 7: LOG 2>&?1 & VER10LI…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 – O R A C L E B A S E H O M E / n e t w o r k / l o g L O G = ORACLE_BASE_HOME/network/log LOG= ORACLEB?ASEH?OME/network/logLOG=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 >> KaTeX parse error: Expected 'EOF', got '&' at position 7: LOG 2>&?1 & VER10LI…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 – O R A C L E B A S E H O M E / n e t w o r k / l o g L O G = ORACLE_BASE_HOME/network/log LOG= ORACLEB?ASEH?OME/network/logLOG=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 – O R A C L E B A S E H O M E / n e t w o r k / l o g L O G = ORACLE_BASE_HOME/network/log LOG= ORACLEB?ASEH?OME/network/logLOG=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= O R A C L E H O M E / b i n / d b s t a r t E x e c S t o p = ORACLE_HOME/bin/dbstart ExecStop= ORACLEH?OME/bin/dbstartExecStop=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