5 sept 2013

apt-pinning: Controlar desde qué repositorio se instalan nuestros paquetes

En ocasiones nos interesa forzar la instalación de paquetes desde un repositorio determinado, como por ejemplo, backports o un repositorio local.  Para ello, podemos hacer apt-pinning en Debian y derivados.

Para configurar el apt-pinning podemos usar dos ubicaciones:
  • El fichero /etc/apt/preferences
  • El directorio /etc/apt/preferences.d/
Personalmente, prefiero usar el directorio /etc/apt/preferences.d/, más que nada por organización y tener claro de un vistazo lo que estoy forzando. Si os decantáis por esta opción, lo único que tenéis es que hacer es crear un fichero dentro del directorio con las preferencias que queráis establecer. Por ejemplo, puedo tener un fichero /etc/apt/preferences.d/iceweasel en el que configuro las opciones de apt para que los paquetes de iceweasel se instalen desde squeeze-backports.

Cada vez que quiero definir unas determinadas preferencias, uso la siguiente estructura básica en el archivo de configuración:

Package: 
Pin:
Pin-Priority:

Mediante Package indicamos la lista de paquetes a instalar.
Mediante Pin indicamos la rama, versión o repositorio desde el que queremos instalar los paquetes.
Mediante Pin-Priority especificamos la prioridad que tendrán los paquetes a la hora de actualizar.

Veamos un fichero de ejemplo:
sprofesores-pro:~# cat /etc/apt/preferences/iceweasel 
Package: iceweasel iceweasel-l10n-es-es libnss3 libnss3-1d
Pin: release a=squeeze-backports
Pin-Priority: 600

En el fichero anterior, estoy indicando a apt que los paquetes iceweasel, iceweasel-l10n-es-es, libnss y libnss3-1d deben instalarse desde squeeze-backports siempre que la versión instalada sea más antigüa y no exista una versión más reciente en la rama principal.

Otro ejemplo:
sprofesores-pro:~# cat /etc/apt/preferences/repositorio-local 
Package: *
Pin: origin recursos.valledeljerte3
Pin-Priority: 1000

En este ejemplo le estoy diciendo a apt que se debe instalar cualquier paquete (*) del repositorio recursos.valledeljerte3 (que es mi repositorio local) salvo que la versión ya instalada sea más reciente, aunque dicho paquete no provenga de la rama principal.
Si os fijáis en el ejemplo anterior, he usado un comodín (*) para representar cualquier paquete.

También podría especificar lo siguiente: 

Package: iceweasel*

Y estaría indicando que las condiciones se apliquen a cualquier paquete que comience por la cadena iceweasel. 

Diferentes opciones para especificar en Pin:
  • Pin: release a=stable (Indicamos la rama del repositorio a usar: stable, unstable, testing)
  • Pin: release n=wheezy (Indicamos el nombre de la versión de Debian: squeeze, wheezy, ...)
  • Pin: release v=6.0 (Indicamos la versión de Debian: 5.0, 6.0, 7.0, ...)
  • Pin: release c=main (Indicamos el componente del repositorio: main, contrib, non-free )
  • Pin: origin recursos.valledeljerte3 (Indicamos el servidor de los paquetes)

Definición de prioridades con Pin-Priority:
Las prioridades pueden ser números enteros positivos o negativos. Veamos cómo se interpretan los valores de prioridad:
  • Prioridad > 1000: La versión se instalará desde el repositorio indicado, incluso si es una versión anterior a la instalada en el sistema.
  • 990 < Prioridad <= 1000: Se instalará la versión aunque no provenga de la distribución objetivo, a menos que la versión instalada sea más reciente.
  • 500 < Prioridad <= 990: Se instalará el paquete siempre que la versión instalada sea más antigua y no exista una versión más reciente en la rama principal.
  • 100 < Prioridad <= 500: Se instalará el paquete siempre que la versión instalada sea más antigua y no exista una versión más reciente en cualquiera de los repositorios.
  • 0 < Prioridad <= 100: La versión sólo se instala si no hay ninguna versión del paquete ya instalada.
  • Prioridad <= 0: Evita la instalación del paquete especificado en las condiciones.

Usar Stages en Puppet para garantizar las actualizaciones de paquetes mediante APT

Uno de los problemas que tenemos con Puppet es que, en ocasiones, realizamos un módulo que instala un software y cuando se ejecuta dicho módulo, falla la ejecución porque los índices de los repositorios no se encuentran actualizados.

Una forma de solucionar este problema es haciendo uso de un recurso que nos proporciona Puppet: Las stages o etapas.

Este recurso nos permite ordenar "la ejecución" de los recursos en etapas. Por defecto, tan sólo hay una etapa definida en Puppet llamada "main", de forma que todos los recursos son automáticamente asociados a ella, a menos que asignemos especificamente un recurso a otra etapa definida por nosotros.

Las etapas definidas por el usuario se declaran como cualquier otro recurso.

Vamos a ver a continuación cómo usar el recurso stage para garantizar que se realice siempre un apt-get update antes de que se ejecute cualquier otro recurso:

Lo ideal es que definamos nuestras stages en el archivo /etc/puppet/manifests/site.pp, salvo que no tengamos control sobre él, en cuyo caso, podemos definirlo en otra clase de orden inferior. Como éste es nuestro caso, las he definido en las clases de servidores ltsp, workstations y  portátiles:
  • /etc/puppet/manifests/classes/clase-especifica-squeeze.pp
  • /etc/puppet/manifests/classes/especifica-workstation.pp
  • /etc/puppet/manifests/classes/especifica-miniportatil-2011.pp


Primero.- Definimos las etapas que queramos crear y les damos el nombre que queramos. Como se puede ver en el siguiente ejemplo, yo he creado dos etapas: first y last.

stage { [first, last]: }

Segundo.- Especificamos cuál debe ser el orden de ejecución de las etapas. Por ejemplo:

Stage[first] -> Stage[main] -> Stage[last]

Con ésto, logramos:
  • Que la etapa "first" se ejecute antes que la etapa "main". 
  • Y la etapa "last" se ejecute después que la etapa "main".

Tercero.- Asociamos módulos o clases a etapas. Como se puede ver a continuación, estoy definiendo que el módulo "apt-get-update" debe ejecutarse en la etapa "first".

class { apt-get-update: stage => first }

Y listo.Con ésto, garantizaremos que el apt-get update se ejecute siempre antes que cualquier otro módulo que por defecto estará asociado a la etapa "main".

Como se puede ver, he definido tres etapas. De momento, no uso la etapa "last". Tan sólo la he creado por si la necesito posteriormente.