From 6b0ac44291ec40ad1fed3f50cdcf2e61e3af153c Mon Sep 17 00:00:00 2001 From: unknown Date: Wed, 7 Jun 2023 19:30:29 +0300 Subject: [PATCH] Add HRQ documentation Signed-off-by: unknown --- docs/user-guide/concepts.md | 71 ++++++++++++++++++++++++++++++- docs/user-guide/how-to.md | 16 +++++++ docs/user-guide/quickstart.md | 79 +++++++++++++++++++++++++++++++++-- test/e2e/quickstart_test.go | 37 ++++++++++++++++ 4 files changed, 198 insertions(+), 5 deletions(-) diff --git a/docs/user-guide/concepts.md b/docs/user-guide/concepts.md index fee0751c6..8778a2409 100644 --- a/docs/user-guide/concepts.md +++ b/docs/user-guide/concepts.md @@ -13,6 +13,7 @@ namespaces are, and _why_ they behave the way they do. * [Basic concepts](#basic) * [Parents, children, trees and forests](#basic-trees) * [Full namespaces and subnamespaces](#basic-subns) + * [Hierarchical resource quotas (HRQs)](#hrq) * [Policy inheritance and object propagation](#basic-propagation) * [Tree labels and non-propagated policies](#basic-labels) * [Exceptions and propagation control](#basic-exceptions) @@ -87,6 +88,8 @@ namespaces: as isolation units for their own services. However, namespace creation is a privileged cluster-level operation, and you typically want to control this privilege very closely. +* You might want to give some amount of resources to a team (similar to `ResourceQuota`), and they can + distribute those resources between their subnamespaces. * Finally, you might want to avoid having to find unique names for every namespace in the cluster. @@ -218,6 +221,70 @@ probably not a great idea. You can create a subnamespace from the command line via `kubectl hns create child -n parent`. + + +### Hierarchical resource quotas (HRQs) + +***Hierarchical resource quotas are beta in HNC v1.1*** + +***HRQs are not included by default, to use it install the hrq.yaml file in the [releases -> Assets](https://github.com/kubernetes-sigs/hierarchical-namespaces/releases)*** + +When you want to give some amount of resources to `team-a`, and want them to be able to +flexibly use resources in any of their subnamespaces, you create a `HierarchicalResourceQuota` +in namespace `team-a`. The sum of all resources from all the subnamespaces of the +members wont be over the amount of resources that is configured in +`HierarchicalResourceQuota` of namespace `team-a`. All of the reasources of `team-a` are +equally shared between the applications in their subnamespaces, which is very efficient. + +In addition, you can let an org or team's admin create their own hierarchical +quotas without violating the overall HRQ for their org or team. For example, +if you start with the following structure: +``` +company-a +├── organization-a +│ ├── org-a-team-1 +│ ├── org-a-team-2 +│ ... +├── organization-b +│ ├── org-b-team-1 +│ ├── org-b-team-2 +│ ... +... +``` +Instead of each team asking from the `cluster-admin` to modify their `ResourceQuota`, +you can insert an additional "policy" namespace above each level to hold +the policy objects (like hierarchical quota) that the sub-admin _cannot_ +change, while giving them permission to create their own quotas in the +lower level namespaces, like this: +``` +company-a-policy +└── company-a +``` +And put the resources HRQ in the `company-a-policy` namespace. This will restrict +whole `company-a` to the amount of resources that they are paying for. Then `company-a` +can do similar HRQ with their organizations: +``` +company-a-policy (has HRQ) +└── company-a + ├── org-a-policy (has HRQ) + │ └── organization-a + ├── org-b-policy (has HRQ) + │ └── organization-b + ... +``` +Lower-level quotas cannot override more restrictive quotas from ancestor namespaces; +the most restrictive quota always wins. +This way each individual can fairly and securely distribute their resources across +their members. + +To implement hierarchical quotas, HNC automatically creates `ResourceQuota` objects in each +affected namespace. This is a part of the internal implementation and shouldn't be modified or +inspected. Use the `kubectl hns hrq` command to inspect hierarchical quotas, +or look at the `HierarchicalResourceQuota` object in the ancestor +namespaces. + +Note: Decimal point values cannot be specified in HRQ (you can't do `cpu: 1.5` but you can do `cpu: "1.5"` or `cpu: 1500m`). See [#292](https://github.com/kubernetes-sigs/hierarchical-namespaces/issues/292) + ### Policy inheritance and object propagation @@ -327,7 +394,7 @@ HNC typically propagates _all_ objects of a [specified type](how-to.md#admin-res from ancestor namespaces to descendant namespaces. However, sometimes this is too restrictive, and you need to create ***exceptions*** to certain policies. For example: -* A ResourceQuota was propagated to many children, but one child namespace now +* A `ResourceQuota` was propagated to many children, but one child namespace now has higher requirements than the rest. Rather than getting rid of the quota in the parent namespace, or raising the limit for everyone, you can stop the quota in the parent from being propagated to that _one_ child namespace, @@ -345,7 +412,7 @@ an object can also control how it is propagated to descendant namespaces. If you modify an exception - for example, by removing it - this could cause the object to be propagated to descendants from which it had previously been excluded. This could cause you to accidentally overwrite objects that were -intended to be exceptions from higher-level policies, like the ResourceQuota +intended to be exceptions from higher-level policies, like the `ResourceQuota` in the example above. To prevent this, if modifying an exception would cause HNC to overwrite another object, HNC’s admission controllers will prevent you from modifying the object, and will identify the objects that would have been diff --git a/docs/user-guide/how-to.md b/docs/user-guide/how-to.md index 8bf6dfcba..22af4feac 100644 --- a/docs/user-guide/how-to.md +++ b/docs/user-guide/how-to.md @@ -10,6 +10,7 @@ This document describes common tasks you might want to accomplish using HNC. * [Create a subnamespace](#use-subns-create) * [Inspect namespace hierarchies](#use-inspect) * [Propagating policies across namespaces](#use-propagate) + * [Apply hierarchical resource quotas (HRQs)](#use-hrq) * [Select namespaces based on their hierarchies](#use-select) * [Delete a subnamespace](#use-subns-delete) * [Organize full namespaces into a hierarchy](#use-full) @@ -186,6 +187,21 @@ permissions. To understand why an object is not being propagated to a namespace, use `kubectl hns describe `, where `` is either the source (ancestor) or destination (descendant) namespace. + + +### Limit Resources over parent namespaces + +***Hierarchical resource quotas are beta in HNC v1.1*** + +***HRQs are not included by default, to use it install the hrq.yaml file in the [releases -> Assets](https://github.com/kubernetes-sigs/hierarchical-namespaces/releases)*** + +HNC has an object called `HierarchicalResourceQuota` which is a drop-in replacement for `ResourceQuota` +but across all the namespaces in a hierarchy. It allows you to distribute your resources between +teams, and those teams can distribute their resources between their subteams. +[Learn how it works](concepts.md#hierarchical-resource-quota) or see an [quickstart example](quickstart.md#hrq) + +Note: Decimal point values cannot be specified in HRQ (you can't do `cpu: 1.5` but you can do `cpu: "1.5"` or `cpu: 1500m`). See [#292](https://github.com/kubernetes-sigs/hierarchical-namespaces/issues/292) + ### Select namespaces based on their hierarchies diff --git a/docs/user-guide/quickstart.md b/docs/user-guide/quickstart.md index a7b25b3b6..418ee9911 100644 --- a/docs/user-guide/quickstart.md +++ b/docs/user-guide/quickstart.md @@ -21,6 +21,7 @@ also to all contributors since then. * [Basic functionality](#basic) * [Propagating different resources](#resources) * [Hierarchical network policy](#netpol) +* [Hierarchical resource quotas](#hrq) * [Subnamespaces deep-dive](#subns) * [Keeping objects out of certain namespaces](#exceptions) @@ -341,7 +342,7 @@ Now we'll create a default network policy that blocks any ingress from other namespaces: ```bash -cat << EOF | kubectl apply -f - +kubectl apply -f - << EOF kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata: @@ -394,7 +395,7 @@ other. We do this by creating a policy that selects namespaces based on the that is automatically added by the HNC. ```bash -cat << EOF | kubectl apply -f - +kubectl apply -f - << EOF kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata: @@ -458,6 +459,78 @@ kubectl delete svc s2 -n service-2 kubectl delete pods s2 -n service-2 ``` + + +### Hierarchical resource quotas + +***Hierarchical resource quotas are beta in HNC v1.1*** + +***HRQs are not included by default, to use it install the hrq.yaml file in the [releases -> Assets](https://github.com/kubernetes-sigs/hierarchical-namespaces/releases)*** + +_Will demonstrate: Create and delete [HierarchicalResourceQuota](concepts.md#hierarchical-resource-quota)._ + +Let's assume you own _acme-org_ from the previous example: +``` +acme-org +├── [s] team-a +└── [s] team-b +``` + +You can create a `HierarchicalResourceQuota` in namespace `acme-org`, and the sum of +all subnamespaces resource usage can't go over what is configured in the HRQ. + +We will demonstrate how it works using services, but you could also limit `cpu`, +`memory` or any other `ResourceQuota` field. + +Creating the HRQ: +```bash +kubectl apply -f - << EOF +kind: HierarchicalResourceQuota +apiVersion: hnc.x-k8s.io/v1alpha2 +metadata: + name: acme-org-hrq + namespace: acme-org +spec: + hard: + services: 1 +EOF +``` + +Lets create Service in namespace `team-a`: +```bash +kubectl create service clusterip team-a-svc --clusterip=None -n team-a +``` + +And when we try to create Service in namespace `team-b`: +```bash +kubectl create service clusterip team-b-svc --clusterip=None -n team-b +``` +We get an error: +``` +Error from server (Forbidden): + error when creating "STDIN": + admission webhood "resourcesquotasstatus.hnc.x-k8s.io" denied the request: + exceeded hierarchical quota in namespace "acme-org": + "acme-org-hrq", requested: services=1, used: services=1, limited: services=1 +``` + +To view the HRQ usage, simply run: +```bash +kubectl hns hrq -n acme-org +``` +You can also view in all namespace: +```bash +kubectl hns hrq --all-namespaces +``` + +And you can delete the HRQ via simply deleting the CR: +```bash +kubectl delete hrq acme-org-hrq -n acme-org +``` + +Note: Decimal point values cannot be specified (you can't do `cpu: 1.5` but +you can do `cpu: "1.5"` or `cpu: 1500m`). See [#292](https://github.com/kubernetes-sigs/hierarchical-namespaces/issues/292) + ### Subnamespaces deep dive @@ -672,7 +745,7 @@ Of course, the annotation can also be part of the object when you create it: ```bash kubectl delete secret my-secret -n acme-org -cat << EOF | k create -f - +kubectl create -f - << EOF apiVersion: v1 kind: Secret metadata: diff --git a/test/e2e/quickstart_test.go b/test/e2e/quickstart_test.go index 714e393e2..c619fd597 100644 --- a/test/e2e/quickstart_test.go +++ b/test/e2e/quickstart_test.go @@ -191,6 +191,43 @@ spec: RunErrorShouldContain("wget: download timed out", defTimeout, clientCmd, nsTeamB, alpineArgs, wgetArgs) }) + PIt("Create and use HierarchicalResourceQuota", func() { + // set up initial structure + CreateNamespace(nsOrg) + CreateSubnamespace(nsTeamA, nsOrg) + CreateSubnamespace(nsTeamB, nsOrg) + + expected := "" + // empty string make go fmt happy + nsOrg + "\n" + + "├── [s] " + nsTeamA + "\n" + + "└── [s] " + nsTeamB + // The subnamespaces takes a bit of time to show up + RunShouldContain(expected, propogationTimeout, "kubectl hns tree", nsOrg) + + // create hrq in parent acme-org + hrq:=`# quickstart_test.go: hrq in acme-org +kind: HierarchicalResourceQuota +apiVersion: hnc.x-k8s.io/v1alpha2 +metadata: + name: acme-org-hrq + namespace: acme-org +spec: + hard: + services: 1` + MustApplyYAML(hrq) + + // create service in team-a + MustRun("kubectl create service clusterip", nsTeamA + "-svc", "--clusterip=None", "-n", nsTeamA) + + // show that you can't use resources more than the hrq + MustNotRun("kubectl create service clusterip", nsTeamB + "-svc", "--clusterip=None", "-n", nsTeamB) + + // show hrq usage + RunShouldContain("services: 1/1", defTimeout, "kubectl hns hrq", "-n", nsOrg) + + MustRun("kubectl delete hrq", nsOrg + "-hrq", "-n", nsOrg) + }) + It("Should create and delete subnamespaces", func() { // set up initial structure CreateNamespace(nsOrg)