前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >[Kubernetes](一)Kubernetes设计解读

[Kubernetes](一)Kubernetes设计解读

原创
作者头像
baron
发布2019-12-05 14:59:32
6990
发布2019-12-05 14:59:32
举报
pod设计解读

在Kubernetes中,能够被创建、调度和管理的最小单元是pod,而非单个容器。前面已经说过,一个pod是由若干个Docker容器构成的容器组(pod意为豆荚,里面容纳了多个豆子,很形象)。这里需要强调的是,pod里的容器共享network namespace,并通过volume机制共享一部分存储。

  • pod是IP等网络资源的分配的基本单位,这个IP及其对应的network namespace是由pod里的容器共享的;
  • pod内的所有容器也共享volume。当有一个volume被挂载在同属一个pod的多个Docker容器的文件系统上时,该volume可以被这些容器共享。

另外,从Linux namespace的角度看,同属一个pod的容器还共享以下namespace。

  • IPC namespace,即同一个pod内的应用容器能够使用System V IPC或POSIX消息队列进行通信。
  • UTS namespace,即同一个pod内的应用容器共享主机名。

同一个pod里的容器有如下两个特性:

  • 通过Kubernetes volume机制,在容器之间共享存储;
  • 可以通过localhost直接访问另一个容器。

pod的使用

使用Kubernetes的客户端工具kubectl来创建pod,该命令行工具支持对Kubernetes对象(pod,replication controller, service)的增、删、改、查操作以及其他对集群的管理操作。创建Kubernetes资源对象的一般方法如下所示。

代码语言:txt
复制
$ kubectl create -f obj.json

其中obj.json可以是定义pod, replication controller, service等Kubernetes对象的JSON格式的资源配置文件。

下面是一个pod类型的json文件。

pod_json.png
pod_json.png

以上配置信息描述了一个name为podtest的对象。而该配置信息的kind字段表明该对象是一个pod。 apiVersion字段表明客户端使用的服务端API版本是v1。

spec:containers字段描述了pod内的容器的属性,包括:容器名(name )、镜像(image )、端口映射(ports)等。

replication controller设计解读(新版本已用seplicaSet替代)

Kubernetes中第二个重要的概念就是replication controller,它决定了一个pod有多少同时运行的副本,并保证这些副本的期望状态与当前状态一致。所以,如果创建了一个pod,并且在希望该pod是持续运行的应用时即仅适用于重启策略(RestartPolicy)为Always的pod ,一般都推荐同时给pod创建一个replication controller,让这个controller一直守护pod,直到pod被删除。

replication controller在设计上依然体现出了“旁路控制”的思想,在Kubernetes中并没有像Cloud Foundry那样设置一个专门的健康检查组件,而是为每个pod“外挂”了一个控制器进程,从而避免了健康检查组件成为性能瓶颈;即使这个控制器进程失效,容器依然可以正常运行,pod和容器无需知道这个控制器,也不会把这个控制器作为依赖。

pod的状态转换

在Kubernetes中,pod的状态值(podStatus)的数量和定义是系统严格保留和规定的,如下表所示:

pod_status及其含义.png
pod_status及其含义.png

replication controller的描述文件

前面已经介绍过,replication controller是Kubernetes为解决“如何构造完全同质的pod副本”问题而引人的资源对象。与pod对象类似,Kubernetes使用一份JSON格式的资源配置文件来定义一个replication controlle树象。replication controller的资源配置文件主要邮个方面组成:一个用于创建pod的pod模板(pod template )、一个期望副本数和一个用于选择被控制的pod集合的label selector。replication controller会不断地监测控制的pod集合的数量并与期望的副本数量进行比较,根据实际情况进行创建和删除pod的操作。一份定义replication controller的资源配置文件示例如下所示,该replication controller例化了两个运行nginx的pod。

replication_controller描述文件.png
replication_controller描述文件.png

