/ Ghost

Instalar Ghost en Ubuntu 16.04 - Parte 1

Como he comentado en el post sobre las distintas plataformas de blog que estuve barajando para montar este blog, elegí Ghost como CMS. Pues bien, vamos a explicar cómo realizar la instalación y puesta en funcionamiento de Ghost sobre Ubuntu 16.04 para poder comenzar a bloguear.

Quiero hacer una aclaración antes de comenzar. La instalación del CMS Ghost no requiere de muchos de los pasos que vamos a dar a continuación, pero como estamos preparando una imagen de sistema para poner en producción, queremos que sea robusta, por lo que vamos a trabajar también en temas de seguridad.

Voy a dividir la instalación en distintos puntos:

Elegir servidor donde instalar Ghost

A pesar de que Ghost permite a través de su página TryGhost crearte un blog sin necesidad de realizar ningún tipo de complicación, nosotros la hemos montado sobre nuestra propia plataforma. Para ello, tan solo tienes que hacerte con los servicios de un servidor web donde te permita acceso root para poder administrar los servicios del sistema sobre el que vas a trabajar. En mi caso, la facilidad que permite DigitalOcean para crear y manejar nuestras máquinas, seleccionando las necesidades en cada caso, es lo que ha hecho decantarnos por su servicio. Podéis ver más información aquí. Aun así, más adelante crearemos un post con información sobre el funcionamiento de DigitalOcean.

Como las necesidades de un blog en Ghost con poca carga no son excesivas, y además no esperamos picos de visitas de ningún tipo, hemos elegido el plan más básico. Para nuestro servidor, hemos seleccionado el plan de 512Mb de RAM con 1 core y 20Gb de almacenamiento, con un coste de 5$. Una vez elegimos el plan, tenemos la posibilidad de elegir el sistema operativo a instalar en dicha máquina, y nosotros, como indicamos anteriormente, vamos a hacerlo sobre Ubuntu 16.04. Una vez tenemos la máquina creada, nos conectamos a ella vía SSH.

Primera conexión y configuración base

Vamos a sobreentender que estáis trabajando con un sistema Linux o en su defecto con algún sistema que os permita conectar por ssh, por lo que vamos a establecer la conexión.

$ ssh [usuario]@[ip]

Tras conectarnos, lo primero que hemos de hacer es actualizar la paquetería:

$ sudo apt-get update && sudo apt-get upgrade -y

Configuración de Firewall

Teniendo en cuenta que en el momento que levantemos el servicio estaremos expuestos a todo tipo de conexiones desde la red, lo primero y más importante es instalar y configurar una primera capa de seguridad. Por ello, lo primero que vamos a instalar es un Firewall. A pesar de que hay varias opciones, a cada cual más compleja y configurable, vamos a elegir Uncomplicated Firewall, o UFW. El paquete debe venir instalado por defecto en la instalación de Ubuntu, pero por si acaso lo instalamos.

$ sudo apt-get install ufw

Para confirmar si está funcionando, podemos lanzar:

$ sudo ufw status
Estado: Inactivo

Lo normal es que el servicio esté inactivo por defecto. Antes de activarlo, vamos a configurar algunas reglas para no quedarnos sin conexión con la máquina.

Primero vamos a bloquear todo el tráfico entrante por defecto y permitir el saliente.

$ sudo ufw default deny incoming  
$ sudo ufw default allow outgoing  

Ahora pasaremos a configurar servicios concretos. En nuestro caso usaremos los servicios SSH, HTTP y también habilitaremos el HTTPS para una futura implementación. Como extra, añadiremos el puerto 2368/TCP que es el que usa Ghost por defecto.

$ sudo ufw allow ssh
$ sudo ufw allow http
$ sudo ufw allow https
$ sudo ufw allow 2368/tcp

Una vez ya tenemos las reglas básicas configuradas, ahora sí, pasamos a habilitar el servicio UFW.

$ sudo ufw enable
El cortafuegos está activo y habilitado en el arranque del sistema.

Para comprobar que hemos hecho correctamente la configuración, revisamos los puertos abiertos de nuevo con status, pero en este caso usamos la opción verbose para que nos refleje más información.

$ sudo ufw status verbose
Estado: activo
Acceso: on (low)
Predeterminado: deny (entrantes), allow (salientes), disabled (enrutados)
Perfiles nuevos: skip

Hasta                      Acción      Desde
-----                      ------      -----
22                         ALLOW IN    Anywhere                  
80                         ALLOW IN    Anywhere                  
443                        ALLOW IN    Anywhere                  
2368/tcp                   ALLOW IN    Anywhere                  
22 (v6)                    ALLOW IN    Anywhere (v6)             
80 (v6)                    ALLOW IN    Anywhere (v6)             
443 (v6)                   ALLOW IN    Anywhere (v6)             
2368/tcp (v6)              ALLOW IN    Anywhere (v6)    

La anotación (V6) hace referencia a estar preparado para el protocolo IPV6.

Instalación de paquetería NodeJS y NPM

OJO: Los usuarios con 512Mb de RAM, como es el caso de la máquina que tenemos en DigitalOcean, tendremos problemas en la instalación de paquetes. Si es tu caso, debes realizar antes estos pasos. El problema es que nos quedamos sin RAM y la instalación no puede finalizar. La solución esta en crearnos un archivo para Swap. Sigue los pasos que explicamos aquí.

Para instalar Ghost, debemos resolver sus dependencias. Ghost está escrito en NodeJS, y utiliza el gestor de paquetes NPM, por lo que vamos a instalar ambos. En Ubuntu 16.04 vienen unas versiones estables que son completamente funcionales y compatibles con Ghost, por lo que no vamos a complicarnos y lo instalaremos desde el propio repositorio. Además, instalaremos el paquete nodejs-legacy ya que en esta versión de Ubuntu se ha cambiado la nomenclatura del ejecutable de node a nodejs, y muchos scripts e instalaciones pueden crear conflictos.

$ sudo apt-get install nodejs nodejs-legacy npm zip -y

Instalación del paquete Ghost

Tras tener toda la paquetería preparada, vamos a instalar el Ghost. Para ello nos descargaremos la última versión estable y la instalaremos sobre la carpeta /var/www. Esta carpeta no existe, así que lo primero que haremos será crearla. Una vez descargado, lo descomprimimos.

$ sudo mkdir -p /var/www/
$ cd /var/www/
$ sudo wget https://ghost.org/zip/ghost-latest.zip
$ sudo unzip -d ghost ghost-latest.zip

Para ganar un poco más de seguridad, vamos a crear un usuario específico para que ejecute el servicio de Ghost.

$ sudo useradd -m -s /bin/bash ghost
$ sudo passwd ghost

Una vez creado el usuario e introducida la contraseña, vamos a darle permisos a la carpeta, cambiamos al usuario que hemos creado, accedemos a ella, y procedemos con la instalación del paquete Ghost.

$ sudo chown -R ghost:ghost ghost
$ su ghost
ghost$ cd ghost/
ghost$ npm install --production

En este paso empezará a descargar dependencias, por lo que podría tardar unos 5 minutos.

El parámetro --production en la instalación del paquete es para evitar descargar paquetes de desarrollo, que para un uso normal del blog no nos va a hacer falta.

Tras la descarga e instalación de las dependencias, y si no encontramos ningún tipo de errores, ya podremos arrancar nuestra máquina con Ghost. La primera vez que la arrancamos, tendremos una salida parecida a la siguiente:

ghost$ npm start

> ghost@0.11.2 start /var/www/ghost
> node index

WARNING: Ghost is attempting to use a direct method to send email. 
It is recommended that you explicitly configure an email service.
Help and documentation can be found at http://support.ghost.org/mail.

Migrations: Creating tables...
Migrations: Creating table: posts
Migrations: Creating table: users
Migrations: Creating table: roles
Migrations: Creating table: roles_users
Migrations: Creating table: permissions
Migrations: Creating table: permissions_users
Migrations: Creating table: permissions_roles
Migrations: Creating table: permissions_apps
Migrations: Creating table: settings
Migrations: Creating table: tagsp/43bb2cea-a585-49f9-b736-91b5ff36a363/
Migrations: Creating table: posts_tags
Migrations: Creating table: apps
Migrations: Creating table: app_settings
Migrations: Creating table: app_fields
Migrations: Creating table: clients
Migrations: Creating table: client_trusted_domains
Migrations: Creating table: accesstokens
Migrations: Creating table: refreshtokens
Migrations: Creating table: subscribers
Migrations: Running fixture populations
Migrations: Creating owner
Ghost is running in development... 
Listening on 127.0.0.1:2368 
Url configured as: http://localhost:2368 
Ctrl+C to shut down

