Translate

domingo, 1 de diciembre de 2024

Ecommerce moderna con quasar + vue 3, mariadb, s3, nodejs, Caddy y docker. Capítulo I

Creando una Aplicación Web Moderna con Caddy, Quasar, Vue 3, Node.js, MySQL y S3

1. Caddy: Un servidor Web sencillo y eficiente desarrollado en GO. Un servidor con muchas características y fácil de utilizar. Si no lo conoces, te asombrará.

2. Quasar: Framework para Aplicaciones Vue.js. Es un framework que permite el desarrollo de aplicaciones web, móviles, PWA y de escritorio.

3. Vue 3: La Evolución del Framework.

4. Node.js: El Entorno de Ejecución del Lado del Servidor

5. MariaDB: La Base de Datos Relacional.

El ejemplo ya disponible LAPAYAS.A. 

lapayasa.com


Todos los días surgen ecommerce, los comerciantes buscan ampliar su mercado sin abrir nuevos locales, los fabricantes buscan eliminar intermediarios y mayor margen de beneficios, y todos lo quieren ya. 

Con ellos ha surgido un nuevo nicho de mercado que son las plataformas de ecommerce que busca democratizar las tiendas para todos. Otros acuden a los diseñadores que por poco más de 2000€ te proporcionan un WOOCOMERCE y tienen algo más de control sobre cómo cubrir sus necesidades. Y si solo quieres tener una infraestructura ya creada que te proporcione soporte para vender, aunque compitiendo con otros vendedores iguales que tú o incluso mucho más preparados, siempre tienes Amazon, Miravia, ...etc. 

Con la incorporación de la IA se incorpora una nueva herramienta que busca producir mejores resultados en la creación de páginas web con unos pocos pasos y con los mejores resultados. La promesa es que no necesitas saber casi nada para crear una web  con buen diseño y que cumplas tus expectativas.

Todas las soluciones son muy válidas y las diferencian unos pocos aspectos pero muy importantes. En todos los casos trabajas para una plataforma o suerte de framework que alimentas con tus productos, fotos, contenido y que no controlas. Deberás cumplir todas las reglas que te impongan y pagar las comisiones correspondientes, a cambio te dejarán competir con los demás usuarios.

Poner todos los huevos en la misma cesta conlleva peligros que dejo a tu imaginación. El hecho es que muchos de estos usuarios no solo compiten en una plataforma sino en varias, esto supone muchas más actualizaciones de productos, precios, stock, ...penalizaciones por rotura de stock, por no cumplir el envío en fecha, y en muchos casos, continuar con las ventas en el local comercial.

Mi propuesta siempre ha sido trabaja una vez y que la información se distribuya en todos los canales ocupando solo una fracción de tu tiempo, de esta forma es posible disponer de negocios que no requieran de grandes inversiones en infraestructura y personal, que puedan crecer en características a medida que tu empresa las necesite. Este pensamiento es el que me llevó a crear KGESTION (PYTHON 3.11 + PYSIDE 6 + MARIADB ) allá por el 2021 un poco antes de la PANDEMIA, permitiendo que muchas personas pudieran realizar su trabajo desde el domicilio durante los días de aislamiento. 

El siguiente paso ha sido seleccionar las mejores tecnologías, aunque siempre hay algo mejor de lo que puedas seleccionar, la perfección siempre es enemiga de lo bueno, y llegué hasta quasar 2 + vue 3 + nodejs que junto a la base de datos ya creada para KGESTION, me ha permitido desarrollar una SPA mucho más ligera y parametrizable que cualquier sistema desarrollado en PHP.  El reto posterior ha sido proporcionar información a los buscadores (crawlers) desde la SPA, que no es imposible pero necesita algo de paciencia y pruebas, pero es posible a pesar de que todo el mundo aconseja desarrollar una SSR.

