March 17, 2024

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

Composición acoplable

Hasta ahora hemos dedicado todo nuestro tiempo a explorar el cliente Docker. En el ecosistema Docker, sin embargo, hay muchas otras herramientas de código abierto que funcionan muy bien con Docker. Algunos de ellos son -

  1. Docker Machine : cree hosts Docker en su computadora, en proveedores de nube y dentro de su propio centro de datos
  2. Docker Compose : una herramienta para definir y ejecutar aplicaciones Docker de múltiples contenedores.
  3. Docker Swarm : una solución de agrupación en clústeres nativa para Docker
  4. Kubernetes : Kubernetes es un sistema de código abierto para automatizar la implementación, el escalado y la gestión de aplicaciones en contenedores.

En esta sección, veremos una de estas herramientas, Docker Compose, y veremos cómo puede facilitar el manejo de aplicaciones de múltiples contenedores.

La historia de fondo de Docker Compose es bastante interesante. Aproximadamente en enero de 2014, una empresa llamada OrchardUp lanzó una herramienta llamada Fig. La idea detrás de Fig era hacer que los entornos de desarrollo aislados funcionaran con Docker. El proyecto fue muy bien recibido en Hacker News ; curiosamente recuerdo haber leído sobre él, pero no lo entendí del todo.

El primer comentario en el foro realmente explica bien de qué se trata Fig.

Realmente, en este punto, de eso se trata Docker: ejecutar procesos. Ahora Docker ofrece una API bastante rica para ejecutar los procesos: volúmenes compartidos (directorios) entre contenedores (es decir, imágenes en ejecución), reenviar puerto desde el host al contenedor, mostrar registros, etc. Pero eso es todo: Docker por ahora permanece en el nivel de proceso.
Si bien proporciona opciones para organizar varios contenedores para crear una única "aplicación", no aborda la gestión de dicho grupo de contenedores como una sola entidad. Y ahí es donde entran herramientas como Fig: hablar de un grupo de contenedores como una sola entidad. Piense en "ejecutar una aplicación" (es decir, "ejecutar un grupo orquestado de contenedores") en lugar de "ejecutar un contenedor".

Resulta que mucha gente que usa Docker está de acuerdo con este sentimiento. Lenta y constantemente, a medida que Fig se hizo popular, Docker Inc. se dio cuenta, adquirió la empresa y cambió el nombre de Fig a Docker Compose.

Entonces, ¿para qué se utiliza Compose ? Compose es una herramienta que se utiliza para definir y ejecutar aplicaciones Docker de múltiples contenedores de una manera sencilla. Proporciona un archivo de configuración llamado docker-compose.ymlque se puede utilizar para abrir una aplicación y el conjunto de servicios del que depende con un solo comando. Compose funciona en todos los entornos: producción, puesta en escena, desarrollo, pruebas, así como flujos de trabajo de CI, aunque Compose es ideal para entornos de desarrollo y pruebas.

Veamos si podemos crear un docker-compose.ymlarchivo para nuestra aplicación SF-Foodtrucks y evaluar si Docker Compose cumple su promesa.

Sin embargo, el primer paso es instalar Docker Compose. Si está ejecutando Windows o Mac, Docker Compose ya está instalado tal como viene en Docker Toolbox. Los usuarios de Linux pueden obtener fácilmente Docker Compose siguiendo las instrucciones de los documentos. Dado que Compose está escrito en Python, también puedes simplemente hacerlo pip install docker-compose. Pruebe su instalación con -

$ docker-compose --version
docker-compose version 1.21.2, build a133471

Ahora que lo tenemos instalado, podemos pasar al siguiente paso, es decir, el archivo Docker Compose docker-compose.yml. La sintaxis de YAML es bastante simple y el repositorio ya contiene el archivo docker-compose que usaremos.

version: "3"
services:
  es:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.3.2
    container_name: es
    environment:
      - discovery.type=single-node
    ports:
      - 9200:9200
    volumes:
      - esdata1:/usr/share/elasticsearch/data
  web:
    image: yourusername/foodtrucks-web
    command: python3 app.py
    depends_on:
      - es
    ports:
      - 5000:5000
    volumes:
      - ./flask-app:/opt/flask-app
