04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

controller控制器

pod和controller的关系

pod是通过controller实现应用的运维比如伸缩,滚动升级等等

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

node通过labels和master的labels建立联系,然后master进行管理
声明式定义: apply
命令式定义:create

什么是controller

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

是实际存在的,管理pod(pod是虚拟的),在集群上管理和运行容器的对象

所有控制器种类(内置):

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

ReplicationController和ReplicaSet

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

在新版本的 Kubernetes 中建议使用 ReplicaSet 来取代 ReplicationController 。 ReplicaSet 支持集合式的 selector和标签选择器

Deployment
为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的ReplicationController 来方便的管理应用。典型的应用场景包括

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

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

Job
Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束

CronJob
管理基于时间的 Job,即:**

  • 在给定时间点只运行一次
  • 周期性地在给定时间点运行

StatefulSet
StatefulSet 作为 Controller 为 Pod 提供唯一的标识。它可以保证部署和 scale 的顺序.StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用场景包括

  • 稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
  • 稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现
  • 有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实现
  • 有序收缩,有序删除(即从N-1到0

HPA
应用的资源使用率通常都有高峰和低谷的时候,如何削峰填谷,提高集群的整体资源利用率,让 service 中的Pod个数自动调整呢?这就有赖于 HPA (Horizontal Pod Autoscaling)了,顾名思义,使 Pod 水平自动缩放

控制器详细介绍

RC 控制器:

rc.yaml

apiVersion: v1
kind: ReplicationController
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: php-redis
        image: wangyanglinux/myapp:v1
        env:
        - name: GET_HOSTS_FROM
          value: dns
          name: zhangsan
          value: "123"
        ports:
        - containerPort: 80
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

selector下的app应该是metadata下的labels下的app的子集,要求上匹配下

注入环境变量:

env:
-  key1:  value1
   key2:  value2     

kubectl create -f rc.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

删除后依然管控,保持原先的数量

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

控制器是通过标签来控制资源的,如果修改了标签,控制器将无法控制

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl label pod frontend-bqvs9 app=myapp --overwrite
一般标签不能乱改不建议这样操作

kubectl get pods --show-labels

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

被修改了标签的pod不在控制器管辖范围之内,所以重新建了一个pod

RS控制器:

kubectl explain rs.spec.selector

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

添加了更多的匹配机制(matchLabels && matchExpressions【更是支持了运算】),可以取代RC

启动一个RS:
matchLabels.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
spec:
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: myapp
        image: wangyanglinux/myapp:v1
        env:
        - name: GET_HOSTS_FROM
          value: dns
        ports:
        - containerPort: 80

kubectl  create -f matchLabels

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

使用更灵活的选择器案例:selector.matchExpressions

rs 在标签选择器上,除了可以定义键值对的选择形式,还支持 matchExpressions 字段,可以提供多种选择。

目前支持的操作包括:

  • In:label 的值在某个列表中
  • NotIn:label 的值不在某个列表中
  • Exists:某个 label 存在
  • DoesNotExist:某个 label 不存在

例1 Exists判断:
matchExpressionsExists.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: rs-demo
spec:
  selector:
    matchExpressions:
      - key: app
        operator: Exists
  template:
    metadata:
      labels:
        app: spring-k8s
    spec:
      containers:
        - name: rs-c1
          image: wangyanglinux/myapp:v1
          ports:
            - containerPort: 80
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl create -f matchExpressionsExists.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

学习一条扩容命令:
kubectl scale rs/foo --replicas=3 

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

例2:in判断:
matchExpressionsIn.yaml

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: rs-demo
spec:
  selector:
    matchExpressions:
      - key: app
        operator: In
        values:
        - spring-k8s
        - hahahah
  template:
    metadata:
      labels:
        app: sg-k8s
    spec:
      containers:
        - name: rs-c1
          image: wangyanglinux/myapp:v1
          ports:
            - containerPort: 80
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

修改:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

Deployment控制器

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义(declarative)方法,用来替代以前的ReplicationController 来方便的管理应用。典型的应用场景包括

  • 定义Deployment来创建Pod和ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续Deployment
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

升级回滚

滚动更新过程

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

弹性伸缩和回滚操作:

弹性伸缩
nginx-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-deploy
  name: nginx-deploy
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-deploy
  template:
    metadata:
      labels:
        app: nginx-deploy
    spec:
      containers:
      - name: nginx
        image: wangyanglinux/myapp:v1
        ports:
        - containerPort: 80
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

通过deployment控制器扩容:
kubectl scale deployments.apps nginx-deploy --replicas=3

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

通过资源资源利用率弹性扩容(horizontal pod autoscaling):

kubectl autoscale deployment nginx-deploy --min=2 --max=5 --cpu-percent=80
kubectl get horizontalpodautoscalers.autoscaling (kubectl get hpa)
学习helm之后演示

更新镜像:
kubectl set image deployment/nginx-deploy nginx=wangyanglinux/myapp:v2
将容器名containers的名字为"nginx"的镜像修改成wangyanglinux/myapp:v2

更新前为v1版本

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

更新后为v2版本

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

回滚:

kubectl rollout undo deployment/nginx-deploy

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

选择版本进行回滚:
kubectl rollout history deployment nginx-deploy

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl rollout undo deployment nginx-deploy --to-revision=2

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

但是此时也不知道2 3 到底对应的什么版本,这就需要在创建一个版本时需要一个记录
删除所有deployment,重新来
kubectl apply -f nginx-deploy.yaml --record
kubectl set image deployment/nginx-deploy nginx=wangyanglinux/myapp:v2 --record
kubectl rollout history deployment/nginx-deploy

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

继续升级为v3、v4版本
kubectl set image deployment/nginx-deploy nginx=wangyanglinux/myapp:v3 --record
kubectl set image deployment/nginx-deploy nginx=wangyanglinux/myapp:v4 --record
kubectl rollout history deployment nginx-deploy

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

可以看到deployment已经创建了4个rs控制器:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

指定回滚到某个版本:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

使用--record=ture所带来的问题

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

只要有一次忘记添加此参数,就全都乱了

此时如果更新到v5版本,且忘记添加--record=true参数,则会显示又一次设置为v4版本,会搞乱,所以最佳方案是,在本地目录下创建多个版本的yaml文件并加上名字标记的唯一性,更新版本和回滚版本直接kubectl apply -f 对应版本文件即可

Deployment 更新策略

旧版本:
Deployment 可以保证在升级时只有一定数量的 Pod 是 down 的。默认的,它会确保至少有比期望的Pod数量少一个是up状态(最多一个不可用)
Deployment 同时也可以确保只创建出超过期望数量的一定数量的 Pod。默认的,它会确保最多比期望的Pod数量多一个的 Pod 是 up 的(最多1个 surge )

参数讲解:
maxSurge:指定超出副本数有几个,两种方式:1、指定数量 2、百分比
maxUnavailable : 最多有几个(百分之多少)不可用

Kuberentes 新版本中,将从1-1变成25%-25%
kubectl describe deployments.apps nginx-deploy

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl edit deployments.apps nginx-deploy

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

打补丁:
使用json进行补丁更新:
使用打补丁方式将镜像版本修改为v2:
kubectl create deployment web --image=wangyanglinux/myapp:v1
kubectl get deployments.apps web -o json >web.json
层层粘贴抓取name和image

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl patch deployment web -p '{"spec":{"template":{"spec":{"containers":[{"name": "wangyanglinux","image": "wangyanglinux/myapp:v2"}]}}}}'

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

金丝雀(之后使用service方式更好):
添加一个pod使用新版本内测,如果没问题,再全都切换至新版本

实验(使用nginx-deploy.yaml,副本设置为3):
kubectl patch deployment nginx-deploy -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'

kubectl describe deployments.apps nginx-deploy

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

更新策略将会变成添加一个创建中的pod且不能有pod不可用

金丝雀命令:
kubectl set image deploy nginx-deploy nginx=wangyanglinux/myapp:v2 && kubectl rollout pause deploy nginx-deploy
更新等添加一个pod然后瞬间暂停

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

多了一个v2版本的pod
测试负载均衡:
kubectl get pods --show-labels

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

curl 10.97.217.174

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

等v2版本没问题了,恢复执行更新,所有pod都成为新版本:
kubectl rollout resume deploy nginx-deploy

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

查看是否全部更新成功:
kubectl rollout status deployments nginx-deploy

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

Rollover(多个rollout并行)
假如您创建了一个有5个nginx:1.7.9 replica的 Deployment,但是如果还只有3个nginx:1.7.9的 replica 创建出来的时候您就开始更新含有5个nginx:1.9.1 replica 的 Deployment,在这种情况下,Deployment 会立即杀掉已创建的3个nginx:1.7.9的 Pod,并开始创建nginx:1.9.1的 Pod。它不会等到所有的5个 nginx:1.7.9的 Pod 都创建完成后才开始改变航道

清理 Policy
可以通过设置.spec.revisionHistoryLimit项来指定 deployment 最多保留多少 revision 历史记录。默认的会保留所有的 revision;如果将该项设置为0,Deployment 就不允许回退了,可以有效节约资源

目前有很多版本的rs,比较浪费资源:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

生产环境:
nginx-deploy-v1.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-deploy
  name: nginx-deploy
spec:
  revisionHistoryLimit: 0
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deploy
  template:
    metadata:
      labels:
        app: nginx-deploy
    spec:
      containers:
      - name: nginx
        image: wangyanglinux/myapp:v1

nginx-deploy-v2.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx-deploy
  name: nginx-deploy
spec:
  revisionHistoryLimit: 0
  replicas: 3
  selector:
    matchLabels:
      app: nginx-deploy
  template:
    metadata:
      labels:
        app: nginx-deploy
    spec:
      containers:
      - name: nginx
        image: wangyanglinux/myapp:v2

创建并升级版本,假如不对rs进行限制会产生两个rs
kubectl create -f nginx-deploy-v1.yaml
kubectl apply -f nginx-deploy-v2.yaml

kubectl get rs

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

但是目前只有一个rs

生产环境金丝雀部署:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

deployment控制器部署无状态应用
导出yaml文件
kubectl create deployment web --image=nginx --dry-run -o yaml > web.yaml
部署命令+尝试部署+导出命令+导出文件
vim web.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

使用yaml文件部署
kubectl apply -f web.yaml
kubectl get pods

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

对外进行发布,暴露端口号
kubectl expose deployment web --port=80 --type=NodePort --target-port=80(可省略) --name=web1 -o yaml > web-expose.yaml
vim web-expose.yaml
kubectl apply -f web-expose.yaml
kubectl get svc

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

DaemonSet

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

使用 DaemonSet  的一些典型用法:

  • 运行集群存储 daemon,例如在每个 Node 上运行 glusterdceph
  • 在每个 Node 上运行日志收集 daemon,例如fluentdlogstash
  • 在每个 Node 上运行监控 daemon,例如 Prometheus Node Exporter、collectd、Datadog 代理、New Relic 代理,或 Ganglia gmond

练习: vi DaemonSet.yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: deamonset-example
  labels:
    app: daemonset
spec:
  selector:
    matchLabels:
      name: deamonset-example
  template:
    metadata:
      labels:
        name: deamonset-example
    spec:
      containers:
      - name: daemonset-example
        image: wangyanglinux/myapp:v1

apply一下

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

node1和node都被部署了pod,但是master没有被部署,因为master自带污点
kubectl describe node k8s-master

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

如果master有污点,设置容忍的node有可能会接受master,也可以选择不容忍
flannel就是典型的daemonSet
kubectl get daemonsets.apps -n kube-system

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

部署守护进程DaemonSet日志采集工具

实现所有node在同一个pod中运行,新加入的node也同样在一个pod中

每个node中安装数据采集工具

ds.yaml

apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: ds-test 
  labels:
    app: filebeat
spec:
  selector:
    matchLabels:
      app: filebeat
  template:
    metadata:
      labels:
        app: filebeat
    spec:
      containers:
      - name: logs
        image: nginx
        ports:
        - containerPort: 80
        volumeMounts:
        - name: varlog
          mountPath: /tmp/log
      volumes:
      - name: varlog
        hostPath:
          path: /var/log

kubectl delete statefulset --all
kubectl apply -f ds.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl get pods

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

进入容器查看日志
kubectl exec -it ds-test-pkzk7 bash

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

deployment控制器部署有状态应用(statefulset)

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

有状态通过唯一的网络标识符和持久存储实现每个pod的唯一性

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

部署有状态应用
无头service

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

安装流程:
1:无头IP
2:使用StatefulSet

通过statefulset部署
特点:
sts.yaml

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
#通过clusterIP: None创建一个无状态的service
  clusterIP: None
  selector:
    app: nginx

---

apiVersion: apps/v1
#重点:StatefulSet
kind: StatefulSet
metadata:
  name: nginx-statefulset
  namespace: default
spec:
  serviceName: nginx
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - containerPort: 80

kubectl apply -f sts.yaml

kubectl get pods

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

三个pod,每个都是唯一的名称

kubectl get svc

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

查看svc,类型为无头service

区分方式

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

一次性任务和定时任务

job

概念:
Job 负责【批处理】任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束(比如说数据库备份需要处理多次)

Job可以干什么

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

Job配置参数详解

apiVersion: batch/v1
kind: Job
metadata:
  labels:
    job-name: echo
  name: echo
  namespace: default
spec: # 单个Pod时,默认Pod成功运行后Job即结束
  activeDeadlineSeconds: 100 # 标志失败Pod的重试最大时间,超过这个时间不会继续重试
  # suspend: true # 1.21+
  # ttlSecondsAfterFinished: 100 # 自动清理已结束的 Job(Complete 或 Failed),需要开启特性
  backoffLimit: 4 # 如果任务执行失败,失败多少次后不再执行,默认为1
  completions: 5 # 有多少个Pod执行成功,认为任务是成功的,默认为1
  parallelism: 3 # 并行执行任务的数量,如果parallelism数值大于未完成任务数,只会创建未完成的数量,比如completions是4,并发是3,第一次会创建3个Pod执行任务,第二次只会创建一个Pod执行任务
  template: # 格式同Pod
    spec:
      containers:
      - command:
        - echo
        - Hello, Job
        image: registry.cn-beijing.aliyuncs.com/dotbalo/busybox
        imagePullPolicy: Always
        name: echo
        resources: {}
      restartPolicy: Never # RestartPolicy仅支持Never或OnFailure

python项目实战(计算圆周率):
main.py

# -*- coding: utf-8 -*-
from __future__ import division
# 导入时间模块
import time
# 计算当前时间
time1=time.time()
# 算法根据马青公式计算圆周率 #
number = 1000
# 多计算10位,防止尾数取舍的影响
number1 = number+10
# 算到小数点后number1位
b = 10**number1
# 求含4/5的首项
x1 = b*4//5
# 求含1/239的首项
x2 = b // -239
# 求第一大项
he = x1+x2
#设置下面循环的终点,即共计算n项
number *= 2
#循环初值=3,末值2n,步长=2
for i in xrange(3,number,2):
  # 求每个含1/5的项及符号
  x1 //= -25
  # 求每个含1/239的项及符号
  x2 //= -57121
  # 求两项之和
  x = (x1+x2) // i
  # 求总和
  he += x
# 求出π
pai = he*4
#舍掉后十位
pai //= 10**10
# 输出圆周率π的值
paistring=str(pai)
result=paistring[0]+str('.')+paistring[1:len(paistring)]
print result
time2=time.time()
print u'Total time:' + str(time2 - time1) + 's'

python main.py

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

vi Dockerfile

FROM hub.c.163.com/public/python:2.7
ADD ./main.py /root
CMD /usr/bin/python /root/main.py

docker build -t pi:v1 .

只做好之后保存并传送node下载下镜像
docker save -o pi.tar pi:v1
docker load -i pi.tar

vi job.yaml

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  template:
    metadata:
      name: pi
    spec:
      containers:
      - name: pi
        image: pi:v1
      restartPolicy: Never

kubectl apply -f job.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

添加选项:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl delete job --all

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl apply -f job.yaml && kubectl get pods -o wide -w

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

CronJob

特性:
管理基于时间的
- 在给定时间点只运行一次
- 周期性地在给定时间点运行

典型的用法如下所示:
在给定的时间点调度 Job 运行
创建周期性运行的 Job,例如:数据库备份、发送邮件

使用条件:当前使用的 Kubernetes 集群,版本 >= 1.8(对 CronJob)

CronJob模版:

apiVersion: batch/v1beta1 # apiVersion: batch/v1beta1 #1.21+ batch/v1
kind: CronJob
metadata:
  labels:
    run: hello
  name: hello
  namespace: default
spec:
  schedule: '*/1 * * * *' # schedule:调度周期,和Linux一致,分别是分时日月周
  successfulJobsHistoryLimit: 3 # 保留多少已完成的任务,按需配置
  failedJobsHistoryLimit: 1 # 保留多少失败的任务。
  suspend: false # 如果设置为true,则暂停后续的任务,默认为false
  concurrencyPolicy: Allow # 并发调度策略。可选参数如下 
  # Allow:允许同时运行多个任务。 
  # Forbid:不允许并发运行,如果之前的任务尚未完成,新的任务不会被创建。 
  # Replace:如果之前的任务尚未完成,新的任务会替换的之前的任务。 
  jobTemplate:
    metadata:
    spec:
      activeDeadlineSeconds: 8 # 在 8 秒后终止 Pod,必须在 8 秒内完成运行,这个属于 job 的策略
      template:
        metadata:
          labels:
            run: hello
        spec:
          containers:
          - args:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
            image: registry.cn-beijing.aliyuncs.com/dotbalo/busybox
            imagePullPolicy: Always
            name: hello
            resources: {}
          restartPolicy: OnFailure # restartPolicy:重启策略,和Pod一致
          securityContext: {}

Job Spec参考Job

CronJob Spec
.spec.schedule:调度,必需字段,指定任务运行周期,格式同 Cron
.spec.jobTemplate:Job 模板,必需字段,指定需要运行的任务,格式同 Job**
.spec.startingDeadlineSeconds :启动 Job 的期限(秒级别),该字段是可选的。如果因为任何原因而错过了被调度的时间,那么错过执行时间的 Job 将被认为是失败的。如果没有指定,则没有期限
.spec.concurrencyPolicy:并发策略,该字段也是可选的。它指定了如何处理被 Cron Job 创建的 Job 的并发执行。只允许指定下面策略中的一种:
Allow(默认):允许并发运行 Job(可能第二个job被创建时,第一个job还未完成,则并行执行)
Forbid:禁止并发运行,如果前一个还没有完成,则直接跳过下一个(干掉此时要新建的job)
Replace:取消当前正在运行的 Job,用一个新的来替换(干掉上一个job)
注意,当前策略只能应用于同一个 Cron Job 创建的 Job。如果存在多个 Cron Job,它们创建的 Job 之间总是允许并发运行。
.spec.suspend :挂起,该字段也是可选的。如果设置为 true,后续所有执行都会被挂起。它对已经开始执行的 Job 不起作用。默认值为 false(认为的在运行时运行,不运行时edit下选择false即可)
.spec.successfulJobsHistoryLimit.spec.failedJobsHistoryLimit :历史限制,是可选的字段。它们指定了可以保留多少完成和失败的 Job。默认情况下,它们分别设置为 31。设置限制的值为 0,相关类型的 Job 完成后将不会被保留

练习:
cronjob.yaml

apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            args:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

kubectl create -f cronjob.yaml
kubectl get cronjobs,pods

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

保留三个job,间隔60秒
暂时不用了,先挂起:
kubectl edit cronjobs.batch hello

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl  get cronjobs,pods

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

一般挂起适用于集群告警暂停处理问题

CrondJob 本身的一些限制:
创建 Job 操作应该是幂等的

service

service介绍:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

在 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 代理

userspace模式

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

userspace模式所有流量都要经过kube-proxy,类似于ipvs的nat模式

在 userspace 模式下,kube-proxy 通过监听 K8s apiserver 获取关于 Service 和 Endpoint 的变化信息,在内存中维护一份从ClusterIP:Port 到后端 Endpoints 的映射关系,通过反向代理的形式,将收到的数据包转发给后端,并将后端返回的应答报文转发给客户端。该模式下,kube-proxy 会为每个 Service (每种协议,每个 Service IP,每个 Service Port)在宿主机上创建一个 Socket 套接字(监听端口随机)用于接收和转发 client 的请求。默认条件下,kube-proxy 采用 round-robin 算法从后端 Endpoint 列表中选择一个响应请求。

由于其需要来回在用户空间和内核空间交互通信,因此效率很差

Iptables模式

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

iptables模式减少可kube-proxy压力

在iptables 模式下,kube-proxy 依然需要通过监听K8s apiserver 获取关于 Service 和 Endpoint 的变化信息。不过与 userspace 模式不同的是,kube-proxy 不再为每个 Service 创建反向代理(也就是无需创建 Socket 监听),而是通过安装 iptables 规则,捕获访问 Service ClusterIP:Port 的流量,直接重定向到指定的 Endpoints 后端。默认条件下,kube-proxy 会 随机 从后端 Endpoint 列表中选择一个响应请求。ipatbles 模式与 userspace 模式的不同之处在于,数据包的转发不再通过 kube-proxy 在用户空间通过反向代理来做,而是基于 iptables/netfilter 在内核空间直接转发,避免了数据的来回拷贝,因此在性能上具有很大优势,而且也避免了大量宿主机端口被占用的问题。

  但是将数据转发完全交给 iptables 来做也有个缺点,就是一旦选择的后端没有响应,连接就会直接失败了,而不会像 userspace 模式那样,反向代理可以支持自动重新选择后端重试,算是失去了一定的重试灵活性。不过,官方建议使用 Readiness 探针来解决这个问题,一旦检测到后端故障,就自动将其移出 Endpoint 列表,避免请求被代理到存在问题的后端。

  并且iptables 因为它纯粹是为防火墙而设计的,并且基于内核规则列表,集群数量越多性能越差。

  一个例子是,在5000节点集群中使用 NodePort 服务,如果我们有2000个服务并且每个服务有10个 pod,这将在每个工作节点上至少产生20000个 iptable 记录,这可能使内核非常繁忙。

Ipvs代理模式

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

ipvs模式引入了LVS,可以承受更大的并发量

IPVS 是一个用于负载均衡的 Linux 内核功能。IPVS 模式下,kube-proxy 使用 IPVS 负载均衡代替了 iptable。这种模式同样有效,IPVS 的设计就是用来为大量服务进行负载均衡的,它有一套优化过的 API,使用优化的查找算法,而不是简单的从列表中查找规则。

  这样一来,kube-proxy 在 IPVS 模式下,其连接过程的复杂度为 O(1)。换句话说,多数情况下,他的连接处理效率是和集群规模无关的。

  另外作为一个独立的负载均衡器,IPVS 包含了多种不同的负载均衡算法,例如轮询、最短期望延迟、最少连接以及各种哈希方法等。而 iptables 就只有一种随机平等的选择算法。

  IPVS代理模式基于netfilter hook函数,该函数类似于iptables模式,但使用hash表作为底层数据结构,在内核空间中工作。这意味着IPVS模式下的kube-proxy使用更低的重定向流量。其同步规则的效率和网络吞吐量也更高。因此当service数量达到一定规模时,hash查表的速度优势就会显现出来,从而提高service的服务性能。IPVS是专门为负载均衡设计的,并且底层使用哈希表这种非常高效的数据结构,几乎可以允许无限扩容。

  IPVS 的一个潜在缺点就是,IPVS 处理数据包的路径和通常情况下 iptables 过滤器的路径是不同的。如果计划在有其他程序使用 iptables 的环境中使用 IPVS,需要进行一些研究,看看他们是否能够协调工作。(Calico 已经和 IPVS kube-proxy 兼容)

在 Kubernetes 1.14 版本开始默认使用 ipvs 代理

在 Kubernetes v1.0 版本,Service 是 “4层”(TCP/UDP over IP)概念。 在 Kubernetes v1.1 版本,新增了 Ingress API(beta 版),用来表示 “7层”(HTTP)服务

Service能够提供负载均衡的能力,但是只提供 4 层负载均衡能力,而没有 7 层功能,但有时我们可能需要更多的匹配规则来转发请求,这点上 4 层负载均衡是不支持的

性能对比

iptables 的连接处理算法复杂度是 O(n),而 IPVS 模式是 O(1)

在微服务环境中,其具体表现如何呢

在多数场景中,有两个关键属性需要关注:

  • 响应时间:一个微服务向另一个微服务发起调用时,第一个微服务发送请求,并从第二个微服务中得到响应,中间消耗了多少时间?
  • CPU消耗:运行微服务的过程中,总体 CPU 使用情况如何?包括用户和核心空间的 CPU 使用,包含所有用于支持微服务的进程(也包括 kube-proxy)。

为了说明问题,我们运行一个微服务作为客户端,这个微服务以 Pod 的形式运行在一个独立的节点上,每秒钟发出 1000 个请求,请求的目标是一个 Kubernetes 服务,这个服务由 10 个 Pod 作为后端,运行在其它的节点上。接下来我们在客户端节点上进行了测量,包括 iptables 以及 IPVS 模式,运行了数量不等的 Kubernetes 服务,每个服务都有 10 个 Pod,最大有 10,000 个服务(也就是 100,000 个 Pod)。我们用 golang 编写了一个简单的测试工具作为客户端,用标准的 NGINX 作为后端服务

响应时间

响应时间很重要,有助于我们理解连接和请求的差异。典型情况下,多数微服务都会使用持久或者 keepalive 连接,这意味着每个连接都会被多个请求复用,而不是每个请求一次连接。这很重要,因为多数连接的新建过程都需要完成三次 TCP 握手的过程,这需要消耗时间,也需要在 Linux 网络栈中进行更多操作,也就会消耗更多 CPU 和时间。

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

这张图展示了两个关键点:

  • iptables 和 IPVS 的平均响应时间在 1000 个服务(10000 个 Pod)以上时,会开始观察到差异。
  • 只有在每次请求都发起新连接的情况下,两种模式的差异才比较明显。

  不管是 iptables 还是 IPVS,kube-proxy 的响应时间开销都是和建立连接的数量相关的,而不是数据包或者请求数量,这是因为 Linux 使用了 Conntrack,能够高效地将数据包和现存连接关联起来。如果数据包能够被 Conntrack 成功匹配,那就不需要通过 kube-proxy 的 iptables 或 IPVS 规则来推算去向。Linux conntrack 非常棒!(绝大多数时候)

  值得注意的是,例子中的服务端微服务使用 NGINX 提供一个静态小页面。多数微服务要做更多操作,因此会产生更高的响应时间,也就是 kube-proxy 处理过程在总体时间中的占比会减少。

  还有个需要解释的古怪问题:既然 IPVS 的连接过程复杂度是 O(1),为什么在 10,000 服务的情况下,非 Keepalive 的响应时间还是提高了?我们需要深入挖掘更多内容才能解释这一问题,但是其中一个因素就是因为上升的 CPU 用量拖慢了整个系统。这就是下一个主题需要探究的内容。

CPU用量

为了描述 CPU 用量,下图关注的是最差情况:不使用持久/keepalive 连接的情况下,kube-proxy 会有最大的处理开销。

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

上图说明了两件事:

  • 在超过 1000 个服务(也就是 10,000 个 Pod)的情况下,CPU 用量差异才开始明显。
  • 在一万个服务的情况下(十万个后端 Pod),iptables 模式增长了 0.35 个核心的占用,而 IPVS 模式仅增长了 8%。

有两个主要因素造成 CPU 用量增长:

第一个因素是,缺省情况下 kube-proxy 每 30 秒会用所有服务对内核重新编程。这也解释了为什么 IPVS 模式下,新建连接的 O(1) 复杂度也仍然会产生更多的 CPU 占用。另外,如果是旧版本内核,重新编程 iptables 的 API 会更慢。所以如果你用的内核较旧,iptables 模式可能会占用更多的 CPU。

另一个因素是,kube-proxy 使用 IPVS 或者 iptables 处理新连接的消耗。对 iptables 来说,通常是 O(n) 的复杂度。在存在大量服务的情况下,会出现显著的 CPU 占用升高。例如在 10,000 服务(100,000 个后端 Pod)的情况下,iptables 会为每个请求的每个连接处理大约 20000 条规则。如果使用 NINGX 缺省每连接 100 请求的 keepalive 设置,kube-proxy 的 iptables 规则执行次数会减少为 1%,会把 iptables 的 CPU 消耗降低到和 IPVS 类似的水平。

客户端微服务会简单的丢弃响应内容。真实世界中自然会进行更多处理,也会造成更多的 CPU 消耗,但是不会影响 CPU 消耗随服务数量增长的事实。

结论

二者有着本质的差别:iptables是为防火墙而设计的;IPVS则专门用于高性能负载均衡,并使用更高效的数据结构(Hash表),允许几乎无限的规模扩张。

  在超过 1000 服务的规模下,kube-proxy 的 IPVS 模式会有更好的性能表现。虽然可能有多种不同情况,但是通常来说,让微服务使用持久连接、运行现代内核,也能取得较好的效果。如果运行的内核较旧,或者无法使用持久连接,那么 IPVS 模式可能是个更好的选择。

  抛开性能问题不谈,IPVS 模式还有个好处就是具有更多的负载均衡算法可供选择。

  如果你还不确定 IPVS 是否合适,那就继续使用 iptables 模式好了。这种传统模式有大量的生产案例支撑,他是一个不完美的缺省选项。

Calico和kube-proxy的iptables比较

我们看到,kube-proxy 中的 iptables 用法在大规模集群中可能会产生性能问题。有人问我 Calico 为什么没有类似的问题。答案是 Calico 中 kube-proxy 的用法是不同的。kube-proxy 使用了一个很长的规则链条,链条长度会随着集群规模而增长,Calico 使用的是一个很短的优化过的规则链,经由 ipsets 的加持,也具备了 O(1) 复杂度的查询能力。

  下图证明了这一观点,其中展示了每次连接过程中,kube-proxy 和 Calico 中 iptables 规则数量的平均值。这里假设集群中的节点平均有 30 个 Pod,每个 Pod 具有 3 个网络规则。

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

即使是使用 10,000 个服务和 100,000 个 Pod 的情况下,Calico 每连接执行的 iptables 规则也只是和 kube-proxy 在 20 服务 200 个 Pod 的情况基本一致。

service存在的意义

1、防止pod失联

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

pod升级回滚操作会导致ip变化,需要在service上注册ip,pod IP即使变化也会在service中记录,通过pod发现防止服务器失联

2、负载均衡作用

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

pod和service的关系

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

service类似于lvs进行负载均衡,并拥有自己的VIP,service通过labels控制pod

常用sevice类型

kubectl explain service.spec.type

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

ClusterIP:集群内部使用

ClusterIp:默认类型,自动分配一个仅 集群 内部可以访问的虚拟 IP

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

clusterIP是不会变的,由service分配生成,即使有deployment实例化的pod异常退出,svc后端pod IP也会动态监听,修改轮巡IP配置

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

ClusterIP类似于内部要配置NAT

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

为了实现图上的功能,主要需要以下几个组件的协同工作:

  • apiserver:用户通过 kubectl 命令向 apiserver 发送创建 service 的命令,apiserver 接收到请求后将数据存储到 etcd 中
  • kube-proxy:kubernetes 的每个节点中都有一个叫做 kube-porxy 的进程,这个进程负责感知service,pod 的变化,并将变化的信息写入本地的 ipvs 规则中
  • ipvs:基于内核的钩子函数机制实现负载

创建一个集群内部IP
kubectl create deployment web --image=nginx --dry-run -o yaml > web.yaml
kubectl apply -f web.yaml
kubectl expose deployment web --port=80 --target-port=80 --dry-run -o yaml > service1.yaml

cat service1.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl apply -f service1.yaml

kubectl get svc

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

curl 10.100.27.227

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

验证service在kube-dns中注册了内部域名:

myapp-deploy.yaml文件:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      release: stabel
  template:
    metadata:
      labels:
        app: myapp
        release: stabel
        env: test
    spec:
      containers:
      - name: myapp
        image: wangyanglinux/myapp:v2
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 80

kubectl apply -f myapp-deploy.yaml

创建 Service 信息
clusterIp-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  type: ClusterIP
  selector:
    app: myapp
    release: stabel
  ports:
  - name: http
    port: 80
    targetPort: 80

kubectl apply -f clusterIp-svc.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kube-dns中查看

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

dig -t A myapp.default.svc.cluster.local. @10.96.0.10
格式:dig -t -A [svc名字].[命名空间].svc.cluster.local. @[kube-system中kube-dns的域名]

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

svc在内部kube-dns上注册了内部域名

任意创建一个pod
pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: readiness-httpget-pod
  namespace: default
  labels:
    app: myapp
spec:
  containers:
  - name: readiness-httpget-container
    image: wangyanglinux/myapp:v1
    imagePullPolicy: IfNotPresent
    readinessProbe:
      httpGet:
        port: 80
        path: /index.html
      initialDelaySeconds: 1
      periodSeconds: 3

进入pod
kubectl exec -it readiness-httpget-pod -- sh

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

说明每个新建的pod都可以通过coredns获取到服务的域名

基于 service 模拟金丝雀部署:

设计思路:通过相同的标签选择器,将不同版本容器的 pod 部署在相同的 service 后端,新版本先分配小部分流量(创建极小比例 pod),如果新版本没有问题,旧版本使用 edit 方式跟着升级到新版本,最后删除新版本的测试 pod

kubectl delete deployments.apps --all

myapp-deploy-v1.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy-v1
  namespace: default
spec:
  replicas: 3
  selector:
    matchLabels:
      app: myapp
      release: stabel
      version: v1
  template:
    metadata:
      labels:
        app: myapp
        release: stabel
        version: v1
    spec:
      containers:
      - name: myapp
        image: wangyanglinux/myapp:v1
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 80

myapp-deploy-v2.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp-deploy-v2
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: myapp
      release: stabel
      version: v2
  template:
    metadata:
      labels:
        app: myapp
        release: stabel
        version: v2
    spec:
      containers:
      - name: myapp
        image: wangyanglinux/myapp:v2
        imagePullPolicy: IfNotPresent
        ports:
        - name: http
          containerPort: 80

kubectl  apply -f myapp-deploy-v1.yaml

kubectl  get svc

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl apply -f myapp-deploy-v2.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

svc成功负载v2版本作为金丝雀

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

如果v2版本没问题,继续升级v1到v2版本
kubectl edit deployments.apps myapp-deploy-v1

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

如果没必要最后可以删除金丝雀版本测试pod
kubectl delete -f myapp-deploy-v2.yaml

Headless Service(隶属于ClusterIp)

有时不需要或不想要负载均衡,以及单独的 Service IP 。遇到这种情况,可以通过指定 Cluster IP ( spec.clusterIP ) 的值为 “ None ” 来创建 Headless Service 。这类 Service 并不会分配 Cluster IP, kube-proxy 不会处理它们,而且平台也不会为它们进行负载均衡和路由,但是会给分配一个内部的dns

headless-svc.yaml

apiVersion: v1
kind: Service
metadata:
  name: myapp-headless
  namespace: default
spec:
  selector:
    app: myapp
  clusterIP: "None"
  ports: 
  - port: 80
    targetPort: 80
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

dig -t A myapp-headless.default.svc.cluster.local. @10.96.0.10

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

给每一个pod提供一个固定的网络标识,当一个pod消失会自动添加一个pod,并更新core-dns的clusterIP

NodePort(对外访问应用使用,将调度器地址绑定在物理网卡上)

NodePort:在 ClusterIP 基础上为 Service 在每台机器上绑定一个端口,这样就可以通过 : NodePort 来访问该服务

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

创建一个暴露外部集群的端口(NodePort)
vim service1.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl apply -f service1.yaml
kubectl get svc

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

可实现外网访问

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

练习:
kubectl  get pod --show-labels

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

vim myapp-service.yaml

apiVersion: v1
kind: Service
metadata:
  name: myapp
  namespace: default
spec:
  type: NodePort
  selector:
    app: myapp
    release: stabel
  ports:
  - name: http
    port: 80
    targetPort: 80
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

k8s中升级较稳定,降级建议先删除要修改的svc,然后再创建新的svc,例如clusterIP->NodePort为升级,反之为降级

升级操作:
kubectl edit svc myapp

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

master和nodeIP都可以用过NodePort访问:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

LoadBalacer:对外访问使用,公有云

因为node在内网部署,不使用外网,可以使用nginx进行负载均衡

LoadBalancer:在 NodePort 的基础上,借助 cloud provider 创建一个外部负载均衡器,并将请求转发到: NodePort

纯物理云搭建高可用方式:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

公有云默认没有IPVS内核模块,需要购买负载均衡,公有云会在NodePort创建之时自动创建一个负载均衡对外暴露端口(LAAS)

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持

当内部有多个pod要访问外部的服务时,可以将外部的服务抽象到内部集群的域名。可以使用变量的方式,pod通过本地svc获取到外部服务,当外部服务IP发生变化时,只需要修改内部svc的IP值。CoreDNS会自动将外部的服务做解析解析到新的IP上,使得服务更加灵活

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

这种类型的 Service 通过返回 CNAME 和它的值,可以将服务映射到 externalName 字段的内容( 例如:hub.hongfu.com )。ExternalName Service 是 Service 的特例,它没有 selector,也没有定义任何的端口和 Endpoint。相反的,对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务

ExternalName-svc.yaml

kind: Service
apiVersion: v1
metadata:
  name: my-service-1
  namespace: default
spec:
  type: ExternalName
  externalName: hub.hongfu.com

另外也可以使用DNS但是需要DNS的add-on

ConfigMap

作用:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者 JSON 二进制等对象,给我们提供了向容器中注入配置信息的机制

使用configmap创建环境变量和使用环境变量

创建环境变量:

使用目录注入环境变量:

文件目录结构和内容(/root/k8s/configmap下创建并编辑game.file和ui.properties两个文件)

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

game.file

version=1.17
name=dave
age=18

ui.properties

level=2
color=yellow

目录注入环境变量
kubectl create configmap game-config --from-file=/root/k8s/configmap
--from-file 指定所在目录下的所有文件都会被用在ConfigMap里面创建一个键值对,键的名字就是文件名,值就是文件内容

熟悉下yaml文件格式:
kubectl create configmap game-config --from-file=/root/k8s/configmap --dry-run=client -o yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

查看cm中的kv信息方式:
kubectl describe cm game-config

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl get cm -o yaml

使用文件创建环境变量:
只要指定为一个文件就可以从单个文件中创建ConfigMap
kubectl create configmap game-config-v2 --from-file=/root/k8s/configmap/game.file
kubectl describe cm game-config-v2

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

使用字面值命令创建:
利用 —from-literal 参数传递配置信息,该参数可以使用多次
kubectl create configmap literal-config --from-literal=name=dave --from-literal=password=pass
kubectl describe cm literal-config

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

pod使用环境变量:

使用 ConfigMap 来替代环境变量

运行过程:现将环境变量信息注入在cm中,在pod运行时使用cm中的值

literal-config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: literal-config
  namespace: default
data:
  name: dave
  password: pass

env-config.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: env-config
  namespace: default
data:
  log_level: INFO

cm-env-test-pod.yaml

apiVersion: v1
kind: Pod
metadata:
  name: cm-env-test-pod
spec:
  containers:
    - name: test-container
      image: wangyanglinux/myapp:v1
      command: [ "/bin/sh", "-c", "env" ]
      env:
        - name: USERNAME
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: name
        - name: PASSWORD
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: password
      envFrom:
        - configMapRef:
            name: env-config
  restartPolicy: Never
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl  logs cm-env-test-pod

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

用configmap设置启动命令行参数
cm-command-dapi-test-pod

apiVersion: v1
kind: Pod
metadata:
  name: cm-command-dapi-test-pod
spec:
  containers:
    - name: test-container
      image: wangyanglinux/myapp:v1
      command: [ "/bin/sh", "-c", "echo $(USERNAME) $(PASSWORD)" ]
      env:
        - name: USERNAME
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: name
        - name: PASSWORD
          valueFrom:
            configMapKeyRef:
              name: literal-config
              key: password
  restartPolicy: Never

kubectl logs cm-command-dapi-test-pod

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

通过数据卷插件使用ConfigMap

在数据卷里面使用这个 ConfigMap,有不同的选项。最基本的就是将文件填入数据卷,在这个文件中,【键就是文件名,键值就是文件内容】,环境变量方式是不支持热更新的

ConfigMap 的热更新配置文件

1、文件热更新
热更新是通过环境变量主动进行批量化注入的方式(不是完全共享目录方式),不会对IO造成很大的影响,但更新速度也不会太快

log-config-cm.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: log-config
  namespace: default
data:
  log_level: INFO

hot-update-deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    run: my-nginx
  name: hot-update
spec:
  replicas: 1
  selector:
    matchLabels:
      run: my-nginx
  template:
    metadata:
      labels:
        run: my-nginx
    spec:
      containers:
      - name: my-nginx
        image: wangyanglinux/myapp:v1
        ports:
        - containerPort: 80
        volumeMounts:
        - name: config-volume
          mountPath: /etc/config
      volumes:
        - name: config-volume
          configMap:
            name: log-config
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

while 2>1; do kubectl exec hot-update-75b6496495-mmzqf -- cat /etc/config/log_level; echo; sleep 1s; done

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

新开一个终端:kubectl edit cm log-config

所有INFO都要修改成ERROR:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

热更新显示:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

2、ConfigMap 更新后滚动更新 Pod
会有很多服务不单单是配置文件更新就可,往往需要重启才能更新

方式一:通过修改docker tag 改名实现滚动更新

方式二:打补丁

更新 ConfigMap 目前并不会触发相关 Pod 的滚动更新,可以通过修改 pod annotations 的方式强制触发重启滚动更新(会更新容器)

kubectl patch deployment hot-update --patch '{"spec": {"template": {"metadata": {"annotations": {"version/config": "20190411" }}}}}'

希望未来可以直接edit就实现热更新

自定义挂载权限及名称

volumes:
        - name: redisconf
          configMap:
            name: redis-conf
        - name: cmfromfile
          configMap:
            name: cmfromfile
            items:
              - key: redis.conf
                path: redis.conf.bak
              - key: redis.conf
                path: redis.conf.bak2
                mode: 0644 # 设置权限优先级高
            defaultMode: 0666  # 默认权限优先级

secret

Secret常用类型

*Opaque:通用型Secret,默认类型;
*kubernetes.io/service-account-token:作用于ServiceAccount,包含一个令牌,用于标识API服务账户;
*kubernetes.io/dockerconfigjson:下载私有仓库镜像使用的Secret,和宿主机的/root/.docker/config.json一致,宿主机登录后即可产生该文件;
*kubernetes.io/basic-auth:用于使用基本认证(账号密码)的Secret,可以使用Opaque取代;
*kubernetes.io/tls:用于存储HTTPS域名证书文件的Secret,可以被Ingress使用;
*bootstrap.kubernetes.io/token:一种简单的 bearer token,用于创建新集群或将新节点添加到现有集群,在集群安装时可用于自动颁发集群的证书。

Secret 存在意义

Secret 解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者 Pod Spec 中。Secret 可以以 Volume 或者环境变量的方式使用

Secret 有三种类型:
Service Account :用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod 的 /run/secrets/kubernetes.io/serviceaccount 目录中
Opaque :base64 编码格式的 Secret,用来存储密码、密钥等
kubernetes.io/dockerconfigjson :用来存储私有 docker 仓库的认证信息

创建secret的方式和类型

Service Account

创建证书资源消耗较大,且pods变化较大,因此使用SA类似于临时员工牌,可以共享使用,Service Account 用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod的 /run/secrets/kubernetes.io/serviceaccount 目录中

查看:
kubectl get pods -n kube-system
kubectl exec -it kube-flannel-ds-4jdrk -n kube-system -- /bin/sh
cd /run/secrets/kubernetes.io/serviceaccount

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

ca.crt:pod认证apiserver是否合法
namespace:标识当前的名称空间作用域
token:json web token实现单点认证,api认证当前的的pod使用

在安全认证和准入控制中会细讲

Opaque

base64编码格式的Secret,用来存储密码,密钥等,当Opaque secret挂在到pod之后,会自动解码

base64加密解密过程:
加密:
echo -n "user1" | base64

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

解密:
echo -n "dXNlcjE=" | base64 -d

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

使用secret挂载数据

1、以环境变量的形式挂载到pod容器中

创建volumn加密数据

通过base64进行加密
secret.yaml

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4=
  password: MWYyZDFlMmU2N2Rm
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl create -f secret.yaml

kubectl get secret

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

secret-env.yaml

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: nginx
    image: nginx
    env:
      - name: SECRET_USERNAME
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: username
      - name: SECRET_PASSWORD
        valueFrom:
          secretKeyRef:
            name: mysecret
            key: password

解析:secret.yaml中的name为mysecret,通过mysecret建立联系,将SECRET_USERNAME的key的值对应在secret.yaml创建的mysecret的username,SECRET_PASSWORD的key的值对应在secret.yaml创建的mysecret的password

应用:
kubectl apply -f secret-var.yaml
kubectl get secret

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl get pods

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

进入pod验证
kubectl exec -it mypod bash
echo $SECRET_USERNAME && echo $SECRET_PASSWORD

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

2、以数据卷方式挂载

secret-vol.yaml

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - name: nginx
    image: nginx
    volumeMounts:
    - name: foo
      mountPath: "/etc/foo"
      readOnly: true
  volumes:
  - name: foo
    secret:
      secretName: mysecret
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

防止metadata重复,先删除之前的变量挂载规则
kubectl delete -f secret-env.yaml

kubectl apply -f secret-vol.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl exec -it mypod -- bash
cd /etc/foo/
cat password
cat username

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubernetes.io/dockerconfigjson

如果有非常多的node,不可能每个node都要dockerlogin一遍,kubernetes.io/dockerconfigjson批量解决了私有docker仓库认证问题

dockerhub搭建请参考章节“Harbor – 企业级 Docker 私有仓库”:https://www.ljh.cool/4978.html

添加一台虚拟机,搭建dockerhub,hub域名设置为hub.ljh.com,并添加到hosts集群中

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

node以及master添加私有镜像仓库
"insecure-registries": ["hub.ljh.com"],

重启客户端docker
systemctl daemon-reload
systemctl restart docker

公开仓库
master作为推送镜像客户端:
docker login hub.ljh.com

推送镜像进行测试:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

启动一个deployment使用公有镜像仓库:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

hub.ljh.com/library/wangyanglinux/myapp:v1
因为访问级别是公开,所以必定会成功推送和下载

私有仓库使用:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

master推送下镜像:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

推送镜像并退出登陆:
docker tag wangyanglinux/myapp:v1 hub.ljh.com/secret/myapp:v1
docker push hub.ljh.com/secret/myapp:v1
docker logout

删除公有镜像deployment
kubectl delete deployments.apps hot-update

更新为私有仓库镜像:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

检测:
kubectl  get pods

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl describe pod hot-update-db8f6949d-2656p

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

因为是私有镜像仓库,所以解决方式有两种:
所有节点docker login
dockerconfigjson认证

dockerconfigjson实现
使用 Kuberctl 创建 docker 仓库认证的 secret:
kubectl create secret docker-registry myregistrykey --docker-server=hub.ljh.com --docker-username=admin --docker-password=Harbor12345 --docker-email=ljhxxxxxxxx@163.com

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

在创建 Pod 的时候,通过 `imagePullSecrets` 来引用刚创建的 `myregistrykey`

添加spec.imagePullSecrets:
imagePullSecrets:
- name: myregistrykey

vi hot-update-deploy.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

kubectl apply -f hot-update-deploy.yaml

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

k8s安全机制

概述

三”A“服务

Kubernetes 作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。API Server 是集群内部各个组件通信的中介,也是外部控制的入口。所以 Kubernetes 的安全机制基本就是围绕保护 API Server 来设计的。Kubernetes 使用了认证(Authentication)、鉴权(Authorization)、准入控制(Admission Control)三步来保证API Server的安全

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

是不是自己人、有没有权力、是不是有自己权限范围内内但不应该出现操作

Authentication(认证)

认证概念:

当请求发起方建立与 API Server 的安全连接后,进入请求的认证阶段(图中步骤 1)。认证的方式主要有:客户端证书、密码、普通 token、bootstrap token 和 JWT 认证 (主要用于 Service Account)。认证模块会检查请求头或者客户端证书的内容,我们可以同时配置一种或几种方式对请求进行认证。多种认证方式会被依次执行,只要一种方式通过,请求便得到合法认证。当所有方式都未通过时,会返回 401 状态码并中断请求。认证解决的问题是校验访问方是否合法并识别其身份。

两种类型认证:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

UA和 SA:用户账户UA是人机交互的过程。 是大家非常熟悉的用 kubectl 对 apiserver 的一个请求过程,k8s 无法直接操作这种资源,需要通过外部创建用户进行授权认证;服务账户SA是 Pod 中的业务逻辑与 apiserver 之间的交互。

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

k8s 常用的三种认证方式:

api-server配置查看下默认支持的认证方式

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

HTTP Token 认证:通过一个 Token 来识别合法用户
HTTP 为验证使用者身份,需要客户端向服务端提供一个可靠的身份信息,称之为Token,这个Token被放在HTTP Header头里,在Token里有信息来表明客户身份。Token通常是一个有一定长度的难以被篡改的字符串,每一个 Token 对应一个用户名存储在 API Server 能访问的文件中。当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token。在K8s中,每个Bearer Token都对应一个用户名,存储在API Server能访问的一个文件中(Static Token file)。客户端发起API调用请求时,需要在HTTPHeader里放入此Token,这样一来,API Server就能识别合法用户和非法用户了。SA认证方式也采用了与HTTP Bearer Token相同的实现方式。每个sa都对应一个Secret,在Secret中有一个加密的Token字段,这个Token字段就是Bearer Token。当API Server设置了启动参数–service-account-lookup=true,API Server就会验证Token是否在etcd中存在,若已从etcd中删除,则将注销容器中Token的有效性

HTTP Base 认证:通过 用户名+密码 的方式认证(1.19 版本已废弃)
用户名+:+密码 用 BASE64 算法进行编码后的字符串放在 HTTP Request 中的 Heather Authorization 域里发送给服务端,服务端收到后进行编码,获取用户名及密码

最严格的 HTTPS 证书认证:基于 CA 根证书签名的客户端身份认证方式(目前使用方式)(一般说是X509 证书认证

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

当两个组件进行双向 TLS 认证时,会涉及到下表中的相关文件:

名称作用例子
服务端证书包含服务端公钥和服务端身份信息通过根证书手动或者 kubeadm 自动生成的 API Server 服务端证书文件 apiserver.crt
服务器私钥主要用于 TLS 认证时进行数字签名,证明自己是服务端证书的拥有者通过根证书手动或者 kubeadm 生成的 API Server 服务端私钥文件 apiserver.key
客户端证书包含客户端公钥和客户端身份信息由同一个 CA 根证书签发的.crt 文件
客户端私钥主要用于 TLS 认证时进行数字签名,证明自己是客户端证书的拥有者由同一个 CA 根证书签发的.key 文件
服务端 CA 根证书签发服务端证书的 CA 根证书通过 openssl 等工具生成的 ca.crt 文件,并在服务端启动时进行指定
客户端 CA 根证书签发客户端证书的 CA 根证书通过 openssl 等工具生成的 ca.crt 文件,并在客户端启动时进行指定 (一般与服务端使用一个)

k8s 内部系统之间的两种类型的访问

1、Kubenetes 组件对 API Server 的访问:kubectl、Controller Manager、Scheduler、kubelet、kube-proxy

2、Kubernetes 管理的 Pod 对容器的访问:Pod(dashborad 也是以 Pod 形式运行)

kubectl对集群访问:kubeconfig

kubeconfig 文件包含集群参数(CA证书、API Server地址),客户端参数(上面生成的证书和私钥),集群 context 信息(集群名称、用户名)。Kubenetes 组件通过启动时指定不同的 kubeconfig 文件可以切换到不同的集群

需要认证的组件:

k8s集群中两套认证系统(apiserver和etcd):

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

ServiceAccount认证:

Pod中的容器访问API Server。因为Pod的创建、销毁是动态的,所以要为它手动生成证书就不可行了。Kubenetes使用了Service Account解决Pod 访问API Server的认证问题

Secret 与 SA 的关系
查看SA

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

Kubernetes 设计了一种资源对象叫做 Secret,分为两类,一种是用于 ServiceAccount 的 service-account-token, 另一种是用于保存用户自定义保密信息的 Opaque。ServiceAccount 中用到包含三个部分:Token、ca.crt、namespace

1、token是使用 API Server 私钥签名的 JWT。用于访问API Server时,Server端认证(<!--Json web token (JWT) , 是为了在网络应用环境间传递声明而执行的一种基于 JSON 的开放标准([(RFC 7519] ).该token被设计为紧凑且安全的,特别适用于分布式站点的单点登录(SSO)场景。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该 token 也可直接被用于认证,也可被加密-->)

2、ca.crt,根证书。用于Client端验证API Server发送的证书

3、namespace, 标识这个service-account-token的作用域名空间

一个SA可以对应多个pod

总结:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

Authorization(鉴权)

鉴权概念:

K8s API 访问请求必须包括请求者的用户名、请求的操作以及操作对象,该阶段就是对用户的操作合法性进行校验。如果现有策略声明用户具有完成请求操作的权限,则对请求进行授权。K8s 支持 ABAC 模式、RBAC 模式、Webhook 模式等多种授权模块。同样的,当多个授权模块被配置时,请求只要满足其中任意一种授权规则便会被放行,反之,API Server 会返回 403 状态码并终止该请求。鉴权是为了判别用户的操作权限范围。

上面认证过程,只是确认通信的双方都确认了对方是可信的,可以相互通信。而鉴权是确定请求方有哪些资源的权限。API Server 目前支持以下几种授权策略 (通过 API Server 的启动参数 “--authorization-mode” 设置)

AlwaysDeny:表示拒绝所有的请求,一般用于测试
AlwaysAllow:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略
ABAC(Attribute-Based Access Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制
Webhook:通过调用外部 REST 服务对用户进行授权
RBAC(Role-Based Access Control):基于角色的访问控制,现行默认规则(目前使用)

RBAC 授权模式

RBAC(Role-Based Access Control)基于角色的访问控制,在 Kubernetes 1.5 中引入,现行版本成为默认标准。相对其它访问控制方式,拥有以下优势:

  • 对集群中的资源和非资源均拥有完整的覆盖
  • 整个 RBAC 完全由几个 API 对象完成,同其它 API 对象一样,可以用 kubectl 或 API 进行操作
  • 可以在运行时进行调整,无需重启 API Server

细粒度权限划分

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

绑定方式:

RBAC 引入了 4 个新的顶级资源对象:Role、ClusterRole、RoleBinding、ClusterRoleBinding,4 种对象类型均可以通过 kubectl 与 API 操作

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

权限只能降级,不能升级

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

需要注意的是 Kubenetes 并不会提供用户管理,那么 User、Group、ServiceAccount 指定的用户又是从哪里来的呢? Kubenetes 组件(kubectl、kube-proxy)或是其他自定义的用户在向 CA 申请证书时,需要提供一个证书请求文件

{
  "CN": "admin",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "HangZhou",
      "L": "XS",
      "O": "system:masters",
      "OU": "System"
    }
  ]
}
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

API Server会把客户端证书的CN字段作为User,把names.O字段作为Group

kubelet 使用 TLS Bootstraping 认证时,API Server 可以使用 Bootstrap Tokens 或者 Token authentication file 验证 =token,无论哪一种,Kubenetes 都会为 token 绑定一个默认的 User 和 Group

Pod使用 ServiceAccount 认证时,service-account-token 中的 JWT 会保存 User 信息

有了用户信息,再创建一对角色/角色绑定(集群角色/集群角色绑定)资源对象,就可以完成权限绑定了

Role and ClusterRole

在 RBAC API 中,Role 表示一组规则权限,权限只会增加(累加权限),不存在一个资源一开始就有很多权限而通过 RBAC 对其进行减少的操作;Role 可以定义在一个 namespace 中,如果想要跨 namespace 则可以创建 ClusterRole

Role

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" indicates the core API group
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

ClusterRole

kubectl get clusterrole | grep -E 'cluster-admin|flannel'

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

ClusterRole 具有与 Role 相同的权限角色控制能力,不同的是 ClusterRole 是集群级别的,ClusterRole 可以用于:

集群级别的资源控制( 例如 node 访问权限 )
非资源型 endpoints( 例如 /health 访问 )
所有命名空间资源控制(例如 pods )

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  # "namespace" omitted since ClusterRoles are not namespaced
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]

聚合ClusterRole

如果你创建一个与某个已存在的聚合 ClusterRole 的标签选择算符匹配的 ClusterRole, 这一变化会触发新的规则被添加到聚合 ClusterRole 的操作。 下面的例子中,通过创建一个标签同样为 rbac.example.com/aggregate-to-monitoring: true 的 ClusterRole,新的规则可被添加到 "monitoring" ClusterRole 中

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: monitoring
aggregationRule:
  clusterRoleSelectors:
  - matchLabels:
      rbac.example.com/aggregate-to-monitoring: "true"
rules: [] # 控制面自动填充这里的规则

kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
  name: monitoring-endpoints
  labels:
    rbac.example.com/aggregate-to-monitoring: "true"
# 当你创建 "monitoring-endpoints" ClusterRole 时,
# 下面的规则会被添加到 "monitoring" ClusterRole 中
rules:
- apiGroups: [""]
  resources: ["services", "endpoints", "pods"]
  verbs: ["get", "list", "watch"]

Resources限制

Kubernetes 集群内一些资源一般以其名称字符串来表示,这些字符串一般会在 API 的 URL 地址中出现;同时某些资源也会包含子资源,例如 logs 资源就属于 pods 的子资源,API 中 URL 样例如下

如果要在 RBAC 授权模型中控制这些子资源的访问权限,可以通过‘/’分隔符来实现,以下是一个定义 pods 资资源 logs 访问权限的 Role 定义样例(常见动作类型:verbs: ["get", "list", "watch", "create", "update", "patch", "delete"])

GET /api/v1/namespaces/{namespace}/pods/{name}/log

kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  namespace: default
  name: pod-and-pod-logs-reader
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log"]
  verbs: ["get", "list"]

写的越详细,权限越小

RoleBinding and ClusterRoleBinding

RoleBinding:
RoleBinding 可以将角色中定义的权限授予用户或用户组,RoleBinding 包含一组权限列表(subjects),权限列表中包含有不同形式的待授予权限资源类型(users, groups, or service accounts);RoloBinding 同样包含对被 Bind 的 Role 引用;RoleBinding 适用于某个命名空间内授权,而 ClusterRoleBinding 适用于集群范围内的授权

将 default 命名空间的pod-readerRole 授予 jane 用户,此后 jane 用户在 default 命名空间中将具有pod-reader的权限

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: jane
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

ClusterRoleBinding

使用 ClusterRoleBinding 可以对整个集群中的所有命名空间资源权限进行授权;以下 ClusterRoleBinding 样例展示了授权 manager 组内所有用户在全部命名空间中对 secrets 进行访问

kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: read-secrets-global
subjects:
- kind: Group
  name: manager
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

降级:

RoleBinding 同样可以引用 ClusterRole 来对当前 namespace 内用户、用户组或 ServiceAccount 进行授权,这种操作允许集群管理员在整个集群内定义一些通用的 ClusterRole,然后在不同的 namespace 中使用 RoleBinding 来引用

RoleBinding 同样可以引用 ClusterRole 来对当前 namespace 内用户、用户组或 ServiceAccount 进行授权,这种操作允许集群管理员在整个集群内定义一些通用的 ClusterRole,然后在不同的 namespace 中使用 RoleBinding 来引用

以下 RoleBinding 引用了一个 ClusterRole,这个 ClusterRole 具有整个集群内对 secrets 的访问权限;但是其授权用户dave只能访问 development 空间中的 secrets(因为 RoleBinding 定义在 development 命名空间)

kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: read-secrets
  namespace: dev # This only grants permissions within the "development" namespace.
subjects:
- kind: User
  name: dave
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: ClusterRole
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

to Subjects

RoleBinding 和 ClusterRoleBinding 可以将 Role 绑定到 Subjects;Subjects 可以是 groups、users 或者 service accounts

Subjects 中 Users 使用字符串表示,它可以是一个普通的名字字符串,如 “alice”;也可以是 email 格式的邮箱地址,如 “wangyanglinux@163.com”;甚至是一组字符串形式的数字 ID 。但是 Users 的前缀 system: 是系统保留的,集群管理员应该确保普通用户不会使用这个前缀格式

Groups 书写格式与 Users 相同,都为一个字符串,并且没有特定的格式要求;同样 system: 前缀为系统保留

集群默认角色:

默认 ClusterRole默认 ClusterRoleBinding描述
cluster-adminsystem:masters 组允许超级用户在平台上的任何资源上执行所有操作。 当在 ClusterRoleBinding 中使用时,可以授权对集群中以及所有名字空间中的全部资源进行完全控制。 当在 RoleBinding 中使用时,可以授权控制角色绑定所在名字空间中的所有资源,包括名字空间本身。
admin允许管理员访问权限,旨在使用 RoleBinding 在名字空间内执行授权。如果在 RoleBinding 中使用,则可授予对名字空间中的大多数资源的读/写权限, 包括创建角色和角色绑定的能力。 此角色不允许对资源配额或者名字空间本身进行写操作。 此角色也不允许对 Kubernetes v1.22+ 创建的 EndpointSlices(或 Endpoints)进行写操作。 更多信息参阅 “EndpointSlices 和 Endpoints 写权限”小节
edit允许对名字空间的大多数对象进行读/写操作。此角色不允许查看或者修改角色或者角色绑定。 不过,此角色可以访问 Secret,以名字空间中任何 ServiceAccount 的身份运行 Pod, 所以可以用来了解名字空间内所有服务账户的 API 访问级别。 此角色也不允许对 Kubernetes v1.22+ 创建的 EndpointSlices(或 Endpoints)进行写操作。 更多信息参阅 “EndpointSlices 和 Endpoints 写操作”小节
view允许对名字空间的大多数对象有只读权限。 它不允许查看角色或角色绑定。此角色不允许查看 Secrets,因为读取 Secret 的内容意味着可以访问名字空间中 ServiceAccount 的凭据信息,进而允许利用名字空间中任何 ServiceAccount 的身份访问 API(这是一种特权提升)。

实践一:基于ServiceAcount

题目要求:

  • 创建一个名为deployment-clusterrole的clusterrole,该clusterrole只允许创建Deployment、Daemonset、Statefulset的create操作
  • 在名字为app-team1的namespace下创建一个名为cicd-token的serviceAccount,并且将上一步创建clusterrole的权限绑定到该serviceAccount

1、创建namespace和serviceAccount

[root@k8s-master01 examples]# kubectl  create ns app-team1
namespace/app-team1 created

[root@k8s-master01 examples]# kubectl  create sa cicd-token -n app-team1
serviceaccount/cicd-token created

2、创建clusterrole,授权动作和资源

[root@k8s-master01 ~]# kubectl create clusterrole/role NAME --verb=["get", "list", "watch", "create", "update", "patch", "delete"] --resource=deployments,statefulsets,daemonsets
clusterrole.rbac.authorization.k8s.io/deployment-clusterrole created

3、创建 rolebinding,绑定权限到SA

[root@k8s-master01 ~]# kubectl create rolebinding deployment-rolebinding --clusterrole=deployment-clusterrole --serviceaccount=app-team1:cicd-token -n app-team1

或者使用 yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: deployment-rolebinding
  namespace: app-team1
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: deployment-clusterrole
subjects:
- kind: ServiceAccount
  name: cicd-token
  namespace: app-team1

4、创建deployment,使用 SA

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  namespace: app-team1
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        imagePullPolicy: Always
        name: nginx
        resources: {}
        serviceAccountName: app-team1
      restartPolicy: Always

也可以直接使用命令授权

kubectl -n app-team1 set serviceaccount deployments nginx-deployment cicd-token

实践二:基于用户仅能管理 dev 空间

实施思路:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
  1. 用K8S CA签发客户端证书,也就是是用K8s根证书签发一个客户端证书。(客户端证书都是基于K8s根证书来生成的,根证书在搭建K8s集群时候就被创建了,且创建时生成的ca-config配置文件是被删除了,根证书位置在/etc/kubernetes/pki目录下,一个是在K8s层面上的根证书,一个是etcd是用的),然后生成客户端证书请求文件,使用cfssl工具来生成客户端证书
  2. 生成kubeconfig授权文件(集群信息、客户端信息、上下文信息)
  3. 创建RBAC权限策略
  4. 把kubeconfig文件给小弟就可以使用了。

证书生成过程:通过ca.crt和ca.key 以及devuser.json生成devuser-key.pem和devuser.pem -> 通过注入主机IP、devuser-key.pem和devuser.pem

cd /etc/kubernetes/pki/

vi devuser.json

{
  "CN": "devuser",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "ST": "BeiJing",
      "L": "BeiJing",
      "O": "k8s",
      "OU": "System"
    }
  ]
}

