User Tools

Site Tools


zededa:sharing-persist-volume-by-two-apps

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
zededa:sharing-persist-volume-by-two-apps [2026/07/22 15:54] – mczededa:sharing-persist-volume-by-two-apps [2026/07/24 15:26] (current) – mc
Line 1: Line 1:
-====== Sharing a Persistent Volume Between Multiple Apps (ZEDEDA / EVE-OS) ======+====== Sharing a Volume Between Multiple Edge Applications (ZEDEDA / EVE-OS) ======
  
-===== Can a persistent volume be shared by two applications? =====+===== Can a volume be shared by two apps? =====
  
-**Yes.** ZEDEDA/EVE-OS supports attaching a single volume instance to **multiple +**Yes — but the rules differ for read-only vs read-write:**
-application instances** via the **''multiattach''** flag combined with the +
-**''MULTIREAD_SINGLEWRITE''** access mode. Sharing data between edge apps on the same +
-node is one of the documented primary use cases of the storage feature.+
  
-This is a supported, real-world pattern: a single block-storage volume (optionally backed +  * **Read-only sharing** — **any** volume type can be shared read-only by multiple apps. 
-by a shared content tree) attached to multiple app instances on one node with +  * **Read-write sharing** (multiple apps read //and// write the same data) — the volume **must be backed by a CONTAINER content-tree**. A bare block-storage / QCOW2 / RAW / ISO (file-based) volume **cannot** be shared for writing across VMs, because multiple VMs cannot write to a single file.
-''multiattach=True''.+
  
-===== Access modes =====+This is a documented ZEDEDA feature ("Attach Volume Instances to Multiple Edge Applications", **EVE-OS >= 12.7.0**). When done correctly, EVE shares the volume to the guests over **plan 9 (9P)**, which arbitrates concurrent access — so **all attached apps can read AND write** the same data.
  
-The ''accessmode'' field accepts the enum values below. Each is prefixed with +===== The common error (and its real cause) =====
-''VOLUME_INSTANCE_ACCESS_MODE_'' (e.g. the full value for "Read-write" is +
-''VOLUME_INSTANCE_ACCESS_MODE_READWRITE''):+
  
-^ Value (suffix) ^ Meaning ^ +If you attach a **writable, file-based** volume (plain block-storage/QCOW2) to two apps, EVE rejects the second one with:
-| ''READWRITE'' | Single app, read-write (default for a private volume) | +
-| ''READONLY'' | Read-only | +
-| ''MULTIREAD_SINGLEWRITE'' | **Shared** — many readers, one writer | +
-| ''INVALID'' | Unset / invalid |+
  
-To share, use **''MULTIREAD_SINGLEWRITE''** and set **''multiattach = true''**.+  //Multiple app instances (N) are trying to use the same file-based volume <name>//
  
-===== Important constraints =====+This is a **deliberate safeguard**, verified in EVE source (''pkg/pillar/cmd/volumemgr/handlevolumeref.go''). The guard fires when a volume is shared by ≥2 apps **and** it is **not container-based** **and** it is **not read-only**. It is **not** related to the node's storage backend, ZFS, zvols, disk format, or the ''multiattach'' flag — none of those bypass it. The fix is to use a **container content-tree** volume (below), or make the shared volume read-only.
  
-  * **The node's ''/persist'' must be ZFS.** This is the big one. ''multiattach = true'' + ''MULTIREAD_SINGLEWRITE'' on the controller are **necessary but not sufficient** — the edge node must actually back the volume as a **ZFS zvol** (a real block device). See [[#node storage backend zfs vs ext4|Node storage backend]] below. +===== How to set it up (writable shared volume) =====
-  * **Same node only.** A persistent volume instance belongs to a specific edge node. To share it, **every app instance attaching it must run on that same edge node**. To have it on another node you must create a separate volume instance there. +
-  * **One writer, many readers.** ''MULTIREAD_SINGLEWRITE'' means the platform lets multiple apps attach, but it does **not** arbitrate concurrent writers. Two apps mounting the same block device read-write and both writing **will corrupt the filesystem** (ext4/xfs are not cluster-aware). +
-  * **Safe pattern:** one app owns writes; the others mount **read-only** (''readonly = true'' on the drive). If you genuinely need concurrent read-write from multiple apps, do **not** use block-level multiattach — instead have one app export the data over a network share (NFS / SMB / MinIO) that the others mount.+
  
-===== Node storage backend (ZFS vs ext4) =====+  - **Content-tree volume** — create a volume instance of type **Content Tree**, based on a **container image**. The image is **never executed**; its rootfs simply seeds the shared volume's initial contents, so use a **small/empty** image (e.g. ''busybox'', or a ''FROM scratch'' image if you want the share to start empty). //Do not// use something like ''nginx'' — you'd just get its filesystem as clutter. 
 +  - **Block-storage volume** — create a Block Storage volume instance **connected to that content-tree** (''content_tree_id''), with an access mode and a **label**. Size it larger than the container. 
 +  - **Attach to each app** — on every edge app (container or VM), add a drive that references the **block-storage volume by its label**, and set a **mount path**. 
 +  - **Deploy** all the app instances on the **same edge node**.
  
-Whether an edge node can honor multi-attach depends entirely on how it backs volumes, +===== Access modes =====
-which is set by ''/persist'' storage type:+
  
-  * **ZFS persist** → block-storage volumes are **zvols** (real block devices) → multi-attach works. +The ''accessmode'' field (each value prefixed with ''VOLUME_INSTANCE_ACCESS_MODE_''):
-  * **ext4 persist** (typical single-disk box) → block-storage volumes are **qcow2 image files** → EVE **cannot** attach the same file to two live domains, regardless of the ''multiattach'' flag. You get the device-side error: //Multiple app instances (2) are trying to use the same file-based volume <name>//.+
  
-**Key facts:**+^ Value (suffix) ^ Meaning ^ 
 +| ''READ'' / ''READONLY'' | Read-only — any volume type can be shared this way | 
 +| ''READWRITE'' | Read + write | 
 +| ''MULTIREAD_SINGLEWRITE'' | Treated the **same as Read,Write** by EVE |
  
-  * The backend is decided at EVE **install / onboarding time**, driven by disk layout. It is **not** a controller/Terraform setting and cannot be toggled later. +For a writable shared content-tree volume, ''READWRITE'' (or ''MULTIREAD_SINGLEWRITE'') plus ''multiattach = true''.
-  * Roughly: **single disk → ext4**, **multiple disks → ZFS** (EVE builds a zpool for ''/persist''). +
-  * Converting a node to ZFS is heavyweight: add a disk and **re-install/re-onboard EVE** (wipes persist). It is not an in-place conversion. +
- +
-**Check what a node has:** ''mount | grep /persist'' (shows ''zfs'' or ''ext4''), or +
-''zpool status'' (no pools = not ZFS), via edgeview / SSH. +
- +
-**If the node is ext4** (no ZFS), do **not** use block-level multi-attach. Instead: +
- +
-  * **Isolated per-VM volumes** — each app gets its own volume (no sharing, no error), or +
-  * **NFS / SMB share** — one app owns the volume and exports it; others mount it over a local network instance for live concurrent access. +
- +
-==== How to: install EVE with a ZFS persist pool ==== +
- +
-There is **no in-place conversion** — ''/persist'' holds all node state, and its filesystem +
-type is chosen at **install / first boot**. To get ZFS you must (re)install EVE with ZFS +
-targeted from the start; changing an existing ext4 node means **wipe + reinstall + re-onboard**. +
- +
-ZFS is selected via a grub install variable in the installer's ''grub.cfg'' (in the +
-''config'' / EFI partition of the install media, or baked into a custom EVE installer image): +
- +
-<code> +
-eve_install_zfs_with_raid_level=none +
-</code> +
- +
-Accepted values (default is ''none''): +
- +
-^ Value ^ Redundancy ^ ZFS layout / disks needed ^ +
-| ''none'' | **None** | single disk / stripe — no redundancy (use this for a single-disk box) | +
-| ''raid1'' | Mirror | survives 1 disk loss — needs ≥2 disks | +
-| ''raid5'' | RAIDZ1 | survives 1 disk loss — needs ≥3 disks | +
-| ''raid6'' | RAIDZ2 | survives 2 disk losses — needs ≥4 disks | +
- +
-**Notes:** +
- +
-  * **''none'' is enough for multi-attach.** Multi-attach only needs ZFS *backing* (volumes become zvols); redundancy is irrelevant. On a single-disk node, ''none'' is the only option. +
-  * There is **no** ''raid0'' / numeric ''0'' value — use ''none''. +
-  * The variable also applies on **first boot of a live image**, not just a full installer — so baking it into the config makes first boot lay down a ZFS persist pool instead of ext4. +
-  * Set it alongside the other install vars in the same ''grub.cfg'' (''eve_install_disk'', ''eve_persist_disk'', ''eve_install_server''). +
-  * **Verify the variable/values against your EVE version** — install-var names have shifted across releases; check the ''grub.cfg'' on your current install media or EVE's ''docs/BOOTING.md''. +
- +
-After the ZFS install completes and the node re-onboards, block-storage volumes are zvols +
-and ''multiattach = true'' + ''MULTIREAD_SINGLEWRITE'' will work. +
- +
-===== Do the VMs have to stay one-reader / one-writer? ===== +
- +
-At any given time **only one app may mount the volume read-write; every other app must +
-mount it read-only**. A few points that are easy to get wrong: +
- +
-  * **It is not enforced automatically.** EVE will not stop you from setting ''readonly = false'' on two apps. If you do, both mount ''/data'' read-write, both can write, and the filesystem **will corrupt**. Safety comes entirely from **you** marking all-but-one drive as ''readonly = true''. +
-  * **The writer role can be changed, but not doubled.** To hand off the writer role, flip the flags (old writer → ''readonly = true'', new writer → ''readonly = false'') and re-apply, so the volume is only ever mounted read-write by one app at a time. This is an operational hand-off, **not** concurrent shared writing. +
-  * **Readers do not see live updates.** A read-only block mount does not track the writer's new files in real time — the reader's filesystem cache is unaware of changes the writer makes. "One writes, others read" works best when readers mount **after** the data is written, or remount to pick up changes. It is **not** a live shared filesystem. +
- +
-**Rule of thumb:** +
- +
-  * One app **produces** data, the other(s) only **consume** it → multiattach with writer/reader flags is fine. +
-  * Multiple apps need **live concurrent read-write** on shared files → block-level multiattach is the **wrong** mechanism regardless of the flags. Run an **NFS / SMB server** app on the volume and have the others mount it over a local network instance for coherent, concurrent read-write.+
  
 ===== Terraform (zedcloud provider) ===== ===== Terraform (zedcloud provider) =====
  
