Guía de inicio rápido: Compila e implementa

En esta página, se muestra cómo crear una aplicación de Hello World simple, empaquetarla en una imagen de contenedor, subir esta imagen de contenedor a Container Registry y, luego, implementarla en Cloud Run. La muestra se indica en varios lenguajes, pero ten en cuenta que puedes usar otros además de los que se señalan.

También puedes seguir esta guía de inicio rápido con una cuenta de demostración en Qwiklabs.

Antes de comenzar

  1. Accede a tu Cuenta de Google.

    Si todavía no tienes una cuenta, regístrate para obtener una nueva.

  2. En la página de selección de proyectos de Cloud Console, selecciona o crea un proyecto de Cloud.

    Ir a la página Selector de proyectos

  3. Comprueba que la facturación esté habilitada en tu proyecto.

    Descubre cómo puedes habilitar la facturación

  4. Instala e inicializa el SDK de Cloud.

Escribe la aplicación de muestra

Si deseas obtener instrucciones para crear una aplicación de muestra de Hello World que se ejecute en Cloud Run, haz clic en la pestaña correspondiente a tu lenguaje:

Go

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

    mkdir helloworld-go
    cd helloworld-go
    
  2. Inicializa un archivo go.mod para declarar el módulo de go:

    module github.com/knative/docs/docs/serving/samples/hello-world/helloworld-go
    
    go 1.13
    
  3. Crea un archivo nuevo llamado helloworld.go y pega el código siguiente en él:

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

    Con este código, se crea un servidor web básico que escucha en el puerto definido por la variable de entorno PORT.

La app está lista para organizarla en contenedores y subirla a Container Registry.

Node.js

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

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

    {
      "name": "knative-serving-helloworld",
      "version": "1.0.0",
      "description": "Simple 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 él:

    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 básico que escucha en el puerto definido por la variable de entorno PORT.

La app está lista para organizarla en contenedores y subirla a Container Registry.

Python

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

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

    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)))
    

    Este código responde a las solicitudes con nuestro saludo “Hello World”. El control de HTTP lo realiza un servidor web de Gunicorn en el contenedor. Cuando se invoca directamente para uso local, este código crea un servidor web básico que escucha en el puerto definido por la variable de entorno PORT.

La app está lista para organizarla en contenedores y subirla a Container Registry.

Java

Crea una aplicación de Spring Boot.

  1. En la consola, usa los comandos de descompresión y de cURL para crear un proyecto web vacío nuevo:

    curl https://start.spring.io/starter.zip \
        -d dependencies=web \
        -d javaVersion=1.8 \
        -d bootVersion=2.1.3.RELEASE \
        -d name=helloworld \
        -d artifactId=helloworld \
        -d baseDir=helloworld \
        -o helloworld.zip
    unzip helloworld.zip
    cd helloworld
    

    Se creará un proyecto de Spring Boot.

  2. Con el objeto de actualizar la clase HelloworldApplication en src/main/java/com/example/helloworld/HelloworldApplication.java, agrega un @RestController a fin de controlar la asignación de / y, también, agrega un campo @Value para proporcionar la variable de entorno TARGET:

    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 básico que escucha en el puerto definido por la variable de entorno PORT.

La app está lista para organizarla en contenedores y subirla a Container Registry.

A fin de implementar Java en Cloud Run con otros frameworks, revisa las muestras de Knative para Spark y Vert.x.