下载证书生成工具

wget https://pkg.cfssl.org/R1.2/cfssl_linux-amd64
mv cfssl_linux-amd64 /usr/local/bin/cfssl

wget https://pkg.cfssl.org/R1.2/cfssljson_linux-amd64
mv cfssljson_linux-amd64 /usr/local/bin/cfssljson

wget https://pkg.cfssl.org/R1.2/cfssl-certinfo_linux-amd64
mv cfssl-certinfo_linux-amd64 /usr/local/bin/cfssl-certinfo

chmod +x /usr/local/bin/cfssl*

通过ca生成 devuser 的证书和私钥
cfssl gencert -ca=ca.crt -ca-key=ca.key -profile=kubernetes devuser.json | cfssljson -bare devuser

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

设置集群参数,生成 devuser.kubeconfig

export KUBE_APISERVER="https://192.168.1.10:6443"

kubectl config set-cluster kubernetes \
--certificate-authority=ca.crt \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=devuser.kubeconfig
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

设置客户端认证参数

kubectl config set-credentials devuser \
--client-certificate=devuser.pem \
--client-key=devuser-key.pem \
--embed-certs=true \
--kubeconfig=devuser.kubeconfig

设置上下文参数

kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=devuser \
--namespace=dev \
--kubeconfig=devuser.kubeconfig
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