-The ''zedcloud_volume_instance'' resource exposes both ''accessmode'' and ''multiattach'', +<code hcl> 
-so this is done entirely in Terraform.+# A tiny CONTAINER image — only used to seed the content-tree (never executed) 
 +resource "zedcloud_image" "shared_ct_image" { 
 +  datastore_id  = zedcloud_datastore.docker_hub.id 
 +  image_type    = "IMAGE_TYPE_APPLICATION" 
 +  image_arch    = "AMD64" 
 +  image_format  = "CONTAINER" 
 +  image_size_bytes = 0 
 +  name          = "SHARED-CT-BUSYBOX" 
 +  title         = "SHARED-CT-BUSYBOX" 
 +  image_rel_url = "busybox:latest" 
 +}
  
-==== Volume definition ====+# 1) Content-tree volume seeded from that container image 
 +resource "zedcloud_volume_instance" "shared_ct" { 
 +  device_id = zedcloud_edgenode.my_node.id 
 +  type      = "VOLUME_INSTANCE_TYPE_CONTENT_TREE" 
 +  image     = zedcloud_image.shared_ct_image.name 
 +  label     = "shared-ct" 
 +  name      = "shared-ct" 
 +  title     = "shared-ct" 
 +}
  
-<code hcl> +# 2) Block-storage volume connected to the content-tree (this is what apps attach) 
-resource "zedcloud_volume_instance" "shared_persist" { +resource "zedcloud_volume_instance" "shared_blk" { 
-  device_id   = zedcloud_edgenode.my_edgenode.id +  device_id       = zedcloud_edgenode.my_node.id 
-  accessmode  = "VOLUME_INSTANCE_ACCESS_MODE_MULTIREAD_SINGLEWRITE" +  type            = "VOLUME_INSTANCE_TYPE_BLOCKSTORAGE" 
-  multiattach = true +  content_tree_id = zedcloud_volume_instance.shared_ct.id 
-  cleartext   = false +  accessmode      = "VOLUME_INSTANCE_ACCESS_MODE_READWRITE" 
-  label       = "shared-persist" +  multiattach     = true 
-  name        = "shared-persist" +  size_bytes      = 2147483648 
-  size_bytes  = 1073741824 +  label           = "shared-blk" 
-  title       = "shared-persist" +  name            = "shared-blk" 
-  type        = "VOLUME_INSTANCE_TYPE_BLOCKSTORAGE" +  title           = "shared-blk"
-  lifecycle { +
-    ignore_changes = [device_id] +
-  }+
 } }
 </code> </code>
  
-==== Attaching to the writer app (read-write) ====+Then, on **each** app instance, add a drive referencing the block-storage volume by label:
  
 <code hcl> <code hcl>
   drives {   drives {
     imagename   = ""     imagename   = ""
-    volumelabel = zedcloud_volume_instance.shared_persist.label+    volumelabel = zedcloud_volume_instance.shared_blk.label
     mountpath   = "/data"     mountpath   = "/data"
-    cleartext   = false 
-    ignorepurge = true 
-    maxsize     = 1073741824 
-    preserve    = true 
     target      = "Disk"     target      = "Disk"
     drvtype     = "HDD"     drvtype     = "HDD"
-    readonly    = false   ### writer 
-  } 
-</code> 
- 
-==== Attaching to the reader app(s) (read-only) ==== 
- 
-<code hcl> 
-  drives { 
-    imagename   = "" 
-    volumelabel = zedcloud_volume_instance.shared_persist.label 
-    mountpath   = "/data" 
-    cleartext   = false 
-    ignorepurge = true 
-    maxsize     = 1073741824 
     preserve    = true     preserve    = true
-    target      = "Disk" +    readonly    = false   # both apps may read AND write (9P arbitrates)
-    drvtype     = "HDD" +
-    readonly    = true    ### reader+
   }   }
 </code> </code>
  
