1.k8s基本使用(测试加分)

测试为什么要学习容器技术及k8s

k8s不是运维的专属技术

随着互联网技术的发展,架构也已经从单体架构发展到容器云( “微服务 + k8s” 完美结合)

很多人认为,k8s只是运维需要掌握的技术,讲真,测试和运维、开发都有技术交集

所以,作为测试,也要跟上技术发展的节奏,与时俱进,技多不压身,大家也可以看看招聘要求,容器技术及k8s是加分项

为什么要学习docker?
可以编写dockerfile,制作镜像
快速验证镜像是否ok(docker run)
轻松快捷部署一个学习环境,比如mysql,tomcat

为什么要学习k8s?

1、使用k8s的时候,docker被封装在k8s里面,对docker操作很少

docker是一种容器技术,非k8s组件
docker最小操作单元是容器
k8s最小操作单元是pod

2、不管是功能测试还是其它测试,查看日志需要会k8s基础操作,好比你看linux服务器上日志,需要linux基础一样

3、性能监控分析需要

1、node和linux整体监控一样的, https://www.cnblogs.com/uncleyong/p/15398198.html
2、因为k8s的pod是根据算法调度的,所以你去看这个node上有哪些pod,Kubectl get po -owide | grep k8s-worker01
3、结合微服务链路监控工具, https://www.cnblogs.com/uncleyong/p/15535852.html
4、可以进入容器,kubectl exec -it podname -- sh,有top、vmstat常用命令,java项目,你也可以jstack打栈,也可以使用arthas, https://www.cnblogs.com/uncleyong/p/14944401.htm

node(no)

Node是Pod真正运行的主机,可以是物理机也可以是虚拟机。

Node本质上不是Kubernetes来创建的, Kubernetes只是管理Node上的资源。

为了管理Pod,每个Node节点上至少需要运行container runtime(Docker)、kubelet和kube-proxy服务。

常用node操作:kubectl get nodes,查询所有node

名称空间(ns)

简介

是对一组资源和对象的抽象集合,比如可以用来将系统内部的对象划分为不同的项目组或者用户组。

常见的pod、service、replicaSet和deployment等都是属于某一个namespace的(默认是default),而node, persistentVolumes等则不属于任何namespace。

常见操作

kubectl get namespace, 查询所有namespace
kubectl create namespace ns-name,创建namespace
kubectl delete namespace ns-name, 删除namespace

说明:

删除一个namespace会自动删除所有属于该namespace的资源。
default 和 kube-system 命名空间不可删除。

  

名称空间资源限额-该名称空间下每个pod 的限额

namespace-resourcequota.yaml

apiVersion: v1
kind: ResourceQuota
metadata:
  name: cpu-mem-quota
  namespace: test
spec:
  hard:
    requests.cpu: "2"
    requests.memory: 2Gi
    limits.cpu: "4"
    limits.memory: 4Gi

标签(label)

简介

label是标签的意思,一对 key/value ,被关联到对象上,k8s中的资源对象大都可以打上标签,如Node、Pod、Service 等

一个资源可以绑定任意多个label,k8s 通过 Label 可实现多维度的资源分组管理,后续可通过 Label Selector 查询和筛选拥有某些 Label 的资源对象

操作

帮助

kubectl label --help

打标签

pod:kubectl label pods busybox app=busybox

node:kubectl label node k8s-node01 k8s-node02 env=test

查看

查看pods为busybox的标签:kubectl get pods busybox --show-labels

查看默认名称空间下所有pod资源的标签:kubectl get pods --show-labels

查看指定名称空间:kubectl get pods -n kube-system --show-labels

通过标签筛选

列出默认名称空间下标签key是app的pod,不显示标签:kubectl get pods -l app

列出默认名称空间下标签key是app、值是busybox的pod,不显示标签:kubectl get pods -l app=busybox

多个筛选条件:kubectl get po -l version!=v1,app=nginx

删除标签

pod:kubectl label po busybox app- -n kube-public

node:kubectl label node k8s-node02 env-

修改标签

加上--overwrite参数修改标签

kubectl label po busybox app=busybox2 -n kube-public --overwrite

Service

试想一个问题,ReplicaSet定义了pod的数量是2,当一个pod由于某种原因停止了,ReplicaSet会新建一个pod,以确保运行中的pod数量始终是2。但每个pod都有自己的ip,前端请求不知道这个新pod的ip是什么,那前端的请求如何发送到新pod中呢?

答案是使用Service

k8s的Service定义了一个服务的访问入口地址,前端的应用通过这个入口地址访问其背后的一组由Pod副本组成的集群实例,来自外部的访问请求被负载均衡到后端的各个容器应用上。Service与其后端Pod副本集群之间则是通过Label Selector实现关联。

简单来说前端请求不是直接发送给Pod,而是发送到Service,Service再将请求转发给pod。

假设我们把一个服务做了高可用,这个服务分别部署在了两个节点上,当我们想访问该服务时,则此时访问的不是pod而是service,service会再给我们做负载均衡

k8s中的service中有三种类型:

ClusterIP:提供一个集群内部的虚拟IP以供Pod访问。

NodePort:在每个Node上打开一个端口以供外部访问。

LoadBalancer:通过外部的负载均衡器来访问。

核心资源:资源调度(Pod、RC、RCS、Deployment、Daemonset、Statefulset)

Pod

简介

Pod是一组紧密关联的容器集合,支持多个容器在一个Pod中共享网络和文件系统,可以通过进程间通信和文件共享这种简单高效的方式完成服务,是Kubernetes调度的基本单位。

Pod的设计理念是每个Pod都有一个唯一的IP。

Pod具有如下特征:

包含多个共享IPC、Network和UTC namespace的容器,可直接通过localhost通信
所有Pod内容器都可以访问共享的Volume,可以访问共享数据
优雅终止:Pod删除的时候先给其内的进程发送SIGTERM,等待一段时间(grace period)后才强制停止依然还在运行的进程
特权容器(通过SecurityContext配置)具有改变系统配置的权限(在网络插件中大量应用)
支持三种重启策略(restartPolicy),分别是:Always、OnFailure、Never
支持三种镜像拉取策略(imagePullPolicy),分别是:Always、Never、IfNotPresent
资源限制,Kubernetes通过CGroup限制容器的CPU以及内存等资源,可以设置request以及limit值
健康检查,提供两种健康检查探针,分别是livenessProbe和redinessProbe,前者用于探测容器是否存活,如果探测失败,则根据重启策略进行重启操作,后者用于检查容器状态是否正常,如果检查容器状态不正常,则请求不会到达该Pod
Init container在所有容器运行之前执行,常用来初始化配置
容器生命周期钩子函数,用于监听容器生命周期的特定事件,并在事件发生时执行已注册的回调函数,支持两种钩子函数:postStart和preStop,前者是在容器启动后执行,后者是在容器停止前执行

pod资源清单详解

资源分类

K8s中所有的内容都抽象为资源,资源实例化之后,叫做对象。集群资源分为:名称空间级别、集群级别、元数据型。

名称空间级别:

工作负载型资源:service,pod,Redeployment(控制器,我们很少直接操作pod,而是通过控制器来操作Pod)、ReplicaSet、StatefilSet、Job、Cronjob

服务发现及服务均衡资源型资源:Service、Ingress

配置与存储型资源:Volume、CSI(容器存储接口,可以扩展各种第三方的存储卷)、ConfigMap、Secret、DownwardAPI

特殊类型的存储卷:ConfigMap(当配置中心来使用的资源类型)、Secret(保存敏感数据)、DownwardAPI(把外部环境中的信息输出给容器)

集群级别:

集群级资源:Namespace、Node、Role、rolebinding、clusterrolebinding

元数据型:

元数据型资源:HPA、podtemplate、limitrange(限制资源的如cpu、内存等)

资源清单

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

资源清单中常见字段的解释:

编写资源清单(yaml文件)

在编写资源清单文件时需要指明api资源属于哪个群组和版本,我们在编写前可以先进行查询

创建pod目录并在该目录下编写文件构建资源清单pod.yaml(.yaml文件与.yml文件无区别),应用资源清单,此时可以查看到生成的pod,其中包含了由myapp镜像生成的一个副本

[root@server2 ~]# mkdir pod
[root@server2 ~]# cd pod/
[root@server2 pod]# vim pod.yml
[root@server2 pod]# cat pod.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-example
spec:
containers:
- name: myapp
image: myapp:v1
[root@server2 pod]# kubectl apply -f pod.yml
[root@server2 pod]# kubectl get pod

imagePullPolicy镜像拉取策略:

删除由资源清单生成的pod,重新编写资源清单文件,添加镜像拉取策略

[root@server2 pod]# kubectl delete -f pod.yml
pod "pod-example" deleted
[root@server2 pod]# kubectl get pod
No resources found in default namespace.
[root@server2 pod]# vim pod.yml

