diff --git a/README.md b/README.md index 712a086..e8a7101 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,13 @@ # keellinux.org -The project site, served by GitHub Pages from `main`: two static pages, one +The project site, served by GitHub Pages from `main`: three static pages, one stylesheet and the brand assets. Nothing builds it, so what is in the repository is what the browser gets. | Path | What it is | |------|------------| | `index.html` | the home page, also served at `www` | +| `architecture.html` | the architecture the project aims for, in three scenarios, served at `/architecture` | | `guidelines.html` | how the organization works, and the mark | | `style.css` | the whole stylesheet, light and dark | | `assets/` | the mark in its variants and the social card | diff --git a/architecture.html b/architecture.html new file mode 100644 index 0000000..cee07f8 --- /dev/null +++ b/architecture.html @@ -0,0 +1,585 @@ + + + + + + Architecture, Keel Linux + + + + + + + + + + + + + + + + + + + +
+
+
+ Architecture +

How far the architecture goes

+

The architecture the project aims for, in three scenarios. The first is the goal of the first release. The second is decided (decision 0020) and not built. The third is a direction with no design yet.

+ +
    +
  1. + Keel Linux Standalone + Our goal · not yet released +
  2. +
  3. + Keel Cloud, three nodes + Planned +
  4. +
  5. + Separated tiers + Future +
  6. +
+
+
+ + +
+
+ Scenario 1 +
+

Keel Linux Standalone

+ Our goal · not yet released +
+

This is what Keel Linux is building first. It runs today from source and test images; there is no release for end users yet.

+

One LXC container or VM runs the whole stack: the application, its database and its files. One instance spec describes it, and Keel keeps the machine and the spec in step.

+ +
+ + Keel Linux Standalone, the project's goal, not yet released + An operator runs keel inspect, keel diff and keel spec apply against one LXC container or VM at 2001:db8:4b1::10, which holds the application, the database and the files, described by one instance spec. On a VM, a network change opens a 120 second window: keel network confirm from a new session keeps it, otherwise the old network is restored, also after a reboot. In an LXC container the host owns the network and keel only compares it. The network change is marked as VM only, in keel 0.9.0, not yet released. The whole scenario is the project's goal and not yet released. + + + + + + + + OUR GOAL · NOT YET RELEASED + + + + Operator + over SSH or in confconsole + keel inspect + what the machine is + keel diff + spec against machine + keel spec apply + converge, safe to re-run + + + + + + One LXC or VM + 2001:db8:4b1::10 + + application + WordPress, NodeBB, LAMP + + database + MariaDB or PostgreSQL, local + + files + uploads and other state + described by one instance spec + IPv6 first, IPv4 optional + + + + Network change + + VM only · in keel 0.9.0, not yet released + + + apply writes the new network + + a 120 s window opens + + keel network confirm + from a new session: kept + + no confirmation in time: + the old network is restored, + also after a reboot + In LXC the host owns it: compared only + + + + Keel Linux Standalone, the project's goal, not yet released + An operator runs keel inspect, keel diff and keel spec apply against one LXC container or VM at 2001:db8:4b1::10, which holds the application, the database and the files, described by one instance spec. On a VM, a network change opens a 120 second window: keel network confirm from a new session keeps it, otherwise the old network is restored, also after a reboot. In an LXC container the host owns the network and keel only compares it. The network change is marked as VM only, in keel 0.9.0, not yet released. The whole scenario is the project's goal and not yet released. + + + + + + + + OUR GOAL · NOT YET RELEASED + + + Operator + over SSH or in confconsole + keel inspect + what the machine is + keel diff + spec against machine + keel spec apply + converge, safe to re-run + + + + + One LXC or VM + 2001:db8:4b1::10 + + application + WordPress, NodeBB, LAMP + + database + MariaDB or PostgreSQL, local + + files + uploads and other state + described by one instance spec + + + Network change + + VM only · in keel 0.9.0, not yet released + + + apply writes the new network + + a 120 s window opens + + keel network confirm + from a new session: kept + + no confirmation in time: + the old network is restored, + also after a reboot + In LXC the host owns it: compared only + +
Everything runs on one machine. What the spec declares, keel diff compares and keel spec apply converges.
+
+ +
    +
  • Declared and compared. keel inspect reads the running machine, keel diff shows where it differs from the spec, keel spec apply converges it and is safe to run again.
  • +
  • Network changes that undo themselves. VM only · in keel 0.9.0, not yet released On a VM, a changed network must be confirmed with keel network confirm from a new session within 120 seconds, or the previous configuration comes back. A machine reached only over SSH is the case this is for. In an LXC container the host owns the network; keel compares it and does not change it.
  • +
  • Not through Webmin. The network is changed in confconsole or at the command line; Webmin shows it read-only (decision 0019).
  • +
