Categories
DevOps

How to create Elasticsearch cluster using docker

Create an Elasticsearch cluster using docker to learn how it behaves during specific operations.

Preparation

Tune system settings to deal with initial bootstrap issues.

elasticsearch-emu | ERROR: [2] bootstrap checks failed
elasticsearch-emu | [1]: memory locking requested for elasticsearch process but memory is not locked
elasticsearch-emu | [2]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

Enable memory locking to prevent storing JVM heap on the disk.

$ sudo mkdir /etc/systemd/system/docker.service.d
$ echo -e "[Service]\nLimitMEMLOCK=infinity" | sudo tee /etc/systemd/system/docker.service.d/memlock.conf
$ sudo systemctl daemon-reload
$ sudo systemctl restart docker

Increase the maximum number of memory map areas a process may have.

$ echo vm.max_map_count=262144 | sudo tee /etc/sysctl.d/99-max_map_count.conf
$ echo vm.max_map_count=262144 | sudo tee /etc/sysctl.d/99-max_map_count.conf
$ sudo sysctl --system

Elasticsearch 6

Docker-compose for Elasticsearch 6.8.11. This cluster consists of five nodes, but only three are master eligible ones.

version: "3.3"
services:
  elasticsearch-eel:
    image: elasticsearch:6.8.11
    environment:
      node.name: elasticsearch-eel
      cluster.name: elasticsearch-cluster
      discovery.zen.ping.unicast.hosts: elasticsearch-eel,elasticsearch-elk,elasticsearch-emu,elasticsearch-ewe,elasticsearch-ewt
      bootstrap.memory_lock: "true"
      discovery.zen.minimum_master_nodes: 2
      ES_JAVA_OPTS: "-Xmx512m -Xms512m"
    volumes:
      - elasticsearch_data_eel:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - elasticsearch-internal-network
    labels:
      - stack=elasticsearch6
      - traefik.http.routers.elasticsearch.rule=PathPrefix(`/`)
      - traefik.http.services.elasticsearch.loadbalancer.server.port=9200
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.path=/_cluster/health?local=true
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.interval=15s
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.timeout=10s
  elasticsearch-elk:
    image: elasticsearch:6.8.11
    environment:
      node.name: elasticsearch-elk
      cluster.name: elasticsearch-cluster
      discovery.zen.ping.unicast.hosts: elasticsearch-eel,elasticsearch-elk,elasticsearch-emu,elasticsearch-ewe,elasticsearch-ewt
      bootstrap.memory_lock: "true"
      discovery.zen.minimum_master_nodes: 2
      ES_JAVA_OPTS: "-Xmx512m -Xms512m"
    volumes:
      - elasticsearch_data_elk:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - elasticsearch-internal-network
    labels:
      - stack=elasticsearch6
      - traefik.http.routers.elasticsearch.rule=PathPrefix(`/`)
      - traefik.http.services.elasticsearch.loadbalancer.server.port=9200
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.path=/_cluster/health?local=true
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.interval=15s
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.timeout=10s
  elasticsearch-emu:
    image: elasticsearch:6.8.11
    environment:
      node.name: elasticsearch-emu
      cluster.name: elasticsearch-cluster
      discovery.zen.ping.unicast.hosts: elasticsearch-eel,elasticsearch-elk,elasticsearch-emu,elasticsearch-ewe,elasticsearch-ewt
      bootstrap.memory_lock: "true"
      discovery.zen.minimum_master_nodes: 2
      ES_JAVA_OPTS: "-Xmx512m -Xms512m"
    volumes:
      - elasticsearch_data_emu:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - elasticsearch-internal-network
    labels:
      - stack=elasticsearch6
      - traefik.http.routers.elasticsearch.rule=PathPrefix(`/`)
      - traefik.http.services.elasticsearch.loadbalancer.server.port=9200
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.path=/_cluster/health?local=true
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.interval=15s
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.timeout=10s
  elasticsearch-ewe:
    image: elasticsearch:6.8.11
    environment:
      node.name: elasticsearch-ewe
      cluster.name: elasticsearch-cluster
      discovery.zen.ping.unicast.hosts: elasticsearch-eel,elasticsearch-elk,elasticsearch-emu,elasticsearch-ewe,elasticsearch-ewt
      bootstrap.memory_lock: "true"
      node.master: "false"
      discovery.zen.minimum_master_nodes: 2
      ES_JAVA_OPTS: "-Xmx512m -Xms512m"
    volumes:
      - elasticsearch_data_ewe:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - elasticsearch-internal-network
    labels:
      - stack=elasticsearch6
      - traefik.http.routers.elasticsearch.rule=PathPrefix(`/`)
      - traefik.http.services.elasticsearch.loadbalancer.server.port=9200
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.path=/_cluster/health?local=true
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.interval=15s
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.timeout=10s
  elasticsearch-ewt:
    image: elasticsearch:6.8.11
    environment:
      node.name: elasticsearch-ewt
      cluster.name: elasticsearch-cluster
      discovery.zen.ping.unicast.hosts: elasticsearch-eel,elasticsearch-elk,elasticsearch-emu,elasticsearch-ewe,elasticsearch-ewt
      bootstrap.memory_lock: "true"
      node.master: "false"
      discovery.zen.minimum_master_nodes: 2
      ES_JAVA_OPTS: "-Xmx512m -Xms512m"
    volumes:
      - elasticsearch_data_ewt:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - elasticsearch-internal-network
    labels:
      - stack=elasticsearch6
      - traefik.http.routers.elasticsearch.rule=PathPrefix(`/`)
      - traefik.http.services.elasticsearch.loadbalancer.server.port=9200
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.path=/_cluster/health?local=true
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.interval=15s
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.timeout=10s
  traefik:
    image: traefik:2.3
    command:
      - --entrypoints.web.address=:9200
      - --providers.docker=true
      - --providers.docker.constraints=Label(`stack`,`elasticsearch6`)
      - --api.insecure
      - --accesslog=true
    depends_on:
      - elasticsearch-eel
      - elasticsearch-elk
      - elasticsearch-emu
      - elasticsearch-ewe
      - elasticsearch-ewt
    ports:
      - 9200:9200
      - 8080:8080
      - 8000:8000
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - elasticsearch-internal-network
      - traefik-external-network
