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 del selector de proyectos de Google Cloud Console, selecciona o crea un proyecto de Google Cloud.

    Ir a la página del selector de proyectos

  3. Asegúrate de que la facturación esté habilitada para tu proyecto de Cloud. Descubre cómo confirmar que tienes habilitada la facturación en un proyecto.

  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 y usa el comando de cambio de directorio en él:

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

    module github.com/GoogleCloudPlatform/golang-samples/run/helloworld
    
    go 1.13
    
  3. Crea un archivo nuevo llamado main.go y pega el siguiente código en él:

    
    // Sample run-helloworld is a minimal Cloud Run service.
    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    	"os"
    )
    
    func main() {
    	log.Print("starting server...")
    	http.HandleFunc("/", handler)
    
    	// Determine port for HTTP service.
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    		log.Printf("defaulting to port %s", port)
    	}
    
    	// Start HTTP server.
    	log.Printf("listening on port %s", port)
    	if err := http.ListenAndServe(":"+port, nil); err != nil {
    		log.Fatal(err)
    	}
    }
    
    func handler(w http.ResponseWriter, r *http.Request) {
    	name := os.Getenv("NAME")
    	if name == "" {
    		name = "World"
    	}
    	fmt.Fprintf(w, "Hello %s!\n", name)
    }
    

    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 y usa el comando de cambio de directorio en él:

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

    {
      "name": "helloworld",
      "description": "Simple hello world sample in Node",
      "version": "1.0.0",
      "private": true,
      "main": "index.js",
      "scripts": {
        "start": "node index.js",
        "test": "mocha test/index.test.js --exit",
        "system-test": "NAME=Cloud test/runner.sh mocha test/system.test.js --timeout=30000",
        "lint": "eslint '**/*.js'",
        "fix": "eslint --fix '**/*.js'"
      },
      "engines": {
        "node": ">= 12.0.0"
      },
      "author": "Google LLC",
      "license": "Apache-2.0",
      "dependencies": {
        "express": "^4.17.1"
      },
      "devDependencies": {
        "got": "^11.0.0",
        "mocha": "^8.0.0",
        "supertest": "^6.0.0"
      }
    }
    
  3. En el mismo directorio, crea un archivo index.js y copia las siguientes líneas en él:

    const express = require('express');
    const app = express();
    
    app.get('/', (req, res) => {
      const name = process.env.NAME || 'World';
      res.send(`Hello ${name}!`);
    });
    
    const port = process.env.PORT || 8080;
    app.listen(port, () => {
      console.log(`helloworld: 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 y usa el comando de cambio de directorio en él:

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

    import os
    
    from flask import Flask
    
    app = Flask(__name__)
    
    @app.route('/')
    def hello_world():
        name = os.environ.get('NAME', 'World')
        return 'Hello {}!'.format(name)
    
    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.3.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.

    A fin de usar el comando cURL anterior en Microsoft Windows, necesitas una de las siguientes líneas de comandos o, de manera opcional, usar Spring Initializr (configuración precargada) para generar el proyecto:

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

    
    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("${NAME:World}")
      String name;
    
      @RestController
      class HelloworldController {
        @GetMapping("/")
        String hello() {
          return "Hello " + name + "!";
        }
      }
    
      public static void main(String[] args) {
        SpringApplication.run(HelloworldApplication.class, args);
      }
    }
  3. Configura el puerto del servidor que define la variable de entorno PORT en application.properties:

    server.port=${PORT:8080}

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.

C++

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

    mkdir helloworld-cpp
    cd helloworld-cpp
    
  2. Crea un archivo nuevo llamado CMakeLists.txt y pega el siguiente código en él:

    cmake_minimum_required(VERSION 3.10)
    
    # Define the project name and where to report bugs.
    set(PACKAGE_BUGREPORT "https://github.com/GoogleCloudPlatform/cpp-samples/issues")
    project(Docker-Run-C++ CXX C)
    
    # Configure the Compiler options, we will be using C++17 features.
    set(CMAKE_CXX_STANDARD 17)
    set(CMAKE_CXX_STANDARD_REQUIRED ON)
    
    find_package(Boost 1.66 REQUIRED COMPONENTS program_options)
    find_package(Threads)
    
    # When using static libraries the FindgRPC.cmake module does not define the
    # correct dependencies (OpenSSL::Crypto, c-cares, etc) for gRPC::grpc.
    # Explicitly listing these dependencies avoids the undefined symbols problems.
    add_executable(cloud_run_hello cloud_run_hello.cc)
    target_link_libraries(cloud_run_hello PRIVATE Boost::headers
                                                  Boost::program_options
        Threads::Threads)
    
    include(GNUInstallDirs)
    install(TARGETS cloud_run_hello RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
  3. Crea un archivo nuevo llamado cloud_run_hello.cpp y pega el siguiente código en él:

    #include <boost/asio/ip/tcp.hpp>
    #include <boost/asio/strand.hpp>
    #include <boost/beast/core.hpp>
    #include <boost/beast/http.hpp>
    #include <boost/beast/version.hpp>
    #include <boost/program_options.hpp>
    #include <cstdlib>
    #include <iostream>
    #include <optional>
    #include <thread>
    
    namespace be = boost::beast;
    namespace asio = boost::asio;
    namespace po = boost::program_options;
    using tcp = boost::asio::ip::tcp;
    
    po::variables_map parse_args(int& argc, char* argv[]) {
      // Initialize the default port with the value from the "PORT" environment
      // variable or with 8080.
      auto port = [&]() -> std::uint16_t {
        auto env = std::getenv("PORT");
        if (env == nullptr) return 8080;
        auto value = std::stoi(env);
        if (value < std::numeric_limits<std::uint16_t>::min() ||
            value > std::numeric_limits<std::uint16_t>::max()) {
          std::ostringstream os;
          os << "The PORT environment variable value (" << value
             << ") is out of range.";
          throw std::invalid_argument(std::move(os).str());
        }
        return static_cast<std::uint16_t>(value);
      }();
    
      // Parse the command-line options.
      po::options_description desc("Server configuration");
      desc.add_options()
          //
          ("help", "produce help message")
          //
          ("address", po::value<std::string>()->default_value("0.0.0.0"),
           "set listening address")
          //
          ("port", po::value<std::uint16_t>()->default_value(port),
           "set listening port");
    
      po::variables_map vm;
      po::store(po::parse_command_line(argc, argv, desc), vm);
      po::notify(vm);
      if (vm.count("help")) {
        std::cout << desc << "\n";
      }
      return vm;
    }
    
    int main(int argc, char* argv[]) try {
      po::variables_map vm = parse_args(argc, argv);
    
      if (vm.count("help")) return 0;
    
      auto address = asio::ip::make_address(vm["address"].as<std::string>());
      auto port = vm["port"].as<std::uint16_t>();
      std::cout << "Listening on " << address << ":" << port << std::endl;
    
      auto handle_session = [](tcp::socket socket) {
        auto report_error = [](be::error_code ec, char const* what) {
          std::cerr << what << ": " << ec.message() << "\n";
        };
    
        be::error_code ec;
        for (;;) {
          be::flat_buffer buffer;
    
          // Read a request
          be::http::request<be::http::string_body> request;
          be::http::read(socket, buffer, request, ec);
          if (ec == be::http::error::end_of_stream) break;
          if (ec) return report_error(ec, "read");
    
          // Send the response
          // Respond to any request with a "Hello World" message.
          be::http::response<be::http::string_body> response{be::http::status::ok,
                                                             request.version()};
          response.set(be::http::field::server, BOOST_BEAST_VERSION_STRING);
          response.set(be::http::field::content_type, "text/plain");
          response.keep_alive(request.keep_alive());
          std::string greeting = "Hello ";
          auto const* target = std::getenv("TARGET");
          greeting += target == nullptr ? "World" : target;
          greeting += "\n";
          response.body() = std::move(greeting);
          response.prepare_payload();
          be::http::write(socket, response, ec);
          if (ec) return report_error(ec, "write");
        }
        socket.shutdown(tcp::socket::shutdown_send, ec);
      };
    
      asio::io_context ioc{/*concurrency_hint=*/1};
      tcp::acceptor acceptor{ioc, {address, port}};
      for (;;) {
        auto socket = acceptor.accept(ioc);
        if (!socket.is_open()) break;
        // Run a thread per-session, transferring ownership of the socket
        std::thread{handle_session, std::move(socket)}.detach();
      }
    
      return 0;
    } catch (std::exception const& ex) {
      std::cerr << "Standard exception caught " << ex.what() << '\n';
      return 1;
    }

    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.

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 nuevo llamado index.php y pega el siguiente código en él:

    
    $name = getenv('NAME', true) ?: 'World';
    echo sprintf('Hello %s!', $name);
    

    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 y usa el comando de cambio de directorio en él:

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

    require "sinatra"
    
    set :bind, "0.0.0.0"
    port = ENV["PORT"] || "8080"
    set :port, port
    
    get "/" do
      name = ENV["NAME"] || "World"
      "Hello #{name}!"
    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 el siguiente código en él:

    source "https://rubygems.org"
    
    gem "sinatra", "~>2.0"
    
    group :test do
      gem "rack-test"
      gem "rest-client"
      gem "rspec"
      gem "rspec_junit_formatter"
      gem "rubysl-securerandom"
    end
    
  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:

    
    set -e
    echo "Hello ${NAME:-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 siguiente contenido:

    
    // Sample helloworld-shell is a Cloud Run shell-script-as-a-service.
    package main
    
    import (
    	"log"
    	"net/http"
    	"os"
    	"os/exec"
    )
    
    func main() {
    	http.HandleFunc("/", scriptHandler)
    
    	// Determine port for HTTP service.
    	port := os.Getenv("PORT")
    	if port == "" {
    		port = "8080"
    		log.Printf("Defaulting to port %s", port)
    	}
    
    	// Start HTTP server.
    	log.Printf("Listening on port %s", port)
    	if err := http.ListenAndServe(":"+port, nil); err != nil {
    		log.Fatal(err)
    	}
    }
    
    func scriptHandler(w http.ResponseWriter, r *http.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)
    }
    

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 offical golang image to create a binary.
# This is based on Debian and sets the GOPATH to /go.
# https://hub.docker.com/_/golang
FROM golang:1.15-buster as builder

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

# Retrieve application dependencies.
# This allows the container build to reuse cached dependencies.
# Expecting to copy go.mod and if present go.sum.
COPY go.* ./
RUN go mod download

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

# Build the binary.
RUN go build -mod=readonly -v -o server

# Use the official Debian slim image for a lean production container.
# https://hub.docker.com/_/debian
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM debian:buster-slim
RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
    ca-certificates && \
    rm -rf /var/lib/apt/lists/*

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

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

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

# The .dockerignore file excludes files from the container build process.
#
# https://docs.docker.com/engine/reference/builder/#dockerignore-file

# Exclude locally vendored dependencies.
vendor/

# Exclude "build-time" ignore files.
.dockerignore
.gcloudignore

# Exclude git history and configuration.
.gitignore

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 copying both package.json AND package-lock.json (when available).
# Copying this first prevents re-running npm install on every code change.
COPY package*.json ./

# Install production dependencies.
# If you add a package-lock.json, speed your build by switching to 'npm ci'.
# RUN npm ci --only=production
RUN npm install --only=production

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

# Run the web service on container startup.
CMD [ "node", "index.js" ]

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

Dockerfile
.dockerignore
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.9-slim

# Allow statements and log messages to immediately appear in the Knative logs
ENV PYTHONUNBUFFERED True

# 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 main:app

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

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

Java


# Use the official maven/Java 8 image to create a build artifact.
# https://hub.docker.com/_/maven
FROM maven:3.6-jdk-11 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/openjdk11: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"]

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

Dockerfile
.dockerignore
target/

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 de origen de la app de muestra:

# The .gcloudignore file excludes file from upload to Cloud Build.
# If this file is deleted, gcloud will default to .gitignore.
#
# https://cloud.google.com/cloud-build/docs/speeding-up-builds#gcloudignore
# https://cloud.google.com/sdk/gcloud/reference/topic/gcloudignore

**/obj/
**/bin/

# Exclude git history and configuration.
.git/
.gitignore

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.

C++

El Dockerfile de C++ inicia la aplicación que escucha en el puerto definido por la variable de entorno PORT:

# We chose Alpine to build the image because it has good support for creating
# statically-linked, small programs.
ARG DISTRO_VERSION=edge
FROM alpine:${DISTRO_VERSION} AS base

# Create separate targets for each phase, this allows us to cache intermediate
# stages when using Google Cloud Build, and makes the final deployment stage
# small as it contains only what is needed.
FROM base AS devtools

# Install the typical development tools and some additions:
#   - ninja-build is a backend for CMake that often compiles faster than
#     CMake with GNU Make.
#   - Install the boost libraries.
RUN apk update && \
    apk add \
        boost-dev \
        boost-static \
        build-base \
        cmake \
        git \
        gcc \
        g++ \
        libc-dev \
        nghttp2-static \
        ninja \
        openssl-dev \
        openssl-libs-static \
        tar \
        zlib-static

# Copy the source code to /v/source and compile it.
FROM devtools AS build
COPY . /v/source
WORKDIR /v/source

# Run the CMake configuration step, setting the options to create
# a statically linked C++ program
RUN cmake -S/v/source -B/v/binary -GNinja \
    -DCMAKE_BUILD_TYPE=Release \
    -DBoost_USE_STATIC_LIBS=ON \
    -DCMAKE_EXE_LINKER_FLAGS=-static

# Compile the binary and strip it to reduce its size.
RUN cmake --build /v/binary
RUN strip /v/binary/cloud_run_hello

# Create the final deployment image, using `scratch` (the empty Docker image)
# as the starting point. Effectively we create an image that only contains
# our program.
FROM scratch AS cloud-run-hello
WORKDIR /r

# Copy the program from the previously created stage and make it the entry point.
COPY --from=build /v/binary/cloud_run_hello /r

ENTRYPOINT [ "/r/cloud_run_hello" ]

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 image.
# https://hub.docker.com/_/php
FROM php:7.4-apache

# Configure PHP for Cloud Run.
# Precompile PHP code with opcache.
RUN docker-php-ext-install -j "$(nproc)" opcache
RUN set -ex; \
  { \
    echo "; Cloud Run enforces memory & timeouts"; \
    echo "memory_limit = -1"; \
    echo "max_execution_time = 0"; \
    echo "; File upload at Cloud Run network limit"; \
    echo "upload_max_filesize = 32M"; \
    echo "post_max_size = 32M"; \
    echo "; Configure Opcache for Containers"; \
    echo "opcache.enable = On"; \
    echo "opcache.validate_timestamps = Off"; \
    echo "; Configure Opcache Memory (Application-specific)"; \
    echo "opcache.memory_consumption = 32"; \
  } > "$PHP_INI_DIR/conf.d/cloud-run.ini"

# Copy in custom code from the host machine.
WORKDIR /var/www/html
COPY . ./

# Use the PORT environment variable in Apache configuration files.
# https://cloud.google.com/run/docs/reference/container-contract#port
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://github.com/docker-library/docs/blob/master/php/README.md#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.

# The .dockerignore file excludes files from the container build process.
#
# https://docs.docker.com/engine/reference/builder/#dockerignore-file

# Exclude locally vendored dependencies.
vendor/

# Exclude "build-time" ignore files.
.dockerignore
.gcloudignore

# Exclude git history and configuration.
.gitignore

Ruby


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

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

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

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

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

Dockerfile
README.md
.ruby-version
.bundle/
vendor/

Shell


# Use the offical golang image to create a binary.
# This is based on Debian and sets the GOPATH to /go.
# https://hub.docker.com/_/golang
FROM golang:1.14-buster as builder

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

# Retrieve application dependencies.
# This allows the container build to reuse cached dependencies.
# Expecting to copy go.mod and if present go.sum.
COPY go.* ./
RUN go mod download

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

# Build the binary.
RUN go build -mod=readonly -v -o server

# Use the official Debian slim image for a lean production container.
# https://hub.docker.com/_/debian
# https://docs.docker.com/develop/develop-images/multistage-build/#use-multi-stage-builds
FROM debian:buster-slim
RUN set -x && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install -y \
    --no-install-recommends \
    ca-certificates && \
    rm -rf /var/lib/apt/lists/*

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

# Run the web service on container startup.
CMD ["/app/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:

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

Sujetas a los Precios del nivel 2

  • asia-east2 (Hong Kong)
  • asia-northeast3 (Seúl, Corea del Sur)
  • asia-southeast1 (Singapur)
  • asia-southeast2 (Yakarta)
  • asia-south1 (Bombay, India)
  • australia-southeast1 (Sídney)
  • europe-west2 (Londres, Reino Unido)
  • europe-west3 (Fráncfort, Alemania)
  • europe-west6 (Zúrich, Suiza)
  • northamerica-northeast1 (Montreal)
  • southamerica-east1 (São Paulo, Brasil)

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 reduce la escala de la imagen de contenedor de forma automática y horizontal para controlar las solicitudes que se reciben y, luego, escala horizontalmente cuando disminuye la demanda. Solo debes pagar por la CPU, la memoria y las herramientas de redes que se usen durante el control de las 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: