今でもあなたは私の光丶

K8S(4)pod控制器进阶

pod控制器进阶

简介

Controller Manager 由 kube-controller-manager 和 cloud-controller-manager 组成, 是 Kubernetes 的大脑, 它通过 apiserver 监控整个集群的状态, 并确保集群处于预期的工作状态。

kube-controller-manager 由一系列的控制器组成

1 Replication Controller
2 Node Controller
3 CronJob Controller
4 DaemonSet Controller
5 Deployment Controller
6 Endpoint Controller
7 Garbage Collector
8 Namespace Controller
9 Job Controller
10 Pod AutoScaler
11 RelicaSet
12 Service Controller
13 ServiceAccount Controller
14 StatefulSet Controller
15 Volume Controller
16 Resource quota Controller

cloud-controller-manager 在 Kubernetes 启用 Cloud Provider 的时候才需要, 用来配合云服务 提供商的控制, 也包括一系列的控制器

1 Node Controller
2 Route Controller
3 Service Controller

从v1.6开始,cloud provider已经经历了几次重大重构,以便在不修改Kubernetes核心代码的同时构建 自定义的云服务商支持

常见Pod控制器及含义

  1. ReplicaSet:适合无状态的服务部署
    用户创建指定数量的pod副本数量,确保pod副本数量符合预期状态,并且支持滚动式自动扩容和 缩容功能。
    帮助用户管理无状态的pod资源,精确反应用户定义的目标数量,但是RelicaSet不是直接使用的 控制器,而是使用Deployment。
    ReplicaSet主要三个组件组成:
    1. 用户期望的pod副本数量
    2. 标签选择器,判断哪个pod归自己管理
    3. 当现存的pod数量不足,会根据pod资源模板进行新建
  2. deployment:适合无状态的服务部署
    工作在ReplicaSet之上,用于管理无状态应用,目前来说最好的控制器。支持滚动更新和回滚功 能,还提供声明式配置。
  3. StatefullSet:适合有状态的服务部署。需要学完存储卷后进行系统学习。
  4. DaemonSet:一次部署,所有的node节点都会部署,例如一些典型的应用场景:
    • 运行集群存储 daemon,例如在每个Node上运行 glusterd、ceph
    • 在每个Node上运行日志收集 daemon,例如 fluentd、 logstash
    • 在每个Node上运行监控 daemon,例如 Prometheus Node Exporter
      用于确保集群中的每一个节点只运行特定的pod副本,通常用于实现系统级后台任务。比如ELK服 务
      特性:服务是无状态的
      服务必须是守护进程
  5. Job:一次性的执行任务。 只要完成就立即退出,不需要重启或重建。
  6. Cronjob:周期性的执行任务。 周期性任务控制,不需要持续后台运行。

使用镜像

演示pod的控制器升级使用:
docker pull nginx:1.17.10-alpine
docker pull nginx:1.18.0-alpine
docker pull nginx:1.19.2-alpine

replication Controller控制器

replication controller简称RC,是kubernetes系统中的核心概念之一,简单来说,它其实定义了一个期 望的场景,即声明某种pod的副本数量在任意时刻都复合某个预期值,所以RC的定义包含以下部分:

  • pod期待的副本数量
  • 用于筛选目标pod的Label Selector
  • 当pod的副本数量小于期望值时,用于创建新的pod的pod模板(template)

ReplicaSet

ReplicationController用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退 出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收。

在新版本的Kubernetes中建议使用ReplicaSet来取代ReplicationController。ReplicaSet跟 ReplicationController没有本质的不同,只是名字不一样,并且ReplicaSet支持集合式的selector。

虽然ReplicaSet可以独立使用,但一般还是建议使用 Deployment 来自动管理ReplicaSet,这样就无需 担心跟其他机制的不兼容问题(比如ReplicaSet不支持rolling-update但Deployment支持)。

ReplicaSet模板说明

apiVersion: apps/v1 #api版本定义
kind: ReplicaSet #定义资源类型为ReplicaSet
metadata: #元数据定义
 name: myapp
 namespace: default
spec: #ReplicaSet的规格定义
 replicas: 2 #定义副本数量为2个
 selector: #标签选择器,定义匹配pod的标签
  matchLabels:
   app: myapp
   release: canary
 template: #pod的模板定义
  metadata: #pod的元数据定义
   name: myapp-pod #自定义pod的名称
   labels: #定义pod的标签,需要和上面定义的标签一致,也可以多出其他标签
    app: myapp
    release: canary
    environment: qa
  spec: #pod的规格定义
   containers: #容器定义
   - name: myapp-container #容器名称
     image: nginx:1.17.10-alpine #容器镜像
     ports: #暴露端口
     - name: http
       containerPort: 80

可以通过kubectl命令行方式获取更加详细信息

kubectl explain rs
kubectl explain rs.spec
kubectl explain rs.spec.template.spec

部署ReplicaSet

controller/replicasetdemo.yml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
 name: replicasetdemo
 labels:
  app: replicasetdemo
spec:
 replicas: 3
 template:
  metadata:
   name: replicasetdemo
   labels:
    app: replicasetdemo
  spec:
   containers:
    - name: replicasetdemo
      image: nginx:1.17.10-alpine
      imagePullPolicy: IfNotPresent
      ports:
       - containerPort: 80
  restartPolicy: Always
 selector: 
  matchLabels:
   app: replicasetdemo