imagePullPolicy即为镜像拉取策略,镜像拉取策略有Always、Never、IfNotPresent三个值可选Always:意思是每次都尝试重新拉取镜像
Never:表示仅适用本地镜像
IfNotPresent:如果本地有镜像就使用本地镜像,没有就拉取在线镜像。上面三个值都没设置的话,默认是Always。

应用资源清单生成pod

查看pod的详细信息可以看到未重复拉取镜像

[root@server2 pod]# kubectl describe pod pod-example

containerPort+hostPort容器端口映射

编辑资源清单,指定生成的pod中容器副本需要监听的端囗号containerPort,指定容器所在主机需要监听的端囗号hostPort,默认跟上面containerPort相同

[root@server2 pod]# vim pod.yml
[root@server2 pod]# cat pod.yml
apiVersion: v1
kind: Pod
metadata:
name: pod-example
spec:
containers:
- name: myapp
image: myapp:v1
imagePullPolicy: IfNotPresent
ports:
- name: http
containerPort: 80
hostPort: 80

读取应用资源清单重新创建pod,查看Pod资源的额外信息得到容器运行在哪个节点上,去该节点主机上可以查看到端口映射设置成功

注意:设置了hostPort后,同一台主机无法启动该容器的相同副本,因为主机端囗号不能相同,这样会冲突

[root@server2 pod]# kubectl delete -f pod.yml
[root@server2 pod]# kubectl apply -f pod.yml
[root@server2 pod]# kubectl get pod
[root@server2 pod]# kubectl get pod -o wide

server4上查看映射的端口

[root@server4 ~]# iptables -t nat -nL | grep :80

hostNetwork主机网络模式

删除由资源清单生成的pod,编辑资源清单,设置主机网络模式hostNetwork值为true即使用宿主机网络,读取应用资源清单重新创建pod

注意: 需注释掉containerPort+hostPort容器端口映射

查看Pod资源的额外信息可以看到pod的ip为其运行节点主机的ip,curl访问该ip,可以成功访问pod中容器副本的默认发布页面

注意:设置了hostNetwork值为true将无法在同一台宿主机上启动该容器的第二个副本

resources资源限制(重点)

删除由资源清单生成的pod,编辑资源清单,指定容器运行时资源的运行下限resources.requests和上限resources.limits

读取应用资源清单重新创建pod,查看pod的详细信息可以看到资源限制设定成功

kubectl describe pod pod-example

nodeSelector定向调度

删除已有的pod,编辑资源清单,nodeSelector是最简单也是最推荐的节点约束方式。nodeSelector是PodSpec的一个字段。通过key-value的方式映射。要想让pod在指定节点上运行,该节点必须将加上对应的标签

kubernetes.io/hostname: server4 #指定调度节点为带有label标记为:kubernetes.io/hostname=server4的node节点

读取资源清单重新创建pod,查看Pod资源的额外信息可以看到pod被调度到了指定节点server4

restartPolicy重启策略

利用busybox镜像新建一个pod,设置其重启策略为Never,终止运行后查看Pod的信息可以看到这一新建pod没有被k8s重启

[root@server2 pod]# kubectl run -it busybox --image=busybox --restart=Never
[root@server2 pod]# kubectl get pod

删除busybox后重建一个pod,不指定其重启策略时默认值为Always,即一旦pod终止运行,则无论容器是如何终止的,k8s检测到容器关闭会将其自动重启,使用提示命令可以再次进入容器

创建pod方式

yaml语法及格式校验

详见:https://www.cnblogs.com/uncleyong/p/15437385.html

参考:https://www.cnblogs.com/uncleyong/p/15571059.html

docker可以创建容器,Kubernetes不能直接创建容器,Kubernetes创建的是pod,pod里面包含了一个或者多个容器。

Pod 是对容器的“打包”,Pod 中的容器总是能够一起被调度,一起运行。Pod 这个抽象的整体属于 Kubernetes 管理,Pod 可以在不触碰底层容器的情况下任意定制。可以说,有了 Pod,Kubernetes 在集群级别上的管理就会变得得心应手。

Kubernetes 让 Pod 去编排处理容器,然后把 Pod 作为应用调度部署的最小单位,Pod 也因此成为了 Kubernetes 世界里的“原子”(当然这个“原子”内部是有结构的),基于 Pod 就可以构建出更多更复杂的业务形态了。

Kubernetes(k8s)Deployment、StatefulSet、DaemonSet、Job、CronJob五种控制器详解

参考:https://www.cnblogs.com/liugp/p/16368518.html

  1. ReplicationController(RC)主要功能

Replication Controller(简称RC,副本控制器)可确保Pod副本数达到期望值,也就是RC定义的数量。换句话说,Replication Controller可确保一个Pod或一组同类Pod总是可用

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

2)ReplicaSet主要功能(RS)(副本控制器)

在新版本的Kubernetes中建议使用ReplicaSet来取ReplicationController。ReplicaSet跟ReplicationController没有本质的不同,只是名字不一样,并且ReplicaSet支持集合式的selector
虽然ReplicaSet可以独立使用,但一般还是建议使用 Deployment 来自动管理ReplicaSet,这样就无需担心跟其他机制的不兼容问题(比如ReplicaSet不支持rolling-update但Deployment支持)。

主要用作Deployment协调创建、删除和更新Pod,和Replication Controller唯一的区别是,ReplicaSet支持标签选择器

二者很少单独被使用,都是使用更高级的资源Deployment、DaemonSet、StatefulSet来管理Pod。

  • 用户期望的pod副本数量

  • 标签选择器,判断哪个pod归自己管理

  • 当现存的pod数量不足,会根据pod资源模板进行新建

3)deployment的主要功能

  • 管理无状态应用

  • 管理Pod和ReplicaSet

  • 具有上线部署、副本设定、滚动升级、回滚等功能

  • 提供声明式更新,例如只更新一个新的Image
    应用场景:web服务

小结:Pod里是容器,Pod被ReplicaSet管理,ReplicaSet控制pod的数量;ReplicaSet被Deployment管理,Deployment控制pod应用的升级、回滚,当然也能控制pod的数量。Service提供一个统一固定入口,负责将前端请求转发给Pod。

deployment和service yaml:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment
  labels:
    app: nginx
spec:
  replicas: 2
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        ports:
        - containerPort: 80

---

apiVersion: v1
kind: Service
metadata:
  name: nginx-service
spec:
  ports:
  - port: 81   
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: NodePort

Deployment:

① apiVersion 是当前配置格式的版本。

② kind 是要创建的资源类型,这里分别创建了Deployment和Service。

③ metadata 是该资源的元数据,name 是必需的元数据项。

④ spec 部分是 Deployment 的规格说明。

⑤ replicas 指明副本数量,默认为 1。

⑥matchLabels 指匹配的pod的标签是什么。

⑦ template 定义 Pod 的模板,这是配置文件的重要部分。

⑧ metadata 定义 Pod 的元数据,至少要定义一个 label。label 的 key 和 value 可以任意指定。

⑨ spec 描述 Pod 的规格,此部分定义 Pod 中每一个容器的属性,name 和 image 是必需的。

Service:

① port Service 服务暴露的端口

② targetPort 容器暴露的端口

③ type Service的类型

4)SatefulSet控制器

  • 管理有状态应用

  • kind:service

  • 解决Pod独立生命周期,保持Pod启动顺序和唯一性

  • 稳定,唯一的网络标识符,持久存储(例如:etcd配置文件,节点地址发生变化,将无法使用)

  • 有序,优雅的部署和扩展、删除和终止(例如:mysql主从关系,先启动主,再启动从),有序,滚动更新

  • 应用场景:数据库

5)DaemonSet控制器

  • 它用于确保集群中的每一个节点只运行特定的pod副本。

6)Job&CronJob

  • Job只要完成就立即退出,不需要重启或重建。

  • CronJob其实就是在Job的基础上加上了时间调度。

详细讲解

一、Deployment控制器概述

一般情况下,我们并不直接创建 Pod,而是通过 Deployment 来创建 Pod,由 Deployment 来负责创建、更新、维护其所管理的所有 Pods

ReplicationSet(RS)和ReplicationController(RC)区别

这里就需要说一下ReplicationSet(RS)和ReplicationController(RC),RS是在RC基础上发展来的,在新版的Kubernetes中,已经将RC替换为RS 了,它们两者没有本质的区别,都是用于Pod副本数量的维护与更新的使得副本数量始终维持在用户定义范围内,即如果存在容器异常退出,此时会自动创建新的Pod进行替代;而且异常多出来的容器也会自动回收。

总结不同点在于:RS在RC的基础上支持集合化的selector

一般情况下RS也是可以单独使用的,但是一般推荐和Deployment一起使用,这样会使得的Deployment提供的一些回滚更新操作同样用于RS上,因为**RS不支持回滚更新操作,Deployment支持**;

二、Deployment工作原理

先看一下Deployment、RS、Pod它们三者之间的关系:

RS负责控制副本数量,由Deployment来创建具体的Pod。

Deployment控制器支持两种更新策略:滚动更新(rolling update)和重新创建(recreate),默认为滚动更新。

1)滚动升级

