DéveloppeurWeb.Com
    DéveloppeurWeb.Com
    • Agile Zone
    • AI Zone
    • Cloud Zone
    • Database Zone
    • DevOps Zone
    • Integration Zone
    • Web Dev Zone
    DéveloppeurWeb.Com
    Home»Java Zone»Fast Spring Boot AWS Lambdas avec GraalVM
    Java Zone

    Fast Spring Boot AWS Lambdas avec GraalVM

    novembre 13, 2021
    Fast Spring Boot AWS Lambdas avec GraalVM
    Share
    Facebook Twitter Pinterest Reddit WhatsApp Email

    Dans mon article de blog précédent, j’ai expliqué comment prendre une application Java Spring Boot et la convertir en une fonction sans serveur, qui peut être exécutée dans AWS Lambda.

    Quiconque a déjà fait cela sait que les démarrages à froid sont un gros inconvénient – Java et Spring Boot ne sont pas connus pour leurs temps de démarrage rapides, et un lambda typique converti par Spring Boot peut prendre de 10 à 90 secondes selon la quantité de mémoire et CPU vous l’allouez. Cela peut vous obliger à les sur-approvisionner pour compenser les démarrages à froid, mais c’est un marteau assez cher. Il y a toujours une simultanéité provisionnée, mais cela ne fonctionne pas beaucoup moins cher non plus (et annule l’évolutivité réactive de lambda car vous devez anticiper le nombre dont vous aurez besoin à l’avance).

    Mais et si je vous disais que la même fonction pourrait démarrer à partir d’un démarrage à froid en 3 secondes ? Par rapport à d’autres langages, c’est encore un peu lent, mais étant donné les temps de démarrage comparables des pots Sprint Boot dans des conteneurs ou Lambda, c’est assez révolutionnaire. Et c’est possible grâce à GraalVM.

    Démarrage à froid vs allocation de mémoire

    GraalVM a gagné beaucoup de terrain au cours des deux dernières années – il nous permet de créer des binaires spécifiques à la plate-forme qui peuvent être exécutés directement sans avoir besoin d’une JVM, et avec cela, nous pouvons accélérer le temps de démarrage à froid de notre les fonctions. Il en est encore à ses balbutiements, mais à un moment où il existe une communauté forte et où de nombreux problèmes courants auxquels vous êtes confrontés peuvent être résolus avec un peu de Google-fu.

    Dans cet article, je vais vous montrer comment vous pouvez prendre un exemple d’application REST du monde réel (Spring Petclinic) adapté à spring-cloud-function, et accélérer considérablement le temps de démarrage à froid en utilisant GraalVM, tout en réduisant l’empreinte mémoire/CPU.

    Je vais travailler sur mon exemple GitHub que j’ai mis en place, n’hésitez pas à suivre et à emprunter à vos propres fins.

    Avis de non-responsabilité – au moment de la rédaction de cet article, GraalVM est encore en version bêta et vous pouvez rencontrer d’autres problèmes en plus de ceux documentés ici. La discrétion et la considération sont conseillées si vous adoptez cette approche pour les charges de travail de production.

    Passer à GraalVM

    Pour commencer, j’ai suivi le guide Spring sur la façon de démarrer avec GraalVM.

    L’astuce consiste à optimiser le temps de construction autant que possible. Plus vous pouvez pousser pour construire l’initialisation du temps, mieux c’est. Par défaut, Spring Native initialise toutes les classes au moment de l’exécution (ce qui n’offre pas beaucoup d’avantages par rapport à la combinaison JVM avec JIT habituelle), mais vous pouvez déclarer explicitement les classes qui doivent être initialisées au moment de la construction.

    Il y a un bon article ici qui parle de ce comportement par défaut et comment déterminer quelles classes sont candidates pour l’initialisation au moment de la génération. Spring Native simplifie cela de manière significative, car il connaît déjà toutes les classes du framework Spring pouvant être initialisées au démarrage et configure le native-image construire en conséquence.

    GraalVM est assez compatible avec Spring et Spring Boot, cependant, il existe une liste connue de problèmes entre les deux qui, espérons-le, seront corrigés au fil du temps, méritent d’être connus maintenant car ils sont susceptibles de vous faire trébucher. J’ai dressé une liste des problèmes que j’ai rencontrés en cours de route – il existe des moyens de contourner ces problèmes, mais ils peuvent ne pas fonctionner pour toutes les applications.

    Pour commencer, il y a quelques dépendances et plugins à ajouter au pom.xml qui permet l’utilisation de GraalVM.

    Ceci est basé sur mon article précédent qui montre comment porter une application Spring Boot vers Lambda, je n’inclurai donc pas ces détails ici. Vous pouvez voir mon pom complet ici mais plus précisément, il s’agit d’ajouter ce qui suit au lambda profil:

                <properties>
                    ...
                    <repackage.classifier>exec</repackage.classifier>
                </properties>
                 ...   
                 <dependency>
                        <groupId>org.springframework.experimental</groupId>
                        <artifactId>spring-native</artifactId>
                        <version>0.10.3</version>
                    </dependency>
                </dependencies>
                ...
                <plugin>
                            <groupId>org.springframework.experimental</groupId>
                            <artifactId>spring-aot-maven-plugin</artifactId>
                            <version>0.10.3</version>
                            <executions>
                                <execution>
                                    <id>test-generate</id>
                                    <goals>
                                        <goal>test-generate</goal>
                                    </goals>
                                </execution>
                                <execution>
                                    <id>generate</id>
                                    <goals>
                                        <goal>generate</goal>
                                    </goals>
                                </execution>
                            </executions>
                        </plugin>
                        <plugin>
                            <groupId>org.hibernate.orm.tooling</groupId>
                            <artifactId>hibernate-enhance-maven-plugin</artifactId>
                            <version>5.4.30.Final</version>
                            <executions>
                                <execution>
                                    <configuration>
                                        <failOnError>true</failOnError>
                                        <enableLazyInitialization>true</enableLazyInitialization>
                                        <enableDirtyTracking>true</enableDirtyTracking>
                                        <enableAssociationManagement>true</enableAssociationManagement>
                                        <enableExtendedEnhancement>false</enableExtendedEnhancement>
                                    </configuration>
                                    <goals>
                                        <goal>enhance</goal>
                                    </goals>
                                </execution>
                            </executions>
                        </plugin>
                        <plugin>
                            <groupId>org.apache.maven.plugins</groupId>
                            <artifactId>maven-deploy-plugin</artifactId>
                            <configuration>
                                <skip>true</skip>
                            </configuration>
                        </plugin>
                        <plugin>
                            <groupId>org.springframework.boot</groupId>
                            <artifactId>spring-boot-maven-plugin</artifactId>
                            <configuration>
                                <classifier>${repackage.classifier}</classifier>
                            </configuration>
                        </plugin>
                        <plugin>
                            <groupId>org.graalvm.buildtools</groupId>
                            <artifactId>native-maven-plugin</artifactId>
                            <version>0.9.4</version>
                            <executions>
                                <execution>
                                    <goals>
                                        <goal>build</goal>
                                    </goals>
                                    <phase>package</phase>
                                </execution>
                                <execution>
                                    <id>test</id>
                                    <goals>
                                        <goal>test</goal>
                                    </goals>
                                    <phase>test</phase>
                                </execution>
                            </executions>
                            <configuration>
                                <buildArgs>
                                    --enable-url-protocols=http
                                    -H:+AddAllCharsets
                                </buildArgs>
                            </configuration>
                        </plugin>
                        <plugin>
                            <artifactId>maven-assembly-plugin</artifactId>
                            <executions>
                                <execution>
                                    <id>native-zip</id>
                                    <phase>package</phase>
                                    <goals>
                                        <goal>single</goal>
                                    </goals>
                                    <inherited>false</inherited>
                                </execution>
                            </executions>
                            <configuration>
                                <descriptors>
                                    <descriptor>src/assembly/native.xml</descriptor>
                                </descriptors>
                            </configuration>
                        </plugin>

    Il y a quelques points à mentionner concernant la configuration ci-dessus :

    • hibernate-enhance-maven-plugin – cela permet à Hibernate d’optimiser une grande partie de ce qu’il fait au moment de la construction, pour réduire le temps de démarrage. Ne doit pas nécessairement être utilisé avec Lambda ou GraalVM – vous pouvez également l’utiliser sur des applications standard
    • spring-boot-maven-plugin – la propriété classifier empêche Spring Boot d’écraser le fichier jar utilisé par le native-image outil avec le Spring Boot Uber Jar, qui n’est pas compatible
    • native-maven-plugin – c’est là que toute la magie se produit que je reviendrai plus en détail plus tard. Une partie importante de ceci est dans le <configuration>, qui vous permet de contrôler divers aspects du processus de création d’image native.
    • maven-assembly-plugin – ceci est utilisé pour prendre le binaire que nous allons créer et encapsuler dans une archive zip avec un script d’amorçage utilisé par AWS Lambda

    C’est la majeure partie de la configuration dont vous avez besoin pour prendre votre fonction Spring-Cloud (ou l’application Spring Boot standard d’ailleurs) et générer un binaire natif à partir de celle-ci. L’étape suivante consiste à exécuter une commande de package Maven pour lancer cela. Si vous êtes comme moi, vous voudrez exécuter le processus de construction dans un conteneur Docker qui a déjà Java et GraalVM préconfigurés. C’est l’image et la commande que j’ai utilisées pour monter mon code d’application et .m2 répertoire dans un conteneur :

    docker run -v $(pwd):/petclinic -v ~/.m2:/root/.m2 -it --name petclinic-graalvm ghcr.io/graalvm/graalvm-ce:latest bash

    Une fois dans ce conteneur, vous pouvez ensuite exécuter ce qui suit pour déclencher une génération (skipTests est purement ici du point de vue de la vitesse, non recommandé pour votre application !) :

    ./mvnw clean package -D skipTests -P lambda

    Le premier problème (beaucoup plus documenté à la fin) que j’ai rencontré est que Devtools n’est pas encore pris en charge :

    Première erreur d'exécution

    Si vous utilisez Devtools, vous devez soit le supprimer, soit le déplacer dans un profil distinct que vous désactivez conditionnellement lors de la création de vos fichiers binaires, devrait ressembler à quelque chose de similaire à ceci :

    <!--    <dependency>-->
    <!--      <groupId>org.springframework.boot</groupId>-->
    <!--      <artifactId>spring-boot-devtools</artifactId>-->
    <!--      <optional>true</optional>-->
    <!--    </dependency>-->

    Avec une autre exécution de la commande Maven ci-dessus et une tasse de thé, la construction se termine avec succès :

    Exécution de la commande Maven et d'une tasse de thé

    Donc à ce stade, nous avons un binaire compilé, jusqu’ici tout va bien ! Le compromis pour avoir un binaire optimisé est des temps de construction plus longs, cependant, je pense que cela est acceptable étant donné les temps de démarrage à froid rapides qu’il offre (et il existe des moyens d’accélérer ce processus, comme la construction des binaires sur des agents de construction puissants mais éphémères) .

    À ce stade, nous avons un binaire, sans aucun moyen de l’exécuter dans AWS Lambda. Ce n’est pas un fichier jar, nous ne pouvons donc plus simplement le télécharger et dire à Lambda de l’exécuter dans un environnement d’exécution Java.

    Utilisation d’un environnement d’exécution personnalisé

    Prochain…

    Share. Facebook Twitter Pinterest LinkedIn WhatsApp Reddit Email
    Add A Comment

    Leave A Reply Cancel Reply

    Catégories

    • Politique de cookies
    • Politique de confidentialité
    • CONTACT
    • Politique du DMCA
    • CONDITIONS D’UTILISATION
    • Avertissement
    © 2023 DéveloppeurWeb.Com.

    Type above and press Enter to search. Press Esc to cancel.