Implementa una app en una imagen de contenedor en un clúster de GKE


Esta página te muestra cómo hacer lo siguiente:

  1. Crear una app de Hello World
  2. Empaquetar la app en una imagen de contenedor con Cloud Build
  3. Crear un clúster en Google Kubernetes Engine (GKE)
  4. Implementar la imagen de contenedor en tu clúster.

La muestra se indica en varios lenguajes, pero puedes usar otros además de los que se señalan.


Para seguir la guía paso a paso sobre esta tarea directamente en el editor de Cloud Shell, haz clic en Guiarme:

GUIARME


Antes de comenzar

  1. Accede a tu cuenta de Google Cloud. Si eres nuevo en Google Cloud, crea una cuenta para evaluar el rendimiento de nuestros productos en situaciones reales. Los clientes nuevos también obtienen $300 en créditos gratuitos para ejecutar, probar y, además, implementar cargas de trabajo.
  2. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.

    Ir al selector de proyectos

  3. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  4. Habilita las API de Artifact Registry, Cloud Build, and Google Kubernetes Engine.

    Habilita las API

  5. Instala Google Cloud CLI.
  6. Para inicializar la CLI de gcloud, ejecuta el siguiente comando:

    gcloud init
  7. En la página del selector de proyectos de la consola de Google Cloud, selecciona o crea un proyecto de Google Cloud.

    Ir al selector de proyectos

  8. Asegúrate de que la facturación esté habilitada para tu proyecto de Google Cloud.

  9. Habilita las API de Artifact Registry, Cloud Build, and Google Kubernetes Engine.

    Habilita las API

  10. Instala Google Cloud CLI.
  11. Para inicializar la CLI de gcloud, ejecuta el siguiente comando:

    gcloud init
  12. kubectl se usa para administrar Kubernetes, el sistema de organización de clúster que usa GKE. Puedes instalar kubectl con gcloud:
    gcloud components install kubectl

Escribe la app de muestra

Si quieres obtener instrucciones para crear una app de Hello World que se ejecute en GKE, haz clic en tu lenguaje:

Go

  1. Crea un directorio nuevo llamado helloworld-gke y usa el comando de cambio de directorio en él:

    mkdir helloworld-gke
    cd helloworld-gke
    
  2. Crea un módulo nuevo llamado example.com/helloworld:

    go mod init example.com/helloworld
    
  3. Crea un archivo nuevo llamado helloworld.go y pega el siguiente código en él:

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    )
    
    func main() {
    	http.HandleFunc("/", handler)
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    
    	log.Printf("Listening on localhost:%s", port)
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    
    func handler(w http.ResponseWriter, r *http.Request) {
    	log.Print("Hello world received a request.")
    	target := os.Getenv("TARGET")
    	if target == "" {
    		target = "World"
    	}
    	fmt.Fprintf(w, "Hello %s!\n", target)
    }
    

    Con este código, se crea un servidor web que recibe datos en el puerto definido por la variable de entorno PORT.

Tu app está lista para empaquetarse en un contenedor de Docker y, luego, subirse a Artifact Registry.

Node.js

  1. Crea un directorio nuevo llamado helloworld-gke y cambia a este directorio:

    mkdir helloworld-gke
    cd helloworld-gke
    
  2. Crea un archivo package.json con el siguiente contenido:

    {
      "name": "gke-helloworld",
      "version": "1.0.0",
      "description": "GKE hello world sample in Node",
      "main": "index.js",
      "scripts": {
        "start": "node index.js"
      },
      "author": "",
      "license": "Apache-2.0",
      "dependencies": {
        "express": "^4.16.4"
      }
    }
    
  3. En el mismo directorio, crea un archivo index.js y copia las líneas siguientes en este archivo:

    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
      console.log('Hello world received a request.');
    
      const target = process.env.TARGET || 'World';
      res.send(`Hello ${target}!`);
    });
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log('Hello world listening on port', port);
    });

    Con este código, se crea un servidor web que recibe datos en el puerto definido por la variable de entorno PORT.

Tu app está lista para empaquetarse en un contenedor de Docker y subirse a Artifact Registry.

