diff --git a/.gitignore b/.gitignore index 3c3629e..b9f5f3b 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ node_modules + +.idea/ diff --git a/Dockerfile b/Dockerfile index 60fce0c..7bbf29a 100644 --- a/Dockerfile +++ b/Dockerfile @@ -3,6 +3,7 @@ FROM python:alpine AS base WORKDIR /app COPY requirements.txt . +RUN apk add --no-cache g++ RUN pip install -r requirements.txt # Run tests to validate app diff --git a/docs/index.es.md b/docs/index.es.md new file mode 100644 index 0000000..6d9c155 --- /dev/null +++ b/docs/index.es.md @@ -0,0 +1,2 @@ +redirect: /tutorial/ + diff --git a/docs/tutorial/image-building-best-practices/index.es.md b/docs/tutorial/image-building-best-practices/index.es.md new file mode 100644 index 0000000..6aa2c96 --- /dev/null +++ b/docs/tutorial/image-building-best-practices/index.es.md @@ -0,0 +1,279 @@ +## Escaneo de Seguridad + + +Cuando haya creado una imagen, es una buena práctica escanearla en busca de vulnerabilidades de seguridad utilizando el +comando `docker scan`. +Docker se ha asociado con [Snyk](http://snyk.io) para proporcionar el servicio de análisis de vulnerabilidades. + +Por ejemplo, para escanear la imagen `getting-started` que creó anteriormente en el tutorial, simplemente escriba + +```bash +docker scan getting-started +``` + +El análisis utiliza una base de datos de vulnerabilidades que se actualiza constantemente, por lo que el resultado que +ve variará a medida que se descubran nuevas vulnerabilidades, pero podría verse así: + +```plaintext +✗ Low severity vulnerability found in freetype/freetype + Description: CVE-2020-15999 + Info: https://snyk.io/vuln/SNYK-ALPINE310-FREETYPE-1019641 + Introduced through: freetype/freetype@2.10.0-r0, gd/libgd@2.2.5-r2 + From: freetype/freetype@2.10.0-r0 + From: gd/libgd@2.2.5-r2 > freetype/freetype@2.10.0-r0 + Fixed in: 2.10.0-r1 + +✗ Medium severity vulnerability found in libxml2/libxml2 + Description: Out-of-bounds Read + Info: https://snyk.io/vuln/SNYK-ALPINE310-LIBXML2-674791 + Introduced through: libxml2/libxml2@2.9.9-r3, libxslt/libxslt@1.1.33-r3, nginx-module-xslt/nginx-module-xslt@1.17.9-r1 + From: libxml2/libxml2@2.9.9-r3 + From: libxslt/libxslt@1.1.33-r3 > libxml2/libxml2@2.9.9-r3 + From: nginx-module-xslt/nginx-module-xslt@1.17.9-r1 > libxml2/libxml2@2.9.9-r3 + Fixed in: 2.9.9-r4 +``` + +El resultado enumera el tipo de vulnerabilidad, una URL para obtener más información y, lo que es más importante, qué +versión de la biblioteca relevante corrige la vulnerabilidad. + +Hay varias otras opciones, sobre las que puede leer en [documentación docker scan](https://docs.docker.com/engine/scan/). + +Además de escanear su imagen recién construida en la línea de comando, también puede +[configurar Docker Hub](https://docs.docker.com/docker-hub/vulnerability-scanning/) +para escanear todas las imágenes recién enviadas automáticamente, y luego puede ver los resultados tanto en Docker Hub +como en Docker Desktop. + +![Escaneo de vulnerabilidades del hub](hvs.png){: style=width:75% } +{: .text-center } + +## Capas de imagen + +¿Sabías que puedes mirar lo que compone una imagen? Usando el comando `docker image history`, +puede ver el comando que se usó para crear cada capa dentro de una imagen. + +1. Utilice el comando `docker image history` para ver las capas en la imagen de `getting-started` que creó + anteriormente en el tutorial. + + ```bash + docker image history getting-started + ``` + + Debería obtener un resultado que se parezca a esto (los identificadores de fechas pueden ser diferentes). + + ```plaintext + IMAGE CREATED CREATED BY SIZE COMMENT + a78a40cbf866 18 seconds ago /bin/sh -c #(nop) CMD ["node" "src/index.j… 0B + f1d1808565d6 19 seconds ago /bin/sh -c yarn install --production 85.4MB + a2c054d14948 36 seconds ago /bin/sh -c #(nop) COPY dir:5dc710ad87c789593… 198kB + 9577ae713121 37 seconds ago /bin/sh -c #(nop) WORKDIR /app 0B + b95baba1cfdb 13 days ago /bin/sh -c #(nop) CMD ["node"] 0B + 13 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B + 13 days ago /bin/sh -c #(nop) COPY file:238737301d473041… 116B + 13 days ago /bin/sh -c apk add --no-cache --virtual .bui… 5.35MB + 13 days ago /bin/sh -c #(nop) ENV YARN_VERSION=1.21.1 0B + 13 days ago /bin/sh -c addgroup -g 1000 node && addu… 74.3MB + 13 days ago /bin/sh -c #(nop) ENV NODE_VERSION=12.14.1 0B + 13 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B + 13 days ago /bin/sh -c #(nop) ADD file:e69d441d729412d24… 5.59MB + ``` + + Cada una de las líneas representa una capa en la imagen. La pantalla aquí muestra la base en la parte inferior con + la capa más nueva en la parte superior. Con esto, también puede ver rápidamente el tamaño de cada capa, lo que ayuda + a diagnosticar imágenes grandes. + +1. Notarás que varias de las líneas están truncadas. Si agrega el indicador `--no-trunc`, obtendrá la salida completa + (sí ... es curioso cómo usa una bandera truncada para obtener una salida no truncada, ¿eh?) + + ```bash + docker image history --no-trunc getting-started + ``` + + +## Almacenamiento en caché de capas + +Ahora que ha visto las capas en acción, hay una lección importante que aprender para ayudar a reducir los build +times de las imágenes de su contenedor. + +> Una vez que cambia una capa, todas las capas posteriores también deben volver a crearse + +Veamos el Dockerfile que estábamos usando una vez más ... + +```dockerfile +FROM node:12-alpine +WORKDIR /app +COPY . . +RUN yarn install --production +CMD ["node", "src/index.js"] +``` + +Volviendo a la salida del historial de la imagen, vemos que cada comando en el Dockerfile se convierte en una nueva capa +en la imagen. Quizás recuerde que cuando hicimos un cambio en la imagen, las dependencias yarn tuvieron que reinstalarse. +¿Hay alguna forma de solucionar este problema? No tiene mucho sentido distribuir las mismas dependencias cada vez que +construimos, ¿verdad? + +Para solucionar esto, necesitamos reestructurar nuestro Dockerfile para ayudar a soportar el almacenamiento en caché de +las dependencias. Para aplicaciones Node-based, esas dependencias se definen en el archivo `package.json`. Entonces, +¿qué pasa si copiamos solo ese archivo primero, instalamos las dependencias y, luego, copiamos todo lo demás? Luego, +solo recreamos las dependencias yarn si hubo un cambio en el `package.json`. ¿Tener sentido? + +1. Actualice el Dockerfile para copiar en el `package.json` primero, instale las dependencias y luego copie todo lo + demás en. + + ```dockerfile hl_lines="3 4 5" + FROM node:12-alpine + WORKDIR /app + COPY package.json yarn.lock ./ + RUN yarn install --production + COPY . . + CMD ["node", "src/index.js"] + ``` + +1. Cree un archivo llamado `.dockerignore` en la misma carpeta que Dockerfile con el siguiente contenido. + + ```ignore + node_modules + ``` + + Los archivos `.dockerignore` son una manera fácil de copiar selectivamente solo archivos relevantes de imagen. + Puede leer más sobre esto + [aquí](https://docs.docker.com/engine/reference/builder/#dockerignore-file). + En este caso, la carpeta `node_modules` debe omitirse en el segundo paso `COPY` porque de lo contrario, + posiblemente sobrescribirá los archivos que fueron creados por el comando en el paso `RUN`. + Para obtener más detalles sobre por qué se recomienda esto para las aplicaciones Node.js y otras prácticas + recomendadas, echa un vistazo a su guía en + [Dockerizar una aplicación web Node.js](https://nodejs.org/en/docs/guides/nodejs-docker-webapp/). + +1. Cree una nueva imagen usando `docker build`. + + ```bash + docker build -t getting-started . + ``` + + Debería ver un resultado como este ... + + ```plaintext + Sending build context to Docker daemon 219.1kB + Step 1/6 : FROM node:12-alpine + ---> b0dc3a5e5e9e + Step 2/6 : WORKDIR /app + ---> Using cache + ---> 9577ae713121 + Step 3/6 : COPY package.json yarn.lock ./ + ---> bd5306f49fc8 + Step 4/6 : RUN yarn install --production + ---> Running in d53a06c9e4c2 + yarn install v1.17.3 + [1/4] Resolving packages... + [2/4] Fetching packages... + info fsevents@1.2.9: The platform "linux" is incompatible with this module. + info "fsevents@1.2.9" is an optional dependency and failed compatibility check. Excluding it from installation. + [3/4] Linking dependencies... + [4/4] Building fresh packages... + Done in 10.89s. + Removing intermediate container d53a06c9e4c2 + ---> 4e68fbc2d704 + Step 5/6 : COPY . . + ---> a239a11f68d8 + Step 6/6 : CMD ["node", "src/index.js"] + ---> Running in 49999f68df8f + Removing intermediate container 49999f68df8f + ---> e709c03bc597 + Successfully built e709c03bc597 + Successfully tagged getting-started:latest + ``` + + Verá que se reconstruyeron todas las capas. Perfectamente bien desde que cambiamos bastante el Dockerfile. + +1. Ahora, haga un cambio en el archivo `src/static/index.html` (como cambiar el `` para decir "The Awesome Todo App"). + +1. Cree la imagen de Docker ahora usando `docker build -t Getting started .` nuevamente. Esta vez, su salida debería + verse un poco diferente. + + ```plaintext hl_lines="5 8 11" + Sending build context to Docker daemon 219.1kB + Step 1/6 : FROM node:12-alpine + ---> b0dc3a5e5e9e + Step 2/6 : WORKDIR /app + ---> Using cache + ---> 9577ae713121 + Step 3/6 : COPY package.json yarn.lock ./ + ---> Using cache + ---> bd5306f49fc8 + Step 4/6 : RUN yarn install --production + ---> Using cache + ---> 4e68fbc2d704 + Step 5/6 : COPY . . + ---> cccde25a3d9a + Step 6/6 : CMD ["node", "src/index.js"] + ---> Running in 2be75662c150 + Removing intermediate container 2be75662c150 + ---> 458e5c6f080c + Successfully built 458e5c6f080c + Successfully tagged getting-started:latest + ``` + + Antes que nada, ¡Deberías notar que la construcción fue MUCHO más rápida! Y verá que todos los pasos 1-4 tienen + "Using cache". Entonces, ¡hurra! Estamos usando la caché de compilación. Pushing y pulling esta imagen y las + actualizaciones también será mucho más rápido. ¡Hurra! + + +## Construcciones Multi-Stage + +Si bien no vamos a profundizar demasiado en este tutorial, las compilaciones multi-stage son una herramienta +increíblemente poderosa para ayudar a usar multiple stages para crear una imagen. Tienen varias ventajas: + +- Separe las dependencias en build-time de las dependencias en runtime +- Reduzca el tamaño general de la imagen enviando _solo_ lo que su aplicación necesita para ejecutarse + +### Ejemplo Maven/Tomcat + +Al crear aplicaciones basadas en Java, se necesita un JDK para compilar el código fuente en código de bytes Java. +Sin embargo, ese JDK no es necesario en producción. Además, es posible que esté utilizando herramientas como Maven o +Gradle para ayudar a crear la aplicación. Esos tampoco son necesarios en nuestra imagen final. Las compilaciones de +varias etapas ayudan. + +```dockerfile +FROM maven AS build +WORKDIR /app +COPY . . +RUN mvn package + +FROM tomcat +COPY --from=build /app/target/file.war /usr/local/tomcat/webapps +``` + +En este ejemplo, usamos una etapa (llamada `build`) para realizar la compilación real de Java usando Maven. En la segunda +etapa (comenzando en `FROM tomcat`), copiamos archivos de la etapa de `build`. La imagen final es solo la última etapa +que se está creando (que se puede anular usando el indicador `--target`). + + +### Ejemplo React + +Al crear aplicaciones React, necesitamos un entorno Node para compilar el código JS (normalmente JSX), SASS stylesheets, +y más en HTML, JS, y CSS estático. Si no estamos haciendo renderizado del lado del servidor, ni siquiera necesitamos +un entorno Node para nuestra compilación de producción. ¿Por qué no enviar los recursos estáticos en un contenedor nginx +estático? + +```dockerfile +FROM node:12 AS build +WORKDIR /app +COPY package* yarn.lock ./ +RUN yarn install +COPY public ./public +COPY src ./src +RUN yarn run build + +FROM nginx:alpine +COPY --from=build /app/build /usr/share/nginx/html +``` + +Aquí, estamos usando una imagen `node: 12` para realizar la compilación (maximizando el almacenamiento en caché de la +capa) y luego copiando la salida en un contenedor nginx. Genial, ¿eh? + + +## Resumen + +Al comprender un poco cómo se estructuran las imágenes, podemos crear imágenes más rápido y enviar menos cambios. +El escaneo de imágenes nos da la confianza de que los contenedores que estamos ejecutando y distribuyendo son seguros. +Las compilaciones Multi-stage también nos ayudan a reducir el tamaño general de la imagen y a aumentar la seguridad del +contenedor final al separar las dependencias en build-time de las dependencias en runtime. \ No newline at end of file diff --git a/docs/tutorial/index.es.md b/docs/tutorial/index.es.md new file mode 100644 index 0000000..00d4561 --- /dev/null +++ b/docs/tutorial/index.es.md @@ -0,0 +1,75 @@ +--- +next_page: app.md +--- + +## El comando que acabas de ejecutar + +¡Felicidades! ¡Ha iniciado el contenedor para este tutorial! +Primero expliquemos el comando que acaba de ejecutar. En caso de que lo hayas olvidado +aquí está el comando: + +```cli +docker run -d -p 80:80 docker/getting-started +``` + +Notará que se utilizan algunas banderas. Aquí hay más información sobre ellas: + +- `-d` - ejecuta el contenedor en modo separado (en segundo plano) +- `-p 80:80` - mapea el puerto 80 del host al puerto 80 en el contenedor +- `docker/getting-started` - la imagen a usar + +!!! info "Consejo profesional" + Puede combinar banderas de un solo carácter para acortar el comando completo. + Como ejemplo, el comando anterior podría escribirse como: + ``` + docker run -dp 80:80 docker/getting-started + ``` + +## El panel de Docker + +Antes de ir demasiado lejos, queremos resaltar el panel de Docker, que le brinda +una vista rápida de los contenedores que se ejecutan en su máquina. Le brinda acceso +rápido a los registros del contenedor, le permite obtener un shell dentro del +contenedor y le permite administrar fácilmente el ciclo de vida del contenedor (detener, +eliminar, etc.). + +Para acceder al panel, siga las instrucciones para +[Mac](https://docs.docker.com/docker-for-mac/dashboard/) o +[Windows](https://docs.docker.com/docker-for-windows/dashboard/). Si abre el panel ahora, +verá este tutorial en ejecución. El nombre del contenedor (`jolly_bouman` a continuación) +es un nombre creado al azar. Por lo tanto, lo más probable es que tenga un nombre diferente. + +![Tutorial container running in Docker Dashboard](tutorial-in-dashboard.png) + + +## ¿Qué es un contenedor? + +Ahora que ha ejecutado un contenedor, que _es_ un contenedor? En pocas palabras, un contenedor es +simplemente otro proceso en su máquina que ha sido aislado de todos los demás procesos en la máquina host. +Ese aislamiento aprovecha [kernel namespaces y cgroups](https://medium.com/@saschagrunert/demystifying-containers-part-i-kernel-space-2c53d6979504), características que han estado +en Linux durante mucho tiempo. Docker ha trabajado para hacer que estas capacidades sean accesibles y fáciles de usar. + +!!! info "Creación de contenedores desde cero" + Si desea ver cómo se construyen los contenedores desde cero, Liz Rice de Aqua Security + tiene una charla fantástica en la que crea un contenedor desde cero en Go. Si bien hace + un contenedor simple, esta charla no se relaciona con las redes, el uso de imágenes para + el sistema de archivos y más. Pero ofrece una _fantástica_ inmersión en el modo en que + funcionan las cosas. + + <iframe width="560" height="315" src="https://www.youtube-nocookie.com/embed/8fi7uSYlOdc" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe> + +## ¿Qué es una imagen de contenedor? + +Cuando se ejecuta un contenedor, utiliza un sistema de archivos aislado. Este sistema de +archivos personalizado es proporcionado por una **imagen de contenedor**. Dado que la +imagen contiene el sistema de archivos del contenedor, debe contener todo lo necesario +para ejecutar una aplicación: todas las dependencias, configuración, scripts, binarios, +etc. La imagen también contiene otra configuración para el contenedor, como variables de +entorno, un comando predeterminado para ejecutar, y otros metadatos. + +Más adelante profundizaremos en las imágenes, cubriendo temas como capas, mejores prácticas y más. + +!!! info + Si está familiarizado con `chroot`, piense en un contenedor como una versión extendida de` chroot`. + El sistema de archivos simplemente proviene de la imagen. Pero, un contenedor agrega aislamiento + adicional que no está disponible cuando simplemente se usa chroot. \ No newline at end of file diff --git a/docs/tutorial/multi-container-apps/index.es.md b/docs/tutorial/multi-container-apps/index.es.md new file mode 100644 index 0000000..ff34591 --- /dev/null +++ b/docs/tutorial/multi-container-apps/index.es.md @@ -0,0 +1,266 @@ + +Hasta este momento, hemos estado trabajando con aplicaciones de contenedor único. Pero ahora queremos +agregar MySQL al stack de aplicaciones. A menudo surge la siguiente pregunta: "¿Dónde se ejecutará MySQL? +¿Instalarlo en el mismo contenedor o ejecutarlo por separado?" En general, **cada contenedor debe hacer +una cosa y hacerlo bien.** Algunas razones: + +- Existe una gran posibilidad de que tenga que escalar las API y los front-ends de manera diferente + a las bases de datos +- Los contenedores separados le permiten versionar y actualizar versiones de forma aislada +- Si bien puede usar un contenedor para la base de datos localmente, es posible que desee usar un + servicio administrado para la base de datos en producción. Entonces no desea enviar su motor de + base de datos con su aplicación. +- La ejecución de varios procesos requerirá un administrador de procesos (el contenedor solo inicia + un proceso), lo que agrega complejidad al inicio del contenedor + +Y hay más razones. Entonces, actualizaremos nuestra aplicación para que funcione así: + +![Todo App connected to MySQL container](multi-app-architecture.png) +{: .text-center } + + +## Redes de contenedores + +Recuerde que los contenedores, por defecto, se ejecutan de forma aislada y no saben nada sobre otros +procesos o contenedores en la misma máquina. Entonces, ¿cómo permitimos que un contenedor hable con +otro? La respuesta es la **creación de redes**. Ahora, no tiene que ser un ingeniero de redes (¡hurra!). +Simplemente recuerda esta regla ... + +> Si dos contenedores están en la misma red, pueden comunicarse entre sí. Si no lo están, no pueden. + + +## Iniciando MySQL + +Hay dos formas de poner un contenedor en una red: 1) Asignarlo al inicio o 2) Conectar un contenedor existente. +Por ahora, crearemos la red primero y adjuntaremos el contenedor MySQL al inicio. + +1. Crea la red. + + ```bash + docker network create todo-app + ``` + +1. Inicie un contenedor MySQL y conéctelo a la red. También vamos a definir algunas variables de entorno que la base de + datos utilizará para inicializar la base de datos. (consulte la sección "Environment Variables" en la [MySQL Docker Hub listing](https://hub.docker.com/_/mysql/)). + + ```bash + docker run -d \ + --network todo-app --network-alias mysql \ + -v todo-mysql-data:/var/lib/mysql \ + -e MYSQL_ROOT_PASSWORD=secret \ + -e MYSQL_DATABASE=todos \ + mysql:5.7 + ``` + + Si está utilizando PowerShell, utilice este comando. + + ```powershell + docker run -d ` + --network todo-app --network-alias mysql ` + -v todo-mysql-data:/var/lib/mysql ` + -e MYSQL_ROOT_PASSWORD=secret ` + -e MYSQL_DATABASE=todos ` + mysql:5.7 + ``` + + También verá que especificamos la bandera `--network-alias`. Volveremos a eso en un momento. + + !!! info "Pro-tip" + Notarás que estamos usando un volume named `todo-mysql-data` aquí y lo estamos montando en `/var/lib/mysql`, + que es donde MySQL almacena sus datos. Sin emabargo, nunca ejecutamos un comando `docker volume create`. + Docker reconoce que queremos usar un volume named y crea uno automáticamente para nosotros. + +1. Para confirmar que tenemos la base de datos en funcionamiento, conéctese a la base de datos y verifique que se conecte. + + ```bash + docker exec -it <mysql-container-id> mysql -p + ``` + + Cuando aparezca la solicitud de contraseña, escriba **secret**. En el shell de MySQL, enumere las bases + de datos y verifique que vea la base de datos `todos`. + + ```cli + mysql> SHOW DATABASES; + ``` + + Debería ver una salida que se ve así: + + ```plaintext + +--------------------+ + | Database | + +--------------------+ + | information_schema | + | mysql | + | performance_schema | + | sys | + | todos | + +--------------------+ + 5 rows in set (0.00 sec) + ``` + + ¡Hurra! ¡Tenemos nuestra base de datos `todos` y está lista para que la usemos! + + +## Conectarse a MySQL + +Ahora que sabemos que MySQL está funcionando, usémoslo! ¿Pero la pregunta es cómo? Si ejecutamos +otro contenedor en la misma red, ¿cómo encontramos el contenedor (recuerde que cada contenedor +tiene su propia dirección IP)? + +Para resolverlo, usaremos el [nicolaka/netshoot](https://github.com/nicolaka/netshoot) contenedor, +que se envía con _muchas_ herramientas que son útiles para solucionar problemas o depurar problemas de red. + +1. Inicie un nuevo contenedor con la imagen de nicolaka/netshoot. Asegúrate de conectarlo a la misma red. + + ```bash + docker run -it --network todo-app nicolaka/netshoot + ``` + +1. Dentro del contenedor, usaremos el comando `dig`, que es una útil herramienta de DNS. + Vamos a buscar la dirección IP del nombre de host `mysql`. + + ```bash + dig mysql + ``` + + Y obtendrás un resultado como este ... + + ```text + ; <<>> DiG 9.14.1 <<>> mysql + ;; global options: +cmd + ;; Got answer: + ;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 32162 + ;; flags: qr rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 0 + + ;; QUESTION SECTION: + ;mysql. IN A + + ;; ANSWER SECTION: + mysql. 600 IN A 172.23.0.2 + + ;; Query time: 0 msec + ;; SERVER: 127.0.0.11#53(127.0.0.11) + ;; WHEN: Tue Oct 01 23:47:24 UTC 2019 + ;; MSG SIZE rcvd: 44 + ``` + + En la "ANSWER SECTION", verá un registro `A` para` mysql` que se resuelve en `172.23.0.2` + (lo más probable es que su dirección IP tenga un valor diferente). Si bien `mysql` no es normalmente un nombre de + host válido, Docker pudo resolverlo en la dirección IP del contenedor que tenía ese alias de red (¿recuerda el + indicador` --network-alias` que usamos anteriormente?). + + Lo que esto significa es que... nuestra aplicación solo necesita conectarse a un host llamado `mysql` y se + comunicará con la base de datos. ¡No hay nada más simple que eso! + + +## Ejecutando nuestra aplicación con MySQL + +La aplicación **todo** admite la configuración de algunas variables de entorno para especificar la configuración de la +conexión MySQL. Ellos son: + +- `MYSQL_HOST` - el nombre de host del servidor MySQL en ejecución +- `MYSQL_USER` - el nombre de usuario que se utilizará para la conexión +- `MYSQL_PASSWORD` - la contraseña que se utilizará para la conexión +- `MYSQL_DB` - la base de datos para usar una vez conectado + +!!! warning Establecer la configuración de conexión a través de Env Vars + Si bien el uso de env vars para establecer la configuración de conexión generalmente está bien para el desarrollo, + Es **MUY DESALENTADOR** cuando se ejecutan aplicaciones en producción. Diogo Monica, el ex líder de seguridad en Docker, + [wrote a fantastic blog post](https://diogomonica.com/2017/03/27/why-you-shouldnt-use-env-variables-for-secret-data/) + explicando por qué. + + Un mecanismo más seguro es utilizar el soporte secreto proporcionado por su marco de orquestación de contenedores. + En la mayoría de los casos, estos secrets se montan como archivos en el contenedor en ejecución. Verá muchas + aplicaciones (incluida la imagen MySQL y la aplicación de tareas pendientes) que también admiten vars env con un + sufijo `_FILE` para señalar un archivo que contiene la variable. + + Como ejemplo, la configuración de la var `MYSQL_PASSWORD_FILE` hará que la aplicación use el contenido del archivo + referenciado como contraseña de conexión. Docker no hace nada para admitir estas variables de entorno. Su aplicación + necesitará saber para buscar la variable y obtener el contenido del archivo. + + +Con todo eso explicado, ¡comencemos nuestro contenedor listo para desarrolladores! + +1. Especificaremos cada una de las variables de entorno anteriores, además de conectar el contenedor a nuestra red de + aplicaciones. + + ```bash hl_lines="3 4 5 6 7" + docker run -dp 3000:3000 \ + -w /app -v "$(pwd):/app" \ + --network todo-app \ + -e MYSQL_HOST=mysql \ + -e MYSQL_USER=root \ + -e MYSQL_PASSWORD=secret \ + -e MYSQL_DB=todos \ + node:12-alpine \ + sh -c "yarn install && yarn run dev" + ``` + + Si está utilizando PowerShell, utilice este comando. + + ```powershell hl_lines="3 4 5 6 7" + docker run -dp 3000:3000 ` + -w /app -v "$(pwd):/app" ` + --network todo-app ` + -e MYSQL_HOST=mysql ` + -e MYSQL_USER=root ` + -e MYSQL_PASSWORD=secret ` + -e MYSQL_DB=todos ` + node:12-alpine ` + sh -c "yarn install && yarn run dev" + ``` + +1. Si miramos los registros del contenedor (`docker logs <container-id>`), deberíamos ver un mensaje que indica que está + usando la base de datos mysql. + + ```plaintext hl_lines="7" + # Previous log messages omitted + $ nodemon src/index.js + [nodemon] 1.19.2 + [nodemon] to restart at any time, enter `rs` + [nodemon] watching dir(s): *.* + [nodemon] starting `node src/index.js` + Connected to mysql db at host mysql + Listening on port 3000 + ``` + +1. Abra la aplicación en su navegador y agregue algunos elementos a su lista de tareas pendientes. + +1. Conéctese a la base de datos mysql y demuestre que los elementos se escriben en la base de datos. Recuerde, la + contraseña es **secreta**. + + ```bash + docker exec -it <mysql-container-id> mysql -p todos + ``` + + Y en el shell de mysql, ejecute lo siguiente: + + ```plaintext + mysql> select * from todo_items; + +--------------------------------------+--------------------+-----------+ + | id | name | completed | + +--------------------------------------+--------------------+-----------+ + | c906ff08-60e6-44e6-8f49-ed56a0853e85 | Do amazing things! | 0 | + | 2912a79e-8486-4bc3-a4c5-460793a575ab | Be awesome! | 0 | + +--------------------------------------+--------------------+-----------+ + ``` + + Obviamente, tu tabla se verá diferente porque tiene tus artículos. ¡Pero deberías verlos almacenados allí! + +Si echas un vistazo rápido al panel de Docker, verás que tenemos dos contenedores de aplicaciones ejecutándose. Pero no +hay indicios reales de que estén agrupados en una sola aplicación. ¡Veremos cómo mejorarlo en breve! + +![Docker Dashboard showing two ungrouped app containers](dashboard-multi-container-app.png) + +## Resumen + +En este punto, tenemos una aplicación que ahora almacena sus datos en una base de datos externa que se ejecuta en un +contenedor separado. Aprendimos un poco sobre redes de contenedores y vimos cómo se puede realizar el descubrimiento de +servicios utilizando DNS. + +Pero es muy probable que empiece a sentirse un poco abrumado con todo lo que necesita hacer para iniciar esta aplicación. +¡Tenemos que crear una red, iniciar contenedores, especificar todas las variables de entorno, exponer puertos y más! +Eso es mucho para recordar y ciertamente hace que las cosas sean más difíciles de transmitir a otra persona. + +En la siguiente sección, hablaremos sobre Docker Compose. Con Docker Compose, podemos compartir nuestras pilas de +aplicaciones de una manera mucho más fácil y dejar que otros las giren con un solo (y simple) comando. diff --git a/docs/tutorial/our-application/index.es.md b/docs/tutorial/our-application/index.es.md new file mode 100644 index 0000000..bcea450 --- /dev/null +++ b/docs/tutorial/our-application/index.es.md @@ -0,0 +1,114 @@ + +Para el resto de este tutorial, trabajaremos con un administrador de +lista de tareas simple que se ejecuta en Node.js. Si no está familiarizado +con Node.js, ¡no se preocupe! ¡No se necesita experiencia real en JavaScript! + +En este punto, su equipo de desarrollo es bastante pequeño y simplemente +está creando una aplicación para probar su PMV (producto mínimo viable). +Desea mostrar cómo funciona y qué es capaz de hacer sin necesidad de pensar +en cómo funcionará para un equipo grande, múltiples desarrolladores, etc. + +![Todo List Manager Screenshot](todo-list-sample.png){: style="width:50%;" } +{ .text-center } + +## Obteniendo nuestra aplicación + +Antes de que podamos ejecutar la aplicación, necesitamos obtener el código fuente +de la aplicación en nuestra máquina. Para proyectos reales, normalmente clonarás el +repositorio. Pero, para este tutorial, hemos creado un archivo ZIP que contiene la aplicación. + +1. [Descarga el ZIP](/assets/app.zip). Abra el archivo ZIP y asegúrese de extraer el contenido. + +1. Una vez extraído, usa tu editor de código favorito para abrir el proyecto. Si necesitas un + editor, puedes usar [Visual Studio Code](https://code.visualstudio.com/). Debería ver el + `package.json` y dos subdirectorios (`src` y `spec`). + + ![Screenshot of Visual Studio Code opened with the app loaded](ide-screenshot.png){: style="width:650px;margin-top:20px;"} + {: .text-center } + +## Creación de la imagen del contenedor de la aplicación + +Para construir la aplicación, necesitamos usar un `Dockerfile`. Un +dockerfile es simplemente un script de instrucciones basado en +texto que se usa para crear una imagen de contenedor. Si ha creado +Dockerfiles antes, es posible que vea algunas fallas en el Dockerfile +a continuación. ¡Pero no te preocupes! Los repasaremos. + +1. Cree un archivo llamado `Dockerfile` en la misma carpeta que el archivo `package.json` con el siguiente contenido. + + ```dockerfile + FROM node:12-alpine + RUN apk add --no-cache python g++ make + WORKDIR /app + COPY . . + RUN yarn install --production + CMD ["node", "src/index.js"] + ``` + + Compruebe que el archivo `Dockerfile` no tenga una extensión de archivo como `.txt`. Algunos editores pueden agregar esta extensión de archivo automáticamente y esto daría como resultado un error en el siguiente paso. + +1. Si aún no lo ha hecho, abra una terminal y vaya al directorio `app` con el `Dockerfile`. Ahora construya la imagen del contenedor usando el comando `docker build`. + + ```bash + docker build -t getting-started . + ``` + + Este comando usó el Dockerfile para crear una nueva imagen de contenedor. Es posible + que haya notado que se descargaron muchas "capas". Esto se debe a que le indicamos + al constructor que queríamos comenzar desde la imagen `node: 12-alpine`. Pero, como + no teníamos eso en nuestra máquina, era necesario descargar esa imagen. + + Después de descargar la imagen, copiamos en su aplicación y usamos `yarn` para instalar + las dependencias de su aplicación. La directiva `CMD` especifica el comando predeterminado + que se ejecutará al iniciar un contenedor desde esta imagen. + + Finalmente, la bandera `-t` etiqueta nuestra imagen. Piense en esto simplemente como un nombre + legible por humanos para la imagen final. Dado que llamamos a la imagen `getting-started`, + podemos hacer referencia a esa imagen cuando ejecutamos un contenedor. + + El `.` al final del comando `docker build` indica que Docker debe buscar el `Dockerfile` en el directorio actual. + +## Iniciar un contenedor de aplicaciones + +Ahora que tenemos una imagen, ¡ejecutemos la aplicación! Para hacerlo, usaremos el +comando `docker run` (¿recuerdas eso de antes?). + +1. Inicie su contenedor usando el comando `docker run` y especifique el nombre de la imagen que acabamos + de crear: + + ```bash + docker run -dp 3000:3000 getting-started + ``` + + ¿Recuerda las banderas `-d` y `-p`? Estamos ejecutando el nuevo contenedor en modo "separado" (en + segundo plano) y creando un mapeo entre el puerto 3000 del host y el puerto 3000 del contenedor. + Sin el mapeo de puertos, no podríamos acceder a la aplicación. + +1. Después de unos segundos, abra su navegador web a [http://localhost:3000](http://localhost:3000). + ¡Deberías ver nuestra aplicación! + + ![Empty Todo List](todo-list-empty.png){: style="width:450px;margin-top:20px;"} + {: .text-center } + +1. Continúe y agregue uno o dos elementos y compruebe que funcionan como espera. Puede marcar elementos + como completos y eliminar elementos. ¡Tu interfaz está almacenando elementos con éxito en el backend! + Bastante rápido y fácil, ¿eh? + + +En este punto, debería tener un administrador de lista de tareas en ejecución con algunos elementos, +¡todos creados por usted! Ahora, hagamos algunos cambios y aprendamos a administrar nuestros contenedores. + +Si echa un vistazo rápido al panel de Docker, debería ver sus dos contenedores ejecutándose ahora +(este tutorial y su contenedor de aplicaciones recién lanzado). + +![Docker Dashboard with tutorial and app containers running](dashboard-two-containers.png) + + +## Resumen + +En esta breve sección, aprendimos los conceptos básicos sobre cómo crear una imagen de contenedor +y crear un Dockerfile. Una vez que creamos una imagen, iniciamos el contenedor y vimos la aplicación +en ejecución. + +A continuación, haremos una modificación en nuestra aplicación y aprenderemos cómo actualizar nuestra +aplicación en ejecución con una nueva imagen. En el camino, aprenderemos algunos otros comandos útiles. \ No newline at end of file diff --git a/docs/tutorial/persisting-our-data/index.es.md b/docs/tutorial/persisting-our-data/index.es.md new file mode 100644 index 0000000..7083b7c --- /dev/null +++ b/docs/tutorial/persisting-our-data/index.es.md @@ -0,0 +1,166 @@ + +En caso de que no se haya dado cuenta, nuestra lista de tareas pendientes se borra cada vez +que lancemos el contenedor. ¿Por qué es esto? Veamos cómo está funcionando el contenedor. + +## El sistema de archivos del contenedor + +Cuando se ejecuta un contenedor, utiliza las distintas capas de una imagen para su sistema de archivos. +Cada contenedor también tiene su propio "espacio temporal" para crear/actualizar/eliminar archivos. +Los cambios no se verán en otro contenedor, _incluso si_ están usando la misma imagen. + +### Viendo esto en la práctica + +Para ver esto en acción, vamos a iniciar dos contenedores y crear un archivo en cada uno. +Lo que verá es que los archivos creados en un contenedor no están disponibles en otro. + +1. Inicie un contenedor `ubuntu` que creará un archivo llamado `/data.txt` con un número + aleatorio entre 1 y 10000. + + ```bash + docker run -d ubuntu bash -c "shuf -i 1-10000 -n 1 -o /data.txt && tail -f /dev/null" + ``` + + En caso de que sienta curiosidad por el comando, estamos iniciando un shell bash e invocando + dos comandos (porque tenemos `&&`). La primera parte elige un solo número aleatorio y lo + escribe en `/data.txt`. El segundo comando es simplemente mirar un archivo para mantener el + contenedor en ejecución. + +1. Validamos que podemos ver la salida ejecutando `exec` en el contenedor. Para hacerlo, abra el Panel de control y haga clic en la primera acción del contenedor que ejecuta la imagen de `ubuntu`. + + ![Dashboard open CLI into ubuntu container](dashboard-open-cli-ubuntu.png){: style=width:75% } +{: .text-center } + + Verá una terminal que está ejecutando un shell en el contenedor de ubuntu. Ejecute el siguiente comando para ver el contenido del archivo `/data.txt`. Cierra esta terminal luego nuevamente. + + ```bash + cat /data.txt + ``` + + Si prefiere la línea de comandos, puede usar el comando `docker exec` para hacer lo mismo. Necesita obtener el ID + del contenedor (use `docker ps` para obtenerlo) y obtener el contenido con el siguiente comando. + + ```bash + docker exec <container-id> cat /data.txt + ``` + + ¡Debería ver un número aleatorio! + +1. Ahora, comencemos otro contenedor `ubuntu` (la misma imagen) y veremos que no tenemos el mismo archivo. + + ```bash + docker run -it ubuntu ls / + ``` + + ¡Y mira! ¡No hay ningún archivo `data.txt` allí! Eso es porque se escribió en el espacio temporal solo + para el primer contenedor. + +1. Continúe y elimine el primer contenedor usando el comando `docker rm -f`. + +## Volúmenes de contenedores + +Con el experimento anterior, vimos que cada contenedor comienza desde la definición de la imagen cada vez +que comienza. Si bien los contenedores pueden crear, actualizar y eliminar archivos, esos cambios se pierden +cuando se elimina el contenedor y todos los cambios se aíslan en ese contenedor. Con los volúmenes, podemos +cambiar todo esto. + +[Volumes](https://docs.docker.com/storage/volumes/) brindan la capacidad de conectar rutas específicas del +sistema de archivos del contenedor a la máquina host. Si se monta un directorio en el contenedor, los cambios +en ese directorio también se ven en la máquina host. Si montamos ese mismo directorio en los reinicios del +contenedor, veríamos los mismos archivos. + +Hay dos tipos principales de volúmenes. Eventualmente usaremos ambos, pero comenzaremos con **volúmenes con nombre**. + +## Persistiendo nuestra Todo Data + +De forma predeterminada, la aplicación de tareas pendientes almacena sus datos en una +[SQLite Database](https://www.sqlite.org/index.html) en `/etc/todos/todo.db`. +Si no está familiarizado con SQLite, ¡no se preocupe! Es simplemente una base de datos relacional +en la que todos los datos se almacenan en un solo archivo. Si bien esto no es lo mejor para aplicaciones +a gran escala, funciona para demostraciones pequeñas. Hablaremos de cambiar esto a un motor de base de +datos diferente más adelante. + +Dado que la base de datos es un solo archivo, si podemos conservar ese archivo en el host y ponerlo a +disposición del siguiente contenedor, debería poder continuar donde lo dejó el último. Al crear un +volumen y adjuntarlo (a menudo llamado "montar") al directorio en el que se almacenan los datos, +podemos conservar los datos. A medida que nuestro contenedor escribe en el archivo `todo.db`, se +mantendrá en el host en el volumen. + +Como se mencionó, usaremos un **volumen con nombre**. Piense en un volumen con nombre como simplemente +un depósito de datos. Docker mantiene la ubicación física en el disco y solo necesita recordar el nombre +del volumen. Cada vez que utilice el volumen, Docker se asegurará de que se proporcionen los datos correctos. + +1. Cree un volumen usando el comando `docker volume create`. + + ```bash + docker volume create todo-db + ``` + +1. Detenga el contenedor de la aplicación **todo** una vez más desde el Dashboard (o con `docker rm -f <id>`), ya + que todavía se está ejecutando sin usar el volumen persistente. + +1. Inicie el contenedor de la aplicación **todo**, pero agregue el indicador `-v` para especificar un montaje de + volumen. Usaremos el volumen nombrado y lo montaremos en `/etc/todos`, que capturará todos los archivos creados + en la ruta. + + ```bash + docker run -dp 3000:3000 -v todo-db:/etc/todos getting-started + ``` + +1. Una vez que se inicia el contenedor, abra la aplicación y agregue algunos elementos a su lista de **todo**. + + ![Items added to todo list](items-added.png){: style="width: 55%; " } + {: .text-center } + +1. Retire el contenedor de la aplicación **todo**. Utilice el Dashboard o `docker ps` para obtener el ID y luego + `docker rm -f <id>` para eliminarlo. + +1. Inicie un nuevo contenedor usando el mismo comando de arriba. + +1. Abra la aplicación. ¡Debería ver sus artículos todavía en su lista! + +1. Continúe y retire el contenedor cuando haya terminado de revisar su lista. + +¡Hurra! ¡Ahora ha aprendido a conservar los datos! + +!!! info "Pro-tip" + Mientras que los named volumes y los bind mounts (de los que hablaremos en un minuto) son los dos + tipos principales de volúmenes admitidos por una instalación predeterminada del motor Docker, ¡hay + muchos complementos de controladores de volumen disponibles para admitir NFS, SFTP, NetApp y más! + Esto será especialmente importante una vez que comience a ejecutar contenedores en varios hosts en + un entorno agrupado con Swarm, Kubernetes, etc. + +## Sumergiéndonos en nuestro volumen + +Mucha gente pregunta con frecuencia "¿Dónde está Docker _realmente_ almacenando mis datos cuando utilizo +un named volume?" Si quieres saberlo, puedes usar el comando `docker volume inspect`. + +```bash +docker volume inspect todo-db +[ + { + "CreatedAt": "2019-09-26T02:18:36Z", + "Driver": "local", + "Labels": {}, + "Mountpoint": "/var/lib/docker/volumes/todo-db/_data", + "Name": "todo-db", + "Options": {}, + "Scope": "local" + } +] +``` + +El `Mountpoint` es la ubicación real en el disco donde se almacenan los datos. Tenga en cuenta que en la mayoría +de las máquinas, deberá tener acceso de root para acceder a este directorio desde el host. ¡Pero ahí es donde está! + +!!! info "Acceder a los datos de volumen directamente en Docker Desktop" + Mientras se ejecuta en Docker Desktop, los comandos de Docker se ejecutan realmente dentro de una pequeña máquina + virtual en su máquina. Si desea ver el contenido real del directorio Mountpoint, primero debe ingresar a la VM. + +## Resumen + +En este punto, tenemos una aplicación en funcionamiento que puede sobrevivir a los reinicios. +¡Podemos mostrárselo a nuestros inversores y esperamos que puedan captar nuestra visión! + +Sin embargo, vimos anteriormente que la reconstrucción de imágenes para cada cambio lleva bastante tiempo. +Tiene que haber una mejor manera de hacer cambios, ¿verdad? Con bind mounts (que insinuamos anteriormente), +¡hay una mejor manera! ¡Echemos un vistazo a eso ahora! diff --git a/docs/tutorial/sharing-our-app/index.es.md b/docs/tutorial/sharing-our-app/index.es.md new file mode 100644 index 0000000..ccd6896 --- /dev/null +++ b/docs/tutorial/sharing-our-app/index.es.md @@ -0,0 +1,93 @@ + +Ahora que hemos creado una imagen, ¡compartámosla! Para compartir imágenes de Docker, +debe utilizar un Docker registry. El registry predeterminado es Docker Hub y es de +donde provienen todas las imágenes que hemos utilizado. + +## Crear un repositorio + +Para enviar una imagen, primero debemos crear un repositorio en Docker Hub. + +1. Vaya a [Docker Hub](https://hub.docker.com) e inicie sesión si es necesario. + +1. Haga clic en el botón **Create Repository**. + +1. Para el nombre del repositorio, use `getting-started`. Asegúrese de que la visibilidad sea `Public`. + +1. ¡Haga clic en el botón **Create**! + +Si miras en el lado derecho de la página, verás una sección llamada **Docker commands**. Esto proporciona +un comando de ejemplo que deberá ejecutar para enviarlo a este repositorio. + +![Docker command with push example](push-command.png){: style=width:75% } +{: .text-center } + +## Empujando nuestra imagen + +1. En la línea de comandos, intente ejecutar el comando push que ve en Docker Hub. Tenga en cuenta que su comando + utilizará su namespace, no "docker". + + ```plaintext + $ docker push docker/getting-started + The push refers to repository [docker.io/docker/getting-started] + An image does not exist locally with the tag: docker/getting-started + ``` + + ¿Por qué falló? El comando push buscaba una imagen llamada docker/getting-started, pero + no encontró una. Si ejecuta `docker image ls`, tampoco verá ninguna. + + Para solucionar este problema, necesitamos "etiquetar" nuestra imagen existente que hemos + creado para darle otro nombre. + +1. Inicie sesión en Docker Hub usando el comando `docker login -u YOUR-USER-NAME`. + +1. Use el comando `docker tag` para darle un nuevo nombre a la imagen de `getting-started`. Asegúrese de cambiar + `YOUR-USER-NAME` por su ID de Docker. + + ```bash + docker tag getting-started YOUR-USER-NAME/getting-started + ``` + +1. Ahora intente su comando push de nuevo. Si está copiando el valor de Docker Hub, puede eliminar la parte + `tagname`, ya que no agregamos una etiqueta al nombre de la imagen. Si no especifica una etiqueta, Docker + usará una etiqueta llamada `latest`. + + ```bash + docker push YOUR-USER-NAME/getting-started + ``` + +## Ejecutando nuestra imagen en una nueva instancia + +Ahora que nuestra imagen se ha creado y enviado a un registry, ¡intentemos ejecutar nuestra aplicación en +una instancia nueva que nunca ha visto esta imagen de contenedor! Para hacer esto, usaremos Play with Docker. + +1. Abra su navegador en [Play with Docker](http://play-with-docker.com). + +1. Inicie sesión con su cuenta de Docker Hub. + +1. Una vez que haya iniciado sesión, haga clic en el enlace "+ ADD NEW INSTANCE" en la barra lateral izquierda. (Si no lo ve, amplíe un poco su navegador). Después de unos segundos, se abrirá una ventana de terminal en su navegador. + + ![Play with Docker add new instance](pwd-add-new-instance.png){: style=width:75% } +{: .text-center } + + +1. En la terminal, inicie su aplicación recién lanzada. + + ```bash + docker run -dp 3000:3000 YOUR-USER-NAME/getting-started + ``` + + ¡Debería ver que la imagen se baja y finalmente se inicia! + +1. Haga clic en la insignia 3000 cuando aparezca y debería ver la aplicación con sus modificaciones. ¡Hurra! + Si la insignia 3000 no aparece, puede hacer clic en el botón "Open Port" y escribir 3000. + +## Resumen + +En esta sección, aprendimos cómo compartir nuestras imágenes empujándolas a un registry. Luego fuimos a una +nueva instancia y pudimos ejecutar la imagen recién enviada. Esto es bastante común en las CI pipelines, donde +el pipeline creará la imagen y la enviará a un registry y luego el entorno de producción puede usar la última +versión de la imagen. + +Ahora que lo hemos resuelto, volvamos a lo que notamos al final de la última sección. Como recordatorio, notamos +que cuando reiniciamos la aplicación, perdimos todos los elementos de nuestra lista de tareas pendientes. Obviamente, +esa no es una gran experiencia de usuario, ¡así que aprendamos cómo podemos conservar los datos durante los reinicios! \ No newline at end of file diff --git a/docs/tutorial/updating-our-app/index.es.md b/docs/tutorial/updating-our-app/index.es.md new file mode 100644 index 0000000..e32dc56 --- /dev/null +++ b/docs/tutorial/updating-our-app/index.es.md @@ -0,0 +1,114 @@ + +Como una pequeña solicitud de función, el equipo de producto nos ha pedido que cambiemos +el "texto vacío" cuando no tenemos ningún elemento de la lista de tareas pendientes. Les +gustaría hacer la transición a lo siguiente: + +> You have no todo items yet! Add one above! + +Bastante simple, ¿verdad? Hagamos el cambio. + +## Actualización de nuestro código fuente + +1. En el archivo `src/static/js/app.js`, actualice la línea 56 para usar el nuevo texto vacío. + + ```diff + - <p className="text-center">No items yet! Add one above!</p> + + <p className="text-center">You have no todo items yet! Add one above!</p> + ``` + +1. Construyamos nuestra versión actualizada de la imagen, usando el mismo comando que usamos antes. + + ```bash + docker build -t getting-started . + ``` + +1. Comencemos un nuevo contenedor usando el código actualizado. + + ```bash + docker run -dp 3000:3000 getting-started + ``` + +**Uh oh!** Probablemente vio un error como este (las ID serán diferentes): + +```bash +docker: Error response from daemon: driver failed programming external connectivity on endpoint laughing_burnell +(bb242b2ca4d67eba76e79474fb36bb5125708ebdabd7f45c8eaf16caaabde9dd): Bind for 0.0.0.0:3000 failed: port is already allocated. +``` + +¿Entonces qué pasó? No podemos iniciar el nuevo contenedor porque nuestro contenedor anterior todavía se está +ejecutando. La razón por la que esto es un problema es porque ese contenedor está usando el puerto 3000 del +host y solo un proceso en la máquina (contenedores incluidos) puede escuchar un puerto específico. Para solucionar +este problema, debemos quitar el contenedor antiguo. + + +## Reemplazo de nuestro contenedor viejo + +Para quitar un contenedor, primero debe detenerse. Una vez que se haya detenido, se puede quitar. Tenemos dos +formas de quitar el contenedor viejo. Siéntase libre de elegir el camino con el que se sienta más cómodo. + + +### Eliminar un contenedor usando la CLI + +1. Obtenga el ID del contenedor usando el comando `docker ps`. + + ```bash + docker ps + ``` + +1. Utilice el comando `docker stop` para detener el contenedor. + + ```bash + # Cambie <the-container-id> con el ID de docker ps + docker stop <the-container-id> + ``` + +1. Una vez que el contenedor se ha detenido, puede eliminarlo utilizando el comando `docker rm`. + + ```bash + docker rm <the-container-id> + ``` + +!!! info "Pro tip" + Puede detener y eliminar un contenedor con un solo comando agregando el indicador "force" + al comando `docker rm`. Por ejemplo: `docker rm -f <the-container-id>` + +### Eliminar un contenedor con el panel de Docker + +Si abre el panel de Docker, puede eliminar un contenedor con dos clics. Sin duda, +es mucho más fácil que tener que buscar el ID del contenedor y eliminarlo. + +1. Con el tablero abierto, coloque el cursor sobre el contenedor de la aplicación y verá aparecer + una colección de botones de acción a la derecha. + +1. Haga clic en el icono de la papelera para eliminar el contenedor. + +1. Confirma la eliminación y listo. + +![Docker Dashboard - removing a container](dashboard-removing-container.png) + + +### Iniciando nuestro contenedor de aplicaciones actualizado + +1. Ahora, inicie su aplicación actualizada. + + ```bash + docker run -dp 3000:3000 getting-started + ``` + +1. Actualiza tu navegador a [http://localhost:3000](http://localhost:3000) ¡y debería ver su texto de ayuda actualizado! + +![Updated application with updated empty text](todo-list-updated-empty-text.png){: style="width:55%" } +{: .text-center } + + + +## Resumen + +Si bien pudimos crear una actualización, es posible que haya notado dos cosas: + +- ¡Todos los elementos existentes en nuestra lista de tareas pendientes se han ido! ¡Esa no es una muy buena aplicación! +Hablaremos de eso en breve. +- Hubo _muchos_ pasos involucrados para un cambio tan pequeño. En una próxima sección, hablaremos sobre cómo ver las +actualizaciones de código sin necesidad de reconstruir e iniciar un nuevo contenedor cada vez que hagamos un cambio. + +Antes de hablar de persistencia, veremos rápidamente cómo compartir estas imágenes con otros. diff --git a/docs/tutorial/using-bind-mounts/index.es.md b/docs/tutorial/using-bind-mounts/index.es.md new file mode 100644 index 0000000..d474be4 --- /dev/null +++ b/docs/tutorial/using-bind-mounts/index.es.md @@ -0,0 +1,115 @@ + +En el capítulo anterior, hablamos y usamos un **named volume** para conservar los datos en nuestra base de datos. +Los named volumes son excelentes si simplemente queremos almacenar datos, ya que no tenemos que preocuparnos por +_dónde_ se almacenan los datos. + +Con **bind mounts**, controlamos el punto de montaje exacto en el host. Podemos usar esto para conservar datos, +pero a menudo se usa para proporcionar datos adicionales en contenedores. Cuando trabajamos en una aplicación, +podemos usar un bind mount para montar nuestro código fuente en el contenedor para permitirle ver los cambios +de código, responder y ver los cambios de inmediato. + +Para las aplicaciones basadas en Node, [nodemon](https://npmjs.com/package/nodemon) es una gran herramienta para +observar los cambios de archivos y luego reiniciar la aplicación. Existen herramientas equivalentes en la mayoría +de los otros lenguajes y frameworks. + +## Comparaciones rápidas de tipos de volumen + +Los bind mounts y named volumes son los dos tipos principales de volúmenes que vienen con el motor de Docker. +Sin embargo, hay controladores de volumen adicionales disponibles para admitir otros casos de uso ([SFTP](https://github.com/vieux/docker-volume-sshfs), [Ceph](https://ceph.com/geen-categorie/getting-started-with-the-docker-rbd-volume-plugin/), [NetApp](https://netappdvp.readthedocs.io/en/stable/), [S3](https://github.com/elementar/docker-s3-volume) y más). + +| | Named Volumes | Bind Mounts | +| - | ------------- | ----------- | +| Ubicación del host | Docker elige | Tú controlas | +| Ejemplo de montaje (using `-v`) | my-volume:/usr/local/data | /path/to/data:/usr/local/data | +| Llena un nuevo volumen con el contenido del contenedor. | Si | No | +| Soporta controladores de volumen | Si | No | + + +## Iniciar un contenedor en modo de desarrollo + +Para ejecutar nuestro contenedor para admitir un flujo de trabajo de desarrollo, haremos lo siguiente: + +- Monte nuestro código fuente en el contenedor +- Instale todas las dependencias, incluidas las dependencias "dev" +- Inicie nodemon para observar los cambios en el sistema de archivos + +¡Hagamoslo! + +1. Asegúrese de no tener ningún contenedor de `getting-started` en ejecución. + +1. Ejecute el siguiente comando. Explicaremos lo que está pasando después: + + ```bash + docker run -dp 3000:3000 \ + -w /app -v "$(pwd):/app" \ + node:12-alpine \ + sh -c "yarn install && yarn run dev" + ``` + + Si está utilizando PowerShell, utilice este comando. + + ```powershell + docker run -dp 3000:3000 ` + -w /app -v "$(pwd):/app" ` + node:12-alpine ` + sh -c "yarn install && yarn run dev" + ``` + + - `-dp 3000:3000` - igual que antes. Ejecuta en modo independiente (en segundo plano) y crea un mapeo de puertos + - `-w /app` - establece el "directorio de trabajo" o el directorio actual desde el que se ejecutará el comando + - `-v "$(pwd):/app"` - enlaza la montura al directorio actual desde el host en el contenedor en el directorio `app` + - `node:12-alpine` - la imagen a usar. Tenga en cuenta que esta es la imagen base para nuestra aplicación del Dockerfile + - `sh -c "yarn install && yarn run dev"` - el comando. Estamos iniciando un shell usando `sh` (alpine no tiene` bash`) + y ejecutando `yarn install` para instalar _todas_ las dependencias y luego ejecutando `yarn run dev`. Si miramos + en el `package.json`, veremos que el script `dev` está iniciando `nodemon`. + +1. Puede ver los registros usando `docker logs -f <container-id>`. Sabrá que está listo para comenzar cuando vea esto ... + + ```bash + docker logs -f <container-id> + $ nodemon src/index.js + [nodemon] 1.19.2 + [nodemon] to restart at any time, enter `rs` + [nodemon] watching dir(s): *.* + [nodemon] starting `node src/index.js` + Using sqlite database at /etc/todos/todo.db + Listening on port 3000 + ``` + + Cuando haya terminado de ver los registros, salga presionando `Ctrl`+`C`. + +1. Ahora, hagamos un cambio en la aplicación. En el archivo `src/static/js/app.js`, cambiemos el botón "Add Item" para que + simplemente diga "Add". Este cambio estará en la línea 109. + + ```diff + - {submitting ? 'Adding...' : 'Add Item'} + + {submitting ? 'Adding...' : 'Add'} + ``` + +1. Simplemente actualice la página (o ábrala) y debería ver el cambio reflejado en el navegador casi de inmediato. Es + posible que el servidor Node demore unos segundos en reiniciarse, por lo que si obtiene un error, intente actualizar + después de unos segundos. + + ![Screenshot of updated label for Add button](updated-add-button.png){: style="width:75%;"} + {: .text-center } + +1. No dude en realizar cualquier otro cambio que desee. Cuando haya terminado, detenga el contenedor y cree su nueva + imagen usando `docker build -t Getting-started .`. + + +El uso de bind mounts es _muy_ común para las configuraciones de desarrollo local. La ventaja es que la máquina de +desarrollo no necesita tener instaladas todas las herramientas y entornos de compilación. Con un solo comando +"docker run", el entorno de desarrollo se extrae y está listo para funcionar. Hablaremos de Docker Compose en un paso +futuro, ya que esto ayudará a simplificar nuestros comandos (ya tenemos muchas banderas en nuestros comandos). + +## Resumen + +En este punto, podemos conservar nuestra base de datos y responder rápidamente a las necesidades y demandas de nuestros +inversores y fundadores. ¡Hurra! ¿Pero adivina que? ¡Recibimos buenas noticias! + +**¡Su proyecto ha sido seleccionado para un futuro desarrollo!** + +Para prepararnos para producción, necesitamos migrar nuestra base de datos de trabajar en SQLite a algo que pueda +escalar un poco mejor. Para simplificar, mantendremos una base de datos relacional y cambiaremos nuestra aplicación +para usar MySQL. Pero, ¿cómo deberíamos ejecutar MySQL? ¿Cómo permitimos que los contenedores se comuniquen entre sí? +¡Hablaremos de eso a continuación! diff --git a/docs/tutorial/using-docker-compose/index.es.md b/docs/tutorial/using-docker-compose/index.es.md new file mode 100644 index 0000000..c38a375 --- /dev/null +++ b/docs/tutorial/using-docker-compose/index.es.md @@ -0,0 +1,360 @@ + +[Docker Compose](https://docs.docker.com/compose/) es una herramienta que se desarrolló para ayudar a definir y compartir +aplicaciones de varios contenedores. Con Compose, podemos crear un archivo YAML para definir los servicios y con un solo +comando, podemos hacer girar todo o destruirlo todo. + +La gran ventaja de usar Compose es que puede definir su pila de aplicaciones en un archivo, mantenerlo en la raíz del +repositorio de su proyecto (ahora está controlado por la versión) y permitir fácilmente que otra persona contribuya a +su proyecto. Alguien solo necesitaría clonar su repositorio e iniciar la aplicación de redacción. De hecho, es posible +que veas bastantes proyectos en GitHubGitLab haciendo exactamente esto ahora. + +Entonces, ¿cómo empezamos? + +## Instalación de Docker Compose + +Si instaló Docker DesktopToolbox para Windows o Mac, ¡ya tiene Docker Compose! Las instancias de Play-with-Docker +también tienen Docker Compose instalado. Si está en una máquina Linux, deberá instalar Docker Compose usando +[las instrucciones aquí](https://docs.docker.com/compose/install/). + +Después de la instalación, debería poder ejecutar lo siguiente y ver la información de la versión. + +```bash +docker-compose version +``` + + +## Creando nuestro Compose File + +1. En la raíz del proyecto de la aplicación, cree un archivo llamado `docker-compose.yml`. + +1. En el archivo de redacción, comenzaremos definiendo la versión del esquema. En la mayoría de los casos, es mejor + utilizar la última versión compatible. Puede consultar la + [Referencia a Compose file](https://docs.docker.com/compose/compose-file/) para ver las versiones actuales + del esquema y la matriz de compatibilidad. + + ```yaml + version: "3.7" + ``` + +1. A continuación, definiremos la lista de servicios (o contenedores) que queremos ejecutar como parte de nuestra aplicación. + + ```yaml hl_lines="3" + version: "3.7" + + services: + ``` + +Y ahora, comenzaremos a migrar un servicio a la vez en el compose file. + + +## Definición de la App Service + +Para recordar, este era el comando que estábamos usando para definir nuestro contenedor de aplicaciones. + +```bash +docker run -dp 3000:3000 \ + -w /app -v "$(pwd):/app" \ + --network todo-app \ + -e MYSQL_HOST=mysql \ + -e MYSQL_USER=root \ + -e MYSQL_PASSWORD=secret \ + -e MYSQL_DB=todos \ + node:12-alpine \ + sh -c "yarn install && yarn run dev" +``` + +Si está utilizando PowerShell, utilice este comando. + +```powershell +docker run -dp 3000:3000 ` + -w /app -v "$(pwd):/app" ` + --network todo-app ` + -e MYSQL_HOST=mysql ` + -e MYSQL_USER=root ` + -e MYSQL_PASSWORD=secret ` + -e MYSQL_DB=todos ` + node:12-alpine ` + sh -c "yarn install && yarn run dev" +``` + +1. Primero, definamos la entrada de servicio y la imagen del contenedor. Podemos elegir cualquier nombre para el servicio. + El nombre se convertirá automáticamente en un alias de red, que será útil a la hora de definir nuestro servicio MySQL. + + ```yaml hl_lines="4 5" + version: "3.7" + + services: + app: + image: node:12-alpine + ``` + +1. Normalmente, verá el comando cerca de la definición de la `image`, aunque no hay ningún requisito para realizar el pedido. + Entonces, sigamos adelante y traslademos eso a nuestro archivo. + + ```yaml hl_lines="6" + version: "3.7" + + services: + app: + image: node:12-alpine + command: sh -c "yarn install && yarn run dev" + ``` + + +1. Migremos la parte `-p 3000: 3000` del comando definiendo los `ports` para el servicio. + Usaremos la [sintaxis corta](https://docs.docker.com/compose/compose-file/#short-syntax-1) aquí, pero también hay una + [sintaxis larga](https://docs.docker.com/compose/compose-file/#long-syntax-1) más detallada disponible también. + + ```yaml hl_lines="7 8" + version: "3.7" + + services: + app: + image: node:12-alpine + command: sh -c "yarn install && yarn run dev" + ports: + - 3000:3000 + ``` + +1. A continuación, migraremos tanto el directorio de trabajo (`-w app`) como el mapeo de volumen (`-v "$(pwd):/app"`) + usando las definiciones `working_dir` y `volumes`. Los volúmenes también tienen una sintaxis [corta](https://docs.docker.com/compose/compose-file/#short-syntax-3) y [larga](https://docs.docker.com/compose/compose-file/#long-syntax-3). + + Una ventaja de las definiciones de volumen de Docker Compose es que podemos usar rutas relativas del directorio actual. + + ```yaml hl_lines="9 10 11" + version: "3.7" + + services: + app: + image: node:12-alpine + command: sh -c "yarn install && yarn run dev" + ports: + - 3000:3000 + working_dir: /app + volumes: + - ./:/app + ``` + +1. Finalmente, necesitamos migrar las definiciones de variables de entorno usando la clave `environment`. + + ```yaml hl_lines="12 13 14 15 16" + version: "3.7" + + services: + app: + image: node:12-alpine + command: sh -c "yarn install && yarn run dev" + ports: + - 3000:3000 + working_dir: /app + volumes: + - ./:/app + environment: + MYSQL_HOST: mysql + MYSQL_USER: root + MYSQL_PASSWORD: secret + MYSQL_DB: todos + ``` + + +### Definición del servicio MySQL + +Ahora es el momento de definir el servicio MySQL. El comando que usamos para ese contenedor fue el siguiente: + +```bash +docker run -d \ + --network todo-app --network-alias mysql \ + -v todo-mysql-data:/var/lib/mysql \ + -e MYSQL_ROOT_PASSWORD=secret \ + -e MYSQL_DATABASE=todos \ + mysql:5.7 +``` + +Si está utilizando PowerShell, utilice este comando. + +```powershell +docker run -d ` + --network todo-app --network-alias mysql ` + -v todo-mysql-data:/var/lib/mysql ` + -e MYSQL_ROOT_PASSWORD=secret ` + -e MYSQL_DATABASE=todos ` + mysql:5.7 +``` + +1. Primero definiremos el nuevo servicio y lo llamaremos `mysql` para que automáticamente obtenga el alias de la red. + Continuaremos y especificaremos la imagen a usar también. + + ```yaml hl_lines="6 7" + version: "3.7" + + services: + app: + # The app service definition + mysql: + image: mysql:5.7 + ``` + +1. A continuación, definiremos el mapeo de volumen. Cuando ejecutamos el contenedor con `docker run`, el named volume + se creó automáticamente. Sin embargo, eso no sucede cuando se ejecuta con Compose. Necesitamos definir el volumen + en la sección `volúmenes:` de nivel superior y luego especificar el punto de montaje en la configuración del servicio. + Simplemente proporcionando solo el nombre del volumen, se utilizan las opciones predeterminadas. + Sin embargo, hay [muchas más opciones disponibles](https://docs.docker.com/compose/compose-file/#volume-configuration-reference). + + ```yaml hl_lines="8 9 10 11 12" + version: "3.7" + + services: + app: + # The app service definition + mysql: + image: mysql:5.7 + volumes: + - todo-mysql-data:/var/lib/mysql + + volumes: + todo-mysql-data: + ``` + +1. Finalmente, solo necesitamos especificar las variables de entorno. + + ```yaml hl_lines="10 11 12" + version: "3.7" + + services: + app: + # The app service definition + mysql: + image: mysql:5.7 + volumes: + - todo-mysql-data:/var/lib/mysql + environment: + MYSQL_ROOT_PASSWORD: secret + MYSQL_DATABASE: todos + + volumes: + todo-mysql-data: + ``` + +En este punto, nuestro `docker-compose.yml` completo debería verse así: + + +```yaml +version: "3.7" + +services: + app: + image: node:12-alpine + command: sh -c "yarn install && yarn run dev" + ports: + - 3000:3000 + working_dir: /app + volumes: + - ./:/app + environment: + MYSQL_HOST: mysql + MYSQL_USER: root + MYSQL_PASSWORD: secret + MYSQL_DB: todos + + mysql: + image: mysql:5.7 + volumes: + - todo-mysql-data:/var/lib/mysql + environment: + MYSQL_ROOT_PASSWORD: secret + MYSQL_DATABASE: todos + +volumes: + todo-mysql-data: +``` + + +## Ejecutando nuestra pila de aplicaciones + +Ahora que tenemos nuestro archivo `docker-compose.yml`, ¡podemos iniciarlo! + +1. Asegúrese de que no se estén ejecutando primero otras copias de la appdb (`docker ps` y `docker rm -f <ids>`). + +1. Inicie la pila de aplicaciones usando el comando `docker-compose up`. Agregaremos la bandera `-d` para ejecutar todo en segundo plano. + + ```bash + docker-compose up -d + ``` + + Cuando ejecutamos esto, deberíamos ver un resultado como este: + + ```plaintext + Creating network "app_default" with the default driver + Creating volume "app_todo-mysql-data" with default driver + Creating app_app_1 ... done + Creating app_mysql_1 ... done + ``` + + ¡Notará que el volumen fue creado además de una red! De forma predeterminada, Docker Compose crea automáticamente + una red específicamente para el stack de aplicaciones (razón por la cual no definimos una en el archivo de composición). + +1. Veamos los logs usando el comando `docker-compose logs -f`. Verá los logs de cada uno de los servicios intercalados + en un solo flujo. Esto es increíblemente útil cuando desea estar atento a problemas relacionados con el tiempo. La + bandera `-f` "sigue" el log, por lo que le dará una salida en vivo a medida que se genere. + + Si aún no lo ha hecho, verá un resultado que se ve así ... + + ```plaintext + mysql_1 | 2019-10-03T03:07:16.083639Z 0 [Note] mysqld: ready for connections. + mysql_1 | Version: '5.7.27' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL) + app_1 | Connected to mysql db at host mysql + app_1 | Listening on port 3000 + ``` + + El nombre del servicio se muestra al principio de la línea (a menudo de color) para ayudar a distinguir los mensajes. + Si desea ver los registros de un servicio específico, puede agregar el nombre del servicio al final del comando de logs + (por ejemplo, `docker-compose logs -f app`). + + !!! info "Consejo profesional: esperar la base de datos antes de iniciar la aplicación" + Cuando la aplicación se inicia, en realidad se sienta y espera a que MySQL esté listo y listo antes de intentar + conectarse. Docker no tiene ningún soporte integrado para esperar a que otro contenedor esté completamente en + funcionamiento, en ejecución y listo antes de iniciar otro contenedor. Para proyectos Node-based, puede usar la + dependencia [wait-port](https://github.com/dwmkerr/wait-port). Existen proyectos similares para marcos de otros + lenguajes. + +1. En este punto, debería poder abrir su aplicación y verla en ejecución. ¡Y oye! ¡Estamos en un solo comando! + +## Ver nuestro stack de aplicaciones en el Dashboard Docker + +Si miramos el panel de Docker, veremos que hay un grupo llamado **app**. Este es el "nombre del proyecto" de Docker Compose +y se utiliza para agrupar los contenedores. Por defecto, el nombre del proyecto es simplemente el nombre del directorio +en el que se encontraba el `docker-compose.yml`. + +![Dashboard Docker con proyecto app](dashboard-app-project-collapsed.png) + +Si gira la aplicación, verá los dos contenedores que definimos en el compose file. Los nombres también son un poco más +descriptivos., ya que siguen el patrón de `<project-name>_<service-name>_<replica-number>`. Por lo tanto, es muy fácil +ver rápidamente qué contenedor es nuestra aplicación y qué contenedor es la base de datos mysql. + +![Dashboard Docker con proyecto app ampliado](dashboard-app-project-expanded.png) + + +## Derribarlo todo + +Cuando esté listo para derribarlo todo, simplemente ejecute `docker-compose down` o presione la papelera en el Docker +Dashboard para toda la aplicación. Los contenedores se detendrán y la red se eliminará. + +!!! warning "Removing Volumes" + De forma predeterminada, los named volumes en su compose file NO se eliminan cuando se ejecuta `docker-compose down`. + Si desea eliminar los volúmenes, deberá agregar la marca `--volumes`. + + El panel de Docker _no_ elimina volúmenes cuando elimina el stack de aplicaciones. + +Una vez derribado, puede cambiar a otro proyecto, ejecutar `docker-compose up` y estar listo para contribuir a ese proyecto. +¡Realmente no hay nada más simple que eso! + + +## Resumen + +En esta sección, aprendimos sobre Docker Compose y cómo nos ayuda a simplificar drásticamente la definición y +el uso compartido de aplicaciones multiservicio. Creamos un archivo de redacción traduciendo los comandos que +estábamos usando al formato de redacción apropiado. + +En este punto, comenzamos a concluir el tutorial. Sin embargo, existen algunas mejores prácticas sobre +la creación de imágenes que queremos cubrir, ya que hay un gran problema con el Dockerfile que hemos +estado usando. Entonces, ¡echemos un vistazo! diff --git a/docs/tutorial/what-next/index.es.md b/docs/tutorial/what-next/index.es.md new file mode 100644 index 0000000..addd472 --- /dev/null +++ b/docs/tutorial/what-next/index.es.md @@ -0,0 +1,26 @@ + +Aunque hemos terminado con nuestro taller, ¡todavía hay MUCHO más que aprender sobre los contenedores! +No vamos a profundizar aquí, ¡pero aquí hay algunas otras áreas para ver a continuación! + +## Orquestación de contenedores + +Running containers in production is tough. No desea iniciar sesión en una máquina y simplemente ejecutar un `docker run` +o un` docker-compose up`. ¿Por qué no? Bueno, ¿qué pasa si los contenedores mueren? ¿Cómo escalas en varias máquinas? La +orquestación de contenedores resuelve este problema. Herramientas como Kubernetes, Swarm, Nomad y ECS ayudan a resolver +este problema, todas de formas ligeramente diferentes. + +La idea general es que tienes "managers" que reciben el **estado esperado**. Este estado podría ser "Quiero ejecutar dos +instancias de mi aplicación web y exponer el puerto 80". Luego, los managers examinan todas las máquinas del clúster y +delegan el trabajo a los nodos "worker". Los managers observan los cambios (como la salida de un contenedor) y luego +trabajan para que el **actual state** refleje el estado esperado. + + +## Proyectos de la Cloud Native Computing Foundation + +El CNCF es un hogar vendor-neutral para varios proyectos de código abierto, incluidos Kubernetes, Prometheus, Envoy, +Linkerd, NATS y más! Puede ver los [proyectos graduados e incubados aquí](https://www.cncf.io/projects/) y el +[Landscape CNCF aquí](https://landscape.cncf.io/). ¡Hay MUCHOS proyectos para ayudar a resolver problemas relacionados +con el monitoreo, logging, la seguridad, image registries, la mensajería y más! + +Por lo tanto, si es nuevo en el landscape de contenedores y el desarrollo de aplicaciones cloud-native, ¡bienvenido! +¡Conéctese con la comunidad, haga preguntas y siga aprendiendo! ¡Estamos emocionados de tenerte! diff --git a/mkdocs.yml b/mkdocs.yml index d0a0d14..62b2764 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -33,11 +33,20 @@ plugins: - search - minify: minify_html: true + - redirects: + redirect_maps: + 'index.md': 'tutorial/index.md' + - i18n: + languages: + es: "Español" + en: "English" + default_language: 'en' + translate_nav: # Customization extra: social: - - type: github-alt + - icon: fontawesome/brands/github-alt link: https://github.com/docker/getting-started # Extensions @@ -69,6 +78,7 @@ markdown_extensions: # Page tree nav: +# English - Getting Started: tutorial/index.md - Our Application: tutorial/our-application/index.md - Updating our App: tutorial/updating-our-app/index.md @@ -79,3 +89,14 @@ nav: - Using Docker Compose: tutorial/using-docker-compose/index.md - Image Building Best Practices: tutorial/image-building-best-practices/index.md - What Next?: tutorial/what-next/index.md +# Spanish + - Empezando: tutorial/index.es.md + - Nuestra Aplicación: tutorial/our-application/index.es.md + - Actualizando nuestra Aplicación: tutorial/updating-our-app/index.es.md + - Compartiendo nuestra Aplicación: tutorial/sharing-our-app/index.es.md + - Persistiendo nuestra DB: tutorial/persisting-our-data/index.es.md + - Uso de Bind Mounts: tutorial/using-bind-mounts/index.es.md + - Aplicaciones Multi-Container: tutorial/multi-container-apps/index.es.md + - Uso de Docker Compose: tutorial/using-docker-compose/index.es.md + - Mejores Prácticas para Creación de Imágen: tutorial/image-building-best-practices/index.es.md + - ¿Qué sigue?: tutorial/what-next/index.es.md diff --git a/requirements.txt b/requirements.txt index 848bb8d..10cbd83 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,8 @@ -mkdocs==1.0.4 -mkdocs-material==4.6.3 +mkdocs==1.1.2 +mkdocs-material==7.1.0 mkdocs-minify-plugin==0.2.3 pygments==2.7.4 pymdown-extensions==7.0 +mkdocs-translations==0.1.1 +mkdocs-redirects==1.0.1 +mkdocs-i18n==0.3.0