初级运维 / 云原生岗位 — 面试常问题目集合(中)容器篇
·
初级运维 / 云原生岗位 — 面试常问题目集合(中)容器篇
本册覆盖:Docker · Kubernetes
适用岗位:初级运维工程师、云原生运维、Kubernetes 运维、DevOps 初级工程师
四、Docker 容器
4.1 核心概念
- 什么是容器?容器和虚拟机的区别?
# 容器:操作系统级虚拟化,共享宿主机内核,通过 Namespace 隔离 + Cgroups 限制资源
# 虚拟机:硬件级虚拟化,每个 VM 有独立 Guest OS 内核,通过 Hypervisor 模拟硬件
#
# 核心区别:
# 1. 启动速度:容器秒级,VM 分钟级
# 2. 资源开销:容器 MB 级(共享内核),VM GB 级(独立 OS)
# 3. 隔离性:VM 强隔离(独立内核),容器进程级隔离(共享内核)
# 4. 移植性:容器镜像可跨环境运行,VM 依赖 Hypervisor 类型
# 5. 密度:同配置宿主机可运行更多容器(无 Guest OS 开销)
- Docker 的三大核心概念:镜像、容器、仓库分别是什么?
# 镜像(Image):只读模板,包含运行应用所需的文件系统、依赖、配置等,采用分层构建
# 容器(Container):镜像的运行实例,在镜像层上加一层可写层,拥有独立的进程/网络/文件系统
# 仓库(Registry):存储和分发镜像的服务,如 Docker Hub、Harbor;分为公开仓库和私有仓库
#
# 三者关系:docker build 构建镜像 -> docker run 从镜像启动容器 -> docker push/pull 与仓库交互
- 联合文件系统(UnionFS)是什么?镜像分层有什么好处?
# UnionFS:将多个目录/文件系统挂载到同一挂载点,呈现为单一文件系统的技术
# Docker 使用 Overlay2 等存储驱动实现镜像分层,每层对应 Dockerfile 中的一条指令
#
# 分层好处:
# 1. 复用共享层:多个镜像共享同一基础层,节省磁盘空间
# 2. 加速构建:未变更的层直接使用缓存,只重建变更层
# 3. 快速分发:pull 时只下载本地缺失的层,减少网络传输
# 4. 版本管理:每层有唯一 digest,便于追溯和回滚
- Namespace 和 Cgroups 分别在容器中起什么作用?
# Namespace(命名空间):实现资源隔离,让容器拥有"独立视角"
# PID ns - 进程隔离,容器内 PID 1 是容器内首个进程
# NET ns - 网络隔离,容器有独立的网卡、IP、端口空间
# MNT ns - 文件系统挂载点隔离,容器看到独立的根文件系统
# UTS ns - 主机名和域名隔离
# IPC ns - 进程间通信隔离(信号量、消息队列等)
# USER ns - 用户和用户组隔离
#
# Cgroups(控制组):实现资源限制与监控,防止单个容器耗尽宿主机资源
# CPU - 限制 CPU 使用率(--cpus)
# 内存 - 限制内存使用(-m/--memory),超出触发 OOM
# 磁盘 I/O - 限制块设备读写速率(--blkio-weight)
# 进程数 - 限制容器内最大进程数(--pids-limit)
4.2 常用操作
docker run常用参数有哪些?-d、-p、-v、-e、--name、--rm分别做什么?
# -d (--detach) 后台运行容器,打印容器 ID 后立即返回
# -p (--publish) 端口映射:主机端口:容器端口,如 -p 8080:80
# -v (--volume) 挂载数据卷:主机路径:容器路径 或 卷名:容器路径
# -e (--env) 设置环境变量:-e MYSQL_ROOT_PASSWORD=123456
# --name 给容器命名,便于后续引用(否则随机生成名称)
# --rm 容器退出后自动删除容器(适合一次性任务)
#
# 其他常用参数:
# -it 交互式终端(-i 保持 STDIN 打开,-t 分配伪终端)
# --restart=always 容器退出时自动重启
# --network 指定容器网络
# --cpus / -m 限制 CPU 核数 / 内存大小
- 如何查看容器日志?如何进入正在运行的容器?
# ========== 查看容器日志 ==========
docker logs <容器名/ID> # 查看全部日志
docker logs -f <容器名/ID> # 实时跟踪日志(类似 tail -f)
docker logs --tail 100 <容器名/ID> # 查看最近 100 行
docker logs --since 30m <容器名/ID> # 查看最近 30 分钟的日志
docker logs -f --tail 50 <容器名/ID> # 查看最近 50 行并持续跟踪
# ========== 进入正在运行的容器 ==========
docker exec -it <容器名/ID> /bin/bash # 进入容器启动 bash(推荐)
docker exec -it <容器名/ID> /bin/sh # Alpine 等精简镜像用 sh
docker attach <容器名/ID> # 附着到容器主进程(不推荐,exit 会停止容器)
docker exec <容器名/ID> <命令> # 在容器内执行单条命令,如 ls /etc
ENTRYPOINT和CMD的区别?
# CMD:指定容器启动时的默认命令/参数,可被 docker run 后的命令完全覆盖
# CMD ["executable","param1","param2"] (exec 形式,推荐)
# CMD command param1 param2 (shell 形式,以 /bin/sh -c 启动)
#
# ENTRYPOINT:指定容器的固定入口,docker run 后的命令作为参数追加给 ENTRYPOINT
# ENTRYPOINT ["executable","param1"] (exec 形式,推荐)
# ENTRYPOINT command param1 (shell 形式)
#
# 核心区别:
# 同时存在时,CMD 的内容作为 ENTRYPOINT 的默认参数
# docker run <img> 新命令 -- 覆盖 CMD,但不覆盖 ENTRYPOINT(除非用 --entrypoint)
#
# 典型组合:ENTRYPOINT ["nginx"] + CMD ["-g", "daemon off;"]
# 默认启动 nginx -g "daemon off;",docker run <img> -t 则变为 nginx -t
COPY和ADD的区别?
# COPY(推荐优先使用):
# COPY <src> <dest> 仅复制本地文件/目录到镜像
# 简单、透明、可预期,适合绝大多数场景
#
# ADD(功能更强但不够透明):
# ADD <src> <dest> COPY 的超集,额外支持:
# 1. <src> 为 URL 时自动下载
# 2. <src> 为本地 tar 压缩包时自动解压到 <dest>
#
# 最佳实践:
# 普通文件复制 -> 用 COPY(明确、无副作用)
# 需要自动解压 tar -> 用 ADD
# 下载远程文件 -> 用 RUN curl/wget 替代 ADD URL(减少镜像层、更可控)
- 如何将容器提交为镜像?
docker commit和 Dockerfile 构建的区别?
# docker commit:将容器当前状态保存为新镜像
docker commit <容器名/ID> <新镜像名:tag> # 基本用法
docker commit -m "描述信息" -a "作者" <容器> <镜像> # 带元信息
docker commit --change='CMD ["nginx","-g","daemon off;"]' <容器> <镜像> # 同时修改配置
# docker commit(手动快照)vs Dockerfile(声明式构建):
# 维度 | docker commit | Dockerfile
# -----------|------------------------|--------------------------
# 可复现性 | 手动操作,不可复现 | 声明式,可复现,可版本控制
# 透明度 | 黑盒,不知道做了什么 | 每条指令可见,变更可审计
# 镜像层 | 所有变更在一层 | 每条指令一层,可缓存
# 适用场景 | 紧急调试/临时保存现场 | 生产构建,CI/CD 流水线
# 维护性 | 差 | 好,可纳入 Git 管理
#
# 面试金句:docker commit 像手动配置一台服务器后打快照,Dockerfile 像写 Infrastructure as Code
- 如何给镜像打标签并推送到仓库?
# ========== 给镜像打标签 ==========
docker tag <源镜像:tag> <目标仓库/镜像名:tag> # 基本格式
docker tag myapp:latest harbor.example.com/project/myapp:v1.0
docker tag <镜像ID> myapp:v2.0 # 用镜像 ID 打标签
# 一个镜像可以有多个 tag,本质是给同一 Image ID 添加别名
# ========== 推送到仓库 ==========
docker login <仓库地址> # 先登录(Docker Hub 可省略地址)
docker login harbor.example.com -u admin -p 123456
docker push <仓库地址/镜像名:tag> # 推送指定 tag
docker push myapp:v1.0 # 推送到 Docker Hub(默认)
docker push harbor.example.com/project/myapp:v1.0 # 推送到私有仓库
# 规范 tag 命名:registry/namespace/repo:version
# 例:docker.io/library/nginx:1.25
- 如何清理没用的镜像、容器、数据卷?
# ========== 一键清理(推荐定期执行)==========
docker system prune # 清理停止的容器、未使用的网络、悬空镜像、构建缓存
docker system prune -a # 额外清理所有未被使用的镜像(非仅悬空)
docker system prune -a --volumes # 连带清理未使用的数据卷(危险,确认后执行)
# ========== 分类清理 ==========
docker container prune # 清理所有已停止的容器
docker image prune # 清理悬空镜像(dangling,<none>:<none>)
docker image prune -a # 清理所有未被任何容器引用的镜像
docker volume prune # 清理未被任何容器使用的数据卷
docker network prune # 清理未被任何容器使用的网络
docker builder prune # 清理构建缓存
# ========== 查看磁盘占用 ==========
docker system df # 查看镜像/容器/数据卷的磁盘占用
docker system df -v # 详细模式,列出每个对象的占用
4.3 Dockerfile
- 写一个 Dockerfile,部署一个 Nginx 并替换主页。
# Dockerfile
FROM nginx:1.25-alpine # 使用轻量 Alpine 基础镜像
LABEL maintainer="admin@example.com" # 元信息
# 方式一:COPY 直接覆盖默认首页
COPY index.html /usr/share/nginx/html/index.html
# 方式二:COPY 整个目录(推荐)
# COPY ./html/ /usr/share/nginx/html/
# 方式三:用 RUN echo 内联写入(简单场景)
# RUN echo '<h1>Hello from Docker!</h1>' > /usr/share/nginx/html/index.html
EXPOSE 80 # 声明端口(文档作用)
CMD ["nginx", "-g", "daemon off;"] # 前台运行(容器内必须)
# ========== 构建与运行 ==========
# docker build -t my-nginx:v1 .
# docker run -d -p 8080:80 --name web my-nginx:v1
# curl http://localhost:8080
- Dockerfile 中的
RUN、CMD、ENTRYPOINT各有什么作用?
# ====== RUN(构建时执行)======
# 在镜像构建阶段执行,结果作为新的一层固化到镜像中
# 通常用于安装软件、修改配置等构建操作
RUN apt-get update && apt-get install -y curl \
&& rm -rf /var/lib/apt/lists/* # 清理缓存,减小层大小
RUN ["/bin/bash", "-c", "echo hello"] # exec 形式
# ====== CMD(运行时默认命令)======
# 容器启动时的默认行为,可被 docker run 后的命令覆盖
CMD ["nginx", "-g", "daemon off;"] # exec 形式(推荐)
CMD nginx -g "daemon off;" # shell 形式
# ====== ENTRYPOINT(运行时固定入口)======
# 容器启动时的固定入口,docker run 后的参数作为追加参数
ENTRYPOINT ["docker-entrypoint.sh"] # 适合做初始化脚本
ENTRYPOINT ["nginx"] # 固定命令 + CMD 提供默认参数
CMD ["-g", "daemon off;"] # -> 最终执行:nginx -g "daemon off;"
- 多阶段构建(multi-stage build)有什么好处?
# 多阶段构建:一个 Dockerfile 中定义多个 FROM,最终只复制产物到最终镜像
#
# 好处:
# 1. 减小镜像体积:编译工具链(gcc、maven 等)不进入最终镜像
# 2. 安全:减少攻击面(无编译器、无源码、无密钥残留)
# 3. 简化 CI/CD:一个 Dockerfile 完成构建+打包,无需外部脚本
# 4. 层复用:构建阶段层可单独缓存,加速重复构建
# ====== 示例:Go 应用多阶段构建 ======
# 阶段 1:编译
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 go build -o /app/server .
# 阶段 2:运行(最终镜像)
FROM alpine:3.19
COPY --from=builder /app/server /usr/local/bin/server
EXPOSE 8080
CMD ["server"]
# 最终镜像仅 ~10MB(Go 二进制 + Alpine),而构建阶段镜像 ~400MB
4.4 网络与存储
- Docker 的 bridge、host、none 网络模式各有什么特点?
# bridge(默认网桥模式):
# 容器通过 docker0 虚拟网桥连接,NAT 访问外网
# 端口映射 docker run -p 8080:80
# 容器间通过 IP 通信(自定义 bridge 网络可通过容器名通信)
# 适用:绝大多数场景
# host(主机网络模式):
# 容器与宿主机共享网络栈,容器端口直接暴露在宿主机
# 无端口映射开销,性能最高
# 端口冲突风险:容器端口不能与宿主机端口重复
# 适用:高性能网络需求(如网络密集型应用)
# none(无网络模式):
# 容器只有 lo 回环网卡,完全隔离
# 适用:不需要网络的批处理任务、安全计算场景
# ========== 查看与操作 ==========
docker network ls # 列出所有网络
docker network inspect bridge # 查看网桥详细信息
docker run --network=host <镜像> # 使用 host 模式
docker run --network=none <镜像> # 使用 none 模式
docker network create my-net # 创建自定义 bridge 网络
docker run --network=my-net <镜像> # 使用自定义网络(支持 DNS 容器名解析)
- Docker 的数据卷有几种方式?
bind mount和volume的区别?
# Docker 三种挂载方式:
# 1. Volume(数据卷) Docker 管理的存储,位于 /var/lib/docker/volumes/
# 2. Bind Mount(绑定挂载) 挂载宿主机任意路径到容器
# 3. tmpfs(内存挂载) 挂载到内存,容器停止即消失(适合临时敏感数据)
# ====== Volume vs Bind Mount ======
# 特性 | Volume | Bind Mount
# ----------|-----------------------------|---------------------------
# 管理方 | Docker 管理 | 宿主机文件系统直接管理
# 路径 | /var/lib/docker/volumes/ | 宿主机任意路径
# 创建 | docker volume create | 手动创建目录或让 Docker 创建
# 可移植性 | 好(与宿主机解耦) | 差(依赖宿主机目录结构)
# 容器写入 | 隔离,不污染宿主机 | 直接修改宿主机文件
# 适用场景 | 数据库持久化、生产环境 | 开发时热更新代码、共享配置
# ====== 使用示例 ======
docker volume create mydata # 创建 Volume
docker run -v mydata:/data <镜像> # 使用 Volume
docker run -v /home/user/app:/app <镜像> # Bind Mount
docker run --mount type=volume,src=mydata,dst=/data <镜像> # --mount 语法(推荐)
docker run --tmpfs /tmp:rw,noexec,nosuid,size=64m <镜像> # tmpfs 挂载
- 两个容器之间如何通信?
# 方式一:自定义 bridge 网络(推荐,支持 DNS 容器名解析)
docker network create my-net
docker run -d --name web --network my-net nginx
docker run -d --name app --network my-net myapp
# app 容器内可以直接 ping web(Docker 内置 DNS 自动解析容器名)
# 方式二:默认 bridge 网络(只能通过 IP 通信,不推荐)
docker run -d --name web nginx
docker inspect web | grep IPAddress # 获取容器 IP
docker run --rm alpine ping 172.17.0.2 # 用 IP 访问
# 方式三:端口映射(外部/主机访问容器)
docker run -d -p 8080:80 nginx # 主机 8080 -> 容器 80
curl http://localhost:8080
# 方式四:共享网络栈(--network container:<容器名>)
docker run -d --name web nginx
docker run --network container:web alpine sh # 共享 web 容器的网络
# 方式五:Docker Compose(自动创建网络,服务名互相访问,推荐生产用)
4.5 Docker Compose
- Docker Compose 是做什么的?和
docker run比有什么优势?
# Docker Compose:通过 YAML 文件定义和管理多容器应用的工具
# 一条命令启动/停止/重建整个应用栈的所有服务
#
# ====== 与 docker run 的对比 ======
# 维度 | docker run | Docker Compose
# -----------|-----------------------------|---------------------------
# 配置方式 | 命令行参数,散落在脚本中 | 集中在 docker-compose.yml
# 多容器管理 | 需逐个手动启动,手动处理依赖 | 一键编排,自动处理启动顺序
# 网络 | 需手动创建和管理网络 | 自动创建网络,服务名即 DNS
# 数据卷 | 手动创建和管理 | 在 YAML 中声明,自动管理
# 环境一致性 | 依赖脚本,不同环境可能不同 | 一个 YAML 文件保证一致性
# 版本控制 | 不便 | YAML 纳入 Git,声明式
# 适用场景 | 单个容器、临时调试 | 微服务本地开发、集成测试
# 面试金句:docker run 像一个个手动启动进程,Compose 像 systemd 统一管理一组服务
docker-compose up -d中的-d代表什么?
# -d (--detach):后台运行模式(detached mode)
# 服务在后台启动,终端不阻塞,打印容器名后立即返回命令行
#
# 不加 -d:前台模式,所有容器日志输出到终端,Ctrl+C 停止所有服务
# 加 -d:后台模式,日志不输出到终端,需用 docker-compose logs 查看
#
# ====== 常用命令 ======
docker-compose up -d # 后台启动所有服务(生产/开发环境常用)
docker-compose up # 前台启动(调试时用,实时看日志)
docker-compose up -d nginx # 后台启动指定服务
docker-compose logs -f # 跟踪查看所有服务日志
docker-compose logs -f web # 跟踪查看指定服务日志
docker-compose ps # 查看服务状态
docker-compose down # 停止并移除所有服务(加 -v 同时删除数据卷)
docker-compose restart # 重启所有服务
- 如何用 Docker Compose 编排一个 LNMP 环境?
# docker-compose.yml — LNMP (Linux + Nginx + MySQL + PHP-FPM)
version: '3.8'
services:
# ====== Nginx ======
nginx:
image: nginx:1.25-alpine
container_name: lnmp-nginx
ports:
- "80:80"
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d # Nginx 配置
- ./www:/usr/share/nginx/html # 静态文件
depends_on:
- php
networks:
- lnmp-net
# ====== PHP-FPM ======
php:
image: php:8.2-fpm-alpine
container_name: lnmp-php
volumes:
- ./www:/usr/share/nginx/html # 代码共享目录
networks:
- lnmp-net
# ====== MySQL ======
mysql:
image: mysql:8.0
container_name: lnmp-mysql
environment:
MYSQL_ROOT_PASSWORD: root123
MYSQL_DATABASE: appdb
ports:
- "3306:3306"
volumes:
- mysql-data:/var/lib/mysql # 数据持久化
- ./mysql/init:/docker-entrypoint-initdb.d # 初始化脚本
networks:
- lnmp-net
volumes:
mysql-data: # 声明数据卷
networks:
lnmp-net: # 自定义网络
driver: bridge
# ====== Nginx 配置(./nginx/conf.d/default.conf) ======
# server {
# listen 80;
# server_name localhost;
# root /usr/share/nginx/html;
# index index.php index.html;
#
# location ~ \.php$ {
# fastcgi_pass php:9000; # 通过服务名解析 PHP 容器
# fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# include fastcgi_params;
# }
# }
# ====== 启动 ======
# docker-compose up -d
# ###### 验证 ======
# curl http://localhost
# docker-compose ps
五、Kubernetes
5.1 架构与核心概念
- Kubernetes 的核心组件有哪些?各自的作用?
# ==================== Master 节点组件 ====================
# 1. API Server:集群统一入口,REST API,认证/授权/准入控制
# 2. etcd:分布式 KV 存储,保存集群所有状态数据
# 3. Controller Manager:运行各种控制器(Deployment/Node/Endpoint 等),确保实际状态向期望状态收敛
# 4. Scheduler:负责将 Pod 调度到合适的 Node 上
# ==================== Worker 节点组件 ====================
# 5. kubelet:节点上的 Agent,负责管理 Pod 生命周期,上报节点状态
# 6. kube-proxy:实现 Service 的网络代理和负载均衡(iptables/IPVS)
# 7. Container Runtime:容器运行时(containerd/CRI-O),负责拉取镜像、运行容器
# ==================== 插件/Addon ====================
# 8. CoreDNS:集群内 DNS 服务,解析 Service 和 Pod 域名
# 9. CNI 插件(Calico/Flannel):实现 Pod 网络互通
- Master 节点上的组件:API Server、etcd、Controller Manager、Scheduler 各做什么?
# API Server
# 所有组件交互的中心枢纽,提供 RESTful API,负责认证(Authentication)、
# 授权(Authorization)、准入控制(Admission Control),唯一与 etcd 直连的组件
# etcd
# 分布式 KV 数据库,基于 Raft 共识协议,存储集群所有对象状态。
# 是集群的"数据库",集群恢复的关键
# Controller Manager
# 运行一组控制循环(Deployment Controller/Node Controller 等),
# 持续监控 API Server 中的资源状态,将实际状态向声明式期望状态对齐
# 例如:Deployment → ReplicaSet → Pod 这一级联管理链条的驱动者
# Scheduler
# 监听未调度 Pod(nodeName 为空),通过预选(Filter)+ 优选(Score)
# 两阶段打分,选择最优 Node,然后更新 Pod 的 nodeName 字段
- Worker 节点上的组件:kubelet、kube-proxy、Container Runtime 各做什么?
# kubelet
# 节点上的核心 Agent,接收 PodSpec,确保容器按声明的状态运行;
# 上报 Node 状态和 Pod 状态到 API Server;执行 liveness/readiness 探针
# 启动命令:kubelet --config=/var/lib/kubelet/config.yaml
# kube-proxy
# 在每个 Node 上运行,监听 Service 和 Endpoints,维护网络规则
# (iptables 规则或 IPVS 规则),将访问 Service ClusterIP 的流量
# 负载均衡到后端 Pod IP
# Container Runtime
# 通过 CRI 接口与 kubelet 交互,负责镜像拉取、容器创建/启停等操作
# 常见:containerd(默认)、CRI-O、Docker(dockershim 已移除)
# 检查运行时状态:crictl ps / crictl images
- Pod 是什么?Pod 和容器的关系?一个 Pod 里为什么要放多个容器?
# Pod 是什么?
# Pod 是 K8s 最小调度单元,是一组共享命名空间(Network/IPC)和存储卷的容器的集合。
# 生命周期短暂、可替换,Pod 中所有容器总是运行在同一 Node 上。
# Pod 和容器的关系
# Pod 是容器的"逻辑主机"——一个 Pod 可包含一个或多个容器。
# 同一个 Pod 内的容器共享:
# - 同一个网络命名空间(同一个 IP、共享 localhost)
# - 同一个 IPC 命名空间
# - 可以通过 shared volumes 共享文件
# 为什么一个 Pod 里放多个容器?(Sidecar 模式)
# 1. Sidecar 辅助模式:主容器 + 日志收集(filebeat)/ 服务网格代理(envoy/istio-proxy)
# 2. Ambassador 代理模式:主容器 + 外部代理(连接外部服务)
# 3. Adapter 适配器模式:主容器 + 格式转换容器(统一监控指标格式)
# 示例:带 Sidecar 的 Pod YAML
# spec.containers[0] 是主容器 nginx,containers[1] 是日志收集 fluentd
- Label 和 Selector 的作用?Annotation 和 Label 的区别?
# Label 和 Selector
# Label:附加在 K8s 对象上的 key/value 对(如 app=nginx, env=prod)
# Selector:根据 Label 筛选/关联资源的条件表达式
# 作用:Service 通过 Selector 关联 Pod,Deployment 通过 Selector 管理 Pod
# 示例:kubectl get pods -l 'app=nginx,env in (staging,prod)'
# Annotation 和 Label 的区别
# | 特性 | Label | Annotation |
# |-----------|--------------------------------|----------------------------------|
# | 用途 | 标识、分组、筛选资源 | 存储非标识性元数据 |
# | 被查询 | 是(通过 Selector) | 否 |
# | 典型场景 | Service 选 Pod、调度约束 | 构建信息、git commit、联系邮箱 |
# | 大小限制 | 63 字符以内 | 可更大 |
5.2 工作负载资源
- Deployment、StatefulSet、DaemonSet、Job、CronJob 各适用于什么场景?
# Deployment:无状态应用
# 场景:Web 服务(nginx/tomcat)、API 服务、微服务
# 特点:Pod 可互换、无固定标识、支持滚动更新/回滚
# StatefulSet:有状态应用
# 场景:数据库(MySQL/PostgreSQL/MongoDB)、消息队列(Kafka/RabbitMQ)、
# 分布式存储(Ceph/GlusterFS)、ZooKeeper/etcd 集群
# 特点:Pod 有固定网络标识和持久存储、按序创建删除
# DaemonSet:每个节点运行一个 Pod
# 场景:日志收集(fluentd/filebeat)、监控 Agent(node-exporter/datadog)、
# CNI 插件(Calico node)、kube-proxy、存储插件
# 特点:新节点加入自动创建,节点移除自动清理
# Job:一次性任务
# 场景:数据迁移、备份、批处理、ETL 任务
# 特点:执行完退出、可设置并行数和重试次数
# CronJob:定时任务
# 场景:定期备份、定时报表、证书续期、定期清理
# 特点:Cron 表达式调度、每个周期创建新的 Job
- Deployment 和 StatefulSet 的核心区别?(对比列表)
# | 特性 | Deployment | StatefulSet |
# |-------------------|------------------------|--------------------------|
# | Pod 标识 | 随机名称(随机后缀) | 固定序号(如 mysql-0,-1) |
# | 网络标识 | 无固定 DNS | 有固定 DNS(Headless Svc)|
# | 存储 | 共享 PVC/无状态 | 每个 Pod 独立 PVC |
# | 启动/停止顺序 | 并行/随机 | 按序(0→1→2) |
# | 滚动更新 | 默认支持 | 按序反向更新(N-1→0) |
# | 扩缩容 | 随机 | 按序(扩容 0→N,缩容 N→0)|
# | 删除 Pod | 删除随机 Pod | 按序从最高序号开始 |
# 一句话:Deployment 管理无状态的可互换 Pod,StatefulSet 管理需要
# 稳定网络标识和独立持久存储的有状态 Pod
- 什么是滚动更新(Rolling Update)?
maxSurge和maxUnavailable怎么设置?
# 滚动更新(Rolling Update)
# 逐步用新版本 Pod 替换旧版本 Pod,实现零停机更新。
# Deployment 的默认更新策略(默认 maxSurge=25%, maxUnavailable=25%)
# maxSurge
# 更新过程中允许超出期望副本数的最大 Pod 数量(百分比或绝对数)
# 默认 25%,设为 1 表示最多临时多出 1 个新 Pod
# maxSurge 越大 → 更新越快,但占用资源更多
# maxUnavailable
# 更新过程中允许不可用 Pod 的最大数量(百分比或绝对数)
# 默认 25%,设为 0 表示必须保证所有 Pod 可用(即"先启动新的再停旧的")
# maxUnavailable 越小 → 对服务影响越小
# 示例策略:
spec:
strategy:
type: RollingUpdate
rollingUpdate:
maxSurge: 1 # 最多多出 1 个 Pod(或 25%)
maxUnavailable: 0 # 不允许任何 Pod 不可用(先启新后停旧)
- 如何回滚一个 Deployment?如何查看历史版本?
# 回滚到上一个版本
kubectl rollout undo deployment/nginx-deployment
# 回滚到指定版本
kubectl rollout undo deployment/nginx-deployment --to-revision=3
# 查看历史版本(前提:.spec.revisionHistoryLimit > 0,默认 10)
kubectl rollout history deployment/nginx-deployment
# 查看某个版本的详细信息
kubectl rollout history deployment/nginx-deployment --revision=3
# 查看回滚状态
kubectl rollout status deployment/nginx-deployment
# 暂停/恢复滚动更新
kubectl rollout pause deployment/nginx-deployment
kubectl rollout resume deployment/nginx-deployment
- StatefulSet 的 Pod 名称有什么规律?为什么需要 Headless Service?
# Pod 名称规律
# StatefulSet Name + 从 0 开始的序号:<statefulset-name>-<ordinal-index>
# 例如:mysql-0, mysql-1, mysql-2
# 创建顺序:0 → 1 → 2;删除顺序:2 → 1 → 0
# 每个 Pod 有稳定的网络标识(DNS):
# <pod-name>.<headless-service-name>.<namespace>.svc.cluster.local
# 例如:mysql-0.mysql.default.svc.cluster.local
# 为什么需要 Headless Service?
# Headless Service (clusterIP: None) 不分配 ClusterIP,DNS 直接返回 Pod IP 列表。
# 作用:
# 1. 给每个 Pod 提供稳定的网络标识(固定 DNS 名称)
# 2. 客户端可直接通过 DNS 发现所有 Pod(如 ZooKeeper 集群成员发现)
# 3. Pod 重启后虽然 IP 可能变,但 DNS 名称不变
# 示例:
apiVersion: v1
kind: Service
metadata:
name: mysql
spec:
clusterIP: None # Headless Service
selector:
app: mysql
ports:
- port: 3306
- DaemonSet 的典型使用场景有哪些?
# 1. 日志收集 Agent
# 每个节点运行 Filebeat/Fluentd,收集该节点所有 Pod 日志
# kubectl apply -f https://download.elastic.co/beats/filebeat/filebeat-kubernetes.yaml
# 2. 监控 Agent
# node-exporter(Prometheus 生态)收集节点级别指标
# Datadog Agent、Grafana Agent 等
# 3. CNI 网络插件
# Calico node(calico-node)、Flannel(kube-flannel-ds)
# 需要在每个节点创建网络接口和路由规则
# 4. kube-proxy
# K8s 原生的 kube-proxy 就是 DaemonSet 部署
# kubectl -n kube-system get ds kube-proxy
# 5. 存储插件
# CSI 驱动的 Node Plugin(如 Ceph CSI、NFS CSI node-plugin)
# 6. 节点维护任务
# 节点清理(清理旧镜像)、安全扫描(漏洞扫描Agent)
# 特点:自动适配集群扩缩,新节点加入自动创建 Pod
- Job 和 CronJob 的区别?CronJob 的并发策略有哪些?
# Job vs CronJob
# | 特性 | Job | CronJob |
# |---------|--------------------------|----------------------------------|
# | 触发方式 | 一次性 / 手动创建 | 定时触发(Cron 表达式) |
# | 运行次数 | 执行完成即停止 | 按周期重复创建 Job |
# | 典型场景 | 数据迁移、一次性批处理 | 定期备份、定时报表、证书续期 |
# Job 关键参数
spec:
completions: 5 # 需要成功完成 5 个 Pod
parallelism: 2 # 最多 2 个 Pod 并行运行
backoffLimit: 4 # 失败重试次数
activeDeadlineSeconds: 100 # Job 最大运行时间
# CronJob 并发策略(.spec.concurrencyPolicy)
# Allow(默认):允许多个 Job 同时运行
# Forbid:如果上一个 Job 未完成,跳过本次
# Replace:如果上一个 Job 未完成,取消它并启动新 Job
# 示例 CronJob
apiVersion: batch/v1
kind: CronJob
metadata:
name: backup-job
spec:
schedule: "0 2 * * *" # 每天凌晨 2 点
concurrencyPolicy: Forbid # 不允许并发
successfulJobsHistoryLimit: 3 # 保留成功 Job 数
failedJobsHistoryLimit: 1 # 保留失败 Job 数
jobTemplate:
spec:
template:
spec:
containers:
- name: backup
image: backup-tool
restartPolicy: OnFailure
5.3 网络
- Service 有哪几种类型?ClusterIP、NodePort、LoadBalancer、ExternalName 的区别?
# Service 四种类型
# 1. ClusterIP(默认)
# 分配一个集群内部 IP,只能在集群内访问
# 场景:微服务内部互相调用
# spec.type: ClusterIP
# 2. NodePort
# 在每个 Node 上开放一个端口(30000-32767),通过 <NodeIP>:<NodePort> 从外部访问
# 底层自动创建 ClusterIP Service
# 场景:开发测试、简单外部访问
# spec.type: NodePort
# 3. LoadBalancer
# 调用云厂商(或 MetalLB)的 LB 接口,创建一个外部负载均衡器
# 底层自动创建 NodePort + ClusterIP
# 场景:生产环境对外暴露服务
# spec.type: LoadBalancer
# 4. ExternalName
# 不创建 Pod 选择器,通过 CNAME 将 Service 映射到外部 DNS
# 场景:将外部服务(如数据库)映射为集群内部 Service
# spec.type: ExternalName
# spec.externalName: mydb.example.com
# 快速查看:kubectl get svc -A
- ClusterIP 的虚拟 IP 是怎么实现的?(iptables / IPVS)
# ClusterIP 是虚拟 IP,不存在任何网络接口上,由 kube-proxy 通过 iptables/IPVS 规则实现
# iptables 模式(默认模式):
# 1. kube-proxy 在 iptables 的 PREROUTING/OUTPUT 链中添加规则
# 2. 匹配到 ClusterIP:Port 的包被 DNAT 到后端 Pod IP
# 3. 使用 iptables 的 statistic 模块做随机负载均衡(概率轮询)
# 4. 缺点:规则数量与 Service/Pod 成正比,大规模集群规则匹配性能差
# IPVS 模式:
# 1. 在 PREROUTING 链将包转发到 IPVS,IPVS 在内核层做负载均衡
# 2. 支持多种负载均衡算法:rr、lc、dh、sh、sed、nq
# 3. 优点:性能远优于 iptables(哈希查找 vs 线性匹配)
# 查看 kube-proxy 模式:
kubectl -n kube-system logs ds/kube-proxy | head -5
# 或查看 ConfigMap
kubectl -n kube-system describe cm kube-proxy | grep mode
# 查看 iptables 规则示例:
iptables -t nat -L KUBE-SERVICES -n | grep <ClusterIP>
- kube-proxy 的三种模式?iptables 和 IPVS 的区别?
# kube-proxy 三种模式
# 1. userspace 模式(已淘汰)
# kube-proxy 监听端口,用户态转发,性能最差
# 2. iptables 模式(默认)
# 在内核态通过 iptables 规则做 DNAT + 随机负载均衡
# 优点:无需额外内核模块、通用性好
# 缺点:线性匹配规则,大规模集群严重性能下降;
# 非增量更新(全量重刷规则引起瞬时中断)
# 3. IPVS 模式
# 使用 Linux 内核 IPVS 模块做四层负载均衡
# 优点:Hash 表 O(1) 查找、支持多种调度算法、性能远优于 iptables
# 缺点:需要加载 ip_vs 内核模块;某些场景与 CNI 可能有兼容性问题
# iptables vs IPVS 对比
# | 特性 | iptables | IPVS |
# |-------------|---------------------|----------------------------|
# | 查找方式 | 线性匹配 | Hash 表 O(1) |
# | 规则更新 | 全量替换 | 增量更新 |
# | 负载均衡算法 | 随机(概率轮询) | rr/lc/dh/sh/sed/nq 等 |
# | 大规模集群 | 性能急剧下降 | 性能稳定 |
# 启用 IPVS 模式
# kube-proxy --proxy-mode=ipvs --ipvs-scheduler=rr
- Endpoints 是什么?为什么 Endpoints 为空会导致 Service 不可用?
# Endpoints
# Endpoints 是 K8s 的一种资源对象,记录了 Service 对应的后端 Pod IP:Port 列表。
# Service 通过 Selector 自动创建并维护 Endpoints。
# 为什么 Endpoints 为空会导致 Service 不可用?
# kube-proxy 根据 Endpoints 中的 IP 列表生成转发规则
# 如果 Endpoints 为空 → 规则中没有后端目标 → 流量无处可去 → Service 不通
# 常见 Endpoints 为空的原因:
# 1. Selector 不匹配任何 Pod(Label 写错、Pod 没有对应 Label)
# 2. Readiness Probe 失败,Pod 被移出 Endpoints
# 3. Pod 所在的 Namespace 与 Service 不同
# 排查命令:
kubectl get endpoints <svc-name> -n <namespace>
kubectl describe endpoints <svc-name>
# 检查 Selector 是否匹配
kubectl get pods -l <selector> -n <namespace>
# EndpointSlice(v1.21+)是新一代 Endpoints 实现,支持更大规模集群
- Ingress 是什么?Ingress 和 Service 的关系?
# Ingress 是什么?
# Ingress 是 K8s 的七层负载均衡资源,提供 HTTP/HTTPS 路由规则管理。
# 支持:域名路由、路径路由、TLS 终止、重定向、自定义规则等。
# Ingress 本身只是"规则定义",需要 Ingress Controller 来实现。
# Ingress 和 Service 的关系
# Ingress → 将外部流量路由到 → Service → 转发到 → Pod
# Ingress 本身不是 Service 类型,而是建立在 Service 之上的七层路由
# 一个 Ingress 可以路由到多个 Service(不同域名/路径映射到不同 Service)
# Ingress vs Service(NodePort/LB)
# | 特性 | Service (NodePort/LB) | Ingress |
# |-----------|----------------------|------------------------------|
# | 层级 | L4(TCP/UDP) | L7(HTTP/HTTPS) |
# | 路由方式 | IP:Port 级别 | 域名/路径级别 |
# | TLS | 需要额外配置 | 内置 TLS 终止 |
# | 多服务 | 每个 Service 一个 LB | 一个 Ingress 路由多个 Service |
# 示例 Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: api.example.com
http:
paths:
- path: /users
pathType: Prefix
backend:
service:
name: user-service
port:
number: 80
- Ingress Controller 是什么?Nginx Ingress Controller 的工作流程?
# Ingress Controller 是什么?
# Ingress Controller 是实际处理 Ingress 规则的组件,监听 Ingress 资源变化,
# 动态配置 L7 代理(Nginx/HAProxy/Traefik/Envoy 等)。
# Ingress 只是规则,Controller 才是干活的人。
# Nginx Ingress Controller 工作流程:
# 1. Controller 监听 K8s API Server 中的 Ingress/Service/Endpoints/Secret 变化
# 2. 检测到变化后,根据 Ingress 规则生成 nginx.conf 配置
# 3. 将配置写入内部的 nginx 进程并 reload(或热更新 Lua 插件)
# 4. 外部请求 → NodePort/LoadBalancer → Nginx Pod → 按规则路由 → 后端 Service Pod
# 常见 Ingress Controller:
# - ingress-nginx (Kubernetes 社区维护)
# - nginx-ingress (NGINX Inc 维护)
# - Traefik、HAProxy Ingress、Kong Ingress、Istio Gateway
# - 云厂商:ALB Ingress Controller (AWS)、AGIC (Azure)
# 部署 ingress-nginx:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.0/deploy/static/provider/cloud/deploy.yaml
- CNI 是什么?Calico 和 Flannel 的主要区别?
# CNI(Container Network Interface)
# K8s 网络插件的标准接口规范,定义了容器网络配置的标准 API。
# kubelet 调用 CNI 插件为新创建的 Pod 配置网络(分配 IP、创建网络接口、设置路由)。
# CNI 插件只需实现 ADD/DEL/CHECK 等操作。
# 常见 CNI 插件分类:
# - Overlay 模式:Flannel (vxlan)、Calico (IPIP)
# - 路由模式(BGP):Calico (BGP)
# - 二层模式:Flannel (host-gw)
# - 其他:Cilium (eBPF)、Weave、Macvlan
# Calico vs Flannel 主要区别
# | 特性 | Calico | Flannel |
# |-------------|--------------------------|---------------------------|
# | 网络模式 | BGP / IPIP / VXLAN | VXLAN / host-gw |
# | 网络策略 | 完整支持 NetworkPolicy | 不原生支持(需额外组件) |
# | 性能 | BGP 模式接近裸金属 | VXLAN 有一定封装开销 |
# | 适用场景 | 中大型集群、需要网络策略 | 小集群、简单场景 |
# | 复杂度 | 较高 | 低,开箱即用 |
# 一句话:Flannel 简单易用适合小集群,Calico 功能强大(网络策略+高性能)适合生产环境
- NetworkPolicy 是做什么的?Ingress 和 Egress 分别控制什么?
# NetworkPolicy
# 定义 Pod 间网络访问规则的资源(Pod 级别防火墙),基于 CNI 实现
# 注意:不是所有 CNI 都支持 NetworkPolicy(Flannel 不原生支持)
# Ingress(入站规则)
# 控制"哪些来源"可以访问当前 Pod
# 规则三要素:来源 Pod(podSelector)、来源 Namespace(namespaceSelector)、允许的 IP 段(ipBlock)
spec:
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
- namespaceSelector:
matchLabels:
env: production
- ipBlock:
cidr: 10.0.0.0/8
ports:
- protocol: TCP
port: 6379
# Egress(出站规则)
# 控制当前 Pod 可以访问"哪些目标"
spec:
egress:
- to:
- podSelector:
matchLabels:
app: database
ports:
- protocol: TCP
port: 5432
# 默认策略:没有 NetworkPolicy 时所有流量开放
# 只要有一条 NetworkPolicy 选中 Pod,该 Pod 默认拒绝所有,只允许策略中声明的流量
# 查看:kubectl get networkpolicy -A
- 客户访问 Pod 不通,你的排查思路?(完整链路排查)
# 完整链路排查:客户端 → Ingress/LB → Service → Endpoints → Pod → 容器应用
# 步骤 1:确认 Pod 自身正常
kubectl get pod <pod-name> -n <ns> -o wide # 看 Pod 状态、IP
kubectl logs <pod-name> -n <ns> --tail=50 # 看应用日志
kubectl exec -it <pod-name> -n <ns> -- curl localhost:8080 # 本地测试端口
# 步骤 2:检查 Readiness Probe 是否通过
kubectl describe pod <pod-name> -n <ns> | grep -A5 Readiness
kubectl get endpoints <svc-name> -n <ns> # 确认 Pod IP 在 Endpoints 中
# 步骤 3:检查 Service 和 Endpoints
kubectl get svc <svc-name> -n <ns> # 确认 ClusterIP/端口
kubectl describe svc <svc-name> -n <ns> # 检查 Selector 与 Pod Labels
kubectl get endpoints <svc-name> -n <ns> # 确认 Endpoints 非空
# 步骤 4:从另一个 Pod 测试 Service 连通性
kubectl run test-pod --rm -it --image=busybox -- sh
# 测试 ClusterIP
wget -O- http://<svc-cluster-ip>:<port>
# 测试 DNS 解析
nslookup <svc-name>.<namespace>.svc.cluster.local
# 步骤 5:检查 DNS
kubectl -n kube-system get pods -l k8s-app=kube-dns # CoreDNS 是否正常
kubectl exec -it <dns-pod> -n kube-system -- nslookup <svc-name>
# 步骤 6:检查 kube-proxy 规则
# iptables 模式
iptables -t nat -L KUBE-SERVICES -n | grep <cluster-ip>
# IPVS 模式
ipvsadm -L -n | grep <cluster-ip>
# 步骤 7:检查 CNI 网络
kubectl -n kube-system get pods -l k8s-app=calico-node
# 检查 Pod 网络接口
kubectl exec <pod-name> -- ip addr
# ping 测试 Pod 间通信(需 CNI 支持或 Pod 有 ICMP 能力)
# 步骤 8:检查 NetworkPolicy
kubectl get networkpolicy -n <ns> # 是否有策略阻止流量
# 步骤 9:外部入口层(如 Ingress)
kubectl get ingress -n <ns>
kubectl describe ingress <name> -n <ns> # 检查规则和 backend
kubectl -n ingress-nginx logs <ingress-controller-pod> --tail=100
# 步骤 10:节点/系统层面
kubectl describe node <node-name> # 节点状态
kubectl top node / kubectl top pod # 资源消耗
5.4 存储
- Volume 的作用是什么?emptydir、hostPath、PV、PVC 各有什么特点?
# Volume 的作用
# 容器文件系统是临时的(容器重启数据丢失),Volume 提供持久化存储和 Pod 内
# 容器间数据共享。Volume 的生命周期与 Pod 绑定(除 PV 外)。
# emptydir
# Pod 创建时分配的空目录,Pod 删除即销毁
# 数据存在 Node 本地磁盘(或内存 ramdisk)
# 场景:临时缓存、暂存数据、同 Pod 容器间共享
spec:
volumes:
- name: cache
emptyDir:
medium: Memory # 可选 Memory 模式(tmpfs,更快)
sizeLimit: 256Mi
# hostPath
# 挂载 Node 上的文件或目录到 Pod
# Pod 删除后数据仍保留在 Node 上
# 场景:访问 Docker socket (/var/run/docker.sock)、节点日志、DaemonSet 收集数据
# 风险:Pod 可能被重调度到其他节点丢失数据;多副本数据不一致
spec:
volumes:
- name: host-data
hostPath:
path: /data
type: DirectoryOrCreate
# PV(PersistentVolume)
# 集群管理员创建的存储资源(存储抽象),独立于 Pod 存在
# 支持:NFS/Ceph/GlusterFS/本地磁盘/云存储 等
# 生命周期与 Pod 无关
# PVC(PersistentVolumeClaim)
# 用户对存储的请求(容量、访问模式),与 PV 绑定后使用
# 类比:PV 是存储池,PVC 是从池中申请一块
- PV(PersistentVolume)和 PVC(PersistentVolumeClaim)的关系?
# PV(PersistentVolume)和 PVC(PersistentVolumeClaim)的关系
# PV:集群级别的存储资源,由管理员预创建或由 StorageClass 动态创建
# PVC:用户对存储的"请求",指定需要的容量和访问模式
# 工作流程:
# 1. 管理员创建 PV(或配置 StorageClass 动态供应)
# 2. 用户创建 PVC,声明需要的存储大小和访问模式
# 3. K8s 根据匹配条件(容量 >= 请求、访问模式一致、StorageClass 相同、
# Selector 匹配)自动将 PVC 绑定到合适的 PV
# 4. Pod 引用 PVC 名称挂载存储
# 类比理解:
# PV = 运维准备的存储资源(像停车场里的停车位)
# PVC = 开发者申请的存储请求(像一张停车券)
# K8s 自动匹配最合适的 PV 给 PVC
# 绑定条件:
# - PV 容量 >= PVC 请求容量
# - PV 访问模式满足 PVC 需求
# - PV 的 StorageClass = PVC 的 StorageClass
# - PV 的 Selector 匹配 PVC 的 Label
# 一旦绑定,PV 进入 Released 状态,不能复用(1:1 绑定关系)
# 查看绑定状态
kubectl get pv
kubectl get pvc -A
- PV 的回收策略(Retain、Delete、Recycle)有什么区别?
# PV 的三种回收策略(persistentVolumeReclaimPolicy)
# 1. Retain(保留)—— 默认策略
# 删除 PVC 后,PV 保留(状态变为 Released)
# 数据保留在存储后端,需管理员手动清理 PV 和数据
# 管理员操作:
# kubectl delete pv <pv-name> # 删除 PV(后端存储可能还需手动清理)
# 重新创建同配置 PV 供新 PVC 绑定
# 2. Delete(删除)
# 删除 PVC 后,PV 和关联的后端存储(如云盘/NFS子目录)自动删除
# 适用场景:云环境 + StorageClass 动态供应
# 注意:生产环境慎用,可能导致数据丢失
# 3. Recycle(已弃用,v1.25 移除)
# 对 PV 的数据执行 rm -rf /,然后 PV 变为 Available 状态可重新绑定
# 注意:不适用于 HostPath 之外的存储后端
# 示例:创建 Retain 策略的 PV
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv-nfs-retain
spec:
capacity:
storage: 10Gi
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
nfs:
path: /data/nfs
server: 10.1.8.10
# 命令:修改已有 PV 的回收策略
kubectl patch pv <pv-name> -p '{"spec":{"persistentVolumeReclaimPolicy":"Retain"}}'
- PV 的访问模式(RWO、ROX、RWX、RWOP)分别代表什么?
# PV 访问模式(Access Modes)
# 注意:是否支持某模式取决于底层存储能力,不是 K8s 决定的
# RWO(ReadWriteOnce)
# 可读写,但只能被单个 Node 上的 Pod 挂载
# 典型存储:云盘(AWS EBS/GCE PD)、本地磁盘
# ROX(ReadOnlyMany)
# 只读,可被多个 Node 上的 Pod 同时挂载
# 场景:共享配置文件、静态资源
# RWX(ReadWriteMany)
# 可读写,可被多个 Node 上的 Pod 同时挂载
# 典型存储:NFS、CephFS、GlusterFS、Azure File
# 场景:多个 Pod 需要共享读写同一存储(如共享上传文件)
# RWOP(ReadWriteOncePod)—— v1.22 GA
# 可读写,只能被单个 Pod 挂载(强调单 Pod 而非单 Node)
# 比 RWO 更严格:同一个 Node 上也只允许一个 Pod 挂载
# 场景:需要确保存储独占的任务
# 查看存储支持的访问模式:
kubectl describe pv <pv-name> | grep "Access Modes"
# | 缩写 | 全称 | 粒度 | 读写 |
# |-------|--------------------|------|------|
# | RWO | ReadWriteOnce | 单Node | 读写 |
# | ROX | ReadOnlyMany | 多Node | 只读 |
# | RWX | ReadWriteMany | 多Node | 读写 |
# | RWOP | ReadWriteOncePod | 单Pod | 读写 |
- StorageClass 是什么?动态卷供应和静态卷供应的区别?
# StorageClass 是什么?
# StorageClass 定义了存储"类别"(如 SSD/HDD、不同性能/可用区),
# 配合 provisioner 实现 PV 的自动创建。用户只需创建 PVC 并指定 StorageClass,
# Provisioner 自动创建 PV 并绑定给 PVC。
# 动态卷供应(Dynamic Provisioning)
# 管理员只需创建 StorageClass,用户创建 PVC 时自动创建对应 PV
# PV 按需创建、自动绑定,运维成本低
# 示例流程:
# 创建 StorageClass (带 provisioner: nfs) →
# 用户创建 PVC (指定 StorageClass) →
# Provisioner 自动创建 PV → 自动绑定 PVC
# 适用:云环境、生产环境大规模集群
# 静态卷供应(Static Provisioning)
# 管理员需手动创建 PV,然后 PVC 匹配绑定
# 流程:
# 管理员手动创建 PV(指定 NFS 服务器/路径、云盘 ID 等)→
# 用户创建 PVC(不能指定 PV 名字,通过条件匹配)
# 缺点:需要预先知道所有需求、PV 管理繁琐
# 适用:小规模、存储资源固定
# 示例 StorageClass:
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-sc-fast
provisioner: nfs.csi.k8s.io
parameters:
server: 10.1.8.10
path: /data/nfs
mountOptions:
- nfsvers=4.1
reclaimPolicy: Delete
volumeBindingMode: Immediate # Immediate / WaitForFirstConsumer
- NFS 动态卷制备的部署流程(provisioner + StorageClass)?
# NFS 动态卷制备部署流程(以 nfs-subdir-external-provisioner 为例)
# 步骤 1:确保有 NFS 服务器并导出目录
# NFS 服务器端:
yum install -y nfs-utils
mkdir -p /data/nfs
echo "/data/nfs *(rw,sync,no_root_squash,no_subtree_check)" >> /etc/exports
exportfs -r
systemctl enable --now nfs-server
# 步骤 2:所有 K8s 节点安装 nfs-utils(需要挂载能力)
yum install -y nfs-utils # CentOS/RHEL
apt install -y nfs-common # Ubuntu/Debian
# 步骤 3:通过 Helm 部署 nfs-subdir-external-provisioner
helm repo add nfs-subdir-external-provisioner \
https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/
helm install nfs-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner \
--set nfs.server=10.1.8.10 \
--set nfs.path=/data/nfs \
--set storageClass.name=nfs-client \
--set storageClass.reclaimPolicy=Retain \
--set storageClass.archiveOnDelete=false \
-n kube-system
# 步骤 4:验证部署
kubectl -n kube-system get pods -l app=nfs-subdir-external-provisioner
kubectl get storageclass
# 步骤 5:创建 PVC 测试
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: test-nfs-pvc
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
EOF
# 步骤 6:验证 PVC 和自动创建的 PV
kubectl get pvc test-nfs-pvc
kubectl get pv | grep test-nfs-pvc
# 步骤 7:Pod 中使用 PVC
# spec:
# containers:
# - volumeMounts:
# - name: data
# mountPath: /data
# volumes:
# - name: data
# persistentVolumeClaim:
# claimName: test-nfs-pvc
5.5 调度
- Kube-scheduler 的调度过程是怎样的?(Filter + Score)
# Kube-scheduler 调度过程:两步走
# 第一步:Filter(预选 / 过滤)
# 从所有 Node 中排除不满足条件的节点:
# 1. 节点资源不够(CPU/内存不足)
# 2. 节点有 Pod 不匹配的 Taint(且无 Toleration)
# 3. 不满足 NodeSelector / NodeAffinity
# 4. 不满足 PodAffinity / PodAntiAffinity
# 5. 节点磁盘压力 / 内存压力 / PID 压力
# 6. 端口冲突(hostPort 冲突)
# 7. 存储卷能否挂载(VolumeZone 等)
# 结果:剩余一组"可行节点"
# 第二步:Score(优选 / 打分)
# 对每个可行节点打分,选择总分最高的节点:
# 1. LeastRequestedPriority:资源剩余越多分越高(倾向均衡)
# 2. MostRequestedPriority:资源利用率越高分越高(倾向装箱)
# 3. BalancedResourceAllocation:CPU 和内存比例均衡的分高
# 4. NodeAffinityPriority:亲和性匹配加权
# 5. ImageLocality:镜像已存在该节点的分高
# 6. InterPodAffinityPriority:Pod 间亲和性加权
# 最终选择得分最高的节点,同分则随机选
# 查看调度事件:
kubectl describe pod <pod-name> | grep -A10 Events
# 查看调度器日志:
kubectl -n kube-system logs <scheduler-pod> | tail
# PriorityClass 指定 Pod 优先级(高优先级可抢占低优先级):
# kubectl get priorityclass
- nodeSelector 和 nodeAffinity 的区别?
# nodeSelector
# 最简单的节点选择方式,Pod 只在匹配 Label 的 Node 上调度
# 只能做"等于"匹配,无法做复杂表达式
spec:
nodeSelector:
disktype: ssd
gpu: "true"
# 缺点:功能有限、无法表达"不等于"或"存在性"条件
# nodeAffinity
# 更灵活的节点亲和性调度,支持丰富的匹配表达式
# 分为 preferredDuringScheduling(软亲和性)和 requiredDuringScheduling(硬亲和性)
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution: # 硬要求
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
- key: disktype
operator: NotIn # 支持 NotIn
values:
- hdd
preferredDuringSchedulingIgnoredDuringExecution: # 软偏好
- weight: 1
preference:
matchExpressions:
- key: zone
operator: In
values:
- zone-a
# 对比总结
# | 特性 | nodeSelector | nodeAffinity |
# |-----------|------------------|-----------------------------------|
# | 匹配操作 | 仅 In (=) | In/NotIn/Exists/DoesNotExist/Gt/Lt |
# | 软硬区分 | 无 | required + preferred |
# | 复杂条件 | 不支持 | 支持多条件组合 |
# | 选择 | 简单场景 | 复杂调度策略 |
- 污点(Taint)和容忍度(Toleration)是做什么的?与 nodeAffinity 的区别?
# Taint(污点)和 Toleration(容忍度)
# Taint 打在 Node 上,表示"排斥"Pod;Toleration 在 Pod 上配置,表示"接受"该 Node 的 Taint
# Taint 效果(effect):
# NoSchedule:不允许未容忍的 Pod 调度到该节点
# PreferNoSchedule:尽量不调度未容忍的 Pod
# NoExecute:驱逐节点上未容忍的 Pod,且不允许调度
# 给 Node 打 Taint
kubectl taint nodes node1 key=value:NoSchedule
# 移除 Taint
kubectl taint nodes node1 key=value:NoSchedule-
# Toleration 示例(Pod 上配置)
spec:
tolerations:
- key: "dedicated"
operator: "Equal"
value: "gpu"
effect: "NoSchedule"
# Taint/Toleration 与 nodeAffinity 的核心区别
# Taint + Toleration:拒绝式调度("此节点不接受你",除非你有 Toleration)
# nodeAffinity:吸引力调度("我希望/要求调度到这些特征的节点上")
#
# 类比:
# Taint = 节点说"闲人免进"
# Toleration = Pod 说"我有通行证"
# nodeAffinity = Pod 说"我偏好去什么地方"
# 两者可以组合使用:
# Taint 确保节点专用(如 GPU 节点),nodeAffinity 引导 Pod 过去
- cordon 和 drain 的区别?什么场景下用?
# cordon(封锁 / 暂停调度)
# 标记节点不可调度,已运行的 Pod 不受影响(不会被驱逐)
kubectl cordon <node-name>
# 效果:Scheduler 不会再调度新 Pod 到此节点
# 场景:节点维护前暂时停调、故障排查
# uncordon(解锁 / 恢复调度)
kubectl uncordon <node-name>
# drain(排水 / 驱逐)
# 驱逐节点上所有 Pod(除了 DaemonSet 的 Pod 和镜像 Pod),
# 并标记节点不可调度(等同 cordon + 驱逐)
kubectl drain <node-name> --ignore-daemonsets --delete-emptydir-data
# 参数:
# --ignore-daemonsets:跳过 DaemonSet Pod
# --delete-emptydir-data:允许删除使用 emptyDir 的 Pod
# --force:强制驱逐(不加 --force 已足够)
# 场景:节点下线、节点升级、换机器
# 对比:
# | 操作 | 驱逐 Pod | 新调度 | 已运行 Pod | 场景 |
# |-------|---------|--------|-------------------|--------------------|
# | cordon| 否 | 禁止 | 正常运行 | 临时停调、排障 |
# | drain | 是 | 禁止 | 全部驱逐 | 节点下线、维护升级 |
# 完整节点维护流程:
kubectl drain node1 --ignore-daemonsets --delete-emptydir-data
# 执行维护操作...
kubectl uncordon node1
- podAffinity 和 podAntiAffinity 的应用场景?
# podAffinity(Pod 亲和性)
# 让 Pod 调度到"与特定 Pod 位于同一拓扑域"的节点上
# 场景:
# 1. 缓存服务和 Web 服务部署在一起(减少延迟)
# 2. 前后端服务部署在同一节点/可用区
spec:
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- redis-cache
topologyKey: "kubernetes.io/hostname" # 同一节点
# podAntiAffinity(Pod 反亲和性)
# 让 Pod 调度到"不与特定 Pod 位于同一拓扑域"的节点上
# 场景:
# 1. 高可用部署:同应用多副本分散到不同节点/可用区
# 2. 避免资源竞争:将资源密集型 Pod 分开
# 3. 故障隔离:避免单点故障影响多个副本
spec:
affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- nginx
topologyKey: "kubernetes.io/hostname"
# 常用 topologyKey:
# kubernetes.io/hostname → 同一节点
# topology.kubernetes.io/zone → 同一可用区
# topology.kubernetes.io/region → 同一区域
- topologyKey 的作用是什么?
# topologyKey 的作用
# 定义了 Pod 亲和性/反亲和性中的"拓扑域"边界。
# 具有相同 topologyKey 值(同一个 Node Label 值)的节点属于同一拓扑域。
# 工作原理:
# 当计算 podAffinity 时,K8s 会找"目标 Pod 所在节点的 topologyKey 标签值",
# 然后将"具有相同标签值的节点"都视为同一拓扑域。
# 如果找不到任何节点满足条件,Pod 调度失败(required 模式)。
# 常用 topologyKey 值:
# kubernetes.io/hostname — 同节点(每个节点的 hostname 唯一)
# topology.kubernetes.io/zone — 同可用区(云环境,如 us-east-1a)
# topology.kubernetes.io/region — 同区域
# failure-domain.beta.kubernetes.io/zone — 旧版标签(兼容)
# 示例 1:限制 Web 和缓存在同一节点
# podAffinity + topologyKey: kubernetes.io/hostname
# 示例 2:确保 MySQL 副本分散到不同节点(反亲和)
# podAntiAffinity + topologyKey: kubernetes.io/hostname
# 示例 3:限制 Kafka 副本在不同可用区(高可用)
# podAntiAffinity + topologyKey: topology.kubernetes.io/zone
# 查看节点相关标签:
kubectl get nodes --show-labels | grep topology
5.6 配置与存储
- ConfigMap 和 Secret 分别做什么?如何挂载到 Pod 中?
# ConfigMap
# 存储非敏感配置数据(key-value 形式),如:配置文件、环境变量、命令行参数
# 最大 1MB(etcd 限制),明文存储
# Secret
# 存储敏感数据,如:密码、Token、密钥、TLS 证书
# 数据以 base64 编码存储(注意:base64 不是加密,默认不加密需额外安全措施)
# 最大 1MB
# ConfigMap 挂载方式(4 种):
# 方式 1:环境变量(指定 key)
spec.containers[].env:
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: app-config
key: database.host
# 方式 2:envFrom 批量注入所有 key
spec.containers[].envFrom:
- configMapRef:
name: app-config
# 方式 3:Volume 挂载为文件(最常用,支持热更新)
spec.volumes:
- name: config
configMap:
name: app-config
spec.containers[].volumeMounts:
- name: config
mountPath: /etc/app/config
# 方式 4:命令行参数
spec.containers[].command:
- /bin/app
- --config=/etc/app/config.yaml
# Secret 挂载方式与 ConfigMap 相同(env/valueFrom/volumeMount)
# 注意:Secret 用 Volume 挂载时自动解密(tmpfs 内存文件系统)
# 创建示例:
kubectl create configmap app-config --from-file=app.properties --from-literal=ENV=prod
kubectl create secret generic db-secret --from-literal=password=MyP@ssw0rd
kubectl create secret docker-registry my-registry --docker-server=... --docker-username=... --docker-password=...
kubectl create secret tls tls-secret --cert=cert.pem --key=key.pem
- Secret 的三种类型:Opaque、dockerconfigjson、tls 分别用于什么场景?
# Secret 三种主要类型
# 1. Opaque(默认类型,通用密钥)
# 存储任意键值对,数据是 base64 编码的
# 场景:数据库密码、API Key、应用密钥
kubectl create secret generic db-credentials \
--from-literal=username=admin \
--from-literal=password=S3cr3t!
# 查看(base64 解码):kubectl get secret db-credentials -o jsonpath='{.data.password}' | base64 -d
# 2. kubernetes.io/dockerconfigjson(镜像仓库认证)
# 存储 Docker 私有仓库的认证信息(~/.docker/config.json 的内容)
# 场景:从私有镜像仓库拉取镜像时提供认证
kubectl create secret docker-registry regcred \
--docker-server=my-registry.example.com \
--docker-username=admin \
--docker-password=password123 \
--docker-email=admin@example.com
# Pod 中引用:
spec:
imagePullSecrets:
- name: regcred
# 3. kubernetes.io/tls(TLS 证书)
# 存储 TLS 私钥和证书(PEM 格式),key 必须是 tls.key 和 tls.crt
# 场景:Ingress TLS 终止、服务间 mTLS
kubectl create secret tls tls-cert \
--cert=server.crt \
--key=server.key
# 验证:openssl x509 -in <(kubectl get secret tls-cert -o jsonpath='{.data.tls\.crt}' | base64 -d) -noout -text
# 其他类型:
# kubernetes.io/service-account-token — ServiceAccount Token(自动创建)
# kubernetes.io/basic-auth — 基本认证凭据
# kubernetes.io/ssh-auth — SSH 认证凭据
# bootstrap.kubernetes.io/token — Bootstrap Token
- ConfigMap 更新后,Pod 中的配置会自动更新吗?
# 答案:取决于挂载方式
# 1. Volume 挂载为文件 → 会自动更新(有延迟)
# 挂载到 Pod 内文件系统中的 ConfigMap/Secret 会自动同步更新
# 但 K8s kubelet 同步有延迟(默认约 60-90 秒,由 kubelet syncFrequency 控制)
# 注意:如果 subPath 挂载,不会自动更新!
# 注意:应用进程可能不会自动重新加载配置文件
# 2. 环境变量注入 → 不会更新
# 环境变量只在 Pod 创建时注入一次,更新 ConfigMap 不影响已运行的 Pod
# 必须重启/重建 Pod 才能生效
# 3. 命令行参数 → 不会更新(同环境变量)
# 测试自动更新:
# 更新 ConfigMap
kubectl edit cm app-config
# 等待一段时间后查看 Pod 内文件
kubectl exec <pod> -- cat /etc/app/config/database.host
# 查看文件更新时间(确认已同步)
kubectl exec <pod> -- ls -la /etc/app/config/
# 最佳实践:
# 1. 应用支持动态重载配置(如 SIGHUP 信号 / 监听文件变化)
# 2. 使用 deploy 工具(如 Reloader)检测 ConfigMap 变更自动滚动重启 Pod
# 3. 生产环境建议:配置变更 → 滚动更新 Deployment 确保一致性
# 4. 使用 immutable ConfigMap 可以避免意外修改
5.7 资源管理
- Requests 和 Limits 的区别?如果不设置会怎样?
# Requests(请求值)
# 容器"保证"能获得的最小资源量
# Scheduler 根据 Requests 的总和决定将 Pod 调度到哪个节点
# 容器运行时至少分配这么多资源
spec.containers[].resources.requests:
cpu: "500m" # 500 毫核 = 0.5 CPU
memory: "256Mi"
# Limits(限制值)
# 容器"最多"能使用的资源量(上限)
# CPU 达到 Limit:被 CPU throttling(限流),不会被 Kill
# 内存达到 Limit:触发 OOMKill,容器被终止(restartPolicy OnFailure)
spec.containers[].resources.limits:
cpu: "1000m" # 最多使用 1 个 CPU
memory: "512Mi" # 最多使用 512MiB 内存
# 不设置会怎样?
# 1. 资源不受控制:容器可无限使用节点资源,影响其他 Pod
# 2. Scheduler 无法合理调度:不知道 Pod 需要多少资源,可能调度到资源不足的节点
# 3. QoS 等级降低:成为 BestEffort(最低优先级,OOM 时最先被 Kill)
# 4. HPA 无法工作:HPA 需要设置 Requests 才能计算利用率
# 生产环境最佳实践:
# 1. CPU Requests = Limits(或接近),CPU 是可压缩资源
# 过高的 Limits 会导致不必要的 throttling 影响性能
# 2. Memory Requests = Limits,内存不可压缩,避免 OOMKilled
# 3. 设置合理的资源不要过度分配(overcommit)
- ResourceQuota 和 LimitRange 的作用分别是什么?
# ResourceQuota(资源配额)
# 限制 Namespace 级别的资源使用总量
# 防止某个 Namespace 过度消耗集群资源
# 可以限制:
# - 计算资源:CPU 总量、内存总量
# - 存储资源:PVC 总数量、总存储量
# - 对象数量:Pod/Service/ConfigMap/Secret/Deployment 数量
# - Scope:仅 BestEffort / NotBestEffort / Terminating / NotTerminating
apiVersion: v1
kind: ResourceQuota
metadata:
name: compute-quota
namespace: dev-team
spec:
hard:
requests.cpu: "20"
requests.memory: 40Gi
limits.cpu: "40"
limits.memory: 80Gi
persistentvolumeclaims: "10"
pods: "50"
services: "10"
# LimitRange(范围限制)
# 限制 Namespace 内单个 Pod/Container/PVC 的资源最大值和最小值
# 防止创建超大或超小的资源
# 可以设置:容器的默认 Requests/Limits、最小值、最大值、最大最小比例
apiVersion: v1
kind: LimitRange
metadata:
name: container-limits
namespace: dev-team
spec:
limits:
- type: Container
default: # 容器默认 Limits(不设置时使用)
cpu: "500m"
memory: "512Mi"
defaultRequest: # 容器默认 Requests
cpu: "250m"
memory: "256Mi"
min: # 最小限制
cpu: "100m"
memory: "128Mi"
max: # 最大限制
cpu: "2"
memory: "4Gi"
# 总结:
# ResourceQuota → 限制整个 Namespace 总量(宏观)
# LimitRange → 限制单个 Pod/容器 的资源范围(微观)
# 两者配合使用,形成 Namespace 完整的资源管控体系
- QoS 的三种等级(Guaranteed、Burstable、BestEffort)如何确定?
# QoS(Quality of Service)等级由 K8s 根据 Pod 中容器的资源设置自动划分
# 影响 OOM 时的 Kill 优先级:BestEffort > Burstable > Guaranteed
# 1. Guaranteed(保证级)—— 最高优先级,最后被 Kill
# 条件:Pod 中的每个容器都必须同时满足:
# - 设置了 Memory Requests 和 Memory Limits
# - 设置了 CPU Requests 和 CPU Limits
# - Requests == Limits(内存和 CPU 都要等值)
spec:
containers:
- name: app
resources:
requests:
cpu: "500m"
memory: "512Mi"
limits:
cpu: "500m"
memory: "512Mi"
# 2. Burstable(弹性级)—— 中等优先级
# 条件:至少有一个容器设置了 Requests(CPU 或内存)
# 但至少有一个容器不满足 Guaranteed 条件
spec:
containers:
- name: app
resources:
requests:
memory: "256Mi"
limits:
memory: "512Mi" # Requests != Limits,所以是 Burstable
# 3. BestEffort(尽力级)—— 最低优先级,最先被 Kill
# 条件:没有任何容器设置 Requests 或 Limits
# Pod 在资源紧张时最先被驱逐
# 生产环境强烈不建议使用!
# 查看 Pod QoS 等级:
kubectl get pod <pod-name> -o jsonpath='{.status.qosClass}'
kubectl describe pod <pod-name> | grep "QoS Class"
# OOM Score 计算(简化):
# Guaranteed: 低 oom_score,最后被杀
# Burstable: 中等 oom_score,按超出 Requests 比例 Kill
# BestEffort: 高 oom_score,最先被杀
- OOMKilled 是什么原因导致的?怎么排查?
# OOMKilled 是什么?
# 容器的内存使用超过了 Memory Limits,Linux 内核的 OOM Killer 终止了容器进程
# exit code = 137(128 + SIGKILL 信号 9)
# 常见原因:
# 1. Memory Limits 设置太小,实际内存需求超过限制
# 2. 内存泄漏(应用 bug 导致内存持续增长)
# 3. 突发流量导致内存飙升
# 4. 没设置 Memory Limits(但节点内存紧张时仍可能被内核 Kill)
# 排查思路:
# 步骤 1:确认 OOMKilled
kubectl describe pod <pod-name> | grep -A5 "State"
# 输出:State: Terminated, Reason: OOMKilled, Exit Code: 137
# 步骤 2:查看 Pod 事件
kubectl get events --field-selector involvedObject.name=<pod-name> -n <ns>
# 步骤 3:查看历史资源使用
kubectl top pod <pod-name> -n <ns>
# 如果 Pod 已不存在,查看历史指标(Prometheus/Grafana)
# 步骤 4:查看容器日志(最后时刻)
kubectl logs <pod-name> -n <ns> --previous # 查看上一个容器的日志
# 步骤 5:检查资源配置是否合理
kubectl get pod <pod-name> -o yaml | grep -A10 resources
# 步骤 6:使用 PromQL 分析(如有 Prometheus)
# container_memory_working_set_bytes{pod="<pod-name>"}
# 查看内存使用趋势,找到泄漏或突增时间点
# 解决方案:
# 1. 调大 Memory Limits
# 2. 修复应用内存泄漏
# 3. 使用 HPA 分担负载
# 4. 设置合理的 Requests == Limits(获得 Guaranteed QoS)
# 5. 添加 liveness probe 在内存异常前重启
5.8 健康检查
- LivenessProbe、ReadinessProbe、StartupProbe 各有什么作用?区别是什么?
# ==================== 三种探针 ====================
# 1. LivenessProbe(存活探针)
# 检查"容器是否还活着"(是否正常运行)
# 失败 → kubelet 杀掉容器,根据 restartPolicy 重启
# 场景:检测死锁、应用卡住但不退出
# 2. ReadinessProbe(就绪探针)
# 检查"容器是否准备好接收流量"
# 失败 → Pod 从 Service 的 Endpoints 中移除,不再接收流量
# 成功 → 重新加入 Endpoints
# 场景:应用启动慢、依赖服务未就绪、暖机(warmup)
# 3. StartupProbe(启动探针)—— v1.16 beta,v1.20 stable
# 检查"容器应用是否已完成启动"
# 在 StartupProbe 成功之前,LivenessProbe 和 ReadinessProbe 不会执行
# 失败 → 同 LivenessProbe(杀容器重启)
# 场景:启动特别慢的应用(避免 Liveness 过早介入导致误杀)
# ==================== 关键区别 ====================
# | 探针 | 失败后果 | 典型场景 |
# |----------------|-------------------------------|-----------------------------|
# | LivenessProbe | 杀容器 + 重启 | 死锁、应用卡住 |
# | ReadinessProbe | 从 Endpoints 移除,停止流量 | 应用未就绪、依赖未建连 |
# | StartupProbe | 杀容器 + 重启 | 启动慢,保护 Liveness 探针 |
# 时序关系:
# Pod Start → StartupProbe(如有)→ 成功 → LivenessProbe + ReadinessProbe 开始
# → 失败 → 杀容器重启(重置计数)
# 生产环境建议:
# 1. 启动慢的应用一定加 StartupProbe
# 2. ReadinessProbe 的 failureThreshold 可以比 LivenessProbe 高
# (允许临时不健康但不杀 Pod)
# 3. LivenessProbe 不要依赖外部服务(避免级联故障)
- 探针的三种检测方式(httpGet、exec、tcpSocket)各适用于什么场景?
# 1. httpGet —— 最常用
# 向容器的指定路径发起 HTTP GET 请求,2xx/3xx 状态码 = 健康
# 场景:Web 服务、API 服务、带 HTTP 接口的任何服务
spec:
containers:
- name: nginx
livenessProbe:
httpGet:
path: /healthz
port: 8080
httpHeaders: # 可选自定义 Header
- name: X-Custom-Header
value: Awesome
readinessProbe:
httpGet:
path: /ready
port: 8080
# 2. tcpSocket —— 网络层检测
# 对指定端口发起 TCP 连接,连接成功 = 健康(不进行三次握手之外的操作)
# 场景:没有 HTTP 接口的服务(如数据库、Redis、gRPC)
spec:
containers:
- name: redis
livenessProbe:
tcpSocket:
port: 6379
initialDelaySeconds: 15
# 3. exec —— 自定义命令检测
# 在容器内执行命令,命令返回码 0 = 健康
# 场景:复杂的健康检查逻辑、gRPC 服务(grpc_health_probe)、文件检查
spec:
containers:
- name: app
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
readinessProbe:
exec:
command:
- /usr/local/bin/grpc_health_probe
- -addr=:50051
# 通用配置参数:
# initialDelaySeconds: 启动后等 N 秒才开始探测(有 StartupProbe 后可省略)
# periodSeconds: 每隔 N 秒探测一次
# timeoutSeconds: 单次探测超时时间
# failureThreshold: 连续失败 N 次才算失败
# successThreshold: 连续成功 N 次才算成功
- ReadinessProbe 和 LivenessProbe 的失败分别会导致什么结果?
# ReadinessProbe 失败的结果:
# 1. Pod 的 Ready 状态变为 False
# 2. Pod 从对应 Service 的 Endpoints 中移除(不接收流量)
# 3. 容器不会重启!!!Pod 继续运行
# 4. 恢复后(连续成功 successThreshold 次),重新加入 Endpoints 接收流量
# 关键理解:Readiness 失败不代表容器有问题,只是"暂时不适合接收流量"
# LivenessProbe 失败的结果:
# 1. kubelet 认定容器不健康
# 2. 杀掉该容器(发送 SIGTERM → 等 terminationGracePeriodSeconds → SIGKILL)
# 3. 根据 Pod 的 restartPolicy 创建新容器(Always / OnFailure)
# 4. restartCount +1,容器重新经历 Startup → Readiness 流程
# 关键理解:Liveness 失败是致命的,意味容器无法自愈,只能重启
# 两者失败的链式影响:
# Readiness 失败:只有流量层受影响(Service 不会转发给不 ready 的 Pod)
# Liveness 失败:容器被重启,所有状态丢失,若有 PVC 则数据可保留
# 示例:不当配置导致的级联故障
# 如果 LivenessProbe 检查数据库连接:
# 数据库短暂故障 → 所有 Pod LivenessProbe 失败 → 全部重启
# → 重启后仍需连接数据库 → 可能雪崩
# 正确做法:Liveness 检查进程存活,Readiness 检查数据库连接
initialDelaySeconds、periodSeconds、failureThreshold各控制什么?
# 探针通用配置参数说明
# initialDelaySeconds(初始延迟秒数)
# 容器启动后等待多少秒才开始第一次探测
# 用来给应用启动留时间(避免启动未完成就探测导致失败)
# 默认 0 秒
# 如果有 StartupProbe,Liveness/Readiness 的此参数可设小或不设
# periodSeconds(探测间隔秒数)
# 两次探测之间的间隔时间
# 默认 10 秒
# 太短:增加系统负载;太长:故障检测延迟大
# timeoutSeconds(超时秒数)
# 单次探测的超时时间
# 默认 1 秒
# httpGet/tcpSocket/exec 如果超过此时间未响应,视为失败
# failureThreshold(失败阈值)
# 连续多少次失败后,才认定探测失败
# LivenessProbe:默认 3 次 → 连续失败 3 次才重启容器
# ReadinessProbe:默认 3 次 → 连续失败 3 次才从 Endpoints 移除
# 实际触发时间 ≈ periodSeconds × failureThreshold
# successThreshold(成功阈值)
# 连续多少次成功后,才认定探测成功/恢复
# LivenessProbe:默认 1 次(固定不可改)
# ReadinessProbe:默认 1 次
# 配置示例(生产环境推荐):
spec:
containers:
- name: app
startupProbe:
httpGet:
path: /healthz
port: 8080
initialDelaySeconds: 0 # 立即开始探测启动状态
periodSeconds: 5 # 每 5 秒探测一次
failureThreshold: 20 # 最多等 5×20=100 秒启动
livenessProbe:
httpGet:
path: /healthz
port: 8080
periodSeconds: 10 # 每 10 秒探测
timeoutSeconds: 3 # 3 秒超时
failureThreshold: 3 # 连续 3 次失败即重启(30s 内)
readinessProbe:
httpGet:
path: /ready
port: 8080
periodSeconds: 5 # 每 5 秒探测
failureThreshold: 2 # 连续 2 次失败就摘流量(快速摘除)
5.9 自动扩缩容
- HPA(Horizontal Pod Autoscaler)的原理是什么?支持哪些指标?
# HPA(Horizontal Pod Autoscaler)原理
# 1. HPA Controller 定期(默认 15s)从 Metrics API 获取 Pod 指标
# 2. 计算当前指标值和目标值的比率:desiredReplicas = ceil(currentReplicas * (currentMetricValue / targetMetricValue))
# 3. 根据计算结果自动调整 Deployment/StatefulSet/ReplicaSet 的副本数
# 4. 有缩容冷却窗口(默认 5min)和扩容冷却窗口(默认 3min,但 3min 内可继续扩)
# HPA 支持的指标:
# 1. Resource Metrics(资源指标)
# CPU 使用率、Memory 使用率(基于 requests 的百分比)
spec:
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70 # 目标 CPU 使用率 70%
# 2. Pod Metrics(自定义 Pod 指标)
# Pod 级别的自定义指标(如 QPS、请求延迟)
spec:
metrics:
- type: Pods
pods:
metric:
name: http_requests_per_second
target:
type: AverageValue
averageValue: "100" # 每个 Pod 平均 100 QPS
# 3. Object Metrics(对象指标)
# K8s 对象级别的指标(如 Ingress 的 QPS)
spec:
metrics:
- type: Object
object:
metric:
name: requests-per-second
describedObject:
apiVersion: networking.k8s.io/v1
kind: Ingress
name: main-ingress
target:
type: Value
value: "1000"
# 4. External Metrics(外部指标)
# 集群外部的指标(如云负载均衡的 QPS、消息队列长度)
spec:
metrics:
- type: External
external:
metric:
name: queue_messages_ready
selector:
matchLabels:
queue: worker-queue
target:
type: AverageValue
averageValue: "30"
# 创建 HPA:
kubectl autoscale deployment nginx --cpu-percent=70 --min=2 --max=10
# 完整 YAML:
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: nginx-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: nginx
minReplicas: 2
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- VPA 和 HPA 的区别?能不能同时开启?
# HPA(Horizontal Pod Autoscaler)—— 横向扩缩
# 调整 Pod 副本数量(replicas)
# 场景:无状态服务(Web/API),增加实例数分担负载
# 公式:副本数 × 单 Pod 容量 = 总容量
# VPA(Vertical Pod Autoscaler)—— 纵向扩缩
# 调整 Pod 的资源 Requests/Limits(不改变副本数)
# 场景:有状态服务(数据库)、单体应用
# 三种模式:
# - Off: 只推荐值,不执行
# - Initial: 只在 Pod 创建时设值
# - Auto: 自动调整并重启 Pod
# HPA vs VPA 对比
# | 特性 | HPA | VPA |
# |-----------|----------------------|---------------------|
# | 调整对象 | 副本数 | CPU/内存 Requests/Limits |
# | 对应用影响 | 几乎无感知(水平扩) | 需要重启 Pod |
# | 适用架构 | 分布式/无状态 | 有状态/单体 |
# | 弹性速度 | 快(秒级) | 慢(分级别) |
# | 成熟度 | GA | Beta |
# 能不能同时开启 HPA 和 VPA?
# 可以,但有限制:
# 1. HPA 用 Resource Metrics(CPU/Mem),VPA 不可也用 CPU/Mem 的 Auto 模式
# → 因为 VPA 修改了 Requests 后,HPA 的利用率计算基准会变化,互相干扰
# 2. 推荐组合:HPA(资源指标)+ VPA(自定义指标,仅推荐 Off 模式)
# 3. 或者:HPA 用 CPU/Mem,VPA 用 Off 模式只生成推荐值,人工审批执行
# 4. 社区有 KEDA(事件驱动)+ HPA 替代 VPA 的趋势
# 查看 VPA 推荐值(如已安装):
kubectl get vpa <vpa-name> -o yaml | grep -A10 recommendation
- HPA 的缩容稳定窗口期默认是多少?扩容冷却时间呢?
# HPA 关键时间参数(可通过 behavior 字段自定义)
# 缩容稳定窗口期(Downscale Stabilization Window)
# 默认:5 分钟(300 秒)
# 含义:HPA 在缩容前回顾过去 5 分钟的指标推荐值,取"最大值"作为实际缩容目标
# 作用:防止指标短期波动导致频繁缩容→扩容(抖动)
# 举例:突然流量下降 → 等 5 分钟确认 → 确实持续低 → 才缩容
# 扩容冷却时间(Upscale Cooldown)
# 默认:0 秒(无冷却,但扩容后有 3 分钟的隐性稳定期)
# 含义:上次扩容后至少过 3 分钟才能再次扩容
# 作用:给新 Pod 的指标一个稳定的窗口期
# 缩容冷却时间(Downscale Cooldown)
# 默认:5 分钟
# 含义:上次缩容后至少过 5 分钟才能再次缩容
# 通过 behavior 自定义(k8s v1.18+):
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
spec:
behavior:
scaleDown:
stabilizationWindowSeconds: 300 # 缩容稳定窗口(默认 300)
policies:
- type: Percent
value: 10 # 每 60 秒最多缩 10%
periodSeconds: 60
- type: Pods
value: 2 # 每 60 秒最多缩 2 个 Pod
periodSeconds: 60
selectPolicy: Min # 选限制更严格的(缩更少)
scaleUp:
stabilizationWindowSeconds: 0 # 扩容稳定窗口(默认 0)
policies:
- type: Percent
value: 100 # 每 60 秒最多扩 100%(翻倍)
periodSeconds: 60
- type: Pods
value: 4 # 每 60 秒最多扩 4 个
periodSeconds: 60
selectPolicy: Max # 选限制更宽松的(扩更多)
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
5.10 认证授权
- Kubernetes 中的用户分为哪两类?(普通用户 vs 服务账号)
# Kubernetes 用户分为两大类:
# 1. 普通用户(Normal User / Human User)
# - 人类操作者(管理员、开发者)
# - 由外部身份系统管理(K8s 本身不管理用户)
# - 认证方式:X509 客户端证书、Bearer Token、OIDC、Webhook
# - 没有对应的 K8s API 对象(不能用 kubectl get users)
# 常见管理方式:
# - 静态 Token 文件(--token-auth-file)
# - X509 证书(通过 kubeconfig 使用)
# - OpenID Connect(OIDC)集成(对接 LDAP/AD)
# - Webhook 认证(自定义认证服务)
# 2. 服务账号(ServiceAccount)
# - Pod 内运行的应用程序使用(非人类身份)
# - 由 K8s API 管理(有对应的 API 对象)
# - 存储在 Namespace 内(每个 NS 有默认 default SA)
# - 认证方式:自动挂载到 Pod 的 Token(/var/run/secrets/kubernetes.io/serviceaccount/token)
# 查看和使用:
kubectl get serviceaccounts -n <ns>
kubectl describe sa default -n <ns>
# | 特性 | 普通用户 | ServiceAccount |
# |-----------|----------------------|----------------------|
# | 管理方式 | 外部系统 | K8s API |
# | 范围 | 集群级别 | Namespace 级别 |
# | 对象 | 无 API 对象 | 有 API 对象 |
# | 使用场景 | 人工操作 kubectl | Pod 内程序调用 API |
- Role 和 ClusterRole 的区别?RoleBinding 和 ClusterRoleBinding 的区别?
# Role vs ClusterRole
# | 特性 | Role | ClusterRole |
# |---------|--------------------------------|----------------------------|
# | 作用域 | Namespace 级别 | 集群级别 |
# | 适用资源 | Namespace 资源(Pod/Deploy 等) | 所有资源(含 Node/PV 等集群资源)|
# | 典型场景 | 某 NS 开发者读写权限 | 集群管理员、只读用户 |
# Role 示例(default NS 读写 Pod)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: default
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
# ClusterRole 示例(集群只读)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: cluster-reader
rules:
- apiGroups: [""]
resources: ["*"]
verbs: ["get", "list", "watch"]
# RoleBinding vs ClusterRoleBinding
# 共同点:都是将角色(Role/ClusterRole)绑定到主体(用户/组/SA)
# 区别:
# RoleBinding 在 Namespace 内生效,可绑定 Role 或 ClusterRole
# ClusterRoleBinding 在集群级别生效,只能绑定 ClusterRole
# 重要技巧:用 RoleBinding 绑定 ClusterRole 到某个 Namespace
# 效果:用户在这个 Namespace 获得 ClusterRole 的权限,但在其他 NS 没有
# 场景:用同一个 ClusterRole 模板,通过 RoleBinding 授予不同 NS 的权限
# 好处:不需要在每个 NS 都创建一个同名 Role(复用 ClusterRole)
- RBAC 的核心三要素是什么?(Subject、Role/ClusterRole、RoleBinding/ClusterRoleBinding)
# RBAC(Role-Based Access Control)核心三要素
# 1. Subject(主体)—— 谁?
# 被授权的主体:
# - User(普通用户,X509 CN / Token User)
# - Group(用户组,X509 O 字段)
# - ServiceAccount(服务账号)
subjects:
- kind: User
name: "zhangsan"
apiGroup: rbac.authorization.k8s.io
- kind: ServiceAccount
name: default
namespace: kube-system
# 2. Resource + Verb(资源 + 操作)—— 能做什么?
# 定义在 Role / ClusterRole 中
# - Resource: pods, deployments, services, nodes...
# - Verb: get, list, watch, create, update, patch, delete, deletecollection
# - APIGroup: "", apps, batch, networking.k8s.io...
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list", "watch"]
# 3. Binding(绑定)—— 在哪些范围?
# - RoleBinding:Namespace 级别绑定
# - 将 Role/ClusterRole 绑定到 Subject → 在特定 NS 生效
# - ClusterRoleBinding:集群级别绑定
# - 将 ClusterRole 绑定到 Subject → 集群范围生效
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: view
# 关联关系:
# Subject(User/SA/Group)
# + RoleBinding/ClusterRoleBinding(绑定)
# + Role/ClusterRole(权限定义)
# → Subject 获得 Role 中定义的权限
# 验证权限:
kubectl auth can-i create deployments --as=zhangsan -n default
kubectl auth can-i get pods --as=system:serviceaccount:default:my-sa
- ServiceAccount 是做什么的?Pod 如何使用 SA 的 Token 访问 API Server?
# ServiceAccount 是做什么的?
# SA 是 Pod 在集群内的"身份",让 Pod 内的进程能以特定身份访问 K8s API Server。
# 每个 Namespace 有默认的 default SA。
# SA 由两部分组成:
# 1. SA 对象本身(资源对象)
# 2. 关联的 Secret(Token),用于认证
# Pod 如何使用 SA 的 Token 访问 API Server?
# 方式 1:自动挂载(默认方式,v1.24 前)
# Pod 创建时,kubelet 自动将 SA 的 Token 挂载到 Pod 内
# 挂载路径:/var/run/secrets/kubernetes.io/serviceaccount/
# 文件:
# token → JWT Token(认证用)
# ca.crt → API Server CA 证书(验证 API Server 身份)
# namespace → 当前 Namespace 名
# Pod 内测试访问 API Server:
TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
CA_CERT=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt
NAMESPACE=$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace)
APISERVER=https://kubernetes.default.svc
# 使用 curl 访问 API
curl -s --cacert $CA_CERT -H "Authorization: Bearer $TOKEN" \
$APISERVER/api/v1/namespaces/$NAMESPACE/pods
# 指定 Pod 使用的 SA:
spec:
serviceAccountName: my-sa # 指定 SA(否则用 default)
automountServiceAccountToken: true # 自动挂载(默认 true)
# 方式 2:绑定 ServiceAccount Token Volume(v1.22+ / v1.24 GA)
# 使用 projected volume 注入有时间限制的 Token(更安全)
spec:
volumes:
- name: token
projected:
sources:
- serviceAccountToken:
path: token
expirationSeconds: 3600 # 1 小时后过期
# 方式 3:禁用自动挂载(安全最佳实践)
spec:
automountServiceAccountToken: false # 不需要访问 API Server 时禁用
- 如何创建一个用户并给他某个 namespace 的只读权限?
# 场景:创建用户 developer,授予 dev namespace 的只读权限
# 步骤 1:生成用户证书(模拟一个新用户)
# 生成私钥
openssl genrsa -out developer.key 2048
# 生成证书签名请求(CSR),CN=用户名,O=组名
openssl req -new -key developer.key -out developer.csr -subj "/CN=developer/O=dev-team"
# 用 K8s CA 签发证书(需要集群 CA 证书和私钥)
openssl x509 -req -in developer.csr \
-CA /etc/kubernetes/pki/ca.crt \
-CAkey /etc/kubernetes/pki/ca.key \
-CAcreateserial -out developer.crt -days 365
# 步骤 2:创建 kubeconfig 文件(配置用户凭据)
kubectl config set-credentials developer \
--client-certificate=developer.crt \
--client-key=developer.key \
--embed-certs=true \
--kubeconfig=developer.kubeconfig
# 步骤 3:设置上下文(集群 + 用户 + namespace)
kubectl config set-context developer-context \
--cluster=kubernetes \
--user=developer \
--namespace=dev \
--kubeconfig=developer.kubeconfig
# 步骤 4:创建 Role(dev NS 只读权限)
kubectl create role dev-reader \
--verb=get,list,watch \
--resource=pods,deployments,services,configmaps,secrets \
-n dev
# 或使用 ClusterRole view(K8s 内置只读角色)
# kubectl get clusterrole view -o yaml
# 步骤 5:绑定 Role 到用户(RoleBinding)
kubectl create rolebinding dev-reader-binding \
--role=dev-reader \
--user=developer \
-n dev
# 或者绑定内置 view ClusterRole(更简便)
kubectl create rolebinding dev-view-binding \
--clusterrole=view \
--user=developer \
-n dev
# 步骤 6:验证权限
kubectl auth can-i get pods --as=developer -n dev # yes
kubectl auth can-i create pods --as=developer -n dev # no
kubectl auth can-i get pods --as=developer -n kube-system # no
# 步骤 7:将 kubeconfig 交给用户
# developer.kubeconfig 文件可直接使用
# kubectl --kubeconfig=developer.kubeconfig get pods -n dev
# 快捷方式(使用已有用户证书):
kubectl config view --kubeconfig=developer.kubeconfig
# 生产环境建议:使用 OIDC(对接 LDAP/AD)而非证书认证,更易管理
- X509 客户端证书认证的流程是怎样的?
# X509 客户端证书认证流程
# 1. 用户创建证书签名请求(CSR)
# CN(Common Name)= 用户名
# O(Organization)= 用户组
openssl req -new -key user.key -out user.csr -subj "/CN=zhangsan/O=dev-team"
# 2. 集群 CA 签发证书
openssl x509 -req -in user.csr -CA /etc/kubernetes/pki/ca.crt \
-CAkey /etc/kubernetes/pki/ca.key -CAcreateserial \
-out user.crt -days 365
# 3. 用户使用签发后的证书访问 API Server
# curl 方式:
curl https://<apiserver>:6443/api/v1/pods \
--cert user.crt --key user.key --cacert ca.crt
# 4. API Server 认证过程:
# a) 验证客户端证书是否由受信任的 CA 签发(验证证书链)
# b) 检查证书是否在有效期内(NotBefore ~ NotAfter)
# c) 提取 CN 作为用户名、O 作为用户组
# d) 认证通过后,将用户名和组传递给授权模块(RBAC 检查)
# 5. 授权(RBAC 检查):
# 根据 CN(用户名/O(用户组)查找对应的 RoleBinding/ClusterRoleBinding
# 确定用户是否有权限执行该操作
# 证书认证的优缺点:
# 优点:证书由集群 CA 签发,安全可靠,无需额外组件
# 缺点:
# - 难以吊销(除非 CRL/OCSP,K8s 支持有限)
# - 无法动态管理用户(需重新签发证书)
# - 大规模用户管理困难
# - 证书过期后需重新签发和分发
# 查看 API Server 启动参数:
# --client-ca-file=/etc/kubernetes/pki/ca.crt # 客户端证书的 CA
# 生产环境建议:X509 适用于小组集群或 CI/CD;大规模推荐 OIDC
5.11 集群搭建与管理
- 用 kubeadm 部署 K8s 集群的完整流程?(7 个关键步骤)
# ==================== 用 kubeadm 部署 K8s 集群(完整流程) ====================
# === 所有节点(Master + Worker) ===
# 1. 系统基础配置
# 关闭 swap
swapoff -a
sed -i '/swap/d' /etc/fstab
# 关闭 SELinux(或设为 permissive)
setenforce 0
sed -i 's/^SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config
# 关闭防火墙(或开放必要端口)
systemctl stop firewalld && systemctl disable firewalld
# 配置内核参数
cat <<EOF > /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables=1
net.bridge.bridge-nf-call-ip6tables=1
net.ipv4.ip_forward=1
EOF
sysctl --system
# 2. 安装容器运行时(containerd)
yum install -y yum-utils
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
yum install -y containerd.io
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
# 修改 SystemdCgroup = true(cgroup 驱动一致)
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl enable --now containerd
# 3. 安装 kubeadm、kubelet、kubectl
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
EOF
yum install -y kubeadm-1.28.0 kubelet-1.28.0 kubectl-1.28.0
systemctl enable kubelet # 先 enable,等 init 后会自动启动
# === Master 节点 ===
# 4. kubeadm init(初始化集群)
kubeadm init \
--apiserver-advertise-address=10.1.8.10 \
--image-repository registry.aliyuncs.com/google_containers \
--pod-network-cidr=10.244.0.0/16 \
--kubernetes-version=v1.28.0
# 5. 配置 kubectl(管理员使用)
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
# 6. 安装 CNI 网络插件
# Flannel:
kubectl apply -f https://raw.githubusercontent.com/flannel-io/flannel/master/Documentation/kube-flannel.yml
# 或 Calico:
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/calico.yaml
# === Worker 节点 ===
# 7. 加入集群(使用 init 输出的 join 命令)
kubeadm join 10.1.8.10:6443 \
--token <token> \
--discovery-token-ca-cert-hash sha256:<hash>
# === 验证集群 ===
kubectl get nodes
kubectl get pods -A
# 所有节点 Ready,所有 Pod Running,集群部署完成
- kubeadm init 之前需要做哪些前置环境准备?
# kubeadm init 前置环境准备清单(所有节点)
# 1. 主机名和 hosts
hostnamectl set-hostname master01 # 每节点不同
cat >> /etc/hosts <<EOF
10.1.8.10 master01
10.1.8.11 worker01
10.1.8.12 worker02
EOF
# 2. 关闭 swap(必须)
swapoff -a
sed -i '/swap/s/^/#/' /etc/fstab
# kubelet 要求关闭 swap(v1.28+ 开始支持 swap 但有限制)
# 3. 关闭 SELinux
setenforce 0
sed -i 's/SELINUX=enforcing/SELINUX=permissive/' /etc/selinux/config
# 4. 关闭防火墙或开放端口
# 控制面:6443, 2379-2380, 10250, 10259, 10257
# Worker:10250, 30000-32767
# 简单方式(测试环境):
systemctl stop firewalld && systemctl disable firewalld
# 5. 内核参数配置
cat <<EOF | tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-iptables = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward = 1
EOF
sysctl --system
# 6. 加载内核模块(containerd 需要)
cat <<EOF | tee /etc/modules-load.d/containerd.conf
overlay
br_netfilter
EOF
modprobe overlay
modprobe br_netfilter
# 7. 安装容器运行时(containerd)
yum install -y containerd.io
mkdir -p /etc/containerd
containerd config default > /etc/containerd/config.toml
# 关键:确保 cgroup 驱动为 systemd
sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl enable --now containerd
systemctl status containerd
# 8. 安装 kubeadm / kubelet / kubectl
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=0
EOF
yum install -y kubeadm kubelet kubectl
systemctl enable kubelet
# 9. 时间同步
yum install -y chrony
systemctl enable --now chronyd
chronyc sources -v
# 10. 验证
lsmod | grep br_netfilter
sysctl net.bridge.bridge-nf-call-iptables
crictl info | grep -i cgroup
kubeadm version
kubeadm token create --print-join-command什么时候用?
# kubeadm token create --print-join-command
# 作用:生成 Worker 节点加入集群的完整命令(含 token 和证书 hash)
# 在 Master 节点上执行
# 使用场景:
# 1. kubeadm init 时未保存 join 命令
# init 输出的 join 命令丢失了,重新生成
kubeadm token create --print-join-command
# 2. Token 过期(默认 24 小时)
# 之前的 Token 已过期,需要创建新 Token 让节点加入
kubeadm token create --print-join-command
# Token 有效期管理:
kubeadm token list # 查看所有 Token
kubeadm token create --ttl 0 # 创建永不过期的 Token
kubeadm token delete <token-id> # 删除指定 Token
# 3. 扩容集群(新增 Worker 节点)
# 在新 Worker 上执行生成的命令即可
# 4. Master 节点也加入集群
# kubeadm init 后,如果需要添加额外的控制面节点:
kubeadm token create --print-join-command \
--certificate-key <key> # 加控制面证书密钥
# 输出示例:
# kubeadm join 10.1.8.10:6443 --token abcdef.0123456789abcdef \
# --discovery-token-ca-cert-hash sha256:1234567890abcdef...
# 注意事项:
# - Token 格式:<6位>.<16位>(如 abcdef.0123456789abcdef)
# - --discovery-token-ca-cert-hash:确保加入的节点连接到正确的 Control Plane
# - 获取 CA 证书 hash 的备用方法:
openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | \
openssl rsa -pubin -outform der 2>/dev/null | \
openssl dgst -sha256 -hex | sed 's/^.* //'
- etcd 备份怎么做?如何恢复?
# ==================== etcd 备份 ====================
# 方法 1:使用 etcdctl snapshot save(推荐)
# 设置 etcd 连接参数
ETCDCTL_API=3 etcdctl snapshot save /backup/etcd-snapshot-$(date +%Y%m%d-%H%M%S).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
# 验证备份文件
ETCDCTL_API=3 etcdctl --write-out=table snapshot status /backup/etcd-snapshot-xxx.db
# 方法 2:kubeadm 编排备份(如果 etcd 是 kubeadm 部署的 Pod)
# 先查看 etcd Pod 信息
kubectl -n kube-system get pod -l component=etcd
# etcdctl 在 etcd 容器内执行
kubectl -n kube-system exec etcd-master01 -- etcdctl \
--cacert=/etc/kubernetes/pki/etcd/ca.crt \
--cert=/etc/kubernetes/pki/etcd/server.crt \
--key=/etc/kubernetes/pki/etcd/server.key \
--endpoints=https://127.0.0.1:2379 \
snapshot save /var/lib/etcd/snapshot.db
# 方法 3:定时备份 CronJob
# 创建 CronJob 定期备份 etcd 到持久存储/S3
# ==================== etcd 恢复 ====================
# 恢复流程(需在所有 Master 节点停止 etcd/kube-apiserver)
# 步骤 1:停止 kube-apiserver 和 etcd(所有 Master 节点)
# kubeadm 部署的集群:
mkdir /tmp/etcd-backup
mv /etc/kubernetes/manifests/kube-apiserver.yaml /tmp/etcd-backup/
mv /etc/kubernetes/manifests/etcd.yaml /tmp/etcd-backup/
# 等待 kubelet 自动停止这些静态 Pod
# 步骤 2:从备份恢复 etcd(在一台 Master 上执行)
ETCDCTL_API=3 etcdctl snapshot restore /backup/etcd-snapshot-xxx.db \
--name=master01 \
--data-dir=/var/lib/etcd-restore \
--initial-cluster=master01=https://10.1.8.10:2380 \
--initial-advertise-peer-urls=https://10.1.8.10:2380
# 步骤 3:更新 etcd 数据目录
mv /var/lib/etcd /var/lib/etcd.bak
mv /var/lib/etcd-restore /var/lib/etcd
chown -R etcd:etcd /var/lib/etcd
# 步骤 4:恢复 etcd 和 kube-apiserver 的 manifest
mv /tmp/etcd-backup/kube-apiserver.yaml /etc/kubernetes/manifests/
mv /tmp/etcd-backup/etcd.yaml /etc/kubernetes/manifests/
# kubelet 会自动重新启动 etcd 和 kube-apiserver
# 步骤 5:验证恢复
kubectl get nodes
kubectl get pods -A
# 关键注意事项:
# 1. 备份文件中包含集群所有数据,务必安全存储
# 2. 恢复会覆盖当前所有集群状态(不可逆)
# 3. 备份恢复后,原有 PVC/PV/Secret 等资源仍存在(etcd 中),
# 但 Pod 等运行时对象重新创建
# 4. 建议定期备份 + 验证备份可恢复性
- 节点 NotReady 了怎么排查?
# 节点 NotReady 排查(完整思路)
# 步骤 1:查看节点状态和条件
kubectl describe node <node-name>
# 重点关注 Conditions 部分:
# - MemoryPressure(内存压力)
# - DiskPressure(磁盘压力)
# - PIDPressure(PID 压力)
# - NetworkUnavailable(网络不可用)
# - Ready(节点就绪状态,True/False/Unknown)
# 哪个 Condition 为 False 或报错,就是根因方向
# 步骤 2:检查 kubelet 状态
# SSH 到问题节点
systemctl status kubelet
journalctl -u kubelet -f --no-pager # 实时查看日志
journalctl -u kubelet --since "10 min ago" --no-pager
# 常见 kubelet 问题:
# - kubelet 进程挂了 / OOM
# - 证书过期
# - 磁盘空间不足(kubelet 无法写入)
# - 与 API Server 通信失败
# 步骤 3:检查容器运行时
systemctl status containerd # 或 docker
crictl ps # 查看运行中的容器
crictl info # 运行时信息
# 步骤 4:检查磁盘空间
df -h
# 特别关注:/var(镜像、容器存储)、/(根分区)
du -sh /var/lib/containerd/
du -sh /var/log/
# 清理:crictl rmi --prune / journalctl --vacuum-size=500M
# 步骤 5:检查网络
# 节点能否 ping 通 API Server
ping <apiserver-ip>
# CNI 插件是否正常
kubectl -n kube-system get pods -l k8s-app=calico-node -o wide
# 查看 CNI 相关 Pod 日志
# 步骤 6:检查节点资源
kubectl top node <node-name> # CPU/内存使用
free -h # 系统内存
# 步骤 7:未知原因,尝试重启 kubelet
systemctl restart kubelet
# 步骤 8:极端情况 — 移除节点并重新加入
kubectl drain <node-name> --ignore-daemonsets
kubectl delete node <node-name>
# 在节点上重置 kubeadm
kubeadm reset -f
# 重新加入集群
kubeadm join <master-ip>:6443 --token ... --discovery-token-ca-cert-hash ...
5.12 排错实战
- Pod 一直 Pending 怎么排查?
# Pod Pending 排查(调度失败或资源不足)
# 步骤 1:看 Pod 详情和 Events
kubectl describe pod <pod-name> -n <ns>
# 重点看 Events 部分,一般会明确指出原因
# 步骤 2:常见原因及解决方案
# 原因 1:资源不足(CPU/内存不足)
# Events: "0/3 nodes are available: insufficient cpu"
# 解决:减少 requests、扩容节点、删除不用的 Pod
kubectl top nodes
kubectl describe nodes | grep -A5 "Allocated resources"
# 原因 2:nodeSelector / nodeAffinity 不匹配
# Events: "0/3 nodes are available: node(s) didn't match node selector"
# 解决:检查 Pod 的 nodeSelector 是否与 Node Labels 匹配
kubectl get nodes --show-labels
# 原因 3:Taint 排斥
# Events: "0/3 nodes are available: node(s) had taint..."
# 解决:添加 Toleration 或移除 Taint
kubectl describe nodes | grep Taint
kubectl taint nodes <node-name> key=value:NoSchedule-
# 原因 4:PVC 无法绑定
# Events: "persistentvolumeclaim 'xxx' not found" 或 "pod has unbound immediate PVC"
# 解决:检查 PVC/PV/StorageClass 状态
kubectl get pvc -n <ns>
kubectl get pv
kubectl get storageclass
# 原因 5:端口冲突(hostPort 已被占用)
# Events: "host port is already allocated"
# 解决:检查同节点其他 Pod 的 hostPort
# 原因 6:镜像问题(ImagePullBackOff → 但可能仍显示 Pending)
# Events: 可能是镜像拉取失败
kubectl describe pod <pod-name> | grep -A5 "Failed"
# 原因 7:PodAntiAffinity 限制
# Events: "node(s) didn't match pod anti-affinity rules"
# 原因 8:PriorityClass 抢占
# 低优先级 Pod 被高优先级 Pod 抢占了节点
# 步骤 3:全局视图
kubectl get events -n <ns> --sort-by='.lastTimestamp'
kubectl get pods -n <ns> -o wide
- Pod CrashLoopBackOff 怎么排查?
# CrashLoopBackOff 含义
# 容器启动后很快崩溃退出,kubelet 重复重启,每次重启间隔以指数级增长
# (10s → 20s → 40s → ... → 最多 5 分钟)
# 排查步骤:
# 步骤 1:看 Pod 状态和重启次数
kubectl get pod <pod-name> -n <ns>
kubectl describe pod <pod-name> -n <ns> | grep -E "State|Restart|Exit|Reason|OOM"
# 步骤 2:看容器日志(包括上一个已崩溃的容器)
kubectl logs <pod-name> -n <ns> --previous # 上一个崩溃容器的日志
kubectl logs <pod-name> -n <ns> --tail=100 # 当前容器日志
kubectl logs <pod-name> -n <ns> -f # 实时跟踪
# 步骤 3:常见原因及排查
# 原因 1:应用启动失败(配置错误 / 依赖缺失)
# 日志关键词:connection refused / file not found / config parse error
# 解决:检查 ConfigMap、Secret、环境变量是否正确
# 原因 2:OOMKilled(内存不足)
# describe 输出:Reason: OOMKilled, Exit Code: 137
# 解决:增加 Memory Limits 或排查内存泄漏
# 原因 3:探针配置不当(Liveness/Startup Probe 失败)
# describe 输出:Liveness probe failed: ...
# 解决:调整 initialDelaySeconds、periodSeconds、failureThreshold
# 原因 4:启动命令/参数错误
# 容器启动命令本身有问题
# 解决:检查 command/args 配置,可手动测试
kubectl run test --rm -it --image=<same-image> --command -- /bin/sh
# 原因 5:权限问题
# 日志关键词:permission denied
# 解决:调整 securityContext 或容器内用户
# 原因 6:外部依赖不可用
# 日志关键词:timeout / connection refused
# 解决:检查数据库、缓存等外部服务连通性
# 步骤 4:临时禁止重启,进入调试模式
kubectl debug <pod-name> -n <ns> --image=busybox --target=<container-name>
# 或者修改 Pod 命令为 sleep infinity 保持运行
kubectl edit pod <pod-name> -n <ns>
# 将 command 改为:["sleep", "3600"]
- ImagePullBackOff 怎么排查?
# ImagePullBackOff 含义
# kubelet 尝试拉取镜像失败,并进入指数退避重试(BackOff)
# 常见错误:ImagePullBackOff / ErrImagePull / InvalidImageName
# 排查步骤:
# 步骤 1:查看详细错误原因
kubectl describe pod <pod-name> -n <ns> | grep -A10 "Events"
# 关键信息:
# - Failed to pull image ... → 拉取失败
# - pull access denied → 权限问题
# - repository does not exist → 镜像不存在
# - connection refused / no such host → 网络问题
# 步骤 2:常见原因及解决
# 原因 1:镜像名或 Tag 错误
# Events: "manifest for xxx:latest not found" 或 "not found"
# 解决:确认镜像名和 Tag 在镜像仓库中存在
# 检查:docker pull <image> / crictl pull <image>
# 原因 2:私有仓库认证问题
# Events: "pull access denied" 或 "authentication required"
# 解决:创建 imagePullSecrets
kubectl create secret docker-registry regcred \
--docker-server=my-registry.com \
--docker-username=admin \
--docker-password=pass123 \
-n <ns>
# 在 Pod 中引用
spec:
imagePullSecrets:
- name: regcred
# 或添加到 ServiceAccount
kubectl patch serviceaccount default -n <ns> \
-p '{"imagePullSecrets": [{"name": "regcred"}]}'
# 原因 3:网络问题(无法连接到镜像仓库)
# Events: "dial tcp: lookup xxx: no such host" 或 "timeout"
# 解决:检查节点网络、DNS、防火墙
# 从节点测试:crictl pull docker.io/library/nginx:latest
# 检查 /etc/resolv.conf
# 检查 /etc/containerd/config.toml 中的 mirror 配置
# 原因 4:镜像仓库宕机或限流
# Docker Hub 免费账户有拉取速率限制
# 解决:配置镜像代理/缓存(如 Harbor)、使用私有仓库
# 原因 5:containerd 配置问题
# containerd 的 registry mirror 配置不正确
# 解决:检查 /etc/containerd/config.toml
[plugins."io.containerd.grpc.v1.cri".registry.mirrors]
[plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
endpoint = ["https://registry-1.docker.io"]
# 步骤 3:手动在节点上拉取测试
crictl pull <image-name>
echo $? # 检查返回码
crictl images | grep <image-name>
# 步骤 4:查看 kubelet 日志
journalctl -u kubelet --since "5 min ago" | grep -i pull
# 步骤 5:检查磁盘空间
df -h /var/lib/containerd # 镜像存储路径
# 磁盘满也会导致镜像拉取失败
- kubectl 连接 API Server 报
connection refused怎么排查?
# kubectl 连接 API Server 报 connection refused 排查
# 现象:
# The connection to the server <ip>:6443 was refused - did you specify the right host or port?
# 步骤 1:检查 API Server 是否运行
# kubeadm 部署的(静态 Pod):
ssh <master-node>
crictl ps | grep kube-apiserver
# 或查看文件是否存在
ls -la /etc/kubernetes/manifests/kube-apiserver.yaml
# 步骤 2:检查 API Server 进程/容器日志
# 容器运行时方式查看
crictl logs <kube-apiserver-container-id>
# 或查看 kubelet 日志
journalctl -u kubelet --since "5 min ago" | grep apiserver
# 步骤 3:检查端口是否监听
ss -tlnp | grep 6443
# 如果端口没监听 → API Server 没启动
# 如果监听在 127.0.0.1:6443 → 检查 --bind-address 参数
# 步骤 4:检查 kubeconfig 配置
kubectl cluster-info
cat ~/.kube/config | grep server
# 确认 server 地址正确
# 步骤 5:检查网络连通性(从 kubectl 客户端到 API Server)
ping <apiserver-ip>
telnet <apiserver-ip> 6443
nc -zv <apiserver-ip> 6443
curl -k https://<apiserver-ip>:6443/healthz
# 步骤 6:API Server 自身原因排查
# - etcd 不可用 → API Server 无法启动
ETCDCTL_API=3 etcdctl endpoint health --endpoints=...
# - 证书过期
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -noout -dates
kubeadm certs check-expiration
# - CPU/内存不足导致被 OOM Kill
# 查看节点资源:kubectl top node <master>
# 步骤 7:负载均衡器问题(如有外部 LB)
# 如果经过 LB(如 HAProxy / Nginx)访问
# 检查 LB 后端健康检查、LB 配置
# 步骤 8:防火墙/安全组
iptables -L -n | grep 6443
firewall-cmd --list-all
# 步骤 9:重建 API Server(最后手段 + 需谨慎)
# 检查并修正 /etc/kubernetes/manifests/kube-apiserver.yaml
# kubelet 会自动检测并重启该 Pod
- Service 不通怎么排查?(DNS → Endpoints → kube-proxy → Pod)
# Service 不通完整排查链路:DNS → Endpoints → kube-proxy → iptables/IPVS → Pod
# ====== 1. 确认 Service 和 Endpoints ======
kubectl get svc <svc-name> -n <ns> -o wide
kubectl get endpoints <svc-name> -n <ns>
# Endpoints 为空 → 检查 Selector 是否匹配 Pod 的 Labels
kubectl describe svc <svc-name> -n <ns> | grep Selector
kubectl get pods -n <ns> -l <selector-label> --show-labels
# ====== 2. 检查 Pod 就绪状态 ======
kubectl get pods -n <ns> -l <selector-label>
# READY 列:1/1 才是就绪,0/1 说明 ReadinessProbe 未通过
kubectl describe pod <pod-name> -n <ns> | grep -A5 Readiness
# ====== 3. 检查 DNS 解析 ====
# 从测试 Pod 中测试 DNS
kubectl run dns-test --rm -it --image=busybox:1.28 -- nslookup <svc-name>.<ns>.svc.cluster.local
# DNS 解析不成功:
# a) 检查 CoreDNS Pod
kubectl -n kube-system get pods -l k8s-app=kube-dns
kubectl -n kube-system logs -l k8s-app=kube-dns --tail=50
# b) 检查 Service 的 ClusterIP 是否能解析
# c) 测试 Pod 的 /etc/resolv.conf 是否正确
kubectl exec <test-pod> -- cat /etc/resolv.conf
# ====== 4. 从 Pod 内直接测试 Service IP ======
kubectl run curl-test --rm -it --image=curlimages/curl -- sh
# 在容器内执行:
curl -v http://<cluster-ip>:<port>
curl -v http://<svc-name>.<ns>:<port>
# ====== 5. 检查 kube-proxy 规则 ======
# 到发生问题的 Node 上检查
# iptables 模式:
iptables-save | grep <cluster-ip>
iptables -t nat -L KUBE-SERVICES -n | grep <cluster-ip>
iptables -t nat -L KUBE-SVC-XXX -n # 查看后端 Pod 转发链
# IPVS 模式:
ipvsadm -L -n | grep <cluster-ip>
# 如果规则没用 → kube-proxy 有问题
kubectl -n kube-system logs -l k8s-app=kube-proxy --tail=100
# ====== 6. 检查 Service 端口和 TargetPort ======
kubectl describe svc <svc-name> -n <ns>
# 确认:port(Service端口)→ targetPort(容器端口)→ containerPort 一致
# 确认容器内实际监听端口
kubectl exec <pod-name> -n <ns> -- netstat -tlnp
# ====== 7. 检查 NetworkPolicy ======
kubectl get networkpolicy -n <ns>
# 有 NetworkPolicy 时默认拒绝,必须显示允许
# ====== 8. 测试 Pod 直连 ======
# 绕过 Service,直接访问 Pod IP:Port
kubectl run curl-direct --rm -it --image=curlimages/curl -- curl <pod-ip>:<port>
# Pod 直连通但 Service 不通 → kube-proxy/iptables 问题
# Pod 直连也不通 → CNI 网络或应用问题
# ====== 9. 跨节点问题 ======
# 不同节点的 Pod 是否通
# 检查 CNI Pod 日志
kubectl -n kube-system logs -l k8s-app=calico-node --tail=100
- 节点 NotReady 怎么排查?(kubelet → containerd → CNI)
# 节点 NotReady 排查(从三个核心组件入手)
# ====== 1. 先看节点状态 ======
kubectl get node <node-name> -o wide
kubectl describe node <node-name> | grep -A10 Conditions
# 哪个 Condition 为 False 就是根因
# ====== 2. kubelet 层排查 ======
# SSH 到该节点
systemctl status kubelet
journalctl -u kubelet --since "10 min ago" --no-pager | tail -50
# 关键词搜索:
journalctl -u kubelet | grep -i error
journalctl -u kubelet | grep -i "failed"
journalctl -u kubelet | grep -i "certificate"
# 常见问题:
# - kubelet 与 API Server 失去联系(网络/证书)
# - kubelet 自身 OOM / 进程退出
# - /var/lib/kubelet 磁盘满
# 解决:
systemctl restart kubelet # 尝试重启
# ====== 3. 容器运行时层排查(containerd) ======
systemctl status containerd
crictl info # 运行时信息和配置
crictl ps # 运行中的容器
crictl images # 本地镜像
# 常见问题:
# - containerd 挂了
# - 磁盘空间不足(/var/lib/containerd)
# - cgroup 驱动不一致(systemd vs cgroupfs)
# 磁盘清理:
crictl rmi --prune # 清理未使用的镜像
# 检查配置:
cat /etc/containerd/config.toml | grep SystemdCgroup
# 应该为:SystemdCgroup = true
# ====== 4. CNI 层排查 ======
# CNI Pod 状态
kubectl -n kube-system get pods -o wide | grep -E "calico|flannel|weave|cilium"
# CNI Pod 日志
kubectl -n kube-system logs <cni-pod-on-problem-node>
# 检查 CNI 配置(节点上)
ls /etc/cni/net.d/
cat /etc/cni/net.d/*.conf # CNI 配置文件
# 检查 CNI 二进制
ls /opt/cni/bin/
# 检查 Pod 网络接口
crictl inspect <container-id> | grep -i network
# 检查路由
ip route
route -n
# ====== 5. 节点资源检查 ======
df -h # 磁盘
free -h # 内存
top -bn1 | head -20 # CPU
# 磁盘压力(DiskPressure) → 清理 /var、/tmp
# 内存压力(MemoryPressure) → 限制或驱逐 Pod
# PID 压力(PIDPressure) → 排查僵尸进程或限制进程数
# ====== 6. 证书检查 ======
kubeadm certs check-expiration
openssl x509 -in /var/lib/kubelet/pki/kubelet-client-current.pem -noout -dates
# ====== 7. 终极排查:对比法 ======
# 与正常节点对比关键输出
diff <(ssh normal-node 'systemctl status kubelet') \
<(ssh problem-node 'systemctl status kubelet')
5.13 进阶
- Helm 是什么?和直接 kubectl apply 相比有什么优势?
# Helm 是什么?
# Helm 是 K8s 的"包管理器"(类似 yum/apt),用于管理 K8s 应用的定义、
# 安装和升级。Helm 使用 Chart(模板 + 值)来定义应用。
# 核心概念:
# Chart:包含应用所需 K8s 资源模板的打包文件
# Release:Chart 在集群中的运行实例(同一个 Chart 可多次安装 = 多个 Release)
# Repository:Chart 的存储和分发仓库
# Values:模板变量值(values.yaml / --set)
# 与 kubectl apply 对比
# | 特性 | kubectl apply | Helm |
# |---------------|--------------------|-------------------------------|
# | 模板化 | 无 | 支持(Go Template) |
# | 版本管理 | 需手动 | 内置(Release revision) |
# | 回滚 | 困难 | helm rollback 一键回滚 |
# | 依赖管理 | 无 | Chart 依赖自动安装 |
# | 参数化部署 | 需修改 YAML | --set / -f values.yaml |
# | 生命周期钩子 | 无 | 支持(pre-install/post-upgrade 等)|
# | 复用/共享 | YAML 文件 | Chart 仓库分发 |
# | 多环境管理 | 多份 YAML 文件 | 不同 values 文件 |
# | 干运行 | --dry-run | helm template / --dry-run |
# | 发布状态 | 不明确 | helm list / helm status |
# Helm 常用命令:
helm repo add bitnami https://charts.bitnami.com/bitnami
helm repo update
helm search repo nginx
helm install my-release bitnami/nginx --set service.type=NodePort
helm upgrade my-release bitnami/nginx --set replicaCount=3
helm rollback my-release 1
helm uninstall my-release
helm list -A
helm history my-release
- Operator 模式是什么?解决了什么问题?
# Operator 模式是什么?
# Operator 是 K8s 的扩展模式。它将"人类运维知识"编码成软件(自动化运维),
# 通过自定义控制器管理自定义资源(CRD + Custom Controller)。
# Operator = CRD(声明期望状态) + Controller(实现期望状态)
# Operator 工作流程:
# 1. 用户创建自定义资源 CR 实例(如创建 MySQL 集群)
# 2. Operator Controller 监听到 CR 创建
# 3. Controller 执行运维逻辑(创建 StatefulSet + Service + PVC + 配置主从复制...)
# 4. 持续监控,确保实际状态 = 期望状态(自动处理故障、扩缩容、备份等)
# 解决了什么问题?
# 1. 复杂应用自动化管理
# 传统:人工管理数据库集群部署/主从切换/备份/扩容
# Operator:声明式定义 + 自动执行
# 2. 领域知识编码
# 将有状态应用(DB/Kafka/Redis 集群)的最佳运维实践固化到代码中
# 减少人为操作失误
# 3. 生命周期自动化 Day 2 运维
# 不仅是 Day 1(部署),更解决 Day 2(升级、备份、故障恢复、扩缩容)
# k8s 原生 Deployment/StatefulSet 无法覆盖复杂 Day 2 场景
# 4. 自愈能力
# 检测到异常状态(如从库同步延迟、脑裂),自动修复
# 常见 Operator:
# - Prometheus Operator:自动管理 Prometheus/Alertmanager/Grafana
# - etcd Operator:管理 etcd 集群
# - MySQL Operator / Postgres Operator:数据库集群管理
# - Strimzi Kafka Operator:Kafka 集群管理
# - cert-manager:证书自动化管理
# - Istio Operator:服务网格管理
# Operator 框架:
# - Operator SDK(Go/Ansible/Helm)
# - Kubebuilder
# - Kopf (Python)
- Kubernetes Dashboard 的作用?如何对外暴露访问?
# Kubernetes Dashboard 的作用
# K8s 官方 Web UI,提供图形化管理界面:
# - 查看集群资源(Node/Pod/Deploy/Service 等)
# - 创建/修改/删除资源
# - 查看资源日志和事件
# - 查看资源使用(CPU/内存)
# - 执行命令(Pod 内)
# - 管理 RBAC
# 部署 Dashboard
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.7.0/aist/deploy/recommended.yaml
# 对外暴露访问的几种方式:
# 方式 1:kubectl proxy(开发测试)
kubectl proxy --address='0.0.0.0' --accept-hosts='^*$'
# 访问:http://<master-ip>:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
# 缺点:不安全、性能差、仅限临时使用
# 方式 2:NodePort
kubectl -n kubernetes-dashboard patch svc kubernetes-dashboard \
-p '{"spec":{"type":"NodePort"}}'
kubectl -n kubernetes-dashboard get svc kubernetes-dashboard | grep NodePort
# 访问:https://<node-ip>:<nodeport>
# 需要处理 TLS 证书和认证
# 方式 3:Ingress(推荐生产环境)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: dashboard-ingress
namespace: kubernetes-dashboard
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
cert-manager.io/cluster-issuer: "letsencrypt-prod"
spec:
tls:
- hosts:
- dashboard.example.com
secretName: dashboard-tls
rules:
- host: dashboard.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: kubernetes-dashboard
port:
number: 443
# 方式 4:API Server 代理(kubectl proxy)
# 通过 API Server 作为反向代理访问(适合开发测试)
# ====== 认证配置 ======
# 创建管理员 ServiceAccount 获取 Token(测试用)
kubectl -n kubernetes-dashboard create serviceaccount admin-user
kubectl -n kubernetes-dashboard create clusterrolebinding admin-user \
--clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:admin-user
kubectl -n kubernetes-dashboard create token admin-user
# 生产环境建议:集成 OIDC/LDAP 或使用 RBAC 细粒度权限
# 安全建议:
# 1. 生产环境必须用 Ingress + TLS
# 2. 集成 OIDC 认证(对接 LDAP/AD)
# 3. 配置 RBAC 细粒度权限(cluster-admin 仅限管理员)
# 4. 可配合 oauth2-proxy 增加额外认证层
# 5. 考虑使用 NetworkPolicy 限制访问来源
- MetalLB 是什么?解决什么问题?
# MetalLB 是什么?
# MetalLB 是为裸金属(Bare-metal / 非云环境)K8s 集群提供
# LoadBalancer 类型 Service 的实现。让没有云厂商 LB 的私有环境也能用
# kubectl expose --type=LoadBalancer 获得外部负载均衡 IP。
# 解决了什么问题?
# 在公有云,创建 type=LoadBalancer Service 时,云控制器自动创建外部 LB,
# 分配公网 IP。但在裸金属/私有数据中心,没有这个能力,LoadBalancer Service
# 永远处于 Pending 状态(EXTERNAL-IP 显示 <pending>)。
# MetalLB 解决了"裸金属集群如何获得 LoadBalancer 类型的 Service 外部 IP"的问题。
# MetalLB 两种工作模式:
# 1. Layer 2 模式(ARP/NDP)
# 选举一个节点为"Leader",响应所有 LB IP 的 ARP 请求
# 优点:无需路由器支持、简单
# 缺点:单节点承载所有流量(带宽瓶颈)、无故障转移时短暂中断
# 场景:小型/开发环境
# 2. BGP 模式
# 通过 BGP 协议与路由器对等,向路由器宣告 LB IP 路由
# 优点:真正的多节点负载均衡、故障转移快、适合生产
# 缺点:需要支持 BGP 的路由器、配置复杂
# 场景:生产环境、需要跨节点负载均衡
# 部署 MetalLB:
# 前提:K8s 集群网络已配好(CNI),kube-proxy 禁用 strict ARP
kubectl -n kube-system get cm kube-proxy -o yaml
# 确保 strictARP: true(IPVS 模式)或改 iptables 相关配置
# 安装 MetalLB
kubectl apply -f https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml
# 配置 IP 地址池(IPAddressPool) - Layer 2 模式
apiVersion: metallb.io/v1beta1
kind: IPAddressPool
metadata:
name: my-pool
namespace: metallb-system
spec:
addresses:
- 10.1.8.100-10.1.8.120 # 可分配的 IP 段
---
apiVersion: metallb.io/v1beta1
kind: L2Advertisement
metadata:
name: my-l2-adv
namespace: metallb-system
spec:
ipAddressPools:
- my-pool
# 测试:
kubectl create deploy nginx --image=nginx --replicas=3
kubectl expose deploy nginx --port=80 --type=LoadBalancer
kubectl get svc nginx
# EXTERNAL-IP 应显示 10.1.8.100(自动分配)
# 直接访问:
curl http://10.1.8.100
# 底层流程:请求 → MetalLB ARP 响应 → 节点 → kube-proxy → Pod
# 同类型替代:
# - OpenELB(原 PorterLB)
# - PureLB
# - kube-vip (强大的 VIP 方案)
# - HAProxy + keepalived (传统方案)
本册共 88 题,覆盖 Docker · Kubernetes
更多推荐
所有评论(0)