volumes:
  esdata1:
    driver: local

Permítanme desglosar lo que significa el archivo anterior. En el nivel principal, definimos los nombres de nuestros servicios esweb. El imageparámetro siempre es obligatorio y para cada servicio que queramos que ejecute Docker, podemos agregar parámetros adicionales. Para es, simplemente nos referimos a la elasticsearchimagen disponible en el registro de Elastic. Para nuestra aplicación Flask, nos referimos a la imagen que creamos al principio de esta sección.

Otros parámetros como commandportsproporcionan más información sobre el contenedor. El volumesparámetro especifica un punto de montaje en nuestro webcontenedor donde residirá el código. Esto es puramente opcional y es útil si necesita acceso a registros, etc. Más adelante veremos cómo esto puede ser útil durante el desarrollo. Consulte la referencia en línea para obtener más información sobre los parámetros que admite este archivo. También agregamos volúmenes para el escontenedor para que los datos que cargamos persistan entre reinicios. También especificamos depends_on, lo que le dice a Docker que inicie el escontenedor antes web. Puede leer más al respecto en Docker Compose Docs .

Nota: Debe estar dentro del directorio con el docker-compose.ymlarchivo para poder ejecutar la mayoría de los comandos de Redacción.

¡Excelente! Ahora el archivo está listo, veámoslo docker-composeen acción. Pero antes de comenzar, debemos asegurarnos de que los puertos y nombres estén libres. Entonces, si tiene los contenedores Flask y ES en ejecución, apaguémoslos.

$ docker stop es foodtrucks-web
es
foodtrucks-web

$ docker rm es foodtrucks-web
es
foodtrucks-web

Ahora podemos correr docker-compose. Navegue hasta el directorio de camiones de comida y ejecute docker-compose up.

$ docker-compose up
Creating network "foodtrucks_default" with the default driver
Creating foodtrucks_es_1
Creating foodtrucks_web_1
Attaching to foodtrucks_es_1, foodtrucks_web_1
es_1  | [2016-01-11 03:43:50,300][INFO ][node                     ] [Comet] version[2.1.1], pid[1], build[40e2c53/2015-12-15T13:05:55Z]
es_1  | [2016-01-11 03:43:50,307][INFO ][node                     ] [Comet] initializing ...
es_1  | [2016-01-11 03:43:50,366][INFO ][plugins                  ] [Comet] loaded [], sites []
es_1  | [2016-01-11 03:43:50,421][INFO ][env                      ] [Comet] using [1] data paths, mounts [[/usr/share/elasticsearch/data (/dev/sda1)]], net usable_space [16gb], net total_space [18.1gb], spins? [possibly], types [ext4]
es_1  | [2016-01-11 03:43:52,626][INFO ][node                     ] [Comet] initialized
es_1  | [2016-01-11 03:43:52,632][INFO ][node                     ] [Comet] starting ...
es_1  | [2016-01-11 03:43:52,703][WARN ][common.network           ] [Comet] publish address: {0.0.0.0} is a wildcard address, falling back to first non-loopback: {172.17.0.2}
es_1  | [2016-01-11 03:43:52,704][INFO ][transport                ] [Comet] publish_address {172.17.0.2:9300}, bound_addresses {[::]:9300}
es_1  | [2016-01-11 03:43:52,721][INFO ][discovery                ] [Comet] elasticsearch/cEk4s7pdQ-evRc9MqS2wqw
es_1  | [2016-01-11 03:43:55,785][INFO ][cluster.service          ] [Comet] new_master {Comet}{cEk4s7pdQ-evRc9MqS2wqw}{172.17.0.2}{172.17.0.2:9300}, reason: zen-disco-join(elected_as_master, [0] joins received)
es_1  | [2016-01-11 03:43:55,818][WARN ][common.network           ] [Comet] publish address: {0.0.0.0} is a wildcard address, falling back to first non-loopback: {172.17.0.2}
es_1  | [2016-01-11 03:43:55,819][INFO ][http                     ] [Comet] publish_address {172.17.0.2:9200}, bound_addresses {[::]:9200}
es_1  | [2016-01-11 03:43:55,819][INFO ][node                     ] [Comet] started
es_1  | [2016-01-11 03:43:55,826][INFO ][gateway                  ] [Comet] recovered [0] indices into cluster_state
es_1  | [2016-01-11 03:44:01,825][INFO ][cluster.metadata         ] [Comet] [sfdata] creating index, cause [auto(index api)], templates [], shards [5]/[1], mappings [truck]
es_1  | [2016-01-11 03:44:02,373][INFO ][cluster.metadata         ] [Comet] [sfdata] update_mapping [truck]
es_1  | [2016-01-11 03:44:02,510][INFO ][cluster.metadata         ] [Comet] [sfdata] update_mapping [truck]
es_1  | [2016-01-11 03:44:02,593][INFO ][cluster.metadata         ] [Comet] [sfdata] update_mapping [truck]
es_1  | [2016-01-11 03:44:02,708][INFO ][cluster.metadata         ] [Comet] [sfdata] update_mapping [truck]
es_1  | [2016-01-11 03:44:03,047][INFO ][cluster.metadata         ] [Comet] [sfdata] update_mapping [truck]
web_1 |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)