replication controller的典型场景

  • 重调度。一旦当前的宿主机节点异常崩溃或pod运行终止Kubernetes就会进行相应pod重调度。
  • 弹性伸缩。不论是通过手动还是自动弹性伸缩控制代理来修改副本数(replicas)字段,replication controller都能轻松实现pod数量的弹性伸缩。
  • 滚动更新(灰度发布)。replication controller被设计成通过逐个替换pod的方式来进行副本增删操作,这使得容器的滚动更新会非常简单。
  • 应用多版本release追踪。在生产环境中一个已经发布的应用程序同时在线多个release版本是一个很普遍的现象。通过replica selector机制,我们能很方便地实现对一个应用的多版本release进行管理。
service设计解读

service这个概念存在的意义颇为重要,首先由于重新调度等原因,pod在Kubernetes中的IP地址不是固定的,因此需要一个代理来确保需要使用pod的应用不需要知晓pod的真实IP地址。另一个原因是当使用replication controller创建了多个pod的副本时,需要一个代理来为这些pod做负载均衡。

service主要由一个IP地址和一个label selector组成。在创建之初,每个service便被分配了一个独一无二的IP地址,该IP地址与service的生命周期相同且不再更改(pod IP地址与此不同,会随着pod的生命周期产生及消亡)。

如何定义一个service

和pod一样,service也是一个Kubernetes REST对象,客户端可以通过向APIServe泼送一个http POST请求来创建一个新的service实例。假设有一个pod集,该pod集中所有pod均被贴上labels{"app": "MyApp"},且所有容器均对外暴露TCP 9376端口。那么以下配置信息指定新创建一个名为myapp的service对象。

service定义样例.png
service定义样例.png

该service会将外部流量转发到所有label匹配{"app" : "MyApp"}的pod的9376 TCP端口上。每个service会由系统分配一个虚拟IP地址作为service的人口IP地址(cluster IP ),然后监听上述文件中的port指定的端口(比如上述的80端口)。上述虚拟IP地址和port的组合称为service入口。而当名为my-service的service对象被创建后,系统就会随之创建一个同样名为my-service的Endpoints对象,该对象即保存了所有匹配label selector端pod的IP地址和端口。

使用service来代理遗留系统

需要注意的是,与replication controller不同,service对象的.spec.selector属性是可选项,即允许存在没有label selector的service。这类service有什么作用呢?它们是为了让用户能够使用service代理一些并非pod,或者得不到label的资源,比如:

  • 访问Kubernetes集群外部的一个数据库;
  • 访问其他namespace或者其他集群的service;
  • 任何其他类型的外部遗留系统。

在上面提到的那些场景中,可以定义一个没有selector属性的service对象,如下所示。

headerless_server_service.png
headerless_server_service.png

注意,因为该service没有selector属性,所以系统不会自动创建Endpoints对象。此时,可以通过自定义一个Endpoints对象,显式地将上述service对象映射到一个或多个后端(例如被代理的遗留系统地址),如下所示。

自定义Endpoint对象.png
自定义Endpoint对象.png

这时访问该service,流量将会被分发给用户自定义的endpoints,在上面的例子中即1.2.3.4:9376。

service的自发现机制

Kubernetes中有一个很重要的服务自发现特性。一旦一个service被创建,该service的service IP和service port等信息都可以被注入到pod中供它们使用。Kubernetes主要支持两种service发现机制:环境变量和DNS,这与etcd集群启动时的自发现方法类似,现逐一分析如下。

  • 环境变量方式

kubelet创建pod时会自动添加所有可用的service环境变量到该pod中,如有需要,这些环境变量就被注人pod内的容器里。这些环境变量是诸如{SVCNAME}SERVICE_HOS下和{SVCNAME} SERVICEPORT这样的变量,其中{SVCNAME}部分将service名字全部替换成大写且将破折号(-)替换成下划线()。

  • DNS方式

Kubernetes集群现在支持增加一个可选的组件—DNS服务器。这个DNS服务器使用Kubernetes的watchAPI,不间断地监测新service的创建并为每个service新建一个DNS记录。如果DNS在整个集群范围内都可用,那么所有pod都能够自动解析service的域名。

service外部可路由性设计

为了实现service从外部可以路由,有以下3种常见的解决方案。

  • NodePort

service通常分为3种类型,分别为ClusterIP, NodePort和LoadBalancer。其中,ClusterIP是最基本的类型,即在默认情况下只能在集群内部进行访问;另外两种则与实现从集群外部路由有着密不可分的联系。

