<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Blockstor</title><link>https://blockstor.org/</link><description>Recent content on Blockstor</description><generator>Hugo</generator><language>en</language><atom:link href="https://blockstor.org/index.xml" rel="self" type="application/rss+xml"/><item><title>Building</title><link>https://blockstor.org/developer-guide/building/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/developer-guide/building/</guid><description>&lt;h2 id="binaries"&gt;Binaries&lt;/h2&gt;
&lt;p&gt;Blockstor is a Go module, &lt;code&gt;github.com/cozystack/blockstor&lt;/code&gt;. &lt;code&gt;make build&lt;/code&gt; regenerates the manifests and deep-copy code, runs &lt;code&gt;go fmt&lt;/code&gt; and &lt;code&gt;go vet&lt;/code&gt;, and builds the controller, the satellite, the CLI and the migration tool into &lt;code&gt;bin/&lt;/code&gt;. The API server builds the usual Go way:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make build
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go build -o bin/apiserver ./cmd/apiserver
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Binary&lt;/th&gt;
					&lt;th&gt;Source&lt;/th&gt;
					&lt;th&gt;What it is&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;controller&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cmd/controller&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;controller-runtime manager with the controller-side reconcilers.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;apiserver&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cmd/apiserver&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;the stateless LINSTOR-compatible REST front end.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;satellite&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cmd/satellite&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;the per-node manager that drives DRBD, LUKS and storage.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;blockstor&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cmd/blockstor&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;the operator CLI.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;linstor-migrate&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;cmd/linstor-migrate&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;the LINSTOR database converter.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Other generators: &lt;code&gt;make manifests&lt;/code&gt; regenerates the CRDs and RBAC from the kubebuilder markers in &lt;code&gt;api/v1alpha1&lt;/code&gt;, and &lt;code&gt;make generate&lt;/code&gt; regenerates the deep-copy code.&lt;/p&gt;</description></item><item><title>Overview</title><link>https://blockstor.org/docs/overview/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/overview/</guid><description>&lt;p&gt;Blockstor turns the local disks of your Kubernetes nodes into replicated block storage. Each volume is a logical volume (LVM) or a zvol (ZFS) on a node, and DRBD keeps copies of it on other nodes in sync, so a pod can lose its node and come back elsewhere with its data.&lt;/p&gt;
