5:虚拟集群与命名空间

命名空间(Namespace)是将 Kubernetes 集群划分为多个虚拟集群的一种方式。

本章将奠定 命名空间 的基础,让你快速掌握创建和管理它们的方法,并介绍一些使用场景。在后续章节中你将看到它们的实际应用。

本章内容划分如下:

  • 命名空间简介
  • 命名空间使用场景
  • 默认命名空间
  • 创建和管理命名空间
  • 向命名空间部署应用

命名空间简介

首先要知道,Kubernetes 命名空间与 Linux 内核命名空间不同。

  • 内核命名空间将操作系统划分为称为“容器”的虚拟操作系统。
  • Kubernetes 命名空间将 Kubernetes 集群划分为称为“命名空间”的虚拟集群。

术语约定

当指代 Kubernetes 命名空间时,我们将大写首字母(Namespace)。这遵循了 Kubernetes API 资源的大写惯例,并明确我们指的是 Kubernetes 命名空间,而非内核命名空间。

同样重要的是,命名空间是一种软隔离形式,支持软多租户。例如,你可以为开发、测试和 QA 环境创建不同的命名空间,并为每个命名空间应用不同的配额和策略。然而,一个命名空间中受损的工作负载很容易影响其他命名空间中的工作负载。

以下命令显示对象是否为命名空间作用域。如你所见,大多数对象是命名空间作用域,这意味着你可以将它们部署到具有自定义策略和配额的特定命名空间。非命名空间作用域的对象(如 Node 和 PersistentVolume)是集群作用域的,你不能将它们部署到特定的命名空间。

$ kubectl api-resources
NAME                     SHORTNAMES   ...    NAMESPACED   KIND
nodes                    no                  false        Node
persistentvolumeclaims   pvc                 true         Persist
persistentvolumes        pv                  false        Persist
pods                     po                  true         Pod
podtemplates                                 true         PodTemp
replicationcontrollers   rc                  true         Replica
resourcequotas           quota               true         Resourc
secrets                                      true         Secret
serviceaccounts          sa                  true         ServiceA
services                 svc                 true         Service
<Snip>

除非你另行指定,否则 Kubernetes 会将对象部署到 default 命名空间。

命名空间使用场景

命名空间是多个租户共享同一个集群的一种方式。

“租户”是一个宽泛的术语,可以指单个应用程序、不同团队或部门,甚至外部客户。如何实现命名空间以及将什么视为租户取决于你,但最常见的用法是将集群划分为同一组织内的不同租户。

例如,你可以将一个生产集群划分为以下三个命名空间,以匹配你的组织结构:

  • finance
  • hr
  • corporate-ops

你将金融应用部署到 finance 命名空间,HR 应用部署到 hr 命名空间,企业应用部署到 corporate-ops 命名空间。每个命名空间可以拥有自己的用户、权限、资源配额和策略。

使用命名空间将集群划分给外部租户的情况并不常见。这是因为它们只提供软隔离,无法阻止受损的工作负载逃离命名空间并影响其他命名空间中的工作负载。在撰写本文时,实现强隔离的最常见方式是将租户运行在它们各自的集群和各自的硬件上。

图 5.1 展示了左侧一个使用命名空间进行软多租户的集群。该集群上的所有应用共享相同的节点和控制平面,受损的工作负载可能影响两个命名空间。右侧的两个集群通过实现两个独立的集群(每个集群使用专用硬件)来提供强隔离。

![Image 1507 on Page 140]
图 5.1 - 软隔离与硬隔离

综上所述,命名空间轻量且易于管理,但仅提供软隔离。运行多个集群成本更高且引入更多管理开销,但提供了强隔离。

默认命名空间

每个 Kubernetes 集群都有一组预先创建的命名空间。运行以下命令列出你的命名空间。

$ kubectl get namespaces
NAME             STATUS    AGE
default           Active   2d
kube-system       Active   2d
kube-public       Active   2d
kube-node-lease   Active   2d
  • default 命名空间:如果你在创建对象时未指定命名空间,新对象将放置于此。
  • kube-system 命名空间:控制平面组件(如内部 DNS 服务和 metrics-server)运行于此。
  • kube-public 命名空间:用于需要可被任何人读取的对象。
  • 最后但同样重要的是,kube-node-lease 命名空间用于节点心跳和管理节点租约。

运行 kubectl describe 来检查集群上的一个命名空间。在 kubectl 中,你可以用 ns 代替 namespace

$ kubectl describe ns default
Name:         default
Labels:       kubernetes.io/metadata.name=default
Annotations:  <none>
Status:       Active
No resource quota.
No LimitRange resource.

你也可以在 kubectl 命令中添加 -n--namespace 来针对特定命名空间过滤结果。

运行以下命令列出 kube-system 命名空间中的所有 Service 对象。你的输出可能不同。

$ kubectl get svc --namespace kube-system
NAME                 TYPE           CLUSTER-IP     EXTERNAL-IP   
kube-dns             ClusterIP      10.43.0.10     <none>        
metrics-server       ClusterIP      10.43.4.203    <none>        4
traefik-prometheus   ClusterIP      10.43.49.213   <none>        
traefik              LoadBalancer   10.43.222.75   <pending>     

你还可以使用 --all-namespaces 标志返回所有命名空间中的对象。

创建和管理命名空间

在本节中,你将看到如何创建、检查并删除命名空间。

如果你希望跟随操作,需要克隆本书的 GitHub 仓库,并切换到 2025 分支。

