Skip to content

Commit 86a7275

Browse files
chore: add docs (#3)
Signed-off-by: Moritz Johner <[email protected]>
1 parent aa9ef7b commit 86a7275

20 files changed

+962
-48
lines changed

Diff for: .github/actions/mkdocs/Dockerfile

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
FROM squidfunk/mkdocs-material:9.4.5
2+
3+
COPY action.sh /action.sh
4+
5+
RUN apk add --no-cache bash \
6+
&& chmod +x /action.sh
7+
8+
ENTRYPOINT ["/action.sh"]

Diff for: .github/actions/mkdocs/action.sh

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
3+
# Copyright 2020 The Kubernetes Authors.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
set -e
18+
19+
REQUIREMENTS="${GITHUB_WORKSPACE}/docs/requirements.txt"
20+
21+
if [ -f "${REQUIREMENTS}" ]; then
22+
pip install -r "${REQUIREMENTS}"
23+
fi
24+
25+
if [ -n "${GITHUB_TOKEN}" ]; then
26+
remote_repo="https://x-access-token:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
27+
elif [ -n "${PERSONAL_TOKEN}" ]; then
28+
remote_repo="https://x-access-token:${PERSONAL_TOKEN}@github.com/${GITHUB_REPOSITORY}.git"
29+
fi
30+
31+
git config --global user.name "$GITHUB_ACTOR"
32+
git config --global user.email "$GITHUB_ACTOR@users.noreply.github.com"
33+
34+
mkdocs build --config-file "${GITHUB_WORKSPACE}/docs/mkdocs.yml"
35+
36+
git clone --branch=gh-pages --depth=1 "${remote_repo}" gh-pages
37+
cd gh-pages
38+
39+
# copy current index file index.yaml before any change
40+
temp_worktree=$(mktemp -d)
41+
cp --force "index.yaml" "$temp_worktree/index.yaml"
42+
# remove current content in branch gh-pages
43+
git rm -r .
44+
# copy new doc.
45+
cp -r ../site/* .
46+
# restore chart index
47+
cp "$temp_worktree/index.yaml" .
48+
# commit changes
49+
git add .
50+
git commit -m "Deploy GitHub Pages"
51+
git push --force --quiet "${remote_repo}" gh-pages > /dev/null 2>&1

Diff for: .github/actions/mkdocs/action.yml

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# action.yml
2+
name: 'Deploy MkDocs'
3+
description: 'Deploys MkDocs site'
4+
branding:
5+
icon: 'arrow-up-circle'
6+
color: 'orange'
7+
runs:
8+
using: 'docker'
9+
image: 'Dockerfile'

Diff for: README.md

+1-48
Original file line numberDiff line numberDiff line change
@@ -5,54 +5,7 @@ X-PDB allows you to define multi-cluster PodDisruptionBudgets and blocks evictio
55
This allows you to operate stateful workloads spanning multiple clusters and limit disruptions to a necessary minimum.
66
This is needed, because each cluster acts individually (evictions, rolling out updates etc) which may cause service disruptions simultaneously across clusters.
77

8-
## Eviction Process
9-
10-
X-PDB hooks into the DELETE `pods` or CREATE `pods/eviction` API calls using a `ValidatingWebhookConfiguration`.
11-
It will acquire locks on all clusters to prevent race conditions and read the state (expected pod count & healthy pod count) from all clusters and compute if a eviction/deletion is allowed.
12-
13-
X-PDB need to talk to the other X-PDB pods the other clusters to read the remote state.
14-
15-
### Locking mechanism
16-
17-
X-PDB acquires a lock on the remote clusters using a HTTP API.
18-
The lock is valid for a specific `namespace/selector` combination and it has a `leaseHolderIdentity`. This is the owner of the given lock.
19-
20-
The lock is **valid for 5 seconds**. After that it can be re-acquired or taken over by a different holder.
21-
The lock prevents a race condition which occur if multiple evictions happen simultaneously across clusters which would lead to inconsistent data and wrong decisions. E.g. a read can happen while a eviction is being processed which would lead to multiple evictions happen at the same time that could break the pod disruption budget.
22-
23-
We leave the lock as it is and DO NOT unlock it after the admission webhook have finished processing.
24-
Once it expires it can be re-acquired or taken over. We rely on the caller to retry the eviction or deletion of a Pod.
25-
26-
27-
**Why 5 seconds? - Why leave it locked when we've finished processing?**
28-
29-
We need to lock evictions for a period of time **after** we have returned the admission result to allow kube-apiserver to update the Pod (set the `deletionTimestamp`).
30-
31-
The lease duration should be higher than the sum of the following duration:
32-
- the round-trip latency across all clusters (1-2 seconds)
33-
- the processing time of the x-pdb http server (<1 second)
34-
- the time kube-apiserver needs to process the x-pdb admission control response
35-
and the time it takes until the desired action (evict/delete pod) is observable through the kube-apiserver (1-2 seconds)
36-
- a generous surcharge (1-... seconds)
37-
38-
### State server
39-
40-
In order for x-pdb servers to communicate between each other they expose a [gRPC server](./protos/state/state.proto) which has the following API:
41-
42-
```proto
43-
service State {
44-
45-
rpc Lock(LockRequest) returns (LockResponse) {}
46-
rpc Unlock(UnlockRequest) returns (UnlockResponse) {}
47-
rpc GetState(GetStateRequest) returns (GetStateResponse) {}
48-
}
49-
```
50-
51-
## Threat Modelling
52-
53-
- [Threat Modelling inventory](/threat-modelling/TMinventory.md)
54-
55-
## Observability & monitoring
8+
Please refer to the documentation at https://form3tech-oss.github.com/x-pdb
569

5710
## Development
5811
### Running tests

Diff for: docs/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
site/

Diff for: docs/Makefile

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
.PHONY: check_dead_links
2+
check_dead_links: ## Check if the documentation contains dead links.
3+
@docker run -t \
4+
-w /tmp \
5+
-v $$PWD:/tmp dkhamsing/awesome_bot:1.20.0 \
6+
--allow-dupe \
7+
--allow-redirect $(shell find $$PWD -mindepth 1 -name vendor -prune -o -name .modcache -prune -o -iname Changelog.md -prune -o -name "*.md" | sed -e "s#$$PWD/##")
8+
9+
.PHONY: build-image
10+
build-image:
11+
@docker build \
12+
--no-cache \
13+
-t xpdb-docs ../.github/actions/mkdocs
14+
15+
.PHONY: build-docs
16+
build-docs: build-image
17+
@docker run --rm -it \
18+
-p 8000:8000 \
19+
-v ${PWD}:/docs \
20+
--entrypoint /bin/bash \
21+
xpdb-docs \
22+
-c "pip install -r /docs/requirements.txt && mkdocs build --config-file mkdocs.yml"
23+
24+
.PHONY: live-docs
25+
live-docs: build-image ## Build and launch a local copy of the documentation website in http://localhost:8000
26+
@docker run --rm -it \
27+
-p 8000:8000 \
28+
-v ${PWD}:/docs \
29+
--entrypoint /bin/bash \
30+
xpdb-docs \
31+
-c "pip install -r /docs/requirements.txt && mkdocs serve --dev-addr=0.0.0.0:8000"
32+
33+
.PHONY: misspell
34+
misspell: ## Check for spelling errors.
35+
@go install github.com/client9/misspell/cmd/misspell@latest
36+
misspell \
37+
-locale US \
38+
-error \
39+
.

Diff for: docs/extra.css

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
.md-typeset__table {
2+
min-width: 100%;
3+
}
4+
5+
@media only screen and (min-width: 768px) {
6+
td:nth-child(1) {
7+
white-space: nowrap;
8+
}
9+
}

Diff for: docs/mkdocs.yml

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
site_name: X-PDB
2+
repo_name: "form3tech-oss/x-pdb"
3+
repo_url: https://github.com/form3tech-oss/x-pdb
4+
site_url: https://form3tech-oss.github.io/x-pdb
5+
edit_uri: edit/main/docs/src
6+
docs_dir: "/docs/src"
7+
8+
# Extensions
9+
markdown_extensions:
10+
- admonition
11+
- abbr
12+
- attr_list
13+
- def_list
14+
- footnotes
15+
- meta
16+
- md_in_html
17+
- pymdownx.blocks.caption
18+
- toc:
19+
# insert a blank space before the character
20+
permalink: ""
21+
- pymdownx.arithmatex:
22+
generic: true
23+
- pymdownx.betterem:
24+
smart_enable: all
25+
- pymdownx.caret
26+
- pymdownx.critic
27+
- pymdownx.details
28+
- pymdownx.emoji:
29+
emoji_index: !!python/name:materialx.emoji.twemoji
30+
emoji_generator: !!python/name:materialx.emoji.to_svg
31+
- pymdownx.highlight
32+
- pymdownx.inlinehilite
33+
- pymdownx.keys
34+
- pymdownx.mark
35+
- pymdownx.smartsymbols
36+
- pymdownx.snippets:
37+
check_paths: true
38+
- pymdownx.superfences:
39+
custom_fences:
40+
- name: mermaid
41+
class: mermaid
42+
format: !!python/name:pymdownx.superfences.fence_code_format
43+
- pymdownx.tabbed
44+
- pymdownx.tasklist:
45+
custom_checkbox: true
46+
- pymdownx.tilde
47+
48+
theme:
49+
name: material
50+
features:
51+
- navigation.instant
52+
- navigation.sections
53+
54+
palette:
55+
primary: "deep orange"
56+
accent: "indigo"
57+
58+
include_sidebar: true
59+
60+
plugins:
61+
- search
62+
- awesome-pages
63+
- minify:
64+
minify_html: true
65+
66+
extra_css: [extra.css]
67+
68+
nav:
69+
- Introduction:
70+
- Overview: "index.md"
71+
- Getting Started: "getting-started.md"
72+
- Configuring x-pdb:
73+
- XPodDisruptionBudget: "configuring-xpdb.md"
74+
- Disruption Probes: "configuring-disruption-probes.md"
75+
- Operating x-pdb:
76+
- Failure Scenarios: "failure-scenarios.md"
77+
- Metrics & SLOs: "metrics-slos.md"

Diff for: docs/requirements.txt

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mkdocs-material==9.4.5
2+
mkdocs-awesome-pages-plugin==2.9.2
3+
mkdocs-minify-plugin==0.7.1
4+
mkdocs-redirects==1.2.1
5+
pymdown-extensions==10.12

Diff for: docs/src/configuring-disruption-probes.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Configuring Disruption Probes
2+
3+
x-pdb allows workload owners to define a disruption probe endpoint.
4+
5+
This endpoint is used by x-pdb to make a decision whether or not a disruption is allowed. Without the probe endpoint, x-pdb considers only pod readiness as a indicator of pod healthiness.
6+
7+
With the probe endpoint configured, x-pdb will ask the probe endpoint whether or not the disruption is allowed.
8+
9+
The probe endpoint is just a gRPC server, see details below.
10+
11+
It might be helpful to probe internal state of some workloads like databases to verify wether an eviction can happen or not.
12+
Database Raft groups might become unavailable if a given pod is disrupted. In these cases workload owners might want to
13+
block disruptions to happen, even if all pods are ready.
14+
15+
Example use-cases:
16+
17+
- assess health of a database cluster as a whole before allowing the deletion of a single pod, as this disruption may add more pressure on the database.
18+
- assess the state of raft group leaders in a cluster before allowing an eviction
19+
- assess the replication lag of database clusters
20+
- assess if any long-running queries/jobs or backup are running, where a deletion can cause a problem
21+
22+
```yaml
23+
apiVersion: x-pdb.form3.tech/v1alpha1
24+
kind: XPodDisruptionBudget
25+
metadata:
26+
name: kube-dns
27+
namespace: kube-system
28+
spec:
29+
minAvailable: 80%
30+
selector:
31+
matchLabels:
32+
k8s-app: kube-dns
33+
probe:
34+
endpoint: opensearch-disruption-probe.opensearch.svc.cluster.local:8080
35+
```
36+
37+
## TLS and authentication
38+
39+
At this point, the communication between x-pdb and the probe server does not use mutual TLS and only validates the server certificate presented by the probe endpoint and verifies it has been issued by the CA defined in `--controller-certs-dir`.
40+
41+
#### DisruptionProbe Server
42+
43+
The DisruptionProbe server allows the client to ask if a disruption for a given pod and XPDB resource is allowed.
44+
45+
```proto
46+
service DisruptionProbe {
47+
// Sends a IsDisruptionAllowed request
48+
rpc IsDisruptionAllowed(IsDisruptionAllowedRequest) returns (IsDisruptionAllowedResponse) {}
49+
}
50+
51+
// The request message containing the user's name.
52+
message IsDisruptionAllowedRequest {
53+
string pod_name = 1;
54+
string pod_namespace = 2;
55+
string xpdb_name = 3;
56+
string xpdb_namespace = 4;
57+
}
58+
59+
// The response message containing the greetings
60+
message IsDisruptionAllowedResponse {
61+
bool is_allowed = 1;
62+
string error = 2;
63+
}
64+
65+
```
66+

Diff for: docs/src/configuring-xpdb.md

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# Configuring XPodDisruption Resource
2+
3+
## Nomenclature
4+
5+
| term | description |
6+
| --------------- | ----------------------------------------------------------------------------------- |
7+
| local cluster | This is the Kubernetes cluster where a pod deletion or eviction is being requested. |
8+
| remote clusters | These are the clusters which are not handling the pod eviction / deletion. |
9+
10+
## Configuration and Behavior
11+
12+
X-PDB works under the assumption that your workloads are structured in a similar way across clusters, i.e. that pods that you want to protect sit in the same namespace no matter which cluster you look at.
13+
14+
The `XPodDisruptionBudget` resources looks and feels just like `PodDisruptionBudget` resources:
15+
16+
- A label selector `.spec.selector` to specify the set of pods to which it applies. The `selector` applies to all clusters for decision making, not just the local one where pod eviction/deletion is happening.
17+
18+
- `.spec.minAvailable` which is a description of the number of pods from that set that must still be available after the eviction, even in the absence of the evicted pod. minAvailable can be either an absolute number or a percentage.
19+
- `.spec.maxUnavailable` which is a description of the number of pods from that set that can be unavailable after the eviction. It can be either an absolute number or a percentage.
20+
21+
You can specify only one of `maxUnavailable` and `minAvailable` in a single PodDisruptionBudget. `maxUnavailable` can only be used to control the eviction of pods that have an associated controller managing them.
22+
23+
In addition to that, `XPodDisruptionBudget` has the following fields:
24+
25+
- `.spec.suspend` which allows you to disable the XPDB resource. This allows all pod deletions/evictions. It is intended to be used as a break-glass procedure to allow engineers to take manual action. The suspension is configured on a per-cluster basis and affects only local pods. I.e. other clusters that run x-pdb will not be able to evict pods if there isn't enough disruption budget available globally.
26+
- `.spec.probe` that allows workload owners to define a [disruption probe](./configuring-disruption-probes.md) endpoint. Without a probe, x-pdb will only consider pod readiness as an indicator of healthiness and compute the disruption verdict based on that. With `.spec.probe`, x-pdb considers the response of the probe endpoint as well.
27+
28+
It is irrelevant for `x-pdb` if the remote cluster has a `XPodDisruptionBudget` resource and whether or not the configuration match.
29+
30+
The user is supposed to deploy the `XPodDisruptionBudget` to all clusters. It may lead to unexpected disruptions when the resource is missing.
31+
32+
```yaml
33+
apiVersion: x-pdb.form3.tech/v1alpha1
34+
kind: XPodDisruptionBudget
35+
metadata:
36+
name: kube-dns
37+
namespace: kube-system
38+
spec:
39+
# Specify either `minAvailable` or `maxUnavailable`
40+
# Both percentages and numbers are supported
41+
minAvailable: 80%
42+
selector:
43+
matchLabels:
44+
k8s-app: kube-dns
45+
```
46+
47+
## gRPC State Server
48+
49+
In order for x-pdb servers to communicate between each other they expose a gRPC state server interface with the following APIs. It allows x-pdb to asses the health of pods on remote clusters.
50+
51+
The communication between x-pdb servers is secured using mutual TLS. The certificate directory can be configured with `--controller-certs-dir` which is supposed to contain `ca.crt`, `tls.crt` and `tls.key` files.
52+
53+
```proto
54+
// State is the service that allows x-pdb servers to talk with
55+
// each other.
56+
service State {
57+
// Acquires a lock on the local cluster using the specified leaseHolderIdentity.
58+
rpc Lock(LockRequest) returns (LockResponse) {}
59+
60+
// Frees a lock on the local cluster if the lease identity matches.
61+
rpc Unlock(UnlockRequest) returns (UnlockResponse) {}
62+
63+
// Calculates the expected count based off the Deployment/StatefulSet/ReplicaSet number of replicas or - if implemented - a `scale` sub resource.
64+
rpc GetState(GetStateRequest) returns (GetStateResponse) {}
65+
}
66+
```

0 commit comments

Comments
 (0)