C++-Dienst erstellen und bereitstellen

Erfahren Sie, wie Sie eine einfache Hello World-Anwendung erstellen, in ein Container-Image verpacken, das Container-Image in Container Registry hochladen und anschließend in Cloud Run bereitstellen. Sie können zusätzlich zu den angezeigten Sprachen auch andere Sprachen verwenden.

Hinweis

  1. Melden Sie sich bei Ihrem Google Cloud-Konto an. Wenn Sie mit Google Cloud noch nicht vertraut sind, erstellen Sie ein Konto, um die Leistungsfähigkeit unserer Produkte in der Praxis sehen und bewerten zu können. Neukunden erhalten außerdem ein Guthaben von 300 $, um Arbeitslasten auszuführen, zu testen und bereitzustellen.
  2. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  3. Die Abrechnung für das Cloud-Projekt muss aktiviert sein. So prüfen Sie, ob die Abrechnung für Ihr Projekt aktiviert ist.

  4. Installieren und initialisieren Sie das Cloud SDK.
  5. Wählen Sie in der Google Cloud Console auf der Seite der Projektauswahl ein Google Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  6. Die Abrechnung für das Cloud-Projekt muss aktiviert sein. So prüfen Sie, ob die Abrechnung für Ihr Projekt aktiviert ist.

  7. Installieren und initialisieren Sie das Cloud SDK.

Beispielanwendung schreiben

So schreiben Sie eine Anwendung in C++:

  1. Erstellen Sie ein neues Verzeichnis mit dem Namen helloworld-cpp und ersetzen Sie das aktuelle Verzeichnis durch dieses Verzeichnis:

    mkdir helloworld-cpp
    cd helloworld-cpp
    
  2. Erstellen Sie eine neue Datei mit dem Namen CMakeLists.txt und fügen Sie dort den folgenden Code ein:

    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. Erstellen Sie eine neue Datei mit dem Namen cloud_run_hello.cc und fügen Sie dort den folgenden Code ein:

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

    Mit diesem Code wird ein einfacher Webserver erstellt, der den von der Umgebungsvariable PORT definierten Port überwacht.

  4. Erstellen Sie eine neue Datei mit dem Namen Dockerfile im selben Verzeichnis wie die Quelldateien. Das C++-Dockerfile beginnt mit der Überwachung der Anwendung an dem Port, der mit der Umgebungsvariable PORT definiert wurde:

    # We chose Alpine to build the image because it has good support for creating
    # statically-linked, small programs.
    ARG DISTRO_VERSION=3.13
    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" ]

Die Anwendung ist fertig und kann bereitgestellt werden.

Aus Quelle in Cloud Run bereitstellen

Wichtig: In dieser Kurzanleitung wird davon ausgegangen, dass Sie Inhaber- oder Bearbeiterrollen in dem Projekt haben, das Sie für die Kurzanleitung verwenden. Andernfalls finden Sie die Berechtigungen unter Cloud Run-Bereitstellungsberechtigungen, Cloud Build-Berechtigungen und Artifact Registry-Berechtigungen.

So führen Sie die Bereitstellung aus:

  1. Stellen Sie es mit dem folgenden Befehl aus der Quelle bereit:

    gcloud run deploy

    Wenn Sie aufgefordert werden, die API zu aktivieren, antworten Sie mit y, um dies zu tun.

    1. Wenn Sie zur Eingabe des Quellcodes aufgefordert werden, drücken Sie die Eingabetaste, um den aktuellen Ordner bereitzustellen.

    2. Wenn Sie zur Eingabe des Dienstnamens aufgefordert werden, drücken Sie die Eingabetaste, um den Standardnamen helloworld zu akzeptieren.

    3. Wenn Sie aufgefordert werden, die Artifact Registry API zu aktivieren, antworten Sie mit "y".

    4. Wenn Sie zur Eingabe der Region aufgefordert werden, wählen Sie die Region Ihrer Wahl aus, zum Beispiel us-central1.

    5. Sie werden aufgefordert, nicht authentifizierte Aufrufe zuzulassen: Antworten Sie mit y.

    Warten Sie dann einige Sekunden, bis die Bereitstellung abgeschlossen ist. Bei Erfolg wird in der Befehlszeile die Dienst-URL angezeigt.

  2. Rufen Sie den bereitgestellten Service auf. Dazu öffnen Sie in einem Webbrowser die Dienst-URL.

