Skip to content

第一部分 Kubernetes 入门与核心概念

本部分是《Kubernetes + RKE2 + K8s 辅助工具:从入门到精通》系列教材的起点,面向有 Linux 运维基础但零 Kubernetes 经验的工程师。我们将从"为什么需要 Kubernetes"讲起,搭建本地实验环境,精通 kubectl,然后逐一攻克 Pod、Deployment、Service、存储、命名空间等核心概念。学完本部分,你将能够独立完成应用的部署、暴露、配置管理与基础排错,为后续 RKE2 生产级集群部署打下坚实基础。本部分所有命令均基于 Kubernetes v1.28+ 版本验证。


第1章 容器与 Kubernetes 概览

1.1 为什么需要 Kubernetes

1.1.1 从物理机到容器的演进

时代部署方式资源利用率启动速度隔离性典型痛点
物理机时代一台服务器跑一个应用低(<15%)分钟~小时级强(整机隔离)资源浪费、扩容慢
虚拟机时代一台物理机虚拟出多台 VM中(30~50%)分钟级强(Hypervisor 隔离)镜像庞大、Guest OS 开销大
容器时代一台主机跑多个容器高(60~80%)秒级中(Namespace/Cgroup)容器编排管理复杂

容器解决了"应用打包与运行环境一致性"问题,但当容器数量从几个增长到几百上千个时,新的问题出现了:

  • 调度问题:这个容器应该跑在哪台主机上?主机资源够不够?
  • 高可用问题:容器挂了谁来重启?主机宕机了上面的容器怎么迁移?
  • 网络问题:跨主机的容器如何通信?服务发现怎么做?
  • 伸缩问题:流量高峰时如何自动扩容?低谷时如何缩容省钱?
  • 发布问题:如何滚动更新而不中断服务?出问题如何快速回滚?

Kubernetes(简称 K8s,因为 K 和 s 之间有 8 个字母)就是为了解决这些**容器编排(Container Orchestration)**问题而生的。

1.1.2 Kubernetes 简史

  • 2014 年:Google 开源 Kubernetes,源自其内部运行了十几年的 Borg 系统。
  • 2015 年:CNCF(云原生计算基金会)成立,Kubernetes 成为其第一个托管项目。
  • 2018 年:Kubernetes 从 CNCF 毕业,成为事实上的容器编排标准。
  • 至今:平均每 4 个月发布一个版本,本教材以 v1.28+ 为基准。

1.2 Kubernetes 与 Docker / Swarm 对比

注意:Docker 和 Kubernetes 不是同一层面的东西。Docker 是容器运行时(Runtime),Kubernetes 是编排平台(Orchestrator),两者是协作关系而非竞争关系。真正与 K8s 竞争的是 Docker Swarm。

对比维度DockerDocker SwarmKubernetes
定位容器引擎/运行时轻量级容器编排企业级容器编排平台
学习曲线较高
集群规模单机中小规模(数十节点)大规模(数千节点)
服务发现无(需配合)内置(基于 DNS)内置(CoreDNS)
负载均衡内置(Routing Mesh)Service + Ingress
滚动更新/回滚支持(功能简单)强大且可精细控制
自动伸缩仅手动 scaleHPA/VPA 自动伸缩
存储编排Volume 插件基础支持PV/PVC/StorageClass 完整体系
配置与密钥管理基础 SecretConfigMap + Secret 完整体系
生态极广(镜像生态)日渐萎缩极其繁荣(CNCF 全景图)
社区活跃度极高

结论:Docker Swarm 适合"Docker 单机用户平滑过渡到小集群",但在生产环境中,Kubernetes 凭借强大的功能与生态已成为绝对主流。本教材以 Kubernetes 为核心。

1.3 Kubernetes 架构总览

一个 Kubernetes 集群由**控制平面(Control Plane)工作节点(Node)**两大部分组成。

┌────────────────────────────────────────────────────────────────────┐
│                        Kubernetes 集群                              │
│                                                                    │
│  ┌────────────────────────── 控制平面 ──────────────────────────┐  │
│  │                                                              │  │
│  │   ┌──────────────┐   ┌──────────────┐   ┌────────────────┐   │  │
│  │   │kube-apiserver│◄─►│     etcd     │   │ kube-scheduler │   │  │
│  │   │  (API 网关)   │   │  (键值存储)   │   │   (调度器)      │   │  │
│  │   └──────┬───────┘   └──────────────┘   └────────────────┘   │  │
│  │          │                                                    │  │
│  │   ┌──────┴───────────────┐   ┌────────────────────────────┐   │  │
│  │   │kube-controller-manager│   │ cloud-controller-manager   │   │  │
│  │   │   (控制器管理器)        │   │   (云控制器管理器)          │   │  │
│  │   └──────────────────────┘   └────────────────────────────┘   │  │
│  └──────────────────────────┬───────────────────────────────────┘  │
│                             │ (API 调用)                            │
│        ┌────────────────────┼────────────────────┐                 │
│        ▼                    ▼                    ▼                 │
│  ┌──────────────┐    ┌──────────────┐    ┌──────────────┐         │
│  │   Node 1     │    │   Node 2     │    │   Node 3     │         │
│  │ ┌──────────┐ │    │ ┌──────────┐ │    │ ┌──────────┐ │         │
│  │ │ kubelet  │ │    │ │ kubelet  │ │    │ │ kubelet  │ │         │
│  │ ├──────────┤ │    │ ├──────────┤ │    │ ├──────────┤ │         │
│  │ │kube-proxy│ │    │ │kube-proxy│ │    │ │kube-proxy│ │         │
│  │ ├──────────┤ │    │ ├──────────┤ │    │ ├──────────┤ │         │
│  │ │ container│ │    │ │ container│ │    │ │ container│ │         │
│  │ │ runtime  │ │    │ │ runtime  │ │    │ │ runtime  │ │         │
│  │ ├──────────┤ │    │ ├──────────┤ │    │ ├──────────┤ │         │
│  │ │ Pod Pod  │ │    │ │ Pod Pod  │ │    │ │ Pod Pod  │ │         │
│  │ └──────────┘ │    │ └──────────┘ │    │ └──────────┘ │         │
│  └──────────────┘    └──────────────┘    └──────────────┘         │
│                                                                    │
│   用户 ──► kubectl ──► kube-apiserver (6443/HTTPS)                 │
└────────────────────────────────────────────────────────────────────┘

1.4 控制平面组件详解

1.4.1 kube-apiserver

  • 角色:整个集群的唯一入口,所有组件(kubectl、kubelet、scheduler 等)都通过它交互。
  • 功能:认证、鉴权、准入控制(Admission)、API 对象的校验与持久化(写入 etcd)。
  • 特点:无状态、可水平扩展(多副本 + 负载均衡),默认监听 6443 端口(HTTPS)。
  • 类比:公司的前台/总机,一切请求都先到它这里。

1.4.2 etcd

  • 角色:分布式一致性键值存储,保存集群的全部状态数据(所有 API 对象)。
  • 特点:基于 Raft 协议,要求奇数个节点(3/5/7)保证仲裁(Quorum)。
  • 运维要点etcd 的备份等于集群的备份,生产环境必须定期做快照。
bash
# etcd 快照示例(在 etcd 节点上执行)
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snap-$(date +%F).db \
  --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key

1.4.3 kube-scheduler

  • 角色:调度器,负责为新创建的 Pod 选择一个最合适的 Node。
  • 调度两阶段
    1. 过滤(Filtering):剔除不满足条件的节点(资源不足、端口冲突、污点不容忍等)。
    2. 打分(Scoring):对剩余节点打分(资源均衡、亲和性等),选得分最高者。
  • 注意:scheduler 只负责"决策",真正启动容器的是 Node 上的 kubelet。

1.4.4 kube-controller-manager

  • 角色:控制器管理器,一个进程内运行了几十个控制器(Controller)。
  • 常见控制器
