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` yservice
) -
Desplegar aplicaciones en Kubernetes
-
Crear
init containers
-
Crear una aplicación autoescalable
Repositorio usado en este seminario: |
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.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
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 »</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 »</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 »</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 »</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
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 »</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 »</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 »</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 »</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
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
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
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
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
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
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
-
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
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
-
…
-
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
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)
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
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/