-===== Changing the writer (flipping the read/write flags) =====+===== Mounting inside the guest =====
  
-Handing the writer role from one app to another is an **in-place update of the app +  * **Container apps** — EVE **auto-mounts** the share at the drive's ''mountpath''. Nothing to do in the guest. 
-instance config** — not a recreate.+  * **VMs** — you must **mount the 9P share manually**. EVE exposes it as a virtio-9P device with mount tag **''share_dir''** (from EVE ''hypervisor/kvm.go''):
  
-==== The Terraform operation ====+<code bash> 
 +modprobe 9pnet_virtio 2>/dev/null || true 
 +mkdir -p /data 
 +# fstab (nofail so a missing share never blocks boot): 
 +echo "share_dir  /data  9p  trans=virtio,version=9p2000.L,rw,nofail  0  0" >> /etc/fstab 
 +mount /data 
 +</code>
  
-  - Edit the ''readonly'' values on the two ''drives{}'' blocks (old writer → ''true'', new writer → ''false''). +Do this via cloud-init so it happens on every boot. **No ''mkfs'', no block device** — it is a 9P filesystem, not a raw disk.
-  - ''terraform plan'' — you should see **''~ update in-place''** on both ''zedcloud_application_instance'' resources (a nested-attribute change), **not** ''-/+ replace''. If plan shows a replace, stop and investigate. +
-  - ''terraform apply'' — the provider issues a PUT/update to each app instance, bumping the EdgeAppInstance config version. The controller pushes the new config to the edge node and EVE reconciles.+
  
-==== What happens on the device ====+===== Requirements & gotchas =====
  
-The ''readonly'' attribute is fixed **when EVE (domainmgr) builds the VM's domain config +  * **EVE-OS >= 12.7.0.** 
-and launches QEMU** — it is **not** a live/hot change. EVE cannot just remount; it will +  * **All sharing apps on the same edge node.** 
-**restart the affected app instance(s)** to bring the disk up in the new mode. Expect a +  * **Writable share ⇒ container content-tree base.** A plain block-storage/QCOW2 volume shared writable across VMs is the blocked case (see the error above). 
-brief VM restart on each VM whose flag changed. (Standard eve-kvm drives QEMU directly, no +  * **The content-tree image is a seed, not a workload** — it is never run; its rootfs is the volume's starting content. Use a tiny/empty base. 
-libvirt; libvirt is only in the picture in kube mode via KubeVirt.)+  * **VMs mount manually (9P, tag ''share_dir''); containers auto-mount** at the mount path. 
 +  * **9P allows concurrent read+write** for all apps — no single-writer restriction, unlike a raw shared block device.
  