Otra de las ocurrencias ha sido disponer todos los archivos estáticos como imágenes, videos, documentos, ...etc en un S3 ( me refiero a S3 porque es el servicio más conocido por todos por ser de AWS, pero en realidad me refiero a un servicio 'block storage' disponible en muchos proveedores). 

Además, como es posible crear tantos dominios y/o webs distintas para multiplicar los canales de ventas y competir en mejores condiciones, se hace imprescindible crear un API que englobe todos los servicios comunes como accesos a la base de datos, securización de usuarios, plataformas de pago, plataformas de envíos, accesos a block storage y disponibilidad de imágenes, vídeos, documentos,... KODAMA ( nodejs + sequelize + mariadb ).

Aunque blog estuvo dedicado inicialmente a PYTHON, es posible compartir información entre varios sistemas, es en este punto final cuando se muestra la conexión y sincronización del sistema KGESTION con SHOPPRO que ha sido el nombre clave proporcionado al proyecto ecommerce.

Podrás seguir el desarrollo del proyecto SHOPPRO desde la dirección PURO QUASAR

Cómo optimizar tablas en MySQL


Al final se puede resumir que EXPLAIN MySQL es una herramienta indispensable (no la única) a la hora de realizar tareas de otpimización sql.

Explain MySQL no es más que una manera de mostrar como MySQL procesa las sentencias SQL mediante sus índices y uniones. El uso de Explain MySQL permite ayudar a los DBAs, en una primera instancia, a mejor el diseño de base de datos agregando índices y permitiendo una selección de consultas más óptimas.

Lo único que debemos hacer para hacer uso de Explain MySQL es anteponer “Explain” a la SQL deseada.

EXPLAIN SELECT * FROM `localidades` WHERE id =1

Ejecutando esta SQL, MySQL nos indica como la está procesando y nos mostrará un listado con información sobre índices, tablas, resultados, etc.

En el resultado de explain de mysql visualizaremos una tabla con 10 columnas de información para cada tabla implicada
explain mysql
De las columnas anteriores cabe destacar:

type: Esta columna indica el tipo de unión que se está usando (de más a menos óptimo).
const: Es la más óptima y se dá cuando la tabla tiene como máximo una fila que coincide. Como solo hay una fila coincidente, MySQL la considerará como constante por el optimizador.
eq_ref: Una fila será leída de la tabla A por cada combinación de fila de la tabla B. Este tipo es usada cuando todas las partes de un índice son usados para la consulta y el índice es UNIQUE o PRIMARY
ref: Todas las filas con valores en el índice que coincidan serán leídos desde esta tabla por cada combinación de filas de las tablas previas. Si la clave que es usada coincide sólo con pocas filas, esta unión es buena.
range: Sólo serán recuperadas las filas que estén en un rango dado, usando un índice para seleccionar las filas. La columna key indica que índice se usará, y el valor key_len contiene la parte más grande de la clave que fue usada. La columna ref será NULL para este tipo.
index: Este es el mismo que ALL, excepto que sólo el índice es escaneado. Este es usualmente más rápido que ALL, ya que el índice es usualmente de menor tamaño que la tabla completa.
ALL: Realiza un escaneo completo de tabla por cada combinación de filas de las tablas previas. Este caso es el peor de todos.
possible_keys: Esta columna indica los posibles índices a utilizar en la consulta
key: Esta columna indica el indice que MySQL actualmente está usando. Esta columna es NULL si no se ha elegido ninguno. Es interesante saber que podemos forzar a MySQL a usarlo (y también a ignorarlo) mediante el uso de FORCE INDEX, USE INDEX o IGNORE INDEX
key_len: El tamaño del índice usado. A menor valor mejor.
ref: La columna ref muestra que columna o constante es usada junto a la key para seleccionar las columnas de la tabla
rows: Indica el número de columnas que MySQL cree necesario examinar para ejecutar la SQL.
extra: Indica información adicional de como MySQL ha resuelto la SQL y hay que prestar atención si aparece USING FILESORT o USING TEMPORARY. En el primer caso, indica que MySQL debe hacer un paso extra para recuperar la información. En el segundo, MySQL necesita generar una tabla extra para mantener la información y después mostrarla y es típico al usar GROUP BY u ORDER BY.