控制器职责
Deployment Controller管理 Deployment 的滚动更新与副本数
ReplicaSet Controller确保 Pod 副本数符合期望
Node Controller监控节点健康,节点失联时驱逐 Pod
Job/CronJob Controller管理一次性/定时任务
EndpointSlice Controller维护 Service 与 Pod 的映射
ServiceAccount Controller为命名空间创建默认 ServiceAccount
  • 核心思想调谐循环(Reconcile Loop)——不断对比"期望状态(Spec)"与"实际状态(Status)",发现差异就采取行动使其一致。这是 K8s 声明式 API 的灵魂。

1.4.5 cloud-controller-manager

  • 角色:与底层云厂商(AWS/阿里云/腾讯云等)交互的控制器集合。
  • 功能:云负载均衡器(LoadBalancer 类型 Service)、云节点生命周期、云路由等。
  • 注意:在自建(裸金属/虚拟机)集群中通常不部署该组件;RKE2 在裸金属环境同样不需要它。

1.5 节点组件详解

组件职责关键点
kubelet节点代理:注册节点、按 PodSpec 启停容器、上报节点与 Pod 状态、执行探针每个节点必装,直接与容器运行时交互
kube-proxy实现 Service 的负载均衡与转发,维护节点上的网络规则支持 iptables / IPVS / nftables 模式
Container Runtime真正拉取镜像、运行容器的软件containerd、CRI-O 等

1.5.1 CRI(容器运行时接口)

Kubernetes 通过 CRI(Container Runtime Interface) 与容器运行时解耦:

kubelet ──► CRI (gRPC, /run/containerd/containerd.sock) ──► containerd ──► runc ──► 容器

重要历史:K8s v1.24 起正式移除了对 Docker 的直接支持(dockershim 被删除)。现在主流运行时是 containerd(RKE2 默认内置 containerd)。节点上依然有 Docker 不影响使用,但 kubelet 不再通过 Docker 启动容器。

查看集群使用的运行时:

bash
kubectl get nodes -o wide
# 输出中 CONTAINER-RUNTIME 一列显示如 containerd://1.7.x

1.6 声明式 API 与调谐思想

K8s 的使用方式是声明式的:你提交一个 YAML 描述"我想要什么"(期望状态),K8s 的控制器负责"去实现它"并持续维持。

yaml
# 我只声明:要 3 个 nginx 副本。具体怎么调度、挂了怎么拉起,K8s 全权负责。
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 3
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:1.27

最佳实践:生产环境一律使用 YAML 声明式管理(配合 Git 做 GitOps),少用 kubectl create/run 这类命令式操作,保证环境可重现、可审计。


第2章 快速体验环境

在学习阶段,我们需要一个本地可用的 K8s 集群。主流方案有 minikube、kind、k3d 三种。

2.1 三种本地方案对比

维度minikubekindk3d
全称/含义mini KubernetesKubernetes in Dockerk3s in Docker
底层实现VM 或 Docker 容器Docker 容器Docker 容器(跑 k3s)
启动速度较慢(1~3 分钟)快(<1 分钟)非常快(<30 秒)
资源占用较高
多节点集群支持支持支持
附加组件生态极丰富(addons)一般一般
适用场景初学者、功能体验CI/CD 测试轻量本地开发
与生产差异接近标准 K8s接近标准 K8sk3s 为精简发行版

本系列教材的生产集群使用 RKE2(第 2 部分详细讲解)。本地学习推荐 minikube(功能最全、文档最多),资源紧张的机器推荐 k3d

2.2 minikube 安装与使用

bash
# 1. 安装 minikube(Linux x86_64)
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube

# 2. 安装 kubectl(K8s 命令行客户端)
curl -LO "https://dl.k8s.io/release/$(curl -Ls https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install kubectl /usr/local/bin/kubectl

# 3. 启动集群(使用 docker 驱动,需提前安装 Docker)
minikube start --driver=docker --kubernetes-version=v1.30.0

# 4. 验证
kubectl get nodes
# NAME       STATUS   ROLES           AGE   VERSION
# minikube   Ready    control-plane   1m    v1.30.0

# 5. 常用管理命令
minikube status          # 查看状态
minikube stop            # 停止(保留数据)
minikube delete          # 删除集群
minikube dashboard       # 打开 Web 仪表盘
minikube addons list     # 查看可用插件
minikube addons enable metrics-server   # 启用指标服务(kubectl top 依赖它)

2.3 kind 安装与使用

bash
# 1. 安装 kind
curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64
sudo install kind /usr/local/bin/kind

# 2. 创建单节点集群
kind create cluster --name demo

# 3. 创建多节点集群(1 控制平面 + 2 工作节点)
cat <<'EOF' > kind-multi.yaml
kind: Cluster
apiVersion: kind.x-k8s.io/v1alpha4
nodes:
- role: control-plane
- role: worker
- role: worker
EOF
kind create cluster --name multi --config kind-multi.yaml

kubectl get nodes
kind delete cluster --name demo

2.4 k3d 安装与使用

bash
# 1. 安装 k3d
curl -s https://raw.githubusercontent.com/k3d-io/k3d/main/install.sh | bash

# 2. 创建集群(1 server + 2 agent,并映射 8080->80 便于测试 Ingress)
k3d cluster create demo --agents 2 -p "8080:80@loadbalancer"

kubectl get nodes
k3d cluster delete demo

2.5 第一个应用:部署与暴露

以 nginx 为例,走通"部署 → 暴露 → 访问 → 清理"全流程:

bash
# 1. 部署 2 副本并查看状态(等待 STATUS 变为 Running)
kubectl create deployment web --image=nginx:1.27 --replicas=2
kubectl get pods -w

# 2. 暴露为 NodePort 类型 Service 并查看分配的端口
kubectl expose deployment web --port=80 --type=NodePort
kubectl get service web

# 3. 访问(minikube 环境)
minikube service web --url        # 输出如 http://192.168.49.2:31234
curl $(minikube service web --url)
# kind/k3d 环境需端口映射或 port-forward(见第3章)

# 4. 清理
kubectl delete service web && kubectl delete deployment web

注意事项

  1. kubectl create deployment 仅用于快速实验,正式环境请用 YAML + kubectl apply
  2. minikube 的 NodePort 需要通过 minikube serviceminikube ip 访问,因为集群跑在容器/VM 内。
  3. 拉取镜像失败时,检查节点网络与镜像仓库可达性;国内环境可配置镜像加速器。

第3章 kubectl 精通

kubectl 是运维工程师与 K8s 交互的最主要工具,必须达到肌肉记忆级别。

3.1 基本语法

kubectl [command] [TYPE] [NAME] [flags]
         动词      资源类型  资源名   选项

例:kubectl   get     pods   nginx-xxx  -n kube-system -o wide
  • command(动词):get、describe、create、apply、delete、edit、logs、exec...
  • TYPE(资源类型):pods(po)、services(svc)、deployments(deploy)、nodes(no)、namespaces(ns) 等,支持单数/复数/缩写。
  • NAME(资源名):可省略(表示全部);可用 -l 标签选择器代替。
bash
kubectl get pods                          # 当前命名空间所有 Pod
kubectl get pods -n kube-system           # 指定命名空间
kubectl get pods -A                       # 所有命名空间(--all-namespaces)
kubectl get deploy,svc                    # 多种资源一起看
kubectl get pods -l app=nginx             # 按标签过滤
kubectl delete pods --all                 # 删除当前命名空间所有 Pod(慎用!)

3.2 核心命令速查表

