Crear contenedores optimizados

En esta página se describe cómo crear imágenes de Docker más ligeras.

Crear contenedores optimizados

Cuando se contenedoriza una aplicación, se pueden incluir por error en la imagen del contenedor archivos que no son necesarios en el tiempo de ejecución, como las dependencias del tiempo de compilación y los archivos intermedios. Estos archivos innecesarios pueden aumentar el tamaño de la imagen del contenedor y, por lo tanto, añadir tiempo y costes adicionales a medida que la imagen se mueve entre tu registro de Docker y tu tiempo de ejecución del contenedor.

Para reducir el tamaño de la imagen de contenedor, separa la compilación de la aplicación, junto con las herramientas que se usan para compilarla, del ensamblaje del contenedor de tiempo de ejecución.

Cloud Build proporciona una serie de contenedores Docker con herramientas de desarrollo comunes, como Git, Docker y la CLI de Google Cloud. Usa estas herramientas para definir un archivo de configuración de compilación con un paso para compilar la aplicación y otro para ensamblar su entorno de ejecución final.

Por ejemplo, si estás creando una aplicación Java que requiere archivos como el código fuente, las bibliotecas de aplicaciones, los sistemas de compilación, las dependencias del sistema de compilación y el JDK, puedes tener un Dockerfile como el siguiente:

FROM java:8

COPY . workdir/

WORKDIR workdir

RUN GRADLE_USER_HOME=cache ./gradlew buildDeb -x test

RUN dpkg -i ./gate-web/build/distributions/*.deb

CMD ["/opt/gate/bin/gate"]

En el ejemplo anterior, Gradle, que se usa para compilar el paquete, descarga un gran número de bibliotecas para funcionar. Estas bibliotecas son esenciales para crear el paquete, pero no son necesarias en el tiempo de ejecución. Todas las dependencias del tiempo de ejecución se incluyen en el paquete.

Cada comando de Dockerfile crea una capa en el contenedor. Si se generan datos en esa capa y no se eliminan con el mismo comando, ese espacio no se podrá recuperar. En este caso, Gradle descarga cientos de megabytes de bibliotecas en el directorio cache para realizar la compilación, pero las bibliotecas no se eliminan.

Una forma más eficiente de realizar la compilación es usar Cloud Build para separar la compilación de la aplicación de la compilación de su capa de tiempo de ejecución.

En el siguiente ejemplo, se separa el paso para compilar la aplicación Java del paso para ensamblar el contenedor de tiempo de ejecución:

YAML

  1. Compila la aplicación: en cloudbuild.yaml, añade un paso para compilar la aplicación.

    El siguiente código añade un paso que compila la imagen java:8, que contiene el código Java.

    steps:
    
    - name: 'java:8'
      env: ['GRADLE_USER_HOME=cache']
      entrypoint: 'bash'
      args: ['-c', './gradlew gate-web:installDist -x test']
    
    
  2. Monta el contenedor del entorno de ejecución: en cloudbuild.yaml, añade un paso para montar el contenedor del entorno de ejecución.

    El siguiente código añade un paso llamado gcr.io/cloud-builders/docker que ensambla el contenedor de tiempo de ejecución. Define el contenedor de tiempo de ejecución en un archivo independiente llamado Dockerfile.slim.

    En el ejemplo se usa la capa base de Alpine Linux openjdk:8u111-jre-alpine, que es increíblemente ligera. Además, incluye JRE en lugar del JDK, que es más voluminoso y era necesario para compilar la aplicación.

    cloudbuild.yaml
    
    steps:
    - name: 'java:8'
      env: ['GRADLE_USER_HOME=cache']
      entrypoint: 'bash'
      args: ['-c',
             './gradlew gate-web:installDist -x test']
    
    - name: 'gcr.io/cloud-builders/docker'
      args: ['build',
             '-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA',
             '-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:latest',
             '-f', 'Dockerfile.slim',
             '.'
      ]
    
    
    Dockerfile.slim
    
    FROM openjdk:8-jre-alpine
    
    COPY ./gate-web/build/install/gate /opt/gate
    
    CMD ["/opt/gate/bin/gate"]
    
  3. Crea las imágenes Docker: en cloudbuild.yaml, añade un paso para crear las imágenes.
    steps:
    - name: 'java:8'
      env: ['GRADLE_USER_HOME=cache']
      entrypoint: 'bash'
      args: ['-c', './gradlew gate-web:installDist -x test']
    - name: 'gcr.io/cloud-builders/docker'
      args: ['build',
             '-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA',
             '-t', 'gcr.io/$PROJECT_ID/$REPO_NAME:latest',
             '-f', 'Dockerfile.slim', '.']
    images:
    - 'gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA'
    - 'gcr.io/$PROJECT_ID/$REPO_NAME:latest'
    

JSON

  1. Compila la aplicación: en cloudbuild.json, añade un paso para compilar la aplicación.

    El siguiente código añade un paso llamado java:8 para crear el código Java.

    {
        "steps": [
        {
            "name": "java:8",
            "env": [
                "GRADLE_USER_HOME=cache"
            ],
            "entrypoint": "bash",
            "args": [
                "-c",
                "./gradlew gate-web:installDist -x test"
            ]
        },
    }
    
  2. Monta el contenedor de tiempo de ejecución: en cloudbuild.json, añade un paso para montar el contenedor de tiempo de ejecución.

    El siguiente código añade un paso llamado gcr.io/cloud-builders/docker que ensambla el contenedor de tiempo de ejecución. Define el contenedor de tiempo de ejecución en un archivo independiente llamado Dockerfile.slim.

    En el ejemplo se usa la capa base de Alpine Linux openjdk:8u111-jre-alpine, que es increíblemente ligera. Además, incluye JRE en lugar del JDK, que es más voluminoso y era necesario para compilar la aplicación.

    cloudbuild.json:
    
    {
        "steps": [
        {
            "name": "java:8",
            "env": [
                "GRADLE_USER_HOME=cache"
            ],
            "entrypoint": "bash",
            "args": [
                "-c",
                "./gradlew gate-web:installDist -x test"
            ]
        },
        {
            "name": "gcr.io/cloud-builders/docker",
            "args": [
                "build",
                "-t",
                "gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA",
                "-t",
                "gcr.io/$PROJECT_ID/$REPO_NAME:latest",
                "-f",
                "Dockerfile.slim",
                "."
            ]
        }
        ],
    }
    
    Dockerfile.slim:
    
    FROM openjdk:8u111-jre-alpine
    
    COPY ./gate-web/build/install/gate /opt/gate
    
    CMD ["/opt/gate/bin/gate"]
    
  3. Crea las imágenes Docker: en cloudbuild.json, añade un paso para crear las imágenes.
    {
        "steps": [
        {
            "name": "java:8",
            "env": [
                "GRADLE_USER_HOME=cache"
            ],
            "entrypoint": "bash",
            "args": [
                "-c",
                "./gradlew gate-web:installDist -x test"
            ]
        },
        {
            "name": "gcr.io/cloud-builders/docker",
            "args": [
                "build",
                "-t",
                "gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA",
                "-t",
                "gcr.io/$PROJECT_ID/$REPO_NAME:latest",
                "-f",
                "Dockerfile.slim",
                "."
            ]
        }
        ],
        "images": [
            "gcr.io/$PROJECT_ID/$REPO_NAME:$COMMIT_SHA",
            "gcr.io/$PROJECT_ID/$REPO_NAME:latest"
        ]
    }