添加命名空间:
kubectl create ns dev

创建角色,绑定权限到用户
kubectl config use-context kubernetes --kubeconfig=devuser.kubeconfig
kubectl create rolebinding devuser-admin-binding --clusterrole=admin --user=devuser --namespace=dev

上面那条命令类似于yaml方式创建,可以导出查看下
kubectl create rolebinding devuser-admin-binding --clusterrole=admin --user=devuser --namespace=dev --dry-run -o yaml

apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: devuser-admin-binding
  namespace: dev
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: admin
subjects:
- apiGroup: rbac.authorization.k8s.io
  kind: User
  name: devuser
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

使用普通用户去登陆devuser

useradd devuser
passwd devuser
mkdir -p /home/devuser/.kube/
cp /etc/kubernetes/pki/devuser.kubeconfig /home/devuser/.kube/config
chown devuser:devuser -R /home/devuser/.kube

登陆devuser用户:

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

实践三:基于不同用户配置不同权限

如何进行授权?

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

如何进行用户管理?

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

在权限上可以选择创建多个ClusterRole分别具有不同权限,这样角色可以复用授权可以绑定到任何名称空间下,对于用户管理来说,在数据库中会有一个单独的user表,同理,需要分配一个单独的命名空间kube-users来作为用户的统一进行管理,所有用户都需要切换名称空间和查看pod权限,使用ClusterRileBinding绑定,然后使用RoleBinding将clusterrole和对应需要授权的user进行降级绑定操作,将具体的角色绑定到具体的名称空间进行授权

