logocloudstic.png

Resumen

Notas sobre conceptos básicos del desarrollo con contenedores Docker y Kubernetes. Se incluyen también el código de bastantes ejemplos para ilustrar los conceptos descritos.

Objetivos

  • Conocer los componentes básicos de las aplicaciones basadas en contenedores

  • Crear contenedores a partir de imágenes de Docker Hub

  • Usar volúmenes para almacenamiento persistente

  • Aprender a usar Dockerfile para la creación de imágenes

  • Usar Docker Compose para construir entornos de contenedores

  • Aprender la técnica de desarrollo de aplicaciones multicontenedor con Docker Compose

  • Conocer los componentes básicos de Kubernetes (pod, deployment` y service)

  • Desplegar aplicaciones en Kubernetes

  • Crear init containers

  • Crear una aplicación autoescalable

Repositorio usado en este seminario:

Software para el seguimiento del tutorial

1. Docker

  • Proyecto Open Source 2013​

  • Revolución en el desarrollo y despliegue de aplicaciones​

  • Plataforma para que desarrolladores y administradores puedan desarrollar, desplegar y ejecutar aplicaciones en un entorno aislado denominado contenedor​

  • Docker permite separar las aplicaciones de la infraestructura acelerando el proceso de entrega de software a producción​

1.1. Docker vs Máquinas virtuales

containers vs vm.png

1.2. Despliegue de un contenedor

Al desplegar un contenedor s descarga su imagen al registro local

Enlace al ejemplo​

Ejemplo01-Contenedor/crear-contenedor-apache.sh
#!/bin/bash
docker run \
-d \
-p 80:80 \
--name myapache \
httpd
crear contenedor apache.png

1.3. Despliegue de un contenedor con un volumen​

  • Los contenedores son efímeros​

  • El volumen es persistente​

Código de la aplicación​

Ejemplo02-Contenedor-con-volumen/myweb/index.php
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <meta name="description" content="" />
    <meta
      name="author"
      content="Mark Otto, Jacob Thornton, and Bootstrap contributors"
    />
    <meta name="generator" content="Jekyll v3.8.5" />
    <title>Seminario Docker</title>

    <link
      rel="canonical"
      href="https://getbootstrap.com/docs/4.3/examples/jumbotron/"
    />

    <!-- Bootstrap core CSS -->
    <link
      href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
      crossorigin="anonymous"
    />

    <style>
      .bd-placeholder-img {
        font-size: 1.125rem;
        text-anchor: middle;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
      }

      @media (min-width: 768px) {
        .bd-placeholder-img-lg {
          font-size: 3.5rem;
        }
      }
    </style>
    <!-- Custom styles for this template -->
    <link href="jumbotron.css" rel="stylesheet" />
  </head>
  <body>
    <main role="main">
      <!-- Main jumbotron for a primary marketing message or call to action -->
      <div class="jumbotron">
        <div class="container">
          <h1 class="display-3">Seminario sobre Contenedores</h1>
          <p>
            Contenedor creado con un volumen aparte. Edita el código en la
            carpeta local y verás la magia
          </p>
          <p>
            <a
              class="btn btn-primary btn-lg"
              href="https://www.docker.com/"
              role="button"
              >Docker &raquo;</a
            >
          </p>
        </div>
      </div>

      <div class="container">
        <!-- Example row of columns -->
        <div class="row">
          <div class="col-md-4">
            <h2>Revolucionario</h2>
            <p>
              Docker ha supuesto una revolución en el desarrollo y despliegue de
              aplicaciones​
            </p>
            <p>
              <a class="btn btn-secondary" href="#" role="button"
                >View details &raquo;</a
              >
            </p>
          </div>
          <div class="col-md-4">
            <h2>Todos juntos</h2>
            <p>
              Plataforma para que desarrolladores y administradores puedan
              desarrollar, desplegar y ejecutar aplicaciones en un entorno
              aislado denominado contenedor
            </p>
            <p>
              <a class="btn btn-secondary" href="#" role="button"
                >View details &raquo;</a
              >
            </p>
          </div>
          <div class="col-md-4">
            <h2>Entrega a producción</h2>
            <p>
              Docker permite separar las aplicaciones de la infraestructura
              acelerando el proceso de entrega de software a producción​
            </p>
            <p>
              <a class="btn btn-secondary" href="#" role="button"
                >View details &raquo;</a
              >
            </p>
          </div>
        </div>

        <hr />
      </div>
      <!-- /container -->
    </main>

    <footer class="container">
      <p>@ualmtorres</p>
    </footer>
    <script
      src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
      integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
      crossorigin="anonymous"
    ></script>
    <script>
      window.jQuery ||
        document.write(
          '<script src="/docs/4.3/assets/js/vendor/jquery-slim.min.js"><\/script>'
        );
    </script>
    <script
      src="/docs/4.3/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-xrRywqdh3PHs8keKZN+8zzc5TX0GRTLCcmivcbNJWm2rs5C8PRhcEn3czEjhAO9o"
      crossorigin="anonymous"
    ></script>
  </body>
</html>
Ejemplo02-Contenedor-con-volumen/crear-contenedor-apache-con-volumen.sh
#!/bin/bash
docker run \
-d \
-p 81:80 \
-v "$PWD/myweb":/usr/local/apache2/htdocs/ \
--name myapache-vol \
httpd
crear contenedor con volumen.png

1.4. Imágenes Docker interesantes​

  • Servidores Web: Apache, Nginx, …​​

  • BD: MySQL, MongoDB, Redis, PostgreSQL, …​

  • Sistemas operativos: Alpine, Ubuntu, …​

  • Y muchas más: node, php, elasticsearch, haproxy, wordpress, rabbitmq, python, openjdk, tomcat, jenkins, redmine, flink, spark, …​

1.5. Crear imagen propia

El Dockerfile:

  • Indica cómo y con qué construir la imagen​

  • Produce repetibilidad​

  • La imagen queda en el registro local​

Ejemplo03-Imagen/Dockerfile
FROM httpd:2.4
COPY ./myweb/ /usr/local/apache2/htdocs/

Crear imagen

Ejemplo03-Imagen/crear-imagen.sh
#!/bin/bash
docker build -t myweb .

El código de la aplicación

Ejemplo03-Imagen/myweb/index.html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta
      name="viewport"
      content="width=device-width, initial-scale=1, shrink-to-fit=no"
    />
    <meta name="description" content="" />
    <meta
      name="author"
      content="Mark Otto, Jacob Thornton, and Bootstrap contributors"
    />
    <meta name="generator" content="Jekyll v3.8.5" />
    <title>Seminario Docker</title>

    <link
      rel="canonical"
      href="https://getbootstrap.com/docs/4.3/examples/jumbotron/"
    />

    <!-- Bootstrap core CSS -->
    <link
      href="https://getbootstrap.com/docs/4.3/dist/css/bootstrap.min.css"
      rel="stylesheet"
      integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T"
      crossorigin="anonymous"
    />

    <style>
      .bd-placeholder-img {
        font-size: 1.125rem;
        text-anchor: middle;
        -webkit-user-select: none;
        -moz-user-select: none;
        -ms-user-select: none;
        user-select: none;
      }

      @media (min-width: 768px) {
        .bd-placeholder-img-lg {
          font-size: 3.5rem;
        }
      }
    </style>
    <!-- Custom styles for this template -->
    <link href="jumbotron.css" rel="stylesheet" />
  </head>
  <body>
    <main role="main">
      <!-- Main jumbotron for a primary marketing message or call to action -->
      <div class="jumbotron">
        <div class="container">
          <h1 class="display-3">Seminario sobre Contenedores</h1>
          <p>
            Contenedor creado con un volumen aparte. Edita el código en la
            carpeta local y verás la magia
          </p>
          <p>
            <a
              class="btn btn-primary btn-lg"
              href="https://www.docker.com/"
              role="button"
              >Docker &raquo;</a
            >
          </p>
        </div>
      </div>

      <div class="container">
        <!-- Example row of columns -->
        <div class="row">
          <div class="col-md-4">
            <h2>Revolucionario</h2>
            <p>
              Docker ha supuesto una revolución en el desarrollo y despliegue de
              aplicaciones​
            </p>
            <p>
              <a class="btn btn-secondary" href="#" role="button"
                >View details &raquo;</a
              >
            </p>
          </div>
          <div class="col-md-4">
            <h2>Todos juntos</h2>
            <p>
              Plataforma para que desarrolladores y administradores puedan
              desarrollar, desplegar y ejecutar aplicaciones en un entorno
              aislado denominado contenedor
            </p>
            <p>
              <a class="btn btn-secondary" href="#" role="button"
                >View details &raquo;</a
              >
            </p>
          </div>
          <div class="col-md-4">
            <h2>Entrega a producción</h2>
            <p>
              Docker permite separar las aplicaciones de la infraestructura
              acelerando el proceso de entrega de software a producción​
            </p>
            <p>
              <a class="btn btn-secondary" href="#" role="button"
                >View details &raquo;</a
              >
            </p>
          </div>
        </div>

        <hr />
      </div>
      <!-- /container -->
    </main>

    <footer class="container">
      <p>@ualmtorres</p>
    </footer>
    <script
      src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
      integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
      crossorigin="anonymous"
    ></script>
    <script>
      window.jQuery ||
        document.write(
          '<script src="/docs/4.3/assets/js/vendor/jquery-slim.min.js"><\/script>'
        );
    </script>
    <script
      src="/docs/4.3/dist/js/bootstrap.bundle.min.js"
      integrity="sha384-xrRywqdh3PHs8keKZN+8zzc5TX0GRTLCcmivcbNJWm2rs5C8PRhcEn3czEjhAO9o"
      crossorigin="anonymous"
    ></script>
  </body>
</html>

Despliegue de imagen creada​

Ejemplo03-Imagen/crear-contenedor-myweb.sh
#!/bin/bash
docker run \
-d \
-p 80:80 \
--name myweb \
myweb
crear imagen propia.png

1.6. Subir imagen a Docker Hub

  • Docker Hub es un registro de imágenes público

  • La cuenta gratuita permite

    • Imágenes (repositorios) públicos indefinidos​

    • Una imagen privada

Subida a Docker Hub​

Ejemplo03-Imagen/subir-imagen.sh
#!/bin/bash
docker tag myweb ualmtorres/myweb:v0
docker push ualmtorres/myweb:v0

Despliegue de imagen subida​

Ejemplo03-Image/crear-contenedor-myweb-desde-dockerhub.sh
#!/bin/bash
docker run \
-d \
-p 84:80 \
--name myweb-from-dockerhub \
ualmtorres/myweb:v0
crear contenedor con imagen subida.png

1.7. Ciclo de desarrollo con Docker

  • Crear carpetas de desarrollo​

  • Desplegar imagen de base​

  • Crear Dockerfile​

  • Iterar​

    • Programar + Subir a repo​

    • Crear imagen propia​

    • Etiquetar como nueva versión​

    • Subir nueva imagen a Docker Hub​

    • Poner en producción

2. Docker Compose

  • Permite definir y ejecutar aplicaciones Docker con varios contenedores​

  • Podemos tener varios entornos aislados (normalmente con nombre de directorio)​

  • Sólo vuelve a crear los contenedores que hayan cambiado

2.1. Despliegue con Docker Compose​

Ejemplo04-Docker-Compose/init.sql
CREATE SCHEMA IF NOT EXISTS SG;
USE SG;

DROP TABLE IF EXISTS s_customer;
CREATE TABLE s_customer
 (id                         VARCHAR(3)  NOT NULL,
  name                       VARCHAR(20) NOT NULL,
  phone                      VARCHAR(20) NOT NULL,
  address                    VARCHAR(20),
  city                       VARCHAR(20),
  state                      VARCHAR(15),
  country                    VARCHAR(20),
  zip_code                   VARCHAR(15),
  credit_rating              VARCHAR(9),
  sales_rep_id               VARCHAR(3),
  region_id                  VARCHAR(3),
  comments                   VARCHAR(255),
  CONSTRAINT s_customer_id_pk PRIMARY KEY (id),
  CONSTRAINT s_customer_credit_rating_ck
  CHECK (credit_rating IN ('EXCELLENT', 'GOOD', 'POOR'))
 );


INSERT INTO s_customer VALUES ('301', 'Sports,Inc', '540-123-4567','72 High St',
'Harrisonburg', 'VA','US', '22809','EXCELLENT', '12', '1', NULL);
INSERT INTO s_customer VALUES ('302', 'Toms Sporting Goods', '540-987-6543','6741 Main St',
'Harrisonburg', 'VA','US', '22809','POOR', '14', '1', NULL);
INSERT INTO s_customer VALUES ('303', 'Athletic Attire', '540-123-6789','54 Market St',
'Harrisonburg', 'VA','US', '22808','GOOD', '14', '1', NULL);
INSERT INTO s_customer
VALUES ('304', 'Athletics For All', '540-987-1234','286 Main St', 'Harrisonburg', 'VA',
'US', '22808','EXCELLENT', '12', '1', NULL);
INSERT INTO s_customer VALUES ('305', 'Shoes for Sports', '540-123-9876','538 High St',
'Harrisonburg', 'VA','US', '22809','EXCELLENT', '14', '1', NULL);
INSERT INTO s_customer VALUES ('306', 'BJ Athletics', '540-987-9999','632 Water St',
'Harrisonburg', 'VA','US', '22810','POOR', '12', '1', NULL);

INSERT INTO s_customer VALUES ('403', 'Athletics One', '717-234-6786','912 Columbia Rd',
'Lancaster', 'PA','US', '17601','GOOD', '14', '1', NULL);
INSERT INTO s_customer VALUES ('404', 'Great Athletes', '717-987-2341','121 Litiz Pike',
'Lancaster', 'PA','US', '17602','EXCELLENT', '12', '1', NULL);
INSERT INTO s_customer VALUES ('405', 'Athletics Two', '717-987-9875','435 High Rd',
'Lancaster', 'PA','US', '17602','EXCELLENT', '14', '1', NULL);
INSERT INTO s_customer VALUES ('406', 'Athletes Attic', '717-234-9888','101 Greenfield Rd',
'Lancaster', 'PA','US', '17601','POOR', '12', '1', NULL);

INSERT INTO s_customer VALUES ('201', 'One Sport', '55-112066222','82 Via Bahia', 'Sao Paolo',
NULL, 'Brazil', NULL,'EXCELLENT', '12', '2', NULL);
INSERT INTO s_customer VALUES ('202', 'Deportivo Caracas', '58-28066222','31 Sabana Grande',
'Caracas', NULL, 'Venezuela', NULL,'EXCELLENT', '12', '2', NULL);
INSERT INTO s_customer VALUES ('203', 'New Delhi Sports', '91-11903338','11368 Chanakya',
'New Delhi', NULL, 'India', NULL,'GOOD', '11', '4', NULL);
INSERT INTO s_customer VALUES ('204', 'Ladysport', '1-206-104-0111','281 Queen Street',
'Seattle', 'Washington', 'US', NULL,'EXCELLENT', '11', '1', NULL);
INSERT INTO s_customer VALUES ('205', 'Kim''s Sporting Goods', '852-3693888','15 Henessey Road',
'Hong Kong', NULL, NULL, NULL,'EXCELLENT', '11', '4', NULL);
INSERT INTO s_customer VALUES ('206', 'Sportique', '33-93425722253','172 Rue de Place',
'Cannes', NULL, 'France', NULL,'EXCELLENT', '13', '5', NULL);
INSERT INTO s_customer VALUES ('207', 'Tall Rock Sports', '234-16036222','10 Saint Antoine',
'Lagos', NULL, 'Nigeria', NULL,'GOOD', NULL, '3', NULL);
INSERT INTO s_customer VALUES ('208', 'Muench Sports', '49-895274449','435 Gruenestrasse',
'Munich', NULL, 'Germany', NULL,'GOOD', '13', '5', NULL);

INSERT INTO s_customer VALUES ('209', 'Beisbol Si!', '809-352666','415 Playa Del Mar',
 'San Pedro de Macoris', NULL, 'Dominican Republic', NULL, 'EXCELLENT', '11', '6', NULL);
INSERT INTO s_customer VALUES ('210', 'Futbol Sonora', '52-404555','5 Via Saguaro', 'Nogales',
NULL, 'Mexico', NULL,'EXCELLENT', '12', '2', NULL);
INSERT INTO s_customer VALUES ('211', 'Helmut''s Sports', '42-2111222','45 Modrany', 'Prague',
NULL, 'Czechoslovakia', NULL,'EXCELLENT', '11', '5', NULL);
INSERT INTO s_customer VALUES ('212', 'Hamada Sport', '20-31209222','47A Corniche',
'Alexandria', NULL, 'Egypt', NULL,'EXCELLENT', '13', '3', NULL);
INSERT INTO s_customer VALUES ('213', 'Sports Emporium', '1-415-555-6281','4783 168th Street',
'San Francisco', 'CA', 'US', NULL,'EXCELLENT', '11', '1', NULL);
INSERT INTO s_customer VALUES ('214', 'Sports Retail', '1-716-555-7777','115 Main Street',
'Buffalo', 'NY', 'US', NULL, 'POOR', '11', '1', NULL);
INSERT INTO s_customer VALUES ('215', 'Sports Russia', '7-0953892444','7070 Yekatamina',
'Saint Petersburg', NULL, 'Russia', NULL,'POOR', '11', '5', NULL);
COMMIT;
Ejemplo04-Docker-Compose/app/index.php
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Customer Catalog</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
</head>
<body>
  <div class = "container">
    <div class="jumbotron">
      <h1 class="display-4">Sporting Goods App</h1>
      <p class="lead">Customer Catalog Sample Application</p>
      <hr class="my-4">
      <p>PHP sample application connected to a MySQL database to list a customer catalog</p>
    </div>
    <table class="table table-striped table-responsive">
      <thead>
        <tr>
          <th>Name</th>
          <th>Credit Rating</th>
          <th>Address</th>
          <th>City</th>
          <th>State</th>
          <th>Country</th>
          <th>Zip</th>
        </tr>
      </thead>
      <tbody>
        <?php

        $conexion = mysqli_connect("mysql", "root", "secret", "SG");

        $cadenaSQL = "select * from s_customer";
	$sentencia = mysqli_prepare($cadenaSQL);
        $resultado = mysqli_query($conexion, $cadenaSQL);

        while ($fila = mysqli_fetch_object($resultado)) {
         echo "<tr><td> " .$fila->name .
         "</td><td>" . $fila->credit_rating .
         "</td><td>" . $fila->address .
         "</td><td>" . $fila->city .
         "</td><td>" . $fila->state .
         "</td><td>" . $fila->country .
         "</td><td>" . $fila->zip_code .
         "</td></tr>";
       }
       ?>
     </tbody>
   </table>
 </div>
 <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
 <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
</body>
</html>
Ejemplo04-Docker-Compose/docker-compose.yml
version: "2"
services:
  mysql:
    container_name: mysql
    restart: always
    image: mysql:5.6
    environment:
      MYSQL_ROOT_PASSWORD: "secret" # TODO: Change this
    ports:
      - "3306:3306"
    volumes:
      - ./data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
  webapp:
    container_name: webapp
    restart: always
    image: ualmtorres/phalcon-apache-ubuntu
    ports:
      - "80:80"
    volumes:
      - ./app:/var/www/html
  adminer:
    container_name: adminer
    image: adminer
    restart: always
    ports:
      - 8080:8080
Ejemplo04-Docker-Compose/lanzar-app-ejemplo.sh
#!/bin/bash
docker-compose up -d
Ejemplo04-Docker-Compose/Dockerfile
FROM ualmtorres/phalcon-apache-ubuntu

ADD ./app/ /var/www/html

VOLUME /var/www/html

EXPOSE 80
Ejemplo04-Docker-Compose/limpiar-entorno.sh
#!/bin/bash
docker-compose down
aplicacion docker compose.png

2.2. Despliegue de una aplicación que consume de una API con Docker Compose​

Ejemplo05-Docker-Compose-API/init.sql
CREATE SCHEMA IF NOT EXISTS SG;
USE SG;

DROP TABLE IF EXISTS s_customer;
CREATE TABLE s_customer
 (id                         VARCHAR(3)  NOT NULL,
  name                       VARCHAR(20) NOT NULL,
  phone                      VARCHAR(20) NOT NULL,
  address                    VARCHAR(20),
  city                       VARCHAR(20),
  state                      VARCHAR(15),
  country                    VARCHAR(20),
  zip_code                   VARCHAR(15),
  credit_rating              VARCHAR(9),
  sales_rep_id               VARCHAR(3),
  region_id                  VARCHAR(3),
  comments                   VARCHAR(255),
  CONSTRAINT s_customer_id_pk PRIMARY KEY (id),
  CONSTRAINT s_customer_credit_rating_ck
  CHECK (credit_rating IN ('EXCELLENT', 'GOOD', 'POOR'))
 );


INSERT INTO s_customer VALUES ('301', 'Sports,Inc', '540-123-4567','72 High St',
'Harrisonburg', 'VA','US', '22809','EXCELLENT', '12', '1', NULL);
INSERT INTO s_customer VALUES ('302', 'Toms Sporting Goods', '540-987-6543','6741 Main St',
'Harrisonburg', 'VA','US', '22809','POOR', '14', '1', NULL);
INSERT INTO s_customer VALUES ('303', 'Athletic Attire', '540-123-6789','54 Market St',
'Harrisonburg', 'VA','US', '22808','GOOD', '14', '1', NULL);
INSERT INTO s_customer
VALUES ('304', 'Athletics For All', '540-987-1234','286 Main St', 'Harrisonburg', 'VA',
'US', '22808','EXCELLENT', '12', '1', NULL);
INSERT INTO s_customer VALUES ('305', 'Shoes for Sports', '540-123-9876','538 High St',
'Harrisonburg', 'VA','US', '22809','EXCELLENT', '14', '1', NULL);
INSERT INTO s_customer VALUES ('306', 'BJ Athletics', '540-987-9999','632 Water St',
'Harrisonburg', 'VA','US', '22810','POOR', '12', '1', NULL);

INSERT INTO s_customer VALUES ('403', 'Athletics One', '717-234-6786','912 Columbia Rd',
'Lancaster', 'PA','US', '17601','GOOD', '14', '1', NULL);
INSERT INTO s_customer VALUES ('404', 'Great Athletes', '717-987-2341','121 Litiz Pike',
'Lancaster', 'PA','US', '17602','EXCELLENT', '12', '1', NULL);
INSERT INTO s_customer VALUES ('405', 'Athletics Two', '717-987-9875','435 High Rd',
'Lancaster', 'PA','US', '17602','EXCELLENT', '14', '1', NULL);
INSERT INTO s_customer VALUES ('406', 'Athletes Attic', '717-234-9888','101 Greenfield Rd',
'Lancaster', 'PA','US', '17601','POOR', '12', '1', NULL);

INSERT INTO s_customer VALUES ('201', 'One Sport', '55-112066222','82 Via Bahia', 'Sao Paolo',
NULL, 'Brazil', NULL,'EXCELLENT', '12', '2', NULL);
INSERT INTO s_customer VALUES ('202', 'Deportivo Caracas', '58-28066222','31 Sabana Grande',
'Caracas', NULL, 'Venezuela', NULL,'EXCELLENT', '12', '2', NULL);
INSERT INTO s_customer VALUES ('203', 'New Delhi Sports', '91-11903338','11368 Chanakya',
'New Delhi', NULL, 'India', NULL,'GOOD', '11', '4', NULL);
INSERT INTO s_customer VALUES ('204', 'Ladysport', '1-206-104-0111','281 Queen Street',
'Seattle', 'Washington', 'US', NULL,'EXCELLENT', '11', '1', NULL);
INSERT INTO s_customer VALUES ('205', 'Kim''s Sporting Goods', '852-3693888','15 Henessey Road',
'Hong Kong', NULL, NULL, NULL,'EXCELLENT', '11', '4', NULL);
INSERT INTO s_customer VALUES ('206', 'Sportique', '33-93425722253','172 Rue de Place',
'Cannes', NULL, 'France', NULL,'EXCELLENT', '13', '5', NULL);
INSERT INTO s_customer VALUES ('207', 'Tall Rock Sports', '234-16036222','10 Saint Antoine',
'Lagos', NULL, 'Nigeria', NULL,'GOOD', NULL, '3', NULL);
INSERT INTO s_customer VALUES ('208', 'Muench Sports', '49-895274449','435 Gruenestrasse',
'Munich', NULL, 'Germany', NULL,'GOOD', '13', '5', NULL);

INSERT INTO s_customer VALUES ('209', 'Beisbol Si!', '809-352666','415 Playa Del Mar',
 'San Pedro de Macoris', NULL, 'Dominican Republic', NULL, 'EXCELLENT', '11', '6', NULL);
INSERT INTO s_customer VALUES ('210', 'Futbol Sonora', '52-404555','5 Via Saguaro', 'Nogales',
NULL, 'Mexico', NULL,'EXCELLENT', '12', '2', NULL);
INSERT INTO s_customer VALUES ('211', 'Helmut''s Sports', '42-2111222','45 Modrany', 'Prague',
NULL, 'Czechoslovakia', NULL,'EXCELLENT', '11', '5', NULL);
INSERT INTO s_customer VALUES ('212', 'Hamada Sport', '20-31209222','47A Corniche',
'Alexandria', NULL, 'Egypt', NULL,'EXCELLENT', '13', '3', NULL);
INSERT INTO s_customer VALUES ('213', 'Sports Emporium', '1-415-555-6281','4783 168th Street',
'San Francisco', 'CA', 'US', NULL,'EXCELLENT', '11', '1', NULL);
INSERT INTO s_customer VALUES ('214', 'Sports Retail', '1-716-555-7777','115 Main Street',
'Buffalo', 'NY', 'US', NULL, 'POOR', '11', '1', NULL);
INSERT INTO s_customer VALUES ('215', 'Sports Russia', '7-0953892444','7070 Yekatamina',
'Saint Petersburg', NULL, 'Russia', NULL,'POOR', '11', '5', NULL);
COMMIT;
Ejemplo05-Docker-Compose-API/api/.htaccess
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^(.*)$ index.php?_url=/$1 [QSA,L]
</IfModule>
Ejemplo05-Docker-Compose-API/api/index.php
<?php

// Instantiate the class responsible for implementing a micro application
$app = new \Phalcon\Mvc\Micro();

// Routes
$app->get('/say/date', 'currentDate');
$app->get('/say/hello/{name}', 'greeting');
$app->get('/customers', 'getCustomers');
$app->get('/customers/{name}', 'getCustomer');

$app->notFound('notFound');

function sendResponse($statusCode, $response) {
  http_response_code($statusCode);
  header("Content-Type: application/json");
  echo json_encode($response);
}

// Handlers
function currentDate() {
  $response = array("date" => date('Y-m-d'));
  sendResponse(200, $response);
}

function greeting($name) {
  $response = array("greeting" => "Hello $name");
  sendResponse(200, $response);
}

function getCustomers() {
  $conexion = mysqli_connect("mysql", "root", "secret", "SG");

  $cadenaSQL = "select * from s_customer";
  $sentencia = mysqli_prepare($cadenaSQL);
  $resultado = mysqli_query($conexion, $cadenaSQL);
  $response = array();

  while ($fila = mysqli_fetch_object($resultado)) {
    $customer = [
      "name" => $fila->name,
      "credit_rating" => $fila->credit_rating,
      "address" => $fila->address,
      "city" => $fila->city,
      "state" => $fila->state,
      "country" => $fila->country,
      "zip_code" => $fila->zip_code
    ];
    $response[] = $customer;
 }

 sendResponse(200, $response);
 mysqli_close($conexion);
}

function getCustomer($name) {
  $conexion = mysqli_connect("mysql", "root", "secret", "SG");

  $cadenaSQL = "select * from s_customer where name = '" . $name . "'";
  $sentencia = mysqli_prepare($cadenaSQL);
  $resultado = mysqli_query($conexion, $cadenaSQL);
  $response = array();

  while ($fila = mysqli_fetch_object($resultado)) {
    $customer = [
      "name" => $fila->name,
      "credit_rating" => $fila->credit_rating,
      "address" => $fila->address,
      "city" => $fila->city,
      "state" => $fila->state,
      "country" => $fila->country,
      "zip_code" => $fila->zip_code
    ];
    $response[] = $customer;
 }

 sendResponse(200, $response);
 mysqli_close($conexion);
}

function notFound() {
  $response = array("error" => "Endpoint not found");
  sendResponse(404, $response);
}

// Handle the request
$app->handle();

?>
Ejemplo05-Docker-Compose-API/app/index.php
<html>
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title>Customer Catalog</title>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/css/bootstrap.min.css" integrity="sha384-WskhaSGFgHYWDcbwN70/dfYBj47jz9qbsMId/iRN3ewGhXQFZCSftd1LZCfmhktB" crossorigin="anonymous">
</head>
<body>
  <div class = "container">
    <div class="jumbotron">
      <h1 class="display-4">Sporting Goods App</h1>
      <p class="lead">Customer Catalog Sample Application</p>
      <hr class="my-4">
      <p>PHP sample application connected to a MySQL database to list a customer catalog</p>
    </div>
    <table class="table table-striped table-responsive">
      <thead>
        <tr>
          <th>Name</th>
          <th>Credit Rating</th>
          <th>Address</th>
          <th>City</th>
          <th>State</th>
          <th>Country</th>
          <th>Zip</th>
        </tr>
      </thead>
      <tbody>
        <?php

          $ch = curl_init();
          $url = "sgapi/customers";

          // set url
          //curl_setopt($ch, CURLOPT_URL, "http://localhost/json-producer");
          curl_setopt($ch, CURLOPT_URL, $url);
          curl_setopt($ch,CURLOPT_FOLLOWLOCATION,true);

          //return the transfer as a string
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

          // $output contains the output string
          $output = json_decode(curl_exec($ch), true);
          foreach ($output as $customer) {
            echo "<tr><td> " .$customer['name'] .
            "</td><td>" . $customer['credit_rating'] .
            "</td><td>" . $customer['address'] .
            "</td><td>" . $customer['city'] .
            "</td><td>" . $customer['state'] .
            "</td><td>" . $customer['country'] .
            "</td><td>" . $customer['zip_code'] .
            "</td></tr>";
            }

            // close curl resource to free up system resources
            curl_close($ch);

       ?>
     </tbody>
   </table>
 </div>
 <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
 <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity="sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin="anonymous"></script>
 <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.1/js/bootstrap.min.js" integrity="sha384-smHYKdLADwkXOn1EmN1qk/HfnUcbVRZyYmZ4qpPea6sjB/pTJ0euyQp0Mk8ck+5T" crossorigin="anonymous"></script>
</body>
</html>
Ejemplo05-Docker-Compose-API/docker-compose.yml
version: "2"
services:
  mysql:
    container_name: mysql
    restart: always
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: "secret" # TODO: Change this
    ports:
      - "3306:3306"
    volumes:
      - ./data:/var/lib/mysql
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql
  sgapi:
    container_name: sgapi
    restart: always
    image: ualmtorres/phalcon-apache-ubuntu
    ports:
      - "80:80"
    volumes:
      - ./api:/var/www/html
  sgapp:
    container_name: sgapp
    restart: always
    image: ualmtorres/phalcon-apache-ubuntu
    ports:
      - "81:80"
    volumes:
      - ./app:/var/www/html
  adminer:
    container_name: adminer
    image: adminer
    restart: always
    ports:
      - 8080:8080
Ejemplo05-Docker-Compose-API/lanzar-app-ejemplo.sh
#!/bin/bash
docker-compose up -d
aplicacion docker compose.png
Ejemplo05-Docker-Compose-API/api/Dockerfile
FROM ualmtorres/phalcon-apache-ubuntu

ADD . /var/www/html

VOLUME /var/www/html

EXPOSE 80
Ejemplo05-Docker-Compose-API/app/Dockerfile
FROM ualmtorres/phalcon-apache-ubuntu

ADD . /var/www/html

VOLUME /var/www/html

EXPOSE 80
Ejemplo05-Docker-Compose-API/subir-imagenes.sh
#!/bin/bash
docker push ualmtorres/sgapi:v0
docker push ualmtorres/sgapp:v0
Ejemplo05-Docker-Compose-API/limpiar-entorno.sh
#!/bin/bash
docker-compose down

2.3. Ciclo de desarrollo con Docker Compose

  • Crear carpetas de desarrollo​

  • Crear docker-compose.yaml​

  • Lanzar docker-compose.yaml​

  • Crear Dockerfile​

  • Iterar​

    • Programar + Subir a repo​

    • Crear imagen propia​

    • Etiquetar como nueva versión​

    • Subir nueva imagen a Docker Hub​

    • Poner en producción​

3. Kubernetes

  • Proyecto Open Source 2014​

  • Plataforma para despliegue automático, escalado y gestión de aplicaciones contenedorizadas.​

  • Permite el despliegue de aplicaciones en un cluster sin pensar en las máquinas que lo soportan​

  • Ofrece:​

    • Replicación​

    • (Auto)escalado​

3.1. Arquitectura de Kubernetes

  • El Master inicia los contenedores de la aplicación. ​

  • El máster organiza los contenedores para que se ejecuten en los nodos del cluster. ​

  • Los nodos interactúan con el master informando del estado de los pods​

kubernetes arquitectura.png

3.2. Dónde usar Kubernetes

  • Local (desarrollo)​

    • Minikube​

  • Cloud​

    • AKS (Azure Kubernetes Service)​

    • GKE (Google Kubernetes Engine)​

    • EKS (Amazon Elastic Kubernetes Service)​

    • …​

  • On premise​

    • OpenStack (IaaS) + Rancher (k8s)​

    • …​

3.3. Ejemplo de coste diario en AKS

  • 4 nodos​

  • Características nodos​

    • 2 vcpu​

    • 7 GB RAM​

    • 32 GB HDD​

  • Coste diario total (6.5 EUR)​

    • 4.6 EUR MV​

    • 1 EUR almacenamiento​

    • 0.9 EUR Log analytics

3.4. kubectl

CLI para Kubernetes​

Bloques de ~/.kube/config:

  • Clusters (nombre, IP, certificado, …)​

  • Contextos (nombre, cluster, usuario)​

  • Usuarios (nombre, certificados, tokens, …)​

Uso:

  • Despliegue:

    $ kubectl apply -f <manifiesto.yaml>​
  • Destrucción:

    $ kubectl delete <manifiesto.yaml>​
  • Información:

    $ kubectl get [pods | deployments | services | hpa | namespaces | …]​

3.5. Obtener credenciales AKS para kubectl

  • Usar Cloud Shell​

    $ az aks get-credentials \​
    --resource-group <myResourceGroup> \​
    --name <myAKSCluster>​
  • El resultado está en ~/.kube/config de Azure Cloud Shell​

    • Cluster info​

    • Context info​

    • User info​

  • Pegar esos datos en ~/.kube/config del equipo de desarrollo​

3.6. Contextos

Configura kubectl contra un cluster Kubernetes​

Comandos:

$ kubectl config get-contexts​
$ kubectl config use-context mtorres-kube-cluster

3.7. Objetos de Kubernetes. Pod​

  • Grupo de uno o más contenedores de una aplicación y algunos recursos compartidos de esos contenedores (p.e. volúmenes, redes)​

  • Contenedores auxiliares en un pod:​

    • Proxy​

    • Volcado de logs​

    • Certificado SSL​

kubernetes pod.png

3.8. Despliegue de un pod

Ejemplo06-Pod/namespace-demo.yml
apiVersion: v1
kind: Namespace
metadata:
  name: demo
Ejemplo06-Pod/pod-myweb.yml
apiVersion: v1
kind: Pod
metadata:
  name: myweb
  namespace: demo
spec:
  containers:
    - name: myweb
      image: ualmtorres/myweb:v0
Ejemplo06-Pod/lanzar-ejemplo.sh
#!/bin/bash
kubectl apply -f namespace-demo.yml
kubectl apply -f pod-myweb.yml
kubectl get pods --watch
sudo kubectl port-forward myweb 80:80
crear imagen propia.png

3.9. Init containers

Ejemplo07-InitContainer/deployment-init-sg.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: demo
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql
          env:
            # Use secret in real usage
            - name: MYSQL_ROOT_PASSWORD
              value: secret
          ports:
            - containerPort: 3306
          volumeMounts:
            - name: workdir
              mountPath: /docker-entrypoint-initdb.d
      initContainers:
        - name: install
          image: busybox
          command:
            - wget
            - "-O"
            - "/work-dir/init.sql"
            - https://gist.githubusercontent.com/ualmtorres/eb328b653fcc5964f976b22c320dc10f/raw/448b00c44d7102d66077a393dad555585862f923/init.sql
          volumeMounts:
            - name: workdir
              mountPath: "/work-dir"
      dnsPolicy: Default
      volumes:
        - name: workdir
          emptyDir: {}
Ejemplo07-InitContainer/lanzar-ejemplo.sh
#!/bin/bash
kubectl apply -f deployment-init-sg.yml
kubectl get pods -n demo --watch
sudo kubectl port-forward `kubectl get pods -n demo | grep 'mysql' | awk '{print $1}'` -n demo 3306:3306

3.10. Objetos de Kubernetes. Deployment​

  • Declaración de los pods de una aplicación (servicio)​

    • Imagen de base​

    • Puertos​

    • Volúmenes​

    • Número de réplicas​

    • Recursos demandados (cpu, RAM)​

    • Límites para autoescalado​

    • …​​

kubernetes deployment.png

3.10.1. Despliegue básico

Ejemplo08-Deployment-Basico/deployment-myweb.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myweb
  namespace: demo
  labels:
    app: myweb
spec:
  revisionHistoryLimit: 2
  strategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: myweb
  template:
    metadata:
      labels:
        app: myweb
    spec:
      containers:
        - name: myweb
          image: ualmtorres/myweb:v0
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi
Ejemplo08-Deployment-Basico/lanzar-ejemplo.sh
#!/bin/bash
kubectl apply -f deployment-myweb.yml
kubectl get deployments -n demo --watch
sudo kubectl port-forward -n demo `kubectl get pods -n demo | grep 'myweb' | awk '{print $1}'` 80:80

3.10.2. Despliegue con base de datos, API y frontend

Ejemplo09-Deployment-SG/deployment-sgbd.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: demo
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:5.7
          env:
            # Use secret in real usage
            - name: MYSQL_ROOT_PASSWORD
              value: secret
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: workdir
              mountPath: /docker-entrypoint-initdb.d
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi
      initContainers:
        - name: install
          image: busybox
          command:
            - wget
            - "-O"
            - "/work-dir/init.sql"
            - https://gist.githubusercontent.com/ualmtorres/eb328b653fcc5964f976b22c320dc10f/raw/448b00c44d7102d66077a393dad555585862f923/init.sql
          volumeMounts:
            - name: workdir
              mountPath: "/work-dir"
      dnsPolicy: Default
      volumes:
        - name: workdir
          emptyDir: {}
Ejemplo09-Deployment-SG/deployment-sgapi.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sgapi
  namespace: demo
  labels:
    app: sgapi
spec:
  revisionHistoryLimit: 2
  strategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: sgapi
  template:
    metadata:
      labels:
        app: sgapi
    spec:
      containers:
        - name: sgapi
          image: ualmtorres/sgapi:v0
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi
Ejemplo09-Deployment-SG/deployment-sgapp.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sgapp
  namespace: demo
  labels:
    app: sgapp
spec:
  revisionHistoryLimit: 2
  strategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: sgapp
  template:
    metadata:
      labels:
        app: sgapp
    spec:
      containers:
        - name: sgapp
          image: ualmtorres/sgapp:v0
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi
Ejemplo09-Deployment-SG/lanzar-ejemplo.sh
#!/bin/bash
kubectl apply -f deployment-sgbd.yml
kubectl apply -f deployment-sgapi.yml
kubectl apply -f deployment-sgapp.yml

kubectl get deployments -n demo --watch
sudo kubectl port-forward -n demo `kubectl get pods -n demo | grep 'sgapp' | awk '{print $1}'` 80:80
kubernetes desplegar deployment.png

La aplicación no consigue extraer los datos de la API. Esto se debe a que no se han creado los servicios, que son los objetos de Kubernetes que permiten el descubrimiento de pods.

3.11. Objetos de Kubernetes. Service​

  • Los servicios son una abstracción que definen un conjunto lógico de pods y una política de acceso a ellos estableciendo un nombre para acceder a ellos​

  • Cada pod tiene una dirección IP única, pero esa IP no se expone fuera del cluster sin lo que se denomina un Servicio​

  • Los servicios pemiten que las aplicaciones reciban tráfico​

    • ClusterIP: Servicio con IP interna a nivel de cluster ​

    • NodePort: Servicio expuesto fuera del cluster concatenando IP del nodo con puerto [30000-32767]​

    • LoadBalancer: Ofrece una IP externa​

    • ExternalName: Expone el servicio usando un nombre arbitrario

  • Enrutado de tráfico entre pods proporcionando una abstracción que permite que los pods mueran y se repliquen sin impactar en la aplicación. ​

  • Gestionan el descubrimiento y enrutado entre pods dependientes (p.e. frontend y backend)

kubernetes service.png

3.12. Despliegue de un Service​

Ejemplo10-Service/deployment-sgbd.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: demo
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:5.7
          env:
            # Use secret in real usage
            - name: MYSQL_ROOT_PASSWORD
              value: secret
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: workdir
              mountPath: /docker-entrypoint-initdb.d
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi
      initContainers:
        - name: install
          image: busybox
          command:
            - wget
            - "-O"
            - "/work-dir/init.sql"
            - https://gist.githubusercontent.com/ualmtorres/eb328b653fcc5964f976b22c320dc10f/raw/448b00c44d7102d66077a393dad555585862f923/init.sql
          volumeMounts:
            - name: workdir
              mountPath: "/work-dir"
      dnsPolicy: Default
      volumes:
        - name: workdir
          emptyDir: {}
Ejemplo10-Service/deployment-sgapi.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sgapi
  namespace: demo
  labels:
    app: sgapi
spec:
  revisionHistoryLimit: 2
  strategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: sgapi
  template:
    metadata:
      labels:
        app: sgapi
    spec:
      containers:
        - name: sgapi
          image: ualmtorres/sgapi:v0
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi
Ejemplo10-Service/deployment-sgapp.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sgapp
  namespace: demo
  labels:
    app: sgapp
spec:
  revisionHistoryLimit: 2
  strategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: sgapp
  template:
    metadata:
      labels:
        app: sgapp
    spec:
      containers:
        - name: sgapp
          image: ualmtorres/sgapp:v0
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi
Ejemplo10-Service/services.yml
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: demo
spec:
  type: ClusterIP
  ports:
    - port: 3306
  selector:
    app: mysql
---
apiVersion: v1
kind: Service
metadata:
  name: sgapi
  namespace: demo
spec:
  type: ClusterIP
  ports:
    - port: 80
  selector:
    app: sgapi
---
apiVersion: v1
kind: Service
metadata:
  name: sgapp
  namespace: demo
spec:
  type: LoadBalancer
  ports:
    - port: 80
  selector:
    app: sgapp
Ejemplo10-Service/lanzar-ejemplo.sh
#!/bin/bash
kubectl apply -f deployment-sgbd.yml
kubectl apply -f deployment-sgapi.yml
kubectl apply -f deployment-sgapp.yml
kubectl apply -f services.yml

kubectl get deployments -n demo --watch
kubectl get services -n demo --watch
EXTERNAL_IP=`kubectl get services -n demo | grep "sgapp" | awk '{print $4}'`
echo -e "Abre en un navegador la EXTERNAL-IP: \033[0;31m${EXTERNAL_IP} \033[0;39m del servicio sgapp"

3.13. Objetos de Kubernetes. HorizontalPodAutoscaler​

  • Consulta cada 15s las métricas de uso (CPU, RAM, …) de cada pod​

  • Fija un mínimo y máximo de réplicas de un deployment​

  • Define las condiciones de stress (p.e. porcentaje de uso de la CPU)​

  • Ante stress escala hacia arriba​

  • 5m sin stress escala hacia abajo

kubernetes hpa.png
Weave Scope

Herramienta de Visualización y Monitorización de Docker y Kubernetes​

Ejemplo11-Autoscaling/lanzar-weavescope.sh
#!/bin/bash
kubectl apply -f "https://cloud.weave.works/k8s/scope.yaml?k8s-version=$(kubectl version | base64 | tr -d '\n')"
kubectl port-forward -n weave "$(kubectl get -n weave pod --selector=weave-scope-component=app -o jsonpath='{.items..metadata.name}')" 4040
weavescope.png

3.14. Autoescalado

Nos basamos en los mismos archivos de despliegue de la API, Front y Service, pero añadimos el autoescalado (HPA)

Ejemplo11-Autoscaling/deployment-sgbd.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mysql
  namespace: demo
spec:
  selector:
    matchLabels:
      app: mysql
  template:
    metadata:
      labels:
        app: mysql
    spec:
      containers:
        - name: mysql
          image: mysql:5.7
          env:
            # Use secret in real usage
            - name: MYSQL_ROOT_PASSWORD
              value: secret
          ports:
            - containerPort: 3306
              name: mysql
          volumeMounts:
            - name: workdir
              mountPath: /docker-entrypoint-initdb.d
      initContainers:
        - name: install
          image: busybox
          command:
            - wget
            - "-O"
            - "/work-dir/init.sql"
            - https://gist.githubusercontent.com/ualmtorres/eb328b653fcc5964f976b22c320dc10f/raw/448b00c44d7102d66077a393dad555585862f923/init.sql
          volumeMounts:
            - name: workdir
              mountPath: "/work-dir"
      dnsPolicy: Default
      volumes:
        - name: workdir
          emptyDir: {}
Ejemplo11-Autoscaling/deployment-sgapi.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sgapi
  namespace: demo
  labels:
    app: sgapi
spec:
  revisionHistoryLimit: 2
  strategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: sgapi
  template:
    metadata:
      labels:
        app: sgapi
    spec:
      containers:
        - name: sgapi
          image: ualmtorres/sgapi:v0
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi
Ejemplo11-Autoscaling/deployment-sgapp.yml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: sgapp
  namespace: demo
  labels:
    app: sgapp
spec:
  revisionHistoryLimit: 2
  strategy:
    type: RollingUpdate
  selector:
    matchLabels:
      app: sgapp
  template:
    metadata:
      labels:
        app: sgapp
    spec:
      containers:
        - name: sgapp
          image: ualmtorres/sgapp:v0
          ports:
            - name: http
              containerPort: 80
          resources:
            requests:
              cpu: 100m
              memory: 128Mi
            limits:
              cpu: 250m
              memory: 256Mi
Ejemplo11-Autoscaling/services.yml
apiVersion: v1
kind: Service
metadata:
  name: mysql
  namespace: demo
spec:
  type: ClusterIP
  ports:
    - port: 3306
  selector:
    app: mysql
---
apiVersion: v1
kind: Service
metadata:
  name: sgapi
  namespace: demo
spec:
  type: ClusterIP
  ports:
    - port: 80
  selector:
    app: sgapi
---
apiVersion: v1
kind: Service
metadata:
  name: sgapp
  namespace: demo
spec:
  type: LoadBalancer
  ports:
    - port: 80
  selector:
    app: sgapp
Ejemplo11-Autoscaling/autoscaler.yml
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: sgapi
  namespace: demo
spec:
  scaleTargetRef:
    apiVersion: apps/v1beta1
    kind: Deployment
    name: sgapi
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 15
---
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
  name: sgapp
  namespace: demo
spec:
  scaleTargetRef:
    apiVersion: apps/v1beta1
    kind: Deployment
    name: sgapp
  minReplicas: 1
  maxReplicas: 10
  targetCPUUtilizationPercentage: 15
Ejemplo11-Autoscaling/lanzar-ejemplo-autoscaler.sh
#!/bin/bash
kubectl apply -f deployment-sgbd.yml
kubectl apply -f deployment-sgapi.yml
kubectl apply -f deployment-sgapp.yml
kubectl apply -f services.yml

kubectl get deployments -n demo --watch
kubectl get services -n demo --watch
EXTERNAL_IP=`kubectl get services -n demo | grep "sgapp" | awk '{print $4}'`
echo -e "Abre en un navegador la EXTERNAL-IP: \033[0;31m${EXTERNAL_IP} \033[0;39m del servicio sgapp"

kubectl apply -f autoscaler.yml

kubectl get hpa --namespace=demo --watch

3.15. Prueba de stress con Apache Benchmark

  • 10.000 peticiones totales​

  • 100 peticiones simultáneas​

$ ab -n 10000 -c 100 http://13.80.126.78/
autoescalado weavescope blue.png
autoescalado weavescope red.png
autoescalado ab.png
autoescalado kubectl.png

3.16. Ciclo de desarrollo con Kubernetes​

  • Crear carpetas de desarrollo​

  • Crear docker-compose.yaml​

  • Lanzar docker-compose.yaml​

  • Crear Dockerfile​

  • Iterar:

    • Programar + Subir a repo​

    • Crear imagen propia​

    • Etiquetar como nueva versión​

    • Subir nueva imagen a Docker Hub​

    • Poner en producción​