[DOCKER] S'entendre avec les conteneurs Java dans Cloud Run

introduction

Ceci est l'article du 12ème jour du Calendrier de l'Avent NewsPicks 2019. Hier, c'était "La nouvelle équipe a utilisé l'asana avec une certaine ingéniosité" de @ ncken.

Cette fois, je vais essayer différentes choses en utilisant Cloud Run de GCP, qui est récemment devenu GA, et des applications Java, qui étaient auparavant considérées comme incompatibles avec les conteneurs.

Qu'est-ce que Cloud Run?

Un service qui vous permet d'exécuter des conteneurs HTTP sans état dans un environnement entièrement géré ou un cluster GKE. C'est similaire à Fargate dans AWS, mais je pense qu'il est de plus en plus facile de configurer et de gérer des conteneurs HTTP.

Je pense que la plus grande différence entre un environnement entièrement géré et GKE est qu'il n'y a pas de coûts fixes. Lors de l'exécution sur GKE, il y a toujours un coût pour les GCE qui composent le cluster. D'autre part, dans un environnement entièrement géré, vous serez facturé pour le temps que le conteneur est opérationnel. En fonction de l'application, celle qui est la plus rentable sera différente, vous devrez donc la calculer vous-même.

D'autres différences sont que vous pouvez modifier le type de machine lors de l'exécution sur GKE, et il existe des différences telles que l'accès ou non au VPC, alors vérifiez les contraintes autres que le coût et sélectionnez l'environnement optimal.

Cette fois, je ne vais pas établir de lien avec d'autres services et je souhaite réduire les coûts, je décrirai donc Cloud Run dans un environnement entièrement géré.

À propos de la compatibilité entre Java et les conteneurs

Récemment, je pense que les opportunités d'exécuter des applications telles que les micro-services et le serverless se multiplient. Dans ces environnements, il existe de nombreux cas où l'opération est différente de l'application Web conventionnelle qui est toujours en cours d'exécution, comme la mise à l'échelle à l'aide d'un conteneur ou son démarrage temporaire. Ici, je voudrais écrire brièvement sur la compatibilité entre Java et les conteneurs.

JVM et conteneur

Lors de l'utilisation de la JVM, je pense que la compatibilité avec le conteneur n'est pas si bonne pour les raisons suivantes.

  1. Les applications JVM démarrent lentement
  2. Les applications JVM utilisent beaucoup de ressources

Cependant, il existe un nombre croissant de cas où le serveur est mis à l'échelle automatiquement en fonction de la charge, tels que les micro-services et sans serveur. Dans de tels moments, Java, qui présente ces deux caractéristiques, présente des inconvénients majeurs par rapport aux autres langages.

Cloud Run, qui fait l'objet de cet article, est également une application sans serveur sur GKE ou dans un environnement entièrement géré. Par conséquent, il est nécessaire de concevoir pour éliminer ces inconvénients afin de gérer les applications JVM sur Cloud Run. C'est là qu'intervient GraalVM.

GraalVM et conteneur

GraalVM est un runtime multilingue annoncé par Oracle en 2018. graalvm_architecture.png

GraalVM peut être utilisé lors de la construction de Java pour créer une image native exécutable instantanément par compilation AOT.

Il est important de noter que GraalVM lance la JVM plus rapidement et utilise relativement moins de ressources, mais ce n'est en aucun cas plus rapide. Dans certains cas, il peut être préférable d'utiliser la JVM, vous devez donc faire un choix en fonction de l'endroit où vous souhaitez l'utiliser. Cette fois, nous utiliserons GraalVM pour l'optimiser pour les conteneurs et sans serveur.

Créer un service

Créons maintenant le service. Pour utiliser GraalVM, nous utiliserons cette fois un framework appelé Quarkus. Quarkus est un framework natif Kubernetes personnalisé pour GraalVM et HotSpot qui prend actuellement en charge Java et Kotlin. La version officielle de ce Quarkus est également sortie le 25 novembre 2019. Par conséquent, il y a encore des fonctionnalités qui ne sont pas suffisantes par rapport à d'autres frameworks tels que Spring, mais je pense que des fonctionnalités seront ajoutées petit à petit dans le futur.

