Chạy Elasticsearch một nút và Kibana với docker compose trên linux

Từ phiên bản 8.x trở đi, Elasticsearch sử dụng kết nối ssl làm mặc định. Việc kết nối ssl từ website đến Elasticsearch đối với mình cũng là một vấn đề lớn. Chính vì vậy mình đi theo hướng khác - sử dụng Nginx-proxy-manager để tạo kết nối ssl cho elastic.

:warning: Elasticsearch trong bài này chạy bên dưới nginx-proxy-manager. Nếu bạn không sử dụng nginx-proxy-manager, hãy vô hiệu hóa các phần liên quan trong tệp docker-compose.

Giả sử bạn đã cài đặt docker-compose và nginx-proxy-manager trên hệ thống. bạn chỉ cần tạo 2 tệp sau để chạy Elasticsearch.

Trước tiện tạo thư mục chứa elastic như:

mkdir /opt/elastic8

Trong thư mục elastic8 tạo 2 tệp .envdocker-compose.yml như sau

Tệp .env

# .env
# Password for the 'elastic' user (at least 6 characters)
ELASTIC_PASSWORD=elastic

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

# Version of Elastic products
STACK_VERSION=8.5.3

# Set the cluster name
CLUSTER_NAME=docker-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 Kibana to the host
KIBANA_PORT=5601
#KIBANA_PORT=80

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

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

ELASTIC_CLIENT_APIVERSIONING=true

Lưu ý: Thay đổi giá trị của ELASTIC_PASSWORD và KIBANA_PASSWORD tùy theo bạn.

Tệp docker-compose.yml

version: "3.9"

services:
  setup:
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
    user: "0"
    networks:
      - elastic_net
    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";
          echo -ne \
          "instances:\n"\
          "  - name: es01\n"\
          "    dns:\n"\
          "      - es01\n"\
          "      - localhost\n"\
          "    ip:\n"\
          "      - 127.0.0.1\n"\
          > config/certs/instances.yml;
          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 "Waiting for Elasticsearch availability";
        until curl -s --cacert config/certs/ca/ca.crt https://es01:9200 | grep -q "missing authentication credentials"; do sleep 30; done;
        echo "Setting kibana_system password";
        until curl -s -X POST --cacert config/certs/ca/ca.crt -u elastic:${ELASTIC_PASSWORD} -H "Content-Type: application/json" https://es01:9200/_security/user/kibana_system/_password -d "{\"password\":\"${KIBANA_PASSWORD}\"}" | grep -q "^{}"; do sleep 10; done;
        echo "All done!";
      '      
    healthcheck:
      test: ["CMD-SHELL", "[ -f config/certs/es01/es01.crt ]"]
      interval: 1s
      timeout: 5s
      retries: 120

  es01:
    depends_on:
      setup:
        condition: service_healthy
    image: docker.elastic.co/elasticsearch/elasticsearch:${STACK_VERSION}
    volumes:
      - certs:/usr/share/elasticsearch/config/certs
      - esdata01:/usr/share/elasticsearch/data
    ports:
      - ${ES_PORT}:9200
    networks:
      - nginx-proxy-man
      - elastic_net
      # restart: always
    environment:
      - node.name=es01
      - cluster.name=${CLUSTER_NAME}
      - cluster.initial_master_nodes=es01
      #- discovery.seed_hosts=
      - 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.http.ssl.verification_mode=certificate
      - 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}
      - http.cors.allow-origin=http://localhost,http://127.0.0.1, example.com
      - http.cors.enabled=true
      - http.cors.allow-headers=X-Requested-With,X-Auth-Token,Content-Type,Content-Length,Authorization
      - http.cors.allow-credentials=true
    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:
    depends_on:
      es01:
        condition: service_healthy
    image: docker.elastic.co/kibana/kibana:${STACK_VERSION}
    volumes:
      - certs:/usr/share/kibana/config/certs
      - kibanadata:/usr/share/kibana/data
    ports:
      - ${KIBANA_PORT}:5601
    networks:
      - nginx-proxy-man
      - elastic_net
    # restart: always
    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

      
volumes:
  certs:
    driver: local
  esdata01:
    driver: local
  kibanadata:
    driver: local

networks:
  nginx-proxy-man:
    external: true
  elastic_net:
    internal: true

:warning: Lưu ý, nginx-proxy-man là mạng bên ngoài của Nginx Proxy Manager

Chạy ElasticSearch

cd /opt/elastic8
docker compose up -d --build

Sau khi cài đặt xong, chạy lệnh docker compose ps để kiểm tra. Nếu gặp lỗi Exit 137 (code 137), hãy tham khảo bài viết sau: Cách kiểm tra và thay đổi vm.max_map_count mặc định trên hệ thống Linux

❯ docker compose ps
NAME                COMMAND                  SERVICE             STATUS              PORTS
elastic8-es01-1     "/bin/tini -- /usr/l…"   es01                running (healthy)   0.0.0.0:9200->9200/tcp, :::9200->9200/tcp, 9300/tcp
elastic8-kibana-1   "/bin/tini -- /usr/l…"   kibana              running (healthy)   0.0.0.0:5601->5601/tcp, :::5601->5601/tcp

Để có thể vào được Elastic hoặc Kibana, chúng ta cần khai báo nó trong Nginx Proxy Manager:

Forward/Hostname/IP chính là container_name mà chúng ta có khi chạy docker compose ps

Chuyển qua Tab SSL và thiết lập ssl cho domain.
Với Kibana, thực hiên tương tự. Để vào kibana: https://kibana.domain.com hãy nhập mật khẩu mà bạn đã khai báo trong tệp .env