Kubernetes学习笔记_尚硅谷

 

 

一、K8s介绍

 

k8s是一个编排容器的工具,其实也是管理应用的全生命周期的一个工具,从创建应用,应用的部署,应用提供服务,扩容缩容应用,应用更新,都非常的方便,而且可以做到故障自愈。

 

1.1 发展经历

 

基础设施级服务 iaas :阿里云 

平台设施级服务 paas :新浪云

软件设施级服务 saas :Office365

 

资源管理器:

前生:

Apache:MESOS - 分布式系统内核 、分布式资源管理框架 2019-05 Twitter>k8s

docker:SWARM 集群,轻量 2019-07 阿里云宣布 Docker Swarm集群框架从阿里云选择框架剔除

今世:

google:kubernetes,10年google容器基础框架borg ,容器火了以后,Google使用GO语言参考Borg设计思路开发出K8s

  特点:轻量级,基于GO语言,消耗资源小

     开源

     弹性伸缩

     负载均衡:LVS(IPVS)

 

1.2 知识图谱

 

图片原图

 


K8s框架

K8s关键字含义

基础概念 什么是Pod 控制器类型 

Pod概念  最小的封装集合,一个Pod会封装多个容器,达到一个子节点的运行环境,K8s管理的最小单位

网络通讯模式

K8s安装:构建K8s集群

资源清单:资源 掌握资源清单的语法 编写Pod 掌握Pod的生命周期***

Pod控制器:掌握各种控制器的特点以及使用定义方式

服务发现:SVC原理及其构建方式

存储:掌握多种存储类型的特点 并且能够在不同环境中选择合适的存储方案(有自己的见解)

  服务分类

    有状态服务:DBMS

    无状态服务:LVS APACHE

   高可用集群副本数最好是>=3的奇数

调度器:掌握调度器原理,能够根据要求把Pod定义到想要的节点运行

安全:集群的认证 鉴权 访问控制 原理及其流程 集群安全机制

HELM:类Linux yum  掌握HELM原理   HELM模板自定义  HELM部署一些常用插件

运维:CICD构建 POD特殊的创建管理方式  修改Kubeadm达到证书可用期限10年  构建高可用K8S集群

 

1.3 组件说明

 

Borg架构:

 

K8s架构:

  

APISERVER:所有服务访问统一入口
CrontrollerManager:维持副本期望数目
Scheduler::负责介绍任务,选择合适的节点进行分配任务
ETCD:键值对数据库 储存K8S集群所有重要信息(持久化),协助分布式集群的正常运转。
Kubelet:直接跟容器引擎交互实现容器的生命周期管理
Kube-proxy:负责写入规则至 IPTABLES、IPVS 实现服务映射访问的
COREDNS:可以为集群中的SVC创建一个域名IP的对应关系解析
DASHBOARD:给 K8S 集群提供一个 B/S 结构访问体系
INGRESS CONTROLLER:官方只能实现四层代理,INGRESS 可以实现七层代理
FEDERATION:提供一个可以跨集群中心多K8S统一管理功能
PROMETHEUS:提供K8S集群的监控能力
ELK:提供 K8S 集群日志统一分析介入平台

 

推荐在 Kubernetes 集群中使用 Etcd v3,v2 版本已在 Kubernetes v1.11 中弃用

 

etcd 的官方将它定位成一个可信赖的分布式键值存储服务,它能够为整个分布式集群存储一些关键数据,协助分布式集群的正常运转

 

二、基础概念

 

2.1 Pod概念

 

Pod类型:

 自主式Pod:(不是被控制器管理的Pod):死亡后不会被拉起来,也不会有人创建新的Pod

 

 

 每个Pod里运行着一个特殊的被称为Pause容器,其他容器为业务容器,这些业务容器共享Pause容器的网络栈和Volume挂载卷,因此他们之间通信和数据交互更为高效。

 在设计时我们可以充分利用这一特性将一组密切相关的服务进程放入同一个Pod中,同一个Pod里的容器之间仅需通过localhost就能互相通信。

 

 控制器管理的Pod

 

POD控制器类型:

 ReplicationController & ReplicaSet & Deployment

 ReplicationController :确保期望值,少了就创建新的Pod替代,多了会自动回收。

  新版本的K8S种建议使用ReplicaSet来取代ReplicationControlle,没有本质不同,但只有RS支持集合式的selector(每个Pod有不同标签,RS操作Pod可以按照标签条件操作)

  虽然RS可以独立,但是一般还是建议使用Deployment来自动管理RS,这样就无需担心跟其他机制不兼容问题(如RS不支持rolling-update(滚动更新),但是D支持(D本身并不支持Pod创建),所以这俩要一起运行。)

 Deployment(ReplicaSet)
  Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的 ReplicationController 来方便的管理应用。典型的应用场景包括:
   * 定义 Deployment 来创建 Pod 和 ReplicaSet
   * 滚动升级和回滚应用
   * 扩容和缩容
   * 暂停和继续 Deployment

 滚动更新:

  

  更新V1到V2,新建个RS然后创建1个V2,删除1个V1

  

  >直至

  

  达到滚动更新,此时RS,停用、保留,可以回滚>

  

  RS启用,开始回滚老版V1以此类推。

 

 HPA(HorizontalPodAutoScale)根据利用率平滑扩展仅适用于D和RS,在V1版本中支持根据Pod的利用率扩容,在vlalpha版本中,支持根据内存和用户自定义的metric扩缩容。

 

  HPA基于RS定义,并且监控V2Pod的资源利用率

  

 

  当符合条件后,会创建Pod

  

 

  每次创建后判断条件,符合后继续创建,直到最大值。使用率小就回收,直到最小值,实现水平自动扩展(弹性伸缩)。

 

 StatefulSet:为了解决有状态服务的问题(Deployment和RS是为了解决无状态服务而设计(Docker主要也是)),其场景包括:

  *稳定的持久化存储,即有个Pod死了,重新调度回来以后还是能访问到相同的持久化数据(数据不丢失),基于PVC实现。

  *稳定的网络标识,即重新调度后的PodName和HostName不变,基于Headless Service(即没有Cluster IP的Service)来实现

  *有序部署,有序扩展,按照顺序进行M>A>N(从0到N-1,在下一个Pod运行前,之前所有的Pod必须是running和Ready状态),基于init containers来实现。

  *有序收缩,有序删除(即从N-1到0) Nagix > Apache >Mysql

 

 DaemonSet

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

  典型用法:

    *运行集群存储 daemon,例如在每个Node上运行glusterd、ceph

    *在每个Node上运行日志收集daemon,例如fluentd、logstash

    *在每个Node上运行监控daemon,例如Prometheus Node Exporter、Zabix Agent 都可以封装在DaemonSet中在每个Node上运行,帮我们收集数据。

 

 Job,Cronjob

  job负责批处理任务,即仅执行一次的任务,他保证批处理任务的一个或者多个Pod成功结束。(比如要备份数据库,备份代码可以放到统一Pod里,再放到Job里执行,与Linux直接运行不同点是是封装好的Job可以重复利用,并且脚本执行异常退出可以重复执行,并且可以设置正常退出次数才算Job执行成功)

  Cronjob管理基于时间的Job,即

    *在给定时间点运行一次

    *周期性地在给定时间点运行

 

 服务发现:

  Client访问service的IP和端口,使用RR(Round ribbon轮训)等算法间接访问到Pod。

  

 

 

2.2 网络通讯方式

 

网络通讯模式:

 Kubernetes的网络模型假定了所有Pod都在一个可以直接连通的扁平的网络空间中(都可以通过IP直接到达,其实底层有很多转换机制),这在GCE(Google Compute Engine) 里面是现成的网络模型,K8S假定这个网络已存在。而在私有云搭建K8S集群,就不能假定这个网络已经存在了。我们需要自己实现这个网络假设,将不同节点上的Docker容器之间互相访问先打通,然后再运行K8S。

 同一个Pod内的多个容器间:lo     pause

 各Pod之间的通讯: Overlay Network

 Pod与Service之间的通讯:各节点的Iptables规则,新版本支持LVS 转发上限、效率更高

 

网络解决方案K8S+Flannel 

  Flannel是CoreOS团队针对K8S设计的一个网络规划服务,简单来说他,他的功能是让集群中的不同节点主机创建的Docker容器具有全集群唯一的虚拟IP主机。而且它还能在这些IP之间建立一个覆盖网络(Overlay Network),通过这个覆盖网络,将数据包原封不动地传递到目标容器内

  

 ETCD之Flannel提供说明:

  >存储管理Flannel可分配的IP地址段资源

  >监控ETCD中每个Pod的实际地址,并在内存中建立维护Pod节点路由表

 

不同情况下网络通信方式

 同一个 Pod 内部通讯:同一个 Pod 共享同一个网络命名空间,共享同一个 Linux 协议栈

 Pod1 至 Pod2

  > Pod1 与 Pod2 不在同一台主机,Pod的地址是与docker0在同一个网段的,但docker0网段与宿主机网卡是两个完全不同的IP网段,并且不同Node之间的通信只能通过宿主机的物理网卡进行。将Pod的IP和所在Node的IP关联起来,通过 这个关联让Pod可以互相访问

  > Pod1 与 Pod2 在同一台机器,由 Docker0 网桥直接转发请求至 Pod2,不需要经过 Flannel

 Pod 至 Service 的网络:目前基于性能考虑,全部为 iptables 维护和转发

 Pod 到外网:Pod 向外网发送请求,查找路由表, 转发数据包到宿主机的网卡,宿主网卡完成路由选择后,iptables执 行Masquerade,把源 IP 更改为宿主网卡的 IP,然后向外网服务器发送请求

 外网访问 Pod:Service

 

 组件通讯示意图

  

 

三、集群安装

 

K8S构建1台master2台node+Harbor_笔记 :https://www.cnblogs.com/yyq1/p/13991453.html

 

四、资源清单

 

资源:K8s 中所有的内容都抽象为资源, 资源实例化之后,叫做对象

 

4.1 K8S 中的资源

名称空间级别
 工作负载型资源( workload ): Pod、ReplicaSet、Deployment、StatefulSet、DaemonSet、Job、 CronJob ( ReplicationController 在 v1.11 版本被废弃 )

 服务发现及负载均衡型资源( ServiceDiscovery LoadBalance ): Service、Ingress、...

 配置与存储型资源:Volume( 存储卷 )、CSI( 容器存储接口,可以扩展各种各样的第三方存储卷 )
 特殊类型的存储卷:ConfigMap( 当配置中心来使用的资源类型 )、Secret(保存敏感数据)、 DownwardAPI(把外部环境中的信息输出给容器)

集群级资源:Namespace、Node、Role、ClusterRole、RoleBinding、ClusterRoleBinding

元数据型资源:(根据某些指标进行操作):HPA、PodTemplate、LimitRange 

 

4.2 资源清单

 

资源清单含义:

  在 k8s 中,一般使用 yaml 格式的文件来创建符合我们预期期望的 pod ,这样的 yaml 文件我们一般 称为资源清单

 

简单说明

  是一个可读性高,用来表达数据序列的格式。YAML 的意思其实是:仍是一种标记语言,但为了强调这种语言以数 据做为中心,而不是以标记语言为重点


基本语法
  缩进时不允许使用Tab键,只允许使用空格

  缩进的空格数目不重要,只要相同层级的元素左侧对齐即可

  #标识注释,从这个字符一直到行尾,都会被解释器忽略

 

YAML 支持的数据结构
  对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)

  数组:一组按次序排列的值,又称为序列(sequence) / 列表 (list)

  纯量(scalars):单个的、不可再分的值

 

对象类型:对象的一组键值对,使用冒号结构表示

  name: Steve

  age: 18

 

Yaml 也允许另一种写法,将所有键值对写成一个行内对象

  hash: { name: Steve, age: 18 }

 

数组类型:一组连词线开头的行,构成一个数组

  animal

    - Cat

    - Dog

 

数组也可以采用行内表示法

  animal: [Cat, Dog]

 

复合结构:对象和数组可以结合使用,形成复合结构

  1 languages:

  2 - Ruby

  3 - Perl

  4 - Python

  5 websites:

  6 YAML: yaml.org

  7 Ruby: ruby-lang.org

  8 Python: python.org

  9 Perl: use.perl.org

  

纯量:纯量是最基本的、不可再分的值。以下数据类型都属于纯量

1 字符串 布尔值 整数 浮点数 Null
2 时间 日期

数值直接以字面量的形式表示 number: 12.30
布尔值用true和false表示 isSet: true null用 ~ 表示 parent: ~ 时间采用 ISO8601 格式 iso8601: 2001-12-14t21:59:43.10-05:00 日期采用复合 iso8601 格式的年、月、日表示 date: 1976-07-31 YAML 允许使用两个感叹号,强制转换数据类型 e: !!str 123 f: !!str true

  

字符串

字符串默认不使用引号表示

str: 这是一行字符串

  

如果字符串之中包含空格或特殊字符,需要放在引号之中

  str: '内容: 字符串'

   

单引号和双引号都可以使用,双引号不会对特殊字符转义

s1: '内容\n字符串'
s2: "内容\n字符串"

  

单引号之中如果还有单引号,必须连续使用两个单引号转义

str: 'labor''s day'

   

字符串可以写成多行,从第二行开始,必须有一个单空格缩进。换行符会被转为 空格

str: 这是一段

 多行

 字符串

   

多行字符串可以使用|保留换行符,也可以使用>折叠换行

this:|

Foo

Bar

that: >

Foo