滚动升级是默认的更新策略,它在删除一部分旧版本Pod资源的同时,补充创建一部分新版本的Pod对象进行应用升级,其优势是升级期间,容器中应用提供的服务不会中断,但要求应用程序能够应对新旧版本同时工作的情形,例如新旧版本兼容同一个数据库方案等。不过,更新操作期间,不同客户端得到的响应内容可能会来自不同版本的应用。

Deployment控制器的滚动更新操作并非在同一个ReplicaSet控制器对象下删除并创建Pod资源,而是将它们分置于两个不同的控制器之下:旧控制器的Pod对象数量不断减少的同时,新控制器的Pod对象数量不断增加,直到旧控制器不再拥有Pod对象,而新控制器的副本数量变得完全符合期望值为止

滚动更新时,应用升级期间还要确保可用的Pod对象数量不低于某阈值以确保可以持续处理客户端的服务请求,变动的方式和Pod对象的数量范围将通过spec.strategy.rollingUpdate.maxSurge和spec.strategy.rollingUpdate.maxUnavailable两个属性协同进行定义,它们的功用如图所示:

maxSurge:指定升级期间存在的总Pod对象数量最多可超出期望值的个数,其值可以是0或正整数,也可以是一个期望值的百分比;例如,如果期望值为3,当前的属性值为1,则表示Pod对象的总数不能超过4个。

maxUnavailable:升级期间正常可用的Pod副本数(包括新旧版本)最多不能低于期望数值的个数,其值可以是0或正整数,也可以是一个期望值的百分比;默认值为1,该值意味着如果期望值是3,则升级期间至少要有两个Pod对象处于正常提供服务的状态。

maxSurge和maxUnavailable属性的值不可同时为0,否则Pod对象的副本数量在符合用户期望的数量后无法做出合理变动以进行滚动更新操作。

2)版本回滚

Deployment控制器也支持用户保留其滚动更新历史中的旧ReplicaSet对象版本,这赋予了控制器进行应用回滚的能力:用户可按需回滚到指定的历史版本。控制器可保存的历史版本数量由“spec.revisionHistoryLimit”属性进行定义。当然,也只有保存于revision历史中的ReplicaSet版本可用于回滚,因此,用户要习惯性地在更新操作时指定保留旧版本。

三、Deployment的资源清单文件详解

apiVersion: apps/v1  #版本号
kind: Deployment  #类型
metadata:    #元数据
  name:    #rs名称
  namespace:   #所属命名空间
  labels:   #标签
    controller: deploy
spec:   #详情描述
  replicas:  #副本数量
  revisionHistoryLimit: #保留历史版本,默认是10
  paused: #暂停部署,默认是false
  progressDeadlineSeconds: #部署超时时间(s),默认是600
  strategy: #策略
    type: RollingUpdates  #滚动更新策略
    rollingUpdate:  #滚动更新
      maxSurge: #最大额外可以存在的副本数,可以为百分比,也可以为整数
      maxUnavaliable: #最大不可用状态的pod的最大值,可以为百分比,也可以为整数
  selector:  #选择器,通过它指定该控制器管理哪些pod
    matchLabels:   #Labels匹配规则
       app: nginx-pod
    matchExpressions:   #Expression匹配规则
      - {key: app, operator: In, values: [nginx-pod]}
  template:  #模板,当副本数量不足时,会根据下面的模板创建pod副本
    metadata:
        labels:
          app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1
        ports:
        - containerPort: 80

四、Deployment实战

1)示例1

创建pc-deployment.yaml,内容如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: pc-deployment
  namespace: dev
spec:
  replicas: 3
  selector:
    matchLabels:
     app: nginx-pod
  template:
    metadata:
      labels:
        app: nginx-pod
    spec:
      containers:
      - name: nginx
        image: nginx:1.17.1

执行以下命令

$ kubectl create namespace dev
$ kubectl create -f pc-deployment.yaml
$ kubectl get deploy -n dev -o wide

查看deployment控制的rs和pod,发现rs是在deployment之后加了一段字符串,而pod是在rs之后加了一段字符串。

2)扩缩容

方式一:命令行

kubectl scale deploy deploy名称 --replicas=pod数量 -n 命名空间

过命令行变更pod数量为5个

$ kubectl scale deploy pc-deployment --replicas=5 -n dev
$ kubectl get pod -n dev

方式二:编辑deploy文件

kubectl edit deploy deploy名字 -n 命名空间

通过编辑deploy文件编辑pod数量为3个

$ kubectl edit deploy pc-deployment -n dev

$ kubectl get pod -n dev

3)镜像更新

deployment支持两种镜像更新策略:重建更新和滚动更新(默认),可以通过strategy选项进行配置

strategy:指定新的pod替换旧的pod的策略,支持两个属性:
type:指定策略类型,支持两种策略
Recreate:在创建出新的pod之前会先杀掉所有已存在的pod
RollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本pod
rollingUpdate:当type为RollingUpdate时生效,用于为RollingUpdate设置参数,支持两个属性
maxUnavailable:用来指定在升级过程中不可用pod的最大数量,默认为25%
maxSurge:用来指定在升级过程中可以超过期望的pod的最大数量,默认为25%

1、重建更新

编辑pc-deployment.yaml,在spec节点下添加更新策略

spec:
strategy: #策略
type: Recreate #重建更新策略

$ kubectl apply -f pc-deployment.yaml

$ kubectl get pod -n dev

创建deploy进行验证

#首先记录原本的pod名

$ kubectl get pod -n dev

#更改pod镜像

$ kubectl set image deploy pc-deployment nginx=nginx:1.17.2 -n dev

#再次查看镜像

$ kubectl get pod -n dev

发现pod镜像已经改变了

2、滚动更新

编辑pc-deployment.yaml,在spec节点下添加滚动更新策略(也可以把strategy去掉,因为默认滚动更新策略)

strategy:
  type: RollingUpdate #滚动更新策略
  rollingUpdate:
    maxUnavailable: 25%
    maxSurge: 25%

执行

$ kubectl apply -f pc-deployment.yaml

创建deploy进行验证

#记录以前的pod
$ kubectl get pod -n dev
#更新镜像
$ kubectl set image deploy pc-deployment nginx=nginx:1.17.3 -n dev
#查看pod状态
$ kubectl get pod -n dev
$ kubectl get pod -n dev

发现pod是旧的一遍停止新的一边创建,最后全变成了新的

滚动更新的过程

3、镜像更新中rs的变化

#重建deployment

$ kubectl delete -f pc-deployment.yaml

#添加record参数,表明创建时记录

$ kubectl create -f pc-deployment.yaml --record

$ kubectl get deploy,rs,pod -n dev

打开两个窗口,用于监听rs和pod

在2窗口中监听rs,3窗口中监听pod

#在2窗口中输入

$ kubectl get rs -n dev -w

#在3窗口中输入

$ kubectl get pod -n dev -w

#在1窗口中改变pod镜像

$ kubectl set image deploy pc-deployment nginx=nginx:1.17.2 -n dev

查看3窗口中pod的变化,发现序号5开头的pod在逐渐暂停,序号7开头的pod在逐渐创建

查看2窗口中rs的变化,可以看见序号5开头的rs的pod数在减少,序号7开头的rs的pod数在增加

在1窗口中查看最终rs变化,发现原来的rs依旧存在,只是pod数量变为了0,而后又新产生了一个rs,pod数量为3,其实这就是deployment能够进行版本回退的奥妙所在,后面会详细解释

4)版本回退

1、简介

deployment支持版本升级过程中的暂停,继续功能以及版本回退等诸多功能,下面具体来看

kubectl rollout:版本升级相关功能,支持下面的选项:

status:显示当前升级状态

history:显示升级历史记录

pause:暂停版本升级过程

resume:继续已经暂停的版本升级过程

restart:重启版本升级过程

undo:回滚到上一级版本(可以使用–to-revision回滚到指定版本)

#查看升级状态

$ kubectl rollout status deploy pc-deployment -n dev

#查看升级历史(注意:如果只显示版本号说明一开始使用yaml创建文件的时候没有加上–record命令)

$ kubectl rollout history deploy pc-deployment -n dev

2、版本回滚实验

#这里使用–to-revision=1回滚到1版本,如果省略这个选项,则会回退到上个版本

$ kubectl rollout undo deploy pc-deployment --to-revision=1 -n dev

#查看是否回滚成功,发现5序号开头的rs被启动了

$ kubectl get rs -n dev

可以看到版本7开头的已经回滚到5开头的版本了,这也就是rs依旧存在的原因。

五、Deployment金丝雀发布(灰度发布)

deployment支持更新过程中的控制,如"暂停(pause)"或"继续(resume)"更新操作。

比如有一批新的pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新的pod应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布,其实也叫做灰度发布。

#更新deployment版本,并配置暂停deployment

$ kubectl set image deploy pc-deployment nginx=nginx:1.17.2 -n dev && kubectl rollout pause deploy pc-deployment -n dev

#查看rs,发现老版本rs没有减少,新版本rs增加一个

