kubernetes入门

搭建kubernetes环境

⽂档地址:
https://kubernetes.io/zh-cn/docs

搭建方式

kubeadm 工具化部署(推荐新手 / 中小团队)

kubeadm 是官方提供的自动化部署工具,能快速初始化集群,降低搭建复杂度,适合绝大多数场景。

  • 核心优势
    1. 自动化程度高,只需几条命令即可完成集群初始化。
    2. 官方维护,兼容性和稳定性有保障,支持集群升级。
    3. 学习成本低,无需深入理解 K8s 组件底层细节。
  • 关键步骤
    1. 准备至少 2 台 Linux 服务器(推荐 CentOS/Ubuntu),配置静态 IP、关闭防火墙和 SELinux。
    2. 在所有节点安装 Docker、kubeadm、kubelet 和 kubectl。
    3. 在主节点(Master)执行kubeadm init初始化控制平面。
    4. 按照初始化输出的提示,在主节点配置 kubectl 命令行工具。
    5. 在从节点(Worker)执行kubeadm join命令,将节点加入集群。

二进制手动部署(适合对集群有深度控制需求的场景)

通过手动下载 K8s 各组件(如 kube-apiserver、kube-controller-manager)的二进制包,逐一配置启动,灵活性最高但复杂度也最大。

  • 核心优势
    1. 组件版本、配置参数可完全自定义,适合特殊业务需求。
    2. 无依赖工具,集群性能和资源占用可控性更强。
    3. 能深入理解 K8s 各组件的工作原理,便于问题排查。
  • 核心劣势
    1. 部署步骤繁琐,需手动配置证书、服务启停脚本、网络插件等。
    2. 集群升级、维护成本高,需手动处理组件依赖和版本兼容。

Minikube搭建(学习)
Minikube 是 Kubernetes 官方推荐的本地单节点测试 / 学习环境搭建工具,核心优势是 轻量、一键部署、资源占用低,适合新手入门学习、本地开发测试(无需多台服务器),但不适合生产环境(仅单节点,无高可用)。

云服务商托管部署(适合企业级生产环境)

阿里云(ACK)、腾讯云(TKE)、AWS(EKS)等云厂商提供托管 K8s 服务,无需手动维护控制平面,专注于业务容器管理。

  • 核心优势
    1. 控制平面(Master 节点)由云厂商托管,无需担心高可用和运维。
    2. 支持弹性扩容、自动修复节点,与云厂商的存储、网络服务无缝集成。
    3. 提供监控、日志、告警等配套工具,降低运维复杂度。
  • 核心劣势
    1. 长期使用有服务费用,成本高于自建集群。
    2. 对云厂商有一定依赖,迁移到其他平台需额外适配。

minikube搭建

主要是为了学习k8s相关命令。

Docker环镜:

1
2
3
4
# 查看版本(需 >= 19.03)
docker --version
# 查看 Docker 状态(无报错即可)
docker info

Mac os

1
brew install minikube
1
minikube version

linux

购买2C8G的虚拟机 中国香港

1
2
3
4
5
6
# 安装docker
yum -y install docker-ce
docker -v
systemctl enable docker.service
systemctl start docker
systemctl status docker
1
docker info
1
2
3
4
5
# 如果下载慢,先下下来再上传
curl -LO https://github.com/kubernetes/minikube/releases/latest/download/minikube-linux-amd64
install minikube-linux-amd64 /usr/local/bin/minikube

minikube version

启动

1
2
# minikube start --force minikube的docker驱动不允许使用root用户启动
minikube start

安装kubectl用于验证:

1
2
3
4
5
6
7
8
9
cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-\$basearch/
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
1
yum install -y kubectl

命令

查看k8s集群信息

1
kubectl cluster-info
1
2
3
4
5
kubectl cluster-info
Kubernetes control plane is running at https://192.168.49.2:8443
CoreDNS is running at https://192.168.49.2:8443/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy

To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

查看节点信息

1
kubectl get node
1
2
NAME       STATUS   ROLES           AGE     VERSION
minikube Ready control-plane 6m39s v1.34.0

查看内部组件

1
kubectl get pod -A
1
2
3
4
5
6
7
8
NAMESPACE     NAME                               READY   STATUS    RESTARTS        AGE
kube-system coredns-66bc5c9577-nvh5f 1/1 Running 0 8m5s
kube-system etcd-minikube 1/1 Running 0 8m11s
kube-system kube-apiserver-minikube 1/1 Running 0 8m11s
kube-system kube-controller-manager-minikube 1/1 Running 0 8m11s
kube-system kube-proxy-67bvn 1/1 Running 0 8m5s
kube-system kube-scheduler-minikube 1/1 Running 0 8m11s
kube-system storage-provisioner 1/1 Running 1 (7m35s ago) 8m10s

部署Nginx

1
kubectl create deployment my-nginx --image=nginx:1.23.0

查看deployment和pod

1
kubectl get deployment,pod,svc
1
2
3
4
5
6
7
8
NAME                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/my-nginx 1/1 1 1 45s

NAME READY STATUS RESTARTS AGE
pod/my-nginx-99d9f854c-jr4zd 1/1 Running 0 45s

NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 10m

暴露80端⼝, 就是service服务

1
kubectl expose deployment my-nginx --port=80 --type=NodePort

转发端⼝(Mini Kube临时)

1
2
3
4
kubectl port-forward --address 0.0.0.0 service/my-nginx 80:80

解释:
kubectl port-forward 转发⼀个本地端⼝到 Pod 端⼝,不会返回数据,需要打开另⼀个终端来继续这个练习

在浏览器访问,可以看到nginx的index.html

Minikube vs Kubeadm 的网络差异

1. Kubeadm 集群的网络(直接暴露)

