Gestión de configuraciones de servicios en Swarm

No hace mucho escribimos una entrada en este mismo blog acercándonos un poco más a los secretos, objetos de cluster swarm, disponibles a partir de la versión 1.13 (a.k.a. 17.03).

En esta entrada, hacíamos referencia al uso más inmediato para gestionar contraseñas, usuarios y en general elementos susceptibles de permanecer encriptados a la vista de los usuarios en tiempo de ejecución de un servicio. Vimos además una forma sencilla de gestionar configuraciones de servicios en clúster, evitando tener que usar directorios compartidos o sincronizados en entre los distintos hosts que formaban parte del propio cluster usando los secretos. Así, podríamos crear un servicio nginx, añadiendo su configuración en tiempo de ejecución mediante un secreto, si bien teníamos que preparar nuestras imágenes para poder gestionarlos.

Recordemos que los secretos se añaden en tiempo de ejecución en las tareas (contenedores) como sistema de ficheros tmpfs en /run/secrets/NOMBRE_DE_SECRETO. De esta forma, nuestro entrypoint debía modificarse para acomodar esta situación, copiando este fichero de configuración a una ubicación genérica (o bien especificar si el binario lo permite la ubicación del mismo). En todo caso, suponía realizar cambios en la forma de ejecutar una imagen ya existente.

En la versión 17.06 se añade un nuevo tipo de objeto, las configuraciones (“config”). Este objeto tiene un comportamiento similar al de los secretos, salvo que no se encriptan y se montan en este caso de forma directa en los contenedores, sin usar un sistema tmpfs. En este caso, estos objetos no requieren de encriptación y además, pueden montarse en una ubicación específica, sobreescribiendo ficheros en la imagen en tiempo de ejecución (contenedor). Por otra parte, a diferencia de los secretos, que se usan de forma diferente en Windows, los objetos de configuración se gestionan de forma similar. Esto se debe a no usar sistemas de almacenamiento en memoria tmpfs.

Vamos a ver un ejemplo sencillo con un servicio de DNS que podremos actualizar de forma dinámica.

Usaremos como imagen un sencillo unbound construido sobre alpine (frjaraur/unbound:testing). Esta imagen contiene una configuración por defecto en /etc/unbound/unbound.conf (es la instalación más sencilla, sin incluir includes anidados).

Supongamos que queremos aplicar una sencilla configuración de forward y resolución de elementos internos. Una configuración simplificada con este rol quedaría como sigue:

$ cat unbound.conf## Simple forward caching DNS

server:

interface: 0.0.0.0

access-control: 0.0.0.0/0 allow

verbosity: 1

local-data: “ten.zero.zero.one. A 10.0.0.1”

local-data-ptr: “10.0.0.1 ten.zero.zero.one.”

local-data: “ten.zero.zero.two. A 10.0.0.2”

local-data-ptr: “10.0.0.2 ten.zero.zero.two.”

 

forward-zone:

name: “.”

forward-addr: 8.8.4.4        # Google

forward-addr: 8.8.8.8        # Google

Crearemos ahora un objeto de configuración en el cluster con este contenido:

 

$ docker config create UNBOUND-1 unbound.conf
xc8b68hfdz5u095vj5wb1oq1a
$ docker config ls
ID                          NAME                CREATED             UPDATED
xc8b68hfdz5u095vj5wb1oq1a   UNBOUND-1           5 seconds ago       5 seconds ago

 

Podemos inspeccionar su contenido:

$ docker config inspect UNBOUND-1
[
{
“ID”: “xc8b68hfdz5u095vj5wb1oq1a”,
“Version”: {
“Index”: 21329
},
“CreatedAt”: “2017-09-14T15:11:04.04193389Z”,
“UpdatedAt”: “2017-09-14T15:11:04.04193389Z”,
“Spec”: {
“Name”: “UNBOUND-1”,
“Labels”: {},
“Data”:”IyMgU2ltcGxlIGZvcndhcmQgY2FjaGluZyBETlMKc2VydmVyOgoJaW50ZXJmYWNlOiAwLjAuMC4wCiAgICAgICAgYWNjZXNzLWNvbnRyb2w6IDAuMC4wLjAvMCBhbGxvdyAKICAgICAgICB2ZXJib3NpdHk6IDEKCWxvY2FsLWRhdGE6ICJ0ZW4uemVyby56ZXJvLm9uZS4gQSAxMC4wLjAuMSIKCWxvY2FsLWRhdGEtcHRyOiAiMTAuMC4wLjEgdGVuLnplcm8uemVyby5vbmUuIgoJbG9jYWwtZGF0YTogInRlbi56ZXJvLnplcm8udHdvLiBBIDEwLjAuMC4y

IgoJbG9jYWwtZGF0YS1wdHI6ICIxMC4wLjAuMiB0ZW4uemVyby56ZXJvLnR3by4iCgpmb

3J3YXJkLXpvbmU6CiAgICAgIG5hbWU6ICIuIgogICAgICBmb3J3YXJkLWFkZHI6IDguOC4

0LjQgICAgICAgICMgR29vZ2xlCiAgICAgIGZvcndhcmQtYWRkcjogOC44LjguOCAgICAgIC

AgIyBHb29nbGUKCgo=”
}
}
]