Bar

   

+ 表示保留文字块末尾的换行,- 表示删除字符串末尾的换行

s1: |

 Foo

s2: |+

 Foo


s3: |-

 Foo

   

4.3 常用字段的解释

  参考资料: https://www.cnblogs.com/panwenbin-logs/p/9895953.html

  必须存在的属性:

   主要对象:

  

额外的参数项:

 

kubectl explain pod  

查看pod模板

 

 

继续查看

kubectl explain pod.spec

  

kubectl explain pod.spec.containers

 

 

创建一个简单的yaml模板

vi pod.yaml

  

这里使用相同镜像,默认情况会端口冲突

使用yaml文件创建pod:

kubectl apply -f pod.yaml

  

已经创建

查看运行情况 

  

发现容器报错,不断重启,已经重启4次了

排查问题:

kubectl describe pod mynginx-pod

第二个 test容器报错了

 

查看test容器

kubectl log mynginx-pod -c test
kubectl logs mynginx-pod -c test

指定查看pod和容器日志

80端口被占用

 

删除 test容器:

vim pod.yaml

 

 

kubectl delete pod mynginx-pod
kubectl get pod
kubectl apply -f pod.yaml
kubectl get pod 

 

 

4.4 容器生命周期

 

 

 

Init 容器
  Pod 能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个先于应用容器启动的 Init 容器

  Init 容器与普通的容器非常像,除了如下两点:

    Ø Init 容器总是运行到成功完成为止

    Ø 每个 Init 容器都必须在下一个 Init 容器启动之前成功完成

  如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该 Pod,直到 Init 容器成功为止。然而,

  如果 Pod 对应的 restartPolicy 为 Never,它不会重新启动

 

Init 容器的作用

  因为 Init 容器具有与应用程序容器分离的单独镜像,所以它们的启动相关代码具有如下优势:

    Ø 它们可以包含并运行实用工具,但是出于安全考虑,是不建议在应用程序容器镜像中包含这 些实用工具的

    Ø 它们可以包含使用工具和定制化代码来安装,但是不能出现在应用程序镜像中。例如,创建 镜像没必要 FROM 另一个镜像,只需要在安装过程中使用类似 sed、 awk、 python 或 dig 这样的工具。

    Ø 应用程序镜像可以分离出创建和部署的角色,而没有必要联合它们构建一个单独的镜像。

    Ø Init 容器使用 Linux Namespace,所以相对应用程序容器来说具有不同的文件系统视图。因 此,它们能够具有访问 Secret 的权限,而应用程序容器则不能。

    Ø 它们必须在应用程序容器启动之前运行完成,而应用程序容器是并行运行的,所以 Init 容 器能够提供了一种简单的阻塞或延迟应用容器的启动的方法,直到满足了一组先决条件。

 

 

Init 容器

init 模板

apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox
    command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
  - name: init-mydb
    image: busybox
    command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']

 

kind: Service
apiVersion: v1
metadata:
  name: myservice
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9376
---
kind: Service
apiVersion: v1
metadata:
  name: mydb
spec:
  ports:
    - protocol: TCP
      port: 80
      targetPort: 9377

 

检测探针 - 就绪检测

 

readinessProbe-httpget

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

  

检测探针 - 存活检测

livenessProbe-exec

apiVersion: v1
kind: Pod
metadata:
  name: liveness-exec-pod
  namespace: default
spec:
  containers:
  - name: liveness-exec-container
    image: hub.atguigu.com/library/busybox
    imagePullPolicy: IfNotPresent
    command: ["/bin/sh","-c","touch /tmp/live ; sleep 60; rm -rf /tmp/live; sleep 3600"]
    livenessProbe:
      exec:
        command: ["test","-e","/tmp/live"]
      initialDelaySeconds: 1
      periodSeconds: 3

  

livenessProbe-httpget

apiVersion: v1
kind: Pod
metadata:
  name: liveness-httpget-pod
  namespace: default
spec:
  containers:
  - name: liveness-httpget-container
    image: hub.atguigu.com/library/myapp:v1
    imagePullPolicy: IfNotPresent
    ports:
    - name: http
      containerPort: 80
    livenessProbe:
      httpGet:
        port: http
        path: /index.html
      initialDelaySeconds: 1
      periodSeconds: 3
      timeoutSeconds: 10

  

livenessProbe-tcp

apiVersion: v1
kind: Pod
metadata:
  name: probe-tcp
spec:
  containers:
  - name: nginx
    image: hub.atguigu.com/library/myapp:v1
    livenessProbe:
      initialDelaySeconds: 5
      timeoutSeconds: 1
      tcpSocket:
        port: 80

  

启动、退出动作

apiVersion: v1
kind: Pod
metadata:
  name: lifecycle-demo
spec:
  containers:
  - name: lifecycle-demo-container
    image: nginx
    lifecycle:
      postStart:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the postStart handler > /usr/share/message"]
      preStop:
        exec:
          command: ["/bin/sh", "-c", "echo Hello from the poststop handler > /usr/share/message"]

 

开始测试:

在节点执行

docker pull busybox
vim init.pod.yaml

  

kubectl create -f init.pod.yaml

 

 如果遇到重名,删除操作:

kubectl get pod
kubectl delete deployment --all

 

kubectl get pod

  

kubectl delete pod --all

 

  

kubectl get pod

  

kubectl get svc
kubectl delete svc nginx-deployment

 

kubectl get svc

 

kubectl create -f init.pod.yaml

状态没有ready

 

kubectl describe pod myapp-pod

  

kubectl log  myapp-pod -c init-myservice

  

一直没解析到 myservice

创建 myservice

vim myservice.yaml

  

kubectl log  myapp-pod -c init-myservice

等待后

kubectl get pod

  

已经成功了1个

  

发现已经创建了myservice的svc

这个svc会被集群内部的dns解析

 

  

创建pod时指定了myservice的svc,创建svc后会写入到coredns,pod请求core会返回请求,至此按照pod yaml的配置已经过去了第一个myservice,还剩一个mydb。

创建mydb

vim mydb.yaml

  

kubectl create -f mydb.yaml

 

成功启来了

只有所有init c成功运行后 ,main c才会被运行。

如果报错

 

kubectl describe pod myapp-pod

原因为node1下载busybox失败,可能原因为 不指定版本号,会使用latest标签保存,每次使用都会去下载最新版,下载失败就会报错。

 

特殊说明 -1
  在 Pod 启动过程中,Init 容器会按顺序在网络和数据卷初始化之后启动。每个容器必须在下一个 容器启动之前成功退出
  如果由于运行时或失败退出,将导致容器启动失败,它会根据 Pod 的 restartPolicy 指定的策略 进行重试。然而,如果 Pod 的 restartPolicy 设置为 Always,Init 容器失败时会使用 RestartPolicy 策略
  在所有的 Init 容器没有成功之前,Pod 将不会变成 Ready 状态。Init 容器的端口将不会在 Service 中进行聚集。 正在初始化中的 Pod 处于 Pending 状态,但应该会将 Initializing 状 态设置为 true
  如果 Pod 重启,所有 Init 容器必须重新执行

  # 对 Init 容器 spec 的修改被限制在容器 image 字段,修改其他字段都不会生效。更改 Init 容器的 image 字段,等价于重启该 Pod

  Init 容器具有应用容器的所有字段。除了 readinessProbe,因为 Init 容器无法定义不同于完成 (completion)的就绪(readiness)之外的其他状态。这会在验证过程中强制执行

  在 Pod 中的每个 app 和 Init 容器的名称必须唯一;与任何其它容器共享同一个名称,会在验证 时抛出错误

 

容器探针
  探针是由 kubelet 对容器执行的定期诊断。要执行诊断,kubelet 调用由容器实现的 Handler。有三 种类型的处理程序:

    ExecAction:在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。

    TCPSocketAction:对指定端口上的容器的 IP 地址进行 TCP 检查。如果端口打开,则诊断 被认为是成功的。
    HTTPGetAction:对指定的端口和路径上的容器的 IP 地址执行 HTTP Get 请求。如果响应的 状态码大于等于200 且小于 400,则诊断被认为是成功的
  每次探测都将获得以下三种结果之一:

    成功:容器通过了诊断。

    失败:容器未通过诊断。

    未知:诊断失败,因此不会采取任何行动

 

探测方式
  livenessProbe:指示容器是否正在运行。如果存活探测失败,则 kubelet 会杀死容器,并且容器将 受到其 重启策略 的影响。如果容器不提供存活探针,则默认状态为 Success
  readinessProbe:指示容器是否准备好服务请求。如果就绪探测失败,端点控制器将从与 Pod 匹配的 所有 Service 的端点中删除该 Pod 的 IP 地址。初始延迟之前的就绪状态默认为 Failure。如果容 器不提供就绪探针,则默认状态为 Success

 

检测探针 - 就绪检测(符合条件才READY状态) 测试:

  readinessProbe-httpget 方案

vim read.yaml

  

kubectl create -f read.yaml

  

kubectl get pod

  

虽然显示Running 但是没有READY查看日志

kubectl describe pod  readiness-httpget-pod

  

没有找到index1.html 报404页面不存在,原因为yaml文件里检测的index1.html ,真实不存在

进入容器 readiness-httpget-pod

kubectl exec readiness-httpget-pod -it -- /bin/sh 
cd /usr/share/nginx/html
echo "123" > index1.html
exit
kubectl get pod

  

因为文件已经存在,检测到就READY了,测试完毕,删除:

kubectl delete pod --all
kubectl delete svc mydb myservice

 

检测探针 - 存活检测(不符合条件就重启) 测试:

 

  livenessProbe-exec方案

vim live-exec.yaml

  

kubectl create -f live-exec.yaml

 

等待一分钟

 

重启原因为yaml文件设置如果文件不存在,存活检测会重启Pod,达到60秒(排除启动和延迟,实际要多一点)重启的结果。清理:

kubectl delete pod --all

 

  livenessProbe-httpget方案

vim live.http.yaml

 

kubectl create -f live.http.yaml
kubectl get pod

 

正常启动

可以访问

删除index.html 

kubectl exec liveness-httpget-pod -it -- /bin/sh
# rm -rf /usr/share/nginx/html/index.html
exit
curl 10.244.2.9/index.html

报错404

 

kubectl get pod

 

由于yaml文件中设置了检测index.html是否存在,如果不存在就重启, 查看重启次数已经是1了。

 

由于重启后index.html又回来, 之后就没有重启。

再删一次:

由于设置的是3秒检测一次,很快就又重启了。

清理:

kubectl delete pod --all

 

  livenessProbe-tcp方案

vim live-tcp.yaml

 

kubectl create -f live-tcp.yaml
kubectl get pod -w

 

由于检测的端口是8080,nginx端口是80,初始5秒后开始检测,发现没有8080,1秒后重启。

 

合并检测方式:

vim live-http.yaml

 

同时进行 readinessProbe(就绪检测) 和livenessProbe(存活检测)

 

发现由于没有index1.html 一直没ready

kubectl exec liveness-httpget-pod -it -- /bin/bash
echo "123" >> /usr/share/nginx/html/index1.html
exit
kubectl get pod

 

发现已经就绪了 

继续测试 存活检测:

kubectl exec liveness-httpget-pod -it -- rm -rf /usr/share/nginx/html/index.html
kubectl get pod -w

发现删除后就重启,并且由于重启后又没有index1.html,就绪检测执行后未通过。

 

readinessProbe(就绪检测) 和livenessProbe(存活检测)可以配合init c  start stop 使用

 了解 start stop

  

vim post.yaml

 

kubectl create -f post.yaml

 

kubectl exec lifecycle-demo -it -- cat /usr/share/message 

 

验证通过 

此不会采取任何行动

 

Pod hook
  Pod hook(钩子)是由 Kubernetes 管理的 kubelet 发起的,当容器中的进程启动前或者容器中的进 程终止之前运行,这是包含在容器的生命周期之中。可以同时为 Pod 中的所有容器都配置 hook

  Hook 的类型包括两种:

    exec:执行一段命令

    HTTP:发送HTTP请求

 

重启策略
  PodSpec 中有一个 restartPolicy 字段,可能的值为 Always、OnFailure 和 Never。默认为 Always。 restartPolicy 适用于 Pod 中的所有容器。restartPolicy 仅指通过同一节点上的 kubelet 重新启动容器。失败的容器由 kubelet 以五分钟为上限的指数退避延迟(10秒,20秒,40 秒...)重新启动,并在成功执行十分钟后重置。如 Pod 文档 中所述,一旦绑定到一个节点,Pod 将 永远不会重新绑定到另一个节点。

 

Pod phase

  Pod 的 status 字段是一个 PodStatus 对象,PodStatus中有一个 phase 字段。
  Pod 的相位(phase)是 Pod 在其生命周期中的简单宏观概述。该阶段并不是对容器或 Pod 的综合汇 总,也不是为了做为综合状态机
  Pod 相位的数量和含义是严格指定的。除了本文档中列举的状态外,不应该再假定 Pod 有其他的 phase 值

 

Pod phase 可能存在的值
  挂起(Pending):Pod 已被 Kubernetes 系统接受,但有一个或者多个容器镜像尚未创建。等待时间 包括调度 Pod 的时间和通过网络下载镜像的时间,这可能需要花点时间
  运行中(Running):该 Pod 已经绑定到了一个节点上,Pod 中所有的容器都已被创建。至少有一个容 器正在运行,或者正处于启动或重启状态

  成功(Succeeded):Pod 中的所有容器都被成功终止,并且不会再重启
  失败(Failed):Pod 中的所有容器都已终止了,并且至少有一个容器是因为失败终止。也就是说,容 器以非 0 状态退出或者被系统终止

  未知(Unknown):因为某些原因无法取得 Pod 的状态,通常是因为与 Pod 所在主机通信失败

 

 

