4.5 配置网桥过滤和地址转发
4.6 配置ipvs功能
安装配置 ipvs
查看ipvs规则
5. 安装docker
安装必备的软件包以允许apt通过 HTTPS 使用存储库
添加Docker官方版本库的GPG密钥
~~添加官方源~~
官方源被墙了,转用阿里源
安装docker
每个节点都必须安装docker
卸载 docker engine 方式 在安装时,不要卸载!!!
6. 安装 kubernetes
6.1 安装 k8s 需要的包
6.2 添加 k8s 源
以下源二选一
6.3 添加 k8s 公钥
6.4 安装 kubelet kubeadm kubectl
6.5 安装 cri-dockerd
cri-dockerd是容器运行时接口(Container Runtime Interface,CRI),通过 cri 才能使用 docker 。在 v1.24 更早版本中,k8s中自带一个 dockershim 组件,v1.24时就已经把它给移除了,所以需要一个替代品,就是 cri-dockerd 。
control-plane、work-node 节点都需要安装!
注意:如果是从 containerd 换成的 cri-dockerd ,那么执行 kubeadm命令时报 : Found multiple CRI endpoints on the host. Please define which one do you wish to use by setting the 'criSocket' field in the kubeadm configuration file: unix:///var/run/containerd/containerd.sock, unix:///var/run/cri-dockerd.sock 在语句后面加上 --cri-socket=unix:///var/run/cri-dockerd.sock
配置kubelet 使用新的容器运行时
6.6 初始化控制平面
- --image-repository registry.aliyuncs.com/google_containers:表示使用阿里云镜像仓库,不然有些镜像下载不下来
- --kubernetes-version=v1.24.17:指定kubernetes的版本
- --pod-network-cidr=10.244.0.0/16:指定pod的网段
- --config kubeadm-config.yaml
- coredns是一个用go语言编写的开源的DNS服务
- --apiserver-advertise-address 集群内通告地址,相当于内网地址
- --control-plane-endpoint=124.223.45.80:6443 参数指定了公网 IP 地址和端口,用于外部访问 Kubernetes 控制平面
- --service-cidr=10.96.0.0/12 集群内部虚拟网络,Pod统一访问入口
- --cri-socket 指定cri-dockerd接口,如果是containerd则使用--cri-socket unix:///run/containerd/containerd.sock
6.7 重置 kubeadm (集群有问题时才执行)
按需执行!!!
master节点执行命令
子节点执行命令
6.8 配置kubectl命令tab键自动补全
7. 部署CNI网络插件 calico
虽然现在kubernetes集群已经有1个master节点,2个worker节点,但是此时三个节点的状态都是NotReady的,原因是没有CNI网络插件,为了节点间的通信,需要安装cni网络插件,常用的cni网络插件有calico和flannel,两者区别为:flannel不支持复杂的网络策略,calico支持网络策略,因为今后还要配置kubernetes网络策略networkpolicy,所以本文选用的cni网络插件为calico! calico官方文档
7.1 安装 calico
只需在一台 control-plane 安装即可
7.2 清除 calico 网络接口
8. 部署nginx测试 k8s 能否正常工作
9. 配置 在 node 节点上运行 kubectl 命令
kubectl的配置文件在 $HOME/.kube 目录下,要在node节点运行,配置文件肯定不能少。
10. 一些设置命令
10.1 允许控制平面上调度 pod
11. k8s 升级
k8s升级版本只能一个版本逐级升级,不能跨版本升级!升级节点时,最好每个节点依次升级!
12. 备份 etcd
etcd 是 k8s 的数据库,数据库目录位于 /var/lib/etcd 。

