March 17, 2024

Aprenda a crear e implementar sus aplicaciones distribuidas fácilmente en la nube con Docker-3

ENTORNOS DE MÚLTIPLES CONTENEDORES

En la última sección vimos lo fácil y divertido que es ejecutar aplicaciones con Docker. Comenzamos con un sitio web estático simple y luego probamos una aplicación Flask. Ambos podríamos ejecutarlos localmente y en la nube con solo unos pocos comandos. Una cosa que ambas aplicaciones tenían en común era que se ejecutaban en un único contenedor .

Aquellos de ustedes que tienen experiencia ejecutando servicios en producción saben que normalmente las aplicaciones hoy en día no son tan simples. Casi siempre hay una base de datos (o cualquier otro tipo de almacenamiento persistente) involucrada. Sistemas como Redis y Memcached se han convertido en elementos de rigor en la mayoría de las arquitecturas de aplicaciones web. Por lo tanto, en esta sección dedicaremos algo de tiempo a aprender cómo Dockerizar aplicaciones que dependen de diferentes servicios para ejecutarse.

En particular, veremos cómo podemos ejecutar y gestionar entornos acoplables de múltiples contenedores . ¿Por qué múltiples contenedores, te preguntarás? Bueno, uno de los puntos clave de Docker es la forma en que proporciona aislamiento. La idea de agrupar un proceso con sus dependencias en un sandbox (llamado contenedores) es lo que lo hace tan poderoso.

Así como es una buena estrategia desacoplar los niveles de su aplicación, es aconsejable mantener separados los contenedores para cada uno de los servicios . Es probable que cada nivel tenga diferentes necesidades de recursos y esas necesidades puedan crecer a diferentes ritmos. Al separar los niveles en diferentes contenedores, podemos componer cada nivel utilizando el tipo de instancia más apropiado según las diferentes necesidades de recursos. Esto también encaja muy bien con todo el movimiento de microservicios , que es una de las principales razones por las que Docker (o cualquier otra tecnología de contenedores) está a la vanguardia de las arquitecturas de microservicios modernas.

Camiones de comida de San Francisco

La aplicación que vamos a acoplar se llama SF Food Trucks. Mi objetivo al crear esta aplicación era tener algo que fuera útil (en el sentido de que se pareciera a una aplicación del mundo real), que dependiera de al menos un servicio, pero que no fuera demasiado complejo para el propósito de este tutorial. Esto es lo que se me ocurrió.

Camiones de comida de San Francisco

El backend de la aplicación está escrito en Python (Flask) y para la búsqueda utiliza Elasticsearch . Como todo lo demás en este tutorial, la fuente completa está disponible en Github . Usaremos esto como nuestra aplicación candidata para aprender cómo construir, ejecutar e implementar un entorno de contenedores múltiples.

Primero, clonemos el repositorio localmente.

$ git clone https://github.com/prakhar1989/FoodTrucks
$ cd FoodTrucks
$ tree -L 2
.
├── Dockerfile
├── README.md
├── aws-compose.yml
├── docker-compose.yml
├── flask-app
│   ├── app.py
│   ├── package-lock.json
│   ├── package.json
│   ├── requirements.txt
│   ├── static
│   ├── templates
│   └── webpack.config.js
├── setup-aws-ecs.sh
├── setup-docker.sh
├── shot.png
└── utils
    ├── generate_geojson.py
    └── trucks.geojson

La flask-appcarpeta contiene la aplicación Python, mientras que la utilscarpeta tiene algunas utilidades para cargar los datos en Elasticsearch. El directorio también contiene algunos archivos YAML y un Dockerfile, los cuales veremos con mayor detalle a medida que avancemos en este tutorial. Si tienes curiosidad, no dudes en echar un vistazo a los archivos.

Ahora que está entusiasmado (con suerte), pensemos en cómo podemos acoplar la aplicación. Podemos ver que la aplicación consta de un servidor backend Flask y un servicio Elasticsearch. Una forma natural de dividir esta aplicación sería tener dos contenedores: uno ejecutando el proceso Flask y otro ejecutando el proceso Elasticsearch (ES). De esa manera, si nuestra aplicación se vuelve popular, podemos escalarla agregando más contenedores dependiendo de dónde se encuentre el cuello de botella.