命令作用示例
get列出资源kubectl get pods -o wide
describe查看资源详情与事件(排错首选)kubectl describe pod web-xxx
apply声明式创建/更新(推荐)kubectl apply -f app.yaml
create命令式创建(已存在则报错)kubectl create ns dev
edit在线编辑资源kubectl edit deploy web
delete删除资源kubectl delete -f app.yaml
exec进入容器执行命令kubectl exec -it web-xxx -- bash
logs查看容器日志kubectl logs -f web-xxx
port-forward本地端口转发到 Pod/Svckubectl port-forward svc/web 8080:80
cp与容器互传文件kubectl cp ./a.txt web-xxx:/tmp/
top查看资源用量(需 metrics-server)kubectl top pod
api-resources列出所有资源类型kubectl api-resources
api-versions列出所有 API 版本kubectl api-versions
explain查看字段文档(神器)kubectl explain pod.spec.containers
diff对比 YAML 与线上差异kubectl diff -f app.yaml
cordon标记节点不可调度kubectl cordon node1
drain驱逐节点上所有 Pod(维护前)kubectl drain node1 --ignore-daemonsets
uncordon恢复节点可调度kubectl uncordon node1
rollout管理滚动更新kubectl rollout status deploy/web

3.3 常用命令详解

3.3.1 describe:排错第一利器

bash
kubectl describe pod web-7d9c5f8b6-x2v4k
# 重点关注输出末尾的 Events 部分:
# Events:
#   Type     Reason     Age   From     Message
#   ----     ------     ----  ----     -------
#   Warning  Failed     10s   kubelet  Failed to pull image "ngnix": not found

3.3.2 exec 与 logs

bash
# 进入容器(类似 docker exec)
kubectl exec -it web-xxx -- /bin/sh

# 多容器 Pod 需指定 -c 容器名
kubectl exec -it mypod -c sidecar -- bash

# 查看日志
kubectl logs web-xxx                  # 当前日志
kubectl logs -f web-xxx               # 持续跟踪(类似 tail -f)
kubectl logs web-xxx --previous       # 上一次崩溃前的日志(排查 CrashLoopBackOff 必用)
kubectl logs web-xxx --tail=50        # 最后 50 行
kubectl logs -l app=web --all-containers --prefix   # 按标签批量看日志

3.3.3 port-forward 与 cp

bash
# 把本地 8080 转发到 Service 的 80 端口(调试神器,无需 NodePort)
kubectl port-forward svc/web 8080:80 &
curl http://localhost:8080

# 转发到 Pod
kubectl port-forward web-xxx 8080:80

# 文件互传
kubectl cp ./config.txt web-xxx:/etc/config.txt
kubectl cp web-xxx:/var/log/nginx/access.log ./access.log

3.3.4 cordon 与 drain:节点维护

bash
kubectl cordon node1                                  # 禁止新 Pod 调度到 node1
kubectl drain node1 --ignore-daemonsets --delete-emptydir-data  # 驱逐现有 Pod
# ... 执行节点维护(升级内核、加内存等)...
kubectl uncordon node1                                # 恢复调度

注意drain 会驱逐 Pod,有单副本应用的节点 drain 前务必确认业务可容忍中断;--ignore-daemonsets 几乎总是必需的(DaemonSet 的 Pod 无法被驱逐)。

3.4 输出格式

bash
kubectl get pods -o wide          # 额外列:IP、所在节点等
kubectl get pod web-xxx -o yaml   # 完整 YAML(含 status)
kubectl get pod web-xxx -o json   # 完整 JSON
kubectl get pods -o name          # 只输出 资源类型/名称
kubectl get pods --show-labels    # 显示标签
kubectl get pods -L app,version   # 把标签显示为列
kubectl get pods --sort-by=.metadata.creationTimestamp  # 排序

3.4.1 jsonpath:精确提取字段

bash
# 取某个 Pod 的 IP
kubectl get pod web-xxx -o jsonpath='{.status.podIP}'

# 取所有节点的 OS 镜像
kubectl get nodes -o jsonpath='{.items[*].status.nodeInfo.osImage}'

# 取所有 Pod 名和重启次数(表格化)
kubectl get pods -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.status.containerStatuses[0].restartCount}{"\n"}{end}'

3.4.2 go-template 与自定义列

bash
# go-template:复杂格式化
kubectl get pods -o go-template='{{range .items}}{{.metadata.name}} -> {{.spec.nodeName}}{{"\n"}}{{end}}'

# custom-columns:自定义表格列(日常最实用的格式化方式)
kubectl get pods -o custom-columns=NAME:.metadata.name,IP:.status.podIP,NODE:.spec.nodeName

3.5 --dry-run:生成 YAML 模板

bash
# -o yaml --dry-run=client:不真正创建,只输出 YAML(写 YAML 的快捷方式)
kubectl create deployment web --image=nginx:1.27 --replicas=3 \
  --dry-run=client -o yaml > web-deploy.yaml

kubectl create service clusterip web --tcp=80:80 --dry-run=client -o yaml > web-svc.yaml

kubectl create configmap myconf --from-literal=key1=val1 --dry-run=client -o yaml > cm.yaml

最佳实践:不记得 YAML 字段时,用 --dry-run=client -o yaml 生成骨架再修改,比手写快得多。这是 CKA 考试和日常工作的核心技巧。

3.6 explain 与 api-resources

bash
kubectl explain pod                        # Pod 有哪些顶层字段
kubectl explain pod.spec                   # 层层下钻
kubectl explain pod.spec.containers.resources   # 精确到任意字段
kubectl explain deployment.spec.strategy --recursive  # 递归展示全部字段

kubectl api-resources                      # 所有资源类型(含缩写、是否命名空间级)
kubectl api-resources --namespaced=false   # 只看集群级资源(Node/PV/NS...)
kubectl api-resources | grep -i deploy     # 查某个资源
kubectl api-versions | grep apps           # 查 API 版本

3.7 别名与自动补全

bash
# 写入 ~/.bashrc,永久生效
cat <<'EOF' >> ~/.bashrc
source <(kubectl completion bash)
alias k=kubectl
complete -o default -F __start_kubectl k
alias kgp='kubectl get pods'
alias kgs='kubectl get svc'
alias kaf='kubectl apply -f'
alias kdf='kubectl delete -f'
alias kgpw='kubectl get pods -o wide'
alias kexec='kubectl exec -it'
export do='--dry-run=client -o yaml'     # 用法:k create deploy web --image=nginx $do
EOF
source ~/.bashrc

常用缩写:po=pods、svc=services、deploy=deployments、rs=replicasets、sts=statefulsets、ds=daemonsets、cm=configmaps、ns=namespaces、no=nodes、pv/pvcing=ingresses、sa=serviceaccounts。

3.8 kubeconfig 与多集群切换

bash
# 默认读取 ~/.kube/config,也可通过环境变量指定
export KUBECONFIG=~/.kube/config:~/.kube/rke2.yaml   # 多文件合并
kubectl config get-contexts                          # 查看所有上下文
kubectl config use-context rke2-prod                 # 切换集群
kubectl config current-context                       # 查看当前
kubectl config set-context --current --namespace=dev # 切换默认命名空间

注意事项

  1. kubeconfig 包含集群管理员凭证,权限应为 600严禁提交到 Git 仓库
  2. 操作生产集群前先 kubectl config current-context 确认当前指向的集群,避免误操作。

第4章 核心工作负载对象

4.1 Pod:最小调度单元

Pod 是 K8s 中最小的部署与调度单元。一个 Pod 可包含一个或多个共享网络命名空间和存储卷的容器。同一 Pod 内容器通过 localhost 互相通信。

┌─────────────── Pod ───────────────┐
│  ┌────────────┐  ┌─────────────┐  │
│  │ App 容器    │  │ Sidecar 容器 │  │   共享:
│  │ (nginx)    │  │ (日志收集)   │  │   - 网络(同一 IP/端口空间)
│  └────────────┘  └─────────────┘  │   - 存储卷(Volumes)
│         IP: 10.244.1.15           │
└───────────────────────────────────┘

最佳实践:绝大多数情况一个 Pod 只跑一个主容器;只有"强绑定"的辅助进程(日志收集、配置同步、代理)才作为 sidecar 放入同一 Pod。

yaml
# pod-demo.yaml
apiVersion: v1
kind: Pod
metadata:
  name: pod-demo
  labels:
    app: demo
