Kubernetes | Usando kubeadm para crear un cluster

Posted on Actualizado enn

He estado aprendiendo mucho de Cloud Computing, en especial Docker junto con Swarm para orquestar contenedores, tratando de hacer el despliegue de servicios de la mejor manera. Siempre me ha llamado la atención el tema de Cloud Computing y la cantidad inmensa de cosas que puedes hacer.

Ahora estoy aprendiendo Kubernetes que nace por supuesto en Google y que le da mucha seriedad a este orquestador de contenedores. Con mucho que decir acerca de este orquestador voy a escribir a continuación como crear un cluster usando kubeadm que es parte de kubernetes y que sirve para crear cluster.

 

 

Mi arquitectura:

Tengo dos nodos que forman parte de mi cluster, un nodo llamado tlacuache1 que es el master y el otro nodo llamado tlacuachebaby que es el worker.

tlacuache1—> Master

tlacuachebaby—>Worker

 

Iniciar kubeadm en el master:

kubeadm init --pod-network-cidr=10.244.0.0/16

 

Usar el cluster como usuario regular:

mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

ó como root:

export KUBECONFIG=/etc/kubernetes/admin.conf

Instalando un pod network

Un pod consiste en un contenedor o el encapsulamiento de varios contenedores, estos pods solo pueden existir en los nodos workers y gracias a kubectl el master puede comunicarse con los pods dando instrucciones para un deployment.  

 

Eligiendo Flannel como mi gestor de tráfico de red:

kubectl apply -f https://raw.githubusercontent.com/coreos/flannel/v0.9.0/Documentation/kube-flannel.yml
Flannel gestiona como el tráfico de red es transportado entre los nodos.

Inspeccionando si se ha creado kube-dns pod y revisar si está ejecutando con éxito:

kubectl get pods --all-namespaces

 

Aislando el master

kubectl taint nodes --all node-role.kubernetes.io/master-

Vemos esto como salida:

node "tlacuache1" untainted
taint key="dedicated" and effect="" not found.
taint key="dedicated" and effect="" not found.

 

Uniendo nodos

Añadiendo tlacuachebaby al cluster

Se ejecuta lo siguiente en tlacuachebaby:

kubeadm join --token <token> <master-ip>:<master-port> --discovery-token-ca-cert-hash sha256:<hash>

Voy a revisar en el nodo tlacuache1 que nodos tengo en el cluster:

kubectl get nodes

 

Desplegando una pequeña app que usa microservicios.

A manera de ejemplo voy a desplegar una tienda online para ver como trabajan nuestros nodos:

kubectl create namespace sock-shop
kubectl apply -n sock-shop -f "https://github.com/microservices-demo/microservices-demo/blob/master/deploy/kubernetes/complete-demo.yaml?raw=true"

 

Revisando sobre que puerto está corriendo el despliegue:

kubectl -n sock-shop get svc front-end

lo que vemos de salida:

NAME        CLUSTER-IP       EXTERNAL-IP   PORT(S)        AGE
front-end   10.110.250.153   <nodes>       80:30001/TCP   59s

 

Viendo la magia de k8s y viendo el funcionamiento del cluster:

 

 

Si se consulta la IP que tienen el nodo worker tlacuachebaby también podemos ver el deployment.

 

Eliminando lo que hice con kubeadm

Drenando:

Para eliminar todo lo que hice tengo que drenar primero el nodo  para asegurarme de que quede vacío, de esa manera puedo eliminar todo lo que se hizo.

kubectl drain <tlacuache1> --delete-local-data --force --ignore-daemonsets
kubectl delete node <tlacuache1>

Resetear todo lo que se instaló con kubeadm:
kubeadm reset

Desplegando VoteApp usando Docker Swarm

Posted on Actualizado enn

VoteApp es un aplicación para realizar votos, lo cual usa microservicios, es decir, está dividido en varios servicios independientes que a su vez tienen la capacidad de comunicarse entre ellos, además los microservicios tiene dos caracteristicas fundamentales; Service Discovery and Load Balancing, esto significa que cuando se desarrolla una aplicación está debe tener la capacidad de escalar así mismo buscar la manera en como cada componente o servicio tiene que ser descubierto por otros servicios dentro o desde un afuera de un cluster.

A manera de ejemplo voy a usar varias imágenes que provienen de dockersamples hosteadas por supuesto en dockerhub, para mas detalles ver el repo en github.

Iniciando Docker Swarm

Usé dos nodos para poder desplegar los servicios, los nodos son tlacuache1 como mi administrador(manager) y tlacuachebaby  es el trabajador(worker). Para poder desplegar los servicios usando docker swarm necesito iniciar el servicio haciendo uso de la siguiente instrucción:

docker swarm init --advertise-addr IPtlacuache1

Aquí hay que tomar en cuenta que no estoy especificando el puerto, por lo tanto tomará por default el puerto 2377.

Ahora voy incluir tlacuachebaby al enjambre(swarm), esto con la finalidad de que apoye o haga trabajos que tlacuache1 indique:

Visualizando nodos

Con la siguiente instrucción puedo ver que nodos están participando en docker swarm.

docker node ls

