Docker Compose permet de démarrer, avec une seule ligne de commande, un ensemble de conteneurs communiquant entre eux. L’exemple de WordPress et sa base de données MariaDB est assez simple. Est-il possible de démarrer un ensemble de conteneurs un peu plus complexe comme un cluster de bases de données Cassandra ?

Premiers essais avec Docker

Une première approche consiste à valider les images à utiliser et la communication entre les conteneurs via la ligne de commande docker. L’équipe Docker propose une image officielle pour Cassandra qui peut être lancée de cette façon :

$ docker run --name myproject-cassandra -d cassandra

Cette même image contient le client CQLSH pour se connecter au serveur Cassandra. Il est donc possible d’exécuter le conteneur suivant pour créer une table dans la base de données :

$ docker run -it --link myproject-cassandra:cassandra --rm cassandra cqlsh cassandra
Connected to Test Cluster at cassandra:9042.
[cqlsh 5.0.1 | Cassandra 2.2.3 | CQL spec 3.3.1 | Native protocol v4]
Use HELP for help.
cqlsh> CREATE KEYSPACE myproject
   ... WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };
cqlsh> use myproject;
cqlsh:myproject> CREATE TABLE users (
        ... firstname text,
        ... lastname text,
        ... age int,
        ... email text,
        ... city text,
        ... PRIMARY KEY (lastname));

Maintenant que le conteneur Cassandra tourne et est accessible, il faut démarrer une seconde instance de Cassandra afin d’obtenir un cluster. Le démarrage d’un nouveau nœud Cassandra passe encore par l’utilisation de la même image avec la commande suivante :

$ docker run --name myproject-cassandra-2 -d -e CASSANDRA_SEEDS="$(docker inspect --format='{{ .NetworkSettings.IPAddress }}' myproject-cassandra)" cassandra

Une fois le second nœud démarré, il est possible d’aller vérifier si la réplication est fonctionnelle en s’y connectant en CQLSH :

$ docker run -it --link myproject-cassandra-2:cassandra --rm cassandra cqlsh cassandra
Connected to Test Cluster at cassandra:9042.
[cqlsh 5.0.1 | Cassandra 2.2.3 | CQL spec 3.3.1 | Native protocol v4]
Use HELP for help.
cqlsh> use myproject;
cqlsh:myproject> describe myproject;

CREATE KEYSPACE myproject WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}  AND durable_writes = true;

CREATE TABLE myproject.users (
    lastname text PRIMARY KEY,
    age int,
    city text,
    email text,
    firstname text
) WITH bloom_filter_fp_chance = 0.01
    AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
    AND comment = ''
    AND compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy'}
    AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
    AND dclocal_read_repair_chance = 0.1
    AND default_time_to_live = 0
    AND gc_grace_seconds = 864000
    AND max_index_interval = 2048
    AND memtable_flush_period_in_ms = 0
    AND min_index_interval = 128
    AND read_repair_chance = 0.0
    AND speculative_retry = '99.0PERCENTILE';

Avec 2 lignes de commandes, il a été possible de monter un cluster Cassandra avec une réplication de données fonctionnelle.

Passage à Docker Compose

Deux commandes, c’est une de trop ! De plus, il est nécessaire de se rappeler de tous les arguments de la ligne de commande… Heureusement, Docker Compose propose de définir les options de chargement et de communication des conteneurs dans un fichier docker-compose.yml.

Le cluster de 2 noeuds Cassandra peut donc être décrit de la façon suivante :

cassandra0:
  image: cassandra

cassandran:
  image: cassandra
  links:
    - cassandra0:seed
  environment:
    - CASSANDRA_SEEDS=seed

Avec ce fichier, Docker Compose est capable de démarrer le cluster Cassandra avec cette unique ligne de commande :

$ docker-compose up -d
Starting dockermyproject_cassandra0_1
Starting dockermyproject_cassandran_1

Comme lors de la validation des images à utiliser, la création de la table dans la base de données se fait ainsi :

$ docker run -it --link  dockermyproject_cassandra0_1:cassandra --rm cassandra cqlsh cassandra
Connected to Test Cluster at cassandra:9042.
[cqlsh 5.0.1 | Cassandra 2.2.3 | CQL spec 3.3.1 | Native protocol v4]
Use HELP for help.
cqlsh> CREATE KEYSPACE myproject
   ... WITH REPLICATION = { 'class' : 'SimpleStrategy', 'replication_factor' : 1 };
cqlsh> use myproject;
cqlsh:myproject> CREATE TABLE users (
        ... firstname text,
        ... lastname text,
        ... age int,
        ... email text,
        ... city text,
        ... PRIMARY KEY (lastname));

De même, il est possible de vérifier que la réplication est active avec les mêmes commandes :

$ docker run -it --link  dockermyproject_cassandran_1:cassandra --rm cassandra cqlsh cassandra
Connected to Test Cluster at cassandra:9042.
[cqlsh 5.0.1 | Cassandra 2.2.3 | CQL spec 3.3.1 | Native protocol v4]
Use HELP for help.
cqlsh> use myproject;
cqlsh:myproject> describe myproject;

CREATE KEYSPACE myproject WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}  AND durable_writes = true;

CREATE TABLE myproject.users (
    lastname text PRIMARY KEY,
    age int,
    city text,
    email text,
    firstname text
) WITH bloom_filter_fp_chance = 0.01
    AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
    AND comment = ''
    AND compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy'}
    AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
    AND dclocal_read_repair_chance = 0.1
    AND default_time_to_live = 0
    AND gc_grace_seconds = 864000
    AND max_index_interval = 2048
    AND memtable_flush_period_in_ms = 0
    AND min_index_interval = 128
    AND read_repair_chance = 0.0
    AND speculative_retry = '99.0PERCENTILE';

Docker Compose a permis de créer et démarrer un cluster Cassandra.

Scalabilité à la demande

Pour aller plus loin, il est possible de “scaler” ce cluster à la demande. En effet, Docker Compose permet de définir le nombre d’instance d’un service (ie. conteneur) en ligne de commande.

Par exemple, le précédent docker-compose.yml défini 2 services cassandran et cassandra0. Il est possible de démarrer 5 instances de cassandra0 très simplement :

$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                         NAMES
48d7187e6330        cassandra           "/docker-entrypoint.s"   12 minutes ago      Up 8 minutes        7000-7001/tcp, 7199/tcp, 9042/tcp, 9160/tcp   dockermyproject_cassandran_1
2ca9652f450f        cassandra           "/docker-entrypoint.s"   12 minutes ago      Up 8 minutes        7000-7001/tcp, 7199/tcp, 9042/tcp, 9160/tcp   dockermyproject_cassandra0_1

$ docker-compose scale cassandran=5
Creating and starting 2 ...
Creating and starting 3 ... done
Creating and starting 2 ... done
Creating and starting 5 ...
Creating and starting 5 ... done
Creating and starting 4 ... done

docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                                         NAMES
bd86014b99bc        cassandra           "/docker-entrypoint.s"   4 seconds ago       Up 3 seconds        7000-7001/tcp, 7199/tcp, 9042/tcp, 9160/tcp   dockermyproject_cassandran_4
788dd5732ac7        cassandra           "/docker-entrypoint.s"   4 seconds ago       Up 3 seconds        7000-7001/tcp, 7199/tcp, 9042/tcp, 9160/tcp   dockermyproject_cassandran_2
851dc5295705        cassandra           "/docker-entrypoint.s"   4 seconds ago       Up 3 seconds        7000-7001/tcp, 7199/tcp, 9042/tcp, 9160/tcp   dockermyproject_cassandran_5
ec3d4a96a7a9        cassandra           "/docker-entrypoint.s"   4 seconds ago       Up 3 seconds        7000-7001/tcp, 7199/tcp, 9042/tcp, 9160/tcp   dockermyproject_cassandran_3
48d7187e6330        cassandra           "/docker-entrypoint.s"   13 minutes ago      Up 9 minutes        7000-7001/tcp, 7199/tcp, 9042/tcp, 9160/tcp   dockermyproject_cassandran_1
2ca9652f450f        cassandra           "/docker-entrypoint.s"   13 minutes ago      Up 9 minutes        7000-7001/tcp, 7199/tcp, 9042/tcp, 9160/tcp   dockermyproject_cassandra0_1

$ docker run -it --link  dockermyproject_cassandran_2:cassandra --rm cassandra cqlsh cassandra
Connected to Test Cluster at cassandra:9042.
[cqlsh 5.0.1 | Cassandra 2.2.3 | CQL spec 3.3.1 | Native protocol v4]
Use HELP for help.
cqlsh> use myproject;
cqlsh:myproject> describe myproject;

CREATE KEYSPACE myproject WITH replication = {'class': 'SimpleStrategy', 'replication_factor': '1'}  AND durable_writes = true;

CREATE TABLE myproject.users (
    lastname text PRIMARY KEY,
    age int,
    city text,
    email text,
    firstname text
) WITH bloom_filter_fp_chance = 0.01
    AND caching = '{"keys":"ALL", "rows_per_partition":"NONE"}'
    AND comment = ''
    AND compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy'}
    AND compression = {'sstable_compression': 'org.apache.cassandra.io.compress.LZ4Compressor'}
    AND dclocal_read_repair_chance = 0.1
    AND default_time_to_live = 0
    AND gc_grace_seconds = 864000
    AND max_index_interval = 2048
    AND memtable_flush_period_in_ms = 0
    AND min_index_interval = 128
    AND read_repair_chance = 0.0
    AND speculative_retry = '99.0PERCENTILE';

Il est également possible de définir la notion de multi-site le fichier docker-compose.yml

Conclusion

Avec Docker Compose, il a été possible d’obtenir un cluster Cassandra en très peu de temps et d’effort. Cet environnement est disponible pour les développeurs qui vont pouvoir tester leur code dans des conditions très proches de la production. Ici par exemple avec de la réplication et un certain nombre de nœuds.

Quel sera le prochain logiciel qui passera dans Composer ? 🙂