通過OpenTelemetry eBPF Instrumentation(OBI)提供的無侵入監控能力上報鏈路資料至ARMS後,ARMS即可開始監控應用,您可以查看應用拓撲、調用鏈路、異常事務、慢事務和SQL分析等一系列監控資料。本文介紹如何使用OBI為應用進行監控並上報資料。
前提條件
如果您在使用OpenTelemetry eBPF過程中有任何問題,歡迎通過DingTalk答疑群(群號:159215000379)與我們聯絡。
已準備一台滿足以下條件的 Linux 伺服器(Elastic Compute Service 或本地環境均可)。
要求
支援範圍
核心版本
Linux 5.8 及以上。RHEL 系發行版(帶有 eBPF 回移補丁)最低支援 Linux 4.18。
BTF
核心必須暴露 BTF(BPF Type Format)資訊。
CPU 架構
amd64(x86_64)、arm64(aarch64)。
許可權
Root 許可權。
說明阿里雲 Alibaba Cloud Linux 3 和 Alibaba Cloud Linux 4 預設滿足以上要求。
如果需要確認伺服器是否滿足條件,運行以下命令逐項檢查:
# 查看核心版本(需 >= 5.8,RHEL 系 >= 4.18) uname -r # 查看 CPU 架構(需為 amd64 或 arm64) uname -m # 檢查 BTF 是否可用(檔案存在即支援) ls /sys/kernel/btf/vmlinux # 檢查是否有 root 許可權 whoami
背景資訊
OBI(OpenTelemetry eBPF Instrumentation)是一款基於 eBPF 技術的 Kubernetes 可觀測性自動插樁組件。它以 DaemonSet 方式部署在叢集節點上,無需修改應用代碼或注入 Sidecar,即可自動採集應用的 Metrics 和 Traces 資料。OBI 支援 HTTP、gRPC、SQL、Redis、Kafka 等主流協議的自動探測,並針對 GenAI 情境(OpenAI、Anthropic、通義千問、MCP 、Embedding、Rerank等)提供了請求/響應體的深度提取能力。採集的遙測資料通過 OTLP 協議匯出,可無縫對接 ARMS 等可觀測性平台,協助使用者實現零侵入、低開銷的全鏈路可觀測。
應用發現匹配方式
OBI 通過 discovery.instrument 配置段發現和匹配要監控的進程,支援以下匹配方式(所有條件為 AND 邏輯,全部滿足才會匹配):
YAML 欄位 | 類型 | 說明 | 樣本 |
| 連接埠號碼/範圍 | 進程監聽的連接埠號碼,支援逗號分隔和範圍 |
|
| Glob 萬用字元 | 匹配進程可執行檔完整路徑 |
|
| Glob 萬用字元 | 匹配進程命令列參數(不含可執行檔名) |
|
| Glob 萬用字元 | 按程式設計語言自動匹配(go/java/python/ruby/nodejs/rust/dotnet/php/cpp) |
|
| PID 列表 | 直接指定進程 PID |
|
| Glob 萬用字元 | 匹配 OCI 容器名稱(適用於 Docker 部署) |
|
| 布爾值 | 僅匹配容器內進程 |
|
| 字串 | 在ARMS控制台顯示的應用程式名稱 |
|
| 字串 | 服務命名空間 |
|
| 訊號列表 | 控制匯出的訊號類型(metrics/traces/logs),空數組關閉全部 |
|
| 採樣配置 | 按比例採樣 Traces,減少資料量 |
|
常見情境配置樣本
情境1:按連接埠號碼匹配 — 最簡單,適用於任何類型的 Web 應用程式
discovery:
instrument:
- name: my-web-app
open_ports: 8080情境2:Java 應用(exe_path + cmd_args) — 通過 java 進程 + jar 包名定位
discovery:
instrument:
- name: my-java-app
exe_path: "*/java"
cmd_args: "*petclinic*"情境3:按程式設計語言自動探索 — 自動檢測指定語言的所有進程
discovery:
instrument:
- name: go-services
languages: go
- name: python-services
languages: "{python,nodejs}"情境4:按可執行檔路徑匹配 — 適用於自編譯的二進位
discovery:
instrument:
- name: my-binary
exe_path: "*/my-server"情境5:Docker 容器名匹配 — 適用於 ECS 上跑 Docker 的情境
discovery:
instrument:
- name: my-container-app
container_name: "my-container-*"情境6:多連接埠範圍 — 監控一批連接埠範圍內的服務
discovery:
instrument:
- name: microservices
open_ports: 8080-8090,9090情境7:指定 PID 臨時調試 — 直接指定進程 PID 進行監控
discovery:
instrument:
- name: debug-target
target_pids: [12345]情境8:僅容器進程 + 採樣 — 只監控容器內進程且對 Traces 採樣 50%
discovery:
instrument:
- name: container-apps
containers_only: true
open_ports: 8080
sampler:
name: traceidratio
arg: "0.5"情境9:多應用 + 排除規則 — 監控多個應用,排除特定進程
discovery:
instrument:
- name: api-gateway
open_ports: 80,443
- name: backend-java
exe_path: "*/java"
cmd_args: "*backend*"
- name: worker
exe_path: "*/python*"
cmd_args: "*celery*"
exclude_instrument:
- exe_path: "{obi,prometheus,otelcol*}"情境10:控制匯出訊號 — 只匯出 Metrics 不匯出 Traces
discovery:
instrument:
- name: high-qps-app
open_ports: 8080
exports: [metrics]操作步驟
操作流程總覽:安裝部署(擷取存取點資訊 → 下載 OBI → 編寫設定檔 → 部署服務) → 驗證監控資料
安裝部署
提供一鍵指令碼部署(推薦)和手動分步部署方式。
方式一:一鍵指令碼部署(推薦)
已將所有步驟合并為一個 obi-setup.sh 指令碼,支援互動式和非互動式兩種模式。
#!/bin/bash
# ============================================================================
# OBI (OpenTelemetry eBPF Instrumentation) 一鍵部署指令碼
# 合并了環境檢查、下載、配置產生、服務安裝的完整流程
#
# 用法:
# sudo bash obi-setup.sh install — 互動式安裝(二進位 + systemd)
# sudo bash obi-setup.sh docker — Docker 方式部署
# sudo bash obi-setup.sh check — 僅做環境檢查
# sudo bash obi-setup.sh uninstall — 卸載 OBI 服務
# sudo bash obi-setup.sh status — 查看運行狀態與日誌
#
# 非互動式部署(環境變數):
# export OTLP_ENDPOINT="http://xxx:4318"
# export OTLP_HEADERS="Authentication=xxx"
# export APP_NAME="my-app"
# export APP_MATCH_TYPE="port" # port|cmd|exe|lang|container|pid
# export APP_MATCH_VALUE="8080"
# sudo -E bash obi-setup.sh install
# ============================================================================
set -euo pipefail
# ========================= 全域變數 =========================
VERSION="0.9.5"
SERVICE_NAME="obi"
INSTALL_DIR="/opt/obi"
CONFIG_FILE="${INSTALL_DIR}/config.yaml"
OBI_BINARY="${INSTALL_DIR}/obi"
DOCKER_IMAGE="registry.cn-hangzhou.aliyuncs.com/private-mesh/obi:${VERSION}"
OSS_BASE="https://arms-apm-cn-hangzhou-pre.oss-cn-hangzhou.aliyuncs.com/obi-agent/${VERSION}"
# 顏色
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
# 應用列表(數組)
declare -a APP_ENTRIES=()
# 排除規則
EXCLUDE_PATTERN=""
# ========================= 工具函數 =========================
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
fail() { echo -e "${RED}[FAIL]${NC} $*"; }
die() { fail "$*"; exit 1; }
prompt_input() {
local var_name="$1" prompt_msg="$2" default_val="${3:-}"
local current_val="${!var_name:-}"
if [[ -n "$current_val" ]]; then
info "使用環境變數 ${var_name}=${current_val}"
return
fi
if [[ -n "$default_val" ]]; then
read -rp "$(echo -e "${BOLD}${prompt_msg} [${default_val}]: ${NC}")" input
eval "$var_name=\"${input:-$default_val}\""
else
while true; do
read -rp "$(echo -e "${BOLD}${prompt_msg}: ${NC}")" input
if [[ -n "$input" ]]; then
eval "$var_name=\"$input\""
return
fi
warn "此項為必填"
done
fi
}
prompt_yesno() {
local prompt_msg="$1" default="${2:-n}"
local yn
read -rp "$(echo -e "${BOLD}${prompt_msg} [y/N]: ${NC}")" yn
case "${yn:-$default}" in
[Yy]*) return 0 ;;
*) return 1 ;;
esac
}
# ========================= 環境檢查 =========================
check_root() {
if [[ "$EUID" -ne 0 ]]; then
die "請使用 root 許可權運行: sudo bash $0 $*"
fi
ok "Root 許可權"
}
check_env() {
echo ""
echo -e "${BOLD}========== 環境檢查 ==========${NC}"
local kernel_ver major minor
kernel_ver=$(uname -r)
major=$(echo "$kernel_ver" | cut -d. -f1)
minor=$(echo "$kernel_ver" | cut -d. -f2)
local is_rhel=false
if [[ -f /etc/redhat-release ]] || grep -qi 'rhel\|centos\|rocky\|alma\|anolis\|alinux' /etc/os-release 2>/dev/null; then
is_rhel=true
fi
if [[ "$major" -gt 5 ]] || { [[ "$major" -eq 5 ]] && [[ "$minor" -ge 8 ]]; }; then
ok "核心版本: ${kernel_ver} (>= 5.8)"
elif $is_rhel && { [[ "$major" -gt 4 ]] || { [[ "$major" -eq 4 ]] && [[ "$minor" -ge 18 ]]; }; }; then
ok "核心版本: ${kernel_ver} (RHEL 系, >= 4.18)"
else
fail "核心版本: ${kernel_ver} (最低要求 5.8, RHEL 系 4.18)"
return 1
fi
local arch
arch=$(uname -m)
case "$arch" in
x86_64|amd64) ok "CPU 架構: ${arch} (amd64)" ;;
aarch64|arm64) ok "CPU 架構: ${arch} (arm64)" ;;
*) fail "CPU 架構: ${arch} (不支援)"; return 1 ;;
esac
if [[ -f /sys/kernel/btf/vmlinux ]]; then
ok "BTF: 可用"
else
fail "BTF: /sys/kernel/btf/vmlinux 不存在"
return 1
fi
echo -e "${GREEN}========== 環境檢查通過 ==========${NC}"
echo ""
return 0
}
detect_arch() {
local arch
arch=$(uname -m)
case "$arch" in
x86_64|amd64) echo "amd64" ;;
aarch64|arm64) echo "arm64" ;;
*) die "不支援的 CPU 架構: ${arch}" ;;
esac
}
# ========================= 單個應用配置收集 =========================
collect_one_app() {
local app_name match_type
local entry=""
prompt_input app_name "應用程式名稱 (ARMS 控制台顯示名稱)" "my-app"
echo ""
echo -e " ${BOLD}選擇應用匹配方式:${NC}"
echo -e " 1) 連接埠號碼匹配 — open_ports, 如 8080 或 80,443 或 8000-8999"
echo -e " 2) 命令列匹配 — exe_path + cmd_args, 適合 java -jar 等情境"
echo -e " 3) 可執行檔路徑匹配 — exe_path, 適合自編譯二進位"
echo -e " 4) 程式設計語言自動探索 — languages, 自動檢測 go/java/python/nodejs 等"
echo -e " 5) Docker 容器名匹配 — container_name, 適合 ECS 上跑 Docker"
echo -e " 6) PID 指定 — target_pids, 臨時調試用"
read -rp "$(echo -e "${BOLD}選擇 [1-6, 預設1]: ${NC}")" match_choice
entry=" - name: ${app_name}"
case "${match_choice:-1}" in
1)
local ports
prompt_input ports "監聽連接埠號碼 (如 8080, 80,443, 8000-8999)" "8080"
entry="${entry}
open_ports: ${ports}"
;;
2)
local exe_path cmd_args
prompt_input exe_path "可執行檔路徑 Glob (如 */java)" "*/java"
prompt_input cmd_args "命令列參數 Glob (如 *petclinic*)"
entry="${entry}
exe_path: \"${exe_path}\"
cmd_args: \"${cmd_args}\""
;;
3)
local exe_path
prompt_input exe_path "可執行檔路徑 Glob (如 */my-server, */nginx)"
entry="${entry}
exe_path: \"${exe_path}\""
;;
4)
local lang
echo -e " 支援的語言: go, java, python, ruby, nodejs, rust, dotnet, php, cpp"
echo -e " 多語言用花括弧: {go,rust} 或 {python,nodejs}"
prompt_input lang "程式設計語言" "java"
entry="${entry}
languages: \"${lang}\""
;;
5)
local cname
prompt_input cname "Docker 容器名 Glob (如 my-container-*)"
entry="${entry}
container_name: \"${cname}\""
;;
6)
local pids
prompt_input pids "PID 列表, 逗號分隔 (如 12345,67890)"
local pid_array
pid_array=$(echo "$pids" | tr ',' '\n' | sed 's/^[[:space:]]*//' | awk '{printf " - %s\n", $0}')
entry="${entry}
target_pids:
${pid_array}"
;;
*)
local ports
prompt_input ports "監聽連接埠號碼" "8080"
entry="${entry}
open_ports: ${ports}"
;;
esac
# 可選:僅容器進程
if [[ "${match_choice:-1}" != "5" ]]; then
if prompt_yesno "是否僅匹配容器內進程 (containers_only)?"; then
entry="${entry}
containers_only: true"
fi
fi
# 可選:採樣
if prompt_yesno "是否配置 Traces 採樣 (減少資料量)?"; then
echo -e " 採樣器: always_on, always_off, traceidratio, parentbased_traceidratio"
local sampler_name sampler_arg
prompt_input sampler_name "採樣器名稱" "traceidratio"
prompt_input sampler_arg "採樣參數 (如 0.5 表示採樣50%)" "1.0"
entry="${entry}
sampler:
name: ${sampler_name}
arg: \"${sampler_arg}\""
fi
APP_ENTRIES+=("$entry")
ok "已添加應用: ${app_name}"
}
# ========================= 收集配置(完整) =========================
collect_config() {
echo -e "${BOLD}========== 配置資訊 ==========${NC}"
echo ""
info "請從 ARMS 接入中心擷取以下資訊:"
info " ARMS 控制台 -> 接入中心 -> OpenTelemetry 卡片 -> 點擊擷取 LicenseKey"
echo ""
prompt_input OTLP_ENDPOINT "OTEL_EXPORTER_OTLP_ENDPOINT (如 http://xxx:4318)"
prompt_input OTLP_HEADERS "OTEL_EXPORTER_OTLP_HEADERS (如 Authentication=xxx)"
echo ""
echo -e "${BOLD}========== 應用配置 ==========${NC}"
# 非互動模式:從環境變數構建單個 app
if [[ -n "${APP_MATCH_TYPE:-}" ]]; then
local entry=" - name: ${APP_NAME:-my-app}"
case "$APP_MATCH_TYPE" in
port)
entry="${entry}
open_ports: ${APP_MATCH_VALUE:-8080}" ;;
cmd)
entry="${entry}
exe_path: \"${APP_EXE_PATH:-*/java}\"
cmd_args: \"${APP_CMD_ARGS}\"" ;;
exe)
entry="${entry}
exe_path: \"${APP_MATCH_VALUE}\"" ;;
lang)
entry="${entry}
languages: \"${APP_MATCH_VALUE}\"" ;;
container)
entry="${entry}
container_name: \"${APP_MATCH_VALUE}\"" ;;
pid)
local pid_array
pid_array=$(echo "${APP_MATCH_VALUE}" | tr ',' '\n' | sed 's/^[[:space:]]*//' | awk '{printf " - %s\n", $0}')
entry="${entry}
target_pids:
${pid_array}" ;;
*)
die "未知的 APP_MATCH_TYPE: ${APP_MATCH_TYPE} (支援: port|cmd|exe|lang|container|pid)" ;;
esac
APP_ENTRIES+=("$entry")
ok "已添加應用: ${APP_NAME:-my-app} (${APP_MATCH_TYPE})"
return
fi
# 互動模式:迴圈添加應用
while true; do
echo ""
collect_one_app
echo ""
if ! prompt_yesno "是否繼續添加另一個應用?"; then
break
fi
done
# 可選:排除規則
echo ""
if prompt_yesno "是否配置排除規則 (exclude_instrument)?"; then
echo -e " 輸入要排除的可執行檔路徑 Glob"
echo -e " 樣本: {obi,prometheus,otelcol*,node_exporter}"
local exclude_val
prompt_input exclude_val "排除路徑 Glob"
EXCLUDE_PATTERN="$exclude_val"
ok "已添加排除規則: ${exclude_val}"
fi
}
# ========================= 組建組態檔案 =========================
generate_config() {
local endpoint_metrics="${OTLP_ENDPOINT}/v1/metrics"
# 拼接 instrument 段
local instrument_yaml=""
for entry in "${APP_ENTRIES[@]}"; do
instrument_yaml="${instrument_yaml}
${entry}"
done
# 拼接 exclude 段
local exclude_yaml=""
if [[ -n "$EXCLUDE_PATTERN" ]]; then
exclude_yaml="
exclude_instrument:
- exe_path: \"${EXCLUDE_PATTERN}\""
fi
cat > "${CONFIG_FILE}" <<YAML
# OBI 設定檔 - 由 obi-setup.sh 自動產生
# 產生時間: $(date '+%Y-%m-%d %H:%M:%S')
log_level: WARN
# ========== Metrics 匯出 ==========
otel_metrics_export:
endpoint: ${endpoint_metrics}
metrics:
features:
- application
- network
- stats_tcp_rtt
routes:
unmatched: heuristic
# ========== eBPF 探針配置 ==========
ebpf:
context_propagation: all
track_request_headers: true
payload_extraction:
http:
genai:
openai:
enabled: true
anthropic:
enabled: true
jsonrpc:
enabled: true
mcp:
enabled: true
qwen:
enabled: true
rerank:
enabled: true
embedding:
enabled: true
buffer_sizes:
http: 8192
# ========== Traces 匯出 ==========
otel_traces_export:
endpoint: ${OTLP_ENDPOINT}
instrumentations:
- http
- grpc
- sql
- redis
- kafka
- mqtt
- mongo
- couchbase
- memcached
- genai
javaagent:
enabled: false
# ========== 應用發現規則 ==========
discovery:
instrument:${instrument_yaml}${exclude_yaml}
YAML
ok "設定檔已產生: ${CONFIG_FILE}"
}
# ========================= 下載 OBI =========================
download_obi() {
local arch
arch=$(detect_arch)
local url="${OSS_BASE}/obi-linux-${arch}"
info "下載 OBI v${VERSION} (${arch})..."
mkdir -p "${INSTALL_DIR}"
if command -v wget &>/dev/null; then
wget -q --show-progress "${url}" -O "${OBI_BINARY}"
elif command -v curl &>/dev/null; then
curl -fL --progress-bar "${url}" -o "${OBI_BINARY}"
else
die "wget 和 curl 均不可用"
fi
chmod +x "${OBI_BINARY}"
ok "OBI 已下載至 ${OBI_BINARY}"
}
# ========================= Systemd 服務 =========================
install_systemd() {
info "建立 systemd 服務..."
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=OBI eBPF Instrumentation Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=${INSTALL_DIR}
Environment="OTEL_EBPF_CONFIG_PATH=${CONFIG_FILE}"
Environment="OTEL_EXPORTER_OTLP_HEADERS=${OTLP_HEADERS}"
ExecStart=${OBI_BINARY}
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=${SERVICE_NAME}
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "${SERVICE_NAME}" --quiet
ok "Systemd 服務已建立並設為開機自啟"
}
start_service() {
info "啟動 OBI 服務..."
systemctl start "${SERVICE_NAME}"
sleep 2
if systemctl is-active --quiet "${SERVICE_NAME}"; then
ok "OBI 服務啟動成功!"
else
fail "OBI 服務啟動失敗, 錯誤記錄檔如下:"
journalctl -u "${SERVICE_NAME}" -n 30 --no-pager
exit 1
fi
}
# ========================= Docker 部署 =========================
deploy_docker() {
if ! command -v docker &>/dev/null; then
die "Docker 未安裝"
fi
collect_config
mkdir -p "${INSTALL_DIR}"
generate_config
info "拉取 Docker 鏡像: ${DOCKER_IMAGE}"
docker pull "${DOCKER_IMAGE}"
if docker ps -a --format '{{.Names}}' | grep -q "^${SERVICE_NAME}$"; then
warn "移除已有容器..."
docker rm -f "${SERVICE_NAME}" >/dev/null 2>&1
fi
info "啟動 OBI 容器..."
docker run -d \
--name "${SERVICE_NAME}" \
--pid=host \
--privileged \
--restart=always \
-v "${CONFIG_FILE}:/config/config.yaml:ro" \
-e "OTEL_EXPORTER_OTLP_HEADERS=${OTLP_HEADERS}" \
-e "OTEL_EXPORTER_OTLP_ENDPOINT=${OTLP_ENDPOINT}" \
"${DOCKER_IMAGE}" \
--config=/config/config.yaml
sleep 2
if docker ps --format '{{.Names}}' | grep -q "^${SERVICE_NAME}$"; then
ok "OBI 容器啟動成功!"
else
fail "容器啟動失敗:"
docker logs "${SERVICE_NAME}" --tail 30
exit 1
fi
echo ""
echo -e "${BOLD}Docker 管理命令:${NC}"
echo " 查看日誌: docker logs -f ${SERVICE_NAME}"
echo " 停止容器: docker stop ${SERVICE_NAME}"
echo " 刪除容器: docker rm -f ${SERVICE_NAME}"
echo ""
}
# ========================= 卸載 =========================
uninstall() {
echo -e "${BOLD}========== 卸載 OBI ==========${NC}"
if systemctl is-active --quiet "${SERVICE_NAME}" 2>/dev/null; then
systemctl stop "${SERVICE_NAME}"
fi
if [[ -f "/etc/systemd/system/${SERVICE_NAME}.service" ]]; then
systemctl disable "${SERVICE_NAME}" --quiet 2>/dev/null || true
rm -f "/etc/systemd/system/${SERVICE_NAME}.service"
systemctl daemon-reload
ok "Systemd 服務已移除"
fi
if command -v docker &>/dev/null && docker ps -a --format '{{.Names}}' | grep -q "^${SERVICE_NAME}$"; then
docker rm -f "${SERVICE_NAME}" >/dev/null 2>&1
ok "Docker 容器已移除"
fi
if [[ -d "${INSTALL_DIR}" ]]; then
rm -rf "${INSTALL_DIR}"
ok "安裝目錄已清理"
fi
ok "OBI 已完全卸載"
}
# ========================= 狀態查看 =========================
show_status() {
echo -e "${BOLD}========== OBI 運行狀態 ==========${NC}"
if [[ -f "/etc/systemd/system/${SERVICE_NAME}.service" ]]; then
echo -e "\n${CYAN}[Systemd 服務]${NC}"
systemctl status "${SERVICE_NAME}" --no-pager 2>/dev/null || true
echo -e "\n${CYAN}[最近日誌]${NC}"
journalctl -u "${SERVICE_NAME}" -n 20 --no-pager 2>/dev/null || true
fi
if command -v docker &>/dev/null && docker ps -a --format '{{.Names}}' | grep -q "^${SERVICE_NAME}$"; then
echo -e "\n${CYAN}[Docker 容器]${NC}"
docker ps -f "name=${SERVICE_NAME}"
echo -e "\n${CYAN}[最近日誌]${NC}"
docker logs "${SERVICE_NAME}" --tail 20 2>&1 || true
fi
if [[ -f "${CONFIG_FILE}" ]]; then
echo -e "\n${CYAN}[設定檔: ${CONFIG_FILE}]${NC}"
cat "${CONFIG_FILE}"
fi
}
# ========================= 二進位安裝主流程 =========================
install_binary() {
check_env || die "環境檢查未通過"
collect_config
download_obi
generate_config
install_systemd
start_service
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} OBI 部署完成!${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
echo -e "${BOLD}安裝目錄:${NC} ${INSTALL_DIR}"
echo -e "${BOLD}設定檔:${NC} ${CONFIG_FILE}"
echo ""
echo -e "常用命令:"
echo " 查看狀態: sudo systemctl status ${SERVICE_NAME}"
echo " 查看日誌: sudo journalctl -u ${SERVICE_NAME} -f"
echo " 重啟服務: sudo systemctl restart ${SERVICE_NAME}"
echo " 卸載: sudo bash $0 uninstall"
echo ""
}
# ========================= 入口 =========================
print_usage() {
echo ""
echo -e "${BOLD}OBI 一鍵部署指令碼 v${VERSION}${NC}"
echo ""
echo "用法: sudo bash $0 <命令>"
echo ""
echo "命令:"
echo " install 二進位方式安裝 (systemd 管理)"
echo " docker Docker 方式部署"
echo " check 僅執行環境檢查"
echo " status 查看運行狀態與日誌"
echo " uninstall 卸載 OBI"
echo ""
echo "應用匹配方式 (APP_MATCH_TYPE):"
echo " port 連接埠號碼匹配 (open_ports)"
echo " cmd 命令列匹配 (exe_path + cmd_args)"
echo " exe 可執行檔路徑匹配 (exe_path)"
echo " lang 程式設計語言自動探索 (languages)"
echo " container Docker 容器名匹配 (container_name)"
echo " pid PID 指定 (target_pids)"
echo ""
}
main() {
local action="${1:-}"
case "$action" in
install) check_root "$@"; install_binary ;;
docker) check_root "$@"; check_env || die "環境檢查未通過"; deploy_docker ;;
check) check_root "$@"; check_env ;;
uninstall) check_root "$@"; uninstall ;;
status) check_root "$@"; show_status ;;
*) print_usage; exit 1 ;;
esac
}
main "$@"
1. 擷取存取點資訊
登入 ARMS 控制台,在左側導覽列單擊「接入中心」,在服務端應用地區單擊 OpenTelemetry 卡片,擷取以下兩個值:
OTEL_EXPORTER_OTLP_ENDPOINT(如http://xxx:4318\)OTEL_EXPORTER_OTLP_HEADERS(如Authentication=xxx)
2. 下載並執行指令碼
chmod +x obi-setup.sh3. 互動式安裝(二進位 + systemd)
sudo bash obi-setup.sh install指令碼會依次完成:環境檢查 → 收集 endpoint/headers → 選擇應用匹配方式(支援 6 種情境) → 支援添加多個應用 → 可選配置排除規則/採樣/匯出控制 → 自動檢測 CPU 架構並下載 OBI → 產生 config.yaml → 建立 systemd 服務並啟動。
4. Docker 方式部署
sudo bash obi-setup.sh docker5. 非互動式部署(批量情境)
通過環境變數傳參跳過互動。支援以下匹配類型:
# 按連接埠匹配
export OTLP_ENDPOINT="http://xxx:4318"
export OTLP_HEADERS="Authentication=xxx"
export APP_NAME="my-app"
export APP_MATCH_TYPE="port"
export APP_MATCH_VALUE="8080"
sudo -E bash obi-setup.sh install
# Java 應用 (exe_path + cmd_args)
export APP_MATCH_TYPE="cmd"
export APP_EXE_PATH="*/java"
export APP_CMD_ARGS="*petclinic*"
# 按程式設計語言
export APP_MATCH_TYPE="lang"
export APP_MATCH_VALUE="java"
# 按可執行檔路徑
export APP_MATCH_TYPE="exe"
export APP_MATCH_VALUE="*/my-server"
# 按 Docker 容器名
export APP_MATCH_TYPE="container"
export APP_MATCH_VALUE="my-container-*"
# 按 PID
export APP_MATCH_TYPE="pid"
export APP_MATCH_VALUE="12345,67890"
6. 其他命令
sudo bash obi-setup.sh check # 僅執行環境檢查
sudo bash obi-setup.sh status # 查看運行狀態與日誌
sudo bash obi-setup.sh uninstall # 卸載 OBI方式二:手動分步部署
如果你需要更精細地控制每一步,可以按以下步驟手動操作。
步驟一:擷取存取點資訊
同上,從 ARMS 控制台 擷取 OTEL_EXPORTER_OTLP_ENDPOINT 和 OTEL_EXPORTER_OTLP_HEADERS。
步驟二:下載 OBI
# amd64 架構
wget https://arms-apm-cn-hangzhou-pre.oss-cn-hangzhou.aliyuncs.com/obi-agent/0.9.5/obi-linux-amd64 -O obi
# arm64 架構
wget https://arms-apm-cn-hangzhou-pre.oss-cn-hangzhou.aliyuncs.com/obi-agent/0.9.5/obi-linux-arm64 -O obi
chmod +x obi步驟三:編寫設定檔
建立 config.yaml,將 <OTLP_ENDPOINT> 替換為實際值,並根據應用情況修改 discovery.instrument 部分(參見上方「應用發現匹配方式」章節選擇合適的配置):
log_level: WARN
otel_metrics_export:
endpoint: <OTLP_ENDPOINT>/v1/metrics
metrics:
features:
- application
- network
- stats_tcp_rtt
routes:
unmatched: heuristic
ebpf:
context_propagation: all
track_request_headers: true
payload_extraction:
http:
genai:
openai:
enabled: true
anthropic:
enabled: true
jsonrpc:
enabled: true
mcp:
enabled: true
qwen:
enabled: true
rerank:
enabled: true
embedding:
enabled: true
buffer_sizes:
http: 8192
otel_traces_export:
endpoint: <OTLP_ENDPOINT>
instrumentations:
- http
- grpc
- sql
- redis
- kafka
- mqtt
- mongo
- couchbase
- memcached
- genai
javaagent:
enabled: false
discovery:
instrument:
# 根據實際情境選擇匹配方式,參見上方「應用發現匹配方式」
- name: my-app
open_ports: 8080步驟四:部署服務
二進位方式(systemd 管理):
建立 systemd 服務檔案 /etc/systemd/system/obi.service:
[Unit]
Description=OBI eBPF Instrumentation Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=/path/to/obi-dir
Environment="OTEL_EBPF_CONFIG_PATH=config.yaml"
Environment="OTEL_EXPORTER_OTLP_HEADERS=<替換為實際值>"
ExecStart=/path/to/obi-dir/obi
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=obi
[Install]
WantedBy=multi-user.target啟動服務:
sudo systemctl daemon-reload
sudo systemctl enable obi
sudo systemctl start obi
sudo systemctl status obi
Docker 方式:
docker pull registry.cn-hangzhou.aliyuncs.com/private-mesh/obi:0.9.5
docker run -d \
--name obi \
--pid=host \
--privileged \
--restart=always \
-v $(pwd)/config.yaml:/config/config.yaml:ro \
-e OTEL_EXPORTER_OTLP_HEADERS="<替換為實際值>" \
-e OTEL_EXPORTER_OTLP_ENDPOINT="<替換為實際值>" \
registry.cn-hangzhou.aliyuncs.com/private-mesh/obi:0.9.5 \
--config=/config/config.yaml查看監控資料
登入 ARMS 控制台,選擇目標工作空間,在左側導覽列選擇「所有功能 → 應用監控」,在應用列表中找到你配置的應用程式名稱,點擊即可查看拓撲、鏈路、異常等監控詳情。
常用營運命令速查
操作 | systemd 方式 | Docker 方式 |
查看狀態 |
|
|
查看日誌 |
|
|
重啟 |
|
|
停止 |
|
|
卸載 |
|
|
附錄:一鍵部署指令碼(obi-setup.sh)
將以下內容儲存為 obi-setup.sh,賦予執行許可權後使用。
#!/bin/bash
# ============================================================================
# OBI (OpenTelemetry eBPF Instrumentation) 一鍵部署指令碼
# 合并了環境檢查、下載、配置產生、服務安裝的完整流程
#
# 用法:
# sudo bash obi-setup.sh install — 互動式安裝(二進位 + systemd)
# sudo bash obi-setup.sh docker — Docker 方式部署
# sudo bash obi-setup.sh check — 僅做環境檢查
# sudo bash obi-setup.sh uninstall — 卸載 OBI 服務
# sudo bash obi-setup.sh status — 查看運行狀態與日誌
#
# 非互動式部署(環境變數):
# export OTLP_ENDPOINT="http://xxx:4318"
# export OTLP_HEADERS="Authentication=xxx"
# export APP_NAME="my-app"
# export APP_MATCH_TYPE="port" # port|cmd|exe|lang|container|pid
# export APP_MATCH_VALUE="8080"
# sudo -E bash obi-setup.sh install
# ============================================================================
set -euo pipefail
# ========================= 全域變數 =========================
VERSION="0.9.5"
SERVICE_NAME="obi"
INSTALL_DIR="/opt/obi"
CONFIG_FILE="${INSTALL_DIR}/config.yaml"
OBI_BINARY="${INSTALL_DIR}/obi"
DOCKER_IMAGE="registry.cn-hangzhou.aliyuncs.com/private-mesh/obi:${VERSION}"
OSS_BASE="https://arms-apm-cn-hangzhou-pre.oss-cn-hangzhou.aliyuncs.com/obi-agent/${VERSION}"
# 顏色
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'
CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
# 應用列表(數組)
declare -a APP_ENTRIES=()
# 排除規則
EXCLUDE_PATTERN=""
# ========================= 工具函數 =========================
info() { echo -e "${CYAN}[INFO]${NC} $*"; }
ok() { echo -e "${GREEN}[OK]${NC} $*"; }
warn() { echo -e "${YELLOW}[WARN]${NC} $*"; }
fail() { echo -e "${RED}[FAIL]${NC} $*"; }
die() { fail "$*"; exit 1; }
prompt_input() {
local var_name="$1" prompt_msg="$2" default_val="${3:-}"
local current_val="${!var_name:-}"
if [[ -n "$current_val" ]]; then
info "使用環境變數 ${var_name}=${current_val}"
return
fi
if [[ -n "$default_val" ]]; then
read -rp "$(echo -e "${BOLD}${prompt_msg} [${default_val}]: ${NC}")" input
eval "$var_name=\"${input:-$default_val}\""
else
while true; do
read -rp "$(echo -e "${BOLD}${prompt_msg}: ${NC}")" input
if [[ -n "$input" ]]; then
eval "$var_name=\"$input\""
return
fi
warn "此項為必填"
done
fi
}
prompt_yesno() {
local prompt_msg="$1" default="${2:-n}"
local yn
read -rp "$(echo -e "${BOLD}${prompt_msg} [y/N]: ${NC}")" yn
case "${yn:-$default}" in
[Yy]*) return 0 ;;
*) return 1 ;;
esac
}
# ========================= 環境檢查 =========================
check_root() {
if [[ "$EUID" -ne 0 ]]; then
die "請使用 root 許可權運行: sudo bash $0 $*"
fi
ok "Root 許可權"
}
check_env() {
echo ""
echo -e "${BOLD}========== 環境檢查 ==========${NC}"
local kernel_ver major minor
kernel_ver=$(uname -r)
major=$(echo "$kernel_ver" | cut -d. -f1)
minor=$(echo "$kernel_ver" | cut -d. -f2)
local is_rhel=false
if [[ -f /etc/redhat-release ]] || grep -qi 'rhel\|centos\|rocky\|alma\|anolis\|alinux' /etc/os-release 2>/dev/null; then
is_rhel=true
fi
if [[ "$major" -gt 5 ]] || { [[ "$major" -eq 5 ]] && [[ "$minor" -ge 8 ]]; }; then
ok "核心版本: ${kernel_ver} (>= 5.8)"
elif $is_rhel && { [[ "$major" -gt 4 ]] || { [[ "$major" -eq 4 ]] && [[ "$minor" -ge 18 ]]; }; }; then
ok "核心版本: ${kernel_ver} (RHEL 系, >= 4.18)"
else
fail "核心版本: ${kernel_ver} (最低要求 5.8, RHEL 系 4.18)"
return 1
fi
local arch
arch=$(uname -m)
case "$arch" in
x86_64|amd64) ok "CPU 架構: ${arch} (amd64)" ;;
aarch64|arm64) ok "CPU 架構: ${arch} (arm64)" ;;
*) fail "CPU 架構: ${arch} (不支援)"; return 1 ;;
esac
if [[ -f /sys/kernel/btf/vmlinux ]]; then
ok "BTF: 可用"
else
fail "BTF: /sys/kernel/btf/vmlinux 不存在"
return 1
fi
echo -e "${GREEN}========== 環境檢查通過 ==========${NC}"
echo ""
return 0
}
detect_arch() {
local arch
arch=$(uname -m)
case "$arch" in
x86_64|amd64) echo "amd64" ;;
aarch64|arm64) echo "arm64" ;;
*) die "不支援的 CPU 架構: ${arch}" ;;
esac
}
# ========================= 單個應用配置收集 =========================
# 產生單個 instrument entry 的 YAML 文本(每行縮排 4 空格 + 屬性縮排 6 空格)
collect_one_app() {
local app_name match_type
local entry=""
prompt_input app_name "應用程式名稱 (ARMS 控制台顯示名稱)" "my-app"
echo ""
echo -e " ${BOLD}選擇應用匹配方式:${NC}"
echo -e " 1) 連接埠號碼匹配 — open_ports, 如 8080 或 80,443 或 8000-8999"
echo -e " 2) 命令列匹配 — exe_path + cmd_args, 適合 java -jar 等情境"
echo -e " 3) 可執行檔路徑匹配 — exe_path, 適合自編譯二進位"
echo -e " 4) 程式設計語言自動探索 — languages, 自動檢測 go/java/python/nodejs 等"
echo -e " 5) Docker 容器名匹配 — container_name, 適合 ECS 上跑 Docker"
echo -e " 6) PID 指定 — target_pids, 臨時調試用"
read -rp "$(echo -e "${BOLD}選擇 [1-6, 預設1]: ${NC}")" match_choice
entry=" - name: ${app_name}"
case "${match_choice:-1}" in
1)
local ports
prompt_input ports "監聽連接埠號碼 (如 8080, 80,443, 8000-8999)" "8080"
entry="${entry}
open_ports: ${ports}"
;;
2)
local exe_path cmd_args
prompt_input exe_path "可執行檔路徑 Glob (如 */java)" "*/java"
prompt_input cmd_args "命令列參數 Glob (如 *petclinic*)"
entry="${entry}
exe_path: \"${exe_path}\"
cmd_args: \"${cmd_args}\""
;;
3)
local exe_path
prompt_input exe_path "可執行檔路徑 Glob (如 */my-server, */nginx)"
entry="${entry}
exe_path: \"${exe_path}\""
;;
4)
local lang
echo -e " 支援的語言: go, java, python, ruby, nodejs, rust, dotnet, php, cpp"
echo -e " 多語言用花括弧: {go,rust} 或 {python,nodejs}"
prompt_input lang "程式設計語言" "java"
entry="${entry}
languages: \"${lang}\""
;;
5)
local cname
prompt_input cname "Docker 容器名 Glob (如 my-container-*)"
entry="${entry}
container_name: \"${cname}\""
;;
6)
local pids
prompt_input pids "PID 列表, 逗號分隔 (如 12345,67890)"
# 轉成 YAML 數組
local pid_array
pid_array=$(echo "$pids" | tr ',' '\n' | sed 's/^[[:space:]]*//' | awk '{printf " - %s\n", $0}')
entry="${entry}
target_pids:
${pid_array}"
;;
*)
local ports
prompt_input ports "監聽連接埠號碼" "8080"
entry="${entry}
open_ports: ${ports}"
;;
esac
# 可選:僅容器進程
if [[ "${match_choice:-1}" != "5" ]]; then
if prompt_yesno "是否僅匹配容器內進程 (containers_only)?"; then
entry="${entry}
containers_only: true"
fi
fi
# 可選:採樣
if prompt_yesno "是否配置 Traces 採樣 (減少資料量)?"; then
echo -e " 採樣器: always_on, always_off, traceidratio, parentbased_traceidratio"
local sampler_name sampler_arg
prompt_input sampler_name "採樣器名稱" "traceidratio"
prompt_input sampler_arg "採樣參數 (如 0.5 表示採樣50%)" "1.0"
entry="${entry}
sampler:
name: ${sampler_name}
arg: \"${sampler_arg}\""
fi
APP_ENTRIES+=("$entry")
ok "已添加應用: ${app_name}"
}
# ========================= 收集配置(完整) =========================
collect_config() {
echo -e "${BOLD}========== 配置資訊 ==========${NC}"
echo ""
info "請從 ARMS 接入中心擷取以下資訊:"
info " ARMS 控制台 -> 接入中心 -> OpenTelemetry 卡片 -> 點擊擷取 LicenseKey"
echo ""
prompt_input OTLP_ENDPOINT "OTEL_EXPORTER_OTLP_ENDPOINT (如 http://xxx:4318)"
prompt_input OTLP_HEADERS "OTEL_EXPORTER_OTLP_HEADERS (如 Authentication=xxx)"
echo ""
echo -e "${BOLD}========== 應用配置 ==========${NC}"
# 非互動模式:從環境變數構建單個 app
if [[ -n "${APP_MATCH_TYPE:-}" ]]; then
local entry=" - name: ${APP_NAME:-my-app}"
case "$APP_MATCH_TYPE" in
port)
entry="${entry}
open_ports: ${APP_MATCH_VALUE:-8080}" ;;
cmd)
entry="${entry}
exe_path: \"${APP_EXE_PATH:-*/java}\"
cmd_args: \"${APP_CMD_ARGS}\"" ;;
exe)
entry="${entry}
exe_path: \"${APP_MATCH_VALUE}\"" ;;
lang)
entry="${entry}
languages: \"${APP_MATCH_VALUE}\"" ;;
container)
entry="${entry}
container_name: \"${APP_MATCH_VALUE}\"" ;;
pid)
local pid_array
pid_array=$(echo "${APP_MATCH_VALUE}" | tr ',' '\n' | sed 's/^[[:space:]]*//' | awk '{printf " - %s\n", $0}')
entry="${entry}
target_pids:
${pid_array}" ;;
*)
die "未知的 APP_MATCH_TYPE: ${APP_MATCH_TYPE} (支援: port|cmd|exe|lang|container|pid)" ;;
esac
APP_ENTRIES+=("$entry")
ok "已添加應用: ${APP_NAME:-my-app} (${APP_MATCH_TYPE})"
return
fi
# 互動模式:迴圈添加應用
while true; do
echo ""
collect_one_app
echo ""
if ! prompt_yesno "是否繼續添加另一個應用?"; then
break
fi
done
# 可選:排除規則
echo ""
if prompt_yesno "是否配置排除規則 (exclude_instrument)?"; then
echo -e " 輸入要排除的可執行檔路徑 Glob"
echo -e " 樣本: {obi,prometheus,otelcol*,node_exporter}"
local exclude_val
prompt_input exclude_val "排除路徑 Glob"
EXCLUDE_PATTERN="$exclude_val"
ok "已添加排除規則: ${exclude_val}"
fi
}
# ========================= 組建組態檔案 =========================
generate_config() {
local endpoint_metrics="${OTLP_ENDPOINT}/v1/metrics"
# 拼接 instrument 段
local instrument_yaml=""
for entry in "${APP_ENTRIES[@]}"; do
instrument_yaml="${instrument_yaml}
${entry}"
done
# 拼接 exclude 段
local exclude_yaml=""
if [[ -n "$EXCLUDE_PATTERN" ]]; then
exclude_yaml="
exclude_instrument:
- exe_path: \"${EXCLUDE_PATTERN}\""
fi
cat > "${CONFIG_FILE}" <<YAML
# OBI 設定檔 - 由 obi-setup.sh 自動產生
# 產生時間: $(date '+%Y-%m-%d %H:%M:%S')
log_level: WARN
# ========== Metrics 匯出 ==========
otel_metrics_export:
endpoint: ${endpoint_metrics}
metrics:
features:
- application
- network
- stats_tcp_rtt
routes:
unmatched: heuristic
# ========== eBPF 探針配置 ==========
ebpf:
context_propagation: all
track_request_headers: true
payload_extraction:
http:
genai:
openai:
enabled: true
anthropic:
enabled: true
jsonrpc:
enabled: true
mcp:
enabled: true
qwen:
enabled: true
rerank:
enabled: true
embedding:
enabled: true
buffer_sizes:
http: 8192
# ========== Traces 匯出 ==========
otel_traces_export:
endpoint: ${OTLP_ENDPOINT}
instrumentations:
- http
- grpc
- sql
- redis
- kafka
- mqtt
- mongo
- couchbase
- memcached
- genai
javaagent:
enabled: false
# ========== 應用發現規則 ==========
discovery:
instrument:${instrument_yaml}${exclude_yaml}
YAML
ok "設定檔已產生: ${CONFIG_FILE}"
}
# ========================= 下載 OBI =========================
download_obi() {
local arch
arch=$(detect_arch)
local url="${OSS_BASE}/obi-linux-${arch}"
info "下載 OBI v${VERSION} (${arch})..."
mkdir -p "${INSTALL_DIR}"
if command -v wget &>/dev/null; then
wget -q --show-progress "${url}" -O "${OBI_BINARY}"
elif command -v curl &>/dev/null; then
curl -fL --progress-bar "${url}" -o "${OBI_BINARY}"
else
die "wget 和 curl 均不可用"
fi
chmod +x "${OBI_BINARY}"
ok "OBI 已下載至 ${OBI_BINARY}"
}
# ========================= Systemd 服務 =========================
install_systemd() {
info "建立 systemd 服務..."
cat > "/etc/systemd/system/${SERVICE_NAME}.service" <<EOF
[Unit]
Description=OBI eBPF Instrumentation Service
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=root
WorkingDirectory=${INSTALL_DIR}
Environment="OTEL_EBPF_CONFIG_PATH=${CONFIG_FILE}"
Environment="OTEL_EXPORTER_OTLP_HEADERS=${OTLP_HEADERS}"
ExecStart=${OBI_BINARY}
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
SyslogIdentifier=${SERVICE_NAME}
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable "${SERVICE_NAME}" --quiet
ok "Systemd 服務已建立並設為開機自啟"
}
start_service() {
info "啟動 OBI 服務..."
systemctl start "${SERVICE_NAME}"
sleep 2
if systemctl is-active --quiet "${SERVICE_NAME}"; then
ok "OBI 服務啟動成功!"
else
fail "OBI 服務啟動失敗, 錯誤記錄檔如下:"
journalctl -u "${SERVICE_NAME}" -n 30 --no-pager
exit 1
fi
}
# ========================= Docker 部署 =========================
deploy_docker() {
if ! command -v docker &>/dev/null; then
die "Docker 未安裝"
fi
collect_config
mkdir -p "${INSTALL_DIR}"
generate_config
info "拉取 Docker 鏡像: ${DOCKER_IMAGE}"
docker pull "${DOCKER_IMAGE}"
if docker ps -a --format '{{.Names}}' | grep -q "^${SERVICE_NAME}$"; then
warn "移除已有容器..."
docker rm -f "${SERVICE_NAME}" >/dev/null 2>&1
fi
info "啟動 OBI 容器..."
docker run -d \
--name "${SERVICE_NAME}" \
--pid=host \
--privileged \
--restart=always \
-v "${CONFIG_FILE}:/config/config.yaml:ro" \
-e "OTEL_EXPORTER_OTLP_HEADERS=${OTLP_HEADERS}" \
-e "OTEL_EXPORTER_OTLP_ENDPOINT=${OTLP_ENDPOINT}" \
"${DOCKER_IMAGE}" \
--config=/config/config.yaml
sleep 2
if docker ps --format '{{.Names}}' | grep -q "^${SERVICE_NAME}$"; then
ok "OBI 容器啟動成功!"
else
fail "容器啟動失敗:"
docker logs "${SERVICE_NAME}" --tail 30
exit 1
fi
echo ""
echo -e "${BOLD}Docker 管理命令:${NC}"
echo " 查看日誌: docker logs -f ${SERVICE_NAME}"
echo " 停止容器: docker stop ${SERVICE_NAME}"
echo " 刪除容器: docker rm -f ${SERVICE_NAME}"
echo ""
}
# ========================= 卸載 =========================
uninstall() {
echo -e "${BOLD}========== 卸載 OBI ==========${NC}"
if systemctl is-active --quiet "${SERVICE_NAME}" 2>/dev/null; then
systemctl stop "${SERVICE_NAME}"
fi
if [[ -f "/etc/systemd/system/${SERVICE_NAME}.service" ]]; then
systemctl disable "${SERVICE_NAME}" --quiet 2>/dev/null || true
rm -f "/etc/systemd/system/${SERVICE_NAME}.service"
systemctl daemon-reload
ok "Systemd 服務已移除"
fi
if command -v docker &>/dev/null && docker ps -a --format '{{.Names}}' | grep -q "^${SERVICE_NAME}$"; then
docker rm -f "${SERVICE_NAME}" >/dev/null 2>&1
ok "Docker 容器已移除"
fi
if [[ -d "${INSTALL_DIR}" ]]; then
rm -rf "${INSTALL_DIR}"
ok "安裝目錄已清理"
fi
ok "OBI 已完全卸載"
}
# ========================= 狀態查看 =========================
show_status() {
echo -e "${BOLD}========== OBI 運行狀態 ==========${NC}"
if [[ -f "/etc/systemd/system/${SERVICE_NAME}.service" ]]; then
echo -e "\n${CYAN}[Systemd 服務]${NC}"
systemctl status "${SERVICE_NAME}" --no-pager 2>/dev/null || true
echo -e "\n${CYAN}[最近日誌]${NC}"
journalctl -u "${SERVICE_NAME}" -n 20 --no-pager 2>/dev/null || true
fi
if command -v docker &>/dev/null && docker ps -a --format '{{.Names}}' | grep -q "^${SERVICE_NAME}$"; then
echo -e "\n${CYAN}[Docker 容器]${NC}"
docker ps -f "name=${SERVICE_NAME}"
echo -e "\n${CYAN}[最近日誌]${NC}"
docker logs "${SERVICE_NAME}" --tail 20 2>&1 || true
fi
if [[ -f "${CONFIG_FILE}" ]]; then
echo -e "\n${CYAN}[設定檔: ${CONFIG_FILE}]${NC}"
cat "${CONFIG_FILE}"
fi
}
# ========================= 二進位安裝主流程 =========================
install_binary() {
check_env || die "環境檢查未通過"
collect_config
download_obi
generate_config
install_systemd
start_service
echo ""
echo -e "${GREEN}============================================${NC}"
echo -e "${GREEN} OBI 部署完成!${NC}"
echo -e "${GREEN}============================================${NC}"
echo ""
echo -e "${BOLD}安裝目錄:${NC} ${INSTALL_DIR}"
echo -e "${BOLD}設定檔:${NC} ${CONFIG_FILE}"
echo ""
echo -e "常用命令:"
echo " 查看狀態: sudo systemctl status ${SERVICE_NAME}"
echo " 查看日誌: sudo journalctl -u ${SERVICE_NAME} -f"
echo " 重啟服務: sudo systemctl restart ${SERVICE_NAME}"
echo " 卸載: sudo bash $0 uninstall"
echo ""
}
# ========================= 入口 =========================
print_usage() {
echo ""
echo -e "${BOLD}OBI 一鍵部署指令碼 v${VERSION}${NC}"
echo ""
echo "用法: sudo bash $0 <命令>"
echo ""
echo "命令:"
echo " install 二進位方式安裝 (systemd 管理)"
echo " docker Docker 方式部署"
echo " check 僅執行環境檢查"
echo " status 查看運行狀態與日誌"
echo " uninstall 卸載 OBI"
echo ""
echo "應用匹配方式 (APP_MATCH_TYPE):"
echo " port 連接埠號碼匹配 (open_ports)"
echo " cmd 命令列匹配 (exe_path + cmd_args)"
echo " exe 可執行檔路徑匹配 (exe_path)"
echo " lang 程式設計語言自動探索 (languages)"
echo " container Docker 容器名匹配 (container_name)"
echo " pid PID 指定 (target_pids)"
echo ""
}
main() {
local action="${1:-}"
case "$action" in
install) check_root "$@"; install_binary ;;
docker) check_root "$@"; check_env || die "環境檢查未通過"; deploy_docker ;;
check) check_root "$@"; check_env ;;
uninstall) check_root "$@"; uninstall ;;
status) check_root "$@"; show_status ;;
*) print_usage; exit 1 ;;
esac
}
main "$@"