Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
183dcf5072 | ||
|
|
8fc7e89cdd | ||
|
|
825d522d5c | ||
|
|
82825fd4c4 | ||
|
|
eeee0301c6 | ||
|
|
ee566734a0 | ||
|
|
a2f315f959 |
@@ -4,7 +4,7 @@
|
||||
"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.",
|
||||
"tagline": "Distributed search and analytics engine",
|
||||
"version": "1.0.0",
|
||||
"version": "1.0.17",
|
||||
"healthCheckPath": "/_cluster/health?pretty",
|
||||
"httpPort": 9200,
|
||||
"manifestVersion": 2,
|
||||
@@ -17,10 +17,6 @@
|
||||
"localDir": "/data"
|
||||
}
|
||||
},
|
||||
"accessRestriction": {
|
||||
"users": true,
|
||||
"defaultValue": "internal"
|
||||
},
|
||||
"tags": [
|
||||
"elasticsearch",
|
||||
"search",
|
||||
@@ -37,4 +33,4 @@
|
||||
"description": "Elasticsearch transport port for node-to-node communication"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
10
Dockerfile
10
Dockerfile
@@ -16,11 +16,11 @@ RUN apt-get update && \
|
||||
net-tools \
|
||||
iputils-ping \
|
||||
dnsutils \
|
||||
openjdk-17-jdk
|
||||
openjdk-21-jdk
|
||||
|
||||
# Set Elasticsearch version
|
||||
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
|
||||
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} && \
|
||||
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 elasticsearch.yml /app/elasticsearch.yml
|
||||
COPY .env.template /app/.env.template
|
||||
COPY start.sh /app/start.sh
|
||||
COPY stop.sh /app/stop.sh
|
||||
|
||||
@@ -50,4 +54,4 @@ HEALTHCHECK --interval=15s --timeout=10s --start-period=120s --retries=5 \
|
||||
CMD curl -fs -u elastic:$(cat /app/data/secrets/elastic_password) http://localhost:9200/_cluster/health?pretty || exit 1
|
||||
|
||||
# Command to run
|
||||
CMD ["/app/start.sh"]
|
||||
CMD ["/app/start.sh"]
|
||||
|
||||
@@ -32,6 +32,11 @@ After installation:
|
||||
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`
|
||||
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 Nextcloud’s 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 9’s filesystem restrictions).
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
@@ -174,4 +179,4 @@ If successful, you should see a JSON response with Elasticsearch information.
|
||||
|
||||
#### Advanced Configuration
|
||||
|
||||
In some cases, you might need to modify additional Elasticsearch settings. You can do this via the elasticsearch.yml file, which is stored in `/app/data/config/elasticsearch.yml` within the Elasticsearch app container.
|
||||
In some cases, you might need to modify additional Elasticsearch settings. You can do this via the elasticsearch.yml file, which is stored in `/app/data/config/elasticsearch.yml` within the Elasticsearch app container.
|
||||
|
||||
22
README.md
22
README.md
@@ -7,6 +7,7 @@ This package provides Elasticsearch for Cloudron, configured for internal use on
|
||||
- Elasticsearch 9.1.5
|
||||
- Single-node configuration optimized for Cloudron
|
||||
- Security enabled with basic authentication
|
||||
- Automatically installs the `analysis-icu` plugin (configurable)
|
||||
- Internal access only by default (not publicly exposed)
|
||||
- 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`.
|
||||
|
||||
### 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
|
||||
|
||||
- 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:
|
||||
|
||||
- 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
|
||||
- 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
|
||||
|
||||
- This package is for internal use only and is not exposed to the web by default
|
||||
@@ -94,4 +112,4 @@ The package automatically configures Elasticsearch based on the container's avai
|
||||
|
||||
## Support
|
||||
|
||||
For support, please create an issue on the package's GitHub repository or contact the package maintainer.
|
||||
For support, please create an issue on the package's GitHub repository or contact the package maintainer.
|
||||
|
||||
209
start.sh
209
start.sh
@@ -56,21 +56,34 @@ setup_password() {
|
||||
|
||||
# Set up Java environment
|
||||
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
|
||||
fi
|
||||
|
||||
echo "Setting up Java environment..."
|
||||
|
||||
# Try Java 17 first, then fall back to system Java
|
||||
if [ -f /usr/lib/jvm/java-17-openjdk-amd64/bin/java ]; then
|
||||
ln -sf /usr/lib/jvm/java-17-openjdk-amd64/bin/{java,javac,javadoc,jar} /app/data/jdk/bin/
|
||||
else
|
||||
JAVA_PATH=$(which java)
|
||||
if [ -n "$JAVA_PATH" ]; then
|
||||
ln -sf $JAVA_PATH /app/data/jdk/bin/java
|
||||
JAVA_FOUND=0
|
||||
for candidate in /usr/lib/jvm/java-21-openjdk-amd64/bin /usr/lib/jvm/java-17-openjdk-amd64/bin; do
|
||||
if [ -x "$candidate/java" ]; then
|
||||
ln -sf "$candidate/java" /app/data/jdk/bin/java
|
||||
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
|
||||
else
|
||||
echo "ERROR: No Java found on system. Elasticsearch requires Java to run."
|
||||
@@ -78,15 +91,13 @@ setup_java() {
|
||||
fi
|
||||
fi
|
||||
|
||||
# Verify Java is available
|
||||
if [ -L /app/data/jdk/bin/java ]; then
|
||||
echo "Java version: $(/app/data/jdk/bin/java -version 2>&1 | head -n 1)"
|
||||
else
|
||||
if [ ! -x /app/data/jdk/bin/java ]; then
|
||||
echo "ERROR: Failed to link Java executable"
|
||||
ls -l /app/data/jdk/bin || true
|
||||
exit 1
|
||||
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
|
||||
}
|
||||
|
||||
@@ -100,24 +111,6 @@ configure_elasticsearch() {
|
||||
chmod 755 $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
|
||||
if [ ! -f $ES_PATH_CONF/elasticsearch.yml ]; then
|
||||
echo "Setting up configuration files..."
|
||||
@@ -126,6 +119,14 @@ configure_elasticsearch() {
|
||||
chown -R elasticsearch:elasticsearch $ES_PATH_CONF
|
||||
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
|
||||
if [ -f $ES_PATH_CONF/elasticsearch.yml ]; then
|
||||
echo "Checking elasticsearch.yml for index-level settings..."
|
||||
@@ -204,13 +205,6 @@ configure_elasticsearch() {
|
||||
chmod 600 $ES_PATH_CONF/{users,users_roles}
|
||||
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
|
||||
echo "Final permission check on all data directories..."
|
||||
chown -R elasticsearch:elasticsearch /app/data
|
||||
@@ -360,20 +354,135 @@ setup_keystore() {
|
||||
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_heap() {
|
||||
# Calculate optimal heap size (50% of available memory)
|
||||
CONTAINER_MEM=$(cat /sys/fs/cgroup/memory.max 2>/dev/null || echo "4294967296")
|
||||
[ "$CONTAINER_MEM" = "max" ] && CONTAINER_MEM="4294967296" # Default to 4GB if unlimited
|
||||
if [ "${ES_SKIP_AUTO_HEAP:-0}" = "1" ]; then
|
||||
echo "Skipping automatic heap sizing because ES_SKIP_AUTO_HEAP=1"
|
||||
return 0
|
||||
fi
|
||||
|
||||
HEAP_SIZE=$(expr $CONTAINER_MEM / 2097152) # Convert to MB and take 50%
|
||||
[ $HEAP_SIZE -gt 31744 ] && HEAP_SIZE=31744 # Max 31GB
|
||||
[ $HEAP_SIZE -lt 512 ] && HEAP_SIZE=512 # Min 512MB
|
||||
if [ -n "${ES_JAVA_OPTS:-}" ]; then
|
||||
echo "Detected user-specified ES_JAVA_OPTS; not overriding heap settings."
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Set JVM options
|
||||
export ES_JAVA_OPTS="-Xms${HEAP_SIZE}m -Xmx${HEAP_SIZE}m -XX:+UseG1GC -XX:G1ReservePercent=25 -XX:InitiatingHeapOccupancyPercent=30"
|
||||
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"
|
||||
if has_custom_heap_file; then
|
||||
echo "Detected custom heap options in jvm.options.d; not overriding heap settings."
|
||||
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
|
||||
}
|
||||
|
||||
@@ -385,7 +494,7 @@ 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_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
|
||||
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"
|
||||
@@ -464,4 +573,4 @@ configure_heap
|
||||
start_elasticsearch
|
||||
|
||||
# Keep container running
|
||||
tail -f /app/data/logs/*.log 2>/dev/null || sleep infinity
|
||||
tail -f /app/data/logs/*.log 2>/dev/null || sleep infinity
|
||||
|
||||
Reference in New Issue
Block a user