使用 Docker 部署 Elasticsearch 集群

2025/08/06

目前最新的 Elasticsearch 集群采用部署包的方式安装配置的过程还是比较复杂的,除了基本的配置外还涉及到 SSL 证书相关的配置,所以采用容器的方式部署就显得更加方便。

官方直接提供了容器化的 Docker 镜像,但是给的示例都只是单机情况下的伪分布式部署方案,我们稍微进行修改就可以在集群中部署了。

部署过程参考:https://www.elastic.co/guide/en/elasticsearch/reference/current/docker.html

1. 环境准备

当前我们部署的版本是:8.15.2

我们当前的节点分布如下:

10.0.2.51  es01
10.0.2.52  es02
10.0.2.53  es03

我们首先在所有节点都拉取 Elasticsearch 的镜像:

docker pull docker.elastic.co/elasticsearch/elasticsearch:8.15.2

然后选择其中一个节点拉取 Kibana 镜像:

docker pull docker.elastic.co/kibana/kibana:8.15.2

2. 配置集群

这一步我们按照官网的提示下载 .envdocker-compose.yml 示例文件,我们可以在每个节点创建一个目录,例如 /opt/elasticsearch-8.15.2 作为 Docker Compose 的项目目录,并且把上面下载的文件放进去。

2.1. 修改 .env 配置

我们首先来修改 .env 这个文件:

# Password for the 'elastic' user (at least 6 characters)
ELASTIC_PASSWORD=elastic-search

# Password for the 'kibana_system' user (at least 6 characters)
KIBANA_PASSWORD=kibana-elastic

# Version of Elastic products
STACK_VERSION=8.15.2

# Set the cluster name
CLUSTER_NAME=es-cluster

# Set to 'basic' or 'trial' to automatically start the 30-day trial
LICENSE=basic
#LICENSE=trial

# Port to expose Elasticsearch HTTP API to the host
ES_PORT=9200
#ES_PORT=127.0.0.1:9200

# Port to expose Elasticsearch Transport to the host
ES_TRANSPORT_PORT=9300

# Port to expose Kibana to the host
KIBANA_PORT=5601
#KIBANA_PORT=80

# Increase or decrease based on the available host memory (in bytes)
MEM_LIMIT=17179869184

# Project namespace (defaults to the current folder name if not set)
#COMPOSE_PROJECT_NAME=myproject

我们要设置其中 elastic 用户的密码、kibana_system 用户的密码、版本号和集群名等。

然后需要配置端口号,其中 ES_PORT 设置 Elasticsearch HTTP 的端口,默认是 9200。KIBANA_PORT 设置 Kibana 的端口,默认是 5601。开始配置中并没有 Transport 端口,因为我们是跨节点部署,所以我们添加 ES_TRANSPORT_PORT 变量,默认是 9300。

最后 MEM_LIMIT 设置 Docker 容器对内存的限制,设置后对于 JVM 内存会自动进行配置,正常我们就不需要单独指定 JVM 的内存参数了。

2.1. 修改 docker-compose.yml 配置文件

另外重点就是修改 docker-compose.yml 配置文件,官网给的其中有一个 setup 服务、多个 Elasticsearch 服务以及一个 Kibana 服务,我们这里需要对其进行修改和拆分,按照证书、服务分开进行配置。

配置证书

为了方便配置证书,我们先将 setup 服务单独拆出来一个文件,也就是 docker-compose-setup.yml 内容如下:

version: "2.2"

services:
  setup:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - ./certs:/usr/share/elasticsearch/config/certs
    user: "0"
    command: >
      bash -c '
        if [ x${ELASTIC_PASSWORD} == x ]; then
          echo "Set the ELASTIC_PASSWORD environment variable in the .env file";
          exit 1;
        elif [ x${KIBANA_PASSWORD} == x ]; then
          echo "Set the KIBANA_PASSWORD environment variable in the .env file";
          exit 1;
        fi;
        if [ ! -f config/certs/ca.zip ]; then
          echo "Creating CA";
          bin/elasticsearch-certutil ca --silent --pem -out config/certs/ca.zip;
          unzip config/certs/ca.zip -d config/certs;
        fi;
        if [ ! -f config/certs/certs.zip ]; then
          echo "Creating certs";
          bin/elasticsearch-certutil cert --silent --pem -out config/certs/certs.zip --in config/certs/instances.yml --ca-cert config/certs/ca/ca.crt --ca-key config/certs/ca/ca.key;
          unzip config/certs/certs.zip -d config/certs;
        fi;
        echo "Setting file permissions"
        chown -R root:root config/certs;
        find . -type d -exec chmod 750 \{\} \;;
        find . -type f -exec chmod 640 \{\} \;;
        echo "All done!";
      '