运行ReplicaSet

运行ReplicaSet
kubectl apply -f replicasetdemo.yml
查看rs控制器
kubectl get rs
查看pod信息
kubectl get pod
查看pod详细信息
kubectl describe pod replicasetdemo-7fdd7b5f67-5gzfg
测试controller控制器下的pod删除、重新被controller控制器拉起
kubectl delete pod --all
kubectl get pod
修改pod的副本数量:通过命令行方式
kubectl scale replicaset replicasetdemo --replicas=8
kubectl get rs
修改pod的副本数量:通过资源清单方式
kubectl edit replicasets.apps replicasetdemo
kubectl get rs
显示pod的标签
kubectl get pod --show-labels
修改pod标签(label)
kubectl label pod replicasetdemo-652lc app=lagou --overwrite=True
再次显示pod的标签:发现多了一个pod,原来的rs中又重新拉起一个pod,说明rs是通过label去管
理pod
kubectl get pod --show-labels
删除rs
kubectl delete rs replicasetdemo

总结

kubectl命令行工具适用于RC的绝大部分命令同样适用于ReplicaSet,此外,我们当前很少单独适用 ReplicaSet,它主要被Deployment这个更高层的资源对象所使用,从而形成一整套Pod创建,删除, 更新的编排机制,我们在使用Deployment时无需关心它是如何维护和创建ReplicaSet的,这一切都是 自动发生的

最后,总结一下RC(ReplicaSet)的一些特性和作用:

  • 在绝大多数情况下,我们通过定义一个RC实现Pod的创建及副本数量的自动控制
  • 在RC里包括完整的Pod定义模板
  • RC通过Label Selector机制实现对Pod副本的自动控制
  • 通过改变RC里的Pod副本数量,可以实现Pod的扩容和缩容
  • 通过改变RC里Pod模板中的镜像版本,可以实现滚动升级

Deployment

Deployment是kubernetes在1.2版本中引入的新概念,用于更好的解决Pod的编排问题,为此, Deployment在内部使用了ReplicaSet来实现目的,我们可以把Deployment理解为ReplicaSet的一次升 级,两者的相似度超过90%

Deployment的使用场景有以下几个:

  • 创建一个Deployment对象来生成对应的ReplicaSet并完成Pod副本的创建
  • 检查Deployment的状态来看部署动作是否完成(Pod副本数量是否达到了预期的值)
  • 更新Deployment以创建新的Pod(比如镜像升级)
  • 如果当前Deployment不稳定,可以回滚到一个早先的Deployment版本
  • 暂停Deployment以便于一次性修改多个PodTemplateSpec的配置项,之后在恢复Deployment, 进行新的发布
  • 扩展Deployment以应对高负载
  • 查看Deployment的状态,以此作为发布是否成功的标志
  • 清理不在需要的旧版本ReplicaSet

Deployment模板说明

可以通过kubectl命令行方式获取更加详细信息

kubectl explain deploy
kubectl explain deploy.spec
kubectl explain deploy.spec.template.spec

部署Deployment

除了API生命与Kind类型有区别,Deployment的定义与Replica Set的定义很类似。
controller/deploymentdemo.yml

apiVersion: apps/v1
kind: Deployment
metadata:
 name: deploymentdemo1
 labels:
  app: deploymentdemo1
spec:
 replicas: 10
 template:
  metadata:
   name: deploymentdemo1
   labels:
    app: deploymentdemo1
  spec:
   containers:
    - name: deploymentdemo1
      image: nginx:1.17.10-alpine
      imagePullPolicy: IfNotPresent
      ports:
       - containerPort: 80
   restartPolicy: Always
 selector:
  matchLabels:
   app: deploymentdemo1

运行Deployment

kubectl apply -f deploymentdemo.yml
查看deployment
kubectl get rs
查看rs:deployment名称+hashcode码组成
查看pod
kubectl get pod

镜像更新升级

命令行方式

升级nginx镜像版本为1.18.0
kubectl set image deployment deploymentdemo1 deploymentdemo1=nginx:1.18.0-
alpine
查看pod升级情况
kubectl get pods -w
进去某一个pod内部,查看nginx升级版本信息
kubectl exec -it deploymentdemo1-df6bc5d4c-flc7b sh
nginx -v
exit

yml文件方式

升级nginx镜像版本为1.19.2-alpine
kubectl edit deployments.apps deploymentdemo1
查看pod升级情况
kubectl get pods -w
进去某一个pod内部,查看nginx升级版本信息
kubectl exec -it deploymentdemo1-584f6b54dd-4l62t sh
nginx -v
exit

Deployment扩容

命令行方式

kubectl scale deployment deploymentdemo1 --replicas=15
kubectl get pods

yml文件方式

kubectl edit deployments.apps deploymentdemo1
kubectl get pods

滚动更新

概述