Dirígete a la IP para ver tu aplicación en vivo. Eso fue increíble ¿no? Solo unas pocas líneas de configuración y tendremos dos contenedores Docker ejecutándose exitosamente al unísono. Detengamos los servicios y volvamos a ejecutarlos en modo independiente.

web_1 |  * Running on http://0.0.0.0:5000/ (Press CTRL+C to quit)
Killing foodtrucks_web_1 ... done
Killing foodtrucks_es_1 ... done

$ docker-compose up -d
Creating es               ... done
Creating foodtrucks_web_1 ... done

$ docker-compose ps
      Name                    Command               State                Ports
--------------------------------------------------------------------------------------------
es                 /usr/local/bin/docker-entr ...   Up      0.0.0.0:9200->9200/tcp, 9300/tcp
foodtrucks_web_1   python3 app.py                   Up      0.0.0.0:5000->5000/tcp

Como era de esperar, podemos ver que ambos contenedores se ejecutan correctamente. ¿De dónde vienen los nombres? Estos fueron creados automáticamente por Compose. ¿Pero Compose también crea la red automáticamente? ¡Buena pregunta! Vamos a averiguar.

En primer lugar, detengamos la ejecución de los servicios. Siempre podemos recuperarlos con un solo comando. Los volúmenes de datos persistirán, por lo que es posible iniciar el clúster nuevamente con los mismos datos usando Docker-Compose Up. Para destruir el clúster y los volúmenes de datos, simplemente escriba docker-compose down -v.

$ docker-compose down -v
Stopping foodtrucks_web_1 ... done
Stopping es               ... done
Removing foodtrucks_web_1 ... done
Removing es               ... done
Removing network foodtrucks_default
Removing volume foodtrucks_esdata1

Mientras estamos en eso, también eliminaremos la foodtrucksred que creamos la última vez.

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

¡Excelente! Ahora que tenemos borrón y cuenta nueva, volvamos a ejecutar nuestros servicios y veamos si Compose hace su magia.

$ docker-compose up -d
Recreating foodtrucks_es_1
Recreating foodtrucks_web_1

$ docker container ls
CONTAINER ID        IMAGE                        COMMAND                  CREATED             STATUS              PORTS                    NAMES
f50bb33a3242        yourusername/foodtrucks-web  "python3 app.py"         14 seconds ago      Up 13 seconds       0.0.0.0:5000->5000/tcp   foodtrucks_web_1
e299ceeb4caa        elasticsearch                "/docker-entrypoint.s"   14 seconds ago      Up 14 seconds       9200/tcp, 9300/tcp       foodtrucks_es_1

Hasta ahora, todo bien. Es hora de ver si se crearon redes.

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

Puede ver que Compose siguió adelante y creó una nueva red llamada foodtrucks_defaulty adjuntó ambos nuevos servicios en esa red para que cada uno de ellos sea reconocible para el otro. Cada contenedor de un servicio se une a la red predeterminada y otros contenedores de esa red pueden acceder a él y descubrirlo en un nombre de host idéntico al nombre del contenedor.