Arquitectura

Bueno, ya tengo tlacuache1 como manager y tlacuachebaby como worker , a continuación muestro la arquitectura que voy a usar para el despliegue de los servicios:

Tengo 5 servicios:

  • voting-app: Front-end que muestra al usuario la interfaz para realizar la votación entre dos tipos de animales; Cat and dog.
  • Redis: Es un motor de base de datos que guarda los datos en memoria, estos datos que emitió el usuario en el servicio voting-app.
  • .NET worker: Es un servicio que obtiene datos desde Redis y lo almacena en una base de datos de PostgreSQL.
  • db: Base de datos en PostgreSQL en donde los resultados de voto son guardados gracias a Docker Volume.
  • Node.js result-app: Front-end que muestra los resultados de las votaciones en tiempo real.

 

Mi fichero compose

version: "3"
services:

  redis:
    image: redis:alpine
    ports:
      - "6379"
    networks:
      - frontend
    deploy:
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure
  db:
    image: postgres:9.4
    volumes:
      - db-data:/var/lib/postgresql/data
    networks:
      - backend
    deploy:
      placement:
        constraints: [node.role == manager]
  vote:
    image: dockersamples/examplevotingapp_vote:before
    ports:
      - 5000:80
    networks:
      - frontend
    depends_on:
      - redis
    deploy:
      replicas: 2
      update_config:
        parallelism: 2
      restart_policy:
        condition: on-failure
  result:
    image: dockersamples/examplevotingapp_result:before
    ports:
      - 5001:80
    networks:
      - backend
    depends_on:
      - db
    deploy:
      replicas: 1
      update_config:
        parallelism: 2
        delay: 10s
      restart_policy:
        condition: on-failure

  worker:
    image: dockersamples/examplevotingapp_worker
    networks:
      - frontend
      - backend
    deploy:
      mode: replicated
      replicas: 1
      labels: [APP=VOTING]
      restart_policy:
        condition: on-failure
        delay: 10s
        max_attempts: 3
        window: 120s
      placement:
        constraints: [node.role == manager]

  visualizer:
    image: dockersamples/visualizer:stable
    ports:
      - "8080:8080"
    stop_grace_period: 1m30s
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
    deploy:
      placement:
        constraints: [node.role == manager]

networks:
  frontend:
  backend:

volumes:
  db-data:

 

¿Qué está pasando en mi fichero compose?

Visualizer

En la arquitectura únicamente tengo 5 servicios, a manera de usar visualizer  he agregado otro servicio más. Visualizer es un servicio que me muestra a través de un diagrama los servicios que están corriendo en Docker swarm, además es importante mencionar que visualizer solo funciona cuando esta en modo swarm y es recomendado usarlo solo para fines de testeo ya que se considera inseguro.

Networks y Servicios

En mi fichero compose estoy usando la versión 3 de yaml compose, he creado 2 redes para conectar los servicios, por ejemplo en el servicio redis y vote estoy usando la misma red, lo mismo pasa con los servicios db y result.

Placement

Cuando uso placement me estoy asegurando que el servicio solo puede ejecutarse donde yo estoy indicando, no tiene por que repartirse en otros nodos  por ejemplo aquí estoy instruyendo que solo corra en swarm manager nunca en el nodo worker.

Así lo describo en los servicios db, worker visualizer.

placement:
        constraints: [node.role == manager]

 volumes

Uso volumes por que necesito almacenar los datos y mantenerlos, asegurando que cuando un contenedor se elimine, la información quede almacenada.

volumes:
      - db-data:/var/lib/postgresql/data
volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"

Deploying

Desplegaré los servicios haciendo uso de mi fichero docker compose y mi stack se llamará voteapp.

docker stack deploy -c docker-compose.yml voteapp

He desplegado 6 servicios a través de tlacuache1 y tlacuachebaby.

Mostrando información acerca del deploy

De esta manera puedo ver cuantos contenedores están corriendo en tlacuache1:

docker ps

Cuantos contenedores en tlacuachebaby:

En total tengo 7 contenedores corriendo.

Ahora quiero ver los servicios:

servicels

Listando mi stack:

 

 

Viendo los servicios en acción

Revisaré en el navegador el servicio visualizer, este servicio está corriendo en el puerto 8080 y ocuparé la IP de tlacuache1:

 

 

VoteApp está corriendo en el puerto 5000:

 

 

VoteApp está corriendo en el puerto 5001:

 

He usado la IP de tlacuache1, ahora bien, que pasa si lo consulto con la IP de tlacuachebaby, sin duda alguna puedo alcanzar los servicios de la misma manera como lo hice con tlacuache1, aquí puedo ver el super poder de Docker Swarm, más bien de Routing Mesh, gracias a esto podemos tener los servicios siempre a la escucha y repartiendose la carga, sin importar a que nodo le haga la consulta y lo mejor de todo, a pesar de que el contenedor donde se encuentra los resultados de las votaciones esté en tlacuache1 lo puedo consultar desde la IP de tlacuachebaby, ¡esto es mágico!

Más detalles–> https://docs.docker.com/engine/swarm/ingress/#configure-an-external-load-balancer

 

