1.简介
Kubernetes的本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。目的是实现资源管理的自动化,主要提供了如下的主要功能:
- 自我修复:一旦某一个容器崩溃,能够在1秒中左右迅速启动新的容器
- 弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整
- 服务发现:服务可以通过自动发现的形式找到它所依赖的服务
- 负载均衡:如果一个服务起动了多个容器,能够自动实现请求的负载均衡
- 版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本
- 存储编排:可以根据容器自身的需求自动创建存储卷
2.组件
一个kubernetes集群主要是由控制节点(Master)、工作节点(Node)构成,每个节点上都会安装不同的组件。
2.1Master
集群的控制平面,负责集群的决策 ( 管理 )
- ApiServer : 资源操作的唯一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制
- Scheduler : 负责集群资源调度,按照预定的调度策略将Pod调度到相应的node节点上
- ControllerManager : 负责维护集群的状态,比如程序部署安排、故障检测、自动扩展、滚动更新等
- Etcd :负责存储集群中各种资源对象的信息
2.2Node
集群的数据平面,负责为容器提供运行环境 ( 干活 )
- Kubelet : 负责维护容器的生命周期,即通过控制docker,来创建、更新、销毁容器
- KubeProxy : 负责提供集群内部的服务发现和负载均衡
- Docker : 负责节点上容器的各种操作
2.3案例
- 1.首先要明确,一旦kubernetes环境启动之后,master和node都会将自身的信息存储到etcd数据库中
- 2.一个nginx服务的安装请求会首先被发送到master节点的apiServer组件
- 3.apiServer组件会调用scheduler组件来决定到底应该把这个服务安装到哪个node节点上,在此时,它会从etcd中读取各个node节点的信息,然后按照一定的算法进行选择,并将结果告知apiServer
- 4.apiServer调用controller-manager去调度Node节点安装nginx服务
- 5.kubelet接收到指令后,会通知docker,然后由docker来启动一个nginx的pod。pod是kubernetes的最小操作单元,容器必须跑在pod中至此,
- 6.一个nginx服务就运行了,如果需要访问nginx,就需要通过kube-proxy来对pod产生访问的代理,外界用户就可以访问集群中的nginx服务了
3.专业术语
3.1无状态服务和有状态服务
无状态服务(stateless service)
:该服务运行的实例不会在本地存储需要持久化的数据,并且多个实例对于同一个请求响应的结果是完全一致的。有状态服务(stateful service)
:该服务运行的实例需要在本地存储持久化数据,比如Mysql数据库。
3.2对象规约和状态
规约
:“spec”是“规约”、“规格”的意思,spec是必需的,它描述了对象的期望状态,即希望对象所具有的特征,当创建Kubernetes对象时,必须提供对象的规约,用来描述该对象的期望状态,以及关于对象的一些基本信息(例如名称)状态:
表示对象的实际状态,该属性由K8S自己维护,K8S会通过一系列的控制器对对应对象进行管理,让对象的状态与规约重合。
3.3资源对象
3.3.1集群级别
- Namespace:命名空间,即“资源对象组”
- Node:Node不是由K8S创建的,K8S只是管理Node上的资源。
- ClusterRole:是 Kubernetes 中用于定义集群范围内权限的 RBAC (基于角色的访问控制) 资源对象。它与 Role 类似,权限适用于整个集群,允许对集群级别的资源(如节点、持久卷等)和非资源端点(如/healthz)进行授权。
- ClusterRoleBinding:是 Kubernetes RBAC (基于角色的访问控制) 机制中的关键资源,用于将 ClusterRole 的权限绑定到特定的用户、组或服务账户。
3.3.2命名空间级别
- Pod:Pod指的是容器组,是K8S中的最小可部署单元,可能由单个容器或者几个紧耦合在一起的容器组成,Pod内一定有一个Pause容器,用来Pod内容器进行网络通信。
- RC(ReplicationController):用来确保容器应用的副本数始终保持在用户定义的副本数,如果有容器异常退出,会自动创建新的Pod来替代,多的容器会自动回收
- RS(ReplicaSet):RS和RC没有本质的不同,只是名字不一样,并且RS支持集合式的seletor。
- Deployment:底层也是创建一个RS,同时支持RS的扩容和缩容功能,除此之外,还支持滚动升级/回滚,暂停与恢复功能。
- StatefulSet:具有稳定的持久化存储,稳定的网络标志,有序部署,有序扩展,有序删除,由Headless Service和VolumeClaimTemplate组成。
- DaemonSet :为每一个Node上都运行一个容器副本,常用来部署一些集群的日志、监控或者其他系统管理应用。
- Job:一次性任务,运行完成后Pod销毁,不会重新启动新容器。
- CronJob:在Job的基础上加上了定时功能
- Service :简称“svc”,节点间的通信应该用Service,Service就是把Pod暴露出来提供服务,Service才是真正的服务,定义了Pod逻辑集合和访问Pod集合的策略。
- Ingress:是 Kubernetes 中管理外部访问集群服务的 API 对象,它提供了 HTTP/HTTPS 路由规则,用于将外部请求路由到集群内部的服务。Ingress 相当于 Kubernetes 的"智能路由层"或"入口网关"。
- Volume:数据卷,共享Pod中容器使用的数据,用来放持久化的数据,比如数据库数。
- ConfigMap:在 Kubernetes 中,ConfigMap 是一种用于管理和存储配置数据的资源。它允许你将配置数据与应用程序代码分离,从而使应用程序更加灵活和易于管理。ConfigMap 通常用于存储非机密数据,例如配置文件、环境变量或命令行参数。
- Secret:在 Kubernetes 中,Secret 是一种用于存储和管理敏感信息的资源。与 ConfigMap 类似,Secret 可以将配置信息与应用程序代码分离,但 Secret 专门用于存储机密数据,比如密码、API 密钥、证书等。Secret 中的数据默认会被编码成 Base64 格式以防止意外泄漏,并且支持加密存储。
- DownwardApi:在 Kubernetes 中,Downward API 是一种机制,用于将集群中的某些信息(如 Pod 的元数据和状态信息)传递给容器。通过 Downward API,容器可以获取运行时的上下文数据,例如 Pod 的名称、命名空间、节点名称、资源请求和限制等。这有助于容器在不直接调用 Kubernetes API 的情况下获取相关信息,从而增强应用程序的自适应能力。
- Role:Role 是一种资源,用于在命名空间范围内定义权限,以控制哪些用户、服务账户或其他角色可以访问特定资源。Role 通过定义规则(rules)来指定可以进行哪些操作,以及哪些资源可以被操作,从而实现基于角色的访问控制(RBAC)。
- RoleBinding:在 Kubernetes 中,RoleBinding 是一种资源,用于将指定的 Role(或 ClusterRole)绑定到用户、组或服务账户。通过 RoleBinding,可以授予特定主体(用户、组或服务账户)在命名空间内的权限,从而实现基于角色的访问控制(RBAC)。
- Horizontal Pod Autoscaler(HPA):Pod自动扩容策略,控制管理器每隔30S查询metrics的资源使用情况。支持三种metrics类型,即预定义的metrics(以利用率的方式计算,比如CPU利用率)、自定义的Pod metrics(以原始值《raw value》的方式计算)、自定义的object metrics。支持两种metrics查询方式(Heapster和自定义的REST API),支持多种metrics
- Pod Template:Pod的模板定义,被包含在其他Kubernetes对象中(例如Deployment、StatefulSet、DaemonSet),控制器通过Pod Template信息创建Pod。
- LimitRange:可以对集群内Request和Limits的配置全局统一限制,相当于批量设置一个范围内的Pod资源使用限制。
- PV:集群中的一块网络存储资源,由管理员预先配置,或者使用 StorageClass 动态配置。它是集群级别的资源,独立于Pod生命周期。
- PVC:是用户对存储的请求,它类似于Pod消耗节点资源的方式,PVC消耗PV资源。
3.3.3资源清单
3.4多资源的yaml文件编写
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
app: nginx-deploy
name: nginx-deploy
namespace: dev
spec:
replicas: 2
revisionHistoryLimit: 10
selector:
matchLabels:
app: nginx-deploy
strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
template:
metadata:
labels:
app: nginx-deploy
spec:
containers:
- image: nginx:latest
imagePullPolicy: IfNotPresent
name: nginx
resources:
limits:
cpu: 200m
memory: 128Mi
requests:
cpu: 100m
memory: 128Mi
restartPolicy: Always
3.5Kubernetes API版本
Kubernetes API是Kubernetes最强大的部分,它为你的基础设施和应用程序提供可预测、可扩展的API。也就是说KubernetesAPI就是K8S中各个组件之间通信使用的API,而yaml中的apiVersion指的就是这些接口的版本。常用的如下:
- alpha:该软件可能包含错误,启用一个功能可能会导致bug,随时可能会丢弃对该功能的支持,恕不另行通知
- beta:软件经过很好的测试。启用功能被认为是安全的 默认情况下功能是开启的 细节可能会改变,但功能在后续不会被删除
- stable:稳定版,该版本名称命名方式:vX这里X是一个整数 稳定版本,放心使用 将出现在后续发布的软件版本中,例如:v1
4.资源管理
在Kubernetes中,所有内容都被抽象为资源,用户需要通过操作资源来管理kubernetes。Kubernetes的本质上就是一个集群系统,用户可以在集群中部署各种服务,所谓的部署服务,其实就是在Kubernetes集群中运行一个个的容器,并将指定的程序跑在容器中。Kubernetes的最小管理单元是pod而不是容器,所以只能将容器放在Pod中,而Kubernetes一般也不会直接管理Pod,而是通过Pod控制器来管理Pod的。如何考虑访问Pod中服务,Kubernetes提供了Service资源实现这个功能。当然,如果Pod中程序的数据需要持久化,Kubernetes还提供了各种存储系统。
4.1命令式对象管理
直接使用 kubectl 命令操作集群,无需编写配置文件,适合快速执行一次性任务。
# 创建资源
kubectl run nginx --image=nginx
kubectl create deployment nginx --image=nginx
# 更新资源
kubectl scale deployment nginx --replicas=3
kubectl annotate pod nginx description='my annotation'
# 删除资源
kubectl delete deployment nginx
4.2命令式对象配置
用配置文件定义对象,通过命令明确指定操作(create/replace/delete),每个文件通常包含单个对象定义。
# 创建资源
kubectl create -f nginx.yaml
# 更新资源(完全替换)
kubectl replace -f nginx.yaml
# 删除资源
kubectl delete -f nginx.yaml
4.3声明式对象配置
使用配置文件定义期望状态,让 Kubernetes 决定如何达到该状态,使用 apply 操作,使用目录管理多个相关文件。
# 应用配置(创建或更新)
kubectl apply -f configs/
# 查看差异
kubectl diff -f configs/
# 删除资源(通过文件)
kubectl delete -f configs/
4.4声明式对象配置和命令式对象配置
声明式对象配置,保留历史版本信息(可通过 kubectl rollout 查看),有差异对比,而命令式都没有。
4.5Kubectl
Kubectl是Kubernetes集群的命令行工具,通过它能够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署,kubectl命令语法如下:
kubectl [command] [type] [name] [flags]
- comand:指定要对资源执行的操作,例如create、get、delete
- type:指定资源类型,比如deployment、pod、service
- name:指定资源的名称,名称大小写敏感
- flags:指定额外的可选参数
4.6Kubectl可以在node节点上运行
Kubectl的运行是需要进行配置的,它的配置文件是$HOME/.kube,如果想要在node节点运行此命令,需要将master上的.kube文件复制到node节点上,即在master节点上执行下面操作:
find / -name .kube
scp -r 192.168.126.201:/root/.kube/ /root
scp -r 192.168.126.201:/etc/kubernetes/admin.conf /etc/kubernetes/admin.conf
5.资源管理
5.1Namespace(ns)
Namespace是Kubernetes系统中的一种非常重要资源,它的主要作用是用来实现多套环境的资源隔离或者多租户的资源隔离。默认情况下,Kubernetes集群中所有的pod都是可以互相访问的,但是实际中,可能不想让两个pod之间相互的访问,那此时就可以将两个pod划分到不同的namespace中,Kubernetes通过将集群内部的资源分配到不同的namespace中,可以形成逻辑上的组,以方便不同的组的资源进行隔离使用和管理。可以同通过Kubernetes的授权机制,讲不同的namespace交给不同的租户进行管理,这样就实现了多租户的资源隔离,此时还能结合kubernetes的资源配额机制,限定不同租户能占用的资源,例如CPU使用量、内存使用量等等,实现多租户可用资源的管理。
常用命令如下:
- 查看所有命令空间:`kubectl get ns`/`kubectl get namespace`
- 查看指定的命名空间:`kubectl get ns <命名空间名称>`
- 指定输出格式查看命名控件:`kubectl get ns <命令空间名称> -o yaml/wide/json`
- 查看命名空间详情:`kubectl describe ns <命名空间名称>`
- 创建:`kubectl create ns <创建命名空间的名称>`
- 删除:`kubectl delete ns dev`
ymal创建格式
apiVersion: v1
kind: Namespace
metadata:
name: dev
5.2Pod
Pod是kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器。kubernetes在集群启动之后,集群中的各个组件也都是以Pod方式运行的。Kubernetes 通过 CRI(Container Runtime Interface) 与容器运行时交互,而不是直接调用 Docker CLI。
5.2.1常见命令
- 查看命名空间中的pod:kubectl get pods -n <命名空间名称>
- 查看pod的详细信息:kubectl describe pod <pod命名空间> -n <命名空间名称>
- 查看所有命名空间的pod:kubectl get pod --all-namespaces
- 删除指定pod:kubectl delete pod <pod命名空间> -n <命名空间名称>
- 进入指定pod:kubectl exec -it <pod命名空间> -n <命名空间名称> -c <容器名称> — /bin/bash或者/bin/sh
== yaml创建格式==
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
labels:
type: app
version: 1.0.0
spec:
containers:
- name: nginx-demo
image: nginx:latest
imagePullPolicy: IfNotPresent
command:
- nginx
- -g
- 'daemon off;'
workingDir: /usr/share/nginx/html
ports:
- name: http
containerPort: 80
protocol: TCP
env:
- name: JVM_OPTS
value: '-Xms128m -Xmx128m'
restartPolicy: OnFailure
5.2.2生命周期
- 调度:kube-scheduler 选择一个合适的节点。
- 拉取镜像:kubelet 下载容器镜像。
- 运行 Init 容器(如果配置)。
- 运行主容器:
- 执行 postStart 钩子(如果配置)。
- 启动 startupProbe → livenessProbe/readinessProbe。
- 标记为 Running。
5.3探针
Kubernetes 中的探针(Probes)是一种用于监控容器健康状况的机制,通过定期检测容器的状态来决定是否采取相应操作(如重启容器、停止流量路由等)。Kubernetes 提供了三种类型的探针,每种探针有不同的作用场景和触发行为。
5.3.1启动探针(startup probe)
- 作用:检测容器是否已经成功启动。如果配置了启动探针,其他探针(如就绪和存活探针)会等到启动探针成功后再开始运行。
- 适用场景:启动时间较长的应用(如 Java 应用或传统服务),避免在启动过程中被其他探针误杀。
5.3.2就绪探针(Readiness Probe)
- 作用:检测容器是否已准备好接收流量。如果探测失败,容器会从 Service 的负载均衡池中移除,直到探测成功。
- 适用场景:应用需要完成初始化(如加载配置、连接数据库)后才能提供服务。
5.3.3存活探针(Liveness Probe)
- 作用:检测容器是否仍在正常运行。如果探测失败,kubelet 会重启容器。
- 适用场景:检测死锁、长时间阻塞等不可恢复的问题。
5.3.4探针的检测方式
- Exec:在容器内执行命令,返回值为 0 表示成功。
livenessProbe:
exec:
command: ["cat", "/tmp/healthy"]
- HTTP GET:向容器的指定端口和路径发送 HTTP 请求,状态码为 2xx/3xx 表示成功。
readinessProbe:
httpGet:
path: /health
port: 8080
- TCP Socket:尝试与容器的指定端口建立 TCP 连接,连接成功即通过。
livenessProbe:
tcpSocket:
port: 3306
5.4Label
Label是Kubernetes系统中的一个重要概念。它的作用就是在资源上添加标识,用来对它们进行区分和选择。
5.4.1Label的特点
- 一个Label会以key/value键值对的形式附加到各种对象上,如Node、Pod、Service等等
- 一个资源对象可以定义任意数量的Label ,同一个Label也可以被添加到任意数量的资源对象
- Label通常在资源对象定义时确定,当然也可以在对象创建后动态添加或者删除
5.4.2Label Selector
标签定义完毕之后,还要考虑标签的选择,Label Selector的作用就是用于标签的选择。Label Selector分为两种。
- 基于等式的Label Selector
- name = slave:选择所有包含Label中key=“name”且value=”slave“的对象
- env !=production :选择所有包括Label中key=env且value不等于production的对象
- 基于集合的Label Selector
- name in (master,slave):选择所有包含Label中key=”name“且value=”master“或”slave“的对象
- name not in (frontend):选择所有包含Label中的key=”name”且value不等于”fronted“的对象
标签的选择条件可以使用多个,此时将多个Label Selector进行组合,使用, 进行分割即可,例如:name=slave,env!=production
5.4.3常用命令
- 为pod资源打标签:kubectl label pod <pod名称> <标签名=标签值> -n <命名空间名称>
- 覆盖标签:kubectl label pod <pod名称> <现有标签名称=新的标签值> -n <命名空间名称> --overwrite
- 查看并筛选标签:kubectl get pods -n <命名空间名称> -l
- 删除标签:kubectl label 资源类型 <资源对象名称> -n <命名空间名称> 标签名-
== yaml配置方式 ==
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
labels:
version: "3.0"
env: "test"
spec:
containers:
- image: nginx:latest
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
5.5Pod控制器
Pod是kubernetes的最小管理单元,在Kubernetes中,按照Pod的创建方式分为自主式Pod(Kubernetes直接通过命令创建的Pod)、控制器创建的Pod(kubernetes通过控制器创建的pod,这种pod删除后还会重建),常见的如下:
5.5.1Deployment(deploy)
为了更好的管理服务编排,Kubernetes引入了Deployment控制器。值得一提的是,Deployment控制器并不是直接管理pod,而是通过管理ReplicaSet来间接管理Pod,Deployment可以控制ReplicaSet的创建、升级、回滚等操作,从而管理Pod的数量和版本。即Deployment管理ReplicaSet,ReplicaSet管理Pod。相比之下,Deployment比ReplicaSet功能更加强大,能够支持服务发布的停止、继续、滚动更新、回滚和水平扩缩容等功能,使得服务编排更加方便和灵活。
5.5.1.1控制流程
- 获取实际状态: Deployment 控制器从 Etcd 中获取所有携带了指定标签的 Pod,并统计它们的数量
- 获取期望状态: 通过 Deployment 对象的 Replicas 字段,获取期望的 Pod 数量
- 对比状态: 对比实际状态和期望状态的数量。如果它们不一致,就需要进行调谐,确定是创建新的 Pod 还是删除现有的 Pod
一个 Kubernetes 对象的主要编排逻辑,实际上是在第三步的“对比”阶段完成的。这个操作,通常被叫作调谐(Reconcile)。这个调谐的过程,则被称作“Reconcile Loop”(调谐循环)或者“Sync Loop”(同步循环)。
5.5.1.2创建Deployment(声明式创建)
yaml文件如下:
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
name: nginx-deploy # deployment控制器名称
namespace: test # 所属命名空间
labels: # 标签列表
plmType: nginx # 定义标签的键值对
spec: # 详情描述
replicas: 2 # 期望的副本数量,默认为1
revisionHistoryLimit: 3 # 保留历史版本
paused: false # 暂停部署,默认是false
progressDeadlineSeconds: 600 # 部署超时时间(s),默认是600,超过这个时间就会变为失败
strategy: # 更新时替换旧pod的策略
type: RollingUpdate # 滚动更新策略
rollingUpdate: # 滚动更新
maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数
maxUnavailable: 30% # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
plmType: nginx
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
plmType: nginx #Pod 标签
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
然后执行如下命令:
kubectl apply -f deploy1.yaml
5.5.1.3创建Deployment的yaml模板
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
name: string # deployment控制器名称
namespace: string # 所属命名空间
labels: # 标签列表
key: value # 定义标签的键值对
annotations: # 自定义注解列表
key: value # 定义注解的键值对
spec: # 详情描述
replicas: int # 期望的副本数量,默认为1
revisionHistoryLimit: 3 # 保留历史版本
paused: false # 暂停部署,默认是false
progressDeadlineSeconds: 600 # 部署超时时间(s),默认是600,超过这个时间就会变为失败
strategy: # 更新时替换旧pod的策略
type: RollingUpdate # 滚动更新策略
# RollingUpdate 以滚动更新的方式更新pod,并可以通过设置maxSurge、maxUnavailable来控制滚动更新的过程
# Recreate 所有现有的pod都会在创建新的pod之前被终止
rollingUpdate: # 滚动更新
maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数
maxUnavailable: 30% # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: nginx-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod #Pod 标签
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
5.5.1.4查看创建的Deploy
[root@master ~]# kubectl get deployment -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
deployment-nginx 2/2 2 2 6s
- NAME 列出了集群中 Deployment 的名称
- READY 显示应用程序的可用的副本数。显示的模式是 就绪个数/期望个数
- UP-TO-DATE 当前处于最新版本的 Pod 的个数,所谓最新版本指的是 Pod 的 Spec 部分与 Deployment 里 Pod 模板里定义的完全一致
- AVAILABLE 当前可用的pod的数量,既是running状态又是最新版本,并且处于ready状态。AVAILABLE 字段,描述的才是用户所期望的最终状态
- AGE 显示应用程序运行的时间
创建deployment同时也会自动创建出对应的ReplicaSet,ReplicaSet的名称被格式化[Deployment名称]-[随机字符串]其中的随机字符串是使用 pod-template-hash 随机生成的。Deployment 控制器将 pod-template-hash 标签添加到 Deployment 所创建或收留的每个 ReplicaSet。此标签可确保 Deployment 的子ReplicaSets 不重叠,从而避免子ReplicaSets创建的pod与其它pod混淆。
[root@master k8s]# kubectl get pods -n test --show-labels -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
nginx-deploy-dcdf74b56-v8dxr 1/1 Running 0 10m 10.42.0.50 master <none> <none> plmType=nginx,pod-template-hash=dcdf74b56
nginx-deploy-dcdf74b56-v7qpr 1/1 Running 0 10m 10.42.0.49 master <none> <none> plmType=nginx,pod-template-hash=dcdf74b56
[root@master k8s]# kubectl get rs -n test --show-labels -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR LABELS
nginx-deploy-dcdf74b56 2 2 2 10m nginx nginx:1.17.1 plmType=nginx,pod-template-hash=dcdf74b56 plmType=nginx,pod-template-hash=dcdf74b56
[root@master k8s]# kubectl get pods -n test --show-labels -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES LABELS
nginx-deploy-dcdf74b56-v8dxr 1/1 Running 0 10m 10.42.0.50 master <none> <none> plmType=nginx,pod-template-hash=dcdf74b56
nginx-deploy-dcdf74b56-v7qpr 1/1 Running 0 10m 10.42.0.49 master <none> <none> plmType=nginx,pod-template-hash=dcdf74b56
5.5.1.5水平扩缩容
水平扩缩容非常容易实现,deployment只需要修改它所控制的ReplicaSet的pod副本个数就可以了
- 使用edit命令,修改yaml文件中spec中的replicas为5
[root@k8s-master ~]# kubectl get deploy -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
deployment-nginx 2/2 2 2 17h
[root@k8s-master ~]# kubectl edit deploy deployment-nginx -n dev
deployment.apps/deployment-nginx edited
[root@k8s-master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
deployment-nginx-587fdd789f-j6pgk 1/1 Running 0 23s
deployment-nginx-587fdd789f-lffcm 1/1 Running 0 23s
deployment-nginx-587fdd789f-nzppf 1/1 Running 1 17h
deployment-nginx-587fdd789f-r6z6t 1/1 Running 0 23s
deployment-nginx-587fdd789f-zwsdt 1/1 Running 1 17h
- 使用scale命令
[root@k8s-master ~]# kubectl scale deploy deployment-nginx --replicas=3 -n dev
deployment.apps/deployment-nginx scaled
[root@k8s-master ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
deployment-nginx-587fdd789f-lffcm 1/1 Running 0 2m24s
deployment-nginx-587fdd789f-nzppf 1/1 Running 1 17h
deployment-nginx-587fdd789f-zwsdt 1/1 Running 1 17h
scale和edit的区别
scale命令适合临时调整,仅调整副本数,不会影响 Pod 模板,不涉及 YAML 文件。下次 kubectl apply 时可能会被覆盖(如果 YAML 里定义的 replicas 不同)。但是edit修改的是 Deployment 的声明式配置(存储在 etcd 中)。会持久化修改,即使重启集群或重新 apply 也不会丢失。
5.5.1.6滚动更新
仅当 Deployment Pod 模板(即 .spec.template)发生改变时,例如模板的标签或容器镜像被更新,才会触发Deployment 上线。 其他更新(如对 Deployment 执行扩缩容的操作)不会触发上线动作,当你修改 Deployment 的副本数量(spec.replicas)时,只是调整 Pod 的数量,而不涉及 Pod 模板的变更。这不会触发新的 Pod 模板创建,也不会引发滚动更新。例如,你把 replicas 从 3 调整到 5,Kubernetes 只会再创建两个 Pod 来满足新的副本数量要求,而不会重新创建已有的 Pod。
例如修改镜像后,Kubernetes 就会立刻触发滚动更新 的过程。通过 kubectl rollout status 指令查看 deployment-nginx 的状态变化:kubectl rollout status deploy deployment-nginx -n dev。
[root@master ~]# kubectl describe deployment deployment-nginx -n dev
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 10m deployment-controller Scaled up replica set deployment-nginx-dcbd8844d to 2
Normal ScalingReplicaSet 7m9s deployment-controller Scaled up replica set deployment-nginx-dcbd8844d to 5
Normal ScalingReplicaSet 3m56s deployment-controller Scaled down replica set deployment-nginx-dcbd8844d to 3
Normal ScalingReplicaSet 19s deployment-controller Scaled up replica set deployment-nginx-9588fc68c to 1
Normal ScalingReplicaSet 18s deployment-controller Scaled down replica set deployment-nginx-dcbd8844d to 2
Normal ScalingReplicaSet 18s deployment-controller Scaled up replica set deployment-nginx-9588fc68c to 2
Normal ScalingReplicaSet 17s deployment-controller Scaled down replica set deployment-nginx-dcbd8844d to 1
Normal ScalingReplicaSet 17s deployment-controller Scaled up replica set deployment-nginx-9588fc68c to 3
Normal ScalingReplicaSet 16s deployment-controller Scaled down replica set deployment-nginx-dcbd8844d to 0
可以看到,首先,当你修改了 Deployment 里的 Pod 定义之后,Deployment Controller 会使用这个修改后的 Pod 模板,创建一个新的 ReplicaSet(hash=9588fc68c),这个新的 ReplicaSet 的初始 Pod 副本数是:0。然后在 Age=19 s 的位置,Deployment Controller 开始将这个新的 ReplicaSet 所控制的 Pod 副本数从 0 个变成 1 个,即水平扩展出一个副本。紧接着,在 Age=18 s 的位置,Deployment Controller 又将旧的 ReplicaSet(hash=dcbd8844d)所控制的旧 Pod 副本数减少一个,即水平收缩成一个副本。如此交替进行,新 ReplicaSet 管理的 Pod 副本数,从 0 个变成 1 个,再变成 2 个,最后变成 3 个。而旧的 ReplicaSet 管理的 Pod 副本数则从 3 个变成 2 个,再变成 1 个,最后变成 0 个。这样,就完成了这一组 Pod 的版本升级过程。像这样,将一个集群中正在运行的多个 Pod 版本,交替地逐一升级的过程,就是 滚动更新 。
5.5.1.7rollout
kubectl rollout status 显示当前升级状态
kubectl rollout history 显示升级历史记录
kubectl rollout pause 暂停版本升级过程
kubectl rollout resume 继续已经暂停的版本升级过程
kubectl rollout restart 重启版本升级过程
kubectl rollout undo 回滚到上一级版本,可以使用 --to-revision回滚到指定版本
默认情况下 Deployment 的所有上线记录都保留在系统中,以便可以随时回滚 。假设你在更新 Deployment 时犯了一个拼写错误,将镜像名称命名设置为
nginx:1.161
而不是nginx:1.61.1,
此上线进程会出现停滞,可以通过检查上线状态来验证
[root@master ~]# kubectl rollout status deploy deployment-nginx -n dev
Waiting for deployment "deployment-nginx" rollout to finish: 1 out of 3 new replicas have been updated...
查看所创建的 Pod,你会注意到新 ReplicaSet 所创建的 1 个 Pod 卡顿在镜像拉取循环中
[root@master ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
deployment-nginx-8545c8f6d8-gzbcq 0/1 ImagePullBackOff 0 80s
deployment-nginx-86b659b6c4-cc4x4 1/1 Running 0 25m
deployment-nginx-86b659b6c4-znhh6 1/1 Running 0 26m
查看ReplicaSet发现新版本的 ReplicaSet(hash=8545c8f6d8)的水平扩展已经停止。而且此时它已经创建了一个 Pod,但是它们都没有进入 READY 状态。这当然是因为这个 Pod 都拉取不到有效的镜像。(nginx:1.161这个镜像在docker hub中并不存在,因此滚动更新被触发后会立即停止)。与此同时旧版本的 ReplicaSet(hash=86b659b6c4)的水平收缩,也自动停止了。此时已经有一个旧 Pod 被删除,还剩下两个旧 Pod
[root@master ~]# kubectl get rs -n dev
NAME DESIRED CURRENT READY AGE
deployment-nginx-8545c8f6d8 1 1 0 44s
deployment-nginx-86b659b6c4 2 2 2 25m
deployment-nginx-9588fc68c 0 0 0 74m
deployment-nginx-dcbd8844d 0 0 0 85m
Deployment 控制器自动停止有问题的上线过程,并停止对新的 ReplicaSet 扩容。 这行为取决于所指定的 rollingUpdate 参数(具体为 maxUnavailable)。 默认情况下,Kubernetes 将此值设置为 25%
[root@master ~]# kubectl describe deployment deployment-nginx -n dev
RollingUpdateStrategy: 1 max unavailable, 0 max surge
我们只需要执行一条
kubectl rollout undo
命令,就能把整个 Deployment 回滚到上一个版本,让这个旧 ReplicaSet(hash=86b659b6c4)再次扩展成 3 个 Pod,而让新的 ReplicaSet(hash=8545c8f6d8)重新收缩到 0 个 Pod
[root@master ~]# kubectl rollout undo deployment deployment-nginx -n dev
deployment.apps/deployment-nginx rolled back
[root@master ~]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
deployment-nginx-86b659b6c4-4gfn4 1/1 Running 0 24s
deployment-nginx-86b659b6c4-cc4x4 1/1 Running 0 30m
deployment-nginx-86b659b6c4-znhh6 1/1 Running 0 30m
[root@master ~]# kubectl get rs -n dev
NAME DESIRED CURRENT READY AGE
deployment-nginx-8545c8f6d8 0 0 0 6m32s
deployment-nginx-86b659b6c4 3 3 3 31m
deployment-nginx-9588fc68c 0 0 0 80m
deployment-nginx-dcbd8844d 0 0 0 91m
果我想回滚到更早之前的版本,可以通过
kubectl rollout history
命令,查看每次 Deployment 变更对应的版本。由于我们在创建这个 Deployment 的时候,指定了--record
参数,所以我们创建这些版本时执行的 kubectl 命令,都会被记录下来。如果执行命令时没有指定--record
参数,那么 CHANGE-CAUSE 字段记录值为<none>
[root@master ~]# kubectl rollout history deploy deployment-nginx -n dev
deployment.apps/deployment-nginx
REVISION CHANGE-CAUSE
1 <none>
2 kubectl set image deployment deployment-nginx nginx=nginx:1.17.1 --namespace=dev --record=true
4 kubectl set image deploy deployment-nginx nginx=nginx:1.161 --namespace=dev --record=true
5 kubectl apply --filename=nginx-deploy.yaml --record=true
你还可以通过这个
kubectl rollout history
指令,看到每个版本对应的 Deployment 的 API 对象的细节
kubectl rollout history deploy deployment-nginx -n dev --revision=2
然后,我们就可以在
kubectl rollout undo
命令行最后,加上要回滚到的指定版本的版本号,就可以回滚到指定版本了。直接使用--to-revision=2
回滚到了2版本, 如果省略这个选项,就是回退到上个版本。
kubectl rollout undo deploy deployment-nginx -n dev --to-revision=2
其实deployment之所以可是实现版本的回滚,就是通过记录历史RS来实现的,一旦想回滚到哪个版本,只需要将当前版本pod数量降为0,然后将回滚版本的pod提升为目标数量就可以了。保留历史记录的本质是保留每次修改创建的RS控制器,而回滚的本质是切换到对应版本的RS控制器。deployment对象
spec.revisionHistoryLimit
字段就是k8s为deployment保留的历史版本个数,如果设置为0,那么就再也不能进行回滚操作了
通过控制RS,比如创建新RS,旧RS就会把里面的pod副本数一个个转移到新RS,达到滚动更新,滚动更新以后旧RS并没有被删除,而是被停用,如果想要用旧RS,达到回滚
更新的暂停与恢复
无论是直接更新还是滚动更新,都会一直更新到结束。但为了避免更新有问题,可以尝试只更新一个pod,待这个pod验证无误后,再更新其它pod。Deployment控制器支持控制更新过程中的控制,如暂停(pause)或继续(resume)更新操作
kubectl rollout pause deployment <deployment name> #暂停
kubectl rollout resume deployment <deployment name> #恢复
比如有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。
例子
例如,对于一个刚刚创建的 Deployment,获取 Deployment 信息:
[root@master ~]# kubectl get deploy -n dev -owide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment-nginx 3/3 3 3 8s nginx nginx:1.8 app=nginx-deployment
[root@master ~]# kubectl get rs -n dev -owide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
deployment-nginx-86b659b6c4 3 3 3 14s nginx nginx:1.8 app=nginx-deployment,pod-template-hash=86b659b6c4
更新deployment的版本,并配置暂停deployment,升级完第一个pod后会立即暂停后续操作
kubectl set image deploy deployment-nginx nginx=nginx:1.17.4 -n dev && kubectl rollout pause deployment deployment-nginx -n dev
5.5.1.8删除
删除deployment,其下的RS和pod也将被删除
[root@k8s-master ~]# kubectl delete deploy deployment-nginx -n dev
deployment.apps "deployment-nginx" deleted
如果某台服务器宕机或关机,那么在该节点上的pod将变成了ternminating状态,表示已经终止。另外还有新的pod在创建,所以控制器保证集群中的pod数量与配置中期望的pod数量保持一致
5.5.1.9部署策略
在Kubernetes中有几种不同的方式发布应用,所以为了让应用在升级期间依然平稳提供服务,选择一个正确的发布策略就非常重要了。选择正确的部署策略是要依赖于我们的业务需求的,下面我们列出了一些可能会使用到的策略:
重建(recreate)
:停止旧版本部署新版本滚动更新(rolling-update)
:一个接一个地以滚动更新方式发布新版本蓝绿(blue/green)
:新版本与旧版本一起存在,然后切换流量金丝雀(canary)
:将新版本面向一部分用户发布,然后继续全量发布A/B测(a/b testing)
:以精确的方式(HTTP 头、cookie、权重等)向部分用户发布新版本。A/B测实际上是一种基于数据统计做出业务决策的技术。在 Kubernetes 中并不原生支持,需要额外的一些高级组件来完成改设置(比如Istio、Linkerd、Traefik、或者自定义 Nginx/Haproxy 等)。
5.5.2DaemonSet(ds)
DaemonSet的主要作用就是在Kubernetes集群里每一个节点中运行一个pod,当有新节点加入kubernetes集群中,该pod会自动在该节点上被创建出来。当节点从集群移除后,该节点的这个Pod也会被回收。所有和这个对象相关的 Pod都会被删除。
5.5.3Job/CronJob
Job和CronJob,他们组合了Pod,实现了离线业务的处理。
在线业务 :比如Nginx、Nodejs、Mysql、Redis等等,一旦运行起来基本就不会停,也就是永远在线。
离线业务:一般不直接服务于外部用户,只对内部用户有意义,比如日志分析、数据转码等等,虽然计算很大,但只会考虑一段时间,离线业务的特点是必定会退出,不会无期限地运行下去。离线业务分为两种,一种是临时任务,跑完就完事了。一种是定时任务,可以按时按点周期运行,不需要过多干预,对应到Kubernetes里,临时任务就是API对象Job,定时任务就是API对象CronJob。
5.5.3.1Job
Job对象通常用于运行那些仅需要执行一次的任务,例如数据库迁移、批处理脚本,Job的本质是确保一个或多个Pod健康地运行直至运行完毕。如定时脚本意外退出是没办法重新执行的,Job可以判断这个脚本是不是正常退出,如果不是正常退出job会重新执行该脚本直到正常退出为止。并且还可以设置重试的次数。
创建:
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
template:
spec:
containers:
- name: pi
image: busybox
command: ["sh", "-c", "echo 'scale=5000; 4*a(1)' | bc -l "]
restartPolicy: Never #重启策略
backoffLimit: 4 #失败时重试次数,默认为6
BusyBox是一个开源项目,提供了大约400个常见UNIX/Linux命令的精简实现。它被打包为单个二进制文件,适合资源受限的环境,如嵌入式设备和物联网设备。BusyBox的特点是短小精悍,特别适合对尺寸敏感的系统。
我们可以看到这个 Job 创建的 Pod 进入了 Running 状态,这意味着它正在计算 Pi 的值
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pi-mmjnd 1/1 Running 0 40s
跟其他控制器不同的是,Job 对象并不要求你定义一个
spec.selector
来描述要控制哪些 Pod。在成功创建后,我们来查看一下这个 Job 对象
[root@master ~]# kubectl describe jobs pi
......
Pod Template:
Labels: controller-uid=500d112e-df22-43f4-a6e0-49efc462f15b
job-name=pi
......
可以看到,这个 Job 对象在创建后,它的 Pod 模板,被自动加上了一个
controller-uid=< 一个随机字符串 >
这样的 Label。而这个 Job 对象本身,则被自动加上了这个 Label 对应的 Selector,从而保证了 Job 与它所管理的 Pod 之间的匹配关系。而 Job Controller 之所以要使用这种携带了 UID 的 Label,就是为了避免不同 Job 对象所管理的 Pod 发生重合。像这种离线job可能要执行多次,若是pod的标签一样,会出现错误选择的问题我们可以看到很快 Pod 变成了 Completed 状态,这是因为容器的任务执行完成正常退出了,我们可以查看对应的日志
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pi-mmjnd 0/1 Completed 0 48s
[root@master ~]# kubectl logs pi-8xv5b
3.141592653589793238462643383279...
但是如果执行任务的 Pod 因为某种原因一直没有结束怎么办呢?同样我们可以在 Job 对象中通过设置字段
spec.activeDeadlineSeconds
来限制任务运行的最长时间,比如:
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
activeDeadlineSeconds: 10
template:
spec:
containers:
- name: pi
image: busybox
command: ["sh", "-c", "echo 'scale=5000; 4*a(1)' | bc -l "]
restartPolicy: Never #重启策略
backoffLimit: 4 #失败时重试次数,默认为6
EOF
那么当我们的任务 Pod 运行超过了 10s 后,这个 Job 的所有 Pod 都会被终止,并且 Pod 的终止原因会变成 DeadlineExceeded
如果这个离线作业失败了要怎么办?比如,我们在例子中定义了restartPolicy=Never
,那么离线作业失败后 Job Controller 就会不断地尝试创建一个新 Pod。这也是我们需要在 Pod 模板中定义restartPolicy=Never
的原因,离线计算的 Pod 永远都不应该被重启,所以想让任务进行就只能创新的Pod。如果你定义的restartPolicy=OnFailure
,那么离线作业失败后,Job Controller 就不会去尝试创建新的 Pod。但是,它会不断地尝试重启 Pod 里的容器。
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: job-failed-demo
spec:
template:
spec:
containers:
- name: test-job
image: busybox
command: ["echo123", "test failed job!"]
restartPolicy: OnFailure
EOF
重启策略改为 OnFailure,则当 Job 任务执行失败后不会创建新的 Pod 出来,只会不断重启 Pod
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
job-failed-demo-9hsfx 0/1 RunContainerError 1 (21s ago) 33s
并行
在 Job 对象中,负责并行控制的参数有两个:
spec.parallelism
:它定义的是一个 Job 在任意时间最多可以启动多少个 Pod 同时运行
spec.completions
:它定义的是 Job 至少要完成的 Pod 数目,即 Job 的最小完成数现在,我在之前计算 Pi 值的 Job 里,添加这两个参数,这样我们就指定了这个 Job 最大的并行数是 2,而最小的完成数是 4
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: pi
spec:
parallelism: 2
completions: 4
template:
spec:
containers:
- name: pi
image: busybox
command: ["sh", "-c", "echo 'scale=5000; 4*a(1)' | bc -l "]
restartPolicy: Never
backoffLimit: 4
EOF
查看这个job,COMPLETIONS 定义的最小完成数
[root@master ~]# kubectl get job
NAME COMPLETIONS DURATION AGE
pi 0/4 79s 79s
我们可以看到,这个 Job 首先创建了两个并行运行的 Pod 来计算 Pi
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pi-8f766 1/1 Running 0 19s
pi-lzdbh 1/1 Running 0 19s
而在这两个 Pod 相继完成计算后。每当有一个 Pod 完成计算进入 Completed 状态时,就会有一个新的 Pod 被自动创建出来
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pi-8f766 0/1 Completed 0 59s
pi-9rkfc 0/1 ContainerCreating 0 1s
pi-hzwmb 0/1 ContainerCreating 0 3s
pi-lzdbh 0/1 Completed 0 59s
当所有的 Pod 均已经成功退出,这个 Job 也就执行完了,所以你会看到它的 COMPLETIONS 字段的值变成了 4/4
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
pi-8f766 0/1 Completed 0 107s
pi-9rkfc 0/1 Completed 0 49s
pi-hzwmb 0/1 Completed 0 51s
pi-lzdbh 0/1 Completed 0 107s
[root@master ~]# kubectl get job
NAME COMPLETIONS DURATION AGE
pi 4/4 104s 110s
Job Controller 控制的对象,直接就是 Pod。Job Controller在控制循环中进行调谐操作,根据实际运行的、已经退出的和设置的允许并行的和最少完成的这两个参数共同计算出在这个周期里应该创建或者删除的 Pod 数目,然后调用 Kubernetes API 来执行这个操作。以上面计算 Pi 值的这个例子为例,当 Job 一开始创建出来时,实际处于 Running 状态的 Pod 数目 = 0,已经成功退出的 Pod 数目 = 0,而用户定义的 completions,也就是最终用户需要的 Pod 数目 = 4。所以在这个时刻,需要创建的 Pod 数目 = 最终需要的 Pod 数目 - 实际在 Running 状态 Pod 数目 - 已经成功退出的 Pod 数目 = 4 - 0 - 0= 4。也就是说,Job Controller 需要创建 4 个 Pod 来纠正这个不一致状态。可是,我们又定义了这个 Job 的 parallelism=2。也就是说,我们规定了每次并发创建的 Pod 个数不能超过 2 个。所以,Job Controller 会对前面的计算结果做一个修正,修正后的期望创建的 Pod 数目应该是:2 个。这时候,Job Controller 就会并发地向 kube-apiserver 发起两个创建 Pod 的请求。类似地,如果在这次调谐周期里,Job Controller 发现实际在 Running 状态的 Pod 数目,比 parallelism 还大,那么它就会删除一些 Pod,使两者相等。
5.5.3.2
CronJob和Job的关系,正如同Deployment和ReplicaSet的关系一样,CronJob是一个专门用来管理Job对象的控制器,只不过,他创建和删除Job的依据是schedule字段定义的,schedule字段是一个标准的unix Cron格式的表达式。一个 CronJob 对象其实就对应linux系统中 crontab 文件中的一行,它根据配置的时间格式周期性地运行一个Job,格式和 crontab 也是一样的:分 时 日 月 周 要运行的命令
yaml文件
apiVersion: batch/v1beta1
kind: CronJob
metadata:
name: cronjob-demo
spec:
schedule: "*/1 * * * *"
jobTemplate: #CronJob 是一个 Job 对象的控制器
spec:
template:
spec:
restartPolicy: OnFailure
containers:
- name: busybox
image: busybox
args:
- "bin/sh"
- "-c"
- "for i in 9 8 7 6 5 4 3 2 1; do echo $i; done"
spec.successfulJobsHistoryLimit(默认为3) 和 spec.failedJobsHistoryLimit(默认为1),表示历史限制,是可选的字段,指定可以保留多少完成和失败的 Job。然而,当运行一个 CronJob 时,Job 可以很快就堆积很多,所以一般推荐设置这两个字段的值。如果设置限制的值为 0,那么相关类型的 Job 完成后将不会被保留
稍微等一会儿查看可以发现多了几个 Job 资源对象,这个就是因为上面我们设置的 CronJob 资源对象,每1分钟执行一个新的 Job
[root@master ~]# kubectl get job
NAME COMPLETIONS DURATION AGE
cronjob-demo-28651212 1/1 7s 84s
cronjob-demo-28651213 0/1 24s 24s
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
cronjob-demo-28651212-pg529 0/1 Completed 0 91s
cronjob-demo-28651213-469cp 0/1 ContainerCreating 0 31s
由于定时任务的特殊性,很可能某个 Job 还没有执行完,另外一个新 Job 就产生了。这时候,你可以通过
spec.concurrencyPolicy
字段来定义具体的处理策略。比如:concurrencyPolicy=Allow
这也是默认情况,这意味着这些 Job 可以同时存在;concurrencyPolicy=Forbid
这意味着不会创建新的 Pod,该创建周期被跳过;concurrencyPolicy=Replace
这意味着新产生的 Job 会替换旧的、没有执行完的 Job。而如果某一次 Job 创建失败,这次创建就会被标记为“miss”。当在指定的时间窗口内,miss 的数目达到 100 时,那么 CronJob 会停止再创建这个 Job。这个时间窗口,可以由spec.startingDeadlineSeconds
字段指定。比如startingDeadlineSeconds=200
,意味着在过去 200 s 里,如果 miss 的数目达到了 100 次,那么这个 Job 就不会被创建执行了。
5.5.4StatefulSet(sts)
Deployment和RelicaSet是为无状态服务而设计的,一个应用的所有Pod是完全一样的。所以它们互相之间没有顺序,也无所谓在哪台宿主机上。需要的时候,Deployment就可以通过Pod模板创建新的Pod,不需要的时候,可以任意杀掉。因此对Pod的启动顺序,集群要求,点对点TCP连接也没有任意的要求。为此Kubernetes引入了StatefulSet这种资源对象来支持这种复杂的功能,StatefulSet类似于ReplicaSet,但是它可以处理Pod的启动顺序,为保留每个Pod的状态设置唯一标识,具有以下几个功能特性:
稳定的持久化存储
:即Pod死亡重新调度后还是能访问到相同的持久化数据,数据不会丢失,基于PVC来实现(公用一个存储卷,Pod死亡后,StatefulSet会维持副本数重新创建Pod,仍会使用到上个Pod使用到的存储卷)稳定的网络标志
:即Pod重新调度后其PodName和HostName不变,很多服务会以PodName或HostName为连接对象,为防止新的Pod名称发生改变,需要重新写入,基于Headless Service实现有序部署,有序扩展
:即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次进行,当前的Pod必须都是Running(运行)和Ready(就绪)状态,下一个Pod才能运行。有序收缩,有序删除
:即从N-1到0,比如先起mysql,再起nginx。停先停nginx,再停mysql
创建用的yaml
apiVersion: v1
kind: Service
metadata:
name: nginx
labels:
app: nginx
spec:
ports:
- port: 80
name: web
clusterIP: None
selector:
app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: web
spec:
serviceName: "nginx"
replicas: 3 # 默认值是 1
selector:
matchLabels:
app: nginx # 必须匹配 .spec.template.metadata.labels
template:
metadata:
labels:
app: nginx # 必须匹配 .spec.selector.matchLabels
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
name: web
扩缩容
# 使用scale命令
kubectl scale statefulSet web --replicas=5
# 使用patch命令
kubectl patch sts web -p '{"spec":{"replicas":3}}'
kubectl patch命令用于更新资源的字段。这个命令可以用来修改Kubernetes集群中的对象而不需要提供完整的资源规格。如果只是简单调整副本数,使用 scale 更直观方便。如果需要同时修改多个配置或自动化场景,patch 更灵活
级联删除
默认删除statefulset时会级联删除pod,如果不想删除pod,可以使用如下命令
[root@k8s-master statefulset]# kubectl delete sts web --cascade=false
statefulset.apps "web" deleted
[root@k8s-master statefulset]# kubectl get pods
NAME READY STATUS RESTARTS AGE
web-0 1/1 Running 0 79m
web-1 1/1 Running 0 79m
web-2 0/1 ImagePullBackOff 0 46m
5.5.5HPA
HPA(Horiaontal Pod Autoscaling)他可以根据当前Pod资源(如CPU,磁盘,内存),进行副本数的动态扩容和缩容,以便减轻各个Pod的压力,当Pod负载达到一定的阈值后,会根据扩缩容的策略生成更多新的Pod来分担压力,当Pod的使用比较空闲时,在稳定空闲一段时候后,还回自动减少Pod的副本数量。
5.5.5.1安装Metrics-server
在新版的K8S中,系统资源的采集均使用Metrics-Server服务,可以通过Metrics-Server服务采集节点和Pod的内存、磁盘、CPU和网络的使用率等信息。说的具体点:新版K8S资源使用情况的度量(如容器的 CPU 和内存使用)可以通过 Metrics API 获取。
- 1.下载并解压Metrics-Server
wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.3.7/components.yaml
- 2.修改镜像地址
- 3.安装Metrics-Server
kubectl apply -f components.yaml
- 4.查看node信息
[root@k8s-master metrics-server]# kubectl top node
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
k8s-master 187m 4% 4765Mi 61%
k8s-node1 109m 2% 3306Mi 42%
k8s-node2 90m 2% 3112Mi 40%
如果其中一个节点显示为unknow,有可能是该节点没有关闭防火墙(开放端口),关闭防火墙(开放端口)后,metrics-service需要一段时间才能检测到CPU信息。
5.5.5.2HPA的使用
- 1.创建Deployment
[root@k8s-master metrics-server]# vi php-apache.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: php-apache
spec:
selector:
matchLabels:
run: php-apache
replicas: 1
template:
metadata:
labels:
run: php-apache
spec:
containers:
- name: php-apache
image: "registry.cn-shenzhen.aliyuncs.com/cookcodeblog/hpa-example:latest"
ports:
- containerPort: 80
resources:
limits:
cpu: 500m
requests:
cpu: 200m
---
apiVersion: v1
kind: Service
metadata:
name: php-apache
labels:
run: php-apache
spec:
ports:
- port: 80
selector:
run: php-apache
- 2.创建php-apache并验证
[root@k8s-master metrics-server]# kubectl apply -f php-apache.yaml
[root@k8s-master metrics-server]# kubectl get deploy,svc php-apache
NAME READY UP-TO-DATE AVAILABLE AGE
deployment.apps/php-apache 1/1 1 1 2m26s
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/php-apache ClusterIP 10.100.133.191 <none> 80/TCP 2m26s
[root@k8s-master metrics-server]# kubectl get pod -l run=php-apache -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
php-apache-5b58575b9d-bc56q 1/1 Running 0 3m6s 10.244.1.38 k8s-node1 <none> <none>
- 3.创建HPA
[root@k8s-master metrics-server]# kubectl autoscale deployment php-apache --cpu-percent=50 --min=1 --max=10
horizontalpodautoscaler.autoscaling/php-apache autoscaled
注:为deployment php-apache 创建HPA,其中最小副本数为1,最大副本数为10,保持该deployment的所有Pod的平均CPU使用率不超过50%。在本例中,deployment的pod的resources.request.cpu为200m (200 milli-cores vCPU),所以HPA将保持所有Pod的平均CPU使用率不超过100m。
- 4.通过kubectl top pods查看pod的CPU使用情况。
[root@k8s-master metrics-server]# kubectl get hpa php-apache
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 0%/50% 1 10 1 17s
- 5.模拟增加负载
打开一个新的Terminal,创建一个临时的pod
load-generator
,并在该pod中向php-apache
服务发起不间断循环请求,模拟增加php-apache
的负载(CPU使用率)。
[root@k8s-master metrics-server]# kubectl run -i --tty load-generator --rm --image=busybox --restart=Never – /bin/sh -c “while sleep 0.01; do wget -q -O- http://php-apache; done”
If you don’t see a command prompt, try pressing enter.
OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!OK!
- 模拟压力测试几分钟后,观察HPA:
```bash
[root@k8s-master metrics-server]# kubectl get hpa php-apache
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 75%/50% 1 10 5 10m
可以看到TARGETS(CPU使用率)的acutal值已升高到75% (超过了期望值50%),副本数REPLICAS也从1自动扩容到了5。
注:如果Kubernetes集群worker节点的CPU资源已经不足,HPA自动扩容会失败,新扩容的pod会一直处在Pending状态。通过kubectl describe命令查看pod详细信息时,会看到“Insufficient cpu"的错误信息。
注:也就是HPA通过自动扩容到5个副本,来分摊了负载,使得所有Pod的平均CPU使用率保持在目标值内
- 6.模拟减少负载
在运行
load-generator
的Terminal,按下Ctrl
+C
来终止进程。
等待几分钟后,观察HPA:
[root@k8s-master metrics-server]# kubectl get hpa php-apache
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
php-apache Deployment/php-apache 0%/50% 1 10 1 22m
注:Kuberntes为了保证缩容时业务不中断,和防止频繁伸缩导致系统抖动,scaledown一次后需要等待一段时间才能再次scaledown,也叫伸缩冷却(cooldown)。默认伸缩冷却时间为5分钟。
通过
kubectl describe hpa php-apache
查看HPA自动伸缩的事件,可以看到“horizontal-pod-autoscaler New size: 1; reason: All metrics below target”的事件。
5.6Service
“Service”简写“svc”,Pod不能直接提供给外网访问,而是应该使用Service,Service就是把Pod暴露出来提供服务,可以说Service是一个应用服务的抽象,定义了Pod逻辑集合和访问这个Pod集合的策略,Service代理Pod集合,对外表现为一个访问入口,访问该入口的请求将经过负责均衡,转发到后端Pod中的容器。
在Kubernetes中,当我们使用kubectl命令的时候,master节点上的api-server会接收到kubectl命令,然后分配给对应的service,让service去调用endpoint,endpoint中存储着node节点的ip和端口,然后endpoint通过kube-proxy去调用pod中容器。
5.6.1创建
apiVersion: v1 # api版本
kind: Service
metadata:
namespace: dev # 命名空间
name: nginx-svc # 对象名称
labels:
app: nginx # Service本身的标签
spec:
selector:
app: nginx-deploy # 所有匹配到这些标签的Pod都可以通过该Service进行访问
ports:
- port: 80 # Service自己的端口,在使用内网Ip访问时使用
targetPort: 80 # 目标Pod的端口
protocol: TCP # 端口绑定的协议,支持TCP,UDP,SCTP,默认为TCP
name: web # 为端口起个名字
type: NodePort # 随机起一个端口,映射到Ports中的端口,该端口是直接绑定在Node上的,且集群中的每一个node都会绑定这个端口,也可以用于将服务暴露给外部使用,但是这种方式实际环境不推荐,效率较低,而且Service是四层负载。
5.6.2查看svc和删除svc
[root@k8s-master services]# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc NodePort 10.101.91.104 <none> 80:30718/TCP 7s
[root@k8s-master services]# kubectl delete -f nginx-service.yaml
service "nginx-svc" deleted
5.6.3创建一个 NodePort 类型的 Service,并手动指定一个外部服务的 IP 和端口作为后端(Endpoints)
普通的Service通过Label Selector对后端Endpoint列表进行了一次抽象,如果后端Endpoint不是由Pod副本提供,则Service还可以抽象定义为任意其他服务,将一个Kubernetes集群外的已知服务定义为Kubernetes内的一个Service,供集群内其他应用访问,常见的应用场景。
apiVersion: v1 # api版本
kind: Service
metadata:
namespace: dev # 命名空间
name: nginx-svc-external # 对象名称
labels:
app: nginx # Service本身的标签
spec:
ports:
- port: 80 # Service自己的端口,在使用内网Ip访问时使用
targetPort: 80 # 目标Pod的端口
name: web
type: NodePort
---
apiVersion: v1
kind: Endpoints
metadata:
labels:
app: nginx
name: nginx-svc-external
namespace: dev
subsets:
- addresses:
- ip: 172.29.201.237
ports:
- port: 8099
name: web
protocol: TCP
#创建资源
kubectl create -f nginx-svc-external-ip.yaml
#查看svc,ep
kubectl get svc,ep
#使用busybox容器检测,没有的可以创建
kubectl run -it --image busybox:1.28.4 dns-test -- /bin/sh
#已存在使用该指令进入pod
kubectl exec -it dns-test -- sh
#进入pod内使用wget命令检测,其中nginx-svc-external为服务的名称,支持跨namespace访问,访问方式为<serviceName>.<namespace>
wget http://nginx-svc-external
5.6.4将集群内部对 nginx-svc-external-domain 的访问映射到外部域名 www.wssnail.com
apiVersion: v1
kind: Service #类型
metadata: #元数据
name: nginx-svc-external-domain #service的名称
namespace: dev
labels:
app: nginx-svc-external-domain #自身的标签
spec:
type: ExternalName
externalName: www.wssnail.com
总结下来,之所以将IP或者域名配置成service的原因在于,当外部服务迁移到集群内时,只需给Service添加Selector,业务代码无需修改。也就是通过服务发现来访问。
什么是服务发现?服务发现是分布式系统中的一个核心概念,指的是让应用程序或服务能够自动发现并访问它所依赖的其他服务,而无需硬编码 IP 地址或主机名。在 Kubernetes 中,服务发现主要由 Service 和 DNS 机制来实现。也就是可以直接通过“http://服务名称”可以直接访问pod
5.6.5常用的类型
5.6.5.1ClusterIP
仅限集群内部访问,不暴露到外网,自动分配一个虚拟 IP(VIP),生命周期内固定,通过 DNS 名称(如 my-service.default.svc.cluster.local)访问。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: ClusterIP # 可省略,默认就是 ClusterIP
selector:
app: my-app
ports:
- port: 80 # Service 暴露的端口
targetPort: 8080 # Pod 的端口
5.6.5.2NodePort
在 ClusterIP 基础上,额外在每个 Node 上绑定一个静态端口(30000-32767),可以通过 NodeIP:NodePort 从集群外部访问。
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
selector:
app: my-app
ports:
- port: 80 # Service 端口(集群内访问)
targetPort: 8080 # Pod 端口
nodePort: 31000 # 手动指定 Node 端口(默认随机)
5.6.5.3LoadBalancer
自动请求云厂商(AWS/GCP/Azure)的负载均衡器。分配一个公网 IP,并自动配置负载均衡规则
。底层仍然是 NodePort + ClusterIP,但由云厂商管理流量。本地集群(如裸金属)默认不支持 LoadBalancer,需安装 MetalLB 等解决方案。
apiVersion: v1
kind: Service
metadata:
name: my-webapp
spec:
type: LoadBalancer # 关键配置
selector:
app: nginx
ports:
- port: 80 # 负载均衡器监听的端口(外部访问用)
targetPort: 80 # Pod 容器的端口
protocol: TCP
externalTrafficPolicy: Local # 可选,保留客户端真实 IP
5.6.5.4ExternalName
不代理 Pod,而是返回一个 CNAME 记录(DNS 重定向)。将 Service 名称映射到外部域名(如 my-service → api.example.com)。
apiVersion: v1
kind: Service
metadata:
name: my-external-service
spec:
type: ExternalName
externalName: api.example.com # 解析到这个域名
5.6.5.5 Headless Service(无头服务)
没有 ClusterIP,直接返回 Pod IP 列表,适用于需要直接访问 Pod 的场景(如 StatefulSet),DNS 查询会返回所有 Pod 的 IP(而不是负载均衡),适用场景为数据库集群(如 MySQL、MongoDB 副本集)。
apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None # 关键配置!
selector:
app: my-app
ports:
- port: 80
targetPort: 8080
5.7Ingress
Ingress 是 Kubernetes 中用于管理外部访问集群服务的 API 对象,主要提供 HTTP/HTTPS 路由、基于域名的虚拟主机、SSL/TLS 终止等功能。与 LoadBalancer 相比,Ingress 更专注于 应用层(L7)流量管理,且能通过单一入口暴露多个服务。Ingress 规则需要由 Ingress Controller 实现,它是一个实际运行在集群中的代理服务(如 Nginx、Traefik)。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: / # 注解(依赖 Ingress Controller)
spec:
tls:
- hosts:
- example.com
secretName: tls-secret # 引用存储证书的 Secret
rules:
- host: example.com # 域名
http:
paths:
- path: /app1 # 路径
pathType: Prefix
backend:
service:
name: app1-service # 后端 Service
port:
number: 80
- path: /app2
pathType: Prefix
backend:
service:
name: app2-service
port:
number: 8080
6.配置
6.1ConfigMap
存储非敏感的配置数据(如环境变量、配置文件)。以键值对(key-value)形式存储。可通过环境变量或文件挂载到 Pod。
apiVersion: v1
kind: ConfigMap
metadata:
name: my-config
data:
APP_COLOR: "blue"
APP_ENV: "prod"
config.json: | # 多行文本(如配置文件)
{
"logLevel": "debug"
}
作为环境变量使用
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: nginx
env:
- name: APP_COLOR # 环境变量名
valueFrom:
configMapKeyRef:
name: my-config # ConfigMap 名称
key: APP_COLOR # 键名
挂载为文件
spec:
containers:
- name: my-container
image: nginx
volumeMounts:
- name: config-volume # 卷名称
mountPath: /etc/config # 挂载路径
volumes:
- name: config-volume
configMap:
name: my-config # ConfigMap 名称
items:
- key: config.json # 键名
path: app-config.json # 挂载后的文件名
6.2Secret
存储敏感数据(如密码、TLS 证书、API 密钥)。数据以 Base64 编码(非加密,需配合 RBAC 和网络策略保护)。支持挂载为文件或环境变量。
6.2.1创建Secret
apiVersion: v1
kind: Secret
metadata:
name: my-secret
type: Opaque # 通用类型
data:
DB_USER: YWRtaW4= # admin(Base64 编码)
DB_PASSWORD: MTIzNDU2 # 123456
作为环境变量
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: my-secret
key: DB_PASSWORD
作为文件
volumes:
- name: secret-volume
secret:
secretName: my-secret
items:
- key: ssl.crt
path: tls.crt # 挂载为 /etc/secret/tls.crt
7.存储
7.1Volumn
Volume 的生命周期与 Pod 绑定,但可以超越单个容器的生命周期,解决了容器内数据持久化的问题。通过spec.volumes定义,spec.containers.volumeMounts挂载。
7.1.1EmptyDir
EmptyDir是最基础的Volumn类型,一个EmptyDir就是一个空目录,EmptyDir是在Pod被分配到Node时创建的,它的初始内容为空,并且必须指定宿主机上对应的目录文件,因为kubernets会自动分配一个目录,当Pod销毁,EmptyDir中的数据也会被永久删除。常用在临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留。
apiVersion: v1
kind: Pod
metadata:
name: empty-dir-pod
namespace: dev
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
emptyDir: {}
7.1.2HostPath
EmptyDir中的数据不会被数据化,它会随着Pod的结束而销毁,如果想简单的将数据持久化到主机中,可以选择HostPath,HostPath就是将Node主机中一个实际目录挂在到Pod中,以供容器使用,这样的设计可以保证Pod销毁了,但是数据一句依旧存在于Node主机中。例子如下(但是需要注意的是这两个容器使用了一个HostPath,所以都能读取到对方的挂载文件):
apiVersion: v1
kind: Pod
metadata:
name: host-path-pod
namespace: dev
spec:
containers:
- name: nginx
image: nginx:latest
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:latest
imagePullPolicy: IfNotPresent
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
hostPath:
path: /root/logs
type: DirectoryOrCreate # 目录存在就使用,不存在就先创建后使用
spec.volumes.hostPath.type的取值:
DirectoryOrCreate 目录存在就使用,不存在就先创建后使用
Directory 目录必须存在
FileOrCreate 文件存在就使用,不存在就先创建后使用
File 文件必须存在
Socket unix套接字必须存在
CharDevice 字符设备必须存在
BlockDevice 块设备必须存在
7.1.3NFS
HostPath可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到了别的节点,就会出现问题,此时需要准备单独的网络存储系统,比如常见的是NFS、CIFS,NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统,这样的话,无论Pod在节点上怎么转移,只要Node跟NFS的对接没问题,数据就可以成功访问了。
NFS代表网络文件系统(Network File System),它是一种用于在计算机系统之间共享文件和目录的协议,NFS被广泛用于Unix和Linux操作系统种,它允许远程计算机像访问本地文件一样访问和操作远程文件,从而方便了多台计算机之间的文件共享和协作。
例子如下:
- 1.首先要准备nfs的服务器,这里为了简单,直接是master节点做nfs服务器
# 在nfs上安装nfs服务
[root@nfs ~]# yum install nfs-utils -y
# 准备一个共享目录
[root@nfs ~]# mkdir /root/data/nfs -pv
# 将共享目录以读写权限暴露给192.168.126.0/24网段中的所有主机
[root@nfs ~]# vim /etc/exports
[root@nfs ~]# more /etc/exports
/root/data/nfs 192.168.126.0/24(rw,no_root_squash)
# 启动nfs服务
[root@nfs ~]# systemctl restart nfs
- 2.接下来,要在的每个node节点上都安装下nfs,这样的目的是为了node节点可以驱动nfs设备
# 在node上安装nfs服务,注意不需要启动
[root@k8s-master01 ~]# yum install nfs-utils -y
- 3.接下来,就可以编写pod的配置文件了,创建volume-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-nfs
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
volumes:
- name: logs-volume
nfs:
server: 192.168.126.201 #nfs服务器地址
path: /root/data/nfs #共享文件路径
- 4.最后,运行下pod,观察结果
# 创建pod
[root@k8s-master01 ~]# kubectl create -f volume-nfs.yaml
pod/volume-nfs created
# 查看pod
[root@k8s-master01 ~]# kubectl get pods volume-nfs -n dev
NAME READY STATUS RESTARTS AGE
volume-nfs 2/2 Running 0 2m9s
# 查看nfs服务器上的共享目录,发现已经有文件了
[root@k8s-master01 ~]# ls /root/data/
access.log error.log
7.2PV和PVC
我刚刚在想有了HostPath和NFS,为什么还用PV和PVC,AI给我一个解答,我觉得是正确的。开发/运维分离,开发者只需声明"我需要5GB可读写存储",运维者决定实际使用NFS、云存储还是本地SSD。
7.2.1PV
Persistent Volume,系统资源)是持久化卷的意思,是对底层的共享存储的一种抽象。一般情况下PV由kubernetes管理员进行创建和配置,它与底层具体的共享存储技术有关,并通过插件完成与共享存储的对接。创建yaml如下:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
nfs: # 存储类型,与底层真正存储对应
capacity: # 存储能力,目前只支持存储空间的设置
storage: 2Gi
accessModes: # 访问模式,ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载,ReadOnlyMany(ROX): 只读权限,可以被多个节点挂载,ReadWriteMany(RWX):读写权限,可以被多个节点挂载
storageClassName: # 存储类别,PV可以通过storageClassName参数指定一个存储类别。具有特定类别的PV只能与请求了该类别的PVC进行绑定。未设定类别的PV则只能与不请求任何类别的PVC进行绑定。
persistentVolumeReclaimPolicy: # 当PV不再被使用了之后,对其的处理方式。目前支持三种策略,需要注意的是,底层不同的存储类型可能支持的回收策略不同。Retain (保留) 保留数据,需要管理员手工清理数据,Recycle(回收) 清除 PV 中的数据,效果相当于执行 rm -rf /thevolume/*,Delete (删除) 与 PV 相连的后端存储完成 volume 的删除操作,当然这常见于云服务商的存储服务。
例子如下:
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
capacity:
storage: 3Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /root/data/pv3
server: 192.168.5.6
7.2.2PVC
PVC(Persistent Volume Claim,命名空间级别资源)是持久卷声明的意思,是用户对于存储需求的一种声明。换句话说,PVC其实就是用户向Kubernetes系统发出的一种资源需求申请。创建yaml如下:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
namespace: dev
spec:
accessModes: # 访问模式
selector: # 采用标签对PV选择
storageClassName: # 存储类别
resources: # 请求空间
requests:
storage: 5Gi
参数描述
- 访问模式:用于描述用户应用对存储资源的访问资源
- 选择条件:通过Label Selector的设置,可使PVC对于系统中已存在的PV进行筛选
- 存储类别:PVC定义时可以设定需要的后端存储的类别,只有设置了该class的pv才能被系统选出
- 资源请求:描述对存储资源的请求
例子
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
namespace: dev
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
7.2.3生命周期
PVC和PV是一一对应的,在用户定义好PVC之后,系统将根据PVC对存储资源的请求在已存在的PV中选择一个满足条件的,如果找不到PVC会无限期处于Pending状态,直到系统管理员创建一个符合其要求的PV,PV一旦绑定到某个PVC上,就会被这个PVC独占,不能再与其他PVC绑定了。用户可以在Pod中向Volume一样使用PVC。
当存储资源使用完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为已释放,但还不能立即与其他PVC进行绑定,通过之前PVC写入的数据可能还留在存储设备上,只有在清除之后PV才能再次被使用。
对于PV,管理员可以设定回收策略,用于设置与之绑定的PVC释放资源之后如何处理遗留数据的问题。只有PV的存储空间完成回收,才能供新的PVC绑定和使用。
8原生组件介绍
8.1执行流程
─────────────────────────────────────────────────────
Kubernetes 控制平面流程
─────────────────────────────────────────────────────
+─────────────+ +───────────────+
│ 用户/工具 │ │ Cloud Provider│
│ (kubectl/UI)│ │ API │
+──────┬──────+ +───────┬───────+
│ │
│ 1. 创建/更新资源请求 │
├──────────────────────┘
▼
+───────────────+ 2. 验证/存储 +──────+
│ kube-apiserver │◄──────────────────►│ etcd │
+──────┬───────┬─+ 集群状态 +──────+
│ │
│ │ 3. 资源变更通知(kube-scheduler 只关心未调度的 Pod,它的核心职责是决定 Pod 应该运行在哪个节点上。kube-controller-manager 包含多个控制器,每个控制器监听不同的资源,确保集群状态符合用户声明的期望状态.)
│ ├──────────────────────┐
│ │ │
▼ ▼ ▼
+─────────────+ +─────────────────+ +──────────────────+
│ kube-scheduler │ │ kube-controller- │ │ cloud-controller- │
+──────┬───────+ │ manager │ │ manager │
│ +────────┬────────+ +────────┬────────+
│ │ │
│ 4. 分配节点 │ 5. 确保期望状态 │ 6. 云资源操作
▼ ▼ ▼
+─────────────+ +─────────────+ +─────────────+
│ 工作节点 │ │ 工作节点 │ │ 云基础设施 │
│ (kubelet) │ │ (kubelet) │ │ (VM/负载均衡)│
+─────────────+ +─────────────+ +─────────────+
8.2CoreDNS
负责为整个k8s集群提供 DNS 服务,属于DNS插件。基于dns的接口去实现集群内部的dns内部域名解析的一种能力。k8s集群创建后,会在kube-system名称空间下默认生成两个coredns的pod,所有pod的域名请求会以负载均衡的方式向这两个coredns的pod进行域名解析。
8.2.1Pod的请求链路分析
Pod 网络栈 → kube-proxy → CoreDNS Pod。
在 Kubernetes 环境中,当 CoreDNS 成功解析一个域名后,请求不会继续传递到操作系统 /etc/resolv.conf 中配置的上游 DNS 服务器。这是由 Kubernetes DNS 解析的层级机制和 CoreDNS 的工作逻辑决定的。在 Kubernetes 环境中,CoreDNS 不会缓存 /etc/resolv.conf 中配置的上游 DNS 服务器地址,但会根据配置动态读取和使用这些上游 DNS。
9.K3S
9.1关闭防火墙
systemctl disable firewalld --now
9.2安装Docker源
# 安装docker仓库源
yum -y install wget && wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
# 安装docker
yum install -y docker-ce-20.10.12-3.el7 docker-ce-cli-20.10.12-3.el7 containerd.io
# 设置docker服务自启动并启动服务
systemctl enable docker --now
# 设置docker镜像源
mkdir /etc/docker
cat > /etc/docker/daemon.json << EOF
{
"exec-opts": ["native.cgroupdriver=systemd"],
"registry-mirrors": [
"https://atomhub.openatom.cn",
"https://registry.dockermirror.com"
]
}
EOF
# 重启docker
systemctl daemon-reload && systemctl restart docker
9.3安装K3S主节点
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn INSTALL_K3S_VERSION=v1.29.0+k3s1 sh -s server --docker
# 相关命令
systemctl status k3s # 查看服务状态
systemctl stop k3s # 停止服务
systemctl start k3s # 启动服务
systemctl restart k3s # 重新启动服务
k3s-uninstall.sh # 卸载服务
# 配置ip
vi /etc/rancher/k3s/k3s.yaml
# 配置环境变量
vi /etc/profile
# 查看集群是否正常
watch kubectl get node -o wide
9.4安装K3S Node节点
注意关闭防火墙
- 查看主节点token
cat /var/lib/rancher/k3s/server/node-token
- 添加node节点