RBAC企业实战:不同用户不同权限

需求:

  • 1.    用户ljh可以查看default、kube-system下Pod的日志
  • 2.    用户howell可以在default下的Pod中执行命令,并且可以删除Pod

授权四个ClusterRole

# 命名空间切换和pod查看权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: namespace-readonly
rules:
- apiGroups:
  - ""
  resources:
  - namespaces
  verbs:
  - get
  - list
  - watch
- apiGroups:
  - metrics.k8s.io
  resources:
  - pods
  verbs:
  - get
  - list
  - watch

# 删除权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pod-delete
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - list
  - delete

# 执行命令权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pod-exec
rules:
- apiGroups:
  - ""
  resources:
  - pods
  verbs:
  - get
  - list
- apiGroups:
  - ""
  resources:
  - pods/exec
  verbs:
  - create

# 查看日志权限
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pod-log
rules:
- apiGroups:
  - ""
  resources:
  - pods
  - pods/log
  verbs:
  - get
  - list
  - watch

创建用户管理命名空间:

kubectl create ns kube-users

将角色授予kube-users名字空间中查看查看所有名称空间和pod权限(将角色授予名字空间中所有服务账户,如果你想要名字空间中所有应用都具有某角色,无论它们使用的什么服务账户, 可以将角色授予该名字空间的服务账户组。):