volumes:
  elasticsearch_data_eel:
  elasticsearch_data_elk:
  elasticsearch_data_emu:
  elasticsearch_data_ewe:
  elasticsearch_data_ewt:
networks:
  elasticsearch-internal-network:
    internal: true
  traefik-external-network:

Cluster status.

$ curl http://localhost:9200/_cluster/health?pretty
{
  "cluster_name" : "elasticsearch-cluster",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 5,
  "number_of_data_nodes" : 5,
  "active_primary_shards" : 0,
  "active_shards" : 0,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

Node list.

$ curl http://localhost:9200/_cat/nodes?v
ip         heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.30.0.3           75          99  27    1.62    2.87     3.45 di        -      elasticsearch-ewt
172.30.0.2           68          99  27    1.62    2.87     3.45 di        -      elasticsearch-ewe
172.30.0.4           35          99  27    1.62    2.87     3.45 mdi       *      elasticsearch-emu
172.30.0.6           69          99  27    1.62    2.87     3.45 mdi       -      elasticsearch-elk
172.30.0.5           46          99  27    1.62    2.87     3.45 mdi       -      elasticsearch-eel

Please read about node roles in Elasticsearch 6.

Elasticsearch 7

Docker-compose for Elasticsearch 7.8.1. This cluster consists of five nodes, but only three are master eligible ones.

version: "3.3"
services:
  elasticsearch-tapir:
    image: elasticsearch:7.8.1
    environment:
      node.name: elasticsearch-tapir
      cluster.name: elasticsearch-cluster
      discovery.seed_hosts: elasticsearch-tetra,elasticsearch-tiger
      cluster.initial_master_nodes: elasticsearch-tapir,elasticsearch-tetra,elasticsearch-tiger
      bootstrap.memory_lock: "true"
      ES_JAVA_OPTS: "-Xmx512m -Xms512m"
    volumes:
      - elasticsearch_data_tapir:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - elasticsearch-internal-network
    labels:
      - stack=elasticsearch7
      - traefik.http.routers.elasticsearch.rule=PathPrefix(`/`)
      - traefik.http.services.elasticsearch.loadbalancer.server.port=9200
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.path=/_cluster/health?local=true
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.interval=15s
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.timeout=10s
  elasticsearch-tetra:
    image: elasticsearch:7.8.1
    environment:
      node.name: elasticsearch-tetra
      cluster.name: elasticsearch-cluster
      discovery.seed_hosts: elasticsearch-tapir,elasticsearch-tiger
      cluster.initial_master_nodes: elasticsearch-tapir,elasticsearch-tetra,elasticsearch-tiger
      bootstrap.memory_lock: "true"
      ES_JAVA_OPTS: "-Xmx512m -Xms512m"
    volumes:
      - elasticsearch_data_tetra:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - elasticsearch-internal-network
    labels:
      - stack=elasticsearch7
      - traefik.http.routers.elasticsearch.rule=PathPrefix(`/`)
      - traefik.http.services.elasticsearch.loadbalancer.server.port=9200
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.path=/_cluster/health?local=true
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.interval=15s
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.timeout=10s
  elasticsearch-tiger:
    image: elasticsearch:7.8.1
    environment:
      node.name: elasticsearch-tiger
      cluster.name: elasticsearch-cluster
      discovery.seed_hosts: elasticsearch-tapir,elasticsearch-tetra
      cluster.initial_master_nodes: elasticsearch-tapir,elasticsearch-tetra,elasticsearch-tiger
      ES_JAVA_OPTS: "-Xmx512m -Xms512m"
    volumes:
      - elasticsearch_data_tiger:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - elasticsearch-internal-network
    labels:
      - stack=elasticsearch7
      - traefik.http.routers.elasticsearch.rule=PathPrefix(`/`)
      - traefik.http.services.elasticsearch.loadbalancer.server.port=9200
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.path=/_cluster/health?local=true
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.interval=15s
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.timeout=10s
  elasticsearch-trout:
    image: elasticsearch:7.8.1
    environment:
      node.name: elasticsearch-trout
      cluster.name: elasticsearch-cluster
      discovery.seed_hosts: elasticsearch-tapir,elasticsearch-tetra,elasticsearch-tiger
      cluster.initial_master_nodes: elasticsearch-tapir,elasticsearch-tetra,elasticsearch-tiger
      bootstrap.memory_lock: "true"
      node.master: "false"
      ES_JAVA_OPTS: "-Xmx512m -Xms512m"
    volumes:
      - elasticsearch_data_trout:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - elasticsearch-internal-network
    labels:
      - stack=elasticsearch7
      - traefik.http.routers.elasticsearch.rule=PathPrefix(`/`)
      - traefik.http.services.elasticsearch.loadbalancer.server.port=9200
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.path=/_cluster/health?local=true
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.interval=15s
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.timeout=10s
  elasticsearch-tayra:
    image: elasticsearch:7.8.1
    environment:
      node.name: elasticsearch-tayra
      cluster.name: elasticsearch-cluster
      discovery.seed_hosts: elasticsearch-tapir,elasticsearch-tetra,elasticsearch-tiger
      cluster.initial_master_nodes: elasticsearch-tapir,elasticsearch-tetra,elasticsearch-tiger
      bootstrap.memory_lock: "true"
      node.master: "false"
      ES_JAVA_OPTS: "-Xmx512m -Xms512m"
    volumes:
      - elasticsearch_data_tayra:/usr/share/elasticsearch/data
    ulimits:
      memlock:
        soft: -1
        hard: -1
    networks:
      - elasticsearch-internal-network
    labels:
      - stack=elasticsearch7
      - traefik.http.routers.elasticsearch.rule=PathPrefix(`/`)
      - traefik.http.services.elasticsearch.loadbalancer.server.port=9200
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.path=/_cluster/health?local=true
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.interval=15s
      - traefik.http.services.elasticsearch.loadbalancer.healthcheck.timeout=10s
  traefik:
    image: traefik:2.3
    command:
      - --entrypoints.web.address=:9200
      - --providers.docker=true
      - --providers.docker.constraints=Label(`stack`,`elasticsearch7`)
      - --api.insecure
      - --accesslog=true
    depends_on:
      - elasticsearch-tapir
      - elasticsearch-tetra
      - elasticsearch-tiger
      - elasticsearch-trout
      - elasticsearch-tayra
    ports:
      - 9200:9200
      - 8080:8080
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
    networks:
      - elasticsearch-internal-network
      - traefik-external-network
volumes:
  elasticsearch_data_tapir:
  elasticsearch_data_tetra:
  elasticsearch_data_tiger:
  elasticsearch_data_trout:
  elasticsearch_data_tayra:
networks:
  elasticsearch-internal-network:
    internal: true
  traefik-external-network:

Cluster status.

$ curl http://localhost:9200/_cluster/health?pretty
{
  "cluster_name" : "elasticsearch-cluster",
  "status" : "green",
  "timed_out" : false,
  "number_of_nodes" : 5,
  "number_of_data_nodes" : 5,
  "active_primary_shards" : 0,
  "active_shards" : 0,
  "relocating_shards" : 0,
  "initializing_shards" : 0,
  "unassigned_shards" : 0,
  "delayed_unassigned_shards" : 0,
  "number_of_pending_tasks" : 0,
  "number_of_in_flight_fetch" : 0,
  "task_max_waiting_in_queue_millis" : 0,
  "active_shards_percent_as_number" : 100.0
}

Node list.

$ curl http://localhost:9200/_cat/nodes?v
ip         heap.percent ram.percent cpu load_1m load_5m load_15m node.role master name
172.30.0.4           20          97  26    6.13    3.95     3.75 dilmrt    *      elasticsearch-tetra
172.30.0.5           47          97  26    6.13    3.95     3.75 dilrt     -      elasticsearch-tayra
172.30.0.3           62          97  26    6.13    3.95     3.75 dilrt     -      elasticsearch-trout
172.30.0.6           20          97  26    6.13    3.95     3.75 dilmrt    -      elasticsearch-tiger
172.30.0.2           33          97  26    6.13    3.95     3.75 dilmrt    -      elasticsearch-tapir

Please read about node roles in Elasticsearch 7.