Sunday 23 September 2018

Cleaning up old Docker images in Jenkins

When using Jenkins for building our Docker images we will start suffering problems with the high disk usage because the images are kept stored and they are not deleted by default. The simplest solution would be to delete the image from disk as part of the Jenkins job/pipeline, once the image has been pushed to our Docker Registry.

The major problem of this approach is that on every build triggered, Docker will have to build the new image from scratch and it will not be able to leverage on the Docker layer caching. As you know, when building a new image each instruction within the Dockerfile is examined, and Docker looks for an existing image in its cache that it can reuse, rather than creating a new (duplicate) image. This implies that the building of new images will probably be much slower. So ideally we would like to keep on disk at least the latest version of our image, so that Docker can reuse previous created layers for building the new image quickly.

As an example, suppose we have a job/pipeline in Jenkins for building our images called "myDockerImage", using the Jenkins build number as image tags.

$ docker images
REPOSITORY                TAG                   IMAGE ID            CREATED             SIZE
myDockerImage             456                   1fdcfbbb6e95        2 days ago          1.2GB
myDockerImage             455                   e7deb97e4677        2 weeks ago         1.2GB
myDockerImage             454                   c4297a8dc099        2 weeks ago         1.2GB
myDockerImage             453                   36f69b5805d9        2 weeks ago         1.2GB
...                       ...                   ...                 ...                 ...
centos                    6.8                   d63aca2e7714        6 weeks ago         195MB
nginx                     1.15.2-alpine         36f3464a2197        8 weeks ago         18.6MB
postgres                  alpine                e6c5e6a76255        8 months ago        38.2MB
rabbitmq                  3.6.14-alpine         5a9e735a5ad8        8 months ago        38.9MB
openjdk                   8u151-jre-alpine3.7   b1bd879ca9b3        8 months ago        82MB
mysql                     5.7                   c356247174ed        2 months ago        372MB
varnish                   latest                ec0406c252f9        3 months ago        161MB
alpine                    3.7                   3fd9065eaf02        8 months ago        4.15MB
ruby                      2.3.1                 ffe8239a147c        22 months ago       730MB

As you can see, we have other images stored as well. Now, how could we delete all images of "myDockerImage" except the one we have just created? Let's take a look to the different options to the "docker images" command:

$ docker images --help

Usage: docker images [OPTIONS] [REPOSITORY[:TAG]]

List images

Options:
  -a, --all             Show all images (default hides intermediate images)
      --digests         Show digests
  -f, --filter filter   Filter output based on conditions provided
      --format string   Pretty-print images using a Go template
      --no-trunc        Don't truncate output
  -q, --quiet           Only show numeric IDs

The first step would be to list only the desired image (myDockerImage):

$ docker images myDockerImage
REPOSITORY                TAG                   IMAGE ID            CREATED             SIZE
myDockerImage             456                   1fdcfbbb6e95        2 days ago          1.2GB
myDockerImage             455                   e7deb97e4677        2 weeks ago         1.2GB
myDockerImage             454                   c4297a8dc099        2 weeks ago         1.2GB
myDockerImage             453                   36f69b5805d9        2 weeks ago         1.2GB
...                       ...                   ...                 ...                 ...

Then we could use the filter option (-f) for listing only the images that were built "before" the one we have just built:

$ docker images --filter "before=myDockerImage:456" myDockerImage
REPOSITORY                TAG                   IMAGE ID            CREATED             SIZE
myDockerImage             455                   e7deb97e4677        2 weeks ago         1.2GB
myDockerImage             454                   c4297a8dc099        2 weeks ago         1.2GB
myDockerImage             453                   36f69b5805d9        2 weeks ago         1.2GB
...                       ...                   ...                 ...                 ...

Then we will use the option -q for listing only the numeric IDs, and the command "docker rmi" for deleting the images in the list.
$ docker rmi $(docker images -q --filter "before=myDockerImage:456" myDockerImage)

Thursday 17 May 2018

How to load iframes faster

