在搭建大型共享集群时,首先要确保的是在多个用户同时使用集群时不会互相干扰,使用Kubernetes命名空间来隔离不同开发者是自然而然的。命名空间可以当作服务的边界,使得一个用户的前端服务不会干扰其他用户的相同服务。命名空间同样也是RBAC的边界,这样开发者就不会意外删除其他人的工作成果。因此在共享集群中,使用命名空间作为开发者的工作空间是合理的。在接下来的章节中我们将介绍如何添加用户和命名空间,以及如何保护命名空间。
将用户分配到命名空间之前,必须先将用户添加到集群。添加用户有两种方式:使用基于证书的认证方式,为用户创建新的证书并提供一个kubeconfig配置文件来登录;为集群配置外部认证(比如Microsoft Azure Active Directory或者AWS Identity and Access Management [IAM])。
通常,使用外部认证是一种最佳实践,因为你不需要维护两种不同的身份标识来源。但是某些场景下只能使用证书,幸运的是,你可以通过Kubernetes证书API来创建和管理这些证书。以下是将新用户添加到现有集群的详细流程。
首先,你需要生成证书签名请求来生成新的证书,以下是一个执行此操作的简单Go程序:
可以使用以下命令运行该程序:
go run csr-gen.go client <user-name>;
它将创建client-key.pem和client.csr这两个文件,然后可以运行以下脚本来创建和下载证书:
可以将此脚本打印出的相关信息添加到kubeconfig配置文件以启用用户。当然,此用户还没有任何访问权限,还需要为该用户配置Kubernetes RBAC来授予它对某个命名空间的权限。
首先我们需要创建一个命名空间。可以使用
kubectl create namespace mynamespace
命令进行创建。
实际过程中,会在创建命名空间时为其设置一些元数据,例如,使用该命名空间的团队的联系方式。通常,这是以注解的形式标记的。可以使用一些模板技术(Jinja或其他)生成包含注解的YAML文件,或者先创建命名空间,然后再为其添加注解,如以下的简单脚本所示:
在创建命名空间后,需要确保将其访问权限授予给特定用户,以保证此命名空间的安全。为此,需要将角色绑定到该命名空间中的用户,这可以通过在命名空间中创建一个
RoleBinding
对象来实现。如下所示:
可以运行
kubectl create -f role-binding.yaml
命令来创建它。请注意,只要将
RoleBinding
中的
namespace
更新为正确的命名空间,就可以根据需要重用该绑定。如果你确信该用户没有绑定其他的角色,则可以断言在整个集群中该用户仅能访问此命名空间。另外一种合理的做法是授予用户对整个集群的读取权限,这样开发者可以查看其他人的操作,以防干扰他们的工作。但是请小心授予这类读取权限,因为这些权限会包含对机密资源的访问权限,这种做法在开发集群中通常是可行的,因为所有人位于同一组织并且机密仅仅用作开发用途。但是,如果你对此存在顾虑,那么就需要创建更细粒度的角色以取消其读取机密资源的权限。
如果你希望限制特定命名空间所能消耗的资源总量,可以使用ResourceQuota资源为特定的命名空间设置资源使用限制。例如,如下的ResourceQuota将此命名空间中所有Pod的Request和Limit的资源总量限制都设为10核CPU和100G内存:
现在你已经了解了如何添加新用户以及如何创建命名空间作为其工作空间,接下来的问题就是如何将开发者分配给命名空间。与许多问题一样,这里并没有一个标准答案。确切地说,有两种方式可供选择。一种是在用户加入初期,为其分配一个独有的命名空间,这样他们就可以在专有的工作空间中开发和管理应用。但是如果命名空间长期存在,会让开发者在使用完命名空间后不自觉地遗留一些资源,这将使垃圾回收和资源独立计费变得更加复杂。另一种方式则是每次临时创建和分配命名空间,并为其设置有限的留存时间(TTL)。这会确保开发者将集群中的资源视为临时的,而且在命名空间过期后能够自动删除。
在这种模式下,当开发者想要开始一个新项目时,可以使用工具为项目分配一个新的命名空间。并且在创建命名空间时,需要关联一组用于命名空间管理和计费的元数据。显然,这些元数据中应该包含命名空间的TTL、所分配的开发者、资源配额(例如CPU和内存)以及所属的团队和用途。它们能够确保你既能对资源的使用情况进行跟踪,又能在合适的时间删除命名空间。
开发一款支持按需分配命名空间的工具看起来很有挑战,不过开发一些简单的工具却没那么难。例如,可以开发一个简单的脚本来分配命名空间,它创建命名空间,并提示将相关元数据添加到命名空间。
如果要与Kubernetes进一步集成,则可以使用CRD(自定义资源),它能让用户使用
kubectl
工具动态地创建和分配新的命名空间。如果你有足够的时间和意愿,这绝对是一个好的实践,因为它可以声明式地管理命名空间,并且支持使用Kubernetes RBAC。
分配命名空间的工具就绪后,还需要开发用来回收已过期命名空间的工具。为此,同样可以通过简单的脚本来检查命名空间是否过期,并删除已过期的命名空间。
可以将这些脚本构建到容器中,并使用
ScheduledJob
以一定的间隔(比如每小时一次)运行。这些工具的结合可以确保开发者轻松地为项目分配独立资源,并且这些资源会在合适的时间进行回收,这样既保证了资源不会浪费,同时也确保了过期的资源不会阻碍新的开发。
除了用于分配和管理命名空间的工具外,还有一些实用的集群级服务,最好在开发集群中也启用它们。首先要考虑的就是将日志聚合到中心化的日志即服务(Logging as a Service)系统。开发者想要了解应用程序的运行情况,最简单的做法就是将调试信息打印到标准输出(STDOUT)。虽然可以通过
kubectllogs
命令访问这些日志,但是这些日志只能保留有限的长度且不易于搜索。然而,如果将日志自动发送到日志即服务系统(例如某个云服务或Elasticsearch集群),开发者将能够在日志中轻松地搜索相关信息,并且能够将服务中跨多个容器的日志聚合在一起。