3.3 名称空间
由多个独立的团队或项目共享使用的Kubernetes集群或许会运行大量彼此无关的工作负载,即便使用需求相对较小,部署的对象数量也可能很快变得难以管理,进而降低操作响应速度并增加发生危险错误的可能性。Kubernetes使用“名称空间”来帮助降低这种复杂性:通过一个抽象组件将存在关联关系的对象组织成一个逻辑单元进行统一管控,可用于对集群中的任意对象组进行分类、筛选和管理。
注意
Kubernetes的名称空间不同于Linux系统的名称空间,它们是各自独立的概念。另外,Kubernetes名称空间并不能实现Pod间的通信隔离,它仅用于限制资源对象名称的作用域。
3.3.1 名称空间的作用
名称空间的核心功能在于限制集群上对象名称的作用域:同一类型的资源对象名称在同一个名称空间中必须唯一,但不同名称空间却可为同一类型的资源使用相同的名称。对于某些场景,这种功能有着非常实用的价值,例如名称空间可用于分隔不同应用程序的生命周期环境(例如开发、预发和生产等),从而能够在每个环境中维护具有相同名称的同一类型的资源对象。名称空间本身并不具有网络隔离和访问限制等功能,但它可以作为网络访问控制策略(NetworkPolicy)、授权策略(RBAC)、资源限制策略(ResourceQuota)和Pod安全策略(PodSecurityPolicy)等管理逻辑的承载组件,这也是支撑集群“多租户”机制的基础组件。
Kubernetes API使用Namespace资源类型来表示名称空间,它自身是集群级别(或称为全局级别)的资源,不能嵌套于其他名称空间,但Kubernetes的绝大多数资源类型都隶属于名称空间级别,例如Pod、Service、Deployment和StatefulSet等。而集群级别的资源类型除了Namespace外还有Node和PersistentVolume等,它们的名称标识作用域为集群全局。
默认情况下,Kubernetes集群会内置如下4个名称空间,其中第4个名称空间是为kubelet引入租约机制后才新增的。
▪default:创建名称空间级别的资源对象,但未指定从属的名称空间时将默认使用defaut名称空间。
▪kube-public:用于为集群上的所有用户(包括匿名用户)提供一个公共可用的名称空间,保留给集群使用,以防某些资源在整个集群中公开可见;该名称空间的公共属性仅是约定,并非强制要求。
▪kube-system:用于部署与Kubernetes系统自身相关的组件,例如kube-proxy、CNI网络插件,甚至是kube-apiserver、kube-controller-manager和kube-scheduler等控制平面组件的静态Pod等;不建议在该名称空间中运行与系统无关的工作负载。
▪kube-node-lease:目前是专用于放置kubelet lease对象的名称空间,这些对象对于Kubernetes系统自身健康运行至关重要;因而同样不建议在该名称空间中运行与系统无关的工作负载。
kubectl get namespaces命令可以查看系统上的namespaces资源:
~$ kubectl get namespaces NAME STATUS AGE default Active 2d kube-node-lease Active 2d kube-public Active 2d kube-system Active 2d
对于那些隶属名称空间级别的资源,通过API对其进行管理操作时必须明确指定名称空间,kubectl命令经常使用的选项-n或--namespace就用于此目的。例如,下面用于查看kube-system下的所有Pod资源的命令:
~$ kubectl get pods -n kube-system
细心的读者可以看到内置各名称空间下的资源可能各不相同,但有时也可具有相同名称的同类型资源,这正是Namespace资源名称隔离功能的具体体现。
3.3.2 管理Namespace资源
Kubernetes Namespace资源的管理支持3种方式,用户可按需选择最适合的一种。本节将以Namespace对象为例详细说明这3种操作方式的用法。不过,出于版本控制等目的,建议读者在非测试场景中使用命令式对象配置或声明式对象配置的管理机制。
1. 命令式命令
命令式命令的管理通常是针对集群上的活动对象直接进行,用户或管理员针对指定的资源运行create、get、describe、edit和delete命令即能完成资源的创建、查看、编辑和删除等基础管理操作。以Namespace资源为例,创建名称空间要使用kubectl create namespace命令,例如,下面的命令创建了名为myns的名称空间。
~$ kubectl create namespace myns namespace/myns created
注意
Namespace资源的名称仅能由字母、数字、连接线、下划线等字符组成。
get命令默认显示资源对象简要状态信息,而使用-o yaml或-o json选项。get命令能够返回资源对象的元数据、期望的状态及当前状态等信息,这些其实是Kubernetes保存的当前API对象的完整规范定义,只不过status是由系统自己填充的。以Namespace对象myns为例的命令及结果如下所示。
~$ kubectl get namespaces myns -o yaml apiVersion: v1 kind: Namespace metadata: creationTimestamp: "2020-07-12T04:54:10Z" name: myns resourceVersion: "690155" selfLink: /api/v1/namespaces/myns uid: 289acccc-73b7-42da-9eb3-d7e8080256be spec: finalizers: - kubernetes status: phase: Active
get命令也能够借助--custom-columns选项自定义要显示的字段,例如下面的命令只显示了myns名称空间的简要信息中的Name字段。
~$ kubectl get namespaces/myns -o custom-columns=NAME:metadata.name NAME myns
若要打印活动对象的详细信息,则需要用到kubectl describe命令,该命令可根据资源清单、资源名或卷标等方式过滤输出符合条件的资源对象的信息。命令格式为kubectl describe TYPE/NAME。例如,显示myns名称空间的详细信息,可使用类似如下命令。
~$ kubectl describe namespace myns Name: myns Labels: <none> Annotations: <none> Status: Active No resource quota. No LimitRange resource.
对于Namespace资源来说,describe命令能够返回活动对象的名称、标签、注解、状态、资源配额(Resource Quota)和资源限制范围(Limit Range)等信息。不同资源类型的详细描述各不相同,但这些详情对于了解目标资源对象的状态或进行错误排查等操作来说至关重要。资源配额用于跟踪名称空间中资源的总体使用情况,以及让管理员定义名称空间可以消耗的硬资源上限,而限制范围用于定义单个实体可以在Namespace中消耗的资源量的最小及最大约束,后面的章节对这两个概念将有详细描述。
对于活动对象,并非其每个属性值都支持修改,例如Namespace对象的metadata.name字段就不支持修改,除非删除并重建它。对于那些支持修改的属性,可以使用kubectl edit TYPE NAME命令进行编辑,该命令会首先使用get命令获取目标对象的配置清单并保存在临时文件中,打开默认编辑器(由EDITOR或KUBE_EDITOR环境变量定义)进行编辑,在保存后提交(声明式操作命令apply)到系统之上。例如下面的命令可打开myns名称空间的配置的编辑界面,不过,名称空间资源并无太多可直接编辑的配置,因此一般很少会有修改需求,但也有些类型的资源(例如Service和Deployment等)的编辑操作很常见。
~$ kubectl edit namespaces myns
不再需要的名称空间可以使用kubectl delete命令予以删除,但删除名称空间会级联清除其内部部署的所有资源,因而它是一个便捷、强大但又非常危险的命令。在删除之前列出其内部所有资源并再三确认各资源是否仍被需要应该被视作一个常规步骤。管理员切不可对Kubernetes内置的名称空间进行删除操作,当然,对于不再包含任何资源的自定义名称空间,通常可以安全删除。
~$ kubectl get all -n myns No resources found in myns namespace. ~$ kubectl delete namespaces myns namespace "myns" deleted
表3-1给出了几个常用的删除命令格式。
2. 命令式对象配置
命令式对象配置管理机制,是将资源操作命令作用于资源对象的配置清单进行的管理操作,常用的命令有create、delete、replace、get和describe等。与前一节命令式命令的格式不同,这些命令都要使用-f FILENAME选项从配置清单中读取资源对象的相关信息,这也是将它称为命令式对象配置的主要原因。
绝大多数API资源对象的配置格式都遵循3.2节中描述的资源对象配置规范,主要由apiVersin、kind、metadata和spec这4个一级字段组成,其中,spec是定义期望状态的核心所在。Namespace资源对象的配置格式文档可通过kubectl explain namespaces命令得到。下面的配置清单定义了一个名为demo的Namespace对象:
apiVersion: v1 kind: Namespace metadata: name: demo spec: finalizers: - kubernetes
Namespace对象仅支持在spec中定义期望使用的终结器(finalizer,也称为垃圾收集器),用于让监测者在删除名称空间时清除相关的资源,它是个可选字段,且目前仅支持使用kubernetes作为其属性值。在Namespace对象上指定了不存在的终结器时并不影响创建操作,但删除该Namespace对象的操作将会被“卡住”,即删除操作将一直停留在Terminating状态。
提示
如果读者熟悉JSON,也可直接将清单文件定义为JSON格式;YAML格式的清单文件本身也是由API Server事先将其转换为JSON格式而后才进行应用。
把上面的配置清单保存于以.yaml或.yml结尾的文本格式的文件中,例如ns-demo.yaml,使用kubectl [COMMAND] -f /PATH/TO/YAML_FILE命令就能够使用命令式对象配置进行资源对象创建,下面是相应的命令及响应结果。
$ kubectl create -f ns-demo.yaml namespace/demo created
命令的返回信息表示Namespace对象demo得以成功创建。事实上,create命令中的-f选项也支持使用目录路径或URL,而且目标路径为目录时,还支持使用-R选项进行子目录递归。另外,--record选项可以把命令本身记录为目标对象的注解信息kubernetes.io/change-cause,而--save-config选项则能够把提供给命令的资源对象配置信息保存于对象的注解信息kubectl.kubernetes.io/last-applied-configuration中,后一个选项的功能与声明式对象配置命令apply的功能相近。
对于命令式对象配置来说,查看对象的配置或状态依然要通过get或describe命令进行,但要以-f FILENAME选项从指定配置清单中的metadata.name字段获取目标资源对象。
~$ kubectl get -f ns-demo.yaml NAME STATUS AGE demo Active 4s
kubectl replace命令提供了一种替换式对象更新机制,它删除了配置清单中目标资源的名称对应的现存对象,并基于配置清单重新创建了一个同名的该类型对象,因此这是一种破坏性更新(disruptive update)机制。同时为命令使用--force[=true]选项可实现立即执行删除操作而无视宽限期(grace-period)的定义。该命令仅支持更新操作,若目标对象不存在时,则会报错并终止。例如,以前面创建的Namespace对象demo为例,删除其指定的终结器,首先需要将其spec.finalizers字段的值修改为空值,如下所示。
apiVersion: v1 kind: Namespace metadata: name: demo spec: finalizers:
将上面的配置清单保存于指定的配置清单,例如ns-demo-v2.yaml,而后使用下面的命令即可完成替换操作,即删除demo之后进行重新创建。我们已经知道,删除名称空间时会删除其内部的所有资源,因此这种修改操作的破坏性毋庸置疑,务必要谨慎使用。实践中,Namespace对象几乎不存在修改的需求,该操作仅是用于描述replace命令的用法。
~$ kubectl replace --force -f ns-demo-v2.yaml namespace "demo" deleted namespace/demo replaced
命令式对象配置的删除操作同样使用delete命令,它同样基于配置清单获取目标对象的资源类型及名称标识,而后针对相应的对象执行删除操作。例如,如下命令可删除前面创建的demo名称空间。再次提醒,删除名称空间将级联删除其内部的所有资源。
$ kubectl delete -f ns-demo.yaml namespace "demo" deleted
有时候,名称空间的正常删除操作可能会莫名卡在Terminating状态。常用的解决方案是获取并保存目标Namespace对象的JSON格式的配置清单,将spec.finalizers字段的值置空,而后手动终止相应的名称空间对象即可。以demo为例,首先获取其当前配置:
~$ kubectl get namespaces/demo -o json > ns-demo-term.json
编辑ns-demo-term.json文件,将其中的spec.finalizers的配置部分修改为如下所示:
…… "spec": { "finalizers": [ ] }, ……
而后运行如下命令手动终止demo名称空间:
~$ kubectl replace --raw "/api/v1/namespaces/demo/finalize" -f ns-demo-term.json
3. 声明式对象配置
在命令式对象配置管理机制中,若同时指定了多个资源,则这些资源必须进行同一种操作(不可以有的进行创建,而另外一些进行更新等),而且更新操作的replace命令通过完全替换现有活动对象进行资源更新操作,对于生产环境来说,这并非理想的选择。声明式对象配置操作在管理资源对象时会把配置信息保存在目标对象的注解中,并通过比较活动对象的当前配置、前一次管理操作时保存于注解中的配置,以及当前命令提供配置生成更新补丁从而完成活动对象的补丁式更新操作。
例如,创建前面ns-demo.yaml中定义的名称空间,可以直接使用如下命令进行:
~$ kubectl apply -f ns-demo.yaml namespace/demo created
若apply的目标对象于集群中已然存在,且活动对象的当前配置、上一次管理操作时保存于注解中的存储,以及当前配置清单中的配置存在不同之外,则执行的是更新操作。否则,活动对象将保持不变。例如,将前面ns-demo-v2.yaml文件定义的配置apply到集群中实现更新操作的命令如下。
~$ kubectl apply -f ns-demo-v2.yaml namespace/demo configured
命令结果显示资源重新配置完成并已经生效。事实上,此类操作也完全能够使用patch命令直接进行补丁操作。而资源对象的删除操作依然可使用apply命令,但要同时使用--prune选项,命令的格式为kubectl apply -f <directory/> --prune -l <labels>。需要注意的是,此命令异常“凶险”,因为它将基于标签选择器过滤出所有符合条件的对象,并检查由-f指定的目录中是否存在某配置文件定义的相应资源对象,那些不存在相应定义的资源对象将被删除。因此,需要删除资源对象时,依然建议使用命令式对象配置的命令kubectl delete进行,这样的命令格式操作目标明确且不易出现偏差。