微服务部署:蓝绿部署、滚动部署、灰度发布、金丝雀发布。

  1. 蓝绿部署是不停老版本,部署新版本然后进行测试,确认OK,将流量切到新版本,然后老版本同 时也升级到新版本。 蓝绿部署无需停机,并且风险较小。
  2. 滚动发布:一般是取出一个或者多个服务器停止服务,执行更新,并重新将其投入使用。周而复 始,直到集群中所有的实例都更新成新版本。 这种部署方式相对于蓝绿部署,更加节约资源—— 它不需要运行两个集群、两倍的实例数。我们可以部分部署,例如每次只取出集群的20%进行升 级。
  3. 灰度发布是指在黑与白之间,能够平滑过渡的一种发布方式。AB test就是一种灰度发布方式,让 一部分用户继续用A,一部分用户开始用B,如果用户对B没有什么反对意见,那么逐步扩大范围, 把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发 现、调整问题,以保证其影响度,而我们平常所说的金丝雀部署也就是灰度发布的一种方式。

金丝雀发布

Deployment控制器支持自定义控制更新过程中的滚动节奏,如“暂停(pause)”或“继续(resume)”更新操 作。比如等待第一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用, 主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳 定地按期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操 作。这就是所谓的金丝雀发布(Canary Release)

更新deployment的nginx:1.18.0-alpine版本,并配置暂停deployment
kubectl set image deployment deploymentdemo1 deploymentdemo1=nginx:1.18.0-
alpine && kubectl rollout pause deployment deploymentdemo1
观察更新状态
kubectl rollout status deployments deploymentdemo1
监控更新的过程,可以看到已经新增了一个资源,但是并未按照预期的状态去删除一个旧的资源,就是
因为使用了pause暂停命令
kubectl get pods -l app=deploymentdemo1 -w
确保更新的pod没问题了,继续更新
kubectl rollout resume deploy deploymentdemo1
查看最后的更新情况
kubectl get pods -l app=deploymentdemo1 -w
进去某一个pod内部,查看nginx更新版本信息
kubectl exec -it deploymentdemo1-df6bc5d4c-flc7b sh
nginx -v

Deployment版本回退

默认情况下,kubernetes 会在系统中保存前两次的 Deployment 的 rollout 历史记录,以便可以随 时回退(您可以修改 revision history limit 来更改保存的revision数)。

注意: 只要 Deployment 的 rollout 被触发就会创建一个 revision。也就是说当且仅当 Deployment 的 Pod template(如 .spec.template )被更改,例如更新template 中的 label 和容器 镜像时,就会创建出一个新的 revision。

其他的更新,比如扩容 Deployment 不会创建 revision——因此我们可以很方便的手动或者自动扩容。 这意味着当您回退到历史 revision 时,只有 Deployment 中的 Pod template 部分才会回退。

rollout常见命令

history操作

kubectl rollout history deployment deploymentdemo1

status操作

kubectl rollout status deployment deploymentdemo1

undo操作

回滚版本信息
kubectl rollout undo deployment deploymentdemo1
查看pod回滚情况
kubectl get pods -w
进去某一个pod内部,查看nginx回滚版本信息
kubectl exec -it deploymentdemo1-df6bc5d4c-flc7b sh

Deployment 更新策略

Deployment 可以保证在升级时只有一定数量的 Pod 是 down 的。默认的,它会确保至少有比期望的 Pod数量少
一个是up状态(最多一个不可用)

Deployment 同时也可以确保只创建出超过期望数量的一定数量的 Pod。默认的,它会确保最多比期望 的Pod数
量多一个的 Pod 是 up 的(最多1个 surge )
Kuberentes 版本v1.17.5中,从1-1变成25%-25%

kubectl describe deployments.apps deploymentdemo1
查看到属性:
RollingUpdateStrategy: 25% max unavailable, 25% max surge

kubectl describe deployments.apps deploymentdemo1 查看到属性: RollingUpdateStrategy: 25% max unavailable, 25% max surge

总结

Deployment为Pod和Replica Set(下一代Replication Controller)提供声明式更新。

只需要在 Deployment 中描述想要的目标状态是什么,Deployment controller 就会帮您将 Pod 和 ReplicaSet 的实际状态改变到您的目标状态。也可以定义一个全新的 Deployment 来创建 ReplicaSet 或者删除已有的 Deployment 并创建一个新的来替换。

Replicas(副本数量):

.spec.replicas 是可以选字段,指定期望的pod数量,默认是1。

Selector(选择器):

.spec.selector是可选字段,用来指定 label selector ,圈定Deployment管理的pod范围。如果被指 定, .spec.selector 必须匹配 .spec.template.metadata.labels,否则它将被API拒绝。如果 .spec.selector 没有被指定, .spec.selector.matchLabels 默认是.spec.template.metadata.labels。

在Pod的template跟.spec.template不同或者数量超过了.spec.replicas规定的数量的情况下, Deployment会杀掉label跟selector不同的Pod。

Pod Template(Pod模板):

.spec.template 是 .spec中唯一要求的字段。
.spec.template 是 pod template. 它跟 Pod有一模一样的schema,除了它是嵌套的并且不需要 apiVersion 和 kind字段。
另外为了划分Pod的范围,Deployment中的pod template必须指定适当的label(不要跟其他 controller重复了,参考selector)和适当的重启策略。
.spec.template.spec.restartPolicy 可以设置为 Always , 如果不指定的话这就是默认配置。

strategy(更新策略):

.spec.strategy 指定新的Pod替换旧的Pod的策略。 .spec.strategy.type 可以是"Recreate"或者是 "RollingUpdate"。"RollingUpdate"是默认值。