kubectl create clusterrolebinding namespace-readonly --clusterrole=namespace-readonly  --serviceaccount=system:serviceaccounts:kube-users

创建用户:

kubectl create sa ljh howell -n kube-users

绑定权限:

# 用户ljh可以查看default、kube-system下Pod的日志
kubectl create rolebinding ljh-pod-log \
--clusterrole=pod-log   --serviceaccount=kube-users:ljh --namespace=kube-system
kubectl create rolebinding ljh-pod-log \
--clusterrole=pod-log   --serviceaccount=kube-users:ljh --namespace=default

# 用户howell可以在default下的Pod中执行命令,并且可以删除Pod
kubectl create rolebinding howell-pod-exec \
--clusterrole=pod-exec   --serviceaccount=kube-users:howell --namespace=default
kubectl create rolebinding howell-pod-delete \
--clusterrole=pod-delete   --serviceaccount=kube-users:howell --namespace=default

准入控制

准入控制概念:

准入控制是请求操作被持久化到 etcd 之前的 “拦截器”。准入控制模块由多个 “准入控制器” 构成,“准入控制器” 就是一段自定义代码,它们能够在请求对 K8s 创建、修改、删除或者连接一个 K8s 对象时生效。官方自带了 30 多个准入控制器可供使用,同时支持用户扩展。准入控制器的作用往往是检查请求的规范性或者赋予一些默认信息。例如,我们在创建一个 pod 时,准入控制器会检查提交的信息是否符合 pod 资源的规范,并对请求中没有明确规定的字段,设置对应的默认值填充到请求中。与前两个阶段不同的是,只要有一个 “准入校验” 逻辑未通过,那么请求就会被拒绝。若请求仅仅是读取一个对象,“准入控制器” 将不会生效。准入控制作用于 K8s 中的对象,通过校验规范和默认值的设置,能够保证系统的安全可靠。