En ocasiones, MySQL puede mostrar ALL en la columna type cuando:

La tabla es tan pequeña que MySQL ve más rapido hacer un escaneo completo de la tabla.
No hay restricciones usando las cláusulas ON o WHERE.
Se compara columnas indexadas con valores cosntantes
Las claves utilizadas devuelven muchos registros que coinciden con el valor.



Cada columna que devuelve EXPLAIN tiene el siguiente significado:

id - identificador, es sólo una secuencia numérica autoincrementada para cada una de las filas del explain
select_type - Debe ser uno de los siguiente tipos
SIMPLE - No usa subqueries ni UNION
PRIMARY - Último SELECT
UNION - Segunda o última sentencia SELECT en una UNION
DEPENDENT UNION - Segunda o última sentencia SELECT en una UNION, dependiente de otra query
SUBQUERY - Primera SELECT en una subquery
DEPENDENT SUBQUERY - Primera SELECT en una subquery, dependiente de otra query
DERIVED - Tabla SELECT derivada (subquery en una clausula FROM)
UNCACHEABLE SUBQUERY - Una subquery cuyo resultado no puede ser cacheado y debe ser re-evaluado para cada file de la otra query.

table - la tabla a la que se refiere la información
tipo - el tipo de unión empleado, que puede ser:
sytem - SYSTEM SELECT sólo hay una fila en la tabla (tipo especial del tipo const)
const - La tabla tiene como mucho un sola fila resultado, que es leída al principio de la operación convirtiéndose en una constante para el resto de las operaciones del select (operación muy rápida)
eq_ref - Una fila es leída de la tabla por cada combinación de filas en las tablas previas.
ref - Todas las filas con valores de índices validos son leídos de esta tabla por cada combinación de filas de las tablas previas.
fulltext - El join se utiliza mediante un índice FULLTEXT
ref_or_null - Este tipo de join es como el ref, pero además MySQL hace una busqueda extra por filas que contengan valores NULL
index_merge - la columna key en la fila de salida contiene una lista de indices a utilizar, y key_len contiene una lista de las claves más largas para los índices utilizados.
unique_subquery - Este tipo sustituye al ref para algunas subqueries IN de el siguiente tipo de estructuras: value IN (SELECT primary_key FROM single_table WHERE some_expr)
index_subquery - Este tipo join es similar al unique_subquery. Este sustituye subqueries IN, pero funciona para índices no únicos en subqueries del siguiente tipo: value IN (SELECT key_column FROM single_table WHERE some_expr)
range - Solamente las columnas que están en un rango dado son retornadas, empleando un índice para seleccionar las columnas. La columna key indica que índice es utilizado. key_len contiene el índice más largo de la parte del índice más larga utilizada. ref es NULL para este tipo.
index - Este ipo de join es el mismo que el de ALL, con la excepción de que sólo se realiza el escaneado a través de un índice. Esto es mas rápido que ALL ya que los ficheros índice son generalmente más pequeños que el fichero de datos.
all - Se realiza un escaneado de toda la tabla por cada combinación de filas de las tablas anteriores.

Estos tipos están ordenados en cuando a lo mejor o peor en cuanto a optimización. Así un tipo all sería lo peor y un tipo system sería lo mejor. Reducir de un tipo a otro, cambiando o creando índices para una determinada tabla o modificando la estrucutra misma de la petición es la tarea básica que debemos intentar para optimizar nuestra querys.

possible_keys - La columna indica que índices se pueden seleccionar para ser usados a la hora de encontrar filas en la tabla.
key - Índica que índice es empleado finalmente (podría se uno que no estuviera listado en las possible_keys).
key_len - Longitud del índice empleado
ref - Muestra que columnas o constante son empleadas para comparar con el índice
rows - Número de filas que MySQL piensa que debe examinar al ejecutar la petición
extra - Información adicional sobre la consulta.