五、资源控制器

Pod 的分类

  自主式 Pod:Pod 退出了,此类型的 Pod 不会被创建
  控制器管理的 Pod:在控制器的生命周期里,始终要维持 Pod 的副本数目

 

5.1 Kubernetes Pod 控制器

什么是控制器
  Kubernetes 中内建了很多 controller(控制器),这些相当于一个状态机,用来控制 Pod 的具体状态和行为

控制器类型
  ReplicationController 和 ReplicaSet (无状态服务RS-Deployment)
  Deployment
  DaemonSet(以Node为节点部署)
  StateFulSet (有状态服务)
  Job/CronJob (批处理任务部署)
  Horizontal Pod Autoscaling (可以理解为并不是一个控制器,而是一个控制器的附属品,以其他控制器作为模板)
ReplicationController 和 ReplicaSet
  ReplicationController(RC)用来确保容器应用的副本数始终保持在用户定义的副本数,即如果有容器异常退出,会自动创建新的 Pod 来替代;而如果异常多出来的容器也会自动回收;
  在新版本的 Kubernetes 中建议使用 ReplicaSet 来取代 ReplicationController 。ReplicaSet 跟ReplicationController 没有本质的不同,只是名字不一样,并且 ReplicaSet 支持集合式的 selector;

Deployment
  Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义 (declarative) 方法,用来替代以前的ReplicationController 来方便的管理应用。典型的应用场景包括;
    定义 Deployment 来创建 Pod 和 ReplicaSet
    滚动升级和回滚应用
    扩容和缩容
    暂停和继续 Deployment

  命令式编程:它侧重于如何实现程序,就像编程那样,把步骤一步步写下来。

  声明式编程:它侧重定义想要什么,然后告诉计算机/引擎,让他帮你去实现。

  声明式编程 (Deployment) apply(优) create
  命令式 (rs) create(优) apply

  

  

  Deployment创建rs,rs创建、管理pod 

DaemonSet

  DaemonSet 确保全部(或者一些,根据调度策略或者污点定义)Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod使用 DaemonSet 的一些典型用法:
  运行集群存储 daemon,例如在每个 Node 上运行 glusterd 、 ceph
  在每个 Node 上运行日志收集 daemon,例如 fluentd 、 logstash
  在每个 Node 上运行监控 daemon,例如 zabbix agent、Prometheus Node Exporter、 collectd 、Datadog 代理、New Relic 代理,或 Ganglia gmond
Job
  Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束(跟linux crontab比有纠错功能)
CronJob (在特定的时间循环创建Job)
  Cron Job 管理基于时间的 Job,即:   分时日月周
    在给定时间点只运行一次
    周期性地在给定时间点运行
使用前提条件:**当前使用的 Kubernetes 集群,版本 >= 1.8(对 CronJob)。对于先前版本的集群,版本 <1.8,启动 API Server时,通过传递选项 --runtime-config=batch/v2alpha1=true 可以开启 batch/v2alpha1API**
典型的用法如下所示:
  在给定的时间点调度 Job 运行
  创建周期性运行的 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)

  

 

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

 

5.2 Kubernetes Deployment 控制器

[root@k8s-master01 ~]# kubectl explain rs

RS 与 RC 与 Deployment 关联

  RC (ReplicationController )主要的作用就是用来确保容器应用的副本数始终保持在用户定义的副本数 。即如果有容器异常退出,会自动创建新的Pod来替代;而如果异常多出来的容器也会自动回收
  Kubernetes 官方建议使用 RS(ReplicaSet ) 替代 RC (ReplicationController ) 进行部署,RS 跟 RC 没有本质的不同,只是名字不一样,并且 RS 支持集合式的 selector

[root@k8s-master01 ~]# vim rs.yaml

 

kubectl create -f rs.yaml

kubectl get pod

多出了3个Pod

 

kubectl delete pod --all 

清理以前的Pod

kubectl get pod  

之前4个pod全被删除,没有删除deployment,为了维持定义的副本数又创建了3个pod,但是名称不一样了。

 

查看标签

kubectl get pod --show-labels

yaml文件中定义的标签是 frontend

 

更改标签

报错,提示frontend-bdhhj已有标签,除非添加--overwrite参数

 

kubectl label pod frontend-bdhhj tier=frontend1 --overwrite=True

  

提示已修改,可以看到frontend-bdhhj标签已改,并且由于yaml模板中pod设置了匹配标签选项,frontend标签继续保持副本数添加了一个新的pod。

 

删除rs

kubectl delete rs --all
kubectl get pod --show-labels

仅删除了rs关联的pod

 

清理

kubectl delete pod --all

 

RS 与 Deployment 的关联

 

Deployment
Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义(declarative)方法,用来替代以前的ReplicationController 来方便的管理应用。典型的应用场景包括:
  定义Deployment来创建Pod和ReplicaSet
  滚动升级和回滚应用
  扩容和缩容
  暂停和继续Deployment
I、部署一个简单的 Nginx 应用

vim deployment.yaml

 

kubectl create -f https://kubernetes.io/docs/user-guide/nginx-deployment.yaml --record

## --record参数可以记录命令,我们可以很方便的查看每次 revision 的变化

 

kubectl get deployment
kubectl get rs
kubectl get pod

deployment创建会创建对应的rs

  

访问

curl 10.244.1.16

  

II、扩容副本数

kubectl scale deployment nginx-deployment --replicas=10

  

发现像nginx这种无状态服务扩容特别简单

名称未变,数目调整不会调整模板信息

 

III、更新镜像:

kubectl set image deployment/nginx-deployment nginx=hub.yyq.com/library/mynginx:v2

 

提示已更新

 

查看rs

kubectl get rs

发现 期望是2 当前是2 但是ready是0 

 

查看Pod

发现当前并没有v2版本,也就pull不到

 

创建一个v2

 

cd n/
vim Dockerfile

 

#Version:2
FROM hub.yyq.com/library/mynginx:v1
RUN echo $HOSTNAME > /usr/share/nginx/html/index.html
docker build -t="hub.yyq.com/library/mynginx:v2" .
docker login https://hub.yyq.com
docker push hub.yyq.com/library/mynginx:v2 

 

kubectl delete deployment nginx-deployment
kubectl create -f deployment.yaml
kubectl scale deployment nginx-deployment --replicas=10
kubectl set image deployment/nginx-deployment nginx=hub.yyq.com/library/mynginx:v2
kubectl get pod -o wide -w

过程如:

陆续更新完成

 

kubectl get rs

已全部更新

 

验证

 

IV、回滚:

kubectl rollout undo deployment/nginx-deployment

交替过程

kubectget pod -w -o wide

kubectl rollout status deployment/nginx-deployment

 

 

kubect get rs

 

验证

kubectl rollout status deployment/nginx-deployment

 

curl 10.244.2.64

 回v1版了

 

V、如果集群支持 horizontal pod autoscaling 的话,还可以为Deployment设置自动扩展

kubectl autoscale deployment nginx-deployment --min=10 --max=15 --cpu-percent=80


可以使用 edit 命令来编辑 Deployment

kubectl edit deployment/nginx-deployment
deployment "nginx-deployment" edited

可以更新副本数或其他

 

 

Deployment 更新策略
  Deployment 可以保证在升级时只有一定数量的 Pod 是 down 的。默认的,它会确保至少有比期望的Pod数量少一个是up状态(最多一个不可用)
  Deployment 同时也可以确保只创建出超过期望数量的一定数量的 Pod。默认的,它会确保最多比期望的Pod数量多一个的 Pod 是 up 的(最多1个 surge )
  未来的 Kuberentes 版本中,将从1-1变成25%-25% (无论数量多少,交替过程中每次操作25%的数量)

      

 

kubectl describe deployments

 

Rollover(多个rollout并行)
  假如您创建了一个有5个 niginx: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 都创建完成后才开始改变航道


回退 Deployment

kubectl set image deployment/nginx-deployment nginx=nginx:1.91
kubectl rollout status deployments nginx-deployment
kubectl get pods
kubectl rollout history deployment/nginx-deployment

CHANGE-CAUSE 是none 因为创建时没有加--record

 

测试一下:

  kubectl delete deployment --all
  kubectl create -f deployment.yaal  --record
  kubectl set image deployment/nginx-deployment nginx=hub.yyq.com/library/mynginx:v2
  kubectl rollout history deployment/nginx-deployment

  

 

   kubectl set image deployment/nginx-deployment nginx=hub.yyq.com/library/mynginx:v3
   kubectl rollout history deployment/nginx-deployment

  

 

  kubectl get rs

  

  产生了新的rs

   

kubectl rollout undo deployment/nginx-deployment --to-revision=1

## 可以使用 --revision参数指定某个历史版本

 

 

再看历史版本

kubectl rollout history deployment/nginx-deployment

当前版本的这里查看历史版本,回退到以前的版本时以前的版本号会变成新的号,比如按照上图回退到2,就会变成3 4 5,再会退到4就变成356

 

kubectl rollout pause deployment/nginx-deployment

 

## 暂停 deployment 的更新
您可以用 kubectl rollout status 命令查看 Deployment 是否完成。如果 rollout 成功完成, kubectl rollout
status 将返回一个0值的 Exit Code

kubectl rollout status deploy/nginx

 

Waiting for rollout to finish: 2 of 3 updated replicas are available...
deployment "nginx" successfully rolled out
$ echo $?
0 

 

kubectl delete deployment --all

 

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

 

5.3 Kubernetes DaemonSet 控制器 

什么是 DaemonSet

  DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本。当有 Node 加入集群时,也会为他们新增一个 Pod 。当有 Node 从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod
使用 DaemonSet 的一些典型用法:
  运行集群存储 daemon,例如在每个 Node 上运行 glusterd 、 ceph
  在每个 Node 上运行日志收集 daemon,例如 fluentd 、 logstash
  在每个 Node 上运行监控 daemon,例如 Zabbix agent、 Prometheus Node Exporter、 collectd 、Datadog 代理、New Relic 代理,或 Ganglia gmond
 

vim daemonset.yaml

 

 

 

kubectl create -f daemonset.yaml
kubect get pod 

 

kubectl get daemonset.apps

 

kubectl get pod 

 

5.4 Kubernetes JobCronJob 控制器

 

Job

 Job 负责批处理任务,即仅执行一次的任务,它保证批处理任务的一个或多个 Pod 成功结束
 特殊说明
  spec.template格式同Pod
  RestartPolicy仅支持Never或OnFailure
  单个Pod时,默认Pod成功运行后Job即结束
  .spec.completions 标志Job结束需要成功运行的Pod个数,默认为1
  .spec.parallelism 标志并行运行的Pod的个数,默认为1
  spec.activeDeadlineSeconds 标志失败Pod的重试最大时间,超过这个时间不会继续重试

vim job.yam

计算Pi2000位(根据机器性能可以调小点)

 

kubectl create -f job.yaml

 

 

kubectl get pod 

 

kubectl describe pod pi-lbrh5

 

正在下镜像,太慢了,手动导入

yum -y install lrzsz

perl.tar.gz

[root@k8s-master01 ~]# tar xvf perl.tar.gz
[root@k8s-master01 ~]# docker load -i perl.tar
[root@k8s-master01 ~]# scp perl.tar root@k8s-node01:/root
[root@k8s-master01 ~]# scp perl.tar root@k8s-node02:/root
[root@k8s-node01 ~]# docker load -i perl.tar
[root@k8s-node02 ~]# docker load -i perl.tar
kubectl get pod

 

时间比较久,已经ImagePullBackOff了,删除,自己重建。

 

kubectl get pod -o wide

等待

 

kubectl get pod 

已经Completed了

 

get pod job

作业完成

 

查看日志

已经将圆周率推算到2000位,如果运行太慢,修改yaml文件将数字改小,删除后然后重新创建。

 

CronJob Spec

  spec.template格式同Pod
  RestartPolicy仅支持Never或OnFailure
  单个Pod时,默认Pod成功运行后Job即结束
  .spec.completions 标志Job结束需要成功运行的Pod个数,默认为1
  .spec.parallelism 标志并行运行的Pod的个数,默认为1
  spec.activeDeadlineSeconds 标志失败Pod的重试最大时间,超过这个时间不会继续重试

 

CronJob

 Cron Job 管理基于时间的 Job,即:
  在给定时间点只运行一次
  周期性地在给定时间点运行

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

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

CronJob Spec

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

 

vim cronjob.yaml

 

kubectl apply -f cronjob.yaml

 

 

kubectl get cronjob

 

 

kubectl get job

 

kubectl log hello-1605656340-gbf95

 

清理

kubectl delete cronjob --all

 

pods=$(kubectl get pods --selector=job-name=hello-1202039034 --output=jsonpath={.items..metadata.name})

  

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

 

六、Service

Service 的概念

 

  Kubernetes Service 定义了这样一种抽象:一个 Pod 的逻辑分组,一种可以访问它们的策略 —— 通常称为微服务。 这一组 Pod 能够被 Service 访问到,通常是通过 Label Selector

 

  这里的分配机制只有RR轮训 

 

 

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

Service 的类型
 Service 在 K8s 中有以下四种类型
  ClusterIp:默认类型,自动分配一个仅 Cluster 内部可以访问的虚拟 IP

   

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

  

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

  

   需要借助于供应商来保存注册、端口等信息,需要单独收费

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

  

 

  

 

 

