一键搭建 es + kibana

bash 命令 docker 一键部署 es + kibana

为方便部署单机 es,写了个 bash 脚本

#!/bin/bash

# Record start time
START_TIME=$(date +%s)

# Normalize paths
normalize_paths() {
    SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
    if [[ "$BASE_DIR" == ./* ]]; then
        BASE_DIR="${SCRIPT_DIR}/${BASE_DIR#./}"
    elif [[ "$BASE_DIR" != /* ]]; then
        BASE_DIR="${SCRIPT_DIR}/${BASE_DIR}"
    fi
}

# Color output functions
print_green() { echo -e "\033[32m$1\033[0m"; }
print_yellow() { echo -e "\033[33m$1\033[0m"; }
print_red() { echo -e "\033[31m$1\033[0m"; }
print_blue() { echo -e "\033[34m$1\033[0m"; }

# Check directory permissions
check_directory_permissions() {
    TEST_DIR="./elastic-stack-test"
    mkdir -p "$TEST_DIR" || return 1
    echo "test" > "$TEST_DIR/test.txt" || { rm -rf "$TEST_DIR"; return 1; }
    rm -rf "$TEST_DIR"
    return 0
}

# Execute permission check
if ! check_directory_permissions; then
    print_red "Script requires permission to create files and directories in the current directory."
    exit 1
fi

print_green "✅ Directory permission check passed"

# Check Docker installation
if ! command -v docker &> /dev/null; then
    print_red "Docker not detected, please install Docker first"
    exit 1
fi

# Check Docker Compose installation
if ! command -v docker compose &> /dev/null; then
    print_red "Docker Compose not detected, please install Docker Compose first"
    exit 1
fi

# Detect operating system and adapt
detect_os() {
    if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" || "$OSTYPE" == "mingw"* ]]; then
        IS_WINDOWS=true
    else
        IS_WINDOWS=false
    fi
}

# Adapt path for Windows
adapt_path() {
    if [ "$IS_WINDOWS" = true ]; then
        BASE_DIR=$(echo "$BASE_DIR" | sed 's/\\/\//g')
        if [[ "$BASE_DIR" =~ ^[A-Za-z]: ]]; then
            BASE_DIR="./${BASE_DIR#?:}"
        fi
    fi
}

# Detect OS
detect_os

# Configuration parameters
ES_VERSION="8.12.2"
ES_PASSWORD="elastic_password"
KIBANA_PORT=5601
ES_PORT=9200
BASE_DIR="./elastic-stack"

# Adapt and normalize paths
adapt_path
normalize_paths

# Ask for custom password
read -p "Set custom password? (y/n, default n): " SET_CUSTOM_PASSWORD
if [[ $SET_CUSTOM_PASSWORD == "y" || $SET_CUSTOM_PASSWORD == "Y" ]]; then
    read -s -p "Enter Elasticsearch password: " CUSTOM_PASSWORD
    echo
    if [[ ${#CUSTOM_PASSWORD} -lt 6 ]]; then
        print_red "Password must be at least 6 characters"
        exit 1
    fi
    ES_PASSWORD=$CUSTOM_PASSWORD
    print_green "✅ Custom password set"
else
    print_yellow "Using default password: elastic_password"
fi

print_blue "====== Elasticsearch + Kibana Deployment Started ======"

# Create necessary directories
mkdir -p ${BASE_DIR}/elasticsearch/data
mkdir -p ${BASE_DIR}/elasticsearch/plugins
mkdir -p ${BASE_DIR}/kibana/config

# Validate directory creation
if [ ! -d "${BASE_DIR}/kibana/config" ]; then
    ABSOLUTE_BASE_DIR="$(pwd)/${BASE_DIR}"
    mkdir -p "${ABSOLUTE_BASE_DIR}/kibana/config"
    if [ -d "${ABSOLUTE_BASE_DIR}/kibana/config" ]; then
        BASE_DIR="${ABSOLUTE_BASE_DIR}"
    else
        print_red "Directory creation failed, check permissions and disk space"
        exit 1
    fi
fi

# Set directory permissions
if [ "$(uname)" = "Linux" ]; then
    chmod -R 777 ${BASE_DIR}/elasticsearch/data
    chmod -R 777 ${BASE_DIR}/elasticsearch/plugins
    chown -R 1000:1000 ${BASE_DIR}/elasticsearch/data || true
    chown -R 1000:1000 ${BASE_DIR}/elasticsearch/plugins || true
fi

# Generate Docker Compose file
print_yellow "Configuring Elasticsearch password: $ES_PASSWORD"

# Check if Windows environment for volume strategy
USE_NAMED_VOLUMES=false
if [[ "$OSTYPE" == "msys" || "$OSTYPE" == "cygwin" || "$OSTYPE" == "win32" ]]; then
    USE_NAMED_VOLUMES=true
fi

# Create Docker Compose file with appropriate volume strategy
if [ "$USE_NAMED_VOLUMES" = true ]; then
    cat > ${BASE_DIR}/docker-compose.yml << EOF
version: "3.1"
services:
  elasticsearch:
    container_name: es02
    image: elasticsearch:${ES_VERSION}
    restart: unless-stopped
    environment:
      - "ES_JAVA_OPTS=-Xms1024m -Xmx1024m"
      - "TZ=Asia/Shanghai"
      - "discovery.type=single-node"
      - "ELASTIC_PASSWORD=${ES_PASSWORD}"
      - "xpack.security.enabled=true"
      - "xpack.security.authc.api_key.enabled=true"
      - "xpack.security.authc.token.enabled=true"
      - "bootstrap.memory_lock=true"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    ports:
      - "${ES_PORT}:9200"
      - "9300:9300"
    volumes:
      - esdata:/usr/share/elasticsearch/data
      - esplugins:/usr/share/elasticsearch/plugins
    networks:
      - elastic_net
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9200"]
      interval: 30s
      timeout: 10s
      retries: 5

  kibana:
    container_name: kibana02
    image: kibana:${ES_VERSION}
    restart: unless-stopped
    volumes:
      - ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml
    environment:
      - "TZ=Asia/Shanghai"
      - "I18N_LOCALE=zh-CN"
      - "ELASTICSEARCH_HOSTS=http://elasticsearch:9200"
    ports:
      - "${KIBANA_PORT}:5601"
    networks:
      - elastic_net
    depends_on:
      - elasticsearch

networks:
  elastic_net:
    driver: bridge

volumes:
  esdata:
    driver: local
  esplugins:
    driver: local
EOF
else
    cat > ${BASE_DIR}/docker-compose.yml << EOF
version: "3.1"
services:
  elasticsearch:
    container_name: es02
    image: elasticsearch:${ES_VERSION}
    restart: unless-stopped
    environment:
      - "ES_JAVA_OPTS=-Xms1024m -Xmx1024m"
      - "TZ=Asia/Shanghai"
      - "discovery.type=single-node"
      - "ELASTIC_PASSWORD=${ES_PASSWORD}"
      - "xpack.security.enabled=true"
      - "xpack.security.authc.api_key.enabled=true"
      - "xpack.security.authc.token.enabled=true"
      - "bootstrap.memory_lock=true"
    ulimits:
      memlock:
        soft: -1
        hard: -1
      nofile:
        soft: 65536
        hard: 65536
    ports:
      - "${ES_PORT}:9200"
      - "9300:9300"
    volumes:
      - ./elasticsearch/data:/usr/share/elasticsearch/data
      - ./elasticsearch/plugins:/usr/share/elasticsearch/plugins
    networks:
      - elastic_net
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:9200"]
      interval: 30s
      timeout: 10s
      retries: 5

  kibana:
    container_name: kibana02
    image: kibana:${ES_VERSION}
    restart: unless-stopped
    volumes:
      - ./kibana/config/kibana.yml:/usr/share/kibana/config/kibana.yml
    environment:
      - "TZ=Asia/Shanghai"
      - "I18N_LOCALE=zh-CN"
      - "ELASTICSEARCH_HOSTS=http://elasticsearch:9200"
    ports:
      - "${KIBANA_PORT}:5601"
    networks:
      - elastic_net
    depends_on:
      - elasticsearch

networks:
  elastic_net:
    driver: bridge
EOF
fi

# Generate initial Kibana config
cat > ${BASE_DIR}/kibana/config/kibana.yml << EOF
server.name: kibana
server.host: 0.0.0.0
elasticsearch.hosts: [ "http://elasticsearch:9200" ]

# Temporary config - will be updated after Elasticsearch starts

# Localization
i18n.locale: "zh-CN"

# Disable telemetry
telemetry.enabled: false
EOF

# Start Elasticsearch
cd ${BASE_DIR}
docker compose up -d elasticsearch

# Wait for Elasticsearch to start
print_yellow "Waiting for Elasticsearch to start (may take a minute)..."
MAX_RETRIES=60
RETRY_COUNT=0

while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
    if curl --output /dev/null --silent --head --fail --max-time 5 -u elastic:${ES_PASSWORD} http://localhost:${ES_PORT}; then
        break
    else
        RETRY_COUNT=$((RETRY_COUNT+1))
        sleep 2
    fi
    
    if [ $RETRY_COUNT -eq $((MAX_RETRIES/2)) ]; then
        if ! docker ps | grep -q es02; then
            print_red "Error: Elasticsearch container not running. Check Docker logs."
            docker logs es02
            exit 1
        fi
    fi
done

if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
    print_red "Error: Timeout waiting for Elasticsearch to start."
    docker logs es02
    exit 1
fi

print_green "✅ Elasticsearch started successfully!"

# Flag to track token acquisition
KIBANA_TOKEN_ACQUIRED=false

# Create service account token for Kibana
print_yellow "Creating service account token for Kibana..."
TOKEN_NAME="kibana-token-$(date +%s)"
TOKEN_OUTPUT=$(docker exec es02 /bin/bash -c "bin/elasticsearch-service-tokens create elastic/kibana ${TOKEN_NAME}" 2>&1)
KIBANA_TOKEN=$(echo "$TOKEN_OUTPUT" | grep -oP "SERVICE_TOKEN elastic/kibana/${TOKEN_NAME} = \K.*")

if [ -n "$KIBANA_TOKEN" ]; then
    print_green "✅ Kibana service token created successfully"
    KIBANA_TOKEN_ACQUIRED=true
    
    # Create necessary roles and mappings
    docker exec es02 curl -s -X POST "http://localhost:9200/_security/role/kibana_system_role" \
        -H "Content-Type: application/json" \
        -u elastic:${ES_PASSWORD} \
        -d '{"cluster":["monitor","manage_index_templates","manage_ilm"],"indices":[{"names":[".kibana*",".reporting*"],"privileges":["all"]}]}' > /dev/null
    
    docker exec es02 curl -s -X POST "http://localhost:9200/_security/role_mapping/service_account_kibana_mapping" \
        -H "Content-Type: application/json" \
        -u elastic:${ES_PASSWORD} \
        -d '{"roles":["kibana_system","kibana_system_role"],"enabled":true,"rules":{"field":{"username":"elastic/kibana"}}}' > /dev/null
else
    # Try alternative method
    TOKEN_NAME="kibana-token2-$(date +%s)"
    TOKEN_OUTPUT=$(docker exec es02 /bin/bash -c "bin/elasticsearch-service-tokens create elastic/kibana ${TOKEN_NAME}" 2>&1)
    KIBANA_TOKEN=$(echo "$TOKEN_OUTPUT" | grep -oP "SERVICE_TOKEN elastic/kibana/${TOKEN_NAME} = \K.*")
    
    if [ -n "$KIBANA_TOKEN" ]; then
        KIBANA_TOKEN_ACQUIRED=true
        
        docker exec es02 curl -s -X POST "http://localhost:9200/_security/role/kibana_system_role" \
            -H "Content-Type: application/json" \
            -u elastic:${ES_PASSWORD} \
            -d '{"cluster":["monitor","manage_index_templates","manage_ilm"],"indices":[{"names":[".kibana*",".reporting*"],"privileges":["all"]}]}' > /dev/null
        
        docker exec es02 curl -s -X POST "http://localhost:9200/_security/role_mapping/service_account_kibana_mapping" \
            -H "Content-Type: application/json" \
            -u elastic:${ES_PASSWORD} \
            -d '{"roles":["kibana_system","kibana_system_role"],"enabled":true,"rules":{"field":{"username":"elastic/kibana"}}}' > /dev/null
    else
        print_red "❌ Failed to create service account token"
        exit 1
    fi
fi

# Ensure token was acquired
if [ "$KIBANA_TOKEN_ACQUIRED" != "true" ]; then
    print_red "❌ Failed to acquire Kibana service account token"
    exit 1
fi

# Update Kibana config with service token
cat > ${BASE_DIR}/kibana/config/kibana.yml << EOF
server.name: kibana
server.host: 0.0.0.0
elasticsearch.hosts: [ "http://elasticsearch:9200" ]

# Service account token authentication
elasticsearch.serviceAccountToken: ${KIBANA_TOKEN}

# Localization
i18n.locale: "zh-CN"

# Disable telemetry
telemetry.enabled: false
EOF

# Ensure config file contains token
if ! grep -q "elasticsearch.serviceAccountToken:" "${BASE_DIR}/kibana/config/kibana.yml"; then
    print_red "❌ Kibana config not properly updated, attempting direct write..."
    echo "elasticsearch.serviceAccountToken: ${KIBANA_TOKEN}" >> ${BASE_DIR}/kibana/config/kibana.yml
fi

# Test authentication
AUTH_TEST=$(docker exec es02 curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer ${KIBANA_TOKEN}" http://localhost:9200/_security/_authenticate)

if [ "$AUTH_TEST" != "200" ]; then
    print_red "Service account token authentication failed (HTTP ${AUTH_TEST}), retrying..."
    
    # Retry role mapping
    docker exec es02 curl -s -X POST "http://localhost:9200/_security/role_mapping/service_account_kibana_mapping" \
        -H "Content-Type: application/json" \
        -u elastic:${ES_PASSWORD} \
        -d '{"roles":["kibana_system","kibana_system_role"],"enabled":true,"rules":{"field":{"username":"elastic/kibana"}}}' > /dev/null
    
    # Test again
    sleep 2
    AUTH_TEST=$(docker exec es02 curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer ${KIBANA_TOKEN}" http://localhost:9200/_security/_authenticate)
fi

# Final check before starting Kibana
print_yellow "Starting Kibana..."

# Verify config file has token
CONFIG_FILE="${BASE_DIR}/kibana/config/kibana.yml"
if [ ! -f "$CONFIG_FILE" ] || ! grep -q "elasticsearch.serviceAccountToken:" "$CONFIG_FILE" 2>/dev/null; then
    print_red "❌ Warning: Token not found in Kibana config, rewriting..."
    mkdir -p "$(dirname "$CONFIG_FILE")"
    cat > "$CONFIG_FILE" << EOF
server.name: kibana
server.host: 0.0.0.0
elasticsearch.hosts: [ "http://elasticsearch:9200" ]

# Service account token authentication
elasticsearch.serviceAccountToken: ${KIBANA_TOKEN}

# Localization
i18n.locale: "zh-CN"

# Disable telemetry
telemetry.enabled: false
EOF
    
    if grep -q "elasticsearch.serviceAccountToken:" "$CONFIG_FILE" 2>/dev/null; then
        print_green "✅ Token successfully written to config file"
    else
        print_red "❌ Token write failed"
    fi
fi

# Start Kibana
docker compose up -d kibana

# Verify Kibana connection
verify_kibana_connection() {
    sleep 10
    KIBANA_LOGS=$(docker logs kibana02 2>&1)
    if echo "$KIBANA_LOGS" | grep -q "security_exception: unable to authenticate"; then
        print_red "⚠️ Authentication problem detected: Kibana authentication failed"
        return 2
    elif echo "$KIBANA_LOGS" | grep -q "ERROR"; then
        print_yellow "⚠️ Errors found in Kibana logs but not authentication issues."
        return 2
    else
        print_green "✅ Kibana connection to Elasticsearch successful"
        return 0
    fi
}

verify_kibana_connection

# Verify password in config
ES_CONFIG_PASSWORD=$(grep "ELASTIC_PASSWORD" "${BASE_DIR}/docker-compose.yml" | grep -o "=[^\"]*" | cut -d= -f2)
if [ "$ES_CONFIG_PASSWORD" != "$ES_PASSWORD" ]; then
    print_red "⚠️ Warning: Password mismatch in config file"
    sed -i "s/ELASTIC_PASSWORD=[^\"]*\"/ELASTIC_PASSWORD=${ES_PASSWORD}\"/" "${BASE_DIR}/docker-compose.yml"
    print_green "✅ Config file updated, restart Elasticsearch to apply new password:"
    print_yellow "cd ${BASE_DIR} && docker compose restart elasticsearch"
fi

# Calculate deployment time
END_TIME=$(date +%s)
ELAPSED_TIME=$((END_TIME - START_TIME))
MINUTES=$((ELAPSED_TIME / 60))
SECONDS=$((ELAPSED_TIME % 60))

print_green "
================================================
🎉 Deployment complete! Services are running:
🔹 Elasticsearch: http://localhost:${ES_PORT}
   Username: elastic
   Password: ${ES_PASSWORD}

🔹 Kibana: http://localhost:${KIBANA_PORT}
   Authentication: Service Account Token
================================================
"

print_blue "Deployment time: ${MINUTES}m${SECONDS}s" 

有任何问题欢迎反馈

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值