Ratnopam Chakrabarti is a software developer currently working for Ericsson Inc. He has been focused on IoT, machine-to-machine technologies, connected cars, and smart city domains for quite a while. He loves learning new technologies and putting them to work. When he’s not working, he enjoys spending time with his 3-year-old son.
Introduction
Running Couchbase as a Docker container is fairly easy. Simply inherit from the latest, official Couchbase image and add your customized behavior according to your requirement. In this post, I am going to show how you can fire up a web application using Spring Boot, Vaadin, and of course Couchbase (as backend)– all using Docker.
This is part one of a two-part series where I am going to describe ways to run a fully featured web application powered by Couchbase as the NoSQL backend using Docker toolsets. In this post, I will describe the steps to set up and configure a Couchbase environment using Docker; I will also mention ways to Dockerize the web application (in this case, it’s a Spring Boot application with Vaadin) and talk to the Couchbase backend for the CRUD operations.
Prerequisites
Docker needs to be set up and working. Please refer to the following link for details of the installation: https://docs.docker.com/engine/installation/ If you are on macOS or Windows 10, you can go for native Docker packages. If you are on an earlier version of Windows (7 or 8) like me, then you can use Docker Toolbox which comes with Docker achine.
The Application
Ours is a simple CRUD application for maintaining a bookstore. Users of the application can add books by entering information such as title and/or author, and can then view the list of books, edit some information, and even delete the books. The app is built on Spring Boot. The backend is powered by Couchbase 4.6, and for the front-end I have used Vaadin 7 since it has pretty neat integration with the Spring Boot framework.
The main steps to build this app are listed below:
- Run and configure Couchbase 4.6, including setting up the bucket and services using Docker.
- Build the application using Spring Boot, Vaadin, and Couchbase.
- Dockerize and run the application.
Run Couchbase 4.6 using Docker
Check your Docker host IP. You can use:
1 2 3 4 5 |
docker-machine ip default to find out the docker_host ip address. You can also check the environment variables by doing printenv | grep -i docker_host; it would show something like this -> DOCKER_HOST=tcp://192.168.99.100:2376 |
The next task is to write the Dockerfile to run and configure Couchbase. For our application to talk to the Couchbase backend, we need to set up a bucket named “books” and also enable the index query services on the Couchbase node. The Dockerfile to all of this can be found here.
The Dockerfile uses a configuration script to set up the Couchbase node. Couchbase offers REST endpoints that can easily enable services such as querying, N1QL, and index. One can also set up buckets using these REST APIs. The configuration script can be downloaded from here.
Let’s try to build and run the Couchbase image now.
Go to the directory where the Dockerfile is.
1 2 3 4 5 6 7 8 9 |
Build the image -> docker build -t <chakrar27>/couchbase:books . Replace chakrar27 by your image-prefix or docker hub id. Once the image is built, verify by doing $ docker images |
REPOSITORY TAG IMAGE ID CREATED SIZE
chakrar27/couchbase books 93e7ba199eef 1 hour ago 581 MB
couchbase latest 337dab68d2d1 9 days ago 581 MB
Run the image by typing
1 |
docker run -p 8091-8093:8091-8093 -p 8094:8094 -p 11210:11210 chakrar27/couchbase:books |
Sample output:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
Starting Couchbase Server -- Web UI available at http://<ip>:8091 and logs available in /opt/couchbase/var/lib/couchbase/logs Start configuring env by calling REST endpoints Note: Unnecessary use of -X or --request, POST is already inferred. * Trying 192.168.99.100... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 127.0.0.1 (127.0.0.1) port 8091 (#0) > POST /pools/default HTTP/1.1 > Host: 127.0.0.1:8091 > User-Agent: curl/7.49.1-DEV > Accept: */* > Content-Length: 55 > Content-Type: application/x-www-form-urlencoded > } [55 bytes data] * upload completely sent off: 55 out of 55 bytes < HTTP/1.1 200 OK < Server: Couchbase Server < Pragma: no-cache < Date: Fri, 24 Mar 2017 03:20:51 GMT < Content-Length: 0 < Cache-Control: no-cache < 100 55 0 0 100 55 0 2966 --:--:-- --:--:-- --:--:-- 3666 * Connection #0 to host 127.0.0.1 left intact * Trying 127.0.0.1... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 127.0.0.1 (127.0.0.1) port 8091 (#0) > POST /node/controller/setupServices HTTP/1.1 > Host: 127.0.0.1:8091 > User-Agent: curl/7.49.1-DEV > Accept: */* > Content-Length: 32 > Content-Type: application/x-www-form-urlencoded > } [32 bytes data] * upload completely sent off: 32 out of 32 bytes < HTTP/1.1 200 OK < Server: Couchbase Server < Pragma: no-cache < Date: Fri, 24 Mar 2017 03:20:56 GMT < Content-Length: 0 < Cache-Control: no-cache < 100 32 0 0 100 32 0 3389 --:--:-- --:--:-- --:--:-- 4000 * Connection #0 to host 127.0.0.1 left intact % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 180 100 152 100 28 8068 1486 --:--:-- --:--:-- --:--:-- 8444 HTTP/1.1 200 OK Server: Couchbase Server Pragma: no-cache Date: Fri, 24 Mar 2017 03:21:01 GMT Content-Type: application/json Content-Length: 152 Cache-Control: no-cache {"storageMode":"memory_optimized","indexerThreads":0,"memorySnapshotInterval":200,"stableSnapshotInterval":5000,"maxRollbackPoints":5,"logLevel":"info"}Note: Unnecessary use of -X or --request, POST is already inferred. * Trying 127.0.0.1... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 127.0.0.1 (127.0.0.1) port 8091 (#0) > POST /settings/web HTTP/1.1 > Host: 127.0.0.1:8091 > User-Agent: curl/7.49.1-DEV > Accept: */* > Content-Length: 50 > Content-Type: application/x-www-form-urlencoded > } [50 bytes data] * upload completely sent off: 50 out of 50 bytes < HTTP/1.1 200 OK < Server: Couchbase Server < Pragma: no-cache < Date: Fri, 24 Mar 2017 03:21:01 GMT < Content-Type: application/json < Content-Length: 44 < Cache-Control: no-cache < { [44 bytes data] 100 94 100 44 100 50 1554 1765 --:--:-- --:--:-- --:--:-- 2380 * Connection #0 to host 127.0.0.1 left intact {"newBaseUri":"http://127.0.0.1:8091/"}bucket set up start bucket name = books Note: Unnecessary use of -X or --request, POST is already inferred. * Trying 127.0.0.1... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0* Connected to 127.0.0.1 (127.0.0.1) port 8091 (#0) * Server auth using Basic with user 'Administrator' > POST /pools/default/buckets HTTP/1.1 > Host: 127.0.0.1:8091 > Authorization: Basic QWRtaW5pc3RyYXRvcjpwYXNzd29yZA== > User-Agent: curl/7.49.1-DEV > Accept: */* > Content-Length: 55 > Content-Type: application/x-www-form-urlencoded > } [55 bytes data] * upload completely sent off: 55 out of 55 bytes < HTTP/1.1 202 Accepted < Server: Couchbase Server < Pragma: no-cache < Location: /pools/default/buckets/books < Date: Fri, 24 Mar 2017 03:21:01 GMT < Content-Length: 0 < Cache-Control: no-cache < 100 55 0 0 100 55 0 748 --:--:-- --:--:-- --:--:-- 820 * Connection #0 to host 127.0.0.1 left intact bucket set up done /entrypoint.sh couchbase-server |
Verify the configuration by typing http://192.168.99.100:8091 into your favorite browser.
Type “Administrator” as Username and “password” in the Password field and click Sign In.
Check the settings of the Couchbase node and verify that it is according to the configure.sh we used above.
The bucket “books”.
At this point our back-end Couchbase infrastructure is up and running. We now need to build an application that can use this backend to build something functional.
Build the application using Spring Boot, Vaadin, and Couchbase
Go to start.spring.io and add Couchbase as a dependency. This would place spring-data-couchbase libraries in the application classpath. Since Couchbase is considered a first-class citizen of the Spring Boot ecosystem, we can make use of the Spring Boot auto-configuration feature to access the Couchbase bucket at runtime.
Also, add Vaadin as a dependency in the project. We are going to use it for building the UI layer.
The project object model (pom) file can be found here.
We create a Couchbase repository like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
@ViewIndexed(designDoc = "book") @N1qlPrimaryIndexed @N1qlSecondaryIndexed(indexName = "bookSecondaryIndex") public interface BookStoreRepository extends CouchbasePagingAndSortingRepository<Book, Long> { List<Book> findAll(); List<Book> findByAuthor(String author); List<Book> findByTitleStartsWithIgnoreCase(String title); List<Book> findByCategory(String category); } |
The annotations ensure that a View named “book” will be supplied at runtime to support view-based queries. A primary index will be created to support N1QL queries. In addition, a secondary index will also be provided.
The methods have been defined to return List<Book>. We don’t have to provide any implementation since that is already provided behind the scenes by the spring-data-couchbase.
We need to define the entity, which in our case is Book. We annotate it with @Document.
@Document
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class Book { @Id private String id = UUID.randomUUID().toString(); private String title; private String author; private String isbn; private String category; } |
To enable auto-configuration, use application.properties or application.yml file as shown below:
1 2 3 4 5 6 7 |
spring.couchbase.bootstrap-hosts=127.0.0.1 spring.couchbase.bucket.name=books spring.couchbase.bucket.password= spring.data.couchbase.auto-index=true |
One thing to note here is that when the application container runs, it would need to connect to the Couchbase container and set up the auto-configuration. The property spring.couchbase.bootstrap-hosts lists the IP address of the Couchbase node. Here, I have specified 127.0.0.1 which is not going to work since at runtime, the app container will not find the Couchbase container running in that IP. So we need to pass an environment variable (env variable) when running the Docker image of the application.
In order to pass an env variable as mentioned above, we need to write the Dockerfile of the application such that the value of the spring.couchbase.bootstrap-hosts property can be passed as an env variable. Here’s the Dockerfile of the app:
1 2 3 4 5 6 7 8 9 |
FROM frolvlad/alpine-oraclejdk8:full VOLUME /tmp ADD target/bookstore-1.0.0-SNAPSHOT.jar app.jar RUN sh -c 'touch /app.jar' CMD java -Dspring.couchbase.bootstrap-hosts=$HOSTS -Djava.security.egd=file:/dev/./urandom -jar /app.jar |
As you can see, we are basically overriding the value of the spring.couchbase.bootstrap-hosts property defined in the application.properties file by the env variable HOSTS.
This is pretty much all we have to do to wire Spring Boot with Couchbase.
UI (U and I)
For UI, we make use of the spring-vaadin integration. I am using version 7.7.3 of Vaadin, vaadin-spring version 1.1.0, and “Viritin,” a useful Vaadin add-on. To install Viritin, add the following dependency:
1 2 3 4 5 6 7 8 9 |
<dependency> <groupId>org.vaadin</groupId> <artifactId>viritin</artifactId> <version>1.57</version> </dependency> |
Annotate the UI class as
@SpringUI
@Theme(“valo”)
public class BookstoreUI extends UI {
//////
}
And then hook the repository methods with the UI elements.
A bean that implements the CommandLineRunner interface is used to prepopulate the database with some initial values.
For full source code, refer to this link.
Dockerize the application
Using Maven, it’s very easy to Dockerize an application using Spotify’s docker-maven plugin. Please check the pom.xml file plugin section.
Alternatively, you can build using Docker command line ->
1 |
docker build -t chakrar27/books:standalone . |
And then run the image -> Note that we need to pass the value of the variable HOSTS that our app container is going to look for when it tries to connect to the Couchbase container. The run command would look like:
1 |
docker run -p 8080:8080 -e HOSTS=192.168.99.100 chakrar27/books:standalone |
Once the application is started, navigate to http://192.168.99.100:8080/
The following page shows up:
An entry can be edited and saved.
There’s also a neat filtering feature provided by the N1QL query running underneath.
Users can also add a new book and delete an existing record. All the CRUD (Create/Read/Update/Delete) features of this simple application are powered by Couchbase N1QL queries, which we enabled by creating the “BookStoreRepository,” and, in turn, extends the “CouchbasePagingAndSortingRepository.”
This post is part of the Couchbase Community Writing Program
[…] run a Couchbase powered, fully functional Spring Boot web application using the Docker toolset. In part one of the series, I demonstrated how to run two Docker containers to run a functional application with […]