[DOCKER] Kommen Sie mit Java-Containern in Cloud Run zurecht

Einführung

Dies ist der Artikel zum 12. Tag von NewsPicks Adventskalender 2019. Gestern war @ nckens "Das neue Team hat Asana mit etwas Einfallsreichtum eingesetzt".

Dieses Mal werde ich verschiedene Dinge ausprobieren, indem ich den kürzlich zu GA gewordenen Cloud Run von GCP und Java-Anwendungen verwende, von denen zuvor gesagt wurde, dass sie nicht mit Containern kompatibel sind.

Was ist Cloud Run?

Ein Dienst, mit dem Sie zustandslose HTTP-Container in einer vollständig verwalteten Umgebung oder einem GKE-Cluster ausführen können. Es ähnelt Fargate in AWS, aber ich denke, es ist immer einfacher, HTTP-Container einzurichten und zu verwalten.

Ich denke, der größte Unterschied zwischen einer vollständig verwalteten Umgebung und GKE besteht darin, dass keine Fixkosten anfallen. Bei der Ausführung mit GKE fallen für die GCEs, aus denen der Cluster besteht, immer Kosten an. In einer vollständig verwalteten Umgebung wird Ihnen jedoch die Zeit in Rechnung gestellt, in der der Container betriebsbereit ist. Welches kostengünstiger ist, hängt von der Anwendung ab. Sie müssen es also selbst berechnen.

Andere Unterschiede bestehen darin, dass Sie den Maschinentyp ändern können, wenn Sie mit GKE arbeiten, und es gibt einige Unterschiede, z. B. ob Sie Zugriff auf die VPC haben oder nicht. Überprüfen Sie daher andere Einschränkungen als die Kosten und wählen Sie die optimale Umgebung aus.

Dieses Mal möchte ich die Kosten niedrig halten, ohne eine Verknüpfung mit anderen Diensten herzustellen. Daher werde ich Cloud Run in einer vollständig verwalteten Umgebung beschreiben.

Informationen zur Kompatibilität zwischen Java und Containern

In letzter Zeit denke ich, dass die Möglichkeiten, Anwendungen wie Mikrodienste und Serverless auszuführen, zunehmen. In diesen Umgebungen gibt es viele Fälle, in denen sich der Vorgang von der herkömmlichen Webanwendung unterscheidet, die immer ausgeführt wird, z. B. das Skalieren mithilfe eines Containers oder das vorübergehende Starten. Hier möchte ich kurz auf die Kompatibilität zwischen Java und Containern eingehen.

JVM und Container

Bei der Verwendung der JVM denke ich, dass die Kompatibilität mit dem Container aus den folgenden Gründen nicht so gut ist.

  1. JVM-Anwendungen werden langsam gestartet
  2. JVM-Anwendungen verbrauchen viele Ressourcen

Es gibt jedoch eine zunehmende Anzahl von Fällen, in denen der Server entsprechend der Auslastung automatisch skaliert wird, z. B. Mikrodienste und serverlos. In solchen Zeiten hat Java, das diese beiden Eigenschaften aufweist, im Vergleich zu anderen Sprachen große Nachteile.

Cloud Run, das Gegenstand dieses Artikels ist, ist auch eine serverlose Anwendung auf GKE oder in einer vollständig verwalteten Umgebung. Daher müssen diese Nachteile beseitigt werden, um JVM-Anwendungen in Cloud Run verarbeiten zu können. Hier kommt GraalVM ins Spiel.

GraalVM und Container

GraalVM ist eine mehrsprachige Laufzeit, die Oracle 2018 angekündigt hat. graalvm_architecture.png

GraalVM kann beim Erstellen von Java verwendet werden, um durch AOT-Kompilierung ein sofort ausführbares natives Image zu erstellen.

Es ist wichtig zu beachten, dass GraalVM die JVM schneller startet und relativ weniger Ressourcen verbraucht, aber keineswegs schneller. In einigen Fällen ist es möglicherweise besser, die JVM zu verwenden. Sie müssen daher eine Auswahl treffen, je nachdem, wo Sie sie verwenden möchten. Dieses Mal werden wir GraalVM verwenden, um es für Container und Serverless zu optimieren.

Einen Service erstellen