Créer un projet

Commençons par créer une application Java à exécuter. La commande suivante crée un projet Quarkus.

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

Créer Dockerfile pour la construction

Vient ensuite la création du Dockerfile. Lorsque vous créez un projet, un Dokcerfile pour exécuter jvm et un Dockerfile pour exécuter une image native sont automatiquement générés. Cependant, si vous souhaitez utiliser ce Dockerfile, vous devez le créer dans l'environnement local, donc créez un Dockerfile qui sera généré lorsque vous créez l'image du conteneur. (Celui créé cette fois-ci a été créé en référence à celui du document officiel Quarkus.)

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"]

Générez et exécutez cette image pour lancer l'application.

CI/CD Ensuite, nous préparerons l'environnement CI / CD. Cette fois, j'utiliserai Cloud Build.

Création de cloudbuild.yml

Dans Cloud Build, chaque étape de compilation est décrite en yml ou json. Cette fois, je décrirai les étapes suivantes.

  1. Construction de l'image Docker
  2. Poussée d'image Docker
  3. déployer sur Cloud Run

Le fichier yml réel ressemble à ceci:

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: './'

Les gcr.io / cloud-builders / docker et gcr.io / cloud-builders / docker qui apparaissent ici sont appelés cloud builders et sont préconstruits auxquels vous pouvez vous référer dans les étapes de construction pour l'exécution de la tâche. C'est une image. Ceux-ci sont fournis par Cloud Build et d'autres communautés. Cette fois, j'utilise les images docker et gcloud, mais il existe de nombreuses autres images telles que kubectl et gradle.

Créer un déclencheur

Configurez la tâche Cloud Build pour qu'elle s'exécute lorsqu'une validation se produit dans master. Lorsque vous activez la gâchette, vous pouvez visualiser l'état de chaque étape à partir de l'écran comme indiqué ci-dessous.

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

Si vous essayez de développer une fonction appropriée et de fusionner la demande d'extraction, la génération démarre.

Contrôle de fonctionnement

Je vérifierai le temps de réponse du service créé en dernier et le fonctionnement de l'autoscale.

Cette fois, j'utiliserai Gatling, qui est un outil de test réalisé par Scala. Avec Gatling, les résultats des tests sont générés au format HTML, vous pouvez donc facilement saisir les résultats.

Bien qu'il soit possible d'acquérir les métriques suivantes sur GCP, nous avons adopté Gatling cette fois car nous voulions également acquérir les données du côté client. スクリーンショット 2019-12-11 2.47.10.png

Création de scénario de test Gatling

Tout d'abord, créez un projet sbt vide et ajoutez les bibliothèques à plugins.sbt, build.sbt. Ensuite, créez et exécutez le code suivant.

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

Une brève description du code consisterait à charger progressivement l'application Cloud Run déployée sur une période de 10 minutes, avec une charge maximale de 150 rps. Cette fois, nous n'accédons qu'à un seul point de terminaison, mais il est possible de charger plusieurs points de terminaison.

résultats de test

Maintenant, déplaçons réellement le scénario créé ci-dessus.

Temps de réponse

Commençons par vérifier le temps de réponse. スクリーンショット 2019-12-11 2.39.33.png D'après ce que je peux voir sur ce graphique à barres, la plupart des demandes ont reçu une réponse dans les 800 ms.

Échelle automatique

Ensuite, vérifions la mise à l'échelle automatique. スクリーンショット 2019-12-11 2.34.35.png La ligne orange représente rps et le graphique de surface empilée représente le temps de réponse. En regardant cela, il y a certaines parties où le temps de réponse est lent. Cela est peut-être dû à un démarrage à froid. Cependant, même s'il est démarré à froid, la plupart des demandes sont renvoyées dans les 2 secondes, il peut donc être possible de supporter l'opération de service.

À la fin