Este post ha sido inspirado gracias a Tlacuaches Project.

 

Orquestar con Docker Swarm

Posted on Actualizado enn

Swarm es un orquestador de contenedores nativo de Docker, con docker swarm puedes desplegar servicios  distribuyendo carga de trabajo, tráfico de red, persistencia de datos entre otras cosas interesantes a través de nodos.
docker

Instalar swarm:

Docker swarm ya viene por default en la  versión 1.12.0 o posterior, pero si no tienes swarm instalado:

apt-get install docker.io

o bien: https://docs.docker.com/engine/installation/

Lo que necesito:

./Dockerfile
./apache-config.conf
./www/index.php
/docker-compose.yml

 

Dockerfile:

FROM ubuntu:latest
MAINTAINER Ismael Garcia <tuxisma@gmail.com>

# Install apache, PHP, and supplimentary programs. openssh-server, curl, and lynx-cur are for debugging the container.
RUN apt-get update && apt-get -y upgrade && DEBIAN_FRONTEND=noninteractive apt-get -y install \
    apache2 php7.0 php7.0-mysql libapache2-mod-php7.0 curl lynx-cur

# Enable apache mods.
RUN a2enmod php7.0
RUN a2enmod rewrite

# Update the PHP.ini file, enable <? ?> tags and quieten logging.
RUN sed -i "s/short_open_tag = Off/short_open_tag = On/" /etc/php/7.0/apache2/php.ini
RUN sed -i "s/error_reporting = .*$/error_reporting = E_ERROR | E_WARNING | E_PARSE/" /etc/php/7.0/apache2/php.ini

# Manually set up the apache environment variables
ENV APACHE_RUN_USER www-data
ENV APACHE_RUN_GROUP www-data
ENV APACHE_LOG_DIR /var/log/apache2
ENV APACHE_LOCK_DIR /var/lock/apache2
ENV APACHE_PID_FILE /var/run/apache2.pid

# Expose apache.
EXPOSE 80

# Copy this repo into place.
ADD www /var/www/site

# Update the default apache site with the config we created.
ADD apache-config.conf /etc/apache2/sites-enabled/000-default.conf

# By default start up apache in the foreground, override with /bin/bash for interative.
CMD /usr/sbin/apache2ctl -D FOREGROUND

 

apache-config.conf:

<VirtualHost *:80>
  ServerAdmin me@mydomain.com
  DocumentRoot /var/www/site

  <Directory /var/www/site/>
      Options Indexes FollowSymLinks MultiViews
      AllowOverride All
      Order deny,allow
      Allow from all
  </Directory>

  ErrorLog ${APACHE_LOG_DIR}/error.log
  CustomLog ${APACHE_LOG_DIR}/access.log combined

</VirtualHost>

 

www/index.php:

<html>
  <head><title>Tlacuache1 running</title></head>
  <body>
        <center><h1>Hello Tlacuaches!</h1></center>
        <img src="https://pre00.deviantart.net/c124/th/pre/f/2016/177/6/1/un_tlacuache_casual_g__by_supercrazyhyena-da7sdpj.png" />
        <?php
                echo "This comment is runnig the server side";
        ?>
  </body>
</html>

docker-compose:

version: "3"
services:
   web:
       image: tuxisma/apachephp:latest
       deploy:
          replicas: 20
          resources:
            limits:
              cpus: "0.1"
              memory: 50M
          restart_policy:
            condition: on-failure
       ports:
            - "8080:80"
       networks:
            - webnet
networks:
    webnet:

Iniciar Docker swarm:

docker swarm init --advertise-addr IP
alt text

He iniciado docker swarm y he usado tlacuache1 que es el nombre del nodo líder.

Voy a usar otro nodo que lo nombraré como tlacuachebaby para demostrar como dos nodos pueden trabajar juntos y ayudarse entre si, el primer nodo se llama tlacuach1 que es el líder y el otro nodo se llama tlacuachebaby que va a funcionar como trabajador. No especifiqué  un puerto déspues de la dirección IP así que Docker Swarm usará el puerto 2377 por default.

Agregar un nodo como trabajador a swarm

Dentro de tlacuachebaby:

docker swarm join --token SWMTKN-1-3oluxaye8s8denuv8ugo602lb99as6qos5rifwtp39kkn69fwg-7qsmgtk1vjriuqghvclj9mpy5 IP:2377

 

Dentro del manager (tlacuache1):

docker node ls
37659051851_0c9a3a81a9_b

Desplegando servicio:

docker stack deploy -c docker-compose.yml app
app –> nombre del servicio.

alt text

 

Esto es mágico, acabamos de desplegar 20 contenedores y estamos balanceando la carga entre dos nodos, si quieres escalar el servicio puedes editar el archivo docker-compose.yml.

Viendo la magia:

37609660866_074bd9b89f_b

Intenta detener un contenedor y vas a poder ver la magia. 
Docker Swarm crea otro contenedor para compensar el que se ha detenido todo esto gracias a restart_policy: condition: on-failure que tenemos en el archivo docker-compose.yml

 Este articulo ha sido inspirado gracias a Tlacuaches Project.