7 Commits

Author SHA1 Message Date
Your Name
183dcf5072 Improve heap configuration controls 2025-11-07 06:25:38 -06:00
Your Name
8fc7e89cdd Strip bootstrap.password from config cleanup 2025-11-07 05:24:08 -06:00
Your Name
825d522d5c Store bootstrap password only in keystore 2025-11-07 05:19:25 -06:00
Your Name
82825fd4c4 Fix path.plugins removal 2025-11-07 05:16:09 -06:00
Your Name
eeee0301c6 Preinstall analysis-icu and drop runtime plugin install 2025-11-07 05:05:49 -06:00
Your Name
ee566734a0 Bump to 1.0.12 with Java 21 and plugin automation 2025-11-07 04:53:08 -06:00
Your Name
a2f315f959 Ensure plugin installation works on Cloudron 2025-11-07 04:17:23 -06:00
5 changed files with 194 additions and 62 deletions

View File

@@ -4,7 +4,7 @@
"author": "Elastic and Cloudron Community", "author": "Elastic and Cloudron Community",
"description": "Elasticsearch is a distributed, open source search and analytics engine for all types of data. This package is designed for internal use only.", "description": "Elasticsearch is a distributed, open source search and analytics engine for all types of data. This package is designed for internal use only.",
"tagline": "Distributed search and analytics engine", "tagline": "Distributed search and analytics engine",
"version": "1.0.0", "version": "1.0.17",
"healthCheckPath": "/_cluster/health?pretty", "healthCheckPath": "/_cluster/health?pretty",
"httpPort": 9200, "httpPort": 9200,
"manifestVersion": 2, "manifestVersion": 2,
@@ -17,10 +17,6 @@
"localDir": "/data" "localDir": "/data"
} }
}, },
"accessRestriction": {
"users": true,
"defaultValue": "internal"
},
"tags": [ "tags": [
"elasticsearch", "elasticsearch",
"search", "search",

View File

@@ -16,11 +16,11 @@ RUN apt-get update && \
net-tools \ net-tools \
iputils-ping \ iputils-ping \
dnsutils \ dnsutils \
openjdk-17-jdk openjdk-21-jdk
# Set Elasticsearch version # Set Elasticsearch version
ENV ELASTIC_VERSION=9.1.5 ENV ELASTIC_VERSION=9.1.5
ENV JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ENV JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64
# Create elasticsearch user and group # Create elasticsearch user and group
RUN groupadd elasticsearch && \ RUN groupadd elasticsearch && \
@@ -37,8 +37,12 @@ RUN mkdir -p /usr/share/elasticsearch && \
RUN mkdir -p /app/data/{elasticsearch,logs,config,secrets,jdk/bin,run} && \ RUN mkdir -p /app/data/{elasticsearch,logs,config,secrets,jdk/bin,run} && \
chown -R elasticsearch:elasticsearch /app/data chown -R elasticsearch:elasticsearch /app/data
# Preinstall required plugins
RUN ES_JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 /usr/share/elasticsearch/bin/elasticsearch-plugin install --batch analysis-icu
# Copy configuration files # Copy configuration files
COPY elasticsearch.yml /app/elasticsearch.yml COPY elasticsearch.yml /app/elasticsearch.yml
COPY .env.template /app/.env.template
COPY start.sh /app/start.sh COPY start.sh /app/start.sh
COPY stop.sh /app/stop.sh COPY stop.sh /app/stop.sh

View File

@@ -32,6 +32,11 @@ After installation:
1. Check the app logs to ensure Elasticsearch has started correctly 1. Check the app logs to ensure Elasticsearch has started correctly
2. Note the generated password from the logs or from `/app/data/credentials.txt` 2. Note the generated password from the logs or from `/app/data/credentials.txt`
3. Configure your other Cloudron apps to connect to Elasticsearch using the format: `http://elastic:<password>@localhost:9200` 3. Configure your other Cloudron apps to connect to Elasticsearch using the format: `http://elastic:<password>@localhost:9200`
4. (Recommended) Verify the bundled `analysis-icu` plugin (required by Nextclouds full-text search):
```bash
curl -X GET -u elastic:<password> "localhost:9200/_nodes/plugins?pretty"
```
The output should list `analysis-icu` before you kick off `occ fulltextsearch:index`. If you need additional plugins, rebuild this package with the desired plugin baked into the image (runtime installation is blocked by Elasticsearch 9s filesystem restrictions).
## Troubleshooting ## Troubleshooting

View File

@@ -7,6 +7,7 @@ This package provides Elasticsearch for Cloudron, configured for internal use on
- Elasticsearch 9.1.5 - Elasticsearch 9.1.5
- Single-node configuration optimized for Cloudron - Single-node configuration optimized for Cloudron
- Security enabled with basic authentication - Security enabled with basic authentication
- Automatically installs the `analysis-icu` plugin (configurable)
- Internal access only by default (not publicly exposed) - Internal access only by default (not publicly exposed)
- Automatic optimization based on container resources - Automatic optimization based on container resources
@@ -69,6 +70,16 @@ curl -X PUT "http://elastic:PASSWORD@IPADDRESS:9200/nextcloud" -H 'Content-Type:
You can get the IP address from the Cloudron admin panel or by using the `cloudron status` command. The password is stored in `/app/data/credentials.txt`. You can get the IP address from the Cloudron admin panel or by using the `cloudron status` command. The password is stored in `/app/data/credentials.txt`.
### Language Analysis Plugins
Many integrations (for example, Nextcloud Full-Text Search with German documents) require the `analysis-icu` plugin so Elasticsearch understands language-specific analyzers. This package ships with `analysis-icu` preinstalled. You can verify it from the Elasticsearch web terminal:
```bash
curl -X GET -u elastic:<password> "localhost:9200/_nodes/plugins?pretty"
```
Look for `analysis-icu` in the output before running `occ fulltextsearch:index`. If you need additional plugins, rebuild this package with the desired plugin installed in the Docker image (Elasticsearch 9 no longer allows runtime plugin installation into read-only filesystems).
## Security Notes ## Security Notes
- The app is configured as internal-only by default, so it's not exposed to the public internet - The app is configured as internal-only by default, so it's not exposed to the public internet
@@ -82,10 +93,17 @@ You can get the IP address from the Cloudron admin panel or by using the `cloudr
The package automatically configures Elasticsearch based on the container's available resources: The package automatically configures Elasticsearch based on the container's available resources:
- Java heap size is set to 50% of available memory - Java heap size defaults to 50% of the app's memory allocation
- GC optimization for container environments - GC optimization for container environments
- Index settings tuned for single-node operation - Index settings tuned for single-node operation
You can override the automatic heap sizing when necessary:
- Define `ES_JAVA_HEAP` (e.g. `export ES_JAVA_HEAP=8g`) in `/app/data/.env`
- Provide a full `ES_JAVA_OPTS` string in `/app/data/.env` to take complete control
- Add explicit `-Xms`/`-Xmx` entries in `/app/data/config/jvm.options.d/heap.options`
- Set `ES_SKIP_AUTO_HEAP=1` in `/app/data/.env` to rely solely on the configuration files
## Limitations ## Limitations
- This package is for internal use only and is not exposed to the web by default - This package is for internal use only and is not exposed to the web by default

207
start.sh
View File

@@ -56,21 +56,34 @@ setup_password() {
# Set up Java environment # Set up Java environment
setup_java() { setup_java() {
if [ -L /app/data/jdk/bin/java ]; then mkdir -p /app/data/jdk/bin
if [ -x /app/data/jdk/bin/java ]; then
echo "Java already configured: $(/app/data/jdk/bin/java -version 2>&1 | head -n 1)"
return 0 return 0
fi fi
echo "Setting up Java environment..." echo "Setting up Java environment..."
# Try Java 17 first, then fall back to system Java JAVA_FOUND=0
if [ -f /usr/lib/jvm/java-17-openjdk-amd64/bin/java ]; then for candidate in /usr/lib/jvm/java-21-openjdk-amd64/bin /usr/lib/jvm/java-17-openjdk-amd64/bin; do
ln -sf /usr/lib/jvm/java-17-openjdk-amd64/bin/{java,javac,javadoc,jar} /app/data/jdk/bin/ if [ -x "$candidate/java" ]; then
else ln -sf "$candidate/java" /app/data/jdk/bin/java
JAVA_PATH=$(which java)
if [ -n "$JAVA_PATH" ]; then
ln -sf $JAVA_PATH /app/data/jdk/bin/java
for tool in javac javadoc jar; do for tool in javac javadoc jar; do
ln -sf $(which $tool 2>/dev/null) /app/data/jdk/bin/$tool 2>/dev/null || true [ -x "$candidate/$tool" ] && ln -sf "$candidate/$tool" /app/data/jdk/bin/$tool
done
JAVA_FOUND=1
break
fi
done
if [ $JAVA_FOUND -eq 0 ]; then
JAVA_PATH=$(command -v java || true)
if [ -n "$JAVA_PATH" ]; then
ln -sf "$JAVA_PATH" /app/data/jdk/bin/java
for tool in javac javadoc jar; do
TOOL_PATH=$(command -v $tool 2>/dev/null || true)
[ -n "$TOOL_PATH" ] && ln -sf "$TOOL_PATH" /app/data/jdk/bin/$tool
done done
else else
echo "ERROR: No Java found on system. Elasticsearch requires Java to run." echo "ERROR: No Java found on system. Elasticsearch requires Java to run."
@@ -78,15 +91,13 @@ setup_java() {
fi fi
fi fi
# Verify Java is available if [ ! -x /app/data/jdk/bin/java ]; then
if [ -L /app/data/jdk/bin/java ]; then
echo "Java version: $(/app/data/jdk/bin/java -version 2>&1 | head -n 1)"
else
echo "ERROR: Failed to link Java executable" echo "ERROR: Failed to link Java executable"
ls -l /app/data/jdk/bin || true
exit 1 exit 1
fi fi
# Ensure Java symlinks have correct ownership echo "Java version: $(/app/data/jdk/bin/java -version 2>&1 | head -n 1)"
chown -R elasticsearch:elasticsearch /app/data/jdk chown -R elasticsearch:elasticsearch /app/data/jdk
} }
@@ -100,24 +111,6 @@ configure_elasticsearch() {
chmod 755 $ES_PATH_CONF chmod 755 $ES_PATH_CONF
chown -R elasticsearch:elasticsearch $ES_PATH_CONF chown -R elasticsearch:elasticsearch $ES_PATH_CONF
# Add bootstrap password to keystore
echo "Setting bootstrap password..."
# Verify keystore exists before trying to add password
if [ ! -f $ES_PATH_CONF/elasticsearch.keystore ]; then
echo "ERROR: Keystore not found, cannot add bootstrap password. Creating keystore first..."
setup_keystore || {
echo "CRITICAL ERROR: Failed to create keystore, cannot proceed."
exit 1
}
fi
# Now add the bootstrap password
if ! printf "%s" "$ELASTIC_PASSWORD" | su -c "ES_PATH_CONF=$ES_PATH_CONF ES_JAVA_HOME=/app/data/jdk $ES_HOME/bin/elasticsearch-keystore add -f -x 'bootstrap.password' --stdin" elasticsearch; then
echo "ERROR: Failed to add bootstrap password to keystore."
exit 1
fi
# Copy configuration files if needed # Copy configuration files if needed
if [ ! -f $ES_PATH_CONF/elasticsearch.yml ]; then if [ ! -f $ES_PATH_CONF/elasticsearch.yml ]; then
echo "Setting up configuration files..." echo "Setting up configuration files..."
@@ -126,6 +119,14 @@ configure_elasticsearch() {
chown -R elasticsearch:elasticsearch $ES_PATH_CONF chown -R elasticsearch:elasticsearch $ES_PATH_CONF
fi fi
ensure_directory_structure
clean_legacy_settings
ensure_setting "xpack.security.http.ssl.enabled" "false"
ensure_setting "network.host" "0.0.0.0"
ensure_setting "discovery.type" "single-node"
ensure_setting "path.data" "/app/data/elasticsearch"
ensure_setting "path.logs" "/app/data/logs"
# CRITICAL FIX: Remove any index-level settings from elasticsearch.yml to prevent startup failure # CRITICAL FIX: Remove any index-level settings from elasticsearch.yml to prevent startup failure
if [ -f $ES_PATH_CONF/elasticsearch.yml ]; then if [ -f $ES_PATH_CONF/elasticsearch.yml ]; then
echo "Checking elasticsearch.yml for index-level settings..." echo "Checking elasticsearch.yml for index-level settings..."
@@ -204,13 +205,6 @@ configure_elasticsearch() {
chmod 600 $ES_PATH_CONF/{users,users_roles} chmod 600 $ES_PATH_CONF/{users,users_roles}
fi fi
# Ensure basic settings in elasticsearch.yml
for setting in "xpack.security.http.ssl.enabled: false" "network.host: 0.0.0.0" "discovery.type: single-node"; do
if ! grep -q "$setting" $ES_PATH_CONF/elasticsearch.yml; then
echo "$setting" >> $ES_PATH_CONF/elasticsearch.yml
fi
done
# Final permission check # Final permission check
echo "Final permission check on all data directories..." echo "Final permission check on all data directories..."
chown -R elasticsearch:elasticsearch /app/data chown -R elasticsearch:elasticsearch /app/data
@@ -360,20 +354,135 @@ setup_keystore() {
return 0 return 0
} }
# Ensure writable dirs exist
ensure_directory_structure() {
mkdir -p /app/data/elasticsearch
mkdir -p /app/data/elasticsearch/plugins
mkdir -p /app/data/logs
chown -R elasticsearch:elasticsearch /app/data/elasticsearch /app/data/logs
}
clean_legacy_settings() {
sed -i '/^path\.home:/d' "$ES_PATH_CONF/elasticsearch.yml"
sed -i '/^path\.data:/d' "$ES_PATH_CONF/elasticsearch.yml"
sed -i '/^path\.logs:/d' "$ES_PATH_CONF/elasticsearch.yml"
sed -i '/^path\.plugins:/d' "$ES_PATH_CONF/elasticsearch.yml"
sed -i '/^bootstrap\.password:/d' "$ES_PATH_CONF/elasticsearch.yml"
}
ensure_setting() {
local key="$1"
local value="$2"
if grep -q "^$key:" "$ES_PATH_CONF/elasticsearch.yml"; then
sed -i "s|^$key:.*|$key: $value|" "$ES_PATH_CONF/elasticsearch.yml"
else
echo "$key: $value" >> "$ES_PATH_CONF/elasticsearch.yml"
fi
}
default_gc_opts() {
echo "-XX:+UseG1GC -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30 -Xlog:gc*,gc+age=trace,safepoint:file=/app/data/logs/gc/gc.log:utctime,level,pid,tags:filecount=32,filesize=64m"
}
normalize_heap_value() {
local raw="${1:-}"
raw=$(echo "$raw" | tr '[:upper:]' '[:lower:]')
if [[ "$raw" =~ ^[0-9]+[mg]$ ]]; then
echo "$raw"
return 0
fi
if [[ "$raw" =~ ^[0-9]+$ ]]; then
echo "${raw}m"
return 0
fi
return 1
}
detect_memory_limit_bytes() {
local limit=""
if [ -r /sys/fs/cgroup/memory.max ]; then
limit=$(cat /sys/fs/cgroup/memory.max)
if [ "$limit" != "max" ]; then
echo "$limit"
return 0
fi
fi
if [ -r /sys/fs/cgroup/memory/memory.limit_in_bytes ]; then
limit=$(cat /sys/fs/cgroup/memory/memory.limit_in_bytes)
echo "$limit"
return 0
fi
if [ -n "${CLOUDRON_MEMORY_LIMIT:-}" ]; then
# CLOUDRON_MEMORY_LIMIT is in MB
echo $((CLOUDRON_MEMORY_LIMIT * 1024 * 1024))
return 0
fi
echo ""
}
has_custom_heap_file() {
local heap_file="$ES_PATH_CONF/jvm.options.d/heap.options"
if [ -f "$heap_file" ] && grep -Eq '^-Xms|^-Xmx' "$heap_file"; then
return 0
fi
return 1
}
# Configure JVM heap size # Configure JVM heap size
configure_heap() { configure_heap() {
# Calculate optimal heap size (50% of available memory) if [ "${ES_SKIP_AUTO_HEAP:-0}" = "1" ]; then
CONTAINER_MEM=$(cat /sys/fs/cgroup/memory.max 2>/dev/null || echo "4294967296") echo "Skipping automatic heap sizing because ES_SKIP_AUTO_HEAP=1"
[ "$CONTAINER_MEM" = "max" ] && CONTAINER_MEM="4294967296" # Default to 4GB if unlimited return 0
fi
HEAP_SIZE=$(expr $CONTAINER_MEM / 2097152) # Convert to MB and take 50% if [ -n "${ES_JAVA_OPTS:-}" ]; then
[ $HEAP_SIZE -gt 31744 ] && HEAP_SIZE=31744 # Max 31GB echo "Detected user-specified ES_JAVA_OPTS; not overriding heap settings."
[ $HEAP_SIZE -lt 512 ] && HEAP_SIZE=512 # Min 512MB return 0
fi
# Set JVM options if has_custom_heap_file; then
export ES_JAVA_OPTS="-Xms${HEAP_SIZE}m -Xmx${HEAP_SIZE}m -XX:+UseG1GC -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30" echo "Detected custom heap options in jvm.options.d; not overriding heap settings."
export ES_JAVA_OPTS="$ES_JAVA_OPTS -Xlog:gc*,gc+age=trace,safepoint:file=/app/data/logs/gc/gc.log:utctime,level,pid,tags:filecount=32,filesize=64m" return 0
fi
local heap_value=""
if [ -n "${ES_JAVA_HEAP:-}" ]; then
if heap_value=$(normalize_heap_value "$ES_JAVA_HEAP"); then
echo "Using heap size from ES_JAVA_HEAP (${heap_value})."
else
echo "Warning: ES_JAVA_HEAP value '$ES_JAVA_HEAP' is invalid; falling back to automatic sizing."
heap_value=""
fi
fi
if [ -z "$heap_value" ]; then
local limit_bytes
limit_bytes=$(detect_memory_limit_bytes)
if [ -z "$limit_bytes" ] || [ "$limit_bytes" = "0" ]; then
limit_bytes=$((4 * 1024 * 1024 * 1024))
fi
# Treat huge values as "unlimited" and fall back to 4GB to avoid 31GB default
if [ "$limit_bytes" -gt $((256 * 1024 * 1024 * 1024)) ]; then
limit_bytes=$((4 * 1024 * 1024 * 1024))
fi
local heap_mb=$((limit_bytes / 1024 / 1024 / 2))
[ "$heap_mb" -gt 31744 ] && heap_mb=31744
[ "$heap_mb" -lt 512 ] && heap_mb=512
heap_value="${heap_mb}m"
echo "Auto-configured heap size to ${heap_value} based on container limit."
fi
export ES_JAVA_OPTS="-Xms${heap_value} -Xmx${heap_value} $(default_gc_opts)"
export PATH=$ES_HOME/bin:$PATH export PATH=$ES_HOME/bin:$PATH
} }
@@ -385,7 +494,7 @@ start_elasticsearch() {
# Command to start Elasticsearch # Command to start Elasticsearch
ES_START_CMD="ES_PATH_CONF=$ES_PATH_CONF ES_JAVA_HOME=/app/data/jdk $ES_HOME/bin/elasticsearch" ES_START_CMD="ES_PATH_CONF=$ES_PATH_CONF ES_JAVA_HOME=/app/data/jdk $ES_HOME/bin/elasticsearch"
ES_START_CMD="$ES_START_CMD -E xpack.security.enabled=true -E bootstrap.password=$ELASTIC_PASSWORD" ES_START_CMD="$ES_START_CMD -E xpack.security.enabled=true"
# Add explicit settings for transport SSL # Add explicit settings for transport SSL
ES_START_CMD="$ES_START_CMD -E xpack.security.transport.ssl.enabled=true" ES_START_CMD="$ES_START_CMD -E xpack.security.transport.ssl.enabled=true"
ES_START_CMD="$ES_START_CMD -E xpack.security.transport.ssl.verification_mode=certificate" ES_START_CMD="$ES_START_CMD -E xpack.security.transport.ssl.verification_mode=certificate"