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
Welcome to the part two of the series where I describe how to develop and 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 a presentable UI. The two Docker containers that we were running are:
- A Couchbase container with preconfigured settings
- An application container talking to the Couchbase container (Run in step 1)
While this method is useful, it’s not fully automated – meaning the automated orchestration is not there. You have to run two different Docker run commands to run the entire setup.
Is there a way to build and run the application container which also triggers running of the Couchbase container? Of course there’s a way.
Enter Docker Compose
Using Docker Compose, you can orchestrate the running of multi-container environments, which is exactly what we need for our use case. We need to run the Couchbase container first, and then the application container should run and talk to the Couchbase container.
Here’s the docker-compose.yml file to achieve this:
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 |
version: "2" services: app: build: . ports: - 8080:8080 environment: - BUCKET_NAME=books - HOST=192.168.99.100 depends_on: - db db: image: chakrar27/couchbase:books ports: - 8091:8091 - 8092:8092 - 8093:8093 - 8094:8094 - 11210:11210 |
Our app “depends_on” the db image which is the Couchbase container. In other words, the Couchbase container runs first and then the app container starts running. There’s one potential issue here: the “depends_on” keyword doesn’t guarantee that the Couchbase container has finished configuring the image and started running. All it ensures is that the container is started first; it doesn’t check whether the container is actually running or ready to be accepting requests by an application. In order to ensure that the Couchbase container is actually running and that all the pre-configuration steps, such as setting up the query, index services, and bucket, is completed, we need to do a check from the application container.
Here’s the Dockerfile of the app container that invokes a script which, in turn, checks whether the bucket “books” has been set up already or not. It goes into a loop till the bucket is set up and then triggers the app container.
https://github.com/ratchakr/bookstoreapp/blob/master/Dockerfile-v1
The script can be seen at https://github.com/ratchakr/bookstoreapp/blob/master/run_app.sh
The script does the following things:
It uses the REST endpoint supported by Couchbase for querying the bucket.
Curl is used to call REST endpoints. Installation of curl is covered in the Dockerfile of the application.
The script parses the JSON response of the REST call by using a tool called jq.
If the bucket is set up, it then runs the app container; otherwise it waits for the bucket to be set up first.
It’s worth mentioning that more checks, such as verifying if the index service and the query service are set up properly or not, can be added in the shell script to make it more robust. One word of caution is to confirm your particular use case and requirement before following the docker-compose approach; there’s not a sure-fire way to determine if the Couchbase db container is fully up and running and ready to serve requests from the client application. Some of the approaches that might work are as follows:
- If you have a preconfigured bucket, you can test if the bucket exists
- Check if the indexes are in place
- If you know the record-count in a bucket (let’s say for a .csv file which has been imported into a bucket at the time of initial data load), you can check if the count matches the number of records in the .csv file). For our use case, the one mentioned above works nicely.
Build and Run
Now that we have our docker-compose file and Dockerfile, we can build the application image by using the simple docker-compose up command.
Here’s the output snippet from the Docker console:
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 |
$ docker-compose up Creating network "bookstoreapp_default" with the default driver Pulling db (chakrar27/couchbase:books)... books: Pulling from chakrar27/couchbase Digest: sha256:4bc356a1f2b5b3d7ee3daf10cd5c55480ab831a0a147b07f5b14bea3de909fd9 Status: Downloaded newer image for chakrar27/couchbase:books Building app Step 1/8 : FROM frolvlad/alpine-oraclejdk8:full full: Pulling from frolvlad/alpine-oraclejdk8 Digest: sha256:a344745faa77a9aa5229f26bc4f5c596d13bcfc8fcac051a701b104a469aff1f Status: Downloaded newer image for frolvlad/alpine-oraclejdk8:full ---> 5f7037acb78d Step 2/8 : VOLUME /tmp ---> Running in 7d18e0b90bfd ---> 6a43ccb712dc Removing intermediate container 7d18e0b90bfd Step 3/8 : ADD target/bookstore-1.0.0-SNAPSHOT.jar app.jar ---> a3b4bf7745e0 Removing intermediate container 0404f1d094d3 Step 4/8 : RUN sh -c 'touch /app.jar' ---> Running in 64d1c82a0694 ---> 1ec5a68cafa9 Removing intermediate container 64d1c82a0694 Step 5/8 : RUN apk update && apk add curl ---> Running in 1f912e8341bd fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/main/x86_64/APKINDEX.tar.gz fetch http://dl-cdn.alpinelinux.org/alpine/v3.5/community/x86_64/APKINDEX.tar.gz v3.5.2-16-g53ad101cf8 [http://dl-cdn.alpinelinux.org/alpine/v3.5/main] v3.5.2-14-gd7ba0e189f [http://dl-cdn.alpinelinux.org/alpine/v3.5/community] OK: 7961 distinct packages available (1/4) Installing ca-certificates (20161130-r1) (2/4) Installing libssh2 (1.7.0-r2) (3/4) Installing libcurl (7.52.1-r2) (4/4) Installing curl (7.52.1-r2) Executing busybox-1.25.1-r0.trigger Executing ca-certificates-20161130-r1.trigger Executing glibc-bin-2.25-r0.trigger OK: 12 MiB in 18 packages ---> 8f99863af926 Removing intermediate container 1f912e8341bd Step 6/8 : ADD run_app.sh . ---> cedb8d545070 Removing intermediate container 8af5ac3ab0a0 Step 7/8 : RUN chmod +x run_app.sh ---> Running in 74a141de2f52 ---> 77ffd7425bea Removing intermediate container 74a141de2f52 Step 8/8 : CMD sh run_app.sh ---> Running in 6f81c8ebaa37 ---> 56a3659005ef Removing intermediate container 6f81c8ebaa37 Successfully built 56a3659005ef Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. Creating bookstoreapp_db_1 Creating bookstoreapp_app_1 Attaching to bookstoreapp_db_1, bookstoreapp_app_1 db_1 | docker host ip = 192.168.99.100 db_1 | sleeping... app_1 | Starting application run script........... app_1 | couchbase is running on 192.168.99.100 app_1 | bucket to check is books db_1 | < Date: Fri, 24 Mar 2017 06:53:00 GMT db_1 | < Content-Length: 0 db_1 | < Cache-Control: no-cache db_1 | < 100 55 0 0 100 55 0 827 --:--:-- --:--:-- --:--:-- 833 db_1 | * Connection #0 to host 127.0.0.1 left intact db_1 | bucket set up done app_1 | response from cb app_1 | ************************************************ app_1 | ************************************************ app_1 | response from cb books app_1 | ************************************************ app_1 | ************************************************ app_1 | bucket is now ready bucket name books app_1 | Run application container now app_1 | ************************************************ app_1 | app_1 | . ____ _ __ _ _ app_1 | /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ app_1 | ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ app_1 | \\/ ___)| |_)| | | | | || (_| | ) ) ) ) app_1 | ' |____| .__|_| |_|_| |_\__, | / / / / app_1 | =========|_|==============|___/=/_/_/_/ app_1 | :: Spring Boot :: (v1.4.2.RELEASE) app_1 | app_1 | 2017-03-24 06:53:59.839 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book Details = Book [id=06bad9c4-85fc-4c0b-83a7-ad21b2fdd405, title=The Immortal Irishman, author=Timothy Egan, isbn=ISBN444, category=History] app_1 | 2017-03-24 06:53:59.839 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book Details = Book [id=328eaf44-edff-43c6-9f55-62d7e095256d, title=The Kite Runner, author=Khaled Hosseini, isbn=ISBN663, category=Fiction] app_1 | 2017-03-24 06:53:59.839 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book Details = Book [id=56882f5a-d466-457f-82c1-1c3bca0c6d75, title=Breaking Blue, author=Timothy Egan, isbn=ISBN777, category=Thriller] app_1 | 2017-03-24 06:53:59.839 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book Details = Book [id=845a2fe8-cbbf-4780-b216-41abf86d7d61, title=History of Mankind, author=Gabriel Garcia, isbn=ISBN123, category=History] app_1 | 2017-03-24 06:53:59.840 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book Details = Book [id=9d2833c3-e005-4c4f-98f9-75b69bbb7bf5, title=The Night Gardener, author=Eric Fan, isbn=ISBN333, category=Kids Books] app_1 | 2017-03-24 06:53:59.840 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book Details = Book [id=5756bf4d-551c-429e-8bc3-2339dc065ff8, title=Grit: The Power of Passion and Perseverance, author=Angela Duckworth, isbn=ISBN555, category=Business] app_1 | 2017-03-24 06:53:59.840 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book Details = Book [id=e8e34f30-6fdf-4ca7-9cef-e06f504f8778, title=War and Turpentine, author=Stefan Hertmans, isbn=ISBN222, category=Fiction] app_1 | 2017-03-24 06:54:00.234 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Books by Timothy Egan = Book [id=06bad9c4-85fc-4c0b-83a7-ad21b2fdd405, title=The Immortal Irishman, author=Timothy Egan, isbn=ISBN444, category=History] app_1 | 2017-03-24 06:54:00.238 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Books by Timothy Egan = Book [id=56882f5a-d466-457f-82c1-1c3bca0c6d75, title=Breaking Blue, author=Timothy Egan, isbn=ISBN777, category=Thriller] app_1 | 2017-03-24 06:54:00.346 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book Starting with title 'The' = Book [id=06bad9c4-85fc-4c0b-83a7-ad21b2fdd405, title=The Immortal Irishman, author=Timothy Egan, isbn=ISBN444, category=History] app_1 | 2017-03-24 06:54:00.349 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book Starting with title 'The' = Book [id=328eaf44-edff-43c6-9f55-62d7e095256d, title=The Kite Runner, author=Khaled Hosseini, isbn=ISBN663, category=Fiction] app_1 | 2017-03-24 06:54:00.349 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book Starting with title 'The' = Book [id=9d2833c3-e005-4c4f-98f9-75b69bbb7bf5, title=The Night Gardener, author=Eric Fan, isbn=ISBN333, category=Kids Books] app_1 | 2017-03-24 06:54:00.443 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book in Fiction = Book [id=328eaf44-edff-43c6-9f55-62d7e095256d, title=The Kite Runner, author=Khaled Hosseini, isbn=ISBN663, category=Fiction] app_1 | 2017-03-24 06:54:00.453 INFO 31 --- [ main] c.chakrar.sample.books.BookStoreRunner : Book in Fiction = Book [id=e8e34f30-6fdf-4ca7-9cef-e06f504f8778, title=War and Turpentine, author=Stefan Hertmans, isbn=ISBN222, category=Fiction] app_1 | 2017-03-24 06:54:02.745 INFO 31 --- [nio-8080-exec-1] o.v.spring.servlet.Vaadin4SpringServlet : Could not find a SystemMessagesProvider in the application context, using default app_1 | 2017-03-24 06:54:02.753 INFO 31 --- [nio-8080-exec-1] o.v.spring.servlet.Vaadin4SpringServlet : Custom Vaadin4Spring servlet initialization completed app_1 | 2017-03-24 06:54:02.864 INFO 31 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet 'dispatcherServlet' app_1 | 2017-03-24 06:54:02.865 INFO 31 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet 'dispatcherServlet': initialization started |
At this point our application is up and running with a single docker-compose orchestration command.
Type 192.168.99.100:8080 into the browser; you should see the following screen:
Docker Compose is a nice way to orchestrate multi-container Docker environments. It has almost similar command chains as “docker” command sets. For instance, to see a list of running containers, you simply type:
docker-compose ps > which would give you
1 2 3 4 5 6 7 8 9 |
$ docker-compose ps Name Command State Ports --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- bookstoreapp_app_1 /bin/sh -c sh run_app.sh Up 0.0.0.0:8080->8080/tcp bookstoreapp_db_1 /entrypoint.sh /opt/couchb ... Up 11207/tcp, 0.0.0.0:11210->11210/tcp, 11211/tcp, 18091/tcp, 18092/tcp, 18093/tcp, 0.0.0.0:8091->8091/tcp, 0.0.0.0:8092->8092/tcp, 0.0.0.0:8093->8093/tcp, 0.0.0.0:8094->8094/tcp |
The name of the containers are shown in bold here.
If you need to stop or tear down your orchestrated environment with Docker Compose, you can do that with the docker-compose down command as shown below:
A sample run produces:
1 2 3 4 5 6 7 8 9 10 11 |
$ docker-compose down Stopping bookstoreapp_app_1 ... done Stopping bookstoreapp_db_1 ... done Removing bookstoreapp_app_1 ... done Removing bookstoreapp_db_1 ... done Removing network bookstoreapp_default |
Now, if you do a docker-compose ps, it shows that no container is currently running.
1 2 3 4 5 |
$ docker-compose ps Name Command State Ports --------------------------------------------------------------- |
You can also use Docker compose for an automated test environment where you fire up your Docker containers, run the tests, then tear down the complete infrastructure – all with Compose. For a detailed overview of Docker compose, please visit the official website.
This post is part of the Couchbase Community Writing Program