As you know, an iframe is an HTML tag that specifies an "inline frame" and is used to embed another document within the current HTML document. The two documents, the main one and the iframe, are independent and both are treated as complete documents, instead of treating one as part of the other.

Iframes are quite useful for embedding third party content, ads and widgets in our pages, because we can be sure that they are not going to interfere with our main page.

Using iframes has many advantages but also many disadvantages, such as their "special" behaviour for loading their content.

For example, let's take a look to the following simple HTML page:

<html>
<head>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.js"></script>
</head>

<body>
    <h1>Main Page: Example 1</h1>

    <iframe id="myiframe" src="https://www.ernestojpg.com" width="90%" height="90%"
            style="margin:auto;display:block">
    </iframe>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/ng2-bootstrap/1.6.2/ng2-bootstrap.umd.js"></script>

    <h1>Footer</h1>

</body>
</html>

As you can see, in the example above, we load two big javascript files in different parts of the document: angular.js in the page head, and ng2-bootstrap.umd.js at the end of the page body. We also embed an iframe with the content of this blog just before loading the second javascript file.

You can execute this example page here.

How do you think the three external components/dependencies (javascript1, iframe, and javacript2) are going to be loaded by the browser? If we load the example above using, for example, the Google Chrome's Developer Tools we will see something like this:


Surprisingly, the browser loads the two javascript files first, and when they are completely loaded, it starts loading the iframe document. Then when the iframe document is loaded, its dependencies are loaded as well.

Obviously, this is not the most efficient way of loading our page. If we have to load many resources within the main page (images, javascript, css, etc), the loading of the iframe will be delayed for a long time.

Ideally, we would like to start loading the iframe at the same time as the other page dependencies. But, how could we achieve that?

Fortunately there is a small trick that we could apply for achieving it: In general, an iframe will start loading its content if there is no other pending resource to load at that moment.

For this reason, making the following simple change in our code we could improve the page loading time dramatically:

<html>
<head>
</head>

<body>
    <h1>Main Page: Example 2</h1>

    <iframe id="myiframe" src="https://www.ernestojpg.com" width="90%" height="90%"
            style="margin:auto;display:block">
    </iframe>

    <script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.6.5/angular.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/ng2-bootstrap/1.6.2/ng2-bootstrap.umd.js"></script>

    <h1>Footer</h1>

</body>
</html>

As you can see in the code above, we have moved the reference to the first javascript file from the <head> section to the <body> section, just after the iframe. In that way, the iframe will be the first external resource to load within the page.

You can execute this example page here.

Let's have a look to how the page is loaded now in the browser:


As you can see, now the two javascript files and the iframe document are loaded practically in parallel!

Saturday 10 October 2009

Instalar Bouncy Castle en JBoss

Si alguna vez habéis intentado instalar en JBoss una o varias aplicaciones que hacen uso de la librería criptográfica Bouncy Castle, y sin explicación alguna habéis obtenido errores del tipo:

java.lang.SecurityException: JCE cannot authenticate the provider BC
o del tipo
java.security.NoSuchAlgorithmException: Error constructing implementation (algorithm: SHA1WithRSAEncryption, provider: BC, class: org.bouncycastle.jce.provider.JDKDigestSignature$SHA1WithRSAEncryption)
os contaré como he solucionado el problema.

En la documentación de Bouncy Castle nos comentan que hay varias formas de inicializar la librería.

De forma estática