$ docker ps
CONTAINER ID        IMAGE                                                 COMMAND                  CREATED              STATUS              PORTS                              NAMES
8c6bb7e818ec        docker.elastic.co/elasticsearch/elasticsearch:6.3.2   "/usr/local/bin/dock…"   About a minute ago   Up About a minute   0.0.0.0:9200->9200/tcp, 9300/tcp   es
7640cec7feb7        yourusername/foodtrucks-web                           "python3 app.py"         About a minute ago   Up About a minute   0.0.0.0:5000->5000/tcp             foodtrucks_web_1

$ docker network inspect foodtrucks_default
[
    {
        "Name": "foodtrucks_default",
        "Id": "f3b80f381ed3e03b3d5e605e42c4a576e32d38ba24399e963d7dad848b3b4fe7",
        "Created": "2018-07-30T03:36:06.0384826Z",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": true,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "7640cec7feb7f5615eaac376271a93fb8bab2ce54c7257256bf16716e05c65a5": {
                "Name": "foodtrucks_web_1",
                "EndpointID": "b1aa3e735402abafea3edfbba605eb4617f81d94f1b5f8fcc566a874660a0266",
                "MacAddress": "02:42:ac:13:00:02",
                "IPv4Address": "172.19.0.2/16",
                "IPv6Address": ""
            },
            "8c6bb7e818ec1f88c37f375c18f00beb030b31f4b10aee5a0952aad753314b57": {
                "Name": "es",
                "EndpointID": "649b3567d38e5e6f03fa6c004a4302508c14a5f2ac086ee6dcf13ddef936de7b",
                "MacAddress": "02:42:ac:13:00:03",
                "IPv4Address": "172.19.0.3/16",
                "IPv6Address": ""
            }
        },
        "Options": {},
        "Labels": {
            "com.docker.compose.network": "default",
            "com.docker.compose.project": "foodtrucks",
            "com.docker.compose.version": "1.21.2"
        }
    }
]

Flujo de trabajo de desarrollo

Antes de pasar a la siguiente sección, hay una última cosa que quería cubrir sobre Docker-Compose. Como se indicó anteriormente, Docker-compose es realmente excelente para el desarrollo y las pruebas. Entonces, veamos cómo podemos configurar Compose para hacernos la vida más fácil durante el desarrollo.

A lo largo de este tutorial, hemos trabajado con imágenes acoplables listas para usar. Si bien hemos creado imágenes desde cero, todavía no hemos tocado ningún código de aplicación y principalmente nos limitamos a editar Dockerfiles y configuraciones YAML. Una cosa que debes preguntarte es ¿cómo se ve el flujo de trabajo durante el desarrollo? ¿Se supone que uno debe seguir creando imágenes de Docker para cada cambio, luego publicarlas y luego ejecutarlas para ver si los cambios funcionan como se esperaba? Estoy seguro de que suena muy tedioso. Tiene que haber una mejor manera. En esta sección, eso es lo que vamos a explorar.

Veamos cómo podemos realizar un cambio en la aplicación Foodtrucks que acabamos de ejecutar. Asegúrate de tener la aplicación ejecutándose,

$ docker container ls
CONTAINER ID        IMAGE                                                 COMMAND                  CREATED             STATUS              PORTS                              NAMES
5450ebedd03c        yourusername/foodtrucks-web                           "python3 app.py"         9 seconds ago       Up 6 seconds        0.0.0.0:5000->5000/tcp             foodtrucks_web_1
05d408b25dfe        docker.elastic.co/elasticsearch/elasticsearch:6.3.2   "/usr/local/bin/dock…"   10 hours ago        Up 10 hours         0.0.0.0:9200->9200/tcp, 9300/tcp   es

Ahora veamos si podemos cambiar esta aplicación para que muestre un Hello world!mensaje cuando se realiza una solicitud de /helloruta. Actualmente, la aplicación responde con un 404.

$ curl -I 0.0.0.0:5000/hello
HTTP/1.0 404 NOT FOUND
Content-Type: text/html
Content-Length: 233
Server: Werkzeug/0.11.2 Python/2.7.15rc1
Date: Mon, 30 Jul 2018 15:34:38 GMT

