Curso de Docker

Docker nos permite definir dentro de una máquina todos los parámetros del entorno que utilizaremos para desplegar nuestra aplicación (versión del servidor Java, versión de Node…)

Docker crea un entorno que utiliza el sistema operativo de la máquina donde es utilizado, en lugar de crear una máquina virtual utilizando un sistema operativo independiente (usando Virtual Box, por ejemplo).

Por un lado, esto nos simplificará la configuración del entorno de desarrollo dentro de nuestro ordenador y nos facilitará la migración de nuestro entorno a otros sistemas como AWS, etc.

Pasos para la ejecución

Paso 1. Instalación de Docker Desktop.

1. Instalamos la versión Desktop de Docker.

Para un entorno de node:

3. Creamos en la raíz de nuestro proyecto un fichero llamado Dockerfile con el siguiente código:

# Instalar NodeJS en el contenedor de Docker
# El comando FROM instala una imagen oficial
FROM node:16.16.0-alpine

# Establecer la carpeta de trabajo dentro del ecosistema de Docker
WORKDIR /app

# Copiar todo el código de nuestra aplicación en el contenedor de Docker
COPY . ./

RUN npm i

EXPOSE 3000

# start app
CMD ["npm", "start"]

Para un entorno de springboot:

Necesitamos tener maven instalado en el sistema operativo. Lo descargamos y añadimos la carpeta de binarios al path de las variables de entorno de nuestro sistema operativo.

Debemos asegurarnos de que la siguiente línea forma parte de nuestro pom.xml:

<packaging>war</packaging>

El siguiente comando creará un empaquetado war dentro de la carpeta target.

mvn clean package -DskipTests

Podemos comprobar que el empaquetado se ha generado correctamente ejecutándolo:

java -jar target/[nombreEmpaquetado].war

Creamos en la raíz de nuestro proyecto un fichero llamado Dockerfile con el siguiente código:

FROM openjdk:8-alpine
ADD target/prueba-0.0.1-SNAPSHOT.war /usr/share/app.war
ENTRYPOINT ["/usr/bin/java", "-jar", "/usr/share/app.war"]

Paso 3. Build de la imagen

4. Creamos la imagen ejecutando el siguiente comando en la raíz de la carpeta de nuestro proyecto:

docker build . -t=nombre-proyecto-docker

5. Ejecutamos nuestra imagen. Para ello, crearemos un contenendor:

docker run -p 3010:3000 --name nombre-contenedor nombre-proyecto-docker

Al crear la imagen hemos especificado:

  • el puerto de nuestro ordenador (3010) asociado al puerto interno del contenedor (3000). Para un proyecto de springboot pondríamos 8080:8080.
  • El nombre del contenedor.
  • El nombre de la imagen que queremos ejecutar.

Si quisieramos realizar estos pasos utilizando Docker Desktop, hubieramos pulsado aquí:

Curso de Docker 1
Curso de Docker 2

6. Para probar la aplicación iremos a localhost: 3010.

Curso de Docker 3

Docker Compose

Nos va a permitir, con un solo comando del package.json:

  1. Crear una imagen a partir de nuestro proyecto.
  2. Configurar dicha imagen
  3. Ejecutarla en un contenedor.

Debemos crear el siguiente fichero en nuestro proyecto. Lo he creado en la raíz, pero podría estar en otro sitio.

docker-compose.yml

version: "3.8" # Versión de sintaxis del propio fichero docker-compose.yml

services:
  app-dev:
    container_name: container-name
    build:
      context: . # Ruta en la que se encuentra el Dockerfile
    ports:
      - 3002:3000

Añadimos el siguiente comando a los scripts del package.json:

"dev": "docker-compose up --build app-dev",

Volúmenes

Nos permiten vincular un directorio externo al contenedor con uno interno.

app-dev:
    container_name: container-name
    build:
      context: . # Ruta en la que se encuentra el Dockerfile
    volumes:
      - ./src:/app/src
    ports:
      - 3002:3000

Configuración para producción

Dockerfile

FROM node:16.16.0-alpine as installer
WORKDIR /app
COPY . ./
RUN npm i

FROM installer as builder
RUN npm run build

FROM installer as development
EXPOSE 3000
CMD ["npm", "start"]


FROM nginx:1.21.0-alpine AS production
COPY --from=builder /app/build /usr/share/nginx/html
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

docker-compose.yml

version: "3.8" # Versión de sintaxis del propio fichero docker-compose.yml

services:
  app-dev:
    container_name: container-name
    build:
      context: . # Ruta en la que se encuentra el Dockerfile
      target: development
    volumes:
      - ./src:/app/src
    ports:
      - 3000:3000
  app-pro:
    container_name: container-name-pro
    build:
      context: . # Ruta en la que se encuentra el Dockerfile
      target: production
    ports:
      - 3001:80

package.json:

  "scripts": {
    "dev": "docker-compose up --build app-dev",
    "pro": "docker-compose up --build app-pro",
    "clean-docker": "docker-compose down && docker-compose down --volumes",
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },

Configuración para routing

Debemos subir una especie de .htacces en apache, pero para ngix.

ngix.cond

server {
  listen 80;

  location / {
    root /usr/share/nginx/html/;
    include /etc/nginx/mime.types;
    try_files $uri $uri/ /index.html;
  }
}

Dockerfile

...
FROM nginx:1.21.0-alpine AS production
COPY --from=builder /app/build /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]