主题
第一部分 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。
| 对比维度 | Docker | Docker Swarm | Kubernetes |
|---|---|---|---|
| 定位 | 容器引擎/运行时 | 轻量级容器编排 | 企业级容器编排平台 |
| 学习曲线 | 低 | 低 | 较高 |
| 集群规模 | 单机 | 中小规模(数十节点) | 大规模(数千节点) |
| 服务发现 | 无(需配合) | 内置(基于 DNS) | 内置(CoreDNS) |
| 负载均衡 | 无 | 内置(Routing Mesh) | Service + Ingress |
| 滚动更新/回滚 | 无 | 支持(功能简单) | 强大且可精细控制 |
| 自动伸缩 | 无 | 仅手动 scale | HPA/VPA 自动伸缩 |
| 存储编排 | Volume 插件 | 基础支持 | PV/PVC/StorageClass 完整体系 |
| 配置与密钥管理 | 无 | 基础 Secret | ConfigMap + 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.key1.4.3 kube-scheduler
- 角色:调度器,负责为新创建的 Pod 选择一个最合适的 Node。
- 调度两阶段:
- 过滤(Filtering):剔除不满足条件的节点(资源不足、端口冲突、污点不容忍等)。
- 打分(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.x1.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 三种本地方案对比
| 维度 | minikube | kind | k3d |
|---|---|---|---|
| 全称/含义 | mini Kubernetes | Kubernetes in Docker | k3s in Docker |
| 底层实现 | VM 或 Docker 容器 | Docker 容器 | Docker 容器(跑 k3s) |
| 启动速度 | 较慢(1~3 分钟) | 快(<1 分钟) | 非常快(<30 秒) |
| 资源占用 | 较高 | 中 | 低 |
| 多节点集群 | 支持 | 支持 | 支持 |
| 附加组件生态 | 极丰富(addons) | 一般 | 一般 |
| 适用场景 | 初学者、功能体验 | CI/CD 测试 | 轻量本地开发 |
| 与生产差异 | 接近标准 K8s | 接近标准 K8s | k3s 为精简发行版 |
本系列教材的生产集群使用 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 demo2.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 demo2.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注意事项
kubectl create deployment仅用于快速实验,正式环境请用 YAML +kubectl apply。- minikube 的 NodePort 需要通过
minikube service或minikube ip访问,因为集群跑在容器/VM 内。- 拉取镜像失败时,检查节点网络与镜像仓库可达性;国内环境可配置镜像加速器。
第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/Svc | kubectl 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 found3.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.log3.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.nodeName3.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/pvc、ing=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 # 切换默认命名空间注意事项
- kubeconfig 包含集群管理员凭证,权限应为
600,严禁提交到 Git 仓库。- 操作生产集群前先
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 / Neverbash
kubectl apply -f pod-demo.yaml
kubectl get pod pod-demo -o wide4.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探针支持三种检查方式:
| 方式 | 说明 | 示例 |
|---|---|---|
httpGet | HTTP 请求,200~399 为成功 | 最常用,适合 Web 服务 |
tcpSocket | TCP 端口可连通即成功 | 适合数据库、MQ 等非 HTTP 服务 |
exec | 容器内执行命令,退出码 0 为成功 | 灵活但有性能开销 |
常用参数:initialDelaySeconds(首次探测延迟)、periodSeconds(探测间隔)、timeoutSeconds(超时)、successThreshold、failureThreshold。
注意事项: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,容器被内核杀掉 | Mi、Gi(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 ← 更新时创建新 RSyaml
# 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: 54.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 的关键差异:
| 特性 | Deployment | StatefulSet |
|---|---|---|
| Pod 名称 | 随机(web-7d9c5f8b6-x2v4k) | 固定有序(mysql-0, mysql-1...) |
| 网络标识 | 随机 | 稳定的 DNS 名(需 headless Service) |
| 存储 | 所有副本共享或无存储 | 每个副本独立 PVC(volumeClaimTemplates) |
| 扩缩容顺序 | 随机 | 扩容按 0,1,2 顺序;缩容逆序 |
| 更新策略 | RollingUpdate/Recreate | RollingUpdate/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 # 稳定 DNS4.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/logbash
kubectl get ds -A # DESIRED 数量 = 节点数量
kubectl get pods -o wide -l app=node-agent # 每个节点一个 Pod4.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: 205.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-config5.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注意事项
- env 注入的 ConfigMap 更新后不会自动生效(环境变量在进程启动时固化);卷挂载方式会自动更新(延迟约 1~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/tls | TLS 证书 | 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: xxx5.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-tlsbash
# 解码查看 Secret 内容(需要相应权限)
kubectl get secret db-secret -o jsonpath='{.data.password}' | base64 -d5.2.3 Secret 安全性说明(重要!)
注意事项
- Base64 是编码不是加密! 任何能
get secret的人都能轻易解码。Secret 的安全性依赖 RBAC 权限控制。- 生产环境应开启 etcd 静态加密(EncryptionConfiguration),让 Secret 在 etcd 中以密文落盘(RKE2 支持通过配置启用,后续章节详述)。
- 不要把含 Secret 的 YAML 提交到 Git 明文仓库;可使用 Sealed Secrets、External Secrets、SOPS 等方案。
- 优先用卷挂载而非环境变量注入 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.combash
kubectl apply -f svc.yaml
kubectl get svc
kubectl get endpoints web # 或 kubectl get endpointslice,查看后端 Pod IP
curl 10.96.10.20 # 集群内任意节点可访问 ClusterIP6.3 DNS 服务发现
集群内的 CoreDNS 会为每个 Service 注册 DNS 记录:
<service-name>.<namespace>.svc.cluster.local
# 同命名空间内可直接用短名
curl http://web
# 跨命名空间
curl http://web.production.svc.cluster.localPod 的 DNS 策略由 spec.dnsPolicy 控制,默认 ClusterFirst(集群内域名走 CoreDNS,外部域名转发上游 DNS)。
bash
# 测试 DNS 解析
kubectl run dnstest --rm -it --image=busybox:1.36 --restart=Never -- nslookup web6.4 Headless Service
设置 clusterIP: None 的 Service 称为 Headless Service:不做负载均衡,DNS 直接返回所有后端 Pod 的 IP 列表。主要用于:
- StatefulSet 的稳定网络标识(见 4.4 节)。
- 客户端自主实现负载均衡/发现(如直接访问数据库主从的特定节点)。
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-svcbash
# minikube 一键启用 ingress-nginx controller
minikube addons enable ingressyaml
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: 80bash
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注意事项
- Ingress 只是"路由规则",必须有 Ingress Controller(ingress-nginx、traefik 等)才真正生效——这是初学者最常见的误区。
pathType: Prefix中/api也会匹配/apis,注意路径设计。- RKE2 默认自带 ingress-nginx controller,生产环境开箱即用。
第7章 存储入门
7.1 Volume:Pod 级存储
容器内文件系统随容器销毁而丢失。Volume 为 Pod 提供持久或共享的存储,生命周期与 Pod 一致。常见类型:
| 类型 | 说明 | 场景 |
|---|---|---|
emptyDir | Pod 创建时生成的空目录,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-pvcbash
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 回收策略
| ReclaimPolicy | PVC 删除后 PV 的行为 |
|---|---|
Retain | PV 保留,数据保留,需管理员手动清理后复用(生产推荐,防误删) |
Delete | PV 与底层存储一并删除(动态供给常用) |
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 # 允许扩容 PVCyaml
# 用户只需创建 PVC,PV 自动出现
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: auto-pvc
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: local-path # 若为默认 SC,可省略此字段
resources:
requests:
storage: 2Gibash
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"}}}}'注意事项
volumeBindingMode: WaitForFirstConsumer可避免"PV 建在 A 节点、Pod 调度到 B 节点"的冲突,本地存储类 SC 强烈建议启用。- 一个集群只能有一个默认 StorageClass;PVC 不指定 SC 时使用默认 SC。
- 生产环境优先使用带
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注意事项
- Namespace 只隔离资源命名与配额,不隔离网络——不同 ns 的 Pod 默认可互通(需 NetworkPolicy 做网络隔离,进阶内容)。
- Node、PV、StorageClass、Namespace 本身是集群级资源,不属于任何 ns。可用
kubectl api-resources --namespaced=false查看。- 删除 Namespace 会级联删除其中所有资源,务必谨慎!
- 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| 对比 | ResourceQuota | LimitRange |
|---|---|---|
| 作用粒度 | 命名空间总量 | 单个 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> -- sh9.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 拼写,容器内无该命令 |
| OOMKilled | describe 中 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 found、manifest unknown | 核对 image 字段 |
| 私有仓库未认证 | unauthorized、pull access denied | 创建 docker-registry Secret 并配置 imagePullSecrets |
| 网络不通 | timeout、connection 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=xxxyaml
spec:
imagePullSecrets:
- name: regcred
containers:
- name: app
image: harbor.example.com/proj/app:v19.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容器名> |
| 节点 NotReady | kubelet 挂了;容器运行时异常;磁盘压力 | 节点上 systemctl status kubelet;df -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 备份恢复与集群升级。