AWS S3 as Maven repository

Toda organización que se precie de trabajar en proyectos Java debe disponer de, al menos, un buen repositorio privado en él que almacenar los artefactos Maven generados por sus distintas iniciativas.

Es evidente que, a estas alturas, existen ya en el mercado una serie productos consolidados y especializados en dicha tarea, como por ejemplo, Nexus o Arfiactory, pero en el presente artículo se quiere detallar la posibilidad de utilizar el popular servicio AWS S3 para dichos menesteres.

AWS S3

Tal y como se describe en la documentación oficial de AWS, Amazon Simple Storage Service (Amazon S3) es un servicio de almacenamiento de objetos que ofrece escalabilidad, disponibilidad de datos, seguridad y rendimiento.

Esta definición tan comercial y genérica, que bien podría encajar con otros tantos servicios, describe unas características muy interesantes de cara a construir un repositorio de artefactos Maven. Pero, ¿Cuales son los motivos específicos por lo que se debería optar por dicha tecnología?

Maintenance

AWS S3 es un servicio gestionado, lo que significa que no requiere mantenimiento alguno por parte del usuario. Es decir, al contrario que en las soluciones previamente mencionadas, no es necesario un sistema de monitorización que garantice qué tanto la maquina física sobre la que corre el repositorio como la aplicación propiamente dicha sigan funcionando.

De igual manera, también permite despreocuparse de las actualizaciones del sistema, Kernel o aplicación. Simplemente lo utilizas.

Availability

AWS S3 garantiza un 99,99% de disponibilidad para los objetos almacenamos, o lo que es lo mismo, el tiempo de inactividad no planeado asciende a un máximo de 4.38 minutos/mes o 52.6 minutos/año.

Estos indicadores son perfectamente asumibles para un repositorio de artefactos de Maven, que si bien es importante, no es precisamente un sistema crítico.

Durability

Una de las características mas rimbombantes de AWS S3 es el famoso “eleven 9’s of durability”. Es decir, en caso de almacenar 10,000 objetos, garantiza que, de media, solo se pierde uno cada 10,000,000 años.

Esto es posible gracias a que AWS almacena copias de los datos en múltiples ubicaciones, lo que le permite superar desde fallos individuales del sistema de almacenamiento hasta caídas de datacenters enteros. Redundancia al fin y al cabo.

En definitiva, esto garantiza que los artefactos Maven se almacenan de forma segura a lo largo del tiempo, sin intervención alguna por parte del usuario.

Security

Al igual que otros tantos servicios de AWS, el S3 se integra con el sistema IAM (Identity and Access Management) para poder gestionar tanto la autenticación como autorización a grano fino de los objetos almacenados.

Esta funcionalidad, fundamental para la construcción de un repositorio privado, es especialmente util, no solo para conceder permisos de lectura a los desarrolladores y escritura para los ciclos de integración continua, sino para filtrar que grupos de usuario tienen acceso a que artefactos del repositorio.

Pricing

Una de las claves del éxito del S3 es su reducido coste, tanto que sale mas económico que aprovisionar una o varias maquinas (IaaS) en las que instalar un producto con el que gestionar un repositorio de artefactos Maven.

Para ser mas exactos, AWS S3 factura en base a los siguientes factores:

  • Almacenamiento: Los primeros 50Tb al mes se cobran a 0,023 USD por GB.
  • Operaciones: 0,0004 USD por cada 1000 operaciones de lectura y 0,005 USD por cada 1000 operaciones de escritura.
  • Transferencia de datos: La transferencia entrante de datos desde Internet a AWS S3 es gratuita mientras que la saliente se factura a 0,09 USD por GB para los 10 primeros TB.

Traducido a números concretos, suponiendo que se almacenan 3500GB y se registran 10,000,000 operaciones de lectura y otras 10,000,000 de escritura, el precio mensual ascendería a 135.20 USD.

Dado que estas volumetrias son particularmente altas y que a cambio se obtienen todas las ventajas descritas anteriormente, la relación calidad/precio es francamente buena.

Usage

El primer paso para poder utilizar AWS S3 como repositorio de Maven es crear un bucket privado con un directorio para los artefactos “release” y otros para los “snapshot“. A continuación se proporciona un script de Terraform para su aprovisionamiento:

# S3 Maven Repository
resource "aws_s3_bucket" "maven-repository" {
  bucket = "mycompany-maven-repository"
  acl = "authenticated-read"
  force_destroy = true
  
  versioning {
    enabled = true
  }
  
  tags = merge(
    map(
      "Name", "mycompany-maven-repository"
    )
  )
}

# S3 Maven Repository Snapshots Folder
resource "aws_s3_bucket_object" "maven-repository-snapshots" {
    bucket = aws_s3_bucket.maven-repository.id
    acl    = "authenticated-read"
    key    = "snapshots/"
    content_type = "application/x-directory"
}

# S3 Maven Repository Releases Folder
resource "aws_s3_bucket_object" "maven-repository-releases" {
    bucket = aws_s3_bucket.maven-repository.id
    acl    = "authenticated-read"
    key    = "releases/"
    content_type = "application/x-directory"
}

