Usando un cluster hadoop para levantar containers de docker.
Una de las características más interesantes de la versión 3 de hadoop es la evolución que ha sufrido yarn
- La versión 1 de hadoop estaba orientada a Map&Reduce
- En la versión 2 se reescribió el motor de gestión de recursos de map&reduce y pasó a ser un motor general de gestión de recursos y se llamó YARN (Yet Another Resaource Negociator)
- En la versión 3 se ha seguido potenciando YARN para que sea capaz de gestionar no solo la CPU y la memoria del cluster. También es ahora capaz de gestionar cualquier tipo de recurso, como puede ser, la red, la GPU, el disco etc… Además, se ha generalizado para que no solo puede ejecutar aplicaciones «YARN-izadas». Ahora también puede ejecutar cualquier tipo de aplicación entre ellas containers de dockers. Además se ha integrado en el core de YARN la ejecución de servicios Long-Running que en versiones anteriores se podía hacer usando Slider pero que ahora, definitivamente, se ha integrado en YARN. Dentro de este marco de aplicaciones Long-Running se ha potenciado el servicio de registro y la integración de dicho registro con un DNS, algo tan necesario en la fase de auto-discover de los sistemas destinados a levantar containers y/o mircro-servicios.
En este artículo vamos a explicar cómo usar YARN para levantar un docker, para ello hemos seguido las guías de hortonworks siguientes:
Cómo configurar YARN para que puede ejecutar dockers
Donde se explica, qué modificaciones hay que hacer en yarn para que éste pueda ejecutar containers docker. Siguiendo la documentación todo funciona como es de esperar, pero hay que tener cuidado con el concepto de confianza de la imagen que se quiere levantar. YARN, para levantar el container, prepara en el filesystem del node-manager un fichero launch.sh (donde se especifica el comando que se va a ejecutar del container). El fichero no pertenece a la imagen docker y por tanto el docker accede a este fichero porque monta una zona del sistema operativo anfitrión. Para poder hacer este montaje la imagen docker debe ser confiable. En el caso de yarn lo que quiere decir es que debe tener el tag apropiado. Si por ejemplo el parámetro «Docker Trusted Registries» está configurado con el valor centos,local,hortonworks (tal como se indica en la documentación), la imagen docker debe tener algunos de los tags especificados en el parámetro. En el caso que estamos viendo lo que quiere decir es que la imagen que descargamos de docker.io debe ser taggeada como centos, local o hortonworks
[tez@enbarr004 ~]$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE centos/local latest 7a232ff544d5 10 days ago 356 MB docker.io/centos/httpd-24-centos7 latest 7a232ff544d5 10 days ago 356 MB
Cómo levantar un container docker en YARN.
Donde se explica cómo preparar el docker y el fichero de yarn que hay que utilizar para levantar el servicio. En este caso se define un único servicios httpdservice que contiene dos componentes, httpd y httpd-proxy. El componente httpd se configura para que tenga dos instancias y para que tenga un index.html concreto. El componente httpd-proxy para que haga de balanceador de las otras dos instancias, para ello lee su configuración de un fichero que se ha dejado previamente en el file-system-distribuido.
Una vez que hemos explicado qué se quiere hacer, vamos a presentar un diagrama de red de cómo lo vamos a montar.
Hay que tener en cuenta que los dockers se van a levantar en modo bridge y que como yarn tiene varios node-manager hay que ser capaz de configurar el demonio de docker de cada node-manager de tal modo que sea visible por los demás y que no genere conflictos de IPs. Además, para que el sistema se útil, las redes de los dockers y los nombres de las máquinas deben ser accesibles.
Cómo vemos en la imagen se va a configurar cada node-manager para que tenga su propia subred, para ello crearemos el fichero /etc/docker/daemon.json con el siguiente contenido
{ "live-restore" : true, "debug" : true, "bip": "192.168.162.1/24", "fixed-cidr": "192.168.162.2/25", "mtu": 1500, "dns": ["192.168.1.2"] }
Donde lo que indicamos es que la subred donde se van a ejecutar los dockers para este node-manager será la 192.168.162.0/24, el gateway de acceso será el 192.168.162.1 y las ips serán asignadas a partir de la 192.168.162.2 en adelante.
El node-manager está en otra red que es la 192.168.2.0/24. Para que no haya conflictos con los otros node-manger seguiremos el convenio de de usar los tres dígitos de la ip de node-manager como red de docker. Al hacer esto tendremos 3 subredes (una por cada node-manager). Docker creará un bridge (docker0) y unas reglas de iptables en cada node-manager que actuará de gateway para enrutar las peticiones de red interna hacia fuera y viceversa.
Lo único que nos queda es poder acceder a estas redes desde el resto de máquinas de la LAN. Para ello hay que añadir la siguiente regla iptables en cada node-manager
sudo iptables -P FORWARD ACCEPT
Hay que que crear reglas estáticas en el firewall para que desde la LAN se enruten las peticiones a cada gateway es decir:
- Las peticiones a la subred 192.168.162.0/24 se deben enrutar por su gateway que es el propio node manager que contiene el docker es decir 192.168.2.162
- Las peticiones a la subred 192.168.163.0/24 se deben enrutar por su gateway que es el propio node manager que contiene el docker es decir 192.168.2.163
- Las peticiones a la subred 192.168.164.0/24 se deben enrutar por su gateway que es el propio node manager que contiene el docker es decir 192.168.2.164
Por último para que todo quede cerrado vamos a configurar el DNS de la LAN para que haga un forward de la zona que hemos configurado en YARN (La zona en la que YARN registra las ips de los containers que está gestionando se configura con el parámetro hadoop.registry.dns.domain-name = yarn.bigdata.zylklab.net en YARN).
Una vez configurado todo ya podemos levantar el el servicio YARN, para ello ejecutaremos el siguiente comando (en este caso como usuario tez)
[tez@enbarr004 ~]$ yarn app -launch httpdservice ./yarn-services/httpdservice.json 18/10/20 17:19:09 INFO client.RMProxy: Connecting to ResourceManager at enbarr001.bigdata.zylk.net/192.168.2.161:8050 18/10/20 17:19:09 INFO client.AHSProxy: Connecting to Application History server at enbarr002.bigdata.zylk.net/192.168.2.162:10200 18/10/20 17:19:09 INFO client.RMProxy: Connecting to ResourceManager at enbarr001.bigdata.zylk.net/192.168.2.161:8050 18/10/20 17:19:09 INFO client.AHSProxy: Connecting to Application History server at enbarr002.bigdata.zylk.net/192.168.2.162:10200 18/10/20 17:19:09 INFO client.ApiServiceClient: Loading service definition from local FS: /home/tez/yarn-services/httpdservice.json 18/10/20 17:19:10 INFO util.log: Logging initialized @1348ms 18/10/20 17:19:11 INFO client.ApiServiceClient: Application ID: application_1540048597720_0001
Al de unos segundo podremos ver si todo está correcto ejecutando
[tez@enbarr004 ~]$ yarn app -status httpdservice | jq 18/10/20 17:20:34 INFO client.RMProxy: Connecting to ResourceManager at enbarr001.bigdata.zylk.net/192.168.2.161:8050 18/10/20 17:20:34 INFO client.AHSProxy: Connecting to Application History server at enbarr002.bigdata.zylk.net/192.168.2.162:10200 18/10/20 17:20:34 INFO client.RMProxy: Connecting to ResourceManager at enbarr001.bigdata.zylk.net/192.168.2.161:8050 18/10/20 17:20:34 INFO client.AHSProxy: Connecting to Application History server at enbarr002.bigdata.zylk.net/192.168.2.162:10200 18/10/20 17:20:34 INFO util.log: Logging initialized @1108ms { "name": "httpdservice", "id": "application_1540048597720_0001", "lifetime": 3515, "components": [ { "name": "httpd", "dependencies": [], "artifact": { "id": "centos/httpd-24-centos7:latest", "type": "DOCKER" }, "resource": { "cpus": 1, "memory": "1024", "additional": {} }, "state": "STABLE", "configuration": { "properties": { "docker.network": "bridge" }, "env": {}, "files": [ { "type": "TEMPLATE", "properties": { "content": "<html><header><title>Title</title></header><body>Hello from ${COMPONENT_INSTANCE_NAME}!</body></html>" }, "dest_file": "/var/www/html/index.html" } ] }, "quicklinks": [], "containers": [ { "id": "container_e50_1540048597720_0001_01_000003", "ip": "192.168.164.2", "hostname": "httpd-1.httpdservice.tez.yarn.bigdata.zylklab.net", "state": "READY", "launch_time": 1540048757537, "bare_host": "enbarr004.bigdata.zylk.net", "component_instance_name": "httpd-1" }, { "id": "container_e50_1540048597720_0001_01_000002", "ip": "192.168.162.2", "hostname": "httpd-0.httpdservice.tez.yarn.bigdata.zylklab.net", "state": "READY", "launch_time": 1540048757532, "bare_host": "enbarr002.bigdata.zylk.net", "component_instance_name": "httpd-0" } ], "readiness_check": { "type": "HTTP", "properties": { "url": "http://${THIS_HOST}:8080" } }, "launch_command": "/usr/bin/run-httpd", "number_of_containers": 2, "run_privileged_container": false, "restart_policy": "ALWAYS" }, { "name": "httpd-proxy", "dependencies": [ "httpd" ], "artifact": { "id": "centos/httpd-24-centos7:latest",YARN para que puede ejecutar dockers "type": "DOCKER" }, "resource": { "cpus": 1, "memory": "1024", "additional": {} }, "state": "STABLE", "configuration": { "properties": { "docker.network": "bridge" }, "env": {}, "files": [ { "type": "TEMPLATE", "properties": {}, "dest_file": "/etc/httpd/conf.d/httpd-proxy.conf", "src_file": "httpd-proxy-no-dns.conf" } ] }, "quicklinks": [], "containers": [ { "id": "container_e50_1540048597720_0001_01_000004", "ip": "192.168.162.3", "hostname": "httpd-proxy-0.httpdservice.tez.yarn.bigdata.zylklab.net", "state": "READY", "launch_time": 1540048786679, "bare_host": "enbarr002.bigdata.zylk.net", "component_instance_name": "httpd-proxy-0" } ], "launch_command": "/usr/bin/run-httpd", "number_of_containers": 1, "run_privileged_container": false, "restart_policy": "ALWAYS" } ], "configuration": { "properties": { "docker.network": "bridge" }, "env": {}, "files": [] }, "state": "STABLE", "quicklinks": {}, "version": "1.0.0", "kerberos_principal": {} }
Si todo está correcto tendremos 3 containers en estado READY, dos del servicio httpd y uno para el servicio httpd-proxy. Los nombres que YARN habrá registrado en el DNS para estos tres containers serán
- httpd-proxy-0.httpdservice.tez.yarn.bigdata.zylklab.net
- httpd-0.httpdservice.tez.yarn.bigdata.zylklab.net
- httpd-1.httpdservice.tez.yarn.bigdata.zylklab.net
Si todo está correcto deberíamos poder hacer un ping a estas tres máquinas desde la LAN
gus@minotauro:~$ ping httpd-proxy-0.httpdservice.tez.yarn.bigdata.zylklab.net PING httpd-proxy-0.httpdservice.tez.yarn.bigdata.zylklab.net (192.168.162.3) 56(84) bytes of data. 64 bytes from 192.168.162.3 (192.168.162.3): icmp_seq=1 ttl=63 time=1.79 ms gus@minotauro:~$ ping httpd-0.httpdservice.tez.yarn.bigdata.zylklab.net PING httpd-0.httpdservice.tez.yarn.bigdata.zylklab.net (192.168.162.2) 56(84) bytes of data. From gateway.zylk.net (192.168.2.1): icmp_seq=1 Redirect Host(New nexthop: enbarr002.bigdata.zylk.net (192.168.2.162)) 64 bytes from 192.168.162.2 (192.168.162.2): icmp_seq=1 ttl=63 time=1.30 ms gus@minotauro:~$ ping httpd-1.httpdservice.tez.yarn.bigdata.zylklab.net PING httpd-1.httpdservice.tez.yarn.bigdata.zylklab.net (192.168.164.2) 56(84) bytes of data. From gateway.zylk.net (192.168.2.1): icmp_seq=1 Redirect Host(New nexthop: enbarr004.bigdata.zylk.net (192.168.2.164)) 64 bytes from 192.168.164.2 (192.168.164.2): icmp_seq=1 ttl=63 time=0.588 ms
En este caso vemos que dos de ellos están en la subred 192.168.162.0/24 y el otro en 192.168.164.0/24, debido al convenio que hemos seguido esto lo que quiere decir es que dos están en el nodo 192.168.2.162 y uno en el nodo 192.168.2.164
La última prueba que podemos hacer para ver que está todo correcto es acceder a desde el navegador al proxy y ver que funciona correctamente deberíamos ver lo siguiente.
y si hacemos otra petición debería saltar a otro nodo
Por último hay que tener en cuenta que el servicio se detendrá al de 1h porque así se ha definido en el json de yarn.