+
+
+ + +
+
+ Scenario 2 +
+

Keel Cloud, three nodes

+ Planned, decision 0020 +
+

Three nodes, LXC containers or VMs, two or three of them holding the application: one primary takes the writes, the replicas follow it and stand ready to take over. Keel Cloud is optional: three standalone appliances configured by hand do the same, and Keel Cloud only automates the setup; without it, the public name is moved by hand after a failover.

+ +
+ + Keel Cloud with three nodes, planned + Visitors resolve the name through keel-cloud, an optional service with DNS and a health check, node discovery and WireGuard key exchange, which points the name at node A, the primary. Nodes A, B and C sit on a WireGuard private network. Node A at 2001:db8:4b1::11 is the primary and takes the writes. Nodes B and C at ::12 and ::13 are replicas: the database replicates to them and they pull the files from A with rsync. Node C may instead be a voter holding no data. Every node runs etcd and keel-quorum; a majority of the three elects the primary, and a primary that loses the majority turns itself read-only. Without keel-cloud, the same three nodes set up by hand elect and fail over alike; the public name is then moved by hand. + + + + + + + + PLANNED · DECISION 0020 + + + + Visitors + resolve the name + + + + keel-cloud + + optional + DNS with a health check: the name points at the primary + node discovery · WireGuard key exchange + + + Without keel-cloud + the same three nodes, + set up by hand, elect + and fail over alike; + the public name is then + moved by hand + + + DNS answer: node A + + + + WireGuard private network + + + + Node A · primary + 2001:db8:4b1::11 + app · database · files + takes the writes + etcd · keel-quorum + a voter + + + Node B · replica + 2001:db8:4b1::12 + app · database · files + follows, can be promoted + etcd · keel-quorum + a voter + + + Node C · replica + 2001:db8:4b1::13 + app · database · files + follows, can be promoted + etcd · keel-quorum + or only a voter, no data: + etcd and keel-quorum alone + + + database + + files, pulled + + by B, rsync + + + + the same flows reach node C + + + + Election on the nodes: keel-quorum on etcd + A majority of the three voters elects the primary. A primary that loses the majority turns itself read-only (fencing). + Automatic failover needs three voters, to avoid split brain. With fewer, a replica is promoted by hand. + + + + Keel Cloud with three nodes, planned + keel-cloud, an optional service with DNS and a health check, node discovery and WireGuard key exchange, points the name at node A, the primary. Nodes A, B and C sit on a WireGuard private network. Node A at 2001:db8:4b1::11 is the primary and takes the writes. Nodes B and C at ::12 and ::13 are replicas: the database replicates to them and they pull the files from A with rsync. Node C may instead be a voter holding no data. Every node runs etcd and keel-quorum; a majority of the three elects the primary, and a primary that loses the majority turns itself read-only. Without keel-cloud, the same three nodes set up by hand elect and fail over alike; the public name is then moved by hand. + + + + + + + + PLANNED · DECISION 0020 + + + keel-cloud + + optional + DNS with a health check, + the name points at the primary + node discovery · WireGuard keys + Without it, the same three nodes set + up by hand elect and fail over alike; + the public name is then moved by hand. + + + DNS answer: node A + + + WireGuard private network + + + Node A · primary + 2001:db8:4b1::11 + app · database · files + etcd · keel-quorum, a voter + takes the writes + + + database replication + files, pulled by B with rsync + + + Node B · replica + 2001:db8:4b1::12 + app · database · files + etcd · keel-quorum, a voter + + + Node C · replica + 2001:db8:4b1::13 + app · database · files + etcd · keel-quorum, a voter + or only a voter: no data, not an appliance + + + + + Election on the nodes + keel-quorum on etcd. A majority of + the three voters elects the primary. + A primary that loses the majority + turns itself read-only (fencing). + Automatic failover needs three voters. + With fewer, a replica is promoted + by hand. + +
One writer at a time. The replicas pull, the primary never pushes, and the election runs on the nodes themselves, so no outside service is required for a failover; without Keel Cloud, the public name is then moved by hand.
+
+ +
    +
  • Keel Cloud is optional. keel-cloud adds DNS with a health check, node discovery and WireGuard key exchange. It is in the spirit of TurnKey's HubDNS, which updates a name but checks no health. An API key for it is offered at installation and never required.
  • +
  • The election is its own project, keel-quorum. One etcd election for the whole appliance: database, files and web tier are promoted together. Every data node is a voter; the third node may be a voter that holds no data, which is not an appliance and runs only etcd and keel-quorum.
  • +
  • Private network. The nodes talk over WireGuard. Its configuration is converged under the same 120 second confirmation window as any network change.
  • +
  • Status panel. A read-only screen in confconsole shows one line per node: role, replication lag, last file pull, health, and the elected leader. It changes nothing on the other nodes.
  • +
  • What it does not promise. Replication and the file pull are asynchronous, so the last seconds before a failure can be lost. DNS takes one to five minutes to follow a failover, and some resolvers ignore low TTLs. In an LXC container there is no watchdog, so fencing relies on the process demoting itself in time; a VM can have one.
  • +