Una vez construido, es buen momento para definir un grupo de IAM para los desarrolladores, al que asignar permisos de lectura sobre los repositorios. De esta manera, cada vez que un nuevo miembro se una al equipo, bastará con asignarlo al grupo de developers para que tenga acceso instantáneo al repositorio de Maven, eso si, solo con permisos de lectura, ya que las buenas practicas dictaminan que los artefactos deben ser subidos haciendo uso de un ciclo de integración continua.

# IAM Group Developers
resource "aws_iam_group" "developers" {
  name = "developers"
}

# IAM Group Policty Attachment Developers MavenRepositoryReadOnlyAccess
resource "aws_iam_group_policy_attachment" "developers-maven-repository-read-only-access" {
  group      = aws_iam_group.developers.name
  policy_arn = "aws_iam_policy.maven-repository-read-only-access.arn"
}

# Policy Maven Repository Read Only Access
resource "aws_iam_policy" "maven-repository-read-only-access" {
  name = "maven-repository-read-only-access"
  path = "/"
  description = "Maven Repository Read Only Access"
  policy= <<EOF
{
  "Version": "2012-10-17",
  "Statement": [
      {
          "Effect": "Allow",
          "Action": [
              "s3:Get*",
              "s3:List*"
          ],
          "Resource": [
              "arn:aws:s3:::mycompany-maven-repository",
              "arn:aws:s3:::mycompany-maven-repository/*"
          ]
      }
 ]
}
EOF
}

Una vez construida toda la infraestructura, llega el momento interactuar con el ansiado repositorio. Ahora bien, como Maven no soporta AWS S3 de forma nativa, será necesario incluir un wagon específico para ello.

Si habéis leído otros artículos similares a este, habréis visto que en la mayoría de ellos señalan que basta con añadir una librería en el fichero pom.xml del proyecto. Aunque esta solución es valida para los casos mas sencillos, lo cierto es que cuando se trabaja en proyectos multimodulo deja de funcionar, devolviendo un error muy similar a este:

Cannot access s3://mycompany-maven-repository/snapshots with type default using the available connector factories: BasicRepositoryConnectorFactory and 'parent.relativePath' points at wrong local POM @ line 6, column 10: 
Cannot access s3://mycompany-maven-repository/snapshots using the registered transporter factories: WagonTransporterFactory: java.util.NoSuchElementException

Esto se debe a que en Maven primero se procesa el POM padre y solo después de que este se haya cargado, entran en juego los wagons. La solución a esta problemática pasa por definir el wagon como una extensión en un fichero .mvn/extensions.xml en la raid del proyecto.

<extensions xmlns="http://maven.apache.org/EXTENSIONS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/EXTENSIONS/1.0.0 http://maven.apache.org/xsd/core-extensions-1.0.0.xsd">
    <!-- Repository on AMAZON S3 -->
    <extension>
        <groupId>org.springframework.build</groupId>
        <artifactId>aws-maven</artifactId>
        <version>5.0.0.RELEASE</version>
    </extension>
</extensions>

Nota: También es posible añadir el JAR de la extensión en el directorio ${maven.home}/lib/ext, si bien implica modificar la instalación de Maven y no siempre es posible.

Ahora sí, ya está todo listo para poder interactuar como de si de un repositorio Maven convencional se tratara.

Para ello, se debe definir el repositorio privado en el fichero pom.xml del proyecto.

<repositories>
	<repository>
		<id>mycompany-snapshots</id>
		<name>mycompany-snapshots</name>
		<url>s3://mycompany-maven-repository/snapshots</url>
	</repository>
	<repository>
		<id>mycompany-releases</id>
		<name>mycompany-releases</name>
		<url>s3://mycompany-maven-repository/releases</url>
	</repository>
</repositories>

<distributionManagement>
	<snapshotRepository>
		<id>mycompany-snapshots</id>
		<name>mycompany-snapshots</name>
		<url>s3://mycompany-maven-repository/snapshots</url>
	</snapshotRepository>
	<repository>
		<id>mycompany-releases</id>
		<name>mycompany-releases</name>
		<url>s3://mycompany-maven-repository/releases</url>
	</repository>
</distributionManagement>

Así como las propiedades de autenticación en el settings.xml:

<settings>
  <servers>
    <server>
      <id>mycompany-snapshots</id>
      <username>DEVELOPER_USERNAME</username>
      <password>DEVELOPER_PASSWORD</password>
    </server>
    <server>
      <id>mycompany-releases</id>
      <username>DEVELOPER_USERNAME</username>
      <password>DEVELOPER_PASSWORD</password>
    </server>
    [...]
  </servers>
  [...]
</settings>

Esta misma configuración también es valida para realizar un despliegue sobre el repositorio, siempre y cuando el usuario definido en el fichero settings.xml tenga permisos de escritura.

Conclusiones

En conclusión, AWS S3 puede ser una alternativa muy a tener en cuenta a la hora de escoger el producto o servicio a utilizar para dar vida a un repositorio de artefactos de Maven, especialmente si se tienen en cuenta que no requiere de mantenimiento y es francamente barato.

A nivel de aplicación tampoco tiene mayor impacto, basta con añadir el wagon como una extensión en un fichero .mvn/extensions.xml en la raid del proyecto y pasara a funcionar como un repositorio corriente.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s