¿Por qué pasó esto? Dado que la nuestra es una aplicación Flask, podemos ver app.pyenlace ) para obtener respuestas. En Flask, las rutas se definen con la sintaxis @app.route. En el archivo, verá que solo tenemos tres rutas definidas //debug/search. La /ruta representa la aplicación principal, la debugruta se usa para devolver información de depuración y finalmente searchla aplicación la usa para consultar elasticsearch.

$ curl 0.0.0.0:5000/debug
{
  "msg": "yellow open sfdata Ibkx7WYjSt-g8NZXOEtTMg 5 1 618 0 1.3mb 1.3mb\n",
  "status": "success"
}

Dado ese contexto, ¿cómo agregaríamos una nueva ruta para hello? ¡Lo adivinaste! Abramos flask-app/app.pyen nuestro editor favorito y hagamos el siguiente cambio.

@app.route('/')
def index():
  return render_template("index.html")

# add a new hello route
@app.route('/hello')
def hello():
  return "hello world!"

Ahora intentemos hacer una solicitud nuevamente.

$ curl -I 0.0.0.0:5000/hello
HTTP/1.0 404 NOT FOUND
Content-Type: text/html
Content-Length: 233
Server: Werkzeug/0.11.2 Python/2.7.15rc1
Date: Mon, 30 Jul 2018 15:34:38 GMT

¡Oh, no! ¡Eso no funcionó! que hicimos mal? Si bien hicimos el cambio en app.py, el archivo reside en nuestra máquina (o en la máquina host), pero como Docker ejecuta nuestros contenedores basándose en la yourusername/foodtrucks-webimagen, no conoce este cambio. Para validar esto, intentemos lo siguiente:

$ docker-compose run web bash
Starting es ... done
root@581e351c82b0:/opt/flask-app# ls
app.py        package-lock.json  requirements.txt  templates
node_modules  package.json       static            webpack.config.js
root@581e351c82b0:/opt/flask-app# grep hello app.py
root@581e351c82b0:/opt/flask-app# exit

Lo que intentamos hacer aquí es validar que nuestros cambios no estén en app.pyel contenedor que se está ejecutando. Hacemos esto ejecutando el comando docker-compose run, que es similar a su primo docker runpero toma argumentos adicionales para el servicio (que weben nuestro caso es ). Tan pronto como ejecutamos bash, el shell se abre /opt/flask-appcomo se especifica en nuestro Dockerfile . Desde el comando grep podemos ver que nuestros cambios no están en el archivo.

Veamos cómo podemos solucionarlo. En primer lugar, debemos indicarle a Docker Compose que no use la imagen y que en su lugar use los archivos localmente. También configuraremos el modo de depuración para trueque Flask sepa que debe recargar el servidor cuando app.pyhaya cambios. Reemplace la webparte del docker-compose.ymlarchivo así:

version: "3"
services:
  es:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.3.2
    container_name: es
    environment:
      - discovery.type=single-node
    ports:
      - 9200:9200
    volumes:
      - esdata1:/usr/share/elasticsearch/data
  web:
    build: . # replaced image with build
    command: python3 app.py
    environment:
      - DEBUG=True # set an env var for flask
    depends_on:
      - es
    ports:
      - "5000:5000"
    volumes:
      - ./flask-app:/opt/flask-app
volumes:
  esdata1:
    driver: local

Con ese cambio ( diff ), paremos y comencemos los contenedores.

$ docker-compose down -v
Stopping foodtrucks_web_1 ... done
Stopping es               ... done
Removing foodtrucks_web_1 ... done
Removing es               ... done
Removing network foodtrucks_default
Removing volume foodtrucks_esdata1

$ docker-compose up -d
Creating network "foodtrucks_default" with the default driver
Creating volume "foodtrucks_esdata1" with local driver
Creating es ... done
Creating foodtrucks_web_1 ... done

Como paso final, hagamos el cambio app.pyagregando una nueva ruta. Ahora intentamos rizar

$ curl 0.0.0.0:5000/hello
hello world

