#!/bin/sh # Ce script sert à créer ou détruire une instance Tomcat # en créant un CATALINA_BASE indépendant. # L'usage habituel se fait sur une Debian 7 "Wheezy" avec # tomcat, libapache2-mod-jk et apache2 installé. # Prérequis : # - Debian Wheezy (Squeeze non testée) # - tomcat7 (6 non testée) # - libapache2-mod-jk avec le workers.properties du module # Trucs à nettoyer (si quelque chose part en vrille, ou simplement # pour désinstaller ): # - supprimer tout le répertoire REP_TOMCAT_MULTIINSTANCE # - supprimer /etc/default/tomcatN_XXX # - supprimer /etc/init.d/tomcatN_XXX # - supprimer les utilisateurs tomcatN_YYYY # - nettoyer les clusters PostgreSQL # Version de Tomcat : 6 ou 7 # TODO : voir si on peut le passer en paramètre pour gérer # Tomcat 6 & 7 sur la même machine VERSION_TOMCAT="7" # Quelques constantes internes MAX_INSTANCES=999 START_RANGE_TOMCATUID=8000 START_RANGE_TOMCAT_PORT_SYS=15000 START_RANGE_TOMCAT_PORT_HTTP=18000 START_RANGE_TOMCAT_PORT_AJP=19000 # Initialisation des variables globales MODE_CREATION=0 MODE_DESTRUCTION=0 TOMCAT_ENABLE_SHUTDOWN_PORT="" OPTION_WORKERS_PROPERTIES="" OPTION_VHOST_APACHE_SERVERNAME="" NB_CHIFFRES_DANS_NUM_INSTANCE=$( printf "%d" "$MAX_INSTANCES" | wc -c ) REP_TOMCAT_MULTIINSTANCE="/var/lib/tomcat$VERSION_TOMCAT-multiinstances" # On récupère également le nom de l'utilisateur et du groupe de Tomcat if [ ! -f /etc/default/tomcat$VERSION_TOMCAT ]; then echo "ERREUR: /etc/default/tomcat$VERSION_TOMCAT inaccessible." >&2 exit 1 fi . /etc/default/tomcat$VERSION_TOMCAT # Par défaut, on arrête le script à la première erreur non "catchée" set -e # Fonctions # Arguments # $1 : Version de Tomcat # $2 : nom du worker # $3 : port AJP du worker ajout_worker_properties() { test -n "$1" && test -n "$2" && test -n "$3" || exit 1 # On se fiche un peu de la version de Tomcat, en fait :) case "$1" in 6|7) cat <<EOF # Script MI Tomcat : ajout du worker $2 worker.$2.port = $3 worker.$2.host = localhost worker.$2.type = ajp13 EOF ;; *) echo "ERREUR: version de Tomcat non-gérée." >&2 exit 1 ;; esac } # Arguments # $1 : Version de Tomcat # $2 : port système (d'habitude 8005) # $3 : port connecteur HTTP (8080, d'habitude) # $4 : port connecteur AJP (8009, d'habitude) generate_patch_tomcat_server_xml() { test -n "$1" && test -n "$2" && test -n "$3" && test -n "$4" && test -n "$5" || exit 1 case "$1" in 6|7) generate_patch_tomcat_server_xml_6 "$2" "$3" "$4" "$5" ;; *) echo "ERREUR: version de Tomcat non-gérée." >&2 exit 1 ;; esac } # Arguments # $1 : port système (d'habitude 8005) # $2 : port connecteur HTTP (8080, d'habitude) # $3 : port connecteur AJP (8009, d'habitude) generate_patch_tomcat_server_xml_6() { local NOM_WORKER TOMCAT_PORT_SYS TOMCAT_PORT_HTTP TOMCAT_PORT_AJP test -n "$1" && test -n "$2" && test -n "$3" && test -n "$4" || exit 1 NOM_WORKER="$1" TOMCAT_PORT_SYS="$2" TOMCAT_PORT_HTTP="$3" TOMCAT_PORT_AJP="$4" cat <<EOF --- server.xml +++ server.xml @@ -19,7 +19,7 @@ define subcomponents such as "Valves" at this level. Documentation at /docs/config/server.html --> -<Server port="8005" shutdown="SHUTDOWN"> +<Server port="$TOMCAT_PORT_SYS" shutdown="SHUTDOWN"> <!-- Security listener. Documentation at /docs/config/listeners.html <Listener className="org.apache.catalina.security.SecurityListener" /> --> @@ -69,10 +69,12 @@ APR (HTTP/AJP) Connector: /docs/apr.html Define a non-SSL HTTP/1.1 Connector on port 8080 --> - <Connector port="8080" protocol="HTTP/1.1" + <!-- + <Connector port="$TOMCAT_PORT_HTTP" protocol="HTTP/1.1" connectionTimeout="20000" URIEncoding="UTF-8" redirectPort="8443" /> + --> <!-- A "Connector" using the shared thread pool--> <!-- <Connector executor="tomcatThreadPool" @@ -91,9 +93,7 @@ --> <!-- Define an AJP 1.3 Connector on port 8009 --> - <!-- - <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" /> - --> + <Connector port="$TOMCAT_PORT_AJP" protocol="AJP/1.3" redirectPort="8443" address="127.0.0.1" /> <!-- An Engine represents the entry point (within Catalina) that processes @@ -105,7 +105,7 @@ <!-- You should set jvmRoute to support load-balancing via AJP ie : <Engine name="Catalina" defaultHost="localhost" jvmRoute="jvm1"> --> - <Engine name="Catalina" defaultHost="localhost"> + <Engine name="Catalina" defaultHost="localhost" jvmRoute="$NOM_WORKER"> <!--For clustering, please take a look at documentation at: /docs/cluster-howto.html (simple how to) EOF } # Petites fonctions pour éviter de faire des concatenations # hasardeuses un peu partout get_tomcat_user() { case $VERSION_TOMCAT in '6') printf "%s" "$TOMCAT6_USER" ;; '7') printf "%s" "$TOMCAT7_USER" ;; esac } get_tomcat_group() { # TODO : une idée à creuser serait de créer un groupe tomcat incluant tomcat6 et tomcat7 case $VERSION_TOMCAT in '6') printf "%s" "$TOMCAT6_GROUP" ;; '7') printf "%s" "$TOMCAT7_GROUP" ;; esac } # affiche le message d'aide usage() { cat <<EOF $0 -c suffixe $0 -d numero_instance $0 -h -c xxxx : création d'une instance avec le suffixe 'xxxx' (15 caract. max) -d NNN : destruction de l'instance numéro NNN -S : activation du port d'extinction de Tomcat (déconseillé en production, ne gêne pas le gestionnaire de démon de Debian : http://tomcat.apache.org/tomcat-7.0-doc/config/server.html#Common_Attributes http://wiki.apache.org/tomcat/FAQ/Security#Q2 ) Ce script peut également modifier le fichier workers.properties pour y ajouter les infos du worker créé. -w /etc/.../workers.properties : Modification du workers.properties -h : ce message d'aide EOF } # Début du code # gestion des options de lancement while getopts c:d:Sw:h f; do case $f in 'c') MODE_CREATION=1 SUFFIX="$OPTARG" OLD_LC_ALL="$LC_ALL" export LC_ALL=C export LANG=C if [ $( printf "$SUFFIX" | egrep -c "^[a-z][a-z0-9_]{0,14}$" ) -ne 1 ]; then echo "ERREUR: suffixe vide ou contenant des caractères interdits : regexp : [a-z][a-z0-9_]{0,14}" >&2 exit 1 fi export LC_ALL="$OLD_LC_ALL" export LANG="$OLD_LC_ALL" ;; 'd') MODE_DESTRUCTION=1 NUM_INSTANCE=$( printf "%d" "$OPTARG" ) ;; 'h') usage exit 0 ;; 'S') TOMCAT_ENABLE_SHUTDOWN_PORT="1" ;; 'w') OPTION_WORKERS_PROPERTIES="$OPTARG" if [ ! -f "$OPTION_WORKERS_PROPERTIES" ]; then echo "ERREUR: fichier workers.properties inexistant : $OPTION_WORKERS_PROPERTIES" >&2 exit 1 fi ;; \?) usage >&2 exit 1 ;; esac done #(code inutile pour ce script, mais que je garde parce qu'on ne sait jamais) #shift $( expr $OPTIND - 1 ) #DATA="$1" # Petite vérif. case $(( $MODE_CREATION + $MODE_DESTRUCTION )) in '0') echo "ERREUR: veuillez choisir entre création et destruction." >&2 usage >&2 exit 1 ;; '2') echo "ERREUR: tu veux créer et détruire en même temps, petit malin ?" >&2 exit 1 ;; esac # - est-ce qu'on est bien sur une Debian 6 ou 7 # (c'est surtout pour éviter les mauvaises surprises, ce script n'ayant pas été testé ailleurs) if [ ! -f "/etc/debian_version" ] || [ $( egrep -c "^(6.|7.)" /etc/debian_version ) -ne 1 ]; then echo "ERREUR: mauvaise version de Debian détectée. Arrêt par prudence. Vérifiez le code de ce script avant de forcer." >&2 exit 1 fi # - est-ce que Tomcat est bien installé ? if ! dpkg -l tomcat$VERSION_TOMCAT >/dev/null 2>&1; then echo "ERREUR: tomcat$VERSION_TOMCAT ne semble pas avoir été installé." >&2 exit 1 fi # - on vérifie que le /etc/default/tomcatN contient bien les infos qui nous intéresse if [ ! -n $( get_tomcat_user ) ] || [ ! -n $( get_tomcat_group) ]; then echo "ERREUR: TOMCAT_USER ou TOMCAT_GROUP indeterminé." >&2 exit 1 fi # Fin des vérifications if [ "$MODE_CREATION" -eq 1 ]; then # Détermination du numéro d'instance : # On cherche un numéro inutilisé ni par une instance Tomcat, ni par une instance Postgres for NUM_INSTANCE in $( seq 1 $MAX_INSTANCES ); do # Finalement, on base tout sur le nom de l'instance : ça # nous permet de retrouver facilement le répertoire de l'instance # lors de la suppression (le HOME de tomcat7 est sur CATALINA_HOME, # pas sur CATALINA_BASE :'( NOM_INSTANCE=$( printf "%s_%0$NB_CHIFFRES_DANS_NUM_INSTANCE""d_%s" "$( get_tomcat_user )" "$NUM_INSTANCE" "$SUFFIX" ) USERID_TOMCAT=$(( $START_RANGE_TOMCATUID + $NUM_INSTANCE )) USERNAME_TOMCAT="$NOM_INSTANCE" SCRIPTNAME_TOMCAT="$NOM_INSTANCE" WORKERNAME_TOMCAT="worker_$NOM_INSTANCE" if [ ! -d "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE" ] && ! getent passwd "$USERID_TOMCAT" >/dev/null 2>&1 && ! getent passwd "$USERNAME_TOMCAT" >/dev/null 2>&1; then echo "INFO: Numéro d'instance libre trouvé : $NUM_INSTANCE" break fi done if [ "$NUM_INSTANCE" -eq "$MAX_INSTANCES" ]; then echo "ERREUR: plus de numéro d'instance disponible." >&2 exit 1 fi # Vérifier que le REP_TOMCAT_MULTIINSTANCE pré-existe. # TODO review : finalement pas une bonne idée de le faire créer par le script ? if [ ! -d "$REP_TOMCAT_MULTIINSTANCE" ]; then # (finalement, on le crée nous même...) echo "WARNING: le répertoire '$REP_TOMCAT_MULTIINSTANCE' n'existe pas (normal si première exécution). Création..." mkdir -p "$REP_TOMCAT_MULTIINSTANCE" chown -R "root:$( get_tomcat_group )" "$REP_TOMCAT_MULTIINSTANCE" chmod 750 "$REP_TOMCAT_MULTIINSTANCE" fi # Copie du script d'init et du /etc/default de Tomcat # Note : pour les scripts d'init, j'aurais aimé faire de simples liens symboliques vers # un script générique. Malheureusement, ça pose de gros soucis pour LSB (insserv # semble avoir les liens symboliques en horreur) et on aurait dû tout faire à la # main, d'où plus d'inconvénients que d'avantages. echo "INFO: création de l'utilisateur système dédié..." useradd -u "$USERID_TOMCAT" -s /bin/false -g $( get_tomcat_group ) -d $( getent passwd $( get_tomcat_user ) | cut -d : -f 6 ) "$USERNAME_TOMCAT" echo "INFO: création du script de lancement..." cat /etc/init.d/tomcat$VERSION_TOMCAT | \ sed "s/^\(#[[:space:]]*Provides[[:space:]]*:[[:space:]]*\)tomcat[0-9]\+[[:space:]]*$/\1$SCRIPTNAME_TOMCAT/" | \ sed 's/^NAME=tomcat[0-9]\+$/NAME=$( basename "$0" | sed "s\/^[SK][0-9]\+\/\/" )/' \ >/etc/init.d/$SCRIPTNAME_TOMCAT chmod +x /etc/init.d/$SCRIPTNAME_TOMCAT echo "INFO: création du fichier de paramétrage dans /etc/default..." cat /etc/default/tomcat$VERSION_TOMCAT | sed "s/^\(TOMCAT[0-9]\+_USER\)=tomcat[0-9]\+$/\1=$USERNAME_TOMCAT/" >/etc/default/$SCRIPTNAME_TOMCAT echo "# Script MI Tomcat : Spécification des CATALINA_HOME et CATALINA_BASE" >>/etc/default/$SCRIPTNAME_TOMCAT echo "CATALINA_HOME=/usr/share/tomcat$VERSION_TOMCAT" >>/etc/default/$SCRIPTNAME_TOMCAT echo "CATALINA_BASE=$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE" >>/etc/default/$SCRIPTNAME_TOMCAT # Activation du script d'init echo "INFO: activation du script d'init..." update-rc.d $SCRIPTNAME_TOMCAT defaults # Instanciation du CATALINA_BASE # Copie du /var/lib/tomcatN (notre template, en gros) echo "INFO: génération du CATALINA_BASE ( $REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE )..." mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE" cp -a /var/lib/tomcat$VERSION_TOMCAT/* "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/" rm "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf" rm "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/logs" rm "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/work" mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf" mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/logs" mkdir "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/work" cp -r /var/lib/tomcat$VERSION_TOMCAT/conf/* "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf/" # Parfois, le work est vide. Solution temporaire : on émet un warning if [ "$( ls /var/lib/tomcat$VERSION_TOMCAT/work/* 2>/dev/null | wc -l )" -gt 0 ]; then cp -r /var/lib/tomcat$VERSION_TOMCAT/work/* "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/work/" else echo "INFO: le répertoire /work/ du template est vide. A priori, c'est normal." fi chown -R $USERNAME_TOMCAT:$( get_tomcat_group ) "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE" # Réglage des ports TCP/IP generate_patch_tomcat_server_xml "$VERSION_TOMCAT" \ "$WORKERNAME_TOMCAT" \ $( test -n "$TOMCAT_ENABLE_SHUTDOWN_PORT" && echo $(( $START_RANGE_TOMCAT_PORT_SYS + $NUM_INSTANCE )) || echo "-1" ) \ $(( $START_RANGE_TOMCAT_PORT_HTTP + $NUM_INSTANCE )) \ $(( $START_RANGE_TOMCAT_PORT_AJP + $NUM_INSTANCE )) | \ patch $REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/conf/server.xml # Génération du fichier de config de logrotate echo "INFO: génération du logrotate..." cat /etc/logrotate.d/tomcat$VERSION_TOMCAT | \ sed "s#^/var/log/tomcat[0-9]\+/catalina.out #$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE/logs/catalina.out #" | \ sed "s/create 640 tomcat[0-9]\+ adm/create 640 $USERNAME_TOMCAT adm/" \ >/etc/logrotate.d/$SCRIPTNAME_TOMCAT # partie annexe n°1 : workers.properties if [ -f "$OPTION_WORKERS_PROPERTIES" ]; then echo "INFO: Modification du workers.properties..." # Ajout du worker à la worker.list TMP_SUFFIX=$( date +%Y%m%d-%H%M%S ) sed -i.$TMP_SUFFIX -e "s/^worker.list=\(.\+\)$/worker.list=\1,$WORKERNAME_TOMCAT/g" -e "s/^worker.list=$/worker.list=$WORKERNAME_TOMCAT/g" "$OPTION_WORKERS_PROPERTIES" echo "NOTICE: une sauvegarde $OPTION_WORKERS_PROPERTIES.$TMP_SUFFIX a été créée, n'hésitez pas à la supprimer." # Ajouts de la définition du worker ajout_worker_properties "$VERSION_TOMCAT" "$WORKERNAME_TOMCAT" $(( $START_RANGE_TOMCAT_PORT_AJP + $NUM_INSTANCE )) >> "$OPTION_WORKERS_PROPERTIES" fi # Fin echo "INFO: Création terminée. Serveur Tomcat éteint." echo "INFO: Pour connecter mod-jk au worker, voici la ligne à ajouter au vhost : JkMount /* $WORKERNAME_TOMCAT" echo "INFO: Pour allumer l'instance Tomcat : service $SCRIPTNAME_TOMCAT start" fi if [ "$MODE_DESTRUCTION" -eq 1 ]; then USERID_TOMCAT=$(( $START_RANGE_TOMCATUID + $NUM_INSTANCE )) #echo "NOTICE: le script est en mode -e. Il s'arrête donc à la première erreur. Le cas échéant, il faudra terminer le nettoyage à la main." # On recherche le nom de l'instance USERNAME_TOMCAT=$( getent passwd $USERID_TOMCAT | cut -d : -f 1 ) NOM_INSTANCE="$USERNAME_TOMCAT" SCRIPTNAME_TOMCAT="$NOM_INSTANCE" WORKERNAME_TOMCAT="worker_$NOM_INSTANCE" if [ ! -n "$USERNAME_TOMCAT" ]; then echo "ERREUR: l'utilisateur correspondant à l'uid $USERID_TOMCAT est introuvable." >&2 exit 1 fi if service "$SCRIPTNAME_TOMCAT" status >/dev/null 2>&1; then echo "ERREUR: l'instance tomcat tourne toujours. Ne pas oublier le service $SCRIPTNAME_TOMCAT stop" >&2 exit 1 fi # Suppression utilisateur et répertoire dédié echo "INFO: suppression de l'utilisateur..." userdel $USERNAME_TOMCAT echo "INFO: suppression du répertoire dédié..." rm -rf "$REP_TOMCAT_MULTIINSTANCE/$NOM_INSTANCE" echo "INFO: suppresion fichier default..." rm -f "/etc/default/$SCRIPTNAME_TOMCAT" # Suppression du script d'init echo "INFO: désactivation et suppression du script d'init..." rm -f /etc/init.d/$SCRIPTNAME_TOMCAT update-rc.d "$SCRIPTNAME_TOMCAT" remove # Suppression logrotate echo "INFO: suppression logrotate..." rm -f /etc/logrotate.d/$SCRIPTNAME_TOMCAT # partie annexe n°1 : workers.properties if [ -f "$OPTION_WORKERS_PROPERTIES" ]; then echo "INFO: Modification du workers.properties..." # Soustraction de la définition du worker TMP_SUFFIX=$( date +%Y%m%d-%H%M%S ) sed -i.$TMP_SUFFIX -e "/^worker.list=/s/$WORKERNAME_TOMCAT[,]\?//g" \ -e '/^worker.list=.*,$/s/,$//' \ -e "/^# Script MI Tomcat : ajout du worker $WORKERNAME_TOMCAT$/d" \ -e "/^worker.$WORKERNAME_TOMCAT/d" \ "$OPTION_WORKERS_PROPERTIES" echo "NOTICE: une sauvegarde $OPTION_WORKERS_PROPERTIES.$TMP_SUFFIX a été créée, n'hésitez pas à la supprimer." fi # Fin echo "INFO: suppression terminée." fi