spec:
  containers:
  - name: nginx
    image: nginx:1.27
    ports:
    - containerPort: 80
  restartPolicy: Always      # Always(默认) / OnFailure / Never
bash
kubectl apply -f pod-demo.yaml
kubectl get pod pod-demo -o wide

4.1.1 Pod 生命周期

阶段(Phase)含义
Pending已被 API Server 接受,但尚未调度成功或镜像仍在拉取
Running已绑定到节点,至少一个容器在运行
Succeeded所有容器正常退出(退出码 0),常见于 Job
Failed所有容器终止且至少一个异常退出
Unknown节点失联,无法获取 Pod 状态
创建 → Pending ──调度成功/拉取镜像──► Running ──┬─ 正常退出 ─► Succeeded
       │                                        └─ 异常退出 ─► Failed
       └─ 调度失败:一直 Pending(查 events)

4.1.2 探针(Probe):三种健康检查

探针作用失败后果
livenessProbe容器是否"活着"重启容器(kill 后按 restartPolicy 重建)
readinessProbe容器是否可接收流量从 Service 的 Endpoints 中摘除(不重启)
startupProbe容器是否完成启动启动期间暂停另两个探针,超时则重启。适合慢启动的老应用
yaml
spec:
  containers:
  - name: web
    image: myapp:v1
    startupProbe:                     # 给慢启动应用最长 30*10=300 秒启动时间
      httpGet:
        path: /healthz
        port: 8080
      failureThreshold: 30
      periodSeconds: 10
    livenessProbe:                    # 存活检查:连续 3 次失败则重启
      httpGet:
        path: /healthz
        port: 8080
      periodSeconds: 10
      failureThreshold: 3
    readinessProbe:                   # 就绪检查:成功后才开始接流量
      httpGet:
        path: /ready
        port: 8080
      periodSeconds: 5
      initialDelaySeconds: 3

探针支持三种检查方式:

方式说明示例
httpGetHTTP 请求,200~399 为成功最常用,适合 Web 服务
tcpSocketTCP 端口可连通即成功适合数据库、MQ 等非 HTTP 服务
exec容器内执行命令,退出码 0 为成功灵活但有性能开销

常用参数:initialDelaySeconds(首次探测延迟)、periodSeconds(探测间隔)、timeoutSeconds(超时)、successThresholdfailureThreshold

注意事项:liveness 与 readiness 不要用完全相同的检查。liveness 失败会重启容器,若因下游依赖(数据库)故障导致 liveness 失败,重启毫无意义且引发雪崩;这种场景应只配 readiness。

4.1.3 资源 Requests 与 Limits

yaml
resources:
  requests:          # 调度依据:节点剩余可分配资源须 ≥ requests
    cpu: "250m"      # 250m = 0.25 核
    memory: "128Mi"
  limits:            # 使用上限
    cpu: "500m"      # CPU 超限 → 被限流(throttle),不杀容器
    memory: "256Mi"  # 内存超限 → OOMKilled,容器被杀
资源超 limits 后果单位说明
CPU(可压缩资源)限流(CPU Throttling),容器变慢但不死100m = 0.1 核
内存(不可压缩资源)OOMKilled,容器被内核杀掉MiGi(1024 进制)

4.1.4 QoS 等级

K8s 根据 requests/limits 设置情况给 Pod 划分 QoS 等级,节点资源紧张时按等级顺序驱逐

QoS 等级条件驱逐优先级
Guaranteed每个容器 CPU+内存的 requests = limits(且都设置)最后被杀
Burstable至少一个容器设置了 requests 或 limits,但不满足 Guaranteed中间
BestEffort所有容器都没设置 requests/limits最先被杀
bash
kubectl get pod xxx -o jsonpath='{.status.qosClass}'

最佳实践:核心服务设为 Guaranteed;一般服务至少设置 requests(便于准确调度);永远不要不设任何 requests 就跑生产负载。

4.2 ReplicaSet:副本保持

ReplicaSet(RS)确保任何时候都有指定数量的 Pod 副本在运行。Pod 挂了自动补,节点挂了自动在别的节点重建。

yaml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: web-rs
spec:
  replicas: 3
  selector:
    matchLabels:
      app: web
  template:                    # Pod 模板:RS 用它来"印" Pod
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.27

注意:实际工作中几乎不会直接创建 ReplicaSet,而是通过 Deployment 间接管理。理解 RS 是理解 Deployment 的基础。

4.3 Deployment:无状态应用的首选

Deployment 在 ReplicaSet 之上封装了声明式更新能力,是无状态应用的标准部署方式。

Deployment ──管理──► ReplicaSet(v1) ──管理──► Pod × 3
                 └─► ReplicaSet(v2) ──管理──► Pod × 3   ← 更新时创建新 RS
yaml
# deploy-web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 3
  revisionHistoryLimit: 5        # 保留历史 RS 数量(用于回滚)
  strategy:
    type: RollingUpdate          # RollingUpdate(默认) / Recreate(先全杀再起)
    rollingUpdate:
      maxSurge: 1                # 更新中最多多出 1 个 Pod
      maxUnavailable: 0          # 更新中最多 0 个不可用(保证服务不中断)
  selector:
    matchLabels:
      app: web
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
      - name: nginx
        image: nginx:1.27
        ports:
        - containerPort: 80
        resources:
          requests:
            cpu: 100m
            memory: 64Mi
          limits:
            cpu: 200m
            memory: 128Mi
        readinessProbe:
          httpGet:
            path: /
            port: 80
          periodSeconds: 5

4.3.1 滚动更新

bash
kubectl apply -f deploy-web.yaml

# 方式一:改 YAML 后 apply(推荐,声明式)
# 方式二:命令式改镜像
kubectl set image deployment/web nginx=nginx:1.28

# 观察滚动更新过程
kubectl rollout status deployment/web
kubectl get rs -w          # 可看到旧 RS 缩容、新 RS 扩容的过程

滚动更新流程(replicas=3, maxSurge=1, maxUnavailable=0):

初始: [v1] [v1] [v1]
  1.  新建 1 个 v2 → [v1] [v1] [v1] [v2(未就绪)]
  2.  v2 就绪 → 删 1 个 v1 → [v1] [v1] [v2]
  3.  重复 → [v1] [v2] [v2] → [v2] [v2] [v2]
全程可用 Pod ≥ 3,服务不中断

4.3.2 回滚

bash
kubectl rollout history deployment/web           # 查看历史版本
kubectl rollout history deployment/web --revision=2   # 查看某版详情
kubectl rollout undo deployment/web              # 回滚到上一版本
kubectl rollout undo deployment/web --to-revision=2 # 回滚到指定版本

4.3.3 扩缩容

bash
kubectl scale deployment/web --replicas=5
kubectl autoscale deployment/web --min=2 --max=10 --cpu-percent=70   # HPA(进阶内容)

4.3.4 金丝雀发布(概念)

金丝雀发布(Canary):先让新版本只承载少量流量,验证无误后再全量切换。纯 Deployment 的实现方式:

bash
# 1. 新增一个金丝雀 Deployment(1 副本,标签带 track=canary)
# 2. Service 的 selector 只选 app=web(不含 track),新旧版本同时接流量
#    3 个稳定版 + 1 个金丝雀 = 金丝雀约占 25% 流量
# 3. 验证 OK 后升级主 Deployment,删除金丝雀

进阶提示:生产级金丝雀/蓝绿发布通常借助 Ingress(按权重分流)、Argo Rollouts 或 Service Mesh 实现,后续章节详述。

4.4 StatefulSet:有状态应用

StatefulSet 用于有状态应用(数据库、ZK、Kafka 等),与 Deployment 的关键差异:

