# Introduction

This is a community documentation that captures everything [Ray Tsang (@saturnism)](https://twitter.com/saturnism) knows about running Spring Boot microservices/applications on Google Cloud Platform.

Instead of writing individual blogs on each topic, the content is organized and intended to be kept up to date. Feel free to [contribute via GitHub](https://github.com/saturnism/spring-on-gcp-gitbook), and/or [file topic requests](https://github.com/saturnism/spring-on-gcp-gitbook/issues)! If you'd like to discuss more in detail, you can [schedule an office hour](http://saturnism.me/office-hour/).

There is a lot of content on this site. Here are some recommended paths depending on what you are looking for:

{% tabs %}
{% tab title="New to Google Cloud" %}
{% content-ref url="/pages/-M1b3iFFYb\_oUDl6W-Xa" %}
[Google Cloud Platform](/getting-started/google-cloud-platform)
{% endcontent-ref %}

{% content-ref url="/pages/-MDNawI-wgJvu4Ssa02t" %}
[Cloud Shell](/getting-started/cloud-shell)
{% endcontent-ref %}

{% content-ref url="/pages/-M1b55iNDUHjRCAvgycm" %}
[gcloud CLI](/getting-started/gcloud-cli)
{% endcontent-ref %}

{% content-ref url="/pages/-MBuEI33Gmz4dg9tLBZF" %}
[Hello World!](/getting-started/helloworld)
{% endcontent-ref %}
{% endtab %}

{% tab title="Serverless" %}
{% content-ref url="/pages/-MBuEODMafcpnJvDYgKK" %}
[App Engine](/getting-started/helloworld/app-engine)
{% endcontent-ref %}

{% content-ref url="/pages/-MBuERsg0ZxHvo\_jl3O3" %}
[Cloud Run](/getting-started/helloworld/cloud-run)
{% endcontent-ref %}

{% content-ref url="/pages/-MBuEZ2760Lf17zhkj6B" %}
[Cloud Functions](/getting-started/helloworld/cloud-functions)
{% endcontent-ref %}
{% endtab %}

{% tab title="Containers" %}
{% content-ref url="/pages/-MEV1HOrp\_C9aSYfVy6B" %}
[Container Image](/deployment/docker/container-image)
{% endcontent-ref %}

{% content-ref url="/pages/-MEV2OAA78p2pSQ1Uvvp" %}
[Container Awareness](/deployment/docker/container-awareness)
{% endcontent-ref %}

{% content-ref url="/pages/-MBuERsg0ZxHvo\_jl3O3" %}
[Cloud Run](/getting-started/helloworld/cloud-run)
{% endcontent-ref %}

{% content-ref url="/pages/-MDKvYQB-5BPGws0HZew" %}
[Kubernetes](/deployment/kubernetes)
{% endcontent-ref %}
{% endtab %}

{% tab title="App Development" %}
{% content-ref url="/pages/-M1bKdbY4RUN-0foV1rO" %}
[Development Tools](/app-dev/development-tools)
{% endcontent-ref %}

{% content-ref url="/pages/-M3T05Kmk5kgZiSUXuYT" %}
[Spring Cloud GCP](/app-dev/spring-cloud-gcp)
{% endcontent-ref %}

{% content-ref url="/pages/-M1heU31\_bvKza\_-mCF6" %}
[Cloud SQL](/app-dev/cloud-services/databases/cloud-sql)
{% endcontent-ref %}

{% content-ref url="/pages/-M1hKixgeNUWU8zOrOJW" %}
[Cloud Services](/app-dev/cloud-services)
{% endcontent-ref %}
{% endtab %}
{% endtabs %}

{% hint style="info" %}
This is not official Google Cloud documentation. Always refer to [official documentation](https://cloud.google.com) for current pricing, features, limitations, etc.
{% endhint %}


# Google Cloud Platform

Get started on Google Cloud Platform by signing up for a free account and creating a new project to use.

## Sign Up

If you don't already use [Google Cloud Platform](http://cloud.google.com/), you can [get started for free](http://cloud.google.com/freetrial), and receive $300 credit.

## Project

All cloud services and resources (such as virtual machines, network, load balancer, etc) are created under a Google Cloud Platform project.

A project is a billing unit. Any services / resources you create under the project will be charged to the Billing Account associated with the project.

A project is a security boundary. You can assign additional users to access different services / resources within the project.

Projects are usually referred to by Project ID. A Project ID is globally unique.

### New Account

If this is your first time signing up for Google Cloud Platform, it will automatically create a Google Cloud Platform Project.

![Google Cloud Platform console with a default project](/files/-M1bFQRuYNSJBf07ZkyC)

Every project has a Project ID and a Project Number. Project ID is most used. Find the Project ID in **Home**, under **Project info**.

![Project info panel showing the Project ID](/files/-M1bGH7CiX_EKH0tGIFS)

### Existing Account

If you already have an account, use an existing project, or create a new one.

## Identity Access Management

IAM may be one of the hardest concepts to grasp about Google Cloud Platform - but once you understand it, everything else becomes clear.

### Member

All Members (i.e., a user) are identified by an e-mail address:

| Type            | Uses                                                    | Identified By                    |
| --------------- | ------------------------------------------------------- | -------------------------------- |
| User Account    | User interaction with `gcloud` CLI, or the web console. | User's e-mail address            |
| Service Account | Service to Service authentication                       | Service account's e-mail address |
| G Suite Group   | A collection of user accounts or service accounts.      | G Suite Group e-mail             |
| G Suite Domain  | All users and groups of a G Suite domain.               | G Suite domain name              |

Sometimes, when referring to different types of Members, you may need to add a prefix:

| Type            | Prefix         | Example                                                 |
| --------------- | -------------- | ------------------------------------------------------- |
| User Account    | user           | user:<jane@example.com>                                 |
| Service Account | serviceAccount | serviceAccount:<my-service@appspot.gserviceaccount.com> |
| G Suite Group   | group          | group:<webmaster@example.com>                           |
| G Suite Domain  | domain         | domain:example.com                                      |

See [Identity Access Management Overview documentation](https://cloud.google.com/iam/docs/overview) for more details.

### Permission

A Permission is the finest grain of a particular action that a Member can perform. For example, a permission to list objects / files from Cloud Storage is `storage.objects.list`.

### Roles

Each Member can be associated with [different Roles](https://cloud.google.com/iam/docs/understanding-roles), and each Role is associated with a set of [Permissions](/getting-started/google-cloud-platform#permission).  For example, a `roles/storage.objectViewer` role, has the `storage.objects.get` and the `storage.objects.list` permissions.  See [Understanding roles documentation](https://cloud.google.com/iam/docs/understanding-roles) for all the available roles and the associated permissions.

{% hint style="info" %}
You can create [Custom Roles](https://cloud.google.com/iam/docs/understanding-custom-roles) to associate with specific permissions too.
{% endhint %}

### Credential

| Type            | Credential                                                                                                                                                                                                                       |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| User Account    | OAuth credentials - an Access Token, or a Refresh Token, or [Application Default Credentials](/getting-started/google-cloud-platform#application-default-credentials).                                                           |
| Service Account | A [Service Account Key file](https://cloud.google.com/iam/docs/creating-managing-service-account-keys) **or** from [Machine Credentials from Metadata Server](https://cloud.google.com/compute/docs/storing-retrieving-metadata) |

{% hint style="info" %}
A User Account is great for local development when using `gcloud`.  Service Account is great for your application/microservice.
{% endhint %}

#### Application Default Credentials

This is the default credential that a Google Cloud client library will discover. And Application Default Credential can be:

* Created by `gcloud auth application-default login` when running locally,
* **or** a `GOOGLE_APPLICATION_CREDENTIALS` environmental variable that points to the path of a Service [Account key file](/getting-started/google-cloud-platform#service-account-key),
* **or** automatically discovered using the [Metadata Server](/getting-started/google-cloud-platform#machine-credentials).&#x20;

When using a Google Cloud client library to access a Cloud service, the client library will automatically discover the credential to use based on precedence. See [Google Auth Library README](https://github.com/googleapis/google-auth-library-java/blob/master/README.md#application-default-credentials) for more information.

#### Service Account Key

Service Account Key file is a JSON file that contains a private key, and the private key is used to retrieve OAuth access token. The Service Account file is like a password and must be stored securely!

{% hint style="danger" %}
Never expose the service account key file in the public.

Never check-in your service account key file.

Never put your service account key file in a container image, or deployable artifact like a JAR file.
{% endhint %}

{% hint style="success" %}
Always store your service account securely.
{% endhint %}

{% hint style="success" %}
In most cases, your application is associated with a service account, but will **not** need the Service Account key file. See [Machine Credentials](/getting-started/google-cloud-platform#machine-credentials-from-metadata-server).
{% endhint %}

#### Machine Credentials from Metadata Server

All Google Cloud runtime environments (App Engine, Cloud Functions, Cloud Run, Kubernetes Engine, Compute Engine, ...) have access to the [Metadata Server](https://cloud.google.com/compute/docs/storing-retrieving-metadata). From the runtime environment, you can retrieve the current access token associated with the Service Account:

```bash
curl -H "Metadata-Flavor: Google" \
  http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token
```

{% hint style="info" %}
Each runtime / service may be associated with a specific Service Account. For example, VM1 uses Service Account A, and VM2 uses Service Account B. Depending on which VM is used to access the Metadata Server, the Metadata Server will return the token for the associated Service Account.
{% endhint %}


# Cloud Shell

[Cloud Shell](https://cloud.google.com/shell/docs) is an interactive shell environment for Google Cloud Platform that works directly in the browser. Cloud Shell has many commonly used CLI tools preinstalled, including `gcloud`. Almost all the tasks/examples on this site can be done in Cloud Shell, so you don't need to install any CLIs!

## Select a Project

After you logged into the Google Cloud console, make sure you have selected a project in the top bar, by clicking **Select a project**.

![Select a project](/files/-MDNcYmJKsRwdQBeR0EC)

From the **Select a project** dialog, select a Google Cloud Project to use. This will help pre-configure Cloud Shell to use that project as the default project.

## Activate Cloud Shell

On the top right, click the **Activate Cloud Shell** icon.

![Activate Cloud Shell icon](/files/-MDNd7Fno-SAJx_4uURj)

If it's your first time using Cloud Shell, in the introduction dialog, click **Start Cloud Shell** to continue. Wait for the Cloud Shell machine to provision (it may take a few minutes).

Make sure your Cloud Shell is configured with the current project by checking the current Project ID configured for `gcloud`:

```bash
gcloud config get-value project
```

## Home Directory

The Cloud Shell instance is ephemeral, but your home directory will persist and its contents will be carried to future Cloud Shell sessions. Any data/binaries stored outside of the home directory may be lost.

If you want to install any additional binaries, make sure to store them inside your home directory, maybe under a `bin` directory.

```bash
mkdir $HOME/bin
echo 'export PATH="$HOME/bin:$PATH"' >> $HOME/.bashrc
```

## Boost Mode

When working with Java applications and running heavier workloads in Cloud Shell, it'll be useful to enable Boost Mode. In the Cloud Shell's **More** menu, click **Boost Cloud Shell**.

![Boost Cloud Shell](/files/-MDNeVuyEfHyFz8uj7fI)

This will re-provision your Cloud Shell instance and replace the original `e2-small` (0.5 vCPU, 2GB of memory) machine type with a larger `e2-medium` (1 vCPU, 4GB of memory) machine type.

{% hint style="info" %}
See [Compute Engine Machine Types documentation](https://cloud.google.com/compute/docs/machine-types#e2_machine_types) for more details on the machine types.
{% endhint %}

## Multiple Tabs

You can open new Cloud Shell tabs by clicking the **+** icon.

![Open a new tab + icon](/files/-MDNgPCFIkM70N25qYU-)

## Code Editor

Cloud Shell comes with common text editing tools, such as `vi`, `emacs`, `nano`. It also has a built-in web-based text editor. You can open the web-based editor by clicking **Open Editor**.

![Open Editor](/files/-MDNfbFhwg4pzXFqhvKx)

This will launch an embedded editor where you can open and edit text files. You can switch back to the terminals by clicking **Open Terminal**.

![Open Terminal](/files/-MDNg6EcgCsn7ltmi8xM)

## Default Zone and Region

You can specify the default `zone` or `region` with a `gcloud` command. If you primarily operate within a single zone or region, set the default `zone` and default `region`.

```bash
gcloud config set compute/region us-central1
gcloud config set compute/zone us-central1-c
gcloud config set run/region us-central1
```

{% hint style="info" %}
See the complete list in [Regions and Zones documentation](https://cloud.google.com/compute/docs/regions-zones).
{% endhint %}


# gcloud CLI

Install gcloud command to interact with Google Cloud Platform from the command line.

## Cloud Shell

Cloud Shell already has the `gcloud` CLI pre-installed, so you can **skip ahead to** [**configure default zone and region**](/getting-started/gcloud-cli#default-zone-and-region).

{% hint style="danger" %}
Jump to the last section to [configure default zone and region](/getting-started/gcloud-cli#default-zone-and-region) so you do not need to repeatedly specify it.
{% endhint %}

## Local Installation

To install the `gcloud` CLI on your local machine, follow the [official installation guide](https://cloud.google.com/sdk/docs/downloads-interactive) for your platform and then follow the below steps to finish configuration.

{% hint style="danger" %}
If you are using Cloud Shell, jump to the last section to [configure default zone and region](/getting-started/gcloud-cli#default-zone-and-region) so you do not need to repeatedly specify it.
{% endhint %}

### Authenticate

Authenticate gcloud so that it can interact with Google Cloud Platform using your account.

```bash
gcloud auth login
```

{% hint style="info" %}
This authenticated `gcloud` so that you can run all the commands.
{% endhint %}

### Project ID

Set the default Project ID to your project.

```bash
gcloud config set project YOUR_PROJECT_ID
```

{% hint style="info" %}
If you already have a project, run `gcloud projects list` to list available projects. Find one and then set it as a default project.
{% endhint %}

### Application Default Credentials

In addition to authenticating gcloud, also authenticate Application Default Credentials (ADC). ADC is used by your application/microservices during local development to authenticate with cloud services.

```bash
gcloud auth application-default login
```

### Quota Project

API calls to Google Cloud may be rate limited and subject to quotas. The quotas are typically tied to a Google Cloud Project. When you are running the application locally, and using the Application Default Credentials, the requests need to be associated with a project to account for usage quota. Typically, the Quota Project should be the same as the project that you are currently working with. In a larger organization, it can be a Project that's used for development purposes and not the production project.

When configuring the Application Default Credentials the first time, It will also configure a Quota Project to be the same as the default project you previously configured.

If needed, you can configure a different Quota Project:

```bash
gcloud auth application-default set-quota-project YOUR_PROJECT_ID
```

{% hint style="info" %}
Application Default Credentials are used by client libraries when making calls to Google Cloud. This is different from the [first gcloud Authenticate](/getting-started/gcloud-cli#authenticate), which is for `gcloud` to make calls to Google Cloud.
{% endhint %}

This will store the credential (OAuth refresh token) in a well-known location, such as `~/.config/gcloud/application_default_credentials.json`. Google Cloud client libraries can automatically detect this file and use this credential.

### Default Zone and Region

A cloud resource can be Zonal, Regional, or Multi-Regional. For example, a VM is Zonal, because it can only live in a single availability zone. App Engine service is regional, because it's automatically distributed across multiple zones within a single Region. Cloud Storage can store your data in a Regional bucket, or a Multi-Regional bucket.

You can always specify the `zone` or `region` with each of the `gcloud` command. If you primarily operate within a single zone or region, set the default `zone` and default `region`.

```bash
gcloud config set compute/region us-central1
gcloud config set compute/zone us-central1-c
gcloud config set run/region us-central1
```

{% hint style="info" %}
See the complete list in [Regions and Zones documentation](https://cloud.google.com/compute/docs/regions-zones).
{% endhint %}


# Hello World!

Google Cloud Platform has a range of different runtime environments to run your Java / Spring Boot application.


# Cloud Shell

Use Cloud Shell to build and test applications for development purpose.

You can run and test a an application directly within Cloud Shell. Cloud Shell has many tools pre-installed, such OpenJDK, Maven, Gradle, and more. Cloud Shell is meant for development and not meant for production or long running processes.

## Getting Started

### Clone

```bash
cd $HOME
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

### Build

```bash
./mvnw package
```

### Run

{% tabs %}
{% tab title="Plugin" %}

```bash
./mvnw spring-boot:run
```

{% endtab %}

{% tab title="JAR" %}

```bash
java -jar target/helloworld.jar
```

{% endtab %}
{% endtabs %}

### Connect

From Cloud Shell, click **Web Preview**, then click **Preview on port 8080.**

![Web Preview](/files/-MEYivaxAseYveaxV1w9)


# App Engine

Deploy a JAR to a fully managed PaaS with just one command.

[App Engine](https://cloud.google.com/appengine/docs/standard/java11) is a fully managed Platform-as-a-Service that can run your application, provision a HTTPS load balancer, and scale out your workload as needed. When no one is using your application, it can scale down to zero.

{% embed url="<https://www.youtube.com/watch?v=qx_T6-EKkBE>" %}

## Getting Started

### Clone

```bash
cd $HOME
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

### Build

```bash
./mvnw package
```

### Deploy

```bash
gcloud app deploy target/helloworld.jar
```

{% hint style="info" %}
If this is your first time using App Engine on the project, you'll be prompted to choose a region. Pick the region that's most suitable for your application. This site mostly uses `us-central` as an example.
{% endhint %}

{% hint style="warning" %}
Once you select the region, you cannot change it for an App Engine application.
{% endhint %}

### Connect

Once deployed, the command will output the HTTPs URL. To open the URL in your browser:

```bash
gcloud app browse
```

To find the URL without opening the browser:

```bash
gcloud app browse --no-launch-browser
```

You can `curl` the URL:

```bash
URL=$(gcloud app browse --no-launch-browser)
curl ${URL}
```

{% hint style="info" %}
You can run any Java service in App Engine as long as it's packaged as a JAR file, and can be executed with `java -jar app.jar`.
{% endhint %}

## Additional Configuration

By default, App Engine will deploy with the smallest `F1`instance class. You can specify a larger instance, configure environment variables, and more tuning parameters using an `app.yaml`:

{% code title="app.yaml" %}

```
runtime: java11
instance_class: F4
env_variables:
  SPRING_PROFILES_ACTIVE: "prod"
```

{% endcode %}

{% hint style="info" %}
See [App Engine Standard Instance Classes documentation](https://cloud.google.com/appengine/docs/standard#instance_classes) for a list of Instance Classes and associated CPU/Memory resources.
{% endhint %}

Deploy the JAR file with the configuration:

```bash
gcloud app deploy target/helloworld.jar \
  --appyaml app.yaml
```

{% hint style="info" %}
Learn more about the configurations in [app.yaml reference documentation](https://cloud.google.com/appengine/docs/standard/java11/config/appref).
{% endhint %}

## Learn More

* [App Engine Java 11 documentation](https://cloud.google.com/appengine/docs/standard/java11)
* [Deploy with App Engine Maven plugin](https://cloud.google.com/appengine/docs/standard/java11/using-maven#setting_up_maven)
* [Deploy with App Engine Gradle plugin](https://cloud.google.com/appengine/docs/standard/java11/using-gradle)


# Cloud Run

Deploy a container to serverless environment using a single command.

[Cloud Run](https://cloud.google.com/run/docs) is a fully managed container runtime environment, where you can deploy any HTTP serving container, and Cloud Run will automatically scale out the number of instances as needed, and scale down to zero when no one is using it.

## Getting Started - Click to Deploy

You can deploy a [Hello World Application](https://github.com/saturnism/jvm-helloworld-by-example/tree/master/helloworld-springboot-tomcat) simply by click on the **Run on Google Cloud** button below!

[![Deploy a Spring Boot app on Cloud Run](https://deploy.cloud.run/button.svg)](https://deploy.cloud.run/?git_repo=https://github.com/saturnism/jvm-helloworld-by-example.git\&dir=helloworld-springboot-tomcat)

## Getting Started - Manual Deployment

### Clone

```bash
cd $HOME
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

### Build

```bash
./mvnw package
```

### Containerize

#### Enable API

Enable the Container Registry API so that you can push container images to [Container Registry](https://cloud.google.com/container-registry).

```bash
gcloud services enable containerregistry.googleapis.com
```

#### Jib

Use Jib to containerize the application:

```bash
PROJECT_ID=$(gcloud config get-value project)

./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

{% hint style="info" %}
Learn different ways to containerize a Java application in the [Container Image](/deployment/docker/container-image) section.
{% endhint %}

### Deploy

#### Enable API

```bash
# To use Cloud Run
gcloud services enable run.googleapis.com
```

#### Deploy Container

```bash
PROJECT_ID=$(gcloud config get-value project)

gcloud run deploy helloworld \
  --region=us-central1 \
  --platform=managed \
  --allow-unauthenticated \
  --image=gcr.io/${PROJECT_ID}/helloworld
```

### Connect

Once deployed, Cloud Run will display the HTTPs URL. You can also find the URL with the command line:

```
gcloud run services describe helloworld \
  --region=us-central1 \
  --platform=managed
```

You can `curl` the URL:

```bash
URL=$(gcloud run services describe helloworld \
  --region=us-central1 \
  --platform=managed \
  --format='value(status.address.url)')

curl ${URL}
```

## Additional Configurations

By default, Cloud Run will deploy with the smallest 1CPU 256MB instance. You can specify a larger instance, and configure environment variables with the `gcloud` CLI:

```bash
PROJECT_ID=$(gcloud config get-value project)

gcloud run deploy helloworld --platform=managed --allow-unauthenticated \
  --cpu=2 --memory=512M --set-env-vars="SPRING_PROFILES_ACTIVE=prod" \
  --image=gcr.io/${PROJECT_ID}/helloworld
```

## Learn More

* [Optimizing Java Applications on Cloud Run](https://cloud.google.com/run/docs/tips/java)


# Kubernetes Engine

Create a Kubernetes cluster and deploy a container.

[Kubernetes Engine](https://cloud.google.com/kubernetes-engine/docs) is a secured and managed Kubernetes service so you can deploy containerized application in an enterprise/production-grade Kubernetes cluster with a click of a button.&#x20;

## Getting Started

### Clone

```bash
cd $HOME
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

### Build

```bash
./mvnw package
```

### Containerize

#### Enable API

Enable the Container Registry API so that you can push container images to [Container Registry](https://cloud.google.com/container-registry).

```bash
gcloud services enable containerregistry.googleapis.com
```

#### Jib

Use Jib to containerize the application:

```bash
PROJECT_ID=$(gcloud config get-value project)

./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

{% hint style="info" %}
Learn different ways to containerize a Java application in the [Container Image](/deployment/docker/container-image) section.
{% endhint %}

### Create Cluster

#### Enable API

```bash
gcloud services enable compute.googleapis.com
gcloud services enable container.googleapis.com
```

#### Create Cluster

Create a [VPC-native Kubernetes Engine cluster](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips).

```bash
gcloud container clusters create helloworld-cluster \
  --num-nodes 2 \
  --enable-ip-alias \
  --scopes=cloud-platform \
  --network=default \
  --machine-type n1-standard-1
```

{% hint style="info" %}
See [Compute Engine Machine Types documentation](https://cloud.google.com/compute/docs/machine-types) for a list of Machine Types and the associated CPU/Memory resources.
{% endhint %}

#### Cluster Credentials

Kubernetes credentials are automatically retrieved and stored in your `$HOME/.kube/config` file. If you need to re-retrieve the credentials:

```bash
gcloud container clusters get-credentials helloworld-cluster
```

### Deploy

```bash
PROJECT_ID=$(gcloud config get-value project)

kubectl create deployment helloworld \
  --image=gcr.io/${PROJECT_ID}/helloworld
```

Check that the container is deployed:

```bash
kubectl get pods
```

### Expose

You can expose this one service using a single [Network (L4) Load Balancer](https://cloud.google.com/load-balancing/docs/network):

```bash
kubectl create service loadbalancer helloworld --tcp=8080:8080
```

{% hint style="info" %}
A Network (L4) Load Balancer is the easiest way to expose a single service for a demo. For production environment, you likely will need to [use a HTTP Load Balancer](https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing) instead.
{% endhint %}

### Connect

Find the Load Balancer's External IP address:

```bash
kubectl get services helloworld
```

Initially, it may display that the External IP is `<pending>`.

```bash
NAME         TYPE           CLUSTER-IP   EXTERNAL-IP   PORT(S)          AGE
helloworld   LoadBalancer   ...          <pending>     8080:32414/TCP   ...
```

Re-check until the External IP is assigned.

Then connect with `curl`:

```bash
EXTERNAL_IP=$(kubectl get svc helloworld \
  -ojsonpath='{.status.loadBalancer.ingress[0].ip}')

curl http://${EXTERNAL_IP}:8080
```

## Learn More

* [Kubernetes from Basic to Advanced code lab](https://bit.ly/k8s-lab)
* [Spring Boot on GCP code lab](https://bit.ly/spring-gcp-lab)
* [Spring to Kubernetes Faster and Easier](https://saturnism.me/talk/kubernetes-spring-java-best-practices/)


# Compute Engine

Create a VM then deploy your application to the VM.

## Getting Started

### Clone

```bash
cd $HOME
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

### Build

```bash
./mvnw package
```

### Create a VM

#### Enable API

```bash
gcloud services enable compute.googleapis.com
```

#### Create a VM

```bash
gcloud compute instances create helloworld \
  --scopes=cloud-platform
```

{% hint style="info" %}
If you want to use a specific distribution, such as Debian 10, you can add additional parameters:

```bash
gcloud compute instances create helloworld \
  --image-family debian-10 --image-project debian-cloud
```

{% endhint %}

{% hint style="info" %}
See [Compute Engine Machine Types documentation](https://cloud.google.com/compute/docs/machine-types) for a list of Machine Types and the associated CPU/Memory resources.
{% endhint %}

### Copy File to VM

```bash
gcloud compute scp target/helloworld.jar helloworld:
```

{% hint style="info" %}
If this is your first time connecting to the VM, it will automatically prompt you to generate a new SSH key.
{% endhint %}

### SSH to VM

```bash
gcloud compute ssh helloworld
```

### Install OpenJDK in the VM

```bash
sudo apt-get update && sudo apt-get install -y openjdk-11-jdk
```

### Run in the VM

```bash
java -jar helloworld.jar
```

### Expose

#### Firewall

By default, most ports on the Compute Engine are firewalled off. If you want to expose port `8080` in this case, you can first add a `tag` to the Compute Engine instance, and then add a firewall rule to allow inbound port `8080` traffic for any Compute Engine instance with a certain tag.

From outside of the VM (e.g., your computer, or Cloud Shell):

#### Add Tag

```bash
gcloud compute instances add-tags helloworld --tags=webapp
```

#### Add Firewall Rule

```bash
gcloud compute firewall-rules create webapp-rule \
  --source-ranges=0.0.0.0/0 \
  --target-tags=webapp \
  --allow=tcp:8080
```

### Connect

Find the external IP address of the Compute Engine VM instance:

```bash
gcloud compute instances list
```

You can now connect to the external IP on port `8080` of the application:

```bash
EXTERNAL_IP=$(gcloud compute instances describe helloworld \
  --format='value(networkInterfaces.accessConfigs[0].natIP)')

curl http://${EXTERNAL_IP}:8080
```

{% hint style="info" %}
In production environments, you would most likely want to put a Load Balancer in front, either with a [Network (L4) Load Balancer](https://cloud.google.com/load-balancing/docs/network/setting-up-network), or a [HTTP (L7) Load Balancer](https://cloud.google.com/load-balancing/docs/https/ext-http-lb-simple).
{% endhint %}

## Getting Started - Container in Compute Engine

### Clone

```
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

### Build

```
./mvnw package
```

### Containerize

#### Enable API

Enable Container Registry API to be able to push container images to the Container Registry.

```bash
gcloud services enable containerregistry.googleapis.com
```

#### Jib

Use Jib to containerize the application:

```bash
PROJECT_ID=$(gcloud config get-value project)

./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

{% hint style="info" %}
Learn different ways to containerize a Java application in the [Container Image](/deployment/docker/container-image) section.
{% endhint %}

### Create a VM with Container Image

#### Enable API

```bash
gcloud services enable compute.googleapis.com
```

#### Create a VM

```bash
PROJECT_ID=$(gcloud config get-value project)
gcloud compute instances create-with-container \
  helloworld-with-container \
  --container-image=gcr.io/${PROJECT_ID}/helloworld \
  --scopes=cloud-platform
```

{% hint style="info" %}
This will automatically create a Container-Optimized VM, and start the container on VM startup.
{% endhint %}

### Expose

#### Firewall

By default, most ports on a Compute Engine instance are firewalled off. If you want to expose port `8080` in this case, you can first add a `tag` to the Compute Engine instance, and then add a firewall rule to allow inbound port `8080` traffic for any Compute Engine instance with a certain tag.

Add a tag:

```bash
gcloud compute instances add-tags \
  helloworld-with-container --tags=webapp
```

Add Firewall rule:

```bash
gcloud compute firewall-rules create webapp-rule \
  --source-ranges=0.0.0.0/0 \
  --target-tags=webapp \
  --allow=tcp:8080
```

### Connect

Find the external IP address of the Compute Engine VM instance:

```bash
gcloud compute instances list
```

You can now connect to the external IP on port `8080` of the application:

```bash
EXTERNAL_IP=$(gcloud compute instances describe helloworld-with-container \
  --format='value(networkInterfaces.accessConfigs[0].natIP)')
curl http://${EXTERNAL_IP}:8080
```

{% hint style="info" %}
In production environments, you would most likely want to put a Load Balancer in front, either with a [Network (L4) Load Balancer](https://cloud.google.com/load-balancing/docs/network/setting-up-network), or a [HTTP (L7) Load Balancer](https://cloud.google.com/load-balancing/docs/https/ext-http-lb-simple).
{% endhint %}

{% hint style="info" %}
To deploy a fleet of VMs, you can use [Managed Instance Group](https://cloud.google.com/compute/docs/containers/deploying-containers#managedinstancegroupcontainer) to deploy a set of VMs running the same container image.
{% endhint %}


# Cloud Functions

Deploy a simple HTTP function.

[Cloud Function](https://cloud.google.com/functions/docs/) is a scalable, pay as you go, Functions-as-a-Service (FaaS).

Spring Cloud Functions has pre-GA support for Cloud Functions for Java 11. See [Spring Cloud Functions Reference Documentation](https://docs.spring.io/spring-cloud-function/docs/current/reference/html/gcp.html) for more details.

This guide currently uses a non-Spring example for Cloud Functions.

{% embed url="<https://www.youtube.com/watch?v=UsYRKkibLPI>" %}

## Getting Started

### Clone

```bash
cd $HOME
git clone https://github.com/GoogleCloudPlatform/java-docs-samples
cd java-docs-samples/functions/helloworld/helloworld
```

### Build

```bash
mvn package
```

### Run Locally

```bash
mvn function:run

# In a different tab, trigger the function:
curl localhost:8080
```

### Deploy

#### Enable API

```bash
gcloud services enable cloudfunctions.googleapis.com
```

#### Deploy

```bash
gcloud functions deploy helloworld --trigger-http \
  --runtime=java11 \
  --entry-point=functions.HelloWorld \
  --allow-unauthenticated
```

### Connect

Once a HTTP function is deployed, you can connect to it using `curl`. You can also find the URL:

```bash
gcloud functions describe helloworld --format='value(httpsTrigger.url)'
```

Trigger the function with `curl`:

```bash
URL=$(gcloud functions describe helloworld --format='value(httpsTrigger.url)')

curl ${URL}
```

Alternatively, you can also use `gcloud`:

```bash
gcloud functions call helloworld
```

## Additional Configurations

By default, Cloud Functions will deploy to the smallest 256MB instance. You can specify a larger instance and configure environment variables with the `gcloud` CLI:

```bash
gcloud functions deploy helloworld --trigger-http \
  --runtime=java11 \
  --memory=512M
  --entry-point=functions.HelloWorld \
  --allow-unauthenticated
```

## Learn More

* [Cloud Functions Java Runtime documentation](https://cloud.google.com/functions/docs/concepts/java-runtime)
* [Framework support](https://cloud.google.com/functions/docs/concepts/java-frameworks) for [Spring Cloud Functions](https://cloud.spring.io/spring-cloud-static/spring-cloud-function/3.0.7.RELEASE/reference/html/gcp.html), [Micronaut](https://micronaut-projects.github.io/micronaut-gcp/2.0.x/guide/#cloudFunction), [Quarkus](https://quarkus.io/guides/gcp-functions)


# Development Tools

Overview of application development tools for Java developers with Google Cloud Platform. Google Cloud Platform has a range of tools to span across all application development lifecycle.

A general overview of all the tools available for Java developers so that you are aware of the breadth and depth of what's available. You do not need to install any of this at the movement, or only install what you need.

## IDE

|                          | IntelliJ                                                                  | VS Code                                                            | Eclipse                                                     |
| ------------------------ | ------------------------------------------------------------------------- | ------------------------------------------------------------------ | ----------------------------------------------------------- |
| **Plugin**               | [Cloud Code](https://cloud.google.com/code/docs/intellij/quickstart-IDEA) | [Cloud Code](https://cloud.google.com/code/docs/vscode/quickstart) | [Google Cloud Tools](https://cloud.google.com/eclipse/docs) |
| **App Engine Support**   | Yes                                                                       | Yes                                                                | Yes                                                         |
| **Kubernetes Support**   | Yes                                                                       | Yes                                                                | No                                                          |
| **Add Client Libraries** | Yes                                                                       | Yes                                                                | No                                                          |

## Maven / Gradle Plugins

|                            | Maven                                                                                             | Gradle                                                                                         |
| -------------------------- | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- |
| **App Engine Support**     | [appengine-maven-plugin](https://cloud.google.com/appengine/docs/standard/java/tools/using-maven) | [appengine-gradle-plugin](https://cloud.google.com/appengine/docs/standard/java/tools/gradle)  |
| **Cloud Function Support** | [function-maven-plugin](https://github.com/GoogleCloudPlatform/functions-framework-java)          | N/A                                                                                            |
| **Containerize with Jib**  | [jib-maven-plugin](https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin)      | [jib-gradle-plugin](https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin) |

## Framework Support

### Spring Boot

[Spring Cloud GCP](https://spring.io/projects/spring-cloud-gcp) provides 10+ integrations with Spring Boot across Spring Data, Spring Integration, Spring Cloud Streams, and more to provide idiomatic access databases, Cloud Trace, and Cloud Logging.

### Micronaut

[Micronaut GCP](https://micronaut-projects.github.io/micronaut-gcp/latest/guide/index.html) provides integration with GCP services.

| Concerns                | GCP Service | Micronaut Abstraction |
| ----------------------- | ----------- | --------------------- |
| **Distributed Tracing** | Cloud Trace | Zipkin / Brave        |

### Hibernate

Use [Hibernate Cloud Spanner Dialect](https://cloud.google.com/spanner/docs/use-hibernate) to continue use Hibernate / JPA to use Cloud Spanner in your application.

### R2DBC

Use R2DBC to access database to produce highly concurrent non-blocking microservices.&#x20;

| Database                             | R2DBC Support                                                                                                                              |
| ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ |
| **Cloud Spanner**                    | [cloud-spanner-r2dbc](https://github.com/GoogleCloudPlatform/cloud-spanner-r2dbc)                                                          |
| **Cloud SQL - PostgreSQL**           | [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) with [Cloud SQL Proxy](https://cloud.google.com/sql/docs/postgres/sql-proxy) |
| **Cloud SQL - MySQL**                | [r2dbc-mysql](https://github.com/mirromutth/r2dbc-mysql) with [Cloud SQL Proxy](https://cloud.google.com/sql/docs/mysql/sql-proxy)         |
| **Cloud SQL - Microsoft SQL Server** | [r2dbc-mssql](https://github.com/r2dbc/r2dbc-mssql) with [Cloud SQL Proxy](https://cloud.google.com/sql/docs/sqlserver/sql-proxy)          |

## DevOps Tools

### Cloud Build

Declare your CI/CD pipeline and run it with Cloud Build. See [Cloud Build with Java application documentation](https://cloud.google.com/cloud-build/docs/building/build-java).

### Artifact Registry

Publish Java artifacts to Artifact Registry, which can host Maven repositories.  See [Artifact Repository](/app-dev/devops/artifact-repository) section.

### Cloud Trace

Use Spring Cloud Sleuth and send Distributed Tracing data to Cloud Trace, using Spring Cloud GCP.  See [Trace section](/app-dev/observability/trace).

### Cloud Logging

Aggregate logs into a centralized logging console to easily search and view logs. See [Logging section](/app-dev/observability/logging).

### Error Reporting

Automatically identifies Java exceptions and produce reports. Easily see new exceptions and their frequencies. See [Logging section](/app-dev/observability/logging#error-reporting).

### Cloud Monitoring

Collect system and application metrics, build dashboards, and setup alerts.  See [Metrics section](/app-dev/observability/metrics).

### Cloud Debugger

Cloud Debugger can debug your production application without halting the application. Cloud Debugger can capture application state as a Snapshot, and also able to add additional log messages without redeploying the code. See [Cloud Debugger section](/app-dev/observability/debugging).

### Cloud Profiler

Cloud Profiler can continuously profile CPU and heap usages in a production application with minimal overhead.  The profiled flame graph can help you understand performance hotspots. See [Cloud Profiler section](/app-dev/observability/profiling).


# Spring Cloud GCP

Spring Cloud GCP contains a set of easy to use starters/autoconfigurations that allow you to easily connect and adopt GCP services.

Spring Cloud GCP is part of the Spring Cloud release train.

{% hint style="info" %}
Even though Spring Cloud GCP is part of the Spring Cloud release train, it doesn't mean that you need to use any Spring Cloud features (Eureka, Config Server, etc.). The release train helps manage dependency versions so that you don't need to specify versions. It avoids having incompatible dependency versions, and eliminates dependency conflicts.
{% endhint %}

## Demo

{% embed url="<https://www.youtube.com/watch?v=5d_dy7RVcpE>" %}
A short demo video of Spring Cloud GCP
{% endembed %}

## Configure Spring Boot Project

### New Spring Boot Project

Create a new Spring Boot project use [Spring Initializr](https://start.spring.io/#!type=maven-project\&language=java\&platformVersion=2.2.6.RELEASE\&packaging=jar\&jvmVersion=1.8\&groupId=com.example\&artifactId=demo\&name=demo\&description=Demo%20project%20for%20Spring%20Boot\&packageName=com.example.demo\&dependencies=cloud-gcp,web), and add the [GCP Support dependency](https://start.spring.io/#!type=maven-project\&language=java\&platformVersion=2.2.6.RELEASE\&packaging=jar\&jvmVersion=1.8\&groupId=com.example\&artifactId=demo\&name=demo\&description=Demo%20project%20for%20Spring%20Boot\&packageName=com.example.demo\&dependencies=cloud-gcp,web).

![Add the GCP Support dependency](/files/-M3T1rj4oULn2bxnk39Q)

Or, generate the project using `curl`:

```bash
curl https://start.spring.io/starter.zip \
  -d dependencies=web,cloud-gcp,lombok \
  -d bootVersion=2.3.1.RELEASE \
  -d baseDir=demo
```

Unpack the downloaded zip file:

```bash
unzip demo.zip
```

The generated project will include Spring Cloud release train BOM.&#x20;

### Existing Spring Boot Project

If you want to add Spring Cloud GCP to existing project, simply add Spring Cloud release train configuration. Different Spring Boot version is compatible with different Spring Cloud releases.

| Spring Boot Version | Spring Cloud Version |
| ------------------- | -------------------- |
| 2.1                 | Greenwich            |
| 2.2                 | Hoxton               |
| 2.3                 | Hoxton.SR5           |

See [Spring Cloud documentation](https://spring.io/projects/spring-cloud#learn) for the latest versions.

Add the compatible Spring Cloud BOM version to your project:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Hoxton.SR5</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
buildscript {
  dependencies {
    classpath "io.spring.gradle:dependency-management-plugin:1.0.2.RELEASE"
  }
}

apply plugin: "io.spring.dependency-management"

dependencyManagement {
  imports {
    mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Hoxton.RELEASE'
  }
}
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
A BOM is a Bill of Material, when imported, you can specify dependencies managed by the BOM without explicitly specifying a version for that dependency.  Using the Spring Cloud BOM will allow you to use all Google Cloud Client Libraries and Spring Cloud GCP libraries without explicitly specifying a version.
{% endhint %}

## Services

Spring Cloud GCP supports many GCP services using de-facto Spring abstraction layers.

| Concerns                | GCP Service                | Spring Abstraction             |
| ----------------------- | -------------------------- | ------------------------------ |
| **Databases**           | Cloud SQL                  | JDBC template                  |
|                         |                            | Spring Data JPA                |
|                         | Cloud Spanner              | Spring Data Spanner            |
|                         |                            | Spring Data JPA with Hibernate |
|                         | Cloud Datastore            | Spring Data Datastore          |
|                         | Cloud Firestore            | Spring Reactive Data Firestore |
| **Messaging**           | Cloud Pub/Sub              | Pub/Sub Template               |
|                         |                            | Spring Integration             |
|                         |                            | Spring Cloud Stream            |
|                         |                            | Spring Dataflow                |
| **Configuration**       | Cloud Secret Manager       | Spring Cloud Config            |
| **Storage**             | Cloud Storage              | Spring Resource                |
| **Cache**               | Cloud Memorystore          | Spring Data Redis              |
| **Distributed Tracing** | Cloud Trace                | Spring Cloud Sleuth            |
|                         |                            | Zipkin / Brave                 |
| **Centralized Logging** | Cloud Logging              | SLF4J / Logback                |
| **Monitoring Metrics**  | Cloud Monitoring           | Micrometer / Prometheus        |
| **Security**            | Cloud Identity Aware Proxy | Spring Security                |


# Cloud Services


# Databases

Google Cloud Platform offers a number of different fully managed database services from RDBMS to NoSQL for different use cases.

| Service                                                              | Description                                                                                          | Use Case                                                                                                                                                                                      |
| -------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Cloud SQL](/app-dev/cloud-services/databases/cloud-sql)             | Managed, highly available RDBMS with MySQL, PostgreSQL, and SQL Server.                              | Traditional RDBMS, enterprise/business applications, web applications.                                                                                                                        |
| [Cloud Spanner](/app-dev/cloud-services/databases/cloud-spanner)     | Horizontally scalable, globally/regionally distributed, highly available, strongly consistent RDBMS. | Mission critical applications that requires 99.999% SLA. Regionally or globally available applications that needs fast data access. Large data sizes that no longer fit in traditional RDBMS. |
| [Cloud Firestore](/app-dev/cloud-services/databases/cloud-firestore) | Managed NoSQL Document-oriented database.                                                            | Applications with data structure that's fast changing, or document oriented.                                                                                                                  |


# Cloud SQL

[Cloud SQL](https://cloud.google.com/sql/) is managed MySQL, PostgreSQL, and SQL Server. Cloud SQL automates backups, replication, and failover to ensure your database is reliable, highly available.

Cloud SQL has automatic data encryption at rest and in transit. Private connectivity with Virtual Private Cloud (VPC) and user-controlled network access that includes firewall protection. Compliant with SSAE 16, ISO 27001, PCI DSS v3.0, and HIPAA

## Cloud SQL Instance

### Enable API

```bash
gcloud services enable sqladmin.googleapis.com
```

### Create an Instance

{% tabs %}
{% tab title="MySQL" %}
Create a new Cloud SQL - MySQL Instance.

```bash
gcloud sql instances create mysql-instance \
  --database-version=MYSQL_5_7 \
  --region=us-central1 \
  --cpu=2 \
  --memory=4G \
  --root-password=[CHOOSE A PASSWORD]
```

{% endtab %}

{% tab title="PostgreSQL" %}
Create a new Cloud SQL - PostgreSQL instance.

```bash
gcloud sql instances create postgresql-instance \
  --database-version=POSTGRES_11 \
  --region=us-central1 \
  --cpu=2 \
  --memory=4G \
  --root-password=[CHOOSE A PASSWORD]
```

{% endtab %}

{% tab title="SQL Server" %}
Create a new Cloud SQL - SQL Server instance.

```bash
gcloud beta sql instances create sqlserver-instance \
  --database-version=SQLSERVER_2017_STANDARD \
  --region=us-central1 \
  --cpu=2 \
  --memory=4G \
  --root-password=[CHOOSE A PASSWORD]
```

{% endtab %}
{% endtabs %}

### Create a Database

{% tabs %}
{% tab title="MySQL" %}
Create a new database inside of the MySQL database instance.

```bash
gcloud sql databases create orders --instance=mysql-instance
```

{% endtab %}

{% tab title="PostgreSQL" %}
Create a new database inside of the PostgreSQL database instance.

```bash
gcloud sql databases create orders --instance=postgresql-instance
```

{% endtab %}

{% tab title="SQL Server" %}
Create a new database inside of the SQL Server database instance.

```bash
gcloud sql databases create orders --instance=sqlserver-instance
```

{% endtab %}
{% endtabs %}

### Connect to Database instance

By default, every database instance has a public IP address. However, the instance is not publicly accessible because it's protected by the firewall.

To easily connect to the database instance from command line:

{% tabs %}
{% tab title="MySQL" %}
{% hint style="warning" %}
You need the [MySQL client](https://dev.mysql.com/doc/mysql-getting-started/en/) installed locally first, so that you can use `mysql` to connect to any MySQL server.
{% endhint %}

Connect to the MySQL instance using `gcloud` CLI.

```bash
gcloud sql connect mysql-instance
```

{% endtab %}

{% tab title="PostgreSQL" %}
{% hint style="warning" %}
You need the [PostgreSQL client](https://www.postgresql.org/download/) installed locally first, so that you can use `psql` to connect to any PostgreSQL server.
{% endhint %}

Connect to the PostgreSQL instance using `gcloud` CLI.

```bash
gcloud sql connect postgresql-instance
```

{% endtab %}

{% tab title="SQL Server" %}
{% hint style="warning" %}
You need the [MS SQL Server client](https://docs.microsoft.com/en-us/sql/tools/mssql-cli?view=sql-server-ver15) installed locally first, so that you can use `mssql-cli` to connect to any SQL Server.
{% endhint %}

Connect to the SQL Server instance using `gcloud` CLI.

```bash
gcloud sql connect sqlserver-instance
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
You can configure Cloud SQL instances to only have [private IP addresses](https://cloud.google.com/sql/docs/mysql/private-ip), so that it's only accessible from a Virtual Private Cloud network.
{% endhint %}

### Create a Table

From the command line connection, you can use the client to create a table for the corresponding database. For example:

{% tabs %}
{% tab title="MySQL" %}

```sql
# Change to orders database
USE orders;

CREATE TABLE order_items (
  id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  order_id BIGINT,
  description VARCHAR(255),
  quantity INT DEFAULT 1
);

CREATE TABLE orders (
  id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  description VARCHAR(255),
  creation_timestamp TIMESTAMP
);

ALTER TABLE order_items ADD FOREIGN KEY (order_id) REFERENCES orders (id);
```

{% endtab %}

{% tab title="PostgreSQL" %}

```sql
# Change to orders database
\c orders

CREATE TABLE order_items (
  id BIGSERIAL NOT NULL PRIMARY KEY,
  order_id BIGINT,
  description VARCHAR(255),
  quantity INT DEFAULT 1
);

CREATE TABLE orders (
  id BIGSERIAL NOT NULL PRIMARY KEY,
  description VARCHAR(255),
  creation_timestamp TIMESTAMP
);

ALTER TABLE order_items ADD FOREIGN KEY (order_id) REFERENCES orders (id);
```

{% endtab %}

{% tab title="SQL Server" %}

```sql
USE orders;

CREATE TABLE order_items (
  id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
  order_id BIGINT,
  description VARCHAR(255),
  quantity INT DEFAULT 1
);

CREATE TABLE orders (
  id BIGINT NOT NULL IDENTITY(1,1) PRIMARY KEY,
  description VARCHAR(255),
  creation_timestamp TIMESTAMP
);

ALTER TABLE order_items ADD FOREIGN KEY (order_id) REFERENCES orders (id);
```

{% endtab %}
{% endtabs %}

### Add a User

You can add a user using `gcloud` command line:

{% tabs %}
{% tab title="MySQL" %}
Use `gcloud` command line to create a new user:

```bash
gcloud sql users create order-user
  --instance=mysql-instance \
  --password=...
```

{% hint style="danger" %}
The new user has no privileges. Connect to the database server and grant privileges. Refer to [MySQL documentation to use `GRANT`](https://dev.mysql.com/doc/refman/8.0/en/grant.html).
{% endhint %}
{% endtab %}

{% tab title="PostgreSQL" %}
Use `gcloud` command line to create a new user:

```bash
gcloud sql users create order-user \
  --instance=postgresql-instance \
  --password=...
```

{% hint style="danger" %}
The new user has no privileges. Connect to the database server and grant privileges. Refer to [PostgreSQL documentation to use `GRANT`](https://www.postgresql.org/docs/9.0/sql-grant.html).
{% endhint %}
{% endtab %}

{% tab title="SQL Server" %}
Use `gcloud` command line to create a new user:

```bash
gcloud sql users create order-user \
  --instance=sqlserver-instance \
  --password=...
```

{% hint style="danger" %}
The new user has no privileges. Connect to the database server and grant privileges. Refer to [SQL Server documentation to use `GRANT`](https://docs.microsoft.com/en-us/sql/t-sql/statements/grant-object-permissions-transact-sql?view=sql-server-ver15).
{% endhint %}
{% endtab %}
{% endtabs %}

### Instance Connection Name

Every Cloud SQL Instance has a unique instance connection name for the form of `PROJECT_ID:REGION:INSTANCE_NAME`.

Find the Instance Connection Name using `gcloud` command line:

```bash
gcloud sql instances describe INSTANCE_NAME --format='value(connectionName)'
```

{% tabs %}
{% tab title="MySQL" %}
MySQL instance's Instance Connection Name

```bash
gcloud sql instances describe mysql-instance \
  --format='value(connectionName)'
```

{% endtab %}

{% tab title="PostgreSQL" %}
PostgreSQL instance's Instance Connection Name

```bash
gcloud sql instances describe postgresql-instance \
  --format='value(connectionName)'
```

{% endtab %}

{% tab title="SQL Server" %}
SQL Server instance's Instance Connection Name

```bash
gcloud sql instances describe sqlserver-instance \
  --format='value(connectionName)'
```

{% endtab %}
{% endtabs %}

## JDBC

There are different ways to connect to a Cloud SQL instance. All methods will configure a JDBC URL to allow you to use the corresponding JDBC Driver, and subsequently, JPA / Hibernate and Spring Data.

| Method                   | MySQL | PostgreSQL | SQL Server | Considerations                                                   |
| ------------------------ | ----- | ---------- | ---------- | ---------------------------------------------------------------- |
| Cloud SQL Starter        | ✅     | ✅          | 🚫         | Easy to configure for Spring Boot projects.                      |
| Cloud SQL Socket Factory | ✅     | ✅          | 🚫         | Works with non Spring Boot projects.                             |
| Cloud SQL Proxy          | ✅     | ✅          | ✅          | Offloads authentication to proxy.                                |
| VPC Private IP           | ✅     | ✅          | ✅          | Access via VPC. Can be used with all of the other methods above. |

### Cloud SQL Starter

When using Spring Boot, you can use [Spring Cloud GCP's Cloud SQL starter](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#spring-jdbc).

Cloud SQL starter will automatically:

* Add dependency to the corresponding JDBC driver, and the [Cloud SQL socket factory](/app-dev/cloud-services/databases/cloud-sql#cloud-sql-socket-factory). You **do not** need to add those dependency separately.
* Configure the JDBC URL for the corresponding database instance.

#### Dependency

Add the Cloud SQL Starter dependency:

{% tabs %}
{% tab title="MySQL" %}
Maven:

{% code title="pom.xml" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-sql-mysql</artifactId>
</dependency>
```

{% endcode %}

Gradle:

{% code title="build.gradle" %}

```groovy
dependencies {
    compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-sql-mysql'
}
```

{% endcode %}
{% endtab %}

{% tab title="PostgreSQL" %}
Maven:

{% code title="pom.xml" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-sql-postgresql</artifactId>
</dependency>
```

{% endcode %}

Gradle:

{% code title="build.gradle" %}

```groovy
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-sql-postgresql'
```

{% endcode %}
{% endtab %}

{% tab title="SQL Server" %}
{% hint style="danger" %}
Cloud SQL Starter is not supported for SQL Server. Use Cloud SQL Proxy instead.
{% endhint %}
{% endtab %}
{% endtabs %}

#### Configuration

Configure Spring Boot application's`application.properties` with [Instance Connection Name](/app-dev/cloud-services/databases/cloud-sql#instance-connection-name) and the database name:

{% code title="application.properties" %}

```bash
# Retrieve instance connection name from the previous step
spring.cloud.gcp.sql.instance-connection-name=INSTANCE_CONNECTION_NAME
spring.cloud.gcp.sql.database-name=orders

# Cloud SQL starter automatically configures the JDBC URL

# Configure username/password
spring.datasource.username=...
spring.datasource.password=...

# Configure connection pooling if needed
spring.datasource.hikari.maximum-pool-size=10
```

{% endcode %}

#### Sample

* [Spring Boot with Cloud SQL PostgreSQL](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-sql-postgres-sample)
* [Spring Boot with Cloud SQL MySQL](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-sql-mysql-sample)

### Cloud SQL Socket Factory

If you don't use Spring Cloud GCP's Cloud SQL starter, and need to configure JDBC URL directly, you can use [Cloud SQL Socket Factory](https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory) with existing JDBC driver.

#### Dependency

In addition to the JDBC Driver dependency, add the Cloud SQL Socket Factory dependency:

{% tabs %}
{% tab title="MySQL" %}
Maven:

```markup
<dependency>
    <groupId>com.google.cloud.sql</groupId>
    <artifactId>mysql-socket-factory-connector-j-8</artifactId>
    <version>1.1.0</version>
</dependency>
```

Gradle:

{% code title="build.gradle" %}

```groovy
dependencies {
    compile 'com.google.cloud.sql:mysql-socket-factory-connector-j-8:1.1.0'
}
```

{% endcode %}

Different MySQL Socket Factory artifact is needed for different MySQL Connector/J versions. See [MySQL Socket Factory README](https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#mysql) for more information.
{% endtab %}

{% tab title="PostgreSQL" %}
Maven:

```markup
<dependency>
    <groupId>com.google.cloud.sql</groupId>
    <artifactId>postgres-socket-factory</artifactId>
    <version>1.1.0</version>
</dependency>
```

Gradle:

{% code title="build.gradle" %}

```groovy
dependencies {
    compile 'com.google.cloud.sql:postgres-socket-factory:1.1.0'
}
```

{% endcode %}
{% endtab %}
{% endtabs %}

#### Configuration

{% tabs %}
{% tab title="MySQL" %}
MySQL instance's JDBC URL with Cloud SQL Socket Factory follows the format of:

```bash
jdbc:mysql:///DATABASE_NAME?cloudSqlInstance=INSTANCE_CONNECTION_NAME&socketFactory=com.google.cloud.sql.mysql.SocketFactory
```

The JDBC URL for the Cloud SQL instance in this example is:

```bash
jdbc:mysql:///orders?cloudSqlInstance=PROJECT_ID:us-central1:mysql-instance&socketFactory=com.google.cloud.sql.mysql.SocketFactory
```

{% endtab %}

{% tab title="PostgreSQL" %}
PostgreSQL instance's JDBC URL with Cloud SQL Socket Factory follows the format of:

```
jdbc:postgresql:///DATABASE_NAME?cloudSqlInstance=INSTANCE_CONNECTION_NAME&socketFactory=com.google.cloud.sql.postgres.SocketFactory
```

The JDBC URL for the Cloud SQL instance in this example is:

```
jdbc:postgresql:///orders?cloudSqlInstance=PROJECT_ID:us-central1:postgresql-instance&socketFactory=com.google.cloud.sql.postgres.SocketFactory
```

{% endtab %}

{% tab title="SQL Server" %}
{% hint style="danger" %}
Cloud SQL Socket Factory is not supported for SQL Server. Use Cloud SQL Proxy instead.
{% endhint %}
{% endtab %}
{% endtabs %}

### Cloud SQL Proxy

[Cloud SQL Proxy](https://cloud.google.com/sql/docs/mysql/sql-proxy) is the generic way of establishing secured connection to a Cloud SQL instance. Rather than using the Cloud SQL Socket Factory to exchange certificates, Cloud SQL Proxy will authenticate and exchange the certificates.

![Cloud SQL Proxy diagram](/files/-M3TG-lpAuBtvbx20Zaw)

Install Cloud SQL Proxy:

```bash
gcloud components install cloud_sql_proxy
```

Start the proxy:

{% tabs %}
{% tab title="MySQL" %}

```bash
# Refer to Instance Connection Name from previous section
cloud_sql_proxy -instances=INSTANCE_CONNECTION_NAME=tcp:3306
```

{% endtab %}

{% tab title="PostgreSQL" %}

```bash
# Refer to Instance Connection Name from previous section
cloud_sql_proxy -instances=INSTANCE_CONNECTION_NAME=tcp:5432
```

{% endtab %}

{% tab title="SQL Server" %}

```bash
# Refer to Instance Connection Name from previous section
cloud_sql_proxy -instances=INSTANCE_CONNECTION_NAME=tcp:1433
```

{% endtab %}
{% endtabs %}

You can then establish connections on `localhost` with the corresponding ports.

{% tabs %}
{% tab title="MySQL" %}
Connect with `mysql` CLI:

```bash
mysql -u root -p
```

Or, connect with JDBC using JDBC URL:

```bash
jdbc:mysql://localhost/orders
```

{% endtab %}

{% tab title="PostgreSQL" %}
Connect with `psql` CLI:

```
psql -h localhost -U postgres
```

Or, connect with JDBC using JDBC URL:

```bash
jdbc:postgresql://localhost/orders
```

{% endtab %}

{% tab title="SQL Server" %}
Connect with `mssql-cli` CLI:

```
mssql-cli -U sqlserver
```

Or, connect with JDBC using JDBC URL:

```bash
jdbc:sqlserver://localhost/databaseName=orders
```

{% endtab %}
{% endtabs %}

#### Unix Socket Domain

You can optionally configure Cloud SQL Proxy to expose not a TCP IP port, but using Unix Socket Domain instead, and configure the Cloud SQL Socket Factory to connect using the Unix Socket Domain. See [Connect External App documentation](https://cloud.google.com/sql/docs/mysql/connect-external-app#unix-sockets) for more details.

### VPC Private IP

If your Cloud SQL instance is on [VPC and has a private IP](https://cloud.google.com/sql/docs/mysql/private-ip), and your application is running in the Cloud able to access the same VPC, then configure JDBC drivers normally connecting to the private IP address.

## R2DBC

You can use R2DBC driver for reactive database access when you connect to Cloud SQL instances using:

* Cloud SQL Proxy
* VPC Private IP
* Using R2DBC Cloud SQL Connector

### Cloud SQL Proxy or VPC Private IP

You can use standard R2DBC driver to connect using the IP address. See [R2DBC documentation](https://r2dbc.io/) for corresponding driver usages:

* [r2dbc-mysql](https://github.com/mirromutth/r2dbc-mysql)
* [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql)
* [r2dbc-mssql](https://github.com/r2dbc/r2dbc-mssql)

### Cloud SQL Connector

You can use R2DBC Cloud SQL Connector that automatically exchanges the certificates like the Cloud SQL Socket Factory.

{% hint style="info" %}
See [Cloud SQL Socket Factory README](https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#instructions-for-r2dbc) for more information on configuring the R2DBC driver for Cloud SQL.
{% endhint %}


# Cloud Spanner

[Cloud Spanner](https://cloud.google.com/spanner) is a scalable, enterprise-grade, globally-distributed, and strongly consistent database service built for the cloud specifically to combine the benefits of relational database structure with non-relational horizontal scale. It delivers high-performance transactions and strong consistency across rows, regions, and continents with an industry-leading 99.999% availability SLA, no planned downtime, and enterprise-grade security. Cloud Spanner revolutionizes database administration and management and makes application development more efficient.

## Cloud Spanner Instance

### Enable API

```bash
gcloud services enable spanner.googleapis.com
```

### Create an Instance

```bash
gcloud spanner instances create spanner-instance \
  --config=regional-us-central1 \
  --nodes=1 --description="A Spanner Instance"
```

{% hint style="info" %}
This example creates a new regional instance (i.e., spanning across zones within the same region). Cloud Spanner supports multi-regional configuration to span across multiple regions for highest availability.
{% endhint %}

### Create a Database

A Cloud Spanner instance can host multiple databases, that each contains its own tables.

```bash
gcloud spanner databases create orders \
  --instance=spanner-instance
```

### Connect to Instance

There is no interactive CLI to Cloud Spanner. You can create table and execute SQL statements from the Cloud Console, or from `gcloud` command line. Following are some common operations:

#### List Databases

```bash
gcloud spanner databases list --instance=spanner-instance
```

#### Execute SQL Statements

```bash
gcloud spanner databases execute-sql orders \
  --instance=spanner-instance \
  --sql="SELECT 1"
```

#### Show Query Plan

```bash
gcloud spanner databases execute-sql orders \
  --instance=spanner-instance \
  --sql="SELECT 1" \
  --query-mode=PLAN
```

### Create a Table

Create a DDL file, `schema.ddl`:

```sql
CREATE TABLE orders (
  order_id STRING(36) NOT NULL,
  description STRING(255),
  creation_timestamp TIMESTAMP,
) PRIMARY KEY (order_id);

CREATE TABLE order_items (
  order_id STRING(36) NOT NULL,
  order_item_id STRING(36) NOT NULL,
  description STRING(255),
  quantity INT64,
) PRIMARY KEY (order_id, order_item_id),
  INTERLEAVE IN PARENT orders ON DELETE CASCADE;
```

{% hint style="warning" %}
Cloud Spanner differs from traditional RDBMS in a several ways:

* No server-side automatic ID generation.
* Avoid monodically increasing IDs - I.e., no auto incremented ID, because it may create hot spots in partitions.
* No foreign key constraints.
  {% endhint %}

{% hint style="success" %}
Cloud Spanner, being horizontally scalable and shards data with your keys, prefers the following:

* UUIDv4 as IDs - It's random and can be partitioned easily.
* Parent-Children relationship encoded using a compose primary key.
* Colocate Parent-Children data using Interleave tables - If accessing parent means children data is also likely to be read, interleaving allows children data to be colocated with parent row in the same parition.
  {% endhint %}

Use gcloud to execute the DDL:

```bash
gcloud spanner databases ddl update orders \
  --instance=spanner-instance \
  --ddl="$(<schema.ddl)"
```

## Spring Data Spanner Starter

The easiest way to access Cloud Spanner is using Spring Cloud GCP's [Spring Data Spanner starter](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#spring-data-cloud-spanner). This starter provides full Spring Data support for Cloud Spanner while implementing idiomatic access patterns.

| Spring Data Feature     | Supported |
| ----------------------- | --------- |
| ORM                     | ✅         |
| Declarative Transaction | ✅         |
| Repository              | ✅         |
| REST Repository         | ✅         |
| Query methods           | ✅         |
| Query annotation        | ✅         |
| Pagination              | ✅         |
| Events                  | ✅         |
| Auditing                | ✅         |

### Dependency

Add the Spring Data Spanner starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-data-spanner</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-spanner'
```

{% endtab %}
{% endtabs %}

### Configuration

Configure Cloud Spanner instance and database to connect to.

{% code title="application.properties" %}

```bash
spring.cloud.gcp.spanner.instance-id=spanner-instance
spring.cloud.gcp.spanner.database=orders
```

{% endcode %}

{% hint style="info" %}
Notice that there is no explicit configuration for username/password. Cloud Spanner authentication uses the GCP credential (either your user credential, or Service Account credential), and authorization is configured via Identity Access Management (IAM).
{% endhint %}

### ORM

Spring Data Cloud Spanner allows you to map domain POJOs to Cloud Spanner tables via annotations. Read the [Spring Data Spanner reference documentation](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#object-mapping) for details

{% code title="Order.java" %}

```java
import java.time.LocalDateTime;
import java.util.List;
import lombok.Data;
import org.springframework.cloud.gcp.data.spanner.core.mapping.*;

@Table(name="orders")
@Data // Lombok to generate getter/setters
class Order {
  @PrimaryKey
  @Column(name="order_id")
  private String id;

  private String description;

  @Column(name="creation_timestamp")
  private LocalDateTime timestamp;

  @Interleaved
  private List<OrderItem> items;
}  
```

{% endcode %}

{% code title="OrderItem.java" %}

```java
import lombok.Data;
import org.springframework.cloud.gcp.data.spanner.core.mapping.*;

@Table(name="order_items")
@Data // Lombok to generate getter/setters
class OrderItem {
    @PrimaryKey(keyOrder = 1)
    @Column(name="order_id")
    private String orderId;

    @PrimaryKey(keyOrder = 2)
    @Column(name="order_item_id")
    private String orderItemId;

    private String description;
    private Long quantity;
}
```

{% endcode %}

{% hint style="info" %}
Read the [Spring Data Spanner reference documentation](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#object-mapping) for more details.
{% endhint %}

### Repository

Use Spring Data repository to quickly get CRUD access to the Cloud Spanner tables.

{% code title="OrderRepository.java" %}

```java
package com.example.demo;

import org.springframework.cloud.gcp.data.spanner.repository.SpannerRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface OrderRepository extends SpannerRepository<Order, String> {
}
```

{% endcode %}

{% code title="OrderItemRepoistory.java" %}

```java
package com.example.demo;

import lombok.Data;
import org.springframework.cloud.gcp.data.spanner.core.mapping.*;

@Table(name="order_items")
@Data
class OrderItem {
  @PrimaryKey(keyOrder = 1)
  @Column(name="order_id")
  private String orderId;

  @PrimaryKey(keyOrder = 2)
  @Column(name="order_item_id")
  private String orderItemId;

  private String description;
  private Long quantity;
}
```

{% endcode %}

{% hint style="info" %}
`Order` is the parent and has only a primary key. Spring Data repository's ID parameter type can be the type for the single key. `OrderItem`, however, has a composite key. Spring Data repository's ID parameter type must be Cloud Spanner's `Key` type for a composite key.
{% endhint %}

In a business logic service, you can utilize the repositories:

```java
@Service
class OrderService {
  private final OrderRepository orderRepository;

  OrderService(OrderRepository orderRepository,
      OrderItemRepository orderItemRepository) {
    this.orderRepository = orderRepository;
  }

  @Transactional
  Order createOrder(Order order) {
    // Use UUID String representation for the ID
    order.setId(UUID.randomUUID().toString());

    // Set the creation time
    order.setCreationTimestamp(LocalDateTime.now());

    // Set the parent Order ID and children ID for each item.
    if (order.getItems() != null) {
        order.getItems().stream().forEach(orderItem -> {
        orderItem.setOrderId(order.getId());
        orderItem.setOrderItemId(UUID.randomUUID().toString());
      });
    }

    // Children are saved in cascade.
    return orderRepository.save(order);
  }
}
```

### Rest Repository

[Spring Data Rest](https://spring.io/projects/spring-data-rest) can expose a Spring Data repository directly on a RESTful endpoint, and rendering the payload as JSON with [HATEOS](https://en.wikipedia.org/wiki/HATEOAS) format. It supports common access patterns like pagination.

Add Spring Data Rest starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-rest'
```

{% endtab %}
{% endtabs %}

```java
@RepositoryRestResource
interface OrderRepository extends SpannerRepository<Order, String> {
}

@RepositoryRestResource
interface OrderItemRepository extends SpannerRepository<OrderItem, Key> {
  List<OrderItem> findAllByOrderId(String orderId);
}
```

To access the endpoint for Order:

```java
curl http://localhost:8080/orders
```

### Samples

* [Spring Boot with Cloud Spanner sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-spanner-sample)

## JDBC

[Cloud Spanner JDBC Driver](https://cloud.google.com/spanner/docs/use-oss-jdbc) can be used if you need raw JDBC access.

### Dependency

Add Cloud Spanner JDBC Driver:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>com.google.cloud</groupId>
    <artifactId>google-cloud-spanner-jdbc</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'com.google.cloud', name: 'google-cloud-spanner-jdbc'
```

{% endtab %}
{% endtabs %}

Use Spring Boot JDBC Starter to use JDBC Template:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.boot', name: 'spring-boot-starter-jdbc'
```

{% endtab %}
{% endtabs %}

### Configuration

Cloud Spanner JDBC Driver Class:

```java
com.google.cloud.spanner.jdbc.JdbcDriver
```

Cloud Spanner JDBC URL format:

```java
jdbc:cloudspanner:/projects/PROJECT_ID/instances/INSTANCE_ID/databases/DATABASE_NAME
```

With Spring Data JDBC, you can configure the datasource:

{% code title="application.properties" %}

```java
spring.datasource.driver-class-name=com.google.cloud.spanner.jdbc.JdbcDriver
spring.datasource.url=jdbc:cloudspanner:/projects/PROJECT_ID/instances/spanner-instance/databases/orders
```

{% endcode %}

## Hibernate

[Cloud Spanner Hibernate Dialect](https://cloud.google.com/spanner/docs/use-hibernate) lets you use Cloud Spanner with [Hibernate ORM](https://hibernate.org/orm/). You can use Cloud Spanner with Hibernate from any Java application. When using Spring Boot, you can use the Hibernate Dialect with [Spring Data JPA](https://spring.io/projects/spring-data-jpa).

### Dependency

Add both the Cloud Spanner JDBC driver and Cloud Spanner Hibernate Dialect:

{% tabs %}
{% tab title="Maven" %}

```bash
<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-spanner-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-spanner-hibernate-dialect</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```
compile group: 'com.google.cloud', name: 'google-cloud-spanner-jdbc'
compile group: 'com.google.cloud', name: 'google-cloud-spanner-hibernate-dialect'
```

{% endtab %}
{% endtabs %}

Add Spring Data JPA starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-jpa'
```

{% endtab %}
{% endtabs %}

### Configuration

Cloud Spanner Hibernate Dialect class:

```java
com.google.cloud.spanner.hibernate.SpannerDialect
```

Configure Spring Data JPA:

{% code title="application.properties" %}

```java
spring.datasource.driver-class-name=com.google.cloud.spanner.jdbc.JdbcDriver
spring.datasource.url=jdbc:cloudspanner:/projects/PROJECT_ID/instances/spanner-instance/databases/orders

spring.jpa.database-platform=com.google.cloud.spanner.hibernate.SpannerDialect
```

{% endcode %}

### ORM

Use JPA annotations to map POJO to Cloud Spanner database tables.

{% hint style="info" %}
In Cloud Spanner, parent-children relationship is modeled as a composite key. With JPA, use `IdClass` or `EmbeddableId` to map a composite key.
{% endhint %}

```java
@Entity
@Table(name="orders")
class Order {
    @Id
    @Column(name="order_id")
  private String id;

    private String description;

    @Column(name="creation_timestamp")
    private LocalDateTime creationTimestamp;

    @OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
    private List<OrderItem> items;

  // Getter and Setter ...
}

@Embeddable
class OrderItemId implements Serializable {
  @Column(name="order_id")
    private String orderId;

    @Column(name="order_item_id")
    private String orderItemId;

    // Getter and Setter ...
    // Hashcode and Equals ...
}

@Entity
@Table(name="order_items")
class OrderItem {
    @EmbeddedId
    private OrderItemId orderItemId;

    private String description;
    private Long quantity;

    @ManyToOne
  @JoinColumn(name="order_id", insertable = false, updatable = false)
    private Order order;

    // Getter and Setter ...
}
```

### Repository

Use Spring Data repository for CRUD access to the tables.

```java
@Repository
interface OrderRepository extends JpaRepository<Order, String> {
}

@Repository
interface OrderItemRepository extends JpaRepository<OrderItem, OrderItemId> {
}
```

### Rest Repository

Use Spring Data Rest to expose the repositories as RESTful services with HATEOS format.

```java
@RepositoryRestResource
interface OrderRepository extends JpaRepository<Order, String> {
}

@RepositoryRestResource
interface OrderItemRepository extends JpaRepository<OrderItem, OrderItemId> {
}
```

### Sample

* [Spring Boot with Spring Data JPA and Cloud Spanner](https://github.com/GoogleCloudPlatform/google-cloud-spanner-hibernate/tree/master/google-cloud-spanner-hibernate-samples/spring-data-jpa-sample)
* [Quarkus with Hibernate and Cloud Spanner](https://github.com/GoogleCloudPlatform/google-cloud-spanner-hibernate/tree/master/google-cloud-spanner-hibernate-samples/quarkus-jpa-sample)
* [Microprofile with Hibernate and Cloud Spanner](https://github.com/GoogleCloudPlatform/google-cloud-spanner-hibernate/tree/master/google-cloud-spanner-hibernate-samples/microprofile-jpa-sample)

## R2DBC

[Cloud Spanner R2DBC driver](https://github.com/GoogleCloudPlatform/cloud-spanner-r2dbc) let's you access Cloud Spanner data with reactive API.

[Cloud Spanner R2DBC driver](https://github.com/GoogleCloudPlatform/cloud-spanner-r2dbc) is under active development.

It can be used with [Spring Data R2DBC](https://spring.io/projects/spring-data-r2dbc) using the [Cloud Spanner R2DBC Dialect](https://github.com/GoogleCloudPlatform/cloud-spanner-r2dbc/tree/master/cloud-spanner-spring-data-r2dbc).

### Samples

* [Spring Boot with Spring Data R2DBC and Cloud Spanner](https://github.com/GoogleCloudPlatform/cloud-spanner-r2dbc/tree/master/cloud-spanner-r2dbc-samples/cloud-spanner-spring-data-r2dbc-sample)


# Cloud Firestore

Cloud Firestore is a managed, highly scalable, NoSQL database service. Cloud Firestore automatically handles sharding and replication, providing you with a highly available and durable database that scales automatically to handle your applications' load.

Cloud Firestore has two modes - Datastore Mode, and Native Mode. While both modes are NoSQL databases, there are a lot of difference between them, primarily:

* Native Mode is a real-time database, meaning you can listen to updates in real-time, and the data model is composed of Document and Collection of documents..
* Datastore mode does not have the real-time capability, and the data model is composed of Entity and organized in Kind of entities.

{% hint style="info" %}
Read [Choosing between Native mode and Datastore mode](https://cloud.google.com/datastore/docs/firestore-or-datastore) for more information.
{% endhint %}

For traditional backend applications where real-time data updates is not needed, the Datastore mode is simple to use. For backend applications that wants to adopt reactive programming model, then the Native mode is better suited.&#x20;

{% hint style="danger" %}
A single Google Cloud Project can only choose one of the modes. Once the mode chosen, you cannot change the mode. I.e., if you created a project that chose to use the Native mode, then the same project can no longer use the Datastore mode.
{% endhint %}

Learn how to use each of the mode in the following pages.

{% content-ref url="/pages/-M9dZMn1ziyaegs7TJtP" %}
[Datastore Mode](/app-dev/cloud-services/databases/cloud-firestore/datastore-mode)
{% endcontent-ref %}


# Datastore Mode

## Cloud Firestore Datastore Instance

There can only be one Cloud Firestore instance associated with a single project. The Datastore instance is automatically created when you enable the API:

There can only be one Datastore instance associated with a single project. The Cloud Firestore in Datastore instance is automatically created when you enable the API:

### Enable API

```bash
gcloud services enable datastore.googleapis.com
```

### Data Schema

Because Cloud Firestore is a NoSQL database, you do not need to explicitly create tables, define data schema, etc. Simply use the API to store new documents, and perform CRUD operations.

## Spring Data Datastore

The easiest way to access Datastore is using Spring Cloud GCP's [Spring Data Datastore starter](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#spring-data-cloud-datastore). This starter provides full Spring Data support for Datastore while implementing idiomatic access patterns.

| Spring Data Feature     | Supported |
| ----------------------- | --------- |
| ORM                     | ✅         |
| Declarative Transaction | ✅         |
| Repository              | ✅         |
| REST Repository         | ✅         |
| Query methods           | ✅         |
| Query annotation        | ✅         |
| Pagination              | ✅         |
| Events                  | ✅         |
| Auditing                | ✅         |

### Dependency

Add the Spring Data Datastore starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-data-datastore</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-datastore'
```

{% endtab %}
{% endtabs %}

### Configuration

There is no explicit configuration required if you use the automatic authentication and project ID detection. I.e., if you already logged in locally with `gcloud` command line, then it'll automatically use Datastore from the project you configured in `gcloud`.

{% hint style="info" %}
Notice that there is no explicit configuration for username/password. Cloud Firestore authentication uses the GCP credential (either your user credential, or Service Account credential), and authorization is configured via Identity Access Management (IAM).
{% endhint %}

### ORM

Spring Data Cloud Datastore allows you to map domain POJOs to Datastore documents via annotations. Read the [Spring Data Datastore reference documentation](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#object-mapping-2) for details

```java
@Entity
class Order {
    @Id
    private Long id;
    private String description;
    private LocalDateTime timestamp;
    private List<OrderItem> items;

    // Getters and setters ...
}

@Entity
class OrderItem {
    private String description;
    private Long quantity;

    // Getters and setters ...
}
```

Because Datastore is a document-oriented NoSQL database, you can have nested structure, you can establish parent-children relationships without complicated foreign keys.

{% hint style="info" %}
Read the [Spring Data Datastore reference documentation](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#object-mapping-2) for more details.
{% endhint %}

### Repository

Use Spring Data repository to quickly get CRUD access to the Datastore.

```java
@Repository
interface OrderRepository extends DatastoreRepository<Order, Long> {
}
```

In a business logic service you can utilize the repositories:

```java
@Service
class OrderService {
    private final OrderRepository orderRepository;

    OrderService(OrderRepository orderRepository) {
        this.orderRepository = orderRepository;
    }

    @Transactional
    Order createOrder(Order order) {
        // Set the creation time
        order.setTimestamp(LocalDateTime.now());

        // Children are saved in cascade.
        return orderRepository.save(order);
    }
}
```

### Rest Repository

[Spring Data Rest](https://spring.io/projects/spring-data-rest) can expose a Spring Data repository directly on a RESTful endpoint, and rendering the payload as JSON with [HATEOAS](https://en.wikipedia.org/wiki/HATEOAS) format. It supports common access patterns like pagination.

Add Spring Data Rest starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-rest'
```

{% endtab %}
{% endtabs %}

```java
@RepositoryRestResource
interface OrderRepository extends DatastoreRepository<Order, String> {
}
```

To access the endpoint for Order:

```java
curl http://localhost:8080/orders
```

### Samples

* [Spring Boot with Datastore sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-data-datastore-sample)


# Native Mode

## Cloud Firestore Native Instance

There can only be one Datastore instance associated with a single project. The Cloud Firestore in Datastore instance is automatically created when you enable the API:

### Enable API

```bash
gcloud services enable firestore.googleapis.com
```

### Data Schema

Because Cloud Firestore is a NoSQL database, you do not need to explicitly create tables, define data schema, etc. Simply use the API to store new documents, and perform CRUD operations.

## Spring Data Firestore

The easiest way to access Cloud Firestore is using Spring Cloud GCP's [Spring Data Firestore starter](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#spring-data-reactive-repositories-for-cloud-firestore). This starter provides full Spring Data support for Cloud Firestore while implementing idiomatic access patterns.

| Spring Data Feature     | Supported |
| ----------------------- | --------- |
| Reactive Repository     | ✅         |
| ORM                     | ✅         |
| Declarative Transaction | ✅         |
| Repository              | ✅         |
| REST Repository         | ❌         |
| Query methods           | ✅         |
| Query annotation        | ✅         |
| Pagination              | ✅         |
| Events                  | ✅         |
| Auditing                | ✅         |

### Dependency

Add the Spring Data Firestore starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-data-firestore</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-firestore'
```

{% endtab %}
{% endtabs %}

### Configuration

There is no explicit configuration required if you use the automatic authentication and project ID detection. I.e., if you already logged in locally with `gcloud` command line, then it'll automatically use Datastore from the project you configured in `gcloud`.

{% hint style="info" %}
Notice that there is no explicit configuration for username/password. Cloud Firestore authentication uses the GCP credential (either your user credential, or Service Account credential), and authorization is configured via Identity Access Management (IAM).
{% endhint %}

### ORM

Spring Data Cloud Firestore allows you to map domain POJOs to Datastore documents via annotations.

```java
@Document
class Order {
    @DocumentId
    private String id;
    private String description;
    private LocalDateTime timestamp;
    private List<OrderItem> items;

    // Getters and setters ...
}

class OrderItem {
    private String description;
    private Long quantity;

    // Getters and setters ...
}
```

Because Firestore is a document-oriented NoSQL database, you can have nested structure and can establish parent-children relationships without complicated foreign keys.

{% hint style="info" %}
Read the [Spring Data Firestore reference documentation](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#object-mapping-3) for more details.
{% endhint %}

### Repository

Use Spring Data Reactive repository to quickly get CRUD access to the Cloud Firestore.

```java
@Repository
interface OrderRepository extends FirestoreReactiveRepository<Order> {
}
```

In a business logic service, you can utilize the repositories:

```java
@Service
class OrderService {
  private final OrderRepository orderRepository;

  OrderService(OrderRepository orderRepository) {
    this.orderRepository = orderRepository;
  }

  @Transactional
  Mono<Order> createOrder(Order order) {
    // Set the creation time
    order.setTimestamp(Timestamp.of(new Date()));

    // Children are saved in cascade.
    return orderRepository.save(order);
  }
}
```

### Samples

* [Spring Boot with Cloud Firestore sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-data-firestore)


# Messaging


# Cloud Pub/Sub

## Cloud Pub/Sub

[Cloud Pub/Sub](https://cloud.google.com/pubsub/docs/) is a managed publish/subscribe service, where you can send messages to a topic, and subscribe via push, pull, or streaming pull. A single Cloud Pub/Sub Topic can be associated with one or more Subscriptions. Each Subscription can have one or more subscribers. Cloud Pub/Sub delivers messages with guaranteed at-least-once delivery, and there is no ordering guarantee.

### Enable API

```bash
gcloud services enable pubsub.googleapis.com
```

### Create a Topic

```bash
gcloud pubsub topics create orders
```

### Create a Pull Subscription

```bash
gcloud pubsub subscriptions create orders-subscription --topic=orders
```

### Publish a Message

```bash
gcloud pubsub topics publish orders \
  --message='{"id":"1", "description": "My Order"}'
```

### Pull a Message

```bash
gcloud pubsub subscriptions pull orders-subscription --auto-ack
```

### Dead Letter Topic

Normally if you failed to process a message, then you will need to un-acknowledge, and the message will be re-delivered again. However, you may not want to continuously re-deliver the same message indefinitely because your application simply cannot process it. In this case, you'd want to create a subscription with a Dead Letter Topic. You can then configure the max re-delivery attempts - and when all the attempts are exhausted, Pub/Sub will then re-deliver the message to a different topic. In order for the Dead Letter Topic to persist the message, you must also create a subscription for it - otherwise, no message will be persisted for the Dead Letter Topic.

#### Create a Dead Letter Topic

```bash
gcloud pubsub topics create order-dlt
gcloud pubsub subscriptions create order-dlt-subscription \
  --topic=order-dlt \
  --ack-deadline=300 \
  --expiration-period=never
```

#### Create a Subscription with Dead Letter Topic

```bash
gcloud pubsub subscriptions create orders-subscription \
  --topic=orders \
  --dead-letter-topic=order-dlt \
  --max-delivery-attempts=5
```

#### Grant Cloud Pub/Sub Permissions

You need to grant Cloud Pub/Sub additional permissions in order for Cloud Pub/Sub to be able to remove message from the original subscription and then publishing it to the DLT.

```bash
PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID --format='value(projectNumber)')
PUBSUB_SERVICE_ACCOUNT="service-${PROJECT_NUMBER}@gcp-sa-pubsub.iam.gserviceaccount.com"

# Permission to subscribe to the original subscription
gcloud pubsub subscriptions add-iam-policy-binding orders-subscription \
    --member="serviceAccount:$PUBSUB_SERVICE_ACCOUNT"\
    --role="roles/pubsub.subscriber"
    
# Permission to re-publish the message to the Dead Letter Topic
gcloud pubsub topics add-iam-policy-binding orders-dlt \
    --member="serviceAccount:${PUBSUB_SERVICE_ACCOUNT}"\
    --role="roles/pubsub.publisher"
```

{% hint style="info" %}
See [Cloud Pub/Sub Dead Letter Topic](https://cloud.google.com/pubsub/docs/dead-letter-topics) for more information.
{% endhint %}

### Ordering

You can enable Message Ordering to a Pub/Sub topic, so that messages with the same key value (e.g., the same Order ID) can be delivered in order. Ordering can be important for a CQRS system that cannot process events out of order.

{% hint style="info" %}
See [Cloud Pub/Sub Ordering documentation](https://cloud.google.com/pubsub/docs/ordering) for more information.
{% endhint %}

### Filtering

Some architectures may be delivering many different types of messages to a single topic. For example, there may be different event types for an Order event (e.g., Created, Fulfilled, Returned ...). If you need to create different workers to process different type of events, then you can create a Subscription for each event type, and select the type of message you want to process with a Filter.

{% hint style="info" %}
See [Cloud Pub/Sub Filtering documentation](https://cloud.google.com/pubsub/docs/filtering) for more information.
{% endhint %}

## Spring Cloud Pub/Sub

The easiest way to use Cloud Pub/Sub is using Spring Cloud GCP's [Spring Pub/Sub starter](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#google-cloud-pubsub). This starter provides easy to use `PubSubTemplate` bean to send and receive messages.

### Dependency

Add the Spring Cloud GCP Pub/Sub starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-pubsub</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-pubsub'
```

{% endtab %}
{% endtabs %}

### Configuration

There is no explicit configuration required if you use the automatic authentication and project ID detection. I.e., if you already logged in locally with `gcloud` command line, then it'll automatically use Pub/Sub topics/subscriptions from the project you configured in `gcloud`.

{% hint style="info" %}
Notice that there is no explicit configuration for username/password. Cloud Pub/Sub authentication uses the GCP credential (either your user credential, or Service Account credential), and authorization is configured via Identity Access Management (IAM).
{% endhint %}

### Pub/Sub Template

#### JSON Serialization

You need to produce a `PubSubMessageConverter` bean in order for Spring Cloud GCP Pub/Sub to automatically serialize a POJO into JSON payload,

```java
@Bean
public PubSubMessageConverter pubSubMessageConverter() {
    return new JacksonPubSubMessageConverter(new ObjectMapper());
}
```

#### Non-Web Applications

In a web application, the Java process will stay alive until it's explicitly killed. If your Pub/Sub message subscriber does not use a Web starter (Web or Webflux), then the application may exit as soon as it initializes. When you need the Pub/Sub subscribers to stay alive without exiting immediately, you must create a bean `ThreadPoolTaskScheduler` named `pubsubSubscriberThreadPool`.

```java
@Bean
ThreadPoolTaskScheduler pubsubSubscriberThreadPool() {
  return new ThreadPoolTaskScheduler();
}
```

#### Publish a Message

You can use `PubSubPublisherTemplate` to easily publish a message.

```java
@RestController
class OrderController {
  private final PubSubPublisherTemplate publisherTemplate;

  OrderController(
      PubSubPublisherTemplate publisherTemplate) {
    this.publisherTemplate = publisherTemplate;
  }

  @PostMapping("/order/submit")
  void submitOrder(@RequestBody Order order) {
    publisherTemplate.publish("orders", order);
  }
}
```

#### Pull a Message

You can pull N number of messages by using `PubSubSubscriberTemplate`.

```java
@Bean
ApplicationRunner runner(PubSubSubscriberTemplate subscriberTemplate) {
  return (args) -> {
    var msgs = subscriberTemplate
        .pullAndConvert("orders-subscription", 1, true, Order.class);
    msgs.forEach(msg -> {
      logger.info(msg.getPayload().getId());
      msg.ack();
    });
  };
}
```

#### Subscribe to a Subscription

You can also just subscribe to a subscription using Streaming Pull, so that it maintains a persistent connection, and can process messages whenever they arrive:

```java
@Bean
ApplicationRunner subscribeRunner(PubSubSubscriberTemplate subscriberTemplate) {
  return (args) -> {
    subscriberTemplate.subscribeAndConvert("orders-subscription", msg -> {
      System.out.println(msg.getPayload().getId());
      msg.ack();
    }, Order.class);
  };
}
```

{% hint style="warning" %}
Streaming pull currently does not support back-pressure well. If you have many small messages, but each message takes a long time to process, then you may not want to use Streaming Pull.
{% endhint %}

### Reactive Stream

If you are using Project Reactor (or Webflux that uses Project Reactor), you can also subscribe to a Pub/Sub Subscription using `PubSubReactiveFactory`.

```java
@Bean
ApplicationRunner reactiveSubscriber(PubSubReactiveFactory reactiveFactory, PubSubMessageConverter converter) {
  return (args) -> {
    reactiveFactory.poll("orders-subscription", 250L)
      // Convert a JSON payload into an object
      .map(msg -> converter.fromPubSubMessage(msg.getPubsubMessage(), Order.class))
      .doOnNext(order -> System.out.println(order.getId()))
      // Mannually acknowledge the message
      .doOnNext(AcknowledgeablePubsubMessage::ack);
      .subscribe();
  };
}
```

### Samples

* [Spring Cloud GCP Pub/Sub Template sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-pubsub-sample)
* [Spring Cloud GCP Reactive Pub/Sub sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-pubsub-reactive-sample)

## Spring Integration

[Spring Integration](https://spring.io/projects/spring-integration) is allows you to easily create Enterprise Integration pipelines by supporting well known [Enterprise Integration Patterns](https://www.enterpriseintegrationpatterns.com/). If you use Spring Integration, you can easily use Pub/Sub to send and consume messages, using an `InboundChannelAdapter` and `MessageHandler`.

### Inbound Channel

In Spring Integration, you can configure bind an input channel to a Pub/Sub Subscription using the `PubSubInboundChannelAdapter`.

```java
@Bean
public MessageChannel orderRequestInputChannel() {
  return MessageChannels.direct().get();
}

@Bean
public PubSubInboundChannelAdapter orderRequestChannelAdapter(
    @Qualifier("orderRequestInputChannel") MessageChannel inputChannel,
    PubSubTemplate pubSubTemplate) {
  PubSubInboundChannelAdapter adapter =
      new PubSubInboundChannelAdapter(
          pubSubTemplate, "orders-subscription");
  adapter.setOutputChannel(inputChannel);
  adapter.setPayloadType(Order.class);
  adapter.setAckMode(AckMode.AUTO);

  return adapter;
}
```

You can then create a new message processor and binding a method to the input channel, by using the `ServiceActivator` annotation.

```java
public class OrderProcessor {
  private static final Logger logger = LoggerFactory.getLogger(OrderProcessor.class);

  @ServiceActivator(inputChannel = "orderRequestInputChannel")
  void process(@Payload Order order) {
    logger.info(order.getId());
  }
}
```

### Message Handler and Message Gateway

To send the message to a topic, you can use `PubSubMessageHandler` to bind it to a channel by using the `ServiceActivator` annotation.

```java
@Bean
@ServiceActivator(inputChannel = "ordersRequestOutputChannel")
public MessageHandler ordersOutputMessageHandler() {
  return new PubSubMessageHandler(pubSubTemplate, "orders");
}
```

With Spring Integration Message Gateway, you can also bind a gateway method to a channel that's handled by the `PubSubMessageHandler`.

```java
@MessagingGateway
public interface OrdersGateway {
  @Gateway(requestChannel = "ordersRequestOutputChannel")
  void sendOrder(Order order);
}
```

Now you can send a message to the Pub/Sub Topic by using an instance of the Gateway.

```java
@Bean
ApplicationRunner sendOrder(OrderGateway gateway) {
  return (args) -> {
    Order order = new Order();
    order.setId(UUID.randomUUID().toString());
    gateway.sendOrder(order);
  };
}
```

### Integration Flow

Last but not least, you can create an entire message flow, with patterns such as Retry and Rate Limiting, and processing the message by creating a new [Integration Flow](https://docs.spring.io/spring-integration/reference/html/dsl.html).

### Samples

* [Spring Integration with Pub/Sub sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-integration-pubsub-sample)
* [Spring Integration with Pub/Sub and JSON payload sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-integration-pubsub-json-sample)

## Spring Cloud Stream

Spring Cloud Stream allows you to write event-driven microservices by simply implementing well known Java functional interfaces such as `Function`, `Consumer`, and `Supplier`. Messaging infrastructure (such as a Pub/Sub Topic or Subscription) can be bound to these functions at the runtime.

### Dependency

Spring Cloud Stream depends on Spring Integration. In addition, add the Pub/Sub Stream Binder.

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-gcp-pubsub-stream-binder</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.cloud', name: 'spring-cloud-stream'
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-pubsub-stream-binder'
```

{% endtab %}
{% endtabs %}

### Consume Messages

#### Consumer

A Spring Cloud Stream consumer to consume messages is simply a Java `Consumer`.

```java
@Bean
public Consumer<Order> processOrder() {
  return order -> {
    logger.info(order.getId());
    };
};
```

#### Binding

You can bind the `processOrder` consumer with `applications.properties` configuration. See [Spring Cloud Streams documentation](https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/current/reference/html/spring-cloud-stream.html#_binding_and_binding_names) on the binding naming convention, where `processOrder` becomes `processorOrder-in-0`.

```
spring.cloud.stream.bindings.processOrder-in-0.destination=orders
spring.cloud.stream.bindings.processOrder-in-0.group=orders-processor-group

# For development use, but not recommended for production.
spring.cloud.stream.gcp.pubsub.default.consumer.auto-create-resources=true
```

A Spring Cloud Streams Consumer Group is mapped to a subscription, with the naming convention of `[destination-name].[consumer-group-name]`. So in this example, a subscription named `orders.orders-processor-group` will be automatically created.

### Consume and Produce Message

If your consumer need to also produce a message to another topic, you can implement a `Function`.

#### Function

```java
@Bean
public Function<Order, String> processOrder() {
    return order -> {
      logger.info(order.getId());
      return order.getId();
    };
};
```

#### Binding

The output of the function can be forwarded to the next destination/topic.

```
spring.cloud.stream.bindings.processOrder-in-0.destination=orders
spring.cloud.stream.bindings.processOrder-in-0.group=orders-processor-group
spring.cloud.stream.bindings.processOrder-out-0.destination=order-processed

# For development use, but not recommended for production.
spring.cloud.stream.gcp.pubsub.default.consumer.auto-create-resources=true
```

### Produce Messages

If you need to continuously produce messages, then you can implement `Supplier`. Supplier can be used in two ways, either supply the object itself, or supply a `Flux` that can then continuously emit new messages. Read Spring Cloud Streams documentation for more information.

#### Supplier

```java
@Bean
Supplier<Flux<Order>> ordersToProcess() {
    return () -> Flux.from(e -> {
        while (true) {
            try {
                Order order = new Order();
                order.setId(UUID.randomUUID().toString());
                e.onNext(order);
                Thread.sleep(1000L);
            } catch (InterruptedException interruptedException) {
            }
      }
    });
}
```

#### Binding

The output of the supplier can be sent to the destination/topic.

```
spring.cloud.stream.bindings.ordersToProcess-out-0.destination=order-processed

# For development use, but not recommended for production.
spring.cloud.stream.gcp.pubsub.default.consumer.auto-create-resources=true
```

This will send the output of the supplier to the `order-processed` topic.

### Samples

* [Spring Cloud GCP Pub/Sub Stream Binder sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-pubsub-binder-sample)
* [Spring Cloud GCP Pub/Sub Stream Binder (no annotations) sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-pubsub-stream-binder-functional-sample)


# Kafka

Google Cloud does not have first-party managed Kafka service. For messaging, you can mostly use Cloud Pub/Sub. If you need capabilities of Kafka, then you can run Kafka cluster either as a third-party managed service (e.g., from Confluent Cloud), or run it on Kubernetes with an operator.

## Confluent Cloud

Confluent can create managed Kafka clusters using [Confluent Cloud](https://www.confluent.io/confluent-cloud/) on Google Cloud. Check out the [Confluent Cloud's Quickstart documentation](https://docs.confluent.io/current/quickstart/cloud-quickstart/index.html) for more information.

## Confluent Operator

If you want to run Confluent's Kafka platform yourself, you can use the [Confluent Operator](https://docs.confluent.io/current/installation/operator/index.html), which can provision Kafka clusters on Kubernetes Engine. See [Confluent Platform on Google Kubernetes Engine documentation](https://docs.confluent.io/current/tutorials/examples/kubernetes/gke-base/docs/index.html#quickstart-demos-operator-gke) for more detail.

## Strimzi Operator

You can run Kafka in Kubernetes using the [Strimzi Operator](https://strimzi.io). See [Strimzi Quickstart ](https://strimzi.io/quickstarts/)documentation and the more detailed [Strimzi Quick Start Guide](https://strimzi.io/docs/operators/latest/quickstart.html) for more information. &#x20;


# Secret Management

## Cloud Secret Manager

Secret Manager is a secure and convenient storage system for API keys, passwords, certificates, and other sensitive data. Secret Manager provides a central place and single source of truth to manage, access, and audit secrets across Google Cloud.

### Enable API

```bash
gcloud services enable secretmanager.googleapis.com
```

### Create a Secret

```bash
echo -n "qwerty" | \
  gcloud secrets create order-db-password --data-file=- --replication-policy=automatic
```

### List Secrets

```bash
gcloud secrets list
```

### Delete a Secret

```bash
gcloud secrets delete order-db-password
```

### Assign IAM Permission

You can finely control CRUD permissions for an account (user account, service account, a Google Group) to a secret. See the [Secret Manager IAM access control](https://cloud.google.com/secret-manager/docs/access-control) for more information.

```bash
gcloud secrets add-iam-policy-binding --help
```

## Spring Cloud Secret Manager

You can easily get value from Secret Manager by using [Spring Cloud GCP's Secret Manager starter](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#secret-manager).

### Dependency

Add the Spring Cloud GCP Secret Manager starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-secretmanager</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-secretmanager'
```

{% endtab %}
{% endtabs %}

### Configuration

Secret Manager can be configured during Bootstrap phase, via `bootstrap.properties`. The starter automatically enables Secret Manager integration. But you can also disable it by configuring `spring.cloud.gcp.secretmanager.enabled=false` in a different Spring Boot profile.

{% hint style="info" %}
Read [Spring Cloud GCP Secret Manager configuration](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#configuration-10) documentation for more details.
{% endhint %}

### Property Source

You can access individual secrets in stored in Secret Manager by looking up property keys with the `sm://` prefix.

#### @Value Annotation

You can inject the secret value by using the `Value` annotation.

```java
@Value("sm://order-db-password") String databasePassword;
```

#### Properties Mapping

You can refer to the secret value like any other properties, and reference the secret values in a `properties` file.

{% code title="application.properties" %}

```
spring.datasource.password=${sm://order-db-password}
```

{% endcode %}

Mapping properties this way, rather than hard-coding the Secret Manager property key using `@Value` annotation can be help you utilize multiple profiles.

For example, you can have `application-dev.properties` with:

{% code title="application.properties" %}

```
spring.datasource.password=${sm://order-db-dev-password}
```

{% endcode %}

And, for production, create an `application-prod.properties` with:

{% code title="application-prod.properties" %}

```
spring.datasource.password=${sm://order-db-prod-password}
```

{% endcode %}

#### Property Key Syntax

| Form                                 | Example                                                           |
| ------------------------------------ | ----------------------------------------------------------------- |
| Short                                | `sm://order-db-password`                                          |
| Short - Versioned                    | `sm://order-db-password/1`                                        |
| Short - Project Scoped and Versioned | `sm://your-project/order-db-password/1`                           |
| Long - Project Scoped                | `sm://projects/your-project/order-db-password/1`                  |
| Long - Fully Qualified               | `sm://projects/your-project/secrets/order-db-password/versions/1` |

### Local Development

Use Spring Boot Profile to differentiate local development profile vs deployed environments. For example, for local development, you can hard-code test credentials/values, but for the cloud environment, you can use a different profile.

#### Default Profile

Configure the default profile to disable Secret Manager

{% code title="bootstrap.properties" %}

```
spring.cloud.gcp.secretmanager.enabled=false
```

{% endcode %}

Hard-code the local test credentials with the value as usual.

{% code title="application.properties" %}

```
...
spring.datasource.password=admin
```

{% endcode %}

#### Production Profile

Configure the production profile to enable Secret Manager.

{% code title="bootstrap-prod.properties" %}

```
spring.cloud.gcp.secretmanager.enabled=true
```

{% endcode %}

Configure production profile to retrieve the credential from Secret Manager.

{% code title="application-prod.properties" %}

```
...
spring.datasource.password=${sm://order-db-prod-password}
```

{% endcode %}

Start your application with the profile, for example:

```bash
# From Maven
./mvnw spring-boot:run -Dspring-boot.run.profiles=prod

# From Java startup command
java -jar target/...jar -Dspring.profiles.active=prod
```

### Samples

* [Spring Cloud GCP Secret Manager sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-secretmanager-sample)


# Storage

## Cloud Storage

Cloud Storage provides globally unified, scalable, and highly durable object storage. You can store files in Cloud Storage without having to worry about running out of space, or managing your own filesystems.

Cloud Storage buckets can be configured to store files in a single region, dual-region, or multi-region.

| Location     | Use Case                                                                                                                                                                                                                                                                                                 |
| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Region       | Use a region to help optimize latency and network bandwidth for data consumers, such as analytics pipelines, that are grouped in the same region.                                                                                                                                                        |
| Dual-Region  | Use a dual-region when you want similar performance advantages as regions, but also want the higher availability that comes with being [geo-redundant](https://cloud.google.com/storage/docs/key-terms#geo-redundant).                                                                                   |
| Multi-Region | Use a multi-region when you want to serve content to data consumers that are outside of the Google network and distributed across large geographic areas, or when you want the higher availability that comes with being [geo-redundant](https://cloud.google.com/storage/docs/key-terms#geo-redundant). |

{% hint style="info" %}
See [Cloud Storage Bucket locations](https://cloud.google.com/storage/docs/locations) for more information.
{% endhint %}

Cloud Storage buckets can also be configured with a different Storage Class, for different access patterns to optimize cost.

| Storage Class | Use Case                                                                                                                                                                                                                                                                     |
| ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Standard      | Standard Storage is best for data that is frequently accessed ("hot" data) and/or stored for only brief periods of time.                                                                                                                                                     |
| Nearline      | Nearline Storage is a low-cost, highly durable storage service for storing infrequently accessed data. Nearline Storage is a better choice than Standard Storage in scenarios where slightly lower availability, a 30-day minimum storage duration.                          |
| Coldline      | Coldline Storage is a very-low-cost, highly durable storage service for storing infrequently accessed data. Coldline Storage is a better choice than Standard Storage or Nearline Storage in scenarios where slightly lower availability, a 90-day minimum storage duration. |
| Archive       | Archive Storage is the lowest-cost, highly durable storage service for data archiving, online backup, and disaster recovery. Unlike the "coldest" storage services offered by other Cloud providers, your data is available within milliseconds, not hours or days.          |

{% hint style="info" %}
See [Cloud Storage Classes](https://cloud.google.com/storage/docs/storage-classes) for more information.
{% endhint %}

### Enable API

```bash
gcloud services enable storage-component.googleapis.com
```

### Create a Bucket

A bucket is the top-level directory that you can add additional files and sub-directories into. A bucket name must be globally unique. For example, create a bucket with the same name as your Google Cloud project.

```bash
PROJECT_ID=$(gcloud config get-value project)
gsutil mb gs://$PROJECT_ID
```

### Copy a File

```bash
echo "Hello World" > hello.txt
gsutil cp hello.txt gs://$PROJECT_ID
```

### Delete a File

```bash
gsutil rm gs://$PROJECT_ID/hello.txt
```

## Spring Resource

With Spring Cloud GCP, you can use [Spring Resource](https://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/resources.html) to perform store and retrieve file from Cloud Storage.

### Dependency

Add the Spring Cloud GCP Storage starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-storage</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy

compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-storage'
```

{% endtab %}
{% endtabs %}

### Configuration

There is no explicit configuration required if you use the automatic authentication and project ID detection. I.e., if you already logged in locally with `gcloud` command line, then it'll automatically access buckets that you have access to.

{% hint style="info" %}
Notice that there is no explicit configuration for username/password. Cloud Storage authentication uses the GCP credential (either your user credential, or Service Account credential), and authorization is configured via Identity Access Management (IAM).
{% endhint %}

### Storage Client

The starter automatically creates a pre-configured `Storage` bean that provides raw-access to Google Cloud Storage.

```java
@Bean
ApplicationRunner storageRunner(Storage storage, GcpProjectIdProvider projectIdProvider) {
  return (args) -> {
    Page<Blob> list = storage.list(projectIdProvider.getProjectId());
    list.iterateAll().forEach(blob -> System.out.println(blob.getName()));
  };
}
```

### Resource URI

You can address a Cloud Storage file by using the resource URI prefixed with `gs://`. The fully qualified URI is of the form: `gs://project-id/path/to/file`.

### Read a file

You can open the `Resource` using `ApplicationContext`. Then read the content from `InputStream`. You must `close` the stream when you are done (or wrap with try-with-resource since it's auto-closeable).

```java
@Bean
ApplicationRunner runner(ApplicationContext ctx, GcpProjectIdProvider projectIdProvider) {
  return (args) -> {
    WritableResource resource = (WritableResource) ctx
        .getResource(String.format("gs://%s/hello.txt", projectIdProvider.getProjectId()));
    try (PrintWriter writer = new PrintWriter(resource.getOutputStream())) {
      writer.println("Hello World!");
    }
  };
}
```

### Write a file

You can open the `Resource` using `ApplicationContext`. Then cast the `Resource` to a `WritableResource`, and then use the `OutputStream` to write the content. Lastly, you must `close` the stream in order for the file to write (or wrap with try-with-resource since it's auto-closeable).

```java
@Bean
ApplicationRunner readRunner(ApplicationContext ctx, GcpProjectIdProvider projectIdProvider) {
  return (args) -> {
    Resource resource = ctx
        .getResource(String.format("gs://%s/hello.txt", projectIdProvider.getProjectId()));
    try (InputStreamReader reader = new InputStreamReader(resource.getInputStream())) {
      BufferedReader bufferedReader = new BufferedReader(reader);
      bufferedReader.lines().forEach(System.out::println);
    }
  };
}
```

### Samples

* [Spring Cloud GCP Storage sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-storage-resource-sample)

## Spring Integration

You can channel adapters for Google Cloud Storage to read and write files to Google Cloud Storage through `MessageChannels`.

### Dependency

Add both the Spring Cloud GCP Storage starter, and Spring Integration File component.

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-storage</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.integration</groupId>
    <artifactId>spring-integration-file</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy

compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-storage'
compile group: 'org.springframework.integration', name: 'spring-integration-file'
```

{% endtab %}
{% endtabs %}

### Inbound Channel Adapter

#### Inbound File Synchronizer

File-based integration with files typically requires polling a directory that contains the new files.  In Spring Integration, this is configured through an `InboundFileSynchronizer`. Use `GcsInboundFileSyncronizer` to create a `MessageSource` and adapt it to a `MessageChannel`.

{% hint style="warning" %}
The files are temporarily stored in a directory in the local file system.
{% endhint %}

```java
@Bean
public MessageChannel gcsInputChannel() {
  return MessageChannels.direct().get();
}

@Bean
@InboundChannelAdapter(channel = "gcsInputChannel", poller = @Poller(fixedDelay = "5000"))
public MessageSource<File> (Storage gcs, GcpProjectIdProvider projectIdProvider)
    throws IOException {
  GcsInboundFileSynchronizer synchronizer = new GcsInboundFileSynchronizer(gcs);
  synchronizer.setRemoteDirectory(projectIdProvider.getProjectId());

  GcsInboundFileSynchronizingMessageSource messageSource =
          new GcsInboundFileSynchronizingMessageSource(synchronizer);
  File localDirectory = Files.createTempDirectory("gcs");
  messageSource.setLocalDirectory(localDirectory);

  return messageSource;
}
```

#### Streaming Message Source

For most use cases, you should use the streaming message source, which does not require files to be stored in the file system.

```java
@Bean
public MessageChannel gcsInputChannel() {
  return MessageChannels.direct().get();
}

@Bean
@InboundChannelAdapter(channel = "gcsInputChannel", poller = @Poller(fixedDelay = "5000"))
public MessageSource<InputStream> streamingAdapter(Storage gcs, GcpProjectIdProvider projectIdProvider) {
  GcsStreamingMessageSource adapter =
          new GcsStreamingMessageSource(new GcsRemoteFileTemplate(new GcsSessionFactory(gcs)));
  adapter.setRemoteDirectory(projectIdProvider.getProjectId());
  return adapter;
}
```

### Outbound Channel Adapter

The outbound channel adapter allows files to be written to Google Cloud Storage. When it receives a `Message` containing a payload of type `File`, it writes that file to the Google Cloud Storage bucket specified in the adapter.

```java
@Bean
@ServiceActivator(inputChannel = "gcsOutputChannel")
public MessageHandler outboundChannelAdapter(Storage gcs, GcpProjectIdProvider projectIdProvider) {
  GcsMessageHandler outboundChannelAdapter = new GcsMessageHandler(new GcsSessionFactory(gcs));
  outboundChannelAdapter.setRemoteDirectoryExpression(new ValueExpression<>(projectIdProvider.getProjectId()));

  return outboundChannelAdapter;
}
```

### Samples

* [Spring Cloud Integration with Cloud Storage Sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-integration-storage-sample)


# Cache

## Memorystore

Memory Store is a fully managed in-memory cache service having protocol compatibility with Redis and Memcached. See documentations for [Memorystore Redis](https://cloud.google.com/memorystore/docs/redis/) and [Memorystore Memcached](https://cloud.google.com/memorystore/docs/memcached) (beta) for more information.

#### Zonal Resource

Memorystore is zonal, meaning each Memorystore instance is only available within a zone, or accessible from other zones within the same region. For high-availability, create a **Standard** tier instance, which includes a failover replica in a separate zone.

#### Connectivity

All Memorystore instances can only be accessed by a private IP on a VPC network. You can connect to a Memorystore instance from different Google Cloud Platform computing resources differently. In general, VM-based products (Compute Engine, Kubernetes Engine, and App Engine Flexible) requires the VM to be on the same VPC, and Serverless products requires [VPC Service Connector](https://cloud.google.com/vpc/docs/configure-serverless-vpc-access).

| Resource            | Method                                                                                             |
| ------------------- | -------------------------------------------------------------------------------------------------- |
| Compute Engine      | Out-of-the-box                                                                                     |
| Kubernetes Engine   | [VPC-Native cluster](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips)             |
| App Engine Flexible | [Additional Configuration](https://cloud.google.com/appengine/docs/flexible/java/using-shared-vpc) |
| App Engine Standard | [VPC Service Connector](https://cloud.google.com/appengine/docs/standard/java11/connecting-vpc)    |
| Cloud Run           | [VPC Service Connector](https://cloud.google.com/run/docs/configuring/connecting-vpc)              |
| Cloud Function      | [VPC Service Connector](https://cloud.google.com/functions/docs/networking/connecting-vpc)         |

#### Protocol Compatibility

Because Memorystore is protocol compatible. You can use existing Spring Boot integration with Redis and Memcached as-is.

{% content-ref url="/pages/-MADiJMKwGFHIUmIY9Kl" %}
[Memorystore Redis](/app-dev/cloud-services/cache/memorystore-redis)
{% endcontent-ref %}

{% content-ref url="/pages/-MADiM\_9RTTfDh\_qFZXY" %}
[Memorystore Memcached (beta)](/app-dev/cloud-services/cache/memorystore-memcached)
{% endcontent-ref %}


# Memorystore Redis

## Memorystore Redis Instance

### Enable API

```bash
gcloud services enable servicenetworking.googleapis.com
gcloud services enable redis.googleapis.com
```

{% hint style="warning" %}
Enabling this API may take a few minutes.
{% endhint %}

### Create an Instance

Create an instance and attach it to the default VPC.

```bash
gcloud redis instances create orders-cache \
  --size=1 --region=us-central1
```

{% hint style="warning" %}
Creating a Redis instance may take a few minutes.
{% endhint %}

### Get Instance IP Address

```bash
gcloud redis instances describe orders-cache \
  --region=us-central1 --format="value(host)"
```

{% hint style="warning" %}
The IP address is not a static IP address. If you create the instance, the IP address may be different.
{% endhint %}

### Connect to Instance

See [Memorystore connectivity options](/app-dev/cloud-services/cache#connectivity) to see how to connect to a Memorystore instance from different computing environments.

| Computing Environment |                                                                                             |
| --------------------- | ------------------------------------------------------------------------------------------- |
| Compute Engine        | [Guide](https://cloud.google.com/memorystore/docs/redis/connect-redis-instance-gce)         |
| Kubernetes Engine     | [Guide](https://cloud.google.com/memorystore/docs/redis/connect-redis-instance-gke)         |
| App Engine Flexible   | [Guide](https://cloud.google.com/memorystore/docs/redis/connect-redis-instance-flex#java_1) |
| App Engine Standard   | [Guide](https://cloud.google.com/memorystore/docs/redis/connect-redis-instance-standard)    |
| Cloud Run             | [Guide](https://cloud.google.com/run/docs/configuring/connecting-vpc)                       |
| Cloud Function        | [Guide](https://cloud.google.com/memorystore/docs/redis/connect-redis-instance-functions)   |

You can test quickly by creating a Compute Engine instance in a zone within the same region:

```bash
gcloud compute instances create test-memorystore-vm --zone=us-central1-c
```

SSH into the machine:

```bash
gcloud compute ssh test-memorystore-vm --zone=us-central1-c
```

Install `redis-cli`:

```bash
sudo apt-get update && sudo apt-get install -y redis-tools
```

Connect to the instance:

```bash
redis-cli -h <MEMORYSTORE_REDIS_IP>
```

You can try different Redis commands, for example:

```
> PING
PONG
> SET greeting Hello
OK
> GET greeting
"Hello"
```

{% hint style="info" %}
See [redis-cli documentation](https://redis.io/topics/rediscli) for more information.
{% endhint %}

## Spring Boot Cache

Spring Boot can [use Redis](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-caching-provider-redis) to [cache with annotations](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-caching).

### Dependency

Add the Spring Data Redis starter:

{% tabs %}
{% tab title="Maven" %}

```bash
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```bash
compile group: 'org.springframework.cloud', name: 'spring-boot-starter-cache'
compile group: 'org.springframework.cloud', name: 'spring-boot-starter-data-redis'
```

{% endtab %}
{% endtabs %}

### Configuration

Configure the Redis instance to connect to:

{% code title="application.properties" %}

```bash
spring.redis.host=<MEMORYSTORE_REDIS_IP>

# Configure default TTL, e.g., 10 minutes
spring.cache.redis.time-to-live=600000
```

{% endcode %}

### Enable Caching

Turn on caching capability explicitly with the `@EnableCaching` annotation:

```java
@SpringBootApplication
@EnableCaching
class DemoApplication {
  ...
}
```

### Cacheable

Once you configured the Spring Boot with Redis and enabled caching, you can use the `@Cacheable` annotation to cache return values.

```java
@Service
class OrderService {
  private final OrderRepository orderRepository;
  
  public OrderService(OrderRepository orderRepository) {
    this.orderRepository = orderRepository;
  }
  
  @Cacheable("order")
  public Order getOrder(Long id) {
    orderRepository.findById(id);
  }
}
```

{% hint style="info" %}
Read Spring Boot documentation on [Cacheable](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-caching) and [Redis](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-caching-provider-redis) for more information.
{% endhint %}

## Spring Boot Session

Spring Boot can [use Redis for session data](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-session) with [Spring Session Data Redis](https://docs.spring.io/spring-session/docs/2.3.0.RELEASE/reference/html5/#httpsession-redis).

### Dependency

Add the Spring Data Spanner starter:

{% tabs %}
{% tab title="Maven" %}

```bash
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-session-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```bash
compile group: 'org.springframework.cloud', name: 'spring-session-data-redis'
compile group: 'org.springframework.cloud', name: 'spring-boot-starter-data-redis'
```

{% endtab %}
{% endtabs %}

### Configuration

Configure the Redis instance to connect to:

{% code title="application.properties" %}

```bash
spring.redis.host=<MEMORYSTORE_REDIS_IP>

# Configure default TTL, e.g., 10 minutes
spring.cache.redis.time-to-live=600000
```

{% endcode %}

### Enable HTTP Session

Turn on caching capability explicitly with the `@EnableSpringHttpSession` annotation:

```java
@SpringBootApplication
@EnableSpringHttpSession
class DemoApplication {
  ...
}
```

### Samples

* [Spring Boot Session Data Redis sample](https://github.com/spring-projects/spring-session/tree/master/spring-session-samples/spring-session-sample-boot-redis-simple)


# Memorystore Memcached (beta)

## Memorystore Memcached Instance

### Enable API

```bash
gcloud services enable servicenetworking.googleapis.com
gcloud services enable memcache.googleapis.com
```

{% hint style="warning" %}
Enabling this API may take a few minutes.
{% endhint %}

### Enable Private Service Access

Memorystore Memcached requires Private Services Access to be enabled. See [Establishing a private services access connection](https://cloud.google.com/memorystore/docs/memcached/establishing-connection) documentation for more information.

Reserve an IP address range to be used in a VPC, so that the Memcached instance's IP address can be allocated within this range:

```bash
gcloud beta compute addresses create reserved-range \
  --global --prefix-length=24 \
  --description=description --network=default \
  --purpose=vpc_peering
```

{% hint style="info" %}
This is a simplified range creation on the `default` VPC network. In a production environment, you should verify what the range should be and which VPC network to allocate in.
{% endhint %}

Establish peering so that Memorystore can allocate the IP address in the reserved range in the VPC.

```bash
gcloud services vpc-peerings connect \
  --service=servicenetworking.googleapis.com \
  --ranges=reserved-range --network=default
```

### Create an Instance

Create an instance and attach it to the default VPC.

```bash
gcloud beta memcache instances create orders-cache \
  --node-count=1 --node-cpu=1 --node-memory=1G --region=us-central1
```

{% hint style="warning" %}
Creating a Memcached instance may take a few minutes.
{% endhint %}

### Get Instance IP Address

```bash
gcloud beta memcache instances describe orders-cache \
  --region=us-central1 --format="value(memcacheNodes.host)"
```

{% hint style="warning" %}
The IP address is not a static IP address. If you create the instance, the IP address may be different.
{% endhint %}

### Connect to Instance

See [Memorystore connectivity options](/app-dev/cloud-services/cache#connectivity) to see how to connect to a Memorystore instance from different computing environments.

| Computing Environment |                                                                                                                                              |
| --------------------- | -------------------------------------------------------------------------------------------------------------------------------------------- |
| Compute Engine        | [Guide](https://cloud.google.com/memorystore/docs/memcached/connecting-memcached-instance#connecting-compute-engine)                         |
| Kubernetes Engine     | [Guide](https://cloud.google.com/memorystore/docs/memcached/connecting-memcached-instance#connecting_to_a_memcached_instance_from_a_cluster) |
| App Engine Flexible   | [Additional Configuration](https://cloud.google.com/appengine/docs/flexible/java/using-shared-vpc)                                           |
| App Engine Standard   | [VPC Service Connector](https://cloud.google.com/appengine/docs/standard/java11/connecting-vpc)                                              |
| Cloud Run             | [VPC Service Connector](https://cloud.google.com/run/docs/configuring/connecting-vpc)                                                        |
| Cloud Function        | [VPC Service Connector](https://cloud.google.com/functions/docs/networking/connecting-vpc)                                                   |

You can test quickly by creating a Compute Engine instance in a zone within the same region:

```bash
gcloud compute instances create test-memorystore-vm --zone=us-central1-c
```

SSH into the machine:

```bash
gcloud compute ssh test-memorystore-vm --zone=us-central1-c
```

Install `redis-cli`:

```bash
sudo apt-get update && sudo apt-get install -y telnet
```

Connect to the instance:

```bash
telnet <MEMORYSTORE_MEMCACHED_IP> 11211
```

You can try different Memcached commands, for example, `stats`:

```
Trying ...
Connected to 10.111.98.4.
Escape character is '^]'.
stats
STAT pid 1
STAT uptime 1020
STAT time 1594348128
...
END
quit
Connection closed by foreign host.
```

{% hint style="info" %}
See [Memcached commands](https://github.com/memcached/memcached/wiki/Commands) for more information.
{% endhint %}

## Spring Boot Cache

Spring Boot does not have a built-in Memcached support. However you can use a 3rd party Memcached starter to provide Spring Boot cache support, e.g.:

* <https://github.com/sixhours-team/memcached-spring-boot>

### Dependency

Add the 3rd party Memcached Spring Boot starter:

{% tabs %}
{% tab title="Maven" %}

```bash
<dependency>
    <groupId>io.sixhours</groupId>
    <artifactId>memcached-spring-boot-starter</artifactId>
    <version>2.1.2</version>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```bash
compile group: 'io.sixhours', name: 'memcached-spring-boot-starter:2.1.2'
```

{% endtab %}
{% endtabs %}

### Configuration

Configure the Memcached instance to connect to:

{% code title="application.properties" %}

```bash
memcached.cache.servers=<MEMORYSTORE_MEMCACHED_IP>:11211
memcached.cache.provider=static
```

{% endcode %}

### Enable Caching

Turn on caching capability explicitly with the `@EnableCaching` annotation:

```java
@SpringBootApplication
@EnableCaching
class DemoApplication {
  ...
}
```

### Cacheable

Once you configured the Spring Boot with Redis and enabled caching, you can use the `@Cacheable` annotation to cache return values.

```java
@Service
class OrderService {
  private final OrderRepository orderRepository;
  
  public OrderService(OrderRepository orderRepository) {
    this.orderRepository = orderRepository;
  }
  
  @Cacheable("order")
  public Order getOrder(Long id) {
    orderRepository.findById(id);
  }
}
```


# Other Services

Spring Cloud GCP has idiomatic integrations and starters for a number of Google Cloud services, but not all services. There may be cases where you need to use a Google Cloud client library directly. In this case, you can re-use basic bootstrapping provided by the Spring Cloud GCP, so you can have a consistent way of specifying credentials for your application.

## Dependency

Spring Cloud GCP already imports the [Google Cloud Java BOM](https://github.com/googleapis/java-cloud-bom), and it already has encoded the client library versions. So you can specify any Google Cloud client library without explicitly specifying a version. This is great to ensure that you are using a compatible version of a Google Cloud client library.

For example, to add Container Analysis client library:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
  <groupId>com.google.cloud</groupId>
  <artifactId>google-cloud-containeranalysis</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'com.google.cloud', name: 'google-cloud-containeranalysis'
```

{% endtab %}
{% endtabs %}

## Credentials

You need Google Cloud credentials to access any services. [Spring Cloud GCP Core](https://docs.spring.io/spring-cloud-gcp/docs/1.2.5.RELEASE/reference/html/#credentials) produces a `CredentailsProvider` bean so you can re-use the same credentials.

Usually you only need a singleton instance of the client library, so it makes sense to configuring it as a Spring Bean. Most client libraries needs to be shutdown gracefully, so you should specify the `destroyMethod` as well:

```java
@Bean(destroyMethod = "shutdownNow")
ContainerAnalysisClient containerAnalysisClient(CredentialsProvider credentialsProvider) throws IOException {
  return ContainerAnalysisClient.create(
    ContainerAnalysisSettings.newBuilder()
      .setCredentialsProvider(credentialsProvider).build());
}
```

## Project ID

In rare cases, you may want to know which Project ID you are currently configured to use by default. You can find out from the [`GcpProjectIdProvider` bean](https://docs.spring.io/spring-cloud-gcp/docs/1.2.5.RELEASE/reference/html/#project-id).


# Observability


# Trace

## Cloud Trace

Cloud Trace is a managed distributed tracing system that collects latency data from your applications and displays it in the Google Cloud Console. You can track how requests propagate through your application and receive detailed near real-time performance insights.

### Enable API

```bash
gcloud services enable cloudtrace.googleapis.com
```

## Spring Cloud Sleuth

[Spring Cloud GCP's Trace integration](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#stackdriver-trace) uses [Spring Cloud Sleuth](https://spring.io/projects/spring-cloud-sleuth) behind the scenes to instrument and trace your application. In addition, it'll also enhance the log messages to include the current trace context (Trace ID, Span ID) for trace to log correlation.

### Dependency

Add the Spring Cloud GCP Trace starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-trace</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-trace'
```

{% endtab %}
{% endtabs %}

### Configuration

By default, Spring Cloud Sleuth samples only 10% of the requests. I.e., 1 in 10 requests may have traces propagated to the trace server (Cloud Trace). In a non-production environment, you may want to see all of the trace. You can adjust the sampling rate using Spring Cloud Sleuth's properties:

{% code title="application.properties" %}

```
# Set sampler probability to 100%
spring.sleuth.sampler.probability=1.0
```

{% endcode %}

{% hint style="info" %}
Notice that there is no explicit configuration for username/password. Cloud Trace authentication uses the GCP credential (either your user credential, or Service Account credential), and authorization is configured via Identity Access Management (IAM).
{% endhint %}

### Instrumentation

Spring Cloud Sleuth automatically adds trace instrumentation to commonly used components, such as [incoming HTTP requests](https://docs.spring.io/spring-cloud-sleuth/docs/2.2.x-SNAPSHOT/reference/html/#http-integration), and incoming [messages from Spring Integration](https://docs.spring.io/spring-cloud-sleuth/docs/2.2.x-SNAPSHOT/reference/html/#messaging-2). See [Spring Cloud Sleuth Integrations documentation](https://docs.spring.io/spring-cloud-sleuth/docs/2.2.x-SNAPSHOT/reference/html/#integrations) for more details.

#### Web

Spring Cloud Sleuth will automatically trace incoming requests from WebMVC, or WebFlux as-is.

```java
@RestController
class OrderController {
  private final OrderRepository orderRepository;

  OrderController(OrderRepository orderService) {
    this.orderRepository = orderRepository;
  }

  @GetMapping("/order/{orderId}")
  public Order getOrder(@PathParam String orderId) {
    return orderRepository.findById(orderId);
  }
}
```

{% hint style="info" %}
In this example, an incoming request to `/order/{orderId}` endpoint will be automatically traced, and the traces will be propagated to Cloud Trace based on the sampler probability.
{% endhint %}

#### Messaging

Spring Cloud Sleuth will automatically trace incoming messages and handlers when using Spring Integration

#### Custom Spans

If there is a piece of code/method that you want to break out into it's own span, you can use Spring Cloud Sleuth's `@NewSpan` annotation. See [Spring Cloud Sleuth's Creating New Span documentation](https://docs.spring.io/spring-cloud-sleuth/docs/2.2.x-SNAPSHOT/reference/html/#creating-new-spans).

```java
@Service
class OrderService {
    private final OrderRepository orderRepository;

    ...

  @NewSpan
    @Transactional
    Order createOrder(Order order) {
      ...
        return orderRepository.save(order);
    }
}
```

#### Tagging Spans

You can associate additional data to a Span (a tag) via annotation. See [Spring Cloud Sleuth's Continuing Span documentation](https://docs.spring.io/spring-cloud-sleuth/docs/2.2.x-SNAPSHOT/reference/html/#continuing-spans-2).

```java
@Service
class OrderService {
    private final OrderRepository orderRepository;

  ...

  @NewSpan
    @Transactional
    Order getOrder(@SpanTag("orderId") String id) {
        return orderRepository.findById(id);
    }
}
```

### Propagation

Spring Cloud Sleuth automatically propagates the trace context to a remote system (e.g., via HTTP request, or messaging) when using `RestTemplate`, `WebClient`, Spring Integration, and more. See [Spring Cloud Sleuth Integrations documentation](https://docs.spring.io/spring-cloud-sleuth/docs/2.2.x-SNAPSHOT/reference/html/#integrations) for more details.

#### Rest Template / WebClient

Simply create a `RestTemplate` or `WebClient` bean and Spring Cloud Sleuth will automatically add filters to propagate the trace context via HTTP headers.

```java
@Bean
RestTemplate restTemplate() {
  return new RestTemplate();
}
```

#### Messaging

When using Spring Integration, Spring Cloud Sleuth will automatically propagate trace context via message headers. For example, send a [Pub/Sub message with Spring Integration's Gateway](/app-dev/cloud-services/messaging/pubsub#spring-integration) will automatically add trace headers to the Pub/Sub message.

#### Additional Headers

Spring Cloud Sleuth uses [OpenZipkin's Brave tracer](https://github.com/openzipkin/brave), and uses [B3 propagation](https://github.com/openzipkin/b3-propagation). Over HTTP, it will automatically propagate B3 headers to HTTP headers.

When running your application in Istio, you may need to propagate [additional trace headers required by Istio](https://istio.io/latest/faq/distributed-tracing/#how-to-support-tracing), such as `x-request-id` and `x-ot-span-context`.

```
spring.sleuth.propagation-keys=x-request-id,x-ot-span-context
```

### Log / Trace Correlation

Spring Cloud Sleuth automatically associate each log message with the trace context (Trace ID, Span ID). When the log message is sent to Cloud Logging, you can then be able to see the log messages alongside the trace itself. See [how to configure Logback](/app-dev/observability/logging#log-trace-correlation) to achieve this.

![](https://lh3.googleusercontent.com/O6u214GgMO_GD-xNUkHVj8KTOBH6pf8-_SJP1x17QhdT9Fle3D30gjV-wuTOSSYDHWnjMqFyZmymAIroBTrxNRJGXrT6JqWRQYGVyZE0DMXRDCR4IkNxBCoAwKGnzyctcJMk7-PPBQ)

### Samples

* [Spring Cloud GCP Trace sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-trace-sample)

## Istio

If you use Istio service mesh, Istio can automatically capture service to service traces. You can use Spring Cloud Sleuth to [propagate additional trace headers](/app-dev/observability/trace#additional-headers), without any trace senders:

```
spring.sleuth.propagation-keys=x-request-id,x-ot-span-context
```

For in-application trace, you can use [Spring Cloud GCP Trace starter](/app-dev/observability/trace#spring-cloud-sleuth).

## Learn More

* [Troubleshooting and Debugging Microservices in Kubernetes](https://saturnism.me/talk/troubleshooting-debugging-microservices/)


# Logging

## Cloud Logging

Cloud Logging allows you to store, search, analyze, monitor, and alert on logging data and events from Google Cloud runtime environments and also any other on-premises or Cloud environments.

### Enable API

```bash
gcloud services enable logging.googleapis.com
```

{% hint style="info" %}
Logging API is usually enabled by default for your project.
{% endhint %}

## Centralized Logging

There are a couple of ways to send log messages to Google Cloud.

* If you are running in a Kubernetes Engine,  App Engine, Cloud Run, Cloud Functions, then logs to `STDOUT` or `STDERR` are automatically sent to Cloud Logging.
* If you are running in Compute Engine, then you can install a [Logging Agent](https://cloud.google.com/logging/docs/agent/installation).
* If you are running outside of Google Cloud runtime environment, e.g., from on-premise datacenter, or another cloud, you can:
  * Use the Cloud Logging API to send log entries to Cloud Logging
  * Use a [Logging Agent](https://cloud.google.com/logging/docs/agent/installation)
  * Use a [Fluend adapter](https://github.com/GoogleCloudPlatform/google-fluentd)

Once the log is collected by Cloud Logging, you can see:

* Search the logs
* Create metrics from logs based on criteria, to see in Cloud Monitoring, or create alerts
* Stream log entries to BigQuery, Pub/Sub, or Cloud Storage for further analysis

![](/files/-MCtO9eSTqMLbv936Tf4)

## Error Reporting

Google Cloud will automatically identify exceptions and in Error Reporting console, list recently occurring errors, in order of frequency. You can quickly identify new errors, frequent errors, and dig into details through Centralized Logging.

![Error Reporting showing an new exception in identified from the logs](/files/-MCtNT4ul8ZwDTWrTh26)

## Severity Level

Cloud Logging has [9 different log severity levels](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#LogSeverity) the log entries can associate with.

However, in all the runtime environments where logs printed `STDOUT` and `STDERR` are sent to Cloud Logging, original log entry's severity level is not retained:

* Log entries printed to `STDOUT` will have a severity level of `INFO` regardless of the original log entry level.
* Log entries printed to `STDERR` will have a severity level of `WARNING` regardless of the original log entry level.

Different runtime environments have different ways of associating the log level properly.

| Environment         | Preferred Logging                                                                                           |
| ------------------- | ----------------------------------------------------------------------------------------------------------- |
| Cloud Function      | [Use Java Logging API (JUL)](https://cloud.google.com/functions/docs/concepts/java-logging)                 |
| App Engine Standard | [Output Structured Logs in JSON format](https://cloud.google.com/logging/docs/structured-logging)           |
| Cloud Run           | [Output Structured Logs in JSON format](https://cloud.google.com/logging/docs/structured-logging)           |
| Compute Engine      | [Install Logging Agent](https://cloud.google.com/logging/docs/agent/installation), or use Cloud Logging API |
| Kubernetes Engine   | [Output Structured Logs in JSON format](https://cloud.google.com/logging/docs/structured-logging)           |

In Cloud Logging dashboard, you can see graphs with segmentation on the Severity levels:

![](/files/-MCtOyPGMfESCJ4tRRbx)

## Log / Trace Correlation

When your log messages are also associated with the same trace ID and span ID as the ones sent to Cloud Trace, then the Trace console can display the logs along side of the trace/spans views when you click **Show Logs**:

![](https://lh3.googleusercontent.com/O6u214GgMO_GD-xNUkHVj8KTOBH6pf8-_SJP1x17QhdT9Fle3D30gjV-wuTOSSYDHWnjMqFyZmymAIroBTrxNRJGXrT6JqWRQYGVyZE0DMXRDCR4IkNxBCoAwKGnzyctcJMk7-PPBQ)

## Request Log Grouping

For a HTTP-based application, it's useful to see all of the log messages related to a single request grouped together. The Log Viewer can do this if your log messages meet the following criteria:

* A "request" log entry that contains the `httpRequest` information that contains request information such as the URL, response code, latency, etc. This is usually produced by Google Cloud HTTP Load Balancer.
* Associate each log message with the same Trace ID. This is usually generated by the Google Cloud HTTP Load Balancer.

When these conditions are met, then the Log Viewer can group these log entries together, with the top-level log that contains the `httpRequest` information:

![Log entries are grouped under the top-level request log](/files/-MHm-wfRMa-PPzXsOTgt)

When using Google Cloud HTTP Load Balancer (default if you are running in App Engine or Cloud Run), the Load Balancer will automatically:

* Produce the request log with the `httpRequest` information.
* Generate a Trace ID and it's propagated to your application via the `X-Cloud-Trace-Context` HTTP header.

You can use [Spring Cloud GCP Trace starter](/app-dev/observability/trace#cloud-trace) to automatically read and use this trace header. In addition, use the [Spring Cloud GCP Logging starter](/app-dev/observability/logging#logback) to automatically associate log entries with the Trace ID.

![Log entries are grouped under the top-level request log](/files/-MHm-wfRMa-PPzXsOTgt)

If you are not using a Google Cloud HTTP Load Balancer, then you can produce the `httpRequest` log manually. See [Other Loggers' JSON Logging](/app-dev/observability/logging#json-logging) section.

## Logback

Spring Boot uses [Slf4J](http://www.slf4j.org/) logging API and [Logback](http://logback.qos.ch/) logger by default. You can user [Spring Cloud GCP's Logging Starter](https://cloud.spring.io/spring-cloud-static/spring-cloud-gcp/current/reference/html/#stackdriver-logging) to use pre-configured Logback appenders to produce Structured JSON logs, or send the log via the Cloud Logging API.

### Dependency

Add the Spring Cloud GCP Trace starter:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-gcp-starter-logging</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy
compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-logging'
```

{% endtab %}
{% endtabs %}

### Configuration

Configure Logback to use the additional appenders, by adding a `logback-spring.xml` file, and import the appender configuration:

{% code title="logback-spring.xml" %}

```markup
<configuration>
  <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
  <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
  <include resource="org/springframework/cloud/gcp/logging/logback-appender.xml"/>

  ...
</configuration>
```

{% endcode %}

### Log with Cloud Logging API

{% code title="logback-spring.xml" %}

```markup
<configuration>
  <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
  <include resource="org/springframework/boot/logging/logback/console-appender.xml"/>
  <include resource="org/springframework/cloud/gcp/logging/logback-appender.xml"/>

  <root level="INFO">
    <appender-ref ref="CONSOLE"/>
    <appender-ref ref="STACKDRIVER"/>
  </root>
</configuration>
```

{% endcode %}

{% hint style="info" %}
Notice that there is no explicit configuration for username/password. Cloud Logging authentication uses the GCP credential (either your user credential, or Service Account credential), and authorization is configured via Identity Access Management (IAM).
{% endhint %}

### Log with Structured JSON Logging

{% code title="logback-spring.xml" %}

```markup
<configuration>
  <include resource="org/springframework/boot/logging/logback/defaults.xml"/>
  <include resource="org/springframework/cloud/gcp/logging/logback-json-appender.xml"/>

  <root level="INFO">
    <appender-ref ref="CONSOLE_JSON"/>
  </root>
</configuration>
```

{% endcode %}

#### Use Different Appenders with Profile

It's useful to configure different appenders when running in [Spring Boot profiles](https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-profiles). For example, in local/dev environments, simply output regular log entries to `STDOUT`, in staging/production environments, use Structured JSON Logging.

{% hint style="info" %}
See [Spring Boot Logging documentation](https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#profile-specific-configuration) and Spring Boot profiles for more details.
{% endhint %}

For example, to configure default profile to use regular logging, and higher environments with Structured JSON Logging:

{% code title="logback-spring.xml" %}

```markup
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />

    <springProfile name="qa | staging | prod">
        <include resource="org/springframework/cloud/gcp/logging/logback-json-appender.xml"/>
        <root level="INFO">
            <appender-ref ref="CONSOLE_JSON"/>
        </root>
    </springProfile>
    <springProfile name="default | dev">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
        </root>
    </springProfile>
</configuration>
```

{% endcode %}

This sample application allows you to:

* If no profile is specified, then the `default` profile is used, then use the default `CONSOLE` appender.
* If you specify `dev` profile, then use the `CONSOLE` appender
* If you specify `qa`, `staging`, `prod` profile, then it'll output to Structured JSON Logging.

Alternatively, you can also mix and match the profiles with more generic profiles:

{% code title="logback-spring.xml" %}

```markup
<configuration>
    <include resource="org/springframework/boot/logging/logback/defaults.xml" />
    <include resource="org/springframework/boot/logging/logback/console-appender.xml" />

    <springProfile name="logging-json">
        <include resource="org/springframework/cloud/gcp/logging/logback-json-appender.xml"/>
        <root level="INFO">
            <appender-ref ref="CONSOLE_JSON"/>
        </root>
    </springProfile>
    <springProfile name="logging-api">
        <include resource="org/springframework/cloud/gcp/logging/logback-appender.xml"/>
        <root level="INFO">
            <appender-ref ref="STACKDRIVER"/>
        </root>
    </springProfile>
    <springProfile name="logging-console | default">
        <root level="INFO">
            <appender-ref ref="CONSOLE"/>
        </root>
    </springProfile>
</configuration>
```

{% endcode %}

This sample application allows you to:

* If no profile is specified, then the `default` profile is used, then use the default `CONSOLE` appender.
* If you specify `logging-json` profile, it'll output to Structured JSON Logging.
* If you specify `logging-api` profile, it'll send the logs via the API.
* If you speicfy `default` and `logging-api` profiles, then it'll use the default `CONSOLE` appender and send the logs via the API.

### Log / Trace Correlation

When using Structured JSON Logging or logging using the API, then Spring Cloud Sleuth's trace context (Trace ID, Span ID) are automatically added to the log metadata. If you explore the log message in the Cloud Logging Console, you can see the `trace` attribute and the `spanId` attribute are both populated with the correct values:

![](https://lh3.googleusercontent.com/4Z0u20hq8WiwuueOU-DqDG58hqbs2m6IG3jCOZpmrNTu8vJjN8sfcjBbbhiDfmQI1MZf_IeJ9x8tnLSUapoiR8kM5M8fqu7avXucQ4JgU3FoWEWu_NbzL8nd1l7kbXdfzqJkiAYfeA)

In the Cloud Trace console, check **Show Logs**, then you can then see the logs alongside the trace itself:

![](https://lh3.googleusercontent.com/O6u214GgMO_GD-xNUkHVj8KTOBH6pf8-_SJP1x17QhdT9Fle3D30gjV-wuTOSSYDHWnjMqFyZmymAIroBTrxNRJGXrT6JqWRQYGVyZE0DMXRDCR4IkNxBCoAwKGnzyctcJMk7-PPBQ)

### Request Log Grouping

In addition to Trace / Log Correlation, if the application is running in Cloud Run, App Engine, or any environment that's fronted by a GCP's HTTP load balancer, then the log entries can be grouped into the top level load balancer produced request log.

![Log entries are grouped under the top-level request log](/files/-MHm-wfRMa-PPzXsOTgt)

### Samples

* [Spring Cloud GCP Logging sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-logging-sample)

## Other Loggers

It's highly recommended that you use the default logger (Logback) with Spring Boot, to take advantage of Spring Cloud GCP features. If you do use other Loggers, you may be able to configure logging to API with different appenders/handlers.

### JSON Logging

The official [Structured Logging documentation](https://cloud.google.com/logging/docs/structured-logging) suggests that you output the JSON format according to the [LogEntry](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry). Rather than producing the entire `LogEntry`, you can produce a more simplified JSON payload:

```javascript
{
  "message": "My log message",
  "severity": "WARN"
}
```

Cloud Logging agents will automatically extrapolate the `severity` attribute, and also fill in the rest of the `LogEntry` fields so that you don't need to.

If you want to add the trace ID or span ID, you can do so by adding [Special Fields](https://cloud.google.com/logging/docs/agent/configuration#special-fields) to the JSON payload. These special fields will be automatically extrapolated to the `LogEntry`.

```bash
{
  "message": "My log message",
  "severity": "WARN",
  "logging.googleapis.com/trace": "projects/PROJECT_ID/traces/TRACE_ID",
  "logging.googleapis.com/spanId": "SPAN_ID"
}
```

If you want to associate HTTP request information (especially if you are not using a Google Cloud Load Balancer), then you can also add the [`httpRequest`](https://cloud.google.com/logging/docs/reference/v2/rest/v2/LogEntry#HttpRequest) field:

```bash
{
  "message": "My log message",
  "severity": "WARN",
  "logging.googleapis.com/trace": "projects/PROJECT_ID/traces/TRACE_ID",
  "logging.googleapis.com/spanId": "SPAN_ID",
  "httpRequest": {
    ...
  }
}
```

### API Logging

#### Java Logging API (JUL)

See [Cloud Logging handler for Java Logging API](https://cloud.google.com/logging/docs/setup/java#the_javautillogging_handler).

#### Apache Commons Logging (JCL)

There is no ready-to-use appender to Cloud Logging. But you can [bridge it to Slf4J](http://www.slf4j.org/legacy.html), or [bridge it to Java Logging API](http://commons.apache.org/proper/commons-logging/apidocs/org/apache/commons/logging/impl/Jdk14Logger.html).

#### Log4J 2

There is no ready-to-use appender to Cloud Logging. But you can [bridge it to Slf4J](https://logging.apache.org/log4j/log4j-2.2/log4j-to-slf4j/index.html).

## Learn More

* [Troubleshooting and Debugging Microservices in Kubernetes](https://saturnism.me/talk/troubleshooting-debugging-microservices/)


# Metrics

## Cloud Monitoring

Cloud Monitoring provides visibility into the performance, uptime, and overall health of your applications. It can collect metrics, events, and metadata from Google Cloud, Amazon Web Services, hosted uptime probes, application instrumentation, Metrics data can be used to generate insights via dashboards, charts, and alerts. Cloud Monitoring alerting helps you collaborate by integrating with Slack, PagerDuty, and more.

### Enable API

```bash
gcloud services enable monitoring.googleapis.com
```

## Micrometer

[Micrometer](http://micrometer.io/) is the de-facto metrics collector for Spring Boot applications. Micrometer can export JVM metrics, Spring Boot metrics, and also application metrics with [Counters](http://micrometer.io/docs/concepts#_counters), [Gauges](http://micrometer.io/docs/concepts#_gauges), and [Timers](http://micrometer.io/docs/concepts#_timers). You can export the metrics to Cloud Monitoring with two methods:

| Method                                                                                                                                                 | Description                                                                                                                                                                                                   | When to use?                                                                                                                |
| ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- |
| [Prometheus](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-metrics-export-prometheus) | <p>Export metrics using the Prometheus format from a Spring Boot Actuator endpoint (<code>/actuator/prometheus</code>).</p><p></p><p>A Prometheus agent will need to be configured to scrape the metrics.</p> | Great option when running in Kubernetes, where metrics are usually collected using Prometheus operator.                     |
| [Cloud Monitoring API](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-metrics-export-stackdriver)              | Export the metrics directly to Cloud Monitoring using the API.                                                                                                                                                | This is needed whenever Prometheus scraping is not possible, such as Serverless environments like Cloud Run and App Engine. |

### Prometheus

#### Dependency

Add Spring Boot Actuator and Micrometer Prometheus dependencies:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy

compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator'
compile group: 'io.micrometer', name: 'micrometer-registry-prometheus'
```

{% endtab %}
{% endtabs %}

#### Configuration

Configure Spring Boot Actuator to expose the Prometheus endpoint:

{% code title="application.properties" %}

```
management.endpoints.web.exposure.include=health,info,prometheus
```

{% endcode %}

{% hint style="info" %}
Notice that there is no explicit configuration for username/password. Cloud Trace authentication uses the GCP credential (either your user credential, or Service Account credential), and authorization is configured via Identity Access Management (IAM).
{% endhint %}

#### Prometheus Endpoint

You should be able to access the metrics in Prometheus format from `/actuator/prometheus`.

```
$ curl http://localhost:8080/actuator/prometheus

# HELP jvm_memory_committed_bytes The amount of memory in bytes that is committed for  the Java virtual machine to use
# TYPE jvm_memory_committed_bytes gauge
jvm_memory_committed_bytes{area="nonheap",id="Code Cache",} 1.8284544E7
jvm_memory_committed_bytes{area="nonheap",id="Metaspace",} 6.6281472E7
jvm_memory_committed_bytes{area="nonheap",id="Compressed Class Space",} 8609792.0
jvm_memory_committed_bytes{area="heap",id="PS Eden Space",} 6.01358336E8
jvm_memory_committed_bytes{area="heap",id="PS Survivor Space",} 2.2020096E7
jvm_memory_committed_bytes{area="heap",id="PS Old Gen",} 1.1010048E8
# HELP tomcat_global_sent_bytes_total  
...
```

#### Prometheus Scraper

If you are running in Kubernetes Engine, you can use Prometheus Operator to install a Prometheus instance. You also need to configure Prometheus with a sidecar that can propagate Prometheus metrics to Cloud Monitoring.

#### Install Prometheus Operator

```bash
kubectl apply -f \
  https://raw.githubusercontent.com/coreos/prometheus-operator/v0.38.1/bundle.yaml
```

#### Provisioning Prometheus server with Sidecar

Create a `prometheus.yaml` for the Prometheus Operator, but replace the variables for `${RPOJECT_ID},` `${LOCATION}`, and `${CLUSTER_NAME}`with your Kubernetes Engine cluster information.

{% code title="prometheus.yaml" %}

```yaml
# This config is cooked based on following resource:
# https://godoc.org/github.com/coreos/prometheus-operator/pkg/apis/monitoring/v1#Prometheus
# https://github.com/istio/installer/blob/master/istio-telemetry/prometheus-operator/templates/prometheus.yaml
apiVersion: monitoring.coreos.com/v1
kind: Prometheus
metadata:
  name: prometheus
  labels:
spec:
  image: "docker.io/prom/prometheus:v2.12.0"
  version: v2.12.0
  retention: 720h
  scrapeInterval: 15s
  serviceAccountName: prometheus
  serviceMonitorSelector:
    any: true
  serviceMonitorNamespaceSelector:
    any: true
  podMonitorSelector:
    any: true
  podMonitorNamespaceSelector:
    any: true
  enableAdminAPI: false
  podMetadata:
    labels:
      app: prometheus
  containers:
  - name: sd-sidecar
    image: gcr.io/stackdriver-prometheus/stackdriver-prometheus-sidecar:0.7.3
    args:
    - --stackdriver.project-id=${PROJECT_ID}
    - --stackdriver.kubernetes.location=${LOCATION}
    - --stackdriver.kubernetes.cluster-name=${CLUSTER_NAME}
    - --prometheus.wal-directory=/prometheus/wal
    ports:
    - name: sidecar
      containerPort: 9091
    volumeMounts:
    - name: prometheus-prometheus-db
      mountPath: /prometheus
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: prometheus
  labels:
    app: prometheus
rules:
  - apiGroups: [""]
    resources:
      - nodes
      - services
      - endpoints
      - pods
      - nodes/proxy
    verbs: ["get", "list", "watch"]
  - apiGroups: [""]
    resources:
      - configmaps
    verbs: ["get"]
  - nonResourceURLs: ["/metrics"]
    verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: prometheus-default
  labels:
    app: prometheus
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: prometheus
subjects:
  - kind: ServiceAccount
    name: prometheus
    namespace: default
---
apiVersion: v1
kind: Service
metadata:
  name: prometheus
  annotations:
    prometheus.io/scrape: 'true'
  labels:
    app: prometheus
spec:
  selector:
    app: prometheus
  ports:
    - name: http-prometheus
      protocol: TCP
      port: 9090
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: prometheus
  namespace: default
  labels:
    app: prometheus
```

{% endcode %}

Apply `prometheus.yaml`to the Kubernetes cluster to provision a new instance using the Prometheus Operator.

```bash
kubectl apply -f prometheus.yaml
```

Configure Prometheus server to scrape the metrics. Create a `pod-monitors.yaml`:

{% code title="pod-monitors.yaml" %}

```yaml
apiVersion: monitoring.coreos.com/v1
kind: PodMonitor
metadata:
  name: order-service
  labels:
    app: order-service
spec:
  namespaceSelector:
    any: true
  selector:
    matchLabels:
      app: order-service
  podMetricsEndpoints:
  - targetPort: 8080 
    path: /actuator/prometheus
    interval: 15s
```

{% endcode %}

### Cloud Monitoring API

To export metrics directly to Cloud Monitoring, you can use the [Micrometer Stackdriver registry](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#production-ready-metrics-export-stackdriver).

#### Dependency

Add Spring Boot Actuator and Micrometer Stackdriver dependencies:

{% tabs %}
{% tab title="Maven" %}

```markup
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-registry-stackdriver</artifactId>
</dependency>
```

{% endtab %}

{% tab title="Gradle" %}

```groovy

compile group: 'org.springframework.boot', name: 'spring-boot-starter-actuator'
compile group: 'io.micrometer', name: 'micrometer-registry-stackdriver'
```

{% endtab %}
{% endtabs %}

#### Configuration

Configure the Project ID.

{% code title="application.properties" %}

```
management.metrics.export.stackdriver.project-id=<PROJECT_ID>
```

{% endcode %}

{% hint style="warning" %}
Better integration is [coming soon with Spring Cloud GCP](https://cloud.spring.io/spring-cloud-gcp/reference/html/#stackdriver-monitoring).&#x20;
{% endhint %}

## Learn More

* [Troubleshooting and Debugging Microservices in Kubernetes](https://saturnism.me/talk/troubleshooting-debugging-microservices/)


# Profiling

## Cloud Profiler

[Cloud Profiler](https://cloud.google.com/profiler/docs/concepts-profiling) allows you to continuously profile CPU and heap usages to help identify performance bottlenecks and critical paths in your application. It'll be able to produce flame graph on which parts of your application uses the most CPU and/or Heap.

### Enable API

```bash
gcloud services enable cloudprofiler.googleapis.com
```

### CPU Time

The CPU time for a function tells you how long the CPU was busy executing instructions. It doesn't include the time the CPU was waiting or processing instructions for something else.

![](/files/-MDMnBzbB4JBAhLMG2Lq)

### Wall Time

The wall-clock time for a function measures the time elapsed between entering and exiting a function. Wall-clock time includes all wait time, including that for locks and thread synchronization. If the wall-clock time is significantly longer than the CPU time, then that is an indication the code spends a lot of time waiting. This might be an indication of a resource bottleneck.

![](/files/-MDMn71-3Ef2wWRtsDDu)

### Heap

The heap consumption is the amount of memory allocated in the Java program's heap - this can help you find potential inefficiencies and memory leaks in your application.

![](/files/-MDMnH3o7-FMXhKxFGQ2)

## Java Agent

Cloud Profiler works by adding a Java agent to your JVM startup argument, and the agent can communicate with the Cloud Profiler service in the Cloud. Through the Cloud Console, you can then see the collected profile data.

### Agent Files

A Cloud Profiler agent can work both within Google Cloud environments using the [Machine Credentials](/getting-started/google-cloud-platform#machine-credentials-from-metadata-server), and outside of Google Cloud environments (e.g., on-premises, and in another cloud) using a [Service Account key file](/getting-started/google-cloud-platform#service-account-key).

| Latest Version                                                                                   | Versioned URL                                                                                    |
| ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------ |
| [Download](https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz) | <https://storage.googleapis.com/cloud-profiler/java/cloud-profiler-java-agent_${VERSION}.tar.gz> |

Unfortunately, the list of versions are not available on GitHub. The only way to see the list of available versions is listing the Google Cloud Storage bucket that contains all the binaries:

```bash
gsutil ls gs://cloud-profiler/java/cloud-profiler-*
```

{% hint style="info" %}
See [Profiling Java Applications](https://cloud.google.com/profiler/docs/profiling-java) for more information.
{% endhint %}

### Agent Configurations

#### Agent Path

To use the agent, you'll need to configure the JVM command line using the standard `-agentpath` , e.g.:

```bash
java -agentpath:/opt/cprof/profiler_java_agent.so \
  -jar ...
```

Rather than hard coding the startup command line, you can also configure it with the `JAVA_TOOL_OPTIONS` environmental variable:

```bash
JAVA_TOOL_OPTIONS="-agentpath:/opt/cprof/profiler_java_agent.so"
java -jar ...
```

#### Agent Configurations

You can specify additional Agent configurations within the same `agentpath` argument, in the form of `java -agentpath:/opt/cprof/profiler_java_agent.so=FLAG1=VALUE1,FLAG2=VALUE2`.

Heap sampling is only available in Java 11 and higher. To turn on both CPU and Heap profiling for a Java 11 application:

```bash
java -agentpath:/opt/cprof/profiler_java_agent.so=-cprof_enable_heap_sampling=true \
  -jar ...
```

{% hint style="info" %}
See [Profiling Java applications Agent Configuration document](https://cloud.google.com/profiler/docs/profiling-java#agent_configuration) for all the possible agent configuration flags.
{% endhint %}

#### Logging

By default the Cloud Profiler does not output any logs. You can turn on logging by using `-logtostderr`flag, and configure the log level using `‑minloglevel`flag.

```
java -agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-minloglevel=2 \
  -jar ...
```

{% hint style="info" %}
See [Profiling Java applications Agent Logging document](https://cloud.google.com/profiler/docs/profiling-java#agent_logging) for all the possible log levels.
{% endhint %}

### Runtime Configuration

{% tabs %}
{% tab title="App Engine" %}
Follow [App Engine Hello World!](/getting-started/helloworld/app-engine) instructions to deploy an application to App Engine.

Cloud Profiler agent is already present in your App Engine application. However, it is not on by default. You can turn it on by using the `JAVA_TOOL_OPTIONS` environmental variable in an `app.yaml` file:

{% code title="app.yaml" %}

```yaml
runtime: java11
env_variables:
  JAVA_TOOL_OPTIONS: "-agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true"
```

{% endcode %}

Redeploy the application with the `app.yaml` file:

```bash
gcloud app deploy target/helloworld.jar \
  --appyaml app.yaml
```

It'll take a couple of minutes before Cloud Profiler can display the information. In Cloud Profiler console, you can find the Default service in the drop down:

![](/files/-MDMrDmq53sZFDA3hUGk)
{% endtab %}

{% tab title="Cloud Run" %}
Add the Cloud Profiler Java agent to the container, and configure the agent in the startup command line.

#### Clone

```bash
# Clone the sample repository manually
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

#### Containerize with a Dockerfile

In the Dockerfile, download the Cloud Debugger and build it as part of the container image:

{% code title="Dockerfile" %}

```
FROM openjdk:11

# Create a directory for the Profiler. Add and unzip the agent in the directory.
RUN mkdir -p /opt/cprof && \
  wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \
  | tar xzv -C /opt/cprof

COPY target/helloworld.jar /app.jar

ENTRYPOINT ["java", "-jar", "/app.jar"]
```

{% endcode %}

Then build and push the container:

```bash
mvn package

PROJECT_ID=$(gcloud config get-value project)
docker build -t gcr.io/${PROJECT_ID}/helloworld .
docker push gcr.io/${PROJECT_ID}/helloworld
```

#### Containerize with Jib

Download the Cloud Debugger Java agent into `src/main/jib` directory so that Jib can include the agent files as part of the container image:

```bash
# Make a directory to store the Java agent
mkdir -p src/main/jib/opt/cprof

# Download and extract the Java agent to the directory
wget -qO- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz | \
  tar xvz -C src/main/jib/opt/cprof
```

Create the container image with Jib:

```bash
PROJECT_ID=$(gcloud config get-value project)
mvn compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

#### Deploy

Deploy to Cloud Run with Debugger Enabled using the environmental variable:

```bash
gcloud run deploy helloworld \
  --region=us-central1 \
  --platform=managed \
  --allow-unauthenticated \
  --set-env-vars="JAVA_TOOL_OPTIONS=-agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true" \
  --image=gcr.io/${PROJECT_ID}/helloworld
```

In Cloud Profiler console, you can see the `helloworld` service in the drop down:

![](/files/-MDMr8GcExPHZeOdvnXa)
{% endtab %}

{% tab title="Kubernetes Engine" %}
Add the Cloud Profiler Java agent to the container, and configure the agent in the startup command line.

#### Clone

```bash
# Clone the sample repository manually
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

#### Containerize with a Dockerfile

In the Dockerfile, download the Cloud Debugger and build it as part of the container image:

{% code title="Dockerfile" %}

```
FROM openjdk:11

# Create a directory for the Profiler. Add and unzip the agent in the directory.
RUN mkdir -p /opt/cprof && \
  wget -q -O- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \
  | tar xzv -C /opt/cprof

COPY target/helloworld.jar /app.jar

ENTRYPOINT ["java", "-jar", "/app.jar"]
```

{% endcode %}

Then build and push the container:

```bash
mvn package

PROJECT_ID=$(gcloud config get-value project)
docker build -t gcr.io/${PROJECT_ID}/helloworld .
docker push gcr.io/${PROJECT_ID}/helloworld
```

#### Containerize with Jib

Download the Cloud Debugger Java agent into `src/main/jib` directory so that Jib can include the agent files as part of the container image:

```bash
# Make a directory to store the Java agent
mkdir -p src/main/jib/opt/cprof

# Download and extract the Java agent to the directory
wget -qO- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz | \
  tar xvz -C src/main/jib/opt/cprof
```

Create the container image with Jib:

```bash
PROJECT_ID=$(gcloud config get-value project)
mvn compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

#### Deploy

Deploy to Kubernetes Engine with Debugger Enabled using the environmental variable using a Deployment YAML:

```bash
# Make a directory to store Kubernetes YAMLs
mkdir k8s/
```

Create a Deployment YAML file and configure the environmental variable:

{% code title="k8s/deployment.yaml" %}

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: helloworld
  name: helloworld
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - image: gcr.io/YOUR_PROJECT_ID/helloworld
        name: helloworld
        env:
        - name: JAVA_TOOL_OPTIONS
          value: "-agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true,-cprof_service=helloworld-gke,-cprof_service_version=1.0"
```

{% endcode %}

Deploy the YAML file:

```bash
kubectl apply -f k8s/deployment.yaml
```

In Cloud Debugger console, you can see the `helloworld-gke` service in the drop down:

![](/files/-MDNE3Bbg0VM65xuYhOH)
{% endtab %}

{% tab title="Compute Engine" %}
Follow the [Compute Engine Hello World!](/getting-started/helloworld/compute-engine) to deploy an application in Compute Engine.

SSH into the Compute Engine instance:

```bash
gcloud compute ssh helloworld
```

From the Compute Engine instance, download the Java agent:

```bash
sudo mkdir -p /opt/cprof
curl -s -o- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \
  | sudo tar xvz -C /opt/cprof
```

Run the Java application with the Cloud Debugger agent:

```bash
java -agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true,-cprof_service=helloworld-gce,-cprof_service_version=1.0 \
  -jar helloworld.jar
```

In Cloud Debugger console, you can see the `helloworld-gce` service in the drop down:

![](/files/-MDNAGjDgOlkPvD2FZdq)
{% endtab %}

{% tab title="Non-Google Cloud Environment" %}
You can attach the Cloud Debugger agent to any Java application even if it runs outside of the Google Cloud environment (whether it's in a container, or on your local laptop, or in another cloud). Authentication has to be done using Service Account key file rather than using the Machine Credentials.

{% hint style="danger" %}
This only works on a Linux x86 based system.
{% endhint %}

#### Clone

```bash
# Clone the sample repository manually
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

#### Build

```bash
mvn package
```

#### Download Agent

```bash
sudo mkdir -p /opt/cprof
curl -s -o- https://storage.googleapis.com/cloud-profiler/java/latest/profiler_java_agent.tar.gz \
  | sudo tar xvz -C /opt/cprof
```

#### Create a Service Account

```bash
PROJECT_ID=$(gcloud config get-value project)
gcloud iam service-accounts create helloworld-app
```

#### Add IAM Permission

```bash
PROJECT_ID=$(gcloud config get-value project)
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:helloworld-app@${PROJECT_ID}.iam.gserviceaccount.com \
  --role roles/cloudprofiler.agent
```

#### Create a Service Account Key File

```bash
PROJECT_ID=$(gcloud config get-value project)
gcloud iam service-accounts keys create \
  $HOME/helloworld-app-sa.json \
  --iam-account helloworld-app@${PROJECT_ID}.iam.gserviceaccount.com
```

#### Use Service Account Cloud Debugger Agent

```bash
PROJECT_ID=$(gcloud config get-value project)
GOOGLE_APPLICATION_CREDENTAILS=$HOME/helloworld-app-sa.json
java -agentpath:/opt/cprof/profiler_java_agent.so=-logtostderr,-cprof_enable_heap_sampling=true,-cprof_service=helloworld-local,-cprof_service_version=1.0,-cprof_project_id=${PROJECT_ID} \
  -jar target/helloworld.jar
```

{% endtab %}
{% endtabs %}


# Debugging

## Cloud Debugger

Cloud Debugger lets you inspect the state of an application, at any code location, without stopping or slowing down the running application.

Cloud Debugger is supported in all Google Cloud runtime environments (except for Cloud Functions) and can also be used when running applications in non-Google Cloud environments (on-premises, other clouds).

### Enable API

```bash
gcloud services enable clouddebugger.googleapis.com
```

### Snapshot

A Snapshot can introspect the context information on a given line of code as user go through the code flow.

![Example of a Snapshot](/files/-MCmolQAMVFp-cCGF0DV)

### Logpoint

A Logpoint can add additional log messages to a running application without modifying the code nor redeploying the code.

![Example of a Logpoint](/files/-MCmp2Yr2grwh4G9PWf_)

### Conditions

In both Snapshot and Logpoint, you can specify conditionals so you can capture specific information for a specific request (e.g., match against a session ID, or request ID).

## Java Agent

Cloud Debugger works by adding a Java agent to your JVM startup argument, and the agent can communicate with the Cloud Debugger service in the Cloud. Through the Cloud Console, you can then instruct your JVM instances to take a Snapshot of the application state at a specific line of code, or to add an additional log message on a specific line.

### Agent Files

There are 2 types of Cloud Debugger Java agents that authenticates with Cloud Debugger service differently:

| Type                | When to use?                      | Latest Version                                                                                                              | Versioned URL                                                                                                          |
| ------------------- | --------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| Machine Credentials | Google Cloud runtime environments | [Download](https://storage.googleapis.com/cloud-debugger/compute-java/debian-wheezy/cdbg_java_agent_gce.tar.gz)             | <https://storage.googleapis.com/cloud-debugger/archive/java/${VERSION}/cdbg\\_java\\_agent\\_gce.tar.gz>               |
| Service Account Key | Non-Google Cloud environments     | [Download](https://storage.googleapis.com/cloud-debugger/compute-java/debian-wheezy/cdbg_java_agent_service_account.tar.gz) | <https://storage.googleapis.com/cloud-debugger/archive/java/${VERSION}/cdbg\\_java\\_agent\\_service\\_account.tar.gz> |

{% hint style="info" %}
You can find all the versions in the [cloud-debug-java](https://github.com/GoogleCloudPlatform/cloud-debug-java/releases) GitHub repository. For example, Cloud Debugger agent version `2.25` using Machine Credentials can be downloaded with URL: <https://storage.googleapis.com/cloud-debugger/archive/java/2.25/cdbg_java_agent_gce.tar.gz>
{% endhint %}

### Agent Configurations

#### Agent Path

To use the agent, you'll need to configure the JVM command line using the standard  `-agentpath` , e.g.:

```bash
java -agentpath:/opt/cdbg/cdbg_java_agent.so \
  -jar ...
```

Rather than hard coding the startup command line, you can also configure it with the `JAVA_TOOL_OPTIONS` environmental variable:

```bash
JAVA_TOOL_OPTIONS="-agentpath:/opt/cdbg/cdbg_java_agent.so"
java -jar ...
```

#### System Properties

There are additional flags you can pass to the Java agent using Java's system properties.

| System Properties                                               | Description                                                                                                                                                                                                                | Required                                       |
| --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- |
| <p>com.google.cdbg</p><p>.module</p>                            | The name of your application.                                                                                                                                                                                              | Not required for Cloud Run or App Engine.      |
| <p>com.google.cdbg</p><p>.version</p>                           | The version of your application.                                                                                                                                                                                           | Not required for Cloud Run or App Engine.      |
| <p>com.google.cdbg</p><p>.breakpoints</p><p>.enable\_canary</p> | `true` or `false`.Whether to turn on debugger for a subset of the running instances. See [Canary snapshots and logpoints documentation](https://cloud.google.com/debugger/docs/setup/java#canary_snapshots_and_logpoints). | Not required and defaults to `false`.          |
| <p>com.google.cdbg</p><p>.auth.serviceaccount.enable</p>        | `true` or `false`. Whether to authenticate with a Service Account key file.                                                                                                                                                | Required when running outside of Google Cloud. |
| <p>com.google.cdbg.auth</p><p>.serviceaccount.jsonfile</p>      | File path to the Service Account key file.                                                                                                                                                                                 | Required when running outside of Google Cloud. |

For example, you can enable the snapshot using the system property:

```
JAVA_TOOL_OPTIONS="-agentpath:/opt/cdbg/cdbg_java_agent.so \
  -Dcom.google.cdbg.breakpoints.enable_canary=true"
```

#### Logging

By default the Cloud Debugger agent writes its logs to `cdbg_java_agent.INFO` file in the default logging directory. You can overwrite the log file path:

```
java -agentpath:/opt/cdbg/cdbg_java_agent.so=--log_dir=/tmp/cdbg.log \
  -jar ...
```

Alternatively you can make the Java Cloud Debugger log to `stderr`:

```
java -agentpath:/opt/cdbg/cdbg_java_agent.so=--logtostderr=1 \
  -jar ...
```

{% hint style="info" %}
See [Setting Up Cloud Debugger for Java](https://cloud.google.com/debugger/docs/setup/java#overview) documentation for more information.
{% endhint %}

### Runtime Configuration

{% tabs %}
{% tab title="App Engine" %}
Follow [App Engine Hello World!](/getting-started/helloworld/app-engine) instructions to deploy an application to App Engine.

Cloud Debugger agent is automatically added to your App Engine application.

In Cloud Debugger console, you can see the Default service in the drop down:

![](/files/-MCnaHJdcU4-EiEDJnKN)
{% endtab %}

{% tab title="Cloud Run" %}
Add the Cloud Debugger Java agent to the container, and configure the agent in the startup command line.

#### Clone

```bash
# Clone the sample repository manually
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

#### Containerize with a Dockerfile

In the Dockerfile, download the Cloud Debugger and build it as part of the container image:

{% code title="Dockerfile" %}

```
FROM openjdk:11

# Create a directory for the Debugger. Add and unzip the agent in the directory.
RUN mkdir /opt/cdbg && \
     wget -qO- https://storage.googleapis.com/cloud-debugger/compute-java/debian-wheezy/cdbg_java_agent_gce.tar.gz | \
     tar xvz -C /opt/cdbg

COPY target/helloworld.jar /app.jar

ENTRYPOINT ["java", "-jar", "/app.jar"]
```

{% endcode %}

Then build and push the container:

```bash
mvn package

PROJECT_ID=$(gcloud config get-value project)
docker build -t gcr.io/${PROJECT_ID}/helloworld .
docker push gcr.io/${PROJECT_ID}/helloworld
```

#### Containerize with Jib

Download the Cloud Debugger Java agent into `src/main/jib` directory so that Jib can include the agent files as part of the container image:

```bash
# Make a directory to store the Java agent
mkdir -p src/main/jib/opt/cdbg

# Download and extract the Java agent to the directory
wget -qO- https://storage.googleapis.com/cloud-debugger/compute-java/debian-wheezy/cdbg_java_agent_gce.tar.gz | \
  tar xvz -C src/main/jib/opt/cdbg
```

Create the container image with Jib:

```bash
PROJECT_ID=$(gcloud config get-value project)
mvn compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

#### Deploy

Deploy to Cloud Run with Debugger Enabled using the environmental variable:

```bash
gcloud run deploy helloworld \
  --region=us-central1 \
  --platform=managed \
  --allow-unauthenticated \
  --set-env-vars='JAVA_TOOL_OPTIONS="-agentpath:/opt/cdbg/cdbg_java_agent.so=--logtostderr=1"' \
  --image=gcr.io/${PROJECT_ID}/helloworld
```

In Cloud Debugger console, you can see the `helloworld` service in the drop down:

![](/files/-MCna1ZlWfuRXsUm2YYk)
{% endtab %}

{% tab title="Kubernetes Engine" %}
Add the Cloud Debugger Java agent to the container, and configure the agent in the startup command line.

#### Clone

```bash
# Clone the sample repository manually
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

#### Containerize with a Dockerfile

In the Dockerfile, download the Cloud Debugger and build it as part of the container image:

{% code title="Dockerfile" %}

```
FROM openjdk:11

# Create a directory for the Debugger. Add and unzip the agent in the directory.
RUN mkdir /opt/cdbg && \
    wget -qO- https://storage.googleapis.com/cloud-debugger/compute-java/debian-wheezy/cdbg_java_agent_gce.tar.gz | \
    tar xvz -C /opt/cdbg

COPY target/helloworld.jar /app.jar

ENTRYPOINT ["java", "-jar", "/app.jar"]
```

{% endcode %}

Then build and push the container:

```bash
mvn package

PROJECT_ID=$(gcloud config get-value project)
docker build -t gcr.io/${PROJECT_ID}/helloworld .
docker push gcr.io/${PROJECT_ID}/helloworld
```

#### Containerize with Jib

Download the Cloud Debugger Java agent into `src/main/jib` directory so that Jib can include the agent files as part of the container image:

```bash
# Make a directory to store the Java agent
mkdir -p src/main/jib/opt/cdbg

# Download and extract the Java agent to the directory
wget -qO- https://storage.googleapis.com/cloud-debugger/compute-java/debian-wheezy/cdbg_java_agent_gce.tar.gz | \
  tar xvz -C src/main/jib/opt/cdbg
```

Create the image with Jib:

```bash
PROJECT_ID=$(gcloud config get-value project)
mvn compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

#### Deploy

Deploy to Kubernetes Engine with Debugger Enabled using the environmental variable using a Deployment YAML:

```bash
# Make a directory to store Kubernetes YAMLs
mkdir k8s/
```

Create a  Deployment YAML file and configure the environmental variable:

{% code title="k8s/deployment.yaml" %}

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: helloworld
  name: helloworld
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - image: gcr.io/YOUR_PROJECT_ID/helloworld
        name: helloworld
        env:
        - name: JAVA_TOOL_OPTIONS
          value: "-agentpath:/opt/cdbg/cdbg_java_agent.so=--logtostderr=1 -Dcom.google.cdbg.module=helloworld-gke -Dcom.google.cdbg.version=1.0"
```

{% endcode %}

Deploy the YAML file:

```bash
kubectl apply -f k8s/deployment.yaml
```

In Cloud Debugger console, you can see the `helloworld-gke` service in the drop down:

![](/files/-MCtPLB2G8Ncan1SdoVX)
{% endtab %}

{% tab title="Compute Engine" %}
Follow the [Compute Engine Hello World!](/getting-started/helloworld/compute-engine) to deploy an application in Compute Engine.

SSH into the Compute Engine instance:

```bash
gcloud compute ssh helloworld
```

From the Compute Engine instance, download the Java agent:

```bash
sudo mkdir -p /opt/cdbg
curl -s -o- https://storage.googleapis.com/cloud-debugger/compute-java/debian-wheezy/cdbg_java_agent_gce.tar.gz \
  | sudo tar xvz -C /opt/cdbg
```

Run the Java application with the Cloud Debugger agent:

```bash
java -agentpath:/opt/cdbg/cdbg_java_agent.so=--logtostderr=1 \
    -Dcom.google.cdbg.module=helloworld-gce \
    -Dcom.google.cdbg.version=1.0 \
    -jar helloworld.jar
```

In Cloud Debugger console, you can see the `helloworld-gce` service in the drop down:

![](/files/-MCtQij-P80bXwe5YaQA)
{% endtab %}

{% tab title="Non-Google Cloud Environment" %}
You can attach the Cloud Debugger agent to any Java application even if it runs outside of the Google Cloud environment (whether it's in a container, or on your local laptop, or in another cloud). Authentication has to be done using Service Account key file rather than using the Machine Credentials.

{% hint style="danger" %}
This only works on a Linux x86 based system.
{% endhint %}

#### Clone

```bash
# Clone the sample repository manually
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

#### Build

```bash
mvn package
```

#### Download Agent

```bash
sudo mkdir -p /opt/cdbg
curl -s -o- https://storage.googleapis.com/cloud-debugger/compute-java/debian-wheezy/cdbg_java_agent_gce.tar.gz \
  | sudo tar xvz -C /opt/cdbg
```

#### Create a Service Account

```bash
PROJECT_ID=$(gcloud config get-value project)
gcloud iam service-accounts create helloworld-app
```

#### Add IAM Permission

```bash
PROJECT_ID=$(gcloud config get-value project)
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:helloworld-app@${PROJECT_ID}.iam.gserviceaccount.com \
  --role roles/clouddebugger.agent
```

#### Create a Service Account Key File

```bash
PROJECT_ID=$(gcloud config get-value project)
gcloud iam service-accounts keys create \
  $HOME/helloworld-app-sa.json \
  --iam-account helloworld-app@${PROJECT_ID}.iam.gserviceaccount.com
```

#### Use Service Account Cloud Debugger Agent

```bash
java -agentpath:/opt/cdbg/cdbg_java_agent.so=--logtostderr=1 \
    -Dcom.google.cdbg.module=helloworld-local \
    -Dcom.google.cdbg.version=1.0 \
    -Dcom.google.cdbg.auth.serviceaccount.enable=true \
    -Dcom.google.cdbg.auth.serviceaccount.jsonfile=$HOME/hellworld-app-sa.json \
    -jar target/helloworld.jar
```

{% endtab %}
{% endtabs %}

## Source Code

Cloud Debugger needs to have access to the application's source code in order for you to add a Snapshot or Logpoint from the Cloud Debugger console. There are severals ways to associating the source code:

* Existing Git Repository
* Source code capture / upload
* Git repository reference from `git.properties`
* IntelliJ Cloud Code plugin

### Git Repository

From Cloud Debugger console, navigate to **Deployed Files** > **Add source code**.

![Cloud Debugger console and find "Add source code"](/files/-MCmmhhrSU7PdnnYdXeR)

Choose an **Alternative source code**.

![Alternative source code choices](/files/-MCmnMr3uIM9WlkRiX4B)

For example, using an existing GitHub repository:

![Select source from GitHub.com](/files/-MCmnEFdwuBwZRbYF4N4)

Once selected, the contents of the Git repository will be available to navigate.

### Upload

#### Upload from Browser

From Cloud Debugger console, navigate to **Deployed Files** > **Add source code**.

![Cloud Debugger console and find "Add source code"](/files/-MCmmhhrSU7PdnnYdXeR)

Choose an **Alternative source code**.

![](/files/-MCmr1toj3uonakwXCuB)

Click on **Local files's Select Source**, then simply select the folder/directory that contains the source code.

#### Upload from Command Line

You can use `gcloud` CLI to upload the source code into a Source Captures repository.

Create a Source Captures repository:

```bash
# Enable API
gcloud services enable sourcerepo.googleapis.com

# Create a source capture repository
gcloud source repos create google-source-captures
```

In the **Alternative source code** choices, scroll to the very bottom is **Upload a source code capture to Google servers**.

{% hint style="danger" %}
Do not click on **Select source** yet.
{% endhint %}

![Alternative source code choices](/files/-MCmquhD-aQNAPrkqHNa)

Use the command line to upload the source code (for example, if you deployed the [Helloworld Application](/getting-started/helloworld/app-engine#clone)):

```bash
# Clone the sample repository manually
git clone https://github.com/GoogleCloudPlatform/java-docs-samples
cd java-docs-samples/appengine-java11/springboot-helloworld

# Upload just the `src/` directory.
# Note that the `branch` value is important and you must use the same value
# that's shown in the UI
gcloud beta debug source upload \
  --project=<FROM THE UI> \
  --branch=<FROM THE UI> \
  src/
```

Once uploaded, click **Select source.**

### **Use git.properties**

You can associate a Git repository using the [`git-commit-plugin`](https://github.com/git-commit-id/git-commit-id-maven-plugin) that generates a `git.properties` file, which contains the information to the Git repository. This only works if the repository is publicly accessible.

```markup
<plugin>
  <groupId>pl.project13.maven</groupId>
  <artifactId>git-commit-id-plugin</artifactId>
  <version>4.0.1</version>
  <executions>
    <execution>
      <goals>
        <goal>revision</goal>
      </goals>
    </execution>
	</executions>
</plugin>
```

Cloud Debugger service will automatically examine this file, and clone the code, and checkout the corresponding revision.

### IntelliJ with Cloud Code

You can use the Cloud Code plugin to directly add a Snapshot point without using the Cloud Debugger console.

Navigate to **Tools** > **Cloud Code** > **Attach Cloud Debugger**.

![](/files/-MCmwNZV7lHFlxE4VkyL)

Once configured the IntelliJ profile, you can add Snapshot to source code directly from the IDE.

![Debug in the Cloud](/files/-MCn-nY_Zjic-fuhg9ho)

## Learn More

* [Troubleshooting and Debugging Microservices in Kubernetes](https://saturnism.me/talk/troubleshooting-debugging-microservices/)


# DevOps


# Artifact Repository

When developing applications in a larger project, you may find a need to share common libraries across multiple teams or applications. If this library is a public OSS library, it's usually hosted on Maven Central. For an internal library, though, you'll need to use a private repository. Typically, in an on-premise datacenter, these Java (Maven) artifacts may be stored in private repositories such as Sonatype Nexus, or JFrog Artifactory.

On Google Cloud, you can continue setup/configure/use these repositories. JFrog can also run [Artifactory as a hosted service on Google Cloud](https://jfrog.com/partner/google-cloud-platform/)!

In addition, Google Cloud also has a fully managed artifact repository service called [Artifact Registry](https://cloud.google.com/artifact-registry) (beta).

## Artifact Registry

[Artifact Registry](https://cloud.google.com/artifact-registry) is a fully managed artifact repository service - you can use it to store container images, NPM packages, and Java artifacts, without having to setup any infrastructure and worry about availably or disk space.

{% hint style="info" %}
See [Artifact Registry documentation](https://cloud.google.com/artifact-registry) for more information.
{% endhint %}

### Enable API

```bash
gcloud services enable artifactregistry.googleapis.com
```

## Maven Repository

Artifact Registry can host Maven repositories to host the Java artifacts. Artifacts are hosted within a region of your choice, and you can apply Identity Access Management to control who can access/update artifacts.

{% embed url="<https://www.youtube.com/watch?v=2-P4cSCk1VM>" %}

{% hint style="warning" %}
Artifact Registry is currently in beta, and the Maven Repository feature is in Alpha. You'll need to sign up for the Alpha program first.

[Sign up for Artifact Registry Alpha](https://docs.google.com/forms/d/e/1FAIpQLSf5q3CeDna_c27ifadF1KO17W3PrYO91w-UI-jjUdnvGS1cmQ/viewform) feature to try the hands-on instructions.
{% endhint %}

Once you are confirmed to be enrolled in the alpha program, you can give it a try!

### Create a Maven Repository

```bash
gcloud beta artifacts repositories create private-maven-repo \
  --repository-format=maven \
  --location=us-central1
```

### List Artifacts

```bash
gcloud beta artifacts packages list \
  --repository=private-maven-repo \
  --location=us-central1
```

There should be no artifacts at the moment.

### Deploy a Maven Artifact

You need to update the build configuration (e.g., `pom.xml`) in order to configure an artifact to Artifact Registry's Maven repository. You can find the full configuration needed through by running the utility command:

#### Generate a New Project

This example will use Maven. First, create a brand new Maven project:

```bash
mvn archetype:generate \
  -DinteractiveMode=false \
  -DgroupId=com.example \
  -DartifactId=common-libs \
  -DarchetypeGroupId=org.apache.maven.archetypes \
  -DarchetypeArtifactId=maven-archetype-quickstart
  
cd common-libs/
```

#### Configuration

Once you have a Java project you want to publish to Artifact Registry, then you can use `gcloud` CLI to print out the configuration for your build system (Maven or Gradle). You'll need to use the configuration to be able to publish artifacts to the repository, or consume artifacts from the repository.

{% tabs %}
{% tab title="Maven" %}

```bash
gcloud beta artifacts print-settings mvn \
  --repository=private-maven-repo \
  --location=us-central1
```

Note that an Artifact Registry Wagon extension is needed to publish to Artifact Registry.
{% endtab %}

{% tab title="Gradle" %}

```bash
gcloud beta artifacts print-settings gradle \
  --repository=private-maven-repo \
  --location=us-central1
```

Note that an Artifact Registry Gradle plugin is needed to publish to Artifact Registry.
{% endtab %}
{% endtabs %}

Artifact Registry's plugins will automatically detect the current [Application Default Credentials](/getting-started/google-cloud-platform#application-default-credentials) to authorize access.

This example uses Maven, so edit the `pom.xml` to add the additional settings:

```markup
<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  
  ...
  
  <!-- Add Distributuion Management -->
  <distributionManagement>
    ...
  </distributionManagement>
  
  <!-- Add Repository -->
  <repositories>
    ...
  </repositories>

  <build>
    <!-- Add the Wagon Extension -->
    <extensions>
      <extension>
        <groupId>com.google.cloud.artifactregistry</groupId>
        <artifactId>artifactregistry-maven-wagon</artifactId>
        <version>2.1.0</version>
      </extension>
    </extensions>
    
    <pluginManagement>
      ...
    </pluginManagement>
  </build>
</project>
```

#### Build and Deploy

```bash
mvn clean package deploy
```

Verify that the artifact is published!

```bash
gcloud alpha artifacts packages list \
  --repository=private-maven-repo \
  --location=us-central1
```

In the Cloud Console, you can also browse to **Artifact Registry > private-maven-repo.**

![](/files/-MGFlu5taOBPtOl1hUcQ)

And see manage the artifacts:

![](/files/-MGFm4rptzApfZQ1ne8F)

{% hint style="info" %}
See [Artifact Registry Quickstart for Maven and Gradle](https://cloud.google.com/artifact-registry/docs/java/quickstart) for more information.
{% endhint %}


# Runtime Environments

## Basics

|                         | Cloud Functions                                   | App Engine                                                           | Cloud Run                                                            | Kubernetes Engine                                                        | Compute Engine                                             |
| ----------------------- | ------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------------------------- | ------------------------------------------------------------------------ | ---------------------------------------------------------- |
| **Fully Managed**       | FaaS                                              | PaaS                                                                 | CaaS/PaaS                                                            | Kubernetes Clusters                                                      | Virtual Machines                                           |
| **Deployable Artifact** | Source or JAR                                     | Source or JAR                                                        | Container Image                                                      | Container Image                                                          | Anything, and Container Image                              |
| **Locality**            | Regional                                          | Regional                                                             | Regional                                                             | Zonal/Regional                                                           | Zonal/Regional                                             |
| **Billing Units**       | Instance execution time seconds and Invocations   | Instance up time minutes                                             | Instance execution time seconds                                      | Control plane and VM instance hours                                      | VM instance hours                                          |
| **vCPU**                | 1                                                 | 1, up to 4.8GHz                                                      | Up to 2                                                              | Up to 416 per node, and up to 5000 nodes.                                | 0.5 to 416                                                 |
| **Memory**              | Up to 2GB                                         | Up to 2GB                                                            | Up to 4GB                                                            | Up to 11TB per node.                                                     | Up to 11TB                                                 |
| **Disk**                | Writable `/tmp` directory                         | Writable `/tmp` directory                                            | Writable `/tmp` directory                                            | Attach Tmpfs/PD/SSD                                                      | Attach PD/SSD                                              |
| **Use For**             | <p>Webhooks</p><p>Event Handlers</p><p>Tasks </p> | <p>Web Apps</p><p>Microservices</p><p>Event Handlers</p><p>Tasks</p> | <p>Web Apps</p><p>Microservices</p><p>Event Handlers</p><p>Tasks</p> | <p>Any container workload</p><p>JEE applications</p><p>Microservices</p> | <p>Any workload</p><p>JEE applications</p><p>Databases</p> |

## Application Lifecycle

|                            | Cloud Functions   | App Engine   | Cloud Run         | Kubernetes Engine         | Compute Engine |
| -------------------------- | ----------------- | ------------ | ----------------- | ------------------------- | -------------- |
| **Liveness Check**         | Port is listening | /\_ah/health | Port is listening | Liveness Probe            | Manual         |
| **Readiness/Warmup Check** | No                | /\_ah/warmup | No                | Readiness Probe           | Manual         |
| **Graceful Shutdown**      | No Signal         | /\_ah/stop   | No Signal         | `SIGTERM` or custom hooks | Manual         |

## Scaling

|                     | Cloud Functions   | App Engine        | Cloud Run         | Kubernetes Engine                    | Compute Engine                   |
| ------------------- | ----------------- | ----------------- | ----------------- | ------------------------------------ | -------------------------------- |
| **Scaling**         | 0 to N in seconds | 0 to N in seconds | 0 to N in seconds | 1 to N in seconds or minutes         | 1 to N in minutes                |
| **Autoscaling**     | Yes               | Yes               | Yes               | Yes, with HPA and Cluster Autoscaler | Yes, with Managed Instance Group |
| **Scaling Min/Max** | No                | Yes               | Yes (alpha)       | Yes, with HPA and Cluster Autoscaler | Yes, with Managed Instance Group |
| **Manual Scaling**  | No                | Yes               | No                | Yes                                  | Yes                              |

## Load Balancing

|                             | Cloud Functions | App Engine | Cloud Run | Kubernetes Engine              | Compute Engine                  |
| --------------------------- | --------------- | ---------- | --------- | ------------------------------ | ------------------------------- |
| **Network Load Balancer**   | No              | No         | No        | Yes, with `Service`            | Yes, with Network Load Balancer |
| **HTTP**                    | Yes             | Yes        | Yes       | Yes, with `Ingress`            | Yes, with HTTP(s) Load Balancer |
| **HTTPs**                   | Yes             | Yes        | Yes       | Yes, with `Ingress`            | Yes, with HTTP(s) Load Balancer |
| **Custom Domain**           | No              | Yes        | Yes       | Yes, manual configuration      | Yes, manual configuration       |
| **Managed SSL Certificate** | Yes             | Yes        | Yes       | Yes, with `ManagedCertificate` | Yes, with HTTP(s) Load Balancer |

## Networking

|                                  | Cloud Functions | App Engine | Cloud Run | Kubernetes Engine | Compute Engine |
| -------------------------------- | --------------- | ---------- | --------- | ----------------- | -------------- |
| Use VPC                          | Yes             | Yes        | Yes       | Yes               | Yes            |
| Expose on VPC Only               | Yes             | No         | No        | Yes               | Yes            |
| Internal VPC Only Load Balancing | No              | No         | No        | Yes               | Yes            |


# Container

Docker and container image are becoming the de-facto packaging format when it comes to deploying applications into a cluster of machines.

If you are new to Docker and containers, you can learn more in this video:

{% embed url="<https://www.youtube.com/watch?v=pnOLWFBpb2A>" %}

But, if you didn't watch the whole video, that's OK! Long story short, try not write your own Dockerfile to build a production container image. There are too many best practices to learn/apply. See the [Container Image](https://app.gitbook.com/s/-L_Laqs9uSAihPmRemDj/deployment/container-image.md) section for automated tools that can create optimized container images by default.

&#x20;


# Container Image

Build container images with tools and best practices.

## Clone

```bash
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

## Build

```bash
./mvnw package
```

## Enable API

Enable Container Registry API to push your container images to the Container Registry.

```bash
gcloud services enable containerregistry.googleapis.com
```

## Containerize

Typically, tutorials teach you how to write a `Dockerfile` to containerize a Java application. A `Dockerfile` can be error prone and it's hard to implement all the best practices. Rather than writing a `Dockerfile`, use tools such as Jib and Buildpacks to automatically create optimized container images.

### Build and Push

Most tools can build and push directly into a container registry. In case of Jib, this step does not require a Docker daemon at all, and it can push changed layers directly into a remote registry. This is great for automated CI/CD pipelines.

{% tabs %}
{% tab title="Jib" %}
[Jib](https://github.com/GoogleContainerTools/jib) can containerize any Java application easily, without a `Dockerfile` nor `docker` installed. Jib will push the container image directly to the remote registry.

```bash
PROJECT_ID=$(gcloud config get-value project)
./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

{% hint style="info" %}
You can configure [Jib Maven plugin](https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin) or [Jib Gradle plugin](https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin) directly in the build file to run the Jib easier, such as `./mvnw jib:build`.
{% endhint %}
{% endtab %}

{% tab title="Buildpack" %}
[Cloud Native Buildpacks](https://buildpacks.io) can containerize applications written in different language without a `Dockerfile`. It does require `docker` installed.

1. Install Docker locally - see [Get Docker documentation](https://docs.docker.com/get-docker/).
2. Install `pack` CLI - see [Installing `pack` documentation](https://buildpacks.io/docs/install-pack/)
3. Build container with `pack`, and use `--publish` flag to push directly to the remote registry:

```bash
# Paketo Buildpack
PROJECT_ID=$(gcloud config get-value project)
pack build \
  --builder gcr.io/paketo-buildpacks/builder:base \
  --publish \
  gcr.io/${PROJECT_ID}/helloworld

# GCP Buildpack
PROJECT_ID=$(gcloud config get-value project)
pack build \
  --builder gcr.io/buildpacks/builder:v1 \
  --publish \
  gcr.io/${PROJECT_ID}/helloworld
```

{% hint style="info" %}
Learn about [Paketo Buildpack](https://paketo.io/) and [GCP Buildpack](https://github.com/GoogleCloudPlatform/buildpacks).
{% endhint %}

{% hint style="danger" %}
Paketo Buildpack will calculate the minimum memory needed to run the Spring Boot application. For this Hello World example, the minimum is 1GB of RAM.
{% endhint %}
{% endtab %}

{% tab title="Buildpack with Cloud Build" %}
Cloud Build has built-in Buildpack support, so you can build the container image in the remote Cloud Build environment:

```bash
# GCP Buildpack
PROJECT_ID=$(gcloud config get-value project)
gcloud alpha builds submit \
  --pack image=gcr.io/${PROJECT_ID}/helloworld

# Paketo Buildpack
PROJECT_ID=$(gcloud config get-value project)
gcloud alpha builds submit \
  --pack image=gcr.io/${PROJECT_ID}/helloworld,builder=gcr.io/paketo-buildpacks/builder:base
```

{% endtab %}

{% tab title="Spring Boot 2.3" %}
Since Spring Boot 2.3+, you can build container using the Spring Boot plugin.

```bash
PROJECT_ID=$(gcloud config get-value project)

# Maven with Paketo Buildpack
./mvnw spring-boot:build-image \
  -Dspring-boot.build-image.imageName=gcr.io/${PROJECT_ID}/helloworld

# Maven with GCP Buildpack
./mvnw spring-boot:build-image \
  -Dspring-boot.build-image.imageName=gcr.io/${PROJECT_ID}/helloworld \
  -Dspring-boot.build-image.builder=gcr.io/buildpacks/builder

# Gradle with Paketo Buildpack
./gradlew bootBuildImage --imageName=gcr.io/${PROJECT_ID}/helloworld

# Gradle with GCP Buildpack
./gradlew bootBuildImage --imageName=gcr.io/${PROJECT_ID}/helloworld \
  --builder=gcr.io/buildpacks/builder
```

After the image is built, push the docker image to Container Registry:

```bash
docker push gcr.io/${PROJECT_ID}/helloworld
```

{% hint style="info" %}
Learn about [Paketo Buildpack](https://paketo.io/) and [GCP Buildpack](https://github.com/GoogleCloudPlatform/buildpacks).
{% endhint %}

{% hint style="danger" %}
Paketo Buildpack will calculate the minimum memory needed to run the Spring Boot application. For this Hello World example, the minimum is 1GB of RAM.
{% endhint %}
{% endtab %}
{% endtabs %}

### Build Locally

If you are running a local Docker daemon and you do not want to push straight to a remote registry, then you can build container images without pushing:

{% tabs %}
{% tab title="Jib" %}

```bash
PROJECT_ID=$(gcloud config get-value project)
./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:dockerBuild \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

{% endtab %}

{% tab title="Buildpack" %}

```bash
# Paketo Buildpack
PROJECT_ID=$(gcloud config get-value project)
pack build \
  --builder gcr.io/paketo-buildpacks/builder:base \
  gcr.io/${PROJECT_ID}/helloworld

# GCP Buildpack
PROJECT_ID=$(gcloud config get-value project)
pack build \
  --builder gcr.io/buildpacks/builder:v1 \
  gcr.io/${PROJECT_ID}/helloworld
```

{% endtab %}

{% tab title="Spring Boot 2.3" %}

```bash
PROJECT_ID=$(gcloud config get-value project)

# Maven with Paketo Buildpack
./mvnw spring-boot:build-image \
  -Dspring-boot.build-image.imageName=gcr.io/${PROJECT_ID}/helloworld

# Maven with GCP Buildpack
./mvnw spring-boot:build-image \
  -Dspring-boot.build-image.imageName=gcr.io/${PROJECT_ID}/helloworld \
  -Dspring-boot.build-image.builder=gcr.io/buildpacks/builder

# Gradle with Paketo Buildpack
./gradlew bootBuildImage --imageName=gcr.io/${PROJECT_ID}/helloworld

# Gradle with GCP Buildpack
./gradlew bootBuildImage --imageName=gcr.io/${PROJECT_ID}/helloworld \
  --builder=gcr.io/buildpacks/builder
```

{% endtab %}
{% endtabs %}

## Run Locally

If you have Docker installed locally, you can run the docker container locally to ensure everything works. This command will run the container locally and forward localhost's port 8080 to the container instance's port 8080.

```bash
PROJECT_ID=$(gcloud config get-value project)

docker pull gcr.io/${PROJECT_ID}/helloworld
docker run -ti --rm -p 8080:8080 gcr.io/${PROJECT_ID}/helloworld
```

{% hint style="info" %}
The `-ti` flag means allocate a `TTY`, and expect interaction via `STDIN`. The `--rm` flag means delete the container completely upon exit.
{% endhint %}

## Connect Locally

You can connect to the container that's running locally

```bash
curl localhost:8080
```

##


# Secure Container Image

While [Creating a new container image](/deployment/docker/container-image#containerize) is fairly easy and straightforward for development purposes, you should consider building a secure container image for production use. Below are some basic considerations.

## No Source Code

It's a common mistake to copy too much data/information into a container image. In general, you should only have the content that's absolutely necessary to run your application. But there are things that often make it into a container image that you may not realize:

* Source code, build files are easily copied into a runtime container image by accident when using a Dockerfile.
* Version control directories, such as `.git` are easily copied into a runtime container image by accident when using a Dockerfile.

{% tabs %}
{% tab title="Jib" %}
Jib automatically builds thin container images without the source.
{% endtab %}

{% tab title="Buildpacks" %}
Paketo automatically builds thin container images without the source.

GCP Buildpack needs to set `GOOGLE_CLEAR_SOURCE=true` to remove the source from the container image. See [GCP Buildpack README](https://github.com/GoogleCloudPlatform/buildpacks#configuration) for more information.

```bash
PROJECT_ID=$(gcloud config get-value project)                                                            ⬢ system ⎈ demo-cluster
pack build \
  -e "GOOGLE_CLEAR_SOURCE=true"
  --builder gcr.io/buildpacks/builder:v1 \
  --publish \
  gcr.io/${PROJECT_ID}/helloworld
```

{% endtab %}
{% endtabs %}

## No Secrets/Credentials

Do not copy secrets and/or credentials into a container image (e.g., do not copy a service account key file!). For the most part, secrets can be stored in the runtime environment (e.g., a Kubernetes Secret), or better, a secret store (e.g., [Cloud Secret Manager](/app-dev/cloud-services/secret-management), or HashiCorp Vault).

## Minimal Base Image

Many base images comes with all the command line utilities from a typical Linux distribution (e.g., a shell, package manager, etc). These container images may allow you (or an attacker!) to get into a shell, and install additional tools. To reduce the attack surface, consider using a minimal base image that has the least attack surface. These images will be more secure, but may also be harder to debug.

{% tabs %}
{% tab title="Jib" %}
Jib uses the [Distroless](https://github.com/GoogleContainerTools/distroless/blob/master/java/README.md) base image by default.
{% endtab %}

{% tab title="Buildpacks" %}
Buildpack's runtime image ultimately does not use Distroless. Paketo, for example, executed shell script to calculate memory needs, and thus Shell is needed. There is no easy way to switch out the base image when using a Buildpack. You may need to create your own to change the base image.
{% endtab %}
{% endtabs %}

## Non-Root User

One of the most overlooked configuration for a container image is which user is used to run your application? In a VM environment, you would never want to run an application as `root`. It's no different in a container. Every container image may have a different set of non-privileged users.

For example, for a Distroless base image (using a debug image that has a shell):

```bash
docker run -ti --rm --entrypoint=sh \
  gcr.io/distroless/java:debug -c "cat /etc/passwd"
```

You'll see that it has only 3 users:

```
root:x:0:0:root:/root:/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/sbin/nologin
nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin
```

But, an AdoptOpenJDK base image has more system users, and you'll need to pick the one you want to use as the user to run your application:

```bash
docker run -ti --rm adoptopenjdk:11-jre-hotspot-bionic cat /etc/passwd
```

{% tabs %}
{% tab title="Jib" %}
Jib uses `root` user by default. You should configure it to use a non-root user according to the base image you use. For example:

```bash
PROJECT_ID=$(gcloud config get-value project)
./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Djib.container.user=nonroot:nonroot
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

Validate that the JVM was started with the `nonroot` user:

```bash
PROJECT_ID=$(gcloud config get-value project)

docker pull gcr.io/${PROJECT_ID}/helloworld
docker run -ti --rm --entrypoint=java \
  gcr.io/${PROJECT_ID}/helloworld \
  -XshowSettings:properties -version
```

Look for the `user.name` property is now `nonroot`.
{% endtab %}

{% tab title="Buildpacks" %}
Buildpacks (with Paketo and GCP builders) run as a non-root user by default, as the user `cnb`.
{% endtab %}
{% endtabs %}

## Summary

So, what do the automated tools do by default?

|                    | Jib                                                                | Paketo Builder       | GCP Builder                                                                                                                                    |
| ------------------ | ------------------------------------------------------------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| Source Code        | No source in runtime                                               | No source in runtime | <p>Set <code>GOOGLE\_CLEAR\_SOURCE.</code></p><p>See <a href="https://github.com/GoogleCloudPlatform/buildpacks#configuration">README</a>.</p> |
| Minimal Base Image | Uses Distroless                                                    | Not Distroless       | Not Distroless                                                                                                                                 |
| Non-Root User      | <p>Defaults to <code>root</code>,</p><p>Configure to non-root.</p> | `cnb` user           | `cnb` user                                                                                                                                     |


# Container Awareness

Learn the intricate details of how JVM applications see container resources and how it impacts heap, CPU, and threads.

When you containerize a Java application, make sure you use a base JDK image that is container-aware (CGroup aware) so that the JDK can allocate memory and CPU counts properly.

Older versions of JDK (prior to 8u192) may not have container awareness (or may have experimental support that requires explict flags to enable). Older versions of JDK may look at the traditional `/proc/meminfo` and `/proc/cpuinfo` files for available memory and CPUs. The content of these files reflects the amount of resources of the host/node machine that is running the container, but do not reflect the actual limits assigned to the container (which may be much less).

Newer versions of JDK (8u192 and above) will automatically discover the CGroup resource allocations located in `/sys/fs/cgroup/cpu` and `/sys/fs/cgroup/memory`.

## Heap

Run a Docker container and give it only 256MB of memory, and see an older version of JDK will assign for the default Max Heap.

```bash
docker run -ti --rm --cpus=1 --memory=256M openjdk:8u141-jre \
  java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
```

Because version `8u141` is not container-aware, it will output the `MaxHeapSize` (in bytes) that is calculated from the host machine and can be significantly higher than the 256MB of memory you originally assigned. This means your Java process may allocate heap aggressively and go beyond the original limit, causing the container instance to be killed, usually result in a `OOMKilled`message.

Run the same command, but with a newer version of JDK:

```bash
 docker run -ti --rm --cpus=1 --memory=256M openjdk:8u252-jre \
   java -XX:+PrintFlagsFinal -version | grep MaxHeapSize
```

The output of `MaxHeapSize` is now `132120576` bytes, which is \~126MB, indicating that it's now respecting the 256MB limitation we assigned for the container.

The JVM heap size should never be equal memory resource you assigned. In this case, even though we assigned 256MB of memory to the container, the Max Heap must be much lower than that (e.g., 50% of that, or depending on your application). This is because the JVM also uses native memory in addition to the heap.

JVM native memory usage contains thread stack, code cache, metaspace, and potentially direct memory buffer allocations.

#### Estimate Memory Needs

According to the [Cloud Foundry Java Buildpack Memory calculator documentation](https://docs.google.com/document/d/1vlXBiwRIjwiVcbvUGYMrxx2Aw1RVAtxq3iuZ3UK2vXA/edit), the total native memory needed for a JVM instance is approximately linear to the number of loaded classes.

You can use [Cloud Foundry Java Buildpack Memory calculator](https://github.com/cloudfoundry/java-buildpack-memory-calculator) to the memory needs and configurations.

#### Understand Memory Used

In cases where you are getting `OOMKilled` for your container instance, and have already made sure that you are using a container-aware version of JDK, then you may want to turn on [Native Memory Tracking](https://docs.oracle.com/javase/8/docs/technotes/guides/troubleshoot/tooldescr007.html).

Native Memory Tracking can only be enabled via command line argument, and cannot be enabled using `JAVA_TOOL_OPTIONS`.&#x20;

You can run this command to see a sample output of Native Memory Tracking:

```bash
docker run -ti --rm openjdk:8u252-jre \
  java -XX:+UnlockDiagnosticVMOptions \
  -XX:NativeMemoryTracking=summary \
  -XX:+PrintNMTStatistics \
  -version
```

Native Memory Tracking can only print out memory usage details upon a **successful** exit.

```bash
java -XX:+UnlockDiagnosticVMOptions \
  -XX:NativeMemoryTracking=summary \
  -XX:+PrintNMTStatistics \
  -jar ...
```

If your application was `OOMKilled`, then it's an unsuccessful exit, so the memory details may not be printed. In this case, consider first increase the amount of memory allocation, and then trigger a successful exit, to get the native memory usage details.

## CPU

Run a Docker container and giving it only 2 CPUs, and see what an older version of JDK will assign for the default Parallel GC threads.

```bash
docker run -ti --rm --cpus=2 openjdk:8u141-jre java \
  -XX:+PrintFlagsFinal -XX:+UseParallelGC -version | grep ParallelGCThreads
```

It will output the `ParallelGCThreads` that is calculated from the number of CPUs of the host machine and can be significantly higher than `2`.

Run the same command, but with a newer version of JDK:

```bash
docker run -ti --rm --cpus=2 --memory=256M openjdk:8u252-jre java \
  -XX:+PrintFlagsFinal -XX:+UseParallelGC -version | grep ParallelGCThreads
```

The output of `ParallelGCThreads` is `2`.

## Runtime API

When using non-container-aware JDK versions, both Memory and CPU can be inaccurately reflected in the [`Runtime`](https://docs.oracle.com/javase/8/docs/api/java/lang/Runtime.html) API as well.

```java
// Max heap you can use
Runtime.getRuntime().maxMemory()

// Number of processors
Runtime.getRuntime().availableProcessors()
```

This is important because some libraries and applications may use `availableProcessors` to determine the size of the thread pools. So, if you allocated only `2` CPUs, but the JVM inaccurately sees `32` CPUs from the host, then the libraries may over-allocate the thread pool size, and causing your application to run more than the underlying system allows.


# Vulnerability Scanning

[Cloud Container Analysis](https://cloud.google.com/container-registry/docs/container-analysis) can scan your container images stored in Container Registry for vulnerabilities. See [Vulnerability Scanning documentation](https://cloud.google.com/container-registry/docs/vulnerability-scanning) for more detail.

Container images are scanned upon push to Container Registry, and then continuously monitored/scanned if the image was pulled in the last 30 days.

## Enable API

```bash
gcloud services enable containeranalysis.googleapis.com
gcloud services enable containerscanning.googleapis.com
```

## Push an Image

Container images are scanned when they are pushed to Container Registry. To force a scan on an existing image, you  have to re-push it the image. For example, follow the [Container Image section](/deployment/docker/container-image), and re-push the Hello World container image.

```bash
PROJECT_ID=$(gcloud config get-value project)

./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

## Vulnerabilities

Once the image is scanned, you can see the status of Vulnerability Scanning in Container Registry.

```bash
PROJECT_ID=$(gcloud config get-value project)

open https://gcr.io/$PROJECT_ID/helloworld
```

On the right hand side, see the **Vulnerabilities** column:

![](/files/-MFkXpYhboNg6DsA6zuK)

Click into **View vulnerabilities** to see the details:

![](/files/-MFkY-Dvxwgr9QYHKbMG)

You can list vulnerabilities for a specific container image. It'll be outputted in the raw YAML format:

```bash
PROJECT_ID=$(gcloud config get-value project)

gcloud beta container images describe gcr.io/$PROJECT_ID/helloworld \
  --show-package-vulnerability
```

{% hint style="info" %}
See [Vulnerability Scanning documentation](https://cloud.google.com/container-registry/docs/vulnerability-scanning) for more information on vulnerability database sources.
{% endhint %}

## Continuous Scan

Container images are scanned upon push to Container Registry, and then continuously monitored/scanned if the image was pulled in the last 30 days.

{% hint style="info" %}
See [Vulnerability Scanning documentation](https://cloud.google.com/container-registry/docs/vulnerability-scanning) for more information.
{% endhint %}


# Attestation

To secure your software supply chain, you should consider signing your container images with attestations. Runtime environments like Kubernetes Engine can validate the signature and run only the container images that you have signed/attested with Binary Auth.

## Enable API

```bash
gcloud services enable container.googleapis.com
gcloud services enable containeranalysis.googleapis.com
gcloud services enable binaryauthorization.googleapis.com
```

## Attestor

You need to create an Attestor, which is associated with the metadata of the an asymetric key pair that's used to sign and validate a signature for an image digest.

### Create a Note

A [Note](https://cloud.google.com/container-registry/docs/metadata-storage#note) is a metadata entry in Google Container Analysis and is required when associating with an Attestor. An Attestation ultimately becomes an instance of a Note.

```bash
PROJECT_ID=$(gcloud config get-value project)
cat > $HOME/attestor-note.json << EOF
{
  "name": "projects/${PROJECT_ID}/notes/default-attestor",
  "attestation": {
    "hint": {
      "human_readable_name": "Default Container Image Attestor"
    }
  }
}
EOF
```

Post the Note to Container Analysis service:

```bash
PROJECT_ID=$(gcloud config get-value project)
curl -X POST \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $(gcloud auth print-access-token)"  \
  -H "x-goog-user-project: $PROJECT_ID" \
  --data-binary @$HOME/attestor-note.json\
  https://containeranalysis.googleapis.com/v1/projects/$PROJECT_ID/notes/?noteId=default-attestor
```

### Create an Attestor

```bash
PROJECT_ID=$(gcloud config get-value project)

gcloud beta container binauthz attestors create default-attestor \
    --attestation-authority-note=default-attestor \
    --attestation-authority-note-project=$PROJECT_ID
```

## Asymetric Key Pair

You need to create a key pair so that you can sign an attestation with a private key, and later, verify it with a public key. You can create your own key pair, but this guide will use Cloud KMS.

### Enable API

```bash
gcloud services enable cloudkms.googleapis.com
```

### Create a Keyring

```bash
gcloud kms keyrings create attestor-keyring --location global
```

### Create a Key

```bash
gcloud kms keys create default-attestor-key \
  --location=global \
  --keyring=attestor-keyring  \
  --purpose=asymmetric-signing  \
  --default-algorithm=ec-sign-p256-sha256
```

### Add Key to Attestor

```bash
PROJECT_ID=$(gcloud config get-value project)

gcloud alpha container binauthz attestors public-keys add \
  --attestor=default-attestor \
  --keyversion-project=$PROJECT_ID \
  --keyversion-location=global \
  --keyversion-keyring=attestor-keyring \
  --keyversion-key=default-attestor-key \
  --keyversion=1
```

## Attestation

You can create an attestation for a container image, but you'll need the full SHA256 container image digest. The easiest way to find this is from Container Registry:

```bash
PROJECT_ID=$(gcloud config get-value project)

gcloud container images describe gcr.io/$PROJECT_ID/helloworld
```

### Create an Attestation

```bash
PROJECT_ID=$(gcloud config get-value project)
IMAGE=$(gcloud container images describe gcr.io/$PROJECT_ID/helloworld \
  --format='value(image_summary.fully_qualified_digest)')

gcloud beta container binauthz attestations sign-and-create \
    --artifact-url=$IMAGE \
    --attestor=default-attestor \
    --attestor-project=$PROJECT_ID \
    --keyversion-project=$PROJECT_ID \
    --keyversion-location=global \
    --keyversion-keyring=attestor-keyring \
    --keyversion-key=default-attestor-key \
    --keyversion=1
```

### List Attestations

Once created, you can see the attestation:

```bash
PROJECT_ID=$(gcloud config get-value project)
IMAGE=$(gcloud container images describe gcr.io/$PROJECT_ID/helloworld \
  --format='value(image_summary.fully_qualified_digest)')
  
gcloud beta container binauthz attestations list \
  --artifact-url=$IMAGE \
  --attestor=default-attestor
```

## Binary Authorization

Once the container image has a signed attestation, it can then be used to authorize deployments into a Kubernetes Engine cluster by enabling Binary Authorization.

1. [Create a Kubernetes Engine cluster](/deployment/kubernetes/kubernetes-cluster#create-cluster) that has Binary Authorization enabled.
2. [Enable Binary Authorization](/deployment/kubernetes/binary-authorization#enforce-attestation) policy to enforce attestations.

{% hint style="info" %}
See [Binary Authorization](/deployment/kubernetes/binary-authorization) section for more information.
{% endhint %}


# Kubernetes

Kubernetes is becoming the de-facto cluster/container orchestration system. Learn how you can build and deploy Java applications with Kubernetes.

If you are unfamiliar with Kubernetes, you can learn more in this video:

{% embed url="<https://www.youtube.com/watch?v=kT1vmK0r184>" %}


# Kubernetes Cluster

Learn how to create a production-grade Kubernetes cluster to deploy your application.

This section requires basic understanding of Docker and container images - make sure you do the tutorial in sequence.

{% content-ref url="/pages/-MEV1HOrp\_C9aSYfVy6B" %}
[Container Image](/deployment/docker/container-image)
{% endcontent-ref %}

## Enable API

```bash
gcloud services enable compute.googleapis.com
gcloud services enable container.googleapis.com
```

## Create Cluster

While it's easy to create a Kubernetes Engine cluster, it takes a bit more to provision a production-grade cluster. This cluster will enable many features for production use:

| Feature                                                                                       | Description                                                                                                                                                                                             |
| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [Workload Identity](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity) | Workload Identity is the recommended way to access Google Cloud services from within GKE, so you can securely associate specific service account to a workload.                                         |
| [VPC Native Cluster](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips)        | Allow Kubernetes Pod IP addresses to be natively routable on a VPC. Most importantly, it allows one-hop from Google Cloud Load Balancer to the Kubernetes Pod without unnecessary intermediary routing. |
| [Network Policy](https://cloud.google.com/kubernetes-engine/docs/how-to/network-policy)       | Network policy enforcement to control the communication between your cluster's Pods and Services.                                                                                                       |
| [Cloud Operations](https://cloud.google.com/stackdriver/docs/solutions/gke/installing)        | Allows you to monitor your running Google Kubenetes Engine clusters, manage your system and debug logs, and analyze your system's performance using advanced profiling and tracing capabilities.        |
| [Binary Authorization](https://cloud.google.com/binary-authorization/docs)                    | Binary Authorization is a deploy-time security control that ensures only trusted container images are deployed on Google Kubernetes Engine.                                                             |
| [Shielded Nodes](https://cloud.google.com/kubernetes-engine/docs/how-to/shielded-gke-nodes)   | Shielded GKE Nodes provide strong, verifiable node identity and integrity to increase the security of GKE nodes.                                                                                        |
| [Secure Boot](https://cloud.google.com/security/shielded-cloud/shielded-vm#secure-boot)       | Secure Boot helps ensure that the system only runs authentic software by verifying the digital signature of all boot components, and halting the boot process if signature verification fails.          |
| [Auto Repair](https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-repair)        | Automatically repair a Google Kubernetes Engine node if it becomes unhealthy.                                                                                                                           |
| [Auto Upgrade](https://cloud.google.com/kubernetes-engine/docs/how-to/node-auto-upgrades)     | Automatically upgrade Google Kubernetes Engine nodes version to keep up to date with the cluster control plane version.                                                                                 |

```bash
PROJECT_ID=$(gcloud config get-value project)
gcloud container clusters create demo-cluster \
  --num-nodes 4 \
  --machine-type n1-standard-4 \
  --network=default \
  --workload-pool=${PROJECT_ID}.svc.id.goog \
  --enable-ip-alias \
  --enable-network-policy \
  --enable-stackdriver-kubernetes \
  --enable-binauthz \
  --enable-shielded-nodes \
  --shielded-secure-boot \
  --enable-autorepair \
  --enable-autoupgrade \
  --scopes=cloud-platform
```

These nodes will still have a public IP, and be able to access the public Internet. For most production clusters, you'll want to consider creating a [Private Cluster](https://cloud.google.com/kubernetes-engine/docs/how-to/private-clusters), and control egress via [Cloud NAT](https://cloud.google.com/nat/docs/gke-example).

## Credentials

Kubernetes credentials are automatically retrieved and stored in your `$HOME/.kube/config` file. If you need to re-retrieve the credential:

```bash
gcloud container clusters get-credentials demo-cluster
```

## Node Pool and Nodes

The Kubernetes cluster is composed of multiple Nodes - each node is a Compute Engine Virtual Machine.  When you deploy a container image into Kubernetes, a container instance is ultimately scheduled and ran on one of the Nodes.

In Kubernetes Engine, theses nodes are managed by a Node Pool, which is a set of homogenous Compute Engine Virtual Machines (i.e., they have exactly the same configuration, such as machine type, disk, operation system, etc).

{% hint style="info" %}
You can add different machine types to your Kubernetes Engine cluster, by creating a new Node Pool with the configuration you want.
{% endhint %}

You can see a list of Virtual Machines using `gcloud`:

```bash
gcloud compute instances list
```

You can also use `kubectl` to list the nodes that belong to the current cluster:

```bash
kubectl get nodes
```

You can also SSH into the node directly if needed, by specifying the name of the node:

```bash
gcloud compute ssh gke-demo-cluster-default-pool-...
```

Once you are in the Compute Engine Virtual Machine, you can also see the containers that are running inside of the Node:

```bash
docker ps
exit
```


# Deployment

Learn how to create a Kubernetes deployment and deploying the Hello World container image.

This section continues from the previous section - make sure you do the tutorial in sequence.

{% content-ref url="/pages/-MEV1HOrp\_C9aSYfVy6B" %}
[Container Image](/deployment/docker/container-image)
{% endcontent-ref %}

{% content-ref url="/pages/-MDKwCMFLQGEn89cU0V\_" %}
[Kubernetes Cluster](/deployment/kubernetes/kubernetes-cluster)
{% endcontent-ref %}

## Pod

A Kubernetes Pod is a group of tightly coupled containers, tied together that must start, stop, and scale together. In many case a Pod is associated to only one container.  Multiple containers in a single pod can be useful when you have, for example, a container that runs the application, and another container that periodically polls logs/metrics from the application container.

Every Pod has a unique, ephemeral, and routable IP address. I.e., a container inside of a Pod can directly reach another the IP address of another Pod (and the containers in that Pod).

All containers within a single pod are scheduled to a single physical resource (the same Node), and all containers within a Pod will share the same networking interface, IP address, volumes, etc.&#x20;

You can start a single Pod in Kubernetes by creating a Pod resource. However, a Pod created this way would be known as an Unmanaged Pod. If an Unmanaged Pod dies/exits, it will not be restarted by Kubernetes. A better way to start a Pod, is by using a higher-level construct such as a Deployment.

## Deployment

Deployment provides declarative way to manage a set of Pods. You only need to describe the desired state in a Deployment resource, and behind the scenes, a Kubernetes Deployment controller will change the actual state to the desired state for you. It does this using a resource called a ReplicaSet under the covers.

![](/files/-MDNjZrn80j6xcPcyx8B)

### Deployment YAML

You can create a Deployment and deploy into Kubernetes using `kubectl` command line like in the [Hello World tutorial](/getting-started/helloworld/kubernetes-engine). That's great to get a feel of Kubernetes. However, it's best that you create a YAML file first, and then deploy the YAML file.

```bash
# Under the helloworld-springboot-tomcat directory , create a k8s directory
mkdir k8s/

PROJECT_ID=$(gcloud config get-value project)
kubectl create deployment helloworld \
  --image=gcr.io/${PROJECT_ID}/helloworld \
  --dry-run \
  -o yaml > k8s/deployment.yaml
```

You can open the `k8s/deployment.yaml` file to see the content. Following is a version of the YAML file where it's slimmed down to the bare minimum.

{% code title="k8s/deployment.yaml" %}

```yaml
# API Version and Kind are important to indicate the type of resource
apiVersion: apps/v1
kind: Deployment
metadata:
  # Every Kubernetes resource has a name that's unique within a namespace
  name: helloworld
  # Every Kubernetes can have labels, label key/value pairs can be queried later.
  labels:
    app: helloworld
spec:
  # The number of the pods that start
  replicas: 1
  
  # The labels that matches the pods within this deployment
  selector:
    matchLabels:
      app: helloworld
  # Every instance of the pod will be created using the template below
  template:
    metadata:
      # Every new pod created by the deployment will have these labels
      # The name of a newly created pod will be generated
      labels:
        app: helloworld
    spec:
      containers:
      # Every container can have a name, and the container image to run
      - name: helloworld
        image: gcr.io/.../helloworld
```

{% endcode %}

{% hint style="info" %}
You can read more about Deployment in the [Kubernetes Deployment Guide](http://kubernetes.io/docs/user-guide/deployments/).
{% endhint %}

### Deploy

Use `kubectl` command line to deploy the YAML file:

```bash
kubectl apply -f k8s/deployment.yaml
```

To verify the application is deployed, see all the pods that are running:

```bash
kubectl get pods
```

You should see that there is one pod running!

```bash
NAME                         READY   STATUS    RESTARTS   AGE
helloworld-...               1/1     Running   0          ...
```

## Basic Interactions

### Describe Details

For every Kubernetes resource, you can describe the details of a resource, and see its current state, and any events, errors that may have occurred.

Describe a Deployment:

```bash
kubectl describe deployment helloworld
```

Describe a Pod:

```bash
POD_NAME=$(kubectl get pods -lapp=helloworld -o jsonpath='{.items[0].metadata.name}')
kubectl delete pod ${POD_NAME}
```

### Find Pods with Labels

`kubectl get pods` shows you every pod running in the current namespace. You can limit the output to just the application you are interested in by select only pods matching certain label key/value pairs.&#x20;

```bash
kubectl get pods -lapp=helloworld
```

### Scaling

Scale the number of instances from 1 to 2.

```bash
kubectl scale deployment helloworld --replicas=2
```

Verify that there are now 2 pods running:

```bash
kubectl get pods
```

### Delete a Pod

Out of the 2 pods, pick one to delete, and then observe that Kubernetes automatically starts another pod instance so that there are always 2 pods running.

```bash
POD_NAME=$(kubectl get pods -lapp=helloworld -o jsonpath='{.items[0].metadata.name}')
kubectl delete pod ${POD_NAME}
```

Verify that there still 2 pods running, but one of them has a lower `age` indicating it's recently started.

```bash
kubectl get pods
```

### Delete All Pods

You can use labels to delete all pods matching certain label key/value pairs.

```bash
kubectl delete pod -lapp=helloworld
```

### Stream Logs

You can see the logs from the pod, and follow the log as new logs are produced:

```bash
POD_NAME=$(kubectl get pods -lapp=helloworld -o jsonpath='{.items[0].metadata.name}')
kubectl logs -f ${POD_NAME}
```

### Executing Commands

You can execute commands directly in the container instance. However, the container image will need to contains the command that you'd like to run.  The Hello World application built with Jib uses a Distroless base image by default - and the Distroless base image does not come with any shell commands for security purposes.

Let's deploy an Nginx container that contains the executables and see how you can shell into the container instance.

```bash
kubectl apply -f https://raw.githubusercontent.com/kubernetes/website/master/content/en/examples/controllers/nginx-deployment.yaml
```

See that Nginx container is running:

```bash
kubectl get pods -lapp=nginx
```

Use a specific Nginx pod, and shell into the container instance:

```bash
POD_NAME=$(kubectl get pods -lapp=nginx -o jsonpath='{.items[0].metadata.name}')
kubectl exec -ti ${POD_NAME} /bin/bash
```

{% hint style="info" %}
The `-ti` flag means to receive output from TTY, and also that the session is interactive (i.e., you'll be typing commands).
{% endhint %}

Once you are in the container instance's shell, you can explore the container instance:

```bash
# Within the container instance shell:
ls
ls /sbin
ls /bin
exit
```

In this Nginx container image, you can see that there are actually many command line utilities that's not needed for production deployment of an Nginx server. Exposing more commands like this may increase attack surface area if the container instance is compromised.  For this reason, Distroless base images do not include any commands. On the other hand, lack of commands may increase the difficulty to debug the application instance.

{% hint style="warning" %}
Delete the Nginx deployment before you continue!

`kubectl delete deployment nginx-deployment`
{% endhint %}


# Resources

Learn how to assign CPU/memory resources to your containerized application.

This section continues from the previous section - make sure you do the tutorial in sequence.

{% content-ref url="/pages/-MDMCQpZLAWjRCEWGOpp" %}
[Deployment](/deployment/kubernetes/deployment)
{% endcontent-ref %}

## Default Configuration

You can specify the computing resource needs for each of the containers. By default, each container is given 10% of a CPU and no memory use restrictions.

{% hint style="danger" %}
The defaults can cause issues:

* If a Node has 1 full CPU, then Kubernetes may schedule up to 10 instances of the same container, which may overload the system.
* If a Node has 16GB of RAM, and without memory restriction, then each container instance (JVM) may think they each can use up to 16GB, causing memory overuse (and thus, virtual memory swapping, etc)
  {% endhint %}

You can see the current resource by describing a Pod instance, look for the Requests/Limits lines.

```bash
POD_NAME=$(kubectl get pods -lapp=helloworld -o jsonpath='{.items[0].metadata.name}')

kubectl describe pod $POD_NAME
```

The details should have a `Requests` section with `cpu` value set to `100m`:

```
Name:           helloworld-...
Namespace:      default...
Containers:
  helloworld:
    ...
    Requests:
      cpu:  100m
...
```

{% hint style="info" %}
The default value is `100m`, which means `100 milli` = `100/1000` = `10%`of a vCPU core.
{% endhint %}

The default is configured per Namespace. The application was deployed into the `default` Namespace. Look at the default resource configuration for this Namespace:

```bash
kubectl describe ns default
```

See the output:

```bash
Name:         default
Labels:       <none>
Annotations:  <none>
Status:       Active

Resource Quotas
 Name:                       gke-resource-quotas
 Resource                    Used  Hard
 --------                    ---   ---
 count/ingresses.extensions  1     100
 count/jobs.batch            0     5k
 pods                        3     1500
 services                    2     500

Resource Limits
 Type       Resource  Min  Max  Default Request  Default Limit  Max Limit/Request Ratio
 ----       --------  ---  ---  ---------------  -------------  -----------------------
 Container  cpu       -    -    100m             -              -
```

However, the configuration is actually stored in a `LimitRange` Kubernetes resource:

```bash
kubectl get limitrange limits -oyaml
```

{% hint style="info" %}
The default can be updated. See [Configure Default CPU Requests and Limits for a Namespace](https://kubernetes.io/docs/tasks/administer-cluster/manage-resources/cpu-default-namespace/) documentation.
{% endhint %}

## Resource Request

In Kubernetes, you can reserve capacity by setting the Resource Requests to reserve more CPU and memory. Configure the deployment to reserve at least `20%` of a CPU, and `128Mi` of RAM.

{% code title="k8s/deployment.yaml" %}

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: helloworld
  name: helloworld
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - image: gcr.io/.../helloworld
        name: helloworld
        # Add the resources requests block
        resources:
          requests:
            cpu: 200m
            memory: 128Mi
```

{% endcode %}

{% hint style="info" %}
In this example, CPU request is `200m` which means `200 milli`=`200/1000` = `20%` of 1 vCPU core.

Memory is `128Mi`, which is `128 Mebibytes` = `~134 Megabytes`.
{% endhint %}

{% hint style="info" %}
See [Kubernetes Resource Units](https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/#resource-units-in-kubernetes) documentation for the units descriptions such as `m`, `M`, and `Mi`.
{% endhint %}

{% hint style="danger" %}
When specifying the Memory resource allocation, do not accidentally use `m` as the unit. `128m` means `0.128 bytes`.
{% endhint %}

## Resource Limit

The application can consume more CPU and memory than requested - it can burst up to the limit, but cannot exceed the limit. Configure the deployment to set the limit:

{% code title="k8s/service.yaml" %}

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: helloworld
  name: helloworld
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - image: gcr.io/.../helloworld
        name: helloworld
        # Add the resources requests block
        resources:
          requests:
            cpu: 200m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 256Mi
```

{% endcode %}

{% hint style="info" %}
CPU limit is a *compressible* resource. If the application exceeds the CPU limit, it'll simply be throttled, and thus capping the latency and throughput.
{% endhint %}

{% hint style="danger" %}
Memory is not a compressible resource. If the application exceeds the Memory limit, then the container will be killed (`OOMKilled`) and restarted.
{% endhint %}

{% hint style="info" %}
For Java applications, read the [Container Awareness](/deployment/docker/container-awareness) section to make sure you are using a Container-Aware OpenJDK version to avoid unnecessary `OOMKilled` errors.
{% endhint %}


# Service

Learn how to create a Kubernetes service and how service discovery works.

This section continues from the previous section - make sure you do the tutorial in sequence.

{% content-ref url="/pages/-MDMCQpZLAWjRCEWGOpp" %}
[Deployment](/deployment/kubernetes/deployment)
{% endcontent-ref %}

## Service

Each Pod has a unique IP address - but the address is ephemeral. The Pod IP addresses are not stable and it can change when Pods start and/or restart. Moreover, if you have a Deployment that starts multiple Pods, and you need to consume an API from the Pods, you do not want to connect using an ephemeral IP address. Usually, you'll need to add a load balancer to distribute traffic to each individual instance.

In Kubernetes, a Service is a Network (L4) Load Balancer that'll provide you a single stable Service IP (and hostname) to load balance the traffic to a set of pods selected by using labels.

### Service YAML

You can create a Service and deploy into Kubernetes using the `kubectl` CLI like in the [Hello World tutorial](/getting-started/helloworld/kubernetes-engine). That's great to get a feel of Kubernetes. However, it's best that you create a YAML file first, and then deploy the YAML file.

```bash
# Under the helloworld-springboot-tomcat directory , create a k8s directory
mkdir k8s/

kubectl create service clusterip helloworld \
  --tcp=8080:8080 \
  --dry-run \
  -o yaml > k8s/service.yaml
```

You can open the `k8s/service.yaml` file to see the content. Below is a version of the YAML file that's slimmed down to the bare minimum.

{% code title="k8s/service.yaml" %}

```yaml
# API Version and Kind are important to indicate the type of resource
apiVersion: v1
kind: Service
metadata:
  # Every Kubernetes resource has a name that's unique within a namespace
  name: helloworld
  # Every Kubernetes can have labels, label key/value pairs can be queried later.
  labels:
    app: helloworld
spec:
  # The type of the service - this one is an internal only service
  type: ClusterIP
  # Any Pods that matches these labels will be load balanced through
  # this Service (L4 load balancer)
  selector:
    app: helloworld
  ports:
    # A port can have a name, it can be renamed to be more descriptive
    # such as "http", "jmx", "metrics", etc.
  - name: 8080-8080 
    # The port to listen on by the Service (L4 Load Balancer)
    port: 8080
    # The port to forward traffic to on the destination Pod
    targetPort: 8080
    # TCP or UDP protocol
    protocol: TCP
```

{% endcode %}

{% hint style="info" %}
You can read more about Deployment in the [Kubernetes Service Guide](http://kubernetes.io/docs/user-guide/deployments/).
{% endhint %}

### Deploy

Use `kubectl` command line to deploy the YAML file:

```bash
kubectl apply -f k8s/service.yaml
```

Verify that the service is configured:

```bash
kubectl get svc helloworld
```

You should see that the Service has a Cluster IP address:

```bash
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
helloworld   ClusterIP   ...          <none>        8080/TCP   7s
```

## Service Discovery

In a traditional Spring Cloud application, service discovery is done using an external service registry like Eureka, and client-side load balancing with Ribbon.

In Kubernetes, the Kubernetes Service itself can act as the service registry:

* You can discover all the endpoints associated with a service
* Kubernetes Service's Cluster IP is a built-in L4 load balancer, and it's automatically associated with a DNS name

### Endpoints

Each Service has a `selector` block that is used to find Pods with matching labels and enlisting them as an Endpoint of the Service (or, you can think of it as a backend instance of a load balancer).

In the example above, the selector is `app: helloworld`, you can also find the matching Pods using `kubectl`:

```bash
# Scale out the # of instances so we can see more than one pod
kubectl scale deployment helloworld --replicas=2

# Find Pods using a selector
kubectl get pods -lapp=helloworld
```

Describe the Service to see the Endpoint IP addresses that it's currently enlisted,, and look for the `Endpoints` output:

```bash
kubectl describe svc helloworld
```

It is possible to continue to use Ribbon for client-side load balancing by retrieving these endpoints using the Kubernetes API. This is an advanced usage and not covered in this documentation. However, you can achieve a similar result if you use [Spring Cloud Kubernetes](https://spring.io/projects/spring-cloud-kubernetes) to replace Spring Cloud Eureka.

### DNS Name

The newly created Kubernetes Service is also a L4 Load Balancer, and it can be accessed using the Cluster IP, or the hostname `helloworld`, or a Fully Qualified Name (FQN) of `helloworld.default.svc.cluster.local`.

{% hint style="info" %}
See [Kubernetes DNS for Services and Pods documentation](https://kubernetes.io/docs/concepts/services-networking/dns-pod-service/) for more detail on how a DNS name is associated with a Service.
{% endhint %}

This service is currently only accessible from within the Kubernetes cluster. I.e., there is no public IP address. You can start a new Pod within the cluster that has a shell, and execute commands within the cluster. This accurately simulates a client application sending request to another backend service.

`nginx` container image has a shell that we can use, so deploy one instance of `nginx`.

```bash
kubectl create deployment nginx --image=nginx
```

Attach to the `nginx` Pod:

```bash
POD_NAME=$(kubectl get pods -lapp=nginx -o jsonpath='{.items[0].metadata.name}')
kubectl exec -ti ${POD_NAME} /bin/bash
```

From within the Pod, `curl` the service:

```bash
# From within the nginx Pod
curl http://helloworld:8080

exit
```

{% hint style="info" %}
If you have a client service that needs to reach the `helloworld` service within the same cluster, you can simply use the DNS name. This will resolve to the Cluster IP, and subsequently, L4 load balanced to one of the backend endpoints.
{% endhint %}


# Health Checks

Learn how Kubernets checks the application health, and how to use liveness probes and readiness probes.

This section continues from the previous section - make sure you do the tutorial in sequence.

{% content-ref url="/pages/-MDNjfpbBni\_y2D1Ue5v" %}
[Service](/deployment/kubernetes/service)
{% endcontent-ref %}

## Spring Boot Actuator

[Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/2.3.0.BUILD-SNAPSHOT/reference/html/production-ready-features.html#production-ready-enabling) can provide some basic health checking mechanisms via the `/actuator/health` endpoint. However, which endpoint you use depends on the Spring Boot version.

Spring Boot 2.3 and above, [Spring Boot Actuator has dedicated support for Liveness Probe](https://docs.spring.io/spring-boot/docs/2.3.0.BUILD-SNAPSHOT/reference/html/production-ready-features.html#production-ready-kubernetes-probes).

Spring Boot < 2.3 and below, it's best to create a simple endpoint that simply returns HTTP `200` response status instead of using the Spring Boot Actuator's `/actuator/health` endpoint. This is because `/actuator/health` by default may fail if an external dependency fails.

| Spring Boot Version | Liveness Probe                         | Readiness Probe            |
| ------------------- | -------------------------------------- | -------------------------- |
| >= 2.3              | /actuator/health/liveness              | /actuator/health/readiness |
| < 2.3               | Any endpoint that simply returns `200` | /actuator/health           |

### Clone

```bash
git clone https://github.com/saturnism/jvm-helloworld-by-example
cd jvm-helloworld-by-example/helloworld-springboot-tomcat
```

### Add Dependencies

```markup
<project>
  ...
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    
    ...
  </dependencies>
  ...
</project>
```

### Build

```
./mvnw package
```

### Containerize

Use Jib to containerize the application:

```bash
PROJECT_ID=$(gcloud config get-value project)

./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \
  -Dimage=gcr.io/${PROJECT_ID}/helloworld
```

{% hint style="info" %}
Learn different ways to containerize a Java application in the [Container Image](/deployment/docker/container-image) section.
{% endhint %}

## Liveness Probe

Kubernetes can automatically detect application issues using a Liveness Probe. When the Liveness Probe check fails, Kubernetes will automatically restart the container, in case restarting your application helps it to recover. If the container continues to fail the Liveness Probe, Kubernetes will go into a Crash Loop and backs off the restart exponentially.

{% hint style="info" %}
Liveness Probe failure indicates to Kubernetes that the failure can be recovered after a restart.
{% endhint %}

{% hint style="danger" %}
If your Liveness Probe checks an endpoint that fails due to an external dependency, but the external dependency cannot recover simply because your container restarts, then it's not a good check! This type of checks may cause catastropic cascading failures.
{% endhint %}

{% code title="k8s/deployment.yaml" %}

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: helloworld
  name: helloworld
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - image: gcr.io/.../helloworld
        name: helloworld
        resources:
          requests:
            cpu: 200m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 256Mi
        # Configure the liveness probe
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 10
```

{% endcode %}

{% hint style="info" %}
In addition to `httpGet`, you can also configure different type of probes such as `exec` to execute a command to perform a non-HTTP check, or use `tcpSocket` to simply check if a port is listening. See Kubernetes [Configure Liveness, Readiness, and Startup Probes documentation](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-startup) for more details.
{% endhint %}

{% hint style="info" %}
Notice the additional `initialDelaySeconds` configuration. If your application starts slowly (e.g., 1 minute to start), and the `livenessProbe` starts the check early (e.g., 10 seconds), then the `livenessProbe` might never succeed - causing the application to always getting restarted.
{% endhint %}

{% hint style="success" %}
When configuring a `livenessProbe`, always consider the initial delay needed for your application.
{% endhint %}

## Readiness Probe

Even if your application is alive, it doesn't mean that it's ready to receive traffic. For example, during the startup, the application is alive, but it needs to pre-load data, or warmup caches, before it's ready to accept traffic. A Readiness Probe will let Kubernetes know when your application is ready to receive traffic, and only then will the instance be enlisted into the load balancer as a backend to serve requests (i.e., a Service's Endpoint).

{% code title="k8s/deployment.yaml" %}

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: helloworld
  name: helloworld
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - image: gcr.io/.../helloworld
        name: helloworld
        resources:
          requests:
            cpu: 200m
            memory: 256Mi
          limits:
            cpu: 500m
            memory: 256Mi
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
        # Configure the readiness probe
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
```

{% endcode %}

{% hint style="success" %}
You should always configure a `readinessProbe`. Even if you don't use Spring Boot Actuator, you can point the probe to `/` or some endpoint that indicates the traffic is ready serve.
{% endhint %}


# Load Balancing

Learn the different ways to load balance traffic both for both external and internal consumers.


# External Load Balancing

This section continues from the previous section - make sure you do the tutorial in sequence.

{% content-ref url="/pages/-MDNjfpbBni\_y2D1Ue5v" %}
[Service](/deployment/kubernetes/service)
{% endcontent-ref %}

There are primarily 2 ways to expose a Kubernetes Service on the public internet:

| Type                                                                                   | Protocol | Locality | When to use?                                                                                                                                                        |
| -------------------------------------------------------------------------------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| [External Network Load Balancer](https://cloud.google.com/load-balancing/docs/network) | TCP/UDP  | Regional | Non-HTTP requests, or no need for a global load balancer. Connection to the Load Balancer is routed by public Internet to region of the load balancer.              |
| [External HTTP(s) Load Balancer](https://cloud.google.com/load-balancing/docs/https)   | HTTP(s)  | Global   | HTTP requests. GCP's L7 Load Balancer is a global load balancer - a single IP address can automatically route traffic to the nearest region within the GCP network. |

## External Network Load Balancer

### Service YAML

To create an external network load balancer, simply change Kubernetes Service's type from `clusterip` to `loadbalancer`. Modify the `k8s/service.yaml`:

{% code title="k8s/service.yaml" %}

```yaml
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  labels:
    app: helloworld
  annotations:
    cloud.google.com/neg: '{"exposed_ports": {"8080":{}}}'
spec:
  ports:
  - name: 8080-8080
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: helloworld
  # Use LoadBalancer type instead of ClusterIP
  type: LoadBalancer
```

{% endcode %}

### Deploy

Use `kubectl` command line to deploy the YAML file:

```bash
kubectl apply -f k8s/service.yaml
```

To verify the application is deployed, run :

```bash
kubectl get svc helloworld
```

You should see that the Service has a Cluster IP address, but also the External IP address with the initial value of `<pending>`. This is because, behind the scenes, Kubernetes Engine is provisioning a Google Cloud Network Load Balancer.

```bash
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
helloworld   ClusterIP   ...          <pending>     8080/TCP   7s
```

### Connect

Continuously check the External IP address, until an IP address is assigned. Once the IP Address is assigned, you can connect to the External IP address, and it'll be load balanced to the `helloworld` service backend pods.

```bash
EXTERNAL_IP=$(kubectl get svc helloworld -ojsonpath="{.status.loadBalancer.ingress[0].ip}")
curl $EXTERNAL_IP:8080
```

### Static IP Address

You can assign a static IP address to the Network Load Balancer.

Reserve a regional static IP address:

```bash
REGION=$(gcloud config get-value compute/region)
gcloud compute addresses create helloworld-service-ip --region=${REGION}
```

See the reserved IP address:

```bash
REGION=$(gcloud config get-value compute/region)
gcloud compute addresses describe helloworld-service-ip --region=${REGION} --format='value(address)'
```

Update the `k8s/service.yaml` to pin the Load Balancer IP address:

{% code title="k8s/service.yaml" %}

```yaml
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  labels:
    app: helloworld
spec:
  ports:
  - name: 8080-8080
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: helloworld
  type: LoadBalancer
  # Replace the value with the IP address you reserved
  loadBalancerIP: RESERVED_IP_ADDRESS
```

{% endcode %}

## External HTTP Load Balancer

You can configure an external HTTP load balancer using Kubernetes Ingress. In order for the HTTP Load Balancer to find the backends, it's recommended to use [container-native load balancing](https://cloud.google.com/kubernetes-engine/docs/how-to/container-native-load-balancing) on Google Cloud.

### Service YAML

In the `k8s/service.yaml`, use the `cloud.google.com/neg` annotation to enable Network Endpoint Group (NEG) in order to use container-native load balancing:

{% code title="k8s/service.yaml" %}

```yaml
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  labels:
    app: helloworld
  # Add the NEG annotation to enable Network Endpoint Group
  # in order to use container-native load balancing
  annotations:
    cloud.google.com/neg: '{"ingress": true}'
spec:
  ports:
  - name: 8080-8080
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: helloworld
  type: ClusterIP
```

{% endcode %}

### Ingress YAML

Create a Kubernetes Ingress configuration that will create the HTTP Load Balancer. Create a `k8s/ingress.yaml`:

{% code title="k8s/ingress.yaml" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: helloworld
spec:
  rules:
  - http:
      paths:
      - path: /*
        backend:
          service:
            name: helloworld
            post:
              number: 8080
```

{% endcode %}

### Deploy

Use `kubectl` command line to deploy the YAML files:

```bash
# Delete the existing service because it may contain a node port:
kubectl delete -f k8s/service.yaml

# Redeploy the service
kubectl apply -f k8s/service.yaml

# Deploy the ingress
kubectl apply -f k8s/ingress.yaml
```

To verify the Ingress is deployed:

```bash
kubectl get ingress helloworld
```

You should see that the Ingress has an IP address provisioned:

```bash
NAME         HOSTS   ADDRESS         PORTS   AGE
helloworld   *       ...             80      81s
```

Many Google Cloud components are being configured behind the scenes to enable global load balancing. It'll take a few minutes before the address is accessible. Use `kubectl describe` to see the current status:

```bash
kubectl describe ingress helloworld
```

Initially, you may see:

```bash
Name:             helloworld
Namespace:        default
Address:          ...
Default backend:  helloworld:8080 (...)
Rules:
  Host  Path  Backends
  ----  ----  --------
  *     *     helloworld:8080 (...)
Annotations:
  ...
  ingress.kubernetes.io/backends:  {"...":"Unknown"}
```

When the annotation value of `ingress.kubernetes.io/backends` is `Unknown`, it means that the backend is not yet accessible.

Re-check the status until the backend becomes `HEALTHY`.

```bash
Name:             helloworld
Namespace:        default
Address:          ...
Default backend:  helloworld:8080 (...)
Rules:
  Host  Path  Backends
  ----  ----  --------
  *     *     helloworld:8080 (...)
Annotations:
  ...
  ingress.kubernetes.io/backends:  {"...":"HEALTHY"}
```

### Connect

You can then use the IP address to connect:

```bash
EXTERNAL_IP=$(kubectl get ingress helloworld -ojsonpath="{.status.loadBalancer.ingress[0].ip}")
curl $EXTERNAL_IP
```

### Static IP Address

By default, the Ingress IP address is ephemeral - it'll change if you ever delete and recreate the Ingress. You can associate the Ingress with a static IP address instead.

#### Global Static IP Address

Reserve a global static IP address:

```bash
gcloud compute addresses create helloworld-ingress-ip --global
```

See the static IP address you reserved:

```bash
gcloud compute addresses describe helloworld-ingress-ip --global \
  --format='value(address)'
```

#### Configurations

In `k8s/ingress.yaml`, use the `kubernetes.io/ingress.global-static-ip-name` annotation to specify the IP name:

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: helloworld
  annotations:
    kubernetes.io/ingress.global-static-ip-name: "helloworld-ingress-ip"
spec:
  rules:
  - http:
      paths:
      - path: /*
        backend:
          service:
            name: helloworld
            post:
              number: 8080
```

#### Deploy

Deploy the Ingress:

```bash
kubectl apply -f k8s/ingress.yaml
```

Continuously check the IP address to be updated. It'll take several minutes for the IP address to update:

```bash
kubectl get ingress helloworld
```

### SSL Certificate

In order to use a SSL certificate to serve HTTPs traffic, you must use a real fully qualified domain name and configure it to point to the IP address. If you don't have a real domain, then you can use [xip.io](https://xip.io).

```bash
EXTERNAL_IP=$(kubectl get ingress helloworld -ojsonpath="{.status.loadBalancer.ingress[0].ip}")
DOMAIN="${EXTERNAL_IP}.xip.io"
curl $DOMAIN

echo $DOMAIN
```

You can provision the External HTTP(s) Load Balancer using Ingress with a Managed Certificate, or you can provide your own Self-Managed Certificate.

#### Managed Certificate

Google Cloud can automatically provision a certificate for your domain name when using the External HTTP(s) Load Balancer.

Create a new `k8s/certificate.yaml`:

{% tabs %}
{% tab title="With xip.io" %}

```bash
EXTERNAL_IP=$(kubectl get ingress helloworld -ojsonpath="{.status.loadBalancer.ingress[0].ip}")
DOMAIN="${EXTERNAL_IP}.xip.io"

cat << EOF > k8s/certificate.yaml
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
  name: helloworld-certificate
spec:
  domains:
  # Replace the value with your domain name
  - ${DOMAIN}
EOF
```

{% endtab %}

{% tab title="With Custom Domain" %}
{% code title="k8s/certificate.yaml" %}

```yaml
apiVersion: networking.gke.io/v1
kind: ManagedCertificate
metadata:
  name: helloworld-certificate
spec:
  domains:
  # Replace the value with your domain name
  - YOUR_DOMAIN_NAME
```

{% endcode %}
{% endtab %}
{% endtabs %}

In `k8s/ingress.yaml`, use the `networking.gke.io/managed-certificates` annotation to associate the certificate:

{% code title="k8s/ingress.yaml" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: helloworld
  annotations:
    kubernetes.io/ingress.global-static-ip-name: "helloworld-ingress-ip"
    # Associate the ingress with the certificate name
    networking.gke.io/managed-certificates: "helloworld-certificate"
spec:
  rules:
  - http:
      paths:
      - path: /*
        backend:
          service:
            name: helloworld
            post:
              number: 8080
```

{% endcode %}

Deploy both files:

```bash
kubectl apply -f k8s/certificate.yaml
kubectl apply -f k8s/ingress.yaml
```

It may take several minutes to provision the certificate. Check the Managed Certificate status:

```bash
kubectl describe managedcertificate helloworld-certificate
```

Wait until the Certificate Status becomes `ACTIVE`:

```
Name:         helloworld
Namespace:    default
...
Status:
  Certificate Name:    ...
  Certificate Status:  Active
...
```

You can then use HTTPs to connect:

{% tabs %}
{% tab title="Use xip.io" %}

```bash
EXTERNAL_IP=$(kubectl get ingress helloworld -ojsonpath="{.status.loadBalancer.ingress[0].ip}")
DOMAIN="${EXTERNAL_IP}.xip.io"

curl "https://${DOMAIN}"
```

{% endtab %}

{% tab title="With Custom Domain" %}

```bash
curl https://YOUR_DOMAIN_NAME
```

{% endtab %}
{% endtabs %}

{% hint style="info" %}
See [Using Google-managed SSL certificates](https://cloud.google.com/kubernetes-engine/docs/how-to/managed-certs) for more details.
{% endhint %}

#### Self-Managed Certificate

You can configure the Ingress to serve with your own SSL certificate. Usually you would already have a certificate/key pair.

If you don't already have one, you can provision a self-signed certificate for non-production use.

```bash
EXTERNAL_IP=$(kubectl get ingress helloworld -ojsonpath="{.status.loadBalancer.ingress[0].ip}")
DOMAIN="${EXTERNAL_IP}.xip.io"

mkdir -p cert/

# Generate a key
openssl genrsa -out cert/helloworld-tls.key 2048

# Generate a certificate signing request
openssl req -new -key cert/helloworld-tls.key \
  -out cert/helloworld-tls.csr \
  -subj "/CN=${DOMAIN}"

# 
openssl x509 -req -days 365 -in cert/helloworld-tls.csr \
  -signkey cert/helloworld-tls.key \
  -out cert/helloworld-tls.crt
```

Create a Kubernetes Secret to hold the certificate/key pair:

```bash
kubectl create secret tls helloworld-tls \
  --cert cert/helloworld-tls.crt --key cert/helloworld-tls.key \
  --dry-run -oyaml > k8s/tls-secret.yaml
```

Update the Ingress to refer to the secret for TLS certificate/key pair:

{% code title="k8s/ingress.yaml" %}

```yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: helloworld
  annotations:
    kubernetes.io/ingress.global-static-ip-name: "helloworld-ingress-ip"
spec:
  # Associate with the TLS certificate/key pair by the secret name
  tls:
  - secretName: helloworld-tls
  rules:
  - http:
      paths:
      - path: /*
        backend:
          service:
            name: helloworld
            post:
              number: 8080
```

{% endcode %}

Deploy the configurations:

```bash
kubectl apply -f k8s/tls-secret.yaml
kubectl apply -f k8s/ingress.yaml
```

It will take several minutes for the new configuration to take effect.

You can then use HTTPs to connect. However, if you used a self-signed certificate, you will need to ignore certificate validation errors:

```bash
EXTERNAL_IP=$(kubectl get ingress helloworld -ojsonpath="{.status.loadBalancer.ingress[0].ip}")
DOMAIN="${EXTERNAL_IP}.xip.io"

curl -k "https://${DOMAIN}"
```

{% hint style="info" %}
See [Using multiple SSL certificates in HTTP(s) load balancing with Ingress](https://cloud.google.com/kubernetes-engine/docs/how-to/ingress-multi-ssl) for more details.
{% endhint %}


# Internal Load Balancing

This section continues from the previous section - make sure you do the tutorial in sequence.

{% content-ref url="/pages/-MDNjfpbBni\_y2D1Ue5v" %}
[Service](/deployment/kubernetes/service)
{% endcontent-ref %}

## In-Cluster Load Balancer

A [Kubernetes Service](/deployment/kubernetes/service#service) acts as an internal L4 load balancer only accessible from within the same Kubernetes Cluster. See the [Service section](/deployment/kubernetes/service#service) for more information.

## Internal Network Load Balancer

The setup of the Internal Network Load Balancer is similar to the [External Network Load Balancer](/deployment/kubernetes/load-balancing/external-load-balancing#external-network-load-balancer), but with an additional annotation.

### Service YAML

In `k8s/service.yaml`, use the `cloud.google.com/load-balancer-type` annotation to mark the service to use the Internal Network Load Balancer:

{% code title="k8s/service.yaml" %}

```yaml
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  annotations:
    # Indicate this is an Internal Network Load Balancer
    cloud.google.com/load-balancer-type: "Internal"
  labels:
    app: helloworld
spec:
  ports:
  - name: 8080-8080
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: helloworld
  # Use LoadBalancer type instead of ClusterIP
  type: LoadBalancer
```

{% endcode %}

### Internal Static IP

You can assign an internal static IP address to the Network Load Balancer.

Reserve a regional static IP address:

```bash
REGION=$(gcloud config get-value compute/region)

gcloud compute addresses create helloworld-service-internal-ip \
  --subnet=default --region=${REGION}
```

See the reserved IP address:

```bash
REGION=$(gcloud config get-value compute/region)

gcloud compute addresses describe helloworld-service-internal-ip \
  --region=${REGION} --format='value(address)'
```

Update the `k8s/service.yaml` to pin the Load Balancer IP address:

{% code title="k8s/service.yaml" %}

```yaml
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  annotations:
    cloud.google.com/load-balancer-type: "Internal"
  labels:
    app: helloworld
spec:
  ports:
  - name: 8080-8080
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: helloworld
  type: LoadBalancer
  # Replace the value with the IP address you reserved
  loadBalancerIP: RESERVED_IP_ADDRESS
```

{% endcode %}

## Internal HTTP(s) Load Balancer

The setup of the Internal Network Load Balancer is similar to the [External HTTP(s) Load Balancer](/deployment/kubernetes/load-balancing/external-load-balancing#external-http-load-balancer), but with an additional annotation.

### Service YAML

In the `k8s/service.yaml`, use the `cloud.google.com/neg` annotation to enable Network Endpoint Group (NEG) in order to use container-native load balancing:

{% code title="k8s/service.yaml" %}

```yaml
apiVersion: v1
kind: Service
metadata:
  name: helloworld
  labels:
    app: helloworld
  # Add the NEG annotation to enable Network Endpoint Group
  # in order to use container-native load balancing
  annotations:
    cloud.google.com/neg: '{"ingress": true}'
spec:
  ports:
  - name: 8080-8080
    port: 8080
    protocol: TCP
    targetPort: 8080
  selector:
    app: helloworld
  type: ClusterIP
```

{% endcode %}

### Ingress YAML

Create a Kubernetes Ingress configuration that will create the HTTP Load Balancer. Create a `k8s/ingress.yaml`, but also use `kubernetes.io/ingress.class` annotation to indicate this is an Internal HTTP(s) Load Balancer

{% code title="k8s/ingress.yaml" %}

```yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
  name: helloworld
  annotations:
    # Add the Ingress Class annotation to use Internal HTTP(s) Load Balancer
    kubernetes.io/ingress.class: "gce-internal"
spec:
  rules:
  - http:
      paths:
      - path: /*
        backend:
          serviceName: helloworld
          servicePort: 8080
```

{% endcode %}


# Scheduling

## Anti-Affinity

By default, Kubernetes will schedule a pod onto a random node as long as the node has capacity to execute the pod based on the resource constraints. However, when you scale a deployment to `2`, there is a chance where both of the Pods are scheduled onto the same Kuberntes Node. This can cause issues if the Node goes down, causing both available Pods to shutdown and need to reschedule onto another node.

One solution is to avoid scheduling the pods onto the same node and this is called [ant-affinity](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity).

### Required Anti-Affinity

This example will enforce anti-affinity and not schedule any pods if the anti-affinity requirement cannot be met. For the Hello World container, add additional configuration to make sure the pods are scheduled onto different nodes.

{% code title="k8s/deployment.yaml" %}

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld
  labels:
    app: helloworld
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - name: helloworld
        image: gcr.io/.../helloworld
      # Add configuration for anti-affinity
      affinity:
        podAntiAffinity:
          requiredDuringSchedulingIgnoredDuringExecution:
          - labelSelector:
              matchExpressions:
              - key: app
                operator: In
                values:
                - helloworld
            # Prefer spreading the Pods across multiple Nodes.
            # There are other keys you can use, e.g., anti-affinity
            # across zones.
            topologyKey: "kubernetes.io/hostname"
```

{% endcode %}

Scale out the deployment to 4 pods:

```bash
kubectl scale deployment helloworld --replicas=4
```

List all the pods and show which node it is running on:

```bash
kubectl get pods -lapp=helloworld -owide
```

Observe in the `NODE` column, that each pod is running on a different node.

But since the demo cluster only has 4 nodes, if you scale out to 5 pods, it can no longer satisfy the anti-affinity requirement, and the 5th pod will remain in the unschedulable (Pending) state:

```bash
kubectl scale deployment helloworld --replicas=5
```

Find the pending pod:

```bash
kubectl get pods -lapp=helloworld --field-selector='status.phase=Pending'
```

Describe it's detail:

```bash
POD_NAME=$(kubectl get pods -lapp=helloworld \
  --field-selector='status.phase=Pending' \
  -o jsonpath='{.items[0].metadata.name}')

kubectl describe pod $POD_NAME
```

{% hint style="info" %}
See Kubernetes [Affinity / Anti-Affinity documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) for more information
{% endhint %}

### Preferred Anti-Affinity

In the Required case, if a pod cannot ensure anti-affinity, it'll simply not run. This may not be desirable for most workload. Instead, tell Kubernetes that anti-affinity is Preferred, but if the condition cannot be met, schedule it onto a host with another pod anyways. Use `preferredDuringSchedulingIgnoredDuringExecution` block instead.

{% code title="k8s/deployment.yaml" %}

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld
  labels:
    app: helloworld
spec:
  replicas: 1
  selector:
    matchLabels:
      app: helloworld
  template:
    metadata:
      labels:
        app: helloworld
    spec:
      containers:
      - name: helloworld
        image: gcr.io/.../helloworld
      affinity:
        podAntiAffinity:
          # Use Preferred rather than Required
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app
                  operator: In
                  values:
                  - helloworld
              topologyKey: "kubernetes.io/hostname"
```

{% endcode %}

Scale out to 5 pods, it can no longer satisfy the anti-affinity requirement, and the 5th pod will still be scheduled, but onto a node that already has another Hello World pod running.

```bash
kubectl scale deployment helloworld --replicas=5
```

See that all the pods are running:

```bash
kubectl get pods -lapp=helloworld
```

## Affinity

Sometimes you may want to pin a Pod to run together, or only on certain nodes, or only in certain zones/regions. To do this, you can specify Affinity rather than Anti-Affinity.

{% hint style="info" %}
See Kubernetes [Affinity / Anti-Affinity documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) for more information
{% endhint %}

## Disruption Budget

{% hint style="info" %}
See Kubernetes [Disruption Budget documentation](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) for more information.
{% endhint %}


# Workload Identity

Workload Identity allows you to assign a specific Google Cloud Service Account to a specific application, so that each application can get its own service account identity/permissions using Machine Credential.

## Create Service Accounts

### Create a Kubernetes Service Account (KSA)

```bash
kubectl create serviceaccount helloworld \
  --dry-run -oyaml > k8s/helloworld-sa.yaml

kubectl apply -f k8s/helloworld-sa.yaml
```

### Create a Google Cloud Service Account (GSA)

```bash
gcloud iam service-accounts create helloworld

PROJECT_ID=$(gcloud config get-value project)
gcloud projects add-iam-policy-binding ${PROJECT_ID} \
  --member serviceAccount:helloworld@${PROJECT_ID}.iam.gserviceaccount.com \
  --role roles/pubsub.publisher
```

## Bind Service Accounts

Bind the Kubernetes Service Account (KSA) to Google Cloud Service Account (GSA)

### Binding from Google Cloud

```bash
PROJECT_ID=$(gcloud config get-value project)
gcloud iam service-accounts add-iam-policy-binding \
  --role roles/iam.workloadIdentityUser \
  --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/helloworld]" \
  helloworld@${PROJECT_ID}.iam.gserviceaccount.com
```

### Binding from Kubernetes

```bash
PROJECT_ID=$(gcloud config get-value project)
kubectl annotate -f k8s/helloworld-sa.yaml \
  iam.gke.io/gcp-service-account=helloworld@${PROJECT_ID}.iam.gserviceaccount.com
  
kubectl apply -f k8s/helloworld-sa.yaml
```

## Use the Kubernetes Service Account

{% code title="k8s/nginx-sa-deployment.yaml" %}

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-sa
  labels:
    app: nginx-sa
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-sa
  template:
    metadata:
      labels:
        app: nginx-sa
    spec:
      # Specify the KSA to use
      serviceAccountName: helloworld
      containers:
      - image: nginx
        name: nginx
```

{% endcode %}

To try it out, first `exec` into the Pod:

```bash
POD_NAME=$(kubectl get pods -lapp=nginx-sa -o jsonpath='{.items[0].metadata.name}')

kubectl exec -ti ${POD_NAME} -- /bin/bash
```

Inside the Pod, see metadata server:

```bash
curl -H"Metadata-Flavor: Google" \
  http://metadata/computeMetadata/v1/instance/service-accounts/default/email
```


# Binary Authorization

This section continues from the previous section - make sure you do the tutorial in sequence.

{% content-ref url="/pages/-MDMCQpZLAWjRCEWGOpp" %}
[Deployment](/deployment/kubernetes/deployment)
{% endcontent-ref %}

{% content-ref url="/pages/-MFIM\_oJuQ20IcJg\_PnV" %}
[Attestation](/deployment/docker/attestation)
{% endcontent-ref %}

## Enforce Attestation

Binary Authorization allows you to enforce container image attestation, so that only attested container images can run.

Before you can turn this on, you must have [attested a container image](/deployment/docker/attestation).

### Enable Policy

First, export the existing Binary Authorization policy:

```bash
gcloud container binauthz policy export > $HOME/binauthz-policy.yaml
```

Edit the `binauthz-policy.yaml` and enable attestation policy:

{% code title="binauthz-policy.yaml" %}

```yaml
admissionWhitelistPatterns:
- namePattern: gcr.io/google_containers/*
- namePattern: gcr.io/google-containers/*
- namePattern: k8s.gcr.io/*
- namePattern: gke.gcr.io/*
- namePattern: gcr.io/stackdriver-agents/*
defaultAdmissionRule:
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
  # Change evaluationMode to require attestation
  evaluationMode: REQUIRE_ATTESTATION
  # Add the policy, and reference the `default-attestor` created from
  # Attestation section.
  # Replace PROJECT_ID with your Project ID.
  requireAttestationsBy:
  - projects/PROJECT_ID/attestors/default-attestor
globalPolicyEvaluationMode: ENABLE
name: projects/PROJECT_ID/policy
```

{% endcode %}

Import the Policy File:

```bash
gcloud container binauthz policy import $HOME/binauthz-policy.yaml
```

## Unattested Container Image

You can verify that the policy is being enforced by deploying an unattested container image:

```bash
kubectl create deployment unattested-nginx --image=nginx
```

While this should have created a new deployment for `nginx` and running a Pod, you can validate that no Pod is running:

```bash
kubectl get pods -lapp=unattested-nginx
```

In addition, you can verify the Kubernetes events stream:

```bash
kubectl get event
```

Observe the event where the container image was denied by the attestor:

```
... Error creating: pods "..." is forbidden: image policy webhook backend denied one or more images: Denied by default admission rule. Denied by Attestor. ...
```

Delete the deployment:

```bash
kubectl delete deployment unattested-nginx
```

## Attested Container Image

Deploy a [previously attested container image](/deployment/docker/attestation#create-an-attestation) from the [Container Image Attestation](/deployment/docker/attestation) section.

```bash
PROJECT_ID=$(gcloud config get-value project)
IMAGE=$(gcloud container images describe gcr.io/$PROJECT_ID/helloworld \
  --format='value(image_summary.fully_qualified_digest)')

kubectl create deployment attested-helloworld --image=$IMAGE
```

Verify that the Pod is up and running:

```bash
kubectl get pods -lapp=attested-helloworld
```

## Allow List

It may be impossible to attest every single container image you want to run. For example, you may trust certain images from open source projects. You can add these images into an allow list.

For example, to be able to deploy the `nginx` container image from Dockerhub without attestation, you need to add it to the policy.

First, export the existing Binary Authorization policy:

```bash
gcloud container binauthz policy export > $HOME/binauthz-policy.yaml
```

Edit the `binauthz-policy.yaml` and enable attestation policy:

{% code title="binauthz-policy.yaml" %}

```yaml
admissionWhitelistPatterns:
- namePattern: gcr.io/google_containers/*
- namePattern: gcr.io/google-containers/*
- namePattern: k8s.gcr.io/*
- namePattern: gke.gcr.io/*
- namePattern: gcr.io/stackdriver-agents/*
# Add nginx to the allow list
- namePattern: nginx
defaultAdmissionRule:
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
  evaluationMode: REQUIRE_ATTESTATION
  requireAttestationsBy:
  - projects/PROJECT_ID/attestors/default-attestor
globalPolicyEvaluationMode: ENABLE
name: projects/PROJECT_ID/policy
```

{% endcode %}

Import the Policy File:

```bash
gcloud container binauthz policy import $HOME/binauthz-policy.yaml
```

Deploy `nginx` again:

```bash
kubectl create deployment unattested-nginx --image=nginx
```

Verify that the Pod is up and running due to the allow list:

```bash
kubectl get pods -lapp=unattested-nginx
```

If you trust every container image from a particular Project:

{% code title="binauthz-policy.yaml" %}

```yaml
admissionWhitelistPatterns:
- namePattern: gcr.io/google_containers/*
- namePattern: gcr.io/google-containers/*
- namePattern: k8s.gcr.io/*
- namePattern: gke.gcr.io/*
- namePattern: gcr.io/stackdriver-agents/*
# Add the container registry from a project to the allow list.
# Replace PROJECT_ID.
- namePattern: gcr.io/PROJECT_ID/*
defaultAdmissionRule:
  enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG
  evaluationMode: REQUIRE_ATTESTATION
  requireAttestationsBy:
  - projects/PROJECT_ID/attestors/default-attestor
globalPolicyEvaluationMode: ENABLE
name: projects/PROJECT_ID/policy
```

{% endcode %}


# Istio

Istio is a service mesh that can manage your service to service communication policies.

If you are unfamiliar with Istio, you can see a quick introduction in the following video:

{% embed url="<https://www.youtube.com/watch?v=AGztKw580yQ>" %}


# Getting Started

This section requires basic understanding of Kubernetes - make sure you do the tutorial in sequence.

{% content-ref url="/pages/-MDKvYQB-5BPGws0HZew" %}
[Kubernetes](/deployment/kubernetes)
{% endcontent-ref %}

## Install Istio

First, make sure you already have a [Kubernetes cluster](/deployment/kubernetes/kubernetes-cluster) up and running.

### Install istioctl

You need to get the `istioctl` CLI to install Istio into the cluster.

```bash
cd $HOME

# Specify an Istio version to install
curl -L https://istio.io/downloadIstio | \
  ISTIO_VERSION=1.7.4 sh -
```

Add Istio's `bin` path to shell's `PATH.`

```bash
echo 'export PATH="$PATH:$HOME/istio-1.7.4/bin"' >> ~/.bash_profile

source $HOME/.bash_profile
```

Verify `istioctl` is installed properly and with the correct version:

```bash
istioctl version
```

### Install Istio

Install the `demo` profile of Istio, which comes with the basic settings for most of the things you'll want to learn about. In addition, because the cluster in this guide enabled Network Policy, so we can use Istio with Container Network Interface (CNI).

```bash
istioctl install \
  --set profile=demo \
  --set values.cni.cniBinDir=/home/kubernetes/bin \
  --set components.cni.enabled=true \
  --set components.cni.namespace=kube-system
```

Validate that Istio is installed. `istioctl version` should show you the `control plane version`.

```bash
istioctl version
```

In addition, Istio is installed into the `istio-system` namespace.

```bash
kubectl get ns

kubectl -n=istio-system get pods
```

## Install Addons

In addition to core-Istio, you can install addons for observability, for example to see distributed traces and out-of-the-box metrics/dashboard.

```bash
# Zipkin
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/extras/zipkin.yaml

# Prometheus
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/prometheus.yaml

# Grafana
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/grafana.yaml

# Wait for Grafana a bit before Kiali
sleep 20

# Kiali
kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/kiali.yaml
```


# Sidecar Proxy

Istio requires to run a sidecar proxy next to every instance of your containers that needs to participate in the service mesh. There are 2 ways of adding the sidecar proxy:

1. Automatic sidecar injection
2. Manual sidecar injection

## Automatic Sidecar Injection

You can inject the Istio sidecar automatically for every pod that's deployed into a specific namespace. You can enable automatic injection by annotating the namespace you want to use the service mesh.

```bash
kubectl label namespace default istio-injection=enabled
```

Deploy a workload, such as the Helloworld application from the [Kubernetes Deployment](/deployment/kubernetes/deployment#deployment-yaml) section.

```bash
kubectl apply -f k8s/deployment.yaml
```

Verify that the Helloworld pod has 2 containers rather than only 1:

```bash
kubectl get pods
```

Each container within a pod is named. Now that the pod has multiple containers, you can specify a container within the pod using `-c containername` parameter:

```bash
POD_NAME=$(kubectl get pods -lapp=helloworld -o jsonpath='{.items[0].metadata.name}')

kubectl logs ${POD_NAME} -c helloworld
kubectl logs ${POD_NAME} -c istio-proxy
```

If, for some reason, a workload do not want to participate in the mesh, then you can explicitly turn off automatic sidecar injection using annotation:

```yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: helloworld
  ...
spec:
  template:
    metadata:
      labels:
        ...
      annotations:
        # Explicitly turn off automatic sidecar injection
        sidecar.istio.io/inject: "false"
    spec:
      ...

```

## Manual Sidecar Injection

You can use `istioctl` to filter your existing Kubernetes deployment file and it'll produce the enhanced deployment manifest.

```bash
istioctl kube-inject -f k8s/deployment.yaml
```

In addition to your original manifest, the enhanced manifest now has an additional `istio-proxy`container.

You can save the enhanced manifest into a file for future deployments. Or, you can filter and apply in one command:

```bash
istioctl kube-inject -f k8s/deployment.yaml| kubectl apply -f
```

In most cases, Automatic Sidecar Injection is what you need.


# Code Labs

Long and short code labs to learn Spring Boot on GCP.

| Topics                    | Short/Long Form | Link                                                                                                                             |
| ------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| Spring Boot on GCP        | Short           | [gcplab.me/spring](https://gcplab.me/spring)                                                                                     |
| Spring Boot on GCP        | Long            | [bit.ly/spring-gcp-lab](http://bit.ly/spring-gcp-lab)                                                                            |
| Spring Boot on App Engine | Short           | [gcplab.me/spring](https://codelabs.developers.google.com/codelabs/cloud-app-engine-springboot/index.html?index=..%2F..spring#4) |
| Spring Boot on Cloud Run  | Short           | [gcplab.me/spring](https://codelabs.developers.google.com/codelabs/cloud-kotlin-jib-cloud-run/index.html?index=..%2F..spring#0)  |
| Spring Boot on Kubernetes | Short           | [gcplab.me/spring](https://codelabs.developers.google.com/codelabs/cloud-springboot-kubernetes/index.html?index=..%2F..spring#4) |
| Spring Boot on Kubernetes | Long            | [bit.ly/k8s-lab](http://bit.ly/k8s-lab)                                                                                          |
| Spring Boot with Istio    | Long            | [bit.ly/istio-lab](http://bit.ly/istio-lab)                                                                                      |


# Presentations / Videos

List of presentations and videos on Spring Boot with Google Cloud Platform.

| Topic                                         | Video                                                  | Slides                                                                                                                      | Code Lab                                                                                                                                               |
| --------------------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Spring Boot with App Engine Java 11           | [YouTube](https://www.youtube.com/watch?v=qx_T6-EKkBE) | N/A                                                                                                                         | [Short](https://codelabs.developers.google.com/codelabs/cloud-app-engine-springboot/index.html?index=..%2F..spring#4)                                  |
| Cloud Native Java with Spring Boot and GCP    | [YouTube](https://www.youtube.com/watch?v=g9qqEnhU_uU) | N/A                                                                                                                         | [Short](https://gcplab.me/spring) / [Long](http://bit.ly/spring-gcp-lab)                                                                               |
| Spring Boot with Kubernetes                   | [YouTube](https://www.youtube.com/watch?v=kT1vmK0r184) | [Speaker Deck](https://speakerdeck.com/saturnism/2017-jfokus-managing-cloud-native-applications-with-kubernetes-end-to-end) | [Short](https://codelabs.developers.google.com/codelabs/cloud-springboot-kubernetes/index.html?index=..%2F..spring#4) / [Long](https://bit.ly/k8s-lab) |
| Spring Boot with Istio                        | [YouTube](https://www.youtube.com/watch?v=AGztKw580yQ) | [Speaker Deck](https://speakerdeck.com/saturnism/making-microservices-micro-with-istio-service-mesh)                        | [Long](http://bit.ly/istio-lab)                                                                                                                        |
| Debugging and Troubleshooting with Kubernetes | [YouTube](https://www.youtube.com/watch?v=2hxTTyc6IH8) | [Speaker Deck](https://speakerdeck.com/saturnism/debugging-and-troubleshooting-microservices-in-kubernetes-and-stackdriver) | N/A                                                                                                                                                    |


# Cheat Sheets

Here are some commonly used commands and links.

## gcloud

### Basics

| Task                          |                                                                          |
| ----------------------------- | ------------------------------------------------------------------------ |
| Enable an API                 | `gcloud services enable ${API}`                                          |
| Current Project ID            | `gcloud config get-value project`                                        |
| Export to PROJECT\_ID         | `PROJECT_ID=$(gcloud config get-value project)`                          |
| Authentication                | `gcloud auth login`                                                      |
| Application Credentials Login | `gcloud auth application-default login`                                  |
| Default Zone                  | `gcloud config set compute/zone us-central1-c`                           |
| Default Region                | `gcloud config set compute/region us-central1`                           |
| All Regions and Zones         | [Regions and Zones](https://cloud.google.com/compute/docs/regions-zones) |

### Identity Access Management

| Task                              |                                                                                                                                                                              |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Create a Service Account          | <p><code>gcloud iam service-accounts create \</code></p><p>  <code>${SA\_NAME}</code></p>                                                                                    |
| Service Account E-Mail            | `${SA_NAME}@${PROJECT_ID}.iam.gserviceaccount.com`                                                                                                                           |
| Add Permission                    | <p><code>gcloud projects add-iam-policy-binding ${PROJECT\_ID} \</code></p><p>  <code>--member serviceAccount:${SA\_EMAIL} \</code></p><p>  <code>--role ${ROLES}</code></p> |
| Create a Service Account Key File | <p><code>gcloud iam service-accounts keys create \</code></p><p>  <code>$HOME/sa-key.json \</code></p><p>  <code>--iam-account ${SA\_EMAIL}</code></p>                       |
| All Possible Roles                | [Understanding roles](https://cloud.google.com/iam/docs/understanding-roles)                                                                                                 |

### Serverless Deployments

| Task           |                                                                                                                                                                                                                                                                                                                                                           |
| -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| App Engine     | `gcloud app deploy ${JAR_FILE}`                                                                                                                                                                                                                                                                                                                           |
|                | `gcloud app deploy ${JAR_FILE} --appyaml app.yaml`                                                                                                                                                                                                                                                                                                        |
|                | [Maven plugin](https://cloud.google.com/appengine/docs/standard/java11/using-maven#setting_up_maven)                                                                                                                                                                                                                                                      |
|                | [Gradle Plugin](https://cloud.google.com/appengine/docs/standard/java11/using-gradle)                                                                                                                                                                                                                                                                     |
| Cloud Run      | <p><code>gcloud run deploy ${NAME} \</code></p><p>  <code>--platform=managed \</code></p><p>  <code>--allow-unauthenticated \</code></p><p>  <code>--image=gcr.io/${PROJECT\_ID}/${IMAGE\_NAME}</code></p>                                                                                                                                                |
|                | <p><code>gcloud run deploy ${NAME} \</code></p><p>  <code>--platform=managed \</code></p><p>  <code>--allow-unauthenticated \</code></p><p>  <code>--cpu=2 \</code></p><p>  <code>--memory=512M \</code></p><p>  <code>--set-env-vars="JAVA\_TOOL\_OPTIONS=-Dproperty=value"</code></p><p>  <code>--image=gcr.io/${PROJECT\_ID}/${IMAGE\_NAME}</code></p> |
| Cloud Function | <p><code>gcloud functions deploy ${NAME}</code></p><p>  <code>--trigger-http \</code></p><p>  <code>--runtime=java11 \</code></p><p>  <code>--allow-unauthenticated \</code></p><p>  <code>--entry-point=${FUNCTION\_CLASS\_FQN}</code></p>                                                                                                               |
|                | [Maven plugin](https://github.com/GoogleCloudPlatform/functions-framework-java#running-a-function-with-the-maven-plugin)                                                                                                                                                                                                                                  |

## Jib

| Task                                               |                                                                                                                                                                            |
| -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Create a remote Image                              | `mvn jib:build`                                                                                                                                                            |
|                                                    | `gradle jib`                                                                                                                                                               |
| Create a local image                               | `mvn jib:dockerBuild`                                                                                                                                                      |
|                                                    | `gradle jibDockerBuild`                                                                                                                                                    |
| <p>Run Jib without</p><p>pre-configured plugin</p> | <p><code>mvn compile \</code></p><p><code>com.google.cloud.tools:jib-maven-plugin:2.4.0:build \</code></p><p><code>-Dimage=gcr.io/${PROJECT\_ID}/${IMAGE\_NAME}</code></p> |
| Jib READMEs                                        | [Maven plugin](https://github.com/GoogleContainerTools/jib/tree/master/jib-maven-plugin)                                                                                   |
|                                                    | [Gradle plugin](https://github.com/GoogleContainerTools/jib/tree/master/jib-gradle-plugin)                                                                                 |

## Spring Initializer

| Task                                              |                                                                                                                                                                                                                 |
| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| <p>Create a new app w/</p><p>Spring Cloud GCP</p> | <p><code>curl <https://start.spring.io/starter.zip>  \</code></p><p>  <code>-d dependencies=web,cloud-gcp \</code></p><p>  <code>-d bootVersion=2.3.1.RELEASE \</code></p><p>  <code>-d baseDir=demo</code></p> |


