一键搭建 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"
有任何问题欢迎反馈