Quand je touche Cloud Run, je me demande s'il y a des situations où il est nécessaire de lancer plusieurs processus dans un conteneur parce que la communication entre les conteneurs n'est pas possible, ou parce qu'il n'est pas possible de se connecter au VPC, il y a des restrictions sur ce qui peut être fait et cela touche le mur. pense. Cependant, il est très facile de lancer une application Web, alors si vous en avez la possibilité, pourquoi ne pas en faire l'une de vos options?

Une dernière note. Il y a un risque de frais pour les éléments créés cette fois, alors assurez-vous de les supprimer à la fin.

Demain, c'est Tegoshi de NewsPicks, «Signer avec Apple» de @ kohei1218. impatient de!

Article de référence

Recommended Posts

S'entendre avec les conteneurs Java dans Cloud Run
Java EE sans serveur à partir de Quarkus et Cloud Run
Exécuter un lot avec docker-compose avec Java batch
[Java] Obtenir KClass en Java [Kotlin]
Exécuter Java VM avec Web Assembly
Analyse morphologique en Java avec Kuromoji
[Pour les débutants] Exécutez Selenium sur Java
Exécuter des applications Java dans Azure Batch
Obtenir des valeurs de carte nulles en Java
Utiliser Java 11 avec Google Cloud Functions
Restez coincé dans un Java Primer
Exécuter des applications écrites en Java8 en Java6
Exécuter un processus externe en Java
Obtenez le résultat de POST en Java
[Java] Obtenir des éléments List / Map avec Iterator
Méthode de concurrence en Java avec exemple de base
Contactez Eclipse MicroProfile Health
Développement Java avec Codenvy: Hello World! Run
Lire le fichier xlsx en Java avec Selenium
Comment obtenir la date avec Java
Obtenir l'historique du serveur Zabbix en Java
Diviser une chaîne avec ". (Dot)" en Java
Gérez d'énormes JSON avec Java Lambda
Obtenez des horodatages avec le SDK Java Azure BlobStorage
Essayez le débogage à distance Java avec des conteneurs distants dans Visual Studio Code Insiders
[Java] Obtenez la date avec la classe LocalDateTime
Exécuter Rust depuis Java avec JNA (Java Native Access)
Lire une chaîne dans un fichier PDF avec Java
Créer un CSR avec des informations étendues en Java
Outil GUI refactorisé réalisé avec Java8 + JavaFX en 2016
Analyse de code statique par Checkstyle avec Java + Gradle
Résumé Il n'y a rien de particulier. Dois-je y prêter attention? .. .. J'ai ressenti cela, alors j'ai écrit un article. Si vous avez des commentaires, veuillez. Obtenez des informations de bloc avec le SDK Java de Python, Python3, Glossy Hyperledger Iroha
Comment obtenir une classe depuis Element en Java
Extraction de texte en Java à partir de PDF avec pdfbox-2.0.8
Obtenez unixtime (secondes) de ZonedDateTime dans Scala / Java
En utilisant Gradle avec VSCode, compilez Java → exécutez
[Java] Obtenez des images avec l'API Google Custom Search
Bibliothèque "OSHI" pour acquérir des informations système avec Java
Entraînez-vous à travailler avec des paires de substitution Unicode en Java
[JAVA] [Spring] [MyBatis] Utiliser IN () avec SQL Builder
[Débutant] Installez l'outil de développement java dans l'environnement de développement cloud9.
[LeJOS] Obtenez la valeur du capteur EV3 à distance avec Java
Crypter / décrypter avec AES256 en PHP et Java
Programmation utilisant le type de somme directe en Java (news)
Partition en Java
Changements dans Java 11
Janken à Java
Taux circonférentiel à Java
FizzBuzz en Java
Obtenez l'URL de la destination de la redirection HTTP en Java
Comment appeler des fonctions en bloc avec la réflexion Java
[Tutoriel] Télécharger Eclipse → Lancer l'application avec Java (Pléiades)
Les utilisateurs Java expérimentés se lancent dans le développement d'applications Android
[Mon mémo] Entendons-nous bien avec Pry / DB avec Rails
Inclure l'image dans le fichier jar avec la méthode statique java
[Tutoriel] Télécharger Eclipse → Lancer l'application Web avec Java (Pléiades)