<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: JHipster</title>
    <description>The latest articles on Forem by JHipster (@jhipster).</description>
    <link>https://forem.com/jhipster</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F5284%2F528a411d-f80d-47e2-b4e0-5fb43fa71691.png</url>
      <title>Forem: JHipster</title>
      <link>https://forem.com/jhipster</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/jhipster"/>
    <language>en</language>
    <item>
      <title>Micro Frontends for Java Microservices</title>
      <dc:creator>Matt Raible</dc:creator>
      <pubDate>Fri, 20 Jan 2023 15:47:47 +0000</pubDate>
      <link>https://forem.com/jhipster/micro-frontends-for-java-microservices-2057</link>
      <guid>https://forem.com/jhipster/micro-frontends-for-java-microservices-2057</guid>
      <description>&lt;p&gt;Microservices have been quite popular in the Java ecosystem ever since Spring Boot and Spring Cloud made them easy to build and deploy. Things have gotten even easier in recent years with the proliferation of new Java frameworks built specifically for microservices: MicroProfile, Micronaut, Quarkus, and Helidon. Not only do these frameworks provide an excellent developer experience, but they also tend to have built-in Docker support. They even work with GraalVM, so you can compile your apps to native code!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;To be fair, MicroProfile isn't a framework; it's a spec with many implementations. In fact, both Quarkus and Helidon have MicroProfile flavors you can use.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Today, I'll show you how to build a Java microservices architecture that leverages micro frontends for the UI. The backend will use Spring Boot and Spring Cloud, while the frontend will use React. You can also use Angular or Vue if you'd like.&lt;/p&gt;

&lt;p&gt;This tutorial is also available &lt;a href="https://youtu.be/haTQ1xJKQQ8" rel="noopener noreferrer"&gt;as a screencast&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/haTQ1xJKQQ8"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;NOTE: This video uses JHipster 7.9.3. All the steps will still work with JHipster 8.1.0, but you have to use Java 17 and Node 18.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://sdkman.io/" rel="noopener noreferrer"&gt;Java&lt;/a&gt; 17+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.com/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; 18+&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.docker.com/products/docker-desktop/" rel="noopener noreferrer"&gt;Docker Desktop&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jhipster.tech/installation/" rel="noopener noreferrer"&gt;JHipster&lt;/a&gt; 8.1.0&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can install JHipster with npm:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; generator-jhipster@8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Java microservices with Spring Boot and JHipster
&lt;/h2&gt;

&lt;p&gt;JHipster is an application generator that creates a Spring Boot backend. You can configure it to use SQL or NoSQL databases, plain ol' Spring MVC, or reactive with WebFlux. It also generates a UI for your REST API and offers you the choice of Angular, React, or Vue. Last year, I showed you how to build &lt;a href="https://developer.okta.com/blog/2021/01/20/reactive-java-microservices" rel="noopener noreferrer"&gt;reactive Java microservices with Spring Boot and JHipster&lt;/a&gt;. In this tutorial, you'll build a gateway with Spring Cloud Gateway. Then, you create a blog microservice and a store microservice, each with its own database.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fioi1fkmrcid5wujypo2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fioi1fkmrcid5wujypo2v.png" alt="JHipster microservices architecture" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This microservices architecture had one major flaw: the UI for it is a monolith, with all its files on the gateway. This isn't good from a loose-coupling point of view because changes in a microservice might require changes in the UI. Instead of being able to deploy the microservice independently, you have to deploy the gateway too.&lt;/p&gt;

&lt;p&gt;Today, I'm proud to show you how you can solve this problem with micro frontends. JHipster recently added support for micro-frontends. Microfrontends provide a way for you to remotely load and execute code at runtime so your microservice's UI can live in the same artifact without being coupled to the gateway!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcwgh7ad81rs8dfz3vkl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frcwgh7ad81rs8dfz3vkl.png" alt="JHipster microfrontends architecture" width="800" height="245"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the previous paragraph, you might notice I spelled micro frontends three different ways. The current literature is all over the place on this one!&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1499098253920460802-810" src="https://platform.twitter.com/embed/Tweet.html?id=1499098253920460802"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1499098253920460802-810');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1499098253920460802&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;I'm going to use "micro frontends" for the remainder of this post since that's what &lt;a href="https://twitter.com/thecamjackson" rel="noopener noreferrer"&gt;Cam Jackson&lt;/a&gt; used in his &lt;a href="https://martinfowler.com/articles/micro-frontends.html" rel="noopener noreferrer"&gt;Micro Frontends article&lt;/a&gt; on Martin Fowler's blog.&lt;/p&gt;

&lt;h2&gt;
  
  
  A quick introduction to Module Federation
&lt;/h2&gt;

&lt;p&gt;Webpack's &lt;a href="https://webpack.js.org/concepts/module-federation/" rel="noopener noreferrer"&gt;Module Federation&lt;/a&gt; is one of the best-known implementations for micro frontends. Its &lt;a href="https://webpack.js.org/plugins/module-federation-plugin" rel="noopener noreferrer"&gt;&lt;code&gt;ModuleFederationPlugin&lt;/code&gt;&lt;/a&gt; allows a build to provide or consume modules with other independent builds at runtime. It even allows you to share libraries between frontends to reduce the size of remote bundles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://twitter.com/ScriptedAlchemy" rel="noopener noreferrer"&gt;Zack Jackson&lt;/a&gt; is the creator of Module Federation and recently &lt;a href="https://twitter.com/ScriptedAlchemy/status/1564411584851505153" rel="noopener noreferrer"&gt;collaborated&lt;/a&gt; with &lt;a href="https://twitter.com/ManfredSteyer" rel="noopener noreferrer"&gt;Manfred Steyer&lt;/a&gt; to create Native Federation. This means you can use micro frontend concepts with any build tool, not just webpack.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1564312149580582912-428" src="https://platform.twitter.com/embed/Tweet.html?id=1564312149580582912"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1564312149580582912-428');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1564312149580582912&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should Java developers care?
&lt;/h2&gt;

&lt;p&gt;I think micro frontends are a fascinating architectural concept. Microservices weave everything together on the backend with protocols like HTTP and gRPC. With micro frontends, it's all HTTP. You can see your app get stitched together by watching your browser's network console and seeing remote modules load.&lt;/p&gt;

&lt;p&gt;I've encountered quite a few monolith UIs in my time as a consultant. The backend was a beautiful microservice architecture, but it was all tightly coupled on the frontend. There's a good chance many Java developers don't care about the UI because they just work on the beautiful backends. However, if you consider yourself a Java &lt;em&gt;web&lt;/em&gt; developer, micro frontends are as revolutionary as HTML5!&lt;/p&gt;

&lt;p&gt;And that's the beauty of this tutorial; you don't have to write any micro frontends. JHipster can create them for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Micro frontends in action with JHipster
&lt;/h2&gt;

&lt;p&gt;I used a JDL (JHipster Domain Language) file named &lt;code&gt;reactive-ms.jdl&lt;/code&gt; to create the reactive Java microservices tutorial I mentioned earlier. You can see this file online in the &lt;a href="https://github.com/jhipster/jdl-samples/blob/main/reactive-ms.jdl" rel="noopener noreferrer"&gt;JDL samples repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I copied this file to &lt;code&gt;reactive-mf.jdl&lt;/code&gt; and changed a few things for this tutorial:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enabled micro frontends by adding &lt;code&gt;microfrontends [blog, store]&lt;/code&gt; to the gateway's definition.&lt;/li&gt;
&lt;li&gt;Changed the client framework from Vue to React.&lt;/li&gt;
&lt;li&gt;Added &lt;code&gt;clientFramework react&lt;/code&gt; and Cypress to each microservice.&lt;/li&gt;
&lt;li&gt;Changed the service discovery to &lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt;, since this is the default in JHipster 8.&lt;/li&gt;
&lt;li&gt;Added a deployment section for Kubernetes.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;CAUTION&lt;/strong&gt;: JHipster's JDL allows you to specify different client frameworks, but micro frontends will only work if you set the same one for all your apps.&lt;/p&gt;

&lt;p&gt;Run the following command to create a new directory for your micro frontends project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;take micro-frontends-jhipster
&lt;span class="c"&gt;# mkdir micro-frontends-jhipster &amp;amp;&amp;amp; cd micro-frontends-jhipster if you don't have take installed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Download both the &lt;code&gt;-ms&lt;/code&gt; and &lt;code&gt;-mf&lt;/code&gt; JDLs and compare them in IntelliJ IDEA. You'll need IDEA's &lt;a href="https://www.jetbrains.com/help/idea/working-with-the-ide-features-from-command-line.html" rel="noopener noreferrer"&gt;Command-line Launcher&lt;/a&gt; for the &lt;code&gt;idea&lt;/code&gt; command to work.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jhipster download reactive-ms.jdl
jhipster download reactive-mf.jdl
idea diff reactive-ms.jdl reactive-mf.jdl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwza4yl2q8w4vc8s1wni.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzwza4yl2q8w4vc8s1wni.png" alt="Reactive microservices refactored to micro frontends" width="800" height="474"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojxazc8v25gx1jbx60xm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fojxazc8v25gx1jbx60xm.png" alt="Additional K8s deployment" width="800" height="279"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: The &lt;a href="https://plugins.jetbrains.com/plugin/19697-jhipster-jdl" rel="noopener noreferrer"&gt;JHipster JDL Plugin&lt;/a&gt; is a handy tool for working with JDL files.&lt;/p&gt;

&lt;h3&gt;
  
  
  Micro frontend options: Angular, React, and Vue
&lt;/h3&gt;

&lt;p&gt;JHipster has support for the big three JavaScript frameworks: Angular, React, and Vue. All are implemented using TypeScript, and a newly generated app should have around 70% code coverage, both on the backend and frontend.&lt;/p&gt;

&lt;p&gt;There is also a &lt;a href="https://github.com/jhipster/generator-jhipster-svelte" rel="noopener noreferrer"&gt;Svelte blueprint&lt;/a&gt;, but it does not support micro frontends at the time of this writing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build Java microservices with Spring Boot and WebFlux
&lt;/h3&gt;

&lt;p&gt;To generate a microservices architecture with micro frontend support, run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jhipster jdl reactive-mf.jdl &lt;span class="nt"&gt;--monorepository&lt;/span&gt; &lt;span class="nt"&gt;--workspaces&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last two arguments are optional, but I expect you to use them for this tutorial. Without the &lt;code&gt;monorepository&lt;/code&gt; flag, the gateway and microservices would have their own Git repos. The &lt;code&gt;workspaces&lt;/code&gt; flag enables &lt;a href="https://docs.npmjs.com/cli/v8/using-npm/workspaces" rel="noopener noreferrer"&gt;npm workspaces&lt;/a&gt;, which are kinda like having an aggregator &lt;code&gt;pom.xml&lt;/code&gt; that allows you to execute commands across projects. It also makes it so there's only one &lt;code&gt;node_modules&lt;/code&gt; in the root directory. To learn more, I recommend egghead's &lt;a href="https://egghead.io/courses/introduction-to-monorepos-with-npm-workspaces-c03f500b" rel="noopener noreferrer"&gt;Introduction to Monorepos with NPM Workspaces&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you want to use Angular, append &lt;code&gt;--client-framework angular&lt;/code&gt; to the command above to override the JDL value:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--client-framework&lt;/span&gt; angular
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather try out Vue, use the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nt"&gt;--client-framework&lt;/span&gt; vue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Run your reactive Spring Boot microservices
&lt;/h3&gt;

&lt;p&gt;When the process is complete, cd into the &lt;code&gt;gateway&lt;/code&gt; directory and start Consul, Keycloak, and PostgreSQL using Docker Compose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;gateway
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; src/main/docker/consul.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; src/main/docker/keycloak.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;
docker compose &lt;span class="nt"&gt;-f&lt;/span&gt; src/main/docker/postgresql.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, run &lt;code&gt;./gradlew&lt;/code&gt; (or &lt;code&gt;npm run app:start&lt;/code&gt; if you prefer npm commands). When the startup process completes, open your favorite browser to &lt;code&gt;http://localhost:8080&lt;/code&gt;, and log in with the credentials displayed on the page.&lt;/p&gt;

&lt;p&gt;You'll be redirected back to the gateway, but the &lt;strong&gt;Entities&lt;/strong&gt; menu won't have any links because the micro frontends it tries to load are unavailable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewtgycilhzwfp1utg3vl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fewtgycilhzwfp1utg3vl.png" alt="The gateway's entities are unavailable" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Start the &lt;code&gt;blog&lt;/code&gt; by opening a terminal and navigating to its directory. Then, start its database with Docker and the app with Gradle.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run docker:db:up
./gradlew
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a new terminal and do the same for the &lt;code&gt;store&lt;/code&gt; microservice.&lt;/p&gt;

&lt;p&gt;You can verify everything is started using Consul at &lt;code&gt;http://localhost:8500&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fes5bt0tky3pt644xkom6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fes5bt0tky3pt644xkom6.png" alt="Consul services" width="800" height="498"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Refresh the gateway app; you should see menu items to navigate to the microservices now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafhai7815q2tyga2a0nl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fafhai7815q2tyga2a0nl.png" alt="Gateway entities available" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Zero turnaround development that sparks joy
&lt;/h3&gt;

&lt;p&gt;At this point, I've only shown you how to run the Spring Boot backends with their packaged React apps. What if you want to work on the UI and have zero turnaround that sparks joy? ✨🤗&lt;/p&gt;

&lt;p&gt;In the gateway app, run &lt;code&gt;npm start&lt;/code&gt;. This command will run the UI on a web server, open a browser window to &lt;code&gt;http://localhost:9000&lt;/code&gt;, and use &lt;a href="https://browsersync.io/" rel="noopener noreferrer"&gt;Browsersync&lt;/a&gt; to keep your browser in sync with your code.&lt;/p&gt;

&lt;p&gt;Modify the code in &lt;code&gt;gateway/src/main/webapp/app/modules/home/home.tsx&lt;/code&gt; to make a quick change. For example, add the following HTML below the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h2&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"text-primary"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  Hi, I'm a quick edit!
&lt;span class="nt"&gt;&amp;lt;/h2&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll see this change immediately appear within your browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfto5k80mfplisndi3of.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvfto5k80mfplisndi3of.png" alt="Gateway quick edit" width="800" height="477"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remove it, and it'll disappear right away too.&lt;/p&gt;

&lt;p&gt;Now, open another terminal and navigate into the &lt;code&gt;store&lt;/code&gt; directory. Run &lt;code&gt;npm start&lt;/code&gt;, and you'll have a similar zero-turnaround experience when modifying files in the &lt;code&gt;store&lt;/code&gt; app. The app will start a webserver on &lt;code&gt;http://localhost:9002&lt;/code&gt;, and there will only be one menu item for product. Modify files in the &lt;code&gt;store/src/main/webapp/app/entities/store/product&lt;/code&gt; directory, and you'll see the changes in your browser immediately. For example, change the wrapper &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; in &lt;code&gt;product.tsx&lt;/code&gt; to have a background color:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;className=&lt;/span&gt;&lt;span class="s"&gt;"bg-info"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The UI will change before you can Command+Tab back to your browser.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5026sm9ff5xepnxc5xq3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5026sm9ff5xepnxc5xq3.png" alt="Store edit" width="800" height="321"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The backend has quick turnaround abilities too, thanks to &lt;a href="https://docs.spring.io/spring-boot/docs/current/reference/html/using.html#using.devtools" rel="noopener noreferrer"&gt;Spring Boot devtools&lt;/a&gt;. If you modify a backend class, recompiling it will cause Spring Boot to reload your component lickety-split. It's pretty slick!&lt;/p&gt;

&lt;h3&gt;
  
  
  A look under the hood of micro frontends
&lt;/h3&gt;

&lt;p&gt;When you're learning concepts like micro frontends, it's often helpful to look at the code that makes things work.&lt;/p&gt;

&lt;p&gt;The gateway's &lt;code&gt;webpack.microfrontend.js&lt;/code&gt; handles specifying the shared dependencies and components between apps. The &lt;code&gt;src/main/webapp/app/shared/layout/menus/entities.tsx&lt;/code&gt; file contains the menu items for each micro frontend.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ModuleFederationPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack/lib/container/ModuleFederationPlugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;packageJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Microfrontend api, should match across gateway and microservices.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sharedDefaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;strictVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shareMappings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sharedDefaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="p"&gt;}]));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shareDependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;skipList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;packageJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;skipList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sharedDefaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;}]),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;serve&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;optimization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;moduleIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;named&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;chunkIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;named&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;runtimeChunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ModuleFederationPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;shareScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;shareDependencies&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;shareMappings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/config/constants&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/config/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/error/error-boundary-routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/layout/menus/menu-components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/layout/menus/menu-item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/reducers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/reducers/locale&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/reducers/reducer.utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/util/date-utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/util/entity-utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;publicPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The blog's &lt;code&gt;webpack.microfrontend.js&lt;/code&gt; looks similar, except that it exposes its &lt;code&gt;remoteEntry.js&lt;/code&gt;, menu items, and routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ModuleFederationPlugin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack/lib/container/ModuleFederationPlugin&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DefinePlugin&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;webpack&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;packageJson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c1"&gt;// Microfrontend api, should match across gateway and microservices.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sharedDefaults&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;singleton&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;strictVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shareMappings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mappings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sharedDefaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;apiVersion&lt;/span&gt; &lt;span class="p"&gt;}]));&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shareDependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;skipList&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;packageJson&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;skipList&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(([&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;sharedDefaults&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;requiredVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;}]),&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;serve&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;optimization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;moduleIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;named&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;chunkIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;named&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;runtimeChunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ModuleFederationPlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;blog&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;remoteEntry.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;shareScope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;exposes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./entities-menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/main/webapp/app/entities/menu&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./entities-routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./src/main/webapp/app/entities/routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
        &lt;span class="na"&gt;shared&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;shareDependencies&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
          &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;shareMappings&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/config/constants&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/config/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/error/error-boundary-routes&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/layout/menus/menu-components&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/layout/menus/menu-item&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/reducers&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/reducers/locale&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/reducers/reducer.utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/util/date-utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app/shared/util/entity-utils&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
      &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;DefinePlugin&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;BLOG_I18N_RESOURCES_PREFIX&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="p"&gt;}),&lt;/span&gt;
    &lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="na"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;publicPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;auto&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build and run with Docker
&lt;/h2&gt;

&lt;p&gt;To build Docker images for each application, run the following command from the root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run java:docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The command is slightly different if you're using a Mac with Apple Silicon.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run java:docker:arm64
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: You can see all npm scripts with &lt;code&gt;npm run&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then, navigate to the &lt;code&gt;docker-compose&lt;/code&gt; directory, stop the existing containers, and start all the containers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;docker-compose
docker stop &lt;span class="si"&gt;$(&lt;/span&gt;docker ps &lt;span class="nt"&gt;-a&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This command will start and run all the apps, their databases, Consul, and Keycloak. To make Keycloak work, you must add the following line to your hosts file (&lt;code&gt;/etc/hosts&lt;/code&gt; on Mac/Linux, &lt;code&gt;c:\Windows\System32\Drivers\etc\hosts&lt;/code&gt; on Windows).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;127.0.0.1  keycloak
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is because you will access your application with a browser on your machine (where the name is localhost, or &lt;code&gt;127.0.0.1&lt;/code&gt;), but inside Docker it will run in its own container, where the name is &lt;code&gt;keycloak&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to prove everything works, ensure everything is started at &lt;code&gt;http://localhost:8500&lt;/code&gt;, then run &lt;code&gt;npm run e2e -ws&lt;/code&gt; from the root project directory. This command will run the Cypress tests that JHipster generates in your browser.&lt;/p&gt;

&lt;h2&gt;
  
  
  Switch identity providers
&lt;/h2&gt;

&lt;p&gt;JHipster ships with Keycloak when you choose OAuth 2.0 / OIDC as the authentication type. However, you can easily change it to another identity provider, like Auth0!&lt;/p&gt;

&lt;p&gt;First, you'll need to register a regular web application. Log in to your Auth0 account (or &lt;a href="https://auth0.com/signup" rel="noopener noreferrer"&gt;sign up&lt;/a&gt; if you don't have an account). You should have a unique domain like &lt;code&gt;dev-xxx.us.auth0.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Create Application&lt;/strong&gt; in the &lt;a href="https://manage.auth0.com/#/applications" rel="noopener noreferrer"&gt;Applications section&lt;/a&gt;. Use a name like &lt;code&gt;Micro Frontends&lt;/code&gt;, select &lt;strong&gt;Regular Web Applications&lt;/strong&gt;, and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Switch to the &lt;strong&gt;Settings&lt;/strong&gt; tab and configure your application settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allowed Callback URLs: &lt;code&gt;http://localhost:8080/login/oauth2/code/oidc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Allowed Logout URLs: &lt;code&gt;http://localhost:8080/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scroll to the bottom and click &lt;strong&gt;Save Changes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://manage.auth0.com/#/roles" rel="noopener noreferrer"&gt;roles&lt;/a&gt; section, create new roles named &lt;code&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code&gt;ROLE_USER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a new user account in the &lt;a href="https://manage.auth0.com/#/users" rel="noopener noreferrer"&gt;users&lt;/a&gt; section. Click the &lt;strong&gt;Role&lt;/strong&gt; tab to assign the roles you just created to the new account.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Make sure your new user's email is verified before attempting to log in!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next, head to &lt;strong&gt;Actions&lt;/strong&gt; &amp;gt; &lt;strong&gt;Flows&lt;/strong&gt; and select &lt;strong&gt;Login&lt;/strong&gt;. Create a new action named &lt;code&gt;Add Roles&lt;/code&gt; and use the default trigger and runtime. Change the &lt;code&gt;onExecutePostLogin&lt;/code&gt; handler to be as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onExecutePostLogin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;namespace&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://www.jhipster.tech&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setCustomClaim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;preferred_username&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setCustomClaim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/roles`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;api&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setCustomClaim&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;namespace&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/roles`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code adds the user's roles to a custom claim (prefixed with &lt;code&gt;https://www.jhipster.tech/roles&lt;/code&gt;). This claim is mapped to Spring Security authorities in &lt;code&gt;SecurityUtils.java&lt;/code&gt; in the gateway app.&lt;/p&gt;

&lt;p&gt;Select &lt;strong&gt;Deploy&lt;/strong&gt; and drag the &lt;code&gt;Add Roles&lt;/code&gt; action to your Login flow.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;docker-compose/central-server-config/application.yml&lt;/code&gt; and append the following YAML block to add your Auth0 settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jhipster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;oauth2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;audience&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://&amp;lt;your-auth0-domain&amp;gt;/api/v2/&lt;/span&gt;

&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;oauth2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;oidc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;issuer-uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://&amp;lt;your-auth0-domain&amp;gt;/&lt;/span&gt; &lt;span class="c1"&gt;# make sure to include the trailing slash!&lt;/span&gt;
        &lt;span class="na"&gt;registration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;oidc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;client-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;your-client-id&amp;gt;&lt;/span&gt;
            &lt;span class="na"&gt;client-secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;your-client-secret&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NOTE: Want to have all these steps automated for you? Vote for &lt;a href="https://github.com/auth0/auth0-cli/issues/351" rel="noopener noreferrer"&gt;issue #351&lt;/a&gt; in the Auth0 CLI project.&lt;/p&gt;

&lt;p&gt;Stop all your Docker containers with Ctrl+C and start them again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, Spring Security will be configured to use Auth0, and Consul will distribute these settings to all your microservices. When everything is started, navigate to &lt;code&gt;http://localhost:8080&lt;/code&gt; and click &lt;strong&gt;sign in&lt;/strong&gt;. You will be prompted for your Auth0 credentials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr43ne0n7qoptf5v7gzw1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr43ne0n7qoptf5v7gzw1.png" alt="Auth0 login" width="800" height="619"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After entering your credentials, you'll be redirected back to the gateway, and your username will be displayed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F755ktrwpg8mcw1xh465g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F755ktrwpg8mcw1xh465g.png" alt="Auth0 login success" width="800" height="543"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should be able to add, edit, and delete blogs, posts, tags, and products, proving that your microservices and micro frontends can talk to each other.&lt;/p&gt;

&lt;p&gt;If you'd like to use Okta for your identity provider, see &lt;a href="https://www.jhipster.tech/security/#okta" rel="noopener noreferrer"&gt;JHipster's documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: You can configure JHipster quickly with the &lt;a href="https://cli.okta.com" rel="noopener noreferrer"&gt;Okta CLI&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;okta apps create jhipster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy with Kubernetes
&lt;/h2&gt;

&lt;p&gt;The JDL you used to generate this microservices stack has a section at the bottom for deploying to Kubernetes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;deployment {
  deploymentType kubernetes
  appsFolders [gateway, blog, store]
  clusteredDbApps [store]
  kubernetesNamespace demo
  kubernetesUseDynamicStorage true
  kubernetesStorageClassName ""
  serviceDiscoveryType consul
  dockerRepositoryName "mraible"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;jhipster jdl&lt;/code&gt; command generates a &lt;code&gt;kubernetes&lt;/code&gt; directory with this information and configures all your apps, databases, and Consul to be Kubernetes-ready. If you have a Kubernetes cluster created, you can deploy to its &lt;code&gt;demo&lt;/code&gt; namespace using the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also generates files for Kustomize and Skaffold if you'd prefer to use those tools. See the &lt;a href="https://github.com/oktadev/auth0-micro-frontends-jhipster-example/blob/main/kubernetes/K8S-README.md" rel="noopener noreferrer"&gt;&lt;code&gt;kubernetes/K8S-README.md&lt;/code&gt;&lt;/a&gt; file for more information.&lt;/p&gt;

&lt;p&gt;I won't go into the nitty-gritty details of deploying a JHipster microservices stack to cloud providers with K8s, mainly because it's covered in previous blog posts. The first post below shows how to run Minikube locally, encrypt your secrets, and deploy to Google Cloud.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/06/01/kubernetes-spring-boot-jhipster" rel="noopener noreferrer"&gt;Kubernetes to the Cloud with Spring Boot and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/05/05/kubernetes-microservices-azure" rel="noopener noreferrer"&gt;Kubernetes Microservices on Azure with Cosmos DB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/06/06/microservices-digitalocean-kubernetes" rel="noopener noreferrer"&gt;Run Microservices on DigitalOcean with Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/07/11/kubernetes-jhipster-aws" rel="noopener noreferrer"&gt;JHipster Microservices on AWS with Amazon Elastic Kubernetes Service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/08/12/ci-cd-circleci-spinnaker-microservices" rel="noopener noreferrer"&gt;CI/CD Java Microservices with CircleCI and Spinnaker&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The JHipster team also has a blog you can follow at &lt;a href="https://dev.to/jhipster"&gt;dev.to/jhipster&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more about micro frontends and microservices
&lt;/h2&gt;

&lt;p&gt;I hope you enjoyed this overview of how to use micro frontends within a Java microservices architecture. I like how micro frontends allow each microservice application to be self-contained and deployable, independent of the other microservices. It's also pretty neat how JHipster generates Docker and Kubernetes configuration for you. Cloud-native FTW!&lt;/p&gt;

&lt;p&gt;You can find the source code for this example on GitHub, in the &lt;a href="https://github.com/oktadev/auth0-micro-frontends-jhipster-example" rel="noopener noreferrer"&gt;@oktadev/auth0-micro-frontends-jhipster-example&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;If you'd like to learn more about micro frontends and microservices, I recommend these posts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://auth0.com/blog/micro-frontends-with-angular-module-federation-and-auth0/" rel="noopener noreferrer"&gt;Micro Frontends with Angular, Module Federation, and Auth0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/05/17/angular-microfrontend-auth" rel="noopener noreferrer"&gt;How to Build Micro Frontends Using Module Federation in Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/05/19/angular-microfrontend-deploy" rel="noopener noreferrer"&gt;Secure and Deploy Micro Frontends with Angular&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/01/20/reactive-java-microservices" rel="noopener noreferrer"&gt;Reactive Java Microservices with Spring Boot and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.okta.com/blog/2021/02/microservices/" rel="noopener noreferrer"&gt;What are Microservices?&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please follow &lt;a href="https://twitter.com/auth0" rel="noopener noreferrer"&gt;@auth0&lt;/a&gt; and &lt;a href="https://twitter.com/oktadev" rel="noopener noreferrer"&gt;@oktadev&lt;/a&gt; on Twitter. We also have YouTube channels that you might enjoy at &lt;a href="https://www.youtube.com/auth0" rel="noopener noreferrer"&gt;youtube.com/auth0&lt;/a&gt; and &lt;a href="https://www.youtube.com/oktadev" rel="noopener noreferrer"&gt;youtube.com/oktadev&lt;/a&gt;. If you have any questions, please leave a comment below!&lt;/p&gt;

</description>
      <category>java</category>
      <category>microservices</category>
      <category>microfrontends</category>
      <category>jhipster</category>
    </item>
    <item>
      <title>CI/CD Java Microservices with CircleCI and Spinnaker</title>
      <dc:creator>Jimena Garbarino</dc:creator>
      <pubDate>Tue, 23 Aug 2022 23:37:28 +0000</pubDate>
      <link>https://forem.com/jhipster/cicd-java-microservices-with-circleci-and-spinnaker-4ho8</link>
      <guid>https://forem.com/jhipster/cicd-java-microservices-with-circleci-and-spinnaker-4ho8</guid>
      <description>&lt;p&gt;Continuous integration and delivery (CI/CD) are essential practices for modern software development. In this post we cover the basics of how to add CI/CD for a JHipster microservices architecture and Kubernetes as the target cloud deployment environment.&lt;/p&gt;

&lt;p&gt;Briefly, &lt;em&gt;continuous integration&lt;/em&gt; is the practice of integrating code into the main branch of a shared repository early and often. Instead of integrating features at the end of a development cycle, code is integrated with the shared repository multiple times throughout the day. Each commit triggers automated tests, so issues are detected and fixed earlier and faster, improving team confidence and productivity. The chosen continuous integration platform is from CircleCI, a company founded in 2011 and headquartered in San Francisco. They offer a free cloud to test their services.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Continuous delivery&lt;/em&gt; is the practice of releasing to production often in a fast, safe, and automated way, allowing faster innovation and feedback loops. Its adoption requires the implementation of techniques and tools like Spinnaker, an open-source, multi-cloud, continuous delivery platform that provides application management and deployment features. The intersection between CI and CD is not always clear, but for this example, we assume CI produces and validates the artifacts and CD deploys them. The CI/CD workflow for the exploration of the proposed tools is illustrated below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mqZ-Xt5a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zexfttzlws9jld6d2xar.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mqZ-Xt5a--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zexfttzlws9jld6d2xar.png" alt="CI/CD workflow" width="880" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This tutorial was created with the following frameworks and tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jhipster.tech/installation/"&gt;JHipster 7.9.2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jdk.java.net/java-se-ri/11"&gt;Java OpenJDK 11&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cli.okta.com"&gt;Okta CLI 0.10.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://spinnaker.io/docs/setup/install/halyard/#install-on-debianubuntu-and-macos"&gt;Halyard 1.44.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/tools/#kubectl"&gt;kubectl 1.23&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/sdk/docs/install"&gt;gcloud CLI 391.0.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://k9scli.io/topics/install/"&gt;k9s v0.25.18&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/install/"&gt;Docker 20.10.12&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
Create a Java microservices architecture

&lt;ul&gt;
&lt;li&gt;Configure Okta for OIDC authentication&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Set up CI for JHipster with CircleCI&lt;/li&gt;
&lt;li&gt;
Install Spinnaker on Google Kubernetes Engine

&lt;ul&gt;
&lt;li&gt;Understand Spinnaker artifacts and accounts&lt;/li&gt;
&lt;li&gt;Install Halyard&lt;/li&gt;
&lt;li&gt;Choose GKE for Spinnaker deployment&lt;/li&gt;
&lt;li&gt;Choose the installation environment&lt;/li&gt;
&lt;li&gt;Choose a storage service&lt;/li&gt;
&lt;li&gt;Deploy Spinnaker&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;
Set up CD for a JHipster microservices architecture

&lt;ul&gt;
&lt;li&gt;Use GKE for Kubernetes deployment&lt;/li&gt;
&lt;li&gt;Add a Docker registry account&lt;/li&gt;
&lt;li&gt;Add a GitHub artifact account&lt;/li&gt;
&lt;li&gt;Create the store microservice pipeline&lt;/li&gt;
&lt;li&gt;Configure the Docker image triggers&lt;/li&gt;
&lt;li&gt;Configure the deployment stages&lt;/li&gt;
&lt;li&gt;Trigger the CI/CD pipeline with a GitHub push&lt;/li&gt;
&lt;li&gt;Inspect Spinnaker logs&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Spinnaker features and best practices&lt;/li&gt;
&lt;li&gt;Learn more about JHipster, microservices, and Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Create a Java microservices architecture &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;If you haven't tried JHipster yet, you can do the classical local installation with npm.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; generator-jhipster@7.9.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather use Yarn or Docker, follow the instructions at &lt;a href="https://www.jhipster.tech/installation/#local-installation-with-npm-recommended-for-normal-users"&gt;jhipster.tech&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You can also use the example &lt;a href="https://github.com/oktadev/java-microservices-examples/tree/main/reactive-jhipster"&gt;reactive-jhipster&lt;/a&gt; from GitHub, a reactive microservices architecture with Spring Cloud Gateway and Spring WebFlux, Vue as the client framework, and Gradle as the build tool. You can find the complete instructions for building the architecture in a previous post: &lt;a href="https://dev.to/blog/2021/01/20/reactive-java-microservices"&gt;Reactive Java Microservices with Spring Boot and JHipster&lt;/a&gt;.&lt;br&gt;
Create a project folder &lt;code&gt;jhipster-ci-cd&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;jhipster-ci-cd
http &lt;span class="nt"&gt;-d&lt;/span&gt; https://raw.githubusercontent.com/oktadev/java-microservices-examples/bc7cbeeb1296bd0fcc6a09f4e39f4e6e472a076a/reactive-jhipster/reactive-ms.jdl
jhipster jdl reactive-ms.jdl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the generation, you will find sub-folders were created for the &lt;code&gt;gateway&lt;/code&gt;, &lt;code&gt;store&lt;/code&gt;, and &lt;code&gt;blog&lt;/code&gt; services. The &lt;code&gt;gateway&lt;/code&gt; will act as the front-end application and as a secure router to the &lt;code&gt;store&lt;/code&gt; and &lt;code&gt;blog&lt;/code&gt; microservices.&lt;/p&gt;

&lt;p&gt;The next step is to generate the Kubernetes deployment descriptors. In the project root folder, create a &lt;code&gt;kubernetes&lt;/code&gt; directory and run the &lt;code&gt;k8s&lt;/code&gt; JHipster sub-generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;kubernetes
&lt;span class="nb"&gt;cd &lt;/span&gt;kubernetes
jhipster k8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You must set up the Docker repository name for the cloud deployment, so go ahead and create a &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt; personal account, if you don't have one, before running the k8s sub-generator.&lt;/p&gt;

&lt;p&gt;Choose the following options when prompted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type of application: &lt;strong&gt;Microservice application&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Root directory: &lt;strong&gt;../&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Which applications? (select all)&lt;/li&gt;
&lt;li&gt;Set up monitoring? &lt;strong&gt;No&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Which applications with clustered databases? select &lt;strong&gt;store&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Admin password for JHipster Registry: (generate one)&lt;/li&gt;
&lt;li&gt;Kubernetes namespace: &lt;strong&gt;demo&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Docker repository name: &lt;strong&gt;your-dockerhub-username&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Command to push Docker image: &lt;code&gt;docker push&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable Istio? &lt;strong&gt;No&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Kubernetes service type? &lt;strong&gt;LoadBalancer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use dynamic storage provisioning? &lt;strong&gt;Yes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use a specific storage class? (leave empty)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Configure Okta for OIDC authentication &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;One more configuration step before building the applications, let's configure Okta for authentication. You must run the commands at the root folder of the project.&lt;/p&gt;

&lt;p&gt;Before you begin, you’ll need a free Okta developer account. Install the &lt;a href="https://cli.okta.com"&gt;Okta CLI&lt;/a&gt; and run &lt;code class="language-plaintext highlighter-rouge"&gt;okta register&lt;/code&gt; to sign up for a new account. If you already have an account, run &lt;code class="language-plaintext highlighter-rouge"&gt;okta login&lt;/code&gt;.
Then, run &lt;code class="language-plaintext highlighter-rouge"&gt;okta apps create jhipster&lt;/code&gt;. Select the default app name, or change it as you see fit.
  Accept the default Redirect URI values provided for you.&lt;/p&gt;

&lt;p&gt;What does the Okta CLI do?&lt;/p&gt;

&lt;p&gt;The Okta CLI streamlines configuring a JHipster app and does several things for you:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Creates an OIDC app with the correct redirect URIs:
    &lt;ul&gt;
      &lt;li&gt;login: &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8080/login/oauth2/code/oidc&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8761/login/oauth2/code/oidc&lt;/code&gt;
&lt;/li&gt;
      &lt;li&gt;logout: &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8080&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8761&lt;/code&gt;
&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Creates &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_USER&lt;/code&gt; groups that JHipster expects&lt;/li&gt;
  &lt;li&gt;Adds your current user to the &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_USER&lt;/code&gt; groups&lt;/li&gt;
  &lt;li&gt;Creates a &lt;code class="language-plaintext highlighter-rouge"&gt;groups&lt;/code&gt; claim in your default authorization server and adds the user’s groups to it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8761*&lt;/code&gt; redirect URIs are for the JHipster Registry, which is often used when creating microservices with JHipster. The Okta CLI adds these by default.&lt;/p&gt;

&lt;p&gt;You will see output like the following when it’s finished:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Okta application configuration has been written to: /path/to/app/.okta.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Run &lt;code class="language-plaintext highlighter-rouge"&gt;cat .okta.env&lt;/code&gt; (or &lt;code class="language-plaintext highlighter-rouge"&gt;type .okta.env&lt;/code&gt; on Windows) to see the issuer and credentials for your app. It will look like this (except the placeholder values will be populated):&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://{yourOktaDomain}/oauth2/default"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{clientId}"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{clientSecret}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You can also use the Okta Admin Console to create your app. See &lt;a href="https://www.jhipster.tech/security/#okta"&gt;Create a JHipster App on Okta&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;Add the settings from the generated &lt;code&gt;.okta.env&lt;/code&gt; to &lt;code&gt;kubernetes/registry-k8s/application-configmap.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;application.yml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;spring:&lt;/span&gt;
      &lt;span class="s"&gt;security:&lt;/span&gt;
        &lt;span class="s"&gt;oauth2:&lt;/span&gt;
          &lt;span class="s"&gt;client:&lt;/span&gt;
            &lt;span class="s"&gt;provider:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;issuer-uri: https://&amp;lt;your-okta-domain&amp;gt;/oauth2/default&lt;/span&gt;
            &lt;span class="s"&gt;registration:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;client-id: &amp;lt;client-id&amp;gt;&lt;/span&gt;
                &lt;span class="s"&gt;client-secret: &amp;lt;client-secret&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the OIDC authentication in the &lt;code&gt;jhipster-registry&lt;/code&gt; service by adding the &lt;code&gt;oauth2&lt;/code&gt; profile in the &lt;code&gt;kubernetes/registry-k8s/jhipster-registry.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_PROFILES_ACTIVE&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod,k8s,oauth2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We are going to build the &lt;code&gt;gateway&lt;/code&gt; and &lt;code&gt;store&lt;/code&gt; in a continuous integration workflow with CircleCI.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up CI for JHipster with CircleCI &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;First, create the CircleCI configuration for the &lt;code&gt;store&lt;/code&gt; microservice and the &lt;code&gt;gateway&lt;/code&gt;. "What about the &lt;code&gt;blog&lt;/code&gt; app?", you might ask. I'm not including it in this example to streamline this tutorial. You should be able to configure it similar to the &lt;code&gt;store&lt;/code&gt; if you want to deploy everything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;store
jhipster ci-cd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;gateway
jhipster ci-cd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For both applications, choose the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What CI/CD pipeline do you want to generate? &lt;strong&gt;CircleCI&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;What tasks/integrations do you want to include? (none)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You must tweak the generated configuration at &lt;code&gt;store/.circleci/config.yml&lt;/code&gt;, as the following changes are required for successful execution:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Change the execution environment of the workflow to a dedicated VM, as that is required by the &lt;a href="https://www.testcontainers.org/supported_docker_environment/continuous_integration/circle_ci"&gt;TestContainers&lt;/a&gt; dependency in tests. The chosen environment is &lt;a href="https://circleci.com/developer/machine/image/ubuntu-2004"&gt;LinuxVM&lt;/a&gt;, a standalone Ubuntu 20.04 virtual machine.&lt;/li&gt;
&lt;li&gt;As the dedicated VM includes Docker, the Docker installation step in the configuration must be removed.&lt;/li&gt;
&lt;li&gt;Add a conditional step for building and pushing the image to Docker Hub, only if the branch name is &lt;code&gt;main&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: You can also configure the job to only build specific &lt;a href="https://circleci.com/docs/configuration-reference#branches"&gt;branches&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The final &lt;code&gt;config.yml&lt;/code&gt; must look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.1&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DOCKERHUB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-dockerhub-username&lt;/span&gt;
      &lt;span class="na"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-dockerhub-username/store&lt;/span&gt;
    &lt;span class="na"&gt;machine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-2004:current&lt;/span&gt;
    &lt;span class="na"&gt;resource_class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;large&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;checkout&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;restore_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;v1-dependencies-{{ checksum "build.gradle" }}-{{ checksum "package-lock.json" }}&lt;/span&gt;
            &lt;span class="c1"&gt;# Perform a Partial Cache Restore (https://circleci.com/docs/2.0/caching/#restoring-cache)&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;v1-dependencies-&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print Java Version&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;java&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-version'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print Node Version&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;node&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-v'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print NPM Version&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-v'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Node Modules&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;install'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;save_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;~/.gradle&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1-dependencies-{{ checksum "build.gradle" }}-{{ checksum "package-lock.json" }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Give Executable Power&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;chmod&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;+x&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;gradlew'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Backend tests&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run ci:backend:test&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;equal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;&amp;lt;&amp;lt; pipeline.git.branch &amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
          &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Publish Docker Image&lt;/span&gt;
                &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                  &lt;span class="s"&gt;./gradlew -Pprod bootJar jib \&lt;/span&gt;
                    &lt;span class="s"&gt;-Djib.to.image=$IMAGE_NAME \&lt;/span&gt;
                    &lt;span class="s"&gt;-Djib.to.auth.username=$DOCKERHUB_USER \&lt;/span&gt;
                    &lt;span class="s"&gt;-Djib.to.auth.password=$DOCKERHUB_PASS          &lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do the same updates to the &lt;code&gt;gateway/.circleci/config.yml&lt;/code&gt; file. Additionally, the following change is required:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Add &lt;code&gt;~/.cache/Cypress&lt;/code&gt; as a cache path&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be sure to replace &lt;code&gt;your-dockerhub-username&lt;/code&gt; and set the correct &lt;code&gt;IMAGE_NAME&lt;/code&gt;.&lt;br&gt;
The final &lt;code&gt;config.yml&lt;/code&gt; for the &lt;code&gt;gateway&lt;/code&gt; must look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2.1&lt;/span&gt;
&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;DOCKERHUB_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-dockerhub-username&lt;/span&gt;
      &lt;span class="na"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;your-dockerhub-username/gateway&lt;/span&gt;
    &lt;span class="na"&gt;machine&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-2004:current&lt;/span&gt;
    &lt;span class="na"&gt;resource_class&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;large&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;checkout&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;restore_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;v1-dependencies-{{ checksum "build.gradle" }}-{{ checksum "package-lock.json" }}&lt;/span&gt;
            &lt;span class="c1"&gt;# Perform a Partial Cache Restore (https://circleci.com/docs/2.0/caching/#restoring-cache)&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;v1-dependencies-&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print Java Version&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;java&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-version'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print Node Version&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;node&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-v'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Print NPM Version&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;-v'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Install Node Modules&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;npm&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;install'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;save_cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;~/.gradle&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;~/.cache/Cypress&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1-dependencies-{{ checksum "build.gradle" }}-{{ checksum "package-lock.json" }}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Give Executable Power&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;chmod&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;+x&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;gradlew'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Backend tests&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run ci:backend:test&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Front End Tests&lt;/span&gt;
          &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm run ci:frontend:test&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;equal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt; &lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;&amp;lt;&amp;lt; pipeline.git.branch &amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
          &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Publish Docker Image&lt;/span&gt;
                &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
                  &lt;span class="s"&gt;./gradlew -Pprod bootJar jib \&lt;/span&gt;
                    &lt;span class="s"&gt;-Djib.to.image=$IMAGE_NAME \&lt;/span&gt;
                    &lt;span class="s"&gt;-Djib.to.auth.username=$DOCKERHUB_USER \&lt;/span&gt;
                    &lt;span class="s"&gt;-Djib.to.auth.password=$DOCKERHUB_PASS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To take advantage of the one-step GitHub integration of CircleCI, you need a &lt;a href="https://github.com/signup"&gt;GitHub&lt;/a&gt; account. After signing in to GitHub, create a new public repository &lt;code&gt;store&lt;/code&gt;. Follow the instructions to push the existing repository from your local machine to GitHub using the command line. Do the same with the &lt;code&gt;gateway&lt;/code&gt; project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://circleci.com/integrations/github"&gt;Sign up&lt;/a&gt; at CircleCI with your GitHub account, and on the left menu choose &lt;strong&gt;Projects&lt;/strong&gt;. Find the &lt;code&gt;store&lt;/code&gt; project and click &lt;strong&gt;Set Up Project&lt;/strong&gt;. Configure the project to use the &lt;strong&gt;Fastest&lt;/strong&gt; option (Use the &lt;code&gt;.circleci/config.yml&lt;/code&gt; in my repo) and type &lt;code&gt;main&lt;/code&gt; for the branch. Click &lt;strong&gt;Set Up Project&lt;/strong&gt; to continue.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ER_I1xzK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ns78a4d0y2y77zz5jle.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ER_I1xzK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ns78a4d0y2y77zz5jle.png" alt="CircleCI project setup form" width="478" height="424"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Do the same for the &lt;code&gt;gateway&lt;/code&gt; project. The configuration triggers an initial pipeline execution that will fail, because you still must set up Docker Hub credentials for both projects, allowing CircleCI to push the container images. At the &lt;code&gt;store&lt;/code&gt; project page, on the top right, choose &lt;strong&gt;Project Settings&lt;/strong&gt;. Then choose &lt;strong&gt;Environment Variables&lt;/strong&gt;. Click &lt;strong&gt;Add Environment Variable&lt;/strong&gt;. and set the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: &lt;code&gt;DOCKERHUB_PASS&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Value: your Docker Hub password, or better, a Docker Hub access token if you have 2FA enabled.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: For creating a Docker Hub access token, sign in to &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt;, and choose &lt;strong&gt;Account Settings&lt;/strong&gt; in the top right user menu. Then, on the left menu, choose &lt;strong&gt;Security&lt;/strong&gt;. Click &lt;strong&gt;New Access Token&lt;/strong&gt; and set a description for the token, for example, &lt;em&gt;circleci&lt;/em&gt;. Then click &lt;strong&gt;Generate&lt;/strong&gt; and copy the new token. You can use the same token for both projects &lt;code&gt;store&lt;/code&gt; and &lt;code&gt;gateway&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Once a project is set up in CircleCI, a pipeline is triggered each time a commit is pushed to the configured branch. The pipeline in execution appears on the &lt;strong&gt;Dashboard&lt;/strong&gt; page. You can also manually trigger the pipeline from the &lt;strong&gt;Dashboard&lt;/strong&gt; if you choose the project and branch from the pipeline filters, and then click &lt;strong&gt;Trigger Pipeline&lt;/strong&gt;. Before moving on to the next section, manually execute the store pipeline and the gateway pipeline once, to push the first image of each to DockerHub.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_cANLmEX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjdalhbbt09ncjy2m8bb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_cANLmEX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/sjdalhbbt09ncjy2m8bb.png" alt="CircleCI job success" width="880" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: If you modify the configuration, and encounter CircleCI cache errors, the only way I found to recreate the cache is to update the version prefix, for example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;v2-dependencies-{{ checksum "build.gradle" }}-{{ checksum "package-lock.json" }}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Install Spinnaker on Google Kubernetes Engine &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Although CircleCI supports deployment for common targets, Spinnaker is a deployment manager that supports multiple cloud providers, promoting artifacts through stages (dev, staging, UAT, production), and creating continuous delivery workflows. Spinnaker does not support CI, but it can listen to events and collect artifacts produced by external CI systems.&lt;/p&gt;

&lt;p&gt;You can choose among the following installation environments for Spinnaker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Distributed installation on Kubernetes&lt;/li&gt;
&lt;li&gt;Local installation of Debian packages&lt;/li&gt;
&lt;li&gt;Local git installation from GitHub&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For this example, the distributed alternative was selected. But before the installation, learn about some key concepts in Spinnaker configuration.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understand Spinnaker artifacts and accounts &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;A &lt;em&gt;Spinnaker artifact&lt;/em&gt; is a named JSON object that refers to an external resource, for example, a Docker image or a file stored in GitHub. In a pipeline trigger, you can specify an &lt;em&gt;expected artifact&lt;/em&gt;, and Spinnaker will compare the incoming artifact from the trigger and bind it to be used by the trigger or another stage in the pipeline.&lt;br&gt;
Also in Kubernetes, the deployed manifests can be provided statically or as an artifact. In this example, manifests are provided as artifacts stored in GitHub.&lt;/p&gt;

&lt;p&gt;For Spinnaker to access and act on resources, like Docker registries, cloud providers, and code repositories, different types of accounts must be enabled in the Spinnaker configuration, along with the credentials. In addition, the configuration has the optional step of associating Spinnaker with a Kubernetes service account, allowing you to restrict permissions over the cluster resources granted to Spinnaker.&lt;/p&gt;
&lt;h3&gt;
  
  
  Install Halyard &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;As described in Spinnaker &lt;a href="https://spinnaker.io/docs/setup/install/"&gt;docs&lt;/a&gt;, the first step is to install &lt;a href="https://spinnaker.io/docs/setup/install/halyard/"&gt;Halyard&lt;/a&gt;. For a local install in MacOS:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://raw.githubusercontent.com/spinnaker/halyard/master/install/macos/InstallHalyard.sh
&lt;span class="nb"&gt;sudo &lt;/span&gt;bash InstallHalyard.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify the installation with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You must also install &lt;a href="https://kubernetes.io/docs/tasks/tools/"&gt;&lt;code&gt;kubectl&lt;/code&gt;&lt;/a&gt;, the Kubernetes command line tool, to run commands against the clusters.&lt;/p&gt;

&lt;h3&gt;
  
  
  Choose GKE for Spinnaker deployment &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The second step is to choose a cloud provider for the environment in which you will install Spinnaker. For Kubernetes, Spinnaker needs a &lt;code&gt;kubeconfig&lt;/code&gt; file, to access and manage the cluster. For creating a &lt;code&gt;kubeconfig&lt;/code&gt; for a GKE cluster, you must first create the cluster. Google Cloud provides a &lt;a href="https://cloud.google.com/free"&gt;free tier&lt;/a&gt; of their services that grants you $300 in free credits if you are a new user.&lt;/p&gt;

&lt;p&gt;After you sign up, install &lt;a href="https://cloud.google.com/sdk/docs/install"&gt;&lt;code&gt;gcloud&lt;/code&gt; CLI&lt;/a&gt;. When you reach the end of the process, the last step is to run &lt;code&gt;gcloud init&lt;/code&gt; and set up authorization for the tool. Also install the &lt;a href="https://cloud.google.com/blog/products/containers-kubernetes/kubectl-auth-changes-in-gke"&gt;Kubectl authentication plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Create the cluster for the Spinnaker deployment with the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters create spinnaker-cluster &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zone&lt;/span&gt; southamerica-east1-a &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--machine-type&lt;/span&gt; n1-standard-4 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--enable-autorepair&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--enable-autoupgrade&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then fetch the cluster credentials with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters get-credentials spinnaker-cluster &lt;span class="nt"&gt;--zone&lt;/span&gt; southamerica-east1-a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;get-credentials&lt;/code&gt; will update a &lt;code&gt;kubeconfig&lt;/code&gt; file with appropriate credentials and endpoint information to point &lt;code&gt;kubectl&lt;/code&gt; at a specific cluster in Google Kubernetes Engine.&lt;/p&gt;

&lt;p&gt;Create a namespace for the Spinnaker cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace spinnaker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Spinnaker &lt;a href="https://spinnaker.io/docs/setup/install/providers/kubernetes-v2/#optional-create-a-kubernetes-service-account"&gt;documentation&lt;/a&gt; recommends creating a Kubernetes service account, using Kubernetes role definitions that restrict the permissions granted to the Spinnaker account. Create a &lt;code&gt;spinnaker&lt;/code&gt; folder in the root folder or any other location. Add the file &lt;code&gt;spinnaker-service-account.yml&lt;/code&gt; with the following content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRole&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spinnaker-role&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;namespaces'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;configmaps'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;events'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;replicationcontrollers'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;serviceaccounts'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pods/log'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
      &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pods'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;services'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;secrets'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;delete'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deletecollection'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;patch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;update'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;watch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
      &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;autoscaling'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;horizontalpodautoscalers'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;apps'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;controllerrevisions'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;extensions'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;apps'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;daemonsets'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deployments'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deployments/scale'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ingresses'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;replicasets'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statefulsets'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;delete'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deletecollection'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;patch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;update'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;watch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
      &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;# These permissions are necessary for Halyard to operate. We use this role also to deploy Spinnaker itself.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;services/proxy'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pods/portforward'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;delete'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deletecollection'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;patch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;update'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;watch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
      &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRoleBinding&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spinnaker-role-binding&lt;/span&gt;
&lt;span class="na"&gt;roleRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRole&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spinnaker-role&lt;/span&gt;
&lt;span class="na"&gt;subjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spinnaker&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spinnaker-service-account&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spinnaker-service-account&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;spinnaker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then execute the following commands to create the service account in the current context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CONTEXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl config current-context&lt;span class="si"&gt;)&lt;/span&gt;

kubectl apply &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;$CONTEXT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-f&lt;/span&gt; ./spinnaker-service-account.yml

&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get secret &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;$CONTEXT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="si"&gt;$(&lt;/span&gt;kubectl get serviceaccount spinnaker-service-account &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;$CONTEXT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;-n&lt;/span&gt; spinnaker &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.secrets[0].name}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-n&lt;/span&gt; spinnaker &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.token}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

kubectl config set-credentials &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTEXT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-token-user&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="nv"&gt;$TOKEN&lt;/span&gt;

kubectl config set-context &lt;span class="nv"&gt;$CONTEXT&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTEXT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-token-user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the Kubernetes provider in Halyard:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal config provider kubernetes &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You will see warnings related to missing configuration. Be patient, in the following steps all will be solved.&lt;/p&gt;

&lt;p&gt;Add the service account to the Halyard configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CONTEXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl config current-context&lt;span class="si"&gt;)&lt;/span&gt;

hal config provider kubernetes account add spinnaker-gke-account &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;$CONTEXT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Choose the installation environment &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;strong&gt;Distributed installation&lt;/strong&gt; on Kubernetes deploys each Spinnaker's microservice separately to a remote cloud, and it is the recommended environment for zero-downtime updates of Spinnaker.&lt;/p&gt;

&lt;p&gt;Select the distributed deployment with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal config deploy edit &lt;span class="nt"&gt;--type&lt;/span&gt; distributed &lt;span class="nt"&gt;--account-name&lt;/span&gt; spinnaker-gke-account
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Choose a storage service &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Spinnaker requires an external storage provider for persisting application settings and configured pipelines. To avoid losing this data, using a hosted storage solution is recommended. For this example, we use Google Cloud Storage. Hosting the data externally allows us to delete clusters in between sessions, and keep the pipelines once the clusters have been recreated.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;spinnaker&lt;/code&gt; folder, run the following code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;SERVICE_ACCOUNT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;spinnaker-gcs-account
&lt;span class="nv"&gt;SERVICE_ACCOUNT_DEST&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;~/.gcp/gcs-account.json

gcloud iam service-accounts create &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nv"&gt;$SERVICE_ACCOUNT_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--display-name&lt;/span&gt; &lt;span class="nv"&gt;$SERVICE_ACCOUNT_NAME&lt;/span&gt;

&lt;span class="nv"&gt;SA_EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gcloud iam service-accounts list &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"displayName:&lt;/span&gt;&lt;span class="nv"&gt;$SERVICE_ACCOUNT_NAME&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'value(email)'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="nv"&gt;PROJECT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;gcloud config get-value project&lt;span class="si"&gt;)&lt;/span&gt;

gcloud projects add-iam-policy-binding &lt;span class="nv"&gt;$PROJECT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role&lt;/span&gt; roles/storage.admin &lt;span class="nt"&gt;--member&lt;/span&gt; serviceAccount:&lt;span class="nv"&gt;$SA_EMAIL&lt;/span&gt;

&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;dirname&lt;/span&gt; &lt;span class="nv"&gt;$SERVICE_ACCOUNT_DEST&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

gcloud iam service-accounts keys create &lt;span class="nv"&gt;$SERVICE_ACCOUNT_DEST&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--iam-account&lt;/span&gt; &lt;span class="nv"&gt;$SA_EMAIL&lt;/span&gt;

&lt;span class="nv"&gt;BUCKET_LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;us

hal config storage gcs edit &lt;span class="nt"&gt;--project&lt;/span&gt; &lt;span class="nv"&gt;$PROJECT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--bucket-location&lt;/span&gt; &lt;span class="nv"&gt;$BUCKET_LOCATION&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--json-path&lt;/span&gt; &lt;span class="nv"&gt;$SERVICE_ACCOUNT_DEST&lt;/span&gt;

hal config storage edit &lt;span class="nt"&gt;--type&lt;/span&gt; gcs    
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The last &lt;code&gt;hal&lt;/code&gt; commands edit the storage settings and set the storage source to GCS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy Spinnaker &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;You can verify the deployment configuration with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal config deploy
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the storage configuration with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal config storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List the available Spinnaker versions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal version list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the version for the deployment with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal config version edit &lt;span class="nt"&gt;--version&lt;/span&gt; 1.27.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy Spinnaker to GKE:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal deploy apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Even if &lt;code&gt;hal deploy apply&lt;/code&gt; returns successfully, the installation may not be complete yet. Check if the pods are ready with &lt;code&gt;k9s&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;k9s &lt;span class="nt"&gt;-n&lt;/span&gt; spinnaker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A healthy Spinnaker deployment will look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--up_CyYDm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v01h430r1k8eautfr5vw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--up_CyYDm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/v01h430r1k8eautfr5vw.png" alt="Spinnaker k9s view" width="880" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can connect to the Spinnaker UI using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal deploy connect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to &lt;code&gt;http://localhost:9000&lt;/code&gt; and the UI should load. The &lt;strong&gt;Projects&lt;/strong&gt; tab should look like the screenshot below.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--tJJtFdIT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52pwofi01ohkjjt1qe63.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--tJJtFdIT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/52pwofi01ohkjjt1qe63.png" alt="Spinnaker DeckUI" width="880" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up CD for a JHipster microservices architecture &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;Spinnaker is multi-cloud because it can manage delivery to multiple cloud providers. Some companies use different platforms for production and test environments. In this example, the same cloud provider (GKE) was chosen both for Spinnaker deployment and for the application test deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use GKE for Kubernetes deployment &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Now I'll show you how to add a new Kubernetes account for a different cluster, which will be used for application deployment.&lt;/p&gt;

&lt;p&gt;Create a cluster for the JHipster microservices architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters create jhipster-cluster &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zone&lt;/span&gt; southamerica-east1-a &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--machine-type&lt;/span&gt; n1-standard-4 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--enable-autorepair&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--enable-autoupgrade&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Fetch the cluster credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters get-credentials jhipster-cluster &lt;span class="nt"&gt;--zone&lt;/span&gt; southamerica-east1-a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the namespace &lt;code&gt;demo&lt;/code&gt; for your microservices:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create namespace demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;spinnaker&lt;/code&gt; directory, create the file &lt;code&gt;jhipster-service-account.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRole&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster-role&lt;/span&gt;
&lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;namespaces'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;events'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;replicationcontrollers'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;serviceaccounts'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pods/log'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
      &lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pods'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;services'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;secrets'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;configmaps'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;persistentvolumeclaims'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;delete'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deletecollection'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;patch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;update'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;watch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
      &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;autoscaling'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;horizontalpodautoscalers'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;apps'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;controllerrevisions'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;extensions'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;apps'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;daemonsets'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deployments'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deployments/scale'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ingresses'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;replicasets'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;statefulsets'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;delete'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deletecollection'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;patch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;update'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;watch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
      &lt;span class="pi"&gt;]&lt;/span&gt;
  &lt;span class="c1"&gt;# These permissions are necessary for Halyard to operate. We use this role also to deploy Spinnaker itself.&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;apiGroups&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;services/proxy'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;pods/portforward'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;verbs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;create'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;delete'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deletecollection'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;get'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;patch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;update'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;watch'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
      &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRoleBinding&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster-role-binding&lt;/span&gt;
&lt;span class="na"&gt;roleRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;apiGroup&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rbac.authorization.k8s.io&lt;/span&gt;
  &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterRole&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster-role&lt;/span&gt;
&lt;span class="na"&gt;subjects&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo&lt;/span&gt;
    &lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster-service-account&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ServiceAccount&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster-service-account&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice there are some differences with the Spinnaker service account. The k8s manifests generated by JHipster include the creation of &lt;code&gt;secrets&lt;/code&gt;, &lt;code&gt;configmaps&lt;/code&gt;, and &lt;code&gt;persistentvolumeclaims&lt;/code&gt;. The management of those Kubernetes resources must be granted to the service account used for deploying the JHipster k8s manifests.&lt;/p&gt;

&lt;p&gt;Create the Kubernetes &lt;code&gt;jhipster-service-account&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CONTEXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl config current-context&lt;span class="si"&gt;)&lt;/span&gt;

kubectl apply &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;$CONTEXT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-f&lt;/span&gt; ./jhipster-service-account.yml
&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get secret &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;$CONTEXT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="si"&gt;$(&lt;/span&gt;kubectl get serviceaccount jhipster-service-account &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;$CONTEXT&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;-n&lt;/span&gt; demo &lt;span class="se"&gt;\&lt;/span&gt;
       &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.secrets[0].name}'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-n&lt;/span&gt; demo &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;-o&lt;/span&gt; &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{.data.token}'&lt;/span&gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;

kubectl config set-credentials &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTEXT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-token-user&lt;/span&gt; &lt;span class="nt"&gt;--token&lt;/span&gt; &lt;span class="nv"&gt;$TOKEN&lt;/span&gt;

kubectl config set-context &lt;span class="nv"&gt;$CONTEXT&lt;/span&gt; &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;CONTEXT&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-token-user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the account to the Halyard configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;CONTEXT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl config current-context&lt;span class="si"&gt;)&lt;/span&gt;

hal config provider kubernetes account add jhipster-gke-account &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--context&lt;/span&gt; &lt;span class="nv"&gt;$CONTEXT&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add a Docker registry account &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;As the goal is to trigger the pipeline execution when a new image is pushed to Docker Hub, you need to configure a Docker Registry provider with Halyard.&lt;/p&gt;

&lt;p&gt;First, enable the Docker Registry provider:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal config provider docker-registry &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The following &lt;code&gt;hal config&lt;/code&gt; line will prompt for your password (or access token if you have 2FA enabled). Make sure to replace &lt;code&gt;your-dockerhub-username&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ADDRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;index.docker.io
&lt;span class="nv"&gt;USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-dockerhub-username

hal config provider docker-registry account add docker-account &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--address&lt;/span&gt; &lt;span class="nv"&gt;$ADDRESS&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--repositories&lt;/span&gt; &lt;span class="nv"&gt;$USERNAME&lt;/span&gt;/store &lt;span class="nv"&gt;$USERNAME&lt;/span&gt;/gateway &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--username&lt;/span&gt; &lt;span class="nv"&gt;$USERNAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Add a GitHub artifact account &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The pipeline will deploy k8s manifests from a GitHub repository, so you must also configure a GitHub artifact account. First, enable the feature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal config artifact github &lt;span class="nb"&gt;enable&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Generate an access token on GitHub. Sign in, and in the user menu choose &lt;strong&gt;Settings&lt;/strong&gt;. Then on the left menu choose &lt;strong&gt;Developer settings&lt;/strong&gt;. On the left menu choose &lt;strong&gt;Personal access tokens&lt;/strong&gt;. Click &lt;strong&gt;Generate new token&lt;/strong&gt; and copy the token.&lt;/p&gt;

&lt;p&gt;Then add your GitHub username and set the new token when prompted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;ARTIFACT_ACCOUNT_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your-github-username

hal config artifact github account add &lt;span class="nv"&gt;$ARTIFACT_ACCOUNT_NAME&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--token&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The new GKE, Docker, and GitHub account configurations must be applied to the deployment before starting the pipeline design:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal deploy apply
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create the store microservice pipeline &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The &lt;em&gt;pipeline&lt;/em&gt; is the central concept in deployment management with Spinnaker. It is compounded by a sequence of actions, named &lt;em&gt;stages&lt;/em&gt;. A pipeline can be triggered manually or automatically, and have an execution history. Connect to the Spinnaker UI again to create the first pipeline.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;hal deploy connect
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to &lt;code&gt;https://localhost:9000&lt;/code&gt; &amp;gt; &lt;strong&gt;Applications&lt;/strong&gt;. Click &lt;strong&gt;Create Application&lt;/strong&gt; and just set the application name and owner email. The repository type and additional fields are not mandatory and you can leave all blank for this test.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--_2eRpZva--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bii72y9df28jswoq0038.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--_2eRpZva--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bii72y9df28jswoq0038.png" alt="Spinnaker create application" width="592" height="508"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, on the left menu, choose &lt;strong&gt;Pipelines&lt;/strong&gt; and click &lt;strong&gt;Configure a new pipeline&lt;/strong&gt;. Set a name for the pipeline, for example, &lt;em&gt;store-cd&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the Docker image triggers &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;In the pipeline configuration, click &lt;strong&gt;Add Trigger&lt;/strong&gt;. Set the following configuration for the trigger:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type: &lt;strong&gt;Docker Registry&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Registry Name: &lt;strong&gt;docker-account&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Organization: &lt;strong&gt;your-dockerhub-username&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Image: &lt;strong&gt;your-dockerhub-username/store&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Artifact Constraints: Choose &lt;strong&gt;Define new artifact&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the &lt;em&gt;Expected Artifact&lt;/em&gt; form, set the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Display Name: &lt;strong&gt;store-image&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Account: &lt;strong&gt;docker-registry&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Docker image: Fully qualified &lt;strong&gt;index.docker.io/your-dockerhub-username/store&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use prior execution: &lt;strong&gt;yes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use default artifact: &lt;strong&gt;yes&lt;/strong&gt; (set the same account and object path as before)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save Artifact&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XM3rRetL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9b1jinekm6rnbrh2o3d3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XM3rRetL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9b1jinekm6rnbrh2o3d3.png" alt="Spinnaker pipeline trigger" width="880" height="610"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IMPORTANT NOTE&lt;/strong&gt;: Spinnaker provides a mechanism to &lt;a href="https://spinnaker.io/docs/guides/user/kubernetes-v2/deploy-manifest/#override-artifacts"&gt;override&lt;/a&gt; the version of Docker images, Kubernetes ConfigMaps, and Secrets in manifests, injecting new versions when one of these objects exists in the pipeline context. However, Spinnaker documentation does not have a clear example of the trigger configuration for the Docker image to be available in the pipeline context. To make it happen, you must &lt;em&gt;set it as an artifact constraint in the trigger&lt;/em&gt;. If the artifact constraint is not defined, the original image version in the manifests will be deployed, instead of the new image that triggered the pipeline. If no version is in the manifests, &lt;code&gt;latest&lt;/code&gt; will be the default.&lt;/p&gt;

&lt;p&gt;Add a second Docker registry trigger for the gateway image pushes and click &lt;strong&gt;Save Changes&lt;/strong&gt;.  &lt;/p&gt;

&lt;h3&gt;
  
  
  Configure the deployment stages &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;The store pipeline will execute the following stages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Deploy the application ConfigMap&lt;/li&gt;
&lt;li&gt;Deploy the JHipster Registry&lt;/li&gt;
&lt;li&gt;Deploy the gateway database PostgreSQL&lt;/li&gt;
&lt;li&gt;Deploy the store clustered database MongoDB&lt;/li&gt;
&lt;li&gt;Deploy the gateway microservice&lt;/li&gt;
&lt;li&gt;Deploy the store microservice&lt;/li&gt;
&lt;li&gt;Enable the gateway service&lt;/li&gt;
&lt;li&gt;Enable the store service&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Kubernetes configuration created in the folder &lt;code&gt;jhipster-ci-cd/kubernetes&lt;/code&gt; must be pushed to a GitHub repository, so Spinnaker can access it using the GitHub artifact account created earlier. But before you push, edit &lt;code&gt;kubernetes/store-k8s/store-deployment.yml&lt;/code&gt; and set the image name with the fully qualified address:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-app&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.docker.io/indiepopart/store&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Do the same for &lt;code&gt;kubernetes/gateway-k8s/gateway-deployment.yml&lt;/code&gt;. This is also required for the artifact substitution to work.&lt;/p&gt;

&lt;p&gt;Sign in to GitHub and create a public repository &lt;code&gt;jhipster-k8s&lt;/code&gt;. Follow the instructions to push the existing &lt;code&gt;kubernetes&lt;/code&gt; folder from your local using the command line.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IMPORTANT NOTE&lt;/strong&gt;: At this point, you will be pushing plain secrets contained in the application and Kubernetes configuration. To avoid this insecure anti-pattern, you can run the JHipster registry locally to encrypt application configuration, and also set up &lt;code&gt;kubeseal&lt;/code&gt; for Kubernetes secrets encryption. The process is described in a previous post: &lt;a href="https://dev.to/blog/2021/06/01/kubernetes-spring-boot-jhipster"&gt;Kubernetes to the Cloud with Spring Boot and JHipster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Go back to Spinnaker UI, and in the pipeline configuration choose &lt;strong&gt;Add Stage&lt;/strong&gt; and set the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type: &lt;strong&gt;Deploy (Manifest)&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Stage Name: &lt;strong&gt;deploy application-configmap&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Account: &lt;strong&gt;jhipster-gke-account&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Override Namespace: &lt;strong&gt;yes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Namespace: &lt;strong&gt;demo&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Manifest Source: &lt;strong&gt;Artifact&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Manifest Artifact: &lt;strong&gt;Define a new artifact&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the artifact fields, set the following values:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Account: &lt;strong&gt;your-github-username&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Content URL: &lt;a href="https://api.github.com/repos/your-github-username/jhipster-k8s/contents/registry-k8s/application-configmap.yml"&gt;https://api.github.com/repos/your-github-username/jhipster-k8s/contents/registry-k8s/application-configmap.yml&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Commit/Branch: &lt;strong&gt;main&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Click &lt;strong&gt;Save Changes&lt;/strong&gt;.&lt;br&gt;
 Repeat the process for adding stages to deploy &lt;code&gt;jhipster-registry.yml&lt;/code&gt;, which must depend on the previous manifest. Also repeat for &lt;code&gt;gateway-postgresql.yml&lt;/code&gt;, &lt;code&gt;store-mongodb.yml&lt;/code&gt; manifests, which both should depend on the &lt;code&gt;jhipster-registry&lt;/code&gt; stage. Make sure to set the correct &lt;strong&gt;Content URL&lt;/strong&gt; for the GitHub artifact. It must have the form &lt;code&gt;https://api.github.com/repos/&amp;lt;your-github-username&amp;gt;/&amp;lt;repo-name&amp;gt;/contents/&amp;lt;path&amp;gt;&lt;/code&gt;. If you open the URL in a browser, it should render a JSON response.&lt;/p&gt;

&lt;p&gt;Add a stage for the &lt;code&gt;store-deployment.yml&lt;/code&gt; manifest, it must depend on the &lt;code&gt;store-mongodb&lt;/code&gt; stage. For this manifest, you must bind the Docker image from the context. In the deploy manifest configuration, in &lt;strong&gt;Required Artifacts to Bind&lt;/strong&gt;, choose &lt;strong&gt;store-image&lt;/strong&gt;. The option must be available, as the image was set as an artifact constraint in the trigger configuration, making the artifact available in the pipeline context.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--FJuXyVvJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/trqo90lda5jy2ehmynky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--FJuXyVvJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/trqo90lda5jy2ehmynky.png" alt="Spinnaker deploy manifest configuration binding artifact" width="880" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also add a stage for the &lt;code&gt;gateway-deployment.yml&lt;/code&gt;, binding the &lt;code&gt;gateway&lt;/code&gt; Docker image. It must depend on the &lt;code&gt;gateway-postgresql&lt;/code&gt; stage.&lt;/p&gt;

&lt;p&gt;Finally, add a stage for &lt;code&gt;store-service.yml&lt;/code&gt;, which must depend on the &lt;code&gt;store-deployment&lt;/code&gt; stage, and a stage for &lt;code&gt;gateway-service.yml&lt;/code&gt;, which must depend on the &lt;code&gt;gateway-postgresql&lt;/code&gt; stage. Notice that Docker images have to be bound only for the manifests that contain a reference to the image.&lt;br&gt;
The complete pipeline must look like this:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QTuXWVg9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q4aj648s7bgdo481gd06.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QTuXWVg9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/q4aj648s7bgdo481gd06.png" alt="Spinnaker pipeline for jhipster microservices" width="880" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Test the pipeline manually once. Go to &lt;strong&gt;Pipelines&lt;/strong&gt; and select &lt;strong&gt;Start Manual Execution&lt;/strong&gt; for the &lt;em&gt;store-cd&lt;/em&gt; pipeline. Set the following options:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trigger: leave &lt;strong&gt;docker-account: your-dockerhub-username/store&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Type: &lt;strong&gt;Tag&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Tag: &lt;strong&gt;latest&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Successful execution will show all stages in a green state:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--AHvCkYLT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/il60su7jfiiwyvl4d6oj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AHvCkYLT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/il60su7jfiiwyvl4d6oj.png" alt="Spinnaker pipeline successful execution" width="880" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Get the external IP of the gateway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc gateway &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the redirect URIs in Okta to allow the gateway address as a valid redirect. Run &lt;code&gt;okta login&lt;/code&gt;, open the returned URL in your browser, and sign in to the Okta Admin Console. Go to the &lt;strong&gt;Applications&lt;/strong&gt; section, find your application, edit, and add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sign-in redirect URIs: &lt;code&gt;http://&amp;lt;external-ip&amp;gt;:8080/login/oauth2/code/oidc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sign-out redirect URIs: &lt;code&gt;http://&amp;lt;external-ip&amp;gt;:8080&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Navigate to &lt;code&gt;http://&amp;lt;external-ip&amp;gt;:8080&lt;/code&gt; and sign in to the application with your Okta credentials. Then try creating a &lt;code&gt;Product&lt;/code&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ypkHTrEI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x0hwrprcwm202bvolmd6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ypkHTrEI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x0hwrprcwm202bvolmd6.png" alt="Create a product in the store application" width="880" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt; &lt;/p&gt;

&lt;h3&gt;
  
  
  Trigger the CI/CD pipeline with a GitHub push &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;For testing the workflow, make a code change in the gateway. Edit &lt;code&gt;src/main/webapp/content/scss/_bootstrap-variables.scss&lt;/code&gt; and update the following variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nv"&gt;$body-bg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;steelblue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, the pipeline under test will only trigger if a new image tag is detected. So edit &lt;code&gt;.circleci/confg.yml&lt;/code&gt; and update the image name:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;IMAGE_NAME&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;indiepopart/gateway:v1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Commit and push the change to the &lt;code&gt;main&lt;/code&gt; branch, and watch the CircleCI CI pipeline triggers. After the new gateway image is pushed to Docker Hub, watch the Spinnaker CD pipeline trigger and deploy the updated gateway. On the left menu choose &lt;strong&gt;Clusters&lt;/strong&gt;, and verify the active gateway deployment now has a &lt;em&gt;V002&lt;/em&gt;. If you click over the &lt;strong&gt;V002&lt;/strong&gt; box, you can also verify the image tag that was deployed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--NyW8Ca6e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qeftd2t2v4u1y2n5wqpc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NyW8Ca6e--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qeftd2t2v4u1y2n5wqpc.png" alt="Spinnaker deployment version 2" width="880" height="404"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can delete clusters in between sessions to save costs with the following &lt;code&gt;gcloud&lt;/code&gt; commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters delete &amp;lt;cluster-name&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--zone&lt;/span&gt; &amp;lt;cluster-zone&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cloud storage used by Spinnaker for pipeline persistence generates some costs, you can also delete it with the CLI or using the web console.&lt;/p&gt;

&lt;h3&gt;
  
  
  Inspect Spinnaker logs &lt;a&gt;&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;For pipeline and trigger debugging, the Spinnaker services &lt;code&gt;spin-echo&lt;/code&gt; and &lt;code&gt;spin-igor&lt;/code&gt; inform Docker monitoring events and indicate the reasons why an execution was skipped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; spinnaker
kubectl logs spin-igor-7c8bdd94f5-lx5dl &lt;span class="nt"&gt;-n&lt;/span&gt; spinnaker | &lt;span class="nb"&gt;grep &lt;/span&gt;v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Found 1 new images for docker-account. Images: [{imageId=igor:dockerRegistry:v2:docker-account:indiepopart/gateway:v1, sendEvent=true}]
New tagged image: account=docker-account, image=igor:dockerRegistry:v2:docker-account:indiepopart/gateway:v1. Digest is now [null].
Sending tagged image info to echo: account=docker-account: image=igor:dockerRegistry:v2:docker-account:indiepopart/gateway:v1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Spinnaker features and best practices &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;When implementing continuous delivery, here are some best practices and key features to be aware of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Deploy Docker images by digest&lt;/strong&gt;: Spinnaker documentation recommends deploying images by digest instead of tag, because the tag might reference different content each time. The digest is a content-based hash of the image and it uniquely identifies it. Spinnaker's artifact substitution allows deploying the same manifest, with the image digest supplied by the pipeline trigger. Using the proposed trigger and a free Docker Hub account as the registry, the digest data seems not to be available. It is also recommended to trigger a CD pipeline off of a push to a Docker registry instead of a GitHub push or Jenkins job, allowing development teams to choose their delivery process without more constraints.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollbacks&lt;/strong&gt;: Just as there is a stage for deploying manifests, there are also stages for deleting, scaling, and rolling back manifests. Spinnaker also supports automated rollbacks, as it exposes the &lt;em&gt;Undo Rollout&lt;/em&gt; functionality as a stage, which can be configured to run when other stages fail, and to roll back by a number of revisions of the deployed object. ConfigMaps and Secrets must be versioned for the automated rollback to actually roll back code or configuration.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manual judgments&lt;/strong&gt;: The pipeline can include a manual judgment stage, that will make the execution interrupt, asking for user input to continue or cancel. This can be used to ask for confirmation before promoting a staging deployment to production.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pipeline management&lt;/strong&gt;: Spinnaker represents pipelines as JSON behind the scenes, and maintains a revision history of the changes made to the pipeline. It also supports pipeline templates, that can be managed through the &lt;code&gt;spin CLI&lt;/code&gt;, and help with pipeline sharing and distribution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Canary&lt;/strong&gt;: You can automate Canary analysis by creating canary stages for your pipeline. Canary must be enabled in the Spinnaker installation using &lt;code&gt;hal&lt;/code&gt; commands. The Canary analysis consists of the evaluation of metrics chosen during configuration, comparing a partial rollout with the current deployment. The stage will fail based on the deviation criteria configured for the metric.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secrets management&lt;/strong&gt;: Spinnaker does not directly support secrets management. Secrets should be encrypted at rest and in transit. Credentials encryption for application secrets and Kubernetes secrets was covered in a recent post: &lt;a href="https://dev.to/blog/2021/06/01/kubernetes-spring-boot-jhipster"&gt;Kubernetes to the Cloud with Spring Boot and JHipster&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rollout strategies&lt;/strong&gt;: The Spinnaker Kubernetes provider supports running dark, highlander, and red/black rollouts. In the Deploy Manifest stage, there is a strategy option that allows associating workload to a service, sending traffic to it, and choosing how to handle any previous versions of the workload in the same cluster and namespace.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Learn more about JHipster, microservices, and Kubernetes &lt;a&gt;&lt;/a&gt;
&lt;/h2&gt;

&lt;p&gt;This article looks at the nuts and bolts of JHipster deployments. CI and CD propose several organizational and technical practices aimed at improving team confidence, efficiency, and productivity. Spinnaker is a powerful tool for continuous deployment, with more than 200 &lt;a href="https://spinnaker.io/docs/community/stay-informed/captains-log/#contributions-per-company"&gt;companies&lt;/a&gt; around the world contributing to its growth. &lt;/p&gt;

&lt;p&gt;As each architecture and organization is different, your pipeline design must be customized to your particular use case. I hope this brief introduction will help you get the most out of these wonderful tools. &lt;/p&gt;

&lt;p&gt;To learn more about JHipster and its microservices support, check out the following links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/07/11/kubernetes-jhipster-aws"&gt;JHipster Microservices on AWS with Amazon Elastic Kubernetes Service&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/06/06/microservices-digitalocean-kubernetes"&gt;Run Microservices on DigitalOcean with Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/05/05/kubernetes-microservices-azure"&gt;Kubernetes Microservices on Azure with Cosmos DB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/06/01/kubernetes-spring-boot-jhipster"&gt;Kubernetes to the Cloud with Spring Boot and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be sure to follow us on &lt;a href="https://twitter.com/oktadev"&gt;Twitter&lt;/a&gt; and subscribe to our &lt;a href="https://youtube.com/c/oktadev"&gt;YouTube Channel&lt;/a&gt; so that you never miss any of our excellent content!&lt;/p&gt;

</description>
      <category>java</category>
      <category>jhipster</category>
      <category>circleci</category>
      <category>spinnaker</category>
    </item>
    <item>
      <title>How to Deploy JHipster Microservices on Amazon EKS Using Terraform and Kubernetes</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Tue, 05 Jul 2022 13:50:34 +0000</pubDate>
      <link>https://forem.com/jhipster/how-to-deploy-jhipster-microservices-on-amazon-eks-using-terraform-and-kubernetes-49a5</link>
      <guid>https://forem.com/jhipster/how-to-deploy-jhipster-microservices-on-amazon-eks-using-terraform-and-kubernetes-49a5</guid>
      <description>&lt;p&gt;When it comes to infrastructure, public clouds are the most popular choice these days, especially Amazon Web Services (AWS). If you are in one of those lucky or unlucky (depending on how you see it) teams running microservices, then you need a way to orchestrate their deployments. When it comes to orchestrating microservices, Kubernetes is the de-facto choice. Most public cloud providers also provide managed Kubernetes as a service; for example, Google provides Google Kubernetes Engine (GKE), Microsoft provides Azure Kubernetes Service (AKS), and Amazon provides Amazon Elastic Kubernetes Service (EKS).&lt;/p&gt;

&lt;p&gt;This doesn't mean that deploying and managing microservices on the public cloud is easy; each cloud service comes with its own challenges and pain. This is especially true for Amazon EKS, which, in my opinion, is the most difficult Kubernetes service to use, but also one of the most flexible. This is because EKS consists of some clever orchestrations doing a complex dance on top of other AWS services like EC2, EBS, etc.&lt;/p&gt;

&lt;p&gt;If you want to run a microservice stack on EKS, you will need to spend some extra time and effort setting it up and managing it. This is where infrastructure as code (IaC) tools like &lt;a href="https://www.terraform.io/" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; come in handy.&lt;/p&gt;

&lt;p&gt;So here is what you will learn to do today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Scaffold a Java microservice stack using JHipster, Spring Boot, and Spring Cloud&lt;/li&gt;
&lt;li&gt;Create an EKS cluster, Virtual Private Cloud (VPC), subnets, and required Kubernetes add-ons using Terraform on AWS&lt;/li&gt;
&lt;li&gt;Set up OIDC authentication for the microservice stack using Okta&lt;/li&gt;
&lt;li&gt;Build and deploy the microservice stack to the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://portal.aws.amazon.com/billing/signup" rel="noopener noreferrer"&gt;AWS account&lt;/a&gt; with the &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/security_iam_id-based-policy-examples.html" rel="noopener noreferrer"&gt;IAM permissions to create EKS clusters&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS CLI &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html" rel="noopener noreferrer"&gt;installed&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-quickstart.html" rel="noopener noreferrer"&gt;configured&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/install-aws-iam-authenticator.html" rel="noopener noreferrer"&gt;AWS IAM Authenticator&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/tools/" rel="noopener noreferrer"&gt;kubectl&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; installed and configured&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.terraform.io/downloads" rel="noopener noreferrer"&gt;Terraform&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sdkman.io/usage" rel="noopener noreferrer"&gt;Java 11+&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cli.okta.com/" rel="noopener noreferrer"&gt;Okta CLI&lt;/a&gt; installed&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jhipster.tech/installation/" rel="noopener noreferrer"&gt;JHipster&lt;/a&gt; CLI installed&lt;/li&gt;
&lt;li&gt;[Optional] &lt;a href="https://github.com/kdash-rs/kdash" rel="noopener noreferrer"&gt;KDash&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why Terraform, why not CloudFormation?
&lt;/h2&gt;

&lt;p&gt;At this point, the first question that might pop up in your mind would be, "Why not use &lt;a href="https://aws.amazon.com/cloudformation/" rel="noopener noreferrer"&gt;CloudFormation&lt;/a&gt;?". It's a good question; after all, CloudFormation is built by AWS and hence sounds like an excellent solution to manage AWS resources. But anyone who has tried both CloudFormation and Terraform will probably tell you to forget that CloudFormation even exists. I think CloudFormation is far more complex and less developer-friendly than Terraform. You also need to write a lot more boilerplate with CloudFormation in YAML or JSON. Yikes! And most importantly, Terraform is far more powerful and flexible than CloudFormation. It's cross-platform, which means you can take care of all your infrastructure management needs on any platform with one tool.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaffold a Java microservice stack using JHipster
&lt;/h2&gt;

&lt;p&gt;You need a microservice stack to deploy to the cluster. I'm using a microservice stack scaffolded for demo purposes using &lt;a href="https://www.jhipster.tech" rel="noopener noreferrer"&gt;JHipster&lt;/a&gt;. You can use another microservice stack if you want. If you prefer using the same application as in this demo, then you can either scaffold it using JHipster &lt;a href="https://www.jhipster.tech/jdl/intro" rel="noopener noreferrer"&gt;JDL&lt;/a&gt; or clone the sample repository from &lt;a href="https://github.com/oktadev/okta-jhipster-k8s-eks-microservices-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lbqsamb41i5wpmuht5c.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1lbqsamb41i5wpmuht5c.jpg" alt="JHipster microservice architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Option 1: Scaffold the microservice stack using JHipster&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;mkdir &lt;/span&gt;jhipster-microservice-stack
&lt;span class="nb"&gt;cd &lt;/span&gt;jhipster-microservice-stack
&lt;span class="c"&gt;# download the JDL file.&lt;/span&gt;
jhipster download https://raw.githubusercontent.com/oktadev/okta-jhipster-k8s-eks-microservices-example/main/apps.jdl
&lt;span class="c"&gt;# Update the `dockerRepositoryName` property to use your Docker Repository URI/Name.&lt;/span&gt;
&lt;span class="c"&gt;# scaffold the apps.&lt;/span&gt;
jhipster jdl apps.jdl


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Option 2: Clone the sample repository&lt;/strong&gt;&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;

git clone https://github.com/oktadev/okta-jhipster-k8s-eks-microservices-example


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In either case, remember to change the Docker repository name to match your Docker repository.&lt;/p&gt;

&lt;p&gt;The JHipster scaffolded sample application has a gateway application, two microservices, and uses &lt;a href="https://www.jhipster.tech/jhipster-registry/" rel="noopener noreferrer"&gt;JHipster Registry&lt;/a&gt; for service discovery and centralized configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create an EKS cluster using Terraform
&lt;/h2&gt;

&lt;p&gt;Now let us move on to the important part of the tutorial. Creating an EKS cluster in AWS is not as straightforward as in Google Cloud Platform (GCP). You need to also create a lot more resources for everything to work correctly without surprises. You will be using a bunch of Terraform providers to help with this, and you will also use some prebuilt Terraform modules like &lt;a href="https://github.com/terraform-aws-modules/terraform-aws-vpc" rel="noopener noreferrer"&gt;AWS VPC Terraform module&lt;/a&gt; and &lt;a href="https://github.com/aws-ia/terraform-aws-eks-blueprints" rel="noopener noreferrer"&gt;Amazon EKS Blueprints for Terraform&lt;/a&gt; to reduce the amount of boilerplate you need to write.&lt;/p&gt;

&lt;p&gt;These are the AWS resources and VPC architecture you will create:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wk8rdzsjdbub2um2s76.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8wk8rdzsjdbub2um2s76.jpg" alt="AWS EKS and VPC architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the Terraform configuration
&lt;/h3&gt;

&lt;p&gt;First, make sure you use a specific version of the providers as different versions might use different attributes and features. Create a &lt;code&gt;versions.tf&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;mkdir &lt;/span&gt;terraform
&lt;span class="nb"&gt;cd &lt;/span&gt;terraform
&lt;span class="nb"&gt;touch &lt;/span&gt;versions.tf


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add the following to the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

&lt;span class="nx"&gt;terraform&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;required_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 1.0.0"&lt;/span&gt;

  &lt;span class="nx"&gt;required_providers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;aws&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/aws"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 3.72"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;kubernetes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/kubernetes"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 2.10"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;helm&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/helm"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 2.4.1"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;random&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/random"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 3.2.0"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;nullres&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"hashicorp/null"&lt;/span&gt;
      &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"&amp;gt;= 3.1"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, you need to define variables and configure the providers. Create a &lt;code&gt;config.tf&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;touch &lt;/span&gt;config.tf


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add the following to the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

&lt;span class="c1"&gt;# ##  To save state in s3. Update to suit your needs&lt;/span&gt;
&lt;span class="c1"&gt;# backend "s3" {&lt;/span&gt;
&lt;span class="c1"&gt;#   bucket = "create-an-s3-bucket-and-provide-name-here"&lt;/span&gt;
&lt;span class="c1"&gt;#   region = local.region&lt;/span&gt;
&lt;span class="c1"&gt;#   key    = "eks-cluster-with-new-vpc/terraform.tfstate"&lt;/span&gt;
&lt;span class="c1"&gt;# }&lt;/span&gt;

&lt;span class="nx"&gt;variable&lt;/span&gt; &lt;span class="s2"&gt;"region"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;default&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1"&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"AWS region"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"random_string"&lt;/span&gt; &lt;span class="s2"&gt;"suffix"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;length&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;
  &lt;span class="nx"&gt;special&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="s2"&gt;"aws_availability_zones"&lt;/span&gt; &lt;span class="s2"&gt;"available"&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

&lt;span class="nx"&gt;locals&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"okta-jhipster-eks-${random_string.suffix.result}"&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;var&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"1.22"&lt;/span&gt;
  &lt;span class="nx"&gt;instance_types&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"t2.large"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;# can be multiple, comma separated&lt;/span&gt;

  &lt;span class="nx"&gt;vpc_cidr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
  &lt;span class="nx"&gt;azs&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_availability_zones&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;available&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;names&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;Blueprint&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="nx"&gt;GitHubRepo&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github.com/aws-ia/terraform-aws-eks-blueprints"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;region&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Kubernetes provider&lt;/span&gt;
&lt;span class="c1"&gt;# You should **not** schedule deployments and services in this workspace.&lt;/span&gt;
&lt;span class="c1"&gt;# This keeps workspaces modular (one for provision EKS, another for scheduling&lt;/span&gt;
&lt;span class="c1"&gt;# Kubernetes resources) as per best practices.&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"kubernetes"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;host&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_endpoint&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_certificate_authority_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="nx"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;api_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"client.authentication.k8s.io/v1alpha1"&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt;
    &lt;span class="c1"&gt;# This requires the awscli to be installed locally where Terraform is executed&lt;/span&gt;
    &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"eks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"get-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--cluster-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"helm"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;kubernetes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;host&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_endpoint&lt;/span&gt;
    &lt;span class="nx"&gt;cluster_ca_certificate&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;base64decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_certificate_authority_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="nx"&gt;exec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;api_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"client.authentication.k8s.io/v1alpha1"&lt;/span&gt;
      &lt;span class="nx"&gt;command&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt;
      &lt;span class="c1"&gt;# This requires the awscli to be installed locally where Terraform is executed&lt;/span&gt;
      &lt;span class="nx"&gt;args&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"eks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"get-token"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"--cluster-name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can uncomment the &lt;code&gt;backend&lt;/code&gt; section above to save state in S3 instead of your local filesystem. This is recommended for production setup so that everyone in a team has the same state. This file defines configurable and local variables used across the workspace and configures some of the providers used. The Kubernetes provider is included in this file so the EKS module can complete successfully. Otherwise, it throws an error when creating &lt;code&gt;kubernetes_config_map.aws_auth&lt;/code&gt;. The helm provider is used to install Kubernetes add-ons to the cluster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the VPC
&lt;/h3&gt;

&lt;p&gt;Next up, you need a VPC, subnets, route tables, and other networking bits. You will use the &lt;code&gt;vpc&lt;/code&gt; module from the &lt;a href="https://github.com/terraform-aws-modules" rel="noopener noreferrer"&gt;&lt;code&gt;terraform-aws-modules&lt;/code&gt;&lt;/a&gt; repository. This module is a wrapper around the AWS VPC module. It makes it easier to configure VPCs and all the other required networking resources. Create a &lt;code&gt;vpc.tf&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;touch &lt;/span&gt;vpc.tf


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add the following to the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# VPC, Subnets, Internet gateway, Route tables, etc.&lt;/span&gt;
&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"vpc"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"terraform-aws-modules/vpc/aws"&lt;/span&gt;
  &lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"~&amp;gt; 3.0"&lt;/span&gt;

  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;cidr&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;

  &lt;span class="nx"&gt;azs&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azs&lt;/span&gt;
  &lt;span class="nx"&gt;public_subnets&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azs&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cidrsubnet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="nx"&gt;private_subnets&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;for&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="nx"&gt;in&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;azs&lt;/span&gt; &lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;cidrsubnet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;k&lt;/span&gt; &lt;span class="err"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;

  &lt;span class="nx"&gt;enable_nat_gateway&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;single_nat_gateway&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_dns_hostnames&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# Manage so we can name&lt;/span&gt;
  &lt;span class="nx"&gt;manage_default_network_acl&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;default_network_acl_tags&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name}-default"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;manage_default_route_table&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;default_route_table_tags&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name}-default"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;manage_default_security_group&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;default_security_group_tags&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${local.name}-default"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;public_subnet_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/cluster/${local.name}"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"shared"&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/role/elb"&lt;/span&gt;              &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;private_subnet_tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/cluster/${local.name}"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"shared"&lt;/span&gt;
    &lt;span class="s2"&gt;"kubernetes.io/role/internal-elb"&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This will create:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new VPC, three private subnets, and three public subnets,&lt;/li&gt;
&lt;li&gt;Internet gateway and NAT gateway for the public subnets,&lt;/li&gt;
&lt;li&gt;and AWS routes for the gateways, public/private route tables, and route table associations.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Build the EKS cluster
&lt;/h3&gt;

&lt;p&gt;Now that you have the networking part done, you can build configurations for the EKS cluster and its add-ons. You will use the &lt;code&gt;eks_blueprints&lt;/code&gt; module from &lt;a href="https://aws-ia.github.io/terraform-aws-eks-blueprints/v4.0.9/" rel="noopener noreferrer"&gt;&lt;code&gt;terraform-aws-eks-blueprints&lt;/code&gt;&lt;/a&gt;, which is a wrapper around the &lt;a href="https://github.com/terraform-aws-modules" rel="noopener noreferrer"&gt;&lt;code&gt;terraform-aws-modules&lt;/code&gt;&lt;/a&gt; and provides additional modules to configure EKS add-ons.&lt;/p&gt;

&lt;p&gt;Create a &lt;code&gt;eks-cluster.tf&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;touch &lt;/span&gt;eks-cluster.tf


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add the following to the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;# EKS cluster, worker nodes, security groups, IAM roles, K8s add-ons, etc.&lt;/span&gt;
&lt;span class="c1"&gt;#---------------------------------------------------------------&lt;/span&gt;
&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"eks_blueprints"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github.com/aws-ia/terraform-aws-eks-blueprints?ref=v4.0.9"&lt;/span&gt;

  &lt;span class="nx"&gt;cluster_name&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;cluster_version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cluster_version&lt;/span&gt;

  &lt;span class="nx"&gt;vpc_id&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_id&lt;/span&gt;
  &lt;span class="nx"&gt;private_subnet_ids&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;

  &lt;span class="nx"&gt;managed_node_groups&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;node&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;node_group_name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"managed-ondemand"&lt;/span&gt;
      &lt;span class="nx"&gt;instance_types&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;instance_types&lt;/span&gt;
      &lt;span class="nx"&gt;min_size&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;
      &lt;span class="nx"&gt;subnet_ids&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt; &lt;span class="s2"&gt;"eks_blueprints_kubernetes_addons"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github.com/aws-ia/terraform-aws-eks-blueprints//modules/kubernetes-addons?ref=v4.0.9"&lt;/span&gt;

  &lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;
  &lt;span class="nx"&gt;eks_cluster_endpoint&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_endpoint&lt;/span&gt;
  &lt;span class="nx"&gt;eks_oidc_provider&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;oidc_provider&lt;/span&gt;
  &lt;span class="nx"&gt;eks_cluster_version&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_version&lt;/span&gt;

  &lt;span class="c1"&gt;# EKS Managed Add-ons&lt;/span&gt;
  &lt;span class="nx"&gt;enable_amazon_eks_vpc_cni&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_amazon_eks_coredns&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_amazon_eks_kube_proxy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

  &lt;span class="c1"&gt;# K8S Add-ons&lt;/span&gt;
  &lt;span class="nx"&gt;enable_aws_load_balancer_controller&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_metrics_server&lt;/span&gt;               &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_cluster_autoscaler&lt;/span&gt;           &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="nx"&gt;enable_aws_cloudwatch_metrics&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;

  &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tags&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# To update local kubeconfig with new cluster details&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"null_resource"&lt;/span&gt; &lt;span class="s2"&gt;"kubeconfig"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;depends_on&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints_kubernetes_addons&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;provisioner&lt;/span&gt; &lt;span class="s2"&gt;"local-exec"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws eks --region ${local.region}  update-kubeconfig --name $AWS_CLUSTER_NAME"&lt;/span&gt;
    &lt;span class="nx"&gt;environment&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;AWS_CLUSTER_NAME&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;local&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The &lt;code&gt;eks_blueprints&lt;/code&gt; module definition creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;EKS Cluster Control plane with one &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/managed-node-groups.html" rel="noopener noreferrer"&gt;managed node group&lt;/a&gt; and &lt;a href="https://docs.aws.amazon.com/eks/latest/userguide/fargate-profile.html" rel="noopener noreferrer"&gt;fargate profile&lt;/a&gt;,&lt;/li&gt;
&lt;li&gt;Cluster and node security groups and rules, IAM roles and policies required,&lt;/li&gt;
&lt;li&gt;and AWS Key Management Service (KMS) configuration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;eks_blueprints_kubernetes_addons&lt;/code&gt; module definition creates:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Amazon EKS add-ons vpc-cni, CoreDNS, and kube-proxy,&lt;/li&gt;
&lt;li&gt;AWS Load Balancer Controller that provisions an AWS Network Load Balancer for distributing traffic,&lt;/li&gt;
&lt;li&gt;and &lt;a href="https://github.com/kubernetes-sigs/metrics-server" rel="noopener noreferrer"&gt;Metrics Server&lt;/a&gt;, and Cluster Autoscaler for scaling your workloads.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;code&gt;null_resource&lt;/code&gt; configuration updates your local kubeconfig with the new cluster details. It's not a required step for provisioning but just a handy hack.&lt;/p&gt;

&lt;p&gt;Finally, you can also define some outputs to be captured. Create a &lt;code&gt;outputs.tf&lt;/code&gt; file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;touch &lt;/span&gt;outputs.tf


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Add the following to the file:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_private_subnet_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VPC private subnet CIDR"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;private_subnets_cidr_blocks&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_public_subnet_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VPC public subnet CIDR"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;public_subnets_cidr_blocks&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"vpc_cidr"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"VPC CIDR"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vpc_cidr_block&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_cluster_id"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS cluster ID"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroups"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node groups"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed_node_groups&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroup_ids"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node group ids"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed_node_groups_id&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroup_arns"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node group arns"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed_node_group_arn&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroup_role_name"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node group role name"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed_node_group_iam_role_names&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"eks_managed_nodegroup_status"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"EKS managed node group status"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;managed_node_groups_status&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="s2"&gt;"configure_kubectl"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Configure kubectl: make sure you're logged in with the correct AWS profile and run the following command to update your kubeconfig"&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;eks_blueprints&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;configure_kubectl&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Provision the cluster
&lt;/h3&gt;

&lt;p&gt;Our Terraform definitions are ready. Now you can provision the cluster. First, initialize Terraform workspace and plan the changes:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# download modules and providers. Initialize state.&lt;/span&gt;
terraform init
&lt;span class="c"&gt;# see a preview of what will be done&lt;/span&gt;
terraform plan


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Review the plan and make sure everything is correct. Ensure you have configured your AWS CLI and IAM Authenticator to use the correct AWS account. If not, run the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="c"&gt;# Visit https://console.aws.amazon.com/iam/home?#/security_credentials for creating access keys&lt;/span&gt;
aws configure


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now you can apply the changes:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

terraform apply


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Confirm by typing &lt;code&gt;yes&lt;/code&gt; when prompted. This will take a while (15-20 minutes), so sit back and have a coffee or contemplate what led you to this point in life. 😉&lt;/p&gt;

&lt;p&gt;Once the EKS cluster is ready, you will see the output variables printed to the console.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;

&lt;span class="nx"&gt;configure_kubectl&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"aws eks --region eu-west-1 update-kubeconfig --name okta-tf-demo"&lt;/span&gt;
&lt;span class="nx"&gt;eks_cluster_id&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"okta-tf-demo"&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroup_arns&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"arn:aws:eks:eu-west-1:216713166862:nodegroup/okta-tf-demo/managed-ondemand-20220610125341399700000010/f0c0a6d6-b8e1-cf91-3d21-522552d6bc2e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroup_ids&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"okta-tf-demo:managed-ondemand-20220610125341399700000010"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroup_role_name&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"okta-tf-demo-managed-ondemand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroup_status&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;eks_managed_nodegroups&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tolist&lt;/span&gt;&lt;span class="err"&gt;(&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"node"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"arn:aws:eks:eu-west-1:216713166862:nodegroup/okta-tf-demo/managed-ondemand-20220610125341399700000010/f0c0a6d6-b8e1-cf91-3d21-522552d6bc2e"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_iam_instance_profile_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"arn:aws:iam::216713166862:instance-profile/okta-tf-demo-managed-ondemand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_iam_instance_profile_id"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"okta-tf-demo-managed-ondemand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_iam_role_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"arn:aws:iam::216713166862:role/okta-tf-demo-managed-ondemand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_iam_role_name"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"okta-tf-demo-managed-ondemand"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_id"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"okta-tf-demo:managed-ondemand-20220610125341399700000010"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_launch_template_arn"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_launch_template_id"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_launch_template_latest_version"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="s2"&gt;"managed_nodegroup_status"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s2"&gt;"ACTIVE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="err"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"eu-west-1"&lt;/span&gt;
&lt;span class="nx"&gt;vpc_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"10.0.0.0/16"&lt;/span&gt;
&lt;span class="nx"&gt;vpc_private_subnet_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.10.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.11.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.12.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nx"&gt;vpc_public_subnet_cidr&lt;/span&gt; &lt;span class="err"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.0.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.1.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;"10.0.2.0/24"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You should see the cluster details if you run &lt;code&gt;kdash&lt;/code&gt; or &lt;code&gt;kubectl get nodes&lt;/code&gt; commands.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4ukr6v224k21n7whazi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn4ukr6v224k21n7whazi.png" alt="EKS cluster in KDash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The EKS cluster defined here will not come under AWS free tier; hence, running this will cost money, so delete the cluster as soon as you finish the tutorial to keep the cost within a few dollars.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Set up OIDC authentication using Okta
&lt;/h2&gt;

&lt;p&gt;You can proceed to deploy the sample application. You could skip this step if you used a sample that does not use Okta or OIDC for authentication.&lt;/p&gt;

&lt;p&gt;First, navigate to the &lt;strong&gt;store&lt;/strong&gt; application folder.&lt;/p&gt;

&lt;p&gt;Before you begin, you’ll need a free Okta developer account. Install the &lt;a href="https://cli.okta.com/" rel="noopener noreferrer"&gt;Okta CLI&lt;/a&gt; and run &lt;code&gt;okta register&lt;/code&gt; to sign up for a new account. If you already have an account, run &lt;code&gt;okta login&lt;/code&gt;. Then, run &lt;code&gt;okta apps create jhipster&lt;/code&gt;. Select the default app name, or change it as you see fit. Accept the default Redirect URI values provided for you.&lt;/p&gt;

&lt;p&gt;The Okta CLI streamlines configuring a JHipster app and does several things for you:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Creates an OIDC app with the correct redirect URIs:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;login: &lt;code&gt;http://localhost:8080/login/oauth2/code/oidc&lt;/code&gt; and &lt;code&gt;http://localhost:8761/login/oauth2/code/oidc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;logout: &lt;code&gt;http://localhost:8080&lt;/code&gt; and &lt;code&gt;http://localhost:8761&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Creates &lt;code&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code&gt;ROLE_USER&lt;/code&gt; groups that JHipster expects&lt;/li&gt;
&lt;li&gt;Adds your current user to the &lt;code&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code&gt;ROLE_USER&lt;/code&gt; groups&lt;/li&gt;
&lt;li&gt;Creates a groups claim in your default authorization server and adds the user’s groups to it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The &lt;a href="http://localhost:8761%5C*" rel="noopener noreferrer"&gt;http://localhost:8761\*&lt;/a&gt; redirect URIs are for the JHipster Registry, which is often used when creating microservices with JHipster. The Okta CLI adds these by default.&lt;/p&gt;

&lt;p&gt;The OIDC app configuration will be written to &lt;code&gt;.okta.env&lt;/code&gt; file in the folder where you ran the command.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Make sure to add the newly created &lt;code&gt;.okta.env&lt;/code&gt; file to your &lt;code&gt;.gitignore&lt;/code&gt; file so that you don't accidentally expose your credentials to the public.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Update &lt;code&gt;kubernetes/registry-k8s/application-configmap.yml&lt;/code&gt; with the OIDC configuration from the &lt;code&gt;.okta.env&lt;/code&gt; file. The Spring Cloud Config server reads from this file and shares the values with the gateway and microservices.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;application.yml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;spring:&lt;/span&gt;
      &lt;span class="s"&gt;security:&lt;/span&gt;
        &lt;span class="s"&gt;oauth2:&lt;/span&gt;
          &lt;span class="s"&gt;client:&lt;/span&gt;
            &lt;span class="s"&gt;provider:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;issuer-uri: https://&amp;lt;your-okta-domain&amp;gt;/oauth2/default&lt;/span&gt;
            &lt;span class="s"&gt;registration:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;client-id: &amp;lt;client-id&amp;gt;&lt;/span&gt;
                &lt;span class="s"&gt;client-secret: &amp;lt;client-secret&amp;gt;&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, configure the JHipster Registry to use OIDC for authentication. Modify &lt;code&gt;kubernetes/registry-k8s/jhipster-registry.yml&lt;/code&gt; to enable the &lt;code&gt;oauth2&lt;/code&gt; profile, which is preconfigured in the JHipster Registry application.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_PROFILES_ACTIVE&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod,k8s,oauth2&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The application is now ready.&lt;/p&gt;

&lt;h3&gt;
  
  
  Secure secrets
&lt;/h3&gt;

&lt;p&gt;If you have noticed, you are setting secrets in plain text on the &lt;code&gt;application-configmap.yml&lt;/code&gt; file, which is not ideal and is not a best practice for security. For the specific JHipster application, you can use the encrypt functionality provided by the JHipster Registry to encrypt the secrets. See &lt;a href="https://developer.okta.com/blog/2021/06/01/kubernetes-spring-boot-jhipster#encrypt-your-secrets-with-spring-cloud-config" rel="noopener noreferrer"&gt;Encrypt Your Secrets with Spring Cloud Config&lt;/a&gt; to learn how to do this. But that would also rely on a base64 encoded encryption key added as a Kubernetes Secret, which still can be decoded. The best way to do this would be to use &lt;a href="https://aws.amazon.com/secrets-manager/" rel="noopener noreferrer"&gt;AWS Secrets Manager&lt;/a&gt;, an external service like &lt;a href="https://www.hashicorp.com/products/vault" rel="noopener noreferrer"&gt;HashiCorp Vault&lt;/a&gt;, or &lt;a href="https://github.com/bitnami-labs/sealed-secrets" rel="noopener noreferrer"&gt;Sealed Secrets&lt;/a&gt;. To learn more about these methods see &lt;a href="https://developer.okta.com/blog/2021/06/01/kubernetes-spring-boot-jhipster#encrypt-your-kubernetes-secrets" rel="noopener noreferrer"&gt;Encrypt Your Kubernetes Secrets&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy the microservice stack
&lt;/h2&gt;

&lt;p&gt;You are ready to deploy to our shiny new EKS cluster, but first, you need to build and push the Docker images to a container registry. You can use &lt;a href="https://aws.amazon.com/ecr/" rel="noopener noreferrer"&gt;Amazon Elastic Container Registry (ECR)&lt;/a&gt; or any other container registry.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build the Docker images
&lt;/h3&gt;

&lt;p&gt;You need to build Docker images for each app. This is specific to the JHipster application used in this tutorial which uses &lt;a href="https://github.com/GoogleContainerTools/jib" rel="noopener noreferrer"&gt;Jib&lt;/a&gt; to build the images. Make sure you are logged into Docker using &lt;code&gt;docker login&lt;/code&gt;. Navigate to each app folder (&lt;strong&gt;store&lt;/strong&gt;, &lt;strong&gt;invoice&lt;/strong&gt;, &lt;strong&gt;product&lt;/strong&gt;) and run the following command:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;docker-repo-uri-or-name&amp;gt;/&amp;lt;image-name&amp;gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Image names should be &lt;code&gt;store&lt;/code&gt;, &lt;code&gt;invoice&lt;/code&gt;, and &lt;code&gt;product&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy the applications to EKS
&lt;/h3&gt;

&lt;p&gt;Start the deployment using the handy script provided by JHipster. You could also manually apply deployments using &lt;code&gt;kubectl apply -f &amp;lt;file&amp;gt;&lt;/code&gt; commands.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;span class="nb"&gt;cd &lt;/span&gt;kubernetes
./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzjl4sbkienmdyngbc7t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuzjl4sbkienmdyngbc7t.png" alt="EKS pods in KDash"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also run the following command to see the status of the deployments:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;View the registry using port-forwarding as follows, and you will be able to access the application at &lt;code&gt;http://localhost:8761&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubectl port-forward svc/jhipster-registry &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster 8761


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can access the gateway application using port-forwarding as follows, and you will be able to access the application at &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubectl port-forward svc/store &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster 8080


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Or, you can access the application via the load balancer exposed. Find the external IP of the &lt;code&gt;store&lt;/code&gt; service by navigating to the service tab in KDash or by running the following:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

kubectl get svc store &lt;span class="nt"&gt;-n&lt;/span&gt; jhipster


&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Navigate to the Okta Admin Console and go to &lt;strong&gt;Applications&lt;/strong&gt; &amp;gt; &lt;strong&gt;Applications&lt;/strong&gt; from left-hand navigation. Find the application you created earlier with &lt;code&gt;okta apps create jhipster&lt;/code&gt; and add the external IP from &lt;code&gt;kubectl get svc&lt;/code&gt; command to the &lt;strong&gt;Sign-in redirect URIs&lt;/strong&gt; and &lt;strong&gt;Sign-out redirect URIs&lt;/strong&gt;. Make sure to use the same paths as the current &lt;code&gt;localhost&lt;/code&gt; entries.&lt;/p&gt;

&lt;p&gt;Now you should be able to visit the external IP of the &lt;code&gt;store&lt;/code&gt; service on port 8080 and see the application, and you should be able to log in using your Okta credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tear down the cluster with Terraform
&lt;/h2&gt;

&lt;p&gt;Once you are done with the tutorial, you can delete the cluster and all the resources created using Terraform by running the following commands:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;

&lt;p&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;terraform&lt;br&gt;
&lt;span class="c"&gt;# The commands below might take a while to finish.&lt;/span&gt;&lt;br&gt;
terraform destroy &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module.eks_blueprints_kubernetes_addons"&lt;/span&gt; &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# If deleting VPC fails, then manually delete the load balancers and security groups&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# for the load balancer associated with the VPC from AWS EC2 console and try again.&lt;/span&gt;&lt;br&gt;
terraform destroy &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module.eks_blueprints"&lt;/span&gt; &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;&lt;br&gt;
terraform destroy &lt;span class="nt"&gt;-target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"module.vpc"&lt;/span&gt; &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;&lt;br&gt;
&lt;span class="c"&gt;# cleanup anything left over.&lt;/span&gt;&lt;br&gt;
terraform destroy &lt;span class="nt"&gt;-auto-approve&lt;/span&gt;&lt;/p&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
&lt;br&gt;
  &lt;br&gt;
  &lt;br&gt;
  Learn more about Java Microservices, EKS, Kubernetes, and JHipster&lt;br&gt;
&lt;/h2&gt;

&lt;p&gt;If you want to learn more about Kubernetes, OIDC, or using OIDC with Kubernetes, and security in general, check out these additional resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/06/09/cloud-native-java-microservices-with-istio" rel="noopener noreferrer"&gt;Cloud Native Java Microservices with JHipster and Istio&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/06/01/kubernetes-spring-boot-jhipster#encrypt-your-kubernetes-secrets" rel="noopener noreferrer"&gt;Kubernetes to the Cloud with Spring Boot and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/11/08/k8s-api-server-oidc" rel="noopener noreferrer"&gt;How to Secure Your Kubernetes Cluster with OpenID Connect and RBAC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/12/02/k8s-security-best-practices" rel="noopener noreferrer"&gt;How to Secure Your Kubernetes Clusters With Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/10/08/secure-access-to-aws-eks" rel="noopener noreferrer"&gt;Secure Access to AWS EKS Clusters for Admins&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/06/06/microservices-digitalocean-kubernetes" rel="noopener noreferrer"&gt;Run Microservices on DigitalOcean with Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/05/05/kubernetes-microservices-azure" rel="noopener noreferrer"&gt;Kubernetes Microservices on Azure with Cosmos DB&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find all the code from this example on &lt;a href="https://github.com/oktadev/okta-jhipster-k8s-eks-microservices-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you liked this tutorial, chances are you'll enjoy the others we publish. Please follow &lt;a href="https://twitter.com/oktadev" rel="noopener noreferrer"&gt;@oktadev on Twitter&lt;/a&gt; and &lt;a href="https://youtube.com/oktadev" rel="noopener noreferrer"&gt;subscribe to our YouTube channel&lt;/a&gt; to get notified when we publish new developer tutorials.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>jhipster</category>
      <category>eks</category>
      <category>terraform</category>
    </item>
    <item>
      <title>Cloud Native Java Microservices with JHipster and Istio</title>
      <dc:creator>Deepu K Sasidharan</dc:creator>
      <pubDate>Tue, 28 Jun 2022 10:24:09 +0000</pubDate>
      <link>https://forem.com/jhipster/cloud-native-java-microservices-with-jhipster-and-istio-1emb</link>
      <guid>https://forem.com/jhipster/cloud-native-java-microservices-with-jhipster-and-istio-1emb</guid>
      <description>&lt;p&gt;Microservices are not everyone's cup of tea, and they shouldn't be. Not every problem can or should be solved by microservices. Sometimes building a simple monolith is a far better option. Microservices are solutions for use cases where scale and scalability are important. A few years ago, microservices were all the rage, made popular, especially by companies like Netflix, Spotify, Google, etc. While the hype has died down a bit, genuine use cases still exist. With ongoing advances in cloud computing technologies, building microservices as cloud-native services is the way to go due to many benefits.&lt;/p&gt;

&lt;p&gt;Today we will look at building a cloud-native Java microservice stack that utilizes a service mesh to provide most of the distributed system needs and we'll deploy it to the cloud using Kubernetes.&lt;/p&gt;

&lt;p&gt;So here is what we will do today:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Build a Java microservice stack using JHipster, Spring Boot, and Spring Cloud&lt;/li&gt;
&lt;li&gt;Create a Google Kubernetes Engine (GKE) cluster&lt;/li&gt;
&lt;li&gt;Deploy Istio service mesh to the cluster&lt;/li&gt;
&lt;li&gt;Set up monitoring and observability&lt;/li&gt;
&lt;li&gt;Deploy and monitor the microservices to the cluster&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's get started!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A &lt;a href="https://cloud.google.com/" rel="noopener noreferrer"&gt;Google Cloud Platform&lt;/a&gt; account&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.docker.com/get-started" rel="noopener noreferrer"&gt;Docker&lt;/a&gt; installed on your machine&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/en/" rel="noopener noreferrer"&gt;Node.js&lt;/a&gt; installed on your machine&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.jhipster.tech/installation/" rel="noopener noreferrer"&gt;JHipster&lt;/a&gt; installed on your machine&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;Google Cloud SDK&lt;/a&gt; installed and configured on your machine&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/tools/" rel="noopener noreferrer"&gt;kubectl&lt;/a&gt; or &lt;a href="https://github.com/kdash-rs/kdash" rel="noopener noreferrer"&gt;KDash&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Basic understanding of Java, Spring, Containers, and Kubernetes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you prefer to follow along by watching a video, see [How to Build Low-Code Microservices on the Cloud Using Istio, JHipster, and Kubernetes] on the &lt;a href="https://youtube.com/oktadev" rel="noopener noreferrer"&gt;OktaDev YouTube channel&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/zGpnIhRgMaM"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Why build cloud-native microservices using a service mesh?
&lt;/h2&gt;

&lt;p&gt;Before we dive into building a cloud-native microservice stack, let's look at what a service mesh is and the benefits of using one.&lt;/p&gt;

&lt;p&gt;A service mesh provides features to help with common distributed microservice challenges. Like service discovery, routing, load balancing, and so on. Today we will be using &lt;a href="https://istio.io/" rel="noopener noreferrer"&gt;Istio&lt;/a&gt;, one of the most popular service mesh solutions available. Istio is tailored for distributed application architectures, especially those you might run in Kubernetes. Istio plays nicely with Kubernetes, so nicely that you might think that it's part of the Kubernetes platform itself. Istio isn't the only service mesh around; we also have platforms like &lt;a href="https://linkerd.io/" rel="noopener noreferrer"&gt;Linkerd&lt;/a&gt; and &lt;a href="https://www.consul.io/" rel="noopener noreferrer"&gt;Consul&lt;/a&gt;, which are also quite popular.&lt;/p&gt;

&lt;p&gt;Istio specifically provides the following features.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Secure service-to-service communication over TLS. Of course, with support for identity-based authentication and authorization.&lt;/li&gt;
&lt;li&gt;Service discovery, so that your microservices can discover each other.&lt;/li&gt;
&lt;li&gt;Automatic load balancing for the services&lt;/li&gt;
&lt;li&gt;Traffic control features like routing, circuit breaking, retries, fail-overs, and fault injection.&lt;/li&gt;
&lt;li&gt;A pluggable policy layer that can enforce stuff like access control, rate limiting, A/B testing, traffic splits, quotas, etc.&lt;/li&gt;
&lt;li&gt;It also provides automatic metrics, logs, and traces for all traffic within the cluster from Ingress to Egress and between pods.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  What is Istio Service Mesh?
&lt;/h3&gt;

&lt;p&gt;Let's take a quick look at Istio internals. The Istio architecture can be classified into two distinct planes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qhlxbs4n5145a97m231.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qhlxbs4n5145a97m231.png" alt="Istio Service Mesh Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Control plane&lt;/strong&gt;: It consists of the istiod demon, and it manages and configures the envoy proxies to route traffic. The control plane also enforces policies and collects telemetry, and includes components like Pilot for traffic management, Citadel to manage security, and Galley to manage configurations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Data plane&lt;/strong&gt;: It's made of &lt;a href="https://www.envoyproxy.io/" rel="noopener noreferrer"&gt;Envoy&lt;/a&gt; proxies deployed as sidecars to our application containers. Envoy is a high-performance, lightweight distributed proxy. It controls all the incoming and outgoing traffic to the container it is attached to.&lt;/p&gt;

&lt;p&gt;We can use tools like &lt;a href="https://grafana.com/" rel="noopener noreferrer"&gt;Grafana&lt;/a&gt;, &lt;a href="https://prometheus.io/" rel="noopener noreferrer"&gt;Prometheus&lt;/a&gt;, &lt;a href="https://www.kiali.io/" rel="noopener noreferrer"&gt;Kiali&lt;/a&gt; and &lt;a href="https://zipkin.io/" rel="noopener noreferrer"&gt;Zipkin&lt;/a&gt; for monitoring and observability as they work well with the telemetry provided by Istio. You can use these or use your existing monitoring stack as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build a Java Microservices Stack using JHipster
&lt;/h2&gt;

&lt;p&gt;Before you proceed, ensure you have installed JHipster. If not, install it using the command &lt;code&gt;npm -g install generator-jhipster&lt;/code&gt;. At the moment of writing, I'm using JHipster version &lt;strong&gt;7.8.1&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We will be using the &lt;a href="https://www.jhipster.tech/jdl/intro" rel="noopener noreferrer"&gt;JHipster Domain Language (JDL)&lt;/a&gt; to define our microservices, entities, and deployment options. But first, let's take a look at the architecture we will be building today.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nidl5ydet0tam4b61n6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7nidl5ydet0tam4b61n6.png" alt="Istio Microservice Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We have the Istio control plane taking care of policy, load balancing, etc. We also have the Istio Ingress gateway to route all external traffic to our applications. We have four microservices. First is a gateway application created by JHipster that acts as our React GUI and authentication layer. The remaining are services that provide APIs. Each of our containers will have an envoy proxy as an auto-injected sidecar. We hook up Grafana, Prometheus, Zipkin, and Kiali to the telemetry provided by Istio so that we have monitoring and observability for our cluster. Each microservice also has its own database.&lt;/p&gt;

&lt;p&gt;If you would prefer not to build the application yourself, clone the example from &lt;a href="https://github.com/oktadev/okta-java-spring-k8s-istio-microservices-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/oktadev/okta-java-spring-k8s-istio-microservices-example.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's not an overly complex architecture, but it's also not that simple. First, let us define our microservice using JDL. Create a file called &lt;code&gt;app.jdl&lt;/code&gt; and paste the following content into it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="nf"&gt;application&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;baseName&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;
    &lt;span class="n"&gt;applicationType&lt;/span&gt; &lt;span class="n"&gt;gateway&lt;/span&gt;
    &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;okta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;developer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;
    &lt;span class="n"&gt;serviceDiscoveryType&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
    &lt;span class="n"&gt;authenticationType&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
    &lt;span class="n"&gt;prodDatabaseType&lt;/span&gt; &lt;span class="n"&gt;postgresql&lt;/span&gt;
    &lt;span class="n"&gt;cacheProvider&lt;/span&gt; &lt;span class="n"&gt;hazelcast&lt;/span&gt;
    &lt;span class="n"&gt;buildTool&lt;/span&gt; &lt;span class="n"&gt;gradle&lt;/span&gt;
    &lt;span class="n"&gt;clientFramework&lt;/span&gt; &lt;span class="n"&gt;react&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;entities&lt;/span&gt; &lt;span class="p"&gt;*&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;application&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;baseName&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;
    &lt;span class="n"&gt;applicationType&lt;/span&gt; &lt;span class="n"&gt;microservice&lt;/span&gt;
    &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;okta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;developer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;
    &lt;span class="n"&gt;serviceDiscoveryType&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
    &lt;span class="n"&gt;authenticationType&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
    &lt;span class="n"&gt;prodDatabaseType&lt;/span&gt; &lt;span class="n"&gt;postgresql&lt;/span&gt;
    &lt;span class="n"&gt;cacheProvider&lt;/span&gt; &lt;span class="n"&gt;hazelcast&lt;/span&gt;
    &lt;span class="n"&gt;buildTool&lt;/span&gt; &lt;span class="n"&gt;gradle&lt;/span&gt;
    &lt;span class="n"&gt;serverPort&lt;/span&gt; &lt;span class="mi"&gt;8081&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;entities&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;application&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;baseName&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt;
    &lt;span class="n"&gt;applicationType&lt;/span&gt; &lt;span class="n"&gt;microservice&lt;/span&gt;
    &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;okta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;developer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;invoice&lt;/span&gt;
    &lt;span class="n"&gt;serviceDiscoveryType&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
    &lt;span class="n"&gt;authenticationType&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
    &lt;span class="n"&gt;prodDatabaseType&lt;/span&gt; &lt;span class="n"&gt;postgresql&lt;/span&gt;
    &lt;span class="n"&gt;buildTool&lt;/span&gt; &lt;span class="n"&gt;gradle&lt;/span&gt;
    &lt;span class="n"&gt;serverPort&lt;/span&gt; &lt;span class="mi"&gt;8082&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;entities&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;application&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;config&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;baseName&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;
    &lt;span class="n"&gt;applicationType&lt;/span&gt; &lt;span class="n"&gt;microservice&lt;/span&gt;
    &lt;span class="n"&gt;packageName&lt;/span&gt; &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;okta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;developer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;notification&lt;/span&gt;
    &lt;span class="n"&gt;serviceDiscoveryType&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
    &lt;span class="n"&gt;authenticationType&lt;/span&gt; &lt;span class="n"&gt;jwt&lt;/span&gt;
    &lt;span class="n"&gt;databaseType&lt;/span&gt; &lt;span class="n"&gt;mongodb&lt;/span&gt;
    &lt;span class="n"&gt;cacheProvider&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
    &lt;span class="n"&gt;enableHibernateCache&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
    &lt;span class="n"&gt;buildTool&lt;/span&gt; &lt;span class="n"&gt;gradle&lt;/span&gt;
    &lt;span class="n"&gt;serverPort&lt;/span&gt; &lt;span class="mi"&gt;8083&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="n"&gt;entities&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each application defines its name, package name, authentication type, database, etc. For all supported options and configurations, please refer to the &lt;a href="https://www.jhipster.tech/jdl/applications" rel="noopener noreferrer"&gt;JDL applications documentation&lt;/a&gt;. Each application also defines the &lt;code&gt;applicationType&lt;/code&gt; and the entities it serves. Next, add the entity definitions to the &lt;code&gt;app.jdl&lt;/code&gt; you just created.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="cm"&gt;/**
 * Entities for Store Gateway
 */&lt;/span&gt;
&lt;span class="c1"&gt;// Customer for the store&lt;/span&gt;
&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;firstName&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;lastName&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;gender&lt;/span&gt; &lt;span class="nc"&gt;Gender&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="nf"&gt;pattern&lt;/span&gt;&lt;span class="p"&gt;(/^[^&lt;/span&gt;&lt;span class="err"&gt;@\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]+&lt;/span&gt;&lt;span class="err"&gt;@&lt;/span&gt;&lt;span class="p"&gt;[^&lt;/span&gt;&lt;span class="err"&gt;@\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]+&lt;/span&gt;&lt;span class="err"&gt;\&lt;/span&gt;&lt;span class="p"&gt;.[^&lt;/span&gt;&lt;span class="err"&gt;@\&lt;/span&gt;&lt;span class="n"&gt;s&lt;/span&gt;&lt;span class="p"&gt;]+&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="p"&gt;/)&lt;/span&gt;
  &lt;span class="n"&gt;phone&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;addressLine1&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;addressLine2&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;country&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Gender&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;MALE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;FEMALE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OTHER&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;relationship&lt;/span&gt; &lt;span class="nc"&gt;OneToOne&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Customer&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;user&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;login&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;serviceClass&lt;/span&gt;
&lt;span class="n"&gt;paginate&lt;/span&gt; &lt;span class="nc"&gt;Customer&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pagination&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Entities for product microservice
 */&lt;/span&gt;
&lt;span class="c1"&gt;// Product sold by the Online store&lt;/span&gt;
&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;price&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;itemSize&lt;/span&gt; &lt;span class="nc"&gt;Size&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;image&lt;/span&gt; &lt;span class="nc"&gt;ImageBlob&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;Size&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;M&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;L&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;XL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;XXL&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;ProductCategory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;name&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;description&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;ProductOrder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;placedDate&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;invoiceId&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt;
  &lt;span class="n"&gt;customer&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;OrderStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;COMPLETED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PENDING&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CANCELLED&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;quantity&lt;/span&gt; &lt;span class="nc"&gt;Integer&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;totalPrice&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nc"&gt;OrderItemStatus&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;OrderItemStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;AVAILABLE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OUT_OF_STOCK&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;BACK_ORDER&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;relationship&lt;/span&gt; &lt;span class="nc"&gt;ManyToOne&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;OrderItem&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;product&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;relationship&lt;/span&gt; &lt;span class="nc"&gt;OneToMany&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;ProductOrder&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;orderItem&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;order&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nc"&gt;ProductCategory&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;productCategory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;serviceClass&lt;/span&gt;
&lt;span class="n"&gt;paginate&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pagination&lt;/span&gt;
&lt;span class="n"&gt;microservice&lt;/span&gt; &lt;span class="nc"&gt;Product&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductOrder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ProductCategory&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;OrderItem&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Entities for Invoice microservice
 */&lt;/span&gt;
&lt;span class="c1"&gt;// Invoice for sales&lt;/span&gt;
&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;status&lt;/span&gt; &lt;span class="nc"&gt;InvoiceStatus&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;paymentMethod&lt;/span&gt; &lt;span class="nc"&gt;PaymentMethod&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;paymentDate&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;paymentAmount&lt;/span&gt; &lt;span class="nc"&gt;BigDecimal&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;InvoiceStatus&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;PAID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;ISSUED&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CANCELLED&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;trackingCode&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;PaymentMethod&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;CREDIT_CARD&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;CASH_ON_DELIVERY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PAYPAL&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;relationship&lt;/span&gt; &lt;span class="nc"&gt;OneToMany&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;shipment&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;service&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;serviceClass&lt;/span&gt;
&lt;span class="n"&gt;paginate&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;pagination&lt;/span&gt;
&lt;span class="n"&gt;microservice&lt;/span&gt; &lt;span class="nc"&gt;Invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Shipment&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt;

&lt;span class="cm"&gt;/**
 * Entities for notification microservice
 */&lt;/span&gt;
&lt;span class="n"&gt;entity&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;details&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;
  &lt;span class="n"&gt;sentDate&lt;/span&gt; &lt;span class="nc"&gt;Instant&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;format&lt;/span&gt; &lt;span class="nc"&gt;NotificationType&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;userId&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
  &lt;span class="n"&gt;productId&lt;/span&gt; &lt;span class="nc"&gt;Long&lt;/span&gt; &lt;span class="n"&gt;required&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nc"&gt;NotificationType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nc"&gt;EMAIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;SMS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;PARCEL&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;microservice&lt;/span&gt; &lt;span class="nc"&gt;Notification&lt;/span&gt; &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We define entities for each service and mark the entities as microservice entities. We also define relationships between entities, enums, and other options like pagination, service layer, etc. Please refer to JDL &lt;a href="https://www.jhipster.tech/jdl/entities-fields" rel="noopener noreferrer"&gt;Entities&lt;/a&gt; and &lt;a href="https://www.jhipster.tech/jdl/relationships" rel="noopener noreferrer"&gt;relationships&lt;/a&gt; documentation for more possibilities.&lt;/p&gt;

&lt;p&gt;Now, we are ready to run JHipster. Open a terminal window on the folder where you saved the JDL and run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;jhipster-istio
&lt;span class="nb"&gt;cd &lt;/span&gt;jhipster-istio

jhipster jdl app.jdl &lt;span class="nt"&gt;--fork&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create the applications with all their entities and specified configurations. You should be able to see the gateway application in action by running the following command on the &lt;strong&gt;store&lt;/strong&gt; folder.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew &lt;span class="c"&gt;# starts the Spring Boot application&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see everything that's generated in the &lt;a href="https://github.com/oktadev/okta-java-spring-k8s-istio-microservices-example" rel="noopener noreferrer"&gt;example application on GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a GKE cluster and install Istio
&lt;/h2&gt;

&lt;p&gt;To deploy the stack to Google Kubernetes Engine, we need to create a cluster and install Istio. So let's begin by creating a cluster using Google Cloud SDK.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a cluster
&lt;/h3&gt;

&lt;p&gt;Ensure you are logged into the &lt;a href="https://cloud.google.com/sdk/gcloud" rel="noopener noreferrer"&gt;gcloud CLI&lt;/a&gt; from the command-line and run the following command to create a GKE cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# set region and zone&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;compute/region europe-west1
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;compute/zone europe-west1-b
&lt;span class="c"&gt;# Create a project and enable container APIs&lt;/span&gt;
gcloud projects create jhipster-demo-okta &lt;span class="c"&gt;# You need to also enable billing via GUI&lt;/span&gt;
gcloud config &lt;span class="nb"&gt;set &lt;/span&gt;project jhipster-demo-okta
gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;container.googleapis.com

&lt;span class="c"&gt;# Create GKE Cluster&lt;/span&gt;
gcloud container clusters create hello-hipster &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--num-nodes&lt;/span&gt; 4 &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--machine-type&lt;/span&gt; n1-standard-2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could take anywhere from 5 to 15 minutes. &lt;code&gt;--machine-type&lt;/code&gt; is important as we need more CPU than available in the default setup. Once the cluster is created, it should be set automatically as the current Kubernetes context. You can verify that by running &lt;code&gt;kubectl config current-context&lt;/code&gt;. If the new cluster is not set as the current context, you can set it by running &lt;code&gt;gcloud container clusters get-credentials hello-hipster&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrpwoguep2b4twclgq2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flrpwoguep2b4twclgq2y.png" alt="GKE Cluster nodes"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: I'm using &lt;a href="https://kdash.cli.rs/" rel="noopener noreferrer"&gt;KDash&lt;/a&gt; to monitor the cluster; you can try it or use kubectl, &lt;a href="https://github.com/derailed/k9s" rel="noopener noreferrer"&gt;k9s&lt;/a&gt;, and so on as you prefer.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Install Istio to cluster
&lt;/h3&gt;

&lt;p&gt;As of writing this, I'm using Istio version 1.13.4. You can install &lt;strong&gt;istioctl&lt;/strong&gt; by running the following command, preferably from your home directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;ISTIO_VERSION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.13.4
curl &lt;span class="nt"&gt;-L&lt;/span&gt; https://istio.io/downloadIstio | sh -
&lt;span class="nb"&gt;cd &lt;/span&gt;istio-&lt;span class="nv"&gt;$ISTIO_VERSION&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;/bin:&lt;span class="nv"&gt;$PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should now be able to run &lt;strong&gt;istioctl&lt;/strong&gt; from the command line. Now, we can use the CLI to Install Istio to the GKE cluster. Istio provides a few &lt;a href="https://helm.sh/" rel="noopener noreferrer"&gt;Helm&lt;/a&gt; profiles out of the box. We will use the demo profile for demo purposes. You can choose the production or dev profile as well. The command should install Istio and set up everything required on our cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;istioctl &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;profile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;demo &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: If you run into any trouble with firewall or user privilege issues, please refer to &lt;a href="https://istio.io/latest/docs/setup/platform-setup/gke/" rel="noopener noreferrer"&gt;GKE setup guide from Istio&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Once the installation is complete, we need to fetch the External IP of the Istio Ingress Gateway. If you are using KDash, you can see it on the services tab, or you can run the following command to get it using kubectl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc istio-ingressgateway &lt;span class="nt"&gt;-n&lt;/span&gt; istio-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Install observability tools
&lt;/h3&gt;

&lt;p&gt;Istio also provides addons for most of the popular monitoring and observability tools. Let's install Grafana, Prometheus, Kiali and Zipkin on our cluster. These are preconfigured to work with the telemetry data provided by Istio. Ensure you are in the folder where you installed Istio, like &lt;strong&gt;istio-1.13.4&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;istio-&lt;span class="nv"&gt;$ISTIO_VERSION&lt;/span&gt;
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; samples/addons/grafana.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; samples/addons/prometheus.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; samples/addons/kiali.yaml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; samples/addons/extras/zipkin.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4x0lyz0wlq5xnqduajm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff4x0lyz0wlq5xnqduajm.png" alt="GKE Cluster with Istio pods"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we look at the istio-system namespace, we can see all the Istio components along with Grafana, Prometheus, Kiali, and Zipkin running. You can also see this by running the following command .&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; istio-system
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy the microservice stack to GKE
&lt;/h2&gt;

&lt;p&gt;Our cluster is ready, and we have Istio installed. Now, we can deploy our microservice stack to the cluster. First, we need to create Kubernetes manifests for our deployments and services and configurations for Istio. And once again, JHipster comes to the rescue. We can use the &lt;a href="https://www.jhipster.tech/jdl/deployments" rel="noopener noreferrer"&gt;JDL deployment&lt;/a&gt; configurations to generate Kubernetes setup for our stack with one command easily.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create Kubernetes manifests
&lt;/h3&gt;

&lt;p&gt;Create a new JDL file, say &lt;code&gt;deployment.jdl&lt;/code&gt;, and add the following content.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="c1"&gt;// will be created under 'kubernetes' folder&lt;/span&gt;
&lt;span class="nf"&gt;deployment&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="n"&gt;deploymentType&lt;/span&gt; &lt;span class="n"&gt;kubernetes&lt;/span&gt;
  &lt;span class="n"&gt;appsFolders&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;invoice&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;notification&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;product&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="n"&gt;dockerRepositoryName&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;your-docker-repository-name&amp;gt;"&lt;/span&gt;
  &lt;span class="n"&gt;serviceDiscoveryType&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
  &lt;span class="n"&gt;istio&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
  &lt;span class="n"&gt;kubernetesServiceType&lt;/span&gt; &lt;span class="nc"&gt;Ingress&lt;/span&gt;
  &lt;span class="n"&gt;kubernetesNamespace&lt;/span&gt; &lt;span class="n"&gt;jhipster&lt;/span&gt;
  &lt;span class="n"&gt;ingressDomain&lt;/span&gt; &lt;span class="s"&gt;"&amp;lt;istio-ingress-gateway-external-ip&amp;gt;.nip.io"&lt;/span&gt;
  &lt;span class="n"&gt;ingressType&lt;/span&gt; &lt;span class="n"&gt;gke&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I hope it's self-explanatory. You can refer to the JDL deployment documentation for all the available options. We have enabled Istio and set the ingress domain to the Istio Ingress Gateway's external IP that we noted earlier. Now we need a DNS for our IP. For real use-cases, you should map a DNS for the IP using a service provided by your cloud vendor like Google Cloud DNS, but for testing and demo purposes, we can use a wildcard DNS service like &lt;a href="http://nip.io" rel="noopener noreferrer"&gt;&lt;strong&gt;nip.io&lt;/strong&gt;&lt;/a&gt; to resolve our IP. Just append &lt;code&gt;nip.io&lt;/code&gt; to our IP and use that as the &lt;code&gt;ingressDomain&lt;/code&gt;. Make sure to use a docker repo where you have push rights.&lt;/p&gt;

&lt;p&gt;Now run the following command from the root folder where you ran the previous &lt;code&gt;jhipster jdl&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jhipster jdl deployment.jdl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will create a new folder, &lt;strong&gt;kubernetes&lt;/strong&gt;, with all the required Kubernetes manifests like deployments, services, Istio virtual services, gateways, and so on, for all the applications, databases, and monitoring.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4k0y303lyerwr2orp008.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4k0y303lyerwr2orp008.png" alt="JHipster JDL deployment"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each of the services will also have an Istio &lt;a href="https://istio.io/latest/docs/reference/config/networking/virtual-service/" rel="noopener noreferrer"&gt;virtual service&lt;/a&gt; and &lt;a href="https://istio.io/latest/docs/reference/config/networking/destination-rule/" rel="noopener noreferrer"&gt;destination rule&lt;/a&gt;. For example, the invoice service will have the following destination rule defining traffic policies.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DestinationRule&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invoice-destinationrule&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invoice&lt;/span&gt;
  &lt;span class="na"&gt;trafficPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;loadBalancer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;simple&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;RANDOM&lt;/span&gt;
    &lt;span class="na"&gt;connectionPool&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;tcp&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;maxConnections&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30&lt;/span&gt;
        &lt;span class="na"&gt;connectTimeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;100ms&lt;/span&gt;
      &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;http1MaxPendingRequests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
        &lt;span class="na"&gt;http2MaxRequests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
        &lt;span class="na"&gt;maxRequestsPerConnection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
        &lt;span class="na"&gt;maxRetries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
    &lt;span class="na"&gt;outlierDetection&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;consecutive5xxErrors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;baseEjectionTime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;60s&lt;/span&gt;
  &lt;span class="na"&gt;subsets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
      &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It also includes the following virtual service that defines the route. You could also use virtual services to do traffic split between two versions of the same app, among other things.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VirtualService&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invoice-virtualservice&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;invoice&lt;/span&gt;
  &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invoice&lt;/span&gt;
            &lt;span class="na"&gt;subset&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;v1"&lt;/span&gt;
          &lt;span class="na"&gt;weight&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;100&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;attempts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
        &lt;span class="na"&gt;perTryTimeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;2s&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The gateway is defined for the store application as it is our GUI as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Gateway&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-gateway&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-gateway&lt;/span&gt;
    &lt;span class="na"&gt;istio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingressgateway&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;istio&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ingressgateway&lt;/span&gt;
  &lt;span class="na"&gt;servers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http&lt;/span&gt;
        &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTP&lt;/span&gt;
      &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;store.jhipster.34.76.233.160.nip.io&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;80&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http2&lt;/span&gt;
        &lt;span class="na"&gt;protocol&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HTTP2&lt;/span&gt;
      &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;store.jhipster.34.76.233.160.nip.io&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.istio.io/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;VirtualService&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-gw-virtualservice&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;jhipster&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-gw-virtualservice&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;hosts&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;store.jhipster.34.76.233.160.nip.io&lt;/span&gt;
  &lt;span class="na"&gt;gateways&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;store-gateway&lt;/span&gt;
  &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/services/invoice/&lt;/span&gt;
      &lt;span class="na"&gt;rewrite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
      &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;invoice&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/services/notification/&lt;/span&gt;
      &lt;span class="na"&gt;rewrite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
      &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;notification&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/services/product/&lt;/span&gt;
      &lt;span class="na"&gt;rewrite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/&lt;/span&gt;
      &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;product&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;route&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you can see, there are also many useful commands printed on the console that you can use to do the deployment.&lt;/p&gt;

&lt;h3&gt;
  
  
  Deploy to GKE
&lt;/h3&gt;

&lt;p&gt;We are ready to deploy now. First, we need to build and push the images to the registry. We can use the handy &lt;a href="https://github.com/GoogleContainerTools/jib" rel="noopener noreferrer"&gt;Jib&lt;/a&gt; commands provided by JHipster. Navigate to each of the microservice folders and run the commands below.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;store &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;yourDockerRepository/store
&lt;span class="nb"&gt;cd &lt;/span&gt;invoice &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;yourDockerRepository/invoice
&lt;span class="nb"&gt;cd &lt;/span&gt;notification &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;yourDockerRepository/notification
&lt;span class="nb"&gt;cd &lt;/span&gt;product &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; ./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;yourDockerRepository/product
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the images are pushed to the Docker registry, we can deploy the stack using the handy script provided by JHipster. Navigate to the &lt;code&gt;kubernetes&lt;/code&gt; folder created by JHipster and run the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;kubernetes
./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the deployments are done, we must wait for the pods to be in &lt;strong&gt;RUNNING&lt;/strong&gt; status. Useful links will be printed on the terminal; make a note of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj4qoisgz0u5r1w1x3p4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkj4qoisgz0u5r1w1x3p4.png" alt="GKE cluster with application pods"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can now access the application at the given &lt;code&gt;http://store.jhipster.&amp;lt;istio-ingress-gateway-external-ip&amp;gt;.nip.io&lt;/code&gt; URI and log in with the default credentials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhhbdlkd5kubdfdr7vhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frhhbdlkd5kubdfdr7vhp.png" alt="Store gateway application"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: Currently, the JHipster OIDC setup does not work with Istio and there is an &lt;a href="https://github.com/jhipster/generator-jhipster/issues/17384" rel="noopener noreferrer"&gt;open issue&lt;/a&gt; in JHipster issue tracker for this. Alternative solutions would be to use an &lt;a href="https://istio.io/latest/blog/2021/better-external-authz/" rel="noopener noreferrer"&gt;external authorization server&lt;/a&gt; with something like &lt;a href="https://www.openpolicyagent.org/" rel="noopener noreferrer"&gt;Open Policy Agent&lt;/a&gt;. We will cover this in a later blog post.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Monitoring and observability
&lt;/h3&gt;

&lt;p&gt;Since we deployed tools for observability, let's see what we have.&lt;/p&gt;

&lt;h4&gt;
  
  
  Grafana
&lt;/h4&gt;

&lt;p&gt;First up are Grafana and Prometheus for metrics and dashboards. Click the URI for Grafana from the previous deployment step. Click &lt;strong&gt;General&lt;/strong&gt; at the top left corner and click the &lt;strong&gt;istio&lt;/strong&gt; folder. You should see multiple preconfigured dashboards. You can monitor the performance of the workloads and the istio system itself here. You can also create your own dashboards if you like. Prometheus provides the data visualized on Grafana.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frf9aod84wpoinggq9rwx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frf9aod84wpoinggq9rwx.png" alt="Grafana dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Kiali
&lt;/h4&gt;

&lt;p&gt;Kiali is a management console for Istio service mesh, and it provides a web interface for visualizing the network topology of your service mesh. You can use it to explore the network topology of your cluster and see the network traffic flowing through it. Click &lt;strong&gt;Graph&lt;/strong&gt; on the left side menu to see the network topology.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsfjkx9zr1t2sef5xnrv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxsfjkx9zr1t2sef5xnrv.png" alt="Kiali dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Zipkin
&lt;/h4&gt;

&lt;p&gt;Zipkin is a distributed tracing solution for distributed systems. It is a tool for capturing distributed traces and providing a centralized view of the traces. This is essential for a microservice setup where a request could span multiple services, and debugging would require tracing them. Click &lt;strong&gt;RUN QUERY&lt;/strong&gt; on the home screen to fetch recent traces, and click &lt;strong&gt;SHOW&lt;/strong&gt; on one of them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjwjiamrxz9xdotr3ps5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsjwjiamrxz9xdotr3ps5.png" alt="Zipkin dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Cleanup the GCP cluster
&lt;/h3&gt;

&lt;p&gt;Once you are done with experiments, make sure to delete the cluster you created so that you don't end up with a big bill from Google. You can delete the cluster from the Google Cloud Console GUI or via the command line using the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters delete hello-hipster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Learn more about Java Microservices, Istio, Kubernetes, and JHipster
&lt;/h2&gt;

&lt;p&gt;If you want to learn more about Kubernetes, OIDC, or using OIDC with Kubernetes, and security in general, check out these additional resources.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/06/22/terraform-eks-microservices" rel="noopener noreferrer"&gt;How to Deploy Java Microservices on Amazon EKS Using Terraform and Kubernetes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/11/08/k8s-api-server-oidc" rel="noopener noreferrer"&gt;How to Secure Your Kubernetes Cluster with OpenID Connect and RBAC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/05/05/kubernetes-microservices-azure" rel="noopener noreferrer"&gt;Kubernetes Microservices on Azure with Cosmos DB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/12/02/k8s-security-best-practices" rel="noopener noreferrer"&gt;How to Secure Your Kubernetes Clusters With Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/docs/concepts/oauth-openid/" rel="noopener noreferrer"&gt;OAuth 2.0 and OpenID Connect Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/10/08/secure-access-to-aws-eks" rel="noopener noreferrer"&gt;Secure Access to AWS EKS Clusters for Admins&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find all the code from this example on &lt;a href="https://github.com/oktadev/okta-java-spring-k8s-istio-microservices-example" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you liked this tutorial, chances are you'll enjoy the others we publish. Please follow &lt;a href="https://twitter.com/oktadev" rel="noopener noreferrer"&gt;@oktadev on Twitter&lt;/a&gt; and &lt;a href="https://youtube.com/oktadev" rel="noopener noreferrer"&gt;subscribe to our YouTube channel&lt;/a&gt; to get notified when we publish new developer tutorials.&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>jhipster</category>
      <category>java</category>
      <category>istio</category>
    </item>
    <item>
      <title>Run Microservices on DigitalOcean with Kubernetes</title>
      <dc:creator>Jimena Garbarino</dc:creator>
      <pubDate>Fri, 17 Jun 2022 21:50:28 +0000</pubDate>
      <link>https://forem.com/jhipster/run-microservices-on-digitalocean-with-kubernetes-3epj</link>
      <guid>https://forem.com/jhipster/run-microservices-on-digitalocean-with-kubernetes-3epj</guid>
      <description>&lt;p&gt;Cloud adoption continues to increase rapidly worldwide, and not only in the software industry. Every year more and more companies move their applications to the cloud. In the last JHipster community survey, from December 2021, participants valued JHipster's ability to get them to production faster, and requested more tutorials on deployment to cloud platforms. DigitalOcean is among the most popular "other" cloud vendors, according to some surveys. This post is a quick walk-through of the deployment of a JHipster microservices architecture to a Kubernetes cluster in DigitalOcean's cloud.&lt;/p&gt;

&lt;p&gt;This tutorial was created with the following frameworks and tools:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.jhipster.tech/installation/"&gt;JHipster 7.8.1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://jdk.java.net/java-se-ri/11"&gt;Java OpenJDK 11&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cli.okta.com"&gt;Okta CLI 0.10.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.digitalocean.com/reference/doctl/"&gt;doctl 1.72.0-release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://kubernetes.io/docs/tasks/tools/#kubectl"&gt;kubectl 1.23&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://minikube.sigs.k8s.io/docs/start/"&gt;minikube v1.25.2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://k9scli.io/topics/install/"&gt;k9s v0.25.18&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/install/"&gt;Docker 20.10.12&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  About DigitalOcean
&lt;/h2&gt;

&lt;p&gt;DigitalOcean is a cloud services company founded in 2011 by brothers Ben and Moisey Uretsky. Their headquarters are in New York City in the United States, and they also have offices in Massachusetts and Bangalore. Last March 2022, DigitalOcean reached its IPO (Initial Public Offering), and some press articles describe it as the cloud services provider for small businesses: "Cloud services for the Little Guy".&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pJoQKHiL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nelatao8ayjjgo8fhv6m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pJoQKHiL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nelatao8ayjjgo8fhv6m.png" alt="DigitalOcean Logo" width="354" height="354"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;DigitalOcean Kubernetes (DOKS) is a managed Kubernetes service that lets you deploy Kubernetes clusters without the complexities of handling the control pane and containerized infrastructure. Clusters are compatible with standard Kubernetes toolchains and integrate natively with DigitalOcean load balancers and block storage volumes. DOKS offers fast provisioning and deployment, and provides a free high-availability control pane, for reliability management. It can also provide a Cluster Autoscaler that automatically adjusts the size of the cluster by adding or removing nodes based on the cluster's capacity to schedule pods. Pricing for Kubernetes workloads is based on resources required by the cluster, droplets, block storage, and load balancers.&lt;/p&gt;

&lt;p&gt;The company publishes its data center &lt;a href="https://www.digitalocean.com/trust/certification-reports"&gt;certification reports&lt;/a&gt; on its website, and all the data centers have approved two or more of the following certifications: SOC (System and Organization Controls) 1 Type II, SOC 2 Type II, SOC 3 Type II, and ISO/IEC 27001:2013 (Security techniques - Information security management systems). PCI-DSS (Payment Card Industry - Data Security Standard) has been certified in all data centers.&lt;/p&gt;

&lt;h2&gt;
  
  
  Set up a microservices architecture for Kubernetes
&lt;/h2&gt;

&lt;p&gt;Before working on the application, you need to install JHipster. The classical way of working with JHipster is to do a local installation with NPM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; generator-jhipster@7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather use Yarn or Docker, follow the instructions at &lt;a href="https://www.jhipster.tech/installation/#local-installation-with-npm-recommended-for-normal-users"&gt;jhipster.tech&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For this test, start from the &lt;code&gt;reactive-jhipster&lt;/code&gt; example in the &lt;code&gt;java-microservices-examples&lt;/code&gt; repository on &lt;a href="https://github.com/oktadev/java-microservices-examples"&gt;GitHub&lt;/a&gt;. The example is a JHipster reactive microservices architecture with Spring Cloud Gateway and Spring WebFlux, Vue as the client framework, and Gradle as the build tool. You can read about how it was built in &lt;a href="https://developer.okta.com//blog/2021/01/20/reactive-java-microservices"&gt;Reactive Java Microservices with Spring Boot and JHipster&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/oktadev/java-microservices-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;java-microservices-examples/reactive-jhipster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you inspect the project folder, you will find sub-folders for the &lt;code&gt;gateway&lt;/code&gt; service, which will act as the front-end application, and a gateway to the &lt;code&gt;store&lt;/code&gt; and &lt;code&gt;blog&lt;/code&gt; microservices, which also have their subfolders. A &lt;code&gt;docker-compose&lt;/code&gt; sub-folder contains the service definitions for running the application containers.&lt;/p&gt;

&lt;p&gt;The next step is to generate the Kubernetes deployment descriptors. In the project root folder, create a &lt;code&gt;kubernetes&lt;/code&gt; directory and run the &lt;code&gt;k8s&lt;/code&gt; JHipster sub-generator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;kubernetes
&lt;span class="nb"&gt;cd &lt;/span&gt;kubernetes
jhipster k8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Choose the following options when prompted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type of application: &lt;strong&gt;Microservice application&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Root directory: &lt;strong&gt;../&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Which applications? (select all)&lt;/li&gt;
&lt;li&gt;Set up monitoring? &lt;strong&gt;No&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Which applications with clustered databases? select &lt;strong&gt;store&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Admin password for JHipster Registry: (generate one)&lt;/li&gt;
&lt;li&gt;Kubernetes namespace: &lt;strong&gt;demo&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Docker repository name: (your docker hub username)&lt;/li&gt;
&lt;li&gt;Command to push Docker image: &lt;code&gt;docker push&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable Istio? &lt;strong&gt;No&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Kubernetes service type? &lt;strong&gt;LoadBalancer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use dynamic storage provisioning? &lt;strong&gt;Yes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use a specific storage class? (leave empty)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You can leave the Docker repository name blank for running Kubernetes locally, but a repository will be required for the cloud deployment, so go ahead and create a &lt;a href="https://hub.docker.com/"&gt;DockerHub&lt;/a&gt; personal account, and the image pull configuration will be ready for both local and cloud deployments.&lt;/p&gt;

&lt;p&gt;Build the &lt;code&gt;gateway&lt;/code&gt;, &lt;code&gt;store&lt;/code&gt;, and &lt;code&gt;blog&lt;/code&gt; services container images with Jib. As Jib will push the images to DockerHub, you first need to do a &lt;code&gt;docker login&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you have two-factor authentication enabled, you must generate a token and use it as the password for the login. In the Docker web, go to the user menu and choose &lt;strong&gt;Account Settings&lt;/strong&gt;. Then in the left menu choose &lt;strong&gt;Security&lt;/strong&gt; and &lt;strong&gt;New Access Token&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For example, for building the &lt;code&gt;gateway&lt;/code&gt; service image:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../gateway
./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;docker-repo-name&amp;gt;/gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;IMPORTANT NOTE&lt;/strong&gt;: Unfortunately the application example does not build with Java 17 at the moment of writing this post. As specified in the introduction, this example was built with Java 11.&lt;/p&gt;

&lt;p&gt;Check that the images were uploaded to &lt;a href="https://hub.docker.com"&gt;DockerHub&lt;/a&gt;, and navigate to the project root folder in the terminal for the next step.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configure authentication with OpenID Connect
&lt;/h2&gt;

&lt;p&gt;One more configuration step before running the architecture locally, let's configure Okta for authentication.&lt;/p&gt;

&lt;p&gt;Before you begin, you’ll need a free Okta developer account. Install the &lt;a href="https://cli.okta.com"&gt;Okta CLI&lt;/a&gt; and run &lt;code class="language-plaintext highlighter-rouge"&gt;okta register&lt;/code&gt; to sign up for a new account. If you already have an account, run &lt;code class="language-plaintext highlighter-rouge"&gt;okta login&lt;/code&gt;.
Then, run &lt;code class="language-plaintext highlighter-rouge"&gt;okta apps create jhipster&lt;/code&gt;. Select the default app name, or change it as you see fit.
  Accept the default Redirect URI values provided for you.&lt;/p&gt;

&lt;p&gt;What does the Okta CLI do?&lt;/p&gt;

&lt;p&gt;The Okta CLI streamlines configuring a JHipster app and does several things for you:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Creates an OIDC app with the correct redirect URIs:
    &lt;ul&gt;
      &lt;li&gt;login: &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8080/login/oauth2/code/oidc&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8761/login/oauth2/code/oidc&lt;/code&gt;
&lt;/li&gt;
      &lt;li&gt;logout: &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8080&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8761&lt;/code&gt;
&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Creates &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_USER&lt;/code&gt; groups that JHipster expects&lt;/li&gt;
  &lt;li&gt;Adds your current user to the &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_USER&lt;/code&gt; groups&lt;/li&gt;
  &lt;li&gt;Creates a &lt;code class="language-plaintext highlighter-rouge"&gt;groups&lt;/code&gt; claim in your default authorization server and adds the user’s groups to it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8761*&lt;/code&gt; redirect URIs are for the JHipster Registry, which is often used when creating microservices with JHipster. The Okta CLI adds these by default.&lt;/p&gt;

&lt;p&gt;You will see output like the following when it’s finished:&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Okta application configuration has been written to: /path/to/app/.okta.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Run &lt;code class="language-plaintext highlighter-rouge"&gt;cat .okta.env&lt;/code&gt; (or &lt;code class="language-plaintext highlighter-rouge"&gt;type .okta.env&lt;/code&gt; on Windows) to see the issuer and credentials for your app. It will look like this (except the placeholder values will be populated):&lt;/p&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://{yourOktaDomain}/oauth2/default"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{clientId}"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{clientSecret}"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You can also use the Okta Admin Console to create your app. See &lt;a href="https://www.jhipster.tech/security/#okta"&gt;Create a JHipster App on Okta&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;Add the settings from the generated &lt;code&gt;.okta.env&lt;/code&gt; to &lt;code&gt;kubernetes/registry-k8s/application-configmap.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;application.yml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;spring:&lt;/span&gt;
      &lt;span class="s"&gt;security:&lt;/span&gt;
        &lt;span class="s"&gt;oauth2:&lt;/span&gt;
          &lt;span class="s"&gt;client:&lt;/span&gt;
            &lt;span class="s"&gt;provider:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;issuer-uri: https://&amp;lt;your-okta-domain&amp;gt;/oauth2/default&lt;/span&gt;
            &lt;span class="s"&gt;registration:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;client-id: &amp;lt;client-id&amp;gt;&lt;/span&gt;
                &lt;span class="s"&gt;client-secret: &amp;lt;client-secret&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable the OIDC authentication in the &lt;code&gt;jhipster-registry&lt;/code&gt; service by adding the &lt;code&gt;oauth2&lt;/code&gt; profile in the &lt;code&gt;kubernetes/registry-k8s/jhipster-registry.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_PROFILES_ACTIVE&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod,k8s,oauth2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run locally with minikube
&lt;/h2&gt;

&lt;p&gt;Install &lt;a href="https://minikube.sigs.k8s.io/docs/start/"&gt;minikube&lt;/a&gt; and &lt;a href="https://kubernetes.io/docs/tasks/tools/"&gt;&lt;code&gt;kubectl&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;For minikube, you will need at least 2 CPUs. Start minikube with your number of CPUs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;kubernetes
minikube &lt;span class="nt"&gt;--cpus&lt;/span&gt; &amp;lt;ncpu&amp;gt; start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;minikube will log the Kubernetes and Docker versions on start:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Preparing Kubernetes v1.23.3 on Docker 20.10.12 ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For the &lt;code&gt;store-mongodb&lt;/code&gt; deployment to work, the property &lt;code&gt;Service.spec.publishNotReadyAddresses&lt;/code&gt; was required, instead of the annotation &lt;code&gt;service.alpha.kubernetes.io/tolerate-unready-endpoints&lt;/code&gt;, as the latter was deprecated in &lt;a href="https://github.com/kubernetes/kubernetes/blob/master/CHANGELOG/CHANGELOG-1.11.md#kubernetes-111-release-notes"&gt;Kubernetes release 1.11&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Edit &lt;code&gt;kubernetes/store-k8s/store-mongodb.yml&lt;/code&gt; and add the &lt;code&gt;publishNotReadyAddresses: true&lt;/code&gt; property to the &lt;code&gt;spec&lt;/code&gt; key near the bottom of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="c1"&gt;# Headless service for DNS record&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-mongodb&lt;/span&gt;
  &lt;span class="na"&gt;namespace&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;demo&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
  &lt;span class="na"&gt;clusterIP&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;None&lt;/span&gt;
  &lt;span class="na"&gt;publishNotReadyAddresses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;peer&lt;/span&gt;
      &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;27017&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;store-mongodb&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then deploy the application to minikube.  In the &lt;code&gt;kubernetes&lt;/code&gt; directory, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now is a good time to install &lt;a href="https://k9scli.io/topics/install/"&gt;k9s&lt;/a&gt;, a terminal-based UI to interact with Kubernetes clusters. Then run k9s with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;k9s &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--02CV1aro--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ekpim8iw61zkffi4aa9q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--02CV1aro--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ekpim8iw61zkffi4aa9q.png" alt="k9s User Interface" width="880" height="456"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Check out the &lt;a href="https://k9scli.io/topics/commands/"&gt;commands list&lt;/a&gt;, some useful ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;:namespace&lt;/code&gt;: show all available namespaces&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:pods&lt;/code&gt;: show all available pods&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can navigate the pods with &lt;code&gt;ENTER&lt;/code&gt; and go back with &lt;code&gt;ESC&lt;/code&gt; keys.&lt;/p&gt;

&lt;p&gt;Set up port-forwarding for the JHipster Registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/jhipster-registry &lt;span class="nt"&gt;-n&lt;/span&gt; demo 8761
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to &lt;code&gt;http://localhost:8761&lt;/code&gt; and sign in with your Okta credentials. When the registry shows all services in green, Set up port-forwarding for the gateway as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/gateway &lt;span class="nt"&gt;-n&lt;/span&gt; demo 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate to &lt;code&gt;http://localhost:8080&lt;/code&gt;, sign in, and create some entities to verify everything is working fine.&lt;/p&gt;

&lt;p&gt;After looking around, stop minikube before the cloud deployment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also delete the minikube cluster to have a clean slate for future tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube delete
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Deploy to DigitalOcean Kubernetes
&lt;/h2&gt;

&lt;p&gt;Now that the architecture works locally, let's proceed to the cloud deployment. First, create a &lt;a href="https://cloud.digitalocean.com/registrations/new"&gt;DigitalOcean&lt;/a&gt; account. The registration requires a $5 PayPal payment, or providing a credit card.&lt;/p&gt;

&lt;p&gt;Most of the cluster tasks, if not all, can be accomplished using &lt;a href="https://github.com/digitalocean/doctl#installing-doctl"&gt;doctl&lt;/a&gt;, the command-line interface (CLI) for the DigitalOcean API. Install the tool, and perform the authentication with DigitalOcean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doctl auth init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be prompted to enter the DigitalOcean access token that you can generate in the DigitalOcean control panel. Sign in, and then in the left menu go to &lt;strong&gt;API&lt;/strong&gt;, click &lt;strong&gt;Generate New Token&lt;/strong&gt;. Enter a token name, and click &lt;strong&gt;Generate Token&lt;/strong&gt;. Copy the new token from the &lt;strong&gt;Tokens/Keys&lt;/strong&gt; table.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gOzCn2kB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8sjxu54fxljv419w8erm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gOzCn2kB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8sjxu54fxljv419w8erm.png" alt="DigitalOcean Control Panel for Token Create" width="598" height="528"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find a detailed list of pricing for cluster resources at &lt;a href="https://docs.digitalocean.com/products/kubernetes/"&gt;DigitalOcean&lt;/a&gt;,  and with &lt;code&gt;doctl&lt;/code&gt; you can quickly retrieve a list of node size options available for your account:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doctl k options sizes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the cluster with the following command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doctl k cluster create do1 &lt;span class="nt"&gt;-v&lt;/span&gt; &lt;span class="nt"&gt;--size&lt;/span&gt; s-4vcpu-8gb-intel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After creating a cluster, &lt;code&gt;doctl&lt;/code&gt; adds a configuration context to &lt;code&gt;kubectl&lt;/code&gt; and makes it active, so you can start monitoring your cluster right away with k9s. First, apply the resources configuration to the DigitalOcean cluster:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Monitor the deployment with k9s:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;k9s &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--VksGL7E---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r66phnee1r408kzl0ark.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--VksGL7E---/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/r66phnee1r408kzl0ark.png" alt="k9s UI monitoring DigitalOcean Kubernetes" width="880" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once you see the &lt;code&gt;jhipster-registry&lt;/code&gt; pods are up, set up port forwarding again so you can also monitor the status of the services in the registry UI.&lt;/p&gt;

&lt;h3&gt;
  
  
  Notes on DigitalOcean's node sizes and volumes
&lt;/h3&gt;

&lt;p&gt;Deploying to a Kubernetes cluster on DigitalOcean's cloud can be tricky if you don't specify enough capacity from the start.&lt;/p&gt;

&lt;p&gt;At first, I tested the cluster with the highest size Intel nodes available for my account, &lt;code&gt;s-2vcpu-4gb-intel&lt;/code&gt;. I attempted to run the application in the default cluster configuration, a three-node cluster with a single node pool in the nyc1 region, using the latest Kubernetes version. As I started to see pods not running due to &lt;code&gt;insufficient CPU&lt;/code&gt;, I increased the number of nodes and got everything working. DigitalOcean's latest and default Kubernetes version at the moment of writing this post was 1.22.8.&lt;/p&gt;

&lt;p&gt;If you need to find out the reason why a pod is not running, you can get the pod events with &lt;code&gt;kubectl describe&lt;/code&gt;, for example, for the &lt;code&gt;jhipster-registry-0&lt;/code&gt; pod:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe pod jhipster-registry-0 &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output of the command, in the events section, for a pod that has failed due to insufficient cpu, looks like the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Events:
  Type     Reason                  Age                   From                     Message
  ----     ------                  ----                  ----                     -------
  Normal   NotTriggerScaleUp       4m30s                 cluster-autoscaler       pod didn't trigger scale-up:
  Warning  FailedScheduling        69s (x3 over 95s)     default-scheduler        0/3 nodes are available: 3 Insufficient cpu.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you see a pod failing in &lt;code&gt;k9s&lt;/code&gt;, check the events for that pod with &lt;code&gt;kubectl describe pod&lt;/code&gt; as in the example above.&lt;/p&gt;

&lt;p&gt;To add CPU, increase the number of nodes with &lt;code&gt;doctl&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doctl k cluster node-pool update do1 do1-default-pool &lt;span class="nt"&gt;--count&lt;/span&gt; 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don't need to restart pods when adding nodes.&lt;/p&gt;

&lt;p&gt;After I increased CPU (adding one more node to the cluster), some of the pods, in the &lt;code&gt;store-mongodb&lt;/code&gt; stateful set with 3 replicas, did not run due to &lt;em&gt;unbound immediate PersistentVolumeClaims&lt;/em&gt;. This was reported in the pod events, in the output of &lt;code&gt;kubectl describe pod&lt;/code&gt;, for the failing pods. I then inspected persistent volume claims with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pvc &lt;span class="nt"&gt;-n&lt;/span&gt; demo
kubectl describe pvc datadir-store-mongodb-2 &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;kubectl describe pvc&lt;/code&gt; command output was showing provisioning errors, and instructed to contact support:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Events:
  Type     Reason                Age                  From                                                                       Message
  ----     ------                ----                 ----                                                                       -------
  Normal   Provisioning          3m6s (x10 over 11m)  dobs.csi.digitalocean.com_master-do3_cebb3451-5eba-4805-956c-753ec148e2ea  External provisioner is provisioning volume for claim "demo/datadir-store-mongodb-2"
  Warning  ProvisioningFailed    3m5s (x10 over 11m)  dobs.csi.digitalocean.com_master-do3_cebb3451-5eba-4805-956c-753ec148e2ea  failed to provision volume with StorageClass "do-block-storage": rpc error: code = ResourceExhausted desc = volume limit (10) has been reached. Current number of volumes: 10. Please contact support.
  Normal   ExternalProvisioning  103s (x43 over 11m)  persistentvolume-controller                                                waiting for a volume to be created, either by external provisioner "dobs.csi.digitalocean.com" or manually created by system administrator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As instructed by the event message, I contacted DigitalOcean support and they fixed it.&lt;/p&gt;

&lt;p&gt;Finally, as I first registered for the free trial account, I had to open a second support ticket requesting higher node sizes, so I could use the size &lt;code&gt;s-4vcpu-8gb-intel&lt;/code&gt;. Not all the size options were available after signing up for the trial, but that is not the case for a standard account. Billing should be under $10 USD for a short Kubernetes service test.&lt;/p&gt;

&lt;h3&gt;
  
  
  Find your gateway's external IP and update redirect URIs
&lt;/h3&gt;

&lt;p&gt;Once all the pods are running, find the gateway external IP with the &lt;code&gt;kubectl describe&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe service gateway &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Name:                     gateway
Namespace:                demo
Labels:                   app=gateway
Annotations:              kubernetes.digitalocean.com/load-balancer-id: e81d2b8c-6c28-430d-8ba8-6dab29a1ba76
                          service.beta.kubernetes.io/do-loadbalancer-certificate-id: bd0b1d03-0f90-449d-abbe-ac6a4026c133
Selector:                 app=gateway
Type:                     LoadBalancer
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.245.47.51
IPs:                      10.245.47.51
LoadBalancer Ingress:     157.230.200.181
Port:                     http  8080/TCP
TargetPort:               8080/TCP
NodePort:                 http  31048/TCP
Endpoints:                10.244.1.42:8080
Session Affinity:         None
External Traffic Policy:  Cluster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the redirect URIs in Okta to allow the gateway address as a valid redirect. Run &lt;code&gt;okta login&lt;/code&gt;, open the returned URL in your browser, and sign in to the Okta Admin Console. Go to the &lt;strong&gt;Applications&lt;/strong&gt; section, find your application, edit, and add:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sign-in redirect URIs: &lt;code&gt;http://&amp;lt;load-balancer-ingress-ip&amp;gt;:8080/login/oauth2/code/oidc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sign-out redirect URIs: &lt;code&gt;http://&amp;lt;load-balancer-ingress-ip&amp;gt;:8080&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Navigate to &lt;code&gt;http://&amp;lt;load-balancer-ingress-ip&amp;gt;:8080&lt;/code&gt; and rejoice when everything works!&lt;/p&gt;

&lt;h2&gt;
  
  
  Secure web traffic with HTTPS
&lt;/h2&gt;

&lt;p&gt;Since the gateway service acts as the application front-end, in the k8s descriptors it is defined as a &lt;code&gt;LoadBalancer&lt;/code&gt; service type. This exposes the service externally using the cloud provider's load balancer. DigitalOcean load balancers are a fully-managed, highly available network load-balancing service. Load balancers distribute traffic to groups of droplets, which decouples the overall health of a backend service from the health of a single server to ensure that your services stay online.&lt;/p&gt;

&lt;p&gt;The standard practice is to secure web traffic to your application with HTTPS. For traffic encryption, you need a TLS (SSL) certificate. DigitalOcean also provides automatic certificate creation and renewal if you manage your domain with DigitalOcean's DNS, which is free. But domain registration is not provided.  To use DigitalOcean's DNS, you need to register a domain name with a registrar and update your domain's NS records to point to DigitalOcean's name servers.&lt;/p&gt;

&lt;p&gt;Then, for using DigitalOcean's managed domain and certificate, you must &lt;a href="https://docs.digitalocean.com/tutorials/dns-registrars/"&gt;delegate the domain&lt;/a&gt;, updating NS records in the registrar. Because of this requirement, you cannot use free DNS services where you cannot set up NS records, like &lt;a href="https://nip.io/"&gt;nip.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IMPORTANT NOTE&lt;/strong&gt;: Before changing the registrar NS records (the nameservers), add your domain to DigitalOcean, to minimize service disruptions.&lt;/p&gt;

&lt;p&gt;You can create the certificate and the domain at the same time in the DigitalOcean control panel, when you set up the load balancer HTTPS forwarding.&lt;/p&gt;

&lt;p&gt;DigitalOcean load balancers support two main configurations for encrypted web traffic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSL termination:&lt;/strong&gt; decrypts SSL requests at the load balancer and sends them unencrypted to the backend at the Droplets' private IP address. The slower and CPU-intensive work of decryption is performed at the load balancer, and certificate management is simplified. The traffic between the load balancer and the backend is secured by routing over the VPC network, but data is readable inside the private network.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSL passthrough:&lt;/strong&gt; sends the encrypted SSL requests directly to the backend at the Droplets' private IP address, traffic between the load balancer and the backend is secured. Every backend server must have the certificate, and client information contained in &lt;code&gt;X-forwarded-*&lt;/code&gt; headers might be lost.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Taking advantage of the simplified certificate management, SSL termination will be configured in the following steps through the DigitalOcean control panel.&lt;/p&gt;

&lt;p&gt;Log in to your DigitalOcean account, and in the left menu choose &lt;strong&gt;Kubernetes&lt;/strong&gt;. Then choose your cluster, and on the cluster page, choose &lt;strong&gt;Resources&lt;/strong&gt;. In the &lt;em&gt;LOAD BALANCERS&lt;/em&gt; list, choose the single load balancer that must have been created. On the load balancer page, choose the &lt;strong&gt;Settings&lt;/strong&gt; tab. Click &lt;strong&gt;Edit&lt;/strong&gt; in the &lt;em&gt;Forwarding Rules&lt;/em&gt;. Add a forwarding rule for HTTPS in port 443, and in the certificate drop-down, choose &lt;strong&gt;New certificate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--r3GZc-2y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7kru6m2o0mkp03rrqas2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--r3GZc-2y--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7kru6m2o0mkp03rrqas2.png" alt="DigitalOcean new certificate form" width="656" height="605"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the &lt;strong&gt;New Certificate&lt;/strong&gt; form, choose the &lt;strong&gt;Use Let's Encrypt&lt;/strong&gt; tab, and then in the domain box, choose &lt;strong&gt;Add new domain&lt;/strong&gt;. Then enter your domain name, and list other subdomains to include. Add a name for the certificate and click &lt;strong&gt;Generate Certificate&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2__UNIEF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/512hx0ic4n1617ecwho6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2__UNIEF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/512hx0ic4n1617ecwho6.png" alt="DigitalOcean new domain form" width="558" height="883"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back in the &lt;em&gt;Forwarding rules&lt;/em&gt; page, check that the generated certificate is selected, and forward the traffic to the droplet HTTP port where the gateway is running. Tick the checkbox &lt;strong&gt;Create DNS records for all the new Let's Encrypt certificates&lt;/strong&gt; and then &lt;strong&gt;Save&lt;/strong&gt; the forwarding settings.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qie6ZOka--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6bir8jso7bglbnqaj3o4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qie6ZOka--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6bir8jso7bglbnqaj3o4.png" alt="DigitalOcean load balancer forwarding settings" width="869" height="516"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally, in the SSL section of the settings, tick the checkbox &lt;strong&gt;Redirect HTTP to HTTPS&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qewO0ynd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nxkt7r265ahqvqacrdgp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qewO0ynd--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/nxkt7r265ahqvqacrdgp.png" alt="DigitalOcean load balancer SSL redirect settings" width="870" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once again, update the redirect URIs in Okta to allow the newly configured domain. Add the following redirect URIs:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sign-in redirect URIs: &lt;code&gt;https://&amp;lt;your-domain&amp;gt;/login/oauth2/code/oidc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Sign-out redirect URIs: &lt;code&gt;https://&amp;lt;your-domain&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Test the configuration by navigating to &lt;code&gt;http://&amp;lt;your-domain&amp;gt;&lt;/code&gt;. First, the load balancer should redirect to HTTPs, and then the gateway should redirect to the Okta sign-in page.&lt;/p&gt;

&lt;h2&gt;
  
  
  Inspect and manage cluster resources
&lt;/h2&gt;

&lt;p&gt;You can retrieve the cluster associated resources with the following &lt;code&gt;doctl&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doctl k cluster list-associated-resources do1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output will list the volumes and load balancers created for the cluster, which generate billing charges besides the cost of the nodes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Volumes                                                                                                                                                                                       Volume Snapshots    Load Balancers
[a33dbaba-cb22-11ec-9383-0a58ac145375 a6e18809-cb22-11ec-8723-0a58ac14468c 65fb3778-cb23-11ec-9383-0a58ac145375 ac106c49-cb22-11ec-9383-0a58ac145375 c7122288-cb22-11ec-bd08-0a58ac14467d]    []                  [8ebbcbf2-1e67-46f5-b38a-eddae17f00f3]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unfortunately, there is no way to pause the cluster. According to DigitalOcean, you can power a droplet off, but this does not halt the billing. Billing costs can be reduced by taking a snapshot of the droplet and then destroying it. The snapshot cost is less expensive.&lt;/p&gt;

&lt;p&gt;While testing the platform, you can delete the cluster in between sessions, to avoid spending the trial credit while not actively working on it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doctl k cluster delete do1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Volumes provisioned to the cluster are not deleted with the cluster, and charges are generated hourly. You can delete volumes using the control panel or with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doctl compute volume list
doctl compute volume delete &amp;lt;volume-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The load balancers are not deleted with the cluster either:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doctl compute load-balancer list
doctl compute load-balancer delete &amp;lt;load-balancer-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;During registration, a project must have been created, allowing you to organize resources. You can list all the resources associated with a project with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;doctl projects list
doctl projects resources list &amp;lt;project-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Learn more about JHipster and Kubernetes
&lt;/h2&gt;

&lt;p&gt;This has been a brief walkthrough for deploying JHipster to a DigitalOcean's managed Kubernetes cluster. Some important topics for production deployment were not covered in this post, to focus in particular on DigitalOcean resource requirements. Some key topics we did not cover in this post include external configuration storage with Spring Cloud Config and Git as well as secrets encryption both for the cloud configuration and Kubernetes configuration. You can learn about these good practices in the first post of this cloud deployment series: &lt;a href="https://developer.okta.com//blog/2021/06/01/kubernetes-spring-boot-jhipster"&gt;Kubernetes to the Cloud with Spring Boot and JHipster&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Keep learning, and for more content on JHipster, check out the following links:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/05/05/kubernetes-microservices-azure#spring-boot-microservices-for-azure-and-cosmos-db"&gt;Kubernetes Microservices on Azure with Cosmos DB&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/03/03/spring-native-jhipster"&gt;Introducing Spring Native for JHipster: Serverless Full-Stack Made Easy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/11/22/full-stack-java"&gt;Full Stack Java with React, Spring Boot, and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/03/08/jhipster-quarkus-oidc"&gt;Fast Java Made Easy with Quarkus and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Be sure to follow us on &lt;a href="https://twitter.com/oktadev"&gt;Twitter&lt;/a&gt; and subscribe to our &lt;a href="https://youtube.com/c/oktadev"&gt;YouTube Channel&lt;/a&gt; so that you never miss any of our excellent content!&lt;/p&gt;

</description>
      <category>kubernetes</category>
      <category>digitalocean</category>
      <category>jhipster</category>
      <category>springboot</category>
    </item>
    <item>
      <title>Create Kubernetes Microservices on Azure with Cosmos DB</title>
      <dc:creator>Andrew Hughes</dc:creator>
      <pubDate>Fri, 17 Jun 2022 03:45:43 +0000</pubDate>
      <link>https://forem.com/jhipster/create-a-kubernetes-microservices-on-azure-with-cosmos-db-26i2</link>
      <guid>https://forem.com/jhipster/create-a-kubernetes-microservices-on-azure-with-cosmos-db-26i2</guid>
      <description>&lt;p&gt;In this tutorial, you'll learn how to deploy a JHipster-based reactive microservice to Azure Kubernetes Service (AKS). You'll use Azure's Cosmos DB as a persistent store for one of the services. For security, you'll use Okta as an OAuth 2.0 and OpenID Connect (OIDC) provider. You'll also securely encrypt all secrets in the project configuration files using Kubernetes secrets and &lt;code&gt;kubeseal&lt;/code&gt;. This tutorial focuses on deploying an already generated project to Azure AKS. It does not go into great detail about generating the project. To see how the project was generated using JHipster, take a look at &lt;a href="https://dev.to/blog/2021/01/20/reactive-java-microservices"&gt;Reactive Java Microservices with Spring Boot and JHipster&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The project has a few different pieces:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JHipster Registry: a Eureka server for service discovery and a Spring Cloud Config server for centralized configuration management&lt;/li&gt;
&lt;li&gt;Gateway: public Spring Cloud Gateway application using Vue&lt;/li&gt;
&lt;li&gt;Store: Spring Boot microservice using Azure's Cosmo DB API for MongoDB&lt;/li&gt;
&lt;li&gt;Blog: Spring Boot microservice using a Neo4J database&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T9nfWcq9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51ml7s0mpl6ahisow3a0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T9nfWcq9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/51ml7s0mpl6ahisow3a0.png" alt="Image description" width="812" height="690"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This tutorial has a lot of different technologies in it. I've tried to make it as simple and as explicit as possible, but it's probably helpful to have some basic knowledge of Docker and Kubernetes before you start.&lt;/p&gt;

&lt;p&gt;If you're already familiar with all the tech in this tutorial, you can skip ahead to the prerequisites section. If not, I'm going to explain them a little before we move on.&lt;/p&gt;

&lt;h2&gt;
  
  
  JHipster microservices architecture overview
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://www.jhipster.tech/"&gt;JHipster&lt;/a&gt;&lt;/strong&gt; is a development platform that streamlines the generation, development, and deployment of both monolithic and microservice applications. It supports a dizzying array of frontend (Angular, React, and Vue) and backend (Spring Boot, Micronaut, Quarkus, Node.js, and .NET) technologies. It's designed to be deployed using Docker and Kubernetes, and can easily deploy to all the major cloud platforms, such as AWS, Azure, Heroku, Cloud Foundry, Google Cloud Platform, and OpenShift. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--ldfbIi7j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2kbghpyvis28wyqto54x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ldfbIi7j--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2kbghpyvis28wyqto54x.png" alt="Image description" width="880" height="460"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The project in this tutorial uses &lt;strong&gt;Spring Boot&lt;/strong&gt; with Java resource servers and a &lt;strong&gt;Vue&lt;/strong&gt; frontend. It was built with the &lt;strong&gt;JHipster generator&lt;/strong&gt; that quickly scaffolds a new application based on either an interactive shell or a DSL file. You can read more about generating microservices with JHipster &lt;a href="https://www.jhipster.tech/creating-microservices/"&gt;in their docs&lt;/a&gt;. One of the slick features of the JHipster generator is that you can generate data entities along with applications.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--G2THUjvM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wubfq6ur96ssjteuw71.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--G2THUjvM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2wubfq6ur96ssjteuw71.png" alt="Image description" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;a href="https://www.jhipster.tech/jhipster-registry/"&gt;JHipster Registry&lt;/a&gt;&lt;/strong&gt; that is generated with the microservice includes two important functions: a &lt;strong&gt;Eureka server&lt;/strong&gt; and a &lt;strong&gt;Spring Cloud Config&lt;/strong&gt; server. The Eureka server allows the microservices to dynamically find each other without having to use hard-coded URIs. This means that the microservice can scale and services can be replaced without causing problems. It's a bit like a phonebook or a DNS service for the microservice. The &lt;a href="https://cloud.spring.io/spring-cloud-config/reference/html/"&gt;Spring Cloud Config&lt;/a&gt; server allows project configuration to be centralized and distributed to all of the different services. In this tutorial you'll use this feature to configure all of the services for Okta OAuth in one place.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;&lt;a href="https://www.jhipster.tech/api-gateway/"&gt;JHipster API Gateway&lt;/a&gt;&lt;/strong&gt; is the public face of your microservice. All public traffic comes through this service, which also includes the Vue frontend. The gateway is one of three application types that can be created by the JHipster generator DSL. The other two are monolith and microservice. A monolith is a non-microservice application with a single service.&lt;/p&gt;

&lt;p&gt;The store service and blog service are both examples of the microservice application type. This means that each service is a Spring Boot resources server with some type of SQL or NoSQL backend.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Aj19u4g0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udague9n8m7uhlv0thnq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Aj19u4g0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/udague9n8m7uhlv0thnq.png" alt="Image description" width="880" height="156"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The generator creates four applications. They are designed to be built and run as &lt;strong&gt;docker containers&lt;/strong&gt;, which makes it easy for them to be packaged in &lt;strong&gt;&lt;a href="https://kubernetes.io/docs/concepts/overview/what-is-kubernetes/"&gt;Kubernetes&lt;/a&gt;&lt;/strong&gt; pods. Kubernetes is a container orchestrator specifically designed for managing microservice networks. It's something like &lt;strong&gt;Docker Compose&lt;/strong&gt; but designed for microservices with a lot of great features like service discovery, load balancing, automatic rollouts and restarts, resource management, and storage mounting.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This tutorial has a lot of pieces. Install the required software below and sign up for an Azure Cloud account. You'll need a free Okta account, but you can use the Okta CLI to sign up for it later in the tutorial.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://docs.docker.com/get-docker/"&gt;Docker&lt;/a&gt;: you'll need to have both &lt;strong&gt;Docker Engine&lt;/strong&gt; and &lt;strong&gt;Docker Compose&lt;/strong&gt; installed (If you install the Docker desktop, this will automatically install both. On Linux, if you install Docker Engine individually, you will have to also &lt;a href="https://docs.docker.com/compose/install/"&gt;install Docker Compose&lt;/a&gt;) separately.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt;: you'll need a Docker Hub to host the docker images so that Azure can pull them.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://adoptopenjdk.net/"&gt;Java 11&lt;/a&gt;: this tutorial requires Java 11. If you need to manage multiple Java versions, SDKMAN! is a good solution. Check out &lt;a href="https://sdkman.io/installit"&gt;their docs to install it&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cli.okta.com/manual/#installation"&gt;Okta CLI&lt;/a&gt;: you'll use Okta to add security to the microservice network. You can register for a free account from the CLI.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://azure.microsoft.com/en-us/free/"&gt;Azure Cloud account&lt;/a&gt;: they offer a free-tier account with a $200 credit to start.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.microsoft.com/en-us/cli/azure/install-azure-cli"&gt;Azure CLI&lt;/a&gt;: you'll use the Azure CLI to manage the Kubernetes cluster.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://kubernetes.io/docs/tasks/tools/"&gt;kubectl&lt;/a&gt;: CLI to manage Kubernetes clusters.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have never had an Azure account before, you can create a new one that will allow free-tier access and has $200 credit allocated to it. This is more than enough to finish this tutorial. However, the credit does expire after 30 days. If you do not have credit left or your credit has expired, this tutorial should only cost a few dollars &lt;strong&gt;if you stop and start the AKS cluster when you are not working on it&lt;/strong&gt;. You can keep an eye on your costs using the cost explorer in the Azure portal and set alerts if you are concerned about it. Upgrading to pay-as-you-go may also alleviate some resource throttling issues around testing the AKS clusters.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring Boot microservices for Azure and Cosmos DB
&lt;/h2&gt;

&lt;p&gt;This project is based on two of Matt Raible's tutorials: &lt;a href="https://dev.to/blog/2021/01/20/reactive-java-microservices"&gt;Reactive Java Microservices with Spring Boot and JHipster&lt;/a&gt; and &lt;a href="https://dev.to/blog/2021/06/01/kubernetes-spring-boot-jhipster"&gt;Kubernetes to the Cloud with Spring Boot and JHipster&lt;/a&gt;. In these tutorials, he builds a reactive Java microservice architecture and shows how to deploy it to Google Cloud (GCP). I have modified the project to work with Azure and Cosmos DB.&lt;/p&gt;

&lt;p&gt;You will first run the project using Docker Compose. Once you have this working, you will run the project as a Kubernetes cluster on Azure. My modifications were relatively minor and involved removing the unnecessary MongoDB instances (from both the &lt;code&gt;docker-compose.yml&lt;/code&gt; file and from the Kubernetes descriptors) as well as updating environment values to point the &lt;code&gt;store&lt;/code&gt; service to the Cosmos DB instance instead of a MongoDB instance.&lt;/p&gt;

&lt;p&gt;Listed briefly, the changes I made from Matt Raible's posts to make this work with Azure and Cosmos DB are as follows. You can skip this list (and the next section) and go right to cloning the Git repository if you want, but since this documents how to update a JHipster-generated project to use Cosmos DB, I thought it was worth including.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;docker-compose/docker-compose.yml&lt;/code&gt; file:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;removed the MongoDB service&lt;/li&gt;
&lt;li&gt;updated the &lt;code&gt;store&lt;/code&gt; service property &lt;code&gt;SPRING_DATA_MONGODB_URI&lt;/code&gt; to point to the Cosmos DB instance via a &lt;code&gt;.env&lt;/code&gt; file&lt;/li&gt;
&lt;li&gt;removed the Keycloak service and the environment variables that configured auth to use Keycloak from the remaining services (not strictly necessary but cleaned things up)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the &lt;code&gt;k8s/store-k8s&lt;/code&gt; directory:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;removed the &lt;code&gt;store-mongodb.yml&lt;/code&gt; file (This creates the MongoDB Kubernetes service that our project does not need.)&lt;/li&gt;
&lt;li&gt;in &lt;code&gt;store-deployment.yml&lt;/code&gt;:

&lt;ul&gt;
&lt;li&gt;removed the &lt;code&gt;initContainers&lt;/code&gt; (The init container waits for the MongoDB instance, which is removed.)&lt;/li&gt;
&lt;li&gt;updated &lt;code&gt;SPRING_DATA_MONGODB_URI&lt;/code&gt; env value of the &lt;code&gt;store-app&lt;/code&gt; container to the Cosmos DB URI (This points the store to the Cosmos DB instance.)&lt;/li&gt;
&lt;li&gt;properly secured the Cosmos DB connection string using Kubernetes secrets and &lt;code&gt;kubeseal&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Setting the store app's initial status for Eureka
&lt;/h2&gt;

&lt;p&gt;Creating this tutorial, I ran into a problem with the store app getting stuck as &lt;code&gt;OUT_OF_SERVICE&lt;/code&gt;. When I inspected the logs, I found that the service started as &lt;code&gt;UP&lt;/code&gt;, quickly went to &lt;code&gt;DOWN&lt;/code&gt;, and then &lt;code&gt;OUT_OF_SERVICE&lt;/code&gt;. Later, it would go back to &lt;code&gt;UP&lt;/code&gt; but the Eureka server never registered this change.&lt;/p&gt;

&lt;p&gt;There's an open issue documenting this problem on &lt;a href="https://github.com/spring-cloud/spring-cloud-netflix/issues/3941"&gt;Spring Cloud Netflix&lt;/a&gt; and &lt;a href="https://github.com/Netflix/eureka/issues/1398"&gt;Netflix Eureka&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;A temporary fix taken from the issue on GitHub is to override the health reporting implementation so that it returns &lt;code&gt;DOWN&lt;/code&gt; instead of &lt;code&gt;OUT_OF_SERVICE&lt;/code&gt; while the program is still starting. This blocks it from ever reporting &lt;code&gt;OUT_OF_SERVICE&lt;/code&gt;. You can see the fix in the &lt;a href="https://github.com/oktadev/okta-azure-kubernetes-cosmosdb-example/blob/main/store/src/main/java/com/okta/developer/store/EurekaFix.java"&gt;&lt;code&gt;EurekaFix.java&lt;/code&gt;&lt;/a&gt; file in the store app.&lt;/p&gt;

&lt;h2&gt;
  
  
  Clone the microservices project from GitHub
&lt;/h2&gt;

&lt;p&gt;Clone the modified JHipster reactive microservice project from GitHub and checkout the &lt;code&gt;start&lt;/code&gt; tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/oktadev/okta-azure-kubernetes-cosmosdb-example.git &lt;span class="se"&gt;\&lt;/span&gt;
  azure-k8s-cosmosdb
&lt;span class="nb"&gt;cd &lt;/span&gt;azure-k8s-cosmosdb
git fetch &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;--tags&lt;/span&gt;
git checkout tags/start &lt;span class="nt"&gt;-b&lt;/span&gt; working
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the Azure Cosmos DB with API for MongoDB
&lt;/h2&gt;

&lt;p&gt;You need to create an Azure Cosmos DB instance. You can either use the &lt;a href="https://portal.azure.com"&gt;Azure Portal&lt;/a&gt; or the CLI to create a new Cosmos DB instance. Make sure you create a one that is &lt;strong&gt;Azure Cosmos DB API for MongoDB&lt;/strong&gt; (Cosmos DB supports various database types). If you use the portal, it's pretty self-explanatory but don't forget to enable the free tier and enable a public network.&lt;/p&gt;

&lt;p&gt;Here are the instructions for using the CLI. &lt;/p&gt;

&lt;p&gt;Log into the Azure CLI using a Bash shell. This will redirect you to a browser to log into the Azure portal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This should show you the subscriptions for your account.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"cloudName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AzureCloud"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"homeTenantId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"21c44b6d-a007-4d48-80cb-c45966ca1af9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"90eb9f51-b4be-4a9f-a69f-11b7668a874d"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"isDefault"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"managedByTenants"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Azure subscription 1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"state"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Enabled"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"tenantId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"21c44b6d-a007-4d48-80cb-c45966ca1af9"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"user"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"andrewcarterhughes@outlook.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Set the subscription in the shell. Your subscription is probably &lt;code&gt;Azure subscription 1&lt;/code&gt;, as that is the default. Make sure you use the subscription that was created when you registered for a free account (or whatever subscription you want if you already have an account).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az account &lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;--subscription&lt;/span&gt; &amp;lt;you-subscription-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a Bash shell. Create a resource group with the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az group create &lt;span class="nt"&gt;--name&lt;/span&gt; australia-east &lt;span class="nt"&gt;--location&lt;/span&gt; australiaeast
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the Cosmos DB account in the resource group. Substitute your Azure subscription name in the command below (it's probably &lt;code&gt;Azure subscription 1&lt;/code&gt;, which is what mine defaulted to).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az cosmosdb create &lt;span class="nt"&gt;--name&lt;/span&gt; jhipster-cosmosdb &lt;span class="nt"&gt;--resource-group&lt;/span&gt; australia-east &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--kind&lt;/span&gt; MongoDB &lt;span class="nt"&gt;--subscription&lt;/span&gt; &amp;lt;you-subscription-name&amp;gt; &lt;span class="nt"&gt;--enable-free-tier&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--enable-public-network&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once that command returns (it may take a few minutes), it should list a lot of JSON showing properties of the created Cosmos DB account.&lt;/p&gt;

&lt;p&gt;If you get an error that says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;(BadRequest) DNS record for cosmosdb under zone Document is already taken.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you need to change the &lt;code&gt;--name&lt;/code&gt; parameter to something else. Since this is used to generate the public URI for the database it needs to be unique across Azure. Try adding your name or a few random numbers.&lt;/p&gt;

&lt;p&gt;I'm using the Australia East location because that was the location that had free tier AKS nodes available when I wrote this tutorial. You can use any resource group you want as long as it allows you to create the AKS cluster later in the tutorial. Even if you can't use the free tier or the free credits, if you stop and start the AKS cluster between working on the tutorial, the cost should be very small (mine was less than a few dollars). The application should still work if the Cosmos DB database is in a different resource group and region since the database URI is configured to be publicly accessible.&lt;/p&gt;

&lt;p&gt;List the connection string for the Cosmos DB API for MongoDB endpoint using the following command. &lt;strong&gt;If you changed the database name above, you will need to update it in the command below.&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az cosmosdb keys list &lt;span class="nt"&gt;--type&lt;/span&gt; connection-strings &lt;span class="nt"&gt;--name&lt;/span&gt; jhipster-cosmosdb &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--resource-group&lt;/span&gt; australia-east
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will list four connection strings. You need to save (copy and paste somewhere) the first, the primary connection string. (Ellipses have been used for brevity below.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connectionStrings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"connectionString"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mongodb://jhipster-cosmosdb:XBq5KZ81V8hM63KjCOezi1arq..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Primary MongoDB Connection String"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Edit the &lt;code&gt;.env&lt;/code&gt; file in the &lt;code&gt;docker-compose&lt;/code&gt; subdirectory. Add the following variables to it, substituting your connection string for the placeholder. Make sure the connection string is enclosed in quotes. This value is referenced by the &lt;code&gt;docker-compose.yml&lt;/code&gt; file and passed to the &lt;code&gt;store&lt;/code&gt; service, pointing it to the Cosmos DB MongoDB database.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;ENCRYPT_KEY&lt;/code&gt; will be used as the key for encrypting sensitive values stored in the Spring Cloud Config and used by the JHipster registry. You can put whatever value you want in there. A UUID works well, but any string value will work. The longer, the better.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-compose/.env&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;SPRING_DATA_MONGO_URI=&amp;lt;your-connection-string&amp;gt;
ENCRYPT_KEY=&amp;lt;your-encryption-key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure identity with Okta
&lt;/h2&gt;

&lt;p&gt;Before you begin, you’ll need a free Okta developer account. Install the &lt;a href="https://cli.okta.com"&gt;Okta CLI&lt;/a&gt; and run &lt;code&gt;okta register&lt;/code&gt; to sign up for a new account. If you already have an account, run &lt;code&gt;okta login&lt;/code&gt;. Then, run &lt;code&gt;okta apps create jhipster&lt;/code&gt;. Select the default app name, or change it as you see fit.  Accept the default Redirect URI values provided for you.&lt;/p&gt;

&lt;p&gt;What does the Okta CLI do?&lt;/p&gt;

&lt;p&gt;The Okta CLI streamlines configuring a JHipster app and does several things for you:&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;Creates an OIDC app with the correct redirect URIs:
    &lt;ul&gt;
      &lt;li&gt;login: &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8080/login/oauth2/code/oidc&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8761/login/oauth2/code/oidc&lt;/code&gt;
&lt;/li&gt;
      &lt;li&gt;logout: &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8080&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8761&lt;/code&gt;
&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;Creates &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_USER&lt;/code&gt; groups that JHipster expects&lt;/li&gt;
  &lt;li&gt;Adds your current user to the &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code class="language-plaintext highlighter-rouge"&gt;ROLE_USER&lt;/code&gt; groups&lt;/li&gt;
  &lt;li&gt;Creates a &lt;code class="language-plaintext highlighter-rouge"&gt;groups&lt;/code&gt; claim in your default authorization server and adds the user’s groups to it&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The &lt;code class="language-plaintext highlighter-rouge"&gt;http://localhost:8761*&lt;/code&gt;
 redirect URIs are for the JHipster Registry, which is often used when 
creating microservices with JHipster. The Okta CLI adds these by 
default.&lt;/p&gt;

&lt;p&gt;Run &lt;code class="language-plaintext highlighter-rouge"&gt;cat .okta.env&lt;/code&gt; (or &lt;code class="language-plaintext highlighter-rouge"&gt;type .okta.env&lt;/code&gt;
 on Windows) to see the issuer and credentials for your app.

&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: You can also use the Okta Admin Console to create your app. See &lt;a href="https://www.jhipster.tech/security/#okta"&gt;Create a JHipster App on Okta&lt;/a&gt; for more information.&lt;/p&gt;


&lt;p&gt;Take note of the name because you will need to find the app in the Okta Admin Console and update the redirect URIs a little later. The &lt;code&gt;okta apps&lt;/code&gt; command creates a config file named &lt;code&gt;.okta.env&lt;/code&gt;. It will look something like the following. It helpfully lists the values you will need in the next step.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"https://dev-13337.okta.com/oauth2/default"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"2989u928u383..."&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"09328uu098u4..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both the Docker Compose project and the Kubernetes project use Spring Cloud Config to centralize configuration. The JHipster registry service makes these config values available to all of the other services in the cluster.&lt;/p&gt;

&lt;p&gt;Open &lt;code&gt;docker-compose/central-server-config/application.yml&lt;/code&gt; and add the following to the end, filling in the &lt;code&gt;issuer-uri&lt;/code&gt;, &lt;code&gt;client-id&lt;/code&gt;, and &lt;code&gt;client-secret&lt;/code&gt; taken from the &lt;code&gt;.okta.env&lt;/code&gt; file. This is the Spring Cloud Config file for the Docker Compose project.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-compose/central-server-config/application.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;oauth2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;oidc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;issuer-uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://&amp;lt;your-okta-domain&amp;gt;/oauth2/default&lt;/span&gt;
        &lt;span class="na"&gt;registration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;oidc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;client-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;client-id&amp;gt;&lt;/span&gt;
            &lt;span class="na"&gt;client-secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;client-secret&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Build the Docker images and run the app with Docker Compose
&lt;/h2&gt;

&lt;p&gt;You're all set to run the app locally using Docker and Docker Compose. You need to build the docker image for each of the projects: &lt;code&gt;gateway&lt;/code&gt;, &lt;code&gt;store&lt;/code&gt;, and &lt;code&gt;blog&lt;/code&gt; (you don't have to build the &lt;code&gt;registry&lt;/code&gt; because it uses an image).&lt;/p&gt;

&lt;p&gt;In the three different app directories, run the following Gradle command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew &lt;span class="nt"&gt;-Pprod&lt;/span&gt; bootJar jibDockerBuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The default Docker resource settings may not be enough to run this project. You may need to bump them. These settings worked for me.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPUs: 8&lt;/li&gt;
&lt;li&gt;Memory: 25 GB&lt;/li&gt;
&lt;li&gt;Swap: 2 GB&lt;/li&gt;
&lt;li&gt;Disk image size: 120 GB&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Navigate to the &lt;code&gt;docker-compose&lt;/code&gt; directory and run the app. You can use the &lt;code&gt;-d&lt;/code&gt; param to run it as a daemon but for the moment I like seeing the logs. You're just running this in Docker Compose as a warm-up for the Azure deployment anyway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give that a minute or two to finish running all the services. &lt;/p&gt;

&lt;p&gt;Open the gateway service at &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Go to &lt;strong&gt;Account&lt;/strong&gt; and &lt;strong&gt;Sign in&lt;/strong&gt;. You should be directed to the Okta sign-in form. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--eFVdl0tS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a2blbkuw6tq8sn2tnusz.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--eFVdl0tS--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a2blbkuw6tq8sn2tnusz.png" alt="Image description" width="618" height="760"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should be able to authenticate with your Okta credentials.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--m_Dc4MCw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmrxuhp7u58m4lc0ds58.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--m_Dc4MCw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mmrxuhp7u58m4lc0ds58.png" alt="Image description" width="880" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Make sure all parts of the application are working. First, test the store (which is using the Cosmos DB Mongo database) by going to &lt;strong&gt;Entities&lt;/strong&gt; and &lt;strong&gt;Product&lt;/strong&gt;. Make sure you can add a new product and see it in the list of products. Next, create a blog (&lt;strong&gt;Entities&lt;/strong&gt; and &lt;strong&gt;Blog&lt;/strong&gt;).&lt;/p&gt;

&lt;p&gt;If that works, you can take a look at the &lt;strong&gt;Administration&lt;/strong&gt; menu. There's a lot of helpful info there. &lt;/p&gt;

&lt;p&gt;You can also check the JHipster Registry at &lt;code&gt;http://localhost:8761&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--abuNTeM6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rq6fabe3h7vzrvhebb3x.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--abuNTeM6--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rq6fabe3h7vzrvhebb3x.png" alt="Image description" width="880" height="445"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Encrypt the client secret
&lt;/h2&gt;

&lt;p&gt;Leaving secrets in plain text in repositories is a security risk. There are two values in this app that are sensitive: the Cosmos DB connection string that includes the username and password and the Okta OIDC app client secret. You were able to avoid exposing the database credentials by using a &lt;code&gt;.env&lt;/code&gt; file. However, the Okta client secret is exposed as plain text in the Spring Cloud Config file (&lt;code&gt;docker-compose/central-server-config/application.yml&lt;/code&gt;). This can be encrypted using the JHipster registry.&lt;/p&gt;

&lt;p&gt;Open the registry (&lt;code&gt;http://localhost:8761&lt;/code&gt;) and click on &lt;strong&gt;Configuration&lt;/strong&gt; and &lt;strong&gt;Encryption&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5sLwFuLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ji3o313w4291l6qxbrg0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5sLwFuLl--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ji3o313w4291l6qxbrg0.png" alt="Image description" width="880" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Paste your client secret in the text box. Click &lt;strong&gt;Encrypt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;This, incidentally, is why you needed the &lt;code&gt;ENCRYPT_KEY&lt;/code&gt; property in the &lt;code&gt;.env&lt;/code&gt; file.  That's the key that JHipster registry uses to encrypt these values (so keep it secret!).&lt;/p&gt;

&lt;p&gt;You can now copy the encrypted value and paste it back into the &lt;code&gt;application.yml&lt;/code&gt; file. Make sure you include the &lt;code&gt;{cipher}&lt;/code&gt; part. It should look similar to below. Don't forget the quotes!&lt;/p&gt;

&lt;p&gt;&lt;code&gt;docker-compose/central-server-config/application.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;oauth2&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;client&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;oidc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;issuer-uri&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://dev-123456.okta.com/oauth2/default&lt;/span&gt;
        &lt;span class="na"&gt;registration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;oidc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;client-id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0oa6ycm987987uy98&lt;/span&gt;
            &lt;span class="na"&gt;client-secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{cipher}88acb434dd088acb434dd088acb434dd0..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Stop the app if you have not already using &lt;code&gt;control-c&lt;/code&gt;. Restart it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you can still sign into the gateway at &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You may be wondering (like I did initially), why can't I just put the client secret in the &lt;code&gt;.env&lt;/code&gt; file? This doesn't work because the &lt;code&gt;.env&lt;/code&gt; file is processed by Spring Cloud Config and JHipster registry after the container is composed, not by Docker Compose during the container creation, which is when the &lt;code&gt;.env&lt;/code&gt; file is processed.&lt;/p&gt;

&lt;p&gt;You're done with the Docker Compose implementation. To clean up, you can run the following command. This will stop and remove the containers, networks, volumes, and images created by &lt;code&gt;docker-compose up&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose down &lt;span class="nt"&gt;--remove-orphans&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the Azure Kubernetes cluster
&lt;/h2&gt;

&lt;p&gt;The app works locally. Now it's time to deploy it to an Azure Kubernetes Cluster (AKS). The first step is to create an AKS cluster.&lt;/p&gt;

&lt;p&gt;It's super easy to use the CLI to create a cluster. I'll show you the command below. However, there's a wrinkle. The free tier cannot create a cluster in many of the regions because of resource quotas. At least, this was the case when I was working on this tutorial. Nor is there an easy way to quickly see what regions will allow you to create a free-tier cluster. This is why I used Australia East as the region--it allowed me to create a free cluster. &lt;/p&gt;

&lt;p&gt;If the command below does not work, I suggest going to the Azure portal and creating a Kubernetes cluster there. Select &lt;strong&gt;Create a service&lt;/strong&gt; and &lt;strong&gt;Kubernetes Service&lt;/strong&gt;. You'll have to select different regions and see what sizes are available (under &lt;strong&gt;Node size&lt;/strong&gt; and &lt;strong&gt;Change size&lt;/strong&gt;) until you find a region that will allow you to create something in the free tier. But hopefully the command will work and you won't have to worry about it.&lt;/p&gt;

&lt;p&gt;The size I'm using for this tutorial is &lt;code&gt;Standard B4ms&lt;/code&gt; with two nodes. I found that I needed two nodes for the cluster to start properly.&lt;/p&gt;

&lt;p&gt;Run the following command to create the AKS cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az aks create &lt;span class="nt"&gt;--resource-group&lt;/span&gt; australia-east &lt;span class="nt"&gt;--name&lt;/span&gt; jhipster-demo &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--node-count&lt;/span&gt; 2 &lt;span class="nt"&gt;--enable-addons&lt;/span&gt; monitoring &lt;span class="nt"&gt;--generate-ssh-keys&lt;/span&gt; &lt;span class="nt"&gt;--node-vm-size&lt;/span&gt; standard_b4ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will probably take a few minutes.&lt;/p&gt;

&lt;p&gt;As a side note, you can stop the cluster at any point. This will pause billing on the cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az aks stop &lt;span class="nt"&gt;--resource-group&lt;/span&gt; australia-east &lt;span class="nt"&gt;--name&lt;/span&gt; jhipster-demo 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And you can start it again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az aks start &lt;span class="nt"&gt;--resource-group&lt;/span&gt; australia-east &lt;span class="nt"&gt;--name&lt;/span&gt; jhipster-demo 
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can stop and start the cluster from the Azure portal as well. &lt;/p&gt;

&lt;p&gt;The next step is to get the credentials for the cluster and merge them into &lt;code&gt;.kube/config&lt;/code&gt; so that &lt;code&gt;kubectl&lt;/code&gt; can use them. Use the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az aks get-credentials &lt;span class="nt"&gt;--resource-group&lt;/span&gt; australia-east &lt;span class="nt"&gt;--name&lt;/span&gt; jhipster-demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see output that ends like the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Merged &lt;span class="s2"&gt;"jhipster-demo"&lt;/span&gt; as current context &lt;span class="k"&gt;in&lt;/span&gt; /home/andrewcarterhughes/.kube/config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be able to use &lt;code&gt;kubectl&lt;/code&gt; to get the node on Azure.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get nodes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;NAME                                STATUS   ROLES   AGE    VERSION
aks-nodepool1-21657131-vmss000000   Ready    agent   105s   v1.22.6
aks-nodepool1-21657131-vmss000001   Ready    agent   108s   v1.22.6
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also list a lot of information about the cluster in JSON format using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az aks list &lt;span class="nt"&gt;--resource-group&lt;/span&gt; australia-east
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configure Kubernetes for Okta and Cosmos DB
&lt;/h2&gt;

&lt;p&gt;The Kubernetes files in the &lt;code&gt;k8s&lt;/code&gt; directory were created with the JHipster Kubernetes sub-generator (&lt;a href="https://www.jhipster.tech/kubernetes/"&gt;see the docs for info&lt;/a&gt;). To see how the original project was generated, take a look at &lt;a href="https://dev.to/blog/2021/06/01/kubernetes-spring-boot-jhipster"&gt;Matt Raible's JHipster and Kubernetes tutorial&lt;/a&gt;. As outlined above, these files were modified to work with Azure Cosmos DB instead of MongoDB in a Kubernetes pod (which is what the sub-generator assumes).&lt;/p&gt;

&lt;p&gt;Configure Spring OAuth in the Kubernetes pod by updating &lt;code&gt;k8s/registry-k8s/application-configmap.yml&lt;/code&gt;. You can use the same values you used above in the Docker Compose section, in &lt;code&gt;docker-compose/central-server-config/application.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Make sure you use the encrypted client secret enclosed in quotes with the &lt;code&gt;{cipher}&lt;/code&gt; prefix. You're going to copy both the encryption key and the encrypted client secret from the Docker Compose configuration to avoid having to re-encrypt the client secret with a new key.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;application.yml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;spring:&lt;/span&gt;
      &lt;span class="s"&gt;security:&lt;/span&gt;
        &lt;span class="s"&gt;oauth2:&lt;/span&gt;
          &lt;span class="s"&gt;client:&lt;/span&gt;
            &lt;span class="s"&gt;provider:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;issuer-uri: https://&amp;lt;your-okta-domain&amp;gt;/oauth2/default&lt;/span&gt;
            &lt;span class="s"&gt;registration:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;client-id: &amp;lt;client-id&amp;gt;&lt;/span&gt;
                &lt;span class="s"&gt;client-secret: "{cipher}&amp;lt;encrypted-client-secret&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To configure the JHipster Registry to use OIDC for authentication, you have to modify &lt;code&gt;k8s/registry-k8s/jhipster-registry.yml&lt;/code&gt; to enable the &lt;code&gt;oauth2&lt;/code&gt; profile. This has already been done for you in the example app in the project Git repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_PROFILES_ACTIVE&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod,k8s,oauth2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also in &lt;code&gt;k8s/registry-k8s/jhipster-registry.yml&lt;/code&gt;, update the &lt;code&gt;ENCRYPT_KEY&lt;/code&gt; value to use the same encryption key you used above in the Docker Compose section in the &lt;code&gt;.env&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENCRYPT_KEY&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;your-encryption-key&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To configure the &lt;code&gt;store&lt;/code&gt; service to use the Cosmo database, you need to put your connection string in &lt;code&gt;k8s/store-k8s/store-deployment.yml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_DATA_MONGODB_URI&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;&amp;lt;your-connection-string&amp;gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Both the encryption key and the database connection string are sensitive values that need to be encrypted. You'll see how to do that just a little later in this tutorial.&lt;/p&gt;

&lt;h2&gt;
  
  
  Build Docker images and push to Docker Hub
&lt;/h2&gt;

&lt;p&gt;Previously you built the docker images, but you left them in the local repository. Now you need to upload them to Docker Hub so that Azure AKS can find them. If you haven't already &lt;a href="https://hub.docker.com/"&gt;signed up for a Docker Hub account&lt;/a&gt;, please do so now. &lt;/p&gt;

&lt;p&gt;In each of the three directories (&lt;code&gt;blog&lt;/code&gt;, &lt;code&gt;store&lt;/code&gt;, and &lt;code&gt;gateway&lt;/code&gt;), run the following command. Save your Docker Hub username in a Bash variable as shown below and you can copy and paste the commands and run them in each service directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;DOCKER_HUB_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;docker-hub-username&amp;gt;
&lt;span class="c"&gt;# in blog&lt;/span&gt;
./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$DOCKER_HUB_USERNAME&lt;/span&gt;/blog
&lt;span class="c"&gt;# in store&lt;/span&gt;
./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$DOCKER_HUB_USERNAME&lt;/span&gt;/store
&lt;span class="c"&gt;# in gateway&lt;/span&gt;
./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$DOCKER_HUB_USERNAME&lt;/span&gt;/gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To briefly explain what's happening here, take a look at the blog service's Kubernetes descriptor file. It defines a container named &lt;code&gt;blog-app&lt;/code&gt; that uses the docker image &lt;code&gt;andrewcarterhughes/blog&lt;/code&gt;, which is my Docker Hub username and the blog image.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;k8s/blog-k8s/blog-deployment.yml&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;blog-app&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;andrewcarterhughes/blog&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Thus in the &lt;code&gt;k8s&lt;/code&gt; directory, each service (store, blog, gateway, and registry) defines a container and a docker image to be run in that container, along with a whole lot of configuration (which is really a lot of what JHipster is bootstrapping for you). The registry does not have a project folder because it uses a stock image that can be pulled directly from the JHipster Docker repository.&lt;/p&gt;

&lt;p&gt;To use the images you just created with Kubernetes, do a find and replace in the &lt;code&gt;k8s&lt;/code&gt; directory. Replace all instances of &lt;code&gt;andrewcarterhughes&lt;/code&gt; with your Docker Hub username (&lt;code&gt;&amp;lt;docker-hub-username&amp;gt;&lt;/code&gt;). &lt;/p&gt;

&lt;p&gt;One nice feature of Kubernetes is the ability to define &lt;a href="https://kubernetes.io/docs/concepts/workloads/pods/init-containers/"&gt;init containers&lt;/a&gt;. These are containers that run before the main container and can be used to create or wait for necessary resources like databases. I noticed while I was debugging things in this app that a lot of the errors happened in the init containers. It's helpful to know this because if you try and inspect the main container log nothing will be there because the container hasn't even started yet. You have to check the log for the init container that failed. The Kubernetes management tools that I mention below really come in handy for this.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy your microservices to Azure AKS
&lt;/h2&gt;

&lt;p&gt;You can manage a Kubernetes service purely with &lt;code&gt;kubectl&lt;/code&gt;. However, there are some pretty helpful tools for monitoring and logging. Both &lt;a href="https://github.com/derailed/k9s"&gt;k9s&lt;/a&gt; and &lt;a href="https://k8slens.dev/"&gt;Kubernetes Lens&lt;/a&gt; are great. I recommend installing one or both of these and using them to inspect and monitor your Kubernetes services. They are especially helpful when things go wrong (not that things ever go wrong, I wouldn't know anything about that, I just heard about it from friends, I swear). Kubernetes Lens is a full-on desktop app that describes itself as a Kubernetes IDE. In comparison, k9s is a lighter-weight, text-based tool. &lt;/p&gt;

&lt;p&gt;Open a Bash shell and navigate to the &lt;code&gt;k8s&lt;/code&gt; subdirectory of the project.&lt;/p&gt;

&lt;p&gt;Deploy your microservice architecture to Azure with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you open this file, you'll see that it creates the namespace and applies the project files. If you do this manually, it's important that the namespace is created first and that the registry is run before the other services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;...
&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;k8s
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; namespace.yml
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; registry-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; blog-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; gateway-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; store-&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;suffix&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can check on your pods with the following command. If you're on the free tier, this may take a few minutes. Using the pay-as-you-go plan, everything was up and running almost immediately for me.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;NAME                                  READY   STATUS    RESTARTS   AGE
blog-6896f6dd58-mbjkn                 1/1     Running   0          3m51s
blog-neo4j-0                          1/1     Running   0          3m48s
gateway-7f6d57765f-2fhfb              1/1     Running   0          3m46s
gateway-postgresql-647476b4d5-jdp5c   1/1     Running   0          3m44s
jhipster-registry-0                   1/1     Running   0          3m52s
store-7889695569-k4wkv                1/1     Running   0          3m41s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's an example of what this looks like with Kubernetes Lens (the containers were still booting).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6FjYZq9_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ffyl7qjtkx7l1tg3b7ud.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6FjYZq9_--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ffyl7qjtkx7l1tg3b7ud.png" alt="Image description" width="880" height="468"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another useful command is &lt;code&gt;describe&lt;/code&gt;. I won't replicate the output here, but if you want more detailed information for debugging, you can also run the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe pods &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To tail logs, you can use the name of the pod.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Although, like I said, k9s and Lens are really the way to go for more detailed inspection.&lt;/p&gt;

&lt;p&gt;Here's a screenshot from k9s. You can dig down into the different pods to get more detailed information and inspect logs.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gnWm3SHm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6yqek8skl7z4s9ndhtbm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gnWm3SHm--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6yqek8skl7z4s9ndhtbm.png" alt="Image description" width="880" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can use port-forwarding to see the JHipster Registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/jhipster-registry &lt;span class="nt"&gt;-n&lt;/span&gt; demo 8761
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a browser and navigate to &lt;code&gt;http://localhost:8761&lt;/code&gt;. You will be redirected to the Okta login screen, after which you will be taken to the registry.&lt;/p&gt;

&lt;p&gt;Make sure everything is green. If you have an error, check the logs for the pod that caused the error. You can restart a specific deployment by deleting it and re-applying it. For example, to restart the store, you can use the commands below from the &lt;code&gt;k8s&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete &lt;span class="nt"&gt;-f&lt;/span&gt; store-k8s/
kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; store-k8s/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once all is good, &lt;code&gt;control-c&lt;/code&gt; to stop forwarding the registry. Expose the gateway and open it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/gateway &lt;span class="nt"&gt;-n&lt;/span&gt; demo 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go to &lt;code&gt;http://localhost:8080&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Authenticate with Okta. Make sure you can add blogs, posts, tags, and products. Because the store service uses the same Cosmos DB instance that you were using with Docker Compose locally, any test products you created earlier will still be there.&lt;/p&gt;

&lt;p&gt;In preparation for the next step, delete everything you just deployed to AKS in the &lt;code&gt;demo&lt;/code&gt; namespace.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete all &lt;span class="nt"&gt;--all&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first &lt;code&gt;all&lt;/code&gt; refers to all resource types. The second &lt;code&gt;--all&lt;/code&gt; refers to every object in the resource types (as opposed to specifying an object name or ID). Thus by specifying the namespace you are deleting every object of every resource type in that namespace.&lt;/p&gt;

&lt;p&gt;This is one of the benefits of using a namespace. You can do a delete like this. If you do this from the default namespace, you'll risk deleting things you didn't mean to delete or pods added by Kubernetes and Azure for infrastructure administration.&lt;/p&gt;

&lt;p&gt;You can also just delete the entire namespace. It will be recreated if you use the Bash script to apply the deployments. This deletes absolutely everything from the namespace but is a little slower. If you do this, you need to make sure that you recreate the namespace later (I'll remind you).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl delete namespace demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Encrypt the sensitive configuration parameters
&lt;/h2&gt;

&lt;p&gt;There are two really important config values that need to be encrypted: (1) the Cosmos DB connection string (which contains the database credentials) and (2) the OIDC client secret. Because these two values are processed differently, you're going to use two slightly different methods for encrypting them.&lt;/p&gt;

&lt;p&gt;There are three different layers of encryption happening here. I found this a little confusing so I'm going to explain it briefy.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JHipster registry encryption for Spring Cloud Config values (values in &lt;code&gt;k8s/registry-k8s/application-configmap.yml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Kubernetes "encryption" (obfuscation, really) that moves secrets found in Kubernetes deployment files to base64-encoded secrets files&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kubeseal&lt;/code&gt; which hardens Kubernetes secrets to properly encrypted values&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The first above is only for Spring Cloud Config values. The latter two are used to encrypt values in the Kubernetes descriptor files (the other &lt;code&gt;yml&lt;/code&gt; files in the &lt;code&gt;k8s&lt;/code&gt; directory).&lt;/p&gt;

&lt;p&gt;When you use JHipster registry encryption, you have to define an &lt;code&gt;ENCRYPT_KEY&lt;/code&gt; value that is used by JHipster to encrypt the secrets. However, because this value is stored in &lt;code&gt;yml&lt;/code&gt; files that are going to be committed to a repository, this value must be properly encrypted to ensure the security of the Spring Cloud Config values. &lt;/p&gt;

&lt;p&gt;The Cosmos DB connection string (the &lt;code&gt;SPRING_DATA_MONGODB_URI&lt;/code&gt; env var) is a Kubernetes deployment value, same as the &lt;code&gt;ENCRYPT_KEY&lt;/code&gt;, not a Spring Cloud Config value.&lt;/p&gt;

&lt;p&gt;Thus, to harden the OIDC client secret, you must (1) define an &lt;code&gt;ENCRYPT_KEY&lt;/code&gt; to enable JHipster registry encryption, (2) use JHipster registry to encrypt the client ID and place the encrypted value in the &lt;code&gt;application-configmap.yml&lt;/code&gt;, and (3) use Kubernetes secrets and &lt;code&gt;kubeseal&lt;/code&gt; to properly encrypt the &lt;code&gt;ENCRYPT_KEY&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Securing the Cosmos DB connection string is the same as the &lt;code&gt;ENCRYPT_KEY&lt;/code&gt;: use Kubernetes secrets and &lt;code&gt;kubeseal&lt;/code&gt; to properly encrypt it.&lt;/p&gt;

&lt;p&gt;Matt Raible did a great job of explaining secrets management in Kubernetes in his post, &lt;a href="https://dev.to/blog/2021/06/01/kubernetes-spring-boot-jhipster#encrypt-your-kubernetes-secrets"&gt;Kubernetes to the Cloud with Spring Boot and JHipster&lt;/a&gt;. He also linked to a lot of great resources. I'm not going to go into much more detail explaining it here. Check his post out for more info.&lt;/p&gt;

&lt;p&gt;The first thing you need to do is install &lt;code&gt;kubeseal&lt;/code&gt; into the AKS cluster. You can take a look at &lt;a href="https://github.com/bitnami-labs/sealed-secrets"&gt;the &lt;code&gt;kubeseal&lt;/code&gt; GitHub page&lt;/a&gt; for more info.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.17.5/controller.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve the certificate keypair that this controller generates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &lt;span class="nt"&gt;-l&lt;/span&gt; sealedsecrets.bitnami.com/sealed-secrets-key &lt;span class="nt"&gt;-o&lt;/span&gt; yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the raw value of &lt;code&gt;tls.crt&lt;/code&gt; and decode it. You can use the command line, or learn more about &lt;a href="https://developer.okta.com/docs/guides/implement-grant-type/clientcreds/main/#base64-encode-the-client-id-and-client-secret"&gt;base64 encoding/decoding&lt;/a&gt; in our documentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;paste-value-here&amp;gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put the raw value in a &lt;code&gt;tls.crt&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Next, install Kubeseal. On macOS, you can use Homebrew. For other platforms, see &lt;a href="https://github.com/bitnami-labs/sealed-secrets/releases/tag/v0.17.5"&gt;the release notes&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;kubeseal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Encrypt &lt;code&gt;ENCRYPT_KEY&lt;/code&gt; and &lt;code&gt;SPRING_DATA_MONGODB_URI&lt;/code&gt; (the Cosmos DB connect string). Run the following command to do this, &lt;strong&gt;replacing the two values with your encryption key and your connection string&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic project-secrets &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;ENCRYPT_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;your-encryption-key&amp;gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;SPRING_DATA_MONGODB_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;lt;your-connection-string&amp;gt;'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="nt"&gt;-o&lt;/span&gt; yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; secrets.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you deleted the namespace earlier, you need to create it again (it won't hurt to run this if you didn't delete the namespace).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; namespace.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, use &lt;code&gt;kubeseal&lt;/code&gt; to convert the secrets to encrypted secrets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubeseal &lt;span class="nt"&gt;--cert&lt;/span&gt; tls.crt &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yaml &lt;span class="nt"&gt;-n&lt;/span&gt; demo &amp;lt; secrets.yml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; sealed-secrets.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the original secrets file and deploy your sealed secrets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;secrets.yml
kubectl apply &lt;span class="nt"&gt;-n&lt;/span&gt; demo &lt;span class="nt"&gt;-f&lt;/span&gt; sealed-secrets.yml &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; kubectl get &lt;span class="nt"&gt;-n&lt;/span&gt; demo sealedsecret project-secrets
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You need to update the &lt;code&gt;yml&lt;/code&gt; files to refer to the encrypted values. Add the following env variable to the &lt;code&gt;jhister-registry&lt;/code&gt; container in &lt;code&gt;k8s/registry-k8s/jhipster-registry.yml&lt;/code&gt; (if an &lt;code&gt;ENCRYPT_KEY&lt;/code&gt; already exists, replace it).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="s"&gt;- name&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENCRYPT_KEY&lt;/span&gt;
    &lt;span class="s"&gt;valueFrom&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project-secrets&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENCRYPT_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;k8s/store-k8s/store-deployment.yml&lt;/code&gt;, change the &lt;code&gt;SPRING_DATA_MONGODB_URI&lt;/code&gt; env variable to use the sealed secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="s"&gt;- name&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_DATA_MONGODB_URI&lt;/span&gt;
    &lt;span class="s"&gt;valueFrom&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;project-secrets&lt;/span&gt;
        &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_DATA_MONGODB_URI&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy the cluster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Give the cluster a bit to start. Check it with one of the tools I mentioned or the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once everything is ready, port forward the registry to check the services.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/jhipster-registry &lt;span class="nt"&gt;-n&lt;/span&gt; demo 8761
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure you can log in and that all the services are green. You should either be automatically logged in and redirected back to the home page or directed to log in with the Okta login screen.&lt;/p&gt;

&lt;p&gt;This is the happy dance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--5Rg8-Ytr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8e9m95dvjlhv0hzv4lh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Rg8-Ytr--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/n8e9m95dvjlhv0hzv4lh.png" alt="Image description" width="661" height="369"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Forward the gateway and test the app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/gateway &lt;span class="nt"&gt;-n&lt;/span&gt; demo 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Log in. Make sure everything works. Kick the tires. Start a blog. Create some products. Influence some people. Restore democracy. Take over the world. Whatever you want.&lt;/p&gt;

&lt;p&gt;Once you're done with everything, you can delete the resource group. This will also delete all the resources in the resource group, including the Cosmos database and the AKS cluster. If you have multiple subscriptions, you may need to add a &lt;code&gt;--subscription&lt;/code&gt; param.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;az group delete &lt;span class="nt"&gt;--name&lt;/span&gt; australia-east
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Wait for this to finish. Once it is done, it's probably a good idea to log into the &lt;a href="https://portal.azure.com"&gt;Azure Portal&lt;/a&gt; and make sure that the AKS cluster, the Cosmos DB instance, and the resource group have been deleted.&lt;/p&gt;

&lt;h2&gt;
  
  
  Azure AKS, Kubernetes, and Spring Boot microservices deployed!
&lt;/h2&gt;

&lt;p&gt;Thanks to &lt;a href="https://twitter.com/juliendubois"&gt;Julien Dubois&lt;/a&gt; for help getting this tutorial finished! &lt;/p&gt;

&lt;p&gt;In this project you saw how to deploy a JHipster microservice to Azure AKS. You saw how you can use a managed Cosmos DB instance in place of a MongoDB pod in Kubernetes. You saw how to deploy the app first with Docker Compose and then later with &lt;code&gt;kubectl&lt;/code&gt; and the Azure CLI. Finally, you properly encrypted all of the sensitive configuration values using a combination of JHipster registry encryption, Kubernetes secrets, and &lt;code&gt;kubeseal&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;As I mentioned at the top, this project is based on two of Matt Raible's tutorials: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/2021/01/20/reactive-java-microservices"&gt;Reactive Java Microservices with Spring Boot and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/2021/06/01/kubernetes-spring-boot-jhipster"&gt;Kubernetes to the Cloud with Spring Boot and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Deepu Sasidharan wrote a tutorial, &lt;a href="https://deepu.tech/deploying-jhipster-microservices-on-azure-kubernetes-service-aks/"&gt;Deploying JHipster Microservices on Azure Kubernetes Service (AKS)&lt;/a&gt;, that was also a big help.&lt;/p&gt;

&lt;p&gt;If you liked this post, there's a good chance you'll like similar ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/2022/03/03/spring-native-jhipster"&gt;Introducing Spring Native for JHipster: Serverless Full-Stack Made Easy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/2021/12/02/k8s-security-best-practices"&gt;How to Secure Your Kubernetes Clusters With Best Practices&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/2020/04/27/mobile-development-ionic-react-native-jhipster"&gt;Mobile Development with Ionic, React Native, and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/2021/03/08/jhipster-quarkus-oidc"&gt;Fast Java Made Easy with Quarkus and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/2020/12/07/spring-cloud-config"&gt;Spring Cloud Config for Shared Microservice Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/blog/2021/11/02/k8s-to-the-cloud-aws"&gt;Kubernetes To The Cloud With AWS: Deploying a Node.js App to EKS&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have questions, please ask them in the comments below! If you're into social media, follow us: &lt;a href="https://twitter.com/oktadev"&gt;@oktadev on Twitter&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/oktadev"&gt;Okta for Developers on LinkedIn&lt;/a&gt;, and &lt;a href="https://www.facebook.com/oktadevelopers"&gt;OktaDev&lt;/a&gt; on Facebook. If you like learning via video, subscribe to &lt;a href="https://youtube.com/oktadev"&gt;our YouTube channel&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>java</category>
      <category>microservices</category>
      <category>jhipster</category>
      <category>azure</category>
    </item>
    <item>
      <title>Build Secure Ionic Apps with Angular and JHipster</title>
      <dc:creator>Matt Raible</dc:creator>
      <pubDate>Thu, 16 Jun 2022 23:43:08 +0000</pubDate>
      <link>https://forem.com/jhipster/build-secure-ionic-apps-with-angular-and-jhipster-41g2</link>
      <guid>https://forem.com/jhipster/build-secure-ionic-apps-with-angular-and-jhipster-41g2</guid>
      <description>&lt;p&gt;Ionic is a framework for building mobile apps with web technologies that look and act like native apps. Because they're built with web technologies (HTML, JavaScript, and CSS), you can also deploy your Ionic apps as single-page applications. Or, even better, as progressive web apps (PWAs) that work offline.&lt;/p&gt;

&lt;p&gt;Ionic supports the big three web frameworks: Angular, React, and Vue. Once you've written your app, you can deploy it to a simulator or device with &lt;a href="https://capacitorjs.com/"&gt;Capacitor&lt;/a&gt;. Capacitor (pictured as the blue layer) provides the runtime for your app to communicate with the native operating system and vice versa.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs52yfo9m62eqg2dto9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcs52yfo9m62eqg2dto9z.png" alt="Capacitor diagram" width="800" height="896"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ionic's main competitors are native apps built with Swift or Objective-C (for iOS) and Java or Kotlin (for Android). Ionic also competes with React Native, which uses web technologies and translates them to native components.&lt;/p&gt;

&lt;p&gt;The Ionic blog has a recent post that &lt;a href="https://ionicframework.com/blog/ionic-vs-react-native-performance-comparison/"&gt;does a performance comparison between Ionic and React Native&lt;/a&gt;. TL;DR: Both options will give you a high-performance app with a truly native look and feel.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://nodejs.org"&gt;Node 16&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sdkman.io/"&gt;Java 11&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/#download-and-install"&gt;Docker Desktop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the heck is JHipster?
&lt;/h2&gt;

&lt;p&gt;This tutorial will show you how to use Ionic, Angular, and Capacitor to build a mobile app that talks to a Spring Boot backend. It won't take but a few minutes, thanks to JHipster!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.jhipster.tech"&gt;JHipster&lt;/a&gt; is an application generator that creates an Angular frontend and a Spring Boot backend based on the options you choose. It has the ability for you, as a developer, to customize what it generates with &lt;em&gt;blueprints&lt;/em&gt;. The blueprints feature has resulted in many additional options for an app: Kotlin, Spring Native, Micronaut, Quarkus, .NET Core, NestJS, and Svelte.&lt;/p&gt;

&lt;p&gt;Below is a diagram of the app you'll create in this tutorial and its authentication flow.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbjmw9rrxovwo5674qvuv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbjmw9rrxovwo5674qvuv.png" alt="JHipster Ionic OAuth 2.0 flow" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  ✨ Introducing the JHipster Ionic blueprint!
&lt;/h2&gt;

&lt;p&gt;The JHipster project has supported generating an Ionic app using a &lt;code&gt;generator-jhipster-ionic&lt;/code&gt; module for the past several years.&lt;br&gt;
As the primary maintainer of this module, I'm proud to announce that it has been re-written as a blueprint, and it's &lt;em&gt;much&lt;/em&gt; easier to understand now. The previous module relied on the Ionic CLI, the base Angular &lt;a href="https://github.com/ionic-team/starters"&gt;starter&lt;/a&gt;, the &lt;a href="https://github.com/oktadev/ionic-jhipster-starter"&gt;Ionic JHipster starter&lt;/a&gt;, and custom code to glue it all together.&lt;br&gt;
Now, the source code is all contained in one project.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/mshima"&gt;Marcelo Shima&lt;/a&gt; volunteered to do the conversion, and after a couple of months, I'm proud to say the &lt;a href="https://github.com/jhipster/generator-jhipster-ionic"&gt;JHipster Ionic blueprint&lt;/a&gt; is now available!&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1523893356744609792-214" src="https://platform.twitter.com/embed/Tweet.html?id=1523893356744609792"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1523893356744609792-214');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1523893356744609792&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;Here's how to use it:&lt;/p&gt;

&lt;p&gt;Create an &lt;code&gt;ionic-app&lt;/code&gt; directory alongside your JHipster app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;- backend
- ionic-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Navigate into &lt;code&gt;ionic-app&lt;/code&gt; using your terminal. Install Ionic for JHipster and create a new app using &lt;code&gt;jhipster-ionic&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; generator-jhipster-ionic
jhipster-ionic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll be prompted for the location of your JHipster app, a name for your Ionic app, and then you'll be off to the races!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr40ugfr92qf76dqp2vm0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr40ugfr92qf76dqp2vm0.png" alt="JHipster Ionic prompts" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can also create a JHipster app and an Ionic app simultaneously by using the bundled JHipster.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;bug-tracker &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;bug-tracker
jhipster-ionic jdl bug-tracker.jh
&lt;span class="nb"&gt;cd&lt;/span&gt; ../ionic4j
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This process will follow the same convention where the generated backend and frontend apps are side-by-side on your hard drive.&lt;/p&gt;

&lt;p&gt;Then you can run both apps from your Ionic app using easy-to-remember commands.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run backend:start
&lt;span class="c"&gt;# open a new terminal window&lt;/span&gt;
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzedj3lde8ksus72lzln5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzedj3lde8ksus72lzln5.png" alt="Ionic serve command with backend" width="800" height="462"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The JHipster Ionic blueprint currently only supports Angular. Now that it's a blueprint, it will be much easier to add support for Vue and React. If you're interested in helping out, please let me know! Okta is a platinum sponsor of the JHipster project and enjoys assigning &lt;a href="https://github.com/jhipster/generator-jhipster/issues?q=is%3Aissue+is%3Aopen+label%3A%22%24%24+bug-bounty+%24%24%22"&gt;bug bounties&lt;/a&gt; for feature development.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Build a mobile app with Ionic and Angular
&lt;/h2&gt;

&lt;p&gt;To see Ionic + JHipster in action, let's start with a &lt;a href="https://auth0.com/blog/full-stack-java-with-react-spring-boot-and-jhipster/"&gt;Full Stack Java + React app I created for the Auth0 blog&lt;/a&gt;. I updated the app to the latest version of JHipster (v7.8.1) and created an Ionic app with JHipster Ionic, so everything is guaranteed to work. This Flickr clone allows you to upload photos, tag them, and organize them into albums. First, clone the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/oktadev/okta-jhipster-ionic-example.git &lt;span class="se"&gt;\&lt;/span&gt;
  jhipster-ionic &lt;span class="nt"&gt;--depth&lt;/span&gt; 1
&lt;span class="nb"&gt;cd &lt;/span&gt;jhipster-ionic/backend
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Start the app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run ci:e2e:prepare &lt;span class="c"&gt;# starts Keycloak and PostgreSQL in Docker&lt;/span&gt;
./mvnw
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, navigate to &lt;code&gt;http://localhost:8080&lt;/code&gt; in your favorite browser.&lt;br&gt;
Sign in with &lt;code&gt;admin/admin&lt;/code&gt; credentials and rejoice when it all works.&lt;/p&gt;

&lt;p&gt;Open a new terminal window and enter the &lt;code&gt;jhipster-ionic/ionic-app&lt;/code&gt; directory. Install its dependencies and run &lt;code&gt;npm start&lt;/code&gt; to test the Ionic client.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install
&lt;/span&gt;npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be able to sign in and add a new photo.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2c43qrtu0p4r95bpbt8r.png" alt="Ionic welcome" width="800" height="1054"&gt;&lt;/td&gt;
    &lt;td&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0qeeds5yqtu0a62z1q1w.png" alt="Ionic auth with Keycloak" width="800" height="1054"&gt;&lt;/td&gt;
  &lt;/tr&gt;
  &lt;tr&gt;
    &lt;td&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo108okc0woeam08v0mua.png" alt="Ionic home after log in" width="800" height="1054"&gt;&lt;/td&gt;
    &lt;td&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpchnkkfxrrfaauoawsnf.jpg" alt="Hefe the Bus!" width="800" height="1054"&gt;&lt;/td&gt;
  &lt;/tr&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Please keep reading to learn how JHipster made all of this possible. Or, skip ahead to run your Ionic app on iOS using Capacitor.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to integrate Ionic and Spring Boot
&lt;/h2&gt;

&lt;p&gt;JHipster makes it easy to create a Spring Boot API that Spring Security protects. The JHipster Ionic blueprint generates an Ionic client that talks to your Spring Boot API and understands its auth mechanism. I created the &lt;code&gt;jhipster-ionic&lt;/code&gt; project using the following steps:&lt;/p&gt;

&lt;p&gt;Install the JHipster Ionic blueprint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; generator-jhipster-ionic@8.0.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a parent directory to hold everything:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# take is a shortcut for mdkir &amp;amp;&amp;amp; cd&lt;/span&gt;
take jhipster-ionic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Clone an existing JHipster Flickr example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/oktadev/auth0-full-stack-java-example.git backend &lt;span class="nt"&gt;--depth&lt;/span&gt; 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new directory to hold your Ionic project, then run &lt;code&gt;jhipster-ionic&lt;/code&gt; in it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;take ionic-app
jhipster-ionic
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Provide the path to your backend JHipster app and name your app &lt;code&gt;flickr2&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd8ah0cvthw59hhb1zwkh.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd8ah0cvthw59hhb1zwkh.png" alt="JHipster Ionic with Flickr2" width="800" height="487"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That's it! The blueprint will generate an Ionic client, complete with screens for editing entities, unit tests, and end-to-end tests with Cypress.&lt;/p&gt;

&lt;p&gt;Pretty slick, don't you think?! 😎&lt;/p&gt;

&lt;h3&gt;
  
  
  Run your Spring Boot API
&lt;/h3&gt;

&lt;p&gt;You'll need to start your backend first, so your Ionic app can talk to its API. First, start Keycloak and PostgreSQL in Docker containers:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;backend
npm run ci:e2e:prepare &lt;span class="c"&gt;# starts Keycloak and PostgreSQL in Docker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, update &lt;code&gt;backend/src/main/resources/config/application-prod.yml&lt;/code&gt; to allow CORS from &lt;code&gt;http://localhost:8100&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jhipster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="s"&gt;cors&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;allowed-origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8100'&lt;/span&gt;
    &lt;span class="na"&gt;allowed-methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
    &lt;span class="na"&gt;allowed-headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
    &lt;span class="na"&gt;exposed-headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;Authorization,Link,X-Total-Count,X-${jhipster.clientApp.name}-alert,X-${jhipster.clientApp.name}-error,X-${jhipster.clientApp.name}-params'&lt;/span&gt;
    &lt;span class="na"&gt;allow-credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;max-age&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1800&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, start the backend app using &lt;code&gt;./mvnw -Pprod&lt;/code&gt;. You should be able to log in at &lt;code&gt;http://localhost:8080&lt;/code&gt; (with &lt;code&gt;admin/admin&lt;/code&gt;) and add new photos using &lt;strong&gt;Entities&lt;/strong&gt; &amp;gt; &lt;strong&gt;Photos&lt;/strong&gt;. Add a few photos so you have some data to work with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F997hnqh4cwcxth2rt6rt.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F997hnqh4cwcxth2rt6rt.jpg" alt="Flickr2 photos" width="800" height="538"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Run your Ionic app
&lt;/h3&gt;

&lt;p&gt;Open another terminal and navigate to the &lt;code&gt;ionic-app&lt;/code&gt; folder. Launch your Ionic client using  &lt;code&gt;npm start&lt;/code&gt;. In your default browser, the app will be opened at &lt;code&gt;http://localhost:8100&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4yy46osk7q94sneisej.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fx4yy46osk7q94sneisej.png" alt="Ionic welcome" width="800" height="1054"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You should be able to log in with Keycloak and see all the listed entities in your app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fdgihxb51cxnjmrzagg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8fdgihxb51cxnjmrzagg.png" alt="Ionic entities" width="800" height="1054"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In the JHipster app's tutorial, there's a section where you're instructed to remove photo fields that can be calculated. Specifically, height, width, date taken, and date uploaded. These values are calculated when the photos are uploaded, so there's no reason to display them when adding a photo.&lt;/p&gt;

&lt;p&gt;To add this same functionality to your Ionic app, modify &lt;code&gt;src/app/pages/entities/photo/photo-update.html&lt;/code&gt; and wrap these fields with &lt;code&gt;&amp;lt;div *ngIf="!isNew"&amp;gt;&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;*ngIf=&lt;/span&gt;&lt;span class="s"&gt;"!isNew"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ion-item&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-label&lt;/span&gt; &lt;span class="na"&gt;position=&lt;/span&gt;&lt;span class="s"&gt;"floating"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Height&lt;span class="nt"&gt;&amp;lt;/ion-label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"height"&lt;/span&gt; &lt;span class="na"&gt;formControlName=&lt;/span&gt;&lt;span class="s"&gt;"height"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ion-input&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ion-item&amp;gt;&lt;/span&gt;
  ...
  &lt;span class="nt"&gt;&amp;lt;ion-item&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-label&amp;gt;&lt;/span&gt;Uploaded&lt;span class="nt"&gt;&amp;lt;/ion-label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;ion-datetime&lt;/span&gt; &lt;span class="na"&gt;displayFormat=&lt;/span&gt;&lt;span class="s"&gt;"MM/DD/YYYY HH:mm"&lt;/span&gt; &lt;span class="na"&gt;formControlName=&lt;/span&gt;&lt;span class="s"&gt;"uploaded"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"field_uploaded"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/ion-datetime&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ion-item&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Ionic CLI will auto-compile and reload the app in your browser when you save this file. You can prove everything works as expected by stopping your app (with &lt;code&gt;Ctrl + C&lt;/code&gt;) and running all the end-to-end tests with Cypress.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run e2e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Run your Ionic app on iOS using Capacitor
&lt;/h2&gt;

&lt;p&gt;Generate a native iOS project with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ionic build
npx ionic capacitor add ios
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add your custom scheme (&lt;code&gt;dev.localhost.ionic&lt;/code&gt;) to &lt;code&gt;ios/App/App/Info.plist&lt;/code&gt;. This scheme is configured in &lt;code&gt;src/environments/environment.ts&lt;/code&gt;; you can easily change it to something else if you like.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleURLTypes&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;dict&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleURLName&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;com.getcapacitor.capacitor&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;key&amp;gt;&lt;/span&gt;CFBundleURLSchemes&lt;span class="nt"&gt;&amp;lt;/key&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;array&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;capacitor&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;string&amp;gt;&lt;/span&gt;dev.localhost.ionic&lt;span class="nt"&gt;&amp;lt;/string&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/dict&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/array&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify the JHipster app's CORS settings (in &lt;code&gt;backend/src/main/resources/config/application-prod.yml&lt;/code&gt;) to allow &lt;code&gt;capacitor://localhost&lt;/code&gt; as an origin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jhipster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="s"&gt;cors&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;allowed-origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8100,capacitor://localhost'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart your backend app. Deploy your Ionic app to iOS Simulator and run it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cap run ios
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm you can log in and rejoice in your success!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfiqru83vwzbcthssybi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfiqru83vwzbcthssybi.png" alt="Flickr2 running on iOS" width="800" height="1731"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Run your Ionic app on Android
&lt;/h2&gt;

&lt;p&gt;Generate an Android project with Capacitor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx ionic capacitor add android
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Enable clear text traffic and add &lt;code&gt;dev.localhost.ionic&lt;/code&gt; as a scheme in &lt;code&gt;android/app/src/main/AndroidManifest.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;activity&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="na"&gt;android:usesCleartextTraffic=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="c"&gt;&amp;lt;!-- You'll need to add this intent filter so redirects work --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;intent-filter&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;action&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.action.VIEW"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.DEFAULT"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.BROWSABLE"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;data&lt;/span&gt; &lt;span class="na"&gt;android:scheme=&lt;/span&gt;&lt;span class="s"&gt;"dev.localhost.ionic"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!--data android:scheme="com.okta.dev-133337" /--&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;intent-filter&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;action&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.action.MAIN"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;category&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.intent.category.LAUNCHER"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/intent-filter&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/activity&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Modify the JHipster app's CORS settings to allow &lt;code&gt;http://localhost&lt;/code&gt; as an origin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;jhipster&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;...&lt;/span&gt;
  &lt;span class="s"&gt;cors&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;allowed-origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;http://localhost:8100,capacitor://localhost,http://localhost'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart your backend app and run your Ionic app on Android using the Capacitor CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npx cap run android
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CAUTION&lt;/strong&gt;: If you get an error when running this command, make sure to use Java 11.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You'll need to run a couple of commands to allow the emulator to communicate with JHipster and Keycloak.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;adb reverse tcp:8080 tcp:8080
adb reverse tcp:9080 tcp:9080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should be able to log in and edit entities, just like you can in a browser and on iOS!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzzq4mz2aox4l2tehxxdd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzzq4mz2aox4l2tehxxdd.png" alt="Flickr2 running on Android" width="800" height="1422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why use OpenID Connect for mobile apps?
&lt;/h2&gt;

&lt;p&gt;Storing &lt;a href="https://developer.okta.com/blog/2019/01/22/oauth-api-keys-arent-safe-in-mobile-apps"&gt;API keys and secrets in mobile apps is not safe&lt;/a&gt;. OAuth 2.0 solves this problem by not shipping any secrets in mobile apps and instead involving the user in the process of getting an access token into the app. These access tokens are unique per user, and they're updated every time the user logs in. The &lt;a href="https://www.oauth.com/oauth2-servers/pkce/"&gt;PKCE extension&lt;/a&gt; provides a solution for securely doing the OAuth flow on a mobile app even when there is no pre-provisioned secret.&lt;/p&gt;

&lt;p&gt;If you need to access an API from a mobile app, hopefully, it supports OAuth and PKCE! Thankfully most of the hard work of PKCE is handled by SDKs like &lt;a href="https://appauth.io/"&gt;AppAuth&lt;/a&gt;, so you don't have to write all that code yourself. If you're working with an API like Okta, then Okta's SDKs do PKCE automatically, so you don't have to worry about it. The JHipster Ionic blueprint uses &lt;a href="https://www.npmjs.com/package/ionic-appauth"&gt;Ionic AppAuth&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The previous sections showed you how to use Keycloak as your identity provider. If you're deploying to production, you might not want to manage your users and authentication system. That's where Okta and Auth0 can help!&lt;/p&gt;

&lt;h3&gt;
  
  
  Switch your identity provider to Okta
&lt;/h3&gt;

&lt;p&gt;If you don't have an Okta developer account, you can &lt;a href="https://developer.okta.com/signup"&gt;sign up for one&lt;/a&gt; or run &lt;code&gt;okta register&lt;/code&gt; after installing the Okta CLI.&lt;/p&gt;

&lt;p&gt;If you want to change your JHipster app to use Okta, the &lt;a href="https://cli.okta.com"&gt;Okta CLI&lt;/a&gt; makes this as easy as &lt;code&gt;okta apps create jhipster&lt;/code&gt;. This command creates a &lt;code&gt;.okta.env&lt;/code&gt; file you can source to override the default Keycloak settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .okta.env
./mvnw &lt;span class="nt"&gt;-Pprod&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With Keycloak, you don't need a separate OIDC app for Ionic. With Okta, you do. See JHipster's documentation to learn &lt;a href="https://www.jhipster.tech/security/#create-a-native-app-for-mobile-on-okta"&gt;how to create a native app for Ionic on Okta&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After you've changed the client ID in your Ionic app, run it using &lt;code&gt;npm start&lt;/code&gt;. You'll be prompted to log in using your Okta credentials at &lt;code&gt;http://localhost:8100&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Switch your identity provider to Auth0
&lt;/h3&gt;

&lt;p&gt;To switch your identity provider to Auth0, you first need an &lt;a href="https://auth0.com/signup"&gt;Auth0 account&lt;/a&gt;. Then, create a &lt;code&gt;.auth0.env&lt;/code&gt; file and see &lt;a href="https://www.jhipster.tech/security/#auth0"&gt;JHipster's Auth0 docs&lt;/a&gt; for how to populate it.&lt;/p&gt;

&lt;p&gt;Next, &lt;a href="https://www.jhipster.tech/security/#create-a-native-app-for-mobile-on-auth0"&gt;configure a native app for Ionic on Auth0&lt;/a&gt;. Once you're finished updating your Ionic app with a new client ID and audience, you should be able to run your backend and new frontend client using the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .auth0.env
npm run backend:start
&lt;span class="c"&gt;# open a new terminal&lt;/span&gt;
npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To see it in action on your mobile emulators, use the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run build

&lt;span class="c"&gt;# iOS&lt;/span&gt;
npx cap run ios

&lt;span class="c"&gt;# Android&lt;/span&gt;
npx cap run android
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Learn more about Ionic, Spring Boot, and JHipster
&lt;/h2&gt;

&lt;p&gt;I hope you've enjoyed learning about Ionic and the new Ionic blueprint for JHipster. In my opinion, it's pretty neat that you can rapidly prototype a mobile client for your JHipster. It's even better that you can use a leading-edge mobile application framework to do it.&lt;/p&gt;

&lt;p&gt;You can find the source code for this example on GitHub, in the &lt;a href="https://github.com/oktadev/okta-jhipster-ionic-example"&gt;@oktadev/okta-jhipster-ionic-example&lt;/a&gt; repository.&lt;/p&gt;

&lt;p&gt;If you liked this post, you might like these others too.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/11/22/full-stack-java"&gt;Full Stack Java with React, Spring Boot, and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2020/09/21/ionic-apple-google-signin"&gt;Ionic + Sign in with Apple and Google&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2019/06/24/ionic-4-angular-spring-boot-jhipster"&gt;Build Mobile Apps with Angular, Ionic 4, and Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2020/12/28/spring-boot-docker"&gt;How to Docker with Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/02/16/spring-data-elasticsearch"&gt;A Quick Guide to Elasticsearch with Spring Data and Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have any questions, please leave a comment below.&lt;br&gt;
You can follow &lt;a href="https://twitter.com/oktadev"&gt;@oktadev on Twitter&lt;/a&gt; and subscribe to &lt;a href="https://youtube.com/oktadev"&gt;our YouTube channel&lt;/a&gt; for more leading-edge content. We're also on &lt;a href="https://www.linkedin.com/company/oktadev/"&gt;LinkedIn&lt;/a&gt; and &lt;a href="https://www.facebook.com/oktadevelopers/"&gt;Facebook&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ionic</category>
      <category>springboot</category>
      <category>springsecurity</category>
      <category>jhipster</category>
    </item>
    <item>
      <title>Introducing Spring Native for JHipster: Serverless Full-Stack Made Easy</title>
      <dc:creator>Matt Raible</dc:creator>
      <pubDate>Thu, 24 Mar 2022 16:49:18 +0000</pubDate>
      <link>https://forem.com/jhipster/introducing-spring-native-for-jhipster-serverless-full-stack-made-easy-213b</link>
      <guid>https://forem.com/jhipster/introducing-spring-native-for-jhipster-serverless-full-stack-made-easy-213b</guid>
      <description>&lt;p&gt;Over the years, I've developed a lot of Java applications. I started writing Java code in the late 90s and spent several years doing Java before I tried another server-side language. I was impressed when I first tried building apps in Ruby on Rails, Python, and Node.js - they all started super-fast!&lt;/p&gt;

&lt;p&gt;Starting fast is cool, but we in the Java community have often asked, does it perform over time? The Java Virtual Machine is famous for performance and optimization over time.&lt;/p&gt;

&lt;p&gt;I scoffed at serverless when it first came out. Mostly because I was a Java developer and my apps didn't start in milliseconds. They also used a whole lotta memory and there was no hope in sight.&lt;/p&gt;

&lt;p&gt;Then, along came &lt;a href="https://www.graalvm.org/"&gt;GraalVM&lt;/a&gt;. It's gained support from many Java frameworks in the last few years and has made their apps start in milliseconds!&lt;/p&gt;

&lt;p&gt;Today, I'm proud to announce that this same capability is now available for your JHipster 7+ apps!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://nodejs.org/"&gt;Node 14&lt;/a&gt;+&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://sdkman.io/"&gt;Java 17 with GraalVM&lt;/a&gt;+&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/desktop/#download-and-install"&gt;Docker Desktop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you'd rather learn visually, you can &lt;a href="https://youtu.be/8hPDL9GCD5Q"&gt;watch a video of this tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/8hPDL9GCD5Q"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Why should you care about serverless?
&lt;/h2&gt;

&lt;p&gt;From &lt;a href="https://www.ibm.com/cloud/learn/serverless"&gt;IBM's What is Serverless computing?&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Serverless is a cloud execution model that enables a simpler, more cost-effective way to build and operate cloud-native applications.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's it!&lt;/p&gt;

&lt;p&gt;For companies with high traffic and large cloud bills, serverless makes sense. They can save millions of dollars a month.&lt;/p&gt;

&lt;h2&gt;
  
  
  GraalVM allows serverless Java for everyone!
&lt;/h2&gt;

&lt;p&gt;I've been playing with GraalVM and the Java frameworks that support it for around 18 months. After doing a lot of research, I blogged about &lt;a href="https://developer.okta.com/blog/2021/06/18/native-java-framework-comparison"&gt;building native Java apps with Micronaut, Quarkus, and Spring Boot&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In September 2021, I did a talk with Josh Long about Spring Native with JHipster at the San Francisco JUG.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1442698739563446276-977" src="https://platform.twitter.com/embed/Tweet.html?id=1442698739563446276"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1442698739563446276-977');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1442698739563446276&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;I flew out on a Monday, we got everything working by Wednesday afternoon, and then spoke about our experience on Wednesday night. I wrote about it in &lt;a href="https://www.linkedin.com/pulse/jhipster-works-spring-native-matt-raible/"&gt;JHipster Works with Spring Native!&lt;/a&gt; on LinkedIn and created a &lt;a href="https://github.com/jhipster/generator-jhipster/issues/16498"&gt;JHipster issue&lt;/a&gt; to automate our learnings.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Spring Native?
&lt;/h2&gt;

&lt;p&gt;Spring Native provides an API to configure Spring Boot and GraalVM so classes that are not easily discoverable become recognizable. For example, those that are instantiated using reflection. It's a really slick extension to Spring Boot and will likely disappear with Spring Boot 3 because it'll be native by default.&lt;/p&gt;

&lt;p&gt;As a result of our successful &lt;a href="https://youtu.be/F9oydL_MndA"&gt;presentation at the SF JUG&lt;/a&gt;, Josh and I were invited to speak at the Garden State JUG in December. We laid low for a few months and rekindled our research in December. I wrote about it in &lt;a href="https://www.linkedin.com/pulse/jhipster-works-spring-native-part-2-matt-raible/"&gt;JHipster works with Spring Native, Part 2!&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  🔥 Announcing the JHipster Native blueprint!
&lt;/h2&gt;

&lt;p&gt;In late January 2022, we (the JHipster team) upgraded JHipster to use Spring Boot 2.6 and released version &lt;a href="https://www.jhipster.tech/2022/01/23/jhipster-release-7.6.0.html"&gt;7.6.0&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In early February, I updated &lt;a href="https://github.com/mraible/spring-native-examples"&gt;the Spring Native with JHipster examples&lt;/a&gt; that Josh and I'd been using for research. I was ready to start automating the Spring Native integration using a JHipster module. When &lt;a href="https://github.com/jhipster/generator-jhipster/issues/16498#issuecomment-1030263905"&gt;I asked the JHipster team&lt;/a&gt; about the best way to implement it, &lt;a href="https://github.com/mshima"&gt;Marcelo Shima&lt;/a&gt; volunteered to create the initial blueprint.&lt;/p&gt;

&lt;p&gt;This was on a Friday afternoon (Mountain Time or MT). By the next morning, Marcelo had the MVP finished and provided steps to reproduce our examples. 😳&lt;/p&gt;

&lt;p&gt;I was amazed! I remember telling (my partner) Trish that he'd done a week's worth of (my estimated) work in a matter of hours.&lt;/p&gt;

&lt;p&gt;Today, I'm proud to announce the &lt;a href="https://github.com/jhipster/generator-jhipster-native"&gt;JHipster Native blueprint&lt;/a&gt; is available! Here's how to use it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-g&lt;/span&gt; generator-jhipster-native
jhipster-native
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will generate a JHipster app and integrate GraalVM automatically. You can build and run a native image with the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mvnw package &lt;span class="nt"&gt;-Pnative&lt;/span&gt;,prod &lt;span class="nt"&gt;-DskipTests&lt;/span&gt;
npm run ci:e2e:prepare &lt;span class="c"&gt;# start docker dependencies&lt;/span&gt;
./target/native-executable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;NOTE: Gradle is not currently an option. Follow &lt;a href="https://github.com/jhipster/generator-jhipster-native/issues/24"&gt;generator-jhipster-native#24&lt;/a&gt; to see when it's supported.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go native with Spring Native and GraalVM
&lt;/h2&gt;

&lt;p&gt;To see Spring Native + JHipster in action, let's look at a &lt;a href="https://auth0.com/blog/full-stack-java-with-react-spring-boot-and-jhipster/"&gt;previous JHipster app I created for the Auth0 blog&lt;/a&gt;. First, clone the example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/oktadev/auth0-full-stack-java-example.git jhipster-native
&lt;span class="nb"&gt;cd &lt;/span&gt;jhipster-native
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: Want results right away? Clone the &lt;code&gt;spring-native&lt;/code&gt; branch with the changes below already made:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone -b spring-native https://github.com/oktadev/auth0-full-stack-java-example.git jhipster-native
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Then, skip to the Configure your OpenID Connect identity provider section to continue.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Install JHipster 7.8.1 and the JHipster Native blueprint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; generator-jhipster@7.8.1
npm i &lt;span class="nt"&gt;-g&lt;/span&gt; generator-jhipster-native@1.1.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, remove all the existing project files and regenerate them. The &lt;code&gt;jhipster-native&lt;/code&gt; command includes parameters to disable caching because it's not supported by Spring Native, yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="k"&gt;*&lt;/span&gt;
jhipster-native &lt;span class="nt"&gt;--with-entities&lt;/span&gt; &lt;span class="nt"&gt;--cache-provider&lt;/span&gt; no &lt;span class="nt"&gt;--no-enable-hibernate-cache&lt;/span&gt;
&lt;span class="c"&gt;# When prompted to overwrite .gitignore, type "a" to overwrite all files&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you open the project in IntelliJ IDEA, you can use the &lt;em&gt;Commit Tools Window&lt;/em&gt; (Cmd + 0 on macOS or Ctrl + 0 on Linux/Windows) to view files that changed.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr82mghv3tbcqvwb3981q.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fr82mghv3tbcqvwb3981q.jpg" alt="IntelliJ IDEA's Commit Tools Window" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Next, run the following &lt;code&gt;git checkout&lt;/code&gt; commands to restore the files that were modified in the original example.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout .gitignore
git checkout README.md
git checkout demo.adoc
git checkout flickr2.jdl
git checkout screenshots
git checkout src/main/webapp/app/entities/photo/photo.tsx
git checkout src/main/webapp/app/entities/photo/photo-update.tsx
git checkout src/main/java/com/auth0/flickr2/config/SecurityConfiguration.java
git checkout src/main/resources/config/application-heroku.yml
git checkout src/main/resources/config/bootstrap-heroku.yml
git checkout Procfile
git checkout system.properties
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather not use the command line, you can right-click on each file and select &lt;em&gt;Rollback&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfz1mi9yzimuvt0u20ya.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frfz1mi9yzimuvt0u20ya.jpg" alt="Rollback..." width="800" height="435"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you ran the &lt;code&gt;git checkout&lt;/code&gt; commands, there are several changes I made in the first tutorial that'll need to be re-applied:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;In &lt;code&gt;src/main/resources/config/application-dev.yml&lt;/code&gt;, remove the &lt;code&gt;faker&lt;/code&gt; profile for Liquibase.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In &lt;code&gt;pom.xml&lt;/code&gt;, re-add Drew Noake's &lt;code&gt;metadata-extractor&lt;/code&gt; library:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;com.drewnoakes&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;metadata-extractor&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;version&amp;gt;&lt;/span&gt;2.16.0&lt;span class="nt"&gt;&amp;lt;/version&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, modify the &lt;code&gt;createPhoto()&lt;/code&gt; method in &lt;code&gt;src/main/java/com/auth0/flickr2/web/rest/PhotoResource.java&lt;/code&gt; to set the metadata when an image is uploaded.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.drew.imaging.ImageMetadataReader&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.drew.imaging.ImageProcessingException&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.drew.metadata.Metadata&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.drew.metadata.MetadataException&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.drew.metadata.exif.ExifSubIFDDirectory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;com.drew.metadata.jpeg.JpegDirectory&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;javax.xml.bind.DatatypeConverter&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.io.BufferedInputStream&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.io.ByteArrayInputStream&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.io.IOException&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.io.InputStream&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.time.Instant&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;java.util.Date&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PhotoResource&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="nc"&gt;ResponseEntity&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Photo&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;createPhoto&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;@Valid&lt;/span&gt; &lt;span class="nd"&gt;@RequestBody&lt;/span&gt; &lt;span class="nc"&gt;Photo&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;URISyntaxException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"REST request to save Photo : {}"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getId&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;photo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;setMetadata&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ImageProcessingException&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nc"&gt;MetadataException&lt;/span&gt; &lt;span class="n"&gt;ipe&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ipe&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getMessage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="nc"&gt;Photo&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;photoRepository&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;save&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;Photo&lt;/span&gt; &lt;span class="nf"&gt;setMetadata&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Photo&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="kd"&gt;throws&lt;/span&gt; &lt;span class="nc"&gt;ImageProcessingException&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;IOException&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;MetadataException&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DatatypeConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printBase64Binary&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getImage&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="kt"&gt;byte&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;data2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DatatypeConverter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;parseBase64Binary&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;str&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;InputStream&lt;/span&gt; &lt;span class="n"&gt;inputStream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ByteArrayInputStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data2&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;BufferedInputStream&lt;/span&gt; &lt;span class="n"&gt;bis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BufferedInputStream&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;inputStream&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;Metadata&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ImageMetadataReader&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;readMetadata&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bis&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="nc"&gt;ExifSubIFDDirectory&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getFirstDirectoryOfType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ExifSubIFDDirectory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;directory&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="nc"&gt;Date&lt;/span&gt; &lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getDateDigitized&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTaken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;date&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toInstant&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="o"&gt;}&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getTaken&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Photo EXIF date digitized not available, setting taken on date to now..."&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
            &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setTaken&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setUploaded&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Instant&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;now&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;

        &lt;span class="nc"&gt;JpegDirectory&lt;/span&gt; &lt;span class="n"&gt;jpgDirectory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getFirstDirectoryOfType&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;JpegDirectory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jpgDirectory&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setHeight&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jpgDirectory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getImageHeight&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
            &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setWidth&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;jpgDirectory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getImageWidth&lt;/span&gt;&lt;span class="o"&gt;());&lt;/span&gt;
        &lt;span class="o"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;photo&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;...&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install the React libraries needed:&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i react-photo-album react-images
&lt;/code&gt;&lt;/pre&gt;


&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;IMPORTANT&lt;/strong&gt;: In the previous tutorial, I used &lt;code&gt;react-photo-gallery&lt;/code&gt;. I switched to &lt;code&gt;react-photo-album&lt;/code&gt; because &lt;a href="https://github.com/neptunian/react-photo-gallery/issues/205#issuecomment-1086995379"&gt;it supports React 17&lt;/a&gt;. Because of this, you'll also need to update &lt;code&gt;src/main/webapp/app/entities/photo/photo.tsx&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Change &lt;code&gt;import Gallery from 'react-photo-gallery'&lt;/code&gt; to &lt;code&gt;import PhotoAlbum from 'react-photo-album'&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Change &lt;code&gt;&amp;lt;Gallery photos={photoSet} onClick={openLightbox} /&amp;gt;&lt;/code&gt; to &lt;code&gt;&amp;lt;PhotoAlbum photos={photoSet} layout="rows" onClick={openLightbox} /&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;


&lt;/blockquote&gt;
&lt;/li&gt;
&lt;li&gt;

&lt;p&gt;In &lt;code&gt;src/test/javascript/cypress/integration/entity/photo.spec.ts&lt;/code&gt;, remove the code that sets the calculated data in the &lt;code&gt;should create an instance of Photo&lt;/code&gt; test:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[data-cy="height"]`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;99459&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;99459&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[data-cy="width"]`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;61514&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;61514&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[data-cy="taken"]`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2021-10-11T16:46&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2021-10-11T16:46&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;cy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`[data-cy="uploaded"]`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2021-10-11T15:23&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;should&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;have.value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;2021-10-11T15:23&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Then, you'll need to add type hints for Drew Noake's EXIF processing library in &lt;code&gt;src/main/java/com/auth0/flickr2/Flickr2App.java&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nd"&gt;@org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;springframework&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nativex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;TypeHint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;types&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="o"&gt;...&lt;/span&gt;
        &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;drew&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ExifIFD0Directory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;drew&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ExifSubIFDDirectory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;drew&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;ExifThumbnailDirectory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;drew&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;makernotes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;AppleMakernoteDirectory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;com&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;drew&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;exif&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;GpsDirectory&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="o"&gt;})&lt;/span&gt;
&lt;span class="nd"&gt;@org&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;springframework&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nativex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;hint&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;NativeHint&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"-H:+AddAllCharsets"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;@NativeHint(options = "-H:+AddAllCharsets")&lt;/code&gt; solves the following exception that happens when you upload a photo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Caused by: java.nio.charset.UnsupportedCharsetException: Cp1252
    at java.nio.charset.Charset.forName(Charset.java:528) ~[native-executable:na]
    at com.drew.lang.Charsets.&amp;lt;clinit&amp;gt;(Charsets.java:40) ~[na:na]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once you've made all the changes (or cloned the &lt;code&gt;spring-native&lt;/code&gt; branch), you can build your hip native binary.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build a native JHipster app
&lt;/h3&gt;

&lt;p&gt;You will need a JDK with GraalVM and its &lt;code&gt;native-image&lt;/code&gt; compiler. Using SDKMAN, run the following command and set it as the default:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;sdk &lt;span class="nb"&gt;install &lt;/span&gt;java 22.0.0.2.r17-grl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, use Maven to build the project. Skip tests since there's no support for Mockito at this time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mvnw package &lt;span class="nt"&gt;-Pnative&lt;/span&gt;,prod &lt;span class="nt"&gt;-DskipTests&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This process will take a few minutes to complete.&lt;/p&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Configure your OpenID Connect identity provider
&lt;/h3&gt;

&lt;p&gt;When you generate a JHipster app with OAuth 2.0 / OIDC for authentication, it defaults to using Keycloak. It creates a &lt;code&gt;src/main/docker/keycloak.yml&lt;/code&gt; file for Docker Compose, as well as a &lt;code&gt;src/main/docker/realm-config&lt;/code&gt; directory with files to auto-create users and OIDC clients.&lt;/p&gt;

&lt;p&gt;If you want to use Keycloak for your running app, start it with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker-compose &lt;span class="nt"&gt;-f&lt;/span&gt; src/main/docker/keycloak.yml up &lt;span class="nt"&gt;-d&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you'd rather use Okta or Auth0, that's possible too!&lt;/p&gt;

&lt;h4&gt;
  
  
  Use Okta as your identity provider
&lt;/h4&gt;

&lt;p&gt;Before you begin, you’ll need a free Okta developer account. Install the &lt;a href="https://cli.okta.com"&gt;Okta CLI&lt;/a&gt; and run &lt;code&gt;okta register&lt;/code&gt; to sign up for a new account. If you already have an account, run &lt;code&gt;okta login&lt;/code&gt;. Then, run &lt;code&gt;okta apps create jhipster&lt;/code&gt;. Select the default app name, or change it as you see fit. Accept the default Redirect URI values provided for you.&lt;/p&gt;

&lt;p&gt;You'll need to source the &lt;code&gt;.okta.env&lt;/code&gt; file the Okta CLI creates to override the default Spring Security settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; .okta.env
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;If you're on Windows, you can modify this file to use &lt;code&gt;set&lt;/code&gt; instead of &lt;code&gt;export&lt;/code&gt; and rename it to &lt;code&gt;okta.bat&lt;/code&gt;. Then, run it with &lt;code&gt;okta.bat&lt;/code&gt; from the command line.&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CAUTION&lt;/strong&gt;: Modify your existing &lt;code&gt;.gitignore&lt;/code&gt; file to have &lt;code&gt;*.env&lt;/code&gt; so you don't accidentally check in your secrets!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;em&gt;Skip to Run your native JHipster app if you've configured your app for Okta and just want to see it running.&lt;/em&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Use Auth0 as your identity provider
&lt;/h4&gt;

&lt;p&gt;To switch from Keycloak to Auth0, override the Spring Security OAuth properties. You don't even need to write any code!&lt;/p&gt;

&lt;p&gt;To see how it works, create a &lt;code&gt;.auth0.env&lt;/code&gt; file in the root of your project, and fill it with the code below to override the default OIDC settings:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_PROVIDER_OIDC_ISSUER_URI&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://&amp;lt;your-auth0-domain&amp;gt;/
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-client-id&amp;gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;SPRING_SECURITY_OAUTH2_CLIENT_REGISTRATION_OIDC_CLIENT_SECRET&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-client-secret&amp;gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;JHIPSTER_SECURITY_OAUTH2_AUDIENCE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;https://&amp;lt;your-auth0-domain&amp;gt;/api/v2/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need to create a new web application in Auth0 and fill in the &lt;code&gt;&amp;lt;...&amp;gt;&lt;/code&gt; placeholders before this works.&lt;/p&gt;

&lt;h5&gt;
  
  
  Create an OpenID Connect app on Auth0
&lt;/h5&gt;

&lt;p&gt;Log in to your Auth0 account (or &lt;a href="https://auth0.com/signup"&gt;sign up&lt;/a&gt; if you don't have an account). You should have a unique domain like &lt;code&gt;dev-xxx.eu.auth0.com&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Press the &lt;strong&gt;Create Application&lt;/strong&gt; button in the &lt;a href="https://manage.auth0.com/#/applications"&gt;Applications section&lt;/a&gt;. Use a name like &lt;code&gt;JHipster Native!&lt;/code&gt;, select &lt;code&gt;Regular Web Applications&lt;/code&gt;, and click &lt;strong&gt;Create&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Switch to the &lt;strong&gt;Settings&lt;/strong&gt; tab and configure your application settings:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Allowed Callback URLs: &lt;code&gt;http://localhost:8080/login/oauth2/code/oidc&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Allowed Logout URLs: &lt;code&gt;http://localhost:8080/&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Scroll to the bottom and click &lt;strong&gt;Save Changes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Copy your Auth0 domain, client ID, and client secret into the &lt;code&gt;.auth0.env&lt;/code&gt; file you created earlier. Then, run &lt;code&gt;source .auth0.env&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the &lt;a href="https://manage.auth0.com/#/roles"&gt;roles&lt;/a&gt; section, create new roles named &lt;code&gt;ROLE_ADMIN&lt;/code&gt; and &lt;code&gt;ROLE_USER&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Create a new user account in the &lt;a href="https://manage.auth0.com/#/users"&gt;users&lt;/a&gt; section. Click on the &lt;strong&gt;Role&lt;/strong&gt; tab to assign the roles you just created to the new account.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Make sure your new user's email is verified before attempting to log in!&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Next, head to &lt;strong&gt;Auth Pipeline&lt;/strong&gt; &amp;gt; &lt;strong&gt;Rules&lt;/strong&gt; &amp;gt; &lt;strong&gt;Create&lt;/strong&gt;. Select the &lt;code&gt;Empty rule&lt;/code&gt; template. Provide a meaningful name like &lt;code&gt;Group claims&lt;/code&gt; and replace the Script content with the following.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;preferred_username&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{}).&lt;/span&gt;&lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;prepareCustomClaimKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`https://www.jhipster.tech/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;claim&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rolesClaim&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;prepareCustomClaimKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;roles&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;idToken&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rolesClaim&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;rolesClaim&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code is adding the user's roles to a custom claim (prefixed with &lt;code&gt;https://www.jhipster.tech/roles&lt;/code&gt;). Click &lt;strong&gt;Save changes&lt;/strong&gt; to continue.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: Want to have all these steps automated for you? Add a 👍 to &lt;a href="https://github.com/auth0/auth0-cli/issues/351"&gt;issue #351&lt;/a&gt; in the Auth0 CLI project.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Run your native JHipster app
&lt;/h3&gt;

&lt;p&gt;After you've built your app, it will be available in &lt;code&gt;target/native-executable&lt;/code&gt;. Start Keycloak or source your Okta/Auth0 settings. Then, run the following commands:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm run ci:e2e:prepare &lt;span class="c"&gt;# start docker dependencies&lt;/span&gt;
./target/native-executable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should start in under a second!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpullmw7odynna0aqgt75.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpullmw7odynna0aqgt75.png" alt="Started Flickr2App in 0.577 seconds" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What does the JHipster Native blueprint do?
&lt;/h2&gt;

&lt;p&gt;The JHipster Native blueprint integrates Spring Native into a JHipster project based on &lt;a href="https://github.com/mraible/spring-native-examples#readme"&gt;findings from the research by Josh Long and me&lt;/a&gt;. I documented our findings in September and December 2021.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sep 30, 2021: &lt;a href="https://www.linkedin.com/pulse/jhipster-works-spring-native-matt-raible/"&gt;JHipster Works with Spring Native!&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Dec 14, 2021: &lt;a href="https://www.linkedin.com/pulse/jhipster-works-spring-native-part-2-matt-raible/"&gt;JHipster works with Spring Native, Part 2!&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The unexpected thing is, one of the hardest problems we had to solve was with JPA and relationships. In JVM mode, everything worked fine. When running in native mode, &lt;a href="https://github.com/jhipster/generator-jhipster/issues/17794"&gt;there was an exception&lt;/a&gt;. The solution took days to figure out, but was quite simple to fix: I just had to add a type hint for &lt;code&gt;java.util.HashSet.class&lt;/code&gt;. 🤯&lt;/p&gt;

&lt;p&gt;During this experience, I was surprised to find that Spring Native &lt;a href="https://github.com/spring-projects-experimental/spring-native/issues/465"&gt;doesn't support caching yet&lt;/a&gt;. I believe this support will be added by the community soon. In the meantime, if you're looking to start/stop your infra as fast as possible, you probably don't care about caching. Caching is made for long-lived, JVM-strong, JVM-loving apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's the performance like?
&lt;/h2&gt;

&lt;p&gt;The native binary starts in just over 500ms (577ms) on my 2019 MacBook Pro with a 2.4 GHz 8-Core Intel Core i9 processor and 64 GB of RAM.&lt;/p&gt;

&lt;p&gt;If I start it in JVM mode with Maven, it takes a little over four seconds.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9fefgz4emw9bl2qzrxg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm9fefgz4emw9bl2qzrxg.png" alt="Started Flickr2App in 4.297 seconds" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As far as build time goes, Spring Native says:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Finished generating 'native-executable' in 3m 15s.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If I build a Docker image with the native binary:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;mvn spring-boot:build-image -Pprod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It takes a while the first time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total time:  07:24 min
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And it's slightly faster the second time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Total time:  06:43 min
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The amount of memory used after starting: &lt;code&gt;178 MB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The amount of memory used after running &lt;code&gt;npm run e2e&lt;/code&gt;: &lt;code&gt;211 MB&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the interest of full disclosure, here's the command I used to measure the amount of memory used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ps &lt;span class="nt"&gt;-o&lt;/span&gt; pid,rss,command | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;--color&lt;/span&gt; native | &lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{$2=int($2/1024)" MB";}{ print;}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What about the M1 Max? That's &lt;a href="https://twitter.com/mraible/status/1494417051137585152"&gt;not an option yet&lt;/a&gt;, but &lt;a href="https://github.com/oracle/graal/issues/2666#issuecomment-1057819284"&gt;could be in the next release of GraalVM&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn more about GraalVM and Spring Native
&lt;/h2&gt;

&lt;p&gt;I hope you've enjoyed learning how to make JHipster work with Spring Native and GraalVM. It's still a work-in-progress. Startup time isn't the end-all-be-all metric, but it is important in a serverless environment.&lt;/p&gt;

&lt;p&gt;You can find the source code for this example on GitHub in the &lt;a href="https://github.com/oktadev/auth0-full-stack-java-example/tree/spring-native"&gt;@oktadev/auth0-full-stack-java-example repository&lt;/a&gt;. The source for the JHipster Native blueprint is in the &lt;a href="https://github.com/jhipster/generator-jhipster-native"&gt;@jhipster/generator-jhipster-native repository&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;JHipster also has Micronaut and Quarkus blueprints. However, their native support is currently a work-in-progress. I hope to help improve them in the next few months.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/jhipster/generator-jhipster-quarkus/issues/222"&gt;JHipster Quarkus can't build native image&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/jhipster/generator-jhipster-micronaut/issues/115"&gt;JHipster Micronaut can't build native image&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you liked this post, there's a good chance you'll like similar ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2019/11/27/graalvm-java-binaries"&gt;Watch GraalVM turn your Java into binaries&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/09/16/spring-native-okta-starter"&gt;Spring Native in Action with the Okta Spring Boot Starter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/06/18/native-java-framework-comparison"&gt;Build Native Java Apps with Micronaut, Quarkus, and Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2022/01/06/native-java-helidon"&gt;Build REST APIs and Native Java Apps with Helidon&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have questions, please ask them in the comments below! If you're into social media, follow us: &lt;a href="https://twitter.com/oktadev"&gt;@oktadev on Twitter&lt;/a&gt;, &lt;a href="https://www.linkedin.com/company/oktadev"&gt;Okta for Developers on LinkedIn&lt;/a&gt;, and &lt;a href="https://www.facebook.com/oktadevelopers"&gt;OktaDev&lt;/a&gt; on Facebook. If you like learning via video, subscribe to &lt;a href="https://youtube.com/oktadev"&gt;our YouTube channel&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>springboot</category>
      <category>springnative</category>
      <category>jhipster</category>
      <category>graalvm</category>
    </item>
    <item>
      <title>Deploy JHipster Microservices to GCP with Kubernetes</title>
      <dc:creator>Matt Raible</dc:creator>
      <pubDate>Wed, 09 Mar 2022 02:03:04 +0000</pubDate>
      <link>https://forem.com/jhipster/deploy-jhipster-microservices-to-gcp-with-kubernetes-kk3</link>
      <guid>https://forem.com/jhipster/deploy-jhipster-microservices-to-gcp-with-kubernetes-kk3</guid>
      <description>&lt;p&gt;When your business or application is successful, it needs to scale. Not just technology-wise, but human-wise. When you're growing rapidly, it can be difficult to hire developers fast enough. Using a microservices architecture for your apps can allow you to divide up ownership and responsibilities, and scale teams along with your code.&lt;/p&gt;

&lt;p&gt;Kubernetes is an open-source platform for managing containerized workloads and services. Kubernetes traces its lineage &lt;a href="https://kubernetes.io/blog/2015/04/borg-predecessor-to-kubernetes/"&gt;directly from Borg&lt;/a&gt;, Google's long-rumored internal container-oriented cluster-management system.&lt;/p&gt;

&lt;p&gt;Spring Boot and Spring Cloud were some of the pioneering frameworks in Javaland. However, even they stood on the shoulders of giants when they leveraged Netflix's open-source projects to embrace and extend. In 2018, &lt;a href="https://netflixtechblog.com/netflix-oss-and-spring-boot-coming-full-circle-4855947713a0"&gt;Netflix OSS announced they'd come full circle&lt;/a&gt;, and adopted Spring Boot.&lt;/p&gt;

&lt;p&gt;Today, I'd like to show you how to build and deploy (with Kubernetes) a &lt;strong&gt;reactive&lt;/strong&gt; microservice architecture with Spring Boot, Spring Cloud, and JHipster. Why reactive? Because Spring Cloud Gateway is now the default for JHipster 7 gateways, even if you choose to build your microservices with Spring MVC.&lt;/p&gt;

&lt;p&gt;Spring Cloud Gateway is a library for building an API Gateway on top of Spring WebFlux. It easily integrates with OAuth to communicate between microservices. You just need to add a &lt;code&gt;TokenRelay&lt;/code&gt; filter.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spring&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;cloud&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;gateway&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;default-filters&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;TokenRelay&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CAUTION&lt;/strong&gt;: Spring Cloud no longer supports Netflix Zuul. An &lt;a href="https://github.com/spring-cloud/spring-cloud-gateway/issues/36"&gt;open issue&lt;/a&gt; adds Spring MVC/Servlet support to Spring Cloud Gateway. It's scheduled for implementation before the end of 2021.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prerequisites&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://sdkman.io/"&gt;Java 11+&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://nodejs.org/"&gt;Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/get-docker/"&gt;Docker&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://cloud.google.com/"&gt;Google Cloud&lt;/a&gt; Account&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can also &lt;a href="https://youtu.be/SQFl7ggNYIE"&gt;watch this tutorial as a screencast&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/SQFl7ggNYIE"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  A Brief Intro to Kubernetes (K8s)
&lt;/h2&gt;

&lt;p&gt;Kubernetes is an open-source project from Google that provides an API for deploying your apps and making them talk with each other. It helps automate deployments and updates, and manages your apps and services with limited downtime. You use Docker containers and YAML to make it all work.&lt;/p&gt;

&lt;p&gt;The YAML can be burdensome, but that's where JHipster comes in. It can generate the YAML for you!&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Kubernetes-Ready Microservices Architecture
&lt;/h2&gt;

&lt;p&gt;I showed you how to build &lt;a href="https://developer.okta.com/blog/2021/01/20/reactive-java-microservices"&gt;Reactive Java microservices with Spring Boot and JHipster&lt;/a&gt; in a previous post. Today, I'll show you how to generate K8s deployment descriptors, use Spring Cloud Config with Git, encrypt your secrets, and make it all work on Google Cloud (&lt;a href="https://cloud.google.com/kubernetes-engine/"&gt;GKE&lt;/a&gt; to be specific).&lt;/p&gt;

&lt;p&gt;Start by cloning the JHipster 7 { Vue, Spring Boot, WebFlux } reactive microservices project from GitHub:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/oktadev/java-microservices-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;java-microservices-examples/reactive-jhipster
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: If you just want to see the completed project, just cd into the project's &lt;code&gt;jhipster-k8s&lt;/code&gt; directory.&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd ../jhipster-k8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;p&gt;This project has four directories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;gateway&lt;/code&gt;: a Spring Boot + Spring Cloud Gateway project configured for OpenID Connect (OIDC) login. It's also configured as an OAuth 2.0 resource server. It contains a front-end application built with Vue.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;blog&lt;/code&gt;: a Spring Boot + WebFlux microservice that talks to a Neo4j database.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;store&lt;/code&gt;: a Spring Boot + WebFlux microservice that uses MongoDB.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;docker-compose&lt;/code&gt;: a set of Docker files that describe how to run all containers together.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The SPA app on the gateway is currently a monolith. The JHipster team is still working on &lt;a href="https://github.com/jhipster/generator-jhipster/issues/10189"&gt;micro frontends support&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you don't have JHipster installed, install it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm i &lt;span class="nt"&gt;-g&lt;/span&gt; generator-jhipster@7
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Generate Kubernetes Deployment Descriptors
&lt;/h2&gt;

&lt;p&gt;Navigate to the &lt;code&gt;reactive-jhipster&lt;/code&gt; directory. Next, create a &lt;code&gt;k8s&lt;/code&gt; directory, cd into it, and run JHipster's &lt;a href="https://www.jhipster.tech/kubernetes/"&gt;Kubernetes sub-generator&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;k8s
&lt;span class="nb"&gt;cd &lt;/span&gt;k8s
jhipster k8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll be prompted with several questions. Answer them as I did below:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Type of application: &lt;strong&gt;Microservice application&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Root directory: &lt;strong&gt;../&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Which applications? &lt;code&gt;&amp;lt;select all&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Set up monitoring? &lt;strong&gt;No&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Which applications with clustered databases? select &lt;strong&gt;store&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Admin password for JHipster Registry: &lt;/li&gt;
&lt;li&gt;Kubernetes namespace: &lt;strong&gt;demo&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Docker repository name: &lt;/li&gt;
&lt;li&gt;Command to push Docker image: &lt;code&gt;docker push&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Enable Istio? &lt;strong&gt;No&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Kubernetes service type? &lt;strong&gt;LoadBalancer&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use dynamic storage provisioning? &lt;strong&gt;Yes&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Use a specific storage class? &lt;code&gt;&amp;lt;leave empty&amp;gt;&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: If you don't want to publish your images on &lt;a href="https://hub.docker.com/"&gt;Docker Hub&lt;/a&gt;, leave the Docker repository name blank.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jasoqzaor8irqgr68qo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0jasoqzaor8irqgr68qo.png" alt="JHipster K8s command with answers" width="800" height="501"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After I answered these questions, my &lt;code&gt;k8s/.yo-rc.json&lt;/code&gt; file had the following contents:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"generator-jhipster"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"appsFolders"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"blog"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gateway"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"store"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"directoryPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"../"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"clusteredDbApps"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"store"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"serviceDiscoveryType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eureka"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"jwtSecretKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"NDFhMGY4NjF..."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dockerRepositoryName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"mraible"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dockerPushCommand"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"docker push"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kubernetesNamespace"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"demo"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kubernetesServiceType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"LoadBalancer"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kubernetesUseDynamicStorage"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"kubernetesStorageClassName"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ingressDomain"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;""&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"monitoring"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"istio"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I already showed you how to get everything working with Docker Compose &lt;a href="https://developer.okta.com/blog/2021/01/20/reactive-java-microservices#run-your-microservices-stack-with-docker-compose"&gt;in the previous tutorial&lt;/a&gt;. So today, I'd like to show you how to run things locally with &lt;a href="https://minikube.sigs.k8s.io/docs/"&gt;Minikube&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Minikube to Run Kubernetes Locally
&lt;/h2&gt;

&lt;p&gt;If you have Docker installed, you can run Kubernetes locally with Minikube. Run &lt;code&gt;minikube start&lt;/code&gt; to begin.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube &lt;span class="nt"&gt;--cpus&lt;/span&gt; 8 start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;CAUTION&lt;/strong&gt;: If this doesn't work, use &lt;code&gt;brew install minikube&lt;/code&gt;, or see &lt;a href="https://minikube.sigs.k8s.io/docs/start/"&gt;Minikube's installation instructions&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This command will start Minikube with 16 GB of RAM and 8 CPUs. Unfortunately, the default, which is 16 GB RAM and two CPUs, did not work for me.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;You can skip ahead to creating your Docker images while you wait for this to complete.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After this command executes, it'll print out a message and notify you which cluster and namespace are being used.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;🏄  Done! kubectl is now configured to use &lt;span class="s2"&gt;"minikube"&lt;/span&gt; cluster and &lt;span class="s2"&gt;"default"&lt;/span&gt; namespace by default
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: You can stop Minikube with &lt;code&gt;minikube stop&lt;/code&gt; and start over with &lt;code&gt;minikube delete&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create Docker Images with Jib
&lt;/h2&gt;

&lt;p&gt;Now, you need to build Docker images for each app. In the { &lt;code&gt;gateway&lt;/code&gt;, &lt;code&gt;blog&lt;/code&gt;, &lt;code&gt;store&lt;/code&gt; } directories, run the following Gradle command (where &lt;code&gt;&amp;lt;image-name&amp;gt;&lt;/code&gt; is &lt;code&gt;gateway&lt;/code&gt;, &lt;code&gt;store&lt;/code&gt;, or &lt;code&gt;blog&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;This command should also be in the window where you ran &lt;code&gt;jhipster k8s&lt;/code&gt;, so you can copy them from there.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;docker-repo-name&amp;gt;/&amp;lt;image-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create Private Docker Images
&lt;/h3&gt;

&lt;p&gt;You can also build your images locally and publish them to your Docker daemon. This is the default if you didn't specify a base Docker repository name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# this command exposes Docker images to minikube&lt;/span&gt;
&lt;span class="nb"&gt;eval&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;minikube docker-env&lt;span class="si"&gt;)&lt;/span&gt;
./gradlew &lt;span class="nt"&gt;-Pprod&lt;/span&gt; bootJar jibDockerBuild
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because this publishes your images locally to Docker, you'll need to make modifications to your Kubernetes deployment files to use &lt;code&gt;imagePullPolicy: IfNotPresent&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gateway-app&lt;/span&gt;
  &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gateway&lt;/span&gt;
  &lt;span class="na"&gt;imagePullPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;IfNotPresent&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Make sure to add this &lt;code&gt;imagePullPolicy&lt;/code&gt; to the following files:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;k8s/gateway-k8s/gateway-deployment.yml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k8s/blog-k8s/blog-deployment.yml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;k8s/store-k8s/store-deployment.yml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Register an OIDC App for Auth
&lt;/h2&gt;

&lt;p&gt;You've now built Docker images for your microservices, but you haven't seen them running. First, you'll need to configure Okta for authentication and authorization.&lt;/p&gt;

&lt;p&gt;Before you begin, you’ll need a free Okta developer account. Install the &lt;a href="https://cli.okta.com/"&gt;Okta CLI&lt;/a&gt; and run &lt;code&gt;okta register&lt;/code&gt; to sign up for a new account. If you already have an account, run &lt;code&gt;okta login&lt;/code&gt;. Then, run &lt;code&gt;okta apps create jhipster&lt;/code&gt;. Select the default app name, or change it as you see fit. Accept the default Redirect URI values provided for you.&lt;/p&gt;

&lt;p&gt;JHipster ships with &lt;a href="https://www.jhipster.tech/jhipster-registry/"&gt;JHipster Registry&lt;/a&gt;. It acts as a Eureka service for service discovery and contains a Spring Cloud Config server for distributing your configuration settings.&lt;/p&gt;

&lt;p&gt;Update &lt;code&gt;k8s/registry-k8s/application-configmap.yml&lt;/code&gt; to contain your OIDC settings from the &lt;code&gt;.okta.env&lt;/code&gt; file the Okta CLI just created. The Spring Cloud Config server reads from this file and shares the values with the gateway and microservices.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;application.yml&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
    &lt;span class="s"&gt;...&lt;/span&gt;
    &lt;span class="s"&gt;spring:&lt;/span&gt;
      &lt;span class="s"&gt;security:&lt;/span&gt;
        &lt;span class="s"&gt;oauth2:&lt;/span&gt;
          &lt;span class="s"&gt;client:&lt;/span&gt;
            &lt;span class="s"&gt;provider:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;issuer-uri: https://&amp;lt;your-okta-domain&amp;gt;/oauth2/default&lt;/span&gt;
            &lt;span class="s"&gt;registration:&lt;/span&gt;
              &lt;span class="s"&gt;oidc:&lt;/span&gt;
                &lt;span class="s"&gt;client-id: &amp;lt;client-id&amp;gt;&lt;/span&gt;
                &lt;span class="s"&gt;client-secret: &amp;lt;client-secret&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To configure the JHipster Registry to use OIDC for authentication, modify &lt;code&gt;k8s/registry-k8s/jhipster-registry.yml&lt;/code&gt; to enable the &lt;code&gt;oauth2&lt;/code&gt; profile.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_PROFILES_ACTIVE&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod,k8s,oauth2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that you've configured everything, it's time to see it in action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Start Your Spring Boot Microservices with K8s
&lt;/h2&gt;

&lt;p&gt;In the &lt;code&gt;k8s&lt;/code&gt; directory, start your engines!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see if everything starts up using the following command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get pods &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use the name of a pod with &lt;code&gt;kubectl logs&lt;/code&gt; to tail its logs.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl logs &amp;lt;pod-name&amp;gt; &lt;span class="nt"&gt;--tail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-1&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use port-forwarding to see the JHipster Registry.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/jhipster-registry &lt;span class="nt"&gt;-n&lt;/span&gt; demo 8761
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a browser and navigate to &lt;code&gt;http://localhost:8761&lt;/code&gt;. You'll need to sign in with your Okta credentials.&lt;/p&gt;

&lt;p&gt;Once all is green, use port-forwarding to see the gateway app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/gateway &lt;span class="nt"&gt;-n&lt;/span&gt; demo 8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, go to &lt;code&gt;http://localhost:8080&lt;/code&gt;, and you should be able to add blogs, posts, tags, and products.&lt;/p&gt;

&lt;p&gt;You can also automate testing to ensure that everything works. Set your Okta credentials as environment variables and run end-to-end tests using Cypress (from the gateway directory).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CYPRESS_E2E_USERNAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-username&amp;gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;CYPRESS_E2E_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;your-password&amp;gt;
npm run e2e
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Proof it worked for me:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20zw0ja1741d6qdbls1f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F20zw0ja1741d6qdbls1f.png" alt="Cypress end-to-end tests" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Plain Text Secrets? Uggh!
&lt;/h3&gt;

&lt;p&gt;You may notice that I used a secret in plain text in the &lt;code&gt;application-configmap.yml&lt;/code&gt; file. Secrets in plain text are a bad practice! I hope you didn't check everything into source control yet!!&lt;/p&gt;

&lt;h2&gt;
  
  
  Encrypt Your Secrets with Spring Cloud Config
&lt;/h2&gt;

&lt;p&gt;The JHipster Registry has an encryption mechanism you can use to encrypt your secrets. That way, it's safe to store them in public repositories.&lt;/p&gt;

&lt;p&gt;Add an &lt;code&gt;ENCRYPT_KEY&lt;/code&gt; to the environment variables in &lt;code&gt;k8s/registry-k8s/jhipster-registry.yml&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENCRYPT_KEY&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;really-long-string-of-random-charters-that-you-can-keep-safe&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: You can use JShell to generate a UUID you can use for your encrypt key.&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;jshell

UUID.randomUUID&lt;span class="o"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0965zjdmnqfw64bq7x4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa0965zjdmnqfw64bq7x4.png" alt="JShell UUID" width="800" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can quit by typing &lt;code&gt;/exit&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Restart your JHipster Registry containers from the &lt;code&gt;k8s&lt;/code&gt; directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Encrypt Your OIDC Client Secret
&lt;/h3&gt;

&lt;p&gt;You can encrypt your client secret by logging into &lt;code&gt;http://localhost:8761&lt;/code&gt; and going to &lt;strong&gt;Configuration&lt;/strong&gt; &amp;gt; &lt;strong&gt;Encryption&lt;/strong&gt;. If this address doesn't resolve, you'll need to port-forward again.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/jhipster-registry &lt;span class="nt"&gt;-n&lt;/span&gt; demo 8761
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy and paste your client secret from &lt;code&gt;application-configmap.yml&lt;/code&gt; (or &lt;code&gt;.okta.env&lt;/code&gt;) and click &lt;strong&gt;Encrypt&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb01ymbgzi7igaxpt6k9g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb01ymbgzi7igaxpt6k9g.png" alt="JHipster Registry Encrypt Feature" width="800" height="346"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, copy the encrypted value back into &lt;code&gt;application-configmap.yml&lt;/code&gt;. Make sure to wrap it in quotes!&lt;/p&gt;

&lt;p&gt;You can also use curl:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST http://admin:&amp;lt;password-you-set-earlier&amp;gt;@localhost:8761/config/encrypt &lt;span class="nt"&gt;-d&lt;/span&gt; your-client-secret
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you use curl, make sure to add &lt;code&gt;{cipher}&lt;/code&gt; to the beginning of the string. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;client-secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;{cipher}1b12934716c32d360c85f651a0793df2777090c..."&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Apply these changes and restart all deployments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
kubectl rollout restart deploy &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Verify everything still works at &lt;code&gt;http://localhost:8080&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: If you don't want to restart the Spring Cloud Config server when you update its configuration, see &lt;a href="https://developer.okta.com/blog/2020/12/07/spring-cloud-config#refresh-the-configuration-in-your-spring-cloud-config-server"&gt;Refresh the Configuration in Your Spring Cloud Config Server&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Change Spring Cloud Config to use Git
&lt;/h3&gt;

&lt;p&gt;You might want to store your app's configuration externally. That way, you don't have to redeploy everything to change values. Good news! Spring Cloud Config makes it easy to switch to Git instead of the filesystem to store your configuration.&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;k8s/registry-k8s/jhipster-registry.yml&lt;/code&gt;, find the following variables:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;native&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_LOCATIONS&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;file:./central-config&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below these values, add a second lookup location.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_1_TYPE&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;git&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_1_URI&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;https://github.com/mraible/reactive-java-ms-config/&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_1_SEARCH_PATHS&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_1_LABEL&lt;/span&gt;
  &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;main&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a GitHub repo that matches the URI, path, and branch you entered.&lt;/p&gt;

&lt;p&gt;In my case, I created &lt;a href="https://github.com/mraible/reactive-java-ms-config/"&gt;reactive-java-ms-config&lt;/a&gt; and added a &lt;code&gt;config/application.yml&lt;/code&gt; file in the &lt;code&gt;main&lt;/code&gt; branch. Then, I added my &lt;code&gt;spring.security.*&lt;/code&gt; values to it and removed them from &lt;code&gt;k8s/registry-k8s/application-configmap.yml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See Spring Cloud Config's &lt;a href="https://cloud.spring.io/spring-cloud-config/multi/multi__spring_cloud_config_server.html#_git_backend"&gt;Git Backend docs&lt;/a&gt; for more information.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy Spring Boot Microservices to Google Cloud (aka GCP)
&lt;/h2&gt;

&lt;p&gt;It's nice to see things running locally on your machine, but it's even better to get to production! In this section, I'll show you how to deploy your containers to Google Cloud.&lt;/p&gt;

&lt;p&gt;First, stop Minikube if you were running it previously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;minikube stop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also use &lt;code&gt;kubectl&lt;/code&gt; commands to switch clusters.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl config get-contexts
kubectl config use-context XXX
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cool kids use &lt;code&gt;kubectx&lt;/code&gt; and &lt;code&gt;kubens&lt;/code&gt; to set the default context and namespace. You can learn how to install and use them via the &lt;a href="https://github.com/ahmetb/kubectx"&gt;kubectx GitHub project&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create a Container Registry on Google Cloud
&lt;/h3&gt;

&lt;p&gt;Before the JHipster 7.0.0 release, I tested this microservice example with Kubernetes and Google Cloud. I found many solutions in Ray Tsang's &lt;a href="https://spring-gcp.saturnism.me/"&gt;Spring Boot on GCP Guides&lt;/a&gt;. &lt;a href="https://twitter.com/mraible/status/1372964263237718026"&gt;Thanks, Ray&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;To start with Google Cloud, you'll need an account and a project. &lt;a href="https://spring-gcp.saturnism.me/getting-started/google-cloud-platform"&gt;Sign up for Google Cloud Platform (GCP)&lt;/a&gt;, log in, and create a project. Open a &lt;a href="https://console.cloud.google.com/"&gt;console&lt;/a&gt; in your browser. A GCP project contains all cloud services and resources--such as virtual machines, network, load balancers--that you might use.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: You can also download and install the &lt;a href="https://cloud.google.com/sdk/"&gt;&lt;code&gt;gcloud&lt;/code&gt; CLI&lt;/a&gt; if you want to run things locally.&lt;/p&gt;

&lt;p&gt;Enable the Google Kubernetes Engine API and Container Registry:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;container.googleapis.com containerregistry.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Create a Kubernetes Cluster
&lt;/h3&gt;

&lt;p&gt;Run the following command to create a cluster for your apps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud container clusters create CLUSTER_NAME &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--zone&lt;/span&gt; us-central1-a &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--machine-type&lt;/span&gt; n1-standard-4 &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--enable-autorepair&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;--enable-autoupgrade&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I called my cluster &lt;code&gt;reactive-ms&lt;/code&gt;. See GCP's &lt;a href="https://cloud.google.com/compute/docs/regions-zones/"&gt;zones&lt;/a&gt; and &lt;a href="https://cloud.google.com/compute/docs/machine-types/"&gt;machine-types&lt;/a&gt; for other options. I found the &lt;code&gt;n1-standard-4&lt;/code&gt; to be the minimum for JHipster.&lt;/p&gt;

&lt;p&gt;You created Docker images earlier to run with Minikube. Then, those images were deployed to Docker Hub or your local Docker registry. If you deployed to Docker Hub, you can use your deployment files as-is.&lt;/p&gt;

&lt;p&gt;For Google Cloud and its Kubernetes engine (GKE), you can also publish your images to your project's registry. Thankfully, this is easy to do with Jib.&lt;/p&gt;

&lt;p&gt;Navigate to the &lt;code&gt;gateway&lt;/code&gt; directory and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;gcr.io/&amp;lt;your-project-id&amp;gt;/gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can get your project ID by running &lt;code&gt;gcloud projects list&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Repeat the process for &lt;code&gt;blog&lt;/code&gt; and &lt;code&gt;store&lt;/code&gt;. You can run these processes in parallel to speed things up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ../blog
./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;gcr.io/&amp;lt;your-project-id&amp;gt;/blog
&lt;span class="nb"&gt;cd&lt;/span&gt; ../store
./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;gcr.io/&amp;lt;your-project-id&amp;gt;/store
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: You might have to run &lt;code&gt;gcloud auth configure-docker&lt;/code&gt; for Jib to publish to your GCP container registry.&lt;/p&gt;

&lt;p&gt;Then, in your &lt;code&gt;k8s/**/*-deployment.yml&lt;/code&gt; files, add &lt;code&gt;gcr.io/&amp;lt;your-project-id&amp;gt;&lt;/code&gt; as a prefix. Remove the &lt;code&gt;imagePullPolicy&lt;/code&gt; if you specified it earlier. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;containers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gateway-app&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/jhipster7/gateway&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the &lt;code&gt;k8s&lt;/code&gt; directory, apply all the deployment descriptors to run all your images.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can monitor the progress of your deployments with &lt;code&gt;kubectl get pods -n demo&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: If you make a mistake configuring JHipster Registry and need to deploy it, you can do so with the following command:&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; registry-k8s/jhipster-registry.yml &lt;span class="nt"&gt;-n&lt;/span&gt; demo
kubectl rollout restart statefulset/jhipster-registry &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;You'll need to restart all your deployments if you changed any configuration settings that services need to retrieve.&lt;/p&gt;


&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout restart deploy &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Access Your Gateway on Google Cloud
&lt;/h3&gt;

&lt;p&gt;Once everything is up and running, get the external IP of your gateway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get svc gateway &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll need to add the external IP address as a valid redirect to your Okta OIDC app. Run &lt;code&gt;okta login&lt;/code&gt;, open the returned URL in your browser, and sign in to the Okta Admin Console. Go to the &lt;strong&gt;Applications&lt;/strong&gt; section, find your application, and edit it.&lt;/p&gt;

&lt;p&gt;Add the standard JHipster redirect URIs using the IP address. For example, &lt;code&gt;http://34.71.48.244:8080/login/oauth2/code/oidc&lt;/code&gt; for the login redirect URI, and &lt;code&gt;http://34.71.48.244:8080&lt;/code&gt; for the logout redirect URI.&lt;/p&gt;

&lt;p&gt;You can use the following command to set your gateway's IP address as a variable you can curl.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;EXTERNAL_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get svc gateway &lt;span class="nt"&gt;-ojsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{.status.loadBalancer.ingress[0].ip}"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; demo&lt;span class="si"&gt;)&lt;/span&gt;
curl &lt;span class="nv"&gt;$EXTERNAL_IP&lt;/span&gt;:8080
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run &lt;code&gt;open http://$EXTERNAL_IP:8080&lt;/code&gt;, and you should be able to sign in.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn06rd9t1ms56hc9qgwpb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fn06rd9t1ms56hc9qgwpb.png" alt="First log in on GKE" width="800" height="388"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Great! Now that you know things work, let's integrate better security, starting with HTTPS.&lt;/p&gt;

&lt;h3&gt;
  
  
  Add HTTPS to Your Reactive Gateway
&lt;/h3&gt;

&lt;p&gt;You should always use HTTPS. It's one of the easiest ways to secure things, especially with the free certificates offered these days. Ray Tsang's &lt;a href="https://spring-gcp.saturnism.me/deployment/kubernetes/load-balancing/external-load-balancing"&gt;External Load Balancing docs&lt;/a&gt; was a big help in figuring out all these steps.&lt;/p&gt;

&lt;p&gt;You'll need a static IP to assign your TLS (the official name for HTTPS) certificate.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute addresses create gateway-ingress-ip &lt;span class="nt"&gt;--global&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can run the following command to make sure it worked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud compute addresses describe gateway-ingress-ip &lt;span class="nt"&gt;--global&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'value(address)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, create a &lt;code&gt;k8s/ingress.yml&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;networking.k8s.io/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Ingress&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gateway&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kubernetes.io/ingress.global-static-ip-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gateway-ingress-ip"&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;paths&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/*&lt;/span&gt;
            &lt;span class="na"&gt;pathType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ImplementationSpecific&lt;/span&gt;
            &lt;span class="na"&gt;backend&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gateway&lt;/span&gt;
                &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;8080&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy it and make sure it worked.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; ingress.yml &lt;span class="nt"&gt;-n&lt;/span&gt; demo

&lt;span class="c"&gt;# keep running this command displays an IP address&lt;/span&gt;
&lt;span class="c"&gt;# (hint: up arrow recalls the last command)&lt;/span&gt;
kubectl get ingress gateway &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use a TLS certificate, you must have a fully qualified domain name and configure it to point to the IP address. If you don't have a real domain, you can use &lt;a href="https://nip.io/"&gt;nip.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Set the IP in a variable, as well as the domain.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;EXTERNAL_IP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;kubectl get ingress gateway &lt;span class="nt"&gt;-ojsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"{.status.loadBalancer.ingress[0].ip}"&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; demo&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;EXTERNAL_IP&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.nip.io"&lt;/span&gt;

&lt;span class="c"&gt;# Prove it works&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$DOMAIN&lt;/span&gt;
curl &lt;span class="nv"&gt;$DOMAIN&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To create a certificate, create a &lt;code&gt;k8s/certificate.yml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;EOF&lt;/span&gt;&lt;span class="sh"&gt; &amp;gt; certificate.yml
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
  name: gateway-certificate
spec:
  domains:
  # Replace the value with your domain name
  - &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;DOMAIN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="sh"&gt;
&lt;/span&gt;&lt;span class="no"&gt;EOF
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add the certificate to &lt;code&gt;ingress.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gateway&lt;/span&gt;
  &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;kubernetes.io/ingress.global-static-ip-name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gateway-ingress-ip"&lt;/span&gt;
    &lt;span class="na"&gt;networking.gke.io/managed-certificates&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;gateway-certificate"&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Deploy both files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; certificate.yml &lt;span class="nt"&gt;-f&lt;/span&gt; ingress.yml &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check your certificate's status until it prints &lt;code&gt;Status: ACTIVE&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl describe managedcertificate gateway-certificate &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;While you're waiting, you can proceed to forcing HTTPS in the next step.&lt;/p&gt;

&lt;h3&gt;
  
  
  Force HTTPS with Spring Security
&lt;/h3&gt;

&lt;p&gt;Spring Security's WebFlux support makes it easy to &lt;a href="https://docs.spring.io/spring-security/site/docs/5.5.x/reference/html5/#webflux-http-redirect"&gt;redirect to HTTPS&lt;/a&gt;. However, if you redirect &lt;em&gt;all&lt;/em&gt; HTTPS requests, the Kubernetes health checks will fail because they receive a 302 instead of a 200.&lt;/p&gt;

&lt;p&gt;Crack open &lt;code&gt;SecurityConfiguration.java&lt;/code&gt; in the gateway project and add the following code to the &lt;code&gt;springSecurityFilterChain()&lt;/code&gt; method.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;redirectToHttps&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;redirect&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;redirect&lt;/span&gt;
    &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;httpsRedirectWhen&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRequest&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;getHeaders&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;containsKey&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"X-Forwarded-Proto"&lt;/span&gt;&lt;span class="o"&gt;))&lt;/span&gt;
&lt;span class="o"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rebuild the Docker image for the gateway project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./gradlew bootJar &lt;span class="nt"&gt;-Pprod&lt;/span&gt; jib &lt;span class="nt"&gt;-Djib&lt;/span&gt;.to.image&lt;span class="o"&gt;=&lt;/span&gt;gcr.io/&amp;lt;your-project-id&amp;gt;/gateway
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run the following commands to start a rolling restart of gateway instances:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl rollout restart deployment gateway &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;TIP: Run &lt;code&gt;kubectl get deployments&lt;/code&gt; to see your deployment names.&lt;/p&gt;

&lt;p&gt;Now you should get a 302 when you access your domain. &lt;a href="https://httpie.io/"&gt;HTTPie&lt;/a&gt; is a useful alternative to curl.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftfwxj7b9db0lr126sjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fftfwxj7b9db0lr126sjp.png" alt="302 in HTTPie" width="800" height="382"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Update your Okta OIDC app to have &lt;code&gt;https://${DOMAIN}/login/oauth2/code/oidc&lt;/code&gt; as a valid redirect URI. Add &lt;code&gt;https://${DOMAIN}&lt;/code&gt; to the sign-out redirect URIs too.&lt;/p&gt;

&lt;h2&gt;
  
  
  Encrypt Your Kubernetes Secrets
&lt;/h2&gt;

&lt;p&gt;Congratulations! Now you have everything running on GKE, using HTTPS! However, you have a lot of plain-text secrets in your K8s YAML files.&lt;/p&gt;

&lt;p&gt;"But, wait!" you might say. Doesn't &lt;a href="https://kubernetes.io/docs/concepts/configuration/secret/"&gt;Kubernetes Secrets&lt;/a&gt; solve everything?&lt;/p&gt;

&lt;p&gt;In my opinion, no. They're just unencrypted base64-encoded strings stored in YAML files. There's a good chance you'll want to check in the &lt;code&gt;k8s&lt;/code&gt; directory you created.&lt;/p&gt;

&lt;p&gt;Having secrets in your source code is a bad idea! The good news is most people (where most people = my followers) manage secrets externally.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1387439868444397568-901" src="https://platform.twitter.com/embed/Tweet.html?id=1387439868444397568"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1387439868444397568-901');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1387439868444397568&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Watch &lt;a href="https://www.youtube.com/watch?v=cQAEK9PBY8U"&gt;Kubernetes Secrets in 5 Minutes&lt;/a&gt; if you want to learn more about Kubernetes Secrets.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Current State of Secret Management in Kubernetes
&lt;/h3&gt;

&lt;p&gt;I recently noticed a tweet from &lt;a href="https://twitter.com/daniel_bilar/status/1379845799086022661"&gt;Daniel Jacob Bilar&lt;/a&gt; that links to a talk from FOSDEM 2021 on the &lt;a href="https://fosdem.org/2021/schedule/event/kubernetes_secret_management/"&gt;current state of secret management within Kubernetes&lt;/a&gt;. It's an excellent overview of the various options.&lt;/p&gt;

&lt;h3&gt;
  
  
  Store Secrets in Git with Sealed Secrets and Kubeseal
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://bitnami.com/"&gt;Bitnami&lt;/a&gt; has a &lt;a href="https://github.com/bitnami-labs/sealed-secrets"&gt;Sealed Secrets&lt;/a&gt; Apache-licensed open source project. Its README explains how it works.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: "I can manage all my K8s config in git, except Secrets."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: Encrypt your Secret into a SealedSecret, which is safe to store - even to a public repository. The SealedSecret can be decrypted only by the controller running in the target cluster, and nobody else (not even the original author) is able to obtain the original Secret from the SealedSecret.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://dev.to/stack-labs/store-your-kubernetes-secrets-in-git-thanks-to-kubeseal-hello-sealedsecret-2i6h"&gt;Store your Kubernetes Secrets in Git thanks to Kubeseal. Hello SealedSecret!&lt;/a&gt; by &lt;a href="https://twitter.com/aurelievache"&gt;Aurélie Vache&lt;/a&gt; provides an excellent overview of how to use it.&lt;/p&gt;

&lt;p&gt;First, you'll need to install the Sealed Secrets CRD (Custom Resource Definition).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl apply &lt;span class="nt"&gt;-f&lt;/span&gt; https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.16.0/controller.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve the certificate keypair that this controller generates.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl get secret &lt;span class="nt"&gt;-n&lt;/span&gt; kube-system &lt;span class="nt"&gt;-l&lt;/span&gt; sealedsecrets.bitnami.com/sealed-secrets-key &lt;span class="nt"&gt;-o&lt;/span&gt; yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the raw value of &lt;code&gt;tls.crt&lt;/code&gt; and decode it. You can use the command line, or learn more about &lt;a href="https://developer.okta.com/docs/guides/implement-grant-type/clientcreds/main/#base64-encode-the-client-id-and-client-secret"&gt;base64 encoding/decoding&lt;/a&gt; in our documentation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &amp;lt;paste-value-here&amp;gt; | &lt;span class="nb"&gt;base64&lt;/span&gt; &lt;span class="nt"&gt;--decode&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put the raw value in a &lt;code&gt;tls.crt&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Next, install Kubeseal. On macOS, you can use Homebrew. For other platforms, see &lt;a href="https://github.com/bitnami-labs/sealed-secrets/releases/tag/v0.16.0"&gt;the release notes&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;brew &lt;span class="nb"&gt;install &lt;/span&gt;kubeseal
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The major item you need to encrypt in this example is the &lt;code&gt;ENCRYPT_KEY&lt;/code&gt; you used to encrypt the OIDC client secret. Run the following command to do this, where the value comes from your &lt;code&gt;k8s/registry-k8s/jhipster-registry.yml&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl create secret generic encrypt-key &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--from-literal&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;ENCRYPT_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'your-value-here'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--dry-run&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;client &lt;span class="nt"&gt;-o&lt;/span&gt; yaml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; secrets.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, use &lt;code&gt;kubeseal&lt;/code&gt; to convert the secrets to encrypted secrets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubeseal &lt;span class="nt"&gt;--cert&lt;/span&gt; tls.crt &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;yaml &lt;span class="nt"&gt;-n&lt;/span&gt; demo &amp;lt; secrets.yml &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; sealed-secrets.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Remove the original secrets file and deploy your sealed secrets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm &lt;/span&gt;secrets.yml
kubectl apply &lt;span class="nt"&gt;-n&lt;/span&gt; demo &lt;span class="nt"&gt;-f&lt;/span&gt; sealed-secrets.yml &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; kubectl get &lt;span class="nt"&gt;-n&lt;/span&gt; demo sealedsecret encrypt-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Configure JHipster Registry to use the Sealed Secret
&lt;/h3&gt;

&lt;p&gt;In &lt;code&gt;k8s/registry-k8s/jhipster-registry.yml&lt;/code&gt;, change the &lt;code&gt;ENCRYPT_KEY&lt;/code&gt; to use your new secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENCRYPT_KEY&lt;/span&gt;
  &lt;span class="na"&gt;valueFrom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secretKeyRef&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;encrypt-key&lt;/span&gt;
      &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ENCRYPT_KEY&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;TIP&lt;/strong&gt;: You should be able to encrypt other secrets, like your database passwords, using a similar technique.&lt;/p&gt;

&lt;p&gt;Now, redeploy JHipster Registry and restart all your deployments.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./kubectl-apply.sh &lt;span class="nt"&gt;-f&lt;/span&gt;
kubectl rollout restart deployment &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use port-forwarding to see the JHipster Registry locally.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl port-forward svc/jhipster-registry &lt;span class="nt"&gt;-n&lt;/span&gt; demo 8761
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0l9lkw1n9csllcrapv3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fb0l9lkw1n9csllcrapv3.png" alt="Port-forwarding the Registry to localhost" width="800" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Google Cloud Secret Manager
&lt;/h3&gt;

&lt;p&gt;Google Cloud has a &lt;a href="https://spring-gcp.saturnism.me/app-dev/cloud-services/secret-management"&gt;Secret Manager&lt;/a&gt; you can use to store your secrets. There's even a &lt;a href="https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#secret-manager"&gt;Spring Boot starter&lt;/a&gt; to make it convenient to retrieve these values in your app.&lt;/p&gt;

&lt;p&gt;For example, you could store your database password in a properties file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;spring.datasource.password&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${sm://my-db-password}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is pretty slick, but I like to remain cloud-agnostic. Also, I like how the JHipster Registry allows me to store encrypted secrets in Git.&lt;/p&gt;

&lt;h3&gt;
  
  
  Use Spring Vault for External Secrets
&lt;/h3&gt;

&lt;p&gt;Using an external key management solution like &lt;a href="https://www.hashicorp.com/products/vault"&gt;HashiCorp Vault&lt;/a&gt; is also recommended. The JHipster Registry will have &lt;a href="https://github.com/jhipster/jhipster-registry/pull/498"&gt;Vault support in its next release&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In the meantime, I recommend reading &lt;a href="https://developer.okta.com/blog/2020/05/04/spring-vault"&gt;Secure Secrets With Spring Cloud Config and Vault&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scale Your Reactive Java Microservices
&lt;/h2&gt;

&lt;p&gt;You can scale your instances using the &lt;code&gt;kubectl scale&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;kubectl scale deployments/store &lt;span class="nt"&gt;--replicas&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;2 &lt;span class="nt"&gt;-n&lt;/span&gt; demo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scaling will work just fine for the microservice apps because they're set up as OAuth 2.0 resource servers and are therefore stateless.&lt;/p&gt;

&lt;p&gt;However, the gateway uses Spring Security's OIDC login feature and stores the access tokens in the session. So if you scale it, sessions won't be shared. Single sign-on should still work; you'll just have to do the OAuth dance to get tokens if you hit a different instance.&lt;/p&gt;

&lt;p&gt;To synchronize sessions, you can use &lt;a href="https://developer.okta.com/blog/2020/12/14/spring-session-redis"&gt;Spring Session and Redis&lt;/a&gt; with JHipster.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;CAUTION&lt;/strong&gt;: If you leave everything running on Google Cloud, you will be charged for usage. Therefore, I recommend removing your cluster or deleting your namespace (&lt;code&gt;kubectl delete ns demo&lt;/code&gt;) to reduce your cost.&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud container clusters delete &amp;lt;cluster-name&amp;gt; --zone=us-central1-a
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;You can delete your Ingress IP address too:&lt;/p&gt;


&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud compute addresses delete gateway-ingress-ip --global
&lt;/code&gt;&lt;/pre&gt;

&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Monitor Your Kubernetes Cluster with K9s
&lt;/h2&gt;

&lt;p&gt;Using &lt;code&gt;kubectl&lt;/code&gt; to monitor your Kubernetes cluster can get tiresome. That's where &lt;a href="https://github.com/derailed/k9s"&gt;K9s&lt;/a&gt; can be helpful. It provides a terminal UI to interact with your Kubernetes clusters. K9s was created by my good friend &lt;a href="https://twitter.com/kitesurfer"&gt;Fernand Galiana&lt;/a&gt;. He's also created a commercial version called &lt;a href="https://k9salpha.io/"&gt;K9sAlpha&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install it on macOS, run &lt;code&gt;brew install k9s&lt;/code&gt;. Then run &lt;code&gt;k9s -n demo&lt;/code&gt; to start it. You can navigate to your pods, select them with &lt;strong&gt;Return&lt;/strong&gt;, and navigate back up with &lt;strong&gt;Esc&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54ickve8sfhbp0ppvm03.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F54ickve8sfhbp0ppvm03.gif" alt="K9s in Action" width="800" height="391"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's also &lt;a href="https://github.com/kdash-rs/kdash"&gt;KDash&lt;/a&gt;, from JHipster co-lead, &lt;a href="https://twitter.com/deepu105"&gt;Deepu K Sasidharan&lt;/a&gt;. It's a simple K8s terminal dashboard built with Rust. Deepu recently &lt;a href="https://twitter.com/deepu105/status/1383017556546584578"&gt;released an MVP of the project&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If for some reason you don't like CLI's, you can try &lt;a href="https://www.kubernetic.com/"&gt;Kubernetic&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Continuous Integration and Delivery of JHipster Microservices
&lt;/h2&gt;

&lt;p&gt;This tutorial doesn't mention continuous integration and delivery of your reactive microservice architecture. I plan to cover that in a future post. If you have a solution you like, please leave a comment.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spring on Google Cloud Platform
&lt;/h2&gt;

&lt;p&gt;JHipster uses Docker containers to run all its databases in this example. However, there are a number of Google Cloud services you can use as alternatives. See the &lt;a href="https://spring.io/projects/spring-cloud-gcp"&gt;Spring Cloud GCP project on GitHub&lt;/a&gt; for more information.&lt;/p&gt;

&lt;p&gt;I didn't mention Testcontainers in this post. However, &lt;a href="https://atomfrede.gitlab.io/2019/05/jhipster-with-testcontainers/"&gt;JHipster does support using them&lt;/a&gt;. Testcontainers also has a &lt;a href="https://www.testcontainers.org/modules/gcloud/"&gt;GCloud Module&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Istio?
&lt;/h2&gt;

&lt;p&gt;I didn't use Istio in this example because I didn't want to complicate things. Learning Kubernetes is hard enough without learning another system on top of it. Istio acts as a network between your containers that can do networky things like authentication, authorization, monitoring, and retries. I like to think of it as AOP for containers.&lt;/p&gt;

&lt;p&gt;If you'd like to see how to use JHipster with Istio, see &lt;a href="https://dev.to/deepu105/how-to-set-up-java-microservices-with-istio-service-mesh-on-kubernetes-5bkn"&gt;How to set up Java microservices with Istio service mesh on Kubernetes&lt;/a&gt; by JHipster co-lead &lt;a href="https://twitter.com/deepu105"&gt;Deepu K Sasidharan&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Fernand Galiana recommends checking out BPF (Berkeley Packet Filter) and &lt;a href="https://cilium.io/"&gt;Cilium&lt;/a&gt;. Cilium is open source software for transparently providing and securing the network and API connectivity between application services deployed using Linux container management platforms such as Kubernetes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learn More About Kubernetes, Spring Boot, and JHipster
&lt;/h2&gt;

&lt;p&gt;This blog post showed you how to deploy your reactive Java microservices to production using Kubernetes. JHipster did much of the heavy lifting for you since it generated all the YAML-based deployment descriptors. Since no one really likes writing YAML, I'm calling that a win!&lt;/p&gt;

&lt;p&gt;You learned how to use JHipster Registry to encrypt your secrets and configure Git as a configuration source for Spring Cloud Config. Bitnami's Sealed Secrets is a nice companion to encrypt the secrets in your Kubernetes deployment descriptors.&lt;/p&gt;

&lt;p&gt;For more information about storing your secrets externally, these additional resources might help.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://twitter.com/kelseyhightower/status/1393062669754667017"&gt;Kelsey Hightower's Vault on Cloud Run Tutorial&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://twitter.com/jstrachan/status/1393213646340337670"&gt;James Strachan's Helm Post Renderer&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can find the source code for this example on GitHub in our &lt;a href="https://github.com/oktadev/java-microservices-examples"&gt;Java microservices examples repository&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/oktadev/java-microservices-examples.git
&lt;span class="nb"&gt;cd &lt;/span&gt;java-microservices-examples/jhipster-k8s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;See JHipster's documentation on &lt;a href="https://www.jhipster.tech/kubernetes/"&gt;Kubernetes&lt;/a&gt; and &lt;a href="https://www.jhipster.tech/gcp/"&gt;GCP&lt;/a&gt; if you'd like more concise instructions.&lt;/p&gt;

&lt;p&gt;If you enjoyed this post, I think you'll like these others as well:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/01/20/reactive-java-microservices"&gt;Reactive Java Microservices with Spring Boot and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2020/08/17/micronaut-jhipster-heroku"&gt;Build a Secure Micronaut and Angular App with JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2021/03/08/jhipster-quarkus-oidc"&gt;Fast Java Made Easy with Quarkus and JHipster&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2020/12/28/spring-boot-docker"&gt;How to Docker with Spring Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.okta.com/blog/2020/03/23/microservice-security-patterns"&gt;Security Patterns for Microservice Architectures&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://developer.okta.com/blog/2019/04/01/spring-boot-microservices-with-kubernetes"&gt;Build a Microservice Architecture with Spring Boot and Kubernetes&lt;/a&gt; (uses Spring Boot 2.1)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you have any questions, please ask them in the comments below.&lt;/p&gt;

&lt;p&gt;To be notified when we publish new blog posts, follow us on &lt;a href="https://twitter.com/oktadev"&gt;Twitter&lt;/a&gt; or &lt;a href="https://www.linkedin.com/company/oktadev"&gt;LinkedIn&lt;/a&gt;. We frequently publish videos to our &lt;a href="https://youtube.com/c/oktadev"&gt;YouTube channel&lt;/a&gt; too. &lt;a href="https://youtube.com/c/oktadev?sub_confirmation=1"&gt;Subscribe today&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A huge thanks goes to &lt;a href="https://twitter.com/kitesurfer"&gt;Fernand Galiana&lt;/a&gt; for his review and detailed feedback.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>jhipster</category>
      <category>microservices</category>
      <category>gcp</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>We analyze the JHipster Community Survey on YouTube</title>
      <dc:creator>Anthony Viard</dc:creator>
      <pubDate>Thu, 03 Mar 2022 17:12:06 +0000</pubDate>
      <link>https://forem.com/jhipster/we-analyze-the-jhipster-community-survey-on-youtube-5dlk</link>
      <guid>https://forem.com/jhipster/we-analyze-the-jhipster-community-survey-on-youtube-5dlk</guid>
      <description>&lt;p&gt;Hi there,&lt;br&gt;
You probably did not miss our Community Survey and the results shared by Alina on &lt;a href="https://dev.to/jhipster/jhipster-community-survey-results-1gp9"&gt;this blog post&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;During my weekly "Compose with Anthony" Live stream session with the &lt;a href="https://discover.entando.com/lets-build-a-modular-world?utm_medium=email&amp;amp;_hsmi=204175740&amp;amp;_hsenc=p2ANqtz-8fEoPhANqnIU9Mx-pAOPtrSoLDSMOzlH3xMviVwPYvBIaGVGVy5gVxSfNxkANZgTWU9ZDCU7ezGX3tS50HlI9yf_9_UQ&amp;amp;utm_content=204175740&amp;amp;utm_source=hs_email" rel="noopener noreferrer"&gt;ModSquad&lt;/a&gt; on YouTube, I tried to understand and analyze the results of the survey and how we can imagine the future of the project.&lt;/p&gt;

&lt;p&gt;Thanks to all participants! Please find the replay below and share your thoughts with us ;)&lt;/p&gt;

&lt;p&gt;&lt;iframe width="710" height="399" src="https://www.youtube.com/embed/H3nmGgiPwNc"&gt;
&lt;/iframe&gt;
&lt;/p&gt;

</description>
      <category>jhipste</category>
      <category>opensource</category>
      <category>javascript</category>
      <category>java</category>
    </item>
    <item>
      <title>JHipster Community Survey Results</title>
      <dc:creator>Аlina Yurenko</dc:creator>
      <pubDate>Thu, 10 Feb 2022 15:04:27 +0000</pubDate>
      <link>https://forem.com/jhipster/jhipster-community-survey-results-1gp9</link>
      <guid>https://forem.com/jhipster/jhipster-community-survey-results-1gp9</guid>
      <description>&lt;p&gt;Several weeks ago, we &lt;a href="https://twitter.com/jhipster/status/1466081146056450054" rel="noopener noreferrer"&gt;launched&lt;/a&gt; the JHipster Community Survey. The goal was to get feedback from the community about the most useful features and components, things that are missing, and where we should focus our attention to make the project even better for everyone. &lt;/p&gt;

&lt;p&gt;We got over 500 responses; thank you all for taking the time to contribute and share the survey with your community! Now let’s take a look at the results!&lt;/p&gt;

&lt;h2&gt;
  
  
  How do you use JHipster?
&lt;/h2&gt;

&lt;p&gt;The first section of questions was dedicated to understanding the most common scenarios in which JHipster is used, and how exactly, so we can focus our efforts accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which client-side frameworks do you use with JHipster?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The most commonly used framework in the JHipster community is Angular, with 68.3% of respondents making it their #1 choice. React is in second place with 35.3% votes, and the rest of the votes are distributed between Vue.js, Svelte, and using none:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzshsgnb54kdn71elk1n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frzshsgnb54kdn71elk1n.png" alt="Which client-side frameworks do you use with JHipster?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What kind of applications do you create with JHipster?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It was very interesting to understand what kind of applications developers are building with JHipster. SPA/Monolith is a clear winner (78.3%), with Microservices being a close second (60.8%). Gateways and Server-only projects were equally popular:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yc37cvl4s8y636t18e5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2yc37cvl4s8y636t18e5.png" alt="What kind of applications do you create with JHipster?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Outside Java/Spring Boot, do you use other languages/frameworks supported by JHipster?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Many developers use JHipster for Java and Spring Boot projects, but do they use it for other platforms and frameworks? Yes; Node.js, Kotlin, and Flutter are quite popular, as well as such frameworks as Quarkus, Ionic, React Native, and Micronaut.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8knap4ilrmnjgnj6991j.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8knap4ilrmnjgnj6991j.png" alt="Outside Java/Spring Boot, do you use other languages/frameworks supported by JHipster?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What is one thing you love about JHipster?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This was an open question, where everyone could share what they like about JHipster in any form. Some of the most popular answers were the following:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Easy bootstrapping of web applications&lt;/li&gt;
&lt;li&gt;Use of best practices and standards&lt;/li&gt;
&lt;li&gt;Eliminating routine work and getting to production fast&lt;/li&gt;
&lt;li&gt;Enabling unit and integration tests&lt;/li&gt;
&lt;li&gt;Using latest technologies and open source stack&lt;/li&gt;
&lt;li&gt;Flexibility and customizations out of the box&lt;/li&gt;
&lt;li&gt;Continuous support and community&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;…and many other things.&lt;/p&gt;

&lt;p&gt;Thanks everyone for this great feedback!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What can be improved?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We also asked to identify one thing people hate about JHipster, so we can improve it. Many users said there is no such thing! :) Others pointed out that improving some of the documentation, the upgrade process, entities update, and adding more tutorials, in particular for various cloud platforms, would be great.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How would you rate the quality of generated code?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;78.3% of users rated the quality of the code, generated by JHipster, as 8 out of 10 or higher!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwdtrdwr83ldh2bgawiky.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwdtrdwr83ldh2bgawiky.png" alt="How would you rate the quality of generated code?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  JHipster's Roadmap
&lt;/h2&gt;

&lt;p&gt;Some of the most interesting and at the same time challenging questions to answer for each project are about the roadmap. Which features should we prioritize? What is missing? Are our current plans and assumptions right, or do our users feel differently? To get night into that, we asked our users several questions about the roadmap.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What features would you like to see in JHipster?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All of the options that we put up for voting - micro frontends, GraphQL support and GraalVM Native Image support, are highly anticipated by the community. Some of the features suggested by the community, were support for TypeScript, DynamoDB, Flyway, Redis and Vert.x, extended Neo4J support, and Python support, along with introducing app templates and entity-based application dashboards, and others.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbbzqtsdla5zcdw7a5sf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvbbzqtsdla5zcdw7a5sf.png" alt="What features would you like to see in JHipster?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If you could improve the getting started experience for JHipster, what would you change?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A few users suggested having a minimalistic version of Jhipster, which is already addressed with the recent announcement of &lt;a href="https://github.com/jhipster/jhipster-lite" rel="noopener noreferrer"&gt;JHipster Lite&lt;/a&gt; — go and give it a try, if you haven’t yet!&lt;/p&gt;

&lt;p&gt;Other suggestions were to have more project samples, a simple JDL sample (like “Book” and “Author”), outlining best practices for upgrading, creating sample projects with various integrations, and adding more short explainer videos and step-by-step tutorials for beginners.&lt;/p&gt;

&lt;h2&gt;
  
  
  JHipster User Profile
&lt;/h2&gt;

&lt;p&gt;We also asked a few questions to better understand who our users are, and in what context they are using JHipster.&lt;/p&gt;

&lt;p&gt;First, a few short facts:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Most of JHipster users define their programming level experience as “Experienced” (50.7%) or “Expert” (27.7%). Only 5% of them are beginners;&lt;/li&gt;
&lt;li&gt;Most of them also learned about JHipster from a colleague or via GitHub;&lt;/li&gt;
&lt;li&gt;For the majority of users (65%), JHipster is an integral part of their workflow;&lt;/li&gt;
&lt;li&gt;42% of respondents' companies approve using JHipster.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And a few more details about developers using JHipster.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Which programming languages do you regularly use?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Another question we asked was about programming languages. Almost all our respondents use Java (96.1%), which is not surprising since a lot of JHipster’s backend options are Java-based. JavaScript is also popular (61.9%), probably primarily for the front-end part. Python and Kotlin are next, which also corresponds to feature request data we've seen before. 9% of the users also chose C# and Golang:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pe5dy80yaqv8lnyuest.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3pe5dy80yaqv8lnyuest.png" alt="Which programming languages do you regularly use?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you ever made contributions to the project?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;JHipster is lucky to have a very active and engaged community. Every third community member helped the project by providing feedback via &lt;a href="https://github.com/jhipster" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;, and every fourth created content about it, such as blog posts, event talks, etc. Other contributions, such as sending code PRs, contributing to docs, and helping other developers, are extremely valuable and improve the project for everyone.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsp0oethhqygt2c8z29pp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsp0oethhqygt2c8z29pp.png" alt="Have you ever made contributions to the project?"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Thanks again to everyone for taking the time to provide this feedback. We will go through the suggestions with the core team, and will use the results to adjust the project plans. &lt;/p&gt;

&lt;p&gt;Some of the most requested features are already on our roadmap, with ongoing work happening and bounties on them — feel to contribute or share your thoughts in corresponding GitHub tickets!&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jhipster/generator-jhipster/issues/10189" rel="noopener noreferrer"&gt;[#10189] Implement a micro frontends prototype with JHipster&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jhipster/generator-jhipster-nodejs/issues/214" rel="noopener noreferrer"&gt;[#214] GraphQL support&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/jhipster/generator-jhipster/issues/13733" rel="noopener noreferrer"&gt;[#13733] Add native image support with GraalVM and Spring Native&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you didn’t have a chance to participate in the survey, or have other suggestions, your feedback is always welcome on &lt;a href="https://github.com/jhipster/generator-jhipster" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; and &lt;a href="https://twitter.com/jhipster" rel="noopener noreferrer"&gt;Twitter&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>community</category>
      <category>jhipster</category>
    </item>
    <item>
      <title>Add a CLI to your JHipster blueprint</title>
      <dc:creator>Anthony Viard</dc:creator>
      <pubDate>Fri, 13 Aug 2021 10:53:37 +0000</pubDate>
      <link>https://forem.com/jhipster/add-a-cli-to-your-jhipster-blueprint-1igj</link>
      <guid>https://forem.com/jhipster/add-a-cli-to-your-jhipster-blueprint-1igj</guid>
      <description>&lt;p&gt;Hello my fellow hipsters. Who has never dreamt to have his own CLI for his blueprint? This is what I want to show you today.&lt;/p&gt;

&lt;h2&gt;
  
  
  JHipster versions and your blueprint
&lt;/h2&gt;

&lt;p&gt;If like me, you like to play with many JHipster versions, switching between your fork and official JHipster releases you probably struggle with version management. How to be sure my JHipster command will launch the version I need? And how I’ll be sure my users will not have bad experiences when using my blueprint?&lt;/p&gt;

&lt;h2&gt;
  
  
  How a CLI can improve your blueprint experience
&lt;/h2&gt;

&lt;p&gt;When you generate a project with the JHipster command line with your blueprint you type that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;jhipster &lt;span class="nt"&gt;--blueprint&lt;/span&gt; myBlueprint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means you want to launch JHipster with a blueprint called “myBlueprint” using a global JHipster installation, or the local one. In both cases you can’t be sure the good version will be used and your blueprint as compatibility with a specific version.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create your blueprint and its CLI
&lt;/h2&gt;

&lt;p&gt;First, you need to start by creating your own blueprint, you can use the blueprint generator here : &lt;a href="https://github.com/jhipster/generator-jhipster-blueprint" rel="noopener noreferrer"&gt;https://github.com/jhipster/generator-jhipster-blueprint&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then we need to add a cli folder that will contain the node.js code. I split it into two files, the app.js (which will launch JHipster for us) and the utils.js (which will contain some functions).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;app.js
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="c"&gt;#!/usr/bin/env node&lt;/span&gt;

    &lt;span class="k"&gt;**&lt;/span&gt;const &lt;span class="k"&gt;**&lt;/span&gt;fork &lt;span class="o"&gt;=&lt;/span&gt; require&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'child_process'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;.fork&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;**&lt;/span&gt;const &lt;span class="k"&gt;**&lt;/span&gt;utils &lt;span class="o"&gt;=&lt;/span&gt; require&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'./utils'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;**&lt;/span&gt;const &lt;span class="k"&gt;**&lt;/span&gt;jhipsterPath &lt;span class="o"&gt;=&lt;/span&gt; utils.generateJHipsterPath&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'../node_modules/generator-jhipster/cli/jhipster'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;**&lt;/span&gt;const &lt;span class="k"&gt;**&lt;/span&gt;args &lt;span class="o"&gt;=&lt;/span&gt; utils.initArguments&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'--blueprint'&lt;/span&gt;, &lt;span class="s1"&gt;'myBlueprint'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    utils.printBanner&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    fork&lt;span class="o"&gt;(&lt;/span&gt;jhipsterPath, args&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;utils.js
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;    &lt;span class="c"&gt;#!/usr/bin/env node&lt;/span&gt;

    &lt;span class="k"&gt;**&lt;/span&gt;const &lt;span class="k"&gt;**&lt;/span&gt;p &lt;span class="o"&gt;=&lt;/span&gt; require&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'path'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;**&lt;/span&gt;const &lt;span class="k"&gt;**&lt;/span&gt;chalk &lt;span class="o"&gt;=&lt;/span&gt; require&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'chalk'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;**function&lt;/span&gt; &lt;span class="k"&gt;**&lt;/span&gt;generateJHipsterPath&lt;span class="o"&gt;(&lt;/span&gt;relativePath&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;**&lt;/span&gt;const &lt;span class="k"&gt;**&lt;/span&gt;absolutePath &lt;span class="o"&gt;=&lt;/span&gt; p.join&lt;span class="o"&gt;(&lt;/span&gt;__dirname, relativePath&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;**return&lt;/span&gt; &lt;span class="k"&gt;**&lt;/span&gt;p.normalize&lt;span class="o"&gt;(&lt;/span&gt;absolutePath&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;**function&lt;/span&gt; &lt;span class="k"&gt;**&lt;/span&gt;initArguments&lt;span class="o"&gt;(&lt;/span&gt;...args&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;**&lt;/span&gt;const &lt;span class="k"&gt;**&lt;/span&gt;inputArgs &lt;span class="o"&gt;=&lt;/span&gt; _removeNodeArgsInProcessArgv&lt;span class="o"&gt;()&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;**return&lt;/span&gt; &lt;span class="k"&gt;**&lt;/span&gt;args.concat&lt;span class="o"&gt;(&lt;/span&gt;inputArgs&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;**function&lt;/span&gt; &lt;span class="k"&gt;**&lt;/span&gt;_removeNodeArgsInProcessArgv&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;**return&lt;/span&gt; &lt;span class="k"&gt;**&lt;/span&gt;process.argv ? process.argv.slice&lt;span class="o"&gt;(&lt;/span&gt;2&lt;span class="o"&gt;)&lt;/span&gt; : &lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;**function&lt;/span&gt; &lt;span class="k"&gt;**&lt;/span&gt;printBanner&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        /&lt;span class="k"&gt;*&lt;/span&gt; eslint-disable no-console &lt;span class="k"&gt;*&lt;/span&gt;/
        console.log&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'\n'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        console.log&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;chalk&lt;/span&gt;&lt;span class="p"&gt;.yellow(&lt;/span&gt;&lt;span class="s1"&gt;'Welcome to myBlueprint JHipster Generator'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        console.log&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'This CLI will call JHipster with myBlueprint blueprint for you. You could use all JHipster CLI options.\n'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        console.log&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;chalk&lt;/span&gt;&lt;span class="p"&gt;.redBright(&lt;/span&gt;&lt;span class="s1"&gt;'Ready ?... '&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}${&lt;/span&gt;&lt;span class="nv"&gt;chalk&lt;/span&gt;&lt;span class="p"&gt;.blueBright(&lt;/span&gt;&lt;span class="s2"&gt;"Let's go!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        console.log&lt;span class="o"&gt;(&lt;/span&gt;
            chalk.green&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;' _______________________________________________________________________________________________________________\n'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        /&lt;span class="k"&gt;*&lt;/span&gt; eslint-enable no-console &lt;span class="k"&gt;*&lt;/span&gt;/
    &lt;span class="o"&gt;}&lt;/span&gt;

    module.exports &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        generateJHipsterPath,
        initArguments,
        printBanner
    &lt;span class="o"&gt;}&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This code will generate the right JHipster path (from our node_modules) and launch it with the follow argument (and print a personal header before the JHipster’s one):&lt;br&gt;
&lt;code&gt;-- blueprint myBlueprint&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When this is done, we just need to register the node.js code as “bin” in our package.json :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"bin"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"myBlueprint"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./cli/app.js"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And add the cli folder in the “files” entry (to add it when doing a npm publish):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="nl"&gt;"files"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"generators"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="s2"&gt;"cli"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Link your blueprint
&lt;/h2&gt;

&lt;p&gt;As long as your blueprint is not published in the npm registry you need to link it.&lt;br&gt;
&lt;code&gt;npm link&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;for yours final users a simple install is necessary&lt;br&gt;
&lt;code&gt;npm i -g generator-jhipster-myBlueprint&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Use your CLI
&lt;/h2&gt;

&lt;p&gt;In a terminal, create a new folder and type your blueprint command&lt;br&gt;
&lt;code&gt;myBlueprint&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73d1t2yvhsjbb7ec5mpw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F73d1t2yvhsjbb7ec5mpw.png" alt="We can see that the JHipster version used is 5.4.0, same as our package.json" width="800" height="522"&gt;&lt;/a&gt;&lt;em&gt;We can see that the JHipster version used is 5.4.0, same as our package.json&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;You can, of course, add options like we used to do with the original JHipster CLI:&lt;br&gt;
&lt;code&gt;myBlueprint --skip-client --skip-git --skip-checks&lt;/code&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope this article will help you, you can find all the source code in my github repository here:&lt;br&gt;
&lt;a href="https://github.com/avdev4j/myBlueprint" rel="noopener noreferrer"&gt;https://github.com/avdev4j/myBlueprint&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to use it and contribute to jhipster.tech and &lt;a href="https://github.com/jhipster" rel="noopener noreferrer"&gt;https://github.com/jhipster&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jhipster</category>
      <category>cli</category>
      <category>generator</category>
    </item>
  </channel>
</rss>