特性DeploymentStatefulSet
Pod 名称随机(web-7d9c5f8b6-x2v4k)固定有序(mysql-0, mysql-1...)
网络标识随机稳定的 DNS 名(需 headless Service)
存储所有副本共享或无存储每个副本独立 PVC(volumeClaimTemplates)
扩缩容顺序随机扩容按 0,1,2 顺序;缩容逆序
更新策略RollingUpdate/RecreateRollingUpdate/OnDelete
典型应用Web、API 服务MySQL、Redis 集群、Kafka、ES
yaml
apiVersion: v1
kind: Service
metadata:
  name: nginx-headless
spec:
  clusterIP: None                 # Headless Service:提供稳定 DNS 而非负载均衡
  selector:
    app: nginx-sts
  ports:
  - port: 80
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web-sts
spec:
  serviceName: nginx-headless     # 必须关联 headless Service
  replicas: 3
  selector:
    matchLabels:
      app: nginx-sts
  template:
    metadata:
      labels:
        app: nginx-sts
    spec:
      containers:
      - name: nginx
        image: nginx:1.27
        volumeMounts:
        - name: data
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates:           # 每个 Pod 自动创建独立 PVC
  - metadata:
      name: data
    spec:
      accessModes: ["ReadWriteOnce"]
      resources:
        requests:
          storage: 1Gi

创建后可验证稳定标识:

bash
kubectl get pods -l app=nginx-sts
# web-sts-0   web-sts-1   web-sts-2        ← 名字固定
kubectl get pvc
# data-web-sts-0   data-web-sts-1   data-web-sts-2   ← 每个 Pod 独立存储
kubectl exec web-sts-0 -- nslookup web-sts-0.nginx-headless   # 稳定 DNS

4.5 DaemonSet:每个节点跑一份

DaemonSet 确保每个(符合条件的)节点上运行一个 Pod 副本,节点加入自动部署,节点移除自动回收。典型场景:日志采集(fluentd)、监控 agent(node-exporter)、网络插件(calico)、存储插件。

yaml
apiVersion: apps/v1
kind: DaemonSet
metadata:
  name: node-agent
spec:
  selector:
    matchLabels:
      app: node-agent
  template:
    metadata:
      labels:
        app: node-agent
    spec:
      tolerations:                    # 容忍控制平面污点,使其也能部署
      - key: node-role.kubernetes.io/control-plane
        effect: NoSchedule
      containers:
      - name: agent
        image: fluent/fluent-bit:3.1
        volumeMounts:
        - name: varlog
          mountPath: /var/log
          readOnly: true
      volumes:
      - name: varlog
        hostPath:                     # 挂载宿主机目录
          path: /var/log
bash
kubectl get ds -A          # DESIRED 数量 = 节点数量
kubectl get pods -o wide -l app=node-agent   # 每个节点一个 Pod

4.6 Job 与 CronJob

4.6.1 Job:一次性任务

Job 运行一个或多个 Pod 直到成功完成,适合数据迁移、批量计算、离线任务。

yaml
apiVersion: batch/v1
kind: Job
metadata:
  name: pi-calc
spec:
  completions: 3              # 需要成功完成 3 次
  parallelism: 2              # 最多同时跑 2 个
  backoffLimit: 4             # 失败重试上限(默认 6)
  activeDeadlineSeconds: 300  # 整个 Job 最长运行时间
  ttlSecondsAfterFinished: 100  # 完成后 100 秒自动清理
  template:
    spec:
      restartPolicy: Never    # Job 中只能是 Never 或 OnFailure
      containers:
      - name: pi
        image: perl:5.36
        command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
bash
kubectl apply -f job.yaml
kubectl get jobs
kubectl logs job/pi-calc     # 注意:logs 用 job/<name> 简写形式

4.6.2 CronJob:定时任务

yaml
apiVersion: batch/v1
kind: CronJob
metadata:
  name: backup
spec:
  schedule: "0 2 * * *"        # crontab 语法:每天凌晨 2 点
  concurrencyPolicy: Forbid    # Allow(默认)/Forbid(禁止并发)/Replace(替换)
  successfulJobsHistoryLimit: 3
  failedJobsHistoryLimit: 1
  jobTemplate:
    spec:
      template:
        spec:
          restartPolicy: OnFailure
          containers:
          - name: backup
            image: busybox:1.36
            command: ["sh", "-c", "echo 'backup at' $(date)"]
bash
kubectl get cronjobs
kubectl create job --from=cronjob/backup manual-backup-01   # 手动触发一次执行

4.7 工作负载选型速查

应用特征选择
无状态、可任意替换(Web/API)Deployment
有状态、需稳定身份与独立存储(DB/中间件集群)StatefulSet
每个节点都要跑(日志/监控/网络 agent)DaemonSet
跑完即结束的一次性任务Job
周期性定时任务CronJob

第5章 配置与密钥:ConfigMap 与 Secret

核心思想:配置与镜像分离。同一份镜像,通过注入不同的配置运行于 dev/test/prod 环境。

5.1 ConfigMap

存储非敏感的键值配置(配置文件、环境变量、启动参数)。单个 ConfigMap 上限 1MiB。

5.1.1 创建方式

bash
# 方式一:字面值
kubectl create configmap app-config \
  --from-literal=LOG_LEVEL=debug \
  --from-literal=MAX_CONN=100

# 方式二:从文件
kubectl create configmap nginx-conf --from-file=nginx.conf

# 方式三:从目录
kubectl create configmap confdir --from-file=/etc/myapp/conf.d/

# 方式四:YAML(推荐,可入库管理)
yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: app-config
data:                            # 普通键值
  LOG_LEVEL: "debug"
  application.yaml: |            # 整个文件作为值
    server:
      port: 8080
      thread: 20

5.1.2 使用方式一:环境变量注入

yaml
spec:
  containers:
  - name: app
    image: myapp:v1
    env:
    - name: LOG_LEVEL                    # 单个键注入
      valueFrom:
        configMapKeyRef:
          name: app-config
          key: LOG_LEVEL
    envFrom:                             # 整体注入:CM 中所有键都变成环境变量
    - configMapRef:
        name: app-config

5.1.3 使用方式二:卷挂载(推荐用于配置文件)

yaml
spec:
  containers:
  - name: app
    image: myapp:v1
    volumeMounts:
    - name: config
      mountPath: /etc/myapp          # CM 的每个 key 变成该目录下的一个文件
  volumes:
  - name: config
    configMap:
      name: app-config
      items:                          # 可选:只挂载部分 key,并重命名
      - key: application.yaml
        path: app.yaml

注意事项

  1. env 注入的 ConfigMap 更新后不会自动生效(环境变量在进程启动时固化);卷挂载方式会自动更新(延迟约 1~2 分钟),但应用需支持热加载或重启进程。
  2. 修改 ConfigMap 后,最稳妥的做法是 kubectl rollout restart deployment/xxx 触发滚动重启。

5.2 Secret

存储敏感数据(密码、Token、TLS 证书)。与 ConfigMap 用法几乎一致,区别在于数据以 Base64 编码存储。

5.2.1 Secret 类型