Python

  1. Crea un directorio nuevo llamado helloworld-gke y cambia a este directorio:

    mkdir helloworld-gke
    cd helloworld-gke
    
  2. Crea un archivo llamado app.py y pega el siguiente código en este archivo:

    import os
    
    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def hello_world():
        target = os.environ.get('TARGET', 'World')
        return 'Hello {}!\n'.format(target)
    
    if __name__ == "__main__":
        app.run(debug=True,host='0.0.0.0',port=int(os.environ.get('PORT', 8080)))

Java

Crea una app de Spring Boot.

  1. Instala Java SE 8 o un JDK y cURL superior. Solo se requieren Java SE y cURL para crear el proyecto web nuevo en el paso siguiente. Dockerfile, que se describe más adelante, carga todas las dependencias en el contenedor.

  2. Desde tu terminal, crea un proyecto web vacío nuevo:

    curl https://start.spring.io/starter.zip \
        -d dependencies=web \
        -d javaVersion=1.8 \
        -d type=maven-project \
        -d bootVersion=2.6.6 \
        -d name=helloworld \
        -d artifactId=helloworld \
        -d baseDir=helloworld-gke \
        -o helloworld-gke.zip
    unzip helloworld-gke.zip
    cd helloworld-gke
    

    Ahora tienes un proyecto de Spring Boot nuevo en helloworld-gke.

  3. En el archivo src/main/java/com/example/helloworld/HelloworldApplication.java, actualiza la clase HelloworldApplication mediante un @RestController para controlar la asignación /.

    package com.example.helloworld;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    @SpringBootApplication
    public class HelloworldApplication {
    
    	@Value("${TARGET:World}")
    	String target;
    
    	@RestController
    	class HelloworldController {
    		@GetMapping("/")
    		String hello() {
    			return "Hello " + target + "!";
    		}
    	}
    
    	public static void main(String[] args) {
    		SpringApplication.run(HelloworldApplication.class, args);
    	}
    }

    Con este código, se crea un servidor web que recibe datos en el puerto definido por la variable de entorno PORT.

Tu app está lista para empaquetarse en un contenedor de Docker y, luego, subirse a Artifact Registry.

C#

  1. Instala el SDK de .NET. Solo se requiere el SDK de .NET para crear el proyecto web nuevo en el paso siguiente. Dockerfile, que se describe más adelante, carga todas las dependencias en el contenedor.

  2. Desde tu terminal, crea un proyecto web vacío nuevo:

    dotnet new web -o helloworld-gke
    
  3. Cambia el directorio a helloworld-gke.

    cd helloworld-gke
    
  4. Actualiza Program.cs para escuchar en el puerto 8080:

    var builder = WebApplication.CreateBuilder(args);
    
    // Google Cloud Run sets the PORT environment variable to tell this
    // process which port to listen to.
    var port = Environment.GetEnvironmentVariable("PORT") ?? "8080";
    var url = $"http://0.0.0.0:{port}";
    var target = Environment.GetEnvironmentVariable("TARGET") ?? "World";
    
    var app = builder.Build();
    
    app.MapGet("/", () => $"Hello {target}!");
    
    app.Run(url);

Tu app está lista para empaquetarse en un contenedor de Docker y, luego, subirse a Artifact Registry.

PHP

  1. Crea un directorio nuevo llamado helloworld-gke y cambia a este directorio:

    mkdir helloworld-gke
    cd helloworld-gke
    
  2. Crea un archivo llamado index.php y pega el siguiente código en este archivo:

    <?php
    $target = getenv('TARGET', true) ?: 'World';
    echo sprintf("Hello %s!", $target);
    ?>

Tu app está lista para empaquetarse en un contenedor de Docker y, luego, subirse a Artifact Registry.