VIP 和 Service 代理
 在 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)服务
  !为何不使用 round-robin DNS?

  解析在客户端缓存,很多客户端解析后不会及时清理,可能会造成无法及时更新。

 

代理模式的分类
 I、userspace 代理模式

 

 

 

 II、iptables 代理模式

  

 III、ipvs 代理模式

  这种模式,kube-proxy 会监视 Kubernetes Service 对象和 Endpoints ,调用 netlink 接口以相应地创建ipvs 规则并定期与 Kubernetes Service 对象和 Endpoints 对象同步 ipvs 规则,以确保 ipvs 状态与期望一致。访问服务时,流量将被重定向到其中一个后端 Pod

  与 iptables 类似,ipvs 于 netfilter 的 hook 功能,但使用哈希表作为底层数据结构并在内核空间中工作。这意味着 ipvs 可以更快地重定向流量,并且在同步代理规则时具有更好的性能。此外,ipvs 为负载均衡算法提供了更多选项,例如:
    rr :轮询调度
    lc :最小连接数
    dh :目标哈希
    sh :源哈希
    sed :最短期望延迟
    nq : 不排队调度


 

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

 

 

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

  apiserver 用户通过kubectl命令向apiserver发送创建service的命令,apiserver接收到请求后将数据存储到etcd中
  kube-proxy kubernetes的每个节点中都有一个叫做kube-porxy的进程,这个进程负责感知service,pod的变化,并将变化的信息写入本地的iptables规则中
  iptables 使用NAT等技术将virtualIP的流量转至endpoint中

wget https://github.com/kubernetes/ingress-nginx/blob/nginx-0.25.0/deploy/static/mandatory.yaml

wget https://github.com/kubernetes/ingress-nginx/blob/nginx-0.25.0/deploy/static/provider/baremetal/service-nodeport.yaml

 

由于镜像获取问题Service后续测试无法验证。

delete deployment --all

delete ingress --all

delete svc svc-1 svc-2 svc-3

 

6.2 Kubernetes Ingress

 

 

7、存储 configMap

7.1configMap(存储配置文件)

7.2Secret(加密信息)

7.3volume(共享存储卷)

7.4Persistent Volume(PV、PVC 持久卷)

 

7.1 configMap

 

configMap 描述信息

 ConfigMap 功能在 Kubernetes1.2 版本中引入,许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。ConfigMap API 给我们提供了向容器中注入配置信息的机制,ConfigMap 可以被用来保存单个属性,也可以用来保存整个配置文件或者 JSON二进制大对象

 

 

 

ConfigMap 的创建

 

I、使用目录创建

mkdir configmap
cd configmap/
mkdir dir
cd dir/
vim game.properties

 

enemies=aliens
lives=3
enemies.cheat=true
enemies.cheat.level=noGoodRotten
secret.code.passphrase=UUDDLRLRBABAS
secret.code.allowed=true
secret.code.lives=30

 

vim ui.properties

 

color.good=purple
color.bad=yellow
allow.textmode=true
how.nice.to.look=fairlyNice

 

kubectl create configmap game-config --from-file=/root/configmap/dir/

—from-file 指定在目录下的所有文件都会被用在 ConfigMap 里面创建一个键值对,键的名字就是文件名,就是文件的内容

 

kubectl get cm
kubectl get cm game-config -o yaml

可以看到设置的值信息

 

kubectl describe cm game-config

也可以看到

 

II、使用文件创建

只要指定为一个文件就可以从单个文件中创建 ConfigMap

kubectl create configmap game-config-2 --from-file=/root/configmap/dir/game.properties

—from-file 这个参数可以使用多次,你可以使用两次分别指定上个实例中的那两个配置文件,效果就跟指定整个目录是一样的

 

 

 

kubectl get configmaps game-config-2 -o yaml

 

III、使用字面值创建
使用文字值创建,利用 —from-literal 参数传递配置信息,该参数可以使用多次,格式如下

kubectl create configmap special-config --from-literal=special.how=very --from-literal=special.type=charm

 

 

kubectl get configmaps special-config -o yaml

 

kubectl describe configmap special-config

 

Pod 中使用 ConfigMap

 

I、使用 ConfigMap 来替代环境变量

已有

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm

 

mkdir mkdir /root/configmap/env/
cd mkdir /root/configmap/env/
vim env.yaml 

 

kubectl get cm

  

vim pod.yaml

 

kubectl get pod

由于yaml设置的command执行成功后状态就完成了

 

kubectl log dapi-test-pod

 

II、用 ConfigMap 设置命令行参数

已有

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm

 

vim pod1.yaml

 

kubectl create -f pod1.yaml
kubectl get pod

 

kubectl log dapi-test-pod66

command 中的$(SPECIAL_LEVEL_KEY) 对应的 special-config中的special.how设置的的very charm

 

III、通过数据卷插件使用ConfigMap

已有

apiVersion: v1
kind: ConfigMap
metadata:
  name: special-config
  namespace: default
data:
  special.how: very
  special.type: charm

 

在数据卷里面使用这个 ConfigMap,有不同的选项。最基本的就是将文件填入数据卷,在这个文件中,键就是文件名,键值就是文件内容

 

vim 111.yaml

 

kubectl create -f 111.yaml
kubectl get pod
kubectl log dapi-test-pod11

报错

 

修改yaml

vim 111.yaml

 

command: [ "/bin/sh", "-c", "sleep 600s" ]

 

delete -f 111.yaml
kubectl create -f 111.yaml
kubectl get pod

 

kubectl exec dapi-test-pod11 -it -- /bin/sh
cd /etc/config
ls
cat special.how

volumes挂载成功

 

清理

kubectl delete pod --all

kubectl delete cm --all

 

ConfigMap 的热更新

 

mkdir /root/configmap/config
cd /root/configmap/config
vim 111.yaml

 

kubectl apply -f 111.yaml
kubectl get pod

 

kubectl exec my-nginx-64b66b49d8-rz6bc -it -- cat /etc/config/log_level

验证完成

 

热更新

修改 ConfigMap

 

kubectl edit configmap log-config

 

 

修改 log_level 的值为 DEBUG 等待大概 10 秒钟时间,再次查看环境变量的值

 

kubectl exec my-nginx-64b66b49d8-rz6bc -it -- cat /etc/config/log_level

再次执行,发现已经改过来了。 

 

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

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

这个例子里我们在 .spec.template.metadata.annotations 中添加 version/config ,每次通过修改version/config 来触发滚动更新

!!! 更新 ConfigMap 后:
  使用该 ConfigMap 挂载的 Env 不会同步更新
  使用该 ConfigMap 挂载的 Volume 中的数据需要一段时间(实测大概10秒)才能同步更新

 

 

7.2Secret(加密信息)

 

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 registry 的认证信息

 

Service Account(不常用)
 Service Account 用来访问 Kubernetes API,由 Kubernetes 自动创建,并且会自动挂载到 Pod的/run/secrets/kubernetes.io/serviceaccount 目录中

kubectl get pod
kubectl exec my-nginx-64b66b49d8-rz6bc -it -- /bin/sh
cd /run/secrets/kubernetes.io/serviceaccount 
ls  

 

 

ca.crt

namespace #pod所在namespace

token #认证信息

 

Opaque Secret

 

 

I、创建说明
Opaque 类型的数据是一个 map 类型,要求 value 是 base64 编码格式:

echo -n "admin" | base64
YWRtaW4=
echo -n "1f2d1e2e67df" | base64
MWYyZDFlMmU2N2Rm

 

[root@k8s-master01 config]# vim secrets.yml

 

kubectl apply -f secrets.yml
kubectl get secrets

 已创建,并且每个名称空间下都会有一个默认的token,用于pod的挂载。

 

 

kubectl get secrets -n kube-system

 

 

II、使用方式
1、将 Secret 挂载到 Volume 中

 

[root@k8s-master01 config]# vim pod1.yaml

 

kubectl apply -f pod1.yaml 
kubectl get pod 

 

kubectl exec seret-test -it -- /bin/sh
cat /erc/secrets/username

已经被解密

 

清理

kubectl delete pod --all

 

2、将 Secret 导出到环境变量中

 

[root@k8s-master01 config]# vim env.yaml

 

kubectl apply -f env.yaml

 

kubectl exec pod-deployment-564f59b6b7-6q5zg -it -- /bin/sh

测试通过

 

清理

kubectl delete -f env.yaml
kubectl delete deployment --all
kubernetes.io/dockerconfigjson

 

kubernetes.io/dockerconfigjson

使用 Kuberctl 创建 docker registry 认证的 secret 

模拟K8s启动时候提示无法pull镜像的场景,然后用认证解决,首先把仓库设置成私有。

 

每台都删除之前的镜像

docker rmi hub.yyq.com/library/mynginx:v1

 

每台都退出登录

docker logout hub.yyq.com

 

mkdir reg
cd reg
vim pod.yaml

 

kubectl create -f pod.yaml
kubectl get pod

报错,查看日志

 

kubectl describe pod foo

无法pull镜像,必须要认证以后才能下载

 

通过dockerconfigjson解决

 

kubectl create secret docker-registry myregistrykey --docker-server=hub.yyq.com --docker-username=admin --docker-password=Harbor12345 --docker-email=13439629295@139.com

 

secret 创建成功

 

编辑yaml文件,添加

vim pod.yaml

 

imagePullSecrets:
- name: myregistrykey

 

kubectl delete -f 
kubectl create -f pod.yaml
kubectl get pod

 

kubectl describe pod foo

pull成功

 

清理

kubectl delete pod --all

 

 

7.3 volume

  容器磁盘上的文件的生命周期是短暂的,这就使得在容器中运行重要应用时会出现一些问题。首先,当容器崩溃时,kubelet 会重启它,但是容器中的文件将丢失——容器以干净的状态(镜像最初的状态)重新启动。其次,在Pod 中同时运行多个容器时,这些容器之间通常需要共享文件。Kubernetes 中的 Volume 抽象就很好的解决了这些问题

 

背景

Kubernetes 中的有明确的寿命 —— 与封装它的 Pod 相同。所f以,卷的生命比 Pod 中的所有容器都长,当这个容器重启时数据仍然得以保存。当然,当 Pod 不再存在时,卷也将不复存在。也许更重要的是,Kubernetes支持多种类型的卷,Pod 可以同时使用任意数量的卷

 

卷的类型

Kubernetes 支持以下类型的卷:

awsElasticBlockStore
azureDisk
azureFile
cephfs
csi
downwardAPI
emptyDir
fc
flocker
gcePersistentDisk
gitRepo
glusterfs
hostPath
iscsi
local
nfs
persistentVolumeClaim
projected
portworxVolume
quobyte
rbd
scaleIO
secret
storageos
vsphereVolume


emptyDir
  当 Pod 被分配给节点时,首先创建 emptyDir 卷,并且只要该 Pod 在该节点上运行,该卷就会存在。正如卷的名字所述,它最初是空的。Pod 中的容器可以读取和写入 emptyDir 卷中的相同文件,尽管该卷可以挂载到每个容器中的相同或不同路径上。当出于任何原因从节点中删除 Pod 时, emptyDir 中的数据将被永久删除

 <--注意:容器崩溃不会从节点中移除 pod,因此 ·emptyDir· 卷中的数据在容器崩溃时是安全的-->

emptyDir 的用法有:
  暂存空间,例如用于基于磁盘的合并排序
  用作长时间计算崩溃恢复时的检查点
  Web服务器容器提供数据时,保存内容管理器容器提取的文件

 

 

[root@k8s-master01 reg]# ntpdate ntp1.aliyun.com

 

mkdir volume
cd volume
vim em.yaml

 

 

kubectl apply -f em.yaml

  

kubectl exec test-pd -it -- /bin/sh

 

修改yaml,再添加一个容器

vim em.yaml

 

 

kubectl create -f em.yaml

 

 

kubectl exec test-pd1 -c test-container -it -- /bin/sh

 

使用另外一个终端登录另外一个容器

 

有index.html,继续测

 

返回另外一个容器

同步了

 

清理

kubectl delete pod --all

 

hostPath

  hostPath 卷将主机节点的文件系统中的文件或目录挂载到集群中
hostPath 的用途如下:
  运行需要访问 Docker 内部的容器;使用 /var/lib/docker 的 hostPath,本机需要访问容器内部使用
  在容器中运行 cAdvisor;使用 /dev/cgroups 的 hostPath
  允许 pod 指定给定的 hostPath 是否应该在 pod 运行之前存在,是否应该创建,以及它应该以什么形式存在
除了所需的 path 属性之外,用户还可以为 hostPath 卷指定 type

 

使用这种卷类型是请注意,因为:
  由于每个节点上的文件都不同,具有相同配置(例如从 podTemplate 创建的)的 pod 在不同节点上的行为可能会有所不同
  当 Kubernetes 按照计划添加资源感知调度时,将无法考虑 hostPath 使用的资源
  在底层主机上创建的文件或目录只能由 root 写入。您需要在特权容器中以 root 身份运行进程,或修改主机上的文件权限以便写入 hostPath 卷

vim pod1.yaml

 

在node01 node02上创建文件

mkdir /data
kubectl create -f pod1.yaml

 

kubectl exec test-pd -it -- /bin/sh

 

在node02上查看 /data

 

在node上重新写入 

 

查看容器 

同步了

 

7.4  Persistent Volume

 

概念