Kubeadm 是把 Kubernetes 组件(apiserver、kubelet 等)直接部署在宿主机上(或物理节点):

  • NodePort 服务会在 宿主机的物理网卡 上监听一个端口(比如 30000-32767 之间);
  • 所以你能直接用「宿主机 IP + NodePort 端口」访问(比如 http://192.168.1.100:30080),因为宿主机的端口和 Kubernetes 服务是 “直接绑定” 的。

2. Minikube 集群的网络(隔离在虚拟环境)

Minikube 为了 “一键搭建、不污染宿主机”,会创建一个 独立的虚拟环境(默认是 Docker 容器,也支持 VM):

  • 这个虚拟环境相当于一个 “迷你虚拟机”,有自己独立的内网 IP(比如你之前看到的 192.168.49.2);
  • Kubernetes 集群的所有组件(包括 NodePort 服务)都运行在这个 “虚拟环境” 内部,没有直接绑定宿主机的端口
  • 宿主机和 Minikube 虚拟环境之间是隔离的,所以你直接用「宿主机 IP + NodePort 端口」访问时,宿主机根本不知道这个端口对应的服务在哪里 —— 相当于 “门牌号指到了小区门口,但小区里的房子藏在独立院子里,外面进不去”。

“minikube service” 和 “端口转发” 的本质:打通隔离的 “通道”

既然 Minikube 的服务藏在 “虚拟环境” 里,就需要两种方式 “打通通道”,让外部(宿主机或公网)能访问:

1. minikube service(内网访问:宿主机→Minikube 虚拟环境)

minikube service <服务名> --url 是 Minikube 内置的 “快捷通道”,作用是:

  • 自动找到 Minikube 虚拟环境的内网 IP(比如 192.168.49.2);
  • 找到 NodePort 服务的端口(比如 30080);
  • 直接输出「Minikube 内网 IP + NodePort 端口」的访问地址(比如 http://192.168.49.2:30080)。

为什么能访问?

这个地址的 IP 是 Minikube 虚拟环境的内网 IP(宿主机能通过 Docker 桥接网络访问这个 IP),相当于 “直接访问小区里的房子门牌号”,而不是 “小区门口的门牌号”。

适用场景:

  • 仅在 宿主机内部访问(比如你在搭建 Minikube 的机器上用浏览器或 curl 访问);
  • 简单快捷,无需手动配置,适合开发 / 测试。

2. 端口转发(公网访问:外部机器→宿主机→Minikube 虚拟环境)

如果需要让 宿主机以外的机器(比如同一局域网的其他电脑、公网机器) 访问 Minikube 里的服务,就需要 “端口转发”,本质是:

  • 宿主机的物理网卡 上监听一个端口(比如 8080);
  • 把所有访问「宿主机 IP:8080」的请求,转发到「Minikube 内网 IP:NodePort 端口」(比如 192.168.49.2:30080);
  • 相当于在 “小区门口” 设一个中转站,外部访客找中转站,中转站再把人领到小区里的房子。

方式 1:kubectl 端口转发(临时,重启失效)

1
2
# 格式:kubectl port-forward svc/<服务名> 宿主机端口:服务内部端口
kubectl port-forward svc/nginx-test 8080:80

方式 2:Minikube 隧道(永久,后台运行)

1
2
# 后台启动隧道(转发所有 NodePort 服务到宿主机)
minikube tunnel &

概念

k8s是⼀个服务器集群系统,⽤户可以在集群中部署各种服务,也就是在k8s集群上运⾏⼀个个的容器。

在k8s中, pod是最⼩的管理单元⽽⾮容器,⼀个pod中可以有多个容器。

在k8s集群中,所有内容都可以被抽象为资源,通过操作资源来管理k8s集群。

使⽤kubectl来管理资源

1
kubectl [command] [TYPE] [NAME] [flags]

commad:对资源具体的操作,如create创建、 get获取、 delete删除
TYPE:指定资源类型,⼤⼩写敏感
NAME:指定资源的名称,⼤⼩写敏感,如果省略名称则显示所有资源
flags:指定可选的参数,如可⽤-s或者-server指定Kubernetes API server的地址和端⼝

1
2
3
4
5
6
7
8
9
10
11
12
13
#获取全部节点
kubectl get node
#获取全部pod
kubectl get pod
#查看某个pod内容
kubectl get pod pod_name
#获取全部名称空间
kubectl get ns
#查看创建的资源
kubectl get pod,svc,deploy
# 删除nginx pod,如果是靠deploy控制器创建的pod, 直接删除则会⾃动创建新的;
kubectl delete pod pod名称
# 如果需要删除则直接删除depoly控制器即可, pod会被删除

资源管理⽅式:

命令式对象管理:直接使⽤命令去操作资源

1
2
3
4
5
kubectl run 资源名称 --image=镜像名称 --port=端⼝号

例⼦:
kubectl run my-nignx-pod --image=nginx:1.23.0 --port=80
kubectl create deployment my-nginx --image=nginx:1.23.0

命令式对象配置:通过命令配置和配置⽂件去操作资源

1
kubectl create -f 配置⽂件名称.yaml

声明式对象配置:通过apply和配置⽂件操作资源

1
kubectl apply -f 配置⽂件名称.yaml

yaml例⼦:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx-deployment
spec:
replicas: 1
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.23.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
1
2
3
4
5
6
7
8
9
10
11
12
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- image: nginx:1.23.0
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP

kubectl create 和 kubectl apply的区别

kubectl create 命令⾸次执⾏时会创建资源,当再次执⾏的时候会报错,因为资源名称在同⼀命名空间内是唯⼀的

kubectl apply在⾸次执⾏的时候也会创建对应的资源,当再次执⾏的时候会根据配置⽂件进⾏升级、扩容等操作,即使配置⽂件没有变化也不影响

Kubernetes资源⽂件yaml参数介绍

yaml⽂件的编写:

不需要从零⼿写,快速编写yaml⽂件,通过命令导出新的yaml⽂件

1
2
3
4
5
#创建nginx资源⽂件并且不启动资源
kubectl create deployment my-nginx --image=nginx:1.23.0 -o yaml --dry-run=client >nginx.yaml

#查看nginx.yaml
cat nginx.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
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: my-nginx
name: my-nginx
spec:
replicas: 1
selector:
matchLabels:
app: my-nginx
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: my-nginx
spec:
containers:
- image: nginx:1.23.0
name: nginx
resources: {}
status: {}

pod属性解析:

1
2
3
4
5
6
7
8
9
10
# pod的最基础的yaml⽂件最少需要以下的⼏个参数
apiVersion: v1 # API版本号,注意:具有多个,不同的对象可能会使⽤不同API
kind: Pod # 对象类型, pod
metadata: # 元数据
name: minimal-pod # Pod 名称(必填)
namespace: default # 所属命名空间(可选,默认就是 default,可省略)
spec: # specification of the resource
containers: # 容器列表(必填,Pod 至少包含一个容器)
- name: nginx-container # 容器名称(必填,唯一标识容器)
image: nginx:1.23.0 # 容器镜像(必填,指定要运行的镜像)

常⽤字段含义

必须存在的属性

参数名 字段类型 说明
apiVersion String k8s API 的版本,可使用 kubectl apiversions 命令查询
kind String 指定 k8s 资源的类型,比如 Pod、Deployment
metadata Object 元数据对象,固定写值 metadata
metadata.name String 元数据对象的名字(如 Pod 的名称)
metadata.namespace String 元数据对象的命名空间(默认值为 default,可自定义)
spec Object 详细定义资源的核心配置,固定写值 spec
spec.containers[] list spec 对象的容器列表定义(Pod 至少包含一个容器,列表形式)
spec.containers[].name String 容器对象的名字(容器列表内唯一,用于标识单个容器)
spec.containers[].image String 定义容器要使用的镜像名称(如 nginx:1.23.0,需指定镜像版本或 latest

spec主要对象

spec.containers

参数名 字段类型 说明
spec.containers[].name String 定义容器的名字
spec.containers[].image String 定义要用到的镜像名称
spec.containers[].imagePullPolicy String 定义镜像拉取策略,可选值:1. Always:每次都尝试重新拉取镜像2. Never:仅使用本地镜像3. IfNotPresent:本地有镜像则使用本地,无则拉取在线镜像默认值:Always
spec.containers[].command[] List 指定容器启动命令(数组类型,可多个),不指定则使用镜像打包时的默认启动命令
spec.containers[].args[] List 指定容器启动命令的参数(数组类型,可多个)
spec.containers[].workingDir String 指定容器的工作目录
spec.containers[].volumeMounts[] List 指定容器内部的存储卷配置
spec.containers[].volumeMounts[].name String 指定可被容器挂载的存储卷名称(需与 spec.volumes.name 对应)
spec.containers[].volumeMounts[].mountPath String 指定存储卷在容器内的挂载路径
spec.containers[].volumeMounts[].readOnly String 设置挂载路径的读写模式,可选 true(只读)或 false(读写),默认值:false(读写)
spec.containers[].ports[] List 指定容器需要用到的端口列表
spec.containers[].ports[].name String 指定端口名称(需唯一)
spec.containers[].ports[].containerPort String 指定容器需要监听的端口号
spec.containers[].ports[].hostPort String 指定容器所在主机需要监听的端口号(默认与 containerPort 相同);注意:同一主机无法启动该容器的相同副本(端口冲突)
spec.containers[].ports[].protocol String 指定端口协议,支持 TCPUDP,默认值:TCP
spec.containers[].env[] List 指定容器运行前需设置的环境变量列表
spec.containers[].env[].name String 指定环境变量名称
spec.containers[].env[].value String 指定环境变量值
spec.containers[].resources Object 指定容器的资源限制和资源请求配置
spec.containers[].resources.limits Object 指定容器运行时的资源上限
spec.containers[].resources.limits.cpu String CPU 限制,单位为核心数(core),对应 Docker 的 --cpu-shares 参数
spec.containers[].resources.limits.memory String 内存限制,单位支持 MIB(兆字节)、GiB(吉字节)
spec.containers[].resources.requests Object 指定容器启动和调度时的资源请求(调度器会根据该值分配节点)
spec.containers[].resources.requests.cpu String CPU 请求,单位为核心数(core),容器启动时的初始化可用 CPU 数量
spec.containers[].resources.requests.memory String 内存请求,单位支持 MIB、GiB,容器启动时的初始化可用内存数量

spec.volumes

参数名 字段类型 说明
spec.volumes[] List Pod 的共享存储卷列表(供容器挂载使用)
spec.volumes[].name String 存储卷的名称,需与 spec.containers[].volumeMounts[].name 完全一致(用于关联容器和存储卷)
spec.volumes[].emptyDir Object 临时存储卷(Pod 生命周期内有效,Pod 销毁后数据丢失),配置格式:emptyDir: {}(空对象)
spec.volumes[].hostPath Object 挂载 Pod 所在宿主机的目录(数据持久化到宿主机,跨 Pod 不共享)
spec.volumes[].hostPath.path String 宿主机上的目录路径(需提前存在或配置 type 自动创建),将被挂载到容器内指定路径
spec.volumes[].secret Object 密钥型存储卷,用于挂载集群中预定义的 Secret 对象(存储密码、Token、密钥等敏感数据,挂载后容器内以文件形式访问,数据自动 base64 解码)
spec.volumes[].configMap Object 配置型存储卷,用于挂载集群中预定义的 ConfigMap 对象(存储非敏感配置数据,如配置文件、环境变量参数等)
spec.containers[].livenessProbe Object 容器健康检查(存活探针):探测无响应达到阈值后,系统自动重启容器;支持 3 种探测方式:exec(执行命令)、httpGet(HTTP 请求)、tcpSocket(TCP 连接)
spec.containers[].livenessProbe.exec Object 存活探针类型:执行命令探测(如 ls /tmp/health
spec.containers[].livenessProbe.exec.command[] List 探测执行的命令 / 脚本(数组类型,如 ["cat", "/tmp/health"]),命令退出码为 0 表示健康
spec.containers[].livenessProbe.httpGet Object 存活探针类型:HTTP 请求探测(如访问容器内 /health 接口)
spec.containers[].livenessProbe.tcpSocket Object 存活探针类型:TCP 连接探测(如访问容器的 8080 端口)
spec.containers[].livenessProbe.initialDelaySeconds Number 容器启动后延迟探测的时间(单位:秒),避免容器未启动完成就被误判为不健康
spec.containers[].livenessProbe.timeoutSeconds Number 探测超时时间(单位:秒,默认 1 秒);超过该时间无响应则判定为不健康,触发容器重启
spec.containers[].livenessProbe.periodSeconds Number 定期探测间隔时间(单位:秒,默认 10 秒);即每隔多久执行一次健康检查

Kubernetes常⻅资源类型和缩写

查看资源类型和命令

1
2
3
4
#查看资源
kubectl api-resources
#查看命令帮助
kubectl --help

常⽤资源分类和缩写:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
节点 nodes ,缩写 no
名称空间 namespaces,缩写ns
pod资源 pods, 缩写 po

pod控制器:
replicasets,缩写rs
deployments,缩写 deploy

服务发现:
统⼀pod对外接⼝ services ,缩写 svc
统⼀pod对外接⼝ingress ,缩写 ing

存储资源:
persistentvolumes,缩写pv
persistentvolumeclaims,缩写pvc

常⽤资源操作命令:

命令分类 命令 翻译(中文说明) 补充说明(核心用途)
基本命令 create 创建资源 从命令行或文件创建 Kubernetes 资源(如 Pod、Service),例:kubectl create -f pod.yaml
基本命令 delete 删除资源 删除指定资源,例:kubectl delete pod nginxkubectl delete -f pod.yaml
基本命令 edit 编辑资源 在线编辑集群中已存在的资源配置(默认用 vi 编辑器),例:kubectl edit deployment nginx
基本命令 get 获取资源 查看资源列表或详情,例:kubectl get pods(列表)、kubectl get pod nginx -o yaml(详情)
基本命令 patch 更新资源 局部修改资源配置(无需编辑完整 YAML),例:kubectl patch pod nginx -p '{"spec":{"replicas":2}}'
基本命令 explain 解释资源 查看资源的 API 文档和字段说明,例:kubectl explain pod.spec.containers
运行和调试 run 运行指定的镜像 快速创建并运行 Pod(简化版 create),例:kubectl run nginx --image=nginx:1.23.0
运行和调试 expose 暴露服务 将 Pod/Deployment 暴露为 Service(如 NodePort、ClusterIP),例:kubectl expose pod nginx --type=NodePort
运行和调试 describe 描述资源内部信息 查看资源的详细状态、事件、关联对象等(排障常用),例:kubectl describe pod nginx
运行和调试 logs 打印容器在 Pod 中的日志 查看 Pod 内容器的运行日志,例:kubectl logs nginx(默认容器)、kubectl logs nginx -c 容器名(多容器)
运行和调试 attach 进入运行中的容器(附加到容器) 实时查看容器的标准输入 / 输出(类似 docker attach),例:kubectl attach nginx -it
运行和调试 exec 执行容器中的一个命令 在运行的容器内执行命令(排障常用),例:kubectl exec nginx -it -- /bin/bash(进入终端)
运行和调试 cp 在 Pod 内外复制文件 实现 Pod 与宿主机之间的文件传输,例:kubectl cp 本地文件 nginx:/容器路径
运行和调试 scale 扩容 / 缩容 Pod 的数量 调整 Deployment/StatefulSet 的副本数,例:kubectl scale deployment nginx --replicas=3
运行和调试 autoscale 自动扩容 / 缩容 Pod 的数量 基于 CPU / 内存使用率自动调整副本数,例:kubectl autoscale deployment nginx --min=1 --max=5
高级命令 apply 应用配置(创建 / 更新资源) 从文件或命令行配置资源(支持增量更新),例:kubectl apply -f deployment.yaml(推荐优先使用)
高级命令 label 管理资源标签 为资源添加 / 修改 / 删除标签(用于筛选、调度等),例:kubectl label pod nginx env=test
其他命令 cluster-info 集群信息
version 版本

命名空间和Pod

Namespace:

Namespace是k8s系统中的⼀种⾮常重要资源,它的主要作⽤是⽤来实现多套环境的资源隔离或者多⽤户的资源隔离。

默认情况下, k8s集群中的所有的Pod都是可以相互访问的。

但是在实际中,可能不想让两个Pod之间进⾏互相的访问,那此时就可以将两个Pod划分到不同的namespace下。

k8s通过将集群内部的资源分配到不同的Namespace中,可以形成逻辑上的”组”,以⽅便不同的组的资源进⾏隔离使⽤和管理。

可以通过k8s的授权机制,将不同的namespace交给不同租户进⾏管理,这样就实现了多租户的资源隔离。

结合k8s的资源配额机制,限定不同租户能占⽤的资源,例如CPU使⽤量、内存使⽤量等等,来实现租户可⽤资源的管理。

Kubernetes 会创建四个初始NameSpace名称空间:

default: 没有指明使⽤其它名字空间的对象所使⽤的默认名字空间

kube-system: Kubernetes 系统创建对象所使⽤的名字空间

kube-public: 是⾃动创建的,命名空间下的资源可以被所有⼈访问(包括未认证⽤户)

kube-node-lease: kube-node-lease

查看所有命令空间:

1
2
kubectl get namespace
#缩写:kubectl get ns
1
2
3
4
5
NAME              STATUS   AGE
default Active 56m
kube-node-lease Active 56m
kube-public Active 56m
kube-system Active 56m

命名空间操作:

1
2
3
4
5
6
7
8
9
# 创建新命名空间
kubectl create ns dev
# 查看命名空间详情
kubectl describe ns dev
# 指定输出格式 命令: kubectl get ns ns名称 -o 格式参数, 常⻅的是wide、 json、 yaml
# 输出命名空间详细信息
kubectl get ns dev -o yaml
# 删除命名空间
kubectl delete ns dev
1
2
3
4
5
6
7
8
9
10
11
# 基于配置⽂件⽅式 namespace-dev.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev
#创建:
kubectl create -f namespace-dev.yaml
#创建:
kubectl apply -f namespace-dev.yaml
#删除:
kubectl delete -f namespace-dev.yaml

Pod:

Pod是⼀组容器, 在K8S中,最⼩的单位是Pod, ⼀个Pod可以包含多个容器,但通常情况下我们在每个Pod中仅使⽤⼀个容器,可以把Pod理解成豌⾖荚, Pod内的每个容器是⼀颗颗豌⾖。

Pod 的核⼼是运⾏容器,必须指定容器引擎,⽐如 Docker是其中⼀种技术。

分类:

⾃主创建:直接创建出来的Pod,这种pod删除后就没有了,也不会⾃动重建。

控制器创建:通过控制器创建的pod,这类Pod删除了之后,还会⾃动重建。

Pod运⾏容器数量:

每个Pod中⼀个容器的模式是最常⻅的⽤法, Pod是容器的简单封装, K8S管理Pod⽽不是直接管理容器。

⼀个Pod中同时运⾏多个需要互相协作的容器,它们共享资源,同⼀个Pod中的容器可以作为service单位。

Pod⽹络:

⼀个 pod 包含⼀组容器,⼀个 pod 不会跨越多个⼯作节点。

每个Pod都会被分配⼀个唯⼀的IP地址, Pod中的所有容器共享⽹络空间,包括IP地址和端⼝。

Pod内部的容器可以使⽤localhost互相通信。

Pod存储:

Volume 也可以⽤来持久化Pod中的存储资源,以防容器重启后⽂件丢失。

Pod中 的所有容器都可以访问共享的Volume。

K8S集群中的系统组件都是以Pod⽅式运⾏的:

1
kubectl get pod -n kube-system
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#查看创建的资源
kubectl get pod,deploy
# 删除nginx pod,如果是靠deploy控制器创建的pod, 直接删除则会⾃动创建新的;
kubectl delete pod pod名称 -n 名称空间
#通过控制器创建pod,则删除控制器即可
kubectl delete pod控制器名称 -n 名称空间

# 运⾏pod,指定名称空间
kubectl run my-nignx-pod --image=nginx:1.23.0 --port=80 --namespace dev

# 通过 deployment控制器导出yaml⽂件,指定名称空间
kubectl create deployment my-nginx --image=nginx:1.23.0 --namespace dev -o yaml --dry-run=client > nginx.yaml

#⼀次性部署3个pod,修改nginx.yaml 下⾯的从 1 改为 3
#使⽤ kubectl apply -f nginx.yaml
spec:
replicas: 3

#查看某个名称空间下 pod 更多信息
kubectl get pod -n dev -o wide

#访问pod,同个集群下⾯, master和node节点都可以访问(同个K8S内部⽹络)
curl xx.xx.xx.xx
#备注,如果使⽤了Service,即NodePort端⼝,开放了⽹络安全组则外⽹也可以访问

Pod底层原型:

每个Pod中都可以包含⼀个或者多个容器,这些容器可以分为两类:

⽤户程序所在的容器: 数量可多可少

Pause容器,这是每个Pod都会有的⼀个根容器,它的作⽤有两个:

​ 以它为依据,评估整个Pod的健康状态

​ 可以在根容器上设置Ip地址,其它容器都此有Ip(PodIP),以实现Pod内部的⽹路通信

实现机制:

通过共享⽹络:通过pause容器,把其他业务容器加⼊到pause容器⾥⾯,让所有业务容器在同⼀个namespace中,可以实现⽹络共享。

通过共享存储: 引⼊数据卷volume的概念,使⽤数据卷进⾏持久化存储。

pod镜像拉取策略imagesPullPolicy:

1
2
3
IfNotPresent:默认值,镜像在宿主机上不存在时才拉取
Always:每次创建pod都会重新拉取⼀次镜像
Never:pod永远不会主动拉取这个镜像

pod资源限制:

requests: 资源请求量,当容器运⾏时,向Node申请的最少保障资源。

limits: 资源上限,容器在Node上所能消耗的最⾼上限

1
2
3
4
5
6
7
8
9
10
11
12
resources:
requests:
memory: "内存⼤⼩"
cpu: "cpu占⽤⼤⼩"
limits:
memory: "内存占⽤⼤⼩"
cpu: "cpu占⽤⼤⼩"
——————————————————————————————————————————————
CPU 资源以 CPU 单位度量。 Kubernetes 中的⼀个 CPU 等同于:
1 个 AWS vCPU
1 个 Azure vCore
例如 100m CPU、 100 milliCPU 和 0.1 CPU 都相同

pod的创建流程:

通过kubectl apply -f xxx.yaml 创建pod

创建pod的时候:

1
2
3
4
- kubectl向apiserver发送创建pod的请求
- apiserver把pod的创建信息存储到etcd进⾏保存
- scheduler监听到未绑定node的pod资源,通过调度算法对该pod资源选定⼀个合适的node进⾏绑定,然后响应给apiserver,更新pod状态并存储到etcd中
- 在绑定的node中, Controller-Manager通知kubelet收到分配到⾃身节点上的pod,调⽤容器引擎api创建容器,并把容器状态响应给apiserver

Pod的调度策略:

在默认情况下,⼀个Pod在哪个Node节点上运⾏,是由Scheduler组件采⽤相应的算法计算出来的,这个过程是不受⼈⼯控制的。但是在实际使⽤中,这并不满⾜的需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做呢?这就要求了解k8s对Pod的调度规则。

影响pod调度的因素:

pod资源限制: scheduler根据requests找到⾜够⼤⼩的node进⾏调度

节点选择器标签(nodeSelector):把节点进⾏区分,例如dev开发环境和prod⽣产环境

例如,当前需要把pod调度到开发环境中,则可以通过scheduler将pod调度到标签选择器中为env_role:dev的node中

1
2
nodeSelector:
env_role:dev/prod

标签Label和标签选择器

Label:

K8S提供了⼀种机制来为资源进⾏分类,那就是Label(标签),同⼀类资源会拥有相同的标签。

具体形式是key-value的标记对,可以在创建资源的时候设置,也可以在后期添加和修改。

可以附加到各种资源对象上,如Node,Pod,Service,RC等,⼀个资源拥有多个标签,可以实现不同维度的管理。

给某个资源对象定义⼀个Label,就相当于给它打了⼀个标签,可以通过Label Selector(标签选择器)查询和筛选拥有某些Label的资源对象, K8S通过这种⽅式实现了类似SQL的对象查询机制。

每个资源都存在多维度属性。

1
2
3
4
版本标签: "release" : "stable" , "release" :"canary"
环境标签: "environment" : "dev" , "environment" :"production"
架构标签: "tier" : "frontend" , "tier" : "backend" , "tier" : "middleware"
分区标签: "partition" : "customerA" , "partition": "customerB"

应⽤场景:

未使⽤前,分散难管理,如果需要部署不同版本的应⽤到不同的环境中,难操作。

为Pod打上不同标签,使⽤Label组织的Pod,轻松管理

标签选择器Label selector:

是Kubernetes核⼼的分组机制,通过label selector客户端/⽤户能够识别⼀组有共同特征或属性的资源对象。

对应的资源打上标签后,可以使⽤标签选择器过滤指定的标签。

标签选择器⽬前有两个:

matchLabels ⽤于定义⼀组Label , 基于等值关系(等于、不等于) ,类似于SQL语句中的=或!=

matchExpressions 基于集合关系(属于、不属于、存在) ,类似于SQL语句中的in或 not in

备注:如果同时设置了matchLabels和matchExpressions,则两组条件为 AND关系,即需要同时满⾜所有条件才能完成Selector的筛选

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
# 通过 deployment控制器导出yaml⽂件,指定名称空间
kubectl create deployment my-nginx --image=nginx:1.23.0 --namespace dev -o yaml --dry-run=client > nginx.yaml

#命令默认不会列出任何标签
kubectl get pods

# 使⽤ --show-labels 选项来查看
kubectl get pod -n dev --show-labels

# 查看 deploy控制器标签
kubectl get deploy -n dev --show-labels

# 查看 pod 标签
kubectl get pod -n dev --show-labels
# 查看node节点标签
kubectl get node --show-labels
#给Pod资源打标签
kubectl label pod pod名称 -n dev version=1.0
#例⼦:
kubectl label pod my-nginx-99d9f854c-8xlw8 -n dev version=1.0
#给Pod资源更新标签
kubectl label pod pod名称 -n dev version=2.0 --overwrite
#例⼦:
kubectl label pod my-nginx-99d9f854c-8xlw8 -n dev version=2.0 --overwrite
#使⽤标签选择器
kubectl get pod -l "version=2.0" -n dev --show-labels
kubectl get pod -l "version!=2.0" -n dev --show-labels
#删除标签 语法
# 通用格式
kubectl label pod <pod名称> -n <命名空间> <标签名>-
# 例子
kubectl label pod my-nginx-99d9f854c-8xlw8 -n dev version-

NodeSelector标签选择器

nodeSelector是 Pod的Spec 的⼀个字段

使⽤NodeSelector,测试 pod 并指定 nodeSelector 选项绑定节点

1
2
# 查看node节点标签
kubectl get node --show-labels

配置⽂件:

1
2
3
4
5
6
7
8
9
10
11
apiVersion: v1
kind: Pod
metadata:
name: xdclass-nginx
spec:
containers:
- name: nginx
image: nginx:1.23.0
imagePullPolicy: IfNotPresent
nodeSelector:
env: test
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#创建pod
kubectl apply -f nginx.yaml
#查看pod资源
kubectl get pod

# 当Pod ⼀直处于 Pending 状态时,说明该 Pod 还未被调度到某个节点上
# Pod 中 nodeSelector 指定了节点 Label,调度器将只考虑调度 Pod 到包含该 Label 的 Node 上
# 当不存在符合该条件的 Node 时, Pod 将⽆法被调度
# 给Node资源打标签,不能给master打标签
kubectl label node node名称 env=test
kubectl label node minikube env=test
#删除 node的标签 kubectl label node 删除指定的labels(标签 key 接 - 号即可)
kubectl label node node名称 env-
kubectl label node minikube env-

Pod控制器

Pod Controller控制器

控制器是管理pod的中间层,只需要告诉Pod控制器,想要创建多少个什么样的Pod,它会创建出满⾜条件的Pod,相当于⼀个状态机,⽤来控制Pod的具体状态和⾏为。 controller会⾃动创建相应的pod资源,并在当pod发⽣故障的时候按照策略进⾏重新编排。

通过它来实现对pod的管理,⽐如启动pod、停⽌pod、扩展pod的数量等等。

yaml⽂件中 kind 填写对应的类型即可。

Pod Controller的类型概述:

ReplicaSet:

⼀种副本控制器,简称rs,主要是控制由其管理的pod,使pod副本的数量始终维持在预设的个数。

并⽀持pod数量扩缩容,镜像版本升级。

官⽅建议不要直接使⽤ReplicaSet,⽤Deployments更好,并提供很多其它有⽤的特性。

Deployment:

通过控制ReplicaSet来控制Pod,并⽀持滚动升级、回退版本,适合⽆状态的服务部署。

当某个应⽤有新版本发布时, Deployment会同时操作两个版本的ReplicaSet。

其内置多种滚动升级策略,会按照既定策略降低⽼版本的Pod数量,同时也创建新版本的Pod。

Deployment控制器不直接管理Pod对象,⽽是Deployment 管理ReplicaSet, 再由ReplicaSet管理Pod对象。

DaemonSet:

在K8S集群部署中由于节点数量不定,那么如果我们需要对每个节点中都运⾏⼀个守护进程、⽇志收集进程等情况时,在k8s中如何实现呢?这个时候就是DaemonSet应⽤场景了

这类Pod 运⾏在K8S 集群⾥的每⼀个节点(Node)上,确保所有节点上有且仅有⼀个pod。

有新的节点加⼊ K8S集群后,该 Pod 会⾃动地在新节点上被创建出来。

⽽当旧节点被删除后,它上⾯的 Pod也相应地会被回收掉。

应⽤场景:监控告警Agent、⽇志组件、监控组件等。

StatefulSet:

像RS、 Deployment、 DaemonSet都是⾯向⽆状态的服务,所管理的Pod的IP、名字,启停顺序都是随机。

StatefulSet就是有状态的集合,管理所有有状态的服务。

StatefulSet 中的 Pod 具有黏性的、独⼀⽆⼆的身份标识,重新调度后PodName和HostName不变。

Pod重新调度后还是能访问到相同的持久化数据,基于PVC实现。

分配给每个 Pod 的唯⼀顺序索引, Pod 的名称的形式为

1
<statefulset name>-<ordinal index>

应⽤场景:⽐如MySQL、 MongoDB集群。

Horizontal Pod Autoscaler:

可以基于 CPU 利⽤率或其他指标实现Pod⽔平⾃动扩缩。

被伸缩的pod需要是通过deployment或者replica set管理。

HPA不能应⽤于不可伸缩的对象,如: DaemonSets。

由资源来决定控制器⾏为,控制器周期性调整⽬标pod的副本数量,让⽬标pod的实际cpu使⽤率符合⽤户指定的数值。

Job:

普通任务容器控制器,只会执⾏⼀次,只要完成任务就⽴即退出,不需要重启或重建。

容器中的进程在正常运⾏结束后不会对其进⾏重启,⽽是将pod对象置于completed状态。

若容器中的进程因错误⽽终⽌,则需要依据配置确定是否需要重启。

应⽤场景:批处理程序,完成后容器就退出等。

Cronjob:

Linux 中有 cron 程序定时执⾏任务, K8s的 CronJob 提供了类似的功能,可以定时执⾏ Job。

创建的Pod负责周期性任务控制。

应⽤场景:执⾏周期性的重复任务,如备份数据、发送邮件、数据报表、报告⽣成等。

ReplicaSet

早期k8s没那么多控制器,使⽤Replication Controller 来部署、升级Pod,简称RC。

Replica Set – 下⼀代Replication Controller

Yaml模板介绍:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: apps/v1 # 版本号
kind: ReplicaSet #资源⽂件
metadata: # 元对象信息
name: my-rs #rs控制器名称
namespace: dev #名称空间
spec: #具体详情
replicas: 5 #副本数量
selector: # 选择器,指定rs控制器管理哪些pod资源
matchLabels: # 标签匹配规则
app: my-nginx-pod #标签key是app,值是xdclass-nginx-pod
template: # pod模板,当数量不满⾜的时候,根据下⾯编码创建pod副本
metadata: # 元对象信息
labels: # pod资源标签
app: my-nginx-pod
spec: #具体详情
containers: # 容器数组列表
- name: my-nginx #容器名称
image: nginx:1.23.0 # 镜像和版本

replicaset-nginx.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: my-rs
namespace: dev
spec:
replicas: 5
selector:
matchLabels:
app: my-nginx-pod
template:
metadata:
labels:
app: my-nginx-pod
spec:
containers:
- name: my-nginx
image: nginx:1.23.0
1
2
3
4
5
6
7
8
9
10
11
#创建
kubectl apply -f replicaset-nginx.yaml

#查看
kubectl get pods,deploy,replicaset -o wide -n dev

# 命令⾏缩容
kubectl scale rs my-rs --replicas=2 -n dev

# 删除,可以直接删除rs;也可以通过yaml删除
kubectl delete -f replicaset-nginx.yaml

Deployment

deploy-nginx-pod.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deploy
namespace: dev
spec:
replicas: 5
selector:
matchLabels:
app: my-nginx-pod
template:
metadata:
labels:
app: my-nginx-pod
spec:
containers:
- name: my-nginx
image: nginx:1.23.0
1
2
3
4
5
6
7
8
#创建
kubectl apply -f deploy-nginx-pod.yaml
# 查看deployment
kubectl get deployment -n dev
#查看
kubectl get pods,deploy,replicaset -o wide -n dev
# 删除,通过yaml删除
kubectl delete -f deploy-nginx-pod.yaml

检查集群中的 Deployment 时,所显示的字段有:

NAME 列出了集群中 Deployment 的名称。

READY 显示应⽤程序的可⽤的“副本”数,格式是“就绪个数/期望个数”。

UP-TO-DATE 显示为了达到期望状态已经更新的副本数。

AVAILABLE 显示可⽤的副本数。

AGE 应⽤程序运⾏的时间。

rollout实现滚动升级

滚动升级 (⾦丝雀发布 or 灰度发布)

对各个实例批次进⾏单独更新,⽽⾮同⼀时刻对所有实例进⾏全部更新,达到不中断服务的更新升级⽅式。

Deployment控制器 给旧版本(old_rs)副本数减少⾄0、给新版本(new_rs)副本数量增⾄期望值(replicas)。

Deployment更新有两种⽅式:

1
2
3
spec:
strategy: # 策略
type: RollingUpdate

Recreate : 删除全部旧的pod,然后创建新的pod

RollingUpdate:滚动升级更新,删除部分,更新部分,在整个更新过程中,存在两个版本的pod。

1
2
3
4
5
6
7
8
9
10
11
12
13
maxUnavailable
升级过程中不可⽤Pod的最⼤数量,默认为25%
在滚动更新时,我们可以忍受多少个Pod⽆法提供服务
值越⼩越能保证服务稳定,更新越平滑

maxSurge
升级过程中可以超过期望的Pod的最⼤数量,默认为25%;
在滚动更新时,可以有多少个额外的Pod值
调的越⼤,副本更新速度越快

MaxUnavailable为0, 在新 Pod 启动并就绪之前,不要关闭任何旧Pod
MaxSurge 为100%, ⽴即启动所有新 Pod,也就是有⾜够的资源希望尽快完成更新。
默认两个值都是 25%,如果更新⼀个 100 Pod 的 Deployment,会⽴刻创建25 个新 Old,同时会关闭 25 个旧 Pod;每次有 Pod 启动就绪,就可以关闭旧 Pod

deploy-rollout.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deploy
namespace: dev
spec:
replicas: 5
revisionHistoryLimit: 5 # 保留历史版本5个
strategy:
type: RollingUpdate # 滚动更新策略(默认也是该类型,显式声明更清晰)
selector:
matchLabels:
app: my-nginx-pod
template:
metadata:
labels:
app: my-nginx-pod
spec:
containers:
- name: my-nginx
image: nginx:1.23.0

查看控制器参数

1
2
#可以使⽤describe查看
kubectl describe deploy my-deploy -n dev

滚动升级:

将nginx版本更新⾄1.15.8

1
2
3
4
5
6
7
8
9
kubectl set image deployment/my-deploy my-nginx=nginx:1.15.8 -n dev

#备注
#追加 --record 以保存正在更改资源的 kubectl 命令,⽅便查看history版本列表修改命令
kubectl set image deployment/my-deploy my-nginx=nginx:1.15.8 -n dev --record=true
kubectl set image deployment/my-deploy my-nginx=nginx:1.23.0 -n dev --record=true

#动态查看升级过程,存在多个不同版本
kubectl get pods -n dev -w

发布回滚:

kubectl rollout 版本升级相关介绍

history 升级历史记录

undo 默认回滚上⼀版本 ,使⽤–to-revision回滚到指定版本

pause 暂停版本升级发布

resume 继续恢复刚暂停的版本升级

status 升级状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#查看历史版本列表
kubectl rollout history deployment/my-deploy -n dev

#查看具体某⼀个历史版本信息
kubectl rollout history deployment/my-deploy -n dev --revision=2

#回滚上⼀版本
kubectl rollout undo deployment/my-deploy -n dev

#查看升级情况
kubectl rollout status deployment/my-deploy -n dev

#回滚指定版本
kubectl rollout undo deployment/my-deploy -ndev --to-revision=2

#删除deployment
kubectl delete -f deploy-nginx-pod.yaml

DaemonSet

daemonset-nginx.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: my-ds
namespace: dev
spec:
selector:
matchLabels:
app: my-nginx-pod
template:
metadata:
labels:
app: my-nginx-pod
spec:
containers:
- name: my-nginx
image: nginx:1.23.0
imagePullPolicy: IfNotPresent
1
2
3
4
#创建
kubectl apply -f daemonset-nginx.yaml
#只有⼀个节点,多个节点的话,每个节点都有⼀个pod
kubectl get pod,deploy,rs,ds -n dev

Job

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: batch/v1
kind: Job
metadata:
name: xdclass-job
namespace: dev
spec:
parallelism: 2 # job并发运⾏Pods的数量,默认 1
completions: 3 # job需要成功运⾏Pods的次数,默认 1
backoffLimit: 5 # job失败后进⾏重试的次数,默认是6
activeDeadlineSeconds: 100 # job运⾏超时时间,当job超过timeout时间,则job的状态也会更新为failed
template:
spec:
restartPolicy: Never # job重启策略,OnFailure或Never(Job不支持Always)
containers:
- name: demo
image: busybox:1.35.0
# 容器的启动命令列表,在pod中的容器初始化完毕后运行命令
command: ["echo", "hello xdclass.net k8s job"]

BusyBox:

是⼀个集成了三百多个最常⽤Linux命令和⼯具的软件。

包含了简单的⼯具,例如ls、 cat和echo等等,还包含了⼀些更复杂的⼯具,例grep、 find、 telnet。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
apiVersion: batch/v1
kind: Job
metadata:
name: xdclass-job
spec:
parallelism: 2
completions: 3
backoffLimit: 5
activeDeadlineSeconds: 100
template:
spec:
restartPolicy: Never
containers:
- name: demo
image: busybox:1.35.0
command: ["echo", "hello xdclass.net k8s job"]
1
2
3
4
5
6
kubectl get job -n dev -o wide

kubectl get pods -o wide

#查看⽇志
kubectl logs my-job-75xfb

Service

在k8s⾥⾯,每个Pod都会被分配⼀个单独的IP地址,但这个IP地址会随着Pod的销毁⽽消失,重启pod的ip地址会发⽣变化,此时客户如果访问原先的ip地址则会报错。

Service (服务)就是⽤来解决这个问题的, 对外服务的统⼀⼊⼝,防⽌pod失联,定义⼀组pod的访问策略(服务发现、负载均衡)。

⼀个Service可以看作⼀组提供相同服务的Pod的对外访问接⼝,作⽤于哪些Pod是通过标签选择器来定义的。

Service是⼀个概念,主要作⽤的是节点上的kube-proxy服务进程。

定义了3个商品微服务,由⽹关作为统⼀访问⼊⼝, 前端不需要关⼼它们调⽤了哪个后端副本。 然⽽组成这⼀组后端程序的 Pod 实际上可能会发⽣变化, 前端客户端不应该也没必要知道,⽽且也不需要跟踪这⼀组后端的状态。

Service 定义的抽象能够解耦这种关联。

service分类:

ClusterIP:

默认类型,⾃动分配⼀个【仅集群内部】可以访问的虚拟IP。

NodePort:

对外访问应⽤使⽤,在ClusterIP基础上为Service在每台机器上绑定⼀个端⼝,就可以通过: ip+NodePort来访问该服务。

LoadBalancer(花钱的⽅案):

使在NodePort的基础上,借助公有云创建⼀个外部负载均衡器,并将请求转发到NodePort。

可以实现集群外部访问服务的另外⼀种解决⽅案,不过并不是所有的k8s集群都会⽀持,⼤多是在公有云托管集群中会⽀持该类型。

ExternalName(很少):

把集群外部的服务引⼊到集群内部来,在集群内部直接使⽤。没有任何类型代理被创建,这只有 Kubernetes 1.7或更⾼版本的kube-dns才⽀持。

service和pod的关系:

service和pod之间是通过 selector.app进⾏关联的。

1
2
3
spec: # 描述
selector: # 标签选择器,确定当前service代理控制哪些pod
app: my-nginx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
name: my-svc
spec:
ports:
- port: 80 # service服务端⼝
protocol: TCP
targetPort: 80 # pod端⼝,常规和容器内部端⼝⼀致
selector:
app: my-nginx-pod
status:
loadBalancer: {}

port-targetPort -nodePort-containerPort

port:
是service端⼝,即k8s中服务之间的访问端⼝ ,clusterIP:port 是提供给集群内部客户访问service的⼊⼝。

nodePort:
容器所在node节点的端⼝,通过nodeport类型的service暴露给集群节点,外部可以访问的端⼝。

targetPort:

是pod的端⼝ ,从port和nodePort来的流量经过kubeproxy流⼊到后端pod的targetPort上,最后进⼊容器。

containerPort:

是pod内部容器的端⼝, targetPort映射到containerPort。

4种端⼝作⽤不⼀样, port和nodePort都是service的端⼝。

port暴露给集群内客户访问服务, nodePort暴露给集群外客户访问服务。

这两个端⼝到来的数据都需要经过反向代理kube-proxy流⼊后端pod的targetPod,从⽽到达pod中的容器。

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
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.20
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80 # 容器端⼝
---
apiVersion: apps/v1
kind: Service
metadata:
name: nginx
spec:
type: NodePort # 有配置NodePort,外部可访问k8s中的服务
ports:
- name: nginx
port: 80 # 服务service的访问端⼝
protocol: TCP
targetPort: 80 # pod端⼝,映射到容器端⼝
nodePort: 30015 # NodePort,通过nodeport类型的service暴露给集群外部访问
selector:
app: nginx

ClusterIP

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#创建deployment
kubectl apply -f deploy-nginx-pod.yaml

#查看deployment和pod(service的缩写是svc)
kubectl get deployment,pod,svc -n dev -o wide

# 暴露服务 clusterIP 类型
kubectl expose deploy my-deploy --name=svcnginx1 --type=ClusterIP --port=80 --target-port=80 -n dev

#查看服务(多了个类型是ClusterIP的,通过curl clusterIp+port可以访问)
kubectl get deployment,pod,svc -n dev -o wide

# 查看服务详情
kubectl describe svc svc-nginx1 -n dev

NodePort和EndPoint

NodePort

常规业务的场景不全是集群内访问,也需要集群外业务访问。

那么ClusterIP就满⾜不了了, NodePort是其中的⼀种实现集群外部访问的⽅案。

对外访问应⽤使⽤,在ClusterIP基础上为Service在每台机器上绑定⼀个端⼝,就可以通过: ip+NodePort来访问该服务。

1
2
3
4
5
6
7
8
9
10
11
12
# 创建NodePort类型服务
kubectl expose deploy my-deploy --name=svcnodeport-nginx1 --type=NodePort --port=80 --targetport=80 -n dev

#查看服务(多了个类型是NodePort的,通过curl clusterIp+port可以访问)
kubectl get deployment,pod,svc -n dev -o wide

在浏览器访问, ⼯作节点开放端⼝31325,访问⼯作节点ip:31325(⽹络安全组记得开放31325端⼝)
master、 node节点访问这个 开放的端⼝都可以
Kubeadm部署,暴露端⼝对外服务,会随机选端⼝,默认范围是30000~32767,可以修改指定范围

#查看服务详情
kubectl describe svc svc-nodeport-nginx1 -n dev

Endpoint(缩写是ep)

是k8s中的⼀个资源对象,存储在etcd中,记录service对应的所有pod的访问地址。

⾥⾯有个Endpoints列表,就是当前service可以负载到的pod服务⼊⼝。

service和pod之间的通信是通过endpoint实现的。

1
2
#查看endpoint列表
kubectl get ep svc-nodeport-nginx1 -n dev -o wide

kubernetes提供了两种负载均衡策略:

默认, kube-proxy的策略,如随机、轮询

使⽤会话保持模式,即同个客户端的请求固定到某个pod,在spec中添加sessionAffinity:ClientIP即可

1
2
3
4
5
6
#验证
#查看全部pod和ip
kubectl get pods -n dev -o wide
#修改pod⾥⾯容器nginx的默认静态⻚⾯,内容为Pod所在的ip
kubectl exec -it my-deploy-64967f6b67-p66wh -n dev /bin/sh echo "my.net 5" >/usr/share/nginx/html/index.html
# curl访问ip+port 或 浏览器直接访问

数据存储持久化

容器的⽣命周期可能很短,会被频繁地创建和销毁,容器中的⽂件在磁盘上是临时存放的,这给容器中运⾏的较重要的应⽤程序带来⼀些问题

问题⼀ :当容器崩溃时⽂件丢失, kubelet 会重新启动容器,但容器会以⼲净的状态重启,之前保存在容器中的数据也会被清除

问题⼆:当在⼀个 Pod 中同时运⾏多个容器时,常常需要在这些容器之间共享⽂件

Kubernetes 卷(Volume) 这⼀抽象概念能够解决这两个问题,卷的核⼼是包含⼀些数据的⽬录, Pod 中的容器可以访问该⽬录。

Volume是k8s抽象出来的对象,它被定义在Pod上,然后被⼀个Pod⾥的多个容器挂载到具体的⽂件⽬录下。

kubernetes通过Volume实现同⼀个Pod中不同容器之间的数据共享以及数据的持久化存储。

Volume的⽣命周期不与Pod中单个容器的⽣命周期相关,当容器终⽌或者重启时, Volume中的数据也不会丢失。

K8S可以⽀持许多类型的卷, Pod 也能同时使⽤任意数量的卷。

Volume常⻅的类型:

常规存储: EmptyDir、 HostPath
⾼级存储: PV、 PVC
配置存储: ConfigMap、 Secret
其他:⽹络存储系统 NFS、 CIFS,包括云服务商提供的、本地、分布式

EmptyDir

当 Pod 指定到某个节点上时,⾸先创建的是⼀个 emptyDir卷,只要 Pod 在该节点上运⾏卷就⼀直存在。
当 Pod 因为某些原因被从节点上删除时, emptyDir 卷中的数据也会永久删除。
容器崩溃并不会导致 Pod 被从节点上移除,所以容器崩溃时emptyDir 卷中的数据是安全的。
⽤途:临时缓存空间
案例 volume-emptydir.yaml , pod⾥⾯定义两个容器,⼀个产⽣⽇志,⼀个是读⽇志输出到控制台

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
apiVersion: v1
kind: Pod
metadata:
name: my-volume-emptydir # Pod 名称
namespace: dev # 所属命名空间(需确保 dev 命名空间已存在)
spec:
containers:
- name: my-nginx # 第一个容器(Nginx,输出日志)
image: nginx:1.20 # 镜像版本(稳定版)
ports:
- containerPort: 80 # 容器暴露端口(仅声明,无端口映射)
volumeMounts: # 挂载 EmptyDir 到 Nginx 容器
- name: nginx-log-volume # 与下方 volumes.name 一致(关联存储卷)
mountPath: /var/log/nginx # Nginx 日志默认输出目录(容器内路径)
readOnly: false # 允许 Nginx 写入日志(默认 false,可省略)

- name: my-busybox # 第二个容器(Busybox,读取日志)
image: busybox:1.35.0 # 镜像版本(兼容 tail 命令)
command: ["/bin/sh", "-c", "tail -f /usr/local/test/access.log"] # 实时读取日志
volumeMounts: # 挂载同一个 EmptyDir 到 Busybox 容器
- name: nginx-log-volume # 与 Nginx 容器的 volume.name 一致(共享存储核心)
mountPath: /usr/local/test # Busybox 容器内的挂载路径(需与 command 中路径匹配)
readOnly: true # 仅读取权限(安全优化,避免误写)

volumes: # 声明存储卷(EmptyDir 类型)
- name: nginx-log-volume # 存储卷名称(与上方 volumeMounts.name 对应)
emptyDir: # 类型:EmptyDir(Pod 生命周期内的临时存储,Pod 删除后数据丢失)
medium: "" # 存储介质(默认使用节点磁盘,可选 "Memory" 表示内存tmpfs)
sizeLimit: "" # 存储大小限制(默认无限制,可选如 "1Gi" 避免磁盘溢出)
1
2
3
4
5
6
7
8
9
10
#创建
kubectl apply -f volume-emptydir.yaml
#查看
kubectl get pods -n dev -o wide
#访问nignx 产⽣访问⽇志
curl ip
#查看容器输出 -f 后是 pod的名称
kubectl logs -f my-volume-emptydir -n dev -c my-busybox
# 删除
kubectl delete -f volume-emptydir.yaml

hostPath

emptyDir中数据没做持久化,随着Pod的结束⽽销毁,需要持久化到磁盘则选其他⽅式。

hostPath类型的磁盘就是挂在了主机的⼀个⽂件或者⽬录。

案例:某些应⽤需要⽤到docker的内部⽂件,这时只需要挂在本机的/var/lib/docker作为hostPath

HostPath有多种类型,列举常⻅⼏个:

Directory 给定的⽬录路径必须存在。
DirectoryOrCreate 如果给定路径不存在,将根据需要在那⾥创建⼀个空⽬录。
File 给定路径上必须存在对应⽂件。
FileOrCreate 如果给定路径不存在,将根据需要在那⾥创建⼀个空⽂件。

volume-hostpath.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
apiVersion: v1
kind: Pod
metadata:
name: my-volume-hostpath # Pod 名称
namespace: dev # 所属命名空间(需确保 dev 已存在)
spec:
containers:
- name: my-nginx # Nginx 容器(输出日志到 HostPath)
image: nginx:1.20 # 稳定镜像版本
ports:
- containerPort: 80 # 容器暴露端口(仅声明)
volumeMounts:
- name: nginx-log-volume # 关联下方 HostPath 存储卷
mountPath: /var/log/nginx # Nginx 日志默认输出目录(容器内)
readOnly: false # 允许写入日志(默认 false,可省略)
securityContext: # 权限优化:解决 Nginx 写入宿主机目录权限问题
runAsUser: 0 # 以 root 用户运行(Nginx 需 root 权限写入日志目录)
allowPrivilegeEscalation: false # 禁止权限提升(安全加固)

- name: my-busybox # Busybox 容器(读取 HostPath 中的日志)
image: busybox:1.35.0 # 兼容 tail 命令的镜像
command: ["/bin/sh", "-c", "tail -f /usr/local/test/access.log"] # 实时读日志
volumeMounts:
- name: nginx-log-volume # 关联同一个 HostPath 存储卷(共享数据)
mountPath: /usr/local/test # Busybox 容器内挂载路径(与 command 路径匹配)
readOnly: true # 仅读取权限(安全优化,避免误写宿主机文件)
securityContext:
runAsUser: 0 # 以 root 权限读取(避免宿主机目录权限不足)
allowPrivilegeEscalation: false

volumes:
- name: nginx-log-volume # 存储卷名称(与 volumeMounts.name 对应)
hostPath:
path: /usr/local/test # 宿主机上的目录路径(Pod 调度到的节点)
type: DirectoryOrCreate # 若宿主机目录不存在,自动创建空目录(避免启动失败)
1
2
3
4
5
6
7
8
9
10
11
12
#创建
kubectl apply -f volume-hostpath.yaml
#查看
kubectl get pods -n dev -o wide
#访问nignx 产⽣访问⽇志
curl ip
#查看容器输出 -f 后是 pod的名称
kubectl logs -f my-volume-hostpath -n dev -c xdclass-busybox
#去节点对应的⽬录查看⽂件是否有,即pod运⾏的节点,主节点上是没的!!!!
ls /usr/local/test
# 删除
kubectl delete -f volume-hostpath.yaml

emptyDir和hostPath对⽐:

都是本地存储卷⽅式;
emptyDir是临时存储空间,完全不提供持久化⽀持;
hostPath的卷数据是持久化在node节点的⽂件系统中的,即便pod已经被删除了, volume卷中的数据还留存在node节点上;

ConfigMap

需求:
很多应⽤在其初始化或运⾏期间要依赖⼀些配置信息。
⼤多数时候, 存在要调整配置参数所设置的数值的需求。
ConfigMap 是 Kubernetes ⽤来向应⽤ Pod 中注⼊配置数据的⽅法。

ConfigMap介绍(缩写cm):
是K8S的⼀种API对象,⽤来把【⾮加密数据】保存到键值对中,⽐如etcd。
可以⽤作环境变量、命令⾏参数等,将环境变量、配置信息和容器镜像解耦,便于应⽤配置的修改。

使⽤⽅式:
kubectl create configmap 命令,基于⽬录、 ⽂件或者键值对来创建 ConfigMap。

1
2
#格式如下
kubectl create configmap NAME --fromliteral=key1=value1 --from-literal=key2=value2

⽅式⼀:使⽤命令⾏创建:

1
2
3
kubectl create configmap my-config --fromliteral=account=my --fromliteral=password=123456

kubectl get cm my-config -o yaml

⽅式⼆:使⽤⽂件创建configmap.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: v1
kind: ConfigMap
metadata:
name: my-configmap # ConfigMap 名称
namespace: dev # 所属命名空间(需确保 dev 已存在)
data:
# 方式1:直接存储键值对(推荐,简洁易读)
username: "my" # 字符串值(引号可选,特殊字符需加引号)
password: "123456"

# 方式2:存储多行文本/配置文件(如需存储 nginx.conf、application.yml 等)
app.conf: |
env: production
log_level: info
server:
port: 8080
timeout: 30s
1
2
3
4
#创建
kubectl create -f configmap.yaml
# 查看configmap详情
kubectl describe cm my-configmap -n dev

创建pod的yaml, pod-configmap.yaml,然后吧将创建的configmap挂载进去:

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
apiVersion: v1
kind: Pod
metadata:
name: pod-configmap # Pod 名称
namespace: dev # 所属命名空间(需确保 dev 已存在,且 ConfigMap 在此命名空间)
spec:
containers:
- name: nginx
image: nginx:1.20 # 稳定镜像版本
ports:
- containerPort: 80 # 声明容器暴露端口(可选,仅用于文档说明)
volumeMounts:
- name: config # 关联下方的 configmap 存储卷(名称必须一致)
mountPath: /config # 容器内挂载目录(ConfigMap 键会成为该目录下的文件)
readOnly: true # 安全优化:仅读取权限(配置文件无需写入,避免误改)
subPath: "" # 可选:若需挂载单个文件而非整个目录,可指定 subPath(下文扩展)
securityContext:
runAsUser: 101 # Nginx 容器默认运行用户 ID(101),避免权限不足
allowPrivilegeEscalation: false # 禁止权限提升(安全加固)
volumes:
- name: config # 存储卷名称(与 volumeMounts.name 对应)
configMap:
name: my-configmap # 关联的 ConfigMap 名称(必须已创建)
# 可选:仅挂载 ConfigMap 中的部分键(默认挂载所有键)
# items:
# - key: username # 要挂载的 ConfigMap 键
# path: user.txt # 容器内的文件名(自定义,默认与 key 一致)
# - key: app.conf
# path: app_config.yml
1
2
3
4
5
6
7
8
9
10
#创建pod
kubectl create -f pod-configmap.yaml
#查看
kubectl get pod pod-configmap -n dev
#进⼊容器
kubectl exec -it pod-configmap -n dev -- /bin/sh
# cd /config
# cat info
username:my
password:123456

Secret

需求:

有些配置需要加密存储, ConfigMap只能使⽤明⽂保存,因此不适合。

Secret:

⽤来保存敏感信息,例如密码、秘钥、证书、 OAuth 令牌和ssh key等。
就不需要把这些敏感数据暴露到镜像或者Pod中。

Pod 可以⽤三种⽅式之⼀来使⽤ Secret:
作为挂载到⼀个或多个容器上的卷 中的⽂件
作为容器的环境变量
由 kubelet 在为 Pod 拉取镜像时使⽤。

secret有多个类型:
dockerconfigjson
⽤来存储私有 docker registry的认证信息
Service Account
只要与Kubernetes API有交互的Pod,都会⾃动拥有此种类型的Secret
K8S⾃动创建,并且会⾃动挂载到Pod的/run/secrets/kubernetes.io/serviceaccount ⽬录中

1
2
3
4
5
6
7
8
9
#查看
kubectl get pod -A | grep 'kube-proxy'
#进到容器,加了 -- /bin/bash,不会有警告
kubectl exec -it -n kube-system kube-proxy-9wb4g -- /bin/sh
#查看
ls -l
/run/secrets/kubernetes.io/serviceaccount
cd /run/secrets/kubernetes.io/serviceaccount
cat ca.crt

Opaque

加密类型为base64,其特点就是将明⽂改为了密⽂

1
2
echo -n 'admin' | base64 #账号
echo -n '123456' | base64 #密码

secret.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apiVersion: v1
kind: Secret
metadata:
name: mysecret # Secret 名称
namespace: dev # 与之前的 Pod/ConfigMap 统一命名空间(推荐,避免跨命名空间访问问题)
labels:
app: xdclass # 标签(方便筛选和管理)
type: Opaque # 通用类型:存储任意键值对(Base64 编码)
data:
# 注意:data 字段的值必须是 Base64 编码后的字符串(敏感数据不明文存储)
username: YWRtaW4= # 解码后:admin(Base64 编码命令:echo -n "admin" | base64)
password: MTIzNDU2 # 解码后:123456(echo -n "123456" | base64)
stringData: # 可选:直接存储明文(K8s 会自动转为 Base64 编码,推荐用这个更方便)
db-host: "localhost" # 明文值,无需手动 Base64 编码
db-port: "3306" # 数字也需用字符串格式
1
2
3
4
5
6
#创建
kubectl apply -f secret.yaml
# 查看secret的信息
kubectl get secret
# 查看mysecret详细信息
kubectl get secret mysecret -o yaml

将secret挂载到Pod的Volume中,创建pod-secretvolume.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
apiVersion: v1
kind: Pod
metadata:
name: pod-secret # Pod 名称
namespace: dev # 与 Secret(mysecret)统一命名空间(必须,否则找不到 Secret)
spec:
containers:
- name: nginx
image: nginx:1.20 # 稳定镜像版本
ports:
- containerPort: 80 # 声明容器暴露端口(可选,仅文档说明)
volumeMounts:
- name: xdclass-config # 关联下方的 secret 存储卷(名称必须一致)
mountPath: /etc/secret # 容器内挂载目录(Secret 键会成为该目录下的文件)
readOnly: true # 核心安全优化:敏感数据仅允许读取,禁止写入/修改
# subPath: "" # 可选:需挂载单个文件时启用(避免目录覆盖,下文说明)
securityContext:
runAsUser: 101 # Nginx 默认运行用户 ID(101),适配挂载文件权限
allowPrivilegeEscalation: false # 禁止权限提升,降低安全风险
volumes:
- name: xdclass-config # 存储卷名称(与 volumeMounts.name 对应)
secret:
secretName: mysecret # 关联的 Secret 名称(必须已在 dev 命名空间创建)
# 可选:仅挂载 Secret 中的部分敏感键(按需启用,减少暴露面)
# items:
# - key: username # 要挂载的 Secret 键
# path: db-user # 容器内文件名(自定义,默认与 key 一致)
# - key: password
# path: db-pass
1
2
3
4
5
6
7
8
9
10
11
#创建pod
kubectl apply -f pod-secret-volume.yaml
#查看
kubectl get pod -o wide
#查看secret, 在pod中的secret信息实际已经被解密
kubectl exec -it pod-secret -- /bin/sh
#ls /etc/secret
#cat /etc/secret/username
#cat /etc/secret/password
#删除secret
kubectl delete secret mysecret

NFS⽹络⽂件系统搭建

什么是NFS(Network File System)

⼀种基于TCP/IP 传输的⽹络⽂件系统协议, 通过使⽤NFS协议,可以像访问本地⽬录⼀样访问远程服务器中的共享资源。

NFS服务的实现依赖于RPC (Remote Process Call,远端过程调⽤)机制,以完成远程到本地的映射过程。

⼀般需要安装nfs-utils、 rpcbind 软件包来提供NFS共享服务,前者⽤于NFS共享发布和访问,后者⽤于RPC⽀持。

采⽤TCP/IP传输⽹络⽂件,适合局域⽹环境,简单操作:

NFS端⼝: 2049
RPC端⼝: 111

NFS 在 K8S Volume⾥⾯的作⽤

当某个节点发⽣故障的时候,该节点上的pod发⽣了转移,如何保证这些pod的数据不丢失呢?

此时就需要引⼊外部⽹络⽂件系统,例如nfs或者其他具有对象存储功能的系统,可以保存当pod发⽣转移的时候数据也不丢失。

部署NFS服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#下载nfs-util (对应要⽤到的节点都需要安装,但是不需要启动)
yum install nfs-utils -y

#创建⽬录(nfs服务器)
#⽬录可以⾃定义
mkdir /opt/nfsdata
#给路径授权
chmod 777 /opt/nfsdata

#编辑/etc/exports 配置⽂件(nfs服务器)
vim /etc/exports
# ⽬录的读写权限暴露给这个⽹段的全部主机
/opt/nfsdata 172.31.101.0/24(rw,insecure,sync)

解释
172.31.101.0/24表示的IP范围, 换成32位⼆进制,四组,每组8位
/24 表示前24位不变,后8位由全0变化到全1的过程,也就是由“00000000”变化到“11111111”
⼜因为全0是⼦⽹⽹络地址,全1是⼦⽹⼴播地址,这两个地址是不分配给主机使⽤的。
所以有效的可分配的范围是前24位不变,后8位由
“00000001”变化为“11111110”的范围
再转换回⼗进制就是172.31.101.1~172.31.101.254

参数:

rw 共享⽬录可读可写

secure 限制客户端只能从⼩于1024的tcp/ip端⼝连接服务器;

insecure允许客户端从⼤于1024的tcp/ip端⼝连接服务器;

sync 将数据同步写⼊内存缓冲区与磁盘中,效率低,但可以保证数据的⼀致性;

async 将数据先保存在内存缓冲区中,必要时才写⼊磁盘;

1
2
3
# 启动rpcbind(安装nfs依赖包会⾃动下载)和nfs服务
systemctl start rpcbind
systemctl start nfs

验证:

showmount -e localhost

NF持久卷挂载

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
apiVersion: v1
kind: Pod
metadata:
name: my-nfs
namespace: dev
labels:
apps: nginx-nfs
spec:
containers:
- name: nginx
image: nginx:1.20
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx # Nginx 日志默认输出目录(容器内)
readOnly: false # 允许写入日志(必需,默认 false 可省略)
securityContext:
runAsUser: 0 # 以 root 运行(Nginx 需 root 权限写入日志目录)
allowPrivilegeEscalation: false # 禁止权限提升(安全加固)
resources:
limits:
ephemeral-storage: 1Gi # 限制临时存储使用(避免日志占满磁盘)
requests:
ephemeral-storage: 512Mi
volumes:
- name: logs-volume
nfs:
server: 172.31.101.8 # NFS 服务器 IP(必须能被 K8s 节点访问)
path: /opt/nfsdata # NFS 共享目录(需提前在服务器创建并授权)
readOnly: false # 客户端挂载为可写(与容器权限匹配)
# NFS 连接参数(优化稳定性,按需调整)
mountOptions:
- hard # 硬挂载:NFS 不可用时,容器会阻塞(避免数据丢失)
- nfsvers=4.1 # 指定 NFS 版本(推荐 4.1,兼容性和性能更好)
- rw # 读写权限(与 readOnly: false 一致)
- noatime # 禁用文件访问时间更新(提升性能)
1
2
3
4
5
6
7
8
9
#暴露服务
kubectl expose pod my-nfs -n dev --port=80 --target-port=80 --type=NodePort

#查看nfs服务器的/opt/nfsdata
ls
# 此时路径下⾯已经有内容了

#访问nginx服务并查看nfs⽇志
tail -f access.log

PV和PVC

Kubernetes ⽀持的存储插件很多:
awsElasticBlockStore - AWS 弹性块存储(EBS)
azureDisk - Azure Disk
azureFile - Azure File
cephfs- CephFS volume
azureDisk - Azure Disk
hostPath - HostPath 卷
nfs- ⽹络⽂件系统 (NFS) 存储

在整个k8s集群中,有⼀些存储的资源,⽐如说NFS、 CIFS等存储,这些存储都是由存储⼯程师去创建的,不同的存储⽅式不⼀样, 如果都掌握才可以使⽤,则很不⽅便的。

所以在k8s中提供了新的对象资源叫做PV(Persistent Volume)和PVC(Persistent Volume Claim),更⽅便⽤户直接进⾏使⽤。

什么是PV 持久卷(Persistent Volume)
是集群中由管理员配置的⼀段⽹络存储,它是集群的⼀部分资源和底层存储密切相关,对象包含存储实现的细节,即 对接NFS、 CIFS等存储系统。
不同的PV会对应到不⽤的存储资源,这样在部署pod的时候直接调⽤集群内部的pv即可。

PV没有命名空间隔离概念。

什么是PVC 持久卷声明 (Persistent Volume Claim)

假如存在很多PV, K8S 要⽤PV的时候直接调⽤某个PV的话,那如果需要的是存储能⼒⽐较⼤存储资源,所以这个时候需要⼀个⼀个去对⽐pv,这样很耗费资源(因为要满⾜需求)。

PVC是⽤户存储的⼀种声明, PVC 可以请求特定的存储空间和访问模式, PVC 消耗的是 PV 资源。

PVC必须与对应的PV建⽴关系, PVC会根据定义的PV去申请。

创建pod的时候会附带⼀个PVC的请求, PVC的请求相当于就是去寻找⼀个合适的pv。

使⽤逻辑:
在 pod 中定义⼀个存储卷(该存储卷类型为 PVC),定义的时候按指定⼤⼩, PVC 必须与对应的 PV 建⽴关系, PVC会根据定义的需求【去 PV 申请】,⽽ PV 是由存储空间创建出来的。

PV和PVC逻辑:
PV 是集群中的【资源】, PVC 是对这些【资源的请求】
PV 和 PVC 之间的相互作⽤遵循这个⽣命周期:
Provisioning(配置) —> Binding(绑定) —> Using(使⽤) —>Releasing(释放) —> Recycling(回收)

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
apiVersion: v1
kind: PersistentVolume
metadata:
name: xdclass-pv # PV 名称(集群级资源,不绑定命名空间)
labels:
app: xdclass-pv # 标签(方便 PVC 通过标签选择器匹配)
spec:
capacity:
storage: 5Gi # PV 存储容量(需与 PVC 申请容量匹配或更大)
accessModes:
- ReadWriteOnce # 访问模式:仅允许单个节点以读写方式挂载(适配 NFS 单客户端写入场景)
# - ReadOnlyMany # 可选:允许多个节点以只读方式挂载(按需启用)
# - ReadWriteMany # 可选:允许多个节点以读写方式挂载(需 NFS 服务器支持,需谨慎配置)
persistentVolumeReclaimPolicy: Retain # 回收策略:保留数据(生产环境推荐,避免误删数据)
storageClassName: slow # 存储类名称(PVC 需指定相同 storageClassName 才能匹配)
# 存储类绑定(若需自动匹配 PVC,可关联 StorageClass,此处仅声明名称)
nfs:
server: 172.31.101.8 # NFS 服务器 IP(集群所有节点需能访问)
path: /opt/nfs-pv-data # NFS 共享目录(需提前创建并授权,避免使用 /tmp 临时目录)
readOnly: false # PV 默认可写(与访问模式匹配)
mountOptions: # NFS 挂载参数(优化稳定性和性能)
- hard # 硬挂载:NFS 不可用时阻塞,避免数据丢失
- nfsvers=4.1 # 指定 NFS 版本(推荐 4.1,兼容性和安全性更好)
- rw # 读写权限(与 accessModes 一致)
- noatime # 禁用文件访问时间更新,提升性能
# 节点亲和性(可选:限制 PV 仅能被特定节点挂载,增强安全性)
# nodeAffinity:
# required:
# nodeSelectorTerms:
# - matchExpressions:
# - key: kubernetes.io/hostname
# operator: In
# values:
# - node-1 # 仅允许 node-1 节点挂载该 PV

存储⼤⼩: 存储⼤⼩是可以设置和请求的唯⼀资源。 未来可能会包含 IOPS、吞吐量等属性。

访问模式:⽤户对资源的访问权限:

ReadWriteOnce(RWO):读写权限,只能被单个节点挂载

ReadOnlyMany(ROX): 只读权限,可以被多个节点挂载

ReadWriteMany(RWX):读写权限,可以被多个节点挂载

存储类别:

每个 PV 可以属于某个类,通过将其 storageClassName属性设置为某个 StorageClass 的名称来指定。

特定类的 PV 卷只能绑定到请求该类存储卷的 PVC 申领。

未设置 storageClassName 的 PV 卷没有类设定,只能给到那些没有指定特定 存储类的 PVC 申领。

回收策略(当PV不再被使⽤了之后的处理策略)

保留 Retain – 当PV对象被删除之后,与之相关的位于外部的基础设施中的数据仍然存在(如nfs),需要根据实际情况⼿动回收

回收 Recycle – 相当于在卷上执⾏rm -rf /volume/* 操作,之后该卷可以⽤于新的pvc申领

删除 Delete – 当PV对象被删除之后,与之相关的位于外部的基础设施中的数据也被⼀并删除(如nfs),需要根据实际情况⼿动回收,更多是云⼚商设备

状态( PV 的⽣命周期有4种不同状态):

Available(可⽤) ——⼀块空闲资源还没有被任何声明绑定

Bound(已绑定) ——卷已经被声明绑定

Released(已释放) ——声明被删除,但是资源还未被集群重新声明

Failed(失败) ——该卷的⾃动回收失败

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: my-pvc # PVC 名称(命名空间级资源)
namespace: dev # 与目标 Pod 同一命名空间
labels:
app: my-pvc # 标签(方便管理和筛选)
spec:
accessModes:
- ReadWriteMany # 访问模式:允许多节点/多 Pod 读写(需 PV 支持该模式)
# 注意:若关联的 PV 仅支持 ReadWriteOnce(RWO),需改为 RWO,否则无法绑定
selector: # 标签选择器:精准匹配目标 PV(避免绑定到无关 PV)
matchLabels:
app: my-pv # 与之前创建的 PV(xdclass-pv)的 labels 一致
storageClassName: slow # 存储类名称:必须与 PV 的 storageClassName 完全一致(否则无法匹配)
resources:
requests:
storage: 3Gi # 申请存储容量(必须 ≤ 目标 PV 的容量 5Gi,否则无法绑定)

NFS+PV和PVC案例

需求:
基于NFS存储,创建2个PV
创建PVC绑定PV
创建Pod挂载PVC

PV和PVC的使⽤:

搭建nfs环境(nfs服务器,基于上⾯的nfs):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#创建⽬录

mkdir /opt/nfsdata/pv1
mkdir /opt/nfsdata/pv2
chmod 777 /opt/nfsdata/pv1
chmod 777 /opt/nfsdata/pv2

#修改配置⽂件
vim /etc/exports
#暴露nfs服务
/opt/nfsdata/pv1
172.31.101.0/24(rw,insecure,sync)
/opt/nfsdata/pv2
172.31.101.0/24(rw,insecure,sync)

systemctl restart nfs

创建两个PV

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
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv1 # PV1 名称(1Gi 容量)
labels:
app: my-pv # 统一标签(方便 PVC 通过标签批量匹配)
storage-tier: small # 容量层级标签(区分不同容量 PV)
spec:
capacity:
storage: 1Gi # 存储容量:1Gi
accessModes:
- ReadWriteMany # RWX 模式:允许多节点/多 Pod 读写(NFS 天然支持)
persistentVolumeReclaimPolicy: Retain # 保留数据(生产环境推荐)
storageClassName: slow # 存储类名称(与 PVC 保持一致,便于批量匹配)
nfs:
server: 172.31.101.8 # NFS 服务器 IP(集群所有节点需可达)
path: /opt/nfsdata/pv1 # PV1 专属 NFS 目录(需提前创建授权)
readOnly: false # 可写权限(与 RWX 模式匹配)
mountOptions: # NFS 挂载优化(提升稳定性和性能)
- hard # 硬挂载:NFS 不可用时阻塞,避免数据丢失
- nfsvers=4.1 # 指定 NFS 4.1 版本(兼容性/安全性更好)
- rw # 读写权限(与 accessModes 一致)
- noatime # 禁用文件访问时间更新,减少 I/O 压力
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: my-pv2 # PV2 名称(2Gi 容量)
labels:
app: my-pv # 统一标签(与 PV1 一致,支持 PVC 批量匹配)
storage-tier: medium # 容量层级标签(区分 1Gi/2Gi)
spec:
capacity:
storage: 2Gi # 存储容量:2Gi
accessModes:
- ReadWriteMany # 与 PV1 保持相同访问模式,便于 PVC 统一申请
persistentVolumeReclaimPolicy: Retain
storageClassName: slow # 统一存储类(与 PV1 一致)
nfs:
server: 172.31.101.8
path: /opt/nfsdata/pv2 # PV2 专属 NFS 目录(与 PV1 隔离,避免数据混淆)
readOnly: false
mountOptions: # 与 PV1 保持一致的挂载参数,统一优化
- hard
- nfsvers=4.1
- rw
- noatime
1
2
# 查看PV
kubectl get pv -o wide

创建PVC,使⽤pvc.yaml (与PV不同, PVC不属于集群资源,拥有⾃⼰的名称空间)

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
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: xdclass-pvc1 # PVC1:申请 1Gi RWX 存储
namespace: dev
spec:
accessModes:
- ReadWriteMany # 与 PV 的访问模式一致(RWX)
storageClassName: slow # 必须与 PV 的 storageClassName 一致(关键匹配条件)
selector: # 精准匹配 1Gi 的 PV1(xdclass-pv1)
matchLabels:
app: xdclass-pv
storage-tier: small # PV1 的容量标签(small=1Gi)
resources:
requests:
storage: 1Gi # 申请容量 = PV1 容量(1Gi),绑定成功率最高
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: xdclass-pvc2 # PVC2:申请 3Gi RWX 存储(注意:现有 PV 无 3Gi 容量)
namespace: dev
spec:
accessModes:
- ReadWriteMany # 与 PV 访问模式一致
storageClassName: slow # 与 PV 存储类一致
# 无 selector:允许 K8s 自动匹配所有符合条件的 PV(但现有 PV 最大 2Gi,会绑定失败)
resources:
requests:
storage: 3Gi # 申请容量 > 现有 PV 最大容量(2Gi),需调整或新增 PV
1
2
3
4
5
6
7
8
9
10
11
# 查看pvc
kubectl apply -f pvc.yaml

kubectl get pvc -n dev

#可以发现⽬my-pvc1已经与my-pv1绑定了,⽽my-pvc2却没有绑定

#查看pv
kubectl get pv

#这是因为pvc2所申请的存储⼤⼩为3G,⽽pv2只能提供2G, 因此⽆法进⾏绑定。

创建pod 挂载pvc

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
apiVersion: v1
kind: Pod
metadata:
name: my-pod1
namespace: dev
labels:
app: my-pod
spec:
containers:
- name: my-busybox
image: busybox:1.35.0 # 指定稳定版本(避免使用 latest 镜像)
command: ["/bin/sh", "-c", "while true; do echo 'hello xdclass pod1' >> /opt/print.txt; sleep 5; done"] # 修正换行+添加引号
volumeMounts:
- name: volume
mountPath: /opt/ # 挂载 PVC 到容器 /opt 目录
readOnly: false # 可写权限(需写入文件,默认 false 可省略)
securityContext:
runAsUser: 0 # 以 root 运行(避免 NFS 目录权限不足)
allowPrivilegeEscalation: false # 禁止权限提升(安全加固)
resources:
limits:
cpu: 100m # 限制 CPU 使用(避免资源占用过高)
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
volumes:
- name: volume
persistentVolumeClaim:
claimName: my-pvc1 # 绑定 1Gi 的 PVC1(对应 PV1)
readOnly: false # 与容器权限一致(可写)
---
apiVersion: v1
kind: Pod
metadata:
name: my-pod2
namespace: dev
labels:
app: my-pod
spec:
containers:
- name: my-busybox
image: busybox:1.35.0 # 统一镜像版本
command: ["/bin/sh", "-c", "while true; do echo 'hello xdclass pod2' >> /opt/print.txt; sleep 5; done"] # 修正换行+添加引号
volumeMounts:
- name: volume
mountPath: /opt/
readOnly: false
securityContext:
runAsUser: 0
allowPrivilegeEscalation: false
resources:
limits:
cpu: 100m
memory: 128Mi
requests:
cpu: 50m
memory: 64Mi
volumes:
- name: volume
persistentVolumeClaim:
claimName: my-pvc2 # 绑定 2Gi 的 PVC2(对应 PV2)
readOnly: false
1
2
# 创建pod完成后,到nfs服务器查看 /opt/nfsdata/pv1/print.txt
tail -f pv1/print.txt

kubernetes入门
http://hanqichuan.com/2025/11/23/devops/kubernetes入门/
作者
韩启川
发布于
2025年11月23日
许可协议