diff --git a/spring-geode-docs/src/docs/asciidoc/_includes/docker.adoc b/spring-geode-docs/src/docs/asciidoc/_includes/docker.adoc new file mode 100644 index 00000000..a5865914 --- /dev/null +++ b/spring-geode-docs/src/docs/asciidoc/_includes/docker.adoc @@ -0,0 +1,377 @@ +[[geode-docker]] +== Docker +:docker-docs-url: https://docs.docker.com +:docker-site-url: https://www.docker.com/ +:geode-name: Apache Geode +:testcontainers-url: https://www.testcontainers.org + +The state of modern software application development is moving towards https://www.docker.com/resources/what-container[_containerization_]. +Containers offer a controlled environment to predictably build (configure & package), run and manage your applications +in a reliable and repeatable manner regardless of context. The intrinsic benefit of using Containers is a no brainer. + +Understandably, {docker-site-url}[Docker's] popularity took off like wildfire given its highly powerful and simplified +model for creating, using and managing Containers to run packaged applications. + +Docker's ecosystem is also quite impressive, with the event of {testcontainers-url}[Testcontainers] +along with Spring Boot's now {spring-boot-docs-html}/spring-boot-features.html#building-docker-images[dedicated support] +to create packaged Spring Boot apps in {docker-docs-url}/get-started/overview/#docker-objects[Docker Images] +that are then later run in a Docker Container. + +TIP: Also see {spring-boot-docs-html}/deployment.html#containers-deployment[Deploying to Containers] to learn more. + +{geode-name} is no exception to being able to run in a controlled, containerized environment. The goal of this chapter +is to get you started running {geode-name} in a Container and interfacing to a containerized {geode-name} cluster from +your Spring Boot, {geode-name} client applications. + +This chapter does not cover how to run your Spring Boot, {geode-name} client applications in a Container since that is +already covered by Spring Boot (again, see {spring-boot-docs-html}/spring-boot-features.html#building-docker-images[here] +and {spring-boot-docs-html}/deployment.html#containers-deployment[here], along with Docker's {docker-docs-url}/get-started/overview/[docs]). +Instead, our focus is on how to run an {geode-name} cluster in a Container and connect to it from a Spring Boot, +{geode-name} client application, regardless of whether the app is running in a Container or not. + +Let's get started. + +[[geode-docker-image]] +=== Acquiring the {geode-name} Docker Image + +To run an {geode-name} cluster inside a Docker Container you must first acquire the Docker Image. + +While {geode-name}'s official {apache-geode-docs}[documentation] is less than clear on how to use {geode-name} in Docker, +we find a bit of relief in the {apache-geode-wiki}/How+to+use+Geode+on+Docker[Wiki]. However, for a complete write up, +please refer to the instructions in the https://github.com/markito/geode-docker#building-the-container-image[README] +from this https://github.com/markito/geode-docker[GitHub Repo]. + +The {geode-name} Docker Image can be acquired from https://hub.docker.com/r/apachegeode/geode/[Docker Hub]. + +NOTE: You must have {docker-docs-url}/get-docker[Docker] installed on your local system to complete the following steps. + +Effectively, the high-level steps are as follows: + +1) Acquire the {geode-name} Docker Image from Docker Hub using the `docker pull` command from the command-line: + +.Download/Install the {geode-name} Docker Image +[source,text] +---- +$ docker pull apachegeode/geode +Using default tag: latest +latest: Pulling from apachegeode/geode +Digest: sha256:6a6218f22a2895bb706175727c7d76f654f9162acac22b2d950d09a2649f9cf4 +Status: Image is up to date for apachegeode/geode:latest +docker.io/apachegeode/geode:latest +---- + +Instead of pulling from the `nightly` TAG as suggested, the Spring Team highly recommends that you pull from the +`latest` TAG, which pulls a stable, production-grade {geode-name} Docker Image based on the latest {geode-name} +GA version. + +2) Verify the {geode-name} Docker Image was downloaded and installed successfully: + +[source,text] +---- +$ docker image ls +REPOSITORY TAG IMAGE ID CREATED SIZE +apachegeode/geode latest a2e210950712 2 months ago 224MB +cloudfoundry/run base-cnb 3a7d172559c2 8 weeks ago 71.2MB +open-liberty 19.0.0.9-webProfile8 dece75feff1a 3 months ago 364MB +tomee 11-jre-8.0.0-M3-webprofile 0d03e4d395e6 3 months ago 678MB +... +---- + +Now, you are ready to run {geode-name} in a Docker Container. + +[[geode-docker-container]] +=== Running {geode-name} in a Docker Container + +Now that we have acquired the Docker Image, we can run {geode-name} in a Docker Container. Use the following +`docker run` command to start the {geode-name} Docker Container: + +.Start the {geode-name} Docker Container +[source,text] +---- +$ docker run -it -p 10334:10334 -p 40404:40404 -p 1099:1099 -p 7070:7070 -p 7575:7575 apachegeode/geode + _________________________ __ + / _____/ ______/ ______/ /____/ / + / / __/ /___ /_____ / _____ / + / /__/ / ____/ _____/ / / / / +/______/_/ /______/_/ /_/ 1.12.0 + +Monitor and Manage Apache Geode +gfsh> +---- + +Since the {geode-name} Docker Container was started in interactive mode, you must open a separate command-line shell +to verify the {geode-name} Docker Container is in fact running: + +.Verify the {geode-name} Docker Container is Running +[source,text] +---- +$ docker container ls +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +3b30b9ffc5dc apachegeode/geode "gfsh" 44 seconds ago Up 43 seconds 0.0.0.0:1099->1099/tcp, 0.0.0.0:7070->7070/tcp, 0.0.0.0:7575->7575/tcp, 0.0.0.0:10334->10334/tcp, 0.0.0.0:40404->40404/tcp, 8080/tcp awesome_khorana + +---- + +Of course, we know that the {geode-name} Docker Container is running since we ended up at a _Gfsh_ command prompt in the +interactive shell. + +We also mapped ports between the Docker Container and the host system, exposing well-known ports used by {geode-name} +server-side, cluster processes, such as Locators and Cache Servers. + +.{geode-name} Ports +[width=30%, options="header", cols="2,1"] +|=== +| Process | Port + +| HTTP | 7070 + +| Locator | 10334 + +| Manager | 1099 + +| Server | 40404 + +|=== + +It is unfortunate that the {geode-name} Docker Image only gives you a _Gfsh_ command prompt, leaving you to provision a +cluster. It would have been more useful to provide preconfigured Docker Images with different {geode-name} cluster +configurations, such as 1 Locator + 1 Server, or 2 Locators + 4 Servers, etc. But, no matter, we can start the cluster +ourselves. + +[[geode-docker-cluster]] +=== Run an {geode-name} Cluster + +From inside the {geode-name} Docker Container we can start a Locator and a Server. + +.Start {geode-name} Locator & Server +[source, text] +---- +gfsh>start locator --name=LocatorOne --log-level=config --hostname-for-clients=localhost +Starting a Geode Locator in /LocatorOne... +......... +Locator in /LocatorOne on 3b30b9ffc5dc[10334] as LocatorOne is currently online. +Process ID: 167 +Uptime: 9 seconds +Geode Version: 1.12.0 +Java Version: 1.8.0_212 +Log File: /LocatorOne/LocatorOne.log +JVM Arguments: -Dgemfire.enable-cluster-configuration=true -Dgemfire.load-cluster-configuration-from-dir=false -Dgemfire.log-level=config -Dgemfire.launcher.registerSignalHandlers=true -Djava.awt.headless=true -Dsun.rmi.dgc.server.gcInterval=9223372036854775806 +Class-Path: /geode/lib/geode-core-1.12.0.jar:/geode/lib/geode-dependencies.jar + +Successfully connected to: JMX Manager [host=3b30b9ffc5dc, port=1099] + +Cluster configuration service is up and running. + + +gfsh>start server --name=ServerOne --log-level=config --hostname-for-clients=localhost +Starting a Geode Server in /ServerOne... +....... +Server in /ServerOne on 3b30b9ffc5dc[40404] as ServerOne is currently online. +Process ID: 267 +Uptime: 7 seconds +Geode Version: 1.12.0 +Java Version: 1.8.0_212 +Log File: /ServerOne/ServerOne.log +JVM Arguments: -Dgemfire.default.locators=172.17.0.2[10334] -Dgemfire.start-dev-rest-api=false -Dgemfire.use-cluster-configuration=true -Dgemfire.log-level=config -Dgemfire.launcher.registerSignalHandlers=true -Djava.awt.headless=true -Dsun.rmi.dgc.server.gcInterval=9223372036854775806 +Class-Path: /geode/lib/geode-core-1.12.0.jar:/geode/lib/geode-dependencies.jar + + +gfsh>list members +Member Count : 2 + + Name | Id +---------- | -------------------------------------------------------------- +LocatorOne | 172.17.0.2(LocatorOne:167:locator):41000 [Coordinator] +ServerOne | 172.17.0.2(ServerOne:267):41001 + + +gfsh>describe member --name=LocatorOne +Name : LocatorOne +Id : 172.17.0.2(LocatorOne:167:locator):41000 +Host : 3b30b9ffc5dc +Regions : +PID : 167 +Groups : +Used Heap : 50M +Max Heap : 443M +Working Dir : /LocatorOne +Log file : /LocatorOne/LocatorOne.log +Locators : 172.17.0.2[10334] + + +gfsh>describe member --name=ServerOne +Name : ServerOne +Id : 172.17.0.2(ServerOne:267):41001 +Host : 3b30b9ffc5dc +Regions : +PID : 267 +Groups : +Used Heap : 77M +Max Heap : 443M +Working Dir : /ServerOne +Log file : /ServerOne/ServerOne.log +Locators : 172.17.0.2[10334] + +Cache Server Information +Server Bind : +Server Port : 40404 +Running : true + +Client Connections : 0 + +---- + +We now have an {geode-name} cluster running with 1 Locator and 1 Server inside a Docker Container. We deliberately +started the cluster with a minimal configuration. For example, we have no Regions in which to store data: + +[source,text] +---- +gfsh>list regions +No Regions Found + +---- + +But, that is OK. Once more, we want to showcase the full power of SBDG and let the Spring Boot application drive +the configuration of the {geode-name} cluster running in the Docker Container as required by the application. + +Let's have a quick look at our Spring Boot application. + +[[geode-docker-application]] +=== The Spring Boot, {geode-name} client application explained + +The Spring Boot, {geode-name} `ClientCache` application we will use to connect to our {geode-name} cluster running in +the Docker Container, appears as follows: + +.Spring Boot, {geode-name} Docker client application +[source,java] +---- +include::{docs-src-dir}/org/springframework/geode/docs/example/app/docker/SpringBootApacheGeodeDockerClientCacheApplication.java[tags=class] +---- + +Our `Customer` application domain model object type is defined as: + +.`Customer` class +[source,java] +---- +@Region("Customers") +class Customer { + + @Id + private Long id; + + private String name; + +} +---- + +And, we define a Spring Data CRUD _Repository_ to persist and access `Customers` stored in {geode-name}, +"/Customers" Region: + +.`CustomerRepository` interface +[source,java] +---- +interface CustomerRepository extends CrudRepository { + + Customer findByNameLike(String name); + +} +---- + +Our main class is annotated with `@SpringBootApplication` making it a proper Spring Boot application. + +We additionally annotate the main class with SBDG's `@EnableClusterAware` to automatically detect the {geode-name} +cluster running in the Docker Container as well as to push cluster configuration metadata from the application to +the cluster as required by the application. + +Specifically, the application requires that a Region called "Customers", as defined by the `@Region` mapping annotation +on the `Customer` application domain model class, exists on the server(s) in the cluster to persist `Customer` data. + +We use the SDG `@EnableEntityDefinedRegions` annotation to define the matching, client `PROXY` "Customers" Region. + +Optionally, we have also annotated our main class with SBDG's `@UseMemberName` annotation to give the `ClientCache` +a name, which will assert in the `assertClientCacheAndConfigureMappingPdxSerializer(:ClientCache)` method. + +The primary work performed by this application is done in the Spring Boot `ApplicationRunner` bean definition. We +essentially create a `Customer` instance, "Jon Doe", save "Jon Doe" to the "Customers" Region on the server(s) in +the cluster, and then query for "Jon Doe" using OQL, asserting that the result is equal to the expected. + +We log the output from the application's operations to see the application in action. + +[[geode-docker-application-run]] +=== Running the Spring Boot, {geode-name} client application + +When you run the Spring Boot, {geode-name} client, application, you should see output similar to: + +.Application log output +[source,text] +---- +/Library/Java/JavaVirtualMachines/jdk1.8.0_241.jdk/Contents/Home/bin/java ... + org.springframework.geode.docs.example.app.docker.SpringBootApacheGeodeDockerClientCacheApplication + + . ____ _ __ _ _ + /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ +( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ + \\/ ___)| |_)| | | | | || (_| | ) ) ) ) + ' |____| .__|_| |_|_| |_\__, | / / / / + =========|_|==============|___/=/_/_/_/ + :: Spring Boot :: (v2.3.0.RELEASE) + +Saving Customer [Customer(name=Jon Doe)]... +Querying for Customer [SELECT * FROM /Customers WHERE name LIKE '%Doe']... +Customer was [Customer(name=Jon Doe)] + +Process finished with exit code 0 +---- + +Now when we review the configuration of the cluster, we see that the "/Customers" Region was created +once the application has run: + +./Customers Region Configuration +[source,text] +---- +gfsh>list regions +List of regions +--------------- +Customers + + +gfsh>describe region --name=/Customers +Name : Customers +Data Policy : partition +Hosting Members : ServerOne + +Non-Default Attributes Shared By Hosting Members + + Type | Name | Value +------ | ----------- | --------- +Region | size | 1 + | data-policy | PARTITION + +---- + +Our "/Customers" Region contains a value, "Jon Doe", and we can verify this by running an OQL Query using _Gfsh_: + +.Query the "/Customers" Region +[source,text] +---- +gfsh>query --query="SELECT customer.name FROM /Customers customer" +Result : true +Limit : 100 +Rows : 1 + +Result +------- +Jon Doe + +---- + +Indeed, our application ran successfully! + +[[geode-docker-application-conclusion]] +=== Conclusion + +In this chapter, we saw how to connect a Spring Boot, {geode-name} `ClientCache` application to an {geode-name} cluster +running in a Docker Container. + +Later, we will provide more information on how to scale up, or rather scale out, our {geode-name} cluster running in +Docker. Additionally, we will provide details on how you can use {geode-name}'s Docker Image with **Testcontainers** +when writing _Integration Tests_, which will formally become part of the Spring Test for Apache Geode (STDG) project. diff --git a/spring-geode-docs/src/docs/asciidoc/index.adoc b/spring-geode-docs/src/docs/asciidoc/index.adoc index 56bca000..d5d9b226 100644 --- a/spring-geode-docs/src/docs/asciidoc/index.adoc +++ b/spring-geode-docs/src/docs/asciidoc/index.adoc @@ -16,6 +16,7 @@ John Blum :apache-geode-docs: https://geode.apache.org/docs/guide/{apache-geode-version} :apache-geode-javadoc: https://geode.apache.org/releases/latest/javadoc :apache-geode-website: https://geode.apache.org/ +:apache-geode-wiki: https://cwiki.apache.org/confluence/display/GEODE :pivotal-cloudcache-version: {master-pivotal-cloudcache-version} :pivotal-cloudcache-docs: https://docs.pivotal.io/p-cloud-cache/{pivotal-cloudcache-version} :pivotal-cloudcache-website: https://pivotal.io/pivotal-cloud-cache @@ -250,5 +251,6 @@ include::{include-dir}/geode-api-ext.adoc[] include::{include-dir}/actuator.adoc[] include::{include-dir}/session.adoc[] include::{include-dir}/cloudfoundry.adoc[] +include::{include-dir}/docker.adoc[] include::{include-dir}/samples.adoc[] include::{include-dir}/appendix.adoc[]