-==== Ordering matters ====+===== Doing it via zcli / REST / WebUI =====
  
-Because ''MULTIREAD_SINGLEWRITE'' allows at most one RW mount at a time, avoid any window +Same objects as Terraform: 
-where the **old writer is still RW while the new writer comes up RW**.+  * **WebUI:** Library → Volume Instances → create a **Content Tree** (from a container image), then a **Block Storage** connected to it; add a drive on each edge app referencing the block-storage label + mount path. 
 +  * **REST:** ''POST/PUT /api/v1/volumes/instances'' for both volumes; app-instance drives via ''PUT /api/v1/apps/instances/id/{id}''. 
 +  * **zcli:** ''zcli volume-instance ...'' and ''zcli edge-app-instance ...''.
  
-  * **Two-step (safest):** +===== Summary =====
-    - Apply #1 — set the **old writer to ''readonly = true''** (both now RO; volume has zero writers). Let it settle. +
-    - Apply #2 — set the **new writer to ''readonly = false''**. +
-    This guarantees the volume is never mounted RW by two VMs at once. +
-  * **One-step:** flip both in a single apply. Usually works because both VMs restart and the old writer releases RW during its restart — but there is a small race depending on restart order. Acceptable for a demo, not for data you care about.+
  
-==== Notes ====+  * Read-only sharing: any volume type. 
 +  * Read-write sharing across VMs: **container content-tree** volume + block-storage connected to it, attached to each app; shared via **9P** (all apps read+write). 
 +  * **Not** a ZFS/zvol/disk-format/''multiattach''-flag matter — the gate is container-vs-file and read-only-vs-writable. 
 +  * VMs mount the 9P share manually (''mount -t 9p ... share_dir''); containers auto-mount. 
 +  * Requires EVE-OS >= 12.7.0, all apps on the same node.
  
-  * No ''-replace'' is needed, and this is **not** a cloud-init change, so purge-counter caveats do not apply. +===== Worked example: a container writer + VM readers =====
-  * After apply, confirm on the new writer that ''/data'' is writable (''mount | grep /data'', or ''touch /data/test'') and that the old writer shows it ''ro''.+
  
-===== Doing it via zcli / REST API / WebUI =====+The practical shape of "one app generates data, the others consume it" when the 
 +consumers are **VMs** (which cannot share a writable volume among themselves):
  