¡Guau! ¡Recibimos una respuesta válida! Intente experimentar haciendo más cambios en la aplicación.

Con esto concluye nuestro recorrido por Docker Compose. Con Docker Compose, también puede pausar sus servicios, ejecutar un comando único en un contenedor e incluso escalar la cantidad de contenedores. También le recomiendo que consulte algunos otros casos de uso de Docker Compose. Con suerte, pude mostrarles lo fácil que es administrar entornos de múltiples contenedores con Compose. En la sección final, implementaremos nuestra aplicación en AWS.

Servicio de contenedor elástico de AWS

En la última sección solíamos docker-composeejecutar nuestra aplicación localmente con un solo comando: docker-compose up. Ahora que tenemos una aplicación que funciona, queremos compartirla con el mundo, conseguir algunos usuarios, ganar toneladas de dinero y comprar una casa grande en Miami. La ejecución de los últimos tres está más allá del alcance del tutorial, por lo que dedicaremos nuestro tiempo a descubrir cómo podemos implementar nuestras aplicaciones de contenedores múltiples en la nube con AWS.

Si ha leído hasta aquí, estará bastante convencido de que Docker es una tecnología genial. Y tu no estas solo. Al ver el meteórico ascenso de Docker, casi todos los proveedores de la nube comenzaron a trabajar para agregar soporte para implementar aplicaciones Docker en su plataforma. A partir de hoy, puede implementar contenedores en Google Cloud Platform , AWS , Azure y muchos otros. Ya tenemos una introducción a la implementación de aplicaciones de contenedor único con Elastic Beanstalk y en esta sección veremos Elastic Container Service (o ECS) de AWS.

AWS ECS es un servicio de administración de contenedores escalable y súper flexible que admite contenedores Docker. Le permite operar un clúster Docker sobre instancias EC2 a través de una API fácil de usar. Mientras que Beanstalk tenía valores predeterminados razonables, ECS le permite ajustar completamente su entorno según sus necesidades. Esto hace que ECS, en mi opinión, sea bastante complejo para empezar.

¡Afortunadamente para nosotros, ECS tiene una herramienta CLI amigable que comprende los archivos Docker Compose y aprovisiona automáticamente el clúster en ECS! Dado que ya tenemos un funcionamiento, docker-compose.ymlno debería requerir mucho esfuerzo ponerlo en funcionamiento en AWS. ¡Entonces empecemos!

El primer paso es instalar la CLI. Las instrucciones para instalar la CLI tanto en Mac como en Linux se explican muy claramente en los documentos oficiales . Continúe, instale la CLI y cuando haya terminado, verifique la instalación ejecutando

$ ecs-cli --version
ecs-cli version 1.18.1 (7e9df84)

A continuación, trabajaremos en la configuración de la CLI para que podamos hablar con ECS. Seguiremos los pasos que se detallan en la guía oficial de los documentos de AWS ECS. En caso de cualquier confusión, no dude en consultar esa guía.

El primer paso implicará crear un perfil que usaremos durante el resto del tutorial. Para continuar, necesitarás tu AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY. Para obtenerlos, siga los pasos que se detallan en la sección titulada Clave de acceso y Clave de acceso secreta en esta página .

$ ecs-cli configure profile --profile-name ecs-foodtrucks --access-key $AWS_ACCESS_KEY_ID --secret-key $AWS_SECRET_ACCESS_KEY

A continuación, necesitamos obtener un par de claves que usaremos para iniciar sesión en las instancias. Dirígete a tu consola EC2 y crea un nuevo par de claves. Descargue el par de claves y guárdelo en un lugar seguro. Otra cosa a tener en cuenta antes de salir de esta pantalla es el nombre de la región. En mi caso, le puse un nombre a mi clave ecsy configuré mi región como us-east-1. Esto es lo que asumiré durante el resto de este tutorial.

Par de llaves EC2

El siguiente paso es configurar la CLI.

$ ecs-cli configure --region us-east-1 --cluster foodtrucks
INFO[0000] Saved ECS CLI configuration for cluster (foodtrucks)