Lo que viene a explicarnos, basicamente, es que se han creado todas esas tablas en la base de datos, que en este caso es SQLite3, que por cierto se aloja en content/data/ghost-dev.db.

Como podéis ver, el puerto sobre el que ha arrancado Ghost es el 2368, ya que es el que viene configurado por defecto en el archivo config.js. Dentro de dicho archivo también tendremos que modificar la URL de escucha, ya que de momento, se encuentra exclusivamente para localhost. Para ello, editamos el archivo.

ghost$ nano config.js

Dentro de dicho fichero encontraremos una de las caracteristicas principales de Ghost, y es que dispone de 2 entornos, uno para desarrollo y otro para producción. Esto permite a los desarrolladores no tener que tirar la web ni probar desarrollos en vivo mientras se están mostrando errores al publico. Dentro de dicho fichero, tendremos que modificar la url y el host en la configuración de ambos entornos:

production: {
        url: 'http://my-ghost-blog.com',
        ...
        server: {
            host: '127.0.0.1',
            port: '2368'
        }
    },

por...

production: {
        url: 'http://tudominio.com',
        ...
        server: {
            host: '0.0.0.0',
            port: '2368'
        }
    },

Y en la parte del entorno de desarrollo igual:

development: {
        ...
        server: {
            ...
            host: '127.0.0.1',
            ...
            port: '2368'
        },
        ...
    },

Lo sustituiremos por...

development: {
        ...
        server: {
            ...
            host: '0.0.0.0',
            ...
            port: '2368'
        },
        ...
    },

Tras esto, podremos lanzar el entorno de producción y acceder a la web.

ghost$ npm start --production

Y si accedemos a la url, podremos ver nuestro blog: http://tuipdeservidor:2368

Ahora tan solo tendremos que entrar en la url de administración (http://tuipdeservidor:2368/ghost/) y se nos abrirá el asistente que nos pedirá nuestro primer usuario, y tras ello ya podremos empezar a escribir.

Como lo normal no es tener la web en el puerto 2368, sino en el 80 que es el que por defecto detecta el navegador, podemos cambiarlo también en el fichero de configuración en los parámetros mostrados anteriormente. De todas formas, nosotros los vamos a dejar así porque el siguiente post será sobre cómo hacer que Ghost pase por un proxy NGinx.

Creación de un servicio para Ghost

Os habréis fijado que para que pueda funcionar el servidor Ghost, tiene que estar tirado el comando anterior. Pero podemos hacer que se arranque de forma automática creando un servicio y añadiéndolo al arranque del sistema. Para ello haremos uso de SystemD, puesto que en las nuevas distribuciones de Ubuntu está por defecto instalado.

Para ello, lo primero que haremos será salir de la cuenta de usuario Ghost con la que accedimos antes, y con permisos de administración, crearemos el fichero /etc/systemd/system/ghost.service

ghost$ exit
$ sudo nano /etc/systemd/system/ghost.service

A dicho fichero le añadiremos el siguiente contenido:

[Unit]
Description=Servidor GHOST
After=network-online.target

[Service]
Type=simple  
PIDFile=/run/ghost.pid
WorkingDirectory=/var/www/ghost/
User=ghost  
Group=ghost
ExecStart=/usr/bin/npm start --production  
ExecStop=/usr/bin/npm stop /var/www/ghost/
StandardOutput=null
StandardError=null

[Install]
WantedBy=multi-user.target 

A pesar de que más adelante hablaremos de SystemD y explicaremos cómo crear servicios con él, vamos a detallarlo un poco. Igualmente podéis encontrar más información de cómo crear unidades aquí.

En SystemD los servicios se llaman Units (unidades), y el primer bloque es directamente información de dicha unidad:

[Unit]
# Descripción de la unidad
Description=Servidor GHOST
# Cuándo se arranca la unidad. En este caso tras la carga de la interfaz de red
After=network-online.target

El segundo bloque explica exactamente lo que hará esta unidad:

[Service]
Type=simple
# El PID que se le asignará a la unidad
PIDFile=/run/ghost.pid  
# Este es el directorio donde está instalado ghost
WorkingDirectory=/var/www/ghost/
# El usuario y grupo con el que trabajaremos
User=ghost  
Group=ghost
# El comando a ejecutar en dicho directorio para arrancar y parar el servicio
ExecStart=/usr/bin/npm start --production  
ExecStop=/usr/bin/npm stop /var/www/ghost/
# Si queremos que se registre la entrada y salida de dicho comando
StandardOutput=null
StandardError=null

El bloque install da información sobre la instalación de SystemD

[Install]
WantedBy=multi-user.target  

Una vez que tenemos la unidad creada, daremos permisos de ejecución y tendremos que notificar al sistema que dicho servicio existe, y ya podremos ejecutarla directamente como un servicio del sistema.

$ sudo systemctl reenable ghost
$ sudo systemctl daemon-reload
$ sudo service ghost start

El servicio también se puede manejar a través de la interfaz de Systemctl, haciendo uso del comando:

$ sudo systemctl start ghost.service

Una vez tenemos configurado el servicio, cada vez que arranquemos el sistema, automáticamente el usuario ghost arrancará el servicio y no tendremos que intervenir.

Para comprobar que la instalacón del servicio ha salido correcta, ejecutaremos el siguiente comando:

$ sudo service ghost status
● ghost.service - Servidor GHOST
   Loaded: loaded (/etc/systemd/system/ghost.service; disabled; vendor preset: enabled)
   Active: active (running) since jue 2016-10-20 20:00:16 CEST; 7s ago
 Main PID: 26330 (npm)
    Tasks: 19
   Memory: 113.6M
      CPU: 6.866s
   CGroup: /system.slice/ghost.service
           ├─26330 npm                                            
           ├─26342 sh -c node index
           └─26343 node index

oct 20 20:00:16 ghost systemd[1]: Started Servidor GHOST.

Si la salida es como la mostrada, veréis que no tendréis ningún error y podreís acceder correctamente a la web de Ghost.

En los siguientes post vamos a centrarnos un poco más en la seguridad del servidor y en la optimización del mismo, por lo que estad pendiente.

Solución de posibles errores

Problema de RAM

Para aquellos que tenis problemas con la memoria en DigitalOcean, seguid los siguientes pasos. Lo primero que haremos será confirmar que no tenemos SWAP montada el sistema:

$ sudo swapon -s  

Si no obtenemos salida ninguna, es que efectivamente no tenemos ninguna partición Swap cargada en el sistema, por lo que tendremos que crearla.

Para ello, crearemos un fichero de 1Gb que sáer el que usaremos como Swap y le daremos los permisos pertinenes. Todo esto lo haremos con el usuario root, no el que hemos creado, por lo que tendremos que salir de la sesión de dicho usuario:

$ sudo fallocate -l 1G /swapfile
$ sudo chmod 600 /swapfile

Tras esto creamos la Swap en dicho fichero y obtendremos una salida parecida a esta:

$ sudo mkswap /swapfile
Setting up swapspace version 1, size = 1.0G  
no label, UUID=bb58c487-1d34-4303-beb1-23484a956bdb  

Ahora la activaremos.

$ sudo swapon /swapfile  

Para confirmar que ya tenemos la Swap activa, volveremos a ejecutar:

$ sudo swapon -s

Ahora, tan sol tenemos que añadirla al fichero /etc/fstab para que se inicie al arrancar el sistema. Al editarlo, añadimos la siguientíe lnea:

$ sudo nano /etc/fstab
/swapfile   none    swap    sw    0   0

Como último paso, y para no sobrecargar demasiado la lectura y escritura de los discos SSD de DigitalOcean, vamos a reducir el Swapiness. Para ello realizamos lo siguiente.

$ sudo sysctl vm.swappiness=10

Y también editaremos el siguiente fichero:

$ sudo nano /etc/sysctl.conf  

Para añadir en la última línea:

vm.swappiness=10  

Con esta configuración básica ya tendremos la Swap funcionando y podremos ejecutar el instalador sin problemas. Recordar volver al usuario que creamos previamente ghost:

$ su ghost