├── .gitbook └── assets │ ├── image (1).png │ ├── image (10).png │ ├── image (11).png │ ├── image (12).png │ ├── image (13).png │ ├── image (14).png │ ├── image (15).png │ ├── image (16).png │ ├── image (17).png │ ├── image (18).png │ ├── image (19).png │ ├── image (2).png │ ├── image (20).png │ ├── image (21).png │ ├── image (22).png │ ├── image (23).png │ ├── image (24).png │ ├── image (25).png │ ├── image (26).png │ ├── image (27).png │ ├── image (28).png │ ├── image (29).png │ ├── image (3).png │ ├── image (30).png │ ├── image (31).png │ ├── image (32).png │ ├── image (33).png │ ├── image (34).png │ ├── image (35).png │ ├── image (36).png │ ├── image (37).png │ ├── image (38).png │ ├── image (39).png │ ├── image (4).png │ ├── image (40).png │ ├── image (41).png │ ├── image (42).png │ ├── image (43).png │ ├── image (5).png │ ├── image (6).png │ ├── image (7).png │ ├── image (8).png │ ├── image (9).png │ └── image.png ├── LICENSE ├── README.md ├── SUMMARY.md ├── additional-resources ├── cheet-sheets.md ├── code-labs.md └── presentations-videos.md ├── app-dev ├── cloud-services │ ├── README.md │ ├── cache.md │ ├── cache │ │ ├── README.md │ │ ├── memorystore-memcached.md │ │ └── memorystore-redis.md │ ├── databases │ │ ├── README.md │ │ ├── cloud-datastore.md │ │ ├── cloud-firestore-1 │ │ │ ├── README.md │ │ │ ├── cloud-firestore-datastore.md │ │ │ └── native-mode.md │ │ ├── cloud-firestore.md │ │ ├── cloud-firestore │ │ │ ├── README.md │ │ │ ├── datastore-mode.md │ │ │ └── native-mode.md │ │ ├── cloud-spanner.md │ │ └── cloud-sql.md │ ├── messaging │ │ ├── README.md │ │ ├── cloud-pub-sub.md │ │ ├── confluent-cloud-kafka.md │ │ ├── kafka.md │ │ └── pubsub.md │ ├── other-services.md │ ├── secret-management.md │ └── storage.md ├── development-tools.md ├── devops │ ├── README.md │ └── artifact-repository.md ├── observability │ ├── README.md │ ├── debugging.md │ ├── logging.md │ ├── metrics.md │ ├── profiling.md │ └── trace.md └── spring-cloud-gcp.md ├── deployment ├── docker │ ├── README.md │ ├── attestation.md │ ├── container-awareness.md │ ├── container-image.md │ ├── secure-container-image.md │ └── vulnerability-scanning.md ├── istio.md ├── istio │ ├── README.md │ ├── getting-started.md │ ├── istio-sidecar-proxy.md │ └── sidecar-proxy.md ├── kubernetes │ ├── README.md │ ├── binary-authorization.md │ ├── container-image.md │ ├── deployment.md │ ├── health-checks.md │ ├── kubernetes-cluster.md │ ├── load-balancing │ │ ├── README.md │ │ ├── external-load-balancing.md │ │ └── internal-load-balancing.md │ ├── resource.md │ ├── resources.md │ ├── scheduling.md │ ├── service.md │ └── workload-identity.md └── runtime-environments.md └── getting-started ├── cloud-shell.md ├── gcloud-cli.md ├── google-cloud-platform.md └── helloworld ├── README.md ├── app-engine.md ├── cloud-functions.md ├── cloud-run.md ├── cloud-shell.md ├── compute-engine.md └── kubernetes-engine.md /.gitbook/assets/image (1).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (1).png -------------------------------------------------------------------------------- /.gitbook/assets/image (10).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (10).png -------------------------------------------------------------------------------- /.gitbook/assets/image (11).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (11).png -------------------------------------------------------------------------------- /.gitbook/assets/image (12).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (12).png -------------------------------------------------------------------------------- /.gitbook/assets/image (13).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (13).png -------------------------------------------------------------------------------- /.gitbook/assets/image (14).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (14).png -------------------------------------------------------------------------------- /.gitbook/assets/image (15).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (15).png -------------------------------------------------------------------------------- /.gitbook/assets/image (16).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (16).png -------------------------------------------------------------------------------- /.gitbook/assets/image (17).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (17).png -------------------------------------------------------------------------------- /.gitbook/assets/image (18).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (18).png -------------------------------------------------------------------------------- /.gitbook/assets/image (19).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (19).png -------------------------------------------------------------------------------- /.gitbook/assets/image (2).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (2).png -------------------------------------------------------------------------------- /.gitbook/assets/image (20).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (20).png -------------------------------------------------------------------------------- /.gitbook/assets/image (21).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (21).png -------------------------------------------------------------------------------- /.gitbook/assets/image (22).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (22).png -------------------------------------------------------------------------------- /.gitbook/assets/image (23).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (23).png -------------------------------------------------------------------------------- /.gitbook/assets/image (24).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (24).png -------------------------------------------------------------------------------- /.gitbook/assets/image (25).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (25).png -------------------------------------------------------------------------------- /.gitbook/assets/image (26).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (26).png -------------------------------------------------------------------------------- /.gitbook/assets/image (27).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (27).png -------------------------------------------------------------------------------- /.gitbook/assets/image (28).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (28).png -------------------------------------------------------------------------------- /.gitbook/assets/image (29).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (29).png -------------------------------------------------------------------------------- /.gitbook/assets/image (3).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (3).png -------------------------------------------------------------------------------- /.gitbook/assets/image (30).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (30).png -------------------------------------------------------------------------------- /.gitbook/assets/image (31).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (31).png -------------------------------------------------------------------------------- /.gitbook/assets/image (32).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (32).png -------------------------------------------------------------------------------- /.gitbook/assets/image (33).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (33).png -------------------------------------------------------------------------------- /.gitbook/assets/image (34).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (34).png -------------------------------------------------------------------------------- /.gitbook/assets/image (35).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (35).png -------------------------------------------------------------------------------- /.gitbook/assets/image (36).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (36).png -------------------------------------------------------------------------------- /.gitbook/assets/image (37).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (37).png -------------------------------------------------------------------------------- /.gitbook/assets/image (38).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (38).png -------------------------------------------------------------------------------- /.gitbook/assets/image (39).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (39).png -------------------------------------------------------------------------------- /.gitbook/assets/image (4).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (4).png -------------------------------------------------------------------------------- /.gitbook/assets/image (40).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (40).png -------------------------------------------------------------------------------- /.gitbook/assets/image (41).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (41).png -------------------------------------------------------------------------------- /.gitbook/assets/image (42).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (42).png -------------------------------------------------------------------------------- /.gitbook/assets/image (43).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (43).png -------------------------------------------------------------------------------- /.gitbook/assets/image (5).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (5).png -------------------------------------------------------------------------------- /.gitbook/assets/image (6).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (6).png -------------------------------------------------------------------------------- /.gitbook/assets/image (7).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (7).png -------------------------------------------------------------------------------- /.gitbook/assets/image (8).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (8).png -------------------------------------------------------------------------------- /.gitbook/assets/image (9).png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image (9).png -------------------------------------------------------------------------------- /.gitbook/assets/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saturnism/spring-on-gcp-gitbook/10f2f531d7384ef5e6c85f9e0c7efdfaff9bda46/.gitbook/assets/image.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | 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. 4 | 5 | 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/). 6 | 7 | There is a lot of content on this site. Here are some recommended paths depending on what you are looking for: 8 | 9 | {% tabs %} 10 | {% tab title="New to Google Cloud" %} 11 | {% page-ref page="getting-started/google-cloud-platform.md" %} 12 | 13 | {% page-ref page="getting-started/cloud-shell.md" %} 14 | 15 | {% page-ref page="getting-started/gcloud-cli.md" %} 16 | 17 | {% page-ref page="getting-started/helloworld/" %} 18 | {% endtab %} 19 | 20 | {% tab title="Serverless" %} 21 | {% page-ref page="getting-started/helloworld/app-engine.md" %} 22 | 23 | {% page-ref page="getting-started/helloworld/cloud-run.md" %} 24 | 25 | {% page-ref page="getting-started/helloworld/cloud-functions.md" %} 26 | {% endtab %} 27 | 28 | {% tab title="Containers" %} 29 | {% page-ref page="deployment/docker/container-image.md" %} 30 | 31 | {% page-ref page="deployment/docker/container-awareness.md" %} 32 | 33 | {% page-ref page="getting-started/helloworld/cloud-run.md" %} 34 | 35 | {% page-ref page="deployment/kubernetes/" %} 36 | {% endtab %} 37 | 38 | {% tab title="App Development" %} 39 | {% page-ref page="app-dev/development-tools.md" %} 40 | 41 | {% page-ref page="app-dev/spring-cloud-gcp.md" %} 42 | 43 | {% page-ref page="app-dev/cloud-services/databases/cloud-sql.md" %} 44 | 45 | {% page-ref page="app-dev/cloud-services/" %} 46 | {% endtab %} 47 | {% endtabs %} 48 | 49 | {% hint style="info" %} 50 | This is not official Google Cloud documentation. Always refer to [official documentation](https://cloud.google.com) for current pricing, features, limitations, etc. 51 | {% endhint %} 52 | 53 | -------------------------------------------------------------------------------- /SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Table of contents 2 | 3 | * [Introduction](README.md) 4 | 5 | ## Getting Started 6 | 7 | * [Google Cloud Platform](getting-started/google-cloud-platform.md) 8 | * [Cloud Shell](getting-started/cloud-shell.md) 9 | * [gcloud CLI](getting-started/gcloud-cli.md) 10 | * [Hello World!](getting-started/helloworld/README.md) 11 | * [Cloud Shell](getting-started/helloworld/cloud-shell.md) 12 | * [App Engine](getting-started/helloworld/app-engine.md) 13 | * [Cloud Run](getting-started/helloworld/cloud-run.md) 14 | * [Kubernetes Engine](getting-started/helloworld/kubernetes-engine.md) 15 | * [Compute Engine](getting-started/helloworld/compute-engine.md) 16 | * [Cloud Functions](getting-started/helloworld/cloud-functions.md) 17 | 18 | ## Application Development 19 | 20 | * [Development Tools](app-dev/development-tools.md) 21 | * [Spring Cloud GCP](app-dev/spring-cloud-gcp.md) 22 | * [Cloud Services](app-dev/cloud-services/README.md) 23 | * [Databases](app-dev/cloud-services/databases/README.md) 24 | * [Cloud SQL](app-dev/cloud-services/databases/cloud-sql.md) 25 | * [Cloud Spanner](app-dev/cloud-services/databases/cloud-spanner.md) 26 | * [Cloud Firestore](app-dev/cloud-services/databases/cloud-firestore/README.md) 27 | * [Datastore Mode](app-dev/cloud-services/databases/cloud-firestore/datastore-mode.md) 28 | * [Native Mode](app-dev/cloud-services/databases/cloud-firestore/native-mode.md) 29 | * [Messaging](app-dev/cloud-services/messaging/README.md) 30 | * [Cloud Pub/Sub](app-dev/cloud-services/messaging/pubsub.md) 31 | * [Kafka](app-dev/cloud-services/messaging/kafka.md) 32 | * [Secret Management](app-dev/cloud-services/secret-management.md) 33 | * [Storage](app-dev/cloud-services/storage.md) 34 | * [Cache](app-dev/cloud-services/cache/README.md) 35 | * [Memorystore Redis](app-dev/cloud-services/cache/memorystore-redis.md) 36 | * [Memorystore Memcached \(beta\)](app-dev/cloud-services/cache/memorystore-memcached.md) 37 | * [Other Services](app-dev/cloud-services/other-services.md) 38 | * [Observability](app-dev/observability/README.md) 39 | * [Trace](app-dev/observability/trace.md) 40 | * [Logging](app-dev/observability/logging.md) 41 | * [Metrics](app-dev/observability/metrics.md) 42 | * [Profiling](app-dev/observability/profiling.md) 43 | * [Debugging](app-dev/observability/debugging.md) 44 | * [DevOps](app-dev/devops/README.md) 45 | * [Artifact Repository](app-dev/devops/artifact-repository.md) 46 | 47 | ## Deployment 48 | 49 | * [Runtime Environments](deployment/runtime-environments.md) 50 | * [Container](deployment/docker/README.md) 51 | * [Container Image](deployment/docker/container-image.md) 52 | * [Secure Container Image](deployment/docker/secure-container-image.md) 53 | * [Container Awareness](deployment/docker/container-awareness.md) 54 | * [Vulnerability Scanning](deployment/docker/vulnerability-scanning.md) 55 | * [Attestation](deployment/docker/attestation.md) 56 | * [Kubernetes](deployment/kubernetes/README.md) 57 | * [Kubernetes Cluster](deployment/kubernetes/kubernetes-cluster.md) 58 | * [Deployment](deployment/kubernetes/deployment.md) 59 | * [Resources](deployment/kubernetes/resources.md) 60 | * [Service](deployment/kubernetes/service.md) 61 | * [Health Checks](deployment/kubernetes/health-checks.md) 62 | * [Load Balancing](deployment/kubernetes/load-balancing/README.md) 63 | * [External Load Balancing](deployment/kubernetes/load-balancing/external-load-balancing.md) 64 | * [Internal Load Balancing](deployment/kubernetes/load-balancing/internal-load-balancing.md) 65 | * [Scheduling](deployment/kubernetes/scheduling.md) 66 | * [Workload Identity](deployment/kubernetes/workload-identity.md) 67 | * [Binary Authorization](deployment/kubernetes/binary-authorization.md) 68 | * [Istio](deployment/istio/README.md) 69 | * [Getting Started](deployment/istio/getting-started.md) 70 | * [Sidecar Proxy](deployment/istio/sidecar-proxy.md) 71 | 72 | ## Additional Resources 73 | 74 | * [Code Labs](additional-resources/code-labs.md) 75 | * [Presentations / Videos](additional-resources/presentations-videos.md) 76 | * [Cheat Sheets](additional-resources/cheet-sheets.md) 77 | 78 | -------------------------------------------------------------------------------- /additional-resources/code-labs.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Long and short code labs to learn Spring Boot on GCP. 3 | --- 4 | 5 | # Code Labs 6 | 7 | 8 | 9 | | Topics | Short/Long Form | Link | 10 | | :--- | :--- | :--- | 11 | | Spring Boot on GCP | Short | [gcplab.me/spring](https://gcplab.me/spring) | 12 | | Spring Boot on GCP | Long | [bit.ly/spring-gcp-lab](http://bit.ly/spring-gcp-lab) | 13 | | 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) | 14 | | 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) | 15 | | Spring Boot on Kubernetes | Short | [gcplab.me/spring](https://codelabs.developers.google.com/codelabs/cloud-springboot-kubernetes/index.html?index=..%2F..spring#4) | 16 | | Spring Boot on Kubernetes | Long | [bit.ly/k8s-lab](http://bit.ly/k8s-lab) | 17 | | Spring Boot with Istio | Long | [bit.ly/istio-lab](http://bit.ly/istio-lab) | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /additional-resources/presentations-videos.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: List of presentations and videos on Spring Boot with Google Cloud Platform. 3 | --- 4 | 5 | # Presentations / Videos 6 | 7 | | Topic | Video | Slides | Code Lab | 8 | | :--- | :--- | :--- | :--- | 9 | | 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) | 10 | | 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) | 11 | | 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) | 12 | | 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) | 13 | | 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 | 14 | 15 | -------------------------------------------------------------------------------- /app-dev/cloud-services/README.md: -------------------------------------------------------------------------------- 1 | # Cloud Services 2 | 3 | -------------------------------------------------------------------------------- /app-dev/cloud-services/cache.md: -------------------------------------------------------------------------------- 1 | # Cache 2 | 3 | ## Memorystore 4 | 5 | Memory Store is a fully managed in-memory data store 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. 6 | 7 | All Memorystore instances are accessible via a private IP that's local to your VPC network. Memorystore is zonal, meaning each Memorystore instance is only available within a zone, or accessible from other zones within the same region. What that also means is that Memorystore does not have regional availability \(e.g., replication or fail-over across zones\) . 8 | 9 | Because Memorystore is protocol compatible. You can use existing Spring Boot integration with Redis and Memcached as-is. 10 | 11 | ## Memorystore Redis 12 | 13 | ## Memorystore Memcached 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app-dev/cloud-services/cache/README.md: -------------------------------------------------------------------------------- 1 | # Cache 2 | 3 | ## Memorystore 4 | 5 | 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. 6 | 7 | #### Zonal Resource 8 | 9 | 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. 10 | 11 | #### Connectivity 12 | 13 | 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). 14 | 15 | | Resource | Method | 16 | | :--- | :--- | 17 | | Compute Engine | Out-of-the-box | 18 | | Kubernetes Engine | [VPC-Native cluster](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips) | 19 | | App Engine Flexible | [Additional Configuration](https://cloud.google.com/appengine/docs/flexible/java/using-shared-vpc) | 20 | | App Engine Standard | [VPC Service Connector](https://cloud.google.com/appengine/docs/standard/java11/connecting-vpc) | 21 | | Cloud Run | [VPC Service Connector](https://cloud.google.com/run/docs/configuring/connecting-vpc) | 22 | | Cloud Function | [VPC Service Connector](https://cloud.google.com/functions/docs/networking/connecting-vpc) | 23 | 24 | #### Protocol Compatibility 25 | 26 | Because Memorystore is protocol compatible. You can use existing Spring Boot integration with Redis and Memcached as-is. 27 | 28 | {% page-ref page="memorystore-redis.md" %} 29 | 30 | {% page-ref page="memorystore-memcached.md" %} 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /app-dev/cloud-services/cache/memorystore-memcached.md: -------------------------------------------------------------------------------- 1 | # Memorystore Memcached \(beta\) 2 | 3 | ## Memorystore Memcached Instance 4 | 5 | ### Enable API 6 | 7 | ```bash 8 | gcloud services enable servicenetworking.googleapis.com 9 | gcloud services enable memcache.googleapis.com 10 | ``` 11 | 12 | {% hint style="warning" %} 13 | Enabling this API may take a few minutes. 14 | {% endhint %} 15 | 16 | ### Enable Private Service Access 17 | 18 | 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. 19 | 20 | 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: 21 | 22 | ```bash 23 | gcloud beta compute addresses create reserved-range \ 24 | --global --prefix-length=24 \ 25 | --description=description --network=default \ 26 | --purpose=vpc_peering 27 | ``` 28 | 29 | {% hint style="info" %} 30 | 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. 31 | {% endhint %} 32 | 33 | Establish peering so that Memorystore can allocate the IP address in the reserved range in the VPC. 34 | 35 | ```bash 36 | gcloud services vpc-peerings connect \ 37 | --service=servicenetworking.googleapis.com \ 38 | --ranges=reserved-range --network=default 39 | ``` 40 | 41 | ### Create an Instance 42 | 43 | Create an instance and attach it to the default VPC. 44 | 45 | ```bash 46 | gcloud beta memcache instances create orders-cache \ 47 | --node-count=1 --node-cpu=1 --node-memory=1G --region=us-central1 48 | ``` 49 | 50 | {% hint style="warning" %} 51 | Creating a Memcached instance may take a few minutes. 52 | {% endhint %} 53 | 54 | ### Get Instance IP Address 55 | 56 | ```bash 57 | gcloud beta memcache instances describe orders-cache \ 58 | --region=us-central1 --format="value(memcacheNodes.host)" 59 | ``` 60 | 61 | {% hint style="warning" %} 62 | The IP address is not a static IP address. If you create the instance, the IP address may be different. 63 | {% endhint %} 64 | 65 | ### Connect to Instance 66 | 67 | See [Memorystore connectivity options](./#connectivity) to see how to connect to a Memorystore instance from different computing environments. 68 | 69 | | Computing Environment | | 70 | | :--- | :--- | 71 | | Compute Engine | [Guide](https://cloud.google.com/memorystore/docs/memcached/connecting-memcached-instance#connecting-compute-engine) | 72 | | Kubernetes Engine | [Guide](https://cloud.google.com/memorystore/docs/memcached/connecting-memcached-instance#connecting_to_a_memcached_instance_from_a_cluster) | 73 | | App Engine Flexible | [Additional Configuration](https://cloud.google.com/appengine/docs/flexible/java/using-shared-vpc) | 74 | | App Engine Standard | [VPC Service Connector](https://cloud.google.com/appengine/docs/standard/java11/connecting-vpc) | 75 | | Cloud Run | [VPC Service Connector](https://cloud.google.com/run/docs/configuring/connecting-vpc) | 76 | | Cloud Function | [VPC Service Connector](https://cloud.google.com/functions/docs/networking/connecting-vpc) | 77 | 78 | You can test quickly by creating a Compute Engine instance in a zone within the same region: 79 | 80 | ```bash 81 | gcloud compute instances create test-memorystore-vm --zone=us-central1-c 82 | ``` 83 | 84 | SSH into the machine: 85 | 86 | ```bash 87 | gcloud compute ssh test-memorystore-vm --zone=us-central1-c 88 | ``` 89 | 90 | Install `redis-cli`: 91 | 92 | ```bash 93 | sudo apt-get update && sudo apt-get install -y telnet 94 | ``` 95 | 96 | Connect to the instance: 97 | 98 | ```bash 99 | telnet 11211 100 | ``` 101 | 102 | You can try different Memcached commands, for example, `stats`: 103 | 104 | ```text 105 | Trying ... 106 | Connected to 10.111.98.4. 107 | Escape character is '^]'. 108 | stats 109 | STAT pid 1 110 | STAT uptime 1020 111 | STAT time 1594348128 112 | ... 113 | END 114 | quit 115 | Connection closed by foreign host. 116 | ``` 117 | 118 | {% hint style="info" %} 119 | See [Memcached commands](https://github.com/memcached/memcached/wiki/Commands) for more information. 120 | {% endhint %} 121 | 122 | ## Spring Boot Cache 123 | 124 | 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.: 125 | 126 | * [https://github.com/sixhours-team/memcached-spring-boot](https://github.com/sixhours-team/memcached-spring-boot) 127 | 128 | ### Dependency 129 | 130 | Add the 3rd party Memcached Spring Boot starter: 131 | 132 | {% tabs %} 133 | {% tab title="Maven" %} 134 | ```bash 135 | 136 | io.sixhours 137 | memcached-spring-boot-starter 138 | 2.1.2 139 | 140 | ``` 141 | {% endtab %} 142 | 143 | {% tab title="Gradle" %} 144 | ```bash 145 | compile group: 'io.sixhours', name: 'memcached-spring-boot-starter:2.1.2' 146 | ``` 147 | {% endtab %} 148 | {% endtabs %} 149 | 150 | ### Configuration 151 | 152 | Configure the Memcached instance to connect to: 153 | 154 | {% code title="application.properties" %} 155 | ```bash 156 | memcached.cache.servers=:11211 157 | memcached.cache.provider=static 158 | ``` 159 | {% endcode %} 160 | 161 | ### Enable Caching 162 | 163 | Turn on caching capability explicitly with the `@EnableCaching` annotation: 164 | 165 | ```java 166 | @SpringBootApplication 167 | @EnableCaching 168 | class DemoApplication { 169 | ... 170 | } 171 | ``` 172 | 173 | ### Cacheable 174 | 175 | Once you configured the Spring Boot with Redis and enabled caching, you can use the `@Cacheable` annotation to cache return values. 176 | 177 | ```java 178 | @Service 179 | class OrderService { 180 | private final OrderRepository orderRepository; 181 | 182 | public OrderService(OrderRepository orderRepository) { 183 | this.orderRepository = orderRepository; 184 | } 185 | 186 | @Cacheable("order") 187 | public Order getOrder(Long id) { 188 | orderRepository.findById(id); 189 | } 190 | } 191 | ``` 192 | 193 | -------------------------------------------------------------------------------- /app-dev/cloud-services/cache/memorystore-redis.md: -------------------------------------------------------------------------------- 1 | # Memorystore Redis 2 | 3 | ## Memorystore Redis Instance 4 | 5 | ### Enable API 6 | 7 | ```bash 8 | gcloud services enable servicenetworking.googleapis.com 9 | gcloud services enable redis.googleapis.com 10 | ``` 11 | 12 | {% hint style="warning" %} 13 | Enabling this API may take a few minutes. 14 | {% endhint %} 15 | 16 | ### Create an Instance 17 | 18 | Create an instance and attach it to the default VPC. 19 | 20 | ```bash 21 | gcloud redis instances create orders-cache \ 22 | --size=1 --region=us-central1 23 | ``` 24 | 25 | {% hint style="warning" %} 26 | Creating a Redis instance may take a few minutes. 27 | {% endhint %} 28 | 29 | ### Get Instance IP Address 30 | 31 | ```bash 32 | gcloud redis instances describe orders-cache \ 33 | --region=us-central1 --format="value(host)" 34 | ``` 35 | 36 | {% hint style="warning" %} 37 | The IP address is not a static IP address. If you create the instance, the IP address may be different. 38 | {% endhint %} 39 | 40 | ### Connect to Instance 41 | 42 | See [Memorystore connectivity options](./#connectivity) to see how to connect to a Memorystore instance from different computing environments. 43 | 44 | | Computing Environment | | 45 | | :--- | :--- | 46 | | Compute Engine | [Guide](https://cloud.google.com/memorystore/docs/redis/connect-redis-instance-gce) | 47 | | Kubernetes Engine | [Guide](https://cloud.google.com/memorystore/docs/redis/connect-redis-instance-gke) | 48 | | App Engine Flexible | [Guide](https://cloud.google.com/memorystore/docs/redis/connect-redis-instance-flex#java_1) | 49 | | App Engine Standard | [Guide](https://cloud.google.com/memorystore/docs/redis/connect-redis-instance-standard) | 50 | | Cloud Run | [Guide](https://cloud.google.com/run/docs/configuring/connecting-vpc) | 51 | | Cloud Function | [Guide](https://cloud.google.com/memorystore/docs/redis/connect-redis-instance-functions) | 52 | 53 | You can test quickly by creating a Compute Engine instance in a zone within the same region: 54 | 55 | ```bash 56 | gcloud compute instances create test-memorystore-vm --zone=us-central1-c 57 | ``` 58 | 59 | SSH into the machine: 60 | 61 | ```bash 62 | gcloud compute ssh test-memorystore-vm --zone=us-central1-c 63 | ``` 64 | 65 | Install `redis-cli`: 66 | 67 | ```bash 68 | sudo apt-get update && sudo apt-get install -y redis-tools 69 | ``` 70 | 71 | Connect to the instance: 72 | 73 | ```bash 74 | redis-cli -h 75 | ``` 76 | 77 | You can try different Redis commands, for example: 78 | 79 | ```text 80 | > PING 81 | PONG 82 | > SET greeting Hello 83 | OK 84 | > GET greeting 85 | "Hello" 86 | ``` 87 | 88 | {% hint style="info" %} 89 | See [redis-cli documentation](https://redis.io/topics/rediscli) for more information. 90 | {% endhint %} 91 | 92 | ## Spring Boot Cache 93 | 94 | 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). 95 | 96 | ### Dependency 97 | 98 | Add the Spring Data Redis starter: 99 | 100 | {% tabs %} 101 | {% tab title="Maven" %} 102 | ```bash 103 | 104 | org.springframework.cloud 105 | spring-boot-starter-cache 106 | 107 | 108 | org.springframework.boot 109 | spring-boot-starter-data-redis 110 | 111 | ``` 112 | {% endtab %} 113 | 114 | {% tab title="Gradle" %} 115 | ```bash 116 | compile group: 'org.springframework.cloud', name: 'spring-boot-starter-cache' 117 | compile group: 'org.springframework.cloud', name: 'spring-boot-starter-data-redis' 118 | ``` 119 | {% endtab %} 120 | {% endtabs %} 121 | 122 | ### Configuration 123 | 124 | Configure the Redis instance to connect to: 125 | 126 | {% code title="application.properties" %} 127 | ```bash 128 | spring.redis.host= 129 | 130 | # Configure default TTL, e.g., 10 minutes 131 | spring.cache.redis.time-to-live=600000 132 | ``` 133 | {% endcode %} 134 | 135 | ### Enable Caching 136 | 137 | Turn on caching capability explicitly with the `@EnableCaching` annotation: 138 | 139 | ```java 140 | @SpringBootApplication 141 | @EnableCaching 142 | class DemoApplication { 143 | ... 144 | } 145 | ``` 146 | 147 | ### Cacheable 148 | 149 | Once you configured the Spring Boot with Redis and enabled caching, you can use the `@Cacheable` annotation to cache return values. 150 | 151 | ```java 152 | @Service 153 | class OrderService { 154 | private final OrderRepository orderRepository; 155 | 156 | public OrderService(OrderRepository orderRepository) { 157 | this.orderRepository = orderRepository; 158 | } 159 | 160 | @Cacheable("order") 161 | public Order getOrder(Long id) { 162 | orderRepository.findById(id); 163 | } 164 | } 165 | ``` 166 | 167 | {% hint style="info" %} 168 | 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. 169 | {% endhint %} 170 | 171 | ## Spring Boot Session 172 | 173 | 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). 174 | 175 | ### Dependency 176 | 177 | Add the Spring Data Spanner starter: 178 | 179 | {% tabs %} 180 | {% tab title="Maven" %} 181 | ```bash 182 | 183 | org.springframework.cloud 184 | spring-session-data-redis 185 | 186 | 187 | org.springframework.boot 188 | spring-boot-starter-data-redis 189 | 190 | ``` 191 | {% endtab %} 192 | 193 | {% tab title="Gradle" %} 194 | ```bash 195 | compile group: 'org.springframework.cloud', name: 'spring-session-data-redis' 196 | compile group: 'org.springframework.cloud', name: 'spring-boot-starter-data-redis' 197 | ``` 198 | {% endtab %} 199 | {% endtabs %} 200 | 201 | ### Configuration 202 | 203 | Configure the Redis instance to connect to: 204 | 205 | {% code title="application.properties" %} 206 | ```bash 207 | spring.redis.host= 208 | 209 | # Configure default TTL, e.g., 10 minutes 210 | spring.cache.redis.time-to-live=600000 211 | ``` 212 | {% endcode %} 213 | 214 | ### Enable HTTP Session 215 | 216 | Turn on caching capability explicitly with the `@EnableSpringHttpSession` annotation: 217 | 218 | ```java 219 | @SpringBootApplication 220 | @EnableSpringHttpSession 221 | class DemoApplication { 222 | ... 223 | } 224 | ``` 225 | 226 | ### Samples 227 | 228 | * [Spring Boot Session Data Redis sample](https://github.com/spring-projects/spring-session/tree/master/spring-session-samples/spring-session-sample-boot-redis-simple) 229 | 230 | -------------------------------------------------------------------------------- /app-dev/cloud-services/databases/README.md: -------------------------------------------------------------------------------- 1 | # Databases 2 | 3 | Google Cloud Platform offers a number of different fully managed database services from RDBMS to NoSQL for different use cases. 4 | 5 | | Service | Description | Use Case | 6 | | :--- | :--- | :--- | 7 | | [Cloud SQL](cloud-sql.md) | Managed, highly available RDBMS with MySQL, PostgreSQL, and SQL Server. | Traditional RDBMS, enterprise/business applications, web applications. | 8 | | [Cloud Spanner](cloud-spanner.md) | 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. | 9 | | [Cloud Firestore](cloud-firestore/) | Managed NoSQL Document-oriented database. | Applications with data structure that's fast changing, or document oriented. | 10 | 11 | -------------------------------------------------------------------------------- /app-dev/cloud-services/databases/cloud-datastore.md: -------------------------------------------------------------------------------- 1 | # Cloud Datastore 2 | 3 | ## Cloud Datastore Instance 4 | 5 | ### Enable API 6 | 7 | ## Spring Data Datastore 8 | 9 | ### ORM 10 | 11 | ### Repository 12 | 13 | ### Rest Repository 14 | 15 | -------------------------------------------------------------------------------- /app-dev/cloud-services/databases/cloud-firestore-1/README.md: -------------------------------------------------------------------------------- 1 | # Cloud Firestore 2 | 3 | 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. 4 | 5 | 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: 6 | 7 | * 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.. 8 | * Datastore mode does not have the real-time capability, and the data model is composed of Entity and organized in Kind of entities. 9 | 10 | {% hint style="info" %} 11 | Read [Choosing between Native mode and Datastore mode](https://cloud.google.com/datastore/docs/firestore-or-datastore) for more information. 12 | {% endhint %} 13 | 14 | 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. 15 | 16 | {% hint style="danger" %} 17 | 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. 18 | {% endhint %} 19 | 20 | Learn how to use each of the mode in the following pages. 21 | 22 | {% page-ref page="cloud-firestore-datastore.md" %} 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app-dev/cloud-services/databases/cloud-firestore-1/cloud-firestore-datastore.md: -------------------------------------------------------------------------------- 1 | # Datastore Mode 2 | 3 | ## Cloud Firestore Datastore Instance 4 | 5 | There can only be one Cloud Firestore instance associated with a single project. The Datastore instance is automatically created when you enable the API: 6 | 7 | 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: 8 | 9 | ### Enable API 10 | 11 | ```bash 12 | gcloud services enable datastore.googleapis.com 13 | ``` 14 | 15 | ### Data Schema 16 | 17 | 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. 18 | 19 | ## Spring Data Datastore 20 | 21 | 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. 22 | 23 | | Spring Data Feature | Supported | 24 | | :--- | :--- | 25 | | ORM | ✅ | 26 | | Declarative Transaction | ✅ | 27 | | Repository | ✅ | 28 | | REST Repository | ✅ | 29 | | Query methods | ✅ | 30 | | Query annotation | ✅ | 31 | | Pagination | ✅ | 32 | | Events | ✅ | 33 | | Auditing | ✅ | 34 | 35 | ### Dependency 36 | 37 | Add the Spring Data Datastore starter: 38 | 39 | {% tabs %} 40 | {% tab title="Maven" %} 41 | ```bash 42 | 43 | org.springframework.cloud 44 | spring-cloud-gcp-starter-data-datastore 45 | 46 | ``` 47 | {% endtab %} 48 | 49 | {% tab title="Gradle" %} 50 | ```bash 51 | compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-datastore' 52 | ``` 53 | {% endtab %} 54 | {% endtabs %} 55 | 56 | ### Configuration 57 | 58 | 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`. 59 | 60 | {% hint style="info" %} 61 | 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\). 62 | {% endhint %} 63 | 64 | ### ORM 65 | 66 | 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 67 | 68 | ```java 69 | @Entity 70 | class Order { 71 | @Id 72 | private Long id; 73 | private String description; 74 | private LocalDateTime timestamp; 75 | private List items; 76 | 77 | // Getters and setters ... 78 | } 79 | 80 | @Entity 81 | class OrderItem { 82 | private String description; 83 | private Long quantity; 84 | 85 | // Getters and setters ... 86 | } 87 | ``` 88 | 89 | Because Datastore is a document-oriented NoSQL database, you can have nested structure, you can establish parent-children relationships without complicated foreign keys. 90 | 91 | {% hint style="info" %} 92 | 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. 93 | {% endhint %} 94 | 95 | ### Repository 96 | 97 | Use Spring Data repository to quickly get CRUD access to the Datastore. 98 | 99 | ```java 100 | @Repository 101 | interface OrderRepository extends DatastoreRepository { 102 | } 103 | ``` 104 | 105 | In a business logic service, you can utilize the repositories: 106 | 107 | ```java 108 | @Service 109 | class OrderService { 110 | private final OrderRepository orderRepository; 111 | 112 | OrderService(OrderRepository orderRepository) { 113 | this.orderRepository = orderRepository; 114 | } 115 | 116 | @Transactional 117 | Order createOrder(Order order) { 118 | // Set the creation time 119 | order.setTimestamp(LocalDateTime.now()); 120 | 121 | // Children are saved in cascade. 122 | return orderRepository.save(order); 123 | } 124 | } 125 | ``` 126 | 127 | ### Rest Repository 128 | 129 | [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. 130 | 131 | Add Spring Data Rest starter: 132 | 133 | {% tabs %} 134 | {% tab title="Maven" %} 135 | ```markup 136 | 137 | org.springframework.boot 138 | spring-boot-starter-data-rest 139 | 140 | ``` 141 | {% endtab %} 142 | 143 | {% tab title="Gradle" %} 144 | ```groovy 145 | compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-rest' 146 | ``` 147 | {% endtab %} 148 | {% endtabs %} 149 | 150 | ```java 151 | @RepositoryRestResource 152 | interface OrderRepository extends DatastoreRepository { 153 | } 154 | ``` 155 | 156 | To access the endpoint for Order: 157 | 158 | ```java 159 | curl http://localhost:8080/orders 160 | ``` 161 | 162 | ### Samples 163 | 164 | * [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) 165 | 166 | -------------------------------------------------------------------------------- /app-dev/cloud-services/databases/cloud-firestore-1/native-mode.md: -------------------------------------------------------------------------------- 1 | # Native Mode 2 | 3 | ## Cloud Firestore Native Instance 4 | 5 | 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: 6 | 7 | ### Enable API 8 | 9 | ```bash 10 | gcloud services enable firestore.googleapis.com 11 | ``` 12 | 13 | ### Data Schema 14 | 15 | 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. 16 | 17 | ## Spring Data Firestore 18 | 19 | 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. 20 | 21 | | Spring Data Feature | Supported | 22 | | :--- | :--- | 23 | | Reactive Repository | ✅ | 24 | | ORM | ✅ | 25 | | Declarative Transaction | ✅ | 26 | | Repository | ✅ | 27 | | REST Repository | ❌ | 28 | | Query methods | ✅ | 29 | | Query annotation | ✅ | 30 | | Pagination | ✅ | 31 | | Events | ✅ | 32 | | Auditing | ✅ | 33 | 34 | ### Dependency 35 | 36 | Add the Spring Data Firestore starter: 37 | 38 | {% tabs %} 39 | {% tab title="Maven" %} 40 | ```bash 41 | 42 | org.springframework.cloud 43 | spring-cloud-gcp-starter-data-firestore 44 | 45 | ``` 46 | {% endtab %} 47 | 48 | {% tab title="Gradle" %} 49 | ```bash 50 | compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-firestore' 51 | ``` 52 | {% endtab %} 53 | {% endtabs %} 54 | 55 | ### Configuration 56 | 57 | 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`. 58 | 59 | {% hint style="info" %} 60 | 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\). 61 | {% endhint %} 62 | 63 | ### ORM 64 | 65 | Spring Data Cloud Firestore allows you to map domain POJOs to Datastore documents via annotations. 66 | 67 | ```java 68 | @Document 69 | class Order { 70 | @DocumentId 71 | private String id; 72 | private String description; 73 | private LocalDateTime timestamp; 74 | private List items; 75 | 76 | // Getters and setters ... 77 | } 78 | 79 | class OrderItem { 80 | private String description; 81 | private Long quantity; 82 | 83 | // Getters and setters ... 84 | } 85 | ``` 86 | 87 | Because Firestore is a document-oriented NoSQL database, you can have nested structure, you can establish parent-children relationships without complicated foreign keys. 88 | 89 | {% hint style="info" %} 90 | 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. 91 | {% endhint %} 92 | 93 | ### Repository 94 | 95 | Use Spring Data Reactive repository to quickly get CRUD access to the Cloud Firestore. 96 | 97 | ```java 98 | @Repository 99 | interface OrderRepository extends FirestoreReactiveRepository { 100 | } 101 | ``` 102 | 103 | In a business logic service, you can utilize the repositories: 104 | 105 | ```java 106 | @Service 107 | class OrderService { 108 | private final OrderRepository orderRepository; 109 | 110 | OrderService(OrderRepository orderRepository) { 111 | this.orderRepository = orderRepository; 112 | } 113 | 114 | @Transactional 115 | Mono createOrder(Order order) { 116 | // Set the creation time 117 | order.setTimestamp(Timestamp.of(new Date())); 118 | 119 | // Children are saved in cascade. 120 | return orderRepository.save(order); 121 | } 122 | } 123 | ``` 124 | 125 | ### Samples 126 | 127 | * [Spring Boot with Cloud Firestore sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-data-firestore) 128 | 129 | -------------------------------------------------------------------------------- /app-dev/cloud-services/databases/cloud-firestore.md: -------------------------------------------------------------------------------- 1 | # Cloud Firestore 2 | 3 | ## Cloud Firestore Instance 4 | 5 | ### Enable API 6 | 7 | ### Create an Instance 8 | 9 | ### Create a Database 10 | 11 | ## Spring Data Firestore 12 | 13 | ### ORM 14 | 15 | ### Repository 16 | 17 | ### Rest Repository 18 | 19 | -------------------------------------------------------------------------------- /app-dev/cloud-services/databases/cloud-firestore/README.md: -------------------------------------------------------------------------------- 1 | # Cloud Firestore 2 | 3 | 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. 4 | 5 | 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: 6 | 7 | * 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.. 8 | * Datastore mode does not have the real-time capability, and the data model is composed of Entity and organized in Kind of entities. 9 | 10 | {% hint style="info" %} 11 | Read [Choosing between Native mode and Datastore mode](https://cloud.google.com/datastore/docs/firestore-or-datastore) for more information. 12 | {% endhint %} 13 | 14 | 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. 15 | 16 | {% hint style="danger" %} 17 | 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. 18 | {% endhint %} 19 | 20 | Learn how to use each of the mode in the following pages. 21 | 22 | {% page-ref page="datastore-mode.md" %} 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app-dev/cloud-services/databases/cloud-firestore/datastore-mode.md: -------------------------------------------------------------------------------- 1 | # Datastore Mode 2 | 3 | ## Cloud Firestore Datastore Instance 4 | 5 | There can only be one Cloud Firestore instance associated with a single project. The Datastore instance is automatically created when you enable the API: 6 | 7 | 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: 8 | 9 | ### Enable API 10 | 11 | ```bash 12 | gcloud services enable datastore.googleapis.com 13 | ``` 14 | 15 | ### Data Schema 16 | 17 | 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. 18 | 19 | ## Spring Data Datastore 20 | 21 | 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. 22 | 23 | | Spring Data Feature | Supported | 24 | | :--- | :--- | 25 | | ORM | ✅ | 26 | | Declarative Transaction | ✅ | 27 | | Repository | ✅ | 28 | | REST Repository | ✅ | 29 | | Query methods | ✅ | 30 | | Query annotation | ✅ | 31 | | Pagination | ✅ | 32 | | Events | ✅ | 33 | | Auditing | ✅ | 34 | 35 | ### Dependency 36 | 37 | Add the Spring Data Datastore starter: 38 | 39 | {% tabs %} 40 | {% tab title="Maven" %} 41 | ```markup 42 | 43 | org.springframework.cloud 44 | spring-cloud-gcp-starter-data-datastore 45 | 46 | ``` 47 | {% endtab %} 48 | 49 | {% tab title="Gradle" %} 50 | ```groovy 51 | compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-datastore' 52 | ``` 53 | {% endtab %} 54 | {% endtabs %} 55 | 56 | ### Configuration 57 | 58 | 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`. 59 | 60 | {% hint style="info" %} 61 | 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\). 62 | {% endhint %} 63 | 64 | ### ORM 65 | 66 | 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 67 | 68 | ```java 69 | @Entity 70 | class Order { 71 | @Id 72 | private Long id; 73 | private String description; 74 | private LocalDateTime timestamp; 75 | private List items; 76 | 77 | // Getters and setters ... 78 | } 79 | 80 | @Entity 81 | class OrderItem { 82 | private String description; 83 | private Long quantity; 84 | 85 | // Getters and setters ... 86 | } 87 | ``` 88 | 89 | Because Datastore is a document-oriented NoSQL database, you can have nested structure, you can establish parent-children relationships without complicated foreign keys. 90 | 91 | {% hint style="info" %} 92 | 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. 93 | {% endhint %} 94 | 95 | ### Repository 96 | 97 | Use Spring Data repository to quickly get CRUD access to the Datastore. 98 | 99 | ```java 100 | @Repository 101 | interface OrderRepository extends DatastoreRepository { 102 | } 103 | ``` 104 | 105 | In a business logic service you can utilize the repositories: 106 | 107 | ```java 108 | @Service 109 | class OrderService { 110 | private final OrderRepository orderRepository; 111 | 112 | OrderService(OrderRepository orderRepository) { 113 | this.orderRepository = orderRepository; 114 | } 115 | 116 | @Transactional 117 | Order createOrder(Order order) { 118 | // Set the creation time 119 | order.setTimestamp(LocalDateTime.now()); 120 | 121 | // Children are saved in cascade. 122 | return orderRepository.save(order); 123 | } 124 | } 125 | ``` 126 | 127 | ### Rest Repository 128 | 129 | [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. 130 | 131 | Add Spring Data Rest starter: 132 | 133 | {% tabs %} 134 | {% tab title="Maven" %} 135 | ```markup 136 | 137 | org.springframework.boot 138 | spring-boot-starter-data-rest 139 | 140 | ``` 141 | {% endtab %} 142 | 143 | {% tab title="Gradle" %} 144 | ```groovy 145 | compile group: 'org.springframework.boot', name: 'spring-boot-starter-data-rest' 146 | ``` 147 | {% endtab %} 148 | {% endtabs %} 149 | 150 | ```java 151 | @RepositoryRestResource 152 | interface OrderRepository extends DatastoreRepository { 153 | } 154 | ``` 155 | 156 | To access the endpoint for Order: 157 | 158 | ```java 159 | curl http://localhost:8080/orders 160 | ``` 161 | 162 | ### Samples 163 | 164 | * [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) 165 | 166 | -------------------------------------------------------------------------------- /app-dev/cloud-services/databases/cloud-firestore/native-mode.md: -------------------------------------------------------------------------------- 1 | # Native Mode 2 | 3 | ## Cloud Firestore Native Instance 4 | 5 | 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: 6 | 7 | ### Enable API 8 | 9 | ```bash 10 | gcloud services enable firestore.googleapis.com 11 | ``` 12 | 13 | ### Data Schema 14 | 15 | 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. 16 | 17 | ## Spring Data Firestore 18 | 19 | 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. 20 | 21 | | Spring Data Feature | Supported | 22 | | :--- | :--- | 23 | | Reactive Repository | ✅ | 24 | | ORM | ✅ | 25 | | Declarative Transaction | ✅ | 26 | | Repository | ✅ | 27 | | REST Repository | ❌ | 28 | | Query methods | ✅ | 29 | | Query annotation | ✅ | 30 | | Pagination | ✅ | 31 | | Events | ✅ | 32 | | Auditing | ✅ | 33 | 34 | ### Dependency 35 | 36 | Add the Spring Data Firestore starter: 37 | 38 | {% tabs %} 39 | {% tab title="Maven" %} 40 | ```markup 41 | 42 | org.springframework.cloud 43 | spring-cloud-gcp-starter-data-firestore 44 | 45 | ``` 46 | {% endtab %} 47 | 48 | {% tab title="Gradle" %} 49 | ```groovy 50 | compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-data-firestore' 51 | ``` 52 | {% endtab %} 53 | {% endtabs %} 54 | 55 | ### Configuration 56 | 57 | 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`. 58 | 59 | {% hint style="info" %} 60 | 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\). 61 | {% endhint %} 62 | 63 | ### ORM 64 | 65 | Spring Data Cloud Firestore allows you to map domain POJOs to Datastore documents via annotations. 66 | 67 | ```java 68 | @Document 69 | class Order { 70 | @DocumentId 71 | private String id; 72 | private String description; 73 | private LocalDateTime timestamp; 74 | private List items; 75 | 76 | // Getters and setters ... 77 | } 78 | 79 | class OrderItem { 80 | private String description; 81 | private Long quantity; 82 | 83 | // Getters and setters ... 84 | } 85 | ``` 86 | 87 | Because Firestore is a document-oriented NoSQL database, you can have nested structure and can establish parent-children relationships without complicated foreign keys. 88 | 89 | {% hint style="info" %} 90 | 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. 91 | {% endhint %} 92 | 93 | ### Repository 94 | 95 | Use Spring Data Reactive repository to quickly get CRUD access to the Cloud Firestore. 96 | 97 | ```java 98 | @Repository 99 | interface OrderRepository extends FirestoreReactiveRepository { 100 | } 101 | ``` 102 | 103 | In a business logic service, you can utilize the repositories: 104 | 105 | ```java 106 | @Service 107 | class OrderService { 108 | private final OrderRepository orderRepository; 109 | 110 | OrderService(OrderRepository orderRepository) { 111 | this.orderRepository = orderRepository; 112 | } 113 | 114 | @Transactional 115 | Mono createOrder(Order order) { 116 | // Set the creation time 117 | order.setTimestamp(Timestamp.of(new Date())); 118 | 119 | // Children are saved in cascade. 120 | return orderRepository.save(order); 121 | } 122 | } 123 | ``` 124 | 125 | ### Samples 126 | 127 | * [Spring Boot with Cloud Firestore sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-data-firestore) 128 | 129 | -------------------------------------------------------------------------------- /app-dev/cloud-services/messaging/README.md: -------------------------------------------------------------------------------- 1 | # Messaging 2 | 3 | -------------------------------------------------------------------------------- /app-dev/cloud-services/messaging/cloud-pub-sub.md: -------------------------------------------------------------------------------- 1 | # Cloud Pub/Sub 2 | 3 | -------------------------------------------------------------------------------- /app-dev/cloud-services/messaging/confluent-cloud-kafka.md: -------------------------------------------------------------------------------- 1 | # Confluent Cloud Kafka 2 | 3 | -------------------------------------------------------------------------------- /app-dev/cloud-services/messaging/kafka.md: -------------------------------------------------------------------------------- 1 | # Kafka 2 | 3 | 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. 4 | 5 | ## Confluent Cloud 6 | 7 | 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. 8 | 9 | ## Confluent Operator 10 | 11 | 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. 12 | 13 | ## Strimzi Operator 14 | 15 | 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. 16 | 17 | -------------------------------------------------------------------------------- /app-dev/cloud-services/other-services.md: -------------------------------------------------------------------------------- 1 | # Other Services 2 | 3 | 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. 4 | 5 | ## Dependency 6 | 7 | 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. 8 | 9 | For example, to add Container Analysis client library: 10 | 11 | {% tabs %} 12 | {% tab title="Maven" %} 13 | ```markup 14 | 15 | com.google.cloud 16 | google-cloud-containeranalysis 17 | 18 | ``` 19 | {% endtab %} 20 | 21 | {% tab title="Gradle" %} 22 | ```groovy 23 | compile group: 'com.google.cloud', name: 'google-cloud-containeranalysis' 24 | ``` 25 | {% endtab %} 26 | {% endtabs %} 27 | 28 | ## Credentials 29 | 30 | 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. 31 | 32 | 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: 33 | 34 | ```java 35 | @Bean(destroyMethod = "shutdownNow") 36 | ContainerAnalysisClient containerAnalysisClient(CredentialsProvider credentialsProvider) throws IOException { 37 | return ContainerAnalysisClient.create( 38 | ContainerAnalysisSettings.newBuilder() 39 | .setCredentialsProvider(credentialsProvider).build()); 40 | } 41 | ``` 42 | 43 | ## Project ID 44 | 45 | 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). 46 | 47 | -------------------------------------------------------------------------------- /app-dev/cloud-services/secret-management.md: -------------------------------------------------------------------------------- 1 | # Secret Management 2 | 3 | ## Cloud Secret Manager 4 | 5 | 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. 6 | 7 | ### Enable API 8 | 9 | ```bash 10 | gcloud services enable secretmanager.googleapis.com 11 | ``` 12 | 13 | ### Create a Secret 14 | 15 | ```bash 16 | echo -n "qwerty" | \ 17 | gcloud secrets create order-db-password --data-file=- --replication-policy=automatic 18 | ``` 19 | 20 | ### List Secrets 21 | 22 | ```bash 23 | gcloud secrets list 24 | ``` 25 | 26 | ### Delete a Secret 27 | 28 | ```bash 29 | gcloud secrets delete order-db-password 30 | ``` 31 | 32 | ### Assign IAM Permission 33 | 34 | 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. 35 | 36 | ```bash 37 | gcloud secrets add-iam-policy-binding --help 38 | ``` 39 | 40 | ## Spring Cloud Secret Manager 41 | 42 | 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). 43 | 44 | ### Dependency 45 | 46 | Add the Spring Cloud GCP Secret Manager starter: 47 | 48 | {% tabs %} 49 | {% tab title="Maven" %} 50 | ```markup 51 | 52 | org.springframework.cloud 53 | spring-cloud-gcp-starter-secretmanager 54 | 55 | ``` 56 | {% endtab %} 57 | 58 | {% tab title="Gradle" %} 59 | ```groovy 60 | compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-secretmanager' 61 | ``` 62 | {% endtab %} 63 | {% endtabs %} 64 | 65 | ### Configuration 66 | 67 | 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. 68 | 69 | {% hint style="info" %} 70 | 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. 71 | {% endhint %} 72 | 73 | ### Property Source 74 | 75 | You can access individual secrets in stored in Secret Manager by looking up property keys with the `sm://` prefix. 76 | 77 | #### @Value Annotation 78 | 79 | You can inject the secret value by using the `Value` annotation. 80 | 81 | ```java 82 | @Value("sm://order-db-password") String databasePassword; 83 | ``` 84 | 85 | #### Properties Mapping 86 | 87 | You can refer to the secret value like any other properties, and reference the secret values in a `properties` file. 88 | 89 | {% code title="application.properties" %} 90 | ```text 91 | spring.datasource.password=${sm://order-db-password} 92 | ``` 93 | {% endcode %} 94 | 95 | Mapping properties this way, rather than hard-coding the Secret Manager property key using `@Value` annotation can be help you utilize multiple profiles. 96 | 97 | For example, you can have `application-dev.properties` with: 98 | 99 | {% code title="application.properties" %} 100 | ```text 101 | spring.datasource.password=${sm://order-db-dev-password} 102 | ``` 103 | {% endcode %} 104 | 105 | And, for production, create an `application-prod.properties` with: 106 | 107 | {% code title="application-prod.properties" %} 108 | ```text 109 | spring.datasource.password=${sm://order-db-prod-password} 110 | ``` 111 | {% endcode %} 112 | 113 | #### Property Key Syntax 114 | 115 | | Form | Example | 116 | | :--- | :--- | 117 | | Short | `sm://order-db-password` | 118 | | Short - Versioned | `sm://order-db-password/1` | 119 | | Short - Project Scoped and Versioned | `sm://your-project/order-db-password/1` | 120 | | Long - Project Scoped | `sm://projects/your-project/order-db-password/1` | 121 | | Long - Fully Qualified | `sm://projects/your-project/secrets/order-db-password/versions/1` | 122 | 123 | ### Local Development 124 | 125 | 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. 126 | 127 | #### Default Profile 128 | 129 | Configure the default profile to disable Secret Manager 130 | 131 | {% code title="bootstrap.properties" %} 132 | ```text 133 | spring.cloud.gcp.secretmanager.enabled=false 134 | ``` 135 | {% endcode %} 136 | 137 | Hard-code the local test credentials with the value as usual. 138 | 139 | {% code title="application.properties" %} 140 | ```text 141 | ... 142 | spring.datasource.password=admin 143 | ``` 144 | {% endcode %} 145 | 146 | #### Production Profile 147 | 148 | Configure the production profile to enable Secret Manager. 149 | 150 | {% code title="bootstrap-prod.properties" %} 151 | ```text 152 | spring.cloud.gcp.secretmanager.enabled=true 153 | ``` 154 | {% endcode %} 155 | 156 | Configure production profile to retrieve the credential from Secret Manager. 157 | 158 | {% code title="application-prod.properties" %} 159 | ```text 160 | ... 161 | spring.datasource.password=${sm://order-db-prod-password} 162 | ``` 163 | {% endcode %} 164 | 165 | Start your application with the profile, for example: 166 | 167 | ```bash 168 | # From Maven 169 | ./mvnw spring-boot:run -Dspring-boot.run.profiles=prod 170 | 171 | # From Java startup command 172 | java -jar target/...jar -Dspring.profiles.active=prod 173 | ``` 174 | 175 | ### Samples 176 | 177 | * [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) 178 | 179 | -------------------------------------------------------------------------------- /app-dev/development-tools.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Overview of application development tools for Java developers with Google 4 | Cloud Platform. Google Cloud Platform has a range of tools to span across all 5 | application development lifecycle. 6 | --- 7 | 8 | # Development Tools 9 | 10 | 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. 11 | 12 | ## IDE 13 | 14 | | | IntelliJ | VS Code | Eclipse | 15 | | :--- | :--- | :--- | :--- | 16 | | **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) | 17 | | **App Engine Support** | Yes | Yes | Yes | 18 | | **Kubernetes Support** | Yes | Yes | No | 19 | | **Add Client Libraries** | Yes | Yes | No | 20 | 21 | ## Maven / Gradle Plugins 22 | 23 | | | Maven | Gradle | 24 | | :--- | :--- | :--- | 25 | | **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) | 26 | | **Cloud Function Support** | [function-maven-plugin](https://github.com/GoogleCloudPlatform/functions-framework-java) | N/A | 27 | | **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) | 28 | 29 | ## Framework Support 30 | 31 | ### Spring Boot 32 | 33 | [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. 34 | 35 | ### Micronaut 36 | 37 | [Micronaut GCP](https://micronaut-projects.github.io/micronaut-gcp/latest/guide/index.html) provides integration with GCP services. 38 | 39 | | Concerns | GCP Service | Micronaut Abstraction | 40 | | :--- | :--- | :--- | 41 | | **Distributed Tracing** | Cloud Trace | Zipkin / Brave | 42 | 43 | ### Hibernate 44 | 45 | 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. 46 | 47 | ### R2DBC 48 | 49 | Use R2DBC to access database to produce highly concurrent non-blocking microservices. 50 | 51 | | Database | R2DBC Support | 52 | | :--- | :--- | 53 | | **Cloud Spanner** | [cloud-spanner-r2dbc](https://github.com/GoogleCloudPlatform/cloud-spanner-r2dbc) | 54 | | **Cloud SQL - PostgreSQL** | [r2dbc-postgresql](https://github.com/r2dbc/r2dbc-postgresql) with [Cloud SQL Proxy](https://cloud.google.com/sql/docs/postgres/sql-proxy) | 55 | | **Cloud SQL - MySQL** | [r2dbc-mysql](https://github.com/mirromutth/r2dbc-mysql) with [Cloud SQL Proxy](https://cloud.google.com/sql/docs/mysql/sql-proxy) | 56 | | **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) | 57 | 58 | ## DevOps Tools 59 | 60 | ### Cloud Build 61 | 62 | 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). 63 | 64 | ### Artifact Registry 65 | 66 | Publish Java artifacts to Artifact Registry, which can host Maven repositories. See [Artifact Repository](devops/artifact-repository.md) section. 67 | 68 | ### Cloud Trace 69 | 70 | Use Spring Cloud Sleuth and send Distributed Tracing data to Cloud Trace, using Spring Cloud GCP. See [Trace section](observability/trace.md). 71 | 72 | ### Cloud Logging 73 | 74 | Aggregate logs into a centralized logging console to easily search and view logs. See [Logging section](observability/logging.md). 75 | 76 | ### Error Reporting 77 | 78 | Automatically identifies Java exceptions and produce reports. Easily see new exceptions and their frequencies. See [Logging section](observability/logging.md#error-reporting). 79 | 80 | ### Cloud Monitoring 81 | 82 | Collect system and application metrics, build dashboards, and setup alerts. See [Metrics section](observability/metrics.md). 83 | 84 | ### Cloud Debugger 85 | 86 | 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](observability/debugging.md). 87 | 88 | ### Cloud Profiler 89 | 90 | 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](observability/profiling.md). 91 | 92 | -------------------------------------------------------------------------------- /app-dev/devops/README.md: -------------------------------------------------------------------------------- 1 | # DevOps 2 | 3 | -------------------------------------------------------------------------------- /app-dev/devops/artifact-repository.md: -------------------------------------------------------------------------------- 1 | # Artifact Repository 2 | 3 | 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. 4 | 5 | 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/)! 6 | 7 | In addition, Google Cloud also has a fully managed artifact repository service called [Artifact Registry](https://cloud.google.com/artifact-registry) \(beta\). 8 | 9 | ## Artifact Registry 10 | 11 | [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. 12 | 13 | {% hint style="info" %} 14 | See [Artifact Registry documentation](https://cloud.google.com/artifact-registry) for more information. 15 | {% endhint %} 16 | 17 | ### Enable API 18 | 19 | ```bash 20 | gcloud services enable artifactregistry.googleapis.com 21 | ``` 22 | 23 | ## Maven Repository 24 | 25 | 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. 26 | 27 | {% embed url="https://www.youtube.com/watch?v=2-P4cSCk1VM" %} 28 | 29 | {% hint style="warning" %} 30 | 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. 31 | 32 | [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. 33 | {% endhint %} 34 | 35 | Once you are confirmed to be enrolled in the alpha program, you can give it a try! 36 | 37 | ### Create a Maven Repository 38 | 39 | ```bash 40 | gcloud beta artifacts repositories create private-maven-repo \ 41 | --repository-format=maven \ 42 | --location=us-central1 43 | ``` 44 | 45 | ### List Artifacts 46 | 47 | ```bash 48 | gcloud beta artifacts packages list \ 49 | --repository=private-maven-repo \ 50 | --location=us-central1 51 | ``` 52 | 53 | There should be no artifacts at the moment. 54 | 55 | ### Deploy a Maven Artifact 56 | 57 | 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: 58 | 59 | #### Generate a New Project 60 | 61 | This example will use Maven. First, create a brand new Maven project: 62 | 63 | ```bash 64 | mvn archetype:generate \ 65 | -DinteractiveMode=false \ 66 | -DgroupId=com.example \ 67 | -DartifactId=common-libs \ 68 | -DarchetypeGroupId=org.apache.maven.archetypes \ 69 | -DarchetypeArtifactId=maven-archetype-quickstart 70 | 71 | cd common-libs/ 72 | ``` 73 | 74 | #### Configuration 75 | 76 | 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. 77 | 78 | {% tabs %} 79 | {% tab title="Maven" %} 80 | ```bash 81 | gcloud beta artifacts print-settings mvn \ 82 | --repository=private-maven-repo \ 83 | --location=us-central1 84 | ``` 85 | 86 | Note that an Artifact Registry Wagon extension is needed to publish to Artifact Registry. 87 | {% endtab %} 88 | 89 | {% tab title="Gradle" %} 90 | ```bash 91 | gcloud beta artifacts print-settings gradle \ 92 | --repository=private-maven-repo \ 93 | --location=us-central1 94 | ``` 95 | 96 | Note that an Artifact Registry Gradle plugin is needed to publish to Artifact Registry. 97 | {% endtab %} 98 | {% endtabs %} 99 | 100 | Artifact Registry's plugins will automatically detect the current [Application Default Credentials](../../getting-started/google-cloud-platform.md#application-default-credentials) to authorize access. 101 | 102 | This example uses Maven, so edit the `pom.xml` to add the additional settings: 103 | 104 | ```markup 105 | 106 | 107 | 109 | 110 | ... 111 | 112 | 113 | 114 | ... 115 | 116 | 117 | 118 | 119 | ... 120 | 121 | 122 | 123 | 124 | 125 | 126 | com.google.cloud.artifactregistry 127 | artifactregistry-maven-wagon 128 | 2.1.0 129 | 130 | 131 | 132 | 133 | ... 134 | 135 | 136 | 137 | ``` 138 | 139 | #### Build and Deploy 140 | 141 | ```bash 142 | mvn clean package deploy 143 | ``` 144 | 145 | Verify that the artifact is published! 146 | 147 | ```bash 148 | gcloud alpha artifacts packages list \ 149 | --repository=private-maven-repo \ 150 | --location=us-central1 151 | ``` 152 | 153 | In the Cloud Console, you can also browse to **Artifact Registry > private-maven-repo.** 154 | 155 | ![](../../.gitbook/assets/image%20%2842%29.png) 156 | 157 | And see manage the artifacts: 158 | 159 | ![](../../.gitbook/assets/image%20%2841%29.png) 160 | 161 | {% hint style="info" %} 162 | See [Artifact Registry Quickstart for Maven and Gradle](https://cloud.google.com/artifact-registry/docs/java/quickstart) for more information. 163 | {% endhint %} 164 | 165 | -------------------------------------------------------------------------------- /app-dev/observability/README.md: -------------------------------------------------------------------------------- 1 | # Observability 2 | 3 | -------------------------------------------------------------------------------- /app-dev/observability/trace.md: -------------------------------------------------------------------------------- 1 | # Trace 2 | 3 | ## Cloud Trace 4 | 5 | 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. 6 | 7 | ### Enable API 8 | 9 | ```bash 10 | gcloud services enable cloudtrace.googleapis.com 11 | ``` 12 | 13 | ## Spring Cloud Sleuth 14 | 15 | [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. 16 | 17 | ### Dependency 18 | 19 | Add the Spring Cloud GCP Trace starter: 20 | 21 | {% tabs %} 22 | {% tab title="Maven" %} 23 | ```markup 24 | 25 | org.springframework.cloud 26 | spring-cloud-gcp-starter-trace 27 | 28 | ``` 29 | {% endtab %} 30 | 31 | {% tab title="Gradle" %} 32 | ```groovy 33 | compile group: 'org.springframework.cloud', name: 'spring-cloud-gcp-starter-trace' 34 | ``` 35 | {% endtab %} 36 | {% endtabs %} 37 | 38 | ### Configuration 39 | 40 | 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: 41 | 42 | {% code title="application.properties" %} 43 | ```text 44 | # Set sampler probability to 100% 45 | spring.sleuth.sampler.probability=1.0 46 | ``` 47 | {% endcode %} 48 | 49 | {% hint style="info" %} 50 | 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\). 51 | {% endhint %} 52 | 53 | ### Instrumentation 54 | 55 | 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. 56 | 57 | #### Web 58 | 59 | Spring Cloud Sleuth will automatically trace incoming requests from WebMVC, or WebFlux as-is. 60 | 61 | ```java 62 | @RestController 63 | class OrderController { 64 | private final OrderRepository orderRepository; 65 | 66 | OrderController(OrderRepository orderService) { 67 | this.orderRepository = orderRepository; 68 | } 69 | 70 | @GetMapping("/order/{orderId}") 71 | public Order getOrder(@PathParam String orderId) { 72 | return orderRepository.findById(orderId); 73 | } 74 | } 75 | ``` 76 | 77 | {% hint style="info" %} 78 | 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. 79 | {% endhint %} 80 | 81 | #### Messaging 82 | 83 | Spring Cloud Sleuth will automatically trace incoming messages and handlers when using Spring Integration 84 | 85 | #### Custom Spans 86 | 87 | 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). 88 | 89 | ```java 90 | @Service 91 | class OrderService { 92 | private final OrderRepository orderRepository; 93 | 94 | ... 95 | 96 | @NewSpan 97 | @Transactional 98 | Order createOrder(Order order) { 99 | ... 100 | return orderRepository.save(order); 101 | } 102 | } 103 | ``` 104 | 105 | #### Tagging Spans 106 | 107 | 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). 108 | 109 | ```java 110 | @Service 111 | class OrderService { 112 | private final OrderRepository orderRepository; 113 | 114 | ... 115 | 116 | @NewSpan 117 | @Transactional 118 | Order getOrder(@SpanTag("orderId") String id) { 119 | return orderRepository.findById(id); 120 | } 121 | } 122 | ``` 123 | 124 | ### Propagation 125 | 126 | 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. 127 | 128 | #### Rest Template / WebClient 129 | 130 | Simply create a `RestTemplate` or `WebClient` bean and Spring Cloud Sleuth will automatically add filters to propagate the trace context via HTTP headers. 131 | 132 | ```java 133 | @Bean 134 | RestTemplate restTemplate() { 135 | return new RestTemplate(); 136 | } 137 | ``` 138 | 139 | #### Messaging 140 | 141 | 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](../cloud-services/messaging/pubsub.md#spring-integration) will automatically add trace headers to the Pub/Sub message. 142 | 143 | #### Additional Headers 144 | 145 | 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. 146 | 147 | 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`. 148 | 149 | ```text 150 | spring.sleuth.propagation-keys=x-request-id,x-ot-span-context 151 | ``` 152 | 153 | ### Log / Trace Correlation 154 | 155 | 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](logging.md#log-trace-correlation) to achieve this. 156 | 157 | ![](https://lh3.googleusercontent.com/O6u214GgMO_GD-xNUkHVj8KTOBH6pf8-_SJP1x17QhdT9Fle3D30gjV-wuTOSSYDHWnjMqFyZmymAIroBTrxNRJGXrT6JqWRQYGVyZE0DMXRDCR4IkNxBCoAwKGnzyctcJMk7-PPBQ) 158 | 159 | ### Samples 160 | 161 | * [Spring Cloud GCP Trace sample](https://github.com/spring-cloud/spring-cloud-gcp/tree/master/spring-cloud-gcp-samples/spring-cloud-gcp-trace-sample) 162 | 163 | ## Istio 164 | 165 | 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](trace.md#additional-headers), without any trace senders: 166 | 167 | ```text 168 | spring.sleuth.propagation-keys=x-request-id,x-ot-span-context 169 | ``` 170 | 171 | For in-application trace, you can use [Spring Cloud GCP Trace starter](trace.md#spring-cloud-sleuth). 172 | 173 | ## Learn More 174 | 175 | * [Troubleshooting and Debugging Microservices in Kubernetes](https://saturnism.me/talk/troubleshooting-debugging-microservices/) 176 | 177 | -------------------------------------------------------------------------------- /app-dev/spring-cloud-gcp.md: -------------------------------------------------------------------------------- 1 | # Spring Cloud GCP 2 | 3 | Spring Cloud GCP contains a set of easy to use starters/autoconfigurations that allow you to easily connect and adopt GCP services. 4 | 5 | Spring Cloud GCP is part of the Spring Cloud release train. 6 | 7 | {% hint style="info" %} 8 | 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. 9 | {% endhint %} 10 | 11 | ## Demo 12 | 13 | {% embed url="https://www.youtube.com/watch?v=5d\_dy7RVcpE" caption="A short demo video of Spring Cloud GCP" %} 14 | 15 | ## Configure Spring Boot Project 16 | 17 | ### New Spring Boot Project 18 | 19 | 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). 20 | 21 | ![Add the GCP Support dependency](../.gitbook/assets/image%20%283%29.png) 22 | 23 | Or, generate the project using `curl`: 24 | 25 | ```bash 26 | curl https://start.spring.io/starter.zip \ 27 | -d dependencies=web,cloud-gcp,lombok \ 28 | -d bootVersion=2.3.1.RELEASE \ 29 | -d baseDir=demo 30 | ``` 31 | 32 | Unpack the downloaded zip file: 33 | 34 | ```bash 35 | unzip demo.zip 36 | ``` 37 | 38 | The generated project will include Spring Cloud release train BOM. 39 | 40 | ### Existing Spring Boot Project 41 | 42 | 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. 43 | 44 | | Spring Boot Version | Spring Cloud Version | 45 | | :--- | :--- | 46 | | 2.1 | Greenwich | 47 | | 2.2 | Hoxton | 48 | | 2.3 | Hoxton.SR5 | 49 | 50 | See [Spring Cloud documentation](https://spring.io/projects/spring-cloud#learn) for the latest versions. 51 | 52 | Add the compatible Spring Cloud BOM version to your project: 53 | 54 | {% tabs %} 55 | {% tab title="Maven" %} 56 | ```markup 57 | 58 | 59 | 60 | org.springframework.cloud 61 | spring-cloud-dependencies 62 | Hoxton.SR5 63 | pom 64 | import 65 | 66 | 67 | 68 | ``` 69 | {% endtab %} 70 | 71 | {% tab title="Gradle" %} 72 | ```groovy 73 | buildscript { 74 | dependencies { 75 | classpath "io.spring.gradle:dependency-management-plugin:1.0.2.RELEASE" 76 | } 77 | } 78 | 79 | apply plugin: "io.spring.dependency-management" 80 | 81 | dependencyManagement { 82 | imports { 83 | mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Hoxton.RELEASE' 84 | } 85 | } 86 | ``` 87 | {% endtab %} 88 | {% endtabs %} 89 | 90 | {% hint style="info" %} 91 | 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. 92 | {% endhint %} 93 | 94 | ## Services 95 | 96 | Spring Cloud GCP supports many GCP services using de-facto Spring abstraction layers. 97 | 98 | | Concerns | GCP Service | Spring Abstraction | 99 | | :--- | :--- | :--- | 100 | | **Databases** | Cloud SQL | JDBC template | 101 | | | | Spring Data JPA | 102 | | | Cloud Spanner | Spring Data Spanner | 103 | | | | Spring Data JPA with Hibernate | 104 | | | Cloud Datastore | Spring Data Datastore | 105 | | | Cloud Firestore | Spring Reactive Data Firestore | 106 | | **Messaging** | Cloud Pub/Sub | Pub/Sub Template | 107 | | | | Spring Integration | 108 | | | | Spring Cloud Stream | 109 | | | | Spring Dataflow | 110 | | **Configuration** | Cloud Secret Manager | Spring Cloud Config | 111 | | **Storage** | Cloud Storage | Spring Resource | 112 | | **Cache** | Cloud Memorystore | Spring Data Redis | 113 | | **Distributed Tracing** | Cloud Trace | Spring Cloud Sleuth | 114 | | | | Zipkin / Brave | 115 | | **Centralized Logging** | Cloud Logging | SLF4J / Logback | 116 | | **Monitoring Metrics** | Cloud Monitoring | Micrometer / Prometheus | 117 | | **Security** | Cloud Identity Aware Proxy | Spring Security | 118 | 119 | -------------------------------------------------------------------------------- /deployment/docker/README.md: -------------------------------------------------------------------------------- 1 | # Container 2 | 3 | Docker and container image are becoming the de-facto packaging format when it comes to deploying applications into a cluster of machines. 4 | 5 | If you are new to Docker and containers, you can learn more in this video: 6 | 7 | {% embed url="https://www.youtube.com/watch?v=pnOLWFBpb2A" %} 8 | 9 | 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](container-image.md) section for automated tools that can create optimized container images by default. 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /deployment/docker/attestation.md: -------------------------------------------------------------------------------- 1 | # Attestation 2 | 3 | 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. 4 | 5 | ## Enable API 6 | 7 | ```bash 8 | gcloud services enable container.googleapis.com 9 | gcloud services enable containeranalysis.googleapis.com 10 | gcloud services enable binaryauthorization.googleapis.com 11 | ``` 12 | 13 | ## Attestor 14 | 15 | 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. 16 | 17 | ### Create a Note 18 | 19 | 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. 20 | 21 | ```bash 22 | PROJECT_ID=$(gcloud config get-value project) 23 | cat > $HOME/attestor-note.json << EOF 24 | { 25 | "name": "projects/${PROJECT_ID}/notes/default-attestor", 26 | "attestation": { 27 | "hint": { 28 | "human_readable_name": "Default Container Image Attestor" 29 | } 30 | } 31 | } 32 | EOF 33 | ``` 34 | 35 | Post the Note to Container Analysis service: 36 | 37 | ```bash 38 | PROJECT_ID=$(gcloud config get-value project) 39 | curl -X POST \ 40 | -H "Content-Type: application/json" \ 41 | -H "Authorization: Bearer $(gcloud auth print-access-token)" \ 42 | -H "x-goog-user-project: $PROJECT_ID" \ 43 | --data-binary @$HOME/attestor-note.json\ 44 | https://containeranalysis.googleapis.com/v1/projects/$PROJECT_ID/notes/?noteId=default-attestor 45 | ``` 46 | 47 | ### Create an Attestor 48 | 49 | ```bash 50 | PROJECT_ID=$(gcloud config get-value project) 51 | 52 | gcloud beta container binauthz attestors create default-attestor \ 53 | --attestation-authority-note=default-attestor \ 54 | --attestation-authority-note-project=$PROJECT_ID 55 | ``` 56 | 57 | ## Asymetric Key Pair 58 | 59 | 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. 60 | 61 | ### Enable API 62 | 63 | ```bash 64 | gcloud services enable cloudkms.googleapis.com 65 | ``` 66 | 67 | ### Create a Keyring 68 | 69 | ```bash 70 | gcloud kms keyrings create attestor-keyring --location global 71 | ``` 72 | 73 | ### Create a Key 74 | 75 | ```bash 76 | gcloud kms keys create default-attestor-key \ 77 | --location=global \ 78 | --keyring=attestor-keyring \ 79 | --purpose=asymmetric-signing \ 80 | --default-algorithm=ec-sign-p256-sha256 81 | ``` 82 | 83 | ### Add Key to Attestor 84 | 85 | ```bash 86 | PROJECT_ID=$(gcloud config get-value project) 87 | 88 | gcloud alpha container binauthz attestors public-keys add \ 89 | --attestor=default-attestor \ 90 | --keyversion-project=$PROJECT_ID \ 91 | --keyversion-location=global \ 92 | --keyversion-keyring=attestor-keyring \ 93 | --keyversion-key=default-attestor-key \ 94 | --keyversion=1 95 | ``` 96 | 97 | ## Attestation 98 | 99 | 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: 100 | 101 | ```bash 102 | PROJECT_ID=$(gcloud config get-value project) 103 | 104 | gcloud container images describe gcr.io/$PROJECT_ID/helloworld 105 | ``` 106 | 107 | ### Create an Attestation 108 | 109 | ```bash 110 | PROJECT_ID=$(gcloud config get-value project) 111 | IMAGE=$(gcloud container images describe gcr.io/$PROJECT_ID/helloworld \ 112 | --format='value(image_summary.fully_qualified_digest)') 113 | 114 | gcloud beta container binauthz attestations sign-and-create \ 115 | --artifact-url=$IMAGE \ 116 | --attestor=default-attestor \ 117 | --attestor-project=$PROJECT_ID \ 118 | --keyversion-project=$PROJECT_ID \ 119 | --keyversion-location=global \ 120 | --keyversion-keyring=attestor-keyring \ 121 | --keyversion-key=default-attestor-key \ 122 | --keyversion=1 123 | ``` 124 | 125 | ### List Attestations 126 | 127 | Once created, you can see the attestation: 128 | 129 | ```bash 130 | PROJECT_ID=$(gcloud config get-value project) 131 | IMAGE=$(gcloud container images describe gcr.io/$PROJECT_ID/helloworld \ 132 | --format='value(image_summary.fully_qualified_digest)') 133 | 134 | gcloud beta container binauthz attestations list \ 135 | --artifact-url=$IMAGE \ 136 | --attestor=default-attestor 137 | ``` 138 | 139 | ## Binary Authorization 140 | 141 | 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. 142 | 143 | 1. [Create a Kubernetes Engine cluster](../kubernetes/kubernetes-cluster.md#create-cluster) that has Binary Authorization enabled. 144 | 2. [Enable Binary Authorization](../kubernetes/binary-authorization.md#enforce-attestation) policy to enforce attestations. 145 | 146 | {% hint style="info" %} 147 | See [Binary Authorization](../kubernetes/binary-authorization.md) section for more information. 148 | {% endhint %} 149 | 150 | -------------------------------------------------------------------------------- /deployment/docker/container-awareness.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Learn the intricate details of how JVM applications see container resources 4 | and how it impacts heap, CPU, and threads. 5 | --- 6 | 7 | # Container Awareness 8 | 9 | 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. 10 | 11 | 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\). 12 | 13 | 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`. 14 | 15 | ## Heap 16 | 17 | 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. 18 | 19 | ```bash 20 | docker run -ti --rm --cpus=1 --memory=256M openjdk:8u141-jre \ 21 | java -XX:+PrintFlagsFinal -version | grep MaxHeapSize 22 | ``` 23 | 24 | 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. 25 | 26 | Run the same command, but with a newer version of JDK: 27 | 28 | ```bash 29 | docker run -ti --rm --cpus=1 --memory=256M openjdk:8u252-jre \ 30 | java -XX:+PrintFlagsFinal -version | grep MaxHeapSize 31 | ``` 32 | 33 | 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. 34 | 35 | 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. 36 | 37 | JVM native memory usage contains thread stack, code cache, metaspace, and potentially direct memory buffer allocations. 38 | 39 | #### Estimate Memory Needs 40 | 41 | 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. 42 | 43 | You can use [Cloud Foundry Java Buildpack Memory calculator](https://github.com/cloudfoundry/java-buildpack-memory-calculator) to the memory needs and configurations. 44 | 45 | #### Understand Memory Used 46 | 47 | 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). 48 | 49 | Native Memory Tracking can only be enabled via command line argument, and cannot be enabled using `JAVA_TOOL_OPTIONS`. 50 | 51 | You can run this command to see a sample output of Native Memory Tracking: 52 | 53 | ```bash 54 | docker run -ti --rm openjdk:8u252-jre \ 55 | java -XX:+UnlockDiagnosticVMOptions \ 56 | -XX:NativeMemoryTracking=summary \ 57 | -XX:+PrintNMTStatistics \ 58 | -version 59 | ``` 60 | 61 | Native Memory Tracking can only print out memory usage details upon a **successful** exit. 62 | 63 | ```bash 64 | java -XX:+UnlockDiagnosticVMOptions \ 65 | -XX:NativeMemoryTracking=summary \ 66 | -XX:+PrintNMTStatistics \ 67 | -jar ... 68 | ``` 69 | 70 | 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. 71 | 72 | ## CPU 73 | 74 | 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. 75 | 76 | ```bash 77 | docker run -ti --rm --cpus=2 openjdk:8u141-jre java \ 78 | -XX:+PrintFlagsFinal -XX:+UseParallelGC -version | grep ParallelGCThreads 79 | ``` 80 | 81 | It will output the `ParallelGCThreads` that is calculated from the number of CPUs of the host machine and can be significantly higher than `2`. 82 | 83 | Run the same command, but with a newer version of JDK: 84 | 85 | ```bash 86 | docker run -ti --rm --cpus=2 --memory=256M openjdk:8u252-jre java \ 87 | -XX:+PrintFlagsFinal -XX:+UseParallelGC -version | grep ParallelGCThreads 88 | ``` 89 | 90 | The output of `ParallelGCThreads` is `2`. 91 | 92 | ## Runtime API 93 | 94 | 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. 95 | 96 | ```java 97 | // Max heap you can use 98 | Runtime.getRuntime().maxMemory() 99 | 100 | // Number of processors 101 | Runtime.getRuntime().availableProcessors() 102 | ``` 103 | 104 | 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. 105 | 106 | -------------------------------------------------------------------------------- /deployment/docker/secure-container-image.md: -------------------------------------------------------------------------------- 1 | # Secure Container Image 2 | 3 | While [Creating a new container image](container-image.md#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. 4 | 5 | ## No Source Code 6 | 7 | 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: 8 | 9 | * Source code, build files are easily copied into a runtime container image by accident when using a Dockerfile. 10 | * Version control directories, such as `.git` are easily copied into a runtime container image by accident when using a Dockerfile. 11 | 12 | {% tabs %} 13 | {% tab title="Jib" %} 14 | Jib automatically builds thin container images without the source. 15 | {% endtab %} 16 | 17 | {% tab title="Buildpacks" %} 18 | Paketo automatically builds thin container images without the source. 19 | 20 | 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. 21 | 22 | ```bash 23 | PROJECT_ID=$(gcloud config get-value project) ⬢ system ⎈ demo-cluster 24 | pack build \ 25 | -e "GOOGLE_CLEAR_SOURCE=true" 26 | --builder gcr.io/buildpacks/builder:v1 \ 27 | --publish \ 28 | gcr.io/${PROJECT_ID}/helloworld 29 | ``` 30 | {% endtab %} 31 | {% endtabs %} 32 | 33 | ## No Secrets/Credentials 34 | 35 | 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.md), or HashiCorp Vault\). 36 | 37 | ## Minimal Base Image 38 | 39 | 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. 40 | 41 | {% tabs %} 42 | {% tab title="Jib" %} 43 | Jib uses the [Distroless](https://github.com/GoogleContainerTools/distroless/blob/master/java/README.md) base image by default. 44 | {% endtab %} 45 | 46 | {% tab title="Buildpacks" %} 47 | 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. 48 | {% endtab %} 49 | {% endtabs %} 50 | 51 | ## Non-Root User 52 | 53 | 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. 54 | 55 | For example, for a Distroless base image \(using a debug image that has a shell\): 56 | 57 | ```bash 58 | docker run -ti --rm --entrypoint=sh \ 59 | gcr.io/distroless/java:debug -c "cat /etc/passwd" 60 | ``` 61 | 62 | You'll see that it has only 3 users: 63 | 64 | ```text 65 | root:x:0:0:root:/root:/sbin/nologin 66 | nobody:x:65534:65534:nobody:/nonexistent:/sbin/nologin 67 | nonroot:x:65532:65532:nonroot:/home/nonroot:/sbin/nologin 68 | ``` 69 | 70 | 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: 71 | 72 | ```bash 73 | docker run -ti --rm adoptopenjdk:11-jre-hotspot-bionic cat /etc/passwd 74 | ``` 75 | 76 | {% tabs %} 77 | {% tab title="Jib" %} 78 | 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: 79 | 80 | ```bash 81 | PROJECT_ID=$(gcloud config get-value project) 82 | ./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \ 83 | -Djib.container.user=nonroot:nonroot 84 | -Dimage=gcr.io/${PROJECT_ID}/helloworld 85 | ``` 86 | 87 | Validate that the JVM was started with the `nonroot` user: 88 | 89 | ```bash 90 | PROJECT_ID=$(gcloud config get-value project) 91 | 92 | docker pull gcr.io/${PROJECT_ID}/helloworld 93 | docker run -ti --rm --entrypoint=java \ 94 | gcr.io/${PROJECT_ID}/helloworld \ 95 | -XshowSettings:properties -version 96 | ``` 97 | 98 | Look for the `user.name` property is now `nonroot`. 99 | {% endtab %} 100 | 101 | {% tab title="Buildpacks" %} 102 | Buildpacks \(with Paketo and GCP builders\) run as a non-root user by default, as the user `cnb`. 103 | {% endtab %} 104 | {% endtabs %} 105 | 106 | ## Summary 107 | 108 | So, what do the automated tools do by default? 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 142 | 143 | 144 | 145 | 146 |
JibPaketo BuilderGCP Builder
Source CodeNo source in runtimeNo source in runtime 125 |

Set GOOGLE_CLEAR_SOURCE. 126 |

127 |

See README.

128 |
Minimal Base ImageUses DistrolessNot DistrolessNot Distroless
Non-Root User 139 |

Defaults to root,

140 |

Configure to non-root.

141 |
cnb usercnb user
147 | 148 | -------------------------------------------------------------------------------- /deployment/docker/vulnerability-scanning.md: -------------------------------------------------------------------------------- 1 | # Vulnerability Scanning 2 | 3 | [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. 4 | 5 | Container images are scanned upon push to Container Registry, and then continuously monitored/scanned if the image was pulled in the last 30 days. 6 | 7 | ## Enable API 8 | 9 | ```bash 10 | gcloud services enable containeranalysis.googleapis.com 11 | gcloud services enable containerscanning.googleapis.com 12 | ``` 13 | 14 | ## Push an Image 15 | 16 | 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](container-image.md), and re-push the Hello World container image. 17 | 18 | ```bash 19 | PROJECT_ID=$(gcloud config get-value project) 20 | 21 | ./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \ 22 | -Dimage=gcr.io/${PROJECT_ID}/helloworld 23 | ``` 24 | 25 | ## Vulnerabilities 26 | 27 | Once the image is scanned, you can see the status of Vulnerability Scanning in Container Registry. 28 | 29 | ```bash 30 | PROJECT_ID=$(gcloud config get-value project) 31 | 32 | open https://gcr.io/$PROJECT_ID/helloworld 33 | ``` 34 | 35 | On the right hand side, see the **Vulnerabilities** column: 36 | 37 | ![](../../.gitbook/assets/image%20%2839%29.png) 38 | 39 | Click into **View vulnerabilities** to see the details: 40 | 41 | ![](../../.gitbook/assets/image%20%2840%29.png) 42 | 43 | You can list vulnerabilities for a specific container image. It'll be outputted in the raw YAML format: 44 | 45 | ```bash 46 | PROJECT_ID=$(gcloud config get-value project) 47 | 48 | gcloud beta container images describe gcr.io/$PROJECT_ID/helloworld \ 49 | --show-package-vulnerability 50 | ``` 51 | 52 | {% hint style="info" %} 53 | See [Vulnerability Scanning documentation](https://cloud.google.com/container-registry/docs/vulnerability-scanning) for more information on vulnerability database sources. 54 | {% endhint %} 55 | 56 | ## Continuous Scan 57 | 58 | Container images are scanned upon push to Container Registry, and then continuously monitored/scanned if the image was pulled in the last 30 days. 59 | 60 | {% hint style="info" %} 61 | See [Vulnerability Scanning documentation](https://cloud.google.com/container-registry/docs/vulnerability-scanning) for more information. 62 | {% endhint %} 63 | 64 | -------------------------------------------------------------------------------- /deployment/istio.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Istio is a service mesh that can manage your service to service communication 4 | policies. 5 | --- 6 | 7 | # Istio 8 | 9 | If you are unfamiliar with Istio, you can see a quick introduction in the following video: 10 | 11 | {% embed url="https://www.youtube.com/watch?v=AGztKw580yQ" %} 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /deployment/istio/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Istio is a service mesh that can manage your service to service communication 4 | policies. 5 | --- 6 | 7 | # Istio 8 | 9 | If you are unfamiliar with Istio, you can see a quick introduction in the following video: 10 | 11 | {% embed url="https://www.youtube.com/watch?v=AGztKw580yQ" %} 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /deployment/istio/getting-started.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | This section requires basic understanding of Kubernetes - make sure you do the tutorial in sequence. 4 | 5 | {% page-ref page="../kubernetes/" %} 6 | 7 | ## Install Istio 8 | 9 | First, make sure you already have a [Kubernetes cluster](../kubernetes/kubernetes-cluster.md) up and running. 10 | 11 | ### Install istioctl 12 | 13 | You need to get the `istioctl` CLI to install Istio into the cluster. 14 | 15 | ```bash 16 | cd $HOME 17 | 18 | # Specify an Istio version to install 19 | curl -L https://istio.io/downloadIstio | \ 20 | ISTIO_VERSION=1.7.4 sh - 21 | ``` 22 | 23 | Add Istio's `bin` path to shell's `PATH.` 24 | 25 | ```bash 26 | echo 'export PATH="$PATH:$HOME/istio-1.7.4/bin"' >> ~/.bash_profile 27 | 28 | source $HOME/.bash_profile 29 | ``` 30 | 31 | Verify `istioctl` is installed properly and with the correct version: 32 | 33 | ```bash 34 | istioctl version 35 | ``` 36 | 37 | ### Install Istio 38 | 39 | 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\). 40 | 41 | ```bash 42 | istioctl install \ 43 | --set profile=demo \ 44 | --set values.cni.cniBinDir=/home/kubernetes/bin \ 45 | --set components.cni.enabled=true \ 46 | --set components.cni.namespace=kube-system 47 | ``` 48 | 49 | Validate that Istio is installed. `istioctl version` should show you the `control plane version`. 50 | 51 | ```bash 52 | istioctl version 53 | ``` 54 | 55 | In addition, Istio is installed into the `istio-system` namespace. 56 | 57 | ```bash 58 | kubectl get ns 59 | 60 | kubectl -n=istio-system get pods 61 | ``` 62 | 63 | ## Install Addons 64 | 65 | In addition to core-Istio, you can install addons for observability, for example to see distributed traces and out-of-the-box metrics/dashboard. 66 | 67 | ```bash 68 | # Zipkin 69 | kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/extras/zipkin.yaml 70 | 71 | # Prometheus 72 | kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/prometheus.yaml 73 | 74 | # Grafana 75 | kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/grafana.yaml 76 | 77 | # Wait for Grafana a bit before Kiali 78 | sleep 20 79 | 80 | # Kiali 81 | kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.7/samples/addons/kiali.yaml 82 | ``` 83 | 84 | -------------------------------------------------------------------------------- /deployment/istio/istio-sidecar-proxy.md: -------------------------------------------------------------------------------- 1 | # Istio Sidecar Proxy 2 | 3 | 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: 4 | 5 | 1. Automatic sidecar injection 6 | 2. Manual sidecar injection 7 | 8 | ## Automatic Sidecar Injection 9 | 10 | 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. 11 | 12 | ```bash 13 | kubectl label namespace default istio-injection=enabled 14 | ``` 15 | 16 | Deploy a workload, such as the Helloworld application from the [Kubernetes Deployment](../kubernetes/deployment.md#deployment-yaml) section. 17 | 18 | ```bash 19 | kubectl apply -f k8s/deployment.yaml 20 | ``` 21 | 22 | Verify that the Helloworld pod has 2 containers rather than only 1: 23 | 24 | ```bash 25 | kubectl get pods 26 | ``` 27 | 28 | 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: 29 | 30 | ```bash 31 | POD_NAME=$(kubectl get pods -lapp=helloworld -o jsonpath='{.items[0].metadata.name}') 32 | 33 | kubectl logs ${POD_NAME} -c helloworld 34 | kubectl logs ${POD_NAME} -c istio-proxy 35 | ``` 36 | 37 | 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: 38 | 39 | ```yaml 40 | apiVersion: apps/v1 41 | kind: Deployment 42 | metadata: 43 | name: helloworld 44 | ... 45 | spec: 46 | template: 47 | metadata: 48 | labels: 49 | ... 50 | annotations: 51 | # Explicitly turn off automatic sidecar injection 52 | sidecar.istio.io/inject: "false" 53 | spec: 54 | ... 55 | 56 | ``` 57 | 58 | ## Manual Sidecar Injection 59 | 60 | You can use `istioctl` to filter your existing Kubernetes deployment file and it'll produce the enhanced deployment manifest. 61 | 62 | ```bash 63 | istioctl kube-inject -f k8s/deployment.yaml 64 | ``` 65 | 66 | In addition to your original manifest, the enhanced manifest now has an additional `istio-proxy`container. 67 | 68 | You can save the enhanced manifest into a file for future deployments. Or, you can filter and apply in one command: 69 | 70 | ```bash 71 | istioctl kube-inject -f k8s/deployment.yaml| kubectl apply -f 72 | ``` 73 | 74 | In most cases, Automatic Sidecar Injection is what you need. 75 | 76 | -------------------------------------------------------------------------------- /deployment/istio/sidecar-proxy.md: -------------------------------------------------------------------------------- 1 | # Sidecar Proxy 2 | 3 | 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: 4 | 5 | 1. Automatic sidecar injection 6 | 2. Manual sidecar injection 7 | 8 | ## Automatic Sidecar Injection 9 | 10 | 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. 11 | 12 | ```bash 13 | kubectl label namespace default istio-injection=enabled 14 | ``` 15 | 16 | Deploy a workload, such as the Helloworld application from the [Kubernetes Deployment](../kubernetes/deployment.md#deployment-yaml) section. 17 | 18 | ```bash 19 | kubectl apply -f k8s/deployment.yaml 20 | ``` 21 | 22 | Verify that the Helloworld pod has 2 containers rather than only 1: 23 | 24 | ```bash 25 | kubectl get pods 26 | ``` 27 | 28 | 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: 29 | 30 | ```bash 31 | POD_NAME=$(kubectl get pods -lapp=helloworld -o jsonpath='{.items[0].metadata.name}') 32 | 33 | kubectl logs ${POD_NAME} -c helloworld 34 | kubectl logs ${POD_NAME} -c istio-proxy 35 | ``` 36 | 37 | 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: 38 | 39 | ```yaml 40 | apiVersion: apps/v1 41 | kind: Deployment 42 | metadata: 43 | name: helloworld 44 | ... 45 | spec: 46 | template: 47 | metadata: 48 | labels: 49 | ... 50 | annotations: 51 | # Explicitly turn off automatic sidecar injection 52 | sidecar.istio.io/inject: "false" 53 | spec: 54 | ... 55 | 56 | ``` 57 | 58 | ## Manual Sidecar Injection 59 | 60 | You can use `istioctl` to filter your existing Kubernetes deployment file and it'll produce the enhanced deployment manifest. 61 | 62 | ```bash 63 | istioctl kube-inject -f k8s/deployment.yaml 64 | ``` 65 | 66 | In addition to your original manifest, the enhanced manifest now has an additional `istio-proxy`container. 67 | 68 | You can save the enhanced manifest into a file for future deployments. Or, you can filter and apply in one command: 69 | 70 | ```bash 71 | istioctl kube-inject -f k8s/deployment.yaml| kubectl apply -f 72 | ``` 73 | 74 | In most cases, Automatic Sidecar Injection is what you need. 75 | 76 | -------------------------------------------------------------------------------- /deployment/kubernetes/README.md: -------------------------------------------------------------------------------- 1 | # Kubernetes 2 | 3 | Kubernetes is becoming the de-facto cluster/container orchestration system. Learn how you can build and deploy Java applications with Kubernetes. 4 | 5 | If you are unfamiliar with Kubernetes, you can learn more in this video: 6 | 7 | {% embed url="https://www.youtube.com/watch?v=kT1vmK0r184" %} 8 | 9 | -------------------------------------------------------------------------------- /deployment/kubernetes/binary-authorization.md: -------------------------------------------------------------------------------- 1 | # Binary Authorization 2 | 3 | This section continues from the previous section - make sure you do the tutorial in sequence. 4 | 5 | {% page-ref page="deployment.md" %} 6 | 7 | {% page-ref page="../docker/attestation.md" %} 8 | 9 | ## Enforce Attestation 10 | 11 | Binary Authorization allows you to enforce container image attestation, so that only attested container images can run. 12 | 13 | Before you can turn this on, you must have [attested a container image](../docker/attestation.md). 14 | 15 | ### Enable Policy 16 | 17 | First, export the existing Binary Authorization policy: 18 | 19 | ```bash 20 | gcloud container binauthz policy export > $HOME/binauthz-policy.yaml 21 | ``` 22 | 23 | Edit the `binauthz-policy.yaml` and enable attestation policy: 24 | 25 | {% code title="binauthz-policy.yaml" %} 26 | ```yaml 27 | admissionWhitelistPatterns: 28 | - namePattern: gcr.io/google_containers/* 29 | - namePattern: gcr.io/google-containers/* 30 | - namePattern: k8s.gcr.io/* 31 | - namePattern: gke.gcr.io/* 32 | - namePattern: gcr.io/stackdriver-agents/* 33 | defaultAdmissionRule: 34 | enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG 35 | # Change evaluationMode to require attestation 36 | evaluationMode: REQUIRE_ATTESTATION 37 | # Add the policy, and reference the `default-attestor` created from 38 | # Attestation section. 39 | # Replace PROJECT_ID with your Project ID. 40 | requireAttestationsBy: 41 | - projects/PROJECT_ID/attestors/default-attestor 42 | globalPolicyEvaluationMode: ENABLE 43 | name: projects/PROJECT_ID/policy 44 | ``` 45 | {% endcode %} 46 | 47 | Import the Policy File: 48 | 49 | ```bash 50 | gcloud container binauthz policy import $HOME/binauthz-policy.yaml 51 | ``` 52 | 53 | ## Unattested Container Image 54 | 55 | You can verify that the policy is being enforced by deploying an unattested container image: 56 | 57 | ```bash 58 | kubectl create deployment unattested-nginx --image=nginx 59 | ``` 60 | 61 | While this should have created a new deployment for `nginx` and running a Pod, you can validate that no Pod is running: 62 | 63 | ```bash 64 | kubectl get pods -lapp=unattested-nginx 65 | ``` 66 | 67 | In addition, you can verify the Kubernetes events stream: 68 | 69 | ```bash 70 | kubectl get event 71 | ``` 72 | 73 | Observe the event where the container image was denied by the attestor: 74 | 75 | ```text 76 | ... Error creating: pods "..." is forbidden: image policy webhook backend denied one or more images: Denied by default admission rule. Denied by Attestor. ... 77 | ``` 78 | 79 | Delete the deployment: 80 | 81 | ```bash 82 | kubectl delete deployment unattested-nginx 83 | ``` 84 | 85 | ## Attested Container Image 86 | 87 | Deploy a [previously attested container image](../docker/attestation.md#create-an-attestation) from the [Container Image Attestation](../docker/attestation.md) section. 88 | 89 | ```bash 90 | PROJECT_ID=$(gcloud config get-value project) 91 | IMAGE=$(gcloud container images describe gcr.io/$PROJECT_ID/helloworld \ 92 | --format='value(image_summary.fully_qualified_digest)') 93 | 94 | kubectl create deployment attested-helloworld --image=$IMAGE 95 | ``` 96 | 97 | Verify that the Pod is up and running: 98 | 99 | ```bash 100 | kubectl get pods -lapp=attested-helloworld 101 | ``` 102 | 103 | ## Allow List 104 | 105 | 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. 106 | 107 | For example, to be able to deploy the `nginx` container image from Dockerhub without attestation, you need to add it to the policy. 108 | 109 | First, export the existing Binary Authorization policy: 110 | 111 | ```bash 112 | gcloud container binauthz policy export > $HOME/binauthz-policy.yaml 113 | ``` 114 | 115 | Edit the `binauthz-policy.yaml` and enable attestation policy: 116 | 117 | {% code title="binauthz-policy.yaml" %} 118 | ```yaml 119 | admissionWhitelistPatterns: 120 | - namePattern: gcr.io/google_containers/* 121 | - namePattern: gcr.io/google-containers/* 122 | - namePattern: k8s.gcr.io/* 123 | - namePattern: gke.gcr.io/* 124 | - namePattern: gcr.io/stackdriver-agents/* 125 | # Add nginx to the allow list 126 | - namePattern: nginx 127 | defaultAdmissionRule: 128 | enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG 129 | evaluationMode: REQUIRE_ATTESTATION 130 | requireAttestationsBy: 131 | - projects/PROJECT_ID/attestors/default-attestor 132 | globalPolicyEvaluationMode: ENABLE 133 | name: projects/PROJECT_ID/policy 134 | ``` 135 | {% endcode %} 136 | 137 | Import the Policy File: 138 | 139 | ```bash 140 | gcloud container binauthz policy import $HOME/binauthz-policy.yaml 141 | ``` 142 | 143 | Deploy `nginx` again: 144 | 145 | ```bash 146 | kubectl create deployment unattested-nginx --image=nginx 147 | ``` 148 | 149 | Verify that the Pod is up and running due to the allow list: 150 | 151 | ```bash 152 | kubectl get pods -lapp=unattested-nginx 153 | ``` 154 | 155 | If you trust every container image from a particular Project: 156 | 157 | {% code title="binauthz-policy.yaml" %} 158 | ```yaml 159 | admissionWhitelistPatterns: 160 | - namePattern: gcr.io/google_containers/* 161 | - namePattern: gcr.io/google-containers/* 162 | - namePattern: k8s.gcr.io/* 163 | - namePattern: gke.gcr.io/* 164 | - namePattern: gcr.io/stackdriver-agents/* 165 | # Add the container registry from a project to the allow list. 166 | # Replace PROJECT_ID. 167 | - namePattern: gcr.io/PROJECT_ID/* 168 | defaultAdmissionRule: 169 | enforcementMode: ENFORCED_BLOCK_AND_AUDIT_LOG 170 | evaluationMode: REQUIRE_ATTESTATION 171 | requireAttestationsBy: 172 | - projects/PROJECT_ID/attestors/default-attestor 173 | globalPolicyEvaluationMode: ENABLE 174 | name: projects/PROJECT_ID/policy 175 | ``` 176 | {% endcode %} 177 | 178 | -------------------------------------------------------------------------------- /deployment/kubernetes/health-checks.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Learn how Kubernets checks the application health, and how to use liveness 4 | probes and readiness probes. 5 | --- 6 | 7 | # Health Checks 8 | 9 | This section continues from the previous section - make sure you do the tutorial in sequence. 10 | 11 | {% page-ref page="service.md" %} 12 | 13 | ## Spring Boot Actuator 14 | 15 | [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. 16 | 17 | 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). 18 | 19 | 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. 20 | 21 | | Spring Boot Version | Liveness Probe | Readiness Probe | 22 | | :--- | :--- | :--- | 23 | | >= 2.3 | /actuator/health/liveness | /actuator/health/readiness | 24 | | < 2.3 | Any endpoint that simply returns `200` | /actuator/health | 25 | 26 | ### Clone 27 | 28 | ```bash 29 | git clone https://github.com/saturnism/jvm-helloworld-by-example 30 | cd jvm-helloworld-by-example/helloworld-springboot-tomcat 31 | ``` 32 | 33 | ### Add Dependencies 34 | 35 | ```markup 36 | 37 | ... 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-actuator 42 | 43 | 44 | ... 45 | 46 | ... 47 | 48 | ``` 49 | 50 | ### Build 51 | 52 | ```text 53 | ./mvnw package 54 | ``` 55 | 56 | ### Containerize 57 | 58 | Use Jib to containerize the application: 59 | 60 | ```bash 61 | PROJECT_ID=$(gcloud config get-value project) 62 | 63 | ./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \ 64 | -Dimage=gcr.io/${PROJECT_ID}/helloworld 65 | ``` 66 | 67 | {% hint style="info" %} 68 | Learn different ways to containerize a Java application in the [Container Image](../docker/container-image.md) section. 69 | {% endhint %} 70 | 71 | ## Liveness Probe 72 | 73 | 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. 74 | 75 | {% hint style="info" %} 76 | Liveness Probe failure indicates to Kubernetes that the failure can be recovered after a restart. 77 | {% endhint %} 78 | 79 | {% hint style="danger" %} 80 | 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. 81 | {% endhint %} 82 | 83 | {% code title="k8s/deployment.yaml" %} 84 | ```yaml 85 | apiVersion: apps/v1 86 | kind: Deployment 87 | metadata: 88 | labels: 89 | app: helloworld 90 | name: helloworld 91 | spec: 92 | replicas: 1 93 | selector: 94 | matchLabels: 95 | app: helloworld 96 | template: 97 | metadata: 98 | labels: 99 | app: helloworld 100 | spec: 101 | containers: 102 | - image: gcr.io/.../helloworld 103 | name: helloworld 104 | resources: 105 | requests: 106 | cpu: 200m 107 | memory: 256Mi 108 | limits: 109 | cpu: 500m 110 | memory: 256Mi 111 | # Configure the liveness probe 112 | livenessProbe: 113 | httpGet: 114 | path: /actuator/health/liveness 115 | port: 8080 116 | initialDelaySeconds: 10 117 | ``` 118 | {% endcode %} 119 | 120 | {% hint style="info" %} 121 | 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. 122 | {% endhint %} 123 | 124 | {% hint style="info" %} 125 | 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. 126 | {% endhint %} 127 | 128 | {% hint style="success" %} 129 | When configuring a `livenessProbe`, always consider the initial delay needed for your application. 130 | {% endhint %} 131 | 132 | ## Readiness Probe 133 | 134 | 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\). 135 | 136 | {% code title="k8s/deployment.yaml" %} 137 | ```yaml 138 | apiVersion: apps/v1 139 | kind: Deployment 140 | metadata: 141 | labels: 142 | app: helloworld 143 | name: helloworld 144 | spec: 145 | replicas: 1 146 | selector: 147 | matchLabels: 148 | app: helloworld 149 | template: 150 | metadata: 151 | labels: 152 | app: helloworld 153 | spec: 154 | containers: 155 | - image: gcr.io/.../helloworld 156 | name: helloworld 157 | resources: 158 | requests: 159 | cpu: 200m 160 | memory: 256Mi 161 | limits: 162 | cpu: 500m 163 | memory: 256Mi 164 | livenessProbe: 165 | httpGet: 166 | path: /actuator/health/liveness 167 | port: 8080 168 | # Configure the readiness probe 169 | readinessProbe: 170 | httpGet: 171 | path: /actuator/health/readiness 172 | port: 8080 173 | ``` 174 | {% endcode %} 175 | 176 | {% hint style="success" %} 177 | 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. 178 | {% endhint %} 179 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /deployment/kubernetes/kubernetes-cluster.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Learn how to create a production-grade Kubernetes cluster to deploy your 4 | application. 5 | --- 6 | 7 | # Kubernetes Cluster 8 | 9 | This section requires basic understanding of Docker and container images - make sure you do the tutorial in sequence. 10 | 11 | {% page-ref page="../docker/container-image.md" %} 12 | 13 | ## Enable API 14 | 15 | ```bash 16 | gcloud services enable compute.googleapis.com 17 | gcloud services enable container.googleapis.com 18 | ``` 19 | 20 | ## Create Cluster 21 | 22 | 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: 23 | 24 | | Feature | Description | 25 | | :--- | :--- | 26 | | [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. | 27 | | [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. | 28 | | [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. | 29 | | [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. | 30 | | [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. | 31 | | [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. | 32 | | [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. | 33 | | [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. | 34 | | [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. | 35 | 36 | ```bash 37 | PROJECT_ID=$(gcloud config get-value project) 38 | gcloud container clusters create demo-cluster \ 39 | --num-nodes 4 \ 40 | --machine-type n1-standard-4 \ 41 | --network=default \ 42 | --workload-pool=${PROJECT_ID}.svc.id.goog \ 43 | --enable-ip-alias \ 44 | --enable-network-policy \ 45 | --enable-stackdriver-kubernetes \ 46 | --enable-binauthz \ 47 | --enable-shielded-nodes \ 48 | --shielded-secure-boot \ 49 | --enable-autorepair \ 50 | --enable-autoupgrade \ 51 | --scopes=cloud-platform 52 | ``` 53 | 54 | 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). 55 | 56 | ## Credentials 57 | 58 | Kubernetes credentials are automatically retrieved and stored in your `$HOME/.kube/config` file. If you need to re-retrieve the credential: 59 | 60 | ```bash 61 | gcloud container clusters get-credentials demo-cluster 62 | ``` 63 | 64 | ## Node Pool and Nodes 65 | 66 | 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. 67 | 68 | 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\). 69 | 70 | {% hint style="info" %} 71 | You can add different machine types to your Kubernetes Engine cluster, by creating a new Node Pool with the configuration you want. 72 | {% endhint %} 73 | 74 | You can see a list of Virtual Machines using `gcloud`: 75 | 76 | ```bash 77 | gcloud compute instances list 78 | ``` 79 | 80 | You can also use `kubectl` to list the nodes that belong to the current cluster: 81 | 82 | ```bash 83 | kubectl get nodes 84 | ``` 85 | 86 | You can also SSH into the node directly if needed, by specifying the name of the node: 87 | 88 | ```bash 89 | gcloud compute ssh gke-demo-cluster-default-pool-... 90 | ``` 91 | 92 | Once you are in the Compute Engine Virtual Machine, you can also see the containers that are running inside of the Node: 93 | 94 | ```bash 95 | docker ps 96 | exit 97 | ``` 98 | 99 | -------------------------------------------------------------------------------- /deployment/kubernetes/load-balancing/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Learn the different ways to load balance traffic both for both external and 4 | internal consumers. 5 | --- 6 | 7 | # Load Balancing 8 | 9 | -------------------------------------------------------------------------------- /deployment/kubernetes/load-balancing/internal-load-balancing.md: -------------------------------------------------------------------------------- 1 | # Internal Load Balancing 2 | 3 | This section continues from the previous section - make sure you do the tutorial in sequence. 4 | 5 | {% page-ref page="../service.md" %} 6 | 7 | ## In-Cluster Load Balancer 8 | 9 | A [Kubernetes Service](../service.md#service) acts as an internal L4 load balancer only accessible from within the same Kubernetes Cluster. See the [Service section](../service.md#service) for more information. 10 | 11 | ## Internal Network Load Balancer 12 | 13 | The setup of the Internal Network Load Balancer is similar to the [External Network Load Balancer](external-load-balancing.md#external-network-load-balancer), but with an additional annotation. 14 | 15 | ### Service YAML 16 | 17 | In `k8s/service.yaml`, use the `cloud.google.com/load-balancer-type` annotation to mark the service to use the Internal Network Load Balancer: 18 | 19 | {% code title="k8s/service.yaml" %} 20 | ```yaml 21 | apiVersion: v1 22 | kind: Service 23 | metadata: 24 | name: helloworld 25 | annotations: 26 | # Indicate this is an Internal Network Load Balancer 27 | cloud.google.com/load-balancer-type: "Internal" 28 | labels: 29 | app: helloworld 30 | spec: 31 | ports: 32 | - name: 8080-8080 33 | port: 8080 34 | protocol: TCP 35 | targetPort: 8080 36 | selector: 37 | app: helloworld 38 | # Use LoadBalancer type instead of ClusterIP 39 | type: LoadBalancer 40 | ``` 41 | {% endcode %} 42 | 43 | ### Internal Static IP 44 | 45 | You can assign an internal static IP address to the Network Load Balancer. 46 | 47 | Reserve a regional static IP address: 48 | 49 | ```bash 50 | REGION=$(gcloud config get-value compute/region) 51 | 52 | gcloud compute addresses create helloworld-service-internal-ip \ 53 | --subnet=default --region=${REGION} 54 | ``` 55 | 56 | See the reserved IP address: 57 | 58 | ```bash 59 | REGION=$(gcloud config get-value compute/region) 60 | 61 | gcloud compute addresses describe helloworld-service-internal-ip \ 62 | --region=${REGION} --format='value(address)' 63 | ``` 64 | 65 | Update the `k8s/service.yaml` to pin the Load Balancer IP address: 66 | 67 | {% code title="k8s/service.yaml" %} 68 | ```yaml 69 | apiVersion: v1 70 | kind: Service 71 | metadata: 72 | name: helloworld 73 | annotations: 74 | cloud.google.com/load-balancer-type: "Internal" 75 | labels: 76 | app: helloworld 77 | spec: 78 | ports: 79 | - name: 8080-8080 80 | port: 8080 81 | protocol: TCP 82 | targetPort: 8080 83 | selector: 84 | app: helloworld 85 | type: LoadBalancer 86 | # Replace the value with the IP address you reserved 87 | loadBalancerIP: RESERVED_IP_ADDRESS 88 | ``` 89 | {% endcode %} 90 | 91 | ## Internal HTTP\(s\) Load Balancer 92 | 93 | The setup of the Internal Network Load Balancer is similar to the [External HTTP\(s\) Load Balancer](external-load-balancing.md#external-http-load-balancer), but with an additional annotation. 94 | 95 | ### Service YAML 96 | 97 | 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: 98 | 99 | {% code title="k8s/service.yaml" %} 100 | ```yaml 101 | apiVersion: v1 102 | kind: Service 103 | metadata: 104 | name: helloworld 105 | labels: 106 | app: helloworld 107 | # Add the NEG annotation to enable Network Endpoint Group 108 | # in order to use container-native load balancing 109 | annotations: 110 | cloud.google.com/neg: '{"ingress": true}' 111 | spec: 112 | ports: 113 | - name: 8080-8080 114 | port: 8080 115 | protocol: TCP 116 | targetPort: 8080 117 | selector: 118 | app: helloworld 119 | type: ClusterIP 120 | ``` 121 | {% endcode %} 122 | 123 | ### Ingress YAML 124 | 125 | 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 126 | 127 | {% code title="k8s/ingress.yaml" %} 128 | ```yaml 129 | apiVersion: networking.k8s.io/v1beta1 130 | kind: Ingress 131 | metadata: 132 | name: helloworld 133 | annotations: 134 | # Add the Ingress Class annotation to use Internal HTTP(s) Load Balancer 135 | kubernetes.io/ingress.class: "gce-internal" 136 | spec: 137 | rules: 138 | - http: 139 | paths: 140 | - path: /* 141 | backend: 142 | serviceName: helloworld 143 | servicePort: 8080 144 | ``` 145 | {% endcode %} 146 | 147 | -------------------------------------------------------------------------------- /deployment/kubernetes/resource.md: -------------------------------------------------------------------------------- 1 | # Resource Allocation 2 | 3 | This section continues from the previous section - make sure you do the tutorial in sequence. 4 | 5 | {% page-ref page="deployment.md" %} 6 | 7 | ## Default Configuration 8 | 9 | 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. 10 | 11 | {% hint style="danger" %} 12 | The defaults can cause issues: 13 | 14 | * If a Node has 1 full CPU, then Kubernetes may schedule up to 10 instances of the same container, which may overload the system. 15 | * 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\) 16 | {% endhint %} 17 | 18 | You can see the current resource by describing a Pod instance, look for the Requests/Limits lines. 19 | 20 | ```bash 21 | POD_NAME=$(kubectl get pods -lapp=helloworld -o jsonpath='{.items[0].metadata.name}') 22 | 23 | kubectl describe pod $POD_NAME 24 | ``` 25 | 26 | The details should have a `Requests` section with `cpu` value set to `100m`: 27 | 28 | ```text 29 | Name: helloworld-... 30 | Namespace: default... 31 | Containers: 32 | helloworld: 33 | ... 34 | Requests: 35 | cpu: 100m 36 | ... 37 | ``` 38 | 39 | {% hint style="info" %} 40 | The default value is `100m`, which means `100 milli` = `100/1000` = `10%`of a vCPU core. 41 | {% endhint %} 42 | 43 | The default is configured per Namespace. The application was deployed into the `default` Namespace. Look at the default resource configuration for this Namespace: 44 | 45 | ```bash 46 | kubectl describe ns default 47 | ``` 48 | 49 | See the output: 50 | 51 | ```bash 52 | Name: default 53 | Labels: 54 | Annotations: 55 | Status: Active 56 | 57 | Resource Quotas 58 | Name: gke-resource-quotas 59 | Resource Used Hard 60 | -------- --- --- 61 | count/ingresses.extensions 1 100 62 | count/jobs.batch 0 5k 63 | pods 3 1500 64 | services 2 500 65 | 66 | Resource Limits 67 | Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio 68 | ---- -------- --- --- --------------- ------------- ----------------------- 69 | Container cpu - - 100m - - 70 | ``` 71 | 72 | However, the configuration is actually stored in a `LimitRange` Kubernetes resource: 73 | 74 | ```bash 75 | kubectl get limitrange limits -oyaml 76 | ``` 77 | 78 | {% hint style="info" %} 79 | 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. 80 | {% endhint %} 81 | 82 | ## Resource Request 83 | 84 | 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. 85 | 86 | {% code title="k8s/deployment.yaml" %} 87 | ```yaml 88 | apiVersion: apps/v1 89 | kind: Deployment 90 | metadata: 91 | labels: 92 | app: helloworld 93 | name: helloworld 94 | spec: 95 | replicas: 1 96 | selector: 97 | matchLabels: 98 | app: helloworld 99 | template: 100 | metadata: 101 | labels: 102 | app: helloworld 103 | spec: 104 | containers: 105 | - image: gcr.io/.../helloworld 106 | name: helloworld 107 | # Add the resources requests block 108 | resources: 109 | requests: 110 | cpu: 200m 111 | memory: 128Mi 112 | ``` 113 | {% endcode %} 114 | 115 | {% hint style="info" %} 116 | In this example, CPU request is`200m` means `200 milli`=`200/1000` = `20%` of 1 vCPU core. 117 | 118 | Memory is `128Mi`, which is `128 Mebibytes` = `~134 Megabytes`. 119 | {% endhint %} 120 | 121 | {% hint style="info" %} 122 | 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`. 123 | {% endhint %} 124 | 125 | {% hint style="danger" %} 126 | When specifying the Memory resource allocation, do not accidentally use `m` as the unit. `128m` means `0.128 bytes`. 127 | {% endhint %} 128 | 129 | ## Resource Limit 130 | 131 | The application can consume more CPU and memory than requested - they can burst up to the limit, but cannot exceed the limit. Configure the deployment to set the limit: 132 | 133 | {% code title="k8s/service.yaml" %} 134 | ```yaml 135 | apiVersion: apps/v1 136 | kind: Deployment 137 | metadata: 138 | labels: 139 | app: helloworld 140 | name: helloworld 141 | spec: 142 | replicas: 1 143 | selector: 144 | matchLabels: 145 | app: helloworld 146 | template: 147 | metadata: 148 | labels: 149 | app: helloworld 150 | spec: 151 | containers: 152 | - image: gcr.io/.../helloworld 153 | name: helloworld 154 | # Add the resources requests block 155 | resources: 156 | requests: 157 | cpu: 200m 158 | memory: 256Mi 159 | limits: 160 | cpu: 500m 161 | memory: 256Mi 162 | ``` 163 | {% endcode %} 164 | 165 | {% hint style="info" %} 166 | 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. 167 | {% endhint %} 168 | 169 | {% hint style="danger" %} 170 | Memory is bot a compressible resource. If the application exceeds the Memory limit, then the container will be killed \(`OOMKilled`\) and restarted. 171 | {% endhint %} 172 | 173 | {% hint style="info" %} 174 | For Java applications, read the [Container Awareness](../docker/container-awareness.md) section to make sure you are using a Container-Aware OpenJDK version to avoid unnecessary `OOMKilled` errors. 175 | {% endhint %} 176 | 177 | -------------------------------------------------------------------------------- /deployment/kubernetes/resources.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Learn how to assign CPU/memory resources to your containerized application. 3 | --- 4 | 5 | # Resources 6 | 7 | This section continues from the previous section - make sure you do the tutorial in sequence. 8 | 9 | {% page-ref page="deployment.md" %} 10 | 11 | ## Default Configuration 12 | 13 | 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. 14 | 15 | {% hint style="danger" %} 16 | The defaults can cause issues: 17 | 18 | * If a Node has 1 full CPU, then Kubernetes may schedule up to 10 instances of the same container, which may overload the system. 19 | * 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\) 20 | {% endhint %} 21 | 22 | You can see the current resource by describing a Pod instance, look for the Requests/Limits lines. 23 | 24 | ```bash 25 | POD_NAME=$(kubectl get pods -lapp=helloworld -o jsonpath='{.items[0].metadata.name}') 26 | 27 | kubectl describe pod $POD_NAME 28 | ``` 29 | 30 | The details should have a `Requests` section with `cpu` value set to `100m`: 31 | 32 | ```text 33 | Name: helloworld-... 34 | Namespace: default... 35 | Containers: 36 | helloworld: 37 | ... 38 | Requests: 39 | cpu: 100m 40 | ... 41 | ``` 42 | 43 | {% hint style="info" %} 44 | The default value is `100m`, which means `100 milli` = `100/1000` = `10%`of a vCPU core. 45 | {% endhint %} 46 | 47 | The default is configured per Namespace. The application was deployed into the `default` Namespace. Look at the default resource configuration for this Namespace: 48 | 49 | ```bash 50 | kubectl describe ns default 51 | ``` 52 | 53 | See the output: 54 | 55 | ```bash 56 | Name: default 57 | Labels: 58 | Annotations: 59 | Status: Active 60 | 61 | Resource Quotas 62 | Name: gke-resource-quotas 63 | Resource Used Hard 64 | -------- --- --- 65 | count/ingresses.extensions 1 100 66 | count/jobs.batch 0 5k 67 | pods 3 1500 68 | services 2 500 69 | 70 | Resource Limits 71 | Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio 72 | ---- -------- --- --- --------------- ------------- ----------------------- 73 | Container cpu - - 100m - - 74 | ``` 75 | 76 | However, the configuration is actually stored in a `LimitRange` Kubernetes resource: 77 | 78 | ```bash 79 | kubectl get limitrange limits -oyaml 80 | ``` 81 | 82 | {% hint style="info" %} 83 | 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. 84 | {% endhint %} 85 | 86 | ## Resource Request 87 | 88 | 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. 89 | 90 | {% code title="k8s/deployment.yaml" %} 91 | ```yaml 92 | apiVersion: apps/v1 93 | kind: Deployment 94 | metadata: 95 | labels: 96 | app: helloworld 97 | name: helloworld 98 | spec: 99 | replicas: 1 100 | selector: 101 | matchLabels: 102 | app: helloworld 103 | template: 104 | metadata: 105 | labels: 106 | app: helloworld 107 | spec: 108 | containers: 109 | - image: gcr.io/.../helloworld 110 | name: helloworld 111 | # Add the resources requests block 112 | resources: 113 | requests: 114 | cpu: 200m 115 | memory: 128Mi 116 | ``` 117 | {% endcode %} 118 | 119 | {% hint style="info" %} 120 | In this example, CPU request is `200m` which means `200 milli`=`200/1000` = `20%` of 1 vCPU core. 121 | 122 | Memory is `128Mi`, which is `128 Mebibytes` = `~134 Megabytes`. 123 | {% endhint %} 124 | 125 | {% hint style="info" %} 126 | 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`. 127 | {% endhint %} 128 | 129 | {% hint style="danger" %} 130 | When specifying the Memory resource allocation, do not accidentally use `m` as the unit. `128m` means `0.128 bytes`. 131 | {% endhint %} 132 | 133 | ## Resource Limit 134 | 135 | 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: 136 | 137 | {% code title="k8s/service.yaml" %} 138 | ```yaml 139 | apiVersion: apps/v1 140 | kind: Deployment 141 | metadata: 142 | labels: 143 | app: helloworld 144 | name: helloworld 145 | spec: 146 | replicas: 1 147 | selector: 148 | matchLabels: 149 | app: helloworld 150 | template: 151 | metadata: 152 | labels: 153 | app: helloworld 154 | spec: 155 | containers: 156 | - image: gcr.io/.../helloworld 157 | name: helloworld 158 | # Add the resources requests block 159 | resources: 160 | requests: 161 | cpu: 200m 162 | memory: 256Mi 163 | limits: 164 | cpu: 500m 165 | memory: 256Mi 166 | ``` 167 | {% endcode %} 168 | 169 | {% hint style="info" %} 170 | 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. 171 | {% endhint %} 172 | 173 | {% hint style="danger" %} 174 | Memory is not a compressible resource. If the application exceeds the Memory limit, then the container will be killed \(`OOMKilled`\) and restarted. 175 | {% endhint %} 176 | 177 | {% hint style="info" %} 178 | For Java applications, read the [Container Awareness](../docker/container-awareness.md) section to make sure you are using a Container-Aware OpenJDK version to avoid unnecessary `OOMKilled` errors. 179 | {% endhint %} 180 | 181 | -------------------------------------------------------------------------------- /deployment/kubernetes/scheduling.md: -------------------------------------------------------------------------------- 1 | # Scheduling 2 | 3 | ## Anti-Affinity 4 | 5 | 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. 6 | 7 | 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). 8 | 9 | ### Required Anti-Affinity 10 | 11 | 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. 12 | 13 | {% code title="k8s/deployment.yaml" %} 14 | ```yaml 15 | apiVersion: apps/v1 16 | kind: Deployment 17 | metadata: 18 | name: helloworld 19 | labels: 20 | app: helloworld 21 | spec: 22 | replicas: 1 23 | selector: 24 | matchLabels: 25 | app: helloworld 26 | template: 27 | metadata: 28 | labels: 29 | app: helloworld 30 | spec: 31 | containers: 32 | - name: helloworld 33 | image: gcr.io/.../helloworld 34 | # Add configuration for anti-affinity 35 | affinity: 36 | podAntiAffinity: 37 | requiredDuringSchedulingIgnoredDuringExecution: 38 | - labelSelector: 39 | matchExpressions: 40 | - key: app 41 | operator: In 42 | values: 43 | - helloworld 44 | # Prefer spreading the Pods across multiple Nodes. 45 | # There are other keys you can use, e.g., anti-affinity 46 | # across zones. 47 | topologyKey: "kubernetes.io/hostname" 48 | ``` 49 | {% endcode %} 50 | 51 | Scale out the deployment to 4 pods: 52 | 53 | ```bash 54 | kubectl scale deployment helloworld --replicas=4 55 | ``` 56 | 57 | List all the pods and show which node it is running on: 58 | 59 | ```bash 60 | kubectl get pods -lapp=helloworld -owide 61 | ``` 62 | 63 | Observe in the `NODE` column, that each pod is running on a different node. 64 | 65 | 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: 66 | 67 | ```bash 68 | kubectl scale deployment helloworld --replicas=5 69 | ``` 70 | 71 | Find the pending pod: 72 | 73 | ```bash 74 | kubectl get pods -lapp=helloworld --field-selector='status.phase=Pending' 75 | ``` 76 | 77 | Describe it's detail: 78 | 79 | ```bash 80 | POD_NAME=$(kubectl get pods -lapp=helloworld \ 81 | --field-selector='status.phase=Pending' \ 82 | -o jsonpath='{.items[0].metadata.name}') 83 | 84 | kubectl describe pod $POD_NAME 85 | ``` 86 | 87 | {% hint style="info" %} 88 | See Kubernetes [Affinity / Anti-Affinity documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) for more information 89 | {% endhint %} 90 | 91 | ### Preferred Anti-Affinity 92 | 93 | 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. 94 | 95 | {% code title="k8s/deployment.yaml" %} 96 | ```yaml 97 | apiVersion: apps/v1 98 | kind: Deployment 99 | metadata: 100 | name: helloworld 101 | labels: 102 | app: helloworld 103 | spec: 104 | replicas: 1 105 | selector: 106 | matchLabels: 107 | app: helloworld 108 | template: 109 | metadata: 110 | labels: 111 | app: helloworld 112 | spec: 113 | containers: 114 | - name: helloworld 115 | image: gcr.io/.../helloworld 116 | affinity: 117 | podAntiAffinity: 118 | # Use Preferred rather than Required 119 | preferredDuringSchedulingIgnoredDuringExecution: 120 | - weight: 100 121 | podAffinityTerm: 122 | labelSelector: 123 | matchExpressions: 124 | - key: app 125 | operator: In 126 | values: 127 | - helloworld 128 | topologyKey: "kubernetes.io/hostname" 129 | ``` 130 | {% endcode %} 131 | 132 | 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. 133 | 134 | ```bash 135 | kubectl scale deployment helloworld --replicas=5 136 | ``` 137 | 138 | See that all the pods are running: 139 | 140 | ```bash 141 | kubectl get pods -lapp=helloworld 142 | ``` 143 | 144 | ## Affinity 145 | 146 | 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. 147 | 148 | {% hint style="info" %} 149 | See Kubernetes [Affinity / Anti-Affinity documentation](https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity) for more information 150 | {% endhint %} 151 | 152 | ## Disruption Budget 153 | 154 | {% hint style="info" %} 155 | See Kubernetes [Disruption Budget documentation](https://kubernetes.io/docs/tasks/run-application/configure-pdb/) for more information. 156 | {% endhint %} 157 | 158 | -------------------------------------------------------------------------------- /deployment/kubernetes/service.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Learn how to create a Kubernetes service and how service discovery works. 3 | --- 4 | 5 | # Service 6 | 7 | This section continues from the previous section - make sure you do the tutorial in sequence. 8 | 9 | {% page-ref page="deployment.md" %} 10 | 11 | ## Service 12 | 13 | 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. 14 | 15 | 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. 16 | 17 | ### Service YAML 18 | 19 | You can create a Service and deploy into Kubernetes using the `kubectl` CLI like in the [Hello World tutorial](../../getting-started/helloworld/kubernetes-engine.md). 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. 20 | 21 | ```bash 22 | # Under the helloworld-springboot-tomcat directory , create a k8s directory 23 | mkdir k8s/ 24 | 25 | kubectl create service clusterip helloworld \ 26 | --tcp=8080:8080 \ 27 | --dry-run \ 28 | -o yaml > k8s/service.yaml 29 | ``` 30 | 31 | 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. 32 | 33 | {% code title="k8s/service.yaml" %} 34 | ```yaml 35 | # API Version and Kind are important to indicate the type of resource 36 | apiVersion: v1 37 | kind: Service 38 | metadata: 39 | # Every Kubernetes resource has a name that's unique within a namespace 40 | name: helloworld 41 | # Every Kubernetes can have labels, label key/value pairs can be queried later. 42 | labels: 43 | app: helloworld 44 | spec: 45 | # The type of the service - this one is an internal only service 46 | type: ClusterIP 47 | # Any Pods that matches these labels will be load balanced through 48 | # this Service (L4 load balancer) 49 | selector: 50 | app: helloworld 51 | ports: 52 | # A port can have a name, it can be renamed to be more descriptive 53 | # such as "http", "jmx", "metrics", etc. 54 | - name: 8080-8080 55 | # The port to listen on by the Service (L4 Load Balancer) 56 | port: 8080 57 | # The port to forward traffic to on the destination Pod 58 | targetPort: 8080 59 | # TCP or UDP protocol 60 | protocol: TCP 61 | ``` 62 | {% endcode %} 63 | 64 | {% hint style="info" %} 65 | You can read more about Deployment in the [Kubernetes Service Guide](http://kubernetes.io/docs/user-guide/deployments/). 66 | {% endhint %} 67 | 68 | ### Deploy 69 | 70 | Use `kubectl` command line to deploy the YAML file: 71 | 72 | ```bash 73 | kubectl apply -f k8s/service.yaml 74 | ``` 75 | 76 | Verify that the service is configured: 77 | 78 | ```bash 79 | kubectl get svc helloworld 80 | ``` 81 | 82 | You should see that the Service has a Cluster IP address: 83 | 84 | ```bash 85 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 86 | helloworld ClusterIP ... 8080/TCP 7s 87 | ``` 88 | 89 | ## Service Discovery 90 | 91 | In a traditional Spring Cloud application, service discovery is done using an external service registry like Eureka, and client-side load balancing with Ribbon. 92 | 93 | In Kubernetes, the Kubernetes Service itself can act as the service registry: 94 | 95 | * You can discover all the endpoints associated with a service 96 | * Kubernetes Service's Cluster IP is a built-in L4 load balancer, and it's automatically associated with a DNS name 97 | 98 | ### Endpoints 99 | 100 | 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\). 101 | 102 | In the example above, the selector is `app: helloworld`, you can also find the matching Pods using `kubectl`: 103 | 104 | ```bash 105 | # Scale out the # of instances so we can see more than one pod 106 | kubectl scale deployment helloworld --replicas=2 107 | 108 | # Find Pods using a selector 109 | kubectl get pods -lapp=helloworld 110 | ``` 111 | 112 | Describe the Service to see the Endpoint IP addresses that it's currently enlisted,, and look for the `Endpoints` output: 113 | 114 | ```bash 115 | kubectl describe svc helloworld 116 | ``` 117 | 118 | 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. 119 | 120 | ### DNS Name 121 | 122 | 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`. 123 | 124 | {% hint style="info" %} 125 | 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. 126 | {% endhint %} 127 | 128 | 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. 129 | 130 | `nginx` container image has a shell that we can use, so deploy one instance of `nginx`. 131 | 132 | ```bash 133 | kubectl create deployment nginx --image=nginx 134 | ``` 135 | 136 | Attach to the `nginx` Pod: 137 | 138 | ```bash 139 | POD_NAME=$(kubectl get pods -lapp=nginx -o jsonpath='{.items[0].metadata.name}') 140 | kubectl exec -ti ${POD_NAME} /bin/bash 141 | ``` 142 | 143 | From within the Pod, `curl` the service: 144 | 145 | ```bash 146 | # From within the nginx Pod 147 | curl http://helloworld:8080 148 | 149 | exit 150 | ``` 151 | 152 | {% hint style="info" %} 153 | 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. 154 | {% endhint %} 155 | 156 | -------------------------------------------------------------------------------- /deployment/kubernetes/workload-identity.md: -------------------------------------------------------------------------------- 1 | # Workload Identity 2 | 3 | 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. 4 | 5 | ## Create Service Accounts 6 | 7 | ### Create a Kubernetes Service Account \(KSA\) 8 | 9 | ```bash 10 | kubectl create serviceaccount helloworld \ 11 | --dry-run -oyaml > k8s/helloworld-sa.yaml 12 | 13 | kubectl apply -f k8s/helloworld-sa.yaml 14 | ``` 15 | 16 | ### Create a Google Cloud Service Account \(GSA\) 17 | 18 | ```bash 19 | gcloud iam service-accounts create helloworld 20 | 21 | PROJECT_ID=$(gcloud config get-value project) 22 | gcloud projects add-iam-policy-binding ${PROJECT_ID} \ 23 | --member serviceAccount:helloworld@${PROJECT_ID}.iam.gserviceaccount.com \ 24 | --role roles/pubsub.publisher 25 | ``` 26 | 27 | ## Bind Service Accounts 28 | 29 | Bind the Kubernetes Service Account \(KSA\) to Google Cloud Service Account \(GSA\) 30 | 31 | ### Binding from Google Cloud 32 | 33 | ```bash 34 | PROJECT_ID=$(gcloud config get-value project) 35 | gcloud iam service-accounts add-iam-policy-binding \ 36 | --role roles/iam.workloadIdentityUser \ 37 | --member "serviceAccount:${PROJECT_ID}.svc.id.goog[default/helloworld]" \ 38 | helloworld@${PROJECT_ID}.iam.gserviceaccount.com 39 | ``` 40 | 41 | ### Binding from Kubernetes 42 | 43 | ```bash 44 | PROJECT_ID=$(gcloud config get-value project) 45 | kubectl annotate -f k8s/helloworld-sa.yaml \ 46 | iam.gke.io/gcp-service-account=helloworld@${PROJECT_ID}.iam.gserviceaccount.com 47 | 48 | kubectl apply -f k8s/helloworld-sa.yaml 49 | ``` 50 | 51 | ## Use the Kubernetes Service Account 52 | 53 | {% code title="k8s/nginx-sa-deployment.yaml" %} 54 | ```yaml 55 | apiVersion: apps/v1 56 | kind: Deployment 57 | metadata: 58 | name: nginx-sa 59 | labels: 60 | app: nginx-sa 61 | spec: 62 | replicas: 1 63 | selector: 64 | matchLabels: 65 | app: nginx-sa 66 | template: 67 | metadata: 68 | labels: 69 | app: nginx-sa 70 | spec: 71 | # Specify the KSA to use 72 | serviceAccountName: helloworld 73 | containers: 74 | - image: nginx 75 | name: nginx 76 | ``` 77 | {% endcode %} 78 | 79 | To try it out, first `exec` into the Pod: 80 | 81 | ```bash 82 | POD_NAME=$(kubectl get pods -lapp=nginx-sa -o jsonpath='{.items[0].metadata.name}') 83 | 84 | kubectl exec -ti ${POD_NAME} -- /bin/bash 85 | ``` 86 | 87 | Inside the Pod, see metadata server: 88 | 89 | ```bash 90 | curl -H"Metadata-Flavor: Google" \ 91 | http://metadata/computeMetadata/v1/instance/service-accounts/default/email 92 | ``` 93 | 94 | -------------------------------------------------------------------------------- /deployment/runtime-environments.md: -------------------------------------------------------------------------------- 1 | # Runtime Environments 2 | 3 | ## Basics 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 83 | 88 | 94 | 100 | 105 | 110 | 111 | 112 |
Cloud FunctionsApp EngineCloud RunKubernetes EngineCompute Engine
Fully Managed 19 | FaaSPaaSCaaS/PaaSKubernetes ClustersVirtual Machines
Deployable Artifact 28 | Source or JARSource or JARContainer ImageContainer ImageAnything, and Container Image
Locality 37 | RegionalRegionalRegionalZonal/RegionalZonal/Regional
Billing Units 46 | Instance execution time seconds and InvocationsInstance up time minutesInstance execution time secondsControl plane and VM instance hoursVM instance hours
vCPU 55 | 11, up to 4.8GHzUp to 2Up to 416 per node, and up to 5000 nodes.0.5 to 416
Memory 64 | Up to 2GBUp to 2GBUp to 4GBUp to 11TB per node.Up to 11TB
Disk 73 | Writable /tmp directoryWritable /tmp directoryWritable /tmp directoryAttach Tmpfs/PD/SSDAttach PD/SSD
Use For 82 | 84 |

Webhooks

85 |

Event Handlers

86 |

Tasks

87 |
89 |

Web Apps

90 |

Microservices

91 |

Event Handlers

92 |

Tasks

93 |
95 |

Web Apps

96 |

Microservices

97 |

Event Handlers

98 |

Tasks

99 |
101 |

Any container workload

102 |

JEE applications

103 |

Microservices

104 |
106 |

Any workload

107 |

JEE applications

108 |

Databases

109 |
113 | 114 | ## Application Lifecycle 115 | 116 | | | Cloud Functions | App Engine | Cloud Run | Kubernetes Engine | Compute Engine | 117 | | :--- | :--- | :--- | :--- | :--- | :--- | 118 | | **Liveness Check** | Port is listening | /\_ah/health | Port is listening | Liveness Probe | Manual | 119 | | **Readiness/Warmup Check** | No | /\_ah/warmup | No | Readiness Probe | Manual | 120 | | **Graceful Shutdown** | No Signal | /\_ah/stop | No Signal | `SIGTERM` or custom hooks | Manual | 121 | 122 | ## Scaling 123 | 124 | | | Cloud Functions | App Engine | Cloud Run | Kubernetes Engine | Compute Engine | 125 | | :--- | :--- | :--- | :--- | :--- | :--- | 126 | | **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 | 127 | | **Autoscaling** | Yes | Yes | Yes | Yes, with HPA and Cluster Autoscaler | Yes, with Managed Instance Group | 128 | | **Scaling Min/Max** | No | Yes | Yes \(alpha\) | Yes, with HPA and Cluster Autoscaler | Yes, with Managed Instance Group | 129 | | **Manual Scaling** | No | Yes | No | Yes | Yes | 130 | 131 | ## Load Balancing 132 | 133 | | | Cloud Functions | App Engine | Cloud Run | Kubernetes Engine | Compute Engine | 134 | | :--- | :--- | :--- | :--- | :--- | :--- | 135 | | **Network Load Balancer** | No | No | No | Yes, with `Service` | Yes, with Network Load Balancer | 136 | | **HTTP** | Yes | Yes | Yes | Yes, with `Ingress` | Yes, with HTTP\(s\) Load Balancer | 137 | | **HTTPs** | Yes | Yes | Yes | Yes, with `Ingress` | Yes, with HTTP\(s\) Load Balancer | 138 | | **Custom Domain** | No | Yes | Yes | Yes, manual configuration | Yes, manual configuration | 139 | | **Managed SSL Certificate** | Yes | Yes | Yes | Yes, with `ManagedCertificate` | Yes, with HTTP\(s\) Load Balancer | 140 | 141 | ## Networking 142 | 143 | | | Cloud Functions | App Engine | Cloud Run | Kubernetes Engine | Compute Engine | 144 | | :--- | :--- | :--- | :--- | :--- | :--- | 145 | | Use VPC | Yes | Yes | Yes | Yes | Yes | 146 | | Expose on VPC Only | Yes | No | No | Yes | Yes | 147 | | Internal VPC Only Load Balancing | No | No | No | Yes | Yes | 148 | 149 | -------------------------------------------------------------------------------- /getting-started/cloud-shell.md: -------------------------------------------------------------------------------- 1 | # Cloud Shell 2 | 3 | [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! 4 | 5 | ## Select a Project 6 | 7 | After you logged into the Google Cloud console, make sure you have selected a project in the top bar, by clicking **Select a project**. 8 | 9 | ![Select a project](../.gitbook/assets/image%20%2832%29.png) 10 | 11 | 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. 12 | 13 | ## Activate Cloud Shell 14 | 15 | On the top right, click the **Activate Cloud Shell** icon. 16 | 17 | ![Activate Cloud Shell icon](../.gitbook/assets/image%20%2828%29.png) 18 | 19 | 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\). 20 | 21 | Make sure your Cloud Shell is configured with the current project by checking the current Project ID configured for `gcloud`: 22 | 23 | ```bash 24 | gcloud config get-value project 25 | ``` 26 | 27 | ## Home Directory 28 | 29 | 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. 30 | 31 | If you want to install any additional binaries, make sure to store them inside your home directory, maybe under a `bin` directory. 32 | 33 | ```bash 34 | mkdir $HOME/bin 35 | echo 'export PATH="$HOME/bin:$PATH"' >> $HOME/.bashrc 36 | ``` 37 | 38 | ## Boost Mode 39 | 40 | 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**. 41 | 42 | ![Boost Cloud Shell](../.gitbook/assets/image%20%2830%29.png) 43 | 44 | 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. 45 | 46 | {% hint style="info" %} 47 | See [Compute Engine Machine Types documentation](https://cloud.google.com/compute/docs/machine-types#e2_machine_types) for more details on the machine types. 48 | {% endhint %} 49 | 50 | ## Multiple Tabs 51 | 52 | You can open new Cloud Shell tabs by clicking the **+** icon. 53 | 54 | ![Open a new tab + icon](../.gitbook/assets/image%20%2823%29.png) 55 | 56 | ## Code Editor 57 | 58 | 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**. 59 | 60 | ![Open Editor](../.gitbook/assets/image%20%2834%29.png) 61 | 62 | 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**. 63 | 64 | ![Open Terminal](../.gitbook/assets/image%20%2821%29.png) 65 | 66 | 67 | 68 | ## Default Zone and Region 69 | 70 | 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`. 71 | 72 | ```bash 73 | gcloud config set compute/region us-central1 74 | gcloud config set compute/zone us-central1-c 75 | gcloud config set run/region us-central1 76 | ``` 77 | 78 | {% hint style="info" %} 79 | See the complete list in [Regions and Zones documentation](https://cloud.google.com/compute/docs/regions-zones). 80 | {% endhint %} 81 | 82 | -------------------------------------------------------------------------------- /getting-started/gcloud-cli.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Install gcloud command to interact with Google Cloud Platform from the command 4 | line. 5 | --- 6 | 7 | # gcloud CLI 8 | 9 | ## Cloud Shell 10 | 11 | Cloud Shell already has the `gcloud` CLI pre-installed, so you can **skip ahead to** [**configure default zone and region**](#default-zone-and-region). 12 | 13 | {% hint style="danger" %} 14 | Jump to the last section to [configure default zone and region](#default-zone-and-region) so you do not need to repeatedly specify it. 15 | {% endhint %} 16 | 17 | ## Local Installation 18 | 19 | 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. 20 | 21 | {% hint style="danger" %} 22 | If you are using Cloud Shell, jump to the last section to [configure default zone and region](#default-zone-and-region) so you do not need to repeatedly specify it. 23 | {% endhint %} 24 | 25 | ### Authenticate 26 | 27 | Authenticate gcloud so that it can interact with Google Cloud Platform using your account. 28 | 29 | ```bash 30 | gcloud auth login 31 | ``` 32 | 33 | {% hint style="info" %} 34 | This authenticated `gcloud` so that you can run all the commands. 35 | {% endhint %} 36 | 37 | ### Project ID 38 | 39 | Set the default Project ID to your project. 40 | 41 | ```bash 42 | gcloud config set project YOUR_PROJECT_ID 43 | ``` 44 | 45 | {% hint style="info" %} 46 | If you already have a project, run `gcloud projects list` to list available projects. Find one and then set it as a default project. 47 | {% endhint %} 48 | 49 | ### Application Default Credentials 50 | 51 | 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. 52 | 53 | ```bash 54 | gcloud auth application-default login 55 | ``` 56 | 57 | ### Quota Project 58 | 59 | 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. 60 | 61 | 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. 62 | 63 | If needed, you can configure a different Quota Project: 64 | 65 | ```bash 66 | gcloud auth application-default set-quota-project YOUR_PROJECT_ID 67 | ``` 68 | 69 | {% hint style="info" %} 70 | Application Default Credentials are used by client libraries when making calls to Google Cloud. This is different from the [first gcloud Authenticate](#authenticate), which is for `gcloud` to make calls to Google Cloud. 71 | {% endhint %} 72 | 73 | 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. 74 | 75 | ### Default Zone and Region 76 | 77 | 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. 78 | 79 | 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`. 80 | 81 | ```bash 82 | gcloud config set compute/region us-central1 83 | gcloud config set compute/zone us-central1-c 84 | gcloud config set run/region us-central1 85 | ``` 86 | 87 | {% hint style="info" %} 88 | See the complete list in [Regions and Zones documentation](https://cloud.google.com/compute/docs/regions-zones). 89 | {% endhint %} 90 | 91 | -------------------------------------------------------------------------------- /getting-started/google-cloud-platform.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Get started on Google Cloud Platform by signing up for a free account and 4 | creating a new project to use. 5 | --- 6 | 7 | # Google Cloud Platform 8 | 9 | ## Sign Up 10 | 11 | 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. 12 | 13 | ## Project 14 | 15 | All cloud services and resources \(such as virtual machines, network, load balancer, etc\) are created under a Google Cloud Platform project. 16 | 17 | 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. 18 | 19 | A project is a security boundary. You can assign additional users to access different services / resources within the project. 20 | 21 | Projects are usually referred to by Project ID. A Project ID is globally unique. 22 | 23 | ### New Account 24 | 25 | If this is your first time signing up for Google Cloud Platform, it will automatically create a Google Cloud Platform Project. 26 | 27 | ![Google Cloud Platform console with a default project](../.gitbook/assets/image%20%281%29.png) 28 | 29 | Every project has a Project ID and a Project Number. Project ID is most used. Find the Project ID in **Home**, under **Project info**. 30 | 31 | ![Project info panel showing the Project ID](../.gitbook/assets/image%20%282%29.png) 32 | 33 | ### Existing Account 34 | 35 | If you already have an account, use an existing project, or create a new one. 36 | 37 | ## Identity Access Management 38 | 39 | IAM may be one of the hardest concepts to grasp about Google Cloud Platform - but once you understand it, everything else becomes clear. 40 | 41 | ### Member 42 | 43 | All Members \(i.e., a user\) are identified by an e-mail address: 44 | 45 | | Type | Uses | Identified By | 46 | | :--- | :--- | :--- | 47 | | User Account | User interaction with `gcloud` CLI, or the web console. | User's e-mail address | 48 | | Service Account | Service to Service authentication | Service account's e-mail address | 49 | | G Suite Group | A collection of user accounts or service accounts. | G Suite Group e-mail | 50 | | G Suite Domain | All users and groups of a G Suite domain. | G Suite domain name | 51 | 52 | Sometimes, when referring to different types of Members, you may need to add a prefix: 53 | 54 | | Type | Prefix | Example | 55 | | :--- | :--- | :--- | 56 | | User Account | user | user:jane@example.com | 57 | | Service Account | serviceAccount | serviceAccount:my-service@appspot.gserviceaccount.com | 58 | | G Suite Group | group | group:webmaster@example.com | 59 | | G Suite Domain | domain | domain:example.com | 60 | 61 | See [Identity Access Management Overview documentation](https://cloud.google.com/iam/docs/overview) for more details. 62 | 63 | ### Permission 64 | 65 | 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`. 66 | 67 | ### Roles 68 | 69 | 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](google-cloud-platform.md#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. 70 | 71 | {% hint style="info" %} 72 | You can create [Custom Roles](https://cloud.google.com/iam/docs/understanding-custom-roles) to associate with specific permissions too. 73 | {% endhint %} 74 | 75 | ### Credential 76 | 77 | | Type | Credential | 78 | | :--- | :--- | 79 | | User Account | OAuth credentials - an Access Token, or a Refresh Token, or [Application Default Credentials](google-cloud-platform.md#application-default-credentials). | 80 | | 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) | 81 | 82 | {% hint style="info" %} 83 | A User Account is great for local development when using `gcloud`. Service Account is great for your application/microservice. 84 | {% endhint %} 85 | 86 | #### Application Default Credentials 87 | 88 | This is the default credential that a Google Cloud client library will discover. And Application Default Credential can be: 89 | 90 | * Created by `gcloud auth application-default login` when running locally, 91 | * **or** a `GOOGLE_APPLICATION_CREDENTIALS` environmental variable that points to the path of a Service [Account key file](google-cloud-platform.md#service-account-key), 92 | * **or** automatically discovered using the [Metadata Server](google-cloud-platform.md#machine-credentials). 93 | 94 | 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. 95 | 96 | #### Service Account Key 97 | 98 | 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! 99 | 100 | {% hint style="danger" %} 101 | Never expose the service account key file in the public. 102 | 103 | Never check-in your service account key file. 104 | 105 | Never put your service account key file in a container image, or deployable artifact like a JAR file. 106 | {% endhint %} 107 | 108 | {% hint style="success" %} 109 | Always store your service account securely. 110 | {% endhint %} 111 | 112 | {% hint style="success" %} 113 | In most cases, your application is associated with a service account, but will **not** need the Service Account key file. See [Machine Credentials](google-cloud-platform.md#machine-credentials-from-metadata-server). 114 | {% endhint %} 115 | 116 | #### Machine Credentials from Metadata Server 117 | 118 | 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: 119 | 120 | ```bash 121 | curl -H "Metadata-Flavor: Google" \ 122 | http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token 123 | ``` 124 | 125 | {% hint style="info" %} 126 | 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. 127 | {% endhint %} 128 | 129 | -------------------------------------------------------------------------------- /getting-started/helloworld/README.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: >- 3 | Google Cloud Platform has a range of different runtime environments to run 4 | your Java / Spring Boot application. 5 | --- 6 | 7 | # Hello World! 8 | 9 | -------------------------------------------------------------------------------- /getting-started/helloworld/app-engine.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Deploy a JAR to a fully managed PaaS with just one command. 3 | --- 4 | 5 | # App Engine 6 | 7 | [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. 8 | 9 | {% embed url="https://www.youtube.com/watch?v=qx\_T6-EKkBE" %} 10 | 11 | ## Getting Started 12 | 13 | ### Clone 14 | 15 | ```bash 16 | cd $HOME 17 | git clone https://github.com/saturnism/jvm-helloworld-by-example 18 | cd jvm-helloworld-by-example/helloworld-springboot-tomcat 19 | ``` 20 | 21 | ### Build 22 | 23 | ```bash 24 | ./mvnw package 25 | ``` 26 | 27 | ### Deploy 28 | 29 | ```bash 30 | gcloud app deploy target/helloworld.jar 31 | ``` 32 | 33 | {% hint style="info" %} 34 | 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. 35 | {% endhint %} 36 | 37 | {% hint style="warning" %} 38 | Once you select the region, you cannot change it for an App Engine application. 39 | {% endhint %} 40 | 41 | ### Connect 42 | 43 | Once deployed, the command will output the HTTPs URL. To open the URL in your browser: 44 | 45 | ```bash 46 | gcloud app browse 47 | ``` 48 | 49 | To find the URL without opening the browser: 50 | 51 | ```bash 52 | gcloud app browse --no-launch-browser 53 | ``` 54 | 55 | You can `curl` the URL: 56 | 57 | ```bash 58 | URL=$(gcloud app browse --no-launch-browser) 59 | curl ${URL} 60 | ``` 61 | 62 | {% hint style="info" %} 63 | 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`. 64 | {% endhint %} 65 | 66 | ## Additional Configuration 67 | 68 | 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`: 69 | 70 | {% code title="app.yaml" %} 71 | ```text 72 | runtime: java11 73 | instance_class: F4 74 | env_variables: 75 | SPRING_PROFILES_ACTIVE: "prod" 76 | ``` 77 | {% endcode %} 78 | 79 | {% hint style="info" %} 80 | 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. 81 | {% endhint %} 82 | 83 | Deploy the JAR file with the configuration: 84 | 85 | ```bash 86 | gcloud app deploy target/helloworld.jar \ 87 | --appyaml app.yaml 88 | ``` 89 | 90 | {% hint style="info" %} 91 | Learn more about the configurations in [app.yaml reference documentation](https://cloud.google.com/appengine/docs/standard/java11/config/appref). 92 | {% endhint %} 93 | 94 | ## Learn More 95 | 96 | * [App Engine Java 11 documentation](https://cloud.google.com/appengine/docs/standard/java11) 97 | * [Deploy with App Engine Maven plugin](https://cloud.google.com/appengine/docs/standard/java11/using-maven#setting_up_maven) 98 | * [Deploy with App Engine Gradle plugin](https://cloud.google.com/appengine/docs/standard/java11/using-gradle) 99 | 100 | -------------------------------------------------------------------------------- /getting-started/helloworld/cloud-functions.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Deploy a simple HTTP function. 3 | --- 4 | 5 | # Cloud Functions 6 | 7 | [Cloud Function](https://cloud.google.com/functions/docs/) is a scalable, pay as you go, Functions-as-a-Service \(FaaS\). 8 | 9 | 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. 10 | 11 | This guide currently uses a non-Spring example for Cloud Functions. 12 | 13 | {% embed url="https://www.youtube.com/watch?v=UsYRKkibLPI" caption="" %} 14 | 15 | ## Getting Started 16 | 17 | ### Clone 18 | 19 | ```bash 20 | cd $HOME 21 | git clone https://github.com/GoogleCloudPlatform/java-docs-samples 22 | cd java-docs-samples/functions/helloworld/helloworld 23 | ``` 24 | 25 | ### Build 26 | 27 | ```bash 28 | mvn package 29 | ``` 30 | 31 | ### Run Locally 32 | 33 | ```bash 34 | mvn function:run 35 | 36 | # In a different tab, trigger the function: 37 | curl localhost:8080 38 | ``` 39 | 40 | ### Deploy 41 | 42 | #### Enable API 43 | 44 | ```bash 45 | gcloud services enable cloudfunctions.googleapis.com 46 | ``` 47 | 48 | #### Deploy 49 | 50 | ```bash 51 | gcloud functions deploy helloworld --trigger-http \ 52 | --runtime=java11 \ 53 | --entry-point=functions.HelloWorld \ 54 | --allow-unauthenticated 55 | ``` 56 | 57 | ### Connect 58 | 59 | Once a HTTP function is deployed, you can connect to it using `curl`. You can also find the URL: 60 | 61 | ```bash 62 | gcloud functions describe helloworld --format='value(httpsTrigger.url)' 63 | ``` 64 | 65 | Trigger the function with `curl`: 66 | 67 | ```bash 68 | URL=$(gcloud functions describe helloworld --format='value(httpsTrigger.url)') 69 | 70 | curl ${URL} 71 | ``` 72 | 73 | Alternatively, you can also use `gcloud`: 74 | 75 | ```bash 76 | gcloud functions call helloworld 77 | ``` 78 | 79 | ## Additional Configurations 80 | 81 | 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: 82 | 83 | ```bash 84 | gcloud functions deploy helloworld --trigger-http \ 85 | --runtime=java11 \ 86 | --memory=512M 87 | --entry-point=functions.HelloWorld \ 88 | --allow-unauthenticated 89 | ``` 90 | 91 | ## Learn More 92 | 93 | * [Cloud Functions Java Runtime documentation](https://cloud.google.com/functions/docs/concepts/java-runtime) 94 | * [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) 95 | 96 | -------------------------------------------------------------------------------- /getting-started/helloworld/cloud-run.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Deploy a container to serverless environment using a single command. 3 | --- 4 | 5 | # Cloud Run 6 | 7 | [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. 8 | 9 | ## Getting Started - Click to Deploy 10 | 11 | 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! 12 | 13 | [![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) 14 | 15 | ## Getting Started - Manual Deployment 16 | 17 | ### Clone 18 | 19 | ```bash 20 | cd $HOME 21 | git clone https://github.com/saturnism/jvm-helloworld-by-example 22 | cd jvm-helloworld-by-example/helloworld-springboot-tomcat 23 | ``` 24 | 25 | ### Build 26 | 27 | ```bash 28 | ./mvnw package 29 | ``` 30 | 31 | ### Containerize 32 | 33 | #### Enable API 34 | 35 | Enable the Container Registry API so that you can push container images to [Container Registry](https://cloud.google.com/container-registry). 36 | 37 | ```bash 38 | gcloud services enable containerregistry.googleapis.com 39 | ``` 40 | 41 | #### Jib 42 | 43 | Use Jib to containerize the application: 44 | 45 | ```bash 46 | PROJECT_ID=$(gcloud config get-value project) 47 | 48 | ./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \ 49 | -Dimage=gcr.io/${PROJECT_ID}/helloworld 50 | ``` 51 | 52 | {% hint style="info" %} 53 | Learn different ways to containerize a Java application in the [Container Image](../../deployment/docker/container-image.md) section. 54 | {% endhint %} 55 | 56 | ### Deploy 57 | 58 | #### Enable API 59 | 60 | ```bash 61 | # To use Cloud Run 62 | gcloud services enable run.googleapis.com 63 | ``` 64 | 65 | #### Deploy Container 66 | 67 | ```bash 68 | PROJECT_ID=$(gcloud config get-value project) 69 | 70 | gcloud run deploy helloworld \ 71 | --region=us-central1 \ 72 | --platform=managed \ 73 | --allow-unauthenticated \ 74 | --image=gcr.io/${PROJECT_ID}/helloworld 75 | ``` 76 | 77 | ### Connect 78 | 79 | Once deployed, Cloud Run will display the HTTPs URL. You can also find the URL with the command line: 80 | 81 | ```text 82 | gcloud run services describe helloworld \ 83 | --region=us-central1 \ 84 | --platform=managed 85 | ``` 86 | 87 | You can `curl` the URL: 88 | 89 | ```bash 90 | URL=$(gcloud run services describe helloworld \ 91 | --region=us-central1 \ 92 | --platform=managed \ 93 | --format='value(status.address.url)') 94 | 95 | curl ${URL} 96 | ``` 97 | 98 | ## Additional Configurations 99 | 100 | 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: 101 | 102 | ```bash 103 | PROJECT_ID=$(gcloud config get-value project) 104 | 105 | gcloud run deploy helloworld --platform=managed --allow-unauthenticated \ 106 | --cpu=2 --memory=512M --set-env-vars="SPRING_PROFILES_ACTIVE=prod" \ 107 | --image=gcr.io/${PROJECT_ID}/helloworld 108 | ``` 109 | 110 | ## Learn More 111 | 112 | * [Optimizing Java Applications on Cloud Run](https://cloud.google.com/run/docs/tips/java) 113 | 114 | -------------------------------------------------------------------------------- /getting-started/helloworld/cloud-shell.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Use Cloud Shell to build and test applications for development purpose. 3 | --- 4 | 5 | # Cloud Shell 6 | 7 | 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. 8 | 9 | ## Getting Started 10 | 11 | ### Clone 12 | 13 | ```bash 14 | cd $HOME 15 | git clone https://github.com/saturnism/jvm-helloworld-by-example 16 | cd jvm-helloworld-by-example/helloworld-springboot-tomcat 17 | ``` 18 | 19 | ### Build 20 | 21 | ```bash 22 | ./mvnw package 23 | ``` 24 | 25 | ### Run 26 | 27 | {% tabs %} 28 | {% tab title="Plugin" %} 29 | ```bash 30 | ./mvnw spring-boot:run 31 | ``` 32 | {% endtab %} 33 | 34 | {% tab title="JAR" %} 35 | ```bash 36 | java -jar target/helloworld.jar 37 | ``` 38 | {% endtab %} 39 | {% endtabs %} 40 | 41 | ### Connect 42 | 43 | From Cloud Shell, click **Web Preview**, then click **Preview on port 8080.** 44 | 45 | ![Web Preview](../../.gitbook/assets/image%20%2837%29.png) 46 | 47 | -------------------------------------------------------------------------------- /getting-started/helloworld/compute-engine.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Create a VM then deploy your application to the VM. 3 | --- 4 | 5 | # Compute Engine 6 | 7 | ## Getting Started 8 | 9 | ### Clone 10 | 11 | ```bash 12 | cd $HOME 13 | git clone https://github.com/saturnism/jvm-helloworld-by-example 14 | cd jvm-helloworld-by-example/helloworld-springboot-tomcat 15 | ``` 16 | 17 | ### Build 18 | 19 | ```bash 20 | ./mvnw package 21 | ``` 22 | 23 | ### Create a VM 24 | 25 | #### Enable API 26 | 27 | ```bash 28 | gcloud services enable compute.googleapis.com 29 | ``` 30 | 31 | #### Create a VM 32 | 33 | ```bash 34 | gcloud compute instances create helloworld \ 35 | --scopes=cloud-platform 36 | ``` 37 | 38 | {% hint style="info" %} 39 | If you want to use a specific distribution, such as Debian 10, you can add additional parameters: 40 | 41 | ```bash 42 | gcloud compute instances create helloworld \ 43 | --image-family debian-10 --image-project debian-cloud 44 | ``` 45 | {% endhint %} 46 | 47 | {% hint style="info" %} 48 | 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. 49 | {% endhint %} 50 | 51 | ### Copy File to VM 52 | 53 | ```bash 54 | gcloud compute scp target/helloworld.jar helloworld: 55 | ``` 56 | 57 | {% hint style="info" %} 58 | If this is your first time connecting to the VM, it will automatically prompt you to generate a new SSH key. 59 | {% endhint %} 60 | 61 | ### SSH to VM 62 | 63 | ```bash 64 | gcloud compute ssh helloworld 65 | ``` 66 | 67 | ### Install OpenJDK in the VM 68 | 69 | ```bash 70 | sudo apt-get update && sudo apt-get install -y openjdk-11-jdk 71 | ``` 72 | 73 | ### Run in the VM 74 | 75 | ```bash 76 | java -jar helloworld.jar 77 | ``` 78 | 79 | ### Expose 80 | 81 | #### Firewall 82 | 83 | 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. 84 | 85 | From outside of the VM \(e.g., your computer, or Cloud Shell\): 86 | 87 | #### Add Tag 88 | 89 | ```bash 90 | gcloud compute instances add-tags helloworld --tags=webapp 91 | ``` 92 | 93 | #### Add Firewall Rule 94 | 95 | ```bash 96 | gcloud compute firewall-rules create webapp-rule \ 97 | --source-ranges=0.0.0.0/0 \ 98 | --target-tags=webapp \ 99 | --allow=tcp:8080 100 | ``` 101 | 102 | ### Connect 103 | 104 | Find the external IP address of the Compute Engine VM instance: 105 | 106 | ```bash 107 | gcloud compute instances list 108 | ``` 109 | 110 | You can now connect to the external IP on port `8080` of the application: 111 | 112 | ```bash 113 | EXTERNAL_IP=$(gcloud compute instances describe helloworld \ 114 | --format='value(networkInterfaces.accessConfigs[0].natIP)') 115 | 116 | curl http://${EXTERNAL_IP}:8080 117 | ``` 118 | 119 | {% hint style="info" %} 120 | 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). 121 | {% endhint %} 122 | 123 | ## Getting Started - Container in Compute Engine 124 | 125 | ### Clone 126 | 127 | ```text 128 | git clone https://github.com/saturnism/jvm-helloworld-by-example 129 | cd jvm-helloworld-by-example/helloworld-springboot-tomcat 130 | ``` 131 | 132 | ### Build 133 | 134 | ```text 135 | ./mvnw package 136 | ``` 137 | 138 | ### Containerize 139 | 140 | #### Enable API 141 | 142 | Enable Container Registry API to be able to push container images to the Container Registry. 143 | 144 | ```bash 145 | gcloud services enable containerregistry.googleapis.com 146 | ``` 147 | 148 | #### Jib 149 | 150 | Use Jib to containerize the application: 151 | 152 | ```bash 153 | PROJECT_ID=$(gcloud config get-value project) 154 | 155 | ./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \ 156 | -Dimage=gcr.io/${PROJECT_ID}/helloworld 157 | ``` 158 | 159 | {% hint style="info" %} 160 | Learn different ways to containerize a Java application in the [Container Image](../../deployment/docker/container-image.md) section. 161 | {% endhint %} 162 | 163 | ### Create a VM with Container Image 164 | 165 | #### Enable API 166 | 167 | ```bash 168 | gcloud services enable compute.googleapis.com 169 | ``` 170 | 171 | #### Create a VM 172 | 173 | ```bash 174 | PROJECT_ID=$(gcloud config get-value project) 175 | gcloud compute instances create-with-container \ 176 | helloworld-with-container \ 177 | --container-image=gcr.io/${PROJECT_ID}/helloworld \ 178 | --scopes=cloud-platform 179 | ``` 180 | 181 | {% hint style="info" %} 182 | This will automatically create a Container-Optimized VM, and start the container on VM startup. 183 | {% endhint %} 184 | 185 | ### Expose 186 | 187 | #### Firewall 188 | 189 | 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. 190 | 191 | Add a tag: 192 | 193 | ```bash 194 | gcloud compute instances add-tags \ 195 | helloworld-with-container --tags=webapp 196 | ``` 197 | 198 | Add Firewall rule: 199 | 200 | ```bash 201 | gcloud compute firewall-rules create webapp-rule \ 202 | --source-ranges=0.0.0.0/0 \ 203 | --target-tags=webapp \ 204 | --allow=tcp:8080 205 | ``` 206 | 207 | ### Connect 208 | 209 | Find the external IP address of the Compute Engine VM instance: 210 | 211 | ```bash 212 | gcloud compute instances list 213 | ``` 214 | 215 | You can now connect to the external IP on port `8080` of the application: 216 | 217 | ```bash 218 | EXTERNAL_IP=$(gcloud compute instances describe helloworld-with-container \ 219 | --format='value(networkInterfaces.accessConfigs[0].natIP)') 220 | curl http://${EXTERNAL_IP}:8080 221 | ``` 222 | 223 | {% hint style="info" %} 224 | 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). 225 | {% endhint %} 226 | 227 | {% hint style="info" %} 228 | 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. 229 | {% endhint %} 230 | 231 | -------------------------------------------------------------------------------- /getting-started/helloworld/kubernetes-engine.md: -------------------------------------------------------------------------------- 1 | --- 2 | description: Create a Kubernetes cluster and deploy a container. 3 | --- 4 | 5 | # Kubernetes Engine 6 | 7 | [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. 8 | 9 | ## Getting Started 10 | 11 | ### Clone 12 | 13 | ```bash 14 | cd $HOME 15 | git clone https://github.com/saturnism/jvm-helloworld-by-example 16 | cd jvm-helloworld-by-example/helloworld-springboot-tomcat 17 | ``` 18 | 19 | ### Build 20 | 21 | ```bash 22 | ./mvnw package 23 | ``` 24 | 25 | ### Containerize 26 | 27 | #### Enable API 28 | 29 | Enable the Container Registry API so that you can push container images to [Container Registry](https://cloud.google.com/container-registry). 30 | 31 | ```bash 32 | gcloud services enable containerregistry.googleapis.com 33 | ``` 34 | 35 | #### Jib 36 | 37 | Use Jib to containerize the application: 38 | 39 | ```bash 40 | PROJECT_ID=$(gcloud config get-value project) 41 | 42 | ./mvnw compile com.google.cloud.tools:jib-maven-plugin:2.4.0:build \ 43 | -Dimage=gcr.io/${PROJECT_ID}/helloworld 44 | ``` 45 | 46 | {% hint style="info" %} 47 | Learn different ways to containerize a Java application in the [Container Image](../../deployment/docker/container-image.md) section. 48 | {% endhint %} 49 | 50 | ### Create Cluster 51 | 52 | #### Enable API 53 | 54 | ```bash 55 | gcloud services enable compute.googleapis.com 56 | gcloud services enable container.googleapis.com 57 | ``` 58 | 59 | #### Create Cluster 60 | 61 | Create a [VPC-native Kubernetes Engine cluster](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips). 62 | 63 | ```bash 64 | gcloud container clusters create helloworld-cluster \ 65 | --num-nodes 2 \ 66 | --enable-ip-alias \ 67 | --scopes=cloud-platform \ 68 | --network=default \ 69 | --machine-type n1-standard-1 70 | ``` 71 | 72 | {% hint style="info" %} 73 | 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. 74 | {% endhint %} 75 | 76 | #### Cluster Credentials 77 | 78 | Kubernetes credentials are automatically retrieved and stored in your `$HOME/.kube/config` file. If you need to re-retrieve the credentials: 79 | 80 | ```bash 81 | gcloud container clusters get-credentials helloworld-cluster 82 | ``` 83 | 84 | ### Deploy 85 | 86 | ```bash 87 | PROJECT_ID=$(gcloud config get-value project) 88 | 89 | kubectl create deployment helloworld \ 90 | --image=gcr.io/${PROJECT_ID}/helloworld 91 | ``` 92 | 93 | Check that the container is deployed: 94 | 95 | ```bash 96 | kubectl get pods 97 | ``` 98 | 99 | ### Expose 100 | 101 | You can expose this one service using a single [Network \(L4\) Load Balancer](https://cloud.google.com/load-balancing/docs/network): 102 | 103 | ```bash 104 | kubectl create service loadbalancer helloworld --tcp=8080:8080 105 | ``` 106 | 107 | {% hint style="info" %} 108 | 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. 109 | {% endhint %} 110 | 111 | ### Connect 112 | 113 | Find the Load Balancer's External IP address: 114 | 115 | ```bash 116 | kubectl get services helloworld 117 | ``` 118 | 119 | Initially, it may display that the External IP is ``. 120 | 121 | ```bash 122 | NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE 123 | helloworld LoadBalancer ... 8080:32414/TCP ... 124 | ``` 125 | 126 | Re-check until the External IP is assigned. 127 | 128 | Then connect with `curl`: 129 | 130 | ```bash 131 | EXTERNAL_IP=$(kubectl get svc helloworld \ 132 | -ojsonpath='{.status.loadBalancer.ingress[0].ip}') 133 | 134 | curl http://${EXTERNAL_IP}:8080 135 | ``` 136 | 137 | ## Learn More 138 | 139 | * [Kubernetes from Basic to Advanced code lab](https://bit.ly/k8s-lab) 140 | * [Spring Boot on GCP code lab](https://bit.ly/spring-gcp-lab) 141 | * [Spring to Kubernetes Faster and Easier](https://saturnism.me/talk/kubernetes-spring-java-best-practices/) 142 | 143 | --------------------------------------------------------------------------------