Es importante resaltar que los datos contenidos en este objeto están visibles para el engine; de manera que podemos decodificarlo, no está encriptado.

 

$ echo “IyMgU2ltcGxlIGZvcndhcmQgY2FjaGluZyBETlMKc2VydmVyOgoJaW50ZXJmYWNlOiAwLjAuMC4wCiAgICAgICAgYWNjZXNzLWNvbnRyb2w6IDAuMC4wLjAvMCBhbGxvdyAKICAgICAg

ICB2ZXJib3NpdHk6IDEKCWxvY2FsLWRhdGE6ICJ0ZW4uemVyby56ZXJvLm9uZS4

gQSAxMC4wLjAuMSIKCWxvY2FsLWRhdGEtcHRyOiAiMTAuMC4wLjEgdGVuLnplcm8ue

mVyby5vbmUuIgoJbG9jYWwtZGF0YTogInRlbi56ZXJvLnplcm8udHdvLiBBIDEwLjAuMC4y

IgoJbG9jYWwtZGF0YS1wdHI6ICIxMC4wLjAuMiB0ZW4uemVyby56ZXJvLnR3by4iCgpmb

3J3YXJkLXpvbmU6CiAgICAgIG5hbWU6ICIuIgogICAgICBmb3J3YXJkLWFkZHI6IDguOC4

0LjQgICAgICAgICMgR29vZ2xlCiAgICAgIGZvcndhcmQtYWRkcjogOC44LjguOCAgICAgIC

AgIyBHb29nbGUKCgo=”|base64 –decode
## Simple forward caching DNS
server:
interface: 0.0.0.0
access-control: 0.0.0.0/0 allow
verbosity: 1
local-data: “ten.zero.zero.one. A 10.0.0.1”
local-data-ptr: “10.0.0.1 ten.zero.zero.one.”
local-data: “ten.zero.zero.two. A 10.0.0.2”
local-data-ptr: “10.0.0.2 ten.zero.zero.two.”

forward-zone:
name: “.”
forward-addr: 8.8.4.4        # Google
forward-addr: 8.8.8.8        # Google

Ahora bien, como hemos comentado, este objeto nos permite configurar un servicio de forma dinámica, no tiene como fin gestionar información sensible (como sí ocurre con los secretos).

Para usar esta configuración, crearemos un sencillo servicio de dns exportando por ejemplo el puerto 20053 (udp y tcp en el ejemplo, y para evitar conflictos con la propia caché de dnsmasq usamos un puerto diferente del 53). Añadiremos en la creación el objeto de configuración creado anteriormente, “UNBOUND-1”.

 

$ docker service create –name unbound –publish 20053:53 –publish 20053:53/udp –config source=UNBOUND-1,target=/etc/unbound/unbound.conf frjaraur/unbound:testingiew88jk25pphxude1f01038pu

Since –detach=false was not specified, tasks will be created in the background.

In a future release, –detach=false will become the default.

 

Verificamos el estado del servicio

 

$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE                      PORTS
ew88jk25pph        unbound             replicated          1/1                 frjaraur/unbound:testing   *:20053->53/tcp,*:20053->53/udp

Y comprobamos de forma sencilla su funcionamiento.

 

$ dig @0.0.0.0 -p20053 ten.zero.zero.one

; <<>> DiG 9.10.3-P4-Ubuntu <<>> @0.0.0.0 -p20053 ten.zero.zero.one

; (1 server found)

;; global options: +cmd

;; Got answer:

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18024

;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

 

;; OPT PSEUDOSECTION:

; EDNS: version: 0, flags:; udp: 4096

;; QUESTION SECTION:

;ten.zero.zero.one.                 IN        A

 

;; ANSWER SECTION:

ten.zero.zero.one.       3600    IN        A         10.0.0.1

 

;; Query time: 0 msec

;; SERVER: 127.0.0.1#20053(0.0.0.0)

