Kubernetes进阶实验

Kubernetes进阶实验 #

实验目的 #

  • 了解Kubernetes的各种特性
  • 熟悉Kubernetes的进阶使用

Pod Controller #

在介绍容器的时候我们提到过,container是脆弱的。在实际的生产环境中,container中运行的进程很可能因为各种各样的原因挂掉(比如JVM进程OOM),这时候,快速恢复业务的方法是重新启动一个新的容器实例。

另一方面,为了实现负载均衡或并行计算,我们需要维护相同的多个容器实例,来共同完成任务。

上述两方面的讨论,归结起来可以表示为:在集群中维护一定数量的容器实例

纯粹由人工来维护一定数量的容器实例当然是可以的,但那将是十分低效和不可靠的。Kubernetes给出了一种新的解决方法——Pod Controller,来解决这一问题。

在学习Pod Controller之前,我们先来了解一下Kubernetes中的Controller机制。

Controller #

在这里引用Kubernetes文档中给出的关于控制器的讨论:

在机器人技术和自动化领域,控制回路(Control Loop)是一个非终止回路,用于调节系统状态。 这是一个控制环的例子:房间里的温度自动调节器。 当你设置了温度,告诉了温度自动调节器你的期望状态(Desired State)。 房间的实际温度是当前状态(Current State)。 通过对设备的开关控制,温度自动调节器让其当前状态接近期望状态。

和上述提到的“温度自动调节器”类似,Kubernetes中的控制器(Controller)将会监控当前集群中的状态,并努力使集群的当前状态满足用户设置的目标状态

Pod Controller #

Pod Controller,顾名思义,就是用于调节集群中当前Pod状态的Controller。下面,我们依次来介绍几种Kubernetes最常用的Pod Controller。

Deployment #

Deployment的主要作用是努力使当前集群中的Pod数量与用户期望的状态相同。

我们先来看一个简单的Deployment的YAML定义:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      kkk: hahaha
  template:
    metadata:
      labels:
        kkk: hahaha
        kubernetes: yyds
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

apiVersionkindmetadata都是Deployment的元信息配置,与Pod中的写法非常类似,不再赘述。

spec中包含以下字段:

  • replicas:表示“副本数”。即该Deployment将会管理多少个Pod。
  • selector:选择器。表示该Deployment如何在集群中查找到它需要管理的Pod。matchLabels表示被打上kkk: hahaha这个标签的Pod将会被管理。
  • template中的内容想必大家都很眼熟,这其实就是我们上面提到的一个典型的Pod的描述。

(可能上面的描述有点抽象,下面我们将举例说明)

将上述代码保存到本地文件中,例如deployment.yaml,然后执行kubectl apply -f deployment.yaml

可以看到,一个名为nginx-deployment的Deployment被创建了。可以使用kubectl get deployment查看:

如果你得到的AVAILABLE的数量不是3,请耐心等一会儿。

同时,我们可以查看一下当前集群里的Pod:

可以看到,集群新启动了3个Pod,其名称前缀都是nginx-deployment,说明它们就是被我们刚刚创建的Deployment创建的。下面具体讨论一下这个过程是怎样发生的:

  1. kubectl apply -f nginx-deployment.yaml在Kubernetes集群中创建了一个名为nginx-deployment的Deployment。
  2. 集群中的Deployment控制器发现了新创建的名为nginx-deployment的Deployment,然后尝试解析其中的内容。
  3. Deployment控制器对Deployment的spec.template部分做hash,得到pod-template-hash的值,例如该值为584784cc75
  4. Deployment控制器发现这个Deployment中描述的Pod的副本数是3,并且发现选择器(selector)选取那些带有标签kkk:hahahha的Pod。于是,Deployment控制器尝试检索集群中同时带有标签kkk:hahahhapod-template-hash:584784cc75的Pod数量,检查其是否是3个。
  5. Deployment控制器发现上述Pod数量0,于是,根据spec.template中的定义创建3个Pod。

