diff --git a/.gitignore b/.gitignore index af2c547..9675f38 100644 --- a/.gitignore +++ b/.gitignore @@ -18,8 +18,19 @@ build/rootfs-work/ # OS .DS_Store +._* Thumbs.db +# Photos / screenshots — keep documentation images under docs/ instead +*.PNG +*.png +*.JPG +*.jpg +*.JPEG +*.jpeg +*.HEIC +*.heic + # Go update/update-agent cloud-init/cloud-init-parser diff --git a/README.md b/README.md index a225f35..aad944b 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ An immutable, bootable Linux distribution purpose-built for [KubeSolo](https://github.com/portainer/kubesolo) — Portainer's ultra-lightweight single-node Kubernetes. -> **Status:** All 6 phases complete. Boots and runs K8s workloads. Portainer Edge Agent tested and connected. +> **Status:** x86_64 is stable — boots and runs K8s workloads, Portainer Edge Agent tested and connected. ARM64 generic UEFI is the active focus for v0.3.0; ARM64 Raspberry Pi support is paused pending physical hardware testing. ## What is this? @@ -227,13 +227,16 @@ Metrics include: `kubesolo_os_info`, `boot_success`, `boot_counter`, `uptime_sec | Phase | Scope | Status | |-------|-------|--------| -| 1 | PoC: boot Tiny Core + KubeSolo, verify K8s | Complete | +| 1 | PoC: boot Tiny Core + KubeSolo, verify K8s | Complete (x86_64) | | 2 | Cloud-init Go parser, network, hostname | Complete | -| 3 | A/B atomic updates, GRUB, rollback agent | Complete | +| 3 | A/B atomic updates, GRUB, rollback agent | Complete (x86_64) | | 4 | Ed25519 signing, Portainer Edge, SSH extension | Complete | -| 5 | CI/CD, OCI distribution, Prometheus metrics, ARM64 | Complete | -| 6 | Security hardening, AppArmor, ARM64 RPi support | Complete | -| - | Custom kernel build for container runtime fixes | Complete | +| 5 | CI/CD, OCI distribution, Prometheus metrics, ARM64 cross-compile | Complete | +| 6 | Security hardening, AppArmor | Complete | +| - | Custom kernel build for container runtime fixes | Complete (x86_64) | +| 7 | ARM64 generic (mainline kernel, UEFI, virtio) | In progress (v0.3.0) | +| 8 | Update engine v2 (state machine, OCI distribution, channels) | In progress (v0.3.0) | +| - | ARM64 Raspberry Pi (custom kernel, firmware, SD card image) | Paused — needs hardware | ## License diff --git a/build/config/versions.env b/build/config/versions.env index ad4010a..9645e25 100644 --- a/build/config/versions.env +++ b/build/config/versions.env @@ -9,6 +9,9 @@ TINYCORE_ISO=CorePure64-${TINYCORE_VERSION}.iso TINYCORE_ISO_URL=${TINYCORE_MIRROR}/${TINYCORE_VERSION%%.*}.x/${TINYCORE_ARCH}/release/${TINYCORE_ISO} # KubeSolo +# Pinned release tag from https://github.com/portainer/kubesolo/releases. +# Bump here and re-run `make fetch` to pull a new version. +KUBESOLO_VERSION=v1.1.0 KUBESOLO_INSTALL_URL=https://get.kubesolo.io # Build tools (used inside builder container) diff --git a/build/scripts/fetch-components.sh b/build/scripts/fetch-components.sh index b25f90e..aa82757 100755 --- a/build/scripts/fetch-components.sh +++ b/build/scripts/fetch-components.sh @@ -51,8 +51,7 @@ if [ "$FETCH_ARCH" = "arm64" ]; then echo "==> Fetching RPi firmware..." "$SCRIPT_DIR/fetch-rpi-firmware.sh" - # Download ARM64 KubeSolo binary - KUBESOLO_VERSION="${KUBESOLO_VERSION:-v1.1.0}" + # Download ARM64 KubeSolo binary (KUBESOLO_VERSION set from versions.env) KUBESOLO_BIN_ARM64="$CACHE_DIR/kubesolo-arm64" if [ -f "$KUBESOLO_BIN_ARM64" ]; then echo "==> KubeSolo ARM64 binary already cached: $KUBESOLO_BIN_ARM64" @@ -112,7 +111,7 @@ else fi # --- KubeSolo --- -KUBESOLO_VERSION="${KUBESOLO_VERSION:-v1.1.0}" +# KUBESOLO_VERSION sourced from versions.env KUBESOLO_BIN="$CACHE_DIR/kubesolo" if [ -f "$KUBESOLO_BIN" ]; then diff --git a/docs/ci-runners.md b/docs/ci-runners.md new file mode 100644 index 0000000..de25c8a --- /dev/null +++ b/docs/ci-runners.md @@ -0,0 +1,165 @@ +# CI Runners + +KubeSolo OS is built and tested on Gitea Actions runners. This document records the +runners currently in service and how to register a new one if a host is wiped. + +## Active runners + +| Name | Host | Arch | OS | Labels | Notes | +|------|------|------|-----|--------|-------| +| `odroid-arm64` | `odroid.local` | aarch64 | Ubuntu 22.04 LTS | `arm64-linux`, `ubuntu-latest`, `ubuntu-24.04`, `ubuntu-22.04` | Native ARM64 builder; 6 cores, 1.8 GB RAM + 4 GB swap; runs as systemd service `act_runner` | + +## Workflow targeting + +ARM64-specific jobs target the Odroid via the `arm64-linux` label: + +```yaml +jobs: + build-arm64: + runs-on: arm64-linux + steps: + - uses: actions/checkout@v4 + - run: make rootfs-arm64 +``` + +Generic ubuntu jobs that don't care about arch fall through to whichever runner picks +them up first; on the Odroid they run in Docker via the `ubuntu-latest` / +`ubuntu-22.04` / `ubuntu-24.04` labels. + +## Registering a new runner + +### Prerequisites + +- Linux host (Ubuntu / Debian preferred; the install instructions below use Ubuntu + 22.04+ paths). +- Outbound HTTPS to the Gitea instance. +- Root access on the runner host (the runner needs to create loop devices and run + `mkfs.ext4` for disk-image builds). +- A Gitea Actions runner registration token. Get it from: + - **Repo-scoped:** `/settings/actions/runners` → "Create new Runner" + - **Org-scoped (preferred for this project):** `/-/settings/actions/runners` → + "Create new Runner" + - **Site-scoped:** `/-/admin/actions/runners` → "Create new Runner" + +### Step 1 — Add swap if the host has <4 GB RAM + +Kernel builds in later phases need ~2 GB resident; tight hosts will OOM-kill `cc1` +without swap. + +```bash +sudo fallocate -l 4G /swapfile +sudo chmod 600 /swapfile +sudo mkswap /swapfile +sudo swapon /swapfile +echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab +``` + +### Step 2 — Install the gitea-runner binary + +Pinned to a known-good version. Check + for the current stable tag before +bumping. + +```bash +sudo -i +mkdir -p /opt/act_runner && cd /opt/act_runner + +# Bump VERSION to the current stable release as needed +VERSION=1.0.3 +ARCH=$(uname -m | sed 's/aarch64/arm64/; s/x86_64/amd64/') + +curl -fL "https://gitea.com/gitea/runner/releases/download/v${VERSION}/gitea-runner-${VERSION}-linux-${ARCH}" \ + -o act_runner +chmod +x act_runner +./act_runner --version +``` + +> The upstream project was renamed `act_runner` → `gitea-runner` at the v1.0.0 +> release. The release asset filenames use `gitea-runner-*` even though we keep the +> local binary named `act_runner` to match this systemd unit. The CLI surface +> (`register`, `daemon`, `generate-config`) is unchanged. + +### Step 3 — Register against Gitea + +```bash +./act_runner register --no-interactive \ + --instance https://git.oe74.net \ + --token PASTE_TOKEN_HERE \ + --name \ + --labels arm64-linux # adjust label for amd64 hosts +``` + +This creates a `.runner` file with the registration credentials. + +### Step 4 — Generate and tune config + +```bash +./act_runner generate-config > config.yaml +``` + +In `config.yaml`, confirm the `runner.labels:` block includes the labels you want. +The `:host` suffix routes jobs directly to the host (no Docker wrapper) — required +for disk-image builds that need loop devices and `mkfs`. + +Example labels for an arm64 host: + +```yaml +runner: + labels: + - "arm64-linux:host" + - "ubuntu-latest:docker://docker.gitea.com/runner-images:ubuntu-latest" + - "ubuntu-24.04:docker://docker.gitea.com/runner-images:ubuntu-24.04" + - "ubuntu-22.04:docker://docker.gitea.com/runner-images:ubuntu-22.04" +``` + +### Step 5 — Install as a systemd service + +```bash +cat > /etc/systemd/system/act_runner.service << 'EOF' +[Unit] +Description=Gitea Actions runner +After=network-online.target +Wants=network-online.target + +[Service] +ExecStart=/opt/act_runner/act_runner daemon --config /opt/act_runner/config.yaml +WorkingDirectory=/opt/act_runner +User=root +Restart=always +RestartSec=5 + +[Install] +WantedBy=multi-user.target +EOF + +systemctl daemon-reload +systemctl enable --now act_runner +systemctl status act_runner --no-pager +``` + +### Step 6 — Verify in Gitea UI + +Visit the runners page at the scope you registered against. The runner should appear +as `Idle` with the labels you configured. + +## Removing a runner + +On the host: + +```bash +systemctl disable --now act_runner +rm -rf /opt/act_runner /etc/systemd/system/act_runner.service +systemctl daemon-reload +``` + +Then delete the runner entry from the Gitea Actions UI so Gitea stops trying to +schedule against it. + +## Operational notes + +- The runner stores in-progress job working directories under `/tmp/act_runner` by + default. Large disk-image builds may need that path moved to a larger volume — + edit `host.workdir_parent:` in `config.yaml`. +- Logs are visible via `journalctl -u act_runner -f`. +- If a job is interrupted (e.g. host reboot mid-build), the Gitea UI will mark it as + failed/cancelled. Re-run from the Actions UI.