<?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>DEV Community: RUARO Thibault</title>
    <description>The latest articles on DEV Community by RUARO Thibault (@truar).</description>
    <link>https://dev.to/truar</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%2Fuser%2Fprofile_image%2F520289%2Fa5354efe-0773-40f7-8752-d2d15facf6bb.png</url>
      <title>DEV Community: RUARO Thibault</title>
      <link>https://dev.to/truar</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/truar"/>
    <language>en</language>
    <item>
      <title>Developer tools for Cloud Run</title>
      <dc:creator>RUARO Thibault</dc:creator>
      <pubDate>Tue, 04 May 2021 18:19:36 +0000</pubDate>
      <link>https://dev.to/zenika/developer-tools-for-cloud-run-4ill</link>
      <guid>https://dev.to/zenika/developer-tools-for-cloud-run-4ill</guid>
      <description>&lt;h1&gt;
  
  
  Developer tools to enhance Cloud Run development
&lt;/h1&gt;

&lt;p&gt;Cloud Run is the next generation of serverless solution offered by Google Cloud. It allows you to execute an &lt;a href="https://opencontainers.org/" rel="noopener noreferrer"&gt;OCI image&lt;/a&gt; (Docker image for instance) of your choice on Google Cloud servers.&lt;/p&gt;

&lt;p&gt;For those who are more familiar with App Engine Standard environment, the main difference is that Cloud Run gives you the choice of the runtime environment for your application. Usually, App Engine provides a set of runtimes (Java, Node, Python...) that will execute your application. It frees the developer from the burden of packaging the application. However, it restricts the possibilities to use the latest version of a runtime environment (like Java 16 for instance).&lt;/p&gt;

&lt;p&gt;Running OCI image gives you a lot of flexibility. But at what cost? You now need to provide such an image to package your application. A common way to do so, can be to create a Dockerfile that contains the instructions to containerize your application.&lt;/p&gt;

&lt;p&gt;As a developer, we, sometimes, like having access to some metrics, data or resources computation used by our application. In this article, we will see how you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;See the logs of your Cloud Run application&lt;/li&gt;
&lt;li&gt;Debug on production your application with no impact on performance&lt;/li&gt;
&lt;li&gt;Profile your applications to monitor CPU usages and other metrics&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Cloud Logging - Check your application logs
&lt;/h2&gt;

&lt;p&gt;Logs have always been an important aspect for an application whose lifetime is intended to be for couples of years. You need to keep in mind that one day, something will go wrong on your application. And when it does, you better be ready to investigate.&lt;/p&gt;

&lt;p&gt;The first reflex everybody has when the application is down, either partially or totally, is to check the logs trying to find the root cause of the issue. That makes total sense, since it is easier to fix something when we know what to look for.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://12factor.net/" rel="noopener noreferrer"&gt;12 factor apps&lt;/a&gt; (a set of best practices when developing containerized application) states "[An application] should not attempt to write to or manage log files. Instead, each running process writes its event stream, unbuffered, to &lt;code&gt;stdout&lt;/code&gt;". This means you should only focus on logging behaviours and data to the console. Leave log capture &amp;amp; aggregation to the Ops.&lt;/p&gt;

&lt;p&gt;If you respect this principle, you can easily use &lt;a href="https://cloud.google.com/logging/docs" rel="noopener noreferrer"&gt;Cloud Logging&lt;/a&gt; to monitor your Cloud Run services logs, aggregate them and define alerts, or custom metrics about your application.&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%2Fdnezv3yxe8hqkrsrna3r.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%2Fdnezv3yxe8hqkrsrna3r.png" alt="Cloud Run specific revisions logs into Cloud Logging"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In this screenshot, we see the output for the revisions &lt;code&gt;cloudrun-developer-00005-xuh&lt;/code&gt; (a revision is an immutable running instance of your application). But we could filter for the Cloud Run service &lt;code&gt;cloudrun-developer&lt;/code&gt; for instance, or all Cloud Run resources...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You do not have to access the server anymore, making a &lt;code&gt;tail -1000f /my/log/file.log&lt;/code&gt; or any command you were used to. You have a great Web Interface, collecting and showing the logs of all your services. Therefore, it is easy to apply filters to see the output of a specific Cloud Run service, or filter by &lt;code&gt;Correlation-ID&lt;/code&gt; to track a transaction across all your services.&lt;/p&gt;

&lt;p&gt;If you wish to access the global documentation about Cloud Logging and Cloud Run, &lt;a href="https://cloud.google.com/run/docs/logging" rel="noopener noreferrer"&gt;here is the link&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud debugger
&lt;/h2&gt;

&lt;p&gt;I know, I know... My code is perfect, and there is no such thing as a "Bug"... So why bother using a Debugger in the first place...&lt;/p&gt;

&lt;p&gt;As great and magnificent you and your code can be, I am sorry to break it to you, but you (of course, not me 😝) will face undesired behaviors in your application. When it happens, you will be happy to be easily able to debug your application.&lt;/p&gt;

&lt;p&gt;What if I told you there is a way. You can debug your application while it is running in production, with no impact on your user experience ? Sounds too good to be true, keep reading.&lt;/p&gt;

&lt;p&gt;If you were familiar with App Engine, you might have already used the &lt;a href="https://cloud.google.com/debugger" rel="noopener noreferrer"&gt;Cloud Debugger&lt;/a&gt; service of Google Cloud. Any AppEngine application can be linked to Cloud Debugger in order to debug your application while it is running. If you tried Cloud Run recently, maybe you realized this feature was not natively provided and you felt betrayed... But it is still possible to enable it.&lt;/p&gt;

&lt;p&gt;Cloud Debugger lets you inspect the state of your application by providing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The possibility to take &lt;strong&gt;snapshots&lt;/strong&gt; of your code. If you run a Java application, you will see the stacktrace and the variables/parameters value.&lt;/li&gt;
&lt;li&gt;The possibility to create &lt;strong&gt;logpoints&lt;/strong&gt; in your code. This logpoint will send a log in Cloud Logging when the user hits the logpoint. Following a specification, you can log existing variables or statements to help you monitor your application directly in production.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Installing the Agent
&lt;/h3&gt;

&lt;p&gt;Deploying an application on app engine includes in the container, an agent capable of interacting with Cloud Debugger. To enable the same service for Cloud Run, it is as easy as it sounds: you need to install the same agent in the OCI image you provide to Cloud Run.&lt;/p&gt;

&lt;p&gt;Following &lt;a href="https://cloud.google.com/debugger/docs/setup/java" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;, here is how you can install the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a directory for the Debugger. Add and unzip the agent in the directory.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; /opt/cdbg &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    wget &lt;span class="nt"&gt;-qO-&lt;/span&gt; https://storage.googleapis.com/cloud-debugger/compute-java/debian-wheezy/cdbg_java_agent_gce.tar.gz | &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nb"&gt;tar &lt;/span&gt;xvz &lt;span class="nt"&gt;-C&lt;/span&gt; /opt/cdbg
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once installed, just add some variables to your &lt;code&gt;java -jar&lt;/code&gt; command line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; java \&lt;/span&gt;
   -Dcom.google.cdbg.breakpoints.enable_canary=true \
   -jar PATH_TO_JAR_FILE
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when running your Cloud Run service, you need to provide environment variables to complete the Debugger agent setup.&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;JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-agentpath&lt;/span&gt;:/opt/cdbg/cdbg_java_agent.so
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Using Source Repositories
&lt;/h3&gt;

&lt;p&gt;To be able to create &lt;strong&gt;snapshots and logpoints&lt;/strong&gt;, Cloud Debugger needs to have access to your source code. Cloud Debugger then displays your source code in the UI, and you just have to navigate to your code to create a snapshot or a logpoint.&lt;/p&gt;

&lt;p&gt;A practice for such use cases would be to sync your github repository with Source repositories. &lt;a href="https://cloud.google.com/source-repositories/docs/mirroring-a-github-repository?hl=en" rel="noopener noreferrer"&gt;Follow this link to do so&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example of logpoints and snapshots
&lt;/h3&gt;