Genial, entonces necesitamos dos contenedores. Eso no debería ser difícil ¿verdad? Ya hemos construido nuestro propio contenedor Flask en la sección anterior. Y para Elasticsearch, veamos si podemos encontrar algo en el centro.

$ docker search elasticsearch
NAME                              DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
elasticsearch                     Elasticsearch is a powerful open source se...   697       [OK]
itzg/elasticsearch                Provides an easily configurable Elasticsea...   17                   [OK]
tutum/elasticsearch               Elasticsearch image - listens in port 9200.     15                   [OK]
barnybug/elasticsearch            Latest Elasticsearch 1.7.2 and previous re...   15                   [OK]
digitalwonderland/elasticsearch   Latest Elasticsearch with Marvel & Kibana       12                   [OK]
monsantoco/elasticsearch          ElasticSearch Docker image                      9                    [OK]

Como era de esperar, existe una imagen oficialmente compatible para Elasticsearch. Para que ES se ejecute, simplemente podemos usar docker runy tener un contenedor ES de un solo nodo ejecutándose localmente en poco tiempo.

Nota: Elastic, la empresa detrás de Elasticsearch, mantiene su propio registro para productos Elastic. Se recomienda utilizar las imágenes de ese registro si planea utilizar Elasticsearch.

Primero extraigamos la imagen.

$ docker pull docker.elastic.co/elasticsearch/elasticsearch:6.3.2

y luego ejecútelo en modo de desarrollo especificando puertos y configurando una variable de entorno que configura el clúster de Elasticsearch para que se ejecute como un solo nodo.

$ docker run -d --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2
277451c15ec183dd939e80298ea4bcf55050328a39b04124b387d668e3ed3943
Nota: Si su contenedor tiene problemas de memoria, es posible que deba modificar algunos indicadores de JVM para limitar su consumo de memoria.

Como se vio arriba, le damos --name esa nuestro contenedor un nombre que facilita su uso en comandos posteriores. Una vez que se inicia el contenedor, podemos ver los registros ejecutando docker container logscon el nombre del contenedor (o ID) para inspeccionar los registros. Debería ver registros similares a los siguientes si Elasticsearch se inició correctamente.

Nota: Elasticsearch tarda unos segundos en iniciarse, por lo que es posible que tengas que esperar antes de verlo initializeden los registros.
$ docker container ls
CONTAINER ID        IMAGE                                                 COMMAND                  CREATED             STATUS              PORTS                                            NAMES
277451c15ec1        docker.elastic.co/elasticsearch/elasticsearch:6.3.2   "/usr/local/bin/dock…"   2 minutes ago       Up 2 minutes        0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp   es