Crea contenedores para la app con Cloud Build

  1. Si quieres crear contenedores para la app de muestra, crea un archivo nuevo llamado Dockerfile en el mismo directorio que los archivos de origen y copia el siguiente contenido:

    Go

    # Use the offical Go image to create a build artifact.
    # This is based on Debian and sets the GOPATH to /go.
    # https://hub.docker.com/_/golang
    FROM golang:1.21.0 as builder
    WORKDIR /app
    
    # Initialize a new Go module.
    RUN go mod init quickstart-go
    
    # Copy local code to the container image.
    COPY *.go ./
    
    # Build the command inside the container.
    RUN CGO_ENABLED=0 GOOS=linux go build -o /quickstart-go
    
    # Use a Docker multi-stage build to create a lean production image.
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM gcr.io/distroless/base-debian11
    
    # Change the working directory.
    WORKDIR /
    
    # Copy the binary to the production image from the builder stage.
    COPY --from=builder /quickstart-go /quickstart-go
    
    # Run the web service on container startup.
    USER nonroot:nonroot
    ENTRYPOINT ["/quickstart-go"]

    Node.js

    # Use the official lightweight Node.js 16 image.
    # https://hub.docker.com/_/node
    FROM node:17-slim
    
    # Create and change to the app directory.
    WORKDIR /usr/src/app
    
    # Copy application dependency manifests to the container image.
    # A wildcard is used to ensure both package.json AND package-lock.json are copied.
    # Copying this separately prevents re-running npm install on every code change.
    COPY package*.json ./
    
    # Install production dependencies.
    RUN npm install --omit=dev
    
    # Copy local code to the container image.
    COPY . ./
    
    # Run the web service on container startup.
    CMD [ "npm", "start" ]

    Agrega otro archivo .dockerignore para asegurarte de que los archivos locales no afecten el proceso de compilación del contenedor:

    Dockerfile
    README.md
    node_modules
    npm-debug.log
    

    Python

    # Use the official lightweight Python image.
    # https://hub.docker.com/_/python
    FROM python:3.7-slim
    
    # Copy local code to the container image.
    ENV APP_HOME /app
    WORKDIR $APP_HOME
    COPY . ./
    
    # Install production dependencies.
    RUN pip install Flask gunicorn
    
    # Run the web service on container startup. Here we use the gunicorn
    # webserver, with one worker process and 8 threads.
    # For environments with multiple CPU cores, increase the number of workers
    # to be equal to the cores available.
    CMD exec gunicorn --bind :$PORT --workers 1 --threads 8 app:app

    Agrega un archivo .dockerignore para asegurarte de que los archivos locales no afecten el proceso de compilación del contenedor:

    Dockerfile
    README.md
    *.pyc
    *.pyo
    *.pyd
    __pycache__
    

    Java

    # Use the official maven/Java 8 image to create a build artifact.
    # https://hub.docker.com/_/maven
    FROM maven:3.5-jdk-8-alpine as builder
    
    # Copy local code to the container image.
    WORKDIR /app
    COPY pom.xml ./
    COPY src ./src/
    
    # Build a release artifact.
    RUN mvn package -DskipTests
    
    # Use AdoptOpenJDK for base image.
    # It's important to use OpenJDK 8u191 or above that has container support enabled.
    # https://hub.docker.com/r/adoptopenjdk/openjdk8
    # https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
    FROM adoptopenjdk/openjdk8:jdk8u202-b08-alpine-slim
    
    # Copy the jar to the production image from the builder stage.
    COPY --from=builder /app/target/helloworld-*.jar /helloworld.jar
    
    # Run the web service on container startup.
    CMD ["java","-Djava.security.egd=file:/dev/./urandom","-Dserver.port=${PORT}","-jar","/helloworld.jar"]

    C#

    # Use Microsoft's official lightweight .NET images.
    FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
    WORKDIR /app
    
    # Install production dependencies.
    # Copy csproj and restore as distinct layers.
    COPY *.csproj ./
    RUN dotnet restore
    
    # Copy local code to the container image.
    COPY . ./
    
    # Build a release artifact.
    RUN dotnet publish -c Release -o out
    
    # Run the web service on container startup in a lean production image.
    FROM mcr.microsoft.com/dotnet/aspnet:6.0
    WORKDIR /app
    COPY --from=build /app/out .
    
    # Start the .dll (will have the same name as your .csproj file)
    ENTRYPOINT ["dotnet", "helloworld-gke.dll"]

    Agrega un archivo .dockerignore para asegurarte de que los archivos locales no afecten el proceso de compilación del contenedor:

    Dockerfile
    README.md
    **/obj/
    **/bin/
    

    PHP

    # Use the official PHP 7.4 image.
    # https://hub.docker.com/_/php
    FROM php:7.4-apache
    
    # Copy local code to the container image.
    COPY index.php /var/www/html/
    
    # Use port 8080 in Apache configuration files.
    RUN sed -i 's/80/${PORT}/g' /etc/apache2/sites-available/000-default.conf /etc/apache2/ports.conf
    
    # Configure PHP for development.
    # Switch to the production php.ini for production operations.
    # RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini"
    # https://hub.docker.com/_/php#configuration
    RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"

    Agrega un archivo .dockerignore para asegurarte de que los archivos locales no afecten el proceso de compilación del contenedor:

    Dockerfile
    README.md
    vendor
    
  2. Obtén tu ID del proyecto de Google Cloud:

    gcloud config get-value project
    
  3. En esta guía de inicio rápido, almacenarás tu contenedor en Artifact Registry y lo implementarás en el clúster desde el registro. Ejecuta el siguiente comando para crear un repositorio llamado hello-repo en la misma ubicación que tu clúster:

    gcloud artifacts repositories create hello-repo \
        --project=PROJECT_ID \
        --repository-format=docker \
        --location=us-central1 \
        --description="Docker repository"
    

    Reemplaza los siguientes valores:

    • PROJECT_ID es el ID del proyecto de Google Cloud.
  4. Compila tu imagen de contenedor con Cloud Build, que es similar a ejecutar docker build y docker push, pero la compilación se realiza en Google Cloud.

    gcloud builds submit \
      --tag us-central1-docker.pkg.dev/PROJECT_ID/hello-repo/helloworld-gke .
    

    La imagen se almacena en Artifact Registry.