Jetzt erstellen wir den Service. Um GraalVM zu verwenden, verwenden wir dieses Mal ein Framework namens Quarkus. Quarkus ist ein natives Kubernetes-Framework, das für GraalVM und HotSpot angepasst wurde und derzeit Java und Kotlin unterstützt. Die offizielle Version dieses Quarkus wurde ebenfalls am 25. November 2019 veröffentlicht. Daher gibt es immer noch einige Funktionen, die im Vergleich zu anderen Frameworks wie Spring nicht ausreichen, aber ich denke, dass Funktionen in Zukunft nach und nach hinzugefügt werden.

Ein Projekt erstellen

Zuerst erstellen wir eine Java-Anwendung, die ausgeführt werden soll. Der folgende Befehl erstellt ein Quarkus-Projekt.

mvn io.quarkus:quarkus-maven-plugin:1.0.1.Final:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=getting-started \
    -DclassName="org.acme.quickstart.GreetingResource" \
    -Dpath="/hello"

Erstellen Sie eine Docker-Datei zum Erstellen

Als nächstes wird die Docker-Datei erstellt. Wenn Sie ein Projekt erstellen, werden automatisch eine Dokcer-Datei zum Ausführen von jvm und eine Docker-Datei zum Ausführen des nativen Images generiert. Wenn Sie diese Docker-Datei jedoch verwenden möchten, müssen Sie sie in der lokalen Umgebung erstellen. Erstellen Sie daher eine Docker-Datei, die beim Erstellen des Images des Containers erstellt wird. (Die diesmal erstellte wurde unter Bezugnahme auf die im offiziellen Quarkus-Dokument erstellt.)

Dockerfile.multistage


## Stage 1 : build with maven builder image with native capabilities
FROM quay.io/quarkus/centos-quarkus-maven:19.2.1 AS build
COPY ./ /usr/src/app
USER root
RUN chown -R quarkus /usr/src/app
USER quarkus
RUN mvn -f /usr/src/app/pom.xml -Pnative clean package

## Stage 2 : create the docker final image
FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY --from=build /usr/src/app/target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
ENV DISABLE_SIGNAL_HANDLERS true
CMD ["./application", "-Dquarkus.http.host=0.0.0.0", "-Dquarkus.http.port=8080"]

Erstellen Sie dieses Image und führen Sie es aus, um die Anwendung zu starten.