我们删除了默认的容器卷,直接使用本地目录代替,这一步是用来生成证书的,我们首先创建证书目录:

mkdir ./certs
chmod g+rwx ./certs
chgrp 0 ./certs

这里我们一定要设置用户组的权限为 rwx,因为 Elasticsearch 运行的用户组是 0,而用户则是普通用户,否则将没有权限写入数据,然后我们将 instances.yml 文件抽取出来了,这样可以方便配置,我们在 certs 目录下新建 instances.yml 配置文件:

instances:
  - name: es01
    dns:
      - es01
      - localhost
    ip:
      - 10.0.2.51
      - 127.0.0.1
  - name: es02
    dns:
      - es02
      - localhost
    ip:
      - 10.0.2.52
      - 127.0.0.1
  - name: es03
    dns:
      - es03
      - localhost
    ip:
      - 10.0.2.53
      - 127.0.0.1

文件内容非常简单,配置后我们保存文件。

然后我们生成证书:

docker compose -f docker-compose-setup.yml up

正常执行完成后就可以看到在 ./certs 下生成了证书,其中有一个 ca 目录,存放根证书,然后有多个以节点名称名称的目录,分别存放每个节点的证书。

执行完成之后目录的用户组权限会重置回来,之后就是只读的状态,如果我们要再次生成证书一定记得再为用户组添加以下可写的权限。

配置没问题之后我们将证书目录同步到所有的节点。

配置 Elasticsearch 和 Kibana 服务

然后我们在第一个节点上配置 Elasticsearch 服务的 Docker Compose 文件:

x-extra_hosts:
  &es_extra_hosts
  - "es01:10.0.2.51"
  - "es02:10.0.2.52"
  - "es03:10.0.2.53"

services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - ./certs:/usr/share/elasticsearch/config/certs
      - /data/elasticsearch:/usr/share/elasticsearch/data
    ports:
      - ${ES_PORT}:9200
      - ${ES_TRANSPORT_PORT}:9300
    extra_hosts: *es_extra_hosts
    environment:
      - node.name=es01
      - cluster.name=${CLUSTER_NAME}
      - cluster.initial_master_nodes=es01,es02,es03
      - discovery.seed_hosts=es01,es02,es03
      - network.publish_host=es01
      - ELASTIC_PASSWORD=${ELASTIC_PASSWORD}
      - bootstrap.memory_lock=true
      - xpack.security.enabled=true
      - xpack.security.http.ssl.enabled=true
      - xpack.security.http.ssl.key=certs/es01/es01.key
      - xpack.security.http.ssl.certificate=certs/es01/es01.crt
      - xpack.security.http.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.enabled=true
      - xpack.security.transport.ssl.key=certs/es01/es01.key
      - xpack.security.transport.ssl.certificate=certs/es01/es01.crt
      - xpack.security.transport.ssl.certificate_authorities=certs/ca/ca.crt
      - xpack.security.transport.ssl.verification_mode=certificate
      - xpack.license.self_generated.type=${LICENSE}
    mem_limit: ${MEM_LIMIT}
    ulimits:
      memlock:
        soft: -1
        hard: -1
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s --cacert config/certs/ca/ca.crt https://localhost:9200 | grep -q 'missing authentication credentials'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120


  kibana:
    image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
    volumes:
      - ./certs:/usr/share/kibana/config/certs
      - /data/kibana:/usr/share/kibana/data
    ports:
      - ${KIBANA_PORT}:5601
    extra_hosts: *es_extra_hosts
    environment:
      - SERVERNAME=kibana
      - ELASTICSEARCH_HOSTS=https://es01:9200
      - ELASTICSEARCH_USERNAME=kibana_system
      - ELASTICSEARCH_PASSWORD=${KIBANA_PASSWORD}
      - ELASTICSEARCH_SSL_CERTIFICATEAUTHORITIES=config/certs/ca/ca.crt
    mem_limit: ${MEM_LIMIT}
    healthcheck:
      test:
        [
          "CMD-SHELL",
          "curl -s -I http://localhost:5601 | grep -q 'HTTP/1.1 302 Found'",
        ]
      interval: 10s
      timeout: 10s
      retries: 120