如果将service的类型设为NodePort,那么系统会从service-node-port-range范围中为其分配一个端口,并且在每个工作节点上都打开该端口,使得访问该端口(.spec.ports.nodesPort)即可访问到这个service。当然,用户也可以选择自定义该端口,则需要自行解决潜在的端口冲突的问题。

特别地,此时在集群外部使用\<NodeIP>:spec.ports*.nodePort或在集群内部spec.clusterIp:spec.ports*.port均可访问到该service。

  • loadBalancer

类型为LoadBalancer的service较为特别,实际上它并不由Kubernetes集群维护,而需要云服务提供商的支持。如何将从外部loadbalancer接入的流量导到后端pod中的实现逻辑,也完全取决于具体的云服务提供商。

用户可以在manifest中自行定义spec.loadBalancerIP,如果运行的云服务商平台支持这一功能,则在创建loadbalancer时会依据这一字段分配IP,否则,该字段会被忽略。

  • external IP

在这个场景中,用户需要维护一个外部IP地址池(externalIPs),并且在service的描述文件中添加externalIPs字段。注意,Kubernetes并不负责维护externalIPs的路由,而需要由集群admin或者IaaS平台等负责维护。externalIPs可以与任何service类型一起使用。

Deployment设计解读

Deployment多用于为pod和replica set提供更新,并且可以方便地跟踪观察其所属的replica set或者pod数量以及状态的变化。换言之,Deployment是为了应用的更新而设计的。

熟悉kubectl的读者可能会产生这样的疑惑:kubectl rolling-update不是也有类似的功能吗?为什么我们需要一个独立的resource来解决这一问题呢?原因在于,kubectl rolling-update是由命令行工具(类似于前端)本身实现更新逻辑,而Deployment则将这一负担移到了服务器端(类似于后端),由专门的controller来负责这部分工作。不仅在使用场景下得到了扩展,并且其健壮性也有了更好的保障。

Deployment使用样例

Deployment典型的应用场景包括:

  • 定义Deployment来创建Pod和ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续Deployment

比如一个简单的nginx应用可以定义为

代码语言:txt
复制
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80
DaemonSet设计解读

在生产环境中,我们可能会希望在每个工作节点上都运行某个相同的pod副本,如下所示。

  • 在每个工作节点上运行一个存储daemon,如glusterd, ceph等。
  • 在每个工作节点上运行一个日志收集daemon,如fluentd, logstash等。
  • 在每个工作节点上运行一个监控daemon,如collectd, New Relic agant, Ganglia gmond等。

此时,我们当然可以在每个工作节点注册到集群时手动将pod绑定到节点上,或者采用其他daemon进程(如init, upstart或systemd等)直接进行管理,但是我们希望部署流程能够尽量地简单化。DaemonSet就提供这样的服务,每当一个新的工作节点加入到集群中,系统就会按照DaemonSet的配置在节点上运行相应的pod,负责这部分工作的是DaemonSet controller。

ConfigMap设计解读

很多生产环境中的应用程序配置较为复杂,可能需要多个config文件、命令行参数和环境量的组合。并且,这些配置信息应该从应用程序镜像中解耦出来,以保证镜像的可移植性以及配置信息不被泄露。社区引入了ConfigMap这个API资源来满足这一需求。

ConfigMap包含了一系列的键值对,用于存储被pod或者系统组件(如controller等)访问的信息。这与Secret的设计理念有异曲同工之妙,它们的主要区别在于ConfigMap通常不用于存储敏感信息,而只存储简单的文本信息。

创建ConfigMap

此时,可以通过如下命令创建包含该目录下所有文件内容的ConfigMap, from-file的参数是包含文件的目录。

代码语言:txt
复制
$ kubectl create configmap game_config --from-file=configmap
# game_config是要创建的ConfigMap的名字
# --from-file参数是指定数据模板文件,也可以是文件内的键值对

使用ConfigMap中的信息

在创建完ConfigMap后,如何使用存储在其中的信息呢?在这里介绍3种主要的方式。

  • 通过环境变量调用
  • 设置命令行参数
  • volume plugin
Job设计解读