Crea un clúster de GKE

Un clúster de GKE es un conjunto administrado de máquinas virtuales de Compute Engine que operan como un solo clúster de GKE.

  1. Crea el clúster.

    gcloud container clusters create-auto helloworld-gke \
      --location us-central1
    
  2. Verifica que tienes acceso al clúster. Con el comando siguiente, se enumeran los nodos de tu clúster de contenedores que están en funcionamiento y que indican que tienes acceso al clúster.

    kubectl get nodes
    

    Si detectas errores, consulta la Guía de solución de problemas de Kubernetes

Implementar en GKE

Si quieres implementar tu app en el clúster de GKE que creaste, necesitas dos objetos de Kubernetes.

  1. Una Implementación para definir tu app.
  2. Un servicio para definir cómo acceder a tu app.

Implementar una app

La app tiene un servidor frontend que controla las solicitudes web. Define los recursos del clúster necesarios para ejecutar el frontend en un archivo nuevo llamado deployment.yaml. Estos recursos se describen como una implementación. Debes usar implementaciones para crear y actualizar un ReplicaSet y sus pods asociados.

  1. Crea el archivo deployment.yaml en el mismo directorio que tus otros archivos y copia el siguiente contenido. Reemplaza los siguientes valores en tu archivo:

    • $GCLOUD_PROJECT es el ID del proyecto de Google Cloud.
    • $LOCATION es la ubicación del repositorio, como us-central1.
    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: helloworld-gke
    spec:
      replicas: 1
      selector:
        matchLabels:
          app: hello
      template:
        metadata:
          labels:
            app: hello
        spec:
          containers:
          - name: hello-app
            # Replace $LOCATION with your Artifact Registry location (e.g., us-west1).
            # Replace $GCLOUD_PROJECT with your project ID.
            image: $LOCATION-docker.pkg.dev/$GCLOUD_PROJECT/hello-repo/helloworld-gke:latest
            # This app listens on port 8080 for web traffic by default.
            ports:
            - containerPort: 8080
            env:
              - name: PORT
                value: "8080"
            resources:
              requests:
                memory: "1Gi"
                cpu: "500m"
                ephemeral-storage: "1Gi"
              limits:
                memory: "1Gi"
                cpu: "500m"
                ephemeral-storage: "1Gi"
  2. Implementa el recurso en el clúster:

    kubectl apply -f deployment.yaml
    
  3. Haz un seguimiento del estado de la implementación.

    kubectl get deployments
    

    El objeto Deployment se completa cuando todas las implementaciones AVAILABLE se encuentran READY.

    NAME              READY   UP-TO-DATE   AVAILABLE   AGE
    helloworld-gke    1/1     1            1           20s
    

    Si la implementación falla, vuelve a ejecutar kubectl apply -f deployment.yaml para actualizarla con los cambios.

  4. Cuando la implementación finalice, podrás ver los pods que creó.

    kubectl get pods
    