;; WHEN: Thu Sep 14 17:50:48 CEST 2017

;; MSG SIZE  rcvd: 62

 

 

De esta forma tenemos una configuración externa a la imagen aplicada al servicio “unbound”.

Supongamos que queremos añadir una nueva entrada al DNS (por ejemplo para ten.zero.zero.three).

 

$ dig @0.0.0.0 -p20053 ten.zero.zero.three

; <<>> DiG 9.10.3-P4-Ubuntu <<>> @0.0.0.0 -p20053 ten.zero.zero.three

; (1 server found)

;; global options: +cmd

;; Got answer:

;; ->>HEADER<<- opcode: QUERY, status: NXDOMAIN, id: 16717

;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

 

;; OPT PSEUDOSECTION:

; EDNS: version: 0, flags:; udp: 4096

;; QUESTION SECTION:

;ten.zero.zero.three.               IN        A

 

;; AUTHORITY SECTION:

.                                   3600    IN        SOA    a.root-servers.net. nstld.verisign-grs.com. 2017091400 1800 900 604800 86400

 

;; Query time: 41 msec

;; SERVER: 127.0.0.1#20053(0.0.0.0)

;; WHEN: Thu Sep 14 17:54:45 CEST 2017

;; MSG SIZE  rcvd: 123

 

 

En este caso, usaremos de nuevo objetos de configuración.Creamos un nuevo objeto de configuración añadiendo las entradas en un nuevo fichero (o en el mismo, lógicamente).

 

$ cat unbound.conf.new
## Simple forward caching DNS
server:
interface: 0.0.0.0
access-control: 0.0.0.0/0 allow
verbosity: 1
local-data: “ten.zero.zero.one. A 10.0.0.1”
local-data-ptr: “10.0.0.1 ten.zero.zero.one.”
local-data: “ten.zero.zero.two. A 10.0.0.2”
local-data-ptr: “10.0.0.2 ten.zero.zero.two.”
local-data: “ten.zero.zero.three. A 10.0.0.3”
local-data-ptr: “10.0.0.3 ten.zero.zero.three.”forward-zone:
name: “.”
forward-addr: 8.8.4.4        # Google
forward-addr: 8.8.8.8        # Google
$ docker config create UNBOUND-2 unbound.conf.new
aamfzbclncmecaavodtn9jqdd
$ docker config lsID                          NAME                CREATED             UPDATEDaamfzbclncmecaavodtn9jqdd   UNBOUND-2           14 seconds ago      14 seconds agoxc8b68hfdz5u095vj5wb1oq1a   UNBOUND-1           About an hour ago   About an hour ago

Ahora bastará con actualizar el servicio para reflejando los cambios de objecto de configuración (borrar el anterior y añadir el nuevo).

 

$ docker service update –config-rm UNBOUND-1 –config-add source=UNBOUND-2,target=/etc/unbound/unbound.conf unboundunbound

Since –detach=false was not specified, tasks will be updated in the background.

In a future release, –detach=false will become the default.

 

 

El servicio se actualiza con la configuración (imaginemos un servicio con 200 réplicas y “rolling update”…) . Y en cuestión de un segundo (una réplica en este caso) la configuración está actualizada.

 

$ docker service ps unbound
ID                  NAME                IMAGE                      NODE                DESIRED STATE       CURRENT STATE             ERROR               PORTS
98ebmtkdi5a7        unbound.1           frjaraur/unbound:testing   hopla               Running             Running 35 seconds ago
siasg5zwcqku         \_ unbound.1       frjaraur/unbound:testing   hopla               Shutdown            Shutdown 36 seconds ago

Por último, verificamos si la resolución de ten.zero.zero.three es ahora correcta.

 

$ dig @0.0.0.0 -p20053 ten.zero.zero.three

; <<>> DiG 9.10.3-P4-Ubuntu <<>> @0.0.0.0 -p20053 ten.zero.zero.three

; (1 server found)

;; global options: +cmd

;; Got answer:

;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 5197

;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 1

 

;; OPT PSEUDOSECTION:

; EDNS: version: 0, flags:; udp: 4096

;; QUESTION SECTION:

;ten.zero.zero.three.               IN        A

 

;; ANSWER SECTION:

ten.zero.zero.three.    3600    IN        A         10.0.0.3

 

;; Query time: 0 msec

;; SERVER: 127.0.0.1#20053(0.0.0.0)

;; WHEN: Thu Sep 14 18:02:51 CEST 2017

;; MSG SIZE  rcvd: 64

 

 

 

Esperamos que el artículo haya sido de utilidad.

Javier Ramírez

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.