Kubernetes(K8S)学习笔记
K8S介绍
什么是K8S?
它是一个为 容器化 应用提供集群部署和管理的开源工具,由 Google 开发。Kubernetes 这个名字源于希腊语,意为“舵手”或“飞行员”。k8s 这个缩写是因为 k 和 s 之间有八个字符的关系。 Google 在 2014 年开源了 Kubernetes 项目。
主要特性:
- 高可用,不宕机,自动灾难恢复
- 灰度更新,不影响业务正常运转
- 一键回滚到历史版本
- 方便的伸缩扩展(应用伸缩,机器加减)、提供负载均衡
- 有一个完善的生态
不同的应用部署方案
- 传统部署方式:应用直接在物理机上部署,机器资源分配不好控制,出现Bug时,可能机器的大部分资源被某个应用占用,导致其他应用无法正常运行,无法做到应用隔离。
- 虚拟机部署:在单个物理机上运行多个虚拟机,每个虚拟机都是完整独立的系统,性能损耗大。
- 容器部署:所有容器共享主机的系统,轻量级的虚拟机,性能损耗小,资源隔离,CPU和内存可按需分配。
什么时候需要 Kubernetes?
- 当你的应用只是跑在一台机器,直接一个 docker + docker-compose 就够了,方便轻松;
- 当你的应用需要跑在 3,4 台机器上,你依旧可以每台机器单独配置运行环境 + 负载均衡器;
- 当你应用访问数不断增加,机器逐渐增加到十几台、上百台、上千台时,每次加机器、软件更新、版本回滚,都会变得非常麻烦、痛不欲生,再也不能好好的摸鱼了,人生浪费在那些没技术含量的重复性工作上。
这时候,Kubernetes 就可以一展身手了,让你轻松管理百万千万台机器的集群。Kubernetes 可以为你提供集中式的管理集群机器和应用,加机器、版本升级、版本回滚,那都是一个命令就搞定的事,不停机的灰度更新,确保高可用、高性能、高扩展。
Kubernetes 集群架构
master
主节点,控制平台(控制平面),不需要很高性能,不跑任务,通常一个就行了,也可以开多个主节点来提高集群可用度。
worker
工作节点,可以是虚拟机或物理计算机,任务都在这里跑,机器性能需要好点;通常都有很多个,可以不断加机器扩大集群;每个工作节点由主节点管理。
Pod
豆荚,K8S 调度、管理的最小单位,一个 Pod 可以包含一个或多个容器,每个 Pod 有自己的虚拟IP。一个工作节点可以有多个 pod,主节点会考量负载自动调度 pod 到哪个节点运行。
Kubernetes 组件
- kube-apiserver :API 服务器,公开了 Kubernetes API
- etcd :键值数据库,可以作为保存 Kubernetes 所有集群数据的后台数据库
- kube-scheduler: 调度 Pod 到哪个节点运行
- kube-controller: 集群控制器
- cloud-controller :与云服务商交互
安装 Kubernetes 集群
安装方式介绍
- minikube
- 只是一个 K8S 集群模拟器,只有一个节点的集群,只为测试用,master 和 worker 都在一起
- 直接用云平台 Kubernetes
- 可视化搭建,只需简单几步就可以创建好一个集群。
- 优点:安装简单,生态齐全,负载均衡器、存储等都给你配套好,简单操作就搞定
- 裸机安装(Bare Metal)
- 至少需要两台机器(主节点、工作节点个一台),需要自己安装 Kubernetes 组件,配置会稍微麻烦点。
- 可以到各云厂商按时租用服务器,费用低,用完就销毁。
- 缺点:配置麻烦,缺少生态支持,例如负载均衡器、云存储。
- K3S(推荐)
minikube
安装简单,支持各种平台,安装方法:https://minikube.sigs.k8s.io/docs/start/
1 | 启动集群 |
云平台搭建
- 腾讯云 TKE(控制台搜索容器)
- 登录阿里云控制台 - 产品搜索 Kubernetes
裸机搭建(Bare Metal)
主节点需要组件
- docker(也可以是其他容器运行时)
- kubectl 集群命令行交互工具
- kubeadm 集群初始化工具
工作节点需要组件
- docker(也可以是其他容器运行)
- kubelet 管理 Pod 和容器,确保他们健康稳定运行。
- kube-proxy 网络代理,负责网络相关的工作
- 相关文档:https://kubernetes.io/zh/docs/concepts/overview/components/#node-components
开始安装
手动搭建:
1
2
3
4每个节点分别设置对应主机名
hostnamectl set-hostname master
hostnamectl set-hostname node1
hostnamectl set-hostname node21
2
3
4
5所有节点都修改 hosts
vim /etc/hosts
172.16.32.2 node1
172.16.32.6 node2
172.16.0.4 master1
2
3所有节点关闭 SELinux
setenforce 0
sed -i --follow-symlinks 's/SELINUX=enforcing/SELINUX=disabled/g' /etc/sysconfig/selinux所有节点确保防火墙关闭
systemctl stop firewalld
systemctl disable firewalld
添加安装源(所有节点):
1 | 添加 k8s 安装源 |
安装所需组件(所有节点):
1 | yum install -y kubelet-1.22.4 kubectl-1.22.4 kubeadm-1.22.4 docker-ce |
启动 kubelet、docker,并设置开机启动(所有节点):
1 | systemctl enable kubelet |
修改 docker 配置(所有节点):
1 | # kubernetes 官方推荐 docker 等使用 systemd 作为 cgroupdriver,否则 kubelet 启动不了 |
用 kubeadm 初始化集群(仅在主节点跑):
1 | 初始化集群控制台 Control plane |
把工作节点加入集群(只在工作节点跑):
1 | kubeadm join 172.16.32.10:6443 --token xxx --discovery-token-ca-cert-hash xxx |
安装网络插件,否则 node 是 NotReady 状态(主节点跑):
1 | 很有可能国内网络访问不到这个资源,你可以网上找找国内的源安装 flannel |
查看节点,要在主节点查看(其他节点有安装 kubectl 也可以查看):
K3s快速搭建集群
提示:
1.本文档基于kubernetes V1.25版本。
2.从V1.24开始,kubernetes默认容器运行时使用containerd,不再使用docker。
为什么使用K3s
K3s 是一个轻量级的、完全兼容的 Kubernetes 发行版本。非常适合初学者。
K3s将所有 Kubernetes 控制平面组件都封装在单个二进制文件和进程中,文件大小<100M,占用资源更小,且包含了kubernetes运行所需要的部分外部依赖和本地存储提供程序。
K3s提供了离线安装包,安装起来非常方便,可以避免安装过程中遇到各种网络资源访问问题。
K3s特别适用于边缘计算、物联网、嵌入式和ARM移动端场景。
K3s完全兼容kubernetes,二者的操作是一样的,使用k3s完全满足我们学习kubernetes的要求
离线安装K3s集群
K3s集群分为k3s Server(控制平面)和k3s Agent(工作节点)。所有的组件都打包在单个二进制文件中。
运行环境
- 最低 内存: 512MB / CPU: 1 核心
- K3s版本:v1.25.0+k3s1
- 集群规划(不同机器的ip和主机名不能一样,都要进行配置)
准备工作
需要在每台机器上执行如下命令:
- 关闭防火墙
- 设置selinux(需要联网)
1 | systemctl disable firewalld --now |
1 | yum install -y container-selinux selinux-policy-base |
下载安装包
- 下载安装脚本install.sh:https://get.k3s.io/
- 下载k3s二进制文件:k3s
- 下载必要的image:https://github.com/k3s-io/k3s/releases/download/v1.25.0%2Bk3s1/k3s-airgap-images-amd64.tar.gz
- 这些文件都可以在github仓库中获取:https://github.com/k3s-io/k3s
执行安装脚本
- 将k3s二进制文件移动到/usr/local/bin目录,并添加执行权限
1 | mv k3s /usr/local/bin |
- 将镜像移动到/var/lib/rancher/k3s/agent/images/目录(无需解压)
1 | mkdir -p /var/lib/rancher/k3s/agent/images/ |
- 在k8s-master节点执行:
1 | #修改权限 |
- 在k8s-worker1和k8s-worker2节点执行
1 | INSTALL_K3S_SKIP_DOWNLOAD=true \ |
排查错误
如果安装或启动不成功,可能有以下几个原因:
1 | 1. 时间不统一 |
K8S基础
Pod
Pod 是包含一个或多个容器的容器组,是 Kubernetes 中创建和管理的最小对象。Pod 有以下特点:
- Pod是kubernetes中最小的调度单位(原子单元),Kubernetes直接管理Pod而不是容器。
- 同一个Pod中的容器总是会被自动安排到集群中的同一节点(物理机或虚拟机)上,并且一起调度。
- Pod可以理解为运行特定应用的“逻辑主机”,这些容器共享存储、网络和配置声明(如资源限制)。
- 每个 Pod 有唯一的 IP 地址。 IP地址分配给Pod,在同一个 Pod 内,所有容器共享一个 IP 地址和端口空间,Pod 内的容器可以使用localhost互相通信。
例如,你可能有一个容器,为共享卷中的文件提供 Web 服务器支持,以及一个单独的 “边车 (sidercar)” 容器负责从远端更新这些文件,如下图所示:
创建和管理Pod
1 | kubectl run mynginx --image=nginx |
镜像加速
由于kubernetes从V1.24版本开始默认使用containerd,需要修改containerd的配置文件,才能让Pod的镜像使用镜像加速器。配置文件路径一般为/etc/containerd/config.toml,详见阿里云镜像加速。
1、在K3s中配置镜像仓库
K3s 会自动生成containerd的配置文件/var/lib/rancher/k3s/agent/etc/containerd/config.toml,不要直接修改这个文件,k3s重启后修改会丢失。
为了简化配置,K3s 通过**/etc/rancher/k3s/registries.yaml**文件来配置镜像仓库,K3s会在启动时检查这个文件是否存在。
我们需要在每个节点上新建/etc/rancher/k3s/registries.yaml文件,配置内容如下:
1 | mirrors: |
重启每个节点:
1 | systemctl restart k3s |
查看配置是否生效:
1 | cat /var/lib/rancher/k3s/agent/etc/containerd/config.toml |
容器与镜像
1、容器运行时接口(CRI)
Kubelet运行在每个节点(Node)上,用于管理和维护Pod和容器的状态。
容器运行时接口(CRI)是kubelet 和容器运行时之间通信的主要协议。它将 Kubelet 与容器运行时解耦,理论上,实现了CRI接口的容器引擎,都可以作为kubernetes的容器运行时。
Docker没有实现(CRI)接口,Kubernetes使用dockershim来兼容docker。
自V1.24版本起,Dockershim 已从 Kubernetes 项目中移除。
crictl是一个兼容CRI的容器运行时命令,他的用法跟docker命令一样,可以用来检查和调试底层的运行时容器。
1 | crictl pull mysql:5.7-debian |
在一些局域网环境下,我们没法通过互联网拉取镜像,可以手动的导出、导入镜像。
crictl命令没有导出、导入镜像的功能。
需要使用ctr命令导出、导入镜像,它是containerd的命令行接口。
2、导入导出
- 从docker导出镜像再导入到containerd中
1 | docker pull alpine:3.16 |
- 从containerd导出、导入镜像
1 | #导出镜像 |
Deployment(部署)与ReplicaSet(副本集)
Deployment是对ReplicaSet和Pod更高级的抽象。它使Pod拥有多副本,自愈,扩缩容、滚动升级等能力。
ReplicaSet(副本集)是一个Pod的集合。它可以设置运行Pod的数量,确保任何时间都有指定数量的 Pod 副本在运行。通常我们不直接使用ReplicaSet,而是在Deployment中声明。
1 | #创建deployment,部署3个运行nginx的Pod |
缩放
- 手动缩放
1 | #将副本数量调整为5 |
- 自动缩放
- 自动缩放通过增加和减少副本的数量,以保持所有 Pod 的平均 CPU 利用率不超过 75%。
- 自动伸缩需要声明Pod的资源限制,同时使用 Metrics Server 服务(K3s默认已安装)。
1 | #自动缩放 |
滚动更新
1 | #查看版本和Pod |
版本回滚
1 | #查看历史版本 |
Service(服务)
Service将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法。
Service为一组 Pod 提供相同的 DNS 名,并且在它们之间进行负载均衡。
Kubernetes 为 Pod 提供分配了IP 地址,但IP地址可能会发生变化。
集群内的容器可以通过service名称访问服务,而不需要担心Pod的IP发生变化。
Kubernetes Service 定义了这样一种抽象:
逻辑上的一组可以互相替换的 Pod,通常称为微服务。
Service 对应的 Pod 集合通常是通过选择算符来确定的。
举个例子,在一个Service中运行了3个nginx的副本。这些副本是可互换的,我们不需要关心它们调用了哪个nginx,也不需要关注 Pod的运行状态,只需要调用这个服务就可以了。
创建Service对象
ServiceType 取值
● ClusterIP:将服务公开在集群内部。kubernetes会给服务分配一个集群内部的 IP,集群内的所有主机都可以通过这个Cluster-IP访问服务。集群内部的Pod可以通过service名称访问服务。
● NodePort:通过每个节点的主机IP 和静态端口(NodePort)暴露服务。 集群的外部主机可以使用节点IP和NodePort访问服务。
● ExternalName:将集群外部的网络引入集群内部。
● LoadBalancer:使用云提供商的负载均衡器向外部暴露服务。
1 | # port是service访问端口,target-port是Pod端口 |
1 | # 随机产生主机端口 |
访问Service
外部主机访问:192.168.56.109:32296。
1.NodePort端口是随机的,范围为:30000-32767。
2.集群中每一个主机节点的NodePort端口都可以访问。
3.如果需要指定端口,不想随机产生,需要使用配置文件来声明。
1 | #集群内访问 |
Namespace(命名空间)
命名空间(Namespace)是一种资源隔离机制,将同一集群中的资源划分为相互隔离的组。命名空间可以在多个用户之间划分集群资源(通过资源配额)。
- 例如我们可以设置开发、测试、生产等多个命名空间。
同一命名空间内的资源名称要唯一,但跨命名空间时没有这个要求。
命名空间作用域仅针对带有名字空间的对象,例如 Deployment、Service 等。
这种作用域对集群访问的对象不适用,例如 StorageClass、Node、PersistentVolume 等。
Kubernetes 会创建四个初始命名空间:
● default 默认的命名空间,不可删除,未指定命名空间的对象都会被分配到default中。
● kube-system Kubernetes 系统对象(控制平面和Node组件)所使用的命名空间。
● kube-public 自动创建的公共命名空间,所有用户(包括未经过身份验证的用户)都可以读取它。通常我们约定,将整个集群中公用的可见和可读的资源放在这个空间中。
● kube-node-lease 租约(Lease)对象使用的命名空间。每个节点都有一个关联的 lease 对象,lease 是一种轻量级资源。lease对象通过发送心跳,检测集群中的每个节点是否发生故障。
使用kubectl get lease -A查看lease对象
使用多个命名空间
● 命名空间是在多个用户之间划分集群资源的一种方法(通过资源配额)。
○ 例如我们可以设置开发、测试、生产等多个命名空间。
● 不必使用多个命名空间来分隔轻微不同的资源。
○ 例如同一软件的不同版本: 应该使用标签 来区分同一命名空间中的不同资源。
● 命名空间适用于跨多个团队或项目的场景。
○ 对于只有几到几十个用户的集群,可以不用创建命名空间。
● 命名空间不能相互嵌套,每个 Kubernetes 资源只能在一个命名空间中。
管理命名空间
1 | #创建命名空间 |
切换当前命名空间
1 | #查看当前上下文 |
声明式对象配置
云原生的代表技术包括:
- 容器
- 服务网格
- 微服务
- 不可变基础设施
- 声明式API
管理对象
- 命令行指令:例如,使用kubectl命令来创建和管理 Kubernetes 对象。命令行就好比口头传达,简单、快速、高效。但它功能有限,不适合复杂场景,操作不容易追溯,多用于开发和调试。
- 声明式配置:kubernetes使用yaml文件来描述 Kubernetes 对象。声明式配置就好比申请表,学习难度大且配置麻烦。好处是操作留痕,适合操作复杂的对象,多用于生产。
- 常用命令缩写
常用命令缩写
名称 | 缩写 | Kind |
---|---|---|
namespaces | ns | Namespace |
nodes | no | Node |
pods | po | Pod |
services | svc | Service |
deployments | deploy | Deployment |
replicasets | rs | ReplicaSet |
statefulsets | sts | StatefulSet |
1 | kubectl create deploy my-deploy --image=nginx:1.22 --replicas=3 |
YAML规范
○ 缩进代表上下级关系
○ 缩进时不允许使用Tab键,只允许使用空格,通常缩进2个空格
○ : 键值对,后面必须有空格
○ -列表,后面必须有空格
○ [ ]数组
○ #注释
○ | 多行文本块
○ —表示文档的开始,多用于分割多个资源对象
1 | group: |
配置对象
在创建的 Kubernetes 对象所对应的 yaml文件中,需要配置的字段如下:
● apiVersion - Kubernetes API 的版本
● kind - 对象类别,例如Pod、Deployment、Service、ReplicaSet等
● metadata - 描述对象的元数据,包括一个 name 字符串、UID 和可选的 namespace
● spec - 对象的配置
使用yaml定义一个Pod:
1 | apiVersion: v1 |
使用yaml文件管理对象:
1 | #创建对象 |
标签
标签(Labels) 是附加到对象(比如 Pod)上的键值对,用于补充对象的描述信息。
标签使用户能够以松散的方式管理对象映射,而无需客户端存储这些映射。
由于一个集群中可能管理成千上万个容器,我们可以使用标签高效的进行选择和操作容器集合。
1 | apiVersion: v1 |
1 | kubectl get pod --show-labels |
选择器
标签选择器 可以识别一组对象。标签不支持唯一性。
标签选择器最常见的用法是为Service选择一组Pod作为后端。
1 | apiVersion: v1 |
目前支持两种类型的选择运算:基于等值的和基于集合的。
多个选择条件使用逗号分隔,相当于And(&&)运算。
- 等值选择
1 | selector: |
- 集合选择
1 | selector: |
金丝雀发布(灰度发布)
金丝雀部署(canary deployment)也被称为灰度发布。早期,工人下矿井之前会放入一只金丝雀检测井下是否存在有毒气体。采用金丝雀部署,你可以在生产环境的基础设施中小范围的部署新的应用代码。一旦应用签署发布,只有少数用户被路由到它,最大限度的降低影响。如果没有错误发生,则将新版本逐渐推广到整个基础设施。
学习笔记/image (1).png)
部署过程
学习笔记/image (2).png)
部署第一个版本
发布v1版本的应用,镜像使用nginx:1.22,数量为 3。
- 创建Namespace
- 创建Deployment
- 创建外部访问的Service
1 | apiVersion: v1 |
创建Canary Deployment
发布新版本的应用,镜像使用docker/getting-started,数量为 1。
1 | apiVersion: apps/v1 |
分配流量
查看服务kubectl describe svc canary-demo –namespace=dev
学习笔记/image (3).png)
- 调整比例:待稳定运行一段时间后,扩大试用范围,将部署的v2版本数量调整为3,v1和v2的数量都是3个。
1 | kubectl scale deployment/deploy-v2-canary --replicas=3 -n=dev |
- 下线旧版本:最后下线所有v1版本,所有服务升级为v2版本。
1 | kubectl scale deployment/deploy-v1 --replicas=0 -n=dev |
清空环境
使用namespace
可以方便的清空环境:
1 | kubectl delete all --all -n=dev |
局限性
按照 Kubernetes 默认支持的这种方式进行金丝雀发布,有一定的局限性:
- 不能根据用户注册时间、地区等请求中的内容属性进行流量分配
- 同一个用户如果多次调用该 Service,有可能第一次请求到了旧版本的 Pod,第二次请求到了新版本的 Pod
在 Kubernetes 中不能解决上述局限性的原因是:Kubernetes Service 只在 TCP 层面解决负载均衡的问题,并不对请求响应的消息内容做任何解析和识别。如果想要更完善地实现金丝雀发布,可以考虑Istio灰度发布。
运行有状态应用
以MySQL数据库为例,在kubernetes集群中运行一个有状态的应用。部署数据库几乎覆盖了kubernetes中常见的对象和概念:
● 配置文件–ConfigMap
● 保存密码–Secret
● 数据存储–持久卷(PV)和持久卷声明(PVC)
● 动态创建卷–存储类(StorageClass)
● 部署多个实例–StatefulSet
● 数据库访问–Headless Service
● 主从复制–初始化容器和sidecar
● 数据库调试–port-forward
● 部署Mysql集群–helm
创建MySQL数据库
配置环境变量
- 使用MySQL镜像创建Pod,需要使用环境变量设置MySQL的初始密码。
- 环境变量配置示例
挂载卷
- 将数据存储在容器中,一旦容器被删除,数据也会被删除。
- 将数据存储到卷(Volume)中,删除容器时,卷不会被删除。
hostPath卷:hostPath 卷将主机节点上的文件或目录挂载到 Pod 中。示例
- hostPath的type值:
DirectoryOrCreate 目录不存在则自动创建。 Directory 挂载已存在目录。不存在会报错。 FileOrCreate 文件不存在则自动创建。不会自动创建文件的父目录,必须确保文件路径已经存在。 File 挂载已存在的文件。不存在会报错。 Socket 挂载 UNIX 套接字。例如挂载/var/run/docker.sock进程
1 | apiVersion: v1 |
注意:hostPath 仅用于在单节点集群上进行开发和测试,不适用于多节点集群;
例如,当Pod被重新创建时,可能会被调度到与原先不同的节点上,导致新的Pod没有数据。
在多节点集群使用本地存储,可以使用
local
卷。
ConfigMap与Secret
在Docker中,我们一般通过绑定挂载的方式将配置文件挂载到容器里。
在Kubernetes集群中,容器可能被调度到任意节点,配置文件需要能在集群任意节点上访问、分发和更新。
ConfigMap
- ConfigMap 用来在键值对数据库(etcd)中保存非加密数据。一般用来保存配置文件。
- ConfigMap 可以用作环境变量、命令行参数或者存储卷。
- ConfigMap 将环境配置信息与 容器镜像 解耦,便于配置的修改。
- ConfigMap 在设计上不是用来保存大量数据的。
- 在 ConfigMap 中保存的数据不可超过 1 MiB。
- 超出此限制,需要考虑挂载存储卷或者访问文件存储服务。
- ConfigMap用法
1 | apiVersion: v1 |
1 | # 修改configMap,配置文件会被自动更新 |
Secret
Secret 用于保存机密数据的对象。一般由于保存密码、令牌或密钥等。
- data字段用来存储 base64 编码数据。
- stringData存储未编码的字符串。
- Secret 意味着你不需要在应用程序代码中包含机密数据,减少机密数据(如密码)泄露的风险。Secret 可以用作环境变量、命令行参数或者存储卷文件。
- Secret用法:
1 | echo -n '123456' | base64 |
1 | apiVersion: v1 |
卷(Volume)
将数据存储在容器中,一旦容器被删除,数据也会被删除。
卷是独立于容器之外的一块存储区域,通过挂载(Mount)的方式供Pod中的容器使用。
使用场景
- 卷可以在多个容器之间共享数据
- 卷可以将容器数据存储在外部存储或云存储上
- 卷更容易备份或迁移
常见的卷类型
- **临时卷(Ephemeral Volume)**:与 Pod 一起创建和删除,生命周期与 Pod 相同
- emptyDir - 作为缓存或存储日志
- configMap 、secret、 downwardAPI - 给Pod注入数据
- 持久卷(Persistent Volume):删除Pod后,持久卷不会被删除
- 本地存储 - hostPath、 local
- 网络存储 - NFS
- 分布式存储 - Ceph(cephfs文件存储、rbd块存储)
- 投射卷(Projected Volumes):projected 卷可以将多个卷映射到同一个目录上
- **临时卷(Ephemeral Volume)**:与 Pod 一起创建和删除,生命周期与 Pod 相同
后端存储
一个集群中可以包含多种存储(如local
、NFS
、Ceph
或云存储)。
每种存储都对应一个存储类(StorageClass) ,存储类用来创建和管理持久卷,是集群与存储服务之间的桥梁。
管理员创建持久卷(PV)时,通过设置不同的StorageClass来创建不同类型的持久卷。
临时卷(EV)
- 临时卷(Ephemeral Volume)
- 与 Pod 一起创建和删除,生命周期与 Pod 相同
- emptyDir - 初始内容为空的本地临时目录
- configMap - 为Pod注入配置文件
- secret - 为Pod注入加密数据
emptyDir
emptyDir会创建一个初始状态为空的目录,存储空间来自本地的 kubelet 根目录或内存(需要将emptyDir.medium
设置为"Memory"
)。
通常使用本地临时存储来设置缓存、保存日志等。例如,将redis的存储目录设置为emptyDir。
1 | apiVersion: v1 |
configMap卷和secret卷
注意:这里的configMap和secret代表的是卷的类型,不是configMap和secret对象。
删除Pod并不会删除ConfigMap对象和secret对象。
configMap卷和Secret卷是一种特殊类型的卷,kubelet引用configMap和Secret中定义的内容,在Pod所在节点上生成一个临时卷,将数据注入到Pod中。删除Pod,临时卷也会被删除。
- 临时卷位于Pod所在节点的
/var/lib/kubelet/pods
目录下。
学习笔记/image (1)-1735993788608-13.png)
持久卷(PV)与持久卷声明(PVC)
持久卷(Persistent Volume):删除Pod后,卷不会被删除
- 本地存储
- hostPath - 节点主机上的目录或文件(仅供单节点测试使用;多节点集群请用 local 卷代替)
- local - 节点上挂载的本地存储设备(不支持动态创建卷)
- 网络存储
- NFS - 网络文件系统 (NFS)
- 分布式存储
- Ceph(cephfs文件存储、rbd块存储)
持久卷(PV)和持久卷声明(PVC)
- 持久卷(PersistentVolume,PV) 是集群中的一块存储。可以理解为一块虚拟硬盘。
- 持久卷可以由管理员事先创建, 或者使用存储类(Storage Class)根据用户请求来动态创建。
- 持久卷属于集群的公共资源,并不属于某个namespace;
- 持久卷声明(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。
- PVC声明好比申请单,它更贴近云服务的使用场景,使用资源先申请,便于统计和计费。
- Pod 将 PVC 声明当做存储卷来使用,PVC 可以请求指定容量的存储空间和访问模式 。PVC对象是带有namespace的。
创建持久卷(PV)
创建持久卷(PV)是服务端的行为,通常集群管理员会提前创建一些常用规格的持久卷以备使用。hostPath仅供单节点测试使用,当Pod被重新创建时,可能会被调度到与原先不同的节点上,导致新的Pod没有数据。多节点集群使用本地存储,可以使用local卷。创建local类型的持久卷,需要先创建存储(StorageClass)。
1 | # 创建本地存储类 |
local卷不支持动态创建,必须手动创建持久卷(PV)。
创建local类型的持久卷,必须设置nodeAffinity(节点亲和性)。
调度器使用nodeAffinity信息来将使用local卷的 Pod 调度到持久卷所在的节点上,不会出现Pod被调度到别的节点上的情况。
注意:
local
卷也存在自身的问题,当Pod所在节点上的存储出现故障或者整个节点不可用时,Pod和卷都会失效,仍然会丢失数据,因此最安全的做法还是将数据存储到集群之外的存储或云存储上。
- 创建PV
1 | apiVersion: v1 |
创建持久卷声明(PVC)
持久卷声明(PVC)是用户端的行为,用户在创建Pod时,无法知道集群中PV的状态(名称、容量、是否可用等),用户也无需关心这些内容,只需要在声明中提出申请,集群会自动匹配符合需求的持久卷(PV)。Pod使用持久卷声明(PVC)作为存储卷。
学习笔记/image (2)-1735994597912-18.png)
1 | apiVersion: v1 |
1 | kubectl apply -f pvc.yaml |
使用PVC作为卷
Pod 的配置文件指定了 PersistentVolumeClaim,但没有指定 PersistentVolume。 对 Pod 而言,PersistentVolumeClaim 就是一个存储卷。
1 | apiVersion: v1 |
绑定
创建持久卷声明(PVC)之后,集群会查找满足要求的持久卷(PV),将 PVC 绑定到该 PV上。
PVC与PV之间的绑定是一对一的映射关系,绑定具有排他性,一旦绑定关系建立,该PV无法被其他PVC使用。
PVC可能会匹配到比声明容量大的持久卷,但是不会匹配比声明容量小的持久卷。
例如,即使集群上存在多个 50 G大小的 PV ,他们加起来的容量大于100G,也无法匹配100 G大小的 PVC。
找不到满足要求的 PV ,PVC会无限期地处于未绑定状态(Pending) , 直到出现了满足要求的 PV时,PVC才会被绑定。
访问模式
- ReadWriteOnce
- 卷可以被一个节点以读写方式挂载,并允许同一节点上的多个 Pod 访问。
- ReadOnlyMany
- 卷可以被多个节点以只读方式挂载。
- ReadWriteMany
- 卷可以被多个节点以读写方式挂载。
- ReadWriteOncePod
- 卷可以被单个 Pod 以读写方式挂载。 集群中只有一个 Pod 可以读取或写入该 PVC。
- 只支持 CSI 卷以及需要 Kubernetes 1.22 以上版本。
卷的状态
- Available(可用)– 卷是一个空闲资源,尚未绑定到任何;
- Bound(已绑定)– 该卷已经绑定到某个持久卷声明上;
- Released(已释放)– 所绑定的声明已被删除,但是资源尚未被集群回收;
- Failed(失败)– 卷的自动回收操作失败。
卷模式
卷模式(volumeMode)是一个可选参数。针对 PV 持久卷,Kubernetes 支持两种卷模式(volumeModes):
- Filesystem(文件系统):默认的卷模式。
- Block(块):将卷作为原始块设备来使用。
存储类(StorageClass)
创建持久卷(PV)
- 静态创建
- 管理员预先手动创建
- 手动创建麻烦、不够灵活(local卷不支持动态创建,必须手动创建PV)
- 资源浪费(例如一个PVC可能匹配到比声明容量大的卷)
- 对自动化工具不够友好
- 动态创建
- 根据用户请求按需创建持久卷,在用户请求时自动创建
- 动态创建需要使用存储类(StorageClass)
- 用户需要在持久卷声明(PVC)中指定存储类来自动创建声明中的卷。
- 如果没有指定存储类,使用集群中默认的存储类。
存储类(StorageClass)
一个集群可以存在多个存储类(StorageClass)来创建和管理不同类型的存储。每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件创建持久卷。 该字段必须指定。
Local Path Provisioner
学习笔记/image (3)-1735995429450-25.png)
K3s自带了一个名为local-path的存储类(StorageClass),它支持动态创建基于hostPath或local的持久卷。
创建PVC后,会自动创建PV,不需要再去手动的创建PV。
删除PVC,PV也会被自动删除。
1 | apiVersion: v1 |
卷绑定模式
volumeBindingMode用于控制什么时候动态创建卷和绑定卷
- Immediate立即创建:创建PVC后,立即创建PV并完成绑定。
- WaitForFirstConsumer 延迟创建:当使用该PVC的 Pod 被创建时,才会自动创建PV并完成绑定。
回收策略(Reclaim Policy)
回收策略告诉集群,当用户删除PVC 对象时, 从PVC中释放出来的PV将被如何处理:
- 删除(Delete,为默认配置):当PVC被删除时,关联的PV 对象也会被自动删除。
- 保留(Retain):当 PVC 对象被删除时,PV 卷仍然存在,数据卷状态变为”已释放(Released)”。