$ kubectl get rs -n dev

#查看更新过程

$ kubectl rollout status deploy pc-deployment -n dev

$ kubectl get rs -n dev

发现老版本均停止,新版本已经创建好

六、有状态、无状态服务区别

1)无状态:

上面所说的deployment 认为所有的pod都是一样的

不用考虑顺序的要求

不用考虑在哪个node节点上运行

可以随意扩容和缩容

2)有状态

实例之间有差别,每个实例都有自己的独特性,元数据不同,例如etcd,zookeeper

实例之间不对等的关系,以及依靠外部存储的应用

七、PV、PVC、NFS、SC

1)PV概述

PersistentVolume (PV:持久化存储卷)是集群中由管理员提供或使用存储类动态提供的一块存储。它是集群中的资源,就像节点是集群资源一样。

PV是与Volumes类似的卷插件,但其生命周期与使用PV的任何单个Pod无关。由此API对象捕获存储的实现细节,不管是NFS、iSCSI还是特定于云提供商的存储系统。

2)PVC概述

PersistentVolumeClaim(PVC:持久化存储卷声明),PVC 是用户存储的一种声明,PVC 和 Pod 比较类似,Pod 消耗的是节点,PVC 消耗的是 PV 资源,Pod 可以请求 CPU 和内存,而 PVC 可以请求特定的存储空间和访问模式。对于真正使用存储的用户不需要关心底层的存储实现细节,只需要直接使用 PVC 即可。

3)通过NFS实现持久化存储

NFS工作原理

首先服务器端启动RPC服务,并开启111端口

服务器端启动NFS服务,并向RPC注册端口信息

客户端启动RPC(rpcbind服务),向服务端的RPC(rpcbind)服务请求服务端的NFS端口

服务端的RPC(rpcbind)服务反馈NFS端口信息给客户端。

客户端通过获取的NFS端口来建立和服务端的NFS连接并进行数据的传输。

安装NFS

1、所有节点安装nfs

$ yum -y install nfs-utils rpcbind

2、在master节点创建共享目录并授权

$ mkdir /opt/nfsdata

# 授权共享目录

$ chmod 666 /opt/nfsdata

3、编辑exports文件

$ cat /etc/exports

/opt/nfsdata *(rw,no_root_squash,no_all_squash,sync)

4、配置生效

$ exportfs -r

exportfs命令

常用选项

-a 全部挂载或者全部卸载

-r 重新挂载

-u 卸载某一个目录

-v 显示共享目录 以下操作在服务端上

5、启动rpc和nfs(客户端只需要启动rpc服务)(注意顺序)

$ systemctl start rpcbind

$ systemctl start nfs-server

$ systemctl enable rpcbind

$ systemctl enable nfs-server

6、查看

$ showmount -e

$ showmount -e 192.168.0.113

-e 显示NFS服务器的共享列表

-a 显示本机挂载的文件资源的情况NFS资源的情况

-v 显示版本号

7、客户端

# 启动rpc服务

$ systemctl start rpcbind

$ systemctl enable rpcbind

# 创建挂载目录

$ mkdir /mnt/nfsdata

# 挂载

$ echo "192.168.0.113:/opt/nfsdata /mnt/nfsdata nfs defaults 0 1">> /etc/fstab

$ mount -a

8、测试

# 客户端执行

$ touch /mnt/nfsdata/test{1..5}

$ ll /mnt/nfsdata/

# 服务端执行

$ ll /opt/nfsdata/

4)基于NFS存储创建PV

查看nfs版本号

# nfs服务端,只能看到大版本

$ nfsstat -s

# nfs客户端看

$ nfsstat -m

# pv-nfs.yaml

apiVersion: v1

kind: PersistentVolume

metadata:

name: bxy-pv

labels:

name: bxy-pv-labels

spec:

capacity:

storage: 6Gi

volumeMode: Filesystem

accessModes:

- ReadWriteMany

persistentVolumeReclaimPolicy: Retain

storageClassName: bxy-storageclass

mountOptions:

- hard

- nfsvers=4.2

nfs:

path: /opt/nfsdata

server: 192.168.0.113

spec.storageClassName 字段对应 StorageClass 配置中的 metedata.name 字段

capacity: #容量

volumeMode: 存储卷模式(默认值为filesystem,除了支持文件系统外(file system)也支持块设备(raw block devices))

accessModes: 访问模式

ReadWriteMany :(RWO/该volume只能被单个节点以读写的方式映射),ReadOnlyMany (ROX/该volume可以被多个节点以只读方式映射), ReadWriteMany (RWX/该volume可以被多个节点以读写的方式映射)

persistentVolumeReclaimPolicy: 回收策略 ,Retain(保留)、 Recycle(回收)或者Delete(删除)

storageClassName: 存储类(通过设置storageClassName字段进行设置。如果设置了存储类,则此PV只能被绑定到也指定了此存储类的PVC)

mountOptions: 挂接选项

path: 我的本地挂载路径为

server: NFS 文件系统所在服务器的真实 IP

执行

$ kubectl apply -f pv-nfs.yaml

$ kubectl get pv

$ kubectl describe pv bxy-pv

注意创建完 PV 后,会看到 Status 类型为 Available ,这是因为 PV 还没有和 PVC 绑定,当绑定成功后会自动改成 Bound。

5)基于NFS-PV创建PVC

# nfs-pv-pvc.yaml

apiVersion: v1

kind: PersistentVolumeClaim

metadata:

name: bxy-pvc

spec:

accessModes:

- ReadWriteMany

volumeMode: Filesystem

resources:

requests:

storage: 5Gi

storageClassName: bxy-storageclass

selector:

matchLabels:

name: bxy-pv-labels

【注意】storage: 5Gi:我写的是 5G ,但实际绑定成功后会自动改变为 PV 中设置的容量大小 6G。

pvc 通过 matchLabels和pv中的label匹配,来关联要使用的存储空间。表明此PVC希望使用Label:name: "bxy-pv-labels"的PV。

执行

$ kubectl apply -f nfs-pv-pvc.yaml

$ kubectl get pvc

# 再查看pv状态

$ kubectl get pv

6)SC(StorageClass:存储类)

SC是StorageClass的缩写,表示存储类;这种资源主要用来对pv资源的自动供给提供接口;所谓自动供给是指用户无需手动创建pv,而是在创建pvc时对应pv会由persistentVolume-controller自动创建并完成pv和pvc的绑定;使用sc资源的前提是对应后端存储必须支持restfull类型接口的管理接口,并且pvc必须指定对应存储类名称来引用SC;简单讲SC资源就是用来为后端存储提供自动创建pv并关联对应pvc的接口;如下图:

【提示】使用sc动态创建pv,对应pvc必须也是属于对应的sc;上图主要描述了用户在创建pvc时,引用对应的sc以后,对应sc会调用底层存储系统的管理接口,创建对应的pv并关联至对应pvc。

创建sc资源

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

name: slow

provisioner: kubernetes.io/glusterfs

parameters:

resturl: "http://127.0.0.1:8081"

clusterid: "630372ccdc720a92c681fb928f27b53f"

restauthenabled: "true"

restuser: "admin"

secretNamespace: "default"

secretName: "heketi-secret"

gidMin: "40000"

gidMax: "50000"

volumetype: "replicate:3"

【提示】在创建pvc时用storageClassName字段来指定对应的SC名称即可。上述是官方文档中的一个示例,在创建sc资源时,对应群组是storage.k8s.io/v1,类型为StorageClass;provisioner字段用于描述对应供给接口名称;parameters用来定义向对应存储管理接口要传递的参数。

7)基于动态sc(StorageClass:存储类)创建一个pv

第一步:创建statefueset的命名空间

$ cat << EOF > nginx-ns.yaml

apiVersion: v1

kind: Namespace

metadata:

name: nginx-ss

EOF

$ kubectl apply -f nginx-ns.yaml

第二步:创建ServiceAccount,为nfs-client-provisioner授权

如果集群启用了RBAC,则必须执行如下命令授权provisioner。(k8s1.6+默认开启)

$ cat << EOF > nfs-rbac.yaml

apiVersion: v1

kind: ServiceAccount

metadata:

name: nfs-client-provisioner

# replace with namespace where provisioner is deployed

namespace: nginx-ss #根据实际环境设定namespace,下面类同

---

kind: ClusterRole

apiVersion: rbac.authorization.k8s.io/v1

metadata:

name: nfs-client-provisioner-runner

namespace: nginx-ss

rules:

- apiGroups: [""]

resources: ["persistentvolumes"]

verbs: ["get", "list", "watch", "create", "delete"]

- apiGroups: [""]

resources: ["persistentvolumeclaims"]

verbs: ["get", "list", "watch", "update"]

- apiGroups: ["storage.k8s.io"]

resources: ["storageclasses"]

verbs: ["get", "list", "watch"]

- apiGroups: [""]

resources: ["events"]

verbs: ["create", "update", "patch"]

---

kind: ClusterRoleBinding

apiVersion: rbac.authorization.k8s.io/v1