C#

  1. Instala .NET Core SDK 3.1. Ten en cuenta que solo necesitamos hacer esto para crear el proyecto web nuevo en el siguiente paso: el Dockerfile, que se describe más adelante, cargará todas las dependencias en el contenedor.

  2. Desde la consola, crea un proyecto web vacío nuevo con el comando de dotnet:

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

  4. Actualiza la definición de CreateHostBuilder en Program.cs para escuchar en el puerto definido por la variable de entorno PORT:

    using System;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.Hosting;
    
    namespace helloworld_csharp
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                CreateHostBuilder(args).Build().Run();
            }
    
            public static IHostBuilder CreateHostBuilder(string[] args)
            {
                string port = Environment.GetEnvironmentVariable("PORT") ?? "8080";
                string url = String.Concat("http://0.0.0.0:", port);
    
                return Host.CreateDefaultBuilder(args)
                    .ConfigureWebHostDefaults(webBuilder =>
                    {
                        webBuilder.UseStartup<Startup>().UseUrls(url);
                    });
            }
        }
    }

    Con este código, se crea un servidor web básico que escucha en el puerto definido por la variable de entorno PORT.

  5. Crea un archivo llamado Startup.cs y pega el código siguiente en él:

    using System;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.Http;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    
    namespace helloworld_csharp
    {
        public class Startup
        {
            // This method gets called by the runtime. Use this method to add services to the container.
            // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
            public void ConfigureServices(IServiceCollection services)
            {
            }
    
            // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
    
                app.UseRouting();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapGet("/", async context =>
                    {
                        var target = Environment.GetEnvironmentVariable("TARGET") ?? "World";
                        await context.Response.WriteAsync($"Hello {target}!\n");
                    });
                });
            }
        }
    }

    Este código responde a las solicitudes con nuestro saludo “Hello World”.

La app está lista para organizarla en contenedores y subirla a Container Registry.

PHP

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

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

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

    Este código responde a las solicitudes con nuestro saludo “Hello World”. El control de HTTP lo realiza Apache Web Server en el contenedor.

La app está lista para organizarla en contenedores y subirla a Container Registry.

Ruby

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

    mkdir helloworld-ruby
    cd helloworld-ruby
    
  2. Crea un archivo llamado app.rb y pega el código siguiente en él:

    require 'sinatra'
    
    set :bind, '0.0.0.0'
    
    get '/' do
      target = ENV['TARGET'] || 'World'
      "Hello #{target}!\n"
    end
    

    Con este código, se crea un servidor web básico que escucha en el puerto definido por la variable de entorno PORT.

  3. Crea un archivo llamado Gemfile y copia la información siguiente en él:

    source 'https://rubygems.org'
    
    gem 'sinatra'
    gem 'rack', '>= 2.0.6'
    
  4. Si no tienes instalado Bundler 2.0 o superior, instala Bundler.

  5. Genera un archivo Gemfile.lock mediante la ejecución del siguiente comando:

    bundle install

La app está lista para organizarla en contenedores y subirla a Container Registry.

Shell

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

    mkdir helloworld-shell
    cd helloworld-shell
    
  2. Crea un archivo script.sh con el siguiente contenido:

    #!/bin/sh
    echo Hello ${TARGET:=World}!
    
    

    Para ejecutar esta secuencia de comandos de shell en cada solicitud entrante, esta muestra usa un pequeño programa de Go que inicia un servidor web básico y escucha en el puerto definido por la variable de entorno PORT.

  3. Crea un archivo invoke.go con el contenido siguiente:

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    	"os/exec"
    )
    
    func handler(w http.ResponseWriter, r *http.Request) {
    	log.Print("helloworld: received a request")
    
    	cmd := exec.CommandContext(r.Context(), "/bin/sh", "script.sh")
    	cmd.Stderr = os.Stderr
    	out, err := cmd.Output()
    	if err != nil {
    		w.WriteHeader(500)
    	}
    	w.Write(out)
    }
    
    func main() {
    	log.Print("helloworld: starting server...")
    
    	http.HandleFunc("/", handler)
    
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    	}
    
    	log.Printf("helloworld: listening on %s", port)
    	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%s", port), nil))
    }
    

La app está lista para organizarla en contenedores y subirla a Container Registry.

Otro

Cloud Run admite la mayoría de los lenguajes. Si deseas obtener muestras simples en otros lenguajes además de los de esta tabla, consulta la información siguiente:

Sin embargo, en todas estas muestras, ignora y omite el material sobre service.yaml y Docker Hub, ya que Cloud Run no los usa.

Organiza una app en contenedores y súbela a Container Registry

Si quieres organizar una app de muestra en contenedores, crea un archivo nuevo llamado Dockerfile en el mismo directorio que los archivos fuente y copia el siguiente contenido:

Go

# Use the official Golang 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.13 as builder

# Create and change to the app directory.
WORKDIR /app

# Retrieve application dependencies using go modules.
# Allows container builds to reuse downloaded dependencies.
COPY go.* ./
RUN go mod download

# Copy local code to the container image.
COPY . ./