-The same two operations — **(a)** creating/setting the shared volume (''accessmode'' + +  * **Writer = a CONTAINER app.** Containers get the writable 9P overlay, so the writer mounts the shared content-tree volume at ''/data'' (writable, auto-mounted) and writes to it. 
-''multiattach'') and **(b)** the writer flip (per-drive ''readonly'' on the app instance) — +  * **Readers = VMs.** They mount the SAME volume over 9P (tag ''share_dir'') and read ''/data''. 
-can be done through any interface. The underlying behavior is identical to Terraform.+  * **Data source = a patch envelope.** ''config.json'' is delivered via the metadata service (''http://169.254.169.254/eve/v1/patch/...''). The writer container polls ''description.json'', downloads the artifact, and writes versioned copies (''config-<ts>.json'', ''config.latest.json'') into ''/data''.
  
-==== REST API ====+Wiring:
  
-Base: ''https://<zedcontrol>/api'' — Bearer token in the ''Authorization'' header.+  - Shared **content-tree + block-storage** volume (as above), attached to the writer container AND the reader VMs. 
 +  - The writer needs a **LOCAL network instance** — ''169.254.169.254'' is only reachable via a Local NI (a Switch NI is pure L2 and cannot reach it). 
 +  - Create a **patch envelope** (''zedcloud_patch_envelope'') with the artifact inline (base64), and **bind** it to the writer instance (''zedcloud_patch_reference_update''). ''action = PATCH_ENVELOPE_ACTION_ACTIVATE'' presents it to the app. 
 +  - Optionally expose a web UI from the writer with a **portmap ACL** (''actions { portmap = true; portmapto { app_port = 8080 } }'').
  
-**Volume (create/update):**+Notes proven on-device: ''description.json'' returns a list of envelopes, each with 
 +**''PatchID''** (capital ID) and **''BinaryBlobs[]''** entries carrying ''fileName'', ''fileSha'', 
 +and a full ''url''. The content-tree's base image rootfs (e.g. busybox: ''bin/'', ''etc/'', …) 
 +appears under ''/data'' as the seed — use a ''FROM scratch'' base if you want ''/data'' to 
 +start empty.
  
-  * ''POST /api/v1/volumes/instances'' (create) or ''PUT /api/v1/volumes/instances/id/{id}'' (update) +===== Deployment gotchas (learned on-device) =====
-  * Body includes ''"accessmode": "VOLUME_INSTANCE_ACCESS_MODE_MULTIREAD_SINGLEWRITE"'' and ''"multiattach": true''+
  
-**Writer flip (app instance) — read-modify-write:**+  * **Container image tag is immutable AND the app holds it.** To bump an image tag you must recreate the image AND every app/instance that references it — otherwise you get ''Image <x> is already in use by app bundle'' (409). ''-replace'' the instance + app + image together so the app is torn down before the image. 
 +  * **"Invalid version number" / "request body parsing failed" on a ''zedcloud_image''** = you changed ''image_rel_url'' on an image that already exists (it's immutable) → an update the API rejects. Recreate it (''-replace''). It is **not** the tag value or the registry image content. 
 +  * **Patch envelope artifact changes = recreate, not update.** Editing the artifact triggers ''PUT /patch-envelope/id/{id}'' → 400. ''-replace'' the envelope (and its ''patch_reference_update'' binding). Bump ''user_defined_version'' per version so the metadata ''Version'' field reflects reality. 
 +  * **Content-tree volume ''accessmode''** is required — omit it and the create fails with ''access mode cannot be invalid''. Use ''READONLY'' for the content-tree; ''READWRITE'' for the connected block-storage. 
 +  * **Provider-computed drift** (e.g. content-tree ''size_bytes "0" -> null'') triggers rejected updates — add ''lifecycle { ignore_changes = [size_bytes] }''. 
 +  * **Build container images with ''--provenance=false --sbom=false''** — clean hygiene (avoids "unknown/unknown" attestation manifests in the index). Not strictly required for ZEDEDA to accept the image, but good practice. 
 +  * **9P mount tag is ''share_dir''** (a literal in EVE ''hypervisor/kvm.go''), and it's triggered by the drive's **CONTAINER format**, not by ''mountpath''.
  
-  - ''GET /api/v1/apps/instances/id/{id}'' — get the current config object +===== Updating the config (runbook) =====
-  - In the JSON, edit the target ''drives[]'' entry's ''readonly'' field (old writer → ''true'', new writer → ''false'') +
-  - ''PUT /api/v1/apps/instances/id/{id}'' with the full modified object (bumps config version) +
-  - Device reconciles; optionally force with ''PUT /api/v1/apps/instances/id/{id}/restart''+
  
-<color green>**A ''.../refresh/purge'' endpoint exists too, but for a ''readonly'' change a plain PUT + restart is enough — no purge needed.**</color>+Push a new config version **without** redeploying the image, app, VMs, or volumes — 
 +you only recreate the patch envelope. The writer auto-fetches it on its next poll and 
 +drops a new versioned file into ''/data''.
  
-==== zcli ====+<code bash> 
 +# 1. Edit the config artifact 
 +$EDITOR Demo-Hummingbird/c-init/patch-config.json
  
-zcli manages the same objects: ''zcli volume-instance ...'' and ''zcli edge-app-instance ...''.+# 2. Bump the version on the envelope in 6-Instances-Deploy-hum.tf 
 +#    user_defined_version = "2.0"  ->  "3.0"
  
-  * **Volume:** ''zcli volume-instance create <name> ...'' with the access-mode / multiattach flags. +# 3. Recreate the envelope + its binding (in-place update is rejected -> -replace) 
-  * **Writer flip:** zcli does **not** expose a clean per-drive ''readonly'' toggle flag. Edit the instance config (''zcli edge-app-instance update <name> --config=<file.json>'' — export, edit the drive's ''readonly'', re-apply), then ''zcli edge-app-instance restart <name>'' to bring the disk up in the new mode.+terraform apply \ 
 +  -replace='zedcloud_patch_envelope.tf_demo_config_pe' \ 
 +  -replace='zedcloud_patch_reference_update.tf_demo_config_pe_bind'
  
-//Verify exact flag names with// ''zcli volume-instance create --help'' //and// ''zcli +# 4. Wait ~30-60s (controller -> device propagation + the writer's poll interval)
-edge-app-instance update --help'' //— the per-drive flip is least ergonomic here; WebUI / +
-REST / Terraform are cleaner.//+
  
-==== WebUI (zedcontrol console) ====+# 5. Verify (on the writer container or any reader VM) 
 +cat /data/config.latest.json      # new content 
 +cat /data/config.history          # <ts>  <sha> per version 
 +#    writer log line: [pcw] new config.json (v=3.0) -> /data/config-<ts>.json (sha=...) 
 +</code>
  
-  * **Volume:** Volume Instances → **New/Create** → set **Access Mode = Multi-Read Single-Write** and enable **Multi-attach** → attach to the edge node → Save. +  * **''-replace'' both resources:** an in-place artifact update returns HTTP 400; and the envelope's ID changes on recreate, so the ''patch_reference_update'' binding must be recreated to re-point at it. 
-  * **Writer flip:** open each **Edge Application Instance** → **Edit** → **Storage / Drives** section → toggle the **Read-only** checkbox on the shared drive (old writer → checked, new writer → unchecked) → **Save/Update**. Saving pushes a new config version; the VM restarts to remount in the new mode.+  * **Bumping ''user_defined_version''** is recommended (labels the version, shows in the metadata ''Version'' field and the writer log) but not strictly required — the writer detects change by content SHA. 
 +  * **You do NOT touch** the container image, the writer app, the VMs, or the volumes. That is the whole point of the patch-envelope design: config changes with nothing redeployed.
  
-==== Common to all interfaces ====+==== First-time deploy (for reference) ====
  
-Regardless of interface (including Terraform), the underlying behavior is identical:+<code bash> 
 +# build + push the writer image (clean manifest — no attestation entries) 
 +docker buildx build --platform linux/amd64,linux/arm64 --provenance=false --sbom=false \ 
 +  -t zedmanny/patch-config-writer:1.2.0 --push .
  
-  * ''readonly'' is fixed when EVE builds the domain config / launches QEMU → the affected **VM restarts** to apply. +# bring everything up 
-  * Respect the **ordering**: old writer → RO first (zero writers), then new writer → RW, to avoid two live RW mounts.+terraform apply
  
-===== Common mistake =====+# if the image was already created and you changed its tag, the image is immutable AND 
 +# held by the app -> recreate the whole chain in one apply: 
 +terraform apply \ 
 +  -replace='zedcloud_application_instance.tf_patch_config_writer_1' \ 
 +  -replace='zedcloud_application.tf_patch_config_writer_app' \ 
 +  -replace='zedcloud_image.demo_patch_config_writer' 
 +</code>
  
-Referencing the same volume from two app instances while the volume is still defined as +===== Sources =====
-plain ''VOLUME_INSTANCE_ACCESS_MODE_READWRITE'' with **no ''multiattach''** is **not** a +
-valid shared config — it either fails to attach to the second app or produces two +
-uncoordinated read-write mounts. Always set ''multiattach = true'' + +
-''MULTIREAD_SINGLEWRITE'', and keep only one writer. +
- +
-===== Summary =====+
  
-  * Sharing a persistent volume across apps is **supported** — ''multiattach = true'' + ''MULTIREAD_SINGLEWRITE'' — **but only on a ZFS-persist node** (file-based/ext4 nodes cannot multi-attach). +  * ZEDEDA Help Center — "Attach Volume Instances to Multiple Edge Applications", "Update Edge Application Instances with Patch Envelopes" 
-  * **Same edge node** for all attaching apps. +  * EVE source: ''pkg/pillar/cmd/volumemgr/handlevolumeref.go'' (the share guard), ''pkg/pillar/hypervisor/kvm.go'' (9P mount tag ''share_dir''), ''pkg/pillar/cmd/domainmgr/domainmgr.go'' (CONTAINER format → 9P)
-  * **One writer, others read-only**; for concurrent multi-writer, use a network share instead. +
-  * Fully configurable in the ''zedcloud'' Terraform provider (''accessmode'', ''multiattach'', per-drive ''readonly'').+
  
zededa/sharing-persist-volume-by-two-apps.1784735649.txt.gz · Last modified: by mc