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.
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é.
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.
Lors de l'utilisation de la JVM, je pense que la compatibilité avec le conteneur n'est pas si bonne pour les raisons suivantes.
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 est un runtime multilingue annoncé par Oracle en 2018.
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é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.
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"
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.
Dans Cloud Build, chaque étape de compilation est décrite en yml ou json. Cette fois, je décrirai les étapes suivantes.
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.
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.
Si vous essayez de développer une fonction appropriée et de fusionner la demande d'extraction, la génération démarre.
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.
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.
Maintenant, déplaçons réellement le scénario créé ci-dessus.
Commençons par vérifier le temps de réponse. 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.
Ensuite, vérifions la mise à l'échelle automatique. 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.
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!
Recommended Posts