$ docker container logs es
[2018-07-29T05:49:09,304][INFO ][o.e.n.Node               ] [] initializing ...
[2018-07-29T05:49:09,385][INFO ][o.e.e.NodeEnvironment    ] [L1VMyzt] using [1] data paths, mounts [[/ (overlay)]], net usable_space [54.1gb], net total_space [62.7gb], types [overlay]
[2018-07-29T05:49:09,385][INFO ][o.e.e.NodeEnvironment    ] [L1VMyzt] heap size [990.7mb], compressed ordinary object pointers [true]
[2018-07-29T05:49:11,979][INFO ][o.e.p.PluginsService     ] [L1VMyzt] loaded module [x-pack-security]
[2018-07-29T05:49:11,980][INFO ][o.e.p.PluginsService     ] [L1VMyzt] loaded module [x-pack-sql]
[2018-07-29T05:49:11,980][INFO ][o.e.p.PluginsService     ] [L1VMyzt] loaded module [x-pack-upgrade]
[2018-07-29T05:49:11,980][INFO ][o.e.p.PluginsService     ] [L1VMyzt] loaded module [x-pack-watcher]
[2018-07-29T05:49:11,981][INFO ][o.e.p.PluginsService     ] [L1VMyzt] loaded plugin [ingest-geoip]
[2018-07-29T05:49:11,981][INFO ][o.e.p.PluginsService     ] [L1VMyzt] loaded plugin [ingest-user-agent]
[2018-07-29T05:49:17,659][INFO ][o.e.d.DiscoveryModule    ] [L1VMyzt] using discovery type [single-node]
[2018-07-29T05:49:18,962][INFO ][o.e.n.Node               ] [L1VMyzt] initialized
[2018-07-29T05:49:18,963][INFO ][o.e.n.Node               ] [L1VMyzt] starting ...
[2018-07-29T05:49:19,218][INFO ][o.e.t.TransportService   ] [L1VMyzt] publish_address {172.17.0.2:9300}, bound_addresses {0.0.0.0:9300}
[2018-07-29T05:49:19,302][INFO ][o.e.x.s.t.n.SecurityNetty4HttpServerTransport] [L1VMyzt] publish_address {172.17.0.2:9200}, bound_addresses {0.0.0.0:9200}
[2018-07-29T05:49:19,303][INFO ][o.e.n.Node               ] [L1VMyzt] started
[2018-07-29T05:49:19,439][WARN ][o.e.x.s.a.s.m.NativeRoleMappingStore] [L1VMyzt] Failed to clear cache for realms [[]]
[2018-07-29T05:49:19,542][INFO ][o.e.g.GatewayService     ] [L1VMyzt] recovered [0] indices into cluster_state

Ahora, intentemos ver si podemos enviar una solicitud al contenedor Elasticsearch. Usamos el 9200puerto para enviar una cURLsolicitud al contenedor.

