diff --git a/src/agent/README.md b/src/agent/README.md index de47d0e..fa43f38 100644 --- a/src/agent/README.md +++ b/src/agent/README.md @@ -9,7 +9,21 @@ cd src/agent ./scripts/build_binary.sh # 生成 dist/argus-agent ``` -脚本会在本地创建虚拟环境、通过 `pyproject.toml` 安装依赖,并调用 PyInstaller 产出单文件可执行程序。构建过程中生成的 `build/`、`dist/` 会被 `.gitignore` 忽略。 +脚本默认会在 Docker 容器 (`python:3.11-slim-bullseye`) 内执行 PyInstaller,确保产物运行时兼容 glibc 2.31+(覆盖 2.35 环境)。构建流程注意事项: + +- 每次构建前会清理 `build/`、`dist/` 并在容器内重新创建虚拟环境。 +- 需要使用内网 Python 镜像时,可通过 `PIP_INDEX_URL`、`PIP_EXTRA_INDEX_URL`、`PIP_TRUSTED_HOST` 等环境变量传入,脚本会自动透传给容器。 +- 如果宿主机无法运行 Docker,可设置 `AGENT_BUILD_USE_DOCKER=0` 回退到本地构建;此时代码必须在 glibc ≤ 2.35 的机器上执行。 + +构建结束后脚本会在 `build/compat_check/` 下解包关键动态库并输出最高 `GLIBC_x.y` 版本,便于快速核对兼容性。如果结果中缺少 `libssl.so.3` / `libcrypto.so.3`,表示系统会在目标宿主机上使用本地 OpenSSL 库,无需额外处理。 + +例如: + +```bash +strings build/compat_check/libpython*.so.1.0 | grep -Eo 'GLIBC_[0-9]+\.[0-9]+' | sort -Vu | tail -n1 +``` + +如遇构建失败,常见原因是 Docker 不可用(请改用 `AGENT_BUILD_USE_DOCKER=0`)或无法访问 Python 包镜像(先设置上述镜像环境变量后重试)。 ## 运行时配置 diff --git a/src/agent/dist/argus-agent b/src/agent/dist/argus-agent index c560696..9c942eb 100755 Binary files a/src/agent/dist/argus-agent and b/src/agent/dist/argus-agent differ diff --git a/src/agent/scripts/build_binary.sh b/src/agent/scripts/build_binary.sh index 50671d4..7dce519 100755 --- a/src/agent/scripts/build_binary.sh +++ b/src/agent/scripts/build_binary.sh @@ -6,37 +6,257 @@ MODULE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" BUILD_ROOT="$MODULE_ROOT/build" DIST_DIR="$MODULE_ROOT/dist" PYINSTALLER_BUILD="$BUILD_ROOT/pyinstaller" +PYINSTALLER_SPEC="$PYINSTALLER_BUILD/spec" +PYINSTALLER_WORK="$PYINSTALLER_BUILD/work" VENV_DIR="$BUILD_ROOT/venv" -mkdir -p "$PYINSTALLER_BUILD" -mkdir -p "$DIST_DIR" +AGENT_BUILD_IMAGE="${AGENT_BUILD_IMAGE:-python:3.11-slim-bullseye}" +AGENT_BUILD_USE_DOCKER="${AGENT_BUILD_USE_DOCKER:-1}" +USED_DOCKER=0 -if [[ ! -d "$VENV_DIR" ]]; then - python3 -m venv "$VENV_DIR" -fi +run_host_build() { + echo "[INFO] Using host Python environment for build" >&2 + rm -rf "$BUILD_ROOT" "$DIST_DIR" + mkdir -p "$PYINSTALLER_BUILD" "$DIST_DIR" + python3 -m venv --copies "$VENV_DIR" + # shellcheck disable=SC1091 + source "$VENV_DIR/bin/activate" -# shellcheck disable=SC1091 -source "$VENV_DIR/bin/activate" + pip install --upgrade pip + pip install . + pip install "pyinstaller==6.6.0" + pyinstaller \ + --clean \ + --onefile \ + --name argus-agent \ + --distpath "$DIST_DIR" \ + --workpath "$PYINSTALLER_WORK" \ + --specpath "$PYINSTALLER_SPEC" \ + --add-data "$MODULE_ROOT/pyproject.toml:." \ + "$MODULE_ROOT/entry.py" + + chmod +x "$DIST_DIR/argus-agent" + deactivate +} + +run_docker_build() { + if ! command -v docker >/dev/null 2>&1; then + echo "[ERROR] docker 命令不存在,无法在容器内构建。请安装 Docker 或设置 AGENT_BUILD_USE_DOCKER=0" >&2 + exit 1 + fi + + USED_DOCKER=1 + echo "[INFO] Building agent binary inside $AGENT_BUILD_IMAGE" >&2 + + docker_env=("--rm" "-u" "$(id -u):$(id -g)" "-v" "$MODULE_ROOT:/workspace" "-w" "/workspace") + + pass_env_if_set() { + local var="$1" + local value="${!var:-}" + if [[ -n "$value" ]]; then + docker_env+=("--env" "$var=$value") + fi + } + + pass_env_if_set PIP_INDEX_URL + pass_env_if_set PIP_EXTRA_INDEX_URL + pass_env_if_set PIP_TRUSTED_HOST + pass_env_if_set HTTP_PROXY + pass_env_if_set HTTPS_PROXY + pass_env_if_set NO_PROXY + pass_env_if_set http_proxy + pass_env_if_set https_proxy + pass_env_if_set no_proxy + + build_script=$(cat <<'INNER' +set -euo pipefail +cd /workspace +apt-get update >/dev/null +apt-get install -y --no-install-recommends binutils >/dev/null +rm -rf /var/lib/apt/lists/* +rm -rf build dist +mkdir -p build/pyinstaller dist +python3 -m venv --copies build/venv +source build/venv/bin/activate pip install --upgrade pip -pip install "$MODULE_ROOT" -pip install "pyinstaller==6.6.0" - -rm -rf "$PYINSTALLER_BUILD"/* -rm -f "$DIST_DIR/argus-agent" - +pip install . +pip install pyinstaller==6.6.0 pyinstaller \ --clean \ --onefile \ --name argus-agent \ - --distpath "$DIST_DIR" \ - --workpath "$PYINSTALLER_BUILD/work" \ - --specpath "$PYINSTALLER_BUILD/spec" \ - --add-data "$MODULE_ROOT/pyproject.toml:." \ - "$MODULE_ROOT/entry.py" + --distpath dist \ + --workpath build/pyinstaller/work \ + --specpath build/pyinstaller/spec \ + --add-data /workspace/pyproject.toml:. \ + entry.py +chmod +x dist/argus-agent -chmod +x "$DIST_DIR/argus-agent" +python3 - <<'PY' +from pathlib import Path +from PyInstaller.archive.readers import CArchiveReader +import sys + +archive = Path('dist/argus-agent') +out_dir = Path('build/compat_check') +out_dir.mkdir(parents=True, exist_ok=True) + +major, minor = sys.version_info[:2] +libpython = f'libpython{major}.{minor}.so.1.0' +expected_libs = [ + libpython, + 'libssl.so.3', + 'libcrypto.so.3', +] +reader = CArchiveReader(str(archive)) +extracted = [] +missing = [] +for name in expected_libs: + try: + data = reader.extract(name) + except KeyError: + missing.append(name) + continue + (out_dir / name).write_bytes(data) + extracted.append(name) +(out_dir / 'manifest').write_text('\n'.join(extracted)) +if extracted: + print('[INFO] Extracted libraries: ' + ', '.join(extracted)) +if missing: + print('[WARN] Missing expected libraries in bundle: ' + ', '.join(missing)) +PY + +compat_check() { + local lib_path="$1" + if [[ ! -f "$lib_path" ]]; then + echo "[WARN] Missing $lib_path for GLIBC check" + return + fi + local max_glibc + max_glibc=$(strings -a "$lib_path" | grep -Eo 'GLIBC_[0-9]+\.[0-9]+' | sort -Vu | tail -n 1 || true) + if [[ -n "$max_glibc" ]]; then + echo "[INFO] $lib_path references up to $max_glibc" + else + echo "[INFO] $lib_path does not expose GLIBC version strings" + fi +} + +compat_libs=() +if [[ -f build/compat_check/manifest ]]; then + mapfile -t compat_libs < build/compat_check/manifest +fi + +if [[ ${#compat_libs[@]} -eq 0 ]]; then + echo "[WARN] No libraries captured for GLIBC inspection" +else + for lib in "${compat_libs[@]}"; do + compat_check "build/compat_check/$lib" + done +fi deactivate +INNER + ) + + if ! docker run "${docker_env[@]}" "$AGENT_BUILD_IMAGE" bash -lc "$build_script"; then + echo "[ERROR] Docker 构建失败,请检查 Docker 权限或设置 AGENT_BUILD_USE_DOCKER=0 在兼容主机上构建" >&2 + exit 1 + fi +} + +if [[ "$AGENT_BUILD_USE_DOCKER" == "1" ]]; then + run_docker_build +else + run_host_build +fi + +if [[ ! -f "$DIST_DIR/argus-agent" ]]; then + echo "[ERROR] Agent binary was not produced" >&2 + exit 1 +fi + +if [[ "$USED_DOCKER" != "1" ]]; then + if [[ ! -x "$VENV_DIR/bin/python" ]]; then + echo "[WARN] PyInstaller virtualenv missing at $VENV_DIR; skipping compatibility check" >&2 + else + COMPAT_DIR="$BUILD_ROOT/compat_check" + rm -rf "$COMPAT_DIR" + mkdir -p "$COMPAT_DIR" + + EXTRACT_SCRIPT=$(cat <<'PY' +from pathlib import Path +from PyInstaller.archive.readers import CArchiveReader +import sys + +archive = Path('dist/argus-agent') +out_dir = Path('build/compat_check') +out_dir.mkdir(parents=True, exist_ok=True) + +major, minor = sys.version_info[:2] +libpython = f'libpython{major}.{minor}.so.1.0' +expected_libs = [ + libpython, + 'libssl.so.3', + 'libcrypto.so.3', +] +reader = CArchiveReader(str(archive)) +extracted = [] +missing = [] +for name in expected_libs: + try: + data = reader.extract(name) + except KeyError: + missing.append(name) + continue + (out_dir / name).write_bytes(data) + extracted.append(name) +(out_dir / 'manifest').write_text('\n'.join(extracted)) +if extracted: + print('[INFO] Extracted libraries: ' + ', '.join(extracted)) +if missing: + print('[WARN] Missing expected libraries in bundle: ' + ', '.join(missing)) +PY +) + + "$VENV_DIR/bin/python" - <&2 + return + fi + if command -v strings >/dev/null 2>&1; then + local max_glibc + max_glibc=$(strings -a "$lib_path" | grep -Eo 'GLIBC_[0-9]+\.[0-9]+' | sort -Vu | tail -n 1 || true) + if [[ -n "$max_glibc" ]]; then + echo "[INFO] $lib_path references up to $max_glibc" + else + echo "[INFO] $lib_path does not expose GLIBC version strings" + fi + else + echo "[WARN] strings command unavailable; cannot inspect $lib_path" >&2 + fi + } + + if [[ ${#compat_libs[@]} -eq 0 ]]; then + echo "[WARN] No libraries captured for GLIBC inspection" >&2 + else + for lib in "${compat_libs[@]}"; do + check_glibc_version "$COMPAT_DIR/$lib" + done + fi + fi +else + echo "[INFO] Compatibility check executed inside container" +fi echo "[INFO] Agent binary generated at $DIST_DIR/argus-agent" diff --git a/src/agent/tests/scripts/agent_entrypoint.sh b/src/agent/tests/scripts/agent_entrypoint.sh index 8536da1..b46a213 100755 --- a/src/agent/tests/scripts/agent_entrypoint.sh +++ b/src/agent/tests/scripts/agent_entrypoint.sh @@ -21,9 +21,14 @@ done if [[ -x "$DNS_SCRIPT" ]]; then log "执行 update-dns.sh 更新容器 DNS" - if ! "$DNS_SCRIPT"; then - log "update-dns.sh 执行失败,继续尝试默认 DNS" - fi + while true; do + if "$DNS_SCRIPT"; then + log "update-dns.sh 执行成功" + break + fi + log "update-dns.sh 执行失败,3 秒后重试" + sleep 3 + done else log "未获取到 update-dns.sh,使用镜像默认 DNS" fi