准入控制是API Server的插件集合,通过添加不同的插件,实现额外的准入控制规则。甚至于API Server的一些主要的功能都需要通过 Admission Controllers 实现,比如 ServiceAccount

官方文档上有一份针对不同版本的准入控制器推荐列表,其中最新的 1.14  的推荐列表是:NamespaceLifecycle,LimitRanger,ServiceAccount,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota

列举几个插件的功能:NamespaceLifecycle: 防止在不存在的 namespace 上创建对象,防止删除系统预置 namespace,删除 namespace 时,连带删除它的所有资源对象。

补充:持久化阶段。当我们的请求通过了前面三个阶段的校验,它会被转换为一个 K8s 对象相应的变更请求,最终持久化到 etcd 中。

k8s常见的资源管理方式:计算资源管理(Compute Resources)、资源的配置范围管理(LimitRange)和资源的配额管理(Resource Quotas)

  • 计算资源管理(Compute Resources): 为Pod中的容器指定使用的计算资源(CPU和内存)。
  • 资源的配置范围管理(LimitRange):可以对集群内Request和Limits的配置做一个全局的统一的限制,相当于批量设置了某一个范围内(某个命名空间)的Pod的资源使用限制。
  • 资源的配额管理(Resource Quotas):可以为每一个命名空间(namespace)提供一个总体的资源使用限制,通过它可以限制命名空间中某个类型的对象的总数目上限,也可以设置命名空间中Pod可以使用到的计算资源的总上限。资源的配额管理有效解决了多用户或多个团队公用一个k8s集群时资源有效分配的问题。