x-extra_hosts 这个相当于配置了公共的 hosts,因为我们是使用 Docker 运行,容器内无法识别宿主机上配置的主机名,所以我们将 hosts 也传到容器中,这样就可以方便使用主机名访问了,由于 Elasticsearch 服务本身和 Kibana 都需要访问主机,所以我们使用 x-* 的写法在配置文件级别共享,实际使用的时候 Docker Compose 会将配置替换进去,我们可以使用下面命令查看最终的配置:

docker compose config

确认没问题后我们再来看其他部分的配置。

volumes 部分除了映射证书目录之外,还要映射 Elasticsearch 数据目录,这个目录我们要提前创建并且设置权限:

mkdir /data/elasticsearch
chmod g+rwx /data/elasticsearch

如果权限不足在启动时会有下面的报错:

failed to obtain node locks, tried [/usr/share/elasticsearch/data]; maybe these locations are not writable or multiple nodes were started on the same data path?

然后 ports 部分是端口映射,使用刚才 .env 中的配置,这里也不需要关心。

重点是 environment 中的变量配置:

  1. node.name 每个机器都要单独配置自己的节点名称。

  2. cluster.initial_master_nodesdiscovery.seed_hosts 配置所有节点的列表即可。

  3. network.publish_host 这个参数默认情况下没有配置,所以启动时会报错:

    [172.17.0.2:9300] handshake failed. unexpected remote node
    

    这是因为 Docker 容器默认发布出来的地址可能是容器的地址,这在另外的容器中是无法访问到的,所以我们需要设置为当前容器所在节点的实际主机名或者 IP 地址,这里我们使用当前节点的主机名。

也可以将上面的各个主机的环境变量配置到 .env 中,这样管理配置更加统一方便。

然后是下面证书的部分配置按照实际的节点名称替换即可。

最后下面 Kibana 部分的配置也比较简单,就不再详细叙述了。

配置修改好之后同步到其他的机器,保证集群所有的节点都要对应修改配置并且创建数据目录设置好权限。

3. 启动集群

在每个节点依次启动 Elasticsearch 服务:

docker compose up elasticsearch -d

都启动后我们可以查看集群的状态:

curl --cacert ./certs/ca/ca.crt -u elastic:$ELASTIC_PASSWORD https://localhost:9200/_cat/nodes?v
# 或者 Docker Compose 内部执行
docker compose exec -it elasticsearch curl --cacert /usr/share/elasticsearch/config/certs/ca/ca.crt -u elastic:$ELASTIC_PASSWORD https://localhost:9200/_cat/health?v

这样集群就安装好了,然后我们选择其中一个节点启动 Kabana 服务,在启动之前我们同样要创建 Kibana 的数据目录并配置权限:

mkdir /data/kibana
chmod g+rwx /data/kibana

然后我们还需要在 Elasticsearch 中初始化 Kibana 用户的密码,否则启动 Kibana 时会报错:

Unable to retrieve version information from Elasticsearch nodes. security_exception
kibana-1  |     Root causes:
kibana-1  |             security_exception: unable to authenticate user [kibana_system] for REST request [/_nodes?filter_path=nodes.*.version%2Cnodes.*.http.publish_address%2Cnodes.*.ip]
# ES 中同时报错
Authentication of [kibana_system] was terminated by realm [reserved] - failed to authenticate user [kibana_system]

我们在任意节点重置 Kibana 用户的密码,设置为和上面 .env 中配置的一样即可:

docker compose exec -it elasticsearch bin/elasticsearch-reset-password -i -u kibana_system

设置好之后我们启动 Kibana 服务:

docker compose up kibana -d

等待启动完毕,我们访问对应节点的 5601 端口即可打开页面了,然后使用 elastic 用户登录即可访问。