PersistentVolume (PV)

  是由管理员设置的存储,它是群集的一部分。就像节点是集群中的资源一样,PV 也是集群中的资源。 PV 是Volume 之类的卷插件,但具有独立于使用 PV 的 Pod 的生命周期。此 API 对象包含存储实现的细节,即 NFS、iSCSI 或特定于云供应商的存储系统

PersistentVolumeClaim (PVC)

  是用户存储的请求。它与 Pod 相似。Pod 消耗节点资源,PVC 消耗 PV 资源。Pod 可以请求特定级别的资源(CPU 和内存)。声明可以请求特定的大小和访问模式(例如,可以以读/写一次或 只读多次模式挂载)

静态 pv
  集群管理员创建一些 PV。它们带有可供群集用户使用的实际存储的细节。它们存在于 Kubernetes API 中,可用于消费

动态
  当管理员创建的静态 PV 都不匹配用户的 PersistentVolumeClaim 时,集群可能会尝试动态地为 PVC 创建卷。此配置基于 StorageClasses :PVC 必须请求 [存储类],并且管理员必须创建并配置该类才能进行动态创建。声明该类为 "" 可以有效地禁用其动态配置
  要启用基于存储级别的动态存储配置,集群管理员需要启用 API server 上的 DefaultStorageClass [准入控制器]。例如,通过确保 DefaultStorageClass 位于 API server 组件的 --admission-control 标志,使用逗号分隔的有序值列表中,可以完成此操作

绑定
  master 中的控制环路监视新的 PVC,寻找匹配的 PV(如果可能),并将它们绑定在一起。如果为新的 PVC 动态调配 PV,则该环路将始终将该 PV 绑定到 PVC。否则,用户总会得到他们所请求的存储,但是容量可能超出要求的数量。一旦 PV 和 PVC 绑定后, PersistentVolumeClaim 绑定是排他性的,不管它们是如何绑定的。 PVC 跟PV 绑定是一对一的映射

持久化卷声明的保护
  PVC 保护的目的是确保由 pod 正在使用的 PVC 不会从系统中移除,因为如果被移除的话可能会导致数据丢失

 <!--注意:当 pod 状态为 'Pending' 并且 pod 已经分配给节点或 pod 为 'Runing' 状态时,PVC处于活动状态-->
  当启用PVC 保护 alpha 功能时,如果用户删除了一个 pod 正在使用的 PVC,则该 PVC 不会被立即删除。PVC 的删除将被推迟,直到 PVC 不再被任何 pod 使用

持久化卷类型
PersistentVolume 类型以插件形式实现。Kubernetes 目前支持以下插件类型:
  GCEPersistentDisk AWSElasticBlockStore AzureFile AzureDisk FC (Fibre Channel)
  FlexVolume Flocker NFS iSCSI RBD (Ceph Block Device) CephFS
  Cinder (OpenStack block storage) Glusterfs VsphereVolume Quobyte Volumes
  HostPath VMware Photon Portworx Volumes ScaleIO Volumes StorageOS

持久卷演示代码

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Recycle
storageClassName: slow mountOptions: - hard - nfsvers=4.1 nfs: path: /tmp server: 172.17.0.2

 

PV 访问模式

  PersistentVolume 可以以资源提供者支持的任何方式挂载到主机上。如下表所示,供应商具有不同的功能,每个PV 的访问模式都将被设置为该卷支持的特定模式。例如,NFS 可以支持多个读/写客户端,但特定的 NFS PV 可能以只读方式导出到服务器上。每个 PV 都有一套自己的用来描述特定功能的访问模式
  ReadWriteOnce——该卷可以被单个节点以读/写模式挂载
  ReadOnlyMany——该卷可以被多个节点以只读模式挂载
  ReadWriteMany——该卷可以被多个节点以读/写模式挂载
在命令行中,访问模式缩写为:
  RWO - ReadWriteOnce
  ROX - ReadOnlyMany
  RWX - ReadWriteMany
Volume 插件

 