Proporcionamos el configurecomando con el nombre de la región en la que queremos que resida nuestro clúster y un nombre de clúster. Asegúrese de proporcionar el mismo nombre de región que utilizó al crear el par de claves. Si no ha configurado AWS CLI en su computadora antes, puede usar la guía oficial , que explica todo con gran detalle sobre cómo hacer que todo funcione.

El siguiente paso permite a la CLI crear una plantilla de CloudFormation .

$ ecs-cli up --keypair ecs --capability-iam --size 1 --instance-type t2.medium
INFO[0000] Using recommended Amazon Linux 2 AMI with ECS Agent 1.39.0 and Docker version 18.09.9-ce
INFO[0000] Created cluster                               cluster=foodtrucks
INFO[0001] Waiting for your cluster resources to be created
INFO[0001] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
INFO[0062] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
INFO[0122] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
INFO[0182] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
INFO[0242] Cloudformation stack status                   stackStatus=CREATE_IN_PROGRESS
VPC created: vpc-0bbed8536930053a6
Security Group created: sg-0cf767fb4d01a3f99
Subnet created: subnet-05de1db2cb1a50ab8
Subnet created: subnet-01e1e8bc95d49d0fd
Cluster creation succeeded.

Aquí proporcionamos el nombre del par de claves que descargamos inicialmente ( ecsen mi caso), la cantidad de instancias que queremos usar ( --size) y el tipo de instancias en las que queremos que se ejecuten los contenedores. La --capability-iambandera le dice a la CLI que reconocemos que este comando puede crear recursos de IAM.

El último y último paso es donde usaremos nuestro docker-compose.ymlarchivo. Necesitaremos hacer algunos cambios menores, así que en lugar de modificar el original, hagamos una copia. El contenido de este archivo (después de realizar los cambios) se ve así (a continuación):

version: '2'
services:
  es:
    image: docker.elastic.co/elasticsearch/elasticsearch:7.6.2
    cpu_shares: 100
    mem_limit: 3621440000
    environment:
      - discovery.type=single-node
      - bootstrap.memory_lock=true
      - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
    logging:
      driver: awslogs
      options:
        awslogs-group: foodtrucks
        awslogs-region: us-east-1
        awslogs-stream-prefix: es
  web:
    image: yourusername/foodtrucks-web
    cpu_shares: 100
    mem_limit: 262144000
    ports:
      - "80:5000"
    links:
      - es
    logging:
      driver: awslogs
      options:
        awslogs-group: foodtrucks
        awslogs-region: us-east-1
        awslogs-stream-prefix: web

Los únicos cambios que hicimos con respecto al original docker-compose.ymlson proporcionar los valores mem_limit(en bytes) y cpu_sharespara cada contenedor y agregar alguna configuración de registro. Esto nos permite ver los registros generados por nuestros contenedores en AWS CloudWatch . Dirígete a CloudWatch para crear un grupo de registros llamado foodtrucks. Tenga en cuenta que, dado que ElasticSearch normalmente termina ocupando más memoria, hemos dado alrededor de 3,4 GB de límite de memoria. Otra cosa que debemos hacer antes de pasar al siguiente paso es publicar nuestra imagen en Docker Hub.

$ docker push yourusername/foodtrucks-web

¡Excelente! ¡Ahora ejecutemos el comando final que implementará nuestra aplicación en ECS!

$ cd aws-ecs
$ ecs-cli compose up
INFO[0000] Using ECS task definition                     TaskDefinition=ecscompose-foodtrucks:2
INFO[0000] Starting container...                         container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/es
INFO[0000] Starting container...                         container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/web
INFO[0000] Describe ECS container status                 container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/web desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-foodtrucks:2
INFO[0000] Describe ECS container status                 container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-foodtrucks:2
INFO[0036] Describe ECS container status                 container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-foodtrucks:2
INFO[0048] Describe ECS container status                 container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/web desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-foodtrucks:2
INFO[0048] Describe ECS container status                 container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus=RUNNING lastStatus=PENDING taskDefinition=ecscompose-foodtrucks:2
INFO[0060] Started container...                          container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/web desiredStatus=RUNNING lastStatus=RUNNING taskDefinition=ecscompose-foodtrucks:2
INFO[0060] Started container...                          container=845e2368-170d-44a7-bf9f-84c7fcd9ae29/es desiredStatus=RUNNING lastStatus=RUNNING taskDefinition=ecscompose-foodtrucks:2