资源配额ResourceQuota

资源配额是限制某个命名空间对资源使用的一个总量限制,比如内存、CPU、Pod数量等。

资源配额的重要性

非k8s管理员的项目组都会在自己的名称空间下创建多个资源,并且没用的资源没有回收,产生大量废弃资源,给指定的项目组分配指定的资源

ResourceQuota是Kubernetes中的一种资源限制机制,它用于对命名空间内的资源使用进行限制和配额控制。它的出现背景是为了解决以下问题:

  • 多租户隔离:在Kubernetes中,多个团队或用户可以在同一个集群中共享资源。为了确保每个命名空间或用户不会无限制地使用资源,需要一种机制来实现资源的隔离和限制。ResourceQuota提供了这样的能力,可以为每个命名空间设置资源使用的上限。
  • 防止资源滥用:资源滥用可能导致整个集群的性能下降或崩溃。通过设置ResourceQuota,可以限制每个命名空间或用户可以使用的资源数量,以防止某个应用程序或用户抢占过多的资源,从而保护集群的稳定性和可靠性。
  • 预测和控制成本:在云计算环境中,资源的使用与成本直接相关。通过设置ResourceQuota,可以根据团队或用户的需求和预算来限制资源使用,从而更好地预测和控制成本。
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

用户可以对给定命名空间下的可被请求的 计算资源总量进行限制。其中配额机制所支持的资源类型:

