From 93bd87547a6014b896c41aa15715e94c63e56d73 Mon Sep 17 00:00:00 2001 From: Daniel Holbach Date: Tue, 29 Nov 2022 21:59:44 +0100 Subject: [PATCH] move documentation to kured.dev Signed-off-by: Daniel Holbach --- README.md | 396 ++----------------------------------- img/slack-notification.png | Bin 7345 -> 0 bytes 2 files changed, 11 insertions(+), 385 deletions(-) delete mode 100644 img/slack-notification.png diff --git a/README.md b/README.md index 6d98e91..1b874a5 100644 --- a/README.md +++ b/README.md @@ -8,28 +8,10 @@ - [kured - Kubernetes Reboot Daemon](#kured---kubernetes-reboot-daemon) - [Introduction](#introduction) - - [Kubernetes & OS Compatibility](#kubernetes--os-compatibility) - - [Installation](#installation) - - [Configuration](#configuration) - - [Reboot Sentinel File & Period](#reboot-sentinel-file--period) - - [Reboot Sentinel Command](#reboot-sentinel-command) - - [Setting a schedule](#setting-a-schedule) - - [Blocking Reboots via Alerts](#blocking-reboots-via-alerts) - - [Blocking Reboots via Pods](#blocking-reboots-via-pods) - - [Adding node labels before and after reboots](#adding-node-labels-before-and-after-reboots) - - [Prometheus Metrics](#prometheus-metrics) - - [Notifications](#notifications) - - [Overriding Lock Configuration](#overriding-lock-configuration) - - [Operation](#operation) - - [Testing](#testing) - - [Disabling Reboots](#disabling-reboots) - - [Manual Unlock](#manual-unlock) - - [Automatic Unlock](#automatic-unlock) - - [Delaying Lock Release](#delaying-lock-release) - - [Building](#building) - - [Frequently Asked/Anticipated Questions](#frequently-askedanticipated-questions) - - [Why is there no `latest` tag on Docker Hub?](#why-is-there-no-latest-tag-on-docker-hub) + - [Documentation](#documentation) - [Getting Help](#getting-help) + - [Trademarks](#trademarks) + - [License](#license) ## Introduction @@ -44,373 +26,17 @@ indicated by the package management system of the underlying OS. - Optionally defers reboots in the presence of active Prometheus alerts or selected pods - Cordons & drains worker nodes before reboot, uncordoning them after -## Kubernetes & OS Compatibility +## Documentation -The daemon image contains versions of `k8s.io/client-go` and -`k8s.io/kubectl` (the binary of `kubectl` in older releases) for the purposes of -maintaining the lock and draining worker nodes. Kubernetes aims to provide -forwards and backwards compatibility of one minor version between client and -server: +Find all our docs on : -| kured | {k8s.io/,}kubectl | k8s.io/client-go | k8s.io/apimachinery | expected kubernetes compatibility | -| ------ | ----------------- | ---------------- | ------------------- | --------------------------------- | -| main | 0.24.7 | v0.24.7 | v0.24.7 | 1.23.x, 1.24.x, 1.25.x | -| 1.11.0 | 0.24.7 | v0.24.7 | v0.24.7 | 1.23.x, 1.24.x, 1.25.x | -| 1.10.2 | 0.23.6 | v0.23.6 | v0.23.6 | 1.22.x, 1.23.x, 1.24.x | -| 1.9.2 | 0.22.4 | v0.22.4 | v0.22.4 | 1.21.x, 1.22.x, 1.23.x | -| 1.8.1 | 0.21.4 | v0.21.4 | v0.21.4 | 1.20.x, 1.21.x, 1.22.x | -| 1.7.0 | 0.20.5 | v0.20.5 | v0.20.5 | 1.19.x, 1.20.x, 1.21.x | -| 1.6.1 | 0.19.4 | v0.19.4 | v0.19.4 | 1.18.x, 1.19.x, 1.20.x | -| 1.5.1 | 0.18.8 | v0.18.8 | v0.18.8 | 1.17.x, 1.18.x, 1.19.x | -| 1.4.4 | 1.17.7 | v0.17.0 | v0.17.0 | 1.16.x, 1.17.x, 1.18.x | -| 1.3.0 | 1.15.10 | v12.0.0 | release-1.15 | 1.15.x, 1.16.x, 1.17.x | -| 1.2.0 | 1.13.6 | v10.0.0 | release-1.13 | 1.12.x, 1.13.x, 1.14.x | -| 1.1.0 | 1.12.1 | v9.0.0 | release-1.12 | 1.11.x, 1.12.x, 1.13.x | -| 1.0.0 | 1.7.6 | v4.0.0 | release-1.7 | 1.6.x, 1.7.x, 1.8.x | +- [All Kured Documentation](https://kured.dev/docs/) +- [Installing Kured](https://kured.dev/docs/installation/) +- [Configuring Kured](https://kured.dev/docs/configuration/) +- [Operating Kured](https://kured.dev/docs/operation/) +- [Developing Kured](https://kured.dev/docs/development/) -See the [release notes](https://github.com/kubereboot/kured/releases) -for specific version compatibility information, including which -combination have been formally tested. - -Versions >=1.1.0 enter the host mount namespace to invoke -`systemctl reboot`, so should work on any systemd distribution. - -## Installation - -To obtain a default installation without Prometheus alerting interlock -or Slack notifications: - -```console -latest=$(curl -s https://api.github.com/repos/kubereboot/kured/releases | jq -r .[0].tag_name) -kubectl apply -f "https://github.com/kubereboot/kured/releases/download/$latest/kured-$latest-dockerhub.yaml" -``` - -If you want to customise the installation, download the manifest and -edit it in accordance with the following section before application. - -## Configuration - -The following arguments can be passed to kured via the daemonset pod template: - -```console -Kubernetes Reboot Daemon - -Usage: - kured [flags] - -Flags: - --alert-filter-regexp regexp.Regexp alert names to ignore when checking for active alerts - --alert-firing-only only consider firing alerts when checking for active alerts - --annotate-nodes if set, the annotations 'weave.works/kured-reboot-in-progress' and 'weave.works/kured-most-recent-reboot-needed' will be given to nodes undergoing kured reboots - --blocking-pod-selector stringArray label selector identifying pods whose presence should prevent reboots - --drain-grace-period int time in seconds given to each pod to terminate gracefully, if negative, the default value specified in the pod will be used (default -1) - --drain-timeout duration timeout after which the drain is aborted (default: 0, infinite time) - --ds-name string name of daemonset on which to place lock (default "kured") - --ds-namespace string namespace containing daemonset on which to place lock (default "kube-system") - --end-time string schedule reboot only before this time of day (default "23:59:59") - --force-reboot force a reboot even if the drain fails or times out - -h, --help help for kured - --lock-annotation string annotation in which to record locking node (default "weave.works/kured-node-lock") - --lock-release-delay duration delay lock release for this duration (default: 0, disabled) - --lock-ttl duration expire lock annotation after this duration (default: 0, disabled) - --log-format string use text or json log format (default "text") - --message-template-drain string message template used to notify about a node being drained (default "Draining node %s") - --message-template-reboot string message template used to notify about a node being rebooted (default "Rebooting node %s") - --message-template-uncordon string message template used to notify about a node being successfully uncordoned (default "Node %s rebooted & uncordoned successfully!") - --node-id string node name kured runs on, should be passed down from spec.nodeName via KURED_NODE_ID environment variable - --notify-url string notify URL for reboot notifications (cannot use with --slack-hook-url flags) - --period duration sentinel check period (default 1h0m0s) - --post-reboot-node-labels strings labels to add to nodes after uncordoning - --pre-reboot-node-labels strings labels to add to nodes before cordoning - --prefer-no-schedule-taint string Taint name applied during pending node reboot (to prevent receiving additional pods from other rebooting nodes). Disabled by default. Set e.g. to "weave.works/kured-node-reboot" to enable tainting. - --prometheus-url string Prometheus instance to probe for active alerts - --reboot-command string command to run when a reboot is required (default "/bin/systemctl reboot") - --reboot-days strings schedule reboot on these days (default [su,mo,tu,we,th,fr,sa]) - --reboot-delay duration delay reboot for this duration (default: 0, disabled) - --reboot-sentinel string path to file whose existence triggers the reboot command (default "/var/run/reboot-required") - --reboot-sentinel-command string command for which a zero return code will trigger a reboot command - --skip-wait-for-delete-timeout int when seconds is greater than zero, skip waiting for the pods whose deletion timestamp is older than N seconds while draining a node - --slack-channel string slack channel for reboot notifications - --slack-hook-url string slack hook URL for reboot notifications [deprecated in favor of --notify-url] - --slack-username string slack username for reboot notifications (default "kured") - --start-time string schedule reboot only after this time of day (default "0:00") - --time-zone string use this timezone for schedule inputs (default "UTC") -``` - -### Reboot Sentinel File & Period - -By default kured checks for the existence of -`/var/run/reboot-required` every sixty minutes; you can override these -values with `--reboot-sentinel` and `--period`. Each replica of the -daemon uses a random offset derived from the period on startup so that -nodes don't all contend for the lock simultaneously. - -### Reboot Sentinel Command - -Alternatively, a reboot sentinel command can be used. If a reboot -sentinel command is used, the reboot sentinel file presence will be -ignored. When the command exits with code `0`, kured will assume -that a reboot is required. - -For example, if you're using RHEL or its derivatives, you can -set the sentinel command to `sh -c "! needs-restarting --reboothint"` -(by default the command will return `1` if a reboot is required, -so we wrap it in `sh -c` and add `!` to negate the return value). - -```yaml -configuration: - rebootSentinelCommand: sh -c "! needs-restarting --reboothint" -``` - -### Setting a schedule - -By default, kured will reboot any time it detects the sentinel, but this -may cause reboots during odd hours. While service disruption does not -normally occur, anything is possible and operators may want to restrict -reboots to predictable schedules. Use `--reboot-days`, `--start-time`, -`--end-time`, and `--time-zone` to set a schedule. For example, business -hours on the west coast USA can be specified with: - -```console - --reboot-days=mon,tue,wed,thu,fri - --start-time=9am - --end-time=5pm - --time-zone=America/Los_Angeles -``` - -Times can be formatted in numerous ways, including `5pm`, `5:00pm` `17:00`, -and `17`. `--time-zone` represents a Go `time.Location`, and can be `UTC`, -`Local`, or any entry in the standard Linux tz database. - -Note that when using smaller time windows, you should consider shortening -the sentinel check period (`--period`). - -### Blocking Reboots via Alerts - -You may find it desirable to block automatic node reboots when there -are active alerts - you can do so by providing the URL of your -Prometheus server: - -```console ---prometheus-url=http://prometheus.monitoring.svc.cluster.local -``` - -By default the presence of *any* active (pending or firing) alerts -will block reboots, however you can ignore specific alerts: - -```console ---alert-filter-regexp=^(RebootRequired|AnotherBenignAlert|...$ -``` - -You can also only block reboots for firing alerts: - -```console ---alert-firing-only=true -``` - -See the section on Prometheus metrics for an important application of this -filter. - -### Blocking Reboots via Pods - -You can also block reboots of an *individual node* when specific pods -are scheduled on it: - -```console ---blocking-pod-selector=runtime=long,cost=expensive -``` - -Since label selector strings use commas to express logical 'and', you can -specify this parameter multiple times for 'or': - -```console ---blocking-pod-selector=runtime=long,cost=expensive ---blocking-pod-selector=name=temperamental -``` - -In this case, the presence of either an (appropriately labelled) expensive long -running job or a known temperamental pod on a node will stop it rebooting. - -> Try not to abuse this mechanism - it's better to strive for -> restartability where possible. If you do use it, make sure you set -> up a RebootRequired alert as described in the next section so that -> you can intervene manually if reboots are blocked for too long. - -### Adding node labels before and after reboots - -If you need to add node labels before and after the reboot process, you can use `--pre-reboot-node-labels` and `--post-reboot-node-labels`: - -```console - --pre-reboot-node-labels=zalando=notready - --post-reboot-node-labels=zalando=ready -``` - -Labels can be comma-delimited (e.g. `--pre-reboot-node-labels=zalando=notready,thisnode=disabled`) or you can supply the flags multiple times. - -Note that label keys specified by these two flags should match. If they do not match, a warning will be generated. - -### Prometheus Metrics - -Each kured pod exposes a single gauge metric (`:8080/metrics`) that -indicates the presence of the sentinel file: - -```console -# HELP kured_reboot_required OS requires reboot due to software updates. -# TYPE kured_reboot_required gauge -kured_reboot_required{node="ip-xxx-xxx-xxx-xxx.ec2.internal"} 0 -``` - -The purpose of this metric is to power an alert which will summon an -operator if the cluster cannot reboot itself automatically for a -prolonged period: - -```console -# Alert if a reboot is required for any machines. Acts as a failsafe for the -# reboot daemon, which will not reboot nodes if there are pending alerts save -# this one. -ALERT RebootRequired - IF max(kured_reboot_required) != 0 - FOR 24h - LABELS { severity="warning" } - ANNOTATIONS { - summary = "Machine(s) require being rebooted, and the reboot daemon has failed to do so for 24 hours", - impact = "Cluster nodes more vulnerable to security exploits. Eventually, no disk space left.", - description = "Machine(s) require being rebooted, probably due to kernel update.", - } -``` - -If you choose to employ such an alert and have configured kured to -probe for active alerts before rebooting, be sure to specify -`--alert-filter-regexp=^RebootRequired$` to avoid deadlock! - -### Notifications - -When you specify a formatted URL using `--notify-url`, kured will notify -about draining and rebooting nodes across a list of technologies. - -![Notification](img/slack-notification.png) - -Alternatively you can use the `--message-template-drain`, `--message-template-reboot` and `--message-template-uncordon` to customize the text of the message, e.g. - -```cli ---message-template-drain="Draining node %s part of *my-cluster* in region *xyz*" -``` - -Here is the syntax: - -- slack: `slack://tokenA/tokenB/tokenC` - - (`slack://@tokenA/tokenB/tokenC` - in case you want to [respect username](https://github.com/kubereboot/kured/issues/482)) - - (`--slack-hook-url` is deprecated but possible to use) - - For the new slack App integration, use:\ - `slack://xoxb:123456789012-1234567890123-4mt0t4l1YL3g1T5L4cK70k3N@?botname=`\ - for more information, [look here](https://containrrr.dev/shoutrrr/v0.5/services/slack/#examples) - -- rocketchat: `rocketchat://[username@]rocketchat-host/token[/channel|@recipient]` - -- teams: `teams://group@tenant/altId/groupOwner?host=organization.webhook.office.com` - -- Email: `smtp://username:password@host:port/?fromAddress=fromAddress&toAddresses=recipient1[,recipient2,...]` - -More details here: [containrrr.dev/shoutrrr/v0.5/services/overview](https://containrrr.dev/shoutrrr/v0.5/services/overview) - -### Overriding Lock Configuration - -The `--ds-name` and `--ds-namespace` arguments should match the name and -namespace of the daemonset used to deploy the reboot daemon - the locking is -implemented by means of an annotation on this resource. The defaults match -the daemonset YAML provided in the repository. - -Similarly `--lock-annotation` can be used to change the name of the -annotation kured will use to store the lock, but the default is almost -certainly safe. - -## Operation - -The example commands in this section assume that you have not -overriden the default lock annotation, daemonset name or namespace; -if you have, you will have to adjust the commands accordingly. - -### Testing - -You can test your configuration by provoking a reboot on a node: - -```console -sudo touch /var/run/reboot-required -``` - -### Disabling Reboots - -If you need to temporarily stop kured from rebooting any nodes, you -can take the lock manually: - -```console -kubectl -n kube-system annotate ds kured weave.works/kured-node-lock='{"nodeID":"manual"}' -``` - -Don't forget to release it afterwards! - -### Manual Unlock - -In exceptional circumstances, such as a node experiencing a permanent -failure whilst rebooting, manual intervention may be required to -remove the cluster lock: - -```console -kubectl -n kube-system annotate ds kured weave.works/kured-node-lock- -``` - -> NB the `-` at the end of the command is important - it instructs -> `kubectl` to remove that annotation entirely. - -### Automatic Unlock - -In exceptional circumstances (especially when used with cluster-autoscaler) a node -which holds lock might be killed thus annotation will stay there for ever. - -Using `--lock-ttl=30m` will allow other nodes to take over if TTL has expired (in this case 30min) and continue reboot process. - -### Delaying Lock Release - -Using `--lock-release-delay=30m` will cause nodes to hold the lock for the specified time frame (in this case 30min) before it is released and the reboot process continues. This can be used to throttle reboots across the cluster. - -## Building - -Kured now uses [Go -Modules](https://github.com/golang/go/wiki/Modules), so build -instructions vary depending on where you have checked out the -repository: - -**Building outside $GOPATH:** - -```console -make -``` - -**Building inside $GOPATH:** - -```console -GO111MODULE=on make -``` - -You can find the current preferred version of Golang in the [go.mod file](go.mod). - -If you are interested in contributing code to kured, please take a look at -our [contributor][contributor] docs. - -[contributor]: CONTRIBUTING.md - -## Frequently Asked/Anticipated Questions - -### Why is there no `latest` tag on Docker Hub? - -Use of `latest` for production deployments is bad practice - see -[here](https://kubernetes.io/docs/concepts/configuration/overview) for -details. The manifest on `main` refers to `latest` for local -development testing with minikube only; for production use choose a -versioned manifest from the [release page](https://github.com/kubereboot/kured/releases/). +And there's much more! ## Getting Help diff --git a/img/slack-notification.png b/img/slack-notification.png deleted file mode 100644 index b8797c8b2db927f1b4ebe83488fb37c6cb78b2c7..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 7345 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