我们可以尝试删除其中上述Deployment中的某一个Pod,来模拟该Pod意外崩溃的情况:

可以在到,在其中一个Pod被删除后(进入Terminating状态后),一个新的Pod立即被创建,补上了缺位。也就是说,Deployment将永远保证集群中,被打上kkk:hahahhapod-template-hash:584784cc75标签的Pod的数量是3。

更新Deployment #

更新Deployment(比如更改Deployment中的副本数)非常简单,只需要编辑YAML文件,重新apply一下即可,例如修改deployment.yaml中的副本数为5:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
spec:
  replicas: 5
  selector:
    matchLabels:
      kkk: hahaha
  template:
    metadata:
      labels:
        kkk: hahaha
        kubernetes: yyds
    spec:
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

kubectl apply -f deployment.yaml之后可以发现Pod数量增加到了5:

当然你也可以减小spec.replicas的值,比如从3减小到1,这时Deployment控制器会删除多余的Pod。

总结一下,Deployment控制器的工作原理与本节开头提到的温度控制器工作原理非常相似:

其他Pod Controller #

Job #

Job会创建一个或多个Pod,并确保指定数量的Pod成功终止。当Pod成功完成时,Job将追踪成功完成的情况。当达到指定的成功完成次数时,Job就完成了。删除一个Job将清除它所创建的Pod。Job一般用于定义并启动一个批处理任务。批处理任务通常并行(或串行)启动多个计算进程去处理一批工作项,处理完成后,整个批处理任务结束。

Kubernetes支持一下几种Job:

  • 非并行Job: 通常创建一个Pod直至其成功结束
  • 固定结束次数的Job: 设置.spec.completions,创建多个Pod,直到.spec.completions个Pod成功结束
  • 带有工作队列的并行Job: 设置.spec.Parallelism但不设置.spec.completions,当所有Pod结束并且至少一个成功时,Job就认为是成功。

DaemonSet #

DaemonSet用于管理在集群中每个Node上运行且仅运行一份Pod的副本实例,一般来说,在以下情形中会使用到DaemonSet:

  • 在每个Node上都运行一个存储进程
  • 在每个Node上都运行一个日志采集程序
  • 在每个Node上都运行一个性能监控程序

StatefulSet #

StatefulSet用来搭建有状态的应用集群(比如MySQL、MongoDB等)。Kubernetes会保证StatefulSet中各应用实例在创建和运行的过程中,都具有固定的身份标识和独立的后端存储;还支持在运行时对集群规模进行扩容、保障集群的高可用等功能。

Service #

Service可以将运行在一组Pods上的应用程序公开为网络服务,简单地实现服务发现、负载均衡等功能。

k8s的Pods具有自己的生命周期,同一时刻运行的Pod集合与稍后运行的Pod集合很有可能不同(如发生更新、node故障等),Pods的IP地址可能会随时发生变化。这就会导致一个问题:如果一组后端Pods为集群内其他前端Pods提供功能,那么前端Pods该如何找出并跟踪需要连接的IP地址?通过Service,能够解耦这种关联,方便的通过Service地址访问到相应的Pods,前端不应该也没必要知道怎么访问、访问到的具体是哪一个Pod。

Service一共有4种类型:

  • ClusterIP:通过集群的内部 IP 暴露服务,选择该值,服务只能够在集群内部可以访问,这也是默认的 ServiceType
  • NodePort: 通过每个 Node 上的 IP 和静态端口(NodePort)暴露服务。NodePort 服务会路由到 ClusterIP 服务,这个 ClusterIP 服务会自动创建。通过请求 <NodeIP>:<NodePort>,可以从集群的外部访问一个 NodePort 服务。
  • LoadBalancer:使用云提供商的负载局衡器,可以向外部暴露服务。外部的负载均衡器可以路由到 NodePort 服务和 ClusterIP 服务。仅作了解。
  • ExternalName:通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容(例如,在集群内查找my-service.my-namespace.svc时,k8s DNS service只返回foo.bar.example.com这样的CNAME record)。没有任何类型代理被创建,网络流量发生在DNS层面。由于ExternalName要求kube-dns而我们使用的是coredns,也只作了解。

