BLiNQ NetworksLifecycle Service All guides/Overview

Operations Guide

Shut down NetLiNQ without breaking it.

Two systemd units scale the platform down in dependency order before the machine stops, drain the nodes, and put everything back exactly as it was after boot. They ship with every install and are switched off until you arm them.

2 systemd units On by default Replica snapshots Restores on boot

Overview

What the lifecycle service does

Pulling power on a running cluster leaves databases mid-write and pods wedged on nodes that never drained. The lifecycle service does that work deliberately: it records how many replicas every workload has, scales them to zero in dependency order, drains the nodes, then stops kubelet. On the next boot it reverses all of it.

Two systemd units, both backed by scripts in /opt/netlinq/installation/:

netlinq-shutdown.serviceMaster: syncs state, scales apps to 0, drains nodes, stops local services. Worker: drains itself, stops local services. Runs on systemctl stop or OS shutdown, via shutdown_netlinq.sh.
netlinq-startup.serviceMaster: uncordons every node and restores the pending replica snapshot. Worker: uncordons itself and waits for the master. Runs automatically after boot, via startup_netlinq.sh.

Day to day nothing calls these by hand — a normal sudo reboot or sudo shutdown -h now runs the shutdown hook, and the restore runs itself on the way back up. To drive them directly:

through systemd
# scale the platform down and drain the nodes
$ sudo systemctl stop netlinq-shutdown.service

# bring it back: uncordon and restore every replica count
$ sudo systemctl restart netlinq-startup.service

# re-arm the shutdown hook for the next reboot
$ sudo systemctl start netlinq-shutdown.service
Why restart, not start?
netlinq-startup.service has RemainAfterExit=yes, so it is still active from boot and start does nothing. restart is what re-runs the restore — it is what the installer itself uses.
Why start the shutdown unit again?
Stopping it left it inactive, and an inactive oneshot unit never runs its ExecStop — the next reboot would be an uncontrolled one. Starting it is free: its ExecStart is /bin/true.
the scripts directly
$ sudo /opt/netlinq/installation/shutdown_netlinq.sh
$ sudo /opt/netlinq/installation/startup_netlinq.sh
Installed before the units were split? The old single netlinq-shutdown.service carried ExecStart=startup_netlinq.sh, so on those hosts systemctl start netlinq-shutdown.service is what scaled everything back up. Check which build you are on with systemctl cat netlinq-shutdown.service: ExecStart=/bin/true means the restore lives in netlinq-startup.service.
Why two units? A oneshot unit only runs its ExecStop while it is active, so when both lived on one unit a single failed restore silently left the machine with no controlled shutdown at all. The shutdown unit now has ExecStart=/bin/true — arming the hook cannot fail.

Step 1

Turn it on

The shipped netlinq_onprem_install.vars sets enable_netlinq_shutdown_service=true, so a stock install enables and starts both units for you. Drop the variable entirely and the installer falls back to leaving them off.

During install

Leave the variable at true

The installer enables and starts both units, and applies the host recovery settings first. Set it to "false" if you do not want them armed.

After installing

Enable them by hand

On a cluster that is already running, enable both units on every node.

netlinq_onprem_install.vars
enable_netlinq_shutdown_service="true"
every node
$ sudo systemctl enable --now netlinq-startup.service netlinq-shutdown.service
Enabling alone is not enough on an existing cluster. Run the host preparation below first, or kubelet keeps its own shutdown grace period and competes with the script for the same nodes.

Step 2

Prepare an existing cluster

Both paths set the two kubelet shutdown grace periods to 0s, so kubelet hands the shutdown over instead of running its own partial eviction, and write persistent IPv6 settings for physical, bond, bridge and VLAN uplinks.

Single node

the node
$ sudo bash installation/configure_host_recovery.sh --all

It restarts kubelet only if the config actually changed. Run it before enabling the units.

Multi-node cluster

Install and enable the units on every node first. Then run the migration from a control-plane host where kubectl get nodes works for your user.

control-plane host
$ cd installation
$ source kubespray-venv/bin/activate
$ kubectl get nodes
$ ansible-playbook -i kubespray-repo/inventory/mycluster/hosts.yaml \
    migrate_kubelet_shutdown.yml --ask-become-pass
Passwordless sudo
Drop --ask-become-pass.
Safety of the playbook
It checks every node and shutdown hook before changing any node, then updates one node at a time and waits for Ready before continuing.
Hostname mismatch
Inventory hostnames must match Kubernetes node names. If one differs, set kube_override_hostname for that host.
After a Kubespray upgrade
A later installation/upgrade_kubespray.sh run keeps both 0s values in the regenerated inventory.

Reference

What happens at shutdown

On a master node, in this order:

1 · Sync state
Live replica counts are written to replicas.json, so any manual scaling done during operations survives the reboot.
2 · Pre-check
Validates the environment and reports pods in CrashLoopBackOff or other unhealthy states. It proceeds anyway, and says so in the journal.
3 · Snapshot
A complete desired-replica snapshot is written before the first scale-down, merged with any restore still owed from an earlier shutdown.
4 · HPAs removed
HorizontalPodAutoscalers are backed up, then deleted — an HPA with minReplicas > 0 would pause the scale-down at its own floor. If the backup fails, the HPAs are left in place.
5 · Operators first
strimzi-cluster-operator and netlinq-cluster-operator are scaled to zero before anything they manage, or they reconcile their workloads straight back up.
6 · Ordered scale-down
Workloads named in shutdown_order stop in that order. Everything else follows in whatever order the API returns, with a warning in the journal naming each one.
7 · Drain and stop
Nodes are drained to evict what is left, then kubelet and the container runtimes stop — this host last, because stopping the local kubelet on a master takes the API server with it.
The ordered list mixes kinds on purpose. keycloak is a Deployment but belongs in the order because it must stop after the applications that authenticate against it and before postgresql, the database it depends on.