Recreate: 重建式更新,就是删一个建一个。类似于ReplicaSet的更新方式,即首先删除现有的 Pod对象,然后由控制器基于新模板重新创建新版本资源对象。

rollingUpdate:滚动更新,简单定义 更新期间pod最多有几个等。可以指定 maxUnavailable 和 maxSurge 来控制 rolling update 进程。

maxSurge: .spec.strategy.rollingUpdate.maxSurge 是可选配置项,用来指定可以超过期 望的Pod数量的最大个数。该值可以是一个绝对值(例如5)或者是期望的Pod数量的百分比(例如 10%)。当 MaxUnavailable 为0时该值不可以为0。通过百分比计算的绝对值向上取整。默认值是1。

例如,该值设置成30%,启动rolling update后新的ReplicatSet将会立即扩容,新老Pod的总数不 能超过期望的Pod数量的130%。旧的Pod被杀掉后,新的ReplicaSet将继续扩容,旧的ReplicaSet会进 一步缩容,确保在升级的所有时刻所有的Pod数量和不会超过期望Pod数量的130%。

maxUnavailable: .spec.strategy.rollingUpdate.maxUnavailable 是可选配置项,用来指 定在升级过程中不可用Pod的最大数量。该值可以是一个绝对值(例如5),也可以是期望Pod数量的百 分比(例如10%)。通过计算百分比的绝对值向下取整。 如 果 .spec.strategy.rollingUpdate.maxSurge 为0时,这个值不可以为0。默认值是1。

例如,该值设置成30%,启动rolling update后旧的ReplicatSet将会立即缩容到期望的Pod数量的 70%。新的Pod ready后,随着新的ReplicaSet的扩容,旧的ReplicaSet会进一步缩容确保在升级的所有 时刻可以用的Pod数量至少是期望Pod数量的70%。

rollbackTo:

.spec.rollbackTo 是一个可以选配置项,用来配置Deployment回退的配置。设置该参数将触发 回退操作,每次回退完成后,该值就会被清除。
revision: .spec.rollbackTo.revision 是一个可选配置项,用来指定回退到的revision。默认 是0,意味着回退到上一个revision。

progressDeadlineSeconds:

.spec.progressDeadlineSeconds 是可选配置项,用来指定在系统报告Deployment的failed progressing——表现为resource的状态中 type=Progressing 、 Status=False 、 Reason=ProgressDeadlineExceeded 前可以等待的Deployment进行的秒数。Deployment controller会继续重试该Deployment。未来,在实现了自动回滚后, deployment controller在观察到 这种状态时就会自动回滚。

如果设置该参数,该值必须大于 .spec.minReadySeconds 。

paused:

.spec.paused 是可以可选配置项,boolean值。用来指定暂停和恢复Deployment。Paused和没有 paused的Deployment之间的唯一区别就是,所有对paused deployment中的PodTemplateSpec的修 改都不会触发新的rollout。Deployment被创建之后默认是非paused。

DaemonSet

DaemonSet 确保全部Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一 个 Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod。

在每一个node节点上只调度一个Pod,因此无需指定replicas的个数,比如:

  • 在每个node上都运行一个日志采集程序,负责收集node节点本身和node节点之上的各个Pod所产 生的日志
  • 在每个node上都运行一个性能监控程序,采集该node的运行性能数据

DaemonSet模板说明

可以通过kubectl命令行方式获取更加详细信息

kubectl explain daemonset
kubectl explain daemonset.spec
kubectl explain daemonset.spec.template.spec

部署DaemonSet

controller/daemonsetdemo.yml

apiVersion: apps/v1
kind: DaemonSet
metadata:
 name: demonsetdemo
 labels:
  app: demonsetdemo
spec:
 template:
  metadata:
   name: demonsetdemo
   labels:
    app: demonsetdemo
  spec:
   containers:
    - name: demonsetdemo
      image: nginx:1.17.10-alpine
      imagePullPolicy: IfNotPresent
   restartPolicy: Always
 selector:
  matchLabels:
   app: demonsetdemo

运行DaemonSet

运行demonset
kubectl apply -f demonsetdemo.yml
查看pod详细信息:只有工作节点创建pod,master节点并不会创建。
kubectl get pod -o wide

DaemonSet的滚动更新

DaemonSet有两种更新策略类型:

  • OnDelete:这是向后兼容性的默认更新策略。使用 OnDelete 更新策略,在更新DaemonSet模板 后,只有在手动删除旧的DaemonSet pod时才会创建新的DaemonSet pod。这与Kubernetes 1.5或更早版本中DaemonSet的行为相同。
  • RollingUpdate:使用 RollingUpdate 更新策略,在更新DaemonSet模板后,旧的DaemonSet pod将被终止,并且将以受控方式自动创建新的DaemonSet pod。

Job

  • 一次性执行任务,类似Linux中的job
  • 应用场景:如离线数据处理,视频解码等业务

使用镜像

docker pull perl:slim

部署Job

apiVersion: batch/v1
kind: Job
metadata:
 name: pi
spec:
 template:
  spec:
   containers:
    - name: pi
      image: perl:slim
      command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(6000)"]
   restartPolicy: Never
 backoffLimit: 4

