Skip to content

Architecture — Edge / Single Node OpenShift

Reference for the presenter. What the kit builds, how the pieces connect, and how long each part takes.

For why it is built this way — the storage race condition, the partition fix, the CIS approach — read image.builder.pipeline/docs/design.md section 11.


The flow

flowchart TD
    A["<b>Phase 1 — Build ISO</b><br/>image.builder.pipeline<br/><i>generate-iso.sh + build_sno_installer.yml</i>"] --> B["<b>Phase 2 — Boot</b><br/>bare hardware<br/><i>ABI ISO, unattended, ~45 min</i>"]
    B --> C["<b>Day 1 — LVMS</b><br/>install_lvms.yml<br/><i>LVMCluster CR, StorageClass</i>"]
    C --> D["<b>Day 1 — AAP</b><br/>install_aap.yml<br/><i>AnsibleAutomationPlatform CR, ~20 min</i>"]
    D --> E["<b>Day 1 — CNV</b><br/>install_cnv.yml<br/><i>HyperConverged CR, boot sources</i>"]
    E --> F["<b>Day 1 — Compliance</b><br/>install_compliance.yml<br/><i>Compliance Operator + CIS scan</i>"]
    F --> G["<b>Day 1 — Verify</b><br/>prepare_env.yml<br/><i>Build and time a real VM</i>"]
    G --> H["<b>config.yml</b><br/><i>AAP CaC — orgs, creds, templates</i>"]
    H --> I["<b>Demo-ready</b><br/><i>Same playbooks as RHDP</i>"]

LVMS must come before AAP because the AAP operator needs PVCs for its database and Hub file storage. AAP must come before CNV because config.yml configures both, and it needs the AAP gateway to be reachable. config.yml is a separate step, not part of the install sequence — it needs the admin password, which only exists after AAP deploys and the user updates the vault. That ordering constraint is why sales.demos#406 proposes setup_edge.yml as the first five stages only.


Inputs

Question Flag / Variable Example
Node IP (CIDR) --ip 192.168.1.100/24
Gateway --gateway 192.168.1.1
DNS server --dns 192.168.1.1
MAC address --mac 1c:69:7a:0d:50:30
NIC name --interface eno1
Disk device --disk /dev/sda
Hostname --hostname nuc01
Cluster name --cluster-name edge
Base domain --base-domain internal.ames.net
Pull secret --pull-secret ~/pull-secret.json
SSH public key --ssh-key ~/.ssh/id_ed25519.pub

All hardware-specific values are required. The script validates them before touching anything.

Deliberately absent from the interface: OCP version. The kit tracks the latest stable channel by default, which is what a demo environment should run. Override with --ocp-version only when pinning to a specific z-stream matters.


What gets created

Day 0 — baked into the ISO

Resource Purpose
install-config.yaml SNO topology: 1 control plane, 0 workers
agent-config.yaml NIC, disk, static IP, hostname
98-lvms-partition.yaml MachineConfig: partition 5 for LVMS, pinning root at 200 GB
AAP operator Subscription stable-2.7 channel
CNV operator Subscription stable channel
LVMS operator Subscription stable-4.22 channel
Compliance Operator Subscription stable channel

Day 1 — applied by the install playbooks

Resource Playbook Purpose
LVMCluster CR install_lvms.yml Thin-provisioned VolumeGroup from partition 5, StorageClass lvms-vg1
AnsibleAutomationPlatform CR install_aap.yml Gateway, Controller, Hub, EDA, PostgreSQL
HyperConverged CR install_cnv.yml CNV with lvms-vg1 scratch space
ScanSettingBinding install_compliance.yml CIS L1 compliance scan
AAP objects config.yml Orgs, credentials, projects, job templates, inventories, schedules

Storage layout

The ISO's Day 0 MachineConfig creates a partition layout that pins root and gives LVMS the rest of the disk:

Partition  Start sector    End sector     Size    Name
    4         1050624      419430399    199.5 GB   root
    5       419430400      end-of-disk  (rest)     lvms

Why partition 5, not sizing root directly: RHCOS runs ignition-ostree-growfs after Ignition's disk stage, which unconditionally grows root into whatever free space follows it. A partition declared after root stops growpart at that boundary. This is the documented RHCOS mechanism, not a workaround.

LVMS auto-discovers the unformatted, unmounted partition 5 — no deviceSelector is needed. The label (lvms) avoids the reserved names that LVMS filters out.

Configurable: set sno_root_partition_size_gb to adjust the split. Set it to 0 to skip the partition and let root fill the entire disk (no LVMS).


Manual AAP deployment

Until install_aap.yml ships (#395), create the CR manually:

oc apply -f - <<'EOF'
apiVersion: aap.ansible.com/v1alpha1
kind: AnsibleAutomationPlatform
metadata:
  name: aap
  namespace: aap
spec:
  controller:
    disabled: false
  eda:
    disabled: false
  hub:
    disabled: false
    file_storage_storage_class: lvms-vg1
    file_storage_size: 50Gi
    file_storage_access_mode: ReadWriteOnce
  lightspeed:
    disabled: true
EOF

Wait for all components:

oc get aap aap -n aap -w

Extract the admin password:

oc get secret aap-admin-password -n aap \
  -o jsonpath='{.data.password}' | base64 -d && echo

Timing

Measured on a NUC (12 CPU, 64 GB RAM, SATA SSD), 2026-09-09:

Phase Time Notes
ISO generation ~2 min Downloads openshift-install binary
Cluster install ~45 min Unattended; varies with hardware and network
LVMS install ~2 min Operator + LVMCluster CR
AAP deployment ~20 min All five components (gateway, controller, hub, eda, postgres)
CNV install ~4 min Operator + HyperConverged CR + rhel9 boot source import
Compliance Operator ~2 min Operator install only; scan runs in background
config.yml ~3 min AAP objects via CaC
Total to demo-ready ~80 min ~35 min hands-on, ~45 min unattended

DNS

SNO requires two DNS records pointing at the node's IP:

Record Example
api.<cluster>.<domain> api.edge.internal.ames.net
*.apps.<cluster>.<domain> *.apps.edge.internal.ames.net

On a home network, dnsmasq provides both from a single address= line. See the run sheet for the exact setup commands.

Why static IP: DHCP risks assigning a different IP after a reboot, breaking the DNS records and making the cluster unreachable. Static IP is the default.


Hardware minimums

From sno_defaults.yml:

Resource Minimum Recommended
RAM 32 GB 64 GB (leaves room for demo VMs)
Disk 120 GB 500 GB+ (200 GB root + LVMS for VM disks)
CPUs 8 12+

Tested on: Intel NUC, 12 CPUs, 64 GB RAM, 953 GB SATA SSD. Any x86_64 hardware meeting the minimums should work — the ISO generator parameterizes everything hardware-specific.


What does not work yet

  • install_aap.yml is not automated — tracked in #395. Manual CR creation documented above.
  • Operator channel selection — the Day 0 manifests hardcode channels (stable-2.7, stable-4.22). Tracked in image.builder.pipeline#107 and #108.
  • CIS L1 MachineConfigs — the Day 0 CIS manifests are placeholders. Tracked in image.builder.pipeline#110.

Cleanup

Destroyed by teardown Preserved
Demo VMs and their PVCs OpenShift Virtualization
AAP inventory hosts for destroyed VMs AAP itself, all job templates
Terraform state for destroyed VMs Terraform state namespace
LVMS StorageClass
Boot source DataSources
Compliance Operator and scan results