Variables de entorno y docker

Después de un tiempo definiendo distintos contenedores docker y coordinando despliegues con docker compose me he encontrado con circunstancias que me obligaban a modificar los archivos de definición cada vez que necesitaba hacer un nuevo despliegue o paso a producción y eso, bueno, eso es un poco molesto. Así que sabiendo que la gente que ha desarrollado todo esto era más lista que yo me puse a buscar cómo proponen que lo hagamos.

Y la respuesta es… Mediante varibles de entorno (al menos una de ellas), así que vamos a ver cómo podemos, como ejercicio, poner el número de versión de nuestro despliegue como varible de entorno..

Uno de mis docker-composes tenía este aspecto:

    web-php:
        container_name: web-php
        build: .
        image: nexus.biblioetech.com/biblioeteca/nomorepass:1.0.0
        ports:
            - "80:80"
        environment: 
            DBHOST: "db"
        links:
            - "web-mysql:db"

Como véis el número de versión acompañaba al nombre de la imagen y esto es así para que al construirla ya llevase la etiqueta adecuada para subirla al repositorio privado. El problema era que cada vez que hacíamos una release nueva teníamos que tocar este archivo a mano… Si queremos no tener que tocar el archivo tenemos que poner algo así:

    web-php:
        container_name: web-php
        build: .
        image: nexus.biblioetech.com/biblioeteca/nomorepass:${VERSION}
        ports:
            - "80:80"
        environment: 
            DBHOST: "db"
        links:
            - "web-mysql:db"

De esta forma solo hay que definir la varible de entorno VERSION al valor que acabamos de generar. Ahora bien, es fácil que nos olvidemos de asignar esta variable de entorno si no está en ningún sitio del repositorio, así que lo más sencillo es incluirla en un archivo .env que será el que docker-compose cargue antes de ejectuar el build… Y quedaría así:

VERSION=1.0.0

En este archivo podemos incluir todas las variables que necesitemos y, lo que es más, podremos pasarselas al Dockerfile si lo necesitamos, eso si, el método es un poco más rebuscado (eso lo veremos un poco más adelante).

Mover el directorio docker en ubuntu

Usar docker es sencillo y rápido, pero, como todo, tienes que tener cuidado con todo lo que ocupa espacio ya que los discos y las particiones son finitas… Y los contendores docker ocupan espacio (bastante), así que aquí dejo el método para mover el directorio de datos de la partición por defecto a otra cualquiera que queramos y para la que tengamos espacio de sobra.

Docker y ubuntu

Lo primero que hay que saber es que el directorio por defecto donde docker guarda sus imágenes, contenedores y demás cosas que ocupan espacio es:

/var/lib/docker

Que, generalmente, está en la partición raiz (o la que tengamos para var) que no suele ser muy grande. Supongamos que queremos moverla a un disco de datos con capacidad suficiente como este:

/mnt/disks/data/docker

Para ello lo que tendremos que hacer es: parar el servicio docker

sudo service docker stop

Crear un archivo llamado daemon.json en el directorio /etc/docker con este contenido:

{ 
   "graph": "/path/to/your/docker" 
}

Copiamos el directorio original al nuevo y lo renombramos por seguridad (ya lo borraremos después)

sudo rsync -aP /var/lib/docker/ /path/to/your/docker
sudo mv /var/lib/docker /var/lib/docker.old

Reiniciamos el servicio docker y todo debería estar como antes del cambio (podéis comprobar con un docker ps si se ha levantado todo lo que teníais funcionando antes).

sudo service docker start

Después de comprobar que todo está ok, ya podemos borrar el original y habremos terminado con la operación del todo:

sudo rm -rf /var/lib/docker.old

Montar un wordpress con docker

Si, esta es una receta muy sencilla y muy rápida. Quizá no sea perfecta, pero en caso de que quieras montar un servidor wordpress sin preocuparte de instalaciones y milongas, este es el método.

Voy a suponer que ya sabes lo que es docker y docker-compose, es más, voy a dar por hecho que los tienes instalados en el servidor donde quieres instalar wordpress, si eso no es así vete disparado a google y búscalo (me lo agradecerás).

Vamos a hacer una instalación donde la base de datos y los archivos de wordpress estén en directorios de la misma máquina y no dentro del contenedor, de manera que podamos reiniciar y actualizar los contenedores preservando los datos. Para ello creamos una estructura haciendo algo similar a esto (pongo como se hace en linux):

mkdir wordpress
cd wordpress
mkdir db
mkdir archivos
mkdir docker
cd docker

Ahora viene lo bueno, creamos dentro del directorio docker el archivo docker-compose.yml con este contenido:

version: '2'
services:
  db:
    image: mysql:5.7
    volumes:
      - "../db:/var/lib/mysql"
    restart: always
    environment:
      MYSQL_ROOT_PASSWORD: wordpress
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wordpress

  wordpress:
    depends_on:
      - db
    image: wordpress:latest
    links:
      - db
    ports:
      - "80:80"
    volumes:
      - ../archivos:/var/www/html
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_PASSWORD: wordpress

Y con esto ya tenemos todo el pescado vendido. Nos metemos en el directorio docker (donde está el docker-compose.yml) y ejecutamos:

docker-compose up -d

Puedes comprobar que todo está funcionando en cualquier momento con el comando docker ps

CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                 NAMES
 18b4c06458b7        wordpress:latest    "docker-entrypoint.s…"   36 seconds ago      Up 32 seconds       0.0.0.0:80->80/tcp    docker_wordpress_1
 211c0832d549        mysql:5.7           "docker-entrypoint.s…"   39 seconds ago      Up 35 seconds       3306/tcp, 33060/tcp   docker_db_1

Ahora, para instalar, de verdad, el wordpress debemos conectar a http://localhost y podremos comenzar la instalación:

Y después de poner todos los datos tendremos un wordpress funcional en el puerto 80, con el añadido de que para realizar un backup solo tenemos que parar los contenedores y copiar los directorios db y archivos (o llevárnoslos a otro servidor).

Hay muchas mejoras que hacer si queremos que esto esté listo para producción como, por ejemplo, hacer que vaya por https (para eso lo mejor es poner un proxy inverso), pero ya lo veremos un poco más adelante. Por ahora, si queréis parar los servicios solo tenéis que ejecutar, desde el directorio docker la instrucción:

docker-compose stop

Y, lo bueno, es que tu máquina no ha sufrido ningún cambio de configuración ni ha instalado ningún paquete de más… Todo ventajas.

Subir a maven central una librería propia

Ahora que ya acabas de construir una librería interesante en Java, la has hecho pública (en github, por ejemplo) y quieres que todo el mundo la use… Queda una tarea pendiente, subirla a un repositorio maven para ponerla a disposición de los que utilicen este sistema (o gradle, que hoy en día ya son casi todos).

Vamos a verlo con un ejemplo que he subido esta mañana… Hay cosas que todavía no entiendo del todo, pero el resultado ha sido bueno, por lo que, al menos, podremos usar esta receta como guía para próximas veces.

El código que intento subir es una librería simple que tengo alojada en github con su pom.xml básico y que si te descargas el proyecto podrías compilar e instalar en tu maven con mvn install. La dirección es esta:

https://github.com/yoprogramo/nomorepass-java/

Ahora, para que todo el mundo pueda descargárselo como dependencia y no tenga que hacer el mvn install del proyecto, tenemos que subirlo a un repositorio público, podemos ver una guía en esta página: Guide to Public Maven Repositories. Tal como explican en la página, lo más sencillo para publicar en Maven Central es usar el repositorio Sonatype. Dicho y hecho… Lo intentamos por aquí.

Lo primero es crear una cuenta en el Jira de Sonatype aquí. Lo siguiente, y esto es un poco «tricky» es crear un ticket solicitando un nuevo id de grupo en esta dirección. No se puede pedir cualquier id de grupo (en mi caso quería pedir com.nomorepass) y generalmente se pedirá alguna prueba de que el dominio es tuyo. En mi caso este es el ticket que creé: https://issues.sonatype.org/browse/OSSRH-49426, para demostrar que el dominio era mío cambié el DNS e incluí una entrada TXT con el identificador del ticket:

Una vez autorizado (tarda un poco, es un proceso manual) hay que modificar nuestro código y prepararlo para la subida, pero, antes de eso, tenemos que generar nuestras claves gpg para poder firmar el código. eso se hace con este comando:

gpg --gen-key

Una vez generada podremos acceder a la lista de claves con el comando:

gpg --list-keys

Toma nota del id de la clave y recuerda la contraseña que usaste para generarla, porque tendrás que recordarla. Además, tendrás que publicarla en algún servidor de claves públicas para que pueda ser comprobada.

gpg --keyserver hkp://keys.gnupg.net --send-keys <el-id-de-la-clave>

Ahora empezamos a modificar el pom.xml para que cumpla con los requisitos para el repositorio Maven Central. En nuestro caso pusimos esto:

<groupId>com.nomorepass</groupId>
  <artifactId>nomorepass</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>

  <name>Nomorepass java library</name>
  <description>NoMorePass protocol 2 implemented on Java.</description>
  <url>https://nomorepass.com</url>

  <licenses>
    <license>
      <name>Apache License, Version 2.0</name>
      <url>http://www.apache.org/licenses/LICENSE-2.0.txt</url>
      <distribution>repo</distribution>
    </license>
  </licenses>

  <developers>
    <developer>
      <name>Jose Antonio Espinosa</name>
      <email>[email protected]</email>
      <organization>Nomorepass</organization>
      <organizationUrl>https://nomorepass.com</organizationUrl>
    </developer>
  </developers>

  <scm>
    <connection>scm:git:git://github.com/yoprogramo/nomorepass-java.git</connection>
    <developerConnection>scm:git:ssh://github.com:yoprogramo/nomorepass-java.git</developerConnection>
    <url>https://github.com/yoprogramo/nomorepass-java/tree/master</url>
</scm>

Y, una vez informado de todo esto, hay que incluir los plugins que nos permitirán hacer el despliegue directamente. Yo añadí esto:

<distributionManagement>
    <snapshotRepository>
      <id>ossrh</id>
      <url>https://oss.sonatype.org/content/repositories/snapshots</url>
    </snapshotRepository>
    <repository>
      <id>ossrh</id>
      <url>https://oss.sonatype.org/service/local/staging/deploy/maven2/</url>
    </repository>
</distributionManagement>

Y puse en mi directorio de maven settings.xml los datos de mi usuario

<settings>
  <servers>
    <server>
      <id>ossrh</id>
      <username>xxxxxxxxxx</username>
      <password>xxxxxxxxxx</password>
    </server>
  </servers>
</settings>

Por último, toda la sección de build (que no tenía) la sustituí por esto:

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-gpg-plugin</artifactId>
        <executions>
          <execution>
            <id>sign-artifacts</id>
            <phase>verify</phase>
            <goals>
              <goal>sign</goal>
            </goals>
          </execution>
        </executions>
      </plugin>
      <plugin>
        <groupId>org.sonatype.plugins</groupId>
        <artifactId>nexus-staging-maven-plugin</artifactId>
        <version>1.6.7</version>
        <extensions>true</extensions>
        <configuration>
          <serverId>ossrh</serverId>
          <nexusUrl>https://oss.sonatype.org/</nexusUrl>
          <autoReleaseAfterClose>true</autoReleaseAfterClose>
        </configuration>
      </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-source-plugin</artifactId>
          <version>2.2.1</version>
          <executions>
            <execution>
              <id>attach-sources</id>
              <goals>
                <goal>jar-no-fork</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-javadoc-plugin</artifactId>
          <version>2.9.1</version>
          <executions>
            <execution>
              <id>attach-javadocs</id>
              <goals>
                <goal>jar</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
    </plugins>
</build>

Y ya, finalmente, pude ejecutar el mágico:

mvn clean deploy

Si todo ha ido bien, el artefacto estará subido a un repositorio que tendremos que promocionar a «Release» para que se sincronice con el repositorio central… Pero al final ya lo tendremos disponible para todo el mundo…

Aquí podéis encontrar lo que acabo de subir: https://search.maven.org/artifact/com.nomorepass/nomorepass/1.0/jar

Protege tu .git

Hasta hace poco en mi empresa utilizábamos subversion como repositorio, no somos un equipo grande y las funcionalidades que nos ofrecía el repositorio eran suficiente para nuestros proyectos.
Recientemente, debido a que un cliente nos ha impuesto utilizar git como repositorio principal y dado que nuestra relación con ellos es muy importante, decidimos mover todos nuestros repositorios a git. Tampoco es que vayamos a utilizar extensivamente las ventajas que nos ofrece, pero si que nos obligaría a funcionar de manera más fluida con una herramienta que vamos a necesitar si-o-si.

El caso es que, en nuestra anterior configuración, utilizábamos subversion para mantener el código de producción de algunas webs y al modificar el repositorio hicimos lo propio con git, teniendo una «feliz transición». El problema vino en que, realmente, no eramos conscientes de las diferencias reales que tenían los dos repositorios y dentro de los directorios servidos junto a la web en cuestión se encontraba el directorio .git.

¿Qué significa esto? Pues ni más ni menos que todo el mundo mundial tiene acceso a tu repositorio local y puede, entre otras cosas, acceder a todo el código de lo que hay allí publicado… Y eso no puede ser. ¿Qué hacemos para evitarlo?

Hay varias formas de hacerlo, dependiendo de si tienes o no un .htaccess en tu web o no y de la configuración de tu servidor, en mi caso la solución que implementamos fue añadir las siguientes líneas al archivo de configuración de cada web:

        <Directory /directorio.de.la.web/.git>
		Options FollowSymLinks
		AllowOverride All
		Require all denied
	</Directory>

Esto indica al servidor que todo lo que hay bajo el directorio .git no está autorizado para ser visto… Reiniciamos el servidor o recargamos la configuración y ya tendremos el problema resuelto.

Y con esto y un bizcocho… Podemos empezar nuestra semana.