backoffLimit说明

.spec.backoffLimit用于设置Job的容错次数,默认值为6。当Job运行的Pod失败次数到 达.spec.backoffLimit次时,Job Controller不再新建Pod,直接停止运行这个Job,将其运行结 果标记为Failure。另外,Pod运行失败后再次运行的时间间隔呈递增状态,例如10s,20s, 40s。。。

运行Job

运行job
kubectl apply -f jobdemo.yml
查看pod日志
kubectl logs -f pi-7nrtv
删除job
kubectl delete -f jobdemo.yml

StatefulSet

在kubernetes系统中,Pod的管理对象RC,Deployment,DaemonSet和Job都面向无状态的服务,但 现实中有很多服务时有状态的,比如一些集群服务,例如mysql集群,集群一般都会有这四个特点:

  1. 每个节点都是有固定的身份ID,集群中的成员可以相互发现并通信
  2. 集群的规模是比较固定的,集群规模不能随意变动
  3. 集群中的每个节点都是有状态的,通常会持久化数据到永久存储中
  4. 如果磁盘损坏,则集群里的某个节点无法正常运行,集群功能受损

如果你通过RC或Deployment控制Pod副本数量来实现上述有状态的集群,就会发现第一点是无法满足 的,因为Pod名称和ip是随机产生的,并且各Pod中的共享存储中的数据不能都动,因此StatefulSet在 这种情况下就派上用场了,那么StatefulSet具有以下特性:

  • StatefulSet里的每个Pod都有稳定,唯一的网络标识,可以用来发现集群内的其它成员,假设, StatefulSet的名称为lagou,那么第1个Pod叫lagou-0,第2个叫lagou-1,以此类推
  • StatefulSet控制的Pod副本的启停顺序是受控的,操作第N个Pod时,前N-1个Pod已经是运行且准 备状态
  • StatefulSet里的Pod采用稳定的持久化存储卷,通过PV或PVC来实现,删除Pod时默认不会删除与 StatefulSet相关的存储卷(为了保证数据的安全

StatefulSet除了要与PV卷捆绑使用以存储Pod的状态数据,还要与Headless,Service配合使用,每个 StatefulSet定义中都要生命它属于哪个Handless Service,Handless Service与普通Service的关键区别 在于,它没有Cluster IP

service

简介

通过以前的学习,我们已经能够通过Deployment来创建一组Pod来提供具有高可用性的服务。虽然每 个Pod都会分配一个单独的Pod IP,然而却存在如下两问题:

  • Pod IP仅仅是集群内可见的虚拟IP,外部无法访问。
  • Pod IP会随着Pod的销毁而消失,当Deployment对Pod进行动态伸缩时,Pod IP可能随时随地都 会变化,这样对于我们访问这个服务带来了难度。
  • Service能够提供负载均衡的能力,但是在使用上有以下限制。只提供 4 层负载均衡能力,而没有 7 层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的

因此,Kubernetes中的Service对象就是解决以上问题的实现服务发现核心关键。

Service 在 K8s 中有以下四种类型

  • ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP
  • NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 :
    NodePort 来访 问该服务
  • LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请 求转发 到NodePort。是付费服务,而且价格不菲。
  • ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理 被创建,
    这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持

详解4 种 Service 类型

Services 和 Pods

Kubernetes的Pods是有生命周期的。他们可以被创建,而且销毁不会再启动。如果您使用 Deployment来运行您的应用程序,则它可以动态创建和销毁 Pod。

一个Kubernetes的Service是一种抽象,它定义了一组Pods的逻辑集合和一个用于访问它们的策略 - 有 的时候被称之为微服务。一个Service的目标Pod集合通常是由Label Selector 来决定的

举个例子,想象一个处理图片的后端运行了三个副本。这些副本都是可以替代的 - 前端不关心它们使用 的是哪一个后端。尽管实际组成后端集合的Pod可能会变化,前端的客户端却不需要知道这个变化,也 不需要自己有一个列表来记录这些后端服务。Service抽象能让你达到这种解耦。

不像 Pod 的 IP 地址,它实际路由到一个固定的目的地,Service 的 IP 实际上不能通过单个主机来进行 应答。相反,我们使用 iptables(Linux 中的数据包处理逻辑)来定义一个虚拟IP地址(VIP),它可以 根据需要透明地进行重定向。当客户端连接到 VIP 时,它们的流量会自动地传输到一个合适的 Endpoint。环境变量和 DNS,实际上会根据 Service 的 VIP 和端口来进行填充。

kube-proxy支持三种代理模式: 用户空间,iptables和IPVS;它们各自的操作略有不同。

  • Userspace代理模式
    Client Pod要访问Server Pod时,它先将请求发给本机内核空间中的service规则,由它再将请求,转给监 听在指定套接字上的kube-proxy,kube-proxy处理完请求,并分发请求到指定Server Pod后,再将请求 递交给内核空间中的service,由service将请求转给指定的Server Pod。由于其需要来回在用户空间和内 核空间交互通信,因此效率很差 。
    当一个客户端连接到一个 VIP,iptables 规则开始起作用,它会重定向该数据包到 Service代理 的端 口。Service代理 选择一个 backend,并将客户端的流量代理到 backend 上。
    这意味着 Service 的所有者能够选择任何他们想使用的端口,而不存在冲突的风险。客户端可以简单地 连接到一个 IP 和端口,而不需要知道实际访问了哪些 Pod。
  • iptables代理模式
    当一个客户端连接到一个 VIP,iptables 规则开始起作用。一个 backend 会被选择(或者根据会话亲和 性,或者随机),数据包被重定向到这个 backend。不像 userspace 代理,数据包从来不拷贝到用户 空间,kube-proxy 不是必须为该 VIP 工作而运行,并且客户端 IP 是不可更改的。当流量打到 Node 的 端口上,或通过负载均衡器,会执行相同的基本流程,但是在那些案例中客户端 IP 是可以更改的。
  • IPVS代理模式
    在大规模集群(例如10,000个服务)中,iptables 操作会显着降低速度。IPVS 专为负载平衡而设计, 并基于内核内哈希表。因此,您可以通过基于 IPVS 的 kube-proxy 在大量服务中实现性能一致性。同 时,基于 IPVS 的 kube-proxy 具有更复杂的负载平衡算法(最小连接,局部性,加权,持久性)。
    在 Kubernetes 集群中,每个 Node 运行一个 kube-proxy 进程。 kube-proxy 负责为 Service 实现了一 种 VIP(虚拟 IP)的形式,而不是 ExternalName 的形式。 在 Kubernetes v1.0 版本,代理完全在 userspace。在 Kubernetes v1.1 版本,新增了 iptables 代理,但并不是默认的运行模式。 从 Kubernetes v1.2 起, 默认就是 iptables 代理。 在 Kubernetes v1.8.0-beta.0 中,添加了 ipvs 代理在 Kubernetes 1.14 版本开始默认 使用 ipvs 代理。在 Kubernetes v1.0 版本, Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了Ingress API(beta 版),用来表示 “7层”(HTTP)服务 。
    这种模式,kube-proxy 会监视 Kubernetes Service 对象和 Endpoints ,调用 netlink 接口以相应地创 建 ipvs 规则并定期与 Kubernetes Service 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期 望一 致。访问服务时,流量将被重定向到其中一个后端 Pod与 iptables 类似,ipvs 于 netfilter 的 hook 功 能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并 且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项。

ClusterIP

类型为ClusterIP的service,这个service有一个Cluster-IP,其实就一个VIP。具体实现原理依靠 kubeproxy组件,通过iptables或是ipvs实现。

clusterIP 主要在每个 node 节点使用 iptables,将发向 clusterIP 对应端口的数据,转发到 kube-proxy 中。然 后 kube-proxy 自己内部实现有负载均衡的方法,并可以查询到这个 service 下对应 pod 的地址和端 口,进而把 数据转发给对应的 pod 的地址和端口

这种类型的service 只能在集群内访问。

使用镜像

docker pull tomcat:9.0.20-jre8-alpine

部署service

service/clusteripdemo.yml:

apiVersion: apps/v1
kind: Deployment
metadata:
 name: clusteripdemo
 labels:
  app: clusteripdemo
spec:
 replicas: 3
 template:
  metadata:
   name: clusteripdemo
   labels:
    app: clusteripdemo
  spec:
   containers:
    - name: clusteripdemo
      image: tomcat:9.0.20-jre8-alpine
      imagePullPolicy: IfNotPresent
      ports:
       - containerPort: 8080
  restartPolicy: Always
 selector:
  matchLabels:
   app: clusteripdemo
---
apiVersion: v1
kind: Service
metadata:
 name: clusterip-svc
spec:
 selector:
  app: clusteripdemo
 ports:
  - port: 8080
    targetPort: 8080
 type: ClusterIP

运行service

运行服务
kubectl apply -f clusteripdemo.yml
查看服务
kubectl get svc
访问服务
curl 10.1.15.24:8080
删除服务
kubectl delete -f clusteripdemo.yml

NodePort

我们的场景不全是集群内访问,也需要集群外业务访问。那么ClusterIP就满足不了了。NodePort当然 是其中的一种实现方案。nodePort 的原理在于在 node 上开了一个端口,将向该端口的流量导入到 kube-proxy,然后由 kube-proxy 进一步到给对应的 pod 。

使用镜像

docker pull tomcat:9.0.20-jre8-alpine

部署service

service/nodeportdemo.yml

apiVersion: apps/v1
kind: Deployment
metadata:
 name: clusteripdemo
 labels:
  app: clusteripdemo
spec:
 replicas: 3
 template:
  metadata:
   name: clusteripdemo
   labels:
    app: clusteripdemo
  spec:
   containers:
    - name: clusteripdemo
      image: tomcat:9.0.20-jre8-alpine
      imagePullPolicy: IfNotPresent
      ports:
       - containerPort: 8080
   restartPolicy: Always
 selector:
  matchLabels:
   app: clusteripdemo
---
apiVersion: v1
kind: Service
metadata:
 name: clusterip-svc
spec:
 selector:
  app: clusteripdemo
 ports:
  - port: 8080
    targetPort: 8080
    nodePort: 30088
 type: NodePort

运行service

运行服务
kubectl apply -f nodeportdemo.yml
查看服务
kubectl get svc
访问服务
curl 10.1.61.126:8080
浏览器访问服务
http://192.168.198.156:30088
删除服务
kubectl delete -f nodeportdemo.yml

LoadBalancer

LoadBalancer类型的service 是可以实现集群外部访问服务的另外一种解决方案。不过并不是所有的 k8s集群都会支持,大多是在公有云托管集群中会支持该类型。负载均衡器是异步创建的,关于被提供 的负载均衡器的信息将会通过Service的status.loadBalancer字段被发布出去。

创建 LoadBalancer service 的yaml 如下:

apiVersion: v1
kind: Service
metadata:
 name: service-lagou
spec:
 ports:
  - port: 3000
    protocol: TCP
    targetPort: 443
    nodePort: 30080
 selector:
  run: pod-lagou
 type: LoadBalancer

ExternalName

类型为 ExternalName 的service将服务映射到 DNS 名称,而不是典型的选择器,例如my-service或者 cassandra。您可以使用spec.externalName参数指定这些服务。

这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例 如:

hub.lagouedu.com )。ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何 的端口和 Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的cname(别名)这种方式来提 供服务