metadata:

name: run-nfs-client-provisioner

subjects:

- kind: ServiceAccount

name: nfs-client-provisioner

namespace: nginx-ss

# replace with namespace where provisioner is deployed

roleRef:

kind: ClusterRole

name: nfs-client-provisioner-runner

apiGroup: rbac.authorization.k8s.io

---

kind: Role

apiVersion: rbac.authorization.k8s.io/v1

metadata:

name: leader-locking-nfs-client-provisioner

namespace: nginx-ss

# replace with namespace where provisioner is deployed

rules:

- apiGroups: [""]

resources: ["endpoints"]

verbs: ["get", "list", "watch", "create", "update", "patch"]

---

kind: RoleBinding

apiVersion: rbac.authorization.k8s.io/v1

metadata:

name: leader-locking-nfs-client-provisioner

namespace: nginx-ss

subjects:

- kind: ServiceAccount

name: nfs-client-provisioner

# replace with namespace where provisioner is deployed

namespace: nginx-ss

roleRef:

kind: Role

name: leader-locking-nfs-client-provisioner

apiGroup: rbac.authorization.k8s.io

EOF

第三步:创建nfs的nfs-client-provisioner

nfs-client-provisioner 是k8s简易的NFS外部提供者(provisioner),本身不提供NFS,做为NFS的客户端为StorageClass提供存储。

$ cat << EOF > nfs-deployment-provisioner.yaml

kind: Deployment

apiVersion: apps/v1

metadata:

name: nfs-client-provisioner

namespace: nginx-ss

spec:

replicas: 1

strategy:

type: Recreate

selector:

matchLabels:

app: nfs-client-provisioner

template:

metadata:

labels:

app: nfs-client-provisioner

spec:

serviceAccountName: nfs-client-provisioner

containers:

- name: nfs-client-provisioner

image: quay.io/external_storage/nfs-client-provisioner:latest

volumeMounts:

- name: nfs-client-root

mountPath: /persistentvolumes #容器内挂载点

env:

- name: PROVISIONER_NAME

value: fuseim.pri/ifs

- name: NFS_SERVER

value: 192.168.0.113

- name: NFS_PATH

value: /opt/nfsdata

volumes:

- name: nfs-client-root #宿主机挂载点

nfs:

server: 192.168.0.113

path: /opt/nfsdata

EOF

$ kubectl apply -f nfs-deployment-provisioner.yaml

$ kubectl get deploy -n nginx-ss

第四步:基于sc创建动态存储

cat << EOF > nginx-sc.yaml

apiVersion: storage.k8s.io/v1

kind: StorageClass

metadata:

name: nginx-nfs-storage

namespace: nginx-ss

provisioner: fuseim.pri/ifs # or choose another name, must match deployment's env PROVISIONER_NAME'

reclaimPolicy: Retain #回收策略:Retain(保留)、 Recycle(回收)或者Delete(删除)

volumeBindingMode: Immediate #volumeBindingMode存储卷绑定策略

allowVolumeExpansion: true #pvc是否允许扩容

EOF

volumeBindingMode存储卷绑定策略

Immediate:创建 PVC 后立即创建后端存储卷,并且立即绑定新创建的 PV 和 PVC。

WaitForFirstConsumer:当 PVC 被 Pod 使用时,才触发 PV 和后端存储的创建,同时实现 PVC/PV 的绑定,启用该配置后,Storage Class 中的 Zone 和 Region 将不再生效,而是使用 Pod 调度所在节点的 zone 和 region 创建文件系统,保证文件系统能被 Pod 挂载。)

StorageClass的定义包含四个部分:

provisioner:该字段指定使用存储卷类型,不同的存储卷提供者类型这里要修改成对应的值。【注意】provisioner必须和上面得Deployment的YAML文件中PROVISIONER_NAME的值保持一致。

parameters:指定 provisioner 的选项,比如 glusterfs 支持 resturl、restuser 等参数。

mountOptions:指定挂载选项,当 PV 不支持指定的选项时会直接失败。比如 NFS 支持 hard 和 nfsvers=4.2 等选项。

reclaimPolicy:指定回收策略,同 PV 的回收策略。Retain(保留)、 Recycle(回收)或者Delete(删除)

$ kubectl apply -f nginx-sc.yaml

$ kubectl get sc -n nginx-ss

八、StatefulSet控制器

1)简介

StatefulSet是用来管理有状态应用的工作负载API对象,实例之间有不对等关系,以及实例对外部数据有依赖关系的应用,称为”有状态应用“。

StatefulSet本质上是Deployment的一种变体,在v1.9版本中已成为GA版本,它为了解决有状态服务的问题,它所管理的Pod拥有固定的Pod名称,启停顺序,在StatefulSet中,Pod名字称为网络标识(hostname),还必须要用到共享存储。

在Deployment中,与之对应的服务是service,而在StatefulSet中与之对应的headless service,headless service,即无头服务,与service的区别就是它没有Cluster IP,解析它的名称时将返回该Headless Service对应的全部Pod的Endpoint列表。

除此之外,StatefulSet在Headless Service的基础上又为StatefulSet控制的每个Pod副本创建了一个DNS域名,这个域名的格式为:

$(podname).(headless server name)

FQDN:$(podname).(headless server name).namespace.svc.cluster.local

2)常规service和无头服务区别

service:一组Pod访问策略,提供cluster-IP群集之间通讯,还提供负载均衡和服务发现。

Headless service 无头服务,不需要cluster-IP,直接绑定具体的Pod的IP。

3)特点

Pod一致性:包含次序(启动、停止次序)、网络一致性。此一致性与Pod相关,与被调度到哪个node节点无关;

稳定的次序:对于N个副本的StatefulSet,每个Pod都在[0,N)的范围内分配一个数字序号,且是唯一的;

稳定的网络:Pod的hostname模式为( statefulset 名 称 ) − (statefulset名称)-(statefulset名称)−(序号);

稳定的存储:通过VolumeClaimTemplate为每个Pod创建一个PV。删除、减少副本,不会删除相关的卷。

4)组成部分

Headless Service:用来定义Pod网络标识( DNS domain);

volumeClaimTemplates :存储卷申请模板,创建PVC,指定pvc名称大小,将自动创建pvc,且pvc必须由存储类供应;

上面【1-4】都得执行,这里是第五步。

此处是基于SC,上面已经创建过了SC,这里就直接创建StatefulSet了

cat << EOF > nginx-ss.yaml

apiVersion: apps/v1

kind: StatefulSet

metadata:

name: web

namespace: nginx-ss

spec:

selector:

matchLabels:

app: nginx #必须匹配 .spec.template.metadata.labels

serviceName: "nginx" #声明它属于哪个Headless Service.

replicas: 3 #副本数

template:

metadata:

labels:

app: nginx # 必须配置 .spec.selector.matchLabels

spec:

terminationGracePeriodSeconds: 10

containers:

- name: nginx

image: nginx:1.17.1

ports:

- containerPort: 80

name: web

volumeMounts:

- name: nginx-pvc

mountPath: /usr/share/nginx/html

volumeClaimTemplates: #可看作pvc的模板

- metadata:

name: nginx-pvc

spec:

accessModes: [ "ReadWriteOnce" ]

storageClassName: "nginx-nfs-storage" #存储类名,就是上面nginx-sc.yaml metadata.name

resources:

requests:

storage: 1Gi

EOF

这里的storageClassName必须与上面SC里面的metadata.name一样。

执行命令

$ kubectl apply -f nginx-ss.yaml

$ kubectl get pods -n nginx-ss

上述例子中:

名为 nginx 的 Headless Service 用来控制网络域名。

名为 web 的 StatefulSet 有一个 Spec,它表明将在独立的 3 个 Pod 副本中启动 nginx 容器。

volumeClaimTemplates 将通过 PersistentVolumes 驱动提供的 PersistentVolumes 来提供稳定的存储。

【问题】Kubernetes v1.20 (opens new window)开始,默认删除了 metadata.selfLink 字段,然而,部分应用仍然依赖于这个字段,例如上面的nfs-deployment-provisioner.yaml。其实上面创建pod是没成功的。报错如下:

【解决】

通过配置 apiserver 启动参数中的 –feature-gates 中的 RemoveSelfLink=false,可以重新启用 metadata.selfLink 字段。

修改 /etc/kubernetes/manifests/kube-apiserver.yaml 文件,并在其启动参数中增加一行 – –feature-gates=RemoveSelfLink=false,如下第 43行所示:

重新加载配置,如果一次未加载成功,多执行几次。

$ kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml

加载完配置后,我们再看pod创建情况。

$ kubectl get pvc -n nginx-ss

$ kubectl get pods -n nginx-ss

最后命令总结:

# 清除,只需要删除命名空间就行

$ kubectl delete -f nginx-ns.yaml

# 重新执行

$ kubectl apply -f nginx-ns.yaml

$ kubectl apply -f nfs-rbac.yaml

$ kubectl apply -f nfs-deployment-provisioner.yaml