Consiste en instalar la librería en nuestro JRE (Java Runtime Environment), y configurarlo como un proveedor de seguridad válido. Los pasos son los siguientes:

  1. Buscar el directorio de nuestro JRE
    Dependiendo de nuestro sistema operativo (Windows, Linux, o Mac OS), el directorio del JRE se encuentra en un sitio distinto. Llamaremos JRE_HOME a este directorio.

  2. Copiar la librería de Bouncy Castle en el siguiente directorio: JRE_HOME/lib/ext
    Yo he usado el fichero 'bcprov-jdk16-144.jar', que es la versión 1.44 de la librería, compilada para Java 6.

  3. Editar el archivo JRE_HOME/lib/security
    El contenido será algo como esto:
    security.provider.1=sun.security.provider.Sun
    security.provider.2=sun.security.rsa.SunRsaSign
    security.provider.3=com.sun.net.ssl.internal.ssl.Provider
    security.provider.4=com.sun.crypto.provider.SunJCE
    security.provider.5=sun.security.jgss.SunProvider
    security.provider.6=com.sun.security.sasl.Provider
    security.provider.7=org.jcp.xml.dsig.internal.dom.XMLDSigRI
    security.provider.8=sun.security.smartcardio.SunPCSC
    
    Sólo tendremos que añadir una línea más, añadiendo nuestro nuevo proveedor de seguridad, quedando de la siguiente forma:
    security.provider.1=sun.security.provider.Sun
    security.provider.2=sun.security.rsa.SunRsaSign
    security.provider.3=com.sun.net.ssl.internal.ssl.Provider
    security.provider.4=com.sun.crypto.provider.SunJCE
    security.provider.5=sun.security.jgss.SunProvider
    security.provider.6=com.sun.security.sasl.Provider
    security.provider.7=org.jcp.xml.dsig.internal.dom.XMLDSigRI
    security.provider.8=sun.security.smartcardio.SunPCSC
    security.provider.9=org.bouncycastle.jce.provider.BouncyCastleProvider
    
    Nota: Como puedes observar, nosotros hemos añadido nuestro nuevo proveedor en la posición 9, pero esto puede variar de un sistema a otro. Hay que añadirlo el último.
Y eso sería todo! La verdad es que no me gusta mucho esta forma de instalar Bouncy Castle, porque me parece excesivo tener que copiar ficheros a nuestro JRE y tocar archivos de configuración del mismo. Creo que es pedirle demasiado a la persona que quiera probar nuestra aplicación. Pero por otro lado, reconozco que esta forma es la que menos problemas da, y en instalaciones de producción esta podría ser una buena solución. Hay que tener en cuenta que de esta forma estamos instalando el proveedor de seguridad a nivel de la máquina virtual, es decir, cualquier aplicación Java que ejecutemos en nuestro sistema tendrá acceso a la librería Bouncy Castle (en tiempo de ejecución).

De forma dinámica

Consiste en poner al principio de nuestro código una instrucción del tipo:
import org.bouncycastle.jce.provider.BouncyCastleProvider;
...
Security.addProvider(new BouncyCastleProvider());
Esta forma, cuando nuestra aplicación es una aplicación de escritorio (una aplicación JavaSE), es la más sencilla de todas, ya que sólo tendremos que asegurarnos que la librería de Bouncy Castle (fichero .jar) esté en nuestro CLASSPATH, es decir, no sería necesario instalar la librería en nuestro JRE. Como se puede observar, de esta forma estaremos inicializando la librería a nivel de aplicación, es decir, cada una de las aplicaciones tendría que inicializar la librería de la forma antes descrita, de forma independiente.

¿Pero que pasa si nuestra aplicación es una aplicación Web y tiene que funcionar en un servidor de aplicaciones como JBoss? En este caso la instrucción anterior afectará también al resto de aplicaciones instaladas en el servidor (ya que supongo que todas comparten el mismo ClassLoader), es decir, si una aplicación inicializa la librería, el resto de aplicaciones en el servidor también verán el proveedor Bouncy Castle como un proveedor disponible. Pero el problema no es ese, el problema es que si una aplicación intenta utilizar la librería Bouncy Castle, habiendo ésta sido inicializada en otra aplicación Web distinta, obtendremos el temido mensaje de: "JCE cannot authenticate the provider BC".