创建 ExternalName 类型的服务的 yaml 如下:

kind: Service
apiVersion: v1
metadata:
 name: service-lagou
spec:
 ports:
  - port: 3000
    protocol: TCP
    targetPort: 443
 type: ExternalName
 externalName: www.lagou.com

ingress网络

什么是Ingress?

K8s集群对外暴露服务的方式目前只有三种:loadblancer、nodeport、ingress。前两种熟悉起来比较 快,而且使用起来也比较方便,前面课程已经讲解过两种技术。在此就不进行介绍了。

下面详细讲解下ingress这个服务,ingress由两部分组成:ingress controller和ingress服务。

其中ingress controller目前主要有两种:基于nginx服务的ingress controller和基于traefik的ingress controller。

而其中traefik的ingress controller,目前支持http和https协议。由于对nginx比较熟悉,而且需要使用 TCP负载,所以在此我们选择的是基于nginx服务的ingress controller。

在kubernetes集群中,我们知道service和pod的ip仅在集群内部访问。如果外部应用要访问集群内的服 务,集群外部的请求需要通过负载均衡转发到service在Node上暴露的NodePort上,然后再由kubeproxy组件将其转发给相关的pod。
而Ingress就是为进入集群的请求提供路由规则的集合,通俗点就是提供外部访问集群的入口,将外部 的HTTP或者HTTPS请求转发到集群内部service上。