Implementa el servicio:

Los servicios proporcionan un solo punto de acceso a un conjunto de pods. Si bien es posible acceder a un solo pod, los pods son efímeros y solo se puede acceder de manera confiable mediante una dirección del servicio. En tu app de Hello World, el servicio “hello” define un balanceador de cargas para acceder a los hello-app pods desde una sola dirección IP. Este servicio se define en el archivo service.yaml.

  1. Crea el archivo service.yaml en el mismo directorio que tus otros archivos de origen con el contenido siguiente:

    # The hello service provides a load-balancing proxy over the hello-app
    # pods. By specifying the type as a 'LoadBalancer', Kubernetes Engine will
    # create an external HTTP load balancer.
    apiVersion: v1
    kind: Service
    metadata:
      name: hello
    spec:
      type: LoadBalancer
      selector:
        app: hello
      ports:
      - port: 80
        targetPort: 8080

    Los pods se definen por separado desde el servicio que usa los Pods. Kubernetes usa etiquetas para seleccionar los Pods a los que se dirige un servicio. Gracias a las etiquetas, puedes tener un servicio que se dirija a los Pods desde conjuntos de réplicas diferentes y tener múltiples servicios que apunten a un Pod en particular.

  2. Crea el servicio Hello World:

    kubectl apply -f service.yaml
    
  3. Obtén la dirección IP externa del servicio:

    kubectl get services
    

    Puede tardar hasta 60 segundos asignar la dirección IP. La dirección IP externa aparecerá en la columna EXTERNAL-IP para el servicio hello.

    NAME         TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
    hello        LoadBalancer   10.22.222.222   35.111.111.11   80:32341/TCP   1m
    kubernetes   ClusterIP      10.22.222.1     <none>          443/TCP        20m
    

Visualiza una app implementada

Ya implementaste todos los recursos necesarios para ejecutar la app de Hello World en GKE.

Usa la dirección IP externa del paso anterior para cargar la app a tu navegador web y visualiza tu app en ejecución:

 http://EXTERNAL_IP

O bien puedes realizar una llamada curl a la dirección IP externa del servicio:

curl EXTERNAL_IP

El resultado muestra la siguiente información:

Hello World!

Limpia

Sigue estos pasos para evitar que se apliquen cargos a tu cuenta de Google Cloud por los recursos que usaste en esta página.

Se cobra por las instancias de Compute Engine que se ejecutan en tu clúster y por la imagen de contenedor en Artifact Registry.

Borra el proyecto

Si borras tu proyecto de Google Cloud, se dejan de facturar todos los recursos que usaste en ese proyecto.

  1. En la consola de Google Cloud, ve a la página Administrar recursos.

    Ir a Administrar recursos

  2. En la lista de proyectos, elige el proyecto que quieres borrar y haz clic en Borrar.
  3. En el diálogo, escribe el ID del proyecto y, luego, haz clic en Cerrar para borrar el proyecto.

Borra tu clúster y contenedor

Si deseas conservar tu proyecto, pero solo borrar los recursos que usaste en este instructivo, borra tu clúster y la imagen.

A fin de borrar un clúster mediante Google Cloud CLI, ejecuta el siguiente comando para el modo que usaste:

gcloud container clusters delete helloworld-gke \
    --location us-central1

Para borrar una imagen en tu repositorio de Artifact Registry, ejecuta el siguiente comando:

gcloud artifacts docker images delete \
    us-central1-docker.pkg.dev/PROJECT_ID/hello-repo/helloworld-gke

Próximos pasos

Para obtener más información sobre Kubernetes, consulta los vínculos siguientes:

Para obtener más información sobre la implementación en GKE, consulta los vínculos siguientes:

Si quieres obtener más información para crear, desarrollar y ejecutar aplicaciones en GKE directamente desde tu IDE con Cloud Code, consulta las siguientes referencias: