Automatización de la instalación de WordPress
Éste será el primero de una serie de posts sobre automatización en varios lenguajes de scripting. En este post expongo una automatización para instalación de WordPress, con un backend de MariaDB y PHP 7.0, en una distribución Debian Jessie. En próximas entregas desgranaré scripts de desinstalación, backup y restauración de la instalación realizada con este script.
Automatización
Las funciones del script son llamadas una vez durante la ejecución del script, excepto la función auxiliar iferror, que es llamada cuando un proceso devuelve un código de error y termina la ejecución del script devolviendo un mensaje a la consola.
Parámetros
El script utiliza variables que han de definirse en un archivo de texto plano. Si ejecutamos el script en una instalación limpia, la contraseña del administrador de la base de datos debemos escribirla en el archivo de configuración antes de ejecutar el script. Más adelante, el instalador de MariaDB nos pedirá una contraseña para el nuevo administrador y deberemos escribir la misma que hayamos puesto en el archivo. En caso de que ya exista la base de datos, en principio hay que usar la contraseña de root. El archivo tiene este formato:
#Domain for WordPress #domain= # Title for WordPress #title= # Mysql root password #mysql_root_pass= # Install path (without final slash) #wp_path= # Charset that will use WordPress #wp_charset= # WordPress locales #wp_locale= # WordPress user with administration rights #wp_admin_user= # Password for WordPress administrator #wp_admin_password= # WordPress administrator email #wp_admin_email= # Host of Mariadb #wp_db_host= # Database user for WordPress #wp_db_user= # Database user for WordPress password #wp_db_password= # Name of database for WordPress #wp_db_name= # User for wp cli. It's needed to execute wp cli. #wpcli_user=
La variables las cargamos en el script con el comando source:
if [[ -e ./conf/config ]];then source ./conf/config; else iferror "First you need configure parameters" fi
En caso de que el script no exista, se llama a la función iferror con un mensaje como parámetro. La función iferror es como sigue:
# function: iferror # produces an exit code 1 with message function iferror { if [[ $? -eq 1 ]]; then echo $1; exit 1; fi }
Y creamos un usuario:
useradd $wpcli_user;
Fuentes
Añadimos los paquetes de PHP7.0, ya que en los repositorios de Debian Jessie sólo existen versiones anteriores.
# function set_php7_sources # add php7 repository to sources.list set_php7_sources() { if ! (grep -qs "dotdeb" /etc/apt/sources.list); then echo "deb http://packages.dotdeb.org jessie all" \ >> /etc/apt/sources.list; fi if ! [[ -e /tmp/dotdeb.gpg ]]; then wget https://www.dotdeb.org/dotdeb.gpg && apt-key add dotdeb.gpg; fi install_dependencies }
Comprobamos que el repositorio no esté en el sources.list y en ese caso el script añade la fuente. La segunda condición comprueba si ya ha sido descargada la clave gpg del repositorio. En caso, contrario la descarga y la añade al anillo de claves. A continuación, llama a la siguiente función.
# function: install_dependencies # Install needed dependencies function install_dependencies { apt-get -y install sudo mariadb-server mariadb-client \ php7.0-mysql php7.0-fpm nginx nginx-extras php7.0 \ && install_WP_cli \ && if ! ( grep -qs $sudoers_root /etc/sudoers ); then echo $sudoers_root >> /etc/sudoers; fi }
Instala todas las dependencias necesarias y añade root como usuario sudo. De entrada, parece absurdo añadir al administrador del sistema como un nuevo administrador, pero no lo es tanto cuando el programa WP-CLI, que llamamos a partir de la función install_WP_cli para instalar Worpress, no permite ser lanzado como root. En cambio, root puede otorgarle permisos a otro usuario, para lo root debe estar en la lista de sudoers…
La función install_dependecies llamó a la función install_WP_cli:
# function: install_WP_cli # Install command line interface for wordpress function install_WP_cli { wget https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar \ || iferror "Failed. WP command line interface not installed."; chmod +x wp-cli.phar; mv wp-cli.phar /usr/local/bin/wp \ || iferror "Failed. /usr/local/bin/wp not created"; if ! (grep -qs $sudoers_wpcli /etc/sudoers); then echo $sudoers_wpcli >> /etc/sudoers fi install_WP }
Descargamos las fuentes de WP-CLI, le asignamos permisos de ejecución y la renombramos a uno de los directorios de variable de entorno PATH para que pueda WP-CLI sea ejecutado llamando al comando wp. Añade, además, al usuario wpcli a sudoers con estos permisos:
sudoers_wpcli="wpcli ALL=(www-data) NOPASSWD: /usr/local/bin/wp"
De este modo, wpcli puede ejecutar /usr/local/bin/wp sin introducir password con los permisos del usuario www-data, el del servidor web. El útlimo paso es la llamada a la función install_WP.
MariaDB
La siguiente función crea la base de datos para WordPress:
# function: create_database # create wordpress data base in mardiadb function create_database { mysql -uroot -p$mysql_root_pass -e "drop database if exists $wp_db_name; create \ database $wp_db_name" \ || iferror "Failed. Database not created"; }
Aquí accede a la base de datos con el usuario root de MariaDB y crea una nueva base de datos para ser usada por WordPress. Si la base de datos no es creada, sale del script.
Creamos un usuario para la base de datos y le otorgamos privilegios:
# function: create_and_grant_user # create user for wordpress and grant all privileges function create_and_grant_user { mysql -uroot -p$mysql_root_pass -e " grant all privileges on $wp_db_name.* to \ '$wp_db_user'@'localhost' identified by '$wp_db_password';" }
Instalación de WordPress
En este punto es donde está la «chicha» del script. Expongo la función y a continuación explico qué hace:
# function: install_WP version path() # install wordpress version at a given path function install_WP { prefix=wp$RANDOM; if ! $(sudo -u $wpcli_user -- wp core is-installed); then create_database; create_and_grant_user; mkdir -p $wp_path && chmod -R 777 $wp_path && chown -R \ www-data:www-data $wp_path; sudo -u $wpcli_user -- wp core download --locale=$wp_locale \ --path=$wp_path \ || iferror "Wordpress not downloaded"; if [ -e $wp_path/wp-config.php ]; then rm $wp_path/wp-config.php; fi sudo -u $wpcli_user -- wp core config --dbname=$wp_db_name \ --dbuser=$wp_db_user --dbpass=$wp_db_password --dbhost=$wp_host \ --dbprefix=$prefix --locale=$wp_locale --path=$wp_path \ || iferror "Wordpress not configured" ; sudo -u $wpcli_user -- wp core install \ --url=$domain --title="$title" --path=$wp_path --admin_user=$wp_admin_user \ --admin_password=$wp_admin_password --admin_email=$wp_admin_email \ --path=$wp_path \ || iferror "Wordpress not installed"; chmod -R 775 $wp_path; nginx_create_site fi }
En la línea 5 se define el prefijo de las tablas de WordPress. Por motivos de seguridad, no utilizo el prefijo por defecto, sino que a la cadena de texto «wp» le añado un numero entero aleatorio.
En la línea 6, el condicional comprueba que WP-cli esté instalado.
Antes de instalar WordPress, llama a las funciones create_database y create_and_grant_user mostradas anteriormente.
Las líneas 10 y 11, crean el directorio raíz para la instancia de WordPress y le cambia permisos y propietarios. El permiso 777 se será cambiado de nuevo al teminar la ejecución de está función.
Línea 13, descarga la última versión de WordPress
Línea 17, comprueba que exista el archivo wp-config.php. Si es el caso (y suele serlo) lo borra para crear uno nuevo en el siguiente paso.
De las líneas 21 a 24, crea un archivo wp-config.php con parámetros definidos en el archivo de configuración.
De las líneas 26 a 30, instala Worpdress con los parámetros que siempre requiere en toda instalación.
Línea 32, modifica los permisos del directorio a 755
Y la línea 33, llama a una función para parsear un archivo de configuración para un host virtual en Nginx.
Nginx
He tomado un archivo de configuracion de Nginx de ejemplo de una publicación para usarlo como plantilla. En todo caso, podéis (y deberíais) utilizar vuestra propia configuración en un servidor en producción. El modo de parsear el archivo será siempre el mismo.
# nginx.available server { listen 80; root ROOTPATH; index index.php index.html index.htm; server_name DOMAIN; location / { try_files $uri $uri/ /index.php?q=$uri&$args; } error_page 404 /404.html; location ~* wp-config.php { deny all; } location = /favicon.ico { log_not_found off; access_log off; } location = /robots.txt { allow all; log_not_found off; access_log off; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } location ~ \.php$ { #NOTE: You should have "cgi.fix_pathinfo = 0;" in php.ini fastcgi_pass unix:/var/run/php/php7.0-fpm.sock; fastcgi_pass_header Set-Cookie; fastcgi_pass_header Cookie; fastcgi_ignore_headers Cache-Control Expires Set-Cookie; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; fastcgi_split_path_info ^(.+.php)(/.+)$; fastcgi_param PATH_INFO $fastcgi_path_info; fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info; fastcgi_intercept_errors on; fastcgi_cache_valid 404 60m; fastcgi_cache_valid 200 60m; fastcgi_cache_valid 304 60m; fastcgi_max_temp_file_size 60m; fastcgi_cache_use_stale updating; fastcgi_index index.php; include fastcgi_params; } set $cache_uri $request_uri; # POST requests and URLs with a query string should always go to PHP if ($request_method = POST) { set $cache_uri 'null cache'; } if ($query_string != "") { set $cache_uri 'null cache'; } # Don't cache URIs containing the following segments if ($request_uri ~* "(/wp-admin/|/xmlrpc.php|/wp-(app|cron|login|register|mail).php|wp-.*.php|/feed/|index.php|wp-comments-popup.php|wp-links-opml.php|wp-locations.php |sitemap(_index)?.xml|[a-z0-9_-]+-sitemap([0-9]+)?.xml)") { set $cache_uri 'null cache'; } # Don't use the cache for logged-in users or recent commenters if ($http_cookie ~* "comment_author|wordpress_[a-f0-9]+|wp-postpass|wordpress_logged_in") { set $cache_uri 'null cache'; } location ~*\.(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg|jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid|midi|wav|bmp|rtf)$ { expires max; log_not_found off; access_log off; } }
Esta plantilla será parseada por el script y atención a las líneas 5 y 8: contienen respectivamente las cadenas ROOT y DOMAIN. Ambas serán reemplazadas por los valores adecuados a la instalación. ROOT será el directorio raíz de WordPress y DOMAIN el nombre de dominio.
# function: nginx_create_site # Configure site for nginx function nginx_create_site { if [ -e ./nginx/nginx.available ]; then sed -e "s|ROOTPATH|$wp_path|g" \ -e "s|DOMAIN|$domain|g" ./nginx/nginx.available > \ /etc/nginx/sites-available/$domain.conf \ || iferror "Site not available"; else iferror "nginx.available does not exists"; fi nginx_enable_site }
Comprobamos que exista la plantilla llamada nginx.available, entonces reemplaza los patrones ROOT y DOMAIN con la ayuda del comando sed y escribe el archivo en sites-available. Sigue la ejecución llamando a la función nginx-enable-site.
# function: nginx_enable_site # Enable site in Nginx function nginx_enable_site { if [[ -e /etc/nginx/sites-enabled/default ]]; then rm -f /etc/nginx/sites-enabled/default; fi if [[ -e /etc/nginx/sites-enabled/$domain ]]; then rm -f /etc/nginx/sites-enabled/$domain fi ln -s /etc/nginx/sites-available/$domain.conf /etc/nginx/sites-enabled/. \ || iferror "sites-enabled/$domain not created"; systemctl reload nginx; }
Desahibilitamos el servidor por defecto de Nginx y también, en caso de existir, una versión antigua del archivo del dominio que estamos instalando. Podría ser que exista si anteriormente hemos ejecutado una instalación fallida. Por último, habilitar el servidor virtual y Nginx recarga los servidores.
Verificación de root
El script debe ejecutare como root. Para comprobar que, efectivamente, sea así, al comienzo de la ejecución del script:
if [ "$(id -u)" != "0" ]; then echo "This script must be run as root" 1>&2 exit 1 fi
Inicio
Esta función actualiza los repositorios, instala sudo y lanza todo el script.
function start { apt-get update && apt-get -y install sudo && set_php7_sources }
Y se llama a start al final de script.
Código fuente
El script no ha sido explicado exactamente en el mismo orden en que se encuentran las funciones escritas en el código fuente. Creo que así se facilita la compresión de las partes para entender el script al completo. El script está en mi github y podéis utilizarlo tal cual está en una Debian Jessie. Es posible que, tras la explicación, se vea más claro visto en conjunto:
https://github.com/gustavomrfz/automate_wordpress
Conclusión
Sin duda, este script es unos de los muchos posibles. Muchos otros modos de automatizar la instalación serán mejores; otros solo serán diferentes. Creo que sólo automatizando procesos es como puedo asegurar que he entendido lo que ocurre cuando realizo, por ejemplo, una instalación de WordPress. Es un gozo, y un ahorro de tiempo, instalar prácticamente editando un archivo de parámetros y poco más. En breve publicaré un script explicando la desintalación automatizada de WordPress, aunque ya podéis ver el script en el github.
Cualquier comentario sensato o sugerencia serán bienvenidos.
Saludos 🙂
Este post forma parte de la serie Automatizaciones
2 Comments
Comments are closed.