From fedc8bfe26410cf68ae1fe92ac63b361a248e001 Mon Sep 17 00:00:00 2001 From: okxlin <61420215+okxlin@users.noreply.github.com> Date: Sun, 19 Jul 2026 17:17:30 +0800 Subject: [PATCH] feat(opensearch): add official single-node package Package OpenSearch 3.7.0 and latest with the official image, authenticated HTTPS API, persistent named volume, and non-destructive uninstall semantics. Validated both versions through strict v2 checks and isolated moelin/1panel:v2 install, restart, API persistence, uninstall, and cleanup runs. --- apps/opensearch/3.7.0/.env.sample | 4 ++ apps/opensearch/3.7.0/data.yml | 52 ++++++++++++++++ apps/opensearch/3.7.0/docker-compose.yml | 35 +++++++++++ apps/opensearch/3.7.0/scripts/init.sh | 2 + apps/opensearch/3.7.0/scripts/uninstall.sh | 8 +++ apps/opensearch/3.7.0/scripts/upgrade.sh | 2 + apps/opensearch/README.md | 62 ++++++++++++++++++++ apps/opensearch/data.yml | 32 ++++++++++ apps/opensearch/latest/.env.sample | 4 ++ apps/opensearch/latest/data.yml | 52 ++++++++++++++++ apps/opensearch/latest/docker-compose.yml | 35 +++++++++++ apps/opensearch/latest/scripts/init.sh | 2 + apps/opensearch/latest/scripts/uninstall.sh | 8 +++ apps/opensearch/latest/scripts/upgrade.sh | 2 + apps/opensearch/logo.png | Bin 0 -> 4674 bytes 15 files changed, 300 insertions(+) create mode 100644 apps/opensearch/3.7.0/.env.sample create mode 100644 apps/opensearch/3.7.0/data.yml create mode 100644 apps/opensearch/3.7.0/docker-compose.yml create mode 100755 apps/opensearch/3.7.0/scripts/init.sh create mode 100755 apps/opensearch/3.7.0/scripts/uninstall.sh create mode 100755 apps/opensearch/3.7.0/scripts/upgrade.sh create mode 100644 apps/opensearch/README.md create mode 100644 apps/opensearch/data.yml create mode 100644 apps/opensearch/latest/.env.sample create mode 100644 apps/opensearch/latest/data.yml create mode 100644 apps/opensearch/latest/docker-compose.yml create mode 100755 apps/opensearch/latest/scripts/init.sh create mode 100755 apps/opensearch/latest/scripts/uninstall.sh create mode 100755 apps/opensearch/latest/scripts/upgrade.sh create mode 100644 apps/opensearch/logo.png diff --git a/apps/opensearch/3.7.0/.env.sample b/apps/opensearch/3.7.0/.env.sample new file mode 100644 index 000000000..60c18536b --- /dev/null +++ b/apps/opensearch/3.7.0/.env.sample @@ -0,0 +1,4 @@ +CONTAINER_NAME=opensearch +PANEL_APP_PORT_HTTPS=9200 +OPENSEARCH_INITIAL_ADMIN_PASSWORD=CHANGE_ME_TO_A_STRONG_PASSWORD +OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m diff --git a/apps/opensearch/3.7.0/data.yml b/apps/opensearch/3.7.0/data.yml new file mode 100644 index 000000000..0cc29564e --- /dev/null +++ b/apps/opensearch/3.7.0/data.yml @@ -0,0 +1,52 @@ +additionalProperties: + formFields: + - default: 9200 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS API Port + labelZh: HTTPS API 端口 + label: + en: HTTPS API Port + zh: HTTPS API 端口 + zh-Hant: HTTPS API 連接埠 + ja: HTTPS API ポート + ko: HTTPS API 포트 + ru: HTTPS API-порт + ms: Port API HTTPS + pt-br: Porta da API HTTPS + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: OPENSEARCH_INITIAL_ADMIN_PASSWORD + labelEn: Initial Admin Password + labelZh: 初始管理员密码 + label: + en: Initial Admin Password + zh: 初始管理员密码 + zh-Hant: 初始管理員密碼 + ja: 初期管理者パスワード + ko: 초기 관리자 비밀번호 + ru: Начальный пароль администратора + ms: Kata Laluan Pentadbir Awal + pt-br: Senha Inicial do Administrador + required: true + rule: paramComplexity + type: password + - default: "-Xms512m -Xmx512m" + edit: true + envKey: OPENSEARCH_JAVA_OPTS + labelEn: JVM Options + labelZh: JVM 参数 + label: + en: JVM Options + zh: JVM 参数 + zh-Hant: JVM 參數 + ja: JVM オプション + ko: JVM 옵션 + ru: Параметры JVM + ms: Pilihan JVM + pt-br: Opcoes da JVM + required: true + type: text diff --git a/apps/opensearch/3.7.0/docker-compose.yml b/apps/opensearch/3.7.0/docker-compose.yml new file mode 100644 index 000000000..d9fbf45c2 --- /dev/null +++ b/apps/opensearch/3.7.0/docker-compose.yml @@ -0,0 +1,35 @@ +services: + opensearch: + image: "opensearchproject/opensearch:3.7.0" + container_name: ${CONTAINER_NAME} + ports: + - "${PANEL_APP_PORT_HTTPS}:9200" + environment: + - discovery.type=single-node + - bootstrap.memory_lock=true + - cluster.name=opensearch-cluster + - node.name=opensearch-node + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} + - OPENSEARCH_JAVA_OPTS=${OPENSEARCH_JAVA_OPTS} + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + volumes: + - opensearch-data:/usr/share/opensearch/data + labels: + createdBy: "Apps" + restart: unless-stopped + networks: + - 1panel-network + +volumes: + opensearch-data: + name: ${CONTAINER_NAME}-data + +networks: + 1panel-network: + external: true diff --git a/apps/opensearch/3.7.0/scripts/init.sh b/apps/opensearch/3.7.0/scripts/init.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/opensearch/3.7.0/scripts/init.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/opensearch/3.7.0/scripts/uninstall.sh b/apps/opensearch/3.7.0/scripts/uninstall.sh new file mode 100755 index 000000000..39108443d --- /dev/null +++ b/apps/opensearch/3.7.0/scripts/uninstall.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +if docker compose version >/dev/null 2>&1; then + docker compose down +else + docker-compose down +fi diff --git a/apps/opensearch/3.7.0/scripts/upgrade.sh b/apps/opensearch/3.7.0/scripts/upgrade.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/opensearch/3.7.0/scripts/upgrade.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/opensearch/README.md b/apps/opensearch/README.md new file mode 100644 index 000000000..c6b7a3e51 --- /dev/null +++ b/apps/opensearch/README.md @@ -0,0 +1,62 @@ +# OpenSearch + +## 产品介绍 + +OpenSearch 是开源的分布式搜索与分析引擎。本应用使用官方镜像部署单节点 +OpenSearch,并保留官方 Demo Security 的认证与自签名 TLS 配置。 + +## 主要功能 + +- 为全文检索、日志分析和数据探索提供 REST API。 +- 使用单节点发现模式,适合独立部署和开发验证。 +- 将索引数据保存到独立的 Docker 命名卷中。 + +## 访问说明 + +- 安装后通过 `https://<服务器 IP>:` 访问 API。 +- 默认管理员用户名为 `admin`,密码使用安装表单提供的初始管理员密码。 +- Demo Security 使用自签名证书,浏览器或 API 客户端需要显式信任证书;这不等同于生产 TLS 配置。 +- API 端口可读写索引数据。不要直接暴露到不受信任的公网;请使用防火墙、受限网络或带有自有证书的反向代理。 +- 本包不包含 OpenSearch Dashboards。需要可视化界面时,请单独部署 Dashboards,并为其配置本实例的 HTTPS 端点与证书信任。 + +## 部署前提 + +- 宿主机必须满足 `vm.max_map_count=262144`。可先执行 + `sudo sysctl -w vm.max_map_count=262144`,并按操作系统方式持久化该设置。 +- 建议为该实例至少预留 4 GiB 内存。安装表单默认 JVM 堆为 512 MiB,可按索引规模调整。 + +## 数据与升级 + +- 索引数据存储在与安装名关联的 Docker 命名卷中。卸载应用应只移除应用容器,不应将该数据卷当作临时数据删除;确认不再需要数据后再由管理员显式删除该卷。 +- 升级前请使用 OpenSearch 快照功能并备份数据卷。生产环境优先选择固定版本,并遵循上游的版本兼容性和滚动升级文档;不要在没有恢复计划的情况下跨大版本升级或降级。 + +## Introduction + +OpenSearch is an open-source distributed search and analytics engine. This +package deploys the official image as a single node and retains the official +Demo Security authentication and self-signed TLS configuration. + +## Features + +- Provide a REST API for full-text search, log analysis, and data exploration. +- Run with single-node discovery for independent deployments and development validation. +- Persist index data in a dedicated Docker named volume. + +## Deployment Notes + +- Access the API at `https://:` with the `admin` + user and the initial administrator password supplied during installation. +- The demo certificate is self-signed. Use a trusted certificate and restricted + network access before exposing the API to users or the public Internet. +- The host must set `vm.max_map_count=262144`, and the instance should have at + least 4 GiB of available memory. The default JVM heap is 512 MiB and can be + adjusted for the expected index workload. +- Dashboards is intentionally not bundled. Deploy it separately and configure + its HTTPS connection and certificate trust for this OpenSearch instance. +- Back up OpenSearch snapshots and the named data volume before upgrades. Do + not delete the volume merely because the application is uninstalled. + +## Sources + +- [OpenSearch Docker documentation](https://docs.opensearch.org/3.7/install-and-configure/install-opensearch/docker/) +- [OpenSearch source repository](https://github.com/opensearch-project/OpenSearch) diff --git a/apps/opensearch/data.yml b/apps/opensearch/data.yml new file mode 100644 index 000000000..7834a8f5a --- /dev/null +++ b/apps/opensearch/data.yml @@ -0,0 +1,32 @@ +name: OpenSearch +tags: +- Database +title: 开源分布式搜索与分析引擎 +description: 开源分布式搜索与分析引擎 +additionalProperties: + key: opensearch + name: OpenSearch + tags: + - Database + shortDescZh: 开源分布式搜索与分析引擎 + shortDescEn: Open-source distributed search and analytics engine + description: + en: Open-source distributed search and analytics engine. + zh: 开源分布式搜索与分析引擎。 + zh-Hant: 開源分散式搜尋與分析引擎。 + ja: オープンソースの分散検索および分析エンジン。 + ko: 오픈 소스 분산 검색 및 분석 엔진입니다. + ru: Открытый распределенный движок поиска и аналитики. + ms: Enjin carian dan analitik teragih sumber terbuka. + pt-br: Mecanismo de busca e analise distribuida de codigo aberto. + type: middleware + crossVersionUpdate: true + limit: 1 + recommend: 0 + memoryRequired: 4096 + website: https://opensearch.org/ + github: https://github.com/opensearch-project/OpenSearch + document: https://docs.opensearch.org/3.7/install-and-configure/install-opensearch/docker/ + architectures: + - amd64 + - arm64 diff --git a/apps/opensearch/latest/.env.sample b/apps/opensearch/latest/.env.sample new file mode 100644 index 000000000..60c18536b --- /dev/null +++ b/apps/opensearch/latest/.env.sample @@ -0,0 +1,4 @@ +CONTAINER_NAME=opensearch +PANEL_APP_PORT_HTTPS=9200 +OPENSEARCH_INITIAL_ADMIN_PASSWORD=CHANGE_ME_TO_A_STRONG_PASSWORD +OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m diff --git a/apps/opensearch/latest/data.yml b/apps/opensearch/latest/data.yml new file mode 100644 index 000000000..0cc29564e --- /dev/null +++ b/apps/opensearch/latest/data.yml @@ -0,0 +1,52 @@ +additionalProperties: + formFields: + - default: 9200 + edit: true + envKey: PANEL_APP_PORT_HTTPS + labelEn: HTTPS API Port + labelZh: HTTPS API 端口 + label: + en: HTTPS API Port + zh: HTTPS API 端口 + zh-Hant: HTTPS API 連接埠 + ja: HTTPS API ポート + ko: HTTPS API 포트 + ru: HTTPS API-порт + ms: Port API HTTPS + pt-br: Porta da API HTTPS + required: true + rule: paramPort + type: number + - default: "" + edit: true + envKey: OPENSEARCH_INITIAL_ADMIN_PASSWORD + labelEn: Initial Admin Password + labelZh: 初始管理员密码 + label: + en: Initial Admin Password + zh: 初始管理员密码 + zh-Hant: 初始管理員密碼 + ja: 初期管理者パスワード + ko: 초기 관리자 비밀번호 + ru: Начальный пароль администратора + ms: Kata Laluan Pentadbir Awal + pt-br: Senha Inicial do Administrador + required: true + rule: paramComplexity + type: password + - default: "-Xms512m -Xmx512m" + edit: true + envKey: OPENSEARCH_JAVA_OPTS + labelEn: JVM Options + labelZh: JVM 参数 + label: + en: JVM Options + zh: JVM 参数 + zh-Hant: JVM 參數 + ja: JVM オプション + ko: JVM 옵션 + ru: Параметры JVM + ms: Pilihan JVM + pt-br: Opcoes da JVM + required: true + type: text diff --git a/apps/opensearch/latest/docker-compose.yml b/apps/opensearch/latest/docker-compose.yml new file mode 100644 index 000000000..69a9e6b24 --- /dev/null +++ b/apps/opensearch/latest/docker-compose.yml @@ -0,0 +1,35 @@ +services: + opensearch: + image: "opensearchproject/opensearch:latest" + container_name: ${CONTAINER_NAME} + ports: + - "${PANEL_APP_PORT_HTTPS}:9200" + environment: + - discovery.type=single-node + - bootstrap.memory_lock=true + - cluster.name=opensearch-cluster + - node.name=opensearch-node + - OPENSEARCH_INITIAL_ADMIN_PASSWORD=${OPENSEARCH_INITIAL_ADMIN_PASSWORD} + - OPENSEARCH_JAVA_OPTS=${OPENSEARCH_JAVA_OPTS} + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 + hard: 65536 + volumes: + - opensearch-data:/usr/share/opensearch/data + labels: + createdBy: "Apps" + restart: unless-stopped + networks: + - 1panel-network + +volumes: + opensearch-data: + name: ${CONTAINER_NAME}-data + +networks: + 1panel-network: + external: true diff --git a/apps/opensearch/latest/scripts/init.sh b/apps/opensearch/latest/scripts/init.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/opensearch/latest/scripts/init.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/opensearch/latest/scripts/uninstall.sh b/apps/opensearch/latest/scripts/uninstall.sh new file mode 100755 index 000000000..39108443d --- /dev/null +++ b/apps/opensearch/latest/scripts/uninstall.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash +set -euo pipefail + +if docker compose version >/dev/null 2>&1; then + docker compose down +else + docker-compose down +fi diff --git a/apps/opensearch/latest/scripts/upgrade.sh b/apps/opensearch/latest/scripts/upgrade.sh new file mode 100755 index 000000000..6e877466e --- /dev/null +++ b/apps/opensearch/latest/scripts/upgrade.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +set -euo pipefail diff --git a/apps/opensearch/logo.png b/apps/opensearch/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..45e9c3340a20c58a72661057830cc7d65c9da2e9 GIT binary patch literal 4674 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 Ef?UEPv;Y7A literal 0 HcmV?d00001