Ingress-nginx组成

Ingress-nginx一般由三个组件组成:

  • 反向代理负载均衡器:通常以service的port方式运行,接收并按照ingress定义的规则进行转发, 常用的有nginx,Haproxy,Traefik等,本次实验中使用的就是nginx。
  • Ingress Controller:监听APIServer,根据用户编写的ingress规则(编写ingress的yaml文件), 动态地去更改nginx服务的配置文件,并且reload重载使其生效,此过程是自动化的(通过lua脚本 来实现)。
  • Ingress:将nginx的配置抽象成一个Ingress对象,当用户每添加一个新的服务,只需要编写一个 新的ingress的yaml文件即可。

Ingress-nginx的工作原理

  1. ingress controller通过和kubernetes api交互,动态的去感知集群中ingress规则变化。然后读取 它,按照自定义的规则,规则就是写明了那个域名对应哪个service,生成一段nginx配置。
  2. 再写到nginx-ingress-controller的pod里,这个Ingress controller的pod里运行着一个Nginx服 务,控制器会把生成的nginx配置写入/etc/nginx.conf文件中。然后reload一下使配置生效,以此 达到分配和动态更新问题。

官网地址

基于nginx服务的ingress controller根据不同的开发公司,又分为k8s社区的ingres-nginx和nginx 公司的nginx-ingress。

Ingress-Nginx github 地址:
https://github.com/kubernetes/ingress-nginx
Ingress-Nginx 官方网站:
https://kubernetes.github.io/ingress-nginx/

下载资源文件

根据github上的活跃度和关注人数,我们选择的是k8s社区的ingres-nginx。
下载ingrees-controller

打开github官网:选择某一个具体版本后进入deploy/static/目录中,复制mandatory.yaml文件
内容。
https://github.com/kubernetes/ingress-nginx/tree/nginx0.30.0/deploy/static/mandatory.yaml

下载ingress服务

下载镜像

需要将镜像pull到k8s集群各个node节点

ocker pull registry.cn-hangzhou.aliyuncs.com/google_containers/nginxingress-controller:0.30.0
docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/nginx-ingresscontroller:0.30.0 quay.io/kubernetes-ingress-controller/nginx-ingresscontroller:0.30.0
docker rmi -f registry.cn-hangzhou.aliyuncs.com/google_containers/nginxingress-controller:0.30.0

ingress与ingress-controller

要理解ingress,需要区分两个概念,ingress和ingress-controller:

  • ingress对象:
    指的是k8s中的一个api对象,一般用yaml配置。作用是定义请求如何转发到service的规则,可以 理解为配置模板。
  • ingress-controller:
    具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请 求转发。

简单来说,ingress-controller才是负责具体转发的组件,通过各种方式将它暴露在集群入口,外部对集 群的请求流量会先到ingress-controller,而ingress对象是用来告诉ingress-controller该如何转发请 求,比如哪些域名哪些path要转发到哪些服务等等。