# Build the binary.
# -mod=readonly ensures immutable go.mod and go.sum in container builds.
RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -v -o server

# Use the official Alpine image for a lean production container.
# https://hub.docker.com/_/alpine
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM alpine:3
RUN apk add --no-cache ca-certificates

# Copy the binary to the production image from the builder stage.
COPY --from=builder /app/server /server

# Run the web service on container startup.
CMD ["/server"]

Node.js

# Use the official lightweight Node.js 12 image.
# https://hub.docker.com/_/node
FROM node:12-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 --only=production

# Copy local code to the container image.
COPY . ./

# Run the web service on container startup.
CMD [ "npm", "start" ]

Agrega un archivo .dockerignore para excluir archivos de tu imagen de contenedor.

Dockerfile
README.md
node_modules
npm-debug.log

Python

El Dockerfile de Python inicia un servidor web de Gunicorn que escucha en el puerto definido por la variable de entorno PORT:

# 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 --timeout 0 app:app

Agrega un archivo .dockerignore para excluir archivos de tu imagen de 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", "-jar", "/helloworld.jar"]

C#

# Use Microsoft's official build .NET image.
# https://hub.docker.com/_/microsoft-dotnet-core-sdk/
FROM mcr.microsoft.com/dotnet/core/sdk:3.1-alpine 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 . ./
WORKDIR /app

# Build a release artifact.
RUN dotnet publish -c Release -o out

# Use Microsoft's official runtime .NET image.
# https://hub.docker.com/_/microsoft-dotnet-core-aspnet/
FROM mcr.microsoft.com/dotnet/core/aspnet:3.1-alpine AS runtime
WORKDIR /app
COPY --from=build /app/out ./

# Run the web service on container startup.
ENTRYPOINT ["dotnet", "helloworld-csharp.dll"]

Para evitar que se suban a Cloud Build archivos producidos a través de operaciones de compilación locales dotnet, agrega un archivo .gcloudignore en el mismo directorio que los archivos fuente de la app de muestra:

**/obj/
**/bin/

Si estas líneas están en un archivo .gitignore, puedes omitir este paso porque .gitignore es una fuente predeterminada para la configuración de .gcloudignore.

Copia estas líneas a un archivo .dockerignore para compilaciones de contenedores locales con la CLI de docker.

PHP

El Dockerfile de PHP inicia un Apache Web Server que escucha en el puerto definido por la variable de entorno PORT:

# Use the official PHP 7.3 image.
# https://hub.docker.com/_/php
FROM php:7.3-apache

# Copy local code to the container image.
COPY index.php /var/www/html/

# Use the PORT environment variable 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 excluir archivos de tu imagen de contenedor.

Dockerfile
README.md
vendor

Ruby

# Use the official lightweight Ruby image.
# https://hub.docker.com/_/ruby
FROM ruby:2.6-slim

# Install production dependencies.
WORKDIR /usr/src/app
COPY Gemfile Gemfile.lock ./
ENV BUNDLE_FROZEN=true
RUN gem install bundler && bundle install

# Copy local code to the container image.
COPY . ./

# Run the web service on container startup.
CMD ["ruby", "./app.rb"]

Shell

# Use the official Golang 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.13 as builder

# Create and change to the app directory.
WORKDIR /app

# Retrieve application dependencies using go modules.
# Allows container builds to reuse downloaded dependencies.
COPY go.* ./
RUN go mod download

# Copy local code to the container image.
COPY invoke.go ./

# Build the binary.
# -mod=readonly ensures immutable go.mod and go.sum in container builds.
RUN CGO_ENABLED=0 GOOS=linux go build -mod=readonly -v -o server

# Use the official Alpine image for a lean production container.
# https://hub.docker.com/_/alpine
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM alpine:3
RUN apk add --no-cache ca-certificates

# Copy the binary to the production image from the builder stage.
COPY --from=builder /app/server /server
COPY script.sh ./

# Run the web service on container startup.
CMD ["/server"]

Otro

Cloud Run admite la mayoría de los lenguajes. Si deseas obtener Dockerfiles de muestra en lenguajes distintos de los de esta tabla, consulta la información siguiente:

Sin embargo, en estas muestras, ignora y omite el material sobre service.yaml y Docker Hub, ya que Cloud Run no los usa.