&lt;p&gt;Blockstor speaks the LINSTOR REST API. The clients built for LINSTOR, &lt;code&gt;linstor-csi&lt;/code&gt;, &lt;code&gt;piraeus-operator&lt;/code&gt;, &lt;code&gt;ha-controller&lt;/code&gt; and &lt;code&gt;golinstor&lt;/code&gt;, work against it unchanged. What is different is how the cluster keeps its state: everything lives in Kubernetes custom resources, and controllers converge the cluster toward it. There is no separate database to back up and no in-memory state to lose when a pod restarts.&lt;/p&gt;</description></item><item><title>Quickstart</title><link>https://blockstor.org/docs/quickstart/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/quickstart/</guid><description>&lt;p&gt;This page is the shortest path from a Kubernetes cluster with DRBD-capable nodes to a pod writing to a replicated volume. It leaves out the choices. &lt;a href="../installation/"&gt;Installation&lt;/a&gt; and &lt;a href="../csi/"&gt;Provisioning volumes with CSI&lt;/a&gt; explain them.&lt;/p&gt;
&lt;h2 id="before-you-start"&gt;Before you start&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;blockstor&lt;/code&gt; CLI this page uses is in no release up to v0.1.17. It ships in the release after that, so pick that one or a newer one for &lt;code&gt;&amp;lt;VERSION&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;You need:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Three or more worker nodes with the DRBD 9 kernel module loaded, and the ZFS module if you want ZFS pools. On Talos Linux both come from the &lt;code&gt;siderolabs/drbd&lt;/code&gt; and &lt;code&gt;siderolabs/zfs&lt;/code&gt; system extensions.&lt;/li&gt;
&lt;li&gt;One spare block device on each of those nodes. The example below uses &lt;code&gt;/dev/sdb&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cert-manager.io/"&gt;cert-manager&lt;/a&gt; in the cluster. It issues the certificates for the API.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;kubectl&lt;/code&gt; with cluster-admin access, and Go to install the &lt;code&gt;blockstor&lt;/code&gt; CLI.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The satellite image carries &lt;code&gt;drbd-utils&lt;/code&gt;, &lt;code&gt;lvm2&lt;/code&gt;, &lt;code&gt;cryptsetup&lt;/code&gt; and &lt;code&gt;zfsutils&lt;/code&gt;. Only the kernel modules come from the host.&lt;/p&gt;</description></item><item><title>Installation</title><link>https://blockstor.org/docs/installation/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/installation/</guid><description>&lt;p&gt;Blockstor installs onto an existing Kubernetes cluster. You need the storage nodes prepared, cert-manager in the cluster, and the blockstor manifests from the release you want to run.&lt;/p&gt;
&lt;h2 id="prerequisites"&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;On every storage node that will run the satellite:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The DRBD 9 kernel module, loaded (&lt;code&gt;modprobe drbd&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;For ZFS pools, the ZFS kernel module. On Talos Linux both modules come from the &lt;code&gt;siderolabs/drbd&lt;/code&gt; and &lt;code&gt;siderolabs/zfs&lt;/code&gt; system extensions.&lt;/li&gt;
&lt;li&gt;At least one spare block device, or an existing LVM volume group or ZFS pool to back the storage pools.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The satellite image already carries the userspace tools (&lt;code&gt;drbd-utils&lt;/code&gt;, &lt;code&gt;lvm2&lt;/code&gt;, &lt;code&gt;cryptsetup&lt;/code&gt;, &lt;code&gt;zfsutils&lt;/code&gt;); only the kernel modules come from the host.&lt;/p&gt;</description></item><item><title>Testing</title><link>https://blockstor.org/developer-guide/testing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/developer-guide/testing/</guid><description>&lt;p&gt;Each tier answers a different question, and a change is expected to come with tests at the lowest tier that can catch the bug.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Tier&lt;/th&gt;
					&lt;th&gt;What it covers&lt;/th&gt;
					&lt;th&gt;Where&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;L1 unit&lt;/td&gt;
					&lt;td&gt;Logic, run on every commit&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;*_test.go&lt;/code&gt; next to the code&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L2 contract, golden&lt;/td&gt;
					&lt;td&gt;Recorded &lt;code&gt;golinstor&lt;/code&gt; responses replayed against the server, byte diff&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/contract/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L3 contract, oracle&lt;/td&gt;
					&lt;td&gt;The same requests to a real LINSTOR and to blockstor, JSON diff&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/contract/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Integration&lt;/td&gt;
					&lt;td&gt;Controllers and REST against a real Kubernetes API server (envtest), satellites faked&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/integration/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L4 integration on DRBD&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;make smoke&lt;/code&gt; on the stand&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/smoke*.sh&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L5 e2e&lt;/td&gt;
					&lt;td&gt;csi-sanity and piraeus-operator e2e on the stand&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/e2e/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L6 operator CLI&lt;/td&gt;
					&lt;td&gt;The real CLI, through REST and the satellite, down to the DRBD kernel state&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/e2e/cli-matrix/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;L7 replay&lt;/td&gt;
					&lt;td&gt;Operator workflows replayed, and the CLI parity diff against upstream&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;tests/operator-harness/&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="unit-tests-and-lint"&gt;Unit tests and lint&lt;/h2&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;go &lt;span class="nb"&gt;test&lt;/span&gt; ./...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;make lint
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Tests that need envtest skip themselves when &lt;code&gt;KUBEBUILDER_ASSETS&lt;/code&gt; is not set. &lt;code&gt;make test&lt;/code&gt; downloads the envtest binaries and runs the suite with them. &lt;code&gt;make lint&lt;/code&gt; must be clean before a push.&lt;/p&gt;</description></item><item><title>Provisioning volumes with CSI</title><link>https://blockstor.org/docs/csi/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/csi/</guid><description>&lt;p&gt;Kubernetes provisions blockstor volumes through the LINSTOR CSI driver, &lt;code&gt;linstor-csi&lt;/code&gt;. The driver speaks the LINSTOR REST API, and blockstor serves that API, so you point the driver at the blockstor API server instead of a LINSTOR controller.&lt;/p&gt;
&lt;h2 id="what-to-deploy"&gt;What to deploy&lt;/h2&gt;
&lt;p&gt;Piraeus normally ships a whole LINSTOR distribution. With blockstor you take only the client side:&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Piraeus component&lt;/th&gt;
					&lt;th&gt;With blockstor&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;linstor-controller&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Skip. The blockstor controller and API server replace it.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;linstor-satellite&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Skip. Blockstor runs its own satellites.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;linstor-csi&lt;/code&gt; (controller and node plugin)&lt;/td&gt;
					&lt;td&gt;Deploy, pointed at the blockstor API server.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;HA controller, &lt;code&gt;drbd-reactor&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Optional. Point them at the API server the same way as CSI.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="point-the-driver-at-blockstor"&gt;Point the driver at blockstor&lt;/h2&gt;
&lt;p&gt;The API server listens with mutual TLS on port 3371 of the &lt;code&gt;blockstor-apiserver&lt;/code&gt; Service in &lt;code&gt;blockstor-system&lt;/code&gt;. The installation created a client certificate for in-cluster clients: the Secret &lt;code&gt;blockstor-apiserver-client-tls&lt;/code&gt; carries &lt;code&gt;tls.crt&lt;/code&gt;, &lt;code&gt;tls.key&lt;/code&gt; and &lt;code&gt;ca.crt&lt;/code&gt;. A pod in &lt;code&gt;blockstor-system&lt;/code&gt; can mount that Secret directly; for a driver in another namespace, see &lt;a href="#client-certificates-in-another-namespace"&gt;Client certificates in another namespace&lt;/a&gt; first. Mount the Secret into both the CSI controller and the CSI node plugin, and set the endpoint and certificate paths:&lt;/p&gt;</description></item><item><title>Architecture</title><link>https://blockstor.org/docs/architecture/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/architecture/</guid><description>&lt;p&gt;Blockstor is built like a Kubernetes operator. The desired state of every node, pool and volume is a custom resource, and controllers keep working until the cluster matches it. Nothing important lives only in a process&amp;rsquo;s memory, so any blockstor pod can restart without losing state.&lt;/p&gt;
&lt;h2 id="components"&gt;Components&lt;/h2&gt;
&lt;pre class="mermaid"&gt;flowchart TB
 subgraph clients[&amp;#34;Clients&amp;#34;]
 csi[&amp;#34;linstor-csi&amp;#34;]
 other[&amp;#34;piraeus-operator, ha-controller, golinstor&amp;#34;]
 cli[&amp;#34;blockstor CLI&amp;#34;]
 end

 subgraph k8s[&amp;#34;Kubernetes API&amp;#34;]
 crds[(&amp;#34;blockstor custom resources&amp;#34;)]
 end

 subgraph cp[&amp;#34;blockstor-system&amp;#34;]
 api[&amp;#34;blockstor-apiserver&amp;lt;br/&amp;gt;LINSTOR-compatible REST, mTLS&amp;#34;]
 ctrl[&amp;#34;blockstor-controller&amp;lt;br/&amp;gt;placement and allocation&amp;#34;]
 end

 subgraph nodes[&amp;#34;Storage nodes&amp;#34;]
 sat1[&amp;#34;satellite on node 1&amp;#34;]
 sat2[&amp;#34;satellite on node 2&amp;#34;]
 sat3[&amp;#34;satellite on node 3&amp;#34;]
 end

 csi --&amp;gt;|REST| api
 other --&amp;gt;|REST| api
 cli --&amp;gt;|kubeconfig| crds
 api &amp;lt;--&amp;gt; crds
 ctrl &amp;lt;--&amp;gt; crds
 crds &amp;lt;--&amp;gt; sat1
 crds &amp;lt;--&amp;gt; sat2
 crds &amp;lt;--&amp;gt; sat3&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;blockstor-apiserver&lt;/strong&gt; serves the LINSTOR REST API over mutual TLS. It does not keep state of its own: every request becomes a read or a write of custom resources. It runs as several replicas behind one Service.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;blockstor-controller&lt;/strong&gt; watches the custom resources and fills in what users do not choose: which nodes get replicas, the DRBD port, minor numbers and node IDs, and tiebreakers and quorum settings.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;blockstor-satellite&lt;/strong&gt; runs on every storage node. It creates the logical volumes or zvols, sets up LUKS and DRBD, and reports what it sees back into the custom resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;The &lt;code&gt;blockstor&lt;/code&gt; CLI&lt;/strong&gt; reads and writes the custom resources directly with your kubeconfig, without going through the REST API.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There is no direct connection between the controller and the satellites. Everything goes through the Kubernetes API.&lt;/p&gt;</description></item><item><title>Repository layout</title><link>https://blockstor.org/developer-guide/repository/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/developer-guide/repository/</guid><description>&lt;pre tabindex="0"&gt;&lt;code&gt;api/v1alpha1/ CRD types and kubebuilder markers
cmd/ controller, apiserver, satellite, blockstor CLI, linstor-migrate
internal/controller/ controller-side reconcilers
internal/cli/ the blockstor CLI
pkg/ REST, stores, satellite, storage, DRBD, LUKS, placer, dispatcher
config/ generated CRDs, RBAC and kustomize bases
stand/ the Talos and QEMU dev stand and the reference manifests
tests/ contract, integration, e2e, cli-matrix and replay suites
docs/ design notes, parity deltas and audit history
site/ this website
&lt;/code&gt;&lt;/pre&gt;&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Path&lt;/th&gt;
					&lt;th&gt;What lives there&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;api/v1alpha1/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The CRD types. &lt;code&gt;make manifests&lt;/code&gt; turns their markers into &lt;code&gt;config/crd/bases/&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/api/v1/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The REST wire types, shaped like upstream LINSTOR&amp;rsquo;s, and the layer-stack resolver.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/rest/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The LINSTOR-compatible REST handlers.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/store/&lt;/code&gt;, &lt;code&gt;pkg/store/k8s/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The in-memory and CRD-backed stores behind one &lt;code&gt;store.Store&lt;/code&gt; interface, and the translation between wire and CRD shapes.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;internal/controller/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Controller-side reconcilers: resources, definitions, groups, pools, snapshots, nodes, plus tiebreakers, auto-diskful, eviction, rebalancing and migration.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/satellite/controllers/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Satellite-side reconcilers for resources, pools, snapshots and physical devices, and the &lt;code&gt;events2&lt;/code&gt; observer.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/satellite/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The DRBD, LUKS and storage layer reconciler and snapshot shipping.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/storage/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Storage providers: LVM, LVM-thin, ZFS, ZFS-thin, loopfile and file.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/drbd/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;drbdadm&lt;/code&gt; and &lt;code&gt;drbdsetup&lt;/code&gt; wrappers, the &lt;code&gt;.res&lt;/code&gt; renderer, the &lt;code&gt;events2&lt;/code&gt; parser and the options resolver.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/luks/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The &lt;code&gt;cryptsetup&lt;/code&gt; wrapper.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/placer/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The autoplacer: capacity-weighted, anti-affinity aware.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;pkg/dispatcher/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Translates CRDs into the desired state a satellite applies: layers, options, passphrases.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;internal/cli/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The &lt;code&gt;blockstor&lt;/code&gt; CLI. Its design is in &lt;a href="https://github.com/cozystack/blockstor/blob/main/docs/cli-design.md"&gt;&lt;code&gt;docs/cli-design.md&lt;/code&gt;&lt;/a&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;cmd/linstor-migrate/&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;The LINSTOR database converter.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;</description></item><item><title>Networking and ports</title><link>https://blockstor.org/docs/networking/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/networking/</guid><description>&lt;p&gt;Blockstor&amp;rsquo;s components do not talk to each other directly. They read and write the custom resources through the Kubernetes API, and the Kubernetes API is the only thing every component needs to reach. The listeners below exist for clients, for kubelet probes, and for the data path between storage nodes.&lt;/p&gt;
&lt;h2 id="listeners"&gt;Listeners&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Component&lt;/th&gt;
					&lt;th&gt;Port&lt;/th&gt;
					&lt;th&gt;Protocol&lt;/th&gt;
					&lt;th&gt;Who connects&lt;/th&gt;
					&lt;th&gt;Reachable from&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;blockstor-apiserver&lt;/td&gt;
					&lt;td&gt;3371&lt;/td&gt;
					&lt;td&gt;HTTPS with mutual TLS&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;linstor-csi&lt;/code&gt;, piraeus-operator, &lt;code&gt;ha-controller&lt;/code&gt;, the &lt;code&gt;linstor&lt;/code&gt; client&lt;/td&gt;
					&lt;td&gt;The Services &lt;code&gt;blockstor-apiserver&lt;/code&gt; and &lt;code&gt;blockstor-controller&lt;/code&gt; in &lt;code&gt;blockstor-system&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;blockstor-apiserver&lt;/td&gt;
					&lt;td&gt;3370&lt;/td&gt;
					&lt;td&gt;Plain HTTP, no authentication&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;kubectl port-forward&lt;/code&gt; for debugging&lt;/td&gt;
					&lt;td&gt;The pod&amp;rsquo;s loopback address only. Not in any Service.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;blockstor-apiserver&lt;/td&gt;
					&lt;td&gt;8081&lt;/td&gt;
					&lt;td&gt;HTTP &lt;code&gt;/healthz&lt;/code&gt;, &lt;code&gt;/readyz&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;kubelet&lt;/td&gt;
					&lt;td&gt;The pod&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;blockstor-controller&lt;/td&gt;
					&lt;td&gt;8081&lt;/td&gt;
					&lt;td&gt;HTTP &lt;code&gt;/healthz&lt;/code&gt;, &lt;code&gt;/readyz&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;kubelet&lt;/td&gt;
					&lt;td&gt;The pod&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;blockstor-satellite&lt;/td&gt;
					&lt;td&gt;3372&lt;/td&gt;
					&lt;td&gt;HTTPS with mutual TLS&lt;/td&gt;
					&lt;td&gt;Other satellites, to fetch a snapshot stream&lt;/td&gt;
					&lt;td&gt;The node&amp;rsquo;s own address&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;blockstor-satellite&lt;/td&gt;
					&lt;td&gt;9101&lt;/td&gt;
					&lt;td&gt;HTTP &lt;code&gt;/healthz&lt;/code&gt;, &lt;code&gt;/readyz&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;kubelet&lt;/td&gt;
					&lt;td&gt;The node&amp;rsquo;s own address&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DRBD (kernel)&lt;/td&gt;
					&lt;td&gt;20000 to 20999&lt;/td&gt;
					&lt;td&gt;DRBD replication, TCP&lt;/td&gt;
					&lt;td&gt;The other replicas of the same volume&lt;/td&gt;
					&lt;td&gt;The node&amp;rsquo;s own address&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The satellite runs with host networking, so its ports are open on the node itself, not on a pod IP. The manifests set &lt;code&gt;hostPort&lt;/code&gt; 3372 and 9101 to make this explicit. Both have to be free on every storage node. Port 3372 is outside the DRBD port range and clear of the LINSTOR ports and of port 9100, where node-exporter usually listens on the host.&lt;/p&gt;</description></item><item><title>Internals</title><link>https://blockstor.org/developer-guide/internals/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/developer-guide/internals/</guid><description>&lt;p&gt;This page is about the invariants whose violation would quietly corrupt cluster state. For the user-level picture, see &lt;a href="https://blockstor.org/docs/architecture/"&gt;Architecture&lt;/a&gt;. For a tour of the code, follow the call graph from &lt;code&gt;cmd/controller/main.go&lt;/code&gt; and &lt;code&gt;cmd/satellite/main.go&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id="spec-and-status"&gt;Spec and Status&lt;/h2&gt;
&lt;p&gt;Every blockstor CRD has two halves:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Spec&lt;/strong&gt; is desired state. Users, REST handlers and the controller&amp;rsquo;s placement write it. The satellite writes Spec in two places: &lt;code&gt;Resource.Spec.Flags&lt;/code&gt; (and &lt;code&gt;ToggleDiskCancel&lt;/code&gt;) while it carries out a toggle-disk, and the discovered volume groups and ZFS pools in &lt;code&gt;Node.Spec.Props&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Status&lt;/strong&gt; is observed state. The satellite writes what it reads from the kernel (&lt;code&gt;drbdsetup events2&lt;/code&gt;, &lt;code&gt;drbdadm status&lt;/code&gt;, &lt;code&gt;lvs&lt;/code&gt;), and the controller&amp;rsquo;s allocators write values derived from Spec. The REST API writes Status in one place: the resource activate endpoint with &lt;code&gt;?reallocate-port=true&lt;/code&gt; clears &lt;code&gt;Resource.Status.DRBDPort&lt;/code&gt; through a Status patch. The allocator only fills an empty &lt;code&gt;Spec.DRBDPort&lt;/code&gt;, and the controller copies the spec value back into &lt;code&gt;Status&lt;/code&gt;, so the port does not change.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A whole-object &lt;code&gt;Update&lt;/code&gt; is unsafe whenever both halves have different writers: a Spec write in flight would overwrite a concurrent Status write, and the other way round. Status goes through the Status subresource. Fields with more than one writer, such as &lt;code&gt;Resource.Status.DRBDPort&lt;/code&gt; from the controller next to &lt;code&gt;DiskState&lt;/code&gt; and &lt;code&gt;CurrentGi&lt;/code&gt; from the satellite, go through Server-Side Apply with separate field managers, &lt;code&gt;blockstor-controller&lt;/code&gt; and &lt;code&gt;blockstor-satellite&lt;/code&gt;, so each side owns only its own fields.&lt;/p&gt;</description></item><item><title>Resources</title><link>https://blockstor.org/docs/resources/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/resources/</guid><description>&lt;p&gt;Everything blockstor manages is a cluster-scoped custom resource in the &lt;code&gt;blockstor.cozystack.io/v1alpha1&lt;/code&gt; API group. You can create and change them with &lt;code&gt;kubectl&lt;/code&gt; or GitOps, with the &lt;a href="../cli/"&gt;&lt;code&gt;blockstor&lt;/code&gt; CLI&lt;/a&gt;, or through the REST API. All three edit the same objects.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Kind&lt;/th&gt;
					&lt;th&gt;What it describes&lt;/th&gt;
					&lt;th&gt;Name&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Node&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;A storage node and its replication address&lt;/td&gt;
					&lt;td&gt;the Kubernetes node name&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;StoragePool&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;A pool of space on one node&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;&amp;lt;pool&amp;gt;.&amp;lt;node&amp;gt;&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;PhysicalDevice&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;A raw disk the satellite found on a node&lt;/td&gt;
					&lt;td&gt;set by the satellite&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ResourceGroup&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;A placement and replication policy&lt;/td&gt;
					&lt;td&gt;any&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ResourceDefinition&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;One volume set: sizes, layers, policy&lt;/td&gt;
					&lt;td&gt;any&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Resource&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;One replica of a definition on one node&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;&amp;lt;definition&amp;gt;.&amp;lt;node&amp;gt;&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;Snapshot&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;A snapshot of a definition&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;&amp;lt;definition&amp;gt;.&amp;lt;snapshot&amp;gt;&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;ControllerConfig&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Cluster-wide settings&lt;/td&gt;
					&lt;td&gt;always &lt;code&gt;default&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The naming rules are enforced by the API server, so a &lt;code&gt;Resource&lt;/code&gt; or &lt;code&gt;StoragePool&lt;/code&gt; with a name that does not match its spec is refused. Each kind has printer columns, so &lt;code&gt;kubectl get resources&lt;/code&gt; or &lt;code&gt;kubectl get storagepools&lt;/code&gt; gives a useful table.&lt;/p&gt;</description></item><item><title>Contributing</title><link>https://blockstor.org/developer-guide/contributing/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/developer-guide/contributing/</guid><description>&lt;h2 id="licensing-apache-20-clean-room"&gt;Licensing: Apache 2.0, clean room&lt;/h2&gt;
&lt;p&gt;Blockstor is Apache 2.0. Do not copy structure or code from GPL sources, in particular &lt;code&gt;linstor-server&lt;/code&gt; and &lt;code&gt;drbd-utils&lt;/code&gt;. You may read them to understand behaviour, never to reproduce how they are written.&lt;/p&gt;
&lt;p&gt;Hard rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No GPL-licensed spec files in the tree. LINSTOR&amp;rsquo;s &lt;code&gt;rest_v1_openapi.yaml&lt;/code&gt; declares GPLv3, and code generated from it is a derivative work.&lt;/li&gt;
&lt;li&gt;No code generated from a GPL source by any tool.&lt;/li&gt;
&lt;li&gt;No Go dependency under GPL, AGPL, LGPL or SSPL. CI enforces this through &lt;a href="https://github.com/cozystack/blockstor/blob/main/.github/workflows/license-check.yml"&gt;&lt;code&gt;license-check.yml&lt;/code&gt;&lt;/a&gt;, which runs &lt;code&gt;go-licenses check --disallowed_types=forbidden,restricted&lt;/code&gt; over &lt;code&gt;./cmd/...&lt;/code&gt;. That is a denylist: it fails on licenses &lt;code&gt;go-licenses&lt;/code&gt; classifies as forbidden or restricted, and lets everything else through, so check a new dependency&amp;rsquo;s license yourself.&lt;/li&gt;
&lt;li&gt;Every Go source file carries an &lt;code&gt;SPDX-License-Identifier: Apache-2.0&lt;/code&gt; header.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Apache-2.0 projects that are safe to study and depend on: &lt;a href="https://github.com/LINBIT/golinstor"&gt;&lt;code&gt;golinstor&lt;/code&gt;&lt;/a&gt;, the authoritative description of the REST wire shape; &lt;a href="https://github.com/piraeusdatastore/piraeus-operator"&gt;&lt;code&gt;piraeus-operator&lt;/code&gt;&lt;/a&gt;; &lt;a href="https://github.com/piraeusdatastore/linstor-csi"&gt;&lt;code&gt;linstor-csi&lt;/code&gt;&lt;/a&gt;; &lt;a href="https://github.com/LINBIT/drbd-reactor"&gt;&lt;code&gt;drbd-reactor&lt;/code&gt;&lt;/a&gt;; and the public DRBD 9 documentation and man pages.&lt;/p&gt;</description></item><item><title>Layer stack</title><link>https://blockstor.org/docs/layer-stack/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/layer-stack/</guid><description>&lt;p&gt;Each volume is built from layers. The bottom layer allocates space on the node, and each layer above adds something on top of the one below. The topmost layer is the device the pod uses.&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Layer&lt;/th&gt;
					&lt;th&gt;What it does&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;STORAGE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Allocates the block device: an LVM or LVM-thin logical volume, a ZFS zvol, or a file. Every stack ends with it.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;LUKS&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Encrypts the device below it with &lt;code&gt;cryptsetup&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;DRBD&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Replicates the device to the other nodes over the network.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="supported-stacks"&gt;Supported stacks&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Stack&lt;/th&gt;
					&lt;th&gt;Use it for&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;DRBD&lt;/code&gt;, &lt;code&gt;STORAGE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Replicated volumes. This is the default.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;DRBD&lt;/code&gt;, &lt;code&gt;LUKS&lt;/code&gt;, &lt;code&gt;STORAGE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Replicated and encrypted at rest. Each replica encrypts its own copy.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;LUKS&lt;/code&gt;, &lt;code&gt;STORAGE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Encrypted volume with a single replica and no DRBD.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;STORAGE&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Plain local volume with a single replica, for caches and scratch space.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;These four are the only orders blockstor accepts. Other orders, such as LUKS on top of DRBD, are refused when the definition is created.&lt;/p&gt;</description></item><item><title>Using the CLI</title><link>https://blockstor.org/docs/cli/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/cli/</guid><description>&lt;p&gt;&lt;code&gt;blockstor&lt;/code&gt; is the command-line tool for blockstor. It reads your kubeconfig and works on the blockstor custom resources directly, so it needs no port-forward and no API certificate: if &lt;code&gt;kubectl&lt;/code&gt; can reach the cluster, so can &lt;code&gt;blockstor&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The commands follow the &lt;code&gt;linstor&lt;/code&gt; client&amp;rsquo;s noun-verb grammar, with the same short aliases, so runbooks written for LINSTOR mostly carry over by changing the command name. Tools that speak the LINSTOR REST API, such as &lt;code&gt;linstor-csi&lt;/code&gt;, keep working against the blockstor API server. The examples on this page use &lt;code&gt;blockstor&lt;/code&gt;.&lt;/p&gt;</description></item><item><title>Day-2 operations</title><link>https://blockstor.org/docs/operations/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/operations/</guid><description>&lt;p&gt;Everything on this page can be done with the &lt;code&gt;blockstor&lt;/code&gt; CLI or by editing the custom resources with &lt;code&gt;kubectl&lt;/code&gt;. The CLI commands are shown; &lt;a href="../resources/"&gt;Resources&lt;/a&gt; has the matching YAML.&lt;/p&gt;
&lt;h2 id="reading-health"&gt;Reading health&lt;/h2&gt;
&lt;p&gt;Start with the replicas that are not healthy:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;blockstor resource list --faulty
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A replica counts as faulty when one of its volumes has a disk state that has not settled, or when one of its DRBD connections is anything other than &lt;code&gt;Connected&lt;/code&gt;. A replica the satellite has not reported on yet is not listed, so an empty result means &amp;ldquo;nothing broken that we can see&amp;rdquo;, not &amp;ldquo;everything verified&amp;rdquo;.&lt;/p&gt;</description></item><item><title>Migrating from LINSTOR</title><link>https://blockstor.org/docs/migration/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/migration/</guid><description>&lt;p&gt;Blockstor can take over a LINSTOR cluster that runs on Kubernetes. The &lt;code&gt;linstor-migrate&lt;/code&gt; tool converts LINSTOR&amp;rsquo;s database into blockstor resources, and blockstor then adopts the volumes and DRBD metadata that are already on disk. No data is copied or resynced, and replica placement does not change.&lt;/p&gt;
&lt;p&gt;Do this in a maintenance window, after a full rehearsal on a staging cluster.&lt;/p&gt;
&lt;h2 id="what-moves-and-what-does-not"&gt;What moves and what does not&lt;/h2&gt;
&lt;p&gt;Converted:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nodes, storage pools, resource groups, resource definitions with their volumes, replicas, and successful snapshots;&lt;/li&gt;
&lt;li&gt;controller-wide DRBD options;&lt;/li&gt;
&lt;li&gt;DRBD minor numbers, node IDs and the shared secret, unchanged;&lt;/li&gt;
&lt;li&gt;DRBD ports, if you capture them from the running nodes (step 3 below).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not converted, and listed in the tool&amp;rsquo;s report:&lt;/p&gt;</description></item><item><title>Troubleshooting</title><link>https://blockstor.org/docs/troubleshooting/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/troubleshooting/</guid><description>&lt;p&gt;Each section below is one symptom. &lt;a href="../operations/#reading-health"&gt;Day-2 operations&lt;/a&gt; explains the commands used to look at state.&lt;/p&gt;
&lt;p&gt;Where a fix needs the DRBD tools, run them inside the satellite pod of the node in question:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;POD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$(&lt;/span&gt;kubectl -n blockstor-system get pod -l &lt;span class="nv"&gt;app&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;blockstor-satellite --field-selector spec.nodeName&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;node&amp;gt; -o name&lt;span class="k"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl -n blockstor-system &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nv"&gt;$POD&lt;/span&gt; -c satellite -- drbdadm status &amp;lt;definition&amp;gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="replicas-stay-in-connecting"&gt;Replicas stay in Connecting&lt;/h2&gt;
&lt;p&gt;Look at the connection state of the replica, and at the node addresses:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get resource &amp;lt;definition&amp;gt;.&amp;lt;node&amp;gt; -o &lt;span class="nv"&gt;jsonpath&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;{range .status.connections[*]}{.peerNodeName}={.message} {end}&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;kubectl get nodes.blockstor.cozystack.io -o wide
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A peer stuck in &lt;code&gt;Connecting&lt;/code&gt; cannot reach the other replica&amp;rsquo;s address and port. The two causes are a wrong address and a blocked port.&lt;/p&gt;</description></item><item><title>Compatibility with LINSTOR</title><link>https://blockstor.org/docs/compatibility/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://blockstor.org/docs/compatibility/</guid><description>&lt;p&gt;Blockstor implements the LINSTOR REST API, so &lt;code&gt;linstor-csi&lt;/code&gt;, &lt;code&gt;piraeus-operator&lt;/code&gt;, &lt;code&gt;ha-controller&lt;/code&gt;, &lt;code&gt;golinstor&lt;/code&gt; and the &lt;code&gt;linstor&lt;/code&gt; client work against it. This page lists the differences an operator can notice. The full list, kept for the project&amp;rsquo;s parity tests, is in &lt;a href="https://github.com/cozystack/blockstor/blob/main/docs/cli-parity-known-deltas.md"&gt;&lt;code&gt;docs/cli-parity-known-deltas.md&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="not-implemented"&gt;Not implemented&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Feature&lt;/th&gt;
					&lt;th&gt;What happens&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Backups, backup shipping, remotes such as S3&lt;/td&gt;
					&lt;td&gt;The API answers &lt;code&gt;501 Not Implemented&lt;/code&gt;. Use snapshots and restore inside the cluster.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Schedules&lt;/td&gt;
					&lt;td&gt;The API answers &lt;code&gt;501 Not Implemented&lt;/code&gt;.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;advise&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Not available.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;In-place snapshot rollback&lt;/td&gt;
					&lt;td&gt;Refused on purpose, because it destroys every newer snapshot. Restore the snapshot into a new volume instead.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Per-connection DRBD peer options (&lt;code&gt;resource-connection drbd-peer-options&lt;/code&gt;)&lt;/td&gt;
					&lt;td&gt;Not available.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;External DRBD metadata (&lt;code&gt;StorPoolNameDrbdMeta&lt;/code&gt;)&lt;/td&gt;
					&lt;td&gt;Not supported yet.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Other providers: SPDK, NVMe-oF, OpenFlex, Exos&lt;/td&gt;
					&lt;td&gt;Not supported.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="behaves-differently"&gt;Behaves differently&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;Area&lt;/th&gt;
					&lt;th&gt;LINSTOR&lt;/th&gt;
					&lt;th&gt;Blockstor&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;Active network interface&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;linstor node interface modify --active&lt;/code&gt; switches the satellite&amp;rsquo;s connection to another interface.&lt;/td&gt;
					&lt;td&gt;Has no effect. DRBD uses the address picked in the order described in &lt;a href="../networking/#which-address-replication-uses"&gt;Networking and ports&lt;/a&gt;, not an active flag. A change of address reaches DRBD after the node&amp;rsquo;s satellite pod restarts.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Default action on lost quorum&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;on-no-quorum io-error&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;on-no-quorum suspend-io&lt;/code&gt;: I/O blocks until quorum returns and then resumes, so a replica that was cut off does not get stuck after the network heals.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Shrinking a volume&lt;/td&gt;
					&lt;td&gt;Allowed for stacks that support it.&lt;/td&gt;
					&lt;td&gt;Refused. &lt;code&gt;--force&lt;/code&gt; lowers the size in the definition only; the backing devices and DRBD keep their size.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DRBD ports and minors&lt;/td&gt;
					&lt;td&gt;Ports from 7000, minors from 1000.&lt;/td&gt;
					&lt;td&gt;Ports from 20000 to 20999, minors from 20000. Blockstor can run on the same nodes as LINSTOR without collisions. Change the port range per node with the &lt;code&gt;DrbdOptions/TcpPortRange&lt;/code&gt; property.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Spawning more replicas than there are nodes&lt;/td&gt;
					&lt;td&gt;Fails and places nothing.&lt;/td&gt;
					&lt;td&gt;Places as many replicas as it can, reports the shortfall, and adds the rest when more nodes join.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Turning a tiebreaker into a replica with data&lt;/td&gt;
					&lt;td&gt;Can skip the initial sync.&lt;/td&gt;
					&lt;td&gt;Runs a full sync, to avoid a DRBD split where both sides refuse to connect.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Removing a replica below quorum&lt;/td&gt;
					&lt;td&gt;Removes the quorum options from the definition and prints a note.&lt;/td&gt;
					&lt;td&gt;Sets &lt;code&gt;quorum off&lt;/code&gt; explicitly, with the same effect on DRBD.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;DRBD option values&lt;/td&gt;
					&lt;td&gt;Validated against the DRBD schema.&lt;/td&gt;
					&lt;td&gt;Not validated: a wrong value is stored and fails later on the node. Check values before you set them.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;Autoplacer weights&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;MaxFreeSpace&lt;/code&gt; is 1, the other weights 0.&lt;/td&gt;
					&lt;td&gt;All four weights default to 1.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;controller version&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Reports a git commit hash.&lt;/td&gt;
					&lt;td&gt;Through the REST API, release images report the commit they were built from. A development build that was not given one reports a placeholder instead.&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;code&gt;node info&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;Shows OS, CPU and memory.&lt;/td&gt;
					&lt;td&gt;These fields are empty.&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id="using-the-linstor-client"&gt;Using the &lt;code&gt;linstor&lt;/code&gt; client&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;linstor&lt;/code&gt; client works against blockstor&amp;rsquo;s API server. Port-forward the plain HTTP port and point the client at it. That port has no authentication. It listens on the pod&amp;rsquo;s loopback address, so a port-forward reaches it and other pods do not; see &lt;a href="../networking/#the-api-server"&gt;Networking and ports&lt;/a&gt;.&lt;/p&gt;</description></item></channel></rss>