&lt;p&gt;You can manage logpoints and snapshots through the Debugger service in Google Cloud. Once the Cloud Run service is deployed with the Debugger Agent, and Source Repositories sync with your code, you can simply use the Debugger service. If you want a step by step procedure, &lt;a href="https://cloud.google.com/debugger/docs/quickstart" rel="noopener noreferrer"&gt;you can follow this link&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Logpoints are points in the program that will send a log message into your service logs. These logs are collected by Cloud Logging as well, fitting nicely into your existing application logs. [To know how to create Logpoints, follow this link].(&lt;a href="https://cloud.google.com/debugger/docs/using/logpoints#console" rel="noopener noreferrer"&gt;https://cloud.google.com/debugger/docs/using/logpoints#console&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;In this example, I created 3 logpoints in the same method of my Controller. When the user asks for a new Todo creation, the logpoint will be triggered and will send the message to Cloud Logging.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;logpoint("task={createTodoDTO.task}, dueDate={createTodoDTO.dueDate}")
logpoint("createTodoDTO={createTodoDTO}")
logpoint("{createTodoDTO.toString()}")
&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%2Fwqhk3u0x28eg6wqstk87.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%2Fwqhk3u0x28eg6wqstk87.png" alt="Logpoints example in the Debugger service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You need to follow some Google Cloud logpoints specification in order to know what expressions you can use (For now, I am not able to find the complete documentation...). Here is the result in Cloud Logging.&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%2Fg4gzkkvhnlyg4wk643t4.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%2Fg4gzkkvhnlyg4wk643t4.png" alt="Logpoints output in Cloud Logging service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For Java, I noticed 2 interesting things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Logging object does not seem to perform a deep reflection &lt;code&gt;LOGPOINT: createTodoDTO={ task: "a todo", dueDate: &amp;lt;Object&amp;gt; }&lt;/code&gt;. Here, you see the &lt;code&gt;dueDate&lt;/code&gt; is logged as &lt;code&gt;object&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;You can't make any method call. The logpoint &lt;code&gt;createTodoDTO.toString()&lt;/code&gt; logged an error: &lt;code&gt;LOGPOINT: Method dev.truaro.blog.cloudrundeveloper.CreateTodoDTO.toString blocked (INVOKEDYNAMIC instruction not supported)&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A snapshot works in a similar way, but the output is different. Instead of seeing logs output, you can directly inspect the stacktrace, and the current state of the object in the scope (&lt;code&gt;this&lt;/code&gt;, parameters, variables...). &lt;a href="https://cloud.google.com/debugger/docs/using/snapshots" rel="noopener noreferrer"&gt;To have a step by step snapshot creation, follow this link&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this example, I created a snaphshot before calling the &lt;code&gt;todoRepository&lt;/code&gt;. Here is the result:&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%2F760vs07kb40l6fhp3fft.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%2F760vs07kb40l6fhp3fft.png" alt="Snapshots example in Debugger service"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can see on the right side 2 interesting panels:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Variables: Display the content of &lt;code&gt;this&lt;/code&gt;, your method parameters and the variables you created.&lt;/li&gt;
&lt;li&gt;Call Stack: Display the stacktrace the lead you to your current snapshot.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Please note a snapshot does not stop your execution, it simply collects data, but does not affect the user experience.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Be also careful, the debugger is linked to a revision of your service. If you deploy a new revision, you will have to configure the Debugger to communicate with the new services.&lt;/li&gt;
&lt;li&gt;Besides, your application needs to be running before creating LogPoints and Snapshots, otherwise the debugger agents will not send data to the Debugger service.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Cloud Profiler
&lt;/h2&gt;

&lt;p&gt;Cloud Profiler is a service provided by Google Cloud to help understands the performance bottlenecks of your application. By analysing your application with Cloud Profiler, you can quickly spot what is causing the slowness in your application, and take actions to fix it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Installing the Agent
&lt;/h3&gt;

&lt;p&gt;Just like Cloud Debugger, profiling an application requires the installation of the Profiler Agent in the OCI image deployed on Cloud Run.&lt;/p&gt;

&lt;p&gt;Following &lt;a href="https://cloud.google.com/profiler/docs/profiling-java#gke" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;, here is how you can install the agent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create a directory for the Profiler. Add and unzip the agent in the directory.&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/cprof &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;   wget &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-O-&lt;/span&gt; https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz &lt;span class="se"&gt;\
&lt;/span&gt;   | &lt;span class="nb"&gt;tar &lt;/span&gt;xzv &lt;span class="nt"&gt;-C&lt;/span&gt; /opt/cprof
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, when running your Cloud Run service, you need to provide environment variables to complete the Profiler agent setup. Here is a combination I found useful to monitor a Spring Boot application:&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;JAVA_TOOL_OPTIONS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-agentpath&lt;/span&gt;:/opt/cprof/profiler_java_agent.so&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nt"&gt;-cprof_service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;SERVICE_NAME&amp;gt;,-cprof_enable_heap_sampling&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;,-cprof_cpu_use_per_thread_timers&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;-cprof_service=&amp;lt;SERVICE_NAME&amp;gt;&lt;/code&gt;: provide the name of the service that will be used in Cloud Profiler. Usually, use the same name as the Cloud Run service.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-cprof_enable_heap_sampling=true&lt;/code&gt;: Enable Heap sampling. Only available for Java 11 and higher.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;-cprof_cpu_use_per_thread_timers=true&lt;/code&gt;: Samples the CPU times per thread more precisely. Can add a little overhead, but as you run your application with multiple threads, it can be a useful metrics to have.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Profiling your application
&lt;/h3&gt;

&lt;p&gt;Cloud Profiler works by collecting profiles of your running application. The profile agent communicates with the Cloud Profile backend API and sends profiling data every minute on average.&lt;/p&gt;

&lt;p&gt;The Profiler can manage different information:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;CPU Time&lt;/strong&gt;: The most basic metrics every developer should be familiar with. It is the time spent by the CPU on your method execution. It doesn't calculate the time spent on other resources. Of course, if your application spent most of its time waiting for a resource, you might find that the CPU time does not reflect well your application execution process. That is why, you need other metrics to know how long a method took.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wall Time&lt;/strong&gt;: The total amount of time spent between the method call until the method returns. The Wall time will always be higher than the CPU times, as it will also track the time you spent waiting for a resource.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Heap memory usage&lt;/strong&gt;:

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Heap&lt;/strong&gt;: The amount of memory allocated in the program  at a single point of time. It does not store the allocation during the program execution. To do so, there is the Allocated Heap metric.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Allocated Heap&lt;/strong&gt;: The allocated heap during the interval. Takes into account memory that has been allocated and memory that has been freed.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Unfortunately, not all metrics are available for all languages. For the full list of supported metrics, &lt;a href="https://cloud.google.com/profiler/docs/concepts-profiling" rel="noopener noreferrer"&gt;check the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Data representation in Cloud Profiler UI
&lt;/h3&gt;

&lt;p&gt;Before showing you a screenshot of the UI for my sample application, I think it is necessary to tell you how the data is represented. The final representation is a &lt;strong&gt;Flame Graph&lt;/strong&gt;, which is an optimized and space efficient representation for a very large amount of information.&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%2Fkvvpr65vh9rfgzs8sj7k.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%2Fkvvpr65vh9rfgzs8sj7k.png" alt="Flame graph concept"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You can find more information in &lt;a href="https://cloud.google.com/profiler/docs/concepts-flame" rel="noopener noreferrer"&gt;this documentation&lt;/a&gt;. It is unfamiliar at first, but once you are used to it, it is easier to read.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example
&lt;/h3&gt;

&lt;p&gt;In my example, I profile a Spring Boot Java 11 application. Therefore, I can only monitor:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CPU Time&lt;/li&gt;
&lt;li&gt;Wall Time&lt;/li&gt;
&lt;li&gt;And Heap usage&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I created a Controller designed specifically for time consuming resources. Here is the code of the Controller:&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;@RestController&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;TimeConsumerController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

   &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/consume"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;consume&lt;/span&gt;&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1000000&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
           &lt;span class="nc"&gt;Random&lt;/span&gt; &lt;span class="n"&gt;random&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;Random&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
           &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i1&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nextInt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
           &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;nextInt&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
           &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;i1&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i2&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
           &lt;span class="nc"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"i3 = "&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;i3&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;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;A simple loop, that generates random numbers, sums them and displays the result in the standard output.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After a couple of API calls, I got the following CPU time graph:&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%2Fmijahpqyo5wi3ph2ifvn.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%2Fmijahpqyo5wi3ph2ifvn.png" alt="CPU Time cloud profiler graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a reference, here is the Wall Time graph:&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%2F37zyek5cxp2emq6gv0g4.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%2F37zyek5cxp2emq6gv0g4.png" alt="Wall time cloud profiler graph"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In terms of seconds spent on the method, the Wall Time is 8.95s and the CPU Time is 5.13s.&lt;/p&gt;

&lt;p&gt;With an application dealing with multiple users, you could have more useful metrics and time spent in specific methods, helping identify bottlenecks.&lt;/p&gt;

&lt;p&gt;IMHO, the Cloud Profiler with Spring Boot is not very practical to profile specific business code and not framework code. Indeed, I spared you from the global Profiler graph, but you mostly see framework related methods, which can make it harder to find bottlenecks in your business code. Obviously, your application needs to be used in order to overcome the cost of the framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  Cloud Trace
&lt;/h2&gt;

&lt;p&gt;The last service you need to know is &lt;a href="https://cloud.google.com/trace/docs/setup" rel="noopener noreferrer"&gt;Cloud Trace&lt;/a&gt;. Its purpose is to &lt;strong&gt;monitor the latency&lt;/strong&gt; of your requests when users call your application. In a nutshell, latency is the time delay the user waits before receiving the response from your application.&lt;/p&gt;

&lt;p&gt;This service is automatically configured to monitor your Cloud Run services, which makes it very easy to use. Here is a screenshot of the latency of several requests I have made for the test.&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%2Fa82evro8jn5t4z0op9b5.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%2Fa82evro8jn5t4z0op9b5.png" alt="Cloud Trace example"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Reading this graph is quite straightforward:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The blue dots represents the latency of a request&lt;/li&gt;
&lt;li&gt;You can see the request with the longest latency took 15 seconds. This request started the Cloud Run service, which is why it took so long&lt;/li&gt;
&lt;li&gt;You can see some requests taking around 10 seconds. These requests were hitting the &lt;code&gt;/consume&lt;/code&gt; API (remember the API I created earlier ?)&lt;/li&gt;
&lt;li&gt;For most of the requests, it took around 50ms to handle a GET on &lt;code&gt;/todos&lt;/code&gt;, which is great. It means the user almost never waits to get a request that went through your Cloud Run and Datastore.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Of course, by default, not all requests are logged. Doing so would increase too much the volume of data, and your usage bill at the same time.&lt;/p&gt;

&lt;p&gt;Cloud Trace offers other features that are not detailed here, like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Building insightful graph to monitor accurately your application&lt;/li&gt;
&lt;li&gt;Monitoring requests made by your application to another application to calculate the global latency&lt;/li&gt;
&lt;li&gt;Exporting the data to aggregate your trace the way you need&lt;/li&gt;
&lt;li&gt;Providing data about the user who generated the request, like its country, its region, its city...&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;The tool I used to generate some traffic is &lt;a href="https://linux.die.net/man/1/siege" rel="noopener noreferrer"&gt;siege&lt;/a&gt;, a Linux tool to generate concurrent traffic on an endpoint. Here is the command line I used: &lt;code&gt;siege -c 3 https://...&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;I hope you have a better view of some developer tools that could make your life with Cloud Run easier. To go further, there are also tools about latency and error reporting. Please, find below the list of the resources we went through together:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.cncf.io" rel="noopener noreferrer"&gt;CNCF website&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://12factor.net/" rel="noopener noreferrer"&gt;The 12 apps factor&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=qlF378oDqW8&amp;amp;list=PLdVDu8iO6zrMurVwGrFR23uw5OtGh4vFx&amp;amp;index=5" rel="noopener noreferrer"&gt;A great video explaining The 12 apps By Julien Landuré&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/blog/products/serverless/build-and-deploy-an-app-to-cloud-run-with-a-single-command" rel="noopener noreferrer"&gt;Deploy your source code with Cloud Run&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/logging/docs/concepts" rel="noopener noreferrer"&gt;Cloud Logging concepts&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/debugger/docs/quickstart?hl=en" rel="noopener noreferrer"&gt;Debugger Quickstart&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/profiler/docs/about-profiler" rel="noopener noreferrer"&gt;About Profiler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/trace/docs/setup" rel="noopener noreferrer"&gt;About Cloud Trace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googlecloud</category>
      <category>cloudrun</category>
      <category>monitoring</category>
    </item>
    <item>
      <title>Improving Cloud Build pipeline with dependencies cache on Cloud Storage</title>
      <dc:creator>RUARO Thibault</dc:creator>
      <pubDate>Sun, 28 Mar 2021 17:00:53 +0000</pubDate>
      <link>https://dev.to/zenika/improving-cloud-build-pipeline-with-dependencies-cache-on-cloud-storage-4cpo</link>
      <guid>https://dev.to/zenika/improving-cloud-build-pipeline-with-dependencies-cache-on-cloud-storage-4cpo</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Building a Continuous Deployment pipeline is amazing as you collect feedback on a daily basis. At first, the pipeline is quite quick and deploying applications is easy. But as your application is growing, you might have faced more and more latency... This article is here to show you how you can reduce the build time by adding dependencies cache on Cloud Storage.&lt;/p&gt;

&lt;p&gt;For this focus, I'll simply use this &lt;code&gt;cloudbuild.yaml&lt;/code&gt; file, watch the current deployment time, and explore how we improved it using Google Storage cache.&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;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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build-project'&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;adoptopenjdk/openjdk11:jdk-11.0.8_10-slim&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;./mvnw package&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dockerize-project'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;args&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;build'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;push-to-cloud-registry'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
   &lt;span class="na"&gt;args&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;push'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-cloud-run'&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;gcr.io/cloud-builders/gcloud&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;apt-get update&lt;/span&gt;
       &lt;span class="s"&gt;apt-get install -qq -y gettext&lt;/span&gt;
       &lt;span class="s"&gt;export PROJECT_ID=$PROJECT_ID&lt;/span&gt;
       &lt;span class="s"&gt;export IMAGE_VERSION=$SHORT_SHA&lt;/span&gt;
       &lt;span class="s"&gt;export SCALING_INSTANCE_COUNT=${_SCALING_INSTANCE_COUNT}&lt;/span&gt;
       &lt;span class="s"&gt;envsubst &amp;lt; gcp-cloudrun-back.yaml &amp;gt; gcp-cloudrun-back_with_env.yaml&lt;/span&gt;
       &lt;span class="s"&gt;gcloud beta run services replace gcp-cloudrun-back_with_env.yaml \&lt;/span&gt;
         &lt;span class="s"&gt;--platform=managed --region=europe-west1&lt;/span&gt;
       &lt;span class="s"&gt;gcloud run services add-iam-policy-binding gcp-cloudrun-back \&lt;/span&gt;
         &lt;span class="s"&gt;--platform=managed --region=europe-west1 \&lt;/span&gt;
         &lt;span class="s"&gt;--member="allUsers" --role="roles/run.invoker"&lt;/span&gt;

&lt;span class="na"&gt;images&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;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the associated Dockerfile&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Use AdoptOpenJDK for base image.&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; adoptopenjdk/openjdk11:jre-11.0.8_10-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Copy the jar to the production image from the previous step&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; target/*.jar /app.jar&lt;/span&gt;

&lt;span class="c"&gt;# Run the web service on container startup.&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["java", "-jar", "/app.jar"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;In this pipeline, we build our application using the maven wrapper from our project. Besides, we use a simple Dockerfile, not a multi-staged one.&lt;/li&gt;
&lt;li&gt;Also note you need to grant Cloud Build service account the &lt;code&gt;Cloud Run&lt;/code&gt; and &lt;code&gt;Service Accounts&lt;/code&gt; roles.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Reducing the build time with cache
&lt;/h1&gt;

&lt;p&gt;Currently, when deploying this pipeline (&lt;a href="https://github.com/truar/blog-resources/tree/master/gcpapplication"&gt;based on this Github project&lt;/a&gt;), it takes my build 1 minute and 45 seconds on average to complete. Let's first understand this build time, to see what can be improved. &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--GnZcCUMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6s3nm3ien37v46ss3w6e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--GnZcCUMn--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6s3nm3ien37v46ss3w6e.png" alt="Cloud Build deployment time"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;The longest step is the one where the dependencies need to be installed &lt;code&gt;build-project&lt;/code&gt;, where we see all the dependencies are downloaded. If you think that those dependencies will not change so often, it clearly needs some optimization to avoid unnecessary resources to be used.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Add maven cache
&lt;/h2&gt;

&lt;p&gt;Reading Google &lt;a href="https://cloud.google.com/cloud-build/docs/speeding-up-builds"&gt;Cloud build optimizations documentation&lt;/a&gt;, we can manually add Google Storage cache to reduce build time.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note that some Cloud Build competitors offer easily managed caching solutions... such a shame Cloud Build is not and we have to do everything manually.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Create a storage bucket
&lt;/h3&gt;

&lt;p&gt;We will store our dependencies cache into a specific bucket. Let's create one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gsutil mb gs://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-cache-dependencies&lt;/span&gt;

Creating gs://&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="nt"&gt;-cache-dependencies&lt;/span&gt;/...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;${PROJECT_ID}&lt;/code&gt; with your actual project Id. Cloud storage needs a unique name among all the buckets in the system. By prefixing your bucket with your project Id, there is a low chance to collide with another bucket name.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Add steps to fetch and save the cache
&lt;/h3&gt;

&lt;p&gt;To fetch and save the cache downloaded by Maven:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We will use the &lt;strong&gt;volumes&lt;/strong&gt; features of Cloud Build. By default, Cloud Build persists the folder &lt;code&gt;/workspace&lt;/code&gt; across your build steps. We can specify other volumes to be persisted.&lt;/li&gt;
&lt;li&gt;Cloud Build does not offer yet an easy way to cache dependencies for any usage across different builds.&lt;/li&gt;
&lt;li&gt;It does not offer also the possibility for a step to fail silently without stopping the pipeline.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Knowing this, here is what the final &lt;code&gt;cloudbuild.yaml&lt;/code&gt; with dependencies cache looks like:&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;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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;download-cached-maven-dependencies'&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;gcr.io/cloud-builders/gsutil&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;volumes&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maven-repository'&lt;/span&gt;
       &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/root/.m2'&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;gsutil cp gs://${PROJECT_ID}-cache-dependencies/cache/maven-dependencies.tgz maven-dependencies.tgz || exit 0&lt;/span&gt;
       &lt;span class="s"&gt;tar -zxf maven-dependencies.tgz --directory / || exit 0&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build-project'&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;adoptopenjdk/openjdk11:jdk-11.0.8_10&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mavencachecloudbuild/gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;volumes&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maven-repository'&lt;/span&gt;
       &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/root/.m2'&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;./mvnw package&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dockerize-project'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mavencachecloudbuild/gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;args&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;build'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;push-to-cloud-registry'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
   &lt;span class="na"&gt;args&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;push'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-cloud-run'&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;gcr.io/cloud-builders/gcloud&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mavencachecloudbuild/gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;apt-get update&lt;/span&gt;
       &lt;span class="s"&gt;apt-get install -qq -y gettext&lt;/span&gt;
       &lt;span class="s"&gt;export PROJECT_ID=$PROJECT_ID&lt;/span&gt;
       &lt;span class="s"&gt;export IMAGE_VERSION=$SHORT_SHA&lt;/span&gt;
       &lt;span class="s"&gt;export SCALING_INSTANCE_COUNT=${_SCALING_INSTANCE_COUNT}&lt;/span&gt;
       &lt;span class="s"&gt;envsubst &amp;lt; gcp-cloudrun-back.yaml &amp;gt; gcp-cloudrun-back_with_env.yaml&lt;/span&gt;
       &lt;span class="s"&gt;gcloud beta run services replace gcp-cloudrun-back_with_env.yaml \&lt;/span&gt;
         &lt;span class="s"&gt;--platform=managed --region=europe-west1&lt;/span&gt;
       &lt;span class="s"&gt;gcloud run services add-iam-policy-binding gcp-cloudrun-back \&lt;/span&gt;
         &lt;span class="s"&gt;--platform=managed --region=europe-west1 \&lt;/span&gt;
         &lt;span class="s"&gt;--member="allUsers" --role="roles/run.invoker"&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;upload-cached-maven-dependencies'&lt;/span&gt;
   &lt;span class="na"&gt;waitFor&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;build-project'&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;gcr.io/cloud-builders/gsutil&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;volumes&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maven-repository'&lt;/span&gt;
       &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/root/.m2'&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;tar -zcf maven-dependencies.tgz /root/.m2&lt;/span&gt;
       &lt;span class="s"&gt;gsutil cp maven-dependencies.tgz gs://${PROJECT_ID}-cache-dependencies/cache/maven-dependencies.tgz&lt;/span&gt;

&lt;span class="na"&gt;images&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;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's review the important part.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The first is about downloading the maven from the Cloud Storage bucket.
&lt;/li&gt;
&lt;/ol&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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;download-cached-maven-dependencies'&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;gcr.io/cloud-builders/gsutil&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;volumes&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maven-repository'&lt;/span&gt;
       &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/root/.m2'&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;gsutil cp gs://${PROJECT_ID}-cache-dependencies/cache/maven-dependencies.tgz maven-dependencies.tgz || exit 0&lt;/span&gt;
       &lt;span class="s"&gt;tar -zxf maven-dependencies.tgz --directory / || exit 0&lt;/span&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;name: gcr.io/cloud-builders/gsutil&lt;/code&gt;: use a provided Google image with &lt;code&gt;gsutil&lt;/code&gt; to manage buckets.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;volumes:&lt;/code&gt;: persist a volume across build steps. With maven, the default volume is &lt;code&gt;/${USER_HOME}/.m2&lt;/code&gt;, which is &lt;code&gt;/root/.m2&lt;/code&gt; in our different images.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;gsutil cp &amp;amp;&amp;amp; tar -xzf&lt;/code&gt;: downloading the files from the bucket (in &lt;code&gt;.tgz&lt;/code&gt; to reduce dependencies size).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;|| exit 0&lt;/code&gt;: As mentioned earlier, there is no easy way to ignore steps on error. Just add &lt;code&gt;exit 0&lt;/code&gt; to make sure if the folder does not exist on the bucket your build does not fail. I know it is not a perfect solution, but the step is small enough not to damage the entire pipeline. Besides, if you don’t have cache, you can still build the application.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Use the volume persisted by the previous steps to enhance the &lt;code&gt;./mvnw&lt;/code&gt; command
&lt;/li&gt;
&lt;/ol&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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build-project'&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;adoptopenjdk/openjdk11:jdk-11.0.8_10-slim&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;mavencachecloudbuild/gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;volumes&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maven-repository'&lt;/span&gt;
       &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/root/.m2'&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;./mvnw package&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Thanks to the shared volume, when the step will execute &lt;code&gt;./mvnw package&lt;/code&gt; it will rely on the dependencies provided by Cloud Storage.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Upload the dependencies. Very useful if you changed your &lt;code&gt;pom.xml&lt;/code&gt; and you need to update the cache with the new dependencies
&lt;/li&gt;
&lt;/ol&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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;upload-cached-maven-dependencies'&lt;/span&gt;
   &lt;span class="na"&gt;waitFor&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;build-project'&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;gcr.io/cloud-builders/gsutil&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;volumes&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="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;maven-repository'&lt;/span&gt;
       &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/root/.m2'&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;tar -zcf maven-dependencies.tgz /root/.m2&lt;/span&gt;
       &lt;span class="s"&gt;gsutil cp maven-dependencies.tgz gs://${PROJECT_ID}-cache-dependencies/cache/maven-dependencies.tgz&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;In this step, we expect it not to fail, so we don't use &lt;code&gt;exit 0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We reuse the volume created by &lt;code&gt;download-cached-maven-dependencies&lt;/code&gt; and potentially modified by &lt;code&gt;build-project&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We use the &lt;code&gt;waitFor&lt;/code&gt; to start this step right after the &lt;code&gt;build-project&lt;/code&gt;, as this volume will no longer be modified later.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Results
&lt;/h3&gt;

&lt;p&gt;The first build will be slightly longer, as you added 2 steps and there is no cached yet. But the next ones will be faster !&lt;/p&gt;

&lt;p&gt;Here is the result of the first build, before the cache was created.&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lEu9hX_t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l3h55qib789mwtbyd22q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lEu9hX_t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l3h55qib789mwtbyd22q.png" alt="Result of first build"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the result of the second build, after the cache was created.&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2Atpi4Bi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x8o2ehc10htvsjiu1a2y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2Atpi4Bi--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/x8o2ehc10htvsjiu1a2y.png" alt="Result of sceond build"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For now, &lt;strong&gt;we save couple of seconds (30 seconds)&lt;/strong&gt;, not worth it you might say. But keep in mind your build will get bigger and bigger, and if you do not optimize it, it will take minutes before completion. Today, we simply deploy one single Cloud Run service, but we can deploy 2 or 3, with Cloud Functions as well. If you download maven dependencies each time, you will waste a lot of time and resources. So keep that in mind.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bonus: the same for a frontend project with yarn
&lt;/h2&gt;

&lt;p&gt;Here is the &lt;code&gt;cloudbuild.yaml&lt;/code&gt; file to also save &lt;code&gt;yarn&lt;/code&gt; dependencies.&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;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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;download-cached-yarn-dependencies'&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;gcr.io/cloud-builders/gsutil&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;gsutil cp gs://${PROJECT_ID}-cache-dependencies/cache/yarn-dependencies.tgz yarn-dependencies.tgz || exit 0&lt;/span&gt;
       &lt;span class="s"&gt;tar -zxf yarn-dependencies.tgz || exit 0&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;install-yarn'&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;node&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
   &lt;span class="na"&gt;args&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;install'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--silent'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build-front'&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;node&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
   &lt;span class="na"&gt;args&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;build'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-firebase'&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;gcr.io/$PROJECT_ID/firebase&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
   &lt;span class="na"&gt;args&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;deploy'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--project=$PROJECT_ID'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--only'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hosting'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;upload-cached-yarn-dependencies'&lt;/span&gt;
   &lt;span class="na"&gt;waitFor&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;build-front'&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;gcr.io/cloud-builders/gsutil&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloudcmr-front&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;tar -zcf yarn-dependencies.tgz ./node_modules&lt;/span&gt;
       &lt;span class="s"&gt;gsutil cp yarn-dependencies.tgz gs://${PROJECT_ID}-cache-dependencies/cache/yarn-dependencies.tgz&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Note for yarn the dependencies are located under /workspace folder hierarchies. Therefore, there is no need to add a custom volume to be persisted by Cloud Build&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;In this article focused on optimizing Cloud Build with Maven and Yarn build steps, we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Common issues when not caching dependencies, by wasting resources and times&lt;/li&gt;
&lt;li&gt;Creation a bucket using &lt;code&gt;gsutil&lt;/code&gt; to store dependencies&lt;/li&gt;
&lt;li&gt;Ignore steps in error, which is not provided out-of-the-box by Cloud Build at this time&lt;/li&gt;
&lt;li&gt;Persistence of other folders across a build&lt;/li&gt;
&lt;li&gt;Downloading and fetching files from a Cloud Storage bucket&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Please note using Cloud Storage as cache storage will incur small fees as you pay for the GigaBytes stored. But don't worry, the cached maven folder is 61MB, which is free for your usage.&lt;/p&gt;

&lt;h2&gt;
  
  
  To go further
&lt;/h2&gt;

&lt;p&gt;If you feel like it, you can improve some parts of the build by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instead of downloading a file from a bucket, why not build an image containing all the dependencies you need ?&lt;/li&gt;
&lt;li&gt;Use a hash to make upload the dependencies only if they changed since last build&lt;/li&gt;
&lt;li&gt;You can also store docker image in a Bucket, which make the build even faster (using Google Jib)&lt;/li&gt;
&lt;li&gt;Improve the build avoiding hiding the errors when downloading the files from the cache folder&lt;/li&gt;
&lt;li&gt;Configure your bucket to delete files older than 2 weeks, in order to save some money for a cache that is no longer used.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/cloud-build/docs/speeding-up-builds"&gt;Best practices for speeding up builds&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/cloud-build/docs/build-config"&gt;Cloud Build global configuration file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/storage/docs/gsutil"&gt;GSutil command to manage buckets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googlecloud</category>
      <category>cloudbuild</category>
      <category>cloudrun</category>
      <category>cloudstorage</category>
    </item>
    <item>
      <title>Continuous Deployment on Firebase using Cloud Build</title>
      <dc:creator>RUARO Thibault</dc:creator>
      <pubDate>Mon, 25 Jan 2021 13:16:36 +0000</pubDate>
      <link>https://dev.to/zenika/continuous-deployment-on-firebase-using-cloud-build-m8f</link>
      <guid>https://dev.to/zenika/continuous-deployment-on-firebase-using-cloud-build-m8f</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;We just created a simple Vue application deployed in Firebase in the &lt;a href="https://dev.to/build-deploy-vue-app-in-firebase-with-cloudrun-backend"&gt;previous article&lt;/a&gt;. Like I mentioned many times in this blog, regular feedback is key to build a successful application.&lt;/p&gt;

&lt;p&gt;Let's enhance the Continuous Deployment pipeline we created in &lt;a href="https://dev.to/continuous-deployment-with-cloud-build"&gt;Continuous Deployment pipeline with Cloud Build on Cloud Run&lt;/a&gt; to add automatic deployment of the frontend part of our application.&lt;/p&gt;

&lt;p&gt;To remind you, we previously created a nice Vue application communicating with a Cloud Run backend application. The application was deployed on Firebase to have an international audience with no effort. Let's automate this deployment.&lt;/p&gt;

&lt;p&gt;Here is the &lt;a href="https://github.com/truar/blog-resources"&gt;Github project&lt;/a&gt; you can clone to follow the article.&lt;/p&gt;

&lt;h1&gt;
  
  
  Create the Continuous Deployment pipeline for a Firebase deployed application
&lt;/h1&gt;

&lt;p&gt;I won't cover all the subjects like I did in &lt;a href="https://dev.to/continuous-deployment-with-cloud-build"&gt;Continuous Deployment pipeline with Cloud Build on Cloud Run&lt;/a&gt;. If you started from here and you wish to configure your GCP project to use Cloud Build, please check previous articles.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Firebase deployment image
&lt;/h2&gt;

&lt;p&gt;To deploy using Firebase, you need to create an image containing the Firebase CLI, making it easy to deploy your application. Here is the official link to create a &lt;a href="https://cloud.google.com/cloud-build/docs/deploying-builds/deploy-firebase"&gt;Firebase image to deploy your application&lt;/a&gt;. At the time of the article, here are the commands:&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/GoogleCloudPlatform/cloud-builders-community.git
&lt;span class="nb"&gt;cd &lt;/span&gt;cloud-builders-community/firebase
gcloud builds submit &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ../..
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; cloud-builders-community/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If these steps do not work for you, check the &lt;a href="https://cloud.google.com/cloud-build/docs/deploying-builds/deploy-firebase"&gt;official link&lt;/a&gt; to make sure it didn't change since the article was written (which is very likely)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The steps above create and push a Firebase ready image on your Container Registry which can be used in a Cloud Build pipeline.&lt;/p&gt;

&lt;p&gt;To make sure the image is in your container, try executing this 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 images list &lt;span class="nt"&gt;--filter&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"name:firebase"&lt;/span&gt;      

NAME
gcr.io/truaro-resources/firebase
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cloud Build pipeline for Vue application in Firebase
&lt;/h2&gt;

&lt;p&gt;Now you have a Firebase image, let's add new steps to deploy our Vue application. As a reminder, you should have the following project structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* gcpapplication/
|--- gcpcloudrunback/
|--- gcpfirebasefront/
|--- cloudbuild.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The content of &lt;code&gt;cloudbuild.yaml&lt;/code&gt; after the first pipeline is:&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;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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dockerize-project'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;args&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;build'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;push-to-cloud-registry'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
   &lt;span class="na"&gt;args&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;push'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-cloud-run'&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;gcr.io/cloud-builders/gcloud&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;apt-get update&lt;/span&gt;
       &lt;span class="s"&gt;apt-get install -qq -y gettext&lt;/span&gt;
       &lt;span class="s"&gt;export PROJECT_ID=$PROJECT_ID&lt;/span&gt;
       &lt;span class="s"&gt;export IMAGE_VERSION=$SHORT_SHA&lt;/span&gt;
       &lt;span class="s"&gt;export SCALING_INSTANCE_COUNT=${_SCALING_INSTANCE_COUNT}&lt;/span&gt;
       &lt;span class="s"&gt;envsubst &amp;lt; gcp-cloudrun-back.yaml &amp;gt; gcp-cloudrun-back_with_env.yaml&lt;/span&gt;
       &lt;span class="s"&gt;gcloud beta run services replace gcp-cloudrun-back_with_env.yaml \&lt;/span&gt;
         &lt;span class="s"&gt;--platform=managed --region=europe-west1&lt;/span&gt;
       &lt;span class="s"&gt;gcloud run services add-iam-policy-binding gcp-cloudrun-back \&lt;/span&gt;
         &lt;span class="s"&gt;--platform=managed --region=europe-west1 \&lt;/span&gt;
         &lt;span class="s"&gt;--member="allUsers" --role="roles/run.invoker"&lt;/span&gt;

&lt;span class="na"&gt;images&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;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&lt;/span&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;It is deploying only the backend part of our application.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's add the Frontend part now.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuring Cloud Build to deploy on Firebase
&lt;/h3&gt;

&lt;p&gt;To deploy on Firebase, your Cloud Build service account must have the role Firebase Admin. Grant the role to your service account 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 projects add-iam-policy-binding &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;YOUR_PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;serviceAccount:XXXX@cloudbuild.gserviceaccount.com &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;roles/firebase.admin
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;${YOUR_PROJECT_ID}&lt;/code&gt; and &lt;code&gt;XXXX&lt;/code&gt; by your actual value&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;roles/firebase.admin&lt;/code&gt;: To allow the Cloud Build service account to deploy on Firebase&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or, &lt;a href="https://cloud.google.com/cloud-build/docs/securing-builds/configure-access-for-cloud-build-service-account"&gt;if you follow this&lt;/a&gt;, you can also manually enable the Firebase Admin role.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding steps to deploy Vue in Firebase
&lt;/h3&gt;

&lt;p&gt;Just add the following 3 steps between the step &lt;code&gt;deploy-cloud-run&lt;/code&gt; and the &lt;code&gt;images&lt;/code&gt; part:&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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;install-yarn'&lt;/span&gt;
   &lt;span class="na"&gt;waitFor&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
   &lt;span class="na"&gt;args&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;install'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--silent'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build-front'&lt;/span&gt;
   &lt;span class="na"&gt;waitFor&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;install-yarn'&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;node&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
   &lt;span class="na"&gt;args&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;build'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-firebase'&lt;/span&gt;
   &lt;span class="na"&gt;waitFor&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;build-front'&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;gcr.io/${PROJECT_ID}/firebase&lt;/span&gt;
   &lt;span class="na"&gt;args&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;deploy'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--project=$PROJECT_ID'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--only'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hosting'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As always, let's review the important part.&lt;/p&gt;

&lt;h4&gt;
  
  
  Install the yarn dependencies
&lt;/h4&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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;install-yarn'&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;waitFor&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node&lt;/span&gt;
 &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn&lt;/span&gt;
 &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
 &lt;span class="na"&gt;args&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;install'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--silent'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;waitFor: [ '-' ]&lt;/code&gt;: Run this step in parallel with the others. Indeed, we don't need to wait for the backend to be deployed in order to deploy the front. It is a performance improvement of your CD pipeline.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name: node&lt;/code&gt;: We use the official &lt;code&gt;node&lt;/code&gt; image from the Docker hub registry.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;entrypoint: yarn&lt;/code&gt;: &lt;code&gt;yarn&lt;/code&gt; is the package manager we use to build the application.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dir: gcpfirebasefront&lt;/code&gt;: We change the directory to go in the &lt;code&gt;gcpfirebasefront&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;args: [ 'install', '--silent' ]&lt;/code&gt;: Just run the &lt;code&gt;yarn install --silent&lt;/code&gt; command, to package our application for production. Not mandatory, but I find &lt;code&gt;yarn install&lt;/code&gt; to be quite verbose.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Build the Vue application
&lt;/h4&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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build-front'&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;waitFor&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;install-yarn'&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;node&lt;/span&gt;
 &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn&lt;/span&gt;
 &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
 &lt;span class="na"&gt;args&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;build'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;waitFor: [ 'install-yarn' ]&lt;/code&gt;: &lt;code&gt;yarn install&lt;/code&gt; needs to be done before moving further&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;args: [ 'build' ]&lt;/code&gt;: Just run the &lt;code&gt;yarn build&lt;/code&gt; command, to package our application for production&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;As you can see, we do not create any Docker image. We simply build the application using &lt;code&gt;yarn build&lt;/code&gt;. The &lt;code&gt;dist&lt;/code&gt; folder is located under &lt;code&gt;/workspace&lt;/code&gt;, the shared volumes of Cloud Build. The next step will be able to deploy on Firebase what has been built at this step.&lt;/p&gt;

&lt;h4&gt;
  
  
  Deploy on Firebase
&lt;/h4&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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-firebase'&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt;&lt;span class="na"&gt;waitFor&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;build-front'&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;gcr.io/$PROJECT_ID/firebase&lt;/span&gt;
 &lt;span class="na"&gt;args&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;deploy'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--project=$PROJECT_ID'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--only'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hosting'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
 &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;waitFor: [ 'build-front' ]&lt;/code&gt;: Just wait for the previous step, not the backend steps.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name: gcr.io/$PROJECT_ID/firebase&lt;/code&gt;: Step based on the image we created earlier to use the Firebase CLI&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;args: [ 'deploy', '--project=$PROJECT_ID', '--only', 'hosting' ]&lt;/code&gt;: The args to be passed to &lt;code&gt;firebase&lt;/code&gt; command&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$PROJECT_ID&lt;/code&gt;: As always, a substitution variable provided by Cloud Build&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;This step simply executes the command &lt;code&gt;firebase deploy --project=${PROJECT_ID} --only hosting&lt;/code&gt;. If you remember well, it was the same command we used in &lt;a href="https://dev.to/build-deploy-vue-app-in-firebase-with-cloudrun-backend"&gt;the previous article&lt;/a&gt; from our local machine.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Once again, do not go too fast, start slow with easy steps. You can only create a Continuous Deployment pipeline if you have something to automate...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Review the Cloud Build deployment file
&lt;/h3&gt;

&lt;p&gt;The final file looks like:&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;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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dockerize-project'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;args&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;build'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;push-to-cloud-registry'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
   &lt;span class="na"&gt;args&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;push'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-cloud-run'&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;gcr.io/cloud-builders/gcloud&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpcloudrunback&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
   &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
     &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
       &lt;span class="s"&gt;apt-get update&lt;/span&gt;
       &lt;span class="s"&gt;apt-get install -qq -y gettext&lt;/span&gt;
       &lt;span class="s"&gt;export PROJECT_ID=$PROJECT_ID&lt;/span&gt;
       &lt;span class="s"&gt;export IMAGE_VERSION=$SHORT_SHA&lt;/span&gt;
       &lt;span class="s"&gt;export SCALING_INSTANCE_COUNT=${_SCALING_INSTANCE_COUNT}&lt;/span&gt;
       &lt;span class="s"&gt;envsubst &amp;lt; gcp-cloudrun-back.yaml &amp;gt; gcp-cloudrun-back_with_env.yaml&lt;/span&gt;
       &lt;span class="s"&gt;gcloud beta run services replace gcp-cloudrun-back_with_env.yaml \&lt;/span&gt;
         &lt;span class="s"&gt;--platform=managed --region=europe-west1&lt;/span&gt;
       &lt;span class="s"&gt;gcloud run services add-iam-policy-binding gcp-cloudrun-back \&lt;/span&gt;
         &lt;span class="s"&gt;--platform=managed --region=europe-west1 \&lt;/span&gt;
         &lt;span class="s"&gt;--member="allUsers" --role="roles/run.invoker"&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;install-yarn'&lt;/span&gt;
   &lt;span class="na"&gt;waitFor&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;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
   &lt;span class="na"&gt;args&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;install'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--silent'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build-front'&lt;/span&gt;
   &lt;span class="na"&gt;waitFor&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;install-yarn'&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;node&lt;/span&gt;
   &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;
   &lt;span class="na"&gt;args&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;build'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;

 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-firebase'&lt;/span&gt;
   &lt;span class="na"&gt;waitFor&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;build-front'&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;gcr.io/$PROJECT_ID/firebase&lt;/span&gt;
   &lt;span class="na"&gt;args&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;deploy'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--project=$PROJECT_ID'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;--only'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;hosting'&lt;/span&gt; &lt;span class="pi"&gt;]&lt;/span&gt;
   &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpfirebasefront&lt;/span&gt;

&lt;span class="na"&gt;images&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;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;
 &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Change the Vue application to see a change
&lt;/h3&gt;

&lt;p&gt;Just change the &lt;code&gt;gcpfirebasefront/src/HelloWorld.vue&lt;/code&gt; file. Update the &lt;code&gt;&amp;lt;h1&amp;gt;&lt;/code&gt; to see the Frontend has been deployed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
 &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"hello"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
   &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;From firebase: &lt;span class="si"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="si"&gt;}}&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As we already have the Cloud Build triggers created in &lt;a href="https://dev.to/continuous-deployment-with-cloud-build"&gt;a previous article&lt;/a&gt;, simply push your application and everything will be automatically deployed.&lt;/p&gt;

&lt;p&gt;To follow up the build execution, go on the GCP console, you will have the logs. Or just use this command to see if the deployment is still ongoing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud builds list &lt;span class="nt"&gt;--ongoing&lt;/span&gt;
46e2992d-24f4-4df5-8190-a6c132a4827b  2020-12-05T12:37:43+00:00            github_truar_try-gcp-deploy@80c5b6d1eb9bc5425e2abec092535ff2a30ee5b6  -       WORKING
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once the build is finished (i.e the command output is empty), just access your firebase web application page. Here is my result: &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--W38BIc1D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xyw4thfcj934cfxtobh3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--W38BIc1D--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xyw4thfcj934cfxtobh3.png" alt="Result from automatic deployment in Firebase"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;In this article, we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a Firebase image to use the &lt;code&gt;firebase&lt;/code&gt; CLI in a Cloud Build step&lt;/li&gt;
&lt;li&gt;Configuring the Cloud Build service account to deploy applications on Firebase&lt;/li&gt;
&lt;li&gt;Configuring Cloud Build steps to build and deploy a static frontend application on Firebase&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With all the previous articles, we are starting to have a nice view of the tools and services we can lever to easily deploy an application on the Cloud. You might have noticed, the pricing is very appealing for an application that is available 24/7 everywhere in the world. This is one of the main advantages of GCP platform, you can build international applications for a very attractive price.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;To go further, we could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configure a cache to improve build speed to avoid downloading frontend dependencies at each build&lt;/li&gt;
&lt;li&gt;Use the multi-site Firebase feature to have a staging and production website&lt;/li&gt;
&lt;li&gt;&lt;a href="https://firebase.google.com/docs/hosting/manage-hosting-resources"&gt;Use the new preview channel feature&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Split the &lt;code&gt;cloudbuild.yaml&lt;/code&gt; file to deploy independently the frontend and the backend&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/cloud-build/docs/deploying-builds/deploy-firebase"&gt;Firebase image to deploy your application with Cloud Build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/sdk/gcloud/reference/container/images/list"&gt;Gcloud container CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/truar/blog-resources"&gt;Project on Github&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googlecloud</category>
      <category>firebase</category>
      <category>cloudbuild</category>
    </item>
    <item>
      <title>Build a Vue app with Firebase and Cloud Run</title>
      <dc:creator>RUARO Thibault</dc:creator>
      <pubDate>Mon, 25 Jan 2021 13:04:19 +0000</pubDate>
      <link>https://dev.to/zenika/build-a-vue-app-with-firebase-and-cloud-run-2h1b</link>
      <guid>https://dev.to/zenika/build-a-vue-app-with-firebase-and-cloud-run-2h1b</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Building a rich and powerful web application has never been so easy and so complicated at the same time. The web expanded really fast, and with it, the need to build more and more advanced applications. Empowered by some great frameworks wishing to simplify your life as a Frontend developer, you can easily start following some tutorials and documentation, and in the meantime, being totally lost in the wide eco-system you can now face. The time when we manipulated the DOM ourselves is now gone, and we must embrace advanced architectural style whose purpose is to ease the application maintenance and evolution.&lt;/p&gt;

&lt;p&gt;Those famous Web frameworks, you might know them already, even only by name. There are three main ones:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Angular, supported by Google developers team&lt;/li&gt;
&lt;li&gt;React supported by Facebook developers team&lt;/li&gt;
&lt;li&gt;Vue, supported by a community of developers sharing passion for frontend development&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Despite the fact Vue was not maintained by one of the &lt;a href="https://en.wikipedia.org/wiki/Big_Tech" rel="noopener noreferrer"&gt;GAFAM&lt;/a&gt;, it is nowadays a great framework used widely, even &lt;a href="https://www.netguru.com/blog/13-top-companies-that-have-trusted-vue.js-examples-of-applications" rel="noopener noreferrer"&gt;by the GAFAM themselves&lt;/a&gt;. Besides, as Vue is the framework I enjoyed the most, it is the one I chose in this article to build the frontend part of the application.&lt;/p&gt;

&lt;p&gt;Developing an application is now accessible quickly to the most, but deploying this application and making it accessible is quite another challenge. Not everyone is comfortable with server administration and deployment tasks. Hopefully, Google comes with an answer: Firebase. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Firebase is a platform developed by Google for creating mobile and web applications. It was originally an independent company founded in 2011. In 2014, Google acquired the platform and it is now their flagship offering for app development. -- Google&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, Firebase makes it easier for you to host your application by offering complete server management. Your app is easily scalable, which means it can support load peaks, and accessible worldwide, for almost a penny. Under the hood, Firebase uses Google Cloud Platform technology to host your application. This makes easy to have a Vue application accessible 100% of the time for free (almost but clearly, you won't pay much) communicating with another backend application hosted on GCP, like Cloud Functions or Cloud Run.&lt;/p&gt;

&lt;p&gt;In this article, we will focus on developing a Vue application communicating with the Spring Boot application hosted on Cloud Run &lt;a href="https://dev.to/deploying-an-app-in-gcp-part1"&gt;we developed in the previous article&lt;/a&gt;. We will host it using Firebase and deploy it by improving the Cloud Build pipeline &lt;a href="https://dev.to/continuous-deployment-with-cloud-build"&gt;we covered in the second article&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Node.js&lt;/strong&gt;: an open-source javascript runtime environment. You need it in order to run javascript code outside of a browser. To install Node, &lt;a href="https://nodejs.org/en/download/package-manager/" rel="noopener noreferrer"&gt;follow this link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yarn or NPM&lt;/strong&gt;: a package manager to download the different libs you need to build your application. If you come from the Java world, you might know Maven or Gradle. Those are famous package manager for java application. For frontend development, I will use Yarn. To install Yarn, &lt;a href="https://classic.yarnpkg.com/en/docs/install/" rel="noopener noreferrer"&gt;click here&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Building the VusJS application
&lt;/h1&gt;

&lt;p&gt;Vue team released recently the third version of Vue. We will not cover differences between Vue 2 and Vue 3, but let's use the latest versions available.&lt;/p&gt;

&lt;p&gt;Remember the folder trees we had in the previous articles?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* gcpapplication
|--- gcpcloudrunback (this one has been created in the first part)
|--- gcpfirebasefront (you migh not have this one yet)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this article, we will create the &lt;code&gt;gcpfirebasefront&lt;/code&gt; folder. Don't do it manually, we will create it automatically in the next section.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating the application using vue-cli
&lt;/h2&gt;

&lt;p&gt;First, &lt;a href="https://cli.vuejs.org/guide/installation.html" rel="noopener noreferrer"&gt;follow the official documentation&lt;/a&gt; to install or upgrade the vue-cli. If you already have, I recommend upgrading to the latest version (4.5.9 at the time of the article creation), as you can use Vue 3.&lt;/p&gt;

&lt;p&gt;Considering your are located in the &lt;code&gt;gcpapplication&lt;/code&gt; folder, run&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vue create gcpfirebasefront &lt;span class="nt"&gt;--preset&lt;/span&gt; __default_vue_3__
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;It might take a couple of minutes, just be patient.&lt;/li&gt;
&lt;li&gt;The preset Vue 3 is very minimal using babel and eslint. It will be more than enough for our application.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;For the rest of this part, we will only be located inside the folder &lt;code&gt;gcpfirebasefront&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring the application
&lt;/h2&gt;

&lt;p&gt;Let's create a file &lt;code&gt;vue.config.js&lt;/code&gt; at the root of &lt;code&gt;gcpfirebasefront&lt;/code&gt;:&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="c1"&gt;// vue.config.js&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="na"&gt;devServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8088&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;proxy&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;/api&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;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;http://localhost:8080&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                &lt;span class="na"&gt;ws&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;changeOrigin&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="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;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;devServer.port&lt;/code&gt;: By default, the development server port is &lt;code&gt;8080&lt;/code&gt;, which will conflict with our backend application. Let's change it to &lt;code&gt;8088&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;devServer.proxy&lt;/code&gt;: Create a middleware proxy to change the origin of request targeting the &lt;code&gt;target&lt;/code&gt; attribute. This is very convenient in order to avoid CORS configuration. This way, the browser sends a request to the same server as the Frontend, and the proxy is in charge of calling the backend service by changing the Origin.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Calling the backend server to change the display
&lt;/h2&gt;

&lt;p&gt;If you take a look into the Vue application generated by &lt;code&gt;vue-cli&lt;/code&gt;, you can see several folders. The interesting one in our use case will be &lt;code&gt;src&lt;/code&gt;, which contains the application &lt;code&gt;js&lt;/code&gt; files. Let's update &lt;code&gt;src/App.vue&lt;/code&gt; to add an interaction with our server, and display the Welcome message coming from the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;// src/App.vue
&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Vue logo"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"./assets/logo.png"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;HelloWorld&lt;/span&gt; &lt;span class="na"&gt;:msg=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;template&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;HelloWorld&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./components/HelloWorld.vue&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&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;App&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;components&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;HelloWorld&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="nf"&gt;data&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="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading...&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="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;created&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;script&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;style&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="nf"&gt;#app&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;font-family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Avenir&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Helvetica&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Arial&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;sans-serif&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;-webkit-font-smoothing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;antialiased&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;-moz-osx-font-smoothing&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;grayscale&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;text-align&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;center&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#2c3e50&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;margin-top&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;60px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="k"&gt;style&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;Let's focus on the changed lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight vue"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;HelloWorld&lt;/span&gt; &lt;span class="na"&gt;:msg=&lt;/span&gt;&lt;span class="s"&gt;"message"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;We bind the property &lt;code&gt;msg&lt;/code&gt; of the &lt;code&gt;HelloWorld&lt;/code&gt; component to an attribute from our component inner &lt;code&gt;data&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is the creation and the modification of the &lt;code&gt;data&lt;/code&gt; part:&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="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
  &lt;span class="nf"&gt;data&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="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Loading...&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="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;created&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;text&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;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Set &lt;code&gt;Loading...&lt;/code&gt; in the &lt;code&gt;message&lt;/code&gt; attribute by default. &lt;/li&gt;
&lt;li&gt;Fetch the data from the server when Vue calls the &lt;code&gt;created&lt;/code&gt; method. This method is part if vue lifecycle and is called automatically at component creation.&lt;/li&gt;
&lt;li&gt;As you might notice, &lt;code&gt;fetch('/api/')&lt;/code&gt; shows the backend server is not directly targeted. Instead, the proxy intercepts the request and change apply corresponding changes according to the &lt;code&gt;devServer.proxy&lt;/code&gt; configuration.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Test locally the application
&lt;/h2&gt;

&lt;p&gt;Just like a cook tasting every part of his meals to make sure it is delicious and has the expected taste, you must taste (😅)   your application at each step. We could have done one to test the creation using &lt;code&gt;vue-cli&lt;/code&gt; but for the article length sake, I decided not to.&lt;/p&gt;

&lt;p&gt;Now, let's try if our frontend is properly communicating with our backend. Open 2 terminals, and from the folder &lt;code&gt;gcpapplication&lt;/code&gt;, run:&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;# terminal 1&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;gcpcloudrunback
./mvnw spring-boot:run
&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="c"&gt;# terminal 2&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;gcpfirebasefront
yarn serve
&lt;span class="c"&gt;# or npm run serve depending on what is installed on your application&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open your browser and navigate to &lt;code&gt;localhost:8088&lt;/code&gt;. You should see something like this: &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%2Fi%2Fhqcdsgmeir2umqzxq7g1.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%2Fi%2Fhqcdsgmeir2umqzxq7g1.png" alt="Local integration between Vue and Spring success"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you see something different than &lt;code&gt;Hello World. I am deployed automatically&lt;/code&gt;, that means you may have missed a step. Read back, and come back again when it is working. Otherwise, you'll miss the best part: deploying in Firebase.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Deploying the application on Firebase Hosting
&lt;/h1&gt;

&lt;p&gt;Firebase Hosting is a great solution to host static websites (like Single Page Application) where the content of the files is static (like a javascript application). With the Hosting solution, you pay depending on your website size. The more files you have, the more expensive is the bill. For this article, the Hosting solution is free given our very small project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect on Firebase and add your project
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Import your GCP project to Firebase
&lt;/h3&gt;

&lt;p&gt;The first thing is to add your GCP project to Firebase (created in &lt;a href="https://dev.to/deploying-an-app-in-gcp-part1"&gt;the first article&lt;/a&gt;). Quickly, just log in to firebase and &lt;a href="https://console.firebase.google.com/u/0/" rel="noopener noreferrer"&gt;go on this URL&lt;/a&gt;. From there:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click on &lt;code&gt;Add Project&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Select the one you created previously.&lt;/li&gt;
&lt;li&gt;Accept or not using Google analytics. For this project, we don't need it. It is up to you.&lt;/li&gt;
&lt;li&gt;Accept the terms and conditions&lt;/li&gt;
&lt;li&gt;Let firebase prepare your project.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Prepare your environment
&lt;/h3&gt;

&lt;p&gt;Once the project is set, install the Firebase CLI locally to execute some commands. To do so, &lt;a href="https://firebase.google.com/docs/cli" rel="noopener noreferrer"&gt;follow the official documentation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After the installation, run this to make sure it works:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;firebase &lt;span class="nt"&gt;--version&lt;/span&gt;
8.16.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;8.16.2 was at the time of the article creation. You might have a different output.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Initialize your Firebase project
&lt;/h2&gt;

&lt;p&gt;The Firebase CLI has an &lt;code&gt;init&lt;/code&gt; command, but it does not support passing all options at once. You need to interact with the CLI, and it is really not convenient for this article. So if you feel like it, you can try running &lt;code&gt;firebase init&lt;/code&gt;. But for the sake of the article, I'll give the file you need to create.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you want to run &lt;code&gt;firebase init&lt;/code&gt; make sure you are located in &lt;code&gt;gcpfirebasefront&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Create a file called &lt;code&gt;firebase.json&lt;/code&gt; in &lt;code&gt;gcpfirebasefront&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 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;"hosting"&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;"public"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ignore"&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;"firebase.json"&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="s2"&gt;"**/node_modules/**"&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;"rewrites"&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;"source"&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;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/index.html"&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;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;blockquote&gt;
&lt;p&gt;Quick explanation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;public&lt;/code&gt;: the folder in which are located the production files built by &lt;code&gt;yarn build&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ignore&lt;/code&gt;: ignores folders/files to be uploaded onto Firebase. As you only pay for the storage, make sure you don't upload unused files.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;rewrites&lt;/code&gt;: A rewrite rules mandatory in the case of Single Page Application, to make sure all URLs use the &lt;code&gt;index.html&lt;/code&gt; file.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;These files should be the result if you ran the command &lt;code&gt;firebase init&lt;/code&gt;. Besides, the &lt;code&gt;firebase init&lt;/code&gt; command generates a file &lt;code&gt;.firebaserc&lt;/code&gt;, but we won't need it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Connect your Firebase site to Cloud Run
&lt;/h2&gt;

&lt;p&gt;Since recently, Firebase has a convenient way to redirect some HTTP requests to a Managed Cloud Run service. To do so, the &lt;code&gt;firebase.json&lt;/code&gt; file needs to configure the &lt;code&gt;rewrites&lt;/code&gt; rule. Add a new &lt;code&gt;rewrite&lt;/code&gt; rule like this as the first &lt;code&gt;rewrites&lt;/code&gt; array element:&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;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"run"&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;"serviceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gcp-cloudrun-back"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"europe-west1"&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;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;source&lt;/code&gt;: Redirect HTTP request &lt;code&gt;api&lt;/code&gt; to the Cloud Run service&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;run&lt;/code&gt;: The Cloud Run service name &lt;code&gt;serviceId&lt;/code&gt; and the &lt;code&gt;region&lt;/code&gt;. Remember &lt;a href="https://dev.to/deploying-an-app-in-gcp-part1"&gt;from the first article&lt;/a&gt;, we deployed a Cloud Run service called &lt;code&gt;gcp-cloudrun-back&lt;/code&gt; in the region &lt;code&gt;europe-west1&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This configuration is really great to avoid &lt;code&gt;CORS&lt;/code&gt; configuration, where most of the time, this can lead to security issues if not thought carefully. So if possible, let's avoid those problems.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is the final form of the &lt;code&gt;firebase.json&lt;/code&gt; file:&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;"hosting"&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;"public"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"dist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ignore"&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;"firebase.json"&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="s2"&gt;"**/node_modules/**"&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;"rewrites"&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;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/api/**"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"run"&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;"serviceId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"gcp-cloudrun-back"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"region"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"europe-west1"&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;span class="nl"&gt;"source"&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;"destination"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/index.html"&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;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;blockquote&gt;
&lt;p&gt;Note the rewrites rules order, first matched first served.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deploy the application on Firebase
&lt;/h2&gt;

&lt;p&gt;Now, let's use the &lt;code&gt;firebase&lt;/code&gt; CLI to deploy our application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn build
firebase deploy &lt;span class="nt"&gt;--project&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="nt"&gt;--only&lt;/span&gt; hosting

...
✔  Deploy &lt;span class="nb"&gt;complete&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;

Project Console: https://console.firebase.google.com/project/truaro-resources/overview
Hosting URL: https://truaro-resources.web.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;${PROJECT_ID}&lt;/code&gt; with your Firebase Project Id. &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;HOSTING_URL&lt;/code&gt; is your application URL.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, you can access your application on Firebase using the &lt;code&gt;Hosting URL&lt;/code&gt; firebase gave you after the execution of the deployment command. The webpage displayed should be the same as the local test we did earlier: &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%2Fi%2Fhqcdsgmeir2umqzxq7g1.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%2Fi%2Fhqcdsgmeir2umqzxq7g1.png" alt="Firebase and Cloud Run integration success"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You might first see &lt;code&gt;Loading...&lt;/code&gt; a couple of seconds before seeing &lt;code&gt;Hello World. I am automatically deployed&lt;/code&gt;. Don't worry, it only means your Cloud Run service is booting. Once Cloud Run is up, the communication with the server is very fast and you won't even see &lt;code&gt;Loading...&lt;/code&gt; anymore. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;🎉  Congratulations !! If you made it, here is what you accomplished:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a Vue 3 application using the &lt;code&gt;vue-cli&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fetching data from your server with a Proxy configuration to avoid CORS request&lt;/li&gt;
&lt;li&gt;Configuring a Firebase project to use the great &lt;code&gt;Hosting&lt;/code&gt; solution&lt;/li&gt;
&lt;li&gt;Use the &lt;code&gt;firebase&lt;/code&gt; CLI to deploy your first website on Firebase &lt;/li&gt;
&lt;li&gt;Configure your Firebase website to communicate with a Cloud Run service&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;To go further with this vue application, you could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Enhance it by &lt;a href="https://router.vuejs.org/" rel="noopener noreferrer"&gt;adding a router&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Improve the design using a nice component library, like &lt;a href="https://vuetifyjs.com/en/" rel="noopener noreferrer"&gt;Vuetify&lt;/a&gt; or &lt;a href="https://quasar.dev/" rel="noopener noreferrer"&gt;Quasar&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Use a CD pipeline using Cloud Build to automatically deploy your application using Cloud Build (coming in a next article)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.tecla.io/blog/2019-stats-on-top-js-frameworks-react-angular-and-vue/" rel="noopener noreferrer"&gt;Vue, React and Angular usage comparison&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cli.vuejs.org/config/" rel="noopener noreferrer"&gt;Configuring Vue application&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://v3.vuejs.org/guide/instance.html#lifecycle-diagram" rel="noopener noreferrer"&gt;Vue lifecycle diagram&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://firebase.google.com/docs/hosting/full-config" rel="noopener noreferrer"&gt;Firebase hosting full configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://firebase.google.com/docs/hosting/cloud-run" rel="noopener noreferrer"&gt;Firebase and Cloud Run communication&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://router.vuejs.org/" rel="noopener noreferrer"&gt;Vue router&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://vuetifyjs.com/en/" rel="noopener noreferrer"&gt;Vuetify&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://quasar.dev/" rel="noopener noreferrer"&gt;Quasar&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googlecloud</category>
      <category>vue</category>
      <category>firebase</category>
    </item>
    <item>
      <title>Continuous Deployment pipeline with Cloud Build on Cloud Run</title>
      <dc:creator>RUARO Thibault</dc:creator>
      <pubDate>Sat, 12 Dec 2020 10:05:02 +0000</pubDate>
      <link>https://dev.to/zenika/continuous-deployment-pipeline-with-cloud-build-on-cloud-run-40a2</link>
      <guid>https://dev.to/zenika/continuous-deployment-pipeline-with-cloud-build-on-cloud-run-40a2</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Building software is a long and difficult process that takes time and skills. In order to succeed in our tasks, we as software developers need to craft the software steps by steps. There are a lot of good development practices for writing code, in order to get feedback as soon as possible. But if we set the boundaries at the code itself, we will miss a lot. &lt;/p&gt;

&lt;p&gt;A great team understands the importance of giving users access to the software as soon as possible. Even if the software is not yet complete, allowing the users to play with the application gives you way better feedback than any other things you could do. In other words, the new functionalities need to be deployed as soon as they are done, to collect those precious feedback. &lt;/p&gt;

&lt;p&gt;That is why building a &lt;strong&gt;Continuous Deployment pipeline&lt;/strong&gt; as soon as possible will put your product on the best track to become a successful software. Once the pipeline set, you don't bother anymore with feature deployments. You give at ease access to the user almost instantaneously, and collect its feedback on a daily basis.&lt;/p&gt;

&lt;p&gt;In my journey as a Cloud Architect, I have decided to try some GCP features to create a CD pipeline. The ones we cover today are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Source Repositories&lt;/strong&gt;: Mirroring a Github repository, it keeps your code sync on the GCP platform.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cloud Build&lt;/strong&gt;: Create a Continuous Deployment pipeline and trigger builds when a change occurs in your repository.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This article is based on the first one where &lt;a href="https://dev.to/deploying-an-app-in-gcp-part1"&gt;we created a Spring application and deployed it on Cloud Run&lt;/a&gt;. You can directly get the code by &lt;a href="https://github.com/truar/blog-resources.git"&gt;cloning the repository&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a reminder, you should have the following folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* gcpapplication
|--- gcpcloudrunback (this one has been created in the first part)
|--- gcpfirebasefront (you migh not have this one yet)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Please note we will use &lt;code&gt;gcpapplication&lt;/code&gt; as the root folder in this article.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Creating a Continuous Deployment pipeline using Cloud Build
&lt;/h1&gt;

&lt;p&gt;Cloud Build is a service that executes your builds on Google infrastructure. De facto, you can create a Continuous Deployment pipeline using Google provided image to build and deploy your application on GCP.&lt;/p&gt;

&lt;p&gt;Together, we will use Cloud Build to deploy our previously created Spring Application hosted on Cloud Run.&lt;/p&gt;

&lt;p&gt;Cloud Build allows you to define steps executed on a Docker container. Among the features, we will be using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;GCP provided image&lt;/strong&gt; to enhance our build. Indeed, Google provided images are already available on build execution, there is no need to download them from Docker registry, which could slow down your build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Steps&lt;/strong&gt; which are the main concept of Cloud Build: defines sequential or parallel steps to execute your build.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Substitutions variables&lt;/strong&gt; that enrich our build to favor re-usability.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Before using the Cloud Build API, as always, we need to activate it:&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;cloudbuild.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood, Cloud Build uses Cloud Storage bucket to store logs and other metadata for the build. This might incur very small fees.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building and Pushing our Docker image using Cloud Build
&lt;/h2&gt;

&lt;p&gt;The first step is to build and push our Dockerfile on Container Registry with Cloud Build. To do so, we will simply add a new file called &lt;code&gt;cloudbuild.yaml&lt;/code&gt; at the root of our project.&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;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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dockerize-project'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
    &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpcloudrunback&lt;/span&gt;
    &lt;span class="na"&gt;args&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;build'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&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;images&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;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's split this file.&lt;/p&gt;

&lt;p&gt;The first step is about building our Dockerfile to generate the Docker image.&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;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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;dockerize-project'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
    &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpcloudrunback&lt;/span&gt;
    &lt;span class="na"&gt;args&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;build'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt;
           &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;-t'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;steps&lt;/code&gt;: Specifies a list of actions that you want Cloud Build to perform on the GCP environment. Here we define a first step to build our multi-stage Dockerfile.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt;: a unique name across the file that can be used as a reference for parallel build.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;name&lt;/code&gt;: The docker image name used to execute the command. You can reference image that will be fetched from Docker registry, or from Google Container Registry. Please note the predefined images fetched from Container Registry are cached by Google.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dir&lt;/code&gt;: The current folder you want to execute the command in. In our project, the &lt;code&gt;cloudbuild.yaml&lt;/code&gt; file is at the root of our project, but the Dockerfile is in &lt;code&gt;gcpcloudrunback&lt;/code&gt; folder.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;args&lt;/code&gt;: The list of arguments passed to the &lt;code&gt;entrypoint&lt;/code&gt; statement of the Dockerfile referenced in the &lt;code&gt;name&lt;/code&gt; part.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$PROJECT_ID&lt;/code&gt;: A substitution variable automatically replaced by Cloud Build when running your build.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$SHORT_SHA&lt;/code&gt;: A substitution variable provided by a Cloud Build trigger. By default, the &lt;code&gt;SHORT_SHA&lt;/code&gt; is not available as the build needs to be triggered. But we will see how to provide substitutions variables when testing our Cloud Build config without trigger.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;The final statement pushes images to Container Registry.&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;images&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;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:latest'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can specify all the images built during your build to Container Registry, they will all be pushed.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In order to check if our configuration works as expected, we can use &lt;code&gt;gcloud builds submit&lt;/code&gt; command. This command uploads your current folder into a Storage Bucket used by Cloud Build to perform the build.&lt;/p&gt;

&lt;p&gt;To reduce the size of the uploaded archive, I recommend creating a &lt;code&gt;.cloudignore&lt;/code&gt; file containing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;.git
dist
node_modules
vendor
*.jar
target
.firebase
.mvn
.idea
*.iml
mvnw*
build
.gradle
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This file covers many things, like maven, gradle, firebase... not all of them are mandatory at the moment for your simple application, but it might get bigger, so let's keep those folders excluded. It doesn't hurt anyway.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, from the root folder, run the command (it might take a few minutes):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud builds submit &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--substitutions&lt;/span&gt; &lt;span class="nv"&gt;SHORT_SHA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;local

&lt;/span&gt;DONE
&lt;span class="nt"&gt;----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------&lt;/span&gt;

ID                                    CREATE_TIME                DURATION  SOURCE                                                                                          IMAGES                                                     STATUS
926b58b7-d97e-406e-b971-378cf190ab3f  2020-11-19T15:30:09+00:00  1M54S     gs://truaro-resources_cloudbuild/source/1605799808.257327-c27971b7200a4f50ba7f84621e119d6a.tgz  gcr.io/truaro-resources/gcp-cloudrun-back:local &lt;span class="o"&gt;(&lt;/span&gt;+2 more&lt;span class="o"&gt;)&lt;/span&gt;  SUCCESS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To make sure the image is pushed, you can also list the image on Container Registry using the 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 images list-tags gcr.io/&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/gcp-cloudrun-back

DIGEST        TAGS          TIMESTAMP
f594672dce19  latest,local  2020-11-19T16:31:56
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Replace &lt;code&gt;${PROJECT_ID}&lt;/code&gt; with your actual Project Id.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deploying the image on Cloud Run using Cloud Build
&lt;/h2&gt;

&lt;p&gt;When using Cloud Build, GCP uses a service account dedicated to Cloud Build. This service account will communicate with your GCP resources, such as Cloud Run in our case. To allow Cloud Build service account managing Cloud Run resources, we need to add the proper roles to Cloud Build service account. &lt;/p&gt;

&lt;h3&gt;
  
  
  Granting access at Cloud Run to the Cloud Build service account
&lt;/h3&gt;

&lt;p&gt;First, figure out what service account your Cloud Build is using by going on the &lt;code&gt;GCP web interface&lt;/code&gt; &amp;gt; &lt;code&gt;Cloud Build&lt;/code&gt; &amp;gt; &lt;code&gt;Settings&lt;/code&gt;. Once you have the service account name, you can use it in the next commamd.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;You should only replace the &lt;code&gt;XXXX&lt;/code&gt;, the email domain name should remain the same.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Grant the &lt;code&gt;roles/run.admin&lt;/code&gt; and &lt;code&gt;roles/iam.serviceAccountUser&lt;/code&gt; roles to &lt;code&gt;XXXX@cloudbuild.gserviceaccount.com&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;gcloud projects add-iam-policy-binding &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;YOUR_PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;serviceAccount:XXXX@cloudbuild.gserviceaccount.com &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;roles/run.admin

gcloud projects add-iam-policy-binding &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;YOUR_PROJECT_ID&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;serviceAccount:XXXX@cloudbuild.gserviceaccount.com &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;roles/iam.serviceAccountUser
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Replace &lt;code&gt;${YOUR_PROJECT_ID}&lt;/code&gt; and &lt;code&gt;XXXX&lt;/code&gt; by your actual value&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;roles/run.admin&lt;/code&gt;: To allow the Cloud Build service account to perform creation and modification of Cloud Run revisions.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;roles/iam.serviceAccountUser&lt;/code&gt;: To allow the Cloud Build service account to use our specific Cloud Run service account created in &lt;a href="https://dev.to/deploying-an-app-in-gcp-part1"&gt;the previous article&lt;/a&gt; to ensure the Principle of least privileges.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Or, &lt;a href="https://cloud.google.com/cloud-build/docs/securing-builds/configure-access-for-cloud-build-service-account"&gt;if you follow this&lt;/a&gt;, you can also manually enable the Cloud Run Admin and Service Account User roles.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding new steps to deploy to Cloud Run
&lt;/h3&gt;

&lt;p&gt;To make the most of Cloud Build, let's update the &lt;code&gt;gcpcloudrunback/gcp-cloudrun-back.yaml&lt;/code&gt; description file to use some &lt;strong&gt;substitutions variables&lt;/strong&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;serving.knative.dev/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;gcp-cloudrun-back&lt;/span&gt;
  &lt;span class="na"&gt;labels&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;cloud.googleapis.com/location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;europe-west1&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;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&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;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;autoscaling.knative.dev/maxScale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;${SCALING_INSTANCE_COUNT}'&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;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcp-cloudrun-back&lt;/span&gt;
      &lt;span class="na"&gt;containerConcurrency&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;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/${PROJECT_ID}/gcp-cloudrun-back:${IMAGE_VERSION}&lt;/span&gt;
          &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1000m&lt;/span&gt;
              &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;256Mi&lt;/span&gt;
  &lt;span class="na"&gt;traffic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;percent&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;latestRevision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;

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

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://dev.to/zenika/deploying-your-spring-boot-application-in-cloud-run-59i4"&gt;From the previous article&lt;/a&gt;, we just replaces some value with placeholders. Please do not modify them in the file, we will Cloud Build and &lt;code&gt;envsubst&lt;/code&gt;, a linux tool, to deploy the revision with correct values.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can add new steps to the &lt;code&gt;cloudbuild.yaml&lt;/code&gt; file that pushes early in the build the image and executes the Cloud Run deployment commands. Add those steps after the step &lt;code&gt;dockerize-project&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;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;push-to-cloud-registry'&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;gcr.io/cloud-builders/docker&lt;/span&gt;
  &lt;span class="na"&gt;args&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;push'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;gcr.io/$PROJECT_ID/gcp-cloudrun-back:$SHORT_SHA'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;deploy-cloud-run'&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;gcr.io/cloud-builders/gcloud&lt;/span&gt;
  &lt;span class="na"&gt;dir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcpcloudrunback&lt;/span&gt;
  &lt;span class="na"&gt;entrypoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bash&lt;/span&gt;
  &lt;span class="na"&gt;args&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;-c'&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;apt-get update&lt;/span&gt;
      &lt;span class="s"&gt;apt-get install -qq -y gettext&lt;/span&gt;
      &lt;span class="s"&gt;export PROJECT_ID=$PROJECT_ID&lt;/span&gt;
      &lt;span class="s"&gt;export IMAGE_VERSION=$SHORT_SHA&lt;/span&gt;
      &lt;span class="s"&gt;export SCALING_INSTANCE_COUNT=${_SCALING_INSTANCE_COUNT}&lt;/span&gt;
      &lt;span class="s"&gt;envsubst &amp;lt; gcp-cloudrun-back.yaml &amp;gt; gcp-cloudrun-back_with_env.yaml&lt;/span&gt;
      &lt;span class="s"&gt;gcloud beta run services replace gcp-cloudrun-back_with_env.yaml \&lt;/span&gt;
        &lt;span class="s"&gt;--platform=managed --region=europe-west1&lt;/span&gt;
      &lt;span class="s"&gt;gcloud run services add-iam-policy-binding gcp-cloudrun-back \&lt;/span&gt;
        &lt;span class="s"&gt;--platform=managed --region=europe-west1 \&lt;/span&gt;
        &lt;span class="s"&gt;--member="allUsers" --role="roles/run.invoker"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;push-to-cloud-registry&lt;/code&gt; pushes the image early in the build. Cloud Run needs the image to be accessible in Container Registry to work. If we rely only on the &lt;code&gt;images&lt;/code&gt; at the end of the pipeline, Cloud Run will not be able to push the image because the image will not be pushed in Container Registry yet.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;envsubst&lt;/code&gt; is a tool replacing &lt;code&gt;${ENV_VAR}&lt;/code&gt; statements with the actual value from your environment in the target file. Very handy ! &lt;/li&gt;
&lt;li&gt;
&lt;code&gt;${_SCALING_INSTANCE_COUNT}&lt;/code&gt; is a substitution variable we will define in a Cloud Build Trigger.&lt;/li&gt;
&lt;li&gt;As you can see, we reused the &lt;code&gt;gcloud beta run&lt;/code&gt; commands we executed in &lt;a href="https://dev.to/deploying-an-app-in-gcp-part1"&gt;the previous article&lt;/a&gt;.
Which is why I insist: start slow with manual command and once you are comfortable, improve gradually and introduce new concepts, like automation script.&lt;/li&gt;
&lt;li&gt;To execute multiple commands in a single step, you can change the &lt;code&gt;entrypoint&lt;/code&gt; to &lt;code&gt;bash&lt;/code&gt; and use the &lt;code&gt;-c&lt;/code&gt; option.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;Execute the deployment command again, and don't forget to add the new substitution variable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud builds submit &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--substitutions&lt;/span&gt; &lt;span class="nv"&gt;SHORT_SHA&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;local&lt;/span&gt;,_SCALING_INSTANCE_COUNT&lt;span class="o"&gt;=&lt;/span&gt;3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Check if the deployment is successful:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud run revisions list &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gcp-cloudrun-back &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;managed &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;europe-west1

   REVISION                 ACTIVE  SERVICE            DEPLOYED                 DEPLOYED BY
✔  gcp-cloudrun-back-n8tcg  &lt;span class="nb"&gt;yes     &lt;/span&gt;gcp-cloudrun-back  2020-11-21 15:12:10 UTC  957758254747@cloudbuild.gserviceaccount.com
✔  gcp-cloudrun-back-jrr7l          gcp-cloudrun-back  2020-11-17 18:18:58 UTC  thibault.ruaro@zenika.com
✔  gcp-cloudrun-back-qfv4q          gcp-cloudrun-back  2020-11-16 20:09:37 UTC  thibault.ruaro@zenika.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Automating the build with a Cloud Build trigger
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Pushing your code into Cloud Source Repositories
&lt;/h3&gt;

&lt;p&gt;Cloud Source Repositories is a Google solution that host a Git server, like Github, to store your code on a remote server. Having your code on Cloud Source Repositories enables some features like :&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Live application profiling&lt;/li&gt;
&lt;li&gt;Deploying with Cloud Build&lt;/li&gt;
&lt;li&gt;Code search&lt;/li&gt;
&lt;li&gt;And even more&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I won't comment much in this article, you basically have to follow the Google documentation to get started.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you want to mirror your Github repository, &lt;a href="https://cloud.google.com/source-repositories/docs/mirroring-a-github-repository"&gt;follow this link&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;If you want to use Cloud Source Repositories as your remote git directory, &lt;a href="https://cloud.google.com/source-repositories/docs/creating-an-empty-repository"&gt;follow this link&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In my opinion, I like having my code open source on Github, so I prefer mirroring my repository. But I leave that choice to you.&lt;/p&gt;

&lt;h3&gt;
  
  
  Create the Cloud Build trigger
&lt;/h3&gt;

&lt;p&gt;Execute the command to create a trigger on your &lt;strong&gt;synchronized cloud-source-repositories&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;gcloud beta builds triggers create cloud-source-repositories &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gcpapplication-trigger &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--repo&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;REPO_NAME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--branch-pattern&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;".*"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--build-config&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cloudbuild.yaml &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--substitutions&lt;/span&gt; &lt;span class="nv"&gt;_SCALING_INSTANCE_COUNT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3

Created &lt;span class="o"&gt;[&lt;/span&gt;https://cloudbuild.googleapis.com/v1/projects/...].
NAME     CREATE_TIME                STATUS
trigger  2020-11-20T08:26:07+00:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Hint: To find your &lt;code&gt;${REPO_NAME}&lt;/code&gt;, you can execute the command &lt;code&gt;gcloud source repos list&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;branch-pattern&lt;/code&gt;: Let's say that every branch will trigger a build. In real life scenario, you could have a trigger for master, and a trigger for the development branches.&lt;/li&gt;
&lt;li&gt;We add a substitution variable &lt;code&gt;_SCALING_INSTANCE_COUNT&lt;/code&gt; to dynamically modify the scalability of our Cloud Run service. Every user substitution variable must start with &lt;code&gt;_&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;For our Cloud Build deployment file, Cloud Build Trigger provides predefined substitutions variables, like &lt;code&gt;PROJECT_ID&lt;/code&gt; or &lt;code&gt;SHORT_SHA&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;You could also directly connect your trigger to Github, but I didn't try it. &lt;a href="https://cloud.google.com/cloud-build/docs/automating-builds/create-manage-triggers#gcloud"&gt;Here is the documentation of you want to try&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trigger the build with a code modification
&lt;/h3&gt;

&lt;p&gt;Modify the class &lt;code&gt;gcpcloudrunback/src/main/java/dev/truaro/blog/gcpcloudrunback/HelloWorldController.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;@RestController&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;HelloWorldController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&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;String&lt;/span&gt; &lt;span class="nf"&gt;helloWorld&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="s"&gt;"Hello World. I am automatically deployed"&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;/div&gt;



&lt;p&gt;Commit and push your modification:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-am&lt;/span&gt; &lt;span class="s2"&gt;"Triggering a build"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If everything is properly configured, after several minutes, you should see a new revision for the Cloud Run service:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud run revisions list &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;gcp-cloudrun-back &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;managed &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;europe-west1

   REVISION                 ACTIVE  SERVICE            DEPLOYED                 DEPLOYED BY
✔  gcp-cloudrun-back-62jvq  &lt;span class="nb"&gt;yes     &lt;/span&gt;gcp-cloudrun-back  2020-11-21 16:14:18 UTC  957758254747@cloudbuild.gserviceaccount.com
✔  gcp-cloudrun-back-n8tcg          gcp-cloudrun-back  2020-11-21 15:12:10 UTC  957758254747@cloudbuild.gserviceaccount.com
✔  gcp-cloudrun-back-jrr7l          gcp-cloudrun-back  2020-11-17 18:18:58 UTC  thibault.ruaro@zenika.com
✔  gcp-cloudrun-back-qfv4q          gcp-cloudrun-back  2020-11-16 20:09:37 UTC  thibault.ruaro@zenika.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Final check, request the service (change this link with the one corresponding to the your Cloud Run service):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://gcp-cloudrun-back-a75acdipmq-ew.a.run.app/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Hello World. I am automatically deployed&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Congratulations, you created an entire Continuous Deployment pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Respecting Google best practices in terms of security (Service Account specific to your service) and development (PORT environment for your container)&lt;/li&gt;
&lt;li&gt;Using mostly &lt;code&gt;gcloud&lt;/code&gt; to increase your productivity and favor re-usability&lt;/li&gt;
&lt;li&gt;Using Source Repositories to host or mirror your Github project&lt;/li&gt;
&lt;li&gt;Using Cloud Build to define the CD pipeline&lt;/li&gt;
&lt;li&gt;Using Cloud Build Triggers to activate the CD pipeline on any branch pushed to your repository&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My opinion about Cloud Build
&lt;/h2&gt;

&lt;p&gt;I have to admit, I had some fun doing this. But it is not always easy to use, as you have to scroll all over the Google documentation to move on properly (configuring the service account and setting the proper roles took me a while). &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If you looked as the Cloud Build platform you could see it is not yet very user-friendly. This will strike you when dealing with parallel steps, you won't have a nice output to know what steps are sequential and what steps are parallel. Comparing to its current opponents on the market, like CircleCi or TravisCi, Cloud Build has a long way to go. &lt;/li&gt;
&lt;li&gt;The fact that only &lt;code&gt;steps&lt;/code&gt; can be declared does not favor re-usability when creating your pipeline and obfuscate your step intent. Indeed, when dealing with multiples stages and parallel execution, I find my &lt;code&gt;yaml&lt;/code&gt; file less and less clear, as I have to add comments in order to keep a clear view of my steps purposes. I think adding a notion of &lt;code&gt;pipeline&lt;/code&gt;, in which you easily configure your steps execution order can favor readability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  To go further with Cloud Build
&lt;/h2&gt;

&lt;p&gt;You might have realized that Cloud Build does not keep your Docker image in a cache. Which means for each build, you end up downloading your maven repositories. To go further, you could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Extract the project compilation from the multi-stage Dockerfile and build your projects in a Cloud Build step. This way, you could download and upload cached maven dependencies easily in a Cloud Storage bucket, and reduce your build time. (I won't even mention storage fees, as your might be string no more than 1Gb...).&lt;/li&gt;
&lt;li&gt;Use what is called &lt;a href="https://cloud.google.com/cloud-build/docs/kaniko-cache"&gt;&lt;strong&gt;Kaniko cache&lt;/strong&gt;&lt;/a&gt;, where every step of your Dockerfile is cached. This also reduce your build time, but incurs in higher storage fees, as Kaniko stores every Dockerfile steps in a Cloud Storage Bucket.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/cloud-build/docs/speeding-up-builds"&gt;Check this link for a full list of possible optimizations&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I will try to provide short articles for those optimizations later.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Define application security based on Firebase token&lt;/li&gt;
&lt;li&gt;Developing a Frontend application to communicate with the Cloud Run service&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Resources
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/source-repositories"&gt;More on Cloud Source Repositories&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/cloud-build/docs"&gt;More on Cloud Build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/cloud-build/docs/build-config"&gt;More on Cloud Build configuration file&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/iam/docs/granting-changing-revoking-access"&gt;Granting access to an account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/cloud-build/docs/securing-builds/configure-access-for-cloud-build-service-account"&gt;Configuring access to Cloud Build Service account&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/sdk/gcloud/reference"&gt;More gcloud commands&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/cloud-build/docs/automating-builds/create-manage-triggers#gcloud"&gt;To create a trigger using gcloud&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run/docs/configuring/service-accounts"&gt;Using a Service Account for Cloud Run&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/cloud-build/docs/configuring-builds/substitute-variable-values"&gt;For a complete list of Cloud Build substitutions&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googlecloud</category>
      <category>cloudbuild</category>
      <category>cloudrun</category>
    </item>
    <item>
      <title>Deploying your Spring Boot application in Cloud Run</title>
      <dc:creator>RUARO Thibault</dc:creator>
      <pubDate>Tue, 08 Dec 2020 13:23:53 +0000</pubDate>
      <link>https://dev.to/zenika/deploying-your-spring-boot-application-in-cloud-run-59i4</link>
      <guid>https://dev.to/zenika/deploying-your-spring-boot-application-in-cloud-run-59i4</guid>
      <description>&lt;h1&gt;
  
  
  Introduction
&lt;/h1&gt;

&lt;p&gt;Nowadays, it is possible for anyone to start an application using the latest Google Cloud Technologies to deploy an entire application. Modern web application requires most of the time splitting the front, what users see, and the back, what handles user requests.&lt;/p&gt;

&lt;p&gt;In this series of articles, we will see how to deploy step by step a complete application. We will progressively cover topics like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hosting docker container on Cloud Run. Very useful when you have a low budget and enterprise needs.&lt;/li&gt;
&lt;li&gt;Continuous deployment. Using Cloud builds to trigger a build and deploying your last version in your environment.&lt;/li&gt;
&lt;li&gt;Configuring application security of a Spring based application with Firebase Identity. How to handle user authentication using JWT.&lt;/li&gt;
&lt;li&gt;Hosting a Vue application in Firebase. Enjoy Firebase CDN and deploy your SPA to target an international audience easily.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Prerequisites
&lt;/h2&gt;

&lt;h3&gt;
  
  
  A Github Account
&lt;/h3&gt;

&lt;p&gt;If you don't have it, I highly recommend you to create one, right now. I'll be using a public repository hosted on my account. My examples will use my repository in the command, but you will need yours when we will communicate with Cloud Repository. &lt;a href="https://github.com/join?ref_cta=Sign+up&amp;amp;ref_loc=header+logged+out&amp;amp;ref_page=%2F&amp;amp;source=header-home" rel="noopener noreferrer"&gt;Click here to create an account on Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Also, make sure &lt;code&gt;Git&lt;/code&gt; is installed on your machine, and you can execute commands like &lt;code&gt;git clone...&lt;/code&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A Google Cloud Platform Account
&lt;/h3&gt;

&lt;p&gt;The examples will deploy your application in your GCP environment. To do so, you need your own GCP account.&lt;/p&gt;

&lt;p&gt;If you create the account, you will get $300 (270€) credit to use their infrastructure during a year. Please note you will have to use a credit card to create the account on Google. &lt;a href="https://cloud.google.com/free" rel="noopener noreferrer"&gt;Click here to sign up on GCP&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you already have an account, please note things we do together will incur very small fees on your account. If you follow all articles rigorously, you might have no more than 1€ on your billing account at the end of the month. &lt;/p&gt;

&lt;p&gt;Make sure you also create a first project that will contain the resources created in the articles.&lt;/p&gt;

&lt;h4&gt;
  
  
  Install the gcloud CLI
&lt;/h4&gt;

&lt;p&gt;Even if you could use the Cloud Shell, I recommend you using the &lt;code&gt;gcloud&lt;/code&gt; CLI on your machine. Trust me, it is really handy. &lt;a href="https://cloud.google.com/sdk/docs/install" rel="noopener noreferrer"&gt;To install the CLI, click here&lt;/a&gt;&lt;br&gt;
Check the CLI is installed on your machine, by running &lt;code&gt;gcloud version&lt;/code&gt; in your shell.&lt;/p&gt;

&lt;p&gt;Then, execute the following command, to make sure you are pointing to the correct project&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;gcloud config set core/project ${PROJECT_ID}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker installed locally
&lt;/h3&gt;

&lt;p&gt;Docker is not 100% mandatory, but personally I like testing locally if my development or Dockerfile are correct. It shortens the feedback loop, which gives me more flexibility to adapt when things don't work out.&lt;/p&gt;

&lt;p&gt;Therefore, I suggest you to install Docker locally. &lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;Here are the resources to do so&lt;/a&gt; &lt;/p&gt;

&lt;h1&gt;
  
  
  Building the Spring application
&lt;/h1&gt;

&lt;p&gt;You can now start building the first application. We will create a &lt;code&gt;Hello world&lt;/code&gt; application, that exposes an endpoint.&lt;br&gt;
The application can evolve later, but let's keep it simple and build it step by step. Just like software development should be done gradually, deploying your application with short iterations shortens the feedback loop. Detect bugs as soon as they appear to reduce the time needed to fix it. &lt;/p&gt;
&lt;h2&gt;
  
  
  Create the application backend
&lt;/h2&gt;

&lt;p&gt;I invite you to create a skeleton using the &lt;a href="https://start.spring.io" rel="noopener noreferrer"&gt;Spring Initializer website&lt;/a&gt;. Here are the information I use:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Maven project&lt;/li&gt;
&lt;li&gt;Java&lt;/li&gt;
&lt;li&gt;Spring Boot version: 2.4.0&lt;/li&gt;
&lt;li&gt;Group: dev.truaro.blog&lt;/li&gt;
&lt;li&gt;Artifact: gcpcloudrunback&lt;/li&gt;
&lt;li&gt;Name: GCP Cloud Run Backend&lt;/li&gt;
&lt;li&gt;Description: My awesome back office fully managed by Cloud Run&lt;/li&gt;
&lt;li&gt;Package name: dev.truaro.blog.gcpcloudrunback&lt;/li&gt;
&lt;li&gt;Dependencies: Spring Web&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This generates a zip file you can unzip. Having in mind what comes after this article, I recommend you to have the following folder structure:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;* gcpapplication
|--- gcpcloudrunback &amp;lt;- unzipped folder
|--- gcpfirebasefront &amp;lt;- will come in another article
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Please note for the rest of this article, the root folder is &lt;code&gt;gcpapplication/gcpcloudrunback&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's review the important part together:&lt;/p&gt;

&lt;p&gt;In &lt;code&gt;/pom.xml&lt;/code&gt;, make sure you have a dependency to &lt;code&gt;spring-boot-starter-web&lt;/code&gt; and the plugin &lt;code&gt;spring-boot-maven-plugin&lt;/code&gt;. This will activate Spring Boot's autoconfiguration to embed a Tomcat, and enable the Fat JAR generation.&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;dependency&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&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;spring-boot-starter-web&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
...
&lt;span class="nt"&gt;&amp;lt;build&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;plugins&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;plugin&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;org.springframework.boot&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;spring-boot-maven-plugin&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/plugin&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/plugins&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/build&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In &lt;code&gt;src/main/java/dev/truaro/blog/gcpcloudrunback/GcpCloudRunBackendApplication.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;@SpringBootApplication&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;GcpCloudRunBackendApplication&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;static&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;SpringApplication&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;GcpCloudRunBackendApplication&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;args&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;/div&gt;



&lt;p&gt;Classic &lt;code&gt;@SpringBootApplication&lt;/code&gt; class to start Spring and the Application Context.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add your first controller
&lt;/h2&gt;

&lt;p&gt;Let's add a simple controller that says &lt;code&gt;Hello World&lt;/code&gt; on a GET request at &lt;code&gt;/&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Create a file &lt;code&gt;src/main/java/dev/truaro/blog/gcpcloudrunback/HelloWorldController.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;@RestController&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;HelloWorldController&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;

    &lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&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;String&lt;/span&gt; &lt;span class="nf"&gt;helloWorld&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="s"&gt;"Hello World"&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;/div&gt;



&lt;p&gt;Briefly, this class sends a 200 HTTP Response containing the body &lt;code&gt;Hello World&lt;/code&gt; in plain text:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;@RestController&lt;/code&gt; -&amp;gt; the class is returning only data to the clients. This annotation bypasses the view resolver of &lt;code&gt;Spring MVC&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;@GetMapping&lt;/code&gt; -&amp;gt; Listen a GET request on the endpoint &lt;code&gt;/&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Configure the server port
&lt;/h2&gt;

&lt;p&gt;A recommendation of Cloud Run is to enable your application to listen the port provided by the PORT environment variable (&lt;a href="https://cloud.google.com/run/docs/configuring/containers" rel="noopener noreferrer"&gt;for more information, check this link&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;To do so with Spring, just a property to the &lt;code&gt;application.properties&lt;/code&gt; 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;server.port&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;${PORT:8080}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;If you want to set a property based on an environment variable with a default, use this: ${MY_ENV_VARIABLE:my default value}. Here, we get the PORT from the environment, or we fall back to 8080.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When developing an API, a common practice is to prefix all URLs with &lt;code&gt;/api&lt;/code&gt;. With Spring, add a new property in &lt;code&gt;application.properties&lt;/code&gt;.&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;server.servlet.context-path&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/api&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Request the server to see if it works
&lt;/h2&gt;

&lt;p&gt;Make sure your controller can handle requests with a provided PORT by executing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./mvnw &lt;span class="nb"&gt;install
&lt;/span&gt;&lt;span class="nv"&gt;PORT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;8088 java &lt;span class="nt"&gt;-jar&lt;/span&gt; target/gcpcloudrunback-0.0.1-SNAPSHOT.jar
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go check the URL &lt;code&gt;http://localhost:8088/api&lt;/code&gt;, and you should see &lt;code&gt;Hello World&lt;/code&gt; displayed.&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%2Fi%2Fgsk2igesr0ki1mrlw66m.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%2Fi%2Fgsk2igesr0ki1mrlw66m.png" alt="Skeleton started - First step success"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Overview
&lt;/h2&gt;

&lt;p&gt;This step wasn't much, but at least you have the basis to go on when we will deploy this resource into GCP using Cloud Run.&lt;/p&gt;

&lt;h1&gt;
  
  
  Deploying the application into Cloud Run
&lt;/h1&gt;

&lt;p&gt;Before talking about Continuous Deployment, let's first deploy our application manually on Cloud Run.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quick words on Cloud Run
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Cloud Run "Fully managed"&lt;/strong&gt; is a service provided by GCP that allows you to run a container (Docker for instance) on the Google infrastructure. It has many advantages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The cost is very attractive. Indeed, you pay only for the CPU allocated, and the CPU is allocated only when your container receives an HTTP request. In other words, you only pay when your container is being used.&lt;/li&gt;
&lt;li&gt;You run a container, which makes you independent of the runtime environment provided by Google in a case of App engine Standard for instance. Besides, it is easy for you to test your container locally.&lt;/li&gt;
&lt;li&gt;As a container, you can easily host your application elsewhere, in another provider for instance.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;See the resources part for full resources URL on Cloud Run.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Containerize the application
&lt;/h2&gt;

&lt;p&gt;Let's add this Dockerfile at the root of our application. We will use a multistage build, as it is an even more portable solution.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;maven:3.6.3-openjdk-11-slim&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;as&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;builder&lt;/span&gt;

&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pom.xml .&lt;/span&gt;
&lt;span class="c"&gt;# Use this optimization to cache the local dependencies. Works as long as the POM doesn't change&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;mvn dependency:go-offline

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src/ /app/src/&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;mvn package

&lt;span class="c"&gt;# Use AdoptOpenJDK for base image.&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; adoptopenjdk/openjdk11:jre-11.0.8_10-alpine&lt;/span&gt;

&lt;span class="c"&gt;# Copy the jar to the production image from the builder stage.&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=builder /app/target/*.jar /app.jar&lt;/span&gt;

&lt;span class="c"&gt;# Run the web service on container startup.&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["java", "-jar", "/app.jar"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Try building the image locally and then run it to make sure it works as expected&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; gcr.io/truaro-resources/gcp-cloudrun-back:latest &lt;span class="nb"&gt;.&lt;/span&gt;
docker run &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; 8080:8080 gcr.io/truaro-resources/gcp-cloudrun-back:latest
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; 6b7fd5ca6136af33589c100d6d45884c304cdaf2299b9f1416a33dc607db08e2
curl http://localhost:8080/
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Hello World
docker stop 6b7fd5ca6136af33589c100d6d45884c304cdaf2299b9f1416a33dc607db08e2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we need to push it to our repository. Run this 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="c"&gt;# Connect docker to google registry. This put your credentials for Cloud Registry into your Docker configuration to authenticate on GCP&lt;/span&gt;
gcloud auth configure-docker
&lt;span class="c"&gt;# Enable the repository API for your project&lt;/span&gt;
gcloud services &lt;span class="nb"&gt;enable &lt;/span&gt;containerregistry.googleapis.com
&lt;span class="c"&gt;# Push the image to the google registry&lt;/span&gt;
docker push gcr.io/truaro-resources/gcp-cloudrun-back:latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Create the Cloud Run service description file
&lt;/h2&gt;

&lt;p&gt;If you are familiar with &lt;code&gt;kubernetes&lt;/code&gt; you might have seen already the kubernetes description file. The process is similar for Cloud Run. Let's create a &lt;code&gt;/gcp-cloudrun-back.yaml&lt;/code&gt; description file for our Cloud Run service, and go through it step by step.&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;serving.knative.dev/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;gcp-cloudrun-back&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;cloud.googleapis.com/location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;europe-west1&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;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&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;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;autoscaling.knative.dev/maxScale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&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;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcp-cloudrun-back&lt;/span&gt;
      &lt;span class="na"&gt;containerConcurrency&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;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/${PROJECT_ID}/gcp-cloudrun-back:latest&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1000m&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;256Mi&lt;/span&gt;
  &lt;span class="na"&gt;traffic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;percent&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;latestRevision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Please update the &lt;code&gt;${PROJECT_ID}&lt;/code&gt; with your own project ID&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's explain each important part.&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;serving.knative.dev/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;gcp-cloudrun-back&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;cloud.googleapis.com/location&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;europe-west1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;metadata.name&lt;/code&gt;: the name of the service being deployed in Cloud Run. You can then use this name to get your Cloud Run service.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;metadata.labels.cloud.googleapis.com/location&lt;/code&gt;: The region in which you want to deploy your application. I chose europe/west1, but you can choose another one if you want to.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&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;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;autoscaling.knative.dev/maxScale&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;3'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Here we define the maximum number of instances Cloud Run is allowed to generate if your service is handling lots of requests. The limit we set is 3, making sure you won't get a nice surprise at the end of month on your GCP invoice.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;serviceAccountName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcp-cloudrun-back&lt;/span&gt;
    &lt;span class="na"&gt;containerConcurrency&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;timeoutSeconds&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;300&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;serviceAccountName&lt;/code&gt;: It is a good practice to use specific service account in order to respect more easily the Principle of least privilege. Gives this service account only access to what it is allowed.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;containerConcurrency&lt;/code&gt;: The number of request to handle on a single instance before scaling up. 80 is the default value.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;timeoutSeconds&lt;/code&gt;: The time within a response must be returned by your service. Failure to do so will result in a 504 error sent to the client.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&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;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;gcr.io/${PROJECT_ID}/gcp-cloudrun-back:latest&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;image&lt;/code&gt;: The image name the container will execute. As you guessed it, the image needs to be accessible by Cloud Run. We will see later how to add the image to Container registry.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;limits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1000m&lt;/span&gt;
            &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;256Mi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cpu&lt;/code&gt;: We allocate the equivalence of 1 CPU to our service. &lt;a href="https://cloud.google.com/run/docs/configuring/cpu" rel="noopener noreferrer"&gt;Read more here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;memory&lt;/code&gt;: We allocate 256Mi to the container. Please consider this carefully, as your container can run out of memory on production. &lt;a href="https://cloud.google.com/run/docs/configuring/memory-limits" rel="noopener noreferrer"&gt;Read more here&lt;/a&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;traffic&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;percent&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;latestRevision&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;At each revision, the new one takes 100% of the incoming traffic.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Deploy to Cloud Run
&lt;/h2&gt;

&lt;p&gt;Now we are all setup, let's deploy our first revision, and make it public.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enable the Cloud Run API.
&lt;/li&gt;
&lt;/ol&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;run.googleapis.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Create a &lt;strong&gt;service account&lt;/strong&gt; for the Cloud Run service. This ensures the respect of the &lt;strong&gt;Principle of least privilege&lt;/strong&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud iam service-accounts create gcp-cloudrun-back &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--description&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Service account that executes the gcp-cloudrun-back application"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--display-name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"GCP Cloudrun Back service account"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Deploy on Cloud Run (it might take some minutes).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud beta run services replace gcp-cloudrun-back.yaml &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;managed &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;europe-west1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Applying new configuration to Cloud Run service [gcp-cloudrun-back] in project [truaro-resources] region [europe-west1]&lt;br&gt;
 New configuration has been applied to service [gcp-cloudrun-back].&lt;/p&gt;

&lt;p&gt;URL: &lt;strong&gt;&lt;a href="https://gcp-cloudrun-back-a75acdipmq-ew.a.run.app" rel="noopener noreferrer"&gt;https://gcp-cloudrun-back-a75acdipmq-ew.a.run.app&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Allow public access to invoke your service.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gcloud run services add-iam-policy-binding gcp-cloudrun-back &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--platform&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;managed &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--region&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;europe-west1 &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--member&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"allUsers"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--role&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"roles/run.invoker"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Updated IAM policy for service [gcp-cloudrun-back]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Check if the service is responding (the first request could be a bit long because of the startup time).
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl https://gcp-cloudrun-back-a75acdipmq-ew.a.run.app/api/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Hello World&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Check your container logs (&lt;a href="https://cloud.google.com/run/docs/logging" rel="noopener noreferrer"&gt;Follow this&lt;/a&gt;). Here are mine:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;2020-11-16 21:14:01.573 CET . ____ _ __ _ _
2020-11-16 21:14:01.573 CET /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
2020-11-16 21:14:01.573 CET( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
2020-11-16 21:14:01.573 CET \\/ ___)| |_)| | | | | || (_| | ) ) ) )
2020-11-16 21:14:01.573 CET ' |____| .__|_| |_|_| |_\__, | / / / /
2020-11-16 21:14:01.573 CET =========|_|==============|___/=/_/_/_/
2020-11-16 21:14:01.574 CET :: Spring Boot :: (v2.4.0)
2020-11-16 21:14:01.574 CET
2020-11-16 21:14:01.856 CET2020-11-16 20:14:01.853 INFO 1 --- [ main] d.t.b.g.GcpSkeletonApplication : Starting GcpSkeletonApplication v0.0.1-SNAPSHOT using Java 11.0.8 on localhost with PID 1 (/app.jar started by root in /)
2020-11-16 21:14:01.857 CET2020-11-16 20:14:01.857 INFO 1 --- [ main] d.t.b.g.GcpSkeletonApplication : No active profile set, falling back to default profiles: default
2020-11-16 21:14:05.265 CET2020-11-16 20:14:05.265 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2020-11-16 21:14:05.284 CET2020-11-16 20:14:05.284 INFO 1 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-11-16 21:14:05.284 CET2020-11-16 20:14:05.284 INFO 1 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.39]
2020-11-16 21:14:05.473 CET2020-11-16 20:14:05.473 INFO 1 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-11-16 21:14:05.473 CET2020-11-16 20:14:05.473 INFO 1 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 3394 ms
2020-11-16 21:14:06.778 CET2020-11-16 20:14:06.777 INFO 1 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-11-16 21:14:07.391 CET2020-11-16 20:14:07.390 INFO 1 --- [ main] d.t.b.g.GcpSkeletonApplication : Started GcpSkeletonApplication in 7.221 seconds (JVM running for 8.938)
2020-11-16 21:14:07.653 CET2020-11-16 20:14:07.652 INFO 1 --- [nio-8080-exec-9] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring DispatcherServlet 'dispatcherServlet'
2020-11-16 21:14:07.653 CET2020-11-16 20:14:07.652 INFO 1 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet : Initializing Servlet 'dispatcherServlet'
2020-11-16 21:14:07.654 CET2020-11-16 20:14:07.654 INFO 1 --- [nio-8080-exec-9] o.s.web.servlet.DispatcherServlet : Completed initialization in 0 ms
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As you could see, my application took &lt;strong&gt;7.221 seconds to start&lt;/strong&gt;. In some infrastructure, this could be too long... I will leave the startup time optimization for another article.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;In this article, we covered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How to create a Spring Boot application with a single non-protected endpoint&lt;/li&gt;
&lt;li&gt;How to activate Google APIs using &lt;code&gt;gcloud services enable...&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;How to dockerize a Spring Boot application with a multi-stage build&lt;/li&gt;
&lt;li&gt;How to push on Container registry&lt;/li&gt;
&lt;li&gt;How to deploy an image on Cloud Run using a description file&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What's next
&lt;/h2&gt;

&lt;p&gt;In further articles, we will cover subjects like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Configuring a Continuous deployment pipeline using Cloud Build&lt;/li&gt;
&lt;li&gt;Securing your application using Firebase and Spring security&lt;/li&gt;
&lt;li&gt;Developing a static VueJS App that requests our Cloud Run service&lt;/li&gt;
&lt;li&gt;Deploying this application on Firebase Hosting&lt;/li&gt;
&lt;li&gt;Optimizing the Boot time of your application to improve the scalability&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Resources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/truar/blog-resources.git" rel="noopener noreferrer"&gt;See the entire code on Github&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run/docs/choosing-a-platform" rel="noopener noreferrer"&gt;Documentation for Cloud Run&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cloud.google.com/run/pricing#cloudrun-pricing" rel="noopener noreferrer"&gt;Pricing of Cloud Run&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@nieldw/caching-maven-dependencies-in-a-docker-build-dca6ca7ad612" rel="noopener noreferrer"&gt;Caching Maven dependencies in a multistage build&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/commandline/build/" rel="noopener noreferrer"&gt;Docker build command&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.docker.com/engine/reference/commandline/run/" rel="noopener noreferrer"&gt;Docker run command&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>googlecloud</category>
      <category>springboot</category>
      <category>cloudrun</category>
    </item>
  </channel>
</rss>