创建Service #

Service通常通过selector(比如通过选取标签)来选择被访问的Pod。

继续沿用我们之前所创建的nginx-deployment。可以通过下列YAML文件创建Service (将下面的内容写入service.yaml)

#service.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
    - port: 80
      targetPort: 80
      protocol: TCP
      name: anyway
  selector:
    kkk: hahaha

注意到,我们在selector中使用了标签kkk: hahaha

解释一下spec.ports中的各个字段:

  • port:Service暴露在集群IP上的端口。集群内通过<clusterIP>:<port>可以访问Service。
  • targetPort:被代理的Pod上的端口。默认与port相同。
  • protocol:Service暴露出来的这个端口所支持的通信协议,通常是TCPUDP
  • name:端口名称,当Service具有多个端口时必须为每个端口提供唯一且无歧义的端口名称,具体内容写啥都行。

创建Service:

kubectl apply -f service.yaml

查看service kubectl get svckubectl get service

可以看到,第二个就是我们刚才创建的service,其中,它有一个cluster-ip:10.43.95.143

验证是否可以通过Service访问Pod,注意,上述这个IP是“cluster-ip”,也就是说,它是一个集群内ip,因此,只能在集群中的机器上访问:

curl 10.43.95.143:80

查看当前5个Pod的IP地址 kubectl get pod -l kkk=hahaha -o wide

删除这5个Pod并等待Deployment重新创建kubectl delete pod -l kkk=hahaha

可以看到重新创建的5个Pods的IP地址都已经发生变化:

但通过Service,仍能访问对应的Pod:

curl 10.97.91.103:80

暴露端口 #

之前创建的Service并没有指定类型,因此为默认的ClusterIP,只能在集群内部访问。如果需要将服务端口暴露在公网,可以使用NodePort类型。

service.yaml修改为下面的内容

#service.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
  labels:
    svc: nginx-svc
spec:
  type: NodePort
  ports:
    - port: 80
      nodePort: 32180
      protocol: TCP
      name: anyway
  selector:
    kkk: hahaha

修改Service kubectl apply -f service.yaml

查看service kubectl get svc nginx-service

此时,从集群内任一节点IP的32180端口均可访问到该Service指定的Pod的80端口。

比如,你现在所使用的虚拟机的IP是10.251.254.183,那么,你在校园网内的任何一台机器上,执行curl http://10.251.254.183:32180,都能得到如下的输出:

当然,你也可以使用浏览器访问:

可以尝试删除Pods并等待新的Pods创建完成,仍可以通过上述方式访问。

实验报告模板 #

# Kubernetes进阶实验报告

## 个人信息

姓名:
学号:

## 实验内容

### 使用Deployment管理Pod

#### 创建Deployment

<!-- 创建Deployment,观察Deployment创建的Pod -->
<!-- 请详细写出自己的操作步骤,并截图说明 -->

#### 模拟Pod崩溃

<!-- 手动删除Pod,观察集群中Pod的变化 -->
<!-- 请详细写出自己的操作步骤,并截图说明 -->

#### 弹性伸缩

<!-- 修改Deployment中的副本数,观察集群中Pod的变化 -->
<!-- 请详细写出自己的操作步骤,并截图说明 -->

### 使用Service暴露Pod的服务

#### 创建Service

<!-- 创建Service,并访问Service的IP,尝试分析Service和Pod的IP之间的关系 -->
<!-- 请详细写出自己的操作步骤,并截图说明 -->

#### 模拟Pod变化

<!-- 删除Pod后重新创建,观察Service的行为变化 -->
<!-- 请详细写出自己的操作步骤,并截图说明 -->