资源名称描述
limits.cpu所有非终止状态的 Pod,其 CPU 限额总量不能超过该值。
limits.memory所有非终止状态的 Pod,其内存限额总量不能超过该值。
requests.cpu所有非终止状态的 Pod,其 CPU 需求总量不能超过该值。
requests.memory所有非终止状态的 Pod,其内存需求总量不能超过该值。
hugepages-<size>对于所有非终止状态的 Pod,针对指定尺寸的巨页请求总数不能超过此值。
cpu与 requests.cpu 相同。
memory与 requests.memory 相同。

对有限的一组资源上实施一般性的对象数量配额也是可能的。

资源名称描述
configmaps在该命名空间中允许存在的 ConfigMap 总数上限。
persistentvolumeclaims在该命名空间中允许存在的 PVC的总数上限。
pods在该命名空间中允许存在的非终止状态的 Pod 总数上限。Pod 终止状态等价于 Pod 的 .status.phase in (Failed, Succeeded) 为真。
replicationcontrollers在该命名空间中允许存在的 ReplicationController 总数上限。
resourcequotas在该命名空间中允许存在的 ResourceQuota 总数上限。
services在该命名空间中允许存在的 Service 总数上限。
services.loadbalancers在该命名空间中允许存在的 LoadBalancer 类型的 Service 总数上限。
services.nodeports在该命名空间中允许存在的 NodePort 类型的 Service 总数上限。
secrets在该命名空间中允许存在的 Secret 总数上限。

ResourceQuota配置样例展示:

apiVersion: v1
kind: ResourceQuota
metadata:
  name: resource-test
  labels:
    app: resourcequota
spec:
  hard:
    pods: 50 # 限制最多启动Pod的个数
    requests.cpu: 0.5 # 所有非终止状态的 Pod,其 CPU 限额总量不能超过该值
    requests.memory: 512Mi # 所有非终止状态的 Pod,其内存限额总量不能超过该值。
    limits.cpu: 5 # 所有非终止状态的 Pod,其 CPU 需求总量不能超过该值。
    limits.memory: 16Gi # 所有非终止状态的 Pod,其内存需求总量不能超过该值。
    requests.storage: 40Gi # 所有 PVC,存储资源的需求总量不能超过该值。
    persistentvolumeclaims: 20 # 在该命名空间中所允许的 PVC 总量。
    replicationcontrollers: 20
    configmaps: 20 
    secrets: 20
    services: 50 # 限制service数量
    services.loadbalancers: "2" # 限制负载均衡数
    services.nodeports: "10" # 设置端口数量

ResourceQuota作用于Pod,并且有命名空间限制

K8s资源限制LimitRange

只有ResourceQuota是不够的

默认情况下, Kubernetes 集群上的容器运行使用的计算资源没有限制。 使用 Kubernetes 资源配额, 管理员(也称为 集群操作者)可以在一个指定的命名空间内限制集群资源的使用与创建。 在命名空间中,一个 Pod最多能够使用命名空间的资源配额所定义的 CPU 和内存用量。 作为集群操作者或命名空间级的管理员,你可能也会担心如何确保一个 Pod 不会垄断命名空间内所有可用的资源。

一个 LimitRange(限制范围) 对象提供的限制能够做到:

  • 在一个命名空间中实施对每个 Pod 或 Container 最小和最大的资源使用量的限制。
  • 在一个命名空间中实施对每个 PersistentVolumeClaim能申请的最小和最大的存储空间大小的限制。
  • 在一个命名空间中实施对一种资源的申请值和限制值的比值的控制。
  • 设置一个命名空间中对计算资源的默认申请/限制值,并且自动的在运行时注入到多个 Container 中。

LimitRange之所以出现,一般只为应对两种常见场景:

场景一:假如我们通过ResourceQuota只限制了内存和CPU,没有限制Pod数量的情况,在CPU和内存为0时无限制地创建Pod,从而造成无法统计的情况。

场景二:假如一个Namespace分配了16核、64GB的空间,之后创建一个申请了requests.cpu为16、requests.memory为64GB的容器,那么单个Pod就能把整个Namespace的资源全部占用。

假如我们只限制内存和CPU,但是在pod中没有配置resources,则默认CPU和内存使用量还被认为0

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

LimitRange做了什么

pod中即使没有任何配置,可以为每个pod默认添加资源配置参数

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

LimitRange配置示例:默认的requests和limits

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-mem-limit-range
spec:
  limits:
  - default: # 默认limits配置
      cpu: 1
      memory: 512Mi
    defaultRequest: # 默认requests配置
      cpu: 0.5
      memory: 256Mi
    type: Container

限制内存和CPU requests和limits的范围

apiVersion: v1
kind: LimitRange
metadata:
  name: cpu-min-max-demo-lr
spec:
  limits:
  - max: # 内存CPU的最大配置
      cpu: "800m"
      memory: 1Gi
    min: # 内存CPU的最小配置
      cpu: "200m"
      memory: 500Mi
    type: Container

限制存储范围

apiVersion: v1
kind: LimitRange
metadata:
  name: storagelimits
spec:
  limits:
  - type: PersistentVolumeClaim
    max: # 最大PVC的空间
      storage: 2Gi
    min: # 最小PVC的空间
      storage: 1Gi

注意:在配置了requests和limits参数时,会以自行配置的为准(如果没有超过LimitRanger的最大、最小限制的话)。如果配置了limits而没有配置requests,那么requests的默认值将被设置成limits配置的参数。

K8s服务质量QoS

虽然我们可以给没有配置requests和limits的Pod添加默认值,也可以限制资源请求的范围,但是在使用Kubernetes部署时,应用的部署和更新都会经过一系列的调度策略将应用部署在最合适的节点上,但是随着时间的推移,当时“最优”的节点可能已经不再是最佳选择,因为在该服务器上别的应用或者其他管理员部署的应用可能忘记了配置资源限制,所以在日积月累的消耗中,宿主机一些不可压缩的资源(比如内存、磁盘)的使用率将达到最高峰。假如内存达到最高峰时会引起OOMKilled故障,此时Kubelet会根据某些策略重启上面的容器用来避免宿主机宕机引来的风险,但是重启容器难免会带来服务中断的现象,如果重启的是比较重要的业务应用,这将是一个非常不好的体验。QoS就应运而生,可以保证在系统资源不够的情况下尽量保证一些比较重要的Pod不被杀死。

Resources配置的重要性

假如node01已经将资源占满,继续创建一个Deployment,如果没有配置Resources则会导致资源继续分配在node01上,导致内存溢出

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))

Resources也并非万能

node01如果只有4c8g,只能满足pod的requests配置,当达到性能上限,就会杀一些pod,那么被杀死的pod顺序是什么呢

04-k8s运行机制(controller(RC、RS、Deployment、DaemonSet、statefulset、Job、CronJob),service,configmap,secret,安全机制(认证、鉴权、准入控制(本篇准入控制器只讲资源配额、资源配置范围和 QoS,更多详见k8s高级安全篇文章)))
  • Guaranteed:最高服务质量,当宿主机内存不够时,会先kill掉QoS为BestEffort和Burstable的Pod,如果内存还是不够,才会kill掉QoS为Guaranteed,该级别Pod的资源占用量一般比较明确,即requests的cpu和memory和limits的cpu和memory配置的一致
  • Burstable: 服务质量低于Guaranteed,当宿主机内存不够时,会先kill掉QoS为BestEffort的Pod,如果内存还是不够之后就会kill掉QoS级别为Burstable的Pod,用来保证QoS质量为Guaranteed的Pod,该级别Pod一般知道最小资源使用量,但是当机器资源充足时,还是想尽可能的使用更多的资源,即limits字段的cpu和memory大于requests的cpu和memory的配置。
  • BestEffort:尽力而为,当宿主机内存不够时,首先kill的就是该QoS的Pod,用以保证Burstable和Guaranteed级别的Pod正常运行。

示例1:实现QoS为Guaranteed的Pod

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
        cpu: "700m"
      requests:
        memory: "200Mi"
        cpu: "700m"

①Pod中的每个容器必须指定requests.memory和limits.memory,并且两者需要相等;
②Pod中的每个容器必须指定requests.cpu和limits.cpu,并且两者需要相等。

示例2:实现QoS为Burstable的Pod

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-2
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-2-ctr
    image: nginx
    resources:
      limits:
        memory: "200Mi"
      requests:
        memory: "100Mi"

①Pod不符合Guaranteed的配置要求;
②Pod中至少有一个容器配置了requests.cpu或requests.memory。

示例3:实现QoS为BestEffort的Pod

apiVersion: v1
kind: Pod
metadata:
  name: qos-demo-3
  namespace: qos-example
spec:
  containers:
  - name: qos-demo-3-ctr
    image: nginx

①不设置resources参数

发布者:LJH,转发请注明出处:https://www.ljh.cool/8084.html

(0)
上一篇 2022年9月21日 下午7:04
下一篇 2022年9月25日 上午1:40

相关推荐

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注