Cloud Run-Standorte

Cloud Run ist regional. Die Infrastruktur, in der die Cloud Run-Dienste ausgeführt werden, befindet sich demnach in einer bestimmten Region. Aufgrund der Verwaltung durch Google sind die Anwendungen in allen Zonen innerhalb dieser Region redundant verfügbar.

Bei der Auswahl der Region, in der Ihre Cloud Run-Dienste ausgeführt werden, ist vorrangig, dass die Anforderungen hinsichtlich Latenz, Verfügbarkeit oder Langlebigkeit erfüllt werden. Sie können im Allgemeinen die Region auswählen, die Ihren Nutzern am nächsten liegt, aber Sie sollten den Standort der anderen Google Cloud-Produkte berücksichtigen, die von Ihrem Cloud Run-Dienst verwendet werden. Die gemeinsame Nutzung von Google Cloud-Produkten an mehreren Standorten kann sich auf die Latenz und die Kosten des Dienstes auswirken.

Cloud Run ist in diesen Regionen verfügbar:

Unterliegt Preisstufe 1

Unterliegt Preisstufe 2

  • asia-east2 (Hongkong)
  • asia-northeast3 (Seoul, Südkorea)
  • asia-southeast1 (Singapur)
  • asia-southeast2 (Jakarta)
  • asia-south1 (Mumbai, Indien)
  • asia-south2 (Delhi, Indien)
  • australia-southeast1 (Sydney)
  • australia-southeast2 (Melbourne)
  • europe-central2 (Warschau, Polen)
  • europe-west2 (London, Vereinigtes Königreich)
  • europe-west3 (Frankfurt, Deutschland)
  • europe-west6 (Zürich, Schweiz) Blattsymbol Niedriger CO2-Wert
  • northamerica-northeast1 (Montreal) Blattsymbol Niedriger CO2-Wert
  • northamerica-northeast2 (Toronto)
  • southamerica-east1 (Sao Paulo, Brasilien) Blattsymbol Niedriger CO2-Wert
  • us-west2 (Los Angeles)
  • us-west3 (Salt Lake City)
  • us-west4 (Las Vegas)

Wenn Sie bereits einen Cloud Run-Dienst erstellt haben, können Sie die Region in der Cloud Console im Cloud Run-Dashboard aufrufen.

Das war's auch schon! Sie haben gerade ein Container-Image aus Quellcode in Cloud Run bereitgestellt. Cloud Run skaliert das Container-Image automatisch horizontal aus, damit die empfangenen Anfragen bearbeitet werden können, und skaliert es wieder herunter, wenn der Bedarf sinkt. Es fallen nur Kosten für die CPU-, Arbeitsspeicher- und Netzwerkressourcen an, die während der Anfrageverarbeitung verbraucht werden.

Bereinigen

Testprojekt entfernen

Während für Cloud Run keine Kosten anfallen, wenn der Dienst nicht verwendet wird, wird Ihnen dennoch das Speichern des Container-Images in Artifact Registry möglicherweise in Rechnung gestellt. Sie können Ihr Image löschen oder Ihr Cloudprojekt löschen, um Kosten zu vermeiden. Durch das Löschen des Cloudprojekts wird die Abrechnung für alle in diesem Projekt verwendeten Ressourcen beendet.

  1. Wechseln Sie in der Cloud Console zur Seite Ressourcen verwalten.

    Zur Seite „Ressourcen verwalten“

  2. Wählen Sie in der Projektliste das Projekt aus, das Sie löschen möchten, und klicken Sie dann auf Löschen.
  3. Geben Sie im Dialogfeld die Projekt-ID ein und klicken Sie auf Shut down (Beenden), um das Projekt zu löschen.

Nächste Schritte

Weitere Informationen dazu, wie Sie einen Container aus Quellcode erstellen und diesen mithilfe eines Push-Vorgangs in ein Repository übertragen, finden Sie unter: