This blog will explain how to create multi-container application deployed on multiple hosts using Docker. This will be achieved using Docker Machine, Swarm and Compose.
Yes, all three tools together makes this blog that much more interesting!
The diagram explains the key components:
- Docker Machine is used to provision multiple Docker hosts
- Docker Swarm will be used to create a multi-host cluster
- Each node in Docker Swarm cluster is registered/discovered using Consul
- Multi-container application will be deployed using Docker Compose
- WildFly and Couchbase are provisioned on different hosts
- Docker multi-host networking is used for WildFly and Couchbase to communicate
In addition, Maven is used to configure Couchbase and deploy application to WildFly.
Latest instructions at Docker for Java Developers.
No story, just pure code, lets do it!
Create Discovery Service using Docker Machine
- Create a Machine that will host discovery service:
12345678910111213docker-machine create -d=virtualbox consul-machineRunning pre-create checks...Creating machine...Waiting for machine to be running, this may take a few minutes...Machine is running, waiting for SSH to be available...Detecting operating system of created instance...Provisioning created instance...Copying certs to the local machine directory...Copying certs to the remote machine...Setting Docker configuration on the remote daemon...To see how to connect Docker to this machine, run: docker-machine env consul-machine - Connect to this Machine:
123eval $(docker-machine env consul-machine) - Run Consul service using the following Compose file:
123456789myconsul:image: progrium/consulrestart: alwayshostname: consulports:- 8500:8500command: "-server -bootstrap"
1234567891011121314151617181920212223242526docker-compose up -dPulling myconsul (progrium/consul:latest)...latest: Pulling from progrium/consul3b4d28ce80e4: Pull completee5ab901dcf2d: Pull complete30ad296c0ea0: Pull complete3dba40dec256: Pull completef2ef4387b95e: Pull complete53bc8dcc4791: Pull complete75ed0b50ba1d: Pull complete17c3a7ed5521: Pull complete8aca9e0ecf68: Pull complete4d1828359d36: Pull complete46ed7df7f742: Pull completeb5e8ce623ef8: Pull complete049dca6ef253: Pull completebdb608bc4555: Pull complete8b3d489cfb73: Pull completec74500bbce24: Pull complete9f3e605442f6: Pull completed9125e9e799b: Pull completeDigest: sha256:8cc8023462905929df9a79ff67ee435a36848ce7a10f18d6d0faba9306b97274Status: Downloaded newer image for progrium/consul:latestCreating consul_myconsul_1
12345docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESf05d8dd11e7f progrium/consul "/bin/start -server -" 30 seconds ago Up 29 seconds 53/tcp, 53/udp, 8300-8302/tcp, 8400/tcp, 0.0.0.0:8500->8500/tcp, 8301-8302/udp consul_myconsul_1
Create Docker Swarm Cluster using Docker Machine
Swarm is fully integrated with Machine, and so is the easiest way to get started.
- Create a Swarm Master and point to the Consul discovery service:
1234567891011121314docker-machine create -d virtualbox --virtualbox-disk-size "5000" --swarm --swarm-master --swarm-discovery="consul://$(docker-machine ip consul-machine):8500" --engine-opt="cluster-store=consul://$(docker-machine ip consul-machine):8500" --engine-opt="cluster-advertise=eth1:2376" swarm-masterRunning pre-create checks...Creating machine...Waiting for machine to be running, this may take a few minutes...Machine is running, waiting for SSH to be available...Detecting operating system of created instance...Provisioning created instance...Copying certs to the local machine directory...Copying certs to the remote machine...Setting Docker configuration on the remote daemon...Configuring swarm...To see how to connect Docker to this machine, run: docker-machine env swarm-master--swarm
configures the Machine with Swarm--swarm-master
configures the created Machine to be Swarm master--swarm-discovery
defines address of the discovery service--cluster-advertise
advertise the machine on the network--cluster-store
designate a distributed k/v storage backend for the cluster--virtualbox-disk-size
sets the disk size for the created Machine to 5GB. This is required so that WildFly and Couchbase image can be downloaded on any of the nodes.
- Find some information about this machine:
1234docker-machine inspect --format='{{json .Driver}}' swarm-master{"Boot2DockerImportVM":"","Boot2DockerURL":"","CPU":1,"DiskSize":5000,"HostOnlyCIDR":"192.168.99.1/24","HostOnlyNicType":"82540EM","HostOnlyPromiscMode":"deny","IPAddress":"192.168.99.102","MachineName":"swarm-master","Memory":1024,"NoShare":false,"SSHPort":51972,"SSHUser":"docker","StorePath":"/Users/arungupta/.docker/machine","SwarmDiscovery":"consul://192.168.99.100:8500","SwarmHost":"tcp://0.0.0.0:3376","SwarmMaster":true,"VBoxManager":{}} - Connect to the master by using the command:
123eval "$(docker-machine env --swarm swarm-master)" - Find some information about the cluster:
1234567891011121314151617docker infoContainers: 2Images: 1Role: primaryStrategy: spreadFilters: health, port, dependency, affinity, constraintNodes: 1swarm-master: 192.168.99.102:2376└ Containers: 2└ Reserved CPUs: 0 / 1└ Reserved Memory: 0 B / 1.021 GiB└ Labels: executiondriver=native-0.2, kernelversion=4.1.13-boot2docker, operatingsystem=Boot2Docker 1.9.1 (TCL 6.4.1); master : cef800b - Fri Nov 20 19:33:59 UTC 2015, provider=virtualbox, storagedriver=aufsCPUs: 1Total Memory: 1.021 GiBName: d074fd97682e - Create a new Machine to join this cluster:
1234567891011121314docker-machine create -d virtualbox --virtualbox-disk-size "5000" --swarm --swarm-discovery="consul://$(docker-machine ip consul-machine):8500" --engine-opt="cluster-store=consul://$(docker-machine ip consul-machine):8500" --engine-opt="cluster-advertise=eth1:2376" swarm-node-01Running pre-create checks...Creating machine...Waiting for machine to be running, this may take a few minutes...Machine is running, waiting for SSH to be available...Detecting operating system of created instance...Provisioning created instance...Copying certs to the local machine directory...Copying certs to the remote machine...Setting Docker configuration on the remote daemon...Configuring swarm...To see how to connect Docker to this machine, run: docker-machine env swarm-node-01--swarm-master
is specified in this command. This ensure that the created Machines are worker nodes. - Create a second Swarm node to join this cluster:
1234567891011121314docker-machine create -d virtualbox --virtualbox-disk-size "5000" --swarm --swarm-discovery="consul://$(docker-machine ip consul-machine):8500" --engine-opt="cluster-store=consul://$(docker-machine ip consul-machine):8500" --engine-opt="cluster-advertise=eth1:2376" swarm-node-02Running pre-create checks...Creating machine...Waiting for machine to be running, this may take a few minutes...Machine is running, waiting for SSH to be available...Detecting operating system of created instance...Provisioning created instance...Copying certs to the local machine directory...Copying certs to the remote machine...Setting Docker configuration on the remote daemon...Configuring swarm...To see how to connect Docker to this machine, run: docker-machine env swarm-node-02 - List all the created Machines:
12345678docker-machine lsNAME ACTIVE DRIVER STATE URL SWARMconsul-machine - virtualbox Running tcp://192.168.99.100:2376swarm-master * virtualbox Running tcp://192.168.99.101:2376 swarm-master (master)swarm-node-01 - virtualbox Running tcp://192.168.99.102:2376 swarm-masterswarm-node-02 - virtualbox Running tcp://192.168.99.103:2376 swarm-masterconsul-machine
is a standalone machine where as all other machines are part of theswarm-master
cluster. The Swarm master is also identified by (master) in the SWARM column. - Connect to the Swarm cluster and find some information about it:
1234eval "$(docker-machine env --swarm swarm-master)"docker infoNote,
--swarm
is specified to connect to the Swarm cluster. Otherwise the command will connect toswarm-master
Machine only.This shows the output as:
123456789101112131415161718192021222324252627docker infoContainers: 4Images: 3Role: primaryStrategy: spreadFilters: health, port, dependency, affinity, constraintNodes: 3swarm-master: 192.168.99.102:2376└ Containers: 2└ Reserved CPUs: 0 / 1└ Reserved Memory: 0 B / 1.021 GiB└ Labels: executiondriver=native-0.2, kernelversion=4.1.13-boot2docker, operatingsystem=Boot2Docker 1.9.1 (TCL 6.4.1); master : cef800b - Fri Nov 20 19:33:59 UTC 2015, provider=virtualbox, storagedriver=aufsswarm-node-01: 192.168.99.103:2376└ Containers: 1└ Reserved CPUs: 0 / 1└ Reserved Memory: 0 B / 1.021 GiB└ Labels: executiondriver=native-0.2, kernelversion=4.1.13-boot2docker, operatingsystem=Boot2Docker 1.9.1 (TCL 6.4.1); master : cef800b - Fri Nov 20 19:33:59 UTC 2015, provider=virtualbox, storagedriver=aufsswarm-node-02: 192.168.99.104:2376└ Containers: 1└ Reserved CPUs: 0 / 1└ Reserved Memory: 0 B / 1.021 GiB└ Labels: executiondriver=native-0.2, kernelversion=4.1.13-boot2docker, operatingsystem=Boot2Docker 1.9.1 (TCL 6.4.1); master : cef800b - Fri Nov 20 19:33:59 UTC 2015, provider=virtualbox, storagedriver=aufsCPUs: 3Total Memory: 3.064 GiBName: d074fd97682eThere are 3 nodes – one Swarm master and 2 Swarm worker nodes. There is a total of 4 containers running in this cluster – one Swarm agent on master and each node, and there is an additional swarm-agent-master running on the master. This can be verified by connecting to the master and listing all the containers.
- List nodes in the cluster with the following command:
123456docker run swarm list consul://$(docker-machine ip consul-machine):8500192.168.99.102:2376192.168.99.103:2376192.168.99.104:2376
Start Application Environment using Docker Compose
Make sure you are connected to the cluster by giving the command eval "$(docker-machine env --swarm swarm-master)"
.
- List all the networks created by Docker so far:
12345678910111213docker network lsNETWORK ID NAME DRIVER33a619ddc5d2 swarm-node-02/bridge bridgee0b73c96ffec swarm-node-02/none nullb315e67f0363 swarm-node-02/host host879d6167be47 swarm-master/bridge bridgef771ddc7d957 swarm-node-01/none nulle042754df336 swarm-node-01/host hostd2f3b512f9dc swarm-node-01/bridge bridge5b5bcf135d7b swarm-master/none nullfffc34eae907 swarm-master/host hostNetwork Name Purpose bridge
Default network that containers connect to. This is docker0
network in all Docker installations.none
Container-specific networking stack host
Adds a container on hosts networking stack. Network configuration is identical to the host. This explains a total of nine networks, three for each node, as shown in this Swarm cluster.
- Use Compose file to start WildFly and Couchbase:
1234567891011121314151617mycouchbase:container_name: "db"image: couchbase/serverports:- 8091:8091- 8092:8092- 8093:8093- 11210:11210mywildfly:image: arungupta/wildfly-adminenvironment:- COUCHBASE_URI=dbports:- 8080:8080- 9990:9990
In this Compose file:
- Couchbase service has a custom container name defined by
container_name
. This name is used when creating a new environment variableCOUCHBASE_URI
during WildFly startup. arungupta/wildfly-admin
image is used as it binds WildFly’s management to all network interfaces, and in addition also exposes port 9990. This enables WildFly Maven Plugin to be used to deploy the application.Source for this file is at https://github.com/arun-gupta/docker-images/blob/master/wildfly-couchbase-javaee7/docker-compose.yml.
This application environment can be started as:
1234567891011121314docker-compose --x-networking up -dCreating network "wildflycouchbasejavaee7" with driver "None"Pulling mywildfly (arungupta/wildfly-admin:latest)...swarm-node-02: Pulling arungupta/wildfly-admin:latest... : downloadedswarm-master: Pulling arungupta/wildfly-admin:latest... : downloadedswarm-node-01: Pulling arungupta/wildfly-admin:latest... : downloadedCreating wildflycouchbasejavaee7_mywildfly_1Pulling mycouchbase (couchbase/server:latest)...swarm-node-02: Pulling couchbase/server:latest... : downloadedswarm-master: Pulling couchbase/server:latest... : downloadedswarm-node-01: Pulling couchbase/server:latest... : downloadedCreating db--x-networking
creates an overlay network for the Swarm cluster. This can be verified by listing networks again:12345678910111213141516docker network lsNETWORK ID NAME DRIVER5e93fc34b4d9 swarm-node-01/docker_gwbridge bridge1c041242f51d wildflycouchbasejavaee7 overlaycc8697c6ce13 swarm-master/docker_gwbridge bridgef771ddc7d957 swarm-node-01/none null879d6167be47 swarm-master/bridge bridge5b5bcf135d7b swarm-master/none nullfffc34eae907 swarm-master/host hoste042754df336 swarm-node-01/host hostd2f3b512f9dc swarm-node-01/bridge bridge33a619ddc5d2 swarm-node-02/bridge bridgee0b73c96ffec swarm-node-02/none nullb315e67f0363 swarm-node-02/host hostThree new networks are created:
- Containers connected to the multi-host network are automatically connected to the
docker_gwbridge
network. This network allows the containers to have external connectivity outside of their cluster, and is created on each worker node. - A new overlay network
wildflycouchbasejavaee7
is created. Connect to different Swarm nodes and check that the overlay network exists on them.Lets begin with master:
12345678910eval "$(docker-machine env swarm-master)"docker network lsNETWORK ID NAME DRIVER1c041242f51d wildflycouchbasejavaee7 overlay879d6167be47 bridge bridge5b5bcf135d7b none nullfffc34eae907 host hostcc8697c6ce13 docker_gwbridge bridgeNext, with
swarm-node-01
:12345678910eval "$(docker-machine env swarm-node-01)"docker network lsNETWORK ID NAME DRIVER1c041242f51d wildflycouchbasejavaee7 overlayd2f3b512f9dc bridge bridgef771ddc7d957 none nulle042754df336 host host5e93fc34b4d9 docker_gwbridge bridgeFinally, with
swarm-node-02
:123456789eval "$(docker-machine env swarm-node-02)"docker network lsNETWORK ID NAME DRIVER1c041242f51d wildflycouchbasejavaee7 overlaye0b73c96ffec none nullb315e67f0363 host host33a619ddc5d2 bridge bridgeAs seen,
wildflycouchbasejavaee7
overlay network exists on all Machines. This confirms that the overlay network created for Swarm cluster was added to each host in the cluster.docker_gwbridge
only exists on Machines that have application containers running.Read more about Docker Networks.
- Couchbase service has a custom container name defined by
- Verify that WildFly and Couchbase are running:
123456docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES23a581295a2b couchbase/server "/entrypoint.sh couch" 9 seconds ago Up 8 seconds 192.168.99.102:8091-8093->8091-8093/tcp, 11207/tcp, 11211/tcp, 192.168.99.102:11210->11210/tcp, 18091-18092/tcp swarm-master/db7a8a885b23f3 arungupta/wildfly-admin "/opt/jboss/wildfly/b" 9 seconds ago Up 8 seconds 192.168.99.103:8080->8080/tcp, 192.168.99.103:9990->9990/tcp swarm-node-01/wildflycouchbasejavaee7_mywildfly_1
Configure Application and Database
- Clone https://github.com/arun-gupta/couchbase-javaee.git. This workspace contains a simple Java EE application that is deployed on WildFly and provides a REST API over
travel-sample
bucket in Couchbase. - Couchbase server can be configured using REST API. The application contains a Maven profile that allows to configure Couchbase server with
travel-sample
bucket. This can be invoked as:
12345678910111213141516171819mvn install -Pcouchbase -Ddocker.host=$(docker-machine ip swarm-master). . .* Server auth using Basic with user 'Administrator'> POST /sampleBuckets/install HTTP/1.1> Authorization: Basic QWRtaW5pc3RyYXRvcjpwYXNzd29yZA==. . .} [data not shown]* upload completely sent off: 17 out of 17 bytes< HTTP/1.1 202 Accepted* Server Couchbase Server is not blacklisted< Server: Couchbase Server. . . - Deploy the application to WildFly by specifying three parameters:
- Host IP address where WildFly is running
- Username of a user in WildFly’s administrative realm
- Password of the user specified in WildFly’s administrative realm
123456789101112131415161718mvn install -Pwildfly -Dwildfly.hostname=$(docker-machine ip swarm-node-01) -Dwildfly.username=admin -Dwildfly.password=Admin#007. . .Nov 29, 2015 12:11:14 AM org.xnio.Xnio <clinit>INFO: XNIO version 3.3.1.FinalNov 29, 2015 12:11:14 AM org.xnio.nio.NioXnio <clinit>INFO: XNIO NIO Implementation Version 3.3.1.FinalNov 29, 2015 12:11:15 AM org.jboss.remoting3.EndpointImpl <clinit>INFO: JBoss Remoting version 4.0.9.Final[INFO] Authenticating against security realm: ManagementRealm[INFO] ------------------------------------------------------------------------[INFO] BUILD SUCCESS[INFO] ------------------------------------------------------------------------. . .
Access Application
Now that WildFly and Couchbase server have started, lets access the application. You need to specify IP address of the Machine where WildFly is running:
1
2
3
4
|
curl http://$(docker-machine ip swarm-node-01):8080/couchbase-javaee/resources/airline
[{"travel-sample":{"id":10123,"iata":"TQ","icao":"TXW","name":"Texas Wings","callsign":"TXW","type":"airline","country":"United States"}}, {"travel-sample":{"id":10642,"iata":null,"icao":"JRB","name":"Jc royal.britannica","callsign":null,"type":"airline","country":"United Kingdom"}}, {"travel-sample":{"id":112,"iata":"5W","icao":"AEU","name":"Astraeus","callsign":"FLYSTAR","type":"airline","country":"United Kingdom"}}, {"travel-sample":{"id":1355,"iata":"BA","icao":"BAW","name":"British Airways","callsign":"SPEEDBIRD","type":"airline","country":"United Kingdom"}}, {"travel-sample":{"id":10765,"iata":"K5","icao":"SQH","name":"SeaPort Airlines","callsign":"SASQUATCH","type":"airline","country":"United States"}}, {"travel-sample":{"id":13633,"iata":"WQ","icao":"PQW","name":"PanAm World Airways","callsign":null,"type":"airline","country":"United States"}}, {"travel-sample":{"id":139,"iata":"SB","icao":"ACI","name":"Air Caledonie International","callsign":"AIRCALIN","type":"airline","country":"France"}}, {"travel-sample":{"id":13391,"iata":"-+","icao":"--+","name":"U.S. Air","callsign":null,"type":"airline","country":"United States"}}, {"travel-sample":{"id":1191,"iata":"UU","icao":"REU","name":"Air Austral","callsign":"REUNION","type":"airline","country":"France"}}, {"travel-sample":{"id":1316,"iata":"FL","icao":"TRS","name":"AirTran Airways","callsign":"CITRUS","type":"airline","country":"United States"}}]
|
Complete set of REST API for this application is documented at github.com/arun-gupta/couchbase-javaee.
Latest instructions at Docker for Java Developers.
Enjoy!
Awesome start… that’s exactly what I was looking for… Maybe you could extend it to use docker-machine to control hosts in any given Data Center using the generic driver. That would make this look like a production deployment Thanks a lot!
Point noted, will look into this.
Thanks for this tutorial
Nice tutorial that doesn’t assume anything. Thanks for taking the time to write it. I’ve some questions though:
1) How do I rejoin the swarm if I’d left? The instructions in the tutorial require creating a new machine and the official docs use a pre-generated cluster id for registration, not Consul.
2) It appears that even though the application (Wildfly + Couchbase) is running on all 3 nodes, Couchbase is running a single host, single container scenario. In other words, the 3 instances of Couchbase are not in a cluster as far as Couchbase is concerned. Is that correct?
3) Please consider extending the tutorial by adding at least one other method of registration and discovery as shown in the official docs.
Abhijit,
Thanks for your feedback!
This application is using a single instance of Couchbase. A future blog will talk about how to create a scalable cluster on Docker.
Can you file bugs at https://github.com/javaee-samples/docker-java for your requests?
Arun
> Can you file bugs…
Done.
https://github.com/javaee-samples/docker-java/issues/131
Thanks for this very good article.Unfortunately docker compose on windows does’nt have the –x-networking option yet (1.6.2)
Chris,
File a bug at https://github.com/docker/compose/issues?
Hi Arun, I filed the issue, and got a quick reply.
As from docker-compose 1.6.0, networking is the default mode, and the experimental –x-networking flag has been removed and that’s a preety good news !
Thank Arun for awesome walkthrough.
In my opinion, information from https://resume-chief.com/blog/functional-resume will help you to get high quality functional resume. You can use it in the future to get a job