12.1 安装 etcdctl
12.2 检查快照数据完整性
12.3 备份
12.4 使用快照恢复
12.5 验证修复
13. kube-proxy 修复
二. Helm
Helm 是 k8s 的包管理器。类似于 ubunutu 的 apt 和 apt-get 包管理器,用于简化应用的部署和管理。Helm 将 Chart 中的模板文件(templates/*.yaml)与用户提供的 values.yaml 或命令行参数结合,生成最终的 Kubernetes 资源清单(YAML 文件),Helm 客户端直接将渲染后的 YAML 提交给 Kubernetes API Server,创建或更新资源。
helm 中几个概念解释: - Chart: 是创建一个应用的信息集合,包括各种 k8s 对象的配置模板、参数定义、依赖关系、文档说明等。chart 是应用部署的自包含逻辑单元。可以将 chart 想象成 apt 的包管理工具。 - Release: 是 chart 的运行实例,代表了一个正在运行的应用。当 chart 被安装到 k8s 集群,就生成一个 release。chart 能够多次安装到同一个集群,每次安装都是一个 release。 - helm cli: helm 客户端组件,负责和 k8s api Server 通信。 - Repository: 用于发布和存储 chart 的仓库。
helm 有两个版本 v2 , v3: - v2:v2 版本是 C/S 架构,依赖服务端组件 Tiller(运行在集群中),负责管理 Release 状态,存在安全风险(Tiller 拥有集群管理员权限)。 - v3:移除了 Tiller,完全通过客户端(helm 命令)与 Kubernetes API 直接交互,Release 信息存储在集群的 Secrets 中,安全性更高。
1. 安装
1.1 ubuntu 安装
1.2 二进制文件安装
- 去他们的 Github 仓库 下载需要的版本,下载 amd64架构的。
- 解压
tar -zxvf helm*.tar.gz - 在解压目录中找到 helm,移动到需要的目录中
mv linux-amd64/helm /usr/local/bin/helm
1.3 Homebrew
2. 部署应用
helm 可以从多个源进行安装
2.1 添加 Chart 仓库
2.2 安装 Chart 示例
默认部署
通过 yaml 文件自定义端口部署 编写配置文件 values.yaml。要修改什么就写什么配置,helm 用 values.yaml 去覆盖默认配置。
部署应用,-f 与 --values 等价,该参数可以使用多次,最右边的配置文件优先级最高!
通过命令行方式自定义端口部署
2.3 helm 安装资源顺序
Helm 在安装 Chart 时,会按照 特定顺序 创建 Kubernetes 资源,以确保依赖关系和初始化逻辑的正确性。以下是 Helm 资源安装的核心顺序规则:
3. 命令
3.1 upgrade
upgrade 用于升级 chart 新版本,或修改 release 的配置
3.2 rollback
用于回滚到之前发布的版本 - --dry-run 模拟回滚操作(不实际执行) - --no-hooks 跳过 Hook 的执行 - --disable-openapi-validation 跳过 OpenAPI 验证(兼容旧版本资源) - --force 强制回滚(即使资源已不存在) - --cleanup-on-fail 回滚失败时清理残留资源 - --timeout 5m 设置超时时间(默认 5 分钟),单位 ns,us,ms,s,m,h - --wait 等待所有资源就绪(默认启用)才会标记该 release 为成功。 - --no-wait 异步回滚(不等待资源就绪)
s
三. Kubernetes
1. 资源管理方式
资源管理方式主要有三种。
- 命令式对象管理
直接使用命令去操作kubernetes资源;缺点:无法审计和跟踪
kubectl run nginx-pod --image=nginx:1.17.1 --port=80
···········- 命令式对象配置
通过命令配置和配置文件去操作kubernetes资源;优点:可以审计跟踪;缺点:项目大时配置文件太多
kubectl create/patch -f nginx-pod.yaml
- 声明式对象配置
通过apply命令和配置文件去操作kubernetes资源,主要用于创建和更新资源;优点:支持目录操作;缺点:意外情况难调试
kubectl apply -f nginx-pod.yaml
1.1 命令式对象管理
kubectl命令的语法: kubectl [command] [type] [name] [flags]
- comand:指定要对资源执行的操作,例如create、get、delete
- type:指定资源类型,比如deployment、pod、service
- name:指定资源的名称,名称大小写敏感
- flags:指定额外的可选参数
kubectl api-resources查看;常用资源如下:
| 资源名称 | 缩写 | 资源作用 |
|---|---|---|
| nodes | no | 集群组成部分 |
| namespaces | ns | 隔离Pod |
| pods | po | 装载容器 |
| replicationcontrollers | rc | 控制pod资源 |
| replicasets | rs | 控制pod资源 |
| deployments | deploy | 控制pod资源 |
| daemonsets | ds | 控制pod资源 |
| jobs | 控制pod资源 | |
| cronjobs | cj | 控制pod资源 |
| horizontalpodautoscalers | hpa | 控制pod资源 |
| statefulsets | sts | 控制pod资源 |
| services | svc | 统一pod对外接口 |
| ingress | ing | 统一pod对外接口 |
| volumeattachments | 存储 | |
| persistentvolumes | pv | 存储 |
| persistentvolumeclaims | pvc | 存储 |
| configmaps | cm | 存储 |
| secrets | 存储 |
kubernetes允许对资源进行多种操作,可以通过--help查看详细的操作命令 kubectl --help;常用操作如下所示:
| 命令 | 翻译 | 命令作用 |
|---|---|---|
| create | 创建 | 创建一个资源 |
| edit | 编辑 | 编辑一个资源 |
| get | 获取 | 获取一个资源 |
| patch | 更新 | 更新一个资源 |
| delete | 删除 | 删除一个资源 |
| explain | 解释 | 展示资源文档 |
| run | 运行 | 在集群中运行一个指定的镜像 |
| expose | 暴露 | 暴露资源为Service |
| describe | 描述 | 显示资源内部信息 |
| logs | 日志 | 输出容器在 pod 中的日志 |
| attach | 缠绕 | 进入运行中的容器 |
| exec | 执行 | 执行容器中的一个命令 |
| cp | 复制 | 在Pod内外复制文件 |
| rollout | 首次展示 | 管理资源的发布 |
| scale | 规模 | 扩(缩)容Pod的数量 |
| autoscale | 自动调整 | 自动调整Pod的数量 |
| apply | 应用 | 通过文件对资源进行配置 |
| label | 标签 | 更新资源上的标签 |
| cluster-info | 集群信息 | 显示集群信息 |
| version | 版本 | 显示当前Server和Client的版本 |
1.2 命令式对象配置
通过命令操作配置文件来管理资源。
创建一个 nginxpod.yaml
1.3 声明式对象配置
声明式对象配置只有一个 apply 命令
2. 资源简介
2.1 namespace
namespacek8s中的一种非常重要的资源,主要用于实现多租户的资源隔离。 默认情况下,集群中的所有pod都是可以互相访问的。实际中,不可能让所有的pod都能进行访问,namespace就可以解决这个问题。namespace把pod划分到不同的“组”,组内资源可以互相访问,但组外资源不可访问,从而实现资源隔离。此外,namespace还可以结合kubernetes的资源配额机制,限定不同租户所占用的资源,如cpu使用量,内存使用量等,实现多租户的可用资源管理。 kubernetes默认存在4个namespace - default :所有未指定namespace的对象都会被分配在default中 - kube-node-lease:集群节点的心跳维护,v1.13开始引入 - kube-public:该namespace中的资源可以被所有人访问,包括为认证用户 - kube-system:由kubernetes所创建的资源都存在此namespace
2.2 pod
pod是k8s中管理的最小单元,程序要运行必须部署在容器中,容器又必须存在于pod中。一个pod中可以有一个或者多个容器。
2.2.1 创建并运行
kubectl run podName [options]
- --image 指定pod镜像
- --port 指定端口
- --namespace 指定namespace
例:kubectl run nginx --image=nginx:1.17.1 --port=80 --namespace=dev
2.2.2 查看pod
2.3 label
Label用于在资源上添加标识,进而对资源进行区分和选择。 - Label以 kv 键值对的形式存在,如“version=1.0”,可以附加在如node、pod、service等资源上。 - 每一资源对象可以添加任意数量label, 每一label也可以分配到任意数量的资源上。 - label通常在资源定义时确定,当然也可以在创建后进行添加和删除。 Label Selector 用于查询和筛选拥有标签的资源对象,有两种方式 - 等式:author!=moloom 选择标签名author值不为moloom的资源 - 集合:author in (moloom,robin) 选择标签名author且值为moloom或robin的资源 author not in (moloom) 选择标签名author值不为moloom的资源
2.4 deployment
在k8s中,pod作为最小资源个体,很少直接被k8s控制。一般来说,k8s通过pod控制器来控制pod,pod控制器用于pod的管理,确保pod资源符合与其的状态,当pod资源出现故障时,会尝试进行重启或重建pod。 kubectl create deployment podName [options] - --image 指定pod镜像 - --port 指定端口 - --replicas 指定创建pod数量 - --namespace 指定namespace 例:kubectl create deployment nginx --image=nginx:1.17.1 --port=80 --replicas=3 -n dev 注:新版创建deploy控制器是如上命令。旧版用run命令即可,默认就是由deploy控制器控制。 如上命令执行后,deploy控制器会自动添加一个label ,app=$podName 。
2.5 service
Service可以看作是一组同类Pod对外的访问接口。借助Service,应用可以方便地实现服务发现和负载均衡。
3. Pod
每一个pod中可以有一个或多个容器。pod中有一个名为Pause的容器,她是pod的根容器,整个pod的ip地址被设置在此容器中,其他容器都通过pause容器来实现网络通信,pause也被用于评估整个pod的健康状态。
3.1 pod 关于yaml的配置清单
pod的yaml配置参数详情
3.2 配置中的5个根属性
在k8s中,基本所有一级属性都是一样的,包括以下5部分:
- apiVersion 《string》,由k8s内部定义,版本好必须是用 kubectl api-version 能查到的
- kind 《tring》 定义该配置是属于哪种资源类型,内部定义,必须是 kubectl api-resource可以查到的
- metadata 《Object》,资源标识和说明,有name、namespace、labels等
- status
3.3 pod.spec.containers
主要有如下属性
3.4 生命周期
容器从创建到结束的这个过程叫做生命周期,主要包括:包括pod创建过程,运行初始化容器过程,运行主容器,pod终止过程。 在生命周期中,pod会出现如下五种状态: - 挂起(Pending):apiserver已经创建了pod资源对象,但它尚未被调度完成或者仍处于下载镜像的过程中 - 运行中(Running):pod已经被调度至某节点,并且所有容器都已经被kubelet创建完成 - 成功(Succeeded):pod中的所有容器都已经成功终止并且不会被重启 - 失败(Failed):所有容器都已经终止,但至少有一个容器终止失败,即容器返回了非0值的退出状态 - 未知(Unknown):apiserver无法正常获取到pod对象的状态信息,通常由网络通信失败所导致
3.4.1 pod的创建过程
1. 用户通过kubectl或其他api客户端提交需要创建的pod信息给apiServer
2. apiServer开始生成pod对象的信息,并将信息存入etcd,然后返回确认信息至客户端
3. apiServer开始反映etcd中的pod对象的变化,其它组件使用watch机制来跟踪检查apiServer上的变动
4. scheduler发现有新的pod对象要创建,开始为Pod分配主机并将结果信息更新至apiServer
5. scheduler发现有新的pod对象要创建,开始为Pod分配主机并将结果信息更新至apiServer
8. node节点上的kubelet发现有pod调度过来,尝试调用docker启动容器,并将结果回送至apiServer
6. apiServer将接收到的pod状态信息存入etcd中
3.4.2 pod的终止过程
1. 用户向apiServer发送删除pod对象的命令
2. apiServcer中的pod对象信息会随着时间的推移而更新,在宽限期内(默认30s),pod被视为dead
3. 将pod标记为terminating状态
4. kubelet在监控到pod对象转为terminating状态的同时启动pod关闭过程
5. 端点控制器监控到pod对象的关闭行为时将其从所有匹配到此端点的service资源的端点列表中移除
6. 如果当前pod对象定义了preStop钩子处理器,则在其标记为terminating后即会以同步的方式启动执行
7. pod对象中的容器进程收到停止信号
8. 宽限期结束后,若pod中还存在仍在运行的进程,那么pod对象会收到立即终止的信号
9. kubelet请求apiServer将此pod资源的宽限期设置为0从而完成删除操作,此时pod对于用户已不可见
3.4.3 init container
pod.spec.initContainers
初始化容器是在pod的主容器启动之前要运行的容器,主要是做一些主容器的前置工作,它具有两大特征:
1. 初始化容器必须运行完成直至结束,若某初始化容器运行失败,那么kubernetes需要重启它直到成功完成
2. 初始化容器必须按照定义的顺序执行,当且仅当前一个成功之后,后面的一个才能运行
init container 和container平级,pod.spec.initContainers,子属性和container类似
yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-initcontainer
namespace: dev
spec:
containers:
- name: main-container
image: nginx:1.17.1
ports:
- name: nginx-port
containerPort: 80
initContainers:
- name: test-mysql
image: busybox:1.30
command: ['sh', '-c', 'until ping 192.168.109.201 -c 1 ; do echo waiting for mysql...; sleep 2; done;']
- name: test-redis
image: busybox:1.30
command: ['sh', '-c', 'until ping 192.168.109.202 -c 1 ; do echo waiting for reids...; sleep 2; done;']
3.4.4 钩子函数
pod.spec.containers.lifecycle.postStart pod.spec.containers.lifecycle.preStop 钩子函数能够感知自身生命周期中的事件,并在相应的时刻到来时运行用户指定的程序代码,kubernetes在主容器的启动之后和停止之前提供了两个钩子函数: - poststart:容器创建之后执行,如果失败了会重启容器 - prestop:容器终止之前执行,执行完成之后容器将成功终止,在其完成之前会阻塞删除容器的操作 钩子函数只支持三种动作
- exec:执行一次命令
- TCPSocket:访问指定的socket
3.4.5 容器探测
pod.spec.containers.livenessProbe pod.spec.containers.readinessProbe 容器探测用于检测容器中的应用实例是否正常工作,是保障业务可用性的一种传统机制。如果经过探测,实例的状态不符合预期,那么kubernetes就会把该问题实例" 摘除 ",不承担业务流量。kubernetes提供了两种探针来实现容器探测,分别是: - liveness probes:存活性探针,用于检测应用实例当前是否处于正常运行状态,如果不是,k8s会重启容器 - readiness probes:就绪性探针,用于检测应用实例当前是否可以接收请求,如果不能,k8s不会转发流量
livenessProbe 决定是否重启容器,readinessProbe 决定是否将请求转发给容器。
容器探测也提供了和钩子函数类似的三种动作: 1. exec:执行一次命令
2. TCPSocket:访问指定的socket 3. HTTPGet:向某url发起http请求3.4.6 重启策略
3.5 pod 调度
默认情况下,pod运行在哪个node上,不是随缘的,而是经过一系列算法的计算而得出的,这个过程不受人为控制。但在某些情况下,需要对pod进行固定的调度,把pod运行在某个指定的node上,pod调度就出来了,可以通过pod调度把pod运行到指定的node节点上,kubernetes提供了四大类调度方式: - 自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出 - 定向调度:NodeName、NodeSelector - 亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity - 污点(容忍)调度:Taints、Toleration
3.5.1 定向调度 Directional Scheduling
定向调度是强制调度,就算是不正常运行的node,也还是会调度到该节点上。定向调度可以通过指定nodeName或者指定nodecSelector来进行定向调度。定向调度的优先级高于亲和性调度!!!
nodeName pod.spec.nodeName 将Pod调度到指定的Name的Node节点上。
nodeSelector pod.spec.nodeSelector 将pod调度到添加了指定标签的node节点上,通过kubernetes的label-selector机制实现的,也就是说,在pod创建之前,会由scheduler使用MatchNodeSelector调度策略进行label匹配,找出目标node,然后将pod调度到目标节点,该匹配规则是强制约束。
示例
3.5.2 亲和性调度 Affinity Scheduling
定向调度,有一个弊端,如若指定的node异常,而且有其他正常的node处于空闲中,pod不会“人性化的”调度到正常的node节点上,而亲和性调度恰恰解决的来这个问题。亲和性调度.... Affinity主要分为三类: - nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node的问题 - podAffinity(pod亲和性) : 以pod为目标,解决pod可以和哪些已存在的pod部署在同一个拓扑域中的问题 - podAntiAffinity(pod反亲和性) : 以pod为目标,解决pod不能和哪些已存在pod部署在同一个拓扑域中的问题
关于亲和性(反亲和性)使用场景的说明: 亲和性:如果两个应用频繁交互,那就有必要利用亲和性让两个应用的尽可能的靠近,这样可以减少因网络通信而带来的性能损耗。 反亲和性:当应用采用多副本部署时,有必要采用反亲和性让各个应用实例打散分布在各个node上,这样可以提高服务的高可用性。
NodeAffinity pod.spec.affinity.nodeAffinity 配置模板
示例
- 在亲和性调度中,required和preferred都设置时,只可能会调度到满足required所有条件的node上,在这些node中优先调度符合preferred条件的node
- 如果同时定义了定向调度nodeSelector和亲和性调度nodeAffinity,那么必须两个条件都得到满足,Pod才能运行在指定的Node上
- 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可
- 如果一个nodeSelectorTerms中有多个matchExpressions ,则一个节点必须满足所有的才能匹配成功
- 如果一个pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的节点亲和性需求,则系统将忽略此变化
PodAffinity pod.spec.affinity.podAffinity 模板
示例
PodAntiAffinity pod.spec.affinity.podAntiAffinity 反pod亲和度调度,不和指定的pod所在一个node,配置基本和podAffiniity一样
3.5.3 污点 Taint 和容忍 Toleration
污点 Taint 用于定义 node,设置 node 对 pod 的接受程度。taints有三种: - PreferNoSchedule:kubernetes 将尽量避免把 Pod 调度到具有该污点的 Node 上,除非没有其他节点可调度 - NoSchedule:kubernetes 将不会把 Pod 调度到具有该污点的 Node 上,但不会影响当前 Node 上已存在的 Pod - NoExecute:kubernetes 将不会把 Pod 调度到具有该污点的 Node 上,同时也会将 Node 上已存在的 Pod 驱离
注:如若使用 pod.sepc.nodeName 指定node,则污点将不起作用,pod会直接调度到指定的node!
master 节点,默认是 NoSchedule,这也是为什么pod不会调度到master节点
容忍 Toleration pod.spec.tolerations 容忍,字面意思,对污点Taints的容忍,对pod定义,用于将pod调度到有污点的node上去。
可配置项
for example
4. Pod控制器
pod控制器用于管理pod。通常创建pod有两种方式,一种是直接创建pod,第二种是通过控制器创建pod。 - ReplicationController: 比较老的控制器,已被废弃 - ReplicaSet: 保证一定数量的pod正常运行,支持pod数量的扩缩容,镜像版本升级(通过升级镜像来升级pod) - Deployment: 基于ReplicaSet实现,拥有ReplicaSet的功能,而且还支持滚动升级和回退版本 - Horizontal Pod Autoscaler: 根据机器负载,自动水平调整pod的数量,实现削峰填谷 - DaemonSet: 在指定的node上运行一个副本,一般用于守护进程类的任务 - Job: 一次性执行任务,执行完后立即退出 - Cronjob: 定时执行任务,不需要持续后台运行 - StatefulSet: 管理有状态的任务
4.1 ReplicaSet
ReplicaSet保证一定数量的pod正常运行,支持pod数量的扩缩容,镜像版本升级(通过升级镜像来升级pod)。 配置模板
replicaSet 的镜像升级设置来之后,正常运行的pod不会升级,还是保持原来版本,只有从该 rs controller 新起来的pod才会是最新版本!
4.2 Deployment
deployment是基于 replicaSet 实现的,拥有 rs 的全部功能。在此之上,还支持滚动更新、回滚版本和发布的停止继续。 配置模板
针对版本升级相关功能,由 rollout 管理,有如下几个选项: - status 显示当前升级状态 - history 显示 升级历史记录 - pause 暂停版本升级过程 - resume 继续已经暂停的版本升级过程 - restart 重启版本升级过程 - undo 回滚到上一级版本(可以使用--to-revision回滚到指定版本)
金丝雀发布:金丝雀发布就是在版本升降级时,先升一小部分运行,看看运行情况怎么样。如果正常则再一部分一部分地升级。金丝雀发布虽然麻烦,但是可以尽量减少升降级时造成系统不可用概率。
4.3 Horizontal Pod Autoscaler
在Deploy中,已经可以通过 scale 命令来实现扩缩容了,但是只能手动扩缩容,显得不够智能,为此,Horizontal Pod Autoscaler (HPA)出现了。hpa 能监控pod使用情况,如果pod负载达到一个阈值,hpa 可以自动的扩容,在流量不多时,为了节省资源,会自动缩容到原始pod个数,真正实现智能"削峰填谷"。
4.3.1 安装 metrics-server
4.3.2 准备deploy
4.3.3 部署hpa
创建 pc-hpa-nginx.yaml
注意:被hpa监控的资源,必须要配置 resources.requests.cpu 设置cpu下限,否则get hpa 时,TARGETS 中会显示
运行和测试
用jmeter进行测试4.4 DaemonSet
daementSet 控制器可以保证在每个集群上(不包括 control-panel)都运行一个副本,适用于日志、监控等场景。 配置模板
4.5 Job
Job执行完任务后自动会销毁,适用于一次性任务。Job在执行成功结束时,会记录成功了的pod数量,当数量达到预先指定的值时,Job将完成执行。 Job的 restartPolicy 不同于其他控制器,Job由于执行完后就销毁,所以不能设置 Always ,只能设置 OnFailure 和 Never。在设置为 OnFailure 参数时,pod故障了只会重启容器,不会新建pod,failed次数不变。在设置为 Nerver 时,pod故障了会新建pod,并且故障的pod不会被删除和重启,failed次数加1。
配置模板
demo
job每个时刻只会运行 parallelism 个pod,如果没有设置则默认为1。
4.6 CronJob (CJ)
CronJob 定时执行一次Job任务,它基于 Job ,所以它也是在达到完成次数时会销毁。有点类似 Linux 系统中的 crontab 命令。
配置模板
5. Service
前面已经学习了 pod 和 pod 控制器,我们现在能很好地使用 k8s 创建 pod 来执行任务了。但是还有些不足,首先,这些 pod 只能在集群内被访问,而且,pod只是临时资源,你无法实时辨别哪些pod是健康与否,它们并不可靠。pod故障重建后,pod 的 ip 会发生变化,原来的ip就不能用了,这就导致了资源访问者无法跟踪和访问要连接的 pod 的 ip 地址,而Service 就是来解决这个问题的! Service 在 k8s 中是一个对象,它一种抽象的概念,实现者是 kube-proxy (除 type 为 ExternalName 外)。service 提供一个入口地址用于访问 service 所匹配的 pod ,且支持负载均衡算法。
5.1 模板和示例
配置模板
示例
5.2 selector 选择算符
service.spec.selector service 分有 selector 和无 selector 的 service。顾名思义,区别就是在 service 中有没有 selector 这项,有就是 有选择算符的,没有就是 无选择算符的。如上简单示例就是有选择算符的。 上面也介绍过,service 就是为某类 pod 提供一个访问入口的。何为“某类”?就是通过 selector 选择算符来设置的。
5.2.1 有选择算符
有选择算符代表你这个 svc 是指向 pod 集合的。k8s 控制平面会为有选择算符的 svc 创建 EndpointSlice 对象,这个对象包含 service 到 pod 的引用,也就是 svc 和 pod 之间的映射关系。
5.2.2 无选择算符
没有选择算符的 svc 只提供一个 “入口”,并没有转到某个资源。相当于申请了一个公网 ip ,缺没有部署任何服务。
无选择算符的 svc ,控制平面不会创建 EndpointSlice ,但是可以自定义 EndpointSlice。自定义的 EndpointSlice 可以映射到其他类型的资源, 包括在集群外运行的资源。EndpointSlice 在下面章节会详细解释,这里只需了解即可。
Kubernetes API 服务器不允许将流量代理到未被映射至 Pod 上的端点。由于此约束,当 Service 没有选择算符时,诸如 kubectl port-forward service/
5.3 EndpointSlice 端点切片
EndpointSlice 是表示某个 svc 的后端网络端点的对象资源,通过 EndpointSlice 可以建立 svc 与 pod 之间的映射关系。需注意:svc 只会对 就绪状态 的 pod 进行映射!若要对 NotReady 状态的 pod 进行映射,需设置 svc.spec.publishNotReadyAddresses: true 。
一般情况下,在 svc 配置中,若设置了选择算符,在创建 svc 时 control-plane 会自动为 svc 创建 EndpointSlice,无选择算符的 svc ,control-plane 不会创建 EndpointSlice ,但是可以自定义 EndpointSlice,自定义的 EndpointSlice 可以映射到其他类型的资源。
5.3.1 示例
创建一个 svc ,把所有访问此 svc 的请求 转到 百度
当为 Service 创建 EndpointSlice 对象时,可以为 EndpointSlice 使用任何名称。 一个名字空间中的各个 EndpointSlice 都必须具有一个唯一的名称。通过在 EndpointSlice 上设置 kubernetes.io/service-name 标签可以将 EndpointSlice 链接到 Service。
官方文档建议:自定义的 endpointSlice ,应添加 endpointslice.kubernetes.io/managed-by 标签,用于标示管理 EndpointSlice 的控制器或实体。打上该标签易于集群内的不同控制器来管理不同的 EndpointSlice 对象。标签值需要依据如下类别来设置。
- 如果是使用 kubectl 来创建的: 自己起一个
- 第三方工具创建的:值为全小写的工具名称,并将空格和其他标点符号更改为短划线 (-)
- 自定义控制器创建的:使用类似于 "my-domain.example/name-of-controller" 的值
默认情况下,控制面创建和管理的 EndpointSlice 将包含不超过 100 个端点。 你可以使用 kube-controller-manager 的 --max-endpoints-per-slice 标志设置此值,最大值为 1000。
5.3.2 一些注意事项
注:端点 IP 地址必须不是:本地回路地址(IPv4 的 127.0.0.0/8、IPv6 的 ::1/128) 或链路本地地址(IPv4 的 169.254.0.0/16 和 224.0.0.0/24、IPv6 的 fe80::/64)。
端点 IP 地址不能是其他 Kubernetes 服务的集群 IP,因为 kube-proxy 不支持将虚拟 IP 作为目标地址。
Kubernetes API 服务器不允许将流量代理到未被映射至 Pod 上的端点。由于此约束,当 Service 没有选择算符时,诸如 kubectl port-forward service/
forwardedPort:servicePort 之类的操作将会失败。 这可以防止 Kubernetes API 服务器被用作调用者可能无权访问的端点的代理。不过,ExternalName 是 svc 的特例,它没有选择算符,也没有 endpointslice ,它是使用的 DNS 名称!
5.3.3 EndpointSlice 和 Endpoints的区别
两者都是提供 svc 和 pod 之前的映射关系的资源对象。svc 和 Endpoints 是一对一的关系,一个 svc 中的所有 Endpoint 都只能存到同一个 Endpoints 对象中,容易导致 Endpoints 对象过于巨大,不好维护和更新。svc 和 EndpointSlice 是一对多的关系,一个 svc 可以有多个 EndpointSlice,使用 EndpointSlices 时,添加或移除单个 Pod 对于正监视变更的客户端会触发相同数量的更新,而不是像 Endpoints 所有的映射都会更新,此外,EndpointSlices 还支持围绕双栈网络和拓扑感知路由等新功能的创新。 Endpoints 只支持1000个端点映射,超出部分不进行映射。对 EndpointSlice 来说,若超过了 100 个端点,则会创建一个新的 EndpointSlice,可以使用 kube-controller-manager 的 --max-endpoints-per-slice 标志来更改每个 EndpointSlice 最大映射的数量,最大值为 1000,但不推荐使用,会影响代理的性能。
5.4 Service 类型
service.spec.type
type类型有4种:ClusterIP、NodePort、LoadBalancer 和 ExternalName。
5.4.1 ClusterIP
service.spec.clusterIP
默认模式,当 service.spec.type 没有赋值时,默认为 ClusterIP 。k8s会为 svc 分配一个集群内部 ip ,仅供集群内部访问。
5.4.1.1 Headless Service 无头服务
若设置 service.spec.clusterIP: none 则不会分配 IP 地址,该 svc 也变成了 Headless Services 无头服务
每当 svc 被创建时,会默认添加一条 DNS 记录到 coreDNS 服务。域名格式为 svcName.nameSpace.svc.cluster.local 例: svc-to-baidu.dev.svc.cluster.local,该域名指向当前 svc 。
Headless Service 通过内部 DNS 记录报告各个 Pod 的端点 IP 地址,DNS 如何自动配置取决于 Service 是否定义了选择器。
1. 带选择算符的服务:Kubernetes 控制平面在 Kubernetes API 中创建 EndpointSlice 对象,并且修改 DNS 配置返回 A 或 AAAA 记录(IPv4 或 IPv6 地址), 这些记录直接指向 Service 的后端 Pod 集合。
2. 无选择算符的服务
- 对于 type: ExternalName Service,查找和配置其 CNAME 记录
- 对于其他类型的 svc ,创建对应的 A/AAAA DNS 记录
CNAME(Canonical Name) 是一种 DNS 记录类型,用于将一个域名映射到另一个域名。
5.4.2 NodePort
分配一个 node 的端口给 svc ,可以通过任一 node:port 方式访问 svc 。
5.4.3 LoadBalance
使用云平台的负载均衡器向外部公开 Service。Kubernetes 不直接提供负载均衡组件; 你必须提供一个,或者将你的 Kubernetes 集群与某个云平台集成。来自外部负载均衡器的流量将被直接重定向到后端各个 Pod 上,云平台决定如何进行负载平衡。
service.spec.loadBalancerClass 来设置 。如果使用默认的负载均衡器则会忽略这个属性;
对于 type: LoadBalancer 的 Service,控制器可以设置 .status.loadBalancer.ingress.ipMode。 .status.loadBalancer.ingress.ipMode 指定负载均衡器 IP 的行为方式。 此字段只能在 .status.loadBalancer.ingress.ip 字段也被指定时才能指定。 - 如果流量被传递到节点,然后 DNAT 到 Pod,则目的地将被设置为节点的 IP 和节点端口; - 如果流量被直接传递到 Pod,则目的地将被设置为 Pod 的 IP 和端口。
如果 LoadBalance 类型的 svc 定义了多个端口时,通过 MixedProtocolLBService 来开启或关闭是否支持使用不同的协议。V1.24 起默认开启。
5.4.4 ExternalName
将服务映射到 externalName 字段的内容(例如,映射到主机名 api.foo.bar.example),集群不会为之创建任何类型代理。在访问时,重定向发生在 DNS 级别,而不是通过代理或转发来完成。
不能映射到 https 服务,会报:SSL 证书未包含请求的主机名!
5.5 Gateway 网关
5.5.1 安装 Gateway API CRDs
5.6 Ingress
ingress 是把集群中服务向外部暴露,供外部访问的一个资源对象,ingress 定义从外部访问集群服务的规则(例如路径转发、域名转发、TLS 等)。下面是 ingress 的流量走向示意图。
5.6.1 Resource 资源后端
ingress 为 service 提供集群外部的可访问 URL,同时支持对流量进行负载均衡。ingress 不仅可以转发外部请求到 service,还能转发到 Resource,Resource 是一个 ObjectRef 对象,指向同 ns 下面的另一个资源对象。Resource 和 service 是互斥的,一个 ingress 只能转发到其中一个。
5.6.2 Path Type 路径类型
Ingress.spec.rules.http.paths.path
ingress 当前支持的路径类型有三种:
1. ImplementationSpecific:该路径类型的匹配方法取决于 ingressClass,具体实现可以将其作为单独的 pathType 处理或者作与 Prefix 或 Exact 类型相同的处理。
2. Exact:精确匹配 URL 路径,且区分大小写!例:path: /foo,请求路径为:/foo/,不匹配
3. Prefix:基于以 / 分隔的 URL 路径前缀匹配。匹配区分大小写!类似于 nginx 的匹配规则
在某些情况下,ingress 中会有多条路径与同一个请求匹配。这时匹配路径最长者优先。 如果仍然有两条同等的匹配路径,则 Exact 路径类型 优先于 Prefix 路径类型。
5.6.3 主机名通配符
不止 URL 的路径可以匹配,host 主机名也可以匹配。host 默认是精确匹配,如 moarch.com,或者使用通配符来匹配,如 *.moarch.com。
5.6.4 Ingress Class
ingress Class 是 Kubernetes 1.18 引入的资源对象,用于定义和选择 ingress controller 来实际处理 ingress 转发规则,是 ingress 资源与实际控制器之间的桥梁。每个 ingress 都可以指定一个 ingress Class。 s IngressClass.spec.controller 是一个部署在集群中的组件(通常是 Pod),它根据 ingress 资源的内容,配置自己的反向代理服务(如 NGINX、Traefik)来实现真正的转发逻辑。
IngressClass.spec.parameters 的默认作用域是 Cluster,表示 IngressClass 所引用的即是一个集群作用域的资源。如果你将 .spec.parameters.scope 字段设为了 Namespace,那么该 IngressClass 将会引用一个名字空间作用域的资源。 .spec.parameters.namespace 必须和此资源所处的名字空间相同。
5.6.4.1常见的 Ingress 控制器
常见的 ingress 控制器有以下几种: - NGINX Ingress Controller: - APISIX Ingress :是一个基于 Apache APISIX 网关 的 Ingress 控制器,常用于微服务应用 - Traefik: 是一个基于云原生的 Ingress 控制器。 - HAProxy:是一个针对 HAProxy 的 Ingress 控制器 - Istio Ingress:是一个可观测流量的 Ingress 控制器
可以同时部署任意数量个 ingress 控制器。在 ingress 的清单文件中,通过 spec.ingressClassName 字段来指定要使用的 ingress 控制器,否则使用默认控制器。给 IngressClass 添加注解 ingressclass.kubernetes.io/is-default-class: "true"来设置集群默认的 ingree 控制器。
示例
IngressClass 中的
.spec.parameters字段可用于引用其他资源以提供与该 IngressClass 相关的配置。 参数(parameters)的具体类型取决于你在 IngressClass 的 .spec.controller 字段中指定的 Ingress 控制器
5.6.4.2 集群中默认的 Ingress Class
在 IngressClass 资源清单中设置 ingressclass.kubernetes.io/is-default-class: true 注解来设置集群中默认的 ingressClass。如上示例。
如果集群中设置了多个默认的 ingressClass,那么必须在 ingress 清单中通过 ingressClassName 来指定该 ingress 使用的 ingressClass,否则准入控制器将不允许 ingress 对象的创建。
当然,有些 ingress controller 可以不需要设置默认的 ingressClass,如 ingress-nginx controller 就可以通过参数
--watch-ingress-without-class来配置。
5.6.4.3 Ingress Class 清单示例
5.6.5 ingress 配置清单模板
5.6.3 部署 ingress-nginx
在集群中部署 ingress-nginx,ingress-nginx 指南
生成自签名证书
6. "配置" Configuration
6.1 configmap
ConfigMap(cm) 是 Kubernetes 用来管理 非敏感配置数据 的资源对象,主要作用是将应用配置和容器镜像 解耦,让配置可以独立管理,而不需要重新构建镜像。 主要特点: - 存储普通配置(如环境变量、配置文件),不适合存敏感信息(密码、密钥等要用 Secret) - 支持多种数据格式,如 键值对、完整配置文件和二进制数据 - 灵活的使用方式,可以注入为 环境变量,或挂载容器卷 - 大小不能超过 1MB(etcd 的限制)
configMap 功能在 k8s 1.2 版本引入通常用于配置数据的共享,以 readOnly 的模式挂载。 共享文件的主要两种方式: - 共享:每次在读取文件时,请求共享文件,每隔一段时间请求最新的文件,会浪费网络IO。 - 注入:在开始时就注入,后续若更新则重新注入,不会浪费网络IO。ConfigMap 提供了向容器中注入配置信息的机制。
模板
6.1.1 创建方式
6.1.1.1 命令行直接创建
6.1.1.2 通过配置文件创建
配置文件 redis.conf
创建命令6.1.1.3 通过资源清单创建
configMap 资源清单 demo
当然,也可以用以下命令,通过配置生成资源清单文件
6.1.2 如何在容器中使用?
6.1.2.1 通过环境变量使用
当用环境变量方式,配置更新,pod 内的配置,也就是环境变量不会更新。
6.1.2.2 卷挂载
一般情况下,用卷挂载时,配置更新,pod 内也会更新,但是有些应用不会自动触发重启,而导致最新配置未生效。这时候只要让 spec.template.metadata.annotations 下的任意一个键值对有变化,就会触发重启。下面是手动触发 nginx pod 重启示例。
6.2 Secret
Secret 用来保存敏感的信息,例如密码,令牌和密钥等。将这些信息放在 secret 中必放在 Pod 的定义或者容器镜像中来说更加安全和灵活。 Secret 的特点: - Secret 对象只会存储在需要访问 Secret 的 Pod 所在的机器节点,以保证其安全性 - Secret 只会存储在节点的内存中,永不写入物理存储 - 从 k8s 1.7 版本开始,etcd 会以加密形式存储 Secret
6.2.1 模板
6.2.2 创建 Secret
6.2.3 使用
6.2.3.1 命令获取 secret 值
6.2.3.2 容器卷挂载
挂载 secret 所有数据
挂载部分数据
6.2.3.3 环境变量
通过环境变量来使用 secret 同 configMap 不支持同步更新。
6.3 Download API
Download API 时 k8s 中的一个功能,它允许容器在运行时从 k8s API 服务器中获取有关它们自身的信息。这些信息可以作为容器内部的环境变量或文件注入到容器中,以便容器可以获取有关其运行环境的各种信息。
6.3.1 使用 Download API
6.3.1.1 通过 env 获取
6.3.1.2 通过 volume 获取
volume 挂载的 download API 数据也支持热更新!
6.3.1.3 获取别的容器信息
为 Pod 配置 ServiceAccount 权限
在 curl Pod 中调用 Kubernetes API
6.3.2 查询 k8s 的 API 文档
主要用于二次开发 k8s 工具时查阅。
7. Storage
卷为容器提供了一种共享宿主机文件系统的一种方式。按生命周期分类有:临时卷、持久卷。 - 临时卷类型的生命周期与 Pod 相同,当 Pod 不再存在时,Kubernetes 也会销毁临时卷 - 持久卷可以比 Pod 的存活期长,当 Pod 不再存在时,Kubernetes 不会销毁持久卷
7.1 Volume
在容器中,容器一旦关闭,那么容器内部的文件都将丢失且无法找回。若要部署一个数据库,容器每次重启一次数据就丢失了,显然是不可用,容器与宿主机之间要能共享文件,使容器内的有效数据不会丢失,而 volume 就是来解决这个文件的。
volume 卷都在宿主机的 /var/lib/kubelet/pods/{uid}/volumes 目录下。
7.1.1. emptyDir
emptyDir 是 Kubernetes 中的一种临时存储卷,生命周期与 Pod 绑定。当 Pod 被删除时,emptyDir 中的数据也会被清除。它通常用于 容器间共享临时数据 或 缓存。 容器崩溃不会从节点中移除 pod,因此 empty 卷中的数据在容器崩溃时是安全的。 emptyDir 的用法: - 暂存空间,例如用于基于磁盘的合并排序、用于长时间计算崩溃恢复时的检查点 - web 服务器容器提供数据时,保存内容管理器容器提取的文件
示例
7.1.2 hostPath
hostPath 卷将主机节点的文件系统中的文件或目录挂载到集群中。hostPath 卷的 type 字段用于强制校验宿主机路径的类型,确保 Pod 按预期访问宿主机资源。
hostPath 可以挂载符号链接,但是最终所指向的必须是目录,且 type 是 Directory
| 类型 | 作用 | 示例路径 | 注意事项 |
|---|---|---|---|
| (空字符串) | 跳过类型检查(兼容旧版本) | 可以挂载任意路径 | |
| Directory | 挂载已存在的目录 | /var/log | 目录不存在则 Pod 启动失败 |
| DirectoryOrCreate | 目录不存在时自动创建(权限 755,属主为 kubelet 用户),不会创建文件的父目录 | /tmp/my-dir | 适合日志目录等可动态创建的场景 |
| File | 挂载已存在的文件 | /etc/hosts | 文件不存在则 Pod 启动失败 |
| FileOrCreate | 文件不存在时自动创建空文件(权限 644,属主为 kubelet 用户) | /tmp/config.yaml | 需确保父目录已存在 |
| Socket | 挂载Unix Socket 文件 | /var/run/docker.sock | 非 Socket 文件则启动失败 |
| CharDevice | 挂载字符设备(如终端、随机数生成器) | /dev/random | 非字符设备则启动失败 |
| BlockDevice | 挂载块设备(如磁盘分区) | /dev/sda1 | 非块设备则启动失败 |
示例
7.2 pv\pvc
pv(PersistentVolume)持久卷,是抽象出来的存储资源对象,和普通 volume 一样,也是使用卷插件来实现的,不同的是它们拥有独立于任何使用 pv 的 Pod 的生命周期。
PVC(PersistentVolumeClaim)持久卷申领,表达的是用户对存储的请求。
7.2.1 pv 示例
7.2.1 pvc 示例
7.2.2 pv 访问模式 accessModes
PersistentVolume.spec.accessModes 读写策略,用于描述用户应用对存储资源的访问权限,访问权限包括下面几种方式:
- ReadWriteOnce - RWO:单节点读写,同一节点上运行的多个 Pod 可以访问(读取或写入)该卷。
- ReadOnlyMany - ROX:多节点只读,无法保证该卷是只读!Pod 仍可能通过其他方式写入数据(需依赖存储插件实现真正的只读)。如果需要强制只读,需在 Pod 的 volumeMounts 中显式设置 readOnly: true
- ReadWriteMany - RWX:多节点读写
- ReadWriteOncePod - RWOP:卷只能被单个 Pod 以读写方式挂载。仅适用于 CSI 卷和 Kubernetes v1.22+。
每个卷同一时刻只能以一种访问模式挂载,即使该卷能够支持多种访问模式。 pv 与 pvc 的读写策略必须
| 卷插件 | ReadWriteOnce | ReadOnlyMany | ReadWriteMany | ReadWriteOncePod |
|---|---|---|---|---|
| AzureFile | ✓ | ✓ | ✓ | - |
| CephFS | ✓ | ✓ | ✓ | - |
| CSI | 取决于驱动 | 取决于驱动 | 取决于驱动 | 取决于驱动 |
| FC | ✓ | ✓ | - | - |
| FlexVolume | ✓ | ✓ | 取决于驱动 | - |
| GCEPersistentDisk | ✓ | ✓ | - | - |
| Glusterfs | ✓ | ✓ | ✓ | - |
| HostPath | ✓ | - | - | - |
| iSCSI | ✓ | ✓ | - | - |
| NFS | ✓ | ✓ | ✓ | - |
| RBD | ✓ | ✓ | - | - |
| VsphereVolume | ✓ | - | - Pod 运行于同一节点上时可行 | - |
| PortworxVolume | ✓ | - | ✓ | - |
7.2.3 pv 回收策略 persistentVolumeReclaimPolicy
PersistentVolume.spec.persistentVolumeReclaimPolicy 回收策略 persistentVolumeReclaimPolicy,当 pv 不再被使用了之后,对其的处理方式。目前支持三种策略:
- Retain (保留) 保留数据,pod被删除后,Retain 模式的pv 和其绑定的 pvc 不会被删除,需要管理员手工清理数据
- Recycle(回收) 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*
- Delete (删除)与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务
当前,只有 NFS 和 HostPath 支持回收策略 。AWS EBS , GCE PD , Azure Disk 和 Cinder 卷支持删除策略。
7.2.4 pv 状态 status
一个 PV 的生命周期中,可能会处于4中不同的阶段:
- Available(可用):表示可用状态,还未被任何 PVC 绑定
- Bound(已绑定):表示 PV 已经被 PVC 绑定
- Released(已释放):表示 PVC 被删除,但是资源还未被集群重新声明
- Failed(失败):表示该 PV 的自动回收失败
7.2.5 制备
PV 卷的制备有两种方式:静态制备或动态制备。
7.2.5.1 静态制备
集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息, 并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。
7.2.5.2 动态制备
创建一个 pvc,在动态制备下,pvc可能已经创建完毕,control- plane 的控制回路监测新的 pvc 对象,为之寻找相匹配(容量大于 pvc)的 pv 卷,如果此时动态制备了一个新 PV(通过 StorageClass),则该 PV 会直接绑定到当前 PVC(一对一专属绑定),如果找不到匹配的 PV 卷,PVC 申领会无限期地处于未绑定状态。 如果动态制备了一个新 PV(通过 StorageClass),则 该 PV 会直接绑定到当前 PVC(一对一专属绑定)。
pvc 与 pv 绑定涉及到预选和优选算法。预选:需要满足如存储类型一致、访问策略一致、存储大小相符的条件,pvc 和 pv 才有可能绑定,若此时 pvc 还有多个 pv 所选择,这时就需要通过优选法来选择最符合的那一个,pvc 会优先选择回收策略 Retain 的 pv,其次是 Recycle,最后是 Delete 的。
7.2.6 实际部署使用 smb 协议共享的空间
7.2.6.1 安装 smb 驱动
-
系统上安装 cifs 驱动
-
用 helm 安装 csi-smb 驱动 在 master 节点安装就行
-
kubectl 安装 csi-smb 驱动
7.2.6.2 在 pod 和 statefulSet 编写 yaml 文件
use-smb.yaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | |
应用这些配置
7.2.8 StorageClass
上面的 pv 、pvc 都需要自己根据 pod 或控制器的需求来制备,非常不方便。StorageClass 类似于一块空的硬盘,StatefulSet 能在这块“硬盘”上根据 pvc 的需求自动创建 pv。
7.2.8.1 清单示例
7.2.8.2 使用
在 StorageClass 中使用 nfs 类型部署 StatefulSet 应用。
nfs-storageclass.yaml
8. scheduler 调度器
Kubernetes Scheduler 是集群的核心组件,scheduler 的主要任务是把定义的 pod 分配到集群的节点上运行。schedule 是作为单独的程序运行的,启动之后会一致监听 API Server,获取 PodSpec.NodeName 为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上。
scheduler 分配 pod 过程:scheduler 会筛选出所有符合 pod 要求的 node(如资源充足;是否和指定的nodeName匹配;host模式下node 端口和 pod 端口是否冲突;标签匹配),对符合条件的 node 进行优先级排序(如资源利用率越低的 Node 得分越高;cpu 和 内存使用率越接近,得分越高;节点若有要使用的镜像,镜像总大小越大,得分越高),这些一系列的操作计算出来的分数来决定 node 节点的排名,并将 pod 绑定到得分最高的 node 上。
8.1 自定义 scheduler
编写 deploy 清单文件 my-scheduler.yaml
deploy 的 pod 状态是 ==pending== ,无法正常运行,因为设置了不存在的自定义调度器,它不会使用默认的调度器 ==default-scheduler== 进行调度。
启动 API Server 代理:kubectl proxy --port=8001 &
安装 jq 命令 sudo apt-get install jq
编写脚本,把 pod 随机调度到可用的节点 binding.sh
9. Security
Kubernetes 作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对 Kubernetes 的各种客户端进行认证和鉴权操作。
在 Kubernetes 集群中,客户端通常有两类: - User Account:一般是独立于 kubernetes 之外的其他服务管理的用户账号。 - Service Account:kubernetes 管理的账号,用于为 Pod 中的服务进程在访问 Kubernetes 时提供身份标识。
认证、授权与准入控制
ApiServer 是访问及管理资源对象的唯一入口。任何一个请求访问 ApiServer,都要经过下面三个流程: - Authentication(认证):身份鉴别,只有正确的账号才能够通过认证 - Authorization(授权): 判断用户是否有权限对访问的资源执行特定的动作 - Admission Control(准入控制):用于补充授权机制以实现更加精细的访问控制功能。
9.1 Authentication 认证模式
Kubernetes 集群安全的最关键点在于如何识别并认证客户端身份,它提供了 3 种客户端身份认证方式,安全级别从上往下越来越高 : - HTTP Token 认证:通过一个Token来识别合法用户。用一个很长的难以被模仿的字符串 Token 来表明客户身份的一种方式。每个 Token 对应一个用户名,当客户端发起 API 调用请求时,需要在 HTTP Header 里放入 Token,API Server 接到 Token 后会跟服务器中保存的 token 进行比对,然后进行用户身份认证的过程。 - HTTP Base 认证:通过用户名+密码的方式认证。这种认证方式是把“用户名:密码”用 BASE64 算法进行编码后的字符串放在 HTTP 请求中的 Header Authorization 域里发送给服务端。服务端收到后进行解码,获取用户名及密码,然后进行用户身份认证的过程。 - HTTPS 证书认证:基于 CA 根证书签名的双向数字证书认证方式。安全性最高的一种方式,但是同时也是操作起来最麻烦的一种方式。
Controller Manager,Scheduler 与 APIServer 在同一台机器,所以直接使用 APIServer 的非安全端口访问 --insecure-bind-address=127.0.0.1
kubectl,kubelet,kube-proxy 因为可以不和 APIServer 安装在同台机器,所以都需要证书进行 HTTPS 双向认证。
HTTPS 认证大体分为3个过程: 1. 证书申请和下发。HTTPS 通信双方的服务器向CA机构申请证书,CA 机构下发根证书、服务端证书及私钥给申请者 2. 客户端和服务端的双向认证。 - 客户端向服务器端发起请求,服务端下发自己的证书给客户端, - 客户端接收到证书后,通过私钥解密证书,在证书中获得服务端的公钥, - 客户端利用服务器端的公钥认证证书中的信息,如果一致,则认可这个服务器 - 客户端发送自己的证书给服务器端,服务端接收到证书后,通过私钥解密证书,在证书中获得客户端的公钥,并用该公钥认证证书信息,确认客户端是否合法 3. 服务器端和客户端进行通信。服务器端和客户端协商好加密方案后,客户端会产生一个随机的秘钥并加密,然后发送到服务器端。服务器端接收这个秘钥后,双方接下来通信的所有内容都通过该随机秘钥加密

注意: Kubernetes允许同时配置多种认证方式,只要其中任意一个方式认证通过即可
ServiceAccount
在每个 namespace 都会有一个 Service Account,如果 pod 在创建时没有指定 ServiceAccount,就会使用 pod 所属的 namespace 的 ServiceAccount。默认挂载目录 /run/secrets/kubenetes.io/serviceaccount/。
证书签发模式
- 手动签发。通过 k8s 集群的跟 CA 进行签发 HTTPS 证书。
- 自动签发。kubelet 首次访问 APIServer 时,使用 token 做认证,通过后,Controller Manager 会为 kubelet 生成一个证书,以后的访问都是证书做认证了。
9.2 Authorization 鉴权
授权发生在认证成功之后,通过认证就可以知道请求用户是谁, 然后 Kubernetes 会根据事先定义的授权策略来决定用户是否有权限访问,这个过程就称为授权。
每个发送到 ApiServer 的请求都带上了用户和资源的信息:比如发送请求的用户、请求的路径、请求的动作等,授权就是根据这些信息和授权策略进行比较,如果符合策略,则认为授权通过,否则会返回错误。
API Server目前支持以下几种授权策略: - AlwaysDeny:表示拒绝所有请求,一般用于测试 - AlwaysAllow:允许接收所有请求,相当于集群不需要授权流程(Kubernetes 默认的策略) - ABAC(Attribute-Based Access Control):基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制 - Webhook:通过调用外部REST服务对用户进行授权 - Node:是一种专用模式,用于对kubelet发出的请求进行访问控制 - RBAC:基于角色的访问控制(kubeadm安装方式下的默认选项)
9.2.1 RBAC(Role-Based Access Control) 基于角色的访问控制
RBAC 在 k8s 1.5 版本引入,RBAC 把用户与角色绑定在一起,用于控制用户的权限。主要有以下几种优势: - 对集群中的资源和非资源均拥有完整的覆盖 - 整个 RBAC 完全由几个 API 对象完成,同其他 API 对象一样,可以用 kubectl 或 API 进行操作 - 可以在运行时进行调整,无需重启 APIServer。
RBAC 引入了4个顶级资源对象,如下所示。角色与角色绑定的关系类似于 pv 与 pvc 的关系,角色表示本身具有的权限,角色绑定像一条纽带,绑定用户,让用户拥有该角色的权限。绑定的对象不止用户,可以是 Group,User,Service Account。 - 角色:Role、ClusterRole;用于指定一组权限,Role - 角色绑定:RoleBinding、ClusterRoleBinding;用于将角色(权限)赋予给对象
9.2.1.1 Role
Role 是 命名空间(Namespace)级别 的资源,其权限仅作用于单个命名空间内的资源。必须与 RoleBinding 配合使用,且 RoleBinding 必须位于同一命名空间。
示例
apiGroups Role.rules.apiGroups 支持的 API 组列表有
resources Role.rules.resources 支持的资源对象列表有
verbs Role.rules.verbs 对资源对象的操作方法列表
9.2.1.2 ClusterRole
ClusterRole 是集群级别的资源,权限可覆盖所有命名空间中的资源(如 Pods)、集群范围资源(node)、非资源端点(/healthz)、跨命名空间资源(如需要跨 Namespace 的权限聚合)。
ClusterRole 可以和 ClusterRoleBinding 或者 RoleBinding 绑定 - 绑定 RoleBinding:权限仅作用于单个命名空间(复用 ClusterRole 的权限定义) - 绑定 ClusterRoleBinding:权限作用于整个集群
示例
9.2.1.3 创建一个用户只能管理 dev 命令空间
创建一个 linux 用户 songyun,只给予它对 pod 资源的 get、watch、delete 权限。
创建 linux 用户 songyun
创建用户凭证
编写 role 和 rolebinding cluster-role-demo.yaml
使用 dev-user 上下文测试权限

9.2.2 官方预定义角色
在 Kubernetes 的 RBAC(基于角色的访问控制) 模型中,官方提供了一系列预定义角色(Predefined Roles),这些角色可以直接使用,无需手动定义,可以使用命令 kubectl get clusterrole 查看。
| 名称 | 权限范围 | 一般用途 |
|---|---|---|
| view | 允许读取除 Role、RoleBinding、Secret 对象外的命名空间内的所有资源 | 第三方人员 |
| edit | 允许读取和修改 Secret,但是不允许查看和修改 RoleBinding,防止权限扩散 | 开发人员 |
| admin | 一个命名空间的资源完全控制权由 admin 赋予,该角色可以修改和访问除 ResourceQuota 和命名空间资源本身外的任何资源 | 运维人员 |
| cluster-admin | 超级管理员(完全控制集群) | 集群管理员 |
9.3 Admission Control 准入控制
通过了前面的认证和授权之后,还需要经过准入控制处理通过之后,apiserver才会处理这个请求。鉴权只是大范围的判断该用户是否满足权限,准入控制则是更加细粒度得判断该操作的合法性。例如:linux 系统中的 rm 命令,普通用户执行 rm 命令,只能删除掉所属该用户的权限,在命令前面加 sudo 获得了管理员权限,则可以删除任意文件,这一步有点类似 鉴权 操作。像执行 sudo rm -rf / 命令,该命令可以不可恢复式破坏 linux 系统,而 k8s 为了防止用户对集群做这种类似的破坏性操作,引入了 Admission Control 准入控制,添加服务,可以防止一些有权限但不合理的操作。
当前可配置的 Admission Control 准入控制如下:
- AlwaysAdmit:允许所有请求
- AlwaysDeny:禁止所有请求,一般用于测试
- AlwaysPullImages:在启动容器之前总去下载镜像
- DenyExecOnPrivileged:它会拦截所有想在 Privileged Container 上执行命令的请求
- ImagePolicyWebhook:这个插件将允许后端的一个Webhook程序来完成 admission controller 的功能。
- Service Account:实现 ServiceAccount 实现了自动化。官方推荐使用
- SecurityContextDeny:这个插件将使用 SecurityContext 的 Pod 中的定义全部失效
- ResourceQuota:用于资源配额管理目的,观察所有请求,确保在 namespace 上的配额不会超标。官方推荐使用
- LimitRanger:用于资源限制管理,作用于 namespace 上,确保对 Pod 进行资源限制。官方推荐使用
- InitialResources:为未设置资源请求与限制的 Pod,根据其镜像的历史资源的使用情况进行设置
- NamespaceLifecycle:如果尝试在一个不存在的 namespace 中创建资源对象,则该创建请求将被拒绝。当删除一个 namespace 时,系统将会删除该 namespace 中所有对象。官方推荐使用
- DefaultStorageClass:为了实现共享存储的动态供应,为未指定 StorageClass 或 PV 的 PVC 尝试匹配默认的 StorageClass,尽可能减少用户在申请 PVC 时所需了解的后端存储细节
- DefaultTolerationSeconds:这个插件为那些没有设置 forgiveness tolerations 并具有 notready:NoExecute 和 unreachable:NoExecute 两种 taints 的 Pod 设置默认的“容忍”时间,为 5min
- PodSecurityPolicy:这个插件用于在创建或修改 Pod 时决定是否根据 Pod的security context 和可用的 PodSecurityPolicy 对 Pod 的安全策略进行控制
10. 搭建高可用 k8s 集群
准备 3 台master,最少 1 台 node,搭建集群,k8s 组件分布如下图所示
etcd 因选举规则的原因,所以安装了 etcd 的 master 节点必须得是奇数台。
10.1 搭建高可用集群
部署集群前需要把各主机配置好基础设置。k8s 对主机的要求有: 关防火墙、关闭 SELinux、关 swap、安装 ipvs
各主机信息
| 主机名 | IP | 角色 |
|---|---|---|
| arch | 192.168.1.172 | master |
| master | 192.168.1.180 | master |
| node1 | 192.168.1.181 | node |
| node2 | 192.168.1.182 | master |
安装脚本和部署脚本在目录中
kubernetes 底层原理
CRI (container runtime interface) 容器运行时
容器运行时 CRI (container runtime interface) 是 k8s 从 1.5 版本开始,在遵循了 OCI 基础上,将容器操作抽象为一个个的接口。各种容器引擎(如 Docker 、 Rocket )通过实现这个接口来接入 k8s。在 k8s 中,通过 Kubelet 发送接口请求来实现容器启动和管理。
2017年,由 Google、RedHat、Intel、SUSE、IBM 联合发起的 CRI-O(Container Runtime Interface Orchestrator)项目发布了首个正式版本。 CRI-O 非常纯粹,目标就是兼容 CRI 和 OCI,使得 k8s 不依赖于传统的容器引擎(比如 Docker),也能够实现管理容器各项工作。
Docker 在早期单独把 Containerd 开源了,还没有捐给 CNCF ,这时的 k8s 调用容器有以下两种方式: - kubelet 发请求,dockershim 调用 Docker ,Docker 调用 containerd 操作容器 - kubelet 发请求,cri-containerd 调用 Containerd 操作容器

显而易见,dockershim 层层调用,调用链过于臃肿。与此同时,kubelet 的代码和 dockershim 的代码都是放在一个仓库内的,这意味着 dockershim 得由 k8s 进行组织开发和维护,但 Docker 版本的更新是 k8s 无法控制和管理的,所以 Docker 每次发布新的版本,k8s 都要集中精力去快速地更新维护 dockershim。因此 k8s 在 v1.24 版本正式弃用了 dockershim 。2018年,Docker 将 Containerd 捐献给 CNCF ,CNCF 在接手之后发布了 1.1 版本。该版本完美支持了 CRI 标准,也就是说,kubectl 将不再需要 cri-containerd 就可以直接和 containerd 交互!

根据 Kubernetes 官方给出的 Containerd 1.1 对比 Docker 18.03 性能测试数据,Pod 的启动延迟降低了大约 20%;CPU 使用率降低了 68%;内存使用率降低了 12%,这是一个相当大的性能改善。
etcd
k8s 的存储层使用的是 etcd。etcd 是 CoreOS 开源的一个高可用强一致性的分布式存储服务,k8s 使用 etcd 作为数据存储后端,把需要记录的 pod、rc、svc 等资源信息存储在 etcd 中。 etcd 使用 RAFT 共识算法将一组主机组成集群,RAFT 集群中的每个节点都可以根据集群运行的情况在三种状态间切换:follower,candidate、leader。leader 和 follower 之间保持心跳,如果 follwer 在一段时间内没有收到来自 leader 的心跳,就会转为 candidate,发送新的选主请求。