Docker Compose is a tool for defining and running complex applications with Docker. With Compose, you define a multi-container application in a single file, then spin your application up in a single command which does everything that needs to be done to get it running.
An application using Docker containers will typically consist of multiple containers. With Docker Compose, there is no need to write shell scripts to start your containers. All the containers are defined in a configuration file using services, and then docker-compose
script is used to start, stop, and restart the application and all the services in that application, and all the containers within that service. The complete list of commands is:
Command | Purpose |
---|---|
|
Build or rebuild services |
|
Get help on a command |
|
Kill containers |
|
View output from containers |
|
Print the public port for a port binding |
|
List containers |
|
Pulls service images |
|
Restart services |
|
Remove stopped containers |
|
Run a one-off command |
|
Set number of containers for a service |
|
Start services |
|
Stop services |
|
Create and start containers |
The application used in this section is a Java EE application talking to a database. The application publishes a REST endpoint that can be invoked using `curl. It is deployed using WildFly Swarm that communicates to MySQL database.
WildFly Swarm and MySQL will be running in two separate containers, and thus making this a multi-container application.
The entry point to Docker Compose is a Compose file, usually called docker-compose.yml
. Create a new directory javaee
. In that directory, create a new file docker-compose.yml
in it. Use the following contents:
version: '3.3'
services:
db:
container_name: db
image: mysql:8
environment:
MYSQL_DATABASE: employees
MYSQL_USER: mysql
MYSQL_PASSWORD: mysql
MYSQL_ROOT_PASSWORD: supersecret
ports:
- 3306:3306
web:
image: arungupta/docker-javaee:dockerconeu17
ports:
- 8080:8080
- 9990:9990
depends_on:
- db
In this Compose file:
-
Two services in this Compose are defined by the name
db
andweb
attributes -
Image name for each service defined using
image
attribute -
The
mysql:8
image starts the MySQL server. -
environment
attribute defines environment variables to initialize MySQL server.-
MYSQL_DATABASE
allows you to specify the name of a database to be created on image startup. -
MYSQL_USER
andMYSQL_PASSWORD
are used in conjunction to create a new user and to set that user’s password. This user will be granted superuser permissions for the database specified by theMYSQL_DATABASE
variable. -
MYSQL_ROOT_PASSWORD
is mandatory and specifies the password that will be set for the MySQL root superuser account.
-
-
Java EE application uses the
db
service as specified in theconnection-url
as specified at https://github.com/arun-gupta/docker-javaee/blob/master/employees/src/main/resources/project-defaults.yml/. -
The
arungupta/docker-javaee:dockerconeu17
image starts WildFly Swarm application server. It consists of the Java EE application built from https://github.com/arun-gupta/docker-javaee. Clone that project if you want to build your own image. -
Port forwarding is achieved using
ports
attribute. -
depends_on
attribute allows to express dependency between services. In this case, MySQL will be started before WildFly. Application-level health check are still user’s responsibility.
All services in the application can be started, in detached mode, by giving the command:
docker-compose up -d
An alternate Compose file name can be specified using -f
option.
An alternate directory where the compose file exists can be specified using -p
option.
This shows the output as:
docker-compose up -d
Creating network "javaee_default" with the default driver
Creating db ...
Creating db ... done
Creating javaee_web_1 ...
Creating javaee_web_1 ... done
The output may differ slightly if the images are downloaded as well.
Started services can be verified using the command docker-compose ps
:
Name Command State Ports
------------------------------------------------------------------------------------------------------
db docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp
javaee_web_1 /bin/sh -c java -jar /opt/ ... Up 0.0.0.0:8080->8080/tcp, 0.0.0.0:9990->9990/tcp
This provides a consolidated view of all the services, and containers within each of them.
Alternatively, the containers in this application, and any additional containers running on this Docker host can be verified by using the usual docker container ls
command:
Name Command State Ports
------------------------------------------------------------------------------------------------------
db docker-entrypoint.sh mysqld Up 0.0.0.0:3306->3306/tcp
javaee_web_1 /bin/sh -c java -jar /opt/ ... Up 0.0.0.0:8080->8080/tcp, 0.0.0.0:9990->9990/tcp
javaee $ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e862a5eb9484 arungupta/docker-javaee:dockerconeu17 "/bin/sh -c 'java ..." 38 seconds ago Up 36 seconds 0.0.0.0:8080->8080/tcp, 0.0.0.0:9990->9990/tcp javaee_web_1
08792c20c066 mysql:8 "docker-entrypoint..." 39 seconds ago Up 37 seconds 0.0.0.0:3306->3306/tcp db
Service logs can be seen using docker-compose logs
command, and looks like:
Attaching to dockerjavaee_web_1, db
web_1 | 23:54:21,584 INFO [org.jboss.msc] (main) JBoss MSC version 1.2.6.Final
web_1 | 23:54:21,688 INFO [org.jboss.as] (MSC service thread 1-8) WFLYSRV0049: WildFly Core 2.0.10.Final "Kenny" starting
web_1 | 2017-10-06 23:54:22,687 INFO [org.wildfly.extension.io] (ServerService Thread Pool -- 20) WFLYIO001: Worker 'default' has auto-configured to 8 core threads with 64 task threads based on your 4 available processors
. . .
web_1 | 2017-10-06 23:54:23,259 INFO [org.jboss.as.connector.subsystems.datasources] (MSC service thread 1-3) WFLYJCA0001: Bound data source [java:jboss/datasources/ExampleDS]
web_1 | 2017-10-06 23:54:24,962 INFO [org.jboss.as] (Controller Boot Thread) WFLYSRV0025: WildFly Core 2.0.10.Final "Kenny" started in 3406ms - Started 112 of 124 services (21 services are lazy, passive or on-demand)
web_1 | 2017-10-06 23:54:25,020 INFO [org.wildfly.extension.undertow] (MSC service thread 1-4) WFLYUT0006: Undertow HTTP listener default listening on 0.0.0.0:8080
web_1 | 2017-10-06 23:54:26,146 INFO [org.wildfly.swarm.runtime.deployer] (main) deploying docker-javaee.war
web_1 | 2017-10-06 23:54:26,169 INFO [org.jboss.as.server.deployment] (MSC service thread 1-3) WFLYSRV0027: Starting deployment of "docker-javaee.war" (runtime-name: "docker-javaee.war")
web_1 | 2017-10-06 23:54:27,748 INFO [org.jboss.as.jpa] (MSC service thread 1-2) WFLYJPA0002: Read persistence.xml for MyPU
web_1 | 2017-10-06 23:54:27,887 WARN [org.jboss.as.dependency.private] (MSC service thread 1-7) WFLYSRV0018: Deployment "deployment.docker-javaee.war" is using a private module ("org.jboss.jts:main") which may be changed or removed in future versions without notice.
. . .
web_1 | 2017-10-06 23:54:29,128 INFO [stdout] (ServerService Thread Pool -- 4) Hibernate: create table EMPLOYEE_SCHEMA (id integer not null, name varchar(40), primary key (id))
web_1 | 2017-10-06 23:54:29,132 INFO [stdout] (ServerService Thread Pool -- 4) Hibernate: INSERT INTO EMPLOYEE_SCHEMA(ID, NAME) VALUES (1, 'Penny')
web_1 | 2017-10-06 23:54:29,133 INFO [stdout] (ServerService Thread Pool -- 4) Hibernate: INSERT INTO EMPLOYEE_SCHEMA(ID, NAME) VALUES (2, 'Sheldon')
web_1 | 2017-10-06 23:54:29,133 INFO [stdout] (ServerService Thread Pool -- 4) Hibernate: INSERT INTO EMPLOYEE_SCHEMA(ID, NAME) VALUES (3, 'Amy')
web_1 | 2017-10-06 23:54:29,133 INFO [stdout] (ServerService Thread Pool -- 4) Hibernate: INSERT INTO EMPLOYEE_SCHEMA(ID, NAME) VALUES (4, 'Leonard')
. . .
web_1 | 2017-10-06 23:54:30,050 INFO [org.wildfly.extension.undertow] (ServerService Thread Pool -- 4) WFLYUT0021: Registered web context: /
web_1 | 2017-10-06 23:54:30,518 INFO [org.jboss.as.server] (main) WFLYSRV0010: Deployed "docker-javaee.war" (runtime-name : "docker-javaee.war")
web_1 | 2017-10-06 23:56:01,766 INFO [stdout] (default task-1) Hibernate: select employee0_.id as id1_0_, employee0_.name as name2_0_ from EMPLOYEE_SCHEMA employee0_
db | Initializing database
. . .
db |
db |
db | MySQL init process done. Ready for start up.
db |
. . .
db | 2017-10-06T23:54:29.434423Z 0 [Note] /usr/sbin/mysqld: ready for connections. Version: '8.0.3-rc-log' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server (GPL)
db | 2017-10-06T23:54:30.281973Z 0 [Note] InnoDB: Buffer pool(s) load completed at 171006 23:54:30
depends_on
attribute in the Compose definition file ensures the container start up order. But application-level start up needs to be ensured by the applications running inside container. In our case, this can be achieved by WildFly Swarm using swarm.datasources.data-sources.KEY.stale-connection-checker-class-name
as defined at https://reference.wildfly-swarm.io/fractions/datasources.html.
Now that the WildFly Swarm and MySQL have been configured, let’s access the application. You need to specify IP address of the host where WildFly is running (localhost
in our case).
The endpoint can be accessed in this case as:
curl -v http://localhost:8080/resources/employees
The output is shown as:
curl -v http://localhost:8080/resources/employees
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /resources/employees HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.51.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Type: application/xml
< Content-Length: 478
< Date: Sat, 07 Oct 2017 00:05:41 GMT
<
* Curl_http_done: called premature == 0
* Connection #0 to host localhost left intact
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><collection><employee><id>1</id><name>Penny</name></employee><employee><id>2</id><name>Sheldon</name></employee><employee><id>3</id><name>Amy</name></employee><employee><id>4</id><name>Leonard</name></employee><employee><id>5</id><name>Bernadette</name></employee><employee><id>6</id><name>Raj</name></employee><employee><id>7</id><name>Howard</name></employee><employee><id>8</id><name>Priya</name></employee></collection>
This shows the result from querying the database.
A single resource can be obtained:
curl -v http://localhost:8080/resources/employees/1
It shows the output:
curl -v http://localhost:8080/resources/employees/1
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 8080 (#0)
> GET /resources/employees/1 HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.51.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Connection: keep-alive
< Content-Type: application/xml
< Content-Length: 104
< Date: Sat, 07 Oct 2017 00:06:33 GMT
<
* Curl_http_done: called premature == 0
* Connection #0 to host localhost left intact
<?xml version="1.0" encoding="UTF-8" standalone="yes"?><employee><id>1</id><name>Penny</name></employee>
Shutdown the application using docker-compose down
:
Stopping javaee_web_1 ... done
Stopping db ... done
Removing javaee_web_1 ... done
Removing db ... done
Removing network javaee_default
This stops the container in each service and removes all the services. It also deletes any networks that were created as part of this application.