$ git clone https://github.com/nigelpoulton/TKB.git
<Snip>
$ cd TKB
$ git fetch origin
$ git checkout -b 2025 origin/2025

你还需在 namespaces 目录下运行所有命令。

命名空间是核心 v1 API 组中的一等资源。这意味着它们稳定、理解充分且存在已久。同时也意味着你可以通过命令式(CLI)和声明式(配置文件)两种方式操作它们。我们将同时使用两种方式。

运行以下命令式命令创建一个名为 hydra 的新命名空间。

$ kubectl create ns hydra
namespace/hydra created

现在,从 shield-ns.yml YAML 文件以声明方式创建一个。这是一个简单的文件,定义了一个名为 shield 的命名空间。

kind: Namespace
apiVersion: v1
metadata:
  name: shield
  labels:
    env: marvel

用以下命令创建它。

$ kubectl apply -f shield-ns.yml
namespace/shield created

列出所有命名空间,查看你创建的两个新命名空间。

$ kubectl get ns
NAME         STATUS   AGE
<Snip>
hydra        Active   49s
shield       Active   3s

如果你对漫威电影宇宙有所了解,你会知道 Shield 和 Hydra 是死对头,绝不应该仅靠命名空间隔离在同一个集群中。

删除 hydra 命名空间。

$ kubectl delete ns hydra
namespace "hydra" deleted

为特定命名空间配置 kubectl

在处理命名空间时,你很快就会发现在所有 kubectl 命令上添加 -n--namespace 标志很麻烦。更好的方法是设置你的 kubeconfig,使其自动针对特定命名空间运行命令。

运行以下命令将你的 kubeconfig 配置为所有未来的 kubectl 命令都针对 shield 命名空间。

$ kubectl config set-context --current --namespace shield
Context "tkb" modified.

运行一些简单的 kubectl get 命令来测试它是否生效。shield 命名空间为空,因此你的命令不会返回任何对象。

将对象部署到命名空间

如前所述,大多数对象是命名空间作用域的,并且除非你另行指定,Kubernetes 将新对象部署到 default 命名空间。

有两种方式将对象部署到特定命名空间:

  • 命令式
  • 声明式

命令式方式:在命令中添加 -n--namespace 标志。声明式方式:在对象的 YAML 清单中指定命名空间。

让我们使用声明式方法将一个应用部署到 shield 命名空间。

该应用定义在本书 GitHub 仓库的 namespaces 文件夹中的 app.yml 文件中。它定义了三个对象:一个 ServiceAccount、一个 Service 和一个 Pod。以下 YAML 摘录显示了这三个对象都指向 shield 命名空间。

如果你不完全理解 YAML 中的内容也不用担心。你只需要知道它定义了三个对象,并将每个对象定位到 shield 命名空间。

apiVersion: v1
kind: ServiceAccount
metadata:
  namespace: shield     # <<---- 命名空间
  name: default
---
apiVersion: v1
kind: Service
metadata:
  namespace: shield     # <<---- 命名空间
  name: the-bus
spec:
  type: LoadBalancer
  ports:
  - port: 8080
    targetPort: 8080
  selector:
    env: marvel
---
apiVersion: v1
kind: Pod
metadata:
  namespace: shield     # <<---- 命名空间
  name: triskelion
<Snip>

用以下命令部署它。如果收到关于 ServiceAccount 缺少注解的警告,不必担心。

$ kubectl apply -f app.yml
serviceaccount/default configured
service/the-bus configured
pod/triskelion created

运行一些命令来验证所有三个对象都在 shield 命名空间中。如果你已将 kubectl 配置为自动针对 shield 命名空间,则无需添加 -n shield 标志。

$ kubectl get pods -n shield
NAME         READY   STATUS    RESTARTS   AGE
triskelion   1/1     Running   0          48s
$ kubectl get svc -n shield
NAME      TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)     
the-bus   LoadBalancer   10.43.30.174   localhost     8080:31112/T

部署应用后,将浏览器或 curl 命令指向 EXTERNAL-IP 列的地址,端口为 8080。一些 Docker Desktop 集群可能在 EXTERNAL-IP 中错误地显示一个 172 开头的 IP 地址。你需要将其替换为 localhost 并连接到 localhost:8080

$ curl localhost:8080
<!DOCTYPE html>
<html>
<head>
    <title>AOS</title>
    <Snip>

祝贺你。你已成功创建命名空间并将应用部署到其中。连接到应用的方式与连接到 default 命名空间中的应用完全相同。

清理

以下命令将清理你的集群,并将 kubeconfig 恢复为使用 default 命名空间。

删除 shield 命名空间。这将自动删除你部署到其中的 Pod、Service 和 ServiceAccount。命令可能需要几秒钟完成。

$ kubectl delete ns shield
namespace "shield" deleted

重置 kubeconfig 使其使用 default 命名空间。如果不这样做,未来的命令将针对已删除的 shield 命名空间运行,不会返回任何结果。

$ kubectl config set-context --current --namespace default
Context "tkb" modified.

本章总结

在本章中,你了解到 Kubernetes 使用命名空间将集群划分为资源管理和记账目的。每个命名空间可以拥有自己的用户、RBAC 规则和资源配额,并且你可以选择性地将策略应用于命名空间。然而,它们并非强大的工作负载隔离边界,因此不能用于硬多租户。

如果你在部署时未指定命名空间,Kubernetes 会将对象部署到 default 命名空间。