No es una coincidencia que la invocación anterior sea similar a la que usamos con Docker Compose . Si todo salió bien, deberías ver a desiredStatus=RUNNING lastStatus=RUNNINGcomo última línea.

¡Impresionante! Nuestra aplicación está activa, pero ¿cómo podemos acceder a ella?

ecs-cli ps
Name                                      State    Ports                     TaskDefinition
845e2368-170d-44a7-bf9f-84c7fcd9ae29/web  RUNNING  54.86.14.14:80->5000/tcp  ecscompose-foodtrucks:2
845e2368-170d-44a7-bf9f-84c7fcd9ae29/es   RUNNING                            ecscompose-foodtrucks:2

¡Continúe y abra http://54.86.14.14 en su navegador y debería ver los Food Trucks en todo su esplendor negro y amarillo! Ya que estamos en el tema, veamos cómo se ve nuestra consola de AWS ECS .

Grupo

Podemos ver arriba que nuestro clúster ECS llamado 'foodtrucks' se creó y ahora ejecuta 1 tarea con 2 instancias de contenedor. Dedica un tiempo a explorar esta consola para familiarizarte con todas las opciones que hay aquí.

Limpiar

Una vez que haya jugado con la aplicación implementada, recuerde apagar el clúster.

$ ecs-cli down --force
INFO[0001] Waiting for your cluster resources to be deleted...
INFO[0001] Cloudformation stack status                   stackStatus=DELETE_IN_PROGRESS
INFO[0062] Cloudformation stack status                   stackStatus=DELETE_IN_PROGRESS
INFO[0124] Cloudformation stack status                   stackStatus=DELETE_IN_PROGRESS
INFO[0155] Deleted cluster                               cluster=foodtrucks

Ahí lo tienes. ¡Con solo unos pocos comandos pudimos implementar nuestra increíble aplicación en la nube de AWS!


CONCLUSIÓN

¡Y eso es una envoltura! Después de un tutorial largo, exhaustivo pero divertido, ¡ahora estás listo para conquistar el mundo de los contenedores! Si seguiste hasta el final, definitivamente deberías estar orgulloso de ti mismo. Aprendió a configurar Docker, ejecutar sus propios contenedores, jugar con sitios web estáticos y dinámicos y, lo más importante, adquirió experiencia práctica en la implementación de sus aplicaciones en la nube.

Espero que terminar este tutorial te haga tener más confianza en tus habilidades para manejar servidores. Cuando tenga la idea de crear su próxima aplicación, puede estar seguro de que podrá presentarla a la gente con un mínimo esfuerzo.

Próximos pasos

¡Tu viaje al mundo de los contenedores acaba de comenzar! Mi objetivo con este tutorial era abrirte el apetito y mostrarte el poder de Docker. En el mar de las nuevas tecnologías, puede resultar difícil navegar solo en las aguas y tutoriales como este pueden ser de ayuda. Este es el tutorial de Docker que desearía tener cuando comencé. Con suerte, cumplió su propósito de entusiasmarte con los contenedores para que ya no tengas que mirar la acción desde los lados.

A continuación se presentan algunos recursos adicionales que serán beneficiosos. Para su próximo proyecto, le recomiendo encarecidamente que utilice Docker. Recuerde: ¡la práctica hace la perfección!

Recursos adicionales

¡Vete, joven padawan!

Dar opinion

Ahora que el tutorial terminó, es mi turno de hacer preguntas. ¿Te gustó el tutorial? ¿El tutorial te pareció un completo desastre o te divertiste y aprendiste algo?

Envíame tus pensamientos directamente o simplemente crea un problema . También estoy en Twitter , así que si ese es tu trato, ¡no dudes en gritar allí!

Me encantaría conocer tu experiencia con este tutorial. Da sugerencias sobre cómo mejorar esto o déjame saber acerca de mis errores. Quiero que este tutorial sea uno de los mejores tutoriales introductorios de la web y no puedo hacerlo sin tu ayuda.

Tareas