回收策略
  Retain(保留)——手动回收
  Recycle(回收)——基本擦除( rm -rf /thevolume/* )
  Delete(删除)——关联的存储资产(例如 AWS EBS、GCE PD、Azure Disk 和 OpenStack Cinder 卷)
  将被删除
 当前,只有 NFS 和 HostPath 支持回收策略。AWS EBS、GCE PD、Azure Disk 和 Cinder 卷支持删除策略

状态
  卷可以处于以下的某种状态:
  Available(可用)——一块空闲资源还没有被任何声明绑定
  Bound(已绑定)——卷已经被声明绑定
  Released(已释放)——声明被删除,但是资源还未被集群重新声明
  Failed(失败)——该卷的自动回收失败
 命令行会显示绑定到 PV 的 PVC 的名称

持久化演示说明 - NFS
I、安装 NFS 服务器

在Harbor上部署NFS:

Harbor启动:

docker-compose start

 

yum install -y nfs-common nfs-utils rpcbind
mkdir /nfs
chown 777 /nfs/
chown nfsnobody /nfs/
cat /etc/exports
/nfs *(rw,no_root_squash,no_all_squash,sync)
systemctl start rpcbind
systemctl start nfs

  

其他节点安装客户端

yum -y install nfs-utils rpcbind

 

测试NFS是否可用

mkdir /test
showmount -e 192.168.66.100

 

mount -t nfs 192.168.66.100:/nfs /test
cd /test
echo "iifsdfsdf" > /test/1.html
cd ..
umount /nfs
rm -rf /nfs

 

II、部署 PV

 

mkdir ~/pv
cd ~/pv
vim py.yaml

 

kubectl create -f pv.yaml
kubectl get pv

  

III、创建服务并使用 PVC

 

mkdir /nfs{1..3}
[root@hub ~]# cat /etc/exports

 

mkdir /nfs{1..3}
chmod 777 /nfs{1..3}
chown nfsnobody /nfs{1..3}
systemctl restart rpcbind
systemctl restart nfs

  

在其他节点测试:

mount -t nfs 192.168.66.100:/nfs1 /test
echo "22" > /test/index.html
umount /test/
rm -rf /test/

 

 

vim pv.yaml 

 

kubectl create -f pv.yaml
kubectl get pv

 

调整一下nfspv3的类
vim pv1.yaml

 

 

kubectl delete pv nfspv3
kubectl create -f pv1.yaml
kubectl get pv

 

 

[root@k8s-master01 pv]# vim pod.yaml

 

kubectl apply -f pod.yaml
kubectl get pod

状态不对

 

kubectl describe pod web-1

没有对应的请求被绑定,因为yaml文件里面定义的条件和实际存在的pv不匹配。

 

排查:

查看PV

kubectl get pv

 

查看yaml

 

 

再看pod

yaml匹配要求类是nfs,访问控制列表要求是RWO,同时满足这两个条件的只有nfspv1,并且RWO只能绑定一个,所以第二个pod一直等待了,副本数定义的3个,但是由于有序部署,第二个状态不是ready或者running,第三个pod还没出来。

  yaml定义的大小只要>=storage即可,满足条件后优先选择小资源的(最低要求)进行绑定。

 

调整:

改PV3和4符合要求的。

kubectl delete pv nfspv3 nfspv4
vim pv2.yaml

 

kubectl create -f pv2.yaml

 

 

ubectl describe pod web-2 

 

kubectl get pv 

 

vim pv2.yaml

 

kubectl delete -f pv2.yaml
kubectl create -f pv2.yaml

 

 

kubectl get pvc 

 

 

kubectl describe pv  nfspv1 

 

在nfs上创建一个Index.html

[root@hub ~]# cd /nfs
[root@hub nfs]# ls
1.html
[root@hub nfs]# rm 1.html rm:是否删除普通文件 "1.html"?y
[root@hub nfs]# echo "aaa" > index.html [root@hub nfs]# chmod 777 index.html

 

[root@k8s-master01 pv]# kubectl get pod -o wide

 

curl 10.244.2.124

 

同理

kubectl get pvc
kubectl get pv
kubectl get pod -o wide
kubectl describe pv nfspv3
kubectl describe pv nfspv4

 

[root@hub nfs]# echo "bbb" > /nfs2/index.html
[root@hub nfs]# echo "ccc" > /nfs3/index.html

 

curl 10.244.1.104
curl 10.244.2.125

  

kubectl delete pod  web-0
kubectl get pod -o wide

删除后保留副本又启了一个新的容器IP变了,访问名称还一致,测试 数据依然存在

可以通过 Pod域名或者FQDN访问同一个名称

 

 

关于 StatefulSet

  匹配 Pod name ( 网络标识 ) 的模式为:$(statefulset名称)-$(序号),比如上面的示例:web-0,web-1,web-2
  StatefulSet 为每个 Pod 副本创建了一个 DNS 域名,这个域名的格式为: $(podname).(headless servername),也就意味着服务间是通过Pod域名来通信而非 Pod IP,因为当Pod所在Node发生故障时, Pod 会被飘移到其它 Node 上,Pod IP 会发生变化,但是 Pod 域名不会有变化
  StatefulSet 使用 Headless 服务来控制 Pod 的域名,这个域名的 FQDN 为:$(servicename).$(namespace).svc.cluster.local,其中,“cluster.local” 指的是集群的域名
  根据 volumeClaimTemplates,为每个 Pod 创建一个 pvc,pvc 的命名规则匹配模式:(volumeClaimTemplates.name)-(pod_name),比如上面的 volumeMounts.name=www, Podname=web-[0-2],因此创建出来的 PVC 是 www-web-0、www-web-1、www-web-2
  删除 Pod 不会删除其 pvc,手动删除 pvc 将自动释放 pv

Statefulset的启停顺序:
  有序部署:部署StatefulSet时,如果有多个Pod副本,它们会被顺序地创建(从0到N-1)并且,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态。
  有序删除:当Pod被删除时,它们被终止的顺序是从N-1到0。
  有序扩展:当对Pod执行扩展操作时,与部署一样,它前面的Pod必须都处于Running和Ready状态。

StatefulSet使用场景:

  稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于 PVC 来实现。
  稳定的网络标识符,即 Pod 重新调度后其 PodName 和 HostName 不变。
  有序部署,有序扩展,基于 init containers 来实现。
  有序收缩。

 

statefulset名称:

kubectl get pod -o wide
kubectl get svc

 

容器内需要用到ping,测试效果:

kubectl exec test-pd -it -- /bin/sh

 

 

 

StatefulSet的Headless地址:

 

 

kubectl get pod -o wide -n kube-system

 

 

dig -t A  nginx.default.svc.cluster.local. @10.244.0.7 

解析成功

 

启停顺序:

kubectl delete statefulset --all

 

kubectl get pod -w

 

 

kubectl create -f pv/pod.yaml 

 

 

 

测试访问

 

kubectl delete -f pv/pod.yaml

 

 

[root@k8s-master01 pv]# kubectl get statefullset 

 

[root@k8s-master01 pv]# kubectl get svc

 

kubectl delete svc nginx
kubectl get pv

 

kubectl get pvc

 

pod删除pvc并不会跟随删除

kubectl delete pvc --all
kubectl get pvc

 

kubectl get pv

 

状态已经变成Released(已释放)——声明被删除,但是资源还未被集群重新声明,之前是Bound(已绑定)——卷已经被声明绑定

先去删除数据

 

kubectl get pv

 

还是Released

kubectl get pv nfspv1 -o yaml

nfspv并不会检查文件数据,但是pv依然有使用者信息

 

手动回收

kubectl edit pv nfspv1

删除选中行

 

kubectl get pv

状态已经变成Available(可用)——一块空闲资源还没有被任何声明绑定

 

 

8、集群调度

 

8.1、Kubernetes 调度器 - 调度说明

8.2、Kubernetes 调度器 - 调度亲和性

8.3、Kubernetes 调度器 - 污点

8.4、Kubernetes 调度器 - 固定节点

 

8.1 Kubernetes 调度器 - 调度说明

 

简介
Scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。听起来非常简单,但有很多要考虑的问题:
  公平:如何保证每个节点都能被分配资源
  资源高效利用:集群所有资源最大化被使用
  效率:调度的性能要好,能够尽快地对大批量的 pod 完成调度工作
  灵活:允许用户根据自己的需求控制调度的逻辑
 Sheduler 是作为单独的程序运行的,启动之后会一直坚挺 API Server,获取 PodSpec.NodeName 为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上

调度过程
  调度分为几个部分:首先是过滤掉不满足条件的节点,这个过程称为 predicate ;然后对通过的节点按照优先级排序,这个是 priority ;最后从中选择优先级最高的节点。如果中间任何一步骤有错误,就直接返回错误
 Predicate 有一系列的算法可以使用:
  PodFitsResources :节点上剩余的资源是否大于 pod 请求的资源
  PodFitsHost :如果 pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
  PodFitsHostPorts :节点上已经使用的 port 是否和 pod 申请的 port 冲突
  PodSelectorMatches :过滤掉和 pod 指定的 label 不匹配的节点
  NoDiskConflict :已经 mount 的 volume 和 pod 指定的 volume 不冲突,除非它们都是只读
 如果在 predicate 过程中没有合适的节点,pod 会一直在 pending 状态,不断重试调度,直到有节点满足条件。经过这个步骤,如果有多个节点满足条件,就继续 priorities 过程: 按照优先级大小对节点排序

 优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。这些优先级选项包括:
  LeastRequestedPriority :通过计算 CPU 和 Memory 的使用率来决定权重,使用率越低权重越高。换句话说,这个优先级指标倾向于资源使用比例更低的节点
  BalancedResourceAllocation :节点上 CPU 和 Memory 使用率越接近,权重越高。这个应该和上面的一起使用,不应该单独使用
  ImageLocalityPriority :倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高通过算法对所有的优先级项目和权重进行计算,得出最终的结果

自定义调度器
  除了 kubernetes 自带的调度器,你也可以编写自己的调度器。通过 spec:schedulername 参数指定调度器的名字,可以为 pod 选择某个调度器进行调度。比如下面的 pod 选择 my-scheduler 进行调度,而不是默认的
  default-scheduler :

apiVersion: v1
kind: Pod
metadata:
  name: annotation-second-scheduler
  labels:
    name: multischeduler-example
spec:
  schedulername: my-scheduler
  containers:
  - name: pod-with-second-annotation-container
    image: gcr.io/google_containers/pause:2.0

 

 

8.2 Kubernetes 调度器 - 调度亲和性

  通过配置可以让不同pod处于同一节点或者不同节点

 

8.1.1 节点亲和性

pod.spec.nodeAffinity
  preferredDuringSchedulingIgnoredDuringExecution:软策略
  requiredDuringSchedulingIgnoredDuringExecution:硬策略

 

查看键名

kubectl get node --show-labels

 

 

requiredDuringSchedulingIgnoredDuringExecution

硬策略:

测试一下NotIn效果:

[root@k8s-master01 ~]# mkdir affi
[root@k8s-master01 ~]# cd affi
[root@k8s-master01 affi]# vim pod1.yaml

这里的匹配关系是NotIn,意思是只要不是node02就可以运行,排除了node02

 

kubectl create -f pod1.yaml
kubectl get pod -o wide

节点在01上

 

kubectl delete pod --all && kubectl create -f pod1.yaml && kubectl get pod -o wide

 

改成运算关系改成In

vim pod1.yaml

 

kubectl delete pod --all && kubectl create -f pod1.yaml && kubectl get pod -o wide
kubectl delete pod --all && kubectl create -f pod1.yaml && kubectl get pod -o wide  

只运行在02节点

 

改成03节点

 

 

kubectl delete pod --all && kubectl create -f pod1.yaml && kubectl get pod -o wide  

 

状态不对

 

kubectl describe pod affinity

由于没有03节点,并且是硬策略,所以坚持在03上运行,最终报节点不匹配。

 


preferredDuringSchedulingIgnoredDuringExecution

软策略:

查看键名

kubectl get node --show-labels

 

vim pod3.yaml 

 

这里配置的软策略期望hostname in 节点名node03

kubectl delete pod --all
kubectl create -f pod3.yaml && kubectl get pod -o wide

 

没有03软策略也会在其他节点运行

改成01

权重值是遇到重复时的优先权 0~100

 

kubectl delete -f pod3.yaml && kubectl create -f pod3.yaml && kubectl get pod -o wide

 

发现01优先,但是也有在02上运行的

改成硬策略:

 

kubectl delete -f pod3.yaml && kubectl create -f pod3.yaml && kubectl get pod -o wide

全都在node01上运行了 

 

kubectl delete -f pod3.yaml

 

合体:

node节点不多,演示效果不太好

vim pod5.yaml

 

排除02节点,选择其他节点运行,并优先选择01

由于只有2个节点排除掉02以后只剩下01了

 

kubectl delete -f pod5.yaml

 

键值运算关系
  In:label 的值在某个列表中
  NotIn:label 的值不在某个列表中
  Gt:label 的值大于某个值
  Lt:label 的值小于某个值
  Exists:某个 label 存在
  DoesNotExist:某个 label 不存在

 

Pod 亲和性

 pod.spec.affinity.podAffinity/podAntiAffinity
  preferredDuringSchedulingIgnoredDuringExecution:软策略
  requiredDuringSchedulingIgnoredDuringExecution:硬策略

 

创建一个pod

vim pod6.yaml

 

kubectl get pod -o wide --show-labels

 

演示pod亲和性:

vim pod7.yaml

 

kubectl create -f pod7.yaml
kubectl get pod -o wide --show-labels

在同一拓扑域

 

修改不亲和

kubectl delete -f pod7.yaml
vim pod7.yaml

  

podAffinity改成podAntiAffinity

kubectl create -f pod7.yaml
kubectl get pod -o wide

由于是podAntiAffinity和硬限制,结果不在同一节点运行

 

kubectl delete -f pod7.yaml

 

如果是硬限制匹配labels不存在的话:

kubectl get pod -o wide --show-labels

 

vim pod7.yaml

 

 

 

kubectl get pod -o wide --show-labels 

等待

 

describe pod pod-3

提示2个节点都不匹配运行

 

测试效果,改一下标签名成匹配的node02

kubectl label pod node01 app=node02

提示已经存在并且当前--overwrite is false

 

kubectl label pod node01 app=node02 --overwrite=True
kubectl get pod -o wide --show-labels

匹配到了,running了,节点也一样

 

亲和性/反亲和性调度策略比较如下:

 

8.3 Kubernetes 调度器 - 污点

 

Taint 和 Toleration


 节点亲和性,是 pod 的一种属性(偏好或硬性要求),它使 pod 被吸引到一类特定的节点。Taint 则相反,它使节点 能够 排斥 一类特定的 pod

 Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个taint ,这表示对于那些不能容忍这些Taint 的 pod,是不会被该节点接受的。如果将 toleration 应用于 pod上,则表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上

 

污点(Taint)


I、 污点 ( Taint ) 的组成
 使用 kubectl taint 命令可以给某个 Node 节点设置污点,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去
 每个污点的组成如下:

  key=value:effect


 每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用。当前 tainteffect 支持如下三个选项:
  NoSchedule :表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上
  PreferNoSchedule :表示 k8s 将尽量避免将 Pod 调度到具有该污点的 Node 上
  NoExecute :表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已经存在的 Pod 驱逐出去

 

kubectl get node

查看mster的污点配置

kubectl describe node k8s-master01

pod不会在master上运行,因为已经配置了污点

 

II、污点的设置、查看和去除

 

kubectl get pod -o wide

 

设置污点

kubectl taint nodes k8s-node01 check=yyq:NoExecute (kubectl taint nodes node1 key1=value1:NoSchedule)

 

由于是单独的pod被删除以后就没有了,如果是deployment控制的Pod会在node02上再起来

 

如果把02也设置污点

kubectl taint nodes k8s-node02 check=yyq:NoExecute
kubectl create -f pod1.yaml
kubectl get pod

 

 容忍污点

  vim pod8.yaml

  

  这里设置了之前污点命令的参数和值,并且tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Pod 上继续保留运行的时间

 

  

  配置了容忍已经可以运行起来了

 

  kubectl delete pod pod-3

  有多个 Master 存在时,防止资源浪费,可以如下设置 

  kubectl taint nodes Node-Name node-ole.kubernetes.io/master=:PreferNoSchedule
  kubectl get node

  

 

 

   kubectl taint nodes k8s-master01 node-role.kubernetes.io/master=:PreferNoSchedule

  

   调整成尽可能不在master01上运行,也就是说如果node节点资源不够用或者不可用,会在master01上运行。

 

  vim pod9.yaml

  

 

  kubectl create -f pod9.yaml
  kubectl get pod

  

  和预想结果不一样还是没有运行,查看问题

  

  kubectl describe pod pod-3

  

  提示3个节点pod都无法容忍

 

  查看master01节点污点设置

  kubectl describe node k8s-master01

  

  配置无法替换,叠加了

 

  把NoSchedule污点去除

  kubectl taint nodes k8s-master01 node-role.kubernetes.io/master=:NoSchedule-

 

  再查看

  kubectl describe node k8s-master01

  

 

   kubectl get pod -o wide

  

  由于matser之前没有下载过镜像,并且镜像仓库目前权限是私人,而且没有配置Secret

 

  docker login hub.yyq.com
  docker pull hub.yyq.com/library/mynginx:v1
  kubectl get pod  -o wide

  

 

  清除污点

  taint nodes k8s-node01 check=yyq:NoExecute-
  taint nodes k8s-node02 check=yyq:NoExecute-
  kubectl describe node k8s-node01
  kubectl describe node k8s-node02

  

 

 

 

8.2.2 容忍(Tolerations)

 

  设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。 但我们可以在 Pod 上设置容忍 ( Toleration ) ,意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上
pod.spec.tolerations

tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
tolerationSeconds: 3600

  

- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"

 

- key: "key2"
operator: "Exists"
effect: "NoSchedule"

  其中 key, vaule, effect 要与 Node 上设置的 taint 保持一致

  operator 的值为 Exists 将会忽略 value 值
  tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Pod 上继续保留运行的时间
I、当不指定 key 值时,表示容忍所有的污点 key:

tolerations:
- operator: "Exists"


II、当不指定 effect 值时,表示容忍所有的污点作用

tolerations:
- key: "key"
  operator: "Exists"


III、有多个 Master 存在时,防止资源浪费,可以如下设置

kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule

 

 

8.4 Kubernetes 调度器 - 固定节点

  指定节点名称或者label来进行强制选择。

 

指定调度节点

I、Pod.spec.nodeName 将 Pod 直接调度到指定的 Node 节点上,会跳过 Scheduler 的调度策略,该匹配规则是强制匹配

 

mkdir /root/nod
cd /root/node
vim pod1.yaml

 

配置根据节node01点选择

 

kubectl apply -f pod1.yaml
kubectl get pod -o wide

 

 

II、Pod.spec.nodeSelector:通过 kubernetes 的 label-selector 机制选择节点,由调度器调度策略匹配 label,而后调度 Pod 到目标节点,该匹配规则属于强制约束

 

vim pod2.yaml

定义了只在disk:ssd的节点上运行

 

kubectl delete deployment --all
kubectl delete pod --all
kubectl apply -f pod2.yaml
kubectl get pod

由于不满足disk:ssd的条件,没有运行

 

通过修改标签完成测试

kubectl label node k8s-node01 disk=ssd

 

kubectl get pod

 

把node02也改成ssd

kubectl label node k8s-node02 disk=ssd

 

增加副本数

kubectl get deployment
kubectl edit deployment myweb11

 

kubectl get pod -o wide

 

 

9、安全

  9.1机制说明

  9.2认证

  9.3鉴权

  9.4准入控制

 

9.1 Kubernetes 集群安全 - 机制说明

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

 

 

9.2 Kubernetes 集群安全 - 认证

 

Authentication

 HTTP Token 认证:通过一个 Token 来识别合法用户
  HTTP Token 的认证是用一个很长的特殊编码方式的并且难以被模仿的字符串 - Token 来表达客户的一种方式。Token 是一个很长的很复杂的字符串,每一个 Token 对应一个用户名存储在 API Server 能访问的文件中。当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token
 HTTP Base 认证:通过 用户名+密码 的方式认证
  用户名+:+密码 用 BASE64 算法进行编码后的字符串放在 HTTP Request 中的 HeatherAuthorization 域里发送给服务端,服务端收到后进行编码,获取用户名及密码
 最严格的 HTTPS 证书认证:基于 CA 根证书签名的客户端身份认证方式(双向)

 

I、HTTPS 证书认证:

 

II、需要认证的节点

 

两种类型
  Kubenetes 组件对 API Server 的访问:kubectl、Controller Manager、Scheduler、kubelet、kube-proxy
  Kubernetes 管理的 Pod 对容器的访问:Pod(dashborad 也是以 Pod 形式运行)
安全性说明
  Controller Manager、Scheduler 与 API Server 在同一台机器,所以直接使用 API Server 的非安全端口访问, --insecure-bind-address=127.0.0.1
  kubectl、kubelet、kube-proxy 访问 API Server 就都需要证书进行 HTTPS 双向认证
证书颁发
  手动签发:通过 k8s 集群的跟 ca 进行签发 HTTPS 证书
  自动签发:kubelet 首次访问 API Server 时,使用 token 做认证,通过后,Controller Manager 会为kubelet 生成一个证书,以后的访问都是用证书做认证了


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

 

[root@k8s-master01 node]# cat ~/.kube/config

包含了集群的访问方式和认证信息

  

 

IV、ServiceAccount

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

V、Secret 与 SA 的关系
  Kubernetes 设计了一种资源对象叫做 Secret,分为两类,一种是用于 ServiceAccount 的 service-account-token, 另一种是用于保存用户自定义保密信息的 Opaque。ServiceAccount 中用到包含三个部分:Token、ca.crt、namespace
  token是使用 API Server 私钥签名的 JWT。用于访问API Server时,Server端认证
  ca.crt,根证书。用于Client端验证API Server发送的证书

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

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

 

  kubectl get secret --all-namespaces
  kubectl describe secret default-token-5gm9r --namespace=kube-syste

默认情况下,每个 namespace 都会有一个 ServiceAccount,如果 Pod 在创建时没有指定 ServiceAccount,就会使用 Pod 所属的 namespace 的 ServiceAccount

  <!--默认挂载目录:/run/secrets/kubernetes.io/serviceaccount/-->

 

  kubectl get pod -n kube-system

  

 

  kubectl exec kube-proxy-m5fw7 -n kube-system -it -- /bin/sh
  ls /run/secrets/kubernetes.io/serviceaccount

  

  
总结

 

 

 

9.3 Kubernetes 集群安全 - 鉴权

 

!---->

Authorization


 上面认证过程,只是确认通信的双方都确认了对方是可信的,可以相互通信。而鉴权是确定请求方有哪些资源的权限。API Server 目前支持以下几种授权策略 (通过 API Server 的启动参数 “--authorization-mode” 设置)
  AlwaysDeny:表示拒绝所有的请求,一般用于测试
  AlwaysAllow:允许接收所有请求,如果集群不需要授权流程,则可以采用该策略
  ABAC(Attribute-Based Access Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制(太麻烦已经淘汰了)
  Webbook:通过调用外部 REST 服务对用户进行授权
  RBAC(Role-Based Access Control):基于角色的访问控制,现行默认规则

RBAC 授权模式
 RBAC(Role-Based Access Control)基于角色的访问控制,在 Kubernetes 1.5 中引入,现行版本成为默认标准。相对其它访问控制方式,拥有以下优势:
  对集群中的资源和非资源均拥有完整的覆盖
  整个 RBAC 完全由几个 API 对象完成,同其它 API 对象一样,可以用 kubectl 或 API 进行操作
  可以在运行时进行调整,无需重启 API Server


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

 

需要注意的是 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"
    }
  ]
}

 

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

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

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

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

 

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

 

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 具有与 Role 相同的权限角色控制能力,不同的是 ClusterRole 是集群级别的,ClusterRole 可以用于:
  集群级别的资源控制( 例如 node 访问权限 )
  非资源型 endpoints( 例如 /healthz 访问 )
  所有命名空间资源控制(例如 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"]

 

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

 

  RoleBinding 同样可以引用 ClusterRole 来对当前 namespace 内用户、用户组或 ServiceAccount 进行授权,这种操作允许集群管理员在整个集群内定义一些通用的 ClusterRole,然后在不同的 namespace 中使用RoleBinding 来引用
  例如,以下 RoleBinding 引用了一个 ClusterRole,这个 ClusterRole 具有整个集群内对 secrets 的访问权限;但是其授权用户 dave 只2能访问 development 空间中的 secrets(因为 RoleBinding 定义在 development 命名空间)

# This role binding allows "dave" to read secrets in the "development" namespace.
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: read-secrets
  namespace: development # 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

 

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

# This cluster role binding allows anyone in the "manager" group to read secrets in any
namespace.
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


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

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


  如果要在 RBAC 授权模型中控制这些子资源的访问权限,可以通过 / 分隔符来实现,以下是一个定义 pods 资资源logs 访问权限的 Role 定义样例

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

  

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

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

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

 

实践:创建一个用户只能管理 dev 空间

[root@k8s-master01 ~]# useradd devuser
[root@k8s-master01 ~]# passwd devuser
ssh devuser@192.168.66.10
[devuser@k8s-master01 ~]$ kubectl get pod
The connection to the server localhost:8080 was refused - did you specify the right host or port?

 

没有权限,创建证书请求:

cd /usr/local/install-k8s/cert/devuser
vim devuser-csr.json

  

cd /usr/local/bin/

 

生成证书

cd /etc/kubernetes/pki/
cfssl gencert -ca=ca.crt -ca-key=ca.key -profile=kubernetes /usr/local/install-k8s/cert/devuser/devuser-csr.json | cfssljson -bare devuser

 

ls

 

 

 

# 设置集群参数
cd /usr/local/install-k8s/cert/devuser/

# 声明环境变量
export KUBE_APISERVER="https://192.168.66.10:6443"

# 设置集群、指定CA证书、是否加密、服务器信息、创建配置文件
kubectl config set-cluster kubernetes \
--certificate-authority=/etc/kubernetes/pki/ca.crt \
--embed-certs=true \
--server=${KUBE_APISERVER} \
--kubeconfig=devuser.kubeconfig

 

# 设置客户端认证参数

创建devuser证书、指定客户端证书、客户端私钥、开启认证方式、写入文件
kubectl config set-credentials devuser \
--client-certificate=/etc/kubernetes/pki/devuser.pem \
--client-key=/etc/kubernetes/pki/devuser-key.pem \
--embed-certs=true \
--kubeconfig=devuser.kubeconfig

cat devuser.kubeconfig

多了用户名、用户证书、用户私钥信息
# 设置上下文参数

创建名称空间
kubectl create namespace dev
kubectl config set-context kubernetes \
--cluster=kubernetes \
--user=devuser \
--namespace=dev \
--kubeconfig=devuser.kubeconfig

cat devuser.kubeconfig 多了namespace和用户名信息
# 设置默认上下文 创建一个rolebinding,devuser可以在dev名称空间下有管理员权限 kubectl create rolebinding devuser-admin-binding --clusterrole=admin --user=devuser --namespace=dev

 

 

[devuser@k8s-master01 ~]$ mkdir .kube
[root@k8s-master01 devuser]# cp devuser.kubeconfig /home/devuser/.kube/config
chown devuser:devuser /home/devuser/.kube/config

[devuser@k8s-master01 .kube]$  kubectl config use-context kubernetes --kubeconfig=config

 

 

 

kubectl get pod
kubectl run nginx --image=hub.yyq.com/library/mynginx:v1
kubectl get pod

 

 

 

kubectl get pod --all-namespaces -o wide | grep nginx 

 

kubectl get pod -n default 

只定义了dev没有定义default无法访问。

 

9.4 Kubernetes 集群安全 - 准入控制

 

准入控制


 准入控制是API Server的插件集合,通过添加不同的插件,实现额外的准入控制规则。甚至于API Server的一些主要的功能都需要通过 Admission Controllers 实现,比如 ServiceAccount
 官方文档上有一份针对不同版本的准入控制器推荐列表,其中最新的 1.14 的推荐列表是:

NamespaceLifecycle,
LimitRanger,
ServiceAccount,
DefaultStorageClass,
DefaultTolerationSeconds,
MutatingAdmissionWebhook,
ValidatingAdmissionWebhook,
ResourceQuota

 

 列举几个插件的功能:
  NamespaceLifecycle: 防止在不存在的 namespace 上创建对象,防止删除系统预置 namespace,删除namespace 时,连带删除它的所有资源对象。
  LimitRanger:确保请求的资源不会超过资源所在 Namespace 的 LimitRange 的限制
  ServiceAccount: 实现了自动化添加 ServiceAccount。
  ResourceQuota:确保请求的资源不会超过资源的 ResourceQuota 限制。

 

10、Helm及其它功能性组件

 

helm命令详解 

http://www.coderdocument.com/docs/helm/v2/helm_commands/helm_repo_add.html#helm-repo-add

 

10.1 部署 Helm

  包管理工具(类linux yum)下yaml文件

10.2 使用 Helm 部署 dashboard

10.3 使用 Helm 部署 metrics-server

10.4 部署 prometheus

10.5 部署 EFK 平台

 

10.1 部署 Helm

  

https://github.com/helm/helm/blob/master/docs/charts.md
什么是 Helm


 在没使用 helm 之前,向 kubernetes 部署应用,我们要依次部署 deployment、svc 等,步骤较繁琐。况且随着很多项目微服务化,复杂的应用在容器中部署以及管理显得较为复杂,helm 通过打包的方式,支持发布的版本管理和控制,很大程度上简化了 Kubernetes 应用的部署和管理
 Helm 本质就是让 K8s 的应用管理(Deployment,Service 等 ) 可配置,能动态生成。通过动态生成 K8s 资源清单文件(deployment.yaml,service.yaml)。然后调用 Kubectl 自动执行 K8s 资源部署
 Helm 是官方提供的类似于 YUM 的包管理器,是部署环境的流程封装。Helm 有两个重要的概念:chart 和release
  chart 是创建一个应用的信息集合,包括各种 Kubernetes 对象的配置模板、参数定义、依赖关系、文档说明等。chart 是应用部署的自包含逻辑单元。可以将 chart 想象成 apt、yum 中的软件安装包
  release 是 chart 的运行实例,代表了一个正在运行的应用。当 chart 被安装到 Kubernetes 集群,就生成一个 release。chart 能够多次安装到同一个集群,每次安装都是一个 release
 Helm 包含两个组件:Helm 客户端和 Tiller 服务器,如下图所示

  

 

 

 Helm 客户端负责 chart 和 release 的创建和管理以及和 Tiller 的交互。Tiller 服务器运行在 Kubernetes 集群中,它会处理 Helm 客户端的请求,与 Kubernetes API Server 交互

Helm 部署
 越来越多的公司和团队开始使用 Helm 这个 Kubernetes 的包管理器,我们也将使用 Helm 安装 Kubernetes 的常用组件。 Helm 由客户端命 helm 令行工具和服务端 tiller 组成,Helm 的安装十分简单。 下载 helm 命令行工具到master 节点 node1 的 /usr/local/bin 下,这里下载的 2.13. 1版本:

ntpdate ntp1.aliyun.com
wget https://storage.googleapis.com/kubernetes-helm/helm-v2.13.1-linux-amd64.tar.gz
tar -zxvf helm-v2.13.1-linux-amd64.tar.gz
cd linux-amd64/
cp helm /usr/local/bin/


 为了安装服务端 tiller,还需要在这台机器上配置好 kubectl 工具和 kubeconfig 文件,确保 kubectl 工具可以在这台机器上访问 apiserver 且正常使用。 这里的 node1 节点以及配置好了 kubectl
 因为 Kubernetes APIServer 开启了 RBAC 访问控制,所以需要创建 tiller 使用的 service account: tiller 并分配合适的角色给它。 详细内容可以查看helm文档中的 Role-based Access Control。 这里简单起见直接分配cluster- admin 这个集群内置的 ClusterRole 给它。创建 rbac-config.yaml 文件:

 

[root@k8s-master01 helm]# vim rbac.yaml

 

kubectl create -f rbac.yaml

 

helm init --service-account tiller --skip-refresh

 

tiller 默认被部署在 k8s 集群中的 kube-system 这个namespace 下

kubectl get pod -n kube-system -l app=helm

 

kubectl describe pod tiller-deploy-58565b5464-f55cv -n kube-system

 

docker pull gcr.io/kubernetes-helm/tiller:v2.13.1 
或者
docker load -i helm-tiller.tar
kubectl get pod -n kube-system -l app=helm

 

helm version

 

自己找helm如reids:

访问 hub.helm.sh

 

Helm 自定义模板

 

# 创建文件夹
[root@k8s-master01 helm]# mkdir test && cd test

 

# 创建自描述文件 Chart.yaml , 这个文件必须有 name 和 version 定义
$ cat <<'EOF' > ./Chart.yaml
name: hello-world
version: 1.0.0
EOF

 

# 创建模板文件, 用于生成 Kubernetes 资源清单(manifests)
$ mkdir ./templates

 

vim ./deployment.yaml

 

 

vim ./templates/service.yaml 

 

# 使用命令 helm install RELATIVE_PATH_TO_CHART 创建一次Release
helm install .

 

# 列出已经部署的 Release
helm ls 

 

 

helm upgrade tinseled-dolphinhelm upgrade tinseled-dolphin . 
helm list 

 

helm  history tinseled-dolphin

 

也可以通过helm看

# 查询一个特定的 Release 的状态
helm  status tinseled-dolphin

 

浏览器访问:192.168.66.10:31586

 

# 配置体现在配置文件 values.yaml
[root@k8s-master01 test]# vim values.yaml

 

# 这个文件中定义的值,在模板文件中可以通过 .VAlues对象访问到
$ vim ./templates/deployment.yaml

 

helm list
helm upgrade tinseled-dolphin .

 

 

kubectl get pod 

 

刷新网页

已经更新版本

 

# 返回v1版本
helm upgrade tinseled-dolphin --set image.tag='v1' .

 

其他命令:

# 移除所有与这个 Release 相关的 Kubernetes 资源
#$ helm delete tinseled-dolphin

 

再重新创建
helm install --name tinseled-dolphin .

提示已存在

 

helm list --deleted

还有保存的信息,默认保存 用于恢复,回滚:

 

helm rollback tinseled-dolphin 8

 

浏览器访问:192.168.66.10:31344

版本变了,端口变了

 

[root@k8s-master01 test]# helm rollback tinseled-dolphin 4

 

# 如果想要全删除需要添加 --perge参数
# 使用 helm delete --purge RELEASE_NAME 移除所有与指定 Release 相关的 KubernetesRelease 的记录
helm delete --purge tinseled-dolphin
helm list --deleted

历史已空

 

Debug
# 使用模板动态生成K8s资源清单,非常需要能提前预览生成的结果。
# 使用--dry-run --debug 选项来打印出生成的清单文件内容,而不执行部署

helm install . --dry-run --debug --set image.tag=latest

 

helm install --dry-run .
helm list

 

10.2 使用 Helm 部署 dashboard

 

使用Helm部署 dashboard
cd /usr/local/install-k8s/plugin/dashboard
下载远程安装包到本地。
helm fetch stable/kubernetes-dashboard

 

  

提示repo更新

 helm repo update

 

遇到Unable to get an update from the “stable” chart repository (https://kubernetes-charts.storage.googleapis.com) 错误

 

手动更换stable 存储库为阿里云的存储库
# 先移除原先的仓库
helm repo remove stable
# 添加新的仓库地址
helm repo add stable https://kubernetes.oss-cn-hangzhou.aliyuncs.com/charts
# 更新仓库
helm repo update

 

查看已有仓库

helm repo list

 

helm fetch stable/kubernetes-dashboard
vim kubernetes-dashboard.yaml

 

helm install . \
-n kubernetes-dashboard \
--namespace kube-system \
-f kubernetes-dashboard.yaml
kubectl -n kube-system get secret | grep kubernetes-dashboard-token
scp dashboard.tar root@192.168.66.20:/root/
scp dashboard.tar root@192.168.66.21:/root/
kubectl delete pod kubernetes-dashboard-79599d7b8d-ms9tb -n kube-system
kubectl get svc -n kube-system

 

kubectl edit svc -n kube-system kubernetes-dashboard

 

修改 ClusterIP 为 NodePort
[root@k8s-master01 kubernetes-dashboard]# kubectl get svc -n kube-system

 

浏览器访问https://192.168.66.10:32756

 

浏览器非可信CA不可访问

可以把证书导入本机 sz /etc/kubernetes/pki/ca.crt 或者使用火狐浏览器访问

接受

 

 

查看 token令牌
kubectl -n kube-system get secret | grep kubernetes-dashboard-token
kubectl describe secret kubernetes-dashboard-token-h8tl4 -n kube-system

 

复制

 

 

 

 点击创建部署新应用

 

点击部署 

 

 

kubectl get pod -n kube-system

 

 

10.3 使用 Helm 部署 metrics-server

  prometheus集成了 metrics-server可跳过直接部署9.4 prometheus

 

使用Helm部署metrics-server
  从 Heapster 的 github <https://github.com/kubernetes/heapster >中可以看到已经,heapster 已经DEPRECATED。这里是 heapster的deprecation timeline。 可以看出 heapster 从 Kubernetes 1.12 开始将从 Kubernetes 各种安装脚本中移除。Kubernetes 推荐使用 metrics-server。我们这里也使用helm来部署metrics-server。
metrics-server.yaml:

args:
- --logtostderr
- --kubelet-insecure-tls
- --kubelet-preferred-address-types=InternalIP

 

helm install stable/metrics-server \
-n metrics-server \
--namespace kube-system \
-f metrics-server.yaml

 

使用下面的命令可以获取到关于集群节点基本的指标信息:
kubectl top node

 

10.4 部署 prometheus

 

相关地址信息
Prometheus github 地址:https://github.com/coreos/kube-prometheus

组件说明
  1.MetricServer:是kubernetes集群资源使用情况的聚合器,收集数据给kubernetes集群内使用,如kubectl,hpa,scheduler等。

  2.PrometheusOperator:是一个系统监测和警报工具箱,用来存储监控数据。

  3.NodeExporter:用于各node的关键度量指标状态数据。

  4.KubeStateMetrics:收集kubernetes集群内资源对象数据,制定告警规则。

  5.Prometheus:采用pull方式收集apiserver,scheduler,controller-manager,kubelet组件数据,通过http协议传输。

  6.Grafana:是可视化数据统计和监控平台。


构建记录

git clone https://github.com/coreos/kube-prometheus.git
cd kube-prometheus/manifests 

 

修改 grafana-service.yaml 文件,使用 nodepode 方式访问 grafana:

vim grafana-service.yaml
修改:
type: NodePort
nodePort: 30100

 

修改 prometheus-service.yaml 文件,使用 nodepode 方式访问 grafana:

vim prometheus-service.yaml
修改:
type: NodePort
nodePort: 30200

 

修改 alertmanager-service.yaml 文件,使用 nodepode 方式访问 grafana:

vim alertmanager-service.yaml
修改:
type: NodePort
nodePort: 30300

 

 

导入镜像

/usr/local/install-k8s/plugin/prometheus
tar -zxvf prometheus.tar.gz
cat load-images.sh

#!/bin/bash cd /root/prometheus ls /root/prometheus | grep -v load-images.sh > /tmp/k8s-images.txt for i in $( cat /tmp/k8s-images.txt ) do docker load -i $i done rm -rf /tmp/k8s-images.txt

 

[root@k8s-master01 prometheus]# mv prometheus load-images.sh  /root/
cd
chmod a+x load-images.sh
./load-images.sh

 

 scp -r prometheus/ load-images.sh root@k8s-node01:/root
 scp -r prometheus/ load-images.sh root@k8s-node02:/root
.[root@k8s-node01 ~]# ./load-images.sh 
.[root@k8s-node02 ~]# ./load-images.sh 

  

 

cd /usr/local/install-k8s/plugin/prometheus/kube-prometheus/manifests
kubectl apply -f ../manifests/

由于会互相连接多执行几次

如果遇到报错namespaces "monitoring" not found 就执行一遍 kubectl apply -f ../manifests/setup/ 再执行 kubectl apply -f ../manifests/

ntpdate ntp1.aliyun.com
kubectl get pod -n monitoring
kubectl get svc --all-namespaces
其中k8s.gcr.io/addon-resizer:1.8.4镜像被墙,无法下载,所以需要先通过阿里云下载下来,再传入本地镜像仓库
docker pull registry.cn-beijing.aliyuncs.com/minminmsn/addon-resizer:1.8.4
docker tag registry.cn-beijing.aliyuncs.com/minminmsn/addon-resizer:1.8.4 k8s.gcr.io/addon-resizer:1.8.4
kubectl get svc --all-namespaces
kubectl get pod -n monitoring -o wideipvsadm -Ln | grep -E "192.168.66.10:30300|192.168.66.10:30200|192.168.66.10:30100" -A 3

 

kubectl top node

 

top pod -n kube-system

 

浏览器访问prometheus收集端

192.168.66.10:30200

通过访问 http://MasterIP:30200/target 可以看到 prometheus 已经成功连接上了 k8s 的 apiserver

 

 

 

prometheus 的 WEB 界面上提供了基本的查询 K8S 集群中每个 POD 的 CPU 使用情况,查询条件如下:
sum by (pod_name)( rate(container_cpu_usage_seconds_total{image!="", pod_name!=""}[1m] ) )

 

 

kubectl get service -n monitoring | grep grafana

浏览器访问grafana

http://MasterIP:30100

如上可以看到 grafana 的端口号是 30100, 用户名密码默认 admin/admin

首次登陆修改密码

 

 

 

 

自带了很多模板 

 

Horizontal Pod Autoscaling

 Horizontal Pod Autoscaling 可以根据 CPU 利用率自动伸缩一个 Replication Controller、Deployment 或者Replica Set 中的 Pod 数量

  创建一个apache,hpa限制请求资源cpu 200m

  kubectl run php-apache --image=gcr.io/google_containers/hpa-example --requests=cpu=200m --expose --port=80

 

 网络问题无法pull 
  docker tag gcr.io/google_containers/hpa-example:latest gcr.io/google_containers/hpa-example:v1
  kubectl run php-apache --image=gcr.io/google_containers/hpa-example:v1 --requests=cpu=200m --expose --port=80

  [root@k8s-master01 prometheus]# kubectl top pod php-apache-5bb5df8bd4-lb86l

 

创建 HPA 控制器 - 相关算法的详情请参阅这篇文档

CPU超过50%自动创建,最多10个
kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10

  

kubectl get hpa

 

增加负载,查看负载节点数目
kubectl run -i --tty load-generator --image=busybox /bin/sh
while true; do wget -q -O- http://php-apache.default.svc.cluster.local; done

 

kubectl get hpa -w 

 

kubectl get pod -o wide

 

已经扩展满了 

关闭压力测试,等待cpu下来,然后pod会减少到1个


资源限制 - Pod
 Kubernetes 对资源的限制实际上是通过 cgroup 来控制的,cgroup 是容器的一组用来控制内核如何运行进程的相关属性集合。针对内存、CPU 和各种设备都有对应的 cgroup
 默认情况下,Pod 运行没有 CPU 和内存的限额。 这意味着系统中的任何 Pod 将能够像执行该 Pod 所在的节点一样,消耗足够多的 CPU 和内存 。一般会针对某些应用的 pod 资源进行资源限制,这个资源限制是通过resources 的 requests 和 limits 来实现

  requests相当于软限制,limits相当于硬限制

spec:
containers:
- image: xxxx
imagePullPolicy: Always
name: auth
ports:
- containerPort: 8080
protocol: TCP
resources:
limits:
cpu: "4"
memory: 2Gi
requests:
cpu: 250m
memory: 250Mi

requests 要分分配的资源,limits 为最高请求的资源值。可以简单理解为初始值和最大值


资源限制 - 名称空间
I、计算资源配额

apiVersion: v1
kind: ResourceQuota   # 资源配额
metadata:
name: compute-resources   #名称
namespace: spark-cluster   #名称空间
spec:
hard:
pods: "20"          #能够创建的Pod数量
requests.cpu: "20"    #能够使用的requests cpu20个
requests.memory: 100Gi  #内存
limits.cpu: "40"   
limits.memory: 200Gi


II、配置对象数量配额限制

apiVersion: v1
kind: ResourceQuota
metadata:
name: object-counts
namespace: spark-cluster
spec:
hard:
configmaps: "10" 
persistentvolumeclaims:"4"  #PVC
replicationcontrollers:"20"    #RC
secrets: "10"
services: "10"
services.loadbalancers:"2"  #基于云服务器负载的方案

 

III、配置 CPU 和 内存 LimitRange

apiVersion: v1
kind: LimitRange
metadata:
name: mem-limit-range
spec:
limits:
- default:
memory: 50Gi
cpu: 5
defaultRequest:
memory: 1Gi
cpu: 1
type: Container
default 即 limit 的值
defaultRequest 即 request 的值

 

10.5 部署 EFK 平台

 

 

[root@k8s-master01 containers]# cd /usr/local/install-k8s/
[root@k8s-master01 install-k8s]# mkdir efk && cd efk
helm fetch incubator/elasticsearch

 

添加 Google incubator 仓库

helm repo add incubator http://storage.googleapis.com/kubernetes-charts-incubator


部署 Elasticsearch

kubectl create namespace efk
helm fetch incubator/elasticsearch
helm install --name els1 --namespace=efk -f values.yaml incubator/elasticsearch
kubectl run cirror-$RANDOM --rm -it --image=cirros -- /bin/sh
  curl Elasticsearch:Port/_cat/nodes

 

部署 Fluentd (未使用Logstash因为Fluentd使用GO语言开发更轻量、消耗资源小,但还是Logstash功能更强)

helm fetch stable/fluentd-elasticsearch
vim values.yaml
  # 更改其中 Elasticsearch 访问地址
helm install --name flu1 --namespace=efk -f values.yaml stable/fluentd-elasticsearch


部署 kibana

helm fetch stable/kibana --version 0.14.8
helm install --name kib1 --namespace=efk -f values.yaml stable/kibana --version 0.14.8

 

ls /var/log/containers/

F收集node节点log信息,传给E,再用K进行数据展示

 

 

 

 

11、证书可用时间修改

 

查看SA\CA证书到日期
openssl x509 -in apiserver.crt -text -noout

 

 

openssl x509 -in ca.crt -text -noout

 

 

 

 

证书可用时限


1、go 环境部署

wget https://dl.google.com/go/go1.12.7.linux-amd64.tar.gz
tar -zxvf go1.12.1.linux-amd64.tar.gz -C /usr/local
vi /etc/profile
export PATH=$PATH:/usr/local/go/bin
source /etc/profile
go version

 

2、下载源码

cd /data && git clone https://github.com/kubernetes/kubernetes.git
git checkout -b remotes/origin/release-1.15.1 v1.15.1


3、修改 Kubeadm 源码包更新证书策略

vim staging/src/k8s.io/client-go/util/cert/cert.go # kubeadm 1.14 版本之前
vim cmd/kubeadm/app/util/pkiutil/pki_helpers.go # kubeadm 1.14 至今
  const duration365d = time.Hour * 24 * 365 * 10
  NotAfter:time.Now().Add(duration365d).UTC(),

 

make WHAT=cmd/kubeadm GOFLAGS=-v
cp _output/bin/kubeadm /root/kubeadm-new

 

4、更新 kubeadm

 

# 将 kubeadm 进行替换
cp /usr/bin/kubeadm /usr/bin/kubeadm.old cp /root/kubeadm-new /usr/bin/kubeadm chmod a+x /usr/bin/kubeadm


5、更新各节点证书至 Master 节点

cp -r /etc/kubernetes/pki /etc/kubernetes/pki.old
cd /etc/kubernetes/pki
 kubeadm alpha certs renew all --config=/usr/local/install-k8s/core/kubeadm-config.yaml
openssl x509 -in apiserver.crt -text -noout | grep Not

 

6、HA集群其余 mater 节点证书更新

#!/bin/bash
masterNode="192.168.66.20 192.168.66.21"
#for host in ${masterNode}; do
#  scp /etc/kubernetes/pki/{ca.crt,ca.key,sa.key,sa.pub,front-proxy-ca.crt,front-proxy-ca.key}
"${USER}"@$host:/etc/kubernetes/pki/
#  scp /etc/kubernetes/pki/etcd/{ca.crt,ca.key} "root"@$host:/etc/kubernetes/pki/etcd
#  scp /etc/kubernetes/admin.conf "root"@$host:/etc/kubernetes/
#done
for host in ${CONTROL_PLANE_IPS}; do
  scp /etc/kubernetes/pki/{ca.crt,ca.key,sa.key,sa.pub,front-proxy-ca.crt,front-proxy-ca.key}
"${USER}"@$host:/root/pki/
  scp /etc/kubernetes/pki/etcd/{ca.crt,ca.key} "root"@$host:/root/etcd
  scp /etc/kubernetes/admin.conf "root"@$host:/root/kubernetes/
done

 

12、高可用的K8S集群构建

https://www.cnblogs.com/yyq1/p/14146568.html

 

 

 

教程视频:

https://www.bilibili.com/video/BV1w4411y7Go?p=1

posted @ 2020-11-14 17:15  一代肝帝  阅读(5283)  评论(0编辑  收藏  举报