Merge branch 'i18n'
This commit is contained in:
commit
6eca886f68
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1,3 @@
|
||||
node_modules
|
||||
|
||||
.idea/
|
||||
|
@ -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
|
||||
|
2
docs/index.es.md
Normal file
2
docs/index.es.md
Normal file
@ -0,0 +1,2 @@
|
||||
redirect: /tutorial/
|
||||
|
279
docs/tutorial/image-building-best-practices/index.es.md
Normal file
279
docs/tutorial/image-building-best-practices/index.es.md
Normal file
@ -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
|
||||
<missing> 13 days ago /bin/sh -c #(nop) ENTRYPOINT ["docker-entry… 0B
|
||||
<missing> 13 days ago /bin/sh -c #(nop) COPY file:238737301d473041… 116B
|
||||
<missing> 13 days ago /bin/sh -c apk add --no-cache --virtual .bui… 5.35MB
|
||||
<missing> 13 days ago /bin/sh -c #(nop) ENV YARN_VERSION=1.21.1 0B
|
||||
<missing> 13 days ago /bin/sh -c addgroup -g 1000 node && addu… 74.3MB
|
||||
<missing> 13 days ago /bin/sh -c #(nop) ENV NODE_VERSION=12.14.1 0B
|
||||
<missing> 13 days ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
|
||||
<missing> 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 `<title>` 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.
|
75
docs/tutorial/index.es.md
Normal file
75
docs/tutorial/index.es.md
Normal file
@ -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.
|
266
docs/tutorial/multi-container-apps/index.es.md
Normal file
266
docs/tutorial/multi-container-apps/index.es.md
Normal file
@ -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.
|
114
docs/tutorial/our-application/index.es.md
Normal file
114
docs/tutorial/our-application/index.es.md
Normal file
@ -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.
|
166
docs/tutorial/persisting-our-data/index.es.md
Normal file
166
docs/tutorial/persisting-our-data/index.es.md
Normal file
@ -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!
|
93
docs/tutorial/sharing-our-app/index.es.md
Normal file
93
docs/tutorial/sharing-our-app/index.es.md
Normal file
@ -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!
|
114
docs/tutorial/updating-our-app/index.es.md
Normal file
114
docs/tutorial/updating-our-app/index.es.md
Normal file
@ -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.
|
115
docs/tutorial/using-bind-mounts/index.es.md
Normal file
115
docs/tutorial/using-bind-mounts/index.es.md
Normal file
@ -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!
|
360
docs/tutorial/using-docker-compose/index.es.md
Normal file
360
docs/tutorial/using-docker-compose/index.es.md
Normal file
@ -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!
|
26
docs/tutorial/what-next/index.es.md
Normal file
26
docs/tutorial/what-next/index.es.md
Normal file
@ -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!
|
23
mkdocs.yml
23
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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user