在Kubernetes诞生之初,其预设的服务场景基本上围绕long-running service,例如web service等。因此replication controller一开始获得了大多数用户的关注,它用来管理那些restart policy是Always的应用。

现在,Kubernetes已经有了专门支持batch job的资源—Job,我们可以简单地将其理解为run to completion的任务,它对应restart policy为OnFailure或者Never的应用。

Job的类型

根据Job中pod的数量和并发关系,我们主要将其分为3种类型。另外,在Job的定义中,有两个比较重要的参数会根据Job的不同类型有不同的配置要求,分别为.spec.completions和.spec.parallelism,也会在下文的介绍中一并展开。

Job的第一个适用场景非常容易想到,用户可以使用Job创建单个pod,一旦pod完成工作退出,则认为这个Job也就成功结束了。这样的Job被称为non-parellel job。对于这个类型的Job, .spec.completions和.spec.parallelism都可以不做设定,系统会默认将其设为1.

对于parallel job,则具体细分成两种类型,分别是有固定completion数值的parallel Job,以及有work queue的Parallel Job。

在前者中,Job成功的标志是有completion数量的pod运行成功并退出,用户需要指定.spec.completions字段。对于这种情况,用户可以指定.spec.parallelism,同样也可以不做设定,使用默认值1。

后者则是同时运行多个pod,其中任意一个pod成功停止,则说明该Job成功完成。所谓的work queue的含义在于,首先成功完成的pod对于Job的运行结果起着决定作用,而一旦有一个pod成功完成,系统不会再为这个Job试图创建新的pod。用户需要指定.spec.parallelism字段,表示在任一时刻同时运行的pod数目。如果该值被设为0,该Job不会被启动,直到该值被设为一个正值。注意,在Job的实际运行过程中,并发的pod数量可能会少于.spec.parallelism字段指定的数值。

Horizontal Pod Autoscaler设计解读

自动扩展主要分为两种,其一为水平扩展,即本节中即将详细介绍的内容,针对于实例数目的增减;其二为垂直扩展,即单个实例可以使用的资源的增减。Horizontal Pod Autoscaling ( HPA )属于前者。

Horizontal Pod Autoscaling如何工作

Horizontal Pod Autoscaling的操作对象是ReplicationController, ReplicaSet或Deployment对应的pod,根据观察到的CPU实际使用量与用户的期望值进行比对,做出是否需要增减实例数量的决策。

controller目前使用heapster来检测CPU使用量,检测周期可以通过horizontal-pod-autoscaler-sync-period参数进行调节,默认情况下是30秒。

Horizontal Pod Autoscaling的决策策略

在HPA controller测到CPU的实际使用量之后,会求出当前的CPU使用率(实际使用量与pod请求量的比率)。然后,HPA controller会对比该CPU使用率和.spec.cpuUtilization.targetPercentage,并且通过调整副本数量使得CPU使用率尽量向期望值靠近,副本数的允许范围是用户设定的minReplicas,maxR即licas之间的数值,期望副本数目下argetNumOfPods通过以下计算公式求得:

代码语言:txt
复制
TargetNumofPods=ceil(sum(CurrentPodsCPUUtilization)/Target)

其中,Target是用户在HPA的manifest中设定的.spec.cpuUtilization.targetPercentage,表示用户期望的CPU使用率,CurrentPodsCPUUtilization表示当前的CPU使用率,是HPA调节对象(可能是replication controller, deployment或者replicaSet)对应的所有的pod的平均CPU使用率。

另外,考虑到自动扩展的决策可能需要一段时间才会生效,甚至在短时间内会引入一些噪声,例如当pod所需要的CPU负荷过大,从而运行一个新的pod进行分流,在创建的过程中,系统的CPU使用量可能会有一个攀升的过程。所以,在每一次作出决策后的一段时间内,将不再进行扩展决策。对于scale up而言,这个时间段为3分钟, scale down为5分钟。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • pod设计解读
  • replication controller设计解读(新版本已用seplicaSet替代)
  • service设计解读
  • Deployment设计解读
  • DaemonSet设计解读
  • ConfigMap设计解读
  • Job设计解读
  • Horizontal Pod Autoscaler设计解读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档