CI/CD Als nächstes bereiten wir die CI / CD-Umgebung vor. Dieses Mal werde ich [Cloud Build] verwenden (https://cloud.google.com/cloud-build/docs/).

Cloudbuild.yml erstellen

In Cloud Build wird jeder Build-Schritt in yml oder json beschrieben. Dieses Mal werde ich die folgenden Schritte beschreiben.

  1. Docker Image Build
  2. Docker Image Push
  3. In Cloud Run bereitstellen

Die eigentliche yml-Datei sieht folgendermaßen aus:

cloudbuild.yml


steps:
  - name: 'gcr.io/cloud-builders/docker'
    id: 'Build Image'
    args: ['build', '-t', 'asia.gcr.io/{MyProject}/quarkus-example', '.', '-f', './src/main/docker/Dockerfile.multistage']
    dir: './'

  - name: 'gcr.io/cloud-builders/docker'
    id: 'Push to Container Registry'
    args: ['push', 'asia.gcr.io/{MyProject}/quarkus-example']
    dir: './'

  - name: 'gcr.io/cloud-builders/docker'
    id: 'Deploy to Cloud Run'
    args: ['beta', 'run', 'deploy', 'quarkus-example', '--image', 'asia.gcr.io/{MyProject}/quarkus-example', '--platform', 'managed', '--region', 'asia-northeast1', '--allow-unauthenticated']
    dir: './'

Die hier angezeigten gcr.io / cloud-builders / docker und gcr.io / cloud-builders / docker werden als Cloud-Builder bezeichnet und sind vorgefertigt, auf die Sie in den Erstellungsschritten für die Aufgabenausführung verweisen können. Es ist ein Bild. Diese werden von Cloud Build und anderen Communitys bereitgestellt. Dieses Mal verwende ich die Docker- und Gcloud-Bilder, aber es gibt viele andere Bilder wie Kubectl und Gradle.

Auslöser erstellen

Legen Sie fest, dass der Cloud Build-Job ausgeführt wird, wenn im Master ein Commit ausgeführt wird. Wenn Sie den Auslöser betätigen, können Sie den Status jedes Schritts auf dem Bildschirm wie unten gezeigt anzeigen.

スクリーンショット 2019-12-09 23.57.21.png

Wenn Sie versuchen, eine geeignete Funktion zu entwickeln und die Pull-Anforderung zusammenzuführen, wird der Build gestartet.

Funktionsprüfung

Ich werde die Antwortzeit des zuletzt erstellten Dienstes und den Betrieb der automatischen Skalierung überprüfen.

Dieses Mal werden wir Scalas Testtool Gatling verwenden. Mit Gatling werden die Testergebnisse als HTML ausgegeben, sodass Sie die Ergebnisse leicht erfassen können.

Obwohl es möglich ist, die folgenden Metriken in GCP zu erfassen, haben wir dieses Mal Gatling übernommen, weil wir auch Daten von der Client-Seite erfassen wollten. スクリーンショット 2019-12-11 2.47.10.png

Erstellung eines Gatling-Testszenarios

Erstellen Sie zunächst ein leeres sbt-Projekt und fügen Sie die Bibliotheken zu plugins.sbt, build.sbt hinzu.

plugins.sbt


addSbtPlugin("io.gatling" % "gatling-sbt" % "3.1.0")

build.sbt


libraryDependencies += "io.gatling.highcharts" % "gatling-charts-highcharts" % "3.3.1" % "test,it"
libraryDependencies += "io.gatling"            % "gatling-test-framework"    % "3.3.1" % "test,it"

BasicSimulation.scala


package computerdatabase

import io.gatling.core.Predef._
import io.gatling.core.structure.ScenarioBuilder
import io.gatling.http.Predef._
import io.gatling.http.protocol.HttpProtocolBuilder
import scala.concurrent.duration._

class BasicSimulation extends Simulation {

  val url = "your app url"

  val httpProtocol: HttpProtocolBuilder = http
    .baseUrl(url)
    .acceptHeader("text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
    .acceptEncodingHeader("gzip, deflate")
    .acceptLanguageHeader("en-US,en;q=0.5")
    .userAgentHeader("Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:16.0) Gecko/20100101 Firefox/16.0")

  val scn: ScenarioBuilder = scenario("cloud run example scinario")
    .exec(http("cloud run example scinario")
      .get("/"))

  setUp(scn.inject(rampUsersPerSec(1) to (150) during (600 seconds)).protocols(httpProtocol))
}

Eine kurze Codebeschreibung besteht darin, die bereitgestellte Cloud Run-Anwendung über einen Zeitraum von 10 Minuten mit einer maximalen Last von 150 U / min schrittweise zu laden. Dieses Mal greifen wir nur auf einen Endpunkt zu, es ist jedoch möglich, mehrere Endpunkte zu laden. Testergebnis Lassen Sie uns nun das oben erstellte Szenario verschieben. Reaktionszeit Lassen Sie uns zunächst die Reaktionszeit überprüfen. スクリーンショット 2019-12-11 2.39.33.png Soweit ich aus diesem Balkendiagramm ersehen kann, haben die meisten Anfragen innerhalb von 800 ms eine Antwort erhalten.

Automatische Skalierung

Als nächstes überprüfen wir die automatische Skalierung. スクリーンショット 2019-12-11 2.34.35.png Die orange Linie steht für rps und das gestapelte Oberflächendiagramm für die Reaktionszeit. In diesem Zusammenhang gibt es einige Bereiche, in denen die Reaktionszeit langsam ist. Vielleicht liegt das an einem Kaltstart. Selbst wenn es kalt gestartet wird, werden die meisten Anforderungen innerhalb von 2 Sekunden zurückgegeben, sodass es möglicherweise möglich ist, dem Servicevorgang standzuhalten.

Am Ende

Wenn ich Cloud Run berühre, frage ich mich, ob es Situationen gibt, in denen mehrere Prozesse in einem Container gestartet werden müssen, weil keine Kommunikation zwischen Containern möglich ist oder weil keine Verbindung zu VPC hergestellt werden kann. Es gibt Einschränkungen, was getan werden kann, und es trifft die Wand. Überlegen. Es ist jedoch sehr einfach, eine Web-App zu starten. Wenn Sie also die Möglichkeit haben, können Sie sie zu einer Ihrer Optionen machen.

Eine letzte Anmerkung. Es besteht die Gefahr von Gebühren für die diesmal erstellten Elemente. Löschen Sie sie daher am Ende.

Morgen ist Tegoshi von NewsPicks, "Sign with Apple Story" von @ kohei1218. freue mich auf!

Referenzartikel

Recommended Posts

Kommen Sie mit Java-Containern in Cloud Run zurecht
Serverloses Java EE beginnend mit Quarkus und Cloud Run
Führen Sie Batch mit Docker-Compose mit Java-Batch aus
[Java] KClass in Java herunterladen [Kotlin]
Führen Sie Java VM mit Web Assembly aus
Morphologische Analyse in Java mit Kuromoji
[Für Anfänger] Führen Sie Selenium auf Java aus
Führen Sie Java-Anwendungen in Azure Batch aus
Holen Sie sich null-sichere Map-Werte in Java
Verwenden Sie Java 11 mit Google Cloud-Funktionen
Bleiben Sie in einem Java Primer stecken
Führen Sie in Java8 geschriebene Anwendungen in Java6 aus
Führen Sie einen externen Prozess in Java aus
Holen Sie sich das Ergebnis von POST in Java
[Java] Listen- / Kartenelemente mit Iterator abrufen
Parallelitätsmethode in Java mit grundlegendem Beispiel
Nehmen Sie Kontakt mit Eclipse MicroProfile Health auf
Java-Entwicklung mit Codenvy: Hello World! Run
Lesen Sie die xlsx-Datei in Java mit Selenium
So erhalten Sie das Datum mit Java
Abrufen des Verlaufs vom Zabbix-Server in Java
Teilen Sie eine Zeichenfolge in Java mit ". (Dot)"
Behandeln Sie große JSON mit Java Lambda
Holen Sie sich Zeitstempel mit dem Azure BlobStorage Java SDK
Versuchen Sie das Remote-Debuggen von Java mit Remote-Containern in Visual Studio Code Insiders
[Java] Ermitteln Sie das Datum mit der LocalDateTime-Klasse
Führen Sie Rust von Java mit JNA (Java Native Access) aus.
Lesen Sie eine Zeichenfolge in einer PDF-Datei mit Java
Erstellen Sie eine CSR mit erweiterten Informationen in Java
Überarbeitetes GUI-Tool, das 2016 mit Java8 + JavaFX erstellt wurde
Führen Sie eine statische Code-Analyse mit Checkstyle mit Java + Gradle durch
Blockinformationen erhalten Sie mit dem Java SDK von Hyperledger Iroha
So erhalten Sie eine Klasse von Element in Java
Textextraktion in Java aus PDF mit pdfbox-2.0.8
Holen Sie sich Unixtime (Sekunden) von ZonedDateTime in Scala / Java
Erstellen Sie mit Gradle mit VSCode Java → Ausführen
[Java] Holen Sie sich Bilder mit der Google Custom Search API
Bibliothek "OSHI" zum Erfassen von Systeminformationen mit Java
Üben Sie die Arbeit mit Unicode-Ersatzpaaren in Java
[JAVA] [Spring] [MyBatis] Verwenden Sie IN () mit SQL Builder
[Anfänger] Installieren Sie das Java-Entwicklungstool in der Cloud9-Entwicklungsumgebung.
[LeJOS] EV3-Sensorwert per Fernzugriff mit Java abrufen
Verschlüsseln / Entschlüsseln mit AES256 in PHP und Java
Programmierung mit dem direkten Summentyp in Java (Nachrichten)
Partisierung in Java
Änderungen in Java 11
Janken in Java
Umfangsrate in Java
FizzBuzz in Java
Rufen Sie die URL des HTTP-Umleitungsziels in Java ab
Aufrufen von Funktionen in großen Mengen mit Java Reflection
[Tutorial] Eclipse herunterladen → Anwendung mit Java ausführen (Plejaden)
Erfahrene Java-Benutzer beginnen mit der Entwicklung von Android-Apps
[Mein Memo] Lass uns mit Pry / DB mit Rails auskommen
Fügen Sie das Bild mit der statischen Java-Methode in die JAR-Datei ein
[Tutorial] Eclipse herunterladen → Webanwendung mit Java ausführen (Plejaden)