¿Como solucionamos esto? La solución consiste en no añadir la librería (fichero .jar) en el interior de nuestras aplicaciones Web (ficheros .war), sino de instalarla de forma global, en la propia instancia de JBoss. Para ellos sólo tendremos que seguir los siguientes pasos:

  1. Instalar la librería de Bouncy Castle (en nuestro caso el fichero 'bcprov-jdk16-144.jar') en el directorio:
    JBOSS_HOME/server/INSTANCIA/lib
    Donde JBOSS_HOME es el directorio donde tenemos instalado JBoss, e INSTANCIA es la instancia del servidor JBoss que estamos usando, normalmente 'default'.
  2. En el código de nuestra aplicación Web, antes de utilizar cualquier función de la librería, escribir el siguiente código:
    // Inicializa el Proveedor de Seguridad BouncyCastle
    if (Security.getProvider(BouncyCastleProvider.PROVIDER_NAME)==null)
    {
      BouncyCastleProvider BC_provider = new BouncyCastleProvider();
      Security.addProvider(BC_provider);
      System.out.println("Cargado " + BC_provider.getInfo());
    }
Y eso es todo! Observa que aquí la librería se ha instalado a nivel del servidor de aplicaciones (JBoss) y todas las aplicaciones Web que corran sobre él no tendrán problemas para acceder al proveedor de seguridad.

Nota para usuarios de Eclipse: Como he comentado antes, la librería (fichero .jar) de Bouncy Castle no puede quedar en el archivo .WAR. Por lo tanto, no debéis copiar la librería en el directorio WebContent/WEB-INF/lib del proyecto. Sin embargo, sí debéis poner la librería en el CLASSPATH del proyecto, para la aplicación compile correctamente.

Saturday 25 April 2009

Instalar Memcached en Mac OS X

Memcached es un sistema distribuido de memoria caché, genérico y de alto rendimiento, usado para aumentar la eficiencia de aplicaciones web dinámicas.

Lo que se pretende es reducir la carga que se realiza sobre la Base de Datos, sobretodo en sistemas distribuidos, donde los tiempos de propagación de la información pueden llegar a ser bastante altos.

En este artículo veremos cómo instalar el sistema en Mac OS X, que es prácticamente igual que el proceso de instalación en Linux.

Antes de empezar, decir que es necesario tener instalado las Xcode Tools de Apple, las cuales se encuentran en el DVD de instalación de Mac OS X, o que podemos descargar de Apple Developer Connection. El motivo es que necesitamos el compilador gcc y todas las librerías necesarias para compilarlo todo.

Instalar libevent

Memcached depende de la librería de notificación de eventos libevent, por lo que lo primero que tenemos que hacer es descargarnos e instalar la última versión estable de la misma:

La página oficial de libevent es: http://www.monkey.org/~provos/libevent/

En el momento de escribir este artículo, la última versión estable de libevent es la 1.4.10. Podemos descargar el código fuente desde la página web, o directamente con el comando:
curl -O http://www.monkey.org/~provos/libevent-1.4.10-stable.tar.gz
Una vez descargado el fichero, lo descomprimimos con el comando:
tar xfz libevent-1.4.10-stable.tar.gz
Tras esto, nos metemos en la carpeta que se ha creado (libevent-1.4.10-stable), y tecleamos los siguientes comandos para compilar e instalar la librería:
./configure --prefix=/usr/local
make
sudo make install
Para el último comando se nos pedirá la contraseña de administrador. Si no ha habido ningún problema, se habrá instalado la librería en el directorio /usr/local

Nota: Para desinstalar libevent sólo tendremos que introducir el comando:
sudo make uninstall

Instalar memcached


Después de instalar libevent, pasamos ya ha instalar memcached. La página oficial es:

http://www.danga.com/memcached/

En el momento de escribir este artículo, la última versión estable de memcached es la 1.2.8. Podemos descargarla desde la página web, o directamente con el comando:
curl -O http://www.danga.com/memcached/dist/memcached-1.2.8.tar.gz
Una vez descargado el fichero con el código fuente, lo descomprimimos con el comando:
tar xfz memcached-1.2.8.tar.gz
Tras esto, nos metemos en la carpeta que se ha creado (memcached-1.2.8), y tecleamos los siguientes comandos para compilar e instalar el sistema:
./configure --prefix=/usr/local
make
sudo make install
Para el último comando se nos pedirá la contraseña de administrador. Si no ha habido ningún problema, se habrá instalado el sistema en el directorio /usr/local

Nota: Para desinstalar memcached sólo tendremos que introducir el comando:
sudo make uninstall