+ +

Chosen by the operator when the set is formed

+

Made once and stored in etcd, so every node reads the same answer. The same choices exist with or without Keel Cloud.

+
+ + + + + + + + + +
ChoiceOptionsDefault
Failover: a replica takes over when the primary is lostautomatic, manualautomatic, active only once three voters are present
Rejoin: the old primary comes back as a replicaautomatic, manualmanual. Automatic first keeps a copy of what the node held
Failback: the primary role returns to the original nodeautomatic, manual, nevermanual
+
+

When: after the orchestrated upgrade of milestone M3. Keel Cloud comes last and starts with a decision note of its own, so its design beyond the three services above is not written yet.

+
+
+ + +
+
+ Scenario 3 +
+

Separated tiers

+ Future +
+

The application and the database move to machines of their own. Not designed yet: only the remote database (0013 phase 1) and Galera (0013 phase 4) are planned pieces.

+ +
+ + Separated tiers, future, not designed yet + An application tier of three VMs, app 1 to app 3 at 2001:db8:4b1::21 to ::23; how they coordinate is to be designed. The application points at the remote database, decision 0013 phase 1. A database tier of three VMs, db 1 to db 3 at 2001:db8:4b1::31 to ::33, with primary and replicas, or Galera members for MariaDB. Sharding is possible: MariaDB through Spider; for PostgreSQL Keel configures the node's role and the shard design belongs to whoever designs the system. + + + + + + + + FUTURE + + Application tier + three VMs; how they coordinate is to be designed + + + app 1 + 2001:db8:4b1::21 + application + + app 2 + 2001:db8:4b1::22 + application + + app 3 + 2001:db8:4b1::23 + application + + + the application points at the remote database + decision 0013, phase 1 + + Database tier + three VMs: primary and replicas, or Galera for MariaDB + + + db 1 + 2001:db8:4b1::31 + primary, or a Galera member + + db 2 + 2001:db8:4b1::32 + replica, or a Galera member + + db 3 + 2001:db8:4b1::33 + replica, or a Galera member + + + Sharding, possible + MariaDB: the Spider engine, a head node and data nodes. + PostgreSQL: Keel configures the node's role; the shard design belongs to whoever designs the system. + + + + Separated tiers, future, not designed yet + An application tier of three VMs, app 1 to app 3 at 2001:db8:4b1::21 to ::23; how they coordinate is to be designed. The application points at the remote database, decision 0013 phase 1. A database tier of three VMs, db 1 to db 3 at 2001:db8:4b1::31 to ::33, with primary and replicas, or Galera members for MariaDB. Sharding is possible: MariaDB through Spider; for PostgreSQL Keel configures the node's role and the shard design belongs to whoever designs the system. + + + + + + + + FUTURE + + Application tier + three VMs, coordination to be designed + + + app 1 + ::21 + application + + app 2 + ::22 + application + + app 3 + ::23 + application + in 2001:db8:4b1::/64 + + + points at the remote database + decision 0013, phase 1 + + Database tier + three VMs + + + db 1 + ::31 + primary + + db 2 + ::32 + replica + + db 3 + ::33 + replica + or three Galera members, for MariaDB + + + Sharding, possible + MariaDB: the Spider engine, + a head node and data nodes. + PostgreSQL: Keel configures the + node's role; the shard design + belongs to whoever designs + the system. + +
Each tier on machines of its own. Keel configures the role of each machine; it does not design the database layout.
+
+ +
    +
  • A remote database first. An application pointing at a database on another machine is the first piece (decision 0013, phase 1). It is deferred today.
  • +
  • Replication, or Galera. Primary and replicas on both engines; Galera gives MariaDB several writers, with a minimum of three nodes.
  • +
  • Sharding is possible, not promised. MariaDB has Spider packaged in Debian 13. PostgreSQL has the pieces but no product: Keel configures the role of the node, and the partition scheme stays with whoever designs the system.
  • +