The unit allows 1800s (TimeoutStopSec) while the script budgets 1500s for itself, so it winds down on its own terms instead of being killed part-way through a drain.

Reference

What happens after boot

1 · Wait for the API
The unit is ordered after the network, containerd and kubelet, so this is usually immediate; otherwise it polls.
2 · Uncordon
The master uncordons every node; a worker uncordons itself and waits for the master.
3 · Scale back up
Workloads are restored from the pending snapshot, operators last, so nothing is reconciled back up before its dependencies are running.

The restore is allowed to take a while: up to 600s waiting on databases and another 600s on the remaining workloads, inside a 2400s TimeoutStartSec. On failure it retries up to three times in three hours, then stops. The unit is deliberately not masked to success — a failed restore stays visible in systemctl status and no longer affects the shutdown hook.

Reference

State files

replica_counts_snapshot_*.txtLive replica counts captured before the first scale-down. A completion marker keeps a partial capture from ever being replayed. Startup consumes it after a successful restore.
replicas.jsonShutdown order per namespace, the operator deployment list, and the counts kept for reference.

Both live in /opt/netlinq/installation/. Until the snapshot is consumed, a later shutdown merges it with live counts, so a workload still sitting at zero keeps its pending count instead of being recorded as zero.

replicas.json
{
  "netlinq": {
    "shutdown_order": [
      "diesel", "elastic", "keycloak",
      "kibana", "mongo-tenant", "postgresql"
    ]
  },
  "kafka": {
    "operator_deployments": [
      "strimzi-cluster-operator", "netlinq-cluster-operator"
    ]
  }
}

A new database with dependents belongs in .netlinq.shutdown_order. Leave it out and it stops in arbitrary order among the other workloads — the journal warns by name when that happens.

Commit the current state without shutting anything down:

master node
$ sudo /opt/netlinq/installation/shutdown_netlinq.sh --sync-only

Fallback

Scaling down by hand

This is the old runbook — what the service now does for you. Use it when the units are not installed, or when you want the platform down without stopping the machine.

Record the replica counts first. Nothing captures them for you here, and kubectl scale does not remember what a workload was set to.
1 · record what is running
$ kubectl -n netlinq get deploy,sts \
    -o custom-columns=KIND:.kind,NAME:.metadata.name,REPLICAS:.spec.replicas \
    > replicas-before.txt
2 · operators, or they scale it all back
$ kubectl -n kafka scale deploy strimzi-cluster-operator \
    netlinq-cluster-operator --replicas=0
3 · autoscalers
$ kubectl -n netlinq get hpa -o yaml > hpa-backup.yaml
$ kubectl -n netlinq delete hpa --all
4 · the ordered workloads, one at a time
# order: diesel, elastic, keycloak, kibana, mongo-tenant, postgresql
$ kubectl -n netlinq get deploy,sts   # check each one's kind
$ kubectl -n netlinq scale deployment/diesel --replicas=0
$ kubectl -n netlinq rollout status deployment/diesel --timeout=120s
5 · everything that is left
$ kubectl -n netlinq scale deploy --all --replicas=0
$ kubectl -n netlinq scale sts --all --replicas=0

To bring it back, reverse the order: databases first, then the applications, then the operators, then re-apply hpa-backup.yaml. Restore each workload to the count in replicas-before.txt, not to 1.

Step 3

Turn it off

Disabling the shutdown unit makes the next reboot an ordinary one: no scale-down, no drain, no snapshot, nothing to restore.

every node
$ sudo systemctl disable --now netlinq-shutdown.service netlinq-startup.service
Use disable --now, not a plain stop. Stopping netlinq-shutdown.service is exactly what runs its ExecStop — a bare systemctl stop triggers a full controlled shutdown of the cluster right then.

Set enable_netlinq_shutdown_service="false" in the vars file as well, so a later install or upgrade does not quietly arm it again.

Help

Verify and read the logs

check the units
$ systemctl status netlinq-shutdown.service netlinq-startup.service kubelet
$ cat /opt/netlinq/installation/replicas.json

Both scripts log to the systemd journal, so the record survives the reboot they were running for:

journal
$ journalctl -u netlinq-shutdown.service -f
$ journalctl -u netlinq-startup.service -b
The startup unit is in failed state›
It allows three attempts in three hours. Read its journal, fix the cause, then clear it: sudo systemctl reset-failed netlinq-startup.service followed by sudo systemctl start netlinq-startup.service.
A workload came back at the wrong replica count›
Check the snapshot it restored from in /opt/netlinq/installation/, and whether an operator reconciled it afterwards. The snapshot is only consumed after a successful restore.
The journal warns about an unordered workload›
It was stopped in arbitrary order because it is not listed. Add it to .netlinq.shutdown_order in replicas.json, then run shutdown_netlinq.sh --sync-only.
Installing the platform for the first time? Start with the NetLiNQ On-Prem installation guide; this page covers what happens afterwards.