Arrancar memcached


Si memcached se ha instalado correctamente podremos escribir lo siguiente para conocer las distintas opciones de las que dispone:
memcached -h
Para arrancar memcached tendríamos que introducir algo como:
memcached -d -m 2048 -l 10.0.0.40 -p 11211
Esto arrancaría memcached, haciendo uso de un máximo de 2GB de RAM, y escuchando en la IP 10.0.0.40, puerto 11211.

Thursday 5 April 2007

Nuevos firmwares para el LVS de Linksys

Después de un tiempo sin muchos cambios, hoy 4 de Abril, ha habido una actualización general de los firmwares para los productos del Sistema de Voz Linksys (LVS)

  • Linksys SPA-9000/9000T: Actualizado a versión 5.1.7
    Por lo que veo en la lista de cambios, quizá se solucionen algunos problemillas con las líneas compartidas, y los problemas que tuve para registrar un Cisco 7960. Habrá que probarlo. Pero, ¿para cuando se va a permitir los registros de clientes remotos sin necesidad de usar una VPN?
    (Zona de Descargas) (Lista de cambios)

  • Linksys SPA-3102: Actualizado a versión 5.1.7
    Algunas correcciones. Poco más se le puede pedir a un producto tan veterano como este.
    (Zona de Descargas) (Lista de cambios)

  • Linksys SPA-2102: Actualizado a versión 5.1.9
    ¿Por qué 5.1.9 y no 5.1.7? Mismas correcciones que en el SPA3102
    (Zona de Descargas) (Lista de cambios)

  • Linksys SPA-400: Actualizado a versión 1.0.0.9
    Según la lista de cambios, sólo han corregido un pequeño problema. Esperábamos mucho más de esta nueva actualización, ¿para cuando se va a poder elegir el FXO en las llamadas salientes?
    (Zona de Descargas) (Lista de cambios)

  • Linksys SPA-922/942: Actualizado a versión 5.1.7
    Correcciones varias (volumen del auricular, lineas compartidas, etc)
    (Zona de Descargas) (Lista de cambios)

  • Linksys SPA-962: Actualizado a versión 5.1.7
    Gran cantidad de bugs corregidos, y añadido lector de noticias RSS ! :P
    (Zona de Descargas) (Lista de cambios)
En los próximos días probablemente lleguen las actualizaciones del SPA921/941, y del PAP2T. Así que ya sabéis, a actualizar toca ;)

Tuesday 3 April 2007

Nuevo curso LVS en Madrid


En los días 9 y 10 de Mayo se celebrará el próximo curso de formación "Linksys Voice System", impartido por Avanzada 7 y que tendrá lugar de nuevo en Madrid.

Como en ocasiones anteriores, el curso será de unas 15 personas, que formarán grupos de 3 o 4. Cada grupo dispondrá de un sistema LVS completo, formado por un SPA9000, un SPA400, y 3 SPA921/SPA941 para que realicen las prácticas.

Tras superar el examen final de aptitud se obtendrá el reconocimiento por parte de Linksys como técnico integrador capacitado de Soluciones LVS, que es requisito imprescindible para trabajar con este tipo de soluciones a corto/medio plazo.

Aún no se sabe el lugar exacto donde se celebrará el curso. En cuanto sepa algo os informo ;)

Para más información: linksys@avanzada7.com

Thursday 29 March 2007

Dispositivos Linksys que soportan T.38

Para los que le interese, estos son los dispositivos de Linksys que soportan el protocolo T.38 para el envío de Faxes:
  • SPA-2100: ATA con 2 puertos FXS y router.
  • SPA-2102: ATA con 2 puertos FXS y router.
  • SPA-3102: ATA con 1 FXS, 1 FXO, y router.
  • SPA-9000: PBX de hasta 16 extensiones, 2 puertos FXS, y router.
Recordar que no basta con que el dispositivo soporte T.38, también lo tiene que soportar nuestro servidor SIP (o proveedor de telefonía IP). Asterisk lo soporta a partir de la versión 1.4