类型用途创建示例
Opaque(默认)通用任意键值`kubectl create secret generic db-pass --from-literal=password=xxx
kubernetes.io/tlsTLS 证书kubectl create secret tls my-tls --cert=tls.crt --key=tls.key
kubernetes.io/dockerconfigjson私有镜像仓库凭证`kubectl create secret docker-registry regcred --docker-server=harbor.local --docker-username=xxx --docker-password=xxx
kubernetes.io/basic-auth基本认证`kubectl create secret generic auth --type=kubernetes.io/basic-auth --from-literal=username=xxx --from-literal=password=xxx
yaml
# YAML 形式(注意 data 中必须是 Base64 编码值)
apiVersion: v1
kind: Secret
metadata:
  name: db-secret
type: Opaque
data:
  password: xxx          # echo -n 'S3cret!' | base64
---
# 或者用 stringData 写明文(提交时由 API Server 自动转 Base64)
apiVersion: v1
kind: Secret
metadata:
  name: db-secret2
type: Opaque
stringData:
  password: xxx

5.2.2 使用 Secret

yaml
spec:
  containers:
  - name: app
    image: myapp:v1
    env:
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-secret
          key: password
    volumeMounts:
    - name: tls
      mountPath: /etc/tls
      readOnly: true
  volumes:
  - name: tls
    secret:
      secretName: my-tls
bash
# 解码查看 Secret 内容(需要相应权限)
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d

5.2.3 Secret 安全性说明(重要!)

注意事项

  1. Base64 是编码不是加密! 任何能 get secret 的人都能轻易解码。Secret 的安全性依赖 RBAC 权限控制。
  2. 生产环境应开启 etcd 静态加密(EncryptionConfiguration),让 Secret 在 etcd 中以密文落盘(RKE2 支持通过配置启用,后续章节详述)。
  3. 不要把含 Secret 的 YAML 提交到 Git 明文仓库;可使用 Sealed Secrets、External Secrets、SOPS 等方案。
  4. 优先用卷挂载而非环境变量注入 Secret(环境变量易被 proc、日志、crash dump 泄露)。

5.3 SubPath:挂载卷中的单个文件

直接挂载卷会覆盖整个目标目录。若只想替换目录中的单个文件,使用 subPath

yaml
volumeMounts:
- name: config
  mountPath: /etc/nginx/nginx.conf    # 只替换这一个文件,而不是整个 /etc/nginx
  subPath: nginx.conf                 # 卷中的文件路径
volumes:
- name: config
  configMap:
    name: nginx-conf

注意:使用 subPath 挂载的 ConfigMap/Secret 更新后不会自动同步到容器,需重启 Pod。

5.4 Downward API:把 Pod 自身信息注入容器

应用有时需要知道自己的 Pod 名、命名空间、所在节点、标签等元数据,Downward API 可以注入这些信息:

yaml
spec:
  containers:
  - name: app
    image: myapp:v1
    env:
    - name: POD_NAME
      valueFrom:
        fieldRef:
          fieldPath: metadata.name
    - name: POD_NAMESPACE
      valueFrom:
        fieldRef:
          fieldPath: metadata.namespace
    - name: NODE_NAME
      valueFrom:
        fieldRef:
          fieldPath: spec.nodeName
    - name: CPU_LIMIT
      valueFrom:
        resourceFieldRef:
          containerName: app
          resource: limits.cpu
    volumeMounts:
    - name: podinfo
      mountPath: /etc/podinfo
  volumes:
  - name: podinfo
    downwardAPI:
      items:
      - path: "labels"
        fieldRef:
          fieldPath: metadata.labels

第6章 Service 与网络入门

6.1 为什么需要 Service

Pod 是** ephemeral( ephemeral 易逝的):重建后 IP 会变。Deployment 的多个副本之间也需要负载均衡。Service 提供稳定的虚拟 IP(ClusterIP)和 DNS 名称**,把流量转发到后端一组健康的 Pod。

                ┌────────────────────────────┐
   客户端 ──►  │ Service: web (10.96.10.20:80) │
                └──────────────┬─────────────┘
                  selector: app=web
        ┌──────────────┬───────┴──────┐
        ▼              ▼              ▼
   Pod 10.244.1.5  Pod 10.244.2.7  Pod 10.244.1.9   (由 kube-proxy 负载均衡)

6.2 Service 四种类型对比

类型访问范围分配的资源典型场景
ClusterIP(默认)仅集群内部虚拟 IP内部服务间通信(前端调后端)
NodePort集群外部(通过任一节点 IP:端口)ClusterIP + 每节点高端口(30000-32767)开发测试、配合外部 LB
LoadBalancer集群外部(云厂商 LB 分配公网 IP)ClusterIP + NodePort + 外部 LB生产对外服务(云上)
ExternalName返回 CNAME 记录无 IP,仅 DNS 别名引用集群外部服务

四者关系:LoadBalancer ⊃ NodePort ⊃ ClusterIP(后者包含前者的能力)。

yaml
# ClusterIP 示例
apiVersion: v1
kind: Service
metadata:
  name: web
spec:
  type: ClusterIP
  selector:
    app: web              # 选中带此标签的 Pod 作为后端
  ports:
  - port: 80              # Service 自身端口
    targetPort: 8080      # 容器实际监听端口
---
# NodePort 示例
apiVersion: v1
kind: Service
metadata:
  name: web-np
spec:
  type: NodePort
  selector:
    app: web
  ports:
  - port: 80
    targetPort: 8080
    nodePort: 30080       # 可选,不指定则自动分配
---
# ExternalName 示例:把外部数据库映射为集群内 DNS 名
apiVersion: v1
kind: Service
metadata:
  name: external-db
spec:
  type: ExternalName
  externalName: mysql.example.com
bash
kubectl apply -f svc.yaml
kubectl get svc
kubectl get endpoints web       # 或 kubectl get endpointslice,查看后端 Pod IP
curl 10.96.10.20                # 集群内任意节点可访问 ClusterIP

6.3 DNS 服务发现

集群内的 CoreDNS 会为每个 Service 注册 DNS 记录:

<service-name>.<namespace>.svc.cluster.local

# 同命名空间内可直接用短名
curl http://web
# 跨命名空间
curl http://web.production.svc.cluster.local

Pod 的 DNS 策略由 spec.dnsPolicy 控制,默认 ClusterFirst(集群内域名走 CoreDNS,外部域名转发上游 DNS)。

bash
# 测试 DNS 解析
kubectl run dnstest --rm -it --image=busybox:1.36 --restart=Never -- nslookup web

6.4 Headless Service

设置 clusterIP: None 的 Service 称为 Headless Service:不做负载均衡,DNS 直接返回所有后端 Pod 的 IP 列表。主要用于:

  1. StatefulSet 的稳定网络标识(见 4.4 节)。
  2. 客户端自主实现负载均衡/发现(如直接访问数据库主从的特定节点)。

6.5 Ingress 快速上手

NodePort 端口不友好,LoadBalancer 每个服务一个 LB 成本高。Ingress 是七层(HTTP/HTTPS)入口:一个入口点,按域名/路径路由到不同 Service。

                     ┌────────────────────────────────┐
  用户 ──域名──►    │        Ingress Controller       │
 (foo.com/api)      │   (如 ingress-nginx, 80/443)     │
                    └───┬───────────────┬─────────────┘
              /api 路由 │               │ /web 路由
                        ▼               ▼
                  Svc: api-svc     Svc: web-svc
bash
# minikube 一键启用 ingress-nginx controller
minikube addons enable ingress
yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: demo-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  ingressClassName: nginx
  rules:
  - host: demo.local
    http:
      paths:
      - path: /api
        pathType: Prefix        # Prefix(前缀) / Exact(精确) / ImplementationSpecific
        backend:
          service:
            name: api-svc
            port:
              number: 80
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web-svc
            port:
              number: 80
bash
kubectl apply -f ingress.yaml
kubectl get ingress                  # 查看分配的 ADDRESS
# 本地测试:把 demo.local 解析到 ingress 地址
echo "$(minikube ip) demo.local" | sudo tee -a /etc/hosts
curl http://demo.local/api

注意事项

  1. Ingress 只是"路由规则",必须有 Ingress Controller(ingress-nginx、traefik 等)才真正生效——这是初学者最常见的误区。
  2. pathType: Prefix/api 也会匹配 /apis,注意路径设计。
  3. RKE2 默认自带 ingress-nginx controller,生产环境开箱即用。

第7章 存储入门

7.1 Volume:Pod 级存储

容器内文件系统随容器销毁而丢失。Volume 为 Pod 提供持久或共享的存储,生命周期与 Pod 一致。常见类型:

类型说明场景
emptyDirPod 创建时生成的空目录,Pod 删除即清空临时缓存、同 Pod 容器间共享文件
hostPath挂载宿主机目录节点级 agent(日志/监控),慎用
configMap / secret注入配置与密钥见第 5 章
nfs挂载 NFS 共享多节点共享数据
persistentVolumeClaim对接 PV/PVC 体系生产持久化存储(推荐)
yaml
spec:
  containers:
  - name: app
    image: nginx:1.27
    volumeMounts:
    - name: cache
      mountPath: /cache
    - name: data
      mountPath: /data
  volumes:
  - name: cache
    emptyDir: {}               # Pod 删了数据就没了
  - name: data
    nfs:
      server: 192.168.1.10
      path: /exports/data

注意hostPath 会把 Pod 绑定到特定节点的文件系统,Pod 漂移后数据不一致,且有安全风险(可挂载宿主机敏感目录),生产环境仅 DaemonSet 类 agent 使用。

7.2 PV / PVC:存储的供需解耦

emptyDir/hostPath 把存储细节写死在 Pod 里。PV/PVC 体系把"存储供给"与"存储消费"分开:

  • PV(PersistentVolume):集群级资源,由管理员创建,描述一块真实存储(NFS/iSCSI/云盘)。
  • PVC(PersistentVolumeClaim):命名空间级资源,由用户创建,描述"我需要多大的存储、什么访问模式",由 K8s 自动匹配绑定合适的 PV。
管理员: 创建 PV(10Gi, NFS)  ◄──── 绑定(Bound) ────  PVC(要 5Gi)  ──► Pod 挂载
         供给方                                        消费方
yaml
# 1. 管理员创建 PV
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-nfs-01
spec:
  capacity:
    storage: 10Gi
  accessModes:
  - ReadWriteMany            # RWO(单节点读写) / ROX(多节点只读) / RWX(多节点读写)
  persistentVolumeReclaimPolicy: Retain   # Retain(保留) / Delete(删除) / Recycle(已废弃)
  storageClassName: manual
  nfs:
    server: 192.168.1.10
    path: /exports/pv01
---
# 2. 用户创建 PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-pvc
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: manual
  resources:
    requests:
      storage: 5Gi
---
# 3. Pod 使用 PVC
apiVersion: v1
kind: Pod
metadata:
  name: app-with-storage
spec:
  containers:
  - name: app
    image: nginx:1.27
    volumeMounts:
    - name: data
      mountPath: /usr/share/nginx/html
  volumes:
  - name: data
    persistentVolumeClaim:
      claimName: data-pvc
bash
kubectl get pv                 # STATUS: Available → Bound
kubectl get pvc                # STATUS: Bound 表示绑定成功

7.2.1 PVC 绑定的匹配规则

PVC 按以下条件筛选 PV:accessModes 匹配、PV 容量 ≥ PVC 请求、storageClassName 一致。若无可用 PV,PVC 一直处于 Pending,使用它的 Pod 也会 Pending(常见排错点!)。

7.2.2 回收策略

ReclaimPolicyPVC 删除后 PV 的行为
RetainPV 保留,数据保留,需管理员手动清理后复用(生产推荐,防误删
DeletePV 与底层存储一并删除(动态供给常用)

7.3 StorageClass:动态供给

手动创建 PV 太繁琐。StorageClass 定义"存储的类别与供给插件(Provisioner)",用户创建 PVC 时自动创建对应 PV,即动态供给(Dynamic Provisioning)。

yaml
# 以 rancher local-path(k3s/RKE2 生态常用)为例
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-path
  annotations:
    storageclass.kubernetes.io/is-default-class: "true"   # 设为默认 SC
provisioner: rancher.io/local-path
volumeBindingMode: WaitForFirstConsumer    # 延迟绑定:等 Pod 调度后再建 PV
reclaimPolicy: Delete
allowVolumeExpansion: true                 # 允许扩容 PVC
yaml
# 用户只需创建 PVC,PV 自动出现
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: auto-pvc
spec:
  accessModes: ["ReadWriteOnce"]
  storageClassName: local-path      # 若为默认 SC,可省略此字段
  resources:
    requests:
      storage: 2Gi
bash
kubectl get sc                         # 查看 StorageClass(default 标记)
kubectl get pvc auto-pvc               # 动态供给后变 Bound
kubectl get pv                         # 自动创建的 PV
# PVC 扩容(SC 需 allowVolumeExpansion: true)
kubectl patch pvc auto-pvc -p '{"spec":{"resources":{"requests":{"storage":"5Gi"}}}}'

注意事项

  1. volumeBindingMode: WaitForFirstConsumer 可避免"PV 建在 A 节点、Pod 调度到 B 节点"的冲突,本地存储类 SC 强烈建议启用。
  2. 一个集群只能有一个默认 StorageClass;PVC 不指定 SC 时使用默认 SC。
  3. 生产环境优先使用带 Delete 回收策略 + 定期备份的方案,核心数据 SC 可改用 Retain

第8章 命名空间与资源配额

8.1 Namespace:集群内的逻辑隔离

Namespace 把集群划分为多个逻辑分区,用于多团队/多环境共享集群时的资源隔离与权限边界。

bash
kubectl get namespaces
# NAME              STATUS
# default           Active     ← 用户资源默认放这里
# kube-system       Active     ← K8s 系统组件
# kube-public       Active     ← 公开可读数据
# kube-node-lease   Active     ← 节点心跳租约

kubectl create namespace dev
kubectl config set-context --current --namespace=dev   # 切换默认 ns
kubectl get pods -n kube-system

注意事项

  1. Namespace 只隔离资源命名与配额,不隔离网络——不同 ns 的 Pod 默认可互通(需 NetworkPolicy 做网络隔离,进阶内容)。
  2. Node、PV、StorageClass、Namespace 本身是集群级资源,不属于任何 ns。可用 kubectl api-resources --namespaced=false 查看。
  3. 删除 Namespace 会级联删除其中所有资源,务必谨慎!
  4. Service 的 DNS 名包含 ns:svc-name.ns-name.svc.cluster.local

8.2 ResourceQuota:命名空间资源配额

限制一个 Namespace 可消耗的资源总量,防止某个团队耗尽集群资源。

yaml
apiVersion: v1
kind: ResourceQuota
metadata:
  name: dev-quota
  namespace: dev
spec:
  hard:
    requests.cpu: "4"              # 该 ns 所有 Pod 的 CPU requests 总和上限
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
    pods: "20"                     # 对象数量配额
    services: "10"
    services.nodeports: "3"
    persistentvolumeclaims: "5"
bash
kubectl apply -f quota.yaml
kubectl describe resourcequota dev-quota -n dev    # 查看已用/上限

重要:命名空间配置了 requests/limits 配额后,该 ns 中每个 Pod 都必须显式设置 requests/limits,否则创建会被拒绝。解决办法是配合下面的 LimitRange 设置默认值。

8.3 LimitRange:单对象约束与默认值

LimitRange 为命名空间中的单个 Pod/容器设置资源上下限和默认 requests/limits

yaml
apiVersion: v1
kind: LimitRange
metadata:
  name: dev-limits
  namespace: dev
spec:
  limits:
  - type: Container
    default:                    # 默认值(容器没写 limits 时自动补上)
      cpu: 500m
      memory: 512Mi
    defaultRequest:             # 默认 requests
      cpu: 100m
      memory: 128Mi
    max:                        # 单容器上限
      cpu: "2"
      memory: 2Gi
    min:                        # 单容器下限
      cpu: 50m
      memory: 64Mi
对比ResourceQuotaLimitRange
作用粒度命名空间总量单个 Pod/容器
常见用途防团队间资源挤占防单个应用失控、提供默认值
配合使用需要每 Pod 有 requests/limits自动补默认值,正好满足 Quota 要求

第9章 常见面试题与排错入门

9.1 排错通用套路(黄金四步)

bash
# 第 1 步:看状态,定位异常对象
kubectl get pods -A | grep -v Running
kubectl get events -n <ns> --sort-by=.lastTimestamp | tail -20

# 第 2 步:describe 看详情与事件(最重要的一步!)
kubectl describe pod <pod-name> -n <ns>

# 第 3 步:看日志(包括崩溃前的日志)
kubectl logs <pod-name> -n <ns> --previous
kubectl logs <pod-name> -n <ns> -c <container>

# 第 4 步:进容器实地验证
kubectl exec -it <pod-name> -n <ns> -- sh

9.2 Pod Pending 排查

Pod 卡在 Pending,说明还没被调度或没开始拉镜像,此时容器根本不存在,logs/exec 都无效。

bash
kubectl describe pod <name>   # 看 Events 中的 FailedScheduling
常见原因Events 典型信息解决方案
资源不足Insufficient cpu/memory降 requests、扩容节点、或清理闲置 Pod
污点/亲和性node(s) had taint... didn't tolerate加 toleration、修正亲和性、或去掉节点污点
PVC 未绑定pod has unbound immediate PersistentVolumeClaims检查 PV/StorageClass 是否可用(kubectl get pvc,pv,sc
端口冲突didn't match Pod's node affinity/selector检查 nodeSelector 是否有匹配节点
镜像拉取中Pulling image...正常等待;长时间卡住按 9.4 处理

9.3 CrashLoopBackOff 排查

容器反复启动→崩溃→重启,K8s 以指数退避间隔重试(10s→20s→40s→上限 5min)。

bash
kubectl logs <pod> --previous       # 看上一次崩溃前的日志(关键!)
kubectl describe pod <pod>          # 看退出码与原因
常见原因排查要点解决方案
应用本身报错日志中有 exception/错误栈修应用 bug、检查依赖服务(DB 是否可达)
配置错误日志提示配置缺失/格式错误检查 ConfigMap/Secret 内容与挂载路径
命令/参数错误container init caused...检查 command/args 拼写,容器内无该命令
OOMKilleddescribe 中 Reason: OOMKilled,退出码 137调大 memory limit 或排查内存泄漏
liveness 探针误杀日志正常但反复重启检查探针路径/端口/initialDelaySeconds
权限问题permission denied检查挂载卷权限、securityContext

退出码速查0=正常退出;1=应用错误;126=命令不可执行;127=命令不存在;137=SIGKILL(常为 OOM);143=SIGTERM(正常终止信号)。

9.4 ImagePullBackOff 排查

镜像拉取失败,K8s 退避重试。

bash
kubectl describe pod <pod>   # 看 Failed to pull image 的具体原因
常见原因典型报错解决方案
镜像名/标签拼错not foundmanifest unknown核对 image 字段
私有仓库未认证unauthorizedpull access denied创建 docker-registry Secret 并配置 imagePullSecrets
网络不通timeoutconnection refused检查节点到外网/镜像仓库的网络与代理
仓库证书问题x509: certificate配置 containerd 的 insecure registry 或 CA
镜像架构不符no matching manifest for linux/arm64使用对应架构的镜像

配置私有仓库凭证:

bash
kubectl create secret docker-registry regcred \
  --docker-server=harbor.example.com \
  --docker-username=xxx --docker-password=xxx
yaml
spec:
  imagePullSecrets:
  - name: regcred
  containers:
  - name: app
    image: harbor.example.com/proj/app:v1

9.5 其他高频故障速查

现象可能原因快速定位
Service 访问不通selector 与 Pod 标签不匹配;Pod 未就绪;targetPort 错kubectl get ep <svc> 看 Endpoints 是否为空
DNS 解析失败CoreDNS 异常;dnsPolicy 错误kubectl get pods -n kube-system -l k8s-app=kube-dns
Pod 一直处于 Terminating节点失联;有 finalizer;存储卸载卡死kubectl get pod -o yaml 查 finalizers;查节点状态
initContainer 卡住init 命令未成功完成kubectl logs <pod> -c <init容器名>
节点 NotReadykubelet 挂了;容器运行时异常;磁盘压力节点上 systemctl status kubeletdf -h

9.6 常见面试题

Q1:Pod 里的容器如何通信?为什么不直接用 Docker 容器?

同一 Pod 内容器共享网络命名空间(通过 pause 容器实现),用 localhost + 不同端口通信。Pod 抽象让 K8s 能统一管理一组强关联容器的网络、存储与生命周期,并解耦具体容器运行时。

Q2:Deployment、ReplicaSet、Pod 三者的关系?

Deployment 管理 ReplicaSet(声明式更新、滚动/回滚),ReplicaSet 管理 Pod 副本数。更新 Deployment 时创建新 RS 并逐步替换旧 RS 的 Pod,旧 RS 保留用于回滚。

Q3:Service 的四种类型及适用场景?

ClusterIP(集群内访问,默认);NodePort(节点高端口对外,测试/配外部 LB);LoadBalancer(云 LB 对外,生产);ExternalName(DNS 别名指向外部服务)。进阶:Headless Service 用于 StatefulSet 与客户端自发现。

Q4:liveness 和 readiness 探针的区别?配错会怎样?

liveness 失败→重启容器;readiness 失败→从 Service Endpoints 摘除流量。若把外部依赖故障也放进 liveness,会造成大面积无意义重启(雪崩);不配 readiness 则未就绪的 Pod 会收到流量导致请求失败。

Q5:Pod 一直 Pending 怎么排查?

kubectl describe pod 看 Events:资源不足(Insufficient cpu/memory)→ 扩节点或降 requests;污点不容忍 → 加 toleration;PVC 未绑定 → 查 PV/StorageClass。

Q6:Secret 是加密的吗?生产环境如何加强?

默认只是 Base64 编码,不是加密。生产措施:开启 etcd 静态加密(EncryptionConfiguration)、严格 RBAC 限制、优先卷挂载而非环境变量、配合 Sealed Secrets/External Secrets 管理。

Q7:滚动更新中 maxSurge 和 maxUnavailable 的作用?如何做到零停机?

maxSurge 控制更新中可超出期望副本的数量,maxUnavailable 控制允许不可用的数量。零停机配置:maxUnavailable: 0 + readiness 探针 + 足够的副本数,并确保应用能优雅处理 SIGTERM。

Q8:PV、PVC、StorageClass 的关系?

PV 是管理员供给的真实存储;PVC 是用户的存储申请,自动匹配合适 PV 绑定;StorageClass 定义动态供给模板,创建 PVC 时自动创建 PV,无需人工预建。

Q9:节点需要维护升级时该怎么做?

kubectl cordon <node>(禁止调度)→ kubectl drain <node> --ignore-daemonsets --delete-emptydir-data(驱逐 Pod,自动在其他节点重建)→ 维护 → kubectl uncordon <node> 恢复。

Q10:requests 和 limits 的区别?CPU 超限与内存超限的后果分别是什么?

requests 是调度依据(保证值),limits 是使用上限。CPU 超限被限流(throttle)不杀容器;内存超限触发 OOMKilled 直接杀容器。据此 K8s 把 Pod 分为 Guaranteed/Burstable/BestEffort 三级 QoS,节点资源紧张时按此顺序驱逐。


本部分小结

章节核心收获
第1章K8s 解决容器编排问题;架构 = 控制平面(apiserver/etcd/scheduler/controller-manager)+ 节点(kubelet/kube-proxy/runtime)
第2章minikube/kind/k3d 搭建本地环境,完成第一个应用的部署与暴露
第3章kubectl 语法、15+ 核心命令、输出格式、--dry-run、别名补全
第4章Pod 生命周期/探针/资源/QoS;Deployment/StatefulSet/DaemonSet/Job 选型
第5章ConfigMap 管配置、Secret 管密钥;subPath 与 Downward API
第6章Service 四类型、DNS 发现、Ingress 七层路由
第7章Volume → PV/PVC → StorageClass 动态供给
第8章Namespace 隔离 + ResourceQuota + LimitRange 配额体系
第9章排错黄金四步 + Pending/CrashLoop/ImagePull 三大故障套路 + 高频面试题

下一部分预告:第二部分将进入生产级主题——使用 RKE2 部署高可用 Kubernetes 集群,涵盖 RKE2 架构、server/agent 节点部署、证书管理、etcd 备份恢复与集群升级。