$ curl 0.0.0.0:9200
{
  "name" : "ijJDAOm",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "a_nSV3XmTCqpzYYzb-LhNw",
  "version" : {
    "number" : "6.3.2",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "053779d",
    "build_date" : "2018-07-20T05:20:23.451332Z",
    "build_snapshot" : false,
    "lucene_version" : "7.3.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}

¡Dulce! ¡Se ve bien! Mientras estamos en eso, pongamos en funcionamiento nuestro contenedor Flask también. Pero antes de llegar a eso, necesitamos un Dockerfile. En la última sección, utilizamos python:3.8la imagen como nuestra imagen base. Esta vez, sin embargo, además de instalar las dependencias de Python a través de pip, queremos que nuestra aplicación también genere nuestro archivo Javascript minimizado para producción. Para esto, necesitaremos Nodejs. Dado que necesitamos un paso de compilación personalizado, comenzaremos desde la ubuntuimagen base para construir la nuestra Dockerfiledesde cero.

Nota: si descubre que una imagen existente no satisface sus necesidades, no dude en comenzar desde otra imagen base y modificarla usted mismo. Para la mayoría de las imágenes en Docker Hub, debería poder encontrar las correspondientes Dockerfileen Github. Leer los Dockerfiles existentes es una de las mejores formas de aprender a desarrollar el suyo propio.

Nuestro Dockerfile para la aplicación flask se ve a continuación:

# start from base
FROM ubuntu:18.04

MAINTAINER Prakhar Srivastav <prakhar@prakhar.me>

# install system-wide deps for python and node
RUN apt-get -yqq update
RUN apt-get -yqq install python3-pip python3-dev curl gnupg
RUN curl -sL https://deb.nodesource.com/setup_10.x | bash
RUN apt-get install -yq nodejs

# copy our application code
ADD flask-app /opt/flask-app
WORKDIR /opt/flask-app

# fetch app specific deps
RUN npm install
RUN npm run build
RUN pip3 install -r requirements.txt

# expose port
EXPOSE 5000

# start app
CMD [ "python3", "./app.py" ]

Hay bastantes cosas nuevas aquí, así que repasemos rápidamente este archivo. Comenzamos con la imagen base de Ubuntu LTS y usamos el administrador de paquetes apt-getpara instalar las dependencias, a saber, Python y Node. El yqqindicador se utiliza para suprimir la salida y asume "Sí" en todas las indicaciones.

Luego usamos el ADDcomando para copiar nuestra aplicación en un nuevo volumen en el contenedor: /opt/flask-app. Aquí es donde residirá nuestro código. También configuramos esto como nuestro directorio de trabajo, para que los siguientes comandos se ejecuten en el contexto de esta ubicación. Ahora que nuestras dependencias de todo el sistema están instaladas, pasamos a instalar las específicas de la aplicación. En primer lugar, abordamos Node instalando los paquetes desde npm y ejecutando el comando de compilación como se define en nuestro package.json archivo . Terminamos el archivo instalando los paquetes de Python, exponiendo el puerto y definiendo el puerto CMDpara ejecutar como lo hicimos en la última sección.

Finalmente, podemos seguir adelante, crear la imagen y ejecutar el contenedor (reemplácelo yourusernamecon su nombre de usuario a continuación).

$ docker build -t yourusername/foodtrucks-web .

En la primera ejecución, esto llevará algún tiempo ya que el cliente Docker descargará la imagen de Ubuntu, ejecutará todos los comandos y preparará su imagen. Volver a ejecutarlo docker builddespués de cualquier cambio posterior que realice en el código de la aplicación será casi instantáneo. Ahora intentemos ejecutar nuestra aplicación.

$ docker run -P --rm yourusername/foodtrucks-web
Unable to connect to ES. Retying in 5 secs...
Unable to connect to ES. Retying in 5 secs...
Unable to connect to ES. Retying in 5 secs...
Out of retries. Bailing out...

¡Ups! Nuestra aplicación flask no pudo ejecutarse porque no pudo conectarse a Elasticsearch. ¿Cómo le contamos a un contenedor sobre el otro contenedor y hacemos que hablen entre sí? La respuesta se encuentra en la siguiente sección.

Red acoplable

Antes de hablar sobre las características que Docker proporciona especialmente para lidiar con tales escenarios, veamos si podemos encontrar una manera de solucionar el problema. Con suerte, esto debería permitirle apreciar la característica específica que vamos a estudiar.

Bien, ejecutemos docker container ls(que es lo mismo que docker ps) y veamos qué tenemos.

$ docker container ls
CONTAINER ID        IMAGE                                                 COMMAND                  CREATED             STATUS              PORTS                                            NAMES
277451c15ec1        docker.elastic.co/elasticsearch/elasticsearch:6.3.2   "/usr/local/bin/dock…"   17 minutes ago      Up 17 minutes       0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp   es

Entonces tenemos un contenedor ES ejecutándose en 0.0.0.0:9200el puerto al que podemos acceder directamente. Si podemos decirle a nuestra aplicación Flask que se conecte a esta URL, debería poder conectarse y hablar con ES, ¿verdad? Profundicemos en nuestro código Python y veamos cómo se definen los detalles de la conexión.

es = Elasticsearch(host='es')

Para que esto funcione, debemos decirle al contenedor Flask que el contenedor ES se está ejecutando en el 0.0.0.0host (el puerto predeterminado es 9200) y eso debería hacer que funcione, ¿verdad? Desafortunadamente, eso no es correcto ya que la IP 0.0.0.0es la IP para acceder al contenedor ES desde la máquina host , es decir, desde mi Mac. Otro contenedor no podrá acceder a esto en la misma dirección IP. Bien, si no es esa IP, ¿a través de qué dirección IP debería ser accesible el contenedor ES? Me alegra que hayas hecho esta pregunta.

Ahora es un buen momento para comenzar nuestra exploración de las redes en Docker. Cuando se instala Docker, crea tres redes automáticamente.

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
c2c695315b3a        bridge              bridge              local
a875bec5d6fd        host                host                local
ead0e804a67b        none                null                local

La red puente es la red en la que se ejecutan los contenedores de forma predeterminada. Eso significa que cuando ejecuté el contenedor ES, se estaba ejecutando en esta red puente. Para validar esto, inspeccionemos la red.

$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "c2c695315b3aaf8fc30530bb3c6b8f6692cedd5cc7579663f0550dfdd21c9a26",
        "Created": "2018-07-28T20:32:39.405687265Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "277451c15ec183dd939e80298ea4bcf55050328a39b04124b387d668e3ed3943": {
                "Name": "es",
                "EndpointID": "5c417a2fc6b13d8ec97b76bbd54aaf3ee2d48f328c3f7279ee335174fbb4d6bb",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

Puede ver que nuestro contenedor 277451c15ec1aparece en la Containerssección del resultado. Lo que también vemos es la dirección IP que se le ha asignado a este contenedor: 172.17.0.2. ¿Es esta la dirección IP que estamos buscando? Averigüemos ejecutando nuestro contenedor flask e intentando acceder a esta IP.

$ docker run -it --rm yourusername/foodtrucks-web bash
root@35180ccc206a:/opt/flask-app# curl 172.17.0.2:9200
{
  "name" : "Jane Foster",
  "cluster_name" : "elasticsearch",
  "version" : {
    "number" : "2.1.1",
    "build_hash" : "40e2c53a6b6c2972b3d13846e450e66f4375bd71",
    "build_timestamp" : "2015-12-15T13:05:55Z",
    "build_snapshot" : false,
    "lucene_version" : "5.3.1"
  },
  "tagline" : "You Know, for Search"
}
root@35180ccc206a:/opt/flask-app# exit

Esto ya debería resultarle bastante sencillo. Iniciamos el contenedor en modo interactivo con el bashproceso. Es --rmuna opción conveniente para ejecutar comandos únicos, ya que el contenedor se limpia cuando finaliza su trabajo. Probamos un curlpero primero necesitamos instalarlo. Una vez que hacemos eso, vemos que podemos hablar con ES en 172.17.0.2:9200. ¡Impresionante!

Aunque hemos descubierto una manera de hacer que los contenedores se comuniquen entre sí, todavía existen dos problemas con este enfoque:

  1. ¿Cómo le decimos al contenedor Flask qué esnombre de host significa 172.17.0.2o alguna otra IP, ya que la IP puede cambiar?
  2. Dado que la red puente es compartida por todos los contenedores de forma predeterminada, este método no es seguro . ¿Cómo aislamos nuestra red?

La buena noticia es que Docker tiene una excelente respuesta a nuestras preguntas. Nos permite definir nuestras propias redes manteniéndolas aisladas mediante el docker networkcomando.

Primero sigamos adelante y creemos nuestra propia red.

$ docker network create foodtrucks-net
0815b2a3bb7a6608e850d05553cc0bda98187c4528d94621438f31d97a6fea3c

$ docker network ls
NETWORK ID          NAME                DRIVER              SCOPE
c2c695315b3a        bridge              bridge              local
0815b2a3bb7a        foodtrucks-net      bridge              local
a875bec5d6fd        host                host                local
ead0e804a67b        none                null                local

El network createcomando crea una nueva red puente , que es lo que necesitamos en este momento. En términos de Docker, una red puente utiliza un puente de software que permite que los contenedores conectados a la misma red puente se comuniquen, al tiempo que proporciona aislamiento de los contenedores que no están conectados a esa red puente. El controlador del puente Docker instala automáticamente reglas en la máquina host para que los contenedores en diferentes redes puente no puedan comunicarse directamente entre sí. Hay otros tipos de redes que puedes crear y te recomendamos leer sobre ellas en los documentos oficiales .

Ahora que tenemos una red, podemos lanzar nuestros contenedores dentro de esta red usando la --netbandera. Hagámoslo, pero primero, para lanzar un nuevo contenedor con el mismo nombre, detendremos y eliminaremos nuestro contenedor ES que se está ejecutando en la red puente (predeterminada).

$ docker container stop es
es

$ docker container rm es
es

$ docker run -d --name es --net foodtrucks-net -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2
13d6415f73c8d88bddb1f236f584b63dbaf2c3051f09863a3f1ba219edba3673

$ docker network inspect foodtrucks-net
[
    {
        "Name": "foodtrucks-net",
        "Id": "0815b2a3bb7a6608e850d05553cc0bda98187c4528d94621438f31d97a6fea3c",
        "Created": "2018-07-30T00:01:29.1500984Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": {},
            "Config": [
                {
                    "Subnet": "172.18.0.0/16",
                    "Gateway": "172.18.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "13d6415f73c8d88bddb1f236f584b63dbaf2c3051f09863a3f1ba219edba3673": {
                "Name": "es",
                "EndpointID": "29ba2d33f9713e57eb6b38db41d656e4ee2c53e4a2f7cf636bdca0ec59cd3aa7",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {}
    }
]

Como puede ver, nuestro escontenedor ahora se ejecuta dentro de la foodtrucks-netred puente. Ahora inspeccionemos qué sucede cuando iniciamos en nuestra foodtrucks-netred.

$ docker run -it --rm --net foodtrucks-net yourusername/foodtrucks-web bash
root@9d2722cf282c:/opt/flask-app# curl es:9200
{
  "name" : "wWALl9M",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "BA36XuOiRPaghPNBLBHleQ",
  "version" : {
    "number" : "6.3.2",
    "build_flavor" : "default",
    "build_type" : "tar",
    "build_hash" : "053779d",
    "build_date" : "2018-07-20T05:20:23.451332Z",
    "build_snapshot" : false,
    "lucene_version" : "7.3.1",
    "minimum_wire_compatibility_version" : "5.6.0",
    "minimum_index_compatibility_version" : "5.0.0"
  },
  "tagline" : "You Know, for Search"
}
root@53af252b771a:/opt/flask-app# ls
app.py  node_modules  package.json  requirements.txt  static  templates  webpack.config.js
root@53af252b771a:/opt/flask-app# python3 app.py
Index not found...
Loading data in elasticsearch ...
Total trucks loaded:  733
 * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
root@53af252b771a:/opt/flask-app# exit

¡Guau! ¡Eso funciona! En redes definidas por el usuario como foodtrucks-net, los contenedores no solo pueden comunicarse por dirección IP, sino que también pueden resolver el nombre de un contenedor en una dirección IP. Esta capacidad se denomina descubrimiento automático de servicios . ¡Excelente! Lancemos nuestro contenedor Flask de verdad ahora:

$ docker run -d --net foodtrucks-net -p 5000:5000 --name foodtrucks-web yourusername/foodtrucks-web
852fc74de2954bb72471b858dce64d764181dca0cf7693fed201d76da33df794

$ docker container ls
CONTAINER ID        IMAGE                                                 COMMAND                  CREATED              STATUS              PORTS                                            NAMES
852fc74de295        yourusername/foodtrucks-web                           "python3 ./app.py"       About a minute ago   Up About a minute   0.0.0.0:5000->5000/tcp                           foodtrucks-web
13d6415f73c8        docker.elastic.co/elasticsearch/elasticsearch:6.3.2   "/usr/local/bin/dock…"   17 minutes ago       Up 17 minutes       0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp   es

$ curl -I 0.0.0.0:5000
HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 3697
Server: Werkzeug/0.11.2 Python/2.7.6
Date: Sun, 10 Jan 2016 23:58:53 GMT

Dirígete a http://0.0.0.0:5000 y mira tu gloriosa aplicación en vivo. Aunque podría haber parecido mucho trabajo, en realidad solo escribimos 4 comandos para pasar de cero a ejecutar. He recopilado los comandos en un script bash .

#!/bin/bash

# build the flask container
docker build -t yourusername/foodtrucks-web .

# create the network
docker network create foodtrucks-net

# start the ES container
docker run -d --name es --net foodtrucks-net -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2

# start the flask app container
docker run -d --net foodtrucks-net -p 5000:5000 --name foodtrucks-web yourusername/foodtrucks-web

Ahora imagina que estás distribuyendo tu aplicación a un amigo o ejecutándola en un servidor que tiene Docker instalado. ¡Puedes ejecutar una aplicación completa con un solo comando!

$ git clone https://github.com/prakhar1989/FoodTrucks
$ cd FoodTrucks
$ ./setup-docker.sh

¡Y eso es! Si me preguntas, ¡creo que esta es una forma extremadamente asombrosa y poderosa de compartir y ejecutar tus aplicaciones!