Créer et déployer un service C++

Découvrez comment créer une application Hello World simple, l'intégrer dans une image de conteneur, importer l'image de conteneur dans Container Registry, puis déployer l'image de conteneur dans Cloud Run. Vous pouvez utiliser d'autres langues en plus de celles présentées.


Pour obtenir des conseils détaillés sur cette tâche directement dans l'éditeur Cloud Shell, cliquez sur Visite guidée:

VISITE GUIDÉE


La procédure décrite dans les sections suivantes, est la même que si vous cliquez sur Visite guidée.

Vous pouvez également suivre ce guide de démarrage rapide avec un compte de démonstration sur Qwiklabs.

Avant de commencer

  1. Connectez-vous à votre compte Google Cloud. Si vous débutez sur Google Cloud, créez un compte pour évaluer les performances de nos produits en conditions réelles. Les nouveaux clients bénéficient également de 300 $ de crédits gratuits pour exécuter, tester et déployer des charges de travail.
  2. Dans Google Cloud Console, sur la page de sélection du projet, sélectionnez ou créez un projet Google Cloud.

    Accéder au sélecteur de projet

  3. Assurez-vous que la facturation est activée pour votre projet Cloud. Découvrez comment vérifier que la facturation est activée pour votre projet.

  4. Installez et initialisez le SDK Cloud.

Écrire l'exemple d'application

Pour écrire une application en C++, procédez comme suit :

  1. Créez un répertoire nommé helloworld-cpp et modifiez les sous-répertoires comme suit :

    mkdir helloworld-cpp
    cd helloworld-cpp
    
  2. Créez un fichier nommé CMakeLists.txt et collez-y le code suivant :

    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(cpp-samples-cloud-run-hello-world 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)
    
    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. Créez un fichier nommé cloud_run_hello.cc et collez-y le code suivant :

    #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;
    }

    Ce code crée un serveur Web de base qui écoute le port défini par la variable d'environnement PORT.

Votre application est terminée et prête à être conteneurisée, puis importée dans Container Registry.

Conteneuriser une application et l'importer dans Container Registry

Pour conteneuriser l'exemple d'application, créez un fichier nommé Dockerfile dans le même répertoire que les fichiers sources, puis copiez le contenu suivant :

Le fichier Dockerfile C++ démarre l'application en écoute sur le port défini par la variable d'environnement 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" ]

Créez votre image de conteneur à l'aide de Cloud Build. Pour cela, exécutez la commande suivante à partir du répertoire contenant le fichier Docker :

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

PROJECT-ID correspond à l'ID de votre projet GCP. Vous pouvez l'obtenir en exécutant gcloud config get-value project.

Si l'opération réussit, un message "SUCCESS" contenant le nom de l'image (gcr.io/PROJECT-ID/helloworld) s'affiche. L'image est stockée dans Container Registry et peut être réutilisée si vous le souhaitez.

Déployer sur Cloud Run

Pour déployer l'image de conteneur, procédez comme suit :

  1. Utilisez la commande suivante pour effectuer le déploiement :

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

    Si vous êtes invité à activer l'API, répondez y pour l'activer.

    Remplacez PROJECT-ID par l'ID de votre projet GCP. Vous pouvez afficher votre ID de projet en exécutant la commande gcloud config get-value project.

    1. Vous serez invité à saisir le nom du service : appuyez sur Entrée pour accepter le nom par défaut, helloworld.
    2. Vous serez invité à indiquer la région : sélectionnez la région de votre choix, par exemple us-central1.
    3. Vous serez invité à autoriser les appels non authentifiés : répondez y.

    Patientez quelques instants jusqu'à la fin du déploiement. En cas de réussite, la ligne de commande affiche l'URL du service.

  2. Accédez à votre conteneur déployé en ouvrant l'URL du service dans un navigateur Web.

Emplacements Cloud Run

Cloud Run est régional, ce qui signifie que l'infrastructure qui exécute vos services Cloud Run est située dans une région spécifique et gérée par Google pour être disponible de manière redondante dans toutes les zones de cette région.

Lors de la sélection de la région dans laquelle exécuter vos services Cloud Run, vous devez tout d'abord considérer vos exigences en matière de latence, de disponibilité et de durabilité. Vous pouvez généralement sélectionner la région la plus proche de vos utilisateurs, mais vous devez tenir compte de l'emplacement des autres produits Google Cloud utilisés par votre service Cloud Run. L'utilisation conjointe de produits Google Cloud dans plusieurs emplacements peut avoir une incidence sur la latence et le coût de votre service.

Cloud Run est disponible dans les régions suivantes :

Soumis aux tarifs de niveau 1

  • asia-east1 (Taïwan)
  • asia-northeast1 (Tokyo)
  • asia-northeast2 (Osaka)
  • europe-north1 (Finlande)
  • europe-west1 (Belgique)
  • europe-west4 (Pays-Bas)
  • us-central1 (Iowa)
  • us-east1 (Caroline du Sud)
  • us-east4 (Virginie du Nord)
  • us-west1 (Oregon)

Soumis aux tarifs de niveau 2

  • asia-east2 (Hong Kong)
  • asia-northeast3 (Séoul, Corée du Sud)
  • asia-southeast1 (Singapour)
  • asia-southeast2 (Jakarta)
  • asia-south1 (Mumbai, Inde)
  • australia-southeast1 (Sydney)
  • europe-central2 (Varsovie, Pologne)
  • europe-west2 (Londres, Royaume-Uni)
  • europe-west3 (Francfort, Allemagne)
  • europe-west6 (Zurich, Suisse)
  • northamerica-northeast1 (Montréal)
  • southamerica-east1 (São Paulo, Brésil)
  • us-west2 (Los Angeles)
  • us-west3 (Las Vegas)
  • us-west4 (Salt Lake City)

Si vous avez déjà créé un service Cloud Run, vous pouvez afficher la région dans le tableau de bord Cloud Run de Cloud Console.

Félicitations ! Vous venez de déployer une application empaquetée dans une image de conteneur sur Cloud Run. Cloud Run effectue un scaling automatique et horizontal de votre image de conteneur pour traiter les requêtes reçues, puis un scaling vertical lorsque la demande diminue. Vous ne payez que pour le processeur, la mémoire et le réseau utilisés lors du traitement des requêtes.

Effectuer un nettoyage

Supprimer votre projet test

Bien que Cloud Run ne facture pas lorsque le service n'est pas utilisé, il se peut que des frais vous soient facturés pour le stockage de l'image de conteneur dans Container Registry. Vous pouvez supprimer votre image ou votre projet Cloud afin d'éviter que des frais ne vous soient facturés. La suppression de votre projet Cloud arrête la facturation de toutes les ressources utilisées dans ce projet.

  1. Dans Cloud Console, accédez à la page Gérer les ressources.

    Accéder à la page Gérer les ressources

  2. Dans la liste des projets, sélectionnez le projet que vous souhaitez supprimer, puis cliquez sur Supprimer.
  3. Dans la boîte de dialogue, saisissez l'ID du projet, puis cliquez sur Arrêter pour supprimer le projet.

Étape suivante

Pour savoir comment créer un conteneur à partir d'un code source et le transférer dans Container Registry, reportez-vous à la page :