Cuando modifiques la estructura de una tabla o añadas índices recuerda ejecutar un:

ANALIZE TABLE nombre_tabla;

Esto hará que el optimizador analize adecuadamente la tabla, los datos los usará para ayudarle a determinar la mejor estrategia en consultas posteriores. Tras modificar la tabla ejecuta ANALIZE y vuelve a ejecutar un EXPLAIN para observar las modificaciónes en el plan de ejecución de la consulta

domingo, 3 de noviembre de 2019

Crear un contenedor DOCKER (2) de forma eficiente

Desplegar contenedores DOCKER en servidores CLOUD, con recursos limitados (
CentOS 8, Nanode 1GB: 1 CPU, 25GB Storage, 1GB RAM),  nos exige profundizar más en cómo crear estos recursos.

Si has realizado todo el tutorial anterior comprobarás que la imagen resultado debería tener un tamaño aproximado de 1.13 Gb., excesivo para un arranque inmediato y un espacio muy limitado.

1.- Distribución Linux

Si observar en docker hub hay múltiples versiones de cada software, alguna etiquetada con la palabra 'alpine'. Utilizan como base la distribución  Alpine Linux, una distribución mínima, con los recursos necesarios e imprescindibles, que permite crear imágenes muy ajustadas. La ventaja : descarga de imágenes muy rápidas y arranques instantáneos.

Alpine Linux tiene su propio gestor de paquetes APK y su propio repositorio de paquetes.

2.- Qué es Dockerfile

Podemos usar los contenedores disponibles en docker hub, donde existen múltiples aplicaciones ya enlatadas. Pero podemos definir nuestras propias imágenes atendiendo a nuestras necesidades. Un fichero Dockerfile es la receta para construir una imagen de forma personalizada.

Previamente veamos algunas instrucciones necesarias para elaborar un Dockerfile :

  • FROM: imagen base sobre la que crearemos la imagen que construirá el Dockerfile.
  • MAINTAINER (deprecated): informa sobre el creador de la imagen. Cambiar esta instrucción por
    LABEL maintainer="email_prueba@home.org"
  • ENV HOME: establece el directorio HOME que usarán los comandos RUN.
  • RUN: permite ejecutar una instrucción en el contenedor, por ejemplo, para instalar algún paquete mediante el gestor de paquetes (apt-get, yum, rpm, apk…). Existen dos formatos :
  1. RUN (shell form, se ejecutará en un entorno que será, por defecto /bin/sh -c en Linux o cmd /S /C en Windows)
  2. RUN ["executable", "param1", "param2"] (ejecutar form)
  • ADD: permite añadir un archivo al contenedor, en muchas ocasiones se utiliza para proporcionar la configuración de los servicios (ssh, mysql, …).
  • VOLUME: establece puntos de montaje que al usar el contenedor se pueden proporcionar, los volúmenes son la forma de externalizar un determinado directorio y proporcionar persistencia (una imagen es de SOLO LECTURA y no almacena datos entre una ejecución y la siguiente).
  • EXPOSE: indica los puertos TCP/IP por los que se pueden acceder a los servicios del contenedor, los típicos son 22 (SSH), 80 (HTTP), 443 (HTTPS), 3306 (MySQL)...
    EXPOSE  [/...]
  • CDM: establece el comando del proceso de inicio que se usará si no se indica uno al iniciar un contenedor con la imagen. Solo puede existir una instrucción CMD por contenedor, si hubiera mas de una, solo se ejecutará la última. Existen tres formatos :
  1. CMD ["executable","param1","param2"] (es el formato preferido)
  2. CMD ["param1","param2"] (como default parameters a ENTRYPOINT)
  3. CMD command param1 param2 (entorno form)
Las variables de entorno están soportadas con las instrucciones :
  • ADD solo soportado en contenedores que se ejecutan sobre linux
    • ADD [--chown=:] ...
    • ADD [--chown=:] ["",... ""] (cuando el path contiene espacios)
  • COPY solo soportado en contenedores que se ejecutan sobre linux
    • COPY [--chown=:] ...
    • COPY [--chown=:] ["",... ""] (cuando el path contiene espacios)
  • ENV o ARG si existe la misma variable con ARG y ENV, ENV sobreescribe ARG 
    • ENV  
    • ENV = ...
  • EXPOSE
  • FROM
  • LABEL  incorpora metadatos a una imagen:  LABEL = = = ...
  • STOPSIGNAL
  • USER
  • VOLUME
  • WORKDIR establece el directorio de trabajo para cualquier instrucción RUN, CMD, ENTRYPOINT, COPY y ADD que la siga en el Dockerfile, tambien crea el directorio si no existe.
  • ENTRYPOINT permite configurar un contenedor que arrancará como ejecutable
    • ENTRYPOINT ["executable", "param1", "param2"] ( ejecución preferida)
    • ENTRYPOINT command param1 param2
Y los comentarios con :  # comment

Argumentos predefinidos, no debe usar ARG :
  • HTTP_PROXY
  • http_proxy
  • HTTPS_PROXY
  • https_proxy
  • FTP_PROXY
  • ftp_proxy
  • NO_PROXY
  • no_proxy
 
Podrás obtener una información más pormenorizada en https://docs.docker.com/engine/reference/builder/

Y ahora un fichero DOCKERFILE, dedique un tiempo para identificar cada acción :

# Alpine Linux - Python 3 and Apache/MOD_WSGI (NOT mod_wsgi-express)
# =============================================================================
# alpine linux has very up-to-date python3 and it can be used as the base.
# =============================================================================
FROM python:3.6-alpine

LABEL maintainer="PYMETRICK "

# Report short python version, e.g. "3.6" for mod_wsgi build
# =============================================================================
ENV PYTHON_VERSION=3.6

# HTTP Proxy Settings -  it is not necessary for me
#ARG http_proxy="http://one.proxy.example.com:8080"
#ARG https_proxy="http://one.proxy.example.com:8080"
#ARG HTTP_PROXY="http://one.proxy.example.com:8080"
#ARG HTTPS_PROXY="http://one.proxy.example.com:8080"

USER root

# Add apache2 packages (alpine package manager apk)
# =============================================================================
RUN apk --update --no-cache add apache2 apache2-dev \
    wget ca-certificates make gcc musl-dev

# Add mod_wsgi shared library compiled to this python3
# =============================================================================
ENV MOD_WSGI_VERSION=4.6.8
ENV MOD_WSGI_SRC_URL="https://github.com/GrahamDumpleton/mod_wsgi/archive/${MOD_WSGI_VERSION}.tar.gz"

RUN wget -O /usr/src/mod_wsgi.tar.gz "${MOD_WSGI_SRC_URL}" && \
    tar -zxvf /usr/src/mod_wsgi.tar.gz -C /usr/src && \
    rm /usr/src/mod_wsgi.tar.gz

WORKDIR /usr/src/mod_wsgi-${MOD_WSGI_VERSION}

ENV CFLAGS="-I/usr/local/include/python${PYTHON_VERSION}m/ -L/usr/local/lib/"
RUN ln -s /usr/lib/libpython${PYTHON_VERSION}m.so /usr/lib/libpython${PYTHON_VERSION}.so && \
    ./configure \
        --with-python=/usr/local/bin/python${PYTHON_VERSION} \
        --with-apxs=/usr/bin/apxs && \
    make && make install clean
RUN rm -rf /usr/src/mod_wsgi-${MOD_WSGI_VERSION}

# Set Apache2 Configurations
# =============================================================================
# Create PID file directory for /run/apache2/httpd.pid
RUN mkdir -p /run/apache2

# Set Servername to something.
RUN sed -i -r 's@#Servername.*@Servername wsgi@i' /etc/apache2/httpd.conf