Usa Cloud Build a fin de compilar la imagen de contenedor. Para ello, ejecuta el siguiente comando desde el directorio que contiene el Dockerfile:

gcloud builds submit --tag gcr.io/PROJECT-ID/helloworld

En el ejemplo anterior, PROJECT-ID es el ID de tu proyecto de GCP. Puedes obtenerlo si ejecutas gcloud config get-value project.

Si se ejecuta de forma correcta, verás un mensaje de ÉXITO que contiene el nombre de la imagen (gcr.io/PROJECT-ID/helloworld). La imagen se almacena en Container Registry y se puede volver a usar si lo deseas.

Implementa en Cloud Run

Para implementar la imagen de contenedor, haz lo siguiente:

  1. Realiza la implementación con el siguiente comando:

    gcloud run deploy --image gcr.io/PROJECT-ID/helloworld --platform managed

    Reemplaza PROJECT-ID por el ID del proyecto de GCP. Para ver el ID del proyecto, ejecuta el comando gcloud config get-value project.

    1. Se te solicitará el nombre del servicio; presiona Intro para aceptar el nombre predeterminado, helloworld.
    2. Se te solicitará la región; selecciona la región que prefieras, por ejemplo, us-central1.
    3. Se te solicitará permitir invocaciones no autenticadas; responde y.

    Luego, espera un momento a que finalice la implementación. Si la operación se completa de forma correcta, la línea de comandos mostrará la URL de servicio.

  2. Abre la URL de servicio en un navegador web para visitar el contenedor implementado.

Ubicaciones de Cloud Run

Cloud Run es regional, lo que significa que la infraestructura que ejecuta los servicios se ubica en una región específica, y Google la administra para que esté disponible de manera redundante en todas las zonas de esa región.

El cumplimiento de los requisitos de latencia, disponibilidad o durabilidad es el factor principal para seleccionar la región en la que se ejecutan los servicios de Cloud Run. Por lo general, puedes seleccionar la región más cercana a los usuarios, pero debes considerar la ubicación de los otros productos de Google Cloud que usa el servicio de Cloud Run. Si usas productos de Google Cloud en varias ubicaciones, la latencia y el costo del servicio pueden verse afectados.

Cloud Run está disponible en las siguientes regiones:

Sujeto a los precios del nivel 1

  • asia-east1 (Taiwán)
  • asia-northeast1 (Tokio)
  • asia-northeast2 (Osaka)
  • europe-north1 (Finlandia)
  • europe-west1 (Bélgica)
  • europe-west4 (Países Bajos)
  • us-central1 (Iowa)
  • us-east1 (Carolina del Sur)
  • us-east4 (Virginia del Norte)
  • us-west1 (Oregón)

Sujeto a precios de nivel 2

  • australia-southeast1 (Sídney)
  • northamerica-northeast1 (Montreal)

Ten en cuenta que no es posible mapear dominios personalizados a servicios de Cloud Run (completamente administrados) en asia-northeast2, australia-southeast1 ni northamerica-northeast1.

Si ya creaste un servicio de Cloud Run, puedes ver la región en el panel de Cloud Run en Cloud Console.

Felicitaciones. Acabas de implementar una aplicación empaquetada en una imagen de contenedor en Cloud Run. Cloud Run escala la imagen de contenedor de forma automática y horizontal para controlar las solicitudes que se reciben y, luego, reduce la escala cuando disminuye la demanda. Solo debes pagar por la CPU, la memoria y las herramientas de redes que se usen durante la administración de solicitudes.

Realiza una limpieza

Quita el proyecto de prueba

Si bien Cloud Run no cobra cuando el servicio no se usa, es posible que se te cobre por el almacenamiento de la imagen de contenedor en Container Registry. Puedes borrar la imagen o borrar el proyecto de Cloud para evitar que se apliquen cargos. Si borras el proyecto de Cloud, se dejan de facturar todos los recursos que usaste en ese proyecto.

  1. En Cloud Console, ve a la página Administrar recursos.

    Ir a la página Administrar recursos

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

Próximos pasos

Para obtener más información sobre cómo compilar un contenedor a partir de código fuente y enviarlo a Container Registry, consulta las páginas siguientes: