Post

Docker Compose

NOTE: All configurations were taken from a lab environment.

TLDR;

Just copy and paste this to have Docker Compose installed

1
2
sudo curl -SL https://github.com/docker/compose/releases/download/v2.29.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

Install Docker Compose

1
sudo curl -SL https://github.com/docker/compose/releases/download/v2.29.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
output
1
2
3
4
5
6
cloud_user@553b1e446c1c:~$ sudo curl -SL https://github.com/docker/compose/releases/download/v2.29.0/docker-compose-linux-x86_64 -o /usr/local/bin/docker-compose
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
  0     0    0     0    0     0      0      0 --:--:-- --:--:-- --:--:--     0
100 60.2M  100 60.2M    0     0  65.1M      0 --:--:-- --:--:-- --:--:-- 65.1M
cloud_user@553b1e446c1c:~$


Apply executable permissions to the binary

1
sudo chmod +x /usr/local/bin/docker-compose
output
1
2
cloud_user@553b1e446c1c:~$ sudo chmod +x /usr/local/bin/docker-compose
cloud_user@553b1e446c1c:~$ 


Docker compose should be installed by now. Verify with the command:

1
docker-compose
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
cloud_user@553b1e446c1c:~$ docker-compose

Usage:  docker compose [OPTIONS] COMMAND

Define and run multi-container applications with Docker

Options:
      --all-resources              Include all resources, even those not used by services
      --ansi string                Control when to print ANSI control characters ("never"|"always"|"auto") (default "auto")
      --compatibility              Run compose in backward compatibility mode
      --dry-run                    Execute command in dry run mode
      --env-file stringArray       Specify an alternate environment file
  -f, --file stringArray           Compose configuration files
      --parallel int               Control max parallelism, -1 for unlimited (default -1)
      --profile stringArray        Specify a profile to enable
      --progress string            Set type of progress output (auto, tty, plain, json, quiet) (default "auto")
      --project-directory string   Specify an alternate working directory
                                   (default: the path of the, first specified, Compose file)
  -p, --project-name string        Project name

Commands:
  attach      Attach local standard input, output, and error streams to a service's running container
  build       Build or rebuild services
  config      Parse, resolve and render compose file in canonical format
  cp          Copy files/folders between a service container and the local filesystem
  create      Creates containers for a service
  down        Stop and remove containers, networks
  events      Receive real time events from containers
  exec        Execute a command in a running container
  images      List images used by the created containers
  kill        Force stop service containers
  logs        View output from containers
  ls          List running compose projects
  pause       Pause services
  port        Print the public port for a port binding
  ps          List containers
  pull        Pull service images
  push        Push service images
  restart     Restart service containers
  rm          Removes stopped service containers
  run         Run a one-off command on a service
  scale       Scale services 
  start       Start services
  stats       Display a live stream of container(s) resource usage statistics
  stop        Stop services
  top         Display the running processes
  unpause     Unpause services
  up          Create and start containers
  version     Show the Docker Compose version information
  wait        Block until the first service container stops
  watch       Watch build context for service and rebuild/refresh containers when files are updated

Run 'docker compose COMMAND --help' for more information on a command.
cloud_user@553b1e446c1c:~$ 


More than one compose file in the directory

There are cases when there is more than a single compose file, for example in this GitHub repo (ksator / frrouting_demo GitHub repo)[https://github.com/ksator/frrouting_demo/tree/master]. In this case we can choose which compose file to use with the -f flag.

1
2
3
4
5
6
7
cloud_user@553b1e446c1c:~/ksator_frrouting_demo/frrouting_demo$ tree
.
├── Dockerfile
├── README.md
...
├── docker-compose-demo1.yml
└── docker-compose-demo2.yml

Example:

1
docker-compose -f docker-compose-demo1.yml up

or

1
docker-compose -f docker-compose-demo2.yml up

Run Compose in detached mode

Use the -d flag. Example:

1
docker-compose -f docker-compose-demo1.yml up -d
output
1
2
3
4
5
6
cloud_user@553b1e446c1c:~/ksator_frrouting_demo/frrouting_demo$ docker-compose -f docker-compose-demo1.yml up -d
WARN[0000] /home/cloud_user/ksator_frrouting_demo/frrouting_demo/docker-compose-demo1.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 2/2
 ✔ Container frr100  Started                                                                                                                                                                           0.9s
 ✔ Container frr200  Started                                                                                                                                                                           0.8s
cloud_user@553b1e446c1c:~/ksator_frrouting_demo/frrouting_demo$


Clone the example-voting-app using Git

We will be using the voting-app to test Docker Compose functionality.

The code is stored in GitHub and the architecture is as follows:

Next, clone the repo:

1
git clone https://github.com/dockersamples/example-voting-app
output
1
2
3
4
5
6
7
cloud_user@553b1e446c1c:~$ git clone https://github.com/dockersamples/example-voting-app
Cloning into 'example-voting-app'...
remote: Enumerating objects: 1140, done.
remote: Total 1140 (delta 0), reused 0 (delta 0), pack-reused 1140
Receiving objects: 100% (1140/1140), 1.19 MiB | 10.71 MiB/s, done.
Resolving deltas: 100% (438/438), done.
cloud_user@553b1e446c1c:~$ 


Create Docker Compose YAML file

  • image is used to pull the image from Docker Hub
  • build is used to create the image locally using a Dockerfile. This is in the GitHub repo we cloned
  • environment need to be passed to postgres, otherwise it shows an error

docker-compose.yml

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
services:
  redis:
    image: redis
  
  db:
    image: postgres:9.4
    environment:
      POSTGRES_USER: "postgres"
      POSTGRES_PASSWORD: "postgres"
  
  vote:
    build: ./example-voting-app/vote
    ports:
      - 5000:80
    links:
      - redis
  
  worker:
    build: ./example-voting-app/worker
    links:
      - db
      - redis
  
  result:
    build: ./example-voting-app/result
    ports:
      - 5001:80
    links:
      - db

NOTE: There is another docker compose YAML file in the repo with more advance options. We are using this one for simplicity. NOTE: links may be removed from this YAML file

Once the YAML file has been created, run:

1
docker-compose up
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
cloud_user@553b1e446c1c:~$ sudo docker-compose up
[+] Running 5/5
 ✔ Container cloud_user-redis-1   Created                                                                                                                                                                                                                                                                0.0s 
 ✔ Container cloud_user-db-1      Recreated                                                                                                                                                                                                                                                              0.4s 
 ✔ Container cloud_user-vote-1    Created                                                                                                                                                                                                                                                                0.0s 
 ✔ Container cloud_user-result-1  Recreated                                                                                                                                                                                                                                                              0.4s 
 ✔ Container cloud_user-worker-1  Recreated                                                                                                                                                                                                                                                              0.4s 
Attaching to db-1, redis-1, result-1, vote-1, worker-1
redis-1   | 1:C 18 Jul 2024 03:43:17.371 # WARNING Memory overcommit must be enabled! Without it, a background save or replication may fail under low memory condition. Being disabled, it can also cause failures without low memory condition, see https://github.com/jemalloc/jemalloc/issues/1328. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect.
redis-1   | 1:C 18 Jul 2024 03:43:17.372 * oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
redis-1   | 1:C 18 Jul 2024 03:43:17.373 * Redis version=7.2.5, bits=64, commit=00000000, modified=0, pid=1, just started
redis-1   | 1:C 18 Jul 2024 03:43:17.373 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf
redis-1   | 1:M 18 Jul 2024 03:43:17.375 * monotonic clock: POSIX clock_gettime
redis-1   | 1:M 18 Jul 2024 03:43:17.377 * Running mode=standalone, port=6379.
redis-1   | 1:M 18 Jul 2024 03:43:17.378 * Server initialized
redis-1   | 1:M 18 Jul 2024 03:43:17.379 * Loading RDB produced by version 7.2.5
redis-1   | 1:M 18 Jul 2024 03:43:17.379 * RDB age 99 seconds
redis-1   | 1:M 18 Jul 2024 03:43:17.379 * RDB memory usage when created 0.83 Mb
redis-1   | 1:M 18 Jul 2024 03:43:17.379 * Done loading RDB, keys loaded: 0, keys expired: 0.
redis-1   | 1:M 18 Jul 2024 03:43:17.379 * DB loaded from disk: 0.001 seconds
redis-1   | 1:M 18 Jul 2024 03:43:17.380 * Ready to accept connections tcp
vote-1    | [2024-07-18 03:43:18 +0000] [1] [INFO] Starting gunicorn 22.0.0
vote-1    | [2024-07-18 03:43:18 +0000] [1] [INFO] Listening at: http://0.0.0.0:80 (1)
vote-1    | [2024-07-18 03:43:18 +0000] [1] [INFO] Using worker: sync
vote-1    | [2024-07-18 03:43:18 +0000] [7] [INFO] Booting worker with pid: 7
vote-1    | [2024-07-18 03:43:18 +0000] [8] [INFO] Booting worker with pid: 8
vote-1    | [2024-07-18 03:43:18 +0000] [9] [INFO] Booting worker with pid: 9
result-1  | Thu, 18 Jul 2024 03:43:18 GMT body-parser deprecated undefined extended: provide extended option at server.js:67:17
result-1  | App running on port 80
result-1  | Waiting for db
vote-1    | [2024-07-18 03:43:18 +0000] [10] [INFO] Booting worker with pid: 10
worker-1  | Waiting for db
result-1  | Waiting for db
worker-1  | Waiting for db
db-1      | The files belonging to this database system will be owned by user "postgres".
db-1      | This user must also own the server process.
db-1      | 
db-1      | The database cluster will be initialized with locale "en_US.utf8".
db-1      | The default database encoding has accordingly been set to "UTF8".
db-1      | The default text search configuration will be set to "english".
db-1      | 
db-1      | Data page checksums are disabled.
db-1      | 
db-1      | fixing permissions on existing directory /var/lib/postgresql/data ... ok
db-1      | creating subdirectories ... ok
result-1  | Waiting for db
db-1      | selecting default max_connections ... 100
db-1      | selecting default shared_buffers ... 128MB
db-1      | selecting default timezone ... Etc/UTC
db-1      | selecting dynamic shared memory implementation ... posix
db-1      | creating configuration files ... ok
worker-1  | Waiting for db
result-1  | Waiting for db
db-1      | creating template1 database in /var/lib/postgresql/data/base/1 ... ok
db-1      | initializing pg_authid ... ok
db-1      | setting password ... ok
db-1      | initializing dependencies ... ok
worker-1  | Waiting for db
db-1      | creating system views ... ok
db-1      | loading system objects' descriptions ... ok
result-1  | Waiting for db
db-1      | creating collations ... ok
worker-1  | Waiting for db
db-1      | creating conversions ... ok
result-1  | Waiting for db
db-1      | creating dictionaries ... ok
db-1      | setting privileges on built-in objects ... ok
worker-1  | Waiting for db
result-1  | Waiting for db
db-1      | creating information schema ... ok
db-1      | loading PL/pgSQL server-side language ... ok
db-1      | vacuuming database template1 ... ok
db-1      | copying template1 to template0 ... ok
worker-1  | Waiting for db
db-1      | copying template1 to postgres ... ok
result-1  | Waiting for db
db-1      | syncing data to disk ... ok
db-1      | 
db-1      | Success. You can now start the database server using:
db-1      | 
db-1      |     postgres -D /var/lib/postgresql/data
db-1      | or
db-1      |     pg_ctl -D /var/lib/postgresql/data -l logfile start
db-1      | 
db-1      | 
db-1      | WARNING: enabling "trust" authentication for local connections
db-1      | You can change this by editing pg_hba.conf or using the option -A, or
db-1      | --auth-local and --auth-host, the next time you run initdb.
db-1      | waiting for server to start....LOG:  database system was shut down at 2024-07-18 03:43:25 UTC
db-1      | LOG:  MultiXact member wraparound protections are now enabled
db-1      | LOG:  autovacuum launcher started
db-1      | LOG:  database system is ready to accept connections
worker-1  | Waiting for db
result-1  | Waiting for db
db-1      |  done
db-1      | server started
db-1      | 
db-1      | /usr/local/bin/docker-entrypoint.sh: ignoring /docker-entrypoint-initdb.d/*
db-1      | 
db-1      | LOG:  received fast shutdown request
db-1      | waiting for server to shut down....LOG:  aborting any active transactions
db-1      | LOG:  autovacuum launcher shutting down
db-1      | LOG:  shutting down
db-1      | LOG:  database system is shut down
worker-1  | Waiting for db
result-1  | Waiting for db
db-1      |  done
db-1      | server stopped
db-1      | 
db-1      | PostgreSQL init process complete; ready for start up.
db-1      | 
db-1      | LOG:  database system was shut down at 2024-07-18 03:43:27 UTC
db-1      | LOG:  MultiXact member wraparound protections are now enabled
db-1      | LOG:  autovacuum launcher started
db-1      | LOG:  database system is ready to accept connections
result-1  | Connected to db
db-1      | ERROR:  relation "votes" does not exist at character 38
db-1      | STATEMENT:  SELECT vote, COUNT(id) AS count FROM votes GROUP BY vote
result-1  | Error performing query: error: relation "votes" does not exist
worker-1  | Connected to db
db-1      | ERROR:  relation "votes" does not exist at character 38
db-1      | STATEMENT:  SELECT vote, COUNT(id) AS count FROM votes GROUP BY vote
result-1  | Error performing query: error: relation "votes" does not exist
worker-1  | Connecting to redis
worker-1  | Found redis at 172.18.0.3

cloud_user@553b1e446c1c:~$ sudo docker ps
CONTAINER ID   IMAGE               COMMAND                  CREATED          STATUS          PORTS                                   NAMES
8266d3f1912b   cloud_user-result   "/usr/bin/tini -- no…"   20 minutes ago   Up 20 minutes   0.0.0.0:5001->80/tcp, :::5001->80/tcp   cloud_user-result-1
da6391d5d1ed   cloud_user-worker   "dotnet Worker.dll"      20 minutes ago   Up 20 minutes                                           cloud_user-worker-1
0f01c8a8af97   postgres:9.4        "docker-entrypoint.s…"   20 minutes ago   Up 20 minutes   5432/tcp                                cloud_user-db-1
b4dac42b91af   cloud_user-vote     "gunicorn app:app -b…"   26 minutes ago   Up 20 minutes   0.0.0.0:5000->80/tcp, :::5000->80/tcp   cloud_user-vote-1
5d13b4d68208   redis               "docker-entrypoint.s…"   26 minutes ago   Up 20 minutes   6379/tcp                                cloud_user-redis-1
cloud_user@553b1e446c1c:~$ 


Verify

By visiting http://ip_address:5000/

By visiting http://ip_address:5001/

You can also verify using cURL

1
curl localhost:5000
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
cloud_user@553b1e446c1c:~$ curl localhost:5000
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Cats vs Dogs!</title>
    <base href="/index.html">
    <meta name = "viewport" content = "width=device-width, initial-scale = 1.0">
    <meta name="keywords" content="docker-compose, docker, stack">
    <meta name="author" content="Tutum dev team">
    <link rel='stylesheet' href="/static/stylesheets/style.css" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">
  </head>
  <body>
    <div id="content-container">
      <div id="content-container-center">
        <h3>Cats vs Dogs!</h3>
        <form id="choice" name='form' method="POST" action="/">
          <button id="a" type="submit" name="vote" class="a" value="a">Cats</button>
          <button id="b" type="submit" name="vote" class="b" value="b">Dogs</button>
        </form>
        <div id="tip">
          (Tip: you can change your vote)
        </div>
        <div id="hostname">
          Processed by container ID b4dac42b91af
        </div>
      </div>
    </div>
    <script src="http://code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery-cookie/1.4.1/jquery.cookie.js"></script>

    
  </body>
</html>


1
curl localhost:5001
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
cloud_user@553b1e446c1c:~$ curl localhost:5001
<!DOCTYPE html>
<html ng-app="catsvsdogs">
  <head>
    <meta charset="utf-8">
    <title>Cats vs Dogs -- Result</title>
    <base href="/index.html">
    <meta name = "viewport" content = "width=device-width, initial-scale = 1.0">
    <meta name="keywords" content="docker-compose, docker, stack">
    <meta name="author" content="Docker">
    <link rel='stylesheet' href='/stylesheets/style.css' />
  </head>
  <body ng-controller="statsCtrl" >
     <div id="background-stats">
       <div id="background-stats-1">
       </div><!--
      --><div id="background-stats-2">
      </div>
    </div>
    <div id="content-container">
      <div id="content-container-center">
        <div id="choice">
          <div class="choice cats">
            <div class="label">Cats</div>
            <div class="stat">%</div>
          </div>
          <div class="divider"></div>
          <div class="choice dogs">
            <div class="label">Dogs</div>
            <div class="stat">%</div>
          </div>
        </div>
      </div>
    </div>
    <div id="result">
      <span ng-if="total == 0">No votes yet</span>
      <span ng-if="total == 1"> vote</span>
      <span ng-if="total >= 2"> votes</span>
    </div>
    <script src="socket.io.js"></script>
    <script src="angular.min.js"></script>
    <script src="app.js"></script>
  </body>
</html>
cloud_user@553b1e446c1c:~$


Stop the containers

1
docker compose down
output
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
cloud_user@553b1e446c1c:~$ sudo docker compose down
[+] Running 6/6
 ✔ Container cloud_user-result-1  Removed                                                                                                                                                                                                                                                                0.8s 
 ✔ Container cloud_user-vote-1    Removed                                                                                                                                                                                                                                                                1.1s 
 ✔ Container cloud_user-worker-1  Removed                                                                                                                                                                                                                                                                0.9s 
 ✔ Container cloud_user-db-1      Removed                                                                                                                                                                                                                                                                0.5s 
 ✔ Container cloud_user-redis-1   Removed                                                                                                                                                                                                                                                                0.9s 
 ✔ Network cloud_user_default     Removed                                                                                                                                                                                                                                                                0.2s 
cloud_user@553b1e446c1c:~$ 

cloud_user@553b1e446c1c:~$ sudo docker ps
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
cloud_user@553b1e446c1c:~$ 

cloud_user@553b1e446c1c:~$ sudo docker ps -a
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
cloud_user@553b1e446c1c:~$ 


References

This post is licensed under CC BY 4.0 by the author.