# Direct access and error logs to stderr for Docker.
RUN sed -i -r 's@(CustomLog .*)@\1\nTransferLog /dev/stderr@i' /etc/apache2/httpd.conf
RUN sed -i -r 's@Errorlog .*@Errorlog /dev/stderr@i' /etc/apache2/httpd.conf

# Direct *.wsgi scripts to mod_wsgi
RUN echo -e "\n\n\
LoadModule wsgi_module modules/mod_wsgi.so\n\
WSGIPythonPath /usr/lib/python${PYTHON_VERSION}\n\
Alias / /home/apache/\n\
\n\
    Options ExecCGI FollowSymLinks\n\
    AllowOverride All\n\
    Require all granted\n\
    AddHandler wsgi-script .wsgi\n\
" >> /etc/apache2/httpd.conf

# "apache" runs a sample "hello world" WSGI script.
# =============================================================================
WORKDIR /home/apache
COPY ./hello.wsgi ./hello.wsgi

# Start Apache2 service with mod_wsgi

EXPOSE 80
EXPOSE 443
CMD ["httpd", "-D", "FOREGROUND", "-e", "info"]

# =============================================================================
# Clean up the package index.
# =============================================================================
RUN rm -rf /var/cache/apk/*



Partimos de la distribución Alpine Linux   FROM python:3.6-alpine
 
Informa del mantenedor  MAINTAINER pymetrick

Crear variables con ENV   ENV PYTHON_VERSION=3.6  que podremos utilizar en otras acciones como ${PYTHON_VERSION}

Ejecutar una acción de instalación RUN apk --update --no-cache add apache2 apache2-dev \ 

Copiamos un fichero desde el host al contenedor  
COPY ./hello.wsgi ./hello.wsgi

El contenido del fichero HELLO.WSGI es solo una prueba para comprobar que todo funciona correctamente una vez instalado y su código es el siguiente :

#! /usr/bin/env python
import logging
import logging.handlers

logger = logging.getLogger('hello_wsgi_logger')
logger.setLevel(logging.INFO)

handler = logging.StreamHandler()
formatter = logging.Formatter("""
{
    "loggerName": "%(name)s",
    "asciTime": "%(asctime)s",
    "pathName": "%(pathname)s",
    "logRecordCreationTime": "%(created)f",
    "functionName": "%(funcName)s",
    "levelNo": "%(levelno)s",
    "lineNo": "%(lineno)d",
    "time": "%(msecs)d",
    "levelName": "%(levelname)s",
    "message":"%(message)s"
}
""")

handler.formatter = formatter
logger.addHandler(handler)


def application(environ, start_response):
    status = '200 OK'
    output = [b'Hello Dockerized World!', b'\n' * 2]
    request_params = [
        '{}: {}'.format(key, value).encode('ascii')
        for key, value in sorted(environ.items())
        if key.startswith('wsgi.')
    ]
    output.extend(request_params)
    output = b'\n'.join(output)

    response_headers = [('Content-type', 'text/plain'),
                        ('Content-Length', str(len(output)))]
    start_response(status, response_headers)

    logger.info("Test Message")

    return [output]

 

3.- Fichero COMPOSE

Es un fichero YAML donde definimos los servicios, redes y volúmenes usados. Existen varias versiones de COMPOSE que debemos indicar en nuestro fichero al inicio, según la versión del motor Docker admita.

Compose file format Docker Engine release
3.7 18.06.0+
3.6 18.02.0+
3.5 17.12.0+
3.4 17.09.0+
3.3 17.06.0+
3.2 17.04.0+
3.1 1.13.1+
3.0 1.13.0+
2.4 17.12.0+
2.3 17.06.0+
2.2 1.13.0+
2.1 1.12.0+
2.0 1.10.0+
1.0 1.9.1.+

Mi fichero DOCKER-COMPOSE.YML :

version: '3.0'
services:
  py36:
    image: pymetrick/alpine-http-python-mod_wsgi:3.6
    build:
      context: .
      args:
        - pyversion=3.6
  latest:
    image: pymetrick/alpine-http-python-mod_wsgi:latest
    build: .

Para construir la imagen, puedes introducir toda la información necesaria en un script que posteriormente ejecutamos ( build_dockerfile.sh ):

#! /bin/bash
#
# Docker Image: Alpine Linux - Python 3 and Apache/MOD_WSGI
# =============================================================================
# Build docker image
# =============================================================================

NAMESPACE="pymetrick"
IMAGE_NAME="alpine-http-python-mod_wsgi"
TAG="3.6"

FULL_IMAGE_NAME="${NAMESPACE}/${IMAGE_NAME}:${TAG}"

docker build -t $FULL_IMAGE_NAME ./ \
    --build-arg http_proxy=$http_proxy \
    --build-arg https_proxy=$https_proxy \
    -f ./Dockerfile


Puede eliminar las siguientes lineas si no necesita parametrizar un proxy
    --build-arg http_proxy=$http_proxy \
    --build-arg https_proxy=$https_proxy \


Ya solo queda ejecutar el script  ./build_dockerfile.sh y obtenemos como resultado correcto :
Successfully built bb2a85d0f6ea
Successfully tagged pymetrick/alpine-http-python-mod_wsgi:3.6 


# docker images
REPOSITORY                                            TAG                   IMAGE ID             CREATED             SIZE
pymetrick/alpine-http-python-mod_wsgi   3.6                     bb2a85d0f6ea        2 minutes ago       304MB
python                                                        3.6-alpine          6ddaac33408f        12 days ago           95MB


Comprobamos con sumo agrado que solo hemos consumido 304 Mb.

 4.- TEST


Iniciamos el contenedor creado :

$ docker run --rm -ti --name http_one -p 8080:80 -p 8443:443 pymetrick/alpine-http-python-mod_wsgi:36

Nota : puede salir del entorno, sin parar el proceso, con CTRL+Q+P 

Comprobar procesos en ejecución dentro del contenedor :

$ docker top http_one 

Comprobar fichero que procesará los datos que devolvemos con el servidor web :

$ docker exec http_one ls -l /home/apache                        # sus datos de configuración
$ docker exec http_one cat /home/apache/hello.wsgi        # su contenido

Y la prueba definitiva desde cualquier navegador :

http://x.x.x.x:8080/hello.wsgi

y su resultado :

Hello Dockerized World!



wsgi.errors: <_io .textiowrapper="" encoding="utf-8" name="<wsgi.errors>">
wsgi.file_wrapper: 
wsgi.input: 
wsgi.input_terminated: True
wsgi.multiprocess: True
wsgi.multithread: False
wsgi.run_once: False
wsgi.url_scheme: http
wsgi.version: (1, 0)

 

5.- Eliminar IMAGENES, VOLUMENES, REDES Y CONTENEDORES


Es fácil acumular una cantidad excesiva de imágenes no utilizadas, contenedores y volúmenes de datos que consumen espacio en disco.

Los comandos que son útiles para liberar espacio en disco y mantener su sistema organizado al eliminar imágenes, contenedores y volúmenes de Docker no utilizados, se muestran a continuación: 

  • limpiará todos los recursos (imágenes, contenedores, volúmenes y redes) $ docker system prune
  • para eliminar adicionalmente los contenedores detenidos y todas las imágenes no utilizadas
    $ docker system prune -a
  • eliminar una o más imágenes 
    $ docker rmi Image Image 
  • detener todos los contenedores
    $ docker stop $(docker ps -a -q) 
  • borrar todos los contenedores
    $ docker rm $(docker ps -a -q)
  • borrar todas las imagenes
    $ docker rmi $(docker images -q)
          
Y hasta aquí todo lo que necesitará para profundizar en el mundo de los contenedores.  La próxima entrega hablamos de los ajustes necesarios para un sistema en producción, no te lo pierdas.