$ kubectl apply -f nginx-sc.yaml

$ kubectl apply -f nginx-ss.yaml

# 检测

$ kubectl get pvc -n nginx-ss

$ kubectl get pods -n nginx-ss

九、DaemonSet控制器

DaemonSet 确保全部(或者一些)Node 上运行一个 Pod 的副本,通常用于实现系统级后台任务。比如ELK服务

创建DaemonSet

DaemonSet的描述文件和Deployment非常相似,只需要修改Kind,并去掉副本数量的配置即可。

cat << EOF > nginx-daemonset.yaml

apiVersion: apps/v1

kind: DaemonSet

metadata:

name: nginx-daemonset

labels:

app: nginx

spec:

selector:

matchLabels:

app: nginx

template:

metadata:

labels:

app: nginx

spec:

containers:

- name: nginx

image: nginx:1.13.12

ports:

- containerPort: 80

EOF

$ kubectl apply -f nginx-daemonset.yaml

在每个node节点上都运行了一个pod副本

十、Job控制器

Job控制器用于调配pod对象运行一次性任务,容器中的进程在正常运行结束后不会对其进行重启,而是将pod对象置于completed状态。若容器中的进程因错误而终止,则需要依据配置确定重启与否,未运行完成的pod对象因其所在的节点故障而意外终止后会被重新调度。

简单示例

cat << EOF > job-demo.yaml

apiVersion: batch/v1

kind: Job

metadata:

name: job-demo

spec:

template:

metadata:

name: job-demo

spec:

restartPolicy: Never

containers:

- name: counter

image: busybox

command:

- "bin/sh"

- "-c"

- "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"

EOF

Pod模板中的spec.restartPolicy默认为Always,这对job控制器来说只能设定为Never或OnFailure。

$ kubectl apply -f job-test.yaml

1

十一、CronJob 控制器

CronJob其实就是在Job的基础上加上了时间调度,我们可以:在给定的时间点运行一个任务,也可以周期性地在给定时间点运行。这个实际上和我们Linux中的crontab就非常类似了。

一个CronJob对象其实就对应中crontab文件中的一行,它根据配置的时间格式周期性地运行一个Job,格式和crontab也是一样的。

crontab的格式如下:

分 时 日 月 星期 要运行的命令 第1列分钟0~59 第2列小时0~23) 第3列日1~31 第4列月1~12 第5列星期0~7(0和7表示星期天) 第6列要运行的命令

现在,我们用CronJob来管理我们上面的Job任务

cat << EOF > cronjob-demo.yaml

apiVersion: batch/v1

kind: CronJob

metadata:

name: cronjob-demo

spec:

schedule: "*/1 * * * *"

jobTemplate:

spec:

template:

spec:

restartPolicy: OnFailure

containers:

- name: hello

image: busybox

args:

- "bin/sh"

- "-c"

- "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"

EOF

执行

$ kubectl get jobs

$ kubectl get cronjobs.batch

核心资源:配置管理(ConfigMap、Secret)

0. 如何进行配置管理

Nginx 有 nginx.conf、Redis 有 redis.conf、MySQL 有 my.cnf ... 很多应用都通过配置文件进行管理。在使用 docker 时,我们可以使用如下几种管理配置文件的方式:

  • 编写 Dockerfile 时使用 COPY 吗,命令将配置文件打包进镜像中

  • 在运行时使用 docker cp 将配置文件拷贝进正在运行的容器

  • 使用 docker run -v 将配置目录映射到本机的文件系统中

当然,这几种方法在大规模的集群中的缺点显而易见,不适合自动化运维管理。Kubernetes 还是使用 YAML 语言来定义 API 对象,再组合起来实现动态配置——它们是 ConfigMap 和 Secret,专门用来管理配置信息的两种对象。

1. ConfigMap 和 Secret 简介

应用程序有很多类别的配置信息,总的来说可以分为两类:

  • 明文配置:可以任意查询修改,比如服务端口、运行参数、文件路径等;

  • 机密配置:涉及敏感信息需要保密,不能随便查看,比如密码、密钥、证书等

这两类配置信息本质上都是字符串,只是由于安全性的原因,在存放和使用方面有些差异,所以 Kubernetes 也就定义了两个 API 对象,ConfigMap 用来保存明文配置,Secret 用来保存秘密配置。

2. ConfigMap

首先先从 kubectl create 生成一个 ConfigMap 的模板:

$ kubectl create cm info --dry-run=client -o yaml

ConfigMap 存储的是配置数据,是静态的字符串,并不是容器,所以它们就不需要用 spec 字段来说明运行时的“规格”。

apiVersion: v1
kind: ConfigMap
metadata:name: info

其实 ConfigMap 存储的数据放在字段 data 中。要生成带有 data 字段的 YAML 模板,我们需要多加一个参数 --from-literal 表示从字面值生成一些数据:

$ kubectl create cm info --from-literal=key=val --dry-run=client -o yaml
因为在 ConfigMap 里的数据都是 Key-Value 结构,所以 --from-literal 参数需要使用 k=v 的形式

然后我们整理一下生成的 YAML 文件,再添加几个键值对:

apiVersion: v1
kind: ConfigMap
metadata:creationTimestamp:nullname: info

data:count:'10'debug:'on'path:'/usr/bin'greeting:|
    say hello to kubernetes.

然后使用下面的命令创建 ConfigMap 对象:

$ kubectl apply -f config-map.yml

创建成功后,我们可以用 kubectl get 、kubectl describe 来查看 ConfigMap 的状态:

$ kubectl get cm
NAME               DATA   AGE
info               4      7s
kube-root-ca.crt   1      7m39s
$ kubectl describe cm info
Name:         info
Namespace:    default
Labels:       <none>
Annotations:  <none>

Data
====
count:
----
10
debug:
----
on
greeting:
----
say hello to kubernetes.

path:
----
/usr/bin

BinaryData
====

Events:  <none>

现在 ConfigMap 的 Key-Value 信息就已经存入了 etcd 数据库,后续就可以被其他 API 对象使用。

3. Secret

在 Kubernetes 里 Secret 对象又细分出很多类,比如:

  • 访问私有镜像仓库的认证信息

  • 身份识别的凭证信息

  • HTTPS 通信的证书

  • 私钥一般的机密信息(格式由用户自行解释)

我们就拿一般机密信息做例子,使用命令行生成一个 YAML 文件:

$ kubectl create secret generic user --from-literal=name=root --from-literal=pwd=12345678 --from-literal=db=postgresql --dry-run=client -o yaml
apiVersion: v1
kind: Secret
metadata:creationTimestamp:nullname: user

data:db: cG9zdGdyZXNxbA==  # postgresqlname: cm9vdA==        # rootpwd: MTIzNDU2Nzg=     # 12345678

Secret 与 ConfigMap 的不同之处就在于,Secret 不让用户直接看到原始数据,起到一定的保密作用。不过它的手法非常简单,只是做了 Base64 编码,根本算不上真正的加密,所以我们完全可以绕开 kubectl,自己用 Linux 命令 base64 来对数据编码,然后写入 YAML 文件。

当然我们也可以使用 base64 命令自己编码一串字符,然后写入 YAML 文件:

$ echo -n "12345678"| base64
MTIzNDU2Nzg=
echo -n 表示去除换行符

然后我们创建和查看 Secret 对象:

$ kubectl apply -f secret.yaml
secret/user created
$ kubectl get secret
NAME                  TYPE                                  DATA   AGE
default-token-kshx4   kubernetes.io/service-account-token   3      2m3s
user                  Opaque                                3      12s
$ kubectl describe secret user
Name:         user
Namespace:    default
Labels:       <none>
Annotations:  <none>

Type:  Opaque

Data
====
db:    10 bytes
name:  4 bytes
pwd:   8 bytes

这样一个存储敏感信息的 Secret 对象也就创建好了,而且因为它是保密的,使用 kubectl describe 不能直接看到内容,只能看到数据的大小。

4. 使用配置

ConfigMap 和 Secret 只是一些存储在 etcd 里的字符串,所以如果想要在运行时产生效果,就必须要以某种方式“注入”到 Pod 里,让应用去读取。Kubernetes 有两种途径来注入配置:环境变量和加载文件。

4.1 以环境变量的方式使用配置

前面在创建 Pod 时,我们使用了 container 字段中的 env 来定义 Pod 容器中可见的环境变量:

spec:containers:-image: busybox:latest
    name: busy
    imagePullPolicy: IfNotPresent
    env:-name: os
        value:"ubuntu"-name: debug
        value:"on"command:- /bin/echo
    args:-"$(os), $(debug)"

我们使用 value 字段为 name 提供了值;其实我们还可以使用 valueFrom 字段来从 ConfigMap 或者 Secret 对象里面获取值,实现把配置信息以环境变量的形式注入 Pod,完成配置与应用的解耦。

我们使用 kubectrl explain 查看一下字段的说明:

$ kubectl explain pod.spec.containers.env.valueFrom
FIELDS:
   configMapKeyRef	<Object>
     Selects a key of a ConfigMap.

   fieldRef	<Object>
     Selects a field of the pod: supports metadata.name, metadata.namespace,
     `metadata.labels['<KEY>']`, `metadata.annotations['<KEY>']`, spec.nodeName,
     spec.serviceAccountName, status.hostIP, status.podIP, status.podIPs.

   resourceFieldRef	<Object>
     Selects a resource of the container: only resources limits and requests
     (limits.cpu, limits.memory, limits.ephemeral-storage, requests.cpu,
     requests.memory and requests.ephemeral-storage) are currently supported.

   secretKeyRef	<Object>
     Selects a key of a secret in the pod's namespace

可以看到 valueFrom 环境变量值的来源可以是 configMapKeyRef 或者 secretKeyRef (中间的 fieldRef 和 resourceFieldRef 可以先不管)。然后我们需要进一步指定 ConfigMap/Secret 的 name 和它的 key,这里的 name 是指 API 对象的名称。

下面是一个Pod YAML 的示例:

apiVersion: v1
kind: Pod
metadata:name: env-pod

spec:containers:-env:-name: COUNT
        valueFrom:configMapKeyRef:name: info
            key: count
      -name: GREETING
        valueFrom:configMapKeyRef:name: info
            key: greeting
      -name: USERNAME
        valueFrom:secretKeyRef:name: user
            key: name
      -name: PASSWORD
        valueFrom:secretKeyRef:name: user
            key: pwd

    image: busybox
    name: busy
    imagePullPolicy: IfNotPresent
    command:["/bin/sleep","300"]

我们先启动这个 Pod:

$ kubectl apply -f env-pod.yaml
pod/env-pod created

然后我们使用 kubectl exec 命令查看环境变量:

$ kubectl exec env-pod -- /bin/ash -c "env"KUBERNETES_SERVICE_PORT=443KUBERNETES_PORT=tcp://10.96.0.1:443
HOSTNAME=env-pod
SHLVL=1HOME=/root
GREETING=say hello to kubernetes.

USERNAME=root
KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
COUNT=10KUBERNETES_PORT_443_TCP_PORT=443KUBERNETES_PORT_443_TCP_PROTO=tcp
KUBERNETES_SERVICE_PORT_HTTPS=443KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
KUBERNETES_SERVICE_HOST=10.96.0.1
PWD=/
PASSWORD=12345678

可以看到环境变量都注入到 Pod 中了。

我们也可以使用交互式命令行查看环境变量:

$ kubectl apply -f env-pod.yml
$ kubectl exec -it env-pod -- shecho$COUNTecho$GREETINGecho$USERNAME$PASSWORD
4.2 以 Volume 的方式使用配置

Kubernetes 为 Pod 定义了一个 Volume 的概念,可以翻译成是“存储卷”。如果把 Pod 理解成是一个虚拟机,那么 Volume 就相当于是虚拟机里的磁盘。我们可以为 Pod 挂载(mount)多个 Volume,里面存放供 Pod 访问的数据,这种方式有点类似 docker run -v,虽然用法复杂了一些,但功能也相应强大一些。

在 Pod 里挂载 Volume 很容易,只需要在 spec 里增加一个 volumes 字段,然后再定义卷的名字和引用的 ConfigMap/Secret 就可以了。要注意的是 Volume 属于 Pod,不属于容器,所以它和字段containers 是同级的,都属于 spec。

下面定义了两个 Volume,分别引用 ConfigMap 和 Secret,名字是 cm-vol 和 sec-vol :

spec:volumes:-name: cm-vol
    configMap:name: info
  -name: sec-vol
    secret:secretName: user

有了 Volume 的定义之后,就可以在容器里挂载了,这要用到 volumeMounts 字段,正如它的字面含义,可以把定义好的 Volume 挂载到容器里的某个路径下,所以需要在里面用 mountPath name 明确地指定挂载路径和 Volume 的名字。

你可以看到,挂载 Volume 的方式和环境变量又不太相同。环境变量是直接引用了 ConfigMap/Secret,而 Volume 又多加了一个环节,需要先用 Volume 引用 ConfigMap/Secret,然后在容器里挂载 Volume。

这种方式的好处在于:以 Volume 的概念统一抽象了所有的存储,不仅现在支持 ConfigMap/Secret,以后还能够支持临时卷、持久卷、动态卷、快照卷等许多形式的存储。

这里是完整的 YAML 描述文件:

apiVersion: v1
kind: Pod
metadata:name: vol-pod

spec:volumes:-name: cm-vol
    configMap:name: info
  -name: sec-vol
    secret:secretName: user

  containers:-volumeMounts:-mountPath: /tmp/cm-items
      name: cm-vol
    -mountPath: /tmp/sec-items
      name: sec-vol

    image: busybox
    name: busy
    imagePullPolicy: IfNotPresent
    command:["/bin/sleep","300"]

我们创建这个 Pod 然后进入 Pod 查看文件:

$ kubectl apply -f vol-pod.yml
pod/vol-pod created
$ kubectl get pod
NAME      READY   STATUS    RESTARTS   AGE
vol-pod   1/1     Running   0          48s
$ kubectl exec -it vol-pod -- /bin/ash

ConfigMap 和 Secret 都变成了目录的形式,而它们里面的 Key-Value 变成了一个个的文件,而文件名就是 Key。

环境变量用法简单,更适合存放简短的字符串,而 Volume 更适合存放大数据量的配置文件,在 Pod 里加载成文件后让应用直接读取使用。

核心资源:服务发布(Service、Ingress)

Ingress-nginx的官方文档:https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#rewrite

Ingress-nginx Github:https://github.com/kubernetes/ingress-nginx

1 Service资源的基础应用

1.1 创建Service资源

  创建Service对象的常用方法有两种:一是直接使用"kubectl expose"命令;另一种是使用资源配置文件,它与此前使用的资源清单文件配置其他资源的方法类似。

1.1.1 第一种方法:

  pod(po),service(svc),replication controller(rc),deployment(deploy),replica set(rs)

  Kubectl语法

kubectl expose (-f FILENAME | TYPE NAME) [--port=port] [--protocol=TCP|UDP] [--target-port=number-or-name] [--name=name] [--external-ip=external-ip-of-service] [--type=type]

  示例:

  为rc的nginx创建一个service,该服务位于端口80上,并连接到端口8000的容器上。

# kubectl expose rc nginx --port=80 --target-port=8000

  为deploy创建一个类型为NodePort的,对外暴露端口为80,名称为myapp的服务

# kubectl expose deployment myapp --type=”NodePort" --port=8O --name=myapp

  注意:Service资源的默认类型为ClusterIP,它仅能接收来自于集群内的Pod对象中的客户端程序的访问请求。

1.1.2 第二种方法:

  定义Service资源对象时,spec的两个较为常用的内嵌字段分别为selector和ports,分别用于定义使用的标签选择器和要暴露的端口。

apiVersion: v1 
kind: Service
metadata:
  name: myapp-svc 
spec:
  selector: 
    app:myapp
  ports:
  - protocol: TCP
    port: 80 
    targetPort: 80

2 Service会话粘性

  Service资源还支持Session affinity(会话粘性)机制,它能将来自同一个客户端的请求始终转发至同一个后端的Pod对象,这意味着他会影响调度算法的流量分发功能,进而降低其负载均衡的效果。

  Session affinity的效果仅会在一定时间期限内生效,默认值为10800秒,超出此时长之后,客户端再次访问被调度算法重新调度。另外,Service资源的Session affinity机制仅能基于客户端IP地址识别客户端身份,他会把经由同一个NAT服务器进行源地址转发的所有客户端识别为同一个客户端,调度粒度粗糙且效果不佳,因此,实践中并不推荐使用此种方法实现粘性会话。

  Service资源通过.spec.sessionAffinity和.spec.sessionAffinityConfig两个字段配置粘性会话。spec.sessionAffinity字段用于定义要使用的粘性会话类型,它仅支持使用“None”和“ClientIp”两种属性。

  • None:不使用sessionAffinity,默认值

  • ClientIP:基于客户端IP地址识别客户端身份,把来自同一个源IP地址的请求始终调度至同一个Pod对象

  在启用粘性会话机制时,.spec.sessionAffinityConfig用于配置会话保持时长,他是一个嵌套字段,使用格式如下,可用时长范围为“1-86400”,默认为10800秒

spec:
  sessionAffinity: ClientIp 
  sessionAffinityConfig:
    clientIP:
    timeoutSeconds: <integer>

3 服务暴露

  Service的IP地址仅在集群内可达,然而,总会有些服务需要暴露到外部网络中接收各类客户端的访问,此时,就需要在集群边缘为其添加一层转发机制,已实现外部请求流量接入到集群的Service资源之上,这种操作也成为发布服务到外部网络中。