+
+
+
+ + + + + + diff --git a/guidelines.html b/guidelines.html index 589423b..863f365 100644 --- a/guidelines.html +++ b/guidelines.html @@ -40,6 +40,7 @@
  • How it is built
  • Standards
  • Status
  • +
  • Architecture
  • Guidelines
  • GitHub
  • @@ -111,7 +112,7 @@

    The mark

    diff --git a/index.html b/index.html index 6f2b94d..eb3c30b 100644 --- a/index.html +++ b/index.html @@ -43,6 +43,7 @@
  • How it is built
  • Standards
  • Status
  • +
  • Architecture
  • Guidelines
  • GitHub
  • @@ -218,12 +219,12 @@

    Rules every change follows

    Early

    The build host reproduces unmodified TurnKey 19.0 from the forked repositories. That is the baseline every later change is measured against, and it passed before anything else started.

    -

    The first bug fixes are on branches, and the first pieces of the library (the instance spec and layer verification) exist with full test coverage. Nothing is published for end users yet: no images, no packages, no layer repository.

    +

    Layers, Proxmox templates and packages are served from mirror.keellinux.org and archive.keellinux.org for testing. There is no release for end users yet, and the test images are not meant for production.

    What is next

      -
    1. Declarative spec. inspect, apply and diff for a running appliance, with the console writing the same file.
    2. -
    3. Layered builds. Hashed core, stack and app layers with signed manifests, and a pull that fetches only the layers that are missing.
    4. +
    5. Declarative spec. keel inspect, keel diff and keel spec apply exist and run on the test images. Next: converging the network on a running machine, and the console writing the same file.
    6. +
    7. Layered builds. Hashed core, stack and app layers with signed manifests are built and served for testing. Next: making them something end users can rely on.
    8. Transition from TurnKey 19.0. A keyring package and a transition package that switch an existing appliance to the project repositories and write its spec from what is installed.
    @@ -233,7 +234,7 @@

    What is next

    diff --git a/style.css b/style.css index 1884e04..8d13cf1 100644 --- a/style.css +++ b/style.css @@ -633,3 +633,306 @@ h2 { .guide li::marker { color: var(--accent); } + +/* Architecture */ + +.scenarios { + list-style: none; + margin: 2rem 0 0; + padding: 0; + display: grid; + gap: 0.6rem; + counter-reset: scenario; +} + +.scenarios li { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.4rem 0.75rem; + counter-increment: scenario; +} + +.scenarios li::before { + content: counter(scenario); + font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, + "Liberation Mono", monospace; + font-size: 0.85rem; + color: var(--accent-text); + min-width: 1.2em; +} + +.scenarios a { + font-weight: 600; +} + +.badge { + display: inline-block; + padding: 0.2rem 0.7rem; + border-radius: 999px; + font-size: 0.78rem; + font-weight: 700; + letter-spacing: 0.06em; + text-transform: uppercase; + line-height: 1.4; + white-space: nowrap; + border: 2px solid transparent; +} + +.badge-goal { + background: var(--accent); + color: var(--navy); +} + +.badge-planned { + border-color: var(--accent); + color: var(--accent-text); +} + +.badge-future { + border: 2px dashed var(--muted); + color: var(--muted); +} + +.badge-soon { + border: 1px dashed var(--accent); + color: var(--accent-text); + font-size: 0.7rem; + padding: 0.05rem 0.55rem; + margin-right: 0.25rem; + white-space: normal; +} + +.arch-note { + max-width: var(--measure); + margin-top: 1rem; + padding-left: 0.9rem; + border-left: 3px solid var(--accent); + font-weight: 600; +} + +.arch-head { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 0.6rem 1rem; +} + +.diagram-arch { + margin: 2.5rem 0 0; +} + +.diagram-arch svg { + width: 100%; + height: auto; + color: var(--fg); + font-size: 15px; +} + +.diagram-arch svg.wide { + display: none; + max-width: 55rem; +} + +.diagram-arch svg.narrow { + max-width: 30rem; +} + +@media (min-width: 56rem) { + .diagram-arch svg.wide { + display: block; + } + + .diagram-arch svg.narrow { + display: none; + } +} + +.diagram-arch text { + fill: currentColor; +} + +.diagram-arch text.t-b { + font-weight: 700; +} + +.diagram-arch text.t-s { + font-size: 13px; +} + +.diagram-arch text.t-xs { + font-size: 13px; +} + +.diagram-arch text.t-m { + font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, + "Liberation Mono", "DejaVu Sans Mono", monospace; + font-size: 13px; +} + +.diagram-arch text.t-cap { + font-size: 13px; + font-weight: 700; + letter-spacing: 0.08em; +} + +.diagram-arch text.f-muted { + fill: var(--muted); +} + +.diagram-arch text.f-accent { + fill: var(--accent-text); +} + +.diagram-arch text.badge-goal-text { + fill: var(--navy); +} + +.diagram-arch .badge-goal-shape { + fill: var(--accent); +} + +.diagram-arch .badge-planned-shape { + fill: none; + stroke: var(--accent); + stroke-width: 2; +} + +.diagram-arch .badge-future-shape { + fill: none; + stroke: var(--muted); + stroke-width: 2; + stroke-dasharray: 5 4; +} + +.diagram-arch .box { + fill: var(--surface); + stroke: currentColor; + stroke-width: 1.5; +} + +.diagram-arch .box-key { + stroke: var(--accent); + stroke-width: 3; +} + +.diagram-arch .box-inner { + fill: var(--bg); + stroke: var(--line); + stroke-width: 1.5; +} + +.diagram-arch .dashed { + stroke-dasharray: 7 5; +} + +.diagram-arch .tag { + fill: none; + stroke: var(--accent); + stroke-width: 1.5; + stroke-dasharray: 4 3; +} + +.diagram-arch .overlay { + fill: none; + stroke: var(--accent); + stroke-width: 2; + stroke-dasharray: 10 6; +} + +.diagram-arch .tier { + fill: var(--code-bg); + stroke: var(--line); + stroke-width: 1.5; +} + +.diagram-arch .flow { + fill: none; + stroke: currentColor; + stroke-width: 1.75; +} + +.diagram-arch .dashed-line { + stroke-dasharray: 5 4; +} + +.diagram-arch .rail { + stroke: var(--line); + stroke-width: 2; +} + +.diagram-arch .dot { + fill: var(--accent); +} + +.diagram-arch .dot-open { + fill: var(--surface); + stroke: var(--accent); + stroke-width: 2; +} + +.diagram-arch figcaption { + color: var(--muted); + font-size: 0.95rem; + max-width: var(--measure); + margin-top: 1.25rem; +} + +.facts { + margin: 2rem 0 0; + padding-left: 1.25rem; + max-width: var(--measure); + display: grid; + gap: 0.75rem; +} + +.facts li::marker { + color: var(--accent); +} + +.arch-sub { + font-size: 1.1rem; + margin-top: 2.5rem; +} + +.arch .hint { + color: var(--muted); + font-size: 0.92rem; + margin-top: 0.5rem; + max-width: var(--measure); +} + +.table-scroll { + overflow-x: auto; + margin-top: 1rem; +} + +.choices { + border-collapse: collapse; + width: 100%; + max-width: 55rem; + font-size: 0.95rem; +} + +.choices th, +.choices td { + text-align: left; + vertical-align: top; + padding: 0.7rem 0.75rem 0.7rem 0; + border-bottom: 1px solid var(--line); +} + +.choices thead th { + font-size: 0.8rem; + letter-spacing: 0.06em; + text-transform: uppercase; + color: var(--muted); +} + +.choices tbody th { + font-weight: 600; +} + +.choices td { + color: var(--muted); +}