Si vous utilisez les services de bases de données d’un environnement RAC, vous avez sûrement déjà eu ce problème :
Lors de l’arrêt d’un noeud, le service est automatiquement transféré sur un des noeuds restant, mais lors du démarrage du premier noeud, le service n’est pas transféré automatiquement, le transfert est manuel, via la commande « relocate ».
Oracle a choisit de ne pas transférer automatiquement les services.
Voir doc Oracle :
When a service moves to an available instance, Oracle Database does not move the service back to the preferred instance when the preferred instance restarts because :
The service is running on the desired number of instances.
Maintaining the service on the current instance provides a higher level of service availability.
Not moving the service back to the initial preferred instance prevents a second outage.
Cependant, sur certains environnements, le transfert doit être transparent sans intervention de la part d’un administrateur.
Le but est donc d’ajouter un script qui permette de faire le relocate automatiquement lorsque le noeud redevient disponible.
La solution la plus adaptée est d’utiliser les FAN Callout (Fast Application Notification). Avant de présenter le script que l’on va utiliser, voici quelques informations à propos des FAN Callout
Depuis la version 10g, Oracle a introduit cette fonctionnalité, qui permet d’appeler un script sur un évènement au niveau du CRS (Arret Instance, Demarrage Service etc…).
Ce script doit se situer dans le répertoire CRS_HOME/racg/usrco (sur certaines versions, il faut créer le répertoire manuellement) et doit avoir les droits d’exécution. Pour chaque évènement un ensemble de paramètres sont passés au script.
On va dans un premier temps, voir quels sont les paramètres envoyés au script.
Pour cela, on crée le fichier CRS_HOME/racg/usrco/callout_test.sh, on lui donne les droits d’exécution et on ajoute les lignes de commandes suivantes :
#!/bin/bash > /tmp/callout.log args=("$@") for arg in ${args[@]}; do echo $arg >> /tmp/callout.log done
On génère un évènement en arrêtant une instance du RAC, et on observe le log généré :
[oracle@atlas1 usrco]$ srvctl stop instance -d CRONOS -i CRONOS1 [oracle@atlas1 usrco]$ cat /tmp/callout.log INSTANCE VERSION=1.0 service=cronos database=cronos instance=CRONOS1 host=atlas1 status=down reason=USER timestamp=2012-11-28 09:16:13
On peut voir dans ce fichier de log, l’ensemble des éléments passés en paramètre du script lors de son appel. Pour chaque action, différents éléments sont passés. On peut l’observer en redémarrant l’instance :
[oracle@atlas1 usrco]$ srvctl start instance -d CRONOS -i CRONOS1 [oracle@atlas1 usrco]$ cat /tmp/callout.log SERVICEMEMBER VERSION=1.0 service=cronoservice database=cronos instance=CRONOS1 host=atlas1 status=up reason=USER card=1 timestamp=2012-11-28 09:18:23 SERVICE VERSION=1.0 service=cronoservice database=cronos instance= host=atlas1 status=up reason=USER timestamp=2012-11-28 09:18:23
On peut maintenant créer plusieurs scripts pour gérer les évènements, ou les monitorer, comme par exemple, un script qui envoie un mail à chaque évènement, etc…
Nous allons voir ici comment reloger un service, qui a été créé en mode actif / passif sur une instance (active/active) après le redémarrage d’un des noeuds.
Créons un service en mode actif / passif :
[oracle@atlas1 usrco]$ srvctl add service -d CRONOS -s cronoservice -r CRONOS1 -a CRONOS2 [oracle@atlas1 usrco]$ srvctl start service -d CRONOS -s cronoservice
On vérifie les informations :
[oracle@atlas1 usrco]$ srvctl config service -d CRONOS -s cronoservice Service name: cronoservice Service is enabled Server pool: CRONOS_cronoservice Cardinality: 1 Disconnect: false Service role: PRIMARY Management policy: AUTOMATIC DTP transaction: false AQ HA notifications: false Failover type: NONE Failover method: NONE TAF failover retries: 0 TAF failover delay: 0 Connection Load Balancing Goal: LONG Runtime Load Balancing Goal: NONE TAF policy specification: NONE Edition: Preferred instances: CRONOS1 Available instances: CRONOS2
Le service aura l’instance CRONOS1 en « Prefered » et pourra se déplacer sur CRONOS2 en cas d’arrêt de la première instance.
Vérifions son état :
[oracle@atlas1 usrco]$ crsctl stat res -t | grep cronoservice -A1 ora.cronos.cronoservice.svc 1 ONLINE ONLINE atlas1
On redémarre le premier noeud, et on vérifie l’état du service sur l’instance 2 :
[oracle@atlas2 ~]$ crsctl stat res -t | grep -A4 cronoservice ora.cronos.cronoservice.svc 1 ONLINE ONLINE atlas2 ora.cronos.db 1 OFFLINE OFFLINE Instance Shutdown 2 ONLINE ONLINE atlas2 Open
Le RAC a correctement relogé le service sur l’instance encore disponible. Mais au redémarrage du noeud1, le service reste sur l’instance 1, il faut faire un relocate manuel :
[oracle@atlas2 ~]$ crsctl stat res -t | grep -A4 cronoservice ora.cronos.cronoservice.svc 1 ONLINE ONLINE atlas2 ora.cronos.db 1 ONLINE ONLINE atlas1 Open 2 ONLINE ONLINE atlas2 Open [oracle@atlas2 ~]$ srvctl relocate service -d CRONOS -s cronoservice -i CRONOS2 -t CRONOS1 [oracle@atlas2 ~]$ crsctl stat res -t | grep -A1 cronoservice ora.cronos.cronoservice.svc 1 ONLINE ONLINE atlas1
Pour éviter toute action manuelle et reloger automatiquement le service lors du redémarrage du noeud 1, on va ajouter un script qui sera appelé par Oracle au démarrage de l’instance (ne pas oublier de faire un chmod +x sous linux, pour lui donner les droits d’exécution):
#!/bin/bash # Desactive la langue sur le serveur pour travailler avec les status des services et des bases en anglais unset LANG LOGFILE=/home/oracle/log/auto_relocate_service.log SCRIPTDIR=`dirname $0` # Determine le grid home if [[ "${SCRIPTDIR:(-11)}" == "/racg/usrco" ]]; then GRID_HOME=""${SCRIPTDIR:0:$(( ${#SCRIPTDIR} - 11 ))}"" export GRID_HOME fi # Executer ce script seulement pour les evenement de type instance if [ "$1" != "INSTANCE" ]; then exit 0 fi STATUS="" DATABASE="" INSTANCE="" # On parse tous les parametres pour leur affecter une variable args=("$@") for arg in ${args[@]}; do if [[ "$arg" == *=* ]]; then KEY=${arg%=*} VALUE=${arg#*=} case "$KEY" in status) STATUS="$VALUE" ;; database) DATABASE="$VALUE" ;; instance) INSTANCE="$VALUE" ;; esac fi done # On veririe que le status de la base est up, sinon on sort if [[ -z "$DATABASE" || -z "$INSTANCE" || "$STATUS" != "up" ]]; then exit 0 fi echo "`date`" >> "$LOGFILE" echo "[$DATABASE][`hostname`] Instance $INSTANCE up" >> "$LOGFILE" # On recupere l oracle home a partir de la commande crsctl DBCONFIG=`$GRID_HOME/bin/crsctl status res ora.$DATABASE.db -f | grep "ORACLE_HOME="` if [ -z "$DBCONFIG" ]; then exit 0 fi declare -r "$DBCONFIG" echo "ORACLE_HOME=$ORACLE_HOME" >> "$LOGFILE" # Fonction sur les tableau in_array() { local hay needle=$1 shift for hay; do [[ $hay == $needle ]] && return 0 done return 1 } # Lecture des informations a propos des services for service in `$GRID_HOME/bin/crsctl status res | grep -E "ora.$DATABASE.(.+).svc" | sed -rne "s/NAME=ora.$DATABASE.(.+).svc/1/gip"`; do SERVICECONFIG=`$ORACLE_HOME/bin/srvctl config service -d $DATABASE -s $service` echo "Service $service" >> "$LOGFILE" if [[ "$SERVICECONFIG" == *"Service is enabled"* ]]; then echo " enabled" >> "$LOGFILE" PREFERRED=( `echo "$SERVICECONFIG" | grep "Preferred instances:" | sed -rne "s/.*: ([a-zA-Z0-9]+)/1/p" | tr "," "n"` ) # On verifie si l instance courante est l instance preferee du service if in_array "$INSTANCE" "${PREFERRED[@]}" ; then echo " preferred" >> "$LOGFILE" # On verifie si le service est deja demarre sur l instance courante SRVSTATUS=`$ORACLE_HOME/bin/srvctl status service -d $DATABASE -s $service` if [[ "$SRVSTATUS" == *"is not running"* ]]; then # Si le service n est pas up, on le demarre echo " service stopped, starting" >> "$LOGFILE" $ORACLE_HOME/bin/srvctl start service -d "$DATABASE" -s "$service" >> "$LOGFILE" else # Le service tourne, mais on verifie si il tourne sur l instance preferee RUNNING=( `echo "$SRVSTATUS" | sed -rne "s/.* ([a-zA-Z0-9]+)/1/p" | tr "," "n"` ) if ! in_array "$INSTANCE" "${RUNNING[@]}" ; then echo " not running on preferred $INSTANCE" >> "$LOGFILE" # On recupere la premiere instance demarree non preferee sur laquelle le service tourne CURRENT="" for inst in "${RUNNING[@]}"; do if ! in_array "$inst" "${PREFERRED[@]}" ; then CURRENT="$inst" break fi done # On deplace le service if [[ -n "$CURRENT" ]]; then echo " relocate $CURRENT -> $INSTANCE" >> "$LOGFILE" $ORACLE_HOME/bin/srvctl relocate service -d "$DATABASE" -s "$service" -i "$CURRENT" -t "$INSTANCE" >> "$LOGFILE" fi else # Il tourne deja sur l instance preferee echo " running on preferred $INSTANCE" >> "$LOGFILE" fi fi fi fi done
Après la mise en place du script, on refait notre test de redémarrage du noeud 1 :
[oracle@atlas1 ~]$ crsctl stat res -t |grep -A4 cronoservice ora.cronos.cronoservice.svc 1 ONLINE ONLINE atlas1 ora.cronos.db 1 ONLINE ONLINE atlas1 Open 2 ONLINE ONLINE atlas2 Open [oracle@atlas1 ~]$ exit logout [root@atlas1 ~]# reboot [oracle@atlas2 ~]$ crsctl stat res -t | grep -A4 cronoservice ora.cronos.cronoservice.svc 1 ONLINE ONLINE atlas2 ora.cronos.db 1 ONLINE OFFLINE Instance Shutdown 2 ONLINE ONLINE atlas2 Open --Quelques minutes plus tard [oracle@atlas2 ~]$ crsctl stat res -t | grep -A4 cronoservice ora.cronos.cronoservice.svc 1 ONLINE ONLINE atlas1 ora.cronos.db 1 ONLINE ONLINE atlas1 Open 2 ONLINE ONLINE atlas2 Open
Le service est correctement relogé sur son instance « Prefered ».
source : http://ilmarkerm.blogspot.fr/2012/05/scipt-to-automatically-move-rac-11gr2.html