ingress-controller

ingress-controller并不是k8s自带的组件,实际上ingress-controller只是一个统称,用户可以选择不同 的ingress-controller实现,目前,由k8s维护的ingress-controller只有google云的GCE与ingress-nginx 两个,其他还有很多第三方维护的ingress-controller,具体可以参考官方文档。但是不管哪一种 ingress-controller,实现的机制都大同小异,只是在具体配置上有差异。一般来说,ingress-controller 的形式都是一个pod,里面跑着daemon程序和反向代理程序。daemon负责不断监控集群的变化,根 据ingress对象生成配置并应用新配置到反向代理,比如nginx-ingress就是动态生成nginx配置,动态更 新upstream,并在需要的时候reload程序应用新配置。

ingress

ingress是一个API对象,和其他对象一样,通过yaml文件来配置。ingress通过http或https暴露集群内 部service,给service提供外部URL、负载均衡、SSL/TLS能力以及基于host的方向代理。ingress要依 靠ingress-controller来具体实现以上功能。

ingress网络实验一

使用镜像

docker pull tomcat:9.0.20-jre8-alpine
docker pull quay.io/kubernetes-ingress-controller/nginx-ingresscontroller:0.30.0

运行ingress-controller

在mandatory.yaml文件的Deployment资源中增加属性sepc.template.sepc.hostNetWork
hostNetwork: true
hostNetwork网络,这是一种直接定义Pod网络的方式。如果在Pod中使用hostNetwork:true配置网
络,那么Pod中运行的应用程序可以直接使用node节点的端口
运行ingress/mandatory.yaml文件
kubectl apply -f mandatory.yaml

运行ingress服务

运行ingress/service-nodeport.yaml文件
kubectl apply -f service-nodeport.yaml

部署tomcat-服务

ingress/tomcat-service.yml

apiVersion: apps/v1
kind: Deployment
metadata:
 name: tomcat-deploy
 labels:
  app: tomcat-deploy
spec:
 replicas: 1
 template:
  metadata:
   name: tomcat-deploy
   labels:
    app: tomcat-deploy
  spec:
   containers:
    - name: tomcat-deploy
      image: tomcat:9.0.20-jre8-alpine
      imagePullPolicy: IfNotPresent
      ports:
       - containerPort: 8080
   restartPolicy: Always
 selector:
  matchLabels:
   app: tomcat-deploy
---
apiVersion: v1
kind: Service
metadata:
 name: tomcat-svc
spec:
 selector:
  app: tomcat-deploy
 ports:
  - port: 8080
    targetPort: 8080
 type: NodePort

运行tomcat-service

kubectl apply -f tomcat-service.yml

部署ingress规则文件

ingress/ingress-tomcat.yml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
 name: nginx-ingress-test
spec:
 backend:
  serviceName: tomcat-svc
  servicePort: 8080

运行ingress规则

kubectl apply -f ingress-tomcat.yml
查看ingress
kubectl get ingress
查看ingress服务:查看service的部署端口号
kubectl get svc -n ingress-nginx
查看ingress-controller运行在那个node节点
kubectl get pod -n ingress-nginx -o wide

通过ingress访问tomcat

链接

ingress网络实验二

上边案例的部署方式只能通过ingress-controller部署的节点访问。集群内其他节点无法访问ingress规 则。本章节通过修改mandatory.yaml文件的控制类类型,让集群内每一个节点都可以正常访问ingress 规则。

ingress-controller

ingress/mandatory.yaml

修改mandatory.yaml配置文件
1.将Deployment类型控制器修改为:DaemonSet
2.属性:replicas: 1 # 删除这行

service-nodeport固定端口

ingress/service-nodeport.yml

apiVersion: v1
kind: Service
metadata:
 name: ingress-nginx
 namespace: ingress-nginx
 labels:
  app.kubernetes.io/name: ingress-nginx
  app.kubernetes.io/part-of: ingress-nginx
spec:
 type: NodePort
 ports:
  - name: http
    port: 80
    targetPort: 80
    nodePort: 31188
    protocol: TCP
  - name: https
    port: 443
    targetPort: 443
    nodePort: 31443
    protocol: TCP
 selector:
  app.kubernetes.io/name: ingress-nginx
  app.kubernetes.io/part-of: ingress-nginx

域名访问ingress规则

service/ingress-tomcat.yml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
 name: nginx-ingress-test
spec:
 rules:
  - host: ingress-tomcat.lagou.com
    http:
     paths:
      - path: /
        backend:
         serviceName: nodeporttomcat-svc
         servicePort: 8080

修改宿主机hosts文件

C:\Windows\System32\drivers\etc\hosts

增加ingress-tomcat.lagou.com 域名配置:
192.168.198.157 ingress-tomcat.lagou.com

部署服务

kubectl apply -f .

浏览器测试

http://ingress-tomcat.lagou.com:31188/

nginx-controller原理

查看ingress-nginx 命名空间下的pod
kubectl get pods -n ingress-nginx
进入ingress-nginx 的pod
kubectl exec -it nginx-ingress-controller-5gt4l -n ingress-nginx sh
查看nginx反向代理域名ingress-tomcat.lagou.com
cat nginx.conf