From 6c45cc94f3b14fc290ec16734ba77b9dc8a49516 Mon Sep 17 00:00:00 2001 From: yuyr Date: Tue, 30 Jun 2026 16:18:41 +0800 Subject: [PATCH] add multi-arch docker installer --- deploy/arm64-installer/.env.example | 14 +- .../compose/docker-compose.yml | 28 +- .../provisioning/dashboards/dashboard.yml | 4 +- deploy/arm64-installer/compose/prometheus.yml | 2 +- deploy/arm64-installer/docs/README.en.md | 53 ++- deploy/arm64-installer/docs/README.zh-CN.md | 41 +- deploy/arm64-installer/docs/operations.en.md | 21 +- .../arm64-installer/docs/operations.zh-CN.md | 21 +- .../docs/troubleshooting.en.md | 36 +- .../docs/troubleshooting.zh-CN.md | 36 +- deploy/arm64-installer/install.sh | 7 +- deploy/arm64-installer/scripts/common.sh | 167 +++++++- deploy/arm64-installer/self-check.sh | 1 + deploy/arm64-installer/start.sh | 1 + deploy/arm64-installer/status.sh | 8 + deploy/arm64-installer/upgrade.sh | 1 + docker/ours-rp-runtime.Dockerfile | 90 +++-- .../docker/build_arm64_installer_package.sh | 213 +--------- scripts/docker/build_arm64_runtime_image.sh | 182 +-------- .../docker/build_docker_installer_package.sh | 367 ++++++++++++++++++ scripts/docker/build_docker_runtime_image.sh | 238 ++++++++++++ 21 files changed, 1032 insertions(+), 499 deletions(-) mode change 100755 => 100644 deploy/arm64-installer/scripts/common.sh create mode 100755 scripts/docker/build_docker_installer_package.sh create mode 100755 scripts/docker/build_docker_runtime_image.sh diff --git a/deploy/arm64-installer/.env.example b/deploy/arm64-installer/.env.example index c20fbeb..96dc20e 100644 --- a/deploy/arm64-installer/.env.example +++ b/deploy/arm64-installer/.env.example @@ -1,8 +1,14 @@ -# ours RP ARM64 installer configuration +# ours RP Docker installer configuration +# `build_docker_installer_package.sh` rewrites package-specific defaults below. # 中文说明见 docs/README.zh-CN.md。English guide: docs/README.en.md +# Package metadata and architecture guardrails. +PACKAGE_ARCH=arm64 +PACKAGE_PLATFORM=linux/arm64 +ALLOW_CROSS_ARCH=0 + # Compose project name. -COMPOSE_PROJECT_NAME=ours-rp-arm64 +COMPOSE_PROJECT_NAME=ours-rp-arm64-installer # Runtime image loaded from images/*.tar.gz by install.sh. RPKI_IMAGE=ours-rp-runtime-arm64:dev @@ -13,7 +19,7 @@ RPKI_PLATFORM=linux/arm64 SOAK_RESTART_POLICY=unless-stopped # Host-side persistent data directory. All state/runs/logs/monitoring data are bind-mounted here. -HOST_DATA_DIR=/var/lib/ours-rp-arm64 +HOST_DATA_DIR=/var/lib/ours-rp-arm64-installer # RIR list. Options: afrinic,apnic,arin,lacnic,ripe RIRS=afrinic,apnic,arin,lacnic,ripe @@ -72,7 +78,7 @@ METRICS_PORT=9556 METRICS_POLL_SECS=10 # Prometheus / Grafana. -# Monitor images are packaged as ARM64 docker-save archives and loaded by install.sh. +# Monitor images are packaged as docker-save archives and loaded by install.sh. MONITOR_PLATFORM=linux/arm64 PROMETHEUS_IMAGE=prom/prometheus:v2.55.1 GRAFANA_IMAGE=grafana/grafana:11.3.1 diff --git a/deploy/arm64-installer/compose/docker-compose.yml b/deploy/arm64-installer/compose/docker-compose.yml index 3221a0c..6c72e5e 100644 --- a/deploy/arm64-installer/compose/docker-compose.yml +++ b/deploy/arm64-installer/compose/docker-compose.yml @@ -2,7 +2,7 @@ services: ours-rp-soak: image: ${RPKI_IMAGE:-ours-rp-runtime-arm64:dev} platform: ${RPKI_PLATFORM:-linux/arm64} - container_name: ${COMPOSE_PROJECT_NAME:-ours-rp-arm64}-soak + container_name: ${COMPOSE_PROJECT_NAME:-ours-rp-package-installer}-soak env_file: - ../.env environment: @@ -13,10 +13,10 @@ services: FIXTURE_DIR: /opt/ours-rp/fixtures volumes: - ../.env:/opt/ours-rp/.env:ro - - ${HOST_DATA_DIR:-/var/lib/ours-rp-arm64}/state:/var/lib/ours-rp/state - - ${HOST_DATA_DIR:-/var/lib/ours-rp-arm64}/runs:/var/lib/ours-rp/runs - - ${HOST_DATA_DIR:-/var/lib/ours-rp-arm64}/logs:/var/lib/ours-rp/logs - - ${HOST_DATA_DIR:-/var/lib/ours-rp-arm64}/tmp:/var/lib/ours-rp/tmp + - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/state:/var/lib/ours-rp/state + - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/runs:/var/lib/ours-rp/runs + - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/logs:/var/lib/ours-rp/logs + - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/tmp:/var/lib/ours-rp/tmp restart: ${SOAK_RESTART_POLICY:-unless-stopped} profiles: - core @@ -24,7 +24,7 @@ services: artifact-metrics: image: ${RPKI_IMAGE:-ours-rp-runtime-arm64:dev} platform: ${RPKI_PLATFORM:-linux/arm64} - container_name: ${COMPOSE_PROJECT_NAME:-ours-rp-arm64}-artifact-metrics + container_name: ${COMPOSE_PROJECT_NAME:-ours-rp-package-installer}-artifact-metrics env_file: - ../.env command: @@ -36,13 +36,13 @@ services: - --poll-secs - ${METRICS_POLL_SECS:-10} - --instance - - ${METRICS_INSTANCE:-arm64-installer} + - ${METRICS_INSTANCE:-package-installer} ports: - "${METRICS_PORT:-9556}:9556" volumes: - - ${HOST_DATA_DIR:-/var/lib/ours-rp-arm64}/state:/var/lib/ours-rp/state:ro - - ${HOST_DATA_DIR:-/var/lib/ours-rp-arm64}/runs:/var/lib/ours-rp/runs:ro - - ${HOST_DATA_DIR:-/var/lib/ours-rp-arm64}/logs:/var/lib/ours-rp/logs:ro + - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/state:/var/lib/ours-rp/state:ro + - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/runs:/var/lib/ours-rp/runs:ro + - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/logs:/var/lib/ours-rp/logs:ro restart: unless-stopped profiles: - sidecar @@ -50,7 +50,7 @@ services: prometheus: image: ${PROMETHEUS_IMAGE:-prom/prometheus:v2.55.1} platform: ${MONITOR_PLATFORM:-linux/arm64} - container_name: ${COMPOSE_PROJECT_NAME:-ours-rp-arm64}-prometheus + container_name: ${COMPOSE_PROJECT_NAME:-ours-rp-package-installer}-prometheus command: - --config.file=/etc/prometheus/prometheus.yml - --storage.tsdb.path=/prometheus @@ -63,7 +63,7 @@ services: - "${PROMETHEUS_PORT:-9090}:9090" volumes: - ./prometheus.yml:/etc/prometheus/prometheus.yml:ro - - ${HOST_DATA_DIR:-/var/lib/ours-rp-arm64}/prometheus:/prometheus + - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/prometheus:/prometheus restart: unless-stopped profiles: - monitor @@ -71,7 +71,7 @@ services: grafana: image: ${GRAFANA_IMAGE:-grafana/grafana:11.3.1} platform: ${MONITOR_PLATFORM:-linux/arm64} - container_name: ${COMPOSE_PROJECT_NAME:-ours-rp-arm64}-grafana + container_name: ${COMPOSE_PROJECT_NAME:-ours-rp-package-installer}-grafana depends_on: - prometheus user: "0:0" @@ -82,7 +82,7 @@ services: GF_SECURITY_ADMIN_PASSWORD: ${GRAFANA_ADMIN_PASSWORD:-admin} GF_USERS_ALLOW_SIGN_UP: "false" volumes: - - ${HOST_DATA_DIR:-/var/lib/ours-rp-arm64}/grafana:/var/lib/grafana + - ${HOST_DATA_DIR:-/var/lib/ours-rp-package-installer}/grafana:/var/lib/grafana - ./grafana/provisioning:/etc/grafana/provisioning:ro - ./grafana/dashboards:/var/lib/grafana/dashboards:ro restart: unless-stopped diff --git a/deploy/arm64-installer/compose/grafana/provisioning/dashboards/dashboard.yml b/deploy/arm64-installer/compose/grafana/provisioning/dashboards/dashboard.yml index 8cba889..e54a042 100644 --- a/deploy/arm64-installer/compose/grafana/provisioning/dashboards/dashboard.yml +++ b/deploy/arm64-installer/compose/grafana/provisioning/dashboards/dashboard.yml @@ -1,9 +1,9 @@ apiVersion: 1 providers: - - name: ours-rp-arm64 + - name: ours-rp-docker-installer orgId: 1 - folder: Ours RP ARM64 + folder: Ours RP Docker Installer type: file disableDeletion: false updateIntervalSeconds: 10 diff --git a/deploy/arm64-installer/compose/prometheus.yml b/deploy/arm64-installer/compose/prometheus.yml index 658f425..1d67cb8 100644 --- a/deploy/arm64-installer/compose/prometheus.yml +++ b/deploy/arm64-installer/compose/prometheus.yml @@ -10,4 +10,4 @@ scrape_configs: - artifact-metrics:9556 labels: rp: ours-rp - source: arm64-compose-artifact-sidecar + source: docker-installer-artifact-sidecar diff --git a/deploy/arm64-installer/docs/README.en.md b/deploy/arm64-installer/docs/README.en.md index c97e966..0f9b6ef 100644 --- a/deploy/arm64-installer/docs/README.en.md +++ b/deploy/arm64-installer/docs/README.en.md @@ -1,16 +1,22 @@ -# ours RP ARM64 Installer Guide +# ours RP Docker Installer Guide (`__PACKAGE_ARCH__` / `__PACKAGE_PLATFORM__`) ## Goal -This package deploys ours RP on a `linux/arm64` server with Docker Compose and continuously runs all five RIR validation. +This package deploys ours RP on Linux servers whose host architecture matches the packaged installer metadata, using Docker Compose and continuous all-five RIR validation. -The package includes the ours RP ARM64 runtime image, Prometheus ARM64 image and Grafana ARM64 image, so deployment does not pull application images on the target host. Runtime state, run artifacts, logs, Prometheus data and Grafana data are persisted through host bind mounts. +The package includes architecture-matched ours RP runtime, Prometheus and Grafana images, so deployment does not need to pull application images on the target host. Runtime state, run artifacts, logs, Prometheus data and Grafana data are persisted through host bind mounts. + +By default the host architecture must match `PACKAGE_ARCH` from `PACKAGE-MANIFEST.env`. If you intentionally want to run this package on a different host architecture through QEMU/binfmt, set: + +```bash +ALLOW_CROSS_ARCH=1 +``` ## Quick Start ```bash -tar -xzf ours-rp-arm64-installer-*.tar.gz -cd ours-rp-arm64-installer-* +tar -xzf ours-rp-docker-installer-__PACKAGE_ARCH__-*.tar.gz +cd ours-rp-docker-installer-__PACKAGE_ARCH__-* ./install.sh cp .env.example .env # install.sh creates .env automatically if missing @@ -21,6 +27,8 @@ vim .env Defaults: +- `PACKAGE_ARCH=__PACKAGE_ARCH__` +- `PACKAGE_PLATFORM=__PACKAGE_PLATFORM__` - `RIRS=afrinic,apnic,arin,lacnic,ripe` - `MAX_RUNS=-1` - `INTERVAL_SECS=600` @@ -28,9 +36,10 @@ Defaults: - `LIVE_TA_REFRESH_BEFORE_SNAPSHOT=1` - `PERIODIC_SNAPSHOT_RESET=0` - `PERIODIC_SNAPSHOT_MAX_DELTAS=100` -- `HOST_DATA_DIR=/var/lib/ours-rp-arm64` +- `HOST_DATA_DIR=__HOST_DATA_DIR__` - `SOAK_RESTART_POLICY=unless-stopped` -- `MONITOR_PLATFORM=linux/arm64` +- `MONITOR_PLATFORM=__PACKAGE_PLATFORM__` +- `ALLOW_CROSS_ARCH=0` ## First Start Semantics @@ -38,6 +47,22 @@ If there is no successful run under `HOST_DATA_DIR/runs`, `start.sh` starts the The first snapshot refreshes live TA certificates before starting the RP process. +## Architecture Guardrails + +The key scripts read: + +- `PACKAGE_ARCH` / `PACKAGE_PLATFORM` from `.env` +- `PACKAGE-MANIFEST.env` +- the current host `uname -m` + +Default behavior: + +1. matching host and package architectures: run natively; +2. mismatched host and package architectures: fail with an explicit error; +3. only when `ALLOW_CROSS_ARCH=1` is set do the scripts attempt to enable matching `binfmt/qemu`. + +That means an `arm64` package on an `x86_64` host does not silently switch to emulation by default. + ## Ports Default ports: @@ -60,7 +85,7 @@ Change the password and restrict public access for production deployments. Default host directory: ```text -/var/lib/ours-rp-arm64/ +__HOST_DATA_DIR__/ state/ runs/ logs/ @@ -109,12 +134,6 @@ Check the latest `run-meta.json` for: ./uninstall.sh ``` -`uninstall.sh` keeps data by default. Use the following only when you really want to delete `HOST_DATA_DIR`: - -```bash -./uninstall.sh --purge-data -``` - For finite acceptance tests, for example `MAX_RUNS=3`, also set: ```bash @@ -122,3 +141,9 @@ SOAK_RESTART_POLICY=no ``` Otherwise Compose `unless-stopped` will restart the container after it exits successfully. + +`uninstall.sh` keeps data by default. Use the following only when you really want to delete `HOST_DATA_DIR`: + +```bash +./uninstall.sh --purge-data +``` diff --git a/deploy/arm64-installer/docs/README.zh-CN.md b/deploy/arm64-installer/docs/README.zh-CN.md index b3dc5ec..023fd16 100644 --- a/deploy/arm64-installer/docs/README.zh-CN.md +++ b/deploy/arm64-installer/docs/README.zh-CN.md @@ -1,16 +1,22 @@ -# ours RP ARM64 安装包使用说明 +# ours RP Docker 安装包使用说明(`__PACKAGE_ARCH__` / `__PACKAGE_PLATFORM__`) ## 目标 -本安装包用于在 `linux/arm64` 服务器上通过 Docker Compose 部署 ours RP,并持续运行 all5 RIR 同步验证任务。 +本安装包用于在目标架构与包元数据匹配的 Linux 服务器上,通过 Docker Compose 部署 ours RP,并持续运行 all5 RIR 同步验证任务。 -安装包内置 ours RP ARM64 runtime、Prometheus ARM64、Grafana ARM64 镜像,部署时不需要现场拉取应用镜像。运行产物、状态数据库、日志、Prometheus 和 Grafana 数据均通过宿主机目录挂载保存。 +安装包内置与包架构一致的 ours RP runtime、Prometheus、Grafana 镜像,部署时不需要现场拉取应用镜像。运行产物、状态数据库、日志、Prometheus 和 Grafana 数据均通过宿主机目录挂载保存。 + +默认情况下,宿主架构必须与 `PACKAGE-MANIFEST.env` 中的 `PACKAGE_ARCH` 匹配;若确需在异构主机上通过 QEMU/binfmt 运行,必须显式设置: + +```bash +ALLOW_CROSS_ARCH=1 +``` ## 快速开始 ```bash -tar -xzf ours-rp-arm64-installer-*.tar.gz -cd ours-rp-arm64-installer-* +tar -xzf ours-rp-docker-installer-__PACKAGE_ARCH__-*.tar.gz +cd ours-rp-docker-installer-__PACKAGE_ARCH__-* ./install.sh cp .env.example .env # 如 install.sh 已自动创建,可直接编辑现有 .env @@ -21,6 +27,8 @@ vim .env 默认配置: +- `PACKAGE_ARCH=__PACKAGE_ARCH__` +- `PACKAGE_PLATFORM=__PACKAGE_PLATFORM__` - `RIRS=afrinic,apnic,arin,lacnic,ripe` - `MAX_RUNS=-1` - `INTERVAL_SECS=600` @@ -28,9 +36,10 @@ vim .env - `LIVE_TA_REFRESH_BEFORE_SNAPSHOT=1` - `PERIODIC_SNAPSHOT_RESET=0` - `PERIODIC_SNAPSHOT_MAX_DELTAS=100` -- `HOST_DATA_DIR=/var/lib/ours-rp-arm64` +- `HOST_DATA_DIR=__HOST_DATA_DIR__` - `SOAK_RESTART_POLICY=unless-stopped` -- `MONITOR_PLATFORM=linux/arm64` +- `MONITOR_PLATFORM=__PACKAGE_PLATFORM__` +- `ALLOW_CROSS_ARCH=0` ## 首次启动语义 @@ -38,6 +47,22 @@ vim .env 第一轮 snapshot 会先拉取 live TA,避免 clean state 使用旧 fixture TA。 +## 架构检查 + +关键脚本会读取: + +- `.env` 中的 `PACKAGE_ARCH` / `PACKAGE_PLATFORM` +- `PACKAGE-MANIFEST.env` +- 当前宿主 `uname -m` + +默认行为: + +1. 宿主与包架构匹配:直接运行; +2. 宿主与包架构不匹配:明确报错并停止; +3. 仅当 `ALLOW_CROSS_ARCH=1` 时,脚本才会尝试启用对应架构的 `binfmt/qemu`。 + +因此在 `x86_64` 主机上运行 `arm64` 包,不会默认静默进入模拟执行。 + ## 访问端口 默认端口: @@ -60,7 +85,7 @@ GRAFANA_ADMIN_PASSWORD=admin 默认宿主机目录: ```text -/var/lib/ours-rp-arm64/ +__HOST_DATA_DIR__/ state/ runs/ logs/ diff --git a/deploy/arm64-installer/docs/operations.en.md b/deploy/arm64-installer/docs/operations.en.md index 5ce99a8..1695e0e 100644 --- a/deploy/arm64-installer/docs/operations.en.md +++ b/deploy/arm64-installer/docs/operations.en.md @@ -1,4 +1,4 @@ -# Operations Guide +# Operations Guide (`__PACKAGE_ARCH__`) ## Install @@ -10,8 +10,9 @@ The installer is idempotent: - existing `.env` is kept; - existing Docker/Compose installation is reused; -- repeated loading of packaged ours RP, Prometheus and Grafana ARM64 images is safe; -- existing data directory is reused. +- repeated loading of packaged ours RP, Prometheus and Grafana images for the package architecture is safe; +- existing data directory is reused; +- matching host/package architecture is required by default. ## Start @@ -25,6 +26,14 @@ Start without waiting for the first snapshot: ./start.sh --no-wait-first-run ``` +If you intentionally want to run this package on a different host architecture through QEMU/binfmt, first set: + +```bash +ALLOW_CROSS_ARCH=1 +``` + +Then rerun `./install.sh` or `./self-check.sh`, and only then will the scripts attempt to enable the matching binfmt handler. + ## Stop and Restart ```bash @@ -46,10 +55,14 @@ Important checks: - `HOST_DATA_DIR` is writable; - Compose config is valid; - latest run status; -- metrics, Prometheus and Grafana endpoints. +- metrics, Prometheus and Grafana endpoints; +- whether `host_arch`, `package_arch`, and `arch_mode` match expectations. `status.sh` also prints: +- `package_arch` +- `package_platform` +- `allow_cross_arch` - `periodic_snapshot_reset` - `periodic_snapshot_max_deltas` diff --git a/deploy/arm64-installer/docs/operations.zh-CN.md b/deploy/arm64-installer/docs/operations.zh-CN.md index 8e6b483..b2a8dee 100644 --- a/deploy/arm64-installer/docs/operations.zh-CN.md +++ b/deploy/arm64-installer/docs/operations.zh-CN.md @@ -1,4 +1,4 @@ -# 运维手册 +# 运维手册(`__PACKAGE_ARCH__`) ## 安装 @@ -10,8 +10,9 @@ - 已有 `.env` 不覆盖; - 已安装 Docker/Compose 则跳过; -- 包内 ours RP、Prometheus、Grafana ARM64 镜像重复加载是安全的; -- 数据目录已存在则复用。 +- 包内 ours RP、Prometheus、Grafana 对应架构镜像重复加载是安全的; +- 数据目录已存在则复用; +- 默认要求宿主与包架构匹配。 ## 启动 @@ -25,6 +26,14 @@ ./start.sh --no-wait-first-run ``` +如需显式允许异构主机通过 QEMU/binfmt 跑包,先在 `.env` 中设置: + +```bash +ALLOW_CROSS_ARCH=1 +``` + +然后重新执行 `./install.sh` 或 `./self-check.sh`,脚本才会尝试启用对应 binfmt。 + ## 停止和重启 ```bash @@ -46,10 +55,14 @@ - `HOST_DATA_DIR` 可写; - Compose 配置合法; - 最新 run 状态; -- metrics、Prometheus、Grafana endpoint。 +- metrics、Prometheus、Grafana endpoint; +- `host_arch`、`package_arch`、`arch_mode` 是否符合预期。 `status.sh` 还会显示: +- `package_arch` +- `package_platform` +- `allow_cross_arch` - `periodic_snapshot_reset` - `periodic_snapshot_max_deltas` diff --git a/deploy/arm64-installer/docs/troubleshooting.en.md b/deploy/arm64-installer/docs/troubleshooting.en.md index 189b67c..d72eab7 100644 --- a/deploy/arm64-installer/docs/troubleshooting.en.md +++ b/deploy/arm64-installer/docs/troubleshooting.en.md @@ -1,4 +1,4 @@ -# Troubleshooting +# Troubleshooting (`__PACKAGE_ARCH__` / `__PACKAGE_PLATFORM__`) ## Docker or Compose Is Unavailable @@ -15,16 +15,34 @@ If missing, run: ./install.sh ``` -## ARM64 Image Cannot Run +## Package Architecture Mismatch -Running ARM64 images on x86_64 requires binfmt/qemu: +If `status.sh`, `install.sh`, or `self-check.sh` reports: -```bash -docker run --rm --privileged tonistiigi/binfmt --install arm64 -docker run --rm --platform linux/arm64 debian:bookworm-slim uname -m +```text +package architecture mismatch ``` -Expected output: `aarch64`. +the host `uname -m` does not match the packaged `PACKAGE_ARCH=__PACKAGE_ARCH__`. The default behavior is to fail clearly rather than silently switching to emulation. + +Only if you intentionally accept QEMU/binfmt cross-architecture execution should you set: + +```bash +ALLOW_CROSS_ARCH=1 +``` + +Then rerun install or self-check. + +## `__PACKAGE_ARCH__` Image Cannot Run + +Running `__PACKAGE_PLATFORM__` images on a different host architecture requires binfmt/qemu. The installer only attempts this automatically when `ALLOW_CROSS_ARCH=1`; you can also enable it manually: + +```bash +docker run --rm --privileged tonistiigi/binfmt --install __PACKAGE_ARCH__ +docker run --rm --platform __PACKAGE_PLATFORM__ debian:bookworm-slim uname -m +``` + +For `arm64` packages the expected output is `aarch64`; for `amd64` packages the expected output is `x86_64`. ## First Snapshot Times Out @@ -40,8 +58,8 @@ Check: ```bash grep LIVE_TA_REFRESH_BEFORE_SNAPSHOT .env -ls -l /var/lib/ours-rp-arm64/state/live-ta -tail -100 /var/lib/ours-rp-arm64/logs/live-ta-refresh-*.log +ls -l __HOST_DATA_DIR__/state/live-ta +tail -100 __HOST_DATA_DIR__/logs/live-ta-refresh-*.log ``` In `file-live-ta` mode, snapshot should wait until live TA refresh succeeds. diff --git a/deploy/arm64-installer/docs/troubleshooting.zh-CN.md b/deploy/arm64-installer/docs/troubleshooting.zh-CN.md index 1c20090..8429a5a 100644 --- a/deploy/arm64-installer/docs/troubleshooting.zh-CN.md +++ b/deploy/arm64-installer/docs/troubleshooting.zh-CN.md @@ -1,4 +1,4 @@ -# 故障排查 +# 故障排查(`__PACKAGE_ARCH__` / `__PACKAGE_PLATFORM__`) ## Docker 或 Compose 不可用 @@ -15,16 +15,34 @@ docker compose version ./install.sh ``` -## ARM64 镜像无法运行 +## 架构不匹配报错 -在 x86_64 机器上运行 ARM64 镜像需要 binfmt/qemu: +当 `status.sh` / `install.sh` / `self-check.sh` 报出: -```bash -docker run --rm --privileged tonistiigi/binfmt --install arm64 -docker run --rm --platform linux/arm64 debian:bookworm-slim uname -m +```text +package architecture mismatch ``` -预期输出 `aarch64`。 +说明宿主 `uname -m` 与包的 `PACKAGE_ARCH=__PACKAGE_ARCH__` 不一致。默认行为就是失败而不是静默模拟运行。 + +只有在你明确接受 QEMU/binfmt 跨架构运行时,才设置: + +```bash +ALLOW_CROSS_ARCH=1 +``` + +然后重新执行安装或自检。 + +## `__PACKAGE_ARCH__` 镜像无法运行 + +在异构主机上运行 `__PACKAGE_PLATFORM__` 镜像需要 binfmt/qemu。安装脚本只有在 `ALLOW_CROSS_ARCH=1` 时才会自动尝试启用;也可以手动执行: + +```bash +docker run --rm --privileged tonistiigi/binfmt --install __PACKAGE_ARCH__ +docker run --rm --platform __PACKAGE_PLATFORM__ debian:bookworm-slim uname -m +``` + +对于 `arm64` 包,预期输出 `aarch64`;对于 `amd64` 包,预期输出 `x86_64`。 ## 首轮 snapshot 超时 @@ -40,8 +58,8 @@ all5 snapshot 可能很慢,尤其在 QEMU 环境。可以提高超时: ```bash grep LIVE_TA_REFRESH_BEFORE_SNAPSHOT .env -ls -l /var/lib/ours-rp-arm64/state/live-ta -tail -100 /var/lib/ours-rp-arm64/logs/live-ta-refresh-*.log +ls -l __HOST_DATA_DIR__/state/live-ta +tail -100 __HOST_DATA_DIR__/logs/live-ta-refresh-*.log ``` `file-live-ta` 模式下,snapshot 应等待 live TA 成功刷新。 diff --git a/deploy/arm64-installer/install.sh b/deploy/arm64-installer/install.sh index 467632e..3ae8699 100755 --- a/deploy/arm64-installer/install.sh +++ b/deploy/arm64-installer/install.sh @@ -8,7 +8,7 @@ usage() { cat <<'USAGE' Usage: ./install.sh [--skip-dep-install] -Install or update the ours RP ARM64 compose package idempotently. +Install or update the ours RP Docker installer package idempotently. USAGE } @@ -29,6 +29,7 @@ while [[ $# -gt 0 ]]; do done load_env +assert_arch_compatibility install_docker_if_missing require_cmd curl require_cmd jq @@ -40,6 +41,8 @@ load_installer_images ensure_binfmt_if_needed verify_runtime_image verify_monitor_images -compose_cmd --profile core --profile sidecar --profile monitor config >/tmp/ours-rp-arm64-compose-config.yml +compose_config_path="$(mktemp "${TMPDIR:-/tmp}/ours-rp-compose-config.XXXXXX.yml")" +compose_cmd --profile core --profile sidecar --profile monitor config >"$compose_config_path" "$SCRIPT_DIR/self-check.sh" --quick +rm -f "$compose_config_path" log "install complete" diff --git a/deploy/arm64-installer/scripts/common.sh b/deploy/arm64-installer/scripts/common.sh old mode 100755 new mode 100644 index 573cefc..58141a6 --- a/deploy/arm64-installer/scripts/common.sh +++ b/deploy/arm64-installer/scripts/common.sh @@ -5,6 +5,7 @@ INSTALLER_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" ENV_FILE="${ENV_FILE:-$INSTALLER_ROOT/.env}" ENV_EXAMPLE="$INSTALLER_ROOT/.env.example" COMPOSE_FILE="$INSTALLER_ROOT/compose/docker-compose.yml" +MANIFEST_FILE="${MANIFEST_FILE:-$INSTALLER_ROOT/PACKAGE-MANIFEST.env}" log() { printf '[ours-rp-installer] %s\n' "$*" @@ -23,7 +24,114 @@ require_cmd() { command -v "$1" >/dev/null 2>&1 || die "missing command: $1" } +normalize_arch() { + case "$1" in + amd64|x86_64|linux/amd64) + printf 'amd64\n' + ;; + arm64|aarch64|linux/arm64) + printf 'arm64\n' + ;; + *) + return 1 + ;; + esac +} + +platform_for_arch() { + printf 'linux/%s\n' "$1" +} + +env_flag_enabled() { + case "${1:-0}" in + 1|true|TRUE|yes|YES|on|ON) + return 0 + ;; + *) + return 1 + ;; + esac +} + +detect_host_arch() { + local raw_arch + raw_arch="$(uname -m)" + normalize_arch "$raw_arch" || die "unsupported host architecture: $raw_arch" +} + +load_manifest() { + MANIFEST_PACKAGE_ARCH_RAW="" + MANIFEST_PACKAGE_PLATFORM_RAW="" + if [[ -f "$MANIFEST_FILE" ]]; then + set -a + # shellcheck disable=SC1090 + source "$MANIFEST_FILE" + set +a + MANIFEST_PACKAGE_ARCH_RAW="${PACKAGE_ARCH:-${package_arch:-}}" + MANIFEST_PACKAGE_PLATFORM_RAW="${PACKAGE_PLATFORM:-${package_platform:-}}" + fi +} + +effective_package_arch() { + if [[ -n "${PACKAGE_ARCH:-}" ]]; then + normalize_arch "$PACKAGE_ARCH" || die "unsupported PACKAGE_ARCH=${PACKAGE_ARCH}" + return 0 + fi + if [[ -n "${PACKAGE_PLATFORM:-}" ]]; then + normalize_arch "$PACKAGE_PLATFORM" || die "unsupported PACKAGE_PLATFORM=${PACKAGE_PLATFORM}" + return 0 + fi + if [[ -n "${RPKI_PLATFORM:-}" ]]; then + normalize_arch "$RPKI_PLATFORM" || die "unsupported RPKI_PLATFORM=${RPKI_PLATFORM}" + return 0 + fi + if [[ -n "${MONITOR_PLATFORM:-}" ]]; then + normalize_arch "$MONITOR_PLATFORM" || die "unsupported MONITOR_PLATFORM=${MONITOR_PLATFORM}" + return 0 + fi + die "unable to determine package architecture from manifest or env" +} + +arch_mode() { + local host_arch package_arch + host_arch="$(detect_host_arch)" + package_arch="$(effective_package_arch)" + if [[ "$host_arch" == "$package_arch" ]]; then + printf 'native\n' + return 0 + fi + if env_flag_enabled "${ALLOW_CROSS_ARCH:-0}"; then + printf 'cross-arch-enabled\n' + return 0 + fi + printf 'mismatch-blocked\n' +} + +assert_arch_compatibility() { + local host_arch package_arch package_platform mode + host_arch="$(detect_host_arch)" + package_arch="$(effective_package_arch)" + package_platform="${PACKAGE_PLATFORM:-${RPKI_PLATFORM:-$(platform_for_arch "$package_arch")}}" + mode="$(arch_mode)" + case "$mode" in + native) + return 0 + ;; + cross-arch-enabled) + log "cross-arch execution explicitly enabled: host_arch=$host_arch package_arch=$package_arch package_platform=$package_platform" + return 0 + ;; + mismatch-blocked) + die "package architecture mismatch: host_arch=$host_arch package_arch=$package_arch package_platform=$package_platform. Set ALLOW_CROSS_ARCH=1 in $ENV_FILE to allow explicit QEMU/binfmt emulation." + ;; + *) + die "unknown arch compatibility mode: $mode" + ;; + esac +} + load_env() { + load_manifest if [[ ! -f "$ENV_FILE" ]]; then [[ -f "$ENV_EXAMPLE" ]] || die "missing $ENV_EXAMPLE" cp "$ENV_EXAMPLE" "$ENV_FILE" @@ -33,18 +141,45 @@ load_env() { # shellcheck disable=SC1090 source "$ENV_FILE" set +a - HOST_DATA_DIR="${HOST_DATA_DIR:-/var/lib/ours-rp-arm64}" - COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-ours-rp-arm64}" - RPKI_IMAGE="${RPKI_IMAGE:-ours-rp-runtime-arm64:dev}" - RPKI_PLATFORM="${RPKI_PLATFORM:-linux/arm64}" - MONITOR_PLATFORM="${MONITOR_PLATFORM:-linux/arm64}" + + if [[ -n "${MANIFEST_PACKAGE_ARCH_RAW:-}" ]]; then + manifest_package_arch="$(normalize_arch "$MANIFEST_PACKAGE_ARCH_RAW")" || die "unsupported manifest package arch: $MANIFEST_PACKAGE_ARCH_RAW" + if [[ -n "${PACKAGE_ARCH:-}" ]]; then + env_package_arch="$(normalize_arch "$PACKAGE_ARCH")" || die "unsupported env package arch: $PACKAGE_ARCH" + [[ "$env_package_arch" == "$manifest_package_arch" ]] || die "env PACKAGE_ARCH=$env_package_arch mismatches manifest PACKAGE_ARCH=$manifest_package_arch" + fi + PACKAGE_ARCH="$manifest_package_arch" + else + if [[ -z "${PACKAGE_ARCH:-}" ]]; then + PACKAGE_ARCH="$(effective_package_arch)" + else + PACKAGE_ARCH="$(normalize_arch "$PACKAGE_ARCH")" + fi + fi + if [[ -n "${MANIFEST_PACKAGE_PLATFORM_RAW:-}" ]]; then + manifest_package_platform="$MANIFEST_PACKAGE_PLATFORM_RAW" + if [[ -n "${PACKAGE_PLATFORM:-}" && "$PACKAGE_PLATFORM" != "$manifest_package_platform" ]]; then + die "env PACKAGE_PLATFORM=$PACKAGE_PLATFORM mismatches manifest PACKAGE_PLATFORM=$manifest_package_platform" + fi + PACKAGE_PLATFORM="$manifest_package_platform" + else + PACKAGE_PLATFORM="${PACKAGE_PLATFORM:-$(platform_for_arch "$PACKAGE_ARCH")}" + fi + + HOST_DATA_DIR="${HOST_DATA_DIR:-/var/lib/ours-rp-${PACKAGE_ARCH}-installer}" + COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-ours-rp-${PACKAGE_ARCH}-installer}" + RPKI_IMAGE="${RPKI_IMAGE:-ours-rp-runtime-${PACKAGE_ARCH}:dev}" + RPKI_PLATFORM="${RPKI_PLATFORM:-$PACKAGE_PLATFORM}" + MONITOR_PLATFORM="${MONITOR_PLATFORM:-$PACKAGE_PLATFORM}" PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-prom/prometheus:v2.55.1}" GRAFANA_IMAGE="${GRAFANA_IMAGE:-grafana/grafana:11.3.1}" + METRICS_INSTANCE="${METRICS_INSTANCE:-${PACKAGE_ARCH}-installer}" + ALLOW_CROSS_ARCH="${ALLOW_CROSS_ARCH:-0}" FIRST_RUN_WAIT_TIMEOUT_SECS="${FIRST_RUN_WAIT_TIMEOUT_SECS:-7200}" } compose_cmd() { - docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" -p "${COMPOSE_PROJECT_NAME:-ours-rp-arm64}" "$@" + docker compose --env-file "$ENV_FILE" -f "$COMPOSE_FILE" -p "$COMPOSE_PROJECT_NAME" "$@" } create_data_dirs() { @@ -225,21 +360,27 @@ load_installer_images() { ensure_binfmt_if_needed() { require_cmd docker load_env - local host_arch - host_arch="$(uname -m)" - if [[ "$RPKI_PLATFORM" == "linux/arm64" && "$host_arch" != "aarch64" && "$host_arch" != "arm64" ]]; then - log "host arch is $host_arch; ensuring binfmt/qemu for arm64" - docker run --rm --privileged tonistiigi/binfmt --install arm64 + local host_arch package_arch + host_arch="$(detect_host_arch)" + package_arch="$(effective_package_arch)" + if [[ "$host_arch" == "$package_arch" ]]; then + return 0 fi + assert_arch_compatibility + log "host arch is $host_arch; enabling binfmt/qemu for package arch $package_arch" + docker run --rm --privileged tonistiigi/binfmt --install "$package_arch" } verify_runtime_image() { load_env require_cmd docker + local help_path + help_path="$(mktemp "${TMPDIR:-/tmp}/ours-rp-rpki-help.XXXXXX")" log "verifying runtime image $RPKI_IMAGE on $RPKI_PLATFORM" docker image inspect "$RPKI_IMAGE" >/dev/null - docker run --rm --platform "$RPKI_PLATFORM" "$RPKI_IMAGE" /opt/ours-rp/bin/rpki --help >/tmp/ours-rp-arm64-rpki-help.txt - head -5 /tmp/ours-rp-arm64-rpki-help.txt || true + docker run --rm --platform "$RPKI_PLATFORM" "$RPKI_IMAGE" /opt/ours-rp/bin/rpki --help >"$help_path" + head -5 "$help_path" || true + rm -f "$help_path" } verify_image_platform() { diff --git a/deploy/arm64-installer/self-check.sh b/deploy/arm64-installer/self-check.sh index ada864f..fee8468 100755 --- a/deploy/arm64-installer/self-check.sh +++ b/deploy/arm64-installer/self-check.sh @@ -22,6 +22,7 @@ while [[ $# -gt 0 ]]; do done load_env +assert_arch_compatibility require_cmd docker require_cmd jq docker compose version >/dev/null diff --git a/deploy/arm64-installer/start.sh b/deploy/arm64-installer/start.sh index 4bde6b3..4466d51 100755 --- a/deploy/arm64-installer/start.sh +++ b/deploy/arm64-installer/start.sh @@ -37,6 +37,7 @@ while [[ $# -gt 0 ]]; do done load_env +assert_arch_compatibility create_data_dirs timeout_secs="${TIMEOUT_SECS:-$FIRST_RUN_WAIT_TIMEOUT_SECS}" before_latest="$(latest_run_dir || true)" diff --git a/deploy/arm64-installer/status.sh b/deploy/arm64-installer/status.sh index 890e822..2602c89 100755 --- a/deploy/arm64-installer/status.sh +++ b/deploy/arm64-installer/status.sh @@ -22,8 +22,16 @@ while [[ $# -gt 0 ]]; do done load_env +host_arch="$(detect_host_arch)" +mode="$(arch_mode)" echo "installer_root=$INSTALLER_ROOT" +echo "manifest_file=$MANIFEST_FILE" echo "host_data_dir=$HOST_DATA_DIR" +echo "host_arch=$host_arch" +echo "package_arch=$PACKAGE_ARCH" +echo "package_platform=$PACKAGE_PLATFORM" +echo "allow_cross_arch=$ALLOW_CROSS_ARCH" +echo "arch_mode=$mode" echo "image=$RPKI_IMAGE" echo "platform=$RPKI_PLATFORM" echo "rirs=${RIRS:-}" diff --git a/deploy/arm64-installer/upgrade.sh b/deploy/arm64-installer/upgrade.sh index b820105..d55af68 100755 --- a/deploy/arm64-installer/upgrade.sh +++ b/deploy/arm64-installer/upgrade.sh @@ -95,6 +95,7 @@ if [[ -n "$REUSE_ENV_FROM" ]]; then fi load_env +assert_arch_compatibility create_data_dirs install_docker_if_missing load_installer_images diff --git a/docker/ours-rp-runtime.Dockerfile b/docker/ours-rp-runtime.Dockerfile index 1e15d42..dbc0107 100644 --- a/docker/ours-rp-runtime.Dockerfile +++ b/docker/ours-rp-runtime.Dockerfile @@ -1,8 +1,12 @@ -ARG BUILDER_IMAGE=ours-rp-base-rust-amd64:1-bookworm -ARG RUNTIME_IMAGE=ours-rp-base-debian-arm64:bookworm-slim +ARG BUILDER_IMAGE=rust:1-bookworm +ARG RUNTIME_IMAGE=debian:bookworm-slim FROM --platform=$BUILDPLATFORM ${BUILDER_IMAGE} AS builder +ARG BUILDARCH +ARG TARGETARCH +ARG TARGETPLATFORM + WORKDIR /src RUN apt-get update \ @@ -11,47 +15,89 @@ RUN apt-get update \ ca-certificates \ clang \ cmake \ - g++-aarch64-linux-gnu \ - gcc-aarch64-linux-gnu \ git \ libclang-dev \ - libc6-dev-arm64-cross \ make \ perl \ pkg-config \ python3 \ + && if [ "$TARGETARCH" != "$BUILDARCH" ]; then \ + case "$TARGETARCH" in \ + arm64) \ + apt-get install -y --no-install-recommends \ + g++-aarch64-linux-gnu \ + gcc-aarch64-linux-gnu \ + libc6-dev-arm64-cross \ + ;; \ + amd64) \ + apt-get install -y --no-install-recommends \ + g++-x86-64-linux-gnu \ + gcc-x86-64-linux-gnu \ + libc6-dev-amd64-cross \ + ;; \ + *) \ + echo "unsupported TARGETARCH=$TARGETARCH" >&2; exit 2 \ + ;; \ + esac; \ + fi \ && rm -rf /var/lib/apt/lists/* \ - && rustup target add aarch64-unknown-linux-gnu - -ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ - CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \ - CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \ - AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar \ - CARGO_BUILD_TARGET=aarch64-unknown-linux-gnu \ - PKG_CONFIG_ALLOW_CROSS=1 + && case "$TARGETARCH" in \ + amd64|arm64) ;; \ + *) echo "unsupported TARGETARCH=$TARGETARCH" >&2; exit 2 ;; \ + esac COPY . . RUN --mount=type=cache,target=/usr/local/cargo/registry \ --mount=type=cache,target=/usr/local/cargo/git \ --mount=type=cache,target=/src/target \ - cargo build --release --target aarch64-unknown-linux-gnu \ + set -eux; \ + case "$TARGETARCH" in \ + amd64) target_triple=x86_64-unknown-linux-gnu ;; \ + arm64) target_triple=aarch64-unknown-linux-gnu ;; \ + *) echo "unsupported TARGETARCH=$TARGETARCH" >&2; exit 2 ;; \ + esac; \ + rustup target add "$target_triple"; \ + if [ "$TARGETARCH" != "$BUILDARCH" ]; then \ + case "$TARGETARCH" in \ + arm64) \ + export \ + CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ + CC_aarch64_unknown_linux_gnu=aarch64-linux-gnu-gcc \ + CXX_aarch64_unknown_linux_gnu=aarch64-linux-gnu-g++ \ + AR_aarch64_unknown_linux_gnu=aarch64-linux-gnu-ar \ + PKG_CONFIG_ALLOW_CROSS=1 \ + ;; \ + amd64) \ + export \ + CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc \ + CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc \ + CXX_x86_64_unknown_linux_gnu=x86_64-linux-gnu-g++ \ + AR_x86_64_unknown_linux_gnu=x86_64-linux-gnu-ar \ + PKG_CONFIG_ALLOW_CROSS=1 \ + ;; \ + *) \ + echo "unsupported TARGETARCH=$TARGETARCH" >&2; exit 2 \ + ;; \ + esac; \ + fi; \ + cargo build --release --target "$target_triple" \ --bin rpki \ --bin rpki_daemon \ --bin db_stats \ - --bin rpki_artifact_metrics \ - && mkdir -p /build-out/bin \ - && cp \ - target/aarch64-unknown-linux-gnu/release/rpki \ - target/aarch64-unknown-linux-gnu/release/rpki_daemon \ - target/aarch64-unknown-linux-gnu/release/db_stats \ - target/aarch64-unknown-linux-gnu/release/rpki_artifact_metrics \ + --bin rpki_artifact_metrics; \ + mkdir -p /build-out/bin; \ + cp \ + "target/$target_triple/release/rpki" \ + "target/$target_triple/release/rpki_daemon" \ + "target/$target_triple/release/db_stats" \ + "target/$target_triple/release/rpki_artifact_metrics" \ /build-out/bin/ FROM --platform=$TARGETPLATFORM ${RUNTIME_IMAGE} AS runtime LABEL org.opencontainers.image.title="ours-rp-runtime" \ - org.opencontainers.image.description="Ours RP runtime image for ARM64 Docker Compose deployment" + org.opencontainers.image.description="Ours RP runtime image for multi-arch Docker Compose deployment" RUN apt-get update \ && apt-get install -y --no-install-recommends \ diff --git a/scripts/docker/build_arm64_installer_package.sh b/scripts/docker/build_arm64_installer_package.sh index 14e8499..f8f50f2 100755 --- a/scripts/docker/build_arm64_installer_package.sh +++ b/scripts/docker/build_arm64_installer_package.sh @@ -2,215 +2,4 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" - -IMAGE_TAG="${IMAGE_TAG:-ours-rp-runtime-arm64:dev}" -IMAGE_TAR="${IMAGE_TAR:-}" -PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-prom/prometheus:v2.55.1}" -PROMETHEUS_IMAGE_TAR="${PROMETHEUS_IMAGE_TAR:-}" -GRAFANA_IMAGE="${GRAFANA_IMAGE:-grafana/grafana:11.3.1}" -GRAFANA_IMAGE_TAR="${GRAFANA_IMAGE_TAR:-}" -OUT_DIR="${OUT_DIR:-$REPO_ROOT/target/arm64-installer}" -PACKAGE_PREFIX="${PACKAGE_PREFIX:-ours-rp-arm64-installer}" -TEMPLATE_DIR="${TEMPLATE_DIR:-$REPO_ROOT/deploy/arm64-installer}" -MONITOR_PLATFORM="${MONITOR_PLATFORM:-linux/arm64}" - -usage() { - cat <<'USAGE' -Usage: - scripts/docker/build_arm64_installer_package.sh [options] - -Options: - --image Runtime image tag recorded in package manifest. - --image-tar Existing docker save tar/tar.gz to include. - --prometheus-image - Prometheus image tag to record and package. - --prometheus-image-tar - Existing Prometheus docker save tar/tar.gz to include. - --grafana-image - Grafana image tag to record and package. - --grafana-image-tar - Existing Grafana docker save tar/tar.gz to include. - --out-dir Output directory. - --prefix Package directory/tar prefix. - -h, --help Show help. - -If --image-tar is omitted, the script uses the newest -target/arm64-docker/*.tar.gz file. -USAGE -} - -while [[ $# -gt 0 ]]; do - case "$1" in - --image) - IMAGE_TAG="$2" - shift 2 - ;; - --image-tar) - IMAGE_TAR="$2" - shift 2 - ;; - --prometheus-image) - PROMETHEUS_IMAGE="$2" - shift 2 - ;; - --prometheus-image-tar) - PROMETHEUS_IMAGE_TAR="$2" - shift 2 - ;; - --grafana-image) - GRAFANA_IMAGE="$2" - shift 2 - ;; - --grafana-image-tar) - GRAFANA_IMAGE_TAR="$2" - shift 2 - ;; - --out-dir) - OUT_DIR="$2" - shift 2 - ;; - --prefix) - PACKAGE_PREFIX="$2" - shift 2 - ;; - -h|--help) - usage - exit 0 - ;; - *) - echo "unknown option: $1" >&2 - usage >&2 - exit 2 - ;; - esac -done - -[[ -d "$TEMPLATE_DIR" ]] || { - echo "missing template dir: $TEMPLATE_DIR" >&2 - exit 2 -} - -if [[ -z "$IMAGE_TAR" ]]; then - IMAGE_TAR="$(find "$REPO_ROOT/target/arm64-docker" -maxdepth 1 -type f \( -name '*.tar.gz' -o -name '*.tar' \) -printf '%T@ %p\n' 2>/dev/null | sort -nr | awk 'NR==1 {print $2}')" -fi - -[[ -n "$IMAGE_TAR" && -f "$IMAGE_TAR" ]] || { - cat >&2 <&2 - exit 2 - } - printf '%s\n' "$existing_tar" - return 0 - fi - if ! docker image inspect "$image" >/dev/null 2>&1; then - cat >&2 </dev/null || echo unknown)" - if [[ "$actual_platform" != "$MONITOR_PLATFORM" ]]; then - cat >&2 <&2 - docker save "$image" | gzip -c > "$tar_path" - printf '%s\n' "$tar_path" -} - -mkdir -p "$OUT_DIR" -commit="$(git -C "$REPO_ROOT" rev-parse --short HEAD 2>/dev/null || echo unknown)" -timestamp="$(date -u +%Y%m%dT%H%M%SZ)" -package_name="${PACKAGE_PREFIX}-${timestamp}-${commit}" -stage="$OUT_DIR/$package_name" -tar_path="$OUT_DIR/$package_name.tar.gz" - -rm -rf "$stage" -rsync -a --delete "$TEMPLATE_DIR"/ "$stage"/ -mkdir -p "$stage/images" -cp "$IMAGE_TAR" "$stage/images/" - -monitor_image_stage="$OUT_DIR/.monitor-images-$timestamp" -rm -rf "$monitor_image_stage" -mkdir -p "$monitor_image_stage" -prometheus_tar="$(save_image_if_needed "$PROMETHEUS_IMAGE" "$PROMETHEUS_IMAGE_TAR" "$monitor_image_stage" "prometheus")" -grafana_tar="$(save_image_if_needed "$GRAFANA_IMAGE" "$GRAFANA_IMAGE_TAR" "$monitor_image_stage" "grafana")" -cp "$prometheus_tar" "$stage/images/" -cp "$grafana_tar" "$stage/images/" - -if [[ -f "$stage/.env.example" ]]; then - tmp_env="$stage/.env.example.tmp" - awk -v image="$IMAGE_TAG" -v prometheus="$PROMETHEUS_IMAGE" -v grafana="$GRAFANA_IMAGE" -v monitor_platform="$MONITOR_PLATFORM" ' - BEGIN { done=0 } - /^RPKI_IMAGE=/ { print "RPKI_IMAGE=" image; done=1; next } - /^PROMETHEUS_IMAGE=/ { print "PROMETHEUS_IMAGE=" prometheus; next } - /^GRAFANA_IMAGE=/ { print "GRAFANA_IMAGE=" grafana; next } - /^MONITOR_PLATFORM=/ { print "MONITOR_PLATFORM=" monitor_platform; next } - { print } - END { if (!done) print "RPKI_IMAGE=" image } - ' "$stage/.env.example" > "$tmp_env" - mv "$tmp_env" "$stage/.env.example" -fi - -cat > "$stage/PACKAGE-MANIFEST.env" </dev/null | wc -l | tr -d ' ') -image_tag=$IMAGE_TAG -image_tar=$(basename "$IMAGE_TAR") -image_tar_size_bytes=$(wc -c < "$IMAGE_TAR") -prometheus_image=$PROMETHEUS_IMAGE -prometheus_image_tar=$(basename "$prometheus_tar") -prometheus_image_tar_size_bytes=$(wc -c < "$prometheus_tar") -grafana_image=$GRAFANA_IMAGE -grafana_image_tar=$(basename "$grafana_tar") -grafana_image_tar_size_bytes=$(wc -c < "$grafana_tar") -target_platform=linux/arm64 -monitor_platform=$MONITOR_PLATFORM -EOF - -chmod +x "$stage"/*.sh "$stage/scripts"/*.sh -tar -C "$OUT_DIR" -czf "$tar_path" "$package_name" -rm -rf "$monitor_image_stage" - -{ - echo "package=$tar_path" - echo "package_dir=$stage" - echo "package_size_bytes=$(wc -c < "$tar_path")" - echo "manifest=$stage/PACKAGE-MANIFEST.env" -} > "$OUT_DIR/$package_name.summary.env" - -echo "package built: $tar_path" +exec "$SCRIPT_DIR/build_docker_installer_package.sh" --arch arm64 "$@" diff --git a/scripts/docker/build_arm64_runtime_image.sh b/scripts/docker/build_arm64_runtime_image.sh index c955651..fee513b 100755 --- a/scripts/docker/build_arm64_runtime_image.sh +++ b/scripts/docker/build_arm64_runtime_image.sh @@ -2,184 +2,4 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" - -IMAGE_TAG="${IMAGE_TAG:-ours-rp-runtime-arm64:dev}" -BUILDER_IMAGE="${BUILDER_IMAGE:-ours-rp-base-rust-amd64:1-bookworm}" -RUNTIME_IMAGE="${RUNTIME_IMAGE:-ours-rp-base-debian-arm64:bookworm-slim}" -OUT_DIR="${OUT_DIR:-$REPO_ROOT/target/arm64-docker}" -DOCKERFILE="${DOCKERFILE:-$REPO_ROOT/docker/ours-rp-runtime.Dockerfile}" -BUILDER_NAME="${BUILDER_NAME:-default}" -INSTALL_BINFMT="${INSTALL_BINFMT:-1}" -SAVE_IMAGE="${SAVE_IMAGE:-1}" -LOAD_IMAGE="${LOAD_IMAGE:-1}" - -usage() { - cat <<'USAGE' -Usage: - scripts/docker/build_arm64_runtime_image.sh [options] - -Options: - --image Docker image tag (default: ours-rp-runtime-arm64:dev) - --out-dir Directory for docker save tar.gz (default: target/arm64-docker) - --dockerfile Dockerfile path - --builder buildx builder name - --no-binfmt Do not install binfmt/qemu - --no-save Build image but do not docker save it - --no-load Use buildx output tar instead of --load - -h, --help Show this help -USAGE -} - -while [[ $# -gt 0 ]]; do - case "$1" in - --image) - IMAGE_TAG="$2" - shift 2 - ;; - --out-dir) - OUT_DIR="$2" - shift 2 - ;; - --dockerfile) - DOCKERFILE="$2" - shift 2 - ;; - --builder) - BUILDER_NAME="$2" - shift 2 - ;; - --no-binfmt) - INSTALL_BINFMT=0 - shift - ;; - --no-save) - SAVE_IMAGE=0 - shift - ;; - --no-load) - LOAD_IMAGE=0 - shift - ;; - -h|--help) - usage - exit 0 - ;; - *) - echo "unknown option: $1" >&2 - usage >&2 - exit 2 - ;; - esac -done - -require_command() { - command -v "$1" >/dev/null 2>&1 || { - echo "missing required command: $1" >&2 - exit 2 - } -} - -safe_tag_name() { - printf '%s' "$1" | tr '/:' '--' -} - -require_command docker -mkdir -p "$OUT_DIR" - -require_docker_image() { - local image="$1" - if ! docker image inspect "$image" >/dev/null 2>&1; then - cat >&2 </dev/null 2>&1; then - docker buildx create --name "$BUILDER_NAME" --driver docker-container --use >/dev/null -else - docker buildx use "$BUILDER_NAME" >/dev/null -fi -docker buildx inspect --bootstrap >/dev/null - -metadata_path="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").build-metadata.json" -tar_path="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").tar.gz" -build_log="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").build.log" - -echo "building linux/arm64 image: $IMAGE_TAG" -echo "repo: $REPO_ROOT" -echo "dockerfile: $DOCKERFILE" -echo "builder_image: $BUILDER_IMAGE" -echo "runtime_image: $RUNTIME_IMAGE" -start_epoch="$(date +%s)" - -if [[ "$LOAD_IMAGE" == "1" ]]; then - docker buildx build \ - --platform linux/arm64 \ - --builder "$BUILDER_NAME" \ - --load \ - --build-arg BUILDKIT_INLINE_CACHE=1 \ - --build-arg "BUILDER_IMAGE=$BUILDER_IMAGE" \ - --build-arg "RUNTIME_IMAGE=$RUNTIME_IMAGE" \ - --metadata-file "$metadata_path" \ - -t "$IMAGE_TAG" \ - -f "$DOCKERFILE" \ - "$REPO_ROOT" 2>&1 | tee "$build_log" -else - raw_tar_path="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").tar" - docker buildx build \ - --platform linux/arm64 \ - --builder "$BUILDER_NAME" \ - --output "type=docker,dest=$raw_tar_path" \ - --build-arg BUILDKIT_INLINE_CACHE=1 \ - --build-arg "BUILDER_IMAGE=$BUILDER_IMAGE" \ - --build-arg "RUNTIME_IMAGE=$RUNTIME_IMAGE" \ - --metadata-file "$metadata_path" \ - -t "$IMAGE_TAG" \ - -f "$DOCKERFILE" \ - "$REPO_ROOT" 2>&1 | tee "$build_log" - gzip -f "$raw_tar_path" - tar_path="${raw_tar_path}.gz" -fi - -elapsed_secs=$(( $(date +%s) - start_epoch )) - -if [[ "$SAVE_IMAGE" == "1" && "$LOAD_IMAGE" == "1" ]]; then - echo "saving image to $tar_path" - docker save "$IMAGE_TAG" | gzip -c > "$tar_path" -fi - -{ - echo "image=$IMAGE_TAG" - echo "platform=linux/arm64" - echo "builder_image=$BUILDER_IMAGE" - echo "runtime_image=$RUNTIME_IMAGE" - echo "elapsed_secs=$elapsed_secs" - echo "metadata=$metadata_path" - echo "tar=$tar_path" - echo "tar_size_bytes=$(wc -c < "$tar_path" 2>/dev/null || echo 0)" - echo "git_commit=$(git -C "$REPO_ROOT" rev-parse --short HEAD 2>/dev/null || echo unknown)" - echo "git_status_count=$(git -C "$REPO_ROOT" status --short 2>/dev/null | wc -l | tr -d ' ')" - echo "built_at_utc=$(date -u +%Y-%m-%dT%H:%M:%SZ)" -} > "$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").build-summary.env" - -echo "build complete: elapsed=${elapsed_secs}s tar=$tar_path" +exec "$SCRIPT_DIR/build_docker_runtime_image.sh" --arch arm64 "$@" diff --git a/scripts/docker/build_docker_installer_package.sh b/scripts/docker/build_docker_installer_package.sh new file mode 100755 index 0000000..c06c2bd --- /dev/null +++ b/scripts/docker/build_docker_installer_package.sh @@ -0,0 +1,367 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +TARGET_ARCH="${TARGET_ARCH:-}" +IMAGE_TAG="${IMAGE_TAG:-}" +IMAGE_TAR="${IMAGE_TAR:-}" +PROMETHEUS_IMAGE="${PROMETHEUS_IMAGE:-prom/prometheus:v2.55.1}" +PROMETHEUS_IMAGE_TAR="${PROMETHEUS_IMAGE_TAR:-}" +GRAFANA_IMAGE="${GRAFANA_IMAGE:-grafana/grafana:11.3.1}" +GRAFANA_IMAGE_TAR="${GRAFANA_IMAGE_TAR:-}" +OUT_DIR="${OUT_DIR:-}" +PACKAGE_PREFIX="${PACKAGE_PREFIX:-}" +TEMPLATE_DIR="${TEMPLATE_DIR:-$REPO_ROOT/deploy/arm64-installer}" + +usage() { + cat <<'USAGE' +Usage: + scripts/docker/build_docker_installer_package.sh --arch amd64|arm64 [options] + +Options: + --arch Target architecture: amd64|arm64 + --image Runtime image tag recorded in package manifest. + --image-tar Existing docker save tar/tar.gz to include. + --prometheus-image + Prometheus image tag to record and package. + --prometheus-image-tar + Existing Prometheus docker save tar/tar.gz to include. + --grafana-image + Grafana image tag to record and package. + --grafana-image-tar + Existing Grafana docker save tar/tar.gz to include. + --out-dir Output directory. + --prefix Package directory/tar prefix. + --template-dir + Package template directory. + -h, --help Show help. + +If --image-tar is omitted, the script uses the newest +target/-docker/*.tar.gz file. +USAGE +} + +normalize_arch() { + case "$1" in + amd64|x86_64) + printf 'amd64\n' + ;; + arm64|aarch64) + printf 'arm64\n' + ;; + *) + return 1 + ;; + esac +} + +platform_for_arch() { + printf 'linux/%s\n' "$1" +} + +safe_tag_name() { + printf '%s' "$1" | tr '/:' '--' +} + +default_runtime_tag() { + printf 'ours-rp-runtime-%s:dev\n' "$1" +} + +default_package_prefix() { + printf 'ours-rp-docker-installer-%s\n' "$1" +} + +default_host_data_dir() { + printf '/var/lib/ours-rp-%s-installer\n' "$1" +} + +default_compose_project() { + printf 'ours-rp-%s-installer\n' "$1" +} + +default_metrics_instance() { + printf '%s-installer\n' "$1" +} + +replace_or_append_env() { + local env_path="$1" + local key="$2" + local value="$3" + local tmp_env + tmp_env="${env_path}.tmp" + awk -v key="$key" -v value="$value" ' + BEGIN { done=0 } + $0 ~ "^" key "=" { print key "=" value; done=1; next } + { print } + END { if (!done) print key "=" value } + ' "$env_path" > "$tmp_env" + mv "$tmp_env" "$env_path" +} + +replace_text_in_file() { + local file_path="$1" + local old_text="$2" + local new_text="$3" + OLD_TEXT="$old_text" NEW_TEXT="$new_text" perl -0pi -e 's/\Q$ENV{OLD_TEXT}\E/$ENV{NEW_TEXT}/g' "$file_path" +} + +ensure_image_for_platform() { + local image="$1" + local expected_platform="$2" + local role="$3" + local actual_platform + if ! docker image inspect "$image" >/dev/null 2>&1; then + echo "pulling $role image for $expected_platform: $image" >&2 + docker pull --platform "$expected_platform" "$image" >&2 + fi + actual_platform="$(docker image inspect --format '{{.Os}}/{{.Architecture}}' "$image" 2>/dev/null || echo unknown)" + if [[ "$actual_platform" != "$expected_platform" ]]; then + echo "re-pulling $role image for $expected_platform: $image (current=$actual_platform)" >&2 + docker pull --platform "$expected_platform" "$image" >&2 + actual_platform="$(docker image inspect --format '{{.Os}}/{{.Architecture}}' "$image" 2>/dev/null || echo unknown)" + fi + [[ "$actual_platform" == "$expected_platform" ]] || { + cat >&2 <&2 + exit 2 + } + printf '%s\n' "$existing_tar" + return 0 + fi + ensure_image_for_platform "$image" "$expected_platform" "$role" + local tar_path="$out_dir/$(safe_tag_name "$image")-${TARGET_ARCH}.tar.gz" + echo "saving $role image to $tar_path" >&2 + docker save "$image" | gzip -c > "$tar_path" + printf '%s\n' "$tar_path" +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --arch) + TARGET_ARCH="$(normalize_arch "$2")" || { + echo "unsupported target architecture: $2" >&2 + exit 2 + } + shift 2 + ;; + --image) + IMAGE_TAG="$2" + shift 2 + ;; + --image-tar) + IMAGE_TAR="$2" + shift 2 + ;; + --prometheus-image) + PROMETHEUS_IMAGE="$2" + shift 2 + ;; + --prometheus-image-tar) + PROMETHEUS_IMAGE_TAR="$2" + shift 2 + ;; + --grafana-image) + GRAFANA_IMAGE="$2" + shift 2 + ;; + --grafana-image-tar) + GRAFANA_IMAGE_TAR="$2" + shift 2 + ;; + --out-dir) + OUT_DIR="$2" + shift 2 + ;; + --prefix) + PACKAGE_PREFIX="$2" + shift 2 + ;; + --template-dir) + TEMPLATE_DIR="$2" + shift 2 + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +[[ -n "$TARGET_ARCH" ]] || { + echo "--arch is required" >&2 + usage >&2 + exit 2 +} + +[[ -d "$TEMPLATE_DIR" ]] || { + echo "missing template dir: $TEMPLATE_DIR" >&2 + exit 2 +} + +if [[ -z "$IMAGE_TAG" ]]; then + IMAGE_TAG="$(default_runtime_tag "$TARGET_ARCH")" +fi +if [[ -z "$OUT_DIR" ]]; then + OUT_DIR="$REPO_ROOT/target/${TARGET_ARCH}-installer" +fi +if [[ -z "$PACKAGE_PREFIX" ]]; then + PACKAGE_PREFIX="$(default_package_prefix "$TARGET_ARCH")" +fi + +TARGET_PLATFORM="$(platform_for_arch "$TARGET_ARCH")" +HOST_DATA_DIR_DEFAULT="$(default_host_data_dir "$TARGET_ARCH")" +COMPOSE_PROJECT_DEFAULT="$(default_compose_project "$TARGET_ARCH")" +METRICS_INSTANCE_DEFAULT="$(default_metrics_instance "$TARGET_ARCH")" + +if [[ -z "$IMAGE_TAR" ]]; then + IMAGE_TAR="$(find "$REPO_ROOT/target/${TARGET_ARCH}-docker" -maxdepth 1 -type f \( -name '*.tar.gz' -o -name '*.tar' \) -printf '%T@ %p\n' 2>/dev/null | sort -nr | awk 'NR==1 {print $2}')" +fi + +[[ -n "$IMAGE_TAR" && -f "$IMAGE_TAR" ]] || { + cat >&2 </dev/null || echo unknown)" +timestamp="$(date -u +%Y%m%dT%H%M%SZ)" +package_name="${PACKAGE_PREFIX}-${timestamp}-${commit}" +stage="$OUT_DIR/$package_name" +tar_path="$OUT_DIR/$package_name.tar.gz" + +rm -rf "$stage" +rsync -a --delete "$TEMPLATE_DIR"/ "$stage"/ +mkdir -p "$stage/images" +cp "$IMAGE_TAR" "$stage/images/" + +monitor_image_stage="$OUT_DIR/.monitor-images-$TARGET_ARCH-$timestamp" +rm -rf "$monitor_image_stage" +mkdir -p "$monitor_image_stage" +prometheus_tar="$(save_image_if_needed "$PROMETHEUS_IMAGE" "$PROMETHEUS_IMAGE_TAR" "$monitor_image_stage" "prometheus" "$TARGET_PLATFORM")" +grafana_tar="$(save_image_if_needed "$GRAFANA_IMAGE" "$GRAFANA_IMAGE_TAR" "$monitor_image_stage" "grafana" "$TARGET_PLATFORM")" +cp "$prometheus_tar" "$stage/images/" +cp "$grafana_tar" "$stage/images/" + +if [[ -f "$stage/.env.example" ]]; then + replace_or_append_env "$stage/.env.example" "PACKAGE_ARCH" "$TARGET_ARCH" + replace_or_append_env "$stage/.env.example" "PACKAGE_PLATFORM" "$TARGET_PLATFORM" + replace_or_append_env "$stage/.env.example" "COMPOSE_PROJECT_NAME" "$COMPOSE_PROJECT_DEFAULT" + replace_or_append_env "$stage/.env.example" "RPKI_IMAGE" "$IMAGE_TAG" + replace_or_append_env "$stage/.env.example" "RPKI_PLATFORM" "$TARGET_PLATFORM" + replace_or_append_env "$stage/.env.example" "HOST_DATA_DIR" "$HOST_DATA_DIR_DEFAULT" + replace_or_append_env "$stage/.env.example" "ALLOW_CROSS_ARCH" "0" + replace_or_append_env "$stage/.env.example" "METRICS_INSTANCE" "$METRICS_INSTANCE_DEFAULT" + replace_or_append_env "$stage/.env.example" "MONITOR_PLATFORM" "$TARGET_PLATFORM" + replace_or_append_env "$stage/.env.example" "PROMETHEUS_IMAGE" "$PROMETHEUS_IMAGE" + replace_or_append_env "$stage/.env.example" "GRAFANA_IMAGE" "$GRAFANA_IMAGE" +fi + +replace_text_in_file "$stage/docs/README.zh-CN.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" +replace_text_in_file "$stage/docs/README.en.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" +replace_text_in_file "$stage/docs/operations.zh-CN.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" +replace_text_in_file "$stage/docs/operations.en.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" +replace_text_in_file "$stage/docs/troubleshooting.zh-CN.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" +replace_text_in_file "$stage/docs/troubleshooting.en.md" "__PACKAGE_ARCH__" "$TARGET_ARCH" +replace_text_in_file "$stage/docs/README.zh-CN.md" "__PACKAGE_PLATFORM__" "$TARGET_PLATFORM" +replace_text_in_file "$stage/docs/README.en.md" "__PACKAGE_PLATFORM__" "$TARGET_PLATFORM" +replace_text_in_file "$stage/docs/troubleshooting.zh-CN.md" "__PACKAGE_PLATFORM__" "$TARGET_PLATFORM" +replace_text_in_file "$stage/docs/troubleshooting.en.md" "__PACKAGE_PLATFORM__" "$TARGET_PLATFORM" +replace_text_in_file "$stage/docs/README.zh-CN.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" +replace_text_in_file "$stage/docs/README.en.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" +replace_text_in_file "$stage/docs/operations.zh-CN.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" +replace_text_in_file "$stage/docs/operations.en.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" +replace_text_in_file "$stage/docs/troubleshooting.zh-CN.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" +replace_text_in_file "$stage/docs/troubleshooting.en.md" "__HOST_DATA_DIR__" "$HOST_DATA_DIR_DEFAULT" + +runtime_tar_sha256="$(sha256sum "$IMAGE_TAR" | awk '{print $1}')" +prometheus_tar_sha256="$(sha256sum "$prometheus_tar" | awk '{print $1}')" +grafana_tar_sha256="$(sha256sum "$grafana_tar" | awk '{print $1}')" + +cat > "$stage/PACKAGE-MANIFEST.env" </dev/null | wc -l | tr -d ' ') +PACKAGE_ARCH=$TARGET_ARCH +PACKAGE_PLATFORM=$TARGET_PLATFORM +package_arch=$TARGET_ARCH +package_platform=$TARGET_PLATFORM +cross_arch_default=deny +cross_arch_enable_var=ALLOW_CROSS_ARCH +image_tag=$IMAGE_TAG +image_tar=$(basename "$IMAGE_TAR") +image_tar_size_bytes=$(wc -c < "$IMAGE_TAR") +image_tar_sha256=$runtime_tar_sha256 +prometheus_image=$PROMETHEUS_IMAGE +prometheus_image_tar=$(basename "$prometheus_tar") +prometheus_image_tar_size_bytes=$(wc -c < "$prometheus_tar") +prometheus_image_tar_sha256=$prometheus_tar_sha256 +grafana_image=$GRAFANA_IMAGE +grafana_image_tar=$(basename "$grafana_tar") +grafana_image_tar_size_bytes=$(wc -c < "$grafana_tar") +grafana_image_tar_sha256=$grafana_tar_sha256 +target_platform=$TARGET_PLATFORM +monitor_platform=$TARGET_PLATFORM +default_host_data_dir=$HOST_DATA_DIR_DEFAULT +default_compose_project_name=$COMPOSE_PROJECT_DEFAULT +default_metrics_instance=$METRICS_INSTANCE_DEFAULT +EOF + +cat > "$stage/PACKAGE-SUMMARY.txt" < "$OUT_DIR/$package_name.summary.env" + +echo "package built: $tar_path" diff --git a/scripts/docker/build_docker_runtime_image.sh b/scripts/docker/build_docker_runtime_image.sh new file mode 100755 index 0000000..80f6129 --- /dev/null +++ b/scripts/docker/build_docker_runtime_image.sh @@ -0,0 +1,238 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" + +TARGET_ARCH="${TARGET_ARCH:-}" +IMAGE_TAG="${IMAGE_TAG:-}" +BUILDER_IMAGE="${BUILDER_IMAGE:-rust:1-bookworm}" +RUNTIME_IMAGE="${RUNTIME_IMAGE:-debian:bookworm-slim}" +OUT_DIR="${OUT_DIR:-}" +DOCKERFILE="${DOCKERFILE:-$REPO_ROOT/docker/ours-rp-runtime.Dockerfile}" +BUILDER_NAME="${BUILDER_NAME:-default}" +INSTALL_BINFMT="${INSTALL_BINFMT:-1}" +SAVE_IMAGE="${SAVE_IMAGE:-1}" +LOAD_IMAGE="${LOAD_IMAGE:-1}" + +usage() { + cat <<'USAGE' +Usage: + scripts/docker/build_docker_runtime_image.sh --arch amd64|arm64 [options] + +Options: + --arch Target architecture: amd64|arm64 + --image Docker image tag (default: ours-rp-runtime-:dev) + --out-dir Directory for docker save tar.gz (default: target/-docker) + --dockerfile Dockerfile path + --builder buildx builder name + --builder-image + Builder base image (default: rust:1-bookworm) + --runtime-image + Runtime base image (default: debian:bookworm-slim) + --no-binfmt Do not install binfmt/qemu for cross-architecture builds + --no-save Build image but do not docker save it + --no-load Use buildx output tar instead of --load + -h, --help Show this help +USAGE +} + +normalize_arch() { + case "$1" in + amd64|x86_64) + printf 'amd64\n' + ;; + arm64|aarch64) + printf 'arm64\n' + ;; + *) + return 1 + ;; + esac +} + +safe_tag_name() { + printf '%s' "$1" | tr '/:' '--' +} + +platform_for_arch() { + printf 'linux/%s\n' "$1" +} + +default_image_tag() { + printf 'ours-rp-runtime-%s:dev\n' "$1" +} + +require_command() { + command -v "$1" >/dev/null 2>&1 || { + echo "missing required command: $1" >&2 + exit 2 + } +} + +host_arch() { + local raw_arch + raw_arch="$(uname -m)" + normalize_arch "$raw_arch" || { + echo "unsupported host architecture: $raw_arch" >&2 + exit 2 + } +} + +while [[ $# -gt 0 ]]; do + case "$1" in + --arch) + TARGET_ARCH="$(normalize_arch "$2")" || { + echo "unsupported target architecture: $2" >&2 + exit 2 + } + shift 2 + ;; + --image) + IMAGE_TAG="$2" + shift 2 + ;; + --out-dir) + OUT_DIR="$2" + shift 2 + ;; + --dockerfile) + DOCKERFILE="$2" + shift 2 + ;; + --builder) + BUILDER_NAME="$2" + shift 2 + ;; + --builder-image) + BUILDER_IMAGE="$2" + shift 2 + ;; + --runtime-image) + RUNTIME_IMAGE="$2" + shift 2 + ;; + --no-binfmt) + INSTALL_BINFMT=0 + shift + ;; + --no-save) + SAVE_IMAGE=0 + shift + ;; + --no-load) + LOAD_IMAGE=0 + shift + ;; + -h|--help) + usage + exit 0 + ;; + *) + echo "unknown option: $1" >&2 + usage >&2 + exit 2 + ;; + esac +done + +[[ -n "$TARGET_ARCH" ]] || { + echo "--arch is required" >&2 + usage >&2 + exit 2 +} + +if [[ -z "$IMAGE_TAG" ]]; then + IMAGE_TAG="$(default_image_tag "$TARGET_ARCH")" +fi +if [[ -z "$OUT_DIR" ]]; then + OUT_DIR="$REPO_ROOT/target/${TARGET_ARCH}-docker" +fi + +require_command docker +mkdir -p "$OUT_DIR" + +target_platform="$(platform_for_arch "$TARGET_ARCH")" +local_host_arch="$(host_arch)" + +if [[ "$INSTALL_BINFMT" == "1" && "$local_host_arch" != "$TARGET_ARCH" ]]; then + echo "installing binfmt/qemu for $TARGET_ARCH" + docker run --rm --privileged tonistiigi/binfmt --install "$TARGET_ARCH" +fi + +if [[ "$BUILDER_NAME" != "default" ]] && ! docker buildx inspect "$BUILDER_NAME" >/dev/null 2>&1; then + docker buildx create --name "$BUILDER_NAME" --driver docker-container --use >/dev/null +else + docker buildx use "$BUILDER_NAME" >/dev/null +fi +docker buildx inspect --bootstrap >/dev/null + +metadata_path="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").build-metadata.json" +tar_path="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").tar.gz" +build_log="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").build.log" + +echo "building $target_platform image: $IMAGE_TAG" +echo "repo: $REPO_ROOT" +echo "dockerfile: $DOCKERFILE" +echo "builder_image: $BUILDER_IMAGE" +echo "runtime_image: $RUNTIME_IMAGE" +start_epoch="$(date +%s)" + +if [[ "$LOAD_IMAGE" == "1" ]]; then + docker buildx build \ + --platform "$target_platform" \ + --builder "$BUILDER_NAME" \ + --load \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + --build-arg "BUILDER_IMAGE=$BUILDER_IMAGE" \ + --build-arg "RUNTIME_IMAGE=$RUNTIME_IMAGE" \ + --metadata-file "$metadata_path" \ + -t "$IMAGE_TAG" \ + -f "$DOCKERFILE" \ + "$REPO_ROOT" 2>&1 | tee "$build_log" +else + raw_tar_path="$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").tar" + docker buildx build \ + --platform "$target_platform" \ + --builder "$BUILDER_NAME" \ + --output "type=docker,dest=$raw_tar_path" \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + --build-arg "BUILDER_IMAGE=$BUILDER_IMAGE" \ + --build-arg "RUNTIME_IMAGE=$RUNTIME_IMAGE" \ + --metadata-file "$metadata_path" \ + -t "$IMAGE_TAG" \ + -f "$DOCKERFILE" \ + "$REPO_ROOT" 2>&1 | tee "$build_log" + gzip -f "$raw_tar_path" + tar_path="${raw_tar_path}.gz" +fi + +elapsed_secs=$(( $(date +%s) - start_epoch )) + +if [[ "$SAVE_IMAGE" == "1" && "$LOAD_IMAGE" == "1" ]]; then + echo "saving image to $tar_path" + docker save "$IMAGE_TAG" | gzip -c > "$tar_path" +fi + +tar_sha256="" +if [[ -f "$tar_path" ]]; then + tar_sha256="$(sha256sum "$tar_path" | awk '{print $1}')" +fi + +{ + echo "image=$IMAGE_TAG" + echo "platform=$target_platform" + echo "arch=$TARGET_ARCH" + echo "builder_image=$BUILDER_IMAGE" + echo "runtime_image=$RUNTIME_IMAGE" + echo "elapsed_secs=$elapsed_secs" + echo "metadata=$metadata_path" + echo "tar=$tar_path" + echo "tar_size_bytes=$(wc -c < "$tar_path" 2>/dev/null || echo 0)" + echo "tar_sha256=$tar_sha256" + echo "git_commit=$(git -C "$REPO_ROOT" rev-parse --short HEAD 2>/dev/null || echo unknown)" + echo "git_status_count=$(git -C "$REPO_ROOT" status --short 2>/dev/null | wc -l | tr -d ' ')" + echo "built_at_utc=$(date -u +%Y-%m-%dT%H:%M:%SZ)" +} > "$OUT_DIR/$(safe_tag_name "$IMAGE_TAG").build-summary.env" + +echo "build complete: elapsed=${elapsed_secs}s tar=$tar_path"