3.1 Service类型

  Kubernetes的Service共有四种类型:ClusterIP、NodePort、LoadBalancer和ExternalName。

  • ClusterIP:通过集群内部IP地址暴露服务,此地址仅在集群内部可达,而无法被集群外部的客户端访问。此为默认的Service类型

  • NodePort:这种类型建立在ClusterIP类型之上,其在每个Node节点的某静态端口暴露服务,简单来说,NodePort类型就是在工作节点的IP地址上选择一个端口用于将集群外部的用户请求转发至目标Service的ClusterIP和Port,因此,这种类型的Service既可如ClusterIP一样受到集群内部客户端Pod的访问,可会受到集群外部客户端通过套接字<NodeIP>:<NodePort>进行请求。

  • LoadBalancer:这种类型构建在NodePort类型之上,也就是通过公有云LB将NodePort暴露的<NodeIP>:<NodePort>配置在LB上,实现负载均衡

  • ExternalName:目的是让集群内的Pod资源能够访问外部的Service的一种实现方式

3.2 NodePort类型的Service资源
apiVersion: v1
kind: Service  
metadata:
  name: myapp-svc-nodeport 
spec:
  type: NodePort
  selector: 
    app:myapp
  ports:
  - protocol: TCP
    port: 80 
    targetPort: 80 
    nodePort: 32223

==============================

port: 80 #表示service端口

targetPort: 80 #表示Pod容器端口

nodePort: 32223 #表示Node节点暴露端口

3.3 LoadBalancer类型的Service资源

  NodePort类型的Service资源虽然能够于集群外部访问,但外部客户端必须得事先得知NodePort和集群中至少一个节点的IP地址,且选定的节点发生故障时,客户端还得自行选择请求访问其他节点,另外,集群节点很可能是某IaaS云环境中使用私有IP地址的VM,或者是IDC中使用的私有地址的物理机,这类地址对互联网客户端不可达,因为,一般,还应该在集群外创建一个具有公网IP地址的负载均衡器,由他接入外部客户端的请求并调度至集群节点相应的NodePort之上。

apiVersion: v1
kind: Service  
metadata:
  name: myapp-svc-lb 
spec:
  type: LoadBalancer
  selector: 
    app:myapp
  ports:
  - protocol: TCP
    port: 80 
    targetPort: 80 
    nodePort: 32223
3.4 ExternalName Service

  ExternalName类型的Service资源用于将集群外部的服务发布到集群中以供Pod中的应用程序访问,因此,它不需要使用标签选择器关联任何的Pod对象,但必须要使用spec.externalName属性定义一个CNAME记录用于返回外部真正提供服务的主机的别名,而后通过CNAME记录值获取到相关主机的IP地址。

apiVersion: v1 
kind: Service
metadata :
  name : external-redis-svc
  namespace: default 
spec:
  type: ExternalName 
  externaIName : redis.ilinux.io 
  ports:
  - protocol: TCP
    port: 6379 
    targetPort: 6379 
    nodePort: 0
    selector: {}

  待Service资源external-redis-svc创建完成后,各Pod对象即可通过external-redis-svc或其FQDN格式个名称external-redis-svc.default.svc.cluster.local访问相应的服务。

4 Headless类型的Service资源

  Service对象隐藏了各Pod资源,并负责将客户端的请求流量调度至该组Pod对象之上。不过,偶尔也会存在这样一类需求:客户端需要直接访问Service资源后端的Pod资源,这时就应该向客户端暴露每个Pod资源的IP地址,而不再是中间层Service对象的ClusterIP,这种类型的Service资源便称为Headless Service。

apiVersion: v1
kind: Service  
metadata:
  name: myapp-headless-svc 
spec:
  clusterIP: None 
  selector :
    app: myapp 
  ports :
  - port: 80 
    targetPort: 80 
    name: httpport

5 Ingress资源

5.1 创建Ingress资源

  Ingress资源是基于HTTP虚拟主机或URL的转发规则,它在资源配置清单的spec字段中嵌套了rules、backend和tls等字段进行定义。

  下面的示例中定义了一个Ingress资源,它包含了一个转发规则,把发往www.ilinux.io的请求代理给名为myapp-svc的Service资源

ap1Version: extesions/v1beta1
kind: Ingress
metadata:
  name: my-ingress 
  annotations :
    kubernetes.io/ingress.class: "nginx" 
spec:
  rules:
  - host: www.ilinux.io
    http: 
      paths:
      - backend:
          serviceName: myapp-svc 
          servicePort: 80

高级调度

污点(Taint)、容忍度(Toleration)

初始化容器(InitContainer)

node亲和性

pod亲和性

拓扑域(Topology),比如实现多地多机房部署

临时容器

计划任务(CronJob)

存储

volumes:存储卷

EmptyDir实现数据共享

HostPath挂载宿主机路径

挂载NFS至容器

持久化存储(PV、PVC)

presistentvolume:pv,持久化存储卷

storageclass:动态存储

权限管理:rbac

Role-Based Access Control,中文全称:基于角色的权限访问控制

资源配额:Resource Quotas

K8s 资源配额管理对象 ResourcesQuota

Kubernetes 是一个多租户平台,更是一个镜像集群管理工具。一个 Kubernetes 集群中的资源一般是由多个团队共享的,这时候经常要考虑的是如何对这个整体资源进行分配。在 kubernetes 中提供了 Namespace 来讲应用隔离,那么是不是也能将资源的大小跟 Namespace 挂钩进行一起隔离呢?这当然是可以的,Kubernetes 提供了 Resources Quotas 工具,让集群管理员可以创建 ResourcesQuota 对象管理这个集群整体资源的配额,它可以限制某个 Namespace 下计算资源的使用量,也可以设置某个 Namespace 下某种类型对象的上限等。

上面说白了就是,通过设置不同的 Namespace 与对应的 RBAC 权限将各个团队隔离,然后通过 ResourcesQuota 对象来限制该 Namespace 能够拥有的资源的多少

总结

可以使用 ResourceQuota 来限定名称空间中所有容器的内存请求(request)之和不超过指定的配额。同时也可以设置内存限定(limit)、CPU请求(request)、CPU限定(limit)的资源配额。

如果需要限定单个Pod、容器的资源使用情况,请参考 LimitRange

ResourceQuota 对象一般在 Kubernetes 中是默认开启的,如果未开启且不能创建该对象,那么可以进入 Master 的 Kubernetes 配置目录修改 Apiserver 配置文件 kube-apiserver.yaml,在添加参数 --admission-control=ResourceQuota 来开启。

spec:
  containers:
  - command:
    - kube-apiserver
    - --advertise-address=192.168.2.11
    - --allow-privileged=true
    - --authorization-mode=Node,RBAC
    - --admission-control=ResourceQuota    #开启ResourceQuota
    - ......

创建 resources-test1.yaml 用于设置计算资源的配额

apiVersion: v1
kind: ResourceQuota
metadata:
  name: compute-resources
spec:
  hard:
    pods: "4"
    requests.cpu: "1"
    requests.memory: 1Gi
    limits.cpu: "2"
    limits.memory: 2Gi

注意:一个 Namespace 中可以拥有多个 ResourceQuota 对象。

计算资源配额: 限制一个 Namespace 中所有 Pod 的计算资源(CPU、Memory)的总和。

存储资源配额: 限制一个 Namespace 中所有存储资源的总量。

对象数量配额: 限制一个 Namespace 中指定类型对象的数量。

(1)、ResourcesQuota 支持的计算资源:

  • cpu: 所有非终止状态的Pod中,其CPU需求总量不能超过该值。

  • limits.cpu: 所有非终止状态的Pod中,其CPU限额总量不能超过该值。

  • limits.memory: 所有非终止状态的Pod中,其内存限额总量不能超过该值。

  • memory: 所有非终止状态的Pod中,其内存需求总量不能超过该值。

  • requests.cpu: 所有非终止状态的Pod中,其CPU需求总量不能超过该值。

  • requests.memory: 所有非终止状态的Pod中,其内存需求总量不能超过该值。

(2)、ResourcesQuota 支持限制的存储资源:

  • requests.storage:所有 PVC 请求的存储大小总量不能超过此值。

  • Persistentvolumeclaims: PVC 对象存在的最大数目。

  • .storageclass.storage.k8s.io/requests.storage: 和 StorageClass 关联的 PVC 的请求存储的量大小不能超过此设置的值。

  • .storageclass.storage.k8s.io/persistentvolumeclaims: 和 StorageClass 关联的 PVC 的总量。

(3)、ResourcesQuota 支持限制的对象资源:

  • Configmaps: 允许存在的 ConfigMap 的数量。

  • Pods: 允许存在的非终止状态的 Pod 数量,如果 Pod 的 status.phase 为 Failed 或 Succeeded , 那么其处于终止状态。

  • Replicationcontrollers: 允许存在的 Replication Controllers 的数量。

  • Resourcequotas: 允许存在的 Resource Quotas 的数量。

  • Services: 允许存在的 Service 的数量。

  • services.loadbalancers: 允许存在的 LoadBalancer 类型的 Service 的数量。

  • services.nodeports: 允许存在的 NodePort 类型的 Service 的数量。

  • Secrets: 允许存在的 Secret 的数量。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值